[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\nAccessModifierOffset: -4\nAlignAfterOpenBracket: AlwaysBreak\nAlignArrayOfStructures: None\nAlignConsecutiveAssignments:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    true\nAlignConsecutiveBitFields:\n  Enabled:         true\n  AcrossEmptyLines: true\n  AcrossComments:  true\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    true\nAlignConsecutiveDeclarations:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    true\nAlignConsecutiveMacros:\n  Enabled:         true\n  AcrossEmptyLines: false\n  AcrossComments:  true\n  AlignCompound:   true\n  AlignFunctionPointers: false\n  PadOperators:    true\nAlignConsecutiveShortCaseStatements:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCaseColons: false\nAlignEscapedNewlines: Left\nAlignOperands:   Align\nAlignTrailingComments:\n  Kind:            Never\n  OverEmptyLines:  0\nAllowAllArgumentsOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowBreakBeforeNoexceptSpecifier: Never\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortCompoundRequirementOnASingleLine: true\nAllowShortEnumsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: WithoutElse\nAllowShortLambdasOnASingleLine: All\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: Yes\nAttributeMacros:\n  - __capability\nBinPackArguments: false\nBinPackParameters: false\nBitFieldColonSpacing: Both\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterClass:      false\n  AfterControlStatement: Never\n  AfterEnum:       false\n  AfterExternBlock: false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  BeforeCatch:     false\n  BeforeElse:      false\n  BeforeLambdaBody: false\n  BeforeWhile:     false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakAdjacentStringLiterals: true\nBreakAfterAttributes: Leave\nBreakAfterJavaFieldAnnotations: false\nBreakArrays:     true\nBreakBeforeBinaryOperators: None\nBreakBeforeConceptDeclarations: Always\nBreakBeforeBraces: Attach\nBreakBeforeInlineASMColon: OnlyMultiline\nBreakBeforeTernaryOperators: false\nBreakConstructorInitializers: BeforeComma\nBreakInheritanceList: BeforeColon\nBreakStringLiterals: false\nColumnLimit:     99\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat:   false\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: false\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\n  - M_EACH\nIfMacros:\n  - KJ_IF_MAYBE\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '.*'\n    Priority:        1\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '.*'\n    Priority:        1\n    SortPriority:    0\n    CaseSensitive:   false\nIncludeIsMainRegex: '(Test)?$'\nIncludeIsMainSourceRegex: ''\nIndentAccessModifiers: false\nIndentCaseBlocks: false\nIndentCaseLabels: false\nIndentExternBlock: AfterExternBlock\nIndentGotoLabels: true\nIndentPPDirectives: None\nIndentRequiresClause: false\nIndentWidth:     4\nIndentWrappedFunctionNames: true\nInsertBraces:    false\nInsertNewlineAtEOF: true\nInsertTrailingCommas: None\nIntegerLiteralSeparator:\n  Binary:          0\n  BinaryMinDigits: 0\n  Decimal:         0\n  DecimalMinDigits: 0\n  Hex:             0\n  HexMinDigits:    0\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nKeepEmptyLinesAtEOF: false\nLambdaBodyIndentation: Signature\nLineEnding:      DeriveLF\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 4\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: true\nObjCSpaceBeforeProtocolList: true\nPackConstructorInitializers: BinPack\nPenaltyBreakAssignment: 10\nPenaltyBreakBeforeFirstCallParameter: 30\nPenaltyBreakComment: 10\nPenaltyBreakFirstLessLess: 0\nPenaltyBreakOpenParenthesis: 0\nPenaltyBreakScopeResolution: 500\nPenaltyBreakString: 10\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 100\nPenaltyIndentedWhitespace: 0\nPenaltyReturnTypeOnItsOwnLine: 60\nPointerAlignment: Left\nPPIndentWidth:   -1\nQualifierAlignment: Leave\nReferenceAlignment: Pointer\nReflowComments:  false\nRemoveBracesLLVM: false\nRemoveParentheses: Leave\nRemoveSemicolon: true\nRequiresClausePosition: OwnLine\nRequiresExpressionIndentation: OuterScope\nSeparateDefinitionBlocks: Leave\nShortNamespaceLines: 1\nSkipMacroDefinitionBody: false\nSortIncludes:    Never\nSortJavaStaticImport: Before\nSortUsingDeclarations: Never\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: true\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeJsonColon: false\nSpaceBeforeParens: Never\nSpaceBeforeParensOptions:\n  AfterControlStatements: false\n  AfterForeachMacros: false\n  AfterFunctionDefinitionName: false\n  AfterFunctionDeclarationName: false\n  AfterIfMacros:   false\n  AfterOverloadedOperator: false\n  AfterPlacementOperator: true\n  AfterRequiresInClause: false\n  AfterRequiresInExpression: false\n  BeforeNonEmptyParentheses: false\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceBeforeSquareBrackets: false\nSpaceInEmptyBlock: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  Never\nSpacesInContainerLiterals: false\nSpacesInLineCommentPrefix:\n  Minimum:         1\n  Maximum:         -1\nSpacesInParens:  Never\nSpacesInParensOptions:\n  InCStyleCasts:   false\n  InConditionalStatements: false\n  InEmptyParentheses: false\n  Other:           false\nSpacesInSquareBrackets: false\nStandard:        c++20\nStatementAttributeLikeMacros:\n  - Q_EMIT\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        4\nUseTab:          Never\nVerilogBreakBetweenInstancePorts: true\nWhitespaceSensitiveMacros:\n  - STRINGIZE\n  - PP_STRINGIZE\n  - BOOST_PP_STRINGIZE\n  - NS_SWIFT_NAME\n  - CF_SWIFT_NAME\n...\n\n"
  },
  {
    "path": ".clangd",
    "content": "CompileFlags:\n    Add: \n        - -Wno-unknown-warning-option\n        - -Wno-format\n    Remove: \n        - -mword-relocations\n\nDiagnostics:\n    ClangTidy:\n        FastCheckFilter: None\n\n---\n\nIf:\n    PathMatch: .*\\.h\nDiagnostics:\n    UnusedIncludes: None\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\n\n[*.{cpp,h,c,py,sh}]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n*.bat eol=crlf\n*.ps1 eol=crlf\n*.cmd eol=crlf\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# Who owns all the fish by default\n* @DrZlo13 @hedger @gsurkov\n\n# Apps\n/applications/debug/bt_debug_app/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/debug/accessor/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/battery_test_app/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/debug/bt_debug_app/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/debug/file_browser_test/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/lfrfid_debug/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/text_box_test/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/uart_echo/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/usb_mouse/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/debug/usb_test/ @DrZlo13 @hedger @gsurkov @nminaylov\n\n/applications/main/archive/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/main/bad_usb/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/main/gpio/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/main/ibutton/ @DrZlo13 @hedger @gsurkov\n/applications/main/infrared/ @DrZlo13 @hedger @gsurkov\n/applications/main/nfc/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/main/subghz/ @DrZlo13 @hedger @gsurkov @Skorpionm\n/applications/main/u2f/ @DrZlo13 @hedger @gsurkov @nminaylov\n\n/applications/services/bt/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/services/cli/ @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3\n/applications/services/crypto/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/services/desktop/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/services/dolphin/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/services/power/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/services/rpc/ @DrZlo13 @hedger @gsurkov @nminaylov\n\n/applications/services/bt_settings_app/ @DrZlo13 @hedger @gsurkov @gornekich\n/applications/services/desktop_settings/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/services/dolphin_passport/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/services/power_settings_app/ @DrZlo13 @hedger @gsurkov @gornekich\n\n/applications/system/storage_move_to_sd/ @DrZlo13 @hedger @gsurkov @nminaylov\n/applications/system/js_app/ @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3\n\n/applications/debug/unit_tests/ @DrZlo13 @hedger @gsurkov @nminaylov @gornekich @Skorpionm\n\n/applications/examples/example_thermo/ @DrZlo13 @hedger @gsurkov\n\n# Firmware targets\n/targets/ @DrZlo13 @hedger @gsurkov @nminaylov\n\n# Assets\n/applications/main/infrared/resources/ @DrZlo13 @hedger @gsurkov\n\n# Documentation\n/documentation/ @DrZlo13 @hedger @gsurkov @portasynthinca3\n/scripts/toolchain/ @DrZlo13 @hedger @gsurkov\n\n# Lib\n/lib/stm32wb_copro/ @DrZlo13 @hedger @gsurkov @gornekich\n/lib/digital_signal/ @DrZlo13 @hedger @gsurkov @gornekich\n/lib/infrared/ @DrZlo13 @hedger @gsurkov\n/lib/lfrfid/ @DrZlo13 @hedger @gsurkov @nminaylov\n/lib/libusb_stm32/ @DrZlo13 @hedger @gsurkov @nminaylov\n/lib/mbedtls/ @DrZlo13 @hedger @gsurkov @nminaylov\n/lib/mjs/ @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3\n/lib/nanopb/ @DrZlo13 @hedger @gsurkov @nminaylov\n/lib/nfc/ @DrZlo13 @hedger @gsurkov @gornekich\n/lib/one_wire/ @DrZlo13 @hedger @gsurkov\n/lib/subghz/ @DrZlo13 @hedger @gsurkov @Skorpionm\n/lib/toolbox/ @DrZlo13 @hedger @gsurkov\n/lib/toolbox/cli @DrZlo13 @hedger @gsurkov @portasynthinca3\n\n# CI/CD\n/.github/workflows/ @DrZlo13 @hedger @gsurkov\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/01_bug_report.yml",
    "content": "name: Bug report\ndescription: File a bug reports regarding the firmware.\nlabels: ['bug']\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero firmware.\n      If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)\n- type: textarea\n  id: description\n  attributes:\n    label: Describe the bug.\n    description: \"A clear and concise description of what the bug is.\"\n  validations:\n    required: true\n- type: textarea\n  id: repro\n  attributes: \n    label: Reproduction\n    description: \"How can this bug be reproduced?\"\n    placeholder: |\n      1. Switch on...\n      2. Press button '....'\n      3. Wait for the moon phase\n      4. It burns\n  validations:\n    required: true\n- type: input\n  id: target\n  attributes:\n    label: Target\n    description: Specify the target\n    # Target seems to be largely ignored by outside sources.\n- type: textarea\n  id: logs\n  attributes:\n    label: Logs\n    description: Attach your debug logs here\n    render: Text\n    # Avoid rendering as Markdown here.\n- type: textarea\n  id: anything-else\n  attributes:\n    label: Anything else?\n    description: Let us know if you have anything else to share.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/02_enhancements.yml",
    "content": "name: Enhancements\ndescription: Suggest improvements for any existing functionality within the firmware.\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality.\n      If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)\n- type: textarea\n  id: proposal\n  attributes:\n    label: \"Describe the enhancement you're suggesting.\"\n    description: |\n      Feel free to describe in as much detail as you wish.\n  validations:\n    required: true\n- type: textarea\n  id: anything-else\n  attributes:\n    label: Anything else?\n    description: Let us know if you have anything else to share.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/03_feature_request.yml",
    "content": "name: Feature Request\ndescription: For feature requests regarding the firmware.\nlabels: ['feature request']\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.\n      If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)\n- type: textarea\n  id: proposal\n  attributes:\n    label: \"Description of the feature you're suggesting.\"\n    description: |\n      Please describe your feature request in as many details as possible.\n        - Describe what it should do.\n        - Note whether it is to extend existing functionality or introduce new functionality.\n  validations:\n    required: true\n- type: textarea\n  id: anything-else\n  attributes:\n    label: Anything else?\n    description: Let us know if you have anything else to share.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Need help?\n    url: https://forum.flipperzero.one\n    about: For any question regarding on how to use the Flipper Zero and its firmware.\n"
  },
  {
    "path": ".github/actions/submit_sdk/action.yml",
    "content": "name: Submit SDK to Catalog\nauthor: hedger\ndescription: |\n  This action checks if SDK exists in the catalog and if not, adds and/or publishes it.\n\ninputs:\n  catalog-url:\n    description: The URL of the Catalog API. Must not be empty or end with a /.\n    required: true\n  catalog-api-token:\n    description: The token to use to authenticate with the Catalog API. Must not be empty.\n    required: true\n  firmware-api:\n    description: Fimware's API version, major.minor\n    required: true\n  firmware-target:\n    description: Firmware's target, e.g. f7/f18\n    required: true\n  firmware-version:\n    description: Firmware's version, e.g. 0.13.37-rc3, or 0.13.37\n    required: true\n\nruns:\n  using: composite\n  steps:\n    - name: Check inputs\n      shell: bash\n      run: |\n        if [ -z \"${{ inputs.catalog-url }}\" ] ; then\n          echo \"Invalid catalog-url: ${{ inputs.catalog-url }}\"\n          exit 1\n        fi\n        if [ -z \"${{ inputs.catalog-api-token }}\" ] ; then\n          echo \"Invalid catalog-api-token: ${{ inputs.catalog-api-token }}\"\n          exit 1\n        fi\n        if ! echo \"${{ inputs.firmware-api }}\" | grep -q \"^[0-9]\\+\\.[0-9]\\+$\" ; then\n          echo \"Invalid firmware-api: ${{ inputs.firmware-api }}\"\n          exit 1\n        fi\n        if ! echo \"${{ inputs.firmware-target }}\" | grep -q \"^f[0-9]\\+$\" ; then\n          echo \"Invalid firmware-target: ${{ inputs.firmware-target }}\"\n          exit 1\n        fi\n        if ! echo \"${{ inputs.firmware-version }}\" | grep -q \"^[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+\\(-rc\\)\\?\\([0-9]\\+\\)\\?$\" ; then\n          echo \"Invalid firmware-version: ${{ inputs.firmware-version }}\"\n          exit 1\n        fi\n    - name: Submit SDK\n      shell: bash\n      run: |\n        curl --fail -sX 'GET' \\\n          '${{ inputs.catalog-url }}/api/v0/0/sdk?length=500' \\\n          -H 'Accept: application/json' > sdk_versions.json\n        if jq -r -e \".[] | select((.api == \\\"${{ inputs.firmware-api }}\\\") and .target == \\\"${{ inputs.firmware-target }}\\\")\" sdk_versions.json > found_sdk.json ; then\n          echo \"API version ${{ inputs.firmware-api }} already exists in catalog\"\n          if [ $(jq -r -e \".released_at\" found_sdk.json) != \"null\" ] ; then\n            echo \"API version is already released\"\n            exit 0\n          fi\n          if ! echo \"${{ inputs.firmware-version }}\" | grep -q -- \"-rc\" ; then\n            SDK_ID=$(jq -r ._id found_sdk.json)\n            echo \"Marking SDK $SDK_ID as released\"\n            curl --fail-with-body -X 'POST' \\\n              \"${{ inputs.catalog-url }}/api/v0/0/sdk/${SDK_ID}/release\" \\\n              -H 'Accept: application/json' \\\n              -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \\\n              -d ''\n          fi\n        else\n          echo \"API version ${{ inputs.firmware-api }} doesn't exist in catalog, adding\"\n          curl --fail-with-body -X 'POST' \\\n            '${{ inputs.catalog-url }}/api/v0/0/sdk' \\\n            -H 'Accept: application/json' \\\n            -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \\\n            -H 'Content-Type: application/json' \\\n            -d \"{\\\"name\\\": \\\"${{ inputs.firmware-version }}\\\", \\\"target\\\": \\\"${{ inputs.firmware-target }}\\\", \\\"api\\\": \\\"${{ inputs.firmware-api }}\\\"}\"\n        fi\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "# What's new\n\n- [ Describe changes here ]\n\n# Verification \n\n- [ Describe how to verify changes ]\n\n# Checklist (For Reviewer)\n\n- [ ] PR has description of feature/bug or link to Confluence/Jira task\n- [ ] Description contains actions to verify feature/bugfix\n- [ ] I've built this code, uploaded it to the device and verified feature/bugfix\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: 'Build'\n\non:\n  push:\n    branches:\n      - dev\n    tags:\n      - '*'\n  pull_request:\n\nenv:\n  DEFAULT_TARGET: f7\n  FBT_TOOLCHAIN_PATH: /runner/_work\n  FBT_GIT_SUBMODULE_SHALLOW: 1\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  main:\n    runs-on: [self-hosted, FlipperZeroShell]\n    strategy:\n      fail-fast: false\n      matrix:\n        target: [f7, f18]\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Get commit details'\n        id: names\n        run: |\n          BUILD_TYPE='DEBUG=1 COMPACT=0'\n          if [[ ${{ github.event_name }} == 'pull_request' ]]; then\n            TYPE=\"pull\"\n          elif [[ \"${{ github.ref }}\" == \"refs/tags/\"* ]]; then\n            TYPE=\"tag\"\n            BUILD_TYPE='DEBUG=0 COMPACT=1'\n          else\n            TYPE=\"other\"\n          fi\n          python3 scripts/get_env.py \"--event_file=${{ github.event_path }}\" \"--type=$TYPE\" || cat \"${{ github.event_path }}\"\n          echo \"event_type=$TYPE\" >> $GITHUB_OUTPUT\n          echo \"FBT_BUILD_TYPE=$BUILD_TYPE\" >> $GITHUB_ENV\n          echo \"TARGET=${{ matrix.target }}\" >> $GITHUB_ENV\n          echo \"TARGET_HW=$(echo \"${{ matrix.target }}\" | sed 's/f//')\" >> $GITHUB_ENV\n\n      - name: 'Check API versions for consistency between targets'\n        run: |\n          set -e\n          N_API_HEADER_SIGNATURES=`ls -1 targets/f*/api_symbols.csv | xargs -I {} sh -c \"head -n2 {} | md5sum\" | sort -u | wc -l`\n          if [ $N_API_HEADER_SIGNATURES != 1 ] ; then\n            echo API versions aren\\'t matching for available targets. Please update!\n            echo API versions are:\n            head -n2 targets/f*/api_symbols.csv\n            exit 1\n          fi\n\n      - name: 'Build the firmware and apps'\n        id: build-fw\n        run: |\n          ./fbt TARGET_HW=$TARGET_HW $FBT_BUILD_TYPE copro_dist updater_package fap_dist\n          echo \"firmware_api=$(./fbt TARGET_HW=$TARGET_HW get_apiversion)\" >> $GITHUB_OUTPUT\n\n      - name: 'Check for uncommitted changes'\n        run: |\n          git diff --exit-code\n\n      - name: 'Copy build output'\n        run: |\n          set -e\n          rm -rf artifacts map_analyser_files || true\n          mkdir artifacts map_analyser_files\n          cp dist/${TARGET}-*/* artifacts/ || true\n          tar czpf \"artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz\" \\\n            -C build/latest resources\n          tar czpf \"artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz\" \\\n            -C dist/${TARGET}-*/apps/Debug .\n          tar czpf \"artifacts/flipper-z-${TARGET}-appsymbols-${SUFFIX}.tgz\" \\\n            -C dist/${TARGET}-*/debug_elf .\n\n      - name: 'Copy universal artifacts'\n        if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET }}\n        run: |\n          tar czpf \"artifacts/flipper-z-any-scripts-${SUFFIX}.tgz\" scripts\n          cp build/core2_firmware.tgz \"artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz\"\n\n      - name: 'Upload artifacts to update server'\n        if: ${{ !github.event.pull_request.head.repo.fork }}\n        run: |\n          FILES=$(for ARTIFACT in $(find artifacts -maxdepth 1 -not -type d); do echo \"-F files=@${ARTIFACT}\"; done)\n          curl --fail -L -H \"Token: ${{ secrets.INDEXER_TOKEN }}\" \\\n              -F \"branch=${BRANCH_NAME}\" \\\n              -F \"version_token=${COMMIT_SHA}\" \\\n              ${FILES[@]} \\\n              \"${{ secrets.INDEXER_URL }}\"/firmware/uploadfiles\n\n      - name: 'Copy & analyse map analyser files'\n        if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET }}\n        run: |\n          cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map\n          cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf\n          source scripts/toolchain/fbtenv.sh\n          python3 scripts/map_analyse_upload.py \\\n              \"--elf_file=map_analyser_files/firmware.elf\" \\\n              \"--map_file=map_analyser_files/firmware.elf.map\" \\\n              \"--analyser_url=${{ secrets.ANALYSER_URL }}\" \\\n              \"--analyser_token=${{ secrets.ANALYSER_TOKEN }}\";\n\n      - name: 'Find previous comment'\n        if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET && github.event.pull_request }}\n        uses: peter-evans/find-comment@v3\n        id: find-comment\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          comment-author: 'github-actions[bot]'\n          body-includes: 'Compiled ${{ matrix.target }} firmware for commit'\n\n      - name: 'Create or update comment'\n        if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET && github.event.pull_request }}\n        uses: peter-evans/create-or-update-comment@v4\n        with:\n          comment-id: ${{ steps.find-comment.outputs.comment-id }}\n          issue-number: ${{ github.event.pull_request.number }}\n          body: |\n            **Compiled ${{ matrix.target }} firmware for commit `${{steps.names.outputs.commit_sha}}`:**\n            - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz)\n            - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu)\n            - [☁️ Web/App updater](https://lab.flipper.net/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}})\n            - [📊 Size report](https://fw-reports.flipp.dev/?branch=${{steps.names.outputs.branch_name}})\n          edit-mode: replace\n\n      - name: 'SDK submission to staging catalog'\n        if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }}\n        uses: ./.github/actions/submit_sdk\n        with:\n          catalog-url: ${{ secrets.CATALOG_STAGING_URL }}\n          catalog-api-token: ${{ secrets.CATALOG_STAGING_API_TOKEN }}\n          firmware-api: ${{ steps.build-fw.outputs.firmware_api }}\n          firmware-target: ${{ matrix.target }}\n          firmware-version: ${{ steps.names.outputs.suffix }}\n\n      - name: 'SDK submission to prod catalog'\n        if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }}\n        uses: ./.github/actions/submit_sdk\n        with:\n          catalog-url: ${{ secrets.CATALOG_URL }}\n          catalog-api-token: ${{ secrets.CATALOG_API_TOKEN }}\n          firmware-api: ${{ steps.build-fw.outputs.firmware_api }}\n          firmware-target: ${{ matrix.target }}\n          firmware-version: ${{ steps.names.outputs.suffix }}\n"
  },
  {
    "path": ".github/workflows/build_compact.yml",
    "content": "name: 'Compact build'\n\non:\n  pull_request:\n\nenv:\n  FBT_TOOLCHAIN_PATH: /runner/_work\n  FBT_GIT_SUBMODULE_SHALLOW: 1\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  compact:\n    runs-on: [self-hosted, FlipperZeroShell]\n    strategy:\n      fail-fast: false\n      matrix:\n        target: [f7, f18]\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          submodules: false\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Get commit details'\n        run: |\n          if [[ ${{ github.event_name }} == 'pull_request' ]]; then\n            TYPE=\"pull\"\n          elif [[ \"${{ github.ref }}\" == \"refs/tags/\"* ]]; then\n            TYPE=\"tag\"\n          else\n            TYPE=\"other\"\n          fi\n          python3 scripts/get_env.py \"--event_file=${{ github.event_path }}\" \"--type=$TYPE\" || cat \"${{ github.event_path }}\"\n\n      - name: 'Build the firmware'\n        id: build-fw\n        run: |\n          set -e\n          TARGET=\"$(echo '${{ matrix.target }}' | sed 's/f//')\"; \\\n          ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package\n          echo \"sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)\" >> $GITHUB_OUTPUT\n          echo \"hw-target-code=$TARGET\" >> $GITHUB_OUTPUT\n\n      - name: Deploy uFBT with SDK\n        uses: flipperdevices/flipperzero-ufbt-action@v0.1\n        with:\n          task: setup\n          sdk-file: ${{ steps.build-fw.outputs.sdk-file }}\n          sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }}\n\n      - name: Build test app with SDK\n        run: |\n          mkdir testapp\n          cd testapp\n          ufbt create APPID=testapp\n          ufbt\n\n      - name: Build example & external apps with uFBT\n        run: |\n          for appdir in 'applications/examples'; do\n            for app in $(find \"$appdir\" -maxdepth 1 -mindepth 1 -type d); do\n              pushd $app\n              TARGETS_FAM=$(grep \"targets\" application.fam || echo \"${{ matrix.target }}\")\n              if ! grep -q \"${{ matrix.target }}\" <<< $TARGETS_FAM ; then\n                  echo Skipping unsupported app: $app\n                  popd\n                  continue\n              fi\n              echo Building $app\n              ufbt\n              popd\n            done\n          done\n\n## Uncomment this for a single job that will run only if all targets are built successfully          \n#  report-status:\n#    name: Report status\n#    needs: [compact]\n#    if: always() && !contains(needs.*.result, 'failure')\n#    runs-on: [self-hosted, FlipperZeroShell]\n#    steps:\n#      - run: echo \"All good ✨\" ;\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: 'Generate documentation with Doxygen'\n\non:\n  push:\n    branches:\n      - dev\n  pull_request:\n\nenv:\n  TARGETS: f7\n  DEFAULT_TARGET: f7\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  check-secret:\n    if: ${{ github.event.pull_request.head.repo.fork == false }}\n    runs-on: ubuntu-latest\n    outputs:\n      s3-valid-config: ${{ steps.check.outputs.s3-valid-config }}\n\n    steps:\n      - name: 'Check if S3 key exists'\n        id: check\n        run: |\n          if [[ -z \"${{ secrets.FW_DOCS_AWS_ACCESS_KEY }}\" || -z \"${{ secrets.FW_DOCS_AWS_SECRET_KEY }}\" || -z \"${{ secrets.FW_DOCS_AWS_BUCKET }}\" ]]; then\n            echo \"s3-valid-config=false\" >> $GITHUB_OUTPUT;\n          else\n            echo \"s3-valid-config=true\" >> $GITHUB_OUTPUT;\n          fi\n\n  doxygen:\n    if: ${{ github.event.pull_request.head.repo.fork == false }}\n    runs-on: ubuntu-latest\n    needs: check-secret\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Get commit details'\n        id: names\n        run: |\n          if [[ ${{ github.event_name }} == 'pull_request' ]]; then\n            TYPE=\"pull\"\n          elif [[ \"${{ github.ref }}\" == \"refs/tags/\"* ]]; then\n            TYPE=\"tag\"\n          else\n            TYPE=\"other\"\n          fi\n          python3 scripts/get_env.py \"--event_file=${{ github.event_path }}\" \"--type=$TYPE\"\n\n      - name: install-doxygen\n        uses: AdarshRawat1/Install-Doxygen@v1.0\n        with:\n          version: \"1.12.0\"\n\n      - name: 'Generate documentation'\n        run: ./fbt doxygen\n\n      - name: 'Upload documentation'\n        if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && needs.check-secret.outputs.s3-valid-config == 'true' }}\n        uses: jakejarvis/s3-sync-action@v0.5.1\n        env:\n          AWS_S3_BUCKET: \"${{ secrets.FW_DOCS_AWS_BUCKET }}\"\n          AWS_ACCESS_KEY_ID: \"${{ secrets.FW_DOCS_AWS_ACCESS_KEY }}\"\n          AWS_SECRET_ACCESS_KEY: \"${{ secrets.FW_DOCS_AWS_SECRET_KEY }}\"\n          AWS_REGION: \"${{ secrets.FW_DOCS_AWS_REGION }}\"\n          SOURCE_DIR: \"./documentation/doxygen/build/html\"\n          DEST_DIR: \"${{steps.names.outputs.branch_name}}\"\n        with:\n          args: \"--delete\"\n"
  },
  {
    "path": ".github/workflows/lint_and_submodule_check.yml",
    "content": "name: 'Lint sources & check submodule integrity'\n\non:\n  pull_request:\n\nenv:\n  TARGETS: f7\n  FBT_TOOLCHAIN_PATH: /runner/_work\n  SET_GH_OUTPUT: 1\n\njobs:\n  lint_sources_check_submodules:\n    runs-on: [self-hosted, FlipperZeroShell]\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 2\n          ref: ${{ github.sha }}\n\n      - name: 'Check protobuf branch'\n        run: |\n          git submodule update --init;\n          SUB_PATH=\"assets/protobuf\";\n          SUB_BRANCH=\"dev\";\n          SUB_COMMITS_MIN=40;\n          cd \"$SUB_PATH\";\n          SUBMODULE_HASH=\"$(git rev-parse HEAD)\";\n          BRANCHES=$(git branch -r --contains \"$SUBMODULE_HASH\");\n          COMMITS_IN_BRANCH=\"$(git rev-list --count dev)\";\n          if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then\n            echo \"name=fails::error\" >> $GITHUB_OUTPUT;\n            echo \"::error::Error: Too few commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)\";\n            exit 1;\n          fi\n          if ! grep -q \"/$SUB_BRANCH\" <<< \"$BRANCHES\"; then\n            echo \"name=fails::error\" >> $GITHUB_OUTPUT;\n            echo \"::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH\";\n            exit 1;\n          fi\n\n      - name: 'Check for new TODOs'\n        id: check_todos\n        if: github.event_name == 'pull_request'\n        run: |\n          set +e;\n          git diff --unified=0 --no-color ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '^\\+' | grep -i -E '(TODO|HACK|FIXME|XXX)[ :]' | grep -v -- '-nofl' > lines.log;\n          MISSING_TICKETS=$( grep -v -E 'FL-[0-9]+' lines.log );\n          if [ -n \"$MISSING_TICKETS\" ]; then\n            echo \"Error: Missing ticket number in \\`TODO\\` comment(s)\" >> $GITHUB_STEP_SUMMARY;\n            echo \"\\`\\`\\`\" >> $GITHUB_STEP_SUMMARY;\n            echo \"$MISSING_TICKETS\" >> $GITHUB_STEP_SUMMARY;\n            echo \"\\`\\`\\`\" >> $GITHUB_STEP_SUMMARY;\n            echo \"Error: Missing issue number in comment(s):\";\n            echo \"$MISSING_TICKETS\";\n            exit 1;\n          else\n            echo \"No new TODOs without tickets found\" >> $GITHUB_STEP_SUMMARY;\n          fi\n\n      - name: 'Check Python code formatting'\n        id: syntax_check_py\n        if: always()\n        run: |\n          set +e;\n          ./fbt -s lint_py 2>&1 | tee lint-py.log;\n          if [ \"${PIPESTATUS[0]}\" -ne 0 ]; then\n            # Save multiline output\n            echo \"errors=1\" >> $GITHUB_OUTPUT;\n            printf \"Python Lint errors:\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            echo \"$(cat lint-py.log)\" >> $GITHUB_STEP_SUMMARY;\n            printf \"\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            exit 1;\n          else\n            echo \"Python Lint: all good ✨\" >> $GITHUB_STEP_SUMMARY;\n          fi\n\n      - name: 'Check C++ code formatting'\n        id: syntax_check_cpp\n        if: always()\n        run: |\n          set +e;\n          ./fbt -s lint 2>&1 | tee lint-cpp.log;\n          if [ \"${PIPESTATUS[0]}\" -ne 0 ]; then\n            # Save multiline output\n            echo \"errors=1\" >> $GITHUB_OUTPUT;\n            printf \"C Lint errors:\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            echo \"$(cat lint-cpp.log)\" >> $GITHUB_STEP_SUMMARY;\n            printf \"\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            exit 1;\n          else\n            echo \"C Lint: all good ✨\" >> $GITHUB_STEP_SUMMARY;\n          fi\n\n      - name: 'Check image assets'\n        if: always()\n        run: |\n          set +e;\n          ./fbt -s lint_img 2>&1 | tee lint-assets.log;\n          if [ \"${PIPESTATUS[0]}\" -ne 0 ]; then\n            # Save multiline output\n            echo \"errors=1\" >> $GITHUB_OUTPUT;\n            printf \"Image Lint errors:\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            echo \"$(cat lint-assets.log)\" >> $GITHUB_STEP_SUMMARY;\n            printf \"\\n\\`\\`\\`\\n\" >> $GITHUB_STEP_SUMMARY;\n            exit 1;\n          else\n            echo \"Image Lint: all good ✨\" >> $GITHUB_STEP_SUMMARY;\n          fi\n\n      - name: Report code formatting errors\n        if: ( steps.syntax_check_py.outputs.errors || steps.syntax_check_cpp.outputs.errors ) && github.event.pull_request\n        run: |\n          echo \"Code formatting errors found\";\n          echo \"Please run './fbt format' or './fbt format_py' to fix them\";\n          exit 1;\n"
  },
  {
    "path": ".github/workflows/merge_report.yml",
    "content": "name: 'Check FL ticket in PR name'\n\non:\n  push:\n    branches:\n      - dev\n\nenv:\n  FBT_TOOLCHAIN_PATH: /runner/_work\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  merge_report:\n    runs-on: [self-hosted, FlipperZeroShell]\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Get commit details'\n        run: |\n          if [[ ${{ github.event_name }} == 'pull_request' ]]; then\n            TYPE=\"pull\"\n          elif [[ \"${{ github.ref }}\" == \"refs/tags/\"* ]]; then\n            TYPE=\"tag\"\n          else\n            TYPE=\"other\"\n          fi\n          python3 scripts/get_env.py \"--event_file=${{ github.event_path }}\" \"--type=$TYPE\" || cat \"${{ github.event_path }}\"\n\n      - name: 'Check ticket and report'\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          python3 -m pip install slack_sdk\n          python3 scripts/merge_report_qa.py \\\n              ${{ secrets.QA_REPORT_SLACK_TOKEN }} \\\n              ${{ secrets.QA_REPORT_SLACK_CHANNEL }}\n\n"
  },
  {
    "path": ".github/workflows/pvs_studio.yml",
    "content": "name: 'Static C/C++ analysis with PVS-Studio'\n\non:\n  push:\n    branches:\n      - dev\n  pull_request:\n\nenv:\n  TARGETS: f7\n  DEFAULT_TARGET: f7\n  FBT_TOOLCHAIN_PATH: /runner/_work\n  FBT_GIT_SUBMODULE_SHALLOW: 1\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  analyse_c_cpp:\n    if: ${{ !github.event.pull_request.head.repo.fork }}\n    runs-on: [self-hosted, FlipperZeroShell]\n    steps:\n      - name: 'Wipe workspace'\n        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \\;\n\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Get commit details'\n        id: names\n        run: |\n          if [[ ${{ github.event_name }} == 'pull_request' ]]; then\n            TYPE=\"pull\"\n          elif [[ \"${{ github.ref }}\" == \"refs/tags/\"* ]]; then\n            TYPE=\"tag\"\n          else\n            TYPE=\"other\"\n          fi\n          python3 scripts/get_env.py \"--event_file=${{ github.event_path }}\" \"--type=$TYPE\" || cat \"${{ github.event_path }}\"\n\n      - name: 'Supply PVS credentials'\n        run: |\n          pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}\n\n      - name: 'Convert PVS-Studio output to html and detect warnings'\n        id: pvs-warn\n        run: |\n          WARNINGS=0\n          ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1\n          echo \"warnings=${WARNINGS}\" >> $GITHUB_OUTPUT\n          if [[ $WARNINGS -ne 0 ]]; then\n            echo \"report-url=https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html\" >> $GITHUB_OUTPUT\n          fi\n\n      - name: 'Upload report'\n        if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }}\n        uses: prewk/s3-cp-action@v2\n        with:\n          aws_s3_endpoint: \"${{ secrets.PVS_AWS_ENDPOINT }}\"\n          aws_access_key_id: \"${{ secrets.PVS_AWS_ACCESS_KEY }}\"\n          aws_secret_access_key: \"${{ secrets.PVS_AWS_SECRET_KEY }}\"\n          source: \"./build/f7-firmware-DC/pvsreport\"\n          dest: \"s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/\"\n          flags: \"--recursive --acl public-read\"\n\n      - name: 'Find Previous Comment'\n        if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }}\n        uses: peter-evans/find-comment@v3\n        id: fc\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          comment-author: 'github-actions[bot]'\n          body-includes: 'PVS-Studio report for commit'\n\n      - name: 'Create or update comment'\n        if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }}\n        uses: peter-evans/create-or-update-comment@v4\n        with:\n          comment-id: ${{ steps.fc.outputs.comment-id }}\n          issue-number: ${{ github.event.pull_request.number }}\n          body: |\n            **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:**\n            - [Report](${{ steps.pvs-warn.outputs.report-url }})\n          edit-mode: replace\n\n      - name: 'Raise exception'\n        if: ${{ steps.pvs-warn.outputs.warnings != 0 }}\n        run: |\n          echo \"Please fix all PVS warnings before merge\"\n          echo \"Report: ${{ steps.pvs-warn.outputs.report-url }}\"\n          echo \"[PVS report](${{ steps.pvs-warn.outputs.report-url }})\" >> $GITHUB_STEP_SUMMARY\n          exit 1\n"
  },
  {
    "path": ".github/workflows/reindex.yml",
    "content": "name: 'Post-release hooks'\n\non:\n  release:\n    types: [prereleased, released]\n\njobs:\n  reindex:\n    name: 'Post-release hooks'\n    runs-on: [self-hosted, FlipperZeroShell]\n    steps:\n      - name: 'Checkout code'\n        uses: actions/checkout@v4\n\n      - name: 'Trigger reindex'\n        run: |\n          curl --fail -L -H \"Token: ${{ secrets.INDEXER_TOKEN }}\" \\\n              \"${{ secrets.INDEXER_URL }}\"/firmware/reindex;\n\n      - name: 'Send release notification'\n        if: ${{ github.event.action == 'released' }}\n        run: |\n          echo '${{ secrets.FIREBASE_TOKEN }}' > firebase-token.json;\n          python3 -m pip install firebase-admin==6.4.0;\n          python3 scripts/send_firebase_notification.py \\\n              \"--version=${{ github.event.release.name }}\" \\\n              \"--token=firebase-token.json\";\n\n      - name: 'Remove firebase token'\n        if: always()\n        run: |\n          rm -rf firebase-token.json;\n"
  },
  {
    "path": ".github/workflows/unit_tests.yml",
    "content": "name: 'Unit tests'\non:\n  pull_request:\n\nenv:\n  TARGETS: f7\n  DEFAULT_TARGET: f7\n  FBT_TOOLCHAIN_PATH: /opt/\n  FBT_GIT_SUBMODULE_SHALLOW: 1\n\njobs:\n  run_units_on_bench:\n    runs-on: [ self-hosted, FlipperZeroTest ]\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Flash unit tests firmware'\n        id: flashing\n        if: success()\n        timeout-minutes: 5\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          ./fbt resources firmware_latest flash LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1\n\n\n      - name: 'Copy assets and unit data, reboot and wait for flipper'\n        id: copy\n        if: steps.flashing.outcome == 'success'\n        timeout-minutes: 5\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          python3 scripts/testops.py -t=15 await_flipper\n          python3 scripts/storage.py -f send build/latest/resources /ext\n          python3 scripts/storage.py -f send /region_data /ext/.int/.region_data\n          python3 scripts/power.py reboot\n          python3 scripts/testops.py -t=30 await_flipper\n\n      - name: 'Run units and validate results'\n        id: run_units\n        if: steps.copy.outcome == 'success'\n        timeout-minutes: 5\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          python3 scripts/testops.py run_units\n\n      - name: 'Upload test results'\n        if: failure() && steps.flashing.outcome == 'success' && steps.run_units.outcome != 'skipped'\n        uses: actions/upload-artifact@v4\n        with:\n          name: unit-tests_output\n          path: unit_tests*.txt\n\n      - name: 'Check GDB output'\n        if: failure() && steps.flashing.outcome == 'success'\n        run: |\n          ./fbt gdb_trace_all LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1\n"
  },
  {
    "path": ".github/workflows/updater_test.yml",
    "content": "name: 'Updater test'\non:\n  pull_request:\n\nenv:\n  TARGETS: f7\n  DEFAULT_TARGET: f7\n  FBT_TOOLCHAIN_PATH: /opt/\n  FBT_GIT_SUBMODULE_SHALLOW: 1\n\njobs:\n  test_updater_on_bench:\n    runs-on: [self-hosted, FlipperZeroTest ]\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: 'Flashing target firmware'\n        id: first_full_flash\n        timeout-minutes: 5\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          python3 scripts/testops.py -t=180 await_flipper\n          ./fbt flash_usb_full FORCE=1\n          \n\n      - name: 'Validating updater'\n        id: second_full_flash\n        timeout-minutes: 5\n        if: success()\n        run: |\n          source scripts/toolchain/fbtenv.sh\n          python3 scripts/testops.py -t=180 await_flipper\n          ./fbt flash_usb FORCE=1\n          python3 scripts/testops.py -t=180 await_flipper\n\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n*.swp\n*.swo\n*.gdb_history\n*.old\n\n\n# LSP\n.cache\ncompile_commands.json\n\n# JetBrains IDEs\n.idea/\n\n# Sublime Text\n.sublime-project.sublime-workspace\n\n# Python VirtEnvironments\n.env\n.venv\nenv/\nvenv/\n\n# Python Byte-compiled / optimized files\n__pycache__/\n*.py[cod]\n*$py.class\n*.pickle\n\n.obj/\nbindings/\n.DS_Store\n.mxproject\nBrewfile.lock.json\n\n# Kate\n.kateproject\n.kateconfig\n\n# kde\n.directory\n\n# SCons\n.sconsign.dblite\n\n\n# bundle output\n/dist\n\n# SCons build dir\n/build\n\n# Toolchain\n/toolchain\n\n# openocd output file\nopenocd.log\n\n# PVS Studio temporary files\n.PVS-Studio/\nPVS-Studio.log\n*.PVS-Studio.*\n\n.gdbinit\n\n/fbt_options_local.py\n\n# JS packages\nnode_modules/\n\n# cli_perf script output in case of errors\n/block.bin\n/return_block.bin\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/mlib\"]\n\tpath = lib/mlib\n\turl = https://github.com/P-p-H-d/mlib.git\n[submodule \"lib/nanopb\"]\n\tpath = lib/nanopb\n\turl = https://github.com/nanopb/nanopb.git\n[submodule \"assets/protobuf\"]\n\tpath = assets/protobuf\n\turl = https://github.com/flipperdevices/flipperzero-protobuf.git\n\tshallow = false\n[submodule \"lib/libusb_stm32\"]\n\tpath = lib/libusb_stm32\n\turl = https://github.com/flipperdevices/libusb_stm32.git\n[submodule \"lib/FreeRTOS-Kernel\"]\n\tpath = lib/FreeRTOS-Kernel\n\turl = https://github.com/FreeRTOS/FreeRTOS-Kernel.git\n[submodule \"lib/microtar\"]\n\tpath = lib/microtar\n\turl = https://github.com/amachronic/microtar.git\n[submodule \"lib/mbedtls\"]\n\tpath = lib/mbedtls\n\turl = https://github.com/Mbed-TLS/mbedtls.git\n[submodule \"lib/heatshrink\"]\n\tpath = lib/heatshrink\n\turl = https://github.com/flipperdevices/heatshrink.git\n[submodule \"lib/st_cmsis_device_wb\"]\n\tpath = lib/stm32wb_cmsis\n\turl = https://github.com/STMicroelectronics/cmsis_device_wb\n[submodule \"lib/stm32wbxx_hal_driver\"]\n\tpath = lib/stm32wb_hal\n\turl = https://github.com/STMicroelectronics/stm32wbxx_hal_driver\n[submodule \"lib/stm32wb_copro\"]\n\tpath = lib/stm32wb_copro\n    url = https://github.com/flipperdevices/stm32wb_copro.git\n[submodule \"documentation/doxygen/doxygen-awesome-css\"]\n\tpath = documentation/doxygen/doxygen-awesome-css\n\turl = https://github.com/jothepro/doxygen-awesome-css.git\n"
  },
  {
    "path": ".pvsconfig",
    "content": "# MLib macros we can't do much about.\n//-V:M_LET:1048,1044\n//-V:M_EACH:1048,1044\n//-V:ARRAY_DEF:760,747,568,776,729,712,654,1103\n//-V:LIST_DEF:760,747,568,712,729,654,776,1103\n//-V:LIST_DUAL_PUSH_DEF:524,760,774\n//-V:BPTREE_DEF2:779,1086,557,773,512\n//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685,1103\n//-V:ALGO_DEF:1048,747,1044\n//-V:TUPLE_DEF2:524,590,1001,760\n//-V:DEQUE_DEF:658,747,760\n\n# Non-severe malloc/null pointer deref warnings\n//-V::522:2,3\n\n# Warning about headers with copyleft license\n//-V::1042\n\n# Potentially null argument warnings\n//-V:memset:575\n//-V:memcpy:575\n//-V:memcmp:575\n//-V:strlen:575\n//-V:strcpy:575\n//-V:strncpy:575\n//-V:strchr:575\n\n# For loop warning on M_FOREACH\n//-V:for:1044\n\n# Bitwise OR\n//-V:bit:792\n\n# Do not complain about similar code\n//-V::525\n\n# Common embedded development pointer operations\n//-V::566\n//-V::1032\n\n# Warnings about length mismatch\n//-V:property_value_out:666\n\n# Model-related warnings\n//-V:with_view_model:1044,1048\n\n# Examples\n//V_EXCLUDE_PATH applications/examples/\n"
  },
  {
    "path": ".pvsoptions",
    "content": "--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e lib/mjs -e */arm-none-eabi/*\n"
  },
  {
    "path": ".sublime-project",
    "content": "{\n    \"folders\":\n    [\n        {\n            \"path\": \".\",\n        }\n    ],\n    \"settings\": {\n        \"LSP\": {\n            \"clangd\": {\n                \"enabled\": true,\n                \"initializationOptions\": {\n                    // Set `\"binary\": \"custom\",` option in LSP-clangd config to use toolchain clangd\n                    \"custom_command\": [\"toolchain/current/bin/clangd\"],\n                    \"clangd.compile-commands-dir\": \"build/latest\",\n                    \"clangd.header-insertion\": \"never\",\n                    \"clangd.query-driver\": \"**/arm-none-eabi-*\",\n                    \"clangd.clang-tidy\": true,\n                },\n            },\n        },\n   },\n}\n"
  },
  {
    "path": ".vscode/ReadMe.md",
    "content": "# Visual Studio Code workspace for Flipper Zero {#vscode}\n\n## Setup\n\n * To start developing with VSCode, run `./fbt vscode_dist` in project root. _That should only be done once_\n * After that, open firmware folder in VSCode: \"File\" > \"Open folder\"\n\n For more details on fbt, see [fbt docs](../documentation/fbt.md).\n\n\n## Workflow\n\nCommands for building firmware are invoked through Build menu: Ctrl+Shift+B.\n\nTo attach a debugging session, first build and flash firmware, then choose your debug probe in Debug menu (Ctrl+Shift+D).\n\nNote that you have to detach debugging session before rebuilding and re-flashing firmware.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nhello@flipperdevices.com.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CODING_STYLE.md",
    "content": "# Intro\n\nNice to see you reading this document, we really appreciate it.\n\nAs all documents of this kind it's unable to cover everything.\nBut it will cover general rules that we are enforcing on PR review.\n\nAlso, we already have automatic rules checking and formatting,\nbut it got its limitations and this guide is still mandatory.\n\nSome part of this project do have its own naming and coding guides.\nFor example: assets. Take a look into `ReadMe.md` in assets folder for more details.\n\nAlso, 3rd party libraries are none of our concern.\n\nAnd yes, this set is not final and we are open to discussion.\nIf you want to add/remove/change something here please feel free to open new ticket.\n\n# Inspiration\n\nOur guide is inspired by, but not claiming to be compatible with:\n\n- https://www.kernel.org/doc/html/v4.10/process/coding-style.html\n- https://docs.unrealengine.com/en-US/Programming/Development/CodingStandard\n- https://webkit.org/code-style-guidelines/\n\n# General rules\n\n## Readability and Simplicity first\n\nCode we write is intended to be public.\nAvoid one-liners from hell and keep code complexity under control.\nTry to make code self-explanatory and add comments if needed.\nLeave references to standards that you are implementing.\nUse project wiki to document new/reverse engineered standards.\n\n## Variable and function names must clearly define what it's doing\n\nIt's ok if it will be long, but it should clearly state what it's doing, without need to dive into code.\nThis also applies to function/method's code.\nTry to avoid one letter variables.\n\n## Encapsulation\n\nDon't expose raw data, provide methods to work with it.\nAlmost everything in flipper firmware is built around this concept.\n\n# C coding style\n\n- Tab is 4 spaces\n- Use `./fbt format` to reformat source code and check style guide before commit\n\n## Naming\n\n### Type names are PascalCase\n\nExamples:\n\n\tFuriHalUsb\n\tGui\n\tSubGhzKeystore\n\n\n### Functions are snake_case\n\n\tfuri_hal_usb_init\n\tgui_add_view_port\n\tsubghz_keystore_read\n\n### File and Package name is a prefix for it's content\n\nThis rule makes easier to locate types, functions and sources.\n\nFor example:\n\nWe have abstraction that we call `SubGhz Keystore`, so there will be:\nfile `subghz_keystore.h` we have type `SubGhzKeystore` and function `subghz_keystore_read`.\n\n### File names\n\n- Directories: `^[0-9A-Za-z_]+$`\n- File names: `^[0-9A-Za-z_]+\\.[a-z]+$`\n- File extensions: `[ \".h\", \".c\", \".cpp\", \".cxx\", \".hpp\" ]`\n\nEnforced by linter.\n\n### Standard function/method names\n\nSuffixes:\n\n- `alloc` - allocate and init instance. C style constructor. Returns pointer to instance.\n- `free` - de-init and release instance. C style destructor. Takes pointer to instance.\n\n# C++ coding style\n\nWork In Progress. Use C style guide as a base.\n\n# Python coding style\n\n- Tab is 4 spaces\n- Use [black](https://pypi.org/project/black/) to reformat source code before commit\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Welcome to FlipperZero contributing guide <!-- omit in toc -->\n\nThank you for investing your time in contributing to our project! \n\nRead our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable.\n\nIn this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.\n\n## New contributor guide\n\nSee the [ReadMe](ReadMe.md) to get an overview of the project. Here are some helpful resources to get you comfortable with open source contribution:\n\n- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)\n- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git)\n- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)\n- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests)\n\n## Getting started\n\nBefore writing code and creating PR make sure that it aligns with our mission and guidelines:\n\n- All our devices are intended for research and education.\n- PR that contains code intended to commit crimes is not going to be accepted.\n- Your PR must comply with our [Coding Style](CODING_STYLE.md)\n- Your PR must contain code compatible with project [LICENSE](LICENSE).\n- PR will only be merged if it passes CI/CD.\n- PR will only be merged if it passes review by code owner.\n\nFeel free to ask questions in issues if you're not sure.\n\n### Issues\n\n#### Create a new issue\n\nIf you found a problem, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/flipperdevices/flipperzero-firmware/issues/new/choose). \n\n#### Solve an issue\n\nScan through our [existing issues](https://github.com/flipperdevices/flipperzero-firmware/issues) to find one that interests you.\n\n### Make Changes\n\n1. Fork the repository.\n- Using GitHub Desktop:\n  - [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop.\n  - Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)!\n\n- Using the command line:\n  - [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them.\n\n2. Install build requirements\n\n3. Create a working branch and start with your changes!\n\n### Commit your update\n\nCommit the changes once you are happy with them. Make sure that code compilation is not broken and passes tests. Check syntax and formatting.\n\n### Pull Request\n\nWhen you're done making the changes, open a pull request, often referred to as a PR. \n- Fill out the \"Ready for review\" template, so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request. \n- Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one.\n- Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge.\nOnce you submit your PR, a Docs team member will review your proposal. We may ask questions or request for additional information.\n- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.\n- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).\n- If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues.\n\n### Your PR is merged!\n\nCongratulations :tada::tada: The FlipperDevices team thanks you :sparkles:.\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "ReadMe.md",
    "content": "<picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"/.github/assets/dark_theme_banner.png\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"/.github/assets/light_theme_banner.png\">\n    <img\n        alt=\"A pixel art of a Dophin with text: Flipper Zero Official Repo\"\n        src=\"/.github/assets/light_theme_banner.png\">\n</picture>\n\n# Flipper Zero Firmware\n\n- [Flipper Zero Official Website](https://flipperzero.one). A simple way to explain to your friends what Flipper Zero can do.\n- [Flipper Zero Firmware Update](https://flipperzero.one/update). Improvements for your dolphin: latest firmware releases, upgrade tools for PC and mobile devices.\n- [User Documentation](https://docs.flipper.net). Learn more about your dolphin: specs, usage guides, and anything you want to ask.\n- [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen). Dive into the Flipper Zero Firmware source code: build system, firmware structure, and more.\n\n# Contributing\n\nOur main goal is to build a healthy and sustainable community around Flipper, so we're open to any new ideas and contributions. We also have some rules and taboos here, so please read this page and our [Code of Conduct](/CODE_OF_CONDUCT.md) carefully.\n\n## I need help\n\nThe best place to search for answers is our [User Documentation](https://docs.flipper.net). If you can't find the answer there, check our [Discord Server](https://flipp.dev/discord) or our [Forum](https://forum.flipperzero.one/). If you want to contribute to the firmware development or modify it for your own needs, you can also check our [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen).\n\n## I want to report an issue\n\nIf you've found an issue and want to report it, please check our [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page. Make sure the description contains information about the firmware version you're using, your platform, and a clear explanation of the steps to reproduce the issue.\n\n## I want to contribute code\n\nBefore opening a PR, please confirm that your changes must be contained in the firmware. Many ideas can easily be implemented as external applications and published in the [Flipper Application Catalog](https://github.com/flipperdevices/flipper-application-catalog). If you are unsure, reach out to us on the [Discord Server](https://flipp.dev/discord) or the [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page, and we'll help you find the right place for your code.\n\nAlso, please read our [Contribution Guide](/CONTRIBUTING.md) and our [Coding Style](/CODING_STYLE.md), and make sure your code is compatible with our [Project License](/LICENSE).\n\nFinally, open a [Pull Request](https://github.com/flipperdevices/flipperzero-firmware/pulls) and make sure that CI/CD statuses are all green.\n\n# Development\n\nFlipper Zero Firmware is written in C, with some bits and pieces written in C++ and armv7m assembly languages. An intermediate level of C knowledge is recommended for comfortable programming. C, C++, and armv7m assembly languages are supported for Flipper applications.\n\n# Firmware RoadMap\n\n[Firmware RoadMap Miro Board](https://miro.com/app/board/uXjVO_3D6xU=/)\n\n## Requirements\n\nSupported development platforms:\n\n- Windows 10+ with PowerShell and Git (x86_64)\n- macOS 12+ with Command Line tools (x86_64, arm64)\n- Ubuntu 20.04+ with build-essential and Git (x86_64)\n\nSupported in-circuit debuggers (optional but highly recommended):\n\n- [Flipper Zero Wi-Fi Development Board](https://shop.flipperzero.one/products/wifi-devboard)\n- CMSIS-DAP compatible: Raspberry Pi Debug Probe and etc...\n- ST-Link (v2, v3, v3mods)\n- J-Link\n\nFlipper Build System will take care of all the other dependencies.\n\n## Cloning source code\n\nMake sure you have enough space and clone the source code:\n\n```shell\ngit clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git\n```\n\n## Building\n\nBuild firmware using Flipper Build Tool:\n\n```shell\n./fbt\n```\n\n## Flashing firmware using an in-circuit debugger\n\nConnect your in-circuit debugger to your Flipper and flash firmware using Flipper Build Tool:\n\n```shell\n./fbt flash\n```\n\n## Flashing firmware using USB\n\nMake sure your Flipper is on, and your firmware is functioning. Connect your Flipper with a USB cable and flash firmware using Flipper Build Tool:\n\n```shell\n./fbt flash_usb\n```\n\n## Documentation\n\n- [Flipper Build Tool](/documentation/fbt.md) - building, flashing, and debugging Flipper software\n- [Applications](/documentation/AppsOnSDCard.md), [Application Manifest](/documentation/AppManifests.md) - developing, building, deploying, and debugging Flipper applications\n- [Hardware combos and Un-bricking](/documentation/KeyCombo.md) - recovering your Flipper from the most nasty situations\n- [Flipper File Formats](/documentation/file_formats) - everything about how Flipper stores your data and how you can work with it\n- [Universal Remotes](/documentation/UniversalRemotes.md) - contributing your infrared remote to the universal remote database\n- [Firmware Roadmap](https://miro.com/app/board/uXjVO_3D6xU=/)\n- And much more in the [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen)\n\n# Project structure\n\n- `applications`        - Applications and services used in firmware\n- `applications_users`  - Place for your additional applications and services\n- `assets`              - Assets used by applications and services\n- `documentation`       - Documentation generation system configs and input files\n- `furi`                - Furi Core: OS-level primitives and helpers\n- `lib`                 - Our and 3rd party libraries, drivers, tools and etc...\n- `site_scons`          - Build system configuration and modules\n- `scripts`             - Supplementary scripts and various python libraries\n- `targets`             - Firmware targets: platform specific code\n\nAlso, see `ReadMe.md` files inside those directories for further details.\n\n# Links\n\n- Discord: [flipp.dev/discord](https://flipp.dev/discord)\n- Website: [flipperzero.one](https://flipperzero.one)\n- Forum: [forum.flipperzero.one](https://forum.flipperzero.one/)\n- Kickstarter: [kickstarter.com](https://www.kickstarter.com/projects/flipper-devices/flipper-zero-tamagochi-for-hackers)\n\n## SAST Tools\n\n- [PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.\n"
  },
  {
    "path": "SConstruct",
    "content": "#\n# Main Flipper Build System entry point\n#\n# This file is evaluated by scons (the build system) every time fbt is invoked.\n# Scons constructs all referenced environments & their targets' dependency\n# trees on startup. So, to keep startup time as low as possible, we're hiding\n# construction of certain targets behind command-line options.\n\nimport os\nfrom fbt.util import open_browser_action\n\nDefaultEnvironment(tools=[])\n\nEnsurePythonVersion(3, 8)\n\n# Progress([\"OwO\\r\", \"owo\\r\", \"uwu\\r\", \"owo\\r\"], interval=15)\n\n# This environment is created only for loading options & validating file/dir existence\nfbt_variables = SConscript(\"site_scons/commandline.scons\")\ncmd_environment = Environment(\n    toolpath=[\"#/scripts/fbt_tools\"],\n    tools=[\n        (\"fbt_help\", {\"vars\": fbt_variables}),\n    ],\n    variables=fbt_variables,\n)\n\n# Building basic environment - tools, utility methods, cross-compilation\n# settings, gcc flags for Cortex-M4, basic builders and more\ncoreenv = SConscript(\n    \"site_scons/environ.scons\",\n    exports={\"VAR_ENV\": cmd_environment},\n    toolpath=[\"#/scripts/fbt_tools\"],\n)\nSConscript(\"site_scons/cc.scons\", exports={\"ENV\": coreenv})\n\n# Create a separate \"dist\" environment and add construction envs to it\ndistenv = coreenv.Clone(\n    tools=[\n        \"fbt_dist\",\n        \"fbt_debugopts\",\n        \"openocd\",\n        \"blackmagic\",\n        \"jflash\",\n        \"doxygen\",\n        \"textfile\",\n    ],\n    ENV=os.environ,\n    UPDATE_BUNDLE_DIR=\"dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}\",\n)\n\nfirmware_env = distenv.AddFwProject(\n    base_env=coreenv,\n    fw_type=\"firmware\",\n    fw_env_key=\"FW_ENV\",\n)\n\n# If enabled, initialize updater-related targets\nif GetOption(\"fullenv\") or any(\n    filter(lambda target: \"updater\" in target or \"flash_usb\" in target, BUILD_TARGETS)\n):\n    updater_env = distenv.AddFwProject(\n        base_env=coreenv,\n        fw_type=\"updater\",\n        fw_env_key=\"UPD_ENV\",\n    )\n\n    # Target for self-update package\n    dist_basic_arguments = [\n        \"${ARGS}\",\n        \"--bundlever\",\n        \"${UPDATE_VERSION_STRING}\",\n    ]\n    dist_radio_arguments = [\n        \"--radio\",\n        \"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}\",\n        \"--radiotype\",\n        \"${COPRO_STACK_TYPE}\",\n        \"${COPRO_DISCLAIMER}\",\n        \"--obdata\",\n        \"${ROOT_DIR.abspath}/${COPRO_OB_DATA}\",\n        \"--stackversion\",\n        \"${COPRO_CUBE_VERSION}\",\n    ]\n    dist_resource_arguments = [\n        \"-r\",\n        firmware_env.subst(\"${RESOURCES_ROOT}\"),\n    ]\n    dist_splash_arguments = (\n        [\n            \"--splash\",\n            distenv.subst(\"assets/slideshow/$UPDATE_SPLASH\"),\n        ]\n        if distenv[\"UPDATE_SPLASH\"]\n        else []\n    )\n\n    selfupdate_dist = distenv.DistCommand(\n        \"updater_package\",\n        (distenv[\"DIST_DEPENDS\"], firmware_env[\"FW_RESOURCES_MANIFEST\"]),\n        DIST_EXTRA=[\n            *dist_basic_arguments,\n            *dist_radio_arguments,\n            *dist_resource_arguments,\n            *dist_splash_arguments,\n        ],\n    )\n\n    selfupdate_min_dist = distenv.DistCommand(\n        \"updater_minpackage\",\n        distenv[\"DIST_DEPENDS\"],\n        DIST_EXTRA=dist_basic_arguments,\n    )\n\n    # Updater debug\n    distenv.PhonyTarget(\n        \"updater_debug\",\n        \"${GDBPYCOM}\",\n        source=updater_env[\"FW_ELF\"],\n        GDBREMOTE=\"${OPENOCD_GDB_PIPE}\",\n    )\n\n    distenv.PhonyTarget(\n        \"updater_blackmagic\",\n        \"${GDBPYCOM}\",\n        source=updater_env[\"FW_ELF\"],\n        GDBOPTS=distenv.subst(\"$GDBOPTS_BLACKMAGIC\"),\n        GDBREMOTE=\"${BLACKMAGIC_ADDR}\",\n    )\n\n    # Installation over USB & CLI\n    usb_update_package = distenv.AddUsbFlashTarget(\n        \"#build/usbinstall.flag\",\n        (firmware_env[\"FW_RESOURCES_MANIFEST\"], selfupdate_dist),\n    )\n    distenv.Alias(\"flash_usb_full\", usb_update_package)\n\n    usb_minupdate_package = distenv.AddUsbFlashTarget(\n        \"#build/minusbinstall.flag\", (selfupdate_min_dist,)\n    )\n    distenv.Alias(\"flash_usb\", usb_minupdate_package)\n\n\n# Target for copying & renaming binaries to dist folder\nbasic_dist = distenv.DistCommand(\"fw_dist\", distenv[\"DIST_DEPENDS\"])\ndistenv.Default(basic_dist)\n\ndist_dir_name = distenv.GetProjetDirName()\ndist_dir = distenv.Dir(f\"#/dist/{dist_dir_name}\")\nexternal_apps_artifacts = firmware_env[\"FW_EXTAPPS\"]\nexternal_app_list = external_apps_artifacts.application_map.values()\n\nfap_dist = [\n    distenv.Install(\n        dist_dir.Dir(\"debug_elf\"),\n        list(app_artifact.debug for app_artifact in external_app_list),\n    ),\n    *(\n        distenv.Install(\n            dist_dir.File(dist_entry[1]).dir,\n            app_artifact.compact,\n        )\n        for app_artifact in external_app_list\n        for dist_entry in app_artifact.dist_entries\n    ),\n]\nDepends(\n    fap_dist,\n    list(app_artifact.validator for app_artifact in external_app_list),\n)\nAlias(\"fap_dist\", fap_dist)\n\n# Copy all faps to device\n\nfap_deploy = distenv.PhonyTarget(\n    \"fap_deploy\",\n    Action(\n        [\n            [\n                \"${PYTHON3}\",\n                \"${FBT_SCRIPT_DIR}/storage.py\",\n                \"-p\",\n                \"${FLIP_PORT}\",\n                \"send\",\n                \"${SOURCE}\",\n                \"/ext/apps\",\n                \"${ARGS}\",\n            ]\n        ]\n    ),\n    source=firmware_env.Dir((\"${RESOURCES_ROOT}/apps\")),\n)\nDepends(fap_deploy, firmware_env[\"FW_RESOURCES_MANIFEST\"])\n\n\n# Target for bundling core2 package for qFlipper\ncopro_dist = distenv.CoproBuilder(\n    \"#/build/core2_firmware.tgz\",\n    [],\n)\ndistenv.AlwaysBuild(copro_dist)\ndistenv.Alias(\"copro_dist\", copro_dist)\n\n\nfirmware_flash = distenv.AddFwFlashTarget(firmware_env)\ndistenv.Alias(\"flash\", firmware_flash)\n\n# To be implemented in fwflash.py\nfirmware_jflash = distenv.AddJFlashTarget(firmware_env)\ndistenv.Alias(\"jflash\", firmware_jflash)\n\ndistenv.PhonyTarget(\n    \"gdb_trace_all\",\n    [[\"${GDB}\", \"${GDBOPTS}\", \"${SOURCES}\", \"${GDBFLASH}\"]],\n    source=firmware_env[\"FW_ELF\"],\n    GDBOPTS=\"${GDBOPTS_BASE}\",\n    GDBREMOTE=\"${OPENOCD_GDB_PIPE}\",\n    GDBFLASH=[\n        \"-ex\",\n        \"thread apply all bt\",\n        \"-ex\",\n        \"quit\",\n    ],\n)\n\n# Debugging firmware\nfirmware_debug = distenv.PhonyTarget(\n    \"debug\",\n    \"${GDBPYCOM}\",\n    source=firmware_env[\"FW_ELF\"],\n    GDBOPTS=\"${GDBOPTS_BASE}\",\n    GDBREMOTE=\"${OPENOCD_GDB_PIPE}\",\n    FBT_FAP_DEBUG_ELF_ROOT=firmware_env[\"FBT_FAP_DEBUG_ELF_ROOT\"],\n)\ndistenv.Depends(firmware_debug, firmware_flash)\n\nfirmware_blackmagic = distenv.PhonyTarget(\n    \"blackmagic\",\n    \"${GDBPYCOM}\",\n    source=firmware_env[\"FW_ELF\"],\n    GDBOPTS=\"${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}\",\n    GDBREMOTE=\"${BLACKMAGIC_ADDR}\",\n    FBT_FAP_DEBUG_ELF_ROOT=firmware_env[\"FBT_FAP_DEBUG_ELF_ROOT\"],\n)\ndistenv.Depends(firmware_blackmagic, firmware_flash)\n\n# Debug alien elf\ndebug_other_opts = [\n    \"-ex\",\n    \"source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py\",\n    # \"-ex\",\n    # \"source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py\",\n    \"-ex\",\n    \"source ${FBT_DEBUG_DIR}/flipperversion.py\",\n    \"-ex\",\n    \"fw-version\",\n]\n\ndistenv.PhonyTarget(\n    \"debug_other\",\n    \"${GDBPYCOM}\",\n    GDBOPTS=\"${GDBOPTS_BASE}\",\n    GDBREMOTE=\"${OPENOCD_GDB_PIPE}\",\n    GDBPYOPTS=debug_other_opts,\n)\n\ndistenv.PhonyTarget(\n    \"debug_other_blackmagic\",\n    \"${GDBPYCOM}\",\n    GDBOPTS=\"${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}\",\n    GDBREMOTE=\"${BLACKMAGIC_ADDR}\",\n    GDBPYOPTS=debug_other_opts,\n)\n\n\n# Just start OpenOCD\ndistenv.PhonyTarget(\n    \"openocd\",\n    [[\"${OPENOCDCOM}\", \"${ARGS}\"]],\n)\n\n# Linter\ndistenv.PhonyTarget(\n    \"lint\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/lint.py\",\n            \"check\",\n            \"${LINT_SOURCES}\",\n            \"${ARGS}\",\n        ]\n    ],\n    LINT_SOURCES=[n.srcnode() for n in firmware_env[\"LINT_SOURCES\"]],\n)\n\ndistenv.PhonyTarget(\n    \"format\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/lint.py\",\n            \"format\",\n            \"${LINT_SOURCES}\",\n            \"${ARGS}\",\n        ]\n    ],\n    LINT_SOURCES=[n.srcnode() for n in firmware_env[\"LINT_SOURCES\"]],\n)\n\n# PY_LINT_SOURCES contains recursively-built modules' SConscript files\n# Here we add additional Python files residing in repo root\nfirmware_env.Append(\n    PY_LINT_SOURCES=[\n        # Py code folders\n        \"site_scons\",\n        \"scripts\",\n        \"applications\",\n        \"applications_user\",\n        \"assets\",\n        \"targets\",\n        # Extra files\n        \"SConstruct\",\n        \"firmware.scons\",\n        \"fbt_options.py\",\n    ],\n    IMG_LINT_SOURCES=[\n        # Image assets\n        \"applications\",\n        \"assets\",\n    ],\n)\n\n\nblack_commandline = [\n    [\n        \"@${PYTHON3}\",\n        \"-m\",\n        \"black\",\n        \"${PY_BLACK_ARGS}\",\n        \"${PY_LINT_SOURCES}\",\n        \"${ARGS}\",\n    ]\n]\nblack_base_args = [\n    \"--include\",\n    '\"(\\\\.scons|\\\\.py|SConscript|SConstruct|\\\\.fam)$\"',\n]\n\ndistenv.PhonyTarget(\n    \"lint_py\",\n    black_commandline,\n    PY_BLACK_ARGS=[\n        \"--check\",\n        \"--diff\",\n        *black_base_args,\n    ],\n    PY_LINT_SOURCES=firmware_env[\"PY_LINT_SOURCES\"],\n)\n\ndistenv.PhonyTarget(\n    \"format_py\",\n    black_commandline,\n    PY_BLACK_ARGS=black_base_args,\n    PY_LINT_SOURCES=firmware_env[\"PY_LINT_SOURCES\"],\n)\n\n# Image assets linting\ndistenv.PhonyTarget(\n    \"lint_img\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/imglint.py\",\n            \"check\",\n            \"${IMG_LINT_SOURCES}\",\n            \"${ARGS}\",\n        ]\n    ],\n    IMG_LINT_SOURCES=firmware_env[\"IMG_LINT_SOURCES\"],\n)\n\ndistenv.PhonyTarget(\n    \"format_img\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/imglint.py\",\n            \"format\",\n            \"${IMG_LINT_SOURCES}\",\n            \"${ARGS}\",\n        ]\n    ],\n    IMG_LINT_SOURCES=firmware_env[\"IMG_LINT_SOURCES\"],\n)\n\ndistenv.Alias(\"lint_all\", [\"lint\", \"lint_py\", \"lint_img\"])\ndistenv.Alias(\"format_all\", [\"format\", \"format_py\", \"format_img\"])\n\n\n# Start Flipper CLI via PySerial's miniterm\ndistenv.PhonyTarget(\n    \"cli\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/serial_cli.py\",\n            \"-p\",\n            \"${FLIP_PORT}\",\n            \"${ARGS}\",\n        ]\n    ],\n)\n\n\n# Measure CLI loopback performance\ndistenv.PhonyTarget(\n    \"cli_perf\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/serial_cli_perf.py\",\n            \"-p\",\n            \"${FLIP_PORT}\",\n            \"${ARGS}\",\n        ]\n    ],\n)\n\n# Update WiFi devboard firmware with release channel\ndistenv.PhonyTarget(\n    \"devboard_flash\",\n    [\n        [\n            \"${PYTHON3}\",\n            \"${FBT_SCRIPT_DIR}/wifi_board.py\",\n            \"${ARGS}\",\n        ]\n    ],\n)\n\n\n# Find blackmagic probe\ndistenv.PhonyTarget(\n    \"get_blackmagic\",\n    \"@echo $( ${BLACKMAGIC_ADDR} $)\",\n)\n\n\n# Find STLink probe ids\ndistenv.PhonyTarget(\n    \"get_stlink\",\n    distenv.Action(\n        lambda **_: distenv.GetDevices(),\n        None,\n    ),\n)\n\n# Prepare vscode environment\nvscode_dist = distenv.Install(\n    \"#.vscode\",\n    [\n        distenv.Glob(\"#.vscode/example/*.json\", exclude=\"*.tmpl\"),\n        distenv.Glob(\"#.vscode/example/${LANG_SERVER}/*.json\"),\n    ],\n)\nfor template_file in distenv.Glob(\"#.vscode/example/*.tmpl\"):\n    vscode_dist.append(\n        distenv.Substfile(\n            distenv.Dir(\"#.vscode\").File(template_file.name.replace(\".tmpl\", \"\")),\n            template_file,\n            SUBST_DICT={\n                \"@FBT_PLATFORM_EXECUTABLE_EXT@\": \".exe\" if os.name == \"nt\" else \"\"\n            },\n        )\n    )\ndistenv.Precious(vscode_dist)\ndistenv.NoClean(vscode_dist)\ndistenv.Alias(\"vscode_dist\", (vscode_dist, firmware_env[\"FW_CDB\"]))\n\n# Configure shell with build tools\ndistenv.PhonyTarget(\n    \"env\",\n    \"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)\",\n)\n\ndoxy_build = distenv.DoxyBuild(\n    \"documentation/doxygen/build/html/index.html\",\n    \"documentation/doxygen/Doxyfile-awesome.cfg\",\n    doxy_env_variables={\n        \"DOXY_SRC_ROOT\": Dir(\".\").abspath,\n        \"DOXY_BUILD_DIR\": Dir(\"documentation/doxygen/build\").abspath,\n        \"DOXY_CONFIG_DIR\": \"documentation/doxygen\",\n    },\n)\ndistenv.Alias(\"doxygen\", doxy_build)\ndistenv.AlwaysBuild(doxy_build)\n\n# Open generated documentation in browser\ndistenv.PhonyTarget(\"doxy\", open_browser_action, source=doxy_build)\n"
  },
  {
    "path": "applications/ReadMe.md",
    "content": "# Structure\n\n## debug \n\nApplications for factory testing the Flipper.\n\n- `accessor`            - Wiegand server \n- `battery_test_app`    - Battery debug app\n- `blink_test`          - LED blinker   \n- `bt_debug_app`        - BT test app. Requires full BT stack installed\n- `display_test`        - Various display tests & tweaks\n- `file_browser_test`   - Test UI for file picker\n- `keypad_test`         - Keypad test\n- `lfrfid_debug`        - LF RFID debug tool\n- `text_box_test`       - UI tests\n- `uart_echo`           - UART mode test \n- `unit_tests`          - Unit tests\n- `usb_mouse`           - USB HID test\n- `usb_test`            - Other USB tests\n- `vibro_test`          - Vibro test\n\n\n## main\n\nApplications for main Flipper menu.\n\n- `archive`             - Archive and file manager \n- `bad_usb`             - Bad USB application\n- `gpio`                - GPIO application: includes USART bridge and GPIO control\n- `ibutton`             - iButton application, onewire keys and more\n- `infrared`            - Infrared application, controls your IR devices\n- `lfrfid`              - LF RFID application\n- `nfc`                 - NFC application, HF rfid, EMV and etc\n- `subghz`              - SubGhz application, 433 fobs and etc\n- `u2f`                 - U2F Application\n\n\n## services\n\nBackground services providing system APIs to applications.\n\n- `applications.h`      - Firmware application list header\n\n- `bt`                  - BLE service and application\n- `cli`                 - Console service and API\n- `crypto`              - Crypto cli tools\n- `desktop`             - Desktop service\n- `dialogs`             - Dialogs service: GUI Dialogs for your app\n- `dolphin`             - Dolphin service and supplementary apps\n- `gui`                 - GUI service and API\n- `input`               - Input service\n- `loader`              - Application loader service\n- `notification`        - Notification service \n- `power`               - Power service\n- `rpc`                 - RPC service and API\n- `storage`             - Storage service, internal + sdcard\n\n\n## settings\n\nSmall applications providing configuration for basic firmware and its services.\n\n- `about`               - Small About application that shows flipper info\n- `bt_settings_app`     - Bluetooth options\n- `desktop_settings`    - Desktop configuration\n- `dolphin_passport`    - Dolphin passport app\n- `notification_settings` - LCD brightness, sound volume, etc configuration\n- `power_settings_app`  - Basic power options\n- `storage_settings`    - Storage settings app\n- `system`              - System settings\n\n\n## system\n\nUtility apps not visible in other menus, plus few external apps pre-packaged with the firmware.\n\n- `hid_app`             - BLE & USB HID remote\n- `js_app`              - JS engine runner\n- `snake_game`          - Snake game\n- `storage_move_to_sd`  - Data migration tool for internal storage\n- `updater`             - Update service & application\n"
  },
  {
    "path": "applications/debug/accessor/accessor.cpp",
    "content": "#include \"accessor_app.h\"\n\n// app enter function\nextern \"C\" int32_t accessor_app(void* p) {\n    UNUSED(p);\n    AccessorApp* app = new AccessorApp();\n    app->run();\n    delete app;\n\n    return 255;\n}\n"
  },
  {
    "path": "applications/debug/accessor/accessor_app.cpp",
    "content": "#include \"accessor_app.h\"\n#include <furi.h>\n#include <furi_hal.h>\n#include <stdarg.h>\n#include <power/power_service/power.h>\n\nvoid AccessorApp::run(void) {\n    AccessorEvent event;\n    bool consumed;\n    bool exit = false;\n\n    wiegand.begin();\n    onewire_host_start(onewire_host);\n\n    scenes[current_scene]->on_enter(this);\n\n    while(!exit) {\n        view.receive_event(&event);\n\n        consumed = scenes[current_scene]->on_event(this, &event);\n\n        if(!consumed) {\n            if(event.type == AccessorEvent::Type::Back) {\n                exit = switch_to_previous_scene();\n            }\n        }\n    };\n\n    scenes[current_scene]->on_exit(this);\n\n    wiegand.end();\n    onewire_host_stop(onewire_host);\n}\n\nAccessorApp::AccessorApp()\n    : text_store{0} {\n    notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));\n    expansion = static_cast<Expansion*>(furi_record_open(RECORD_EXPANSION));\n    power = static_cast<Power*>(furi_record_open(RECORD_POWER));\n    onewire_host = onewire_host_alloc(&gpio_ibutton);\n    expansion_disable(expansion);\n    power_enable_otg(power, true);\n}\n\nAccessorApp::~AccessorApp() {\n    power_enable_otg(power, false);\n    expansion_enable(expansion);\n    furi_record_close(RECORD_EXPANSION);\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_POWER);\n    onewire_host_free(onewire_host);\n}\n\nAccessorAppViewManager* AccessorApp::get_view_manager() {\n    return &view;\n}\n\nvoid AccessorApp::switch_to_next_scene(Scene next_scene) {\n    previous_scenes_list.push_front(current_scene);\n\n    if(next_scene != Scene::Exit) {\n        scenes[current_scene]->on_exit(this);\n        current_scene = next_scene;\n        scenes[current_scene]->on_enter(this);\n    }\n}\n\nvoid AccessorApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {\n    Scene previous_scene = Scene::Start;\n    bool scene_found = false;\n\n    while(!scene_found) {\n        previous_scene = get_previous_scene();\n        for(Scene element : scenes_list) {\n            if(previous_scene == element || previous_scene == Scene::Start) {\n                scene_found = true;\n                break;\n            }\n        }\n    }\n\n    scenes[current_scene]->on_exit(this);\n    current_scene = previous_scene;\n    scenes[current_scene]->on_enter(this);\n}\n\nbool AccessorApp::switch_to_previous_scene(uint8_t count) {\n    Scene previous_scene = Scene::Start;\n\n    for(uint8_t i = 0; i < count; i++) {\n        previous_scene = get_previous_scene();\n        if(previous_scene == Scene::Exit) break;\n    }\n\n    if(previous_scene == Scene::Exit) {\n        return true;\n    } else {\n        scenes[current_scene]->on_exit(this);\n        current_scene = previous_scene;\n        scenes[current_scene]->on_enter(this);\n        return false;\n    }\n}\n\nAccessorApp::Scene AccessorApp::get_previous_scene() {\n    Scene scene = previous_scenes_list.front();\n    previous_scenes_list.pop_front();\n    return scene;\n}\n\n/***************************** NOTIFY *******************************/\n\nvoid AccessorApp::notify_green_blink() {\n    notification_message(notification, &sequence_blink_green_10);\n}\n\nvoid AccessorApp::notify_success() {\n    notification_message(notification, &sequence_success);\n}\n\n/*************************** TEXT STORE *****************************/\n\nchar* AccessorApp::get_text_store() {\n    return text_store;\n}\n\nuint8_t AccessorApp::get_text_store_size() {\n    return text_store_size;\n}\n\nvoid AccessorApp::set_text_store(const char* text...) {\n    va_list args;\n    va_start(args, text);\n\n    vsnprintf(text_store, text_store_size, text, args);\n\n    va_end(args);\n}\n\n/*************************** APP RESOURCES *****************************/\n\nWIEGAND* AccessorApp::get_wiegand() {\n    return &wiegand;\n}\n\nOneWireHost* AccessorApp::get_one_wire() {\n    return onewire_host;\n}\n"
  },
  {
    "path": "applications/debug/accessor/accessor_app.h",
    "content": "#pragma once\n#include <map>\n#include <list>\n#include \"accessor_view_manager.h\"\n#include \"scene/accessor_scene_start.h\"\n#include \"helpers/wiegand.h\"\n#include <one_wire/one_wire_host.h>\n#include <notification/notification_messages.h>\n#include <expansion/expansion.h>\n#include <power/power_service/power.h>\n\nclass AccessorApp {\npublic:\n    void run(void);\n\n    AccessorApp(void);\n    ~AccessorApp(void);\n\n    enum class Scene : uint8_t {\n        Exit,\n        Start,\n    };\n\n    AccessorAppViewManager* get_view_manager(void);\n    void switch_to_next_scene(Scene index);\n    void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);\n    bool switch_to_previous_scene(uint8_t count = 1);\n    Scene get_previous_scene(void);\n\n    void notify_green_blink(void);\n    void notify_success(void);\n\n    char* get_text_store(void);\n    uint8_t get_text_store_size(void);\n    void set_text_store(const char* text...);\n\n    WIEGAND* get_wiegand(void);\n    OneWireHost* get_one_wire(void);\n\nprivate:\n    std::list<Scene> previous_scenes_list = {Scene::Exit};\n    Scene current_scene = Scene::Start;\n    AccessorAppViewManager view;\n\n    std::map<Scene, AccessorScene*> scenes = {\n        {Scene::Start, new AccessorSceneStart()},\n    };\n\n    static const uint8_t text_store_size = 128;\n    char text_store[text_store_size + 1];\n\n    WIEGAND wiegand;\n    OneWireHost* onewire_host;\n\n    NotificationApp* notification;\n    Expansion* expansion;\n    Power* power;\n};\n"
  },
  {
    "path": "applications/debug/accessor/accessor_event.h",
    "content": "#pragma once\n#include <stdint.h>\n\nclass AccessorEvent {\npublic:\n    // events enum\n    enum class Type : uint8_t {\n        Tick,\n        Back,\n    };\n\n    // payload\n    union {\n        uint32_t menu_index;\n    } payload;\n\n    // event type\n    Type type;\n};\n"
  },
  {
    "path": "applications/debug/accessor/accessor_view_manager.cpp",
    "content": "#include \"accessor_view_manager.h\"\n#include \"accessor_event.h\"\n#include \"callback_connector.h\"\n\nAccessorAppViewManager::AccessorAppViewManager() {\n    event_queue = furi_message_queue_alloc(10, sizeof(AccessorEvent));\n\n    view_holder = view_holder_alloc();\n    auto callback =\n        cbc::obtain_connector(this, &AccessorAppViewManager::view_holder_back_callback);\n\n    // allocate views\n    submenu = submenu_alloc();\n    popup = popup_alloc();\n\n    // set back callback\n    view_holder_set_back_callback(view_holder, callback, NULL);\n\n    gui = static_cast<Gui*>(furi_record_open(RECORD_GUI));\n    view_holder_attach_to_gui(view_holder, gui);\n}\n\nAccessorAppViewManager::~AccessorAppViewManager() {\n    // remove current view\n    view_holder_set_view(view_holder, NULL);\n    // free view modules\n    furi_record_close(RECORD_GUI);\n    submenu_free(submenu);\n    popup_free(popup);\n    // free view holder\n    view_holder_free(view_holder);\n    // free event queue\n    furi_message_queue_free(event_queue);\n}\n\nvoid AccessorAppViewManager::switch_to(ViewType type) {\n    View* view;\n\n    switch(type) {\n    case ViewType::Submenu:\n        view = submenu_get_view(submenu);\n        break;\n    case ViewType::Popup:\n        view = popup_get_view(popup);\n        break;\n    default:\n        furi_crash();\n    }\n\n    view_holder_set_view(view_holder, view);\n}\n\nSubmenu* AccessorAppViewManager::get_submenu() {\n    return submenu;\n}\n\nPopup* AccessorAppViewManager::get_popup() {\n    return popup;\n}\n\nvoid AccessorAppViewManager::receive_event(AccessorEvent* event) {\n    if(furi_message_queue_get(event_queue, event, 100) != FuriStatusOk) {\n        event->type = AccessorEvent::Type::Tick;\n    }\n}\n\nvoid AccessorAppViewManager::send_event(AccessorEvent* event) {\n    FuriStatus result = furi_message_queue_put(event_queue, event, 0);\n    furi_check(result == FuriStatusOk);\n}\n\nvoid AccessorAppViewManager::view_holder_back_callback(void*) {\n    if(event_queue != NULL) {\n        AccessorEvent event;\n        event.type = AccessorEvent::Type::Back;\n        send_event(&event);\n    }\n}\n"
  },
  {
    "path": "applications/debug/accessor/accessor_view_manager.h",
    "content": "#pragma once\n#include <furi.h>\n#include <gui/view_holder.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/popup.h>\n#include \"accessor_event.h\"\n\nclass AccessorAppViewManager {\npublic:\n    enum class ViewType : uint8_t {\n        Submenu,\n        Popup,\n    };\n\n    FuriMessageQueue* event_queue;\n\n    AccessorAppViewManager(void);\n    ~AccessorAppViewManager(void);\n\n    void switch_to(ViewType type);\n\n    void receive_event(AccessorEvent* event);\n    void send_event(AccessorEvent* event);\n\n    Submenu* get_submenu(void);\n    Popup* get_popup(void);\n\nprivate:\n    Gui* gui;\n    ViewHolder* view_holder;\n\n    void view_holder_back_callback(void* context);\n\n    // view elements\n    Submenu* submenu;\n    Popup* popup;\n};\n"
  },
  {
    "path": "applications/debug/accessor/application.fam",
    "content": "App(\n    appid=\"accessor\",\n    name=\"Accessor\",\n    apptype=FlipperAppType.DEBUG,\n    targets=[\"f7\"],\n    entry_point=\"accessor_app\",\n    requires=[\"gui\"],\n    stack_size=4 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/accessor/callback_connector.h",
    "content": "#ifndef CALLBACKCONNECTOR_H\n#define CALLBACKCONNECTOR_H\n\n#ifdef __cplusplus\n#include <functional>\nnamespace cbc {\nnamespace Details {\n\ntemplate <std::size_t Tag, typename T, typename Ret, typename... Args>\nclass FuncMemberWrapper {\npublic:\n    FuncMemberWrapper() = delete;\n    using member_fun_t = Ret (T::*)(Args...);\n    using const_member_fun_t = Ret (T::*)(Args...) const;\n    static auto instantiate(T* t, member_fun_t ptr) {\n        obj = t;\n        member = ptr;\n        return MetaCall;\n    }\n\n    static auto instantiate(T* t, const_member_fun_t ptr) {\n        obj = t;\n        const_member = ptr;\n        return ConstMetaCall;\n    }\n\nprivate:\n    static auto MetaCall(Args... args) {\n        return (*obj.*member)(args...);\n    }\n    static auto ConstMetaCall(Args... args) {\n        return (*obj.*const_member)(args...);\n    }\n    static T* obj;\n    static member_fun_t member;\n    static const_member_fun_t const_member;\n};\ntemplate <std::size_t Tag, typename T, typename Ret, typename... Args>\nT* FuncMemberWrapper<Tag, T, Ret, Args...>::obj{};\n\ntemplate <std::size_t Tag, typename T, typename Ret, typename... Args>\ntypename FuncMemberWrapper<Tag, T, Ret, Args...>::member_fun_t\n    FuncMemberWrapper<Tag, T, Ret, Args...>::member{};\n\ntemplate <std::size_t Tag, typename T, typename Ret, typename... Args>\ntypename FuncMemberWrapper<Tag, T, Ret, Args...>::const_member_fun_t\n    FuncMemberWrapper<Tag, T, Ret, Args...>::const_member{};\n\ntemplate <typename Functor, typename Ret, typename... Args>\nstruct FunctorWrapper {\npublic:\n    static std::function<Ret(Args...)> functor;\n    static auto instatiate(Functor fn) {\n        functor = std::move(fn);\n        return MetaCall;\n    }\n\nprivate:\n    static auto MetaCall(Args... args) {\n        return functor(args...);\n    }\n};\n\ntemplate <typename Functor, typename Ret, typename... Args>\nstd::function<Ret(Args...)> FunctorWrapper<Functor, Ret, Args...>::functor;\n\ntemplate <typename Functor, typename Ret, typename T, typename... Args>\nauto deducer(Functor obj, Ret (T::*)(Args...) const) {\n    return FunctorWrapper<Functor, Ret, Args...>::instatiate(std::move(obj));\n}\n\ntemplate <typename Functor, typename Ret, typename T, typename... Args>\nauto deducer(Functor obj, Ret (T::*)(Args...)) {\n    return FunctorWrapper<Functor, Ret, Args...>::instatiate(std::move(obj));\n}\n\ntemplate <std::size_t tag, typename T, typename Ret, typename... Args>\nauto const_instantiate(T* t, Ret (T::*ptr)(Args...) const) {\n    return FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr);\n}\n\ntemplate <std::size_t tag, typename T, typename Func>\nauto const_instantiate(T* t, Func ptr) {\n    return const_instantiate(t, ptr);\n}\n\n} //end of Details scope\n\ntemplate <std::size_t tag = 0, typename T, typename Ret, typename... Args>\nauto obtain_connector(T* t, Ret (T::*ptr)(Args...)) {\n    return Details::FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr);\n}\n\ntemplate <std::size_t tag = 0, typename T, typename Ret, typename... Args>\nauto obtain_connector(T* t, Ret (T::*ptr)(Args...) const) {\n    return Details::FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr);\n}\n\ntemplate <typename Functor>\nauto obtain_connector(Functor functor) {\n    return Details::deducer(std::move(functor), &Functor::operator());\n}\n} //end of cbc scope\n\n#endif // __cplusplus\n#endif // CALLBACKCONNECTOR_H\n"
  },
  {
    "path": "applications/debug/accessor/helpers/wiegand.cpp",
    "content": "#include \"wiegand.h\"\n#include <furi.h>\n#include <furi_hal.h>\n\nunsigned long WIEGAND::_cardTempHigh = 0;\nunsigned long WIEGAND::_cardTemp = 0;\nunsigned long WIEGAND::_lastWiegand = 0;\nunsigned long WIEGAND::_code = 0;\nunsigned long WIEGAND::_codeHigh = 0;\nint WIEGAND::_bitCount = 0;\nint WIEGAND::_wiegandType = 0;\n\nconstexpr uint32_t clocks_in_ms = 64 * 1000;\nconst GpioPin* const pinD0 = &gpio_ext_pa4;\nconst GpioPin* const pinD1 = &gpio_ext_pa7;\n\nWIEGAND::WIEGAND() {\n}\n\nunsigned long WIEGAND::getCode() {\n    return _code;\n}\n\nunsigned long WIEGAND::getCodeHigh() {\n    return _codeHigh;\n}\n\nint WIEGAND::getWiegandType() {\n    return _wiegandType;\n}\n\nbool WIEGAND::available() {\n    bool ret;\n    FURI_CRITICAL_ENTER();\n    ret = DoWiegandConversion();\n    FURI_CRITICAL_EXIT();\n    return ret;\n}\n\nstatic void input_isr_d0(void* _ctx) {\n    WIEGAND* _this = static_cast<WIEGAND*>(_ctx);\n    _this->ReadD0();\n}\n\nstatic void input_isr_d1(void* _ctx) {\n    WIEGAND* _this = static_cast<WIEGAND*>(_ctx);\n    _this->ReadD1();\n}\n\nvoid WIEGAND::begin() {\n    _lastWiegand = 0;\n    _cardTempHigh = 0;\n    _cardTemp = 0;\n    _code = 0;\n    _wiegandType = 0;\n    _bitCount = 0;\n\n    furi_hal_gpio_init_simple(pinD0, GpioModeInterruptFall); // Set D0 pin as input\n    furi_hal_gpio_init_simple(pinD1, GpioModeInterruptFall); // Set D1 pin as input\n\n    furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this);\n    furi_hal_gpio_add_int_callback(pinD1, input_isr_d1, this);\n}\n\nvoid WIEGAND::end() {\n    furi_hal_gpio_remove_int_callback(pinD0);\n    furi_hal_gpio_remove_int_callback(pinD1);\n\n    furi_hal_gpio_init_simple(pinD0, GpioModeAnalog);\n    furi_hal_gpio_init_simple(pinD1, GpioModeAnalog);\n}\n\nvoid WIEGAND::ReadD0() {\n    _bitCount++; // Increment bit count for Interrupt connected to D0\n    if(_bitCount > 31) // If bit count more than 31, process high bits\n    {\n        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); //\tshift value to high bits\n        _cardTempHigh <<= 1;\n        _cardTemp <<= 1;\n    } else {\n        _cardTemp <<= 1; // D0 represent binary 0, so just left shift card data\n    }\n    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received\n}\n\nvoid WIEGAND::ReadD1() {\n    _bitCount++; // Increment bit count for Interrupt connected to D1\n    if(_bitCount > 31) // If bit count more than 31, process high bits\n    {\n        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits\n        _cardTempHigh <<= 1;\n        _cardTemp |= 1;\n        _cardTemp <<= 1;\n    } else {\n        _cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then\n        _cardTemp <<= 1; // left shift card data\n    }\n    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received\n}\n\nunsigned long WIEGAND::GetCardId(unsigned long* codehigh, unsigned long* codelow, char bitlength) {\n    if(bitlength == 26) // EM tag\n        return (*codelow & 0x1FFFFFE) >> 1;\n\n    if(bitlength == 24) return (*codelow & 0x7FFFFE) >> 1;\n\n    if(bitlength == 34) // Mifare\n    {\n        *codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh\n        *codehigh <<= 30; // shift 2 LSB to MSB\n        *codelow >>= 1;\n        return *codehigh | *codelow;\n    }\n\n    if(bitlength == 32) {\n        return (*codelow & 0x7FFFFFFE) >> 1;\n    }\n\n    return *codelow; // EM tag or Mifare without parity bits\n}\n\nchar translateEnterEscapeKeyPress(char originalKeyPress) {\n    switch(originalKeyPress) {\n    case 0x0b: // 11 or * key\n        return 0x0d; // 13 or ASCII ENTER\n\n    case 0x0a: // 10 or # key\n        return 0x1b; // 27 or ASCII ESCAPE\n\n    default:\n        return originalKeyPress;\n    }\n}\n\nbool WIEGAND::DoWiegandConversion() {\n    unsigned long cardID;\n    unsigned long sysTick = DWT->CYCCNT;\n\n    if((sysTick - _lastWiegand) >\n       (25 * clocks_in_ms)) // if no more signal coming through after 25ms\n    {\n        if((_bitCount == 24) || (_bitCount == 26) || (_bitCount == 32) || (_bitCount == 34) ||\n           (_bitCount == 37) || (_bitCount == 40) || (_bitCount == 8) ||\n           (_bitCount ==\n            4)) // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34\n        {\n            _codeHigh = 0;\n            // shift right 1 bit to get back the real value - interrupt done 1 left shift in advance\n            _cardTemp >>= 1;\n            // bit count more than 32 bits, shift high bits right to make adjustment\n            if(_bitCount > 32) _cardTempHigh >>= 1;\n\n            if(_bitCount == 8) // keypress wiegand with integrity\n            {\n                // 8-bit Wiegand keyboard data, high nibble is the \"NOT\" of low nibble\n                // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001\n                char highNibble = (_cardTemp & 0xf0) >> 4;\n                char lowNibble = (_cardTemp & 0x0f);\n                _wiegandType = _bitCount;\n                _bitCount = 0;\n                _cardTemp = 0;\n                _cardTempHigh = 0;\n\n                if(lowNibble ==\n                   (~highNibble & 0x0f)) // check if low nibble matches the \"NOT\" of high nibble.\n                {\n                    _code = (int)translateEnterEscapeKeyPress(lowNibble);\n                    return true;\n                } else {\n                    _lastWiegand = sysTick;\n                    return false;\n                }\n\n                // TODO FL-3490: Handle validation failure case!\n            } else if(4 == _bitCount) {\n                // 4-bit Wiegand codes have no data integrity check so we just\n                // read the LOW nibble.\n                _code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F);\n\n                _wiegandType = _bitCount;\n                _bitCount = 0;\n                _cardTemp = 0;\n                _cardTempHigh = 0;\n\n                return true;\n            } else if(40 == _bitCount) {\n                _cardTempHigh >>= 1;\n\n                _code = _cardTemp;\n                _codeHigh = _cardTempHigh;\n\n                _wiegandType = _bitCount;\n                _bitCount = 0;\n                _cardTemp = 0;\n                _cardTempHigh = 0;\n\n                return true;\n            } else {\n                // wiegand 26 or wiegand 34\n                cardID = GetCardId(&_cardTempHigh, &_cardTemp, _bitCount);\n                _wiegandType = _bitCount;\n                _bitCount = 0;\n                _cardTemp = 0;\n                _cardTempHigh = 0;\n                _code = cardID;\n                return true;\n            }\n        } else {\n            // well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then.\n            _lastWiegand = sysTick;\n            _bitCount = 0;\n            _cardTemp = 0;\n            _cardTempHigh = 0;\n            return false;\n        }\n    } else\n        return false;\n}\n"
  },
  {
    "path": "applications/debug/accessor/helpers/wiegand.h",
    "content": "#pragma once\n\nclass WIEGAND {\npublic:\n    WIEGAND(void);\n    void begin(void);\n    void end(void);\n    bool available(void);\n    unsigned long getCode(void);\n    unsigned long getCodeHigh(void);\n    int getWiegandType(void);\n\n    static void ReadD0(void);\n    static void ReadD1(void);\n\nprivate:\n    static bool DoWiegandConversion(void);\n    static unsigned long\n        GetCardId(unsigned long* codehigh, unsigned long* codelow, char bitlength);\n\n    static unsigned long _cardTempHigh;\n    static unsigned long _cardTemp;\n    static unsigned long _lastWiegand;\n    static int _bitCount;\n    static int _wiegandType;\n    static unsigned long _code;\n    static unsigned long _codeHigh;\n};\n"
  },
  {
    "path": "applications/debug/accessor/scene/accessor_scene_generic.h",
    "content": "#pragma once\n#include \"../accessor_app.h\"\n\nclass AccessorApp;\n\nclass AccessorScene {\npublic:\n    virtual void on_enter(AccessorApp* app) = 0;\n    virtual bool on_event(AccessorApp* app, AccessorEvent* event) = 0;\n    virtual void on_exit(AccessorApp* app) = 0;\n\nprivate:\n};\n"
  },
  {
    "path": "applications/debug/accessor/scene/accessor_scene_start.cpp",
    "content": "#include \"../accessor_app.h\"\n#include \"../accessor_view_manager.h\"\n#include \"../accessor_event.h\"\n#include \"accessor_scene_start.h\"\n\nvoid AccessorSceneStart::on_enter(AccessorApp* app) {\n    AccessorAppViewManager* view_manager = app->get_view_manager();\n    Popup* popup = view_manager->get_popup();\n\n    popup_set_header(popup, \"Accessor App\", 64, 16, AlignCenter, AlignBottom);\n    app->set_text_store(\"[??????]\");\n    popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);\n\n    view_manager->switch_to(AccessorAppViewManager::ViewType::Popup);\n}\n\nbool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {\n    bool consumed = false;\n\n    if(event->type == AccessorEvent::Type::Tick) {\n        WIEGAND* wiegand = app->get_wiegand();\n        Popup* popup = app->get_view_manager()->get_popup();\n        OneWireHost* onewire_host = app->get_one_wire();\n\n        uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};\n        uint8_t type = 0;\n\n        if(wiegand->available()) {\n            type = wiegand->getWiegandType();\n\n            for(uint8_t i = 0; i < 4; i++) {\n                data[i] = wiegand->getCode() >> (i * 8);\n            }\n\n            for(uint8_t i = 4; i < 8; i++) {\n                data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8);\n            }\n        } else {\n            FURI_CRITICAL_ENTER();\n            if(onewire_host_reset(onewire_host)) {\n                type = 255;\n                onewire_host_write(onewire_host, 0x33);\n                for(uint8_t i = 0; i < 8; i++) {\n                    data[i] = onewire_host_read(onewire_host);\n                }\n\n                for(uint8_t i = 0; i < 7; i++) {\n                    data[i] = data[i + 1];\n                }\n            }\n            FURI_CRITICAL_EXIT();\n        }\n\n        if(type > 0) {\n            if(type == 255) {\n                app->set_text_store(\n                    \"[%02X %02X %02X %02X %02X %02X DS]\",\n                    data[5],\n                    data[4],\n                    data[3],\n                    data[2],\n                    data[1],\n                    data[0]);\n            } else {\n                app->set_text_store(\n                    \"[%02X %02X %02X %02X %02X %02X W%u]\",\n                    data[5],\n                    data[4],\n                    data[3],\n                    data[2],\n                    data[1],\n                    data[0],\n                    type);\n            }\n            popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);\n            app->notify_success();\n        }\n    }\n\n    return consumed;\n}\n\nvoid AccessorSceneStart::on_exit(AccessorApp* app) {\n    Popup* popup = app->get_view_manager()->get_popup();\n    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);\n    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);\n}\n"
  },
  {
    "path": "applications/debug/accessor/scene/accessor_scene_start.h",
    "content": "#pragma once\n#include \"accessor_scene_generic.h\"\n\nclass AccessorSceneStart : public AccessorScene {\npublic:\n    void on_enter(AccessorApp* app) final;\n    bool on_event(AccessorApp* app, AccessorEvent* event) final;\n    void on_exit(AccessorApp* app) final;\n};\n"
  },
  {
    "path": "applications/debug/application.fam",
    "content": "App(\n    appid=\"debug_apps\",\n    name=\"Basic debug apps bundle\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"blink_test\",\n        \"vibro_test\",\n        \"keypad_test\",\n        \"usb_test\",\n        \"usb_mouse\",\n        \"uart_echo\",\n        \"display_test\",\n        \"text_box_test\",\n        \"file_browser_test\",\n        \"speaker_debug\",\n    ],\n)\n"
  },
  {
    "path": "applications/debug/battery_test_app/application.fam",
    "content": "App(\n    appid=\"battery_test\",\n    name=\"Battery Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"battery_test_app\",\n    requires=[\n        \"gui\",\n        \"power\",\n    ],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n    fap_libs=[\"assets\"],\n)\n"
  },
  {
    "path": "applications/debug/battery_test_app/battery_test_app.c",
    "content": "#include \"battery_test_app.h\"\n\n#include <notification/notification_messages.h>\n\nvoid battery_test_dialog_callback(DialogExResult result, void* context) {\n    furi_assert(context);\n    BatteryTestApp* app = context;\n    if(result == DialogExResultLeft) {\n        view_dispatcher_stop(app->view_dispatcher);\n    } else if(result == DialogExResultRight) {\n        view_dispatcher_switch_to_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);\n    }\n}\n\nuint32_t battery_test_exit_confirm_view(void* context) {\n    UNUSED(context);\n    return BatteryTestAppViewExitDialog;\n}\n\nstatic void battery_test_battery_info_update_model(void* context) {\n    BatteryTestApp* app = context;\n    power_get_info(app->power, &app->info);\n    BatteryInfoModel battery_info_data = {\n        .vbus_voltage = app->info.voltage_vbus,\n        .gauge_voltage = app->info.voltage_gauge,\n        .gauge_current = app->info.current_gauge,\n        .gauge_temperature = app->info.temperature_gauge,\n        .charge = app->info.charge,\n        .health = app->info.health,\n    };\n    battery_info_set_data(app->battery_info, &battery_info_data);\n    notification_message(app->notifications, &sequence_display_backlight_on);\n}\n\nBatteryTestApp* battery_test_alloc(void) {\n    BatteryTestApp* app = malloc(sizeof(BatteryTestApp));\n\n    // Records\n    app->gui = furi_record_open(RECORD_GUI);\n    app->power = furi_record_open(RECORD_POWER);\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, battery_test_battery_info_update_model, 500);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->battery_info = battery_info_alloc();\n    view_set_previous_callback(\n        battery_info_get_view(app->battery_info), battery_test_exit_confirm_view);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        BatteryTestAppViewBatteryInfo,\n        battery_info_get_view(app->battery_info));\n\n    app->dialog = dialog_ex_alloc();\n    dialog_ex_set_header(app->dialog, \"Close Battery Test?\", 64, 12, AlignCenter, AlignTop);\n    dialog_ex_set_left_button_text(app->dialog, \"Exit\");\n    dialog_ex_set_right_button_text(app->dialog, \"Stay\");\n    dialog_ex_set_result_callback(app->dialog, battery_test_dialog_callback);\n    dialog_ex_set_context(app->dialog, app);\n\n    view_dispatcher_add_view(\n        app->view_dispatcher, BatteryTestAppViewExitDialog, dialog_ex_get_view(app->dialog));\n\n    battery_test_battery_info_update_model(app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);\n    return app;\n}\n\nvoid battery_test_free(BatteryTestApp* app) {\n    furi_assert(app);\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);\n    battery_info_free(app->battery_info);\n    view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewExitDialog);\n    dialog_ex_free(app->dialog);\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    // Records\n    furi_record_close(RECORD_POWER);\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    free(app);\n}\n\nint32_t battery_test_app(void* p) {\n    UNUSED(p);\n    BatteryTestApp* app = battery_test_alloc();\n    // Disable battery low level notification\n    power_enable_low_battery_level_notification(app->power, false);\n\n    view_dispatcher_run(app->view_dispatcher);\n    power_enable_low_battery_level_notification(app->power, true);\n    battery_test_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/battery_test_app/battery_test_app.h",
    "content": "#include <furi.h>\n#include <power/power_service/power.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <notification/notification.h>\n\n#include <gui/modules/dialog_ex.h>\n// FIXME\n#include \"../settings/power_settings_app/views/battery_info.h\"\n\ntypedef struct {\n    Power* power;\n    Gui* gui;\n    NotificationApp* notifications;\n    ViewDispatcher* view_dispatcher;\n    BatteryInfo* battery_info;\n    DialogEx* dialog;\n    PowerInfo info;\n} BatteryTestApp;\n\ntypedef enum {\n    BatteryTestAppViewBatteryInfo,\n    BatteryTestAppViewExitDialog,\n} BatteryTestAppView;\n"
  },
  {
    "path": "applications/debug/battery_test_app/views/battery_info.c",
    "content": "#include \"battery_info.h\"\n#include <furi.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\n#define LOW_CHARGE_THRESHOLD         10\n#define HIGH_DRAIN_CURRENT_THRESHOLD 100\n\nstruct BatteryInfo {\n    View* view;\n};\n\nstatic void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) {\n    canvas_draw_frame(canvas, x - 7, y + 7, 30, 13);\n    canvas_draw_icon(canvas, x, y, icon);\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x - 4, y + 16, 24, 6);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);\n}\n\nstatic void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {\n    char emote[20] = {};\n    char header[20] = {};\n    char value[20] = {};\n\n    int32_t drain_current = data->gauge_current * (-1000);\n    uint32_t charge_current = data->gauge_current * 1000;\n\n    // Draw battery\n    canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28);\n    if(charge_current > 0) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14);\n    } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14);\n    } else if(data->charge < LOW_CHARGE_THRESHOLD) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14);\n    } else {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14);\n    }\n\n    // Draw bubble\n    elements_bubble(canvas, 53, 0, 71, 39);\n\n    // Set text\n    if(charge_current > 0) {\n        snprintf(emote, sizeof(emote), \"%s\", \"Yummy!\");\n        snprintf(header, sizeof(header), \"%s\", \"Charging at\");\n        snprintf(\n            value,\n            sizeof(value),\n            \"%lu.%luV   %lumA\",\n            (uint32_t)(data->vbus_voltage),\n            (uint32_t)(data->vbus_voltage * 10) % 10,\n            charge_current);\n    } else if(drain_current > 0) {\n        snprintf(\n            emote,\n            sizeof(emote),\n            \"%s\",\n            drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? \"Oh no!\" : \"Om-nom-nom!\");\n        snprintf(header, sizeof(header), \"%s\", \"Consumption is\");\n        snprintf(\n            value,\n            sizeof(value),\n            \"%ld %s\",\n            drain_current,\n            drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? \"mA!\" : \"mA\");\n    } else if(drain_current != 0) {\n        snprintf(header, 20, \"...\");\n    } else if(data->charging_voltage < 4.2f) {\n        // Non-default battery charging limit, mention it\n        snprintf(emote, sizeof(emote), \"Charged!\");\n        snprintf(header, sizeof(header), \"Limited to\");\n        snprintf(\n            value,\n            sizeof(value),\n            \"%lu.%luV\",\n            (uint32_t)(data->charging_voltage),\n            (uint32_t)(data->charging_voltage * 10) % 10);\n    } else {\n        snprintf(header, sizeof(header), \"Charged!\");\n    }\n\n    canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);\n    canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);\n    canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);\n}\n\nstatic void battery_info_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    BatteryInfoModel* model = context;\n\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    draw_battery(canvas, model, 0, 5);\n\n    char batt_level[10];\n    char temperature[10];\n    char voltage[10];\n    char health[10];\n\n    snprintf(batt_level, sizeof(batt_level), \"%lu%%\", (uint32_t)model->charge);\n    snprintf(temperature, sizeof(temperature), \"%lu C\", (uint32_t)model->gauge_temperature);\n    snprintf(\n        voltage,\n        sizeof(voltage),\n        \"%lu.%01lu V\",\n        (uint32_t)model->gauge_voltage,\n        (uint32_t)(model->gauge_voltage * 10) % 10UL);\n    snprintf(health, sizeof(health), \"%d%%\", model->health);\n\n    draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level);\n    draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature);\n    draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage);\n    draw_stat(canvas, 104, 42, &I_Health_16x16, health);\n}\n\nBatteryInfo* battery_info_alloc(void) {\n    BatteryInfo* battery_info = malloc(sizeof(BatteryInfo));\n    battery_info->view = view_alloc();\n    view_set_context(battery_info->view, battery_info);\n    view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel));\n    view_set_draw_callback(battery_info->view, battery_info_draw_callback);\n\n    return battery_info;\n}\n\nvoid battery_info_free(BatteryInfo* battery_info) {\n    furi_assert(battery_info);\n    view_free(battery_info->view);\n    free(battery_info);\n}\n\nView* battery_info_get_view(BatteryInfo* battery_info) {\n    furi_assert(battery_info);\n    return battery_info->view;\n}\n\nvoid battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) {\n    furi_assert(battery_info);\n    furi_assert(data);\n    with_view_model(\n        battery_info->view,\n        BatteryInfoModel * model,\n        { memcpy(model, data, sizeof(BatteryInfoModel)); },\n        true);\n}\n"
  },
  {
    "path": "applications/debug/battery_test_app/views/battery_info.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct BatteryInfo BatteryInfo;\n\ntypedef struct {\n    float vbus_voltage;\n    float gauge_voltage;\n    float gauge_current;\n    float gauge_temperature;\n    float charging_voltage;\n    uint8_t charge;\n    uint8_t health;\n} BatteryInfoModel;\n\nBatteryInfo* battery_info_alloc(void);\n\nvoid battery_info_free(BatteryInfo* battery_info);\n\nView* battery_info_get_view(BatteryInfo* battery_info);\n\nvoid battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data);\n"
  },
  {
    "path": "applications/debug/blink_test/application.fam",
    "content": "App(\n    appid=\"blink_test\",\n    name=\"Blink Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"blink_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/blink_test/blink_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <input/input.h>\n\n#include <notification/notification_messages.h>\n\ntypedef enum {\n    BlinkEventTypeTick,\n    BlinkEventTypeInput,\n} BlinkEventType;\n\ntypedef struct {\n    BlinkEventType type;\n    InputEvent input;\n} BlinkEvent;\n\nstatic const NotificationSequence blink_test_sequence_hw_blink_start_red = {\n    &message_blink_start_10,\n    &message_blink_set_color_red,\n    &message_do_not_reset,\n    NULL,\n};\n\nstatic const NotificationSequence blink_test_sequence_hw_blink_green = {\n    &message_blink_set_color_green,\n    NULL,\n};\n\nstatic const NotificationSequence blink_test_sequence_hw_blink_blue = {\n    &message_blink_set_color_blue,\n    NULL,\n};\n\nstatic const NotificationSequence blink_test_sequence_hw_blink_stop = {\n    &message_blink_stop,\n    NULL,\n};\n\nstatic const NotificationSequence* blink_test_colors[] = {\n    &sequence_blink_red_100,\n    &sequence_blink_green_100,\n    &sequence_blink_blue_100,\n    &sequence_blink_yellow_100,\n    &sequence_blink_cyan_100,\n    &sequence_blink_magenta_100,\n    &sequence_blink_white_100,\n    &blink_test_sequence_hw_blink_start_red,\n    &blink_test_sequence_hw_blink_green,\n    &blink_test_sequence_hw_blink_blue,\n    &blink_test_sequence_hw_blink_stop,\n};\n\nstatic void blink_test_update(void* ctx) {\n    furi_assert(ctx);\n    FuriMessageQueue* event_queue = ctx;\n    BlinkEvent event = {.type = BlinkEventTypeTick};\n    // It's OK to loose this event if system overloaded\n    furi_message_queue_put(event_queue, &event, 0);\n}\n\nstatic void blink_test_draw_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n    canvas_clear(canvas);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 2, 10, \"Blink application\");\n}\n\nstatic void blink_test_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n    FuriMessageQueue* event_queue = ctx;\n\n    BlinkEvent event = {.type = BlinkEventTypeInput, .input = *input_event};\n    furi_message_queue_put(event_queue, &event, FuriWaitForever);\n}\n\nint32_t blink_test_app(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(BlinkEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, blink_test_draw_callback, NULL);\n    view_port_input_callback_set(view_port, blink_test_input_callback, event_queue);\n    FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue);\n    furi_timer_start(timer, furi_kernel_get_tick_frequency());\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    uint8_t state = 0;\n    BlinkEvent event;\n\n    while(1) {\n        furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);\n        if(event.type == BlinkEventTypeInput) {\n            if((event.input.type == InputTypeShort) && (event.input.key == InputKeyBack)) {\n                break;\n            }\n        } else {\n            notification_message(notifications, blink_test_colors[state]);\n            state++;\n            if(state >= COUNT_OF(blink_test_colors)) {\n                state = 0;\n            }\n        }\n    }\n\n    notification_message(notifications, &blink_test_sequence_hw_blink_stop);\n\n    furi_timer_free(timer);\n\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/bt_debug_app/application.fam",
    "content": "App(\n    appid=\"bt_debug\",\n    name=\"Bluetooth Debug\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"bt_debug_app\",\n    cdefines=[\"SRV_BT\"],\n    requires=[\n        \"bt\",\n        \"gui\",\n        \"dialogs\",\n    ],\n    provides=[\n        \"bt_debug\",\n    ],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/bt_debug_app/bt_debug_app.c",
    "content": "#include \"bt_debug_app.h\"\n#include <furi_hal_bt.h>\n\n#define TAG \"BtDebugApp\"\n\nenum BtDebugSubmenuIndex {\n    BtDebugSubmenuIndexCarrierTest,\n    BtDebugSubmenuIndexPacketTest,\n};\n\nvoid bt_debug_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    BtDebugApp* app = context;\n    if(index == BtDebugSubmenuIndexCarrierTest) {\n        view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewCarrierTest);\n    } else if(index == BtDebugSubmenuIndexPacketTest) {\n        view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewPacketTest);\n    }\n}\n\nuint32_t bt_debug_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nuint32_t bt_debug_start_view(void* context) {\n    UNUSED(context);\n    return BtDebugAppViewSubmenu;\n}\n\nBtDebugApp* bt_debug_app_alloc(void) {\n    BtDebugApp* app = malloc(sizeof(BtDebugApp));\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->submenu = submenu_alloc();\n    submenu_add_item(\n        app->submenu,\n        \"Carrier test\",\n        BtDebugSubmenuIndexCarrierTest,\n        bt_debug_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu, \"Packet test\", BtDebugSubmenuIndexPacketTest, bt_debug_submenu_callback, app);\n    view_set_previous_callback(submenu_get_view(app->submenu), bt_debug_exit);\n    view_dispatcher_add_view(\n        app->view_dispatcher, BtDebugAppViewSubmenu, submenu_get_view(app->submenu));\n    app->bt_carrier_test = bt_carrier_test_alloc();\n    view_set_previous_callback(\n        bt_carrier_test_get_view(app->bt_carrier_test), bt_debug_start_view);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        BtDebugAppViewCarrierTest,\n        bt_carrier_test_get_view(app->bt_carrier_test));\n    app->bt_packet_test = bt_packet_test_alloc();\n    view_set_previous_callback(bt_packet_test_get_view(app->bt_packet_test), bt_debug_start_view);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        BtDebugAppViewPacketTest,\n        bt_packet_test_get_view(app->bt_packet_test));\n\n    // Switch to menu\n    view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewSubmenu);\n\n    return app;\n}\n\nvoid bt_debug_app_free(BtDebugApp* app) {\n    furi_assert(app);\n\n    // Free views\n    view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewSubmenu);\n    submenu_free(app->submenu);\n    view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewCarrierTest);\n    bt_carrier_test_free(app->bt_carrier_test);\n    view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewPacketTest);\n    bt_packet_test_free(app->bt_packet_test);\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Close gui record\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    // Free rest\n    free(app);\n}\n\nint32_t bt_debug_app(void* p) {\n    UNUSED(p);\n    if(!furi_hal_bt_is_testing_supported()) {\n        FURI_LOG_E(TAG, \"Incorrect radio stack: radio testing features are absent.\");\n        DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n        dialog_message_show_storage_error(dialogs, \"Incorrect\\nRadioStack\");\n        return 255;\n    }\n\n    BtDebugApp* app = bt_debug_app_alloc();\n    // Was bt active?\n    const bool was_active = furi_hal_bt_is_active();\n    // Stop advertising\n    furi_hal_bt_stop_advertising();\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    // Restart advertising\n    if(was_active) {\n        furi_hal_bt_start_advertising();\n    }\n    bt_debug_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/bt_debug_app/bt_debug_app.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n\n#include <dialogs/dialogs.h>\n\n#include \"views/bt_carrier_test.h\"\n#include \"views/bt_packet_test.h\"\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Submenu* submenu;\n    BtCarrierTest* bt_carrier_test;\n    BtPacketTest* bt_packet_test;\n} BtDebugApp;\n\ntypedef enum {\n    BtDebugAppViewSubmenu,\n    BtDebugAppViewCarrierTest,\n    BtDebugAppViewPacketTest,\n} BtDebugAppView;\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_carrier_test.c",
    "content": "#include \"bt_carrier_test.h\"\n#include \"bt_test.h\"\n#include \"bt_test_types.h\"\n#include <furi_hal_bt.h>\n\nstruct BtCarrierTest {\n    BtTest* bt_test;\n    BtTestParam* bt_param_channel;\n    BtTestMode mode;\n    BtTestChannel channel;\n    BtTestPower power;\n    FuriTimer* timer;\n};\n\nstatic BtTestParamValue bt_param_mode[] = {\n    {.value = BtTestModeRx, .str = \"Rx\"},\n    {.value = BtTestModeTx, .str = \"Tx\"},\n    {.value = BtTestModeTxHopping, .str = \"Hopping Tx\"},\n};\n\nstatic BtTestParamValue bt_param_channel[] = {\n    {.value = BtTestChannel2402, .str = \"2402 MHz\"},\n    {.value = BtTestChannel2440, .str = \"2440 MHz\"},\n    {.value = BtTestChannel2480, .str = \"2480 MHz\"},\n};\n\nstatic BtTestParamValue bt_param_power[] = {\n    {.value = BtPower0dB, .str = \"0 dB\"},\n    {.value = BtPower2dB, .str = \"2 dB\"},\n    {.value = BtPower4dB, .str = \"4 dB\"},\n    {.value = BtPower6dB, .str = \"6 dB\"},\n};\n\nstatic void bt_carrier_test_start(BtCarrierTest* bt_carrier_test) {\n    furi_assert(bt_carrier_test);\n    if(bt_carrier_test->mode == BtTestModeRx) {\n        furi_hal_bt_start_packet_rx(bt_carrier_test->channel, 1);\n        furi_timer_start(bt_carrier_test->timer, furi_kernel_get_tick_frequency() / 4);\n    } else if(bt_carrier_test->mode == BtTestModeTxHopping) {\n        furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);\n        furi_timer_start(bt_carrier_test->timer, furi_kernel_get_tick_frequency() * 2);\n    } else if(bt_carrier_test->mode == BtTestModeTx) {\n        furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);\n    }\n}\n\nstatic void bt_carrier_test_switch_channel(BtCarrierTest* bt_carrier_test) {\n    furi_assert(bt_carrier_test);\n    furi_hal_bt_stop_tone_tx();\n    uint8_t channel_i = 0;\n    if(bt_carrier_test->channel == BtTestChannel2402) {\n        bt_carrier_test->channel = BtTestChannel2440;\n        channel_i = 1;\n    } else if(bt_carrier_test->channel == BtTestChannel2440) {\n        bt_carrier_test->channel = BtTestChannel2480;\n        channel_i = 2;\n    } else if(bt_carrier_test->channel == BtTestChannel2480) {\n        bt_carrier_test->channel = BtTestChannel2402;\n        channel_i = 0;\n    }\n    furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);\n    bt_test_set_current_value_index(bt_carrier_test->bt_param_channel, channel_i);\n    bt_test_set_current_value_text(\n        bt_carrier_test->bt_param_channel, bt_param_channel[channel_i].str);\n}\n\nstatic void bt_carrier_test_stop(BtCarrierTest* bt_carrier_test) {\n    furi_assert(bt_carrier_test);\n    if(bt_carrier_test->mode == BtTestModeTxHopping) {\n        furi_hal_bt_stop_tone_tx();\n        furi_timer_stop(bt_carrier_test->timer);\n    } else if(bt_carrier_test->mode == BtTestModeTx) {\n        furi_hal_bt_stop_tone_tx();\n    } else if(bt_carrier_test->mode == BtTestModeRx) {\n        furi_hal_bt_stop_packet_test();\n        furi_timer_stop(bt_carrier_test->timer);\n    }\n}\n\nstatic uint32_t bt_carrier_test_param_changed(BtTestParam* param, BtTestParamValue* param_val) {\n    furi_assert(param);\n    uint8_t index = bt_test_get_current_value_index(param);\n    bt_test_set_current_value_text(param, param_val[index].str);\n    return param_val[index].value;\n}\n\nstatic void bt_carrier_test_mode_changed(BtTestParam* param) {\n    BtCarrierTest* bt_carrier_test = bt_test_get_context(param);\n    bt_carrier_test_stop(bt_carrier_test);\n    bt_carrier_test->mode = bt_carrier_test_param_changed(param, bt_param_mode);\n}\n\nstatic void bt_carrier_test_channel_changed(BtTestParam* param) {\n    BtCarrierTest* bt_carrier_test = bt_test_get_context(param);\n    bt_carrier_test_stop(bt_carrier_test);\n    bt_carrier_test->channel = bt_carrier_test_param_changed(param, bt_param_channel);\n}\n\nstatic void bt_carrier_test_param_channel(BtTestParam* param) {\n    BtCarrierTest* bt_carrier_test = bt_test_get_context(param);\n    bt_carrier_test_stop(bt_carrier_test);\n    bt_carrier_test->power = bt_carrier_test_param_changed(param, bt_param_power);\n}\n\nstatic void bt_carrier_test_change_state_callback(BtTestState state, void* context) {\n    furi_assert(context);\n    BtCarrierTest* bt_carrier_test = context;\n    furi_hal_bt_stop_tone_tx();\n    if(state == BtTestStateStarted) {\n        bt_carrier_test_start(bt_carrier_test);\n    } else if(state == BtTestStateStopped) {\n        bt_carrier_test_stop(bt_carrier_test);\n    }\n}\n\nstatic void bt_carrier_test_exit_callback(void* context) {\n    furi_assert(context);\n    BtCarrierTest* bt_carrier_test = context;\n    bt_carrier_test_stop(bt_carrier_test);\n}\n\nstatic void bt_test_carrier_timer_callback(void* context) {\n    furi_assert(context);\n    BtCarrierTest* bt_carrier_test = context;\n    if(bt_carrier_test->mode == BtTestModeRx) {\n        bt_test_set_rssi(bt_carrier_test->bt_test, furi_hal_bt_get_rssi());\n    } else if(bt_carrier_test->mode == BtTestModeTxHopping) {\n        bt_carrier_test_switch_channel(bt_carrier_test);\n    }\n}\n\nBtCarrierTest* bt_carrier_test_alloc(void) {\n    BtCarrierTest* bt_carrier_test = malloc(sizeof(BtCarrierTest));\n    bt_carrier_test->bt_test = bt_test_alloc();\n    bt_test_set_context(bt_carrier_test->bt_test, bt_carrier_test);\n    bt_test_set_change_state_callback(\n        bt_carrier_test->bt_test, bt_carrier_test_change_state_callback);\n    bt_test_set_back_callback(bt_carrier_test->bt_test, bt_carrier_test_exit_callback);\n\n    BtTestParam* param;\n    param = bt_test_param_add(\n        bt_carrier_test->bt_test,\n        \"Mode\",\n        COUNT_OF(bt_param_mode),\n        bt_carrier_test_mode_changed,\n        bt_carrier_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_mode[0].str);\n    bt_carrier_test->mode = BtTestModeRx;\n\n    param = bt_test_param_add(\n        bt_carrier_test->bt_test,\n        \"Channel\",\n        COUNT_OF(bt_param_channel),\n        bt_carrier_test_channel_changed,\n        bt_carrier_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_channel[0].str);\n    bt_carrier_test->channel = BtTestChannel2402;\n    bt_carrier_test->bt_param_channel = param;\n\n    param = bt_test_param_add(\n        bt_carrier_test->bt_test,\n        \"Power\",\n        COUNT_OF(bt_param_power),\n        bt_carrier_test_param_channel,\n        bt_carrier_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_power[0].str);\n    bt_carrier_test->power = BtPower0dB;\n\n    bt_carrier_test->timer =\n        furi_timer_alloc(bt_test_carrier_timer_callback, FuriTimerTypePeriodic, bt_carrier_test);\n\n    return bt_carrier_test;\n}\n\nvoid bt_carrier_test_free(BtCarrierTest* bt_carrier_test) {\n    furi_assert(bt_carrier_test);\n    bt_test_free(bt_carrier_test->bt_test);\n    furi_timer_free(bt_carrier_test->timer);\n    free(bt_carrier_test);\n}\n\nView* bt_carrier_test_get_view(BtCarrierTest* bt_carrier_test) {\n    furi_assert(bt_carrier_test);\n    return bt_test_get_view(bt_carrier_test->bt_test);\n}\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_carrier_test.h",
    "content": "#pragma once\n#include <gui/view.h>\n\ntypedef struct BtCarrierTest BtCarrierTest;\n\nBtCarrierTest* bt_carrier_test_alloc(void);\n\nvoid bt_carrier_test_free(BtCarrierTest* bt_carrier_test);\n\nView* bt_carrier_test_get_view(BtCarrierTest* bt_carrier_test);\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_packet_test.c",
    "content": "#include \"bt_packet_test.h\"\n#include \"bt_test.h\"\n#include \"bt_test_types.h\"\n#include <furi_hal_bt.h>\n\nstruct BtPacketTest {\n    BtTest* bt_test;\n    BtTestMode mode;\n    BtTestChannel channel;\n    BtTestDataRate data_rate;\n    FuriTimer* timer;\n};\n\nstatic BtTestParamValue bt_param_mode[] = {\n    {.value = BtTestModeRx, .str = \"Rx\"},\n    {.value = BtTestModeTx, .str = \"Tx\"},\n};\n\nstatic BtTestParamValue bt_param_channel[] = {\n    {.value = BtTestChannel2402, .str = \"2402 MHz\"},\n    {.value = BtTestChannel2440, .str = \"2440 MHz\"},\n    {.value = BtTestChannel2480, .str = \"2480 MHz\"},\n};\n\nstatic BtTestParamValue bt_param_data_rate[] = {\n    {.value = BtDataRate1M, .str = \"1 Mbps\"},\n    {.value = BtDataRate2M, .str = \"2 Mbps\"},\n};\n\nstatic void bt_packet_test_start(BtPacketTest* bt_packet_test) {\n    furi_assert(bt_packet_test);\n    if(bt_packet_test->mode == BtTestModeRx) {\n        furi_hal_bt_start_packet_rx(bt_packet_test->channel, bt_packet_test->data_rate);\n        furi_timer_start(bt_packet_test->timer, furi_kernel_get_tick_frequency() / 4);\n    } else if(bt_packet_test->mode == BtTestModeTx) {\n        furi_hal_bt_start_packet_tx(bt_packet_test->channel, 1, bt_packet_test->data_rate);\n    }\n}\n\nstatic void bt_packet_test_stop(BtPacketTest* bt_packet_test) {\n    furi_assert(bt_packet_test);\n    if(bt_packet_test->mode == BtTestModeTx) {\n        furi_hal_bt_stop_packet_test();\n        bt_test_set_packets_tx(bt_packet_test->bt_test, furi_hal_bt_get_transmitted_packets());\n    } else if(bt_packet_test->mode == BtTestModeRx) {\n        bt_test_set_packets_rx(bt_packet_test->bt_test, furi_hal_bt_stop_packet_test());\n        furi_timer_stop(bt_packet_test->timer);\n    }\n}\n\nstatic uint32_t bt_packet_test_param_changed(BtTestParam* param, BtTestParamValue* param_val) {\n    furi_assert(param);\n    uint8_t index = bt_test_get_current_value_index(param);\n    bt_test_set_current_value_text(param, param_val[index].str);\n    return param_val[index].value;\n}\n\nstatic void bt_packet_test_mode_changed(BtTestParam* param) {\n    BtPacketTest* bt_packet_test = bt_test_get_context(param);\n    bt_packet_test_stop(bt_packet_test);\n    bt_packet_test->mode = bt_packet_test_param_changed(param, bt_param_mode);\n}\n\nstatic void bt_packet_test_channel_changed(BtTestParam* param) {\n    BtPacketTest* bt_packet_test = bt_test_get_context(param);\n    bt_packet_test_stop(bt_packet_test);\n    bt_packet_test->channel = bt_packet_test_param_changed(param, bt_param_channel);\n}\n\nstatic void bt_packet_test_param_channel(BtTestParam* param) {\n    BtPacketTest* bt_packet_test = bt_test_get_context(param);\n    bt_packet_test_stop(bt_packet_test);\n    bt_packet_test->data_rate = bt_packet_test_param_changed(param, bt_param_data_rate);\n}\n\nstatic void bt_packet_test_change_state_callback(BtTestState state, void* context) {\n    furi_assert(context);\n    BtPacketTest* bt_packet_test = context;\n    if(state == BtTestStateStarted) {\n        bt_packet_test_start(bt_packet_test);\n    } else if(state == BtTestStateStopped) {\n        bt_packet_test_stop(bt_packet_test);\n    }\n}\n\nstatic void bt_packet_test_exit_callback(void* context) {\n    furi_assert(context);\n    BtPacketTest* bt_packet_test = context;\n    bt_packet_test_stop(bt_packet_test);\n}\n\nstatic void bt_test_packet_timer_callback(void* context) {\n    furi_assert(context);\n    BtPacketTest* bt_packet_test = context;\n    if(bt_packet_test->mode == BtTestModeRx) {\n        bt_test_set_rssi(bt_packet_test->bt_test, furi_hal_bt_get_rssi());\n    }\n}\n\nBtPacketTest* bt_packet_test_alloc(void) {\n    BtPacketTest* bt_packet_test = malloc(sizeof(BtPacketTest));\n    bt_packet_test->bt_test = bt_test_alloc();\n    bt_test_set_context(bt_packet_test->bt_test, bt_packet_test);\n    bt_test_set_change_state_callback(\n        bt_packet_test->bt_test, bt_packet_test_change_state_callback);\n    bt_test_set_back_callback(bt_packet_test->bt_test, bt_packet_test_exit_callback);\n\n    BtTestParam* param;\n    param = bt_test_param_add(\n        bt_packet_test->bt_test,\n        \"Mode\",\n        COUNT_OF(bt_param_mode),\n        bt_packet_test_mode_changed,\n        bt_packet_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_mode[0].str);\n    bt_packet_test->mode = BtTestModeRx;\n\n    param = bt_test_param_add(\n        bt_packet_test->bt_test,\n        \"Channel\",\n        COUNT_OF(bt_param_channel),\n        bt_packet_test_channel_changed,\n        bt_packet_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_channel[0].str);\n    bt_packet_test->channel = BtTestChannel2402;\n\n    param = bt_test_param_add(\n        bt_packet_test->bt_test,\n        \"Data rate\",\n        COUNT_OF(bt_param_data_rate),\n        bt_packet_test_param_channel,\n        bt_packet_test);\n    bt_test_set_current_value_index(param, 0);\n    bt_test_set_current_value_text(param, bt_param_data_rate[0].str);\n    bt_packet_test->data_rate = BtDataRate1M;\n\n    bt_packet_test->timer =\n        furi_timer_alloc(bt_test_packet_timer_callback, FuriTimerTypePeriodic, bt_packet_test);\n\n    return bt_packet_test;\n}\n\nvoid bt_packet_test_free(BtPacketTest* bt_packet_test) {\n    furi_assert(bt_packet_test);\n    bt_test_free(bt_packet_test->bt_test);\n    furi_timer_free(bt_packet_test->timer);\n    free(bt_packet_test);\n}\n\nView* bt_packet_test_get_view(BtPacketTest* bt_packet_test) {\n    furi_assert(bt_packet_test);\n    return bt_test_get_view(bt_packet_test->bt_test);\n}\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_packet_test.h",
    "content": "#pragma once\n#include <gui/view.h>\n\ntypedef struct BtPacketTest BtPacketTest;\n\nBtPacketTest* bt_packet_test_alloc(void);\n\nvoid bt_packet_test_free(BtPacketTest* bt_packet_test);\n\nView* bt_packet_test_get_view(BtPacketTest* bt_packet_test);\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_test.c",
    "content": "#include \"bt_test.h\"\n\n#include <gui/canvas.h>\n#include <gui/elements.h>\n\n#include <lib/toolbox/float_tools.h>\n#include <m-array.h>\n#include <furi.h>\n#include <inttypes.h>\n#include <stdint.h>\n\nstruct BtTestParam {\n    const char* label;\n    uint8_t current_value_index;\n    FuriString* current_value_text;\n    uint8_t values_count;\n    BtTestParamChangeCallback change_callback;\n    void* context;\n};\n\nARRAY_DEF(BtTestParamArray, BtTestParam, M_POD_OPLIST); //-V658\n\nstruct BtTest {\n    View* view;\n    BtTestChangeStateCallback change_state_callback;\n    BtTestBackCallback back_callback;\n    void* context;\n};\n\ntypedef struct {\n    BtTestState state;\n    BtTestParamArray_t params;\n    uint8_t position;\n    uint8_t window_position;\n    const char* message;\n    float rssi;\n    uint32_t packets_num_rx;\n    uint32_t packets_num_tx;\n} BtTestModel;\n\n#define BT_TEST_START_MESSAGE \"Ok - Start\"\n#define BT_TEST_STOP_MESSAGE  \"Ok - Stop\"\n\nstatic void bt_test_process_up(BtTest* bt_test);\nstatic void bt_test_process_down(BtTest* bt_test);\nstatic void bt_test_process_left(BtTest* bt_test);\nstatic void bt_test_process_right(BtTest* bt_test);\nstatic void bt_test_process_ok(BtTest* bt_test);\nstatic void bt_test_process_back(BtTest* bt_test);\n\nstatic void bt_test_draw_callback(Canvas* canvas, void* _model) {\n    BtTestModel* model = _model;\n    char info_str[32];\n\n    const uint8_t param_height = 16;\n    const uint8_t param_width = 123;\n\n    canvas_clear(canvas);\n\n    uint8_t position = 0;\n    BtTestParamArray_it_t it;\n\n    canvas_set_font(canvas, FontSecondary);\n    for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);\n        BtTestParamArray_next(it)) {\n        uint8_t param_position = position - model->window_position;\n        uint8_t params_on_screen = 3;\n        uint8_t y_offset = 0;\n\n        if(param_position < params_on_screen) {\n            const BtTestParam* param = BtTestParamArray_cref(it);\n            uint8_t param_y = y_offset + (param_position * param_height);\n            uint8_t param_text_y = param_y + param_height - 4;\n\n            if(position == model->position) {\n                canvas_set_color(canvas, ColorBlack);\n                elements_slightly_rounded_box(\n                    canvas, 0, param_y + 1, param_width, param_height - 2);\n                canvas_set_color(canvas, ColorWhite);\n            } else {\n                canvas_set_color(canvas, ColorBlack);\n            }\n\n            canvas_draw_str(canvas, 6, param_text_y, param->label);\n\n            if(param->current_value_index > 0) {\n                canvas_draw_str(canvas, 50, param_text_y, \"<\");\n            }\n\n            canvas_draw_str(\n                canvas, 61, param_text_y, furi_string_get_cstr(param->current_value_text));\n\n            if(param->current_value_index < (param->values_count - 1)) {\n                canvas_draw_str(canvas, 113, param_text_y, \">\");\n            }\n        }\n\n        position++;\n    }\n\n    elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params));\n    canvas_draw_str(canvas, 6, 60, model->message);\n    if(model->state == BtTestStateStarted) {\n        if(!float_is_equal(model->rssi, 0.0f)) {\n            snprintf(info_str, sizeof(info_str), \"RSSI:%3.1f dB\", (double)model->rssi);\n            canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);\n        }\n    } else if(model->state == BtTestStateStopped) {\n        if(model->packets_num_rx) {\n            snprintf(info_str, sizeof(info_str), \"%\" PRIu32 \" pack rcv\", model->packets_num_rx);\n            canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);\n        } else if(model->packets_num_tx) {\n            snprintf(info_str, sizeof(info_str), \"%\" PRIu32 \" pack sent\", model->packets_num_tx);\n            canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);\n        }\n    }\n}\n\nstatic bool bt_test_input_callback(InputEvent* event, void* context) {\n    BtTest* bt_test = context;\n    furi_assert(bt_test);\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            bt_test_process_up(bt_test);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            bt_test_process_down(bt_test);\n            break;\n        case InputKeyLeft:\n            consumed = true;\n            bt_test_process_left(bt_test);\n            break;\n        case InputKeyRight:\n            consumed = true;\n            bt_test_process_right(bt_test);\n            break;\n        case InputKeyOk:\n            consumed = true;\n            bt_test_process_ok(bt_test);\n            break;\n        case InputKeyBack:\n            consumed = false;\n            bt_test_process_back(bt_test);\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid bt_test_process_up(BtTest* bt_test) {\n    with_view_model( // -V658\n        bt_test->view,\n        BtTestModel * model,\n        {\n            uint8_t params_on_screen = 3;\n            if(model->position > 0) {\n                model->position--;\n                if(((model->position - model->window_position) < 1) &&\n                   model->window_position > 0) {\n                    model->window_position--;\n                }\n            } else {\n                model->position = BtTestParamArray_size(model->params) - 1;\n                if(model->position > (params_on_screen - 1)) {\n                    model->window_position = model->position - (params_on_screen - 1);\n                }\n            }\n        },\n        true);\n}\n\nvoid bt_test_process_down(BtTest* bt_test) {\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            uint8_t params_on_screen = 3;\n            if(model->position < (BtTestParamArray_size(model->params) - 1)) {\n                model->position++;\n                if((model->position - model->window_position) > (params_on_screen - 2) &&\n                   model->window_position <\n                       (BtTestParamArray_size(model->params) - params_on_screen)) {\n                    model->window_position++;\n                }\n            } else {\n                model->position = 0;\n                model->window_position = 0;\n            }\n        },\n        true);\n}\n\nBtTestParam* bt_test_get_selected_param(BtTestModel* model) {\n    BtTestParam* param = NULL;\n\n    BtTestParamArray_it_t it;\n    uint8_t position = 0;\n    for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);\n        BtTestParamArray_next(it)) {\n        if(position == model->position) {\n            break;\n        }\n        position++;\n    }\n\n    param = BtTestParamArray_ref(it);\n\n    furi_assert(param);\n    return param;\n}\n\nvoid bt_test_process_left(BtTest* bt_test) {\n    BtTestParam* param;\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            param = bt_test_get_selected_param(model);\n            if(param->current_value_index > 0) {\n                param->current_value_index--;\n                if(param->change_callback) {\n                    model->state = BtTestStateStopped;\n                    model->message = BT_TEST_START_MESSAGE;\n                    model->rssi = 0.0f;\n                    model->packets_num_rx = 0;\n                    model->packets_num_tx = 0;\n                }\n            }\n        },\n        true);\n    if(param->change_callback) {\n        param->change_callback(param);\n    }\n}\n\nvoid bt_test_process_right(BtTest* bt_test) {\n    BtTestParam* param;\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            param = bt_test_get_selected_param(model);\n            if(param->current_value_index < (param->values_count - 1)) {\n                param->current_value_index++;\n                if(param->change_callback) {\n                    model->state = BtTestStateStopped;\n                    model->message = BT_TEST_START_MESSAGE;\n                    model->rssi = 0.0f;\n                    model->packets_num_rx = 0;\n                    model->packets_num_tx = 0;\n                }\n            }\n        },\n        true);\n    if(param->change_callback) {\n        param->change_callback(param);\n    }\n}\n\nvoid bt_test_process_ok(BtTest* bt_test) {\n    BtTestState state;\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            if(model->state == BtTestStateStarted) {\n                model->state = BtTestStateStopped;\n                model->message = BT_TEST_START_MESSAGE;\n                model->rssi = 0.0f;\n                model->packets_num_rx = 0;\n                model->packets_num_tx = 0;\n            } else if(model->state == BtTestStateStopped) {\n                model->state = BtTestStateStarted;\n                model->message = BT_TEST_STOP_MESSAGE;\n            }\n            state = model->state;\n        },\n        true);\n    if(bt_test->change_state_callback) {\n        bt_test->change_state_callback(state, bt_test->context);\n    }\n}\n\nvoid bt_test_process_back(BtTest* bt_test) {\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            model->state = BtTestStateStopped;\n            model->rssi = 0.0f;\n            model->packets_num_rx = 0;\n            model->packets_num_tx = 0;\n        },\n        false);\n    if(bt_test->back_callback) {\n        bt_test->back_callback(bt_test->context);\n    }\n}\n\nBtTest* bt_test_alloc(void) {\n    BtTest* bt_test = malloc(sizeof(BtTest));\n    bt_test->view = view_alloc();\n    view_set_context(bt_test->view, bt_test);\n    view_allocate_model(bt_test->view, ViewModelTypeLocking, sizeof(BtTestModel));\n    view_set_draw_callback(bt_test->view, bt_test_draw_callback);\n    view_set_input_callback(bt_test->view, bt_test_input_callback);\n\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            model->state = BtTestStateStopped;\n            model->message = \"Ok - Start\";\n            BtTestParamArray_init(model->params);\n            model->position = 0;\n            model->window_position = 0;\n            model->rssi = 0.0f;\n            model->packets_num_tx = 0;\n            model->packets_num_rx = 0;\n        },\n        true);\n\n    return bt_test;\n}\n\nvoid bt_test_free(BtTest* bt_test) {\n    furi_assert(bt_test);\n\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            BtTestParamArray_it_t it;\n            for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);\n                BtTestParamArray_next(it)) {\n                furi_string_free(BtTestParamArray_ref(it)->current_value_text);\n            }\n            BtTestParamArray_clear(model->params);\n        },\n        false);\n    view_free(bt_test->view);\n    free(bt_test);\n}\n\nView* bt_test_get_view(BtTest* bt_test) {\n    furi_assert(bt_test);\n    return bt_test->view;\n}\n\nBtTestParam* bt_test_param_add(\n    BtTest* bt_test,\n    const char* label,\n    uint8_t values_count,\n    BtTestParamChangeCallback change_callback,\n    void* context) {\n    BtTestParam* param = NULL;\n    furi_assert(label);\n    furi_assert(bt_test);\n\n    with_view_model(\n        bt_test->view,\n        BtTestModel * model,\n        {\n            param = BtTestParamArray_push_new(model->params);\n            param->label = label;\n            param->values_count = values_count;\n            param->change_callback = change_callback;\n            param->context = context;\n            param->current_value_index = 0;\n            param->current_value_text = furi_string_alloc();\n        },\n        true);\n\n    return param;\n}\n\nvoid bt_test_set_rssi(BtTest* bt_test, float rssi) {\n    furi_assert(bt_test);\n    with_view_model(bt_test->view, BtTestModel * model, { model->rssi = rssi; }, true);\n}\n\nvoid bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) {\n    furi_assert(bt_test);\n    with_view_model(\n        bt_test->view, BtTestModel * model, { model->packets_num_tx = packets_num; }, true);\n}\n\nvoid bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) {\n    furi_assert(bt_test);\n    with_view_model(\n        bt_test->view, BtTestModel * model, { model->packets_num_rx = packets_num; }, true);\n}\n\nvoid bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) {\n    furi_assert(bt_test);\n    furi_assert(callback);\n    bt_test->change_state_callback = callback;\n}\n\nvoid bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback) {\n    furi_assert(bt_test);\n    furi_assert(callback);\n    bt_test->back_callback = callback;\n}\n\nvoid bt_test_set_context(BtTest* bt_test, void* context) {\n    furi_assert(bt_test);\n    bt_test->context = context;\n}\n\nvoid bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index) {\n    param->current_value_index = current_value_index;\n}\n\nvoid bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) {\n    furi_string_set(param->current_value_text, current_value_text);\n}\n\nuint8_t bt_test_get_current_value_index(BtTestParam* param) {\n    return param->current_value_index;\n}\n\nvoid* bt_test_get_context(BtTestParam* param) {\n    return param->context;\n}\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_test.h",
    "content": "#pragma once\n#include <gui/view.h>\n\ntypedef enum {\n    BtTestStateStarted,\n    BtTestStateStopped,\n} BtTestState;\n\ntypedef struct BtTest BtTest;\ntypedef void (*BtTestChangeStateCallback)(BtTestState state, void* context);\ntypedef void (*BtTestBackCallback)(void* context);\ntypedef struct BtTestParam BtTestParam;\ntypedef void (*BtTestParamChangeCallback)(BtTestParam* param);\n\nBtTest* bt_test_alloc(void);\n\nvoid bt_test_free(BtTest* bt_test);\n\nView* bt_test_get_view(BtTest* bt_test);\n\nBtTestParam* bt_test_param_add(\n    BtTest* bt_test,\n    const char* label,\n    uint8_t values_count,\n    BtTestParamChangeCallback change_callback,\n    void* context);\n\nvoid bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback);\n\nvoid bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback);\n\nvoid bt_test_set_context(BtTest* bt_test, void* context);\n\nvoid bt_test_set_rssi(BtTest* bt_test, float rssi);\n\nvoid bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num);\n\nvoid bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num);\n\nvoid bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index);\n\nvoid bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text);\n\nuint8_t bt_test_get_current_value_index(BtTestParam* param);\n\nvoid* bt_test_get_context(BtTestParam* param);\n"
  },
  {
    "path": "applications/debug/bt_debug_app/views/bt_test_types.h",
    "content": "#pragma once\n\ntypedef enum {\n    BtTestModeRx,\n    BtTestModeTx,\n    BtTestModeTxHopping,\n} BtTestMode;\n\ntypedef enum {\n    BtTestChannel2402 = 0,\n    BtTestChannel2440 = 19,\n    BtTestChannel2480 = 39,\n} BtTestChannel;\n\ntypedef enum {\n    BtPower0dB = 0x19,\n    BtPower2dB = 0x1B,\n    BtPower4dB = 0x1D,\n    BtPower6dB = 0x1F,\n} BtTestPower;\n\ntypedef enum {\n    BtDataRate1M = 1,\n    BtDataRate2M = 2,\n} BtTestDataRate;\n\ntypedef struct {\n    uint32_t value;\n    const char* str;\n} BtTestParamValue;\n"
  },
  {
    "path": "applications/debug/ccid_test/application.fam",
    "content": "App(\n    appid=\"ccid_test\",\n    name=\"CCID Debug\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"ccid_test_app\",\n    requires=[\n        \"gui\",\n    ],\n    provides=[\n        \"ccid_test\",\n    ],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/ccid_test/ccid_test_app.c",
    "content": "#include <stdint.h>\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n#include <gui/gui.h>\n\n#include \"iso7816/iso7816_handler.h\"\n#include \"iso7816/iso7816_t0_apdu.h\"\n#include \"iso7816/iso7816_atr.h\"\n#include \"iso7816/iso7816_response.h\"\n\n#include \"ccid_test_app_commands.h\"\n\ntypedef enum {\n    EventTypeInput,\n} EventType;\n\ntypedef struct {\n    Gui* gui;\n    ViewPort* view_port;\n    FuriMessageQueue* event_queue;\n    FuriHalUsbCcidConfig ccid_cfg;\n    Iso7816Handler* iso7816_handler;\n} CcidTestApp;\n\ntypedef struct {\n    union {\n        InputEvent input;\n    };\n    EventType type;\n} CcidTestAppEvent;\n\ntypedef enum {\n    CcidTestSubmenuIndexInsertSmartcard,\n    CcidTestSubmenuIndexRemoveSmartcard,\n    CcidTestSubmenuIndexInsertSmartcardReader\n} SubmenuIndex;\n\nstatic void ccid_test_app_render_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n    canvas_clear(canvas);\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 10, \"CCID Test App\");\n\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 0, 63, \"Hold [back] to exit\");\n}\n\nstatic void ccid_test_app_input_callback(InputEvent* input_event, void* ctx) {\n    FuriMessageQueue* event_queue = ctx;\n\n    CcidTestAppEvent event;\n    event.type = EventTypeInput;\n    event.input = *input_event;\n    furi_message_queue_put(event_queue, &event, FuriWaitForever);\n}\n\nuint32_t ccid_test_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nCcidTestApp* ccid_test_app_alloc(void) {\n    CcidTestApp* app = malloc(sizeof(CcidTestApp));\n\n    //setup CCID USB\n    // On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist\n    app->ccid_cfg.vid = 0x076B;\n    app->ccid_cfg.pid = 0x3A21;\n\n    app->iso7816_handler = iso7816_handler_alloc();\n    app->iso7816_handler->iso7816_answer_to_reset = iso7816_answer_to_reset;\n    app->iso7816_handler->iso7816_process_command = iso7816_process_command;\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n\n    //viewport\n    app->view_port = view_port_alloc();\n    gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);\n    view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL);\n\n    //message queue\n    app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent));\n    view_port_input_callback_set(app->view_port, ccid_test_app_input_callback, app->event_queue);\n\n    return app;\n}\n\nvoid ccid_test_app_free(CcidTestApp* app) {\n    furi_assert(app);\n\n    //message queue\n    furi_message_queue_free(app->event_queue);\n\n    //view port\n    gui_remove_view_port(app->gui, app->view_port);\n    view_port_free(app->view_port);\n\n    // Close gui record\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    iso7816_handler_free(app->iso7816_handler);\n\n    // Free rest\n    free(app);\n}\n\nint32_t ccid_test_app(void* p) {\n    UNUSED(p);\n\n    //setup view\n    CcidTestApp* app = ccid_test_app_alloc();\n\n    FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();\n    furi_hal_usb_unlock();\n\n    furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true);\n    iso7816_handler_set_usb_ccid_callbacks();\n    furi_hal_usb_ccid_insert_smartcard();\n\n    //handle button events\n    CcidTestAppEvent event;\n    while(1) {\n        FuriStatus event_status =\n            furi_message_queue_get(app->event_queue, &event, FuriWaitForever);\n\n        if(event_status == FuriStatusOk) {\n            if(event.type == EventTypeInput) {\n                if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {\n                    break;\n                }\n            }\n        }\n        view_port_update(app->view_port);\n    }\n\n    //tear down USB\n    iso7816_handler_reset_usb_ccid_callbacks();\n    furi_hal_usb_set_config(usb_mode_prev, NULL);\n\n    //teardown view\n    ccid_test_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/ccid_test/ccid_test_app_commands.c",
    "content": "#include \"iso7816/iso7816_t0_apdu.h\"\n#include \"iso7816/iso7816_response.h\"\n\n//Instruction 1: returns an OK response unconditionally\n//APDU example: 0x01:0x01:0x00:0x00\n//response: SW1=0x90, SW2=0x00\nvoid handle_instruction_01(ISO7816_Response_APDU* response_apdu) {\n    response_apdu->DataLen = 0;\n    iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);\n}\n\n//Instruction 2: expect command with no body, replies wit with a body with two bytes\n//APDU example: 0x01:0x02:0x00:0x00:0x02\n//response: 'bc' (0x62, 0x63) SW1=0x90, SW2=0x00\nvoid handle_instruction_02(\n    uint8_t p1,\n    uint8_t p2,\n    uint16_t lc,\n    uint16_t le,\n    ISO7816_Response_APDU* response_apdu) {\n    if(p1 == 0 && p2 == 0 && lc == 0 && le >= 2) {\n        response_apdu->Data[0] = 0x62;\n        response_apdu->Data[1] = 0x63;\n\n        response_apdu->DataLen = 2;\n\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);\n    } else if(p1 != 0 || p2 != 0) {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);\n    } else {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);\n    }\n}\n\n//Instruction 3: sends a command with a body with two bytes, receives a response with no bytes\n//APDU example: 0x01:0x03:0x00:0x00:0x02:CA:FE\n//response SW1=0x90, SW2=0x00\nvoid handle_instruction_03(\n    uint8_t p1,\n    uint8_t p2,\n    uint16_t lc,\n    ISO7816_Response_APDU* response_apdu) {\n    if(p1 == 0 && p2 == 0 && lc == 2) {\n        response_apdu->DataLen = 0;\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);\n    } else if(p1 != 0 || p2 != 0) {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);\n    } else {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);\n    }\n}\n\n//instruction 4: sends a command with a body with 'n' bytes, receives a response with 'n' bytes\n//APDU example: 0x01:0x04:0x00:0x00:0x04:0x01:0x02:0x03:0x04:0x04\n//receives (0x01, 0x02, 0x03, 0x04) SW1=0x90, SW2=0x00\nvoid handle_instruction_04(\n    uint8_t p1,\n    uint8_t p2,\n    uint16_t lc,\n    uint16_t le,\n    const uint8_t* command_apdu_data_buffer,\n    ISO7816_Response_APDU* response_apdu) {\n    if(p1 == 0 && p2 == 0 && lc > 0 && le > 0 && le >= lc) {\n        for(uint16_t i = 0; i < lc; i++) {\n            response_apdu->Data[i] = command_apdu_data_buffer[i];\n        }\n\n        response_apdu->DataLen = lc;\n\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);\n    } else if(p1 != 0 || p2 != 0) {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);\n    } else {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);\n    }\n}\n\nvoid iso7816_answer_to_reset(Iso7816Atr* atr) {\n    //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00\n    atr->TS = 0x3B;\n    atr->T0 = 0x00;\n}\n\nvoid iso7816_process_command(\n    const ISO7816_Command_APDU* command_apdu,\n    ISO7816_Response_APDU* response_apdu) {\n    //example 1: sends a command with no body, receives a response with no body\n    //sends APDU 0x01:0x01:0x00:0x00\n    //receives SW1=0x90, SW2=0x00\n\n    if(command_apdu->CLA == 0x01) {\n        switch(command_apdu->INS) {\n        case 0x01:\n            handle_instruction_01(response_apdu);\n            break;\n        case 0x02:\n            handle_instruction_02(\n                command_apdu->P1,\n                command_apdu->P2,\n                command_apdu->Lc,\n                command_apdu->Le,\n                response_apdu);\n            break;\n        case 0x03:\n            handle_instruction_03(\n                command_apdu->P1, command_apdu->P2, command_apdu->Lc, response_apdu);\n            break;\n        case 0x04:\n            handle_instruction_04(\n                command_apdu->P1,\n                command_apdu->P2,\n                command_apdu->Lc,\n                command_apdu->Le,\n                command_apdu->Data,\n                response_apdu);\n            break;\n        default:\n            iso7816_set_response(response_apdu, ISO7816_RESPONSE_INSTRUCTION_NOT_SUPPORTED);\n        }\n    } else {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_CLASS_NOT_SUPPORTED);\n    }\n}\n"
  },
  {
    "path": "applications/debug/ccid_test/ccid_test_app_commands.h",
    "content": "#include \"iso7816/iso7816_t0_apdu.h\"\n\nvoid iso7816_answer_to_reset(Iso7816Atr* atr);\n\nvoid iso7816_process_command(\n    const ISO7816_Command_APDU* command_apdu,\n    ISO7816_Response_APDU* response_apdu);\n"
  },
  {
    "path": "applications/debug/ccid_test/client/ccid_client.py",
    "content": "#!/usr/bin/env python\n# pylint: disable=missing-module-docstring, too-many-arguments, consider-using-f-string, missing-function-docstring\nfrom smartcard.System import readers\n\n\ndef test_apdu(connection, test_name, apdu, expected_sw1, expected_sw2, expected_data):\n    print(\"Running test: [%s]\" % test_name)\n    data, sw1, sw2 = connection.transmit(apdu)\n\n    failed = []\n\n    if sw1 != expected_sw1:\n        failed.append(\"SW1: Expected %x, actual %x\" % (expected_sw1, sw1))\n\n    if sw2 != expected_sw2:\n        failed.append(\"SW2: Expected %x, actual %x\" % (expected_sw2, sw2))\n\n    if len(data) != len(expected_data):\n        failed.append(\n            \"Data: Sizes differ: Expected %x, actual %x\"\n            % (len(expected_data), len(data))\n        )\n        print(data)\n    elif len(data) > 0:\n        data_matches = True\n        for i, _ in enumerate(data):\n            if data[i] != expected_data[i]:\n                data_matches = False\n\n        if not data_matches:\n            failed.append(\"Data: Expected %s, actual %s\" % (expected_data, data))\n\n    if len(failed) > 0:\n        print(\"Test failed: \")\n        for failure in failed:\n            print(\"- %s\" % failure)\n    else:\n        print(\"Test passed!\")\n\n\ndef main():\n    r = readers()\n    print(\"Found following smartcard readers: \")\n\n    for i, sc in enumerate(r):\n        print(\"[%d] %s\" % (i, sc))\n\n    print(\"Select the smartcard reader you want to run tests against:\")\n\n    reader_index = int(input())\n\n    if reader_index < len(r):\n        connection = r[reader_index].createConnection()\n\n        connection.connect()\n\n        test_apdu(\n            connection,\n            \"INS 0x01: No Lc, no Data, No Le. Expect no data in return\",\n            [0x01, 0x01, 0x00, 0x00],\n            0x90,\n            0x00,\n            [],\n        )\n\n        test_apdu(\n            connection,\n            \"INS 0x02: No Lc, no Data, Le=2. Expect 2 byte data in return\",\n            [0x01, 0x02, 0x00, 0x00, 0x02],\n            0x90,\n            0x00,\n            [0x62, 0x63],\n        )\n\n        test_apdu(\n            connection,\n            \"INS 0x03: Lc=2, data=[0xCA, 0xFE], No Le. Expect no data in return\",\n            [0x01, 0x03, 0x00, 0x00, 0x02, 0xCA, 0xFE],\n            0x90,\n            0x00,\n            [],\n        )\n\n        test_apdu(\n            connection,\n            \"INS 0x04: Lc=2, data=[0xCA, 0xFE], Le=2. Expect 1 byte data in return\",\n            [0x01, 0x04, 0x00, 0x00, 0x02, 0xCA, 0xFE, 0x02],\n            0x90,\n            0x00,\n            [0xCA, 0xFE],\n        )\n\n        small_apdu = list(range(0, 0x0F))\n\n        test_apdu(\n            connection,\n            \"INS 0x04: Lc=0x0F, data=small_apdu, Le=0x0F. Expect 14 bytes data in return\",\n            [0x01, 0x04, 0x00, 0x00, 0x0F] + small_apdu + [0x0F],\n            0x90,\n            0x00,\n            small_apdu,\n        )\n\n        upper_bound = 0xF0\n        max_apdu = list(range(0, upper_bound))\n\n        test_apdu(\n            connection,\n            \"INS 0x04: Lc=0x%x, data=max_apdu, Le=0x%x. Expect 0x%x bytes data in return\"\n            % (upper_bound, upper_bound, upper_bound),\n            [0x01, 0x04, 0x00, 0x00, upper_bound] + max_apdu + [upper_bound],\n            0x90,\n            0x00,\n            max_apdu,\n        )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "applications/debug/ccid_test/client/requirements.txt",
    "content": "pyscard\n# or sudo apt install python3-pyscard"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_atr.h",
    "content": "#pragma once\n\ntypedef struct {\n    uint8_t TS;\n    uint8_t T0;\n} Iso7816Atr;\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_handler.c",
    "content": "// transforms low level calls such as XFRCallback or ICC Power on to a structured one\n// an application can register these calls and listen for the callbacks defined in Iso7816Callbacks\n\n#include <stdint.h>\n#include <stddef.h>\n#include <furi.h>\n#include <furi_hal.h>\n\n#include \"iso7816_handler.h\"\n\n#include \"iso7816_t0_apdu.h\"\n#include \"iso7816_atr.h\"\n#include \"iso7816_response.h\"\n\nstatic Iso7816Handler* iso7816_handler;\nstatic CcidCallbacks* ccid_callbacks;\nstatic uint8_t* command_apdu_buffer;\nstatic uint8_t* response_apdu_buffer;\n\nvoid iso7816_icc_power_on_callback(uint8_t* atr_data, uint32_t* atr_data_len, void* context) {\n    furi_check(context);\n\n    Iso7816Handler* handler = (Iso7816Handler*)context;\n\n    Iso7816Atr iso7816_atr;\n    handler->iso7816_answer_to_reset(&iso7816_atr);\n\n    furi_assert(iso7816_atr.T0 == 0x00);\n\n    uint8_t atr_buffer[2] = {iso7816_atr.TS, iso7816_atr.T0};\n\n    *atr_data_len = 2;\n\n    memcpy(atr_data, atr_buffer, sizeof(uint8_t) * (*atr_data_len));\n}\n\n//dataBlock points to the buffer\n//dataBlockLen tells reader how nany bytes should be read\nvoid iso7816_xfr_datablock_callback(\n    const uint8_t* pc_to_reader_datablock,\n    uint32_t pc_to_reader_datablock_len,\n    uint8_t* reader_to_pc_datablock,\n    uint32_t* reader_to_pc_datablock_len,\n    void* context) {\n    furi_check(context);\n\n    Iso7816Handler* handler = (Iso7816Handler*)context;\n\n    ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)response_apdu_buffer;\n    ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)command_apdu_buffer;\n\n    uint8_t result = iso7816_read_command_apdu(\n        command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len, CCID_SHORT_APDU_SIZE);\n\n    if(result == ISO7816_READ_COMMAND_APDU_OK) {\n        handler->iso7816_process_command(command_apdu, response_apdu);\n\n        furi_assert(response_apdu->DataLen < CCID_SHORT_APDU_SIZE);\n    } else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LE) {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LE);\n    } else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH) {\n        iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);\n    }\n\n    iso7816_write_response_apdu(response_apdu, reader_to_pc_datablock, reader_to_pc_datablock_len);\n}\n\nIso7816Handler* iso7816_handler_alloc() {\n    iso7816_handler = malloc(sizeof(Iso7816Handler));\n\n    command_apdu_buffer = malloc(sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE);\n    response_apdu_buffer = malloc(sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE);\n\n    ccid_callbacks = malloc(sizeof(CcidCallbacks));\n    ccid_callbacks->icc_power_on_callback = iso7816_icc_power_on_callback;\n    ccid_callbacks->xfr_datablock_callback = iso7816_xfr_datablock_callback;\n\n    return iso7816_handler;\n}\n\nvoid iso7816_handler_set_usb_ccid_callbacks() {\n    furi_hal_usb_ccid_set_callbacks(ccid_callbacks, iso7816_handler);\n}\n\nvoid iso7816_handler_reset_usb_ccid_callbacks() {\n    furi_hal_usb_ccid_set_callbacks(NULL, NULL);\n}\n\nvoid iso7816_handler_free(Iso7816Handler* handler) {\n    free(ccid_callbacks);\n\n    free(command_apdu_buffer);\n    free(response_apdu_buffer);\n\n    free(handler);\n}\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_handler.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include \"iso7816_atr.h\"\n#include \"iso7816_t0_apdu.h\"\n\ntypedef struct {\n    void (*iso7816_answer_to_reset)(Iso7816Atr* atr);\n    void (*iso7816_process_command)(\n        const ISO7816_Command_APDU* command,\n        ISO7816_Response_APDU* response);\n} Iso7816Handler;\n\nIso7816Handler* iso7816_handler_alloc();\n\nvoid iso7816_handler_free(Iso7816Handler* handler);\nvoid iso7816_handler_set_usb_ccid_callbacks();\nvoid iso7816_handler_reset_usb_ccid_callbacks();\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_response.c",
    "content": "#include <stdint.h>\n#include \"iso7816_t0_apdu.h\"\n#include \"iso7816_response.h\"\n\nvoid iso7816_set_response(ISO7816_Response_APDU* responseAPDU, uint16_t responseCode) {\n    responseAPDU->SW1 = (responseCode >> (8 * 1)) & 0xff;\n    responseAPDU->SW2 = (responseCode >> (8 * 0)) & 0xff;\n}\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_response.h",
    "content": "#pragma once\n\n#define ISO7816_RESPONSE_OK 0x9000\n\n#define ISO7816_RESPONSE_WRONG_LENGTH              0x6700\n#define ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2    0x6A00\n#define ISO7816_RESPONSE_WRONG_LE                  0x6C00\n#define ISO7816_RESPONSE_INSTRUCTION_NOT_SUPPORTED 0x6D00\n#define ISO7816_RESPONSE_CLASS_NOT_SUPPORTED       0x6E00\n#define ISO7816_RESPONSE_INTERNAL_EXCEPTION        0x6F00\n\nvoid iso7816_set_response(ISO7816_Response_APDU* responseAPDU, uint16_t responseCode);\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c",
    "content": "/* Implements rudimentary iso7816-3 support for APDU (T=0) */\n#include <stdint.h>\n#include <string.h>\n#include \"iso7816_t0_apdu.h\"\n\n//reads pc_to_reader_datablock_len with pc_to_reader_datablock_len size, translate it into a ISO7816_Command_APDU type\n//extra data will be pointed to commandDataBuffer\nuint8_t iso7816_read_command_apdu(\n    ISO7816_Command_APDU* command,\n    const uint8_t* pc_to_reader_datablock,\n    uint32_t pc_to_reader_datablock_len,\n    uint32_t max_apdu_size) {\n    command->CLA = pc_to_reader_datablock[0];\n    command->INS = pc_to_reader_datablock[1];\n    command->P1 = pc_to_reader_datablock[2];\n    command->P2 = pc_to_reader_datablock[3];\n\n    if(pc_to_reader_datablock_len == 4) {\n        command->Lc = 0;\n        command->Le = 0;\n        command->LePresent = false;\n\n        return ISO7816_READ_COMMAND_APDU_OK;\n    } else if(pc_to_reader_datablock_len == 5) {\n        //short le\n\n        command->Lc = 0;\n        command->Le = pc_to_reader_datablock[4];\n        command->LePresent = true;\n\n        return ISO7816_READ_COMMAND_APDU_OK;\n    } else if(pc_to_reader_datablock_len > 5 && pc_to_reader_datablock[4] != 0x00) {\n        //short lc\n\n        command->Lc = pc_to_reader_datablock[4];\n        if(command->Lc > 0 && command->Lc < max_apdu_size) { //-V560\n            memcpy(command->Data, &pc_to_reader_datablock[5], command->Lc);\n\n            //does it have a short le too?\n            if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 5)) {\n                command->Le = 0;\n                command->LePresent = false;\n                return ISO7816_READ_COMMAND_APDU_OK;\n            } else if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 6)) {\n                command->Le = pc_to_reader_datablock[pc_to_reader_datablock_len - 1];\n                command->LePresent = true;\n\n                return ISO7816_READ_COMMAND_APDU_OK;\n            } else {\n                return ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH;\n            }\n        } else {\n            return ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH;\n        }\n    } else {\n        return ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH;\n    }\n}\n\n//data buffer contains the whole APU response (response + trailer (SW1+SW2))\nvoid iso7816_write_response_apdu(\n    const ISO7816_Response_APDU* response,\n    uint8_t* reader_to_pc_datablock,\n    uint32_t* reader_to_pc_datablock_len) {\n    uint32_t responseDataBufferIndex = 0;\n\n    //response body\n    if(response->DataLen > 0) {\n        while(responseDataBufferIndex < response->DataLen) {\n            reader_to_pc_datablock[responseDataBufferIndex] =\n                response->Data[responseDataBufferIndex];\n            responseDataBufferIndex++;\n        }\n    }\n\n    //trailer\n    reader_to_pc_datablock[responseDataBufferIndex] = response->SW1;\n    responseDataBufferIndex++;\n\n    reader_to_pc_datablock[responseDataBufferIndex] = response->SW2;\n    responseDataBufferIndex++;\n\n    *reader_to_pc_datablock_len = responseDataBufferIndex;\n}\n"
  },
  {
    "path": "applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include \"iso7816_atr.h\"\n#include \"core/common_defines.h\"\n\n#define ISO7816_READ_COMMAND_APDU_OK                 0\n#define ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LE     1\n#define ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH 2\n\ntypedef struct {\n    //header\n    uint8_t CLA;\n    uint8_t INS;\n    uint8_t P1;\n    uint8_t P2;\n\n    //body\n    uint16_t Lc; //data length\n    uint16_t Le; //maximum response data length expected by client\n\n    //Le can have value of 0x00, which actually meand 0x100 = 256\n    bool LePresent;\n    uint8_t Data[0];\n} FURI_PACKED ISO7816_Command_APDU;\n\ntypedef struct {\n    uint8_t SW1;\n    uint8_t SW2;\n    uint16_t DataLen;\n    uint8_t Data[0];\n} FURI_PACKED ISO7816_Response_APDU;\n\nuint8_t iso7816_read_command_apdu(\n    ISO7816_Command_APDU* command,\n    const uint8_t* pc_to_reader_datablock,\n    uint32_t pc_to_reader_datablock_len,\n    uint32_t max_apdu_size);\nvoid iso7816_write_response_apdu(\n    const ISO7816_Response_APDU* response,\n    uint8_t* reader_to_pc_datablock,\n    uint32_t* reader_to_pc_datablock_len);\n"
  },
  {
    "path": "applications/debug/crash_test/application.fam",
    "content": "App(\n    appid=\"crash_test\",\n    name=\"Crash Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"crash_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/crash_test/crash_test.c",
    "content": "#include <furi_hal.h>\n#include <furi.h>\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n\n#define TAG \"CrashTest\"\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Submenu* submenu;\n} CrashTest;\n\ntypedef enum {\n    CrashTestViewSubmenu,\n} CrashTestView;\n\ntypedef enum {\n    CrashTestSubmenuCheck,\n    CrashTestSubmenuCheckMessage,\n    CrashTestSubmenuAssert,\n    CrashTestSubmenuAssertMessage,\n    CrashTestSubmenuCrash,\n    CrashTestSubmenuHalt,\n    CrashTestSubmenuHeapUnderflow,\n    CrashTestSubmenuHeapOverflow,\n} CrashTestSubmenu;\n\nstatic void crash_test_corrupt_heap_underflow(void) {\n    const size_t block_size = 1000;\n    const size_t underflow_size = 123;\n    uint8_t* block = malloc(block_size);\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstringop-overflow\" // that's what we want!\n    memset(block - underflow_size, 0xDD, underflow_size); // -V769\n#pragma GCC diagnostic pop\n\n    free(block); // should crash here (if compiled with DEBUG=1)\n\n    // If we got here, the heap wasn't able to detect our corruption and crash\n    furi_crash(\"Test failed, should've crashed with \\\"FreeRTOS Assert\\\" error\");\n}\n\nstatic void crash_test_corrupt_heap_overflow(void) {\n    const size_t block_size = 1000;\n    const size_t overflow_size = 123;\n    uint8_t* block1 = malloc(block_size);\n    uint8_t* block2 = malloc(block_size);\n    memset(block2, 12, 34); // simulate use to avoid optimization // -V597 // -V1086\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstringop-overflow\" // that's what we want!\n    memset(block1 + block_size, 0xDD, overflow_size); // -V769 // -V512\n#pragma GCC diagnostic pop\n\n    uint8_t* block3 = malloc(block_size);\n    memset(block3, 12, 34); // simulate use to avoid optimization // -V597 // -V1086\n\n    free(block3); // should crash here (if compiled with DEBUG=1)\n    free(block2);\n    free(block1);\n\n    // If we got here, the heap wasn't able to detect our corruption and crash\n    furi_crash(\"Test failed, should've crashed with \\\"FreeRTOS Assert\\\" error\");\n}\n\nstatic void crash_test_submenu_callback(void* context, uint32_t index) {\n    CrashTest* instance = (CrashTest*)context;\n    UNUSED(instance);\n\n    switch(index) {\n    case CrashTestSubmenuCheck:\n        furi_check(false);\n        break;\n    case CrashTestSubmenuCheckMessage:\n        furi_check(false, \"Crash test: furi_check with message\");\n        break;\n    case CrashTestSubmenuAssert:\n        furi_assert(false);\n        break;\n    case CrashTestSubmenuAssertMessage:\n        furi_assert(false, \"Crash test: furi_assert with message\");\n        break;\n    case CrashTestSubmenuCrash:\n        furi_crash(\"Crash test: furi_crash\");\n        break;\n    case CrashTestSubmenuHalt:\n        furi_halt(\"Crash test: furi_halt\");\n        break;\n    case CrashTestSubmenuHeapUnderflow:\n        crash_test_corrupt_heap_underflow();\n        break;\n    case CrashTestSubmenuHeapOverflow:\n        crash_test_corrupt_heap_overflow();\n        break;\n    default:\n        furi_crash();\n    }\n}\n\nstatic uint32_t crash_test_exit_callback(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nCrashTest* crash_test_alloc(void) {\n    CrashTest* instance = malloc(sizeof(CrashTest));\n\n    View* view = NULL;\n\n    instance->gui = furi_record_open(RECORD_GUI);\n    instance->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(\n        instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);\n\n    // Menu\n    instance->submenu = submenu_alloc();\n    view = submenu_get_view(instance->submenu);\n    view_set_previous_callback(view, crash_test_exit_callback);\n    view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view);\n    submenu_add_item(\n        instance->submenu, \"Check\", CrashTestSubmenuCheck, crash_test_submenu_callback, instance);\n    submenu_add_item(\n        instance->submenu,\n        \"Check with message\",\n        CrashTestSubmenuCheckMessage,\n        crash_test_submenu_callback,\n        instance);\n    submenu_add_item(\n        instance->submenu, \"Assert\", CrashTestSubmenuAssert, crash_test_submenu_callback, instance);\n    submenu_add_item(\n        instance->submenu,\n        \"Assert with message\",\n        CrashTestSubmenuAssertMessage,\n        crash_test_submenu_callback,\n        instance);\n    submenu_add_item(\n        instance->submenu, \"Crash\", CrashTestSubmenuCrash, crash_test_submenu_callback, instance);\n    submenu_add_item(\n        instance->submenu, \"Halt\", CrashTestSubmenuHalt, crash_test_submenu_callback, instance);\n    submenu_add_item(\n        instance->submenu,\n        \"Heap underflow\",\n        CrashTestSubmenuHeapUnderflow,\n        crash_test_submenu_callback,\n        instance);\n    submenu_add_item(\n        instance->submenu,\n        \"Heap overflow\",\n        CrashTestSubmenuHeapOverflow,\n        crash_test_submenu_callback,\n        instance);\n\n    return instance;\n}\n\nvoid crash_test_free(CrashTest* instance) {\n    view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu);\n    submenu_free(instance->submenu);\n\n    view_dispatcher_free(instance->view_dispatcher);\n    furi_record_close(RECORD_GUI);\n\n    free(instance);\n}\n\nint32_t crash_test_run(CrashTest* instance) {\n    view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu);\n    view_dispatcher_run(instance->view_dispatcher);\n    return 0;\n}\n\nint32_t crash_test_app(void* p) {\n    UNUSED(p);\n\n    CrashTest* instance = crash_test_alloc();\n\n    int32_t ret = crash_test_run(instance);\n\n    crash_test_free(instance);\n\n    return ret;\n}\n"
  },
  {
    "path": "applications/debug/direct_draw/application.fam",
    "content": "App(\n    appid=\"direct_draw\",\n    name=\"Direct Draw\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"direct_draw_app\",\n    requires=[\"gui\", \"input\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/direct_draw/direct_draw.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <input/input.h>\n\n#define BUFFER_SIZE (32U)\n\ntypedef struct {\n    FuriPubSub* input;\n    FuriPubSubSubscription* input_subscription;\n    Gui* gui;\n    Canvas* canvas;\n    bool stop;\n    uint32_t counter;\n} DirectDraw;\n\nstatic void gui_input_events_callback(const void* value, void* ctx) {\n    furi_assert(value);\n    furi_assert(ctx);\n\n    DirectDraw* instance = ctx;\n    const InputEvent* event = value;\n\n    if(event->key == InputKeyBack && event->type == InputTypeShort) {\n        instance->stop = true;\n    }\n}\n\nstatic DirectDraw* direct_draw_alloc(void) {\n    DirectDraw* instance = malloc(sizeof(DirectDraw));\n\n    instance->input = furi_record_open(RECORD_INPUT_EVENTS);\n    instance->gui = furi_record_open(RECORD_GUI);\n    instance->canvas = gui_direct_draw_acquire(instance->gui);\n\n    instance->input_subscription =\n        furi_pubsub_subscribe(instance->input, gui_input_events_callback, instance);\n\n    return instance;\n}\n\nstatic void direct_draw_free(DirectDraw* instance) {\n    furi_pubsub_unsubscribe(instance->input, instance->input_subscription);\n\n    gui_direct_draw_release(instance->gui);\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_INPUT_EVENTS);\n\n    free(instance);\n}\n\nstatic void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) {\n    size += 16;\n    uint8_t width = canvas_width(canvas) - size;\n    uint8_t height = canvas_height(canvas) - size;\n\n    uint8_t x = counter % width;\n    if((counter / width) % 2) {\n        x = width - x;\n    }\n\n    uint8_t y = counter % height;\n    if((counter / height) % 2) {\n        y = height - y;\n    }\n\n    canvas_draw_box(canvas, x, y, size, size);\n}\n\nstatic void direct_draw_run(DirectDraw* instance) {\n    size_t start = DWT->CYCCNT;\n    size_t counter = 0;\n    float fps = 0;\n\n    furi_thread_set_current_priority(FuriThreadPriorityIdle);\n\n    do {\n        size_t elapsed = DWT->CYCCNT - start;\n        char buffer[BUFFER_SIZE] = {0};\n\n        if(elapsed >= 64000000) {\n            fps = (float)counter / ((float)elapsed / 64000000.0f);\n\n            start = DWT->CYCCNT;\n            counter = 0;\n        }\n        snprintf(buffer, BUFFER_SIZE, \"FPS: %.1f\", (double)fps);\n\n        canvas_reset(instance->canvas);\n        canvas_set_color(instance->canvas, ColorXOR);\n        direct_draw_block(instance->canvas, instance->counter % 16, instance->counter);\n        direct_draw_block(instance->canvas, instance->counter * 2 % 16, instance->counter * 2);\n        direct_draw_block(instance->canvas, instance->counter * 3 % 16, instance->counter * 3);\n        direct_draw_block(instance->canvas, instance->counter * 4 % 16, instance->counter * 4);\n        direct_draw_block(instance->canvas, instance->counter * 5 % 16, instance->counter * 5);\n        canvas_draw_str(instance->canvas, 10, 10, buffer);\n        canvas_commit(instance->canvas);\n\n        counter++;\n        instance->counter++;\n        furi_thread_yield();\n    } while(!instance->stop);\n}\n\nint32_t direct_draw_app(void* p) {\n    UNUSED(p);\n\n    DirectDraw* instance = direct_draw_alloc();\n    direct_draw_run(instance);\n    direct_draw_free(instance);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/display_test/application.fam",
    "content": "App(\n    appid=\"display_test\",\n    name=\"Display Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"display_test_app\",\n    requires=[\"gui\"],\n    fap_libs=[\"u8g2\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/display_test/display_test.c",
    "content": "#include <furi_hal.h>\n#include <furi.h>\n\n// Need access to u8g2\n#include <gui/gui_i.h>\n#include <gui/canvas_i.h>\n#include <u8g2_glue.h>\n\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/variable_item_list.h>\n\n#include \"view_display_test.h\"\n\n#define TAG \"DisplayTest\"\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    ViewDisplayTest* view_display_test;\n    VariableItemList* variable_item_list;\n    Submenu* submenu;\n\n    bool config_bias;\n    uint8_t config_contrast;\n    uint8_t config_regulation_ratio;\n} DisplayTest;\n\ntypedef enum {\n    DisplayTestViewSubmenu,\n    DisplayTestViewConfigure,\n    DisplayTestViewDisplayTest,\n} DisplayTestView;\n\nconst bool config_bias_value[] = {\n    true,\n    false,\n};\nconst char* const config_bias_text[] = {\n    \"1/7\",\n    \"1/9\",\n};\n\nconst uint8_t config_regulation_ratio_value[] = {\n    0b000,\n    0b001,\n    0b010,\n    0b011,\n    0b100,\n    0b101,\n    0b110,\n    0b111,\n};\nconst char* const config_regulation_ratio_text[] = {\n    \"3.0\",\n    \"3.5\",\n    \"4.0\",\n    \"4.5\",\n    \"5.0\",\n    \"5.5\",\n    \"6.0\",\n    \"6.5\",\n};\n\nstatic void display_test_submenu_callback(void* context, uint32_t index) {\n    DisplayTest* instance = (DisplayTest*)context;\n    view_dispatcher_switch_to_view(instance->view_dispatcher, index);\n}\n\nstatic uint32_t display_test_previous_callback(void* context) {\n    UNUSED(context);\n    return DisplayTestViewSubmenu;\n}\n\nstatic uint32_t display_test_exit_callback(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic void display_test_reload_config(DisplayTest* instance) {\n    FURI_LOG_I(\n        TAG,\n        \"contrast: %d, regulation_ratio: %d, bias: %d\",\n        instance->config_contrast,\n        instance->config_regulation_ratio,\n        instance->config_bias);\n    u8x8_d_st756x_init(\n        &instance->gui->canvas->fb.u8x8,\n        instance->config_contrast,\n        instance->config_regulation_ratio,\n        instance->config_bias);\n}\n\nstatic void display_config_set_bias(VariableItem* item) {\n    DisplayTest* instance = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, config_bias_text[index]);\n    instance->config_bias = config_bias_value[index];\n    display_test_reload_config(instance);\n}\n\nstatic void display_config_set_regulation_ratio(VariableItem* item) {\n    DisplayTest* instance = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, config_regulation_ratio_text[index]);\n    instance->config_regulation_ratio = config_regulation_ratio_value[index];\n    display_test_reload_config(instance);\n}\n\nstatic void display_config_set_contrast(VariableItem* item) {\n    DisplayTest* instance = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    FuriString* temp;\n    temp = furi_string_alloc();\n    furi_string_cat_printf(temp, \"%d\", index);\n    variable_item_set_current_value_text(item, furi_string_get_cstr(temp));\n    furi_string_free(temp);\n    instance->config_contrast = index;\n    display_test_reload_config(instance);\n}\n\nDisplayTest* display_test_alloc(void) {\n    DisplayTest* instance = malloc(sizeof(DisplayTest));\n\n    View* view = NULL;\n\n    instance->gui = furi_record_open(RECORD_GUI);\n    instance->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(\n        instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);\n\n    // Test\n    instance->view_display_test = view_display_test_alloc();\n    view = view_display_test_get_view(instance->view_display_test);\n    view_set_previous_callback(view, display_test_previous_callback);\n    view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewDisplayTest, view);\n\n    // Configure\n    instance->variable_item_list = variable_item_list_alloc();\n    view = variable_item_list_get_view(instance->variable_item_list);\n    view_set_previous_callback(view, display_test_previous_callback);\n    view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);\n\n    // Configuration items\n    VariableItem* item;\n    instance->config_bias = false;\n    instance->config_contrast = 32;\n    instance->config_regulation_ratio = 0b101;\n    // Bias\n    item = variable_item_list_add(\n        instance->variable_item_list,\n        \"Bias:\",\n        COUNT_OF(config_bias_value),\n        display_config_set_bias,\n        instance);\n    variable_item_set_current_value_index(item, 1);\n    variable_item_set_current_value_text(item, config_bias_text[1]);\n    // Regulation Ratio\n    item = variable_item_list_add(\n        instance->variable_item_list,\n        \"Reg Ratio:\",\n        COUNT_OF(config_regulation_ratio_value),\n        display_config_set_regulation_ratio,\n        instance);\n    variable_item_set_current_value_index(item, 5);\n    variable_item_set_current_value_text(item, config_regulation_ratio_text[5]);\n    // Contrast\n    item = variable_item_list_add(\n        instance->variable_item_list, \"Contrast:\", 64, display_config_set_contrast, instance);\n    variable_item_set_current_value_index(item, 32);\n    variable_item_set_current_value_text(item, \"32\");\n\n    // Menu\n    instance->submenu = submenu_alloc();\n    view = submenu_get_view(instance->submenu);\n    view_set_previous_callback(view, display_test_exit_callback);\n    view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewSubmenu, view);\n    submenu_add_item(\n        instance->submenu,\n        \"Test\",\n        DisplayTestViewDisplayTest,\n        display_test_submenu_callback,\n        instance);\n    submenu_add_item(\n        instance->submenu,\n        \"Configure\",\n        DisplayTestViewConfigure,\n        display_test_submenu_callback,\n        instance);\n\n    return instance;\n}\n\nvoid display_test_free(DisplayTest* instance) {\n    view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewSubmenu);\n    submenu_free(instance->submenu);\n\n    view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewConfigure);\n    variable_item_list_free(instance->variable_item_list);\n\n    view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewDisplayTest);\n    view_display_test_free(instance->view_display_test);\n\n    view_dispatcher_free(instance->view_dispatcher);\n    furi_record_close(RECORD_GUI);\n\n    free(instance);\n}\n\nint32_t display_test_run(DisplayTest* instance) {\n    UNUSED(instance);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, DisplayTestViewSubmenu);\n    view_dispatcher_run(instance->view_dispatcher);\n\n    return 0;\n}\n\nint32_t display_test_app(void* p) {\n    UNUSED(p);\n\n    DisplayTest* instance = display_test_alloc();\n\n    int32_t ret = display_test_run(instance);\n\n    display_test_free(instance);\n\n    return ret;\n}\n"
  },
  {
    "path": "applications/debug/display_test/view_display_test.c",
    "content": "#include \"view_display_test.h\"\n\ntypedef struct {\n    uint32_t test;\n    uint32_t size;\n    uint32_t counter;\n    bool flip_flop;\n} ViewDisplayTestModel;\n\nstruct ViewDisplayTest {\n    View* view;\n    FuriTimer* timer;\n};\n\nstatic void view_display_test_draw_callback_intro(Canvas* canvas, void* _model) {\n    UNUSED(_model);\n    canvas_draw_str(canvas, 12, 24, \"Use < and > to switch tests\");\n    canvas_draw_str(canvas, 12, 36, \"Use ^ and v to switch size\");\n    canvas_draw_str(canvas, 32, 48, \"Use (o) to flip\");\n}\n\nstatic void view_display_test_draw_callback_fill(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    if(model->flip_flop) {\n        uint8_t width = canvas_width(canvas);\n        uint8_t height = canvas_height(canvas);\n        canvas_draw_box(canvas, 0, 0, width, height);\n    }\n}\n\nstatic void view_display_test_draw_callback_hstripe(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    uint8_t block = 1 + model->size;\n    uint8_t width = canvas_width(canvas);\n    uint8_t height = canvas_height(canvas);\n\n    for(uint8_t y = model->flip_flop * block; y < height; y += 2 * block) {\n        canvas_draw_box(canvas, 0, y, width, block);\n    }\n}\n\nstatic void view_display_test_draw_callback_vstripe(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    uint8_t block = 1 + model->size;\n    uint8_t width = canvas_width(canvas);\n    uint8_t height = canvas_height(canvas);\n\n    for(uint8_t x = model->flip_flop * block; x < width; x += 2 * block) {\n        canvas_draw_box(canvas, x, 0, block, height);\n    }\n}\n\nstatic void view_display_test_draw_callback_check(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    uint8_t block = 1 + model->size;\n    uint8_t width = canvas_width(canvas);\n    uint8_t height = canvas_height(canvas);\n\n    bool flip_flop = model->flip_flop;\n    for(uint8_t x = 0; x < width; x += block) {\n        bool last_flip_flop = flip_flop;\n        for(uint8_t y = 0; y < height; y += block) {\n            if(flip_flop) {\n                canvas_draw_box(canvas, x, y, block, block);\n            }\n            flip_flop = !flip_flop;\n        }\n        if(last_flip_flop == flip_flop) {\n            flip_flop = !flip_flop;\n        }\n    }\n}\n\nstatic void view_display_test_draw_callback_move(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    uint8_t block = 1 + model->size;\n    uint8_t width = canvas_width(canvas) - block;\n    uint8_t height = canvas_height(canvas) - block;\n\n    uint8_t x = model->counter % width;\n    if((model->counter / width) % 2) {\n        x = width - x;\n    }\n\n    uint8_t y = model->counter % height;\n    if((model->counter / height) % 2) {\n        y = height - y;\n    }\n\n    canvas_draw_box(canvas, x, y, block, block);\n}\n\nconst ViewDrawCallback view_display_test_tests[] = {\n    view_display_test_draw_callback_intro,\n    view_display_test_draw_callback_fill,\n    view_display_test_draw_callback_hstripe,\n    view_display_test_draw_callback_vstripe,\n    view_display_test_draw_callback_check,\n    view_display_test_draw_callback_move,\n};\n\nstatic void view_display_test_draw_callback(Canvas* canvas, void* _model) {\n    ViewDisplayTestModel* model = _model;\n    view_display_test_tests[model->test](canvas, _model);\n}\n\nstatic bool view_display_test_input_callback(InputEvent* event, void* context) {\n    ViewDisplayTest* instance = context;\n\n    bool consumed = false;\n    if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n        with_view_model(\n            instance->view,\n            ViewDisplayTestModel * model,\n            {\n                if(event->key == InputKeyLeft && model->test > 0) {\n                    model->test--;\n                    consumed = true;\n                } else if(\n                    event->key == InputKeyRight &&\n                    model->test < (COUNT_OF(view_display_test_tests) - 1)) {\n                    model->test++;\n                    consumed = true;\n                } else if(event->key == InputKeyDown && model->size > 0) {\n                    model->size--;\n                    consumed = true;\n                } else if(event->key == InputKeyUp && model->size < 24) {\n                    model->size++;\n                    consumed = true;\n                } else if(event->key == InputKeyOk) {\n                    model->flip_flop = !model->flip_flop;\n                    consumed = true;\n                }\n            },\n            consumed);\n    }\n\n    return consumed;\n}\n\nstatic void view_display_test_enter(void* context) {\n    ViewDisplayTest* instance = context;\n    furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 32);\n}\n\nstatic void view_display_test_exit(void* context) {\n    ViewDisplayTest* instance = context;\n    furi_timer_stop(instance->timer);\n}\n\nstatic void view_display_test_timer_callback(void* context) {\n    ViewDisplayTest* instance = context;\n    with_view_model(instance->view, ViewDisplayTestModel * model, { model->counter++; }, true);\n}\n\nViewDisplayTest* view_display_test_alloc(void) {\n    ViewDisplayTest* instance = malloc(sizeof(ViewDisplayTest));\n\n    instance->view = view_alloc();\n    view_set_context(instance->view, instance);\n    view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(ViewDisplayTestModel));\n    view_set_draw_callback(instance->view, view_display_test_draw_callback);\n    view_set_input_callback(instance->view, view_display_test_input_callback);\n    view_set_enter_callback(instance->view, view_display_test_enter);\n    view_set_exit_callback(instance->view, view_display_test_exit);\n\n    instance->timer =\n        furi_timer_alloc(view_display_test_timer_callback, FuriTimerTypePeriodic, instance);\n\n    return instance;\n}\n\nvoid view_display_test_free(ViewDisplayTest* instance) {\n    furi_assert(instance);\n\n    furi_timer_free(instance->timer);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* view_display_test_get_view(ViewDisplayTest* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/debug/display_test/view_display_test.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <gui/view.h>\n\ntypedef struct ViewDisplayTest ViewDisplayTest;\n\nViewDisplayTest* view_display_test_alloc(void);\n\nvoid view_display_test_free(ViewDisplayTest* instance);\n\nView* view_display_test_get_view(ViewDisplayTest* instance);\n"
  },
  {
    "path": "applications/debug/event_loop_blink_test/application.fam",
    "content": "App(\n    appid=\"event_loop_blink_test\",\n    name=\"Event Loop Blink Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"event_loop_blink_test_app\",\n    requires=[\"input\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/event_loop_blink_test/event_loop_blink_test.c",
    "content": "#include <furi.h>\n#include <furi_hal_resources.h>\n\n#include <gui/gui.h>\n#include <gui/elements.h>\n#include <gui/view_port.h>\n\n#include <input/input.h>\n\n#define TAG \"EventLoopBlinkTest\"\n\n#define TIMER_COUNT (6U)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriMessageQueue* input_queue;\n    FuriEventLoopTimer* timers[TIMER_COUNT];\n} EventLoopBlinkTestApp;\n\nstatic const GpioPin* blink_gpio_pins[] = {\n    &gpio_ext_pa7,\n    &gpio_ext_pa6,\n    &gpio_ext_pa4,\n    &gpio_ext_pb3,\n    &gpio_ext_pb2,\n    &gpio_ext_pc3,\n};\n\nstatic_assert(COUNT_OF(blink_gpio_pins) == TIMER_COUNT);\n\nstatic const uint32_t timer_intervals[] = {\n    25,\n    50,\n    100,\n    200,\n    400,\n    800,\n};\n\nstatic_assert(COUNT_OF(timer_intervals) == TIMER_COUNT);\n\nstatic void blink_gpio_init(void) {\n    for(size_t i = 0; i < TIMER_COUNT; ++i) {\n        furi_hal_gpio_init_simple(blink_gpio_pins[i], GpioModeOutputPushPull);\n        furi_hal_gpio_write(blink_gpio_pins[i], false);\n    }\n\n    furi_hal_gpio_init_simple(&gpio_ext_pc0, GpioModeOutputPushPull);\n    furi_hal_gpio_write(&gpio_ext_pc0, false);\n}\n\nstatic void blink_gpio_deinit(void) {\n    for(size_t i = 0; i < TIMER_COUNT; ++i) {\n        furi_hal_gpio_write(blink_gpio_pins[i], false);\n        furi_hal_gpio_init_simple(blink_gpio_pins[i], GpioModeAnalog);\n    }\n\n    furi_hal_gpio_write(&gpio_ext_pc0, false);\n    furi_hal_gpio_init_simple(&gpio_ext_pc0, GpioModeAnalog);\n}\n\nstatic void view_port_draw_callback(Canvas* canvas, void* context) {\n    UNUSED(context);\n    canvas_clear(canvas);\n    elements_text_box(\n        canvas,\n        0,\n        0,\n        canvas_width(canvas),\n        canvas_height(canvas),\n        AlignCenter,\n        AlignCenter,\n        \"\\e#Event Loop Timers Test\\e#\\n\"\n        \"Press buttons\\n\"\n        \"to enable or disable timers\\n\"\n        \"\\e#Exit\\e# = long press \\e#Back\\e#\",\n        false);\n}\n\nstatic void view_port_input_callback(InputEvent* input_event, void* context) {\n    EventLoopBlinkTestApp* app = context;\n    furi_message_queue_put(app->input_queue, input_event, 0);\n}\n\nstatic void input_queue_callback(FuriEventLoopObject* object, void* context) {\n    FuriMessageQueue* queue = object;\n    EventLoopBlinkTestApp* app = context;\n\n    InputEvent event;\n    FuriStatus status = furi_message_queue_get(queue, &event, 0);\n    furi_assert(status == FuriStatusOk);\n\n    if(event.type == InputTypeShort) {\n        const size_t timer_idx = event.key;\n        furi_assert(timer_idx < TIMER_COUNT);\n\n        FuriEventLoopTimer* timer = app->timers[timer_idx];\n\n        if(furi_event_loop_timer_is_running(timer)) {\n            furi_event_loop_timer_stop(timer);\n        } else {\n            furi_event_loop_timer_restart(timer);\n        }\n\n    } else if(event.type == InputTypeLong) {\n        if(event.key == InputKeyBack) {\n            furi_event_loop_stop(app->event_loop);\n        }\n    }\n}\n\nstatic void blink_timer_callback(void* context) {\n    const GpioPin* gpio = blink_gpio_pins[(size_t)context];\n    furi_hal_gpio_write(gpio, !furi_hal_gpio_read(gpio));\n}\n\nstatic void event_loop_tick_callback(void* context) {\n    UNUSED(context);\n    furi_hal_gpio_write(&gpio_ext_pc0, !furi_hal_gpio_read(&gpio_ext_pc0));\n}\n\nint32_t event_loop_blink_test_app(void* arg) {\n    UNUSED(arg);\n\n    blink_gpio_init();\n\n    EventLoopBlinkTestApp app;\n\n    app.event_loop = furi_event_loop_alloc();\n    app.input_queue = furi_message_queue_alloc(3, sizeof(InputEvent));\n\n    for(size_t i = 0; i < TIMER_COUNT; ++i) {\n        app.timers[i] = furi_event_loop_timer_alloc(\n            app.event_loop, blink_timer_callback, FuriEventLoopTimerTypePeriodic, (void*)i);\n        furi_event_loop_timer_start(app.timers[i], timer_intervals[i]);\n    }\n\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, view_port_draw_callback, &app);\n    view_port_input_callback_set(view_port, view_port_input_callback, &app);\n\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    furi_event_loop_tick_set(app.event_loop, 500, event_loop_tick_callback, &app);\n    furi_event_loop_subscribe_message_queue(\n        app.event_loop, app.input_queue, FuriEventLoopEventIn, input_queue_callback, &app);\n\n    furi_event_loop_run(app.event_loop);\n\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n\n    furi_record_close(RECORD_GUI);\n\n    furi_event_loop_unsubscribe(app.event_loop, app.input_queue);\n    furi_message_queue_free(app.input_queue);\n\n    for(size_t i = 0; i < TIMER_COUNT; ++i) {\n        furi_event_loop_timer_free(app.timers[i]);\n    }\n\n    furi_event_loop_free(app.event_loop);\n\n    blink_gpio_deinit();\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/expansion_test/application.fam",
    "content": "App(\n    appid=\"expansion_test\",\n    name=\"Expansion Module Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"expansion_test_app\",\n    requires=[\"expansion_start\"],\n    fap_libs=[\"assets\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n    fap_file_assets=\"assets\",\n)\n"
  },
  {
    "path": "applications/debug/expansion_test/assets/test.txt",
    "content": "\"Did you ever hear the tragedy of Darth Plagueis the Wise?\"\n\"No.\"\n\"I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis... was a Dark Lord of the Sith so powerful and so wise, he could use the Force to influence the midi-chlorians... to create... life. He had such a knowledge of the dark side, he could even keep the ones he cared about... from dying.\"\n\"He could actually... save people from death?\"\n\"The dark side of the Force is a pathway to many abilities... some consider to be unnatural.\"\n\"Wh– What happened to him?\"\n\"He became so powerful, the only thing he was afraid of was... losing his power. Which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew. Then his apprentice killed him in his sleep. It's ironic. He could save others from death, but not himself.\"\n\"Is it possible to learn this power?\"\n\"Not from a Jedi.\"\n"
  },
  {
    "path": "applications/debug/expansion_test/expansion_test.c",
    "content": "/**\n * @file expansion_test.c\n * @brief Expansion module support testing application.\n *\n * Before running, connect pins using the following scheme:\n * 13 -> 16 (USART TX to LPUART RX)\n * 14 -> 15 (USART RX to LPUART TX)\n *\n * Optional: Connect an LED with an appropriate series resistor\n * between pins 1 and 8. It will always be on if the device is\n * connected to USB power, so unplug it before running the app.\n *\n * What this application does:\n *\n * - Enables module support and emulates the module on a single device\n *   (hence the above connection),\n * - Connects to the expansion module service, sets baud rate,\n * - Enables OTG (5V) on GPIO via plain expansion protocol,\n * - Waits 5 cycles of idle loop (1 second),\n * - Starts the RPC session,\n * - Disables OTG (5V) on GPIO via RPC messages,\n * - Waits 5 cycles of idle loop (1 second),\n * - Creates a directory at `/ext/ExpansionTest` and writes a file\n *   named `test.txt` under it,\n * - Plays an audiovisual alert (sound and blinking display),\n * - Enables OTG (5V) on GPIO via RPC messages,\n * - Waits 5 cycles of idle loop (1 second),\n * - Stops the RPC session,\n * - Disables OTG (5V) on GPIO via plain expansion protocol,\n * - Exits (plays a sound if any of the above steps failed).\n */\n#include <furi.h>\n\n#include <furi_hal_resources.h>\n\n#include <furi_hal_serial.h>\n#include <furi_hal_serial_control.h>\n\n#include <pb.h>\n#include <pb_decode.h>\n#include <pb_encode.h>\n\n#include <flipper.pb.h>\n\n#include <storage/storage.h>\n#include <expansion/expansion.h>\n#include <notification/notification_messages.h>\n#include <expansion/expansion_protocol.h>\n\n#define TAG \"ExpansionTest\"\n\n#define TEST_DIR_PATH  EXT_PATH(TAG)\n#define TEST_FILE_NAME \"test.txt\"\n#define TEST_FILE_PATH EXT_PATH(TAG \"/\" TEST_FILE_NAME)\n\n#define HOST_SERIAL_ID   (FuriHalSerialIdLpuart)\n#define MODULE_SERIAL_ID (FuriHalSerialIdUsart)\n\n#define RECEIVE_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum))\n\ntypedef enum {\n    ExpansionTestAppFlagData = 1U << 0,\n    ExpansionTestAppFlagExit = 1U << 1,\n} ExpansionTestAppFlag;\n\n#define EXPANSION_TEST_APP_ALL_FLAGS (ExpansionTestAppFlagData | ExpansionTestAppFlagExit)\n\ntypedef struct {\n    FuriThreadId thread_id;\n    Expansion* expansion;\n    FuriHalSerialHandle* handle;\n    FuriStreamBuffer* buf;\n    ExpansionFrame frame;\n    PB_Main msg;\n    Storage* storage;\n} ExpansionTestApp;\n\nstatic void expansion_test_app_serial_rx_callback(\n    FuriHalSerialHandle* handle,\n    FuriHalSerialRxEvent event,\n    void* context) {\n    furi_assert(handle);\n    furi_assert(context);\n    ExpansionTestApp* app = context;\n\n    if(event == FuriHalSerialRxEventData) {\n        const uint8_t data = furi_hal_serial_async_rx(handle);\n        furi_stream_buffer_send(app->buf, &data, sizeof(data), 0);\n        furi_thread_flags_set(app->thread_id, ExpansionTestAppFlagData);\n    }\n}\n\nstatic ExpansionTestApp* expansion_test_app_alloc(void) {\n    ExpansionTestApp* instance = malloc(sizeof(ExpansionTestApp));\n    instance->buf = furi_stream_buffer_alloc(RECEIVE_BUFFER_SIZE, 1);\n    return instance;\n}\n\nstatic void expansion_test_app_free(ExpansionTestApp* instance) {\n    furi_stream_buffer_free(instance->buf);\n    free(instance);\n}\n\nstatic void expansion_test_app_start(ExpansionTestApp* instance) {\n    instance->thread_id = furi_thread_get_current_id();\n    instance->expansion = furi_record_open(RECORD_EXPANSION);\n    instance->handle = furi_hal_serial_control_acquire(MODULE_SERIAL_ID);\n    furi_check(instance->handle);\n    // Configure the serial port\n    furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE);\n    // Start waiting for the initial pulse\n    expansion_set_listen_serial(instance->expansion, HOST_SERIAL_ID);\n\n    furi_hal_serial_async_rx_start(\n        instance->handle, expansion_test_app_serial_rx_callback, instance, false);\n}\n\nstatic void expansion_test_app_stop(ExpansionTestApp* instance) {\n    // Disable expansion module support\n    expansion_disable(instance->expansion);\n    // Give back the module handle\n    furi_hal_serial_control_release(instance->handle);\n    // Restore expansion user settings\n    expansion_enable(instance->expansion);\n    furi_record_close(RECORD_EXPANSION);\n}\n\nstatic inline bool expansion_test_app_is_success_response(const ExpansionFrame* response) {\n    return response->header.type == ExpansionFrameTypeStatus &&\n           response->content.status.error == ExpansionFrameErrorNone;\n}\n\nstatic inline bool expansion_test_app_is_success_rpc_message(const PB_Main* message) {\n    return (message->command_status == PB_CommandStatus_OK ||\n            message->command_status == PB_CommandStatus_ERROR_STORAGE_EXIST) &&\n           (message->which_content == PB_Main_empty_tag);\n}\n\nstatic size_t expansion_test_app_receive_callback(uint8_t* data, size_t data_size, void* context) {\n    ExpansionTestApp* instance = context;\n\n    size_t received_size = 0;\n\n    while(true) {\n        received_size += furi_stream_buffer_receive(\n            instance->buf, data + received_size, data_size - received_size, 0);\n        if(received_size == data_size) break;\n\n        const uint32_t flags = furi_thread_flags_wait(\n            EXPANSION_TEST_APP_ALL_FLAGS, FuriFlagWaitAny, EXPANSION_PROTOCOL_TIMEOUT_MS);\n\n        // Exit on any error\n        if(flags & FuriFlagError) break;\n    }\n\n    return received_size;\n}\n\nstatic size_t\n    expansion_test_app_send_callback(const uint8_t* data, size_t data_size, void* context) {\n    ExpansionTestApp* instance = context;\n\n    furi_hal_serial_tx(instance->handle, data, data_size);\n    furi_hal_serial_tx_wait_complete(instance->handle);\n\n    return data_size;\n}\n\nstatic bool expansion_test_app_receive_frame(ExpansionTestApp* instance, ExpansionFrame* frame) {\n    return expansion_protocol_decode(frame, expansion_test_app_receive_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool\n    expansion_test_app_send_status_response(ExpansionTestApp* instance, ExpansionFrameError error) {\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeStatus,\n        .content.status.error = error,\n    };\n    return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool expansion_test_app_send_heartbeat(ExpansionTestApp* instance) {\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeHeartbeat,\n        .content.heartbeat = {},\n    };\n    return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool\n    expansion_test_app_send_baud_rate_request(ExpansionTestApp* instance, uint32_t baud_rate) {\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeBaudRate,\n        .content.baud_rate.baud = baud_rate,\n    };\n    return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool expansion_test_app_send_control_request(\n    ExpansionTestApp* instance,\n    ExpansionFrameControlCommand command) {\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeControl,\n        .content.control.command = command,\n    };\n    return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool expansion_test_app_send_data_request(\n    ExpansionTestApp* instance,\n    const uint8_t* data,\n    size_t data_size) {\n    furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE);\n\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeData,\n        .content.data.size = data_size,\n    };\n\n    memcpy(frame.content.data.bytes, data, data_size);\n    return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool expansion_test_app_rpc_encode_callback(\n    pb_ostream_t* stream,\n    const pb_byte_t* data,\n    size_t data_size) {\n    ExpansionTestApp* instance = stream->state;\n\n    size_t size_sent = 0;\n\n    while(size_sent < data_size) {\n        const size_t current_size = MIN(data_size - size_sent, EXPANSION_PROTOCOL_MAX_DATA_SIZE);\n        if(!expansion_test_app_send_data_request(instance, data + size_sent, current_size)) break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_is_success_response(&instance->frame)) break;\n        size_sent += current_size;\n    }\n\n    return size_sent == data_size;\n}\n\nstatic bool expansion_test_app_send_rpc_request(ExpansionTestApp* instance, PB_Main* message) {\n    pb_ostream_t stream = {\n        .callback = expansion_test_app_rpc_encode_callback,\n        .state = instance,\n        .max_size = SIZE_MAX,\n        .bytes_written = 0,\n        .errmsg = NULL,\n    };\n\n    const bool success = pb_encode_ex(&stream, &PB_Main_msg, message, PB_ENCODE_DELIMITED);\n    pb_release(&PB_Main_msg, message);\n    return success;\n}\n\nstatic bool expansion_test_app_receive_rpc_request(ExpansionTestApp* instance, PB_Main* message) {\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_send_status_response(instance, ExpansionFrameErrorNone)) break;\n        if(instance->frame.header.type != ExpansionFrameTypeData) break;\n        pb_istream_t stream = pb_istream_from_buffer(\n            instance->frame.content.data.bytes, instance->frame.content.data.size);\n        if(!pb_decode_ex(&stream, &PB_Main_msg, message, PB_DECODE_DELIMITED)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_send_presence(ExpansionTestApp* instance) {\n    // Send pulses to emulate module insertion\n    const uint8_t init = 0xAA;\n    furi_hal_serial_tx(instance->handle, &init, sizeof(init));\n    furi_hal_serial_tx_wait_complete(instance->handle);\n    return true;\n}\n\nstatic bool expansion_test_app_wait_ready(ExpansionTestApp* instance) {\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_handshake(ExpansionTestApp* instance) {\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_send_baud_rate_request(instance, 230400)) break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_is_success_response(&instance->frame)) break;\n        furi_hal_serial_set_br(instance->handle, 230400);\n        furi_delay_ms(EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS);\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_enable_otg(ExpansionTestApp* instance, bool enable) {\n    bool success = false;\n\n    do {\n        const ExpansionFrameControlCommand command = enable ?\n                                                         ExpansionFrameControlCommandEnableOtg :\n                                                         ExpansionFrameControlCommandDisableOtg;\n        if(!expansion_test_app_send_control_request(instance, command)) break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_is_success_response(&instance->frame)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_start_rpc(ExpansionTestApp* instance) {\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStartRpc))\n            break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_is_success_response(&instance->frame)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_rpc_mkdir(ExpansionTestApp* instance) {\n    bool success = false;\n\n    instance->msg.command_id++;\n    instance->msg.command_status = PB_CommandStatus_OK;\n    instance->msg.which_content = PB_Main_storage_mkdir_request_tag;\n    instance->msg.has_next = false;\n    instance->msg.content.storage_mkdir_request.path = TEST_DIR_PATH;\n\n    do {\n        if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_rpc_write(ExpansionTestApp* instance) {\n    bool success = false;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    do {\n        if(!storage_file_open(file, APP_ASSETS_PATH(TEST_FILE_NAME), FSAM_READ, FSOM_OPEN_EXISTING))\n            break;\n\n        const uint64_t file_size = storage_file_size(file);\n\n        instance->msg.command_id++;\n        instance->msg.command_status = PB_CommandStatus_OK;\n        instance->msg.which_content = PB_Main_storage_write_request_tag;\n        instance->msg.has_next = false;\n        instance->msg.content.storage_write_request.path = TEST_FILE_PATH;\n        instance->msg.content.storage_write_request.has_file = true;\n        instance->msg.content.storage_write_request.file.data =\n            malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(file_size));\n        instance->msg.content.storage_write_request.file.data->size = file_size;\n\n        const size_t bytes_read = storage_file_read(\n            file, instance->msg.content.storage_write_request.file.data->bytes, file_size);\n\n        if(bytes_read != file_size) {\n            pb_release(&PB_Main_msg, &instance->msg);\n            break;\n        }\n\n        if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break;\n        success = true;\n    } while(false);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return success;\n}\n\nstatic bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) {\n    bool success = false;\n\n    instance->msg.command_id++;\n    instance->msg.command_status = PB_CommandStatus_OK;\n    instance->msg.which_content = PB_Main_system_play_audiovisual_alert_request_tag;\n    instance->msg.has_next = false;\n\n    do {\n        if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break;\n        if(instance->msg.which_content != PB_Main_empty_tag) break;\n        if(instance->msg.command_status != PB_CommandStatus_OK) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_rpc_enable_otg(ExpansionTestApp* instance, bool enable) {\n    bool success = false;\n\n    instance->msg.command_id++;\n    instance->msg.command_status = PB_CommandStatus_OK;\n    instance->msg.which_content = PB_Main_gpio_set_otg_mode_tag;\n    instance->msg.content.gpio_set_otg_mode.mode = enable ? PB_Gpio_GpioOtgMode_ON :\n                                                            PB_Gpio_GpioOtgMode_OFF;\n    instance->msg.has_next = false;\n\n    do {\n        if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break;\n        if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break;\n        if(instance->msg.which_content != PB_Main_empty_tag) break;\n        if(instance->msg.command_status != PB_CommandStatus_OK) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) {\n    uint32_t num_cycles_done;\n    for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) {\n        if(!expansion_test_app_send_heartbeat(instance)) break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break;\n        furi_delay_ms(EXPANSION_PROTOCOL_TIMEOUT_MS - 50);\n    }\n\n    return num_cycles_done == num_cycles;\n}\n\nstatic bool expansion_test_app_stop_rpc(ExpansionTestApp* instance) {\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStopRpc))\n            break;\n        if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;\n        if(!expansion_test_app_is_success_response(&instance->frame)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nint32_t expansion_test_app(void* p) {\n    UNUSED(p);\n\n    ExpansionTestApp* instance = expansion_test_app_alloc();\n    expansion_test_app_start(instance);\n\n    bool success = false;\n\n    do {\n        if(!expansion_test_app_send_presence(instance)) break;\n        if(!expansion_test_app_wait_ready(instance)) break;\n        if(!expansion_test_app_handshake(instance)) break;\n        if(!expansion_test_app_enable_otg(instance, true)) break;\n        if(!expansion_test_app_idle(instance, 5)) break;\n        if(!expansion_test_app_start_rpc(instance)) break;\n        if(!expansion_test_app_rpc_enable_otg(instance, false)) break;\n        if(!expansion_test_app_idle(instance, 5)) break;\n        if(!expansion_test_app_rpc_mkdir(instance)) break;\n        if(!expansion_test_app_rpc_write(instance)) break;\n        if(!expansion_test_app_rpc_alert(instance)) break;\n        if(!expansion_test_app_rpc_enable_otg(instance, true)) break;\n        if(!expansion_test_app_idle(instance, 5)) break;\n        if(!expansion_test_app_stop_rpc(instance)) break;\n        if(!expansion_test_app_enable_otg(instance, false)) break;\n        success = true;\n    } while(false);\n\n    expansion_test_app_stop(instance);\n    expansion_test_app_free(instance);\n\n    if(!success) {\n        NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n        notification_message(notification, &sequence_error);\n        furi_record_close(RECORD_NOTIFICATION);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/file_browser_test/application.fam",
    "content": "App(\n    appid=\"file_browser_test\",\n    name=\"File Browser Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"file_browser_app\",\n    requires=[\"gui\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n    fap_icon_assets=\"icons\",\n)\n"
  },
  {
    "path": "applications/debug/file_browser_test/file_browser_app.c",
    "content": "#include \"file_browser_app_i.h\"\n#include <file_browser_test_icons.h>\n\n#include <gui/modules/file_browser.h>\n#include <storage/storage.h>\n#include <lib/toolbox/path.h>\n#include <furi.h>\n#include <furi_hal.h>\n\nstatic bool file_browser_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    FileBrowserApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool file_browser_app_back_event_callback(void* context) {\n    furi_assert(context);\n    FileBrowserApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void file_browser_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    FileBrowserApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nFileBrowserApp* file_browser_app_alloc(char* arg) {\n    UNUSED(arg);\n    FileBrowserApp* app = malloc(sizeof(FileBrowserApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&file_browser_scene_handlers, app);\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, file_browser_app_tick_event_callback, 500);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, file_browser_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, file_browser_app_back_event_callback);\n\n    app->widget = widget_alloc();\n\n    app->file_path = furi_string_alloc();\n    app->file_browser = file_browser_alloc(app->file_path);\n    file_browser_configure(app->file_browser, \"*\", NULL, true, false, &I_badusb_10px, true);\n\n    view_dispatcher_add_view(\n        app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));\n    view_dispatcher_add_view(\n        app->view_dispatcher, FileBrowserAppViewResult, widget_get_view(app->widget));\n    view_dispatcher_add_view(\n        app->view_dispatcher, FileBrowserAppViewBrowser, file_browser_get_view(app->file_browser));\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    scene_manager_next_scene(app->scene_manager, FileBrowserSceneStart);\n\n    return app;\n}\n\nvoid file_browser_app_free(FileBrowserApp* app) {\n    furi_assert(app);\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewStart);\n    view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewResult);\n    view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewBrowser);\n    widget_free(app->widget);\n    file_browser_free(app->file_browser);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_DIALOGS);\n\n    furi_string_free(app->file_path);\n\n    free(app);\n}\n\nint32_t file_browser_app(void* p) {\n    FileBrowserApp* file_browser_app = file_browser_app_alloc((char*)p);\n\n    view_dispatcher_run(file_browser_app->view_dispatcher);\n\n    file_browser_app_free(file_browser_app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/file_browser_test/file_browser_app_i.h",
    "content": "#pragma once\n\n#include \"scenes/file_browser_scene.h\"\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/file_browser.h>\n#include <dialogs/dialogs.h>\n#include <notification/notification_messages.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/widget.h>\n\ntypedef struct FileBrowserApp FileBrowserApp;\n\nstruct FileBrowserApp {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    DialogsApp* dialogs;\n    Widget* widget;\n    FileBrowser* file_browser;\n\n    FuriString* file_path;\n};\n\ntypedef enum {\n    FileBrowserAppViewStart,\n    FileBrowserAppViewBrowser,\n    FileBrowserAppViewResult,\n} FileBrowserAppView;\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene.c",
    "content": "#include \"file_browser_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const file_browser_scene_on_enter_handlers[])(void*) = {\n#include \"file_browser_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const file_browser_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"file_browser_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const file_browser_scene_on_exit_handlers[])(void* context) = {\n#include \"file_browser_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers file_browser_scene_handlers = {\n    .on_enter_handlers = file_browser_scene_on_enter_handlers,\n    .on_event_handlers = file_browser_scene_on_event_handlers,\n    .on_exit_handlers = file_browser_scene_on_exit_handlers,\n    .scene_num = FileBrowserSceneNum,\n};\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) FileBrowserScene##id,\ntypedef enum {\n#include \"file_browser_scene_config.h\"\n    FileBrowserSceneNum,\n} FileBrowserScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers file_browser_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"file_browser_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"file_browser_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"file_browser_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene_browser.c",
    "content": "#include \"../file_browser_app_i.h\"\n#include <furi.h>\n\n#define DEFAULT_PATH \"/\"\n#define EXTENSION    \"*\"\n\nbool file_browser_scene_browser_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    FileBrowserApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_next_scene(app->scene_manager, FileBrowserSceneResult);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeTick) {\n    }\n    return consumed;\n}\n\nstatic void file_browser_callback(void* context) {\n    FileBrowserApp* app = context;\n    furi_assert(app);\n    view_dispatcher_send_custom_event(app->view_dispatcher, SceneManagerEventTypeCustom);\n}\n\nvoid file_browser_scene_browser_on_enter(void* context) {\n    FileBrowserApp* app = context;\n\n    file_browser_set_callback(app->file_browser, file_browser_callback, app);\n\n    file_browser_start(app->file_browser, app->file_path);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewBrowser);\n}\n\nvoid file_browser_scene_browser_on_exit(void* context) {\n    FileBrowserApp* app = context;\n\n    file_browser_stop(app->file_browser);\n}\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene_config.h",
    "content": "ADD_SCENE(file_browser, start, Start)\nADD_SCENE(file_browser, browser, Browser)\nADD_SCENE(file_browser, result, Result)\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene_result.c",
    "content": "#include \"../file_browser_app_i.h\"\n#include <furi.h>\n\nvoid file_browser_scene_result_ok_callback(InputType type, void* context) {\n    furi_assert(context);\n    FileBrowserApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, type);\n}\n\nbool file_browser_scene_result_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    //FileBrowserApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeTick) {\n    }\n    return consumed;\n}\n\nvoid file_browser_scene_result_on_enter(void* context) {\n    FileBrowserApp* app = context;\n\n    widget_add_string_multiline_element(\n        app->widget,\n        64,\n        10,\n        AlignCenter,\n        AlignTop,\n        FontSecondary,\n        furi_string_get_cstr(app->file_path));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewResult);\n}\n\nvoid file_browser_scene_result_on_exit(void* context) {\n    UNUSED(context);\n    FileBrowserApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/debug/file_browser_test/scenes/file_browser_scene_start.c",
    "content": "#include \"../file_browser_app_i.h\"\n\n#include <furi_hal.h>\n#include <gui/modules/widget_elements/widget_element_i.h>\n#include <storage/storage.h>\n\nstatic void\n    file_browser_scene_start_ok_callback(GuiButtonType result, InputType type, void* context) {\n    UNUSED(result);\n    furi_assert(context);\n    FileBrowserApp* app = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, type);\n    }\n}\n\nbool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) {\n    FileBrowserApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        furi_string_set(app->file_path, EXT_PATH(\"badusb/demo_windows.txt\"));\n        scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeTick) {\n    }\n    return consumed;\n}\n\nvoid file_browser_scene_start_on_enter(void* context) {\n    FileBrowserApp* app = context;\n\n    widget_add_string_multiline_element(\n        app->widget, 64, 20, AlignCenter, AlignTop, FontSecondary, \"Press OK to start\");\n\n    widget_add_button_element(\n        app->widget, GuiButtonTypeCenter, \"Ok\", file_browser_scene_start_ok_callback, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewStart);\n}\n\nvoid file_browser_scene_start_on_exit(void* context) {\n    UNUSED(context);\n    FileBrowserApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/debug/infrared_test/application.fam",
    "content": "App(\n    appid=\"infrared_test\",\n    name=\"Infrared Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"infrared_test_app\",\n    fap_category=\"Debug\",\n    targets=[\"f7\"],\n)\n"
  },
  {
    "path": "applications/debug/infrared_test/infrared_test.c",
    "content": "#include <furi.h>\n#include <furi_hal_infrared.h>\n\n#define TAG \"InfraredTest\"\n\n#define CARRIER_FREQ_HZ (38000UL)\n#define CARRIER_DUTY    (0.33f)\n\n#define BURST_DURATION_US (600UL)\n#define BURST_COUNT       (50UL)\n\ntypedef struct {\n    bool level;\n    uint32_t count;\n} InfraredTestApp;\n\nstatic FuriHalInfraredTxGetDataState\n    infrared_test_app_tx_data_callback(void* context, uint32_t* duration, bool* level) {\n    furi_assert(context);\n    furi_assert(duration);\n    furi_assert(level);\n\n    InfraredTestApp* app = context;\n\n    *duration = BURST_DURATION_US;\n    *level = app->level;\n\n    app->level = !app->level;\n    app->count += 1;\n\n    if(app->count < BURST_COUNT * 2) {\n        return FuriHalInfraredTxGetDataStateOk;\n    } else {\n        return FuriHalInfraredTxGetDataStateLastDone;\n    }\n}\n\nint32_t infrared_test_app(void* arg) {\n    UNUSED(arg);\n\n    InfraredTestApp app = {\n        .level = true,\n    };\n\n    FURI_LOG_I(TAG, \"Starting test signal on PA7\");\n\n    furi_hal_infrared_set_tx_output(FuriHalInfraredTxPinExtPA7);\n    furi_hal_infrared_async_tx_set_data_isr_callback(infrared_test_app_tx_data_callback, &app);\n    furi_hal_infrared_async_tx_start(CARRIER_FREQ_HZ, CARRIER_DUTY);\n    furi_hal_infrared_async_tx_wait_termination();\n    furi_hal_infrared_set_tx_output(FuriHalInfraredTxPinInternal);\n\n    FURI_LOG_I(TAG, \"Test signal end\");\n    FURI_LOG_I(\n        TAG,\n        \"The measured signal should be %luus +-%.1fus\",\n        (app.count - 1) * BURST_DURATION_US,\n        (double)1000000.0 / CARRIER_FREQ_HZ);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/keypad_test/application.fam",
    "content": "App(\n    appid=\"keypad_test\",\n    name=\"Keypad Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"keypad_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/keypad_test/keypad_test.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <input/input.h>\n\n#define TAG \"KeypadTest\"\n\ntypedef struct {\n    bool press[5];\n    uint16_t up;\n    uint16_t down;\n    uint16_t left;\n    uint16_t right;\n    uint16_t ok;\n    FuriMutex* mutex;\n} KeypadTestState;\n\nstatic void keypad_test_reset_state(KeypadTestState* state) {\n    state->left = 0;\n    state->right = 0;\n    state->up = 0;\n    state->down = 0;\n    state->ok = 0;\n}\n\nstatic void keypad_test_render_callback(Canvas* canvas, void* ctx) {\n    KeypadTestState* state = ctx;\n    furi_mutex_acquire(state->mutex, FuriWaitForever);\n    canvas_clear(canvas);\n    char strings[5][20];\n\n    snprintf(strings[0], 20, \"Ok: %d\", state->ok);\n    snprintf(strings[1], 20, \"L: %d\", state->left);\n    snprintf(strings[2], 20, \"R: %d\", state->right);\n    snprintf(strings[3], 20, \"U: %d\", state->up);\n    snprintf(strings[4], 20, \"D: %d\", state->down);\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 10, \"Keypad test\");\n\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 0, 24, strings[1]);\n    canvas_draw_str(canvas, 35, 24, strings[2]);\n    canvas_draw_str(canvas, 0, 36, strings[3]);\n    canvas_draw_str(canvas, 35, 36, strings[4]);\n    canvas_draw_str(canvas, 0, 48, strings[0]);\n    canvas_draw_circle(canvas, 100, 26, 25);\n\n    if(state->press[0]) canvas_draw_disc(canvas, 118, 26, 5);\n    if(state->press[1]) canvas_draw_disc(canvas, 82, 26, 5);\n    if(state->press[2]) canvas_draw_disc(canvas, 100, 8, 5);\n    if(state->press[3]) canvas_draw_disc(canvas, 100, 44, 5);\n    if(state->press[4]) canvas_draw_disc(canvas, 100, 26, 5);\n\n    canvas_draw_str(canvas, 10, 63, \"[back] - reset, hold to exit\");\n\n    furi_mutex_release(state->mutex);\n}\n\nstatic void keypad_test_input_callback(InputEvent* input_event, void* ctx) {\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t keypad_test_app(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));\n    KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL};\n    state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    ViewPort* view_port = view_port_alloc();\n\n    view_port_draw_callback_set(view_port, keypad_test_render_callback, &state);\n    view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue);\n\n    // Open GUI and register view_port\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    InputEvent event;\n    while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {\n        furi_mutex_acquire(state.mutex, FuriWaitForever);\n        FURI_LOG_I(\n            TAG,\n            \"key: %s type: %s\",\n            input_get_key_name(event.key),\n            input_get_type_name(event.type));\n\n        if(event.key == InputKeyRight) {\n            if(event.type == InputTypePress) {\n                state.press[0] = true;\n            } else if(event.type == InputTypeRelease) {\n                state.press[0] = false;\n            } else if(event.type == InputTypeShort) {\n                ++state.right;\n            }\n        } else if(event.key == InputKeyLeft) {\n            if(event.type == InputTypePress) {\n                state.press[1] = true;\n            } else if(event.type == InputTypeRelease) {\n                state.press[1] = false;\n            } else if(event.type == InputTypeShort) {\n                ++state.left;\n            }\n        } else if(event.key == InputKeyUp) {\n            if(event.type == InputTypePress) {\n                state.press[2] = true;\n            } else if(event.type == InputTypeRelease) {\n                state.press[2] = false;\n            } else if(event.type == InputTypeShort) {\n                ++state.up;\n            }\n        } else if(event.key == InputKeyDown) {\n            if(event.type == InputTypePress) {\n                state.press[3] = true;\n            } else if(event.type == InputTypeRelease) {\n                state.press[3] = false;\n            } else if(event.type == InputTypeShort) {\n                ++state.down;\n            }\n        } else if(event.key == InputKeyOk) {\n            if(event.type == InputTypePress) {\n                state.press[4] = true;\n            } else if(event.type == InputTypeRelease) {\n                state.press[4] = false;\n            } else if(event.type == InputTypeShort) {\n                ++state.ok;\n            }\n        } else if(event.key == InputKeyBack) {\n            if(event.type == InputTypeLong) {\n                furi_mutex_release(state.mutex);\n                break;\n            } else if(event.type == InputTypeShort) {\n                keypad_test_reset_state(&state);\n            }\n        }\n\n        furi_mutex_release(state.mutex);\n        view_port_update(view_port);\n    }\n\n    // remove & free all stuff created by app\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n    furi_mutex_free(state.mutex);\n\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/application.fam",
    "content": "App(\n    appid=\"lfrfid_debug\",\n    name=\"LF-RFID Debug\",\n    apptype=FlipperAppType.DEBUG,\n    targets=[\"f7\"],\n    entry_point=\"lfrfid_debug_app\",\n    requires=[\n        \"gui\",\n    ],\n    provides=[\n        \"lfrfid_debug\",\n    ],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/lfrfid_debug.c",
    "content": "#include \"lfrfid_debug_i.h\"\n\nstatic bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    LfRfidDebug* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool lfrfid_debug_back_event_callback(void* context) {\n    furi_assert(context);\n    LfRfidDebug* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic LfRfidDebug* lfrfid_debug_alloc(void) {\n    LfRfidDebug* app = malloc(sizeof(LfRfidDebug));\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&lfrfid_debug_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, lfrfid_debug_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, lfrfid_debug_back_event_callback);\n\n    // Open GUI record\n    app->gui = furi_record_open(RECORD_GUI);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Submenu\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, LfRfidDebugViewSubmenu, submenu_get_view(app->submenu));\n\n    // Tune view\n    app->tune_view = lfrfid_debug_view_tune_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        LfRfidDebugViewTune,\n        lfrfid_debug_view_tune_get_view(app->tune_view));\n\n    return app;\n}\n\nstatic void lfrfid_debug_free(LfRfidDebug* app) {\n    furi_assert(app);\n\n    // Submenu\n    view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewSubmenu);\n    submenu_free(app->submenu);\n\n    // Tune view\n    view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewTune);\n    lfrfid_debug_view_tune_free(app->tune_view);\n\n    // View Dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Scene Manager\n    scene_manager_free(app->scene_manager);\n\n    // GUI\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    free(app);\n}\n\nint32_t lfrfid_debug_app(void* p) {\n    UNUSED(p);\n    LfRfidDebug* app = lfrfid_debug_alloc();\n\n    scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneStart);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    lfrfid_debug_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/lfrfid_debug_i.h",
    "content": "#pragma once\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n\n#include <gui/modules/submenu.h>\n\n#include \"views/lfrfid_debug_view_tune.h\"\n#include \"scenes/lfrfid_debug_scene.h\"\n\ntypedef struct LfRfidDebug LfRfidDebug;\n\nstruct LfRfidDebug {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n\n    // Common Views\n    Submenu* submenu;\n    LfRfidTuneView* tune_view;\n};\n\ntypedef enum {\n    LfRfidDebugViewSubmenu,\n    LfRfidDebugViewTune,\n} LfRfidDebugView;\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c",
    "content": "#include \"../lfrfid_debug_i.h\"\n\ntypedef enum {\n    SubmenuIndexTune,\n} SubmenuIndex;\n\nstatic void lfrfid_debug_scene_start_submenu_callback(void* context, uint32_t index) {\n    LfRfidDebug* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_debug_scene_start_on_enter(void* context) {\n    LfRfidDebug* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu, \"Tune\", SubmenuIndexTune, lfrfid_debug_scene_start_submenu_callback, app);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidDebugSceneStart));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewSubmenu);\n}\n\nbool lfrfid_debug_scene_start_on_event(void* context, SceneManagerEvent event) {\n    LfRfidDebug* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexTune) {\n            scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneTune);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_debug_scene_start_on_exit(void* context) {\n    LfRfidDebug* app = context;\n\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c",
    "content": "#include \"../lfrfid_debug_i.h\"\n#include <furi_hal.h>\n\nstatic void comparator_trigger_callback(bool level, void* comp_ctx) {\n    UNUSED(comp_ctx);\n    furi_hal_gpio_write(&gpio_ext_pa7, !level);\n}\n\nvoid lfrfid_debug_view_tune_callback(void* context) {\n    LfRfidDebug* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA);\n}\n\nvoid lfrfid_debug_scene_tune_on_enter(void* context) {\n    LfRfidDebug* app = context;\n\n    furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);\n\n    furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app);\n    furi_hal_rfid_comp_start();\n\n    furi_hal_rfid_tim_read_start(125000, 0.5);\n\n    lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune);\n}\n\nbool lfrfid_debug_scene_tune_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(event);\n\n    LfRfidDebug* app = context;\n    bool consumed = false;\n\n    if(lfrfid_debug_view_tune_is_dirty(app->tune_view)) {\n        furi_hal_rfid_set_read_period(lfrfid_debug_view_tune_get_arr(app->tune_view));\n        furi_hal_rfid_set_read_pulse(lfrfid_debug_view_tune_get_ccr(app->tune_view));\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_debug_scene_tune_on_exit(void* context) {\n    UNUSED(context);\n\n    furi_hal_rfid_comp_stop();\n    furi_hal_rfid_comp_set_callback(NULL, NULL);\n\n    furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);\n    furi_hal_rfid_tim_read_stop();\n    furi_hal_rfid_pins_reset();\n}\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c",
    "content": "#include \"lfrfid_debug_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const lfrfid_debug_on_enter_handlers[])(void*) = {\n#include \"lfrfid_debug_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const lfrfid_debug_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"lfrfid_debug_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const lfrfid_debug_on_exit_handlers[])(void* context) = {\n#include \"lfrfid_debug_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers lfrfid_debug_scene_handlers = {\n    .on_enter_handlers = lfrfid_debug_on_enter_handlers,\n    .on_event_handlers = lfrfid_debug_on_event_handlers,\n    .on_exit_handlers = lfrfid_debug_on_exit_handlers,\n    .scene_num = LfRfidDebugSceneNum,\n};\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) LfRfidDebugScene##id,\ntypedef enum {\n#include \"lfrfid_debug_scene_config.h\"\n    LfRfidDebugSceneNum,\n} LfRfidDebugScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers lfrfid_debug_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"lfrfid_debug_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"lfrfid_debug_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"lfrfid_debug_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene_config.h",
    "content": "ADD_SCENE(lfrfid_debug, start, Start)\nADD_SCENE(lfrfid_debug, tune, Tune)\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c",
    "content": "#include \"lfrfid_debug_view_tune.h\"\n#include <gui/elements.h>\n\n#define TEMP_STR_LEN 128\n\nstruct LfRfidTuneView {\n    View* view;\n};\n\ntypedef struct {\n    bool dirty;\n    bool fine;\n    uint32_t ARR;\n    uint32_t CCR;\n    int pos;\n    void (*update_callback)(void* context);\n    void* update_context;\n} LfRfidTuneViewModel;\n\nstatic void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) {\n    LfRfidTuneViewModel* model = _model;\n    canvas_set_color(canvas, ColorBlack);\n\n    if(model->fine) {\n        canvas_draw_box(\n            canvas,\n            128 - canvas_string_width(canvas, \"Fine\") - 4,\n            0,\n            canvas_string_width(canvas, \"Fine\") + 4,\n            canvas_current_font_height(canvas) + 1);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, \"Fine\");\n    canvas_set_color(canvas, ColorBlack);\n\n    char buffer[TEMP_STR_LEN + 1];\n    double freq = ((double)SystemCoreClock / (model->ARR + 1));\n    double duty = (double)((model->CCR + 1) * 100) / (model->ARR + 1);\n    snprintf(\n        buffer,\n        TEMP_STR_LEN,\n        \"%sARR: %lu\\n\"\n        \"freq = %.4f\\n\"\n        \"%sCCR: %lu\\n\"\n        \"duty = %.4f\",\n        model->pos == 0 ? \">\" : \"\",\n        model->ARR,\n        freq,\n        model->pos == 1 ? \">\" : \"\",\n        model->CCR,\n        duty);\n    elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer);\n}\n\nstatic void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            if(model->pos > 0) model->pos--;\n        },\n        true);\n}\n\nstatic void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            if(model->pos < 1) model->pos++;\n        },\n        true);\n}\n\nstatic void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            if(model->pos == 0) {\n                if(model->fine) {\n                    model->ARR -= 1;\n                } else {\n                    model->ARR -= 10;\n                }\n            } else if(model->pos == 1) {\n                if(model->fine) {\n                    model->CCR -= 1;\n                } else {\n                    model->CCR -= 10;\n                }\n            }\n\n            model->dirty = true;\n        },\n        true);\n}\n\nstatic void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            if(model->pos == 0) {\n                if(model->fine) {\n                    model->ARR += 1;\n                } else {\n                    model->ARR += 10;\n                }\n            } else if(model->pos == 1) {\n                if(model->fine) {\n                    model->CCR += 1;\n                } else {\n                    model->CCR += 10;\n                }\n            }\n\n            model->dirty = true;\n        },\n        true);\n}\n\nstatic void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view, LfRfidTuneViewModel * model, { model->fine = !model->fine; }, true);\n}\n\nstatic bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) {\n    LfRfidTuneView* tune_view = context;\n    bool consumed = false;\n\n    // Process key presses only\n    if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n        consumed = true;\n\n        switch(event->key) {\n        case InputKeyLeft:\n            lfrfid_debug_view_tune_button_left(tune_view);\n            break;\n        case InputKeyRight:\n            lfrfid_debug_view_tune_button_right(tune_view);\n            break;\n        case InputKeyUp:\n            lfrfid_debug_view_tune_button_up(tune_view);\n            break;\n        case InputKeyDown:\n            lfrfid_debug_view_tune_button_down(tune_view);\n            break;\n        case InputKeyOk:\n            lfrfid_debug_view_tune_button_ok(tune_view);\n            break;\n        default:\n            consumed = false;\n            break;\n        }\n\n        if(event->key == InputKeyLeft || event->key == InputKeyRight) {\n            with_view_model(\n                tune_view->view,\n                LfRfidTuneViewModel * model,\n                {\n                    if(model->update_callback) {\n                        model->update_callback(model->update_context);\n                    }\n                },\n                false);\n        }\n    }\n\n    return consumed;\n}\n\nLfRfidTuneView* lfrfid_debug_view_tune_alloc(void) {\n    LfRfidTuneView* tune_view = malloc(sizeof(LfRfidTuneView));\n    tune_view->view = view_alloc();\n    view_set_context(tune_view->view, tune_view);\n    view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel));\n    lfrfid_debug_view_tune_clean(tune_view);\n    view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback);\n    view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback);\n\n    return tune_view;\n}\n\nvoid lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view) {\n    view_free(tune_view->view);\n    free(tune_view);\n}\n\nView* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) {\n    return tune_view->view;\n}\n\nvoid lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            model->dirty = true;\n            model->fine = false;\n            model->ARR = 511;\n            model->CCR = 255;\n            model->pos = 0;\n            model->update_callback = NULL;\n            model->update_context = NULL;\n        },\n        true);\n}\n\nbool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) {\n    bool result = false;\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            result = model->dirty;\n            model->dirty = false;\n        },\n        false);\n\n    return result;\n}\n\nuint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) {\n    uint32_t result = false;\n    with_view_model(tune_view->view, LfRfidTuneViewModel * model, { result = model->ARR; }, false);\n\n    return result;\n}\n\nuint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) {\n    uint32_t result = false;\n    with_view_model(tune_view->view, LfRfidTuneViewModel * model, { result = model->CCR; }, false);\n\n    return result;\n}\n\nvoid lfrfid_debug_view_tune_set_callback(\n    LfRfidTuneView* tune_view,\n    void (*callback)(void* context),\n    void* context) {\n    with_view_model(\n        tune_view->view,\n        LfRfidTuneViewModel * model,\n        {\n            model->update_callback = callback;\n            model->update_context = context;\n        },\n        false);\n}\n"
  },
  {
    "path": "applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h",
    "content": "#pragma once\n#include <gui/view.h>\n\ntypedef struct LfRfidTuneView LfRfidTuneView;\n\nLfRfidTuneView* lfrfid_debug_view_tune_alloc(void);\n\nvoid lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view);\n\nView* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view);\n\nvoid lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view);\n\nbool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view);\n\nuint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view);\n\nuint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view);\n\nvoid lfrfid_debug_view_tune_set_callback(\n    LfRfidTuneView* tune_view,\n    void (*callback)(void* context),\n    void* context);\n"
  },
  {
    "path": "applications/debug/loader_chaining_a/application.fam",
    "content": "App(\n    appid=\"loader_chaining_a\",\n    name=\"Loader Chaining Test: App A\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"chaining_test_app_a\",\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/loader_chaining_a/loader_chaining_a.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n#include <loader/loader.h>\n#include <dialogs/dialogs.h>\n\n#define TAG             \"LoaderChainingA\"\n#define CHAINING_TEST_B \"/ext/apps/Debug/loader_chaining_b.fap\"\n#define NONEXISTENT_APP \"Some nonexistent app\"\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Submenu* submenu;\n\n    Loader* loader;\n\n    DialogsApp* dialogs;\n} LoaderChainingA;\n\ntypedef enum {\n    LoaderChainingASubmenuLaunchB,\n    LoaderChainingASubmenuLaunchBThenA,\n    LoaderChainingASubmenuLaunchNonexistentSilent,\n    LoaderChainingASubmenuLaunchNonexistentGui,\n    LoaderChainingASubmenuLaunchNonexistentGuiThenA,\n} LoaderChainingASubmenu;\n\nstatic void loader_chaining_a_submenu_callback(void* context, uint32_t index) {\n    LoaderChainingA* app = context;\n\n    FuriString* self_path = furi_string_alloc();\n    furi_check(loader_get_application_launch_path(app->loader, self_path));\n\n    switch(index) {\n    case LoaderChainingASubmenuLaunchB:\n        loader_enqueue_launch(app->loader, CHAINING_TEST_B, \"Hello\", LoaderDeferredLaunchFlagGui);\n        view_dispatcher_stop(app->view_dispatcher);\n        break;\n\n    case LoaderChainingASubmenuLaunchBThenA:\n        loader_enqueue_launch(app->loader, CHAINING_TEST_B, \"Hello\", LoaderDeferredLaunchFlagGui);\n        loader_enqueue_launch(\n            app->loader,\n            furi_string_get_cstr(self_path),\n            \"Hello to you from the future\",\n            LoaderDeferredLaunchFlagGui);\n\n        break;\n\n    case LoaderChainingASubmenuLaunchNonexistentSilent:\n        loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagNone);\n        break;\n\n    case LoaderChainingASubmenuLaunchNonexistentGui:\n        loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui);\n        break;\n\n    case LoaderChainingASubmenuLaunchNonexistentGuiThenA:\n        loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui);\n        loader_enqueue_launch(\n            app->loader,\n            furi_string_get_cstr(self_path),\n            \"Hello to you from the future\",\n            LoaderDeferredLaunchFlagGui);\n        break;\n    }\n\n    furi_string_free(self_path);\n    view_dispatcher_stop(app->view_dispatcher);\n}\n\nstatic bool loader_chaining_a_nav_callback(void* context) {\n    LoaderChainingA* app = context;\n    view_dispatcher_stop(app->view_dispatcher);\n    return true;\n}\n\nLoaderChainingA* loader_chaining_a_alloc(void) {\n    LoaderChainingA* app = malloc(sizeof(LoaderChainingA));\n    app->gui = furi_record_open(RECORD_GUI);\n    app->loader = furi_record_open(RECORD_LOADER);\n    app->dialogs = furi_record_open(RECORD_DIALOGS);\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->submenu = submenu_alloc();\n\n    submenu_add_item(\n        app->submenu,\n        \"Launch B\",\n        LoaderChainingASubmenuLaunchB,\n        loader_chaining_a_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu,\n        \"Launch B, then A\",\n        LoaderChainingASubmenuLaunchBThenA,\n        loader_chaining_a_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu,\n        \"Trigger error: silent\",\n        LoaderChainingASubmenuLaunchNonexistentSilent,\n        loader_chaining_a_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu,\n        \"Trigger error: GUI\",\n        LoaderChainingASubmenuLaunchNonexistentGui,\n        loader_chaining_a_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu,\n        \"Error, then launch A\",\n        LoaderChainingASubmenuLaunchNonexistentGuiThenA,\n        loader_chaining_a_submenu_callback,\n        app);\n\n    view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu));\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, loader_chaining_a_nav_callback);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n    view_dispatcher_switch_to_view(app->view_dispatcher, 0);\n\n    return app;\n}\n\nvoid loader_chaining_a_free(LoaderChainingA* app) {\n    furi_record_close(RECORD_DIALOGS);\n    furi_record_close(RECORD_LOADER);\n    furi_record_close(RECORD_GUI);\n    view_dispatcher_remove_view(app->view_dispatcher, 0);\n    submenu_free(app->submenu);\n    view_dispatcher_free(app->view_dispatcher);\n    free(app);\n}\n\nint32_t chaining_test_app_a(const char* arg) {\n    LoaderChainingA* app = loader_chaining_a_alloc();\n\n    if(arg) {\n        if(strlen(arg)) {\n            DialogMessage* message = dialog_message_alloc();\n            FuriString* text;\n\n            dialog_message_set_header(message, \"Hi, I am A\", 64, 0, AlignCenter, AlignTop);\n            text = furi_string_alloc_printf(\"Me from the past says:\\n%s\", arg);\n            dialog_message_set_buttons(message, NULL, \"ok!\", NULL);\n\n            dialog_message_set_text(\n                message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter);\n            dialog_message_show(app->dialogs, message);\n            dialog_message_free(message);\n            furi_string_free(text);\n        }\n    }\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    loader_chaining_a_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/loader_chaining_b/application.fam",
    "content": "App(\n    appid=\"loader_chaining_b\",\n    name=\"Loader Chaining Test: App B\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"chaining_test_app_b\",\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/loader_chaining_b/loader_chaining_b.c",
    "content": "#include <furi.h>\n#include <dialogs/dialogs.h>\n#include <loader/loader.h>\n\nint32_t chaining_test_app_b(const char* arg) {\n    if(!arg) return 0;\n\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n\n    DialogMessage* message = dialog_message_alloc();\n    dialog_message_set_header(message, \"Hi, I am B\", 64, 0, AlignCenter, AlignTop);\n    FuriString* text = furi_string_alloc_printf(\"And A told me:\\n%s\", arg);\n    dialog_message_set_text(message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter);\n    dialog_message_set_buttons(message, \"Just quit\", NULL, \"Launch A\");\n    DialogMessageButton result = dialog_message_show(dialogs, message);\n    dialog_message_free(message);\n    furi_string_free(text);\n\n    if(result == DialogMessageButtonRight)\n        loader_enqueue_launch(\n            loader, \"/ext/apps/Debug/loader_chaining_a.fap\", NULL, LoaderDeferredLaunchFlagGui);\n\n    furi_record_close(RECORD_LOADER);\n    furi_record_close(RECORD_DIALOGS);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/locale_test/application.fam",
    "content": "App(\n    appid=\"locale_test\",\n    name=\"Locale Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"locale_test_app\",\n    requires=[\"gui\", \"locale\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/locale_test/locale_test.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <gui/elements.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/dialog_ex.h>\n#include <locale/locale.h>\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    View* view;\n} LocaleTestApp;\n\nstatic void locale_test_view_draw_callback(Canvas* canvas, void* _model) {\n    UNUSED(_model);\n\n    // Prepare canvas\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n\n    FuriString* tmp_string = furi_string_alloc();\n\n    float temp = 25.3f;\n    LocaleMeasurementUnits units = locale_get_measurement_unit();\n    if(units == LocaleMeasurementUnitsMetric) {\n        furi_string_printf(tmp_string, \"Temp: %5.1fC\", (double)temp);\n    } else {\n        temp = locale_celsius_to_fahrenheit(temp);\n        furi_string_printf(tmp_string, \"Temp: %5.1fF\", (double)temp);\n    }\n    canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string));\n\n    DateTime datetime;\n    furi_hal_rtc_get_datetime(&datetime);\n\n    locale_format_time(tmp_string, &datetime, locale_get_time_format(), false);\n    canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string));\n\n    locale_format_date(tmp_string, &datetime, locale_get_date_format(), \"/\");\n    canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string));\n\n    furi_string_free(tmp_string);\n}\n\nstatic bool locale_test_view_input_callback(InputEvent* event, void* context) {\n    UNUSED(event);\n    UNUSED(context);\n    return false;\n}\n\nstatic uint32_t locale_test_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic LocaleTestApp* locale_test_alloc(void) {\n    LocaleTestApp* app = malloc(sizeof(LocaleTestApp));\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->view = view_alloc();\n    view_set_draw_callback(app->view, locale_test_view_draw_callback);\n    view_set_input_callback(app->view, locale_test_view_input_callback);\n\n    view_set_previous_callback(app->view, locale_test_exit);\n    view_dispatcher_add_view(app->view_dispatcher, 0, app->view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, 0);\n\n    return app;\n}\n\nstatic void locale_test_free(LocaleTestApp* app) {\n    furi_assert(app);\n\n    // Free views\n    view_dispatcher_remove_view(app->view_dispatcher, 0);\n\n    view_free(app->view);\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Close gui record\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    // Free rest\n    free(app);\n}\n\nint32_t locale_test_app(void* p) {\n    UNUSED(p);\n    LocaleTestApp* app = locale_test_alloc();\n    view_dispatcher_run(app->view_dispatcher);\n    locale_test_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/application.fam",
    "content": "App(\n    appid=\"rpc_debug\",\n    name=\"RPC Debug\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"rpc_debug_app\",\n    requires=[\"gui\", \"rpc_start\", \"notification\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/rpc_debug_app.c",
    "content": "#include \"rpc_debug_app.h\"\n#include <core/log.h>\n\n#include <string.h>\n\nstatic bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    RpcDebugApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool rpc_debug_app_back_event_callback(void* context) {\n    furi_assert(context);\n    RpcDebugApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void rpc_debug_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    RpcDebugApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nstatic void\n    rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) {\n    if(data == NULL || data_size == 0) {\n        strlcpy(buf, \"<Data empty>\", buf_size);\n        return;\n    }\n\n    const size_t byte_width = 3;\n    const size_t line_width = 7;\n\n    data_size = MIN(data_size, buf_size / (byte_width + 1));\n\n    for(size_t i = 0; i < data_size; ++i) {\n        char* p = buf + (i * byte_width);\n        char sep = !((i + 1) % line_width) ? '\\n' : ' ';\n        snprintf(p, byte_width + 1, \"%02X%c\", data[i], sep);\n    }\n\n    buf[buf_size - 1] = '\\0';\n}\n\nstatic void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    RpcDebugApp* app = context;\n    furi_assert(app->rpc);\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        scene_manager_stop(app->scene_manager);\n        view_dispatcher_stop(app->view_dispatcher);\n        rpc_system_app_set_callback(app->rpc, NULL, NULL);\n        app->rpc = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        scene_manager_stop(app->scene_manager);\n        view_dispatcher_stop(app->view_dispatcher);\n        rpc_system_app_confirm(app->rpc, true);\n    } else if(event->type == RpcAppEventTypeDataExchange) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes);\n\n        rpc_debug_app_format_hex(\n            event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE);\n\n        view_dispatcher_send_custom_event(\n            app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);\n    } else {\n        rpc_system_app_confirm(app->rpc, false);\n    }\n}\n\nstatic bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) {\n    bool ret = false;\n    if(args && strlen(args)) {\n        uint32_t rpc = 0;\n        if(sscanf(args, \"RPC %lX\", &rpc) == 1) {\n            app->rpc = (RpcAppSystem*)rpc;\n            rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app);\n            rpc_system_app_send_started(app->rpc);\n            ret = true;\n        }\n    }\n    return ret;\n}\n\nstatic RpcDebugApp* rpc_debug_app_alloc(void) {\n    RpcDebugApp* app = malloc(sizeof(RpcDebugApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n    app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app);\n    app->view_dispatcher = view_dispatcher_alloc();\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, rpc_debug_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, rpc_debug_app_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, rpc_debug_app_tick_event_callback, 100);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->widget = widget_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget));\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu));\n    app->text_box = text_box_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box));\n    app->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input));\n    app->byte_input = byte_input_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input));\n\n    return app;\n}\n\nstatic void rpc_debug_app_free(RpcDebugApp* app) {\n    view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput);\n    view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput);\n    view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox);\n    view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu);\n    view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget);\n\n    free(app->byte_input);\n    free(app->text_input);\n    free(app->text_box);\n    free(app->submenu);\n    free(app->widget);\n\n    free(app->scene_manager);\n    free(app->view_dispatcher);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    app->notifications = NULL;\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    if(app->rpc) {\n        rpc_system_app_set_callback(app->rpc, NULL, NULL);\n        rpc_system_app_send_exited(app->rpc);\n        app->rpc = NULL;\n    }\n\n    free(app);\n}\n\nint32_t rpc_debug_app(void* args) {\n    RpcDebugApp* app = rpc_debug_app_alloc();\n\n    if(rpc_debug_app_rpc_init_rpc(app, args)) {\n        notification_message(app->notifications, &sequence_display_backlight_on);\n        scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart);\n    } else {\n        scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy);\n    }\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    rpc_debug_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/rpc_debug_app.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n\n#include <gui/modules/widget.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/text_box.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/byte_input.h>\n\n#include <rpc/rpc_app.h>\n#include <notification/notification_messages.h>\n\n#include \"scenes/rpc_debug_app_scene.h\"\n\n#define DATA_STORE_SIZE 64U\n#define TEXT_STORE_SIZE 64U\n\ntypedef struct {\n    Gui* gui;\n    RpcAppSystem* rpc;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n    NotificationApp* notifications;\n\n    Widget* widget;\n    Submenu* submenu;\n    TextBox* text_box;\n    TextInput* text_input;\n    ByteInput* byte_input;\n\n    char text_store[TEXT_STORE_SIZE];\n    uint8_t data_store[DATA_STORE_SIZE];\n} RpcDebugApp;\n\ntypedef enum {\n    RpcDebugAppViewWidget,\n    RpcDebugAppViewSubmenu,\n    RpcDebugAppViewTextBox,\n    RpcDebugAppViewTextInput,\n    RpcDebugAppViewByteInput,\n} RpcDebugAppView;\n\ntypedef enum {\n    // Reserve first 100 events for button types and indexes, starting from 0\n    RpcDebugAppCustomEventInputErrorCode = 100,\n    RpcDebugAppCustomEventInputErrorText,\n    RpcDebugAppCustomEventInputDataExchange,\n    RpcDebugAppCustomEventRpcDataExchange,\n} RpcDebugAppCustomEvent;\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c",
    "content": "#include \"rpc_debug_app_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const rpc_debug_app_on_enter_handlers[])(void*) = {\n#include \"rpc_debug_app_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"rpc_debug_app_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const rpc_debug_app_on_exit_handlers[])(void* context) = {\n#include \"rpc_debug_app_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers rpc_debug_app_scene_handlers = {\n    .on_enter_handlers = rpc_debug_app_on_enter_handlers,\n    .on_event_handlers = rpc_debug_app_on_event_handlers,\n    .on_exit_handlers = rpc_debug_app_on_exit_handlers,\n    .scene_num = RpcDebugAppSceneNum,\n};\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id,\ntypedef enum {\n#include \"rpc_debug_app_scene_config.h\"\n    RpcDebugAppSceneNum,\n} RpcDebugAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers rpc_debug_app_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"rpc_debug_app_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"rpc_debug_app_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"rpc_debug_app_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h",
    "content": "ADD_SCENE(rpc_debug_app, start, Start)\nADD_SCENE(rpc_debug_app, start_dummy, StartDummy)\nADD_SCENE(rpc_debug_app, test_app_error, TestAppError)\nADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange)\nADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode)\nADD_SCENE(rpc_debug_app, input_error_text, InputErrorText)\nADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange)\nADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange)\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c",
    "content": "#include \"../rpc_debug_app.h\"\n\nstatic void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(\n        app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange);\n}\n\nvoid rpc_debug_app_scene_input_data_exchange_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    byte_input_set_header_text(app->byte_input, \"Enter data to exchange\");\n    byte_input_set_result_callback(\n        app->byte_input,\n        rpc_debug_app_scene_input_data_exchange_result_callback,\n        NULL,\n        app,\n        app->data_store,\n        DATA_STORE_SIZE);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput);\n}\n\nbool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == RpcDebugAppCustomEventInputDataExchange) {\n            rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE);\n            scene_manager_previous_scene(app->scene_manager);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_input_data_exchange_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    UNUSED(app);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c",
    "content": "#include \"../rpc_debug_app.h\"\n\n#include <lib/toolbox/strint.h>\n\nstatic bool rpc_debug_app_scene_input_error_code_validator_callback(\n    const char* text,\n    FuriString* error,\n    void* context) {\n    UNUSED(context);\n\n    for(; *text; ++text) {\n        const char c = *text;\n        if(c < '0' || c > '9') {\n            furi_string_printf(error, \"%s\", \"Please enter\\na number!\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic void rpc_debug_app_scene_input_error_code_result_callback(void* context) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode);\n}\n\nvoid rpc_debug_app_scene_input_error_code_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    strlcpy(app->text_store, \"666\", TEXT_STORE_SIZE);\n    text_input_set_header_text(app->text_input, \"Enter error code\");\n    text_input_set_validator(\n        app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL);\n    text_input_set_result_callback(\n        app->text_input,\n        rpc_debug_app_scene_input_error_code_result_callback,\n        app,\n        app->text_store,\n        TEXT_STORE_SIZE,\n        true);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);\n}\n\nbool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == RpcDebugAppCustomEventInputErrorCode) {\n            uint32_t error_code;\n            if(strint_to_uint32(app->text_store, NULL, &error_code, 10) == StrintParseNoError) {\n                rpc_system_app_set_error_code(app->rpc, error_code);\n            }\n            scene_manager_previous_scene(app->scene_manager);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_input_error_code_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    text_input_reset(app->text_input);\n    text_input_set_validator(app->text_input, NULL, NULL);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c",
    "content": "#include \"../rpc_debug_app.h\"\n\nstatic void rpc_debug_app_scene_input_error_text_result_callback(void* context) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText);\n}\n\nvoid rpc_debug_app_scene_input_error_text_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    strlcpy(app->text_store, \"I'm a scary error message!\", TEXT_STORE_SIZE);\n    text_input_set_header_text(app->text_input, \"Enter error text\");\n    text_input_set_result_callback(\n        app->text_input,\n        rpc_debug_app_scene_input_error_text_result_callback,\n        app,\n        app->text_store,\n        TEXT_STORE_SIZE,\n        true);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);\n}\n\nbool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == RpcDebugAppCustomEventInputErrorText) {\n            rpc_system_app_set_error_text(app->rpc, app->text_store);\n            scene_manager_previous_scene(app->scene_manager);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_input_error_text_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    text_input_reset(app->text_input);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c",
    "content": "#include \"../rpc_debug_app.h\"\n\nvoid rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    strlcpy(app->text_store, \"Received data will appear here...\", TEXT_STORE_SIZE);\n\n    text_box_set_text(app->text_box, app->text_store);\n    text_box_set_font(app->text_box, TextBoxFontHex);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);\n}\n\nbool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == RpcDebugAppCustomEventRpcDataExchange) {\n            rpc_system_app_confirm(app->rpc, true);\n            notification_message(app->notifications, &sequence_blink_cyan_100);\n            notification_message(app->notifications, &sequence_display_backlight_on);\n            text_box_set_text(app->text_box, app->text_store);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    text_box_reset(app->text_box);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c",
    "content": "#include \"../rpc_debug_app.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexTestAppError,\n    SubmenuIndexTestDataExchange,\n};\n\nstatic void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid rpc_debug_app_scene_start_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Test App Error\",\n        SubmenuIndexTestAppError,\n        rpc_debug_app_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Test Data Exchange\",\n        SubmenuIndexTestDataExchange,\n        rpc_debug_app_scene_start_submenu_callback,\n        app);\n\n    submenu_set_selected_item(submenu, SubmenuIndexTestAppError);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);\n}\n\nbool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        if(submenu_index == SubmenuIndexTestAppError) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexTestDataExchange) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_start_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c",
    "content": "#include \"../rpc_debug_app.h\"\n\nvoid rpc_debug_app_scene_start_dummy_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    widget_add_text_box_element(\n        app->widget,\n        0,\n        0,\n        128,\n        64,\n        AlignCenter,\n        AlignCenter,\n        \"This application\\nis meant to be run\\nin \\e#RPC\\e# mode.\",\n        false);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget);\n}\n\nbool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    UNUSED(app);\n    UNUSED(event);\n\n    bool consumed = false;\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_start_dummy_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c",
    "content": "#include \"../rpc_debug_app.h\"\n\ntypedef enum {\n    SubmenuIndexSetErrorCode,\n    SubmenuIndexSetErrorText,\n} SubmenuIndex;\n\nstatic void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid rpc_debug_app_scene_test_app_error_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Set Error Code\",\n        SubmenuIndexSetErrorCode,\n        rpc_debug_app_scene_test_app_error_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Set Error Text\",\n        SubmenuIndexSetErrorText,\n        rpc_debug_app_scene_test_app_error_submenu_callback,\n        app);\n\n    submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);\n}\n\nbool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        if(submenu_index == SubmenuIndexSetErrorCode) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexSetErrorText) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_test_app_error_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c",
    "content": "#include \"../rpc_debug_app.h\"\n\ntypedef enum {\n    SubmenuIndexSendData,\n    SubmenuIndexReceiveData,\n} SubmenuIndex;\n\nstatic void\n    rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) {\n    RpcDebugApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid rpc_debug_app_scene_test_data_exchange_on_enter(void* context) {\n    RpcDebugApp* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Send Data\",\n        SubmenuIndexSendData,\n        rpc_debug_app_scene_test_data_exchange_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Receive Data\",\n        SubmenuIndexReceiveData,\n        rpc_debug_app_scene_test_data_exchange_submenu_callback,\n        app);\n\n    submenu_set_selected_item(submenu, SubmenuIndexSendData);\n    view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);\n}\n\nbool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) {\n    RpcDebugApp* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        if(submenu_index == SubmenuIndexSendData) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexReceiveData) {\n            scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid rpc_debug_app_scene_test_data_exchange_on_exit(void* context) {\n    RpcDebugApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/debug/speaker_debug/application.fam",
    "content": "App(\n    appid=\"speaker_debug\",\n    name=\"Speaker Debug\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"speaker_debug_app\",\n    requires=[\"gui\", \"notification\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n    fap_libs=[\"music_worker\"],\n)\n"
  },
  {
    "path": "applications/debug/speaker_debug/speaker_debug.c",
    "content": "#include <furi.h>\n#include <notification/notification.h>\n#include <music_worker/music_worker.h>\n#include <toolbox/args.h>\n#include <toolbox/pipe.h>\n#include <toolbox/cli/cli_registry.h>\n#include <cli/cli_main_commands.h>\n\n#define TAG \"SpeakerDebug\"\n\n#define CLI_COMMAND \"speaker_debug\"\n\ntypedef enum {\n    SpeakerDebugAppMessageTypeStop,\n} SpeakerDebugAppMessageType;\n\ntypedef struct {\n    SpeakerDebugAppMessageType type;\n} SpeakerDebugAppMessage;\n\ntypedef struct {\n    MusicWorker* music_worker;\n    FuriMessageQueue* message_queue;\n    CliRegistry* cli_registry;\n} SpeakerDebugApp;\n\nstatic SpeakerDebugApp* speaker_app_alloc(void) {\n    SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));\n    app->music_worker = music_worker_alloc();\n    app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));\n    app->cli_registry = furi_record_open(RECORD_CLI);\n    return app;\n}\n\nstatic void speaker_app_free(SpeakerDebugApp* app) {\n    music_worker_free(app->music_worker);\n    furi_message_queue_free(app->message_queue);\n    furi_record_close(RECORD_CLI);\n    free(app);\n}\n\nstatic void speaker_app_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n\n    SpeakerDebugApp* app = (SpeakerDebugApp*)context;\n    SpeakerDebugAppMessage message;\n    FuriString* cmd = furi_string_alloc();\n\n    if(!args_read_string_and_trim(args, cmd)) {\n        furi_string_free(cmd);\n        printf(\"Usage:\\r\\n\");\n        printf(\"\\t\" CLI_COMMAND \" stop\\r\\n\");\n        return;\n    }\n\n    if(furi_string_cmp(cmd, \"stop\") == 0) {\n        message.type = SpeakerDebugAppMessageTypeStop;\n        FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100);\n        if(status != FuriStatusOk) {\n            printf(\"Failed to send message\\r\\n\");\n        } else {\n            printf(\"Stopping\\r\\n\");\n        }\n    } else {\n        printf(\"Usage:\\r\\n\");\n        printf(\"\\t\" CLI_COMMAND \" stop\\r\\n\");\n    }\n\n    furi_string_free(cmd);\n}\n\nstatic bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) {\n    if(music_worker_is_playing(app->music_worker)) {\n        music_worker_stop(app->music_worker);\n    }\n\n    if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) {\n        FURI_LOG_E(TAG, \"Failed to load RTTTL\");\n        return false;\n    }\n\n    music_worker_set_volume(app->music_worker, 1.0f);\n    music_worker_start(app->music_worker);\n\n    return true;\n}\n\nstatic void speaker_app_music_stop(SpeakerDebugApp* app) {\n    if(music_worker_is_playing(app->music_worker)) {\n        music_worker_stop(app->music_worker);\n    }\n}\n\nstatic void speaker_app_run(SpeakerDebugApp* app, const char* arg) {\n    if(!arg || !speaker_app_music_play(app, arg)) {\n        FURI_LOG_E(TAG, \"Provided RTTTL is invalid\");\n        return;\n    }\n\n    cli_registry_add_command(\n        app->cli_registry, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);\n\n    SpeakerDebugAppMessage message;\n    FuriStatus status;\n    while(true) {\n        status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever);\n\n        if(status == FuriStatusOk) {\n            if(message.type == SpeakerDebugAppMessageTypeStop) {\n                speaker_app_music_stop(app);\n                break;\n            }\n        }\n    }\n\n    cli_registry_delete_command(app->cli_registry, CLI_COMMAND);\n}\n\nint32_t speaker_debug_app(void* arg) {\n    SpeakerDebugApp* app = speaker_app_alloc();\n    speaker_app_run(app, arg);\n    speaker_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/application.fam",
    "content": "App(\n    appid=\"subghz_test\",\n    name=\"Sub-Ghz test\",\n    apptype=FlipperAppType.DEBUG,\n    targets=[\"f7\"],\n    entry_point=\"subghz_test_app\",\n    requires=[\"gui\"],\n    stack_size=4 * 1024,\n    fap_icon=\"subghz_test_10px.png\",\n    fap_category=\"Debug\",\n    fap_icon_assets=\"images\",\n    fap_version=\"0.1\",\n)\n"
  },
  {
    "path": "applications/debug/subghz_test/helpers/subghz_test_event.h",
    "content": "#pragma once\n\ntypedef enum {\n    //SubGhzTestCustomEvent\n    SubGhzTestCustomEventStartId = 100,\n    SubGhzTestCustomEventSceneShowOnlyRX,\n} SubGhzTestCustomEvent;\n"
  },
  {
    "path": "applications/debug/subghz_test/helpers/subghz_test_frequency.c",
    "content": "#include \"subghz_test_frequency.h\"\n\nconst uint32_t subghz_frequencies_testing[] = {\n    /* 300 - 348 */\n    300000000,\n    304500000,\n    310000000,\n    312025000,\n    313250000,\n    313625000,\n    315000000,\n    315225000,\n    321950000,\n    348000000,\n    /* 387 - 464 */\n    387000000,\n    433075000, /* LPD433 first */\n    433825000,\n    433920000, /* LPD433 mid */\n    434420000,\n    434775000, /* LPD433 last channels */\n    438900000,\n    464000000,\n    /* 779 - 928 */\n    779000000,\n    868150000,\n    868350000,\n    868550000,\n    915000000,\n    925000000,\n    926500000,\n    927950000,\n    928000000,\n};\n\nconst uint32_t subghz_frequencies_count_testing =\n    sizeof(subghz_frequencies_testing) / sizeof(uint32_t);\nconst uint32_t subghz_frequencies_433_92_testing = 13;\n"
  },
  {
    "path": "applications/debug/subghz_test/helpers/subghz_test_frequency.h",
    "content": "#pragma once\n#include \"../subghz_test_app_i.h\"\n\nextern const uint32_t subghz_frequencies_testing[];\nextern const uint32_t subghz_frequencies_count_testing;\nextern const uint32_t subghz_frequencies_433_92_testing;\n"
  },
  {
    "path": "applications/debug/subghz_test/helpers/subghz_test_types.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#define SUBGHZ_TEST_VERSION_APP \"0.1\"\n#define SUBGHZ_TEST_DEVELOPED   \"SkorP\"\n#define SUBGHZ_TEST_GITHUB      \"https://github.com/flipperdevices/flipperzero-firmware\"\n\ntypedef enum {\n    SubGhzTestViewVariableItemList,\n    SubGhzTestViewSubmenu,\n    SubGhzTestViewStatic,\n    SubGhzTestViewCarrier,\n    SubGhzTestViewPacket,\n    SubGhzTestViewWidget,\n    SubGhzTestViewPopup,\n} SubGhzTestView;\n"
  },
  {
    "path": "applications/debug/subghz_test/protocol/math.c",
    "content": "#include \"math.h\"\n\nuint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {\n    uint64_t reverse_key = 0;\n    for(uint8_t i = 0; i < bit_count; i++) {\n        reverse_key = reverse_key << 1 | bit_read(key, i);\n    }\n    return reverse_key;\n}\n\nuint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {\n    uint8_t parity = 0;\n    for(uint8_t i = 0; i < bit_count; i++) {\n        parity += bit_read(key, i);\n    }\n    return parity & 0x01;\n}\n\nuint8_t subghz_protocol_blocks_crc4(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init << 4; // LSBs are unused\n    uint8_t poly = polynomial << 4;\n    uint8_t bit;\n\n    while(size--) {\n        remainder ^= *message++;\n        for(bit = 0; bit < 8; bit++) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ poly;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder >> 4 & 0x0f; // discard the LSBs\n}\n\nuint8_t subghz_protocol_blocks_crc7(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init << 1; // LSB is unused\n    uint8_t poly = polynomial << 1;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ poly;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder >> 1 & 0x7f; // discard the LSB\n}\n\nuint8_t subghz_protocol_blocks_crc8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ polynomial;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint8_t subghz_protocol_blocks_crc8le(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8);\n    polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8);\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 1) {\n                remainder = (remainder >> 1) ^ polynomial;\n            } else {\n                remainder = (remainder >> 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint16_t subghz_protocol_blocks_crc16lsb(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init) {\n    uint16_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 1) {\n                remainder = (remainder >> 1) ^ polynomial;\n            } else {\n                remainder = (remainder >> 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint16_t subghz_protocol_blocks_crc16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init) {\n    uint16_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte] << 8;\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x8000) {\n                remainder = (remainder << 1) ^ polynomial;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint8_t subghz_protocol_blocks_lfsr_digest8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key) {\n    uint8_t sum = 0;\n    for(size_t byte = 0; byte < size; ++byte) {\n        uint8_t data = message[byte];\n        for(int i = 7; i >= 0; --i) {\n            // XOR key into sum if data bit is set\n            if((data >> i) & 1) sum ^= key;\n\n            // roll the key right (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped LSB as MSB)\n            if(key & 1)\n                key = (key >> 1) ^ gen;\n            else\n                key = (key >> 1);\n        }\n    }\n    return sum;\n}\n\nuint8_t subghz_protocol_blocks_lfsr_digest8_reflect(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key) {\n    uint8_t sum = 0;\n    // Process message from last byte to first byte (reflected)\n    for(int byte = size - 1; byte >= 0; --byte) {\n        uint8_t data = message[byte];\n        // Process individual bits of each byte (reflected)\n        for(uint8_t i = 0; i < 8; ++i) {\n            // XOR key into sum if data bit is set\n            if((data >> i) & 1) {\n                sum ^= key;\n            }\n\n            // roll the key left (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped lsb as MSB)\n            if(key & 0x80)\n                key = (key << 1) ^ gen;\n            else\n                key = (key << 1);\n        }\n    }\n    return sum;\n}\n\nuint16_t subghz_protocol_blocks_lfsr_digest16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t gen,\n    uint16_t key) {\n    uint16_t sum = 0;\n    for(size_t byte = 0; byte < size; ++byte) {\n        uint8_t data = message[byte];\n        for(int8_t i = 7; i >= 0; --i) {\n            // if data bit is set then xor with key\n            if((data >> i) & 1) sum ^= key;\n\n            // roll the key right (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped LSB as MSB)\n            if(key & 1)\n                key = (key >> 1) ^ gen;\n            else\n                key = (key >> 1);\n        }\n    }\n    return sum;\n}\n\nuint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {\n    uint32_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result += message[i];\n    }\n    return (uint8_t)result;\n}\n\nuint8_t subghz_protocol_blocks_parity8(uint8_t byte) {\n    byte ^= byte >> 4;\n    byte &= 0xf;\n    return (0x6996 >> byte) & 1;\n}\n\nuint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {\n    uint8_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result ^= subghz_protocol_blocks_parity8(message[i]);\n    }\n    return result;\n}\n\nuint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) {\n    uint8_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result ^= message[i];\n    }\n    return result;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/protocol/math.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#define bit_read(value, bit) (((value) >> (bit)) & 0x01)\n#define bit_set(value, bit)           \\\n    ({                                \\\n        __typeof__(value) _one = (1); \\\n        (value) |= (_one << (bit));   \\\n    })\n#define bit_clear(value, bit)         \\\n    ({                                \\\n        __typeof__(value) _one = (1); \\\n        (value) &= ~(_one << (bit));  \\\n    })\n#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))\n#define DURATION_DIFF(x, y)             (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Flip the data bitwise\n *\n * @param      key        In data\n * @param      bit_count  number of data bits\n *\n * @return     Reverse data\n */\nuint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count);\n\n/** Get parity the data bitwise\n *\n * @param      key        In data\n * @param      bit_count  number of data bits\n *\n * @return     parity\n */\nuint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count);\n\n/** CRC-4\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc4(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** CRC-7\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc7(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 +\n * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal\n * bit-by-bit parity XOR)\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  byte is from x^7 to x^0 (x^8 is implicitly one)\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** \"Little-endian\" Cyclic Redundancy Check CRC-8 LE Input and output are\n * reflected, i.e. least significant bit is shifted in first\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc8le(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is\n * shifted in first. Note that poly and init already need to be reflected\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint16_t subghz_protocol_blocks_crc16lsb(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init);\n\n/** CRC-16\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint16_t subghz_protocol_blocks_crc16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init);\n\n/** Digest-8 by \"LFSR-based Toeplitz hash\"\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint8_t subghz_protocol_blocks_lfsr_digest8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key);\n\n/** Digest-8 by \"LFSR-based Toeplitz hash\", byte reflect, bit reflect\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint8_t subghz_protocol_blocks_lfsr_digest8_reflect(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key);\n\n/** Digest-16 by \"LFSR-based Toeplitz hash\"\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint16_t subghz_protocol_blocks_lfsr_digest16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t gen,\n    uint16_t key);\n\n/** Compute Addition of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     summation value\n */\nuint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size);\n\n/** Compute bit parity of a single byte (8 bits)\n *\n * @param      byte  single byte to check\n *\n * @return     1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_parity8(uint8_t byte);\n\n/** Compute bit parity of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size);\n\n/** Compute XOR (byte-wide parity) of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     summation value, per bit-position 1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/debug/subghz_test/protocol/princeton_for_testing.c",
    "content": "#include \"princeton_for_testing.h\"\n\n#include <furi_hal.h>\n#include \"math.h\"\n\n/*\n * Help\n * https://phreakerclub.com/447\n *\n */\n\n#define SUBGHZ_PT_SHORT         300\n#define SUBGHZ_PT_LONG          (SUBGHZ_PT_SHORT * 3)\n#define SUBGHZ_PT_GUARD         (SUBGHZ_PT_SHORT * 30)\n#define SUBGHZ_PT_COUNT_KEY_433 9\n#define SUBGHZ_PT_TIMEOUT_433   900\n#define SUBGHZ_PT_COUNT_KEY_868 9\n#define SUBGHZ_PT_TIMEOUT_868   14000\n\n#define TAG \"SubGhzProtocolPrinceton\"\n\nstruct SubGhzEncoderPrinceton {\n    uint32_t key;\n    uint16_t te;\n    size_t repeat;\n    size_t front;\n    size_t count_key;\n    size_t count_key_package;\n    uint32_t time_high;\n    uint32_t time_low;\n    uint32_t timeout;\n    uint32_t time_stop;\n};\n\ntypedef enum {\n    PrincetonDecoderStepReset = 0,\n    PrincetonDecoderStepSaveDuration,\n    PrincetonDecoderStepCheckDuration,\n} PrincetonDecoderStep;\n\nSubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc(void) {\n    SubGhzEncoderPrinceton* instance = malloc(sizeof(SubGhzEncoderPrinceton));\n    return instance;\n}\n\nvoid subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nvoid subghz_encoder_princeton_for_testing_stop(\n    SubGhzEncoderPrinceton* instance,\n    uint32_t time_stop) {\n    instance->time_stop = time_stop;\n}\n\nvoid subghz_encoder_princeton_for_testing_set(\n    SubGhzEncoderPrinceton* instance,\n    uint32_t key,\n    size_t repeat,\n    uint32_t frequency) {\n    furi_assert(instance);\n    instance->te = SUBGHZ_PT_SHORT;\n    instance->key = key;\n    instance->repeat = repeat + 1;\n    instance->front = 48;\n    instance->time_high = 0;\n    instance->time_low = 0;\n    if(frequency < 700000000) {\n        instance->count_key_package = SUBGHZ_PT_COUNT_KEY_433;\n        instance->timeout = SUBGHZ_PT_TIMEOUT_433;\n    } else {\n        instance->count_key_package = SUBGHZ_PT_COUNT_KEY_868;\n        instance->timeout = SUBGHZ_PT_TIMEOUT_868;\n    }\n\n    instance->count_key = instance->count_key_package + 3;\n\n    if((furi_get_tick() - instance->time_stop) < instance->timeout) {\n        instance->time_stop = (instance->timeout - (furi_get_tick() - instance->time_stop)) * 1000;\n    } else {\n        instance->time_stop = 0;\n    }\n}\n\nsize_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance) {\n    furi_assert(instance);\n    return instance->repeat;\n}\n\nvoid subghz_encoder_princeton_for_testing_print_log(void* context) {\n    SubGhzEncoderPrinceton* instance = context;\n    float duty_cycle =\n        ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100;\n    FURI_LOG_I(\n        TAG \"Encoder\",\n        \"Radio tx_time=%luus  ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%\",\n        instance->time_high + instance->time_low,\n        instance->time_high,\n        instance->time_low,\n        (uint32_t)duty_cycle,\n        (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL));\n}\n\nLevelDuration subghz_encoder_princeton_for_testing_yield(void* context) {\n    SubGhzEncoderPrinceton* instance = context;\n    if(instance->repeat == 0) {\n        subghz_encoder_princeton_for_testing_print_log(instance);\n        return level_duration_reset();\n    }\n\n    size_t bit = instance->front / 2;\n    bool level = !(instance->front % 2);\n\n    LevelDuration ret;\n    if(bit < 24) {\n        uint8_t byte = bit / 8;\n        uint8_t bit_in_byte = bit % 8;\n        bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1;\n        if(value) {\n            ret = level_duration_make(level, level ? instance->te * 3 : instance->te);\n            if(level)\n                instance->time_high += instance->te * 3;\n            else\n                instance->time_low += instance->te;\n        } else {\n            ret = level_duration_make(level, level ? instance->te : instance->te * 3);\n            if(level)\n                instance->time_high += instance->te;\n            else\n                instance->time_low += instance->te * 3;\n        }\n    } else {\n        if(instance->time_stop) {\n            ret = level_duration_make(level, level ? instance->te : instance->time_stop);\n            if(level)\n                instance->time_high += instance->te;\n            else {\n                instance->time_low += instance->time_stop;\n                instance->time_stop = 0;\n                instance->front = 47;\n            }\n        } else {\n            if(--instance->count_key != 0) {\n                ret = level_duration_make(level, level ? instance->te : instance->te * 30);\n                if(level)\n                    instance->time_high += instance->te;\n                else\n                    instance->time_low += instance->te * 30;\n            } else {\n                instance->count_key = instance->count_key_package + 2;\n                instance->front = 48;\n                ret = level_duration_make(level, level ? instance->te : instance->timeout * 1000);\n                if(level)\n                    instance->time_high += instance->te;\n                else\n                    instance->time_low += instance->timeout * 1000;\n            }\n        }\n    }\n\n    instance->front++;\n    if(instance->front == 50) {\n        instance->repeat--;\n        instance->front = 0;\n    }\n    return ret;\n}\n\nstruct SubGhzDecoderPrinceton {\n    const char* name;\n    uint16_t te_long;\n    uint16_t te_short;\n    uint16_t te_delta;\n    uint8_t code_count_bit;\n    uint8_t code_last_count_bit;\n    uint64_t code_found;\n    uint64_t code_last_found;\n    uint8_t code_min_count_bit_for_found;\n    uint8_t btn;\n    uint32_t te_last;\n    uint32_t serial;\n    uint32_t parser_step;\n    uint16_t cnt;\n    uint32_t te;\n\n    SubGhzDecoderPrincetonCallback callback;\n    void* context;\n};\n\nSubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void) {\n    SubGhzDecoderPrinceton* instance = malloc(sizeof(SubGhzDecoderPrinceton));\n\n    instance->te = SUBGHZ_PT_SHORT;\n    instance->name = \"Princeton\";\n    instance->code_min_count_bit_for_found = 24;\n    instance->te_short = 400;\n    instance->te_long = 1200;\n    instance->te_delta = 250;\n    return instance;\n}\n\nvoid subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nvoid subghz_decoder_princeton_for_testing_set_callback(\n    SubGhzDecoderPrinceton* instance,\n    SubGhzDecoderPrincetonCallback callback,\n    void* context) {\n    instance->callback = callback;\n    instance->context = context;\n}\n\nvoid subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance) {\n    instance->parser_step = PrincetonDecoderStepReset;\n}\n\nstatic void\n    subghz_decoder_princeton_for_testing_add_bit(SubGhzDecoderPrinceton* instance, uint8_t bit) {\n    instance->code_found = instance->code_found << 1 | bit;\n    instance->code_count_bit++;\n}\n\nvoid subghz_decoder_princeton_for_testing_parse(\n    SubGhzDecoderPrinceton* instance,\n    bool level,\n    uint32_t duration) {\n    switch(instance->parser_step) {\n    case PrincetonDecoderStepReset:\n        if((!level) &&\n           (DURATION_DIFF(duration, instance->te_short * 36) < instance->te_delta * 36)) {\n            //Found Preambula\n            instance->parser_step = PrincetonDecoderStepSaveDuration;\n            instance->code_found = 0;\n            instance->code_count_bit = 0;\n            instance->te = 0;\n        }\n        break;\n    case PrincetonDecoderStepSaveDuration:\n        //save duration\n        if(level) {\n            instance->te_last = duration;\n            instance->te += duration;\n            instance->parser_step = PrincetonDecoderStepCheckDuration;\n        }\n        break;\n    case PrincetonDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)instance->te_short * 10 + instance->te_delta)) {\n                instance->parser_step = PrincetonDecoderStepSaveDuration;\n                if(instance->code_count_bit == instance->code_min_count_bit_for_found) {\n                    instance->te /= (instance->code_count_bit * 4 + 1);\n\n                    instance->code_last_found = instance->code_found;\n                    instance->code_last_count_bit = instance->code_count_bit;\n                    instance->serial = instance->code_found >> 4;\n                    instance->btn = (uint8_t)instance->code_found & 0x00000F;\n\n                    if(instance->callback) instance->callback(instance, instance->context);\n                }\n                instance->code_found = 0;\n                instance->code_count_bit = 0;\n                instance->te = 0;\n                break;\n            }\n\n            instance->te += duration;\n\n            if((DURATION_DIFF(instance->te_last, instance->te_short) < instance->te_delta) &&\n               (DURATION_DIFF(duration, instance->te_long) < instance->te_delta * 3)) {\n                subghz_decoder_princeton_for_testing_add_bit(instance, 0);\n                instance->parser_step = PrincetonDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->te_last, instance->te_long) < instance->te_delta * 3) &&\n                (DURATION_DIFF(duration, instance->te_short) < instance->te_delta)) {\n                subghz_decoder_princeton_for_testing_add_bit(instance, 1);\n                instance->parser_step = PrincetonDecoderStepSaveDuration;\n            } else {\n                instance->parser_step = PrincetonDecoderStepReset;\n            }\n        } else {\n            instance->parser_step = PrincetonDecoderStepReset;\n        }\n        break;\n    }\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/protocol/princeton_for_testing.h",
    "content": "#pragma once\n\n//#include \"base.h\"\n#include <furi.h>\n#include <lib/toolbox/level_duration.h>\n\n/** SubGhzDecoderPrinceton anonymous type */\ntypedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton;\n/** SubGhzEncoderPrinceton anonymous type */\ntypedef struct SubGhzEncoderPrinceton SubGhzEncoderPrinceton;\n\ntypedef void (*SubGhzDecoderPrincetonCallback)(SubGhzDecoderPrinceton* parser, void* context);\n\n/** \n * Allocate SubGhzEncoderPrinceton\n * @return pointer to SubGhzEncoderPrinceton instance\n */\nSubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc(void);\n\n/** \n * Free SubGhzEncoderPrinceton instance\n * @param instance - SubGhzEncoderPrinceton instance\n */\nvoid subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance);\n\n/**\n * Forced transmission stop.\n * @param instance Pointer to a SubGhzEncoderPrinceton instance\n * @param time_stop Transmission stop time, ms\n */\nvoid subghz_encoder_princeton_for_testing_stop(\n    SubGhzEncoderPrinceton* instance,\n    uint32_t time_stop);\n\n/** \n * Set new encoder params\n * @param instance - SubGhzEncoderPrinceton instance\n * @param key - 24bit key\n * @param repeat - how many times to repeat \n * @param frequency - frequency\n */\nvoid subghz_encoder_princeton_for_testing_set(\n    SubGhzEncoderPrinceton* instance,\n    uint32_t key,\n    size_t repeat,\n    uint32_t frequency);\n\n/** \n * Get repeat count left\n * @param instance - SubGhzEncoderPrinceton instance\n * @return repeat count left\n */\nsize_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance);\n\n/** \n * Print encoder log\n * @param instance - SubGhzEncoderPrinceton instance\n */\nvoid subghz_encoder_princeton_for_testing_print_log(void* context);\n\n/** \n * Get level duration\n * @param instance - SubGhzEncoderPrinceton instance\n * @return level duration\n */\nLevelDuration subghz_encoder_princeton_for_testing_yield(void* context);\n\n/** \n * Allocate SubGhzDecoderPrinceton\n * @return SubGhzDecoderPrinceton* \n */\nSubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void);\n\n/** \n * Free SubGhzDecoderPrinceton\n * @param instance \n */\nvoid subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance);\n\nvoid subghz_decoder_princeton_for_testing_set_callback(\n    SubGhzDecoderPrinceton* instance,\n    SubGhzDecoderPrincetonCallback callback,\n    void* context);\n\n/** \n * Reset internal state\n * @param instance - SubGhzDecoderPrinceton instance\n */\nvoid subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance);\n\n/** \n * Parse accepted duration\n * @param instance - SubGhzDecoderPrinceton instance\n * @param data - LevelDuration level_duration\n */\nvoid subghz_decoder_princeton_for_testing_parse(\n    SubGhzDecoderPrinceton* instance,\n    bool level,\n    uint32_t duration);\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene.c",
    "content": "#include \"../subghz_test_app_i.h\" // IWYU pragma: keep\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const subghz_test_scene_on_enter_handlers[])(void*) = {\n#include \"subghz_test_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const subghz_test_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"subghz_test_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const subghz_test_scene_on_exit_handlers[])(void* context) = {\n#include \"subghz_test_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers subghz_test_scene_handlers = {\n    .on_enter_handlers = subghz_test_scene_on_enter_handlers,\n    .on_event_handlers = subghz_test_scene_on_event_handlers,\n    .on_exit_handlers = subghz_test_scene_on_exit_handlers,\n    .scene_num = SubGhzTestSceneNum,\n};\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) SubGhzTestScene##id,\ntypedef enum {\n#include \"subghz_test_scene_config.h\"\n    SubGhzTestSceneNum,\n} SubGhzTestScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers subghz_test_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"subghz_test_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"subghz_test_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"subghz_test_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_about.c",
    "content": "#include \"../subghz_test_app_i.h\"\n\nvoid subghz_test_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {\n    SubGhzTestApp* app = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, result);\n    }\n}\n\nvoid subghz_test_scene_about_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    furi_string_printf(temp_str, \"\\e#%s\\n\", \"Information\");\n\n    furi_string_cat_printf(temp_str, \"Version: %s\\n\", SUBGHZ_TEST_VERSION_APP);\n    furi_string_cat_printf(temp_str, \"Developed by: %s\\n\", SUBGHZ_TEST_DEVELOPED);\n    furi_string_cat_printf(temp_str, \"Github: %s\\n\\n\", SUBGHZ_TEST_GITHUB);\n\n    furi_string_cat_printf(temp_str, \"\\e#%s\\n\", \"Description\");\n    furi_string_cat_printf(\n        temp_str,\n        \"This application is designed\\nto test the functionality of the\\nbuilt-in CC1101 module.\\n\\n\");\n\n    widget_add_text_box_element(\n        app->widget,\n        0,\n        0,\n        128,\n        14,\n        AlignCenter,\n        AlignBottom,\n        \"\\e#\\e!                                                      \\e!\\n\",\n        false);\n    widget_add_text_box_element(\n        app->widget,\n        0,\n        2,\n        128,\n        14,\n        AlignCenter,\n        AlignBottom,\n        \"\\e#\\e!         Sub-Ghz Test          \\e!\\n\",\n        false);\n    widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewWidget);\n}\n\nbool subghz_test_scene_about_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    bool consumed = false;\n    UNUSED(app);\n    UNUSED(event);\n\n    return consumed;\n}\n\nvoid subghz_test_scene_about_on_exit(void* context) {\n    SubGhzTestApp* app = context;\n\n    // Clear views\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c",
    "content": "#include \"../subghz_test_app_i.h\"\n\nvoid subghz_test_scene_carrier_callback(SubGhzTestCarrierEvent event, void* context) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid subghz_test_scene_carrier_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n    subghz_test_carrier_set_callback(\n        app->subghz_test_carrier, subghz_test_scene_carrier_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewCarrier);\n}\n\nbool subghz_test_scene_carrier_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzTestCarrierEventOnlyRx) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_test_scene_carrier_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_config.h",
    "content": "ADD_SCENE(subghz_test, start, Start)\nADD_SCENE(subghz_test, about, About)\nADD_SCENE(subghz_test, carrier, Carrier)\nADD_SCENE(subghz_test, packet, Packet)\nADD_SCENE(subghz_test, static, Static)\nADD_SCENE(subghz_test, show_only_rx, ShowOnlyRx)\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_packet.c",
    "content": "#include \"../subghz_test_app_i.h\"\n\nvoid subghz_test_scene_packet_callback(SubGhzTestPacketEvent event, void* context) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid subghz_test_scene_packet_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n    subghz_test_packet_set_callback(\n        app->subghz_test_packet, subghz_test_scene_packet_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPacket);\n}\n\nbool subghz_test_scene_packet_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzTestPacketEventOnlyRx) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_test_scene_packet_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c",
    "content": "#include \"../subghz_test_app_i.h\"\n#include <subghz_test_icons.h>\n\nvoid subghz_test_scene_show_only_rx_popup_callback(void* context) {\n    SubGhzTestApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, SubGhzTestCustomEventSceneShowOnlyRX);\n}\n\nvoid subghz_test_scene_show_only_rx_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n\n    // Setup view\n    Popup* popup = app->popup;\n\n    const char* header_text = \"Transmission is Blocked\";\n    const char* message_text = \"Transmission on\\nthis frequency is\\nrestricted in\\nyour region\";\n    if(!furi_hal_region_is_provisioned()) {\n        header_text = \"Firmware update needed\";\n        message_text = \"Please update\\nfirmware before\\nusing this feature\\nflipp.dev/upd\";\n    }\n\n    popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop);\n    popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop);\n    popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);\n\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, app);\n    popup_set_callback(popup, subghz_test_scene_show_only_rx_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPopup);\n}\n\nbool subghz_test_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzTestCustomEventSceneShowOnlyRX) {\n            scene_manager_previous_scene(app->scene_manager);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_test_scene_show_only_rx_on_exit(void* context) {\n    SubGhzTestApp* app = context;\n    Popup* popup = app->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_start.c",
    "content": "#include \"../subghz_test_app_i.h\"\n\ntypedef enum {\n    SubmenuIndexSubGhzTestCarrier,\n    SubmenuIndexSubGhzTestPacket,\n    SubmenuIndexSubGhzTestStatic,\n    SubmenuIndexSubGhzTestAbout,\n} SubmenuIndex;\n\nvoid subghz_test_scene_start_submenu_callback(void* context, uint32_t index) {\n    SubGhzTestApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid subghz_test_scene_start_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Carrier\",\n        SubmenuIndexSubGhzTestCarrier,\n        subghz_test_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Packet\",\n        SubmenuIndexSubGhzTestPacket,\n        subghz_test_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Static\",\n        SubmenuIndexSubGhzTestStatic,\n        subghz_test_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"About\",\n        SubmenuIndexSubGhzTestAbout,\n        subghz_test_scene_start_submenu_callback,\n        app);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, SubGhzTestSceneStart));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewSubmenu);\n}\n\nbool subghz_test_scene_start_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexSubGhzTestAbout) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneAbout);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSubGhzTestCarrier) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneCarrier);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSubGhzTestPacket) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestScenePacket);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSubGhzTestStatic) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStatic);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(app->scene_manager, SubGhzTestSceneStart, event.event);\n    }\n\n    return consumed;\n}\n\nvoid subghz_test_scene_start_on_exit(void* context) {\n    SubGhzTestApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/scenes/subghz_test_scene_static.c",
    "content": "#include \"../subghz_test_app_i.h\"\n\nvoid subghz_test_scene_static_callback(SubGhzTestStaticEvent event, void* context) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid subghz_test_scene_static_on_enter(void* context) {\n    SubGhzTestApp* app = context;\n    subghz_test_static_set_callback(\n        app->subghz_test_static, subghz_test_scene_static_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewStatic);\n}\n\nbool subghz_test_scene_static_on_event(void* context, SceneManagerEvent event) {\n    SubGhzTestApp* app = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzTestStaticEventOnlyRx) {\n            scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_test_scene_static_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/subghz_test_app.c",
    "content": "#include \"subghz_test_app_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\nstatic bool subghz_test_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool subghz_test_app_back_event_callback(void* context) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void subghz_test_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    SubGhzTestApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nSubGhzTestApp* subghz_test_app_alloc(void) {\n    SubGhzTestApp* app = malloc(sizeof(SubGhzTestApp));\n\n    // GUI\n    app->gui = furi_record_open(RECORD_GUI);\n\n    // View Dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&subghz_test_scene_handlers, app);\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, subghz_test_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, subghz_test_app_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, subghz_test_app_tick_event_callback, 100);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Open Notification record\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // SubMenu\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, SubGhzTestViewSubmenu, submenu_get_view(app->submenu));\n\n    // Widget\n    app->widget = widget_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, SubGhzTestViewWidget, widget_get_view(app->widget));\n\n    // Popup\n    app->popup = popup_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, SubGhzTestViewPopup, popup_get_view(app->popup));\n\n    // Carrier Test Module\n    app->subghz_test_carrier = subghz_test_carrier_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        SubGhzTestViewCarrier,\n        subghz_test_carrier_get_view(app->subghz_test_carrier));\n\n    // Packet Test\n    app->subghz_test_packet = subghz_test_packet_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        SubGhzTestViewPacket,\n        subghz_test_packet_get_view(app->subghz_test_packet));\n\n    // Static send\n    app->subghz_test_static = subghz_test_static_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        SubGhzTestViewStatic,\n        subghz_test_static_get_view(app->subghz_test_static));\n\n    scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStart);\n\n    return app;\n}\n\nvoid subghz_test_app_free(SubGhzTestApp* app) {\n    furi_assert(app);\n\n    // Submenu\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewSubmenu);\n    submenu_free(app->submenu);\n\n    //  Widget\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewWidget);\n    widget_free(app->widget);\n\n    // Popup\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPopup);\n    popup_free(app->popup);\n\n    // Carrier Test\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewCarrier);\n    subghz_test_carrier_free(app->subghz_test_carrier);\n\n    // Packet Test\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPacket);\n    subghz_test_packet_free(app->subghz_test_packet);\n\n    // Static\n    view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewStatic);\n    subghz_test_static_free(app->subghz_test_static);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Notifications\n    furi_record_close(RECORD_NOTIFICATION);\n    app->notifications = NULL;\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n\n    free(app);\n}\n\nint32_t subghz_test_app(void* p) {\n    UNUSED(p);\n    SubGhzTestApp* subghz_test_app = subghz_test_app_alloc();\n\n    view_dispatcher_run(subghz_test_app->view_dispatcher);\n\n    subghz_test_app_free(subghz_test_app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/subghz_test_app_i.c",
    "content": "#include <furi.h>\n\n#define TAG \"SubGhzTest\"\n"
  },
  {
    "path": "applications/debug/subghz_test/subghz_test_app_i.h",
    "content": "#pragma once\n\n#include \"helpers/subghz_test_types.h\"\n#include \"helpers/subghz_test_event.h\"\n\n#include \"scenes/subghz_test_scene.h\"\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/widget.h>\n#include <gui/modules/popup.h>\n#include <notification/notification_messages.h>\n\n#include \"views/subghz_test_static.h\"\n#include \"views/subghz_test_carrier.h\"\n#include \"views/subghz_test_packet.h\"\n\ntypedef struct SubGhzTestApp SubGhzTestApp;\n\nstruct SubGhzTestApp {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    NotificationApp* notifications;\n    Submenu* submenu;\n    Widget* widget;\n    Popup* popup;\n    SubGhzTestStatic* subghz_test_static;\n    SubGhzTestCarrier* subghz_test_carrier;\n    SubGhzTestPacket* subghz_test_packet;\n};\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_carrier.c",
    "content": "#include \"subghz_test_carrier.h\"\n#include \"../helpers/subghz_test_frequency.h\"\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#include <math.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n\nstruct SubGhzTestCarrier {\n    View* view;\n    FuriTimer* timer;\n    SubGhzTestCarrierCallback callback;\n    void* context;\n};\n\ntypedef enum {\n    SubGhzTestCarrierModelStatusRx,\n    SubGhzTestCarrierModelStatusTx,\n} SubGhzTestCarrierModelStatus;\n\ntypedef struct {\n    uint8_t frequency;\n    uint32_t real_frequency;\n    FuriHalSubGhzPath path;\n    float rssi;\n    SubGhzTestCarrierModelStatus status;\n} SubGhzTestCarrierModel;\n\nvoid subghz_test_carrier_set_callback(\n    SubGhzTestCarrier* subghz_test_carrier,\n    SubGhzTestCarrierCallback callback,\n    void* context) {\n    furi_assert(subghz_test_carrier);\n    furi_assert(callback);\n    subghz_test_carrier->callback = callback;\n    subghz_test_carrier->context = context;\n}\n\nvoid subghz_test_carrier_draw(Canvas* canvas, SubGhzTestCarrierModel* model) {\n    char buffer[64];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 8, \"CC1101 Basic Test\");\n\n    canvas_set_font(canvas, FontSecondary);\n    // Frequency\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"Freq: %03ld.%03ld.%03ld Hz\",\n        model->real_frequency / 1000000 % 1000,\n        model->real_frequency / 1000 % 1000,\n        model->real_frequency % 1000);\n    canvas_draw_str(canvas, 0, 20, buffer);\n    // Path\n    char* path_name = \"Unknown\";\n    if(model->path == FuriHalSubGhzPathIsolate) {\n        path_name = \"isolate\";\n    } else if(model->path == FuriHalSubGhzPath433) {\n        path_name = \"433MHz\";\n    } else if(model->path == FuriHalSubGhzPath315) {\n        path_name = \"315MHz\";\n    } else if(model->path == FuriHalSubGhzPath868) {\n        path_name = \"868MHz\";\n    }\n    snprintf(buffer, sizeof(buffer), \"Path: %d - %s\", model->path, path_name);\n    canvas_draw_str(canvas, 0, 31, buffer);\n    if(model->status == SubGhzTestCarrierModelStatusRx) {\n        snprintf(\n            buffer,\n            sizeof(buffer),\n            \"RSSI: %ld.%ld dBm\",\n            (int32_t)(model->rssi),\n            (int32_t)fabsf(model->rssi * 10.f) % 10);\n        canvas_draw_str(canvas, 0, 42, buffer);\n    } else {\n        canvas_draw_str(canvas, 0, 42, \"TX\");\n    }\n}\n\nbool subghz_test_carrier_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzTestCarrier* subghz_test_carrier = context;\n\n    if(event->key == InputKeyBack || event->type != InputTypeShort) {\n        return false;\n    }\n\n    with_view_model(\n        subghz_test_carrier->view,\n        SubGhzTestCarrierModel * model,\n        {\n            furi_hal_subghz_idle();\n\n            if(event->key == InputKeyLeft) {\n                if(model->frequency > 0) model->frequency--;\n            } else if(event->key == InputKeyRight) {\n                if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;\n            } else if(event->key == InputKeyDown) {\n                if(model->path > 0) model->path--;\n            } else if(event->key == InputKeyUp) {\n                if(model->path < FuriHalSubGhzPath868) model->path++;\n            } else if(event->key == InputKeyOk) {\n                if(model->status == SubGhzTestCarrierModelStatusTx) {\n                    model->status = SubGhzTestCarrierModelStatusRx;\n                } else {\n                    model->status = SubGhzTestCarrierModelStatusTx;\n                }\n            }\n\n            model->real_frequency =\n                furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);\n            furi_hal_subghz_set_path(model->path);\n\n            if(model->status == SubGhzTestCarrierModelStatusRx) {\n                furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);\n                furi_hal_subghz_rx();\n            } else {\n                furi_hal_gpio_init(\n                    &gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);\n                furi_hal_gpio_write(&gpio_cc1101_g0, true);\n                if(!furi_hal_subghz_tx()) {\n                    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);\n                    subghz_test_carrier->callback(\n                        SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context);\n                }\n            }\n        },\n        true);\n\n    return true;\n}\n\nvoid subghz_test_carrier_enter(void* context) {\n    furi_assert(context);\n    SubGhzTestCarrier* subghz_test_carrier = context;\n\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n\n    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);\n\n    with_view_model(\n        subghz_test_carrier->view,\n        SubGhzTestCarrierModel * model,\n        {\n            model->frequency = subghz_frequencies_433_92_testing; // 433\n            model->real_frequency =\n                furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);\n            model->path = FuriHalSubGhzPathIsolate; // isolate\n            model->rssi = 0.0f;\n            model->status = SubGhzTestCarrierModelStatusRx;\n        },\n        true);\n\n    furi_hal_subghz_rx();\n\n    furi_timer_start(subghz_test_carrier->timer, furi_kernel_get_tick_frequency() / 4);\n}\n\nvoid subghz_test_carrier_exit(void* context) {\n    furi_assert(context);\n    SubGhzTestCarrier* subghz_test_carrier = context;\n\n    furi_timer_stop(subghz_test_carrier->timer);\n\n    // Reinitialize IC to default state\n    furi_hal_subghz_sleep();\n}\n\nvoid subghz_test_carrier_rssi_timer_callback(void* context) {\n    furi_assert(context);\n    SubGhzTestCarrier* subghz_test_carrier = context;\n\n    with_view_model(\n        subghz_test_carrier->view,\n        SubGhzTestCarrierModel * model,\n        {\n            if(model->status == SubGhzTestCarrierModelStatusRx) {\n                model->rssi = furi_hal_subghz_get_rssi();\n            }\n        },\n        false);\n}\n\nSubGhzTestCarrier* subghz_test_carrier_alloc(void) {\n    SubGhzTestCarrier* subghz_test_carrier = malloc(sizeof(SubGhzTestCarrier));\n\n    // View allocation and configuration\n    subghz_test_carrier->view = view_alloc();\n    view_allocate_model(\n        subghz_test_carrier->view, ViewModelTypeLocking, sizeof(SubGhzTestCarrierModel));\n    view_set_context(subghz_test_carrier->view, subghz_test_carrier);\n    view_set_draw_callback(subghz_test_carrier->view, (ViewDrawCallback)subghz_test_carrier_draw);\n    view_set_input_callback(subghz_test_carrier->view, subghz_test_carrier_input);\n    view_set_enter_callback(subghz_test_carrier->view, subghz_test_carrier_enter);\n    view_set_exit_callback(subghz_test_carrier->view, subghz_test_carrier_exit);\n\n    subghz_test_carrier->timer = furi_timer_alloc(\n        subghz_test_carrier_rssi_timer_callback, FuriTimerTypePeriodic, subghz_test_carrier);\n\n    return subghz_test_carrier;\n}\n\nvoid subghz_test_carrier_free(SubGhzTestCarrier* subghz_test_carrier) {\n    furi_assert(subghz_test_carrier);\n    furi_timer_free(subghz_test_carrier->timer);\n    view_free(subghz_test_carrier->view);\n    free(subghz_test_carrier);\n}\n\nView* subghz_test_carrier_get_view(SubGhzTestCarrier* subghz_test_carrier) {\n    furi_assert(subghz_test_carrier);\n    return subghz_test_carrier->view;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_carrier.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef enum {\n    SubGhzTestCarrierEventOnlyRx,\n} SubGhzTestCarrierEvent;\n\ntypedef struct SubGhzTestCarrier SubGhzTestCarrier;\n\ntypedef void (*SubGhzTestCarrierCallback)(SubGhzTestCarrierEvent event, void* context);\n\nvoid subghz_test_carrier_set_callback(\n    SubGhzTestCarrier* subghz_test_carrier,\n    SubGhzTestCarrierCallback callback,\n    void* context);\n\nSubGhzTestCarrier* subghz_test_carrier_alloc(void);\n\nvoid subghz_test_carrier_free(SubGhzTestCarrier* subghz_test_carrier);\n\nView* subghz_test_carrier_get_view(SubGhzTestCarrier* subghz_test_carrier);\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_packet.c",
    "content": "#include \"subghz_test_packet.h\"\n#include \"../helpers/subghz_test_frequency.h\"\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#include <math.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n#include <toolbox/level_duration.h>\n#include \"../protocol/princeton_for_testing.h\"\n\n#define SUBGHZ_TEST_PACKET_COUNT 500\n\nstruct SubGhzTestPacket {\n    View* view;\n    FuriTimer* timer;\n\n    SubGhzDecoderPrinceton* decoder;\n    SubGhzEncoderPrinceton* encoder;\n    volatile size_t packet_rx;\n    SubGhzTestPacketCallback callback;\n    void* context;\n};\n\ntypedef enum {\n    SubGhzTestPacketModelStatusRx,\n    SubGhzTestPacketModelStatusOnlyRx,\n    SubGhzTestPacketModelStatusTx,\n} SubGhzTestPacketModelStatus;\n\ntypedef struct {\n    uint8_t frequency;\n    uint32_t real_frequency;\n    FuriHalSubGhzPath path;\n    float rssi;\n    size_t packets;\n    SubGhzTestPacketModelStatus status;\n} SubGhzTestPacketModel;\n\nvolatile bool subghz_test_packet_overrun = false;\n\nvoid subghz_test_packet_set_callback(\n    SubGhzTestPacket* subghz_test_packet,\n    SubGhzTestPacketCallback callback,\n    void* context) {\n    furi_assert(subghz_test_packet);\n    furi_assert(callback);\n    subghz_test_packet->callback = callback;\n    subghz_test_packet->context = context;\n}\n\nstatic void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* context) {\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n    subghz_decoder_princeton_for_testing_parse(instance->decoder, level, duration);\n}\n\nstatic void subghz_test_packet_rx_pt_callback(SubGhzDecoderPrinceton* parser, void* context) {\n    UNUSED(parser);\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n    instance->packet_rx++;\n}\n\nstatic void subghz_test_packet_rssi_timer_callback(void* context) {\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n\n    with_view_model(\n        instance->view,\n        SubGhzTestPacketModel * model,\n        {\n            if(model->status == SubGhzTestPacketModelStatusRx) {\n                model->rssi = furi_hal_subghz_get_rssi();\n                model->packets = instance->packet_rx;\n            } else if(model->status == SubGhzTestPacketModelStatusTx) {\n                model->packets =\n                    SUBGHZ_TEST_PACKET_COUNT -\n                    subghz_encoder_princeton_for_testing_get_repeat_left(instance->encoder);\n            }\n        },\n        true);\n}\n\nstatic void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model) {\n    char buffer[64];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 8, \"CC1101 Packet Test\");\n\n    canvas_set_font(canvas, FontSecondary);\n    // Frequency\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"Freq: %03ld.%03ld.%03ld Hz\",\n        model->real_frequency / 1000000 % 1000,\n        model->real_frequency / 1000 % 1000,\n        model->real_frequency % 1000);\n    canvas_draw_str(canvas, 0, 20, buffer);\n    // Path\n    char* path_name = \"Unknown\";\n    if(model->path == FuriHalSubGhzPathIsolate) {\n        path_name = \"isolate\";\n    } else if(model->path == FuriHalSubGhzPath433) {\n        path_name = \"433MHz\";\n    } else if(model->path == FuriHalSubGhzPath315) {\n        path_name = \"315MHz\";\n    } else if(model->path == FuriHalSubGhzPath868) {\n        path_name = \"868MHz\";\n    }\n    snprintf(buffer, sizeof(buffer), \"Path: %d - %s\", model->path, path_name);\n    canvas_draw_str(canvas, 0, 31, buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Packets: %zu\", model->packets);\n    canvas_draw_str(canvas, 0, 42, buffer);\n\n    if(model->status == SubGhzTestPacketModelStatusRx) {\n        snprintf(\n            buffer,\n            sizeof(buffer),\n            \"RSSI: %ld.%ld dBm\",\n            (int32_t)(model->rssi),\n            (int32_t)fabsf(model->rssi * 10.0f) % 10);\n        canvas_draw_str(canvas, 0, 53, buffer);\n    } else {\n        canvas_draw_str(canvas, 0, 53, \"TX\");\n    }\n}\n\nstatic bool subghz_test_packet_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n\n    if(event->key == InputKeyBack || event->type != InputTypeShort) {\n        return false;\n    }\n\n    with_view_model(\n        instance->view,\n        SubGhzTestPacketModel * model,\n        {\n            if(model->status == SubGhzTestPacketModelStatusRx) {\n                furi_hal_subghz_stop_async_rx();\n            } else if(model->status == SubGhzTestPacketModelStatusTx) {\n                subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick());\n                furi_hal_subghz_stop_async_tx();\n            }\n\n            if(event->key == InputKeyLeft) {\n                if(model->frequency > 0) model->frequency--;\n            } else if(event->key == InputKeyRight) {\n                if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;\n            } else if(event->key == InputKeyDown) {\n                if(model->path > 0) model->path--;\n            } else if(event->key == InputKeyUp) {\n                if(model->path < FuriHalSubGhzPath868) model->path++;\n            } else if(event->key == InputKeyOk) {\n                if(model->status == SubGhzTestPacketModelStatusRx) {\n                    model->status = SubGhzTestPacketModelStatusTx;\n                } else {\n                    model->status = SubGhzTestPacketModelStatusRx;\n                }\n            }\n\n            model->real_frequency =\n                furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);\n            furi_hal_subghz_set_path(model->path);\n\n            if(model->status == SubGhzTestPacketModelStatusRx) {\n                furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance);\n            } else {\n                subghz_encoder_princeton_for_testing_set(\n                    instance->encoder,\n                    0x00AABBCC,\n                    SUBGHZ_TEST_PACKET_COUNT,\n                    subghz_frequencies_testing[model->frequency]);\n                if(!furi_hal_subghz_start_async_tx(\n                       subghz_encoder_princeton_for_testing_yield, instance->encoder)) {\n                    model->status = SubGhzTestPacketModelStatusOnlyRx;\n                    instance->callback(SubGhzTestPacketEventOnlyRx, instance->context);\n                }\n            }\n        },\n        true);\n\n    return true;\n}\n\nvoid subghz_test_packet_enter(void* context) {\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n\n    with_view_model(\n        instance->view,\n        SubGhzTestPacketModel * model,\n        {\n            model->frequency = subghz_frequencies_433_92_testing;\n            model->real_frequency =\n                furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);\n            model->path = FuriHalSubGhzPathIsolate; // isolate\n            model->rssi = 0.0f;\n            model->status = SubGhzTestPacketModelStatusRx;\n        },\n        true);\n\n    furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance);\n\n    furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 4);\n}\n\nvoid subghz_test_packet_exit(void* context) {\n    furi_assert(context);\n    SubGhzTestPacket* instance = context;\n\n    furi_timer_stop(instance->timer);\n\n    // Reinitialize IC to default state\n    with_view_model(\n        instance->view,\n        SubGhzTestPacketModel * model,\n        {\n            if(model->status == SubGhzTestPacketModelStatusRx) {\n                furi_hal_subghz_stop_async_rx();\n            } else if(model->status == SubGhzTestPacketModelStatusTx) {\n                subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick());\n                furi_hal_subghz_stop_async_tx();\n            }\n        },\n        true);\n    furi_hal_subghz_sleep();\n}\n\nSubGhzTestPacket* subghz_test_packet_alloc(void) {\n    SubGhzTestPacket* instance = malloc(sizeof(SubGhzTestPacket));\n\n    // View allocation and configuration\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzTestPacketModel));\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_packet_draw);\n    view_set_input_callback(instance->view, subghz_test_packet_input);\n    view_set_enter_callback(instance->view, subghz_test_packet_enter);\n    view_set_exit_callback(instance->view, subghz_test_packet_exit);\n\n    instance->timer =\n        furi_timer_alloc(subghz_test_packet_rssi_timer_callback, FuriTimerTypePeriodic, instance);\n\n    instance->decoder = subghz_decoder_princeton_for_testing_alloc();\n    subghz_decoder_princeton_for_testing_set_callback(\n        instance->decoder, subghz_test_packet_rx_pt_callback, instance);\n    instance->encoder = subghz_encoder_princeton_for_testing_alloc();\n\n    return instance;\n}\n\nvoid subghz_test_packet_free(SubGhzTestPacket* instance) {\n    furi_assert(instance);\n\n    subghz_decoder_princeton_for_testing_free(instance->decoder);\n    subghz_encoder_princeton_for_testing_free(instance->encoder);\n\n    furi_timer_free(instance->timer);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* subghz_test_packet_get_view(SubGhzTestPacket* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_packet.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef enum {\n    SubGhzTestPacketEventOnlyRx,\n} SubGhzTestPacketEvent;\n\ntypedef struct SubGhzTestPacket SubGhzTestPacket;\n\ntypedef void (*SubGhzTestPacketCallback)(SubGhzTestPacketEvent event, void* context);\n\nvoid subghz_test_packet_set_callback(\n    SubGhzTestPacket* subghz_test_packet,\n    SubGhzTestPacketCallback callback,\n    void* context);\n\nSubGhzTestPacket* subghz_test_packet_alloc(void);\n\nvoid subghz_test_packet_free(SubGhzTestPacket* subghz_test_packet);\n\nView* subghz_test_packet_get_view(SubGhzTestPacket* subghz_test_packet);\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_static.c",
    "content": "#include \"subghz_test_static.h\"\n#include \"../helpers/subghz_test_frequency.h\"\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n#include <notification/notification_messages.h>\n#include \"../protocol/princeton_for_testing.h\"\n\n#define TAG \"SubGhzTestStatic\"\n\ntypedef enum {\n    SubGhzTestStaticStatusIDLE,\n    SubGhzTestStaticStatusTX,\n} SubGhzTestStaticStatus;\n\nstatic const uint32_t subghz_test_static_keys[] = {\n    0x0074BADE,\n    0x0074BADD,\n    0x0074BADB,\n    0x00E34A4E,\n};\n\nstruct SubGhzTestStatic {\n    View* view;\n    SubGhzTestStaticStatus status_tx;\n    SubGhzEncoderPrinceton* encoder;\n    SubGhzTestStaticCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint8_t frequency;\n    uint32_t real_frequency;\n    uint8_t button;\n} SubGhzTestStaticModel;\n\nvoid subghz_test_static_set_callback(\n    SubGhzTestStatic* subghz_test_static,\n    SubGhzTestStaticCallback callback,\n    void* context) {\n    furi_assert(subghz_test_static);\n    furi_assert(callback);\n    subghz_test_static->callback = callback;\n    subghz_test_static->context = context;\n}\n\nvoid subghz_test_static_draw(Canvas* canvas, SubGhzTestStaticModel* model) {\n    char buffer[64];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 8, \"CC1101 Static\");\n\n    canvas_set_font(canvas, FontSecondary);\n    // Frequency\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"Freq: %03ld.%03ld.%03ld Hz\",\n        model->real_frequency / 1000000 % 1000,\n        model->real_frequency / 1000 % 1000,\n        model->real_frequency % 1000);\n    canvas_draw_str(canvas, 0, 20, buffer);\n    snprintf(buffer, sizeof(buffer), \"Key: %d\", model->button);\n    canvas_draw_str(canvas, 0, 31, buffer);\n}\n\nbool subghz_test_static_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzTestStatic* instance = context;\n\n    if(event->key == InputKeyBack) {\n        return false;\n    }\n\n    with_view_model(\n        instance->view,\n        SubGhzTestStaticModel * model,\n        {\n            if(event->type == InputTypeShort) {\n                if(event->key == InputKeyLeft) {\n                    if(model->frequency > 0) model->frequency--;\n                } else if(event->key == InputKeyRight) {\n                    if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;\n                } else if(event->key == InputKeyDown) {\n                    if(model->button > 0) model->button--;\n                } else if(event->key == InputKeyUp) {\n                    if(model->button < 3) model->button++;\n                }\n            }\n\n            model->real_frequency = subghz_frequencies_testing[model->frequency];\n\n            if(event->key == InputKeyOk) {\n                NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n                if(event->type == InputTypePress) {\n                    furi_hal_subghz_idle();\n                    furi_hal_subghz_set_frequency_and_path(\n                        subghz_frequencies_testing[model->frequency]);\n                    if(!furi_hal_subghz_tx()) {\n                        instance->callback(SubGhzTestStaticEventOnlyRx, instance->context);\n                    } else {\n                        notification_message_block(notification, &sequence_set_red_255);\n\n                        FURI_LOG_I(TAG, \"TX Start\");\n\n                        subghz_encoder_princeton_for_testing_set(\n                            instance->encoder,\n                            subghz_test_static_keys[model->button],\n                            10000,\n                            subghz_frequencies_testing[model->frequency]);\n\n                        furi_hal_subghz_start_async_tx(\n                            subghz_encoder_princeton_for_testing_yield, instance->encoder);\n                        instance->status_tx = SubGhzTestStaticStatusTX;\n                    }\n                } else if(event->type == InputTypeRelease) {\n                    if(instance->status_tx == SubGhzTestStaticStatusTX) {\n                        FURI_LOG_I(TAG, \"TX Stop\");\n                        subghz_encoder_princeton_for_testing_stop(\n                            instance->encoder, furi_get_tick());\n                        subghz_encoder_princeton_for_testing_print_log(instance->encoder);\n                        furi_hal_subghz_stop_async_tx();\n                        notification_message(notification, &sequence_reset_red);\n                    }\n                    instance->status_tx = SubGhzTestStaticStatusIDLE;\n                }\n                furi_record_close(RECORD_NOTIFICATION);\n            }\n        },\n        true);\n\n    return true;\n}\n\nvoid subghz_test_static_enter(void* context) {\n    furi_assert(context);\n    SubGhzTestStatic* instance = context;\n\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n\n    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);\n    furi_hal_gpio_write(&gpio_cc1101_g0, false);\n    instance->status_tx = SubGhzTestStaticStatusIDLE;\n\n    with_view_model(\n        instance->view,\n        SubGhzTestStaticModel * model,\n        {\n            model->frequency = subghz_frequencies_433_92_testing;\n            model->real_frequency = subghz_frequencies_testing[model->frequency];\n            model->button = 0;\n        },\n        true);\n}\n\nvoid subghz_test_static_exit(void* context) {\n    furi_assert(context);\n    furi_hal_subghz_sleep();\n}\n\nSubGhzTestStatic* subghz_test_static_alloc(void) {\n    SubGhzTestStatic* instance = malloc(sizeof(SubGhzTestStatic));\n\n    // View allocation and configuration\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzTestStaticModel));\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_static_draw);\n    view_set_input_callback(instance->view, subghz_test_static_input);\n    view_set_enter_callback(instance->view, subghz_test_static_enter);\n    view_set_exit_callback(instance->view, subghz_test_static_exit);\n\n    instance->encoder = subghz_encoder_princeton_for_testing_alloc();\n\n    return instance;\n}\n\nvoid subghz_test_static_free(SubGhzTestStatic* instance) {\n    furi_assert(instance);\n    subghz_encoder_princeton_for_testing_free(instance->encoder);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* subghz_test_static_get_view(SubGhzTestStatic* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/debug/subghz_test/views/subghz_test_static.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef enum {\n    SubGhzTestStaticEventOnlyRx,\n} SubGhzTestStaticEvent;\n\ntypedef struct SubGhzTestStatic SubGhzTestStatic;\n\ntypedef void (*SubGhzTestStaticCallback)(SubGhzTestStaticEvent event, void* context);\n\nvoid subghz_test_static_set_callback(\n    SubGhzTestStatic* subghz_test_static,\n    SubGhzTestStaticCallback callback,\n    void* context);\n\nSubGhzTestStatic* subghz_test_static_alloc(void);\n\nvoid subghz_test_static_free(SubGhzTestStatic* subghz_static);\n\nView* subghz_test_static_get_view(SubGhzTestStatic* subghz_static);\n"
  },
  {
    "path": "applications/debug/text_box_element_test/application.fam",
    "content": "App(\n    appid=\"text_box_element_test\",\n    name=\"Text Box Element Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"text_box_element_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/text_box_element_test/text_box_element_test.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <input/input.h>\n#include <gui/elements.h>\n\n#define TAG \"TextBoxTest\"\n\nstatic void text_box_center_top_secondary_128x22(Canvas* canvas) {\n    canvas_draw_frame(canvas, 0, 0, 128, 22);\n    elements_text_box(canvas, 0, 0, 128, 22, AlignCenter, AlignTop, \"secondary font test\", false);\n}\n\nstatic void text_box_right_bottom_bold_128x22(Canvas* canvas) {\n    canvas_draw_frame(canvas, 0, 0, 128, 22);\n    elements_text_box(\n        canvas, 0, 0, 128, 22, AlignRight, AlignBottom, \"\\e#Bold font test\\e#\", false);\n}\n\nstatic void text_box_left_center_mixed_80x50(Canvas* canvas) {\n    canvas_draw_frame(canvas, 0, 0, 80, 50);\n    elements_text_box(\n        canvas,\n        0,\n        0,\n        80,\n        50,\n        AlignLeft,\n        AlignCenter,\n        \"\\e#Never\\e# gonna give you up\\n\\e!Never\\e! gonna let you down\",\n        false);\n}\n\nstatic void text_box_center_center_secondary_110x44(Canvas* canvas) {\n    canvas_draw_frame(canvas, 4, 20, 110, 30);\n    elements_text_box(\n        canvas,\n        4,\n        20,\n        110,\n        30,\n        AlignCenter,\n        AlignCenter,\n        \"Loooooooooooooo0000000ooong file name from happy 100500 Flipper 0wners\",\n        true);\n}\n\nstatic void (*text_box_test_render[])(Canvas* canvas) = {\n    text_box_center_top_secondary_128x22,\n    text_box_right_bottom_bold_128x22,\n    text_box_left_center_mixed_80x50,\n    text_box_center_center_secondary_110x44,\n};\n\ntypedef struct {\n    uint32_t idx;\n    FuriMutex* mutex;\n} TextBoxTestState;\n\nstatic void text_box_test_render_callback(Canvas* canvas, void* ctx) {\n    TextBoxTestState* state = ctx;\n    furi_mutex_acquire(state->mutex, FuriWaitForever);\n    canvas_clear(canvas);\n\n    text_box_test_render[state->idx](canvas);\n\n    furi_mutex_release(state->mutex);\n}\n\nstatic void text_box_test_input_callback(InputEvent* input_event, void* ctx) {\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t text_box_element_test_app(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));\n    TextBoxTestState state = {.idx = 0, .mutex = NULL};\n    state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n    ViewPort* view_port = view_port_alloc();\n\n    view_port_draw_callback_set(view_port, text_box_test_render_callback, &state);\n    view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue);\n\n    // Open GUI and register view_port\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    uint32_t test_renders_num = COUNT_OF(text_box_test_render);\n    InputEvent event;\n    while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {\n        furi_mutex_acquire(state.mutex, FuriWaitForever);\n\n        if(event.type == InputTypeShort) {\n            if(event.key == InputKeyRight) {\n                if(state.idx < test_renders_num - 1) {\n                    state.idx++;\n                }\n            } else if(event.key == InputKeyLeft) {\n                if(state.idx > 0) {\n                    state.idx--;\n                }\n            } else if(event.key == InputKeyBack) {\n                furi_mutex_release(state.mutex);\n                break;\n            }\n        }\n\n        furi_mutex_release(state.mutex);\n        view_port_update(view_port);\n    }\n\n    // remove & free all stuff created by app\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n    furi_mutex_free(state.mutex);\n\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/text_box_view_test/application.fam",
    "content": "App(\n    appid=\"text_box_view_test\",\n    name=\"Text Box View Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"text_box_view_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/text_box_view_test/text_box_view_test.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/text_box.h>\n#include <gui/view_stack.h>\n\n#define TAG \"TextBoxViewTest\"\n\ntypedef struct {\n    TextBoxFont font;\n    TextBoxFocus focus;\n    const char* text;\n} TextBoxViewTestContent;\n\nstatic const TextBoxViewTestContent text_box_view_test_content_arr[] = {\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusStart,\n        .text = \"Hello, let's test text box. Press Right and Left to switch content\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text = \"First test to add dynamically lines with EndFocus set\\nLine 0\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text = \"First test to add dynamically lines with EndFocus set\\nLine 0\\nLine 1\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text = \"First test to add dynamically lines with EndFocus set\\nLine 0\\nLine 1\\nLine 2\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text =\n            \"First test to add dynamically lines with EndFocus set\\nLine 0\\nLine 1\\nLine 2\\nLine 3\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text =\n            \"First test to add dynamically lines with EndFocus set\\nLine 0\\nLine 1\\nLine 2\\nLine 3\\nLine 4\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusStart,\n        .text =\n            \"Verify that symbols don't overlap borders: llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllend\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusStart,\n        .text =\n            \"\\n\\n\\n Start from several newline chars. Verify that scrolling doesn't break.\\n\\n\\n\\n\\nThe end\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusStart,\n        .text =\n            \"Let's test big text.\\n\\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \\\"Cortex-MxF\\\", where 'x' is the core variant.\\n\\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.\",\n    },\n    {\n        .font = TextBoxFontText,\n        .focus = TextBoxFocusEnd,\n        .text =\n            \"The same but with EndFocus\\n\\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \\\"Cortex-MxF\\\", where 'x' is the core variant.\\n\\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.\",\n    },\n    {\n        .font = TextBoxFontHex,\n        .focus = TextBoxFocusEnd,\n        .text =\n            \"0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\\n0000 0000 0000 0000\\n1111 1111 1111 1111\\n2222 2222 2222 2222\\n3333 3333 3333 3333\\n4444 4444 4444 4444\\n5555 5555 5555 5555\\n6666 6666 6666 6666\\n7777 7777 7777 7777\\n8888 8888 8888 8888\\n9999 9999 9999 9999\",\n    },\n};\n\ntypedef struct {\n    TextBox* text_box;\n    ViewDispatcher* view_dispatcher;\n    size_t current_content_i;\n} TextBoxViewTest;\n\nstatic void text_box_update_view(TextBoxViewTest* instance) {\n    // Intentional incorrect way to reset text box to verify that state resets if text changes\n    text_box_set_text(instance->text_box, \"\");\n\n    const TextBoxViewTestContent* content =\n        &text_box_view_test_content_arr[instance->current_content_i];\n    text_box_set_font(instance->text_box, content->font);\n    text_box_set_focus(instance->text_box, content->focus);\n    text_box_set_text(instance->text_box, content->text);\n}\n\nstatic bool text_box_switch_view_input_callback(InputEvent* event, void* context) {\n    bool consumed = false;\n    TextBoxViewTest* instance = context;\n    size_t contents_cnt = COUNT_OF(text_box_view_test_content_arr);\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyRight) {\n            if(instance->current_content_i < contents_cnt - 1) {\n                instance->current_content_i++;\n                text_box_update_view(instance);\n                consumed = true;\n            }\n        } else if(event->key == InputKeyLeft) {\n            if(instance->current_content_i > 0) {\n                instance->current_content_i--;\n                text_box_update_view(instance);\n                consumed = true;\n            }\n        } else if(event->key == InputKeyBack) {\n            view_dispatcher_stop(instance->view_dispatcher);\n        }\n    }\n\n    return consumed;\n}\n\nint32_t text_box_view_test_app(void* p) {\n    UNUSED(p);\n\n    Gui* gui = furi_record_open(RECORD_GUI);\n    ViewDispatcher* view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);\n\n    TextBoxViewTest instance = {\n        .text_box = text_box_alloc(),\n        .current_content_i = 0,\n        .view_dispatcher = view_dispatcher,\n    };\n\n    text_box_update_view(&instance);\n\n    View* text_box_switch_view = view_alloc();\n    view_set_input_callback(text_box_switch_view, text_box_switch_view_input_callback);\n    view_set_context(text_box_switch_view, &instance);\n\n    ViewStack* view_stack = view_stack_alloc();\n    view_stack_add_view(view_stack, text_box_switch_view);\n    view_stack_add_view(view_stack, text_box_get_view(instance.text_box));\n\n    view_dispatcher_add_view(view_dispatcher, 0, view_stack_get_view(view_stack));\n    view_dispatcher_switch_to_view(view_dispatcher, 0);\n\n    view_dispatcher_run(view_dispatcher);\n\n    view_dispatcher_remove_view(view_dispatcher, 0);\n    view_dispatcher_free(view_dispatcher);\n    view_stack_free(view_stack);\n    view_free(text_box_switch_view);\n    text_box_free(instance.text_box);\n\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/uart_echo/application.fam",
    "content": "App(\n    appid=\"uart_echo\",\n    name=\"UART Echo\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"uart_echo_app\",\n    requires=[\"gui\"],\n    stack_size=2 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/uart_echo/uart_echo.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/elements.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/dialog_ex.h>\n\n#include <lib/toolbox/strint.h>\n\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\n#define TAG \"UartEcho\"\n\n#define LINES_ON_SCREEN   6\n#define COLUMNS_ON_SCREEN 21\n#define DEFAULT_BAUD_RATE 230400\n#define DEFAULT_DATA_BITS FuriHalSerialDataBits8\n#define DEFAULT_PARITY    FuriHalSerialParityNone\n#define DEFAULT_STOP_BITS FuriHalSerialStopBits1\n\ntypedef struct UartDumpModel UartDumpModel;\n\ntypedef struct {\n    Gui* gui;\n    NotificationApp* notification;\n    ViewDispatcher* view_dispatcher;\n    View* view;\n    FuriThread* worker_thread;\n    FuriStreamBuffer* rx_stream;\n    FuriHalSerialHandle* serial_handle;\n} UartEchoApp;\n\ntypedef struct {\n    FuriString* text;\n} ListElement;\n\nstruct UartDumpModel {\n    ListElement* list[LINES_ON_SCREEN];\n    uint8_t line;\n\n    char last_char;\n    bool escape;\n};\n\ntypedef enum {\n    WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event\n    WorkerEventStop = (1 << 1),\n    WorkerEventRxData = (1 << 2),\n    WorkerEventRxIdle = (1 << 3),\n    WorkerEventRxOverrunError = (1 << 4),\n    WorkerEventRxFramingError = (1 << 5),\n    WorkerEventRxNoiseError = (1 << 6),\n    WorkerEventRxParityError = (1 << 7),\n} WorkerEventFlags;\n\n#define WORKER_EVENTS_MASK                                                                 \\\n    (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \\\n     WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventRxParityError)\n\nconst NotificationSequence sequence_notification = {\n    &message_display_backlight_on,\n    &message_green_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_error = {\n    &message_display_backlight_on,\n    &message_red_255,\n    &message_delay_10,\n    NULL,\n};\n\nstatic void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {\n    UartDumpModel* model = _model;\n\n    // Prepare canvas\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontKeyboard);\n\n    for(size_t i = 0; i < LINES_ON_SCREEN; i++) {\n        canvas_draw_str(\n            canvas,\n            0,\n            (i + 1) * (canvas_current_font_height(canvas) - 1),\n            furi_string_get_cstr(model->list[i]->text));\n\n        if(i == model->line) {\n            uint8_t width =\n                canvas_string_width(canvas, furi_string_get_cstr(model->list[i]->text));\n\n            canvas_draw_box(\n                canvas,\n                width,\n                (i) * (canvas_current_font_height(canvas) - 1) + 2,\n                2,\n                canvas_current_font_height(canvas) - 2);\n        }\n    }\n}\n\nstatic bool uart_echo_view_input_callback(InputEvent* event, void* context) {\n    UNUSED(event);\n    UNUSED(context);\n    return false;\n}\n\nstatic uint32_t uart_echo_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic void\n    uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {\n    furi_assert(context);\n    UNUSED(handle);\n    UartEchoApp* app = context;\n    volatile FuriHalSerialRxEvent event_copy = event;\n    UNUSED(event_copy);\n\n    WorkerEventFlags flag = 0;\n\n    if(event & FuriHalSerialRxEventData) {\n        uint8_t data = furi_hal_serial_async_rx(handle);\n        furi_stream_buffer_send(app->rx_stream, &data, 1, 0);\n        flag |= WorkerEventRxData;\n    }\n\n    if(event & FuriHalSerialRxEventIdle) {\n        //idle line detected, packet transmission may have ended\n        flag |= WorkerEventRxIdle;\n    }\n\n    //error detected\n    if(event & FuriHalSerialRxEventFrameError) {\n        flag |= WorkerEventRxFramingError;\n    }\n    if(event & FuriHalSerialRxEventNoiseError) {\n        flag |= WorkerEventRxNoiseError;\n    }\n    if(event & FuriHalSerialRxEventOverrunError) {\n        flag |= WorkerEventRxOverrunError;\n    }\n    if(event & FuriHalSerialRxEventParityError) {\n        flag |= WorkerEventRxParityError;\n    }\n\n    furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag);\n}\n\nstatic void uart_echo_push_to_list(UartDumpModel* model, const char data) {\n    if(model->escape) {\n        // escape code end with letter\n        if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z')) {\n            model->escape = false;\n        }\n    } else if(data == '[' && model->last_char == '\\e') {\n        // \"Esc[\" is a escape code\n        model->escape = true;\n    } else if((data >= ' ' && data <= '~') || (data == '\\n' || data == '\\r')) {\n        bool new_string_needed = false;\n        if(furi_string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {\n            new_string_needed = true;\n        } else if(data == '\\n' || data == '\\r') {\n            // pack line breaks\n            if(model->last_char != '\\n' && model->last_char != '\\r') {\n                new_string_needed = true;\n            }\n        }\n\n        if(new_string_needed) {\n            if((model->line + 1) < LINES_ON_SCREEN) {\n                model->line += 1;\n            } else {\n                ListElement* first = model->list[0];\n\n                for(size_t i = 1; i < LINES_ON_SCREEN; i++) {\n                    model->list[i - 1] = model->list[i];\n                }\n\n                furi_string_reset(first->text);\n                model->list[model->line] = first;\n            }\n        }\n\n        if(data != '\\n' && data != '\\r') {\n            furi_string_push_back(model->list[model->line]->text, data);\n        }\n    }\n    model->last_char = data;\n}\n\nstatic int32_t uart_echo_worker(void* context) {\n    furi_assert(context);\n    UartEchoApp* app = context;\n\n    while(1) {\n        uint32_t events =\n            furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);\n        furi_check((events & FuriFlagError) == 0);\n\n        if(events & WorkerEventStop) break;\n        if(events & WorkerEventRxData) {\n            size_t length = 0;\n            do {\n                uint8_t data[64];\n                length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0);\n                if(length > 0) {\n                    furi_hal_serial_tx(app->serial_handle, data, length);\n                    with_view_model(\n                        app->view,\n                        UartDumpModel * model,\n                        {\n                            for(size_t i = 0; i < length; i++) {\n                                uart_echo_push_to_list(model, data[i]);\n                            }\n                        },\n                        false);\n                }\n            } while(length > 0);\n\n            notification_message(app->notification, &sequence_notification);\n            with_view_model(app->view, UartDumpModel * model, { UNUSED(model); }, true);\n        }\n\n        if(events & WorkerEventRxIdle) {\n            furi_hal_serial_tx(app->serial_handle, (uint8_t*)\"\\r\\nDetect IDLE\\r\\n\", 15);\n        }\n\n        if(events &\n           (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) {\n            if(events & WorkerEventRxOverrunError) {\n                furi_hal_serial_tx(app->serial_handle, (uint8_t*)\"\\r\\nDetect ORE\\r\\n\", 14);\n            }\n            if(events & WorkerEventRxFramingError) {\n                furi_hal_serial_tx(app->serial_handle, (uint8_t*)\"\\r\\nDetect FE\\r\\n\", 13);\n            }\n            if(events & WorkerEventRxNoiseError) {\n                furi_hal_serial_tx(app->serial_handle, (uint8_t*)\"\\r\\nDetect NE\\r\\n\", 13);\n            }\n            if(events & WorkerEventRxParityError) {\n                furi_hal_serial_tx(app->serial_handle, (uint8_t*)\"\\r\\nDetect PE\\r\\n\", 13);\n            }\n            notification_message(app->notification, &sequence_error);\n        }\n    }\n\n    return 0;\n}\n\nstatic UartEchoApp* uart_echo_app_alloc(\n    uint32_t baudrate,\n    FuriHalSerialDataBits data_bits,\n    FuriHalSerialParity parity,\n    FuriHalSerialStopBits stop_bits) {\n    UartEchoApp* app = malloc(sizeof(UartEchoApp));\n\n    app->rx_stream = furi_stream_buffer_alloc(2048, 1);\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n    app->notification = furi_record_open(RECORD_NOTIFICATION);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->view = view_alloc();\n    view_set_draw_callback(app->view, uart_echo_view_draw_callback);\n    view_set_input_callback(app->view, uart_echo_view_input_callback);\n    view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel));\n    with_view_model(\n        app->view,\n        UartDumpModel * model,\n        {\n            for(size_t i = 0; i < LINES_ON_SCREEN; i++) {\n                model->line = 0;\n                model->escape = false;\n                model->list[i] = malloc(sizeof(ListElement));\n                model->list[i]->text = furi_string_alloc();\n            }\n        },\n        true);\n\n    view_set_previous_callback(app->view, uart_echo_exit);\n    view_dispatcher_add_view(app->view_dispatcher, 0, app->view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, 0);\n\n    app->worker_thread = furi_thread_alloc_ex(\"UsbUartWorker\", 1024, uart_echo_worker, app);\n    furi_thread_start(app->worker_thread);\n\n    // Enable uart listener\n    app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);\n    furi_check(app->serial_handle);\n    furi_hal_serial_init(app->serial_handle, baudrate);\n    furi_hal_serial_configure_framing(app->serial_handle, data_bits, parity, stop_bits);\n\n    furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true);\n\n    return app;\n}\n\nstatic void uart_echo_app_free(UartEchoApp* app) {\n    furi_assert(app);\n\n    furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);\n    furi_thread_join(app->worker_thread);\n    furi_thread_free(app->worker_thread);\n\n    furi_hal_serial_deinit(app->serial_handle);\n    furi_hal_serial_control_release(app->serial_handle);\n\n    // Free views\n    view_dispatcher_remove_view(app->view_dispatcher, 0);\n\n    with_view_model(\n        app->view,\n        UartDumpModel * model,\n        {\n            for(size_t i = 0; i < LINES_ON_SCREEN; i++) {\n                furi_string_free(model->list[i]->text);\n                free(model->list[i]);\n            }\n        },\n        true);\n    view_free(app->view);\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Close gui record\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    app->gui = NULL;\n\n    furi_stream_buffer_free(app->rx_stream);\n\n    // Free rest\n    free(app);\n}\n\n// silences \"same-assignment\" false positives in the arg parser below\n// -V::1048\n\nint32_t uart_echo_app(void* p) {\n    uint32_t baudrate = DEFAULT_BAUD_RATE;\n    FuriHalSerialDataBits data_bits = DEFAULT_DATA_BITS;\n    FuriHalSerialParity parity = DEFAULT_PARITY;\n    FuriHalSerialStopBits stop_bits = DEFAULT_STOP_BITS;\n\n    if(p) {\n        // parse argument\n        char* parse_ptr = p;\n        bool parse_success = false;\n\n        do {\n            if(strint_to_uint32(parse_ptr, &parse_ptr, &baudrate, 10) != StrintParseNoError) break;\n\n            if(*(parse_ptr++) != '_') break;\n\n            uint16_t data_bits_int;\n            if(strint_to_uint16(parse_ptr, &parse_ptr, &data_bits_int, 10) != StrintParseNoError)\n                break;\n            if(data_bits_int == 6)\n                data_bits = FuriHalSerialDataBits6;\n            else if(data_bits_int == 7)\n                data_bits = FuriHalSerialDataBits7;\n            else if(data_bits_int == 8)\n                data_bits = FuriHalSerialDataBits8;\n            else if(data_bits_int == 9)\n                data_bits = FuriHalSerialDataBits9;\n            else\n                break;\n\n            char parity_char = *(parse_ptr++);\n            if(parity_char == 'N')\n                parity = FuriHalSerialParityNone;\n            else if(parity_char == 'E')\n                parity = FuriHalSerialParityEven;\n            else if(parity_char == 'O')\n                parity = FuriHalSerialParityOdd;\n            else\n                break;\n\n            uint16_t stop_bits_int;\n            if(strint_to_uint16(parse_ptr, &parse_ptr, &stop_bits_int, 10) != StrintParseNoError)\n                break;\n            if(stop_bits_int == 5)\n                stop_bits = FuriHalSerialStopBits0_5;\n            else if(stop_bits_int == 1)\n                stop_bits = FuriHalSerialStopBits1;\n            else if(stop_bits_int == 15)\n                stop_bits = FuriHalSerialStopBits1_5;\n            else if(stop_bits_int == 2)\n                stop_bits = FuriHalSerialStopBits2;\n            else\n                break;\n\n            parse_success = true;\n        } while(0);\n\n        if(!parse_success) {\n            FURI_LOG_I(\n                TAG,\n                \"Couldn't parse baud rate and framing (%s). Applying defaults (%d_8N1)\",\n                (const char*)p,\n                DEFAULT_BAUD_RATE);\n        }\n    }\n\n    UartEchoApp* app = uart_echo_app_alloc(baudrate, data_bits, parity, stop_bits);\n    view_dispatcher_run(app->view_dispatcher);\n    uart_echo_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/application.fam",
    "content": "App(\n    appid=\"unit_tests\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"unit_tests_on_system_start\",\n    sources=[\"unit_tests.c\", \"test_runner.c\", \"unit_test_api_table.cpp\"],\n    cdefines=[\"APP_UNIT_TESTS\"],\n    requires=[\"system_settings\", \"cli_subghz\"],\n    provides=[\"delay_test\"],\n    resources=\"resources\",\n    order=30,\n)\n\nApp(\n    appid=\"delay_test\",\n    name=\"Delay Test\",\n    sources=[\"tests/common/*.c\", \"tests/rpc/*.c\"],\n    apptype=FlipperAppType.SYSTEM,\n    entry_point=\"delay_test_app\",\n    stack_size=1 * 1024,\n    requires=[\"unit_tests\"],\n    order=30,\n)\n\nApp(\n    appid=\"test_varint\",\n    sources=[\"tests/common/*.c\", \"tests/varint/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_furi\",\n    sources=[\"tests/common/*.c\", \"tests/furi/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_furi_hal\",\n    sources=[\"tests/common/*.c\", \"tests/furi_hal/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_furi_hal_crypto\",\n    sources=[\"tests/common/*.c\", \"tests/furi_hal_crypto/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_furi_string\",\n    sources=[\"tests/common/*.c\", \"tests/furi_string/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_storage\",\n    sources=[\"tests/common/*.c\", \"tests/storage/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_stream\",\n    sources=[\"tests/common/*.c\", \"tests/stream/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_dirwalk\",\n    sources=[\"tests/common/*.c\", \"tests/dirwalk/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_manifest\",\n    sources=[\"tests/common/*.c\", \"tests/manifest/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_flipper_format\",\n    sources=[\"tests/common/*.c\", \"tests/flipper_format/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_flipper_format_string\",\n    sources=[\"tests/common/*.c\", \"tests/flipper_format_string/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_rpc\",\n    sources=[\"tests/common/*.c\", \"tests/rpc/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_subghz\",\n    sources=[\"tests/common/*.c\", \"tests/subghz/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_infrared\",\n    sources=[\"tests/common/*.c\", \"tests/infrared/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n    fap_libs=[\"infrared\"],\n)\n\nApp(\n    appid=\"test_nfc\",\n    sources=[\"tests/common/*.c\", \"tests/nfc/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_power\",\n    sources=[\"tests/common/*.c\", \"tests/power/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_protocol_dict\",\n    sources=[\"tests/common/*.c\", \"tests/protocol_dict/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_lfrfid\",\n    sources=[\"tests/common/*.c\", \"tests/lfrfid/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_bit_lib\",\n    sources=[\"tests/common/*.c\", \"tests/bit_lib/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_datetime\",\n    sources=[\"tests/common/*.c\", \"tests/datetime/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_float_tools\",\n    sources=[\"tests/common/*.c\", \"tests/float_tools/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_bt\",\n    sources=[\"tests/common/*.c\", \"tests/bt/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_dialogs_file_browser_options\",\n    sources=[\"tests/common/*.c\", \"tests/dialogs_file_browser_options/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_expansion\",\n    sources=[\"tests/common/*.c\", \"tests/expansion/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_compress\",\n    sources=[\"tests/common/*.c\", \"tests/compress/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_js\",\n    sources=[\"tests/common/*.c\", \"tests/js/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\", \"js_app\"],\n)\n\nApp(\n    appid=\"test_strint\",\n    sources=[\"tests/common/*.c\", \"tests/strint/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_pipe\",\n    sources=[\"tests/common/*.c\", \"tests/pipe/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\nApp(\n    appid=\"test_args\",\n    sources=[\"tests/common/*.c\", \"tests/args/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n\n\nApp(\n    appid=\"test_notification\",\n    sources=[\"tests/common/*.c\", \"tests/notification/*.c\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"get_api\",\n    requires=[\"unit_tests\"],\n)\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/Manifest_test",
    "content": "V:0\nT:1672935435\nD:infrared\nD:nfc\nD:subghz\nF:4bff70f2a2ae771f81de5cfb090b3d74:3952:infrared/test_kaseikyo.irtest\nF:8556d32d7c54e66771d9da78d007d379:21463:infrared/test_nec.irtest\nF:860c0c475573878842180a6cb50c85c7:2012:infrared/test_nec42.irtest\nF:2b3cbf3fe7d3642190dfb8362dcc0ed6:3522:infrared/test_nec42ext.irtest\nF:c74bbd7f885ab8fbc3b3363598041bc1:18976:infrared/test_necext.irtest\nF:cab5e604abcb233bcb27903baec24462:7460:infrared/test_rc5.irtest\nF:3d22b3ec2531bb8f4842c9c0c6a8d97c:547:infrared/test_rc5x.irtest\nF:c9cb9fa4decbdd077741acb845f21343:8608:infrared/test_rc6.irtest\nF:97de943385bc6ad1c4a58fc4fedb5244:16975:infrared/test_samsung32.irtest\nF:4eb36c62d4f2e737a3e4a64b5ff0a8e7:41623:infrared/test_sirc.irtest\nF:e4ec3299cbe1f528fb1b9b45aac53556:4182:nfc/nfc_nfca_signal_long.nfc\nF:af4d10974834c2703ad29e859eea78c2:1020:nfc/nfc_nfca_signal_short.nfc\nF:224d12457a26774d8d2aa0d4b3a15652:160:subghz/ansonic.sub\nF:ce9fc98dc01230387a340332316774f1:13642:subghz/ansonic_raw.sub\nF:f958927b656d0804036c28b4a31ff856:157:subghz/bett.sub\nF:b4b17b2603fa3a144dbea4d9ede9f61d:5913:subghz/bett_raw.sub\nF:370a0c62be967b420da5e60ffcdc078b:157:subghz/came.sub\nF:0156915c656d8c038c6d555d34349a36:6877:subghz/came_atomo_raw.sub\nF:111a8b796661f3cbd6f49f756cf91107:8614:subghz/came_raw.sub\nF:2101b0a5a72c87f9dce77223b2885aa7:162:subghz/came_twee.sub\nF:c608b78b8e4646eeb94db37644623254:10924:subghz/came_twee_raw.sub\nF:c4a55acddb68fc3111d592c9292022a8:21703:subghz/cenmax_raw.sub\nF:51d6bd600345954b9c84a5bc6e999313:159:subghz/clemsa.sub\nF:14fa0d5931a32674bfb2ddf288f3842b:21499:subghz/clemsa_raw.sub\nF:f38b6dfa0920199200887b2cd5c0a385:161:subghz/doitrand.sub\nF:c7e53da8e3588a2c0721aa794699ccd4:24292:subghz/doitrand_raw.sub\nF:cc73b6f4d05bfe30c67a0d18b63e58d9:159:subghz/doorhan.sub\nF:22fec89c5cc43504ad4391e61e12c7e0:10457:subghz/doorhan_raw.sub\nF:3a97d8bd32ddaff42932b4c3033ee2d2:12732:subghz/faac_slh_raw.sub\nF:06d3226f5330665f48d41c49e34fed15:159:subghz/gate_tx.sub\nF:8b150a8d38ac7c4f7063ee0d42050399:13827:subghz/gate_tx_raw.sub\nF:a7904e17b0c18c083ae1acbefc330c7a:159:subghz/holtek.sub\nF:72bb528255ef1c135cb3f436414897d3:173:subghz/holtek_ht12x.sub\nF:54ceacb8c156f9534fc7ee0a0911f4da:11380:subghz/holtek_ht12x_raw.sub\nF:4a9567c1543cf3e7bb5350b635d9076f:31238:subghz/holtek_raw.sub\nF:ca86c0d78364d704ff62b0698093d396:162:subghz/honeywell_wdb.sub\nF:f606548c935adc8d8bc804326ef67543:38415:subghz/honeywell_wdb_raw.sub\nF:20bba4b0aec006ced7e82513f9459e31:15532:subghz/hormann_hsm_raw.sub\nF:3392f2db6aa7777e937db619b86203bb:10637:subghz/ido_117_111_raw.sub\nF:cc5c7968527cc233ef11a08986e31bf2:167:subghz/intertechno_v3.sub\nF:70bceb941739260ab9f6162cfdeb0347:18211:subghz/intertechno_v3_raw.sub\nF:bc9a4622f3e22fd7f82eb3f26e61f59b:44952:subghz/kia_seed_raw.sub\nF:6b6e95fc70ea481dc6184d291466d16a:159:subghz/linear.sub\nF:77aaa9005db54c0357451ced081857b2:14619:subghz/linear_raw.sub\nF:1a618e21e6ffa9984d465012e704c450:161:subghz/magellan.sub\nF:bf43cb85d79e20644323d6acad87e028:5808:subghz/magellan_raw.sub\nF:4ef17320f936ee88e92582a9308b2faa:161:subghz/marantec.sub\nF:507a8413a1603ad348eea945123fb7cc:21155:subghz/marantec_raw.sub\nF:22b69dc490d5425488342b5c5a838d55:161:subghz/megacode.sub\nF:4f8fe9bef8bdd9c52f3f77e829f8986f:6205:subghz/megacode_raw.sub\nF:b39f62cb108c2fa9916e0a466596ab87:18655:subghz/nero_radio_raw.sub\nF:d0d70f8183032096805a41e1808c093b:26436:subghz/nero_sketch_raw.sub\nF:c6999bd0eefd0fccf34820e17bcbc8ba:161:subghz/nice_flo.sub\nF:9b1200600b9ec2a73166797ff243fbfc:3375:subghz/nice_flo_raw.sub\nF:b52bafb098282676d1c7163bfb0d6e73:8773:subghz/nice_flor_s_raw.sub\nF:e4df94dfdee2efadf2ed9a1e9664f8b2:163:subghz/phoenix_v2.sub\nF:8ec066976df93fba6335b3f6dc47014c:8548:subghz/phoenix_v2_raw.sub\nF:2b1192e4898aaf274caebbb493b9f96e:164:subghz/power_smart.sub\nF:8b8195cab1d9022fe38e802383fb923a:3648:subghz/power_smart_raw.sub\nF:1ccf1289533e0486a1d010d934ad7b06:170:subghz/princeton.sub\nF:8bccc506a61705ec429aecb879e5d7ce:7344:subghz/princeton_raw.sub\nF:0bda91d783e464165190c3b3d16666a7:38724:subghz/scher_khan_magic_code.sub\nF:116d7e1a532a0c9e00ffeee105f7138b:166:subghz/security_pls_1_0.sub\nF:441fc7fc6fa11ce0068fde3f6145177b:69413:subghz/security_pls_1_0_raw.sub\nF:e5e33c24c5e55f592ca892b5aa8fa31f:208:subghz/security_pls_2_0.sub\nF:2614f0aef367042f8623719d765bf2c0:62287:subghz/security_pls_2_0_raw.sub\nF:8eb533544c4c02986800c90e935184ff:168:subghz/smc5326.sub\nF:fc67a4fe7e0b3bc81a1c8da8caca7658:4750:subghz/smc5326_raw.sub\nF:24196a4c4af1eb03404a2ee434c864bf:4096:subghz/somfy_keytis_raw.sub\nF:6a5ece145a5694e543d99bf1b970baf0:9741:subghz/somfy_telis_raw.sub\nF:0ad046bfa9ec872e92141a69bbf03d92:382605:subghz/test_random_raw.sub\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_kaseikyo.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 1B 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 1C 00 00 00\nrepeat: false\n#\nname: decoder_input3\ntype: raw\ndata: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415\n#\nname: decoder_expected3\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 70 01 00 00\nrepeat: false\n#\nname: decoder_input4\ntype: raw\ndata: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414\n#\nname: decoder_expected4\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 43 54 32 00\ncommand: 70 01 00 00\nrepeat: false\n#\nname: decoder_input5\ntype: raw\ndata: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434\n#\nname: decoder_expected5\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 43 54 32 00\ncommand: 1B 00 00 00\nrepeat: false\n#\nname: decoder_input6\ntype: raw\ndata: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412\n#\nname: decoder_expected6\ntype: parsed_array\ncount: 1\n#\nprotocol: Kaseikyo\naddress: 43 54 32 00\ncommand: 05 00 00 00\nrepeat: false\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 4\n#\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 1B 00 00 00\nrepeat: false\n#\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 70 01 00 00\nrepeat: false\n#\nprotocol: Kaseikyo\naddress: 43 54 32 00\ncommand: 05 00 00 00\nrepeat: false\n#\nprotocol: Kaseikyo\naddress: 43 54 32 00\ncommand: 1B 00 00 00\nrepeat: false\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_nec.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 2640671 9071 4445 601 497 578 500 604 501 603 502 581 496 615 498 606 499 584 493 610 1630 576 1640 601 1615 605 1638 581 1634 606 1610 610 1633 577 1639 601 504 580 498 604 501 603 500 582 496 607 498 606 499 585 485 610 1633 576 1640 596 1615 605 1638 582 1634 605 1610 609 1634 586 1630 600 40015 9077 2208 593 1457713 9076 4440 607 508 585 493 610 494 598 506 577 501 603 502 601 504 580 498 605 1638 582 1634 606 1610 610 1633 577 1639 600 1616 605 1638 582 1634 606 499 585 493 609 495 608 496 586 502 612 493 601 504 579 498 605 1638 582 1633 606 1610 610 1633 577 1639 602 1614 574 1668 582 1634 606 1415838 9080 4436 611 494 600 505 578 500 608 501 602 502 580 498 606 508 605 500 583 1633 608 1608 611 1631 578 1638 602 1614 606 1637 583 1633 607 1609 611 494 600 505 570 500 604 501 602 502 581 497 606 499 605 499 583 1633 617 1608 611 1631 579 1638 602\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 3\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 18372093 9030 4495 559 524 585 526 613 496 560 522 595 524 605 504 553 530 578 524 608 1614 581 1668 557 1665 581 1641 585 1664 551 1671 605 1616 578 1670 555 528 581 1668 553 526 582 528 612 498 559 524 585 526 604 507 552 1670 597 504 553 1667 608 1613 582 1667 559 1663 613 1608 586 1662 552 40067 9026 2219 579 3060767 9079 4445 606 505 554 530 578 532 608 502 555 528 581 530 610 500 588 495 614 1635 580 1641 604 1617 618 1621 584 1637 608 1612 612 1636 589 1637 602 507 581 1641 605 505 582 501 609 502 607 503 585 498 611 499 609 1612 613 498 612 1610 615 1633 561 1661 606 1615 609 1639 585 1636 610 40011 9072 2200 588 96480 9075 2198 560 96509 9047 2226 552 96517 9049 2224 555 96514 9042 2222 556 96512 9053 2220 558 96511 9045 2227 561 96507 9048 2225 554 96515 9061 2231 565 96522 9053 2219 559 96510 9044 2229 560 96508 9046 2226 562 96506 9027 2245 553 96511 9030 2243 555 96513 9031 2237 557 96512 9054 2219 559 570349 9027 4495 608 476 583 529 580 529 558 525 584 527 582 528 560 523 596 524 584 1636 578 1669 555 1667 578 1643 581 1666 558 1663 582 1639 586 1662 552 531 577 1670 554 1667 578 532 563 527 582 528 580 529 558 525 584 1665 561 523 586 525 584 1637 577 1670 554 1668 578 1642 582 1667 558 40062 9021 2233 585 172411 9020 4502 559 524 585 526 583 527 551 532 586 523 575 535 553 530 579 532 577 1643 581 1668 557 1664 581 1639 585 1664 552 1670 575 1637 579 1669 556 527 581 529 580 1642 584 536 581 528 560 523 585 524 584 526 552 1670 576 1645 579 530 578 1643 582 1667 558 1663 582 1639 586 1662 545 40068 9026 2220 578 226896 9054 4468 578 500 608 502 607 503 585 498 611 500 610 501 588 496 612 497 602 1610 615 1633 581 1640 606 1616 609 1639 585 1635 610 1612 614 1635 580 504 615 495 604 506 582 1639 606 503 585 499 610 501 609 502 587 1635 610 1610 614 1634 581 502 616 1632 582 1648 606 1615 610 1638 587 40033 9050 2195 614 249594 9020 4502 560 524 594 525 603 506 552 532 587 521 606 504 554 529 579 532 609 1612 582 1667 557 1654 612 1608 585 1663 552 1670 606 1615 579 1669 554 527 582 529 611 500 558 1663 612 497 560 523 585 524 598 505 552 1670 606 1614 580 1668 557 526 582 1665 558 1662 603 1618 587 1661 553 40067 9058 2187 621 97567 9054 4467 584 500 609 501 601 502 586 497 611 499 610 501 588 496 614 497 612 1609 615 1632 582 1640 606 1615 609 1639 586 1636 611 1611 614 1634 581 503 608 493 605 505 583 1639 607 503 586 498 611 500 609 501 588 1634 611 1609 615 1634 582 502 608 1641 553 1668 608 1613 581 1668 558 112307 9078 4443 608 502 606 504 584 498 610 501 608 502 587 497 612 498 610 499 589 1633 603 1619 607 1642 583 1638 607 1614 611 1638 597 1634 611 1610 615 495 603 506 581 502 607 1642 584 500 609 501 607 503 585 498 611 1637 588 1634 611 1610 615 495 603 1618 607 1641 584 1638 607 1605 611 112281 9076 4445 606 505 584 499 610 501 608 502 586 497 612 498 610 500 581 494 614 1635 581 1641 604 1617 608 1640 585 1637 609 1610 613 1636 589 1632 603 507 581 503 607 504 604 1617 608 502 607 503 585 498 611 500 609 1613 613 1636 590 1632 603 507 581 1641 605 1616 609 1640 585 1636 609 94207 9075 4446 605 506 582 501 607 502 606 504 584 500 610 501 608 502 587 497 611 1636 588 1633 612 1610 616 1633 583 1639 604 1615 610 1638 587 1635 610 500 588 495 606 496 602 1619 616 495 605 506 583 501 609 502 606 1614 610 1638 587 1635 611 499 590 1632 603 1618 607 1641 583 1638 608 103762 9076 4446 605 505 553 531 579 532 607 503 555 528 580 530 609 500 557 527 583 1666 559 1662 603 1609 587 1661 553 1669 608 1614 580 1659 557 1665 612 498 580 504 585 526 604 1617 607 503 606 504 584 499 610 500 609 1613 612 1636 588 1633 602 507 573 1641 605 1617 608 1640 585 1636 619 76134 9056 4466 585 525 604 506 552 532 577 533 606 504 553 529 579 531 608 501 556 1665 611 1611 584 1665 561 1661 605 1616 578 1671 555 1667 609 1612 582 528 611 499 559 525 584 1664 551 533 586 524 605 505 553 530 578 1670 555 1667 610 1612 582 528 611 1610 584 1664 551 1671 606 1616 578 112997 9073 4447 603 507 560 523 586 524 605 505 552 531 578 533 607 503 555 529 580 1668 548 1665 611 1611 584 1665 551 1671 615 1616 578 1670 555 1667 610 501 556 527 582 528 611 1609 584 518 604 506 551 533 586 524 606 1615 579 1669 555 1666 610 500 558 1664 612 1609 585 1663 551 1670 606 84870 9053 4470 582 529 611 500 559 525 583 526 602 507 560 523 586 525 574 536 543 1669 608 1614 580 1668 556 1665 620 1610 585 1664 550 1671 605 1616 578 533 608 503 555 529 580 1677 557 526 582 528 612 498 559 525 585 1664 551 1671 605 1615 578 531 608 1614 581 1668 558 1664 611 1609 585 76184 9025 4496 555 529 579 531 609 502 556 528 582 529 610 500 559 525 583 526 603 1619 586 1663 552 1669 606 1614 580 1668 556 1665 611 1610 584 1664 550 533 586 524 605 504 553 1669 608 503 555 529 580 530 609 501 557 1664 605 1609 585 1664 551 532 586 1661 553 1668 608 1614 581 1666 558 96145 9073 4449 612 499 559 524 584 526 603 506 552 532 587 523 606 504 584 499 570 1669 556 1666 610 1611 614 1634 580 1641 605 1617 608 1640 584 1636 609 501 587 497 612 498 611 1611 615 496 602 508 580 503 595 535 614 1627 617 1650 594 1646 604 502 586 1635 611 1618 614 1634 581 1641 604 86183 9080 4442 610 501 607 502 586 498 611 499 610 501 588 496 613 497 611 498 579 1642 604 1617 608 1641 583 1637 608 1613 611 1637 588 1634 611 1608 615 494 604 506 582 501 617 1641 584 499 610 493 608 502 586 497 612 1636 588 1633 602 1619 615 494 604 1617 608 1640 585 1637 608 1613 613 234570 9078 4437 607 503 606 505 584 500 608 501 614 502 585 497 611 499 610 509 588 1634 612 1609 616 1633 582 1639 606 1616 610 1639 587 1635 610 1611 614 1634 581 503 606 504 604 1617 608 502 607 503 585 498 611 500 609 501 597 1634 611 1610 615 495 603 1618 607 1642 584 1638 607 1614 611 112281 9076 4446 605 505 583 501 609 493 606 503 585 498 610 500 609 501 587 497 613 1636 579 1643 602 1618 606 1641 583 1639 607 1614 610 1638 554 1665 611 1611 584 526 603 507 551 1671 597 504 583 500 578 532 607 502 555 528 581 1668 556 1664 611 498 559 1663 604 1618 587 1662 553 1668 608 130332 9076 4446 615 496 605 507 582 502 608 503 605 512 584 499 609 501 608 502 586 1636 610 1611 613 1635 579 1641 604 1617 608 1641 584 1637 608 1613 611 1636 588 495 614 497 613 1609 616 494 604 506 590 500 608 502 607 503 585 1635 609 1613 612 498 610 1610 614 1634 581 1641 604 1617 608 886079 9020 4501 560 523 585 525 583 527 552 532 587 523 576 534 554 529 580 531 577 1644 582 1667 559 1663 582 1639 586 1663 553 1669 576 1645 581 1668 557 521 582 529 581 530 559 1663 582 527 551 532 587 523 575 535 553 1669 577 1644 581 1667 557 526 584 1665 560 1662 582 1637 588 1661 554 76188 9053 4469 583 528 560 523 586 524 585 525 552 531 578 533 576 534 554 528 581 1668 557 1665 581 1631 585 1664 551 1670 575 1646 579 1678 555 1666 579 531 558 1664 581 528 559 1662 584 527 552 532 588 523 575 535 553 1668 578 532 556 1666 580 531 567 1663 582 1639 585 1662 552 1670 575 76165 9054 4468 583 527 581 529 559 524 585 525 583 526 547 531 587 524 576 535 554 1668 577 1644 581 1667 558 1664 582 1640 586 1663 551 1670 576 1645 579 531 578 533 556 527 582 1666 559 525 583 526 582 528 560 523 586 1663 553 1669 576 1645 580 522 578 1642 582 1666 559 1663 582 1639 586 40034 9049 2224 585 114557 9051 4471 581 529 558 525 584 527 582 528 561 522 586 524 585 525 552 531 578 1670 555 1667 579 1643 577 1666 559 1662 583 1638 587 1662 563 1678 587 543 565 537 591 539 589 1651 592 537 591 539 569 533 595 535 593 1647 597 1670 563 1678 587 542 565 1675 589 1651 593 1675 569 1671 593 40045 9047 2225 553 114589 9029 4492 559 525 585 526 582 528 550 533 586 524 575 535 553 530 578 532 577 1645 581 1668 557 1664 581 1640 585 1664 552 1670 575 1646 579 1669 556 527 582 529 580 531 557 1663 582 528 560 523 586 524 585 525 552 1669 576 1645 580 1668 556 526 582 1667 559 1663 582 1639 593 1662 553 40067 9026 4495 556 528 581 529 579 531 557 526 575 527 581 528 561 523 585 525 584 1638 588 1661 554 1667 578 1644 582 1667 559 1663 582 1639 586 1663 552 530 578 1671 555 529 581 1668 556 526 582 529 581 529 559 524 584 1664 550 533 587 1662 553 530 578 1669 555 1667 578 1642 582 1668 559 58109 9049 4473 578 533 576 534 555 529 580 531 578 532 556 527 582 528 580 529 559 1663 583 1639 586 1662 553 1669 580 1644 581 1668 558 1664 581 1640 582 525 584 527 552 531 588 1670 554 529 579 531 578 532 556 527 582 1666 558 1663 582 1639 587 524 584 1636 578 1671 564 1676 588 1652 592 263241 9028 4503 557 526 582 528 581 529 559 523 594 526 584 527 552 532 588 527 575 1646 579 1670 556 1666 579 1642 583 1665 560 1662 584 1638 578 1671 554 529 580 1669 559 527 582 1667 559 525 584 526 582 528 560 523 586 1662 552 530 578 1671 555 529 580 1668 556 1665 581 1640 584 1664 551 40069 9025 2221 588\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 52\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 06 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 06 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 04 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 04 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0A 00 00 00\nrepeat: true\n#\nname: decoder_input3\ntype: raw\ndata: 200000 8862 4452 562 563 559 1681 563 1646 567 586 556 569 563 583 559 571 561 1675 559 565 567 1673 561 561 561 592 561 565 567 579 563 567 565 584 558 1652 561 592 561 561 561 1679 565 560 562 584 558 1659 564 585 557 566 566 1675 559 1649 564 589 564 1649 564 1668 566 565 567 1669 565 43470 8896 4432 561 561 561 1679 565 1648 565 581 561 568 564 586 567 558 564 1676 558 564 558 1681 563 563 559 587 566 565 567 582 561 564 558 595 558 1650 563 590 563 563 559 1674 560 570 562 587 566 1645 568 586 556 565 567 1672 562 1651 562 584 558 1658 566 1671 563 561 561 1679 565 200000 8881 4383 569 549 573 548 574 541 571 550 572 547 575 539 573 551 571 1651 573 545 567 554 568 548 574 1652 572 547 575 1645 568 1661 573 545 567 1657 567 554 568 547 575 1652 572 547 575 539 573 1657 567 550 572 545 577 1651 573 1648 576 545 567 1659 575 1645 568 555 567 1657 567 38995 8883 4369 573 543 569 552 570 549 573 541 571 553 569 548 574 543 569 1658 566 550 572 548 574 546 566 1653 571 553 569 1654 570 1654 570 551 571 1651 573 547 575 545 567 1653 571 552 570 547 575 1649 564 556 566 550 572 1655 569 1656 568 546 566 1664 570 1653 571 547 565 1663 571 200000 8987 4504 561 593 539 589 533 596 515 586 536 592 540 588 534 595 517 1713 541 1664 570 1686 558 596 515 587 535 593 539 1691 543 1689 565 588 513 1691 563 1668 617 1613 641 1615 567 587 535 593 519 610 512 590 542 1714 510 593 539 1691 563 591 510 1720 535 594 518 584 538 591 541 39546 8990 4501 565 590 542 586 536 593 508 593 539 589 543 585 537 592 509 1720 545 1660 615 1642 561 567 534 594 538 590 542 1688 535 1696 558 595 517 1687 567 1664 621 1635 619 1611 561 594 538 590 511 617 515 586 536 1721 513 589 543 1687 568 587 514 1691 563 590 511 591 541 587 535 200000 8986 4505 560 594 538 590 542 586 515 586 536 593 539 589 533 595 517 1714 540 587 535 594 518 1713 542 586 515 587 535 1722 543 1662 562 592 540 1664 570 585 537 591 541 1689 545 584 538 590 542 1688 536 593 539 589 512 590 542 586 536 1720 514 588 544 585 537 591 541 587 514 40671 8986 4505 560 594 538 590 542 586 515 587 535 593 539 589 533 595 516 1714 541 587 535 594 518 1712 542 586 515 587 535 1722 543 1662 561 592 540 1664 570 585 537 591 541 1689 545 584 538 590 542 1688 536 593 539 589 512 590 542 586 536 1720 514 588 544 585 537 591 541 587 514 200000 8990 4500 566 1692 562 1668 566 588 534 594 518 584 538 591 541 587 535 1669 565 589 543 1688 536 592 540 1691 563 1667 567 1664 621 1635 568 586 515 587 535 593 539 589 543 1662 562 592 540 588 534 594 518 585 537 591 541 587 514 587 535 594 538 590 542 586 515 586 536 593 539 39544 8993 4498 567 1690 564 1666 568 586 536 593 508 593 539 589 543 585 537 1668 566 588 544 1687 537 591 541 1690 564 1666 568 1663 561 1696 569 585 516 586 536 593 539 589 543 1661 562 592 540 588 534 594 517 584 538 591 541 587 514 587 535 593 539 589 543 585 516 586 536 592 540 200000 8894 4456 589 1676 589 571 582 574 589 571 582 1683 582 1677 588 1682 583 574 589 568 585 1682 583 1678 587 1680 585 574 589 565 588 575 588 1675 590 567 586 1681 584 571 582 1685 590 568 585 569 584 1685 590 567 586 1678 587 574 589 1672 582 578 585 1679 586 1674 591 572 591 1672 582 39632 8912 4464 560 1703 562 598 565 594 559 594 559 1711 564 1698 567 1697 568 593 560 595 568 1698 567 1698 567 1693 561 602 561 596 567 590 563 1704 561 594 559 1707 568 591 562 1697 568 596 567 590 563 1700 565 596 567 1693 561 599 564 1701 564 589 564 1706 559 1704 561 597 566 1700 565 200000 9018 4500 565 1666 568 1689 565 588 513 1691 615 1616 618 1639 564 1667 567 587 535 594 538 563 538 590 542 586 536 593 508 593 539 589 543 1688 535 592 540 588 544 585 537 591 510 1694 560 1670 564 1693 562 1669 565 1692 542 1689 565 588 534 595 517 585 537 591 541 587 535 568 544 584 538 591 541 1663 560 1696 569 1662 562 1695 539 1692 614 1616 566 1691 563 1667 567 23184 9012 4505 560 1697 537 1693 561 593 508 1696 569 1662 562 1695 560 1671 563 591 541 587 535 594 518 584 538 590 542 586 515 613 509 593 539 1692 542 585 537 592 540 588 534 594 518 1687 567 1663 560 1697 568 1662 562 1695 539 1692 563 591 541 587 514 588 544 584 538 590 542 586 515 587 535 593 539 1666 568 1689 565 1665 569 1688 536 1695 570 1661 562 1694 561 1670 564 200000 8835 4446 537 562 539 562 539 1663 540 1667 536 1669 534 560 531 573 539 559 532 1672 531 570 531 564 537 563 538 561 540 1660 533 1677 536 561 540 557 534 567 534 1668 535 1672 531 1675 538 555 536 1674 539 1665 538 1666 537 1671 532 563 538 1669 534 566 535 558 533 1677 536 562 539 558 533 568 533 1668 535 566 535 1670 533 1667 536 568 533 1671 532 1672 531 1676 537 22779 8870 4437 535 92592 8861 4414 538\n#\nname: decoder_expected3\ntype: parsed_array\ncount: 15\n#\nprotocol: NECext\naddress: 86 02 00 00\ncommand: 49 B6 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 86 02 00 00\ncommand: 49 B6 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 68 00 00\ncommand: 49 B6 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 68 00 00\ncommand: 49 B6 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 63 00 00\ncommand: 0F 15 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 63 00 00\ncommand: 0F 15 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 64 00 00\ncommand: 49 08 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 80 64 00 00\ncommand: 49 08 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 08 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 71 00 00 00\ncommand: 4A 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 71 00 00 00\ncommand: 4A 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 7B 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 7B 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 1C 01 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: FE 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: FE 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: FF 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: NEC\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 01 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 01 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: FF 1F 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: FE 1F 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: FE 1F 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: FF 1F 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: FF 1F 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: FF 1F 00 00\ncommand: FF 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: AA 0A 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: NEC42\naddress: 55 15 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42ext.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 2000000 9000 4500 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 2000000 9000 4500 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 2000000 9000 4500 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 560 10000 560\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 1\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 01 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 01 00 00 00\ncommand: 00 80 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 80 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: FF 00 F0 03\ncommand: 0F F0 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: FE 00 F0 03\ncommand: 0F F0 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: FE 00 F0 03\ncommand: 0F 70 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: FF 00 F0 03\ncommand: 0F 70 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: FF 00 F0 03\ncommand: 0F F0 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: FF 00 F0 03\ncommand: 0F F0 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: AA AA AA 02\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: 55 55 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: 55 55 00 00\nrepeat: true\n#\nprotocol: NEC42ext\naddress: 55 55 55 01\ncommand: 55 55 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_necext.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 1915384 8967 4463 587 527 590 524 584 1647 590 524 583 531 586 527 590 524 583 1646 589 1640 586 527 590 524 583 1647 590 1640 587 1644 582 1647 589 524 583 531 586 1644 593 521 586 527 589 1641 586 528 589 525 592 521 585 1644 592 522 585 1645 592 1638 589 524 592 1637 588 1641 585 1645 592 41082 8965 2220 591 409594 8972 4458 591 523 584 530 587 1642 584 529 588 526 591 522 583 530 587 1643 584 1646 590 523 584 530 587 1643 584 1647 590 1640 586 1643 583 531 586 527 589 1641 586 528 589 524 593 1637 589 524 593 521 586 529 589 1641 585 528 589 1640 586 1644 592 521 585 1645 592 1638 588 1641 585 41088 8968 2218 582 95791 8971 2214 587 95787 8965 2220 591 95783 8971 2215 585 95787 8964 2221 590 95783 8971 2215 586 95788 8965 2220 591 95783 8969 2216 585 95789 8965 2220 590 95782 8970 2215 586 95788 9047 2139 591 95782 8970 2216 585 95788 8966 2220 591 95782 8972 2214 588 95786 8964 2222 590 95784 8971 2214 586 95787 8967 2218 583 95791 8964 2222 588 95785 8969 2217 584 333740 8967 4464 586 528 590 524 592 1637 589 525 592 521 586 528 589 525 593 1637 588 1640 585 528 589 525 592 1638 589 1641 586 1644 592 1638 588 525 592 522 585 1644 592 522 585 528 588 1642 585 529 588 526 591 522 585 1645 591 522 584 1646 591 1639 587 526 591 1639 588 1642 583 1646 590 41082 8963 2223 588 95785 8967 2219 591 95782 8968 2217 584 246369 8972 4459 591 523 583 530 587 1643 583 530 587 527 590 523 584 530 586 1643 583 1647 590 524 583 530 586 1643 583 1646 589 1641 586 1644 583 531 586 528 589 1640 585 528 588 525 592 1638 588 525 592 522 585 529 589 1641 584 529 588 1642 585 1645 591 522 585 1645 590 1639 587 1642 584 41090 8966 2220 591 95782 8966 2220 592 95782 8967 2218 583 165604 9017 4413 586 527 590 524 583 1647 589 523 582 531 586 528 589 525 593 1637 589 1640 585 527 588 525 592 1638 589 1641 585 1644 592 1638 588 525 591 523 585 1645 591 522 584 529 588 1642 584 530 587 527 591 523 584 1646 591 523 584 1646 591 1640 586 527 590 1640 586 1643 583 1646 589 41084 8972 2214 587 95787 8967 2219 581 95792 8971 2215 586 208929 9016 4414 584 529 588 526 591 1638 588 525 591 522 584 529 587 526 591 1639 587 1642 584 529 588 526 591 1638 587 1643 584 1646 591 1639 587 526 590 523 584 1646 590 524 583 530 587 1643 583 530 587 527 590 524 583 1647 590 524 583 1647 590 1640 586 527 589 1640 586 1644 592 1637 589 41085 8972 2214 587 95787 8964 2221 590 95784 8965 2221 590 167378 8969 4460 589 525 582 532 586 1644 592 521 586 528 589 524 592 522 585 1645 592 1638 589 525 592 522 585 1644 591 1639 588 1642 585 1645 591 522 585 529 587 1641 584 530 587 526 591 1639 588 526 591 522 584 530 587 1643 584 530 587 1642 584 1646 591 523 584 1647 590 1640 587 1643 583 41090 9017 2169 591 95781 8969 2216 585 95788 8964 2223 588 192781 8969 4461 589 525 592 522 586 1644 592 521 586 528 589 525 592 522 585 1644 592 1638 589 524 592 521 586 1645 591 1638 588 1642 585 1645 590 522 584 530 587 1642 584 530 587 526 591 1639 588 526 590 524 583 530 587 1643 584 530 587 1643 584 1646 590 524 583 1646 590 1640 587 1643 583 41090 8967 2219 591 95782 8970 2215 586 95788 8963 2222 589 179978 8967 4464 586 528 589 524 593 1637 588 525 592 522 585 529 589 525 592 1638 589 1641 585 529 588 526 591 1638 588 1641 585 1645 590 1639 587 527 590 523 584 1646 591 523 584 530 587 1643 583 530 586 527 590 524 583 1646 590 523 584 1646 589 1640 586 528 589 1640 586 1644 593 1638 589 41084 8971 2214 587 95787 8964 2221 589 95785 8966 2219 592 196616 8967 4463 585 527 590 525 592 1637 589 525 592 521 586 528 589 524 592 1638 588 1641 585 528 589 525 592 1637 589 1641 585 1645 591 1638 588 526 591 522 585 1645 591 522 584 530 587 1642 584 529 588 526 591 523 583 1645 590 523 584 1646 590 1639 587 527 590 1639 586 1644 583 1647 589 41084 8971 2214 587 95787 8964 2222 589 2112164 8969 4462 588 525 592 522 585 1645 591 523 584 529 588 526 591 523 584 1645 591 1639 587 527 590 524 583 1646 590 1639 587 1643 584 1673 563 524 583 531 586 1643 593 521 586 528 589 1641 585 528 589 525 592 521 585 1644 592 522 584 1645 591 1639 588 526 591 1639 588 1642 583 1646 590 41082 8962 2223 588 95785 8965 2220 591 95783 8968 2217 583 164778 8969 4462 588 525 591 522 585 1645 591 522 585 530 587 527 591 523 584 1646 591 1639 588 526 591 523 583 1646 590 1639 587 1643 584 1672 564 523 584 531 586 1643 583 530 587 527 590 1639 587 527 589 524 583 531 586 1644 583 531 586 1643 583 1647 590 525 582 1647 589 1639 586 1644 593 41081 8965 2220 590 95784 8968 2217 583 95790 8970 2215 586 161053 8963 4468 592 521 586 528 589 1641 585 529 588 526 591 522 585 529 588 1642 585 1645 591 523 584 530 587 1642 584 1646 591 1639 586 1669 557 531 586 527 590 1640 586 527 590 525 592 1638 589 525 592 522 585 528 588 1641 585 528 588 1642 584 1645 591 523 584 1645 591 1639 587 1643 583 41090 8964 2221 590 95784 8963 2222 589 95785 8965 2220 590 139334 8968 4463 587 527 590 523 584 1646 590 523 583 531 586 527 589 524 583 1647 590 1640 586 527 590 525 592 1637 589 1641 585 1644 592 1665 562 524 591 522 584 1645 591 523 584 529 588 1642 584 529 587 527 590 523 584 1646 590 523 584 1646 590 1639 587 527 589 1640 586 1644 592 1637 589 41085 8970 2217 584 95789 8972 2213 586 95787 8965 2221 590 141444 8969 4461 589 525 592 522 584 1644 591 522 585 529 588 526 591 522 585 1645 592 1638 587 526 591 523 584 1646 591 1639 588 1642 583 1672 564 523 584 530 587 1643 584 530 587 527 590 1640 587 527 590 524 584 530 587 1643 584 530 586 1644 583 1647 589 524 583 1647 590 1640 586 1645 592 41081 8964 2222 589 95784 8968 2218 583 95790 8971 2214 586 154119 8969 4462 588 526 591 522 585 1645 592 522 585 529 589 526 591 522 584 1646 591 1639 588 526 591 523 583 1645 590 1639 587 1642 584 1671 564 523 584 529 587 1643 583 530 587 527 590 1639 587 526 590 523 583 530 586 1643 583 529 586 1643 583 1646 590 524 583 1648 589 1641 586 1644 592 41081 8965 2220 590 95784 8969 2216 585 95790 8964 2221 590 147134 8966 4464 586 528 589 525 593 1637 589 524 593 522 585 529 589 525 592 1638 589 1641 586 528 589 525 591 1638 588 1641 585 1645 592 1664 561 525 592 523 584 1646 591 523 584 530 588 1642 585 526 587 527 590 523 584 1646 591 523 584 1646 591 1639 586 526 590 1640 587 1643 583 1646 590 41083 8963 2223 587 95786 8965 2221 590 95784 8968 2217 584 158330 8965 4465 585 529 588 526 590 1639 587 526 590 523 584 530 586 526 590 1639 587 1643 583 530 587 527 590 1639 587 1643 584 1647 590 1666 561 527 589 523 583 1646 590 523 583 531 586 1643 583 530 586 527 590 524 582 1646 590 525 582 1647 589 1640 586 528 589 1640 586 1644 592 1638 589 41085 8971 2214 586 95787 8962 2223 588 95786 8965 2222 589 206063 8962 4467 591 521 585 529 588 1642 585 529 588 525 591 522 584 530 587 1642 584 1646 591 523 584 529 588 1642 583 1646 590 1640 587 1668 558 530 587 526 589 1639 586 528 589 524 583 1647 589 524 593 521 585 528 589 1641 585 529 589 1641 585 1645 592 522 585 1644 591 1639 587 1642 584 41090 8965 2221 590 95784 8963 2223 588 95785 8964 2222 589 183026 8970 4460 590 524 583 531 586 1643 583 530 587 528 589 525 592 522 586 1644 592 1637 588 525 591 522 585 1645 592 1638 588 1641 585 1672 565 522 584 530 588 1642 584 529 588 526 591 1639 587 527 590 523 584 530 587 1642 584 530 587 1642 583 1647 590 524 583 1647 590 1640 587 1643 582 41090 8965 2221 590 95783 8970 2216 584 95789 8962 2223 587 184104 8964 4467 583 530 587 527 590 1640 587 527 590 523 582 531 586 528 589 1640 586 1644 593 521 586 528 589 1640 585 1644 592 1638 589 1667 558 528 589 526 591 1638 588 526 591 522 585 1645 591 522 585 530 587 527 591 1639 587 526 591 1639 587 1642 584 530 587 1643 583 1646 590 1639 587 41087 9020 2166 584 95790 8972 2213 587 95787 8963 2222 589 169833 8964 4465 583 529 587 527 590 1639 587 527 591 523 584 530 586 527 590 1640 587 1643 583 531 587 527 590 1640 586 1644 583 1647 590 1666 560 527 590 524 582 1647 589 525 592 521 586 1644 592 521 586 528 589 526 592 1638 588 525 592 1638 589 1641 585 528 589 1641 585 1645 591 1638 588 41086 8971 2215 585 95789 8964 2222 588 95785 8967 2218 583 185701 8971 4460 590 523 584 530 587 1642 584 530 587 527 590 524 583 531 586 1644 583 1647 590 524 592 521 585 1644 592 1638 589 1641 585 1671 565 522 586 529 588 1642 585 529 588 526 591 1638 588 525 590 523 584 530 587 1642 584 530 587 1642 584 1646 590 524 583 1646 590 1640 586 1643 583 41091 8965 2222 589 95784 8965 2221 589 95784 8968 2217 583 146332 8969 4461 669 445 591 522 584 1644 591 523 584 529 588 526 591 522 585 1645 591 1638 587 527 590 524 584 1646 590 1639 587 1642 583 1673 564 524 583 531 586 1643 583 531 586 528 589 1641 585 528 589 525 592 522 585 1644 591 521 585 1645 592 1638 588 525 592 1638 588 1641 584 1646 591 41083 8963 2222 589 95785 8966 2220 592 261924 8965 4465 585 529 588 525 592 1638 588 525 592 523 584 530 587 526 591 1639 587 1642 583 529 587 527 590 1639 587 1643 584 1646 590\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 108\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\nrepeat: true\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 01 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 01 00 00 00\ncommand: 00 80 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 80 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: FF FF 00 00\ncommand: FF FF 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: FE FF 00 00\ncommand: FF FF 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: FE FF 00 00\ncommand: FF 7F 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: FF FF 00 00\ncommand: FF 7F 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: FF FF 00 00\ncommand: FF FF 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: FF FF 00 00\ncommand: FF FF 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: AA AA 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: AA AA 00 00\ncommand: AA AA 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: 55 55 00 00\nrepeat: false\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: 55 55 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: 55 55 00 00\nrepeat: true\n#\nprotocol: NECext\naddress: 55 55 00 00\ncommand: 55 55 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_pioneer.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 25557 8437 4188 571 1538 595 1514 567 1542 570 1539 573 501 565 1544 568 506 571 1539 573 501 565 509 568 508 569 506 571 1538 574 501 565 1543 569 506 571 504 573 1536 566 1544 568 506 592 1517 574 1535 567 507 570 505 572 1537 575 500 566 508 600 1509 572 503 574 501 565 1544 568 1540 593\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 36 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 25609 8444 4152 564 1568 544 1565 547 1561 541 1568 544 530 547 1536 566 510 567 1567 545 529 548 526 540 535 542 508 569 1539 573 527 539 1543 569 506 571 504 573 1561 541 508 569 507 570 1538 564 1545 567 507 570 505 572 1537 565 509 568 1541 571 1538 564 511 566 509 568 1539 573 1537 596\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 32 00 00 00\nrepeat: false\n#\nname: decoder_input3\ntype: raw\ndata: 25582 8448 4176 571 1537 565 1544 568 1540 572 1537 575 500 566 1542 570 505 572 1537 575 500 566 508 569 506 571 504 573 1536 565 510 567 1542 570 504 573 1536 566 1543 569 506 571 504 573 1536 566 1543 569 506 571 504 573 502 575 500 566 1542 570 1539 573 502 575 500 566 1542 570 1540 572\n#\nname: decoder_expected3\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 33 00 00 00\nrepeat: false\n#\nname: decoder_input4\ntype: raw\ndata: 25594 8443 4181 568 1542 570 505 572 1538 564 1545 567 508 569 1540 572 504 573 1536 566 510 567 1542 571 505 572 504 573 1562 540 510 567 1543 570 506 571 504 573 503 574 501 565 510 567 509 568 507 570 1539 573 502 565 1545 568 1542 571 1539 573 1536 566 1544 569 1541 572 503 574 1537 565\n#\nname: decoder_expected4\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 40 00 00 00\nrepeat: false\n#\nname: decoder_input5\ntype: raw\ndata: 25556 8475 4150 597 1512 600 476 601 1509 603 1506 595 480 597 1512 600 476 601 1508 594 482 595 1515 597 478 599 477 600 1510 602 473 604 1506 595 480 597 1513 599 476 601 475 602 474 603 472 594 482 595 1514 598 477 600 476 601 1508 594 1516 596 1513 599 1510 602 1507 595 480 597 1513 599\n#\nname: decoder_expected5\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 41 00 00 00\nrepeat: false\n#\nname: decoder_input6\ntype: raw\ndata: 25567 8471 4155 604 1506 596 480 597 1513 599 1510 603 473 594 1515 597 478 599 1511 602 474 603 1507 595 480 597 479 598 1511 602 474 603 1506 596 480 597 478 599 1511 602 474 603 472 594 481 596 479 598 1512 601 474 603 1506 596 479 598 1511 602 1508 594 1515 598 1512 600 475 602 1507 595\n#\nname: decoder_expected6\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 42 00 00 00\nrepeat: false\n#\nname: decoder_input7\ntype: raw\ndata: 25584 8444 4180 569 1541 572 504 573 1536 566 1544 569 507 570 1539 574 501 566 1569 596 455 570 1540 573 502 575 501 565 1544 569 507 570 1565 548 503 574 1535 567 1543 570 506 571 529 548 502 565 511 566 1543 601 475 571 504 573 503 574 1536 566 1543 570 1539 574 1536 566 509 568 1541 572\n#\nname: decoder_expected7\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 43 00 00 00\nrepeat: false\n#\nname: decoder_input8\ntype: raw\ndata: 25562 8445 4181 568 1543 570 505 572 1538 575 1535 567 508 569 1539 573 503 595 1514 567 508 600 1509 572 503 595 481 596 1513 568 507 601 1508 573 502 575 501 566 1543 570 506 571 504 573 1536 566 510 567 508 600 475 571 1539 573 502 575 1534 568 1542 571 505 572 1537 575 1534 568 1542 571\n#\nname: decoder_expected8\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nname: decoder_input9\ntype: raw\ndata: 25558 8470 4152 597 1513 600 476 601 1508 594 1515 598 478 599 1509 593 483 594 1515 598 478 599 1510 603 474 592 482 595 1514 599 477 600 1509 593 483 594 481 596 1513 600 476 601 1508 594 1515 598 478 599 476 601 474 593 1517 596 479 598 1512 601 474 603 472 594 1515 598 1511 602 1508 594\n#\nname: decoder_expected9\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 1A 00 00 00\nrepeat: false\n#\nname: decoder_input10\ntype: raw\ndata: 25587 8442 4179 601 1507 595 481 565 1544 600 1509 593 482 595 1513 568 507 570 1539 594 481 565 1544 600 476 570 505 593 1516 597 479 598 1511 591 484 593 481 575 1534 600 476 570 1539 595 480 597 478 599 476 570 505 593 1517 564 511 597 1511 602 474 572 1537 597 1513 600 1509 572 1537 565\n#\nname: decoder_expected10\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nname: decoder_input11\ntype: raw\ndata: 25554 8474 4149 600 1510 603 472 594 1515 597 1512 601 475 602 1507 595 480 597 1513 599 475 602 1508 594 481 596 479 598 1512 601 474 603 1506 596 479 598 1512 601 1508 594 481 596 1513 599 476 601 474 603 472 594 481 596 480 597 478 599 1510 603 473 593 1515 597 1512 601 1509 603 1506 596\n#\nname: decoder_expected11\ntype: parsed_array\ncount: 1\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 0B 00 00 00\nrepeat: false\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 11\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 36 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 32 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AF 00 00 00\ncommand: 33 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 40 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 41 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 42 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 43 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 1A 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nprotocol: Pioneer\naddress: AD 00 00 00\ncommand: 0B 00 00 00\nrepeat: false\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nname: decoder_input3\ntype: raw\ndata: 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888\n#\nname: decoder_expected3\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nname: decoder_input4\ntype: raw\ndata: 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888\n#\nname: decoder_expected4\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nname: decoder_input5\ntype: raw\ndata: 27888 888 888 1776 1776 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888\n#\nname: decoder_expected5\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nname: decoder_input6\ntype: raw\ndata: 27888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888\n#\nname: decoder_expected6\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nname: decoder_input7\ntype: raw\ndata: 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 27888 888 888 1776 1776 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 27888 888 888 1776 1776 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888\n#\nname: decoder_expected7\ntype: parsed_array\ncount: 11\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: true\n#\nname: encoder_input1\ntype: parsed_array\ncount: 11\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 11 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: true\n#\nname: encoder_expected1\ntype: raw\ndata: 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 1776 888 27888 888 888 888 888 888 888 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 1776 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888 27888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 27888 888 888 1776 1776 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 27888 888 888 1776 1776 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 10 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 0A 00 00 00\ncommand: 2A 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 15 00 00 00\nrepeat: true\n#\nprotocol: RC5X\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 10 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5X\naddress: 01 00 00 00\ncommand: 20 00 00 00\nrepeat: true\n#\nprotocol: RC5X\naddress: 1F 00 00 00\ncommand: 3F 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 0A 00 00 00\ncommand: 2A 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 15 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: RC5X\naddress: 15 00 00 00\ncommand: 15 00 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5x.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 27888 1776 888 888 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5X\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nname: encoder_input1\ntype: parsed_array\ncount: 1\n#\nprotocol: RC5X\naddress: 13 00 00 00\ncommand: 10 00 00 00\nrepeat: false\n#\nname: encoder_expected1\ntype: raw\ndata: 27888 1776 888 888 1776 1776 888 888 1776 888 888 1776 1776 1776 888 888 888 888 888 888\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_rc6.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 888 888 444 444 888 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 888 888 888 444 444 888 888 888 444 444 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 888 888 444 444 888 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 888 888 888 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 888 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 888 888 888 444 444 888 888 888 444 444 444 444 444 444 444 444 444\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 6\n#\nprotocol: RC6\naddress: 94 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 94 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 95 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 95 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 8\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: true\n#\nname: encoder_input1\ntype: parsed_array\ncount: 8\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: true\n#\nname: encoder_expected1\ntype: raw\ndata: 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 888 27000 2666 889 444 888 444 444 444 444 444 888 1332 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444 27000 2666 889 444 888 444 444 444 444 1332 888 444 888 444 444 888 888 444 444 888 444 444 444 444 888 888 888 444 444 444 444 444 444 444 444 444\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 40\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 80 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 80 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 7F 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 7F 00 00 00\ncommand: FE 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FE 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 55 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 80 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 80 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 7F 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 7F 00 00 00\ncommand: FE 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FE 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 55 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A0 00 00 00\nrepeat: false\n#\nprotocol: RC6\naddress: 93 00 00 00\ncommand: A1 00 00 00\nrepeat: false\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_rca.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 1000000 3994 3969 552 1945 551 1945 552 1945 551 1945 552 946 551 947 550 1947 548 951 546 1953 542 979 518 1979 517 981 492 1006 491 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 2005 492 1006 492 2005 492 1006 492 2006 491 \n#\nname: decoder_expected1\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 54 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 1000000 4055 3941 605 1891 551 1947 550 1946 551 1946 551 947 551 947 550 1947 549 949 548 1951 545 1977 519 1978 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 981 517 1979 518 980 518 980 518 980 518 980 518 \n#\nname: decoder_expected2\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: F4 00 00 00\nrepeat: false\n#\nname: decoder_input3\ntype: raw\ndata: 1000000 4027 3970 551 1946 550 1946 551 1946 551 1946 551 946 551 947 550 1947 549 949 547 1951 545 1978 518 1979 492 1006 492 1007 491 1006 492 1006 492 1006 492 2006 491 2006 491 1006 492 2006 491 1007 491 1007 491 1006 492 2006 491 \n#\nname: decoder_expected3\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 74 00 00 00\nrepeat: false\n#\nname: decoder_input4\ntype: raw\ndata: 1000000 4021 3941 551 1946 550 1946 551 1946 551 1945 552 946 551 947 550 1947 549 950 547 1952 544 1977 519 979 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 980 518 \n#\nname: decoder_expected4\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: B4 00 00 00\nrepeat: false\n#\nname: decoder_input5\ntype: raw\ndata: 1000000 4022 3941 551 1946 551 1946 577 1919 578 1919 578 920 552 946 551 1946 550 947 550 1949 547 1952 544 978 520 979 519 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 1980 517 \n#\nname: decoder_expected5\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 34 00 00 00\nrepeat: false\n#\nname: decoder_input6\ntype: raw\ndata: 1000000 3995 3968 552 1944 552 1946 550 1946 550 1946 551 947 550 948 549 1947 549 1949 547 1952 544 1978 518 1979 492 2005 492 1006 492 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 1006 492 1006 492 1006 492 1006 492 1006 492 \n#\nname: decoder_expected6\ntype: parsed_array\ncount: 1\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: FC 00 00 00\nrepeat: false\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 4\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 74 00 00 00\nrepeat: false\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: B4 00 00 00\nrepeat: false\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 34 00 00 00\nrepeat: false\n#\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: FC 00 00 00\nrepeat: false\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_samsung32.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 3129767 4513 4483 565 530 586 1670 563 1664 588 1666 566 530 586 535 560 535 591 531 565 531 585 1669 563 1666 587 1640 593 531 566 530 587 536 559 562 564 531 585 537 558 1670 562 1665 587 534 561 534 592 530 566 529 587 1668 564 1664 589 533 563 533 594 1661 560 1667 565 1661 591 1664 558 46325 4517 4480 558 1668 595 679127 4511 4485 562 532 584 1671 561 1666 587 1668 564 532 585 537 559 537 589 533 562 533 593 1661 561 1667 586 1642 590 532 563 531 585 537 559 564 563 1666 567 528 588 535 613 483 593 530 586 536 559 536 590 1637 584 537 558 1669 594 1661 561 1667 586 1642 591 1664 559 1670 583 534 567 46429 4515 4480 558 1671 593 618571 4508 4488 560 561 566 1663 559 1667 583 1670 562 534 582 540 565 531 587 536 560 536 590 1664 558 1670 583 1644 588 535 561 534 592 530 566 557 610 1616 616 479 585 537 558 537 589 534 584 539 567 529 587 534 561 534 592 1663 559 1668 564 1664 588 1666 566 1661 581 1646 585 1669 563 46106 4511 4485 563 1664 589 514897 4514 4482 556 564 614 1616 565 1664 589 1666 557 538 588 534 562 534 592 529 566 529 587 1667 565 1663 590 1638 584 538 568 527 590 534 562 562 566 530 587 1642 590 532 563 530 586 537 589 533 563 533 583 539 566 1661 561 561 566 1663 559 1668 584 1671 563 1666 556 1671 591 1663 559 46210 4508 4488 560 1668 585 683922 4509 4487 561 560 565 1662 559 1669 585 1670 562 534 583 540 566 529 586 535 560 535 591 1663 559 1669 584 1644 588 534 561 533 593 529 556 567 561 1668 564 1664 590 534 563 532 584 538 557 537 589 534 562 534 593 530 566 555 561 1667 564 1662 589 1665 557 1671 581 1647 586 1669 564 46686 4514 4481 556 1672 592 1088255 4514 4481 557 564 562 1667 565 1663 591 1665 558 538 590 533 564 532 583 539 567 528 588 1666 566 1663 580 1648 585 537 559 537 589 533 562 560 618 478 589 535 562 1667 565 1662 588 531 565 531 585 537 558 536 590 1666 566 1661 592 530 566 530 586 1669 564 1664 558 1669 594 1662 560 46291 4510 4485 563 1663 589 97349 4510 4487 561 1666 586 97560 4513 4482 566 1662 591 97123 4510 4485 563 1664 588 97605 4508 4487 561 1666 586 97371 4518 4478 560 1668 595 97514 4518 4478 560 1667 586 97014 4515 4480 568 1660 593 96719 4516 4481 567 1660 593 97528 4515 4480 568 1659 593 97453 4510 4485 563 1665 587 97351 4518 4477 561 1668 585 97216 4511 4484 564 1664 589 97708 4518 4477 561 1667 586 96718 4516 4479 559 1669 594 97070 4515 4480 568 1660 593 97500 4511 4484 564 1662 590 97425 4515 4481 567 1660 593 97025 4515 4482 566 1660 592 96796 4509 4487 561 1666 587 97399 4512 4484 565 1662 591 97486 4516 4480 567 1658 594 97425 4515 4481 567 1659 593 97511 4510 4485 563 1664 650 96969 4511 4485 562 1665 588 97243 4512 4484 564 1663 590 97031 4519 4478 560 1666 586 97548 4514 4482 566 1661 591 97302 4515 4480 568 1659 593 97726 4510 4486 562 1665 588 97396 4514 4482 566 1661 592 97301 4515 4480 568 1661 593 97453 4518 4477 560 1667 585 97430 4518 4477 561 1665 587 97521 4511 4484 564 1664 589 97182 4512 4484 564 1663 590 97760 4516 4479 559 1668 595 97268 4516 4479 559 1668 595 97243 4512 4485 563 1663 589 97695 4510 4486 562 1664 588 374205 4513 4483 565 530 586 1669 564 1665 589 1667 567 529 587 535 560 535 591 531 565 530 585 1669 563 1664 588 1639 593 529 566 529 587 536 560 563 565 532 585 537 560 1669 563 1664 587 534 561 534 592 529 566 529 586 1668 564 1663 589 532 563 534 593 1661 562 1666 566 1662 591 1664 558 149343 4512 4483 565 530 586 1669 563 1664 588 1667 565 530 586 536 560 536 590 532 563 531 586 1670 563 1666 567 1660 592 530 565 529 586 537 558 563 563 532 584 538 568 1661 561 1665 587 535 560 535 592 532 565 531 587 1669 563 1665 587 534 561 534 592 1663 559 1668 564 1662 590 1666 566 116399 4514 4482 566 531 587 1669 564 1664 589 1666 566 529 587 535 561 535 592 531 565 529 587 1668 564 1664 558 1670 595 529 566 528 589 535 560 562 565 531 585 536 559 1668 564 1664 589 534 561 533 593 530 565 528 588 1668 564 1665 590 533 564 532 594 1661 561 1666 566 1661 592 1663 558 121946 4517 4478 559 537 590 1664 568 1660 593 1661 560 536 590 531 564 531 585 537 559 536 590 1665 568 1661 561 1666 587 537 559 529 591 531 564 558 558 537 588 533 562 1665 567 1659 593 530 565 529 587 536 561 535 592 1664 559 1671 593 530 566 528 587 1667 565 1662 558 1668 595 1660 561 46509 4516 4479 558 1668 594 88785 4512 4484 564 530 585 1669 563 1664 588 1666 566 530 587 536 560 535 592 532 565 531 585 1669 563 1665 557 1669 594 530 566 530 586 535 560 562 564 530 585 537 558 1669 563 1664 589 535 561 534 593 529 566 529 586 1668 564 1664 589 533 562 532 594 1661 561 1666 565 1662 591 1665 558 289651 4512 4483 564 531 586 1669 563 1665 588 1667 565 529 587 536 560 536 590 531 563 531 584 1670 562 1666 556 1671 592 529 566 531 586 536 561 562 564 532 585 537 558 1669 563 1665 588 535 561 536 590 530 565 531 585 1669 563 1664 587 534 561 533 593 1662 561 1669 564 1663 590 1665 567 46302 4509 4487 561 1667 585 97097 4513 4483 565 529 587 1668 564 1663 589 1666 567 529 587 536 560 535 591 530 565 529 587 1669 563 1664 558 1669 594 529 566 527 587 535 561 561 563 530 586 537 560 1669 563 1663 589 534 561 532 594 529 566 528 587 1668 564 1663 589 532 563 532 594 1660 561 1667 566 1661 592 1663 558 46311 4510 4485 562 1665 589 99001 4514 4481 566 528 587 1667 565 1662 589 1664 568 528 588 535 561 535 593 529 567 528 589 1668 564 1663 559 1668 595 528 567 527 589 534 562 561 565 530 586 536 560 1668 564 1663 590 533 563 532 595 524 567 527 588 1667 565 1662 590 532 563 531 595 1659 562 1666 566 1661 593 1664 559 300259 4514 4482 556 565 561 1668 564 1663 589 1664 556 539 588 535 561 535 643 478 568 530 587 1668 564 1664 589 1636 594 529 567 528 588 534 561 561 565 1662 560 536 590 532 564 531 586 537 589 532 564 533 584 538 568 528 588 1667 565 1662 560 1669 584 1670 562 1666 587 1641 591 1664 559 46271 4561 4435 562 1666 586 81421 4514 4482 556 565 561 1667 565 1662 589 1664 558 538 588 534 562 534 593 530 567 530 587 1669 564 1663 589 1637 594 529 567 528 588 534 561 560 566 1663 559 535 591 531 565 530 587 538 589 533 563 533 583 539 567 529 587 1667 565 1662 560 1669 584 1669 563 1666 587 1640 592 1663 560 46296 4516 4480 558 1669 594 80690 4508 4489 560 561 565 1663 558 1670 593 1660 561 534 592 530 565 530 586 537 560 536 591 1664 558 1670 583 1644 587 535 560 534 592 530 566 556 559 1669 563 532 584 538 558 537 588 534 582 540 567 530 588 535 562 535 591 1663 559 1669 564 1665 588 1666 566 1662 560 1668 585 1669 563 136483 4511 4485 563 531 585 1671 561 1666 587 1668 564 531 585 536 559 537 589 532 563 532 584 1670 562 1666 587 1642 591 531 566 531 585 536 559 563 563 1665 567 528 588 535 561 534 593 529 567 556 560 535 591 530 565 531 585 1670 563 1665 568 1660 593 1662 560 1668 564 1663 590 1666 566 129038 4511 4484 564 533 583 1670 562 1666 587 1668 565 532 586 537 559 536 591 532 564 532 594 1660 562 1666 566 1660 593 531 565 530 586 537 558 563 563 1664 568 528 589 535 562 533 595 530 567 556 560 535 591 531 565 531 584 1669 563 1664 567 1661 592 1662 559 1668 564 1663 590 1666 566 46187 4511 4486 562 1665 589 110663 4517 4478 558 536 590 1665 568 1660 593 1661 560 536 590 531 564 531 584 537 558 536 590 1665 567 1661 561 1666 587 536 560 535 591 532 564 559 557 1670 562 532 594 529 567 528 649 473 561 561 565 531 585 536 559 536 589 1665 567 1660 562 1666 587 1668 564 1663 558 1669 594 1661 561 143736 4517 4479 559 536 591 1664 558 1670 593 1661 559 536 590 531 564 531 585 536 559 537 589 1665 567 1661 561 1666 587 536 560 536 590 530 564 557 558 1669 562 533 593 528 568 530 586 534 561 560 566 530 586 536 560 536 591 1664 558 1671 562 1666 587 1667 565 1663 559 1668 594 1660 562 46234 4514 4482 566 1661 591 120661 4564 4432 565 530 586 1669 563 1665 587 1666 566 531 585 536 559 536 591 532 564 532 586 1670 563 1666 557 1666 592 530 565 530 586 536 560 562 563 1664 558 538 588 533 562 533 593 528 558 565 561 534 582 541 566 530 586 1670 563 1664 567 1660 593 1662 560 1668 564 1663 590 1665 567 46472 4513 4484 564 1664 588 125301 4511 4484 564 532 584 1670 562 1666 587 1668 565 531 586 537 560 537 591 532 565 533 584 1671 562 1666 566 1661 591 530 565 532 584 536 559 562 564 1665 567 529 587 534 563 535 593 529 568 556 561 535 592 531 565 531 585 1669 563 1665 567 1660 593 1663 559 1668 564 1664 589 1666 567 200253 4517 4479 558 562 615 1613 558 1670 592 1661 560 536 591 531 616 480 585 537 559 537 641 1613 568 1661 582 1646 586 536 559 535 591 532 564 558 558 1670 562 533 592 530 566 529 588 536 581 542 617 479 587 537 560 536 590 1665 557 1670 562 1666 587 1668 564 1664 558 1669 593 1662 561 46230 4570 4426 561 1667 586 115418 4514 4483 565 557 561 1669 563 1664 589 1666 566 529 587 534 561 534 592 530 566 530 586 1668 564 1664 589 1639 594 529 567 528 587 534 561 561 565 1663 559 535 590 532 563 532 584\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 78\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 81 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 81 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 02 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 02 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 03 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 03 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 01 00 00 00\nrepeat: true\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 26\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 01 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 01 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 80 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: FE 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: FE 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: FF 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: FF 00 00 00\ncommand: FF 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: AA 00 00 00\ncommand: AA 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\nprotocol: Samsung32\naddress: 55 00 00 00\ncommand: 55 00 00 00\nrepeat: true\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/infrared/test_sirc.irtest",
    "content": "Filetype: IR tests file\nVersion: 1\n#\nname: decoder_input1\ntype: raw\ndata: 1000000 2420 608 1194 608 596 604 1198 603 591 610 1192 609 596 605 599 601 593 607 597 604 590 610 594 606 1196 25957 2426 603 1199 603 591 610 1192 610 594 606 1196 606 599 603 591 609 595 606 598 602 592 609 596 605 1197 25960 2423 606 1196 606 599 602 1200 602 592 609 1193 609 596 606 599 602 592 609 595 605 600 601 593 608 1194 1000000 2422 607 1195 607 598 603 1199 604 590 610 1192 610 594 606 598 603 591 609 595 605 600 601 593 607 1195 25955 2418 610 1192 610 594 606 1196 606 599 602 1200 602 592 608 596 604 590 611 594 607 597 603 591 609 1193 25959 2424 604 1198 604 590 610 1192 610 594 606 1196 605 600 601 593 608 597 603 591 610 595 606 598 602 1200 1000000 2424 605 599 601 593 607 597 603 591 610 594 606 1196 606 1196 605 600 601 593 608 597 604 590 611 1191 26586 2425 604 590 611 593 607 598 603 591 610 595 606 1196 606 1196 606 599 602 592 608 596 604 590 611 1191 26586 2424 604 590 611 593 607 598 603 591 609 595 605 1197 605 1197 604 590 611 593 607 597 603 591 610 1192 1000000 2424 604 1198 604 590 611 1191 610 594 606 598 603 1199 602 1200 602 592 609 595 605 600 601 593 608 1194 25386 2419 610 1192 610 594 606 1196 607 597 603 591 610 1192 609 1193 610 594 606 598 602 592 609 595 605 1197 25385 2421 608 1194 608 596 605 1197 605 599 601 593 608 1194 608 1194 608 596 605 589 611 594 607 597 604 1198 1000000 2426 603 1199 602 1200 602 1200 602 592 608 1194 608 596 604 590 611 594 607 597 603 591 610 594 606 1196 605 600 601 593 608 596 604 590 610 594 607 1195 606 598 603 591 15078 2419 610 1192 610 1192 610 1192 610 594 606 1196 605 600 601 593 608 597 604 590 610 595 606 598 602 1200 602 592 608 597 604 590 611 594 607 597 603 1199 603 591 609 595 15075 2422 607 1195 607 1195 607 1195 607 597 604 1198 603 591 610 594 606 598 603 591 609 595 605 600 601 1191 611 594 607 597 603 591 610 594 606 598 602 1200 602 592 608 596 1000000 2422 607 1195 606 599 602 592 608 596 604 590 610 1192 610 594 606 599 602 592 608 596 604 590 610 1192 26585 2426 602 1200 602 592 608 596 604 590 611 594 607 1195 607 598 603 591 610 594 606 598 603 591 609 1193 26586 2425 604 1198 603 591 610 594 606 598 602 592 609 1193 608 597 605 600 601 593 607 597 604 590 610 1192 1000000 2418 610 594 606 598 603 1199 603 1199 603 1199 603 1199 603 1199 603 591 610 1192 610 594 606 1196 606 1196 606 1196 606 598 602 592 609 1193 609 1193 609 1193 609 595 605 599 11557 2418 611 594 607 598 603 1199 603 1199 603 1199 602 1200 602 1200 601 593 608 1194 607 597 604 1198 603 1199 603 1199 602 592 608 596 604 1198 603 1199 603 1199 603 591 609 595 11561 2424 604 590 610 594 607 1195 606 1196 606 1196 606 1196 606 1196 605 600 601 1191 611 594 607 1195 607 1195 607 1195 607 597 603 591 610 1192 610 1192 610 1192 610 594 606 598 1000000 2424 604 590 611 594 607 1195 607 1195 607 1195 607 1195 607 1195 606 598 603 1199 602 592 609 1193 608 1194 608 1194 608 596 604 590 611 1191 611 1191 611 1191 611 594 607 598 11559 2427 602 592 608 596 605 1197 604 1198 604 1198 604 1198 604 1198 604 590 610 1192 610 595 606 1196 606 1196 606 1196 606 599 603 591 609 1193 609 1193 609 1193 608 597 605 589 11567 2418 610 595 607 597 603 1199 603 1199 602 1200 602 1200 601 1201 601 593 608 1194 607 598 603 1199 603 1199 603 1199 603 591 609 595 605 1197 605 1197 605 1197 604 590 611 594 1000000 2421 608 597 604 590 610 1192 610 1192 609 1193 609 1193 609 1193 608 596 605 1197 604 590 610 1192 611 1191 610 1192 610 594 606 598 603 1199 603 1199 602 1200 602 592 608 596 11561 2424 604 590 610 594 606 1196 606 1196 606 1196 606 1196 605 1197 605 600 601 1201 601 593 607 1195 607 1195 606 1196 606 598 602 592 608 1194 608 1194 607 1195 607 597 603 591 11564 2421 607 597 604 590 610 1192 610 1192 610 1192 610 1192 610 1192 609 595 606 1196 606 598 602 1200 601 1201 601 1201 601 593 607 598 603 1199 603 1199 602 1200 602 592 608 596 1000000 2420 609 595 606 598 602 1200 602 1200 602 1200 602 1200 602 1200 602 592 608 1194 608 596 604 1198 603 1199 603 1199 603 591 610 594 606 1196 606 1196 606 1196 606 598 602 592 11565 2420 609 595 605 600 601 1201 601 1201 601 1201 601 1201 601 1201 601 593 607 1195 607 597 603 1199 603 1199 603 1199 603 591 609 595 605 1197 605 1197 605 1197 605 599 601 593 11563 2422 607 597 603 591 609 1193 609 1193 608 1194 608 1194 608 1194 582 623 603 1199 603 591 610 1202 599 1203 599 1203 599 595 581 623 577 1225 601 1201 601 1201 601 593 582 623 1000000 2425 602 1200 602 1200 602 592 608 1194 608 1194 607 1195 607 1195 607 597 603 1199 602 592 609 1193 608 1194 608 1194 607 597 603 601 575 1227 599 1203 599 1203 573 621 580 625 10931 2426 578 1224 578 1224 578 616 585 1217 585 1217 585 1217 585 1217 585 620 580 1222 580 624 577 1225 577 1225 577 1225 577 617 583 622 580 1222 580 1222 579 1223 579 625 576 618 10936 2421 583 1219 583 1219 582 622 579 1223 578 1224 578 1224 578 1224 578 616 584 1218 584 621 580 1222 579 1223 579 1223 579 625 576 618 583 1219 582 1220 582 1220 582 622 578 616 1000000 2419 584 620 580 1222 580 624 576 1226 576 1226 576 1226 576 1226 576 618 582 1220 582 622 579 1223 578 1224 579 1223 578 616 585 619 581 1221 581 1221 581 1221 580 624 576 618 11563 2422 582 622 579 1223 578 616 585 1217 585 1217 584 1218 584 1218 583 622 579 1223 579 625 575 1216 585 1217 585 1217 585 619 581 623 577 1225 577 1225 577 1225 576 618 583 621 11558 2427 577 617 584 1218 583 621 579 1223 579 1223 578 1224 578 1224 578 647 553 1249 553 651 549 1253 548 1254 549 1253 549 645 555 649 551 1251 551 1251 551 1251 550 654 546 648 1000000 2456 548 646 554 650 551 653 547 1255 547 1255 547 1255 547 1255 547 647 554 1248 554 650 551 1251 551 1251 551 1251 551 653 547 647 554 1248 554 1248 554 1248 553 651 550 644 12112 2449 555 649 551 654 547 647 554 1248 554 1248 553 1249 553 1249 553 651 549 1253 549 645 555 1247 555 1247 555 1247 554 650 550 655 547 1244 557 1224 578 1224 579 615 585 619 12139 2423 580 624 576 618 582 622 578 1224 578 1224 578 1224 578 1224 578 616 584 1218 584 620 580 1222 580 1222 581 1221 580 624 577 617 584 1218 584 1218 584 1218 584 620 581 623 1000000 2422 582 1220 581 623 578 616 584 1218 584 1218 584 1218 584 1218 584 620 580 1222 580 624 576 1226 577 1225 576 1226 576 618 583 622 579 1223 578 1224 577 1225 577 617 585 619 11559 2426 577 1225 577 617 583 621 579 1223 579 1223 578 1224 578 1224 578 616 584 1218 584 620 580 1222 580 1222 579 1223 579 625 575 650 550 1252 550 1252 549 1253 549 645 556 648 11531 2453 549 1253 548 646 555 649 551 1251 551 1251 550 1252 549 1253 549 645 556 1246 555 649 552 1219 582 1220 582 1220 582 622 578 616 585 1217 584 1218 584 1218 584 620 581 623 1000000 2428 576 618 582 1220 583 622 579 1223 578 1224 578 1224 578 1224 578 616 585 1217 585 619 582 1220 582 1220 582 1220 582 622 578 616 585 1217 585 1217 584 1218 584 620 581 623 11557 2427 577 617 584 1218 583 621 580 1222 580 1222 580 1222 580 1222 580 624 576 1246 555 649 552 1250 552 1250 551 1251 551 653 548 646 554 1248 555 1247 555 1247 555 649 551 653 11528 2447 556 648 552 1250 552 653 548 1254 548 1254 547 1245 557 1245 557 647 553 1249 552 652 549 1253 548 1254 548 1254 548 646 555 649 551 1251 551 1251 551 1251 551 643 557 647 1000000 2418 610 594 606 598 603 1199 603 1199 602 1200 602 1200 602 1200 602 592 608 1194 608 596 604 1198 604 1198 604 1198 603 591 610 594 606 1196 606 1196 605 1197 605 599 601 593 11563 2422 606 598 602 592 608 1194 608 1194 608 1194 607 1195 607 1195 607 597 603 1199 603 591 609 1193 609 1193 609 1193 608 596 605 599 601 1201 600 1191 611 1191 611 593 606 598 11558 2427 601 593 607 597 603 1199 603 1199 603 1199 602 1200 602 1200 601 593 607 1195 607 597 603 1199 603 1199 602 1200 602 592 608 596 604 1198 604 1198 604 1198 603 591 609 595 1000000 2424 605 1197 604 600 600 1191 610 1192 610 1192 610 1192 609 1193 609 595 605 1197 606 598 602 1200 602 1200 601 1201 601 593 607 597 603 1199 603 1199 603 1199 603 591 609 595 10937 2420 609 1193 609 595 605 1197 605 1197 605 1197 605 1197 605 1197 605 600 601 1201 601 593 608 1194 608 1194 608 1194 609 595 605 599 601 1201 601 1201 601 1201 601 593 608 596 10936 2420 608 1194 608 596 604 1198 605 1197 605 1197 605 1197 605 1197 605 600 601 1201 601 593 607 1195 608 1194 608 1194 608 596 604 600 600 1202 601 1201 601 1201 601 593 607 597 1000000 2420 609 1193 608 1194 608 596 579 625 600 1202 600 1202 600 1202 600 594 607 1195 607 597 603 1199 603 1199 602 1200 602 592 609 595 605 1197 605 1197 605 1197 604 600 600 594 11561 2423 605 1197 605 1197 605 599 601 593 607 1195 607 1195 607 1195 607 597 604 1198 603 591 610 1192 610 1192 610 1192 610 594 607 597 603 1199 603 1199 603 1199 603 591 610 594 11563 2421 608 1194 608 1194 608 596 605 599 601 1201 602 1200 602 1200 602 592 609 1193 609 595 605 1197 606 1196 606 1196 606 598 602 592 609 1193 609 1193 610 1192 610 594 607 597 1000000 2428 576 1226 600 1192 610 594 606 598 602 1200 602 592 609 595 605 600 601 593 607 597 603 591 609 1193 25955 2427 601 1190 610 1192 610 594 606 598 602 1200 601 593 607 597 603 591 610 594 606 598 602 592 608 1194 25957 2425 604 1198 603 1199 603 591 609 595 605 1197 605 599 601 593 607 598 603 591 610 594 606 598 602 1200 25952 2420 608 1194 608 1194 608 596 604 601 600 1191 610 594 606 598 603 591 609 595 605 599 601 593 608 1194 1000000 2421 607 597 603 1199 603 591 610 594 606 1196 605 600 576 618 583 621 604 601 575 619 606 598 603 1199 26576 2424 605 600 576 1226 601 593 608 596 604 1198 604 600 600 594 607 597 603 591 609 595 606 598 602 1200 26579 2420 583 621 605 1197 604 600 601 593 607 1195 607 597 604 590 610 594 607 597 603 591 610 594 606 1196 26584 2426 603 591 609 1193 609 595 605 599 601 1201 601 593 607 597 603 591 610 594 606 598 603 591 609 1193 1000000 2418 610 594 606 598 602 592 609 595 605 1197 605 1197 605 600 602 592 608 1194 608 596 605 1197 605 1197 604 1198 604 600 601 593 607 1195 607 1195 607 1195 607 597 603 591 13368 2419 610 594 606 598 602 592 608 596 605 1197 604 1198 604 600 601 593 607 1195 607 597 603 1199 603 1199 603 1199 603 591 609 595 605 1197 605 1197 605 1197 604 601 600 594 13362 2425 604 601 600 594 607 597 603 591 610 1192 610 1192 610 594 606 598 603 1199 602 592 609 1193 609 1193 608 1194 608 596 604 601 600 1191 610 1192 610 1192 610 594 607 597 1000000 2427 601 1201 601 593 582 623 603 1199 578 1224 603 1199 577 617 584 620 605 1197 605 600 601 1201 601 1201 601 1201 601 593 582 622 603 1199 603 1199 578 1224 603 591 610 594 12139 2423 605 1197 605 600 601 593 608 1194 608 1194 607 1195 607 597 604 600 601 1201 601 593 607 1195 608 1194 608 1194 583 622 604 601 600 1202 575 1227 601 1201 576 618 607 597 12137 2424 604 1198 604 590 610 594 607 1195 607 1195 607 1195 607 597 604 601 600 1202 600 594 607 1195 606 1196 581 1221 607 597 603 591 610 1202 600 1202 576 1226 602 592 609 595 1000000 2422 607 1195 607 597 603 591 585 619 606 1196 606 1196 581 624 577 617 609 1193 584 621 605 1197 604 1198 604 1198 604 590 610 595 581 1221 605 1197 605 1197 605 599 601 593 12763 2427 603 1199 603 591 609 595 605 599 577 1225 602 1200 601 593 583 622 604 1198 604 590 610 1192 610 1192 610 1192 610 594 606 598 603 1199 603 1199 603 1199 603 591 610 594 12763 2427 602 1200 601 593 608 596 604 600 600 1202 601 1201 601 593 607 597 604 1198 604 590 610 1192 610 1192 611 1191 610 594 607 598 578 1224 603 1199 603 1199 603 591 610 594 1000000 2422 607 597 603 591 610 1192 610 594 606 1196 580 1222 605 600 601 593 583 1219 608 596 579 1223 604 1198 604 1198 604 590 610 594 606 1196 606 1196 606 1196 606 599 602 592 12765 2425 603 591 610 594 606 1196 606 598 602 1200 601 1201 601 593 582 623 604 1198 604 590 610 1192 610 1192 609 1193 609 596 605 599 601 1201 601 1201 601 1201 600 594 607 597 12758 2421 607 597 603 591 609 1193 609 595 605 1197 605 1197 605 599 601 593 607 1195 607 597 603 1199 603 1199 603 1199 603 591 609 595 580 1222 605 1197 605 1197 605 600 600 594 1000000 2422 580 625 601 1201 601 593 608 596 604 1198 604 1198 604 600 600 594 607 1195 607 597 603 1199 603 1199 603 1199 578 616 609 595 606 1196 606 1196 606 1196 581 624 602 592 12766 2424 605 599 601 1201 601 593 608 596 604 1198 604 1198 579 625 601 593 608 1194 608 596 604 1198 580 1222 605 1197 605 599 602 592 608 1194 609 1193 609 1193 609 596 605 599 12759 2420 608 597 604 1198 604 590 610 594 606 1196 606 1196 606 598 602 592 608 1194 607 597 604 1198 603 1199 603 1199 602 592 609 595 605 1197 605 1197 605 1197 604 600 601 593 1000000 2429 600 1202 575 1227 599 595 606 598 602 1200 602 1200 602 592 609 595 605 1197 605 599 601 1201 601 1201 601 1201 601 593 607 597 603 1199 603 1199 603 1199 603 591 609 595 12136 2425 603 1199 603 1199 603 591 609 595 605 1197 605 1197 605 599 601 593 608 1194 608 596 604 1198 604 1198 603 1199 603 591 609 595 606 1196 606 1196 605 1197 605 599 601 593 12137 2424 604 1198 603 1199 603 591 609 595 606 1196 605 1197 605 599 601 593 607 1195 607 597 603 1199 603 1199 603 1199 602 592 609 595 605 1197 605 1197 604 1198 604 600 600 594 1000000 2429 574 1228 573 1229 573 1229 573 1229 573 621 581 624 577 617 583 622 580 1222 579 625 576 1226 577 1225 576 1226 577 617 584 620 581 1221 580 1222 579 1223 579 625 577 617 12142 2419 584 1218 584 1218 583 1219 583 1219 582 623 578 616 585 619 581 623 578 1224 577 617 584 1218 584 1218 584 1218 584 620 580 624 576 1226 575 1227 500 1353 523 620 582 622 12134 2427 576 1226 576 1226 576 1226 576 1226 576 618 583 622 579 625 576 618 583 1219 583 673 527 1223 579 1223 579 1223 579 625 576 618 582 1220 582 1220 582 1220 582 622 578 616 12140 2421 582 1220 581 1221 581 1221 580 1222 580 624 576 618 582 622 578 616 585 1217 584 620 581 1221 580 1222 580 1222 580 624 576 618 582 1220 582 1220 582 1220 582 623 578 616 1000000 2419 584 672 528 625 577 617 583 1219 582 1220 581 1221 582 622 578 616 585 1217 583 621 580 1222 580 1222 580 1222 580 624 576 618 583 1219 583 1219 582 1220 583 621 580 624 12732 2427 577 617 584 672 528 676 524 1226 576 1226 576 1226 576 618 583 621 579 1223 579 625 575 1227 575 1227 575 1227 575 671 529 675 526 1224 578 1224 578 1224 578 616 585 619 12738 2421 583 621 580 624 576 618 582 1220 582 1220 583 1219 583 621 579 625 576 1226 576 670 530 1220 582 1220 582 1220 581 624 577 617 584 1218 583 1219 583 1219 583 622 580 624 1000000 2430 599 1192 609 595 605 1197 580 624 601 1201 601 593 607 597 603 591 610 594 606 598 602 592 608 1194 25958 2423 605 1197 604 600 575 1227 600 594 606 1196 606 598 602 592 608 596 604 601 600 594 607 597 603 1199 25949 2422 607 1195 606 598 603 1199 602 592 608 1194 608 596 604 600 601 593 607 597 604 600 600 594 607 1195 25958 2423 606 1196 606 598 602 1200 602 592 608 1194 608 596 605 600 601 593 607 597 603 591 610 594 606 1196 25957 2425 604 1198 603 591 610 1192 610 594 606 1196 606 598 602 592 609 595 605 599 602 592 608 596 604 1198 25956 2426 604 1198 603 591 610 1192 610 594 606 1196 606 598 602 592 608 597 605 599 601 593 607 597 603 1199 25952 2420 609 1193 608 596 604 1198 604 590 610 1192 610 594 606 598 602 592 608 596 605 599 601 593 607 1195 1000000 2422 583 1219 608 596 605 1197 580 624 601 1201 601 593 608 596 604 600 601 593 607 597 604 590 610 1202 25955 2427 603 1199 603 591 609 1193 610 594 606 1196 607 597 603 591 609 595 606 598 602 592 609 595 605 1197 25957 2425 604 1198 604 590 610 1192 610 594 581 1221 606 598 602 592 608 596 605 599 601 593 607 597 604 1198 25954 2418 610 1192 610 594 606 1196 605 599 601 1201 601 593 608 596 604 590 610 594 606 598 603 591 609 1193 25958 2424 604 1198 604 590 610 1192 610 594 606 1196 606 598 602 592 609 595 605 600 601 593 607 597 603 1199 25953 2419 610 1192 609 595 605 1197 605 600 601 1201 600 594 607 597 603 591 609 595 605 599 601 593 608 1194 25954 2418 611 1191 610 594 606 1196 606 598 602 1200 602 592 608 596 604 601 600 594 607 597 603 591 609 1193 25958 2424 605 1197 605 599 601 1201 600 594 607 1195 607 597 603 591 609 596 605 599 602 592 608 596 605 1197 25954 2428 601 1190 610 594 607 1195 606 598 602 1200 602 592 608 596 604 590 610 594 606 598 603 591 609 1193 25960 2422 607 1195 606 598 602 1200 602 592 609 1193 609 595 605 599 601 593 607 597 604 590 610 594 606 1196 25957 2424 605 1197 604 600 600 1202 601 593 607 1195 607 597 603 591 610 594 606 598 603 591 609 595 606 1196 1000000 2421 608 1194 608 596 604 1198 604 601 575 1227 601 593 607 598 604 600 600 594 607 598 604 601 600 1202 25953 2419 609 1193 609 596 605 1197 605 600 601 1201 601 593 607 597 603 591 610 594 606 599 602 592 608 1194 25958 2424 605 1197 605 600 601 1201 600 594 607 1195 606 598 602 592 609 595 605 600 601 593 608 596 604 1198 1000000 2423 606 1196 606 598 602 1200 602 592 608 1194 608 597 604 590 610 594 607 597 603 591 610 594 606 1196 25958 2424 605 1197 604 600 601 1201 601 593 607 1195 607 597 604 590 610 595 607 597 603 591 610 594 607 1195 25961 2421 608 1194 608 596 605 1197 605 600 601 1201 601 593 607 597 603 591 610 594 606 598 602 592 609 1193 1000000 2429 601 1201 601 593 607 1195 608 596 604 1198 604 600 601 593 607 597 604 590 611 593 607 597 604 1198 25959 2422 607 1195 607 597 603 1199 603 591 609 1193 610 594 606 598 602 592 609 595 605 599 602 592 608 1194 25959 2421 608 1194 607 597 603 1199 603 591 610 1192 610 594 606 598 603 591 609 595 605 599 602 592 608 1194 25959 2422 608 1194 608 596 604 1198 604 590 611 1201 601 593 607 597 604 590 610 594 607 597 603 591 609 1193 25962 2418 610 1192 610 594 606 1196 607 597 603 1199 603 591 609 595 605 599 602 592 608 596 605 599 601 1201 1000000 2357 610 1183 592 1191 614 1179 595 1188 618 584 613 1180 593 588 613 1190 612 590 605 587 613 589 605 587 25398 2355 623 1180 591 1181 627 1186 589 1183 618 584 616 1187 587 584 614 1189 615 587 605 587 615 587 609 593 1000000 2383 609 1214 600 612 580 1213 608 614 583 1210 605 607 586 616 611 1192 599 613 608 614 579 613 615 607 26670 2387 601 1212 600 612 589 1214 603 609 586 1217 595 607 594 618 606 1187 602 610 609 613 588 614 610 612 1000000 2445 582 1221 603 548 602 1191 609 572 602 1191 609 542 607 544 631 1172 603 568 606 545 605 566 608 543 26263 2414 611 1192 607 544 606 1197 602 569 606 1197 602 539 611 540 635 1168 606 565 610 541 608 563 587 564\n#\nname: decoder_expected1\ntype: parsed_array\ncount: 121\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 60 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 60 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 60 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 65 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 65 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 65 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 10 04 00 00\ncommand: 17 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 10 04 00 00\ncommand: 17 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 10 04 00 00\ncommand: 17 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 21 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 21 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 21 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7B 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7B 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7B 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 78 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 78 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 78 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 79 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 79 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 79 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7A 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7C 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7D 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7D 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 7D 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 73 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 73 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 73 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 30 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 30 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 30 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 39 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 39 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 39 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 31 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 31 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 31 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 34 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 34 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 34 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 32 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 32 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 32 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 33 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 33 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 33 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 0F 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 0F 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 0F 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 38 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 38 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 38 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 2F 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 2F 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nname: decoder_input2\ntype: raw\ndata: 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 600 1000000 2400 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 1000000 2400 600 1200 600 600 1000000 2400 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 1000000 2400 600 1200 600 2400 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200 1000000 2400 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 1000000 2400 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600\n#\nname: decoder_expected2\ntype: parsed_array\ncount: 9\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 1F 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 1F 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 1F 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nname: decoder_input3\ntype: raw\ndata: 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600 600 600 600 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600 600 600 600 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600 600 600 600 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600 600 600 600 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 600 600 600 600 600 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 1200\n#\nname: decoder_expected3\ntype: parsed_array\ncount: 11\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 0D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: FD 00 00 00\ncommand: 13 00 00 00\nrepeat: false\n#\nname: decoder_input4\ntype: raw\ndata: 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600\n#\nname: decoder_expected4\ntype: parsed_array\ncount: 6\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nname: decoder_input5\ntype: raw\ndata: 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 1000000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600\n#\nname: decoder_expected5\ntype: parsed_array\ncount: 24\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: B5 0F 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nname: encoder_input1\ntype: parsed_array\ncount: 1\n#\nprotocol: SIRC\naddress: 0A 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nname: encoder_expected1\ntype: raw\ndata:\n10000 2400 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600 600 1200 600 600\n#\nname: encoder_input2\ntype: parsed_array\ncount: 3\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: true\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: true\n#\nname: encoder_expected2\ntype: raw\ndata:\n10000 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 18600 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600 18600 2400 600 1200 600 1200 600 600 600 600 600 1200 600 600 600 1200 600 1200 600 600 600 1200 600 1200 600 1200 600 1200 600 1200 600 600\n#\nname: encoder_decoder_input1\ntype: parsed_array\ncount: 24\n#\nprotocol: SIRC20\naddress: FF 1F 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: FF 1F 00 00\ncommand: 7F 00 00 00\nrepeat: true\n#\nprotocol: SIRC20\naddress: FF 1F 00 00\ncommand: 7F 00 00 00\nrepeat: true\n#\nprotocol: SIRC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: SIRC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: true\n#\nprotocol: SIRC\naddress: 1A 00 00 00\ncommand: 22 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 1A 00 00 00\ncommand: 22 00 00 00\nrepeat: true\n#\nprotocol: SIRC\naddress: 1A 00 00 00\ncommand: 22 00 00 00\nrepeat: true\n#\nprotocol: SIRC\naddress: 17 00 00 00\ncommand: 0A 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: true\n#\nprotocol: SIRC15\naddress: 7D 00 00 00\ncommand: 53 00 00 00\nrepeat: true\n#\nprotocol: SIRC15\naddress: 71 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 15 00 00 00\ncommand: 01 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 01 00 00 00\ncommand: 15 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: AA 00 00 00\ncommand: 55 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 31 03 00 00\ncommand: 71 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC\naddress: 1F 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC15\naddress: FF 00 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: 00 00 00 00\ncommand: 00 00 00 00\nrepeat: false\n#\nprotocol: SIRC20\naddress: FF 1F 00 00\ncommand: 7F 00 00 00\nrepeat: false\n#\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/js/basic.js",
    "content": "let tests = require(\"tests\");\nlet flipper = require(\"flipper\");\n\ntests.assert_eq(1337, 1337);\ntests.assert_eq(\"hello\", \"hello\");\n\ntests.assert_eq(\"compatible\", sdkCompatibilityStatus(1, 0));\ntests.assert_eq(\"firmwareTooOld\", sdkCompatibilityStatus(100500, 0));\ntests.assert_eq(\"firmwareTooNew\", sdkCompatibilityStatus(-100500, 0));\ntests.assert_eq(true, doesSdkSupport([\"baseline\"]));\ntests.assert_eq(false, doesSdkSupport([\"abobus\", \"other-nonexistent-feature\"]));\n\ntests.assert_eq(\"flipperdevices\", flipper.firmwareVendor);\ntests.assert_eq(1, flipper.jsSdkVersion[0]);\ntests.assert_eq(0, flipper.jsSdkVersion[1]);\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/js/event_loop.js",
    "content": "let tests = require(\"tests\");\nlet event_loop = require(\"event_loop\");\n\nlet ext = {\n    i: 0,\n    received: false,\n};\n\nlet queue = event_loop.queue(16);\n\nevent_loop.subscribe(queue.input, function (_, item, tests, ext) {\n    tests.assert_eq(123, item);\n    ext.received = true;\n}, tests, ext);\n\nevent_loop.subscribe(event_loop.timer(\"periodic\", 1), function (_, _item, queue, counter, ext) {\n    ext.i++;\n    queue.send(123);\n    if (counter === 10)\n        event_loop.stop();\n    return [queue, counter + 1, ext];\n}, queue, 1, ext);\n\nevent_loop.subscribe(event_loop.timer(\"oneshot\", 1000), function (_, _item, tests) {\n    tests.fail(\"event loop was not stopped\");\n}, tests);\n\nevent_loop.run();\ntests.assert_eq(10, ext.i);\ntests.assert_eq(true, ext.received);\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/js/math.js",
    "content": "let tests = require(\"tests\");\nlet math = require(\"math\");\n\n// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16\n\n// basics\ntests.assert_float_close(5, math.abs(-5), math.EPSILON);\ntests.assert_float_close(0.5, math.abs(-0.5), math.EPSILON);\ntests.assert_float_close(5, math.abs(5), math.EPSILON);\ntests.assert_float_close(0.5, math.abs(0.5), math.EPSILON);\ntests.assert_float_close(3, math.cbrt(27), math.EPSILON);\ntests.assert_float_close(6, math.ceil(5.3), math.EPSILON);\ntests.assert_float_close(31, math.clz32(1), math.EPSILON);\ntests.assert_float_close(5, math.floor(5.7), math.EPSILON);\ntests.assert_float_close(5, math.max(3, 5), math.EPSILON);\ntests.assert_float_close(3, math.min(3, 5), math.EPSILON);\ntests.assert_float_close(-1, math.sign(-5), math.EPSILON);\ntests.assert_float_close(5, math.trunc(5.7), math.EPSILON);\n\n// trig\ntests.assert_float_close(1.0471975511965976, math.acos(0.5), math.EPSILON);\ntests.assert_float_close(1.3169578969248166, math.acosh(2), math.EPSILON);\ntests.assert_float_close(0.5235987755982988, math.asin(0.5), math.EPSILON);\ntests.assert_float_close(1.4436354751788103, math.asinh(2), math.EPSILON);\ntests.assert_float_close(0.7853981633974483, math.atan(1), math.EPSILON);\ntests.assert_float_close(0.7853981633974483, math.atan2(1, 1), math.EPSILON);\ntests.assert_float_close(0.5493061443340549, math.atanh(0.5), math.EPSILON);\ntests.assert_float_close(-1, math.cos(math.PI), math.EPSILON * 18); // Error 3.77475828372553223744e-15\ntests.assert_float_close(1, math.sin(math.PI / 2), math.EPSILON * 4.5); // Error 9.99200722162640886381e-16\n\n// powers\ntests.assert_float_close(5, math.sqrt(25), math.EPSILON);\ntests.assert_float_close(8, math.pow(2, 3), math.EPSILON);\ntests.assert_float_close(2.718281828459045, math.exp(1), math.EPSILON * 2); // Error 4.44089209850062616169e-16\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/js/storage.js",
    "content": "let storage = require(\"storage\");\nlet tests = require(\"tests\");\n\nlet baseDir = \"/ext/.tmp/unit_tests\";\n\ntests.assert_eq(true, storage.rmrf(baseDir));\ntests.assert_eq(true, storage.makeDirectory(baseDir));\n\n// write\nlet file = storage.openFile(baseDir + \"/helloworld\", \"w\", \"create_always\");\ntests.assert_eq(true, !!file);\ntests.assert_eq(true, file.isOpen());\ntests.assert_eq(13, file.write(\"Hello, World!\"));\ntests.assert_eq(true, file.close());\ntests.assert_eq(false, file.isOpen());\n\n// read\nfile = storage.openFile(baseDir + \"/helloworld\", \"r\", \"open_existing\");\ntests.assert_eq(true, !!file);\ntests.assert_eq(true, file.isOpen());\ntests.assert_eq(13, file.size());\ntests.assert_eq(\"Hello, World!\", file.read(\"ascii\", 128));\ntests.assert_eq(true, file.close());\ntests.assert_eq(false, file.isOpen());\n\n// seek\nfile = storage.openFile(baseDir + \"/helloworld\", \"r\", \"open_existing\");\ntests.assert_eq(true, !!file);\ntests.assert_eq(true, file.isOpen());\ntests.assert_eq(13, file.size());\ntests.assert_eq(\"Hello, World!\", file.read(\"ascii\", 128));\ntests.assert_eq(true, file.seekAbsolute(1));\ntests.assert_eq(true, file.seekRelative(2));\ntests.assert_eq(3, file.tell());\ntests.assert_eq(false, file.eof());\ntests.assert_eq(\"lo, World!\", file.read(\"ascii\", 128));\ntests.assert_eq(true, file.eof());\ntests.assert_eq(true, file.close());\ntests.assert_eq(false, file.isOpen());\n\n// byte-level copy\nlet src = storage.openFile(baseDir + \"/helloworld\", \"r\", \"open_existing\");\nlet dst = storage.openFile(baseDir + \"/helloworld2\", \"rw\", \"create_always\");\ntests.assert_eq(true, !!src);\ntests.assert_eq(true, src.isOpen());\ntests.assert_eq(true, !!dst);\ntests.assert_eq(true, dst.isOpen());\ntests.assert_eq(true, src.copyTo(dst, 10));\ntests.assert_eq(true, dst.seekAbsolute(0));\ntests.assert_eq(\"Hello, Wor\", dst.read(\"ascii\", 128));\ntests.assert_eq(true, src.copyTo(dst, 3));\ntests.assert_eq(true, dst.seekAbsolute(0));\ntests.assert_eq(\"Hello, World!\", dst.read(\"ascii\", 128));\ntests.assert_eq(true, src.eof());\ntests.assert_eq(true, src.close());\ntests.assert_eq(false, src.isOpen());\ntests.assert_eq(true, dst.eof());\ntests.assert_eq(true, dst.close());\ntests.assert_eq(false, dst.isOpen());\n\n// truncate\ntests.assert_eq(true, storage.copy(baseDir + \"/helloworld\", baseDir + \"/helloworld2\"));\nfile = storage.openFile(baseDir + \"/helloworld2\", \"w\", \"open_existing\");\ntests.assert_eq(true, !!file);\ntests.assert_eq(true, file.seekAbsolute(5));\ntests.assert_eq(true, file.truncate());\ntests.assert_eq(true, file.close());\nfile = storage.openFile(baseDir + \"/helloworld2\", \"r\", \"open_existing\");\ntests.assert_eq(true, !!file);\ntests.assert_eq(\"Hello\", file.read(\"ascii\", 128));\ntests.assert_eq(true, file.close());\n\n// existence\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld2\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/sus_amogus_123\"));\ntests.assert_eq(false, storage.directoryExists(baseDir + \"/helloworld\"));\ntests.assert_eq(false, storage.fileExists(baseDir));\ntests.assert_eq(true, storage.directoryExists(baseDir));\ntests.assert_eq(true, storage.fileOrDirExists(baseDir));\ntests.assert_eq(true, storage.remove(baseDir + \"/helloworld2\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/helloworld2\"));\n\n// stat\nlet stat = storage.stat(baseDir + \"/helloworld\");\ntests.assert_eq(true, !!stat);\ntests.assert_eq(baseDir + \"/helloworld\", stat.path);\ntests.assert_eq(false, stat.isDirectory);\ntests.assert_eq(13, stat.size);\n\n// rename\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/helloworld123\"));\ntests.assert_eq(true, storage.rename(baseDir + \"/helloworld\", baseDir + \"/helloworld123\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld123\"));\ntests.assert_eq(true, storage.rename(baseDir + \"/helloworld123\", baseDir + \"/helloworld\"));\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/helloworld123\"));\n\n// copy\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(false, storage.fileExists(baseDir + \"/helloworld123\"));\ntests.assert_eq(true, storage.copy(baseDir + \"/helloworld\", baseDir + \"/helloworld123\"));\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld\"));\ntests.assert_eq(true, storage.fileExists(baseDir + \"/helloworld123\"));\n\n// next avail\ntests.assert_eq(\"helloworld1\", storage.nextAvailableFilename(baseDir, \"helloworld\", \"\", 20));\n\n// fs info\nlet fsInfo = storage.fsInfo(\"/ext\");\ntests.assert_eq(true, !!fsInfo);\ntests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace); // idk \\(-_-)/\nfsInfo = storage.fsInfo(\"/int\");\ntests.assert_eq(true, !!fsInfo);\ntests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace);\n\n// path operations\ntests.assert_eq(true, storage.arePathsEqual(\"/ext/test\", \"/ext/Test\"));\ntests.assert_eq(false, storage.arePathsEqual(\"/ext/test\", \"/ext/Testttt\"));\ntests.assert_eq(true, storage.isSubpathOf(\"/ext/test\", \"/ext/test/sub\"));\ntests.assert_eq(false, storage.isSubpathOf(\"/ext/test/sub\", \"/ext/test\"));\n\n// dir\nlet entries = storage.readDirectory(baseDir);\ntests.assert_eq(true, !!entries);\n// FIXME: (-nofl) this test suite assumes that files are listed by\n// `readDirectory` in the exact order that they were created, which is not\n// something that is actually guaranteed.\n// Possible solution: sort and compare the array.\ntests.assert_eq(\"helloworld\", entries[0].path);\ntests.assert_eq(\"helloworld123\", entries[1].path);\n\ntests.assert_eq(true, storage.rmrf(baseDir));\ntests.assert_eq(true, storage.makeDirectory(baseDir));\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Felica.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 4\n# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB\nDevice type: FeliCa\n# UID is common for all formats\nUID: 29 9F FA 53 AB 75 87 6E\n# FeliCa specific data\nData format version: 1\nManufacture id: 29 9F FA 53 AB 75 87 6E\nManufacture parameter: 00 F1 00 00 00 01 43 00\nBlocks total: 28\nBlocks read: 28\nBlock 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF\nBlock 1: 00 00 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF\nBlock 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 7: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 14: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF\nBlock 15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 17: 00 00 29 9F FA 53 AB 75 87 6E 57 4E 10 2A 94 16 BC 8E\nBlock 18: 00 00 29 9F FA 53 AB 75 87 6E 00 F1 00 00 00 01 43 00\nBlock 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 20: 00 00 88 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 22: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF\nBlock 23: 00 00 FF FF FF 00 FF 00 10 00 00 00 00 00 00 00 00 00\nBlock 24: 00 00 24 FE FF 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 26: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nBlock 27: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ntag213_locked.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 3\n# Nfc device type can be UID, Mifare Ultralight, Mifare Classic\nDevice type: NTAG213\n# UID, ATQA and SAK are common for all formats\nUID: 04 AC 6B 72 BA 6C 80\nATQA: 00 44\nSAK: 00\n# Mifare Ultralight specific data\nData format version: 1\nSignature: 2D AE BC AF 84 B8 85 87 C2 FB FE 76 13 58 86 72 8E 1D 3C B5 DA 24 23 44 E5 63 4D 4C 82 FB D7 18\nMifare version: 00 04 04 02 01 00 0F 03\nCounter 0: 0\nTearing 0: 00\nCounter 1: 0\nTearing 1: 00\nCounter 2: 0\nTearing 2: 00\nPages total: 45\nPages read: 45\nPage 0: 04 AC 6B 4B\nPage 1: 72 BA 6C 80\nPage 2: 24 48 00 00\nPage 3: E1 10 12 00\nPage 4: 00 00 41 50\nPage 5: 00 00 31 31\nPage 6: 00 20 09 28\nPage 7: 00 03 31 59\nPage 8: 91 DF D3 00\nPage 9: 00 00 00 00\nPage 10: 00 00 00 00\nPage 11: 00 00 00 00\nPage 12: 00 00 00 00\nPage 13: 00 00 00 00\nPage 14: 00 00 00 00\nPage 15: 00 00 00 00\nPage 16: 00 00 00 00\nPage 17: 00 00 00 00\nPage 18: 00 00 00 00\nPage 19: 00 00 00 00\nPage 20: 00 00 00 00\nPage 21: 00 00 00 00\nPage 22: 00 00 00 00\nPage 23: 00 00 00 00\nPage 24: 00 00 00 00\nPage 25: 00 00 00 00\nPage 26: 00 00 00 00\nPage 27: 00 00 00 00\nPage 28: 00 00 00 00\nPage 29: 00 00 00 00\nPage 30: 00 00 00 00\nPage 31: 00 00 00 00\nPage 32: 00 00 00 00\nPage 33: 00 00 00 00\nPage 34: 00 00 00 00\nPage 35: 00 00 00 00\nPage 36: 00 00 00 00\nPage 37: 00 00 00 00\nPage 38: 00 00 00 00\nPage 39: 00 00 00 00\nPage 40: 00 00 00 BD\nPage 41: 04 00 00 04\nPage 42: C0 05 00 00\nPage 43: 95 3F 52 FF\nPage 44: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ntag215.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 3\n# Nfc device type can be UID, Mifare Ultralight, Mifare Classic\nDevice type: NTAG215\n# UID, ATQA and SAK are common for all formats\nUID: 04 51 5C FA 6F 73 81\nATQA: 00 44\nSAK: 00\n# Mifare Ultralight specific data\nData format version: 1\nSignature: 42 21 E4 6C 79 6A 81 5E EA 0D 93 6D 85 EE 4B 0C 2A 00 D5 77 F1 C5 67 F3 63 75 F8 EB 86 48 5E 6B\nMifare version: 00 04 04 02 01 00 11 03\nCounter 0: 0\nTearing 0: 00\nCounter 1: 0\nTearing 1: 00\nCounter 2: 00\nTearing 2: 00\nPages total: 135\nPages read: 135\nPage 0: 04 51 5C 81\nPage 1: FA 6F 73 81\nPage 2: 67 48 0F E0\nPage 3: F1 10 FF EE\nPage 4: A5 00 00 00\nPage 5: 90 42 74 71\nPage 6: FD 8F 50 61\nPage 7: C5 65 1B 54\nPage 8: EF 68 D0 8E\nPage 9: 3D 35 DB 83\nPage 10: D3 00 29 F6\nPage 11: 42 2A A5 5C\nPage 12: F1 69 0A FC\nPage 13: B6 44 E9 6B\nPage 14: 77 41 88 81\nPage 15: 86 31 CB AD\nPage 16: B1 DE F1 AB\nPage 17: DF 96 C2 C5\nPage 18: C1 26 99 96\nPage 19: 85 AF 9F 0E\nPage 20: 58 FE ED DC\nPage 21: 0A 0A 00 01\nPage 22: 03 C1 05 02\nPage 23: 38 39 34 33\nPage 24: 49 2D 4E 5C\nPage 25: 5B 21 0F 44\nPage 26: 3F 3F 76 69\nPage 27: B4 72 D8 38\nPage 28: A0 35 53 51\nPage 29: 53 EB A6 7C\nPage 30: 3E 8B 97 C0\nPage 31: 00 7A 45 13\nPage 32: 3A 8B D4 0F\nPage 33: 31 C2 32 CC\nPage 34: B4 24 A6 1B\nPage 35: D3 F5 4A 1F\nPage 36: CD 8F 1D 64\nPage 37: 01 F4 DF C2\nPage 38: 11 16 C2 C5\nPage 39: 30 6D 49 AF\nPage 40: 10 D4 7C 3C\nPage 41: 6E 36 4E 08\nPage 42: 95 76 BC 84\nPage 43: 35 50 DD F0\nPage 44: 21 0F EE D9\nPage 45: 85 19 54 5F\nPage 46: 3E A9 04 20\nPage 47: 1B 97 E4 39\nPage 48: FF 0A 45 F6\nPage 49: 13 D4 3E DD\nPage 50: 97 42 FC 67\nPage 51: 6A AC 78 96\nPage 52: D1 DA 25 23\nPage 53: BF 4D B3 76\nPage 54: F1 21 ED 15\nPage 55: BD 55 11 C4\nPage 56: 4E 8C E9 23\nPage 57: C0 C4 6D 5A\nPage 58: 58 25 FF 95\nPage 59: 3C 2B 7A 57\nPage 60: 66 BE A0 61\nPage 61: BC FC 4A 31\nPage 62: 4D AC EE 81\nPage 63: BE 1A 86 04\nPage 64: F6 D7 5E B3\nPage 65: E7 A8 A2 86\nPage 66: E9 40 AB 47\nPage 67: C8 36 E4 3E\nPage 68: A7 4D D3 EA\nPage 69: 83 9A 64 F7\nPage 70: 96 6B 5D BF\nPage 71: 4E A2 A6 0F\nPage 72: BD 3D BE 7C\nPage 73: 22 0C 68 51\nPage 74: 0F 9A B8 AE\nPage 75: 38 2C C4 CD\nPage 76: 53 D8 DD 18\nPage 77: A6 5D 35 87\nPage 78: C9 6D 99 59\nPage 79: 61 9F B6 DC\nPage 80: E6 22 0F 99\nPage 81: 39 82 79 60\nPage 82: 58 2E BE F7\nPage 83: EF F7 95 62\nPage 84: D5 06 1B 58\nPage 85: 65 05 A9 08\nPage 86: 75 ED 5D 90\nPage 87: 5A E1 7E C9\nPage 88: 35 D6 29 BB\nPage 89: D0 67 6C F9\nPage 90: A0 FF 0B 93\nPage 91: 22 EA A3 3F\nPage 92: E2 BD BD 58\nPage 93: BE 93 D9 94\nPage 94: 41 CC 7E 40\nPage 95: E6 8C 5A 43\nPage 96: 65 C1 24 94\nPage 97: B9 97 61 13\nPage 98: AD 74 FF 21\nPage 99: 0F EC F6 03\nPage 100: 89 5D 89 E5\nPage 101: 8D 11 F8 D7\nPage 102: 33 43 79 2E\nPage 103: 23 E5 29 B5\nPage 104: 53 98 13 FF\nPage 105: E8 79 8B 33\nPage 106: 45 6C 34 38\nPage 107: 3B 69 28 D7\nPage 108: D2 80 B0 2F\nPage 109: D0 18 D5 DD\nPage 110: 6C 2D D9 97\nPage 111: CA 78 B4 A2\nPage 112: B7 3E B8 79\nPage 113: A2 BE 54 E4\nPage 114: C8 28 0C 4A\nPage 115: 81 E7 EC 1C\nPage 116: 39 93 6F 70\nPage 117: 75 77 5C FC\nPage 118: 66 58 0C 1C\nPage 119: 9F 70 2E C8\nPage 120: 52 4A 52 BD\nPage 121: 56 D5 6A 15\nPage 122: 54 1B 33 90\nPage 123: 44 11 C1 07\nPage 124: 11 5C BA 80\nPage 125: 10 14 20 9A\nPage 126: 4A D8 E6 36\nPage 127: DA B8 59 E5\nPage 128: 5E 48 95 DA\nPage 129: 96 6A 26 85\nPage 130: 01 00 0F BD\nPage 131: 00 00 00 04\nPage 132: 5F 00 00 00\nPage 133: 00 00 00 00\nPage 134: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ntag216.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 2\n# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card\nDevice type: NTAG216\n# UID, ATQA and SAK are common for all formats\nUID: 04 D9 65 0A 32 5E 80\nATQA: 44 00\nSAK: 00\n# Mifare Ultralight specific data\nData format version: 1\nSignature: 48 2A F2 01 0F F2 F5 A7 9A D5 79 6E CB 14 54 48 98 D1 57 5D 8A 23 A9 B0 E8 20 02 3E CD C8 16 DB\nMifare version: 00 04 04 02 01 00 13 03\nCounter 0: 0\nTearing 0: 00\nCounter 1: 0\nTearing 1: 00\nCounter 2: 0\nTearing 2: 00\nPages total: 231\nPages read: 231\nPage 0: 04 D9 65 30\nPage 1: 0A 32 5E 80\nPage 2: E6 48 00 00\nPage 3: E1 10 6D 00\nPage 4: 03 37 D1 01\nPage 5: 33 55 04 6D\nPage 6: 2E 79 6F 75\nPage 7: 74 75 62 65\nPage 8: 2E 63 6F 6D\nPage 9: 2F 77 61 74\nPage 10: 63 68 3F 76\nPage 11: 3D 62 78 71\nPage 12: 4C 73 72 6C\nPage 13: 61 6B 4B 38\nPage 14: 26 66 65 61\nPage 15: 74 75 72 65\nPage 16: 3D 79 6F 75\nPage 17: 74 75 2E 62\nPage 18: 65 FE 00 00\nPage 19: 00 00 00 00\nPage 20: 00 00 00 00\nPage 21: 00 00 00 00\nPage 22: 00 00 00 00\nPage 23: 00 00 00 00\nPage 24: 00 00 00 00\nPage 25: 00 00 00 00\nPage 26: 00 00 00 00\nPage 27: 00 00 00 00\nPage 28: 00 00 00 00\nPage 29: 00 00 00 00\nPage 30: 00 00 00 00\nPage 31: 00 00 00 00\nPage 32: 00 00 00 00\nPage 33: 00 00 00 00\nPage 34: 00 00 00 00\nPage 35: 00 00 00 00\nPage 36: 00 00 00 00\nPage 37: 00 00 00 00\nPage 38: 00 00 00 00\nPage 39: 00 00 00 00\nPage 40: 00 00 00 00\nPage 41: 00 00 00 00\nPage 42: 00 00 00 00\nPage 43: 00 00 00 00\nPage 44: 00 00 00 00\nPage 45: 00 00 00 00\nPage 46: 00 00 00 00\nPage 47: 00 00 00 00\nPage 48: 00 00 00 00\nPage 49: 00 00 00 00\nPage 50: 00 00 00 00\nPage 51: 00 00 00 00\nPage 52: 00 00 00 00\nPage 53: 00 00 00 00\nPage 54: 00 00 00 00\nPage 55: 00 00 00 00\nPage 56: 00 00 00 00\nPage 57: 00 00 00 00\nPage 58: 00 00 00 00\nPage 59: 00 00 00 00\nPage 60: 00 00 00 00\nPage 61: 00 00 00 00\nPage 62: 00 00 00 00\nPage 63: 00 00 00 00\nPage 64: 00 00 00 00\nPage 65: 00 00 00 00\nPage 66: 00 00 00 00\nPage 67: 00 00 00 00\nPage 68: 00 00 00 00\nPage 69: 00 00 00 00\nPage 70: 00 00 00 00\nPage 71: 00 00 00 00\nPage 72: 00 00 00 00\nPage 73: 00 00 00 00\nPage 74: 00 00 00 00\nPage 75: 00 00 00 00\nPage 76: 00 00 00 00\nPage 77: 00 00 00 00\nPage 78: 00 00 00 00\nPage 79: 00 00 00 00\nPage 80: 00 00 00 00\nPage 81: 00 00 00 00\nPage 82: 00 00 00 00\nPage 83: 00 00 00 00\nPage 84: 00 00 00 00\nPage 85: 00 00 00 00\nPage 86: 00 00 00 00\nPage 87: 00 00 00 00\nPage 88: 00 00 00 00\nPage 89: 00 00 00 00\nPage 90: 00 00 00 00\nPage 91: 00 00 00 00\nPage 92: 00 00 00 00\nPage 93: 00 00 00 00\nPage 94: 00 00 00 00\nPage 95: 00 00 00 00\nPage 96: 00 00 00 00\nPage 97: 00 00 00 00\nPage 98: 00 00 00 00\nPage 99: 00 00 00 00\nPage 100: 00 00 00 00\nPage 101: 00 00 00 00\nPage 102: 00 00 00 00\nPage 103: 00 00 00 00\nPage 104: 00 00 00 00\nPage 105: 00 00 00 00\nPage 106: 00 00 00 00\nPage 107: 00 00 00 00\nPage 108: 00 00 00 00\nPage 109: 00 00 00 00\nPage 110: 00 00 00 00\nPage 111: 00 00 00 00\nPage 112: 00 00 00 00\nPage 113: 00 00 00 00\nPage 114: 00 00 00 00\nPage 115: 00 00 00 00\nPage 116: 00 00 00 00\nPage 117: 00 00 00 00\nPage 118: 00 00 00 00\nPage 119: 00 00 00 00\nPage 120: 00 00 00 00\nPage 121: 00 00 00 00\nPage 122: 00 00 00 00\nPage 123: 00 00 00 00\nPage 124: 00 00 00 00\nPage 125: 00 00 00 00\nPage 126: 00 00 00 00\nPage 127: 00 00 00 00\nPage 128: 00 00 00 00\nPage 129: 00 00 00 00\nPage 130: 00 00 00 00\nPage 131: 00 00 00 00\nPage 132: 00 00 00 00\nPage 133: 00 00 00 00\nPage 134: 00 00 00 00\nPage 135: 00 00 00 00\nPage 136: 00 00 00 00\nPage 137: 00 00 00 00\nPage 138: 00 00 00 00\nPage 139: 00 00 00 00\nPage 140: 00 00 00 00\nPage 141: 00 00 00 00\nPage 142: 00 00 00 00\nPage 143: 00 00 00 00\nPage 144: 00 00 00 00\nPage 145: 00 00 00 00\nPage 146: 00 00 00 00\nPage 147: 00 00 00 00\nPage 148: 00 00 00 00\nPage 149: 00 00 00 00\nPage 150: 00 00 00 00\nPage 151: 00 00 00 00\nPage 152: 00 00 00 00\nPage 153: 00 00 00 00\nPage 154: 00 00 00 00\nPage 155: 00 00 00 00\nPage 156: 00 00 00 00\nPage 157: 00 00 00 00\nPage 158: 00 00 00 00\nPage 159: 00 00 00 00\nPage 160: 00 00 00 00\nPage 161: 00 00 00 00\nPage 162: 00 00 00 00\nPage 163: 00 00 00 00\nPage 164: 00 00 00 00\nPage 165: 00 00 00 00\nPage 166: 00 00 00 00\nPage 167: 00 00 00 00\nPage 168: 00 00 00 00\nPage 169: 00 00 00 00\nPage 170: 00 00 00 00\nPage 171: 00 00 00 00\nPage 172: 00 00 00 00\nPage 173: 00 00 00 00\nPage 174: 00 00 00 00\nPage 175: 00 00 00 00\nPage 176: 00 00 00 00\nPage 177: 00 00 00 00\nPage 178: 00 00 00 00\nPage 179: 00 00 00 00\nPage 180: 00 00 00 00\nPage 181: 00 00 00 00\nPage 182: 00 00 00 00\nPage 183: 00 00 00 00\nPage 184: 00 00 00 00\nPage 185: 00 00 00 00\nPage 186: 00 00 00 00\nPage 187: 00 00 00 00\nPage 188: 00 00 00 00\nPage 189: 00 00 00 00\nPage 190: 00 00 00 00\nPage 191: 00 00 00 00\nPage 192: 00 00 00 00\nPage 193: 00 00 00 00\nPage 194: 00 00 00 00\nPage 195: 00 00 00 00\nPage 196: 00 00 00 00\nPage 197: 00 00 00 00\nPage 198: 00 00 00 00\nPage 199: 00 00 00 00\nPage 200: 00 00 00 00\nPage 201: 00 00 00 00\nPage 202: 00 00 00 00\nPage 203: 00 00 00 00\nPage 204: 00 00 00 00\nPage 205: 00 00 00 00\nPage 206: 00 00 00 00\nPage 207: 00 00 00 00\nPage 208: 00 00 00 00\nPage 209: 00 00 00 00\nPage 210: 00 00 00 00\nPage 211: 00 00 00 00\nPage 212: 00 00 00 00\nPage 213: 00 00 00 00\nPage 214: 00 00 00 00\nPage 215: 00 00 00 00\nPage 216: 00 00 00 00\nPage 217: 00 00 00 00\nPage 218: 00 00 00 00\nPage 219: 00 00 00 00\nPage 220: 00 00 00 00\nPage 221: 00 00 00 00\nPage 222: 00 00 00 00\nPage 223: 00 00 00 00\nPage 224: 00 00 00 00\nPage 225: 00 00 00 00\nPage 226: 00 00 00 BD\nPage 227: 04 00 00 FF\nPage 228: 00 05 00 00\nPage 229: 00 00 00 00\nPage 230: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_accept_all_pass.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 4\n# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB\nDevice type: SLIX\n# UID is common for all formats\nUID: E0 04 01 08 49 D0 DC 81\n# ISO15693-3 specific data\n# Data Storage Format Identifier\nDSFID: 01\n# Application Family Identifier\nAFI: 3D\n# IC Reference - Vendor specific meaning\nIC Reference: 01\n# Lock Bits\nLock DSFID: true\nLock AFI: true\n# Number of memory blocks, valid range = 1..256\nBlock Count: 80\n# Size of a single memory block, valid range = 01...20 (hex)\nBlock Size: 04\nData Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01\n# Block Security Status: 01 = locked, 00 = not locked\nSecurity Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n# SLIX specific data\n# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords\nCapabilities: AcceptAllPasswords\n# Passwords are optional. If a password is omitted, a default value will be used\nPassword Read: 00 00 00 00\nPassword Write: 00 00 00 00\nPassword Privacy: 0F 0F 0F 0F\nPassword Destroy: 0F 0F 0F 0F\nPassword EAS: 00 00 00 00\n# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.\nSignature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D\nPrivacy Mode: false\n# Protection pointer configuration\nProtection Pointer: 32\nProtection Condition: 02\n# SLIX Lock Bits\nLock EAS: true\nLock PPL: true\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_default.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 4\n# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB\nDevice type: SLIX\n# UID is common for all formats\nUID: E0 04 01 08 49 D0 DC 81\n# ISO15693-3 specific data\n# Data Storage Format Identifier\nDSFID: 01\n# Application Family Identifier\nAFI: 3D\n# IC Reference - Vendor specific meaning\nIC Reference: 01\n# Lock Bits\nLock DSFID: true\nLock AFI: true\n# Number of memory blocks, valid range = 1..256\nBlock Count: 80\n# Size of a single memory block, valid range = 01...20 (hex)\nBlock Size: 04\nData Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01\n# Block Security Status: 01 = locked, 00 = not locked\nSecurity Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n# SLIX specific data\n# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords\nCapabilities: Default\n# Passwords are optional. If a password is omitted, a default value will be used\nPassword Read: 00 00 00 00\nPassword Write: 00 00 00 00\nPassword Privacy: 0F 0F 0F 0F\nPassword Destroy: 0F 0F 0F 0F\nPassword EAS: 00 00 00 00\n# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.\nSignature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D\nPrivacy Mode: false\n# Protection pointer configuration\nProtection Pointer: 32\nProtection Condition: 02\n# SLIX Lock Bits\nLock EAS: true\nLock PPL: true\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_missed.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 4\n# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB\nDevice type: SLIX\n# UID is common for all formats\nUID: E0 04 01 08 49 D0 DC 81\n# ISO15693-3 specific data\n# Data Storage Format Identifier\nDSFID: 01\n# Application Family Identifier\nAFI: 3D\n# IC Reference - Vendor specific meaning\nIC Reference: 01\n# Lock Bits\nLock DSFID: true\nLock AFI: true\n# Number of memory blocks, valid range = 1..256\nBlock Count: 80\n# Size of a single memory block, valid range = 01...20 (hex)\nBlock Size: 04\nData Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01\n# Block Security Status: 01 = locked, 00 = not locked\nSecurity Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n# SLIX specific data\n# Passwords are optional. If a password is omitted, a default value will be used\nPassword Read: 00 00 00 00\nPassword Write: 00 00 00 00\nPassword Privacy: 0F 0F 0F 0F\nPassword Destroy: 0F 0F 0F 0F\nPassword EAS: 00 00 00 00\n# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.\nSignature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D\nPrivacy Mode: false\n# Protection pointer configuration\nProtection Pointer: 32\nProtection Condition: 02\n# SLIX Lock Bits\nLock EAS: true\nLock PPL: true\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_11.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 3\n# Nfc device type can be UID, Mifare Ultralight, Mifare Classic\nDevice type: Mifare Ultralight 11\n# UID, ATQA and SAK are common for all formats\nUID: 04 15 74 F2 B0 5E 81\nATQA: 00 44\nSAK: 00\n# Mifare Ultralight specific data\nData format version: 1\nSignature: A4 37 7D E5 8C 2F 88 D8 04 60 41 6E 3A C8 CD DB 19 94 26 12 C5 D0 12 B0 EB 88 05 72 89 F2 A5 61\nMifare version: 00 04 03 01 01 00 0B 03\nCounter 0: 0\nTearing 0: BD\nCounter 1: 0\nTearing 1: BD\nCounter 2: 0\nTearing 2: BD\nPages total: 20\nPages read: 20\nPage 0: 04 15 74 ED\nPage 1: F2 B0 5E 81\nPage 2: 9D 48 F8 FF\nPage 3: C1 31 3E 3F\nPage 4: B0 00 F0 02\nPage 5: 2F B3 45 A0\nPage 6: D4 9C 02 F2\nPage 7: 4A B1 ED FF\nPage 8: C8 01 00 02\nPage 9: 4F B3 46 70\nPage 10: EE F6 60 B0\nPage 11: B6 C6 12 1B\nPage 12: B9 1E 49 C3\nPage 13: 49 DF 7A 57\nPage 14: 08 52 2A 11\nPage 15: 28 0A 28 59\nPage 16: 00 00 00 FF\nPage 17: 00 05 00 00\nPage 18: FF FF FF FF\nPage 19: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_21.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 3\n# Nfc device type can be UID, Mifare Ultralight, Mifare Classic\nDevice type: Mifare Ultralight 21\n# UID, ATQA and SAK are common for all formats\nUID: 34 BF AB B1 AE 73 D6\nATQA: 00 44\nSAK: 00\n# Mifare Ultralight specific data\nData format version: 1\nSignature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nMifare version: 00 34 21 01 01 00 0E 03\nCounter 0: 0\nTearing 0: 00\nCounter 1: 0\nTearing 1: 00\nCounter 2: 0\nTearing 2: 00\nPages total: 41\nPages read: 41\nPage 0: 34 BF AB A8\nPage 1: B1 AE 73 D6\nPage 2: BA 00 70 08\nPage 3: FF FF FF FC\nPage 4: 45 D9 BB A0\nPage 5: 5D 9D FA 00\nPage 6: 80 70 38 40\nPage 7: 12 30 02 00\nPage 8: 00 00 00 00\nPage 9: 00 00 00 00\nPage 10: AC A1 0D E4\nPage 11: 80 70 38 40\nPage 12: 00 57 A0 01\nPage 13: 00 08 C1 40\nPage 14: 00 00 00 00\nPage 15: AC A1 0D E4\nPage 16: 00 00 00 00\nPage 17: 00 00 00 00\nPage 18: 00 00 00 00\nPage 19: 00 00 00 00\nPage 20: 00 00 00 00\nPage 21: 00 00 00 00\nPage 22: 00 00 00 00\nPage 23: 00 00 00 00\nPage 24: 00 00 00 00\nPage 25: 00 00 00 00\nPage 26: 00 00 00 00\nPage 27: 00 00 00 00\nPage 28: 00 00 00 00\nPage 29: 00 00 00 00\nPage 30: 00 00 00 00\nPage 31: 00 00 00 00\nPage 32: 00 00 00 00\nPage 33: 00 00 00 00\nPage 34: 00 00 00 00\nPage 35: 00 00 00 00\nPage 36: 00 00 00 BD\nPage 37: 00 00 00 FF\nPage 38: 00 05 00 00\nPage 39: FF FF FF FF\nPage 40: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc",
    "content": "Filetype: Flipper NFC device\nVersion: 4\n# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB\nDevice type: NTAG/Ultralight\n# UID is common for all formats\nUID: 04 BA FF CA 4D 5D 80\n# ISO14443-3A specific data\nATQA: 00 44\nSAK: 00\n# NTAG/Ultralight specific data\nData format version: 2\nNTAG/Ultralight type: Mifare Ultralight C\nSignature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nMifare version: 00 00 00 00 00 00 00 00\nCounter 0: 0\nTearing 0: 00\nCounter 1: 0\nTearing 1: 00\nCounter 2: 0\nTearing 2: 00\nPages total: 48\nPages read: 48\nPage 0: 04 BA FF C9\nPage 1: CA 4D 5D 80\nPage 2: 5A 48 00 00\nPage 3: E1 10 12 00\nPage 4: 01 03 A0 0C\nPage 5: 34 03 00 FE\nPage 6: 00 00 00 00\nPage 7: 00 00 00 00\nPage 8: 00 00 00 00\nPage 9: 00 00 00 00\nPage 10: 00 00 BE AF\nPage 11: 00 00 00 00\nPage 12: 00 00 00 00\nPage 13: 00 00 00 00\nPage 14: 00 00 00 00\nPage 15: 00 00 00 00\nPage 16: 00 00 00 00\nPage 17: 00 00 00 00\nPage 18: 00 00 00 00\nPage 19: 00 00 00 00\nPage 20: 00 00 00 00\nPage 21: 00 00 00 00\nPage 22: 00 00 00 00\nPage 23: 00 00 00 00\nPage 24: 00 00 00 00\nPage 25: 00 00 00 00\nPage 26: 00 00 00 00\nPage 27: 00 00 00 00\nPage 28: 00 00 00 00\nPage 29: 00 00 00 00\nPage 30: 00 00 00 00\nPage 31: 00 00 00 00\nPage 32: 00 00 00 00\nPage 33: 00 00 00 00\nPage 34: 00 00 00 00\nPage 35: 00 00 00 00\nPage 36: 00 00 00 00\nPage 37: 00 00 00 00\nPage 38: 00 00 00 00\nPage 39: 00 00 00 00\nPage 40: 00 00 00 00\nPage 41: 00 00 00 00\nPage 42: 05 00 00 00\nPage 43: 00 00 00 00\nPage 44: 00 00 00 00\nPage 45: 00 00 00 00\nPage 46: 00 00 00 00\nPage 47: 00 00 00 00\nFailed authentication attempts: 0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_long.nfc",
    "content": "Filetype: Flipper NFC test\nVersion: 1\nData length: 18\nPlain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9\nTimings length: 1304\nTimings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_short.nfc",
    "content": "Filetype: Flipper NFC test\nVersion: 1\nData length: 4\nPlain data: 14 d8 a0 c9\nTimings length: 296\nTimings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 37\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/storage/md5.txt",
    "content": "Yo dawg, I heard you like md5..."
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/alutech_at_4n_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811\nRAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131\nRAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411\nRAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811\nRAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/ansonic.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Ansonic\nBit: 12\nKey: 00 00 00 00 00 00 05 5A\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/ansonic_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510\nRAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172\nRAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477\nRAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100\nRAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86\nRAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/bett.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: BETT\nBit: 18\nKey: 00 00 00 00 00 00 B8 B8\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/bett_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok270Async\nProtocol: RAW\nRAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307\nRAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035\nRAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/came.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: CAME\nBit: 24\nKey: 00 00 00 00 00 6A B2 34\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/came_atomo_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 65 -4082 1356 -4436 197 -1394 65 -2280 67 -3048 165 -232 99 -7272 99 -958 65 -430 63 -2178 65 -2420 65 -6736 131 -4170 67 -4244 99 -1392 97 -5958 65 -1050 97 -5974 65 -2456 165 -1664 99 -13162 65 -3350 67 -1066 67 -98 65 -2668 63 -294 199 -1648 99 -2418 99 -916 99 -934 131 -102 97 -828 67 -964 65 -1644 263 -398 131 -1486 99 -7252 65 -432 65 -1752 99 -5028 331 -698 65 -266 199 -898 163 -916 65 -958 163 -268 97 -5754 131 -928 65 -2524 131 -1100 99 -1848 65 -3168 131 -6354 67 -1198 65 -1452 67 -6396 167 -3714 65 -4624 67 -464 99 -2326 63 -6984 97 -4866 67 -5602 99 -362 65 -1646 131 -6196 199 -5974 131 -798 199 -6046 97 -666 165 -630 99 -4796 99 -3394 129 -1282 221 -10562 1227 -558 609 -606 609 -586 581 -628 579 -600 575 -1224 1155 -1224 1155 -628 579 -622 583 -584 577 -620 589 -614 571 -1214 577 -602 1173 -1206 1173 -612 599 -584 599 -614 561 -616 587 -1216 1169 -620 581 -612 575 -1184 1197 -588 583 -1202 1199 -1204 575 -624 579 -588 1177 -1204 1177 -1206 1179 -608 585 -1212 1181 -620 581 -1184 1195 -588 609 -1182 581 -608 1205 -580 601 -1180 613 -598 1175 -612 595 -580 605 -580 603 -610 573 -616 575 -608 589 -77180 471 -4430 389 -796 387 -818 623 -198 131 -1440 991 -726 485 -668 539 -660 517 -668 539 -660 517 -666 539 -628 583 -1198 1181 -1198 587 -616 573 -618 1183 -598 579 -1214 585 -610 577 -588 1205 -1182 1207 -1166 615 -578 1215 -580 603 -1182 611 -598 581 -602 579 -592 613 -590 1179 -1206 587 -614 577 -610 591 -580 1201 -1180 607 -586 595 -612 1195 -590 579 -608 613 -1180 587 -612 1167 -1206 1197 -598 579 -622 581 -1178 1193 -592 613 -590 587 -77184 1223 -582 613 -1184 579 -608 611 -588 581 -592 1213 -1176 613 -590 1183 -602 579 -622 581 -588 613 -590 587 -1208 569 -614 597 -586 1195 -588 611 -1186 1175 -612 589 -1208 1169 -614 575 -612 591 -1176 611 -590 1209 -1176 579 -610 611 -594 1171 -1204 579 -606 1207 -580 601 -1182 1207 -580 601 -1178 1207 -1174 613 -590 1179 -608 579 -622 579 -1186 1191 -592 613 -1202 583 -612 579 -586 613 -578 1213 -578 601 -1182 1209 -582 601 -578 627 -586 589 -77176 991 -818 365 -1432 355 -834 367 -826 355 -764 1067 -684 531 -674 509 -652 561 -646 545 -618 553 -642 571 -622 553 -1240 1177 -1206 1177 -608 575 -614 561 -1204 1205 -1178 611 -596 1175 -1208 581 -602 1173 -1212 1175 -614 603 -578 601 -580 597 -1208 587 -588 1211 -1170 605 -590 1209 -582 589 -614 575 -1214 575 -622 593 -578 601 -586 1183 -1212 589 -588 613 -578 623 -588 1173 -614\nRAW_Data: 575 -618 583 -1174 1211 -578 609 -1186 601 -586 1193 -590 613 -590 587 -78186 1227 -586 613 -1184 579 -606 609 -574 613 -576 1203 -614 573 -1206 1171 -612 589 -612 575 -608 573 -1210 1197 -1178 603 -580 605 -586 1189 -626 581 -590 617 -1176 1201 -1194 593 -578 603 -610 571 -614 577 -610 1183 -1204 1183 -1200 1185 -1198 1185 -1198 1183 -1200 589 -612 1171 -618 581 -612 577 -588 613 -578 611 -1188 601 -584 601 -582 593 -616 1199 -582 577 -1216 1171 -1216 577 -604 1175 -1208 1207 -578 601 -580 597 -1206 1205 -142854 65 -8644 65 -198 65 -5350 65 -4006 65 -3572 67 -1684 65 -1488 67 -3324 131 -5538 133 -1516 63 -2528 65 -264 65 -3142 65 -884 97 -4344 199 -996 97 -1218 97 -5302 65 -4404 67 -2780 97 -1020 97 -2982 65 -864 65 -1580 65 -98 65 -298 67 -1066 131 -166 65 -4844 65 -2714 63 -6224 65 -98 65 -562 133 -398 133 -66 65 -3956 99 -3784 63 -1056 133 -3548 63 -952 163 -1912 65 -994 165 -2604 97 -4038 133 -6020 65 -632 99 -130 65 -64 65 -98 99 -132 129 -1218 97 -886 97 -164 65 -1680 131 -932 65 -234 97 -564 163 -1856 131 -166 101 -66 99 -6952 131 -3866 63 -66 97 -4254 99 -4154 99 -718 129 -228 197 -4720 99 -496 65 -3784 65 -2768 99 -460 65 -596 65 -234 65 -166 67 -1758 65 -298 97 -3296 163 -328 65 -1150 65 -1246 65 -5224 163 -790 99 -1860 133 -2214 67 -2056 65 -5288 65 -3048 131 -196 197 -10564 1229 -580 603 -586 611 -570 607 -602 579 -598 609 -1192 1201 -1170 1197 -612 573 -606 585 -614 569 -612 599 -578 605 -1206 589 -590 1173 -1220 1171 -622 561 -612 601 -586 599 -1208 1175 -580 627 -582 605 -580 589 -1212 1171 -616 573 -1212 1171 -1214 575 -622 561 -612 1197 -1184 1195 -1184 1199 -590 611 -1172 1211 -578 611 -1180 1183 -624 581 -1184 601 -582 1217 -588 579 -1184 597 -610 1195 -588 611 -578 611 -588 579 -594 611 -608 579 -590 611 -77158 627 -1612 63 -162 63 -786 467 -738 457 -718 1093 -1274 1101 -688 521 -640 535 -688 521 -640 537 -656 551 -638 571 -622 551 -1232 1173 -1208 583 -592 575 -620 1183 -602 575 -640 547 -640 575 -1184 1195 -1182 1199 -1214 575 -612 1185 -598 579 -1194 589 -614 575 -616 583 -614 575 -588 1207 -1184 613 -578 611 -592 577 -606 1207 -1174 587 -622 581 -586 1211 -582 589 -614 575 -1214 575 -622 1157 -1218 1165 -608 613 -586 581 -1186 1191 -594 613 -606 581 -77200 1215 -596 579 -1184 599 -612 595 -586 601 -580 1185 -1198 603 -612 1165 -606 613 -594 577 -602 579 -622 581 -1184 599 -614 595 -584 1197 -586 613 -1182 1173 -610 605 -1182\nRAW_Data: 587 -618 583 -578 1205 -1210 577 -588 1209 -1182 581 -608 611 -588 1175 -1210 581 -592 1207 -582 591 -1212 1175 -612 587 -1184 1201 -1186 601 -586 1195 -588 613 -588 587 -1208 1165 -630 579 -1184 613 -578 611 -592 577 -608 1203 -578 601 -1208 1169 -614 591 -578 605 -586 599 -77194 601 -4244 469 -724 455 -738 1097 -686 501 -678 491 -684 533 -678 525 -650 535 -644 529 -650 563 -1238 1141 -1234 1173 -606 577 -616 571 -1210 1173 -1212 1173 -1210 579 -604 583 -620 1173 -1212 1175 -610 571 -612 599 -580 605 -1206 575 -588 1209 -1184 581 -610 1207 -580 601 -578 597 -1208 577 -622 581 -588 613 -588 1181 -1204 589 -616 575 -588 595 -612 1199 -592 577 -604 579 -1222 1187 -574 615 -1170 607 -588 1207 -582 591 -616 589 -78180 1243 -580 585 -1206 573 -626 583 -590 587 -614 1171 -618 583 -1208 1177 -612 577 -588 613 -578 585 -1216 1179 -1216 583 -578 619 -588 1173 -616 587 -590 613 -1174 1181 -608 611 -586 581 -1216 573 -614 575 -622 1185 -1192 1187 -1196 1189 -1196 1187 -1192 1169 -1202 605 -586 1211 -584 587 -612 573 -618 583 -612 573 -1216 573 -622 561 -612 599 -584 1183 -618 575 -1216 1169 -1206 579 -606 1207 -1172 1203 -580 603 -620 585 -1176 1177 -78216 503 -12116 421 -790 435 -712 483 -1292 1119 -662 541 -1240 525 -642 1161 -1214 575 -614 599 -618 1153 -1224 1151 -630 579 -1214 1175 -1196 1179 -1214 1175 -588 615 -1180 1193 -1180 605 -612 575 -588 597 -610 1195 -1184 603 -588 1195 -586 613 -590 587 -1210 1169 -1206 603 -578 603 -580 1221 -1190 1197 -1174 1183 -1204 1183 -1196 1203 -1170 1199 -133240 99 -1258 131 -3150 229 -100 97 -3048 67 -400 199 -562 99 -2092 231 -300 199 -4556 99 -1878 65 -3834 457 -4170 163 -3484 131 -1380 97 -634 167 -3880 97 -1778 99 -3416 99 -2390 65 -2948 101 -4186 133 -2054 65 -98 65 -164 165 -1154 65 -3114 163 -3102 131 -626 197 -1662 67 -1494 65 -2650 97 -3266 99 -2436 263 -3076 65 -2718 65 -430 165 -68 99 -832 131 -2976 99 -464 97 -134 165 -66 133 -1660 65 -368 131\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/came_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1601 -32700 381 -296 671 -350 655 -644 343 -332 659 -330 703 -304 697 -314 697 -646 325 -348 647 -646 347 -656 321 -366 621 -23000 351 -324 677 -322 655 -654 335 -352 661 -336 679 -322 689 -296 703 -654 333 -318 665 -634 345 -672 343 -314 661 -22996 359 -306 673 -326 675 -626 359 -352 631 -330 681 -324 681 -344 689 -654 319 -326 645 -678 351 -622 353 -342 621 -23024 345 -304 669 -342 657 -652 325 -352 645 -354 653 -330 703 -346 653 -652 325 -350 649 -646 347 -652 321 -368 607 -23024 351 -322 653 -324 679 -622 363 -354 657 -324 677 -324 677 -346 655 -646 351 -328 679 -644 353 -624 351 -342 639 -22986 349 -328 659 -324 683 -658 329 -350 653 -324 673 -324 681 -342 657 -652 353 -326 653 -646 347 -654 321 -370 607 -23020 351 -324 679 -288 683 -654 367 -322 661 -336 681 -290 717 -310 695 -622 351 -326 679 -642 355 -624 351 -342 625 -23022 315 -366 637 -326 677 -648 345 -312 667 -348 667 -348 669 -334 679 -648 349 -312 667 -654 319 -664 345 -330 623 -23030 355 -312 657 -346 641 -648 351 -348 655 -352 629 -364 645 -354 655 -682 309 -346 637 -678 317 -650 347 -332 645 -23028 359 -324 629 -336 645 -678 349 -346 621 -350 667 -328 677 -324 681 -684 305 -354 629 -660 345 -640 349 -348 623 -23020 379 -354 545 -430 573 -716 273 -420 611 -382 609 -376 643 -372 611 -730 277 -364 611 -714 289 -702 297 -382 605 -23000 371 -304 657 -330 667 -666 321 -346 669 -336 679 -320 691 -296 701 -654 333 -318 667 -654 343 -638 353 -348 623 -22990 371 -324 665 -320 657 -654 333 -350 667 -346 637 -362 645 -354 655 -682 307 -348 635 -678 317 -686 311 -332 647 -23022 359 -324 627 -342 679 -644 351 -314 669 -342 665 -350 667 -312 703 -628 345 -312 665 -650 337 -672 321 -348 635 -23024 339 -326 671 -290 705 -614 375 -320 659 -340 679 -320 691 -296 701 -654 337 -320 667 -654 353 -620 373 -322 625 -23026 335 -322 663 -338 649 -644 387 -316 657 -348 675 -290 703 -324 675 -650 339 -356 629 -654 343 -638 387 -316 623 -22998 347 -342 637 -376 639 -654 343 -316 683 -346 655 -352 661 -334 677 -646 351 -314 655 -646 343 -658 351 -346 621 -22990 365 -360 567 -424 567 -720 269 -446 589 -374 611 -390 631 -392 617 -690 299 -378 609 -692 287 -708 311 -350 613 -23022 355 -314 637 -376 637 -676 317 -354 643 -356 655 -342 655 -350 659 -676 345 -298 685 -626 359 -652 325 -352 611 -23032 345 -304 685 -312 687 -650 325 -316 683 -324 681 -344 655 -354 659 -676\nRAW_Data: 311 -332 679 -618 353 -642 347 -352 619 -23042 307 -336 663 -344 657 -652 325 -352 645 -354 653 -344 657 -352 659 -674 345 -298 683 -626 359 -650 325 -354 615 -23032 349 -304 643 -378 623 -678 325 -352 647 -354 653 -344 655 -352 691 -644 345 -300 681 -624 361 -652 325 -352 617 -23032 387 -328 551 -474 521 -32700 375 -312 657 -344 655 -652 325 -354 677 -320 657 -344 689 -314 667 -688 319 -326 653 -644 349 -654 353 -340 619 -23020 351 -324 645 -354 657 -654 331 -350 667 -312 671 -330 681 -322 717 -616 343 -344 669 -650 325 -642 349 -332 643 -22998 391 -362 563 -402 577 -714 311 -376 621 -378 611 -394 637 -358 655 -688 303 -348 627 -696 311 -686 309 -346 621 -23010 367 -326 663 -320 657 -652 335 -350 667 -346 667 -332 647 -322 717 -648 345 -320 661 -628 347 -654 345 -352 621 -23012 361 -324 659 -322 653 -656 333 -350 667 -312 671 -328 679 -324 717 -648 345 -310 669 -650 325 -644 349 -332 641 -23026 353 -308 665 -310 665 -646 349 -350 647 -322 683 -344 693 -320 693 -644 347 -298 679 -626 361 -648 325 -352 619 -23030 347 -322 645 -322 685 -654 333 -352 665 -310 673 -330 679 -324 715 -648 345 -320 629 -658 345 -638 353 -350 623 -22998 389 -336 577 -408 577 -728 271 -414 625 -354 643 -370 639 -374 641 -674 289 -382 609 -714 311 -680 289 -370 603 -23028 351 -326 681 -288 683 -656 335 -354 659 -334 681 -320 689 -296 703 -654 331 -320 657 -648 353 -660 329 -350 635 -23008 341 -388 547 -428 539 -784 239 -420 579 -414 611 -406 581 -404 611 -724 275 -366 613 -714 289 -702 297 -382 603 -23032 347 -302 679 -310 653 -652 361 -314 681 -324 681 -346 655 -352 661 -674 311 -334 679 -626 359 -646 325 -352 647 -22992 383 -328 557 -440 557 -776 227 -422 575 -422 591 -408 625 -378 621 -706 295 -382 603 -684 305 -714 289 -366 605 -23016 351 -346 637 -326 679 -648 345 -312 697 -316 667 -348 669 -336 679 -644 353 -316 655 -646 345 -658 353 -314 623 -23020 363 -320 669 -322 641 -648 375 -318 659 -340 679 -320 693 -326 673 -656 335 -318 667 -634 345 -672 341 -316 657 -22982 387 -332 581 -408 587 -742 261 -418 609 -356 623 -410 621 -380 623 -706 271 -366 625 -712 293 -678 295 -382 609 -23032 353 -316 655 -324 679 -620 353 -346 669 -326 675 -324 679 -344 655 -678 319 -328 677 -644 353 -624 353 -342 623 -23026 323 -312 681 -324 681 -656 329 -352 629 -364 647 -354 655 -342 691 -656 319 -326 653 -646 349 -654 351 -340 621 -23018 351 -320\nRAW_Data: 647 -324 681 -624 363 -356 661 -298 715 -290 717 -296 701 -622 365 -320 659 -646 353 -626 361 -356 625 -22984 351 -330 657 -324 679 -656 331 -356 661 -332 653 -324 713 -310 691 -624 351 -326 655 -644 351 -658 319 -336 669 -22990 353 -348 617 -354 659 -658 331 -348 635 -376 639 -360 647 -324 683 -684 307 -354 627 -662 347 -654 345 -350 621 -23012 359 -324 629 -362 649 -648 345 -314 667 -350 667 -346 671 -324 673 -646 347 -350 623 -674 345 -622 349 -348 623 -23018 363 -324 659 -320 659 -654 333 -350 667 -346 635 -330 683 -324 715 -614 341 -356 629 -660 349 -654 345 -318 657 -23008 359 -322 627 -354 653 -654 333 -352 661 -334 645 -356 653 -330 703 -656 331 -348 637 -634 345 -672 311 -348 627 -23032 359 -322 659 -308 655 -680 347 -316 667 -314 699 -314 697 -318 671 -670 317 -346 637 -650 375 -636 323 -348 635 -23032 323 -352 641 -322 663 -652 369 -316 665 -348 669 -330 681 -316 689 -648 345 -310 667 -648 325 -646 345 -346 639 -22996 385 -302 675 -310 653 -650 361 -316 677 -324 679 -350 673 -346 651 -654 323 -352 649 -648 345 -654 321 -368 621 -23024 321 -346 653 -324 679 -620 353 -346 655 -324 699 -326 675 -346 653 -656 321 -352 647 -644 351 -664 321 -348 623 -23020 351 -324 647 -324 683 -654 335 -354 661 -334 647 -354 685 -318 675 -638 341 -346 669 -646 317 -648 381 -298 645 -23028 359 -288 663 -342 655 -644 381 -314 669 -312 697 -320 667 -338 679 -646 353 -314 655 -650 329 -670 345 -314 669 -32700 413 -310 647 -330 667 -666 321 -346 669 -336 679 -320 691 -296 701 -638 339 -354 629 -660 343 -640 351 -350 621 -23024 341 -326 671 -322 673 -650 343 -318 661 -340 677 -322 689 -318 679 -652 335 -356 623 -648 351 -656 337 -350 633 -23002 383 -354 543 -430 573 -746 241 -420 611 -378 621 -408 601 -392 605 -716 313 -346 621 -708 271 -692 313 -378 587 -23050 339 -326 669 -322 677 -614 377 -320 659 -340 681 -320 693 -294 703 -654 335 -322 661 -644 351 -632 375 -310 665 -23004 355 -370 543 -438 547 -760 225 -450 565 -420 587 -442 587 -412 593 -736 271 -364 623 -696 295 -710 261 -420 573 -23058 317 -330 677 -318 655 -652 369 -310 665 -352 665 -314 701 -298 715 -642 355 -318 657 -644 347 -620 387 -316 621 -23016 363 -394 533 -424 567 -754 235 -450 547 -446 575 -426 573 -422 591 -720 305 -382 591 -696 307 -688 309 -376 589 -23038 353 -308 665 -320 661 -646 389 -314 657 -322 697 -298 715 -288 719 -646 345 -308 665 -648\nRAW_Data: 347 -622 381 -298 647 -23000 393 -362 535 -468 537 -750 241 -442 555 -444 593 -398 607 -388 629 -718 273 -382 611 -704 257 -718 307 -362 589 -23058 357 -322 657 -310 683 -642 355 -318 689 -322 671 -322 675 -324 713 -614 375 -324 629 -654 343 -634 373 -314 661 -23002 359 -642 225 -568 101 -164 97 -1928 527 -468 537 -456 565 -752 271 -414 557 -736 271 -724 243 -444 555 -23034 381 -274 697 -320 667 -630 343 -346 673 -324 675 -344 653 -356 663 -638 347 -336 675 -622 353 -640 361 -312 645 -23022 345 -3786 131 -436 549 -460 571 -454 527 -788 237 -412 559 -762 239 -758 239 -410 591 -23042 337 -320 667 -322 679 -616 377 -320 659 -340 681 -320 691 -296 701 -638 375 -310 667 -616 349 -646 383 -298 647 -23016 359 -474 393 -570 331 -854 135 -940 555 -474 531 -460 571 -750 241 -412 587 -740 231 -742 255 -416 593 -23036 353 -320 659 -322 661 -644 355 -316 687 -320 669 -328 681 -324 713 -616 377 -288 667 -654 343 -634 389 -318 623 -22988 371 -394 511 -458 531 -790 233 -446 555 -438 569 -424 603 -392 617 -714 267 -414 593 -692 275 -722 273 -412 587 -23014 365 -324 673 -288 703 -616 375 -322 661 -338 683 -288 715 -296 701 -654 337 -322 663 -632 345 -670 341 -314 663 -22998 373 -364 525 -462 549 -766 225 -464 549 -428 573 -422 589 -420 583 -726 263 -410 579 -718 277 -724 267 -410 579 -23040 361 -322 631 -362 649 -648 345 -346 635 -350 659 -340 687 -322 693 -648 345 -312 667 -646 317 -686 345 -300 651 -23014 395 -290\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/came_twee.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: CAME TWEE\nBit: 54\nKey: 00 00 00 00 E7 1E 05 2E\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/came_twee_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 65 -918 131 -3388 1159 -100 65 -2632 65 -4574 65 -754 97 -9206 65 -5652 97 -428 99 -8322 99 -98 227 -1348 67 -300 65 -6066 99 -1760 65 -7064 67 -2742 65 -2912 295 -332 99 -3550 165 -266 199 -632 131 -5510 99 -1362 99 -2144 131 -2270 129 -100 331 -962 99 -704 233 -300 65 -66 99 -2010 197 -1162 165 -364 133 -132 97 -164 131 -196 131 -558 67 -98 231 -98 65 -886 65 -1326 165 -100 133 -5110 67 -962 65 -1492 65 -1320 199 -1854 65 -1428 97 -8926 581 -7682 257 -752 257 -740 269 -742 271 -690 361 -652 351 -1152 849 -642 375 -630 399 -1114 385 -626 883 -1106 903 -586 421 -584 447 -1084 917 -1072 939 -554 457 -1074 445 -550 443 -552 485 -514 971 -1044 975 -534 487 -514 473 -1044 979 -522 473 -536 473 -544 479 -1026 983 -1014 981 -1050 477 -518 987 -514 479 -522 475 -1068 431 -51352 467 -606 377 -626 397 -586 421 -582 415 -592 423 -610 375 -608 435 -586 423 -550 449 -554 445 -582 433 -574 443 -556 427 -1074 939 -550 471 -542 477 -1014 483 -544 969 -1032 967 -554 453 -530 495 -1042 975 -1020 969 -550 477 -1014 483 -514 479 -552 449 -556 985 -1026 983 -516 479 -528 487 -1022 977 -548 449 -546 477 -508 475 -1044 967 -1040 979 -1042 479 -512 1003 -524 471 -522 481 -1030 491 -51314 487 -622 325 -654 353 -656 355 -664 367 -606 405 -622 361 -618 387 -618 415 -580 443 -578 409 -590 425 -584 417 -580 417 -1086 939 -576 443 -560 429 -1074 447 -554 969 -1048 947 -550 455 -1036 1005 -528 463 -1044 973 -1042 445 -540 481 -522 999 -522 481 -530 465 -1044 977 -1016 975 -1056 451 -546 481 -516 977 -536 473 -1032 473 -552 449 -526 989 -546 443 -550 475 -522 475 -554 451 -1040 987 -51838 435 -696 259 -740 271 -702 305 -720 327 -650 353 -652 353 -642 373 -610 407 -592 393 -618 385 -620 385 -628 393 -610 411 -1096 919 -586 417 -598 395 -1104 409 -588 923 -1078 945 -550 477 -1030 455 -548 975 -1038 489 -522 975 -1018 481 -546 971 -1034 967 -1050 477 -522 983 -546 443 -1048 459 -552 981 -518 491 -512 481 -514 477 -550 475 -1016 1005 -528 465 -1042 977 -520 471 -1040 469 -548 451 -51346 477 -560 421 -612 409 -574 439 -588 391 -584 449 -556 421 -612 403 -604 407 -590 425 -582 417 -584 411 -576 445 -582 447 -1044 961 -548 449 -554 451 -1050 479 -530 961 -1044 975 -1030 975 -556 461 -514 481 -1040 475 -524 473 -538 471 -554 969 -1026 985 -514 473 -1034 473 -556 451 -544 963 -546 447 -552 481 -1020 967 -550 481 -520 493 -1006 475 -554 449 -554 475 -542 971 -514\nRAW_Data: 481 -1046 945 -554 485 -51810 487 -586 391 -618 417 -586 415 -580 399 -606 409 -594 427 -582 417 -580 415 -590 423 -578 441 -572 437 -552 455 -546 449 -1078 937 -550 479 -530 467 -1048 449 -552 979 -1048 949 -1044 975 -1018 991 -546 477 -516 491 -512 481 -1044 979 -1038 449 -558 959 -514 481 -554 449 -554 449 -546 469 -548 453 -548 447 -552 479 -538 441 -1050 485 -536 959 -1040 477 -524 485 -540 445 -538 965 -1076 939 -1056 473 -51318 493 -562 431 -582 417 -584 449 -556 423 -568 441 -574 443 -550 455 -546 483 -516 473 -540 475 -524 475 -552 453 -540 467 -1044 975 -524 473 -554 455 -1034 489 -512 975 -1044 967 -1050 481 -516 985 -514 475 -554 449 -554 475 -1038 463 -514 977 -554 449 -554 487 -512 469 -548 451 -542 481 -1014 979 -546 479 -1024 489 -514 979 -1052 459 -550 451 -528 493 -512 483 -514 1011 -520 469 -544 475 -1014 481 -514 1009 -51820 431 -650 353 -638 377 -630 397 -620 355 -652 353 -626 407 -612 409 -594 399 -618 387 -614 383 -590 413 -612 411 -576 441 -1084 915 -584 443 -548 441 -1084 451 -550 941 -1046 979 -1052 451 -546 477 -516 969 -556 451 -1044 989 -1014 981 -546 485 -1026 983 -516 479 -1016 983 -558 465 -512 483 -1044 979 -520 487 -1030 985 -516 481 -1022 487 -516 1001 -1010 975 -1052 481 -528 465 -514 481 -98632 67 -300 65 -700 65 -2382 65 -68 195 -798 99 -3430 65 -5332 65 -2574 97 -234 65 -2240 195 -4416 165 -1324 99 -4840 65 -1326 99 -1924 165 -4644 97 -4552 65 -5576 197 -3004 99 -2060 199 -2966 65 -330 197 -3684 65 -3090 67 -1032 99 -2390 97 -1094 65 -200 231 -792 329 -6554 65 -6300 131 -5596 65 -1292 65 -3076 65 -264 97 -3074 67 -3288 97 -3248 131 -228 163 -986 133 -2688 65 -460 99 -98 97 -1062 131 -628 67 -830 65 -1060 65 -332 99 -1290 263 -66 99 -2590 165 -726 131 -292 165 -332 131 -1860 65 -700 65 -666 99 -368 163 -3396 97 -5880 131 -5130 131 -7598 67 -1330 99 -1878 63 -1578 65 -1350 133 -11606 99 -2400 65 -858 67 -3516 133 -1524 65 -1648 99 -3808 99 -1918 65 -396 63 -2308 129 -1514 97 -562 97 -66 99 -3218 165 -592 65 -2110 65 -8596 63 -592 99 -5050 165 -7482 65 -396 199 -100 199 -626 65 -6278 65 -1392 97 -5676 67 -5218 99 -1826 163 -1738 65 -6246 65 -1146 129 -6776 131 -958 97 -1918 99 -1264 67 -98 133 -500 131 -432 131 -166 99 -960 65 -462 97 -2426 131 -1232 65 -5012 197 -2464 575 -510 479 -514 475 -520 507 -522 487 -514 473 -546 451 -546 479 -516 471 -538 471 -538 471 -538\nRAW_Data: 473 -522 487 -516 473 -1044 977 -520 471 -556 453 -1042 487 -512 979 -1048 471 -522 985 -522 493 -512 483 -514 475 -550 473 -1022 485 -518 1003 -1004 479 -552 979 -522 481 -532 465 -1042 477 -516 479 -526 487 -524 985 -1016 981 -1050 987 -1016 965 -544 479 -1010 1003 -1034 975 -520 493 -512 483 -51832 477 -548 483 -518 479 -542 449 -554 449 -544 473 -548 451 -548 481 -518 481 -526 483 -520 487 -536 463 -548 449 -548 445 -1050 985 -518 481 -524 493 -1044 445 -556 945 -1050 975 -548 453 -546 447 -1050 983 -1046 971 -514 483 -1044 449 -552 485 -512 471 -548 985 -1014 979 -544 449 -560 463 -1042 971 -544 451 -552 443 -572 443 -1044 979 -1016 999 -1014 473 -550 989 -514 475 -524 473 -1050 451 -51346 521 -524 463 -514 485 -548 447 -556 447 -554 449 -546 471 -548 453 -546 447 -550 477 -522 475 -538 475 -550 451 -540 467 -1046 979 -518 485 -514 473 -1046 481 -518 977 -1050 975 -520 459 -1072 937 -540 473 -1050 975 -1052 449 -548 443 -542 969 -552 485 -512 473 -1048 977 -1046 949 -1058 453 -544 481 -516 977 -552 451 -1074 427 -546 483 -516 977 -552 451 -544 469 -546 453 -548 447 -1044 999 -51812 519 -530 465 -514 481 -548 479 -516 481 -526 457 -554 487 -496 495 -512 483 -546 479 -514 473 -522 471 -558 487 -512 471 -1042 977 -526 459 -544 487 -1028 487 -512 973 -1048 965 -554 451 -1050 451 -544 975 -1046 473 -510 1009 -1026 451 -544 975 -1046 967 -1018 509 -516 985 -514 477 -1056 459 -518 981 -548 453 -546 479 -516 473 -520 507 -1018 977 -522 495 -1038 973 -524 471 -1018 487 -526 493 -51324 491 -542 455 -548 449 -552 447 -586 433 -546 477 -522 457 -548 483 -548 445 -552 443 -552 483 -548 439 -550 453 -548 449 -1078 935 -554 481 -528 467 -1044 445 -554 975 -1020 977 -1044 973 -520 473 -554 471 -1040 465 -550 453 -546 447 -550 977 -1020 981 -550 487 -1008 477 -524 485 -540 965 -546 445 -550 455 -1038 977 -554 473 -542 443 -1048 481 -514 477 -522 487 -552 949 -558 461 -1042 971 -524 475 -51838 493 -554 409 -592 427 -584 421 -586 413 -576 445 -574 413 -582 447 -576 435 -550 455 -550 447 -546 443 -574 445 -584 449 -1044 959 -548 445 -554 447 -1066 441 -552 983 -1018 977 -1050 987 -1010 1001 -512 481 -514 475 -550 473 -1016 997 -1016 505 -518 983 -514 479 -522 471 -556 487 -514 469 -546 451 -544 481 -516 479 -524 471 -1032 471 -554 983 -1014 483 -546 445 -552 449 -556 961 -1036 987 -1038 475 -51318 513 -562 401 -570 441 -588 425 -582 417 -586 413 -594 423 -572\nRAW_Data: 447 -572 435 -550 419 -584 449 -576 413 -562 459 -550 445 -1056 989 -518 481 -524 475 -1050 449 -560 955 -1044 975 -1018 483 -558 963 -516 479 -524 483 -522 489 -1032 489 -512 977 -522 485 -536 473 -522 487 -540 463 -514 481 -1042 973 -556 453 -1036 489 -512 979 -1016 521 -522 477 -518 485 -512 479 -514 1003 -522 471 -540 487 -1016 467 -548 981 -51816 509 -512 477 -556 453 -536 443 -576 447 -558 463 -512 483 -548 445 -552 447 -554 485 -514 473 -548 455 -546 479 -518 481 -1018 1001 -518 481 -520 493 -1040 443 -552 967 -1034 967 -1046 469 -550 451 -546 973 -522 473 -1052 977 -1020 979 -552 441 -1048 971 -560 463 -1042 979 -518 485 -514 471 -1048 977 -524 455 -1050 977 -548 453 -1044 481 -524 971 -1050 973 -1044 445 -552 447 -554 485 -51302 523 -534 465 -548 451 -550 447 -556 443 -564 449 -550 441 -586 429 -546 481 -516 477 -522 473 -552 485 -512 471 -548 453 -1078 939 -538 473 -540 471 -1034 485 -512 969 -1044 481 -526 987 -512 479 -562 461 -514 483 -516 479 -1054 447 -554 975 -1042 447 -554 977 -520 479 -534 469 -1044 447 -552 449 -564 483 -518 977 -1040 971 -1038 953 -1044 967 -546 483 -1048 947 -1048 973 -546 451 -548 447 -51842 475 -620 353 -644 373 -644 341 -656 395 -616 387 -586 415 -612 385 -598 409 -610 405 -592 427 -584 417 -584 413 -562 459 -1076 941 -550 453 -550 449 -1044 475 -552 953 -1056 451 -546 975 -520 473 -1032 473 -554 485 -498 993 -546 445 -554 447 -1048 487 -536 463 -516 481 -548 479 -516 975 -554 451 -1036 491 -510 975 -554 449 -1052 983 -1016 981 -522 485 -540 475 -1040 463 -548 451 -546 977 -520 471 -1038 467 -51324 485 -3800 195 -672 307 -694 329 -652 355 -650 353 -642 375 -642 375 -624 363 -620 419 -586 419 -1090 925 -572 439 -586 423 -1076 413 -592 939 -1042 477 -524 989 -1046 945 -1034 487 -540 963 -1044 977 -1030 975 -522 495 -1042 445 -554 975 -1018 483 -524 493 -512 481 -546 475 -506 473 -540 973 -1048 487 -498 995 -514 477 -554 447 -556 485 -1010 989 -1044 977 -51826 499 -7642 327 -652 355 -654 355 -674 333 -634 405 -622 361 -1144 879 -612 411 -574 439 -1082 419 -584 911 -1076 447 -566 963 -1042 443 -560 445 -570 441 -546 477 -558 955 -548 445 -1050 983 -1040 463 -514 483 -546 445 -554 975 -1018 483 -558 463 -514 979 -1052 947 -542 473 -548 455 -544 481 -514 477 -522 473 -1050 485 -534 959 -1034 477 -524 485 -93988 65 -6444 63 -964 65 -428 97 -1692 65 -9326 99 -4816 97 -1218 67 -2008 97 -522 97 -1860 99 -1962\nRAW_Data: 65 -504 65 -798 65 -732 67 -560 65 -824 65 -66 195 -1780 65 -656 65 -100 99 -366 133 -902 133 -132 65 -164 97 -2648 165 -1950 199 -2336 99 -468 165 -1296 131 -232 527 -100 259 -66 131 -5558 99 -792 65 -660 99 -2094 163 -1154 165 -9190 199 -166 331 -166 299 -3056 267 -66 65 -298 65 -1422 97 -2908 129 -496 199 -132 131 -2308 165 -364 131 -332 67 -632 101 -1090 231 -1018 197 -66 163 -394 65 -628 65 -460 95 -954 65 -1932 67 -66 65 -1188 97 -1394 131 -98 395 -432 791 -1332 263 -1328 97 -270 67 -962 231 -2222 131 -168 65 -500 65 -630 99 -958 63 -164 65 -980 97 -396 131 -2674 65 -3818 131 -1234 65 -134 65 -200 65 -1394 97 -2108 329 -460 227 -132 195 -426 97 -630 293 -4052 65 -2470 65 -434 133 -400 67 -600 65 -1726 67 -5132 197 -5216 131 -2552 199 -1846 65 -6540 97 -390 97 -132 63 -726 65 -1002 131 -66 133 -4454 131 -1086 65 -3068 65 -66 97 -1022 65 -594 163 -1304 97 -900 99 -364 133 -1004 97 -1760 197 -268 99 -2812 67 -400 97 -432 99 -6002 65 -66 163 -434 131 -432 197 -1460 165 -8400 65 -524 65 -6948 97 -728 65 -166 97 -4386 97 -266 97 -2942 395 -6642 99 -204 65 -1030 67 -468 99 -66 131 -1098 133 -1060 163 -4446 65 -2920 65 -134 99 -4124 67 -6956 67 -1392 99 -6610 133 -1548 65 -1712 99 -2112 99 -3776 165 -896 65 -428 97 -760 197 -1448 97 -936 233 -98 163 -5932 129\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/cenmax_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 32700 -1024 977 -1010 971 -1046 967 -1012 1007 -1014 1001 -1008 503 -528 523 -518 485 -550 259 -288 491 -556 231 -296 501 -560 265 -268 267 -270 497 -522 489 -550 259 -290 227 -298 267 -306 239 -308 241 -274 507 -524 261 -256 259 -296 265 -268 503 -558 495 -554 229 -296 503 -560 493 -522 259 -256 257 -296 497 -526 525 -520 261 -292 499 -560 497 -524 263 -260 525 -520 261 -290 227 -298 267 -270 505 -558 231 -332 237 -274 507 -524 521 -518 487 -548 259 -286 227 -298 269 -306 239 -308 239 -276 507 -522 261 -258 261 -296 263 -268 269 -268 271 -306 239 -306 241 -274 273 -270 499 -522 489 -550 259 -288 227 -298 503 -558 233 -332 237 -274 999 -1012 977 -1016 1007 -1018 977 -1014 985 -1008 1013 -1014 521 -524 493 -552 477 -546 239 -314 477 -558 231 -298 501 -562 265 -268 267 -270 497 -520 521 -518 259 -290 227 -300 269 -306 239 -306 241 -276 507 -522 261 -258 259 -296 265 -266 501 -558 497 -556 229 -296 503 -558 493 -522 259 -256 257 -294 497 -556 493 -522 263 -292 499 -560 499 -522 263 -260 523 -522 259 -292 227 -298 267 -270 505 -560 229 -300 269 -274 505 -524 489 -550 487 -550 259 -286 227 -298 269 -306 239 -306 241 -276 507 -522 261 -256 259 -296 267 -268 267 -270 271 -304 239 -306 241 -276 273 -270 497 -522 489 -550 259 -290 225 -298 505 -558 231 -300 269 -272 1001 -1008 979 -1014 1009 -1020 977 -1014 997 -1010 983 -1040 507 -508 511 -546 477 -546 257 -280 513 -520 261 -296 499 -558 265 -268 269 -268 497 -522 489 -552 261 -288 227 -296 271 -304 241 -306 241 -276 507 -522 259 -258 259 -296 267 -266 503 -558 495 -556 229 -296 501 -562 495 -518 259 -256 257 -296 497 -524 525 -520 261 -294 499 -558 499 -524 263 -260 523 -522 259 -290 227 -298 267 -270 505 -558 231 -300 271 -272 505 -526 489 -552 485 -550 227 -318 225 -298 269 -306 239 -306 241 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -308 239 -276 273 -270 497 -522 489 -550 261 -288 227 -296 505 -558 231 -300 271 -272 999 -1012 981 -1024 1007 -992 1007 -1010 1003 -982 1001 -1048 479 -548 477 -548 479 -546 257 -282 511 -554 231 -294 499 -560 265 -268 269 -268 497 -522 521 -520 259 -290 225 -298 269 -306 239 -308 239 -276 507 -522 261 -258 259 -296 265 -266 503 -556 495 -556 229 -296 503 -560 493 -520 261 -256 257 -294 499 -524 525 -520 261 -294 499 -560 497 -524 261 -262 523 -522 259 -292 227 -296 267 -270 505 -558 231 -332 237 -274\nRAW_Data: 505 -526 491 -550 485 -550 227 -318 225 -298 269 -306 239 -306 241 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -306 241 -276 271 -270 499 -522 489 -550 259 -288 227 -298 505 -560 231 -298 271 -272 999 -1012 977 -1018 1003 -1020 977 -1016 1001 -1008 979 -1040 515 -518 483 -552 485 -552 225 -306 501 -548 227 -318 493 -556 267 -266 267 -270 497 -522 489 -552 259 -288 227 -300 267 -304 239 -308 241 -276 505 -524 261 -258 259 -296 265 -268 501 -558 495 -554 229 -296 503 -528 527 -520 259 -256 257 -296 497 -524 525 -520 261 -294 501 -558 499 -524 261 -260 523 -522 259 -290 227 -298 269 -268 505 -560 231 -300 269 -274 505 -524 489 -552 487 -550 227 -286 257 -296 269 -306 239 -308 239 -276 507 -522 261 -258 259 -296 265 -266 269 -270 269 -306 239 -308 241 -274 273 -270 497 -522 487 -552 261 -288 225 -298 503 -558 233 -332 237 -274 999 -1010 977 -1016 1007 -1020 977 -1014 999 -1014 993 -1012 513 -514 513 -548 479 -548 225 -310 509 -520 261 -294 499 -558 267 -268 269 -268 497 -524 487 -552 259 -288 227 -298 269 -306 239 -308 241 -274 507 -524 259 -258 259 -296 265 -268 501 -558 497 -552 231 -296 501 -560 493 -520 261 -256 257 -294 497 -558 493 -554 129 -32700 261 -332 131 -534 329 -98 32700 -1002 979 -1048 989 -1006 989 -1012 977 -1024 1007 -1034 225 -284 255 -292 263 -264 267 -268 501 -558 231 -298 267 -306 477 -560 231 -296 229 -296 495 -522 523 -520 521 -518 261 -294 265 -304 239 -276 273 -272 497 -522 259 -258 521 -524 261 -296 497 -558 231 -300 505 -528 525 -522 485 -550 483 -550 485 -548 487 -550 261 -294 495 -560 265 -268 267 -270 497 -522 261 -260 259 -296 267 -268 505 -560 231 -298 271 -274 507 -522 521 -520 485 -548 259 -288 227 -296 271 -306 239 -308 239 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -272 273 -306 241 -274 273 -270 497 -522 489 -550 259 -288 261 -264 503 -560 231 -300 269 -274 999 -1012 977 -1024 1007 -992 1009 -1000 1005 -982 1033 -1016 257 -248 279 -252 259 -296 265 -268 503 -560 231 -296 267 -304 477 -560 231 -296 229 -296 495 -522 523 -518 523 -520 261 -292 265 -304 239 -276 273 -270 497 -522 259 -290 491 -522 263 -294 497 -558 231 -300 505 -528 527 -522 485 -548 483 -550 485 -550 487 -552 259 -292 495 -560 265 -268 267 -268 497 -522 261 -292 227 -296 267 -270 505 -558 231 -300 269 -274 507 -524 487 -550 487 -550 257 -288 227 -296 271 -304 239 -308\nRAW_Data: 241 -276 505 -524 259 -258 259 -296 265 -268 269 -270 271 -270 273 -308 239 -276 273 -270 497 -522 489 -550 259 -288 227 -298 505 -558 231 -300 269 -274 997 -1008 1009 -988 1009 -996 1013 -1014 991 -1014 991 -1012 271 -288 251 -256 253 -292 251 -288 485 -558 231 -300 269 -306 475 -560 229 -296 229 -298 495 -524 523 -518 521 -518 261 -294 263 -304 241 -274 273 -270 499 -520 261 -290 491 -524 261 -294 499 -558 231 -298 503 -562 493 -520 487 -548 485 -548 487 -550 485 -550 261 -290 497 -558 267 -266 269 -268 497 -522 263 -290 227 -296 267 -268 505 -560 231 -300 271 -272 505 -524 489 -552 487 -550 225 -318 227 -296 271 -304 241 -306 241 -276 505 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -308 241 -274 273 -270 497 -522 489 -550 259 -288 227 -298 501 -560 231 -334 237 -274 1001 -1012 979 -1014 1005 -986 1007 -1014 1001 -1006 983 -1040 271 -254 253 -292 253 -292 253 -284 481 -558 231 -296 267 -304 477 -562 231 -294 229 -296 497 -524 489 -550 489 -552 261 -292 265 -302 239 -276 273 -270 499 -522 259 -290 491 -522 263 -294 499 -558 231 -296 503 -560 495 -520 487 -550 485 -548 487 -548 489 -548 261 -290 497 -558 265 -268 269 -268 497 -522 261 -292 227 -296 267 -268 507 -558 231 -298 271 -272 507 -522 489 -552 487 -550 227 -286 257 -298 269 -306 239 -308 241 -274 507 -524 259 -258 257 -296 265 -268 269 -268 271 -306 239 -306 241 -276 273 -270 497 -522 489 -550 259 -288 227 -296 505 -558 231 -300 271 -272 997 -1014 975 -1016 1009 -1020 977 -1016 1001 -1012 989 -1044 237 -288 253 -290 217 -292 253 -290 485 -562 231 -298 269 -306 475 -562 229 -296 229 -296 495 -524 523 -518 523 -518 261 -292 265 -304 239 -274 273 -270 499 -520 261 -292 491 -524 261 -294 495 -558 231 -300 503 -562 493 -522 485 -550 485 -548 485 -550 487 -552 261 -290 497 -560 231 -300 267 -270 497 -522 261 -290 227 -296 267 -268 507 -558 231 -300 269 -274 505 -524 519 -518 487 -550 259 -288 227 -296 271 -304 239 -308 241 -274 507 -524 259 -260 259 -294 233 -300 267 -270 271 -306 239 -308 239 -276 271 -270 497 -522 489 -550 259 -288 227 -298 501 -560 231 -334 237 -274 1001 -1010 979 -1014 1007 -1020 977 -1016 1001 -1008 979 -1044 237 -288 255 -290 255 -290 251 -282 479 -556 231 -296 269 -304 475 -562 231 -296 229 -296 495 -524 521 -518 523 -518 261 -292 265 -304 239 -276 273 -270 497 -522 261 -290 491 -524 263 -292 497 -558 231 -298\nRAW_Data: 505 -560 495 -520 485 -550 485 -548 485 -552 487 -550 261 -290 497 -558 267 -266 267 -268 497 -522 263 -290 227 -296 267 -270 505 -560 231 -298 271 -272 507 -524 487 -552 485 -550 227 -318 227 -298 269 -306 239 -306 241 -276 505 -522 261 -258 259 -296 265 -268 267 -270 271 -306 237 -308 241 -274 273 -270 497 -522 489 -550 259 -288 227 -300 503 -558 231 -300 269 -272 999 -1008 1007 -988 1005 -996 1013 -1012 987 -1004 997 -1044 225 -278 247 -284 255 -296 267 -268 503 -558 231 -296 269 -304 475 -562 229 -296 229 -296 497 -524 521 -518 523 -518 261 -292 265 -302 241 -276 273 -270 497 -522 259 -290 491 -524 263 -292 499 -556 231 -300 505 -558 495 -522 485 -548 485 -550 485 -550 485 -550 261 -292 497 -558 265 -268 267 -270 497 -520 261 -290 229 -296 267 -270 505 -558 231 -300 271 -272 507 -524 489 -550 485 -548 259 -288 225 -298 269 -306 239 -308 241 -274 507 -524 261 -258 259 -294 265 -266 269 -270 269 -306 239 -306 241 -274 273 -270 497 -524 487 -550 259 -290 227 -296 503 -558 231 -334 237 -274 997 -1014 977 -1016 1007 -1018 977 -1016 1001 -1008 981 -1038 257 -274 245 -280 251 -290 263 -266 503 -560 231 -294 267 -306 475 -562 229 -296 229 -296 495 -522 523 -520 521 -518 261 -294 265 -302 239 -276 273 -270 499 -522 259 -290 491 -524 261 -294 497 -556 233 -298 505 -560 493 -520 487 -550 485 -550 485 -550 487 -550 259 -292 495 -558 267 -266 269 -268 497 -522 261 -288 227 -298 269 -270 505 -556 231 -298 271 -274 505 -524 489 -550 485 -548 261 -288 225 -298 269 -306 239 -306 241 -274 507 -524 261 -258 259 -294 265 -268 269 -268 271 -306 237 -308 241 -274 273 -270 497 -522 489 -548 261 -288 227 -296 503 -560 231 -300 271 -272 997 -1010 1003 -1004 999 -1008 991 -1010 1011 -982 1009 -1020 257 -282 251 -286 227 -296 265 -268 503 -558 231 -298 267 -304 477 -560 231 -296 227 -296 495 -524 523 -520 489 -550 261 -292 265 -304 239 -276 273 -270 499 -520 259 -290 491 -522 263 -294 499 -558 231 -296 505 -560 493 -522 485 -550 485 -550 485 -548 489 -548 261 -290 497 -558 267 -266 269 -268 497 -522 261 -290 227 -296 267 -270 505 -560 231 -298 269 -272 507 -524 491 -550 485 -548 259 -288 225 -298 269 -306 239 -308 241 -274 507 -524 259 -258 257 -296 265 -266 269 -270 271 -304 239 -308 241 -274 273 -270 497 -522 489 -550 259 -288 227 -298 505 -558 231 -300 269 -274 997 -1012 979 -1024 1007 -992 1007 -1016 991 -1008\nRAW_Data: 989 -1042 237 -288 253 -290 255 -254 255 -288 487 -560 231 -300 269 -306 475 -562 229 -296 229 -296 495 -524 523 -518 489 -550 261 -292 265 -304 239 -276 273 -270 497 -522 259 -290 493 -524 263 -292 497 -556 231 -300 505 -560 493 -520 485 -550 485 -548 487 -548 487 -550 259 -292 497 -560 265 -268 267 -268 497 -522 259 -292 227 -296 267 -270 505 -558 233 -300 269 -272 507 -524 489 -550 487 -550 227 -286 257 -296 269 -306 239 -308 241 -274 507 -524 259 -258 257 -296 267 -266 269 -270 269 -306 239 -306 241 -274 273 -270 497 -522 489 -550 259 -288 227 -298 503 -560 231 -332 237 -274 999 -1012 981 -1016 1003 -986 1009 -1018 1003 -1006 983 -1044 237 -292 253 -292 217 -292 249 -280 509 -556 229 -296 267 -306 477 -560 229 -298 229 -296 495 -522 523 -518 523 -518 261 -292 265 -304 239 -274 273 -272 497 -522 259 -290 491 -524 261 -294 497 -558 231 -300 503 -560 493 -520 487 -548 487 -548 485 -548 487 -550 261 -292 495 -560 265 -268 267 -268 497 -522 263 -290 227 -296 267 -270 503 -560 231 -300 269 -274 505 -526 489 -552 485 -550 227 -286 257 -296 269 -306 239 -306 241 -276 507 -522 259 -258 259 -296 265 -268 267 -270 271 -306 239 -306 241 -276 273 -270 495 -522 487 -552 261 -288 225 -298 503 -558 231 -332 239 -274 999 -1012 979 -1016 1007 -986 1009 -1016 999 -1012 987 -1012 271 -288 253 -290 219 -290 255 -288 485 -558 231 -300 269 -306 475 -560 231 -296 229 -296 495 -524 523 -518 521 -518 261 -292 265 -302 241 -276 273 -270 497 -522 259 -290 491 -524 261 -294 497 -558 231 -298 507 -560 493 -522 485 -548 485 -548 487 -550 489 -548 261 -290 497 -560 233 -300 267 -266 497 -522 261 -290 227 -298 267 -268 505 -558 231 -300 269 -274 505 -524 491 -550 485 -548 259 -286 227 -298 269 -306 239 -308 241 -274 507 -522 261 -256 259 -296 267 -266 269 -270 271 -304 239 -306 241 -276 271 -270 499 -522 487 -550 261 -288 225 -298 505 -558 231 -300 269 -274 997 -1010 981 -1016 1003 -1020 971 -1006 1025 -1008 987 -1042 225 -276 247 -282 255 -294 265 -268 503 -558 229 -296 267 -304 477 -562 229 -296 229 -296 495 -524 523 -518 523 -518 261 -292 265 -304 239 -274 273 -272 497 -522 259 -292 491 -524 263 -294 497 -558 231 -298 503 -560 493 -520 487 -550 485 -548 485 -548 487 -550 261 -292 497 -560 231 -300 269 -268 497 -522 259 -290 227 -298 267 -268 505 -560 231 -332 237 -274 505 -524 489 -550 485 -548 259 -288 227 -300\nRAW_Data: 267 -304 239 -308 241 -274 509 -524 259 -258 257 -296 265 -268 269 -268 271 -306 239 -306 241 -274 273 -270 497 -522 487 -550 261 -288 227 -298 503 -558 233 -298 271 -274 999 -1012 979 -1016 1007 -1016 975 -1002 999 -1018 995 -1046 237 -288 251 -292 253 -256 253 -290 485 -562 233 -298 269 -306 475 -562 229 -296 229 -298 497 -522 491 -550 491 -548 261 -292 265 -304 239 -274 273 -270 499 -520 261 -290 491 -522 263 -294 497 -558 231 -298 505 -558 495 -520 487 -548 485 -550 485 -548 487 -550 261 -292 495 -558 267 -268 267 -268 499 -522 261 -290 227 -296 267 -270 505 -558 233 -298 269 -272 507 -522 489 -552 487 -550 227 -286 257 -296 271 -304 239 -308 239 -32700 165 -164 97 -1950 465 -232 393 -198 557 -7530 865 -1122 915 -1114 159 -346 187 -348 187 -348 445 -618 419 -584 455 -618 195 -354 429 -596 459 -554 227 -320 225 -294 229 -332 467 -560 229 -330 467 -560 497 -558 229 -296 229 -296 229 -300 497 -558 229 -294 265 -302 237 -308 243 -274 273 -270 233 -298 263 -266 265 -268 501 -558 231 -296 267 -306 241 -276 273 -270 497 -522 261 -258 257 -296 267 -270 507 -556 265 -266 271 -274 507 -524 519 -518 487 -548 259 -256 291 -264 269 -274 273 -310 241 -274 505 -524 259 -258 257 -296 265 -268 269 -270 271 -272 273 -306 241 -276 273 -270 495 -522 521 -516 259 -290 259 -264 505 -558 265 -266 271 -274 1001 -1010 1005 -990 1005 -996 1007 -1014 987 -1016 993 -1010 257 -276 247 -282 255 -294 499 -522 527 -520 523 -520 263 -296 503 -526 527 -520 259 -256 257 -294 265 -268 503 -558 231 -296 501 -560 497 -524 261 -262 259 -296 265 -266 497 -524 261 -296 265 -270 271 -308 241 -274 273 -270 233 -298 231 -298 265 -268 503 -556 231 -296 267 -306 239 -276 273 -270 497 -522 259 -258 291 -266 267 -270 505 -558 265 -266 271 -272 507 -522 521 -516 485 -550 257 -288 261 -266 269 -272 275 -306 241 -276 505 -522 261 -256 259 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -520 521 -518 261 -256 291 -264 503 -560 265 -266 269 -274 999 -1008 1003 -986 1007 -1004 1013 -980 1023 -1008 991 -1042 225 -276 247 -282 255 -294 501 -524 527 -518 527 -520 261 -296 503 -526 525 -520 259 -256 257 -294 265 -268 503 -526 263 -296 501 -560 497 -524 261 -262 261 -296 263 -266 499 -522 263 -294 265 -270 271 -308 241 -276 273 -270 233 -298 229 -298 265 -268 501 -558 231 -298 267 -304 241 -276 273 -270 495 -522 259 -290 261 -264\nRAW_Data: 269 -270 505 -558 231 -300 271 -272 507 -522 521 -518 485 -550 259 -254 291 -264 271 -272 273 -308 241 -274 507 -524 259 -258 257 -296 265 -268 269 -270 271 -272 271 -308 241 -276 271 -270 497 -520 521 -520 259 -256 291 -266 503 -558 265 -266 271 -274 999 -1014 977 -1016 1007 -984 1007 -1014 999 -1016 991 -1010 271 -290 253 -254 291 -254 495 -542 499 -558 493 -522 263 -296 503 -526 525 -522 259 -256 257 -294 267 -268 503 -554 231 -298 501 -558 499 -524 261 -260 261 -298 231 -298 499 -524 261 -294 265 -268 273 -308 241 -276 271 -270 233 -298 231 -298 265 -266 503 -558 231 -298 267 -304 241 -274 273 -270 499 -520 259 -258 291 -266 267 -270 507 -558 233 -296 269 -274 505 -524 521 -520 485 -548 259 -256 291 -264 269 -272 275 -308 239 -276 507 -522 261 -256 259 -296 265 -268 267 -270 271 -272 273 -306 241 -276 271 -270 497 -520 523 -518 259 -290 259 -264 503 -560 265 -266 269 -274 999 -1008 1005 -986 1017 -994 1007 -1012 991 -1014 991 -1012 259 -276 245 -282 253 -294 499 -522 527 -520 525 -518 263 -296 501 -526 527 -520 259 -256 257 -294 265 -268 503 -558 231 -296 503 -558 501 -522 263 -260 259 -294 263 -266 499 -524 261 -296 265 -270 273 -308 241 -274 273 -270 233 -296 231 -296 267 -268 503 -556 231 -296 267 -304 241 -276 273 -270 499 -522 259 -256 259 -296 267 -272 505 -558 233 -298 271 -272 507 -522 521 -516 485 -550 259 -288 259 -264 271 -272 273 -308 241 -276 507 -522 261 -256 259 -296 265 -266 269 -270 271 -272 273 -308 241 -274 273 -270 497 -520 521 -518 259 -288 261 -266 503 -558 265 -266 271 -274 999 -1010 981 -1026 981 -1026 1007 -978 997 -1012 1019 -1006 255 -276 245 -280 255 -294 497 -524 525 -520 525 -520 261 -296 503 -526 527 -520 259 -256 257 -294 267 -268 503 -558 231 -296 499 -560 497 -524 261 -262 261 -296 263 -266 499 -524 261 -294 267 -268 273 -308 239 -276 273 -270 233 -296 231 -298 265 -268 503 -558 231 -296 267 -304 241 -274 273 -270 499 -520 259 -258 259 -298 267 -270 505 -558 265 -266 271 -274 507 -522 521 -516 485 -548 259 -288 259 -266 269 -272 275 -306 243 -274 507 -524 259 -258 257 -296 265 -268 269 -270 269 -272 273 -308 241 -276 271 -270 497 -520 521 -518 259 -290 259 -264 505 -560 263 -266 271 -274 999 -1008 1003 -986 1009 -1000 1011 -1006 1001 -1000 1003 -1018 259 -280 249 -286 261 -264 501 -522 527 -520 525 -518 263 -296 503 -526 525 -522 259 -256 257 -294 267 -266\nRAW_Data: 505 -556 231 -296 501 -558 499 -522 263 -260 261 -296 265 -264 501 -524 261 -294 265 -270 271 -308 241 -274 273 -270 233 -298 231 -298 265 -266 503 -558 231 -296 267 -306 239 -276 273 -270 499 -522 259 -258 257 -296 269 -270 505 -560 231 -298 269 -272 507 -522 521 -518 487 -548 259 -288 259 -264 271 -272 273 -308 241 -276 507 -522 259 -258 257 -296 267 -268 269 -268 271 -272 273 -306 241 -276 271 -270 497 -522 519 -516 261 -288 261 -266 503 -558 265 -266 271 -274 997 -1014 979 -1014 1007 -984 1009 -1016 987 -1006 1013 -1014 273 -290 255 -254 253 -290 485 -526 527 -522 523 -520 263 -294 505 -526 525 -520 259 -256 257 -294 265 -268 501 -558 231 -298 501 -558 499 -522 263 -260 261 -296 263 -266 499 -522 263 -294 267 -268 273 -308 241 -274 273 -270 233 -298 231 -298 265 -266 503 -556 231 -296 269 -306 241 -274 273 -270 497 -520 261 -288 227 -298 269 -270 505 -556 265 -266 271 -274 507 -522 521 -516 487 -548 259 -288 259 -264 269 -272 275 -308 241 -274 507 -522 261 -256 259 -296 265 -268 267 -270 271 -272 275 -306 241 -276 271 -270 497 -520 521 -518 259 -288 259 -266 503 -558 267 -266 269 -274 999 -1006 1005 -986 1007 -1004 1011 -1014 987 -1014 993 -1012 271 -290 253 -254 255 -290 491 -536 503 -558 495 -554 231 -298 503 -524 527 -520 259 -256 257 -296 265 -268 501 -558 231 -298 501 -558 499 -524 261 -260 261 -296 263 -266 499 -522 263 -294 265 -270 273 -306 243 -274 273 -270 233 -298 231 -296 267 -268 501 -558 231 -296 267 -306 239 -276 271 -270 497 -522 261 -258 259 -296 267 -270 507 -560 231 -298 269 -274 505 -524 521 -518 487 -550 259 -254 257 -298 269 -272 275 -308 241 -274 507 -522 261 -256 257 -296 267 -266 269 -270 271 -272 273 -306 241 -276 273 -270 495 -522 519 -518 259 -290 261 -264 505 -558 231 -300 271 -272 999 -1008 1015 -980 1009 -988 1007 -1016 1001 -1010 983 -1044 237 -292 253 -292 253 -254 491 -560 497 -522 527 -520 263 -294 503 -528 527 -518 259 -256 257 -296 265 -268 503 -526 263 -296 501 -558 497 -524 261 -262 261 -296 231 -298 499 -524 261 -294 267 -268 273 -308 241 -274 273 -270 233 -298 229 -298 265 -268 503 -558 231 -298 267 -304 241 -274 273 -270 497 -522 259 -290 259 -266 267 -270 505 -558 265 -266 271 -272 507 -522 521 -520 487 -550 259 -254 257 -298 269 -272 273 -306 243 -274 507 -524 259 -258 257 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -522\nRAW_Data: 519 -518 259 -288 261 -266 505 -558 231 -300 271 -272 997 -1012 1009 -982 1009 -986 1009 -1014 999 -1016 989 -1012 271 -288 253 -290 253 -256 495 -540 501 -558 495 -522 263 -296 501 -528 527 -520 259 -256 257 -294 265 -268 503 -556 233 -296 501 -558 499 -522 263 -260 261 -296 263 -266 499 -524 261 -294 267 -268 273 -308 241 -276 271 -270 233 -298 229 -300 265 -268 501 -556 231 -298 267 -306 241 -276 271 -270 499 -520 259 -258 259 -296 267 -272 505 -558 233 -298 271 -272 507 -522 521 -520 485 -552 259 -252 291 -264 271 -272 273 -308 241 -276 505 -522 259 -258 259 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -520 521 -518 259 -288 261 -266 503 -558 265 -266 269 -274 1001 -1006 1005 -984 1019 -998 1005 -1004 987 -1004 997 -1044 257 -248 279 -252 259 -296 501 -522 525 -520 525 -520 261 -296 503 -528 525 -520 259 -256 257 -294 267 -268 501 -558 231 -296 501 -560 497 -524 261 -262 259 -296 265 -264 501 -522 263 -294 265 -270 271 -308 241 -276 271 -270 233 -298 229 -298 267 -266 503 -558 231 -296 267 -306 239 -276 273 -270 497 -520 259 -290 227 -298 269 -270 505 -558 231 -298 271 -274 507 -522 521 -518 485 -550 257 -256 291 -264 269 -272 273 -308 241 -276 507 -522 259 -258 257 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -520 521 -518 259 -288 259 -264 503 -560 265 -266 271 -274 997 -1012 1011 -984 1005 -988 1007 -1014 1001 -1010 979 -1040 255 -274 245 -278 253 -292 495 -524 527 -520 525 -520 261 -296 499 -528 527 -520 259 -256 257 -294 265 -268 503 -556 231 -298 501 -560 499 -522 261 -260 261 -296 263 -266 501 -522 263 -294 265 -268 273 -308 241 -276 271 -270 231 -298 231 -298 267 -266 501 -558 231 -298 267 -304 241 -274 273 -270 499 -520 259 -258 259 -296 267 -270 507 -558 231 -300 271 -272 507 -522 521 -516 487 -550 259 -254 291 -266 269 -304 241 -308 241 -274 507 -524 259 -258 257 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -520 489 -548 261 -288 261 -264 503 -558 265 -266 271 -274 1001 -1010 977 -1028 981 -1024 1011 -978 997 -1014 1017 -1008 273 -252 255 -290 255 -290 491 -534 499 -522 527 -522 263 -294 503 -528 525 -520 259 -256 257 -294 267 -266 505 -558 231 -294 501 -558 499 -522 263 -260 261 -296 231 -298 499 -524 261 -296 265 -268 273 -306 241 -276 273 -270 231 -298 231 -296 267 -266 503 -558 231 -296 269 -304 239 -276 273 -270 497 -522\nRAW_Data: 259 -258 259 -296 269 -270 505 -558 231 -300 271 -272 505 -524 521 -520 485 -552 227 -286 255 -298 269 -272 273 -308 241 -276 505 -524 259 -258 257 -296 265 -268 269 -270 271 -270 273 -308 241 -274 273 -270 497 -520 521 -518 259 -288 259 -266 503 -558 265 -266 271 -276 997 -1014 975 -1018 1009 -986 1009 -1018 985 -1002 1009 -1006 257 -274 275 -276 249 -286 493 -526 525 -520 523 -520 263 -296 501 -526 529 -520 259 -256 255 -296 265 -268 267 -32700 99 -100 529 -100 397 -134 231\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/clemsa.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Clemsa\nBit: 18\nKey: 00 00 00 00 00 02 FC AA\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/clemsa_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -334 10811 -4320 65 -100 65 -698 10157 -7550 65 -200 165 -166 133 -66 531 -66 331 -102 197 -132 133 -298 99 -132 263 -200 261 -988 99 -262 131 -296 97 -132 229 -100 459 -100 131 -132 393 -100 1119 -100 8325 -6376 133 -366 131 -562 65 -1034 131 -198 563 -168 365 -66 229 -332 297 -100 231 -166 429 -132 295 -166 97 -100 195 -724 97 -132 97 -1088 163 -200 1651 -100 2885 -8520 365 -166 97 -1558 163 -198 163 -132 465 -134 131 -66 267 -198 65 -232 299 -66 165 -166 65 -498 165 -100 233 -200 133 -166 131 -68 821 -100 263 -66 7633 -7610 1259 -200 99 -98 165 -1196 99 -132 263 -266 99 -200 463 -66 627 -66 1981 -98 7801 -4004 97 -628 65 -264 133 -1088 163 -134 131 -928 297 -166 133 -134 131 -266 297 -596 229 -164 427 -564 197 -166 265 -198 65 -100 559 -6708 131 -132 131 -430 527 -200 367 -66 263 -198 233 -98 299 -68 365 -296 465 -132 855 -66 857 -98 4741 -8312 99 -364 163 -200 133 -1428 529 -132 65 -166 595 -1392 97 -100 97 -132 99 -264 199 -828 99 -398 297 -66 233 -98 861 -100 663 -100 2357 -100 5075 -4640 131 -3312 231 -100 363 -132 99 -296 99 -132 165 -132 363 -98 165 -130 65 -98 165 -132 163 -130 63 -164 297 -198 9769 -3852 133 -98 67 -1226 329 -526 99 -164 295 -496 1713 -196 1681 -130 131 -132 5497 -7230 65 -1150 133 -330 259 -66 329 -100 97 -988 165 -134 197 -166 67 -100 361 -68 461 -100 231 -132 165 -66 365 -264 231 -100 99 -98 265 -696 99 -166 199 -100 101 -962 7101 -6484 363 -760 363 -132 265 -134 431 -264 329 -66 427 -330 263 -164 593 -130 231 -130 627 -66 399 -432 329 -526 131 -100 591 -166 9305 -4044 65 -3532 361 -98 163 -66 461 -264 197 -98 391 -66 329 -132 165 -136 463 -66 529 -166 131 -100 199 -264 133 -66 361 -98 689 -66 229 -198 627 -66 297 -100 261 -66 1685 -134 7883 -3932 229 -728 133 -98 133 -862 65 -132 99 -498 297 -166 133 -332 197 -132 693 -198 97 -656 1087 -64 6209 -6164 131 -266 99 -496 165 -432 67 -100 97 -330 821 -98 361 -100 493 -164 133 -66 197 -200 431 -66 65 -66 399 -66 331 -200 199 -402 131 -664 10955 -5314 65 -262 97 -198 97 -724 99 -196 65 -592 327 -66 625 -262 131 -66 197 -64 427 -132 65 -628 265 -332 329 -368 789 -66 8809 -6238 129 -328 295 -232 363 -98 431 -100 199 -98 261 -530 561 -592 263 -132 1645 -5358 65 -764 65 -330 165 -1158 197 -432 265 -98 397 -166 463 -498 561 -398 199 -66 199 -66 3479\nRAW_Data: -12124 165 -626 65 -890 229 -362 1329 -66 2187 -98 2081 -66 725 -134 3309 -9856 165 -166 263 -198 65 -960 653 -66 261 -66 821 -66 12463 -4032 97 -166 97 -924 65 -464 97 -68 163 -198 165 -100 263 -232 97 -366 633 -12244 65 -332 231 -200 197 -134 197 -234 2457 -134 399 -132 923 -198 197 -64 331 -132 295 -66 11377 -3896 163 -98 65 -194 131 -132 231 -366 131 -132 65 -1050 197 -200 299 -66 3007 -100 11685 -6172 133 -1154 491 -100 293 -200 65 -98 429 -266 463 -64 797 -98 265 -266 397 -132 1227 -66 8485 -4224 97 -166 65 -100 199 -2706 65 -66 263 -98 299 -298 231 -100 499 -66 97 -134 295 -66 431 -198 565 -66 2093 -100 533 -4056 65 -1482 229 -1160 165 -168 299 -166 459 -66 165 -134 99 -100 497 -166 397 -200 431 -200 65 -66 661 -164 529 -66 4671 -8442 131 -100 65 -66 165 -530 131 -132 597 -66 963 -488 275 -2806 2641 -438 2639 -420 2691 -412 2677 -404 2669 -416 2693 -416 361 -2730 367 -2724 2665 -422 355 -2726 2687 -394 399 -2698 2697 -390 375 -2732 2701 -388 355 -21244 2697 -416 377 -2722 2675 -416 2689 -388 2707 -396 2679 -414 2689 -392 2717 -388 375 -2706 383 -2732 2673 -396 365 -2734 2695 -386 377 -2728 2689 -384 389 -2730 2671 -386 379 -21252 2693 -416 347 -2748 2687 -396 2707 -384 2701 -388 2679 -412 2701 -418 2669 -404 363 -2726 385 -2698 2699 -416 355 -2706 2689 -416 365 -2736 2673 -386 415 -2692 2709 -378 361 -21270 2685 -452 311 -2774 2637 -442 2665 -418 2661 -448 2653 -408 2703 -384 2687 -410 365 -2738 355 -2742 2689 -382 371 -2738 2677 -384 415 -2700 2673 -410 363 -2730 2701 -386 357 -21270 2705 -398 361 -2740 2689 -386 2679 -414 2687 -410 2673 -418 2697 -386 2681 -412 383 -2702 395 -2706 2703 -380 385 -2696 2709 -418 355 -2702 2699 -418 361 -2700 2717 -386 375 -21242 2697 -418 355 -2748 2695 -382 2683 -410 2703 -380 2707 -384 2705 -404 2675 -416 383 -2700 359 -2728 2695 -382 385 -2728 2687 -416 357 -2708 2705 -386 389 -2700 2717 -388 373 -21234 2721 -418 353 -2724 2667 -416 2705 -380 2693 -386 2711 -384 2691 -422 2699 -398 365 -2726 385 -2700 2703 -378 361 -2728 2697 -420 357 -2704 2711 -388 377 -2734 2667 -406 363 -21260 2699 -418 361 -2702 2711 -380 2695 -416 2689 -402 2687 -384 2695 -416 2677 -408 361 -2732 385 -2704 2707 -414 325 -2730 2695 -418 361 -2728 2669 -414 385 -2702 2701 -414 355 -21246 2705 -378 379 -2728 2693 -384 2715 -380 2717 -386 2695 -390 2709 -388 2683 -420 355 -2730 385 -2700 2685 -416 347 -2748 2683 -396 365 -2740 2667 -416 385 -2696 2693 -414 349 -21260 2689 -452 319 -2762 2631 -476 2627 -430 2689\nRAW_Data: -416 2667 -412 2671 -416 2689 -424 347 -2732 353 -2738 2669 -410 363 -2728 2689 -410 349 -2742 2677 -386 415 -2696 2705 -380 361 -21254 2717 -386 413 -2700 2693 -380 2695 -416 2691 -398 2679 -416 2699 -384 2709 -382 367 -2726 361 -2730 2707 -382 377 -2728 2675 -386 411 -2684 2713 -414 357 -2704 2707 -388 357 -21276 2699 -382 385 -2724 2689 -416 2661 -416 2685 -384 2723 -388 2703 -400 2677 -416 385 -2696 357 -2724 2713 -384 375 -2726 2673 -420 355 -2728 2685 -396 397 -2688 2697 -408 363 -21252 2667 -484 311 -2766 2635 -476 2637 -420 2665 -448 2651 -408 2701 -384 2697 -406 355 -2758 327 -2736 2685 -420 361 -2728 2683 -384 385 -2726 2685 -414 357 -2708 2711 -388 375 -21246 2689 -448 323 -2750 2695 -378 2715 -386 2687 -392 2711 -384 2683 -416 2705 -412 327 -2732 387 -2728 2679 -416 357 -2738 2659 -418 363 -2732 2677 -386 413 -2700 2695 -380 391 -21258 2679 -406 383 -2706 2695 -384 2703 -418 2679 -404 2705 -380 2687 -418 2669 -410 359 -2726 387 -2696 2707 -416 357 -2710 2693 -418 361 -2730 2691 -380 385 -2730 2677 -414 357 -21254 2711 -382 385 -2700 2713 -414 2667 -384 2705 -418 2675 -406 2699 -382 2689 -418 365 -2726 377 -2708 2701 -384 389 -2698 2709 -398 361 -2738 2669 -416 385 -2692 2687 -418 357 -77456 131 -398 197 -132 295 -330 97 -132 229 -164 459 -164 295 -264 393 -264 719 -64 427 -98 855 -134 395 -98 297 -164 263 -262 65 -100 63 -132 197 -328 1185 -66 9359 -6420 261 -664 131 -100 299 -134 301 -232 363 -232 299 -200 165 -166 427 -230 299 -164 361 -394 1025 -100 225 -820 165 -1248 491 -100 293 -66 261 -264 131 -98 589 -164 655 -132 427 -132 295 -164 129 -132 163 -328 263 -196 627 -566 129 -100 131 -98 2377 -130 1255 -3878 297 -232 195 -132 65 -98 165 -596 397 -266 99 -198 363 -98 923 -100 431 -66 1383 -3724 297 -166 165 -66 99 -398 265 -266 463 -232 133 -232 65 -230 65 -266 959 -200 99 -298 231 -68 65 -100 97 -398 363 -132 199 -134 133 -134 133 -266 593 -66 363 -66 827 -2374 65 -1724 399 -166 265 -100 331 -198 165 -398 233 -98 233 -66 165 -266 97 -66 231 -132 165 -298 395 -234 99 -132 65 -100 99 -132 131 -66 297 -264 197 -194 229 -530 2189 -166 9577 -3702 199 -98 465 -398 97 -134 395 -132 429 -100 529 -68 263 -132 265 -368 263 -860 97 -100 163 -196 427 -98 163 -166 327 -98 493 -166 327 -98 233 -5094 99 -198 97 -100 65 -1250 131 -560 855 -66 855 -262 859 -164 10219 -7528 761 -66 1121 -100 429 -298 331 -232 263 -166 261 -166 265 -100 1427 -98 9787 -6682 131 -564 429\nRAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263\nRAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131\nRAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341\nRAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379\nRAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197\nRAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/dickert_mahs.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Dickert_MAHS\nBit: 36\nKey: 00 00 00 01 55 57 55 15\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/dickert_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 112254 -62882 64 -8912 798 -844 416 -418 806 -850 396 -45206 440 -428 794 -442 804 -422 822 -810 414 -414 824 -832 412 -416 808 -848 376 -446 792 -846 382 -448 816 -828 410 -416 810 -844 382 -416 834 -818 410 -414 810 -856 408 -810 412 -836 384 -442 808 -814 402 -844 414 -834 378 -436 808 -844 396 -422 798 -844 416 -416 814 -404 812 -440 810 -842 396 -422 798 -840 414 -414 806 -850 398 -45210 450 -420 796 -436 780 -446 802 -848 380 -434 806 -846 400 -422 800 -840 410 -408 836 -812 414 -410 826 -840 378 -440 804 -848 396 -426 812 -810 426 -394 826 -844 414 -810 420 -834 378 -442 808 -832 412 -812 416 -830 410 -406 810 -844 400 -420 832 -810 414 -416 800 -446 798 -440 812 -808 426 -410 800 -836 412 -414 806 -836 412 -45216 450 -420 798 -434 806 -414 802 -846 382 -438 814 -832 410 -410 838 -834 396 -430 810 -842 394 -392 826 -840 414 -414 802 -850 396 -428 812 -842 394 -394 828 -842 414 -810 424 -812 392 -434 812 -844 398 -848 380 -844 408 -416 820 -810 414 -406 816 -836 412 -416 836 -414 816 -398 816 -840 420 -410 802 -844 416 -416 804 -824 410 -45232 446 -400 802 -442 810 -432 804 -842 396 -392 826 -842 410 -410 834 -818 378 -442 804 -854 406 -408 806 -838 408 -428 804 -844 396 -392 826 -840 410 -410 834 -810 414 -832 408 -834 380 -440 802 -826 410 -836 412 -838 396 -424 796 -842 414 -414 804 -848 396 -426 812 -412 814 -414 824 -832 410 -416 806 -848 382 -420 834 -814 422 -45228 416 -422 802 -446 810 -420 790 -846 382 -448 818 -828 408 -416 808 -848 382 -418 830 -816 410 -412 812 -856 410 -382 834 -846 382 -418 832 -818 408 -412 812 -856 408 -814 414 -838 396 -428 810 -808 424 -836 380 -844 404 -416 802 -840 424 -394 826 -840 414 -382 836 -412 822 -436 812 -806 424 -394 826 -844 416 -382 838 -816 402 -45228 438 -430 796 -444 806 -424 822 -810 412 -416 822 -832 412 -416 804 -844 408 -414 824 -812 412 -408 812 -834 410 -414 804 -848 408 -412 802 -840 424 -412 802 -834 412 -842 384 -848 396 -426 814 -808 424 -816 392 -866 382 -414 838 -816 414 -428 792 -846 380 -440 810 -438 812 -412 802 -846 380 -438 826 -840 380 -416 838 -814 404 -45226 450 -404 820 -408 806 -452 792 -848 382 -440 814 -832 410 -416 810 -846 378 -450 792 -846 380 -446 816 -830 410 -386 836 -846 376 -410 828 -846 380 -446 814 -828 410 -814 414 -836 396 -428 810 -842 394 -816 410 -836 406 -430 812 -810 426 -394 826 -838\nRAW_Data: 414 -414 808 -416 826 -438 814 -816 420 -414 834 -814 418 -418 808 -848 398 -45218 412 -438 824 -412 812 -418 832 -852 378 -446 782 -862 410 -386 838 -848 384 -420 836 -820 418 -414 814 -854 408 -388 838 -814 418 -422 836 -816 394 -434 812 -846 398 -850 380 -848 410 -418 822 -812 416 -850 368 -854 412 -418 810 -850 384 -422 834 -820 416 -414 812 -428 836 -412 804 -848 382 -450 818 -828 412 -418 808 -850 380 -45228 452 -420 798 -434 806 -416 834 -818 384 -440 810 -820 404 -420 834 -814 416 -418 834 -824 386 -442 810 -818 404 -420 834 -814 416 -418 834 -820 410 -414 810 -850 406 -812 414 -816 404 -420 818 -838 386 -848 394 -828 414 -414 838 -814 406 -420 820 -842 384 -446 794 -438 810 -412 802 -848 394 -432 812 -842 394 -392 830 -842 414 -105578 64 -1760 130 -196 130 -832 160 -128 62 -1278 194 -1316 230 -96 362 -64 64 -398\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/doitrand.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Doitrand\nBit: 37\nKey: 00 00 00 1E 60 08 2F 5F\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/doitrand_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476\nRAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428\nRAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140\nRAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566\nRAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100\nRAW_Data: 299 -298 1025 -68 165 -66 165 -102 333 -164 4245 -8938 99 -6070 1159 -132 199 -66 265 -66 363 -266 863 -100 97 -100 2781 -11408 6995 -5040 7341 -15994 819 -1130 383 -1136 385 -1150 379 -1114 353 -432 1531 -446 1539 -1162 375 -1144 385 -394 1537 -456 1511 -450 1513 -448 1559 -442 1519 -454 1541 -446 1517 -450 1513 -476 1505 -1184 365 -410 1509 -482 1511 -478 1511 -448 1547 -444 1541 -1178 381 -414 1533 -1178 385 -1112 393 -1134 381 -1144 353 -452 1507 -1182 383 -410 1535 -1164 381 -1152 379 -1122 383 -1136 377 -1158 343 -24642 815 -1138 377 -1146 353 -1148 355 -1136 345 -428 1495 -448 1573 -1142 417 -1112 393 -416 1515 -448 1563 -450 1533 -452 1543 -448 1547 -448 1529 -466 1537 -460 1513 -450 1519 -1176 383 -398 1559 -456 1537 -460 1541 -470 1533 -450 1543 -1172 377 -426 1543 -1190 351 -1138 415 -1138 387 -1116 357 -416 1503 -1166 369 -418 1521 -1174 379 -1120 383 -1136 415 -1104 385 -1114 321 -24704 635 -1244 243 -1242 237 -1230 243 -1226 257 -492 973 -490 1007 -1204 271 -1200 273 -478 1013 -456 1011 -478 1025 -460 1003 -454 1043 -446 1039 -416 1063 -414 1041 -440 1037 -1170 309 -420 1045 -452 1049 -408 1067 -436 1051 -400 1065 -1172 317 -404 1065 -1172 317 -1158 325 -1138 341 -1158 311 -416 1075 -1152 321 -408 1083 -1126 353 -1156 321 -1138 351 -1150 331 -1144 311 -24740 659 -1174 311 -1140 329 -1162 349 -1130 327 -442 1035 -416 1063 -1148 329 -1168 315 -438 1053 -410 1049 -430 1051 -452 1029 -426 1047 -438 1059 -434 1017 -450 1037 -426 1047 -1132 321 -432 1069 -414 1067 -414 1037 -432 1053 -450 1033 -1158 319 -456 1027 -1154 353 -1120 321 -1178 317 -1152 325 -444 1037 -1174 319 -418 1053 -1148 355 -1122 319 -1160 349 -1150 331 -1144 311 -24724 631 -1238 269 -1202 269 -1224 239 -1230 257 -484 1005 -482 1007 -1204 271 -1190 309 -446 1019 -456 1003 -492 1005 -472 1007 -452 1043 -448 1013 -454 1037 -424 1041 -440 1049 -1150 321 -408 1071 -414 1033 -432 1063 -438 1049 -428 1035 -1172 317 -434 1037 -1166 311 -1164 349 -1136 315 -1172 307 -406 1083 -1138 315 -438 1051 -1150 351 -1148 319 -1156 325 -1136 345 -1130 317 -24770 675 -1162 317 -1150 343 -1130 339 -1138 317 -436 1035 -452 1041 -1172 303 -1158 349 -412 1043 -428 1049 -450 1029 -428 1049 -436 1057 -436 1051 -414 1037 -430 1049 -418 1059 -1150 319 -420 1061 -422 1031 -444 1049 -448 1039 -416 1065 -1146 329 -446 1037 -1142 351 -1152 309 -1156 349 -1116 345 -428 1053 -1140 345 -396 1079 -1148 311 -1154 341 -1148 345 -1120 341 -1150 311 -24748 633 -1238 269 -1230 225 -1238 243 -1244 257 -488 973 -482 1007 -1208 289 -1178 283 -476 1003 -478 1029 -462 1013 -444\nRAW_Data: 1035 -462 1013 -446 1035 -428 1049 -450 1031 -424 1047 -1148 309 -432 1063 -412 1041 -440 1069 -416 1049 -416 1071 -1148 321 -410 1081 -1130 355 -1120 355 -1132 343 -1130 353 -420 1053 -1150 355 -386 1065 -1156 355 -1120 345 -1132 353 -1120 345 -1160 313 -24732 705 -1144 345 -1126 351 -1152 309 -1160 313 -454 1013 -454 1051 -1166 289 -1176 311 -438 1065 -400 1065 -438 1051 -398 1067 -440 1051 -398 1063 -440 1051 -414 1043 -426 1047 -1150 343 -400 1051 -450 1015 -454 1047 -418 1057 -424 1065 -1142 339 -418 1041 -1176 319 -1160 317 -1158 317 -1154 325 -446 1037 -1176 319 -418 1053 -1150 355 -1118 321 -1176 315 -1154 325 -1172 275 -82942 97 -1816 67 -3324 65 -756 297 -100 229 -132 197 -230 165 -332 97 -66 67 -264 363 -100 131 -17052 165 -828 131 -400 133 -232 167 -132 267 -298 329 -464 67 -166 65 -1394 331 -132 265 -166 397 -132 693 -15932 97 -166 65 -100 199 -300 165 -1064 99 -298 67 -298 199 -100 997 -66 1291 -15382 131 -296 99 -364 133 -496 99 -328 363 -164 99 -16946 97 -830 65 -200 233 -132 99 -266 131 -266 397 -166 1557 -66 567 -762 231 -330 265 -10908 65 -1192 99 -698 229 -198 65 -134 99 -66 265 -98 199 -134 131 -166 167 -368 65 -264 197 -234 99 -98 165 -134 297 -66 429 -166 2215 -15050 299 -166 97 -68 263 -132 293 -462 97 -98 97 -132 65 -460 65 -198 625 -132 1493 -68 3543 -10060 65 -1958 201 -200 99 -98 67 -364 165 -66 99 -232 131 -332 97 -928 491 -100 627 -98 1151 -132 1385 -15094 163 -164 695 -66 299 -134 695 -98 99 -132 431 -234 199 -8336 97 -822 65 -628 65 -5300 199 -268 231 -66 197 -98 199 -134 165 -1062 195 -230 2117 -66 693 -15212 197 -98 259 -526 327 -198 163 -822 165 -726 131 -328 163 -22598 97 -132 65 -726 63 -164 165 -66 163 -560 131 -132 131 -198 193 -396 65 -132 65 -1022 855 -132 2663 -68 1465 -11998 67 -298 199 -132 431 -166 99 -198 199 -100 397 -16212 99 -828 65 -4854 65 -100 199 -200 561 -1460 65 -66 493 -332 629 -232 561 -66 2939 -9276 99 -5942 263 -132 65 -166 329 -164 163 -98 229 -494 129 -164 427 -98 131 -164 195 -296 65 -66 625 -100 1833 -15214 263 -362 265 -598 229 -296 65 -196 163 -98 261 -560 65 -724 199 -17234 199 -68 99 -330 97 -1188 559 -662 199 -68 165 -828 65 -100 131 -21754 261 -1618 65 -134 299 -166 133 -232 131 -66 131 -266 199 -98 99 -1092 527 -64 825 -98 1213 -15578 65 -64 165 -66 131 -398 65 -66 133 -2192 131 -396 133 -66 727 -66 295 -66 1781 -15222 229 -300\nRAW_Data: 229 -432 131 -166 133 -198 331 -100 297 -232 133 -530 199 -100 651 -66 2073 -9794 97 -2386 65 -464 99 -100 133 -364 99 -166 65 -328 197 -100 885 -134 165 -132 227 -164 855 -10348 97 -234 99 -132 265 -166 231 -102 65 -234 67 -134 133 -100 401 -66 99 -100 131 -100 297 -66 165 -132 233 -232 65 -20682 131 -362 131 -394 229 -362 559 -130 1091 -166 1195 -68 463 -66 835 -9542 263 -100 165 -100 197 -132 199 -502 131 -200 131 -68 1527 -100 265 -168 99 -66 99 -166 863 -134 231 -200 129 -166 2183 -16600 297 -164 197 -232 163 -230 229 -232 99 -196 295 -166 165 -200 231 -98 165 -428 395 -132 231 -166 1227 -5128 99 -4674 165 -328 197 -328 465 -234 199 -68 331 -98 331 -166 199 -300 3685 -18958 197 -64 163 -66 229 -228 131 -130 197 -262 131 -164 165 -766 333 -332 629 -66 759 -98 299 -3478 3113 -5094 7403 -16004 829 -1146 395 -1148 383 -1140 401 -1146 385 -422 1539 -476 1569 -1180 387 -1142 407 -400 1561 -462 1599 -448 1565 -484 1563 -472 1569 -468 1537 -482 1565 -458 1571 -444 1571 -1180 387 -420 1543 -474 1571 -488 1579 -462 1567 -444 1607 -1152 387 -452 1575 -1166 379 -1136 407 -1158 385 -1148 381 -428 1567 -1166 383 -426 1581 -1168 383 -1138 417 -1126 383 -1150 391 -1130 349 -24636 853 -1170 377 -1158 407 -1118 409 -1146 387 -422 1537 -474 1575 -1178 421 -1108 409 -400 1559 -460 1565 -444 1545 -482 1539 -450 1559 -456 1573 -444 1569 -450 1511 -448 1549 -1178 383 -402 1525 -452 1545 -450 1551 -450 1531 -466 1535 -1158 387 -392 1539 -1160 387 -1150 367 -1130 383 -1132 383 -428 1543 -1180 381 -392 1549 -1170 347 -1136 353 -1100 351 -1138 343 -1126 303 -24692 669 -1164 311 -1162 333 -1138 317 -1164 317 -420 1059 -420 1067 -1142 337 -1148 311 -430 1045 -448 1037 -414 1061 -412 1077 -410 1037 -444 1065 -414 1039 -428 1049 -408 1051 -1152 309 -440 1047 -450 1041 -414 1053 -420 1047 -454 1047 -1150 345 -398 1051 -1152 343 -1158 309 -1154 345 -1118 343 -420 1077 -1142 321 -420 1055 -1148 355 -1126 317 -1156 345 -1126 353 -1152 311 -24734 665 -1156 355 -1130 319 -1158 351 -1118 327 -416 1069 -414 1065 -1152 325 -1144 349 -398 1077 -412 1071 -412 1065 -416 1037 -428 1065 -418 1051 -448 1041 -414 1051 -420 1045 -1144 351 -414 1041 -422 1069 -418 1049 -450 1037 -416 1063 -1148 327 -448 1039 -1140 353 -1132 347 -1146 311 -1160 351 -418 1051 -1148 319 -456 1029 -1158 355 -1116 321 -1176 311 -1162 349 -1148 273 -24754 637 -1238 241 -1242 271 -1228 243 -1210 271 -470 1011 -476 993 -1224 281 -1180 289 -492 975 -492 1005 -472 1009 -448 1037 -434 1027 -466\nRAW_Data: 1043 -414 1039 -444 1041 -416 1047 -1144 349 -400 1075 -412 1037 -446 1063 -410 1041 -440 1069 -1142 313 -418 1071 -1150 355 -1128 317 -1156 351 -1124 325 -410 1071 -1140 353 -420 1047 -1152 321 -1166 319 -1146 311 -1160 351 -1118 309 -24768 691 -1140 349 -1120 345 -1122 343 -1152 345 -394 1049 -450 1041 -1140 353 -1148 317 -440 1033 -418 1051 -450 1041 -416 1049 -448 1041 -422 1067 -418 1047 -414 1039 -416 1061 -1148 345 -400 1063 -428 1037 -442 1053 -426 1039 -442 1041 -1172 311 -422 1075 -1146 319 -1156 343 -1126 353 -1134 345 -400 1063 -1148 327 -412 1073 -1140 353 -1152 311 -1158 347 -1120 345 -1122 309 -24768 579 -1302 201 -1300 173 -1278 195 -1296 203 -528 941 -538 947 -1252 277 -1196 257 -522 975 -494 999 -472 1011 -456 1007 -474 1007 -450 1037 -444 1037 -462 1001 -454 1043 -1138 315 -434 1037 -442 1041 -414 1085 -416 1071 -420 1051 -1166 319 -408 1069 -1140 353 -1120 345 -1158 347 -1142 309 -430 1051 -1142 345 -398 1081 -1148 315 -1164 355 -1130 319 -1160 351 -1122 295 -24776 667 -1146 345 -1158 311 -1150 345 -1156 311 -418 1045 -454 1049 -1150 345 -1124 351 -410 1043 -426 1069 -420 1045 -448 1039 -414 1067 -428 1035 -440 1047 -428 1035 -438 1017 -1154 321 -434 1071 -414 1065 -414 1037 -464 1017 -434 1061 -1170 315 -436 1031 -1170 317 -1154 327 -1172 311 -1156 337 -416 1043 -1178 319 -418 1055 -1150 353 -1124 319 -1160 351 -1150 297 -1176 275 -87940 229 -164 361 -68 131 -66 265 -132 133 -66 429 -132 131 -232 199 -19674 65 -2866 97 -266 97 -68 99 -232 165 -498 99 -132 67 -232 99 -862 65 -100 199 -68 65 -134 99 -166 199 -200 595 -100 365 -17268 97 -732 365 -164 397 -826 65 -166 331 -298 65 -66 297 -334 397 -66 561 -164 65 -100 265 -100 299 -98 1193 -8592 99 -6652 265 -132 131 -934 163 -134 165 -398 167 -100 829 -100 229 -11976 65 -66 329 -366 131 -430 199 -266 97 -164 65 -164 161 -526 129 -66 99 -164 65 -66 131 -130 1493 -15384 165 -398 97 -100 129 -164 131 -294 229 -264 131 -17598 133 -5180 261 -434 231 -232 65 -198 165 -66 97 -100 231 -264 263 -134 99 -430 5485 -4838 231 -100 131 -66 429 -496 65 -100 65 -166 265 -696 233 -132 231 -98 997 -98 1249 -15252 97 -1558 761 -134 99 -66 431 -164 133 -20388 65 -66 65 -530 97 -888 65 -330 97 -200 133 -330 99 -330 399 -396 231 -366 65 -428 295 -558 657 -98 165 -66 4377 -6898 97 -3848 397 -466 197 -66 65 -398 165 -200 231 -564 163 -402 67 -66 595 -132 459 -66 1085 -12238 195 -134 231 -894 231 -68 165 -698 1559 -66 361 -66\nRAW_Data: 2315 -8340 101 -498 65 -1882 267 -430 99 -498 131 -230 133 -266 165 -98 97 -266 99 -398 131 -462 99 -100 3937 -130 3803 -8272 97 -4604 229 -164 97 -164 131 -1250 361 -298 561 -166 629 -68 429 -166 363 -166 231 -98 131 -8864 65 -592 97 -3130 195 -296 65 -696 1059 -134 299 -98 995 -100 365 -100 197 -66 199 -100 2455 -12500 131 -1030 65 -3104 65 -100 263 -1520 295 -162 195 -132 527 -98 493 -66 1053 -132 163 -66 97 -196 3319 -13102 97 -100 267 -366 265 -762 197 -132 1281 -234 129 -264 165 -22650 97 -100 429 -66 163 -198 493 -264 297 -100 1295 -66 131 -66 395 -132 1697 -66 5577 -10084 65 -1894 67 -132 131 -264 259 -132 365 -98 263 -164 201 -166 199 -200 299 -132 165 -134 231 -166 131 -132 1291 -98 65 -98 661 -8376 65 -296 263 -232 165 -166 327 -494 65 -100 65 -66 263 -98 195 -98 65 -22842 99 -132 65 -464 361 -296 99 -130 195 -296 163 -132 97 -460 65 -132 163 -856 1327 -66 265 -15278 131 -468 99 -100 65 -464 97 -200 199 -130 65 -134 133 -164 297 -1390 499 -166 919 -13096 197 -4346 97 -196 97 -262 97 -330 97 -528 133 -198 131 -166 197 -962 65 -98 299 -98 99 -200 1665 -16846 65 -2444 165 -564 397 -498 65 -100 163 -66 365 -66 297 -364 65 -266 65 -134 165 -17172 67 -300 131 -262 99 -166 231 -726 197 -130 689 -200 97 -132 65 -396 97 -98 99 -98 229 -398 163 -168 263 -528 1019 -66 467 -100 533 -15150 97 -500 331 -198 67 -100 131 -566 99 -66 331 -100 197 -366 99 -898 1057 -66 863 -132 757 -66 559 -8454 99 -1196 97 -3724 131 -130 131 -264 97 -594 363 -1060 99 -100 165 -362 163 -460 195 -98 129 -922 99 -328 691 -66 5965 -12210 131 -132 65 -98 263 -164 65 -230 97 -230 163 -532 97 -394 263 -362 65 -98 163 -426 65 -100 1213 -100 495 -164 461 -8616 129 -3792 133 -132 265 -132 131 -100 161 -132 359 -196 361 -790 63 -164 1889 -15496 165 -66 265 -300 299 -334 297 -298 131 -132 97 -100 99 -232 65 -300 65 -132 233 -564 431 -66 533 -98 1459 -9390 97 -3462 231 -266 163 -202 131 -166 301 -66 465 -66 799 -200 199 -266 427 -132 799 -66 4333 -13748 65 -1298 165 -234 133 -168 165 -696 329 -956 327 -166 163 -98 263 -426 1247 -132 3303 -10574 65 -428 263 -832 199 -498 65 -200 165 -924 851 -66 393 -66 563 -134 2149 -66 1291 -10298 67 -1264 7383 -5036 7419 -15962 823 -1198 347 -1188 349 -1150 373 -1186 349 -442 1543 -452 1579 -1148 381 -1168 347 -458 1535 -458 1577 -444 1577 -486\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/doorhan.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: KeeLoq\nBit: 64\nKey: 48 50 F0 72 33 78 95 14\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/doorhan_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1937 -32700 457 -398 425 -356 437 -386 411 -384 447 -384 417 -384 419 -386 417 -386 419 -388 417 -388 419 -388 451 -3998 835 -412 417 -810 821 -420 419 -834 423 -836 819 -450 385 -852 813 -446 419 -822 795 -442 415 -814 419 -850 799 -438 413 -850 415 -860 809 -412 843 -420 397 -826 417 -844 815 -432 387 -842 841 -446 791 -472 377 -850 819 -420 395 -854 787 -454 419 -810 429 -854 807 -416 409 -876 819 -422 395 -854 789 -426 839 -418 813 -422 835 -412 819 -452 809 -474 379 -850 821 -420 395 -852 399 -828 813 -450 809 -422 845 -412 407 -870 421 -812 827 -436 385 -842 415 -840 795 -438 411 -842 407 -848 811 -448 811 -458 387 -838 811 -418 417 -822 417 -830 839 -446 381 -874 785 -452 809 -442 385 -806 417 -15802 423 -388 409 -398 395 -398 423 -408 387 -412 385 -412 417 -416 383 -416 385 -418 387 -416 419 -386 421 -4030 789 -422 415 -844 817 -432 385 -844 419 -850 799 -440 413 -876 791 -426 413 -842 817 -432 387 -838 419 -848 793 -436 411 -842 405 -884 779 -450 807 -426 393 -834 417 -836 825 -434 409 -842 819 -454 797 -434 411 -840 831 -400 433 -806 815 -462 387 -838 419 -848 797 -438 413 -876 785 -438 433 -804 811 -424 815 -446 805 -452 801 -434 803 -448 839 -412 441 -834 811 -416 411 -820 417 -828 835 -444 775 -450 835 -412 409 -868 419 -812 827 -438 385 -838 419 -814 825 -436 415 -850 409 -844 809 -446 809 -424 421 -836 811 -420 415 -822 417 -826 807 -476 377 -876 791 -458 809 -420 385 -820 415 -15780 407 -440 379 -412 379 -444 381 -408 385 -434 391 -394 431 -380 409 -412 413 -380 411 -412 409 -396 411 -4006 819 -414 415 -854 789 -424 447 -808 429 -852 811 -414 409 -868 811 -452 387 -820 819 -450 405 -824 409 -842 801 -426 445 -838 399 -870 781 -460 803 -414 417 -828 407 -850 809 -422 421 -838 819 -450 813 -432 415 -846 787 -452 389 -854 791 -426 413 -842 427 -824 805 -450 423 -838 819 -416 415 -854 791 -428 807 -450 811 -420 817 -448 817 -454 799 -434 415 -848 801 -410 427 -820 413 -842 801 -456 809 -450 811 -432 413 -844 409 -830 813 -448 383 -840 417 -824 801 -442 413 -848 405 -848 809 -448 811 -458 377 -844 817 -420 417 -820 417 -830 805 -476 377 -872 803 -448 799 -442 415 -772 415 -15782 429 -376 437 -376 411 -412 413 -380 413 -414 409 -396 411 -408 379 -444 377 -412 381 -412 413 -408 385 -4064 809 -418 397 -828 813 -450 383 -840 403 -850 813 -450 417 -840 813 -448 407 -828\nRAW_Data: 809 -446 381 -844 417 -822 805 -474 381 -842 417 -864 809 -446 809 -418 395 -830 419 -844 815 -430 385 -874 805 -454 797 -436 413 -850 819 -414 421 -818 819 -418 417 -846 401 -854 807 -422 421 -840 809 -454 393 -854 789 -426 807 -452 811 -422 813 -446 835 -420 805 -454 435 -818 815 -416 417 -850 399 -822 809 -450 821 -420 817 -448 423 -850 387 -842 809 -420 417 -822 417 -830 805 -442 411 -842 403 -850 811 -450 811 -460 387 -838 811 -420 417 -822 403 -852 813 -448 415 -854 789 -456 815 -420 385 -818 417 -15768 453 -370 415 -398 407 -418 377 -438 389 -408 387 -412 385 -446 383 -416 385 -416 385 -418 385 -418 419 -4008 835 -412 409 -830 811 -448 381 -850 417 -826 839 -444 381 -878 787 -424 449 -810 821 -432 387 -838 419 -848 791 -438 413 -844 417 -860 807 -446 807 -416 409 -840 387 -844 823 -432 385 -878 785 -450 819 -434 413 -844 819 -420 395 -854 791 -422 449 -808 431 -818 813 -446 407 -850 811 -452 415 -808 835 -420 813 -420 813 -460 781 -446 835 -420 817 -452 429 -820 819 -418 417 -814 427 -820 817 -418 817 -448 819 -436 415 -848 405 -848 809 -414 415 -848 387 -848 801 -440 413 -842 419 -822 839 -444 811 -418 411 -838 819 -420 415 -822 417 -826 837 -444 379 -850 819 -454 805 -440 387 -806 415 -15792 423 -390 417 -390 409 -384 409 -402 423 -408 387 -410 385 -414 415 -416 383 -418 383 -418 387 -416 419 -4032 787 -438 395 -836 811 -426 421 -836 421 -846 795 -440 413 -878 783 -452 407 -820 805 -414 435 -822 417 -836 819 -434 415 -848 409 -844 811 -450 809 -418 395 -828 419 -844 815 -432 385 -876 801 -428 841 -420 417 -836 813 -416 419 -850 797 -436 415 -814 419 -850 799 -442 413 -850 821 -454 405 -820 805 -414 819 -440 827 -410 837 -418 835 -412 829 -456 415 -840 795 -436 387 -844 417 -840 795 -436 803 -448 837 -410 409 -866 417 -838 799 -440 385 -842 421 -812 827 -434 409 -844 407 -860 811 -450 813 -430 407 -812 807 -450 351 -32700 459 -422 407 -386 411 -416 415 -384 415 -388 415 -388 413 -422 383 -422 409 -396 395 -434 357 -438 389 -4046 783 -448 817 -432 781 -446 415 -824 443 -814 413 -856 819 -450 803 -428 411 -844 779 -460 805 -414 837 -418 817 -452 785 -460 805 -450 425 -838 417 -812 819 -432 387 -842 807 -452 393 -852 391 -868 817 -448 389 -858 409 -810 409 -854 807 -446 379 -840 829 -428 411 -846 821 -468 777 -450 411 -838 779 -452 811 -422 815 -448 803 -458 779 -484\nRAW_Data: 783 -458 411 -846 783 -452 393 -854 415 -832 805 -442 775 -450 835 -444 377 -866 415 -840 799 -438 411 -844 385 -850 795 -440 415 -846 409 -844 809 -448 809 -456 381 -842 809 -452 385 -836 395 -862 815 -418 419 -866 807 -444 805 -418 415 -812 385 -15782 473 -360 429 -358 439 -388 409 -386 447 -382 417 -384 419 -386 417 -388 417 -388 419 -388 419 -388 417 -4030 803 -442 803 -416 819 -438 433 -838 385 -868 385 -872 787 -454 827 -432 385 -842 811 -418 811 -460 813 -412 819 -452 803 -436 807 -450 421 -842 421 -814 821 -430 387 -842 807 -450 421 -824 409 -842 835 -418 413 -840 407 -836 415 -822 819 -444 393 -836 813 -458 385 -874 785 -454 827 -434 385 -844 811 -418 811 -462 813 -412 817 -454 803 -436 809 -450 423 -836 811 -420 417 -822 417 -826 801 -444 809 -452 819 -416 453 -848 413 -816 809 -422 409 -838 421 -810 823 -436 415 -846 419 -818 807 -476 809 -418 409 -838 809 -420 417 -822 415 -866 805 -444 381 -878 785 -470 803 -412 407 -810 417 -15764 455 -368 405 -422 399 -396 391 -436 387 -410 385 -412 415 -418 381 -418 383 -418 387 -416 387 -418 419 -4034 791 -424 809 -450 811 -420 393 -868 419 -810 433 -822 841 -420 813 -446 419 -820 801 -440 809 -448 785 -452 807 -438 809 -450 817 -448 407 -832 419 -812 813 -426 411 -840 819 -454 385 -836 421 -838 821 -450 423 -822 411 -842 407 -824 809 -446 379 -840 819 -442 399 -870 817 -428 809 -450 397 -828 815 -448 811 -418 819 -418 817 -454 789 -462 811 -448 395 -866 779 -452 383 -850 419 -820 803 -440 809 -450 819 -420 417 -850 445 -816 809 -420 409 -840 419 -814 821 -434 415 -848 411 -842 807 -446 809 -422 411 -842 817 -420 415 -844 395 -826 817 -450 415 -854 787 -456 807 -422 417 -788 421 -15780 407 -440 379 -412 381 -442 381 -408 385 -434 393 -394 399 -414 407 -412 411 -382 411 -414 409 -396 379 -4042 807 -446 813 -430 805 -414 409 -866 385 -878 397 -852 781 -480 783 -450 411 -822 811 -450 793 -426 807 -450 813 -454 805 -448 783 -484 383 -850 383 -850 799 -452 407 -822 811 -450 397 -862 383 -878 793 -466 381 -848 417 -820 403 -852 773 -452 395 -864 811 -450 383 -852 817 -450 801 -428 411 -844 817 -430 813 -412 801 -454 805 -452 791 -460 807 -448 411 -838 819 -418 417 -840 395 -860 777 -450 813 -456 781 -446 405 -886 385 -846 787 -466 387 -836 417 -846 789 -436 415 -846 417 -852 773 -476 811 -420 411 -840 783 -450 385 -852 417 -860\nRAW_Data: 773 -474 379 -870 805 -450 815 -412 385 -826 159 -32700 483 -378 411 -414 377 -426 381 -436 379 -410 411 -412 381 -442 381 -408 383 -404 423 -396 397 -414 407 -4014 825 -436 803 -414 435 -822 415 -836 425 -822 839 -416 409 -874 405 -826 419 -812 811 -424 409 -842 421 -846 399 -854 807 -416 441 -848 803 -454 799 -436 385 -842 811 -420 417 -854 803 -416 839 -454 801 -434 411 -844 833 -388 447 -804 823 -434 409 -844 407 -824 419 -868 793 -470 807 -416 409 -840 821 -420 811 -422 813 -446 801 -456 809 -454 815 -428 411 -848 821 -424 389 -856 417 -798 839 -410 841 -418 815 -448 419 -854 415 -814 813 -424 423 -834 389 -844 819 -432 413 -848 405 -826 817 -450 811 -460 385 -838 817 -420 415 -824 417 -826 809 -442 413 -850 823 -454 801 -434 379 -808 419 -15800 421 -408 387 -410 385 -414 415 -386 413 -420 383 -418 385 -420 385 -420 417 -390 417 -390 419 -388 417 -4036 787 -452 799 -432 409 -842 407 -824 413 -838 823 -432 411 -872 377 -864 383 -836 823 -432 379 -872 375 -854 413 -840 821 -430 411 -878 791 -428 807 -448 379 -840 837 -418 417 -846 795 -436 809 -450 815 -448 409 -830 811 -450 383 -848 817 -416 425 -822 411 -848 415 -858 805 -446 809 -420 411 -838 809 -452 779 -460 779 -446 821 -442 797 -474 811 -450 377 -842 821 -418 419 -820 417 -830 807 -440 811 -450 819 -448 411 -842 415 -846 779 -428 411 -838 419 -848 795 -436 413 -846 417 -820 807 -474 811 -420 423 -836 783 -450 383 -850 417 -828 803 -474 379 -874 787 -456 805 -416 413 -830 377 -15802 411 -414 415 -402 393 -396 399 -422 409 -386 411 -416 383 -418 415 -386 415 -386 417 -386 417 -388 417 -4022 809 -442 811 -414 417 -830 445 -814 413 -842 817 -418 419 -850 443 -816 413 -808 823 -420 435 -820 393 -862 423 -810 825 -434 413 -850 821 -420 831 -428 379 -840 809 -450 417 -834 807 -454 813 -412 823 -442 437 -836 781 -428 421 -838 817 -420 415 -824 417 -830 447 -814 813 -458 811 -412 453 -800 805 -440 809 -448 791 -456 811 -420 815 -460 809 -448 395 -864 785 -452 381 -852 419 -818 801 -438 837 -416 817 -446 417 -860 415 -812 813 -422 409 -838 421 -812 827 -436 413 -848 419 -816 807 -474 809 -418 425 -836 785 -448 379 -840 409 -864 811 -418 415 -864 803 -444 801 -416 451 -782 391 -15810 439 -376 407 -378 447 -376 415 -378 419 -420 421 -386 419 -386 421 -388 421 -388 407 -398 393 -400 421 -4010 841 -422 787 -426 411 -842 421 -846\nRAW_Data: 401 -856 807 -418 421 -870 387 -848 403 -818 807 -450 397 -828 421 -846 389 -854 817 -448 419 -852 805 -414 807 -452 417 -800 807 -442 409 -844 801 -454 809 -450 815 -432 413 -850 821 -424 389 -822 827 -428 413 -844 389 -856 393 -866 821 -450 815 -430 409 -810 809 -450 815 -428 803 -448 789 -454 819 -450 815 -430 413 -848 787 -456 387 -836 395 -862 813 -420 809 -458 811 -412 451 -836 417 -812 811 -426 409 -842 421 -814 827 -434 415 -846 421 -816 805 -476 807 -420 421 -838 785 -450 383 -852 419 -824 803 -440 413 -852 819 -444 829 -408 409 -812 415 -15778 423 -406 387 -410 385 -414 415 -384 415 -418 383 -420 383 -420 385 -420 417 -388 417 -388 419 -408 397 -4028 809 -420 801 -444 407 -830 419 -846 385 -854 817 -448 407 -862 387 -846 423 -822 789 -422 449 -810 427 -820 423 -838 811 -454 387 -866 811 -414 827 -428 411 -842 813 -430 385 -840 833 -452 777 -474 807 -452 385 -842 811 -420 419 -824 829 -418 417 -854 379 -850 419 -860 809 -444 809 -420 423 -838 813 -420 811 -420 813 -446 805 -452 803 -470 777 -450 421 -838\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/dooya.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Dooya\nBit: 40\nKey: 00 00 00 E1 DC 03 05 11\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/dooya_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364\nRAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688\nRAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/faac_slh_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -266 12233 -132 4599 -98 2815 -166 10261 -130 1881 -132 11931 -100 2107 -1164 541 -346 243 -630 515 -330 275 -618 281 -572 277 -566 307 -560 257 -626 313 -558 313 -570 547 -324 561 -310 555 -318 285 -544 567 -308 283 -630 297 -572 281 -590 281 -598 275 -568 291 -570 571 -312 283 -556 579 -310 311 -570 303 -578 545 -346 249 -598 553 -302 315 -550 279 -574 311 -592 291 -600 281 -588 553 -316 533 -342 523 -324 559 -312 283 -558 281 -628 559 -294 573 -334 549 -316 541 -314 563 -294 281 -588 543 -312 285 -628 551 -310 283 -594 553 -336 281 -588 283 -560 551 -312 533 -324 281 -626 283 -568 287 -602 543 -348 537 -316 535 -330 273 -580 545 -318 537 -300 1093 -1154 545 -346 279 -598 529 -340 283 -592 281 -560 313 -536 309 -564 257 -640 281 -592 279 -598 557 -294 571 -336 551 -316 285 -560 539 -316 283 -602 259 -606 313 -584 281 -572 307 -564 289 -580 543 -314 285 -560 553 -352 277 -602 257 -608 545 -350 283 -576 559 -296 279 -584 275 -586 281 -592 313 -570 307 -560 559 -340 547 -314 541 -314 563 -296 279 -584 275 -618 539 -348 523 -324 583 -308 549 -314 573 -312 283 -564 543 -292 303 -614 551 -318 279 -602 545 -328 273 -584 281 -594 553 -316 535 -312 283 -596 293 -598 281 -590 555 -314 565 -296 569 -300 277 -588 545 -316 543 -318 1097 -1144 529 -338 283 -604 541 -324 259 -608 281 -572 285 -566 293 -570 277 -622 279 -612 273 -596 549 -302 581 -316 543 -316 283 -572 549 -292 285 -632 283 -578 285 -600 265 -604 281 -590 281 -562 567 -312 283 -564 545 -356 281 -590 281 -572 573 -324 259 -606 551 -316 285 -564 279 -574 275 -630 259 -618 281 -592 539 -348 525 -326 541 -338 551 -280 287 -562 281 -630 557 -326 541 -336 545 -316 537 -348 527 -324 281 -562 581 -280 287 -630 527 -342 269 -608 543 -346 249 -588 279 -596 525 -344 523 -324 261 -640 283 -594 277 -568 579 -292 569 -310 547 -316 281 -578 557 -294 569 -300 1071 -1180 527 -344 269 -608 543 -348 249 -598 279 -598 277 -572 273 -562 291 -616 281 -592 313 -568 541 -324 561 -310 547 -316 283 -580 559 -308 269 -608 277 -622 283 -578 275 -602 257 -602 275 -586 545 -316 283 -582 529 -362 251 -620 281 -592 535 -346 283 -562 547 -328 275 -586 281 -560 315 -580 309 -596 257 -586 581 -316 543 -316 531 -344 523 -322 295 -578 281 -588 553 -350 523 -324 581 -308 547 -316 539 -348 247 -602 543 -294 299 -612 553 -318 281 -602 545 -326 271 -584 281 -584 553\nRAW_Data: -316 533 -308 283 -628 265 -602 279 -594 535 -338 535 -322 563 -310 281 -568 575 -290 559 -310 1077 -1170 541 -322 293 -576 551 -354 245 -580 307 -562 291 -580 275 -586 281 -592 313 -570 307 -560 559 -336 549 -316 539 -316 281 -570 547 -324 293 -608 283 -578 275 -598 289 -580 277 -590 281 -594 539 -310 279 -564 559 -374 283 -582 275 -604 543 -326 271 -584 545 -316 283 -600 279 -564 275 -602 291 -612 281 -582 539 -350 525 -324 541 -338 519 -314 285 -594 283 -596 555 -326 543 -338 551 -318 537 -316 561 -294 279 -586 545 -350 249 -618 541 -324 285 -604 545 -352 245 -602 275 -570 545 -324 559 -310 283 -590 313 -570 307 -558 559 -338 547 -318 539 -314 283 -568 547 -324 557 -310 1079 -1148 557 -326 283 -594 545 -318 281 -602 275 -570 307 -560 259 -590 275 -622 279 -606 273 -592 555 -334 515 -350 541 -318 281 -570 549 -290 287 -638 281 -596 275 -602 257 -602 275 -586 281 -582 539 -312 283 -568 577 -324 309 -588 281 -598 525 -344 269 -600 539 -312 283 -584 283 -560 279 -612 307 -562 295 -602 549 -318 537 -316 561 -294 569 -302 279 -588 281 -626 529 -344 521 -324 567 -348 521 -316 537 -348 283 -564 545 -294 301 -614 539 -348 283 -564 547 -332 275 -588 315 -560 537 -316 529 -344 269 -602 311 -588 283 -596 527 -346 523 -322 561 -310 283 -560 543 -316 537 -314 1093 -1174 541 -346 285 -562 563 -346 267 -568 303 -580 281 -556 281 -584 281 -630 275 -596 257 -618 547 -318 537 -316 561 -294 279 -586 545 -314 285 -618 275 -600 257 -608 313 -584 283 -578 277 -566 541 -322 297 -578 547 -352 279 -572 291 -600 543 -344 249 -596 535 -350 283 -564 257 -570 305 -614 283 -32700 429 -198 97 -726 895 -168 1195 -98 14445 -100 949 -100 4415 -200 1563 -98 10547 -100 7961 -168 267 -166 2059 -132 4651 -98 6711 -100 2341 -1184 511 -382 243 -626 521 -326 283 -600 279 -586 281 -574 275 -566 271 -628 265 -604 279 -590 551 -348 525 -326 543 -306 317 -554 553 -318 245 -632 291 -594 281 -574 285 -600 301 -576 281 -574 539 -320 281 -562 545 -350 279 -598 293 -598 547 -318 279 -600 547 -302 313 -546 317 -536 541 -354 557 -346 537 -302 309 -578 309 -548 577 -282 567 -292 287 -570 547 -350 555 -348 525 -322 317 -576 545 -316 285 -566 313 -536 551 -322 283 -598 317 -596 277 -570 545 -324 563 -312 323 -560 283 -542 315 -568 273 -594 295 -602 551 -318 557 -316 563 -296 311 -556 311 -556 553 -318 545 -284 1103 -1148 553 -336 285\nRAW_Data: -592 541 -314 319 -564 289 -574 275 -586 281 -558 281 -630 271 -596 291 -594 547 -318 567 -286 569 -302 315 -548 545 -318 275 -600 295 -596 281 -584 315 -568 285 -568 303 -546 583 -318 285 -548 555 -352 279 -596 257 -620 547 -318 283 -574 539 -324 283 -568 313 -556 551 -350 539 -326 545 -342 283 -572 285 -600 561 -308 549 -318 245 -566 577 -324 577 -316 533 -350 281 -594 549 -316 277 -566 289 -584 545 -312 285 -594 313 -572 309 -558 557 -340 549 -318 275 -568 291 -582 275 -550 315 -590 315 -572 547 -322 561 -310 547 -354 281 -540 309 -564 545 -296 569 -312 1077 -1172 549 -322 291 -578 545 -318 279 -596 293 -562 313 -550 281 -570 285 -630 273 -582 281 -584 565 -346 519 -322 559 -312 285 -560 545 -316 285 -634 275 -594 257 -620 283 -590 283 -574 283 -566 555 -304 279 -592 551 -348 251 -628 265 -604 545 -318 279 -596 551 -300 277 -586 281 -572 539 -354 557 -346 537 -312 283 -594 291 -562 545 -352 523 -318 285 -566 535 -382 553 -292 565 -312 285 -598 537 -350 281 -566 257 -604 543 -312 285 -592 315 -572 307 -558 559 -336 549 -318 277 -566 289 -580 275 -586 283 -610 261 -598 569 -310 547 -354 533 -312 283 -564 289 -582 545 -314 549 -316 1075 -1150 579 -292 301 -582 545 -354 281 -572 275 -594 257 -584 277 -590 281 -592 313 -572 307 -558 559 -338 547 -318 539 -318 283 -572 547 -290 289 -640 283 -596 277 -568 289 -600 275 -586 281 -596 539 -318 283 -540 583 -322 297 -582 317 -550 567 -310 319 -558 555 -300 315 -552 281 -570 573 -322 557 -346 539 -302 279 -602 275 -588 545 -318 567 -294 277 -582 545 -348 535 -330 569 -336 283 -574 539 -324 283 -598 281 -544 563 -302 277 -634 283 -592 281 -598 545 -326 539 -314 285 -596 281 -566 279 -580 275 -602 289 -586 583 -316 543 -316 563 -310 269 -604 275 -582 547 -320 545 -282 1093 -1152 543 -322 301 -586 545 -352 283 -572 273 -594 257 -586 311 -556 283 -592 315 -570 309 -558 559 -338 547 -320 541 -316 283 -572 547 -290 287 -638 283 -578 275 -598 289 -576 313 -556 283 -598 555 -284 285 -568 565 -346 267 -606 277 -588 545 -352 281 -570 555 -294 279 -584 315 -552 553 -352 529 -346 557 -292 301 -586 281 -596 539 -318 531 -344 271 -564 567 -346 543 -318 561 -294 311 -590 547 -320 283 -564 281 -578 555 -292 309 -592 317 -564 315 -574 547 -290 585 -310 285 -554 317 -566 281 -580 277 -602 289 -32700 525 -130 559 -296 16901 -100 3153 -132 4843 -98 9161\nRAW_Data: -196 817 -234 3911 -166 6897 -98 6789 -166 8923 -100 2785 -100 5393 -1192 541 -344 243 -630 517 -332 309 -578 281 -570 275 -598 259 -572 299 -614 281 -592 275 -600 545 -326 541 -312 557 -316 287 -576 553 -310 285 -596 291 -596 281 -592 283 -574 309 -564 289 -582 543 -312 285 -560 547 -348 315 -564 289 -580 545 -350 283 -572 539 -320 285 -568 311 -550 281 -628 553 -310 559 -322 557 -312 555 -316 321 -538 311 -562 543 -292 567 -346 555 -316 565 -308 559 -326 273 -584 283 -592 555 -280 563 -316 287 -606 543 -328 309 -552 585 -318 561 -278 565 -294 279 -590 547 -312 279 -600 581 -298 315 -574 285 -600 561 -308 549 -316 287 -562 539 -312 535 -308 1119 -1146 551 -318 317 -572 547 -322 259 -610 281 -558 317 -562 281 -566 307 -594 265 -608 317 -568 541 -324 559 -310 541 -314 279 -568 571 -310 281 -602 289 -578 313 -574 283 -598 265 -604 281 -574 539 -326 281 -558 545 -350 279 -598 293 -602 545 -316 277 -598 549 -300 279 -586 281 -570 285 -600 571 -312 577 -318 531 -344 523 -322 297 -580 281 -560 577 -282 575 -346 521 -324 567 -346 513 -352 283 -572 273 -594 551 -300 547 -312 285 -618 527 -344 269 -604 543 -348 545 -318 533 -310 283 -594 547 -300 275 -622 553 -318 281 -602 257 -606 543 -350 511 -354 283 -566 527 -310 559 -292 1107 -1142 567 -312 281 -594 555 -336 283 -556 319 -544 315 -572 273 -562 291 -610 317 -558 279 -600 553 -318 541 -338 549 -318 285 -564 537 -316 283 -604 291 -580 313 -590 283 -562 313 -574 273 -564 547 -330 275 -586 547 -350 281 -570 291 -602 543 -350 285 -562 537 -316 283 -568 291 -568 303 -612 539 -350 521 -320 579 -310 547 -318 283 -576 311 -562 545 -292 565 -346 537 -348 525 -324 545 -342 285 -594 283 -542 563 -312 557 -290 287 -636 543 -314 283 -602 543 -328 543 -314 557 -318 285 -578 527 -342 271 -598 577 -316 283 -600 277 -572 545 -322 561 -312 285 -560 555 -318 541 -318 1101 -1146 529 -340 283 -572 573 -320 293 -576 283 -572 285 -566 293 -568 277 -624 281 -576 309 -594 551 -300 577 -318 543 -318 283 -572 547 -290 285 -632 285 -594 277 -602 273 -566 291 -582 313 -554 547 -316 285 -546 565 -344 269 -608 313 -556 575 -316 279 -566 559 -340 285 -542 281 -598 259 -616 543 -352 529 -344 559 -290 567 -310 287 -596 283 -544 565 -310 557 -322 563 -348 539 -302 565 -326 275 -584 281 -572 571 -292 549 -306 319 -574 573 -322 291 -578 545 -346 535 -294 571 -304 317\nRAW_Data: -544 569 -286 279 -616 543 -350 279 -598 299 -574 547 -316 565 -288 279 -582 579 -280 537 -310 1091 -1176 547 -318 281 -598 551 -338 283 -556 283 -570 287 -596 265 -576 311 -590 279 -610 273 -596 553 -302 549 -350 543 -320 283 -540 585 -290 283 -594 317 -596 277 -568 287 -602 275 -586 281 -594 543 -318 285 -538 553 -354 293 -580 317 -560 569 -310 283 -594 551 -300 277 -586 281 -572 283 -600 569 -310 575 -304 565 -294 569 -314 279 -600 255 -602 543 -312 541 -346 533 -324 569 -348 543 -320 281 -572 309 -558 555 -300 547 -314 287 -592 565 -346 269 -602 543 -310 547 -318 571 -312 283 -564 545 -294 303 -614 541 -350 281 -564 289 -582 577 -318 541 -318 283 -572 549 -290 577 -274 1113 -1146 555 -326 281 -598 545 -318 281 -580 309 -564 287 -574 275 -586 281 -594 313 -572 307 -558 559 -338 547 -318 539 -318 281 -572 547 -292 287 -638 277 -602 257 -618 281 -592 283 -578 251 -596 555 -304 279 -590 547 -346 281 -598 265 -606 545 -318 279 -598 551 -300 279 -586 283 -560 317 -584 555 -324 577 -308 547 -318 557 -316 283 -572 307 -558 553 -300 545 -348 557 -314 561 -324 543 -338 283 -560 317 -548 567 -310 559 -290 283 -630 555 -318 283 -572 545 -324 563 -312 547 -318 283 -566 565 -312 285 -594 555 -338 283 -586 281 -598 527 -344 523 -324 295 -580 547 -318 523 -318 1097 -1142 565 -310 279 -602 545 -326 273 -584 317 -560 281 -562 279 -604 277 -604 257 -606 313 -554 577 -320 531 -342 523 -324 293 -580 547 -318 243 -632 291 -592 281 -572 287 -594 303 -576 281 -572 541 -294 309 -560 545 -350 315 -574 271 -594 559 -336 283 -558 545 -352 283 -540 309 -564 257 -636 551 -316 571 -314 557 -288 585 -310 285 -560 319 -562 539 -316 531 -362 541 -338 549 -318 569 -312 283 -564 291 -588 545 -314 549 -318 285 -598 555 -326 283 -592 547 -318 535 -348 525 -322 283 -564 579 -282 287 -620 557 -326 281 -592 281 -584 539 -350 523 -318 283 -564 549 -316 549 -318 1089 -1134 577 -324 271 -588 545 -352 281 -572 307 -560 291 -558 277 -588 281 -592 313 -572 307 -560 559 -338 549 -318 537 -300 309 -576 541 -312 285 -604 255 -630 273 -584 281 -584 315 -568 285 -570 565 -310 283 -554 547 -352 277 -600 291 -574 545 -352 283 -580 527 -344 267 -570 301 -546 313 -588 569 -312 557 -322 557 -312 539 -342 277 -568 303 -546 579 -282 575 -336 537 -328 545 -352 537 -332 279 -578 313 -548 545 -318 533 -318 279 -612 545 -348 279\nRAW_Data: -596 529 -342 547 -316 567 -310 267 -570 567 -310 285 -592 533 -348 281 -596 293 -562 583 -316 545 -318 283 -540 551 -322 543 -304 1111 -1146 557 -326 281 -596 547 -314 285 -578 311 -562 259 -604 275 -582 281 -592 313 -572 307 -560 559 -338 553 -318 559 -284 283 -580 557 -294 279 -614 281 -598 313 -570 287 -566 305 -584 281 -560 577 -318 285 -542 555 -354 291 -576 317 -566 541 -324 297 -580 545 -316 285 -578 283 -566 259 -624 545 -354 527 -344 557 -294 569 -312 285 -586 283 -562 567 -312 527 -356 555 -312 547 -352 531 -346 267 -566 305 -580 547 -318 537 -300 277 -636 547 -318 279 -598 551 -302 551 -350 537 -304 277 -570 573 -310 279 -604 545 -328 311 -582 247 -598 547 -332 545 -314 279 -568 573 -294 565 -310 1077 -1150 551 -320 285 -606 545 -318 281 -604 207 -32700 231 -134 327 -622 361 -134 8003 -100 4115 -164 1395 -164 3469 -98 5967 -100 4997 -134 6541 -132 4213 -98 1445 -134 697 -238 5817 -134 1455 -132 3877 -130 12819 -100 4195 -134 1829 -100 1755\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/feron.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Feron\nBit: 32\nKey: 00 00 00 00 63 38 84 7B\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/feron_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 329 -296 295 -66 163 -98 2441 -66 9337 -5690 131 -2574 65 -692 65 -460 165 -230 329 -266 131 -134 97 -400 197 -66 197 -66 12303 -5844 97 -200 167 -102 197 -334 297 -100 559 -166 133 -132 3425 -98 99 -13334 65 -66 129 -822 99 -598 133 -366 131 -68 163 -100 131 -66 99 -98 361 -130 63 -296 2175 -100 163 -100 1095 -100 859 -7534 165 -5584 99 -198 99 -1066 99 -232 99 -266 67 -66 99 -132 97 -564 131 -66 197 -132 231 -98 261 -560 195 -166 885 -98 1579 -198 729 -134 431 -68 5783 -5922 131 -992 65 -166 1459 -262 65 -526 97 -100 163 -230 65 -362 197 -958 99 -634 99 -132 65 -234 99 -394 527 -132 461 -130 323 -18704 231 -564 99 -600 133 -98 1359 -66 165 -166 133 -232 1657 -98 1907 -132 4497 -496 99 -66 167 -100 133 -168 233 -264 167 -66 863 -296 399 -68 2453 -134 635 -66 331 -200 395 -134 7025 -4436 165 -528 99 -298 229 -162 97 -362 197 -524 361 -428 65 -66 99 -166 199 -66 67 -198 165 -466 99 -428 263 -330 361 -132 293 -296 225 -66 3753 -66 291 -66 133 -66 6907 -4898 63 -558 65 -918 99 -100 623 -132 359 -100 457 -98 97 -98 363 -100 559 -132 297 -166 129 -296 97 -262 99 -722 297 -196 1187 -266 199 -168 133 -68 597 -66 301 -132 429 -64 6665 -5372 65 -1054 329 -200 167 -100 629 -100 293 -200 165 -100 133 -132 133 -332 197 -100 233 -496 65 -166 561 -232 165 -296 1127 -64 2753 -66 755 -164 12059 -360 65 -266 99 -66 65 -2612 99 -1780 97 -164 1083 -330 197 -100 397 -66 2095 -98 793 -102 65 -100 10223 -594 65 -232 97 -984 131 -296 99 -166 99 -664 197 -66 261 -230 97 -68 1683 -266 691 -98 335 -66 133 -8384 99 -2300 97 -1710 165 -230 229 -426 97 -134 359 -300 555 -164 193 -132 99 -132 163 -100 497 -298 823 -12964 99 -1874 97 -132 97 -262 97 -328 133 -100 231 -198 131 -132 195 -460 63 -198 131 -66 195 -66 1791 -100 97 -66 131 -64 955 -66 559 -98 233 -66 4633 -5270 263 -100 131 -198 99 -164 331 -132 303 -134 133 -430 233 -1192 67 -498 99 -302 233 -300 597 -66 99 -132 1261 -134 65 -5292 63 -756 131 -884 131 -130 231 -98 197 -198 195 -66 97 -592 261 -298 163 -922 163 -164 65 -656 131 -68 231 -66 99 -430 1295 -68 529 -100 1263 -68 1687 -14880 99 -236 65 -566 163 -758 99 -68 461 -98 559 -130 165 -162 995 -100 1787 -196 195 -524 295 -66 821 -66 5703 -3860 63 -3736 523 -166 161 -64 97 -132 163 -164 257 -164 197 -656 229 -1420\nRAW_Data: 99 -598 97 -168 297 -132 199 -232 629 -132 131 -100 97 -266 499 -66 597 -166 229 -66 429 -68 3463 -3518 97 -2346 265 -66 433 -66 503 -464 65 -68 987 -98 131 -98 557 -66 1583 -66 431 -100 1097 -134 5651 -6742 99 -830 197 -102 233 -398 67 -66 197 -168 267 -366 197 -332 363 -364 131 -2374 329 -66 331 -200 133 -100 333 -134 299 -66 4079 -5972 101 -1558 393 -230 331 -330 63 -98 65 -328 131 -426 133 -66 197 -196 65 -1352 65 -590 97 -230 161 -96 163 -230 329 -198 97 -134 1525 -66 433 -168 1455 -230 6225 -2024 99 -266 99 -268 133 -264 297 -100 167 -168 233 -168 267 -660 369 -234 337 -300 97 -132 263 -134 1133 -200 137 -136 265 -166 167 -232 99 -300 167 -66 99 -200 197 -462 131 -432 99 -300 165 -862 197 -496 531 -332 231 -132 1015 -100 427 -100 197 -1018 99 -66 97 -200 199 -168 199 -100 465 -100 197 -68 65 -132 3155 -66 12365 -328 131 -696 133 -896 265 -472 165 -132 133 -232 129 -68 165 -66 723 -564 529 -66 1491 -266 99 -132 397 -66 501 -6672 65 -1580 97 -1420 199 -166 265 -134 699 -168 133 -100 663 -66 637 -68 2119 -66 697 -66 1027 -66 3319 -7648 97 -162 293 -460 197 -230 591 -166 227 -98 65 -262 329 -198 163 -128 97 -328 131 -426 229 -368 299 -202 265 -168 331 -100 729 -68 1597 -66 133 -66 961 -68 231 -166 99 -234 5397 -5158 65 -1328 97 -502 99 -166 199 -198 131 -68 199 -264 231 -436 163 -332 199 -100 131 -166 165 -168 97 -100 133 -600 165 -1754 99 -66 195 -228 263 -164 297 -232 365 -200 499 -66 297 -66 1625 -66 399 -166 5011 -6110 97 -1482 65 -264 367 -166 65 -68 465 -892 197 -330 231 -430 133 -12408 129 -5982 295 -562 165 -132 1033 -66 997 -66 799 -66 297 -196 959 -100 729 -66 2115 -66 2639 -5752 99 -802 365 -232 97 -362 97 -2510 131 -534 99 -296 131 -268 1161 -66 131 -66 1057 -100 1325 -66 463 -4028 97 -364 65 -728 197 -234 99 -100 97 -100 65 -132 265 -428 333 -628 195 -262 99 -228 131 -1766 163 -562 297 -132 465 -302 165 -100 133 -198 365 -132 465 -66 1787 -4594 65 -2792 65 -164 131 -66 131 -98 197 -296 131 -332 165 -330 693 -298 101 -366 265 -1892 65 -134 65 -164 201 -200 65 -266 265 -232 133 -200 235 -200 1001 -68 991 -66 597 -100 963 -200 2913 -9784 65 -426 229 -954 131 -398 531 -198 97 -98 163 -130 131 -326 359 -100 2367 -168 691 -100 13125 -362 65 -394 197 -332 133 -200 299 -366 167 -264 299 -132 835 -234 2493 -132\nRAW_Data: 99 -134 565 -334 231 -134 167 -200 1425 -132 3943 -3864 563 -66 233 -2296 99 -198 165 -234 199 -298 263 -134 131 -530 299 -398 233 -1336 165 -166 65 -132 99 -232 295 -130 99 -328 97 -132 97 -66 97 -132 65 -262 2145 -132 195 -196 65 -362 231 -196 261 -98 65 -198 229 -132 229 -196 793 -66 555 -100 297 -66 493 -230 359 -100 195 -296 229 -66 99 -166 10343 -3654 165 -1392 65 -834 65 -132 495 -100 395 -232 331 -360 265 -132 1681 -5474 165 -2122 65 -200 65 -232 99 -1132 65 -200 165 -102 133 -68 199 -100 663 -234 165 -298 163 -68 1597 -98 1199 -66 197 -332 5539 -1934 65 -3354 133 -168 167 -2354 101 -930 65 -1266 131 -298 99 -1328 259 -232 563 -200 297 -100 65 -132 131 -66 1681 -100 267 -98 331 -168 299 -132 629 -66 5981 -2810 165 -3282 99 -132 227 -98 163 -430 231 -330 293 -66 65 -164 99 -958 65 -134 67 -432 99 -1426 297 -264 295 -132 363 -332 97 -366 133 -98 2869 -102 1755 -100 2911 -7466 261 -196 193 -260 259 -298 135 -66 133 -400 199 -266 195 -334 97 -268 365 -864 129 -1132 395 -300 395 -132 933 -200 367 -134 997 -66 233 -198 5415 -5556 65 -1292 65 -68 131 -268 165 -264 231 -1792 65 -764 65 -1394 299 -396 451 -854 323 -808 97 -62 227 -838 231 -868 635 -460 669 -402 275 -810 271 -838 681 -398 711 -388 691 -392 275 -832 279 -780 311 -774 741 -376 285 -776 321 -762 325 -776 297 -776 765 -324 345 -752 345 -724 341 -756 767 -326 773 -334 743 -340 771 -348 313 -770 741 -350 753 -556 497 -4666 359 -736 779 -318 759 -326 351 -758 347 -726 339 -760 767 -322 775 -334 351 -734 341 -732 769 -318 781 -326 777 -334 355 -724 349 -740 347 -724 775 -324 349 -734 353 -734 355 -756 347 -724 771 -326 347 -758 345 -732 353 -732 755 -350 771 -320 765 -322 763 -356 317 -758 755 -352 755 -524 557 -4642 323 -746 795 -320 773 -296 389 -722 347 -726 375 -724 767 -324 779 -332 355 -724 347 -724 775 -326 779 -334 747 -338 357 -730 355 -728 355 -756 743 -350 355 -722 353 -720 349 -754 345 -758 743 -346 331 -756 323 -744 361 -752 761 -322 773 -332 747 -336 779 -310 343 -764 735 -348 773 -516 561 -4620 349 -744 781 -320 763 -330 351 -756 347 -724 341 -758 769 -324 775 -332 351 -736 341 -734 771 -324 777 -322 773 -332 355 -724 349 -724 377 -724 769 -324 347 -758 345 -734 351 -732 351 -722 791 -320 351 -726 359 -736 321 -768 753 -322 801 -330 753 -334 755 -336 353 -732 765 -354 727 -548\nRAW_Data: 545 -4646 351 -738 755 -322 801 -296 389 -722 347 -740 347 -726 773 -326 777 -334 355 -730 355 -730 779 -308 777 -346 743 -348 321 -758 353 -722 351 -750 769 -314 333 -742 351 -740 361 -720 363 -754 761 -322 345 -756 347 -724 339 -756 767 -324 775 -332 745 -338 779 -310 357 -728 777 -348 739 -550 513 -4672 323 -776 749 -320 763 -328 353 -758 347 -740 347 -726 771 -326 777 -334 355 -724 349 -758 743 -326 777 -332 747 -340 351 -732 343 -736 351 -756 755 -320 371 -724 375 -724 335 -746 337 -746 771 -326 349 -756 347 -722 341 -756 767 -326 775 -332 747 -338 777 -310 343 -766 745 -346 759 -546 509 -4650 345 -770 743 -324 785 -326 347 -758 345 -734 349 -734 769 -344 763 -296 373 -744 333 -754 765 -322 777 -332 745 -340 351 -732 343 -734 351 -756 753 -322 367 -724 341 -760 335 -748 337 -756 767 -324 345 -756 345 -734 351 -732 767 -314 797 -296 769 -322 801 -296 353 -756 777 -332 747 -536 537 -4634 359 -742 777 -320 763 -328 349 -756 347 -726 339 -756 767 -322 775 -334 355 -732 355 -728 779 -310 757 -338 757 -342 351 -732 343 -734 353 -758 753 -322 367 -724 341 -760 335 -748 337 -756 765 -324 347 -736 351 -724 377 -726 769 -324 777 -334 747 -338 779 -310 343 -764 739 -330 769 -546 523 -4664 343 -758 743 -346 761 -324 347 -752 335 -754 333 -746 769 -324 775 -334 351 -734 341 -734 773 -324 775 -322 779 -332 355 -722 349 -740 347 -724 773 -324 351 -756 347 -724 339 -758 337 -748 769 -322 347 -756 347 -722 341 -756 767 -324 777 -332 747 -338 779 -310 341 -766 771 -298 767 -548 523 -4658 329 -770 777 -320 755 -328 349 -756 347 -740 347 -726 773 -324 777 -332 357 -722 349 -754 743 -326 779 -334 745 -342 351 -734 349 -752 347 -722 781 -312 333 -780 321 -742 361 -752 331 -752 763 -324 343 -756 347 -734 351 -734 767 -322 765 -352 753 -346 753 -296 373 -750 763 -322 775 -530 535 -4632 359 -736 779 -320 761 -328 351 -732 351 -758 347 -724 775 -326 781 -330 355 -724 349 -756 743 -326 779 -334 745 -340 351 -732 341 -762 317 -776 749 -320 367 -722 339 -758 337 -746 337 -746 773 -326 347 -736 351 -754 347 -726 771 -324 779 -332 747 -340 779 -310 343 -768 745 -348 753 -516 543 -4650 383 -716 769 -348 753 -326 343 -750 333 -754 335 -744 769 -324 775 -334 351 -734 343 -736 771 -324 775 -322 775 -332 351 -734 353 -722 349 -754 755 -318 337 -768 319 -766 325 -746 361 -752 763 -320 345 -756 345 -726\nRAW_Data: 337 -746 771 -324 777 -334 743 -340 769 -352 311 -768 741 -324 781 -520 561 -4636 359 -736 777 -320 757 -326 349 -756 347 -724 341 -758 769 -324 777 -332 351 -734 341 -734 773 -324 779 -320 773 -332 349 -734 353 -722 349 -756 753 -352 301 -766 319 -768 325 -746 365 -752 761 -322 345 -724 377 -734 351 -732 767 -316 761 -332 767 -322 769 -330 351 -766 745 -336 777 -512 541 -4668 325 -742 779 -320 761 -330 351 -756 347 -740 347 -724 773 -324 779 -332 355 -730 355 -726 777 -346 755 -308 777 -346 319 -760 309 -764 347 -752 751 -320 367 -724 375 -722 337 -746 337 -746 771 -326 349 -756 347 -724 339 -756 767 -324 775 -330 745 -340 769 -322 341 -766 743 -322 781 -552 529 -4628 359 -736 781 -318 759 -328 351 -734 341 -766 323 -746 779 -322 761 -328 351 -766 353 -724 777 -330 751 -336 753 -336 355 -728 355 -726 355 -756 741 -352 353 -722 351 -722 347 -752 349 -738 777 -320 361 -724 339 -756 335 -746 769 -326 775 -334 747 -338 755 -340 357 -724 783 -330 747 -538 539 -4658 347 -750 753 -320 763 -330 351 -732 353 -756 347 -724 775 -326 781 -332 355 -724 347 -756 741 -326 779 -334 747 -340 351 -734 349 -752 347 -722 779 -312 333 -780 323 -740 361 -752 331 -754 763 -320 345 -756 345 -724 339 -746 771 -324 779 -332 747 -338 769 -320 343 -766 741 -348 753 -556 531 -4624 349 -776 747 -320 765 -328 351 -758 347 -726 339 -758 767 -324 777 -332 351 -732 343 -732 771 -318 781 -324 777 -332 355 -722 349 -722 379 -734 745 -340 351 -732 345 -734 349 -760 323 -746 793 -322 341 -722 379 -722 339 -744 771 -324 775 -334 745 -340 757 -340 355 -728 779 -346 741 -548 511 -4676 323 -744 777 -318 763 -328 351 -758 347 -724 377 -722 767 -326 777 -332 355 -732 353 -724 777 -332 745 -338 777 -310 343 -734 349 -744 353 -742 759 -322 371 -734 351 -732 345 -734 353 -756 755 -322 367 -724 341 -770 349 -734 767 -318 765 -332 765 -320 761 -328 351 -756 745 -362 747 -536 537 -4662 317 -778 747 -322 797 -294 389 -724 347 -728 337 -746 771 -326 777 -334 351 -734 343 -734 777 -326 773 -322 775 -330 355 -724 349 -732 351 -732 767 -352 333 -736 351 -738 325 -780 331 -752 763 -322 345 -736 351 -758 345 -724 769 -324 777 -332 743 -340 769 -356 317 -752 769 -314 787 -514 539 -4638 345 -768 773 -320 753 -326 351 -760 347 -712 379 -724 773 -322 777 -334 351 -734 355 -718 789 -320 769 -324 759 -348 323 -758 355 -720 351 -720\nRAW_Data: 801 -314 333 -740 351 -740 361 -720 363 -754 761 -322 345 -726 377 -724 337 -746 769 -324 777 -334 743 -340 769 -320 343 -768 739 -324 783 -552 529 -4634 359 -736 779 -320 757 -326 351 -760 345 -722 341 -756 767 -322 777 -334 349 -736 353 -720 763 -354 759 -320 755 -350 331 -740 353 -740 327 -748 793 -320 341 -758 345 -722 337 -746 339 -746 773 -324 349 -756 345 -724 341 -756 767 -326 775 -334 745 -338 777 -310 343 -764 771 -296 767 -548 523 -4650 347 -742 781 -320 765 -328 351 -732 351 -758 347 -724 777 -326 779 -332 353 -724 349 -754 741 -326 779 -334 745 -342 351 -732 349 -754 347 -722 779 -348 299 -778 321 -742 363 -718 363 -754 763 -322 343 -758 345 -732 351 -732 769 -354 725 -350 755 -348 737 -350 353 -722 763 -354 757 -510 543 -4652 353 -742 791 -288 773 -330 351 -734 387 -722 347 -724 775 -326 781 -330 355 -732 355 -730 745 -342 777 -346 755 -310 357 -726 355 -724 353 -758 755 -348 355 -722 351 -722 349 -756 345 -756 747 -346 329 -752 321 -746 361 -752 763 -322 773 -332 751 -336 745 -342 341 -768 749 -346 753 -514 543 -4656 349 -756 755 -318 789 -316 333 -744 353 -742 359 -718 791 -320 773 -332 357 -724 349 -740 779 -296 785 -334 755 -336 353 -732 341 -736 353 -750 753 -320 369 -724 375 -712 375 -712 375 -736 749 -338 353 -730 343 -734 349 -754 753 -320 801 -294 779 -338 777 -310 341 -764 771 -296 799 -514 523 -4664 347 -756 745 -346 757 -318 349 -756 335 -756 335 -746 767 -324 777 -332 353 -730 343 -732 771 -326 775 -324 777 -332 357 -724 347 -726 375 -736 747 -338 353 -732 341 -734 349 -756 325 -744 793 -320 343 -726 377 -724 337 -746 769 -324 777 -334 747 -338 767 -322 341 -766 741 -322 785 -518 563 -4622 349 -744 783 -320 761 -326 353 -756 347 -724 375 -724 767 -324 777 -334 355 -724 349 -740 745 -330 779 -336 747 -342 353 -732 351 -722 383 -718 781 -314 333 -742 353 -742 361 -716 363 -754 765 -322 345 -724 379 -724 337 -746 769 -324 779 -334 745 -340 777 -312 341 -764 771 -296 769 -546 523 -4654 359 -734 779 -320 757 -328 349 -756 347 -720 377 -742 743 -326 777 -334 353 -722 349 -754 743 -360 745 -336 743 -342 343 -736 329 -768 321 -768 757 -322 369 -722 341 -770 349 -734 311 -768 777 -326 311 -778 331 -746 335 -754 767 -324 775 -332 745 -340 777 -312 355 -760 755 -346 741 -88244 133 -132 99 -232 165 -100 97 -730 99 -66 2213 -264 995 -100 631 -132 6925 -4032\nRAW_Data: 133 -66 65 -2118 165 -1032 197 -66 65 -66 197 -66 99 -132 267 -66 297 -434 133 -134 133 -68 65 -66 131 -266 299 -64 67 -362 161 -296 165 -132 229 -66 361 -498 197 -132 657 -132 1031 -98 1595 -100 965 -132 165 -7880 99 -164 99 -564 67 -928 165 -166 231 -100 65 -166 333 -66 165 -266 165 -398 953 -132 1755 -66 1541 -98 4449 -8044 99 -166 303 -368 591 -132 359 -264 227 -164 99 -1550 299 -200 435 -100 67 -66 593 -132 2943 -134 895 -198 3879 -7540 99 -938 99 -860 99 -1392 99 -1474 197 -166 199 -134 99 -264 233 -134 265 -100 397 -134 1497 -2162 99 -1496 67 -1392 99 -230 233 -66 735 -66 567 -364 197 -66 99 -232 2109 -64 429 -98 99 -98 131 -66 359 -198 195 -162 4861 -4980 131 -950 131 -228 359 -1078 297 -398 231 -262 231 -66 65 -726 295 -366 199 -232 229 -328 459 -68 231 -132 367 -132 3483 -100 597 -134 7113 -1274 99 -236 165 -4414 199 -1432 67 -298 267 -166 329 -234 529 -200 197 -296 129 -294 163 -98 65 -362 131 -100 65 -1426 959 -332 265 -66 297 -166 2683 -66 535 -66 199 -4286 99 -164 525 -1224 363 -166 199 -270 131 -1758 165 -200 557 -298 163 -132 329 -68 233 -66 965 -100 99 -166 365 -234 1391 -9070 131 -1526 97 -1546 131 -98 329 -364 363 -132 329 -164 1183 -10266 65 -6530 65 -732 131 -398 197 -230 195 -98 65 -364 63 -458 65 -198 65 -658 65 -196 163 -98 1805 -98 463 -164 99 -66 623 -98 423 -228 325 -66 233 -100 5543 -5982 97 -1486 331 -198 131 -526 133 -232 131 -132 131 -66 231 -98 131 -134 629 -2026 65 -266 163 -334 265 -66 363 -134 2383 -134 131 -68 233 -98 6503 -98 1329 -9556 65 -166 463 -66 133 -166 233 -266 203 -68 67 -168 99 -66 1161 -66 1667 -132 265 -168 197 -100 199 -200 889 -15904 99 -132 133 -300 1093 -198 201 -132 133 -366 1193 -166 101 -66 935 -134 367 -14672 195 -824 131 -788 395 -430 231 -166 99 -68 99 -17390 65 -1062 65 -498 99 -134 99 -368 65 -66 65 -464 65 -332 265 -132 231 -66 597 -266 133 -330 165 -100 2127 -234 1395 -132 10439 -1004 67 -698 97 -2784 65 -66 265 -264 133 -528 331 -100 131 -234 2027 -164 693 -100 65 -134 197 -130 97 -66 457 -100 4649 -4556 65 -3626 67 -464 65 -100 133 -100 131 -532 67 -566 133 -134 231 -564 65 -166 99 -134 165 -102 65 -1206 199 -666 1261 -66 1031 -132 869 -134 199 -66 597 -100 7409 -7344 97 -296 99 -232 99 -232 65 -662 263 -66 131 -266 397 -100 363 -66 2553 -98 531 -166\nRAW_Data: 7313 -3784 133 -168 133 -1022 99 -558 361 -68 65 -66 97 -166 229 -132 265 -198 97 -792 97 -132 97 -558 163 -264 161 -658 197 -294 231 -328 193 -130 295 -166 231 -166 65 -98 489 -66 885 -100 921 -66 461 -66 323 -304 99 -3408 65 -166 99 -66 195 -296 265 -134 133 -364 65 -296 197 -228 97 -294 231 -368 1387 -66 133 -166 1133 -100 333 -68 431 -66 165 -134 4955 -5736 97 -1088 99 -100 99 -166 199 -264 131 -68 165 -398 199 -630 99 -66 233 -68 403 -98 233 -662 133 -1126 65 -228 131 -98 65 -490 199 -132 229 -232 491 -64 429 -66 667 -100 167 -200 531 -100 263 -264 295 -100 429 -14212 97 -956 65 -930 363 -66 299 -200 331 -66 63 -100 131 -230 1215 -66 1425 -132 927 -132 8041 -732 133 -1064 97 -270 269 -298 129 -228 65 -262 65 -198 459 -100 667 -166 129 -688 67 -2106 97 -232 329 -64 1065 -66 99 -334 167 -132 969 -100 1855 -98 1189 -100 5599 -4798 99 -2932 557 -200 163 -492 295 -758 457 -1788 99 -198 131 -402 197 -134 129 -524 231 -266 1295 -68 1135 -102 701 -10576 65 -5130 63 -100 97 -464 395 -298 99 -100 365 -134 201 -100 99 -100 2515 -98 1611 -132 327 -3806 97 -2502 197 -654 131 -196 97 -196 163 -392 263 -1018 97 -826 197 -130 331 -298 367 -98 267 -68 803 -236 367 -132 851 -166 299 -100 6833 -6158 65 -1062 199 -294 163 -326 227 -724 65 -164 1215 -132 855 -198 467 -66 431 -200 393 -100 229 -98 10229 -338 197 -1756 97 -298 263 -166 97 -164 195 -228 131 -198 199 -234 533 -66 529 -134 397 -100 165 -166 199 -168 99 -100 99 -166 301 -202 529 -66 921 -98 6861 -7702 65 -400 65 -1856 201 -134 65 -100 197 -166 199 -300 165 -134 65 -20006 99 -332 561 -164 199 -630 133 -66 99 -134 131 -200 2393 -134 199 -132 1127 -6284 67 -1900 97 -494 131 -490 163 -264 231 -66 197 -130 327 -164 129 -332 133 -132 3021 -13676 97 -1056 63 -232 65 -264 295 -596 731 -66 295 -66 161 -66 589 -68 3121 -66 199 -68 299 -100 629 -68 8427 -262 405 -744 739 -362 743 -338 353 -760 317 -756 313 -768 741 -360 741 -356 339 -756 307 -786 741 -358 743 -334 743 -374 311 -772 295 -774 319 -764 753 -352 341 -756 307 -758 337 -744 339 -772 743 -338 353 -726 353 -760 313 -766 773 -312 753 -330 771 -354 737 -362 317 -754 745 -364 743 -538 539 -4668 325 -742 781 -320 761 -328 349 -756 347 -724 341 -744 775 -326 777 -334 355 -732 353 -722 779 -332 745 -338 769 -352 311 -768 317 -760 325 -776\nRAW_Data: 761 -320 345 -734 351 -732 345 -770 317 -760 753 -322 369 -724 341 -756 335 -756 765 -324 775 -332 747 -338 755 -340 343 -736 781 -346 755 -516 543 -4650 349 -756 755 -352 755 -314 333 -776 321 -740 361 -752 763 -322 773 -332 353 -722 347 -736 781 -332 745 -338 771 -318 343 -736 351 -758 325 -774 761 -322 341 -736 349 -736 349 -756 347 -724 773 -326 347 -756 345 -724 341 -768 747 -338 777 -344 741 -344 739 -348 353 -722 763 -354 757 -510 543 -4654 353 -740 757 -354 771 -296 353 -756 347 -724 341 -758 769 -326 777 -332 349 -736 341 -736 775 -326 775 -320 773 -330 355 -720 349 -736 349 -754 741 -330 351 -756 347 -722 343 -758 337 -744 771 -324 347 -756 345 -736 349 -734 767 -346 765 -296 769 -354 739 -330 353 -758 743 -364 747 -536 535 -4668 325 -740 777 -320 761 -328 349 -758 347 -722 341 -758 769 -324 777 -334 353 -732 353 -722 777 -332 745 -338 769 -354 315 -752 317 -768 319 -766 755 -322 371 -724 375 -724 337 -746 339 -754 767 -326 345 -756 345 -734 351 -732 755 -350 767 -314 765 -346 757 -324 349 -744 767 -326 775 -530 537 -4636 359 -738 777 -318 761 -328 353 -732 389 -696 389 -724 777 -330 747 -336 357 -730 355 -728 779 -310 779 -348 741 -348 321 -758 353 -722 351 -752 769 -314 333 -742 353 -740 361 -718 363 -752 763 -322 345 -734 351 -726 377 -726 771 -324 777 -332 747 -338 769 -320 343 -766 739 -324 783 -522 561 -4654 315 -778 747 -320 767 -328 351 -758 347 -726 341 -758 765 -324 779 -330 357 -724 347 -724 773 -326 779 -334 747 -340 357 -728 355 -728 355 -758 739 -352 355 -722 341 -736 353 -754 321 -744 793 -318 345 -724 379 -724 337 -746 771 -324 777 -332 755 -336 779 -310 343 -732 779 -346 757 -514 543 -4650 351 -756 753 -352 751 -348 329 -752 321 -746 361 -752 761 -322 775 -298 387 -730 355 -724 781 -296 781 -336 755 -336 353 -730 343 -736 349 -756 751 -322 369 -734 351 -732 345 -732 353 -756 751 -322 369 -726 375 -712 375 -734 747 -338 777 -310 771 -352 733 -344 341 -740 757 -322 803 -528 533 -4634 359 -736 779 -318 763 -328 349 -758 347 -724 339 -756 767 -324 777 -334 351 -734 343 -732 773 -326 775 -322 777 -332 355 -730 357 -722 347 -726 773 -326 349 -734 353 -732 387 -698 389 -724 777 -296 387 -722 347 -726 375 -714 773 -324 777 -334 745 -342 779 -310 343 -766 747 -346 757 -514 543 -4654 341 -764 779 -296 767 -320 373 -724 377 -724 335 -746 771 -324 777 -332\nRAW_Data: 353 -734 343 -732 771 -324 777 -322 779 -332 355 -732 355 -724 347 -724 777 -324 349 -756 347 -724 339 -746 375 -734 747 -338 351 -732 343 -734 349 -756 753 -320 801 -328 747 -336 757 -336 357 -724 781 -332 745 -536 539 -4642 359 -738 779 -318 761 -328 351 -736 353 -756 347 -724 771 -324 777 -334 357 -724 349 -724 775 -326 779 -334 745 -340 351 -732 345 -734 351 -756 753 -322 369 -724 377 -702 383 -734 341 -734 773 -324 345 -752 333 -748 335 -748 771 -324 775 -334 745 -338 779 -312 357 -728 777 -350 741 -548 511 -4676 325 -740 781 -320 763 -328 351 -758 347 -724 341 -746 775 -326 775 -334 355 -732 355 -724 779 -330 747 -336 779 -310 357 -728 355 -724 353 -756 767 -322 351 -722 345 -766 319 -762 325 -778 761 -320 345 -724 377 -724 337 -758 767 -322 775 -332 755 -336 745 -342 341 -732 769 -330 769 -546 523 -4664 345 -742 779 -320 759 -326 349 -736 353 -756 347 -726 771 -324 779 -334 351 -734 341 -736 779 -324 773 -320 777 -296 389 -728 357 -722 349 -724 777 -326 347 -726 379 -734 351 -734 351 -722 793 -322 351 -720 343 -762 323 -744 779 -320 763 -328 781 -334 755 -336 353 -734 767 -316 761 -536 541 -4650 349 -756 753 -352 755 -312 333 -746 353 -742 361 -752 765 -322 773 -298 389 -722 349 -724 775 -326 781 -332 747 -338 357 -728 355 -726 355 -724 799 -322 351 -718 347 -740 353 -738 359 -720 793 -320 343 -724 379 -726 337 -748 767 -324 779 -332 747 -338 757 -340 357 -728 755 -342 757 -538 539 -4660 317 -780 751 -320 767 -328 353 -756 347 -740 347 -726 771 -326 777 -334 355 -730 357 -724 781 -330 747 -336 747 -342 353 -732 349 -754 347 -720 777 -316 341 -746 361 -720 363 -754 331 -748 767 -322 347 -732 355 -758 321 -754 769 -320 765 -354 757 -322 757 -352 331 -736 777 -320 757 -556 533 -4634 359 -738 779 -320 761 -326 351 -756 347 -722 373 -726 767 -322 775 -334 355 -724 349 -724 773 -326 779 -334 747 -338 353 -732 343 -734 349 -760 751 -320 371 -740 347 -726 375 -726 335 -748 769 -322 347 -734 351 -732 345 -768 743 -322 785 -324 777 -334 753 -336 357 -728 777 -312 777 -546 509 -4670 323 -744 779 -320 763 -328 353 -732 353 -732 389 -722 777 -330 747 -338 357 -724 349 -756 741 -328 779 -336 745 -340 341 -736 347 -742 351 -740 791 -288 373 -724 379 -712 377 -724 337 -748 769 -322 347 -758 347 -724 339 -746 771 -324 777 -334 747 -340 767 -322 341 -766 739 -348 755 -558 527 -4632\nRAW_Data: 359 -736 781 -318 761 -326 351 -756 347 -726 339 -748 771 -324 783 -332 351 -736 341 -734 771 -324 775 -322 775 -332 351 -736 341 -734 347 -750 753 -322 367 -742 347 -724 339 -746 375 -734 747 -338 357 -728 353 -728 353 -724 775 -350 767 -320 757 -350 739 -352 353 -724 765 -322 761 -556 523 -4648 347 -754 753 -322 767 -328 351 -734 387 -732 355 -722 779 -330 747 -338 355 -730 355 -728 753 -340 777 -312 791 -310 357 -728 355 -728 353 -758 741 -352 355 -722 351 -720 383 -720 345 -758 745 -346 331 -756 323 -742 361 -752 763 -322 773 -332 749 -336 777 -310 343 -734 781 -346 755 -514 543 -4652 349 -752 769 -348 749 -318 347 -746 337 -754 335 -756 767 -320 775 -332 351 -734 343 -736 775 -326 775 -320 773 -332 351 -734 341 -732 349 -754 749 -322 367 -732 353 -732 343 -734 349 -756 753 -322 371 -734 349 -734 343 -734 775 -322 783 -322 777 -332 747 -338 357 -728 777 -312 791 -508 541 -4660 349 -750 751 -320 765 -330 351 -732 353 -732 389 -722 781 -296 785 -334 357 -730 355 -724 779 -330 747 -338 779 -310 357 -728 355 -726 355 -758 737 -352 341 -732 349 -752 323 -744 363 -746 763 -322 347 -756 345 -726 337 -758 767 -322 775 -332 747 -338 779 -310 343 -764 771 -296 767 -548 523 -4660 345 -756 771 -326 771 -318 345 -724 379 -722 337 -758 767 -324 777 -330 351 -734 341 -738 775 -326 773 -320 775 -300 383 -736 309 -764 347 -754 749 -322 367 -722 341 -760 337 -746 339 -746 773 -324 347 -756 345 -724 339 -746 773 -326 777 -332 747 -340 779 -310 357 -726 765 -356 757 -524 545 -4646 351 -740 791 -320 771 -328 357 -722 349 -724 377 -726 767 -322 777 -334 351 -734 341 -734 769 -316 783 -324 777 -332 353 -732 343 -734 349 -752 751 -322 367 -736 349 -732 353 -752 347 -722 781 -312 333 -744 353 -744 361 -754 763 -320 771 -332 753 -336 747 -340 353 -734 787 -318 759 -548 511 -4672 323 -778 749 -320 763 -328 353 -758 347 -740 347 -726 775 -326 777 -332 357 -722 349 -726 775 -326 777 -334 755 -338 351 -732 343 -732 353 -756 755 -320 369 -722 377 -712 375 -702 389 -724 779 -294 353 -758 347 -724 377 -722 769 -322 777 -332 747 -338 779 -310 343 -764 771 -296 767 -548 523 -4666 345 -758 745 -346 759 -324 347 -752 335 -754 333 -748 769 -322 777 -332 353 -732 343 -730 771 -326 777 -322 777 -334 351 -734 353 -720 351 -754 755 -318 335 -766 319 -764 357 -718 363 -754 765 -318 345 -724 379 -732 351 -732\nRAW_Data: 765 -316 791 -316 775 -322 775 -330 357 -730 747 -340 781 -510 543 -4670 325 -740 779 -322 761 -328 351 -764 319 -756 347 -756 741 -328 777 -336 349 -736 313 -766 771 -326 777 -322 773 -332 353 -720 349 -734 349 -764 735 -350 333 -742 351 -740 325 -780 331 -746 767 -324 343 -756 345 -724 339 -754 767 -324 775 -334 745 -338 771 -350 311 -768 741 -356 751 -91544 97 -1916 131 -560 161 -784 65 -64 495 -98 589 -296 2305 -15072 197 -1258 65 -130 97 -164 97 -98 297 -198 201 -234 261 -98 99 -66 1333 -166 759 -98 95 -196 295 -66 491 -64 163 -66 4795 -4508 131 -922 65 -166 129 -164 163 -66 131 -164 65 -164 99 -196 163 -264 229 -164 65 -164 131 -1932 231 -164 261 -130 65 -264 65 -362 727 -130 2635 -66 327 -66 2397 -4778 65 -364 67 -1134 67 -438 99 -864 163 -366 1187 -830 99 -328 165 -100 131 -134 199 -568 99 -100 99 -466 231 -560 131 -100 129 -132 329 -64 263 -296 1049 -132 333 -98 703 -364 231 -13082 99 -2554 133 -832 133 -164 395 -132 295 -164 65 -66 131 -66 131 -132 65 -134 955 -100 99 -66 961 -66 4911 -4040 65 -2524 165 -896 65 -498 65 -66 99 -232 133 -268 197 -300 265 -266 133 -400 395 -166 297 -98 131 -1020 97 -492 199 -100 563 -132 99 -66 133 -100 3051 -66 591 -196 195 -162 1921 -66 9139 -3400 129 -692 301 -1262 131 -100 265 -166 337 -134 363 -66 133 -332 233 -134 919 -64 2019 -132 295 -132 165 -200 6921 -4912 133 -336 165 -98 99 -66 261 -298 63 -100 65 -264 163 -296 297 -132 465 -100 65 -332 195 -196 165 -334 97 -100 131 -132 165 -300 629 -66 65 -66 165 -134 663 -134 233 -66 861 -66 3939 -134 567 -1310 163 -2894 229 -168 99 -234 431 -494 97 -396 133 -66 1563 -100 497 -198 265 -14052 65 -524 99 -398 923 -66 3991 -6098 99 -1626 99 -1394 99 -100 265 -200 163 -168 97 -432 99 -100 133 -200 4699 -4592 63 -1486 65 -1786 131 -66 197 -334 101 -400 99 -200 99 -168 131 -100 99 -430 231 -234 167 -2338 763 -364 263 -66 131 -200 263 -132 333 -66 3823 -198 1091 -1186 65 -756 65 -494 99 -1546 97 -1432 131 -66 131 -132 295 -132 131 -300 231 -562 99 -66 363 -100 663 -166 457 -66 3199 -6668 99 -962 65 -164 133 -132 131 -266 363 -430 363 -364 99 -66 365 -132 67 -2376 299 -166 165 -130 161 -360 97 -132 953 -198 2041 -66 855 -12212 95 -330 97 -164 97 -1388 65 -366 99 -964 165 -398 163 -232 427 -66 363 -98 133 -166 895 -98 1519 -132 101 -100\nRAW_Data: 133 -166 691 -100 8965 -4740 131 -196 99 -162 231 -226 195 -524 263 -98 391 -66 97 -98 489 -98 2167 -66 129 -198 263 -66 397 -9262 65 -164 97 -2798 197 -830 265 -232 463 -228 425 -230 2667 -132 67 -164 197 -98 2023 -14336 99 -922 65 -1262 531 -100 65 -200 97 -200 197 -102 133 -134 99 -168 395 -100 133 -68 199 -100 855 -66 9821 -5296 65 -4678 229 -166 265 -134 131 -100 65 -166 167 -132 1217 -66 1351 -98 393 -164 487 -12590 99 -2428 131 -434 131 -330 65 -98 131 -298 163 -66 229 -232 559 -66 1805 -66 3565 -100 4163 -4266 67 -3536 65 -824 229 -132 399 -166 67 -100 231 -166 497 -1526 99 -560 231 -134 133 -68 665 -230 291 -230 4463 -66 199 -100 1419 -832 65 -200 65 -464 199 -866 265 -264 231 -100 331 -100 65 -100 131 -100 2415 -132 827 -168 1991 -3852 65 -1928 67 -2620 99 -560 65 -362 65 -198 493 -132 525 -360 231 -100 1017 -132 555 -198 825 -100 10151 -296 131 -698 131 -300 99 -502 99 -966 293 -264 97 -66 395 -430 65 -164 131 -96 97 -98 1617 -132 455 -132 1017 -16428 65 -164 97 -458 131 -66 163 -132 429 -98 463 -66 397 -68 97 -100 367 -100 829 -332 231 -134 1655 -4488 165 -102 165 -830 197 -458 163 -164 65 -132 97 -694 297 -2498 99 -302 165 -764 133 -166 131 -100 763 -98 657 -66 819 -132 229 -130 97 -64 361 -66 401 -66 2079 -5522 99 -2848 297 -298 165 -332 99 -562 99 -1280 99 -594 165 -264 491 -132 99 -164 393 -132 461 -100 465 -132 799 -12900 65 -1660 133 -2006 613 -792 669 -430 671 -436 219 -842 289 -810 259 -826 689 -390 699 -396 311 -782 311 -774 739 -346 737 -348 741 -354 327 -774 307 -772 349 -734 769 -314 345 -742 353 -740 327 -748 363 -750 763 -324 343 -756 347 -722 337 -756 767 -324 775 -334 745 -338 777 -310 343 -766 739 -330 767 -548 523 -4662 345 -760 745 -346 757 -324 347 -752 333 -754 333 -746 769 -326 779 -332 319 -768 309 -766 773 -324 777 -322 773 -330 355 -730 353 -730 353 -722 779 -332 349 -736 311 -768 323 -750 353 -742 759 -352 341 -738 347 -756 345 -720 769 -326 775 -334 745 -340 779 -310 355 -728 787 -346 739 -546 511 -4674 323 -746 781 -320 763 -328 351 -756 347 -726 339 -746 771 -324 779 -334 357 -724 349 -724 777 -326 779 -332 747 -338 351 -732 345 -736 347 -758 751 -320 371 -734 351 -732 343 -736 351 -756 751 -322 369 -736 349 -734 343 -736 771 -324 779 -324 775 -334 755 -336 357 -728 777 -312 775 -548 509 -4676 323 -742\nRAW_Data: 783 -320 759 -328 351 -758 345 -726 375 -722 767 -326 777 -332 357 -722 349 -740 781 -296 781 -336 745 -342 351 -732 353 -754 347 -718 769 -344 333 -756 323 -744 361 -718 365 -752 763 -320 345 -758 345 -734 349 -732 767 -346 761 -316 775 -322 773 -332 351 -734 769 -320 757 -548 513 -4668 347 -752 751 -322 765 -328 353 -756 347 -726 375 -714 773 -326 775 -334 355 -730 353 -724 783 -330 745 -338 777 -312 355 -728 353 -726 353 -756 765 -322 351 -722 345 -770 319 -760 325 -750 791 -320 343 -724 379 -726 335 -746 771 -324 775 -334 745 -340 769 -322 341 -768 741 -322 783 -520 563 -4658 325 -738 777 -318 765 -326 351 -758 347 -724 341 -758 769 -324 775 -332 351 -734 341 -734 773 -324 777 -320 775 -332 355 -724 347 -740 349 -740 777 -296 353 -756 347 -724 375 -724 337 -748 767 -324 349 -732 351 -730 345 -770 743 -324 783 -324 779 -334 745 -340 357 -730 775 -312 791 -508 541 -4664 323 -776 747 -320 767 -328 351 -734 353 -732 353 -766 751 -336 747 -340 357 -730 355 -726 777 -348 755 -310 791 -310 357 -728 355 -726 355 -758 755 -348 319 -758 341 -736 323 -780 319 -744 791 -320 341 -724 377 -724 339 -756 765 -324 775 -332 745 -340 779 -310 343 -764 735 -350 771 -516 527 -4664 345 -760 743 -346 759 -324 349 -752 333 -754 335 -746 767 -324 777 -334 351 -734 341 -734 773 -324 777 -322 775 -332 351 -734 341 -734 347 -754 749 -322 367 -734 351 -732 351 -754 347 -720 785 -314 333 -742 351 -740 361 -752 763 -322 773 -332 751 -334 745 -342 341 -764 739 -332 769 -546 521 -4664 347 -756 745 -346 757 -324 347 -752 333 -754 335 -748 767 -322 777 -332 355 -724 347 -758 741 -328 779 -334 753 -338 351 -732 343 -732 349 -756 753 -322 369 -734 351 -732 343 -736 349 -756 751 -322 371 -734 349 -734 343 -736 775 -324 779 -322 779 -332 745 -338 351 -732 767 -344 765 -512 549 -4650 343 -756 771 -326 769 -320 345 -722 379 -724 339 -744 771 -324 779 -334 349 -736 341 -734 771 -324 779 -322 775 -330 351 -734 343 -732 349 -752 751 -320 369 -734 349 -734 349 -756 333 -732 775 -324 347 -752 333 -746 337 -746 771 -324 777 -332 755 -338 747 -340 353 -732 755 -354 765 -512 543 -4646 351 -744 789 -322 773 -296 387 -724 347 -724 375 -724 771 -324 775 -332 351 -734 341 -736 771 -324 777 -322 775 -332 355 -724 347 -724 377 -736 743 -340 351 -732 343 -734 349 -758 325 -746 795 -288 373 -724 377 -732 351 -732 769 -320\nRAW_Data: 765 -354 759 -320 753 -352 335 -736 779 -320 757 -556 533 -4636 359 -734 779 -318 761 -328 349 -756 347 -742 347 -722 773 -326 777 -332 357 -724 349 -756 743 -326 781 -334 747 -338 353 -732 351 -720 383 -718 783 -312 333 -744 353 -740 363 -720 363 -752 763 -322 343 -758 345 -734 351 -732 765 -346 765 -296 801 -320 773 -298 353 -766 745 -338 779 -510 543 -4670 325 -742 777 -320 761 -328 353 -766 355 -722 349 -738 777 -330 747 -338 357 -728 353 -728 777 -348 741 -350 739 -350 355 -722 351 -720 381 -722 781 -314 341 -740 361 -720 363 -754 331 -748 767 -322 347 -732 351 -730 345 -770 743 -324 785 -324 777 -334 747 -338 353 -732 765 -350 769 -498 565 -4652 333 -746 767 -324 777 -334 355 -724 347 -726 377 -724 767 -324 777 -334 351 -734 341 -734 771 -324 779 -322 775 -332 355 -730 355 -732 353 -724 779 -330 351 -734 341 -734 347 -754 323 -742 795 -320 341 -724 379 -724 337 -744 771 -324 777 -334 745 -340 777 -312 357 -728 787 -310 779 -546 511 -4674 325 -738 779 -320 763 -328 353 -732 353 -766 355 -722 779 -328 747 -336 357 -724 349 -758 741 -328 779 -334 747 -340 353 -732 351 -720 383 -716 767 -318 341 -776 333 -748 335 -756 333 -744 769 -324 347 -732 355 -758 355 -722 755 -350 757 -348 741 -350 737 -352 353 -722 765 -354 759 -524 521 -4650 349 -758 751 -322 799 -296 353 -758 349 -724 375 -724 767 -322 775 -334 353 -734 353 -720 765 -344 767 -320 757 -326 351 -734 351 -758 347 -726 773 -326 347 -758 345 -722 339 -758 335 -746 769 -326 347 -734 341 -766 323 -744 781 -320 763 -328 783 -334 749 -338 355 -728 755 -342 779 -512 543 -4670 325 -742 779 -320 761 -328 351 -756 347 -724 377 -726 767 -322 777 -334 355 -732 355 -730 745 -340 757 -338 779 -312 355 -730 353 -726 351 -756 767 -322 351 -724 383 -720 345 -758 325 -740 777 -320 363 -722 341 -758 335 -746 769 -324 777 -334 745 -340 769 -322 341 -766 739 -324 781 -522 563 -4652 317 -778 747 -320 763 -328 353 -732 387 -724 347 -722 775 -326 781 -332 355 -724 347 -724 775 -326 781 -332 747 -340 353 -732 343 -736 353 -754 749 -320 369 -726 375 -724 335 -748 339 -756 767 -322 345 -736 351 -756 345 -724 775 -324 777 -332 753 -336 747 -340 341 -764 739 -330 769 -546 521 -4660 329 -766 777 -320 757 -326 349 -758 347 -724 373 -724 769 -324 777 -332 353 -732 343 -734 777 -326 773 -320 775 -332 355 -722 349 -732 351 -732 765 -346 339 -754\nRAW_Data: 321 -744 363 -718 363 -748 765 -324 345 -734 349 -732 345 -768 745 -322 783 -326 779 -332 747 -340 353 -730 769 -352 753 -524 557 -4630 351 -740 755 -322 803 -296 387 -722 347 -724 377 -724 769 -322 779 -332 357 -722 349 -724 773 -326 779 -334 747 -338 353 -732 351 -754 345 -722 783 -312 333 -744 353 -744 363 -718 365 -754 761 -322 343 -736 351 -732 349 -756 751 -352 755 -346 753 -326 773 -322 345 -756 743 -326 781 -534 537 -4638 359 -738 779 -320 759 -326 351 -756 347 -726 341 -746 771 -326 777 -334 351 -734 341 -734 777 -324 777 -320 773 -332 355 -732 355 -730 355 -728 777 -310 357 -728 353 -758 341 -732 347 -752 751 -322 367 -724 375 -712 375 -724 767 -324 779 -332 753 -336 747 -342 353 -732 765 -344 763 -516 543 -4650 351 -756 753 -352 753 -314 333 -748 351 -744 361 -752 761 -320 773 -332 355 -724 347 -724 777 -326 781 -332 747 -338 351 -732 343 -734 349 -758 751 -322 371 -724 377 -722 335 -746 339 -746 773 -324 349 -734 353 -734 353 -756 743 -328 781 -336 747 -340 757 -340 355 -730 757 -340 769 -516 549 -4642 353 -758 755 -320 767 -330 353 -758 347 -726 339 -748 771 -324 779 -334 353 -732 343 -732 773 -324 777 -322 775 -332 355 -730 355 -724 349 -758 743 -324 351 -758 347 -724 339 -748 373 -736 747 -336 357 -726 357 -726 355 -726 797 -324 755 -320 783 -316 767 -320 367 -724 773 -326 781 -530 535 -4632 359 -738 781 -318 761 -328 351 -734 387 -724 347 -726 773 -326 779 -332 355 -730 355 -732 745 -342 777 -312 777 -314 357 -726 353 -758 353 -718 789 -322 353 -720 343 -734 349 -760 355 -718 791 -320 343 -724 377 -726 339 -748 771 -324 781 -330 747 -338 777 -312 343 -766 745 -348 757 -514 543 -4678 347 -716 769 -346 757 -324 347 -746 337 -760 333 -758 763 -320 775 -330 357 -730 355 -730 747 -342 777 -310 777 -348 319 -758 341 -734 347 -752 749 -320 371 -722 377 -724 337 -756 333 -748 767 -324 347 -734 349 -732 345 -766 775 -320 755 -328 783 -332 747 -338 357 -730 779 -310 779 -512 543 -4642 359 -738 779 -318 763 -328 351 -734 353 -734 353 -764 747 -336 757 -338 355 -728 355 -726 789 -310 769 -350 733 -342 339 -758 323 -744 363 -746 765 -322 345 -756 345 -742 345 -742 345 -742 743 -328 351 -764 319 -764 353 -728 751 -340 777 -346 739 -346 737 -352 351 -722 767 -350 755 -89978 97 -228 99 -392 97 -364 131 -364 261 -98 491 -198 265 -200 595 -166 2189 -134 1781 -98 131 -66\nRAW_Data: 4895 -3912 129 -1214 65 -132 99 -266 131 -232 201 -200 65 -100 199 -166 265 -100 263 -998 165 -198 97 -298 329 -198 563 -870 99 -166 299 -332 231 -98 163 -98 99 -164 591 -66 1607 -98 327 -11988 65 -134 131 -162 193 -1974 97 -1982 65 -300 265 -230 493 -66 99 -17484 65 -458 165 -362 99 -664 131 -1126 99 -100 99 -168 431 -98 133 -498 197 -66 2277 -100 133 -66 533 -8150 97 -328 97 -100 291 -98 1015 -96 3367 -168 133 -100 1163 -66 667 -66 393 -66 425 -66 5567 -7622 659 -366 199 -330 361 -66 129 -494 131 -264 367 -334 165 -1022 165 -66 133 -166 199 -432 595 -66 131 -98 1363 -6728 65 -2332 65 -7664 99 -1682 99 -196 163 -924 65 -330 65 -196 589 -132 229 -228 229 -198 1909 -100 1477 -66 701 -66 5773 -4316 163 -232 131 -194 65 -392 229 -458 165 -1216 461 -562 263 -462 131 -166 97 -528 97 -394 131 -558 367 -100 233 -68 133 -366 167 -166 267 -66 333 -132 2219 -168 431 -100 1461 -66 1433 -100 5641 -4648 65 -802 97 -794 65 -2290 133 -266 167 -100 165 -334 365 -134 263 -168 65 -266 67 -132 365 -100 599 -168 297 -98 567 -66 331 -198 65 -100 599 -100 1065 -100 5343 -4728 99 -1760 165 -1430 165 -198 99 -202 299 -294 197 -198 129 -64 131 -328 229 -100 357 -464 197 -100 565 -134 133 -102 401 -134 601 -164 129 -66 895 -168 397 -198 397 -100 197 -68 9979 -3582 65 -300 99 -400 63 -694 131 -798 133 -898 167 -200 131 -134 229 -264 197 -360 65 -264 97 -100 763 -100 295 -166 867 -68 65 -132 559 -12458 65 -2028 97 -132 163 -166 129 -100 195 -262 97 -296 263 -130 991 -66 1361 -134 661 -66 361 -66 197 -1088 167 -336 65 -366 65 -794 165 -68 133 -100 131 -266 133 -100 65 -198 135 -366 65 -198 729 -68 995 -100 397 -134 97 -132 363 -232 431 -64 1573 -4672 63 -8966 99 -464 197 -828 165 -602 131 -360 295 -164 63 -66 625 -398 197 -100 2475 -66 367 -132 133 -100 6651 -4462 99 -3092 101 -100 99 -234 99 -198 233 -66 165 -300 165 -132 65 -100 99 -264 65 -598 429 -2318 399 -200 67 -198 133 -166 199 -164 65 -132 165 -98 263 -64 2613 -166 697 -66 6631 -6580 131 -890 65 -66 165 -166 165 -300 133 -368 265 -726 65 -98 161 -164 97 -296 131 -98 131 -988 65 -232 201 -132 165 -100 165 -300 229 -100 261 -268 1459 -132 1575 -364 461 -132 297 -332 8997 -9896 229 -134 265 -164 267 -300 299 -300 763 -134 1933 -100 465 -100 431 -98 199 -134 6761 -2074 65 -3566 65 -132 195 -166\nRAW_Data: 133 -462 67 -166 99 -100 265 -100 131 -364 925 -132 763 -700 231 -856 165 -100 299 -164 631 -166 231 -132 429 -134 463 -66 4979 -4996 165 -430 165 -502 99 -132 97 -1160 457 -100 261 -296 131 -134 201 -98 99 -100 363 -200 361 -134 363 -198 131 -1496 67 -264 329 -360 327 -132 163 -264 985 -68 565 -66 429 -66 263 -130 229 -134 8225 -200 101 -136 101 -368 265 -330 65 -598 195 -304 169 -268 101 -696 99 -1484 67 -1952 99 -1386 165 -66 233 -134 101 -164 65 -364 65 -298 2491 -132 367 -100 1295 -66 6535 -5946 65 -1186 97 -298 623 -426 99 -66 99 -66 559 -134 133 -168 331 -200 133 -132 133 -462 459 -1088 65 -264 131 -164 65 -98 493 -98 97 -198 723 -134 1283 -64 819 -66 459 -66 621 -14282 65 -398 97 -396 97 -366 131 -162 99 -602 165 -66 369 -134 363 -100 561 -134 1261 -100 99 -134 831 -132 8053 -3428 65 -2474 65 -1484 265 -196 131 -98 197 -230 225 -196 97 -232 65 -234 795 -232 397 -234 335 -100 1425 -66 329 -68 527 -134 595 -100 165 -68 165 -200 329 -66 233 -68 889 -64 6761 -1518 97 -5252 133 -1660 65 -528 131 -292 97 -66 165 -66 293 -98 329 -164 97 -130 97 -260 627 -68 1533 -132 867 -132 5585 -6044 97 -1858 99 -132 65 -264 229 -198 97 -924 131 -2698 65 -362 527 -66 263 -100 963 -164 231 -132 295 -166 1093 -66 167 -100 635 -100 165 -68 133 -168 495 -100 299 -100 4855 -6142 65 -1458 97 -134 135 -66 267 -598 131 -168 201 -266 165 -66 133 -232 593 -68 131 -200 97 -198 553 -200 597 -66 1261 -132 2119 -66 199 -168 199 -230 1975 -4282 65 -8382 63 -3286 65 -460 65 -164 97 -100 97 -198 293 -198 63 -326 361 -164 297 -198 1985 -166 297 -100 431 -200 233 -66 65 -68 329 -168 8161 -5090 99 -64 97 -398 67 -2510 263 -1116 65 -230 161 -66 689 -130 129 -132 131 -426 3359 -98 3431 -8782 97 -930 67 -920 97 -786 565 -198 163 -66 461 -198 3347 -164 231 -11298 419 -750 715 -378 721 -360 317 -774 333 -774 305 -782 735 -360 745 -336 315 -768 313 -772 747 -360 741 -356 735 -362 317 -752 347 -752 345 -750 739 -328 347 -756 345 -754 345 -718 339 -772 743 -338 349 -734 349 -754 345 -756 749 -334 737 -354 753 -326 775 -336 347 -736 769 -344 733 -554 507 -4680 351 -722 761 -354 757 -352 317 -760 325 -740 351 -742 757 -354 739 -362 319 -756 347 -756 741 -326 779 -334 745 -340 355 -726 353 -758 319 -758 767 -318 353 -758 311 -754 345 -758 325 -740 777 -322 363 -722\nRAW_Data: 337 -756 337 -756 767 -322 775 -332 747 -338 777 -310 355 -760 739 -350 735 -550 547 -4640 353 -750 751 -320 767 -328 353 -758 347 -724 339 -758 767 -322 777 -332 351 -734 341 -734 773 -324 777 -322 773 -332 355 -732 355 -730 355 -728 753 -338 357 -728 355 -724 355 -758 355 -722 765 -320 351 -754 313 -768 319 -766 753 -352 769 -330 747 -336 779 -310 357 -726 779 -332 747 -540 537 -4670 323 -740 779 -320 761 -328 351 -758 347 -724 375 -724 769 -322 775 -332 351 -734 343 -732 771 -326 777 -322 775 -330 357 -722 347 -726 377 -734 745 -338 353 -732 343 -734 349 -756 325 -744 795 -320 341 -726 377 -724 339 -756 765 -322 777 -332 747 -338 777 -310 343 -764 771 -296 769 -548 521 -4660 329 -768 777 -316 759 -326 351 -758 347 -740 347 -726 771 -324 779 -334 355 -722 349 -724 775 -326 777 -334 747 -340 353 -732 343 -736 351 -756 751 -322 367 -736 351 -732 343 -736 353 -754 751 -322 367 -744 347 -724 339 -746 771 -324 777 -334 745 -340 769 -320 353 -752 769 -314 787 -518 545 -4642 351 -722 789 -310 765 -318 355 -754 337 -758 335 -746 769 -324 775 -334 353 -724 349 -724 773 -326 779 -336 755 -336 353 -732 343 -734 353 -752 753 -320 367 -722 343 -758 337 -746 337 -758 765 -322 345 -756 347 -724 339 -756 767 -324 775 -334 745 -338 777 -312 343 -764 769 -296 769 -548 523 -4650 347 -744 779 -320 763 -330 351 -758 347 -724 341 -758 767 -324 777 -330 351 -734 341 -736 777 -324 775 -320 773 -332 355 -730 357 -722 347 -726 773 -328 349 -756 347 -722 339 -746 375 -734 747 -338 357 -728 353 -728 353 -758 765 -322 757 -352 753 -314 785 -316 345 -752 765 -322 775 -530 535 -4634 359 -736 781 -320 759 -326 351 -756 347 -726 339 -758 769 -324 775 -334 351 -734 341 -734 777 -326 775 -320 773 -298 389 -730 355 -724 347 -722 775 -326 349 -758 345 -724 341 -744 375 -712 773 -324 349 -758 347 -724 339 -756 769 -322 775 -332 745 -340 769 -320 351 -752 771 -314 753 -554 513 -4672 343 -732 771 -318 783 -324 349 -756 347 -724 339 -746 771 -326 775 -336 351 -734 341 -734 777 -326 773 -324 775 -330 355 -724 347 -724 377 -722 767 -324 347 -734 353 -760 353 -722 351 -722 791 -322 351 -720 349 -740 351 -742 757 -352 773 -296 781 -334 755 -338 355 -728 777 -312 777 -544 513 -4674 325 -742 779 -320 759 -328 353 -756 347 -724 341 -758 767 -324 777 -332 357 -730 355 -730 747 -340 781 -310 757 -340 353 -732 343 -738\nRAW_Data: 351 -754 753 -320 369 -724 341 -770 351 -734 341 -732 771 -324 345 -752 333 -748 337 -746 769 -324 777 -332 747 -338 755 -340 353 -732 765 -354 761 -524 515 -4676 319 -764 755 -322 767 -330 351 -758 347 -724 341 -746 773 -324 779 -332 353 -734 343 -732 769 -318 783 -324 779 -332 351 -734 341 -734 347 -754 753 -320 365 -744 347 -724 339 -748 337 -746 773 -324 349 -758 347 -724 339 -744 771 -326 777 -332 745 -340 769 -320 343 -766 741 -322 783 -554 529 -4624 349 -744 779 -320 761 -328 353 -758 347 -724 341 -756 769 -324 775 -334 355 -722 349 -756 741 -326 779 -334 747 -338 353 -732 343 -734 353 -756 751 -322 369 -724 341 -758 337 -746 337 -748 769 -324 349 -732 341 -768 325 -742 781 -322 759 -328 783 -334 747 -338 357 -728 777 -346 755 -510 543 -4664 323 -778 747 -320 763 -330 351 -758 347 -724 339 -746 773 -324 779 -336 351 -734 341 -734 771 -324 777 -324 775 -332 355 -724 347 -724 377 -724 769 -324 347 -756 345 -734 351 -732 343 -732 773 -316 353 -754 337 -746 337 -756 767 -324 777 -334 745 -340 755 -338 353 -732 767 -342 767 -512 549 -4654 345 -754 769 -296 803 -320 341 -724 379 -724 337 -746 769 -326 777 -332 353 -734 341 -732 775 -324 779 -322 773 -332 351 -734 341 -736 323 -780 753 -320 367 -722 375 -722 337 -746 339 -768 747 -336 353 -732 343 -732 351 -754 753 -320 801 -330 745 -338 777 -310 341 -764 767 -318 775 -516 525 -4682 315 -780 749 -320 767 -328 351 -732 353 -758 347 -740 777 -330 749 -336 357 -730 355 -726 779 -310 791 -310 777 -312 357 -726 353 -756 353 -720 765 -354 333 -736 351 -738 359 -716 363 -754 765 -322 343 -724 379 -732 351 -732 767 -314 799 -296 767 -322 801 -296 383 -734 767 -316 763 -522 539 -4680 341 -734 775 -326 773 -322 345 -722 377 -726 337 -756 769 -322 775 -332 351 -736 353 -718 767 -344 765 -320 761 -326 349 -758 347 -740 347 -726 771 -326 347 -758 345 -724 339 -746 375 -734 747 -338 353 -732 341 -734 349 -758 751 -322 767 -330 779 -336 779 -308 357 -726 779 -334 747 -538 539 -4662 317 -780 749 -320 765 -328 353 -732 389 -722 347 -724 775 -326 777 -334 355 -730 355 -724 779 -330 747 -338 777 -310 343 -732 349 -746 353 -740 759 -320 373 -736 349 -734 343 -734 349 -758 755 -320 371 -734 349 -732 343 -760 773 -296 769 -322 801 -328 747 -336 357 -724 781 -332 747 -536 539 -4662 323 -744 781 -318 763 -328 353 -758 347 -726 339 -756 767 -324\nRAW_Data: 777 -334 349 -734 343 -734 777 -326 773 -320 773 -330 355 -724 347 -724 379 -722 769 -324 347 -756 345 -734 351 -732 343 -734 777 -324 347 -752 333 -746 337 -746 769 -326 777 -334 755 -336 779 -308 343 -732 783 -346 755 -514 543 -4646 349 -754 751 -350 753 -346 331 -758 323 -744 361 -754 763 -320 775 -296 389 -732 355 -722 777 -330 747 -336 755 -338 353 -732 351 -720 383 -716 771 -346 329 -756 319 -742 363 -718 365 -752 765 -322 343 -756 347 -732 351 -732 769 -310 799 -316 755 -326 777 -334 353 -732 767 -316 761 -552 545 -4640 345 -732 771 -324 781 -324 347 -758 345 -722 341 -756 767 -326 775 -334 351 -734 341 -736 775 -326 773 -322 771 -332 355 -732 355 -724 349 -724 775 -326 349 -756 345 -724 339 -746 375 -736 745 -338 357 -728 353 -726 353 -758 765 -322 755 -352 753 -348 749 -318 347 -752 765 -322 775 -530 537 -4632 359 -738 779 -320 759 -328 353 -756 347 -726 339 -758 767 -322 777 -334 351 -734 341 -734 771 -318 779 -324 777 -332 357 -730 355 -730 357 -724 777 -332 349 -734 341 -734 323 -782 321 -742 793 -290 373 -722 379 -734 355 -730 747 -340 777 -310 777 -350 739 -348 319 -760 769 -322 767 -520 553 -4646 323 -778 749 -320 767 -328 351 -758 347 -726 339 -756 769 -324 775 -332 353 -734 341 -734 773 -326 775 -322 775 -300 353 -764 309 -766 347 -752 749 -322 367 -722 341 -746 375 -736 351 -732 767 -318 341 -742 353 -738 327 -778 761 -322 773 -332 747 -338 777 -310 355 -728 779 -348 739 -550 511 -4674 323 -746 779 -320 763 -330 351 -758 347 -740 347 -742 743 -330 781 -334 357 -730 357 -728 751 -340 755 -340 769 -318 343 -766 317 -760 325 -778 759 -322 343 -734 351 -732 345 -766 317 -760 755 -322 369 -736 351 -732 353 -756 757 -322 755 -352 753 -346 755 -326 343 -750 767 -322 771 -530 537 -4664 325 -740 777 -320 761 -328 351 -758 347 -726 341 -756 767 -324 777 -332 351 -734 341 -736 775 -326 775 -320 773 -330 351 -734 341 -736 323 -780 749 -320 367 -724 375 -722 337 -744 339 -746 771 -326 347 -758 345 -722 373 -726 767 -324 775 -332 753 -338 745 -340 353 -732 765 -352 753 -526 553 -4646 355 -730 753 -340 779 -312 357 -726 353 -726 353 -758 765 -322 765 -356 317 -758 345 -722 781 -346 755 -326 771 -320 345 -724 377 -726 337 -746 769 -326 347 -732 355 -758 355 -722 351 -754 757 -310 343 -760 325 -746 363 -752 763 -322 771 -332 753 -336 747 -338 353 -732 767 -344 763 -514\nRAW_Data: 541 -4674 313 -768 779 -320 755 -324 349 -734 353 -758 347 -722 773 -326 777 -334 351 -734 341 -734 773 -324 777 -320 775 -332 355 -724 347 -724 379 -732 747 -340 353 -732 343 -732 349 -760 323 -746 795 -290 373 -724 377 -724 337 -746 769 -324 777 -332 747 -338 769 -320 341 -766 743 -322 783 -522 561 -4630 359 -736 777 -320 757 -328 351 -758 347 -724 341 -758 767 -324 779 -300 383 -734 341 -734 779 -296 803 -320 773 -298 387 -724 347 -740 347 -726 775 -322 349 -756 345 -742 347 -724 341 -756 769 -324 345 -724 377 -736 351 -732 765 -312 797 -320 753 -326 777 -334 353 -732 765 -316 761 -536 541 -4648 345 -766 775 -290 785 -324 349 -758 347 -726 339 -746 771 -324 779 -332 353 -730 343 -734 777 -324 777 -322 775 -332 355 -730 357 -724 347 -724 773 -328 349 -758 347 -734 351 -732 343 -732 777 -324 347 -754 335 -758 333 -748 765 -322 775 -332 745 -340 757 -340 355 -728 779 -312 777 -548 511 -4672 323 -742 781 -320 761 -328 387 -698 387 -698 389 -722 781 -296 781 -336 357 -726 349 -732 779 -312 767 -356 759 -322 351 -722 359 -736 351 -740 755 -320 373 -726 375 -726 337 -754 333 -748 767 -324 347 -724 379 -724 339 -758 765 -322 779 -332 745 -340 777 -310 357 -728 775 -348 757 -512 545 -4640 359 -738 779 -320 759 -328 353 -756 347 -726 375 -726 765 -324 777 -300 389 -722 347 -726 775 -324 779 -332 747 -338 353 -730 345 -734 351 -754 753 -322 369 -734 351 -732 343 -728 349 -744 781 -320 365 -726 339 -758 335 -748 769 -324 777 -332 745 -340 779 -312 357 -730 777 -310 795 -510 541 -4666 315 -746 783 -320 763 -326 355 -758 347 -726 339 -748 769 -326 777 -334 353 -730 343 -732 773 -324 779 -322 777 -298 389 -724 347 -726 375 -724 767 -324 347 -724 379 -724 339 -746 339 -760 765 -324 345 -724 377 -726 339 -758 765 -322 777 -332 747 -338 779 -310 341 -734 771 -346 757 -546 543 -4620 383 -716 769 -346 757 -324 345 -752 333 -750 335 -758 765 -320 775 -332 353 -724 349 -756 741 -328 777 -334 753 -338 351 -732 343 -732 349 -756 751 -322 369 -724 341 -746 375 -736 349 -734 769 -322 351 -720 345 -734 349 -762 755 -322 767 -328 781 -334 747 -342 357 -728 777 -346 741 -548 511 -4670 323 -744 779 -320 763 -330 351 -758 349 -724 339 -758 767 -324 775 -330 353 -732 341 -736 769 -324 779 -320 777 -330 357 -724 347 -740 347 -724 775 -324 347 -756 345 -724 341 -744 341 -770 743 -338 353 -732 343 -734\nRAW_Data: 349 -758 753 -320 767 -360 747 -336 777 -308 357 -726 789 -312 777 -79086 67 -500 65 -496 63 -458 131 -856 133 -438 131 -202 99 -398 263 -328 195 -198 363 -100 395 -100 961 -66 393 -132 365 -102 199 -66 5167 -4206 99 -640 99 -726 65 -1814 293 -98 163 -98 97 -164 327 -64 259 -132 231 -428 97 -232 197 -134 65 -398 197 -1124 99 -462 465 -68 397 -132 303 -66 665 -66 3199 -66 1825 -5202 65 -8960 231 -460 131 -396 97 -166 99 -132 133 -330 99 -328 425 -166 133 -100 199 -68 131 -168 133 -66 3381 -66 13735 -270 199 -134 99 -166 133 -302 1029 -100 165 -200 201 -198 301 -132 1467 -66 501 -68 367 -168 1925 -66 495 -98 299 -66 455 -262 5365 -6980 65 -66 129 -462 65 -130 97 -98 327 -394 65 -98 359 -232 97 -330 295 -430 365 -2796 263 -134 199 -198 291 -164 3701 -66 9467 -9052 99 -802 329 -200 197 -64 129 -294 131 -300 891 -266 133 -166 329 -102 99 -132 2173 -64 161 -98 6545 -8052 129 -562 429 -100 563 -168 335 -134 133 -98 133 -232 363 -66 299 -66 1129 -164 397 -464 561 -68 795 -100 1687 -13310 133 -1490 101 -170 231 -66 199 -66 197 -134 65 -334 365 -134 297 -66 959 -100 293 -198 795 -362 231 -100 365 -166 365 -100 4957 -7546 231 -268 397 -232 131 -398 231 -730 65 -434 199 -1492 65 -728 65 -528 97 -164 229 -66 131 -64 197 -132 391 -164 2959 -66 65 -12414 65 -1126 65 -534 67 -400 231 -366 99 -134 65 -166 67 -166 167 -166 167 -298 335 -232 299 -66 1697 -66 233 -134 67 -166 529 -100 67 -66 7763 -728 99 -932 99 -1296 197 -100 99 -630 65 -794 165 -634 165 -130 97 -198 199 -132 133 -198 99 -262 2175 -66 97 -132 1645 -164 563 -12852 131 -1032 67 -1730 99 -166 199 -236 99 -300 263 -196 293 -198 357 -66 1263 -100 1363 -98 725 -134 231 -66 627 -66 5743 -4102 263 -230 131 -262 131 -66 67 -198 99 -498 165 -364 131 -1000 263 -100 101 -464 197 -198 399 -98 1029 -166 133 -198 99 -300 131 -100 199 -432 99 -230 1829 -66 429 -134 131 -66 131 -230 133 -394 395 -196 397 -3828 63 -960 65 -458 197 -198 129 -1480 97 -1948 97 -428 195 -66 229 -426 197 -426 1185 -64 1181 -132 633 -166 363 -170 167 -66 329 -232 4851 -4466 67 -930 65 -434 165 -268 65 -166 133 -234 133 -330 261 -132 199 -68 99 -134 99 -68 65 -234 99 -2214 63 -132 361 -164 297 -98 227 -456 163 -130 127 -228 397 -13124 131 -3210 365 -722 163 -360 97 -362 427 -66 395 -66 163 -132 6621 -9772\nRAW_Data: 231 -332 265 -132 665 -66 531 -232 693 -66 363 -66 1253 -132 621 -428 599 -98 7479 -6028 99 -296 97 -654 99 -724 97 -328 97 -98 435 -166 295 -264 129 -132 589 -66 627 -66 397 -98 199 -66 199 -1528 297 -6504 63 -900 65 -1130 133 -166 663 -398 395 -298 131 -100 599 -98 67 -66 695 -166 403 -164 1807 -200 4679 -3774 131 -130 99 -98 457 -2774 231 -396 525 -262 197 -294 97 -68 163 -134 331 -298 365 -166 99 -634 99 -362 165 -1196 163 -166 231 -98 267 -132 197 -300 2521 -100 895 -100 401 -134 503 -12428 163 -362 231 -396 165 -166 199 -66 99 -232 167 -134 197 -100 99 -330 365 -168 1927 -334 231 -198 495 -66 8407 -5784 229 -1188 163 -492 65 -336 99 -602 99 -100 365 -200 431 -334 331 -100 1791 -134 397 -132 65 -166 359 -66 297 -100 465 -68 1097 -4396 67 -564 431 -298 99 -132 429 -198 1053 -66 327 -98 723 -12552 65 -1232 199 -864 131 -1890 99 -132 133 -134 99 -66 233 -100 165 -628 65 -132 2857 -66 295 -166 631 -100 197 -100 4333 -1082 131 -1476 359 -366 97 -166 99 -100 231 -166 861 -100 529 -98 863 -200 99 -102 99 -132 493 -132 325 -100 8365 -2990 65 -302 133 -964 97 -1100 65 -266 867 -166 99 -100 131 -134 565 -166 695 -100 901 -100 497 -66 401 -12558 63 -830 99 -100 131 -694 99 -498 129 -200 99 -402 99 -266 231 -66 199 -134 65 -200 99 -132 133 -68 167 -166 67 -168 599 -202 1633 -66 723 -100 1547 -64 7133 -5950 97 -522 97 -428 97 -554 65 -2048 133 -132 363 -66 297 -234 2847 -164 791 -132 301 -3286 131 -6472 99 -364 99 -1096 199 -590 65 -362 131 -66 133 -134 299 -98 369 -66 2063 -166 99 -132 599 -100 7507 -8234 199 -232 97 -168 201 -530 229 -132 163 -66 427 -68 165 -100 1981 -66 463 -100 65 -166 1017 -132 6219 -6002 131 -64 165 -428 199 -432 131 -530 133 -200 67 -11500 65 -6344 65 -1958 165 -100 97 -664 65 -334 99 -730 331 -236 367 -168 329 -200 395 -66 131 -98 527 -68 99 -132 1493 -66 197 -14856 199 -1364 197 -132 165 -366 163 -234 65 -98 131 -428 65 -98 65 -232 2273 -164 553 -392 6131 -6662 233 -832 231 -432 63 -1328 263 -300 131 -866 197 -926 65 -166 65 -232 227 -98 263 -198 131 -164 65 -166 791 -100 525 -98 361 -198 365 -132 859 -66 329 -228 529 -7030 131 -620 97 -164 163 -132 131 -66 195 -100 163 -394 229 -262 131 -66 131 -198 527 -100 133 -400 133 -166 99 -1232 65 -134 197 -66 131 -232 431 -596 131 -166 2673 -134 295 -166\nRAW_Data: 5357 -11284 99 -1290 65 -134 1501 -200 263 -100 325 -132 399 -166 1997 -15232 131 -1320 99 -168 65 -466 65 -134 65 -100 165 -98 97 -196 425 -134 331 -132 1491 -168 395 -134 299 -100 1519 -12916 99 -998 197 -794 131 -1654 165 -100 97 -362 163 -66 163 -230 231 -434 497 -100 1499 -100 1495 -166 65 -298 229 -5848 99 -134 99 -8920 133 -1126 131 -234 231 -134 265 -66 131 -134 365 -166 829 -100 1793 -134 395 -12288 97 -2094 65 -430 65 -268 65 -400 65 -232 335 -166 199 -132 231 -100 2191 -100 929 -15146 299 -230 129 -788 591 -100 231 -100 165 -66 431 -100 65 -100 65 -166 133 -264 199 -134 595 -100 397 -132 927 -68 435 -166 267 -232 65 -166 99 -68 7469 -3932 99 -360 231 -2184 131 -166 293 -200 229 -262 99 -1020 65 -656 261 -66 229 -132 163 -66 97 -760 1663 -66 233 -166 397 -100 329 -132 955 -166 491 -14812 167 -1052 163 -528 131 -132 227 -66 659 -66 367 -100 2749 -68 1595 -66 10545 -1162 67 -3130 131 -1224 131 -66 99 -230 199 -166 529 -662 299 -18024 131 -298 201 -366 131 -100 65 -166 333 -166 65 -132 297 -164 395 -392 131 -166 9295 -9924 133 -1430 133 -334 397 -134 131 -68 65 -100 331 -100 197 -200 691 -166 329 -13826 131 -1978 131 -66 531 -66 265 -268 165 -136 165 -100 263 -166 467 -66 365 -102 467 -166 631 -136 67 -696 133 -396 97 -758 165 -3180 131 -362 163 -132 63 -164 461 -164 229 -164 197 -296 2399 -194 293 -13158 131 -1494 231 -524 327 -198 97 -394 297 -68 2025 -66 227 -296 365 -13564 65 -1380 131 -954 67 -658 429 -166 195 -66 65 -98 163 -130 823 -100 99 -64 757 -66 131 -98 99 -64 165 -132 11025 -21882 65 -1816 65 -822 229 -326 163 -100 723 -264 427 -66 329 -98 985 -68 1827 -66 133 -168 401 -98 301 -132 399 -100 1725 -4022 65 -1726 67 -732 99 -1070 99 -198 65 -494 197 -298 165 -100 131 -66 823 -134 299 -166 363 -102 363 -66 823 -132 7641 -4542 65 -834 65 -1220 67 -1164 133 -562 65 -698 67 -464 197 -736 263 -134 231 -332 99 -302 131 -264 361 -164 1245 -66 65 -66 395 -130 161 -98 65 -12312 65 -1324 65 -572 67 -166 101 -202 65 -702 395 -198 401 -100 333 -68 331 -66 133 -132 1281 -68 367 -132 433 -132 99 -66 8729 -2158 65 -3984 65 -1562 265 -166 161 -194 165 -132 497 -300 1055 -166 425 -100 599 -134 991 -66 6793 -5222 99 -1602 65 -328 129 -132 291 -330 99 -228 97 -196 97 -98 297 -624 297 -1186 165 -502 165 -498 97 -1086 525 -230 261 -292 199 -298\nRAW_Data: 2443 -100 8837 -7844 99 -300 101 -3474 65 -426 131 -786 131 -66 261 -66 131 -132 623 -198 431 -98 3805 -64 163 -132 4689 -2290 65 -5964 129 -996 197 -2760 165 -230 395 -300 263 -234 301 -166 2423 -68 367 -200 197 -134 429 -66 131 -100 231 -100 1455 -100 7041 -260 63 -2606 163 -262 97 -164 261 -166 161 -130 493 -200 799 -132 525 -132 129 -1222 97 -918 919 -68 65 -264 165 -168 3119 -64 97 -98 265 -66 293 -66 9961 -3892 97 -168 461 -362 165 -438 267 -100 531 -134 1659 -168 167 -66 101 -762 99 -330 231 -8514 65 -4398 99 -1312 99 -130 163 -294 163 -822 197 -270 429 -100 461 -66 329 -130 295 -330 829 -66 1461 -66 629 -66 233 -168 593 -66 13101 -102 133 -232 1405 -198 327 -296 331 -100 197 -200 365 -66 369 -98 1419 -230 527 -66 529 -302 5013 -3942 99 -1150 165 -298 65 -296 197 -822 133 -528 97 -230 163 -762 99 -666 65 -432 265 -332 201 -66 431 -198 267 -132 2459 -132 403 -100 1359 -68 7933 -3998 67 -4454 131 -198 97 -132 131 -198 295 -162 329 -298 1189 -132 327 -366 435 -66 503 -66 983 -132 4845 -5250 131 -132 133 -98 165 -134 101 -300 233 -332 231 -66 99 -132 99 -132 335 -198 165 -366 131 -1060 197 -402 99 -132 197 -168 67 -266 629 -330 65 -134 131 -336 721 -100 1501 -132 731 -66 1127 -132 5451 -4352 131 -854 163 -296 165 -432 99 -396 493 -132 131 -294 163 -1052 131 -14776 65 -5234 65 -164 129 -852 65 -98 131 -66 165 -164 97 -330 429 -66 165 -432 329 -98 491 -100 857 -198 889 -164 329 -66 295 -806 65 -722 99 -3564 199 -864 397 -400 231 -98 367 -100 797 -164 395 -98 393 -166 6901 -1552 65 -530 165 -66 99 -1186 65 -394 131 -198 99 -366 165 -198 99 -404 99 -2092 261 -264 331 -132 231 -360 297 -234 331 -64 2139 -100 399 -13226 99 -2394 65 -502 63 -394 163 -260 65 -66 131 -198 229 -132 99 -132 297 -296 263 -196 987 -132 265 -198 163 -12344 99 -696 65 -1292 65 -198 99 -894 163 -68 431 -102 65 -102 231 -166 199 -64 263 -164 359 -66 2025 -130 327 -98 11033 -788 99 -2174 65 -722 97 -100 163 -66 263 -362 789 -100 2493 -100 299 -200 7351 -4204 65 -1286 67 -1766 67 -566 99 -266 65 -200 67 -930 97 -134 429 -100 99 -132 65 -198 229 -262 163 -460 789 -98 361 -130 259 -66 657 -134 491 -166 197 -164 163 -166 5757 -6452 65 -864 65 -368 395 -230 165 -468 99 -796 133 -166 99 -134 101 -828 131 -600 163 -560 197 -328 263 -200 295 -66 97 -294 97 -198\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/gangqi.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: GangQi\nBit: 34\nKey: 00 00 00 02 9D DB 77 38\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/gangqi_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1223 -572304 1675 -132 1183 -536 465 -1214 1203 -502 489 -1210 509 -1202 1171 -518 1175 -522 1189 -508 489 -1176 1229 -504 1177 -520 1205 -482 479 -1232 1203 -478 1233 -486 481 -1216 1197 -480 1221 -486 505 -1182 1241 -486 1175 -482 1245 -484 505 -1218 1185 -498 1211 -490 1185 -482 545 -1174 511 -1206 1177 -516 1189 -516 1165 -508 521 -1184 503 -1232 477 -2210 1171 -510 473 -1232 1183 -514 517 -1174 513 -1194 1209 -478 1219 -486 1191 -492 513 -1204 1199 -532 1159 -500 1237 -486 481 -1206 1209 -480 1203 -480 535 -1198 1189 -496 1187 -520 483 -1230 1175 -478 1219 -506 1199 -510 505 -1180 1231 -496 1179 -486 1211 -482 517 -1214 501 -1182 1199 -508 1179 -502 1205 -516 477 -1240 473 -1206 477 -2200 1231 -478 485 -1222 1189 -520 463 -1202 515 -1208 1199 -494 1207 -486 1209 -516 483 -1182 1217 -488 1197 -534 1159 -520 481 -1234 1179 -478 1219 -486 505 -1216 1183 -506 1201 -510 491 -1174 1229 -506 1177 -542 1185 -496 483 -1208 1195 -502 1213 -490 1169 -528 493 -1208 475 -1242 1171 -482 1235 -484 1181 -542 499 -1180 473 -1242 479 -2204 1201 -490 475 -1248 1177 -488 515 -1206 489 -1236 1153 -520 1215 -480 1175 -514 531 -1192 1167 -538 1195 -484 1215 -482 515 -1176 1207 -512 1165 -548 477 -1184 1233 -478 1187 -520 519 -1174 1185 -536 1191 -474 1231 -478 493 -1212 1203 -492 1177 -544 1179 -480 487 -1250 497 -1176 1197 -496 1211 -486 1211 -480 517 -1214 501 -1178 507 -2208 1209 -486 507 -1182 1197 -526 483 -1204 507 -1198 1187 -508 1183 -506 1217 -490 475 -1244 1175 -486 1237 -484 1181 -492 547 -1176 1167 -528 1185 -520 485 -1232 1175 -478 1223 -486 519 -1216 1169 -520 1203 -482 1197 -480 493 -1242 1167 -504 1217 -490 1201 -518 489 -1176 519 -1210 1175 -514 1191 -486 1213 -518 489 -1176 505 -1214 515 -2198 1167 -506 541 -1174 1175 -520 505 -1184 505 -1216 1183 -504 1211 -490 1199 -530 495 -1178 1199 -494 1205 -484 1207 -514 479 -1200 1203 -516 1175 -512 507 -1184 1227 -498 1181 -486 515 -1204 1185 -532 1159 -504 1231 -478 493 -1210 1215 -486 1171 -544 1173 -512 475 -1216 513 -1194 1185 -510 1181 -520 1185 -514 483 -1216 513 -1158 531 -2174 1243 -486 499 -1176 1217 -490 479 -1236 505 -1184 1203 -522 1181 -484 1213 -502 485 -1206 1197 -500 1213 -490 1197 -498 515 -1180 1197 -532 1161 -502 511 -1202 1201 -482 1225 -476 485 -1248 1191 -486 1211 -482 1207 -484 537 -1196 1179 -516 1179 -506 1199 -508 489 -1172 545 -1178 1201 -520 1179 -488 1215 -494 515 -1170 513 -1204 481 -2238 1157 -520 519 -1176 1183 -540 499 -1178 473 -1236 1179 -520 1209 -480 1175 -552 475 -1188 1225 -502 1177 -488 1207 -510 473 -1232 1207 -480 1203 -520 479 -1184\nRAW_Data: 1227 -480 1189 -554 487 -1174 1217 -502 1181 -486 1211 -514 481 -1216 1207 -484 1221 -478 1185 -520 487 -1196 483 -1218 1215 -490 1229 -478 1183 -520 483 -1228 483 -1188 531 -2150 1239 -480 481 -1246 1181 -482 489 -1218 495 -1206 1197 -506 1183 -520 1205 -480 479 -1232 1181 -514 1209 -486 1199 -526 491 -1184 1207 -520 1183 -484 485 -1208 1209 -506 1181 -518 479 -1242 1169 -482 1235 -474 1201 -480 541 -1174 1205 -512 1207 -474 1215 -516 479 -1174 547 -1176 1185 -536 1193 -462 1249 -486 481 -1206 513 -1176 485 -2240 1173 -518 517 -1158 1223 -506 487 -1174 545 -1172 1207 -514 1175 -484 1223 -510 489 -1178 1219 -516 1173 -486 1235 -484 489 -1218 1185 -520 1217 -480 481 -1214 1205 -484 1183 -540 499 -1182 1201 -494 1207 -476 1241 -478 475 -1244 1171 -512 1203 -520 1173 -490 521 -1184 479 -1246 1179 -482 1243 -486 1195 -484 515 -1210 485 -1214 499 -2204 1175 -482 519 -1224 1171 -490 503 -1210 481 -1210 1213 -482 1205 -518 1175 -486 501 -1246 1179 -486 1211 -480 1213 -512 503 -1182 1231 -498 1179 -486 513 -1202 1183 -534 1159 -498 511 -1204 1199 -482 1227 -480 1199 -514 505 -1182 1229 -478 1185 -520 1209 -482 509 -1202 513 -1174 1215 -516 1153 -520 1211 -480 511 -1204 485 -1208 489 -2210 1175 -548 475 -1186 1217 -492 515 -1174 513 -1210 1177 -510 1201 -490 1233 -496 483 -1174 1215 -516 1173 -514 1209 -484 491 -1232 1159 -520 1209 -480 513 -1210 1183 -482 1245 -486 501 -1178 1231 -496 1177 -508 1205 -482 481 -1244 1171 -510 1211 -486 1199 -494 509 -1210 479 -1212 1181 -514 1207 -486 1201 -492 507 -1216 479 -1210 519 -2198 1165 -494 513 -1210 1193 -488 511 -1208 473 -1246 1175 -474 1239 -478 1203 -486 539 -1194 1155 -528 1187 -520 1211 -480 481 -1210 1203 -512 1169 -548 477 -1186 1215 -492 1205 -486 481 -1220 1177 -510 1219 -488 1199 -522 487 -1206 1197 -504 1181 -486 1241 -478 475 -1244 485 -1202 1219 -478 1187 -486 1241 -478 475 -1244 485 -1202 461 -2214 1245 -482 499 -1178 1231 -478 491 -1240 471 -1184 1243 -486 1175 -480 1241 -482 503 -1182 1233 -478 1215 -506 1165 -510 489 -1208 1195 -506 1211 -506 473 -1214 1185 -522 1207 -478 509 -1174 1243 -480 1199 -514 1169 -506 507 -1214 1197 -486 1209 -482 1201 -520 481 -1192 519 -1208 1175 -514 1199 -510 1175 -514 507 -1186 503 -1210 479 -2234 1171 -490 503 -1208 1177 -520 511 -1198 489 -1232 1169 -490 1215 -492 1205 -488 513 -1202 1185 -534 1161 -518 1209 -482 513 -1178 1205 -516 1175 -512 507 -1184 1233 -498 1175 -474 551 -1182 1177 -546 1165 -510 1189 -510 487 -1208 1215 -486 1175 -546 1173 -480 509 -1216 479 -1190 1223 -506 1179 -520 1183 -516 479 -1234 481 -1198 491 -2210\nRAW_Data: 1211 -482 501 -1184 1227 -500 483 -1208 505 -1200 1205 -474 1213 -484 1205 -520 481 -1232 1177 -478 1219 -486 1197 -504 519 -1184 1193 -522 1181 -520 481 -1230 1177 -478 1217 -508 473 -1248 1155 -504 1231 -500 1179 -486 481 -1224 1179 -510 1217 -490 1229 -480 493 -1210 477 -1238 1169 -486 1235 -484 1181 -524 491 -1206 477 -1240 477 -2206 1199 -492 511 -1180 1205 -518 481 -1190 499 -1212 1207 -486 1213 -480 1213 -512 503 -1182 1213 -490 1205 -486 1209 -512 475 -1216 1209 -484 1211 -504 487 -1174 1237 -488 1173 -518 511 -1200 1189 -530 1155 -500 1237 -486 477 -1200 1205 -516 1169 -552 1179 -482 493 -1232 477 -1182 1233 -478 1189 -554 1169 -480 509 -1210 485 -1206 527 -2182 1203 -484 517 -1194 1179 -510 489 -1210 519 -1184 1209 -484 1203 -494 1197 -508 489 -1210 1193 -490 1243 -486 1177 -482 519 -1212 1173 -522 1181 -510 491 -1206 1197 -506 1215 -492 473 -1246 1177 -506 1217 -486 1171 -514 507 -1202 1187 -534 1155 -498 1235 -488 483 -1206 513 -1208 1173 -512 1203 -490 1201 -494 515 -1174 519 -1214 481 -2198 1187 -504 513 -1208 1175 -512 505 -1182 503 -1200 1201 -490 1229 -498 1173 -520 487 -1208 1221 -478 1185 -520 1181 -512 475 -1246 1175 -478 1235 -486 505 -1182 1229 -498 1179 -520 479 -1202 1211 -504 1179 -506 1203 -490 481 -1240 1179 -516 1209 -486 1177 -488 553 -1182 479 -1242 1183 -480 1211 -520 1173 -490 473 -1244 479 -1210 513 -2166 1239 -486 479 -1244 1167 -490 481 -1242 473 -1210 1209 -484 1205 -520 1195 -486 477 -1238 1167 -514 1207 -520 1177 -518 481 -1182 1235 -484 1181 -492 547 -1178 1197 -530 1153 -520 487 -1232 1177 -478 1217 -508 1165 -542 501 -1178 1227 -498 1177 -486 1211 -510 475 -1216 507 -1200 1183 -530 1191 -486 1211 -482 513 -1176 511 -1202 489 -2210 1173 -550 479 -1188 1225 -502 485 -1172 543 -1180 1171 -548 1171 -486 1223 -506 487 -1208 1217 -484 1171 -554 1179 -484 489 -1232 1159 -520 1211 -482 513 -1176 1209 -512 1207 -520 475 -1180 1231 -478 1183 -520 1209 -480 507 -1200 1205 -480 1237 -486 1171 -486 539 -1182 507 -1218 1179 -490 1209 -518 1177 -480 541 -1172 515 -1228 481 -2192 1187 -520 483 -1228 1169 -480 495 -1246 461 -1248 1183 -476 1237 -482 1175 -516 497 -1214 1187 -500 1215 -492 1173 -548 483 -1176 1207 -518 1171 -518 509 -1200 1157 -528 1189 -520 483 -1228 1177 -480 1219 -488 1195 -524 489 -1208 1195 -506 1181 -506 1199 -508 487 -1210 499 -1210 1203 -484 1181 -486 1229 -516 463 -1246 493 -1180 481 -2234 1173 -524 489 -1198 1211 -490 505 -1180 515 -1208 1177 -516 1175 -516 1225 -482 473 -1198 1235 -484 1183 -524 1181 -506 473 -1250 1181 -476 1241 -480 481 -1246 1177 -484 1215 -514\nRAW_Data: 495 -1180 1197 -494 1211 -506 1199 -502 485 -1210 1195 -488 1211 -520 1183 -514 477 -1214 477 -1220 1181 -506 1215 -490 1197 -498 515 -1182 503 -1218 489 -2198 1205 -482 475 -1246 1161 -520 515 -1174 485 -1246 1195 -484 1209 -482 1207 -484 537 -1196 1181 -520 1179 -504 1209 -482 513 -1174 1237 -478 1199 -518 479 -1190 1225 -504 1181 -488 481 -1224 1177 -542 1197 -492 1199 -494 515 -1172 1215 -516 1173 -486 1233 -484 489 -1216 493 -1212 1205 -488 1175 -522 1205 -482 519 -1216 485 -1176 511 -2198 1229 -498 483 -1174 1239 -486 479 -1240 477 -1202 1201 -486 1205 -490 1217 -490 481 -1238 1165 -530 1213 -462 1215 -480 475 -1244 1181 -482 1215 -516 501 -1216 1187 -498 1211 -494 473 -1202 1203 -520 1179 -516 1175 -516 499 -1214 1189 -498 1213 -492 1185 -512 481 -1208 479 -1232 1175 -514 1193 -520 1179 -522 487 -1206 473 -1214 515 -2198 1173 -486 505 -1218 1189 -554 487 -1174 489 -1232 1159 -520 1209 -482 1209 -482 533 -1196 1181 -518 1181 -488 1211 -514 483 -1182 1233 -484 1181 -538 501 -1180 1213 -488 1205 -474 545 -1180 1173 -544 1173 -490 1227 -502 485 -1176 1213 -516 1171 -552 1177 -482 473 -1250 463 -1212 1197 -508 1185 -520 1183 -514 485 -1214 479 -1192 553 -2178 1215 -496 483 -1176 1215 -516 479 -1210 509 -1172 1215 -516 1155 -520 1217 -482 479 -1244 1175 -514 1209 -486 1199 -494 519 -1176 1203 -518 1177 -486 501 -1214 1211 -476 1205 -512 475 -1246 1173 -482 1243 -482 1197 -494 505 -1216 1177 -518 1171 -510 1199 -518 479 -1184 501 -1212 1209 -520 1183 -484 1213 -516 465 -1212 477 -1240 479 -2198 1199 -492 539 -1180 1183 -520 481 -1202 489 -1216 1183 -520 1211 -480 1173 -550 479 -1186 1231 -478 1189 -520 1209 -482 479 -1246 1177 -482 1217 -516 497 -1178 1213 -490 1173 -540 503 -1164 1223 -488 1195 -506 1215 -490 505 -1180 1205 -518 1173 -522 1197 -478 493 -1238 473 -1182 1243 -492 1185 -514 1175 -520 481 -1220 489 -1182 547 -2172 1201 -508 475 -1246 1183 -484 483 -1218 515 -1172 1207 -516 1177 -518 1209 -484 493 -1216 1181 -486 1215 -514 1175 -514 495 -1192 1227 -504 1181 -486 483 -1222 1209 -476 1217 -488 505 -1232 1173 -490 1233 -476 1189 -520 517 -1168 1203 -510 1197 -474 1231 -480 495 -1210 507 -1206 1175 -518 1179 -520 1219 -478 491 -1206 503 -1184 515 -2196 1175 -554 485 -1178 1215 -506 473 -1216 483 -1208 1181 -514 1205 -486 1211 -516 483 -1182 1231 -482 1199 -478 1223 -486 519 -1212 1175 -518 1207 -482 489 -1184 1239 -474 1193 -524 491 -1210 1215 -486 1169 -520 1207 -482 485 -1248 1161 -500 1235 -486 1177 -520 509 -1196 473 -1228 1191 -474 1225 -506 1181 -486 549 -1172 489 -1216 491 -2172 1233 -484 485 -1248\nRAW_Data: 1157 -498 511 -1202 509 -1174 1237 -476 1201 -514 1205 -484 493 -1200 1187 -520 1213 -482 1173 -548 477 -1190 1215 -492 1211 -476 543 -1174 1183 -538 1163 -498 509 -1204 1205 -484 1231 -482 1169 -542 501 -1184 1215 -490 1173 -518 1207 -480 503 -1236 485 -1198 1213 -500 1175 -476 1233 -488 481 -1206 513 -1210 485 -2212 1181 -538 477 -1174 1213 -518 499 -1182 519 -1180 1205 -516 1169 -508 1215 -504 487 -1176 1241 -484 1173 -552 1181 -484 489 -1216 1183 -518 1213 -480 507 -1208 1169 -510 1203 -520 477 -1188 1215 -494 1205 -474 1215 -516 477 -1240 1165 -492 1237 -474 1195 -502 517 -1182 505 -1232 1153 -518 1215 -482 1175 -514 531 -1194 485 -1214 479 -2234 1175 -488 487 -1246 1175 -484 535 -1192 485 -1206 1205 -476 1239 -480 1175 -516 463 -1246 1187 -532 1157 -500 1237 -484 479 -1208 1211 -480 1203 -484 539 -1188 1165 -538 1191 -474 507 -1214 1187 -502 1213 -482 1207 -518 481 -1190 1217 -494 1177 -520 1209 -482 485 -1250 499 -1180 1199 -494 1205 -476 1209 -514 483 -1244 483 -1196 485 -2208 1221 -478 489 -1210 1213 -486 479 -1202 513 -1212 1177 -512 1199 -490 1201 -510 489 -1208 1199 -502 1181 -520 1203 -478 477 -1246 1181 -482 1211 -518 499 -1210 1167 -510 1217 -490 483 -1216 1209 -484 1203 -494 1199 -504 487 -1210 1189 -494 1239 -492 1165 -526 493 -1208 473 -1244 1177 -476 1235 -480 1201 -518 479 -1190 499 -1244 481 -2194 1207 -482 473 -1250 1155 -520 519 -1176 485 -1248 1161 -518 1209 -482 1203 -480 537 -1192 1167 -536 1197 -462 1229 -506 487 -1206 1201 -502 1181 -520 479 -1204 1219 -480 1187 -520 515 -1172 1181 -542 1199 -486 1205 -516 479 -1176 1243 -480 1173 -550 1173 -486 503 -1238 481 -1172 1217 -518 1171 -484 1235 -480 509 -1212 503 -1182 473 -2238 1213 -496 483 -1174 1239 -486 479 -1242 487 -1176 1215 -514 1159 -520 1213 -480 481 -1246 1177 -482 1243 -484 1165 -518 515 -1208 1179 -512 1199 -486 481 -1230 1201 -482 1227 -482 473 -1216 1211 -490 1231 -478 1185 -518 483 -1228 1175 -478 1217 -508 1183 -536 497 -1176 473 -1232 1199 -494 1197 -508 1181 -554 477 -1174 485 -1248 461 -2236 1175 -482 521 -1212 1171 -518 505 -1198 477 -1222 1181 -490 1237 -474 1199 -536 497 -1178 1219 -486 1177 -520 1205 -482 487 -1184 1239 -474 1191 -526 493 -1208 1195 -508 1181 -504 505 -1198 1201 -520 1197 -482 1203 -522 485 -1164 1249 -462 1207 -506 1197 -504 485 -1210 499 -1182 1241 -480 1205 -518 1173 -482 493 -1240 471 -1184 549 -2170 1203 -508 507 -1178 1207 -480 517 -1216 479 -1186 1229 -482 1187 -520 1213 -478 509 -1208 1185 -480 1229 -520 1173 -492 519 -1184 1207 -518 1175 -486 501 -1218 1209 -488 1205 -482 519 -1216 1193 -488\nRAW_Data: 1215 -480 1207 -484 535 -1156 1217 -516 1177 -486 1247 -476 509 -1176 511 -1204 1183 -534 1195 -486 1211 -478 509 -1172 515 -1192 517 -2210 1171 -488 541 -1196 1157 -528 493 -1208 475 -1240 1169 -488 1237 -482 1183 -538 499 -1178 1229 -480 1183 -486 1245 -480 479 -1232 1177 -480 1245 -484 501 -1184 1233 -464 1211 -520 483 -1200 1219 -478 1183 -522 1211 -480 477 -1232 1185 -480 1243 -482 1193 -474 497 -1252 483 -1174 1217 -516 1171 -514 1209 -484 485 -1220 515 -1170 513 -2204 1205 -490 511 -1202 1199 -498 481 -1210 503 -1198 1209 -490 1197 -528 1191 -486 483 -1226 1175 -482 1223 -486 1197 -538 501 -1180 1219 -488 1173 -520 479 -1218 1179 -506 1215 -490 485 -1244 1171 -486 1235 -480 1199 -478 525 -1186 1199 -534 1193 -486 1215 -480 515 -1176 511 -1204 1185 -532 1191 -486 1213 -480 513 -1180 513 -1204 489 -2212 1175 -512 507 -1182 1233 -478 493 -1176 521 -1210 1171 -548 1175 -488 1217 -492 515 -1172 1241 -484 1175 -482 1235 -482 485 -1252 1159 -522 1213 -480 479 -1214 1207 -480 1209 -512 505 -1180 1229 -478 1189 -486 1243 -478 479 -1234 1177 -514 1211 -486 1201 -490 505 -1192 493 -1242 1181 -486 1207 -520 1181 -482 525 -1184 503 -1212 503 -2156 1241 -486 483 -1232 1177 -480 495 -1240 471 -1198 1235 -484 1185 -534 1161 -500 511 -1202 1201 -486 1231 -484 1179 -524 491 -1208 1201 -490 1209 -480 545 -1170 1169 -540 1199 -486 487 -1236 1173 -480 1221 -486 1193 -524 493 -1206 1197 -508 1181 -486 1217 -514 481 -1218 515 -1160 1223 -506 1183 -474 1241 -480 513 -1210 485 -1204 485 -2196 1197 -510 507 -1184 1227 -500 483 -1178 521 -1212 1179 -512 1203 -460 1251 -486 479 -1208 1209 -482 1207 -518 1173 -486 499 -1238 1181 -488 1213 -514 483 -1220 1173 -484 1235 -514 495 -1178 1217 -488 1171 -538 1195 -500 483 -1208 1197 -502 1209 -474 1197 -506 521 -1180 505 -1218 1179 -506 1197 -506 1181 -504 505 -1184 517 -1204 505 -2212 1171 -516 495 -1214 1187 -464 549 -1180 487 -1216 1203 -486 1229 -484 1183 -502 519 -1182 1197 -532 1193 -486 1215 -480 481 -1208 1207 -478 1203 -548 477 -1182 1229 -480 1185 -504 503 -1216 1177 -486 1245 -476 1199 -516 479 -1190 1229 -502 1179 -474 1245 -480 479 -1246 481 -1198 1217 -478 1183 -522 1209 -478 507 -1210 485 -1208 489 -2210 1207 -486 509 -1182 1229 -478 491 -1208 521 -1184 1209 -482 1203 -490 1219 -490 513 -1172 1209 -482 1205 -520 1209 -480 471 -1234 1209 -486 1215 -500 485 -1208 1181 -514 1203 -506 471 -1216 1203 -506 1161 -528 1185 -522 487 -1196 1209 -478 1221 -488 1185 -548 483 -1172 505 -1204 1207 -488 1211 -514 1173 -514 509 -1180 505 -1216 487 -2204 1179 -484 529 -1216 1183 -488 515 -1202\nRAW_Data: 489 -1232 1171 -490 1233 -478 1187 -486 553 -1168 1201 -512 1197 -492 1199 -496 515 -1174 1209 -516 1167 -520 517 -1192 1187 -508 1185 -486 553 -1172 1187 -534 1187 -474 1231 -476 493 -1206 1197 -508 1181 -520 1205 -478 509 -1206 515 -1170 1217 -504 1177 -506 1205 -482 511 -1208 487 -1206 491 -2212 1209 -482 503 -1184 1227 -500 485 -1206 521 -1174 1203 -514 1167 -506 1231 -484 497 -1210 1199 -478 1221 -488 1197 -498 519 -1184 1201 -516 1177 -504 473 -1216 1207 -474 1249 -484 477 -1238 1169 -516 1201 -506 1161 -496 513 -1202 1199 -502 1211 -488 1229 -480 491 -1208 477 -1236 1167 -482 1237 -482 1183 -542 501 -1180 485 -1214 509 -2194 1197 -490 537 -1166 1197 -506 505 -1180 507 -1218 1183 -504 1211 -488 1185 -516 515 -1170 1237 -486 1201 -488 1207 -482 517 -1196 1215 -480 1229 -484 499 -1178 1235 -478 1181 -486 553 -1174 1183 -534 1191 -462 1247 -486 479 -1204 1207 -482 1205 -520 1209 -486 493 -1218 487 -1176 1217 -514 1175 -520 1213 -450 519 -1210 513 -1172 513 -2174 1249 -482 477 -1236 1197 -464 513 -1206 503 -1200 1205 -474 1195 -506 1213 -490 503 -1198 1201 -472 1229 -516 1193 -486 515 -1174 1209 -512 1201 -492 477 -1242 1173 -486 1237 -482 485 -1240 1175 -486 1241 -480 1175 -514 531 -1156 1189 -524 1185 -520 1209 -480 509 -1168 549 -1176 1183 -536 1195 -488 1211 -480 513 -1174 547 -1174 489 -2194 1201 -482 535 -1196 1187 -498 529 -1178 475 -1242 1173 -482 1235 -482 1183 -526 493 -1208 1195 -506 1179 -486 1243 -480 479 -1232 1181 -480 1229 -522 477 -1180 1231 -478 1185 -556 477 -1174 1243 -486 1195 -488 1217 -482 479 -1248 1181 -484 1215 -506 1183 -476 515 -1196 517 -1218 1169 -486 1229 -482 1191 -500 511 -1204 477 -1200 547 -2164 1209 -482 511 -1206 1185 -514 483 -1216 513 -1160 1249 -462 1209 -522 1175 -482 517 -1218 1199 -484 1211 -482 1207 -518 481 -1188 1215 -494 1207 -472 545 -1182 1175 -550 1173 -490 507 -1208 1175 -488\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: GateTX\nBit: 24\nKey: 00 00 00 00 00 02 8F F3\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 711 -646 413 -328 713 -680 381 -648 417 -326 747 -644 1509 -32700 711 -480 597 -454 595 -422 627 -424 659 -388 661 -422 631 -750 319 -394 675 -684 349 -718 339 -690 379 -358 663 -716 345 -716 379 -324 729 -682 379 -654 381 -650 411 -328 709 -322 719 -678 413 -648 381 -358 723 -680 349 -16580 767 -316 699 -350 701 -348 699 -350 701 -348 735 -314 731 -666 425 -304 729 -640 405 -640 403 -638 405 -318 741 -648 413 -648 415 -326 743 -648 417 -618 415 -650 379 -334 703 -344 727 -652 425 -654 385 -348 721 -668 355 -16578 747 -326 717 -324 751 -290 755 -320 723 -320 731 -322 733 -650 453 -292 737 -644 385 -650 417 -650 379 -332 735 -646 415 -648 415 -326 743 -642 387 -648 417 -650 379 -346 725 -314 717 -668 387 -668 423 -312 753 -628 393 -16568 771 -318 703 -322 731 -322 727 -322 727 -322 731 -352 703 -684 379 -352 707 -646 387 -680 385 -648 415 -328 705 -648 415 -648 415 -326 745 -648 383 -650 415 -650 379 -344 725 -348 685 -668 387 -686 383 -350 717 -666 355 -16568 767 -322 735 -322 725 -322 727 -320 723 -322 731 -324 731 -682 409 -320 709 -650 383 -656 405 -638 405 -318 739 -646 413 -640 413 -350 723 -648 409 -648 381 -654 417 -294 753 -322 725 -646 417 -648 419 -324 725 -650 377 -16592 737 -348 691 -350 719 -316 723 -318 725 -346 723 -332 741 -626 441 -304 741 -622 409 -624 431 -622 409 -332 721 -658 409 -644 407 -334 753 -628 405 -644 417 -622 417 -324 727 -318 725 -648 417 -648 419 -324 727 -648 383 -16574 763 -318 699 -350 701 -348 701 -350 701 -350 733 -320 731 -646 411 -348 699 -648 383 -676 387 -636 411 -344 701 -678 379 -672 409 -348 733 -624 385 -678 387 -638 391 -366 699 -346 701 -646 423 -636 421 -342 735 -648 347 -16582 781 -288 753 -320 725 -320 723 -320 729 -322 729 -322 731 -680 409 -320 709 -650 379 -672 383 -672 381 -318 749 -646 419 -650 417 -324 723 -648 419 -650 383 -650 379 -344 727 -346 685 -654 417 -640 417 -348 719 -654 383 -16578 745 -324 709 -324 747 -288 755 -318 725 -322 729 -322 769 -616 455 -292 735 -644 389 -648 419 -616 411 -346 727 -626 429 -622 417 -352 755 -624 381 -654 383 -680 385 -342 705 -336 715 -654 409 -648 419 -332 741 -648 385 -16560 763 -326 699 -336 707 -358 677 -354 705 -356 701 -354 705 -682 409 -320 711 -680 385 -648 383 -648 415 -328 709 -678 381 -648 415 -324 749 -646 385 -682 383 -650 377 -344 727 -346 683 -664 423 -640 403 -342 727 -652\nRAW_Data: 361 -16588 743 -320 735 -320 729 -320 729 -320 729 -320 735 -320 767 -612 439 -332 721 -646 375 -650 407 -662 397 -326 711 -656 407 -648 407 -334 759 -628 407 -652 377 -658 379 -332 725 -328 747 -650 417 -620 421 -326 731 -646 417 -16544 763 -326 731 -306 739 -296 745 -320 739 -322 737 -322 739 -650 419 -294 733 -648 385 -650 417 -652 379 -344 725 -652 389 -670 387 -348 753 -620 387 -678 383 -648 385 -344 719 -336 709 -656 405 -650 435 -326 709 -660 381 -16590 733 -348 701 -348 701 -350 703 -320 731 -352 699 -352 701 -676 413 -310 735 -654 385 -656 381 -650 385 -344 733 -648 415 -646 417 -348 701 -678 351 -674 379 -674 387 -342 701 -350 701 -680 387 -670 379 -346 733 -644 381 -16556 753 -352 691 -354 691 -356 693 -356 725 -354 695 -354 699 -682 377 -354 703 -680 383 -650 381 -650 415 -328 709 -680 381 -646 415 -356 721 -646 383 -684 381 -650 379 -342 723 -318 705 -686 399 -652 391 -346 721 -662 363 -16572 765 -322 733 -322 725 -320 725 -322 727 -322 729 -322 735 -648 419 -328 733 -646 385 -648 417 -650 379 -344 727 -658 397 -658 401 -344 725 -654 403 -620 383 -676 381 -350 725 -320 733 -646 395 -664 395 -368 705 -672 379 -32700 691 -546 435 -522 133 -520 533 -488 595 -454 595 -456 597 -790 305 -422 635 -718 309 -748 307 -712 339 -388 689 -680 375 -678 373 -390 663 -754 275 -752 311 -424 623 -716 343 -392 651 -712 379 -682 379 -356 723 -680 347 -16574 743 -342 709 -326 743 -290 737 -322 733 -320 731 -322 769 -614 453 -292 737 -644 387 -650 419 -614 447 -296 733 -638 419 -646 417 -326 743 -646 421 -612 421 -296 735 -646 417 -326 707 -672 419 -644 419 -292 751 -648 387 -16546 801 -324 691 -326 741 -320 707 -324 735 -322 731 -320 765 -648 409 -330 721 -648 377 -650 405 -644 409 -328 709 -658 409 -666 395 -330 741 -672 367 -646 411 -326 711 -654 409 -328 719 -654 417 -622 417 -326 735 -678 345 -16604 743 -312 721 -346 703 -318 737 -326 745 -322 721 -322 729 -646 441 -330 685 -648 419 -650 385 -650 415 -326 709 -644 419 -646 415 -324 749 -646 417 -614 419 -326 709 -646 415 -328 743 -612 417 -646 417 -324 749 -646 385 -16564 761 -316 729 -346 705 -332 715 -324 741 -324 705 -354 737 -650 411 -318 707 -646 409 -648 383 -652 419 -324 725 -644 417 -650 419 -324 725 -650 417 -616 417 -328 711 -648 415 -328 707 -642 417 -646 417 -324 749 -646 385 -16578 755 -298 741 -290 735 -322 733 -320 733 -322\nRAW_Data: 767 -288 769 -614 451 -294 737 -642 407 -648 385 -654 417 -294 753 -644 421 -616 451 -290 751 -646 419 -616 421 -296 737 -644 421 -296 771 -612 451 -614 449 -292 747 -646 385 -16582 749 -332 711 -322 709 -352 703 -322 729 -322 731 -354 701 -682 417 -326 703 -648 385 -650 413 -642 409 -318 713 -678 383 -682 383 -356 721 -648 383 -652 415 -328 711 -648 413 -328 709 -648 415 -648 413 -326 749 -646 385 -16538 779 -330 709 -326 705 -354 703 -324 733 -322 731 -354 703 -684 379 -350 707 -646 383 -682 381 -652 413 -326 707 -680 381 -648 413 -328 749 -646 383 -648 415 -326 707 -644 415 -326 711 -680 383 -648 415 -326 751 -646 383 -16558 757 -346 703 -346 703 -346 703 -346 703 -348 705 -340 743 -628 437 -326 711 -648 383 -654 417 -614 419 -328 743 -642 417 -646 417 -322 721 -646 417 -648 385 -328 713 -646 417 -328 707 -646 417 -648 415 -324 747 -646 385 -16582 723 -340 705 -338 707 -340 705 -376 703 -346 703 -348 701 -680 389 -344 701 -680 357 -664 379 -668 393 -368 707 -666 367 -654 435 -326 743 -632 405 -644 383 -328 733 -648 417 -324 721 -644 419 -648 417 -322 725 -650 383 -16576 733 -350 721 -332 715 -322 705 -322 735 -322 731 -322 769 -616 445 -318 709 -648 417 -616 417 -650 413 -298 737 -648 413 -646 413 -326 745 -644 419 -614 419 -326 707 -640 415 -326 743 -644 419 -616 451 -290 751 -644 385 -16560 773 -312 709 -330 743 -290 739 -322 731 -322 765 -290 769 -616 449 -294 735 -646 419 -616 417 -614 447 -310 725 -624 433 -656 403 -344 725 -654 391 -654 383 -350 689 -672 395 -326 709 -660 405 -652 415 -334 745 -650 383 -16548 761 -336 719 -322 707 -322 731 -322 731 -322 729 -322 767 -614 445 -318 709 -646 419 -616 417 -650 415 -310 731 -652 389 -666 421 -314 737 -644 419 -616 411 -346 687 -668 403 -346 721 -622 415 -638 431 -346 719 -656 357 -16578 745 -358 687 -354 687 -352 691 -356 723 -322 729 -354 699 -684 383 -358 669 -680 381 -682 347 -684 377 -360 709 -648 413 -648 415 -326 745 -646 383 -650 415 -328 707 -642 415 -328 711 -646 417 -648 415 -326 749 -646 383 -16548 747 -372 681 -362 675 -358 699 -354 701 -354 701 -356 701 -682 379 -362 709 -652 379 -652 415 -656 383 -326 729 -648 415 -650 415 -324 729 -680 377 -648 379 -356 709 -650 377 -350 703 -680 187 -32700 751 -408 653 -378 655 -412 645 -408 667 -370 671 -366 705 -696 369 -358 675 -686 349 -716 337 -716 337 -354 707 -684 379 -690\nRAW_Data: 367 -360 709 -662 373 -678 379 -330 733 -324 725 -648 417 -650 381 -680 417 -324 721 -648 381 -16570 757 -332 711 -324 711 -320 731 -322 731 -322 765 -290 769 -614 451 -324 705 -646 385 -650 417 -650 413 -310 727 -658 399 -660 399 -344 723 -658 403 -630 391 -352 687 -354 725 -618 423 -638 421 -638 423 -340 737 -644 365 -16584 751 -318 721 -318 723 -336 719 -322 741 -322 739 -320 739 -648 411 -320 709 -648 407 -648 415 -618 409 -322 743 -650 409 -646 407 -322 747 -652 383 -652 409 -320 709 -322 735 -648 417 -648 417 -650 419 -290 753 -648 385 -16582 753 -330 711 -322 705 -324 731 -322 729 -322 731 -322 767 -648 411 -320 707 -644 419 -646 385 -648 415 -328 707 -676 387 -646 415 -326 745 -646 419 -614 417 -328 703 -320 719 -676 385 -646 419 -648 417 -324 747 -646 385 -16542 773 -340 701 -370 647 -392 673 -354 705 -354 705 -356 703 -682 383 -358 705 -646 385 -650 415 -652 379 -342 727 -650 389 -668 389 -346 757 -652 353 -684 383 -348 691 -346 703 -670 365 -690 397 -660 397 -328 741 -670 345 -16570 747 -350 721 -318 721 -320 731 -346 707 -336 711 -328 747 -652 409 -332 707 -654 375 -672 377 -654 415 -332 707 -648 419 -650 419 -324 729 -648 419 -648 385 -326 711 -324 715 -676 385 -646 419 -648 415 -326 747 -644 383 -16554 765 -348 671 -380 673 -378 675 -378 673 -380 673 -380 703 -676 381 -346 705 -642 381 -674 387 -672 389 -340 703 -676 387 -672 389 -342 737 -646 385 -670 379 -340 703 -346 705 -642 411 -640 393 -668 423 -340 733 -642 363 -16594 745 -312 723 -352 689 -354 693 -350 733 -308 723 -328 749 -652 419 -328 707 -644 387 -648 417 -650 379 -348 725 -628 429 -624 433 -346 725 -656 401 -620 385 -352 723 -322 725 -628 407 -648 417 -664 419 -294 769 -646 387 -16526 761 -370 649 -392 673 -356 703 -356 697 -354 699 -354 703 -682 383 -360 705 -648 383 -650 415 -652 375 -344 727 -652 389 -668 389 -346 721 -684 355 -672 385 -348 691 -348 719 -666 369 -688 379 -666 407 -320 741 -654 381 -16572 753 -320 723 -320 731 -308 725 -324 747 -322 737 -290 769 -648 415 -318 707 -646 419 -612 421 -648 417 -296 735 -644 419 -646 419 -324 745 -646 387 -650 419 -296 735 -324 711 -642 419 -646 419 -646 415 -326 745 -644 387 -16536 783 -328 681 -358 703 -354 703 -354 697 -354 701 -354 701 -682 383 -360 703 -646 381 -650 417 -650 377 -330 741 -648 411 -648 413 -326 745 -646 381 -650 415 -346 689 -318 739 -650\nRAW_Data: 411 -650 377 -684 411 -344 695 -654 373 -16580 737 -354 703 -322 725 -320 727 -322 727 -322 731 -324 765 -648 407 -330 709 -652 377 -652 407 -646 407 -318 743 -650 407 -628 433 -328 743 -630 417 -622 419 -324 721 -322 721 -646 417 -648 385 -648 419 -324 749 -646 387 -16560 743 -372 671 -360 673 -356 703 -354 703 -356 701 -354 701 -684 379 -360 709 -652 385 -652 381 -684 381 -326 715 -678 383 -682 381 -356 717 -648 381 -682 381 -326 709 -352 689 -680 381 -682 381 -648 415 -324 749 -648 385 -16566 745 -334 721 -322 739 -288 735 -324 729 -322 733 -322 767 -616 453 -294 733 -644 385 -650 417 -650 413 -310 727 -618 421 -656 417 -350 719 -642 393 -654 385 -352 721 -316 721 -632 403 -650 449 -600 445 -318 745 -652 377 -16576 739 -332 701 -332 733 -326 711 -324 749 -320 725 -322 733 -678 407 -330 713 -650 383 -652 387 -648 419 -326 741 -642 419 -646 419 -292 751 -648 419 -616 419 -296 737 -326 711 -676 387 -646 419 -646 417 -326 745 -646 387 -32700 811 -300 743 -300 735 -326 739 -294 765 -294 763 -290 767 -620 441 -324 743 -618 413 -616 447 -616 437 -276 763 -614 455 -602 455 -286 771 -642 413 -310 695 -342 721 -346 719 -316 721 -646 419 -644 419 -346 721 -646 385 -16522 805 -364 649 -374 691 -376 653 -376 687 -378 689 -378 689 -670 387 -378 687 -676 353 -672 359 -702 359 -366 715 -630 403 -688 399 -328 739 -630 399 -320 743 -292 767 -292 733 -324 731 -650 413 -652 413 -324 761 -618 411 -16524 801 -352 647 -374 677 -366 671 -390 675 -358 701 -356 703 -686 373 -386 673 -652 413 -650 379 -652 411 -328 719 -650 411 -652 411 -326 747 -648 413 -294 721 -326 719 -324 749 -290 755 -648 413 -650 415 -324 759 -618 383 -16558 801 -318 703 -332 709 -326 707 -324 735 -322 735 -322 767 -620 443 -320 705 -646 409 -648 413 -618 417 -324 727 -646 417 -648 409 -324 741 -650 403 -318 741 -290 733 -322 729 -322 733 -646 417 -648 415 -326 759 -616 417 -16550 745 -334 713 -322 739 -322 703 -322 733 -324 733 -322 767 -646 407 -322 711 -646 411 -648 411 -624 411 -330 741 -626 411 -660 395 -332 751 -630 399 -322 745 -322 703 -322 733 -322 731 -650 415 -648 413 -328 725 -648 411 -16540 769 -342 689 -348 689 -350 725 -350 691 -346 733 -306 751 -630 433 -328 707 -654 379 -656 415 -652 381 -326 747 -642 417 -648 415 -324 723 -646 411 -320 709 -322 735 -320 727 -322 729 -652 415 -652 419 -322 725 -648 381 -16578 733 -382\nRAW_Data: 663 -374 671 -364 707 -326 741 -322 701 -354 741 -652 411 -320 709 -648 417 -652 381 -650 411 -328 711 -646 415 -648 413 -344 733 -650 377 -344 723 -310 721 -350 685 -350 721 -648 387 -676 389 -380 717 -644 363 -16572 775 -326 707 -320 715 -324 751 -320 725 -320 729 -322 763 -616 443 -330 707 -650 377 -652 411 -624 433 -324 707 -670 397 -658 411 -342 717 -668 365 -354 705 -334 711 -326 741 -320 705 -648 415 -654 415 -326 761 -618 415 -16538 725 -402 637 -394 671 -390 673 -356 703 -356 699 -356 701 -684 379 -360 701 -648 379 -686 379 -652 411 -328 709 -648 411 -648 415 -326 749 -648 383 -328 717 -324 715 -352 721 -320 729 -644 415 -648 415 -324 727 -678 349 -16576 743 -372 673 -360 677 -356 705 -354 705 -324 733 -322 733 -682 409 -320 707 -650 415 -650 381 -650 411 -328 711 -648 413 -648 415 -326 753 -646 379 -328 719 -324 717 -324 717 -352 723 -648 417 -648 417 -324 727 -646 381 -16562 769 -344 683 -362 709 -324 707 -324 733 -352 703 -324 731 -684 409 -330 715 -650 381 -654 415 -618 415 -326 715 -648 413 -650 415 -326 751 -646 383 -328 717 -326 717 -354 723 -320 723 -648 417 -650 415 -324 727 -648 381 -16572 773 -306 741 -296 741 -322 735 -322 707 -324 765 -290 765 -612 449 -326 703 -644 415 -618 417 -650 403 -346 721 -628 427 -628 431 -310 757 -626 433 -312 689 -350 719 -316 731 -342 735 -606 431 -660 395 -330 751 -630 379 -16570 779 -308 729 -344 687 -346 721 -314 721 -350 723 -350 695 -676 411 -350 693 -642 395 -658 395 -658 397 -324 739 -638 395 -664 393 -334 737 -668 365 -356 707 -324 707 -352 705 -322 733 -648 415 -656 415 -326 729 -650 381 -16566 769 -346 687 -350 691 -348 701 -338 717 -326 745 -326 741 -652 415 -328 705 -644 415 -646 379 -650 413 -328 711 -682 381 -650 413 -328 751 -646 379 -328 715 -352 691 -340 705 -362 711 -650 413 -650 411 -328 745 -646 381 -16562 745 -326 711 -322 733 -322 731 -322 731 -322 731 -322 765 -618 443 -318 705 -648 417 -648 381 -652 413 -326 711 -646 415 -648 413 -326 749 -646 415 -294 747 -318 725 -290 753 -320 729 -646 415 -650 419 -324 727 -648\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/hay21_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 99 -820 65 -230 65 -802 361 -852 131 -162 131 -298 133 -306 99 -198 129 -430 99 -732 197 -742 131 -302 197 -238 233 -132 99 -532 99 -168 101 -306 65 -832 133 -304 65 -300 65 -664 131 -198 133 -232 101 -234 99 -134 133 -404 65 -262 163 -654 131 -702 133 -864 131 -194 95 -390 131 -388 99 -1078 65 -260 97 -364 65 -194 161 -258 163 -228 97 -394 131 -336 133 -198 63 -358 97 -392 199 -164 65 -390 97 -426 99 -162 163 -326 193 -326 131 -456 131 -400 99 -700 133 -198 133 -400 131 -302 99 -202 65 -200 99 -1002 133 -236 169 -406 165 -372 99 -202 99 -334 165 -304 131 -332 133 -132 65 -464 133 -400 99 -298 67 -960 163 -400 133 -402 129 -166 101 -268 133 -432 133 -374 101 -366 101 -19558 129 -454 97 -23210 133 -688 129 -196 131 -162 65 -422 65 -132 163 -228 63 -194 65 -228 131 -292 129 -552 65 -986 65 -530 131 -164 99 -294 65 -796 65 -466 133 -200 131 -136 133 -1970 99 -1594 133 -966 65 -100 99 -300 99 -534 65 -664 99 -332 65 -198 97 -1086 131 -304 133 -402 167 -166 67 -996 99 -728 133 -268 131 -1684 163 -394 129 -690 65 -558 65 -132 131 -162 95 -456 97 -362 133 -168 67 -306 133 -202 67 -168 197 -338 167 -438 101 -362 97 -470 101 -402 65 -1058 131 -336 133 -1402 133 -402 165 -236 65 -432 99 -764 133 -470 65 -200 67 -166 131 -436 99 -398 99 -168 135 -902 65 -234 135 -200 199 -166 65 -628 197 -662 165 -694 65 -166 67 -564 65 -1072 99 -334 167 -168 131 -1058 131 -336 131 -370 99 -198 133 -462 131 -302 133 -438 67 -368 133 -1036 97 -634 165 -134 135 -670 99 -370 161 -690 65 -296 131 -1000 99 -436 133 -170 101 -404 99 -170 101 -270 67 -268 131 -302 131 -672 133 -234 65 -502 99 -604 65 -1260 99 -232 99 -964 131 -338 99 -200 65 -232 99 -168 135 -168 67 -204 67 -134 133 -370 99 -428 133 -234 135 -1128 163 -454 97 -362 163 -458 65 -164 99 -596 99 -268 131 -1562 65 -398 99 -402 163 -438 67 -368 65 -988 97 -622 131 -162 97 -390 133 -368 135 -402 133 -164 65 -438 131 -686 165 -334 99 -460 99 -368 167 -22538 65 -170 65 -766 99 -1098 165 -760 99 -364 65 -722 65 -2132 163 -326 163 -402 133 -266 161 -406 97 -168 101 -404 163 -168 67 -266 65 -334 65 -402 97 -494 65 -132 97 -462 99 -336 133 -334 131 -198 133 -672 131 -200 65 -1036 67 -1028 165 -702 99 -402 97 -1234 97 -196 63 -488 131 -658 65 -498\nRAW_Data: 97 -270 131 -566 65 -168 67 -336 67 -996 133 -300 67 -166 101 -702 99 -1202 131 -264 97 -590 163 -1680 99 -228 95 -162 97 -194 65 -886 99 -3586 65 -558 131 -1486 99 -634 65 -100 133 -132 67 -1134 97 -658 65 -1446 65 -564 133 -234 133 -372 65 -166 99 -1362 99 -332 97 -202 133 -1984 97 -198 65 -394 161 -23544 65 -334 99 -298 165 -570 167 -334 99 -198 67 -836 131 -290 65 -724 99 -298 165 -130 97 -264 127 -628 165 -266 131 -200 101 -234 135 -564 165 -234 99 -202 97 -368 131 -466 131 -1770 65 -326 165 -622 131 -296 133 -496 97 -1166 65 -1512 97 -1090 99 -534 165 -498 101 -336 99 -300 167 -302 65 -302 133 -1158 65 -166 99 -666 65 -736 233 -566 99 -502 99 -596 131 -168 135 -370 99 -1758 67 -3408 129 -1212 99 -690 65 -2674 67 -696 97 -200 65 -500 97 -338 65 -298 99 -234 165 -636 97 -724 65 -1424 97 -918 131 -198 65 -366 65 -562 99 -456 163 -1142 99 -394 165 -262 325 -230 97 -424 65 -460 131 -1150 129 -394 163 -356 197 -722 131 -920 97 -424 65 -1114 65 -262 97 -394 97 -888 97 -520 97 -21882 97 -2496 97 -164 97 -848 99 -47158 99 -234 99 -600 67 -1088 65 -918 63 -230 99 -162 127 -288 131 -164 97 -756 131 -392 99 -404 65 -400 67 -132 99 -302 131 -688 65 -982 131 -970 199 -372 101 -966 97 -130 129 -980 133 -1116 99 -162 95 -390 65 -428 163 -324 131 -266 99 -266 135 -1302 131 -470 199 -472 97 -358 97 -164 65 -230 99 -402 99 -398 99 -370 133 -372 99 -398 99 -166 129 -394 99 -266 67 -668 133 -1000 133 -734 131 -898 163 -324 131 -164 97 -424 99 -336 165 -328 163 -162 63 -946 129 -620 131 -130 129 -356 63 -296 131 -194 65 -390 97 -1692 129 -360 195 -390 127 -400 133 -200 65 -1034 133 -630 97 -162 129 -422 133 -1626 97 -368 133 -202 97 -1038 133 -498 133 -630 165 -730 97 -704 65 -134 97 -434 165 -462 99 -268 99 -628 165 -672 131 -1448 165 -604 167 -366 99 -398 131 -230 67 -438 65 -134 133 -432 67 -430 97 -232 65 -404 101 -426 131 -198 97 -756 131 -290 165 -298 99 -200 65 -1330 99 -702 131 -462 229 -294 161 -656 97 -196 97 -390 165 -1510 63 -2134 131 -328 129 -164 97 -726 99 -166 67 -134 97 -66 65 -730 99 -264 133 -266 165 -298 67 -1132 99 -1000 101 -200 67 -766 101 -368 165 -404 133 -832 99 -604 133 -640 197 -234 101 -134 99 -432 99 -100 133 -792 131 -168 101 -500 99 -298 101 -764\nRAW_Data: 133 -334 97 -196 97 -266 131 -202 167 -798 97 -966 99 -802 65 -396 65 -1062 101 -794 97 -770 131 -230 133 -728 99 -398 99 -268 133 -268 101 -1004 65 -964 97 -402 101 -438 131 -1168 97 -1494 65 -1446 99 -202 65 -566 131 -306 99 -198 65 -234 131 -470 63 -1092 165 -660 99 -266 131 -1006 67 -198 99 -3394 65 -696 199 -1100 65 -228 163 -230 131 -360 65 -228 97 -230 97 -164 97 -228 197 -164 135 -332 165 -166 133 -168 131 -1696 97 -1742 99 -556 131 -1180 65 -534 67 -794 65 -360 99 -2958 99 -532 65 -234 101 -166 67 -232 99 -734 99 -528 65 -432 65 -198 133 -66 197 -334 63 -924 131 -754 131 -1064 67 -298 99 -566 65 -330 65 -202 65 -998 65 -2024 163 -834 99 -366 65 -398 167 -5114 755 -4222 691 -310 657 -348 247 -724 257 -714 257 -726 283 -698 669 -302 683 -294 703 -312 285 -694 679 -290 679 -308 691 -318 657 -290 289 -702 677 -316 659 -324 297 -678 677 -316 691 -292 693 -4228 675 -318 667 -304 281 -692 289 -700 285 -694 281 -728 669 -302 683 -294 701 -312 285 -664 683 -324 673 -312 657 -316 691 -290 283 -702 677 -318 659 -322 295 -674 707 -284 697 -274 685 -4252 677 -318 663 -304 317 -688 259 -698 309 -696 283 -684 677 -316 661 -342 653 -294 309 -692 675 -318 659 -324 655 -344 657 -318 287 -698 683 -278 695 -310 285 -694 675 -318 663 -302 683 -4250 675 -320 659 -292 303 -682 291 -700 311 -662 317 -690 677 -318 661 -292 703 -310 269 -696 669 -316 691 -290 677 -310 689 -320 287 -682 685 -314 657 -314 285 -694 677 -316 663 -294 701 -4224 677 -318 661 -322 293 -676 289 -700 297 -696 281 -694 679 -294 701 -312 657 -316 285 -688 681 -314 695 -310 649 -294 701 -302 277 -700 681 -314 681 -290 285 -704 677 -318 659 -292 721 -4218 675 -318 693 -290 295 -680 291 -702 275 -698 313 -686 677 -318 663 -304 681 -294 277 -716 671 -318 659 -324 661 -344 655 -318 285 -698 685 -278 693 -312 285 -686 677 -316 661 -306 683 -4248 679 -318 663 -304 317 -656 289 -700 311 -662 317 -692 677 -318 663 -292 705 -310 271 -696 687 -312 657 -292 701 -312 657 -316 287 -686 677 -316 693 -310 287 -694 677 -318 665 -300 679 -4246 677 -318 665 -300 279 -692 289 -700 309 -662 317 -692 677 -318 667 -300 683 -294 277 -716 677 -320 659 -290 689 -310 689 -320 287 -682 687 -314 657 -314 287 -694 677 -318 663 -304 681 -4254 677 -286 701 -300 277 -684 289 -698 285 -726 277 -704 677 -294 699 -274\nRAW_Data: 693 -320 273 -696 681 -290 681 -308 691 -320 677 -290 295 -680 677 -318 695 -290 295 -678 675 -320 695 -258 721 -4222 677 -320 673 -294 301 -682 291 -696 311 -664 317 -692 673 -320 665 -300 681 -294 275 -718 677 -318 661 -290 687 -308 691 -320 285 -698 683 -280 691 -312 287 -694 677 -318 663 -144512 67 -362 131 -164 97 -456 99 -268 167 -674 133 -404 165 -334 165 -168 67 -404 133 -440 165 -304 165 -568 163 -298 97 -1054 65 -864 165 -302 101 -370 67 -764 97 -362 65 -690 99 -2320 99 -200 65 -1630 131 -564 99 -200 67 -302 65 -762 65 -366 99 -432 131 -134 131 -166 99 -366 133 -168 103 -668 99 -166 133 -304 133 -338 65 -200 99 -768 99 -368 131 -264 101 -402 65 -1062 131 -134 65 -434 133 -234 65 -396 99 -166 133 -366 65 -432 99 -766 167 -762 165 -1320 65 -490 97 -396 131 -464 97 -1346 99 -230 63 -420 97 -324 127 -822 131 -1558 65 -472 99 -500 65 -134 131 -434 65 -664 163 -470 133 -328 131 -1228 97 -164 97 -692 129 -936 133 -1882 99 -1620 63 -1118 97 -488 129 -728 99 -466 101 -468 65 -198 131 -202 65 -266 167 -630 65 -232 99 -1590 131 -666 97 -1294 99 -728 97 -564 67 -436 97 -22018 99 -1560 101 -200 65 -1896 65 -532 133 -202 97 -2158 133 -1600 65 -1016 65 -198 65 -260 67 -1154 65 -2234 97 -1188 133 -232 101 -336 99 -2400 99 -856 133 -884 97 -1312 129 -456 129 -746 65 -988 165 -324 99 -430 129 -164 99 -362 129 -360 131 -428 131 -598 65 -464 99 -468 131 -22500 65 -758 67 -2260 99 -164 99 -658 63 -228 163 -708 65 -730 99 -370 99 -728 97 -502 67 -298 165 -968 99 -198 65 -398 129 -882 131 -358 97 -22984 65 -688 97 -196 97 -718 131 -1210 129 -654 99 -170 135 -168 67 -338 135 -266 65 -500 65 -794 99 -100 99 -406 97 -234 65 -168 99 -406 63 -100 165 -200 99 -1298 131 -772 165 -704 65 -132 65 -1660 163 -360 133 -830 97 -724 99 -732 99 -426 63 -1356 165 -1180 131 -228 129 -720 167 -832 131 -262 163 -590 131 -400 99 -234 133 -166 67 -1598 99 -1428 65 -592 163 -286 131 -816 133 -266 167 -500 131 -698 133 -468 99 -402 99 -434 167 -200 165 -162 99 -304 101 -738 133 -336 99 -1800 165 -732 133 -722 67 -556 97 -326 97 -164 163 -458 99 -434 65 -166 65 -198 161 -1148 165 -914 99 -162 65 -130 165 -992 163 -952 65 -560 99 -826 65 -664 163 -620 197 -350 161 -292 161 -388 65 -494 65 -432 67 -202 101 -234 65 -722\nRAW_Data: 163 -196 63 -460 163 -468 65 -166 99 -334 129 -666 63 -230 63 -66 97 -328 129 -232 127 -494 129 -358 131 -260 197 -322 97 -628 99 -656 163 -358 99 -2128 133 -336 99 -1070 165 -368 99 -1004 65 -332 65 -330 163 -102 99 -2830 65 -1434 65 -800 65 -530 65 -1516 131 -400 97 -554 63 -300 99 -334 99 -1130 65 -896 99 -166 65 -1724 131 -270 99 -1068 131 -1168 97 -52402 99 -2098 99 -196 65 -454 65 -1644 97 -1754 65 -332 97 -794 131 -364 165 -330 99 -2220 129 -468 197 -1090 97 -360 65 -1746 63 -164 127 -784 131 -1284 65 -396 65 -390 97 -422 65 -428 97 -592 97 -1312 65 -694 131 -266 67 -732 97 -460 195 -360 65 -982 97 -2244 133 -472 99 -1096 97 -1762 65 -1664 131 -134 131 -1192 97 -1736 131 -204 99 -668 99 -602 165 -654 163 -324 161 -328 129 -960 65 -466 101 -768 131 -1588 99 -502 67 -988 65 -952 129 -424 65 -228 161 -162 129 -452 131 -954 65 -954 99 -328 65 -1564 133 -1300 131 -164 133 -728 135 -200 165 -298 131 -1572 131 -328 65 -164 133 -336 99 -1260 133 -370 97 -466 67 -830 197 -1260 131 -330 131 -1034 65 -198 97 -1378 133\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/hollarm.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Hollarm\nBit: 42\nKey: 00 00 00 ED 24 3C 22 6F\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/hollarm_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 211 -978 199 -1602 209 -988 193 -990 217 -1592 209 -1616 205 -958 201 -1618 211 -1574 195 -1628 193 -1590 201 -992 209 -2406 199 -1604 195 -1592 195 -1616 193 -990 185 -1628 211 -1586 201 -986 201 -1608 211 -984 199 -996 233 -1576 195 -998 203 -990 195 -1632 193 -994 189 -998 205 -990 199 -1006 205 -1586 197 -1596 225 -1584 195 -1614 195 -992 211 -1020 195 -960 207 -984 195 -1618 201 -998 247 -952 199 -998 201 -1610 197 -1002 205 -1018 197 -1566 193 -1618 201 -1000 209 -1582 195 -1632 195 -1560 197 -1638 207 -956 199 -2416 199 -1626 177 -1614 201 -1580 201 -1032 213 -1548 197 -1626 193 -994 187 -1628 207 -950 225 -992 203 -1588 207 -1026 203 -968 205 -1622 197 -970 191 -1028 209 -982 199 -1028 205 -1552 201 -1628 201 -1556 207 -1632 197 -974 239 -978 201 -982 205 -1022 199 -1572 201 -1032 177 -984 197 -1024 185 -1594 209 -1016 201 -1000 211 -1594 197 -1572 201 -1034 177 -1582 197 -1626 195 -1622 193 -1556 199 -1030 207 -2384 199 -1594 195 -1618 201 -1562 241 -982 195 -1630 195 -1590 195 -1022 189 -1590 185 -1030 205 -954 227 -1590 191 -996 207 -1030 195 -1584 195 -1026 185 -1000 197 -974 205 -1020 199 -1572 227 -1584 193 -1616 195 -1586 201 -1028 207 -956 203 -1016 203 -986 203 -1610 211 -980 199 -976 235 -992 195 -1620 193 -996 211 -974 201 -1590 211 -1610 201 -980 193 -1620 191 -1592 209 -1618 205 -1586 197 -1000 191 -2416 205 -1586 207 -1564 225 -1586 227 -958 221 -1620 189 -1590 215 -994 207 -1588 229 -968 191 -996 205 -1588 229 -972 189 -1028 211 -1574 227 -1000 193 -962 243 -1002 201 -968 201 -1612 197 -1594 203 -1592 209 -1618 207 -958 203 -992 229 -1006 201 -980 199 -1602 195 -996 189 -1028 213 -976 201 -1592 209 -980 195 -1026 187 -1590 211 -1608 201 -978 205 -1610 211 -1606 199 -1574 201 -1596 201 -994 195 -2424 193 -1588 227 -1554 235 -1592 205 -988 227 -1562 201 -1620 195 -990 211 -1608 199 -978 205 -1014 199 -1574 201 -1032 211 -958 199 -1626 211 -958 199 -1038 211 -974 201 -962 229 -1600 195 -1622 193 -1588 193 -1618 195 -988 185 -1032 205 -960 203 -1014 237 -1546 201 -1028 187 -996 201 -978 223 -1582 225 -994 211 -986 199 -1624 213 -1550 195 -1036 201 -1556 207 -1630 197 -1602 195 -1590 193 -1022 189 -2382 215 -1590 211 -1586 203 -1580 195 -1020 225 -1558 185 -1628 209 -988 225 -1580 195 -1020 191 -996 205 -1620 197 -970 191 -996 207 -1594 197 -1004 239 -980 199 -980 205 -1016 199 -1576 201 -1628 203 -1606 213 -1550 195 -1034 203 -976 199 -976 225 -990 211 -1614 199 -978 205 -992 193 -1032 203 -1574 213 -1002 201 -968\nRAW_Data: 207 -1594 231 -1598 195 -1000 207 -1576 245 -1564 193 -1618 195 -1554 257 -990 207 -2372 207 -1596 193 -1590 225 -1586 225 -958 221 -1590 219 -1592 213 -982 193 -1586 199 -1040 211 -970 201 -1588 209 -986 201 -1006 209 -1596 193 -1024 193 -994 211 -1002 201 -968 207 -1596 229 -1566 201 -1596 201 -1618 211 -982 199 -978 199 -1018 201 -996 199 -1606 197 -998 207 -988 229 -974 203 -1612 211 -974 201 -1014 207 -1554 225 -1584 225 -960 219 -1590 219 -1590 215 -1590 185 -1624 185 -998 199 -2404 195 -1626 195 -1556 235 -1594 191 -996 207 -1584 229 -1600 193 -998 205 -1584 207 -1004 201 -992 203 -1580 201 -1030 211 -960 197 -1630 211 -958 199 -1008 241 -964 201 -996 197 -1602 195 -1628 195 -1560 235 -1592 211 -958 199 -1032 207 -964 203 -996 241 -1568 195 -1032 193 -992 211 -976 201 -1606 209 -960 235 -996 207 -1558 259 -1554 195 -1020 225 -1558 209 -1620 207 -1552 201 -1626 199 -996 233 -2368 195 -1588 225 -1586 225 -1552 227 -992 207 -1584 201 -1612 195 -1000 205 -1584 207 -1004 203 -992 201 -1582 227 -994 205 -982 195 -1624 195 -994 189 -1032 211 -970 201 -994 237 -1546 201 -1624 201 -1570 247 -1550 229 -1008 201 -994 197 -976 203 -1016 199 -1574 201 -1030 185 -998 199 -1010 201 -1566 211 -1022 201 -998 213 -1596 197 -1570 201 -1030 185 -1594 247 -1544 197 -1626 195 -1588 193 -1024 205 -2378 207 -1594 195 -1588 227 -1586 225 -960 219 -1594 207 -1584 201 -1010 193 -1588 205 -1014 201 -980 191 -1620 191 -996 207 -1028 195 -1582 195 -1028 213 -956 199 -976 237 -996 201 -1606 211 -1582 197 -1602 193 -1596 201 -1000 211 -982 201 -1030 205 -954 203 -1612 211 -978 195 -1024 185 -996 201 -1610 195 -1002 205 -984 225 -1560 225 -1588 227 -958 219 -1594 219 -1558 217 -1596 211 -1610 201 -982 205 -2402 211 -1608 199 -1572 203 -1592 225 -960 245 -1598 197 -1600 195 -998 205 -1582 209 -1004 201 -960 197 -1630 195 -1000 243 -946 231 -1596 195 -994 191 -996 205 -994 199 -998 199 -1610 195 -1598 239 -1550 193 -1620 191 -996 207 -1032 201 -964 201 -984 203 -1628 207 -968 199 -1014 201 -994 199 -1604 195 -996 189 -1030 211 -1570 193 -1622 193 -990 189 -1628 207 -1586 207 -1556 227 -1588 225 -958 219 -2388 209 -1618 205 -1592 195 -1598 201 -998 207 -1600 193 -1592 201 -992 211 -1608 199 -978 203 -1016 201 -1578 201 -1030 185 -996 197 -1602 197 -996 191 -1028 211 -972 195 -992 247 -1548 229 -1596 197 -1588 195 -1616 195 -990 185 -1028 207 -960 235 -994 201 -1578 201 -1030 185 -996 199 -1008 195 -1582 193 -1028 211 -980 201 -1604 209 -1582 197 -1008 195 -1588\nRAW_Data: 203 -1610 211 -1606 199 -1574 201 -1000 215 -2388 211 -1580 195 -1626 195 -1560 271 -960 211 -1574 229 -1566 201 -1026 209 -1588 197 -1008 201 -996 197 -1602 193 -996 189 -996 241 -1570 195 -1032 203 -976 199 -976 205 -1024 199 -1570 201 -1622 195 -1622 203 -1554 207 -1000 235 -962 201 -1012 195 -992 211 -1608 201 -982 205 -984 229 -970 237 -1556 243 -974 195 -990 209 -1608 201 -1608 197 -998 207 -1578 245 -1564 193 -1588 227 -1584 225 -994 205 -2380 207 -1556 259 -1556 193 -1616 225 -958 219 -1594 209 -1580 199 -1014 195 -1582 255 -960 211 -982 201 -1602 209 -986 195 -1018 193 -1592 211 -1020 193 -992 189 -1030 211 -970 201 -1608 211 -1554 197 -1630 195 -1622 195 -990 189 -996 205 -988 201 -996 201 -1606 195 -998 189 -1030 211 -976 203 -1588 211 -986 199 -1008 209 -1596 195 -1584 227 -990 187 -1624 187 -1594 243 -1548 201 -1610 195 -998 189 -2414 189 -1620 191 -1590 185 -1622 185 -998 235 -1584 195 -1622 193 -992 189 -1626 207 -950 195 -1022 225 -1556 209 -1020 195 -998 207 -1582 245 -968 203 -1012 205 -956 203 -994 195 -1630 193 -1590 227 -1552 225 -1590 199 -998 201 -1016 199 -990 213 -1006 195 -1586 193 -1026 185 -998 199 -974 225 -1584 203 -998 241 -968 195 -1618 195 -1584 191 -1028 211 -1574 193 -1592 225 -1558 271 -1554 191 -1028 211 -2366 227 -1562 237 -1586 199 -1568 211 -1016 193 -1580 197 -1614 195 -990 211 -1612 199 -982 239 -996 197 -1600 195 -994 191 -996 205 -1586 229 -968 191 -1032 211 -976 201 -996 239 -1548 201 -1624 201 -1590 209 -1582 197 -1028 195 -992 211 -972 223 -990 211 -1552 263 -978 203 -978 199 -1012 201 -1568 211 -1018 201 -1000 213 -1596 197 -1566 227 -992 205 -1620 211 -1566 193 -1592 227 -1554 235 -1000 213 -2398 199 -1570 201 -1598 203 -1610 211 -984 199 -1606 209 -1584 197 -1010 199 -1570 211 -1018 201 -1000 211 -1594 199 -970 193 -1028 211 -1572 195 -1028 195 -992 211 -1006 201 -966 201 -1614 197 -1594 193 -1618 201 -1564 245 -982 195 -1000 205 -984 229 -970 191 -1626 203 -984 195 -1028 193 -992 211 -1604 197 -974 203 -1026 199 -1574 203 -1594 203 -990 197 -1630 193 -1560 271 -1554 189 -1628 205 -988 193 -2382 237 -1588 199 -1592 211 -1596 199 -974 191 -1622 189 -1624 205 -968 235 -1576 197 -1000 191 -996 241 -1570 193 -1028 195 -992 211 -1604 199 -976 203 -990 195 -1028 201 -978 197 -1606 195 -1628 195 -1558 235 -1588 187 -996 201 -1016 199 -1006 205 -952 255 -1584 193 -962 279 -948 199 -1006 205 -1582 229 -968 191 -1028 211 -1574 193 -1594 227 -960 243 -1590 205 -1588 197 -1600 201 -1596 203 -990\nRAW_Data: 195 -2422 193 -1584 227 -1618 191 -1558 235 -994 209 -1558 225 -1588 237 -956 211 -1612 197 -980 205 -1000 245 -1560 193 -1026 193 -994 211 -1598 197 -974 205 -1022 199 -980 203 -1022 199 -1570 201 -1598 201 -1588 207 -1620 199 -972 237 -992 211 -974 201 -1012 207 -1550 227 -992 237 -958 195 -1034 193 -1588 189 -1032 211 -974 201 -1608 211 -1556 195 -1034 199 -1558 243 -1596 197 -1602 195 -1594 195 -1026 205 -2388 185 -1592 211 -1602 199 -1574 227 -992 203 -1614 211 -1570 193 -1030 203 -1554 207 -1032 201 -970 207 -1590 229 -968 191 -1030 211 -1572 195 -1032 203 -958 195 -1004 205 -1022 197 -1568 227 -1590 201 -1600 203 -1586 207 -990 225 -958 217 -1000 239 -950 225 -1588 225 -962 209 -996 225 -990 185 -1624 209 -956 225 -962 211 -1616 199 -1608 195 -1002 205 -1608 211 -1568 195 -1588 227 -1588 237 -956 211 -2406 199 -1604 195 -1594 201 -1596 209 -990 201 -1592 209 -1584 233 -986 201 -1590 211 -984 201 -976 203 -1614 197 -1000 207 -988 229 -1566 237 -990 185 -996 197 -1012 201 -996 197 -1602 195 -1592 193 -1616 201 -1598 203 -982 195 -998 223 -990 211 -984 201 -1590 213 -984 195 -1024 187 -994 201 -1610 195 -1004 203 -1000 209 -1562 225 -1586 227 -956 243 -1582 207 -1596 229 -1566 203 -1598 201 -982 197 -2426 193 -1588 227 -1554 271 -1550 193 -1028 211 -1578 195 -1622 193 -992 187 -1622 189 -996 205 -988 225 -1556 203 -1014 237 -952 203 -1618 211 -976 201 -1010 205 -966 225 -964 245 -1564 231 -1598 195 -1590 193 -1618 193 -990 185 -1030 207 -964 201 -1016 237 -1548 201 -1028 211 -960 199 -1032 207 -1556 235 -990 187 -996 203 -1612 197 -1596 201 -998 209 -1560 227 -1590 201 -1624 201 -1572 211 -1012 201 -2360 243 -1580 207 -1586 229 -1566 203 -1002 209 -1580 227 -1596 195 -958 251 -1592 207 -988 195 -1000 191 -1624 205 -982 193 -994 189 -1626 205 -952 257 -964 235 -960 195 -1036 201 -1556 207 -1630 199 -1600 195 -1594 201 -998 207 -1002 201 -958 231 -1006 193 -1622 203 -976 199 -974 191 -1028 211 -1576 227 -968 227 -960 245 -1596 197 -1602 195 -1000 205 -1584 207 -1598 193 -1586 225 -1584 225 -986 189 -2420 207 -1550 201 -1614 195 -1624 195 -994 189 -1626 207 -1550 203 -1016 201 -1592 211 -1008 201 -966 209 -1628 197 -972 191 -1028 211 -1576 195 -1030 201 -978 199 -1006 193 -992 211 -1610 199 -1576 195 -1620 195 -1614 195 -990 211 -956 235 -996 207 -998 201 -1586 213 -978 195 -1024 187 -998 199 -1606 195 -1004 205 -1000 207 -1562 225 -1588 225 -960 219 -1592 219 -1594 207 -1586 199 -1612 195 -998 207 -2372 209 -1598 231 -1562 195 -1614\nRAW_Data: 195 -986 219 -1592 209 -1618 207 -960 203 -1624 211 -974 193 -1026 211 -1566 207 -996 235 -962\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/holtek.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Holtek\nBit: 40\nKey: 00 00 00 50 00 00 AA BA\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Holtek_HT12X\nBit: 12\nKey: 00 00 00 00 00 00 0F FB\nTE: 205\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 97 -264 65 -1890 231 -366 165 -232 99 -166 297 -68 401 -100 97 -100 759 -264 823 -132 919 -98 65 -230 367 -98 1055 -52 51 -104 101 -126 173 -148 151 -100 153 -222 77 -288 53 -280 331 -212 81 -108 246 -156 267 -268 183 -1260 53 -622 51 -468 183 -264 105 -990 77 -421 75 -52 53 -214 185 -504 75 -563 131 -338 165 -82 133 -80 53 -236 231 -208 282 -836 187 -406 81 -102 181 -54 107 -415 81 -54 109 -298 55 -406 323 -136 299 -136 437 -52 133 -294 53 -110 133 -420 333 -402 269 -574 79 -270 163 -594 124 -52 51 -320 79 -324 109 -78 105 -236 79 -292 53 -432 181 -110 187 -268 107 -78 79 -188 79 -78 75 -130 75 -128 103 -628 79 -322 79 -136 81 -196 85 -296 51 -349 159 -325 131 -80 131 -80 402 -110 55 -194 109 -108 53 -108 135 -224 81 -162 57 -228 161 -357 105 -54 103 -347 279 -294 422 -152 51 -104 99 -146 147 -205 77 -216 107 -52 181 -52 107 -158 105 -52 133 -134 139 -418 291 -102 103 -106 105 -584 103 -540 53 -52 153 -208 227 -128 307 -184 105 -328 109 -862 129 -196 55 -244 51 -1501 55 -82 81 -326 79 -500 103 -406 207 -130 75 -126 155 -202 53 -376 135 -158 53 -428 107 -467 167 -138 81 -106 79 -346 157 -84 143 -144 85 -136 53 -308 189 -248 79 -711 109 -592 107 -220 107 -104 77 -583 131 -158 53 -110 135 -191 107 -748 105 -478 77 -398 107 -138 83 -410 127 -477 103 -162 119 -72 103 -442 129 -237 107 -664 175 -74 147 -406 53 -78 127 -178 51 -102 101 -259 125 -202 257 -210 77 -52 77 -130 251 -230 77 -76 77 -178 155 -98 97 -100 153 -54 185 -82 79 -82 135 -398 241 -130 77 -76 311 -210 53 -238 181 -404 241 -352 189 -270 265 -164 215 -998 157 -82 77 -106 51 -54 75 -100 328 -178 103 -224 253 -341 147 -334 446 -76 125 -100 75 -126 173 -690 75 -250 51 -554 81 -300 53 -188 53 -106 265 -1024 105 -646 221 -82 191 -362 75 -234 131 -128 83 -224 55 -240 77 -376 157 -78 187 -358 389 -76 103 -128 199 -513 83 -492 129 -618 123 -52 129 -52 79 -376 101 -688 105 -110 55 -84 179 -152 51 -486 358 -276 99 -74 73 -679 51 -210 113 -196 365 -104 223 -104 53 -430 55 -140 55 -84 153 -202 181 -358 149 -820 103 -80 215 -108 309 -368 105 -538 154 -732 233 -588 53 -253 155 -288 55 -136 55715 -102 407 -100 259 -78 157 -130 206 -52 258 -78 441 -104 77 -80 492 -104 182 -104 79 -155\nRAW_Data: 287 -78 807 -104 1588 -52 2952 -78 1018 -240 341 -262 361 -258 335 -280 309 -100 509 -280 307 -302 307 -6968 321 -290 293 -298 323 -294 289 -314 285 -334 285 -330 257 -330 281 -328 293 -310 281 -124 479 -330 281 -326 267 -6994 295 -324 267 -320 287 -340 259 -332 257 -358 257 -354 257 -328 257 -352 257 -352 255 -150 455 -360 255 -326 255 -7024 253 -352 255 -352 255 -354 267 -326 269 -352 239 -346 261 -362 233 -358 257 -356 255 -152 455 -334 255 -354 255 -7006 267 -342 267 -352 241 -350 263 -342 259 -362 233 -356 257 -356 231 -378 231 -380 231 -150 455 -358 231 -378 229 -7050 229 -378 229 -352 253 -352 239 -372 239 -380 243 -348 235 -368 231 -388 231 -356 233 -176 431 -384 229 -380 231 -7028 239 -368 241 -372 241 -350 239 -372 233 -366 233 -384 231 -358 231 -380 231 -378 231 -176 429 -384 205 -402 205 -7064 213 -380 243 -378 215 -374 233 -368 233 -386 231 -382 205 -382 231 -380 229 -380 231 -176 429 -386 203 -378 231 -7062 229 -378 229 -378 229 -378 229 -378 213 -396 203 -404 203 -404 203 -404 203 -404 203 -198 431 -376 215 -376 239 -7064 235 -364 231 -386 231 -384 205 -408 205 -380 231 -380 231 -380 231 -378 207 -404 205 -200 431 -384 205 -404 205 -7118 205 -408 205 -406 205 -408 205 -408 205 -408 205 -436 179 -440 283 -460 75 -1486 105 -54 103 -784 107 -218 113 -824 125 -2140 85 -583 51 -1291 53 -722 113 -622 107 -1176 51 -52 75 -152 113 -56 111 -784 105 -436 81 -112 85 -272 53 -406 81 -1345 105 -3008 53 -1418 105 -254 107 -874 131 -818 53 -595 77 -130 123 -1088 213 -724 131 -793 99 -428 75 -288 51 -334 101 -1471 81 -274 53 -164 107 -2618 129 -532 55 -366 297 -160 83 -166 184 -208 57 -196 328 -1640 235 -1743 79 -594 51 -76 77 -270 163 -728 51 -408 55 -354 101 -126 153 -184 133 -392 53 -604 77 -804 127 -965 51 -78 103 -668 262 -210 237 -439 75 -340 77 -106 105 -1775 79 -284 51 -3100 157 -433 55 -1355 107 -198 165 -82 79 -770 79 -1587 81 -638 79 -530 103 -703 51 -396 71 -956 151 -248 53 -553 103 -154 75 -806 75 -660 53 -698 127 -1210 53 -3175 79 -608 245 -1590 207 -164 107 -232 51 -1094 99 -695 135 -955 53 -804 217 -587 55 -1452 163 -1232 79 -968 79 -720 81 -1110 129 -1194 105 -1736 79 -386 79 -184 77 -652 75 -2442 103 -555 129 -125 75 -1115 81 -108 135 -980 53 -808 105 -1001 155 -1068 131 -850 99 -268 51 -1106 159 -2408 79 -612 51 -1191\nRAW_Data: 135 -212 77 -2816 137 -980 51 -1921 79 -1554 53 -906 83 -1042 99 -5161 51 -290 75 -558 51 -260 77 -738 99 -2128 105 -1384 53 -108 79 -188 55 -830 77 -5690 109 -2652 51 -1374 53 -2469 79 -2163 203 -152 71 -1459 79 -2741 105 -262 75 -1194 75 -228 153 -210 107 -3236 107 -1500 77 -3562 79 -170 55 -540 53 -1270 51 -304 51 -2071 137 -134 105 -266 101 -834 51 -8117 73 -3292 81 -732 181 -1539 53 -132 109 -1375 77 -132 81 -1061 77 -1620 79 -1373 105 -643 183 -190 53 -620 77 -1633 55 -931 51 -398 135 -924 111 -1122 51 -128 125 -2361 97 -2571 51 -3563 51 -794 77 -844 81 -788 81 -2040 77 -232 77 -1000 131 -322 73 -848 55 -336 107 -1546 81 -602 79 -954 107 -1824 73 -1131 240 -1048 197 -2442 73 -720 127 -78 77 -574 79 -624 101 -5754 103 -1829 149 -719 77 -982 53 -250 53 -634 161 -2201 75 -3483 149 -356 73 -98 123 -176 75 -702 231 -1633 121 -704 53 -1163 51 -1227 73 -368 123 -258 103 -530 279 -628 53 -884 55 -82 101 -102 135 -162 107 -1264 159 -3370 77 -3538 131 -130 81 -2460 129 -588 51 -514 77 -376 53 -616 53 -956 51 -1508 53 -1044 53 -1160 261 -442 53 -640 83 -300 79 -1723 75 -124 125 -532 103 -500 79 -764 53 -1220 279 -458 77 -548 159 -450 223 -718 55 -716 51 -716 203 -732 79 -6974 53 -1086 51 -1794 79 -882 53 -4221 53 -1070 77 -1126 75 -2307 155 -1233 131 -156 111 -1352 51 -478 51 -154 75 -724 51 -635 103 -652 77 -294 75 -268 81 -1046 177 -1778 51 -298 109 -336 77 -80 53 -192 133 -556 99 -394 81 -654 51 -106 79 -1893 55 -240 55 -136 55 -2680 77 -136 81 -164 53 -576 135 -3304 131 -654 103 -788 53 -224 187 -354 79 -1872 81 -290 207 -410 77 -1132 55 -112 57 -512 51 -2736 123 -404 239 -1283 51 -1526 213 -720 133 -354 109 -396 77 -1518 105 -632 53 -164 53 -1090 55 -730 79 -1705 75 -370 75 -378 79 -2022 81 -2480 103 -1108 79 -106 53 -1784 105 -512 219 -669 179 -795 105 -376 105 -514 53 -402 53 -282 83 -554 133 -448 105 -972 79 -490 81 -1184 77 -216 109 -132 51 -164 135 -1570 51 -1329 79 -1218 53 -2450 75 -1246 51 -3825 77 -1114 53 -1718 51 -846 79 -826 165 -306 75 -3821 51 -606 55 -880 107 -56 55 -5505 53 -504 79 -1356 77 -569 51 -2268 149 -358 53 -1979 51 -106 103 -176 221 -508 51 -110 53 -3896 127 -234 79 -1012 79 -84 243 -2860 53 -1010 73 -286 133 -2198 151 -370 129 -1942 101 -154 109 -702 79 -696 51 -302\nRAW_Data: 51 -744 83 -496 79 -672 77 -170 111 -264 103 -472 51 -524 75 -1172 51 -240 51963 -254 333 -278 333 -276 333 -278 305 -302 307 -76 531 -280 331 -276 331 -6938 347 -268 319 -272 319 -292 311 -312 283 -306 309 -304 283 -328 293 -310 281 -326 293 -110 479 -322 295 -322 291 -6964 283 -334 283 -304 283 -328 283 -328 281 -328 255 -354 265 -338 255 -352 255 -352 255 -124 481 -338 277 -328 279 -6984 269 -350 265 -320 287 -340 257 -332 257 -358 255 -356 255 -328 257 -354 257 -352 255 -150 455 -360 229 -376 231 -7040 239 -348 267 -350 239 -346 261 -364 231 -360 257 -356 231 -380 231 -378 231 -352 257 -150 453 -360 229 -378 229 -7032 239 -372 227 -378 227 -380 241 -352 241 -374 233 -366 233 -384 233 -356 231 -380 231 -176 431 -384 229 -354 255 -7030 231 -376 231 -376 229 -378 229 -378 229 -352 255 -352 255 -352 229 -378 229 -378 229 -174 427 -384 229 -376 241 -7040 241 -350 235 -368 233 -388 231 -358 233 -380 231 -380 229 -380 231 -380 229 -380 211 -192 421 -366 227 -380 227 -7068 239 -378 215 -402 209 -394 207 -388 231 -386 231 -384 205 -408 205 -408 205 -408 205 -202 409 -412 207 -1400 105 -1002 51 -9495 53 -250 57 -1093 155 -124 73 -344 75 -2759 83 -468 53 -738 77 -134 53 -1581 51 -106 127 -1209 121 -956 51 -918 83 -276 85 -1696 125 -618 81 -1666 51 -152 101 -1324 107 -54 141 -586 75 -784 55 -1828 51 -4052 81 -480 53 -218 141 -1346 105 -1152 127 -776 53 -426 135 -390 105 -939 81 -887 71 -492 107 -1311 105 -1844 101 -1340 77 -2586 51 -2637 51 -1626 105 -54 53 -1672 151 -2830 57 -3143 51 -1859 79 -929 179 -78 77 -890 73 -894 79 -80 79 -1184 53 -323 53 -1344 79 -636 53 -1808 55 -3048 79 -2287 53 -572 51 -822 51 -608 77 -1772 75 -2521 79 -162 81 -664 163 -110 83 -524 53 -930 53 -1816 79 -1305 51 -816 53 -1358 55 -822 55 -594 81 -2230 55 -234 77 -600 201 -3174 151 -2534 71 -122 51 -1370 81 -3130 127 -236 79 -728 101 -1472 53 -800 127 -528 51 -802 77 -52 99 -3144 77 -6346 51 -1090 81 -588 79 -292 169 -2345 107 -370 187 -1218 81 -296 75 -696 51 -516 77 -2154 75 -558 75 -816 103 -2200 125 -2766 229 -376 151 -3375 79 -1466 53 -535 167 -524 217 -54 131 -3408 51 -54 109 -1886 77 -732 83 -536 99 -3128 103 -168 57 -1852 51 -574 79 -296 155 -844 99 -767 147 -2406 99 -1014 81 -4460 175 -226 195 -454 73 -2236 53 -818 155 -352 83 -752 79 -5083 51 -1716\nRAW_Data: 77 -1925 51 -1760 81 -162 53 -1469 79 -362 53 -471 103 -750 53 -562 127 -238 79 -250 107 -52 53 -210 83 -504 79 -1566 107 -82 79 -968 53 -458 131 -1944 301 -594 79 -382 51 -1196 79 -3963 183 -670 111 -360 105 -1990 53 -1084 79 -658 73 -301 73 -264 75 -1335 79 -1110 81 -1856 159 -3986 75 -1262 53 -1189 53 -158 53 -903 81 -1135 133 -350 107 -178 151 -1096 73 -2734 81 -1280 107 -950 79 -52 53 -5912 53 -1653 111 -1601 79 -742 51 -1104 55 -1254 51 -867 53 -136 109 -813 79 -1082 53 -395 53 -54 109 -504 79 -218 109 -114 57 -1620 157 -3003 79 -656 53 -510 209 -1933 107 -1197 159 -5508 79 -1164 77 -1466 77 -742 101 -124 71 -2049 85 -144 109 -1304 105 -2310 55 -381 83 -114 113 -944 103 -184 83 -558 55 -2064 109 -760 75 -1036 77 -574 51 -134 131 -224 57 -104 53 -440 53 -1262 183 -2454 51 -1966 73 -1950 125 -1095 51 -480 121 -1994 57 -1930 103 -786 79 -2272 105 -3312 51 -746 127 -144 133 -1608 77 -692 51 -1136 53 -164 55 -573 55 -3110 53 -1558 105 -6248 53 -1051 111 -886 105 -2234 103 -106 53 -1256 101 -1446 111 -974 79 -851 81 -136 193 -3392 83 -582 103 -1197 111 -196 55 -906 51 -742 77 -2038 71 -686 53 -1943 51 -134 51 -852 51 -1658 133 -2050 161 -388 77 -326 81 -412 55 -1137 81 -3256 55 -1516 53 -1414 117 -372 51 -1144 199 -3087 51 -430 75 -1856 151 -128 95 -192 398 -1207 77 -280 51 -2716 51 -808 53 -78 77 -1524 109 -54 79 -410 79 -132 53 -770 109 -2066 185 -368 131 -2102 125 -1037 75 -780 127 -128 51 -176 53 -1982 77 -140 111 -1046 109 -3166 169 -1956 77 -4040 79 -2778 73 -204 129 -1546 82945 -150 359 -252 333 -76 533 -280 319 -286 305 -6960 319 -300 323 -270 317 -290 313 -308 283 -306 309 -304 283 -328 291 -310 281 -326 281 -124 481 -334 279 -302 305 -6970 293 -318 301 -304 279 -328 279 -328 277 -330 277 -330 293 -298 295 -320 287 -314 283 -128 489 -334 255 -330 281 -7012 265 -340 265 -344 251 -354 253 -354 269 -322 293 -322 263 -344 259 -336 257 -360 257 -152 457 -358 231 -354 257 -7060 237 -364 255 -354 237 -366 255 -354 255 -354 267 -352 241 -352 265 -348 261 -342 259 -154 439 -388 231 -358 255 -7114 237 -370 235 -394 233 -362 233 -386 233 -386 233 -386 233 -386 231 -388 233 -414 233 -180 441 -392 233 -1832 53 -564 53 -370 289 -867 201 -78 103 -352 213 -586 103 -1226 165 -112 55 -300 105 -975 107 -358 77 -410 55 -1777 51 -973 51 -828\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/holtek_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450\nRAW_Data: 937 -900 447 -454 969 -884 479 -466 939 -452 935 -454 981 -454 943 -452 955 -458 945 -452 979 -444 943 -486 945 -448 951 -464 977 -440 967 -478 935 -480 951 -456 969 -460 975 -450 973 -450 979 -470 977 -450 981 -910 485 -466 939 -928 489 -448 971 -940 445 -484 951 -928 485 -484 945 -948 479 -456 973 -918 481 -944 451 -928 471 -478 995 -912 487 -472 977 -15948 479 -444 1021 -910 521 -444 995 -942 471 -480 991 -450 1013 -452 1023 -446 1001 -484 981 -486 1007 -458 1015 -470 1007 -450 1033 -452 1005 -480 1005 -458 1013 -470 1007 -482 1015 -450 1023 -482 1009 -462 1015 -468 1011 -484 983 -482 1021 -944 487 -484 1015 -942 471 -486 1003 -948 503 -478 991 -948 501 -478 1031 -914 521 -486 991 -946 519 -916 511 -944 485 -474 1009 -974 487 -482 1015 -16224 521 -468 1005 -970 503 -478 1001 -954 505 -458 1035 -484 1019 -482 1039 -482 1019 -486 1031 -490 1009 -478 1033 -486 1033 -486 1011 -494 1039 -478 1039 -450 1049 -486 1033 -488 1005 -476 1071 -448 1067 -486 1017 -468 1045 -482 1045 -484 1015 -952 515 -482 1043 -944 519 -482 1049 -950 519 -454 1039 -956 523 -484 1011 -960 505 -486 1065 -956 509 -926 539 -944 519 -480 1017 -984 521 -454 1037 -16440 553 -440 1043 -976 507 -460 1069 -940 513 -486 1041 -480 1067 -482 1033 -476 1061 -472 1043 -510 1049 -486 1041 -482 1043 -482 1065 -476 1037 -486 1069 -492 1037 -484 1047 -504 1047 -486 1041 -484 1041 -514 1015 -520 1049 -476 1053 -490 1041 -980 519 -486 1043 -962 507 -482 1049 -994 507 -500 1043 -946 507 -516 1033 -982 517 -478 1049 -984 509 -976 505 -950 527 -490 1039 -980 519 -486 1047 -111258 195 -428 263 -162 163 -362 97 -132 65 -98 163 -132 825 -100 795 -100 1795 -134 587 -66 229 -100 1349 -164 3261 -66 2305 -132 2219 -66 5549 -234 497 -132 201 -66 667 -298 2369 -68 4381 -66 3909 -134 923 -98 723 -100 1651 -168 1197 -100 65 -66 199 -68 195 -100 197 -134 1135 -66 2787 -66 3163 -68 231 -68 197 -100 6675 -100 667 -98 1125 -66 67 -98 2423 -66 2017 -332 2949 -100 1129 -68 1655 -100 1229 -66 1285 -130 163 -132 1315 -66 525 -98 295 -100 131 -64 427 -132 2207 -98 1153 -66 99 -100 697 -98 1397 -166 863 -66 1393 -132 5005 -66 497 -100 1753 -100 597 -66 1667 -66 397 -100 961 -66 763 -134 859 -64 689 -98 1917 -134 199 -234 167 -100 131 -166 2061 -66 1521 -98 759 -100 983 -66 825 -166 459 -66 2049 -166 1615 -100 829 -234 631 -66 465 -66 1493 -68 433 -66 1623 -132 65 -100 1133 -132 3083 -66 199 -132 199 -68 1257 -66\nRAW_Data: 265 -68 1061 -98 533 -100 1233 -68 1721 -68 995 -100 2535 -66 4193 -232 727 -100 727 -100 2773 -66 133 -98 399 -134 233 -232 67 -66 497 -100 267 -132 1127 -134 1063 -66 565 -132 97 -132 523 -132 919 -66 891 -66 855 -98 495 -66 3363 -296 3199 -98 563 -66 133 -100 495 -98 1165 -134 1161 -166 1849 -98 853 -132 5647 -134 563 -98 1827 -100 131 -100 1125 -132 1659 -132 265 -68 1121 -66 465 -232 431 -68 3589 -98 197 -68 97 -164 1717 -66 1645 -66 397 -66 97 -68 231 -166 631 -100 627 -66 1757 -66 131 -164 527 -98 1285 -328 1213 -134 2059 -100 1791 -68 931 -66 1611 -66 1511 -66 2211 -66 2597 -100 2545 -98 197 -162 1089 -98 589 -360 495 -132 1685 -202 1095 -100 729 -100 2825 -100 231 -100 567 -100 231 -66 1027 -66 131 -68 525 -132 1613 -232 461 -232 1597 -66 627 -198 231 -98 131 -98 65 -100 1229 -68 2507 -64 1349 -66 195 -134 97 -66 1321 -100 855 -132 163 -132 1151 -100 1025 -164 329 -66 891 -98 951 -132 163 -166 591 -98 1149 -132 955 -66 1329 -98 923 -66 331 -64 4269 -66 797 -134 399 -98 267 -170 197 -100 429 -198 1225 -100 331 -100 231 -132 1463 -100 597 -164 331 -66 1863 -134 659 -98 5507 -100 719 -100 131 -64 655 -164 1579 -98 1423 -130 1381 -98 1317 -132 467 -66 495 -132 361 -132 4417 -98 631 -364 299 -100 1499 -132 267 -68 663 -98 691 -100 2433 -66 953 -98 721 -66 1355 -232 897 -134 897 -134 365 -100 267 -132 2059 -132 199 -102 797 -68 695 -66 601 -66 265 -68 499 -66 1327 -164 1355 -64 1279 -66 3257 -66 1351 -66 131 -96 359 -132 499 -232 623 -96 427 -68 1909 -98 591 -98 4671 -100 4541 -66 1491 -66 3347 -98 1277 -100 1679 -198 295 -130 357 -98 697 -98 865 -100 2817 -66 329 -98 787 -64 1117 -66 3313 -202 1721 -100 199 -100 399 -66 199 -132 1891 -100 235 -100 201 -134 765 -166 761 -132 1529 -66 629 -202 861 -130 3501 -98 1377 -100 1741 -164 1509 -66 735 -68 733 -66 265 -166 2015 -134 131 -100 663 -100 2995 -132 1577 -98 1885 -66 2461 -100 1189 -66 1425 -100 201 -100 1691 -100 199 -98 499 -166 233 -100 233 -134 661 -68 1393 -100 295 -164 2079 -66 1289 -66 329 -198 599 -100 465 -98 3995 -98 199 -268 2045 -264 199 -66 1593 -66 165 -68 1561 -164 629 -66 635 -100 1251 -230 2733 -66 1727 -66 629 -100 1229 -132 731 -66 163 -198 131 -100 693 -66 3223 -68 565 -132 1091 -134 531 -100 3223 -68 729 -100 1527 -134 895 -166 1265 -66 527 -100 201 -200 1463 -66 1233 -132\nRAW_Data: 2397 -200 167 -234 1803 -66 821 -100 1351 -66 1687 -100 165 -66 233 -66 1125 -100 2203 -132 197 -98 97 -66 593 -164 4187 -102 529 -66 1161 -68 799 -66 427 -232 263 -66 589 -68 495 -68 197 -100 525 -66 327 -98 427 -130 1551 -66 727 -102 133 -234 265 -98 459 -66 2337 -64 585 -68 297 -68 691 -98 1857 -134 665 -132 365 -66 931 -166 495 -430 689 -196 1191 -98 465 -100 931 -366 1351 -102 3185 -164 1151 -98 2465 -66 2193 -100 331 -134 165 -98 267 -166 1985 -98 889 -132 765 -66 531 -100 1449 -166 457 -98 1715 -66 299 -166 3131 -130 197 -98 817 -294 793 -100 97 -66 3415 -164 1019 -98 1675 -132 197 -100 133 -68 199 -134 3319 -298 297 -66 791 -66 1029 -134 2153 -100 1629 -132 1391 -68 1229 -100 665 -66 2039 -164 461 -64 261 -66 395 -202 395 -166 3159 -134 2253 -166 265 -132 395 -66 887 -98 163 -66 589 -98 227 -130 1151 -230 293 -66 591 -68 527 -132 2883 -100 231 -66 99 -232 761 -134 499 -64 929 -100 167 -300 2259 -100 691 -164 459 -66 493 -132 163 -64 1283 -164 757 -132 295 -264 1023 -100 197 -198 6635 -198 2407 -100 2091 -132 1531 -66 1889 -100 199 -134 3567 -100 2981 -100 263 -198 425 -164 595 -100 231 -68 2691 -66 965 -100 2907 -98 367 -132 885 -198 1721 -100 659 -100 97 -296 495 -166 299 -134 397 -132 699 -66 1165 -66 465 -68 197 -66 659 -66 1543 -66 819 -164 2913 -98 1061 -66 5475 -132 167 -100 1035 -66 3427 -298 429 -166 2723 -66 831 -98 133 -66 133 -66 495 -98 701 -66 1063 -98 1991 -100 3319 -66 263 -66 233 -66 695 -66 593 -132 595 -66 553 -66 459 -66 197 -164 2241 -66 165 -68 959 -98 1587 -166 65 -102 233 -66 465 -134 1227 -100 2359 -66 1959 -198 331 -232 165 -102 531 -100 63 -66 1999 -68 265 -100 429 -66 657 -166 297 -132 823 -100 129 -132 4511 -164 659 -68 299 -66 593 -66 99 -134 65 -100 397 -66 1561 -66 697 -100 429 -66 265 -134 361 -132 195 -130 1319 -66 133 -66 265 -100 397 -268 895 -100 363 -134 433 -66 133 -100 2321 -68\nRAW_Data: -98 3573 -98 533 -68 961 -68 729 -132 559 -166 2189 -100 131 -68 657 -100 1387 -132 133 -68 2255 -68 429 -66 231 -134 793 -100 887 -98 361 -166 2141 -66 227 -130 663 -100 759 -100 1161 -134 1821 -66 327 -98 985 -130 757 -132 131 -132 1693 -66 361 -98 1411 -100 591 -132 1025 -66 663 -66 1065 -166 1059 -166 365 -66 723 -100 1659 -132 1883 -98 785 -132 1031 -66 261 -66 2501 -98 297 -66 1195 -100 691 -134 3009 -100 3921 -66 861 -66 363 -132 3361 -132 723 -66 459 -164 163 -164 333 -66 1291 -98 821 -230 591 -164 97 -262 361 -66 689 -66 733 -66 233 -134 1627 -66 533 -66 195 -100 521 -100 493 -98 493 -98 2173 -66 2037 -132 165 -100 429 -132 695 -100 67 -132 465 -68 1491 -100 1257 -66 965 -100 365 -68 929 -132 561 -66 899 -132 597 -132 861 -100 2627 -166 197 -98 2079 -66 2223 -100 1791 -364 895 -132 1027 -132 235 -68 599 -132 829 -66 197 -132 695 -66 133 -66 531 -68 333 -64 563 -66 265 -132 369 -134 2239 -164 4269 -100 793 -66 1495 -198 821 -164 133 -66 867 -66 797 -66 429 -66 365 -166 1729 -168 959 -100 1417 -66 233 -100 2579 -166 993 -164 461 -66 1529 -68 961 -66 1049 -98 1061 -132 2847 -66 229 -66 397 -134 263 -100 3285 -66 4115 -66 1547 -134 297 -132 431 -100 2895 -100 563 -66 1491 -66 399 -100 721 -66 395 -68 399 -66 1289 -66 293 -164 2307 -98 525 -66 3663 -64 927 -132 499 -134 1127 -264 397 -98 399 -198 131 -100 333 -100 663 -164 921 -166 1481 -262 691 -64 659 -64 2167 -98 3689 -100 833 -100 2085 -66 697 -100 595 -66 923 -134 893 -232 265 -98 367 -66 1157 -66 263 -130 1017 -66 623 -66 753 -100 2873 -132 395 -198 2787 -100 861 -132 3847 -100 297 -66 233 -98 1333 -100 495 -100 1325 -134 367 -66 595 -66 361 -230 4931 -66 1821 -98 329 -98 365 -168 333 -300 897 -100 2777 -66 1945 -132 2601 -66 951 -66 425 -98 789 -98 359 -64 1051 -66 1443 -132 851 -98 625 -100 97 -66 731 -232 263 -134 2757 -68 3021 -166 265 -100 1633 -132 427 -66 233 -98 799 -100 1059 -100 263 -98 557 -68 1063 -66 461 -100 1023 -98 163 -198 1481 -132 1227 -98 327 -100 327 -66 1317 -66 1853 -66 1061 -134 1287 -66 1315 -66 1345 -132 723 -66 1225 -68 1463 -166 3261 -98 2883 -66 563 -100 821 -100 2077 -166 3137 -66 565 -66 1355 -234 1415 -132 165 -66 397 -132 493 -132 563 -166 893 -66 1193 -66 1249 -100 333 -132 2083 -66 921 -100 1225 -262 861 -166 1321 -100 895 -100 591 -98 1249\nRAW_Data: -98 97 -66 6825 -66 231 -68 14077 -66 1787 -66 1547 -64 2617 -66 2925 -66 1723 -132 1529 -66 865 -166 827 -198 431 -66 495 -66 1121 -198 1327 -100 397 -130 557 -66 97 -100 261 -98 723 -98 557 -98 463 -98 463 -100 325 -66 3703 -100 465 -198 1123 -98 2545 -66 361 -66 857 -64 3455 -132 663 -98 1991 -200 825 -100 919 -98 893 -164 1749 -66 7759 -132 3321 -66 1807 -132 527 -66 393 -100 817 -130 657 -164 1485 -98 2367 -66 4171 -100 197 -130 3665 -134 1059 -132 597 -66 533 -66 1023 -98 1253 -134 2021 -100 231 -100 233 -66 197 -66 199 -66 1961 -168 729 -100 531 -100 461 -98 1361 -100 11161 -100 659 -166 229 -98 1675 -98 1027 -100 2063 -298 431 -100 99 -134 1059 -66 199 -100 763 -134 231 -66 233 -102 1761 -98 331 -68 757 -132 425 -64 457 -132 99 -66 2091 -66 567 -164 2121 -68 2125 -132 595 -200 759 -102 797 -132 1345 -66 429 -132 1019 -66 195 -66 791 -68 1227 -68 797 -132 1591 -200 199 -134 165 -66 1053 -66 559 -98 853 -164 825 -100 329 -98 891 -196 689 -132 657 -100 2341 -98 1119 -66 1883 -100 2607 -100 467 -100 1067 -164 6935 -66 2409 -132 855 -66 1809 -98 1119 -164 65 -66 199 -100 233 -132 931 -132 563 -66 1393 -132 567 -66 301 -68 1295 -66 529 -98 793 -66 131 -134 533 -132 827 -132 731 -332 1251 -98 921 -98 327 -198 361 -234 529 -66 1577 -132 97 -134 199 -100 1099 -68 1193 -132 991 -100 953 -98 2895 -166 1679 -98 161 -130 129 -66 1019 -100 261 -264 531 -100 263 -134 299 -68 495 -98 831 -100 531 -66 1357 -100 2051 -100 229 -98 829 -66 427 -66 859 -134 995 -68 665 -66 1793 -134 361 -100 2349 -66 331 -100 197 -66 1591 -66 959 -66 431 -234 2219 -332 661 -66 1487 -100 3381 -68 261 -164 463 -134 3377 -68 1127 -134 691 -66 529 -132 99 -66 6687 -98 889 -132 197 -164 725 -100 963 -66 2947 -132 327 -132 889 -66 393 -98 1581 -100 193 -130 97 -66 293 -66 1675 -100 1887 -98 2017 -100 597 -66 293 -98 557 -100 259 -98 985 -100 1727 -100 165 -100 301 -232 329 -100 533 -98 727 -100 761 -66 961 -68 2759 -100 2019 -66 855 -230 859 -98 1215 -98 1887 -98 131 -98 819 -166 227 -130 723 -132 625 -66 501 -66 429 -66 831 -66 1291 -66 331 -132 431 -132 1389 -100 265 -166 1461 -66 1907 -490 911 -962 409 -508 919 -944 419 -486 913 -526 915 -480 925 -488 911 -488 943 -476 925 -486 947 -462 945 -482 933 -484 951 -460 977 -440 967 -484 945 -468 945 -466 975 -450 983\nRAW_Data: -444 977 -458 975 -456 975 -444 965 -940 485 -430 967 -928 487 -448 981 -910 489 -446 963 -944 485 -430 975 -930 489 -912 485 -896 487 -452 977 -916 509 -454 977 -922 479 -448 985 -15898 487 -456 973 -926 493 -448 1001 -918 481 -484 981 -478 979 -450 1013 -476 977 -450 1017 -476 983 -448 999 -480 985 -484 983 -478 985 -484 981 -480 983 -476 1001 -474 979 -496 973 -486 1015 -476 977 -476 1005 -484 973 -486 1007 -920 505 -456 1011 -918 509 -458 1011 -950 497 -452 1011 -920 507 -458 1007 -950 477 -958 483 -942 481 -480 1003 -918 515 -446 1039 -922 505 -458 1007 -16154 501 -462 1013 -934 517 -474 999 -958 483 -478 993 -484 1037 -450 1027 -482 1027 -452 1037 -482 1025 -444 1035 -490 1009 -482 1013 -484 1001 -484 1039 -474 1003 -484 1045 -464 1011 -480 1049 -446 1037 -492 1007 -482 1013 -510 1005 -494 1005 -970 485 -482 1017 -974 473 -498 1007 -974 481 -486 1047 -948 479 -482 1051 -948 497 -952 519 -938 485 -482 1041 -946 517 -478 1013 -944 503 -472 415 -83640 97 -200 65 -632 131 -300 365 -364 231 -132 1061 -100 3047 -100 1393 -66 499 -66 1091 -66 333 -66 2353 -100 2273 -66 1427 -166 563 -132 1559 -132 427 -100 925 -98 13469 -100 2019 -98 821 -132 1097 -68 297 -100 897 -100 1129 -166 465 -166 961 -66 597 -100 165 -66 267 -100 201 -100 765 -134 297 -66 165 -66 3081 -100 1293 -100 1289 -136 233 -66 357 -66 1155 -166 295 -100 1197 -68 1089 -98 425 -132 1187 -100 523 -98 463 -98 197 -98 131 -98 493 -66 393 -98 2797 -164 359 -232 325 -66 229 -164 625 -98 1215 -164 425 -66 589 -98 195 -66 1083 -100 197 -68 1557 -66 1427 -66 525 -66 429 -132 863 -66 1129 -166 831 -98 265 -98 1183 -66 3157 -100 2735 -98 2819 -166 4645 -66 301 -68 1395 -132 1097 -100 897 -198 629 -200 1419 -132 493 -66 521 -132 697 -100 695 -66 459 -298 859 -66 559 -100 1029 -100 4113 -66 1167 -66 14017 -66 2123 -68 525 -132 861 -100 329 -66 399 -134 1523 -132 327 -64 691 -98 463 -132 1803 -132 853 -166 715 -66 953 -66 525 -98 723 -132 989 -132 461 -98 459 -164 2239 -66 1185 -66 589 -100 1945 -230 1483 -66 399 -66 265 -168 965 -66 197 -168 699 -68 1125 -68 529 -98 491 -66 987 -130 525 -168 397 -66 597 -100 561 -132 1353 -66 391 -132 393 -66 591 -98 557 -98 787 -66 463 -100 199 -134 395 -100 759 -66 295 -130 261 -98 229 -100 99 -100 1595 -66 699 -100 499 -66 595 -98 327 -132 957 -132 331 -100 493 -100 1313 -66 295 -132 197 -198 1279\nRAW_Data: -66 9461 -100 329 -68 27921 -66 24331 -68 13415 -66 6439 -98 133 -66 4193 -98 395 -66 653 -66 983 -66 163 -66 955 -132 1791 -66 861 -100 363 -132 1659 -132 667 -166 467 -134 429 -166 265 -66 4065 -98 293 -98 3183 -130 555 -98 163 -162 259 -100 661 -100 7057 -100 931 -100 1297 -66 2559 -98 1193 -100 333 -100 563 -132 65 -100 793 -66 855 -64 659 -100 929 -102 893 -132 689 -66 3475 -68 1361 -198 331 -134 691 -66 295 -66 425 -164 731 -266 921 -100 599 -100 165 -66 227 -98 1091 -66 263 -66 1215 -100 227 -164 657 -66 953 -132 359 -66 1845 -66 1779 -132 753 -164 393 -66 731 -66 1195 -66 533 -66 797 -132 1623 -98 1281 -100 493 -98 659 -98 2417 -166 799 -132 1259 -100 559 -134 595 -166 199 -66 1461 -198 865 -100 459 -66 463 -166 165 -100 497 -66 1097 -66 1579 -100 1449 -98 885 -98 263 -100 1097 -132 627 -68 329 -132 487 -132 427 -132 361 -66 525 -98 687 -66 1161 -100 263 -66 729 -100 229 -98 559 -66 1213 -100 1015 -66 795 -66 5475 -66 4043 -66 1683 -166 1151 -132 429 -98 1447 -68 261 -98 985 -100 429 -100 1289 -198 2269 -132 7999 -98 1591 -132 3233 -66 861 -66 2087 -98 557 -98 719 -66 981 -98 563 -100 199 -100 523 -100 2319 -134 833 -100 495 -132 197 -66 295 -64 989 -66 1059 -198 7343 -66 2023 -66 963 -66 593 -66 2401 -100 491 -100 959 -66 297 -134 999 -132 99 -68 3609 -230 97 -198 1911 -66 265 -100 1195 -132 633 -132 595 -66 1381 -66 491 -66 1681 -100 297 -100 1827 -132 2269 -100 1351 -132 1513 -66 1225 -134 231 -66 1523 -100 363 -200 1227 -66 2943 -66 923 -134 2249 -66 1809 -100 1121 -132 265 -66 827 -98 199 -66 201 -100 3279 -100 565 -132 1689 -66 395 -66 2979 -134 1065 -66 367 -168 3585 -200 463 -100 563 -66 97 -166 2293 -66 265 -134 1255 -132 2401 -66 1579 -166 365 -100 861 -298 261 -98 761 -66 363 -132 657 -130 63 -130 557 -66 131 -130 2041 -100 233 -66 1791 -100 925 -134 265 -100 1063 -100 301 -168 661 -66 657 -64 263 -64 197 -66 1853 -100 663 -98 231 -66 731 -100 5539 -166 197 -68 1423 -134 361 -68 1727 -68 929 -100 1397 -134 1885 -66 1661 -66 265 -66 1183 -66 295 -166 263 -166 165 -66 329 -66 465 -100 1159 -134 697 -100 2443 -100 393 -98 1093 -66 953 -296 787 -132 425 -66 2019 -66 461 -98 1201 -100 397 -132 3551 -100 1431 -264 725 -330 1455 -66 263 -100 531 -296 499 -100 265 -100 163 -66 1145 -132 1313 -98 2101 -98 261 -132 1083 -66 5403\nRAW_Data: -66 2223 -66 11583 -66 131 -66 5071 -66 3723 -132 1415 -132 6905 -64 9685 -102 4739 -66 3355 -66 5301 -98 29993 -508 897 -950 437 -490 909 -974 423 -510 935 -486 925 -508 943 -490 937 -494 947 -482 941 -484 979 -440 979 -456 1003 -462 975 -460 971 -458 977 -468 973 -484 975 -472 971 -450 1011 -452 1003 -446 983 -480 979 -450 1017 -908 487 -472 971 -944 471 -450 999 -944 485 -468 977 -918 493 -448 1011 -934 499 -920 487 -914 487 -452 1011 -912 521 -446 1009 -904 517 -468 1007 -16000 531 -444 1009 -908 519 -468 1009 -912 515 -466 1009 -452 1021 -466 1011 -450 1019 -480 1017 -448 1045 -448 1019 -460 1041 -450 1019 -480 1019 -450 1039 -474 1001 -480 1021 -484 1005 -476 1015 -480 1017 -484 1011 -486 1017 -464 1041 -446 1047 -922 503 -458 1037 -946 513 -442 1047 -938 503 -480 1023 -916 537 -450 1049 -926 521 -904 519 -942 519 -450 1055 -910 519 -486 1033 -916 519 -486 1029 -16258 533 -464 1015 -940 515 -456 1053 -946 511 -482 1051 -434 1075 -442 1075 -448 1065 -440 1065 -450 1049 -480 1067 -462 1041 -446 1075 -450 1063 -460 1053 -480 1047 -450 1075 -446 1079 -452 1055 -478 1051 -448 1067 -444 1065 -480 753 -66842 99 -1090 465 -332 131 -68 131 -134 99 -132 167 -200 429 -100 1809 -132 2385 -230 265 -102 597 -134 1025 -66 365 -100 361 -66 825 -168 1331 -100 797 -132 431 -132 299 -198 661 -168 501 -100 463 -164 329 -66 559 -98 391 -98 1085 -198 1939 -66 1871 -164 2251 -134 493 -66 719 -198 361 -98 361 -64 197 -132 391 -164 691 -300 489 -98 2139 -66 1413 -66 1875 -196 557 -66 263 -132 1359 -66 1397 -66 631 -100 793 -132 723 -100 65 -66 529 -134 463 -68 789 -100 227 -66 923 -100 2649 -166 363 -66 395 -200 295 -130 1757 -68 2057 -100 1023 -66 359 -66 391 -132 1679 -66 359 -66 1217 -98 663 -98 463 -100 821 -98 165 -98 1589 -132 2367 -98 559 -132 1079 -100 9617 -66 3669 -134 1787 -68 1679 -132 361 -66 555 -100 661 -66 1523 -100 2057 -198 1025 -66 4177 -100 165 -66 265 -132 465 -134 299 -232 265 -100 1125 -132 1461 -132 1295 -100 499 -132 367 -68 263 -66 331 -66 365 -100 1643 -130 197 -132 997 -98 867 -98 1191 -100 2945 -100 2339 -98 1779 -66 295 -132 597 -66 165 -100 665 -100 463 -66 331 -66 593 -100 459 -68 489 -164 855 -66 261 -64 163 -100 4449 -100 859 -100 699 -132 199 -100 1685 -66 301 -132 2317 -68 231 -100 827 -66 1749 -132 99 -64 1185 -100 329 -100 1253 -66 1127 -98 827 -198 363 -132 265 -134 365 -66 297 -66 1125 -66 261\nRAW_Data: -266 29863 -66 2443 -66 5113 -100 5947 -21026 99 -134 301 -132 199 -132 131 -266 163 -196 131 -66 365 -66 465 -98 13819 -98 525 -98 329 -100 893 -66 1259 -66 431 -98 427 -130 1051 -392 463 -200 795 -164 399 -66 1489 -66 1377 -100 1423 -132 597 -100 689 -68 1559 -100 2263 -100 1327 -98 1059 -98 497 -66 595 -132 265 -66 299 -66 199 -66 563 -134 627 -66 165 -134 889 -66 2751 -232 893 -264 231 -66 299 -132 467 -132 861 -68 1263 -164 795 -66 2601 -100 429 -66 1525 -66 961 -98 265 -98 997 -66 233 -68 695 -100 697 -66 795 -66 1195 -66 1223 -68 2173 -66 467 -66 827 -66 535 -68 697 -100 1221 -166 165 -100 365 -132 723 -66 829 -132 2091 -232 265 -66 195 -66 459 -262 499 -100 461 -68 759 -100 1087 -66 259 -164 2845 -66 1365 -98 561 -200 331 -168 201 -166 1397 -198 197 -66 697 -68 1713 -68 293 -134 1317 -66 593 -328 395 -100 499 -132 2251 -100 563 -134 333 -134 1921 -134 1187 -68 561 -132 933 -66 797 -100 631 -100 399 -132 929 -66 2769 -66 851 -130 2047 -66 265 -100 7219 -66 1987 -66 299 -98 2199 -134 1063 -98 2843 -98 655 -132 231 -66 1123 -198 2137 -64 327 -66 3183 -66 1127 -66 631 -100 263 -102 3173 -132 267 -68 1289 -98 1593 -66 2415 -66 1185 -66 359 -132 1051 -66 2169 -66 427 -98 395 -132 793 -98 293 -166 727 -134 131 -100 1287 -98 427 -98 687 -164 823 -64 853 -66 865 -100 763 -66 2025 -100 959 -66 1891 -64 793 -100 763 -66 729 -166 99 -98 399 -134 763 -100 4203 -66 1321 -230 4023 -98 1053 -66 985 -98 1383 -66 3559 -164 1515 -100 2899 -66 797 -134 1169 -100 3055 -134 1615 -66 429 -100 495 -64 1583 -134 923 -66 921 -66 723 -68 1359 -98 787 -98 425 -100 393 -64 1189 -98 263 -98 491 -100 1455 -98\nRAW_Data: -202 531 -66 531 -66 1093 -66 1389 -66 1551 -134 2699 -66 1291 -132 65 -64 657 -98 1083 -164 393 -98 1359 -134 1461 -66 393 -100 561 -130 2113 -132 597 -66 431 -102 1759 -302 985 -66 235 -100 1395 -66 901 -66 1061 -100 463 -66 5673 -66 227 -66 225 -66 855 -66 1581 -132 2503 -100 657 -66 2535 -98 259 -64 1015 -66 231 -132 1197 -66 827 -166 9641 -66 1823 -132 1565 -132 299 -66 797 -66 1631 -132 327 -132 2227 -232 433 -68 499 -100 1793 -66 1161 -132 525 -66 129 -100 361 -66 1765 -132 229 -66 491 -132 2255 -100 3043 -332 299 -100 499 -100 267 -68 2967 -66 991 -100 729 -100 633 -66 529 -98 825 -100 1033 -100 331 -66 723 -100 725 -264 2987 -68 825 -66 2601 -134 333 -100 3181 -134 1059 -100 299 -134 3279 -100 1221 -132 659 -66 3157 -98 1595 -132 1561 -98 201 -134 465 -66 1843 -130 589 -66 1413 -66 331 -100 333 -66 661 -100 265 -68 201 -234 1027 -166 297 -100 1161 -132 1561 -134 629 -66 431 -66 1025 -98 427 -198 1527 -66 793 -66 1903 -66 131 -130 1285 -66 299 -134 397 -98 229 -132 499 -132 4747 -100 2355 -100 263 -132 1915 -132 1749 -132 759 -66 2253 -100 4545 -66 391 -100 521 -100 1083 -100 929 -134 565 -66 2355 -66 1331 -66 167 -100 465 -100 1727 -132 633 -330 433 -66 897 -132 165 -134 331 -98 627 -66 231 -66 167 -66 1397 -66 729 -132 1397 -68 165 -66 1627 -134 2187 -66 231 -134 795 -200 6469 -232 829 -66 3929 -66 891 -98 1977 -100 525 -68 859 -66 921 -264 1029 -68 959 -134 1555 -66 259 -100 687 -66 429 -264 663 -66 1559 -100 1127 -100 2327 -132 1913 -64 4193 -132 293 -98 99 -100 5613 -132 1351 -66 1545 -66 1677 -66 295 -64 1943 -100 595 -132 1959 -166 765 -66 1389 -100 823 -66 1749 -66 1217 -100 597 -100 297 -66 2019 -98 165 -100 4165 -100 67 -100 2477 -262 295 -66 919 -200 3555 -66 229 -66 2531 -98 557 -66 2525 -66 1463 -100 1293 -68 197 -68 1391 -66 1421 -66 595 -164 327 -68 2285 -66 593 -98 99 -68 463 -98 1063 -100 165 -68 99 -100 631 -66 1085 -66 859 -98 6599 -66 1429 -66 233 -66 397 -98 231 -132 1975 -132 333 -66 131 -134 3373 -100 4277 -66 1363 -232 2893 -166 3133 -64 951 -66 2815 -100 425 -98 327 -66 599 -68 1031 -98 133 -68 633 -68 429 -100 1129 -66 327 -130 2679 -66 1321 -100 463 -200 367 -98 667 -66 493 -132 885 -98 2183 -166 559 -98 981 -66 3201 -164 593 -66 493 -130 1923 -166 565 -100 2421 -98 461 -66 1427 -130 1955 -64 197 -66 1643\nRAW_Data: -132 2291 -66 3057 -68 2521 -166 333 -134 503 -400 3235 -66 2329 -68 995 -100 333 -100 97 -166 1757 -100 397 -100 165 -66 2755 -132 297 -134 163 -100 565 -100 1793 -100 1813 -162 1293 -98 97 -66 999 -66 1763 -68 261 -68 2391 -100 765 -364 859 -100 1855 -98 1399 -230 463 -134 301 -198 397 -100 961 -68 431 -134 695 -202 133 -100 365 -66 925 -98 165 -66 365 -132 663 -98 4573 -134 1479 -66 1019 -66 629 -66 233 -68 201 -66 569 -66 295 -134 1755 -296 3199 -100 3261 -168 3373 -132 1425 -100 759 -66 895 -98 201 -100 265 -166 99 -66 695 -66 1091 -66 855 -168 299 -100 229 -164 589 -66 521 -66 655 -134 329 -98 493 -200 429 -66 929 -66 673 -100 953 -66 823 -66 1283 -66 1979 -68 233 -66 1547 -164 589 -132 597 -66 131 -66 265 -100 761 -200 759 -66 689 -332 263 -100 1227 -68 1067 -164 2945 -100 959 -100 995 -100 399 -100 1193 -100 625 -66 399 -66 3021 -134 393 -66 4805 -66 1095 -68 231 -332 399 -166 1663 -68 561 -66 927 -98 1085 -164 1155 -98 627 -66 265 -132 263 -130 2211 -66 2159 -66 1029 -264 2669 -66 295 -66 8747 -100 329 -232 625 -134 429 -68 1329 -168 1355 -98 987 -66 1545 -98 1015 -98 699 -134 133 -134 1263 -66 4687 -166 8299 -66 1349 -434 933 -906 443 -452 949 -894 441 -480 909 -486 907 -456 947 -448 949 -434 969 -454 931 -460 941 -448 933 -450 979 -450 945 -450 935 -458 935 -486 927 -456 947 -450 951 -482 945 -428 975 -446 967 -452 955 -458 945 -912 485 -434 971 -902 481 -450 949 -926 451 -478 941 -920 481 -450 949 -920 473 -450 955 -916 471 -452 981 -918 449 -486 945 -910 483 -924 473 -15780 479 -450 967 -948 449 -482 943 -944 485 -468 941 -484 979 -446 1001 -444 999 -446 967 -484 969 -482 979 -478 947 -484 985 -470 973 -458 983 -492 971 -458 979 -494 971 -458 983 -494 973 -458 981 -498 975 -458 1013 -466 973 -952 475 -458 973 -950 489 -450 1011 -916 481 -478 1001 -918 481 -478 1001 -916 481 -478 1001 -920 483 -480 983 -946 479 -458 1013 -932 485 -952 479 -16040 493 -476 1009 -912 515 -464 1007 -910 517 -464 1007 -450 1001 -488 1001 -484 1011 -450 1019 -458 1039 -450 1031 -454 1005 -484 1009 -458 1015 -476 1009 -478 1035 -462 1015 -468 1007 -480 1001 -486 1015 -460 1041 -450 1017 -484 1019 -482 1009 -952 477 -494 1003 -944 485 -478 1013 -944 519 -482 1013 -942 487 -482 1013 -946 487 -484 1015 -944 487 -484 1015 -946 519 -454 1019 -942 505 -952 487 -16238 517 -468 1007 -942 521 -482 1011 -944 519\nRAW_Data: -450 1019 -482 1035 -480 1033 -460 1047 -476 1017 -484 1007 -484 1051 -484 1027 -452 1039 -478 1035 -458 1049 -480 1017 -480 1035 -480 1035 -472 1047 -484 1027 -454 1039 -480 1033 -488 1031 -488 1009 -962 521 -486 1017 -966 485 -490 1015 -139210 229 -98 461 -364 165 -334 131 -168 2121 -66 1049 -66 1215 -166 297 -136 1449 -100 3877 -100 1495 -234 331 -64 1345 -262 393 -100 529 -132 2921 -164 1223 -132 1807 -66 765 -66 397 -98 3405 -132 2123 -230 231 -66 2541 -100 2489 -98 4397 -132 461 -98 293 -64 991 -66 1125 -166 401 -100 131 -100 99 -100 265 -100 2555 -100 499 -98 1361 -134 265 -166 895 -100 2253 -100 1057 -100 129 -296 1147 -198 197 -66 1163 -66 1935 -98 1675 -66 1103 -100 891 -100 989 -164 1019 -66 2967 -68 1293 -166 3161 -66 133 -264 1065 -100 731 -66 1693 -66 529 -100 165 -68 865 -66 825 -232 1117 -196 2401 -66 3051 -296 229 -132 1843 -132 1687 -68 1119 -68 299 -68 97 -66 4741 -66 197 -200 2319 -100 1097 -66 3765 -66 131 -100 695 -132 2753 -66 2287 -100 1129 -68 331 -98 1433 -132 893 -100 465 -100 801 -66 529 -66 1515 -264 393 -98 263 -66 1831 -166 3533 -100 633 -100 1051 -100 331 -98 795 -134 959 -132 1229 -100 627 -132 2517 -66 165 -98 131 -66 2301 -166 163 -134 465 -66 2767 -66 1019 -66 401 -134 397 -232 893 -66 397 -66 833 -66 199 -66 303 -66 2775 -66 2069 -98 1841 -100 399 -66 793 -98 2793 -68 3769 -100 867 -66 861 -100 399 -66 1859 -100 631 -132 755 -100 689 -66 163 -64 2045 -64 2191 -102 1127 -68 727 -68 625 -164 1381 -66 1153 -132 1115 -98 1017 -100 491 -100 593 -132 991 -98 1415 -98 4813 -66 331 -98 131 -102 1847 -98 197 -68 263 -100 3265 -66 431 -100 493 -98 435 -134 133 -68 1185 -134 395 -100 131 -66 399 -134 767 -134 1125 -66 429 -198 3185 -100 2261 -66 523 -230 2475 -168 1297 -66 3243 -66 1853 -100 1657 -66 459 -66 827 -100 263 -66 303 -234 197 -166 1167 -100 2299 -66 1329 -68 461 -100 763 -132 3819 -366 757 -66 591 -164 621 -98 1445 -100 2155 -100 231 -100 631 -68 1161 -66 131 -166 67 -98 1915 -166 1891 -66 1261 -68 999 -164 165 -132 133 -168 2695 -68 1055 -198 97 -98 229 -66 229 -66 1215 -66 885 -100 303 -132 297 -164 619 -198 459 -64 989 -66 229 -66 597 -134 693 -64 1255 -100 65 -132 331 -66 199 -98 529 -100 2831 -98 1259 -66 4855 -100 1163 -166 299 -66 395 -98 3141 -66 1319 -66 2139 -100 161 -132 261 -130 821 -200 263 -134 931 -330 65 -98 99 -134 793\nRAW_Data: -66 597 -100 231 -68 167 -66 1659 -100 733 -66 1631 -100 165 -66 199 -66 233 -166 165 -100 1925 -68 595 -198 1785 -134 2177 -134 131 -66 1049 -98 3087 -132 195 -64 589 -66 397 -134 329 -66 2565 -164 327 -100 689 -64 1775 -100 5183 -132 1187 -66 329 -66 395 -132 165 -98 261 -98 1247 -64 1217 -66 927 -66 997 -66 199 -98 1419 -66 531 -166 1231 -66 697 -100 97 -66 563 -66 161 -264 3205 -200 525 -98 293 -100 291 -100 133 -66 759 -66 659 -100 983 -64 523 -130 431 -166 919 -66 1097 -100 1757 -66 1119 -66 917 -98 2647 -166 1247 -66 165 -264 1189 -100 899 -134 597 -68 2323 -66 1893 -66 1095 -100 533 -64 965 -100 1817 -130 1215 -66 1879 -64 821 -164 1117 -132 263 -132 131 -66 557 -66 431 -132 661 -100 1183 -98 629 -100 1679 -132 259 -66 623 -98 431 -66 399 -164 923 -100 297 -66 165 -166 2521 -198 99 -66 431 -132 1225 -66 1063 -68 131 -136 631 -66 163 -100 99 -298 965 -68 465 -68 465 -298 2545 -134 2639 -230 1489 -66 299 -66 1991 -234 65 -132 693 -134 429 -102 101 -68 461 -66 3333 -64 1229 -68 333 -66 265 -66 885 -64 3163 -100 467 -66 2651 -164 1221 -100 1527 -66 1259 -134 431 -232 1259 -100 6029 -164 297 -98 1151 -66 1415 -100 5289 -66 2467 -100 493 -132 495 -200 1121 -66 129 -66 757 -166 327 -130 5477 -66 1227 -230 395 -100 265 -132 497 -132 1133 -132 361 -100 1051 -164 3089 -132 1583 -100 65 -68 2315 -100 529 -132 2157 -68 1257 -66 1975 -98 427 -98 1347 -66 719 -164 857 -66 165 -66 1029 -132 297 -132 467 -100 731 -130 1985 -98 199 -166 899 -100 1391 -166 3425 -100 261 -132 721 -66 4845 -98 1193 -68 1225 -66 721 -100 1015 -64 983 -66 557 -130 693 -98 99 -64 1091 -98 197 -100 2321 -66 431 -134 727 -66 467 -102 891 -98 167 -134 2619 -66 393 -64 97 -100 589 -98 1583 -164 301 -68 1481 -98 295 -98 959 -66 365 -98 1253 -66 231 -100 1255 -132 1813 -132 1645 -100 361 -132 395 -100 427 -164 1197 -98 1001 -100 861 -66 1161 -98 195 -100 197 -66 1429 -66 663 -66 1427 -98 665 -66 699 -100 663 -66 855 -196 161 -100 361 -98 823 -66 227 -66 621 -132 1853 -230 461 -230 623 -100 557 -98 229 -98 133 -134 1291 -66 533 -166 627 -134 195 -134 593 -64 591 -66 1019 -66 1049 -262 297 -100 2921 -66 133 -66 963 -134 165 -100\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Honeywell\nBit: 48\nKey: 00 00 0E DB 70 20 00 01\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 414 -1374 51 -108 79 -216 189 -584 53 -214 53 -400 215 -264 129 -322 77 -288 79 -54 107 -184 51 -446 53 -160 161 -305 263 -104 264 -134 53 -806 267 -80 187 -314 53 -158 83 -1172 243 -833 79 -188 345 -208 77 -182 225 -100 107 -106 187 -190 105 -432 176 -174 119 -328 79 -432 79 -102 99 -514 127 -218 79 -184 77 -52 101 -356 53 -615 243 -266 181 -102 212 -138 81 -486 205 -574 77 -256 155 -152 217 -78 75 -1010 393 -346 79 -488 107 -284 121 -72 215 -174 183 -54 257 -332 179 -52 79 -238 53 -636 161 -80 227 -950 346 -160 109 -564 295 -160 299 -160 435 -487 312 -52 105 -292 77 -246 137 -498 366 -104 255 -362 97 -210 53 -555 374 -206 129 -96 303 -280 179 -172 75 -102 99 -76 353 -228 131 -252 147 -130 79 -304 103 -82 139 -52 103 -183 77 -284 75 -454 55 -188 77 -222 161 -128 107 -136 187 -270 53 -371 184 -364 103 -284 129 -52 103 -321 81 -554 261 -1048 107 -106 243 -280 103 -478 215 -530 53 -108 53 -236 203 -180 51 -78 77 -338 81 -82 53 -231 75 -124 256 -232 227 -448 131 -340 131 -266 107 -346 51 -254 75 -134 210 -182 103 -280 127 -122 305 -310 255 -528 77 -513 79 -214 209 -102 53 -80 133 -1727 237 -78 79 -242 53 -296 133 -532 53 -513 53 -54 131 -190 53 -661 129 -218 107 -394 103 -554 157 -112 343 -314 103 -283 79 -304 135 -56 111 -272 189 -370 271 -270 105 -956 79 -184 77 -868 307 -156 129 -731 51 -200 161 -84 81 -326 51 -54 157 -168 369 -152 101 -188 81 -398 239 -132 215 -54 105 -182 317 -206 99 -198 97 -274 157 -271 121 -268 200 -330 499 -184 55 -434 133 -646 283 -152 255 -428 101 -350 199 -124 145 -304 308 -102 75 -386 107 -186 129 -534 101 -180 175 -100 151 -98 53 -78 133 -794 183 -150 83 -220 185 -280 83 -537 107 -308 55 -138 79 -714 79 -538 77 -106 133 -242 263 -236 75 -884 215 -136 81 -270 133 -624 105 -132 79 -848 291 -373 79 -244 55 -619 203 -202 77 -156 103 -176 99 -408 107 -318 77 -316 79 -500 55 -691 51 -340 129 -266 53 -486 103 -376 103 -185 51 -156 81 -106 77 -104 79 -502 208 -292 133 -432 105 -52 105 -80 51 -158 317 -372 181 -844 51 -270 107 -312 79 -302 83 -444 53 -640 77 -268 183 -138 85 -192 79 -158 131 -132 155 -220 81 -245 127 -386 185 -296 53 -608 77 -308 51 -822 105 -832 51 -850 155 -242 105 -422 79 -270 155 -76 77 -162 55 -414\nRAW_Data: 109 -630 77 -190 53 -52 131 -860 53 -1300 83 -1122 99 -432 53 -724 81 -390 159 -106 81 -2607 79 -474 75 -694 151 -146 73 -104 53 -380 77 -278 77 -128 73 -148 73 -248 99 -314 75 -154 97 -2225 51 -952 183 -970 77 -1210 51 -326 103 -368 151 -252 121 -784 51 -108 187 -256 227 -242 105 -104 150 -128 77 -1287 173 -2513 77 -1144 105 -1268 111 -3964 101 -1348 81 -2718 81 -302 79 -1999 55 -1268 57 -7269 53 -424 83 -3736 105 -1552 51 -8460 57 -5224 101 -244 121 -4124 103 -10775 133 -1858 73 -20679 51 -5532 97 -1718 103 -3764 51 -6496 79 -792 53 -13269 81 -18787 57 -9140 83 -3302 53 -11039 55 -26162 79 -1713 103 -5860 53 -9589 55 -8389 81 -254 85 -16287 73 -8336 53 -6237 75 -828 55 -19389 73 -8028 79 -6284 83 -3460 77 -13504 75 -3198 75 -1280 51 -1948 51 -758 79 -22076 75 -1681 73 -21693 57 -8106 53 -838 51 -10074 51 -4760 55 -492 79 -6558 79 -22996 75 -13904 53 -5564 55 -16578 83 -25603 77 -410 75 -16694 81 -606 53 -2987 53 -3898 107 -3248 79 -5168 55 -754 81 -20662 53 -11066 51 -8624 79 -26384 79 -2214 81 -7442 79 -12488 53 -1656 71 -4508 55 -15680 57 -6669 51 -5410 71 -6411 53 -6082 79 -13772 53 -5945 55 -574 73 -11921 51 -3472 55 -2323 55 -10414 51 -16069 81 -2678 77 -10775 53 -2106 110 -11794 55 -17082 51 -6184 71 -8376 79 -7152 77 -2691 53 -6332 53 -2074 57 -6804 51 -166 53 -2254 53 -452 51 -6771 53 -42749 53 -6658 73 -2034 111 -3440 109 -626 53 -7291 85 -2546 53 -5287 51 -9902 53 -10040 53 -366 79 -13848 51 -3739 77 -47536 51 -354 77 -228 77 -712 53 -530 53 -162 55 -12640 71 -1708 51 -1034 77 -12891 79 -3334 51 -7644 79 -12676 81 -3036 53 -2038 53 -180 57 -818 55 -21459 71 -3009 51 -6572 53 -4015 105 -642 77 -23618 53 -4921 83 -3026 73 -3672 53 -12654 81 -8632 51 -9419 171 -19195 105 -13041 55 -21910 51 -1051 77 -10292 51 -12884 51 -6589 53 -8718 51 -2510 103 -15406 55 -6014 99 -966 73 -2725 53 -12715 51 -4228 55 -3192 57 -8672 51 -14740 51 -17032 75 -11111 1761 -182 399 -106 215 -270 355 -132 329 -150 177 -312 315 -170 169 -290 331 -162 173 -286 187 -312 163 -300 329 -164 311 -162 171 -316 289 -176 297 -194 311 -164 143 -324 167 -314 163 -310 161 -312 163 -300 175 -300 331 -162 173 -312 161 -312 163 -314 161 -298 189 -300 165 -312 161 -312 189 -276 173 -316 193 -310 163 -312 161 -310 163 -300 175 -296 193 -284 191 -284 187 -310 163 -300 175 -300 169 -320 177 -314 485 -464 177 -292 353 -134\nRAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300\nRAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312\nRAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312\nRAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292\nRAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318\nRAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150\nRAW_Data: 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -300 175 -302 193 -314 163 -310 161 -310 163 -300 175 -300 167 -318 177 -290 191 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -310 163 -300 487 -484 149 -320 335 -138 169 -322 305 -164 171 -314 305 -190 293 -168 169 -292 333 -162 171 -314 281 -184 159 -296 189 -300 165 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -310 163 -314 161 -296 189 -302 165 -312 187 -286 189 -276 173 -318 193 -280 191 -284 189 -310 163 -300 175 -300 167 -318 513 -460 167 -314 311 -152 161 -324 311 -162 169 -310 329 -160 293 -190 137 -330 329 -132 173 -316 311 -180 129 -318 189 -300 165 -312 309 -182 311 -168 141 -320 329 -164 299 -174 295 -194 141 -314 167 -334 137 -340 133 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 183 -292 189 -300 193 -284 187 -284 189 -314 471 -486 145 -318 327 -168 171 -290 331 -162 173 -312 307 -158 317 -168 169 -290 333 -162 143 -342 283 -182 159 -294 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 299 -176 293 -196 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 193 -284 187 -284 189 -302 149 -318 193 -280 191 -284 189 -284 189 -300 173 -300 169 -316 177 -316 483 -476 153 -320 329 -134 201 -286 309 -182 157 -294 355 -132 339 -136 169 -320 315 -162 171 -302 323 -148 171 -318 159 -318 147 -318 327 -168 313 -164 169 -302 321 -150 317 -166 305 -164 169 -324 169 -314 163 -310 161 -310 163 -300 175 -296 331 -164 171 -314 161 -312 163 -300 175 -298 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 189 -298 195 -284 187 -284 189 -314 159 -294 513 -470 159 -300 333 -162 171 -300 323 -150 169 -316 325 -160 311 -164 173 -312 307 -158 161 -294 329 -158 173 -314 163 -312 159 -312 335 -170 311 -164 141 -324 305 -164 323 -152 301 -194 141 -318 165 -336 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312\nRAW_Data: 165 -298 175 -304 169 -318 175 -290 191 -298 165 -314 187 -284 189 -312 159 -294 191 -296 167 -312 161 -312 159 -314 193 -270 201 -304 167 -314 163 -312 159 -312 493 -486 145 -318 325 -170 141 -316 331 -162 173 -314 305 -160 313 -170 141 -318 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -162 301 -148 321 -194 141 -314 165 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -310 163 -302 173 -296 193 -312 161 -312 159 -312 163 -300 175 -300 167 -320 177 -290 189 -298 193 -286 187 -284 189 -312 161 -294 513 -468 159 -300 333 -162 171 -302 321 -150 171 -316 323 -162 311 -164 171 -314 307 -158 159 -296 327 -160 171 -316 163 -310 161 -306 321 -168 339 -134 171 -306 321 -150 317 -166 337 -164 143 -324 167 -314 163 -312 159 -312 163 -300 175 -298 331 -162 173 -312 161 -312 163 -300 175 -300 193 -310 163 -312 161 -310 163 -312 161 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 187 -286 185 -292 191 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -146 167 -344 305 -158 315 -168 143 -318 331 -162 143 -342 283 -182 159 -294 189 -302 163 -314 307 -184 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 167 -334 137 -338 161 -312 161 -312 159 -294 329 -160 171 -316 163 -310 161 -312 163 -300 175 -302 169 -318 175 -290 191 -298 165 -312 189 -284 189 -312 159 -292 189 -298 165 -314 161 -312 189 -282 195 -268 203 -304 169 -312 163 -312 187 -278 507 -460 173 -320 303 -166 171 -294 331 -164 171 -314 305 -160 319 -168 169 -292 331 -164 171 -314 281 -182 159 -294 191 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -300 175 -302 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -292 189 -300 165 -312 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 483 -458 173 -320 303 -168 169 -294 331 -164 171 -314 307 -158 319 -168 169 -292 331 -164 171 -314 283 -182 159 -294 191 -300 165 -312 309 -182 313 -168 143 -316 331 -164 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -298 177 -304 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 163 -314 161 -310 189 -284\nRAW_Data: 185 -294 189 -296 165 -314 189 -284 189 -312 157 -294 189 -300 497 -456 189 -284 345 -154 153 -316 325 -160 173 -314 315 -150 325 -170 141 -314 331 -164 143 -314 309 -182 157 -294 189 -302 163 -314 307 -184 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -300 165 -314 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -468 193 -296 323 -150 175 -312 313 -168 171 -292 331 -164 299 -176 149 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 307 -168 143 -316 331 -164 299 -150 319 -194 141 -312 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 173 -296 169 -318 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -294 195 -284 187 -284 191 -300 173 -296 499 -482 163 -314 321 -154 153 -316 325 -160 173 -314 313 -152 301 -196 139 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 169 -316 177 -292 189 -300 165 -314 161 -310 189 -284 185 -292 189 -296 193 -286 187 -284 189 -300 175 -294 499 -482 163 -314 321 -154 153 -316 325 -160 171 -316 313 -152 325 -170 141 -314 331 -164 143 -314 311 -180 157 -294 191 -300 165 -312 309 -182 311 -168 141 -320 329 -164 299 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -310 163 -312 159 -312 163 -300 175 -298 169 -318 177 -292 189 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 187 -286 189 -312 471 -486 145 -320 325 -168 171 -290 331 -162 173 -312 307 -160 315 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -182 311 -170 141 -318 331 -164 299 -148 319 -196 139 -314 167 -334 135 -340 161 -310 163 -300 149 -320 331 -162 171 -314 161 -310 163 -314 161 -296 189 -296 165 -314 161 -310 189 -284 185 -292 189 -298 165 -312 161 -312 189 -276 173 -318 193 -278 191 -286 187 -284 189 -300 175 -298 169 -318 177 -314 483 -466 187 -294 339 -136 169 -306\nRAW_Data: 321 -150 177 -300 333 -164 311 -160 173 -314 313 -152 155 -314 327 -160 171 -316 163 -312 159 -312 307 -166 337 -138 169 -322 305 -164 299 -176 301 -194 141 -316 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -294 169 -318 177 -292 189 -300 165 -312 161 -312 189 -284 183 -292 189 -296 193 -286 187 -284 189 -302 173 -296 499 -458 187 -312 321 -154 153 -316 325 -162 171 -316 313 -152 301 -194 141 -314 331 -162 143 -316 309 -182 157 -292 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 311 -160 295 -190 163 -302 165 -304 165 -312 161 -338 163 -312 157 -294 327 -160 171 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 159 -312 189 -284 185 -290 189 -300 193 -284 189 -284 189 -312 471 -460 483 -2128 125 -52 75 -102 75 -154 101 -100 147 -272 149 -328 75 -228 511 -441 77 -212 87 -114 189 -54 263 -156 171 -393 127 -102 183 -110 293 -242 395 -320 107 -52 81 -346 53 -572 155 -381 305 -234 101 -126 228 -156 195 -102 281 -126 518 -52 313 -240 187 -54 79 -316 133 -136 213 -108 79 -80 185 -464 181 -76 379 -80 81 -506 233 -318 103 -78 53 -559 51 -240 53 -106 207 -136 163 -136 81 -218 187 -104 349 -266 51 -266 129 -80 107 -450 131 -331 127 -359 641 -108 472 -100 547 -82 79 -246 107 -560 567 -130 403 -389 149 -179 77 -526 235 -108 81 -78 51 -455 75 -106 105 -240 101 -557 183 -184 255 -102 311 -130 147 -174 457 -354 75 -442 81 -344 331 -76 51 -106 133 -54 161 -560 77 -130 77 -294 257 -52 99 -126 73 -565 51 -242 131 -497 55 -354 347 -404 81 -108 540 -286 185 -465 219 -264 296 -106 77 -294 193 -110 567 -52 287 -134 53 -106 55 -330 263 -210 83 -86 109 -134 349 -80 221 -80 207 -260 279 -76 75 -74 75 -76 99 -130 443 -78 77 -338 129 -226 179 -280 129 -228 75 -617 185 -324 423 -160 259 -673 159 -188 362 -212 205 -383 151 -316 235 -240 183 -188 79 -56 107 -80 53 -154 417 -158 211 -238 109 -80 79 -218 215 -108 105 -354 215 -112 191 -56 103 -132 77 -236 605 -152 51 -78 185 -78 127 -126 551 -156 135 -186 127 -724 131 -56 81 -378 345 -182 109 -316 107 -316 337 -128 151 -294 101 -1002 101 -52 335 -256 51 -330 81 -448 153 -158 127 -272 131 -160 105 -220 767 -132\nRAW_Data: 75 -210 131 -78 181 -243 345 -108 107 -80 105 -160 107 -218 127 -82 537 -266 239 -340 181 -76 105 -78 421 -482 53 -132 321 -134 51 -262 77 -480 103 -271 475 -154 159 -80 313 -132 79 -282 235 -126 75 -202 175 -226 151 -198 901 -210 51 -78 235 -348 79 -130 103 -584 107 -456 105 -54 431 -54 79 -106 133 -242 105 -486 155 -104 129 -228 147 -100 77 -618 357 -154 531 -104 51 -160 105 -136 109 -140 269 -366 51 -832 53 -270 135 -108 51 -186 51 -160 535 -130 77 -296 51 -182 131 -106 341 -56 135 -194 530 -294 181 -562 75 -179 127 -364 207 -74 125 -332 149 -76 129 -102 103 -102 51 -128 173 -122 482 -474 229 -200 97 -790 105 -104 79 -104 341 -262 101 -110 111 -82 109 -433 271 -210 51 -346 245 -54 209 -284 157 -106 293 -234 77 -826 157 -52 127 -228 299 -490 159 -140 137 -164 247 -294 135 -52 131 -266 77 -128 385 -158 81 -276 356 -476 53 -170 55 -463 79 -190 53 -808 51 -80 159 -106 161 -162 83 -225 187 -341 101 -520 159 -54 428 -182 341 -108 79 -274 77 -108 431 -392 77 -132 131 -138 83 -58 135 -274 305 -300 83 -398 79 -534 131 -528 263 -52 261 -264 437 -342 77 -102 389 -332 103 -130 131 -322 105 -106 53 -134 183 -188 79 -82 81 -104 53 -316 269 -56 347 -286 341 -270 161 -52 131 -136 223 -110 341 -318 469 -138 143 -110 83 -80 53 -54 183 -346 385 -906 51 -382 75 -154 51 -52 207 -186 53 -78 51 -106 293 -52 155 -126 306 -130 412 -416 187 -82 339 -160 135 -194 163 -136 81 -78 281 -426 229 -76 227 -532 177 -528 51 -244 269 -616 75 -52 418 -136 53 -82 51 -154 97 -126 233 -52 51 -740 77 -162 81 -304 263 -162 217 -448 275 -164 185 -138 139 -54 277 -348 531 -642 376 -274 267 -182 75 -126 103 -615 103 -236 81 -380 77 -52 241 -80 105 -438 111 -192 181 -54 135 -186 113 -58 243 -140 57 -385 189 -158 133 -675 231 -80 241 -236 131 -106 109 -108 55 -136 555 -132 303 -494 341 -316 135 -112 302 -366 97 -98 101 -310 177 -78 427 -297 151 -579 163 -314 129 -78 51 -233 105 -210 103 -202 217 -180 121 -229 125 -302 221 -52 235 -158 187 -110 135 -162 51 -108 105 -398 221 -82 297 -110 53 -1400 213 -106 51 -454 723 -368 79 -104 79 -134 81 -323 51 -372 79 -80 235 -132 549 -164 81 -104 129 -602 371 -160 129 -536 179 -234 133 -210 236 -489 159 -52 237 -210 129 -264 53 -242 105 -106 53 -114\nRAW_Data: 193 -290 191 -436 343 -311 207 -322 53 -400 51 -882 135 -678 375 -244 317 -192 380 -558 260 -154 710 -80 237 -218 109 -250 81 -372 107 -160 646 -286 125 -178 269 -726 51 -106 79 -298 523 -682 159 -498 265 -465 127 -592 177 -100 149 -687 103 -78 357 -102 53 -158 105 -240 79 -618 105 -130 79 -478 77 -154 450 -186 53 -192 137 -1038 105 -160 266 -136 51 -345 53 -80 157 -108 235 -102 483 -54 109 -234 327 -376 51 -462 366 -289 163 -108 75 -262 261 -134 81 -78 105 -136 81 -378 105 -272 139 -106 209 -80 205 -184 101 -76 101 -180 229 -427 99 -430 125 -100 103 -469 125 -102 221 -130 209 -106 105 -104 201 -274 332 -232 105 -166 161 -104 275 -80 131 -1000 321 -156 189 -166 81 -130 367 -262 153 -258 75 -260 127 -542 127 -464 107 -80 107 -108 303 -240 77 -238 181 -282 77 -76 224 -104 75 -74 225 -554 131 -56 55 -164 51 -318 247 -130 159 -158 77 -76 285 -354 133 -52 135 -420 155 -256 129 -78 265 -831 103 -231 107 -674 285 -653 337 -102 257 -182 101 -146 147 -303 75 -206 365 -268 157 -54 81 -504 133 -472 107 -82 161 -80 133 -461 81 -825 79 -798 101 -206 207 -80 185 -980 209 -56 247 -246 51 -106 131 -78 79 -236 53 -106 51 -98 101 -206 127 -78 151 -98 279 -155 157 -468 131 -72 173 -372 329 -212 79 -52 101 -375 77 -286 51 -188 185 -308 191 -56 555 -320 179 -86 193 -490 105 -214 81 -138 53 -216 211 -112 57 -134 213 -212 131 -714 73 -128 155 -590 51 -452 189 -261 247 -162 105 -404 167 -56 77 -354 81 -56 215 -316 129 -372 366 -288 133 -56 295 -294 51 -822 53 -482 295 -340 321 -188 459 -631 147 -212 201 -100 173 -258 305 -261 51 -208 97 -124 103 -598 159 -206 77 -710 213 -190 241 -158 133 -186 385 -266 219 -350 133 -52 157 -78 51 -156 403 -162 189 -300 259 -202 103 -760 179 -258 173 -82 431 -164 161 -373 133 -208 191 -82 237 -52 135 -384 131 -192 55 -52 285 -52 79 -274 57 -140 109 -306 53 -132 133 -240 53 -104 51 -214 79 -158 123 -98 99 -126 77 -178 173 -334 178 -518 129 -236 337 -206 230 -100 225 -52 152 -102 103 -308 51 -52 203 -114 139 -318 211 -396 183 -78 51 -106 77 -164 83 -380 133 -234 237 -54 79 -158 83 -84 137 -136 323 -158 365 -128 157 -108 81 -194 109 -618 155 -363 191 -190 109 -110 55 -80 163 -224 79 -82 105 -270 133 -314 159 -106 153 -356 77 -76 75 -458 149 -74\nRAW_Data: 101 -128 53 -130 153 -212 447 -230 171 -636 75 -98 324 -362 51 -160 217 -218 51 -310 101 -152 379 -280 151 -176 203 -210 51 -130 336 -232 179 -596 629 -298 55 -1302 261 -108 109 -80 77 -162 237 -52 125 -52 75 -100 125 -148 297 -426 107 -110 133 -238 155 -228 123 -100 247 -74 179 -126 99 -148 99 -134 81 -194 81 -248 53 -164 135 -82 109 -717 53 -220 81 -234 79 -344 53 -474 79 -154 51 -618 109 -142 83 -593 135 -134 105 -136 81 -243 413 -228 121 -174 125 -328 53 -658 361 -74 401 -280 131 -132 85 -56 139 -298 157 -481 51 -52 129 -76 75 -152 127 -507 321 -486 185 -264 103 -54 325 -76 183 -104 53 -246 81 -78 185 -210 155 -112 55 -80 77 -720 103 -54 51 -450 51 -104 107 -246 185 -78 267 -482 221 -580 113 -222 365 -80 135 -138 199 -296 261 -164 107 -80 131 -134 107 -272 323 -1120 51 -476 81 -106 211 -132 107 -352 183 -134 133 -108 215 -220 55 -666 131 -218 79 -82 253 -56 103 -52 235 -106 103 -134 185 -532 111 -188 361 -399 183 -693 238 -104 567 -106 113 -280 107 -52 287 -106 161 -908 103 -376 339 -180 73 -200 101 -330 209 -54 107 -220 315 -132 216 -138 159 -438 135 -190 73 -130 510 -244 53 -294 81 -84 171 -138 53 -460 515 -82 219 -315 291 -54 107 -270 107 -54 53 -160 79 -374 209 -154 97 -74 233 -454 125 -144 119 -96 99 -542 75 -1200 103 -204 99 -172 95 -98 127 -78 75 -102 103 -320 241 -820 77 -78 105 -326 135 -264 53 -216 133 -517 211 -228 103 -52 257 -455 127 -74 77 -78 235 -436 151 -102 75 -152 75 -76 75 -303 51 -254 177 -284 103 -152 207 -156 79 -302 53 -610 79 -214 133 -80 243 -110 107 -108 185 -286 155 -212 187 -384 75 -128 77 -80 129 -130 99 -76 156 -104 185 -82 81 -242 217 -380 81 -294 157 -182 161 -134 131 -104 77 -52 181 -150 75 -98 147 -230 129 -760 161 -273 137 -84 111 -674 53 -80 127 -317 265 -210 79 -242 297 -238 75 -328 105 -244 345 -342 131 -254 151 -378 103 -335 101 -288 75 -154 275 -498 75 -224 372 -80 157 -190 395 -268 81 -52 105 -160 277 -510 107 -262 105 -192 109 -188 81 -586 105 -78 123 -98 201 -182 55 -614 101 -170 175 -285 79 -222 191 -658 161 -194 135 -532 107 -810 77 -134 241 -128 147 -150 276 -200 97 -230 157 -104 171 -282 77 -212 77 -80 406 -714 259 -182 131 -128 161 -112 81 -380 79 -290 135 -777 345 -370 385 -178 51 -364\nRAW_Data: 215 -189 53 -108 83 -320 209 -530 51 -265 51 -54 109 -242 351 -362 51 -102 51 -156 173 -247 255 -100 99 -176 99 -52 224 -262 53 -80 279 -356 51 -76 379 -176 75 -220 77 -52 129 -208 129 -304 101 -378 157 -52 53 -102 179 -228 255 -311 131 -192 235 -52 51 -126 273 -721 53 -294 161 -170 55 -376 81 -268 131 -98 129 -268 81 -158 107 -80 161 -214 129 -110 501 -220 185 -688 79 -132 325 -212 53 -295 105 -104 51 -106 79 -372 153 -106 137 -84 193 -741 51 -561 51 -402 390 -126 149 -250 327 -338 177 -202 177 -931 105 -271 423 -768 53 -430 159 -110 217 -104 161 -460 249 -82 79 -186 79 -104 53 -132 79 -218 55 -298 53 -214 81 -354 51 -108 107 -196 273 -80 79 -104 153 -100 175 -76 131 -220 81 -56 131 -368 217 -116 137 -82 169 -342 209 -106 319 -396 75 -104 253 -130 149 -430 127 -130 81 -196 55 -404 105 -726 181 -224 199 -106 79 -220 159 -180 75 -126 201 -128 75 -833 127 -128 125 -230 249 -184 51 -218 79 -240 163 -222 135 -82 103 -80 159 -765 107 -276 237 -438 207 -162 133 -128 135 -316 105 -312 137 -486 181 -158 77 -769 183 -764 533 -186 77 -100 173 -128 202 -174 174 -102 185 -52 51 -128 177 -358 101 -96 145 -126 75 -184 135 -558 51 -186 211 -241 237 -154 203 -508 103 -562 171 -320 99 -148 127 -212 53 -472 77 -128 147 -100 105 -136 111 -322 206 -238 79 -162 51 -559 109 -198 113 -461 53 -849 159 -230 51 -306 121 -316 105 -78 105 -182 153 -234 131 -226 83 -800 105 -104 215 -52 185 -572 235 -660 97 -922 53 -517 53 -80 133 -302 53 -134 345 -78 111 -374 133 -80 157 -106 185 -184 373 -110 55 -292 129 -140 247 -240 347 -130 73 -227 75 -648 153 -128 75 -1002 155 -335 169 -404 101 -76 175 -126 103 -373 79 -402 217 -218 101 -171 301 -158 204 -76 123 -100 51 -148 251 -334 77 -211 103 -304 359 -78 105 -52 99 -560 101 -288 303 -436 131 -234 179 -252 131 -656 51 -260 129 -160 79 -106 51 -184 203 -152 103 -234 155 -200 235 -52 133 -164 324 -106 107 -82 107 -348 313 -188 79 -372 79 -549 185 -82 79 -246 265 -720 107 -788 77 -585 79 -128 75 -130 203 -76 75 -206 228 -234 79 -180 101 -532 210 -282 229 -412 103 -140 55 -54 77 -316 151 -356 51 -102 75 -272 51 -506 51 -744 329 -340 129 -104 135 -300 167 -1318 107 -186 51 -136 107 -603 107 -338 105 -492 131 -250 107 -80 109 -170 79 -162\nRAW_Data: 101 -102 109 -306 181 -330 209 -76 101 -286 79 -126 51 -156 103 -78 357 -248 77 -260 51 -76 99 -320 323 -76 99 -124 199 -632 105 -220 290 -260 125 -234 75 -272 103 -252 423 -623 81 -80 105 -264 77 -1040 51 -106 107 -1561 81 -732 107 -82 167 -327 159 -626 53 -162 53 -162 107 -238 235 -106 163 -108 105 -104 105 -136 133 -306 51 -430 129 -152 109 -86 111 -56 81 -616 161 -130 51 -514 107 -236 208 -110 193 -638 133 -82 109 -543 216 -457 111 -84 169 -1168 201 -52 75 -78 289 -284 103 -874 105 -166 81 -244 129 -54 214 -398 81 -56 137 -410 81 -268 159 -372 103 -106 83 -814 207 -230 177 -280 75 -966 51 -823 77 -102 105 -394 139 -112 81 -1292 75 -727 183 -734 51 -590 83 -242 77 -1674 105 -562 107 -484 103 -104 107 -2114 77 -2326 51 -1052 207 -594 129 -232 177 -895 51 -506 203 -671 75 -206 101 -328 77 -104 105 -132 77 -370 186 -82 79 -846 77 -1272 53 -1609 177 -406 51 -226 121 -330 51 -280 73 -3094 75 -802 236 -3347 53 -2218 51 -1176 107 -212 75 -1460 51 -714 81 -1643 105 -4782 55 -5096 51 -7167 53 -17412 53 -6530 51 -20019 51 -12370 71 -442 103 -10796 55 -1320 105 -660 51 -1854 135 -16801 77 -5863 87 -12342 119 -24656 81 -7111 101 -15641 79 -3617 75 -10578 127 -5573 77 -16152 53 -5624 51 -13975 71 -7720 79 -3760 51 -9739 77 -1892 73 -2638 153 -4570 51 -5265 53 -3429 79 -1982 53 -2154 51 -7158 53 -12468 161 -12934 79 -14244 77 -3871 105 -2482 79 -4568 99 -218 79 -17125 53 -2368 53 -10101 55 -15958 81 -2358 109 -3584 51 -6886 85 -6834 51 -4154 53 -218 53 -826 55 -7583 51 -11429 216 -1346 71 -1856 79 -982 55 -2072 51 -4033 105 -7186 79 -1506 81 -8082 53 -6530 83 -6380 109 -192 135 -6058 51 -3274 79 -6105 57 -18103 53 -1142 55 -4687 57 -12931 99 -344 53 -9300 79 -7717 75 -3774 81 -3691 105 -2780 51 -2764 75 -1030 79 -29043 79 -3611 53 -1934 79 -4819 75 -6538 53 -7104 53 -3542 55 -1969 53 -2426 105 -7239 81 -516 79 -14563 123 -2246 53 -3012 71 -900 55 -1196 51 -7049 51 -1790 53 -10358 53 -3988 81 -1214 107\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/hormann_hsm_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1718, -32700, 32700, -32700, 11939, -494, 1047, -496, 1015, -516, 1041, -476, 1037, -510, 1035, -512, 1035, -484, 1047, -492, 1031, -504, 513, -1042, 499, -1020, 1041, -512, 511, -1004, 1035, -512, 507, -1042, 481, -1050, 1017, -522, 489, -1040, 1013, -514, 513, -1010, 543, -1008, 513, -1012, 1045, -522, 1003, -512, 1029, -518, 1009, -514, 1039, -476, 541, -1004, 509, -1038, 1035, -482, 539, -1008, 1033, -490, 539, -1018, 1009, -508, 1059, -482, 1041, -512, 1005, -508, 541, -1006, 513, -1008, 535, -1000, 1035, -522, 525, -1016, 515, -1006, 1021, -514, 1045, -478, 12441, -482, 1045, -512, 1005, -512, 1035, -514, 1037, -482, 1035, -520, 1031, -504, 1035, -486, 1041, -510, 501, -1018, 513, -1042, 1007, -512, 509, -1042, 1007, -506, 541, -1010, 507, -1016, 1031, -520, 513, -1010, 1021, -516, 515, -1046, 477, -1042, 513, -1012, 1051, -484, 1043, -512, 1027, -484, 1043, -514, 1007, -512, 509, -1040, 507, -1036, 1011, -526, 493, -1032, 1033, -486, 509, -1056, 1011, -514, 1007, -512, 1037, -514, 1001, -516, 507, -1034, 517, -1036, 493, -1018, 1041, -502, 525, -1016, 513, -1040, 1005, -512, 1033, -506, 12423, -478, 1039, -514, 1009, -516, 1033, -522, 1001, -522, 1039, -510, 1027, -484, 1041, -512, 1007, -510, 509, -1038, 507, -1034, 1037, -482, 507, -1042, 1037, -520, 487, -1014, 527, -1022, 1007, -512, 509, -1038, 1037, -512, 509, -1008, 507, -1044, 507, -1018, 1027, -514, 1005, -528, 1017, -514, 1041, -512, 1003, -512, 509, -1042, 507, -1010, 1035, -520, 507, -1018, 1033, -508, 511, -1038, 991, -516, 1041, -512, 1005, -512, 1035, -514, 507, -1040, 507, -1014, 519, -1014, 1041, -510, 499, -1018, 515, -1046, 1005, -514, 1011, -520, 12425, -480, 1035, -516, 1035, -492, 1031, -522, 1009, -508, 1021, -518, 1041, -512, 1007, -512, 1037, -512, 509, -1008, 519, -1016, 1029, -522, 513, -1008, 1025, -516, 515, -1044, 479, -1038, 1037, -482, 541, -1008, 1035, -520, 507, -1010, 503, -1026, 515, -1042, 1005, -512, 1037, -512, 1005, -520, 1015, -520, 1039, -510, 499, -1018, 515, -1042, 1007, -512, 507, -1040, 1011, -520, 507, -1016, 1031, -520, 1009, -540, 989, -518, 1043, -512, 511, -1006, 507, -1038, 505, -1036, 1001, -516, 509, -1050, 491, -1052, 1009, -500, 1021, -516, 12409, -524, 1015, -516, 1009, -510, 1037, -512, 1033, -506, 1039, -480, 1035, -524, 1019, -522, 1015, -500, 525, -1014, 515, -1040, 1005, -512, 509, -1042, 1011, -524, 493, -1018, 517, -1036, 1017, -514, 515, -1008, 1035, -514, 507, -1040, 505, -1034, 507, -1010, 1035, -492, 1031, -520, 1009, -540, 991, -516, 1043, -514, 511, -1010, 513, -1044, 1015, -492, 531, -1020, 1009, -528, 521, -1012, 1009, -510, 1037, -512, 1037, -482, 1033, -518, 505, -1032, 507, -1016, 537, -1000, 1025, -520, 513, -1034, 519, -1010, 1007, -512, 1039, -514, 12395, -510, 1037, -512, 1037, -482, 1045, -490, 1057, -488, 1045, -496, 1017, -516, 1041, -514, 1005, -512, 507, -1046, 481, -1044, 1035, -494, 505, -1048, 1011, -502, 527, -1018, 513, -1042, 1005, -516, 513, -1010, 1035, -520, 505, -1032, 507, -1022, 503, -1034, 1013, -504, 1023, -516, 1041, -512, 1005, -512, 1037, -514, 507, -1010, 519, -1050\nRAW_Data: 989, -520, 517, -1034, 1015, -516, 513, -1008, 1039, -512, 1035, -484, 1049, -520, 1001, -514, 503, -1018, 515, -1044, 513, -1008, 1041, -482, 541, -1012, 523, -1016, 1011, -540, 991, -516, 12415, -524, 1015, -516, 1039, -508, 1003, -512, 1033, -506, 1037, -514, 1001, -506, 1033, -520, 1033, -520, 475, -1046, 499, -1022, 1041, -512, 511, -1008, 1039, -516, 515, -1014, 489, -1050, 1011, -504, 529, -1020, 1009, -512, 511, -1040, 515, -1010, 519, -1018, 1031, -520, 1009, -538, 995, -516, 1041, -512, 1007, -514, 513, -1042, 509, -1008, 1039, -520, 477, -1042, 1029, -520, 481, -1042, 1007, -512, 1037, -512, 1035, -486, 1045, -522, 475, -1044, 501, -1024, 513, -1042, 1005, -512, 509, -1040, 509, -1008, 1035, -520, 999, -522, 12417, -520, 1007, -524, 1013, -516, 1039, -512, 1007, -516, 1007, -538, 1001, -506, 1033, -518, 1033, -506, 503, -1040, 489, -1052, 1015, -500, 525, -1016, 1007, -512, 541, -1010, 513, -1012, 1049, -520, 477, -1044, 1025, -518, 483, -1044, 513, -1008, 511, -1038, 1033, -508, 1001, -518, 1037, -490, 1031, -522, 1007, -542, 495, -1020, 515, -1012, 1039, -514, 515, -1012, 1015, -520, 513, -1042, 1025, -484, 1043, -512, 1007, -510, 1037, -514, 507, -1012, 519, -1020, 531, -1016, 1007, -530, 521, -1014, 513, -1010, 1045, -482, 1049, -492, 12413, -504, 1033, -504, 1031, -522, 1009, -542, 993, -516, 1041, -512, 1005, -514, 1035, -508, 1037, -482, 541, -1008, 509, -1046, 999, -522, 513, -1008, 1055, -482, 515, -1042, 511, -1008, 1037, -514, 507, -1010, 1035, -520, 507, -1018, 503, -1034, 503, -1050, 1013, -502, 1019, -516, 1043, -510, 1007, -512, 1035, -514, 507, -1008, 505, -1032, 1033, -506, 505, -1046, 1033, -486, 513, -1040, 1025, -518, 1011, -514, 1009, -512, 1037, -512, 509, -1010, 517, -1020, 531, -1018, 1007, -528, 521, -1012, 513, -1040, 1007, -512, 1037, -482, 12425, -512, 1035, -514, 1001, -518, 1031, -506, 1035, -490, 1033, -504, 1033, -502, 1037, -514, 1027, -520, 485, -1044, 511, -1006, 1037, -514, 507, -1042, 1005, -524, 493, -1050, 483, -1034, 1019, -516, 513, -1012, 1037, -514, 505, -1040, 481, -1036, 517, -1036, 1019, -508, 1007, -542, 993, -516, 1043, -512, 1007, -512, 507, -1036, 509, -1044, 1009, -506, 505, -1038, 1017, -520, 513, -1008, 1021, -514, 1041, -514, 1007, -512, 1037, -512, 509, -1012, 521, -1016, 521, -1010, 1033, -520, 519, -1012, 513, -1008, 1033, -508, 1037, -512, 12407, -512, 1005, -512, 1037, -512, 1033, -506, 1033, -506, 1037, -482, 1035, -522, 999, -520, 1041, -512, 497, -1020, 515, -1046, 1005, -516, 513, -1012, 1049, -486, 513, -1044, 497, -1020, 1045, -512, 479, -1038, 1039, -484, 515, -1034, 507, -1048, 507, -1014, 1007, -542, 1025, -484, 1047, -512, 1009, -512, 1035, -514, 509, -1008, 507, -1052, 1019, -522, 483, -1034, 1017, -516, 513, -1012, 1039, -512, 1035, -482, 1033, -506, 1035, -524, 493, -1030, 513, -1010, 527, -1016, 1043, -512, 511, -1010, 515, -1008, 1035, -522, 1019, -506, 12409, -506, 1033, -522, 1033, -486, 1041, -512, 1029, -484, 1043, -514, 1005, -512, 1037, -512, 1035, -484, 519, -1016, 531, -1018, 1009, -528, 521, -1012, 1041, -510, 509, -1006\nRAW_Data: 509, -1044, 1009, -524, 491, -1032, 1017, -516, 505, -1058, 483, -1042, 511, -1008, 1039, -514, 1001, -516, 1033, -508, 1015, -522, 1041, -512, 497, -1020, 515, -1042, 1007, -514, 507, -1046, 1009, -492, 537, -1016, 1009, -542, 995, -516, 1045, -512, 1007, -514, 513, -1012, 517, -1020, 529, -1020, 1009, -528, 519, -1012, 513, -1010, 1043, -516, 1009, -524, 12405, -490, 1057, -512, 1009, -504, 1023, -516, 1043, -512, 1005, -512, 1037, -514, 1001, -520, 1017, -520, 513, -1010, 527, -1018, 1043, -512, 511, -1006, 1033, -508, 507, -1040, 509, -1008, 1033, -504, 507, -1034, 1015, -522, 513, -1008, 525, -1016, 513, -1046, 1007, -514, 1009, -518, 1035, -490, 1031, -520, 1041, -510, 497, -1020, 515, -1044, 1005, -516, 515, -1012, 1013, -522, 513, -1038, 1029, -516, 1011, -512, 1009, -514, 1041, -482, 541, -1012, 523, -1016, 513, -1008, 1021, -516, 517, -1042, 511, -1008, 1043, -482, 1033, -520, 12405, -512, 1037, -514, 1003, -518, 1033, -492, 1031, -506, 1031, -520, 1009, -540, 993, -518, 1043, -512, 513, -1010, 513, -1040, 1005, -524, 493, -1050, 1013, -500, 525, -1014, 513, -1044, 1007, -512, 509, -1040, 1001, -522, 489, -1050, 513, -1008, 527, -1014, 1041, -512, 1003, -512, 1039, -514, 1003, -520, 1017, -504, 537, -1016, 511, -1042, 989, -518, 513, -1042, 1005, -508, 507, -1036, 1037, -516, 1009, -506, 1031, -506, 1035, -490, 539, -1022, 505, -1034, 509, -1008, 1031, -522, 517, -1014, 511, -1006, 1039, -512, 1033, -506, 12415, -514, 1005, -512, 1035, -514, 1035, -482, 1035, -524, 1019, -524, 1015, -498, 1019, -516, 1041, -510, 511, -1006, 507, -1038, 1039, -486, 517, -1036, 1017, -522, 513, -1008, 525, -1016, 1009, -512, 541, -1004, 1033, -508, 507, -1040, 481, -1048, 489, -1050, 1007, -540, 989, -516, 1045, -514, 1005, -512, 1037, -512, 509, -1008, 505, -1034, 1047, -520, 479, -1044, 1027, -516, 483, -1044, 1007, -512, 1037, -514, 1003, -518, 1015, -520, 515, -1044, 497, -1018, 515, -1046, 1005, -514, 515, -1014, 519, -1020, 1029, -520, 1007, -524, 12421, -494, 1017, -516, 1039, -514, 1005, -512, 1035, -514, 1003, -516, 1037, -492, 1031, -522, 1009, -508, 527, -1016, 515, -1040, 1003, -510, 507, -1038, 1037, -482, 539, -1004, 519, -1018, 1029, -520, 513, -1026, 1015, -516, 513, -1010, 511, -1044, 515, -1010, 1035, -492, 1029, -520, 1009, -528, 1015, -514, 1041, -510, 509, -1006, 509, -1044, 1009, -520, 507, -1018, 1031, -522, 515, -1004, 1019, -516, 1045, -512, 1009, -514, 1009, -518, 507, -1044, 509, -1016, 513, -1042, 991, -518, 515, -1044, 511, -1006, 1033, -508, 1035, -482, 12435, -506, 1035, -482, 1031, -508, 1047, -492, 1033, -520, 1007, -540, 989, -516, 1045, -512, 1007, -512, 507, -1036, 509, -1040, 1001, -518, 505, -1038, 1015, -504, 511, -1044, 499, -1018, 1043, -512, 513, -1006, 1039, -512, 507, -1040, 481, -1034, 519, -1020, 1029, -518, 1007, -526, 1015, -514, 1009, -510, 1037, -512, 507, -1040, 505, -1034, 1003, -520, 523, -1018, 1013, -504, 527, -1018, 1041, -512, 1003, -514, 1035, -514, 1001, -518, 505, -1034, 521, -1020, 501, -1048, 1005, -526, 517, -1012, 513, -1010, 1039, -512, 1005, -516, 12427, -512\nRAW_Data: 1033, -506, 1003, -520, 1015, -522, 1039, -512, 1023, -516, 1011, -514, 1007, -510, 1039, -514, 507, -1010, 507, -1044, 1035, -520, 487, -1048, 991, -516, 515, -1046, 511, -1008, 1011, -516, 505, -1032, 1035, -492, 537, -1016, 511, -1040, 497, -1020, 1043, -510, 1007, -512, 1037, -514, 1003, -516, 1033, -504, 505, -1034, 507, -1016, 1033, -520, 521, -1010, 1017, -516, 515, -1040, 1001, -508, 1037, -512, 1035, -482, 1035, -520, 491, -1032, 505, -1048, 513, -1010, 1019, -516, 515, -1042, 509, -1008, 1043, -484, 1049, -522, 12373, -520, 1045, -498, 1021, -514, 1007, -512, 1039, -514, 1035, -482, 1033, -522, 1017, -520, 1017, -504, 525, -1018, 515, -1010, 1039, -514, 507, -1040, 1003, -518, 507, -1016, 505, -1030, 1031, -520, 515, -1006, 1025, -516, 515, -1042, 509, -1008, 507, -1044, 1007, -520, 1031, -506, 1033, -494, 1031, -522, 1011, -504, 527, -1018, 513, -1010, 1041, -514, 507, -1008, 1033, -506, 505, -1032, 1035, -492, 1035, -520, 1009, -542, 991, -518, 515, -1042, 511, -1008, 513, -1046, 1011, -492, 505, -1050, 513, -1006, 1021, -514, 1041, -512, 12415, -484, 1045, -514, 1005, -512, 1033, -508, 1035, -514, 1001, -540, 1009, -522, 999, -520, 1039, -512, 499, -1018, 515, -1044, 1005, -512, 509, -1040, 1003, -520, 523, -1016, 517, -1012, 1019, -514, 517, -1042, 1005, -514, 505, -1042, 513, -1004, 517, -1020, 1025, -514, 1043, -496, 1019, -516, 1011, -512, 1037, -508, 509, -1040, 509, -1008, 1035, -520, 507, -1018, 1031, -522, 481, -1038, 1019, -516, 1011, -512, 1041, -512, 1037, -484, 515, -1034, 507, -1020, 519, -1018, 1035, -520, 485, -1044, 511, -1012, 1043, -484, 1047, -492, 12433, -492, 1033, -520, 1009, -506, 1023, -516, 1041, -514, 1005, -512, 1037, -512, 1003, -516, 1035, -520, 507, -1018, 505, -1050, 1011, -500, 525, -1014, 1041, -512, 509, -1010, 513, -1042, 1001, -522, 489, -1050, 1015, -502, 529, -1016, 513, -1040, 509, -1004, 1037, -514, 999, -538, 1001, -506, 1035, -520, 1017, -522, 513, -1008, 527, -1018, 1011, -514, 511, -1036, 1037, -482, 517, -1046, 1001, -536, 1001, -520, 1043, -510, 1025, -482, 517, -1044, 511, -1010, 513, -1042, 1001, -506, 505, -1036, 519, -1020, 1027, -514, 1009, -528, 12397, -526, 1015, -514, 1043, -510, 1003, -512, 1037, -512, 1001, -520, 1047, -520, 1005, -512, 1031, -516, 483, -1044, 513, -1006, 1035, -514, 507, -1040, 1001, -508, 521, -1016, 505, -1050, 1009, -504, 529, -1016, 1045, -478, 511, -1038, 509, -1042, 505, -1034, 1001, -520, 1033, -494, 1029, -520, 1009, -528, 1015, -514, 513, -1038, 511, -1004, 1041, -482, 541, -1010, 1033, -492, 505, -1052, 1009, -504, 1023, -518, 1041, -512, 1011, -514, 513, -1040, 509, -1010, 507, -1016, 1031, -520, 521, -1010, 523, -1016, 1009, -514, 1041, -512, 12391, -540, 1003, -512, 1037, -512, 1001, -518, 1045, -508, 1019, -522, 1007, -504, 1021, -518, 1045, -510, 511, -1008, 513, -1046, 1009, -490, 539, -1016, 1009, -542, 495, -1018, 515, -1014, 1039, -516, 517, -1008, 1037, -520, 485, -1050, 495, -1020, 515, -1044, 1007, -514, 1035, -484, 1035, -520, 999, -520, 1039, -512, 499, -1018, 515, -1044, 1005, -512, 507, -1038, 1009, -516\nRAW_Data: 507, -1044, 1035, -484, 1041, -510, 1027, -518, 1011, -514, 511, -1010, 511, -1042, 515, -1008, 1035, -492, 533, -1018, 513, -1032, 1013, -514, 1011, -512, 12423, -512, 1005, -512, 1037, -512, 1037, -480, 1035, -522, 1015, -502, 1037, -512, 1033, -520, 1011, -514, 513, -1006, 509, -1038, 1035, -516, 507, -1010, 1035, -490, 539, -1014, 513, -1010, 1021, -518, 513, -1040, 1005, -508, 509, -1040, 513, -1014, 515, -1034, 1035, -520, 1007, -514, 1025, -518, 1011, -514, 1007, -512, 509, -1038, 509, -1040, 1003, -524, 491, -1050, 1009, -504, 527, -1018, 1009, -510, 1039, -512, 1037, -484, 1045, -490, 539, -1022, 505, -1016, 503, -1054, 1011, -514, 513, -1008, 509, -1040, 1005, -538, 1005, -516, 12403, -508, 1037, -512, 1033, -506, 1035, -484, 1045, -504, 1035, -520, 1001, -514, 1029, -522, 1011, -514, 511, -1010, 513, -1044, 1009, -522, 491, -1050, 1009, -504, 527, -1018, 515, -1040, 1005, -514, 507, -1042, 1001, -506, 517, -1046, 507, -1022, 503, -1020, 1043, -498, 1021, -516, 1041, -512, 1007, -514, 1009, -516, 505, -1034, 505, -1034, 1035, -492, 537, -1016, 1009, -538, 495, -1020, 1043, -512, 1003, -512, 1033, -508, 1035, -484, 517, -1046, 507, -1016, 513, -1040, 1025, -482, 517, -1042, 509, -1008, 1039, -514, 1003, -520, 12435, -484, 1047, -522, 1001, -514, 1033, -486, 1045, -514, 1005, -508, 1037, -514, 1009, -518, 1037, -490, 539, -1018, 513, -1008, 1021, -516, 513, -1040, 1007, -514, 515, -1040, 509, -1010, 1035, -494, 503, -1034, 1029, -520, 513, -1028, 487, -1048, 513, -1010, 1043, -484, 1047, -520, 1005, -514, 1031, -518, 1011, -514, 511, -1006, 509, -1038, 1035, -482, 541, -1008, 1035, -520, 507, -1012, 1031, -522, 1011, -514, 1005, -512, 1035, -508, 513, -1040, 505, -1040, 483, -1046, 1001, -520, 513, -1042, 497, -1016, 1043, -512, 1001, -508, 12423, -514, 1039, -510, 1005, -512, 1037, -512, 1001, -508, 1041, -506, 1031, -506, 1035, -492, 1025, -520, 515, -1032, 519, -1010, 1007, -512, 509, -1038, 1037, -514, 505, -1034, 509, -1010, 1033, -520, 475, -1042, 1029, -520, 485, -1042, 513, -1010, 513, -1044, 1011, -522, 999, -536, 999, -522, 1043, -510, 1025, -518, 483, -1042, 509, -1008, 1041, -514, 509, -1012, 1013, -520, 513, -1044, 991, -516, 1043, -514, 1007, -514, 1043, -484, 517, -1048, 507, -1014, 513, -1040, 991, -518, 515, -1040, 511, -1010, 1043, -482, 1047, -522, 12403, -522, 1013, -498, 1019, -516, 1041, -512, 1005, -514, 1033, -508, 1035, -484, 1045, -492, 1031, -520, 515, -1006, 527, -1018, 1013, -512, 511, -1038, 1035, -482, 541, -1008, 507, -1032, 1037, -494, 505, -1032, 1031, -508, 511, -1042, 497, -1020, 515, -1042, 1007, -512, 1035, -482, 1033, -520, 1035, -520, 1001, -514, 501, -1054, 481, -1044, 1005, -508, 511, -1038, 1035, -484, 517, -1046, 1001, -520, 1041, -510, 1025, -518, 1009, -512, 511, -1010, 513, -1044, 481, -1048, 1015, -504, 511, -1044, 499, -1018, 1043, -512, 1005, -512, 12409, -516, 1041, -510, 1005, -512, 1035, -512, 1037, -482, 1033, -520, 1035, -520, 1003, -512, 1029, -520, 483, -1044, 513, -1006, 1035, -508, 513, -1040, 1003, -518, 507, -1016, 505, -1048, 1007, -540, 497, -1016\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/ido_117_111_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 32700 -4524 475 -1412 481 -538 465 -1440 481 -500 491 -1408 481 -526 449 -508 475 -552 475 -1450 465 -516 481 -1432 479 -1446 473 -514 473 -1408 481 -502 487 -1438 481 -1430 499 -1444 449 -1448 481 -1408 509 -484 483 -514 449 -1448 481 -1440 477 -1448 473 -518 479 -1446 473 -518 477 -1410 481 -1430 475 -1402 469 -1476 481 -1450 463 -516 481 -516 481 -490 499 -1412 487 -1428 481 -1406 469 -1472 473 -1436 479 -522 473 -520 481 -1426 473 -1442 461 -510 477 -1418 479 -1394 4437 -4508 509 -1410 511 -508 469 -1442 481 -498 491 -1406 483 -492 483 -506 475 -552 475 -1444 467 -516 481 -1438 479 -1414 505 -484 507 -1406 483 -498 487 -1438 483 -1426 503 -1402 509 -1406 507 -1410 487 -504 489 -480 479 -1412 495 -1468 471 -518 481 -1414 511 -510 465 -516 479 -516 481 -1394 493 -480 507 -540 477 -514 473 -1446 467 -516 481 -512 473 -504 473 -522 483 -498 487 -510 481 -1462 487 -498 489 -1406 485 -1450 477 -1406 501 -1410 459 -1438 483 -1382 4443 -4528 481 -1416 497 -514 481 -1440 477 -488 513 -1402 481 -490 481 -510 471 -538 475 -1442 505 -484 515 -1406 481 -1420 479 -506 495 -1406 483 -490 481 -1464 483 -1428 487 -1446 461 -1438 479 -1416 495 -514 477 -482 505 -1414 479 -1438 481 -1452 475 -1434 473 -506 479 -1428 473 -1434 475 -488 497 -514 475 -1444 469 -1442 483 -1450 479 -1406 509 -1406 481 -504 493 -478 509 -482 471 -1482 483 -1404 517 -492 491 -512 479 -1438 477 -484 481 -510 473 -1428 465 -1404 4437 -4530 467 -1442 487 -504 493 -1438 483 -496 489 -1406 481 -490 515 -478 481 -526 485 -1426 517 -510 477 -1404 509 -1408 481 -504 491 -1404 481 -492 481 -1464 481 -1430 501 -1410 485 -1430 483 -1434 479 -488 513 -476 473 -1440 463 -1474 471 -520 481 -1412 513 -1404 499 -486 509 -1408 483 -1426 471 -1408 487 -542 471 -1434 475 -520 471 -1450 479 -514 479 -504 471 -506 483 -1396 487 -544 469 -514 515 -1404 483 -1450 471 -1414 483 -504 481 -1430 475 -1404 501 -1378 4469 -4486 483 -1454 471 -514 481 -1418 511 -510 465 -1438 485 -494 483 -480 479 -524 483 -1448 481 -514 481 -1442 467 -1438 481 -500 489 -1408 479 -492 481 -1470 483 -1422 471 -1440 471 -1452 479 -1408 509 -484 481 -514 483 -1414 479 -1442 481 -522 489 -510 479 -512 475 -520 471 -522 483 -496 485 -1404 479 -1448 471 -514 511 -1410 481 -538 465 -1436 483 -496 487 -478 509 -1406 467 -1474 477 -1438 461 -1448 481 -1418 491 -512 479 -1408 505 -484 481 -1406 505 -1374 4449 -4530 463 -1442 483 -500 495 -1440 483 -496 489 -1406 481 -492 515 -480 481 -522 507 -1414 483 -512\nRAW_Data: 479 -1444 469 -1440 483 -500 489 -1400 481 -492 481 -1462 481 -1430 499 -1412 483 -1450 479 -1404 507 -484 481 -512 473 -1414 491 -1436 481 -1432 497 -520 479 -520 485 -1406 479 -1418 481 -504 491 -478 509 -514 475 -516 505 -492 521 -488 505 -484 481 -514 477 -1430 463 -482 509 -540 475 -514 475 -522 489 -1418 477 -524 459 -522 481 -1412 481 -1426 479 -1404 4437 -4516 477 -1444 471 -514 479 -1412 511 -510 463 -1440 483 -494 485 -480 481 -524 483 -1458 487 -510 475 -1428 479 -1412 503 -486 509 -1408 483 -496 485 -1438 481 -1428 501 -1416 483 -1432 477 -1434 479 -488 515 -480 479 -1414 489 -1434 505 -518 481 -520 485 -1400 483 -526 485 -1398 483 -1416 481 -506 489 -1436 483 -1456 473 -522 483 -1422 473 -1442 459 -520 479 -1416 483 -498 487 -1436 481 -536 463 -514 483 -1438 477 -522 483 -478 477 -506 487 -1428 483 -1398 4431 -4516 467 -1438 489 -506 493 -1434 483 -498 489 -1404 485 -492 513 -474 475 -550 473 -1444 469 -516 481 -1436 479 -1414 503 -486 509 -1406 481 -500 485 -1434 481 -1430 503 -1428 487 -1402 515 -1418 479 -510 463 -514 479 -1406 469 -1476 477 -1432 473 -1426 479 -508 509 -482 507 -494 475 -514 465 -1410 483 -558 465 -514 483 -1436 481 -1416 511 -510 465 -514 479 -1404 503 -1408 485 -532 467 -514 483 -512 511 -484 487 -1426 491 -1400 483 -1418 483 -1430 477 -1402 4433 -4526 483 -1422 471 -514 507 -1412 481 -502 493 -1408 483 -492 517 -480 481 -526 485 -1452 479 -512 473 -1418 495 -1410 483 -526 485 -1400 481 -490 479 -1464 481 -1428 487 -1442 471 -1428 491 -1404 517 -496 483 -478 475 -1440 463 -1444 511 -510 467 -1442 483 -500 493 -1410 483 -526 485 -1400 481 -1416 483 -1462 459 -520 485 -524 489 -512 479 -1408 509 -484 481 -1406 509 -1410 483 -1422 503 -1430 479 -1440 479 -1412 513 -1402 471 -1438 471 -506 471 -1416 485 -1398 4441 -4518 477 -1430 507 -484 515 -1404 483 -526 485 -1400 481 -490 515 -476 473 -538 473 -1440 509 -516 481 -1400 485 -1450 477 -476 497 -1406 483 -492 515 -1432 483 -1424 503 -1432 479 -1402 511 -1408 481 -502 493 -480 509 -1396 505 -1436 473 -1446 481 -1418 495 -1438 481 -496 489 -1400 485 -1416 481 -506 491 -510 517 -1404 483 -1452 471 -520 481 -488 513 -1400 483 -490 515 -476 471 -540 473 -1462 477 -520 485 -510 477 -1404 509 -1408 481 -1426 487 -1408 487 -1372 4471 -4482 507 -1412 481 -538 465 -1442 483 -498 489 -1406 481 -492 517 -478 481 -526 487 -1428 481 -512 505 -1432 475 -1410 481 -504 493 -1406 483 -492 479 -1470 483 -1426 471 -1434 509 -32700 99 -266 99 -732\nRAW_Data: 329 -134 233 -232 32700 -4516 489 -1438 483 -498 493 -1438 481 -498 489 -1402 483 -526 483 -478 481 -524 503 -1428 469 -518 481 -1438 481 -1414 511 -476 495 -1404 481 -494 481 -1462 481 -1430 485 -1446 459 -1432 509 -1406 505 -482 481 -514 473 -1414 487 -1436 479 -540 467 -1442 485 -504 491 -514 479 -1406 505 -1408 481 -1392 479 -1466 487 -1450 445 -1446 505 -484 479 -514 479 -520 459 -520 479 -490 479 -1466 481 -534 463 -516 481 -1440 479 -1414 481 -502 491 -1408 481 -492 481 -1406 4433 -4526 491 -1440 483 -498 491 -1440 483 -496 489 -1404 483 -492 517 -478 481 -522 503 -1434 467 -516 481 -1438 479 -1414 503 -486 509 -1404 483 -500 485 -1436 481 -1428 503 -1420 479 -1446 467 -1438 481 -498 489 -478 477 -1428 505 -1438 471 -1450 481 -1416 481 -1440 479 -522 483 -478 505 -1396 507 -482 481 -544 477 -514 473 -1444 469 -1438 483 -1426 471 -522 479 -486 481 -1402 511 -518 491 -514 479 -512 507 -1406 505 -484 479 -1438 479 -1414 481 -502 489 -1402 4437 -4490 495 -1442 481 -500 495 -1440 483 -498 487 -1406 483 -492 517 -478 477 -538 473 -1444 503 -480 513 -1404 481 -1448 471 -486 511 -1406 481 -500 489 -1436 481 -1424 503 -1434 477 -1438 479 -1406 481 -506 491 -480 511 -1406 469 -1474 469 -520 483 -1444 471 -1414 481 -1428 479 -1430 481 -490 513 -472 475 -1476 477 -520 487 -510 477 -1408 511 -484 483 -510 475 -516 471 -1426 475 -1434 515 -1422 471 -1442 487 -1420 477 -522 461 -520 483 -488 481 -512 473 -1414 4445 -4484 515 -1402 517 -492 491 -1434 483 -496 487 -1404 483 -492 515 -478 473 -554 483 -1414 513 -478 509 -1404 511 -1406 481 -504 489 -1400 485 -492 513 -1434 481 -1430 501 -1412 483 -1430 481 -1438 479 -488 513 -480 479 -1412 489 -1438 481 -540 467 -516 481 -514 479 -518 471 -1428 477 -1432 479 -1408 481 -534 503 -1408 481 -1430 501 -484 511 -1410 481 -504 489 -1406 481 -492 481 -544 479 -512 509 -1406 509 -1406 483 -502 495 -514 475 -474 503 -472 503 -1398 4449 -4516 479 -1442 477 -520 487 -1400 485 -526 485 -1404 481 -490 515 -476 471 -538 507 -1412 507 -480 517 -1402 483 -1418 513 -474 493 -1404 485 -492 513 -1434 481 -1428 487 -1446 469 -1432 481 -1438 477 -484 479 -506 475 -1410 495 -1436 513 -1436 459 -520 483 -1416 511 -510 463 -516 479 -482 485 -1428 477 -1438 483 -1450 471 -520 483 -524 485 -476 509 -482 503 -1394 495 -478 509 -1438 481 -1448 471 -522 481 -522 483 -1402 483 -1414 503 -1414 489 -490 481 -1382 4451 -4498 497 -1440 481 -534 463 -1438 481 -498 489 -1402 481 -526 485 -478 481 -524\nRAW_Data: 485 -1448 481 -514 475 -1446 465 -1438 481 -500 487 -1402 481 -490 481 -1470 481 -1426 471 -1442 485 -1430 481 -1442 475 -486 483 -512 475 -1412 489 -1434 505 -514 479 -522 485 -1402 481 -1454 475 -1404 501 -484 471 -518 477 -540 477 -508 475 -516 507 -1412 483 -1438 479 -488 483 -1438 477 -1408 481 -534 469 -1442 481 -536 463 -1442 483 -1420 473 -512 477 -1416 481 -500 487 -1404 4433 -4518 467 -1438 481 -502 495 -1438 483 -496 491 -1402 483 -492 515 -480 479 -524 483 -1448 481 -514 479 -1446 465 -1440 483 -496 487 -1404 483 -490 481 -1472 481 -1424 471 -1440 487 -1420 479 -1446 465 -514 479 -484 483 -1430 479 -1440 483 -1446 471 -1448 481 -522 477 -508 465 -1406 519 -1388 483 -502 489 -1434 503 -1414 481 -1456 485 -1402 483 -526 487 -476 509 -1406 469 -1406 517 -1420 503 -1418 477 -520 485 -506 481 -1434 501 -484 505 -482 479 -482 483 -1428 4437 -4484 511 -1438 477 -518 485 -1404 483 -524 485 -1406 481 -490 511 -474 475 -552 471 -1432 509 -484 483 -1436 483 -1416 503 -488 507 -1408 483 -496 483 -1434 481 -1428 503 -1434 477 -1410 509 -1404 481 -504 493 -478 505 -1400 505 -1444 475 -512 505 -1410 481 -1426 501 -482 483 -526 485 -478 479 -1414 493 -546 483 -1436 483 -496 491 -514 479 -1402 511 -1408 481 -502 489 -480 479 -552 473 -522 471 -1430 483 -510 509 -480 507 -488 489 -506 487 -478 479 -1416 4439 -4514 485 -1436 485 -496 491 -1406 517 -494 487 -1402 483 -490 515 -478 473 -554 483 -1414 481 -510 509 -1408 509 -1408 483 -500 489 -1400 483 -492 479 -1462 483 -1428 499 -1412 481 -1428 513 -1398 483 -524 485 -476 473 -1428 501 -1442 475 -1434 471 -1428 515 -1402 481 -1416 513 -474 495 -1410 481 -1418 481 -1464 457 -522 487 -1454 477 -510 465 -516 481 -1406 509 -484 481 -478 505 -1448 503 -482 517 -510 479 -482 517 -486 483 -504 489 -1400 483 -494 513 -1402 4431 -4482 497 -1440 483 -502 495 -1438 483 -496 489 -1404 483 -492 515 -478 479 -526 483 -1430 517 -476 507 -1432 479 -1410 481 -506 493 -1402 483 -490 481 -1466 483 -1428 501 -1412 485 -1426 493 -1402 517 -494 485 -476 509 -1406 465 -1472 479 -1438 459 -522 483 -524 485 -478 507 -1396 509 -1408 483 -500 485 -512 481 -1436 485 -1446 471 -1418 481 -1450 479 -1408 505 -484 479 -1402 507 -516 491 -1436 483 -1424 471 -516 507 -484 481 -1438 477 -1408 481 -502 487 -1402 4441 -4480 499 -1438 481 -502 497 -1438 483 -498 491 -1404 483 -492 483 -510 471 -538 479 -1444 503 -482 513 -1398 483 -1418 511 -478 495 -1408 483 -492 483 -1470 483 -1424 471 -1442 485 -1418\nRAW_Data: 479 -1444 469 -516 479 -480 485 -1416 479 -1478 477 -520 487 -512 475 -1430 479 -488 515 -474 509 -482 471 -522 489 -1422 501 -1412 483 -522 481 -1430 501 -484 509 -1406 483 -500 489 -1402 481 -1450 471 -550 477 -516 485 -1402 485 -1420 513 -476 493 -480 511 -482 471 -1420 4437 -4492 481 -1460 489 -510 479 -1436 477 -486 513 -1404 479 -488 481 -512 473 -520 505 -1414 497 -514 481 -1438 481 -1414 481 -504 491 -1402 485 -490 515 -1438 483 -1428 471 -1442 487 -1420 479 -1416 495 -514 479 -478 507 -1418 483 -1434 481 -1424 503 -512 481 -1426 469 -1436 475 -488 501 -1430 477 -1406 505 -514 489 -512 481 -32700 99 -632 167 -604 99 -298 233 -132 733 -166 461\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Intertechno_V3\nBit: 32\nKey: 00 00 00 00 3F 86 C5 9F\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 15041 -66 15883 -66 12643 -66 12681 -66 3413 -68 2713 -68 33389 -66 1445 -66 1279 -68 1027 -66 6911 -98 25229 -66 3967 -100 3019 -100 6131 -66 955 -66 3605 -66 12411 -98 1419 -66 3593 -68 2753 -66 2457 -66 6007 -66 627 -100 1597 -66 3071 -98 22749 -66 333 -66 12829 -66 4313 -132 855 -66 44097 -64 20391 -98 29999 -66 3539 -98 557 -66 1489 -100 4081 -100 3857 -64 2895 -132 2261 -166 3089 -66 2429 -68 34467 -66 3585 -66 3087 -66 3329 -132 5287 -66 1063 -98 15259 -100 2535 -66 995 -66 13057 -100 24233 -68 531 -100 26415 -66 1761 -100 2717 -66 4071 -100 12191 -66 23367 -68 2323 -66 19809 -248 245 -1388 255 -242 275 -1358 273 -1370 277 -246 277 -1368 275 -246 275 -1362 275 -244 275 -1364 275 -244 275 -1362 275 -244 275 -1328 273 -278 273 -1358 275 -246 275 -238 263 -1384 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1344 277 -246 275 -1358 275 -244 275 -234 263 -1382 277 -1344 277 -246 279 -1362 275 -246 271 -234 261 -1380 275 -246 273 -1360 275 -246 275 -1366 277 -1340 277 -248 279 -238 263 -1382 275 -1344 277 -246 279 -1364 277 -244 275 -234 263 -1382 277 -244 273 -1358 275 -1344 277 -248 279 -1368 275 -244 273 -1360 239 -280 271 -1358 275 -244 275 -1358 275 -174 269 -10298 289 -2660 267 -238 299 -1356 275 -244 275 -1356 275 -1344 277 -248 277 -1360 275 -246 275 -1328 309 -244 273 -1358 277 -244 275 -1356 275 -246 273 -1326 309 -244 275 -1356 275 -246 273 -234 263 -1380 277 -246 273 -1326 309 -244 273 -1356 277 -246 277 -1358 275 -1338 279 -248 279 -1364 275 -246 273 -234 261 -1380 277 -1344 279 -250 277 -1330 309 -244 273 -232 261 -1384 275 -246 273 -1356 275 -248 275 -1360 275 -1340 279 -248 277 -236 263 -1380 277 -1342 279 -248 279 -1366 275 -246 273 -234 263 -1380 275 -246 275 -1358 275 -1340 279 -248 281 -1336 309 -244 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -176 267 -10306 257 -2646 299 -234 301 -1354 277 -246 275 -1356 277 -1340 279 -250 279 -1332 309 -244 275 -1358 275 -248 273 -1326 309 -246 273 -1326 309 -244 275 -1356 277 -248 275 -1328 309 -246 273 -234 261 -1382 277 -246 277 -1326 309 -244 275 -1358 277 -246 277 -1356 277 -1346 277 -250 277 -1358 277 -246 275 -234 263 -1382 279 -1346 279 -248 281 -1330 307 -246 273 -236 261 -1380 277 -246 277 -1360 277 -246 277 -1360 275 -1344 279 -248 279 -236 263 -1384 277 -1340 279 -250 281 -1338 307 -246 271 -234 261 -1384 277 -246 275 -1356 277 -1340 279 -250 283 -1336 309 -246 273 -1356 277 -246 273 -1360 277 -246\nRAW_Data: 275 -1328 309 -174 269 -10296 289 -2648 267 -238 299 -1356 277 -246 275 -1324 307 -1342 279 -250 277 -1330 309 -244 275 -1362 277 -244 275 -1356 275 -248 273 -1328 309 -244 273 -1328 309 -244 275 -1360 277 -246 275 -234 259 -1384 277 -246 275 -1360 275 -246 273 -1358 277 -248 277 -1362 275 -1344 277 -248 277 -1328 307 -246 273 -236 261 -1384 277 -1348 279 -248 279 -1360 277 -246 273 -234 263 -1388 275 -246 275 -1360 277 -248 279 -1368 277 -1344 279 -248 279 -240 265 -1386 275 -1342 279 -286 247 -1372 275 -248 275 -238 265 -1386 277 -248 275 -1360 275 -1344 277 -286 247 -1374 275 -246 275 -1362 277 -246 275 -1360 277 -248 275 -1326 307 -174 269 -10290 287 -2654 269 -236 301 -1352 275 -248 273 -1326 311 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -244 275 -1326 309 -244 273 -1356 277 -244 273 -1356 275 -246 275 -1358 275 -244 275 -234 261 -1382 277 -246 273 -1358 275 -246 273 -1360 277 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 271 -234 259 -1382 277 -1346 279 -248 277 -1330 309 -244 271 -232 259 -1382 277 -244 275 -1356 277 -248 273 -1354 277 -1342 277 -248 275 -236 261 -1380 277 -1344 277 -248 279 -1330 307 -246 273 -234 261 -1378 277 -246 273 -1356 277 -1342 277 -248 277 -1330 309 -244 273 -1322 307 -246 273 -1326 309 -244 273 -1322 309 -176 267 -10298 257 -2682 265 -236 299 -1324 309 -248 273 -1324 311 -1342 277 -246 279 -1360 277 -244 275 -1362 275 -244 275 -1358 275 -244 275 -1360 275 -246 273 -1360 275 -244 277 -1360 275 -246 273 -234 263 -1384 275 -246 273 -1358 275 -246 275 -1360 277 -246 277 -1356 277 -1342 279 -248 277 -1364 275 -244 275 -234 261 -1384 275 -1344 277 -250 279 -1366 275 -246 273 -236 263 -1384 277 -246 275 -1358 277 -246 277 -1362 277 -1342 279 -248 279 -236 265 -1382 277 -1346 277 -248 281 -1366 275 -246 275 -234 265 -1384 275 -246 273 -1358 277 -1344 279 -248 279 -1364 275 -244 275 -1324 309 -246 273 -1324 307 -246 273 -1326 309 -174 267 -118796 133 -100 131 -892 329 -166 199 -132 131 -166 99 -100 265 -264 4663 -134 4889 -100 365 -98 5921 -100 5903 -68 4877 -98 2953 -98 1645 -64 1687 -66 981 -98 10769 -66 18319 -66 4831 -66 13301 -66 893 -132 5967 -100 15949 -66 3749 -66 497 -100 625 -66 1147 -66 469 -66 1261 -66 3651 -100 265 -100 26741 -68 6873 -66 4485 -100 2667 -68 3159 -68 2857 -132 2655 -66 12903 -66 1277 -66 1711 -66 787 -100 1327 -198 727 -64 1677 -100 1187 -66 1019 -66 891 -66 4303 -100 11297 -66 3923 -254 253 -1380 247 -292 253 -1344\nRAW_Data: 277 -1346 277 -250 279 -1364 275 -244 275 -1362 275 -244 275 -1356 275 -246 273 -1358 241 -278 273 -1356 275 -246 273 -1360 275 -246 273 -234 263 -1382 275 -244 273 -1358 275 -246 273 -1360 275 -246 273 -1358 275 -1340 277 -248 277 -1362 275 -246 273 -234 261 -1380 277 -1344 277 -248 279 -1362 275 -244 273 -236 261 -1380 275 -244 275 -1360 275 -246 275 -1358 275 -1346 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1364 277 -244 273 -234 261 -1378 277 -246 273 -1356 277 -1340 277 -248 281 -1334 307 -246 271 -1356 275 -246 273 -1358 275 -244 273 -1326 309 -174 267 -10296 257 -2650 297 -232 263 -1384 277 -244 273 -1358 275 -1340 279 -248 279 -1328 309 -244 275 -1328 307 -244 273 -1356 275 -244 275 -1358 275 -246 273 -1324 309 -244 275 -1328 307 -244 273 -234 261 -1382 275 -246 273 -1326 309 -244 273 -1358 275 -246 273 -1358 275 -1338 279 -248 279 -1330 309 -244 273 -232 261 -1380 277 -1344 279 -248 279 -1330 309 -244 271 -234 261 -1382 275 -246 273 -1358 277 -244 275 -1330 309 -1338 277 -246 277 -236 263 -1380 277 -1342 277 -248 279 -1364 275 -246 273 -232 261 -1380 275 -248 275 -1328 307 -1338 277 -248 279 -1334 309 -244 271 -1358 275 -244 275 -1324 307 -246 271 -1328 309 -174 265 -10270 291 -2640 297 -232 297 -1350 277 -248 275 -1326 309 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -246 273 -1326 309 -244 273 -1354 275 -246 273 -1330 307 -244 273 -1358 275 -246 273 -234 263 -1380 275 -246 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -1340 277 -248 279 -1364 275 -244 273 -232 261 -1380 277 -1342 279 -250 279 -1332 307 -244 271 -234 261 -1378 277 -246 273 -1358 275 -248 275 -1360 275 -1340 277 -248 275 -236 263 -1382 277 -1344 277 -246 277 -1364 275 -246 273 -234 259 -1380 275 -246 273 -1362 275 -1342 275 -248 277 -1334 309 -244 271 -1356 275 -244 275 -1326 307 -244 273 -1356 275 -176 267 -10290 289 -2644 267 -238 301 -1320 309 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 273 -1326 307 -246 273 -1324 309 -246 273 -1322 309 -246 273 -1322 307 -246 275 -1326 309 -246 273 -234 259 -1382 275 -246 275 -1322 309 -246 273 -1326 309 -246 273 -1326 309 -1340 277 -248 275 -1326 309 -246 273 -232 261 -1380 279 -1346 277 -250 277 -1328 309 -244 271 -232 261 -1380 277 -246 273 -1358 275 -248 273 -1328 307 -1340 277 -248 277 -236 261 -1380 277 -1344 277 -248 279 -1328 309 -244 275 -232 261 -1378 277 -248 273 -1326 309 -1344 277 -248 277 -1358 277 -246 273 -1328 307 -244 271 -1324 309 -244\nRAW_Data: 273 -1324 309 -174 267 -10270 289 -2638 297 -234 297 -1352 275 -248 275 -1328 307 -1340 277 -248 275 -1330 309 -244 273 -1358 275 -244 275 -1326 309 -244 271 -1356 275 -244 275 -1326 307 -246 273 -1326 309 -244 273 -234 261 -1378 275 -248 275 -1326 309 -244 271 -1356 277 -248 273 -1328 309 -1338 277 -248 277 -1328 309 -244 271 -232 261 -1380 277 -1348 279 -248 277 -1328 307 -246 271 -234 259 -1384 275 -244 275 -1356 277 -246 275 -1326 309 -1344 275 -248 275 -236 261 -1378 277 -1342 277 -250 279 -1334 309 -244 271 -232 261 -1380 277 -246 273 -1326 307 -1344 277 -248 277 -1328 309 -246 273 -1326 309 -244 271 -1324 309 -244 273 -1324 307 -176 267 -10288 287 -2618 299 -236 299 -1354 277 -244 273 -1326 307 -1340 279 -248 275 -1328 309 -244 275 -1326 309 -246 273 -1324 307 -246 273 -1322 309 -244 273 -1322 309 -244 275 -1328 309 -246 273 -232 261 -1380 277 -246 275 -1324 309 -244 273 -1356 277 -246 275 -1324 309 -1340 279 -246 277 -1328 309 -244 273 -232 261 -1382 277 -1344 279 -250 277 -1324 309 -246 273 -234 261 -1380 277 -246 273 -1358 277 -246 273 -1328 309 -1340 277 -248 275 -236 261 -1380 275 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1354 277 -1344 277 -248 277 -1328 311 -246 273 -1324 307 -244 273 -1324 309 -244 273 -1320 309 -176 269 -118210 761 -168 267 -66 563 -132 99 -132 3543 -66 5345 -100 4355 -66 4617 -68 20503 -166 2379 -132 293 -98 4117 -66 1151 -98 3353 -66 3485 -66 2491 -66 6133 -66 233 -68 16307 -68 16959 -98 357 -66 5419 -134 799 -100 327 -100 791 -66 2481 -66 963 -100 3481 -98 1679 -134 2473 -100 227 -68 3087 -66 11527 -130 4305 -98 435 -66 563 -100 2887 -100 267 -66 1787 -66 9655 -66 4793 -100 2119 -66 359 -98 1313 -132 3393 -234 995 -66 2681 -98 99 -130 1379 -100 3757 -100 21695 -132 5135 -100 693 -98 4631 -100 2325 -68 4937 -66 10409 -98 897 -100 1287 -66 2565 -66 3753 -66 4055 -66 2023 -68 1961 -68 629 -66 431 -66 5039 -66 2155 -100 2673 -66 1163 -98 6539 -100 825 -66 1197 -100 3053 -66 13973 -68 15515 -100 1861 -66 1027 -66 797 -98 959 -98 787 -132 787 -64 3811 -132 1747 -66 6683 -66 1033 -68 24927 -66 1259 -100 1125 -68 663 -66 1687 -66 4357 -132 4567 -66 3969 -98 3317 -132 433 -134 6043 -66 3249 -100 431 -98 2367 -100 11265 -66 5085 -68 2355 -64 1815 -66 1395 -274 241 -1366 275 -244 275 -1362 275 -1338 277 -284 243 -1368 239 -278 275 -1362 275 -244 275 -1360 241 -278 273 -1356 275 -246 275 -1360 239 -280 275 -1360\nRAW_Data: 275 -244 275 -234 263 -1386 239 -280 273 -1356 275 -244 273 -1360 275 -244 277 -1364 275 -1336 277 -248 277 -1366 275 -244 273 -234 263 -1386 275 -1340 277 -248 279 -1364 275 -244 275 -234 263 -1384 273 -244 275 -1358 275 -244 275 -1364 275 -1342 275 -248 277 -236 265 -1384 275 -1340 277 -282 243 -1366 275 -246 273 -236 263 -1382 277 -244 275 -1358 275 -1342 277 -248 277 -1364 275 -246 275 -1360 239 -280 273 -1358 241 -278 275 -1356 275 -210 233 -10302 257 -2652 297 -232 297 -1354 277 -244 275 -1358 275 -1340 279 -248 279 -1360 275 -246 275 -1360 275 -246 273 -1360 275 -244 275 -1328 309 -242 273 -1324 309 -244 275 -1360 275 -246 273 -234 261 -1384 275 -246 273 -1358 275 -244 275 -1358 277 -248 273 -1358 275 -1340 279 -248 277 -1334 307 -242 273 -232 261 -1380 277 -1348 277 -250 277 -1364 275 -244 275 -234 261 -1380 277 -244 275 -1358 277 -246 277 -1360 277 -1342 275 -248 275 -236 263 -1380 277 -1344 277 -248 279 -1368 275 -244 275 -232 261 -1382 277 -244 275 -1356 275 -1344 277 -248 279 -1362 275 -246 275 -1360 275 -246 273 -1356 275 -246 273 -1356 275 -176 267 -10302 257 -2648 299 -234 297 -1352 277 -246 275 -1326 309 -1340 279 -248 277 -1330 309 -244 275 -1328 309 -244 273 -1324 309 -244 275 -1324 309 -246 273 -1324 307 -246 275 -1328 309 -244 273 -234 261 -1378 277 -248 275 -1328 309 -244 273 -1356 277 -248 275 -1326 309 -1344 277 -248 275 -1326 309 -246 273 -234 259 -1380 277 -1348 281 -248 279 -1328 307 -246 273 -234 259 -1382 277 -246 275 -1360 275 -248 275 -1324 309 -1340 279 -248 277 -238 261 -1382 277 -1344 277 -248 279 -1330 311 -244 273 -234 259 -1378 277 -248 275 -1326 309 -1340 279 -248 279 -1336 307 -246 271 -1324 309 -244 275 -1324 307 -246 273 -1326 309 -174 269 -10296 257 -2648 299 -234 297 -1352 277 -248 273 -1326 309 -1342 277 -248 277 -1328 309 -246 275 -1328 309 -244 273 -1326 309 -244 273 -1322 309 -244 273 -1328 307 -244 275 -1328 309 -246 273 -234 261 -1382 277 -246 275 -1326 309 -244 273 -1352 277 -248 275 -1330 309 -1340 277 -248 277 -1328 309 -244 275 -232 261 -1384 277 -1342 279 -250 279 -1328 309 -244 273 -234 263 -1380 277 -246 273 -1360 277 -246 275 -1326 309 -1340 277 -250 277 -236 263 -1382 277 -1342 277 -248 279 -1362 277 -246 273 -234 263 -1382 277 -244 275 -1356 277 -1340 279 -248 279 -1362 275 -246 275 -1328 307 -246 273 -1356 275 -246 273 -1356 275 -174 269 -10292 287 -2650 269 -236 301 -1354 275 -248 273 -1358 275 -1340 279 -248 277 -1332 307 -246 275 -1328\nRAW_Data: 309 -244 273 -1324 309 -244 273 -1356 275 -246 273 -1358 275 -244 277 -1330 309 -244 273 -234 261 -1382 277 -244 275 -1358 275 -246 273 -1356 277 -248 275 -1360 275 -1340 277 -248 277 -1360 275 -246 273 -236 261 -1382 279 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1360 277 -246 273 -1360 275 -1342 279 -248 275 -236 263 -1382 275 -1344 279 -248 279 -1362 277 -246 273 -234 263 -1380 277 -246 275 -1356 275 -1342 277 -248 281 -1336 307 -246 271 -1354 277 -246 275 -1328 307 -244 273 -1352 277 -176 269 -10300 257 -2650 299 -232 297 -1354 277 -246 275 -1356 277 -1342 277 -248 279 -1328 309 -244 275 -1360 275 -246 273 -1328 307 -246 273 -1356 277 -246 277 -1326 309 -244 277 -1360 277 -246 273 -234 263 -1384 277 -246 275 -1324 309 -246 275 -1358 277 -246 277 -1360 277 -1344 277 -248 277 -1326 309 -246 273 -236 261 -1382 277 -1348 279 -250 281 -1330 307 -246 273 -234 263 -1386 277 -244 275 -1356 277 -248 277 -1362 277 -1342 277 -250 277 -238 263 -1384 277 -1342 277 -250 281 -1332 309 -246 273 -234 263 -1380 277 -246 275 -1360 277 -1342 279 -248 281 -1334 307 -246 273 -1356 275 -248 275 -1328 309 -244 275 -1324 309 -176 269 -115034 163 -362 67 -894 529 -166 14663 -98 4135 -66 3681 -100 299 -68 9829 -66 3517 -64 21569 -66 3251 -66 2209 -64 23701 -66 3359 -68 1057 -66 723 -66 299 -134 765 -66 589 -98 1687 -134 2153 -66 3081 -68 10447 -66 11643 -66 2451 -66 2277 -66 2897 -66 755 -100 5539 -64 5117 -132 4867 -134 3931 -64 625 -66 1317 -98 11597 -66 2255 -66 1165 -66 1123 -66 6371 -100 699 -68 1811 -66 621 -68 2191 -64 1291 -134 3003 -66 2423 -64 1463 -66 663 -100 1127 -100 6169 -100 489 -100 6087 -100 2027 -66 1195 -66 13195 -66 557 -66 40423 -98 1919 -100 1061 -132 201 -66 2553 -132 12549 -66 1789 -100 921 -134 1067 -66 729 -66 10029 -66 3909 -100 265 -100 16017 -134 21177 -68 2461 -66 2215 -68 1197 -66 5911 -66 2645 -66 3419 -132 16275 -64 5091 -68 2123 -66 2677 -64 10305 -66 12381 -100 427 -166 25331 -66 2457 -66 11859 -248 279 -1368 275 -246 275 -1360 275 -1340 277 -246 279 -1364 239 -278 275 -1358 275 -244 275 -1362 239 -278 273 -1358 239 -280 271 -1360 241 -278 273 -1360 275 -244 275 -234 261 -1384 239 -280 273 -1356 275 -244 273 -1360 275 -244 275 -1358 275 -1344 277 -248 275 -1358 275 -244 273 -236 261 -1384 275 -1342 279 -246 279 -1360 275 -244 275 -234 263 -1384 239 -278 273 -1358 275 -244 275 -1362 275 -1342 275 -248 275 -238 263 -1382 275 -1344 275 -248\nRAW_Data: 277 -1364 275 -244 273 -234 263 -1380 275 -246 273 -1358 275 -1342 277 -246 279 -1366 275 -244 273 -1362 239 -278 239 -1386 275 -246 273 -1360 241 -208 269 -10290 257 -2686 265 -232 265 -1384 275 -246 275 -1358 275 -1344 277 -248 275 -1358 275 -246 275 -1360 277 -244 273 -1326 309 -244 271 -1354 275 -244 275 -1358 275 -246 273 -1358 275 -246 273 -234 263 -1378 275 -246 275 -1360 275 -244 273 -1356 275 -246 275 -1360 275 -1342 277 -246 277 -1360 275 -246 273 -232 261 -1382 277 -1342 279 -248 279 -1360 275 -244 275 -232 261 -1380 277 -244 275 -1356 277 -246 277 -1360 275 -1342 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1362 275 -246 273 -234 261 -1378 277 -246 275 -1328 307 -1340 277 -246 279 -1366 275 -244 273 -1326 307 -244 273 -1324 309 -244 273 -1356 275 -174 267 -10304 255 -2648 297 -230 263 -1382 277 -244 275 -1330 307 -1338 277 -248 277 -1330 309 -244 273 -1356 275 -246 273 -1362 275 -244 273 -1356 275 -244 273 -1326 307 -244 273 -1360 273 -246 273 -236 261 -1380 275 -244 275 -1328 307 -244 273 -1358 275 -244 275 -1360 275 -1342 277 -246 277 -1364 275 -244 271 -232 261 -1384 277 -1340 279 -248 279 -1360 275 -246 273 -234 261 -1380 275 -244 275 -1360 277 -244 275 -1356 275 -1342 279 -246 277 -236 263 -1382 275 -1340 277 -248 279 -1366 275 -246 271 -234 261 -1382 277 -244 275 -1354 275 -1342 277 -248 277 -1364 273 -246 273 -1362 275 -244 271 -1360 275 -244 273 -1358 275 -174 267 -10272 289 -2646 265 -262 261 -1382 277 -244 275 -1356 275 -1342 277 -248 277 -1364 275 -244 275 -1360 275 -244 273 -1358 275 -244 273 -1358 275 -244 273 -1326 307 -244 275 -1358 275 -246 273 -234 261 -1382 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1338 277 -248 277 -1362 277 -244 271 -234 261 -1380 277 -1344 279 -248 277 -1332 273 -278 271 -234 261 -1382 275 -244 275 -1356 277 -246 275 -1360 277 -1340 277 -246 277 -234 263 -1384 275 -1342 277 -248 277 -1366 275 -244 273 -234 261 -1380 275 -246 273 -1360 275 -1340 277 -246 279 -1334 307 -244 273 -1356 275 -246 273 -1360 275 -244 271 -1354 277 -174 269 -10300 257 -2648 297 -230 263 -1384 277 -244 273 -1356 277 -1342 277 -248 277 -1362 275 -244 275 -1330 307 -244 273 -1324 309 -244 273 -1324 307 -246 273 -1326 307 -244 273 -1358 275 -246 273 -234 261 -1380 277 -246 273 -1358 275 -244 275 -1354 277 -248 275 -1360 275 -1338 279 -246 277 -1360 275 -244 273 -234 261 -1378 279 -1344 279 -248 279 -1330 309 -244 271 -232 261 -1380 277 -246 273 -1360\nRAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 -248 277 -1362 275 -246 273 -234 263 -1380 275 -244 275 -1358 275 -1340 277 -248 279 -1334 309 -244 273 -1324 307 -246 273 -1356 275 -244 273 -1356 275 -174 269 -10302 257 -2644 297 -232 263 -1384 277 -246 275 -1354 275 -1344 277 -248 275 -1360 275 -246 275 -1358 275 -246 273 -1326 307 -246 273 -1324 307 -244 273 -1328 307 -244 273 -1358 275 -244 273 -236 261 -1380 275 -246 273 -1358 275 -244 273 -1358 275 -246 273 -1360 275 -1344 275 -248 275 -1360 275 -244 273 -234 261 -1378 277 -1344 279 -248 277 -1362 275 -246 273 -234 261 -1378 275 -244 275 -1360 275 -246 275 -1358 275 -1344 277 -246 277 -234 263 -1380 275 -1338 279 -246 281 -1368 275 -244 271 -234 261 -1386 275 -244 271 -1358 275 -1342 277 -246 279 -1362 275 -244 275 -1326 273 -278 273 -1358 239 -278 273 -1358 275 -174 267 -127478 195 -964 2317 -66 763 -98 1455 -100 16109 -66 5683 -98 11469 -66 34413 -66 5443 -66 11613 -66 2737 -66 12191 -66 2951 -68 1851 -68 1895 -68 2643 \n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/kia_seed_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 51 -5136 1113 -104 127 -606 179 -126 325 -102 147 -200 205 -80 107 -190 81 -54 135 -378 181 -204 199 -104 121 -146 51 -230 75 -76 227 -102 415 -236 159 -54 159 -212 77 -312 73 -74 75 -128 155 -102 125 -358 125 -198 173 -608 75 -248 203 -558 53 -186 367 -180 151 -132 133 -138 135 -424 51 -240 53 -132 703 -80 79 -80 129 -294 77 -102 193 -270 77 -52 131 -180 51 -76 77 -76 51 -666 77 -104 101 -54 77 -164 107 -622 615 -294 133 -138 217 -180 75 -104 185 -80 107 -134 159 -398 181 -74 97 -198 73 -76 99 -102 127 -248 71 -128 211 -106 109 -104 101 -384 311 -398 51 -240 111 -84 81 -110 81 -668 105 -54 77 -158 79 -342 493 -78 153 -152 179 -122 223 -696 139 -590 81 -212 83 -322 129 -132 307 -346 129 -78 77 -232 77 -104 503 -52 187 -290 159 -108 159 -108 51 -136 53 -132 103 -566 177 -326 129 -176 177 -52 355 -308 129 -176 73 -104 151 -262 193 -168 111 -162 247 -52 131 -54 107 -84 83 -218 135 -402 107 -300 105 -220 81 -82 137 -82 163 -238 51 -192 293 -184 319 -454 81 -132 131 -132 79 -866 131 -212 163 -112 133 -106 211 -110 189 -214 51 -410 107 -454 131 -562 157 -52 367 -378 165 -110 81 -134 81 -82 75 -104 217 -638 81 -84 109 -160 81 -160 135 -54 53 -478 239 -78 209 -54 107 -52 155 -238 53 -184 105 -318 105 -370 233 -54 109 -246 217 -110 245 -110 209 -80 129 -514 79 -54 79 -314 155 -156 109 -292 79 -504 51 -644 157 -132 53 -208 53 -76 103 -76 77 -260 105 -82 133 -212 163 -136 53 -380 313 -178 193 -172 207 -82 79 -186 107 -136 53 -136 159 -156 97 -98 99 -254 179 -106 135 -214 307 -310 77 -76 123 -242 129 -52 103 -584 103 -178 203 -324 101 -572 253 -78 125 -126 129 -408 131 -52 129 -76 51 -132 79 -248 79 -414 51 -338 717 -322 79 -180 105 -294 109 -254 241 -76 251 -52 101 -152 125 -260 155 -80 77 -292 51 -226 209 -338 147 -100 51 -366 53 -348 133 -268 79 -366 55 -224 111 -248 81 -218 137 -168 79 -288 291 -132 51 -238 79 -78 103 -102 329 -102 123 -178 53 -202 51 -130 79 -138 51 -126 151 -180 99 -388 75 -126 51 -636 329 -150 99 -152 73 -122 177 -124 151 -386 297 -72 103 -130 51 -256 207 -354 75 -124 71 -148 351 -262 51 -540 77 -200 101 -132 77 -362 77 -254 103 -106 79 -296 51 -130 361 -240 51 -358 51 -284 201 -158 185 -288 183 -424 77 -130\nRAW_Data: 121 -146 99 -124 327 -74 99 -148 99 -252 183 -112 161 -216 263 -232 77 -102 103 -416 77 -382 345 -160 131 -110 167 -138 79 -158 133 -54 163 -84 107 -80 107 -108 107 -136 225 -82 321 -108 79 -106 107 -106 55 -162 103 -160 133 -192 53 -84 269 -106 77 -162 53 -136 161 -82 135 -346 77 -864 107 -140 55 -250 189 -128 201 -502 157 -564 105 -52 107 -1150 53 -218 237 -76 159 -1010 129 -196 139 -244 101 -270 77 -106 133 -110 109 -254 501 -496 237 -692 107 -54 159 -134 347 -210 51 -190 55 -276 103 -184 229 -98 149 -252 123 -156 75 -150 537 -236 103 -204 101 -100 73 -368 283 -102 77 -652 229 -206 99 -150 197 -206 53 -80 131 -274 101 -186 159 -396 51 -188 81 -188 83 -350 105 -428 129 -128 79 -78 53 -104 351 -284 235 -130 79 -242 51 -186 77 -134 135 -434 131 -552 53 -136 107 -140 141 -252 53 -54 53 -82 51 -770 79 -130 103 -432 127 -208 153 -226 131 -248 101 -902 125 -206 129 -102 77 -154 77 -188 105 -322 105 -166 133 -474 77 -728 161 -190 107 -54 133 -80 79 -190 53 -162 53 -404 77 -334 105 -52 153 -728 423 -700 133 -476 53 -162 53 -542 159 -168 189 -134 163 -134 81 -106 133 -54 267 -108 163 -242 165 -56 167 -294 81 -136 185 -660 51 -304 157 -54 109 -378 133 -272 159 -304 135 -110 53 -402 105 -630 185 -78 257 -422 173 -252 247 -100 123 -330 107 -166 109 -502 127 -156 219 -76 101 -348 71 -586 149 -154 73 -74 77 -358 131 -270 81 -108 159 -516 295 -346 53 -240 157 -240 55 -562 133 -110 213 -320 103 -560 79 -286 51 -134 55 -452 79 -136 79 -80 77 -52 185 -504 79 -216 79 -242 135 -262 75 -206 125 -472 111 -564 79 -322 79 -432 217 -456 167 -272 103 -664 53 -302 127 -138 241 -184 53 -80 81 -192 395 -604 133 -136 53 -1044 105 -166 215 -426 133 -1222 101 -348 127 -124 123 -248 153 -492 177 -102 103 -180 77 -204 151 -282 101 -74 103 -76 101 -170 73 -506 101 -368 51 -124 193 -236 53 -310 77 -130 155 -366 229 -526 129 -428 83 -922 51 -106 131 -82 219 -706 79 -80 81 -162 133 -276 195 -220 51 -266 81 -54 51 -238 53 -686 77 -354 229 -164 195 -164 81 -294 81 -132 197 -238 73 -202 181 -180 149 -128 207 -192 111 -56 187 -808 77 -608 79 -898 77 -208 97 -200 73 -184 75 -78 203 -664 105 -384 103 -102 129 -238 131 -1346 137 -526 105 -390 129 -138 81 -484 103 -222 109 -268 51 -240\nRAW_Data: 81 -658 53 -626 79 -562 79 -52 77 -266 107 -54 109 -410 105 -514 107 -110 53 -334 79 -928 121 -498 105 -184 149 -174 73 -908 129 -388 199 -450 153 -862 51 -896 101 -776 51 -990 129 -154 177 -412 169 -254 77 -902 105 -474 77 -2390 103 -1000 107 -2112 151 -402 51 -428 337 -440 129 -590 197 -538 53 -138 81 -584 81 -1958 107 -4714 57 -786 53 -684 137 -442 105 -654 53 -696 51 -2382 105 -376 83 -822 155 -6282 103 -560 77 -252 79 -1518 103 -3648 51 -6150 75 -4986 75 -2042 53 -2798 171 -804 2247 -130 357 -168 309 -168 307 -170 293 -172 305 -192 279 -224 253 -224 279 -196 265 -224 261 -222 253 -224 253 -224 279 -214 245 -232 243 -252 253 -224 251 -226 241 -222 253 -246 251 -224 253 -224 251 -252 213 -248 261 -222 251 -226 251 -254 223 -242 245 -232 245 -250 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 233 -248 253 -224 253 -252 223 -242 247 -230 245 -252 223 -252 253 -224 241 -222 255 -244 251 -226 251 -224 253 -252 213 -248 233 -250 251 -226 251 -252 225 -242 245 -230 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 251 -254 213 -246 235 -248 251 -226 251 -254 223 -242 245 -232 245 -252 225 -252 251 -224 253 -214 245 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -226 251 -224 253 -252 213 -248 233 -250 251 -226 253 -252 223 -242 245 -232 243 -254 223 -254 251 -224 253 -214 245 -236 247 -254 251 -224 253 -224 241 -246 231 -246 251 -226 251 -252 225 -252 213 -248 261 -224 251 -226 251 -252 225 -252 213 -250 261 -222 253 -224 253 -252 225 -242 245 -230 245 -252 225 -252 251 -226 241 -220 255 -246 249 -226 251 -226 251 -254 213 -248 233 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 233 -248 253 -224 253 -252 223 -242 245 -232 245 -252 223 -252 253 -224 241 -222 255 -244 251 -226 251 -224 253 -252 213 -246 235 -248 251 -254 223 -252 225 -242 245 -232 245 -252 223 -254 251 -226 241 -222 253 -246 251 -224 253 -224 251 -254 213 -248 233 -248 251 -226 251 -254 223 -242 245 -230 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 251 -254 213 -248 233 -248 253 -224 253 -252 225 -252 213 -248 261 -224 251 -226 251 -252 225 -242 245 -230 245 -254 223 -252 253 -224 241 -222 253 -246 251 -226 251 -224 251 -254 213 -248 259 -224 251 -226\nRAW_Data: 251 -254 223 -242 245 -230 245 -252 225 -252 251 -226 251 -214 247 -234 249 -252 251 -226 251 -224 241 -248 229 -248 251 -224 253 -224 251 -254 213 -246 261 -224 251 -226 251 -252 225 -242 245 -230 247 -252 223 -252 253 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 233 -248 251 -252 225 -252 225 -242 245 -232 245 -252 223 -254 251 -224 253 -214 247 -236 247 -252 251 -226 251 -226 241 -246 231 -246 251 -224 253 -224 251 -254 213 -248 233 -248 253 -252 223 -254 223 -242 245 -232 245 -252 223 -254 251 -226 241 -220 255 -244 251 -226 251 -226 251 -252 213 -248 233 -250 251 -226 251 -252 225 -242 245 -230 245 -254 223 -252 253 -224 241 -222 253 -246 251 -224 253 -224 253 -252 213 -246 235 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -226 251 -214 247 -234 247 -254 251 -224 253 -224 241 -246 231 -246 251 -226 251 -226 251 -252 213 -248 261 -222 253 -224 253 -252 225 -242 245 -230 245 -254 223 -252 253 -224 253 -214 245 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -226 251 -252 225 -252 213 -248 235 -248 251 -254 223 -252 225 -242 245 -230 247 -252 223 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 215 -246 235 -248 251 -224 479 -484 501 -482 461 -492 483 -466 255 -234 245 -252 223 -254 251 -226 241 -222 487 -480 253 -222 489 -480 251 -246 465 -482 251 -246 231 -246 483 -468 255 -234 481 -488 233 -246 233 -248 483 -466 255 -234 245 -252 225 -252 251 -226 469 -494 483 -488 483 -482 221 -250 473 -488 479 -490 231 -248 231 -246 251 -226 251 -226 251 -252 467 -476 249 -252 223 -252 469 -494 481 -470 485 -484 247 -252 469 -462 481 -498 229 -252 243 -252 225 -252 223 -254 251 -214 477 -484 247 -250 251 -214 247 -234 247 -252 481 -460 261 -244 251 -226 479 -486 1435 -1456 231 -236 247 -252 225 -252 253 -224 241 -222 253 -246 251 -224 253 -224 253 -252 213 -246 261 -222 251 -226 251 -252 225 -242 245 -230 247 -252 481 -462 493 -480 481 -492 481 -462 255 -224 263 -222 253 -252 223 -254 223 -242 475 -484 249 -222 505 -488 245 -222 469 -496 247 -252 223 -254 469 -490 249 -224 477 -482 237 -250 249 -226 479 -484 239 -246 251 -226 251 -226 251 -252 467 -474 481 -490 491 -456 271 -224 469 -494 481 -492 229 -236 245 -252 225 -252 251 -226 241 -222 487 -482 251 -246 231 -246 483 -468 487 -482 473 -486 265 -222 485 -492 461 -484 247 -250\nRAW_Data: 253 -214 245 -234 247 -252 225 -252 473 -486 243 -252 225 -254 223 -254 251 -214 477 -486 245 -252 223 -242 477 -484 1463 -1444 249 -222 253 -224 241 -246 233 -248 251 -226 251 -254 223 -252 213 -248 261 -224 251 -226 251 -254 223 -242 245 -232 245 -252 223 -252 253 -224 469 -496 483 -488 457 -506 479 -462 259 -244 223 -252 253 -224 251 -226 241 -246 467 -478 253 -246 467 -480 251 -248 465 -480 251 -248 229 -248 483 -468 255 -232 483 -474 255 -224 265 -224 483 -492 229 -236 249 -252 223 -252 253 -224 469 -496 479 -482 491 -480 225 -246 485 -482 491 -462 245 -252 253 -224 253 -224 241 -246 231 -246 483 -488 229 -250 241 -252 457 -490 489 -460 497 -484 243 -224 483 -490 489 -458 249 -248 249 -240 249 -226 251 -226 251 -226 493 -472 249 -252 223 -252 223 -242 245 -234 483 -488 233 -246 233 -248 483 -468 1451 -1448 229 -492 247 -248 79 -400 157 -566 133 -108 379 -218 109 -54 163 -164 51 -102 315 -260 263 -80 463 -234 151 -76 149 -154 249 -126 99 -130 125 -270 127 -188 427 -210 131 -222 593 -106 241 -562 239 -84 305 -262 183 -80 53 -524 125 -480 227 -328 51 -350 211 -82 181 -108 215 -106 147 -200 183 -104 397 -52 131 -102 147 -172 335 -618 177 -206 131 -220 129 -158 213 -52 187 -164 135 -334 157 -410 73 -120 225 -234 133 -108 81 -138 599 -238 131 -262 77 -368 209 -130 103 -76 335 -78 101 -232 253 -280 97 -74 73 -74 97 -52 383 -272 125 -104 81 -82 455 -106 159 -78 127 -52 183 -358 55 -170 139 -86 53 -108 299 -52 129 -282 385 -104 101 -514 75 -232 153 -510 75 -174 97 -128 151 -152 101 -206 103 -76 227 -308 77 -302 129 -76 77 -592 175 -202 75 -314 157 -400 343 -52 185 -104 135 -136 129 -108 111 -112 271 -266 161 -108 107 -240 343 -342 213 -128 99 -124 181 -54 103 -200 101 -274 157 -104 131 -154 123 -588 105 -108 107 -54 161 -52 349 -184 83 -84 53 -54 133 -540 105 -52 53 -160 105 -334 481 -180 101 -304 257 -176 77 -286 79 -106 179 -102 73 -554 285 -184 77 -396 103 -264 185 -106 233 -732 235 -108 53 -82 185 -140 137 -188 285 -294 281 -338 127 -378 77 -242 239 -112 327 -106 219 -240 55 -54 109 -584 51 -134 79 -80 215 -80 109 -54 81 -264 157 -310 51 -130 161 -140 375 -290 129 -886 263 -78 51 -342 55 -104 207 -288 271 -108 179 -74 75 -460 257 -280 77 -106 51 -104 53 -150 125 -174 155 -80 221 -108 161 -310\nRAW_Data: 77 -104 51 -208 79 -452 379 -130 51 -318 237 -214 257 -180 77 -298 79 -604 103 -398 187 -346 181 -154 97 -174 285 -156 51 -102 583 -78 75 -74 151 -102 255 -158 339 -80 77 -234 155 -102 101 -250 75 -76 147 -150 123 -100 75 -78 259 -180 251 -80 51 -104 77 -130 77 -52 159 -236 103 -622 173 -1016 131 -184 153 -156 73 -274 331 -292 105 -204 199 -516 103 -210 157 -104 287 -304 153 -78 129 -78 385 -332 179 -286 181 -178 145 -176 121 -76 169 -146 123 -102 181 -180 51 -176 147 -230 103 -172 75 -208 199 -52 153 -52 75 -134 265 -80 51 -182 157 -110 237 -104 183 -198 213 -168 219 -132 75 -78 101 -76 101 -306 231 -130 99 -152 125 -130 105 -132 135 -110 133 -476 213 -54 325 -322 209 -404 303 -106 81 -166 403 -134 105 -242 403 -104 129 -182 55 -174 165 -168 83 -304 185 -130 133 -108 135 -134 103 -52 181 -362 207 -54 179 -228 231 -236 105 -214 53 -158 77 -452 319 -106 79 -348 349 -406 133 -84 195 -104 239 -158 197 -164 79 -80 79 -54 79 -156 201 -374 941 -492 77 -174 73 -230 75 -278 177 -52 477 -306 129 -106 79 -108 113 -196 127 -328 51 -102 125 -76 75 -102 153 -76 99 -146 373 -134 185 -134 105 -352 105 -436 127 -246 51 -106 79 -238 131 -82 165 -270 109 -108 79 -634 157 -242 105 -78 129 -52 131 -266 215 -270 329 -162 51 -478 209 -52 157 -372 79 -82 367 -560 129 -210 53 -54 133 -106 133 -292 77 -106 159 -84 185 -80 509 -54 473 -80 103 -108 105 -158 51 -180 123 -122 71 -208 129 -210 371 -212 75 -110 341 -164 425 -130 181 -54 313 -278 51 -52 129 -386 263 -78 245 -344 287 -134 133 -130 81 -276 105 -114 129 -106 487 -78 127 -182 179 -146 127 -148 125 -186 133 -182 127 -76 195 -96 103 -76 75 -52 101 -204 77 -102 405 -102 75 -224 125 -276 153 -100 147 -350 259 -156 77 -128 463 -76 77 -234 103 -252 51 -286 75 -232 201 -102 75 -176 175 -124 151 -100 51 -254 97 -74 123 -122 123 -174 201 -154 129 -190 53 -136 81 -78 79 -216 133 -106 111 -84 137 -136 163 -324 103 -78 51 -56 109 -106 177 -168 75 -492 131 -52 131 -180 73 -74 359 -104 101 -102 101 -492 159 -290 215 -80 187 -214 103 -160 135 -130 175 -126 107 -54 75 -78 77 -152 199 -604 97 -74 101 -284 127 -52 177 -434 153 -122 781 -208 79 -80 105 -292 135 -84 247 -318 51 -430 79 -162 109 -56 135 -190 185 -78 159 -130\nRAW_Data: 185 -188 137 -108 331 -56 79 -132 167 -188 107 -320 393 -310 99 -154 51 -180 153 -74 101 -76 229 -128 183 -184 249 -108 299 -82 191 -532 295 -400 103 -80 79 -272 53 -276 213 -84 55 -110 291 -106 123 -204 101 -102 77 -104 157 -102 51 -126 51 -126 183 -140 83 -264 53 -134 261 -128 81 -82 81 -298 81 -266 77 -52 51 -474 265 -366 103 -226 239 -166 677 -232 175 -150 205 -76 123 -76 97 -176 127 -284 77 -76 357 -182 73 -326 171 -104 675 -236 301 -216 53 -84 107 -52 157 -78 237 -136 289 -130 215 -350 77 -320 187 -110 347 -240 159 -134 79 -486 207 -272 129 -82 293 -188 105 -212 77 -216 445 -836 151 -150 73 -262 75 -102 253 -52 101 -102 281 -386 149 -230 355 -212 51 -130 329 -224 149 -52 75 -204 77 -108 85 -194 341 -134 269 -132 161 -56 81 -556 187 -448 395 -130 177 -136 107 -184 239 -136 53 -214 135 -266 79 -368 109 -372 315 -78 79 -78 103 -132 53 -82 81 -104 129 -182 125 -74 103 -236 211 -294 205 -216 131 -108 211 -236 79 -54 103 -104 161 -54 265 -54 187 -162 107 -134 183 -190 237 -104 51 -186 107 -164 137 -102 77 -206 197 -238 107 -52 53 -78 155 -126 201 -78 125 -204 103 -102 75 -104 253 -76 105 -238 159 -366 209 -216 165 -56 55 -322 585 -80 183 -108 81 -136 109 -168 211 -236 295 -674 337 -78 211 -312 77 -242 297 -378 215 -190 79 -298 161 -108 183 -122 121 -252 103 -516 105 -54 123 -226 291 -78 77 -104 53 -484 197 -452 51 -306 77 -106 127 -178 51 -256 209 -264 101 -76 101 -126 151 -126 251 -1152 359 -126 325 -254 551 -156 83 -666 161 -236 105 -186 103 -108 161 -80 157 -54 51 -344 159 -82 349 -160 141 -270 131 -578 189 -162 287 -134 161 -712 127 -106 51 -108 345 -704 431 -362 51 -52 415 -208 233 -342 257 -288 131 -80 77 -252 53 -330 105 -160 83 -280 393 -358 103 -480 161 -134 51 -54 259 -104 129 -130 105 -80 217 -350 269 -192 57 -302 79 -264 81 -326 157 -80 79 -214 51 -370 129 -236 53 -136 105 -82 57 -140 137 -52 79 -236 103 -80 55 -192 293 -188 53 -348 185 -236 207 -56 135 -54 215 -216 587 -132 103 -160 133 -158 365 -52 103 -54 107 -80 185 -298 267 -136 159 -110 109 -82 241 -78 77 -78 101 -82 141 -226 55 -462 79 -188 81 -216 511 -210 79 -162 161 -160 101 -388 157 -180 101 -100 197 -336 581 -130 211 -264 155 -52 297 -180 75 -100 73 -100 247 -130\nRAW_Data: 133 -346 505 -108 105 -80 243 -292 79 -104 339 -78 75 -128 287 -398 99 -300 479 -104 257 -130 153 -78 359 -152 79 -300 77 -348 109 -82 217 -108 243 -80 185 -430 133 -460 211 -234 207 -52 291 -80 77 -54 81 -82 265 -78 101 -132 255 -122 73 -512 129 -126 77 -158 213 -168 81 -82 479 -160 159 -108 157 -158 133 -236 129 -128 485 -126 153 -412 99 -250 255 -274 173 -358 231 -76 53 -78 131 -54 135 -290 77 -102 305 -128 77 -310 183 -80 239 -80 135 -166 189 -104 81 -140 111 -164 477 -488 51 -134 53 -80 429 -240 529 -476 287 -80 189 -136 327 -84 79 -160 293 -348 133 -148 179 -52 231 -102 175 -76 125 -100 425 -228 77 -200 183 -154 231 -212 253 -78 299 -132 145 -124 337 -150 101 -132 99 -274 153 -182 53 -158 51 -160 453 -134 427 -182 77 -134 275 -528 77 -108 79 -110 109 -110 245 -136 171 -164 473 -106 265 -80 267 -184 179 -330 287 -212 185 -350 129 -630 467 -350 489 -166 107 -56 79 -270 53 -84 757 -624 207 -102 51 -416 287 -288 103 -406 73 -132 51 -214 135 -644 103 -146 73 -358 629 -736 309 -150 275 -254 51 -106 53 -54 105 -80 53 -180 77 -110 297 -106 79 -418 261 -80 161 -80 77 -480 79 -156 215 -190 601 -130 353 -76 73 -76 127 -198 125 -100 531 -78 77 -438 103 -134 191 -312 291 -108 53 -134 53 -214 317 -80 277 -182 279 -302 329 -104 479 -182 203 -98 273 -222 247 -250 241 -252 219 -248 221 -254 243 -252 225 -254 223 -252 253 -214 245 -236 247 -252 251 -226 251 -224 241 -222 255 -246 251 -226 251 -226 251 -252 213 -248 235 -248 253 -224 253 -252 225 -242 245 -230 245 -252 223 -254 251 -224 253 -214 247 -236 247 -252 251 -226 251 -226 241 -222 255 -246 251 -224 253 -224 251 -254 213 -248 233 -248 253 -224 253 -252 223 -242 247 -230 245 -252 225 -252 253 -224 251 -214 247 -236 247 -254 251 -224 253 -224 241 -246 231 -246 251 -226 251 -224 253 -252 213 -248 235 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 241 -222 253 -246 251 -224 253 -224 253 -252 213 -248 233 -248 251 -226 253 -252 223 -242 245 -232 245 -252 225 -252 253 -224 251 -214 247 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 253 -252 225 -252 213 -248 233 -248 253 -252 225 -252 223 -244 245 -230 245 -254 223 -252 253 -224 253 -214 245 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 253 -252 223 -252\nRAW_Data: 213 -248 263 -222 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 253 -214 247 -234 249 -252 251 -226 251 -224 241 -246 231 -246 251 -226 251 -254 223 -252 213 -248 261 -222 253 -252 223 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 253 -252 213 -246 261 -222 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 241 -222 253 -246 251 -224 253 -224 253 -252 213 -248 235 -248 251 -224 251 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 253 -252 213 -246 261 -222 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 243 -220 253 -246 251 -226 251 -224 251 -254 213 -248 233 -248 251 -254 223 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 253 -252 213 -248 233 -248 251 -226 251 -252 225 -242 245 -230 247 -252 223 -254 251 -224 253 -214 245 -236 247 -254 251 -224 253 -224 241 -246 231 -246 251 -224 253 -224 253 -242 219 -256 243 -254 223 -254 251 -224 253 -214 245 -236 247 -252 251 -226 251 -224 243 -246 229 -248 251 -224 253 -252 223 -254 213 -246 261 -224 251 -226 251 -254 223 -242 245 -232 245 -252 223 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 233 -248 253 -224 253 -252 223 -254 213 -248 263 -222 253 -224 253 -252 223 -242 245 -232 245 -252 223 -254 251 -226 241 -220 253 -246 251 -226 251 -224 253 -252 213 -248 261 -222 251 -226 251 -254 223 -242 245 -232 243 -252 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 251 -254 213 -246 261 -222 253 -224 253 -252 223 -242 247 -230 245 -252 223 -254 251 -224 243 -220 255 -246 249 -226 251 -226 251 -254 213 -246 235 -248 251 -226 251 -252 225 -242 245 -230 245 -252 225 -252 253 -224 251 -214 247 -236 247 -254 251 -224 253 -224 241 -246 231 -246 251 -226 251 -224 253 -252 213 -248 261 -222 251 -226 253 -252 223 -242 245 -232 245 -252 225 -252 253 -224 251 -214 247 -234 249 -252 251 -226 251 -224 241 -246 231 -246 251 -226 251 -226 251 -254 213 -248 261 -220 253 -252 225 -252 223 -242 247 -230 245 -254 479 -462 495 -482 479 -490 481 -470 229 -254 245 -250 225 -252 223 -254 251 -214 503 -458 249 -248 481 -484 243 -224 495 -472 249 -252 223 -252 471 -490 249 -224 479 -486 473 -482 241 -242 477 -482 247 -250 253 -214 245 -234 247 -252 481 -462 495 -482\nRAW_Data: 465 -488 245 -252 479 -480 473 -488 243 -252 213 -246 261 -224 251 -224 253 -252 469 -466 245 -254 251 -224 473 -486 481 -480 485 -482 247 -224 505 -458 499 -484 243 -252 223 -252 225 -252 223 -242 247 -232 483 -490 485 -472 249 -252 473 -484 243 -224 483 -492 231 -234 485 -488 487 -472 1445 -1448 237 -248 251 -226 251 -224 253 -252 213 -246 261 -222 251 -254 223 -252 225 -242 245 -230 247 -252 223 -254 251 -224 243 -220 255 -244 251 -226 477 -482 501 -454 507 -480 479 -462 259 -244 251 -224 253 -224 251 -226 241 -246 493 -464 241 -242 475 -484 249 -222 505 -484 225 -246 251 -242 485 -470 229 -254 477 -480 485 -482 247 -224 495 -464 273 -224 253 -224 251 -226 241 -246 493 -480 479 -470 481 -480 233 -266 459 -488 485 -476 247 -252 223 -254 251 -214 245 -234 247 -252 481 -462 259 -244 251 -226 479 -486 469 -482 481 -492 247 -224 477 -482 503 -464 241 -242 245 -232 245 -254 223 -252 253 -224 469 -496 483 -486 229 -252 477 -476 227 -248 497 -480 227 -254 477 -480 483 -482 1439 -1452 245 -252 223 -254 223 -252 253 -214 245 -234 247 -252 225 -252 251 -226 241 -222 253 -246 251 -226 251 -226 251 -252 213 -248 261 -222 253 -224 479 -484 501 -482 461 -492 483 -466 255 -234 245 -252 225 -252 253 -224 241 -222 487 -480 253 -222 489 -480 251 -248 463 -482 251 -246 231 -246 483 -488 229 -252 477 -478 485 -480 223 -248 479 -484 247 -250 251 -214 247 -234 247 -252 481 -460 493 -482 479 -492 249 -224 477 -480 477 -488 243 -240 245 -234 245 -252 223 -254 251 -226 469 -494 247 -224 253 -252 469 -488 483 -470 485 -482 247 -224 497 -462 483 -490 231 -248 233 -248 251 -226 251 -252 225 -242 475 -484 475 -484 239 -250 483 -468 255 -232 483 -490 231 -246 493 -480 481 -470 1445 -1448 239 -772 105 -194 247 -314 105 -176 125 -126 151 -382 51 -266 51 -104 55 -400 129 -114 243 -210 343 -78 161 -108 107 -110 53 -214 83 -242 209 -80 155 -82 53 -268 391 -80 79 -214 211 -132 131 -320 77 -320 157 -138 191 -136 103 -202 99 -446 213 -406 131 -286 101 -258 51 -188 55 -82 81 -162 237 -130 183 -512 291 -80 193 -342 77 -106 159 -272 77 -52 53 -78 183 -544 79 -106 277 -338 185 -132 153 -108 107 -108 163 -54 107 -134 235 -356 523 -108 137 -134 291 -80 79 -214 213 -266 137 -54 53 -162 165 -84 191 -78 53 -132 213 -318 79 -264 215 -320 159 -82 53 -246 131 -132 175 -102 227 -228\nRAW_Data: 315 -106 53 -872 139 -136 209 -332 177 -272 417 -180 51 -52 181 -338 77 -130 51 -104 151 -52 127 -152 99 -76 155 -126 203 -180 151 -126 101 -200 179 -384 123 -592 153 -80 127 -466 75 -100 121 -98 123 -126 97 -336 359 -104 107 -192 55 -80 103 -190 81 -132 79 -78 207 -128 109 -216 183 -270 105 -80 129 -52 51 -354 85 -194 135 -238 243 -82 81 -370 295 -130 135 -136 137 -190 211 -188 81 -184 53 -80 159 -82 55 -350 319 -160 355 -210 99 -462 53 -130 277 -204 619 -184 471 -78 131 -378 453 -162 171 -86 165 -222 81 -82 315 -210 101 -78 383 -102 101 -180 207 -416 183 -310 259 -200 225 -146 333 -460 185 -78 211 -192 85 -84 353 -82 79 -80 213 -158 75 -156 131 -226 133 -54 107 -134 105 -106 131 -80 267 -132 187 -192 271 -110 105 -82 79 -270 135 -102 185 -134 185 -54 191 -134 249 -368 53 -238 79 -54 185 -526 51 -376 243 -586 109 -346 81 -82 179 -78 267 -206 177 -100 125 -104 173 -74 77 -152 155 -186 83 -190 351 -164 135 -106 109 -142 337 -214 81 -188 79 -78 103 -228 101 -226 175 -214 315 -52 79 -184 77 -240 291 -54 79 -80 51 -624 135 -134 203 -146 223 -74 135 -238 133 -134 129 -380 133 -190 79 -108 241 -186 181 -416 155 -134 157 -128 305 -134 425 -508 283 -128 181 -826 127 -268 77 -368 261 -54 189 -374 415 -156 201 -178 177 -206 127 -234 79 -380 133 -54 161 -236 53 -184 79 -298 53 -294 77 -312 177 -154 77 -208 369 -78 53 -160 215 -106 557 -320 473 -274 343 -78 99 -100 119 -72 155 -98 123 -76 257 -280 51 -260 157 -78 127 -52 99 -74 569 -302 131 -428 77 -336 101 -102 177 -230 97 -124 51 -102 75 -156 251 -126 173 -326 79 -428 123 -380 281 -238 161 -158 157 -230 395 -202 123 -220 255 -104 97 -198 77 -100 551 -76 101 -256 231 -410 77 -102 75 -230 105 -158 51 -132 105 -340 215 -108 241 -134 129 -160 191 -292 51 -852 155 -186 77 -52 103 -578 77 -106 209 -132 107 -130 53 -164 455 -78 155 -244 185 -160 217 -216 381 -130 127 -514 209 -82 55 -136 103 -104 173 -226 231 -176 73 -130 205 -102 319 -244 53 -160 107 -162 57 -350 97 -230 203 -252 127 -360 79 -316 103 -180 101 -178 185 -216 101 -302 257 -182 103 -126 75 -152 103 -102 77 -284 413 -612 177 -432 73 -460 79 -78 53 -556 155 -664 75 -214 327 -134 79 -134 133 -136 51 -82 103 -130 145 -74 199 -128 51 -102 103 -482\nRAW_Data: 179 -100 177 -52 103 -158 191 -110 81 -190 217 -326 51 -280 147 -602 209 -76 99 -124 293 -108 193 -192 241 -184 131 -106 159 -52 77 -132 53 -104 101 -188 79 -538 105 -80 107 -52 213 -428 243 -84 81 -132 265 -140 77 -80 51 -214 189 -140 301 -162 75 -102 231 -178 123 -264 123 -98 125 -76 103 -238 77 -228 183 -140 165 -552 297 -54 291 -186 185 -234 155 -78 79 -180 53 -82 505 -182 103 -128 123 -280 133 -158 103 -644 213 -330 77 -106 79 -424 155 -212 163 -192 189 -508 369 -80 79 -220 111 -166 79 -304 133 -108 139 -140 189 -78 51 -348 289 -104 187 -54 109 -508 131 -108 283 -516 99 -100 299 -380 199 -152 325 -304 229 -476 75 -148 101 -102 75 -208 77 -74 125 -74 73 -76 181 -176 179 -186 235 -130 79 -184 77 -152 121 -304 127 -484 103 -152 173 -100 157 -176 185 -82 177 -156 309 -106 79 -132 51 -236 179 -154 73 -236 77 -74 71 -160 235 -130 105 -108 247 -56 53 -208 75 -74 75 -128 505 -154 179 -76 51 -278 103 -178 99 -126 183 -54 161 -526 133 -80 79 -298 105 -236 77 -264 139 -54 243 -164 459 -208 51 -236 105 -370 79 -374 77 -182 129 -202 77 -158 107 -108 107 -82 245 -244 109 -82 81 -292 179 -52 129 -164 423 -52 153 -102 151 -100 125 -76 259 -78 365 -240 107 -162 105 -244 163 -56 163 -108 159 -52 79 -260 79 -502 133 -82 323 -552 105 -210 699 -78 189 -192 105 -128 165 -188 105 -158 77 -158 79 -54 131 -498 131 -130 101 -176 125 -170 213 -52 77 -106 53 -292 389 -104 133 -402 133 -80 109 -54 53 -104 79 -182 505 -54 161 -264 181 -212 107 -164 83 -244 315 -234 127 -130 147 -150 231 -456 101 -124 73 -104 287 -320 81 -134 107 -244 267 -78 157 -108 137 -84 133 -54 79 -186 107 -262 77 -80 81 -240 53 -82 107 -136 245 -110 185 -294 259 -134 135 -80 159 -566 107 -190 187 -190 79 -54 107 -80 107 -54 135 -164 165 -56 79 -54 349 -78 75 -104 157 -320 53 -82 421 -180 97 -172 105 -106 111 -280 163 -132 193 -250 107 -318 155 -266 109 -162 183 -242 105 -484 79 -106 125 -246 99 -492 75 -180 127 -100 95 -196 75 -152 537 -214 53 -186 103 -134 81 -108 109 -560 51 -382 153 -104 51 -188 373 -240 53 -266 129 -622 51 -132 81 -58 137 -364 263 -78 235 -82 177 -486 105 -276 157 -108 237 -80 105 -132 131 -258 289 -102 51 -104 103 -104 73 -508 283 -102 51 -230 99 -178 105 -106\nRAW_Data: 53 -190 105 -160 157 -158 129 -80 241 -162 277 -260 77 -312 157 -320 193 -108 77 -184 77 -102 125 -286 107 -82 371 -266 447 -218 271 -106 51 -162 107 -194 333 -264 99 -76 75 -150 345 -444 105 -696 75 -154 185 -348 105 -108 159 -296 161 -80 107 -222 81 -106 239 -80 75 -78 105 -52 323 -246 51 -162 79 -130 157 -618 241 -82 107 -106 133 -294 137 -108 53 -104 107 -80 51 -80 267 -106 77 -212 105 -84 81 -56 131 -132 211 -78 187 -138 131 -104 53 -78 311 -210 77 -80 109 -160 239 -208 51 -292 443 -158 167 -138 185 -232 293 -78 181 -136 53 -186 107 -108 53 -80 395 -130 299 -160 53 -162 109 -56 135 -468 75 -102 229 -396 105 -98 277 -386 339 -356 125 -226 291 -270 165 -80 79 -106 77 -244 189 -162 215 -672 343 -184 75 -450 131 -208 57 -114 107 -420 53 -106 81 -188 103 -136 501 -110 211 -76 161 -294 107 -54 55 -206 75 -100 75 -432 77 -248 51 -78 419 -52 535 -106 207 -230 99 -124 179 -100 71 -76 77 -400 53 -340 185 -188 235 -106 105 -220 55 -134 53 -346 53 -192 291 -346 209 -242 185 -158 51 -128 75 -176 153 -282 159 -158 73 -330 105 -634 323 -242 79 -210 181 -52 103 -54 107 -108 105 -330 105 -272 51 -206 227 -154 129 -194 285 -80 79 -266 127 -492 131 -432 181 -108 213 -242 107 -346 207 -78 157 -160 53 -108 53 -322 83 -674 317 -188 243 -186 81 -166 111 -84 535 -78 181 -134 327 -240 103 -206 77 -78 177 -152 121 -70 75 -100 535 -152 75 -78 101 -368 267 -168 217 -688 383 -222 247 -234 275 -224 251 -226 251 -224 241 -222 255 -246 251 -224 253 -224 251 -252 215 -246 261 -222 253 -224 253 -252 223 -242 245 -232 245 -252 225 -252 253 -224 253 -214 245 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 251 -254 223 -242 221 -254 245 -252 225 -252 251 -226 251 -214 247 -236 247 -252 253 -224 251 -226 241 -246 229 -246 251 -226 251 -254 223 -252 213 -248 261 -224 251 -226 251 -252 225 -242 245 -230 245 -254 223 -252 253 -224 253 -214 245 -236 247 -252 251 -226 251 -224 243 -246 229 -248 251 -224 253 -252 223 -254 213 -248 261 -222 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 261 -220 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 243 -220 255 -246 249 -226 251 -226 251 -252 213 -248 235 -248 251 -226 251 -252\nRAW_Data: 225 -242 245 -230 247 -252 223 -252 253 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 233 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 253 -214 247 -234 249 -252 251 -226 251 -224 241 -246 231 -246 251 -226 251 -226 251 -252 213 -248 261 -224 251 -226 251 -252 225 -242 245 -230 245 -252 225 -252 253 -224 253 -212 247 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 253 -252 225 -252 213 -248 261 -222 253 -224 253 -252 223 -242 247 -230 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 251 -252 213 -248 233 -250 251 -224 253 -252 225 -242 245 -230 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 253 -252 213 -248 233 -250 251 -224 253 -252 223 -242 245 -232 245 -252 223 -254 251 -224 253 -214 247 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 253 -252 223 -252 213 -248 261 -224 251 -252 223 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 251 -254 213 -246 261 -224 251 -226 251 -252 225 -242 245 -230 245 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 261 -222 251 -226 251 -254 223 -242 245 -232 243 -252 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 233 -248 251 -254 223 -252 225 -242 245 -232 245 -252 223 -254 251 -224 241 -222 255 -244 251 -226 251 -226 251 -254 213 -246 235 -248 251 -226 251 -254 223 -242 245 -230 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 251 -254 213 -248 233 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -226 241 -220 255 -246 249 -226 253 -224 251 -254 213 -246 235 -248 251 -226 251 -252 225 -242 245 -230 245 -252 225 -252 253 -224 241 -220 255 -246 249 -226 251 -226 251 -252 213 -248 261 -222 253 -224 253 -252 223 -242 247 -230 245 -252 225 -252 251 -226 251 -214 247 -234 249 -252 251 -226 251 -224 241 -248 229 -248 249 -226 253 -252 223 -252 213 -248 263 -222 251 -226 251 -254 223 -242 245 -230 245 -254 223 -252 253 -224 241 -222 255 -244 251 -226 251 -226 251 -252 213 -248 233 -250 251 -226 251 -252 225 -252 213 -248 263 -222 251 -226 251 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 485 -488 457 -504 481 -462 493 -480 227 -246 251 -244 251 -226 251 -224 253 -224 493 -474 247 -252 475 -486 243 -222 485 -492\nRAW_Data: 231 -234 247 -252 481 -462 493 -480 225 -248 251 -244 251 -224 479 -486 237 -246 251 -226 251 -224 253 -252 467 -474 481 -492 489 -458 245 -250 469 -494 481 -480 231 -238 247 -252 253 -224 253 -224 241 -246 465 -488 243 -240 245 -234 481 -488 487 -472 481 -492 229 -236 485 -474 485 -478 249 -250 251 -226 241 -246 231 -246 251 -224 479 -486 237 -248 485 -470 253 -232 245 -252 223 -254 473 -486 243 -224 251 -254 1439 -1448 249 -224 251 -226 251 -224 241 -246 233 -248 251 -226 251 -254 223 -242 245 -230 245 -252 225 -252 251 -226 241 -222 253 -246 249 -226 253 -224 497 -462 483 -490 485 -472 479 -480 259 -244 223 -254 251 -224 253 -224 241 -246 467 -488 243 -240 477 -484 247 -250 469 -494 247 -224 253 -224 499 -462 481 -488 233 -248 233 -248 251 -224 477 -486 239 -248 253 -224 253 -252 223 -254 467 -476 479 -490 491 -458 271 -224 469 -496 479 -480 233 -238 249 -254 251 -224 253 -224 241 -222 489 -482 269 -212 247 -234 483 -500 483 -480 481 -460 261 -244 485 -460 485 -478 247 -246 223 -248 261 -222 249 -222 247 -252 479 -488 243 -238 479 -482 225 -248 249 -242 251 -226 479 -486 237 -246 251 -226 1443 -1446 243 -252 225 -252 225 -252 253 -212 247 -234 247 -252 253 -224 251 -226 241 -246 231 -246 251 -226 251 -224 253 -252 213 -248 261 -222 251 -226 477 -484 503 -462 489 -490 479 -482 229 -238 247 -252 223 -254 251 -224 243 -220 489 -480 251 -246 467 -480 251 -248 465 -488 243 -240 247 -232 481 -488 487 -472 249 -252 223 -254 223 -242 475 -486 245 -252 223 -242 247 -234 247 -252 481 -460 493 -482 481 -490 249 -224 477 -482 475 -480 243 -240 247 -234 247 -252 251 -226 251 -224 469 -496 247 -254 223 -252 469 -490 483 -462 483 -478 247 -252 473 -486 479 -478 231 -246 261 -224 251 -254 223 -252 225 -242 475 -482 249 -222 505 -486 245 -224 241 -222 255 -246 483 -468 255 -232 245 -252 1445 -1446 243 -460 421 -100 233 -78 103 -126 73 -364 133 -274 163 -238 509 -132 51 -238 211 -812 77 -208 245 -276 261 -184 309 -1392 587 -182 209 -182 101 -122 121 -128 99 -300 175 -154 283 -210 51 -136 163 -506 655 -132 77 -76 157 -242 265 -214 243 -242 163 -574 605 -336 101 -102 331 -78 741 -52 255 -128 51 -178 127 -200 129 -462 179 -132 165 -108 53 -78 159 -728 157 -134 109 -162 129 -108 239 -344 199 -328 99 -102 209 -384 101 -154 229 -78 157 -52 103 -976 259 -102 123 -76 123 -102\nRAW_Data: 73 -200 79 -52 131 -212 55 -402 285 -212 315 -56 573 -110 81 -54 161 -214 53 -390 351 -106 53 -316 129 -230 131 -162 163 -136 55 -140 263 -54 137 -192 313 -184 211 -322 53 -108 139 -56 719 -104 323 -236 263 -138 355 -162 135 -266 155 -156 103 -76 251 -284 335 -102 123 -252 185 -190 237 -394 75 -78 79 -132 81 -328 109 -82 179 -530 163 -160 85 -56 375 -52 351 -416 79 -160 107 -492 53 -108 109 -280 53 -52 235 -216 303 -84 131 -160 79 -80 243 -54 215 -82 165 -82 77 -182 207 -330 107 -264 451 -402 131 -134 53 -464 103 -354 51 -188 163 -154 133 -266 105 -52 209 -430 51 -108 107 -186 131 -426 51 -108 55 -82 179 -164 135 -104 399 -52 105 -214 105 -312 135 -188 51 -242 107 -134 79 -314 77 -54 133 -54 105 -164 135 -318 81 -232 151 -180 127 -102 99 -102 453 -130 299 -126 99 -798 235 -206 149 -100 79 -108 955 -80 79 -210 107 -132 131 -196 291 -134 159 -80 377 -312 159 -242 107 -52 53 -156 51 -132 79 -54 133 -274 157 -54 105 -134 387 -104 129 -234 77 -254 361 -52 77 -104 101 -52 53 -54 561 -80 163 -54 109 -216 51 -498 535 -82 163 -210 51 -182 259 -102 75 -76 305 -102 253 -172 215 -268 77 -52 81 -186 133 -106 333 -162 111 -220 569 -102 215 -54 131 -196 433 -80 51 -158 129 -108 53 -188 261 -52 101 -100 227 -402 201 -182 77 -78 175 -126 75 -306 231 -100 149 -178 489 -228 221 -154 77 -220 81 -52 51 -308 233 -104 77 -156 73 -286 181 -132 133 -144 165 -160 109 -54 105 -236 153 -76 211 -372 133 -140 105 -104 77 -320 653 -54 159 -54 181 -162 133 -406 213 -80 79 -132 211 -154 233 -138 189 -84 165 -200 217 -298 107 -272 161 -110 135 -172 103 -102 291 -56 109 -56 317 -52 105 -54 587 -1018 53 -132 109 -56 187 -80 109 -160 81 -80 107 -188 53 -56 237 -158 79 -154 127 -76 51 -204 103 -106 83 -54 313 -52 187 -186 183 -82 189 -474 133 -82 189 -110 155 -136 55 -192 133 -80 183 -106 233 -52 79 -188 187 -158 77 -106 133 -242 111 -106 81 -108 53 -130 245 -290 131 -54 163 -356 287 -156 51 -80 155 -86 687 -104 53 -290 341 -108 217 -160 51 -188 215 -52 133 -210 161 -186 185 -244 51 -210 51 -184 187 -84 133 -108 101 -126 123 -52 77 -150 255 -132 79 -820 183 -108 263 -340 79 -110 137 -82 243 -80 81 -162 51 -132 131 -136 83 -78 181 -126 299 -80 571 -134 105 -80\nRAW_Data: 187 -216 109 -112 359 -316 79 -108 189 -82 133 -266 319 -544 311 -314 51 -162 165 -136 107 -162 193 -194 427 -104 269 -136 107 -80 343 -106 207 -266 53 -80 75 -178 99 -74 169 -506 51 -150 149 -620 207 -266 325 -106 183 -184 161 -106 141 -86 137 -134 239 -214 289 -104 51 -52 77 -106 129 -344 51 -372 75 -302 361 -214 393 -290 103 -106 135 -82 81 -82 185 -136 55 -194 81 -164 81 -78 101 -168 83 -186 81 -222 105 -52 105 -186 79 -418 129 -324 125 -128 75 -256 407 -414 131 -108 107 -80 135 -54 693 -52 369 -80 159 -162 53 -370 79 -162 55 -164 463 -236 77 -230 99 -178 153 -230 129 -80 161 -82 241 -322 79 -216 105 -216 81 -244 105 -326 289 -210 105 -82 299 -52 473 -496 159 -52 79 -108 249 -80 179 -132 107 -84 303 -370 239 -162 239 -126 149 -430 75 -52 123 -304 637 -238 109 -160 379 -190 647 -370 157 -294 235 -292 133 -108 135 -900 183 -56 241 -414 77 -210 161 -190 53 -184 185 -54 103 -394 131 -196 135 -546 151 -76 73 -100 201 -102 125 -316 259 -208 351 -160 81 -456 107 -232 129 -126 75 -228 101 -78 75 -230 431 -102 101 -172 201 -132 107 -108 303 -166 55 -458 203 -76 51 -232 131 -168 85 -198 137 -80 79 -84 81 -80 131 -194 585 -292 155 -722 221 -588 371 -132 101 -246 107 -270 81 -136 81 -262 289 -108 217 -218 77 -104 219 -342 259 -106 187 -78 133 -594 51 -132 267 -80 107 -54 53 -134 131 -192 243 -52 77 -102 155 -78 79 -104 79 -286 353 -192 53 -84 133 -184 75 -198 127 -132 123 -464 407 -130 103 -102 335 -202 103 -104 129 -176 289 -188 357 -754 255 -386 53 -138 255 -140 135 -426 53 -134 237 -52 187 -196 401 -54 157 -344 159 -192 55 -82 219 -202 215 -164 55 -166 81 -350 105 -158 101 -52 51 -284 129 -366 103 -392 53 -52 367 -312 133 -248 111 -272 211 -242 51 -158 165 -136 163 -54 133 -132 125 -374 229 -230 173 -304 103 -480 75 -78 175 -128 77 -362 127 -246 275 -76 313 -156 105 -158 247 -436 235 -132 71 -222 177 -236 345 -78 343 -78 341 -190 297 -188 77 -78 101 -76 99 -152 333 -154 101 -74 147 -122 75 -152 175 -452 453 -182 261 -284 149 -156 103 -170 109 -236 155 -110 295 -206 77 -132 81 -110 141 -1406 365 -214 79 -110 105 -484 87 -198 53 -106 185 -140 111 -322 77 -444 131 -78 239 -80 103 -182 131 -184 51 -106 107 -270 239 -134 213 -132 53 -316 513 -236 77 -80\nRAW_Data: 163 -664 131 -188 269 -110 135 -200 73 -126 95 -380 53 -128 195 -98 271 -158 147 -224 103 -132 125 -360 231 -336 229 -374 643 -180 247 -104 75 -434 79 -322 107 -138 107 -322 51 -776 157 -78 105 -184 51 -264 433 -82 53 -344 79 -82 161 -110 253 -106 77 -52 101 -78 127 -804 157 -148 73 -74 75 -256 301 -126 99 -76 235 -52 155 -460 77 -130 73 -222 283 -134 53 -110 55 -162 53 -106 263 -78 105 -214 133 -192 107 -208 133 -164 243 -346 105 -276 73 -52 77 -226 131 -408 189 -138 217 -508 77 -214 53 -78 235 -52 213 -372 219 -108 53 -210 207 -52 207 -184 99 -200 103 -54 129 -76 101 -104 101 -74 73 -406 181 -174 509 -300 143 -378 177 -254 101 -216 243 -616 239 -154 99 -312 335 -52 75 -180 127 -202 75 -108 189 -214 101 -384 267 -528 153 -260 367 -214 161 -108 183 -136 107 -130 77 -296 211 -132 189 -134 133 -54 107 -326 51 -266 103 -76 263 -242 313 -284 129 -132 185 -54 275 -138 239 -136 165 -316 129 -212 319 -214 55 -110 237 -312 77 -54 79 -210 227 -76 101 -102 125 -76 251 -104 187 -192 105 -162 79 -404 557 -486 159 -272 157 -84 141 -650 187 -130 205 -596 257 -102 135 -160 101 -270 355 -162 109 -196 187 -104 75 -52 159 -52 107 -266 129 -128 565 -52 133 -54 241 -160 187 -598 51 -184 797 -164 81 -132 161 -160 179 -54 111 -108 159 -164 53 -82 107 -158 161 -162 153 -458 329 -312 79 -218 77 -238 241 -270 307 -110 135 -314 131 -106 53 -354 157 -186 291 -260 563 -80 133 -80 77 -234 219 -84 215 -292 81 -82 135 -54 79 -270 55 -186 77 -78 215 -192 211 -220 109 -158 71 -104 51 -186 263 -230 99 -152 227 -52 75 -102 127 -104 157 -318 107 -112 111 -116 159 -82 105 -218 239 -160 305 -188 99 -52 279 -102 149 -148 259 -180 51 -52 131 -466 135 -460 183 -580 77 -130 51 -106 77 -80 135 -304 107 -160 709 -372 79 -164 53 -158 53 -184 233 -180 107 -466 187 -78 79 -134 291 -78 51 -52 101 -78 75 -78 77 -416 283 -278 131 -106 137 -108 311 -236 247 -252 251 -226 251 -224 243 -220 255 -244 251 -226 251 -226 251 -252 215 -246 235 -248 251 -224 253 -252 223 -244 245 -230 245 -252 225 -252 251 -226 241 -220 255 -246 249 -226 251 -226 251 -252 213 -248 261 -222 251 -254 223 -252 225 -242 245 -230 247 -252 223 -252 253 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 235 -248 251 -226 251 -252\nRAW_Data: 225 -242 245 -230 245 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -254 213 -246 235 -248 251 -226 251 -252 225 -242 245 -230 245 -254 223 -252 253 -224 253 -212 247 -236 247 -252 253 -224 251 -226 241 -246 231 -246 251 -224 253 -252 223 -254 213 -246 263 -222 251 -254 223 -254 223 -242 245 -232 245 -252 225 -252 251 -226 241 -222 253 -246 251 -224 253 -224 253 -252 213 -246 235 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 253 -214 247 -236 247 -252 251 -226 251 -224 243 -246 229 -246 251 -226 251 -226 251 -252 213 -248 261 -222 253 -224 253 -252 223 -242 245 -232 245 -252 225 -252 251 -224 241 -222 253 -246 251 -226 251 -224 251 -254 213 -248 233 -250 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -252 213 -248 259 -222 253 -224 253 -252 223 -242 247 -230 245 -252 225 -252 251 -226 241 -220 255 -244 251 -226 251 -226 251 -252 215 -246 235 -248 253 -224 253 -252 223 -254 213 -248 261 -224 251 -224 253 -252 225 -242 245 -230 245 -252 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 251 -254 213 -248 233 -250 251 -226 251 -254 223 -242 245 -230 245 -252 223 -254 251 -226 241 -220 255 -246 251 -224 253 -224 251 -254 213 -248 233 -248 251 -226 251 -254 223 -242 245 -232 245 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -254 213 -246 235 -248 251 -226 251 -254 223 -252 215 -248 261 -222 253 -224 253 -252 223 -242 247 -230 245 -252 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 259 -222 251 -226 251 -254 223 -242 245 -232 245 -252 225 -252 251 -226 251 -214 247 -234 249 -252 251 -226 251 -224 241 -248 229 -248 249 -226 253 -252 223 -254 213 -246 261 -224 251 -254 223 -252 225 -242 245 -230 247 -252 223 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 259 -224 251 -226 251 -252 225 -242 245 -230 245 -252 225 -252 253 -224 251 -214 247 -236 247 -254 251 -224 253 -224 241 -246 231 -246 251 -226 251 -252 225 -252 213 -248 261 -222 251 -226 251 -254 223 -242 245 -230 247 -252 223 -254 251 -224 243 -220 255 -244 251 -226 251 -226 251 -254 213 -248 233 -248 251 -226 251 -252 225 -242 245 -230 245 -254 223 -252 253 -224 241 -222 255 -244 251 -226 251 -226 251 -252 213 -248 233 -248 251 -254 223 -254 223 -242 245 -232\nRAW_Data: 245 -252 223 -254 251 -226 241 -220 255 -244 251 -226 251 -226 251 -252 213 -248 261 -222 251 -254 223 -252 225 -242 245 -230 245 -254 223 -252 253 -224 241 -222 253 -246 251 -224 253 -224 251 -254 213 -248 233 -250 251 -224 253 -252 225 -242 243 -232 245 -252 225 -252 253 -224 241 -222 253 -246 251 -226 251 -224 253 -252 213 -248 233 -248 251 -226 477 -486 501 -456 487 -494 483 -466 255 -232 247 -252 223 -254 251 -226 251 -214 503 -458 243 -252 469 -496 247 -224 477 -486 239 -250 251 -226 477 -486 473 -480 253 -246 467 -488 241 -242 475 -484 247 -224 247 -264 221 -252 251 -226 471 -488 479 -476 485 -502 221 -252 473 -486 479 -490 231 -248 229 -248 249 -226 251 -254 223 -242 477 -482 247 -252 223 -242 475 -482 479 -486 481 -480 253 -232 481 -488 487 -472 247 -252 223 -254 223 -242 245 -232 247 -252 483 -460 493 -488 467 -488 479 -490 231 -248 231 -248 483 -466 487 -484 247 -250 1433 -1456 225 -246 251 -240 251 -224 253 -224 251 -226 241 -246 231 -248 251 -226 251 -254 223 -242 245 -230 245 -252 223 -254 251 -224 253 -214 247 -234 483 -498 483 -482 477 -486 471 -488 243 -250 213 -248 235 -248 251 -254 223 -254 467 -490 249 -224 477 -482 237 -248 485 -468 255 -232 245 -252 481 -462 493 -482 225 -248 483 -480 225 -248 485 -480 251 -222 255 -244 251 -226 251 -252 469 -464 483 -498 483 -482 247 -224 471 -488 481 -480 253 -234 245 -252 223 -252 253 -224 251 -214 505 -460 245 -252 251 -214 503 -458 505 -462 483 -498 229 -250 477 -480 485 -482 247 -224 251 -252 213 -248 233 -250 251 -226 477 -486 475 -488 489 -492 483 -462 231 -248 261 -224 483 -492 463 -484 249 -248 1445 -1430 255 -234 245 -252 223 -254 251 -224 253 -214 245 -236 247 -252 251 -226 251 -226 241 -246 231 -246 251 -224 253 -252 223 -254 213 -248 235 -248 485 -492 459 -484 479 -486 483 -500 227 -250 241 -252 223 -254 223 -252 225 -242 477 -484 247 -250 469 -494 247 -224 479 -488 237 -246 251 -226 479 -486 471 -480 251 -222 489 -480 253 -246 465 -480 243 -240 245 -236 247 -252 253 -224 473 -486 481 -478 485 -482 247 -224 503 -460 497 -484 243 -224 251 -254 225 -252 223 -242 247 -232 481 -474 255 -226 265 -222 485 -490 489 -456 505 -462 247 -252 477 -482 471 -482 243 -240 247 -232 247 -252 225 -252 251 -226 467 -496 481 -468 487 -482 497 -458 267 -224 251 -254 449 -484 503 -464 241 -240 1447 -1454 229 -804 209 -238\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/kinggates_stylo4k_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112\nRAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384\nRAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148\nRAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384\nRAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144\nRAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/legrand.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Legrand\nBit: 18\nKey: 00 00 00 00 00 02 E3 7F\nTE: 358\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/legrand_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 10133 -4946 65 -5114 65 -1750 67 -232 165 -536 165 -166 533 -694 395 -198 263 -66 395 -12664 297 -662 133 -264 99 -102 131 -200 131 -234 263 -66 265 -100 661 -168 197 -134 297 -66 431 -132 45005 -12358 131 -100 129 -66 65 -198 65 -66 197 -1018 131 -266 97 -1052 97 -228 459 -100 1517 -100 4361 -10350 165 -1264 65 -232 131 -200 65 -166 133 -232 299 -132 65 -166 299 -134 231 -198 99 -68 429 -12270 231 -628 163 -594 97 -496 227 -1220 163 -166 129 -66 655 -98 163 -66 393 -66 195 -198 293 -12130 67 -68 331 -134 99 -98 99 -332 99 -562 163 -164 65 -130 131 -920 131 -98 65 -198 133 -98 427 -130 359 -132 627 -66 2207 -16066 163 -1316 331 -832 131 -370 131 -132 661 -166 265 -134 833 -168 34477 -10228 65 -2266 261 -230 131 -198 129 -1282 65 -66 463 -100 365 -232 233 -66 297 -166 2151 -12330 99 -400 97 -436 131 -896 165 -400 363 -298 199 -166 97 -430 165 -168 365 -132 725 -130 3253 -12440 131 -796 297 -332 65 -830 65 -828 429 -198 335 -66 665 -266 29175 -12140 163 -230 131 -992 165 -1230 397 -132 199 -132 299 -66 659 -100 9675 -11036 99 -570 99 -830 165 -662 331 -266 429 -132 391 -396 431 -132 233 -166 231 -100 3435 -11728 99 -332 165 -166 63 -128 161 -326 129 -690 231 -130 265 -98 167 -232 199 -66 201 -264 165 -166 365 -66 727 -100 363 -12688 229 -228 95 -98 391 -198 129 -196 97 -364 97 -100 65 -266 99 -64 293 -98 327 -262 761 -66 7681 -5384 99 -762 195 -134 525 -992 99 -166 67 -98 329 -100 659 -100 293 -100 97 -98 163 -264 1741 -100 57331 -9834 67 -664 67 -2158 99 -862 133 -368 331 -864 65 -330 65 -794 99 -66 431 -100 3017 -66 7061 -6558 99 -6316 99 -266 131 -166 97 -198 65 -294 99 -66 661 -132 2661 -130 19413 -12380 65 -262 133 -66 67 -232 133 -798 197 -264 165 -498 97 -166 199 -134 559 -98 165 -13118 99 -562 65 -1256 231 -232 99 -132 131 -66 199 -66 429 -296 131 -200 201 -266 65 -66 895 -132 829 -66 12455 -11974 199 -334 99 -2160 165 -1232 929 -100 327 -166 295 -134 2191 -100 20415 -11032 65 -1170 131 -2326 163 -398 263 -596 99 -400 165 -68 499 -66 661 -11724 97 -1226 99 -166 233 -400 131 -662 165 -400 165 -166 299 -296 1159 -100 131 -98 427 -68 1643 -13934 101 -1294 65 -394 63 -1458 265 -166 265 -364 133 -434 101 -398 195 -264 1097 -166 265 -166 58013 -11978 131 -100 165 -666 199 -66 163 -166 65 -198 195 -592 1295 -134 597 -136 567 -100 1615 -100 17759 -12402 261 -692\nRAW_Data: 265 -730 327 -398 131 -300 199 -432 7947 -4772 233 -366 365 -166 133 -522 99 -164 65 -366 99 -100 395 -66 4423 -15068 197 -430 163 -398 65 -230 165 -228 97 -494 165 -134 197 -168 131 -168 65 -298 267 -132 1657 -98 99 -66 165 -100 7687 -12010 197 -132 163 -198 527 -130 293 -396 297 -360 163 -264 95 -164 161 -694 427 -262 129 -100 885 -100 131 -66 461 -9862 97 -5002 497 -594 197 -66 229 -596 65 -132 195 -262 99 -132 63 -166 65 -230 431 -100 165 -168 461 -164 13159 -12060 133 -64 129 -1316 131 -758 165 -430 165 -270 133 -564 995 -7536 99 -4746 165 -358 457 -132 165 -166 99 -100 397 -132 299 -66 197 -66 131 -392 129 -196 131 -328 459 -100 565 -66 165 -66 265 -100 1131 -100 9533 -12070 65 -400 163 -366 165 -530 197 -196 133 -68 233 -398 165 -66 99 -100 163 -132 791 -66 17359 -12434 131 -328 229 -558 195 -564 167 -132 133 -234 131 -100 65 -364 65 -496 65 -200 297 -66 1725 -12958 63 -68 65 -164 67 -132 63 -200 131 -166 165 -696 131 -166 67 -200 65 -168 1061 -12848 199 -166 2039 -1660 201 -5822 1017 -1126 299 -414 1015 -388 1061 -380 1055 -1086 327 -1118 339 -1084 351 -360 1051 -376 1083 -1070 343 -346 1105 -350 1069 -354 1087 -346 1063 -362 1075 -372 1067 -334 1097 -5694 1083 -1074 351 -378 1057 -332 1101 -370 1065 -1050 379 -1050 379 -1066 363 -348 1073 -388 1047 -1076 351 -380 1053 -362 1071 -372 1067 -352 1081 -352 1069 -350 1091 -346 1087 -5682 1095 -1044 381 -342 1097 -326 1111 -346 1061 -1064 379 -1066 381 -1032 379 -360 1065 -364 1067 -1066 375 -352 1081 -350 1069 -350 1089 -352 1081 -350 1069 -350 1097 -326 1081 -5698 1097 -1074 353 -354 1083 -326 1111 -344 1083 -1054 387 -1046 349 -1094 345 -350 1081 -354 1069 -1076 385 -350 1051 -354 1077 -386 1057 -334 1099 -370 1051 -354 1083 -356 1087 -5670 1107 -1072 353 -354 1081 -326 1111 -346 1085 -1054 389 -1050 353 -1056 377 -352 1083 -350 1071 -1080 381 -312 1099 -334 1107 -358 1079 -320 1091 -350 1095 -348 1081 -318 1113 -5688 1075 -1076 353 -354 1085 -324 1111 -346 1067 -1056 387 -1048 381 -1070 383 -314 1085 -352 1073 -1078 385 -348 1049 -354 1103 -356 1055 -334 1097 -372 1049 -354 1081 -354 1087 -5680 1109 -1038 383 -356 1085 -326 1107 -344 1065 -1066 361 -1076 371 -1054 389 -330 1069 -376 1069 -1054 389 -332 1073 -374 1069 -354 1079 -320 1097 -350 1093 -348 1083 -322 1111 -5680 1105 -1044 359 -356 1083 -354 1085 -348 1097 -1050 387 -1042 349 -1074 385 -350 1053 -352 1077 -1082 379 -314 1099 -336 1095 -346 1075 -374 1071 -354 1081 -320 1095 -348 1097 -5686\nRAW_Data: 1085 -1062 369 -346 1093 -322 1105 -350 1063 -1076 351 -1070 381 -1068 355 -358 1087 -354 1049 -1070 351 -384 1081 -318 1113 -344 1065 -348 1083 -352 1071 -388 1051 -352 1081 -5706 1065 -1064 361 -348 1087 -356 1079 -348 1091 -1072 381 -1020 375 -1068 361 -356 1081 -354 1085 -1042 381 -352 1079 -352 1101 -320 1093 -346 1087 -322 1117 -354 1057 -334 1099 -5702 1085 -1044 381 -340 1087 -352 1081 -352 1069 -1080 379 -1032 379 -1050 377 -346 1099 -332 1077 -1084 355 -334 1101 -334 1089 -350 1097 -320 1095 -348 1097 -326 1077 -376 1053 -5706 1073 -1088 367 -346 1063 -354 1071 -382 1067 -1076 347 -1068 381 -1064 347 -360 1085 -354 1047 -1072 385 -352 1087 -328 1073 -374 1051 -388 1045 -354 1093 -348 1085 -354 1079 -5668 1085 -1082 349 -348 1079 -374 1051 -382 1063 -1076 355 -1082 349 -1068 353 -364 1065 -362 1075 -1064 373 -346 1067 -362 1073 -382 1063 -352 1067 -380 1065 -326 1081 -376 1065 -5714 1075 -1072 345 -346 1099 -332 1097 -346 1075 -1062 373 -1066 347 -1076 373 -344 1069 -362 1073 -1072 383 -314 1113 -344 1063 -350 1079 -374 1065 -332 1099 -370 1065 -336 1099 -5694 1087 -1046 351 -368 1097 -348 1051 -354 1109 -1046 383 -1060 365 -1072 365 -352 1049 -352 1091 -1074 347 -384 1051 -352 1073 -384 1051 -350 1079 -354 1073 -380 1067 -326 1081 -5722 1065 -1078 355 -352 1081 -356 1085 -344 1083 -1050 385 -1050 353 -1092 345 -384 1051 -352 1069 -1078 381 -350 1051 -354 1069 -386 1057 -364 1073 -368 1071 -352 1049 -354 1093 -5710 1077 -1072 353 -354 1085 -326 1079 -376 1069 -1052 381 -1064 349 -1074 381 -346 1073 -368 1067 -1056 359 -350 1107 -348 1063 -352 1069 -382 1065 -326 1111 -346 1063 -364 1071 -5686 1099 -1072 355 -352 1083 -326 1111 -346 1065 -1086 353 -1050 383 -1068 383 -350 1047 -354 1071 -1076 381 -346 1069 -370 1065 -336 1095 -334 1099 -336 1099 -330 1073 -354 1081 -5710 1077 -1050 385 -348 1085 -322 1109 -356 1055 -1084 355 -1054 377 -1052 375 -346 1103 -350 1065 -1074 351 -342 1093 -346 1091 -322 1107 -356 1055 -364 1071 -372 1053 -354 1085 -5704 1077 -1050 383 -346 1073 -370 1065 -334 1097 -1060 371 -1062 347 -1080 373 -344 1069 -360 1075 -1076 351 -346 1109 -344 1063 -364 1069 -370 1065 -334 1099 -332 1097 -336 1097 -5690 1087 -1044 383 -338 1095 -348 1083 -320 1109 -1048 385 -1056 365 -1072 365 -356 1081 -320 1095 -1078 343 -346 1103 -330 1075 -352 1095 -350 1069 -350 1085 -352 1081 -350 1071 -5712 1075 -1042 385 -354 1085 -326 1079 -374 1051 -1084 351 -1064 383 -1068 347 -346 1099 -334 1103 -1054 347 -354 1099 -346 1083 -354 1081 -354 1053 -362 1071 -374 1065 -346 1067 -5726 1053 -1082 351 -360 1091 -344 1083 -350\nRAW_Data: 1081 -1074 353 -1074 351 -1088 325 -386 1051 -386 1047 -1074 353 -378 1051 -384 1047 -354 1073 -384 1049 -376 1065 -356 1073 -352 1087 -5680 1079 -1074 381 -350 1051 -354 1071 -386 1051 -1086 351 -1080 351 -1070 381 -350 1051 -352 1071 -1080 383 -348 1049 -354 1075 -384 1065 -324 1113 -346 1061 -350 1079 -374 1051 -5706 1077 -1086 333 -384 1047 -354 1093 -346 1065 -1078 353 -1094 345 -1082 351 -360 1089 -346 1063 -1074 353 -366 1065 -360 1075 -372 1067 -334 1101 -368 1051 -352 1085 -356 1051 -5734 1073 -1072 353 -354 1083 -328 1073 -376 1083 -1040 375 -1066 359 -1082 353 -334 1099 -368 1069 -1070 343 -346 1105 -354 1055 -362 1071 -370 1069 -352 1077 -352 1069 -350 1087 -5680 1079 -1086 367 -352 1049 -354 1097 -346 1063 -1078 349 -1092 345 -1098 347 -358 1065 -350 1079 -1064 373 -346 1067 -362 1081 -350 1065 -350 1095 -346 1067 -362 1075 -372 1049 -5706 1071 -1086 367 -352 1083 -320 1095 -348 1087 -1050 387 -1050 353 -1094 345 -346 1101 -332 1073 -1072 383 -346 1083 -346 1087 -352 1051 -354 1093 -346 1085 -346 1061 -354 1103 -5700 1069 -1050 393 -352 1063 -352 1069 -350 1097 -1044 383 -1068 379 -1030 379 -358 1065 -348 1073 -1068 377 -346 1099 -330 1073 -352 1093 -350 1073 -348 1087 -352 1081 -350 1069 -5710 1073 -1076 353 -354 1085 -324 1111 -344 1083 -1040 389 -1044 381 -1070 347 -350 1085 -352 1071 -1078 383 -352 1047 -352 1075 -352 1089 -350 1083 -352 1069 -350 1097 -326 1109 -5664 1113 -1040 383 -354 1085 -328 1075 -374 1051 -1084 351 -1066 351 -1096 379 -314 1099 -334 1097 -1052 379 -356 1069 -354 1079 -320 1097 -348 1093 -346 1083 -354 1077 -354 1055 -5714 1073 -1076 345 -350 1081 -354 1069 -382 1065 -1074 349 -1068 379 -1064 347 -360 1065 -364 1071 -1066 371 -344 1067 -360 1077 -354 1083 -354 1087 -346 1065 -364 1071 -368 1071 -5682 1075 -1084 367 -342 1067 -360 1075 -352 1097 -1040 353 -1074 385 -1058 363 -346 1075 -354 1081 -1076 351 -378 1057 -334 1101 -334 1103 -356 1079 -320 1091 -348 1093 -348 1085 -5678 1095 -1050 379 -356 1065 -334 1097 -370 1049 -1086 353 -1086 325 -1080 375 -352 1081 -350 1067 -1078 383 -350 1051 -352 1071 -386 1053 -334 1103 -336 1085 -354 1087 -324 1111 -5676 1079 -1078 351 -380 1055 -364 1073 -368 1051 -1082 349 -1064 351 -1092 347 -378 1073 -334 1099 -1050 381 -328 1095 -346 1083 -350 1081 -352 1069 -382 1049 -344 1095 -354 1077 -5682 1071 -1078 359 -352 1081 -356 1085 -346 1065 -1082 353 -1080 351 -1070 385 -348 1051 -354 1077 -1076 381 -314 1103 -334 1105 -354 1081 -318 1095 -348 1087 -354 1081 -320 1097 -5712 1077 -1074 319 -380 1067 -356 1087 -346 1065 -1080 349 -1092 345 -1082 351 -360\nRAW_Data: 1053 -378 1083 -1050 381 -346 1053 -376 1067 -364 1069 -370 1047 -386 1047 -356 1093 -344 1083 -5690 1079 -1060 371 -344 1067 -362 1073 -348 1095 -1076 353 -1074 353 -1054 359 -352 1101 -348 1085 -1046 361 -388 1043 -350 1093 -346 1081 -352 1081 -352 1067 -384 1055 -346 1085 -5678 1083 -1084 347 -344 1093 -348 1097 -326 1075 -1070 375 -1062 347 -1080 373 -344 1067 -362 1073 -1074 381 -314 1115 -344 1085 -356 1047 -354 1091 -348 1085 -354 1081 -320 1095 -5708 1075 -1074 353 -354 1083 -326 1109 -346 1065 -1054 387 -1050 351 -1096 381 -316 1083 -352 1073 -1080 381 -312 1101 -336 1105 -350 1063 -352 1067 -384 1065 -324 1111 -346 1065 -5704 1073 -1080 347 -350 1083 -352 1073 -350 1085 -1064 375 -1064 357 -1072 383 -314 1111 -344 1087 -1042 387 -330 1071 -374 1069 -352 1081 -350 1067 -350 1097 -348 1083 -322 1113 -5682 1075 -1076 353 -354 1083 -326 1117 -342 1085 -1054 351 -1062 383 -1070 377 -314 1103 -332 1105 -1042 381 -346 1083 -346 1065 -360 1071 -374 1069 -352 1079 -352 1067 -348 1089 -5682 1077 -1088 367 -354 1047 -354 1095 -346 1083 -1068 345 -1096 351 -1074 351 -346 1077 -372 1065 -1066 349 -386 1069 -346 1085 -356 1077 -354 1057 -364 1071 -370 1071 -352 1079 -5676 1073 -1074 381 -314 1117 -344 1063 -352 1081 -1076 357 -1080 355 -1052 359 -352 1095 -334 1097 -1064 371 -352 1083 -320 1095 -348 1085 -352 1081 -350 1069 -350 1093 -348 1085 -5676 1095 -1052 379 -356 1065 -346 1087 -356 1075 -1048 387 -1058 363 -1074 367 -356 1049 -354 1091 -1072 347 -346 1101 -332 1073 -384 1063 -350 1067 -350 1101 -326 1079 -376 1065 -5680 1107 -1074 343 -346 1067 -362 1083 -352 1085 -1078 349 -1070 345 -1066 379 -360 1065 -348 1079 -1062 375 -344 1069 -362 1075 -352 1081 -356 1085 -344 1063 -384 1047 -354 1073 -5708 1073 -1074 357 -354 1083 -356 1087 -344 1087 -1052 351 -1064 379 -1068 379 -314 1103 -334 1099 -1052 347 -386 1061 -348 1073 -374 1067 -352 1083 -350 1067 -350 1095 -348 1081 -5682 1085 -1054 383 -344 1087 -346 1067 -362 1069 -1064 373 -1062 347 -1078 373 -352 1081 -350 1065 -1078 383 -350 1051 -352 1071 -386 1051 -352 1081 -352 1069 -350 1095 -346 1087 -5678 1089 -1052 387 -330 1073 -374 1083 -354 1045 -1072 353 -1074 385 -1056 361 -350 1081 -352 1083 -1080 349 -376 1059 -346 1087 -356 1075 -354 1055 -362 1075 -370 1067 -352 1081 -5676 1075 -1076 387 -330 1071 -374 1065 -352 1079 -1072 355 -1082 353 -1054 361 -350 1095 -348 1073 -1068 373 -352 1081 -322 1097 -348 1085 -354 1079 -352 1069 -348 1093 -348 1083 -5676 1097 -1056 361 -348 1071 -354 1081 -354 1089 -1074 345 -1062 383 -1050 353 -380 1057 -364 1071 -1066 371 -354 1081 -318\nRAW_Data: 1099 -348 1085 -352 1079 -352 1071 -348 1087 -346 1095 -5678 1095 -1050 383 -328 1091 -348 1061 -386 1047 -1078 357 -1084 351 -1066 351 -366 1087 -348 1061 -1076 357 -354 1081 -354 1089 -346 1065 -352 1079 -354 1069 -386 1055 -334 1099 -5694 1085 -1044 383 -340 1087 -352 1081 -350 1065 -1080 383 -1052 351 -1082 349 -376 1059 -348 1083 -1046 395 -356 1049 -352 1091 -348 1085 -354 1083 -320 1093 -348 1087 -354 1081 -5670 1107 -1070 343 -348 1067 -384 1067 -354 1083 -1068 345 -1064 349 -1080 373 -350 1081 -352 1067 -1076 383 -348 1049 -354 1071 -386 1053 -364 1071 -368 1069 -352 1079 -350 1069 -5708 1075 -1074 351 -354 1083 -326 1111 -344 1067 -1052 385 -1050 381 -1070 347 -346 1101 -332 1073 -1072 383 -344 1085 -346 1067 -362 1069 -370 1053 -352 1085 -356 1085 -346 1067 -5682 1111 -1060 369 -344 1071 -360 1081 -354 1053 -1080 353 -1092 345 -1084 351 -360 1053 -376 1067 -1086 351 -330 1081 -376 1051 -382 1067 -352 1067 -380 1067 -326 1081 -374 1051 -5696 1109 -1058 369 -354 1079 -320 1097 -350 1085 -1054 387 -1048 353 -1060 377 -352 1085 -350 1069 -1078 383 -350 1049 -352 1071 -384 1065 -326 1081 -376 1067 -334 1101 -334 1101 -5692 1063 -1064 375 -346 1067 -362 1073 -380 1065 -1074 355 -1072 349 -1066 349 -366 1085 -350 1081 -1076 355 -350 1085 -326 1077 -376 1083 -352 1049 -354 1095 -346 1065 -380 1051 -5712 1083 -1052 389 -330 1073 -374 1053 -354 1083 -1074 351 -1074 385 -1052 343 -346 1107 -352 1065 -1072 351 -376 1065 -356 1081 -346 1065 -362 1069 -372 1067 -336 1097 -332 1099 -5678 1101 -1068 343 -350 1081 -352 1069 -384 1055 -1082 349 -1056 377 -1082 353 -330 1081 -374 1067 -1082 351 -330 1081 -376 1081 -352 1047 -354 1097 -348 1081 -352 1081 -352 1069 -5712 1077 -1038 383 -354 1083 -326 1079 -374 1055 -1074 383 -1064 347 -1066 381 -316 1099 -370 1051 -1082 353 -336 1097 -368 1053 -352 1081 -354 1089 -346 1063 -350 1081 -352 1071 -5714 1075 -1074 355 -350 1083 -358 1053 -376 1065 -1084 347 -1064 353 -1070 385 -346 1085 -352 1077 -1042 387 -346 1053 -354 1077 -386 1055 -364 1069 -370 1051 -388 1051 -354 1087 -5672 1081 -1074 351 -378 1065 -356 1083 -346 1065 -1078 347 -1092 345 -1066 359 -350 1107 -356 1045 -1068 383 -354 1083 -328 1075 -372 1053 -388 1051 -356 1087 -346 1065 -362 1071 -5702 1083 -1076 349 -376 1055 -346 1083 -356 1073 -1078 353 -1054 361 -1078 369 -352 1083 -352 1065 -1072 347 -346 1103 -332 1101 -346 1081 -354 1045 -386 1051 -362 1073 -370 1049 -5718 1075 -1048 365 -388 1047 -354 1091 -346 1085 -1040 387 -1050 351 -1096 349 -348 1083 -352 1073 -1076 379 -316 1101 -334 1097 -336 1099 -330 1071 -388 1047 -354\nRAW_Data: 1087 -348 1085 -5684 1075 -1084 367 -344 1067 -358 1073 -350 1063 -1074 353 -1108 349 -1068 353 -362 1067 -344 1097 -1050 363 -386 1047 -356 1087 -346 1085 -352 1079 -320 1097 -346 1083 -352 1081 -5676 1103 -1054 387 -330 1071 -372 1053 -354 1085 -1074 351 -1070 385 -1058 347 -362 1069 -350 1087 -1054 381 -346 1085 -346 1065 -364 1069 -370 1071 -354 1081 -320 1097 -348 1083 -5676 1079 -1090 367 -344 1067 -360 1073 -350 1095 -1074 351 -1076 349 -1068 353 -364 1081 -352 1081 -1074 321 -382 1051 -360 1075 -374 1051 -386 1049 -354 1091 -346 1085 -346 1067 -5706 1065 -1084 351 -360 1087 -346 1085 -356 1047 -1074 353 -1072 383 -1064 347 -362 1067 -364 1069 -1068 371 -352 1083 -320 1095 -350 1097 -346 1083 -322 1077 -388 1051 -350 1081 -5706 1051 -1084 387 -328 1073 -372 1049 -386 1049 -1076 353 -1072 383 -1056 363 -348 1071 -354 1085 -1076 351 -376 1057 -344 1087 -356 1073 -352 1081 -350 1053 -376 1051 -384 1049 -5710 1083 -1076 351 -348 1073 -372 1063 -334 1099 -1062 371 -1052 389 -1050 353 -364 1087 -354 1049 -1072 353 -382 1055 -362 1075 -370 1051 -352 1095 -350 1067 -348 1099 -326 1077 -5702 1077 -1076 353 -378 1053 -362 1071 -370 1051 -1084 349 -1066 353 -1096 345 -382 1049 -354 1069 -1080 379 -346 1069 -368 1041 -386 1047 -354 1093 -346 1085 -350 1083 -350 1067 -5708 1075 -1074 353 -352 1083 -326 1109 -344 1083 -1050 387 -1052 355 -1054 377 -352 1085 -350 1069 -1076 377 -314 1103 -332 1101 -336 1097 -330 1073 -354 1085 -354 1089 -346 1083 -5688 1073 -1084 331 -386 1047 -356 1087 -346 1061 -1082 383 -1050 353 -1070 385 -346 1051 -354 1105 -1046 385 -346 1085 -322 1073 -386 1049 -382 1047 -354 1069 -386 1053 -362 1071 -5692 1079 -1076 351 -378 1055 -364 1071 -370 1073 -1040 373 -1070 357 -1084 353 -334 1097 -334 1105 -1056 387 -330 1071 -372 1069 -354 1081 -318 1095 -348 1085 -352 1077 -352 1069 -5708 1075 -1076 353 -352 1087 -326 1081 -372 1067 -1084 351 -1050 353 -1098 345 -344 1101 -332 1101 -1054 365 -348 1079 -354 1087 -326 1081 -374 1049 -382 1063 -352 1067 -384 1055 -5708 1071 -1076 345 -350 1081 -352 1069 -386 1059 -1054 361 -1076 371 -1054 387 -330 1073 -372 1069 -1054 383 -344 1085 -346 1065 -364 1073 -368 1065 -334 1099 -334 1073 -382 1061 -5700 1067 -1064 383 -328 1093 -348 1083 -354 1083 -1040 383 -1046 387 -1056 361 -348 1073 -354 1085 -1074 351 -378 1055 -332 1099 -338 1089 -354 1085 -324 1115 -344 1085 -350 1063 -5704 1069 -1068 349 -360 1091 -346 1067 -362 1075 -1062 373 -1068 333 -1102 365 -354 1049 -356 1087 -1074 345 -384 1047 -352 1069 -386 1053 -364 1071 -370 1051 -352 1085 -354 1085 -5678 1081 -1072 351 -378\nRAW_Data: 1055 -362 1069 -372 1069 -1054 387 -1048 353 -1092 345 -346 1101 -332 1073 -1074 383 -312 1113 -346 1063 -364 1071 -370 1073 -354 1081 -320 1091 -348 1085 -5680 1075 -1088 367 -344 1069 -360 1075 -350 1063 -1074 353 -1076 385 -1054 365 -348 1071 -354 1083 -1074 351 -374 1067 -326 1077 -376 1085 -352 1049 -354 1091 -346 1063 -380 1051 -5714 1085 -1038 387 -328 1081 -376 1053 -382 1063 -1072 355 -1076 349 -1064 349 -368 1097 -326 1073 -1070 377 -344 1099 -330 1085 -354 1087 -326 1079 -374 1069 -350 1081 -352 1071 -5700 1067 -1076 357 -350 1095 -320 1095 -348 1065 -1078 349 -1092 347 -1066 379 -358 1069 -332 1101 -1062 371 -350 1085 -320 1095 -348 1097 -346 1049 -356 1075 -386 1057 -364 1069 -5692 1077 -1070 351 -380 1051 -350 1075 -376 1065 -1052 385 -1048 383 -1068 381 -316 1083 -352 1073 -1080 377 -314 1103 -334 1099 -336 1099 -328 1075 -350 1095 -352 1065 -350 1095 -5674 1079 -1076 379 -346 1071 -368 1067 -336 1065 -1088 369 -1050 387 -1048 353 -366 1063 -382 1045 -1078 361 -386 1047 -354 1089 -346 1063 -384 1045 -354 1071 -386 1057 -332 1101 -5698 1081 -1044 383 -340 1085 -352 1079 -352 1067 -1080 379 -1030 379 -1050 377 -344 1099 -330 1073 -1074 381 -346 1085 -346 1085 -356 1047 -354 1091 -348 1063 -362 1075 -374 1063 -5678 1103 -1042 377 -352 1081 -352 1073 -348 1095 -1056 347 -1080 373 -1054 383 -314 1113 -346 1065 -1082 355 -358 1067 -352 1079 -352 1067 -350 1093 -346 1083 -354 1075 -354 1087 -5678 1079 -1074 345 -348 1099 -332 1101 -336 1067 -1090 365 -1052 387 -1048 355 -362 1067 -364 1073 -1066 371 -350 1081 -352 1065 -348 1069 -362 1075 -372 1067 -334 1099 -332 1099 -848 153 -153886 131 -896 231 -464 231 -428 97 -166 99 -966 233 -200 101 -66 199 -102 265 -266 299 -98 131 -100 65 -232 131 -100 44699 -12320 65 -132 131 -300 231 -100 167 -266 197 -68 297 -330 131 -496 131 -400 131 -166 633 -66 431 -230 589 -166 10885 -11354 329 -792 99 -296 129 -166 97 -98 65 -132 229 -294 297 -98 561 -166 231 -98 825 -166 57693 -12116 131 -98 295 -424 163 -398 131 -232 97 -130 65 -66 97 -66 165 -460 529 -66 197 -132 393 -198 267 -168 43035 -10678 99 -1716 97 -498 65 -298 67 -2020 331 -166 957 -66 199 -100 529 -12206 199 -796 67 -66 131 -132 129 -398 197 -230 131 -66 227 -196 65 -526 197 -132 589 -132 597 -100 327 -66 8489 -11212 133 -630 131 -100 133 -166 99 -994 99 -198 333 -1626 329 -130 6045 -13890 65 -434 463 -264 295 -266 1385 -132 461 -134 163951 -12284 131 -858 233 -532 199 -402 97 -266 133 -496 431 -66 1161 -66 493 -68 21495 -5856\nRAW_Data: 95 -560 65 -2896 199 -762 97 -100 65 -430 131 -896 199 -234 101 -800 231 -332 99 -334 133 -132 265 -268 1425 -66 33043 -12400 99 -666 131 -234 67 -232 65 -498 65 -266 65 -498 165 -102 165 -200 2189 -11534 133 -266 197 -232 97 -166 165 -100 99 -98 99 -500 97 -332 99 -200 12547 -12014 65 -232 99 -600 131 -330 299 -332 625 -66 1243 -134 7929 -12344 97 -762 131 -860 165 -298 295 -298 231 -266 97 -134 397 -132 2891 -68 299 -16210 67 -266 133 -234 101 -268 163 -466 97 -232 65 -198 67 -132 67 -100 263 -98 437 -302 133 -66 165 -168 24439 -10722 131 -1534 229 -1318 99 -98 195 -2100 597 -100 733 -134 28455 -10484 231 -2030 67 -100 101 -132 163 -166 265 -854 361 -166 531 -134 233 -100 495 -164 25733 -12966 65 -1880 65 -132 163 -1086 799 -68 16865 -12956 295 -330 163 -232 233 -166 97 -1860 99 -68 333 -66 1613 -98 55627 -12998 97 -266 263 -1594 131 -894 167 -134 231 -332 131 -66 399 -98 99 -66 697 -4972 65 -3944 65 -132 459 -328 197 -100 1557 -98 23673 -12982 333 -928 133 -232 433 -198 131 -360 99 -64 497 -100 463 -98 397 -132 197 -68 765 -13710 65 -166 99 -1684 131 -632 691 -230 263 -66 359 -132 2073 -64 6059 -12430 97 -462 65 -328 65 -792 229 -824 97 -892 529 -166 163 -168 165 -698 165 -166 431 -130 20385 -12774 97 -98 65 -1384 131 -198 131 -622 131 -598 99 -164 129 -98 193 -98 195 -98 263 -98 459 -132 131 -66 73855 -13048 65 -2554 97 -166 761 -68 761 -366 15305 -12094 165 -764 963 -68 65 -66 163 -66 233 -102 333 -100 965 -66 40087 -10292 165 -1724 65 -1754 165 -266 463 -396 267 -432 795 -132 297 -66 267 -68 261 -134 36583 -10588 65 -2738 97 -1184 259 -298 131 -294 395 -98 819 -132 689 -166 7999 -11970 65 -468 63 -1892 429 -262 263 -200 199 -632 265 -66 229 -66 1149 -68 2355 -15782 233 -198 67 -166 295 -818 229 -164 1059 -68 265 -68 1789 -10374 131 -1904 65 -196 229 -230 197 -360 229 -164 129 -1120 963 -166 231 -200 199 -230 333 -10180 67 -966 133 -1264 65 -798 99 -432 65 -898 65 -134 7681 -12052 131 -928 129 -624 99 -526 65 -432 65 -532 65 -596 199 -100 265 -296 895 -100 229 -98 589 -15506 97 -266 65 -434 65 -1520 199 -98 265 -894 293 -198 427 -198 97 -132 459 -64 2145 -66 9075 -7224 99 -66 65 -1060 65 -200 99 -932 167 -894 65 -528 165 -102 927 -100 597 -232 99 -66 97 -98 461 -196 261 -98 197 -264 2909 -134 11635 -804 99 -240 143 -244 369 -5700 1077 -1074 353 -354 1083 -324\nRAW_Data: 1117 -346 1063 -1086 353 -1050 381 -1070 383 -348 1049 -354 1073 -1082 383 -350 1047 -354 1077 -386 1057 -334 1101 -336 1105 -354 1081 -320 1093 -5710 1073 -1076 351 -354 1085 -324 1111 -344 1065 -1084 351 -1082 349 -1068 385 -348 1053 -354 1075 -1078 385 -348 1051 -354 1075 -382 1065 -354 1085 -346 1065 -364 1069 -370 1069 -5686 1077 -1086 367 -354 1047 -354 1095 -346 1095 -1054 363 -1072 369 -1050 387 -328 1077 -374 1051 -1084 349 -348 1081 -376 1081 -348 1063 -352 1073 -380 1067 -356 1053 -376 1083 -5684 1073 -1086 365 -356 1049 -354 1089 -348 1085 -1066 385 -1046 351 -1074 385 -348 1083 -320 1109 -1048 381 -346 1071 -336 1097 -336 1097 -334 1085 -352 1093 -320 1097 -348 1095 -5680 1089 -1064 371 -352 1081 -352 1067 -348 1067 -1078 349 -1094 347 -1096 349 -358 1067 -350 1081 -1076 355 -352 1081 -356 1055 -378 1065 -350 1079 -352 1069 -384 1053 -348 1079 -5702 1099 -1072 353 -350 1065 -354 1087 -348 1097 -1046 347 -1096 377 -1034 377 -360 1085 -356 1049 -1074 351 -382 1051 -362 1071 -374 1051 -388 1049 -352 1093 -346 1061 -384 1049 -5708 1087 -1068 349 -362 1089 -344 1061 -382 1051 -1076 359 -1080 351 -1088 355 -358 1069 -352 1081 -1078 353 -350 1087 -326 1079 -376 1065 -350 1079 -354 1069 -384 1059 -332 1101 -5700 1087 -1044 353 -368 1095 -348 1085 -320 1111 -1044 381 -1064 347 -1090 345 -344 1101 -332 1073 -1074 383 -344 1087 -344 1067 -352 1083 -352 1071 -384 1051 -352 1083 -352 1071 -5712 1077 -1076 355 -350 1087 -326 1079 -374 1053 -1070 375 -1072 357 -1080 353 -334 1103 -334 1073 -1084 349 -378 1051 -376 1067 -364 1073 -368 1065 -336 1101 -332 1083 -352 1083 -5712 1075 -1044 383 -348 1069 -372 1051 -354 1083 -1076 351 -1074 383 -1052 385 -326 1089 -346 1065 -1066 381 -340 1099 -326 1077 -374 1067 -352 1083 -350 1069 -350 1087 -352 1081 -5676 1085 -1074 387 -330 1071 -372 1069 -352 1079 -1076 355 -1078 351 -1084 347 -360 1067 -348 1081 -1074 357 -352 1081 -356 1085 -344 1065 -384 1049 -354 1071 -384 1059 -346 1085 -5680 1087 -1084 343 -348 1103 -352 1065 -354 1087 -1070 345 -1086 351 -1082 351 -340 1097 -346 1087 -1046 361 -352 1083 -354 1089 -346 1067 -350 1081 -354 1071 -386 1053 -346 1097 -5676 1085 -1084 349 -360 1085 -344 1081 -348 1065 -1076 357 -1080 351 -1086 325 -388 1065 -366 1067 -1064 371 -352 1081 -352 1065 -382 1049 -348 1083 -352 1071 -382 1049 -384 1047 -5708 1065 -1062 377 -350 1083 -354 1085 -326 1077 -1070 377 -1052 381 -1066 351 -378 1057 -332 1103 -1060 373 -344 1069 -362 1073 -350 1095 -350 1065 -348 1095 -350 1085 -344 1065 -5704 1079 -1072 347 -382 1049 -354 1075 -382 1051 -1082 351 -1082 351 -1072\nRAW_Data: 381 -348 1053 -354 1071 -1074 381 -348 1071 -370 1049 -386 1047 -356 1091 -346 1083 -346 1067 -354 1071 -5700 1103 -1046 361 -386 1047 -354 1087 -346 1085 -1050 387 -1050 351 -1098 345 -350 1083 -352 1073 -1078 379 -316 1103 -332 1101 -336 1097 -330 1073 -380 1065 -352 1069 -384 1061 -5684 1089 -1064 369 -346 1093 -320 1109 -352 1067 -1076 349 -1070 381 -1032 379 -360 1065 -362 1071 -1064 373 -342 1099 -330 1073 -350 1093 -352 1067 -350 1087 -350 1081 -352 1071 -5710 1077 -1074 355 -354 1081 -326 1113 -346 1083 -1054 381 -1034 383 -1066 379 -316 1099 -334 1097 -1052 379 -358 1067 -350 1081 -352 1067 -386 1055 -334 1101 -334 1107 -322 1085 -5708 1077 -1050 387 -348 1087 -320 1111 -356 1055 -1086 355 -1050 375 -1054 387 -330 1075 -376 1067 -1068 385 -326 1087 -346 1063 -382 1047 -354 1105 -354 1057 -332 1097 -372 1049 -5716 1071 -1080 365 -356 1047 -354 1095 -348 1085 -1070 353 -1050 383 -1070 379 -314 1101 -334 1099 -1052 377 -356 1071 -354 1081 -320 1097 -348 1087 -346 1095 -322 1105 -352 1063 -5702 1075 -1076 351 -346 1109 -342 1087 -322 1081 -1074 351 -1076 383 -1062 347 -362 1065 -364 1071 -1064 373 -344 1099 -330 1077 -350 1097 -320 1097 -348 1095 -326 1079 -376 1063 -5712 1071 -1074 347 -346 1101 -332 1083 -350 1097 -1040 385 -1044 387 -1054 363 -348 1085 -354 1087 -1046 351 -370 1085 -346 1097 -320 1107 -350 1063 -350 1087 -346 1065 -360 1075 -5704 1077 -1076 351 -378 1055 -362 1071 -370 1049 -1084 351 -1084 355 -1054 375 -350 1083 -352 1067 -1076 379 -346 1075 -332 1101 -336 1065 -362 1073 -382 1065 -352 1069 -384 1049 -5682 1077 -1090 369 -354 1049 -354 1093 -348 1087 -1070 347 -1062 383 -1042 385 -346 1073 -372 1051 -1084 351 -346 1079 -374 1083 -354 1051 -354 1091 -348 1089 -354 1047 -354 1097 -5702 1075 -1076 353 -352 1085 -326 1077 -374 1053 -1082 385 -1050 353 -1092 345 -348 1083 -352 1069 -1076 383 -346 1051 -354 1073 -384 1051 -374 1067 -358 1073 -350 1069 -354 1087 -5706 1075 -1042 385 -352 1085 -326 1077 -378 1067 -1050 387 -1048 383 -1064 381 -350 1049 -354 1069 -1080 383 -350 1049 -354 1073 -388 1055 -334 1101 -336 1107 -354 1047 -354 1093 -5674 1109 -1072 353 -350 1083 -348 1051 -376 1065 -1082 351 -1082 351 -1072 379 -348 1073 -332 1073 -1072 381 -346 1085 -344 1065 -362 1073 -372 1047 -386 1047 -354 1089 -346 1083 -5686 1075 -1092 367 -354 1049 -354 1093 -348 1065 -1066 383 -1070 347 -1062 379 -358 1063 -348 1079 -1066 375 -344 1067 -362 1073 -350 1097 -350 1071 -348 1095 -346 1083 -320 1107 -5668 1107 -1078 329 -388 1047 -354 1093 -344 1065 -1082 343 -1098 353 -1080 349 -346 1077 -370 1067 -1052 347 -390\nRAW_Data: 1067 -364 1069 -366 1051 -386 1051 -356 1053 -378 1085 -346 1067 -5712 1053 -1074 381 -346 1083 -344 1063 -350 1077 -1068 377 -1066 383 -1050 351 -378 1057 -364 1073 -1066 371 -346 1063 -354 1071 -386 1053 -346 1103 -332 1071 -382 1063 -352 1065 -5716 1077 -1074 353 -352 1083 -326 1077 -376 1051 -1084 381 -1066 351 -1066 379 -348 1069 -368 1067 -1066 349 -362 1067 -362 1071 -370 1067 -334 1099 -366 1065 -336 1067 -364 1073 -5714 1075 -1050 361 -388 1045 -350 1091 -348 1085 -1054 381 -1066 349 -1070 381 -346 1071 -368 1065 -1052 379 -358 1067 -334 1097 -334 1085 -352 1085 -354 1087 -346 1083 -354 1051 -5706 1087 -1078 349 -348 1075 -372 1051 -382 1067 -1072 353 -1076 353 -1056 361 -350 1083 -352 1083 -1076 351 -376 1055 -364 1071 -370 1049 -352 1083 -356 1087 -346 1083 -352 1049 -5702 1087 -1052 389 -346 1087 -322 1111 -354 1057 -1082 355 -1054 375 -1064 347 -354 1105 -354 1081 -1040 383 -352 1085 -326 1075 -374 1051 -354 1083 -354 1089 -346 1085 -348 1065 -5710 1087 -1062 345 -352 1071 -382 1061 -352 1069 -1078 383 -1052 351 -1082 349 -380 1055 -334 1099 -1062 371 -354 1083 -318 1097 -348 1083 -352 1081 -352 1065 -350 1087 -352 1077 -5710 1069 -1054 389 -328 1075 -376 1049 -356 1079 -1076 351 -1074 385 -1058 365 -344 1073 -352 1097 -1036 383 -350 1063 -354 1087 -348 1097 -328 1071 -376 1063 -332 1101 -336 1101 -5676 1103 -1038 379 -344 1099 -332 1077 -354 1085 -1076 349 -1070 385 -1052 353 -360 1087 -344 1085 -1040 387 -328 1081 -374 1051 -386 1047 -354 1089 -346 1065 -362 1077 -372 1065 -5710 1075 -1042 377 -350 1083 -352 1069 -350 1095 -1050 373 -1064 357 -1074 353 -364 1073 -372 1053 -1074 351 -346 1081 -376 1069 -332 1099 -334 1087 -352 1085 -354 1085 -346 1085 -5682 1075 -1088 329 -388 1049 -354 1089 -346 1065 -1070 353 -1096 345 -1084 351 -362 1085 -344 1067 -1082 355 -358 1083 -354 1047 -354 1093 -346 1065 -360 1073 -372 1081 -354 1045 -5708 1073 -1086 353 -346 1075 -374 1055 -352 1095 -1074 353 -1074 351 -1062 379 -340 1095 -326 1079 -1070 373 -344 1099 -330 1083 -356 1083 -324 1113 -344 1067 -352 1081 -352 1073 -5706 1073 -1076 355 -354 1085 -326 1081 -374 1067 -1054 389 -1048 353 -1096 345 -350 1081 -352 1071 -1080 383 -348 1047 -354 1073 -386 1057 -334 1099 -334 1077 -384 1063 -350 1067 -5704 1071 -1076 357 -354 1081 -356 1083 -346 1067 -1086 353 -1048 383 -1070 383 -314 1081 -354 1071 -1078 385 -348 1047 -354 1107 -354 1057 -334 1099 -334 1085 -354 1083 -354 1085 -5670 1111 -1038 383 -354 1085 -328 1071 -376 1053 -1088 353 -1086 355 -1048 375 -352 1081 -350 1069 -1078 381 -350 1049 -352 1071 -386 1055 -334 1097 -336\nRAW_Data: 1087 -356 1081 -356 1085 -5672 1077 -1074 353 -382 1053 -364 1073 -372 1073 -1054 389 -1050 355 -1054 377 -344 1099 -330 1073 -1076 387 -330 1069 -372 1067 -344 1099 -330 1073 -354 1081 -354 1087 -348 1063 -5718 1081 -1060 369 -342 1067 -360 1073 -354 1083 -1076 349 -1068 385 -1052 353 -360 1085 -346 1087 -1052 389 -330 1069 -372 1051 -356 1083 -354 1087 -346 1089 -348 1065 -352 1069 -5700 1101 -1046 359 -354 1083 -354 1087 -348 1063 -1082 353 -1054 377 -1062 379 -356 1067 -334 1101 -1060 369 -344 1067 -360 1083 -356 1087 -328 1075 -374 1067 -352 1081 -350 1071 -5706 1075 -1076 351 -354 1085 -324 1111 -344 1085 -1040 389 -1048 353 -1094 345 -350 1081 -352 1071 -1076 379 -316 1097 -336 1097 -336 1097 -334 1095 -346 1073 -372 1051 -354 1079 -5692 1085 -1084 351 -346 1075 -374 1051 -354 1083 -1074 351 -1072 385 -1058 381 -328 1073 -348 1085 -1054 389 -328 1075 -376 1067 -350 1081 -352 1069 -350 1085 -352 1083 -350 1071 -5710 1077 -1042 383 -352 1089 -326 1073 -376 1065 -1064 375 -1072 365 -1038 373 -348 1071 -386 1059 -1050 379 -358 1087 -354 1047 -354 1089 -348 1085 -354 1081 -320 1095 -348 1087 -5686 1079 -1084 365 -356 1049 -354 1093 -346 1085 -1052 387 -1048 355 -1090 347 -350 1083 -352 1071 -1076 379 -314 1099 -334 1097 -336 1097 -332 1099 -336 1095 -332 1075 -354 1083 -5708 1051 -1080 383 -348 1049 -354 1105 -354 1055 -1084 355 -1052 375 -1084 351 -330 1079 -376 1051 -1074 387 -328 1075 -376 1067 -350 1079 -352 1069 -380 1067 -326 1081 -374 1051 -5714 1075 -1050 395 -356 1047 -354 1091 -348 1095 -1052 375 -1064 353 -1076 351 -346 1077 -372 1065 -1050 379 -360 1065 -362 1069 -370 1065 -334 1099 -332 1097 -336 1097 -332 1083 -5682 1101 -1046 395 -356 1047 -354 1089 -346 1097 -1050 385 -1046 351 -1074 385 -348 1051 -354 1105 -1042 385 -350 1051 -354 1073 -384 1067 -326 1113 -344 1063 -364 1069 -370 1065 -5690 1077 -1074 383 -348 1047 -354 1075 -386 1057 -1054 361 -1076 373 -1052 387 -330 1073 -374 1069 -1054 387 -330 1075 -374 1067 -352 1081 -352 1067 -350 1085 -352 1079 -352 1069 -5712 1075 -1076 351 -354 1085 -326 1109 -344 1083 -1054 387 -1052 355 -1054 375 -352 1085 -318 1097 -1076 379 -314 1099 -334 1103 -356 1079 -320 1091 -348 1085 -352 1081 -350 1067 -5708 1075 -1076 355 -350 1085 -326 1079 -374 1053 -1084 351 -1084 353 -1090 343 -350 1079 -354 1069 -1080 379 -346 1073 -332 1073 -382 1065 -352 1067 -380 1067 -326 1081 -374 1069 -5680 1103 -1070 345 -346 1099 -330 1073 -388 1047 -1070 351 -1078 385 -1058 363 -346 1081 -354 1087 -1046 351 -368 1095 -348 1051 -354 1077 -384 1065 -326 1113 -344 1085 -350 1061 -5708 1053 -1084\nRAW_Data: 351 -364 1073 -372 1069 -352 1083 -1040 353 -1110 353 -1056 361 -348 1081 -354 1083 -1078 351 -376 1059 -346 1055 -354 1079 -386 1057 -364 1071 -368 1037 -386 1047 -5706 1073 -1084 355 -334 1101 -336 1083 -354 1083 -1074 351 -1072 383 -1050 351 -360 1087 -346 1087 -1038 383 -344 1087 -346 1063 -362 1077 -370 1051 -354 1085 -356 1087 -344 1065 -5704 1081 -1074 345 -384 1047 -354 1069 -384 1057 -1086 325 -1078 373 -1052 385 -330 1079 -374 1049 -1072 381 -346 1085 -344 1067 -362 1073 -370 1067 -334 1101 -332 1071 -386 1049 -5712 1077 -1044 383 -346 1075 -370 1065 -348 1085 -1048 361 -1072 389 -1048 353 -364 1085 -354 1079 -1042 385 -354 1087 -328 1073 -374 1071 -352 1081 -320 1099 -348 1085 -352 1083 -5668 1087 -1084 353 -364 1069 -368 1053 -380 1067 -1072 353 -1078 349 -1068 355 -362 1067 -348 1083 -1074 357 -350 1085 -356 1081 -344 1083 -354 1047 -354 1093 -348 1083 -352 1079 -5704 1073 -1052 387 -330 1077 -372 1051 -352 1085 -1076 349 -1072 379 -1070 323 -388 1063 -348 1077 -1064 375 -352 1079 -352 1065 -384 1049 -352 1083 -352 1069 -350 1085 -352 1083 -5676 1085 -1080 351 -366 1071 -366 1053 -352 1085 -1076 351 -1070 379 -1064 349 -360 1065 -364 1073 -1064 369 -346 1071 -358 1073 -350 1065 -354 1091 -380 1051 -348 1083 -352 1069 -5712 1077 -1074 353 -352 1085 -326 1075 -374 1065 -1066 361 -1074 371 -1056 387 -330 1071 -372 1051 -1086 353 -336 1095 -370 1069 -354 1079 -320 1095 -348 1097 -350 1051 -354 1077 -5708 1075 -1078 355 -352 1085 -326 1081 -374 1051 -1082 349 -1100 321 -1098 345 -348 1101 -332 1099 -1072 325 -388 1065 -334 1099 -368 1051 -352 1083 -356 1053 -378 1083 -352 1047 -5710 1073 -1072 383 -314 1113 -344 1067 -364 1067 -1068 371 -1054 389 -1050 355 -362 1087 -354 1049 -1074 351 -382 1053 -362 1077 -370 1051 -352 1095 -352 1065 -348 1097 -324 1079 -5690 1101 -1074 355 -350 1081 -350 1085 -346 1081 -1050 387 -1048 355 -1094 345 -350 1083 -352 1069 -1074 379 -346 1073 -366 1067 -346 1085 -354 1073 -352 1055 -360 1075 -374 1051 -5718 1071 -1048 395 -356 1047 -354 1091 -346 1085 -1052 383 -1066 349 -1070 381 -314 1101 -334 1085 -1074 351 -346 1081 -376 1067 -332 1099 -334 1099 -336 1101 -330 1085 -350 1065 -5702 1075 -1072 381 -348 1051 -374 1069 -366 1067 -1064 371 -1070 351 -1080 351 -376 1047 -382 1047 -1078 357 -352 1083 -358 1053 -374 1051 -386 1049 -354 1093 -344 1065 -350 1079 -5720 1067 -1076 355 -352 1085 -326 1077 -376 1053 -1086 353 -1084 355 -1056 375 -344 1093 -322 1105 -1074 351 -348 1073 -372 1065 -334 1099 -332 1101 -346 1083 -352 1079 -354 1057 -5706 1077 -1074 347 -348 1081 -354 1071 -384 1057 -1052\nRAW_Data: 361 -1076 373 -1050 387 -330 1075 -374 1049 -1086 387 -330 1073 -370 1053 -352 1083 -354 1085 -346 1065 -362 1071 -370 1065 -5682 1105 -1036 377 -346 1099 -330 1085 -354 1085 -1046 353 -1096 347 -1086 351 -360 1085 -344 1085 -1052 381 -314 1113 -344 1083 -354 1047 -354 1093 -348 1067 -362 1073 -374 1049 -5716 1071 -1052 363 -388 1047 -354 1091 -346 1065 -1080 347 -1090 345 -1064 379 -358 1069 -334 1097 -1062 369 -352 1083 -320 1099 -348 1065 -362 1075 -374 1067 -334 1099 -332 1073 -5720 1073 -1048 395 -356 1049 -356 1089 -346 1065 -1084 343 -1072 361 -1084 355 -334 1101 -336 1099 -1058 363 -344 1085 -354 1089 -326 1075 -376 1051 -356 1081 -356 1085 -346 1065 -5688 1111 -1058 367 -348 1065 -352 1069 -384 1067 -1042 379 -1070 383 -1052 351 -330 1115 -346 1087 -1056 351 -346 1077 -376 1083 -352 1061 -352 1067 -350 1097 -326 1109 -346 1083 -5686 1075 -1048 397 -356 1047 -354 1091 -346 1085 -1054 389 -1048 353 -1058 377 -352 1085 -352 1069 -1074 381 -350 1051 -352 1069 -386 1055 -366 1069 -368 1051 -354 1083 -356 1083 -5672 1081 -1074 351 -382 1055 -364 1069 -372 1053 -1086 355 -1054 359 -1076 373 -352 1079 -350 1065 -1078 383 -316 1083 -354 1073 -352 1085 -352 1083 -350 1071 -348 1099 -328 1075 -5694 1099 -1072 351 -354 1083 -326 1111 -342 1085 -1054 353 -1062 381 -1064 379 -314 1101 -334 1099 -1058 361 -348 1081 -354 1081 -348 1087 -346 1085 -356 1049 -354 1091 -348 1065 -5710 1073 -1078 347 -346 1101 -332 1073 -350 1093 -1074 355 -1050 385 -1062 347 -366 1099 -326 1077 -1066 373 -352 1083 -320 1097 -350 1097 -326 1075 -376 1065 -332 1097 -370 1065 -5684 1101 -1036 377 -344 1101 -330 1073 -356 1079 -1074 349 -1074 385 -1056 365 -344 1081 -354 1085 -1042 379 -340 1095 -348 1085 -320 1109 -356 1055 -332 1101 -334 1107 -354 1049 -5704 1087 -1048 389 -350 1081 -320 1111 -356 1055 -1052 389 -1048 377 -1056 391 -328 1073 -374 1057 -1084 353 -344 1077 -372 1053 -356 1083 -356 1085 -346 1063 -352 1081 -352 1073 -5712 1073 -1074 353 -356 1079 -318 1117 -346 1065 -1056 389 -1048 381 -1066 383 -314 1081 -352 1073 -1080 385 -316 1085 -352 1071 -352 1097 -326 1109 -344 1071 -354 1081 -320 1099 -5696 1069 -1076 355 -356 1083 -324 1113 -346 1063 -1078 347 -1090 379 -1032 377 -356 1069 -354 1081 -1040 387 -354 1087 -326 1075 -376 1069 -342 1097 -330 1077 -356 1083 -324 1115 -5678 1081 -1076 349 -346 1089 -334 1095 -336 1107 -1058 355 -1054 389 -1046 377 -354 1081 -320 1097 -1078 379 -314 1097 -332 1099 -338 1093 -332 1073 -354 1091 -350 1069 -350 1095 -5674 1111 -1044 381 -314 1099 -336 1097 -336 1095 -1064 369 -1038 389 -1050 381 -338 1093 -350\nRAW_Data: 1081 -1044 395 -322 1081 -354 1087 -348 1085 -356 1083 -320 1093 -348 1089 -356 1049 -5706 1077 -1052 387 -350 1087 -320 1113 -318 1097 -1042 379 -1066 381 -1030 379 -362 1063 -364 1069 -1068 373 -356 1081 -320 1095 -350 1087 -352 1063 -352 1071 -350 1099 -326 1111 -5670 1081 -1076 349 -380 1057 -334 1097 -334 1089 -1054 389 -1056 361 -1072 373 -354 1083 -320 1091 -1074 381 -318 1079 -352 1071 -352 1091 -348 1083 -322 1113 -358 1053 -332 1133 -810 65 -262 181 -142572 65 -98 199 -100 133 -300 99 -166 99 -232 99 -134 333 -166 231 -330 99 -230 493 -100 361 -66 525 -12248 131 -268 133 -268 131 -400 229 -726 97 -494 265 -402 65 -664 363 -134 365 -166 131 -262 395 -98 795 -98 68827 -6766 99 -1626 99 -66 65 -700 97 -864 565 -464 99 -98 133 -230 197 -166 591 -98 5135 -12244 97 -2452 65 -734 165 -198 233 -266 631 -198 12423 -12322 165 -796 67 -100 99 -98 67 -862 133 -728 263 -66 361 -166 129 -100 395 -166 625 -232 131 -66 8113 -12068 229 -132 65 -1394 99 -134 65 -300 165 -134 65 -102 99 -166 133 -766 297 -98 199 -102 1067 -166 859 -98 25307 -13210 65 -1764 97 -264 163 -496 133 -132 65 -66 427 -98 721 -68 531 -134 1591 -100 54955 -11122 65 -726 65 -368 65 -492 99 -428 233 -164 99 -266 131 -498 197 -430 295 -66 2793 -130 11307 -12324 97 -132 193 -1216 65 -1286 131 -594 399 -100 431 -266 495 -100 165 -132 63531 -12252 99 -166 165 -596 99 -496 99 -298 97 -368 65 -134 131 -198 165 -434 99 -266 301 -364 37153 -11928 65 -332 195 -528 131 -230 97 -1126 131 -530 99 -790 361 -100 895 -98 597 -66 699 -100 15803 -10136 99 -2044 65 -400 165 -534 165 -134 99 -434 65 -966 97 -100 133 -134 1125 -66 589 -100 26555 -12218 165 -334 65 -100 99 -2586 65 -732 531 -100 461 -132 3347 -10206 97 -2084 63 -296 99 -164 65 -1052 97 -66 231 -330 65 -232 331 -596 1119 -424 361 -66 28413 -12166 67 -264 161 -228 97 -390 65 -856 131 -66 763 -132 201 -132 9717 -13508 129 -990 65 -100 97 -296 165 -724 65 -368 231 -98 401 -66 527 -66 767 -100 637 -66 109351 -13036 165 -796 65 -200 133 -100 199 -600 133 -134 99 -932 461 -132 399 -264 47323 -12184 97 -566 231 -400 67 -1456 229 -132 97 -394 163 -230 1579 -66 231 -164 10685 -12812 99 -132 67 -464 133 -1162 331 -132 99 -266 167 -200 99 -268 791 -164 823 -132 329 -134 63603 -10646 65 -662 165 -234 163 -630 463 -98 233 -100 195 -262 131 -132 491 -12204 65 -1156 131 -954 295 -132 99 -332 65 -664 789 -328\nRAW_Data: 1351 -8346 67 -4708 133 -400 99 -298 65 -556 65 -396 295 -66 263 -64 163 -100 131 -100 97 -68 131 -166 65 -166 497 -132 999 -132 5609 -4806 131 -4842 131 -298 133 -332 67 -596 131 -1186 263 -268 495 -100 461 -462 165 -66 525 -100 1579 -100 4557 -10356 165 -1614 165 -98 97 -630 65 -164 461 -164 65 -558 231 -130 131 -164 591 -66 557 -9122 65 -166 101 -6060 295 -68 65 -594 163 -368 263 -98 65 -98 99 -294 99 -66 363 -432 329 -66 1987 -100 25465 -9810 197 -100 131 -262 97 -530 99 -168 65 -166 431 -530 99 -232 167 -266 97 -234 1065 -66 131 -198 17549 -9930 899 -100 367 -994 67 -132 167 -794 231 -98 99 -464 299 -134 131 -100 199 -732 97 -296 131 -66 1157 -132 359 -130 47799 -12132 99 -630 197 -132 165 -502 195 -164 261 -98 261 -98 263 -66 195 -296 195 -198 65 -66 393 -66 955 -100 12501 -12452 97 -2942 165 -262 97 -228 65 -98 359 -130 625 -100 723 -66 365 -13612 99 -832 233 -66 99 -298 65 -464 233 -164 65 -164 1053 -362 10409 -11212 197 -794 131 -970 67 -132 99 -366 167 -366 165 -134 167 -664 99 -302 1253 -200 131 -15330 97 -132 259 -100 63 -558 165 -398 65 -860 231 -66 163 -98 263 -364 559 -100 1313 -66 89369 -10410 65 -1788 233 -132 133 -1062 165 -532 65 -632 63 -794 65 -166 97 -132 133 -66 431 -100 467 -132 199 -364 15615 -12276 99 -662 165 -66 133 -564 197 -1460 65 -66 65 -364 231 -396 165 -134 5835 -12662 165 -436 163 -468 65 -100 299 -728 99 -534 265 -166 263 -100 627 -100 197 -66 43301 -12090 163 -96 197 -692 97 -862 129 -368 165 -232 65 -998 197 -66 393 -196 683 -98 6471 -12036 165 -662 133 -302 99 -566 727 -66 2289 -66 7119 -12372 99 -662 97 -300 99 -66 131 -962 165 -362 131 -168 65 -428 133 -134 1797 -66 63965 -10524 263 -1458 265 -298 99 -166 199 -130 789 -68 23871 -12668 65 -990 99 -196 331 -990 755 -100 233 -132 267 -13192 65 -932 233 -134 199 -164 133 -568 99 -898 331 -134 99 -66 961 -66 7013 -10302 65 -664 131 -432 99 -532 199 -466 329 -364 65 -134 429 -198 759 -132 617 -198 38455 -12426 199 -134 99 -1252 99 -396 229 -660 99 -362 197 -164 197 -132 689 -66 161 -98 357 -330 265 -100 15001 -11544 133 -636 131 -132 99 -232 65 -1488 165 -636 131 -266 133 -134 367 -66 197 -100 267 -6574 131 -260 165 -166 65 -132 65 -828 295 -66 589 -100\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/linear.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Linear\nBit: 10\nKey: 00 00 00 00 00 00 01 E4\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: LinearDelta3\nBit: 8\nKey: 00 00 00 00 00 00 00 D0\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971\nRAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997\nRAW_Data: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/linear_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554\nRAW_Data: 487 -1556 453 -1570 441 -1590 1467 -556 469 -1538 1501 -522 1501 -552 463 -22124 1513 -514 1509 -548 445 -1582 461 -1568 443 -1574 1467 -536 465 -1574 1499 -516 1503 -556 441 -22150 1537 -484 1495 -544 479 -1558 459 -1574 447 -1580 1471 -548 459 -1576 1471 -554 1471 -550 459 -22160 1517 -516 1503 -546 447 -1574 473 -1546 481 -1576 1479 -520 467 -1572 1497 -550 1469 -556 441 -22156 1533 -528 1495 -530 469 -1568 445 -1566 485 -1548 1505 -544 451 -1572 1471 -554 1503 -516 495 -22160 1511 -520 1477 -546 483 -1542 485 -1546 481 -1542 1509 -526 455 -1578 1507 -544 1481 -524 485 -22146 1543 -510 1481 -552 475 -1546 473 -1568 481 -1540 1503 -522 473 -1556 1507 -558 1491 -518 479 -22164 1523 -520 1503 -544 453 -1566 479 -1550 469 -1572 1493 -546 447 -1576 1477 -552 1509 -544 451 -22178 639 -596 65 -926 63 -1954 237 -1680 355 -1670 383 -1636 1423 -596 439 -1578 1471 -574 1465 -576 445 -22182 1439 -676 1335 -702 339 -1648 387 -1648 409 -1608 1429 -618 419 -1612 1447 -578 1439 -614 425 -22186 1497 -530 1505 -556 471 -1540 479 -1582 431 -1570 1497 -546 481 -1536 1497 -554 1501 -556 463 -22178 1503 -566 1433 -586 433 -1604 445 -1576 441 -1580 1503 -548 453 -1572 1473 -578 1467 -558 469 -149080 311 -1034 895 -1084 939 -1006 337 -78790 97 -6890 2535 -28048 2511 -64482 2573 -88138 65 -6956 2515 -95140 2509 -61986 1589 -522 1475 -548 449 -1576 475 -1548 481 -1546 1475 -564 443 -1564 1477 -580 1477 -554 449 -22166 1503 -532 1481 -544 447 -1576 449 -1572 473 -1548 1473 -580 451 -1568 1463 -552 1477 -552 461 -22164 1521 -528 1493 -548 443 -1580 461 -1540 475 -1574 1473 -554 471 -1568 1489 -516 1497 -538 471 -22154 1385 -786 1245 -796 261 -1702 341 -1650 391 -1644 1411 -608 409 -1620 1415 -624 1409 -614 413 -22178 1533 -498 1519 -546 445 -1576 449 -1578 457 -1568 1503 -546 451 -1570 1467 -554 1503 -552 463 -22162 1401 -774 1257 -754 297 -1678 353 -1650 383 -1666 1397 -618 385 -1642 1437 -612 1433 -586 427 -22194 1483 -552 1477 -546 485 -1544 453 -1576 481 -1576 1479 -560 449 -1548 1507 -546 1479 -558 447 -22170 1413 -786 1245 -776 295 -1664 337 -1680 387 -1652 1385 -638 407 -1614 1441 -582 1451 -606 409 -22204 1503 -528 1507 -550 465 -1534 481 -1584 429 -1572 1497 -548 447 -1580 1481 -550 1503 -546 455 -22166 1519 -552 1469 -580 419 -1612 419 -1596 447 -1582 1479 -554 447 -1582 1479 -550 1519 -518 479 -134730 169 -172 559 -194 579 -676 257 -87040 99 -7384 2269 -488 313 -37888 65 -12084 2515 -33298 2247 -474 309 -42656 63 -51970 2233 -456 309 -94658 2233 -430 325 -90692 357 -186 2417 -518 1517 -514 475 -1550 467 -1570 471 -1538 1499 -556 443 -1564 1483 -552 1501 -524\nRAW_Data: 481 -22144 1433 -668 1373 -666 337 -1680 355 -1634 383 -1634 1431 -622 399 -1606 1443 -582 1443 -586 431 -22166 1521 -512 1497 -548 447 -1580 459 -1568 447 -1574 1473 -554 465 -1572 1497 -516 1507 -554 453 -22148 1503 -540 1489 -546 479 -1542 449 -1578 457 -1576 1475 -546 483 -1540 1507 -552 1473 -548 453 -22154 1539 -516 1469 -586 431 -1574 447 -1576 479 -1558 1485 -554 439 -1580 1501 -550 1457 -552 473 -22154 1521 -550 1485 -540 447 -1578 457 -1578 447 -1572 1471 -556 485 -1540 1483 -552 1501 -556 443 -22186 1437 -662 1345 -704 339 -1688 361 -1646 375 -1640 1399 -616 421 -1612 1441 -614 1435 -586 429 -22200 1513 -520 1503 -544 453 -1572 447 -1580 483 -1550 1475 -552 475 -1548 1501 -554 1491 -540 475 -22184 1499 -530 1485 -548 457 -1570 477 -1578 443 -1576 1501 -546 449 -1562 1499 -538 1493 -572 443 -22166 1471 -612 1429 -624 401 -1608 417 -1620 431 -1600 1461 -582 419 -1610 1441 -582 1473 -548 459 -205658 1761 -436 863 -68558 2597 -23434 1775 -438 519 -94592 1803 -422 829 -86706 1727 -434 527 -66 319 -94536 1753 -444 865 -238 1489 -652 1385 -656 357 -1668 365 -1628 403 -1612 1439 -614 403 -1626 1425 -588 1443 -584 409 -22168 1531 -528 1491 -530 469 -1568 443 -1564 483 -1538 1505 -546 455 -1570 1469 -556 1503 -518 491 -22128 1455 -660 1359 -686 355 -1632 385 -1632 411 -1640 1411 -596 423 -1606 1437 -580 1443 -596 423 -22188 1495 -550 1467 -534 473 -1566 481 -1540 483 -1572 1477 -552 449 -1582 1477 -558 1471 -552 449 -22174 543 -3692 235 -1680 357 -1668 353 -1698 1375 -636 371 -1644 1409 -610 1437 -578 449 -22180 1499 -542 1491 -542 479 -1540 481 -1566 449 -1562 1499 -554 465 -1568 1463 -554 1493 -536 473 -22168 1405 -774 1257 -788 267 -1676 355 -1672 369 -1660 1387 -624 417 -1606 1427 -624 1425 -586 415 -22208 1507 -552 1489 -548 443 -1562 483 -1550 481 -1546 1505 -558 447 -1586 1473 -548 1481 -554 479 -22166 1383 -786 1245 -776 295 -1664 373 -1646 387 -1644 1403 -642 411 -1624 1417 -594 1445 -612 409 -22210 1515 -516 1503 -554 453 -1564 457 -1572 447 -1580 1501 -554 465 -1570 1467 -554 1493 -570 443 -195814 1353 -442 1267 -37900 99 -53710 411 -142 2207 -8336 2487 -46990 1301 -452 1317 -86162 65 -8376 1289 -444 1327 -94552 1311 -488 1251 -1726 1531 -544 1467 -580 417 -1578 457 -1578 443 -1576 1475 -566 441 -1568 1503 -512 1509 -526 455 -22168 1473 -582 1469 -538 483 -1538 459 -1574 447 -1580 1471 -582 429 -1572 1469 -552 1473 -548 461 -22170 1437 -640 1401 -620 393 -1636 385 -1634 409 -1604 1457 -588 415 -1608 1431 -590 1459 -554 449 -22188 1483 -556 1487 -542 445 -1578 459 -1576 443 -1574 1473 -556 469 -1570 1459 -550 1503 -554 443 -22160 1469 -590\nRAW_Data: 1469 -582 409 -1620 429 -1608 415 -1602 1455 -568 441 -1578 1475 -552 1479 -560 467 -22166 1483 -572 1463 -546 451 -1580 453 -1580 481 -1542 1475 -588 451 -1570 1453 -550 1503 -540 469 -22158 1505 -554 1475 -554 487 -1556 453 -1562 477 -1546 1495 -546 479 -1544 1511 -554 1477 -532 471 -22180 1471 -602 1437 -602 407 -1610 421 -1614 421 -1606 1433 -582 457 -1578 1475 -550 1469 -586 465 -22164 1515 -546 1469 -546 483 -1572 447 -1558 471 -1580 1471 -578 451 -1568 1495 -542 1497 -530 471 -22194 1433 -644 1393 -662 371 -1640 385 -1642 413 -1628 1431 -584 431 -1602 1469 -540 1501 -576 443 -22180 1479 -592 1439 -608 407 -1620 427 -1610 415 -1606 1465 -570 441 -1612 1441 -586 1451 -576 445 -22196 1533 -522 1489 -540 477 -1574 481 -1544 455 -1568 1505 -546 459 -1572 1501 -556 1497 -530 471 -210202 685 -540 475 -138 839 -138 239 -9950 2501 -71112 761 -462 511 -66 1297 -85614 97 -8850 745 -466 1821 -94546 827 -468 481 -140 815 -164 237 -32934 1583 -540 1477 -554 441 -1582 447 -1580 453 -1568 1465 -554 479 -1556 1487 -558 1477 -550 453 -22174 1503 -516 1483 -584 429 -1572 449 -1576 447 -1588 1475 -558 449 -1580 1473 -546 1485 -556 449 -22168 1469 -586 1443 -614 401 -1624 401 -1608 415 -1608 1463 -570 441 -1574 1471 -586 1449 -572 445 -22164 1401 -772 1255 -756 301 -1674 353 -1708 329 -1662 1397 -650 387 -1632 1405 -614 1433 -584 431 -22200 1489 -566 1463 -582 419 -1580 457 -1596 447 -1576 1473 -554 443 -1580 1469 -582 1451 -550 479 -22174 1387 -786 1251 -792 257 -1704 331 -1690 337 -1706 1377 -632 395 -1632 1433 -586 1451 -584 413 -22198 1541 -512 1485 -554 481 -1574 445 -1560 461 -1568 1495 -556 469 -1568 1495 -518 1503 -554 469 -22160 1541 -516 1501 -556 443 -1564 495 -1536 479 -1550 1517 -544 445 -1576 1507 -556 1485 -542 445 -22202 1501 -530 1503 -554 465 -1574 445 -1582 461 -1574 1469 -546 479 -1568 1471 -556 1513 -550 463 -22170 1473 -614 1419 -602 439 -1614 417 -1610 415 -1590 1465 -588 429 -1606 1469 -586 1455 -570 441 -22200 1407 -760 1277 -772 267 -1692 363 -1678 355 -1668 1393 -660 369 -1642 1441 -584 1449 -574 445 -22206 1533 -528 1497 -530 471 -1572 449 -1576 455 -1578 1507 -544 455 -1572 1503 -556 1467 -560 469 -22188 1439 -678 1347 -672 375 -1648 393 -1646 387 -1636 1431 -588 431 -1610 1439 -618 1437 -596 437 -143610 67 -510 99 -66 223 -272 173 -70 273 -204 235 -64 483 -26542 2521 -65448 297 -348 2417 -76720 65 -8388 65 -9450 203 -332 587 -72 885 -92 899 -27534 67 -39500 221 -406 521 -72 885 -98 919 -29920 1581 -534 1491 -548 445 -1580 461 -1544 477 -1574 1465 -556 473 -1568 1455 -554 1501 -524 483 -22136 1529 -498\nRAW_Data: 1493 -576 441 -1568 469 -1574 447 -1576 1465 -548 481 -1544 1503 -542 1473 -554 449 -22176 1499 -530 1505 -554 463 -1540 479 -1546 479 -1558 1483 -556 449 -1580 1473 -548 1483 -552 447 -22198 1511 -508 1507 -554 441 -1580 453 -1586 453 -1576 1473 -548 451 -1586 1475 -554 1499 -554 445 -22180 1503 -526 1481 -550 481 -1544 485 -1574 449 -1566 1479 -548 461 -1572 1503 -540 1499 -530 471 -22192 1499 -530 1475 -584 433 -1572 475 -1586 429 -1570 1495 -548 449 -1578 1473 -552 1505 -546 455 -22202 1499 -544 1469 -562 471 -1570 447 -1580 457 -1568 1505 -544 451 -1574 1471 -554 1503 -554 465 -22160 1533 -530 1485 -548 459 -1600 443 -1576 441 -1580 1505 -544 455 -1568 1497 -556 1495 -530 473 -22200 1499 -544 1491 -544 479 -1572 451 -1572 447 -1578 1479 -550 493 -1564 1465 -590 1451 -576 445 -22198 1531 -532 1483 -548 479 -1542 483 -1576 449 -1562 1501 -554 465 -1574 1469 -556 1491 -572 445 -22196 1533 -528 1495 -532 473 -1574 449 -1580 457 -1576 1505 -546 453 -1572 1503 -556 1465 -558 471 -22200 1499 -530 1517 -552 463 -1568 445 -1580 457 -1578 1471 -578 451 -1574 1473 -554 1503 -556 469 -22188 1501 -554 1473 -544 479 -1592 461 -1568 447 -1576 1493 -552 453 -1584 1475 -554 1503 -554 471 -141048 233 -68 741 -68 133 -100 467 -49002 2539 -43448 2529 -91062 2557 -95072 2545 -89712 641 -300 1759 -3612 463 -9880 1221 -712 327 -1694 1367 -616 1445 -582 409 -22192 1505 -522 1497 -554 447 -1584 455 -1570 449 -1578 1473 -548 459 -1572 1475 -576 1465 -554 469 -22142 1507 -526 1509 -548 455 -1574 447 -1578 447 -1580 1479 -554 441 -1580 1467 -584 1457 -554 445 -22200 1513 -510 1509 -526 473 -1562 481 -1554 455 -1574 1473 -544 479 -1556 1481 -550 1497 -554 453 -22156 1529 -524 1497 -552 453 -1588 451 -1574 449 -1582 1475 -546 455 -1572 1505 -520 1503 -554 465 -22156 1537 -516 1485 -548 459 -1570 481 -1550 467 -1570 1495 -548 447 -1576 1493 -552 1481 -548 459 -22194 1513 -524 1481 -578 453 -1574 451 -1564 485 -1558 1477 -556 449 -1580 1471 -582 1491 -516 479 -22170 1535 -530 1499 -548 449 -1580 457 -1574 445 -1576 1511 -554 431 -1572 1497 -548 1507 -556 429 -22194 1523 -546 1473 -568 429 -1598 443 -1606 451 -1574 1471 -546 485 -1562 1481 -554 1503 -540 471 -22190 1387 -788 1277 -776 263 -1696 335 -1680 385 -1652 1385 -638 407 -1614 1445 -594 1445 -612 411 -22212 1541 -518 1499 -554 445 -1562 461 -1568 475 -1560 1495 -554 481 -1556 1487 -552 1507 -556 443 -22214 1521 -514 1507 -552 451 -1570 461 -1606 447 -1578 1483 -556 467 -1572 1471 -552 1491 -574 445 -22232 1507 -528 1481 -562 473 -1574 447 -1580 457 -1578 1505 -546 459 -1574 1471 -588 1461 -562 473 -22200 1431 -694\nRAW_Data: 1341 -740 305 -1690 361 -1644 387 -1670 1391 -626 403 -1644 1441 -598 1443 -608 411 -22244 1515 -518 1501 -552 439 -1598 469 -1572 447 -1576 1495 -554 453 -1586 1475 -552 1501 -554 467 -182986 65 -11040 2545 -94994 2537 -94978 2557 -10004 2527 -82554 2547 -84234 65 -10744 2497 -32446 1555 -544 1479 -558 421 -1608 447 -1582 451 -1542 1509 -552 449 -1548 1507 -528 1479 -550 481 -22134 1537 -518 1483 -548 457 -1566 477 -1574 441 -1572 1465 -552 481 -1576 1445 -586 1453 -552 477 -22154 1519 -522 1503 -546 449 -1570 441 -1578 485 -1558 1481 -558 449 -1550 1505 -544 1479 -558 449 -22154 1515 -542 1479 -556 447 -1594 459 -1544 475 -1570 1467 -558 469 -1566 1491 -548 1465 -558 487 -22132 1403 -776 1249 -800 271 -1678 357 -1648 393 -1634 1399 -650 389 -1610 1441 -610 1437 -584 429 -22204 1499 -528 1505 -552 463 -1574 447 -1578 443 -1558 1485 -552 479 -1548 1491 -574 1467 -546 483 -22168 1453 -632 1431 -614 389 -1614 419 -1634 411 -1584 1453 -616 391 -1636 1431 -582 1479 -554 443 -22204 887 -198 67 -950 65 -194 63 -200 65 -498 65 -910 261 -1696 339 -1646 387 -1650 1417 -640 377 -1614 1441 -612 1439 -618 401 -22186 1539 -516 1505 -556 437 -1596 463 -1572 447 -1582 1477 -546 481 -1540 1505 -552 1499 -554 453 -22184 1529 -500 1497 -586 431 -1570 471 -1576 447 -1576 1477 -552 481 -1580 1479 -558 1473 -554 481 -168716 2521 -95042 2539 -94986 2535 -25722 2535 -26624 2505 -18626 99 -76268 2541 -94974 2559 -17198 65 -6348 67 -42186 2539 -17892 131 -93800 99 -394 65 -7158 1331 -448"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/magellan.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Magellan\nBit: 32\nKey: 00 00 00 00 37 AE 48 28\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/magellan_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153\nRAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65\nRAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/marantec.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Marantec\nBit: 49\nKey: 00 01 30 07 10 DF 86 9F\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/marantec24.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Marantec24\nBit: 24\nKey: 00 00 00 00 00 AC 05 C4\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/marantec24_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 868350000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1224 -1300 1801 -3200 1615 -2374 821 -3198 1603 -2410 785 -3196 815 -3188 1605 -2398 1605 -2398 1607 -2400 1605 -2398 1605 -2396 1601 -2400 1605 -2398 809 -3184 1603 -2396 815 -3194 811 -3190 811 -3194 1603 -2398 1603 -2398 1601 -2392 811 -3194 1605 -2394 1607 -16170 131 -196 217 -493440 135 -1296 1807 -3210 1591 -2396 811 -3198 1591 -2394 807 -3218 807 -3188 1607 -2400 1605 -2394 1599 -2402 1601 -2392 1609 -2400 1605 -2392 1603 -2392 815 -3190 1609 -2402 809 -3188 813 -3192 813 -3190 1605 -2400 1603 -2396 1605 -2396 813 -3194 1601 -2402 1601 -16170 133 -200 219 -382462 101 -1314 1813 -3194 1581 -2414 801 -3214 1605 -2398 807 -3190 811 -3190 1601 -2394 1603 -2398 1607 -2398 1605 -2396 1605 -2394 1601 -2394 1609 -2396 815 -3194 1603 -2400 809 -3194 811 -3190 811 -3190 1603 -2396 1603 -2396 1607 -2398 811 -3192 1607 -2396 1601 -16176 131 -198 217 -348436 303 -1106 1817 -3194 1609 -2386 803 -3214 1597 -2392 811 -3186 815 -3192 1611 -2396 1601 -2394 1609 -2404 1605 -2396 1605 -2396 1607 -2392 1601 -2396 813 -3192 1603 -2394 813 -3190 815 -3188 811 -3194 1601 -2396 1607 -2394 1601 -2396 815 -3186 1611 -2396 1609 -16168 131 -198 217 -349492 347 -1080 1799 -3192 1617 -2388 801 -3218 1601 -2396 809 -3190 809 -3192 1605 -2394 1599 -2394 1605 -2398 1607 -2398 1599 -2398 1607 -2396 1601 -2396 811 -3192 1605 -2400 811 -3184 813 -3192 811 -3192 1607 -2398 1601 -2398 1607 -2400 811 -3190 1603 -2394 1607 -16176 131 -204 213 -447994 135 -1330 977 -1036 65 -230 181 -514880 135 -66 67 -1140 1815 -3192 1615 -2388 803 -3214 1603 -2396 813 -3190 811 -3190 1609 -2398 1601 -2392 1603 -2396 1607 -2398 1607 -2398 1607 -2398 1605 -2394 813 -3186 1611 -2404 809 -3186 811 -3192 813 -3188 1605 -2394 1607 -2400 1607 -2398 807 -3190 1605 -2398 1603 -16172 131 -200 217 -395118 99 -1304 1841 -3160 1621 -2388 805 -3186 1631 -2392 809 -3182 811 -3188 1605 -2402 1601 -2398 1601 -2402 1607 -2398 1605 -2398 1601 -2392 1605 -2404 809 -3188 1599 -2396 817 -3192 811 -3192 811 -3190 1599 -2392 1603 -2398 1607 -2404 809 -3192 1603 -2402 1605 -16164 133 -198 187 -382202 137 -1302 1803 -3192 1601 -2404 803 -3184 1633 -2392 807 -3190 811 -3188 1601 -2400 1607 -2402 1601 -2392 1601 -2396 1607 -2400 1603 -2398 1605 -2398 811 -3184 1601 -2400 813 -3188 809 -3190 811 -3192 1605 -2400 1605 -2396 1599 -2402 809 -3192 1607 -2400 1607 -16174 99 -232 181 -361858 65 -1368 1785 -3228 1567 -2426 777 -3222 1601 -2402 785 -3198 815 -3186 1609 -2402 1601 -2396 1605 -2402 1603 -2396 1605 -2398 1601 -2400 1603 -2398 813 -3194 1605 -2400 809 -3186 813 -3190 813 -3188 1607 -2396 1605 -2400 1605 -2394 809 -3182 1607 -2400 1607 -16210 65 -236 177 -379100 1819 -3192 1611 -2384 799 -3220 1601 -2396 811 -3186 809 -3188 1605 -2394 1607 -2398\nRAW_Data: 1599 -2394 1603 -2396 1609 -2402 1603 -2396 1607 -2398 809 -3196 1603 -2394 809 -3188 813 -3194 809 -3188 1605 -2394 1599 -2396 1609 -2404 811 -3184 1605 -2400 1601 -16162 133 -234 191 -393094 97 -1316 1021 -1010 67 -246 173 -677840 131 -64 59 -1178 1773 -3210 1593 -2430 773 -3222 1601 -2396 811 -3186 813 -3190 1603 -2398 1603 -2396 1607 -2398 1605 -2400 1601 -2396 1605 -2396 1601 -2396 815 -3190 1607 -2402 811 -3190 811 -3186 811 -3192 1607 -2396 1603 -2396 1601 -2400 813 -3188 1607 -2404 1607 -15160 827 -3204 1577 -2404 813 -3200 1591 -2390 807 -3220 807 -3186 1599 -2400 1605 -2398 1603 -2400 1603 -2396 1605 -2398 1605 -2398 1599 -2394 809 -3190 1611 -2394 809 -3186 815 -3192 809 -3192 1603 -2398 1601 -2396 1599 -2396 815 -3188 1609 -2400 1605 -15178 801 -3222 1575 -2392 805 -3190 1615 -2396 809 -3190 819 -3198 1601 -2402 1577 -2402 1609 -2400 1605 -2398 1605 -2396 1605 -2398 1601 -2400 809 -3184 1605 -2396 809 -3190 811 -3192 813 -3190 1601 -2404 1605 -2394 1599 -2396 811 -3190 1605 -2400 1601 -15174 831 -3166 1615 -2396 783 -3200 1611 -2398 813 -3192 811 -3188 1601 -2396 1603 -2398 1607 -2396 1605 -2396 1603 -2398 1601 -2394 1601 -2398 811 -3192 1601 -2394 813 -3192 809 -3194 807 -3190 1605 -2394 1599 -2394 1601 -2394 813 -3194 1601 -2398 1603 -15178 793 -3228 1577 -2404 809 -3186 1611 -2396 815 -3192 811 -3188 1605 -2400 1577 -2404 1603 -2396 1607 -2400 1601 -2398 1603 -2394 1605 -2396 811 -3192 1605 -2394 811 -3190 811 -3188 807 -3186 1601 -2396 1605 -2398 1601 -2398 813 -3186 1601 -2394 1605 -15178 797 -3204 1581 -2400 813 -3188 1603 -2400 813 -3192 811 -3192 1607 -2398 1605 -2398 1607 -2394 1601 -2400 1605 -2398 1601 -2396 1601 -2394 809 -3190 1605 -2400 811 -3186 813 -3192 811 -3184 1601 -2396 1605 -2396 1605 -2400 809 -3184 1599 -2400 1605 -15146 827 -3188 1613 -2402 785 -3200 1605 -2396 815 -3192 811 -3190 1605 -2396 1605 -2396 1607 -2398 1601 -2398 1601 -2392 1599 -2398 1603 -2398 811 -3186 1605 -2396 811 -3190 811 -3192 809 -3190 1597 -2394 1599 -2398 1605 -2398 813 -3186 1599 -2396 1605 -15176 793 -3226 1577 -2398 817 -3198 1605 -2370 817 -3192 813 -3192 1609 -2396 1601 -2396 1605 -2396 1603 -2394 1605 -2394 1607 -2398 1607 -2394 809 -3188 1605 -2400 811 -3194 809 -3188 811 -3192 1605 -2400 1603 -2388 1605 -2394 811 -3186 1605 -2396 1605 -15180 797 -3202 1605 -2398 781 -3196 1605 -2396 815 -3184 817 -3196 1603 -2398 1605 -2398 1605 -2400 1603 -2394 1601 -2396 1607 -2398 1605 -2396 811 -3188 1605 -2396 809 -3186 807 -3194 811 -3194 1603 -2396 1597 -2396 1605 -2396 811 -3192 1603 -2396 1601 -15146 831 -3190 1603 -2404 777 -3224 1605 -2398 785 -3196 813 -3192 1607 -2396 1601 -2398 1607 -2394 1605 -2398 1603 -2394 1601 -2400 1599 -2396 807 -3192 1603 -2396 809 -3186\nRAW_Data: 813 -3190 809 -3190 1603 -2400 1603 -2398 1599 -2390 813 -3192 1603 -2394 1603 -15146 823 -3204 1603 -2398 787 -3204 1595 -2388 805 -3216 807 -3192 1605 -2400 1601 -2398 1597 -2394 1605 -2396 1603 -2398 1605 -2392 1601 -2396 809 -3192 1605 -2398 807 -3190 809 -3186 811 -3194 1603 -2394 1605 -2394 1599 -2398 813 -3192 1609 -2394 1603 -15174 795 -3206 1605 -2398 783 -3196 1607 -2400 809 -3188 813 -3188 1609 -2402 1601 -2394 1609 -2396 1603 -2396 1603 -2400 1599 -2388 1599 -2400 811 -3192 1607 -2394 811 -3186 811 -3190 809 -3194 1603 -2400 1599 -2394 1601 -2392 813 -3192 1603 -2396 1605 -15162 799 -3218 1581 -2404 807 -3202 1603 -2396 787 -3196 819 -3192 1611 -2394 1603 -2396 1607 -2402 1601 -2392 1599 -2392 1601 -2400 1607 -2398 811 -3192 1603 -2396 811 -3190 813 -3188 813 -3186 1603 -2394 1601 -2392 1605 -2400 809 -3188 1609 -2394 1601 -15156 829 -3188 1601 -2414 797 -3182 1629 -2394 807 -3192 811 -3188 1603 -2398 1599 -2392 1603 -2398 1603 -2396 1603 -2396 1607 -2400 1607 -2392 809 -3186 1603 -2400 813 -3194 811 -3192 809 -3188 1599 -2400 1603 -2404 1609 -2394 809 -3194 1599 -2394 1607 -15176 829 -3158 1623 -2404 773 -3224 1605 -2386 799 -3210 811 -3190 1607 -2392 1605 -2398 1607 -2400 1601 -2388 1605 -2398 1603 -2402 1599 -2392 809 -3188 1605 -2400 811 -3186 813 -3190 809 -3190 1603 -2396 1599 -2404 1603 -2400 809 -3188 1605 -2396 1607 -15146 829 -3216 1561 -2414 817 -3196 1599 -2404 797 -3180 809 -3212 1607 -2400 1601 -2394 1603 -2400 1603 -2396 1599 -2400 1605 -2396 1605 -2394 813 -3186 1607 -2394 811 -3186 809 -3190 813 -3192 1607 -2398 1603 -2398 1601 -2396 813 -3192 1603 -2392 1605 -15144 827 -3188 1613 -2404 809 -3192 1615 -2370 811 -3188 811 -3188 1611 -2398 1603 -2400 1601 -2394 1601 -2402 1605 -2396 1601 -2394 1609 -2398 811 -3186 1603 -2392 813 -3196 811 -3182 809 -3188 1603 -2402 1605 -2400 1599 -2394 809 -3194 1607 -2398 1601 -15148 831 -3186 1587 -2424 811 -3192 1575 -2408 803 -3188 805 -3222 1599 -2392 1601 -2400 1609 -2398 1601 -2392 1597 -2394 1601 -2400 1601 -2400 807 -3190 1601 -2400 813 -3190 809 -3190 809 -3188 1607 -2398 1601 -2396 1605 -2398 809 -3184 1603 -2400 1605 -15176 795 -3200 1613 -2400 783 -3198 1603 -2398 813 -3192 809 -3186 1607 -2398 1605 -2398 1605 -2398 1599 -2394 1605 -2398 1603 -2392 1599 -2396 809 -3194 1609 -2390 811 -3194 813 -3188 811 -3188 1599 -2394 1603 -2398 1601 -2394 815 -3192 1605 -2402 1601 -15150 831 -3186 1605 -2412 799 -3206 1601 -2392 809 -3186 813 -3192 1603 -2402 1601 -2392 1605 -2398 1607 -2396 1601 -2392 1609 -2402 1599 -2396 811 -3186 1601 -2398 815 -3196 809 -3188 809 -3188 1607 -2394 1605 -2394 1603 -2394 811 -3194 1603 -2398 1607 -15174 797 -3218 1563 -2424 785 -3194 1633 -2396 795 -3178 805 -3214 1607 -2394 1603 -2400\nRAW_Data: 1603 -2392 1603 -2398 1605 -2400 1597 -2392 1603 -2396 811 -3198 1603 -2390 809 -3192 811 -3188 813 -3192 1599 -2394 1607 -2398 1607 -2394 811 -3192 1601 -2394 1605 -15152 861 -3154 1603 -2410 799 -3186 1601 -2422 809 -3190 809 -3186 1601 -2392 1603 -2394 1611 -2398 1605 -2400 1601 -2392 1609 -2396 1603 -2398 809 -3186 1605 -2400 811 -3188 811 -3188 807 -3192 1605 -2402 1597 -2392 1603 -2396 813 -3194 1601 -2398 1605 -15174 795 -3200 1597 -2428 785 -3196 1613 -2398 785 -3192 815 -3192 1611 -2400 1603 -2394 1603 -2400 1601 -2398 1607 -2394 1603 -2398 1603 -2396 811 -3190 1605 -2394 811 -3194 811 -3182 811 -3192 1607 -2392 1599 -2394 1601 -2398 811 -3192 1607 -2392 1603 -15156 827 -3190 1607 -2408 797 -3210 1599 -2398 777 -3218 811 -3192 1601 -2400 1599 -2398 1601 -2396 1597 -2396 1601 -2392 1607 -2392 1605 -2400 813 -3190 1601 -2396 811 -3194 809 -3190 813 -3190 1603 -2394 1599 -2390 1603 -2400 811 -3190 1607 -2398 1605 -15150 825 -3192 1603 -2404 793 -3178 1605 -2422 777 -3222 775 -3218 1603 -2398 1605 -2398 1597 -2392 1603 -2398 1605 -2400 1601 -2394 1601 -2392 811 -3192 1609 -2398 813 -3190 809 -3182 809 -3184 1607 -2398 1603 -2396 1607 -2398 813 -3190 1607 -2396 1601 -15172 797 -3230 1563 -2424 805 -3190 1599 -2404 795 -3182 807 -3214 1601 -2402 1603 -2396 1601 -2396 1603 -2398 1605 -2398 1599 -2392 1601 -2402 813 -3192 1601 -2396 809 -3188 811 -3188 809 -3192 1605 -2400 1607 -2398 1599 -2394 809 -3190 1607 -2400 1605 -15180 827 -3190 1577 -2394 819 -3196 1613 -2366 815 -3196 813 -3190 1607 -2396 1607 -2402 1599 -2398 1605 -2400 1603 -2398 1601 -2392 1605 -2400 811 -3186 1603 -2400 813 -3190 809 -3196 807 -3182 1601 -2396 1609 -2398 1601 -2398 813 -3192 1601 -2394 1607 -15174 793 -3202 1599 -2424 787 -3196 1607 -2404 785 -3192 815 -3188 1609 -2404 1603 -2398 1607 -2396 1601 -2400 1603 -2394 1601 -2394 1603 -2400 811 -3190 1605 -2400 811 -3188 809 -3190 813 -3194 1597 -2398 1607 -2394 1603 -2394 809 -3190 1603 -2392 1607 -15180 795 -3230 1581 -2392 807 -3218 1593 -2408 777 -3220 781 -3204 1597 -2424 1605 -2394 1595 -2392 1605 -2394 1599 -2396 1605 -2402 1603 -2394 809 -3188 1601 -2396 813 -3194 811 -3188 813 -3190 1605 -2398 1607 -2392 1601 -2398 811 -3192 1599 -2400 1605 -15178 797 -3218 1595 -2400 803 -3198 1605 -2370 825 -3182 803 -3214 1603 -2396 1601 -2398 1601 -2394 1599 -2398 1603 -2396 1607 -2394 1605 -2394 809 -3190 1607 -2398 809 -3194 811 -3188 809 -3194 1605 -2394 1599 -2396 1605 -2396 809 -3196 1603 -2402 1599 -15160 829 -3192 1579 -2400 817 -3198 1605 -2398 783 -3194 813 -3190 1609 -2400 1607 -2400 1599 -2394 1601 -2394 1609 -2398 1601 -2396 1605 -2394 809 -3186 1609 -2400 811 -3186 809 -3196 811 -3190 1605 -2396 1601 -2398 1607 -2396 809 -3186 1605 -2398 1607 -15170\nRAW_Data: 827 -3192 1595 -2376 813 -3192 1601 -2414 797 -3186 807 -3220 1601 -2398 1603 -2394 1599 -2396 1607 -2400 1605 -2394 1597 -2398 1603 -2398 813 -3192 1603 -2394 809 -3188 809 -3188 811 -3192 1607 -2396 1601 -2398 1605 -2398 811 -3188 1603 -2400 1603 -15174 829 -3162 1627 -2374 803 -3224 1571 -2412 801 -3186 807 -3222 1605 -2392 1605 -2392 1599 -2394 1605 -2404 1601 -2396 1607 -2396 1601 -2394 809 -3188 1601 -2398 811 -3190 815 -3194 811 -3184 1603 -2400 1605 -2400 1605 -2394 809 -3188 1609 -2394 1605 -15154 829 -3188 1603 -2412 801 -3210 1603 -2398 807 -3182 809 -3196 1605 -2400 1605 -2396 1599 -2402 1607 -2390 1599 -2396 1599 -2400 1607 -2396 811 -3192 1605 -2396 813 -3196 809 -3190 811 -3192 1609 -2394 1597 -2396 1607 -2398 809 -3186 1605 -2394 1605 -15178 795 -3216 1577 -2408 819 -3190 1607 -2394 817 -3194 779 -3194 1607 -2404 1605 -2398 1607 -2394 1601 -2398 1605 -2400 1603 -2402 1607 -2394 811 -3192 1601 -2396 809 -3186 813 -3194 811 -3188 1599 -2394 1601 -2402 1601 -2398 811 -3186 1603 -2396 1611 -15176 799 -3224 1567 -2424 785 -3202 1599 -2396 809 -3204 783 -3198 1605 -2398 1607 -2402 1605 -2398 1607 -2400 1599 -2398 1605 -2392 1603 -2400 811 -3186 1601 -2400 811 -3194 809 -3184 811 -3186 1603 -2396 1603 -2398 1607 -2398 811 -3188 1605 -2396 1605 -15174 829 -3166 1605 -2402 809 -3190 1615 -2368 815 -3194 815 -3192 1605 -2396 1603 -2400 1603 -2400 1605 -2392 1601 -2400 1607 -2400 1601 -2402 811 -3194 1599 -2392 809 -3188 813 -3184 813 -3192 1605 -2400 1605 -2400 1607 -2390 809 -3188 1607 -2398 1603 -15174 829 -3158 1617 -2400 787 -3192 1607 -2404 811 -3184 815 -3190 1607 -2402 1601 -2396 1605 -2400 1601 -2396 1603 -2396 1607 -2400 1603 -2394 809 -3188 1609 -2398 809 -3190 811 -3188 809 -3188 1607 -2396 1599 -2396 1607 -2400 809 -3188 1607 -2398 1603 -15172 797 -3228 1575 -2424 771 -3202 1615 -2386 801 -3198 807 -3218 1587 -2390 1603 -2394 1603 -2424 1603 -2396 1603 -2398 1599 -2394 1605 -2396 809 -3192 1605 -2400 807 -3184 809 -3190 813 -3190 1603 -2390 1601 -2394 1601 -2396 813 -3196 1607 -2402 1599 -15152 829 -3194 1605 -2408 793 -3182 1597 -2426 807 -3184 811 -3192 1607 -2392 1603 -2396 1605 -2400 1601 -2394 1603 -2394 1605 -2400 1605 -2394 809 -3190 1605 -2396 811 -3192 809 -3186 811 -3190 1601 -2394 1605 -2400 1607 -2398 809 -3186 1601 -2394 1603 -15174 829 -3160 1635 -2382 811 -3194 1609 -2400 779 -3220 781 -3222 1575 -2406 1603 -2398 1607 -2402 1601 -2394 1603 -2396 1601 -2402 1605 -2398 805 -3190 1605 -2394 813 -3194 809 -3188 811 -3192 1603 -2396 1599 -2394 1607 -2394 811 -3194 1603 -2394 1601 -15174 799 -3216 1599 -2402 785 -3198 1615 -2376 819 -3194 811 -3190 1599 -2394 1623 -2392 1599 -2396 1611 -2382 1599 -2390 1633 -2392 1599 -2396 811 -3194 1599 -2394 811 -3188\nRAW_Data: 811 -3190 809 -3192 1603 -2398 1607 -2392 1599 -2398 811 -3190 1603 -2402 1601 -15172 795 -3194 1603 -2404 807 -3186 1599 -2394 821 -3196 785 -3196 1609 -2396 1609 -2398 1603 -2396 1605 -2400 1599 -2394 1605 -2396 1605 -2398 809 -3190 1601 -2396 811 -3190 809 -3194 809 -3188 1601 -2398 1603 -2398 1607 -2392 807 -3190 1603 -2402 1601 -15178 803 -3198 1579 -2410 807 -3188 1625 -2406 793 -3204 779 -3192 1609 -2408 1597 -2392 1595 -2424 1605 -2394 1601 -2394 1601 -2392 1601 -2396 809 -3194 1607 -2398 811 -3194 811 -3186 809 -3182 1607 -2398 1605 -2392 1605 -2392 809 -3190 1611 -2400 1605 -15154 799 -3228 1567 -2434 795 -3180 1627 -2392 809 -3190 809 -3186 1603 -2396 1599 -2394 1603 -2398 1603 -2398 1603 -2392 1607 -2400 1601 -2390 809 -3186 1601 -2404 811 -3188 811 -3190 813 -3186 1605 -2398 1605 -2392 1599 -2400 813 -3190 1599 -2396 1603 -15184 799 -3194 1607 -2410 795 -3176 1603 -2428 807 -3188 811 -3188 1603 -2394 1607 -2398 1599 -2396 1603 -2400 1605 -2394 1601 -2394 1607 -2400 809 -3186 1607 -2400 809 -3184 811 -3188 809 -3196 1605 -2396 1603 -2394 1607 -2392 809 -3188 1607 -2396 1609 -15176 795 -3226 1567 -2406 819 -3198 1579 -2406 809 -3190 821 -3198 1575 -2400 1603 -2398 1611 -2400 1603 -2398 1607 -2396 1601 -2394 1607 -2402 809 -3190 1603 -2396 811 -3186 809 -3192 811 -3192 1607 -2394 1603 -2398 1601 -2396 809 -3196 1605 -2398 1603 -15148 827 -3196 1597 -2426 775 -3200 1613 -2398 783 -3198 813 -3184 1613 -2400 1607 -2400 1605 -2398 1603 -2400 1601 -2394 1603 -2398 1605 -2398 809 -3192 1603 -2396 811 -3190 811 -3190 809 -3194 1605 -2394 1601 -2396 1601 -2400 811 -3192 1607 -2396 1603 -15162 797 -3244 1573 -2398 817 -3196 1603 -2404 795 -3210 775 -3220 1603 -2396 1603 -2396 1607 -2394 1607 -2368 1633 -2392 1601 -2396 1607 -2396 811 -3190 1605 -2400 807 -3186 811 -3194 809 -3186 1607 -2396 1603 -2392 1599 -2394 811 -3186 1611 -2396 1601 -15178 763 -3288 1501 -2496 741 -3254 1543 -2442 777 -3194 815 -3206 1585 -2390 1603 -2426 1601 -2392 1599 -2400 1605 -2396 1599 -2398 1603 -2398 809 -3190 1601 -2396 811 -3188 811 -3188 811 -3194 1607 -2396 1599 -2396 1605 -2398 813 -3190 1605 -2400 1603 -16206 65 -230 181 -579692 165 -1118 1797 -3196 1591 -2420 773 -3224 1603 -2394 809 -3188 809 -3188 1609 -2402 1603 -2398 1599 -2400 1605 -2400 1605 -2396 1605 -2396 1603 -2394 813 -3194 1607 -2398 809 -3188 809 -3192 809 -3196 1603 -2394 1599 -2396 1605 -2402 811 -3190 1605 -2400 1605 -15178 793 -3222 1577 -2402 809 -3192 1601 -2394 807 -3202 819 -3196 1609 -2396 1603 -2396 1601 -2392 1613 -2400 1579 -2398 1613 -2396 1603 -2396 813 -3192 1607 -2394 813 -3198 809 -3188 809 -3188 1599 -2398 1607 -2400 1607 -2398 807 -3186 1603 -2400 1607 -15178 823 -3168 1615 -2398 809 -3192 1615 -2398 783 -3228\nRAW_Data: 779 -3200 1607 -2394 1605 -2402 1607 -2394 1601 -2396 1603 -2400 1607 -2398 1603 -2396 809 -3182 1607 -2402 813 -3194 811 -3188 811 -3186 1603 -2402 1605 -2398 1607 -2396 809 -3188 1603 -2398 1609 -15162 829 -3204 1575 -2428 783 -3202 1597 -2422 775 -3214 809 -3192 1605 -2400 1603 -2402 1601 -2392 1603 -2394 1605 -2392 1611 -2398 1603 -2398 811 -3182 1609 -2400 813 -3196 811 -3186 811 -3186 1609 -2400 1605 -2394 1601 -2402 807 -3190 1607 -2398 1607 -15178 829 -3182 1595 -2424 773 -3224 1589 -2384 805 -3222 809 -3188 1603 -2398 1605 -2400 1599 -2396 1601 -2392 1601 -2400 1609 -2396 1605 -2398 813 -3190 1603 -2396 813 -3186 811 -3194 811 -3196 1603 -2394 1603 -2400 1603 -2396 809 -3186 1609 -2400 1605 -15160 825 -3198 1605 -2402 785 -3208 1599 -2426 775 -3220 807 -3186 1601 -2396 1609 -2398 1607 -2392 1599 -2402 1605 -2398 1599 -2398 1603 -2402 807 -3190 1603 -2398 813 -3186 813 -3196 809 -3184 1601 -2394 1603 -2428 1571 -2400 815 -3220 1575 -2402 1609 -15178 793 -3234 1575 -2404 819 -3188 1599 -2398 807 -3204 819 -3192 1581 -2400 1607 -2396 1607 -2402 1601 -2392 1605 -2398 1605 -2400 1605 -2398 811 -3190 1609 -2396 811 -3186 811 -3194 811 -3186 1603 -2396 1609 -2396 1601 -2400 811 -3192 1603 -2394 1603 -15188 831 -3184 1609 -2382 803 -3190 1629 -2396 809 -3192 809 -3188 1607 -2396 1607 -2398 1607 -2392 1599 -2396 1607 -2398 1605 -2398 1605 -2394 811 -3188 1607 -2400 813 -3192 811 -3190 813 -3186 1603 -2400 1603 -2402 1599 -2394 811 -3190 1609 -2396 1609 -15164 823 -3196 1599 -2396 789 -3210 1595 -2424 809 -3188 811 -3186 1603 -2398 1605 -2390 1601 -2394 1609 -2400 1605 -2396 1607 -2394 1603 -2398 813 -3194 1599 -2394 815 -3194 809 -3192 809 -3190 1607 -2398 1605 -2396 1603 -2394 809 -3188 1605 -2402 1601 -15162 827 -3196 1579 -2426 805 -3206 1605 -2366 811 -3208 799 -3212 1603 -2400 1603 -2394 1601 -2398 1605 -2398 1599 -2394 1601 -2394 1609 -2394 813 -3192 1607 -2398 813 -3186 807 -3188 813 -3188 1601 -2396 1609 -2396 1603 -2392 811 -3194 1601 -2398 1611 -15174 831 -3186 1595 -2406 785 -3200 1625 -2378 813 -3190 815 -3202 1593 -2424 1603 -2400 1603 -2394 1597 -2400 1603 -2400 1605 -2396 1597 -2400 811 -3192 1601 -2400 811 -3190 811 -3190 811 -3192 1607 -2398 1603 -2392 1607 -2394 811 -3194 1603 -2396 1601 -15154 831 -3190 1605 -2404 797 -3180 1635 -2392 807 -3190 809 -3188 1605 -2400 1603 -2394 1605 -2396 1605 -2398 1601 -2394 1605 -2400 1605 -2396 811 -3192 1603 -2394 809 -3186 815 -3192 805 -3192 1601 -2396 1601 -2366 1629 -2388 807 -3192 1603 -2396 1599 -15118 825 -3198 1605 -2400 783 -3198 1589 -2392 809 -3180 809 -3186 1629 -2400 1573 -2390 1631 -2362 1633 -2360 1631 -2366 1599 -2394 1603 -2392 805 -3214 1601 -2396 809 -3184 811 -3190 807 -3160 1631 -2394 1601 -2364 1601 -2420\nRAW_Data: 807 -3192 1603 -2360 1633 -15120 827 -3200 1575 -2400 809 -3196 1605 -2394 807 -3186 809 -3160 1633 -2392 1599 -2366 1599 -2394 1631 -2358 1635 -2366 1601 -2396 1629 -2390 777 -3190 1629 -2400 775 -3218 775 -3216 809 -3188 1603 -2362 1631 -2390 1601 -2396 811 -3190 1597 -2400 1601 -15128 809 -3220 1577 -2398 809 -3196 1603 -2364 811 -3194 811 -3192 1605 -2400 1605 -2360 1601 -2396 1601 -2392 1603 -2390 1631 -2394 1571 -2394 807 -3218 1603 -2396 775 -3214 807 -3186 807 -3162 1603 -2394 1601 -2390 1603 -2396 805 -3222 1603 -2364 1635 -15142 791 -3228 1571 -2400 811 -3188 1601 -2394 809 -3194 809 -3186 1605 -2398 1601 -2390 1603 -2362 1633 -2362 1601 -2394 1635 -2362 1631 -2390 807 -3190 1605 -2394 773 -3220 775 -3186 807 -3192 1599 -2424 1597 -2366 1603 -2396 807 -3216 1601 -2398 1601 -15140 813 -3196 1571 -2398 813 -3190 1601 -2396 811 -3186 811 -3192 1603 -2402 1601 -2392 1601 -2392 1609 -2394 1603 -2394 1607 -2394 1611 -2396 811 -3190 1605 -2400 811 -3190 811 -3190 813 -3190 1601 -2400 1603 -2394 1607 -2398 809 -3188 1607 -2398 1605 -15176 793 -3226 1579 -2400 811 -3186 1613 -2400 785 -3194 815 -3194 1611 -2396 1607 -2396 1601 -2400 1607 -2396 1605 -2398 1603 -2394 1599 -2392 811 -3190 1605 -2396 815 -3192 815 -3190 813 -3188 1601 -2398 1603 -2398 1603 -2398 811 -3194 1605 -2398 1601 -15156 859 -3148 1639 -2378 803 -3214 1569 -2430 807 -3186 809 -3194 1601 -2396 1605 -2398 1607 -2400 1605 -2396 1605 -2396 1607 -2392 1599 -2394 811 -3184 1609 -2400 815 -3194 809 -3188 809 -3196 1605 -2394 1607 -2394 1599 -2396 813 -3194 1603 -2398 1603 -15170 797 -3212 1595 -2402 819 -3184 1613 -2390 831 -3174 819 -3196 1597 -2402 1589 -2388 1601 -2396 1633 -2398 1601 -2392 1599 -2400 1607 -2396 811 -3186 1609 -2402 809 -3188 813 -3192 809 -3188 1607 -2396 1601 -2398 1601 -2398 811 -3192 1599 -2396 1607 -15174 831 -3186 1579 -2402 801 -3202 1615 -2388 827 -3178 803 -3188 1617 -2392 1601 -2392 1635 -2396 1599 -2392 1603 -2398 1605 -2400 1601 -2392 813 -3194 1601 -2400 813 -3190 811 -3190 809 -3188 1603 -2396 1601 -2398 1605 -2400 809 -3184 1601 -2396 1607 -15178 831 -3166 1611 -2398 815 -3198 1577 -2398 811 -3194 815 -3192 1607 -2400 1605 -2400 1599 -2398 1603 -2396 1601 -2398 1605 -2400 1609 -2392 811 -3192 1605 -2398 811 -3188 811 -3192 813 -3190 1607 -2394 1605 -2398 1603 -2400 809 -3190 1601 -2396 1609 -15174 795 -3230 1567 -2432 781 -3204 1597 -2394 807 -3222 807 -3182 1605 -2398 1603 -2392 1603 -2396 1607 -2396 1603 -2404 1605 -2400 1599 -2396 811 -3186 1611 -2398 813 -3188 811 -3192 811 -3188 1605 -2404 1603 -2400 1605 -2398 807 -3190 1605 -2396 1605 -15164 827 -3198 1605 -2398 783 -3206 1595 -2424 807 -3192 807 -3184 1605 -2396 1605 -2400 1607 -2398 1599 -2394 1607 -2396 1605 -2400 1599 -2394\nRAW_Data: 809 -3184 1609 -2400 809 -3194 813 -3188 807 -3188 1603 -2400 1605 -2400 1599 -2398 809 -3190 1605 -2400 1603 -15170 803 -3208 1595 -2402 809 -3196 1589 -2388 807 -3216 809 -3182 1603 -2394 1607 -2398 1603 -2396 1607 -2396 1603 -2400 1605 -2396 1599 -2394 811 -3188 1605 -2396 817 -3196 811 -3182 811 -3192 1607 -2394 1603 -2394 1607 -2398 811 -3190 1603 -2398 1601 -15186 833 -3156 1605 -2412 799 -3218 1595 -2392 809 -3186 811 -3186 1609 -2404 1607 -2394 1605 -2396 1601 -2400 1607 -2396 1601 -2392 1607 -2400 809 -3192 1603 -2396 815 -3194 809 -3188 809 -3192 1603 -2400 1599 -2394 1605 -2400 811 -3188 1599 -2400 1605 -16142 167 -202\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/marantec_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132\nRAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330\nRAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66\nRAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98\nRAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100\nRAW_Data: 529 -166 2423 -100 199 -66 22473 -20988 131 -100 395 -166 165 -268 133 -100 17947 -68 8295 -132 267 -66 295 -100 627 -98 199 -200 597 -168 363 -266 365 -68 597 -66 431 -166 233 -98 465 -100 533 -132 299 -66 533 -66 361 -98 229 -98 459 -132 129 -232 63 -66 427 -132 395 -98 361 -98 359 -100 131 -134 1721 -66 431 -132 67 -398 395 -100 361 -66 1693 -230 1381 -98 1879 -100 693 -66 299 -100 233 -132 133 -134 2807 -132 5113 -66 1317 -68 5575 -98 199 -168 235 -432 463 -100 1679 -66 6115 -134 10653 -19120 97 -426 99 -162 99 -64 263 -166 227 -132 727 -100 99 -98 1163 -66 497 -132 197 -66 293 -98 789 -66 1051 -66 395 -64 293 -132 983 -66 1225 -66 65 -168 99 -100 299 -164 491 -166 365 -132 265 -100 397 -132 99 -100 265 -234 299 -100 233 -100 433 -232 363 -66 197 -130 789 -164 525 -132 331 -132 199 -66 195 -66 459 -98 163 -66 197 -164 229 -132 261 -130 65 -98 361 -64 625 -66 591 -66 863 -198 465 -132 631 -66 699 -100 131 -100 197 -166 535 -66 131 -100 297 -132 499 -66 599 -266 295 -132 359 -66 197 -196 263 -132 99 -98 261 -164 163 -66 493 -132 433 -134 495 -98 197 -132 391 -230 393 -66 263 -66 425 -66 261 -66 427 -200 397 -198 97 -132 99 -132 959 -100 13369 -66 1227 -100 295 -164 263 -232 261 -196 393 -230 227 -132 297 -100 97 -100 195 -262 893 -66 725 -66 331 -100 267 -134 1091 -132 133 -166 265 -100 461 -200 729 -100 431 -98 1093 -66 197 -168 265 -100 663 -98 363 -132 97 -132 523 -328 693 -98 397 -396 229 -164 491 -66 131 -132 591 -66 197 -66 823 -66 331 -66 459 -394 97 -100 529 -98 199 -100 563 -132 461 -166 231 -596 163 -132 687 -164 165 -164 165 -132 265 -68 563 -66 131 -66 233 -166 657 -166 395 -100 1147 -66 229 -132 199 -236 65 -66 263 -66 233 -66 235 -66 199 -266 561 -166 133 -166 199 -132 731 -100 363 -198 227 -132 261 -166 657 -98 459 -98 393 -100 229 -100 229 -130 297 -98 233 -98 463 -200 401 -64 99 -132 199 -132 131 -68 629 -66 265 -66 163 -100 529 -66 297 -166 299 -132 233 -100 199 -134 763 -66 297 -100 865 -236 99 -66 265 -432 265 -198 295 -68 463 -198 365 -166 131 -68 197 -168 731 -68 1293 -168 365 -132 725 -66 199 -64 627 -66 299 -134 231 -264 2051 -98 131 -66 425 -66 391 -100 163 -132 97 -132 523 -164 129 -162 427 -66 231 -100 335 -166 1423 -600 789 -164 165 -66 329 -262 691 -132\nRAW_Data: 197 -162 4471 -64 9599 -60872 143 -10288 613 -890 131 -166 97 -4144 867 -1130 1871 -1104 897 -1048 941 -1076 933 -1048 941 -1046 939 -1048 943 -1012 973 -1048 945 -2002 987 -1008 985 -1014 1983 -984 1005 -998 975 -2008 1999 -978 1001 -978 1011 -976 997 -1984 1009 -990 1997 -1966 1005 -1000 977 -1012 999 -984 983 -1006 999 -988 1989 -982 1007 -1000 973 -1002 1019 -1966 1015 -986 1989 -1980 1991 -984 1009 -1990 985 -982 1021 -972 999 -996 1011 -980 1003 -8954 1991 -992 1007 -1972 1009 -978 2003 -982 985 -978 1019 -986 1019 -970 1015 -958 1025 -974 1011 -990 997 -978 1013 -1980 1007 -978 1019 -976 1995 -972 1007 -976 1009 -1994 2009 -952 1007 -1010 1011 -952 1005 -2000 1003 -974 2003 -1982 1007 -976 999 -978 1009 -978 1023 -986 1017 -972 2011 -972 1009 -978 1001 -974 1011 -1964 1015 -982 2023 -1950 1999 -988 1025 -1964 1021 -958 1005 -982 1029 -984 981 -1004 1001 -8932 1999 -986 1015 -1982 1001 -986 1991 -984 1007 -994 973 -1016 971 -1006 1011 -980 985 -1004 979 -1014 983 -1004 977 -2006 1005 -984 973 -1018 1969 -1016 969 -1006 1013 -1972 1999 -974 999 -1006 997 -974 1003 -2010 977 -1002 1991 -1970 1011 -1002 971 -1014 973 -1016 973 -1014 975 -1016 1995 -980 999 -976 1013 -978 995 -2006 975 -1018 1969 -2008 1967 -1014 969 -1990 1001 -1018 973 -1014 975 -986 1001 -1008 969 -8960 8009 -3874 1797 -165864 327 -132 955 -130 557 -100 229 -98 263 -66 129 -66 461 -198 265 -134 559 -100 165 -266 263 -166 529 -132 327 -130 65 -196 395 -100 165 -98 327 -66 395 -130 521 -66 693 -66 331 -66 365 -132 499 -66 331 -66 231 -164 659 -66 493 -198 263 -98 131 -100 695 -100 133 -98 431 -100 333 -234 265 -166 165 -68 729 -66 99 -66 459 -98 197 -98 327 -100 295 -166 231 -132 299 -66 331 -66 199 -132 197 -100 663 -98 461 -68 329 -196 295 -98 393 -100 459 -100 1701 -98 13057 -100 395 -66 397 -100 663 -100 563 -100 131 -66 331 -100 297 -100 593 -164 701 -66 297 -66 365 -66 331 -98 459 -130 361 -132 495 -264 331 -66 329 -64 165 -66 329 -266 331 -164 301 -266 97 -68 529 -66 233 -132 431 -132 231 -68 267 -66 199 -66 599 -134 65 -100 197 -66 1317 -98 165 -132 523 -130 131 -100 295 -460 65 -164 623 -164 293 -132 331 -166 1051 -130 227 -230 329 -134 265 -66 827 -98 525 -132 97 -66 497 -66 1097 -100 595 -66 99 -166 495 -66 525 -132 327 -100 329 -66 331 -68 263 -436 65 -100 929 -168 465 -266 731 -100 261 -66 525 -66 65 -66 295 -132 591 -98 623 -100 231 -166 231 -396 97 -66 565 -66 499 -100\nRAW_Data: 231 -66 363 -66 2355 -16412 431 -66 1061 -926 99 -594 229 -528 97 -68 501 -100 197 -100 67 -166 22923 -64 3651 -66 25605 -100 23205 -68 10819 -98 327 -164 195 -132 425 -166 231 -134 597 -200 131 -68 431 -66 331 -132 329 -66 301 -100 297 -100 233 -132 529 -66 561 -232 395 -98 295 -98 521 -66 329 -166 467 -200 231 -364 99 -100 299 -198 395 -98 625 -68 131 -98 229 -98 295 -98 329 -98 557 -98 131 -66 491 -134 65 -100 233 -200 365 -232 459 -198 759 -98 165 -100 297 -134 431 -66 397 -134 195 -166 597 -66 1755 -66 955 -132 361 -132 293 -98 65 -66 197 -298 231 -100 559 -98 427 -132 655 -66 6771 -66 8835 -66 567 -68 625 -66 663 -100 493 -130 725 -66 63 -100 263 -134 299 -364 429 -100 395 -100 163 -100 297 -100 199 -66 531 -98 497 -132 595 -132 363 -66 261 -198 395 -130 425 -198 131 -166 429 -264 397 -166 727 -66 725 -132 361 -164 721 -132 195 -134 1151 -132 397 -98 21075 -134 267 -200 1097 -98 297 -100 725 -132 265 -200 597 -66 333 -198 97 -598 197 -66 233 -134 297 -234 659 -64 165 -66 227 -98 197 -100 327 -130 559 -98 991 -100 263 -130 229 -132 359 -100 325 -132 263 -100 399 -98 963 -132 431 -66 761 -68 723 -66 395 -130 493 -100 165 -66 297 -334 97 -132 787 -100 491 -232 949 -100 431 -232 99 -132 233 -164 299 -100 329 -64 233 -100 333 -66 365 -66 661 -200 393 -134 263 -102 297 -132 467 -66 493 -100 631 -66 265 -68 331 -100 295 -162 163 -164 395 -66 697 -66 663 -134 463 -168 65 -66 963 -66 265 -132 199 -134 723 -100 821 -66 163 -130 457 -362 97 -66 461 -98 563 -66 725 -132 727 -100 661 -66 1231 -100 231 -366 2455 -98 689 -132 635 -132 99 -166 729 -66 233 -100 465 -132 561 -168 697 -200 295 -298 65 -198 131 -66 329 -66 231 -134 465 -166 265 -132 299 -200 825 -66 361 -66 129 -198 667 -98 1021 -66 697 -100 397 -68 265 -100 429 -234 99 -98 953 -98 985 -166 293 -66 263 -66 131 -132 295 -66 723 -66 497 -66 265 -166 565 -68 465 -132 65 -66 531 -132 597 -166 331 -66 299 -66 65 -100 1461 -100 463 -66 293 -328 133 -132 499 -66 263 -100 167 -198 267 -66 993 -100 327 -98 493 -164 97 -98 263 -100 427 -66 229 -64 261 -98 325 -100 361 -66 393 -132 595 -164 363 -164 163 -66 131 -134 231 -66 731 -66 1025 -100 563 -134 131 -66 229 -134 233 -100 693 -100 299 -166 299 -134 693 -66 359 -166 499 -100\nRAW_Data: 231 -100 23109 -60820 133 -10320 631 -5396 883 -1106 1887 -1072 943 -1040 957 -1012 987 -1010 957 -1012 983 -1002 999 -1012 977 -984 1003 -2002 999 -978 983 -1010 1977 -1002 1007 -968 999 -1996 1999 -986 995 -1012 977 -984 1001 -2000 999 -982 1979 -2010 983 -1010 989 -978 991 -1012 985 -982 1021 -976 1979 -1006 1013 -982 997 -976 1019 -1968 1005 -1000 1965 -2014 1967 -1016 993 -1970 1005 -998 973 -1014 999 -974 1011 -976 995 -8958 2009 -984 1007 -1968 997 -1008 1965 -994 1009 -984 1007 -986 1011 -962 1009 -984 1009 -1002 1007 -978 1003 -976 1009 -1970 1011 -982 1009 -988 2001 -972 997 -1000 1005 -1968 1985 -1006 1007 -978 999 -976 1009 -1996 983 -986 1989 -2014 973 -1014 1003 -974 1009 -974 997 -986 1023 -970 1987 -1014 981 -1010 989 -984 985 -2002 1009 -960 1995 -2002 1995 -986 997 -1970 1003 -1004 1007 -978 999 -980 1013 -978 1017 -8916 2003 -1016 973 -2002 1001 -976 2013 -988 975 -1020 975 -986 1005 -1000 977 -1014 1001 -974 1007 -1000 967 -1006 997 -2002 971 -1006 1009 -980 1993 -980 1003 -986 1005 -1992 1977 -1002 975 -1014 1001 -974 1009 -1994 975 -988 1993 -2010 973 -1014 997 -980 987 -1008 983 -1018 995 -978 1975 -1006 1009 -978 999 -1008 991 -1966 1005 -998 1993 -1980 2005 -988 995 -1968 1005 -1000 1009 -978 1001 -976 1009 -980 1015 -8920 8017 -3892 1773 -165206 65 -396 99 -332 65 -100 133 -364 329 -100 199 -102 791 -100 261 -264 131 -164 295 -130 261 -100 197 -130 625 -98 299 -166 199 -100 531 -66 823 -100 327 -98 395 -98 825 -200 889 -100 295 -328 131 -98 295 -166 329 -64 393 -100 523 -66 263 -130 427 -98 463 -100 363 -166 67 -66 231 -134 331 -98 629 -132 265 -130 99 -66 953 -98 919 -296 591 -132 129 -132 591 -330 361 -166 763 -98 167 -68 265 -132 265 -130 133 -100 333 -132 295 -132 459 -132 393 -66 555 -132 263 -164 97 -64 229 -100 131 -100 329 -66 425 -64 1717 -132 7375 -66 5079 -230 199 -132 467 -66 925 -100 197 -100 231 -66 829 -166 231 -166 333 -200 359 -100 393 -164 291 -134 461 -132 325 -166 99 -100 329 -100 195 -166 399 -232 165 -98 729 -66 531 -132 99 -98 661 -332 65 -100 761 -66 427 -162 629 -132 559 -66 327 -166 597 -100 163 -66 263 -100 553 -100 789 -230 591 -198 65 -362 263 -66 327 -98 425 -66 261 -166 753 -196 889 -134 429 -134 333 -134 429 -66 231 -100 131 -100 463 -130 131 -66 753 -164 361 -64 131 -260 263 -66 359 -98 295 -100 295 -166 1163 -66 267 -68 329 -134 99 -198 233 -134 231 -132 557 -196 65 -100 263 -98 391 -98 329 -98 129 -66\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/mastercode.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok270Async\nProtocol: Mastercode\nBit: 36\nKey: 00 00 00 0B 7E 00 3C 08\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/mastercode_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok270Async\nProtocol: RAW\nRAW_Data: 10389 -66 405095 -102 207 -106 1165 -130 963739 -1232 899 -2250 2003 -1190 2017 -1202 911 -2256 2021 -1162 2045 -1134 2047 -1164 2047 -1138 2031 -1180 2039 -1182 949 -2190 995 -2214 961 -2228 963 -2198 963 -2214 977 -2212 975 -2210 975 -2208 971 -2200 963 -2210 993 -2184 2075 -1130 2051 -1142 2055 -1136 2047 -1178 965 -2236 933 -2220 975 -2184 999 -2222 967 -2208 969 -2214 979 -2202 2027 -1156 975 -2242 943 -16080 2023 -1162 967 -2220 2057 -1114 2061 -1124 1007 -2242 2025 -1134 2055 -1168 2017 -1138 2075 -1134 2053 -1136 2075 -1130 979 -2214 979 -2174 999 -2182 1001 -2204 977 -2206 1003 -2188 979 -2176 999 -2182 1009 -2176 1009 -2176 1001 -2212 2029 -1116 2091 -1102 2109 -1092 2095 -1126 1001 -2150 1011 -2180 1011 -2180 1009 -2178 1009 -2172 1009 -2166 1001 -2198 2065 -1136 975 -2220 971 -16018 2097 -1166 951 -2240 2009 -1186 2011 -1160 979 -2208 2035 -1134 2053 -1138 2061 -1158 2045 -1152 2029 -1152 2051 -1166 963 -2188 993 -2222 951 -2214 963 -2220 965 -2212 979 -2212 977 -2180 1003 -2202 965 -2218 975 -2216 967 -2188 2061 -1124 2083 -1126 2071 -1130 2059 -1134 993 -2188 979 -2240 947 -2204 979 -2214 971 -2214 973 -2210 971 -2206 2053 -1130 979 -2216 969 -16056 2053 -1134 1001 -2224 2021 -1150 2051 -1154 953 -2240 2045 -1146 2023 -1168 2033 -1144 2065 -1146 2055 -1130 2071 -1160 961 -2192 973 -2190 1005 -2214 975 -2206 967 -2206 975 -2206 967 -2208 975 -2212 967 -2212 979 -2218 977 -2178 2063 -1156 2035 -1160 2061 -1126 2065 -1130 981 -2186 1003 -2210 977 -2208 973 -2202 977 -2200 965 -2248 943 -2206 2039 -1190 941 -48536 65 -7254 263 -68 363 -102 131 -232 263 -264 751 -230 225 -822 397 -634 231 -268 263 -134 267 -64 867 -132 305 -138 67 -100 331 -98 891 -66 455 -66 531 -100 299 -134 897 -98 693 -132 291 -132 333 -98 337 -68 331\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/megacode.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: MegaCode\nBit: 24\nKey: 00 00 00 00 00 8A E2 D2\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/megacode_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017\nRAW_Data: -1968 997 -4952 1011 -7896 1013 -1954 1005 -13860 985 -2000 979 -4952 1005 -4946 993 -7930 979 -1990 1009 -7908 1017 -1968 1011 -7900 985 -4946 1027 -4920 1009 -1964 1015 -4942 1009 -263646 67 -9708 99 -68890 97 -48712 65 -62028 65 -4652 65 -108870 99 -71758 99 -3200 97 -47548 65 -7036 65 -104448 97 -38184 99 -6502 97 -17756 65 -10136 20983 -1968 999 -4950 1005 -4940 1001 -7898 1025 -1966 1011 -7876 1017 -1966 1023 -7918 981 -4936 1027 -4946 975 -2006 979 -4946 1017 -4954 977 -7930 983 -1980 1003 -7920 989 -4952 1007 -1970 1013 -7896 1007 -1970 1015 -4954 987 -7900 1007 -1970 1007 -13860 993 -1972 1007 -4956 1013 -4926 1005 -7908 995 -1972 1029 -7914 987 -1986 1009 -7906 981 -4956 1009 -4930 1005 -1966 1039 -4938 1007 -4916 1009 -7918 1019 -1968 989 -7892 1017 -4934 1027 -1956 1039 -7898 985 -1974 1007 -4960 981 -7930 985 -1996 989 -13824 1045 -1946 1025 -4928 1009 -4942 997 -7926 1011 -1968 1003 -7894 1023 -1962 993 -7902 1017 -4948 999 -4948 1007 -1968 1015 -4918 1045 -4926 999 -7888 1039 -1972 1013 -7888 1005 -4948 1001 -1970 1011 -7886 1013 -1974 1023 -4942 1005 -7890 1033 -1952 1021 -13806 1047 -1964 1001 -4936 1009 -4950 1001 -7882 1031 -1962 1007 -7906 1021 -1966 1011 -7908 985 -4946 1027 -4918 1011 -1968 1017 -4948 1009 -4932 1015 -7882 1009 -1964 1013 -7900 1017 -4950 1001 -1970 1013 -7916 1013 -1944 1031 -4916 1029 -7888 1007 -1976 1005 -13848 1013 -1968 995 -4952 1003 -4946 991 -7920 1019 -1968 983 -7904 1015 -1966 1019 -7894 1011 -4942 1027 -4924 1011 -1966 1015 -4944 1005 -4926 1007 -7914 1007 -1972 1013 -7884 1013 -4940 1027 -1968 1007 -7906 1001 -1980 999 -4944 1013 -7880 1009 -2000 1003 -13830 997 -1974 1009 -4930 1029 -4938 1013 -7880 1011 -1998 1001 -7908 997 -1966 1025 -7902 1013 -4938 983 -4962 979 -1998 1011 -4932 1005 -4924 1011 -7922 1009 -1970 1015 -7890 1011 -4946 1003 -1970 1013 -7892 1013 -1964 1027 -4928 1021 -7886 1007 -1980 1001 -13846 1013 -1968 999 -4948 1009 -4938 993 -7926 1011 -1964 991 -7926 1011 -1962 993 -7898 1041 -4906 1009 -4960 999 -1964 1023 -4932 1007 -4928 1013 -7898 1015 -1968 1013 -7894 1017 -4948 997 -1968 1031 -7892 1027 -1970 1005 -4914 1039 -7884 1023 -1964 1011 -13838 1013 -1948 1023 -4924 1015 -4942 1025 -7872 1029 -1964 1007 -7898 1025 -1960 1025 -7898 1021 -4912 1023 -4920 1011 -1970 1013 -4954 1011 -4926 1003 -7910 1011 -1968 1013 -7890 1009 -4938 1023 -1982 1007 -7902 1013 -1940 1023 -4926 1011 -7922 1007 -1980 997 -13816 1045 -1964 1001 -4948 1005 -4938 993 -7904 1005 -1966 1035 -7888 1027 -1964 1011 -7906 1015 -4916 1027 -4920 1011 -1968 1019 -4944 1009 -4928 1001 -7924 1009 -1972 979 -7914 1013 -4948 1001 -1970 1013 -7892 1015 -1966 1027 -4928 1021 -7872 1035 -1968 1011 -13832 1011 -1980 983 -4956 987 -4948 1025 -7892 1005 -1966 1007 -7914 1021 -1962 1007\nRAW_Data: -7912 985 -4942 1023 -4926 1007 -1992 981 -4944 1035 -4920 1007 -7904 1025 -1962 991 -7934 981 -4948 995 -2004 1007 -7880 1007 -1992 983 -4946 1027 -7890 1007 -1982 999 -13830 1011 -1968 1025 -4926 1013 -4942 997 -7898 1027 -1960 1023 -7910 983 -2000 979 -7906 1015 -4940 1025 -4920 1011 -1968 1013 -225644 65 -2082 65 -155560 133 -5172 65 -1102 131 -48576 99 -24714 67 -6858 65 -1314 67 -38246 65 -64888 65 -4564 67 -59374 99 -20160 99 -17606 65 -42096 97 -11950 131 -29302 65 -19034 99 -32020 97 -366 65 -4430 131 -14620 99 -17318 65 -5556\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nero_radio_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1487 -32700 235 -202 229 -218 217 -176 197 -220 217 -210 191 -218 217 -210 189 -218 217 -210 187 -212 181 -230 203 -194 211 -212 213 -226 197 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 239 -172 235 -218 181 -212 193 -218 219 -208 191 -212 211 -200 205 -224 181 -212 213 -228 197 -188 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 237 -174 235 -218 789 -244 205 -384 415 -230 185 -418 419 -226 179 -436 419 -190 435 -198 415 -210 215 -406 187 -436 421 -190 213 -438 193 -404 413 -224 185 -430 411 -238 181 -432 201 -428 199 -404 207 -420 205 -426 385 -244 213 -400 411 -192 221 -400 239 -412 205 -426 205 -394 203 -412 243 -390 203 -426 417 -214 215 -402 211 -428 203 -392 205 -406 231 -396 411 -232 203 -408 207 -424 393 -232 203 -408 209 -422 395 -232 397 -244 391 -202 219 -430 389 -238 405 -208 425 -206 217 -394 389 -238 207 -388 243 -382 411 -244 383 -1234 219 -406 225 -198 255 -182 175 -204 211 -212 201 -208 227 -182 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -240 171 -230 181 -212 213 -226 199 -188 211 -210 213 -228 195 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -188 209 -212 239 -172 227 -182 211 -212 227 -198 189 -212 211 -214 225 -198 187 -212 241 -182 227 -198 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 219 -182 209 -194 833 -210 213 -410 409 -232 199 -406 415 -204 215 -428 387 -244 391 -204 425 -206 215 -428 205 -382 419 -226 181 -434 195 -440 413 -192 219 -400 447 -202 217 -396 203 -418 189 -438 183 -436 195 -408 443 -194 217 -400 445 -204 217 -396 201 -430 199 -404 209 -422 205 -426 205 -412 193 -420 417 -202 223 -402 239 -408 207 -392 237 -382 223 -412 409 -226 191 -430 209 -416 395 -232 197 -404 209 -422 395 -232 397 -242 393 -202 217 -432 389 -238 407 -206 393 -238 217 -396 387 -238 209 -386 243 -388 411 -240 407 -4594 195 -408 245 -214 173 -218 211 -182 231 -204 223 -180 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -214 227 -196 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 217 -182 211 -194 219 -218 207 -192 211 -212 199 -206 223 -182 211 -214 227 -198 187 -212 211 -240 171 -236 217 -182 211 -194 217 -220 207 -192 211 -212 201 -206 193 -212 241 -182 227 -200 187 -212 209 -240 173 -228\nRAW_Data: 181 -212 211 -228 197 -188 211 -212 213 -228 815 -200 205 -404 437 -200 217 -432 389 -238 171 -414 445 -192 401 -244 389 -204 215 -426 203 -428 383 -246 177 -430 211 -428 389 -236 171 -412 413 -224 187 -430 239 -406 205 -394 237 -394 203 -410 415 -230 185 -432 411 -200 217 -432 203 -394 203 -436 201 -398 221 -404 227 -412 185 -436 423 -206 217 -416 207 -392 201 -418 223 -410 217 -404 425 -206 215 -418 205 -394 415 -244 211 -396 213 -426 387 -202 437 -208 391 -238 181 -432 387 -240 411 -206 425 -206 217 -396 387 -238 209 -388 243 -382 411 -244 383 -4672 243 -394 239 -182 253 -176 195 -182 211 -230 201 -192 209 -212 213 -192 231 -188 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -228 197 -186 211 -212 211 -194 231 -186 211 -212 213 -192 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 229 -188 241 -182 211 -226 197 -188 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 861 -188 211 -412 415 -210 211 -390 449 -190 215 -400 449 -166 465 -170 409 -210 251 -362 245 -396 423 -202 203 -408 247 -392 425 -200 201 -406 453 -170 253 -392 205 -380 257 -382 217 -404 227 -410 405 -200 231 -402 419 -204 217 -430 205 -380 221 -414 217 -404 227 -408 215 -404 227 -374 435 -198 233 -398 245 -390 205 -426 205 -378 257 -382 439 -194 225 -396 245 -388 395 -230 199 -404 247 -390 425 -200 431 -174 429 -202 219 -398 419 -204 441 -208 425 -170 253 -396 387 -240 207 -382 247 -390 409 -204 439 -4538 203 -408 229 -196 223 -182 217 -210 191 -220 217 -208 191 -210 211 -204 205 -194 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -188 211 -212 211 -192 231 -186 211 -212 213 -192 231 -188 211 -212 211 -194 229 -186 211 -212 213 -192 231 -188 211 -212 211 -194 229 -188 211 -210 213 -194 229 -186 211 -212 213 -192 231 -186 243 -180 211 -228 197 -186 211 -212 213 -192 231 -188 211 -212 211 -194 229 -186 211 -212 833 -210 215 -408 407 -234 199 -404 417 -206 215 -430 415 -216 393 -204 427 -206 217 -394 241 -392 429 -186 213 -412 237 -394 431 -174 251 -376 437 -192 225 -396 245 -390 205 -394 237 -396 235 -378 447 -192 213 -400 447 -166 255 -396 201 -430 199 -400 245 -390 205 -426 205 -380 257 -382 439 -194 223 -398 211 -422 205 -394 237 -396 237 -376 453 -166\nRAW_Data: 215 -432 203 -402 439 -204 203 -404 247 -390 425 -202 429 -176 427 -202 219 -396 421 -204 441 -208 427 -168 253 -396 387 -240 207 -382 245 -392 409 -204 439 -4532 225 -398 247 -178 241 -154 249 -182 217 -216 217 -176 239 -182 217 -212 191 -218 219 -176 221 -212 211 -202 205 -196 211 -212 211 -192 231 -186 211 -212 213 -192 231 -186 211 -212 211 -228 197 -186 211 -212 211 -228 197 -186 211 -212 211 -228 197 -186 211 -212 211 -194 231 -186 211 -212 213 -192 231 -188 211 -212 211 -192 231 -186 211 -212 213 -192 263 -156 211 -212 213 -192 231 -184 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 857 -202 217 -396 415 -212 213 -394 447 -190 215 -400 449 -166 447 -192 413 -212 215 -408 211 -428 387 -240 207 -382 247 -394 423 -202 203 -408 419 -204 251 -394 205 -380 255 -384 245 -394 203 -418 415 -206 217 -420 399 -194 229 -398 247 -390 205 -426 205 -380 255 -384 245 -394 203 -418 415 -206 217 -420 207 -394 237 -394 203 -406 247 -390 425 -202 201 -406 245 -392 425 -200 201 -406 245 -392 425 -200 431 -174 427 -204 217 -398 421 -202 441 -208 427 -168 255 -394 387 -240 207 -384 245 -392 407 -204 445 -4518 227 -398 241 -172 257 -152 211 -212 225 -200 217 -182 211 -212 227 -198 215 -182 211 -214 225 -198 217 -182 211 -212 225 -198 217 -180 211 -214 225 -198 217 -182 211 -212 227 -196 215 -182 211 -214 225 -198 217 -182 211 -212 227 -198 215 -182 211 -212 227 -196 217 -180 211 -214 225 -198 215 -182 211 -214 225 -198 217 -182 211 -212 225 -198 215 -182 211 -212 225 -198 217 -182 211 -212 227 -198 217 -182 211 -212 227 -196 217 -182 211 -212 855 -176 243 -380 453 -170 253 -392 417 -214 215 -366 445 -192 431 -216 431 -170 251 -392 205 -394 415 -212 249 -362 245 -396 423 -202 203 -408 453 -170 253 -392 205 -412 225 -384 217 -404 227 -410 421 -200 219 -400 445 -168 253 -396 203 -394 233 -398 247 -388 205 -426 205 -380 223 -416 419 -202 223 -400 241 -374 243 -394 237 -394 203 -406 419 -204 251 -394 205 -380 457 -204 217 -398 205 -426 415 -216 395 -204 427 -204 219 -394 417 -212 421 -206 425 -206 217 -396 417 -212 209 -388 247 -394 423 -202 435 -4428 207 -406 243 -180 211 -196 253 -182 209 -192 217 -218 209 -188 217 -220 207 -188 211 -180 231 -206 223 -182 211 -212 227 -198 215 -182 211 -212 227 -198 217 -180 211 -214 225 -198 215 -182 211 -214 225 -198 215 -182 211 -214 225 -198\nRAW_Data: 217 -180 211 -214 225 -198 215 -182 211 -214 225 -198 215 -182 211 -214 225 -198 217 -182 209 -214 225 -198 215 -182 241 -182 227 -200 185 -212 211 -206 241 -164 211 -212 213 -226 197 -188 211 -212 213 -226 819 -200 241 -370 435 -202 217 -398 421 -202 241 -378 453 -166 429 -210 423 -206 215 -422 207 -392 417 -210 213 -394 247 -394 423 -202 203 -408 453 -170 251 -394 205 -410 225 -386 245 -394 203 -418 411 -190 213 -434 417 -190 211 -406 225 -398 245 -392 205 -428 205 -410 227 -386 245 -394 417 -212 207 -386 245 -394 203 -428 205 -410 227 -386 439 -194 223 -398 209 -422 395 -232 199 -406 245 -390 423 -202 399 -210 427 -202 217 -398 421 -204 439 -208 427 -168 253 -396 387 -240 207 -382 245 -392 409 -240 407 -4630 235 -388 213 -192 249 -162 215 -218 217 -210 189 -220 217 -176 221 -218 217 -178 217 -182 241 -202 205 -192 211 -212 211 -194 231 -186 211 -212 213 -192 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -188 211 -212 829 -210 215 -410 407 -232 201 -404 419 -204 217 -430 385 -210 427 -204 427 -204 219 -394 239 -394 431 -176 251 -374 245 -394 417 -210 209 -388 445 -192 221 -380 223 -416 245 -394 203 -428 199 -400 419 -204 215 -430 383 -246 215 -364 245 -396 239 -392 207 -408 229 -388 217 -404 229 -410 419 -200 219 -400 239 -372 243 -392 239 -394 201 -406 451 -170 251 -392 205 -412 429 -202 217 -398 205 -426 417 -214 429 -168 427 -206 217 -396 417 -210 423 -206 425 -206 217 -396 417 -210 211 -390 245 -394 425 -202 435 -32700 389 -182 211 -212 211 -194 229 -186 211 -212 213 -226 197 -188 211 -212 211 -226 197 -188 211 -212 211 -228 197 -186 211 -212 211 -194 229 -186 211 -212 213 -226 197 -188 211 -212 211 -226 197 -188 211 -212 211 -226 197 -188 209 -212 213 -226 197 -186 211 -212 213 -226 197 -188 211 -212 211 -226 197 -188 211 -210 213 -226 197 -188 211 -212 211 -228 195 -188 211 -212 211 -226 197 -188 211 -210 213 -226 199 -186 211 -212 831 -218 213 -408 417 -210 211 -390 451 -198 183 -430 429 -190 435 -196 411 -212 217 -404 217 -404 425 -206 215 -418 207 -394 415 -210 211 -396 447 -192 221 -396 237 -380 243 -394 203 -428 205 -410 429 -202\nRAW_Data: 219 -398 423 -202 203 -408 245 -388 205 -426 205 -410 227 -388 243 -394 417 -210 209 -386 245 -392 203 -428 205 -410 227 -388 245 -392 417 -210 209 -388 245 -394 421 -202 205 -410 243 -392 423 -202 399 -244 393 -202 217 -432 387 -238 205 -380 245 -392 203 -428 417 -214 215 -400 211 -396 423 -202 441 -1048 211 -396 237 -182 215 -214 217 -182 211 -200 211 -212 237 -174 227 -182 211 -214 225 -198 187 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -240 173 -230 181 -212 211 -228 197 -188 211 -212 213 -228 195 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -186 211 -212 213 -226 229 -158 211 -212 237 -174 227 -180 211 -214 227 -198 187 -212 211 -214 227 -196 187 -210 831 -236 217 -394 395 -232 201 -406 415 -204 213 -430 417 -214 391 -204 425 -206 215 -428 205 -412 391 -238 217 -394 205 -426 385 -244 213 -398 417 -200 189 -432 203 -440 207 -394 201 -416 223 -410 409 -234 197 -402 417 -202 215 -430 205 -412 191 -418 217 -404 227 -410 185 -436 423 -206 215 -416 205 -394 201 -430 201 -408 245 -388 203 -426 417 -214 213 -402 211 -428 389 -238 203 -384 243 -390 423 -202 399 -244 391 -202 217 -432 387 -238 205 -384 243 -390 203 -428 417 -212 215 -402 211 -428 389 -238 405 -4628 201 -420 211 -216 243 -162 217 -218 207 -190 217 -218 209 -188 211 -182 229 -206 221 -182 211 -214 227 -196 187 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -240 171 -236 217 -182 211 -196 219 -218 207 -190 211 -182 231 -206 223 -182 211 -214 227 -198 185 -212 829 -224 211 -404 425 -206 215 -420 397 -230 197 -402 415 -204 407 -230 397 -244 217 -374 209 -428 417 -210 209 -388 243 -394 423 -202 203 -410 417 -240 213 -394 205 -410 227 -386 245 -394 201 -418 417 -206 215 -422 415 -206 203 -410 193 -418 245 -392 203 -428 199 -406 209 -422 395 -232 201 -406 245 -388 205 -426 205 -410 227 -386 245 -394 417 -208 209 -388 245 -392 423 -202 203 -410 243 -390 425 -202 433 -208 393 -202 217 -432 387 -240 203 -382 243 -394 203 -428 415 -214 215 -400 211 -430 389 -202 439 -4622 235 -388 215 -184 265 -170 201 -212 211 -202 207 -194 211 -212 213 -192 231 -186 211 -212\nRAW_Data: 213 -192 231 -186 211 -212 213 -192 231 -188 211 -212 211 -192 231 -186 211 -212 213 -192 231 -188 211 -210 213 -192 231 -186 211 -212 211 -228 197 -186 211 -212 211 -228 197 -186 211 -212 213 -192 231 -186 211 -212 213 -192 231 -186 211 -212 211 -228 197 -186 241 -182 211 -226 199 -186 211 -212 211 -228 197 -186 211 -212 211 -228 197 -186 211 -212 865 -174 251 -376 439 -192 193 -428 419 -204 217 -396 417 -210 425 -206 425 -206 217 -396 203 -430 393 -210 251 -376 245 -394 417 -210 209 -384 453 -166 213 -430 201 -408 245 -394 203 -428 205 -408 429 -202 219 -398 423 -202 201 -406 247 -390 205 -428 205 -378 257 -384 245 -394 417 -210 211 -386 245 -394 205 -426 205 -410 227 -384 247 -394 415 -212 209 -386 247 -392 407 -204 241 -380 247 -392 407 -204 441 -188 435 -198 193 -428 419 -204 217 -396 239 -380 223 -408 407 -234 201 -400 247 -388 427 -200 429 -4488 243 -394 237 -182 217 -210 191 -212 211 -202 205 -196 211 -210 213 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 229 -188 211 -210 213 -226 199 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 211 -228 197 -186 241 -182 211 -226 197 -188 211 -212 211 -194 231 -186 211 -212 211 -194 231 -186 211 -212 865 -176 251 -372 439 -202 199 -404 419 -204 217 -430 383 -212 429 -204 427 -204 219 -394 239 -394 431 -176 251 -374 245 -392 417 -212 209 -386 453 -166 213 -430 203 -408 245 -392 205 -426 205 -410 427 -204 217 -398 423 -202 203 -406 245 -392 205 -426 205 -380 257 -382 245 -396 415 -212 209 -386 245 -394 205 -428 205 -410 225 -384 247 -394 417 -210 209 -388 245 -394 423 -202 235 -376 247 -390 425 -202 429 -176 427 -202 219 -398 419 -204 239 -378 245 -394 205 -426 417 -214 217 -368 245 -396 423 -202 437 -4658 199 -416 213 -216 241 -160 219 -218 207 -186 211 -182 231 -204 223 -182 211 -212 227 -198 215 -182 211 -212 227 -198 185 -212 211 -214 225 -198 215 -182 211 -212 227 -198 215 -182 211 -212 227 -198 215 -182 211 -212 227 -196 217 -182 211 -212 227 -196 217 -182 211 -212 227 -198 215 -182 211 -212 227 -198 215 -182 211 -212 227 -196 217 -182 211 -212 261 -166 193 -212 211 -204 205 -194 211 -212 213 -226 197 -188 211 -212 211 -228 195 -188 857 -202 217 -394 417 -210 213 -394 447 -192\nRAW_Data: 217 -400 445 -204 409 -194 413 -218 211 -410 199 -418 419 -204 215 -424 205 -394 415 -210 213 -398 443 -192 219 -400 239 -412 207 -392 239 -394 203 -408 451 -196 183 -432 409 -202 217 -432 203 -394 241 -370 231 -398 219 -404 227 -412 407 -234 197 -402 209 -420 205 -426 205 -412 193 -418 243 -394 417 -208 209 -388 245 -392 423 -202 205 -410 245 -388 425 -202 397 -244 393 -202 217 -432 387 -238 205 -382 243 -390 203 -428 417 -212 215 -404 211 -430 387 -204 439 -4628 237 -396 209 -216 243 -162 217 -218 209 -188 217 -218 209 -188 217 -218 207 -188 211 -182 231 -204 223 -182 211 -212 227 -198 215 -182 211 -212 227 -196 217 -182 211 -212 225 -198 217 -182 211 -212 225 -198 217 -182 211 -212 225 -198 217 -182 211 -212 227 -198 185 -212 211 -214 225 -198 215 -182 211 -212 227 -198 215 -182 211 -214 225 -198 215 -182 211 -212 225 -198 217 -182 211 -212 227 -198 215 -182 211 -212 225 -198 217 -182 211 -212 855 -178 243 -380 417 -204 251 -394 415 -216 215 -366 447 -190 433 -184 433 -196 219 -400 241 -378 433 -196 229 -398 247 -392 425 -200 199 -404 419 -204 217 -428 205 -412 193 -416 209 -430 201 -418 417 -206 215 -422 397 -196 229 -400 245 -388 205 -426 205 -410 227 -384 243 -394 417 -210 211 -386 245 -392 203 -428 205 -410 227 -388 243 -394 417 -210 209 -388 245 -394 425 -202 203 -408 245 -390 425 -202 431 -174 429 -202 217 -432 387 -202 241 -380 247 -392 203 -428 415 -214 215 -400 211 -428 387 -238 405 -4494 237 -392 241 -180 255 -180 215 -182 217 -210 191 -218 217 -208 189 -212 211 -202 203 -194 211 -212 211 -228 197 -188 211 -212 211 -226 197 -188 211 -212 211 -226 197 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 213 -194 229 -188 211 -210 213 -194 229 -186 211 -212 213 -192 231 -186 211 -212 213 -194 229 -186 211 -212 211 -228 197 -188 211 -212 213 -192 229 -186 211 -212 211 -226 197 -188 211 -212 211 -226 197 -188 859 -204 217 -396 415 -210 211 -392 453 -196 187 -418 419 -224 403 -198 423 -210 215 -408 211 -430 387 -240 205 -384 245 -394 423 -202 203 -408 417 -206 215 -428 205 -412 193 -416 245 -394 201 -428 393 -210 215 -410 405 -226 193 -428 209 -420 205 -428 203 -412 191 -418 209 -430 387 -240 207 -386 245 -392 205 -426 205 -410 227 -388 243 -394 417 -210 209 -388 245 -394 423 -202 203 -408 245 -392 423 -202 433 -210 393 -202 217 -432 387 -238\nRAW_Data: 203 -380 245 -392 203 -428 417 -214 215 -400 213 -396 421 -202 441 -32700 249 -214 213 -192 227 -184 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 857 -196 211 -404 435 -194 211 -404 427 -204 215 -410 411 -208 423 -206 425 -206 217 -396 201 -430 395 -244 215 -376 209 -428 417 -208 209 -388 451 -198 187 -428 201 -410 243 -392 201 -428 203 -412 429 -204 217 -398 425 -200 203 -410 243 -390 203 -428 203 -412 227 -386 209 -428 417 -210 209 -388 245 -390 203 -428 205 -410 227 -392 209 -428 417 -208 209 -390 245 -390 423 -202 203 -412 243 -390 425 -200 399 -244 393 -202 217 -432 387 -238 205 -380 245 -392 203 -426 417 -214 215 -404 211 -430 387 -204 439 -1154 195 -408 243 -196 185 -224 219 -218 209 -200 181 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 195 -188 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 195 -188 211 -212 243 -202 203 -186 211 -212 199 -206 225 -182 211 -214 225 -198 187 -212 211 -212 227 -196 833 -196 225 -412 393 -236 217 -396 393 -232 201 -406 417 -204 435 -202 397 -244 215 -376 243 -394 417 -210 209 -388 245 -382 411 -244 193 -412 425 -202 203 -408 243 -390 203 -428 203 -412 227 -386 417 -202 233 -410 427 -202 217 -396 203 -428 205 -410 193 -418 209 -428 203 -428 393 -210 213 -412 209 -428 203 -428 197 -404 209 -422 205 -426 385 -246 213 -398 213 -396 421 -202 237 -380 245 -390 425 -200 399 -244 393 -202 217 -432 387 -240 203 -382 245 -390 205 -426 417 -214 215 -402 211 -430 387 -202 441 -4664 205 -398 211 -216 241 -164 217 -218 209 -188 217 -220 207 -188 211 -182 231 -204 223 -182 209 -214 227 -198 185 -212 211 -214 225 -198 185 -212 211 -214 227 -196 185 -212 211 -214 225 -198 185 -212 211 -214 227 -196 185 -212 211 -214 225 -198 185 -212 211 -214 225 -198 185 -212 211 -214 227 -196 187 -210 211 -214 227 -196 185 -212\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nero_sketch_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -2704 519 -368 279 -698 297 -712 259 -716 309 -688 647 -356 289 -710 615 -384 275 -692 345 -654 321 -696 619 -376 317 -658 335 -652 325 -684 655 -336 317 -698 295 -682 325 -678 639 -376 319 -662 331 -652 677 -352 631 -326 677 -326 315 -682 323 -680 317 -670 315 -702 637 -362 315 -654 679 -352 315 -658 315 -690 321 -676 647 -346 317 -660 337 -688 289 -686 655 -338 1007 -334 317 -350 301 -344 343 -350 315 -334 325 -352 313 -334 313 -346 349 -316 333 -326 351 -314 333 -348 315 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 301 -358 351 -314 333 -316 345 -350 303 -344 343 -352 313 -334 323 -350 315 -334 313 -344 349 -304 343 -340 317 -350 301 -356 315 -350 301 -346 345 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 333 -326 353 -314 333 -314 347 -350 1315 -320 681 -346 297 -680 317 -672 315 -700 311 -672 649 -354 323 -684 643 -352 313 -658 347 -654 323 -664 651 -376 317 -660 333 -652 323 -684 655 -338 317 -688 321 -676 293 -704 639 -338 353 -662 299 -684 647 -350 667 -326 677 -326 313 -676 323 -680 317 -670 315 -700 639 -362 315 -688 647 -352 313 -658 351 -662 297 -682 647 -350 349 -656 311 -688 321 -660 653 -342 1007 -334 317 -350 303 -344 343 -350 317 -332 325 -352 315 -336 311 -346 349 -316 333 -328 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -350 315 -334 313 -344 315 -352 331 -328 351 -314 333 -316 345 -350 301 -344 341 -352 315 -332 323 -352 315 -334 313 -344 349 -316 333 -328 351 -316 333 -314 347 -350 301 -344 341 -352 315 -334 323 -352 315 -336 311 -346 313 -352 331 -328 351 -316 303 -344 345 -350 303 -342 343 -352 1281 -352 645 -346 333 -678 315 -672 313 -702 311 -672 643 -354 325 -676 645 -352 313 -690 315 -654 323 -704 649 -344 317 -660 335 -652 323 -684 671 -346 317 -660 335 -684 291 -682 671 -346 317 -660 335 -652 677 -352 627 -364 645 -324 351 -648 323 -682 317 -674 347 -656 643 -360 313 -676 649 -350 315 -692 307 -688 287 -690 653 -338 353 -664 297 -678 319 -684 655 -338 983 -362 315 -350 303 -342 341 -352 315 -334 323 -352 313 -334 313 -346 313 -352 333 -326 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -352 313 -334 313 -346 313 -352 301 -358 351 -314 305 -344 345 -350 301 -342 343 -352 315 -302 353 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -354 317 -350 301 -344 345 -350 315\nRAW_Data: -334 325 -354 313 -334 313 -346 349 -302 341 -344 315 -352 301 -354 317 -348 303 -344 345 -350 1315 -324 677 -316 313 -706 293 -702 315 -670 313 -670 675 -322 329 -688 643 -352 315 -688 315 -654 323 -698 641 -348 315 -692 305 -688 289 -686 651 -342 353 -662 329 -652 323 -684 655 -334 317 -690 321 -676 649 -346 639 -354 637 -360 313 -676 323 -680 309 -688 317 -688 645 -350 315 -658 677 -320 329 -684 319 -660 341 -658 643 -354 327 -684 289 -688 341 -658 677 -320 989 -354 303 -352 315 -334 351 -350 315 -334 313 -342 315 -350 301 -358 351 -316 333 -314 345 -350 303 -342 341 -352 315 -334 323 -352 315 -334 311 -346 313 -352 301 -358 351 -314 305 -344 345 -350 315 -336 325 -352 313 -336 313 -346 349 -316 301 -358 351 -316 303 -344 345 -350 303 -342 341 -316 351 -332 323 -352 315 -334 311 -346 315 -350 303 -356 317 -350 301 -346 343 -350 303 -342 341 -318 349 -302 353 -352 313 -334 311 -346 1325 -350 655 -344 317 -662 333 -652 323 -680 317 -672 671 -334 317 -688 649 -350 301 -672 347 -656 347 -654 643 -352 313 -690 317 -654 323 -702 649 -346 317 -660 335 -652 323 -686 655 -338 351 -654 321 -676 647 -346 639 -356 635 -362 311 -676 323 -680 309 -688 317 -688 647 -352 313 -660 643 -354 325 -684 289 -688 343 -656 645 -354 323 -686 289 -682 317 -672 671 -336 975 -364 317 -350 303 -342 341 -352 317 -332 323 -352 315 -334 313 -344 315 -350 333 -326 351 -316 303 -346 345 -350 301 -344 341 -352 315 -302 353 -352 315 -334 313 -344 315 -350 333 -326 353 -314 303 -344 345 -350 315 -334 327 -352 313 -334 315 -346 349 -304 343 -342 315 -350 301 -356 315 -350 303 -344 345 -350 315 -334 325 -352 315 -334 315 -344 351 -302 343 -340 317 -352 299 -356 315 -350 303 -346 345 -350 313 -334 325 -352 1291 -346 655 -348 349 -658 309 -688 319 -658 343 -656 677 -320 327 -686 645 -350 313 -690 315 -652 323 -704 649 -346 317 -662 335 -652 323 -682 655 -338 319 -688 321 -676 295 -708 627 -364 317 -688 289 -704 651 -346 653 -322 659 -332 343 -670 317 -682 311 -688 317 -688 649 -352 315 -658 643 -356 323 -676 321 -662 343 -656 677 -322 325 -686 319 -658 343 -658 651 -354 987 -322 331 -354 313 -336 317 -382 313 -334 313 -342 315 -352 301 -358 315 -350 303 -344 347 -350 301 -344 341 -316 351 -332 323 -352 315 -336 311 -346 313 -352 301 -356 353 -314 305 -344 345 -350 315 -334 325 -354 313 -336 313 -346 349\nRAW_Data: -316 301 -358 351 -316 333 -314 347 -350 301 -344 341 -316 349 -334 323 -352 315 -334 311 -344 349 -302 343 -342 315 -352 299 -354 315 -350 301 -346 343 -350 315 -334 327 -350 315 -336 313 -344 1311 -352 657 -338 351 -664 295 -684 323 -680 309 -688 645 -324 351 -650 677 -352 315 -658 347 -654 321 -662 651 -376 317 -662 333 -650 325 -678 641 -376 317 -662 331 -650 323 -680 639 -376 319 -660 333 -650 677 -352 629 -328 677 -326 317 -680 323 -680 315 -672 313 -702 639 -362 315 -652 681 -352 313 -660 315 -666 327 -682 647 -348 315 -690 309 -676 321 -664 685 -344 971 -336 317 -350 303 -344 345 -350 315 -334 325 -354 313 -336 313 -346 349 -316 333 -326 351 -316 303 -346 345 -350 301 -344 341 -352 315 -302 353 -352 313 -334 311 -346 315 -350 301 -358 351 -316 333 -314 347 -350 301 -344 339 -354 315 -300 355 -352 313 -334 313 -344 351 -316 301 -356 351 -316 301 -346 345 -350 315 -334 327 -352 313 -334 315 -346 349 -316 333 -326 351 -316 333 -314 347 -350 301 -342 343 -350 315 -334 323 -352 1293 -346 657 -348 317 -660 339 -654 321 -686 343 -654 651 -324 353 -650 677 -352 315 -658 345 -652 321 -664 653 -376 317 -662 331 -652 323 -682 657 -336 317 -666 327 -678 323 -682 655 -336 317 -688 321 -674 647 -348 637 -356 637 -330 343 -676 325 -676 315 -672 313 -702 641 -322 331 -688 643 -352 313 -690 315 -654 321 -702 649 -346 317 -662 333 -652 323 -682 655 -338 973 -364 317 -350 301 -342 343 -352 315 -332 323 -352 315 -336 311 -344 313 -352 333 -326 351 -316 301 -346 345 -350 301 -344 343 -350 315 -302 353 -352 313 -334 313 -346 315 -350 333 -326 351 -316 331 -318 345 -350 315 -334 325 -354 313 -336 313 -344 349 -304 341 -342 315 -350 301 -356 315 -350 303 -344 347 -350 315 -334 325 -352 315 -332 315 -346 349 -304 343 -340 317 -352 299 -356 317 -350 301 -344 345 -350 315 -336 325 -352 1289 -346 655 -348 315 -692 309 -688 319 -658 343 -656 677 -322 325 -686 643 -352 313 -658 347 -654 321 -668 683 -344 317 -660 335 -652 323 -680 671 -344 319 -660 333 -650 325 -680 671 -344 319 -660 333 -650 677 -352 629 -328 677 -324 317 -680 323 -680 317 -672 313 -702 643 -322 331 -686 643 -352 313 -690 315 -654 323 -702 649 -346 317 -660 335 -652 323 -684 657 -336 973 -364 317 -350 301 -346 341 -352 315 -334 323 -352 315 -334 313 -344 313 -352 301 -356 353 -314 301 -346 345 -350 301 -344 341 -318 349\nRAW_Data: -302 353 -352 315 -334 311 -346 315 -350 303 -356 351 -314 333 -316 345 -350 315 -334 325 -354 313 -336 313 -344 315 -350 333 -326 353 -314 305 -344 345 -350 301 -342 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 301 -358 351 -314 333 -314 347 -350 301 -342 343 -350 315 -332 325 -352 1317 -316 659 -338 353 -662 299 -682 325 -680 311 -690 645 -326 315 -682 677 -318 349 -656 343 -654 321 -660 653 -374 319 -662 329 -652 323 -678 639 -376 319 -662 331 -650 323 -680 639 -376 319 -660 331 -652 679 -318 665 -326 679 -326 313 -674 319 -682 317 -670 347 -670 639 -328 347 -654 679 -350 315 -656 317 -688 321 -676 647 -346 317 -658 337 -652 323 -682 671 -346 969 -338 353 -314 303 -344 345 -350 315 -336 325 -352 313 -336 313 -344 315 -350 333 -326 351 -316 303 -346 343 -350 317 -334 325 -352 315 -334 313 -344 315 -350 333 -326 317 -350 305 -344 343 -350 317 -334 325 -350 315 -336 311 -346 349 -304 341 -342 315 -352 299 -354 317 -350 301 -344 345 -352 315 -334 323 -352 315 -334 311 -346 313 -352 301 -358 315 -352 303 -344 345 -350 315 -334 325 -352 315 -336 313 -344 315 -350 301 -358 1309 -350 665 -326 345 -670 321 -674 295 -702 315 -670 639 -362 315 -654 681 -352 313 -658 317 -688 321 -676 647 -346 315 -662 337 -654 323 -682 653 -338 353 -654 321 -676 295 -704 639 -340 351 -656 321 -676 647 -346 639 -352 639 -330 345 -670 319 -680 315 -670 315 -700 639 -328 347 -652 681 -352 313 -658 315 -666 329 -680 647 -348 315 -690 309 -688 289 -686 653 -374 949 -364 315 -350 303 -344 343 -352 315 -332 323 -352 313 -334 313 -344 315 -350 333 -326 317 -350 301 -346 345 -350 315 -332 327 -352 315 -334 313 -346 349 -316 301 -360 315 -350 303 -344 345 -350 303 -342 341 -318 349 -300 355 -350 313 -334 313 -346 313 -352 301 -356 351 -316 301 -346 345 -350 301 -344 341 -316 351 -300 353 -352 313 -334 313 -346 313 -352 301 -356 315 -350 303 -344 345 -350 315 -332 325 -352 313 -334 313 -346 1311 -346 671 -352 315 -656 345 -654 321 -662 341 -658 651 -354 323 -684 645 -352 313 -658 345 -654 321 -662 653 -376 319 -662 333 -650 325 -680 657 -334 317 -688 291 -704 295 -702 639 -338 351 -654 321 -676 649 -346 637 -356 635 -362 311 -676 325 -676 317 -670 313 -690 641 -344 301 -690 653 -340 351 -654 323 -678 293 -704 639 -338 353 -662 295 -684 323 -680 657 -336 977 -330 349 -354 317 -302 355 -352 315\nRAW_Data: -304 341 -346 349 -318 333 -326 317 -350 303 -344 343 -354 315 -302 355 -352 315 -336 311 -344 317 -352 301 -356 351 -316 303 -344 343 -352 317 -302 353 -352 315 -336 311 -346 315 -352 301 -356 351 -316 303 -344 345 -352 315 -334 325 -352 315 -304 343 -344 317 -350 303 -356 353 -314 303 -344 345 -352 315 -334 325 -354 313 -304 343 -346 349 -316 333 -326 351 -316 303 -344 345 -350 315 -334 1319 -338 679 -322 321 -680 319 -658 345 -656 351 -630 657 -348 333 -674 641 -342 355 -660 331 -652 323 -682 655 -336 317 -658 353 -674 295 -670 665 -362 317 -656 321 -668 343 -658 677 -320 329 -678 319 -664 685 -310 689 -322 657 -332 309 -698 317 -678 311 -688 319 -668 667 -344 297 -682 653 -338 355 -630 329 -684 323 -676 639 -340 355 -662 331 -650 325 -678 671 -310 1007 -334 319 -350 303 -344 343 -352 317 -334 323 -352 315 -304 343 -346 315 -352 333 -326 351 -316 303 -344 343 -352 315 -334 327 -350 317 -334 313 -344 351 -318 333 -326 351 -316 303 -344 345 -350 315 -334 325 -354 315 -334 313 -344 349 -320 301 -356 351 -316 303 -344 343 -350 317 -332 325 -352 315 -336 311 -346 317 -350 333 -326 353 -314 305 -342 345 -352 315 -334 323 -354 313 -336 311 -346 315 -352 301 -358 351 -314 305 -342 1337 -348 655 -322 329 -688 319 -658 343 -656 353 -654 649 -350 301 -670 663 -328 347 -670 321 -678 293 -702 637 -340 353 -662 329 -648 325 -678 671 -344 319 -662 331 -652 323 -682 655 -338 351 -630 327 -682 647 -350 655 -322 663 -344 333 -662 343 -656 317 -690 321 -676 645 -348 317 -662 669 -346 299 -684 343 -654 317 -688 647 -314 343 -662 343 -656 353 -656 649 -346 999 -326 311 -346 349 -304 341 -342 317 -352 299 -354 317 -350 303 -344 343 -352 317 -334 323 -352 317 -304 343 -344 317 -352 301 -354 317 -352 303 -342 345 -350 301 -344 339 -318 351 -300 353 -352 315 -304 341 -346 317 -350 301 -356 317 -350 303 -344 343 -352 317 -332 323 -354 315 -334 313 -344 317 -352 301 -356 317 -352 303 -344 343 -352 315 -334 323 -352 315 -336 311 -346 315 -352 301 -356 351 -316 303 -344 345 -350 319 -302 1343 -338 645 -352 323 -688 287 -688 345 -658 317 -666 655 -348 333 -674 659 -334 317 -656 321 -700 295 -670 665 -328 349 -654 323 -664 329 -670 669 -338 319 -666 327 -682 323 -678 637 -342 353 -662 329 -652 679 -318 667 -324 675 -318 349 -646 323 -682 317 -670 345 -670 635 -364 317 -656 681 -316 351 -656 317\nRAW_Data: -690 319 -676 649 -346 317 -660 337 -646 323 -684 653 -342 1009 -332 317 -346 311 -330 343 -316 351 -352 315 -304 345 -344 317 -350 301 -354 353 -316 303 -344 343 -352 317 -302 355 -352 315 -304 343 -344 317 -352 301 -354 353 -316 303 -344 345 -352 315 -302 355 -352 315 -304 343 -344 317 -350 303 -356 317 -350 303 -344 345 -352 315 -302 353 -354 313 -304 343 -346 315 -352 301 -354 317 -350 305 -342 345 -352 315 -302 355 -352 315 -336 311 -346 349 -316 301 -358 353 -314 303 -344 343 -352 1311 -320 679 -346 297 -680 317 -670 345 -670 309 -670 675 -322 327 -686 643 -352 315 -656 349 -654 321 -666 683 -344 319 -660 331 -654 323 -678 671 -310 353 -662 331 -652 323 -678 671 -310 353 -662 331 -650 681 -318 665 -326 675 -326 313 -680 323 -682 309 -688 317 -656 681 -316 349 -660 645 -354 323 -650 323 -682 317 -672 671 -336 319 -664 327 -680 325 -676 637 -344 1005 -334 319 -350 303 -344 343 -318 351 -302 353 -352 315 -304 343 -344 317 -352 301 -354 317 -352 303 -344 343 -350 317 -334 325 -352 315 -304 343 -346 315 -352 301 -356 351 -316 303 -344 345 -350 315 -334 325 -354 315 -334 313 -344 349 -304 341 -342 317 -352 299 -352 317 -350 303 -344 343 -352 315 -334 323 -352 315 -304 343 -344 317 -350 303 -354 317 -352 303 -344 343 -350 315 -334 325 -354 313 -336 311 -346 349 -316 301 -358 1339 -318 655 -354 305 -690 319 -658 343 -658 351 -656 651 -350 301 -670 669 -338 317 -690 319 -674 295 -672 669 -338 353 -654 321 -678 293 -702 637 -340 355 -654 321 -676 325 -676 651 -324 345 -654 321 -664 685 -344 643 -322 661 -344 331 -662 343 -656 317 -690 321 -674 649 -348 317 -662 665 -346 299 -684 345 -654 315 -656 683 -352 315 -658 317 -664 327 -682 647 -348 987 -336 353 -316 303 -344 343 -350 317 -334 325 -352 313 -336 311 -346 349 -316 333 -326 351 -316 303 -344 345 -350 315 -336 325 -354 313 -336 311 -346 315 -352 301 -356 351 -316 303 -344 345 -350 315 -334 325 -352 315 -336 311 -346 349 -316 333 -326 353 -316 303 -344 343 -352 317 -332 325 -352 313 -336 313 -344 317 -350 303 -358 351 -316 303 -344 343 -352 315 -334 325 -352 315 -334 313 -344 349 -318 333 -326 351 -316 303 -344 1337 -348 639 -354 339 -654 321 -660 317 -704 315 -670 669 -332 317 -654 681 -350 315 -658 351 -656 321 -674 649 -348 317 -662 337 -654 323 -682 655 -338 353 -662 297 -680 325 -680 657 -336 317 -690 319 -674 647\nRAW_Data: -346 639 -354 639 -330 341 -676 323 -676 315 -672 313 -700 639 -328 347 -668 651 -348 301 -702 315 -670 313 -668 675 -320 331 -686 321 -658 343 -658 677 -320 985 -322 371 -318 313 -336 349 -318 349 -304 341 -344 317 -352 301 -354 317 -350 303 -344 345 -350 315 -334 327 -352 315 -334 313 -344 317 -352 301 -354 351 -316 303 -346 343 -350 315 -336 325 -352 315 -334 313 -346 349 -316 333 -328 351 -314 305 -342 345 -350 315 -334 327 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -354 315 -352 303 -344 343 -352 317 -332 323 -352 315 -334 313 -344 349 -318 331 -326 351 -316 303 -344 1333 -350 653 -322 333 -686 321 -658 343 -658 351 -654 651 -350 301 -670 669 -336 319 -688 321 -674 293 -702 639 -338 353 -656 321 -674 295 -704 637 -340 353 -662 295 -684 323 -678 639 -342 353 -662 331 -652 645 -350 667 -324 675 -326 313 -676 323 -678 317 -670 313 -700 639 -330 347 -654 679 -318 349 -656 317 -690 319 -676 647 -348 317 -660 337 -652 323 -684 653 -338 983 -364 317 -350 301 -344 341 -316 351 -302 353 -352 315 -334 313 -344 317 -350 303 -356 351 -316 303 -344 343 -352 315 -334 325 -354 313 -336 313 -344 349 -318 333 -326 351 -316 303 -344 345 -350 301 -344 341 -318 349 -302 353 -350 315 -304 343 -346 315 -352 301 -356 351 -316 303 -344 343 -350 317 -334 325 -352 315 -336 311 -346 349 -302 341 -344 315 -352 299 -356 315 -350 303 -344 343 -350 317 -334 325 -352 315 -336 311 -346 1329 -350 655 -346 319 -660 333 -650 323 -684 343 -658 647 -324 351 -650 677 -352 315 -656 345 -654 321 -664 653 -342 355 -662 333 -650 325 -680 657 -336 317 -692 319 -676 293 -702 639 -338 353 -662 329 -652 645 -352 667 -326 675 -324 315 -678 325 -678 315 -670 313 -702 639 -328 347 -654 679 -352 315 -660 315 -690 323 -676 647 -346 317 -660 337 -678 317 -658 651 -344 1001 -348 309 -346 325 -348 311 -344 341 -310 313 -356 349 -312 313 -370 317 -352 299 -352 315 -350 303 -344 343 -352 315 -334 323 -352 315 -334 313 -344 349 -304 341 -342 315 -352 301 -354 317 -350 303 -344 343 -352 317 -334 323 -352 313 -336 311 -346 313 -352 333 -326 351 -316 303 -346 343 -350 303 -342 343 -318 349 -302 353 -350 315 -336 311 -346 315 -352 301 -356 351 -314 335 -314 347 -350 315 -334 97 -16804 99 -30844 99 -5628 511 -370 283 -718 287 -712 275 -722 275 -716 615 -382 277 -690 645 -358 289 -714 289 -688 341 -656 649 -354 323\nRAW_Data: -682 289 -682 317 -672 671 -336 317 -688 321 -674 295 -704 639 -338 353 -660 297 -684 647 -350 667 -326 677 -326 313 -676 323 -680 317 -672 313 -704 641 -322 333 -686 645 -350 313 -690 315 -654 323 -704 649 -344 317 -660 335 -654 323 -682 671 -344 971 -338 317 -350 301 -346 345 -350 301 -344 339 -350 315 -334 323 -352 313 -334 313 -346 349 -302 343 -342 315 -352 301 -354 317 -350 301 -346 343 -350 315 -334 327 -352 315 -334 313 -344 351 -302 343 -340 317 -350 301 -354 317 -350 301 -346 343 -350 315 -334 325 -352 315 -334 313 -346 313 -352 333 -326 351 -316 303 -344 345 -350 303 -344 341 -318 349 -302 353 -352 313 -334 313 -346 313 -352 301 -358 351 -314 301 -346 345 -350 1315 -324 679 -346 299 -678 317 -670 313 -702 311 -672 645 -354 325 -684 643 -352 313 -658 347 -672 321 -674 647 -346 317 -660 337 -652 323 -686 655 -340 317 -698 295 -682 325 -680 657 -334 317 -690 321 -674 647 -348 637 -356 637 -328 345 -674 325 -680 309 -688 317 -666 651 -348 303 -704 639 -340 351 -664 295 -682 323 -680 639 -378 319 -662 331 -650 323 -682 655 -336 975 -364 317 -350 301 -344 343 -352 315 -334 323 -352 313 -334 313 -344 315 -350 303 -356 351 -316 303 -346 343 -350 303 -344 341 -316 351 -302 351 -352 313 -334 315 -344 315 -350 303 -356 351 -314 333 -316 345 -350 301 -344 341 -352 315 -334 321 -352 315 -334 313 -342 315 -350 303 -356 317 -350 301 -346 345 -350 315 -334 325 -350 313 -334 315 -346 315 -350 301 -358 351 -314 333 -316 343 -352 301 -344 339 -350 315 -334 323 -352 1289 -346 655 -348 315 -692 307 -656 323 -682 317 -672 671 -338 317 -658 681 -352 315 -660 317 -690 321 -674 649 -348 319 -660 335 -654 323 -682 653 -338 353 -632 327 -684 323 -682 657 -332 319 -688 319 -676 647 -348 653 -320 663 -342 301 -688 343 -656 317 -690 321 -674 647 -346 317 -660 669 -344 301 -680 317 -670 347 -668 637 -364 317 -654 321 -666 351 -674 637 -340 985 -364 317 -352 315 -334 325 -354 313 -336 313 -344 349 -318 333 -326 351 -316 303 -344 345 -350 315 -334 325 -354 313 -336 311 -346 349 -318 333 -326 351 -316 303 -344 345 -350 317 -334 325 -354 313 -336 311 -346 351 -316 333 -326 351 -316 303 -344 343 -350 317 -334 325 -354 315 -304 343 -344 351 -318 301 -358 317 -350 303 -344 345 -352 315 -302 355 -352 315 -304 343 -346 315 -352 301 -356 351 -316 303 -344 343 -350 317 -334 1319 -336 645 -354 325\nRAW_Data: -676 319 -656 319 -672 347 -654 679 -326 315 -680 647 -350 317 -692 307 -690 289 -686 655 -340 353 -656 321 -676 295 -702 637 -340 353 -662 297 -684 323 -678 637 -342 353 -660 331 -652 647 -350 669 -324 675 -326 313 -678 325 -678 315 -670 313 -702 641 -322 333 -688 645 -352 313 -688 317 -656 321 -668 685 -346 319 -660 333 -652 323 -684 655 -336 977 -364 317 -350 303 -342 341 -318 349 -302 351 -352 315 -304 343 -346 351 -316 301 -356 351 -316 303 -344 343 -350 303 -342 341 -318 349 -302 353 -354 313 -306 341 -346 315 -352 301 -356 351 -316 303 -344 345 -350 315 -334 325 -352 315 -336 311 -344 317 -352 301 -356 353 -314 335 -314 345 -350 315 -334 327 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -354 315 -352 303 -342 345 -352 315 -334 323 -352 315 -334 313 -344 1313 -350 659 -336 319 -690 321 -674 295 -672 347 -654 679 -324 315 -676 673 -352 313 -658 345 -640 321 -702 649 -346 317 -660 335 -646 321 -686 653 -340 353 -656 321 -676 295 -702 637 -340 353 -662 297 -684 645 -350 665 -324 675 -326 313 -680 323 -676 317 -670 313 -700 639 -328 349 -656 679 -316 351 -656 315 -690 319 -674 647 -346 317 -660 337 -654 323 -686 653 -338 975 -364 317 -350 303 -342 341 -318 349 -302 353 -352 315 -304 343 -342 317 -352 301 -356 349 -316 303 -344 345 -352 315 -334 323 -352 315 -334 313 -344 315 -352 301 -356 317 -350 303 -344 345 -350 315 -334 323 -352 315 -336 313 -344 317 -350 303 -354 317 -352 303 -344 343 -350 315 -334 325 -352 313 -336 311 -346 315 -350 303 -356 353 -314 335 -314 345 -352 315 -302 355 -352 315 -304 343 -344 317 -350 303 -356 317 -352 303 -344 1337 -346 653 -322 327 -688 289 -688 333 -668 315 -692 639 -344 301 -688 655 -340 349 -636 339 -654 323 -682 671 -346 317 -662 333 -654 323 -678 671 -346 317 -660 333 -654 323 -680 639 -376 319 -660 333 -650 679 -318 665 -326 677 -326 315 -680 323 -678 315 -672 343 -672 635 -362 317 -654 679 -352 315 -658 317 -688 321 -676 647 -346 317 -658 337 -654 323 -682 671 -346 969 -338 353 -316 303 -344 345 -350 315 -334 325 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -356 315 -350 303 -344 343 -350 317 -334 323 -352 315 -336 311 -344 351 -316 301 -358 351 -316 303 -344 345 -350 315 -334 325 -352 315 -336 313 -344 349 -318 333 -326 351 -316 303 -344 345 -350 315 -334 327 -350 315 -336 311 -346 349 -302 343 -342 317\nRAW_Data: -350 301 -354 351 -314 305 -342 345 -352 315 -334 323 -352 315 -336 311 -344 1325 -350 655 -346 317 -660 335 -654 323 -680 315 -672 669 -336 317 -690 649 -350 301 -668 347 -670 311 -670 677 -320 327 -686 289 -688 343 -656 643 -354 327 -686 287 -690 343 -656 649 -324 353 -650 321 -682 671 -346 641 -322 663 -330 343 -676 325 -674 315 -670 313 -670 685 -320 327 -686 643 -352 315 -656 347 -672 319 -676 647 -346 317 -658 337 -654 323 -682 671 -346 971 -338 351 -316 303 -344 345 -352 315 -334 325 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -352 317 -352 303 -344 343 -350 315 -334 323 -352 315 -336 311 -346 317 -350 303 -356 351 -316 303 -344 345 -350 301 -342 343 -316 351 -300 353 -352 315 -304 343 -344 315 -352 333 -326 351 -316 303 -344 343 -350 315 -336 325 -352 313 -336 313 -344 349 -304 341 -342 317 -350 301 -354 315 -350 303 -344 343 -350 315 -334 327 -352 1289 -346 655 -348 317 -692 309 -688 289 -686 343 -656 643 -356 323 -686 645 -352 313 -658 347 -638 321 -706 647 -346 319 -660 333 -652 323 -682 671 -344 319 -660 331 -652 323 -678 639 -378 317 -662 331 -652 647 -350 665 -326 677 -326 315 -678 323 -678 315 -672 313 -702 641 -324 331 -688 645 -352 315 -656 347 -670 321 -676 647 -348 317 -660 337 -652 323 -682 671 -346 971 -338 353 -316 303 -344 343 -350 317 -334 325 -352 315 -334 313 -344 349 -316 333 -326 353 -314 335 -314 345 -350 315 -334 327 -352 315 -334 313 -344 349 -318 301 -358 315 -352 303 -344 343 -350 315 -334 325 -354 313 -336 313 -344 349 -304 341 -342 315 -352 299 -354 315 -352 303 -344 343 -350 317 -334 325 -352 315 -304 343 -344 317 -350 303 -356 315 -350 305 -344 345 -350 315 -334 323 -352 315 -336 311 -346 349 -316 333 -326 1335 -352 629 -328 345 -654 323 -666 343 -656 351 -662 661 -346 297 -678 671 -310 353 -662 331 -650 323 -678 639 -342 353 -662 329 -650 325 -678 639 -376 319 -662 329 -650 323 -680 639 -378 319 -660 331 -652 645 -350 667 -326 679 -326 313 -674 325 -678 315 -670 313 -702 653 -320 329 -686 643 -352 315 -660 347 -672 287 -706 647 -346 317 -662 333 -654 323 -684 655 -336 977 -362 317 -350 301 -344 341 -316 351 -300 353 -352 313 -306 341 -344 315 -352 301 -356 317 -350 303 -344 343 -352 315 -334 325 -352 315 -336 313 -344 317 -350 303 -354 317 -350 303 -344 345 -350 315 -334 323 -352 315 -336 311 -344 351 -316 301 -358 315\nRAW_Data: -352 303 -346 343 -352 317 -334 323 -352 313 -336 311 -346 317 -350 301 -356 351 -316 303 -344 345 -350 315 -334 325 -352 315 -334 313 -344 317 -352 301 -358 315 -352 303 -344 1339 -346 639 -322 333 -690 319 -660 341 -658 351 -662 627 -346 333 -674 673 -310 353 -662 297 -684 323 -682 657 -336 317 -688 319 -674 295 -672 685 -324 343 -652 321 -664 343 -656 677 -322 325 -686 287 -690 653 -340 675 -320 655 -350 313 -676 325 -680 311 -656 349 -654 679 -316 349 -658 643 -356 321 -652 323 -684 345 -656 643 -356 321 -652 323 -682 317 -672 671 -336 975 -362 317 -350 315 -334 325 -352 315 -336 311 -346 313 -352 301 -356 317 -350 303 -344 345 -350 315 -334 323 -352 315 -334 311 -346 313 -352 301 -356 351 -316 303 -346 345 -352 315 -302 353 -352 315 -336 311 -346 315 -352 301 -356 315 -352 303 -344 343 -352 317 -332 323 -352 315 -306 343 -342 317 -350 301 -356 315 -352 303 -344 343 -350 317 -334 325 -352 313 -304 341 -346 313 -352 301 -356 351 -316 303 -344 343 -352 315 -334 1315 -336 645 -354 325 -684 289 -686 345 -656 315 -690 645 -350 301 -668 671 -336 353 -630 327 -682 323 -676 639 -340 353 -662 331 -650 323 -680 639 -342 353 -664 297 -684 323 -682 657 -332 317 -688 291 -704 649 -346 655 -320 659 -346 301 -688 343 -656 315 -656 323 -704 649 -348 317 -660 667 -344 297 -682 315 -674 349 -656 641 -332 343 -676 325 -674 315 -672 669 -332 973 -362 315 -350 317 -334 325 -352 313 -336 313 -344 349 -304 341 -342 315 -352 299 -354 317 -350 303 -344 343 -352 317 -334 323 -352 315 -304 343 -344 317 -352 301 -356 315 -350 303 -346 343 -350 317 -332 323 -352 315 -336 311 -346 349 -316 333 -326 351 -316 303 -344 345 -350 315 -334 325 -352 315 -334 313 -346 313 -352 301 -358 351 -316 303 -344 345 -350 315 -334 325 -352 315 -336 311 -346 315 -352 301 -356 317 -350 303 -344 1333 -348 639 -354 339 -640 323 -664 343 -660 351 -662 661 -310 333 -676 673 -310 353 -662 331 -650 325 -682 657 -334 317 -666 327 -676 317 -678 671 -346 319 -660 331 -652 325 -678 671 -310 355 -660 331 -652 679 -318 667 -326 673 -326 313 -676 319 -680 317 -672 343 -670 637 -362 317 -658 679 -318 349 -658 317 -658 351 -642 685 -346 319 -660 333 -654 323 -680 655 -338 977 -362 319 -352 315 -334 325 -352 315 -304 341 -346 351 -318 303 -356 351 -316 303 -344 345 -352 317 -302 353 -354 315 -304 341 -346 315 -354 301 -354 351\nRAW_Data: -316 303 -344 343 -350 319 -302 355 -352 315 -304 341 -346 351 -318 303 -354 353 -316 303 -342 345 -352 315 -302 355 -354 313 -304 343 -344 351 -318 333 -326 353 -318 303 -342 343 -352 317 -302 355 -352 315 -304 341 -346 351 -318 301 -356 351 -316 303 -344 345 -352 317 -302 1343 -338 645 -354 323 -686 319 -656 343 -656 353 -654 649 -352 317 -658 679 -320 325 -678 319 -660 343 -658 679 -322 323 -654 351 -656 343 -658 647 -324 351 -652 323 -680 317 -670 667 -336 355 -632 327 -682 645 -350 669 -322 677 -296 341 -680 323 -674 315 -670 343 -636 669 -328 351 -668 649 -350 301 -668 345 -668 311 -668 679 -320 327 -656 353 -656 343 -658 679 -290 1015 -322 367 -322 317 -336 311 -350 351 -304 341 -342 319 -354 299 -348 319 -352 303 -342 343 -318 353 -304 349 -318 351 -32700 99 -8980 101 -14522 97 -32700 99 -22564\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Nice FLO\nBit: 24\nKey: 00 00 00 00 00 02 FE C4\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 32700 -4166 259 -25228 693 -724 1407 -1390 679 -714 1395 -1418 679 -1408 709 -1404 709 -678 1413 -680 1401 -718 1403 -688 1401 -720 1389 -1412 691 -710 1401 -1400 673 -730 1373 -724 1397 -1394 711 -1378 711 -1408 707 -678 1395 -710 1391 -1406 703 -710 1395 -712 1401 -25172 443 -12300 491 -932 1179 -834 1279 -800 1343 -766 1343 -762 1339 -1426 675 -750 1369 -1418 679 -716 1381 -714 1399 -1412 681 -1404 713 -1404 709 -676 1393 -712 1397 -1416 681 -714 1415 -680 1395 -25196 691 -746 1357 -1440 671 -714 1391 -1406 701 -1408 697 -1408 697 -678 1415 -686 1407 -698 1427 -676 1413 -678 1409 -1410 701 -700 1403 -1392 711 -686 1427 -696 1403 -1390 687 -1408 709 -1406 707 -676 1415 -680 1405 -1414 681 -704 1403 -718 1409 -25142 727 -706 1403 -1398 709 -690 1413 -1414 677 -1406 711 -1406 673 -706 1395 -714 1395 -720 1397 -696 1405 -696 1403 -1394 709 -684 1413 -1382 711 -714 1387 -714 1383 -1406 707 -1406 675 -1406 703 -710 1397 -712 1397 -1404 675 -710 1395 -712 1395 -25186 655 -846 1233 -1548 593 -788 1299 -1520 587 -1490 615 -1474 645 -744 1357 -746 1381 -714 1373 -736 1367 -706 1399 -1432 673 -728 1377 -1418 679 -714 1415 -682 1413 -1376 713 -1408 709 -1372 705 -710 1397 -712 1381 -1410 721 -680 1409 -690 1405 -25168 701 -760 1341 -1456 639 -756 1345 -1452 677 -1410 673 -1442 671 -712 1399 -712 1371 -718 1411 -688 1409 -690 1407 -1416 679 -712 1395 -1414 685 -716 1383 -714 1383 -1410 711 -1400 709 -1370 705 -710 1397 -710 1397 -1410 679 -712 1395 -686 1427 -25156 563 -7950 557 -1546 555 -1508 621 -790 1329 -758 1331 -778 1355 -714 1377 -714 1407 -1404 669 -730 1407 -1390 709 -686 1411 -684 1397 -1416 685 -1406 711 -1404 707 -676 1427 -682 1395 -1420 679 -712 1395 -688 1429 -25160 745 -712 1359 -1438 671 -712 1399 -1408 697 -1406 695 -1404 697 -682 1403 -714 1413 -686 1393 -710 1411 -686 1407 -1402 701 -676 1431 -1368 735 -674 1399 -704 1397 -1400 711 -1414 683 -1408 709 -678 1421 -678 1427 -1374 711 -710 1395 -686 1431 -25158 713 -708 1391 -1406 703 -712 1397 -1408 707 -1372 707 -1406 699 -712 1379 -716 1405 -688 1403 -720 1387 -684 1409 -1402 705 -712 1399 -1408 673 -722 1403 -694 1397 -1390 709 -1414 681 -1404 709 -680 1421 -680 1415 -1406 697 -714 1369 -718 1413 -25180 709 -688 1403 -1424 673 -696 1429 -1398 679 -1404 717 -1376 719 -700 1411 -696 1377 -724 1397 -700 1405 -698 1407 -1390 685 -716 1383 -1412 709 -714 1385 -712 1387 -1406 709 -1402 673 -1406 703 -710 1403 -680 1403 -1412 685 -718 1407 -692 1399 -25178 727 -686 1401 -1400 709 -686 1429 -1388 709 -1408 679 -1406 711 -678 1421 -680 1423 -680 1423 -676 1423 -678 1395 -1406\nRAW_Data: 711 -716 1387 -1408 675 -710 1397 -710 1395 -1404 703 -1404 699 -1408 697 -712 1371 -716 1405 -1386 715 -684 1399 -718 1381 -25206 689 -716 1411 -1414 681 -716 1389 -1406 709 -1372 703 -1408 699 -712 1383 -718 1407 -686 1391 -688 1409 -716 1401 -1406 689 -688 1411 -1384 715 -716 1381 -712 1395 -1414 685 -1408 711 -1372 707 -708 1395 -710 1397 -1402 699 -710 1395 -708 1393 -25166 715 -712 1399 -1420 681 -714 1381 -1404 711 -1404 707 -1404 675 -712 1397 -710 1395 -710 1395 -684 1397 -724 1401 -1392 709 -686 1407 -1414 683 -710 1397 -690 1413 -1382 711 -1410 709 -1372 703 -712 1413 -680 1403 -1412 687 -718 1375 -728 1399 -32700 97 -132 327 -924 291 -98 625\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nice_flor_s_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1938 -32700 1599 -1476 515 -1044 507 -1050 487 -1052 1007 -572 1009 -556 999 -548 1025 -546 1019 -510 517 -1044 513 -1042 1007 -514 507 -1074 1013 -546 515 -1046 515 -1044 513 -1040 513 -1040 1005 -514 1035 -516 509 -1072 513 -1040 513 -1042 1007 -576 1009 -512 1011 -546 1009 -548 1005 -516 1005 -586 1009 -546 1009 -544 515 -1042 513 -1042 511 -1044 513 -1010 511 -1042 515 -1076 475 -1074 511 -1040 1009 -548 1007 -546 513 -1040 511 -1038 475 -1040 507 -1074 513 -1042 1011 -578 981 -546 1009 -544 515 -1042 1007 -546 475 -1038 509 -1040 1497 -19024 1543 -1508 503 -1034 509 -1038 509 -1038 1003 -584 1005 -574 979 -576 483 -1074 483 -1042 513 -1044 513 -1038 1005 -514 509 -1072 1009 -548 513 -1042 515 -1042 513 -1042 513 -1044 1007 -514 1037 -516 511 -1068 513 -1040 513 -1042 1009 -546 1009 -546 1007 -546 1007 -548 1005 -514 1005 -584 1007 -548 1009 -546 515 -1042 513 -1044 513 -1040 511 -1038 477 -1038 507 -1076 513 -1044 515 -1040 1009 -546 1009 -546 515 -1042 513 -1010 511 -1042 513 -1078 479 -1072 1007 -546 1007 -548 1007 -546 513 -1040 1005 -514 509 -1038 509 -1008 1529 -19028 1545 -1508 513 -1046 483 -1052 489 -1050 1009 -568 1007 -558 995 -548 527 -1052 1015 -512 515 -1044 513 -1040 1003 -506 541 -1074 983 -578 483 -1044 515 -1042 513 -1042 515 -1040 1007 -546 1005 -516 507 -1072 513 -1040 517 -1042 1009 -544 1009 -544 1011 -544 1007 -540 1009 -546 1005 -548 1011 -578 981 -576 485 -1044 515 -1042 513 -1044 513 -1010 509 -1040 507 -1074 511 -1042 513 -1042 1013 -546 1007 -542 513 -1042 515 -1038 511 -1006 507 -1076 515 -1042 1011 -578 977 -574 1013 -544 481 -1044 1007 -546 511 -1008 511 -1042 1505 -19032 1541 -1488 507 -1074 479 -1044 513 -1046 975 -590 1011 -574 483 -1052 1007 -556 499 -1050 489 -1042 517 -1044 1005 -516 509 -1074 1011 -546 517 -1042 515 -1044 513 -1042 513 -1042 1009 -516 1009 -550 477 -1108 477 -1074 513 -1040 1009 -548 1009 -546 1009 -550 975 -548 1003 -554 999 -574 1009 -548 1009 -548 513 -1040 513 -1044 513 -1042 479 -1040 509 -1040 509 -1078 479 -1078 513 -1042 1011 -550 1007 -548 477 -1072 477 -1040 509 -1040 509 -1076 511 -1038 1011 -548 1009 -548 1009 -546 513 -1040 1009 -518 513 -1040 509 -1010 1527 -19010 1541 -1512 511 -1042 511 -1046 483 -1046 1001 -570 1013 -570 493 -1066 1001 -550 979 -564 503 -1052 485 -1048 1009 -516 509 -1076 1011 -546 517 -1044 517 -1044 515 -1044 479 -1076 971 -574 971 -550 509 -1076 481 -1076 513 -1040 1007 -544 1011 -546 1007 -548 1007 -514 1005 -550 1009 -556 1003 -584 1007 -550 479 -1076 479 -1076 479 -1042 513 -1044 483 -1044 507 -1082 507 -1068 477 -1074\nRAW_Data: 1011 -550 973 -550 509 -1044 513 -1044 483 -1048 491 -1082 519 -1048 1005 -556 993 -548 1021 -546 487 -1046 1011 -548 479 -1044 513 -1012 1513 -19026 1535 -1534 507 -1046 483 -1056 491 -1034 1007 -572 1009 -558 501 -1052 525 -1044 487 -1042 515 -1040 513 -1040 1007 -514 507 -1072 1009 -546 515 -1074 483 -1042 515 -1044 515 -1044 1011 -516 1009 -522 519 -1082 507 -1042 509 -1042 1007 -550 1011 -552 1009 -522 1037 -522 1017 -512 1003 -584 999 -582 993 -546 523 -1046 483 -1042 515 -1042 513 -1040 511 -1006 511 -1074 513 -1042 513 -1042 1013 -546 1009 -540 515 -1042 513 -1040 511 -1004 509 -1074 515 -1044 1011 -544 1009 -578 977 -544 515 -1042 1007 -546 511 -1004 509 -1038 1529 -18994 1549 -1510 513 -1044 515 -1010 505 -1044 1001 -590 1017 -556 487 -1048 505 -1054 1019 -544 485 -1046 515 -1042 1007 -516 513 -1074 1007 -548 513 -1040 515 -1042 513 -1044 513 -1042 287 -32700 1577 -1544 481 -1034 507 -1046 509 -1048 1001 -588 989 -552 1013 -566 971 -584 989 -546 487 -1044 515 -1046 479 -1040 509 -1074 513 -1044 515 -1044 517 -1042 513 -1044 513 -1040 1011 -516 509 -1048 483 -1080 1007 -556 513 -1042 1003 -552 1001 -554 1011 -524 1015 -554 985 -538 503 -1086 499 -1050 1017 -546 521 -1048 483 -1044 515 -1040 513 -1040 1005 -518 513 -1076 1007 -548 511 -1042 1005 -542 1009 -548 515 -1040 513 -1038 1005 -516 509 -1072 1009 -546 1009 -578 977 -580 977 -544 515 -1042 1005 -542 1011 -514 509 -1012 1541 -18996 1571 -1504 515 -1014 519 -1020 537 -1018 1011 -568 1011 -556 997 -546 525 -1050 519 -1008 513 -1040 513 -1038 511 -1004 539 -1040 515 -1076 517 -1008 549 -1010 515 -1040 513 -1040 1011 -514 543 -1002 539 -1040 1011 -546 547 -1008 1043 -512 1045 -510 1043 -508 1041 -510 1005 -546 507 -1072 519 -1042 1013 -542 519 -1010 517 -1040 513 -1040 511 -1040 1003 -514 539 -1038 1043 -542 519 -1044 1011 -544 1011 -510 547 -1008 513 -1040 1009 -514 509 -1072 1041 -544 1011 -542 1013 -544 1017 -510 515 -1040 1009 -544 1007 -512 541 -1004 1527 -19000 513 -4202 99 -1444 853 -672 901 -676 867 -666 407 -1158 923 -618 423 -1114 451 -1082 451 -1082 455 -1110 493 -1080 483 -1076 471 -1056 491 -1082 483 -1042 1007 -546 511 -1006 509 -1072 1009 -576 483 -1076 979 -578 977 -544 1007 -542 1007 -546 1007 -546 477 -1072 517 -1076 977 -578 483 -1042 517 -1042 513 -1044 513 -1042 975 -550 481 -1078 1039 -550 477 -1072 1005 -540 1007 -546 513 -1044 513 -1006 1005 -550 507 -1080 1007 -548 1007 -546 1007 -548 1007 -546 511 -1038 1007 -546 1005 -514 509 -1006 1531 -18994 1447 -1714 323 -1204 399 -1134 407 -1132 925 -652 925 -618 425 -1150 917 -618\nRAW_Data: 451 -1112 449 -1074 483 -1046 483 -1070 477 -1082 509 -1042 509 -1074 479 -1074 477 -1078 477 -1042 1007 -554 483 -1046 507 -1086 983 -548 517 -1064 995 -548 1019 -546 981 -544 1009 -544 1009 -514 511 -1074 513 -1076 979 -578 483 -1042 515 -1042 513 -1042 511 -1040 1005 -516 509 -1076 1009 -548 513 -1040 1007 -574 979 -546 513 -1042 515 -1038 1009 -514 507 -1074 1009 -548 1007 -546 1011 -548 1011 -546 513 -1046 1009 -516 1007 -520 507 -1042 1529 -18994 1457 -1686 321 -1212 385 -1150 389 -1142 937 -638 937 -630 437 -1088 961 -616 951 -584 451 -1112 451 -1078 449 -1078 477 -1120 449 -1110 447 -1080 481 -1072 515 -1046 483 -1046 999 -556 519 -1016 503 -1084 995 -550 527 -1048 981 -580 983 -546 1007 -546 1009 -548 1007 -516 515 -1076 477 -1072 1007 -548 513 -1042 513 -1038 515 -1040 513 -1040 975 -552 481 -32700 1607 -1490 513 -1044 513 -1046 483 -1050 1015 -554 1027 -550 1011 -532 1001 -584 989 -548 487 -1044 515 -1042 1007 -540 1005 -550 513 -1076 975 -580 483 -1044 515 -1044 1013 -548 1009 -518 513 -1048 979 -590 985 -586 475 -1076 1009 -550 991 -548 1015 -546 485 -1042 1013 -516 1009 -586 1007 -548 513 -1042 1011 -548 513 -1042 479 -1072 479 -1038 509 -1040 1005 -586 479 -1072 1009 -546 515 -1040 1009 -548 513 -1040 479 -1072 475 -1036 1009 -586 479 -1078 513 -1042 513 -1044 1011 -550 479 -1076 973 -550 511 -1042 1005 -494 1513 -19020 1555 -1504 509 -1048 507 -1052 473 -1058 1001 -578 1001 -546 1003 -574 511 -1024 527 -1048 487 -1044 513 -1044 1009 -518 1009 -590 479 -1076 1007 -550 477 -1070 513 -1040 1007 -548 1005 -514 511 -1040 1005 -588 975 -586 479 -1072 1007 -548 1007 -548 973 -550 511 -1042 1011 -526 1017 -572 1001 -538 517 -1050 1007 -558 501 -1052 487 -1048 513 -1044 479 -1044 1007 -590 475 -1074 1007 -548 513 -1040 1009 -546 513 -1040 481 -1038 511 -1042 1009 -560 511 -1074 477 -1074 477 -1074 1007 -550 477 -1070 973 -542 509 -1040 1005 -526 1511 -19024 1533 -1542 453 -1086 473 -1050 511 -1042 995 -582 993 -582 991 -548 491 -1080 983 -546 515 -1040 513 -1042 975 -554 1011 -586 487 -1048 1015 -544 513 -1030 525 -1046 981 -546 1009 -546 511 -1042 971 -584 1007 -574 485 -1076 977 -578 979 -546 1009 -548 511 -1042 1007 -516 1005 -588 1009 -550 479 -1074 1011 -548 481 -1072 479 -1072 475 -1040 509 -1044 1011 -558 509 -1074 971 -584 477 -1070 1007 -550 477 -1076 477 -1044 515 -1046 979 -590 491 -1050 519 -1044 501 -1054 1021 -548 483 -1044 1011 -548 475 -1040 1003 -520 1543 -18994 1573 -1468 515 -1052 521 -1018 523 -1008 1025 -580 997 -546 527 -1046 1015 -542 519 -1010 513 -1042 513 -1040\nRAW_Data: 1007 -514 1039 -550 511 -1040 1009 -544 513 -1046 515 -1040 1009 -546 1005 -540 511 -1006 1041 -550 1013 -546 513 -1040 1011 -546 1009 -544 1007 -544 513 -1038 1009 -512 1037 -548 1009 -546 515 -1040 1041 -512 513 -1046 515 -1040 511 -1038 511 -1006 1037 -550 511 -1040 1041 -542 485 -1042 1039 -508 515 -1042 515 -1038 511 -1004 1037 -550 515 -1040 517 -1040 517 -1040 1011 -544 515 -1040 1007 -546 509 -1008 1009 -518 1543 -18992 1535 -1542 483 -1052 487 -1050 521 -1046 991 -578 995 -546 527 -1046 1015 -546 1015 -544 483 -1042 513 -1040 1009 -514 1007 -586 511 -1040 1007 -548 513 -1042 513 -1044 1011 -546 1009 -514 511 -1038 1009 -558 1007 -552 513 -1072 1005 -548 1005 -548 1005 -514 507 -1038 1035 -506 1003 -584 1005 -574 481 -1076 1009 -544 485 -1042 513 -1040 513 -1040 511 -1004 1039 -550 513 -1040 1011 -544 515 -1042 1009 -544 515 -1042 513 -1042 479 -1042 1011 -558 511 -1076 477 -1074 513 -1038 1007 -546 511 -1040 1005 -514 507 -1040 1001 -506\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/nice_one_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524\nRAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072\nRAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562\nRAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520\nRAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66\nRAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034\nRAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Phoenix_V2\nBit: 52\nKey: 00 0F 0F BD 7E 7F 10 AE\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699\nRAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419\nRAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395\nRAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/power_smart.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Power Smart\nBit: 64\nKey: FD C1 36 AC AA 3E C9 52\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/power_smart_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219\nRAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/princeton.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Princeton\nBit: 24\nKey: 00 00 00 00 00 95 D5 D4\nTE: 400\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/princeton_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1711 -32700 621 -1600 1623 -564 1639 -552 1637 -520 1701 -514 1665 -516 579 -1556 607 -1562 571 -1570 1697 -484 605 -1544 1701 -500 611 -1544 1697 -528 1675 -518 1705 -492 589 -1580 593 -1536 609 -1562 583 -1574 595 -1542 603 -1568 1699 -490 1693 -496 613 -16342 623 -1538 1693 -520 1675 -486 1721 -492 1709 -520 1701 -486 579 -1588 573 -1578 589 -1568 1695 -482 605 -1582 1703 -492 589 -1578 1675 -518 1707 -484 1703 -500 609 -1576 571 -1574 611 -1532 627 -1538 603 -1546 607 -1548 1729 -474 1719 -490 613 -16368 591 -1570 1689 -504 1699 -484 1711 -506 1715 -486 1707 -488 619 -1576 583 -1540 611 -1562 1711 -486 615 -1546 1705 -490 621 -1542 1705 -506 1713 -486 1713 -486 629 -1540 609 -1564 589 -1574 599 -1540 607 -1568 611 -1546 1707 -492 1711 -480 609 -16394 597 -1566 1701 -494 1693 -492 1705 -520 1705 -474 1727 -502 585 -1574 599 -1570 575 -1566 1699 -520 593 -1570 1703 -480 605 -1572 1705 -490 1717 -482 1735 -486 603 -1566 611 -1532 615 -1562 587 -1578 585 -1574 579 -1586 1677 -512 1705 -490 621 -16376 603 -1556 1677 -540 1667 -516 1699 -506 1717 -486 1701 -510 591 -1576 579 -1562 625 -1538 1727 -492 605 -1566 1703 -484 605 -1570 1703 -492 1713 -482 1733 -482 603 -1570 611 -1548 611 -1564 581 -1558 613 -1548 603 -1574 1701 -484 1701 -520 581 -16396 603 -1568 1693 -514 1687 -486 1711 -486 1721 -492 1707 -520 577 -1568 611 -1564 581 -1580 1679 -516 603 -1544 1707 -512 609 -1564 1707 -482 1725 -476 1717 -520 587 -1548 613 -1552 613 -1564 581 -1580 581 -1578 581 -1554 1725 -488 1703 -520 581 -16392 615 -1588 1663 -514 1705 -514 1689 -518 1713 -484 1709 -492 613 -1576 593 -1540 607 -1578 1703 -492 621 -1542 1715 -502 587 -1572 1693 -520 1713 -482 1709 -510 591 -1570 605 -1568 611 -1562 581 -1580 581 -1580 585 -1546 1741 -484 1715 -500 585 -16404 605 -1566 1765 -516 349 -32700 633 -1566 1667 -512 1659 -550 1677 -490 1695 -492 1705 -510 575 -1576 577 -1566 611 -1536 1709 -492 603 -1534 1695 -516 599 -1546 1697 -502 1679 -520 1677 -492 613 -1570 595 -1544 603 -1538 609 -1568 1701 -462 1721 -488 613 -1538 625 -1548 599 -16338 625 -1544 1697 -506 1669 -522 1681 -488 1707 -484 1701 -504 577 -1586 571 -1570 605 -1554 1677 -512 575 -1578 1671 -520 587 -1578 1675 -506 1695 -500 1679 -518 577 -1570 577 -1568 615 -1548 577 -1564 1709 -490 1679 -512 605 -1554 589 -1574 597 -16376 605 -1532 1695 -508 1691 -486 1703 -480 1693 -500 1711 -484 585 -1572 609 -1556 587 -1568 1693 -486 583 -1598 1669 -488 603 -1572 1699 -482 1689 -520 1677 -522 585 -1576 585 -1542 609 -1560 587 -1570 1691 -518 1675 -490 605 -1568 577 -1562 591 -16398 615 -1568 1663 -518\nRAW_Data: 1673 -492 1709 -480 1693 -512 1689 -504 579 -1592 589 -1542 587 -1578 1699 -484 605 -1544 1701 -518 577 -1568 1699 -484 1677 -520 1675 -518 579 -1598 563 -1576 603 -1568 575 -1566 1703 -492 1707 -480 611 -1556 587 -1572 601 -16374 619 -1566 1667 -516 1675 -524 1675 -516 1667 -514 1691 -506 581 -1580 583 -1560 579 -1588 1679 -512 573 -1580 1677 -520 579 -1572 1705 -494 1681 -514 1703 -478 593 -1576 603 -1574 575 -1576 577 -1568 1703 -492 1691 -492 613 -1570 597 -1542 607 -16402 563 -1620 1597 -592 1605 -586 1609 -606 1619 -544 1631 -588 515 -1620 559 -1600 569 -1564 1671 -526 585 -1580 1675 -518 579 -1564 1711 -486 1709 -494 1695 -514 575 -1562 589 -1576 603 -1544 607 -1566 1703 -494 1691 -490 611 -1572 595 -1544 603 -16400 615 -1536 1707 -494 1679 -514 1673 -510 1715 -486 1707 -494 587 -1582 587 -1574 579 -1546 1705 -522 577 -1580 1699 -494 603 -1534 1729 -482 1693 -502 1699 -510 577 -1586 587 -1570 601 -1544 609 -1578 1703 -492 1711 -480 609 -1560 591 -1576 603 -16408 581 -1588 1701 -516 1703 -514 1689 -32700 641 -1534 1703 -492 1693 -486 1673 -516 1689 -504 1669 -520 587 -1572 573 -1548 611 -1538 1715 -486 581 -1576 1663 -520 587 -1560 1699 -484 1703 -488 1699 -518 575 -1576 575 -1570 1663 -520 1677 -522 587 -1576 585 -1540 609 -1556 587 -1574 563 -16370 595 -1568 1689 -506 1669 -490 1677 -512 1699 -510 1681 -508 581 -1560 585 -1580 583 -1560 1665 -516 607 -1538 1703 -486 607 -1576 1663 -504 1703 -482 1695 -500 587 -1574 599 -1544 1697 -518 1671 -490 587 -1578 589 -1572 577 -1564 589 -1574 599 -16376 571 -1566 1693 -518 1673 -518 1649 -520 1679 -516 1665 -514 567 -1574 605 -1544 609 -1570 1669 -518 581 -1562 1669 -518 587 -1574 1667 -516 1691 -518 1671 -490 601 -1568 573 -1584 1673 -518 1673 -518 579 -1558 611 -1550 587 -1580 585 -1572 579 -16380 617 -1566 1649 -542 1669 -490 1679 -512 1703 -510 1655 -518 579 -1564 615 -1562 587 -1562 1677 -520 587 -1574 1667 -506 589 -1568 1691 -520 1679 -492 1675 -514 611 -1554 589 -1572 1687 -506 1675 -520 585 -1574 579 -1578 585 -1568 601 -1540 605 -16408 585 -1564 1663 -530 1663 -518 1667 -514 1689 -504 1699 -482 605 -1582 581 -1570 563 -1574 1693 -504 577 -1588 1659 -518 579 -1598 1655 -518 1707 -482 1701 -480 605 -1582 583 -1572 1687 -518 1671 -490 587 -1574 605 -1582 581 -1570 561 -1576 601 -16376 615 -1560 1673 -518 1663 -530 1671 -518 1671 -500 1697 -514 573 -1584 587 -1570 597 -1540 1699 -504 577 -1586 1693 -486 579 -1600 1681 -486 1711 -518 1677 -486 587 -1572 577 -1590 1677 -518 1673 -498 605 -1566 607 -1554 589 -1572 601 -1544 605 -16396 615 -1544 1673 -518 1667 -530 1673 -518\nRAW_Data: 1669 -520 1677 -488 621 -1576 581 -1542 615 -1568 1689 -486 583 -1596 1669 -520 571 -1572 1703 -512 1697 -494 1691 -528 583 -1570 599 -1574 1703 -484 1703 -512 573 -1588 585 -1572 601 -1574 577 -1568 613 -16404 595 -1574 1697 -504 1711 -506 1733 -510 1717 -504 1371 -32700 613 -1572 1653 -536 1673 -488 1679 -514 1669 -508 1681 -518 579 -1580 575 -1570 577 -1572 1667 -518 581 -1564 1671 -490 587 -1580 1677 -516 1663 -514 1657 -508 1673 -516 1695 -500 587 -1572 565 -1574 603 -1574 575 -1570 575 -1570 579 -1580 575 -16372 585 -1566 1673 -520 1671 -508 1677 -514 1665 -512 1655 -512 577 -1568 585 -1572 581 -1582 1667 -522 587 -1548 1669 -500 613 -1544 1683 -520 1679 -478 1703 -480 1685 -520 1673 -508 579 -1562 577 -1588 571 -1566 605 -1558 589 -1576 563 -1576 601 -16382 581 -1566 1671 -520 1681 -492 1679 -512 1663 -506 1679 -518 575 -1570 577 -1568 581 -1576 1667 -518 587 -1578 1643 -530 571 -1566 1667 -510 1687 -518 1665 -520 1669 -492 1691 -486 581 -1600 561 -1570 605 -1574 575 -1568 575 -1570 579 -1580 577 -16412 581 -1588 1665 -516 1665 -510 1653 -518 1669 -504 1667 -518 573 -1572 579 -1594 561 -1572 1693 -486 583 -1578 1667 -502 577 -1584 1691 -484 1699 -486 1693 -486 1673 -500 1691 -514 577 -1562 591 -1576 567 -1576 605 -1544 609 -1570 577 -1578 577 -16392 617 -1564 1673 -486 1681 -490 1681 -514 1671 -504 1683 -520 575 -1572 577 -1568 579 -1576 1671 -520 583 -1546 1669 -530 573 -1572 1673 -504 1679 -518 1671 -508 1667 -508 1673 -516 575 -1562 591 -1568 603 -1542 607 -1546 609 -1574 577 -1568 577 -16416 583 -1582 1667 -516 1637 -542 1651 -510 1665 -518 1671 -524 585 -1544 585 -1572 577 -1588 1675 -484 611 -1574 1667 -506 581 -1562 1691 -486 1671 -516 1691 -488 1673 -522 1679 -494 571 -1566 607 -1556 591 -1568 603 -1572 577 -1576 577 -1572 577 -16396 617 -1564 1681 -492 1681 -514 1673 -500 1675 -514 1667 -504 577 -1588 589 -1546 603 -1572 1665 -514 567 -1576 1697 -476 613 -1564 1669 -520 1679 -482 1695 -512 1655 -520 1673 -520 581 -1562 579 -1590 571 -1568 607 -1554 587 -1570 567 -1574 607 -16402 593 -1572 1665 -504\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/revers_rb2.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Revers_RB2\nBit: 64\nKey: FF FF FF FF 39 F9 0A 00\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/revers_rb2_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 215 -498 257 -246 243 -276 215 -280 215 -278 247 -250 507 -258 247 -504 479 -274 253 -254 219 -290 213 -530 485 -482 481 -308 217 -254 253 -252 241 -234 257 -250 249 -278 219 -252 247 -250 279 -544 235 -268 259 -226 255 -250 249 -280 219 -252 249 -250 215 -280 247 -252 249 -250 249 -280 219 -252 249 -250 215 -280 279 -218 251 -250 249 -280 219 -252 251 -248 281 -220 251 -250 249 -248 247 -278 219 -252 249 -250 279 -220 251 -250 247 -280 219 -254 249 -248 217 -280 277 -220 507 -258 247 -504 225 -278 245 -246 469 -292 213 -530 227 -276 215 -274 247 -248 249 -248 247 -248 501 -258 249 -502 479 -272 251 -254 255 -254 249 -476 481 -514 481 -274 253 -254 255 -250 239 -262 225 -254 249 -248 217 -278 247 -250 251 -572 267 -238 263 -224 251 -280 219 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -248 249 -248 247 -250 251 -250 249 -280 219 -252 251 -248 217 -280 247 -250 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 281 -220 251 -250 247 -280 479 -228 279 -500 255 -216 277 -214 531 -228 279 -500 257 -216 275 -214 275 -216 277 -278 217 -250 505 -260 217 -500 509 -258 243 -246 273 -216 275 -500 479 -546 479 -238 253 -256 253 -250 243 -268 221 -250 247 -250 251 -250 247 -280 219 -574 265 -268 199 -292 219 -250 279 -220 251 -250 249 -248 249 -278 219 -252 249 -250 279 -220 251 -248 249 -280 219 -252 251 -248 217 -280 247 -250 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 503 -260 217 -532 225 -276 215 -278 503 -238 251 -502 249 -244 241 -278 215 -272 215 -272 277 -214 493 -310 215 -530 483 -232 261 -258 223 -254 249 -538 483 -482 509 -274 217 -290 217 -252 241 -236 259 -250 247 -248 247 -280 217 -252 249 -602 203 -272 229 -256 281 -220 251 -250 249 -280 219 -252 249 -250 279 -222 251 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -250 247 -280 221 -252 249 -250 215 -280 247 -250 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 507 -258 215 -530 227 -278 215 -276 501 -258 215 -530 227 -278 215 -276 215 -276 247 -250 247 -250 501 -260 249 -502 481 -272 251 -254 219 -290 247 -474 481 -514 481 -274 289 -218 253 -252 241 -230 253 -282 217 -252 249 -248 279 -220 251 -574 267 -238 263 -222\nRAW_Data: 251 -280 219 -252 249 -250 215 -280 279 -220 251 -248 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -250 277 -222 251 -250 247 -280 219 -252 249 -250 217 -280 247 -250 251 -250 247 -280 219 -252 249 -250 279 -222 251 -250 247 -250 501 -258 249 -502 257 -218 275 -216 497 -272 287 -464 219 -274 275 -216 273 -216 271 -278 213 -274 499 -238 253 -498 479 -262 261 -228 257 -252 249 -506 481 -512 507 -236 291 -218 253 -290 211 -270 223 -250 247 -250 251 -250 247 -248 249 -568 267 -272 201 -292 221 -248 217 -280 247 -250 251 -250 247 -280 221 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -222 251 -248 249 -280 219 -252 249 -250 217 -278 247 -252 251 -250 247 -280 219 -252 507 -260 217 -502 269 -252 253 -256 495 -236 265 -482 259 -250 247 -278 217 -252 249 -248 215 -280 503 -258 217 -532 515 -238 253 -254 255 -254 213 -504 513 -514 477 -274 215 -290 217 -256 247 -234 253 -252 249 -250 249 -280 219 -252 249 -602 203 -272 229 -256 281 -220 251 -250 249 -280 219 -252 249 -250 247 -248 249 -250 251 -250 249 -280 219 -250 249 -250 279 -222 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 281 -220 249 -250 249 -280 219 -252 249 -250 215 -280 503 -260 217 -530 227 -280 213 -278 501 -258 217 -496 257 -276 215 -278 215 -278 245 -250 251 -250 501 -260 249 -502 479 -272 251 -256 217 -290 247 -474 483 -514 481 -274 251 -256 253 -252 241 -230 253 -282 217 -252 249 -248 217 -278 247 -576 269 -240 265 -222 251 -248 249 -278 219 -252 249 -250 279 -220 249 -250 249 -280 219 -252 249 -250 217 -278 279 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 535 -226 251 -502 225 -246 275 -246 467 -290 245 -504 225 -276 245 -248 247 -246 247 -248 245 -280 475 -228 279 -500 479 -272 251 -256 253 -256 247 -470 483 -514 513 -238 289 -220 253 -288 209 -270 223 -250 215 -280 245 -250 251 -248 249 -570 267 -238 229 -288 221 -254 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -222 251 -250 247 -280 219 -252 249 -250 217 -278 247 -252 251 -250 247 -280 219 -252 249 -250 279 -222 251 -250 247 -248 249 -280 217 -252\nRAW_Data: 249 -250 279 -220 251 -250 249 -278 221 -252 505 -228 245 -530 257 -214 275 -246 505 -226 247 -532 257 -214 275 -216 275 -246 249 -248 217 -280 503 -258 217 -498 543 -236 251 -254 255 -254 251 -474 513 -516 477 -238 253 -292 217 -254 245 -234 253 -252 251 -248 249 -278 219 -252 251 -602 203 -272 229 -258 279 -222 251 -250 247 -250 247 -280 217 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -248 249 -280 219 -252 249 -250 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 503 -258 217 -564 195 -278 215 -276 503 -258 215 -500 271 -288 215 -256 253 -250 207 -262 251 -252 505 -256 247 -504 481 -306 215 -254 253 -256 213 -534 487 -480 511 -274 215 -292 253 -218 247 -236 257 -250 247 -278 219 -250 249 -250 279 -544 235 -268 259 -226 253 -250 249 -280 219 -252 249 -250 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 277 -220 251 -250 247 -280 221 -252 249 -250 279 -220 251 -250 471 -290 247 -502 225 -278 245 -246 469 -306 251 -466 251 -242 245 -272 245 -246 241 -246 241 -278 497 -238 253 -500 483 -264 229 -258 283 -220 253 -508 481 -508 477 -308 253 -218 253 -254 243 -264 225 -256 247 -250 279 -220 249 -250 249 -570 269 -236 231 -286 223 -252 251 -248 217 -278 247 -252 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -250 279 -220 251 -250 247 -280 221 -252 249 -250 215 -280 247 -250 251 -250 247 -280 219 -254 249 -250 247 -248 277 -220 251 -250 247 -280 479 -228 279 -500 257 -216 275 -214 531 -228 279 -500 257 -216 275 -216 275 -246 249 -250 247 -250 503 -260 215 -500 541 -238 249 -256 253 -256 215 -508 481 -548 479 -238 253 -292 217 -254 245 -232 221 -280 247 -250 251 -250 247 -280 219 -574 263 -268 199 -292 219 -250 279 -220 253 -250 247 -280 221 -252 249 -248 249 -248 277 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 247 -280 219 -252 249 -250 505 -260 217 -498 257 -276 213 -278 503 -260 215 -498 257 -246 243 -276 215 -280 215 -278 247 -252 505 -256 247 -506 479 -274 253 -254 219 -290 213 -530 485 -482 509 -274\nRAW_Data: 215 -292 217 -252 243 -236 259 -250 249 -280 219 -250 249 -250 215 -632 205 -272 261 -226 255 -250 249 -248 247 -280 217 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -218 507 -258 247 -504 225 -278 215 -274 499 -258 215 -530 225 -280 213 -278 245 -250 249 -248 247 -248 501 -260 247 -502 479 -272 251 -256 253 -254 249 -476 481 -514 483 -274 253 -254 255 -252 239 -260 225 -254 249 -248 215 -280 247 -250 251 -572 267 -238 263 -224 249 -280 219 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -280 277 -220 251 -248 249 -280 219 -252 251 -248 247 -248 279 -220 251 -250 247 -282 219 -250 249 -250 279 -222 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 479 -228 277 -500 257 -214 275 -216 529 -228 279 -500 257 -216 275 -216 275 -216 277 -278 217 -250 507 -258 219 -500 511 -274 253 -254 255 -218 245 -500 483 -544 479 -226 273 -244 243 -276 213 -278 213 -278 245 -250 251 -248 247 -280 219 -572 265 -268 199 -292 219 -250 279 -222 251 -250 247 -248 249 -278 221 -250 249 -250 279 -220 251 -250 249 -280 219 -252 251 -248 247 -248 279 -220 251 -248 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 247 -280 219 -254 249 -248 281 -220 251 -250 247 -250 247 -280 219 -250 505 -260 217 -500 257 -276 215 -278 503 -236 249 -504 247 -246 241 -278 213 -274 243 -244 275 -214 493 -310 215 -516 477 -272 235 -262 227 -256 249 -504 509 -482 511 -274 215 -292 217 -252 279 -202 259 -250 249 -248 247 -278 219 -250 249 -604 205 -272 229 -254 281 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -222 249 -250 249 -280 219 -252 249 -250 247 -248 277 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 247 -280 221 -252 249 -248 279 -220 507 -256 215 -532 225 -280 213 -276 501 -258 217 -530 227 -280 213 -276 215 -276 247 -248 249 -250 535 -226 249 -502 479 -258 243 -276 213 -276 245 -502 481 -514 483 -272 253 -254 255 -250 239 -230 253 -280 219 -250 249 -248 279 -220 253 -572 267 -238 263 -224 249 -282 219 -250 249 -250 217 -280 277 -220 251 -250 247 -280 221 -252 249 -248 281 -220 251 -250 249 -248 247 -280 219 -250\nRAW_Data: 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 277 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 501 -260 249 -502 257 -216 275 -216 497 -272 287 -464 243 -290 253 -212 271 -226 251 -280 219 -250 505 -226 247 -530 477 -274 251 -256 253 -218 283 -472 483 -514 515 -236 291 -218 253 -288 209 -268 223 -250 247 -250 251 -250 247 -248 247 -570 267 -270 201 -294 219 -250 247 -248 279 -220 249 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 249 -280 219 -252 249 -250 277 -222 251 -250 247 -250 247 -278 219 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220 249 -250 247 -280 221 -252 505 -260 219 -500 271 -252 253 -256 487 -234 267 -488 257 -250 247 -248 245 -278 219 -252 249 -248 505 -258 217 -532 515 -238 253 -254 255 -254 213 -506 511 -514 477 -274 215 -290 219 -290 211 -234 285 -220 251 -250 247 -280 219 -252 249 -602 239 -238 229 -256 281 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220 251 -250 247 -280 219 -252 249 -250 247 -248 247 -252 251 -248 249 -280 219 -252 249 -250 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -248 249 -280 219 -252 251 -248 249 -248 503 -258 217 -532 227 -278 215 -276 501 -260 215 -498 257 -276 213 -278 215 -278 245 -252 249 -250 535 -226 249 -504 479 -272 251 -254 217 -292 247 -474 483 -514 481 -274 253 -256 253 -252 239 -230 253 -280 219 -252 247 -250 215 -280 277 -544 269 -240 265 -224 251 -248 247 -278 219 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 277 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 277 -222 251 -250 535 -228 249 -502 225 -246 275 -246 469 -306 251 -464 243 -290 253 -212 273 -226 251 -248 247 -280 475 -228 279 -500 477 -274 251 -254 255 -254 247 -472 481 -514 515 -226 273 -214 271 -246 241 -276 215 -278 215 -278 245 -252 249 -250 249 -570 267 -236 231 -288 221 -254 249 -250 277 -222 251 -248 249 -248 247 -280 219 -252 249 -248 281 -220 251 -250 249 -280 219 -252 249 -250 215 -280 247 -250 251 -250 247 -280 221 -252 249 -248 281 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 477 -228 279 -500 255 -216 275 -246 505 -228 247 -530 255 -216 275 -216 275 -246\nRAW_Data: 247 -250 249 -248 503 -260 217 -498 541 -238 249 -256 253 -256 251 -474 513 -518 477 -238 253 -292 217 -254 243 -234 283 -222 249 -250 249 -248 247 -278 219 -606 239 -238 231 -256 249 -248 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -250 279 -220 251 -250 249 -280 219 -250 249 -250 249 -248 247 -250 251 -250 247 -280 219 -252 251 -248 281 -220 251 -250 249 -248 247 -280 219 -252 249 -248 505 -258 217 -498 257 -276 215 -278 501 -258 217 -498 257 -276 215 -278 215 -278 215 -278 247 -250 505 -260 245 -504 481 -288 215 -274 215 -276 213 -530 515 -478 511 -274 215 -256 289 -220 243 -234 255 -250 245 -280 217 -252 249 -248 279 -544 237 -268 259 -224 255 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -248 249 -278 219 -252 249 -248 279 -222 251 -250 247 -280 219 -254 249 -248 247 -248 279 -220 251 -250 249 -278 221 -252 249 -248 279 -222 251 -250 469 -292 245 -504 235 -288 253 -218 489 -266 269 -500 195 -280 215 -276 247 -248 249 -250 247 -280 479 -228 277 -502 477 -272 253 -254 253 -256 247 -474 481 -516 481 -276 253 -256 253 -214 273 -260 223 -254 249 -248 279 -220 251 -250 249 -570 267 -236 231 -288 221 -254 249 -250 215 -280 247 -252 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -222 251 -250 247 -280 219 -252 249 -250 217 -280 247 -250 251 -250 249 -278 479 -228 279 -500 257 -214 275 -216 531 -228 279 -500 257 -214 275 -216 275 -248 247 -250 249 -248 503 -258 217 -500 545 -238 251 -256 253 -218 247 -502 481 -546 481 -238 289 -256 217 -290 213 -234 223 -280 247 -250 251 -250 247 -280 219 -574 263 -268 199 -292 219 -250 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 249 -280 219 -252 249 -248 281 -220 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -222 251 -250 247 -280 219 -252 249 -250 503 -260 217 -498 257 -276 215 -278 503 -258 217 -500 269 -254 253 -254 255 -250 205 -264 281 -220 507 -256 247 -504 481 -272 253 -256 217 -290 213 -532 487 -480 511 -274 215 -292 217 -252 243 -236 257 -250 247 -280 219 -250 249 -250 215 -632 205 -272 261 -226 255 -250 249 -248 249 -278 219 -252\nRAW_Data: 249 -248 279 -222 251 -250 247 -280 219 -252 249 -250 247 -248 279 -220 249 -252 247 -280 219 -252 249 -250 279 -222 251 -250 247 -248 249 -278 221 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -220 505 -258 245 -504 227 -278 213 -274 501 -258 215 -530 227 -280 213 -276 247 -248 249 -248 247 -248 501 -258 249 -502 479 -272 253 -254 253 -256 249 -474 483 -514 483 -274 255 -254 255 -214 273 -260 223 -254 249 -248 215 -280 247 -252 249 -574 267 -238 263 -224 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 253 -250 247 -280 219 -252 249 -250 247 -248 279 -220 251 -250 247 -280 219 -252 249 -250 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -250 249 -280 477 -228 279 -500 257 -216 275 -214 531 -226 279 -500 225 -244 277 -214 277 -216 277 -278 219 -250 505 -260 217 -500 513 -274 251 -256 253 -218 247 -500 481 -546 479 -238 289 -218 255 -286 211 -270 223 -250 247 -250 249 -250 249 -280 219 -572 265 -268 199 -292 221 -248 281 -220 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 253 -250 247 -248 247 -280 219 -252 505 -260 217 -532 225 -276 215 -278 503 -236 251 -504 247 -254 251 -280 201 -260 251 -248 247 -252 505 -258 215 -530 513 -238 253 -254 255 -254 213 -504 513 -514 477 -274 215 -290 219 -252 281 -204 259 -252 247 -248 247 -278 219 -250 249 -604 203 -272 229 -256 281 -220 251 -250 247 -280 219 -252 251 -248 281 -220 251 -250 247 -250 247 -280 217 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 277 -220 251 -250 249 -280 219 -252 249 -250 217 -278 247 -252 249 -250 249 -280 219 -252 249 -250 279 -220 507 -258 215 -530 227 -278 215 -276 499 -260 215 -530 225 -280 213 -276 215 -278 247 -248 249 -248 503 -260 249 -502 479 -258 243 -274 215 -274 247 -504 481 -512 477 -274 251 -256 253 -252 241 -230 253 -280 219 -252 249 -248 279 -220 251 -574 267 -238 263 -224 249 -282 219 -250 249 -250 217 -280 245 -252 251 -250 247 -280 219 -254 249 -248 281 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -248 217 -280 277 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220\nRAW_Data: 249 -250 535 -228 249 -502 257 -218 275 -216 497 -272 285 -466 243 -290 253 -212 271 -226 251 -280 219 -250 505 -228 245 -530 479 -272 253 -254 253 -220 283 -470 481 -516 515 -236 255 -254 253 -288 209 -268 223 -248 217 -278 247 -250 251 -250 247 -570 267 -238 231 -286 221 -254 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 247 -252 251 -248 249 -280 219 -252 249 -250 279 -222 251 -250 247 -248 249 -278 219 -252 249 -248 281 -220 251 -248 249 -280 219 -252 249 -250 249 -248 247 -252 249 -250 249 -280 219 -252 505 -228 247 -530 227 -246 273 -246 505 -228 245 -530 255 -216 275 -214 275 -248 247 -250 247 -250 503 -258 217 -564 483 -238 251 -256 253 -254 215 -504 515 -514 475 -258 213 -274 243 -276 213 -276 245 -248 247 -248 247 -280 219 -250 249 -604 203 -272 229 -256 281 -220 251 -250 247 -280 221 -252 249 -250 215 -280 277 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -218 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 253 -250 247 -280 219 -252 249 -250 217 -280 503 -258 217 -532 225 -280 213 -278 501 -258 217 -496 257 -276 215 -278 215 -278 247 -250 249 -250 469 -292 245 -504 481 -272 251 -254 217 -292 247 -474 485 -512 481 -274 253 -254 255 -252 239 -230 255 -280 219 -250 249 -248 217 -278 279 -544 269 -238 265 -224 251 -248 249 -278 219 -252 249 -250 279 -220 251 -250 247 -280 219 -252 249 -250 217 -280 247 -252 249 -250 249 -280 219 -252 249 -250 247 -248 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 535 -228 249 -504 237 -250 291 -218 491 -264 265 -498 193 -280 247 -248 249 -248 249 -248 247 -278 477 -230 277 -500 479 -272 215 -292 253 -256 247 -472 481 -514 515 -236 291 -218 255 -252 239 -262 225 -252 249 -250 279 -220 249 -250 249 -570 267 -238 231 -286 221 -254 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 253 -250 247 -280 219 -252 249 -250 247 -248 279 -220 251 -248 249 -280 221 -252 249 -248 247 -248 279 -220 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 477 -228 277 -500 257 -216 275 -246 505 -226 247 -530 257 -214 275 -216 275 -246 249 -250 247 -250 503 -258 217 -498 541 -228 245 -244 243 -278 247 -500 511 -516 479 -238 253 -292 217 -252 209 -266 281 -222\nRAW_Data: 249 -250 249 -248 247 -278 219 -606 239 -238 231 -258 249 -248 247 -250 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 279 -220 251 -250 249 -278 221 -250 249 -250 279 -222 251 -250 247 -248 249 -278 219 -252 249 -250 503 -260 217 -498 257 -276 215 -278 501 -258 217 -498 257 -276 215 -278 215 -278 215 -278 247 -250 505 -258 245 -506 479 -308 215 -254 219 -290 213 -534 487 -482 479 -288 213 -274 243 -246 275 -214 275 -216 277 -278 217 -250 247 -248 279 -546 235 -268 259 -226 253 -250 249 -280 219 -252 249 -250 247 -248 279 -220 251 -250 249 -280 219 -252 249 -248 281 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -250 249 -280 219 -252 249 -250 247 -248 279 -218 251 -250 249 -280 219 -252 249 -250 279 -220 251 -250 471 -292 245 -502 227 -276 245 -248 467 -308 215 -504 243 -290 217 -244 265 -228 253 -250 247 -280 477 -228 279 -500 479 -272 251 -256 253 -254 249 -472 483 -514 479 -256 275 -244 243 -246 243 -276 215 -278 215 -276 247 -250 249 -250 249 -570 267 -236 231 -286 221 -254 249 -250 217 -278 279 -220 251 -250 247 -280 221 -252 249 -248 247 -248 279 -220 251 -250 247 -280 219 -254 249 -248 279 -222 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220 249 -250 249 -280 477 -228 277 -502 255 -216 275 -216 531 -228 279 -500 257 -216 275 -214 277 -246 249 -250 247 -250 503 -260 215 -500 541 -226 245 -246 243 -246 275 -500 477 -548 477 -236 255 -290 217 -254 245 -234 221 -282 247 -250 249 -250 249 -280 219 -572 265 -266 199 -292 221 -248 281 -220 251 -250 249 -278 221 -252 249 -250 247 -248 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -250 279 -220 249 -250 249 -248 249 -278 219 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 503 -260 217 -498 257 -276 215 -278 503 -258 217 -498 257 -244 243 -278 215 -278 217 -278 245 -252 505 -258 247 -506 479 -272 255 -254 217 -292 211 -532 485 -480 509 -274 217 -290 219 -252 243 -236 259 -250 247 -280 219 -250 249 -250 217 -630 205 -272 261 -226 255 -252 247 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 279 -220 251 -250 247 -280 219 -252 249 -250 279 -220 251 -250\nRAW_Data: 249 -248 247 -280 219 -252 249 -248 279 -222 251 -250 247 -250 247 -280 217 -252 249 -250 279 -220 507 -258 247 -504 225 -278 213 -274 501 -258 215 -530 225 -280 213 -276 247 -248 249 -248 249 -246 501 -260 249 -502 479 -272 251 -254 255 -254 249 -476 481 -514 483 -274 253 -256 253 -250 239 -230 251 -280 219 -252 247 -250 279 -220 251 -572 269 -236 265 -222 251 -280 219 -252 249 -250 277 -222 251 -250 247 -250 247 -280 217 -252 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 279 -220 251 -250 247 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 251 -252 247 -248 503 -258 249 -502 257 -216 277 -214 531 -228 279 -502 237 -254 253 -256 253 -216 271 -260 223 -254 505 -260 217 -500 513 -274 251 -256 253 -218 247 -498 483 -546 479 -238 289 -220 253 -288 209 -268 223 -250 247 -252 249 -250 249 -278 219 -574 265 -268 199 -292 219 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -222 251 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -250 249 -278 221 -252 249 -250 215 -280 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 505 -258 219 -498 257 -276 215 -278 503 -238 249 -504 245 -254 253 -280 203 -260 219 -280 215 -278 503 -260 215 -532 515 -238 253 -254 255 -254 213 -506 511 -516 477 -272 215 -292 217 -254 279 -204 261 -250 249 -246 247 -278 219 -252 249 -602 205 -272 229 -256 281 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 281 -220 249 -250 249 -248 247 -280 219 -252 249 -250 279 -220 251 -250 249 -280 219 -250 249 -250 217 -280 247 -252 249 -250 249 -280 219 -252 249 -248 281 -220 507 -258 215 -530 227 -280 213 -276 499 -258 215 -532 225 -280 213 -276 215 -276 247 -250 249 -248 503 -258 251 -502 479 -272 251 -254 219 -290 247 -474 515 -482 481 -274 253 -256 253 -252 239 -230 253 -280 219 -252 247 -250 279 -220 251 -572 269 -236 263 -224 251 -280 219 -252 249 -250 215 -280 279 -220 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -248 247 -280 219 -252 249 -248 279 -220 253 -250 247 -280 219 -252 249 -250 217 -280 247 -250 251 -250 535 -226 251 -502 257 -216 277 -216 497 -272 287 -464 245 -288 253 -212 271 -224 251 -280 219 -250 507 -228 247 -528\nRAW_Data: 477 -274 251 -254 255 -218 283 -472 481 -516 515 -236 255 -254 255 -250 245 -266 223 -250 217 -278 245 -252 249 -250 249 -570 267 -238 229 -288 221 -254 249 -250 279 -220 251 -250 247 -280 221 -252 249 -248 217 -278 279 -220 251 -250 247 -282 219 -252 249 -250 277 -222 251 -250 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 247 -252 251 -250 247 -280 219 -252 507 -228 247 -530 255 -216 275 -246 503 -228 245 -530 257 -214 275 -216 275 -246 249 -248 249 -248 503 -260 217 -532 515 -238 253 -254 253 -256 213 -506 511 -516 477 -272 215 -292 217 -254 245 -234 255 -252 249 -250 249 -278 219 -252 249 -602 205 -272 229 -256 281 -220 251 -250 249 -248 249 -278 219 -252 249 -250 279 -220 249 -250 249 -280 219 -254 249 -248 217 -280 277 -220 251 -250 247 -280 221 -252 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 247 -280 221 -252 249 -248 249 -248 503 -258 217 -532 227 -280 213 -278 499 -258 215 -500 271 -288 217 -254 255 -248 241 -234 259 -250 469 -306 249 -464 501 -280 215 -284 207 -262 251 -508 515 -480 481 -274 253 -254 253 -254 241 -230 253 -282 219 -250 249 -248 217 -278 247 -576 269 -240 265 -222 251 -248 249 -278 219 -252 249 -248 279 -220 251 -250 249 -248 249 -278 219 -252 249 -248 281 -220 249 -250 249 -280 219 -252 249 -250 217 -280 247 -250 251 -250 247 -280 219 -254 249 -248 281 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 535 -228 249 -502 225 -246 275 -248 465 -290 245 -504 225 -276 245 -246 247 -248 247 -246 247 -278 477 -228 279 -500 477 -274 251 -254 255 -254 247 -474 483 -514 515 -238 253 -256 253 -250 239 -262 223 -254 249 -250 279 -220 249 -250 249 -570 267 -238 229 -288 221 -254 249 -250 279 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 217 -278 247 -252 249 -250 249 -280 219 -252 249 -250 217 -278 279 -220 251 -248 249 -280 221 -252 249 -250 279 -220 251 -250 249 -248 247 -280 477 -228 277 -500 257 -216 275 -246 505 -228 245 -530 257 -214 275 -216 275 -246 249 -250 247 -250 503 -258 217 -498 543 -226 243 -246 243 -278 247 -500 479 -546 477 -238 255 -290 217 -252 245 -232 283 -222 249 -250 249 -248 247 -280 217 -608 239 -238 229 -256 219 -280 247 -250 251 -250 247 -280 219 -254 249 -248 281 -220 251 -250\nRAW_Data: 247 -250 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 247 -252 249 -250 249 -280 219 -252 249 -250 217 -278 279 -220 251 -250 247 -280 219 -252 249 -250 505 -258 217 -500 255 -278 215 -278 501 -260 215 -498 257 -276 215 -278 215 -278 215 -278 247 -252 505 -258 245 -504 481 -272 251 -254 217 -292 213 -532 485 -480 511 -274 215 -292 217 -252 243 -236 259 -250 249 -278 219 -252 249 -248 279 -546 235 -268 259 -226 255 -250 249 -278 221 -250 249 -250 215 -280 279 -220 251 -250 247 -280 219 -254 249 -248 281 -220 251 -250 249 -248 247 -280 219 -250 249 -250 279 -220 251 -250 249 -280 219 -252 249 -250 215 -280 247 -252 249 -250 249 -280 219 -252 249 -250 279 -220 251 -250 471 -288 247 -502 227 -276 245 -248 469 -292 213 -530 225 -278 213 -276 245 -250 247 -248 249 -278 477 -228 279 -500 477 -256 245 -242 277 -246 247 -502 479 -512 475 -274 291 -218 255 -250 241 -262 223 -254 249 -250 247 -252 249 -250 249 -570 267 -238 231 -286 221 -254 251 -248 217 -278 247 -252 251 -250 247 -280 219 -252 249 -250 217 -278 247 -252 251 -250 247 -280 221 -252 249 -248 279 -220 253 -250 247 -248 249 -278 219 -252 249 -250 279 -220 251 -250 247 -280 221 -252 249 -248 217 -280 247 -250 251 -250 249 -280 477 -228 279 -500 257 -214 275 -216 529 -228 279 -500 257 -216 275 -216 275 -216 277 -278 219 -250 505 -258 217 -500 547 -238 251 -254 255 -218 245 -502 481 -544 479 -236 291 -254 217 -292 213 -268 221 -250 247 -252 249 -250 249 -280 219 -572 265 -268 199 -292 219 -250 279 -222 251 -250 247 -282 219 -252\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/roger.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Roger\nBit: 28\nKey: 00 00 00 00 05 AB A1 01\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/roger_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 22710 -100 52363 -100 323 -66 1865 -66 32843 -66 753 -100 461 -100 19881 -66 16113 -66 20557 -66 6287 -64 25981 -100 587 -66 24707 -66 2367 -66 4681 -66 291 -98 4567 -100 1001 -100 33935 -66 20561 -100 5745 -66 2563 -66 4245 -66 22121 -66 1517 -100 2895 -66 15919 -66 20885 -100 12513 -890 839 -458 415 -914 843 -452 837 -482 387 -928 849 -446 415 -900 843 -474 415 -902 843 -470 841 -470 843 -468 383 -914 845 -460 415 -914 409 -884 423 -902 411 -902 835 -482 411 -880 423 -902 411 -908 397 -898 405 -920 411 -880 441 -896 835 -8268 411 -888 877 -430 415 -908 841 -460 869 -434 413 -920 845 -458 415 -912 845 -454 409 -884 879 -452 869 -428 873 -434 413 -904 871 -434 413 -918 415 -874 441 -882 425 -900 835 -448 443 -880 423 -904 409 -902 409 -904 409 -910 399 -896 417 -894 851 -8248 421 -912 853 -446 427 -872 863 -444 867 -446 441 -876 867 -456 415 -882 879 -452 419 -880 853 -448 881 -450 835 -460 415 -918 845 -454 409 -884 419 -906 411 -910 397 -898 865 -446 453 -860 421 -884 453 -876 415 -876 447 -878 441 -880 437 -882 861 -8216 457 -882 853 -456 405 -884 889 -420 879 -448 413 -890 873 -432 443 -892 841 -456 409 -922 843 -458 841 -460 873 -432 413 -918 843 -456 409 -918 387 -906 409 -912 427 -870 863 -446 439 -874 437 -888 409 -880 439 -898 419 -888 421 -880 453 -876 843 -8232 449 -874 891 -418 451 -878 865 -454 849 -448 413 -902 843 -474 411 -888 877 -432 413 -920 843 -454 869 -432 875 -432 413 -920 845 -458 415 -884 421 -910 395 -900 439 -888 839 -454 451 -880 419 -878 449 -876 415 -876 445 -878 439 -880 435 -870 867 -8246 423 -888 849 -454 445 -880 863 -418 879 -446 413 -896 875 -444 411 -902 873 -442 411 -904 839 -472 837 -472 837 -474 413 -874 871 -438 443 -892 415 -874 441 -878 439 -878 867 -454 417 -882 425 -908 399 -896 419 -896 419 -882 453 -876 413 -878 871 -8228 451 -876 853 -442 437 -904 841 -450 855 -450 449 -886 839 -474 411 -890 843 -460 415 -886 879 -456 843 -458 875 -428 409 -906 869 -436 413 -920 417 -884 423 -874 435 -892 857 -456 417 -880 453 -876 413 -910 413 -878 439 -870 439 -872 439 -870 871 -8248 421 -892 851 -454 445 -880 861 -452 843 -448 413 -896 877 -444 413 -898 853 -450 417 -898 877 -446 841 -474 839 -472 411 -892 843 -460 409 -918 417 -882 423 -872 435 -896 869 -418 453 -888 421 -878 451 -876 415 -876 443 -878 441 -876 437 -882 861 -8236 423 -878 883 -454 401 -884\nRAW_Data: 857 -450 881 -446 413 -888 873 -434 415 -906 875 -428 411 -904 869 -434 873 -438 875 -436 413 -906 869 -434 413 -906 411 -904 411 -906 415 -908 835 -460 411 -906 409 -904 411 -908 417 -874 411 -910 415 -914 421 -884 849 -8232 453 -870 853 -440 439 -902 841 -450 885 -452 413 -870 861 -452 447 -862 877 -446 411 -900 877 -444 839 -474 839 -472 411 -902 841 -470 409 -890 415 -882 421 -910 425 -872 863 -446 451 -866 417 -906 417 -906 419 -868 451 -870 415 -900 413 -904 841 -8244 439 -886 855 -456 415 -908 853 -444 861 -452 421 -886 851 -452 447 -860 877 -448 413 -898 875 -444 873 -442 837 -474 409 -904 839 -472 409 -870 441 -898 421 -886 413 -890 873 -434 443 -890 417 -882 423 -876 433 -896 417 -894 421 -880 453 -876 843 -8254 435 -870 865 -448 409 -904 869 -454 843 -458 415 -912 843 -456 409 -914 835 -458 409 -920 841 -456 867 -432 873 -434 443 -888 845 -460 415 -910 417 -882 425 -872 435 -892 867 -448 407 -898 409 -902 417 -898 419 -880 453 -874 415 -908 413 -878 871 -8236 417 -904 845 -454 451 -880 863 -420 881 -448 413 -902 841 -476 411 -892 873 -430 415 -918 843 -456 875 -426 869 -434 443 -890 841 -458 409 -920 415 -882 425 -870 439 -904 835 -450 421 -912 427 -872 437 -888 419 -888 421 -882 453 -876 413 -880 871 -8234 457 -848 881 -444 435 -880 867 -450 847 -448 415 -920 841 -470 411 -888 845 -460 415 -916 847 -454 835 -460 871 -466 381 -920 843 -458 409 -916 417 -878 411 -912 425 -870 887 -418 445 -878 419 -914 419 -876 415 -908 413 -876 443 -880 441 -880 863 -8232 423 -880 881 -454 401 -904 865 -450 847 -446 415 -898 875 -444 411 -902 839 -476 411 -902 839 -474 837 -472 839 -472 411 -888 875 -430 415 -908 415 -908 409 -882 421 -904 837 -450 421 -910 425 -872 451 -868 419 -882 451 -874 413 -906 413 -902 843 -8256 419 -892 851 -472 405 -906 837 -480 821 -482 417 -904 823 -482 387 -918 843 -472 413 -884 845 -462 875 -432 875 -468 381 -916 843 -460 415 -914 387 -908 395 -938 399 -892 863 -444 409 -902 405 -918 409 -898 407 -920 411 -878 417 -918 417 -880 839 -8254 439 -898 837 -448 411 -906 869 -454 845 -460 415 -882 881 -450 409 -914 845 -452 409 -914 843 -456 843 -458 843 -462 415 -914 843 -456 413 -912 387 -910 423 -908 415 -870 873 -460 413 -910 381 -906 411 -906 409 -906 411 -904 411 -904 411 -904 839 -8254 409 -882 865 -446 439 -874 889 -418 873 -458 417 -878 885 -454\nRAW_Data: 403 -884 857 -482 385 -918 843 -474 843 -468 839 -470 411 -906 837 -468 411 -904 379 -904 413 -900 415 -918 841 -474 381 -918 417 -912 385 -912 395 -900 451 -866 419 -918 417 -878 843 -8274 397 -896 865 -446 411 -904 871 -452 845 -458 417 -874 869 -458 415 -882 879 -454 417 -882 881 -448 837 -458 869 -432 413 -906 869 -434 413 -906 411 -904 411 -904 411 -904 869 -432 415 -916 387 -910 425 -908 415 -868 415 -908 415 -906 415 -876 879 -8236 409 -912 837 -454 451 -882 845 -452 875 -452 417 -890 849 -446 415 -896 877 -446 411 -900 841 -474 841 -470 839 -472 411 -904 839 -472 413 -886 415 -916 387 -904 411 -908 853 -448 409 -912 395 -900 435 -892 409 -916 407 -896 411 -912 409 -894 835 -8258 411 -890 871 -436 445 -888 841 -462 873 -430 413 -920 843 -456 409 -906 869 -430 415 -920 843 -454 869 -432 873 -434 413 -906 865 -470 411 -888 409 -884 449 -882 419 -890 845 -486 411 -870 437 -874 439 -870 439 -902 409 -902 409 -902 409 -902 839 -8252 405 -896 865 -446 441 -876 869 -454 845 -458 415 -882 879 -454 417 -882 879 -418 441 -882 881 -450 845 -456 867 -432 413 -906 869 -432 411 -906 409 -920 415 -884 425 -872 893 -412 439 -912 399 -892 409 -902 417 -900 421 -880 451 -878 413 -876 877 -8222 457 -886 845 -440 441 -880 869 -450 849 -450 415 -922 839 -472 411 -890 841 -462 413 -884 879 -452 869 -432 873 -434 413 -920 843 -458 409 -886 419 -912 417 -880 453 -882 847 -454 443 -880 399 -882 433 -884 427 -916 399 -886 429 -884 429 -886 853 -8258 423 -878 879 -454 403 -884 859 -452 877 -446 413 -888 873 -434 445 -890 843 -460 413 -884 879 -456 845 -458 867 -430 415 -920 843 -454 409 -920 385 -912 427 -872 435 -890 855 -456 419 -878 453 -876 415 -878 445 -878 439 -878 439 -880 401 -914 861 -8224 419 -906 845 -470 407 -902 841 -450 887 -418 451 -884 841 -474 413 -888 875 -432 413 -908 843 -460 867 -434 873 -436 443 -874 869 -436 443 -874 411 -920 415 -884 425 -874 893 -412 435 -894 441 -870 451 -862 419 -880 453 -876 417 -910 413 -878 869 -8234 457 -848 879 -456 403 -902 865 -452 847 -448 413 -896 877 -444 411 -900 873 -444 411 -902 841 -474 841 -472 839 -472 411 -890 845 -462 413 -884 421 -912 425 -872 437 -888 871 -418 445 -878 419 -914 419 -878 415 -910 413 -876 443 -880 439 -880 863 -8226 419 -904 841 -472 417 -872 879 -442 861 -450 423 -884 857 -450 451 -858 879 -448 411 -898 875 -444\nRAW_Data: 841 -474 837 -474 411 -902 839 -472 411 -902 411 -904 409 -872 443 -898 845 -448 415 -900 413 -898 425 -886 425 -888 425 -886 425 -886 423 -898 863 -8226 451 -872 877 -434 409 -904 873 -416 891 -420 453 -886 841 -474 411 -888 873 -430 413 -908 843 -460 869 -432 873 -436 445 -874 869 -436 443 -890 415 -884 423 -902 409 -904 867 -418 419 -912 425 -872 435 -894 409 -914 407 -898 411 -880 447 -880 851 -8230 457 -868 879 -452 417 -868 869 -452 849 -450 449 -858 877 -448 411 -896 877 -444 413 -900 875 -446 841 -438 875 -438 445 -890 845 -460 415 -882 421 -912 395 -898 439 -870 867 -450 425 -910 399 -894 405 -920 413 -878 419 -914 419 -878 415 -910 843 -8232 457 -886 845 -442 439 -876 869 -454 855 -448 415 -886 875 -438 443 -876 875 -434 413 -904 871 -432 873 -434 873 -436 445 -874 869 -436 413 -920 417 -880 423 -902 411 -904 867 -418 421 -912 427 -868 437 -890 409 -916 413 -876 419 -914 417 -876 879 -8234 423 -888 843 -456 449 -880 863 -420 881 -448 413 -902 843 -476 413 -888 877 -430 413 -886 877 -454 867 -432 873 -434 413 -918 845 -460 415 -882 423 -908 397 -898 437 -890 855 -426 447 -874 453 -876 415 -878 447 -878 441 -878 437 -880 435 -880 859 -8212 457 -884 847 -454 445 -880 863 -420 881 -448 411 -902 875 -442 411 -902 839 -474 411 -888 875 -430 875 -432 875 -432 413 -922 843 -458 417 -884 421 -910 397 -898 453 -864 879 -454 415 -878 439 -878 435 -880 435 -882 395 -902 437 -868 437 -870 871 -8244 421 -894 845 -470 409 -908 843 -456 857 -448 415 -890 871 -436 445 -890 843 -460 415 -882 881 -452 869 -428 867 -436 443 -888 841 -460 409 -918 417 -882 453 -844 447 -876 853 -454 449 -880 435 -878 403 -916 397 -914 397 -884 431 -884 429 -884 889 -8228 425 -878 879 -422 435 -886 855 -452 877 -448 413 -888 877 -432 413 -886 877 -454 409 -886 891 -446 855 -444 867 -456 415 -882 851 -448 439 -884 425 -902 409 -874 435 -892 867 -444 409 -910 427 -872 437 -888 411 -880 439 -892 419 -890 421 -878 879 -8234 421 -894 881 -434 409 -902 841 -456 859 -450 415 -900 885 -420 451 -886 839 -472 411 -900 847 -448 877 -446 841 -474 411 -890 845 -460 415 -884 421 -906 409 -912 397 -898 889 -420 447 -876 421 -880 455 -880 411 -878 443 -878 439 -880 435 -882 861 -8210 437 -896 865 -442 407 -908 867 -452 845 -458 415 -912 843 -454 409 -884 879 -452 407 -908 867 -432 873 -432 873 -432 415 -904 869 -434\nRAW_Data: 415 -904 411 -906 409 -906 409 -906 869 -430 415 -908 415 -880 439 -876 411 -906 411 -912 395 -898 437 -890 851 -8232 443 -890 845 -460 415 -884 879 -454 843 -458 415 -884 879 -454 409 -884 879 -448 409 -884 879 -454 841 -458 869 -432 413 -904 869 -434 413 -906 411 -904 409 -908 417 -882 881 -450 409 -880 441 -876 411 -910 425 -109718 133 -700 197 -334 133 -132 133 -166 131 -134 167 -200 427 -234 1129 -132 923 -100 22805 -66 22225 -66 655 -64 24395 -98 5203 -98 15817 -66 5793 -98 819 -98 4477 -66 12519 -68 64793 -66 425 -100 3651 -66 10365 -66 2361 -100 697 -66 13695 -100 15599 -66 1829 -100 21103 -66 40353 -64 1751 -98 14265 -132 54383 -66 2653 -98 13411 -66 15223 -66 8943 -892 857 -432 443 -876 843 -480 857 -452 421 -882 843 -474 413 -886 879 -432 415 -916 845 -454 837 -464 875 -432 415 -914 845 -458 411 -914 389 -908 427 -874 433 -894 867 -450 409 -898 409 -916 409 -896 417 -888 417 -880 449 -876 413 -908 839 -8254 415 -896 853 -470 407 -880 875 -450 853 -450 415 -920 843 -470 411 -872 869 -470 413 -888 843 -462 875 -430 875 -432 415 -918 847 -458 417 -880 425 -908 415 -904 407 -896 835 -456 443 -878 419 -882 449 -878 413 -908 413 -876 441 -876 439 -878 867 -8218 457 -886 845 -452 445 -876 865 -450 847 -450 417 -900 877 -444 413 -870 871 -440 441 -870 873 -438 873 -440 873 -438 443 -870 881 -446 417 -902 413 -902 417 -870 429 -886 891 -416 387 -978 355 -964 353 -930 387 -938 353 -942 385 -940 383 -912 845 -8258 421 -876 879 -422 469 -848 893 -420 843 -506 353 -944 813 -502 383 -912 849 -460 419 -904 837 -460 871 -466 843 -466 381 -900 875 -446 419 -912 383 -934 383 -914 415 -886 843 -462 411 -904 417 -916 417 -882 383 -964 343 -986 307 -982 345 -980 767 -8306 375 -934 839 -484 377 -932 837 -458 839 -466 407 -910 841 -478 395 -916 861 -446 421 -886 851 -448 877 -446 809 -536 353 -962 779 -502 385 -930 383 -936 387 -926 385 -926 815 -474 417 -914 381 -914 417 -912 419 -882 397 -900 405 -918 409 -898 861 -8248 437 -878 861 -450 419 -886 851 -448 877 -444 413 -884 873 -472 411 -886 845 -460 411 -916 847 -454 871 -430 877 -434 415 -916 845 -460 417 -880 423 -904 411 -908 399 -898 853 -454 415 -914 415 -882 415 -908 411 -910 409 -880 439 -878 435 -872 867 -8234 451 -864 875 -428 443 -876 875 -454 865 -482 355 -926 819 -504 355 -940 813 -504 385 -944 815 -488 819 -486 807 -492 387 -934 841 -458\nRAW_Data: 413 -914 389 -904 393 -932 411 -906 825 -480 381 -930 413 -908 413 -866 413 -914 415 -884 449 -876 411 -912 803 -8306 381 -934 827 -480 381 -932 709 -24288 131 -15484 407 -916 799 -514 373 -914 829 -500 793 -510 393 -910 855 -448 387 -912 881 -444 383 -948 789 -524 791 -524 825 -490 375 -936 811 -456 407 -920 411 -918 373 -946 373 -920 831 -478 399 -918 395 -916 395 -914 395 -914 427 -882 431 -880 397 -918 893 -8186 479 -826 881 -438 265 -5122 99 -604 291 -990 785 -540 321 -958 815 -504 813 -472 849 -440 411 -916 845 -450 437 -900 411 -888 415 -916 413 -884 841 -478 393 -916 431 -882 431 -878 431 -878 431 -872 437 -876 437 -876 863 -8226 451 -890 849 -488 369 -920 825 -478 819 -510 385 -912 845 -470 383 -908 845 -476 415 -884 845 -464 877 -432 875 -436 417 -914 843 -460 417 -906 411 -880 425 -908 397 -900 849 -454 417 -914 417 -878 415 -910 411 -912 407 -878 439 -878 439 -876 865 -8228 437 -894 853 -428 449 -878 881 -442 857 -454 423 -888 883 -418 415 -890 875 -438 445 -888 845 -456 869 -432 875 -436 445 -868 869 -438 445 -878 447 -870 409 -886 451 -874 865 -420 441 -886 421 -912 429 -870 441 -854 455 -852 455 -878 451 -840 907 -8212 437 -876 857 -456 415 -918 845 -454 867 -450 417 -886 853 -446 413 -920 843 -440 441 -872 871 -438 871 -472 839 -472 411 -888 843 -464 415 -904 409 -910 417 -876 409 -912 853 -446 411 -906 411 -910 427 -870 453 -866 415 -910 419 -878 449 -876 843 -8242 453 -868 873 -450 415 -880 867 -446 849 -484 383 -918 843 -474 409 -906 841 -470 411 -886 843 -464 875 -432 875 -434 415 -914 845 -458 417 -884 423 -902 411 -906 397 -898 867 -446 409 -906 407 -918 407 -898 409 -900 409 -918 407 -898 417 -888 845 -8236 453 -872 849 -486 411 -872 871 -446 853 -482 417 -890 847 -446 413 -896 875 -446 413 -900 851 -448 875 -444 875 -442 413 -898 841 -474 411 -902 411 -902 411 -904 413 -886 843 -462 417 -906 417 -882 423 -906 397 -900 407 -922 417 -890 419 -880 875 -8242 407 -904 867 -420 421 -906 869 -456 835 -462 409 -918 843 -460 409 -884 879 -452 411 -906 835 -462 869 -434 873 -438 445 -870 871 -438 413 -920 417 -878 423 -902 409 -906 857 -448 429 -872 437 -892 419 -892 419 -880 451 -874 413 -878 441 -868 873 -8250 421 -890 853 -438 439 -878 873 -452 855 -450 415 -888 873 -438 445 -888 843 -462 415 -882 883 -452 835 -462 869 -434 415 -918 843 -458 409 -908 417 -874\nRAW_Data: 439 -874 441 -876 869 -454 409 -908 415 -880 443 -880 427 -872 437 -894 409 -882 449 -878 877 -8236 415 -878 881 -452 411 -880 883 -450 837 -460 411 -916 847 -456 409 -914 845 -456 419 -880 851 -452 881 -452 837 -460 413 -914 843 -458 411 -916 387 -910 425 -906 415 -868 875 -460 415 -878 415 -908 413 -910 409 -878 439 -878 437 -880 437 -880 863 -8226 449 -878 853 -452 443 -878 861 -450 847 -450 415 -920 841 -438 445 -888 843 -462 415 -882 879 -454 867 -432 875 -432 415 -916 845 -460 417 -880 423 -904 409 -912 397 -900 867 -448 419 -890 419 -882 451 -876 415 -912 409 -880 439 -880 435 -872 867 -8242 409 -914 835 -442 451 -870 875 -460 845 -440 437 -902 839 -446 425 -904 863 -452 423 -888 851 -448 879 -444 845 -474 413 -886 843 -462 417 -916 387 -912 427 -872 451 -870 877 -432 441 -872 445 -872 411 -904 411 -904 409 -906 409 -904 411 -902 839 -8242 453 -866 877 -450 413 -874 871 -448 851 -484 383 -920 843 -472 413 -888 845 -462 415 -882 881 -452 837 -460 871 -436 413 -920 845 -460 415 -882 423 -902 411 -910 395 -902 861 -444 441 -874 437 -894 413 -882 419 -912 419 -880 415 -908 413 -880 869 -8228 457 -884 845 -442 435 -878 871 -452 853 -450 415 -896 881 -444 413 -904 841 -474 411 -884 879 -430 875 -432 875 -434 415 -904 869 -432 445 -888 417 -876 411 -906 411 -904 867 -452 411 -906 409 -882 421 -910 397 -900 451 -864 419 -882 449 -878 873 -8238 421 -890 843 -452 447 -878 865 -450 847 -450 415 -894 881 -444 413 -902 841 -474 413 -890 841 -462 869 -436 873 -436 415 -918 845 -462 409 -904 409 -916 387 -912 427 -872 863 -446 441 -872 451 -866 419 -912 419 -880 413 -908 413 -876 443 -880 867 -8224 447 -880 879 -430 445 -876 877 -422 859 -452 449 -860 875 -448 415 -898 851 -450 417 -894 875 -448 877 -446 841 -440 443 -890 877 -428 409 -904 411 -884 453 -880 425 -872 861 -446 441 -874 453 -866 415 -908 419 -880 451 -876 415 -876 443 -878 869 -8224 455 -884 843 -468 407 -906 841 -448 887 -448 417 -892 849 -448 415 -918 839 -472 413 -886 875 -432 845 -462 873 -432 411 -906 873 -434 415 -920 417 -880 425 -904 399 -898 865 -450 407 -902 415 -894 419 -912 417 -878 413 -908 413 -910 409 -878 869 -8234 449 -874 877 -450 413 -872 869 -448 883 -448 413 -872 889 -450 417 -890 847 -448 415 -920 841 -438 877 -436 875 -436 441 -874 873 -434 441 -872 441 -868 451 -884 413 -900 839 -476 413 -902\nRAW_Data: 411 -886 417 -884 421 -910 397 -900 451 -864 415 -910 843 -8248 445 -858 879 -458 417 -882 881 -418 879 -454 409 -882 881 -452 411 -882 883 -418 443 -874 867 -458 871 -434 875 -434 415 -918 845 -460 417 -878 421 -908 427 -872 451 -864 879 -450 417 -880 439 -880 437 -878 435 -880 401 -914 429 -870 435 -870 867 -8252 411 -886 877 -430 447 -876 879 -414 887 -452 419 -886 851 -448 447 -860 877 -446 413 -896 877 -444 873 -444 871 -442 409 -902 839 -474 409 -900 411 -902 409 -900 411 -900 875 -442 411 -902 409 -902 409 -904 409 -900 421 -888 423 -888 423 -890 849 -8264 421 -878 877 -454 401 -884 857 -452 879 -446 411 -900 875 -444 411 -900 839 -474 411 -900 839 -474 839 -474 839 -472 409 -906 839 -472 411 -872 445 -888 417 -874 441 -882 851 -450 423 -912 427 -868 439 -888 411 -882 453 -884 419 -876 449 -874 841 -8238 453 -888 843 -470 407 -904 839 -446 887 -450 415 -904 825 -482 417 -890 849 -448 415 -898 877 -446 845 -474 843 -470 381 -918 845 -460 417 -882 421 -908 427 -872 451 -866 873 -444 413 -908 417 -878 449 -876 413 -908 411 -876 441 -876 439 -878 865 -8234 417 -906 851 -440 437 -904 839 -448 885 -448 419 -890 847 -446 415 -900 857 -450 417 -918 843 -474 841 -472 843 -468 383 -916 869 -430 411 -904 411 -904 411 -902 411 -904 869 -430 443 -870 411 -904 411 -904 411 -902 411 -904 411 -906 409 -906 837 -8266 413 -904 873 -414 453 -870 867 -450 883 -414 447 -860 875 -448 411 -888 873 -440 441 -874 875 -436 873 -436 871 -436 443 -874 875 -436 443 -890 415 -876 423 -910 395 -902 863 -448 441 -872 439 -888 421 -888 419 -880 451 -874 413 -910 411 -874 869 -8234 447 -876 875 -432 445 -874 877 -454 829 -450 449 -890 847 -446 413 -898 877 -444 411 -904 843 -472 841 -474 843 -470 413 -868 875 -436 445 -888 417 -880 423 -910 415 -874 877 -428 447 -876 449 -874 413 -910 411 -876 441 -878 439 -880 437 -878 863 -8232 417 -910 845 -470 405 -904 839 -478 855 -448 421 -882 845 -476 415 -884 875 -432 415 -916 845 -456 839 -464 875 -434 417 -914 845 -458 409 -916 387 -912 425 -906 399 -894 853 -456 413 -916 415 -880 413 -910 411 -912 409 -880 435 -874 437 -872 865 -8248 453 -866 879 -402 475 -842 907 -422 859 -450 415 -886 873 -438 443 -866 879 -446 415 -896 877 -446 873 -444 841 -474 411 -890 843 -462 415 -882 421 -912 427 -872 437 -892 857 -428 447 -880 449 -878 411 -904 409 -872 441 -872\nRAW_Data: 441 -872 441 -872 871 -8252 413 -880 865 -446 439 -872 899 -416 867 -456 409 -904 869 -432 415 -886 877 -460 407 -904 867 -432 877 -434 875 -434 415 -916 845 -460 417 -882 423 -902 411 -902 411 -910 857 -444 411 -904 409 -904 411 -904 409 -902 411 -908 399 -894 407 -922 837 -8230 443 -902 873 -444 411 -898 871 -442 839 -442 443 -890 875 -432 415 -914 845 -456 409 -912 837 -462 845 -462 871 -432 415 -914 843 -462 409 -918 419 -878 409 -904 411 -902 869 -450 411 -880 423 -904 411 -904 409 -910 397 -896 407 -922 417 -890 851 -8226 463 -868 867 -448 415 -882 891 -420 879 -450 419 -886 851 -450 417 -920 839 -474 411 -890 877 -430 875 -432 877 -430 415 -904 869 -434 413 -906 409 -916 407 -906 409 -882 879 -418 443 -882 421 -912 397 -900 453 -862 421 -878 453 -874 449 -878 841 -8230 449 -876 879 -426 449 -876 881 -416 889 -418 447 -890 847 -450 449 -858 877 -446 413 -900 883 -418 879 -446 845 -474 409 -902 839 -474 409 -902 409 -900 411 -900 411 -888 873 -436 413 -920 417 -882 425 -870 453 -868 417 -906 415 -906 417 -874 875 -8238 425 -876 893 -418 445 -880 875 -440 877 -408 447 -874 877 -442 433 -870 871 -450 423 -882 889 -420 879 -446 877 -444 413 -900 839 -476 411 -888 415 -882 421 -912 429 -870 865 -446 453 -864 419 -882 449 -870 417 -910 411 -906 411 -904 407 -904 837 -8248 457 -864 875 -408 449 -874 881 -440 863 -454 427 -888 881 -416 417 -896 875 -446 413 -900 877 -444 841 -440 873 -440 445 -890 843 -460 415 -884 453 -880 429 -868 451 -866 875 -442 415 -874 449 -872 449 -872 447 -870 413 -902 413 -904 409 -904 873 -8206 449 -880 879 -452 415 -880 865 -450 845 -448 415 -894 875 -446 413 -902 853 -448 449 -858 877 -446 877 -444 875 -444 411 -888 877 -426 441 -874 409 -920 417 -880 425 -904 835 -448 441 -882 423 -904 407 -876 433 -900 409 -884 439 -896 411 -882 889 -8232 413 -886 867 -460 415 -884 881 -420 867 -462 415 -916 845 -454 409 -908 843 -460 409 -918 845 -454 869 -432 877 -432 413 -918 845 -460 417 -880 441 -880 451 -848 453 -888 851 -442 433 -880 439 -878 437 -880 435 -882 397 -902 435 -872 437 -874 869 -8244 441 -854 909 -420 415 -906 847 -440 877 -442 433 -870 871 -448 423 -882 891 -450 419 -886 851 -450 879 -446 843 -472 413 -872 875 -470 381 -918 417 -880 425 -906 399 -900 867 -448 409 -920 413 -880 417 -914 417 -878 415 -908 413 -880 439 -878 869 -8228 457 -852\nRAW_Data: 883 -456 405 -882 889 -420 883 -448 415 -888 875 -434 445 -872 869 -436 443 -892 843 -458 869 -432 873 -434 445 -870 871 -436 445 -874 441 -886 415 -876 441 -882 885 -418 425 -902 409 -912 397 -898 419 -894 421 -882 451 -880 413 -876 875 -8222 447 -884 855 -452 449 -878 865 -454 849 -450 413 -902 875 -444 413 -888 875 -432 415 -908 843 -462 875 -430 867 -436 445 -890 843 -458 407 -888 451 -874 411 -906 409 -912 855 -448 429 -874 437 -892 413 -880 455 -880 419 -876 449 -878 413 -876 871 -8238 449 -876 879 -420 449 -878 863 -420 875 -448 413 -894 877 -446 413 -898 875 -444 411 -898 875 -444 839 -474 839 -474 409 -888 877 -432 415 -918 417 -880 411 -884 447 -880 877 -452 417 -876 439 -878 437 -878 437 -878 435 -878 435 -880 433 -848 893 -8224 457 -848 885 -456 437 -870 865 -420 879 -450 445 -862 877 -448 413 -890 873 -432 441 -876 869 -436 869 -438 867 -440 441 -900 847 -450 415 -900 413 -890 445 -890 417 -882 851 -450 421 -910 397 -900 439 -888 409 -886 453 -880 419 -880 449 -876 841 -8234 447 -878 891 -432 443 -876 843 -456 891 -418 415 -898 885 -420 451 -854 879 -450 413 -890 873 -438 875 -436 873 -436 443 -892 843 -458 409 -886 451 -874 439 -884 427 -872 891 -420 451 -852 453 -878 449 -842 445 -878 441 -880 439 -880 431 -870 869 -8242 435 -874 857 -458 413 -910 845 -440 865 -454 431 -884 887 -418 449 -886 841 -472 413 -888 877 -430 869 -434 875 -438 413 -918 843 -460 415 -906 409 -910 387 -912 425 -870 867 -450 425 -904 409 -904 411 -904 409 -908 415 -874 419 -914 417 -880 875 -8236 437 -884 855 -444 441 -880 853 -446 869 -446 429 -872 887 -418 447 -878 877 -430 445 -876 875 -420 895 -420 879 -448 413 -902 875 -444 411 -892 415 -884 421 -912 399 -900 867 -448 455 -860 423 -876 453 -872 449 -844 443 -870 443 -870 443 -870 873 -8232 451 -874 889 -402 441 -876 877 -456 853 -452 413 -896 877 -448 413 -890 875 -434 413 -884 879 -460 875 -426 869 -434 445 -892 845 -458 415 -886 423 -908 417 -872 455 -882 845 -440 433 -904 411 -878 443 -880 437 -882 435 -882 431 -870 433 -874 867 -8242 455 -862 879 -452 415 -880 867 -452 853 -448 415 -900 873 -446 411 -900 849 -450 415 -896 875 -446 873 -444 875 -410 443 -876 873 -436 443 -892 415 -884 451 -848 449 -878 879 -434 441 -870 447 -870 413 -902 411 -902 413 -870 445 -870 443 -900 839 -8242 453 -872 881 -452 413 -872 869 -448 849 -450\nRAW_Data: 445 -870 889 -418 449 -892 847 -446 413 -896 879 -446 875 -442 843 -474 411 -888 845 -462 415 -884 423 -910 427 -870 451 -862 875 -428 443 -876 445 -872 413 -906 413 -904 411 -904 411 -904 409 -904 839 -8264 409 -884 887 -430 445 -876 843 -448 887 -450 417 -888 849 -448 449 -890 847 -446 413 -898 877 -444 871 -444 839 -474 411 -902 839 -474 409 -902 411 -900 409 -888 415 -884 881 -456 417 -882 423 -910 399 -898 419 -896 419 -880 453 -878 413 -878 875 -8236 449 -876 879 -434 441 -870 875 -414 889 -454 419 -890 845 -450 417 -886 871 -440 443 -868 879 -448 879 -444 843 -474 413 -888 845 -462 415 -882 421 -912 427 -872 437 -894 837 -454 449 -884 419 -880 415 -910 413 -878 443 -878 439 -880 437 -878 863 -8230 457 -848 879 -452 437 -872 865 -450 849 -450 415 -902 851 -450 417 -900 887 -418 451 -884 843 -474 839 -476 839 -472 411 -866 877 -448 415 -886 445 -888 417 -880 423 -910 853 -446 409 -912 395 -904 451 -866 415 -910 415 -874 449 -872 445 -874 873 -8212 455 -868 877 -428 445 -874 877 -446 863 -450 425 -882 843 -476 413 -904 839 -472 413 -888 875 -430 875 -432 875 -434 413 -904 873 -434 413 -904 411 -904 411 -916 417 -880 867 -456 409 -908 417 -876 409 -914 425 -872 451 -866 421 -882 415 -944 805 -8276 413 -900 839 -484 389 -908 855 -452 853 -450 425 -906 855 -450 395 -938 827 -448 435 -896 851 -456 841 -476 841 -468 407 -914 839 -452 429 -886 427 -884 427 -884 393 -982 757 -540 325 -992 357 -942 359 -942 355 -976 353 -944 353 -974 355 -942 813 -8292 405 -906 835 -450 431 -884 853 -480 847 -446 413 -886 875 -468 381 -920 845 -460 415 -916 845 -458 843 -458 869 -434 413 -920 843 -460 417 -878 411 -910 411 -908 427 -872 861 -446 441 -874 451 -866 419 -916 417 -878 415 -910 411 -880 443 -878 867 -8226 457 -882 851 -440 433 -904 837 -474 835 -446 427 -916 857 -450 419 -888 849 -448 415 -918 843 -472 845 -470 843 -470 383 -900 871 -468 381 -916 415 -884 423 -908 397 -902 863 -450 439 -874 437 -890 413 -882 419 -882 453 -876 449 -844 445 -876 871 -8232 451 -876 853 -450 445 -880 863 -452 853 -448 415 -886 871 -438 441 -886 871 -432 439 -890 845 -458 843 -460 871 -468 379 -898 879 -444 415 -918 381 -918 417 -880 423 -906 853 -450 427 -902 409 -908 399 -894 417 -894 419 -912 419 -876 415 -910 843 -8250 407 -902 851 -454 445 -878 841 -484 843 -452 427 -886 851 -482 385 -916 843 -472\nRAW_Data: 413 -886 843 -464 843 -466 845 -466 415 -916 847 -458 417 -880 425 -906 413 -100576 129 -4900 97 -1286 555 -132 65 -198 361 -132 98215 -96 21029 -64 5709 -66 22509 -98 23929 -66 4017 -66 687 -98 1353 -66 3641 -66 1391 -64 565 -66 40435 -102 51423 -66 10357 -66 10493 -98 5979 -66 22915 -100 1281 -100 62401 -66 26927 -66 20563 -100 4151 -100 12369 -66 45939 -858 863 -446 437 -894 853 -458 845 -452 449 -878 863 -454 421 -888 849 -448 415 -886 875 -470 841 -470 839 -472 411 -888 843 -464 415 -910 419 -880 425 -872 437 -894 871 -416 449 -882 419 -880 453 -876 415 -906 413 -876 443 -876 441 -878 865 -8230 457 -850 879 -440 449 -872 875 -438 873 -422 433 -882 887 -422 451 -854 875 -440 443 -892 845 -460 875 -428 871 -436 413 -906 869 -434 443 -868 449 -890 419 -884 413 -888 875 -434 415 -916 415 -876 411 -908 411 -910 427 -870 437 -894 409 -882 871 -8250 409 -900 875 -444 411 -888 875 -434 873 -434 445 -864 877 -446 415 -900 873 -446 411 -888 873 -434 871 -436 873 -436 445 -892 841 -458 409 -886 451 -882 453 -844 451 -874 845 -486 415 -878 439 -880 435 -880 401 -914 399 -914 399 -914 397 -904 863 -8224 457 -882 845 -452 441 -872 865 -450 847 -450 417 -918 841 -472 413 -888 841 -464 413 -910 845 -460 875 -432 877 -432 413 -908 843 -460 409 -918 417 -884 425 -874 435 -894 853 -456 417 -880 451 -876 413 -912 411 -904 411 -880 435 -872 439 -872 871 -8244 409 -916 835 -446 439 -904 837 -448 869 -456 411 -906 869 -430 415 -918 845 -458 415 -876 867 -460 875 -430 875 -432 415 -904 869 -434 413 -904 411 -904 409 -906 409 -906 867 -432 413 -906 409 -918 417 -882 425 -870 439 -872 439 -872 439 -904 865 -8220 439 -868 871 -450 423 -884 857 -450 881 -446 413 -902 843 -474 411 -888 877 -430 415 -908 843 -460 869 -434 871 -436 445 -874 869 -436 443 -876 409 -906 409 -906 409 -906 871 -432 415 -916 417 -882 425 -908 399 -896 409 -916 409 -896 411 -880 887 -8232 449 -844 867 -446 439 -872 893 -412 895 -446 429 -868 855 -456 451 -884 845 -442 435 -878 869 -454 853 -448 881 -444 413 -890 875 -432 413 -918 409 -874 441 -876 441 -878 867 -456 415 -882 423 -910 397 -900 437 -888 413 -880 455 -880 419 -876 877 -8238 421 -890 843 -488 413 -880 863 -450 847 -446 415 -892 879 -448 411 -898 875 -444 413 -900 841 -476 843 -472 841 -470 413 -888 843 -464 415 -906 409 -884 421 -908 397 -900 885 -420 445 -880 417 -914 419 -876\nRAW_Data: 415 -910 411 -880 441 -880 435 -872 867 -8254 421 -890 843 -444 445 -874 875 -442 865 -452 427 -890 849 -448 415 -918 841 -472 411 -886 841 -462 871 -434 873 -470 379 -920 843 -458 409 -900 413 -918 413 -886 415 -884 879 -452 409 -906 409 -884 421 -912 395 -900 437 -888 411 -880 441 -896 873 -8228 411 -902 843 -476 411 -888 877 -428 869 -434 415 -916 843 -460 415 -882 869 -458 409 -904 873 -432 875 -436 873 -436 413 -908 875 -430 413 -906 409 -886 419 -914 427 -872 889 -420 451 -882 419 -878 451 -876 415 -876 443 -878 439 -876 437 -878 869 -8228 421 -918 845 -452 403 -904 865 -444 853 -446 417 -924 847 -446 415 -920 839 -468 411 -904 841 -468 839 -468 839 -472 411 -902 841 -470 411 -904 411 -872 413 -902 413 -902 877 -434 411 -900 413 -906 411 -900 415 -916 379 -936 381 -916 415 -908 835 -8254 419 -910 841 -450 423 -884 855 -452 879 -448 417 -900 849 -448 415 -920 841 -472 411 -874 869 -438 871 -472 839 -470 411 -872 869 -440 445 -874 441 -866 449 -884 413 -888 875 -432 413 -906 409 -906 409 -906 409 -886 421 -908 427 -872 451 -866 875 -8236 415 -882 871 -460 417 -880 881 -454 845 -456 417 -880 879 -452 409 -884 881 -452 409 -880 881 -450 837 -462 871 -434 415 -914 845 -458 413 -916 387 -910 427 -904 401 -894 867 -450 411 -884 413 -908 419 -912 417 -876 415 -908 413 -910 409 -878 865 -8234 455 -882 843 -452 445 -880 859 -450 847 -446 415 -894 877 -444 415 -896 879 -444 413 -902 841 -472 841 -472 841 -472 411 -890 845 -462 415 -908 417 -876 411 -908 409 -912 851 -450 425 -908 399 -894 407 -904 437 -888 411 -880 447 -876 419 -880 881 -8238 439 -888 837 -456 451 -880 851 -452 871 -420 449 -892 845 -448 413 -894 879 -446 411 -900 875 -444 839 -474 837 -474 409 -904 839 -472 409 -902 411 -902 409 -904 409 -904 839 -472 409 -904 409 -872 443 -872 443 -890 417 -882 423 -910 417 -874 875 -8220 441 -904 841 -468 411 -904 841 -470 841 -470 413 -886 843 -462 417 -906 843 -458 411 -918 843 -456 869 -432 875 -434 415 -916 845 -456 411 -916 417 -876 411 -912 425 -870 889 -418 445 -882 421 -880 449 -878 415 -910 413 -878 441 -878 439 -880 865 -8230 417 -906 843 -468 407 -906 839 -478 857 -450 423 -884 843 -474 413 -886 877 -432 415 -916 845 -458 845 -460 871 -432 415 -916 845 -458 417 -916 387 -908 427 -870 437 -892 851 -458 415 -882 451 -876 413 -912 413 -878 439 -878 441 -880 435 -878\nRAW_Data: 863 -8240 423 -888 841 -486 415 -878 869 -452 849 -448 415 -920 841 -470 381 -920 845 -460 415 -910 843 -454 869 -432 873 -434 415 -904 869 -436 413 -920 417 -876 441 -880 425 -908 857 -444 409 -910 399 -898 417 -894 419 -882 451 -874 415 -908 413 -878 873 -8254 405 -898 853 -458 417 -914 847 -452 865 -452 421 -884 845 -474 413 -886 873 -430 411 -904 873 -432 871 -470 837 -470 413 -886 843 -460 413 -918 417 -874 413 -906 411 -910 851 -446 411 -914 425 -872 451 -866 417 -916 419 -878 415 -910 413 -878 871 -8228 455 -884 845 -452 445 -880 863 -450 851 -446 413 -920 841 -470 409 -874 873 -468 409 -872 873 -470 843 -470 839 -470 381 -918 843 -462 415 -912 411 -880 423 -906 397 -900 865 -446 419 -894 419 -912 419 -878 413 -910 411 -910 409 -880 441 -878 865 -8254 413 -878 877 -430 445 -880 841 -458 863 -452 415 -920 841 -438 415 -908 871 -434 413 -914 845 -458 871 -466 841 -470 413 -884 843 -460 409 -920 417 -880 425 -872 435 -894 867 -448 407 -902 409 -916 407 -896 407 -902 409 -886 439 -898 411 -880 875 -8248 411 -900 843 -476 413 -886 873 -432 875 -434 413 -918 843 -460 411 -916 845 -454 411 -918 845 -456 845 -460 841 -462 411 -916 843 -456 411 -916 417 -880 425 -908 399 -896 851 -456 415 -916 417 -880 415 -908 413 -880 439 -870 439 -904 407 -874 871 -8238 439 -868 869 -448 411 -906 869 -454 837 -460 411 -916 843 -458 411 -916 843 -456 409 -906 871 -432 873 -434 875 -434 413 -918 845 -458 411 -916 387 -910 425 -908 415 -870 875 -428 447 -880 415 -908 413 -880 439 -872 439 -872 441 -872 439 -872 871 -8246 419 -896 847 -486 413 -882 865 -448 849 -446 415 -928 845 -446 415 -886 875 -468 381 -916 843 -460 843 -464 877 -432 413 -916 843 -458 411 -916 385 -906 411 -910 425 -906 823 -478 411 -902 411 -904 411 -906 397 -898 407 -918 411 -880 417 -916 847 -8236 435 -892 867 -448 397 -898 869 -448 869 -456 417 -906 837 -460 415 -914 847 -456 387 -908 849 -482 837 -458 871 -430 415 -914 847 -458 411 -916 385 -902 411 -910 423 -904 839 -450 423 -910 395 -900 405 -922 417 -888 417 -912 417 -876 415 -910 841 -8264 421 -890 843 -452 445 -880 863 -448 849 -448 417 -918 841 -470 413 -886 871 -430 411 -904 873 -466 837 -470 841 -468 411 -904 839 -470 411 -902 411 -902 381 -918 417 -906 839 -460 415 -914 389 -908 427 -870 437 -892 409 -916 407 -896 409 -916 853 -8234 425 -906 825 -480 401 -894\nRAW_Data: 865 -450 851 -456 415 -918 845 -454 405 -902 865 -448 421 -902 829 -482 847 -446 875 -444 413 -898 845 -474 413 -902 411 -904 413 -904 383 -914 843 -462 411 -914 417 -880 423 -906 397 -900 405 -922 417 -890 417 -882 879 -8232 409 -904 871 -434 411 -904 871 -466 839 -470 381 -932 845 -446 415 -918 839 -470 381 -916 843 -460 873 -466 841 -468 381 -916 841 -460 413 -916 387 -910 423 -908 397 -896 869 -448 427 -906 399 -894 415 -894 417 -912 417 -880 415 -908 411 -882 869 -8228 455 -888 845 -452 409 -916 861 -450 847 -446 415 -896 877 -444 413 -902 853 -448 415 -894 879 -444 841 -476 843 -472 415 -902 839 -472 381 -914 417 -914 387 -910 395 -938 825 -446 437 -894 417 -892 417 -914 415 -876 413 -910 411 -910 409 -910 835 -8232 447 -878 853 -452 451 -880 863 -450 847 -448 413 -920 839 -472 411 -888 877 -430 415 -916 843 -456 843 -460 867 -436 443 -874 869 -436 443 -890 415 -884 421 -904 407 -912 855 -444 409 -906 409 -912 397 -898 439 -890 411 -880 453 -880 421 -872 879 -8246 417 -876 849 -482 411 -880 883 -450 837 -458 411 -916 847 -456 411 -916 847 -454 409 -916 845 -452 835 -462 875 -432 413 -916 843 -460 411 -912 409 -906 411 -878 421 -910 855 -446 411 -910 425 -872 435 -892 409 -918 413 -880 419 -880 451 -880 843 -8264 421 -890 843 -486 411 -872 867 -448 851 -448 417 -926 847 -444 415 -896 875 -442 415 -900 851 -448 877 -444 843 -476 415 -888 843 -462 415 -916 387 -910 425 -908 399 -894 867 -444 411 -908 395 -902 435 -894 417 -892 417 -882 451 -878 413 -878 871 -8250 405 -900 867 -448 409 -910 853 -446 869 -454 411 -914 845 -456 417 -880 881 -448 411 -880 881 -452 837 -460 873 -434 413 -916 845 -460 415 -882 421 -910 395 -902 437 -888 853 -458 415 -882 449 -878 413 -912 411 -880 439 -880 437 -878 435 -880 865 -8224 455 -884 843 -442 437 -878 869 -452 855 -448 415 -920 839 -472 413 -886 845 -462 417 -914 847 -454 869 -426 873 -434 415 -916 843 -460 409 -918 415 -882 425 -906 399 -896 865 -448 407 -902 439 -890 411 -880 417 -916 417 -878 417 -908 413 -880 873 -8230 457 -886 849 -452 407 -906 863 -450 849 -446 415 -894 877 -446 413 -898 877 -442 413 -904 839 -474 843 -472 843 -470 381 -918 843 -462 417 -914 387 -910 427 -872 435 -892 869 -450 407 -896 409 -900 407 -918 407 -896 409 -900 407 -918 407 -898 855 -8234 443 -892 845 -460 415 -916 845 -454 835 -462 417 -912 837 -460\nRAW_Data: 411 -916 843 -458 417 -914 845 -454 835 -462 871 -468 381 -914 871 -430 415 -918 387 -910 425 -906 399 -896 849 -474 407 -896 409 -902 407 -918 407 -898 409 -916 409 -894 409 -884 875 -8230 443 -874 873 -434 413 -920 841 -460 871 -432 413 -918 847 -456 411 -904 871 -430 415 -916 845 -458 845 -460 875 -432 415 -916 843 -456 411 -916 387 -910 425 -908 415 -870 851 -486 411 -872 439 -870 441 -872 439 -904 409 -878 439 -878 437 -878 865 -8230 455 -882 845 -442 437 -878 869 -452 855 -448 415 -920 843 -470 381 -918 845 -460 415 -912 845 -454 869 -432 873 -434 415 -916 845 -458 411 -914 387 -910 425 -902 411 -902 835 -450 441 -880 425 -902 411 -908 397 -894 409 -902 435 -890 419 -892 843 -8266 395 -900 865 -446 439 -890 835 -474 867 -442 409 -910 853 -446 411 -912 851 -450 425 -902 837 -448 869 -458 841 -462 411 -916 843 -456 411 -916 387 -910 425 -908 399 -896 855 -458 415 -914 419 -876 415 -912 411 -880 439 -878 437 -882 401 -912 865 -8226 419 -910 843 -452 447 -880 863 -452 849 -446 413 -898 875 -444 413 -900 875 -442 411 -904 841 -472 841 -472 839 -472 411 -890 843 -460 409 -906 409 -920 417 -880 425 -874 889 -418 445 -880 421 -880 453 -880 413 -910 413 -880 439 -878 437 -878 863 -8238 417 -904 843 -470 405 -902 841 -478 859 -448 421 -890 849 -448 415 -918 841 -472 413 -886 843 -462 869 -468 841 -470 381 -916 845 -460 411 -918 387 -910 425 -906 399 -894 851 -456 415 -916 417 -880 415 -910 411 -912 405 -904 409 -904 407 -904 839 -8246 441 -886 855 -430 449 -878 879 -416 889 -450 415 -900 861 -420 449 -868 889 -418 453 -886 841 -474 839 -472 843 -470 411 -898 845 -450 415 -898 413 -886 445 -874 441 -890 841 -458 409 -898 413 -920 411 -872 441 -890 415 -884 421 -910 397 -902 853 -8254 415 -898 879 -444 413 -904 841 -472 841 -472 413 -886 841 -462 411 -916 843 -458 409 -916 845 -458 841 -462 869 -436 413 -904 869 -470 379 -920 417 -876 421 -908 425 -872 889 -418 451 -884 419 -880 449 -874 415 -908 411 -878 443 -880 435 -872 865 -8226 455 -886 845 -450 445 -880 863 -452 853 -444 415 -918 841 -470 381 -916 845 -462 415 -914 845 -456 837 -460 871 -470 381 -916 843 -460 411 -914 417 -880 423 -902 409 -904 837 -484 389 -910 427 -904 399 -896 407 -916 409 -892 409 -918 409 -896 835 -8254 413 -896 879 -444 413 -900 843 -474 841 -472 411 -902 841 -472 409 -904 839 -472 411 -902 843 -468\nRAW_Data: 839 -472 843 -472 381 -916 843 -462 415 -918 387 -910 427 -870 435 -894 851 -456 415 -914 419 -880 413 -912 411 -880 441 -878 439 -880 401 -914 865 -8224 417 -908 843 -468 407 -904 839 -480 857 -448 421 -886 853 -448 417 -918 841 -472 413 -886 845 -462 867 -466 841 -470 381 -918 847 -460 415 -914 409 -878 413 -908 427 -906 825 -446 441 -902 409 -902 411 -908 397 -894 409 -900 417 -898 419 -914 843 -8246 425 -872 861 -480 399 -898 865 -448 867 -452 411 -880 881 -452 409 -908 845 -456 409 -914 835 -462 873 -432 877 -432 415 -904 869 -434 413 -916 417 -874 441 -880 411 -910 853 -448 411 -912 425 -872 451 -868 419 -914 419 -878 415 -908 411 -910 841 -8226 455 -886 843 -452 449 -878 863 -452 855 -446 415 -886 877 -434 415 -916 843 -462 415 -882 881 -454 845 -460 845 -460 411 -916 845 -456 411 -916 387 -910 425 -872 449 -870 875 -430 445 -880 413 -910 413 -878 441 -868 441 -872 441 -872 441 -902 841 -8246 421 -892 853 -452 443 -872 867 -448 849 -450 415 -926 847 -446 413 -898 877 -444 413 -900 845 -472 839 -470 839 -474 409 -902 843 -470 411 -872 445 -874 411 -904 411 -906 875 -432 413 -908 417 -884 423 -910 399 -898 407 -922 411 -880 445 -878 851 -8250 423 -904 857 -448 397 -902 853 -454 881 -430 443 -878 843 -478 431 -880 867 -450 423 -888 849 -448 843 -476 845 -474 413 -906 843 -470 381 -904 415 -914 417 -880 423 -902 837 -484 411 -878 425 -906 399 -896 405 -922 411 -916 381 -910 415 -910 841 -8232 453 -870 875 -430 447 -878 875 -442 841 -478 397 -912 861 -450 423 -886 845 -474 413 -906 841 -470 843 -470 839 -470 381 -916 843 -462 411 -918 387 -910 425 -902 411 -908 851 -450 411 -882 419 -914 417 -880 415 -908 415 -910 411 -878 439 -878 865 -8232 455 -882 845 -452 443 -880 861 -452 849 -448 417 -902 843 -474 413 -888 873 -430 411 -904 871 -434 869 -470 839 -472 409 -904 839 -470 413 -888 417 -906 411 -874 441 -876 869 -456 409 -886 419 -912 425 -870 451 -866 417 -908 415 -906 415 -872 877 -8248 407 -906 853 -446 411 -906 869 -452 847 -458 417 -880 883 -452 387 -912 851 -450 423 -906 853 -448 223 -124912 1217 -330 99 -132 163 -66 2031 -64 59635 -66 2765 -98 26085 -98 4367 -100 563 -66 24625 -66 1849 -66 10545 -68 21363 -66 15231 -100 42927 -100 3021 -66 55509 -100 4287 -66 2435 -98 1017 -132 3371 -66 11541 -100 1265 -100 799 -66 1859 -68 3385 -66 965 -100 2221 -100 2355 -68 6165 -66 1025 -100\nRAW_Data: 899 -66 173525 -166 2373 -68 397 -66 40101 -66 1891 -66 10969 -132 2617 -66 20187 -66 10559 -100 10491 -98 35135 -66 2287 -68 2649 -66 3393 -98 12375 -100 1265 -100 5605 -66 9997 -68 435 -132 16583 -100 3003 -66 10083 -64 13329 -66 64855 -100 13269 -132 10773 -100 4673 -17250 97 -1358 297 -164 165 -132 199 -66 199 -66 165 -664 65 -98 265 -66 201 -132 531 -132 235 -66 3355 -68 2757 -98 16517 -66 12659 -66 3773 -66 26419 -66 12433 -66 531 -100 20227 -66 1851 -66 11499 -64 10717\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/scher_khan_magic_code.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 95 -240 191 -412 187 -152 95 -120 263 -220 373 -116 199 -88 239 -334 503 -142 167 -174 85 -172 143 -86 85 -96 119 -196 259 -96 167 -148 201 -96 481 -120 237 -264 143 -144 105 -790 257 -88 243 -144 143 -86 553 -216 103 -174 143 -142 119 -144 119 -224 307 -144 239 -120 127 -390 189 -168 119 -312 191 -142 215 -96 103 -86 307 -274 191 -104 143 -96 167 -120 357 -390 651 -218 197 -170 143 -164 85 -210 143 -116 143 -86 255 -160 95 -114 199 -88 545 -288 173 -114 115 -202 85 -316 145 -144 113 -562 105 -254 85 -144 287 -116 115 -202 87 -346 287 -244 103 -370 143 -230 143 -144 85 -88 113 -230 231 -984 119 -144 171 -144 201 -174 115 -288 113 -192 277 -114 345 -202 249 -316 83 -228 119 -146 151 -116 85 -202 85 -144 437 -272 115 -144 85 -86 173 -142 163 -260 205 -280 339 -88 229 -194 363 -144 585 -394 599 -144 85 -100 151 -574 171 -174 85 -114 239 -288 211 -410 115 -144 489 -116 115 -116 201 -336 95 -96 93 -120 95 -128 115 -116 257 -174 141 -292 113 -162 165 -192 287 -192 161 -192 1125 -202 85 -222 461 -144 113 -116 115 -114 201 -288 259 -288 401 -174 85 -106 119 -96 431 -130 85 -114 375 -494 95 -96 167 -288 281 -104 411 -214 335 -144 119 -534 85 -144 143 -144 229 -246 97 -82 85 -260 201 -304 403 -116 391 -172 229 -448 81 -82 95 -116 201 -86 521 -144 119 -336 119 -142 95 -144 119 -96 267 -170 283 -288 95 -190 167 -288 199 -116 247 -144 87 -144 85 -114 215 -260 115 -116 113 -338 103 -240 167 -96 119 -120 95 -96 405 -188 85 -116 191 -334 143 -164 85 -230 115 -154 119 -166 143 -288 195 -144 87 -86 343 -232 113 -202 115 -86 201 -278 201 -86 85 -86 303 -286 95 -120 95 -168 167 -118 129 -94 95 -432 151 -218 119 -186 235 -230 167 -632 143 -144 231 -156 153 -282 147 -96 143 -592 261 -120 191 -96 167 -576 333 -342 171 -462 173 -514 157 -432 87 -312 397 -378 287 -374 403 -230 117 -264 257 -170 189 -346 137 -528 117 -240 805 -454 539 -288 171 -86 173 -286 153 -288 115 -172 173 -158 399 -114 85 -116 201 -116 85 -216 215 -432 113 -100 113 -338 123 -146 289 -86 143 -114 343 -94 95 -96 311 -110 171 -86 143 -88 969 -312 119 -316 401 -134 255 -202 323 -86 87 -114 253 -228 95 -172 85 -170 165 -86 343 -192 119 -144 213 -112 191 -334 287 -96 359 -500 137 -120 261 -242 119 -118 283 -210 167 -178 287 -174 343 -268\nRAW_Data: 185 -96 119 -978 215 -120 213 -96 265 -172 261 -168 225 -462 307 -312 87 -172 115 -288 423 -84 87 -230 143 -404 109 -114 233 -216 165 -240 95 -288 95 -192 215 -238 335 -116 199 -170 135 -144 109 -408 285 -130 97 -98 115 -86 85 -86 87 -120 167 -144 119 -264 171 -316 231 -202 257 -274 115 -114 421 -86 85 -202 143 -144 201 -86 229 -86 143 -116 115 -86 115 -114 85 -202 143 -288 163 -186 483 -202 167 -212 373 -288 115 -86 171 -116 113 -230 231 -230 201 -172 171 -318 113 -390 101 -200 317 -404 287 -220 171 -232 199 -490 171 -144 173 -168 223 -202 173 -200 115 -172 119 -288 209 -220 85 -86 85 -480 423 -450 201 -364 411 -216 143 -120 219 -538 513 -330 199 -120 119 -96 127 -138 141 -140 117 -312 429 -182 259 -422 1027 -280 303 -210 131 -168 85 -86 201 -230 547 -174 113 -88 259 -202 109 -96 215 -144 291 -168 151 -316 85 -258 287 -260 279 -302 171 -284 189 -168 95 -96 309 -240 167 -384 165 -96 169 -226 95 -216 271 -122 195 -102 113 -116 339 -168 85 -192 583 -114 297 -406 167 -326 209 -212 119 -262 143 -86 87 -114 125 -172 115 -172 173 -576 219 -192 335 -120 85 -116 171 -202 711 -116 85 -172 373 -232 381 -144 95 -244 173 -210 119 -198 85 -86 143 -312 211 -96 115 -86 113 -528 103 -150 191 -144 119 -98 333 -172 215 -114 87 -86 529 -490 85 -356 239 -374 143 -260 457 -312 157 -394 131 -116 353 -172 229 -490 171 -144 189 -166 305 -144 85 -288 201 -356 147 -106 167 -740 143 -312 95 -242 137 -88 465 -126 239 -358 167 -120 1385 -130 385 -192 95 -120 817 -218 805 -140 171 -394 143 -86 371 -430 143 -216 187 -84 143 -316 173 -86 115 -202 85 -114 215 -96 167 -316 107 -368 313 -120 95 -382 197 -216 289 -388 119 -264 109 -86 113 -202 963 -172 111 -104 113 -346 115 -266 255 -88 229 -202 109 -288 357 -348 365 -226 381 -144 455 -144 901 -532 531 -108 103 -142 225 -144 143 -230 85 -144 141 -142 645 -82 167 -116 143 -172 403 -230 85 -144 87 -288 229 -144 115 -144 371 -82 107 -306 333 -116 113 -88 143 -86 229 -174 617 -96 677 -286 95 -240 95 -168 141 -216 95 -212 209 -120 95 -144 95 -240 165 -116 765 -230 229 -88 141 -192 97 -86 111 -114 231 -172 143 -144 365 -96 291 -144 129 -144 87 -168 141 -120 215 -144 143 -238 215 -192 95 -96 95 -144 403 -216 163 -102 155 -120 95 -120 263 -120 403 -374 431 -230 303 -134 791 -288\nRAW_Data: 201 -138 251 -262 95 -376 287 -230 143 -114 143 -144 259 -214 231 -238 167 -288 95 -166 119 -120 411 -144 143 -546 243 -260 143 -404 113 -450 137 -184 143 -230 375 -144 85 -230 129 -104 133 -112 171 -258 115 -316 259 -86 163 -96 95 -88 113 -350 167 -780 847 -174 143 -404 85 -314 109 -278 287 -86 101 -172 201 -258 115 -86 143 -144 115 -114 547 -86 423 -116 85 -144 507 -144 133 -518 85 -424 167 -246 143 -96 261 -192 109 -240 167 -240 117 -168 239 -192 85 -116 137 -264 143 -304 119 -96 191 -144 141 -264 95 -96 119 -144 95 -96 121 -478 229 -88 575 -230 85 -312 81 -102 103 -390 93 -96 119 -144 143 -238 215 -288 277 -96 115 -114 87 -288 113 -120 143 -114 261 -146 153 -116 335 -144 259 -136 85 -258 87 -144 85 -86 661 -86 161 -120 95 -110 191 -86 135 -408 103 -144 125 -192 119 -336 113 -86 173 -114 173 -216 335 -116 321 -186 559 -116 229 -316 115 -512 141 -168 191 -130 113 -120 143 -288 127 -174 215 -96 85 -318 1169 -174 85 -86 563 -146 315 -162 163 -96 339 -222 215 -230 229 -88 109 -218 493 -120 189 -264 253 -466 239 -240 157 -288 219 -268 309 -192 121 -156 241 -192 215 -144 95 -168 261 -86 397 -428 569 -88 411 -268 251 -168 167 -268 143 -288 453 -144 163 -86 115 -144 113 -296 81 -224 163 -458 171 -310 161 -88 85 -316 461 -508 201 -162 229 -114 145 -144 143 -86 143 -114 259 -144 345 -346 85 -124 143 -302 83 -116 113 -96 261 -84 297 -116 201 -240 143 -120 861 -144 117 -158 167 -96 215 -454 273 -304 239 -96 95 -204 189 -346 345 -286 545 -88 339 -104 171 -284 95 -360 239 -144 289 -96 95 -120 225 -106 197 -144 219 -208 145 -144 187 -282 95 -408 95 -338 187 -250 229 -86 115 -402 87 -86 143 -202 283 -98 329 -134 167 -88 229 -172 221 -138 169 -202 171 -196 111 -108 85 -144 479 -100 623 -212 133 -116 113 -144 403 -88 257 -462 287 -144 373 -210 239 -144 115 -172 289 -168 143 -98 119 -96 95 -96 357 -96 119 -216 143 -144 311 -192 95 -166 175 -416 351 -100 145 -94 119 -360 427 -94 95 -144 263 -144 115 -366 97 -98 95 -190 95 -264 541 -288 131 -140 85 -202 409 -226 229 -518 143 -202 363 -260 85 -86 269 -142 95 -120 215 -168 189 -208 239 -96 285 -456 129 -144 517 -114 589 -344 115 -288 235 -98 105 -282 109 -278 85 -312 171 -116 287 -316 87 -172 335 -430 119 -274 143 -164 229 -216 167 -232 343 -260\nRAW_Data: 143 -86 565 -144 87 -460 115 -508 363 -86 143 -276 671 -144 115 -86 515 -116 199 -236 185 -140 143 -82 113 -116 85 -116 85 -408 95 -284 199 -88 315 -116 143 -114 717 -202 85 -86 173 -86 201 -144 333 -200 115 -86 85 -174 401 -174 263 -244 229 -86 143 -434 229 -202 231 -164 143 -86 165 -182 167 -172 85 -346 771 -252 81 -106 217 -96 119 -96 287 -140 85 -86 143 -374 143 -116 85 -114 83 -118 119 -164 201 -86 153 -166 359 -116 283 -86 187 -120 459 -280 201 -144 229 -258 143 -408 427 -144 373 -294 197 -120 287 -96 119 -264 143 -120 119 -528 95 -168 119 -192 117 -558 97 -148 143 -120 185 -188 167 -264 95 -176 215 -254 201 -258 263 -158 215 -168 119 -312 873 -388 95 -192 359 -278 341 -96 171 -116 143 -432 173 -316 403 -84 85 -144 113 -174 287 -346 243 -216 95 -394 161 -144 317 -200 199 -140 95 -86 143 -278 139 -86 143 -144 201 -114 191 -264 119 -238 163 -114 143 -766 143 -144 167 -604 349 -390 459 -86 201 -172 633 -114 549 -432 339 -114 201 -174 325 -220 287 -116 123 -264 137 -86 173 -336 171 -422 133 -144 661 -86 163 -96 93 -96 215 -168 105 -114 259 -238 115 -86 315 -86 229 -298 167 -168 319 -432 189 -336 167 -120 123 -250 95 -144 167 -346 97 -96 429 -86 517 -120 95 -480 85 -86 307 -84 109 -112 121 -136 227 -112 115 -114 85 -202 201 -230 201 -88 143 -144 229 -96 347 -172 143 -656 201 -508 199 -174 139 -192 171 -172 257 -86 223 -216 143 -144 215 -120 85 -264 185 -212 271 -112 85 -374 85 -116 113 -88 113 -174 431 -202 315 -264 87 -116 373 -336 201 -172 173 -374 201 -172 115 -114 115 -86 115 -86 489 -280 119 -86 115 -144 137 -144 253 -480 1225 -1598 631 -828 709 -782 689 -792 1063 -1152 1105 -1066 1141 -1076 761 -734 1125 -1090 761 -696 765 -698 817 -672 1183 -1012 1197 -1022 819 -686 775 -710 775 -680 835 -646 839 -646 1181 -1018 801 -710 1139 -1078 1151 -1066 813 -646 843 -644 843 -622 841 -650 1187 -1014 1205 -1018 825 -656 829 -652 1197 -1024 1197 -1016 1209 -990 1167 -1074 789 -676 1179 -1032 831 -644 839 -644 843 -650 813 -678 795 -684 769 -712 775 -708 777 -728 783 -676 817 -646 1205 -1006 1219 -998 845 -632 1189 -1044 1177 -980 1527 -1456 1483 -1440 1537 -1388 839 -672 817 -648 843 -676 1127 -1088 1177 -1022 1199 -1020 817 -648 1213 -1010 831 -624 833 -648 839 -646 1213 -1018 1191 -1022 815 -678 783 -710 779 -710 769 -684 773 -734 1115 -1078 785 -710 1147 -1068\nRAW_Data: 1183 -992 855 -630 827 -664 839 -646 815 -644 1209 -1014 1191 -1050 765 -708 771 -738 1113 -1080 1181 -1006 1199 -1020 1185 -1042 815 -646 1181 -1032 827 -668 811 -670 815 -648 839 -676 769 -714 779 -710 773 -712 775 -708 777 -676 835 -644 1213 -1018 1143 -1078 763 -702 1177 -1008 1199 -994 1575 -1376 1525 -1428 1503 -1438 779 -718 817 -646 843 -650 1209 -1012 1179 -1024 1201 -1016 843 -644 1181 -1032 831 -622 831 -668 839 -644 1181 -1034 1197 -1024 811 -684 781 -712 771 -712 775 -680 775 -706 1177 -1024 807 -710 1137 -1076 1159 -1014 831 -652 835 -646 841 -646 839 -644 1183 -1040 1193 -996 837 -656 833 -686 1143 -1074 1161 -1016 1195 -1026 1185 -1012 831 -648 1207 -996 831 -684 771 -710 775 -702 809 -676 817 -646 817 -672 817 -672 817 -674 767 -734 771 -710 1137 -1076 1131 -1066 803 -642 1209 -1020 1197 -1018 1521 -1392 1549 -1416 1519 -1392 815 -670 841 -678 761 -736 1119 -1092 1155 -1018 1217 -1022 789 -702 1183 -1016 815 -648 815 -674 819 -648 1211 -1012 1199 -1022 833 -684 749 -712 769 -736 751 -678 829 -668 1179 -1016 797 -710 1139 -1078 1133 -1090 817 -644 819 -674 815 -676 789 -676 1153 -1064 1179 -1020 801 -712 773 -708 1141 -1076 1133 -1068 1187 -992 1197 -1046 793 -648 1209 -1012 829 -668 815 -674 819 -648 841 -674 761 -706 761 -732 767 -712 769 -712 777 -678 833 -648 1205 -1016 1161 -1074 817 -624 1207 -1006 1197 -996 1575 -1358 1573 -1386 1547 -1380 835 -686 775 -710 773 -704 1203 -996 1195 -1042 1187 -1018 831 -656 1195 -1026 811 -654 765 -736 771 -712 1139 -1078 1153 -1034 835 -646 817 -670 817 -650 841 -676 761 -704 1149 -1062 809 -646 1211 -1016 1191 -1024 839 -678 771 -710 775 -686 779 -712 1137 -1080 1129 -1040 835 -646 841 -646 1211 -1016 1193 -1022 1179 -1014 1197 -1022 835 -658 1143 -1068 817 -648 843 -674 765 -712 777 -712 769 -710 773 -704 781 -674 839 -648 845 -646 815 -674 1179 -1034 1201 -994 793 -714 1141 -1074 1159 -988 1557 -1404 1559 -1386 1509 -1438 809 -668 843 -674 813 -676 1135 -1070 1159 -1020 1169 -1072 817 -648 1207 -1012 839 -622 841 -646 843 -646 1179 -1034 1197 -1024 821 -686 749 -714 775 -710 775 -702 779 -694 1181 -1018 801 -708 1139 -1076 1127 -1088 811 -646 845 -646 841 -624 843 -650 1181 -1036 1203 -994 835 -658 829 -656 1191 -1050 1177 -1012 1179 -1000 1203 -1016 841 -646 1211 -1018 775 -710 777 -730 759 -702 815 -644 843 -646 817 -674 815 -678 785 -712 753 -734 773 -712 1115 -1076 1155 -1038 833 -648 1203 -994 1211 -1018 1519 -1388 1573 -1362 2045 -2332 103 -86 691 -116 229 -144 239 -120 135 -216 93 -202 315 -202\nRAW_Data: 231 -432 229 -114 403 -116 569 -86 575 -404 133 -120 239 -96 95 -96 93 -240 95 -144 95 -96 95 -168 141 -250 431 -174 113 -116 401 -116 173 -144 143 -172 85 -230 115 -260 137 -96 129 -86 605 -172 257 -168 229 -314 113 -88 257 -466 197 -96 81 -330 139 -86 85 -260 277 -200 87 -202 229 -88 219 -168 109 -86 119 -96 143 -216 143 -364 523 -118 95 -120 225 -330 83 -170 115 -86 81 -302 125 -256 95 -144 117 -96 95 -96 119 -168 167 -386 173 -132 515 -232 277 -144 229 -376 229 -202 455 -262 175 -88 171 -174 85 -86 85 -508 143 -174 315 -130 105 -202 115 -114 173 -432 111 -144 221 -162 273 -118 143 -380 257 -356 315 -236 119 -124 325 -214 115 -530 193 -168 105 -86 109 -144 119 -352 93 -96 119 -216 387 -224 197 -120 463 -144 93 -1134 85 -86 259 -86 201 -192 173 -198 185 -144 307 -200 85 -116 115 -192 95 -104 143 -202 229 -120 285 -120 239 -144 143 -392 519 -296 95 -144 229 -202 85 -202 85 -116 95 -120 101 -200 119 -96 95 -96 95 -172 119 -142 143 -216 81 -316 119 -144 269 -286 167 -130 231 -324 221 -96 167 -240 119 -102 113 -116 143 -192 191 -288 143 -144 309 -114 187 -378 231 -172 171 -458 413 -430 201 -120 469 -94 143 -504 261 -96 95 -144 237 -120 455 -334 163 -116 191 -280 169 -288 99 -492 85 -458 141 -260 171 -244 713 -168 359 -96 195 -154 733 -168 119 -364 143 -88 171 -86 85 -116 339 -86 173 -334 359 -144 95 -540 95 -120 185 -144 317 -114 87 -86 113 -254 373 -86 345 -288 85 -96 151 -122 429 -202 113 -342 247 -202 139 -86 229 -172 143 -88 87 -296 755 -220 447 -330 259 -330 241 -118 95 -340 245 -194 239 -112 201 -200 367 -1020 431 -408 279 -120 95 -96 95 -166 95 -216 281 -116 549 -140 257 -166 157 -192 263 -186 95 -286 119 -170 287 -230 173 -258 343 -366 171 -144 143 -116 85 -86 115 -86 243 -422 319 -120 243 -86 115 -172 143 -116 259 -86 201 -202 257 -664 763 -356 289 -114 85 -86 113 -486 271 -144 229 -116 115 -122 423 -314 969 -144 171 -116 201 -230 101 -202 143 -200 317 -86 85 -116 85 -462 181 -172 115 -202 309 -114 287 -316 139 -430 157 -192 191 -84 137 -168 113 -116 259 -114 373 -86 201 -116 199 -88 85 -172 173 -86 169 -240 119 -174 85 -286 137 -200 143 -172 259 -244 147 -144 115 -116 201 -140 423 -172 85 -144 87 -200 87 -202 85 -86 345 -174 143 -202 315 -404 229 -144 141 -212\nRAW_Data: 119 -114 231 -114 201 -318 431 -404 201 -82 167 -88 85 -114 81 -144 85 -202 85 -134 109 -144 461 -114 189 -354 171 -226 311 -152 115 -178 117 -360 239 -240 143 -286 143 -288 119 -268 315 -82 163 -196 173 -288 173 -192 215 -118 489 -88 143 -230 477 -110 459 -172 115 -172 403 -282 113 -116 143 -288 143 -172 139 -144 95 -214 167 -484 191 -444 95 -96 95 -202 117 -168 167 -120 325 -172 239 -202 229 -144 85 -404 115 -86 85 -528 143 -166 83 -172 403 -144 115 -144 507 -258 403 -156 171 -192 95 -120 293 -166 115 -86 547 -230 143 -192 315 -340 129 -84 229 -86 221 -202 143 -86 215 -178 85 -288 143 -164 141 -96 95 -144 287 -120 189 -216 143 -144 533 -192 85 -260 85 -86 115 -244 129 -134 229 -86 287 -174 171 -346 373 -86 229 -260 469 -100 83 -84 143 -118 95 -100 143 -116 431 -124 115 -114 345 -114 81 -314 199 -144 173 -192 95 -114 85 -88 171 -278 239 -288 171 -86 287 -202 201 -114 219 -336 199 -382 145 -144 121 -288 167 -240 299 -188 161 -96 117 -96 239 -120 213 -96 119 -336 127 -88 171 -390 163 -202 201 -216 119 -144 95 -144 157 -86 655 -206 699 -410 111 -126 135 -88 85 -172 115 -576 85 -116 85 -518 143 -86 87 -266 149 -120 129 -168 215 -110 167 -96 119 -168 95 -118 269 -306 201 -202 513 -96 99 -346 229 -288 115 -216 147 -172 173 -230 211 -124 87 -364 85 -172 259 -230 87 -114 143 -140 237 -88 85 -120 215 -238 95 -718 129 -214 167 -168 95 -262 665 -204 215 -418 97 -142 143 -288 95 -144 191 -168 301 -86 115 -202 113 -144 257 -116 173 -288 285 -198 85 -374 539 -244 119 -432 259 -400 175 -302 87 -402 259 -134 171 -88 113 -144 479 -202 85 -260 335 -266 131 -144 147 -144 143 -230 287 -144 231 -518 113 -144 143 -174 167 -172 201 -86 201 -230 171 -194 135 -86 345 -220 259 -144 85 -316 135 -406 191 -262 119 -96 113 -116 143 -144 143 -86 229 -260 115 -142 223 -318 85 -172 201 -86 373 -88 287 -490 105 -144 573 -116 695 -86 113 -82 209 -190 231 -172 605 -86 85 -646 143 -168 209 -168 153 -86 205 -144 143 -238 701 -262 163 -186 191 -96 197 -432 87 -114 341 -202 227 -120 335 -118 95 -240 191 -118 147 -144 87 -144 143 -172 115 -148 119 -130 345 -172 115 -358 493 -250 85 -172 167 -144 81 -548 143 -96 309 -282 159 -130 119 -96 95 -146 311 -86 191 -144 189 -120 155 -84 103 -96 165 -384 287 -120 527 -454\nRAW_Data: 115 -138 349 -212 143 -116 143 -748 143 -148 95 -168 535 -164 403 -316 201 -144 85 -86 105 -120 95 -144 453 -106 167 -144 213 -192 365 -110 143 -96 119 -120 191 -88 229 -114 173 -144 95 -120 107 -462 173 -200 335 -266 377 -118 167 -568 85 -144 85 -318 247 -174 223 -288 123 -128 143 -336 399 -220 263 -192 161 -144 85 -116 257 -260 229 -174 143 -230 115 -172 115 -258 285 -172 143 -142 153 -432 143 -142 145 -96 267 -202 143 -120 95 -142 167 -116 143 -172 287 -116 171 -116 115 -230 315 -86 423 -360 95 -352 253 -96 407 -96 143 -478 311 -168 167 -164 85 -202 85 -202 85 -114 459 -202 421 -230 85 -174 85 -116 85 -86 85 -374 311 -302 171 -116 201 -172 575 -190 373 -116 403 -432 143 -202 115 -86 113 -290 167 -84 243 -172 345 -88 201 -200 113 -312 99 -112 155 -294 171 -430 239 -220 85 -336 95 -96 117 -144 215 -120 95 -144 287 -310 311 -374 85 -174 171 -86 133 -116 153 -102 143 -144 215 -250 85 -512 201 -116 267 -120 279 -356 213 -120 191 -110 335 -144 95 -216 191 -142 95 -216 119 -190 191 -96 95 -240 191 -164 333 -170 267 -120 95 -192 165 -158 229 -202 85 -144 259 -174 191 -116 113 -86 113 -258 173 -144 113 -174 257 -88 417 -110 169 -144 87 -114 173 -114 221 -94 105 -288 85 -88 85 -144 259 -214 149 -258 115 -86 85 -86 403 -192 95 -306 231 -144 113 -86 115 -200 115 -260 171 -236 95 -240 113 -312 197 -166 191 -168 95 -96 165 -96 167 -120 287 -138 339 -216 117 -98 105 -110 597 -144 359 -186 283 -96 261 -312 127 -140 113 -318 171 -174 143 -318 143 -200 345 -576 143 -116 143 -144 229 -316 143 -346 229 -786 85 -316 229 -244 339 -96 603 -304 173 -432 543 -84 111 -116 113 -466 103 -150 141 -88 381 -104 273 -98 215 -318 143 -100 87 -114 85 -174 201 -86 345 -144 521 -172 113 -86 85 -164 199 -174 85 -202 575 -114 87 -144 257 -146 199 -246 237 -274 329 -184 147 -96 311 -130 195 -312 93 -240 163 -140 141 -168 119 -168 455 -144 259 -202 85 -288 143 -174 113 -260 171 -174 633 -440 243 -250 391 -88 287 -144 143 -144 191 -86 239 -142 263 -246 105 -120 143 -142 263 -96 215 -168 85 -260 87 -480 277 -86 143 -144 403 -114 85 -462 767 -200 85 -174 315 -94 147 -408 85 -260 171 -112 143 -114 903 -116 201 -116 115 -86 173 -344 87 -114 115 -288 143 -288 85 -86 365 -230 143 -316 201 -86 569 -202 171 -202 143 -442\nRAW_Data: 375 -172 211 -96 85 -264 429 -168 119 -116 163 -148 239 -168 95 -192 215 -230 95 -216 119 -144 167 -364 115 -316 115 -346 85 -116 229 -288 103 -480 305 -116 143 -86 85 -304 261 -168 167 -96 141 -144 95 -144 455 -106 113 -144 87 -86 229 -202 143 -316 143 -232 199 -174 233 -216 547 -278 143 -116 345 -288 369 -290 153 -316 305 -230 143 -116 545 -230 287 -168 237 -120 95 -370 201 -288 143 -258 173 -138 361 -82 345 -288 197 -168 165 -240 143 -186 263 -216 195 -202 171 -144 465 -194 501 -192 159 -172 143 -144 85 -192 253 -96 219 -300 205 -86 143 -86 311 -120 269 -634 185 -116 113 -456 97 -96 85 -174 85 -288 115 -144 173 -114 245 -144 119 -120 119 -96 93 -138 199 -144 173 -88 315 -202 157 -120 143 -96 167 -120 271 -268 287 -86 231 -144 143 -144 113 -346 259 -104 441 -204 249 -272 231 -114 345 -116 85 -86 85 -142 127 -100 169 -156 119 -96 167 -238 141 -86 143 -230 115 -114 115 -202 137 -168 143 -312 167 -110 193 -338 281 -230 721 -124 95 -120 309 -110 143 -144 563 -460 171 -88 143 -490 201 -144 85 -232 171 -144 201 -144 171 -144 95 -124 109 -572 633 -316 215 -264 161 -236 215 -202 115 -86 229 -260 257 -144 173 -172 259 -86 171 -264 129 -202 503 -350 161 -112 195 -144 401 -116 201 -230 287 -144 191 -96 167 -284 161 -164 119 -224 143 -376 209 -82 85 -144 201 -116 403 -114 231 -238 107 -174 85 -86 387 -200 173 -86 85 -116 87 -200 201 -174 287 -114 623 -86 143 -260 567 -108 113 -246 99 -396 115 -168 137 -396 85 -116 171 -86 87 -114 231 -144 235 -196 85 -144 305 -126 95 -218 417 -330 133 -116 85 -346 371 -134 345 -114 173 -372 111 -144 345 -402 115 -144 85 -86 87 -144 517 -86 413 -86 403 -120 431 -214 119 -264 191 -374 195 -86 287 -86 143 -608 143 -120 95 -216 263 -96 167 -120 113 -336 157 -192 285 -136 287 -200 87 -292 119 -216 109 -168 253 -118 167 -96 309 -96 191 -264 117 -170 119 -96 95 -406 167 -96 95 -120 167 -188 185 -96 417 -120 759 -144 85 -144 115 -86 173 -202 411 -188 263 -306 259 -288 229 -116 113 -88 171 -116 201 -114 365 -220 119 -398 181 -124 143 -202 85 -288 143 -288 229 -86 87 -114 229 -86 287 -88 143 -114 115 -258 287 -260 143 -86 335 -86 267 -240 119 -144 95 -238 119 -96 119 -246 405 -144 105 -144 147 -222 117 -110 129 -202 171 -86 229 -174 85 -96 109 -108 81 -86 95 -216\nRAW_Data: 247 -86 113 -144 459 -140 115 -86 143 -144 229 -652 273 -96 81 -114 115 -172 87 -594 115 -230 85 -462 85 -86 85 -86 287 -240 263 -96 85 -458 363 -480 95 -192 119 -120 167 -288 105 -288 201 -172 201 -172 345 -144 115 -312 255 -144 167 -112 155 -96 167 -134 173 -114 201 -86 105 -114 347 -86 201 -144 173 -258 143 -260 215 -234 173 -114 229 -174 117 -168 335 -114 231 -114 191 -216 589 -120 141 -96 95 -168 335 -150 201 -230 259 -268 125 -100 287 -114 85 -174 201 -288 161 -166 125 -144 345 -116 575 -336 237 -120 421 -96 95 -96 119 -96 543 -258 115 -86 85 -116 85 -202 257 -202 409 -84 169 -114 115 -374 173 -374 85 -144 215 -168 195 -116 345 -230 173 -114 85 -398 81 -86 143 -144 345 -114 115 -86 173 -222 93 -116 113 -86 375 -114 153 -326 401 -116 87 -288 143 -460 143 -168 95 -168 253 -216 469 -96 167 -96 265 -244 111 -114 1439 -114 115 -140 119 -134 201 -144 199 -346 229 -202 489 -288 459 -222 81 -172 229 -288 335 -198 201 -200 143 -140 119 -134 383 -216 579 -86 85 -288 167 -116 143 -312 287 -192 113 -174 403 -460 287 -308 545 -284 333 -114 119 -190 145 -114 115 -96 153 -220 539 -192 829 -162 201 -216 143 -258 143 -116 171 -86 115 -86 115 -638 213 -120 431 -192 287 -144 239 -94 167 -192 95 -120 417 -162 95 -96 119 -142 167 -120 357 -168 431 -214 81 -516 197 -114 361 -86 257 -136 141 -288 87 -86 143 -288 143 -86 115 -172 143 -174 113 -174 85 -144 171 -144 143 -144 85 -116 257 -116 219 -176 949 -342 107 -140 115 -144 359 -142 95 -120 119 -240 95 -96 167 -96 95 -164 185 -172 197 -112 101 -116 171 -144 229 -202 85 -202 115 -114 263 -168 143 -122 213 -216 95 -96 381 -168 319 -202 171 -230 271 -278 259 -278 95 -220 489 -144 259 -132 315 -202 113 -86 143 -144 287 -174 393 -116 173 -346 143 -262 129 -142 119 -86 115 -138 399 -230 257 -422 315 -306 147 -96 285 -192 455 -130 263 -144 113 -144 461 -278 369 -120 97 -136 81 -144 113 -232 85 -258 317 -144 633 -86 173 -316 119 -192 163 -96 115 -86 173 -518 85 -174 285 -108 107 -488 201 -264 93 -232 85 -240 215 -124 215 -344 169 -94 143 -120 119 -120 179 -140 255 -146 119 -280 141 -316 143 -418 147 -442 127 -174 201 -172 115 -172 85 -346 115 -144 85 -144 259 -172 201 -316 87 -86 127 -96 263 -192 173 -114 367 -120 133 -552 313 -144 143 -96 85 -260 109 -614\nRAW_Data: 333 -140 141 -114 287 -394 257 -88 145 -314 113 -86 113 -178 367 -198 199 -136 229 -662 489 -174 181 -134 171 -82 201 -288 367 -196 85 -88 171 -172 259 -116 85 -144 505 -260 85 -318 489 -114 143 -116 143 -172 115 -116 401 -278 167 -96 335 -120 143 -168 249 -184 95 -86 229 -144 187 -114 87 -86 171 -398 461 -326 95 -96 287 -112 181 -124 115 -178 95 -106 85 -192 109 -120 311 -470 95 -190 825 -114 317 -86 113 -146 113 -174 199 -88 85 -192 261 -332 85 -86 115 -116 661 -86 115 -114 287 -144 259 -202 143 -174 113 -322 297 -202 673 -106 111 -202 171 -232 305 -518 173 -346 113 -404 229 -86 253 -226 113 -144 487 -106 249 -302 195 -120 95 -120 311 -216 209 -192 95 -96 171 -96 95 -168 215 -214 167 -140 311 -140 83 -82 191 -336 95 -216 167 -354 107 -234 315 -88 85 -172 113 -86 315 -236 115 -172 201 -316 115 -374 141 -82 185 -190 325 -274 115 -86 143 -164 191 -86 201 -114 215 -170 95 -264 143 -96 119 -196 229 -82 289 -234 143 -264 201 -86 115 -346 229 -172 143 -202 143 -86 345 -86 173 -138 95 -102 575 -144 143 -230 221 -158 251 -168 103 -96 133 -172 173 -336 143 -188 309 -86 143 -116 85 -144 85 -306 201 -116 215 -286 283 -396 305 -148 317 -216 115 -490 115 -186 111 -144 201 -86 143 -192 285 -128 137 -86 85 -116 173 -216 119 -118 215 -96 95 -216 107 -82 173 -258 115 -168 263 -182 459 -288 143 -86 115 -490 143 -230 115 -144 229 -764 93 -112 81 -118 249 -144 229 -174 201 -374 201 -230 171 -88 223 -304 115 -200 143 -86 87 -174 85 -116 169 -172 429 -168 165 -216 603 -188 119 -120 269 -86 249 -120 141 -216 229 -174 85 -202 251 -186 87 -114 121 -254 649 -144 399 -86 201 -104 169 -162 103 -122 165 -168 119 -120 213 -84 143 -86 143 -168 173 -258 85 -232 315 -86 229 -116 95 -288 217 -124 335 -144 191 -120 333 -120 191 -216 143 -574 375 -86 311 -110 143 -202 287 -86 113 -174 345 -96 511 -88 109 -96 143 -638 133 -120 167 -360 95 -96 95 -144 143 -116 403 -480 341 -88 85 -114 115 -230 111 -226 115 -86 171 -404 259 -144 215 -288 405 -240 105 -402 255 -572 115 -168 113 -216 115 -144 143 -116 143 -374 85 -116 113 -116 287 -162 87 -86 219 -240 113 -116 863 -86 305 -126 113 -202 143 -378 143 -376 391 -88 229 -144 171 -200 259 -86 87 -144 109 -140 85 -116 327 -140 363 -114 143 -244 85 -116 335 -96 309 -352\nRAW_Data: 95 -168 219 -96 95 -168 95 -312 333 -144 117 -290 119 -94 373 -240 215 -96 229 -338 135 -202 257 -490 201 -116 85 -202 257 -144 113 -346 85 -86 289 -230 143 -144 109 -168 167 -96 189 -144 239 -114 143 -144 199 -116 85 -202 85 -174 165 -88 113 -112 315 -116 229 -164 119 -110 113 -116 115 -144 85 -202 113 -116 115 -200 115 -86 231 -114 85 -86 287 -168 93 -168 85 -88 257 -144 201 -230 715 -86 115 -144 373 -202 85 -116 85 -194 217 -144 201 -202 143 -172 143 -86 163 -202 115 -402 187 -244 251 -152 191 -336 117 -216 95 -144 253 -114 201 -456 95 -646 85 -288 85 -84 443 -576 115 -116 143 -432 115 -346 257 -334 621 -96 95 -264 171 -198 147 -452 229 -144 215 -288 93 -96 287 -96 165 -96 287 -138 777 -202 363 -190 191 -240 81 -114 119 -118 283 -138 95 -166 229 -194 119 -198 189 -192 167 -168 215 -94 119 -124 81 -196 351 -286 183 -140 969 -346 171 -586 87 -172 201 -164 267 -130 113 -144 191 -98 191 -96 243 -202 201 -174 199 -202 713 -454 95 -144 107 -144 451 -110 201 -174 143 -116 143 -144 285 -84 83 -328 117 -424 119 -144 95 -214 135 -402 115 -114 115 -174 257 -406 215 -336 193 -288 113 -116 229 -86 345 -260 113 -232 101 -264 167 -94 95 -216 401 -202 373 -114 173 -232 279 -690 259 -172 143 -404 229 -220 115 -114 173 -230 171 -318 309 -156 113 -170 223 -172 287 -174 315 -116 229 -332 111 -116 85 -258 431 -404 201 -86 171 -106 115 -96 369 -124 239 -206 115 -230 389 -200 115 -86 565 -488 83 -82 585 -172 459 -198 87 -86 85 -116 143 -202 371 -96 287 -392 221 -174 85 -230 461 -474 93 -120 95 -274 171 -144 339 -226 143 -184 81 -230 141 -216 143 -216 117 -120 167 -216 167 -96 265 -108 165 -86 315 -260 229 -172 87 -312 587 -150 341 -200 187 -432 171 -84 85 -202 85 -116 85 -116 431 -114 201 -84 169 -86 85 -114 313 -346 85 -86 85 -142 159 -646 143 -88 119 -120 521 -134 173 -128 315 -462 99 -362 227 -144 167 -120 165 -246 215 -86 825 -168 443 -536 835 -316 85 -174 113 -144 85 -144 173 -458 143 -288 81 -170 143 -268 215 -192 285 -360 113 -258 225 -168 163 -334 113 -116 257 -296 287 -312 277 -116 603 -82 255 -96 95 -336 301 -82 95 -552 141 -332 281 -110 201 -172 145 -200 115 -144 95 -344 201 -116 171 -96 119 -120 143 -144 421 -96 199 -394 107 -110 115 -114 145 -316 315 -288 115 -200 87 -114 115 -288\nRAW_Data: 85 -374 259 -260 113 -174 287 -144 507 -202 229 -144 221 -142 115 -124 143 -216 95 -96 117 -216 143 -144 85 -144 143 -144 201 -144 85 -284 469 -190 149 -86 143 -316 283 -290 95 -216 119 -180 167 -200 375 -86 111 -192 143 -144 213 -404 85 -144 719 -202 85 -434 143 -144 143 -172 287 -264 111 -114 315 -96 115 -576 85 -230 85 -144 143 -174 281 -198 201 -86 201 -144 575 -116 85 -174 143 -164 315 -168 171 -268 85 -536 111 -172 153 -462 239 -158 333 -174 361 -258 343 -96 453 -240 119 -256 113 -88 171 -172 259 -230 173 -110 113 -202 113 -88 113 -232 315 -100 165 -422 99 -316 311 -262 191 -120 555 -280 85 -174 431 -86 259 -210 119 -168 885 -462 433 -106 107 -116 113 -644 127 -278 115 -86 87 -226 159 -346 115 -144 85 -144 85 -768 541 -168 113 -86 87 -374 113 -144 747 -230 187 -268 521 -346 311 -1194 383 -478 313 -110 229 -202 171 -144 287 -722 285 -138 225 -188 133 -346 85 -174 199 -374 229 -112 135 -194 95 -216 143 -120 377 -86 245 -120 93 -96 95 -178 217 -86 277 -312 517 -530 505 -192 259 -106 119 -94 143 -336 293 -348 219 -316 87 -258 85 -174 143 -86 85 -116 171 -398 167 -88 85 -116 85 -202 459 -314 143 -202 459 -222 253 -202 171 -462 143 -490 143 -264 249 -340 147 -88 143 -144 113 -144 461 -258 85 -86 115 -202 171 -86 317 -86 115 -114 283 -86 287 -116 373 -86 287 -156 659 -158 87 -114 201 -124 95 -86 287 -172 87 -172 113 -144 663 -230 143 -162 95 -116 143 -116 113 -86 173 -356 99 -198 391 -140 191 -144 219 -172 171 -318 191 -374 201 -328 87 -230 85 -86 143 -116 195 -96 99 -226 383 -382 119 -264 431 -120 167 -120 95 -312 191 -106 85 -172 143 -520 95 -238 299 -288 359 -108 375 -172 185 -150 213 -168 119 -120 127 -146 171 -86 143 -202 115 -144 113 -202 201 -116 113 -144 365 -142 143 -22490 523 -858 969 -1190 1065 -1160 1067 -1146 683 -754 1133 -1100 747 -712 775 -708 779 -702 1125 -1102 1119 -1094 767 -698 763 -728 759 -734 755 -710 757 -738 1117 -1070 815 -648 1207 -1010 1197 -1024 835 -658 779 -710 771 -710 779 -706 1115 -1102 1133 -1066 815 -644 843 -650 1203 -1010 1207 -998 1197 -1042 1153 -1020 803 -710 1141 -1076 791 -678 785 -708 767 -712 773 -710 775 -704 779 -704 815 -646 839 -646 843 -648 813 -656 1205 -1012 1205 -1020 1193 -1020 815 -644 843 -624 1551 -1394 1553 -1364 1575 -1390 819 -672 841 -680 755 -712 1141 -1076 1185 -1016 1191 -1050 763 -704\nRAW_Data: 1183 -1014 813 -646 841 -644 843 -648 1203 -1010 1207 -996 805 -714 775 -710 775 -678 835 -648 813 -672 1181 -1016 799 -710 1139 -1076 1151 -1064 811 -646 845 -646 843 -622 843 -650 1205 -1012 1177 -1022 805 -712 775 -714 1139 -1076 1149 -1034 1205 -998 1177 -1040 811 -670 1183 -1012 855 -626 835 -674 817 -644 843 -648 843 -624 845 -674 761 -732 755 -712 779 -714 773 -710 1139 -1080 1127 -1092 1149 -1024 811 -658 831 -658 1499 -1406 1555 -1404 1557 -1408 805 -668 815 -676 817 -678 1131 -1092 1159 -1018 1213 -1022 815 -676 1131 -1092 789 -642 843 -646 841 -648 1185 -1036 1181 -1018 801 -710 775 -678 829 -668 815 -644 845 -646 1181 -1038 831 -650 1207 -992 1211 -1048 791 -650 843 -676 763 -706 763 -734 1119 -1094 1153 -1018 823 -656 829 -676 1179 -1022 1159 -1070 1179 -1006 1191 -1022 815 -706 1125 -1066 813 -648 845 -646 841 -650 815 -678 783 -710 755 -712 779 -710 769 -710 775 -706 835 -646 1181 -1020 1193 -1022 1185 -1016 833 -654 833 -624 1559 -1410 1515 -1384 1569 -1414 789 -674 843 -674 761 -730 1133 -1068 1177 -1010 1195 -1026 817 -678 1151 -1066 813 -646 817 -672 819 -646 1209 -1012 1197 -1024 835 -658 769 -710 799 -710 749 -690 835 -640 1211 -1018 777 -736 1115 -1078 1151 -1064 809 -644 841 -646 841 -624 841 -650 1201 -1010 1205 -1018 803 -712 773 -704 1145 -1076 1121 -1092 1157 -1016 1185 -1048 791 -674 1179 -1012 835 -648 841 -646 843 -646 843 -674 767 -708 769 -714 777 -714 747 -734 777 -668 843 -644 1205 -1006 1193 -1048 1177 -1012 805 -668 843 -620 1545 -1418 1521 -1388 1575 -1360 841 -672 819 -682 769 -712 1139 -1076 1157 -1040 1197 -1026 835 -658 1143 -1072 817 -620 845 -646 843 -674 1185 -988 1205 -1018 801 -712 777 -706 781 -674 841 -646 817 -648 1207 -1008 831 -652 1199 -1024 1199 -1042 817 -644 817 -648 841 -676 765 -706 1149 -1064 1179 -1020 829 -658 825 -654 1197 -1024 1179 -1012 1205 -992 1183 -1072 789 -674 1183 -1010 837 -650 841 -644 843 -646 843 -678 789 -676 763 -706 793 -710 769 -712 775 -706 781 -674 1205 -1022 1139 -1098 1157 -1014 827 -652 829 -640 1537 -1388 1567 -1388 1541 -1392 813 -698 815 -676 767 -734 1133 -1076 1131 -1040 1195 -1052 811 -678 1133 -1070 819 -620 843 -646 843 -676 1125 -1090 1179 -994 859 -632 797 -710 775 -702 839 -622 841 -644 1211 -992 801 -710 1139 -1078 1151 -1062 813 -646 843 -646 843 -622 841 -650 1205 -1010 1203 -1020 823 -658 825 -654 1195 -1022 1201 -1040 1155 -1016 1185 -1046 815 -650 1177 -1034 833 -644 843 -646 819 -674 815 -676 767 -710 769 -712 775 -710 775 -678 861 -620 841 -646 1205 -1008\nRAW_Data: 1221 -1022 1185 -1016 813 -646 841 -618 1573 -1364 1577 -1362 1811 -2658 227 -138 191 -120 333 -488 345 -86 403 -86 115 -604 283 -82 343 -202 143 -316 87 -128 311 -230 119 -264 165 -168 247 -216 199 -166 283 -230 81 -132 487 -114 115 -142 921 -260 345 -114 373 -116 115 -114 115 -166 191 -192 171 -116 417 -242 167 -240 285 -120 283 -86 257 -230 335 -244 167 -96 167 -188 161 -96 85 -232 173 -114 201 -144 363 -144 87 -86 507 -88 143 -490 103 -124 143 -460 517 -144 219 -202 173 -144 113 -318 229 -174 401 -442 143 -88 603 -144 259 -86 115 -116 113 -144 113 -142 297 -202 171 -86 201 -86 115 -374 163 -364 471 -104 169 -88 201 -144 237 -234 203 -160 113 -86 269 -96 95 -240 431 -94 295 -328 191 -144 239 -240 165 -96 119 -168 421 -238 191 -144 119 -168 143 -120 143 -96 209 -164 239 -240 335 -116 201 -316 115 -172 201 -114 115 -172 173 -86 431 -202 143 -86 229 -116 171 -86 317 -162 143 -174 113 -174 85 -228 85 -142 139 -196 305 -106 85 -232 171 -168 531 -86 85 -86 173 -172 143 -260 137 -342 257 -174 85 -100 115 -170 139 -174 829 -96 191 -192 311 -240 311 -82 211 -144 475 -114 201 -86 85 -286 239 -144 95 -264 227 -120 387 -202 251 -142 143 -316 345 -450 85 -174 315 -372 251 -258 143 -230 221 -156 431 -170 85 -114 431 -364 633 -174 421 -114 259 -86 201 -116 115 -86 229 -174 103 -96 115 -244 307 -86 345 -356 259 -450 693 -98 263 -456 191 -96 357 -116 85 -86 115 -230 201 -88 161 -518 259 -340 173 -116 315 -140 107 -218 517 -96 285 -124 95 -382 119 -144 167 -168 357 -168 119 -144 287 -142 95 -200 143 -260 201 -306 173 -604 479 -224 285 -106 507 -116 165 -408 359 -288 97 -116 225 -430 85 -144 449 -374 113 -120 95 -96 241 -106 101 -86 123 -120 117 -214 107 -144 263 -268 119 -120 141 -96 119 -216 229 -306 287 -150 135 -172 287 -114 215 -168 143 -172 115 -462 113 -144 143 -490 199 -116 403 -116 171 -590 637 -304 113 -86 143 -100 111 -534 149 -312 237 -236 143 -202 85 -202 171 -488 109 -572 201 -238 95 -174 397 -202 283 -164 87 -662 201 -244 315 -562 143 -86 325 -96 575 -190 191 -144 85 -202 189 -358 287 -618 835 -160 163 -142 85 -172 139 -144 215 -82 287 -114 87 -144 541 -438 143 -240 167 -144 359 -190 95 -120 263 -108 109 -184 215 -100 113 -288 119 -144 95 -406 81 -114 87 -256 315 -316 171 -100 105 -144 263 -302 229 -174\nRAW_Data: 113 -232 197 -176 143 -200 187 -240 537 -96 95 -118 263 -432 141 -96 119 -216 761 -282 119 -526 119 -120 185 -116 171 -168 81 -86 143 -172 173 -86 113 -144 489 -202 143 -174 287 -186 373 -168 373 -174 85 -168 119 -330 325 -174 113 -144 373 -202 143 -138 195 -174 185 -192 119 -412 115 -114 283 -120 143 -360 525 -120 119 -120 367 -116 115 -144 345 -114 87 -120 287 -240 239 -96 503 -168 459 -144 317 -242 95 -104 255 -82 227 -172 307 -314 119 -264 901 -282 171 -202 171 -346 201 -576 167 -196 201 -288 315 -116 85 -144 85 -290 143 -264 303 -532 173 -120 215 -272 603 -86 143 -86 85 -190 167 -144 431 -238 119 -96 119 -96 455 -96 285 -144 287 -618 85 -288 253 -592 105 -104 139 -188 335 -120 511 -166 431 -202 373 -86 87 -114 231 -206 83 -190 199 -288 201 -86 171 -88 195 -120 201 -116 257 -202 161 -156 229 -144 143 -298 263 -166 119 -158 115 -326 455 -166 95 -144 119 -168 141 -216 455 -334 95 -144 95 -214 143 -96 143 -196 143 -190 479 -364 287 -114 163 -86 287 -130 297 -96 167 -96 431 -144 95 -190 119 -144 141 -120 335 -144 285 -188 497 -112 429 -258 143 -140 111 -232 87 -116 143 -96 285 -204 273 -632 371 -144 85 -202 115 -172 499 -144 143 -120 119 -120 95 -360 143 -258 173 -930 315 -134 431 -144 115 -114 257 -740 373 -174 95 -144 297 -268 167 -96 143 -834 171 -168 95 -172 119 -96 85 -88 199 -88 315 -172 259 -216 311 -202 85 -260 257 -144 657 -120 119 -116 223 -286 233 -130 115 -202 85 -144 143 -172 115 -88 85 -114 95 -114 345 -288 85 -116 471 -124 173 -114 335 -260 115 -658 339 -260 113 -86 201 -480 217 -470 105 -114 259 -134 137 -86 171 -326 191 -120 263 -144 95 -148 289 -124 121 -384 95 -322 171 -142 193 -288 143 -172 211 -266 109 -144 325 -168 213 -96 685 -310 239 -262 143 -216 143 -316 393 -126 215 -216 387 -202 201 -230 143 -116 85 -144 201 -268 143 -262 311 -86 115 -316 167 -86 229 -116 335 -144 117 -302 113 -116 241 -86 143 -202 201 -116 453 -288 293 -272 367 -120 651 -170 565 -116 143 -882 557 -116 181 -588 119 -168 287 -168 119 -118 191 -168 143 -282 115 -88 171 -404 719 -202 115 -172 253 -144 143 -532 121 -144 239 -96 167 -120 119 -456 199 -114 311 -86 201 -352 111 -144 115 -114 173 -86 85 -538 113 -88 257 -116 143 -96 95 -144 121 -198 127 -172 173 -114 229 -116 143 -144 173 -368 111 -200 163 -116\nRAW_Data: 113 -292 169 -200 145 -86 85 -230 259 -86 85 -86 439 -96 85 -116 115 -86 113 -86 85 -192 191 -214 263 -264 119 -96 345 -116 85 -86 173 -288 85 -104 145 -130 85 -662 243 -390 263 -96 129 -422 223 -288 219 -172 655 -108 361 -96 271 -432 167 -120 421 -278 217 -248 143 -96 165 -240 109 -628 99 -96 95 -168 95 -216 119 -144 171 -172 115 -86 85 -174 387 -190 239 -384 143 -344 115 -114 143 -86 233 -140 85 -724 119 -82 85 -144 115 -144 115 -144 143 -114 201 -174 215 -96 109 -144 85 -144 599 -144 115 -258 249 -206 171 -86 143 -110 255 -86 85 -86 287 -208 225 -100 193 -200 143 -288 139 -460 113 -498 85 -144 143 -88 85 -144 229 -144 85 -144 171 -288 261 -202 113 -88 87 -114 143 -288 373 -366 229 -202 201 -172 173 -172 421 -280 153 -120 81 -160 137 -116 201 -250 141 -230 115 -318 863 -206 119 -462 143 -144 85 -86 201 -230 407 -96 223 -112 143 -94 95 -106 479 -86 113 -174 171 -284 119 -158 239 -190 167 -204 185 -408 215 -168 259 -202 201 -86 85 -202 87 -448 249 -332 363 -490 85 -172 115 -114 145 -114 259 -144 101 -142 119 -240 143 -188 387 -384 425 -144 173 -506 137 -162 139 -174 253 -116 131 -106 115 -130 117 -144 95 -86 239 -192 407 -120 237 -144 143 -82 487 -102 439 -112 199 -88 201 -262 269 -230 229 -192 219 -252 133 -96 191 -346 135 -288 621 -232 661 -202 327 -164 143 -144 139 -536 115 -114 143 -174 85 -100 99 -86 85 -174 85 -144 345 -172 243 -408 115 -86 115 -202 229 -114 87 -114 229 -740 801 -158 143 -168 479 -124 103 -264 119 -240 171 -88 219 -278 431 -402 513 -264 465 -256 141 -168 115 -358 251 -316 387 -172 101 -96 683 -192 95 -120 191 -120 95 -462 261 -96 121 -180 167 -878 287 -86 143 -86 311 -118 287 -168 191 -96 167 -158 85 -86 315 -434 165 -822 171 -316 231 -84 415 -174 87 -296 229 -222 93 -192 167 -190 143 -96 95 -96 237 -168 259 -86 115 -346 171 -116 115 -114 317 -114 95 -140 373 -116 143 -286 207 -260 171 -202 171 -134 119 -320 453 -96 293 -168 143 -144 119 -172 143 -202 237 -360 225 -86 287 -86 201 -202 109 -120 133 -214 215 -116 85 -260 267 -122 335 -316 111 -604 87 -172 259 -258 235 -96 173 -230 339 -140 115 -86 287 -288 143 -186 95 -96 95 -96 339 -256 147 -158 143 -246 223 -264 213 -202 85 -110 95 -168 95 -118 119 -168 95 -264 191 -316 119 -144 119 -382 167 -120\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Security+ 1.0\nBit: 42\nKey: 1C 41 D2 39 E6 A3 8B CC\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236\nRAW_Data: 265 -100 133 -1464 99 -464 67 -592 229 -362 131 -262 163 -134 325 -164 163 -130 97 -164 65 -724 97 -132 97 -958 131 -594 131 -264 161 -98 229 -100 97 -890 131 -364 99 -198 97 -262 131 -234 2497 -134 65 -68 165 -400 563 -400 433 -496 99 -100 131 -266 99 -100 65 -200 231 -894 65 -166 463 -200 201 -1228 131 -266 65 -496 99 -300 97 -202 165 -896 365 -132 163 -1698 165 -132 99 -168 67 -166 67 -796 165 -400 131 -200 165 -796 165 -98 2161 -66 65 -166 293 -228 887 -66 395 -656 297 -100 97 -530 97 -66 97 -594 65 -428 97 -332 65 -400 131 -164 261 -66 299 -334 65 -596 97 -166 99 -1184 161 -460 131 -132 229 -398 299 -664 165 -722 165 -166 263 -300 99 -232 199 -266 267 -660 97 -758 133 -530 65 -632 99 -264 329 -1028 67 -166 2451 -562 427 -464 163 -198 229 -264 229 -790 295 -164 297 -132 263 -726 267 -66 99 -364 197 -564 99 -1162 67 -960 97 -198 163 -630 133 -66 163 -132 195 -1288 197 -430 197 -1546 65 -890 97 -394 231 -924 2173 -100 65 -594 165 -100 99 -528 99 -166 99 -298 497 -66 65 -100 131 -198 265 -362 131 -198 293 -294 129 -1150 263 -262 65 -100 131 -494 231 -166 231 -468 99 -66 165 -1262 197 -298 65 -298 397 -432 297 -498 361 -200 65 -466 131 -166 101 -496 231 -364 233 -64 197 -196 65 -426 265 -198 99 -1218 97 -200 67 -232 131 -200 167 -200 99 -66 165 -928 67 -1722 99 -764 65 -892 99 -232 133 -300 133 -1080 621 -1526 487 -1504 507 -506 1517 -1524 519 -1508 509 -1512 447 -546 1503 -1038 1035 -1482 547 -976 1007 -1208 823 -490 1503 -532 1481 -1048 989 -1534 499 -524 1507 -514 1499 -1014 1007 -524 1503 -1530 473 -60198 1533 -1516 481 -1040 985 -1044 981 -1554 479 -1530 479 -1044 1007 -1502 511 -1538 483 -516 1503 -1542 473 -1016 1033 -1510 483 -1542 505 -1002 1003 -508 1521 -1010 1015 -1512 507 -526 1515 -518 1505 -1016 1007 -59156 529 -1528 463 -1580 447 -542 1507 -1544 469 -1554 481 -1512 499 -518 1507 -1016 1007 -1512 511 -1012 1021 -1010 1007 -518 1529 -472 1555 -982 1023 -1502 505 -508 1537 -488 1545 -1012 1003 -480 1531 -1516 507 -60180 493 -12296 821 -1698 299 -1686 387 -590 1465 -1570 459 -1046 979 -1546 475 -1534 489 -1044 1009 -482 1529 -1032 973 -1538 513 -480 1537 -492 1523 -1012 1017 -59154 497 -11798 301 -704 1325 -1148 913 -1612 417 -1102 897 -1118 945 -586 1437 -568 1465 -1058 977 -1534 477 -550 1495 -502 1515 -1006 1037 -494 1527 -1510 489 -60174 531 -16794 355 -640 1399 -1608 449 -1050 989 -1540 461 -1546 477 -1068 973 -518\nRAW_Data: 1503 -1024 1009 -1536 473 -552 1503 -498 1517 -1010 1017 -59162 513 -9838 227 -1670 371 -604 1425 -1116 949 -1554 477 -1030 981 -1042 973 -550 1477 -532 1511 -1010 1003 -1544 511 -496 1515 -516 1509 -1018 1007 -488 1539 -1504 503 -60216 1525 -1530 485 -1018 999 -1012 1019 -1538 475 -1546 477 -1012 1025 -1506 517 -1530 505 -510 1507 -1512 513 -1010 999 -1516 507 -1520 513 -1012 1015 -506 1527 -1014 991 -1542 483 -524 1517 -482 1529 -1008 1005 -137640 165 -1660 165 -1362 231 -3114 67 -2286 131 -1652 197 -1448 195 -1648 97 -3534 163 -988 65 -556 65 -132 133 -2594 199 -2054 65 -1918 65 -1458 163 -690 65 -1134 131 -1194 67 -1158 131 -1656 199 -198 99 -298 199 -2676 67 -2122 231 -1762 263 -1560 131 -1228 99 -198 197 -830 99 -166 97 -6274 99 -1058 163 -1676 97 -1788 65 -5742 99 -3890 131 -2682 195 -166 65 -1394 265 -432 99 -368 99 -100 67 -198 163 -564 133 -992 131 -266 133 -962 233 -200 131 -196 231 -560 131 -66 65 -764 131 -1058 133 -564 99 -1792 133 -2820 65 -3480 165 -9536 99 -66 97 -626 97 -1828 199 -1860 99 -66 197 -1060 99 -1746 65 -1690 99 -198 99 -1660 165 -1764 65 -1558 199 -1456 99 -164 65 -1318 395 -5966 131 -928 165 -1026 99 -534 65 -434 99 -100 97 -100 97 -3272 65 -2168 131 -2904 231 -1564 67 -958 263 -3780 65 -166 99 -3506 99 -1594 165 -1492 197 -164 63 -1382 197 -1162 65 -364 133 -822 65 -498 495 -230 165 -366 65 -464 231 -666 231 -198 65 -332 163 -566 65 -164 67 -332 231 -232 265 -198 97 -1254 361 -200 65 -2056 129 -430 131 -100 65 -1588 263 -198 97 -2992 97 -528 297 -134 97 -368 427 -66 67 -132 65 -132 367 -330 65 -266 229 -66 65 -66 65 -198 231 -66 133 -528 363 -162 161 -230 525 -230 65 -230 461 -132 327 -330 295 -130 197 -230 165 -270 265 -464 65 -698 265 -264 229 -400 99 -3476 97 -1490 199 -134 99 -2970 165 -1618 167 -1598 65 -1162 99 -234 167 -1162 135 -464 65 -664 165 -1126 197 -362 97 -1222 65 -3496 65 -7024 131 -1592 131 -530 97 -6464 231 -998 263 -428 65 -698 131 -198 131 -1414 63 -1346 165 -1060 65 -1664 167 -566 263 -666 165 -566 331 -100 131 -1522 167 -368 199 -896 229 -232 99 -968 99 -168 231 -66 197 -68 131 -168 133 -400 131 -268 233 -596 233 -132 97 -494 131 -1710 197 -1616 67 -592 99 -1226 267 -268 65 -266 133 -1986 165 -332 99 -366 131 -2700 97 -1398 65 -130 269 -198 65 -1664 131 -3180 165 -992 65 -866 99 -166 163 -2782 65 -2354 265 -5176 65 -66 163 -1290 67 -164 99 -2584 67 -3084\nRAW_Data: 195 -364 65 -164 229 -958 63 -166 193 -130 65 -556 99 -332 199 -430 197 -996 297 -1426 235 -1160 2053 -166 1063 -100 501 -132 535 -198 67 -66 165 -98 165 -460 365 -366 97 -432 329 -264 133 -100 165 -328 197 -360 1087 -264 97 -166 63 -166 233 -98 195 -294 97 -264 163 -266 197 -100 359 -66 65 -1958 165 -694 99 -166 99 -596 299 -466 97 -66 99 -696 231 -1492 297 -1554 165 -1680 165 -368 99 -166 99 -168 65 -794 197 -194 2581 -98 1151 -592 99 -426 197 -328 295 -164 65 -232 163 -530 165 -264 129 -98 229 -294 493 -426 99 -66 163 -164 261 -264 129 -166 229 -68 65 -558 131 -132 65 -132 197 -1550 361 -98 67 -132 99 -132 131 -786 99 -198 301 -366 165 -530 99 -234 2159 -264 1691 -166 367 -132 231 -100 197 -166 97 -366 163 -68 131 -366 165 -268 133 -430 233 -100 133 -132 199 -932 561 -1416 231 -794 199 -1296 165 -564 165 -666 99 -490 97 -760 163 -1582 295 -464 267 -330 2561 -134 931 -66 65 -132 99 -264 63 -132 99 -198 97 -364 129 -460 65 -230 263 -164 163 -100 365 -100 131 -398 97 -530 65 -266 299 -1028 133 -100 97 -166 65 -296 65 -166 133 -498 331 -962 99 -98 199 -1328 165 -200 65 -234 99 -400 231 -632 65 -232 199 -530 65 -866 429 -958 197 -368 165 -668 65 -984 163 -100 129 -1088 259 -752 97 -522 131 -892 65 -298 67 -364 199 -132 65 -788 97 -396 363 -1052 99 -228 131 -98 99 -1526 463 -330 131 -898 263 -332 97 -996 163 -494 99 -2950 65 -798 131 -2490 165 -1424 65 -1694 65 -1822 65 -1756 67 -3820 65 -2536 97 -2410 65 -1030 131 -404 263 -732 165 -1566 197 -1554 199 -400 65 -100 99 -566 165 -3584 65 -764 101 -2630 65 -2896 163 -364 67 -100 65 -1228 263 -232 63 -884 65 -4092 133 -1622 325 -166 99 -2352 65 -500 65 -1324 99 -366 65 -1592 297 -5134 131 -1130 65 -1962 99 -66 99 -1454 67 -1130 99 -134 199 -134 165 -1222 229 -166 131 -464 197 -196 263 -234 99 -534 65 -132 131 -166 133 -134 199 -1590 231 -66 131 -1160 131 -300 65 -698 199 -462 133 -3446 99 -2876 65 -596 65 -1716 133 -2886 97 -134 199 -1628 131 -1790 67 -2556 615 -1510 453 -1584 443 -1566 485 -1014 977 -1054 1003 -516 1501 -1010 1007 -520 1495 -1034 1005 -1530 481 -514 1541 -492 1523 -1012 1001 -1508 521 -1016 999 -512 1527 -1506 509 -488 1521 -1040 989 -1538 473 -60194 1519 -1540 511 -1010 1001 -1020 1009 -1516 515 -1010 1001 -518 1507 -524 1503 -1534 493 -1510 501 -1022 1011 -516 1507 -522 1507 -1512 509 -1506 519 -516\nRAW_Data: 1499 -1542 485 -492 1535 -1508 491 -1046 1005 -1008 1001 -59158 547 -1508 515 -1510 505 -1510 523 -976 1033 -1000 1031 -472 1527 -1008 1039 -482 1541 -1010 1017 -1512 485 -514 1533 -482 1527 -1000 1019 -1534 479 -1044 1011 -488 1505 -1522 511 -512 1529 -984 1013 -1522 509 -60190 1543 -1522 477 -1038 999 -1002 1029 -1504 523 -1012 1009 -482 1541 -486 1543 -1510 481 -1544 505 -1008 997 -516 1507 -520 1513 -1508 517 -1510 519 -490 1521 -1540 485 -514 1503 -1540 487 -1016 1019 -1008 1003 -59184 521 -1536 511 -1506 489 -1548 475 -1026 1011 -1014 1019 -488 1513 -1014 1043 -484 1513 -1012 1023 -1506 519 -492 1525 -520 1507 -1008 1005 -1542 479 -1012 1023 -514 1507 -1508 521 -516 1523 -1008 1009 -1516 511 -60208 1513 -1524 477 -1042 1013 -984 1011 -1516 509 -1008 1015 -522 1505 -526 1509 -1504 503 -1526 513 -1008 1015 -512 1497 -522 1513 -1504 527 -1514 481 -516 1527 -1528 509 -494 1511 -1540 493 -1018 1005 -1014 1003 -59166 553 -1502 511 -1504 487 -1546 511 -992 1011 -1010 1015 -520 1517 -1016 1007 -480 1527 -1016 1009 -1512 509 -510 1529 -490 1525 -1018 999 -1542 481 -1014 1035 -488 1541 -1506 481 -520 1509 -1018 1035 -1510 481 -60216 1525 -1508 521 -1024 1005 -1008 1017 -1504 487 -1018 1009 -526 1517 -516 1509 -1532 475 -1518 511 -1008 1031 -506 1495 -520 1507 -1540 483 -1544 477 -514 1531 -1506 487 -520 1513 -1516 509 -1006 1037 -1016 1011 -139572 133 -1724 99 -1590 165 -1660 65 -168 67 -1000 529 -598 65 -296 131 -130 197 -566 99 -732 99 -832 65 -4560 131 -1156 161 -982 261 -788 65 -720 65 -1286 65 -100 131 -368 131 -298 65 -500 65 -2674 65 -758 197 -168 131 -294 63 -1744 163 -5304 131 -3408 199 -2122 65 -2626 131 -832 67 -166 65 -4384 163 -1608 65 -66 97 -262 65 -1418 65 -390 65 -230 65 -66 165 -1666 133 -1690 165 -1744 65 -2206 131 -392 165 -398 95 -594 97 -864 197 -632 99 -66 101 -364 331 -494 65 -1024 197 -762 165 -200 133 -298 229 -524 97 -560 97 -98 297 -522 65 -166 165 -360 195 -1514 229 -896 99 -562 261 -1552 195 -1422 263 -98 167 -396 99 -4540 165 -266 67 -398 97 -1660 263 -230 131 -1092 99 -134 65 -464 231 -98 99 -1458 97 -5494 99 -3342 65 -432 99 -2910 131 -1492 99 -3612 65 -1684 197 -1608 195 -996 65 -132 99 -1728 165 -1196 65 -862 131 -626 231 -796 65 -732 165 -1462 197 -2710 65 -790 97 -3198 165 -1778 65 -4158 165 -100 229 -5170 133 -2622 65 -3872 97 -754 165 -1260 365 -100 133 -98 133 -98 199 -562 131 -262 165 -1216 197 -230 131 -1612 165 -528 163 -228 63 -264 97 -328 99 -728 299 -758 163 -164 327 -228 65 -164\nRAW_Data: 197 -130 65 -264 163 -462 231 -100 65 -296 163 -196 97 -262 163 -728 299 -462 299 -1492 165 -200 99 -232 65 -1054 2385 -100 395 -132 99 -664 99 -132 365 -596 99 -794 329 -564 133 -298 165 -166 235 -366 229 -132 163 -590 197 -66 197 -1252 293 -262 129 -592 259 -232 163 -526 65 -230 165 -368 101 -1160 99 -826 97 -396 65 -198 3377 -66 791 -100 133 -168 99 -166 199 -100 465 -230 67 -400 197 -396 295 -1310 791 -1228 229 -560 65 -264 129 -326 199 -332 99 -264 65 -426 293 -132 197 -1316 99 -754 131 -822 197 -66 97 -460 65 -756 295 -166 3453 -198 65 -800 97 -100 199 -462 99 -198 67 -66 135 -298 299 -302 65 -300 99 -598 531 -466 233 -234 395 -200 133 -962 99 -100 199 -928 99 -628 97 -100 97 -730 99 -696 233 -528 67 -98 67 -694 365 -198 67 -896 295 -1484 97 -626 2563 -66 525 -100 561 -166 163 -232 265 -396 67 -234 99 -166 495 -166 97 -134 231 -132 133 -436 263 -298 97 -264 129 -230 65 -164 131 -164 393 -98 99 -294 65 -426 261 -264 131 -362 97 -196 65 -462 131 -1906 97 -260 65 -394 129 -660 133 -132 165 -264 231 -730 99 -560 197 -790 65 -896 65 -200 99 -764 261 -404 165 -164 165 -100 97 -164 395 -98 97 -164 99 -98 131 -132 163 -562 229 -532 99 -198 65 -332 99 -200 301 -330 165 -200 99 -928 2555 -362 731 -464 165 -492 165 -134 131 -732 131 -494 99 -164 99 -362 97 -166 97 -362 97 -134 97 -134 131 -166 99 -166 133 -468 329 -296 133 -1198 65 -2178 229 -3362 131 -1792 65 -7008 99 -164 65 -1448 65 -590 65 -134 67 -498 131 -1228 131 -532 131 -132 67 -364 65 -2128 165 -2358 99 -1526 265 -1154 65 -2240 97 -558 65 -2306 605 -1000 999 -1008 1035 -1510 513 -482 1531 -1496 543 -496 1507 -510 1529 -1514 511 -990 1007 -1002 1047 -984 1015 -492 1537 -512 1499 -1020 1011 -490 1539 -1000 1033 -988 1039 -476 1549 -474 1533 -1018 1001 -60188 1493 -1592 449 -1046 949 -1598 439 -564 1455 -552 1475 -548 1505 -1020 975 -544 1497 -1530 511 -1500 513 -1008 1003 -1540 507 -1512 501 -1022 1011 -1014 985 -1532 513 -500 1509 -1534 505 -522 1479 -528 1507 -59194 517 -1006 1017 -1006 1003 -1508 537 -482 1535 -1508 509 -512 1497 -520 1505 -1520 513 -1006 1033 -988 1039 -1006 1015 -480 1533 -490 1525 -1006 1035 -486 1539 -1014 987 -1018 1033 -484 1541 -484 1517 -1016 999 -60216 1481 -1620 387 -1118 923 -1602 431 -556 1481 -556 1447 -578 1467 -1048 977 -520 1505 -1542 499 -1516 481 -1048 989 -1544 493 -1516 515 -1010 1017 -986 1033 -1508 507 -514\nRAW_Data: 1495 -1514 509 -528 1509 -516 1503 -59178 493 -1064 983 -1050 983 -1534 479 -540 1487 -1544 481 -520 1501 -518 1511 -1544 487 -1012 1019 -1010 1013 -1016 1003 -514 1519 -478 1531 -1016 1001 -514 1533 -1012 1003 -1000 1035 -486 1517 -524 1513 -1012 1019 -60160 1525 -1580 443 -1070 929 -1580 451 -556 1505 -548 1467 -520 1511 -1020 999 -514 1527 -1502 505 -1512 511 -1012 1021 -1508 517 -1518 513 -1006 1017 -1006 1001 -1512 511 -514 1527 -1500 507 -524 1517 -486 1535 -59184 527 -1018 993 -1016 1013 -1516 511 -508 1495 -1538 491 -520 1511 -522 1507 -1534 473 -1016 1011 -1024 1007 -1008 1001 -518 1515 -524 1507 -1008 1001 -520 1501 -1026 1009 -1008 1031 -508 1495 -518 1507 -1046 975 -60196 1551 -1516 483 -1050 989 -1532 497 -522 1511 -484 1511 -518 1509 -1044 1007 -484 1517 -1544 495 -1508 509 -1010 1017 -1514 503 -1516 515 -1012 1017 -1018 1005 -1512 515 -482 1529 -1514 503 -524 1505 -512 1499 -135388 65 -928 99 -3478 197 -164 131 -558 65 -1120 131 -2416 265 -1562 99 -4088 133 -1460 165 -202 65 -1428 97 -1518 199 -332 133 -2388 197 -1590 67 -2016 133 -1624 97 -1094 65 -1692 99 -3458 163 -570 131 -4418 229 -296 97 -198 97 -164 297 -132 461 -164 163 -1284 299 -332 131 -1064 133 -796 67 -232 67 -264 201 -432 133 -330 133 -198 99 -166 99 -768 65 -462 133 -98 99 -100 397 -1228 133 -532 65 -5202 163 -2542 99 -766 263 -1686 65 -298 67 -1230 65 -266 197 -262 163 -1296 229 -1742 65 -1054 65 -2286 133 -366 165 -2936 229 -1158 167 -198 67 -3148 263 -3174 165 -1720 133 -1766 131 -5204 163 -1814 67 -1664 65 -4348 97 -364 131 -1532 267 -3838 163 -1348 65 -3364 231 -3336 65 -100 97 -466 65 -730 67 -3524 129 -296 163 -1118 97 -66 231 -786 131 -262 97 -428 429 -528 195 -68 165 -964 99 -366 295 -460 99 -930 197 -300 99 -598 63 -600 131 -430 131 -66 131 -762 363 -1554 99 -168 135 -1452 65 -466 165 -692 99 -296 131 -592 97 -726 131 -134 97 -3000 99 -1608 131 -492 65 -854 193 -1058 99 -498 99 -1694 65 -996 199 -530 97 -1582 165 -1718 131 -3418 131 -1592 267 -1622 99 -530 99 -466 197 -5108 131 -100 67 -1656 95 -1318 65 -3530 133 -1196 65 -1886 131 -490 165 -2222 197 -1694 97 -2076 65 -1022 65 -758 163 -98 97 -164 97 -198 63 -688 97 -1086 65 -1614 65 -1822 67 -1028 133 -398 99 -430 131 -2932 165 -1122 99 -334 297 -2028 65 -1392 97 -622 131 -296 99 -100 165 -100 199 -100 99 -300 497 -100 197 -302 265 -102 99 -1428 165 -168 297 -234 65 -798 131 -68 197 -466 165 -230 67 -132 99 -166 167 -598 231 -264\nRAW_Data: 265 -136 233 -764 165 -100 297 -232 67 -166 2513 -66 1745 -232 165 -436 99 -366 131 -466 99 -468 199 -466 131 -102 527 -764 99 -98 199 -166 133 -266 133 -166 99 -68 197 -434 65 -268 231 -132 131 -200 65 -330 65 -200 131 -232 165 -134 65 -1160 97 -134 99 -200 99 -1200 429 -920 97 -590 2475 -132 1095 -132 133 -66 233 -430 167 -168 429 -166 427 -164 423 -66 99 -230 229 -262 325 -164 65 -330 129 -132 231 -300 329 -332 133 -462 197 -564 99 -996 67 -264 233 -764 99 -98 299 -1462 363 -824 133 -132 99 -298 99 -100 197 -68 65 -132 4251 -132 67 -234 133 -166 199 -100 131 -200 131 -198 67 -330 199 -364 197 -298 99 -66 199 -268 197 -298 397 -430 131 -528 99 -266 231 -328 595 -402 65 -500 129 -298 229 -64 99 -1290 231 -822 229 -328 131 -1116 263 -228 361 -862 99 -66 133 -796 65 -698 3715 -166 463 -564 97 -198 97 -132 97 -230 295 -98 229 -330 393 -164 131 -230 297 -526 229 -98 97 -66 363 -722 295 -64 163 -400 167 -762 363 -66 165 -234 133 -864 97 -798 65 -332 431 -498 65 -928 197 -68 133 -266 165 -594 65 -200 99 -464 265 -230 2321 -196 1857 -98 297 -132 431 -266 431 -462 131 -100 397 -330 65 -364 65 -432 229 -166 297 -66 295 -954 163 -626 163 -526 495 -466 201 -660 327 -666 131 -998 197 -166 67 -468 97 -564 99 -1460 163 -926 299 -926 99 -598 133 -498 67 -232 199 -828 131 -1328 327 -166 99 -598 231 -402 65 -132 65 -566 97 -264 167 -1560 67 -1330 67 -858 65 -1366 131 -2754 99 -1786 131 -5130 195 -164 67 -134 131 -1984 65 -1286 97 -294 99 -228 65 -1062 65 -3418 131 -68 131 -1096 563 -1016 975 -1042 983 -1046 1005 -1516 485 -518 1531 -518 1507 -1514 515 -1014 989 -1516 523 -1016 1007 -1506 505 -1002 1011 -1520 511 -1012 1017 -1000 1005 -1532 513 -510 1499 -518 1497 -1018 1035 -1510 481 -60186 1543 -1514 513 -1014 987 -1518 527 -486 1537 -1008 999 -1018 1009 -1520 513 -1506 519 -1018 991 -516 1503 -520 1515 -1512 527 -1512 481 -1016 1019 -1008 1021 -1506 521 -992 1037 -510 1491 -1010 1017 -1512 507 -59178 481 -1116 879 -1138 897 -1118 915 -1614 409 -608 1423 -586 1441 -1580 441 -1070 957 -1580 455 -1044 1007 -1528 479 -1040 1003 -1518 481 -1034 1017 -1008 1015 -1520 481 -506 1521 -514 1507 -1016 1007 -1518 511 -60172 459 -4210 815 -1730 299 -658 1411 -1108 941 -1074 945 -1562 483 -1542 493 -1012 1005 -526 1507 -512 1497 -1520 513 -1524 511 -1004 1001 -1014 1029 -1494 515 -1002 1043 -478 1529 -1018 1001 -1538 495 -59140 529 -7690 365 -624\nRAW_Data: 1381 -658 1397 -1602 423 -1114 911 -1582 441 -1070 955 -1572 481 -1036 971 -1566 479 -1034 981 -1012 1015 -1514 505 -530 1513 -516 1511 -1018 1007 -1514 481 -60210 459 -7710 1381 -1140 903 -1126 915 -1602 415 -1588 443 -1060 983 -550 1475 -550 1469 -1558 479 -1540 493 -1018 1005 -1018 1009 -1506 503 -1006 1037 -484 1535 -1010 1017 -1518 497 -59168 525 -1008 997 -1046 1009 -1016 989 -1510 527 -486 1539 -480 1527 -1532 515 -1006 1011 -1506 519 -990 1019 -1534 479 -1042 1015 -1512 477 -1026 1009 -1010 1015 -1512 507 -526 1513 -484 1543 -1018 1003 -1510 509 -60172 1509 -1612 389 -1104 939 -1596 419 -582 1471 -1074 961 -1040 991 -1542 453 -1548 481 -1038 1017 -514 1505 -492 1543 -1502 515 -1506 515 -1002 1033 -986 1035 -1514 481 -1016 1027 -518 1503 -1014 1011 -1514 521 -133944 165 -1586 97 -294 63 -5014 99 -492 231 -1222 65 -396 131 -3086 99 -1920 65 -266 129 -1918 65 -922 195 -1908 65 -2080 97 -1894 99 -1558 165 -1424 165 -5468 99 -1694 65 -1756 65 -1812 99 -490 65 -1710 229 -98 99 -132 131 -992 99 -168 131 -68 65 -2322 199 -1888 133 -132 199 -800 97 -662 201 -666 97 -134 65 -132 65 -134 133 -166 199 -396 65 -558 231 -330 99 -692 199 -264 133 -332 99 -268 65 -366 231 -732 163 -264 327 -132 129 -132 99 -724 367 -234 133 -130 363 -598 331 -166 199 -1068 299 -466 99 -2822 65 -66 131 -1562 65 -1684 131 -556 99 -198 199 -1254 99 -1032 65 -1296 65 -100 133 -1330 65 -1058 65 -1096 65 -632 65 -1856 65 -134 163 -890 65 -1646 65 -664 65 -528 65 -392 99 -1624 165 -496 99 -1428 129 -2826 65 -466 65 -864 65 -4422 131 -1926 99 -1466 131 -6930 99 -894 131 -1754 97 -1640 65 -66 97 -396 99 -130 65 -1086 99 -756 65 -198 65 -2188 261 -1594 131 -1722 65 -130 97 -6642 97 -134 131 -2376 67 -1590 65 -66 65 -998 165 -930 165 -264 65 -132 65 -296 97 -558 65 -66 99 -1086 363 -230 131 -1194 165 -1490 231 -166 133 -636 229 -428 363 -826 261 -130 97 -132 97 -164 131 -66 131 -2902 359 -1518 165 -598 133 -990 161 -396 301 -5214 261 -2776 65 -2326 99 -1698 65 -1618 99 -1358 65 -3480 231 -1558 131 -4656 97 -1554 65 -166 199 -200 201 -926 99 -664 133 -1656 229 -98 99 -1030 99 -1656 131 -1294 67 -396 131 -3038 165 -332 427 -230 361 -266 1027 -166 329 -166 265 -100 67 -230 799 -166 135 -200 631 -298 99 -168 297 -464 497 -264 429 -66 597 -164 197 -200 67 -232 97 -300 595 -132 165 -234 163 -100 65 -432 65 -4370 65 -722 131 -1688 131 -398 65 -1162 199 -2120 263 -1098 133 -758\nRAW_Data: 99 -296 97 -300 231 -200 133 -364 163 -532 199 -536 265 -762 399 -66 131 -332 99 -466 97 -132 431 -198 131 -100 131 -234 465 -164 231 -564 131 -398 131 -334 301 -132 99 -132 131 -498 65 -462 99 -1094 65 -66 99 -266 99 -800 65 -432 65 -562 65 -792 65 -166 99 -1230 131 -1248 65 -770 197 -1260 1985 -134 747 -66 655 -198 99 -530 299 -198 65 -166 299 -134 131 -200 531 -328 131 -624 165 -98 97 -230 97 -560 97 -560 131 -1654 131 -200 65 -166 99 -666 297 -796 67 -132 297 -664 199 -100 65 -532 131 -168 65 -466 165 -728 131 -624 2073 -166 233 -168 863 -166 131 -464 463 -100 131 -266 165 -196 399 -330 563 -632 65 -232 97 -66 131 -166 129 -98 131 -1386 99 -462 131 -1254 131 -1548 231 -824 65 -198 265 -996 99 -1062 231 -862 329 -264 261 -658 2367 -66 631 -98 859 -230 231 -558 129 -396 129 -66 297 -98 201 -134 131 -166 231 -162 295 -432 263 -66 331 -1384 65 -198 197 -98 131 -228 65 -132 65 -692 261 -560 97 -262 163 -232 99 -132 99 -134 65 -200 133 -132 99 -798 231 -1624 131 -498 67 -630 99 -300 165 -1126 293 -694 65 -98 65 -100 2267 -98 393 -494 197 -426 131 -166 67 -198 199 -266 429 -166 65 -300 131 -66 65 -232 133 -102 365 -134 97 -332 67 -232 331 -66 363 -366 97 -796 65 -166 165 -1260 263 -164 99 -266 527 -758 65 -428 197 -564 65 -366 429 -166 267 -830 133 -598 333 -666 65 -134 197 -398 97 -132 65 -168 133 -502 65 -364 131 -98 65 -630 197 -166 129 -822 65 -230 295 -64 65 -164 65 -790 97 -230 129 -364 163 -330 131 -132 65 -230 67 -166 97 -66 165 -632 231 -264 97 -294 99 -856 229 -694 261 -1546 97 -2076 65 -462 133 -5548 199 -732 99 -4170 133 -1232 99 -164 165 -1656 65 -766 167 -2258 163 -1610 197 -2020 99 -2518 131 -1020 65 -364 97 -200 97 -1664 163 -526 65 -1096 165 -1684 65 -986 99 -4860 131 -966 131 -1622 65 -1658 65 -1266 97 -266 165 -200 99 -964 99 -330 65 -3440 131 -166 99 -68 65 -1164 65 -132 99 -200 67 -792 163 -496 99 -860 427 -492 195 -230 99 -466 163 -1364 295 -132 165 -364 67 -1026 231 -370 65 -566 65 -530 363 -196 97 -164 97 -594 63 -164 165 -228 163 -66 233 -862 97 -692 65 -1744 163 -1416 67 -132 99 -1718 65 -556 131 -496 99 -1328 99 -132 167 -466 131 -4690 165 -1654 65 -1114 263 -1620 65 -1796 97 -1426 99 -1922 99 -7098 97 -466 131 -1578 131 -2384 165 -298 133 -766 131 -330 295 -2348\nRAW_Data: 131 -534 99 -798 2211 -134 297 -66 361 -66 197 -198 97 -398 199 -100 163 -100 65 -464 133 -134 233 -68 229 -696 199 -200 265 -330 65 -1218 163 -166 97 -426 229 -698 165 -100 99 -134 165 -372 65 -1426 65 -266 99 -794 65 -832 199 -464 165 -698 99 -266 165 -794 99 -298 133 -734 165 -266 99 -864 131 -232 1893 -166 1465 -1484 263 -560 99 -230 65 -462 197 -66 97 -890 165 -130 97 -132 163 -232 97 -132 161 -162 263 -262 99 -362 65 -432 65 -830 97 -234 99 -628 199 -66 99 -166 101 -268 265 -594 131 -1030 97 -1156 595 -800 167 -1920 65 -464 165 -264 131 -66 167 -134 233 -632 329 -100 97 -166 199 -466 199 -98 267 -200 97 -266 199 -300 65 -496 563 -100 101 -166 165 -730 233 -166 131 -590 97 -232 131 -164 359 -428 3159 -792 65 -624 197 -98 229 -132 393 -398 199 -200 133 -366 99 -132 67 -528 265 -730 197 -166 65 -200 397 -1358 131 -1712 363 -1462 299 -132 67 -1792 65 -794 99 -368 131 -134 165 -134 67 -266 265 -430 2315 -98 1473 -100 129 -66 229 -198 261 -166 97 -426 97 -100 229 -496 197 -162 163 -392 231 -166 229 -660 131 -366 297 -166 299 -232 97 -332 301 -866 133 -596 267 -364 99 -502 99 -1988 131 -134 65 -66 165 -366 231 -266 165 -690 99 -234 65 -498 67 -166 133 -762 2459 -166 1019 -264 99 -200 265 -562 65 -328 295 -330 197 -360 131 -98 227 -232 65 -426 259 -166 99 -496 131 -500 399 -992 67 -432 133 -66 99 -1394 231 -1524 133 -430 65 -886 65 -1716 131 -2818 63 -430 229 -296 97 -1706 131 -164 65 -1478 131 -458 99 -1568 165 -756 197 -890 99 -1088 359 -660 97 -98 229 -166 65 -500 99 -266 65 -566 65 -432 365 -200 99 -798 99 -296 327 -1218 165 -132 99 -466 165 -168 133 -632 363 -1482 163 -802 131 -862 133 -1332 65 -100 431 -830 97 -596 165 -3422 99 -896 65 -766 97 -3766 65 -132 65 -1152 163 -1810 99 -230 97 -654 229 -458 165 -200 67 -166 297 -134 99 -4840 133 -762 67 -764 231 -1248 97 -1780 97 -3332 65 -100 261 -1186 99 -3360 99 -364 97 -1738 163 -294 97 -4918 129 -1550 295 -1028 199 -1264 99 -1560 99 -2156 167 -1556 231 -1852 167 -998 67 -1116 65 -562 167 -334 133 -266 165 -394 99 -166 97 -3082 99 -232 301 -5148 131 -502 67 -732 197 -434 199 -200 99 -432 65 -232 199 -464 133 -1060 97 -1582 97 -98 97 -924 129 -462 229 -560 65 -132 97 -166 161 -164 97 -166 229 -360 97 -132 63 -198 131 -162 525 -1714 67 -732 99 -958\nRAW_Data: 65 -1166 165 -200 199 -166 133 -100 65 -1294 99 -166 265 -992 297 -728 65 -332 431 -166 261 -132 65 -132 65 -98 363 -230 163 -430 165 -230 129 -660 129 -66 131 -398 2453 -132 1329 -166 1187 -232 1657 -166 499 -68 99 -132 233 -894 99 -232 99 -134 363 -628 67 -998 131 -234 99 -266 197 -398 97 -200 167 -334 197 -430 429 -168 263 -1160 99 -364 131 -264 97 -1318 131 -790 99 -432 131 -132 199 -100 131 -930 133 -1424 99 -398 165 -1486 299 -166 165 -334 2325 -66 795 -102 397 -100 533 -100 429 -98 131 -464 65 -532 329 -366 165 -332 99 -432 295 -132 99 -662 67 -332 331 -1226 131 -102 199 -562 469 -430 97 -232 63 -328 65 -1914 133 -466 131 -832 163 -1660 331 -464 199 -828 131 -696 65 -132 2139 -66 2051 -66 263 -790 99 -566 429 -428 99 -330 297 -698 65 -200 197 -68 461 -1292 131 -168 165 -634 131 -234 133 -594 163 -360 165 -700 99 -964 133 -1026 65 -532 197 -1520 263 -688 65 -888 295 -658 65 -824 67 -598 97 -426 65 -130 161 -132 63 -658 99 -64 165 -522 199 -298 65 -428 197 -560 65 -200 165 -100 167 -594 229 -100 297 -268 65 -100 459 -200 97 -334 2997 -200 597 -498 97 -626 97 -134 363 -166 333 -266 65 -664 331 -300 299 -568 529 -662 263 -626 67 -594 99 -266 165 -230 361 -462 67 -66 131 -694 397 -1490 167 -132 165 -768 165 -130 165 -466 99 -298 99 -952 163 -428 97 -296 97 -360 99 -196 65 -658 65 -4052 229 -6240 165 -228 65 -426 99 -994 99 -398 363 -1512 161 -2042 65 -592 131 -2866 65 -500 167 -5246 97 -462 67 -1156 99 -2428 99 -960 65 -132 99 -164 65 -166 329 -2484 263 -232 67 -1368 99 -996 267 -498 67 -4612 131 -4866 65 -100 363 -464 231 -264 231 -262 297 -1594 99 -1596 65 -136 133 -1662 67 -1694 429 -1290 197 -198 293 -858 99 -1976 165 -66 97 -460 131 -460 97 -328 163 -1624 397 -300 67 -166 97 -430 299 -300 299 -1792 65 -4438 131 -622 67 -500 131 -1064 65 -400 65 -426 261 -1644 131 -3468 165 -1684 99 -866 101 -66 231 -464 65 -498 133 -492 97 -1712 99 -98 197 -66 265 -1586 199 -1334 99 -1948 97 -200 99 -266 65 -666 131 -132 99 -264 195 -3066 99 -3512 99 -1746 65 -598 97 -6068 167 -600 99 -1262 65 -166 65 -134 65 -132 131 -1520 99 -1254 97 -1828 197 -1294 129 -66 65 -1248 97 -754 165 -6542 231 -662 197 -560 359 -98 97 -130 99 -3080 63 -952 129 -296 65 -230 63 -428 99 -1788 133 -100 65 -462 199 -66 165 -1094 99 -66\nRAW_Data: 167 -3190 265 -1128 195 -298 525 -166 65 -594 597 -664 131 -498 165 -232 65 -296 129 -296 65 -494 297 -498 297 -596 201 -332 2027 -132 593 -68 891 -66 893 -98 365 -396 97 -398 99 -300 199 -366 163 -598 99 -100 131 -234 65 -132 331 -532 165 -166 231 -1660 133 -532 265 -100 131 -100 131 -298 199 -396 199 -334 197 -166 67 -334 131 -1662 165 -298 97 -534 65 -628 65 -534 65 -266 97 -826 133 -696 65 -294 233 -198 165 -134 2081 -66 929 -198 167 -300 99 -232 297 -234 199 -662 67 -166 99 -66 233 -132 433 -232 265 -300 297 -132 65 -132 97 -496 199 -360 465 -132 65 -132 297 -460 293 -64 161 -262 97 -492 199 -200 233 -132 97 -396 99 -858 199 -864 65 -66 99 -532 131 -1608 297 -1550 163 -432 229 -66 163 -394 1953 -134 1491 -100 991 -66 165 -232 265 -130 299 -432 167 -724 363 -134 167 -200 231 -66 199 -98 395 -66 165 -266 99 -332 197 -268 265 -466 265 -166 493 -362 65 -132 99 -360 199 -200 133 -758 99 -330 165 -298 163 -428 199 -368 165 -334 131 -500 163 -986 97 -394 329 -196 97 -558 195 -230 97 -532 2373 -232 165 -232 795 -132 267 -168 363 -166 297 -298 131 -134 65 -466 131 -464 65 -232 67 -266 229 -364 231 -232 297 -464 65 -198 133 -398 529 -134 65 -364 163 -732 233 -298 299 -134 231 -66 129 -264 397 -132 131 -200 329 -430 131 -132 265 -1292 65 -362 131 -264 97 -1020 165 -398 131 -364 65 -300 265 -924 195 -396 331 -166 167 -132 433 -198 99 -464 297 -266 165 -396 131 -1226 165 -694 65 -230 197 -896 65 -594 2969 -164 461 -298 133 -330 133 -234 65 -134 131 -66 65 -68 97 -66 99 -132 231 -694 363 -298 267 -268 231 -368 131 -300 99 -134 65 -166 461 -396 263 -66 165 -298 131 -66 229 -1644 163 -1610 229 -658 97 -132 129 -162 97 -428 65 -66 129 -1912 65 -1194 233 -2218 133 -360 65 -132 229 -3428 65 -262 65 -3208 133 -1224 99 -296 65 -2212 65 -496 65 -200 199 -498 65 -1144 65 -1764 165 -1726 65 -434 65 -898 165 -2354 67 -134 99 -464 99 -792 263 -954 1161 -100 295 -296 131 -132 457 -462 363 -200 725 -298 131 -134 231 -68 531 -166 667 -298 99 -66 595 -794 165 -202 199 -68 99 -298 165 -68 231 -202 201 -200 97 -1360 197 -266 165 -334 99 -234 165 -200 165 -100 131 -664 65 -566 397 -100 165 -132 199 -132 165 -988 197 -394 99 -164 65 -294 99 -262 63 -1288 233 -132 199 -168 265 -64 99 -728 133 -1192 65 -334 165 -132 199 -200\nRAW_Data: 131 -400 65 -398 165 -866 131 -264 393 -1680 231 -1494 299 -894 265 -364 65 -132 131 -960 99 -296 165 -824 65 -1716 99 -664 97 -134 165 -1526 165 -634 131 -166 135 -466 231 -696 2331 -100 329 -100 961 -198 65 -164 199 -98 331 -132 65 -298 731 -66 163 -332 265 -894 65 -132 265 -300 97 -334 229 -100 65 -1026 233 -366 329 -922 99 -266 329 -428 97 -462 361 -758 163 -1886 65 -498 229 -558 65 -600 267 -68 65 -1592 133 -202 2299 -332 393 -132 329 -326 231 -232 97 -590 231 -136 201 -1226 265 -530 199 -134 65 -232 297 -398 65 -564 65 -1458 65 -500 99 -302 99 -1120 65 -66 131 -98 197 -66 133 -66 65 -730 99 -200 197 -730 165 -664 297 -1462 165 -664 133 -168 65 -168 133 -368 197 -466 1957 -66 1525 -266 131 -366 65 -394 65 -98 163 -296 99 -130 265 -696 65 -398 131 -1128 197 -134 67 -894 97 -296 231 -166 401 -98 131 -928 265 -398 131 -798 131 -298 165 -830 65 -230 231 -168 97 -1252 131 -590 65 -460 229 -692 97 -332 65 -366 265 -732 2297 -66 197 -66 131 -166 231 -100 99 -298 365 -168 361 -130 129 -66 161 -198 97 -626 593 -66 99 -98 99 -556 297 -132 65 -134 65 -526 295 -196 197 -68 163 -264 327 -130 855 -66 393 -132 131 -100 65 -98 227 -164 197 -758 131 -268 99 -430 65 -1686 131 -1652 197 -788 63 -726 165 -100 131 -264 197 -1026 197 -1250 131 -1650 65 -1980 97 -132 63 -1424 97 -198 99 -100 65 -1090 65 -398 99 -1686 99 -754 63 -1714 97 -1842 65 -2988 65 -4980 165 -1624 65 -232 67 -1464 65 -3048 65 -166 97 -1986 65 -1062 99 -862 131 -2732 99 -2360 97 -1620 65 -1326 65 -330 197 -3238 67 -266 131 -2968 67 -132 135 -1688 131 -1718 97 -1648 65 -166 65 -1292 65 -1908 99 -424 131 -1222 197 -798 99 -700 97 -1758 165 -1562 99 -396 165 -166 99 -98 65 -366 65 -762 131 -332 233 -498 133 -98 163 -562 65 -68 131 -858 65 -896 65 -366 233 -532 197 -168 65 -68 65 -630 195 -724 231 -296 361 -164 261 -2862 233 -232 99 -3186 67 -232 163 -496 165 -1620 97 -1622 131 -7022 267 -268 165 -466 63 -528 295 -200 197 -332 65 -1656 131 -5478 65 -1296 99 -130 161 -694 67 -1694 131 -734 99 -2590 99 -1056 65 -432 327 -66 165 -164 65 -1120 229 -1656 165 -1128 131 -1232 99 -1560 65 -568 131 -360 231 -1598 99 -3250 67 -1818 65 -1216 263 -3328 65 -1508 133 -2600 65 -528 65 -2418 65 -460 97 -100 97 -164 65 -1560 133 -1822 163 -296 165 -1090 165 -168 133 -1264\nRAW_Data: 229 -1458 165 -66 133 -1626 197 -132 65 -394 65 -724 229 -132 65 -200 263 -296 97 -264 131 -130 229 -132 161 -1230 431 -132 99 -234 233 -362 99 -698 67 -334 65 -366 299 -398 99 -328 131 -396 165 -1458 99 -926 2239 -66 763 -68 1727 -266 65 -364 65 -464 365 -366 97 -664 165 -200 265 -234 261 -264 395 -362 329 -690 261 -164 495 -232 131 -326 165 -264 99 -198 397 -928 299 -268 299 -166 99 -298 167 -436 65 -266 67 -498 231 -896 367 -428 97 -332 133 -628 165 -1394 231 -926 131 -232 3127 -132 921 -428 263 -66 97 -230 97 -1022 329 -430 97 -166 65 -198 63 -100 65 -66 97 -66 361 -132 67 -232 131 -268 99 -366 99 -98 331 -364 99 -428 97 -66 491 -164 99 -164 65 -428 131 -260 429 -1380 263 -1564 65 -300 65 -498 99 -364 167 -200 231 -264 67 -264 365 -266 231 -262 65 -724 65 -130 2213 -130 361 -166 2065 -166 165 -100 133 -264 299 -332 99 -98 229 -198 65 -328 131 -100 263 -628 131 -330 231 -98 65 -132 129 -264 65 -130 163 -692 131 -66 99 -532 133 -234 99 -232 367 -298 401 -702 165 -498 399 -166 65 -132 131 -400 165 -1232 165 -1296 65 -232 165 -300 97 -396 2033 -198 463 -230 67 -68 563 -262 295 -330 197 -1190 197 -66 167 -66 263 -430 263 -100 163 -198 167 -102 165 -100 99 -234 67 -66 267 -396 197 -296 131 -1190 331 -200 67 -234 231 -530 263 -1610 263 -264 329 -196 129 -98 131 -132 227 -132 425 -526 129 -694 299 -856 63 -132 165 -166 165 -436 297 -1430 65 -66 99 -198 167 -266 65 -864 267 -996 65 -500 265 -1456 297 -726 163 -66 579 -1508 523 -1504 509 -1006 1033 -486 1513 -1514 507 -510 1537 -982 1011 -528 1509 -512 1495 -1034 999 -1016 999 -516 1539 -1014 989 -1510 527 -486 1537 -1508 517 -1512 485 -1546 483 -524 1519 -484 1529 -60168 1539 -1506 507 -1006 1015 -1016 1007 -1512 509 -1006 1033 -506 1495 -1544 497 -1014 1007 -1008 1001 -1544 477 -1024 1011 -1538 485 -1512 525 -1010 1005 -482 1529 -1018 1005 -1020 1009 -1532 475 -540 1507 -512 1491 -59188 513 -1506 515 -1512 519 -1020 985 -506 1519 -1538 491 -504 1533 -998 1013 -516 1505 -522 1505 -1018 1005 -1002 1035 -494 1525 -1014 995 -1546 483 -524 1513 -1504 515 -1502 509 -1512 509 -514 1527 -514 1501 -60186 1539 -1506 509 -1008 1003 -1016 1009 -1520 515 -1014 989 -518 1509 -1544 485 -1016 999 -1016 1003 -1544 483 -1016 1017 -1506 515 -1534 475 -1036 987 -520 1507 -1012 1035 -1006 1017 -1502 503 -526 1503 -508 1533 -59160 523 -1508 507 -1502 537 -984 1013 -528 1505 -1504 503 -520 1537 -994\nRAW_Data: 1007 -508 1533 -474 1527 -1008 1037 -1012 1003 -510 1503 -1018 1009 -1518 513 -514 1495 -1546 477 -1520 509 -1528 505 -492 1527 -520 1505 -60188 1543 -1514 483 -1044 1001 -1000 1009 -1520 513 -1012 1019 -508 1505 -1516 515 -1008 999 -1016 1007 -1516 513 -1010 1017 -1514 503 -1520 513 -1008 1001 -530 1495 -1012 1021 -1010 1001 -1530 503 -518 1507 -516 1507 -59162 517 -1554 479 -1540 493 -1034 981 -538 1483 -1534 505 -506 1497 -1038 1007 -488 1537 -484 1539 -1020 1007 -1016 1009 -484 1539 -1000 1005 -1532 511 -480 1535 -1514 513 -1504 515 -1508 519 -494 1529 -486 1535 -60182 1515 -1556 479 -1044 983 -1016 1005 -1512 507 -1036 1001 -488 1529 -1536 495 -1020 1009 -1010 1017 -1510 487 -1014 1039 -1510 505 -1510 521 -980 1039 -484 1539 -986 1039 -1016 1007 -1504 503 -520 1507 -516 1507 -131494 295 -198 195 -98 99 -362 97 -196 165 -130 233 -1528 197 -1756 165 -1024 129 -326 97 -1794 131 -1562 65 -1692 99 -100 99 -532 99 -1728 231 -5256 65 -7488 133 -164 133 -100 97 -266 327 -166 99 -1294 131 -628 97 -502 163 -1122 131 -856 65 -1480 63 -3748 131 -1356 65 -1152 99 -2316 131 -2640 65 -798 131 -2520 99 -66 133 -5954 129 -1680 131 -3458 131 -1086 65 -532 67 -632 165 -3482 65 -1024 163 -2800 65 -362 197 -1880 97 -494 99 -100 65 -1120 131 -3212 299 -266 99 -698 329 -1328 165 -66 133 -534 131 -794 227 -990 65 -164 133 -130 131 -198 65 -196 63 -66 165 -196 131 -98 329 -298 99 -230 65 -164 425 -100 523 -266 65 -1686 65 -1182 163 -3614 131 -3386 133 -6578 65 -432 231 -1682 131 -1680 99 -588 99 -2222 97 -364 131 -5214 131 -698 133 -394 327 -296 295 -396 199 -1256 131 -1190 99 -234 65 -3596 163 -2036 163 -660 133 -430 199 -800 67 -2854 165 -3516 99 -1660 131 -1596 99 -1726 133 -2326 65 -168 65 -530 165 -166 67 -1384 229 -494 165 -1248 99 -1224 65 -1624 99 -468 233 -4386 165 -1264 133 -762 65 -296 229 -1678 165 -1518 163 -1580 65 -132 65 -530 261 -166 161 -428 199 -134 99 -730 99 -558 99 -266 65 -1164 499 -468 131 -462 229 -362 131 -1480 327 -230 65 -732 65 -928 395 -298 65 -568 99 -232 99 -200 99 -1098 65 -3106 97 -266 99 -2356 67 -494 65 -1816 163 -390 165 -1032 67 -2308 165 -134 99 -1284 97 -1550 99 -494 97 -1326 97 -3530 65 -3702 99 -1382 231 -1228 165 -496 97 -968 265 -1452 99 -4736 131 -3916 65 -1282 65 -364 65 -3530 97 -1352 97 -1742 133 -5274 99 -3490 65 -3418 65 -1850 97 -2782 99 -1720 133 -2258 65 -100 65 -1620 65 -1160 65 -1558 99 -662 65 -1458 99 -230 129 -98 163 -392\nRAW_Data: 129 -694 401 -1526 67 -298 65 -1258 231 -102 199 -298 99 -790 99 -100 97 -100 131 -100 97 -164 161 -266 65 -234 133 -132 299 -628 65 -702 197 -330 65 -962 231 -366 165 -330 133 -792 65 -628 2059 -196 633 -166 231 -264 97 -166 163 -336 231 -202 65 -266 65 -266 131 -134 99 -200 197 -330 165 -132 65 -200 99 -266 197 -264 97 -198 559 -66 163 -754 229 -98 65 -64 363 -164 229 -196 363 -426 231 -68 201 -1288 97 -634 331 -2022 99 -264 67 -166 197 -464 3065 -98 97 -132 97 -66 327 -132 853 -132 97 -164 99 -396 63 -100 393 -196 131 -1052 465 -1458 427 -198 97 -198 391 -262 65 -66 165 -494 131 -262 97 -264 97 -264 265 -168 197 -764 97 -232 163 -464 65 -66 131 -398 65 -896 133 -598 67 -430 165 -1230 165 -200 65 -1130 65 -198 263 -230 2753 -66 565 -100 1129 -132 99 -134 67 -532 99 -400 299 -166 131 -134 65 -464 231 -234 329 -132 231 -262 163 -132 65 -394 131 -98 131 -66 397 -100 65 -68 461 -198 99 -296 199 -500 231 -1552 195 -66 65 -1586 195 -1350 197 -1418 131 -488 2647 -396 229 -526 129 -262 97 -262 97 -328 165 -664 99 -166 99 -232 133 -234 131 -726 129 -130 65 -198 129 -132 161 -330 65 -426 97 -694 531 -364 99 -862 165 -694 301 -66 163 -302 65 -232 233 -700 65 -98 165 -628 65 -100 163 -1516 327 -362 65 -956 231 -626 131 -464 65 -232 99 -828 131 -164 99 -400 363 -1324 99 -264 97 -1560 101 -66 65 -464 65 -498 99 -300 297 -100 65 -200 65 -600 97 -166 2321 -100 603 -200 67 -468 299 -300 65 -100 133 -796 131 -66 67 -700 165 -432 97 -332 233 -534 197 -398 199 -166 297 -362 131 -1132 233 -562 165 -166 99 -530 197 -700 131 -856 133 -398 65 -892 97 -2990 65 -4596 99 -130 163 -1770 65 -2256 99 -860 65 -232 131 -1662 267 -232 101 -1166 165 -1160 99 -466 163 -100 97 -366 65 -2828 65 -1624 263 -1786 65 -164 131 -1214 131 -460 227 -2998 63 -494 67 -1658 97 -3456 65 -3618 99 -2554 99 -2484 231 -4088 97 -560 165 -296 129 -2144 197 -528 363 -166 231 -428 163 -296 197 -132 129 -1354 199 -664 163 -624 131 -98 131 -688 131 -590 263 -164 97 -494 197 -362 131 -298 133 -3478 65 -334 583 -1526 509 -1526 481 -514 1509 -1544 475 -1024 1011 -1012 1017 -506 1501 -528 1507 -1530 473 -538 1493 -520 1507 -1014 1011 -520 1515 -1512 491 -1046 1007 -482 1529 -1016 1007 -1512 507 -1004 1039 -990 1015 -60210 1503 -1524 513 -998 1015 -1542 483 -520 1505 -516 1507 -522 1519 -516\nRAW_Data: 1503 -1508 487 -554 1477 -1526 513 -514 1501 -1028 1005 -1526 481 -514 1541 -518 1503 -498 1515 -1506 501 -1544 499 -1020 1005 -1002 1001 -59198 529 -1518 481 -1544 507 -514 1497 -1536 473 -1044 1001 -1018 1009 -490 1539 -482 1529 -1512 507 -526 1507 -512 1503 -1016 1011 -524 1513 -1504 505 -1020 1005 -526 1507 -1008 1001 -1530 517 -1010 1013 -1012 1013 -60178 1557 -1508 489 -1016 1009 -1522 513 -514 1505 -520 1513 -524 1505 -512 1499 -1546 475 -526 1513 -1506 505 -506 1527 -1014 997 -1542 483 -522 1513 -518 1503 -516 1501 -1534 505 -1508 523 -1012 1007 -1014 987 -59186 515 -1578 419 -1608 449 -544 1471 -1546 485 -1050 977 -1042 991 -518 1507 -524 1487 -1538 489 -518 1503 -518 1529 -1002 1005 -524 1511 -1508 503 -1008 1035 -486 1541 -1014 987 -1538 495 -1018 1007 -1008 1001 -60188 453 -4246 817 -1716 295 -674 1393 -594 1449 -586 1447 -546 1467 -1578 447 -550 1497 -1530 511 -510 1489 -1038 1003 -1514 513 -512 1525 -478 1535 -492 1525 -1506 515 -1534 475 -1014 1031 -1000 1011 -59180 507 -11528 557 -686 1341 -674 1363 -1646 383 -620 1409 -610 1431 -1090 947 -554 1477 -1566 477 -1032 981 -516 1505 -1016 1005 -1522 511 -1012 1019 -1020 1005 -60176 1469 -1672 337 -1166 889 -1606 449 -578 1439 -566 1485 -518 1509 -516 1503 -1546 479 -514 1497 -1528 511 -512 1523 -1008 1003 -1514 513 -512 1525 -478 1541 -486 1517 -1522 511 -1508 521 -1010 995 -1010 1033 -133296 65 -1252 99 -332 65 -1220 131 -594 129 -394 99 -1546 131 -856 65 -1494 433 -132 99 -334 263 -164 199 -198 133 -3348 131 -626 65 -2598 65 -66 99 -1284 131 -1588 197 -1664 63 -694 65 -2206 197 -134 131 -300 65 -1146 67 -3524 197 -296 99 -1226 65 -1360 131 -1620 165 -260 231 -786 131 -1018 129 -1224 131 -302 65 -796 129 -396 97 -1744 67 -464 67 -166 131 -766 99 -398 65 -98 233 -66 97 -396 65 -1096 65 -298 99 -500 99 -3474 99 -996 97 -232 67 -2218 197 -1420 131 -1692 297 -6442 99 -368 99 -366 131 -164 65 -2038 65 -232 99 -1526 233 -5196 65 -298 65 -498 65 -1064 263 -232 627 -364 67 -132 129 -728 131 -166 65 -200 133 -596 197 -232 197 -390 97 -1088 331 -496 99 -300 65 -430 265 -402 197 -164 163 -596 165 -430 199 -1026 99 -1460 133 -1394 99 -1998 165 -300 65 -2360 165 -600 65 -3364 197 -658 131 -6356 99 -530 97 -166 165 -200 131 -132 133 -600 65 -5300 67 -762 131 -860 97 -1480 67 -1814 99 -1330 65 -266 99 -1918 99 -1360 165 -234 133 -5506 99 -468 65 -1894 131 -98 65 -1624 99 -98 67 -1808 65 -230 63 -786 99 -1970 131 -3506 99 -196 163 -1058 99 -164 67 -1730 165 -3516\nRAW_Data: 99 -666 99 -700 133 -98 133 -1086 163 -768 65 -1026 2523 -166 2747 -964 195 -362 265 -966 131 -198 197 -100 331 -330 65 -164 131 -528 163 -230 229 -68 97 -332 393 -134 197 -132 197 -330 165 -268 165 -198 133 -564 65 -232 233 -266 267 -432 199 -232 99 -66 131 -532 97 -132 299 -1252 133 -166 199 -796 99 -98 233 -398 197 -1590 163 -826 65 -764 165 -1658 163 -66 165 -698 165 -198 67 -164 99 -998 2029 -232 331 -100 691 -66 523 -98 561 -166 725 -560 65 -362 163 -132 395 -526 67 -166 67 -960 265 -298 131 -464 131 -200 329 -598 199 -132 163 -294 129 -754 99 -294 197 -432 165 -232 67 -202 99 -234 199 -326 163 -824 229 -132 259 -462 99 -266 65 -328 131 -326 229 -562 97 -230 97 -132 3077 -100 1031 -100 131 -100 165 -132 295 -68 97 -364 233 -264 65 -200 131 -562 331 -396 195 -228 197 -166 167 -266 165 -266 133 -662 165 -100 65 -466 531 -364 297 -526 65 -560 163 -364 99 -1678 131 -562 99 -268 65 -200 133 -1714 263 -986 2071 -624 99 -392 359 -296 99 -264 97 -230 295 -230 165 -766 165 -232 129 -302 97 -562 197 -66 97 -526 461 -166 131 -1146 131 -264 229 -788 65 -68 165 -66 97 -100 133 -100 99 -400 65 -198 97 -526 65 -360 195 -854 163 -362 97 -198 65 -362 97 -230 97 -690 99 -424 295 -200 197 -1052 2919 -134 565 -634 99 -498 331 -634 99 -300 65 -598 131 -330 131 -166 163 -132 65 -98 231 -130 65 -296 65 -166 259 -396 99 -362 65 -464 165 -232 131 -196 97 -526 97 -824 261 -330 229 -68 297 -560 397 -466 131 -796 329 -334 231 -166 133 -100 167 -236 335 -364 529 -762 199 -232 201 -1130 131 -866 99 -698 133 -1556 97 -664 99 -300 165 -598 131 -1688 199 -1062 97 -398 331 -164 65 -922 657 -198 393 -98 229 -428 555 -130 163 -394 397 -132 261 -264 65 -130 65 -262 493 -132 363 -134 165 -66 133 -402 265 -330 199 -132 565 -366 99 -134 163 -134 165 -200 99 -132 167 -332 431 -66 399 -200 199 -266 65 -830 133 -2050 97 -888 129 -2404 97 -2646 133 -2058 65 -1924 65 -530 97 -1646 65 -234 97 -3752 65 -1550 165 -2670 99 -328 97 -5326 97 -2246 65 -1094 165 -1672 97 -1582 97 -3722 65 -5080 65 -1676 197 -2936 665 -462 131 -264 65 -168 233 -298 65 -132 65 -600 67 -132 163 -296 99 -1318 131 -562 495 -396 263 -132 133 -200 131 -132 129 -264 263 -164 97 -132 131 -2308 99 -1956 167 -298 99 -592 163 -462 67 -3200 295 -620 65 -824 133 -64 131 -1552\nRAW_Data: 165 -1526 131 -690 65 -526 97 -132 129 -362 65 -198 97 -456 131 -262 575 -1010 1019 -1008 1003 -520 1513 -1016 999 -1012 1009 -522 1511 -1504 517 -1012 1011 -1514 511 -1010 1021 -514 1501 -1514 511 -1504 517 -1510 521 -492 1525 -1506 517 -514 1505 -522 1515 -484 1531 -1012 1011 -60190 1519 -1538 493 -1018 1005 -1514 515 -514 1495 -1528 513 -1502 515 -1010 1011 -1516 517 -514 1489 -1538 475 -1024 1003 -1534 515 -1498 505 -1036 987 -1018 1033 -1510 483 -1538 507 -1000 1009 -526 1505 -1534 491 -59172 529 -1018 1007 -1008 1001 -518 1507 -1024 1009 -1008 1035 -518 1499 -1526 481 -1044 979 -1546 475 -1026 1011 -516 1503 -1516 503 -1524 513 -1512 519 -482 1525 -1540 473 -510 1513 -518 1507 -518 1505 -1048 983 -60204 1495 -1594 439 -1062 983 -1542 461 -548 1503 -1548 449 -1540 507 -1034 1003 -1514 489 -516 1537 -1510 507 -1014 993 -1542 481 -1512 521 -1012 1015 -1006 1007 -1510 503 -1548 495 -1018 1007 -482 1531 -1514 505 -59196 507 -1042 979 -1048 973 -530 1515 -1012 1015 -1018 1005 -522 1505 -1508 519 -1014 993 -1542 483 -1018 997 -514 1529 -1508 517 -1528 473 -1546 495 -524 1505 -1504 501 -518 1505 -518 1505 -518 1513 -1016 997 -60198 495 -7704 1361 -1648 391 -1632 407 -1100 949 -1576 425 -566 1495 -1526 481 -1042 981 -1552 481 -1534 513 -1010 1013 -990 1007 -1536 511 -1506 487 -1020 1011 -526 1511 -1504 503 -59194 513 -1022 1013 -1016 989 -552 1465 -1046 1009 -1014 987 -518 1507 -1544 483 -1016 1017 -1508 515 -1010 1009 -488 1545 -1506 515 -1504 505 -1542 477 -516 1529 -1508 523 -486 1535 -500 1511 -516 1509 -1018 1011 -60166 1513 -1600 431 -1088 945 -1568 447 -580 1471 -1548 479 -1540 475 -1046 973 -1554 479 -510 1533 -1514 481 -1038 1017 -1506 487 -1548 509 -994 1013 -1008 1017 -1514 505 -1514 509 -1004 1015 -520 1517 -1516 513 -136452 65 -496 99 -796 131 -830 197 -168 99 -166 67 -2820 97 -134 65 -298 65 -168 99 -2792 97 -800 67 -3482 231 -166 99 -564 67 -266 99 -932 99 -68 397 -462 165 -66 165 -100 199 -68 497 -98 265 -498 593 -130 99 -360 561 -164 721 -66 131 -392 133 -198 201 -134 233 -66 99 -66 265 -558 163 -232 99 -66 729 -66 165 -164 99 -2250 65 -2340 131 -2166 99 -3386 63 -786 99 -426 197 -1022 197 -1564 131 -5336 99 -294 231 -166 65 -8582 133 -530 133 -234 131 -660 133 -1922 99 -3746 197 -402 65 -302 99 -1456 65 -954 65 -2616 197 -1694 165 -1584 261 -432 65 -986 65 -890 229 -132 97 -298 197 -266 67 -66 65 -200 229 -1018 99 -524 195 -228 165 -166 101 -398 99 -332 331 -1514 259 -164 97 -198 63 -198 95 -690 657 -232 233 -66 133 -68\nRAW_Data: 131 -298 99 -560 199 -984 227 -294 265 -462 97 -230 2307 -68 201 -100 99 -132 2079 -134 163 -234 399 -366 67 -366 131 -268 65 -202 165 -100 97 -364 99 -200 65 -234 199 -230 297 -132 199 -100 99 -696 133 -100 333 -534 165 -166 67 -498 331 -266 165 -696 459 -592 197 -330 131 -98 261 -1578 131 -1016 97 -66 97 -426 99 -630 263 -166 4367 -198 99 -166 99 -334 65 -400 299 -330 67 -926 265 -134 231 -134 197 -66 459 -200 265 -166 131 -132 67 -166 197 -400 165 -134 165 -664 197 -1196 99 -398 65 -564 167 -730 97 -266 165 -430 265 -634 133 -566 199 -66 2627 -366 297 -98 97 -100 131 -164 163 -628 197 -98 97 -428 397 -466 131 -68 99 -132 131 -1286 231 -98 165 -430 97 -364 129 -98 97 -562 65 -200 133 -996 131 -166 65 -234 67 -796 99 -630 297 -1058 165 -134 133 -1230 1931 -98 783 -196 99 -66 97 -532 65 -330 165 -400 333 -696 131 -398 65 -166 297 -198 65 -368 133 -366 199 -132 65 -134 197 -166 167 -266 99 -666 65 -458 197 -1418 229 -130 65 -562 131 -762 99 -564 97 -1064 65 -1158 99 -1364 2125 -132 231 -232 1221 -68 133 -200 165 -198 65 -664 231 -332 261 -494 97 -788 99 -166 131 -368 99 -762 197 -66 131 -134 331 -922 293 -200 165 -198 131 -132 163 -362 395 -64 263 -986 297 -724 163 -888 195 -164 65 -264 129 -196 493 -896 131 -332 165 -434 65 -232 101 -98 97 -598 99 -566 99 -398 165 -200 133 -100 267 -396 65 -332 131 -624 65 -558 63 -396 97 -596 261 -1120 99 -786 131 -130 131 -360 363 -462 233 -366 97 -166 265 -230 65 -756 131 -1646 195 -166 295 -1658 65 -66 195 -1684 65 -6928 99 -1488 65 -164 197 -556 99 -330 97 -528 195 -1352 197 -1590 99 -66 133 -1328 131 -366 97 -2218 65 -600 65 -66 265 -232 133 -1496 133 -462 97 -66 131 -2352 99 -196 67 -2794 99 -2374 97 -100 99 -198 63 -2074 65 -5114 65 -166 99 -3616 133 -1588 97 -1622 163 -98 97 -888 131 -328 195 -196 197 -362 99 -792 161 -164 131 -266 133 -100 65 -234 65 -500 97 -864 131 -132 99 -664 99 -300 99 -300 65 -98 99 -1296 165 -168 231 -796 99 -232 233 -1092 131 -264 361 -964 165 -100 99 -200 263 -2502 165 -630 65 -134 65 -3342 131 -436 199 -564 65 -466 131 -1458 131 -662 131 -936 65 -2260 99 -630 163 -132 65 -4312 197 -330 65 -1126 265 -1694 65 -166 97 -558 97 -694 97 -1754 65 -1656 65 -366 97 -1118 263 -1550 295 -1448 165 -1618 99 -132 163 -1622 165 -1198\nRAW_Data: 231 -730 99 -432 63 -1350 131 -226 197 -432 2459 -66 165 -64 493 -98 1215 -232 229 -494 131 -396 197 -726 165 -196 195 -330 197 -130 99 -134 399 -334 131 -532 99 -1018 99 -164 227 -230 97 -592 295 -66 131 -66 229 -132 559 -724 97 -164 297 -1396 65 -662 65 -1086 97 -1226 65 -166 133 -232 65 -1196 331 -234 67 -564 2619 -132 131 -164 1089 -68 97 -100 997 -66 165 -98 463 -198 133 -266 199 -100 333 -234 197 -198 199 -564 197 -564 163 -792 65 -230 329 -98 65 -234 99 -598 99 -330 327 -198 197 -166 163 -130 131 -1678 575 -1000 1041 -990 1009 -1532 503 -502 1527 -1516 499 -488 1541 -1506 505 -1014 1031 -482 1543 -1504 503 -504 1527 -480 1547 -476 1527 -1002 1021 -514 1527 -1010 1013 -1518 481 -1036 1015 -480 1537 -1516 485 -60180 1555 -994 1009 -512 1503 -506 1531 -518 1505 -1008 1009 -1016 997 -1544 487 -1546 483 -1512 519 -1012 1017 -1508 489 -1544 477 -530 1515 -1510 519 -1506 489 -1048 1005 -1010 1001 -1526 509 -992 1011 -1038 1005 -59156 527 -1090 885 -1112 909 -1604 439 -572 1457 -1580 457 -548 1463 -1562 481 -1046 981 -506 1527 -1510 523 -482 1539 -482 1529 -520 1503 -1018 1011 -514 1497 -1016 1011 -1520 511 -1014 1019 -488 1537 -1510 515 -60180 1501 -1080 947 -546 1493 -538 1485 -518 1505 -1044 973 -1058 983 -1538 465 -1542 501 -1512 509 -1006 1011 -1532 513 -1502 515 -514 1503 -1516 517 -1506 515 -1012 1009 -1018 1001 -1510 527 -1014 1009 -1014 987 -59196 515 -1014 1009 -1016 1015 -1538 483 -516 1507 -1542 479 -512 1531 -1506 489 -1018 1009 -530 1515 -1506 519 -488 1533 -514 1509 -488 1545 -1008 1015 -512 1497 -1016 999 -1548 497 -1014 1009 -482 1543 -1514 499 -60192 493 -7688 1397 -1156 881 -1120 891 -1632 397 -1618 417 -1580 479 -1042 967 -1548 483 -1542 475 -542 1505 -1516 515 -1504 505 -1018 1007 -1018 1009 -1504 537 -984 1013 -1022 1007 -59174 501 -11466 659 -1656 353 -1180 847 -632 1425 -1592 451 -550 1473 -548 1463 -570 1481 -1042 971 -540 1495 -1014 1001 -1544 479 -1034 1001 -518 1509 -1514 513 -60202 1527 -1042 963 -554 1471 -550 1499 -516 1503 -1018 1009 -1012 1019 -1512 501 -1518 513 -1508 501 -1038 1005 -1508 513 -1506 521 -488 1531 -1508 521 -1512 513 -990 1009 -1038 1003 -1514 515 -1008 1017 -1010 1011 -132210 97 -1712 231 -2272 97 -4094 65 -526 65 -2118 67 -432 99 -300 131 -364 165 -1226 199 -134 99 -100 465 -232 99 -726 133 -532 199 -266 133 -232 65 -234 265 -1586 131 -528 99 -696 99 -132 297 -398 67 -464 131 -166 267 -466 165 -764 197 -100 65 -1026 65 -3972 65 -1718 133 -630 99 -200 131 -68 65 -4004 163 -2078 65 -464 131 -496\nRAW_Data: 99 -134 131 -398 65 -1090 295 -132 65 -1646 65 -1396 233 -1296 199 -100 97 -134 65 -764 265 -760 197 -296 65 -526 197 -100 197 -198 63 -1414 163 -1316 195 -98 229 -200 97 -824 99 -100 131 -66 3137 -198 393 -164 131 -824 101 -66 399 -200 67 -232 133 -168 99 -198 65 -366 597 -898 197 -98 65 -330 131 -362 133 -864 97 -632 131 -654 195 -1260 231 -398 99 -626 65 -460 131 -722 99 -300 65 -464 165 -1060 165 -366 67 -132 2189 -66 299 -166 97 -464 99 -200 165 -596 231 -596 65 -134 65 -166 197 -232 265 -266 67 -232 133 -400 199 -232 333 -200 65 -466 65 -430 363 -230 97 -64 197 -758 131 -66 65 -592 131 -558 165 -266 133 -264 165 -1102 131 -134 97 -926 67 -496 197 -1252 2593 -66 229 -66 687 -98 1119 -66 659 -98 197 -960 297 -166 99 -524 429 -1022 99 -364 201 -200 65 -100 199 -498 99 -198 67 -132 399 -432 99 -492 295 -66 197 -66 131 -3066 395 -1156 131 -302 99 -792 161 -264 329 -66 163 -98 259 -762 131 -994 165 -1742 229 -830 165 -430 323 -98 295 -130 99 -368 65 -266 265 -1554 197 -492 2511 -68 795 -132 699 -132 99 -66 633 -562 197 -204 499 -364 99 -200 99 -794 231 -266 99 -564 65 -200 197 -68 429 -198 99 -830 165 -794 99 -166 133 -534 359 -298 99 -234 199 -492 67 -98 167 -528 65 -1858 97 -856 65 -64 131 -462 129 -592 131 -198 97 -396 131 -690 99 -232 2427 -68 2469 -396 467 -264 99 -532 133 -232 63 -362 229 -462 525 -264 97 -260 97 -230 165 -132 131 -992 133 -796 129 -1682 129 -362 99 -230 99 -132 99 -100 65 -1590 265 -1690 197 -1590 97 -392 231 -1454 133 -1298 65 -2296 65 -230 133 -300 99 -2750 65 -560 131 -2966 97 -230 129 -1054 133 -1296 65 -992 67 -5244 167 -200 65 -6756 229 -1608 229 -166 65 -66 97 -396 97 -130 197 -728 163 -2338 197 -132 131 -66 99 -100 97 -300 99 -664 265 -432 99 -696 133 -132 131 -866 233 -134 97 -266 199 -266 229 -1126 99 -100 131 -1424 199 -1594 131 -2186 99 -5780 67 -760 133 -1630 167 -832 99 -2854 65 -2846 65 -5308 231 -1616 97 -1684 131 -236 65 -3044 99 -1594 131 -2108 99 -1660 97 -3448 99 -3556 65 -2690 97 -1648 165 -1858 65 -1492 165 -2882 199 -402 99 -1196 131 -132 99 -66 65 -1728 67 -132 99 -1164 265 -398 99 -166 129 -132 131 -2468 65 -1658 201 -1688 99 -1712 131 -132 99 -992 65 -428 65 -100 165 -498 165 -666 199 -500 329 -732 99 -132 67 -692 65 -362 197 -132 97 -1314 325 -596\nRAW_Data: 165 -66 165 -666 165 -632 65 -198 263 -564 263 -198 99 -230 97 -228 263 -524 231 -98 133 -498 265 -166 99 -924 325 -328 65 -360 165 -196 129 -166 327 -822 99 -398 99 -266 131 -562 99 -460 97 -164 295 -200 197 -964 99 -796 99 -596 165 -1088 65 -200 129 -724 131 -662 295 -724 65 -398 131 -234 99 -430 3857 -66 163 -66 1023 -794 99 -198 363 -68 65 -198 99 -132 265 -296 65 -264 197 -66 167 -530 165 -462 131 -66 425 -428 131 -162 65 -594 493 -362 131 -394 165 -130 297 -394 557 -760 97 -1284 197 -1490 163 -1680 131 -392 231 -264 65 -624 229 -1090 2037 -134 457 -98 131 -164 1691 -100 65 -300 199 -798 99 -528 99 -198 97 -296 99 -262 131 -66 197 -292 133 -198 99 -166 197 -66 199 -1454 229 -396 131 -130 131 -690 99 -98 97 -66 65 -362 63 -98 163 -164 397 -1028 65 -232 99 -66 197 -264 67 -166 165 -202 65 -1134 99 -132 567 -364 197 -696 267 -628 133 -200 133 -166 2173 -296 561 -66 765 -164 97 -266 99 -366 263 -332 99 -566 231 -98 99 -100 265 -132 67 -398 65 -166 97 -230 163 -396 99 -164 97 -98 163 -260 99 -164 229 -1448 163 -298 723 -428 99 -366 65 -330 131 -400 97 -366 131 -596 65 -166 133 -332 99 -366 99 -628 65 -532 165 -66 133 -264 131 -2726 267 -270 131 -232 65 -66 67 -432 165 -200 67 -1030 167 -166 165 -630 231 -196 63 -428 65 -764 2901 -132 99 -198 131 -132 359 -198 63 -132 129 -692 65 -200 233 -98 165 -332 67 -330 133 -166 297 -532 497 -68 165 -168 331 -132 165 -230 333 -564 165 -100 65 -1258 165 -266 99 -760 65 -298 133 -532 163 -464 197 -592 165 -1678 97 -1086 99 -460 65 -2510 97 -66 165 -1428 131 -1280 65 -66 97 -2108 99 -394 129 -230 129 -298 97 -1446 97 -266 97 -230 577 -1538 475 -1542 485 -1018 1023 -514 1503 -490 1523 -1014 1021 -1510 519 -1512 513 -992 1011 -1012 1013 -1032 997 -514 1497 -518 1535 -1010 1001 -1530 481 -504 1521 -1534 473 -522 1539 -1492 513 -514 1511 -60178 1531 -1034 971 -518 1509 -518 1505 -518 1533 -488 1509 -516 1509 -1546 475 -1044 989 -1012 1007 -1544 475 -516 1531 -1008 1011 -490 1545 -1002 1017 -1506 487 -552 1501 -500 1517 -516 1511 -504 1501 -1026 1011 -59168 491 -7578 461 -670 1359 -1184 881 -1614 435 -1566 461 -1050 977 -1050 975 -1042 993 -518 1509 -524 1517 -1014 991 -1508 519 -490 1541 -1502 513 -514 1533 -1506 507 -484 1535 -60182 1535 -1060 949 -554 1483 -540 1467 -558 1475 -550 1469 -538 1499 -1554 481 -1008 1001 -1050 975 -1524 511 -516\nRAW_Data: 1503 -1018 1011 -526 1511 -1014 1017 -1518 495 -488 1537 -482 1527 -522 1503 -526 1505 -1006 1015 -59192 495 -1582 451 -1542 479 -1038 975 -568 1481 -514 1499 -1036 1003 -1512 493 -1544 483 -1012 1033 -1016 1003 -1016 1009 -514 1497 -520 1511 -1018 1011 -1506 519 -490 1533 -1508 519 -492 1525 -1508 515 -514 1501 -60200 1545 -1014 1011 -482 1543 -488 1541 -484 1531 -506 1497 -522 1509 -1520 513 -1012 1019 -986 1037 -1514 513 -486 1541 -1006 1009 -486 1539 -1014 987 -1534 501 -490 1545 -482 1539 -488 1545 -484 1533 -1006 1017 -59174 499 -7598 461 -644 1401 -1094 915 -1614 425 -1572 471 -1060 977 -1012 1019 -1012 1007 -524 1505 -514 1497 -1038 983 -1534 513 -478 1535 -1516 519 -480 1523 -1536 503 -504 1527 -60172 1523 -1078 949 -546 1465 -566 1449 -554 1477 -550 1501 -526 1507 -1508 507 -1014 1005 -1022 1011 -1504 505 -520 1511 -1022 1009 -516 1505 -1018 1007 -1516 511 -506 1529 -488 1517 -524 1507 -512 1529 -984 1011 -132432 263 -1692 97 -1216 65 -788 65 -1320 231 -6950 229 -1658 133 -3184 97 -166 131 -1296 99 -196 99 -2900 165 -1658 131 -1856 99 -1952 133 -632 133 -4016 131 -136 99 -166 99 -1352 95 -2420 99 -98 97 -856 129 -5964 99 -1588 163 -66 65 -1720 65 -2716 67 -398 67 -398 97 -1284 65 -100 65 -788 131 -1748 97 -1596 263 -66 163 -332 431 -130 1161 -198 165 -164 133 -100 199 -338 131 -432 163 -736 231 -660 101 -100 99 -266 99 -102 231 -966 99 -402 465 -464 65 -398 463 -492 231 -430 165 -3936 65 -6980 131 -1248 65 -428 133 -434 199 -728 65 -756 99 -198 97 -4038 133 -2028 65 -2102 65 -660 99 -2552 65 -1860 65 -466 131 -798 65 -1780 165 -1710 261 -132 65 -394 65 -1828 165 -730 65 -1394 97 -132 259 -296 63 -1674 97 -3318 133 -1658 133 -502 65 -2886 131 -1626 163 -196 97 -1414 65 -3520 99 -794 195 -2476 65 -1846 131 -2044 99 -1450 99 -922 99 -1908 65 -262 97 -1776 97 -996 233 -1460 231 -232 131 -702 99 -166 67 -166 165 -168 67 -500 131 -396 99 -298 199 -232 65 -934 299 -334 99 -100 65 -632 165 -168 65 -1226 163 -66 265 -202 233 -862 99 -166 233 -202 231 -762 99 -266 65 -300 65 -1390 67 -3772 131 -528 99 -1316 65 -694 63 -864 197 -1228 67 -430 97 -1712 163 -5212 165 -1624 165 -434 99 -1092 165 -3986 131 -5164 65 -496 65 -4734 163 -1648 97 -1710 199 -1714 99 -1460 359 -1578 165 -1096 67 -626 65 -2172 65 -1580 65 -164 65 -1450 131 -3556 163 -4194 165 -6992 233 -264 97 -464 131 -730 65 -366 65 -528 67 -66 197 -796 131 -298 163 -264 131 -262 131 -826 195 -166 127 -130\nRAW_Data: 293 -328 97 -460 197 -624 65 -262 63 -626 295 -100 131 -66 163 -132 163 -294 97 -164 165 -168 65 -400 299 -68 265 -198 331 -1494 97 -66 131 -1094 1955 -66 723 -132 593 -66 391 -132 1063 -264 2121 -100 1093 -66 165 -100 197 -264 233 -730 263 -166 99 -166 397 -234 165 -728 231 -132 261 -130 163 -194 99 -796 199 -668 131 -826 165 -1626 131 -198 99 -1360 131 -200 131 -132 67 -1058 99 -200 67 -200 67 -768 2605 -96 163 -130 691 -64 1149 -98 163 -132 97 -198 261 -98 131 -1258 297 -430 97 -500 65 -430 131 -398 65 -264 165 -298 165 -134 463 -366 131 -432 565 -166 197 -98 97 -198 133 -230 197 -132 261 -100 65 -592 65 -824 65 -3266 231 -68 65 -432 129 -392 2799 -100 1151 -64 883 -98 99 -562 131 -66 197 -430 195 -196 99 -200 463 -400 99 -1032 131 -330 231 -1092 163 -858 97 -164 97 -226 65 -394 163 -1050 261 -950 263 -230 199 -1786 65 -558 65 -168 65 -396 131 -196 197 -264 99 -588 97 -230 163 -692 97 -262 97 -164 2923 -100 131 -66 561 -162 163 -198 97 -732 133 -732 165 -930 67 -1228 361 -298 131 -928 65 -166 201 -1096 329 -134 299 -432 197 -166 133 -630 165 -896 133 -1126 97 -164 295 -266 265 -134 199 -564 129 -896 131 -600 131 -658 65 -166 499 -232 133 -332 195 -332 497 -100 65 -330 97 -230 129 -362 131 -330 199 -1256 65 -660 65 -790 165 -1358 199 -166 133 -1226 2239 -66 627 -100 595 -132 233 -166 99 -102 299 -66 99 -66 133 -198 167 -266 163 -98 197 -200 233 -466 331 -198 233 -98 99 -332 99 -132 629 -330 497 -466 65 -334 65 -166 131 -300 65 -564 165 -168 99 -1462 65 -134 131 -924 99 -266 233 -300 67 -494 129 -364 97 -662 357 -786 197 -1626 131 -200 65 -396 99 -666 131 -896 131 -600 163 -136 65 -1594 65 -2042 65 -1584 99 -1920 65 -3128 195 -3316 99 -890 195 -626 131 -722 161 -98 97 -626 527 -66 465 -100 295 -200 65 -66 233 -66 163 -166 423 -164 65 -458 295 -524 593 -64 197 -162 65 -98 97 -264 131 -100 327 -132 229 -98 131 -556 395 -66 197 -1060 131 -232 233 -134 65 -464 793 -1620 67 -4074 97 -1018 163 -328 97 -5922 65 -624 133 -668 97 -830 267 -166 133 -100 165 -694 99 -134 231 -230 97 -592 65 -100 229 -490 65 -790 229 -954 97 -198 163 -368 99 -664 65 -100 65 -168 199 -166 233 -600 101 -132 99 -68 65 -232 167 -268 199 -330 263 -496 65 -830 133 -892 99 -1450 129 -3654 63 -1680 99 -1384 97 -2114 97 -430\nRAW_Data: 199 -232 65 -530 131 -1814 65 -164 195 -66 97 -164 65 -232 97 -858 231 -296 163 -164 1939 -266 829 -200 1229 -132 167 -200 231 -230 133 -166 99 -300 331 -332 99 -132 167 -232 67 -864 131 -266 65 -266 131 -200 167 -334 99 -132 231 -134 65 -232 133 -500 65 -66 99 -66 165 -660 99 -198 367 -364 97 -396 131 -1158 63 -1312 131 -164 163 -858 99 -628 363 -330 99 -166 99 -430 97 -330 65 -558 165 -562 2495 -164 423 -66 1653 -166 199 -566 165 -134 97 -234 97 -234 265 -234 99 -232 67 -98 99 -98 131 -266 231 -66 67 -168 197 -996 133 -66 165 -132 133 -198 99 -830 295 -530 99 -166 297 -332 199 -296 129 -1284 165 -694 97 -732 263 -1554 65 -2058 231 -432 99 -264 2167 -164 99 -66 261 -98 719 -496 131 -564 231 -364 65 -466 201 -400 199 -200 67 -232 363 -630 99 -98 65 -500 333 -332 99 -200 397 -366 331 -560 99 -394 231 -130 195 -230 165 -1034 97 -66 131 -1060 97 -562 163 -488 131 -822 229 -500 197 -530 65 -796 197 -164 261 -756 163 -430 65 -530 131 -434 133 -168 133 -298 65 -530 427 -494 65 -628 261 -162 129 -196 131 -1624 65 -332 97 -498 231 -328 99 -166 101 -664 1211 -1510 491 -1542 485 -1020 1015 -510 1493 -1540 489 -534 1501 -1526 479 -1034 1001 -1508 507 -1016 1037 -484 1533 -1500 503 -1030 1007 -998 1009 -1038 1009 -1514 511 -496 1511 -512 1533 -486 1507 -1020 1007 -60200 1525 -1016 999 -514 1539 -484 1511 -520 1507 -514 1529 -474 1537 -522 1505 -1508 505 -1002 1033 -492 1531 -1506 523 -1012 1005 -480 1527 -1018 1011 -1024 1007 -1500 535 -1498 513 -1008 1023 -1008 1013 -492 1527 -59182 477 -1646 337 -1694 365 -1122 881 -618 1405 -1638 407 -600 1455 -1546 461 -1076 971 -1552 447 -1042 1015 -518 1503 -1512 515 -1008 1001 -1016 1011 -1024 1009 -1506 501 -512 1539 -484 1541 -484 1543 -1006 1005 -60182 535 -11720 1363 -658 1347 -1682 373 -1136 897 -590 1447 -1582 441 -1072 957 -550 1501 -1018 997 -1044 989 -1546 485 -1516 517 -1010 1011 -1010 1009 -492 1541 -59174 443 -11526 625 -1648 397 -1106 933 -1584 451 -1084 957 -546 1465 -1548 485 -1046 969 -1052 1001 -1018 977 -1540 507 -520 1505 -490 1535 -482 1531 -1018 1009 -60222 1533 -974 1047 -478 1537 -490 1527 -520 1505 -514 1507 -518 1527 -480 1543 -1506 507 -1008 1001 -522 1501 -1520 511 -1008 1033 -486 1515 -1020 1011 -1012 1013 -1532 511 -1488 513 -1006 1033 -1006 997 -516 1523 -59176 497 -1632 329 -1714 353 -1114 905 -608 1429 -1580 453 -580 1433 -1596 451 -1048 981 -1542 473 -1060 979 -518 1505 -1526 509 -996 1013 -1010 1015 -1016 1007 -1520 511 -516\nRAW_Data: 1505 -522 1509 -488 1537 -1014 1017 -60198 1533 -978 1025 -520 1503 -516 1503 -518 1527 -486 1543 -484 1533 -508 1495 -1548 475 -1026 1013 -516 1503 -1514 505 -1024 1009 -516 1503 -1018 1007 -1018 1009 -1504 501 -1544 497 -1020 1009 -1010 1017 -488 1539 -137350 65 -794 165 -166 65 -432 295 -428 329 -164 131 -324 97 -198 65 -230 165 -166 431 -532 263 -232 67 -430 197 -66 893 -1522 165 -3076 65 -328 163 -2006 65 -894 197 -1362 99 -132 99 -1660 231 -1580 263 -1126 99 -266 133 -864 65 -4456 163 -196 199 -3214 167 -398 67 -1322 63 -1148 197 -1698 197 -1014 65 -7018 133 -462 165 -1664 99 -894 65 -634 327 -1222 97 -6562 97 -2512 131 -758 65 -592 131 -1182 97 -1792 65 -230 129 -1284 65 -1020 65 -988 99 -5252 163 -1692 131 -1970 65 -198 197 -728 65 -366 363 -1592 131 -1216 165 -360 163 -526 99 -460 99 -332 201 -232 65 -200 67 -268 165 -532 197 -268 167 -566 265 -132 231 -194 129 -198 163 -198 195 -394 361 -762 99 -166 99 -536 99 -3384 65 -1684 99 -66 99 -790 133 -1716 97 -1710 165 -1624 231 -2062 99 -1362 299 -3378 65 -196 97 -854 65 -1120 99 -200 99 -268 133 -432 131 -1956 67 -664 65 -1556 65 -1050 97 -132 97 -4740 131 -132 99 -1660 199 -864 165 -1582 133 -1614 131 -590 63 -4394 263 -296 231 -3032 197 -1670 65 -756 131 -4392 131 -1654 167 -168 67 -368 65 -832 99 -100 133 -1532 133 -1690 99 -868 131 -662 131 -3508 97 -852 63 -1120 65 -3144 163 -662 97 -594 97 -98 229 -1126 67 -266 65 -132 195 -296 65 -196 129 -262 99 -462 199 -762 65 -396 65 -328 229 -530 231 -264 165 -200 301 -894 65 -264 195 -166 99 -852 195 -1186 97 -66 97 -166 63 -3368 65 -230 65 -1152 99 -1624 165 -1624 65 -1656 165 -1754 99 -430 131 -3440 65 -3044 99 -2126 199 -1190 233 -232 133 -1722 65 -1658 197 -3220 263 -1780 65 -1590 97 -134 133 -1386 97 -3256 165 -164 99 -1856 99 -232 133 -1298 231 -2322 65 -200 131 -3254 97 -2892 131 -534 231 -364 99 -862 99 -68 99 -1388 65 -2078 131 -632 97 -1850 65 -724 97 -166 197 -100 201 -534 99 -264 131 -100 133 -300 133 -334 99 -1158 163 -300 99 -364 99 -300 165 -1024 97 -66 131 -230 525 -298 131 -564 527 -432 65 -692 65 -2642 65 -568 133 -1884 65 -856 131 -922 65 -3040 99 -100 97 -1896 65 -300 165 -466 67 -98 233 -132 297 -5044 231 -2354 65 -3878 65 -626 99 -1746 99 -1850 131 -230 97 -2434 431 -200 199 -100 167 -298 65 -2452 133 -396 199 -1058 65 -624 131 -5426 129 -2438 229 -956\nRAW_Data: 197 -490 163 -662 163 -396 393 -66 163 -262 65 -328 65 -362 2243 -100 595 -100 1361 -334 299 -230 527 -594 199 -498 461 -798 265 -200 329 -166 267 -466 231 -132 197 -98 99 -264 163 -232 99 -364 99 -132 133 -200 131 -132 99 -68 99 -166 297 -66 297 -198 131 -324 67 -232 97 -166 99 -166 99 -830 99 -264 433 -66 133 -2620 65 -232 233 -556 197 -198 129 -428 197 -228 163 -630 265 -200 133 -200 133 -100 165 -264 131 -100 197 -832 97 -400 231 -300 67 -266 99 -232 67 -532 327 -662 65 -268 99 -332 265 -232 99 -498 3293 -98 67 -98 365 -134 97 -626 65 -166 129 -230 229 -798 165 -164 195 -98 65 -166 163 -298 233 -398 199 -232 163 -100 65 -196 261 -264 295 -328 99 -690 361 -264 195 -722 65 -132 261 -328 65 -822 129 -66 559 -624 97 -1390 99 -866 65 -630 163 -398 65 -200 65 -598 4821 -98 99 -360 561 -98 297 -428 197 -98 197 -690 427 -98 97 -66 99 -1128 297 -266 65 -364 165 -432 133 -366 65 -198 199 -166 97 -364 397 -964 67 -1560 165 -100 361 -630 129 -788 131 -818 67 -132 131 -556 99 -558 99 -722 99 -196 2437 -264 2337 -360 197 -166 65 -330 689 -166 395 -98 65 -396 131 -164 97 -66 593 -262 163 -398 331 -100 267 -266 231 -166 65 -232 231 -166 263 -394 97 -66 65 -730 133 -200 131 -100 429 -230 227 -560 65 -100 97 -132 131 -166 65 -662 131 -166 231 -986 99 -132 263 -200 265 -166 65 -164 163 -164 197 -1184 259 -294 2681 -100 1061 -134 133 -100 65 -530 199 -726 97 -566 497 -630 99 -300 199 -132 363 -168 97 -230 165 -100 263 -198 165 -330 99 -826 133 -1166 365 -1224 165 -1130 131 -134 165 -132 229 -828 99 -330 99 -694 67 -230 163 -558 231 -726 97 -132 129 -166 195 -560 65 -262 63 -396 165 -998 65 -134 99 -200 231 -66 131 -266 165 -362 229 -894 65 -3690 133 -132 131 -132 65 -828 67 -666 131 -1430 99 -200 99 -4978 67 -596 67 -132 99 -664 259 -3486 65 -794 63 -2578 165 -592 99 -236 197 -296 129 -2202 131 -892 197 -2080 263 -66 231 -200 131 -402 233 -896 65 -666 131 -4786 99 -3204 99 -2322 165 -2566 97 -1252 197 -1716 65 -198 97 -7054 99 -934 195 -1616 99 -3522 99 -3344 97 -66 131 -1652 299 -966 297 -1494 65 -3432 131 -1792 65 -1586 101 -1624 65 -66 65 -2622 65 -398 65 -852 165 -3880 99 -1660 197 -1592 129 -1386 131 -164 131 -394 97 -66 97 -824 195 -362 163 -66 131 -694 331 -300 65 -332 97 -296 163 -298 295 -464\nRAW_Data: 97 -764 329 -1064 233 -464 165 -64 129 -328 131 -330 229 -166 65 -332 65 -66 165 -162 163 -198 63 -98 63 -166 97 -298 63 -960 231 -394 99 -1128 133 -500 297 -764 97 -166 199 -334 97 -68 131 -330 99 -66 197 -200 65 -332 397 -1648 197 -526 97 -560 2903 -98 493 -98 99 -230 129 -756 231 -164 429 -362 133 -132 167 -432 263 -234 167 -166 265 -296 163 -98 361 -132 67 -164 67 -496 229 -296 193 -364 131 -1226 165 -164 99 -602 99 -132 231 -1394 65 -232 99 -166 165 -1254 165 -298 199 -330 99 -820 129 -928 65 -330 165 -66 133 -630 67 -166 197 -166 2079 -200 791 -98 1263 -564 363 -624 163 -230 131 -360 233 -130 63 -526 65 -528 363 -296 163 -270 131 -200 261 -166 531 -1434 297 -462 99 -100 131 -464 567 -402 67 -462 131 -494 131 -1196 67 -858 165 -230 97 -198 65 -196 197 -362 131 -2038 295 -166 2333 -66 165 -64 327 -98 65 -164 261 -432 165 -134 131 -100 233 -536 65 -266 631 -360 263 -66 97 -68 231 -396 133 -766 329 -230 65 -296 163 -294 65 -66 329 -230 301 -532 197 -334 133 -1160 199 -562 131 -332 131 -298 97 -928 199 -132 197 -664 197 -230 2811 -66 665 -134 165 -102 365 -166 267 -66 131 -198 201 -266 99 -166 131 -232 593 -264 131 -594 131 -100 263 -164 131 -198 97 -362 163 -164 131 -164 329 -262 295 -526 131 -66 197 -434 195 -66 195 -262 97 -98 525 -228 229 -890 429 -198 131 -268 67 -430 131 -1150 265 -198 99 -956 329 -1186 161 -262 163 -918 99 -332 65 -664 195 -262 295 -1480 297 -492 131 -494 231 -990 65 -198 65 -164 163 -1544 167 -868 65 -164 97 -98 97\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: Security+ 2.0\nBit: 62\nKey: 00 00 3D 29 0F B9 BE EE\nSecplus_packet_1: 00 00 3C 01 6B 19 DD 60\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297\nRAW_Data: -164 6323 -10770 65 -1420 227 -196 263 -198 197 -1086 99 -98 163 -164 163 -426 65 -362 97 -264 295 -132 197 -13586 65 -1808 131 -166 301 -66 465 -432 165 -330 65 -332 297 -962 99 -266 97 -166 265 -132 327 -198 329 -98 293 -14984 99 -862 131 -166 331 -68 165 -98 233 -132 201 -300 197 -364 133 -662 99 -398 99 -166 65 -432 133 -132 1447 -5882 197 -1082 65 -198 163 -1580 129 -264 67 -632 625 -134 165 -68 827 -100 165 -100 99 -164 3949 -9126 67 -164 131 -986 241 -534 309 -208 267 -226 247 -250 247 -248 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -246 247 -248 501 -258 247 -246 245 -248 247 -248 249 -248 249 -504 513 -226 243 -274 243 -500 513 -238 253 -256 253 -492 231 -262 485 -514 223 -244 527 -478 507 -274 255 -218 253 -498 261 -224 251 -250 503 -258 247 -246 243 -246 247 -504 515 -238 251 -472 277 -242 497 -246 289 -220 247 -498 513 -476 255 -68610 311 -208 235 -256 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 479 -274 253 -256 217 -254 247 -236 255 -250 247 -502 513 -240 251 -256 253 -254 249 -240 227 -252 249 -502 259 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -246 501 -226 245 -276 245 -246 245 -248 247 -504 515 -478 255 -244 495 -274 253 -476 511 -240 241 -69142 321 -218 243 -230 251 -248 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -506 257 -248 243 -246 247 -248 501 -258 247 -246 245 -246 247 -248 249 -248 249 -504 513 -238 251 -254 255 -490 499 -228 253 -250 249 -506 259 -248 499 -478 257 -244 495 -510 509 -238 255 -254 253 -494 227 -254 249 -248 505 -258 247 -246 245 -248 247 -504 513 -238 253 -472 279 -254 491 -226 253 -250 249 -504 513 -478 257 -68880 315 -216 241 -258 249 -248 247 -248 247 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 249 -504 257 -246 245 -246 247 -248 503 -516 477 -274 253 -256 217 -254 247 -236 255 -250 249 -502 481 -272 253 -254 255 -254 249 -238 227 -250 249 -504 257 -248 243 -246 501 -516 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -228 245 -274 245 -246 245 -248 247\nRAW_Data: -504 513 -478 257 -244 495 -274 253 -500 483 -232 265 -69136 287 -256 249 -238 227 -250 249 -248 249 -248 249 -248 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -250 247 -504 513 -238 251 -256 253 -496 499 -228 249 -250 249 -504 257 -248 499 -478 257 -244 495 -510 509 -238 255 -254 255 -492 227 -256 249 -248 505 -258 247 -246 245 -248 245 -504 513 -238 253 -472 279 -254 491 -228 253 -250 249 -504 513 -478 257 -68886 309 -208 235 -256 249 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 249 -504 257 -248 245 -246 245 -248 503 -514 477 -274 253 -254 217 -256 247 -238 255 -250 247 -504 513 -238 251 -256 253 -256 249 -240 227 -252 249 -504 257 -248 243 -246 501 -514 237 -252 473 -280 253 -250 241 -228 253 -248 249 -504 257 -246 499 -226 245 -276 245 -246 245 -248 247 -506 511 -478 257 -244 495 -272 255 -474 511 -240 241 -69150 323 -220 251 -242 229 -252 247 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 243 -248 247 -248 503 -258 245 -246 245 -248 245 -250 247 -250 247 -504 515 -236 253 -254 253 -496 499 -228 251 -250 247 -504 257 -248 499 -478 257 -244 495 -510 509 -240 253 -256 253 -492 229 -254 249 -250 505 -258 247 -246 245 -246 247 -504 513 -238 253 -472 247 -272 501 -246 253 -254 243 -494 513 -476 255 -68934 287 -254 213 -240 257 -250 249 -248 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -506 257 -248 245 -246 245 -248 503 -516 479 -272 255 -254 217 -256 245 -236 255 -250 247 -504 513 -238 253 -254 253 -256 249 -238 229 -252 249 -504 257 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -226 245 -276 245 -246 245 -248 247 -504 511 -478 255 -244 495 -256 243 -496 509 -274 253 -69130 309 -218 253 -256 249 -240 227 -252 249 -250 247 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -504 259 -246 245 -246 247 -248 501 -258 247 -244 245 -248 247 -248 247 -250 249 -504 511 -226 245 -276 243 -500 477 -272 253 -254 255 -494 231 -262 481 -514 225 -246 527 -478 507 -256 241 -244 243 -494 257 -244 243\nRAW_Data: -246 527 -228 243 -276 245 -246 245 -500 513 -238 253 -474 279 -254 489 -228 251 -250 249 -506 513 -478 255 -68936 287 -218 249 -236 255 -248 247 -248 247 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -506 257 -246 245 -246 245 -248 503 -514 477 -272 255 -254 255 -218 247 -240 255 -250 247 -502 511 -226 273 -244 243 -244 247 -246 247 -250 247 -506 259 -246 245 -246 501 -512 225 -244 527 -238 251 -256 253 -254 251 -242 227 -508 257 -216 527 -226 275 -244 243 -246 245 -248 247 -504 513 -478 257 -242 527 -238 255 -472 511 -242 281 -119614 299 -200 65 -470 65 -466 297 -630 97 -1592 133 -166 299 -66 231 -100 131 -98 265 -134 165 -166 433 -100 2287 -9916 231 -166 65 -98 199 -166 133 -166 165 -756 65 -724 195 -428 231 -260 263 -98 229 -130 261 -66 163 -264 65 -132 1181 -66 6315 -6798 131 -296 559 -334 131 -166 233 -132 165 -66 133 -264 99 -66 65 -366 99 -630 301 -166 97 -100 167 -164 535 -202 7269 -8266 197 -1022 131 -756 99 -98 99 -164 163 -990 65 -530 163 -230 297 -136 635 -66 2113 -8426 67 -6674 97 -722 197 -362 263 -232 165 -134 99 -234 297 -362 129 -198 131 -556 297 -68 167 -98 331 -200 165 -66 295 -66 8689 -4994 65 -1750 165 -762 163 -864 135 -100 167 -694 1093 -66 695 -102 99 -100 9899 -1650 297 -1216 97 -66 99 -396 65 -198 165 -164 233 -1658 199 -98 465 -134 463 -166 1883 -98 6283 -7302 99 -932 133 -696 263 -298 97 -98 165 -1708 131 -820 229 -98 231 -130 163 -590 131 -130 99 -66 97 -16220 261 -1062 265 -998 197 -1290 97 -362 165 -494 895 -264 7839 -7804 99 -66 99 -364 231 -630 133 -166 427 -496 131 -1252 263 -100 233 -66 133 -132 165 -66 259 -98 3109 -10438 101 -5322 99 -100 65 -666 65 -166 331 -98 197 -132 233 -662 261 -1516 559 -66 263 -130 689 -132 229 -64 3613 -15976 231 -166 133 -66 399 -264 99 -132 295 -366 97 -1692 99 -398 529 -68 397 -130 899 -164 3559 -98 1197 -12106 199 -98 65 -166 99 -266 99 -134 231 -100 133 -132 297 -430 99 -1394 299 -64 397 -166 99 -100 465 -200 331 -132 599 -100 2333 -15214 65 -1230 231 -266 265 -432 165 -398 65 -532 333 -632 65 -232 957 -98 9785 -6320 97 -830 167 -166 133 -732 299 -958 327 -98 197 -66 229 -164 327 -98 653 -66 7993 -6418 65 -1284 97 -458 129 -196 197 -166 393 -134 99 -332 427 -132 131 -66 133 -98 233 -66 133 -364 163 -566 4873 -16030 97\nRAW_Data: -360 65 -364 65 -68 857 -98 65 -232 131 -264 63 -98 391 -396 65 -130 99 -98 65 -66 861 -166 265 -166 7611 -10336 65 -1822 165 -300 165 -166 295 -134 199 -100 67 -264 165 -166 99 -500 99 -198 97 -200 165 -268 197 -130 65 -300 629 -166 561 -132 333 -132 7459 -6294 131 -1096 165 -964 197 -332 65 -166 129 -132 99 -130 99 -100 97 -134 65 -164 131 -494 165 -396 97 -164 131 -198 99 -232 229 -66 821 -64 131 -14954 97 -788 65 -100 263 -66 99 -300 65 -400 131 -198 293 -294 163 -132 65 -692 99 -132 131 -200 1847 -132 8773 -5968 133 -330 65 -66 295 -430 197 -166 565 -132 467 -98 65 -430 165 -262 131 -528 131 -296 131 -100 131 -66 557 -166 787 -98 3221 -16236 299 -166 133 -562 199 -1692 99 -66 65 -364 65 -366 231 -168 367 -100 5541 -14968 297 -164 97 -132 163 -328 99 -532 99 -134 131 -370 397 -66 397 -98 293 -98 197 -98 1151 -66 7019 -6746 129 -296 163 -954 261 -230 229 -64 231 -264 431 -100 99 -466 165 -100 333 -166 133 -666 695 -200 67 -134 397 -100 1667 -7686 97 -426 195 -266 97 -330 63 -98 99 -594 97 -132 133 -270 131 -600 131 -362 833 -98 297 -166 199 -66 99 -200 65 -66 197 -100 2963 -98 1125 -2238 199 -554 275 -242 273 -212 271 -242 241 -242 271 -244 241 -244 273 -244 245 -246 247 -248 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -246 247 -248 249 -250 247 -504 513 -228 243 -244 273 -246 245 -500 513 -478 257 -242 241 -244 273 -244 499 -512 225 -244 525 -228 273 -496 475 -508 507 -240 253 -254 255 -494 261 -222 505 -258 245 -246 245 -246 245 -250 247 -506 513 -478 257 -242 241 -244 273 -244 499 -68858 319 -218 245 -230 251 -248 245 -248 247 -250 247 -250 249 -248 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -250 247 -506 257 -246 245 -246 247 -248 503 -516 477 -274 253 -474 511 -478 527 -484 511 -240 253 -472 281 -254 489 -484 513 -478 513 -480 255 -242 523 -472 507 -276 253 -462 275 -240 491 -258 247 -244 245 -246 247 -502 515 -238 253 -254 253 -492 497 -230 253 -250 247 -250 247 -250 247 -250 247 -506 257 -68880 279 -248 241 -266 223 -252 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -506 225 -280 213 -278 245 -248 503 -260 215 -276 245 -246 247 -248 247 -250 247 -504 481 -272 251\nRAW_Data: -254 255 -254 213 -506 483 -514 271 -254 217 -292 217 -248 497 -516 237 -254 473 -278 251 -488 489 -516 477 -272 253 -254 219 -494 261 -260 481 -260 215 -278 247 -248 247 -248 247 -504 481 -514 235 -290 217 -256 253 -244 497 -69132 311 -212 235 -258 249 -248 247 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -512 479 -272 253 -474 513 -478 527 -482 513 -238 253 -472 281 -254 487 -486 515 -478 509 -480 255 -242 529 -480 509 -240 255 -496 271 -234 487 -260 247 -244 245 -248 247 -504 511 -226 243 -244 275 -498 479 -272 253 -254 255 -254 249 -238 225 -252 247 -504 257 -68882 319 -188 265 -280 235 -228 251 -250 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -250 249 -504 515 -238 251 -256 253 -256 249 -472 515 -480 271 -256 253 -218 255 -248 497 -482 271 -252 473 -280 253 -490 487 -516 477 -274 253 -256 217 -496 261 -224 507 -258 249 -246 245 -248 247 -248 249 -504 511 -478 255 -244 243 -244 275 -246 499 -69134 311 -208 231 -254 249 -246 247 -250 247 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -248 249 -506 259 -246 245 -246 247 -246 503 -516 479 -274 253 -464 519 -496 487 -516 477 -274 255 -498 249 -248 469 -514 479 -516 483 -488 277 -254 491 -498 487 -260 247 -498 227 -244 529 -228 243 -274 245 -246 245 -502 515 -238 251 -256 253 -496 497 -226 251 -248 249 -248 249 -248 249 -250 249 -504 257 -68898 247 -274 213 -312 185 -312 185 -312 185 -312 185 -312 185 -312 185 -310 185 -310 185 -310 215 -278 217 -278 215 -278 217 -278 217 -280 215 -532 237 -288 217 -290 215 -276 461 -260 219 -280 217 -280 217 -278 215 -280 247 -506 481 -270 251 -256 217 -290 213 -502 483 -514 271 -254 253 -218 255 -246 499 -482 271 -254 473 -282 253 -488 485 -516 477 -274 253 -256 217 -496 263 -224 507 -260 247 -246 245 -246 247 -248 249 -504 513 -478 255 -242 243 -244 275 -246 499 -69122 319 -220 243 -228 251 -248 247 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -246 505 -514 479 -274 253 -476 511 -476 493 -516 481 -270 253 -474 279 -254 487 -484 513\nRAW_Data: -478 507 -506 225 -272 495 -482 505 -254 241 -494 255 -242 525 -226 241 -274 243 -242 243 -496 509 -274 253 -254 219 -494 491 -258 249 -248 247 -248 249 -248 249 -248 249 -504 257 -68896 281 -214 241 -260 247 -248 247 -248 247 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -248 247 -248 247 -248 249 -504 513 -238 251 -254 255 -254 251 -474 515 -478 273 -254 255 -218 253 -248 499 -514 237 -254 473 -280 253 -490 485 -516 479 -274 253 -256 217 -496 263 -224 507 -258 247 -248 245 -246 247 -250 247 -504 513 -478 257 -244 241 -244 275 -246 499 -69128 321 -218 241 -230 249 -248 247 -246 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 245 -250 503 -514 477 -272 255 -474 515 -480 491 -518 479 -274 253 -474 281 -254 483 -486 479 -512 507 -506 223 -242 527 -482 509 -240 253 -496 273 -236 487 -260 247 -246 245 -246 247 -504 513 -238 251 -256 253 -492 495 -228 253 -250 249 -250 249 -250 249 -248 249 -504 257 -68860 273 -242 233 -256 249 -250 247 -248 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 247 -248 247 -248 249 -504 513 -238 253 -254 253 -256 249 -474 515 -480 273 -254 255 -218 253 -248 497 -516 237 -252 473 -280 253 -490 487 -514 479 -274 253 -254 219 -496 263 -224 507 -258 247 -246 245 -246 247 -248 249 -504 513 -476 257 -242 243 -244 273 -246 499 -68728 129 -604 249 -242 243 -276 213 -276 245 -244 245 -246 245 -278 215 -278 215 -278 247 -248 247 -248 247 -248 249 -248 247 -250 247 -250 247 -250 247 -504 259 -246 245 -246 247 -248 503 -514 477 -272 255 -474 511 -478 527 -484 481 -272 253 -472 281 -254 485 -486 511 -478 511 -480 255 -242 527 -482 505 -254 241 -494 253 -242 495 -254 241 -244 273 -244 243 -494 509 -238 255 -254 255 -492 489 -260 247 -248 245 -248 247 -248 249 -248 249 -504 257 -129658 99 -98 131 -132 97 -100 97 -132 131 -98 131 -496 297 -266 163 -198 99 -398 165 -626 133 -198 531 -166 67 -66 431 -132 331 -100 65 -132 99 -100 2725 -9960 65 -3686 295 -1552 99 -362 195 -100 887 -98 263 -100 1495 -8372 67 -298 99 -100 131 -332 133 -198 233 -398 65 -1060 99 -164 327\nRAW_Data: -460 129 -262 97 -100 755 -132 6515 -12306 229 -296 625 -198 131 -200 131 -398 165 -1634 231 -68 331 -166 133 -132 5259 -8900 67 -6298 99 -264 199 -66 133 -100 429 -200 131 -132 195 -266 163 -100 229 -462 165 -334 199 -132 63 -264 163 -264 231 -66 363 -68 331 -66 563 -100 6577 -10216 67 -962 99 -794 131 -198 133 -132 299 -66 133 -66 331 -630 163 -266 163 -132 99 -500 97 -232 195 -394 231 -66 133 -100 329 -400 7509 -12254 65 -166 65 -328 361 -266 65 -68 131 -100 65 -926 65 -758 99 -592 459 -66 557 -98 327 -66 1317 -100 3491 -16028 263 -132 227 -96 297 -68 165 -1160 133 -1030 97 -796 199 -134 65 -132 361 -100 389 -198 3891 -16112 131 -98 229 -1120 163 -1648 131 -392 429 -166 231 -926 2655 -16140 395 -230 227 -296 65 -98 199 -200 99 -1062 99 -134 695 -66 231 -100 97 -198 265 -198 67 -100 2705 -16076 99 -66 263 -398 229 -264 131 -132 63 -100 129 -1780 65 -362 229 -98 295 -132 555 -266 4985 -11336 65 -924 99 -200 263 -330 265 -298 133 -100 163 -628 97 -528 163 -228 459 -132 65 -66 297 -100 261 -100 131 -198 295 -98 229 -66 3967 -10946 131 -830 131 -102 663 -66 361 -396 165 -66 131 -1394 99 -98 131 -166 327 -66 559 -98 295 -134 65 -98 97 -132 427 -328 2763 -15960 131 -432 299 -798 131 -1030 295 -732 197 -132 231 -100 99 -98 199 -134 233 -134 331 -200 7663 -7818 163 -532 99 -264 267 -266 229 -1188 97 -98 99 -1492 463 -132 233 -168 97 -132 825 -66 6395 -13604 131 -430 133 -166 133 -864 65 -1162 397 -198 365 -134 165 -100 365 -66 8413 -8042 163 -164 131 -398 65 -1722 229 -428 97 -130 65 -596 395 -100 429 -100 131 -200 331 -98 433 -68 2937 -7902 65 -332 131 -7288 333 -396 65 -366 99 -232 99 -232 333 -1126 131 -594 99 -398 65 -100 893 -296 163 -66 525 -98 3767 -15992 265 -694 199 -366 231 -1000 65 -1358 97 -134 463 -100 231 -134 3623 -15876 129 -328 129 -132 65 -596 199 -566 231 -530 99 -596 595 -166 593 -200 97 -68 3353 -16312 131 -134 231 -202 133 -134 97 -234 131 -66 165 -628 101 -596 1027 -134 331 -198 3715 -66 195 -10652 229 -164 591 -362 97 -560 359 -464 97 -68 197 -266 165 -332 7019 -9984 199 -1166 363 -132 97 -100 131 -198 133 -232 229 -1058 67 -200 529 -198 65 -98 897 -66 265 -134 6685 -9926 297 -530 589 -66 97 -230 131 -98 99 -724 163 -164 163 -196 229 -164 267 -132 167 -68 333 -66 199 -100 131 -66 497 -66 4929 -9050 97 -5010 65 -1394 99\nRAW_Data: -562 165 -98 493 -66 65 -494 63 -132 397 -502 263 -1020 363 -68 8779 -5986 133 -1590 99 -894 165 -66 167 -136 99 -732 265 -66 133 -166 131 -266 267 -564 197 -68 941 -100 3631 -66 4059 -6506 263 -958 165 -568 167 -334 397 -530 231 -166 367 -364 133 -100 131 -134 133 -598 99 -200 2261 -8366 65 -6804 97 -262 263 -132 65 -98 231 -430 97 -164 97 -428 65 -134 99 -668 131 -296 233 -200 499 -98 889 -98 6325 -502 211 -558 271 -240 265 -224 249 -248 247 -248 249 -248 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -258 247 -246 245 -246 247 -248 249 -504 257 -246 499 -480 507 -256 241 -244 273 -496 509 -224 243 -526 477 -510 507 -240 253 -498 245 -244 497 -260 245 -246 243 -502 255 -248 243 -246 501 -258 247 -244 245 -500 511 -476 257 -242 527 -476 505 -254 241 -494 507 -480 255 -68596 289 -214 247 -250 249 -252 249 -250 249 -250 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 477 -272 255 -254 217 -496 491 -516 481 -512 255 -242 497 -274 253 -254 219 -496 263 -224 251 -250 505 -514 477 -256 243 -244 243 -276 243 -500 225 -278 497 -478 509 -512 473 -256 241 -244 273 -496 255 -242 243 -244 495 -274 251 -474 279 -254 487 -486 257 -248 243 -68886 271 -238 265 -226 251 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -248 245 -248 503 -260 245 -246 243 -248 247 -248 249 -504 257 -248 499 -480 507 -256 241 -244 243 -530 475 -256 243 -494 509 -482 507 -274 255 -484 237 -244 499 -260 247 -246 245 -504 257 -246 245 -244 501 -258 245 -246 245 -500 513 -480 273 -256 473 -484 509 -262 225 -508 513 -478 255 -68906 287 -254 211 -272 225 -252 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -506 227 -278 245 -246 247 -246 505 -514 477 -272 253 -254 217 -496 491 -516 479 -510 257 -244 497 -272 253 -254 217 -498 261 -224 253 -250 505 -516 475 -256 243 -246 245 -244 245 -530 225 -246 529 -478 507 -510 483 -276 217 -292 217 -486 259 -248 247 -250 501 -258 215 -532 225 -244 529 -478 257 -242 243 -68922 239 -244 271 -226 251 -248 247 -250 247 -250 247 -250 247 -250 249 -248 249\nRAW_Data: -250 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 247 -506 257 -248 245 -246 245 -248 503 -258 245 -246 245 -248 247 -248 247 -506 257 -248 497 -480 509 -272 255 -254 255 -460 525 -226 249 -500 511 -478 507 -238 291 -476 245 -252 481 -260 247 -246 245 -502 255 -248 245 -246 501 -258 245 -246 245 -500 511 -476 255 -244 525 -478 505 -254 243 -492 507 -480 255 -68946 283 -250 241 -226 251 -248 247 -248 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -512 477 -274 253 -254 255 -460 493 -518 479 -510 255 -244 495 -274 253 -256 217 -496 261 -226 251 -250 503 -514 477 -274 253 -256 253 -218 249 -500 259 -246 499 -480 513 -482 505 -254 241 -244 243 -526 223 -272 243 -244 495 -272 255 -500 249 -246 473 -516 227 -244 243 -68908 317 -218 243 -258 247 -246 245 -248 247 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -248 249 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -504 257 -248 499 -480 507 -274 253 -254 255 -460 527 -224 249 -502 511 -476 505 -256 241 -494 255 -242 527 -240 253 -254 255 -458 261 -262 223 -252 505 -260 245 -246 245 -500 511 -476 257 -242 525 -474 509 -238 289 -464 519 -496 225 -68914 283 -252 243 -266 223 -252 249 -252 249 -248 249 -250 249 -250 249 -250 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -506 225 -278 245 -246 247 -248 503 -516 479 -274 215 -292 217 -494 491 -516 477 -510 255 -244 497 -272 217 -290 219 -494 261 -258 221 -252 505 -514 479 -272 251 -256 217 -254 247 -500 227 -278 499 -480 509 -514 477 -276 253 -256 217 -520 225 -252 247 -248 503 -260 215 -530 225 -246 495 -510 257 -244 245 -68932 283 -218 247 -240 257 -250 247 -248 247 -248 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -506 259 -246 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -504 259 -248 497 -482 509 -274 255 -218 253 -498 491 -258 247 -500 511 -478 507 -238 289 -466 283 -210 503 -258 247 -244 245 -500 257 -246 245 -244 501 -260 245 -244 245 -500 511 -476 257 -242 525 -474 509 -238 289 -464 519 -496 225 -68912 285 -250 243 -230 253 -250 247 -250 247 -250 247 -250 247 -250 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -248 249\nRAW_Data: -250 247 -504 227 -280 245 -246 247 -248 501 -514 477 -272 253 -254 219 -496 493 -514 481 -512 223 -276 495 -274 217 -290 217 -496 229 -256 253 -250 507 -516 477 -258 243 -244 245 -244 245 -530 225 -246 497 -512 511 -482 509 -240 253 -256 251 -482 257 -250 247 -248 505 -258 215 -532 235 -252 473 -518 243 -280 235 -68898 281 -248 239 -230 253 -250 247 -248 247 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 505 -258 247 -244 245 -248 247 -248 247 -504 257 -246 501 -480 513 -238 289 -220 253 -494 489 -260 247 -502 479 -508 509 -238 253 -482 277 -248 479 -260 247 -248 247 -502 257 -246 245 -246 499 -258 247 -244 245 -502 515 -476 257 -242 495 -512 475 -272 255 -496 481 -492 259 -68570 131 -550 315 -218 253 -256 245 -236 255 -248 247 -250 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 249 -504 257 -248 243 -248 245 -248 503 -512 477 -274 253 -254 255 -458 525 -482 515 -478 255 -242 495 -274 253 -256 217 -496 261 -226 253 -250 505 -514 475 -256 243 -244 273 -244 245 -498 257 -246 499 -478 507 -506 507 -238 255 -254 255 -492 227 -254 249 -248 503 -258 247 -498 257 -214 527 -480 271 -256 253 -138338 231 -728 97 -596 199 -556 163 -262 427 -330 359 -164 727 -66 663 -396 4171 -7546 65 -864 131 -362 529 -326 131 -2650 231 -298 197 -66 335 -264 529 -66 2745 -16072 201 -132 133 -664 265 -2520 433 -132 465 -134 459 -364 267 -66 1303 -8330 297 -368 133 -890 197 -1992 131 -462 67 -66 265 -266 561 -296 197 -198 195 -132 459 -66 3345 -16118 99 -164 297 -66 229 -332 97 -234 65 -166 65 -764 97 -700 67 -232 465 -134 563 -200 363 -100 1487 -96 5715 -8386 195 -298 163 -130 295 -100 97 -494 99 -754 327 -164 295 -132 195 -66 425 -66 3351 -64 1925 -6840 231 -64 97 -66 131 -462 99 -98 131 -494 197 -2274 397 -68 163 -166 565 -166 197 -100 3691 -64 3991 -8380 65 -164 197 -194 327 -100 367 -860 233 -596 629 -100 231 -132 1957 -100 1993 -66 3287 -6542 97 -1854 331 -132 131 -662 297 -494 99 -100 201 -1730 365 -166 559 -100 199 -66 331 -198 2209 -130 3295 -13088 99 -332 131 -790 263 -98 163 -1252 165 -196 263 -130 99 -132 361 -132 291 -262 99 -196 6531 -12274 133 -630 65 -302 165 -334 99 -1590 165 -828 291 -98 691 -198 561 -132 133 -68 7049 -9118 331 -662 165 -66 65 -132 65\nRAW_Data: -364 129 -98 265 -98 97 -66 589 -5626 67 -2546 231 -230 197 -232 197 -262 261 -464 97 -594 229 -296 165 -130 65 -166 99 -166 97 -66 229 -100 99 -66 131 -132 3567 -7898 95 -6038 99 -802 163 -68 97 -232 131 -326 65 -298 97 -132 231 -302 99 -298 65 -132 231 -1024 97 -100 331 -100 397 -232 99 -66 263 -68 231 -298 7371 -8180 165 -100 261 -68 165 -430 229 -198 67 -132 131 -300 65 -266 165 -498 165 -66 531 -232 263 -68 593 -134 131 -13852 65 -198 133 -634 97 -1128 97 -264 131 -66 165 -198 293 -494 99 -428 99 -962 99 -132 133 -264 833 -98 233 -14108 65 -1496 131 -230 67 -100 133 -666 131 -132 133 -232 65 -268 99 -400 99 -1854 597 -132 263 -98 493 -230 893 -98 3963 -15854 197 -130 165 -98 195 -330 261 -330 97 -132 97 -664 65 -500 5941 -132 1485 -11064 97 -992 163 -132 65 -434 197 -532 97 -134 297 -922 329 -658 163 -132 421 -264 429 -100 265 -298 229 -134 3353 -15986 131 -362 131 -132 459 -230 199 -66 393 -1060 99 -1096 131 -134 263 -66 163 -132 259 -130 97 -328 331 -66 99 -166 2561 -14808 65 -762 65 -66 197 -100 299 -398 133 -134 131 -66 65 -98 297 -10702 97 -464 131 -1526 133 -198 129 -100 131 -164 99 -132 163 -1098 129 -962 163 -300 65 -66 963 -166 3181 -8734 363 -2112 163 -1062 297 -98 427 -100 165 -100 231 -100 7535 -6418 65 -1522 65 -196 97 -64 131 -166 97 -332 65 -166 197 -100 265 -1564 65 -132 67 -696 133 -198 535 -98 297 -164 263 -8472 97 -560 327 -100 129 -64 65 -228 165 -162 163 -2146 561 -100 827 -232 463 -66 731 -66 4783 -10516 131 -300 131 -832 99 -100 331 -198 133 -232 99 -234 165 -266 165 -362 231 -66 297 -134 495 -98 165 -266 99 -132 859 -66 163 -98 165 -98 5587 -12622 131 -200 165 -1460 99 -432 265 -860 65 -262 591 -66 195 -100 425 -166 259 -66 227 -100 9819 -556 65 -3310 295 -562 199 -98 267 -200 131 -66 131 -168 99 -962 131 -534 99 -1848 197 -100 365 -132 1031 -66 2057 -66 2023 -7754 97 -7222 263 -698 165 -264 131 -366 97 -464 65 -466 65 -298 231 -430 97 -98 263 -64 953 -66 4093 -100 4171 -6616 65 -1220 99 -268 99 -98 99 -200 201 -134 97 -98 165 -164 295 -132 357 -396 165 -98 65 -230 163 -164 423 -130 651 -64 3641 -130 859 -7392 165 -100 165 -166 99 -66 167 -132 101 -132 67 -166 165 -100 333 -660 99 -162 97 -162 63 -424 131 -98 129 -392 131 -68 395 -132 389 -66 161 -17386 261 -298 99 -134 131\nRAW_Data: -100 65 -366 131 -166 197 -68 97 -166 165 -788 131 -198 97 -132 129 -164 163 -132 199 -66 267 -232 165 -68 165 -98 199 -134 1197 -568 273 -256 253 -256 249 -240 225 -252 247 -248 247 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -504 257 -248 499 -226 277 -496 255 -216 525 -480 257 -242 273 -242 497 -480 511 -274 253 -474 513 -478 263 -224 253 -250 247 -250 503 -260 245 -246 245 -500 257 -248 243 -248 499 -514 477 -272 255 -474 511 -478 263 -260 479 -514 477 -68856 257 -248 247 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 247 -250 249 -250 249 -250 247 -504 257 -248 245 -246 247 -248 503 -514 477 -274 253 -256 217 -494 523 -228 249 -502 513 -238 253 -254 253 -492 231 -264 227 -254 505 -258 247 -246 245 -248 245 -250 247 -504 257 -248 499 -478 509 -274 253 -500 249 -246 237 -228 509 -516 477 -516 483 -490 501 -518 479 -262 225 -512 257 -248 499 -69120 321 -218 245 -234 253 -250 247 -250 247 -250 247 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -504 257 -248 499 -228 243 -530 225 -244 529 -478 257 -242 243 -244 529 -476 509 -238 255 -512 479 -506 259 -226 251 -252 249 -250 505 -258 245 -246 245 -504 257 -246 245 -244 501 -516 479 -274 253 -476 479 -508 261 -226 511 -514 477 -69158 271 -240 263 -226 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 249 -504 257 -246 245 -246 247 -248 505 -514 475 -256 243 -244 243 -526 479 -272 255 -474 511 -240 243 -270 225 -508 257 -246 245 -246 501 -258 245 -244 245 -248 247 -248 247 -504 257 -248 499 -480 509 -274 253 -464 285 -246 239 -228 507 -482 509 -506 509 -482 507 -486 515 -240 241 -500 259 -248 499 -69138 283 -254 241 -230 251 -248 247 -248 249 -248 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -246 243 -248 247 -248 247 -504 257 -248 499 -228 245 -530 237 -252 503 -486 279 -246 237 -230 507 -516 479 -274 253 -464 517 -496 227 -254 249 -250 247 -250 505 -258 247 -246 245 -502 257 -246 245\nRAW_Data: -244 501 -512 477 -272 255 -474 511 -478 263 -228 511 -514 475 -69198 287 -218 249 -238 255 -250 249 -248 247 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 247 -250 247 -250 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 477 -272 255 -254 217 -498 493 -258 249 -504 479 -272 253 -254 255 -490 231 -262 225 -252 507 -258 247 -246 245 -248 245 -250 247 -504 257 -248 499 -480 513 -238 255 -498 247 -280 237 -228 507 -514 479 -516 483 -490 503 -506 505 -248 253 -492 229 -254 505 -69150 317 -284 135 -324 159 -348 157 -346 157 -346 157 -314 189 -312 187 -314 187 -312 187 -312 187 -312 185 -312 187 -280 217 -280 215 -312 185 -566 195 -278 215 -278 215 -278 469 -306 213 -290 219 -288 207 -268 223 -510 225 -280 467 -272 249 -506 247 -254 489 -490 257 -248 245 -248 501 -516 477 -256 243 -494 509 -508 237 -256 253 -256 249 -240 487 -260 245 -246 245 -500 257 -248 243 -246 501 -512 475 -274 253 -476 511 -478 263 -260 477 -514 479 -69176 271 -240 265 -224 251 -250 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -506 257 -248 245 -246 245 -248 503 -514 477 -256 243 -244 243 -530 477 -274 253 -476 479 -272 239 -266 225 -508 259 -248 245 -246 501 -258 215 -276 245 -246 247 -248 247 -504 257 -248 501 -480 511 -256 243 -496 255 -242 243 -244 497 -512 475 -516 509 -520 477 -510 495 -226 251 -504 257 -248 499 -69162 285 -218 249 -240 257 -250 247 -248 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -246 247 -248 249 -504 257 -248 499 -226 277 -498 225 -244 527 -478 257 -244 243 -244 527 -476 507 -258 241 -494 507 -510 237 -256 253 -254 251 -242 491 -226 277 -244 245 -500 255 -248 243 -246 501 -512 477 -256 243 -494 507 -506 253 -244 495 -480 505 -69176 261 -258 221 -252 251 -250 249 -250 249 -250 249 -250 249 -250 249 -248 249 -250 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -506 227 -280 245 -246 245 -248 503 -516 477 -256 243 -244 245 -496 509 -256 243 -496 509 -238 255 -254 255 -490 229 -254 251 -250 505 -258 215 -278 245 -246 247 -250 247 -504 227 -278 499 -480 511 -274 217 -514 243 -248 243 -266 483 -514 481 -512 475 -520 473 -506 507 -248 243 -500 249 -244 497 -69180 273 -256 217\nRAW_Data: -290 219 -246 235 -256 251 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 227 -278 245 -246 247 -246 505 -258 215 -278 245 -246 247 -248 247 -506 225 -278 499 -228 245 -530 225 -244 497 -508 257 -244 243 -246 497 -512 477 -272 255 -482 511 -500 229 -254 251 -250 249 -250 503 -260 215 -278 215 -534 225 -246 245 -276 499 -482 513 -238 253 -500 483 -528 227 -252 505 -514 479 -68732 173 -546 275 -240 233 -256 247 -250 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -514 477 -272 255 -254 217 -498 493 -260 249 -500 479 -272 253 -254 255 -494 233 -262 223 -252 505 -258 247 -246 245 -246 247 -250 247 -504 257 -248 499 -482 511 -238 255 -498 245 -280 237 -228 507 -516 479 -512 477 -518 475 -518 481 -270 237 -494 259 -250 501 -136574 65 -1160 65 -770 395 -198 199 -66 65 -330 231 -436 65 -1228 165 -664 727 -198 199 -166 229 -196 457 -64 493 -66 1987 -15790 99 -100 131 -234 231 -496 893 -234 297 -66 8071 -8244 99 -236 165 -134 199 -132 67 -198 165 -1094 65 -1782 195 -66 921 -66 229 -100 391 -132 7785 -7282 65 -402 65 -166 267 -266 165 -100 231 -166 461 -166 65 -696 65 -298 131 -166 99 -232 263 -100 397 -166 499 -66 165 -364 7435 -8394 331 -198 99 -198 99 -234 99 -1738 991 -198 333 -198 499 -132 891 -98 3239 -15978 165 -896 97 -100 131 -198 97 -532 65 -300 67 -1330 329 -66 859 -66 199 -232 397 -100 7707 -8162 363 -232 165 -98 231 -130 165 -294 131 -858 99 -932 65 -164 131 -264 565 -198 165 -132 233 -366 7109 -6100 65 -3046 165 -500 131 -232 67 -1028 99 -232 97 -300 65 -434 727 -166 363 -100 367 -64 263 -100 3147 -15114 65 -164 197 -296 97 -66 229 -264 497 -332 67 -98 99 -432 163 -132 65 -794 199 -166 297 -200 6509 -12574 199 -234 65 -132 99 -334 67 -264 131 -100 331 -168 65 -266 99 -166 397 -266 431 -98 565 -166 527 -100 3021 -68 3551 -12844 163 -294 165 -300 461 -856 331 -66 99 -1160 131 -132 293 -132 493 -166 261 -196 1149 -132 2177 -68 961 -15018 65 -992 99 -266 99 -200 229 -164 197 -956 231 -368 67 -100 99 -930 297 -360 623 -166 695 -134 1631 -66 957 -100 3451 -10430 65 -100 65 -1120 65 -198 65 -98 567 -200 199 -560 297 -792 65 -196 97 -690 197 -164 295 -66 295 -132 6963 -7554 131 -952 197 -228 131\nRAW_Data: -332 97 -264 295 -492 65 -1418 131 -396 363 -66 265 -134 731 -100 165 -68 133 -66 7537 -7378 65 -922 197 -164 229 -230 529 -66 165 -98 199 -132 65 -726 65 -164 97 -824 97 -230 427 -166 1363 -8016 97 -3030 65 -892 133 -164 99 -162 395 -230 163 -230 163 -230 197 -626 65 -166 97 -498 97 -700 495 -66 1215 -164 327 -10158 129 -1690 595 -198 163 -232 165 -164 365 -100 97 -732 99 -498 197 -364 365 -166 165 -134 797 -100 563 -100 3113 -9966 97 -426 165 -5434 393 -332 65 -98 99 -164 97 -232 65 -726 99 -266 265 -232 67 -298 461 -100 663 -68 9469 -8388 165 -396 231 -134 197 -166 67 -1556 97 -132 95 -230 65 -130 99 -98 657 -66 327 -68 527 -98 4249 -7974 97 -300 131 -264 397 -132 165 -562 99 -1060 65 -732 497 -134 531 -100 199 -66 203 -66 2987 -100 1657 -12228 97 -364 265 -200 165 -792 65 -396 129 -1312 623 -164 261 -98 165 -162 465 -66 3855 -8016 97 -6056 65 -1896 65 -366 99 -266 133 -132 99 -332 231 -864 133 -100 725 -98 261 -328 363 -98 293 -66 4765 -12278 99 -434 165 -64 65 -166 197 -498 65 -166 231 -500 99 -398 199 -398 695 -132 431 -100 131 -132 165 -66 199 -302 1463 -3364 241 -526 317 -212 235 -254 247 -248 245 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -258 247 -246 245 -246 247 -248 249 -248 249 -248 249 -506 513 -238 251 -256 253 -490 499 -488 513 -476 509 -274 255 -474 479 -272 237 -266 225 -508 511 -226 245 -244 275 -500 225 -246 275 -244 501 -228 245 -528 225 -246 529 -478 257 -244 243 -244 245 -276 499 -226 245 -276 245 -246 245 -68834 321 -218 243 -232 253 -248 245 -248 247 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -512 477 -272 255 -500 483 -500 489 -514 479 -274 253 -254 219 -254 247 -236 255 -250 247 -504 257 -248 497 -226 245 -530 477 -274 253 -254 255 -458 525 -484 511 -476 509 -482 507 -274 255 -254 217 -488 259 -250 247 -248 245 -250 503 -260 245 -500 479 -272 255 -69110 309 -218 253 -256 253 -246 231 -254 247 -248 247 -248 247 -248 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -506 257 -248 245 -246 245 -248 503 -260 245 -246 245 -248 245 -248 249 -248 249 -250 247 -506 513 -226 273 -244 243 -498 513 -476 509\nRAW_Data: -482 519 -240 255 -496 505 -228 253 -250 247 -504 513 -226 273 -244 243 -498 257 -246 245 -244 499 -258 245 -500 257 -214 527 -478 257 -242 273 -244 243 -246 501 -258 245 -246 245 -246 247 -69160 247 -274 213 -274 245 -242 245 -276 213 -276 245 -244 245 -246 245 -278 213 -278 245 -248 249 -248 247 -250 249 -248 249 -250 247 -248 249 -504 257 -246 245 -246 247 -248 503 -512 477 -274 251 -476 509 -512 461 -518 479 -258 243 -244 245 -276 245 -246 247 -248 247 -504 257 -248 501 -226 245 -530 477 -256 243 -244 245 -532 479 -480 509 -520 485 -488 509 -262 221 -252 249 -506 257 -248 245 -246 247 -248 503 -258 247 -498 477 -258 243 -69136 303 -236 227 -256 247 -250 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -506 257 -248 243 -246 247 -248 505 -258 247 -244 245 -246 247 -250 247 -248 249 -250 247 -506 513 -226 243 -274 245 -498 511 -476 507 -482 507 -274 255 -484 475 -264 261 -222 251 -506 513 -238 251 -256 253 -496 233 -264 225 -252 505 -260 247 -498 225 -246 529 -478 257 -244 243 -244 275 -244 499 -258 245 -244 245 -246 247 -69174 321 -218 253 -242 231 -252 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 247 -504 259 -246 245 -248 245 -248 505 -514 479 -274 253 -476 511 -478 493 -516 481 -272 255 -254 253 -220 247 -240 257 -250 247 -502 257 -248 499 -228 245 -528 479 -272 255 -254 253 -460 525 -482 515 -476 509 -482 505 -254 241 -242 243 -526 225 -272 243 -242 243 -244 529 -238 253 -472 511 -276 247 -69140 309 -212 241 -244 273 -242 243 -242 243 -274 243 -244 241 -244 245 -276 245 -244 247 -248 247 -248 249 -250 247 -250 249 -250 247 -250 247 -506 257 -248 245 -246 245 -248 503 -258 245 -246 247 -246 247 -248 249 -248 249 -250 249 -504 513 -238 251 -254 255 -494 501 -484 513 -478 509 -238 253 -514 481 -270 235 -264 223 -506 513 -238 253 -254 255 -490 231 -266 225 -254 507 -258 247 -500 225 -246 529 -480 271 -256 253 -256 217 -248 501 -258 247 -244 245 -246 247 -69188 287 -218 247 -234 255 -250 247 -248 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -248 245 -250 503 -514 479 -274 253 -498 483 -500 487 -514 477 -258 241 -244 243 -246 275 -244 245 -248 245 -504 257 -248 499 -228 245\nRAW_Data: -528 477 -256 243 -244 245 -530 475 -508 509 -482 509 -492 505 -246 253 -254 243 -494 227 -278 245 -248 245 -248 503 -258 215 -530 479 -272 251 -69146 297 -230 225 -252 249 -250 249 -250 249 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -506 259 -246 245 -246 247 -248 501 -258 247 -244 245 -248 247 -248 249 -248 249 -250 247 -506 513 -238 251 -254 255 -494 501 -484 513 -478 509 -238 253 -514 479 -270 237 -264 223 -508 513 -238 253 -254 255 -494 233 -262 225 -252 505 -258 247 -500 225 -246 529 -478 257 -242 243 -244 275 -246 499 -258 215 -276 245 -246 245 -69160 287 -254 251 -238 227 -252 249 -248 247 -250 247 -250 247 -250 249 -250 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -506 227 -280 215 -276 245 -248 505 -480 511 -272 253 -476 479 -506 493 -514 479 -260 243 -246 245 -276 245 -246 247 -248 247 -504 227 -278 499 -238 249 -504 473 -280 241 -244 243 -500 505 -520 485 -500 487 -518 479 -274 251 -256 217 -496 263 -224 251 -252 249 -250 503 -258 247 -500 479 -258 243 -131656 133 -100 133 -200 165 -100 97 -232 627 -1028 133 -992 163 -132 635 -264 197 -100 4009 -10800 163 -230 231 -168 65 -132 201 -400 395 -98 99 -296 393 -100 295 -66 1425 -6868 99 -1394 129 -164 65 -392 329 -1518 229 -198 163 -100 665 -66 199 -166 335 -166 165 -98 3141 -8982 65 -6896 163 -262 391 -230 195 -428 163 -526 131 -98 165 -196 65 -98 65 -724 131 -200 1585 -164 6977 -13106 131 -164 65 -692 97 -894 65 -998 297 -66 165 -166 401 -132 233 -66 561 -266 1987 -64 555 -13660 99 -430 131 -232 131 -398 99 -630 99 -100 267 -464 97 -924 99 -66 265 -526 561 -132 165 -68 199 -134 1951 -66 1643 -15920 231 -528 163 -162 65 -458 229 -98 65 -622 97 -68 97 -132 163 -134 631 -398 131 -198 883 -66 759 -64 1083 -16320 699 -464 163 -328 131 -232 231 -230 97 -690 263 -294 557 -100 729 -230 427 -98 65 -130 7841 -8604 131 -262 163 -200 199 -398 99 -98 263 -490 97 -132 97 -130 129 -626 229 -396 557 -66 229 -164 393 -262 395 -98 2347 -16232 297 -300 99 -68 297 -3186 461 -330 229 -68 131 -130 1389 -66 3247 -10450 131 -1522 229 -496 131 -100 195 -132 129 -66 327 -64 99 -196 133 -1794 67 -132 329 -100 197 -498 165 -132 165 -166 263 -132 7439 -7152 99 -502 131 -134 65 -200 199 -164 133 -100 401 -266 367 -1260 197 -366 297 -428 399 -198 897\nRAW_Data: -66 3765 -13986 497 -562 97 -732 297 -266 99 -232 429 -132 65 -298 197 -268 97 -1192 1259 -68 65 -132 97 -100 8047 -6842 97 -362 165 -130 131 -66 459 -164 197 -164 263 -164 359 -462 195 -262 65 -1122 229 -66 755 -100 465 -132 6211 -9870 99 -66 129 -100 163 -98 131 -264 131 -162 97 -198 97 -196 197 -262 229 -958 165 -266 65 -98 697 -200 5723 -12050 299 -66 231 -232 99 -528 99 -66 233 -134 65 -534 131 -1028 97 -300 63 -266 97 -100 133 -134 891 -100 4201 -100 3703 -8722 229 -360 263 -164 459 -66 819 -132 163 -1126 267 -66 231 -200 731 -232 229 -8678 131 -132 229 -198 295 -592 165 -820 65 -756 99 -66 65 -68 99 -200 199 -332 297 -100 565 -7800 65 -500 65 -7518 465 -368 99 -230 65 -832 99 -2356 627 -198 131 -100 361 -130 295 -132 3819 -96 5271 -6210 65 -266 393 -98 131 -64 329 -362 365 -66 133 -132 67 -632 65 -430 265 -166 65 -566 131 -132 461 -66 1429 -134 4601 -10498 65 -402 65 -430 229 -328 359 -98 263 -624 165 -168 199 -166 97 -494 197 -196 99 -98 261 -132 297 -166 99 -66 4183 -66 2927 -6458 99 -1894 427 -498 331 -562 131 -1458 229 -690 1785 -68 231 -100 731 -12140 165 -168 67 -134 131 -100 233 -198 99 -662 265 -730 299 -166 227 -328 97 -164 821 -66 2903 -66 1043 -7904 65 -3146 65 -4854 99 -98 133 -132 133 -760 99 -692 259 -1554 65 -98 495 -230 261 -64 491 -166 163 -98 227 -16058 97 -100 163 -294 197 -164 195 -100 129 -362 163 -626 65 -166 129 -526 65 -132 829 -100 597 -68 197 -366 1025 -66 497 -98 1387 -66 329 -12300 99 -232 99 -232 65 -532 99 -100 165 -402 131 -1326 395 -98 565 -232 131 -168 65 -166 1621 -66 2509 -13018 165 -362 99 -1982 99 -66 65 -764 593 -430 229 -132 329 -164 263 -100 2629 -15978 297 -68 131 -662 65 -98 99 -526 133 -132 133 -498 97 -560 229 -66 855 -98 593 -98 7597 -8606 427 -164 131 -822 65 -98 67 -296 97 -496 131 -198 97 -164 97 -394 1449 -230 397 -66 2897 -8398 165 -236 97 -1752 65 -330 65 -462 231 -134 131 -2680 1025 -132 633 -130 7803 -6500 65 -2808 67 -332 197 -164 97 -66 163 -2234 195 -132 425 -66 755 -98 2687 -66 5177 -6880 65 -1510 97 -232 195 -232 131 -234 197 -200 65 -166 65 -198 165 -134 67 -1262 395 -100 297 -98 165 -402 331 -134 5357 -12090 197 -232 99 -332 97 -2278 265 -66 563 -134 297 -66 3607 -10024 65 -4678 363 -166 201 -200 165 -100 163 -392 131 -98 129 -624 195 -332 65 -962 295\nRAW_Data: -132 165 -132 3857 -11128 241 -562 311 -208 235 -256 249 -250 247 -248 249 -248 247 -250 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 247 -504 259 -246 245 -246 247 -248 503 -258 247 -246 245 -248 245 -250 247 -250 247 -250 247 -506 513 -238 251 -256 253 -256 247 -474 259 -248 501 -480 509 -274 217 -514 477 -272 237 -266 223 -508 481 -512 509 -482 509 -490 505 -520 247 -246 241 -230 255 -250 249 -250 247 -250 505 -514 479 -512 475 -274 255 -68824 287 -218 273 -250 237 -230 251 -248 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -514 477 -256 243 -494 509 -274 253 -476 245 -252 517 -226 249 -502 511 -224 243 -528 475 -508 505 -474 255 -242 273 -242 495 -274 253 -500 485 -498 227 -254 247 -250 503 -260 245 -498 513 -478 511 -472 507 -274 255 -254 255 -212 271 -480 511 -226 241 -69142 299 -230 225 -254 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -246 245 -246 247 -248 501 -258 247 -246 245 -248 247 -248 247 -250 247 -250 249 -504 513 -226 243 -276 243 -246 245 -502 257 -246 499 -478 509 -274 253 -500 485 -232 263 -226 251 -506 513 -476 509 -480 505 -508 489 -504 249 -242 241 -274 243 -242 241 -274 243 -242 495 -512 507 -482 509 -240 253 -69136 287 -254 249 -240 227 -252 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -516 477 -256 243 -496 509 -238 253 -514 243 -248 479 -260 249 -504 479 -258 243 -498 509 -514 475 -522 241 -254 255 -250 473 -260 249 -502 477 -510 255 -244 243 -246 497 -272 251 -474 509 -512 497 -486 483 -270 253 -254 255 -254 213 -506 481 -272 251 -69172 263 -226 285 -218 249 -250 249 -250 249 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -506 257 -246 245 -246 247 -248 503 -258 247 -244 245 -248 247 -248 247 -250 249 -250 247 -504 513 -228 243 -244 273 -244 245 -502 259 -246 499 -480 511 -274 255 -474 513 -240 239 -232 257 -506 513 -476 509 -478 505 -516 503 -472 279 -254 253 -252 239 -228 249 -248 247 -248 503 -514 477 -510 509 -238 253 -69168 273 -254 253 -220 253 -248 237 -256 249 -248 247 -250 247\nRAW_Data: -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -246 247 -248 501 -514 479 -272 255 -500 483 -234 263 -486 257 -248 501 -226 247 -528 479 -274 253 -474 511 -510 497 -482 259 -246 245 -244 501 -258 247 -498 477 -510 257 -242 243 -244 495 -272 251 -474 509 -512 497 -486 511 -226 245 -276 243 -246 245 -502 513 -240 251 -69164 295 -228 223 -252 249 -250 251 -250 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -506 257 -248 243 -246 247 -248 503 -258 245 -246 245 -248 247 -248 247 -250 249 -248 249 -504 513 -226 243 -246 275 -246 243 -502 257 -246 499 -478 509 -274 255 -474 479 -272 241 -266 223 -508 515 -478 509 -482 487 -520 481 -508 263 -228 253 -252 249 -250 249 -250 247 -250 503 -514 475 -508 507 -226 241 -69190 287 -218 249 -240 257 -250 247 -248 249 -248 247 -250 249 -250 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -514 477 -274 253 -474 511 -240 241 -500 259 -248 499 -258 245 -498 477 -274 253 -474 511 -512 495 -484 257 -246 245 -246 501 -258 245 -500 481 -512 253 -244 241 -244 495 -272 255 -474 517 -480 493 -516 511 -240 251 -256 253 -254 249 -470 515 -240 251 -69160 255 -244 243 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -506 257 -248 245 -246 247 -248 503 -258 215 -278 245 -246 247 -248 249 -248 249 -248 249 -506 481 -270 253 -254 253 -256 213 -506 257 -250 501 -482 513 -238 255 -514 477 -270 233 -262 223 -508 479 -514 509 -508 475 -520 475 -518 247 -248 243 -268 223 -250 249 -250 247 -250 505 -516 479 -514 475 -256 241 -69148 295 -226 223 -252 249 -250 249 -250 249 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -514 475 -256 243 -494 513 -238 289 -464 283 -210 505 -258 247 -498 513 -238 255 -474 509 -514 497 -484 257 -248 243 -246 499 -258 247 -498 509 -474 257 -242 243 -244 527 -238 251 -502 503 -490 515 -496 481 -258 245 -246 245 -246 247 -504 513 -238 253 -124420 65 -360 97 -98 129 -328 97 -426 99 -296 65 -232 231 -168 497 -232 163 -592 197 -392 131 -728 1593 -134 365 -66 97 -100 3173 -66 2309 -11768 163 -724 457 -100 197\nRAW_Data: -200 461 -66 165 -496 131 -134 99 -98 133 -98 97 -494 165 -230 97 -166 963 -266 3041 -15870 131 -98 231 -166 133 -68 199 -100 133 -132 633 -100 265 -198 65 -334 365 -198 131 -466 1129 -166 299 -234 3643 -16164 99 -364 231 -68 233 -598 133 -232 197 -100 133 -66 229 -66 297 -394 1219 -100 163 -166 3307 -7734 65 -4936 95 -1288 333 -232 233 -200 65 -364 333 -300 131 -100 165 -634 65 -564 131 -200 761 -232 563 -13840 133 -200 67 -464 65 -1318 231 -302 263 -100 199 -132 99 -166 99 -166 197 -328 163 -66 691 -262 497 -66 5665 -9958 99 -364 65 -1028 165 -398 265 -196 65 -132 561 -230 165 -66 461 -266 331 -364 131 -398 165 -166 595 -100 199 -102 729 -132 5765 -10908 65 -1388 129 -658 97 -98 295 -132 99 -132 131 -1152 265 -66 199 -98 731 -98 197 -66 751 -98 2631 -100 1691 -11754 263 -198 131 -132 133 -168 199 -132 99 -66 97 -1580 231 -100 4231 -14088 65 -1810 97 -690 65 -98 99 -164 397 -232 299 -2124 565 -64 267 -15924 99 -266 67 -132 565 -66 331 -266 165 -664 231 -1358 663 -264 297 -134 5749 -12972 197 -854 97 -1782 131 -100 63 -428 461 -66 891 -98 329 -64 1185 -15082 99 -468 97 -68 231 -396 165 -232 65 -134 65 -264 201 -430 231 -98 1097 -68 233 -132 5943 -12230 261 -756 99 -596 97 -132 97 -362 229 -198 163 -194 65 -64 395 -98 199 -100 133 -66 299 -11664 129 -658 97 -66 131 -98 263 -394 163 -132 129 -100 229 -1714 231 -166 99 -66 231 -132 599 -100 1127 -68 431 -100 1119 -14044 65 -1032 131 -826 99 -596 499 -266 199 -862 133 -1292 395 -100 921 -198 331 -132 363 -15244 97 -624 231 -134 163 -500 131 -530 201 -134 133 -132 67 -464 131 -998 965 -66 365 -100 3769 -9910 65 -6926 265 -264 133 -166 131 -166 233 -130 99 -132 65 -396 195 -196 65 -98 97 -98 557 -98 165 -66 397 -66 7539 -8488 199 -332 65 -200 199 -1458 65 -466 99 -66 65 -698 297 -68 361 -132 531 -134 165 -68 6531 -11410 65 -896 99 -264 67 -132 197 -98 165 -532 99 -100 165 -562 99 -100 297 -728 983 -98 131 -98 229 -230 63 -262 4137 -16250 427 -298 329 -430 99 -360 131 -724 131 -264 97 -796 825 -166 265 -134 3663 -14070 97 -1750 197 -68 163 -132 229 -530 165 -428 65 -132 65 -98 65 -1684 229 -100 229 -66 97 -330 131 -134 231 -100 3781 -8504 65 -7400 67 -164 131 -66 495 -164 133 -166 131 -202 65 -102 165 -568 65 -134 65 -498 229 -132 129 -394 197 -98 295 -66 427 -66 397 -68 2533\nRAW_Data: -16126 133 -268 131 -200 63 -196 229 -394 295 -758 131 -358 65 -164 263 -594 427 -66 131 -66 393 -5360 199 -132 165 -526 65 -230 165 -262 65 -66 97 -296 131 -556 65 -792 131 -402 331 -132 97 -168 429 -100 365 -66 401 -4724 65 -1924 99 -300 65 -1066 65 -266 65 -332 165 -366 65 -332 131 -166 233 -166 499 -264 65 -300 67 -132 99 -132 65 -432 397 -164 657 -64 263 -66 555 -66 2729 -10288 65 -5204 97 -100 591 -166 99 -496 97 -100 65 -98 99 -296 97 -428 265 -100 229 -164 263 -100 765 -100 263 -66 3453 -7586 133 -7046 99 -962 131 -98 131 -134 565 -66 131 -134 593 -66 131 -1384 97 -330 131 -402 5065 -15884 65 -664 297 -266 167 -134 101 -134 99 -596 165 -496 99 -564 2351 -66 6223 -11750 65 -330 365 -68 99 -132 99 -634 131 -860 163 -130 65 -1226 197 -98 861 -134 495 -266 2219 -11598 65 -1316 99 -758 229 -2510 131 -100 167 -68 303 -100 499 -164 131 -100 397 -7704 65 -7902 299 -100 131 -100 199 -166 531 -66 133 -298 199 -398 131 -100 65 -166 133 -66 199 -100 635 -132 599 -66 199 -134 361 -66 3253 -226 425 -15180 133 -568 297 -132 199 -132 793 -330 65 -526 97 -132 425 -526 331 -68 303 -548 297 -230 225 -254 249 -250 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -506 257 -248 245 -246 245 -248 503 -260 245 -246 243 -248 247 -502 515 -238 253 -472 277 -242 499 -508 481 -504 489 -516 477 -274 253 -256 253 -460 495 -260 249 -248 247 -250 247 -504 257 -248 245 -246 501 -512 475 -508 255 -242 243 -242 245 -274 499 -478 257 -244 243 -244 529 -476 509 -274 253 -218 255 -68836 285 -252 243 -228 249 -248 245 -248 247 -250 247 -250 249 -248 249 -248 249 -250 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 249 -504 259 -248 243 -246 247 -248 503 -514 475 -256 243 -494 509 -274 253 -218 255 -254 247 -496 257 -246 497 -226 275 -496 511 -474 507 -480 255 -242 273 -242 495 -274 253 -464 523 -498 225 -250 503 -258 245 -244 245 -502 511 -476 509 -510 473 -508 521 -488 249 -252 481 -516 225 -256 289 -68864 259 -224 249 -252 251 -250 251 -250 249 -248 249 -250 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 249 -248 249 -250 247 -504 257 -248 245 -246 247 -248 503 -258 245 -246 245 -248 247 -504 513 -240 251 -474 247 -272 499 -508 483 -506 489 -516 477 -274 253 -256 217 -496 491 -260 247\nRAW_Data: -248 249 -248 247 -504 257 -248 245 -246 501 -512 477 -512 253 -244 241 -244 243 -246 529 -478 257 -242 243 -244 529 -478 509 -238 253 -254 255 -69126 287 -254 251 -240 227 -252 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 249 -504 257 -248 245 -246 245 -248 503 -516 479 -272 255 -476 511 -238 241 -268 223 -252 249 -504 257 -248 499 -228 243 -530 477 -512 505 -474 255 -242 243 -274 495 -274 255 -474 485 -506 261 -222 507 -260 247 -246 245 -502 511 -478 509 -480 507 -488 499 -510 277 -246 471 -516 225 -256 253 -68936 271 -238 231 -256 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -260 245 -246 245 -246 247 -504 513 -238 251 -504 249 -254 489 -488 513 -476 509 -512 475 -274 255 -254 253 -488 483 -258 215 -276 245 -246 245 -504 255 -248 245 -246 503 -514 477 -512 225 -242 273 -242 243 -244 529 -476 255 -244 243 -244 529 -478 509 -238 253 -254 255 -69140 307 -220 253 -254 253 -246 231 -256 247 -248 247 -250 247 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -506 257 -248 245 -246 245 -248 503 -514 477 -272 255 -474 515 -242 239 -268 223 -252 247 -504 259 -246 501 -226 245 -530 479 -512 509 -482 253 -242 241 -244 529 -238 253 -498 481 -528 225 -250 503 -260 245 -244 245 -502 511 -478 509 -482 505 -506 505 -486 249 -252 487 -516 227 -256 251 -68924 311 -210 233 -258 247 -250 247 -250 247 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -250 247 -250 249 -248 249 -250 249 -506 257 -248 245 -244 247 -246 505 -258 247 -244 245 -246 247 -504 515 -238 253 -472 281 -254 487 -486 513 -478 509 -512 475 -274 253 -256 253 -488 483 -228 255 -290 217 -254 255 -494 229 -254 249 -250 505 -514 477 -512 255 -242 243 -242 243 -244 529 -478 257 -242 243 -244 529 -480 513 -240 253 -254 255 -69136 321 -218 253 -246 229 -254 247 -246 247 -248 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -514 479 -272 255 -474 511 -240 241 -270 225 -252 247 -504 259 -246 499 -228 273 -498 511 -476 509 -480 253 -242 273 -242 491 -256 241 -494 507 -504 255 -242 491 -256 243 -242 243 -526 475 -506 505 -506 473 -504 505\nRAW_Data: -504 251 -242 495 -490 249 -242 271 -68916 273 -238 229 -256 249 -248 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -250 247 -506 257 -248 245 -246 245 -248 503 -258 245 -246 247 -246 247 -504 481 -258 243 -528 225 -244 495 -512 509 -482 507 -504 475 -276 255 -254 249 -474 515 -238 251 -254 255 -254 249 -474 257 -250 245 -248 501 -514 479 -512 225 -242 273 -244 243 -246 495 -508 255 -244 243 -246 497 -512 483 -272 255 -254 253 -69136 283 -254 241 -230 251 -248 245 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -250 247 -504 259 -246 245 -246 247 -248 503 -514 477 -274 253 -500 483 -232 265 -228 253 -250 249 -506 259 -248 499 -226 245 -528 477 -508 509 -484 253 -244 241 -244 497 -272 255 -496 479 -528 225 -252 505 -260 215 -276 245 -500 479 -510 509 -482 507 -510 491 -508 245 -246 479 -518 227 -292 215 -68942 321 -218 233 -274 235 -260 223 -250 249 -250 249 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -506 225 -280 213 -278 245 -248 503 -260 215 -276 215 -278 245 -504 479 -258 245 -496 257 -246 497 -514 475 -508 507 -508 461 -280 251 -252 241 -496 481 -272 251 -256 253 -254 249 -470 259 -248 245 -248 499 -512 475 -510 237 -256 253 -256 251 -244 493 -514 223 -244 245 -276 497 -480 509 -256 243 -244 243 -68750 131 -586 287 -220 253 -246 233 -254 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -504 259 -248 243 -248 245 -248 501 -514 477 -274 253 -474 513 -240 241 -268 227 -250 249 -504 257 -248 499 -226 245 -530 477 -510 507 -482 255 -242 241 -244 523 -256 241 -494 505 -474 257 -242 525 -224 273 -242 243 -494 507 -508 481 -506 505 -504 471 -506 249 -242 527 -472 251 -242 271 -119988 65 -164 163 -1380 229 -262 65 -1322 65 -564 99 -860 65 -566 501 -66 499 -164 395 -132 199 -100 895 -11996 131 -166 295 -132 165 -234 65 -164 231 -102 199 -134 97 -498 199 -164 265 -66 197 -132 65 -100 99 -664 1297 -98 8085 -7700 99 -634 365 -134 363 -954 99 -2236 261 -66 789 -98 425 -100 459 -12258 99 -198 267 -100 99 -102 97 -200 133 -300 67 -166 163 -464 133 -66 99 -828 927 -132 331 -166 9065 -7016 67 -98 67 -1290 131 -396 165 -134 131 -1026 97 -1118 65 -1048 919 -134 699\nRAW_Data: -100 3443 -13736 133 -1852 65 -232 363 -202 431 -100 133 -98 265 -166 197 -66 299 -526 131 -100 163 -832 727 -168 131 -200 65 -132 197 -102 331 -16028 99 -132 99 -198 99 -68 133 -364 163 -432 197 -464 131 -100 133 -298 97 -466 165 -200 133 -396 331 -66 823 -66 7065 -12144 97 -428 359 -164 65 -100 593 -198 163 -164 131 -132 99 -98 131 -1448 1193 -100 365 -132 10335 -1482 65 -364 63 -1844 97 -134 231 -630 131 -200 265 -232 65 -166 65 -368 197 -1126 297 -132 131 -66 1259 -134 129 -66 99 -66 8323 -6196 263 -298 97 -164 97 -492 231 -794 131 -166 67 -428 65 -954 755 -98 261 -66 261 -230 131 -230 1425 -11490 167 -562 197 -364 63 -428 65 -228 463 -166 65 -19728 165 -134 363 -100 131 -168 197 -198 101 -826 99 -1678 99 -66 561 -98 161 -66 229 -230 163 -398 265 -166 699 -9874 67 -2192 165 -200 67 -1096 131 -2282 227 -332 231 -98 165 -68 165 -364 3503 -7996 65 -430 99 -662 231 -266 165 -66 133 -134 199 -164 99 -168 65 -398 65 -730 231 -100 297 -198 67 -132 863 -134 231 -102 267 -68 871 -68 765 -66 823 -9234 99 -4694 231 -134 197 -166 99 -368 363 -100 233 -694 231 -400 65 -464 131 -234 99 -230 65 -400 297 -66 8683 -7160 233 -1030 129 -298 263 -100 299 -264 165 -100 229 -792 65 -164 131 -294 65 -264 459 -426 525 -132 229 -298 5509 -9856 131 -100 263 -232 163 -266 531 -232 99 -398 65 -332 165 -534 131 -68 293 -596 887 -232 4775 -16286 197 -364 65 -132 99 -98 163 -1350 131 -100 167 -398 99 -432 533 -100 929 -66 65 -228 3861 -3258 99 -434 65 -232 99 -332 133 -860 265 -66 233 -166 131 -100 197 -100 99 -66 99 -530 65 -564 65 -132 165 -232 265 -200 199 -100 165 -100 67 -98 531 -132 427 -10628 165 -232 199 -132 131 -366 199 -464 65 -2290 399 -198 333 -168 597 -166 533 -66 2453 -66 4151 -7656 99 -1062 131 -232 133 -98 131 -68 163 -198 133 -334 67 -596 923 -132 161 -98 327 -362 4939 -12358 195 -230 131 -528 65 -1744 65 -360 97 -396 63 -166 1047 -198 65 -196 231 -130 97 -12510 131 -198 99 -668 97 -166 199 -1224 99 -1128 65 -98 429 -132 365 -198 765 -132 1227 -9838 65 -5956 295 -266 297 -430 229 -1286 65 -392 197 -988 163 -132 625 -100 165 -66 525 -66 4229 -16222 129 -132 295 -66 163 -132 97 -66 97 -692 97 -462 65 -1026 265 -132 827 -166 163 -166 167 -234 6731 -12228 233 -166 99 -200 131 -632 133 -332 65 -498 263 -100 229 -262 463 -100 199 -100 733\nRAW_Data: -100 3989 -11100 131 -894 131 -464 65 -332 99 -132 133 -132 101 -236 99 -1328 165 -100 133 -198 1027 -360 3341 -9980 133 -1858 65 -166 133 -166 67 -134 395 -166 99 -930 97 -100 199 -234 99 -428 99 -164 1051 -66 657 -100 9339 -3804 197 -624 65 -2224 131 -900 529 -264 197 -296 231 -98 727 -132 199 -100 265 -66 99 -68 6671 -12588 165 -132 165 -132 233 -928 165 -934 99 -200 1557 -100 331 -166 7333 -8240 295 -164 263 -98 97 -132 97 -198 261 -230 197 -298 229 -692 197 -662 661 -200 231 -66 197 -15936 199 -960 65 -234 165 -466 231 -2022 431 -98 801 -232 463 -66 229 -164 3261 -8456 67 -5980 65 -1990 133 -364 97 -400 65 -132 99 -134 99 -132 99 -1292 429 -166 265 -68 3517 -8746 65 -862 165 -332 267 -198 67 -200 65 -166 299 -100 165 -2022 465 -430 165 -134 301 -136 297 -66 3287 -100 951 -132 3825 -8094 197 -132 129 -364 129 -66 163 -164 295 -428 131 -392 99 -394 459 -396 463 -164 4081 -10550 99 -266 67 -2688 203 -582 261 -260 223 -252 249 -250 249 -250 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -504 511 -226 243 -528 225 -244 529 -238 253 -472 517 -244 279 -238 227 -252 247 -250 247 -504 513 -226 243 -528 477 -274 253 -256 217 -254 247 -500 259 -246 499 -226 245 -274 245 -246 245 -502 513 -476 255 -244 495 -274 253 -254 255 -218 247 -502 513 -476 257 -68588 311 -208 233 -256 249 -248 249 -248 247 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -514 477 -272 255 -254 217 -498 263 -226 509 -514 475 -274 253 -256 253 -460 525 -482 513 -478 507 -506 223 -244 271 -244 495 -274 253 -476 243 -252 247 -238 259 -248 503 -260 245 -246 245 -246 247 -248 249 -504 257 -248 245 -246 501 -514 479 -274 253 -218 255 -254 247 -234 255 -69132 319 -220 243 -232 251 -248 247 -248 247 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -258 247 -246 245 -246 247 -504 515 -238 251 -474 279 -254 489 -228 253 -506 513 -238 251 -256 253 -254 251 -240 227 -510 479 -258 243 -528 475 -274 253 -256 217 -254 247 -500 257 -248 499 -226 245 -276 243 -246 245 -502 513 -478 257 -242 495 -274 253 -256 217 -254 247 -500 513\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/smc5326.sub",
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: SMC5326\nBit: 25\nKey: 00 00 00 00 01 7D 55 80\nTE: 210\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/smc5326_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940\nRAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/somfy_keytis_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1753 -32700 2581 -2598 2513 -2604 2535 -2568 2529 -2602 2519 -2608 2523 -2574 2529 -2598 2517 -2604 2537 -2596 2521 -2574 2523 -2600 2515 -2530 4829 -1304 1305 -1308 1309 -678 669 -1280 1305 -680 639 -678 653 -1308 1273 -1334 645 -686 645 -684 611 -716 645 -684 1273 -1340 1269 -1338 641 -684 643 -684 643 -684 609 -714 1273 -682 643 -680 643 -1340 643 -684 1271 -1310 645 -684 1303 -682 643 -1308 1301 -1312 645 -682 1275 -1350 649 -644 649 -702 647 -678 679 -654 645 -646 681 -644 683 -642 1309 -672 647 -690 645 -1314 635 -680 1297 -688 651 -1316 647 -682 617 -686 649 -682 1307 -660 639 -704 639 -1304 1293 -688 653 -672 621 -696 653 -664 653 -668 651 -670 647 -704 613 -704 617 -704 613 -708 613 -708 613 -710 613 -1336 665 -652 1285 -680 655 -1340 583 -1734 2523 -2570 2543 -2572 2529 -2564 2533 -2570 2531 -2596 2519 -2478 4805 -1310 1299 -1314 1313 -680 637 -1328 1289 -684 623 -690 653 -1322 1289 -1308 651 -668 651 -688 615 -688 679 -654 1305 -1304 1305 -1304 645 -686 645 -688 643 -652 643 -712 1273 -680 653 -680 651 -1312 647 -682 1273 -1342 639 -680 1271 -718 613 -1348 1281 -1310 639 -680 1295 -1314 653 -678 645 -682 645 -678 675 -648 651 -678 651 -678 647 -678 1281 -698 613 -706 613 -1336 669 -658 1293 -684 639 -1330 629 -686 653 -686 653 -654 1309 -678 621 -1340 1273 -712 603 -712 639 -672 653 -664 651 -666 651 -670 651 -702 617 -672 649 -704 613 -706 615 -706 615 -708 613 -1334 1293 -688 621 -1316 659 -686 1281 -2338 2501 -2600 2511 -2606 2491 -2624 2481 -2610 2521 -2560 2527 -2506 4797 -1308 1299 -1312 1317 -678 635 -1294 1323 -682 619 -692 635 -1334 1291 -1312 653 -658 679 -656 647 -688 643 -654 1303 -1336 1301 -1304 643 -690 641 -654 675 -654 643 -682 1303 -678 655 -680 645 -1308 677 -654 1309 -1310 645 -682 1275 -686 647 -1320 1281 -1318 679 -642 1317 -1300 667 -636 677 -660 673 -656 705 -612 671 -670 643 -670 681 -646 1309 -660 669 -674 637 -1310 673 -658 1293 -690 653 -1290 657 -688 649 -656 679 -658 1303 -676 635 -1314 657 -658 1307 -678 669 -670 621 -690 635 -672 649 -674 647 -674 647 -674 647 -708 613 -710 645 -676 647 -676 613 -1336 631 -688 1285 -680 655 -1306 647 -688 611 -1706 2535 -2592 2529 -2570 2533 -2566 2543 -2570 2529 -2572 2533 -2474 4807 -1294 1325 -1314 1279 -680 665 -1290 1323 -648 659 -688 653 -1290 1317 -1310 657 -656 679 -658 641 -688 643 -656 1303 -1334 1299 -1302 675 -646 675 -644 677 -658 643 -688 1303 -676 641 -676 633 -1314 691 -654 1277 -1336 649 -656 1305 -674 667 -1284 1315 -1308 653 -656 1305 -1306 679 -658 645 -690\nRAW_Data: 641 -656 709 -624 673 -656 679 -652 675 -648 1301 -678 645 -670 643 -1332 645 -656 1305 -678 645 -1336 641 -692 641 -656 673 -654 1307 -1308 1303 -678 679 -642 647 -678 647 -680 655 -648 655 -686 647 -656 675 -656 655 -656 683 -658 643 -688 643 -654 675 -1314 1275 -692 651 -1288 1319 -678 635 -1296 661 -1684 2527 -2602 2515 -2568 2541 -2602 2479 -2612 2523 -2578 2509 -2502 4783 -1344 1273 -1320 1283 -710 637 -1292 1319 -680 637 -668 649 -1332 1291 -1316 653 -656 679 -642 645 -688 679 -670 1289 -1310 1283 -1308 679 -670 629 -688 653 -654 647 -688 1311 -676 621 -708 641 -1306 677 -646 1307 -1306 643 -678 1303 -676 635 -1318 1289 -1340 651 -672 1275 -1338 649 -670 631 -688 655 -686 679 -636 667 -654 653 -656 681 -676 1277 -680 653 -676 655 -1304 645 -688 1271 -710 635 -1308 653 -668 671 -658 1295 -686 637 -1330 1295 -1310 1281 -678 665 -648 667 -664 647 -670 649 -702 613 -708 613 -710 645 -676 645 -676 647 -642 681 -1304 1295 -1314 653 -656 1309 -678 635 -680 633 -32700 2601 -2608 2491 -2602 2525 -2598 2521 -2604 2511 -2604 2531 -2584 2535 -2602 2503 -2620 2493 -2602 2533 -2600 2505 -2610 2533 -2474 4829 -1338 1297 -1316 1283 -716 651 -1280 1303 -676 635 -688 637 -1334 1297 -1310 653 -656 681 -644 645 -680 1307 -676 655 -1308 1305 -1304 645 -688 643 -656 643 -684 1307 -678 655 -682 647 -680 647 -1302 645 -680 1305 -1310 643 -682 1303 -682 621 -1312 1311 -1314 671 -644 1313 -1316 637 -680 669 -658 637 -696 637 -682 677 -648 643 -684 643 -68"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/somfy_telis_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 675 -624 645 -648 643 -1278 1319 -1242 675 -626 2435 -727768 3971 -2848 681 -1752 2603 -2562 2599 -2534 4613 -1258 1333 -1252 1305 -644 649 -1270 681 -610 667 -614 679 -608 1311 -638 665 -614 665 -620 695 -608 651 -1278 665 -634 667 -614 1303 -652 653 -632 671 -610 649 -642 649 -620 699 -1250 651 -650 1291 -618 675 -642 649 -1268 1331 -616 671 -1250 685 -616 1293 -622 675 -1272 673 -618 1309 -1286 631 -656 665 -626 1289 -628 673 -618 679 -1242 1341 -612 681 -608 651 -1278 1325 -624 643 -646 667 -1248 1319 -616 679 -608 681 -32648 9731 -7680 2595 -2560 2597 -2534 4615 -1256 1303 -1286 1305 -642 649 -1270 665 -616 691 -624 663 -624 1291 -628 673 -1276 1295 -618 665 -1280 677 -628 643 -646 1295 -622 659 -1288 1307 -630 669 -612 681 -1240 679 -614 1319 -628 673 -1244 697 -592 1337 -608 685 -616 669 -1250 1311 -638 669 -614 681 -1236 1341 -1246 1323 -1272 1305 -632 665 -1256 677 -630 1305 -614 673 -1254 685 -616 1295 -622 675 -1270 669 -626 1307 -624 655 -1294 639 -14120 2597 -2568 2589 -2540 2625 -2542 2589 -2572 2593 -2546 2623 -2536 2589 -2586 4593 -1248 1325 -1278 1303 -606 683 -1270 667 -612 665 -636 669 -614 1305 -650 653 -1258 1305 -644 649 -1270 667 -616 695 -624 1291 -640 641 -1286 1297 -620 675 -640 649 -1274 683 -608 1323 -616 675 -1246 661 -628 1335 -608 651 -652 667 -1252 1313 -630 667 -612 679 -1272 1305 -1244 1321 -1290 1305 -626 671 -1240 673 -618 1339 -618 669 -1250 653 -650 1291 -620 697 -1250 677 -612 1297 -642 683 -1242 667 -14104 2623 -2526 2621 -2532 2619 -2532 2623 -2534 2619 -2530 2623 -2534 2619 -2532 4629 -1244 1323 -1274 1303 -642 649 -1268 669 -614 661 -624 699 -594 1319 -614 693 -1256 1303 -644 649 -1268 667 -614 693 -594 1323 -628 673 -1278 1291 -616 665 -622 667 -1280 675 -628 1303 -612 677 -1254 695 -608 1307 -634 671 -614 647 -1272 1341 -616 669 -596 681 -1256 1333 -1256 1305 -1270 1303 -642 649 -1282 667 -618 1303 -618 691 -1256 679 -612 1301 -644 649 -1278 665 -634 1297 -624 681 -1236 683 -32670 9703 -7674 2591 -2582 2575 -2564 4603 -1260 1309 -1274 1301 -644 683 -1246 665 -618 677 -608 685 -612 1333 -622 665 -624 637 -644 647 -642 649 -654 631 -1268 683 -612 1335 -626 637 -644 645 -638 679 -604 679 -1256 679 -606 685 -614 1301 -652 639 -642 647 -1266 1339 -614 671 -1252 687 -616 1293 -622 691 -1272 643 -616 1319 -1270 683 -616 673 -624 1301 -608 683 -614 679 -1256 1313 -630 667 -630 665 -1256 1305 -644 649 -642 649 -1280 1297 -616 679 -614 659 -14758 2623 -2534 2621 -2520 2619 -2558 2609 -2534 2603 -2560 2595 -2546 2605 -2560 4603 -1258 1307 -1274 1337 -608\nRAW_Data: 651 -1280 665 -616 677 -608 683 -612 1333 -624 665 -626 665 -624 665 -624 665 -626 665 -1254 675 -628 1305 -610 677 -624 653 -628 673 -624 681 -1238 679 -628 675 -620 1315 -624 643 -626 679 -1272 1309 -614 681 -1238 677 -614 1319 -626 675 -1250 695 -594 1335 -1272 641 -618 693 -628 1297 -624 681 -608 663 -1278 1301 -610 683 -610 679 -1256 1339 -608 665 -616 665 -1278 1305 -642 649 -644 649 -14760 2597 -2544 2619 -2534 2589 -2588 2587 -2560 2577 -2576 2597 -2564 2567 -2568 4613 -1274 1305 -1252 1307 -642 681 -1240 679 -614 665 -618 667 -620 1323 -642 641 -650 643 -616 693 -592 699 -622 665 -1250 679 -626 1303 -612 679 -626 675 -628 643 -648 655 -1254 675 -628 641 -654 1281 -658 641 -616 677 -1278 1315 -612 681 -1270 643 -616 1317 -626 675 -1280 667 -626 1305 -1270 641 -652 655 -630 1283 -654 653 -630 669 -1242 1333 -608 681 -604 677 -1284 1297 -618 679 -614 663 -1282 1301 -640 651 -610 679 -32664 9707 -7678 2607 -2556 2577 -2576 4589 -1266 1309 -1266 1339 -612 645 -1300 641 -652 653 -630 671 -612 1305 -1276 655 -628 1301 -644 649 -1278 665 -632 671 -612 1305 -1278 653 -628 1303 -644 649 -620 695 -1258 649 -642 1307 -1258 677 -628 643 -652 1279 -1288 1309 -1258 1309 -1272 1303 -1270 1341 -616 673 -624 641 -1284 1295 -1282 675 -594 677 -616 1317 -1288 647 -622 659 -628 1335 -1270 645 -652 655 -626 1291 -1256 679 -622 661 -14126 2589 -2572 2593 -2542 2615 -2532 2621 -2556 2585 -2560 2579 -2572 2591 -2562 4621 -1242 1305 -1268 1343 -614 669 -1252 685 -616 669 -626 641 -626 1307 -1280 663 -620 1325 -628 641 -1280 661 -626 675 -618 1311 -1282 671 -612 1309 -612 693 -624 641 -1270 673 -624 1309 -1284 659 -608 685 -610 1301 -1282 1303 -1270 1301 -1302 1305 -1244 1321 -626 675 -624 653 -1262 1309 -1274 673 -618 681 -610 1325 -1254 677 -624 675 -616 1305 -1272 651 -634 665 -618 1303 -1280 657 -636 675 -14100 2585 -2572 2593 -2548 2591 -2570 2591 -2552 2621 -2532 2609 -2568 2597 -2528 4615 -1278 1305 -1272 1305 -612 679 -1284 639 -644 647 -644 647 -650 1291 -1280 675 -616 1305 -610 679 -1254 685 -614 673 -626 1305 -1272 677 -618 1315 -624 641 -650 641 -1276 655 -626 1333 -1266 671 -618 655 -632 1295 -1276 1297 -1282 1303 -1274 1333 -1238 1337 -616 673 -624 641 -1276 1301 -1284 677 -612 675 -622 1311 -1254 685 -616 671 -626 1301 -1272 675 -622 655 -628 1297 -1284 667 -596 681 -32664 9707 -7662 2623 -2532 2619 -2556 4595 -1276 1297 -1278 1305 -644 647 -1272 685 -606 665 -616 665 -622 1325 -612 673 -624 679 -612 661 -1276 677 -614 675 -622 651 -636 1297 -624 681 -610 665 -618 665 -1280 1305 -1268\nRAW_Data: 669 -624 1309 -622 659 -660 643 -1282 1293 -620 677 -1270 675 -624 1317 -592 675 -1278 671 -624 1301 -1272 675 -622 655 -632 1297 -624 679 -610 663 -1284 1305 -608 685 -612 675 -1252 1341 -604 665 -618 675 -1268 1297 -642 649 -652 631 -14758 2625 -2528 2625 -2546 2581 -2562 2595 -2576 2579 -2564 2593 -2546 2611 -2562 4605 -1254 1337 -1240 1329 -610 683 -1272 667 -610 667 -614 665 -618 1325 -628 673 -622 649 -636 667 -1274 641 -646 669 -620 653 -634 1291 -650 641 -650 653 -634 669 -1242 1333 -1266 669 -618 1311 -626 643 -648 675 -1248 1317 -612 667 -1274 677 -614 1301 -642 649 -1284 665 -626 1291 -1290 649 -622 657 -628 1333 -608 685 -614 673 -1252 1311 -634 665 -614 679 -1272 1303 -614 677 -624 653 -1286 1307 -626 671 -612 681 -14722 2623 -2550 2591 -2570 2591 -2544 2621 -2530 2615 -2542 2597 -2570 2591 -2544 4609 -1286 1297 -1284 1293 -620 675 -1272 675 -620 653 -632 669 -614 1305 -614 691 -630 639 -644 649 -1272 679 -612 661 -620 693 -608 1309 -634 671 -612 679 -608 651 -1280 1321 -1256 679 -624 1313 -612 679 -608 685 -1242 1325 -624 655 -1286 649 -652 1291 -616 665 -1280 675 -614 1301 -1272 679 -610 665 -618 1325 -630 675 -618 653 -1262 1307 -644 647 -644 647 -1280 1327 -624 641 -618 677 -1274 1315 -614 679 -608 649 -32690 9699 -7666 2621 -2560 2589 -2544 4597 -1290 1307 -1254 1305 -644 647 -1294 669 -620 653 -632 665 -626 1293 -638 675 -616 675 -616 667 -1272 651 -608 1333 -1270 1305 -610 681 -624 657 -626 673 -1282 1289 -618 665 -1280 1303 -644 647 -644 647 -1282 1293 -626 679 -1272 653 -626 1303 -644 649 -1284 667 -612 1305 -1276 659 -628 677 -618 1311 -626 655 -626 675 -1278 1291 -616 677 -608 683 -1272 1293 -652 667 -598 679 -1256 1335 -622 665 -594 697 -14730 2627 -2526 2609 -2538 2629 -2528 2607 -2566 2595 -2556 2579 -2584 2591 -2562 4589 -1272 1313 -1278 1301 -610 683 -1278 653 -632 637 -646 681 -608 1307 -632 671 -612 647 -644 653 -1282 665 -614 1323 -1272 1305 -628 671 -612 679 -608 681 -1262 1293 -652 631 -1288 1309 -632 661 -608 685 -1278 1281 -634 677 -1256 675 -626 1315 -616 647 -1272 679 -612 1321 -1256 679 -622 655 -626 1337 -610 649 -654 633 -1286 1311 -630 667 -614 679 -1270 1305 -616 677 -624 643 -1276 1303 -654 639 -642 649 -14754 2611 -2544 2593 -2572 2587 -2544 2623 -2530 2627 -2544 2615 -2530 2617 -2544 4605 -1284 1299 -1248 1323 -618 679 -1268 669 -622 651 -634 665 -614 1327 -612 673 -618 681 -610 663 -1278 675 -594 1339 -1246 1321 -626 675 -624 651 -634 669 -1242 1335 -608 685 -1244 1319 -642 641 -648 643 -1280 1315 -614 679 -1272 645 -650 1287 -626\nRAW_Data: 675 -1278 669 -624 1301 -1270 643 -652 657 -630 1297 -618 681 -614 665 -1276 1301 -642 649 -610 679 -1254 1333 -624 681 -606 665 -1280 1301 -608 681 -612 677 -32676 9683 -7690 2587 -2562 2609 -2536 4601 -1274 1335 -1252 1309 -640 649 -1270 669 -612 693 -592 699 -590 1325 -1266 683 -614 1297 -1278 677 -616 1301 -1272 1311 -1284 671 -612 1305 -1276 1313 -614 679 -1274 1303 -1274 655 -630 671 -612 1305 -1278 1313 -1274 1305 -1270 1303 -1278 1315 -598 679 -628 641 -1276 1319 -1274 675 -614 667 -624 1309 -1254 685 -614 671 -626 1305 -1272 677 -618 651 -636 1295 -1284 669 -626 641 -14100 2633 -2546 2597 -2562 2597 -2546 2601 -2556 2613 -2544 2591 -2572 2591 -2540 4599 -1264 1339 -1268 1309 -614 681 -1234 701 -616 667 -608 665 -634 1291 -1296 649 -644 1305 -1254 677 -628 1303 -1274 1313 -1254 683 -616 1297 -1282 1307 -608 685 -1274 1313 -1260 645 -638 685 -616 1293 -1280 1309 -1270 1303 -1270 1307 -1282 1295 -624 677 -612 661 -1284 1303 -1270 671 -624 679 -612 1325 -1240 679 -624 655 -628 1335 -1238 677 -614 693 -594 1321 -1276 657 -626 675 -14088 2609 -2544 2627 -2530 2607 -2572 2601 -2528 2605 -2574 2601 -2528 2605 -2574 4575 -1286 1295 -1286 1303 -640 649 -1272 667 -610 693 -592 673 -644 1307 -1274 653 -628 1303 -1274 649 -640 1291 -1286 1301 -1278 677 -612 1329 -1270 1303 -614 677 -1262 1307 -1276 651 -626 675 -622 1311 -1256 1303 -1280 1319 -1276 1301 -1272 1303 -644 647 -624 657 -1296 1307 -1270 649 -628 675 -620 1313 -1256 681 -608 685 -616 1295 -1278 675 -628 643 -650 1279 -1286 651 -650 633 -32690 9691 -7690 2587 -2562 2609 -2562 4603 -1240 1337 -1250 1305 -644 647 -1270 667 -652 657 -626 665 -626 1291 -1286 649 -622 1319 -1278 643 -646 667 -622 653 -634 1325 -1258 647 -644 1305 -1254 1339 -1240 669 -626 1307 -1278 665 -632 671 -614 1309 -1272 1315 -1240 1335 -1270 1301 -1272 1307 -622 657 -636 677 -1274 1305 -1254 677 -628 643 -652 1315 -1250 655 -618 697 -598 1309 -1274 675 -616 681 -610 1325 -1256 675 -626 649 -14098 2623 -2546 2585 -2566 2593 -2574 2579 -2558\n"
  },
  {
    "path": "applications/debug/unit_tests/resources/unit_tests/subghz/test_random_raw.sub",
    "content": "Filetype: Flipper SubGhz RAW File\nVersion: 1\nFrequency: 433920000\nPreset: FuriHalSubGhzPresetOok650Async\nProtocol: RAW\nRAW_Data: 1160 -296 263 -166 65 -66 133 -98 133 -494 97 -226 361 -100 295 -66 131 -164 397 -66 431 -198 989 -100 431 -66 131 -66 197 -1052 231 -400 165 -102 199 -100 199 -100 299 -68 229 -234 167 -98 201 -136 267 -132 165 -100 131 -66 67 -66 131 -68 231 -100 99 -134 165 -132 665 -66 231 -100 267 -66 197 -100 659 -66 231 -266 197 -98 229 -990 297 -98 67 -796 99 -562 231 -300 65 -100 363 -1094 163 -336 331 -132 327 -98 261 -98 293 -100 265 -100 1265 -66 335 -134 267 -98 333 -134 99 -66 331 -66 195 -66 227 -164 263 -100 65 -692 163 -1978 97 -232 63 -132 131 -66 263 -66 293 -98 727 -132 65 -66 97 -66 295 -132 97 -66 263 -100 365 -100 165 -268 463 -66 65 -66 265 -1326 97 -100 197 -888 297 -130 327 -98 229 -264 65 -496 131 -266 195 -132 259 -132 361 -132 165 -98 927 -332 99 -66 99 -100 197 -658 65 -100 197 -164 133 -766 197 -236 199 -168 65 -98 233 -198 65 -100 129 -166 197 -790 97 -394 393 -130 557 -100 65 -230 333 -132 1389 -134 65 -66 99 -100 265 -134 99 -100 97 -460 327 -166 233 -266 199 -562 67 -898 197 -100 165 -1100 231 -100 263 -100 263 -132 1161 -66 463 -98 133 -98 165 -130 131 -66 65 -264 97 -266 97 -66 163 -166 97 -66 229 -296 99 -132 391 -98 327 -98 163 -200 631 -196 169 -102 131 -166 1327 -134 1361 -132 331 -134 99 -396 195 -364 97 -132 163 -66 99 -264 133 -200 165 -334 99 -134 65 -1332 165 -132 133 -298 367 -1124 295 -66 129 -66 593 -66 329 -264 397 -66 533 -66 461 -66 133 -132 65 -266 65 -132 99 -232 131 -2186 297 -98 165 -100 65 -66 97 -100 63 -566 99 -100 133 -66 333 -332 231 -364 231 -68 165 -300 497 -100 763 -100 365 -98 165 -200 65 -100 65 -696 65 -134 65 -262 65 -430 97 -66 131 -98 131 -330 293 -198 97 -132 163 -628 133 -98 131 -198 559 -98 459 -64 753 -98 361 -100 165 -132 133 -364 99 -66 197 -100 65 -132 197 -896 199 -66 65 -432 231 -168 231 -100 299 -528 131 -232 99 -134 97 -66 299 -66 197 -68 495 -100 65 -98 65 -100 563 -828 197 -732 267 -1260 65 -100 165 -898 65 -430 165 -496 199 -66 231 -66 131 -132 195 -66 193 -294 1259 -134 165 -100 199 -66 199 -166 233 -430 97 -366 97 -166 65 -1196 97 -132 97 -64 131 -196 97 -66 131 -960 229 -98 99 -264 233 -134 297 -98 467 -100 165 -98 365 -98 735 -98 197 -1464 233 -626 301 -432 97 -132\nRAW_Data: 231 -100 163 -362 163 -132 261 -298 65 -232 67 -132 297 -100 229 -228 497 -66 395 -98 593 -132 165 -132 67 -100 397 -132 99 -600 297 -66 231 -132 495 -136 65 -860 197 -98 129 -260 129 -100 131 -134 331 -268 165 -98 167 -66 231 -68 131 -232 65 -396 99 -100 663 -200 231 -66 1067 -66 99 -166 331 -132 65 -196 65 -134 133 -66 795 -66 397 -100 233 -132 199 -66 233 -200 433 -362 67 -298 327 -100 397 -66 165 -66 233 -232 529 -66 165 -100 67 -66 397 -66 689 -100 195 -462 325 -164 227 -132 261 -426 331 -98 133 -66 97 -296 97 -98 197 -66 97 -428 163 -526 359 -132 327 -164 99 -66 231 -396 329 -64 525 -66 593 -856 195 -100 129 -132 227 -166 229 -130 129 -98 293 -132 163 -98 195 -130 195 -98 97 -66 165 -100 99 -66 199 -466 265 -66 131 -132 197 -198 163 -232 1857 -100 265 -264 67 -264 265 -368 297 -102 133 -66 233 -98 167 -66 201 -66 133 -134 167 -166 65 -168 99 -400 165 -498 97 -298 131 -198 227 -132 327 -98 97 -66 297 -230 593 -132 67 -132 1227 -68 199 -134 197 -102 131 -232 365 -962 133 -298 297 -66 265 -266 333 -230 165 -102 165 -234 65 -98 131 -98 133 -66 167 -134 1357 -134 599 -532 265 -268 99 -298 199 -100 165 -166 131 -100 231 -266 65 -68 231 -334 67 -266 97 -134 65 -198 133 -100 163 -336 165 -234 65 -332 163 -690 97 -564 263 -64 165 -66 499 -100 563 -230 229 -132 99 -166 165 -132 199 -66 497 -66 659 -166 233 -68 331 -100 331 -66 165 -202 133 -896 133 -228 163 -196 131 -66 229 -130 263 -164 65 -98 263 -164 889 -66 261 -164 99 -494 97 -132 131 -64 65 -462 65 -262 99 -332 65 -102 65 -496 99 -132 197 -230 299 -134 65 -166 99 -430 99 -100 563 -66 165 -330 199 -264 197 -68 231 -100 367 -134 365 -68 299 -68 495 -68 165 -100 65 -498 397 -66 327 -100 195 -98 163 -132 163 -360 197 -98 65 -462 97 -526 427 -100 1419 -66 265 -66 333 -98 299 -98 99 -132 97 -100 197 -134 97 -100 231 -68 133 -100 165 -100 97 -100 133 -98 133 -66 165 -166 267 -132 229 -68 299 -66 97 -964 131 -66 133 -98 99 -66 297 -132 131 -68 167 -134 201 -68 167 -66 301 -166 133 -68 365 -100 527 -130 165 -130 233 -262 95 -628 97 -66 231 -168 99 -498 99 -100 229 -100 99 -166 299 -232 65 -100 99 -468 131 -68 197 -100 99 -66 297 -330 99 -100 463 -100 231 -68 133 -234 131 -134 231 -98\nRAW_Data: 561 -264 131 -100 65 -98 99 -66 133 -134 165 -132 65 -68 97 -68 131 -896 329 -198 65 -100 295 -230 199 -264 97 -196 327 -132 131 -100 131 -100 1293 -66 131 -202 463 -198 65 -68 99 -298 65 -232 333 -566 199 -168 99 -168 65 -498 295 -134 167 -134 199 -100 431 -100 65 -530 233 -164 65 -98 297 -134 363 -100 557 -100 163 -100 261 -66 619 -132 231 -296 97 -596 97 -228 131 -66 97 -66 197 -66 97 -360 229 -98 129 -330 99 -130 99 -130 65 -100 65 -132 97 -460 63 -100 265 -330 331 -98 197 -66 855 -464 165 -66 99 -198 99 -868 231 -66 167 -636 329 -530 165 -130 131 -132 99 -528 165 -64 65 -164 65 -166 129 -162 425 -66 231 -100 133 -98 231 -68 333 -98 165 -200 361 -98 99 -66 297 -68 163 -168 65 -498 99 -232 295 -696 99 -266 131 -100 65 -100 99 -628 133 -132 65 -366 265 -398 261 -100 529 -100 297 -132 2535 -596 65 -932 199 -66 199 -866 65 -298 165 -66 67 -132 65 -100 165 -200 131 -166 299 -200 167 -166 199 -334 65 -694 365 -132 727 -232 265 -66 133 -102 65 -100 531 -232 65 -132 65 -100 131 -562 197 -66 297 -66 265 -100 433 -432 495 -332 197 -98 167 -332 97 -66 297 -758 65 -228 99 -194 295 -130 99 -98 261 -98 163 -66 231 -134 1029 -68 399 -200 133 -132 99 -266 163 -264 163 -130 229 -66 689 -166 131 -988 65 -592 97 -132 129 -164 197 -432 65 -100 165 -134 65 -166 133 -232 563 -66 231 -66 599 -66 265 -100 199 -498 231 -464 327 -262 131 -100 327 -430 197 -266 99 -132 65 -66 133 -132 101 -200 65 -100 433 -166 99 -266 265 -98 97 -66 229 -100 263 -200 631 -100 199 -66 167 -300 753 -262 457 -132 231 -98 163 -164 227 -164 65 -262 129 -594 229 -790 131 -66 129 -428 197 -164 197 -200 133 -100 131 -564 65 -134 661 -134 2925 -68 231 -134 167 -132 99 -98 133 -98 167 -98 197 -1086 329 -296 65 -130 65 -264 197 -132 163 -98 165 -66 97 -132 129 -100 261 -66 97 -66 197 -326 95 -134 629 -98 65 -262 65 -132 197 -264 129 -98 163 -230 131 -528 261 -298 197 -64 131 -394 131 -764 99 -132 99 -66 99 -464 367 -98 165 -98 1159 -134 963 -66 633 -562 133 -132 99 -66 131 -564 197 -134 263 -132 195 -66 131 -262 459 -332 197 -66 133 -1060 133 -66 331 -662 197 -134 231 -100 299 -232 1087 -232 131 -100 265 -462 131 -200 433 -168 101 -432 97 -196 65 -132 327 -100 129 -226 163 -100 163 -132\nRAW_Data: 227 -330 129 -466 165 -200 163 -66 97 -130 97 -66 197 -164 457 -166 463 -166 361 -66 163 -66 97 -198 65 -64 99 -300 65 -68 263 -98 99 -134 197 -100 165 -134 231 -66 297 -98 67 -132 365 -66 197 -228 99 -428 195 -132 65 -330 265 -628 63 -130 97 -132 195 -232 195 -130 263 -132 167 -100 133 -166 1229 -264 165 -332 233 -1230 199 -102 167 -100 401 -166 229 -852 197 -264 229 -130 163 -198 65 -98 229 -66 199 -100 335 -98 899 -134 393 -528 97 -66 97 -300 99 -300 99 -664 329 -198 99 -400 131 -198 299 -330 99 -66 65 -132 67 -298 527 -66 493 -132 1085 -198 425 -232 197 -66 131 -164 165 -66 197 -1480 197 -330 65 -494 65 -330 197 -296 99 -66 97 -592 99 -164 97 -426 329 -66 131 -98 99 -66 265 -98 433 -66 131 -234 267 -234 65 -266 99 -98 65 -166 97 -164 197 -198 65 -866 67 -66 231 -430 131 -200 65 -100 631 -198 265 -526 99 -230 129 -68 165 -232 229 -98 427 -328 1053 -134 431 -198 365 -430 131 -100 131 -398 231 -134 1195 -132 131 -134 661 -100 333 -398 97 -400 97 -234 165 -468 65 -100 99 -66 299 -266 67 -98 397 -166 987 -132 97 -64 65 -266 97 -852 297 -132 163 -428 65 -298 99 -1124 97 -266 233 -66 165 -332 65 -800 297 -98 131 -164 469 -100 97 -658 165 -66 133 -132 531 -134 131 -98 297 -722 65 -132 821 -100 229 -132 459 -66 197 -98 985 -330 131 -100 133 -100 101 -198 299 -100 65 -134 363 -100 67 -830 131 -132 99 -100 299 -66 297 -366 99 -166 197 -66 131 -364 131 -200 331 -134 265 -530 99 -564 65 -68 231 -132 233 -134 165 -66 165 -66 431 -102 465 -132 99 -132 99 -364 133 -98 67 -198 133 -66 65 -264 99 -298 231 -100 199 -728 297 -132 65 -134 433 -926 263 -66 227 -528 65 -130 263 -100 97 -98 723 -100 131 -134 531 -66 267 -400 199 -66 65 -100 99 -498 99 -332 529 -98 197 -460 131 -296 231 -296 97 -66 65 -426 131 -132 133 -100 99 -66 99 -964 463 -68 631 -68 1325 -232 365 -68 163 -432 131 -432 301 -234 165 -432 99 -398 65 -100 133 -166 433 -230 163 -236 67 -132 97 -300 65 -66 199 -132 2355 -296 327 -98 65 -66 99 -294 197 -232 133 -100 67 -134 97 -66 65 -760 65 -98 131 -166 197 -132 63 -132 163 -98 163 -754 429 -230 163 -66 695 -132 199 -98 433 -100 1197 -394 491 -460 65 -460 65 -166 97 -68 131 -134 233 -66 299 -762 231 -200 265 -198 499 -132 167 -766\nRAW_Data: 265 -66 165 -134 165 -66 365 -132 2091 -134 231 -632 297 -630 195 -264 65 -296 65 -64 131 -298 165 -100 99 -100 99 -562 165 -398 99 -566 99 -166 133 -334 99 -134 357 -68 97 -332 459 -130 161 -230 163 -132 163 -98 195 -164 97 -130 65 -1262 265 -166 563 -964 133 -100 99 -796 199 -868 67 -134 65 -232 67 -66 199 -132 295 -98 821 -332 1097 -296 129 -758 65 -200 67 -98 531 -198 265 -66 863 -66 65 -300 363 -100 99 -132 165 -100 267 -134 165 -234 231 -100 533 -198 1057 -200 497 -68 531 -100 63 -66 133 -100 163 -330 63 -230 161 -100 97 -66 131 -298 165 -68 167 -732 265 -396 265 -100 99 -166 99 -200 65 -596 131 -696 263 -200 135 -100 133 -66 65 -98 131 -66 399 -66 367 -130 765 -298 97 -98 231 -130 197 -130 65 -294 131 -98 195 -66 129 -66 131 -428 131 -66 197 -660 133 -66 131 -332 165 -628 65 -724 99 -164 131 -98 65 -166 395 -68 435 -68 299 -66 1323 -400 231 -298 99 -264 199 -428 131 -230 65 -1188 99 -98 163 -526 327 -66 197 -394 165 -66 65 -596 857 -66 163 -66 591 -166 129 -234 167 -624 265 -294 263 -500 131 -164 65 -98 195 -166 97 -100 195 -198 131 -1214 1017 -166 97 -428 425 -66 1183 -430 261 -230 65 -196 97 -100 195 -364 65 -328 65 -560 97 -1084 197 -694 99 -132 65 -132 97 -66 229 -332 163 -330 197 -66 327 -100 195 -98 327 -98 993 -132 697 -332 665 -166 199 -234 99 -168 199 -266 201 -66 199 -462 231 -298 195 -66 165 -884 165 -196 131 -66 365 -98 231 -98 329 -430 165 -66 1195 -300 133 -100 163 -132 65 -462 131 -196 163 -228 197 -992 163 -162 95 -824 131 -98 263 -98 97 -166 65 -98 131 -100 65 -496 131 -100 265 -134 199 -68 199 -98 329 -132 131 -66 983 -198 99 -132 295 -66 261 -460 329 -164 131 -426 65 -268 65 -466 131 -66 131 -230 263 -230 297 -200 895 -66 231 -460 1055 -134 433 -100 65 -66 231 -398 131 -400 97 -68 165 -330 297 -330 133 -396 99 -66 263 -262 97 -1644 295 -66 297 -100 729 -132 335 -68 491 -66 231 -326 393 -68 329 -166 65 -922 99 -1652 197 -266 265 -132 99 -432 265 -134 293 -134 65 -166 231 -232 199 -64 631 -400 163 -400 99 -66 131 -730 265 -100 195 -66 195 -330 163 -132 131 -166 67 -830 99 -66 297 -664 97 -134 65 -100 295 -66 133 -166 997 -66 299 -66 795 -98 493 -68 131 -166 131 -100 165 -198 67 -98 99 -598 65 -266 97 -268 199 -132\nRAW_Data: 65 -364 97 -298 97 -692 129 -264 261 -66 263 -564 297 -298 97 -232 131 -66 97 -66 361 -100 199 -68 433 -66 331 -68 333 -438 165 -598 99 -134 65 -100 65 -264 399 -266 67 -764 197 -232 297 -498 199 -858 65 -100 97 -130 131 -230 231 -230 97 -362 229 -66 497 -66 329 -132 65 -64 423 -98 393 -132 65 -664 65 -198 99 -68 331 -100 97 -332 133 -166 199 -98 693 -332 429 -100 265 -166 363 -100 167 -66 497 -100 1365 -202 333 -200 65 -132 199 -100 99 -330 131 -300 229 -832 65 -764 65 -132 263 -200 131 -1026 97 -196 97 -132 165 -98 525 -232 431 -66 659 -66 461 -98 97 -132 65 -100 261 -66 129 -66 163 -100 229 -364 131 -1560 165 -364 133 -268 165 -132 99 -200 65 -434 97 -68 263 -298 131 -230 199 -132 199 -100 165 -68 1829 -134 463 -100 265 -890 395 -364 131 -362 97 -260 97 -462 65 -66 457 -296 197 -268 263 -66 363 -132 895 -66 435 -164 403 -464 165 -928 163 -134 167 -400 263 -660 131 -100 63 -428 199 -132 199 -700 397 -232 335 -66 497 -166 1425 -66 231 -102 231 -134 99 -132 133 -134 99 -530 133 -66 131 -66 165 -166 199 -266 65 -100 131 -100 527 -300 925 -266 363 -530 131 -898 165 -66 199 -134 165 -232 525 -100 1509 -100 99 -200 99 -168 67 -300 163 -404 263 -132 67 -66 263 -664 65 -494 199 -100 165 -100 263 -264 131 -264 161 -66 65 -428 131 -296 163 -264 559 -166 597 -132 199 -498 65 -66 263 -162 163 -66 65 -66 131 -762 197 -162 65 -98 297 -262 163 -66 131 -130 97 -100 131 -564 199 -100 333 -166 165 -66 1653 -100 689 -296 131 -230 229 -526 65 -462 131 -100 197 -398 99 -264 133 -428 233 -134 265 -132 65 -200 97 -100 131 -100 265 -232 99 -232 429 -196 263 -166 669 -230 293 -298 561 -100 65 -100 229 -100 99 -830 99 -132 65 -1058 133 -564 99 -100 131 -826 165 -100 101 -100 65 -100 99 -398 329 -266 65 -66 557 -132 67 -132 627 -464 99 -66 163 -200 99 -66 99 -232 65 -66 165 -132 167 -266 99 -598 65 -432 65 -134 99 -330 197 -202 65 -266 133 -598 99 -68 163 -66 165 -66 1957 -430 393 -134 97 -134 97 -530 65 -428 161 -1150 65 -64 131 -594 229 -326 131 -196 197 -132 65 -566 297 -68 367 -64 1119 -464 163 -334 165 -134 163 -102 99 -66 199 -460 99 -100 99 -132 67 -266 99 -98 99 -532 197 -992 129 -362 231 -166 65 -132 133 -98 599 -98 795 -66 235 -100 333 -232 331 -98 99 -430\nRAW_Data: 131 -266 297 -232 231 -100 267 -266 397 -232 131 -132 133 -668 65 -98 99 -68 131 -398 1325 -66 333 -166 99 -100 199 -960 99 -66 231 -132 167 -134 199 -100 165 -100 495 -132 367 -332 99 -266 495 -66 65 -66 99 -66 65 -134 163 -134 199 -164 199 -98 531 -132 497 -100 65 -100 97 -530 99 -100 131 -68 131 -200 265 -102 97 -332 365 -66 265 -434 163 -68 97 -232 265 -132 65 -264 133 -198 497 -100 197 -68 729 -66 165 -330 165 -762 265 -430 397 -3844 67 -432 1197 -66 631 -66 521 -458 131 -132 197 -264 65 -922 163 -1586 99 -492 229 -530 65 -634 131 -134 65 -398 99 -398 363 -366 295 -1158 99 -134 65 -298 431 -432 97 -634 131 -66 133 -298 97 -466 99 -66 65 -332 65 -498 65 -962 133 -132 99 -464 233 -598 165 -196 199 -68 331 -66 231 -166 65 -200 133 -100 197 -162 393 -264 327 -1052 65 -164 163 -560 99 -98 63 -394 165 -132 99 -166 99 -100 97 -234 131 -1028 131 -66 99 -394 99 -164 97 -298 163 -66 359 -296 197 -66 499 -98 167 -462 695 -132 131 -134 97 -264 65 -100 265 -132 197 -896 195 -166 65 -262 99 -492 97 -196 99 -66 163 -164 97 -1414 133 -364 65 -132 99 -362 131 -164 195 -296 67 -334 197 -98 227 -66 265 -666 165 -430 167 -364 65 -98 97 -230 67 -364 133 -132 165 -134 65 -100 197 -230 97 -462 131 -296 195 -262 99 -98 327 -530 97 -464 99 -198 167 -234 97 -628 99 -266 131 -134 165 -166 65 -630 393 -100 197 -964 99 -400 163 -230 129 -264 97 -100 333 -332 165 -100 67 -66 199 -200 99 -166 133 -992 99 -66 163 -1590 65 -134 65 -366 331 -68 131 -1692 197 -166 99 -132 99 -100 429 -134 65 -100 367 -164 65 -166 329 -166 131 -234 99 -564 99 -100 99 -66 99 -100 263 -164 201 -436 131 -100 133 -268 231 -232 99 -366 65 -498 131 -100 131 -300 265 -432 199 -132 67 -100 163 -300 331 -100 263 -296 129 -68 97 -100 165 -234 99 -234 101 -66 67 -100 97 -1328 97 -264 99 -3316 261 -134 65 -300 65 -102 199 -98 99 -100 231 -1424 165 -164 195 -130 99 -396 129 -598 199 -264 97 -566 65 -762 99 -132 197 -464 131 -66 65 -494 97 -66 229 -66 525 -428 65 -428 229 -920 459 -556 97 -300 199 -100 99 -466 67 -1362 233 -430 131 -132 131 -294 197 -130 163 -98 163 -132 131 -1020 165 -132 165 -98 97 -232 329 -132 165 -130 129 -132 193 -592 197 -130 97 -2076 133 -164 163 -162 97 -98 63 -760 131 -100\nRAW_Data: 659 -560 97 -98 165 -66 65 -396 163 -662 97 -230 133 -598 97 -796 399 -132 165 -132 133 -66 197 -66 131 -398 297 -232 231 -364 199 -430 65 -100 165 -1096 231 -98 165 -98 197 -560 297 -132 65 -1260 429 -824 97 -68 97 -664 199 -296 495 -630 263 -966 99 -234 167 -66 65 -264 231 -68 197 -68 367 -66 163 -132 163 -430 233 -100 399 -134 167 -98 165 -98 99 -132 231 -230 193 -98 161 -396 261 -494 163 -1290 265 -664 65 -66 461 -100 563 -66 431 -100 1087 -198 165 -166 165 -100 899 -102 169 -200 99 -200 265 -66 231 -68 165 -100 133 -396 231 -366 99 -466 65 -234 497 -66 133 -200 133 -100 99 -100 65 -398 199 -264 101 -132 201 -366 195 -228 229 -134 625 -66 163 -66 293 -164 523 -100 97 -196 163 -396 229 -330 99 -98 65 -100 359 -562 195 -98 131 -98 229 -132 63 -822 65 -100 99 -432 229 -100 261 -66 327 -100 325 -198 2007 -132 261 -98 65 -100 397 -164 133 -66 99 -332 65 -98 65 -696 99 -298 231 -260 97 -296 199 -98 229 -396 163 -398 65 -398 197 -100 363 -66 797 -66 199 -100 1259 -132 331 -330 265 -800 197 -200 97 -166 99 -730 133 -432 99 -168 265 -1060 165 -96 129 -164 163 -66 97 -64 295 -68 397 -100 1091 -98 533 -364 261 -64 163 -920 99 -98 65 -164 195 -462 99 -98 97 -198 97 -100 261 -230 131 -594 263 -100 163 -196 129 -328 97 -98 297 -66 129 -66 131 -66 229 -228 461 -100 1127 -64 359 -1052 231 -298 131 -232 163 -428 65 -230 261 -66 97 -366 131 -68 197 -332 231 -460 229 -262 461 -166 629 -200 367 -198 1745 -528 65 -266 67 -166 131 -564 165 -134 135 -1358 197 -198 229 -98 165 -690 131 -196 197 -66 261 -164 263 -98 63 -132 65 -100 229 -100 197 -100 299 -66 465 -66 165 -68 165 -432 131 -530 63 -330 65 -198 97 -326 97 -66 65 -132 195 -498 65 -98 165 -260 95 -264 295 -130 131 -98 197 -526 65 -896 65 -100 429 -98 163 -132 527 -132 1189 -330 299 -166 165 -232 97 -66 99 -198 67 -168 133 -100 167 -200 165 -100 101 -630 199 -596 199 -1354 99 -198 163 -64 227 -132 163 -98 163 -132 425 -100 461 -262 259 -130 195 -98 65 -130 97 -166 129 -66 165 -98 97 -658 99 -1374 163 -132 165 -328 195 -98 99 -496 63 -132 67 -98 197 -130 99 -66 129 -98 1147 -66 1219 -1120 165 -166 263 -66 65 -100 197 -130 99 -164 99 -100 99 -262 199 -332 97 -66 233 -760 65 -464 363 -696 131 -68 97 -68\nRAW_Data: 329 -164 197 -302 429 -296 229 -232 99 -364 97 -98 131 -98 293 -164 133 -200 165 -270 233 -366 131 -400 399 -100 99 -464 133 -66 299 -198 297 -100 199 -66 365 -66 563 -464 425 -166 365 -166 101 -298 165 -166 99 -300 331 -264 329 -298 65 -200 99 -66 165 -1290 129 -594 99 -66 197 -262 197 -164 163 -100 229 -98 163 -96 1709 -100 65 -66 363 -132 99 -824 361 -1388 165 -98 99 -1952 99 -398 263 -200 201 -300 231 -134 265 -166 133 -66 399 -98 233 -232 461 -66 99 -132 165 -100 65 -298 135 -498 99 -394 297 -296 67 -66 165 -432 165 -98 97 -66 265 -66 563 -98 199 -66 265 -232 597 -100 691 -98 631 -132 99 -632 199 -730 97 -132 99 -528 361 -492 131 -296 65 -460 65 -66 97 -164 99 -262 263 -66 97 -98 263 -64 491 -66 851 -100 161 -132 97 -926 99 -100 99 -98 97 -632 163 -326 231 -290 99 -132 199 -66 199 -166 297 -68 167 -564 1059 -1018 1009 -1016 969 -1042 987 -1008 1005 -988 1013 -1032 483 -550 483 -550 225 -282 517 -522 261 -294 499 -560 495 -554 231 -298 269 -266 233 -298 495 -524 521 -520 263 -290 263 -268 271 -308 241 -274 507 -524 259 -260 521 -522 521 -520 523 -518 523 -518 527 -552 231 -298 265 -266 497 -522 263 -258 521 -524 261 -294 499 -558 233 -298 269 -274 271 -270 499 -520 261 -290 229 -296 267 -268 505 -560 233 -298 271 -272 507 -524 487 -550 487 -550 259 -288 225 -298 269 -306 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 269 -270 271 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -288 229 -298 503 -558 231 -334 237 -274 997 -1008 1003 -984 1003 -1034 977 -1006 1013 -986 999 -1046 513 -514 511 -514 257 -280 513 -520 261 -292 497 -556 497 -554 233 -298 267 -268 233 -298 495 -522 527 -518 261 -290 263 -266 273 -306 243 -274 507 -524 261 -258 521 -522 523 -518 523 -516 523 -552 493 -552 231 -298 267 -266 497 -522 263 -256 523 -522 263 -294 497 -558 231 -300 269 -274 271 -272 497 -522 259 -290 227 -298 267 -270 505 -560 231 -300 271 -272 507 -522 521 -518 489 -548 259 -288 225 -298 269 -304 241 -308 241 -276 505 -524 259 -258 259 -294 265 -268 269 -270 271 -306 239 -308 241 -274 271 -270 499 -520 519 -518 261 -288 229 -298 503 -560 231 -300 269 -272 999 -1010 1013 -982 1007 -1018 973 -1016 999 -1010 987 -1044 481 -554 483 -520 273 -292 473 -552 245 -324 487 -532 501 -558 267 -268 269 -268 231 -298 495 -522 523 -520 259 -292\nRAW_Data: 263 -268 271 -308 241 -276 507 -522 261 -256 523 -522 523 -518 521 -518 525 -518 525 -554 229 -298 267 -268 495 -524 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 271 -270 499 -522 261 -290 227 -296 269 -270 505 -556 233 -298 271 -274 505 -524 521 -516 487 -548 259 -288 227 -298 271 -304 241 -308 241 -274 507 -524 259 -258 259 -294 265 -268 267 -270 271 -272 273 -308 241 -276 271 -270 499 -520 489 -550 259 -288 261 -264 505 -558 233 -300 269 -274 997 -1008 1003 -986 1005 -1002 1011 -1010 987 -1006 997 -1044 479 -546 513 -512 259 -280 511 -518 263 -292 497 -556 497 -556 231 -300 267 -266 233 -298 497 -522 523 -518 263 -290 263 -268 273 -306 241 -276 507 -522 261 -258 521 -522 523 -518 521 -518 525 -518 525 -554 231 -298 265 -266 499 -522 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 273 -270 499 -520 261 -288 229 -296 267 -270 505 -560 231 -300 271 -274 505 -524 489 -548 487 -548 261 -286 225 -298 271 -306 239 -308 241 -274 507 -524 259 -258 257 -296 267 -266 269 -270 271 -272 273 -308 239 -276 273 -268 497 -520 491 -550 261 -288 227 -296 505 -560 233 -298 269 -274 997 -1012 1009 -988 1007 -996 1009 -1008 1003 -982 999 -1044 513 -512 511 -514 257 -282 511 -520 261 -294 497 -556 497 -556 231 -300 265 -268 233 -298 495 -522 525 -518 263 -290 263 -268 271 -306 243 -274 509 -522 261 -256 523 -522 523 -518 491 -548 523 -552 493 -552 231 -298 265 -268 497 -520 261 -290 493 -522 263 -292 497 -558 231 -300 271 -272 271 -270 501 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -274 507 -522 489 -550 487 -550 227 -318 225 -298 269 -304 241 -308 239 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -308 241 -274 273 -270 497 -520 491 -550 259 -288 227 -298 505 -558 233 -330 237 -274 999 -1012 979 -1016 1005 -1018 977 -1016 983 -1038 977 -1036 475 -550 509 -514 257 -276 503 -548 259 -288 491 -556 497 -556 231 -298 265 -268 233 -298 497 -522 491 -550 261 -292 263 -300 237 -308 241 -276 505 -524 261 -290 491 -522 521 -518 523 -516 525 -550 495 -554 231 -296 231 -300 497 -522 261 -290 491 -522 261 -294 497 -558 231 -334 237 -274 271 -270 499 -520 261 -290 229 -298 267 -268 505 -558 231 -300 269 -274 505 -524 489 -550 487 -548 259 -288 227 -298 269 -306 239 -308 241 -274 509 -524 259 -256 259 -296 265 -268 269 -268 271 -304 239 -308 241 -276 271 -270 497 -522 489 -550\nRAW_Data: 261 -288 227 -296 505 -558 231 -300 271 -272 1001 -1012 977 -1026 1007 -992 1011 -1008 1001 -1002 1001 -1014 513 -512 513 -514 257 -282 513 -520 263 -292 497 -556 497 -556 231 -300 267 -268 231 -298 495 -522 525 -518 261 -292 263 -266 271 -308 241 -276 507 -522 261 -288 491 -522 523 -518 523 -516 523 -520 525 -554 231 -296 265 -268 497 -522 259 -290 491 -522 263 -294 499 -556 265 -266 271 -274 271 -270 499 -520 261 -290 227 -298 267 -270 505 -558 233 -300 269 -274 505 -524 519 -516 487 -550 259 -286 259 -266 269 -306 241 -308 239 -276 507 -522 261 -256 259 -296 267 -266 269 -270 269 -272 273 -308 241 -276 271 -270 497 -520 521 -518 259 -290 259 -264 503 -560 265 -266 271 -274 999 -1014 977 -1016 1007 -984 1009 -1014 999 -1010 985 -1044 483 -520 519 -522 253 -276 529 -512 257 -282 517 -556 497 -554 231 -298 267 -268 233 -298 497 -522 521 -518 261 -290 265 -268 273 -308 241 -276 505 -524 259 -258 521 -520 525 -518 521 -518 525 -518 525 -552 231 -296 267 -268 497 -522 261 -256 523 -522 263 -294 499 -560 231 -298 269 -274 271 -270 497 -522 259 -292 227 -298 267 -268 505 -558 233 -300 271 -272 507 -522 521 -516 487 -550 259 -286 227 -298 271 -304 239 -308 241 -276 507 -520 261 -256 259 -296 265 -268 269 -268 273 -272 273 -306 241 -276 273 -268 497 -520 521 -516 261 -290 227 -298 505 -556 265 -266 271 -274 999 -1008 1005 -986 1005 -1000 1007 -1008 1001 -996 1009 -1016 515 -514 513 -514 259 -282 515 -520 263 -292 497 -556 499 -554 231 -300 265 -268 233 -298 497 -522 523 -518 261 -290 263 -302 239 -308 241 -276 505 -524 259 -258 521 -522 523 -518 523 -518 523 -518 525 -552 231 -298 265 -268 497 -524 259 -258 521 -524 261 -294 499 -556 233 -298 271 -272 273 -270 497 -522 259 -290 229 -296 267 -270 505 -558 231 -124104 99 -334 97 -794 165 -232 99 -136 595 -260 65 -66 163 -100 1353 -100 231 -134 233 -100 231 -962 99 -432 131 -134 231 -234 133 -98 99 -430 65 -100 99 -396 99 -1228 197 -66 97 -200 299 -100 265 -66 99 -262 99 -64 1447 -66 163 -98 65 -562 197 -66 129 -100 197 -164 65 -262 65 -264 65 -164 65 -1490 99 -66 99 -66 65 -166 197 -134 99 -66 131 -100 99 -66 97 -66 231 -266 465 -66 299 -66 461 -98 295 -132 65 -528 229 -164 129 -396 65 -962 133 -566 99 -198 163 -830 197 -100 201 -400 67 -66 99 -132 299 -66 463 -168 231 -66 433 -66 895 -956 65 -828 229 -468\nRAW_Data: 197 -100 131 -130 97 -132 295 -66 97 -430 133 -198 65 -526 727 -66 229 -262 163 -98 525 -66 853 -132 165 -66 399 -166 99 -268 397 -134 233 -66 67 -298 131 -1130 133 -100 65 -134 297 -198 329 -134 197 -66 129 -590 133 -164 131 -132 131 -132 131 -98 2973 -230 65 -664 165 -134 165 -432 99 -166 165 -496 201 -132 99 -100 197 -68 131 -632 67 -98 133 -132 695 -100 293 -100 131 -98 199 -66 297 -100 199 -100 335 -100 265 -430 165 -66 297 -664 99 -264 199 -98 165 -134 167 -200 99 -100 65 -98 99 -296 65 -164 163 -494 99 -100 267 -198 99 -426 199 -134 99 -232 131 -330 65 -134 131 -134 201 -100 463 -232 97 -296 1087 -100 363 -134 97 -132 197 -300 131 -234 233 -990 133 -232 65 -266 65 -264 199 -200 265 -396 65 -134 165 -66 165 -66 65 -330 231 -300 431 -198 331 -266 99 -232 131 -432 65 -266 131 -100 265 -66 599 -498 97 -98 65 -298 197 -98 227 -328 231 -232 163 -660 197 -132 65 -134 363 -66 197 -166 631 -66 361 -132 231 -132 427 -328 99 -898 199 -332 365 -98 199 -162 197 -230 97 -166 561 -198 263 -196 295 -100 65 -428 65 -98 99 -166 199 -132 131 -100 165 -98 131 -66 1161 -66 167 -166 529 -364 163 -460 231 -196 291 -296 65 -66 97 -398 65 -100 133 -658 165 -164 195 -230 99 -132 229 -98 129 -430 691 -68 165 -100 167 -66 435 -100 597 -66 331 -98 99 -298 263 -262 131 -492 129 -788 97 -428 133 -100 65 -100 65 -98 131 -998 199 -98 235 -100 965 -232 793 -98 861 -100 199 -68 529 -398 195 -960 397 -100 131 -266 201 -66 65 -98 99 -132 233 -168 261 -66 131 -428 165 -66 261 -528 65 -66 195 -166 261 -66 361 -164 525 -200 697 -364 97 -66 131 -558 163 -266 65 -298 331 -464 199 -100 165 -100 97 -66 65 -994 331 -166 131 -534 199 -300 67 -98 329 -266 163 -66 227 -66 133 -66 261 -100 559 -466 231 -260 197 -68 227 -98 97 -96 65 -296 163 -296 131 -164 195 -230 263 -66 393 -230 457 -134 131 -534 133 -630 99 -98 399 -100 265 -266 431 -134 67 -100 789 -1584 165 -624 131 -134 263 -396 99 -692 165 -98 165 -398 295 -66 99 -592 163 -132 131 -328 265 -66 231 -100 363 -98 335 -66 167 -98 499 -100 397 -68 131 -100 231 -100 165 -134 165 -1228 99 -66 233 -1386 63 -924 99 -130 131 -392 231 -166 133 -166 165 -198 501 -66 265 -66 299 -98 299 -100 299 -134 231 -68 165 -232 99 -66 65 -200 165 -100\nRAW_Data: 67 -132 199 -302 65 -1058 199 -798 133 -334 165 -894 65 -132 233 -164 329 -130 493 -66 893 -200 631 -100 1207 -984 1013 -1010 1017 -988 997 -1010 1013 -986 1007 -1032 257 -254 253 -292 265 -266 501 -524 525 -520 261 -294 265 -306 241 -276 507 -522 261 -256 521 -524 521 -518 261 -290 495 -558 497 -556 265 -264 267 -268 265 -264 497 -522 261 -292 261 -268 269 -274 273 -274 275 -276 273 -270 231 -296 265 -264 497 -522 527 -520 261 -294 267 -304 241 -276 273 -270 495 -520 261 -256 293 -264 269 -270 507 -556 265 -266 271 -274 507 -522 521 -516 517 -516 259 -256 291 -266 271 -272 273 -274 275 -276 507 -522 259 -256 259 -296 265 -268 269 -270 271 -270 273 -274 275 -276 507 -522 521 -514 519 -516 259 -254 291 -266 269 -274 511 -558 267 -266 991 -1010 1009 -1000 999 -974 1023 -1012 1009 -990 1007 -1032 225 -284 253 -292 263 -264 501 -524 525 -518 263 -296 265 -306 241 -276 507 -522 261 -256 521 -520 523 -518 261 -290 497 -556 499 -554 265 -266 1535 -556 1321 -340 201 -168 1009 -246 209 -304 1249 -320 489 -196 1129 -338 209 -316 1011 -562 197 -260 539 -66 219 -194 815 -260 515 -132 247 -144 825 -260 815 -272 1301 -262 519 -66 215 -230 533 -132 217 -178 1049 -134 213 -178 347 -208 507 -134 247 -142 511 -192 213 -94 519 -274 521 -278 793 -278 815 -278 521 -132 229 -154 503 -198 839 -228 1597 -262 259 -292 1553 -526 1593 -458 1103 -528 1345 -268 235 -298 1293 -260 523 -194 189 -122 525 -196 221 -94 805 -306 1041 -166 233 -144 493 -228 221 -72 1041 -238 253 -342 791 -314 375 -162 823 -256 1019 -166 213 -154 507 -194 213 -124 815 -266 505 -196 221 -122 407 -166 391 -134 983 -262 219 -312 1213 -360 479 -166 1191 -322 223 -262 1043 -560 229 -166 1035 -206 455 -330 1007 -332 469 -198 837 -196 251 -92 471 -264 519 -330 439 -100 419 -132 421 -132 439 -100 453 -230 549 -330 739 -332 769 -302 347 -210 1025 -98 285 -180 603 -416 619 -404 757 -340 305 -226 463 -134 267 -196 313 -192 1175 -512 1563 -522 1279 -288 253 -290 1553 -524 1579 -492 1075 -528 655 -306 343 -188 373 -164 647 -410 457 -170 615 -344 767 -308 469 -100 275 -178 361 -174 621 -416 625 -418 355 -204 593 -446 583 -466 289 -224 355 -202 327 -206 463 -168 287 -106 399 -134 753 -338 757 -326 283 -230 331 -204 1027 -66 277 -210 487 -100 1219 -324 249 -220 1093 -558 229 -132 995 -282 449 -264 1089 -304 475 -132 1017 -446 483 -68 1235 -338 211 -252 515 -102 243 -176\nRAW_Data: 759 -296 497 -132 213 -214 333 -202 329 -204 331 -240 301 -206 369 -170 595 -442 609 -406 605 -440 347 -194 515 -164 211 -154 1041 -198 193 -180 1503 -516 1361 -258 291 -232 1083 -540 1557 -488 1303 -294 253 -288 1549 -524 393 -172 343 -174 533 -132 617 -264 629 -444 479 -200 217 -144 421 -106 1049 -174 243 -140 1025 -172 855 -166 229 -122 801 -314 1045 -134 239 -178 493 -164 217 -124 1047 -164 221 -122 525 -228 225 -100 457 -66 1041 -226 833 -196 1891 -298 523 -302 411 -100 1011 -290 477 -238 1123 -298 491 -66 1033 -478 1797 -320 225 -162 1001 -242 455 -300 1035 -296 231 -332 767 -302 345 -188 369 -194 567 -452 473 -172 233 -160 471 -208 279 -70 483 -342 605 -432 1511 -172 501 -352 1255 -526 1545 -514 1329 -298 263 -266 1063 -538 1553 -488 615 -310 385 -170 391 -136 621 -404 647 -410 389 -160 405 -132 997 -134 279 -178 987 -134 893 -166 241 -154 409 -130 667 -384 477 -200 613 -308 747 -306 483 -134 283 -146 377 -156 779 -298 795 -304 311 -188 797 -302 505 -502 815 -268 301 -240 305 -240 305 -242 271 -236 561 -470 777 -292 283 -260 1285 -298 267 -274 1573 -520 1483 -70 237 -322 219 -126 991 -314 449 -264 1045 -330 235 -304 1307 -340 209 -244 985 -100 475 -430 603 -426 347 -188 375 -176 513 -168 605 -288 367 -170 1365 -268 1527 -516 1471 -138 497 -386 1233 -486 1571 -514 1339 -262 291 -264 303 -244 821 -208 555 -496 543 -464 545 -98 249 -142 417 -140 1043 -140 241 -166 597 -424 813 -280 519 -100 675 -268 601 -442 515 -132 251 -144 367 -168 1053 -132 215 -178 387 -140 513 -206 211 -108 513 -238 1305 -188 867 -190 213 -94 1043 -280 1013 -276 2453 -282 1009 -262 787 -260 515 -260 1055 -232 225 -62 471 -100 489 -292 491 -196 1095 -298 233 -302 1329 -340 245 -176 1017 -136 459 -356 1043 -198 235 -320 1559 -550 1509 -206 253 -284 1277 -296 231 -298 1365 -276 1527 -528 1473 -100 505 -416 1195 -524 553 -302 739 -334 469 -196 251 -92 499 -234 263 -310 677 -324 1031 -264 165 -132 471 -568 201 -338 473 -558 499 -554 231 -300 233 -302 231 -300 497 -522 261 -292 229 -298 271 -306 239 -306 241 -274 273 -270 233 -298 231 -298 497 -556 493 -518 263 -294 265 -304 241 -276 273 -270 497 -520 261 -290 225 -298 267 -270 505 -558 233 -298 271 -274 505 -522 491 -550 487 -546 227 -286 259 -296 301 -114218 231 -100 97 -402 397 -298 99 -100 363 -164 201 -132 699 -100 1193 -100 199 -100 167 -66 429 -200 99 -1060 99 -400 99 -332 65 -168\nRAW_Data: 199 -232 199 -100 99 -630 67 -962 231 -198 65 -366 427 -100 231 -102 299 -100 299 -66 593 -132 63 -98 457 -426 165 -724 261 -98 163 -164 263 -98 99 -230 195 -862 65 -132 133 -498 131 -266 65 -66 197 -298 663 -298 829 -132 299 -460 197 -930 99 -66 399 -132 99 -960 165 -134 231 -132 99 -66 395 -726 67 -132 99 -132 231 -132 597 -98 593 -66 133 -198 1131 -98 267 -298 97 -724 525 -196 623 -132 199 -98 295 -98 65 -98 97 -66 197 -264 497 -98 163 -134 233 -100 297 -198 131 -568 297 -100 99 -66 99 -98 465 -100 97 -100 199 -398 1051 -1046 951 -1046 967 -1018 1009 -1004 1003 -1012 993 -1046 237 -288 465 -554 245 -290 253 -286 247 -276 509 -558 497 -554 497 -522 523 -518 259 -254 519 -520 263 -292 263 -266 269 -306 239 -308 241 -274 273 -270 499 -520 487 -550 261 -288 493 -554 231 -296 267 -306 241 -276 271 -270 233 -298 495 -522 261 -294 493 -556 497 -554 495 -554 231 -296 265 -266 497 -524 261 -290 227 -298 267 -268 505 -558 231 -332 239 -272 507 -522 521 -518 487 -550 259 -286 227 -296 271 -304 241 -308 241 -274 507 -522 261 -258 259 -296 265 -266 269 -270 271 -272 271 -308 241 -274 507 -522 489 -548 259 -256 259 -296 499 -558 229 -298 269 -306 241 -274 999 -1012 981 -1026 975 -1024 1011 -980 995 -1012 1019 -1012 261 -274 501 -514 259 -282 253 -292 265 -268 507 -556 497 -556 497 -522 523 -520 227 -286 519 -520 261 -292 261 -266 271 -308 239 -306 241 -274 273 -270 497 -520 489 -550 261 -288 493 -556 231 -294 267 -304 241 -274 273 -272 233 -298 495 -520 261 -292 495 -558 493 -556 493 -552 231 -298 265 -268 499 -522 261 -290 227 -296 267 -270 505 -560 231 -298 269 -274 505 -524 489 -552 487 -550 227 -318 225 -298 269 -304 241 -306 241 -276 507 -522 261 -256 259 -296 265 -268 269 -270 271 -304 239 -308 241 -274 507 -524 487 -550 259 -254 259 -296 499 -558 231 -298 267 -304 241 -276 999 -1014 979 -1016 1005 -988 1007 -1014 1001 -1014 993 -1012 273 -288 465 -552 247 -290 251 -286 247 -278 507 -556 495 -556 497 -522 527 -518 227 -286 517 -522 261 -292 229 -300 269 -304 241 -306 241 -276 273 -270 497 -520 487 -550 261 -290 491 -556 231 -296 267 -304 241 -274 273 -270 233 -298 495 -522 261 -292 495 -556 497 -552 495 -554 231 -296 265 -268 499 -522 259 -292 227 -296 267 -268 505 -560 231 -300 271 -274 505 -524 489 -550 485 -550 227 -318 225 -298 269 -304 241 -308 241 -274\nRAW_Data: 507 -522 261 -258 259 -296 231 -302 267 -270 271 -304 239 -308 241 -276 505 -524 487 -550 227 -288 257 -296 499 -558 229 -298 269 -304 241 -276 997 -1012 981 -1026 977 -1026 977 -1014 1001 -1014 993 -1046 235 -288 495 -528 239 -326 219 -290 253 -288 485 -564 501 -556 497 -522 493 -552 227 -286 515 -522 261 -292 231 -300 269 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -290 489 -556 231 -296 267 -306 239 -276 273 -270 233 -298 497 -520 261 -292 493 -558 495 -554 495 -552 231 -298 233 -300 497 -522 261 -290 227 -296 267 -270 505 -558 231 -334 237 -272 507 -522 491 -552 485 -552 227 -318 225 -296 269 -304 239 -308 241 -276 505 -524 261 -290 227 -294 231 -302 269 -268 271 -304 239 -308 241 -276 507 -524 487 -552 227 -286 259 -294 501 -556 229 -298 269 -306 241 -276 997 -1010 1003 -986 1007 -998 1009 -1008 1001 -986 1001 -1048 227 -276 503 -550 259 -254 257 -298 267 -270 505 -558 497 -554 497 -524 521 -518 229 -286 517 -522 263 -290 263 -266 269 -306 241 -306 241 -274 273 -270 497 -520 521 -516 259 -290 493 -556 231 -296 269 -304 241 -274 273 -270 233 -298 495 -524 261 -290 493 -558 495 -552 497 -552 231 -298 265 -266 497 -522 259 -292 227 -298 267 -270 505 -558 231 -300 269 -274 505 -524 489 -550 487 -550 229 -286 257 -296 269 -304 241 -308 241 -276 507 -520 261 -258 259 -294 267 -266 269 -270 269 -306 239 -306 241 -276 507 -524 489 -548 259 -256 257 -294 501 -558 231 -296 269 -306 239 -276 999 -1012 979 -1028 975 -1024 1007 -1000 1001 -1000 1007 -1012 259 -280 507 -518 259 -290 225 -298 267 -270 503 -558 497 -554 497 -524 525 -518 227 -286 517 -522 261 -292 263 -266 269 -306 239 -308 241 -276 271 -270 499 -520 487 -550 259 -288 493 -556 229 -298 267 -304 241 -274 273 -270 235 -298 495 -520 261 -292 495 -556 495 -556 493 -554 231 -298 233 -300 497 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -522 491 -550 485 -550 227 -318 227 -296 271 -304 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 267 -270 271 -304 239 -308 241 -274 507 -524 489 -550 227 -286 257 -296 499 -558 231 -298 267 -304 241 -274 999 -1010 1001 -1004 983 -1004 1011 -1008 1003 -998 1003 -1018 257 -282 509 -518 259 -290 225 -298 267 -270 503 -560 497 -558 497 -520 521 -520 227 -286 517 -522 259 -292 263 -266 271 -304 239 -308 241 -276 273 -268 499 -522 487 -550 259 -288 491 -556 229 -298 267 -304 241 -274\nRAW_Data: 273 -272 233 -296 495 -522 261 -294 493 -556 493 -554 495 -556 229 -298 265 -266 499 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -548 489 -550 227 -316 227 -296 269 -306 239 -308 241 -274 507 -524 259 -258 259 -294 265 -266 269 -270 271 -304 239 -308 239 -276 507 -524 489 -550 227 -286 257 -294 501 -556 231 -296 269 -306 239 -276 999 -1012 981 -1018 1005 -980 1009 -1018 1003 -1008 977 -1038 255 -276 499 -516 259 -282 253 -290 265 -268 505 -556 499 -554 497 -520 525 -518 227 -288 517 -522 261 -292 229 -300 269 -304 239 -308 241 -276 273 -268 497 -522 489 -550 261 -288 489 -556 229 -296 267 -304 241 -276 271 -270 233 -300 495 -522 261 -292 495 -556 495 -552 495 -554 229 -298 231 -300 499 -522 261 -290 227 -296 267 -268 507 -558 231 -298 271 -272 505 -524 489 -552 487 -550 227 -318 225 -296 267 -304 239 -308 241 -274 507 -524 261 -290 227 -296 231 -300 269 -270 269 -306 239 -306 241 -274 505 -524 489 -550 227 -320 225 -296 499 -556 231 -298 267 -306 241 -274 999 -1012 979 -1016 1005 -982 1009 -1014 1003 -1010 979 -1044 237 -292 473 -552 247 -284 245 -276 277 -276 501 -546 515 -548 485 -550 487 -548 225 -286 515 -520 263 -290 263 -266 269 -306 239 -306 243 -274 273 -270 495 -520 491 -548 261 -288 491 -554 231 -296 267 -304 241 -276 273 -270 233 -298 495 -522 261 -290 495 -556 495 -520 527 -554 229 -298 267 -268 497 -522 259 -258 259 -296 269 -270 505 -558 231 -300 269 -274 507 -522 521 -514 489 -548 259 -256 289 -264 271 -272 273 -308 241 -276 505 -522 259 -258 257 -296 267 -266 271 -270 269 -272 273 -306 241 -274 507 -522 521 -516 259 -254 259 -296 501 -556 231 -296 269 -306 241 -276 1001 -1010 979 -1016 1003 -986 1009 -1014 1001 -1006 1011 -1006 253 -276 497 -516 259 -282 255 -292 267 -270 507 -556 497 -554 499 -522 523 -518 259 -254 517 -522 261 -292 265 -266 269 -272 273 -308 243 -274 273 -268 497 -522 521 -516 259 -288 491 -554 231 -298 267 -306 241 -276 273 -270 231 -298 493 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -268 497 -520 261 -290 229 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -550 487 -548 259 -252 291 -264 271 -272 273 -308 241 -274 507 -520 261 -258 259 -296 265 -266 269 -270 271 -270 273 -308 241 -276 505 -524 487 -548 259 -256 255 -298 501 -556 231 -298 267 -306 241 -276 999 -1004 1007 -988 1009 -998 1011 -1012 991 -982 1025 -1010\nRAW_Data: 269 -290 465 -554 247 -290 251 -286 247 -278 505 -556 497 -554 497 -522 525 -516 227 -286 519 -522 261 -292 261 -266 271 -304 239 -308 241 -276 273 -270 497 -520 489 -550 259 -256 521 -556 231 -296 269 -306 241 -274 273 -270 233 -296 495 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -266 499 -518 261 -290 229 -298 267 -268 507 -556 233 -298 271 -272 507 -526 487 -550 485 -548 259 -254 291 -264 271 -272 275 -308 241 -274 505 -522 259 -258 259 -296 265 -268 267 -270 271 -272 273 -274 273 -276 507 -520 521 -516 259 -254 291 -262 501 -556 231 -298 269 -306 241 -276 1001 -1010 979 -1018 1007 -982 1009 -1016 999 -984 1019 -1008 257 -274 499 -548 257 -254 257 -296 267 -270 505 -558 497 -554 499 -520 523 -516 259 -256 517 -524 261 -290 263 -266 271 -270 275 -274 275 -274 273 -268 499 -518 519 -516 261 -288 493 -524 263 -296 269 -270 275 -276 271 -270 233 -298 495 -518 263 -292 493 -524 529 -522 527 -522 265 -294 265 -266 497 -520 261 -258 293 -264 267 -270 507 -556 267 -264 271 -274 507 -522 523 -516 485 -548 259 -254 291 -264 271 -272 275 -274 275 -276 507 -520 259 -256 257 -296 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 523 -514 257 -256 289 -264 503 -524 263 -296 269 -272 275 -276 999 -1004 1001 -998 1019 -972 1007 -1006 1001 -996 1009 -1020 257 -280 511 -520 259 -256 293 -262 269 -272 505 -556 497 -554 497 -522 523 -518 259 -252 517 -522 261 -292 263 -266 273 -272 275 -272 277 -274 271 -236 529 -522 521 -514 261 -256 523 -522 297 -264 269 -270 275 -276 273 -236 265 -264 529 -520 261 -290 497 -522 527 -520 529 -552 265 -264 267 -234 531 -520 259 -258 291 -264 269 -272 507 -554 265 -266 271 -274 507 -522 519 -514 521 -514 259 -256 291 -266 271 -272 275 -274 275 -274 507 -522 259 -256 289 -264 265 -270 269 -270 269 -272 273 -274 275 -276 505 -522 519 -514 259 -254 291 -264 501 -522 299 -264 269 -272 277 -276 999 -978 1045 -958 1011 -1024 1007 -978 1019 -978 1013 -1008 271 -292 503 -490 283 -288 249 -278 273 -268 501 -522 529 -520 531 -520 525 -516 257 -254 519 -520 263 -292 265 -266 271 -274 273 -274 275 -276 269 -236 529 -520 521 -516 259 -256 523 -522 297 -264 269 -272 277 -274 273 -236 265 -262 527 -522 259 -292 495 -524 529 -520 529 -518 297 -264 267 -234 531 -520 259 -256 293 -264 269 -270 505 -556 267 -264 271 -274 507 -520 521 -518 517 -514 259 -256 293 -266 271 -270\nRAW_Data: 275 -306 241 -276 507 -520 259 -258 257 -296 267 -266 269 -268 273 -272 273 -274 275 -274 505 -524 521 -514 259 -254 289 -264 501 -556 231 -298 269 -270 277 -276 997 -980 1011 -996 1009 -990 1045 -980 999 -1010 1015 -1008 271 -256 501 -536 251 -272 245 -276 277 -286 499 -558 499 -554 497 -522 523 -516 259 -254 517 -522 261 -292 265 -266 271 -272 273 -274 275 -274 271 -236 531 -520 521 -516 259 -256 523 -524 263 -296 269 -270 277 -276 273 -236 265 -262 527 -520 263 -292 495 -556 497 -518 527 -552 263 -266 267 -268 497 -520 261 -258 291 -264 269 -268 507 -554 267 -264 271 -276 507 -522 521 -514 519 -516 259 -254 293 -264 271 -272 273 -274 275 -274 507 -522 259 -256 291 -262 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 521 -516 259 -254 289 -264 501 -526 263 -298 267 -272 275 -276 999 -1004 1003 -986 1007 -1000 1013 -1010 985 -1014 1015 -1006 275 -254 501 -524 283 -256 251 -284 275 -272 501 -554 499 -554 497 -520 525 -516 259 -254 517 -520 261 -292 263 -268 271 -274 273 -274 275 -276 271 -236 529 -520 521 -516 259 -256 523 -524 295 -264 269 -272 277 -274 273 -270 231 -264 527 -522 263 -258 527 -522 527 -520 529 -552 265 -266 265 -268 497 -520 259 -258 293 -262 269 -272 507 -556 265 -264 271 -276 507 -520 521 -516 519 -514 259 -254 293 -264 271 -272 277 -274 275 -274 507 -522 259 -256 257 -294 267 -268 269 -270 271 -272 273 -274 275 -274 507 -522 519 -516 259 -254 289 -264 503 -522 263 -298 267 -272 277 -276 999 -1008 1009 -984 1009 -984 1005 -1012 999 -1012 1013 -1004 255 -274 499 -544 257 -252 289 -262 269 -270 507 -556 497 -552 497 -520 525 -518 259 -254 519 -520 261 -294 263 -266 271 -272 275 -274 275 -274 273 -234 531 -520 519 -516 261 -256 523 -522 263 -298 269 -272 275 -276 273 -270 231 -264 527 -522 261 -292 493 -524 359 -91592 331 -232 65 -2392 131 -496 329 -2040 231 -66 201 -200 133 -920 65 -232 97 -100 491 -100 195 -198 131 -196 295 -98 197 -66 655 -166 131 -428 99 -296 65 -330 163 -66 163 -98 97 -66 99 -1778 165 -66 195 -854 129 -494 297 -98 97 -66 197 -260 261 -262 165 -132 293 -66 423 -166 9931 -462 67 -762 99 -268 199 -862 65 -760 165 -232 131 -434 133 -1126 363 -132 165 -134 67 -332 97 -466 97 -66 265 -100 133 -100 231 -1590 65 -334 199 -98 67 -1592 201 -100 131 -66 165 -164 97 -1714 327 -66 329 -98 595 -100 993 -132 65 -100 165 -232 65 -100\nRAW_Data: 97 -100 293 -330 99 -98 163 -458 99 -196 131 -66 163 -426 167 -298 99 -462 99 -66 67 -462 229 -66 195 -100 757 -66 425 -66 427 -100 529 -134 265 -1430 131 -302 97 -66 97 -132 65 -66 229 -100 97 -1530 133 -264 563 -66 1165 -200 1025 -198 167 -1294 265 -132 197 -962 131 -300 131 -66 99 -366 165 -198 167 -830 197 -230 195 -434 299 -98 965 -132 265 -894 99 -98 65 -860 461 -66 97 -398 67 -928 165 -200 199 -66 199 -1456 197 -334 133 -132 501 -198 1579 -66 1151 -132 463 -98 10275 -528 97 -966 263 -760 65 -264 197 -134 65 -512 479 -526 455 -572 443 -572 437 -550 451 -548 447 -552 481 -526 457 -542 483 -538 469 -548 449 -548 447 -554 449 -1048 975 -558 463 -548 451 -1046 449 -554 953 -1054 981 -540 443 -550 475 -1014 985 -1052 981 -516 475 -1048 473 -520 487 -534 463 -548 943 -1044 969 -554 449 -558 465 -1042 979 -520 485 -514 473 -548 453 -1042 973 -1048 981 -1014 483 -546 971 -522 473 -556 445 -1032 493 -51306 533 -514 457 -546 451 -550 477 -522 475 -542 471 -554 453 -538 467 -548 451 -546 447 -550 477 -524 473 -554 451 -540 469 -1044 977 -554 451 -546 441 -1042 483 -514 975 -1048 977 -522 493 -516 481 -1046 977 -1018 979 -552 453 -1042 481 -520 485 -516 471 -550 983 -1014 981 -546 477 -518 489 -1012 975 -554 451 -544 467 -550 451 -1042 975 -1050 949 -1046 483 -548 941 -554 483 -514 469 -1046 447 -51364 513 -518 451 -554 451 -544 469 -548 451 -546 481 -514 475 -522 475 -554 443 -572 443 -548 453 -546 479 -514 477 -554 447 -1050 975 -522 493 -512 483 -1040 471 -538 969 -1032 985 -534 467 -1046 977 -528 485 -1016 965 -1042 481 -524 489 -522 973 -524 493 -514 481 -1040 969 -1050 979 -1016 481 -514 475 -552 977 -522 479 -1028 493 -512 481 -546 975 -520 485 -514 473 -548 453 -544 481 -1016 981 -51834 519 -526 465 -514 481 -546 479 -514 473 -522 471 -558 487 -514 469 -548 451 -546 479 -514 475 -522 471 -538 471 -536 485 -1048 961 -546 445 -552 483 -1016 479 -536 965 -1042 977 -534 475 -1042 465 -548 979 -1014 485 -516 977 -1042 479 -514 1005 -1018 975 -1018 483 -544 975 -520 473 -1058 471 -516 975 -548 453 -546 479 -516 479 -522 471 -1052 985 -520 489 -1006 1005 -528 459 -1050 479 -518 489 -51332 483 -542 453 -546 445 -550 477 -522 483 -554 449 -554 461 -514 483 -548 445 -552 441 -588 447 -548 441 -550 455 -548 481 -1042 967 -540 441 -552 481 -1024 489 -514 975 -1034 971 -1050 973 -548 451 -548 447 -1048 485 -540\nRAW_Data: 453 -542 467 -548 945 -1048 981 -544 449 -1056 453 -546 481 -516 973 -554 449 -544 469 -1046 975 -526 487 -508 485 -1038 489 -512 483 -516 479 -522 1001 -508 483 -1036 983 -514 475 -51832 491 -574 411 -604 399 -618 389 -618 385 -622 383 -610 415 -578 411 -602 437 -552 455 -550 449 -554 445 -584 445 -546 441 -1046 981 -522 485 -546 447 -1054 451 -546 973 -1052 953 -1046 965 -1042 977 -556 451 -542 467 -514 483 -1040 969 -1050 479 -532 961 -546 445 -552 481 -520 485 -516 473 -548 453 -546 481 -516 481 -522 475 -1046 481 -532 961 -1044 481 -530 461 -554 449 -524 987 -1040 969 -1052 449 -51326 525 -532 465 -548 451 -548 445 -552 443 -586 431 -546 477 -524 491 -514 481 -546 445 -552 441 -554 485 -516 473 -548 487 -1010 977 -554 451 -540 469 -1042 475 -508 1007 -1034 967 -1042 477 -518 983 -516 479 -524 487 -536 473 -1018 481 -526 987 -540 479 -516 475 -524 485 -540 453 -544 469 -1044 975 -526 483 -1014 505 -518 981 -1010 473 -556 485 -514 469 -548 451 -546 971 -542 483 -520 485 -1010 493 -512 975 -51842 491 -606 339 -662 333 -652 357 -654 385 -628 357 -642 377 -604 403 -622 391 -584 417 -590 421 -614 409 -574 439 -554 421 -1114 909 -578 415 -578 449 -1094 423 -550 975 -1034 969 -1038 477 -522 493 -514 975 -554 483 -1012 967 -1048 977 -518 481 -1028 987 -510 479 -1034 1005 -520 485 -534 465 -1014 1007 -492 517 -1012 999 -514 483 -1016 487 -520 975 -1060 949 -1050 457 -550 479 -530 467 -51296 517 -552 451 -578 421 -570 447 -572 407 -592 427 -584 417 -586 417 -578 415 -576 445 -576 447 -546 447 -568 465 -550 453 -1046 977 -510 467 -542 479 -1050 449 -542 973 -1052 483 -518 973 -514 483 -546 447 -552 483 -520 471 -1036 471 -516 1011 -1012 487 -506 1017 -510 471 -548 451 -1042 475 -510 483 -542 487 -506 983 -1036 987 -1046 975 -1020 979 -516 483 -1042 973 -1028 997 -512 471 -550 487 -51298 65 -1910 229 -230 99 -66 165 -922 97 -164 197 -66 131 -426 65 -68 231 -264 95 -66 361 -196 1023 -66 227 -98 195 -66 229 -98 4711 -100 233 -98 99 -66 1025 -66 10443 -130 233 -168 465 -66 2057 -100 6537 -66 821 -132 1841 -98 165 -98 7675 -66 1343 -232 67 -132 131 -134 329 -100 365 -132 3561 -66 297 -134 1691 -100 861 -66 497 -66 1359 -66 197 -198 429 -66 929 -66 2675 -100 261 -98 99 -98 459 -166 197 -66 195 -130 625 -98 295 -132 455 -66 391 -130 197 -132 5171 -66 1597 -66 1085 -68 1025 -66 565 -100 131 -132 4741 -66 559 -66 2343 -98\nRAW_Data: 4573 -66 2455 -98 2333 -130 5301 -66 531 -66 1895 -66 163 -68 5167 -98 591 -100 1521 -166 7219 -100 2149 -100 1443 -98 1149 -66 9647 -66 5157 -100 1321 -164 917 -100 2083 -132 7551 -134 165 -68 1169 -100 1263 -68 3619 -166 963 -100 1229 -198 2393 -68 1661 -98 2109 -66 131 -68 299 -100 5029 -66 957 -100 1185 -66 1653 -98 4925 -66 1201 -66 1827 -96 163 -132 501 -66 165 -66 763 -100 163 -98 821 -66 4555 -264 1353 -132 499 -100 163 -66 1749 -100 1063 -132 1577 -100 763 -68 331 -102 299 -100 429 -66 265 -234 331 -66 1963 -68 627 -132 231 -102 763 -66 497 -66 265 -68 2061 -68 463 -66 297 -100 1393 -430 295 -100 823 -98 97 -100 631 -102 529 -66 1251 -68 1195 -66 361 -66 195 -100 4929 -134 231 -100 561 -66 829 -68 893 -66 6729 -66 901 -68 1259 -66 65 -166 131 -232 65 -100 131 -132 131 -100 131 -564 661 -98 329 -66 199 -66 263 -66 165 -68 97 -68 363 -566 197 -1064 395 -862 133 -200 99 -198 199 -596 229 -994 295 -928 163 -130 395 -98 229 -66 99 -98 997 -66 133 -566 165 -232 99 -100 99 -100 99 -530 99 -66 133 -66 165 -764 99 -398 199 -296 165 -96 129 -328 131 -560 227 -298 99 -100 465 -100 397 -66 67 -98 331 -132 199 -232 201 -438 263 -166 329 -166 463 -632 131 -398 99 -134 131 -1590 263 -66 67 -196 163 -298 195 -1220 227 -292 297 -132 591 -298 295 -100 493 -66 163 -162 325 -134 197 -64 197 -164 97 -460 65 -166 195 -266 97 -164 327 -264 229 -234 165 -66 163 -630 299 -532 197 -796 131 -100 65 -466 65 -98 99 -328 361 -66 197 -130 295 -66 197 -692 99 -166 99 -166 99 -66 131 -100 99 -66 165 -134 233 -130 267 -498 265 -66 167 -1192 67 -364 463 -132 133 -300 197 -100 99 -298 629 -66 331 -132 629 -134 65 -132 231 -100 65 -1392 65 -166 131 -230 199 -466 97 -100 233 -730 263 -296 99 -98 133 -100 163 -134 67 -100 131 -98 229 -100 231 -132 497 -264 989 -164 233 -98 65 -102 495 -264 197 -826 99 -496 133 -132 365 -64 131 -66 99 -98 65 -132 65 -100 329 -66 165 -100 131 -700 165 -492 231 -130 195 -130 131 -230 197 -200 691 -132 99 -66 331 -264 165 -100 131 -730 131 -200 131 -498 99 -100 197 -100 131 -66 165 -68 165 -534 65 -498 401 -166 65 -592 65 -98 65 -100 131 -66 231 -132 133 -100 299 -198 133 -164 265 -100 433 -66 429 -330 631 -98 163 -66 265 -232 65 -66 199 -100 165 -198 233 -66 131 -234 67 -564\nRAW_Data: 163 -400 165 -464 131 -362 561 -332 263 -98 133 -132 165 -100 661 -232 591 -328 65 -98 133 -368 131 -560 97 -100 165 -464 131 -296 129 -558 131 -98 99 -196 65 -98 163 -100 97 -394 261 -558 259 -98 97 -426 299 -364 365 -66 599 -166 363 -428 231 -134 625 -98 359 -394 195 -164 197 -66 97 -98 263 -490 195 -98 261 -460 163 -100 199 -330 131 -198 163 -66 293 -66 885 -100 1315 -234 333 -234 365 -200 133 -596 197 -166 295 -100 197 -232 165 -66 131 -336 197 -268 231 -100 99 -696 163 -262 331 -132 197 -132 131 -132 361 -100 525 -66 195 -132 227 -232 163 -262 1233 -68 797 -98 427 -98 231 -200 331 -132 129 -98 163 -66 129 -100 97 -100 5243 -198 1295 -66 197 -66 165 -66 499 -23298 65 -68 131 -564 329 -100 133 -298 101 -166 199 -300 65 -764 133 -266 297 -130 165 -132 265 -66 2793 -198 4161 -66 1747 -100 1729 -98 603 -132 363 -530 99 -266 129 -100 231 -396 129 -198 133 -98 991 -132 397 -462 131 -330 295 -132 197 -134 99 -262 99 -232 229 -268 263 -1448 229 -68 65 -198 99 -134 165 -996 429 -396 631 -632 727 -98 297 -100 199 -134 65 -332 65 -234 165 -362 99 -428 263 -232 67 -1158 165 -196 129 -228 131 -132 195 -98 163 -698 65 -234 531 -728 295 -130 97 -98 229 -366 499 -66 99 -66 131 -66 297 -232 165 -100 65 -400 233 -66 265 -134 131 -134 165 -132 233 -134 97 -300 397 -102 65 -98 99 -98 6595 -132 65 -164 131 -100 529 -66 2157 -66 163 -100 99 -466 99 -530 163 -166 195 -264 197 -66 199 -66 199 -100 11433 -164 1055 -562 497 -134 233 -100 399 -100 131 -66 163 -234 733 -264 163 -236 65 -430 67 -332 133 -132 133 -66 465 -66 603 -98 265 -98 199 -66 3567 -100 391 -166 525 -132 1055 -98 627 -264 333 -100 795 -66 165 -66 297 -66 393 -134 263 -100 1317 -98 131 -98 529 -98 293 -66 99 -66 163 -100 493 -64 265 -164 231 -100 199 -132 265 -66 265 -100 99 -134 199 -132 431 -100 365 -132 231 -68 201 -132 891 -100 831 -98 167 -100 1191 -64 329 -66 393 -98 131 -64 131 -98 229 -132 2693 -132 10455 -66 297 -98 631 -100 431 -100 433 -98 165 -134 65 -100 65 -68 863 -132 499 -100 267 -134 99 -66 989 -66 657 -100 757 -594 395 -66 199 -66 463 -330 663 -264 4219 -66 851 -98 129 -132 915 -66 129 -66 363 -98 99 -100 699 -66 4847 -66 2153 -134 1993 -66 5935 -66 1435 -100 2119 -66 9535 -296 197 -624 131 -1446 129 -362 65 -198\nRAW_Data: 67 -298 197 -226 131 -164 595 -66 463 -98 131 -198 395 -262 131 -66 1773 -516 455 -548 481 -516 479 -556 451 -554 451 -542 469 -548 451 -546 447 -552 449 -554 471 -542 477 -518 489 -512 481 -1044 977 -506 485 -540 469 -1044 481 -518 975 -1050 947 -548 489 -512 481 -1042 967 -1020 979 -550 455 -1038 477 -520 471 -556 483 -514 969 -1044 977 -536 473 -544 449 -1054 955 -552 447 -554 485 -514 471 -1046 975 -1020 485 -518 1003 -512 483 -548 447 -554 447 -556 485 -1010 491 -51312 527 -512 453 -544 477 -540 477 -514 473 -538 473 -524 471 -558 453 -542 467 -514 485 -546 479 -514 473 -522 471 -534 501 -1014 1001 -512 481 -514 479 -1052 459 -544 979 -1016 1009 -516 473 -522 469 -1044 995 -1010 1007 -524 475 -1004 495 -512 483 -544 479 -514 1003 -1018 977 -522 489 -512 479 -1034 1003 -522 487 -512 467 -514 483 -1044 975 -1020 485 -524 991 -514 477 -550 473 -524 473 -536 471 -1030 489 -51306 517 -520 493 -512 483 -546 445 -550 477 -520 469 -544 479 -518 495 -512 481 -514 475 -550 477 -526 471 -538 473 -538 471 -1018 979 -548 451 -546 479 -1010 473 -554 965 -1034 989 -516 479 -1048 967 -554 451 -1022 977 -1034 475 -554 473 -510 1003 -512 481 -546 443 -1064 969 -1034 977 -1020 483 -546 477 -516 967 -538 473 -1052 449 -562 461 -514 481 -546 477 -514 477 -522 999 -510 485 -1038 985 -51816 479 -3892 63 -722 289 -742 265 -734 271 -762 231 -722 291 -720 321 -644 373 -642 339 -626 395 -1114 911 -610 415 -580 413 -1098 425 -548 979 -1054 955 -544 443 -1080 449 -548 939 -1054 473 -550 949 -1048 481 -514 973 -1050 975 -1050 453 -544 971 -538 473 -1032 471 -538 967 -554 449 -524 495 -512 481 -546 479 -1008 473 -556 487 -512 965 -546 481 -516 473 -1036 487 -522 483 -97226 691 -134 1391 -396 231 -166 1025 -98 199 -132 99 -266 395 -100 165 -132 231 -98 689 -66 493 -98 65 -98 557 -134 365 -68 131 -164 425 -132 259 -98 1255 -232 195 -66 495 -64 1651 -100 199 -100 1099 -100 331 -132 265 -168 6433 -66 28525 -100 395 -66 395 -100 259 -164 559 -100 463 -66 327 -66 131 -100 685 -66 395 -98 199 -100 431 -66 101 -100 231 -66 333 -66 399 -266 165 -68 99 -100 165 -398 299 -98 197 -824 97 -132 229 -66 295 -264 97 -230 131 -394 469 -266 397 -198 165 -270 133 -562 165 -66 229 -266 99 -232 363 -530 427 -730 99 -166 97 -534 297 -100 297 -134 133 -132 67 -100 67 -66 361 -430 397 -330 65 -198 165 -200 167 -68 131 -132 199 -68 233 -100\nRAW_Data: 99 -234 65 -300 263 -562 163 -66 199 -166 165 -132 99 -264 131 -262 261 -64 459 -66 427 -132 65 -132 131 -100 97 -228 163 -164 523 -328 297 -66 131 -332 199 -266 65 -134 165 -66 197 -68 697 -98 165 -134 233 -496 231 -164 63 -66 197 -264 297 -166 99 -198 131 -100 365 -266 299 -232 99 -66 627 -132 1415 -100 195 -198 165 -98 327 -98 523 -98 131 -228 229 -230 295 -694 499 -100 531 -66 131 -134 165 -300 165 -132 265 -300 997 -102 1921 -134 723 -98 195 -132 99 -164 163 -68 397 -100 331 -132 165 -266 467 -232 131 -66 465 -198 463 -132 165 -66 329 -100 2691 -100 431 -166 397 -100 199 -134 529 -100 1053 -66 18073 -98 1251 -130 1425 -66 497 -98 6625 -10518 1205 -578 607 -588 599 -612 597 -586 605 -582 587 -1214 1171 -1180 1207 -582 609 -588 613 -578 613 -592 611 -576 613 -1184 583 -580 1205 -1206 1169 -614 577 -590 629 -576 601 -1182 609 -598 1177 -578 627 -588 603 -1178 1207 -582 589 -1178 1209 -1178 613 -588 613 -576 1215 -1174 1181 -1204 1183 -604 579 -1218 1175 -604 579 -1186 1225 -562 615 -1184 611 -578 1207 -578 605 -1184 609 -568 1209 -580 629 -582 603 -574 605 -580 603 -612 577 -610 603 -77146 1139 -774 423 -1374 419 -716 495 -708 493 -682 1089 -1308 1095 -688 519 -638 539 -656 551 -638 539 -656 549 -638 575 -622 551 -1206 1179 -1204 585 -616 577 -620 1189 -594 581 -602 581 -1186 603 -614 1169 -1200 1207 -1176 613 -592 1181 -600 581 -1214 583 -580 611 -588 615 -576 615 -588 1173 -1212 579 -590 613 -590 587 -614 1169 -1212 579 -612 591 -580 1201 -588 615 -578 613 -1180 587 -616 1169 -1210 1201 -580 579 -622 591 -1170 1203 -612 575 -614 593 -77174 1201 -608 589 -1210 583 -622 549 -606 585 -626 1169 -1204 579 -608 1205 -578 601 -586 621 -582 589 -614 575 -1214 575 -620 563 -612 1193 -592 579 -1204 1183 -606 577 -1224 559 -614 1195 -588 611 -1182 581 -606 1205 -1176 611 -586 579 -628 1173 -1208 581 -604 1167 -616 571 -1216 1171 -618 585 -1210 1177 -1208 581 -586 1205 -580 589 -616 571 -1212 1171 -616 587 -1206 573 -594 615 -588 589 -614 1169 -618 583 -1212 1177 -578 609 -590 613 -578 611 -77168 1211 -612 565 -1242 549 -628 577 -620 583 -586 1173 -616 587 -612 587 -588 615 -578 611 -586 579 -628 579 -620 585 -1180 1197 -1194 1183 -600 583 -622 583 -1184 1193 -1180 1197 -604 577 -1214 585 -614 1169 -1210 1165 -614 577 -622 591 -580 603 -1182 609 -602 1173 -1204 577 -598 1203 -610 571 -616 575 -1202 581 -608 611 -592 577 -602 1203 -1172 611 -588 611 -578 611 -592\nRAW_Data: 1171 -622 591 -580 603 -1180 1207 -580 599 -1208 579 -622 1173 -612 575 -590 613 -123688 65 -3788 133 -896 65 -168 65 -926 297 -168 65 -100 365 -66 297 -98 201 -1162 131 -428 195 -66 297 -166 99 -760 99 -196 131 -132 65 -98 197 -66 493 -264 99 -264 821 -66 793 -100 3007 -98 2067 -66 2411 -430 97 -98 197 -130 297 -230 465 -98 4123 -100 391 -132 163 -66 751 -164 161 -990 759 -64 1055 -264 129 -166 429 -66 3943 -66 399 -100 665 -432 431 -132 99 -132 97 -68 97 -330 295 -164 131 -66 291 -66 589 -264 563 -100 1227 -166 233 -298 699 -100 863 -534 131 -332 1619 -100 1595 -132 2191 -164 465 -66 233 -68 299 -66 561 -134 369 -68 233 -134 297 -168 463 -166 795 -100 265 -68 131 -100 3629 -66 227 -66 853 -66 529 -100 1191 -98 501 -132 1417 -64 229 -132 1115 -66 463 -66 231 -64 361 -66 231 -98 989 -98 265 -300 1125 -66 897 -68 197 -100 529 -66 787 -164 163 -66 2705 -66 1899 -132 929 -64 1197 -132 4387 -98 263 -96 261 -100 751 -66 229 -10520 1245 -566 609 -564 615 -572 613 -590 615 -594 589 -1174 1203 -1204 1195 -564 615 -592 617 -580 611 -560 627 -576 603 -1186 609 -564 1213 -1176 1211 -578 623 -588 611 -1176 1205 -582 583 -582 613 -588 611 -578 625 -1152 1205 -592 615 -1176 1217 -1172 601 -576 605 -590 1193 -1186 1191 -1184 1227 -576 615 -1150 1209 -604 613 -1152 1227 -576 591 -1188 613 -578 1207 -576 607 -1184 607 -600 1179 -578 629 -588 607 -582 587 -1182 613 -586 1209 -77154 1187 -666 541 -1242 523 -676 535 -618 553 -644 1163 -1212 1169 -628 577 -620 551 -616 609 -588 589 -614 573 -586 597 -610 599 -1182 1209 -1182 581 -592 613 -604 1169 -1216 577 -604 1175 -1206 1175 -1208 1173 -1206 579 -596 1201 -610 571 -1212 605 -584 587 -610 575 -618 583 -612 1171 -1210 577 -588 613 -580 611 -590 1205 -1170 611 -578 611 -592 1171 -622 591 -578 603 -1182 611 -600 1177 -1206 1177 -610 597 -586 601 -1178 615 -590 589 -578 1205 -77194 1193 -618 583 -1208 575 -624 577 -606 583 -588 1205 -1178 611 -572 1201 -612 575 -588 615 -576 611 -590 579 -1206 585 -616 575 -622 1183 -600 577 -1216 1177 -604 581 -592 611 -608 583 -1178 1197 -1196 573 -616 1199 -1174 611 -586 589 -614 1167 -1214 575 -620 1185 -600 577 -1216 1177 -604 575 -1220 1159 -1222 585 -582 1181 -622 579 -592 611 -1200 1175 -588 585 -1208 605 -576 605 -620 561 -612 1189 -606 581 -1212 1177 -1208 581 -586 1207 -77162 599 -4312 397 -788 425 -772 1027 -722 471 -708 487 -686 503 -678 521 -686 503 -680 523 -652\nRAW_Data: 535 -1274 1109 -1240 1175 -614 559 -650 533 -1240 1177 -616 559 -1208 575 -628 577 -604 577 -620 1175 -1208 1171 -614 605 -578 601 -588 599 -1178 615 -588 1185 -1202 589 -614 1167 -618 583 -612 591 -1186 599 -584 601 -582 591 -618 1199 -1176 603 -580 591 -616 573 -616 1179 -624 581 -584 613 -1184 1173 -622 591 -1172 611 -592 579 -610 613 -588 1173 -122626 229 -330 197 -98 65 -98 97 -100 229 -166 129 -232 65 -66 131 -394 97 -66 97 -66 395 -66 557 -66 657 -64 489 -66 327 -100 65 -98 461 -64 5371 -64 691 -98 859 -66 631 -166 797 -66 299 -132 65 -66 4463 -134 331 -134 233 -132 1495 -264 699 -98 263 -134 1031 -692514 65 -100 97 -1392 131 -398 65 -134 201 -832 4061 -132 523 -66 431 -66 99 -132 99 -100 831 -66 165 -66 1969 -164 8553 -100 297 -68 523 -100 197 -130 957 -98 361 -98 2015 -98 263 -130 195 -130 4497 -66 999 -66 1357 -66 1189 -132 929 -66 4649 -66 263 -66 131 -64 657 -100 195 -68 231 -100 1025 -100 565 -68 931 -98 985 -98 661 -164 363 -100 501 -66 3719 -66 2015 -66 663 -132 299 -330 823 -66 1661 -66 4179 -66 433 -66 1523 -68 697 -68 197 -100 199 -66 1265 -98 629 -68 299 -168 629 -100 3539 -166 331 -66 495 -98 327 -132 589 -166 499 -66 463 -100 331 -66 627 -98 489 -64 129 -130 361 -98 195 -130 65 -100 793 -98 391 -66 1515 -66 129 -66 757 -132 561 -98 653 -64 197 -98 1577 -296 163 -100 789 -66 689 -66 3829 -100 533 -398 467 -166 531 -66 331 -198 753 -64 557 -98 693 -66 165 -100 4907 -134 199 -100 131 -66 333 -198 1345 -66 3781 -98 267 -100 597 -232 1449 -66 723 -330 197 -164 1021 -132 561 -98 591 -100 755 -66 559 -100 229 -228 131 -66 163 -198 689 -100 3365 -100 99 -68 431 -100 797 -66 431 -100 401 -68 397 -134 295 -66 297 -64 65 -100 1085 -132 3135 -66 497 -66 201 -98 99 -168 299 -66 165 -68 267 -366 231 -66 463 -266 733 -132 397 -66 297 -100 4563 -66 163 -66 293 -100 755 -100 697 -100 563 -100 4661 -132 65 -100 97 -494 691 -98 63 -66 459 -232 295 -230 229 -198 495 -66 457 -266 425 -685602 231 -202 133 -66 331 -66 167 -132 699 -98 231 -66 301 -66 299 -596 399 -66 557 -230 7389 -66 733 -98 657 -66 723 -100 789 -68 1661 -132 627 -66 1087 -134 97 -66 5173 -134 693 -134 1261 -132 4435 -66 131 -134 199 -66 891 -66 493 -100 397 -66 365 -100 297 -134 4287 -66 501 -166 1281 -100 195 -100 1151 -100 467 -266 265 -98\nRAW_Data: 2027 -68 131 -66 5771 -98 2171 -130 163 -66 7573 -66 661 -66 653 -66 357 -130 7217 -98 229 -132 393 -66 997 -66 4689 -100 97 -134 9081 -66 163 -132 493 -100 1217 -132 529 -230 131 -66 235 -100 133 -134 1359 -66 5243 -98 329 -66 167 -100 299 -100 431 -66 7349 -132 1059 -66 1393 -100 1317 -66 629 -98 429 -100 495 -100 365 -66 397 -234 1489 -100 4599 -100 399 -100 1855 -68 433 -98 1393 -132 7409 -100 5607 -66 463 -66 665 -68 2553 -134 167 -102 2263 -686936 99 -800 67 -98 99 -66 133 -230 265 -66 3631 -100 229 -130 99 -66 825 -100 99 -232 231 -66 521 -66 593 -98 1413 -130 359 -66 4639 -66 131 -68 97 -202 799 -166 99 -68 365 -66 1595 -66 2987 -66 423 -100 131 -98 791 -132 433 -100 893 -66 4831 -100 101 -98 629 -66 1221 -98 197 -66 625 -98 261 -66 559 -66 665 -66 4409 -66 331 -268 2017 -166 199 -68 333 -98 1553 -68 401 -166 563 -66 393 -100 195 -66 1485 -230 265 -100 4353 -68 431 -100 2383 -66 6737 -98 493 -66 1017 -68 5915 -66 1085 -198 527 -66 4223 -100 163 -64 131 -66 1187 -98 393 -66 1747 -130 263 -66 597 -100 501 -100 431 -132 1491 -100 131 -66 599 -68 2595 -100 497 -68 265 -100 763 -100 265 -100 165 -66 595 -68 331 -100 1587 -66 163 -66 99 -164 559 -130 457 -66 1611 -100 991 -66 4403 -100 1427 -164 565 -132 499 -66 827 -298 299 -100 4637 -166 263 -100 995 -66 1083 -428 365 -424 369 -454 377 -420 377 -418 409 -418 377 -424 381 -436 357 -438 387 -444 383 -414 385 -4058 783 -452 797 -434 811 -412 417 -862 773 -474 779 -484 387 -874 383 -850 403 -822 809 -450 415 -834 807 -444 773 -482 381 -870 787 -478 791 -436 805 -448 803 -420 411 -842 403 -836 415 -838 391 -864 807 -480 395 -850 387 -840 385 -844 391 -858 381 -878 783 -452 795 -464 807 -450 817 -448 377 -858 807 -446 777 -450 789 -454 813 -448 819 -430 809 -448 411 -838 807 -446 385 -862 379 -870 769 -468 801 -446 809 -456 379 -872 409 -858 781 -448 383 -846 409 -846 775 -478 381 -848 415 -858 809 -444 813 -422 393 -868 781 -448 385 -850 409 -860 777 -480 385 -866 805 -444 805 -418 411 -808 391 -15820 421 -372 417 -388 419 -386 437 -394 395 -400 421 -408 385 -412 415 -384 415 -418 383 -420 383 -420 385 -4030 825 -410 845 -420 811 -418 423 -838 821 -420 815 -456 385 -878 419 -816 407 -816 835 -416 409 -842 821 -422 811 -456 387 -876 789 -454 825 -428 801 -414 821 -442 399 -850 381 -852\nRAW_Data: 417 -824 447 -812 809 -462 413 -848 421 -816 399 -822 413 -850 419 -816 803 -440 809 -450 815 -446 821 -456 403 -820 821 -420 819 -418 807 -456 811 -412 821 -454 807 -470 379 -850 823 -416 423 -824 409 -844 789 -450 815 -432 807 -446 409 -838 439 -822 811 -414 409 -844 419 -822 803 -442 413 -848 409 -844 815 -448 811 -424 421 -838 787 -450 411 -810 407 -864 811 -418 415 -866 803 -448 817 -418 419 -776 417 -15782 437 -396 427 -378 407 -410 415 -376 415 -410 419 -388 419 -390 419 -388 411 -382 417 -400 409 -398 423 -4008 821 -420 815 -422 815 -446 405 -830 813 -450 811 -452 379 -876 407 -828 419 -810 813 -458 377 -842 823 -454 783 -458 385 -876 789 -456 827 -432 811 -412 821 -420 435 -820 393 -862 421 -810 429 -852 809 -444 409 -836 421 -812 425 -820 393 -828 421 -846 821 -432 809 -446 821 -448 821 -422 393 -854 787 -454 801 -432 803 -446 803 -446 821 -446 793 -472 381 -840 835 -418 415 -838 399 -820 837 -414 835 -410 835 -418 437 -858 389 -846 817 -432 385 -840 421 -812 827 -432 415 -848 421 -816 809 -472 811 -418 393 -866 785 -450 381 -852 419 -820 807 -440 411 -874 801 -448 803 -426 407 -804 413 -15800 397 -394 435 -388 409 -388 411 -386 447 -382 417 -384 417 -386 419 -388 415 -390 417 -420 385 -420 385 -4032 805 -440 803 -446 801 -416 437 -826 811 -448 809 -454 409 -844 407 -828 419 -834 817 -432 385 -842 819 -418 845 -428 415 -846 787 -456 831 -430 805 -414 821 -454 401 -834 381 -840 409 -866 415 -834 801 -472 379 -842 407 -832 419 -842 387 -832 409 -842 823 -454 785 -462 809 -448 817 -418 419 -846 795 -436 809 -414 829 -428 811 -450 811 -456 809 -444 409 -840 819 -420 383 -852 419 -822 799 -442 807 -452 815 -448 419 -854 413 -814 809 -422 409 -838 421 -844 795 -434 415 -848 419 -818 807 -476 809 -420 393 -834 813 -450 383 -852 417 -824 805 -474 381 -850 821 -454 805 -432 379 -808 417 -119860 67 -134 67 -98 133 -764 65 -398 133 -132 665 -200 597 -64 461 -66 4203 -96 227 -98 655 -98 327 -66 819 -66 589 -132 5777 -66 1495 -328 231 -130 131 -66 463 -66 265 -68 131 -68 429 -66 195 -98 5117 -100 797 -100 299 -166 459 -100 1731 -66 1759 -66 563 -66 825 -98 1081 -68 5321 -232 1291 -134 1955 -100 523 -68 795 -66 767 -66 3589 -66 827 -66 267 -100 2395 -100 4745 -66 2343 -66 1057 -100 1363 -100 6775 -166 329 -66 593 -66 1719 -98 6079 -100 427 -66 523 -66 525 -98\nRAW_Data: 1195 -66 1157 -68 333 -100 533 -66 4957 -134 731 -66 297 -66 97 -98 2989 -66 6633 -66 981 -98 983 -66 993 -98 2015 -132 65 -68 857 -232 723 -66 165 -66 1283 -100 853 -66 5575 -102 627 -98 367 -100 265 -66 853 -66 261 -66 195 -66 751 -66 195 -98 657 -132 953 -66 163 -296 429 -264 263 -66 1511 -98 263 -262 297 -68 263 -66 2523 -66 163 -132 295 -96 1447 -66 199 -66 463 -68 795 -66 1795 -100 701 -66 1333 -66 99 -66 131 -264 1293 -100 99 -66 299 -68 397 -66 399 -66 329 -100 595 -100 795 -66 131 -66 2015 -100 295 -98 2673 -130 1571 -66 753 -66 887 -98 723 -98 821 -100 131 -66 889 -66 793 -66 1057 -132 399 -100 397 -298 165 -66 299 -100 465 -100 233 -132 763 -66 1391 -130 2395 -66 895 -132 99 -134 2029 -166 799 -100 593 -98 195 -64 131 -66 1545 -66 785 -100 97 -98 131 -98 1753 -130 295 -66 619 -198 853 -166 233 -66 1393 -98 1783 -66 291 -132 691 -100 997 -66 133 -332 231 -66 131 -66 525 -66 263 -64 2435 -68 2967 -68 1101 -100 165 -68 599 -66 167 -134 235 -134 297 -68 497 -384 377 -448 377 -418 375 -454 377 -432 369 -424 377 -438 377 -436 377 -430 381 -434 359 -436 387 -4046 383 -852 797 -436 387 -870 385 -848 403 -850 809 -454 379 -876 415 -822 405 -852 381 -846 417 -822 801 -440 415 -846 415 -856 805 -446 383 -874 379 -834 405 -838 813 -460 783 -446 817 -444 793 -472 383 -880 381 -862 773 -442 411 -842 377 -858 413 -846 387 -870 775 -484 771 -480 805 -420 415 -848 795 -438 811 -414 817 -452 803 -470 777 -450 819 -450 417 -822 801 -442 385 -876 385 -848 795 -438 809 -452 819 -450 385 -882 375 -848 811 -422 413 -840 383 -846 823 -434 385 -874 381 -886 771 -476 811 -420 411 -840 809 -450 385 -850 413 -832 809 -444 383 -880 771 -486 803 -430 403 -806 383 -15810 425 -384 401 -420 393 -420 391 -386 421 -422 389 -386 423 -386 423 -388 419 -388 421 -388 421 -388 421 -4026 407 -840 813 -418 411 -842 397 -113844 97 -66 97 -164 99 -328 99 -232 299 -366 99 -1066 165 -66 623 -134 1197 -68 931 -66 599 -66 99 -234 2323 -132 1281 -66 495 -198 555 -414 383 -418 383 -418 417 -386 417 -418 383 -422 375 -422 379 -436 385 -444 351 -446 383 -416 385 -4076 379 -844 421 -812 395 -854 377 -862 813 -452 385 -840 815 -450 799 -488 383 -822 807 -446 803 -420 419 -842 403 -852 779 -450 407 -856 387 -880 805 -430 385 -840 809 -454 385 -856 377 -856\nRAW_Data: 777 -484 783 -454 807 -476 807 -410 797 -454 773 -462 383 -872 789 -454 787 -454 383 -870 797 -474 407 -822 813 -446 775 -448 785 -456 785 -484 783 -452 385 -872 385 -882 777 -442 809 -444 375 -864 777 -450 385 -886 789 -452 783 -470 383 -884 391 -858 779 -448 383 -848 385 -852 411 -856 379 -848 419 -852 777 -504 783 -418 801 -448 375 -852 811 -446 777 -450 813 -448 801 -458 419 -810 783 -424 379 -16422 413 -400 399 -396 405 -426 375 -424 411 -390 411 -390 413 -420 383 -420 383 -418 383 -418 415 -418 385 -4062 419 -816 383 -850 383 -852 409 -856 781 -448 385 -852 805 -468 779 -480 383 -844 819 -420 815 -418 391 -860 387 -880 763 -462 383 -876 417 -852 813 -446 381 -848 769 -456 397 -858 363 -882 779 -450 811 -454 807 -480 777 -446 803 -452 779 -432 383 -874 771 -488 765 -456 383 -878 795 -488 385 -854 773 -448 785 -456 777 -484 783 -450 771 -478 377 -860 407 -880 809 -418 813 -416 391 -862 779 -450 385 -888 791 -452 781 -468 381 -876 409 -834 777 -452 383 -872 377 -856 385 -866 405 -854 355 -898 787 -488 765 -460 777 -446 383 -852 807 -432 809 -446 783 -458 817 -454 391 -822 799 -418 399 -16448 421 -388 381 -424 417 -390 377 -456 379 -406 385 -442 383 -416 385 -418 387 -418 387 -452 355 -450 357 -4096 383 -840 387 -846 389 -858 365 -882 785 -444 385 -886 793 -452 781 -470 415 -844 791 -452 779 -432 379 -872 377 -884 779 -448 383 -872 379 -896 799 -430 411 -834 789 -456 383 -838 421 -846 799 -428 807 -444 803 -480 803 -446 767 -472 777 -448 383 -852 791 -488 775 -466 381 -848 793 -486 389 -852 775 -448 803 -446 801 -454 779 -468 775 -448 389 -896 387 -878 775 -438 807 -448 367 -848 813 -444 385 -848 789 -490 767 -458 383 -912 363 -850 813 -444 381 -848 385 -850 409 -854 385 -870 387 -846 803 -464 811 -418 801 -446 375 -884 779 -444 779 -484 779 -446 797 -472 381 -850 773 -416 407 -16410 409 -422 369 -410 403 -410 409 -410 379 -412 411 -408 377 -442 377 -428 379 -440 347 -444 381 -416 381 -4076 411 -844 375 -848 383 -850 385 -884 757 -488 385 -856 775 -480 769 -478 409 -840 787 -448 777 -450 391 -860 387 -848 793 -458 383 -878 417 -852 813 -412 383 -874 787 -454 389 -844 393 -854 785 -482 781 -454 807 -480 777 -446 801 -452 779 -434 381 -876 801 -454 765 -460 381 -878 787 -488 411 -826 775 -446 801 -458 779 -450 809 -450 803 -444 379 -862 419 -878 777 -438 807 -446 367 -878\nRAW_Data: 777 -446 383 -850 791 -486 783 -468 381 -882 393 -856 777 -446 383 -848 383 -854 411 -858 379 -848 419 -850 775 -506 783 -450 769 -446 377 -852 813 -446 777 -484 779 -448 795 -454 417 -824 777 -414 405 -122598 99 -134 65 -100 199 -832 99 -200 829 -98 163 -100 165 -66 197 -100 361 -98 723 -66 2987 -100 561 -460 1415 -100 527 -164 621 -98 459 -100 797 -66 497 -66 365 -132 401 -134 131 -66 699 -100 231 -66 327 -164 197 -66 299 -66 359 -66 395 -66 1773 -98 591 -66 295 -64 261 -98 833 -132 4215 -66 735 -98 331 -100 431 -66 761 -266 331 -102 497 -200 5637 -66 97 -198 921 -100 531 -66 299 -100 1559 -98 99 -100 233 -100 4173 -68 431 -66 599 -100 231 -66 429 -166 331 -132 431 -100 489 -66 359 -98 131 -66 261 -134 5083 -134 299 -100 1921 -68 197 -136 5299 -100 263 -64 721 -100 399 -130 429 -100 99 -98 1351 -100 295 -66 1315 -164 129 -98 393 -164 925 -66 663 -68 131 -66 229 -100 231 -100 99 -98 427 -66 325 -130 165 -98 197 -130 197 -128 327 -100 429 -66 1227 -100 893 -98 3491 -68 699 -66 365 -100 529 -98 229 -396 261 -66 561 -98 227 -98 329 -426 393 -130 363 -66 133 -166 1353 -100 393 -200 197 -100 199 -100 333 -66 961 -66 199 -66 197 -64 593 -66 627 -66 1317 -66 165 -198 199 -98 99 -66 3621 -98 597 -98 165 -66 4527 -200 263 -66 2751 -100 2131 -66 229 -64 721 -132 1261 -266 629 -98 201 -68 1323 -100 1689 -66 233 -326 1283 -66 261 -66 1381 -66 201 -100 4351 -66 331 -100 565 -134 2025 -66 563 -198 1657 -98 3057 -68 397 -98 297 -66 231 -100 931 -98 365 -66 1127 -66 165 -132 201 -100 99 -66 961 -100 99 -66 425 -66 1087 -166 463 -64 6307 -530 329 -66 297 -100 329 -66 201 -100 1123 -66 723 -98 523 -64 985 -66 131 -134 99 -66 4897 -66 1451 -132 199 -66 659 -66 195 -66 557 -66 263 -68 569 -100 661 -164 1975 -100 1361 -66 3647 -66 335 -100 363 -100 919 -66 887 -166 1775 -166 501 -66 495 -66 2753 -98 887 -66 3259 -66 821 -464 797 -134 567 -66 2401 -98 5415 -100 435 -100 99 -100 427 -66 599 -68 465 -132 435 -100 1495 -66 301 -68 233 -200 431 -66 397 -100 265 -68 231 -100 461 -234 199 -98 893 -132 265 -298 131 -166 2055 -68 199 -66 497 -66 1697 -66 1857 -66 5599 -66 2351 -66 131 -100 4929 -66 265 -134 561 -66 765 -132 861 -66 5351 -134 265 -102 99 -66 563 -66 267 -136 333 -98 233 -100 601 -66 365 -98\nRAW_Data: 1023 -66 5413 -232 631 -134 8367 -66 1583 -196 1901 -98 129 -66 1381 -98 3681 -98 1583 -98 133 -100 263 -134 1731 -100 465 -66 629 -66 4609 -166 201 -132 1995 -66 333 -66 499 -100 723 -134 561 -66 5317 -66 333 -66 1135 -66 1193 -66 1227 -100 885 -132 163 -230 719 -100 2305 -66 1025 -166 397 -68 793 -100 1381 -132 5385 -66 623 -164 763 -98 357 -396 3739 -134 4665 -66 393 -130 1153 -98 687 -66 229 -100 961 -66 467 -66 1301 -66 3647 -98 363 -66 329 -68 1589 -100 261 -66 5647 -66 2039 -66 297 -164 523 -66 197 -100 933 -66 99 -164 67 -66 97 -64 197 -64 559 -132 1151 -64 229 -98 2249 -64 1151 -66 533 -134 299 -134 665 -98 559 -98 1253 -66 2687 -134 995 -132 233 -68 131 -68 467 -132 297 -66 233 -66 167 -66 165 -134 201 -98 165 -166 1421 -428 333 -480 327 -448 361 -462 367 -424 371 -452 377 -420 379 -420 411 -418 381 -422 379 -424 381 -4072 773 -450 393 -852 397 -858 777 -448 811 -458 779 -480 793 -458 809 -448 385 -854 789 -426 411 -844 391 -852 815 -450 801 -444 415 -848 407 -848 383 -850 799 -450 409 -854 773 -450 395 -862 419 -844 401 -852 809 -456 379 -838 417 -848 397 -822 805 -446 411 -840 419 -848 799 -470 811 -418 411 -840 385 -844 423 -838 381 -882 369 -882 777 -480 383 -856 395 -862 385 -842 819 -432 387 -840 419 -850 399 -854 381 -850 403 -884 811 -418 415 -822 417 -826 405 -850 811 -420 817 -446 801 -452 811 -472 381 -840 833 -410 807 -454 785 -458 803 -444 805 -448 801 -458 417 -844 795 -440 809 -382 417 -16778 459 -354 455 -386 385 -422 419 -388 411 -382 405 -420 397 -398 421 -408 387 -410 385 -414 415 -418 383 -4046 787 -424 413 -844 427 -818 817 -416 835 -428 811 -450 811 -458 803 -448 409 -838 785 -450 383 -840 417 -862 809 -442 811 -416 421 -872 385 -850 403 -820 809 -452 397 -828 815 -446 413 -840 409 -838 413 -872 807 -412 411 -848 407 -824 415 -844 783 -460 379 -876 385 -850 807 -474 809 -418 411 -838 419 -810 429 -818 423 -838 419 -846 797 -436 413 -878 381 -828 411 -848 811 -418 411 -840 419 -846 397 -852 387 -870 387 -850 803 -442 413 -844 407 -824 415 -848 781 -462 809 -412 837 -448 803 -458 419 -810 827 -434 811 -414 801 -454 815 -450 779 -458 803 -450 395 -860 809 -416 809 -424 379 -16828 405 -414 379 -412 413 -410 383 -406 423 -396 399 -392 433 -382 407 -412 413 -380 411 -414 409 -394 413 -4044 805 -422 393 -854 395 -826 813 -448\nRAW_Data: 811 -456 811 -412 821 -486 775 -438 411 -844 801 -442 395 -838 413 -838 803 -448 801 -456 413 -840 407 -836 417 -834 805 -446 387 -848 801 -438 415 -844 407 -848 417 -848 819 -432 385 -844 419 -816 429 -826 809 -446 411 -838 419 -848 797 -468 805 -418 393 -866 385 -846 389 -854 409 -838 415 -838 799 -472 379 -886 367 -850 417 -846 781 -458 379 -842 419 -848 401 -854 381 -884 369 -884 783 -450 415 -820 407 -848 413 -814 809 -460 809 -444 805 -448 801 -460 417 -844 795 -434 811 -412 801 -450 809 -436 805 -448 805 -446 417 -864 809 -412 803 -416 409 -16802 423 -386 411 -382 405 -420 377 -122006 97 -98 165 -66 97 -132 65 -428 427 -132 3981 -66 265 -66 161 -66 327 -66 1625 -66 727 -100 4773 -134 863 -66 665 -66 2605 -98 4311 -98 99 -66 1249 -98 263 -100 5105 -68 461 -100 5065 -66 965 -68 799 -164 5737 -19106 327 -196 131 -362 131 -332 229 -134 363 -1162 297 -132 1197 -100 99 -132 231 -66 425 -200 267 -100 395 -134 331 -134 267 -100 563 -100 231 -66 655 -66 427 -66 299 -98 197 -132 3997 -66 463 -134 133 -134 1827 -132 1847 -66 131 -66 933 -100 3749 -132 131 -328 887 -98 457 -66 457 -100 593 -98 1813 -66 3895 -98 1117 -66 199 -66 881 -98 987 -66 397 -98 749 -66 195 -100 953 -100 819 -100 1017 -98 1915 -100 1707 -130 3865 -98 567 -132 265 -132 333 -66 1033 -132 791 -100 165 -100 1261 -66 531 -68 4337 -68 1191 -100 933 -100 767 -66 499 -164 363 -132 65 -100 5559 -66 233 -66 167 -68 265 -232 195 -100 329 -98 1795 -132 5043 -132 197 -66 223 -132 265 -200 525 -66 1147 -132 793 -100 4221 -132 703 -66 197 -132 595 -100 3891 -100 333 -100 1451 -164 261 -328 199 -166 1277 -1574 1699 -490 1705 -516 1663 -516 1695 -518 1699 -484 617 -1538 609 -1562 585 -1576 1693 -518 587 -1576 1701 -514 581 -1554 1737 -484 1737 -484 1711 -528 583 -1572 599 -1576 609 -1530 615 -1560 1719 -494 1739 -520 581 -1560 631 -1534 609 -16396 609 -1576 1707 -490 1713 -516 1697 -520 1699 -508 1711 -530 579 -1572 599 -1576 609 -1544 1707 -518 587 -1570 1731 -484 605 -1578 1699 -516 1729 -476 1753 -494 605 -1568 609 -1566 593 -1540 607 -1566 1733 -510 1705 -512 599 -1578 611 -1544 615 -16410 631 -1536 1711 -530 1703 -500 1731 -516 1737 -518 1741 -480 639 -1566 599 -1568 615 -1580 1743 -482 641 -1534 1751 -486 655 -1542 1735 -520 1735 -514 1745 -512 595 -1570 613 -1546 651 -1548 603 -1574 1767 -484 1745 -516 609 -1570 595 -1580 611 -16402 631 -1542 1733 -486 1747 -480 1773 -484\nRAW_Data: 1729 -486 1783 -462 643 -1542 637 -1548 613 -1562 1743 -480 611 -1566 1747 -490 623 -1540 1747 -502 1739 -492 1759 -500 615 -1544 639 -1532 617 -1556 623 -1542 1777 -462 1771 -490 623 -1540 619 -1570 609 -16422 629 -1546 1747 -486 1739 -474 1737 -516 1713 -506 1751 -488 621 -1574 611 -1558 617 -1542 1729 -520 619 -1534 1735 -512 611 -1546 1743 -508 1727 -514 1729 -482 647 -1554 615 -1546 605 -1568 613 -1560 1713 -512 1737 -486 607 -1568 613 -1562 621 -16400 643 -1538 1715 -518 1709 -482 1725 -514 1723 -520 1711 -530 581 -1570 603 -1596 575 -1592 1693 -516 601 -1580 1703 -520 591 -1574 1737 -484 1741 -482 1761 -482 605 -1576 611 -1560 613 -1548 623 -1538 1739 -478 1715 -520 587 -1556 603 -1568 607 -16414 661 -1524 1743 -478 1727 -486 1727 -486 1747 -490 1731 -522 587 -1558 623 -1546 623 -1574 1737 -484 597 -1566 1739 -480 635 -1560 1745 -464 1745 -516 1729 -504 619 -1546 623 -1546 623 -1574 609 -1560 1743 -486 1747 -480 639 -1572 337 -80934 65 -702 65 -960 165 -132 333 -100 1031 -134 163 -100 199 -398 2413 -66 853 -96 3999 -66 1027 -68 495 -66 431 -98 197 -134 65 -100 431 -68 663 -596 197 -132 525 -100 425 -100 397 -100 429 -100 263 -166 4697 -132 291 -228 459 -64 957 -66 233 -66 723 -364 5193 -198 759 -66 359 -98 231 -98 425 -66 265 -98 457 -66 3309 -198 863 -66 263 -166 393 -66 1025 -166 331 -134 463 -102 4119 -66 331 -66 465 -66 395 -66 267 -100 365 -136 97 -100 333 -134 363 -100 265 -100 799 -66 463 -132 1917 -100 2273 -132 99 -66 2537 -98 97 -100 195 -66 657 -98 1145 -66 359 -66 423 -98 227 -66 5391 -66 4069 -100 795 -130 455 -66 3877 -100 165 -98 365 -198 2085 -66 1059 -66 333 -66 7359 -102 431 -66 1349 -100 131 -66 459 -66 327 -66 4189 -98 533 -100 563 -66 365 -66 465 -66 863 -66 493 -66 1317 -66 3459 -98 99 -66 1153 -130 1875 -166 867 -232 5309 -132 363 -66 301 -66 1333 -166 1525 -66 865 -166 5313 -98 295 -98 363 -98 197 -98 229 -64 557 -100 97 -164 199 -66 99 -66 291 -66 393 -98 163 -326 327 -100 529 -134 467 -68 165 -66 201 -66 6277 -66 229 -64 361 -66 227 -66 1215 -66 525 -66 131 -66 1053 -66 1147 -66 1463 -100 565 -402 1127 -66 1329 -98 331 -66 97 -68 461 -66 265 -68 65 -66 1197 -66 831 -66 761 -66 233 -66 4805 -66 395 -68 297 -100 827 -198 625 -100 399 -66 1191 -68 429 -100 629 -66 499 -66 195 -100 2485 -66 655 -100 4745 -68 297 -66 1965 -134 4807 -68 99 -134 627 -66 233 -132 499 -66\nRAW_Data: 5047 -98 165 -102 167 -66 533 -66 133 -100 1359 -166 493 -100 2385 -100 3759 -66 635 -132 331 -100 2115 -100 431 -66 5673 -66 631 -66 231 -100 1589 -100 9979 -100 2797 -394 327 -66 687 -66 899 -100 5425 -134 461 -100 623 -132 5885 -68 399 -100 165 -98 165 -100 1761 -230 697 -66 227 -98 919 -66 1921 -134 1099 -66 331 -66 333 -166 531 -100 793 -66 129 -164 197 -66 131 -98 887 -100 131 -198 295 -66 197 -68 761 -68 827 -100 297 -100 1585 -100 1083 -66 199 -66 1553 -68 433 -100 631 -232 165 -66 297 -234 431 -66 2297 -100 233 -66 565 -230 129 -196 4915 -66 297 -66 529 -134 365 -132 433 -66 297 -166 359 -66 695 -68 1259 -130 689 -66 1645 -98 525 -100 791 -66 295 -66 1413 -64 165 -68 261 -100 1627 -134 1677 -66 357 -66 633 -132 197 -132 1061 -100 5353 -66 233 -100 897 -66 395 -66 459 -66 129 -100 897 -134 4679 -66 857 -130 1841 -66 1023 -134 5191 -98 361 -98 527 -66 1021 -98 591 -66 493 -66 955 -100 231 -98 295 -364 99 -66 4471 -66 99 -132 1193 -66 563 -66 267 -68 661 -66 1261 -134 827 -98 4705 -66 1643 -132 2273 -166 263 -66 459 -100 3483 -66 565 -100 2155 -130 195 -100 1695 -100 529 -100 1053 -202 133 -100 885 -98 297 -98 853 -68 531 -66 263 -100 4855 -66 331 -66 563 -64 391 -164 333 -68 97 -132 1789 -132 165 -132 267 -66 1191 -66 727 -100 197 -66 601 -98 231 -100 331 -200 465 -66 863 -66 97 -98 361 -66 231 -100 959 -66 525 -66 167 -98 531 -100 4877 -100 829 -66 331 -66 363 -66 589 -64 227 -98 393 -132 99 -98 197 -132 197 -100 763 -100 399 -100 627 -98 301 -66 1595 -1492 511 -1044 513 -1040 507 -1042 1005 -558 1007 -554 1005 -574 1007 -548 1005 -546 1003 -542 477 -1072 1007 -516 1013 -556 1009 -552 513 -1076 975 -550 1009 -554 1013 -524 1017 -552 487 -1042 999 -580 993 -584 493 -1050 985 -580 977 -576 483 -1044 515 -1044 1007 -516 1005 -586 1011 -550 481 -1072 1013 -548 1013 -548 975 -552 515 -1042 475 -1050 1017 -574 491 -1084 983 -570 475 -1056 525 -1048 979 -546 515 -1042 479 -1040 1003 -584 515 -1040 515 -1044 513 -1046 515 -1042 1009 -548 1007 -516 509 -1040 1003 -524 1513 -19022 1537 -1536 477 -1048 507 -1032 509 -1046 1001 -570 1013 -572 979 -580 483 -1064 499 -1052 981 -546 513 -1044 1009 -518 1009 -586 1009 -548 513 -1040 1011 -548 1009 -546 1009 -546 1005 -516 511 -1044 977 -620 981 -570 495 -1050 1013 -534 999 -550 525 -1048 485 -1044 1009 -514 1003 -586 1009 -546 515 -1074 979 -546\nRAW_Data: 1009 -542 1009 -546 513 -1040 479 -1040 1003 -586 513 -1040 1007 -576 483 -1074 483 -1044 1011 -548 479 -1044 511 -1042 1003 -588 477 -1076 479 -1074 513 -1040 513 -1040 1009 -548 1007 -516 509 -1040 1003 -522 1527 -18990 1541 -1550 481 -1042 509 -1048 481 -1046 1001 -588 1017 -554 985 -572 477 -1060 1023 -548 981 -544 515 -1040 1011 -514 1003 -586 1007 -548 513 -1042 1007 -576 981 -546 1013 -548 1007 -516 509 -1042 1005 -574 1005 -576 481 -1076 981 -578 977 -546 513 -1044 513 -1040 1005 -518 1007 -590 971 -584 479 -1074 1011 -548 1007 -548 1005 -516 511 -1040 509 -1044 975 -588 519 -1052 1003 -552 501 -1046 513 -1062 993 -548 485 -1042 515 -1040 1003 -574 485 -1076 485 -1076 485 -1076 483 -1042 1009 -548 1009 -516 509 -1040 1003 -520 1527 -19010 1537 -1524 481 -1076 481 -1046 511 -1044 1013 -554 1013 -552 519 -1044 1005 -556 497 -1050 983 -578 483 -1042 1011 -516 1009 -586 1009 -550 513 -115324 65 -2458 65 -66 231 -896 231 -68 131 -434 1191 -66 399 -66 295 -100 627 -132 561 -66 755 -100 593 -100 1155 -98 163 -66 3687 -100 793 -98 229 -98 425 -100 623 -98 393 -132 131 -132 393 -66 395 -66 259 -132 259 -132 99 -100 463 -166 5379 -132 631 -66 1131 -66 819 -164 1115 -66 1525 -66 3617 -98 297 -98 65 -66 497 -68 199 -100 267 -166 593 -66 787 -132 197 -68 5473 -68 527 -164 329 -66 229 -64 195 -230 359 -100 263 -66 391 -98 821 -66 165 -66 2935 -100 65 -68 1057 -66 267 -68 561 -98 559 -98 329 -166 367 -100 1925 -134 999 -66 399 -98 529 -66 299 -134 299 -164 4801 -132 299 -98 595 -66 265 -68 1259 -98 327 -132 755 -198 4311 -130 721 -68 1259 -200 399 -302 495 -100 1259 -66 665 -66 331 -66 531 -66 3979 -134 461 -66 1313 -66 325 -66 753 -68 329 -98 427 -66 5079 -66 299 -134 527 -68 165 -100 333 -100 1091 -66 195 -164 195 -66 4411 -66 199 -100 1695 -66 567 -66 435 -234 1415 -98 325 -98 97 -98 329 -66 165 -268 131 -164 1905 -132 1123 -68 263 -166 199 -234 533 -134 491 -98 293 -66 989 -98 4053 -134 299 -166 499 -98 893 -66 463 -134 5411 -66 4201 -100 4949 -66 533 -166 1327 -66 131 -100 1717 -100 2787 -166 471 -66 1051 -98 1183 -66 2895 -68 2289 -66 797 -66 295 -68 627 -200 697 -100 163 -66 5101 -100 1791 -66 965 -134 7887 -66 265 -100 99 -134 1891 -100 559 -66 661 -98 167 -68 465 -68 1851 -164 4741 -98 397 -66 261 -98 955 -198 567 -98 233 -132 699 -100 465 -66 265 -164 331 -66 131 -98 765 -100 461 -98 1125 -64\nRAW_Data: 3701 -100 1033 -66 197 -68 4443 -68 595 -134 565 -66 499 -66 199 -134 5461 -66 645 -344 635 -326 677 -648 345 -312 697 -310 669 -350 667 -348 667 -634 345 -312 669 -650 373 -636 323 -348 635 -23014 363 -320 669 -322 641 -650 373 -312 667 -350 667 -346 667 -332 681 -642 351 -312 667 -640 319 -666 347 -318 645 -23022 349 -326 641 -352 659 -656 335 -350 661 -334 651 -354 657 -330 703 -658 329 -316 655 -646 349 -668 341 -318 625 -23038 323 -348 653 -322 663 -654 335 -354 661 -336 653 -322 685 -330 701 -652 321 -346 637 -648 351 -658 333 -354 623 -23022 319 -336 677 -320 661 -654 335 -354 661 -336 679 -322 687 -296 703 -656 331 -318 667 -652 353 -620 375 -318 625 -23036 321 -346 655 -322 661 -652 371 -320 659 -338 677 -322 691 -296 705 -654 333 -350 635 -634 345 -672 353 -300 635 -23040 349 -314 667 -314 665 -642 349 -352 645 -354 651 -344 693 -318 693 -644 345 -300 679 -626 361 -648 323 -354 651 -22990 345 -338 661 -346 653 -652 325 -350 647 -322 681 -350 673 -346 653 -654 321 -354 645 -646 349 -656 351 -340 609 -23046 321 -344 637 -328 679 -648 345 -314 697 -316 667 -348 669 -338 677 -646 351 -316 655 -646 343 -658 351 -314 655 -23016 333 -352 627 -328 681 -646 345 -350 655 -346 655 -322 691 -320 675 -652 337 -350 637 -666 315 -672 351 -300 669 -22998 349 -318 655 -340 653 -646 349 -348 667 -314 667 -352 669 -338 679 -646 351 -316 655 -646 345 -658 351 -314 653 -23020 335 -318 659 -322 665 -672 345 -316 659 -342 671 -330 677 -324 683 -650 341 -354 631 -656 349 -654 345 -352 621 -23002 329 -352 667 -322 641 -684 343 -316 659 -340 677 -322 695 -326 675 -654 333 -354 623 -648 353 -658 331 -354 629 -123230 99 -66 197 -568 97 -594 65 -100 229 -130 589 -134 5281 -66 99 -98 2761 -66 1385 -132 299 -66 4967 -230 1991 -200 627 -66 1515 -232 5531 -132 1693 -98 995 -66 465 -100 399 -66 893 -66 4501 -66 363 -66 197 -66 1989 -66 1727 -198 99 -66 499 -100 427 -98 1649 -132 165 -164 97 -98 295 -132 325 -98 131 -66 295 -66 657 -64 625 -66 295 -130 1515 -68 231 -66 455 -64 521 -66 559 -66 1977 -98 167 -166 335 -66 493 -66 233 -68 335 -66 199 -266 165 -134 167 -68 431 -100 425 -98 163 -98 197 -66 359 -98 691 -98 229 -66 689 -66 195 -66 785 -66 163 -230 231 -196 259 -66 425 -98 265 -102 597 -100 199 -100 231 -100 231 -68 565 -66 133 -166 165 -100 229 -66 629 -134 65 -132\nRAW_Data: 231 -68 2745 -66 4279 -132 1093 -200 663 -98 2791 -66 299 -66 4055 -64 691 -66 529 -66 165 -68 865 -66 629 -66 133 -100 597 -266 297 -68 4367 -66 261 -130 519 -68 1265 -66 2189 -66 1423 -66 131 -66 4185 -66 165 -100 333 -132 1125 -66 363 -66 701 -66 1523 -534 3887 -66 1941 -100 293 -66 327 -164 131 -98 589 -98 133 -66 129 -66 429 -98 1415 -132 1021 -130 555 -100 1791 -134 299 -66 165 -66 331 -100 927 -298 231 -66 97 -68 1025 -66 297 -66 199 -68 699 -66 97 -66 97 -66 261 -166 393 -98 129 -98 161 -100 425 -100 1873 -66 759 -66 799 -100 427 -66 299 -66 1031 -134 267 -100 2349 -68 2659 -100 2097 -132 629 -100 1129 -166 231 -66 231 -66 1719 -132 427 -200 5213 -66 1399 -66 3055 -132 7835 -66 1327 -66 4625 -66 359 -66 2607 -420 373 -452 345 -452 347 -454 379 -422 377 -450 349 -466 359 -434 359 -436 389 -442 353 -444 385 -4052 781 -448 797 -436 379 -872 803 -430 807 -448 813 -458 779 -478 803 -450 805 -430 801 -446 381 -848 815 -444 789 -472 381 -846 819 -454 803 -434 413 -840 375 -858 409 -838 387 -852 395 -864 815 -450 385 -854 391 -870 383 -846 389 -852 395 -860 383 -844 393 -886 781 -448 415 -858 411 -816 417 -820 405 -850 411 -848 779 -458 805 -446 805 -450 803 -458 415 -846 793 -434 387 -840 417 -848 791 -436 809 -448 805 -450 409 -860 383 -848 825 -434 387 -840 417 -846 793 -434 385 -874 383 -888 375 -882 779 -458 807 -416 803 -456 381 -842 821 -434 813 -450 413 -836 809 -444 811 -426 395 -828 409 -17022 421 -414 417 -382 417 -384 417 -386 417 -418 385 -420 409 -398 395 -398 391 -440 387 -408 387 -412 415 -4026 819 -420 793 -430 421 -838 811 -454 781 -462 813 -446 787 -488 777 -440 809 -450 793 -426 411 -844 817 -430 809 -448 397 -862 817 -452 783 -462 385 -838 419 -812 427 -820 393 -866 421 -810 823 -436 413 -842 409 -866 385 -844 389 -854 397 -826 419 -846 387 -854 813 -440 409 -848 419 -844 389 -856 399 -822 417 -844 783 -460 803 -446 801 -448 801 -454 417 -846 799 -438 385 -842 421 -846 793 -436 809 -448 821 -418 419 -848 407 -848 811 -420 409 -840 421 -810 823 -436 383 -874 407 -858 387 -844 821 -438 807 -414 835 -418 417 -844 797 -436 809 -450 395 -864 811 -452 785 -428 423 -804 415 -17028 413 -430 363 -450 339 -448 375 -450 343 -450 375 -452 371 -420 381 -432 351 -470 349 -442 381 -410 383 -4072 773 -446 805 -426 411 -844 787 -464 811 -448\nRAW_Data: 793 -456 813 -450 789 -460 805 -416 803 -456 381 -844 825 -434 811 -448 415 -834 811 -446 813 -458 357 -870 383 -846 391 -852 395 -862 383 -872 793 -468 379 -874 397 -860 383 -844 387 -854 365 -856 415 -848 387 -854 819 -450 383 -886 375 -850 383 -850 417 -826 403 -852 777 -450 805 -446 805 -454 817 -450 395 -850 819 -418 415 -848 397 -850 779 -448 805 -458 777 -482 387 -850 393 -864 809 -450 385 -854 379 -862 773 -474 383 -846 415 -858 377 -882 785 -458 805 -414 803 -456 381 -844 821 -434 809 -448 379 -868 815 -452 789 -432 391 -832 413 -17034 411 -398 427 -380 407 -412 413 -378 413 -412 411 -382 417 -396 417 -396 409 -414 379 -436 387 -410 385 -4044 819 -420 789 -426 411 -840 809 -456 821 -430 809 -448 821 -418 831 -430 805 -450 811 -414 409 -838 811 -454 783 -456 409 -848 819 -448 791 -440 415 -814 421 -814 431 -824 413 -850 385 -850 805 -474 379 -850 419 -828 411 -848 381 -850 417 -822 405 -852 381 -850 823 -452 409 -856 381 -840 417 -830 407 -852 379 -850 821 -422 827 -432 807 -448 821 -418 419 -850 803 -438 409 -810 407 -854 811 -448 809 -418 815 -446 419 -858 417 -814 811 -422 409 -838 421 -812 827 -434 415 -848 419 -816 411 -848 813 -456 805 -412 833 -418 413 -822 807 -452 817 -418 419 -850 803 -472 809 -420 397 -794 411 -17034 429 -394 435 -388 409 -386 411 -416 415 -384 415 -386 417 -386 417 -420 385 -420 383 -422 385 -422 417 -4022 805 -414 807 -448 407 -838 785 -454 809 -456 811 -412 833 -448 801 -454 809 -420 815 -420 409 -838 821 -420 813 -458 387 -876 787 -454 827 -432 385 -840 421 -814 429 -820 421 -836 421 -844 795 -436 409 -874 377 -866 383 -836 425 -824 409 -844 407 -822 415 -836 821 -434 409 -874 377 -866 387 -844 389 -854 397 -824 813 -450 813 -452 811 -412 831 -442 407 -864 783 -450 381 -840 419 -822 805 -442 809 -450 817 -448 419 -850 413 -814 813 -422 409 -840 419 -812 823 -434 415 -846 419 -818 409 -848 811 -458 805 -414 831 -416 415 -820 807 -450 801 -446 409 -864 819 -452 783 -428 393 -834 413 -17032 409 -432 363 -424 369 -448 375 -448 343 -450 375 -450 339 -454 375 -434 377 -432 351 -464 357 -436 389 -4050 773 -444 785 -466 385 -874 771 -452 793 -464 811 -446 803 -448 803 -454 809 -418 813 -422 411 -838 817 -448 781 -462 411 -846 797 -486 777 -438 415 -842 383 -850 403 -852 379 -848 405 -848 811 -448 415 -856 395 -856 387 -846 385 -834 391 -868\nRAW_Data: 387 -844 429 -852 781 -446 409 -862 419 -844 399 -824 385 -838 409 -854 811 -444 779 -452 815 -448 801 -472 385 -842 825 -434 387 -838 421 -812 827 -432 805 -450 819 -420 415 -850 411 -850 811 -422 393 -864 387 -846 787 -462 385 -876 385 -848 407 -848 811 -458 803 -414 831 -416 413 -852 773 -448 803 -446 405 -882 387 -113196 99 -362 163 -66 65 -1810 97 -66 165 -196 129 -98 523 -98 1525 -100 1095 -100 725 -66 6021 -66 261 -64 195 -98 293 -130 1745 -98 99 -132 397 -98 363 -98 199 -98 5181 -166 299 -66 429 -66 367 -66 431 -100 629 -134 1297 -98 229 -66 163 -100 4973 -132 995 -66 165 -98 395 -66 297 -66 1711 -200 5349 -100 1493 -232 663 -198 1721 -66 301 -100 5563 -66 1151 -66 331 -66 227 -66 97 -98 163 -100 65 -98 1975 -66 361 -66 97 -230 525 -66 657 -66 863 -66 4121 -230 231 -66 401 -132 129 -98 259 -98 263 -66 955 -100 327 -132 5011 -170 299 -64 763 -66 435 -100 297 -66 401 -132 827 -200 2287 -64 8629 -100 897 -66 1689 -100 2345 -132 97 -100 1155 -68 1915 -396 787 -66 1021 -130 1057 -100 267 -132 829 -100 4749 -68 599 -66 397 -100 329 -64 163 -296 99 -132 329 -66 4881 -66 2091 -66 165 -66 231 -98 359 -100 663 -98 297 -66 5775 -100 233 -98 699 -98 131 -66 361 -100 297 -66 331 -66 1017 -98 233 -66 231 -132 197 -98 1721 -130 523 -66 197 -98 953 -164 231 -164 231 -98 795 -100 1155 -66 753 -166 825 -66 4525 -98 955 -298 625 -66 885 -66 4759 -100 367 -66 495 -66 597 -100 465 -66 265 -100 601 -98 467 -230 463 -66 4177 -66 495 -66 693 -100 831 -66 431 -68 363 -132 295 -98 131 -166 265 -66 331 -100 3189 -66 395 -298 625 -64 327 -362 631 -66 333 -100 3927 -98 1743 -66 295 -100 197 -68 793 -66 891 -132 65 -100 5247 -132 427 -66 199 -66 263 -68 629 -68 297 -100 1345 -98 263 -66 693 -130 591 -68 263 -100 299 -66 399 -68 565 -200 231 -102 231 -100 497 -68 2259 -236 297 -66 663 -100 493 -98 133 -66 597 -132 265 -166 2459 -66 1857 -134 627 -100 827 -320 705 -354 695 -354 693 -354 721 -320 727 -322 733 -678 377 -358 677 -356 705 -644 381 -672 381 -348 691 -676 383 -672 415 -640 419 -324 697 -680 383 -650 381 -654 407 -658 365 -664 395 -382 685 -350 719 -350 653 -16570 709 -380 651 -384 687 -352 691 -346 701 -336 709 -362 715 -654 409 -352 681 -354 669 -678 383 -648 417 -326 711 -646 417 -648 413 -648 415 -326 711 -642\nRAW_Data: 383 -684 377 -656 367 -684 387 -672 385 -350 685 -384 685 -386 651 -16538 737 -332 705 -334 705 -332 705 -362 707 -320 719 -354 723 -646 417 -324 697 -354 693 -678 385 -652 379 -344 691 -698 361 -686 385 -654 417 -350 685 -652 385 -674 355 -700 357 -664 391 -664 393 -364 685 -364 709 -360 677 -16510 773 -310 695 -354 709 -320 733 -320 727 -322 729 -322 733 -680 387 -326 699 -352 693 -678 385 -648 381 -330 739 -648 415 -648 413 -650 413 -328 675 -682 379 -656 371 -688 353 -686 381 -652 413 -352 689 -386 693 -348 669 -16546 733 -348 697 -344 701 -346 699 -352 685 -354 721 -354 695 -644 423 -340 699 -332 711 -650 383 -652 419 -324 721 -642 419 -646 385 -680 387 -356 687 -646 387 -682 379 -656 401 -654 385 -654 383 -352 717 -352 723 -352 659 -16524 759 -324 697 -374 675 -358 679 -356 709 -352 703 -354 703 -680 375 -358 677 -354 709 -644 385 -680 385 -326 713 -680 385 -650 415 -648 415 -326 713 -648 383 -656 405 -654 385 -654 383 -652 413 -350 689 -386 693 -348 687 -16514 759 -322 699 -348 699 -336 721 -322 709 -354 707 -354 707 -680 385 -326 703 -354 691 -680 383 -638 417 -328 709 -646 419 -650 415 -648 413 -328 675 -682 381 -654 369 -688 385 -656 381 -684 381 -350 721 -354 695 -348 689 -16510 763 -322 697 -350 703 -350 701 -348 703 -350 701 -350 701 -678 379 -350 685 -350 697 -666 379 -662 395 -326 719 -654 417 -656 385 -650 409 -358 677 -680 377 -646 379 -686 367 -680 379 -662 395 -362 687 -364 709 -358 649 -16538 763 -324 699 -348 701 -346 687 -362 711 -322 707 -356 705 -682 377 -358 685 -354 707 -644 385 -682 385 -328 711 -648 417 -648 415 -648 415 -328 679 -684 379 -656 371 -686 353 -684 383 -684 381 -350 689 -386 687 -372 653 -16534 757 -328 689 -320 737 -320 729 -320 731 -320 729 -320 731 -644 407 -360 711 -320 707 -644 387 -650 419 -326 709 -644 423 -646 417 -648 415 -326 711 -646 383 -652 407 -656 387 -656 381 -654 413 -352 689 -352 723 -338 689 -16532 729 -328 711 -354 703 -320 695 -352 727 -322 727 -322 733 -678 375 -358 685 -354 671 -678 385 -648 417 -328 709 -646 417 -648 415 -650 413 -328 679 -684 379 -656 371 -664 393 -652 387 -684 383 -350 723 -350 695 -348 665 -16554 763 -308 713 -322 713 -322 731 -320 729 -322 727 -320 735 -678 387 -326 701 -356 689 -678 385 -650 381 -346 725 -652 387 -654 415 -644 415 -354 685 -654 383 -674 355 -662 391 -662 391 -664 395 -364 687 -362\nRAW_Data: 713 -326 679 -123960 161 -132 263 -624 263 -66 327 -162 131 -1094 131 -98 723 -66 621 -66 459 -66 229 -66 329 -66 591 -66 881 -66 3747 -66 1783 -66 3219 -68 263 -100 695 -66 165 -66 99 -66 1193 -98 531 -234 263 -68 7149 -66 461 -100 3253 -66 595 -134 561 -66 325 -66 685 -66 199 -98 359 -130 99 -98 787 -100 925 -100 5157 -66 591 -98 233 -100 827 -66 1345 -66 10323 -200 3355 -100 367 -100 965 -98 495 -98 233 -102 1923 -66 6641 -68 263 -68 299 -100 705 -198 827 -100 529 -68 625 -66 493 -66 3641 -98 897 -68 333 -100 6513 -68 825 -66 597 -132 231 -100 659 -100 1099 -102 963 -98 831 -68 261 -68 299 -134 4477 -66 561 -66 927 -98 393 -66 1871 -166 327 -132 229 -100 63 -66 561 -1216 479 -414 205 -636 517 -358 235 -642 243 -612 237 -628 227 -614 237 -682 241 -604 269 -628 525 -336 513 -348 537 -346 245 -600 513 -326 265 -644 279 -606 273 -596 257 -620 281 -556 281 -594 553 -314 283 -572 549 -324 299 -586 317 -550 567 -310 319 -558 555 -300 315 -554 281 -572 571 -324 557 -312 279 -598 289 -578 313 -552 577 -320 533 -312 559 -290 573 -342 285 -580 525 -350 279 -594 547 -318 283 -578 559 -310 269 -568 567 -346 281 -578 311 -564 545 -328 545 -350 513 -318 561 -316 283 -572 253 -628 579 -316 541 -318 279 -602 289 -574 313 -552 283 -560 555 -320 531 -288 1123 -1164 537 -332 279 -576 581 -320 283 -578 277 -568 287 -568 305 -550 315 -590 315 -564 275 -600 545 -328 543 -314 559 -320 285 -546 561 -310 285 -628 263 -608 283 -584 281 -600 269 -596 291 -558 549 -314 287 -554 577 -350 281 -598 257 -582 581 -320 283 -582 527 -344 269 -566 303 -548 579 -318 593 -314 283 -564 291 -578 313 -558 545 -318 571 -314 525 -292 569 -336 319 -562 569 -312 283 -596 549 -300 317 -556 553 -320 275 -566 543 -362 281 -594 281 -580 555 -324 541 -340 549 -318 543 -320 283 -540 309 -596 555 -338 549 -318 281 -580 311 -562 291 -578 275 -586 547 -320 511 -318 1099 -1150 557 -336 283 -594 539 -318 317 -534 291 -606 273 -552 317 -556 283 -604 287 -600 277 -586 547 -348 533 -320 541 -340 285 -542 565 -302 277 -632 283 -572 287 -594 303 -576 283 -594 283 -544 565 -310 285 -562 547 -362 283 -594 279 -580 551 -322 285 -604 553 -318 285 -548 281 -602 527 -346 557 -326 277 -588 283 -596 281 -578 553 -294 571 -302 551 -314 549 -352 279 -602 545 -324 275 -590 545 -318 319 -540 555 -324 279 -560\nRAW_Data: 545 -350 317 -574 273 -594 557 -302 549 -352 521 -318 567 -294 277 -582 275 -624 537 -340 565 -290 301 -582 319 -566 285 -568 299 -544 547 -316 549 -318 1103 -1150 543 -326 275 -586 547 -352 281 -574 309 -562 257 -588 275 -590 283 -592 317 -574 271 -594 557 -336 549 -318 543 -318 283 -574 547 -290 283 -634 283 -584 277 -600 257 -604 311 -556 317 -560 541 -320 283 -538 583 -320 297 -582 317 -564 567 -314 283 -562 581 -298 313 -548 283 -572 539 -354 555 -346 245 -598 289 -580 313 -552 575 -316 535 -292 569 -304 547 -348 279 -602 555 -326 279 -594 551 -320 283 -546 565 -310 285 -562 547 -364 283 -594 281 -600 525 -344 557 -290 565 -314 555 -320 287 -562 283 -580 585 -322 555 -310 283 -588 317 -538 309 -564 289 -576 543 -312 549 -320 1071 -1150 579 -290 301 -584 545 -352 283 -574 273 -566 291 -582 277 -588 283 -592 279 -610 273 -596 557 -300 549 -352 533 -316 279 -580 543 -312 285 -596 315 -568 307 -564 291 -580 313 -556 283 -584 537 -316 283 -570 545 -356 307 -590 283 -562 565 -346 247 -594 553 -302 279 -590 283 -570 541 -356 557 -310 279 -600 289 -576 313 -554 545 -316 565 -288 569 -302 547 -348 279 -598 557 -334 283 -572 541 -322 283 -600 547 -320 245 -598 543 -328 313 -574 285 -596 533 -342 549 -316 535 -332 531 -330 273 -586 281 -606 541 -322 565 -312 279 -600 289 -576 275 -588 281 -572 571 -290 543 -304 1117 -1144 557 -326 281 -592 547 -320 281 -580 311 -564 257 -602 275 -586 283 -590 315 -572 273 -594 555 -336 549 -318 523 -320 283 -578 557 -294 277 -618 317 -560 315 -568 273 -602 257 -604 277 -590 547 -318 285 -564 537 -350 317 -560 293 -598 551 -320 283 -564 565 -312 283 -562 291 -580 543 -348 555 -318 283 -602 257 -604 275 -590 545 -318 537 -348 527 -292 569 -336 319 -560 535 -348 283 -596 551 -298 313 -556 545 -318 277 -564 579 -330 283 -594 279 -600 523 -344 561 -290 567 -312 549 -318 283 -566 279 -600 549 -324 585 -308 285 -588 281 -600 277 -572 273 -594 551 -298 543 -312 1077 -1150 585 -290 289 -606 547 -316 319 -540 309 -566 289 -576 275 -586 281 -622 277 -570 291 -602 543 -314 547 -318 569 -312 283 -564 545 -328 273 -618 283 -578 309 -562 291 -580 313 -556 283 -586 537 -314 283 -570 577 -320 311 -588 283 -598 527 -346 269 -564 573 -312 285 -554 317 -564 539 -352 553 -322 291 -578 283 -594 281 -566 563 -302 563 -292 569 -312 549 -348 281 -598 529 -340 283 -570\nRAW_Data: 573 -290 285 -604 549 -318 245 -598 545 -358 281 -572 287 -596 567 -310 547 -320 535 -316 561 -294 277 -586 277 -624 557 -316 561 -326 281 -558 171 -112742 297 -66 231 -100 133 -98 131 -66 231 -66 65 -330 231 -790 165 -400 99 -434 459 -66 233 -132 1581 -98 163 -66 1049 -132 1849 -100 1855 -98 5435 -100 301 -68 761 -66 3115 -100 231 -68 2421 -68 765 -66 167 -68 233 -134 997 -66 233 -66 1061 -66 833 -98 565 -66 369 -102 131 -100 5197 -132 853 -232 825 -100 265 -100 765 -100 631 -66 827 -66 1749 -66 265 -136 165 -66 399 -100 3821 -98 793 -66 599 -100 231 -68 635 -68 197 -134 5091 -132 299 -68 1327 -100 757 -98 629 -66 601 -66 2887 -100 1167 -98 1879 -66 131 -98 295 -162 7033 -66 655 -68 1025 -100 261 -66 295 -66 195 -132 65 -66 9407 -100 4337 -68 795 -100 361 -132 99 -66 4915 -198 819 -100 327 -64 1611 -132 429 -100 331 -66 4037 -68 729 -66 827 -398 2751 -66 295 -66 4365 -98 1187 -66 389 -100 231 -166 1945 -66 431 -450 587 -458 587 -458 587 -458 587 -458 625 -424 623 -748 309 -424 627 -392 657 -716 343 -720 307 -408 657 -694 365 -692 365 -694 363 -410 653 -382 651 -382 651 -716 321 -704 355 -702 357 -370 717 -368 673 -364 675 -16544 735 -352 673 -362 677 -356 709 -320 733 -320 733 -352 703 -680 385 -358 669 -354 693 -678 381 -638 417 -328 709 -646 417 -646 417 -648 413 -326 713 -326 713 -326 713 -648 415 -650 379 -652 409 -346 723 -346 721 -346 653 -16532 777 -296 703 -360 709 -326 709 -348 689 -352 727 -320 731 -678 385 -326 701 -354 691 -678 385 -650 381 -346 725 -652 385 -656 415 -656 381 -386 685 -352 689 -330 711 -650 385 -652 419 -648 385 -328 751 -320 727 -352 671 -16536 757 -318 709 -320 731 -322 695 -352 725 -322 727 -322 731 -678 381 -348 705 -320 697 -676 383 -670 381 -346 687 -678 385 -680 383 -682 385 -326 717 -324 717 -326 711 -672 385 -650 415 -650 379 -330 737 -324 749 -320 699 -16538 747 -328 681 -354 705 -320 733 -320 729 -320 729 -354 703 -678 377 -358 677 -356 703 -642 385 -680 385 -328 711 -680 385 -650 417 -646 417 -326 713 -326 711 -326 713 -646 417 -650 379 -652 411 -344 725 -346 723 -346 653 -16538 707 -404 657 -376 653 -380 687 -382 655 -382 691 -380 679 -668 395 -366 671 -360 675 -682 381 -650 383 -358 687 -680 383 -682 383 -682 385 -326 715 -326 713 -326 715 -682 385 -650 379 -654 409 -346 723 -346 723 -348 651 -16540 743 -324\nRAW_Data: 713 -324 717 -356 687 -354 687 -354 727 -320 729 -682 379 -350 671 -354 693 -678 381 -672 379 -350 687 -678 381 -672 381 -674 413 -348 667 -356 691 -354 689 -678 381 -672 379 -672 381 -350 721 -320 727 -354 671 -16544 747 -328 689 -354 703 -320 731 -320 729 -320 729 -322 733 -680 385 -326 701 -354 693 -678 385 -650 381 -344 693 -686 387 -670 387 -670 385 -350 685 -352 689 -350 695 -672 363 -688 393 -660 395 -328 741 -332 711 -360 685 -16498 781 -328 679 -354 707 -320 731 -318 729 -320 731 -320 733 -680 373 -360 687 -352 705 -642 389 -648 419 -326 707 -646 419 -648 417 -648 415 -326 711 -326 711 -324 713 -646 419 -648 381 -652 409 -346 725 -348 719 -348 651 -16530 775 -298 703 -360 709 -326 711 -326 713 -354 723 -320 725 -646 409 -358 677 -354 707 -644 385 -650 417 -328 709 -646 419 -648 417 -650 415 -326 711 -326 711 -326 711 -648 381 -684 379 -652 407 -346 723 -346 689 -382 651 -16542 739 -316 715 -324 715 -350 689 -354 723 -322 725 -322 729 -680 375 -358 677 -354 705 -644 385 -680 385 -328 713 -678 387 -650 415 -650 413 -328 711 -326 711 -326 713 -648 381 -684 379 -652 407 -346 723 -346 717 -316 705 -16506 781 -288 717 -354 689 -356 687 -354 723 -320 725 -320 731 -680 381 -348 705 -320 695 -676 381 -672 381 -346 687 -678 381 -674 381 -672 419 -324 695 -356 687 -354 687 -678 385 -650 381 -682 379 -346 727 -346 721 -346 653 -16534 739 -346 693 -342 699 -344 699 -342 731 -346 687 -382 685 -686 383 -352 683 -352 689 -680 357 -664 391 -362 687 -654 409 -662 395 -662 393 -366 671 -360 679 -354 703 -646 419 -646 385 -648 417 -328 711 -354 725 -354 671 -16506 783 -316 705 -322 729 -320 727 -320 727 -320 727 -352 703 -678 377 -358 677 -354 707 -644 381 -672 383 -346 691 -678 381 -672 415 -638 419 -326 699 -354 689 -350 689 -678 381 -672 381 -672 383 -348 721 -322 727 -354 669 -16544 747 -328 689 -352 703 -320 727 -320 727 -322 731 -322 733 -680 385 -326 701 -356 691 -678 385 -650 379 -346 689 -686 387 -670 387 -654 417 -352 683 -354 689 -346 687 -656 377 -686 379 -666 393 -362 687 -364 715 -358 649 -129582 163 -694 263 -100 197 -68 199 -166 199 -100 131 -200 65 -662 629 -66 625 -164 65 -100 987 -66 587 -66 163 -66 95 -66 4701 -300 1593 -66 587 -98 587 -98 1907 -100 4575 -264 195 -296 1449 -132 161 -132 195 -66 359 -132 129 -66 5365 -66 65 -132 595 -100 731 -134 299 -68 1361 -100\nRAW_Data: 3975 -100 1023 -98 197 -132 887 -98 195 -100 261 -100 229 -132 1677 -98 5099 -132 1973 -98 2701 -130 4541 -102 165 -66 163 -66 163 -132 591 -66 791 -66 199 -102 4581 -68 1487 -100 161 -98 2113 -66 469 -66 3741 -100 4299 -66 163 -64 391 -166 65 -66 2619 -100 399 -66 4573 -66 2117 -132 6427 -66 1019 -132 529 -66 165 -66 797 -200 165 -102 199 -66 4713 -68 3163 -232 993 -100 363 -98 267 -66 5573 -98 263 -98 261 -66 197 -66 131 -100 2179 -264 4751 -166 467 -100 1727 -100 233 -100 2019 -100 4073 -66 861 -132 1359 -134 201 -66 235 -430 297 -66 599 -68 727 -100 263 -68 265 -68 4727 -100 499 -64 199 -134 65 -66 329 -100 97 -164 525 -64 485 -64 5219 -66 1149 -66 229 -230 1713 -66 131 -98 97 -66 1053 -66 5951 -66 2599 -100 131 -132 955 -132 259 -132 97 -66 99 -98 691 -66 889 -66 4877 -132 1543 -66 1147 -132 65 -298 793 -100 1329 -66 563 -66 595 -232 231 -66 165 -134 4975 -100 791 -100 231 -66 793 -68 1393 -100 463 -132 597 -132 165 -100 4941 -98 425 -132 165 -132 65 -68 231 -66 1033 -66 1003 -132 5379 -166 1745 -66 5759 -66 719 -200 2589 -66 5447 -66 2057 -234 1721 -100 467 -66 5485 -100 65 -66 1187 -134 599 -68 1131 -64 5741 -230 131 -66 819 -100 297 -68 133 -68 231 -132 5413 -100 131 -166 1157 -264 331 -66 727 -66 1283 -66 1513 -98 593 -66 265 -102 233 -66 797 -100 165 -134 665 -100 791 -132 99 -168 5317 -100 299 -100 929 -66 429 -66 835 -100 763 -100 165 -100 3941 -68 231 -68 699 -68 231 -68 1091 -66 299 -100 265 -100 365 -200 531 -66 761 -100 365 -68 5283 -100 299 -100 563 -166 999 -66 657 -132 5585 -64 131 -98 97 -132 361 -66 555 -98 293 -66 1247 -66 521 -196 263 -66 497 -132 6527 -66 399 -66 729 -100 197 -68 6605 -66 1115 -132 919 -98 7149 -66 1297 -66 197 -100 1151 -98 463 -266 197 -98 427 -68 891 -68 3379 -66 397 -68 67 -100 297 -98 983 -232 165 -132 883 -68 3773 -66 263 -64 653 -166 795 -100 397 -98 5091 -68 429 -66 297 -164 227 -98 621 -164 691 -130 2735 -64 1513 -98 299 -66 963 -166 333 -66 993 -100 4049 -66 555 -100 293 -98 815 -64 133 -66 729 -66 165 -66 1493 -66 1183 -234 165 -66 267 -202 1031 -100 693 -64 261 -66 261 -66 4057 -98 327 -98 425 -98 291 -100 65 -100 931 -68 297 -68 527 -68 463 -66 5135 -66 163 -98 5887 -4482 477 -1448 469 -1438 479 -1430 485 -518 467 -1440 451 -532 451 -1440 477 -1444\nRAW_Data: 471 -1474 451 -1446 481 -518 449 -1446 483 -514 475 -520 445 -502 491 -546 453 -1470 449 -1450 473 -1432 473 -1444 479 -482 473 -1460 461 -1404 481 -1484 461 -1444 449 -1446 483 -1440 477 -522 453 -1436 479 -1414 479 -1428 473 -1444 481 -1444 471 -1440 481 -1426 473 -1446 483 -520 451 -508 491 -1402 481 -1448 469 -1474 447 -556 443 -538 467 -514 481 -518 449 -1432 483 -482 485 -1418 4439 -4500 485 -1452 479 -1408 507 -1412 479 -504 495 -1406 483 -526 449 -1432 479 -1444 471 -1450 477 -1448 469 -516 479 -1430 481 -522 449 -510 475 -488 503 -522 481 -1466 461 -1446 447 -1448 481 -1442 477 -484 481 -1440 477 -1410 481 -1456 459 -1438 479 -520 483 -506 481 -538 463 -514 479 -1408 469 -516 481 -514 509 -1440 477 -518 451 -1436 483 -524 485 -478 475 -1444 469 -514 477 -540 477 -1438 475 -520 485 -1402 485 -1420 487 -1436 473 -512 463 -514 479 -1404 4423 -4516 489 -1442 483 -1420 473 -1440 471 -506 481 -1426 471 -512 471 -1408 483 -1484 463 -1442 475 -1448 465 -514 481 -1438 477 -484 481 -512 473 -490 499 -548 481 -1420 469 -1442 487 -1418 481 -1446 463 -516 479 -1408 471 -1442 449 -1486 461 -514 481 -524 491 -510 479 -1404 509 -1410 481 -500 491 -478 475 -1476 479 -520 489 -1434 451 -526 489 -1402 483 -524 487 -1404 479 -490 481 -1468 485 -1426 473 -1440 487 -524 451 -1454 477 -508 463 -1406 483 -492 483 -1406 4439 -4518 491 -1438 481 -1420 471 -1442 487 -522 451 -1418 479 -506 495 -1404 481 -1444 503 -1418 479 -1446 469 -516 481 -1438 479 -486 483 -506 475 -520 473 -522 479 -1464 459 -1436 475 -1444 467 -1442 481 -496 491 -1404 481 -1412 481 -1462 457 -1468 481 -514 475 -1412 483 -512 475 -1444 465 -1408 483 -1414 481 -538 507 -516 485 -512 477 -514 475 -492 503 -490 483 -1414 481 -1426 487 -514 509 -516 451 -542 475 -1430 479 -1414 479 -504 493 -514 475 -482 473 -1428 4417 -4510 483 -1440 481 -1452 475 -1438 461 -512 479 -1416 487 -506 489 -1406 479 -1448 487 -1444 459 -1434 479 -520 487 -1414 477 -518 473 -502 479 -504 491 -546 449 -1466 485 -1416 471 -1438 481 -1414 493 -514 477 -1406 471 -1442 483 -1456 461 -518 483 -1446 471 -514 479 -520 485 -476 477 -516 473 -1428 481 -1440 481 -1448 473 -512 473 -1436 481 -1426 473 -512 481 -1416 481 -1426 473 -1444 475 -518 507 -1422 491 -1436 481 -496 489 -1404 481 -1418 481 -502 491 -1400 4449 -4492 493 -1438 481 -1422 469 -1444 487 -522 449 -1450 471 -486 475 -1440 483 -1454 459 -1440 479 -1446 469 -516 481 -1436 477 -486 483 -510 475 -488 501 -518 421 -129440 263 -162 65 -362\nRAW_Data: 129 -264 65 -266 199 -132 67 -332 99 -98 3287 -100 4663 -132 4127 -98 331 -68 99 -66 5149 -200 1759 -66 65 -100 1051 -132 619 -66 5371 -132 1873 -232 195 -130 657 -132 357 -66 229 -66 4517 -100 365 -64 2447 -132 261 -66 633 -100 1153 -100 593 -132 259 -66 1589 -134 229 -66 65 -66 99 -66 491 -66 3789 -100 2059 -102 301 -66 663 -98 327 -98 5013 -98 4239 -164 987 -98 197 -66 163 -66 1479 -100 897 -66 99 -166 827 -66 465 -100 67 -100 563 -100 5179 -66 2043 -100 985 -66 1355 -66 431 -66 729 -232 165 -352 689 -346 685 -364 709 -322 709 -354 705 -354 701 -680 377 -360 675 -356 709 -644 385 -682 383 -328 711 -680 383 -648 415 -648 415 -328 711 -318 717 -326 713 -680 383 -650 379 -684 377 -330 741 -324 717 -354 691 -16506 751 -352 703 -322 693 -354 693 -354 725 -320 729 -322 731 -680 383 -356 669 -354 695 -678 385 -650 379 -344 691 -696 363 -686 387 -686 383 -352 685 -350 691 -346 699 -668 365 -684 367 -682 379 -366 685 -362 709 -360 677 -16540 731 -354 667 -352 699 -350 689 -362 711 -322 713 -354 707 -680 387 -326 737 -320 691 -676 383 -638 417 -316 715 -676 387 -648 421 -644 419 -326 711 -326 709 -326 713 -642 421 -648 415 -618 413 -346 725 -348 721 -314 685 -16528 775 -298 733 -326 709 -326 711 -324 715 -350 725 -320 731 -646 421 -326 699 -354 689 -678 383 -638 415 -318 711 -678 387 -648 417 -648 417 -326 715 -324 707 -318 717 -678 387 -648 381 -684 379 -346 725 -346 719 -348 651 -16544 741 -352 691 -354 687 -356 691 -348 691 -326 747 -320 743 -646 407 -330 725 -320 707 -644 389 -648 423 -296 735 -646 421 -648 417 -646 417 -326 707 -326 709 -324 711 -646 421 -648 381 -652 411 -346 725 -314 753 -316 683 -16546 649 -538 489 -574 455 -574 487 -542 557 -442 593 -474 575 -802 263 -464 599 -428 605 -752 319 -716 321 -422 639 -720 351 -720 319 -730 361 -366 669 -362 673 -356 705 -684 345 -686 381 -682 383 -326 715 -356 723 -354 669 -16540 747 -330 715 -320 707 -320 731 -320 729 -322 729 -354 701 -680 377 -358 675 -356 707 -646 381 -672 383 -328 717 -678 385 -680 383 -648 415 -326 717 -326 713 -326 713 -646 417 -652 379 -686 375 -346 723 -346 721 -346 653 -16542 775 -316 693 -354 687 -348 699 -354 691 -348 719 -332 711 -658 403 -354 677 -356 707 -644 385 -682 385 -328 713 -646 419 -650 415 -648 415 -326 713 -326 713 -326 711 -646 417 -648 379 -652 411 -344 725 -346 721 -346\nRAW_Data: 653 -16540 741 -348 691 -348 685 -354 687 -350 697 -378 705 -332 719 -652 405 -352 677 -354 705 -644 385 -682 387 -326 713 -646 419 -648 415 -650 415 -326 713 -326 713 -326 713 -646 381 -684 379 -652 407 -346 723 -346 719 -314 705 -16508 781 -290 715 -354 687 -354 689 -354 723 -320 729 -320 731 -646 419 -324 701 -354 689 -680 385 -650 379 -350 711 -648 417 -648 413 -638 415 -326 715 -324 713 -324 713 -648 417 -650 379 -652 411 -344 725 -346 723 -348 651 -16538 745 -324 711 -324 717 -324 717 -354 687 -352 725 -322 727 -678 375 -360 685 -354 705 -644 385 -648 419 -326 709 -648 419 -648 413 -650 415 -326 707 -318 715 -326 713 -646 419 -648 379 -684 379 -344 725 -346 719 -348 653 -16544 741 -336 697 -334 703 -332 737 -328 707 -326 717 -352 725 -646 415 -348 671 -352 693 -678 381 -638 415 -318 715 -678 383 -672 383 -674 383 -348 693 -354 687 -356 689 -678 387 -648 381 -684 379 -332 737 -326 745 -320 695 -16504 781 -286 737 -322 693 -354 691 -354 727 -318 729 -322 731 -680 381 -348 705 -320 697 -678 383 -650 381 -360 707 -648 415 -650 413 -650 413 -326 677 -362 675 -360 711 -648 381 -684 379 -654 405 -344 723 -346 685 -382 651 -16544 743 -350 689 -352 685 -352 691 -346 685 -364 709 -358 711 -650 407 -352 679 -354 671 -678 385 -648 417 -328 709 -648 415 -648 415 -648 413 -328 677 -360 711 -328 713 -648 381 -684 377 -656 405 -346 689 -380 687 -382 653 -16540 741 -352 689 -320 719 -354 689 -336 719 -322 711 -352 707 -680 407 -328 709 -322 711 -644 387 -680 385 -328 711 -678 385 -648 417 -648 415 -326 715 -326 713 -326 711 -648 419 -648 381 -652 409 -346 723 -348 719 -348 651 -130012 261 -132 261 -198 199 -130 791 -66 531 -66 231 -100 463 -66 299 -66 299 -100 785 -332 131 -66 267 -66 4069 -66 657 -100 589 -100 129 -100 523 -166 1325 -66 99 -132 4995 -98 429 -132 1151 -100 231 -130 361 -98 261 -66 1709 -98 165 -98 4413 -100 723 -132 63 -66 259 -98 359 -66 427 -68 665 -266 297 -330 961 -166 2625 -66 763 -98 65 -98 327 -166 463 -198 269 -100 529 -132 97 -98 227 -66 259 -162 327 -198 429 -66 295 -66 133 -162 3959 -234 789 -266 97 -230 827 -98 129 -100 97 -66 97 -66 361 -98 65 -66 525 -100 165 -64 757 -266 163 -166 3769 -166 563 -132 399 -68 563 -68 363 -66 133 -100 263 -66 529 -100 1065 -102 989 -132 1091 -100 729 -66 497 -98 133 -66 67 -98 565 -66 265 -234\nRAW_Data: 467 -166 499 -100 97 -66 4339 -100 165 -134 2121 -66 697 -134 531 -100 3557 -100 829 -130 461 -98 263 -300 199 -68 199 -66 733 -66 431 -132 365 -66 435 -66 637 -68 2411 -66 821 -66 65 -100 331 -100 693 -66 527 -200 131 -268 1959 -66 363 -464 5279 -132 199 -66 165 -200 329 -64 197 -98 1511 -132 525 -66 4411 -66 99 -100 131 -66 1161 -100 431 -66 165 -66 4607 -66 337 -68 267 -134 1623 -66 497 -66 295 -100 401 -100 65 -100 4201 -66 165 -68 233 -168 825 -100 599 -100 1857 -68 2819 -66 325 -164 359 -98 131 -298 99 -66 197 -66 4209 -100 1031 -66 131 -100 263 -66 267 -66 363 -100 1163 -132 633 -102 3097 -100 799 -66 265 -66 331 -100 763 -134 897 -100 333 -66 599 -66 467 -132 99 -66 3491 -66 821 -66 327 -66 557 -164 661 -68 165 -362 197 -98 757 -98 99 -98 525 -98 557 -134 1027 -132 197 -66 663 -100 991 -66 493 -68 131 -132 863 -132 529 -132 165 -102 397 -66 4095 -200 2929 -66 689 -66 397 -66 229 -98 163 -96 163 -98 227 -66 1063 -330 165 -66 495 -100 1021 -66 2569 -132 667 -134 331 -66 329 -64 555 -100 329 -66 227 -230 2225 -66 5081 -98 265 -66 669 -132 363 -100 265 -66 263 -132 731 -100 497 -98 199 -100 431 -166 233 -100 3069 -66 721 -132 329 -64 795 -66 535 -66 265 -100 531 -66 233 -3824 129 -706 305 -696 295 -688 323 -686 351 -644 353 -646 373 -644 375 -592 427 -584 421 -1078 909 -610 413 -602 399 -1080 451 -554 951 -1078 937 -552 455 -548 451 -1084 949 -1046 971 -516 481 -1046 449 -554 473 -542 475 -516 983 -1046 947 -552 451 -530 493 -1042 973 -522 473 -538 473 -540 471 -1040 975 -1006 477 -550 473 -508 1001 -522 487 -534 465 -548 451 -1044 447 -51358 455 -608 373 -628 365 -654 355 -652 353 -658 373 -640 373 -600 401 -618 389 -614 383 -622 415 -566 443 -580 407 -590 425 -1080 943 -564 413 -578 445 -1062 455 -548 943 -1078 947 -562 463 -548 451 -1044 967 -1050 983 -518 487 -1008 477 -524 485 -538 477 -510 1007 -1010 973 -556 453 -546 467 -1042 973 -542 447 -556 473 -510 475 -1044 979 -1016 499 -514 481 -526 991 -514 479 -522 483 -536 487 -1012 493 -51302 493 -3786 199 -670 339 -656 329 -684 321 -646 385 -628 373 -646 375 -626 365 -618 419 -586 417 -1088 909 -610 407 -590 427 -1082 415 -576 939 -1080 941 -584 425 -1074 941 -542 443 -1078 973 -1046 451 -548 445 -550 967 -554 475 -508 473 -1044 977 -1054 957 -1044 465 -516 483 -546 975 -520 485 -1048 461 -514\nRAW_Data: 483 -546 445 -552 975 -1018 979 -552 455 -1044 977 -51850 457 -4674 299 -704 305 -694 297 -720 323 -654 355 -626 385 -642 341 -632 401 -620 389 -1116 883 -610 413 -602 401 -1112 419 -552 943 -1066 937 -590 427 -1078 449 -556 951 -1044 475 -556 921 -1076 445 -550 981 -1030 985 -1012 485 -522 977 -528 495 -1010 479 -554 975 -506 479 -538 473 -548 453 -544 481 -1010 473 -554 985 -530 465 -514 481 -546 479 -1010 473 -556 475 -51320 459 -688 263 -754 225 -750 289 -742 297 -640 339 -658 365 -652 355 -648 353 -644 363 -640 375 -626 397 -586 419 -584 417 -1104 911 -578 445 -560 429 -1080 449 -554 937 -1082 941 -1082 947 -558 421 -584 449 -1034 455 -548 483 -516 479 -540 967 -1050 977 -520 489 -1038 443 -552 471 -538 967 -554 453 -540 467 -1042 977 -522 469 -544 477 -1016 481 -514 1009 -1014 481 -534 963 -1044 977 -554 443 -111144 233 -134 197 -196 131 -132 2559 -98 699 -98 233 -100 895 -100 897 -98 297 -230 295 -64 527 -66 895 -132 65 -98 4231 -134 667 -100 65 -66 297 -100 231 -102 563 -100 2053 -300 3873 -100 957 -66 291 -66 1225 -66 1095 -100 1029 -98 235 -100 4947 -66 131 -132 1159 -232 4881 -130 1649 -130 429 -66 463 -66 327 -266 133 -66 5073 -66 229 -100 697 -134 1353 -66 133 -130 4655 -264 299 -132 1065 -100 65 -100 199 -66 631 -66 1531 -98 233 -100 4353 -66 661 -100 233 -66 167 -68 1657 -66 267 -100 397 -98 761 -66 493 -68 761 -64 823 -98 525 -98 163 -66 197 -98 231 -66 167 -66 133 -98 953 -66 689 -66 735 -66 163 -98 229 -100 595 -168 195 -130 263 -132 957 -66 493 -98 65 -132 723 -98 431 -166 4275 -132 333 -100 97 -68 827 -66 491 -198 327 -264 4271 -134 1087 -100 797 -134 131 -302 695 -66 261 -236 465 -66 4191 -66 663 -100 433 -66 1325 -100 229 -132 295 -100 261 -100 431 -66 531 -130 195 -98 4647 -66 229 -66 359 -264 525 -98 365 -232 165 -264 457 -100 231 -66 629 -366 229 -66 3411 -68 1163 -132 763 -66 165 -66 1657 -66 393 -132 521 -262 5821 -132 1221 -98 129 -100 1345 -66 425 -198 165 -66 525 -66 299 -100 465 -66 497 -100 599 -298 4711 -100 397 -66 99 -132 627 -66 793 -98 327 -98 97 -68 265 -66 99 -98 265 -1152602 163 -1918 65 -330 197 -98 97 -198 721 -66 897 -232 4175 -166 299 -134 299 -132 331 -100 363 -66 499 -166 527 -98 1327 -68 931 -66 701 -198 265 -298 131 -100 301 -132 1029 -132 295 -134 3515 -66 197 -100 427 -100 99 -100 165 -98 231 -100\nRAW_Data: 4709 -232 163 -66 129 -96 1557 -100 229 -64 621 -100 4377 -100 1895 -66 2321 -66 4485 -66 167 -66 67 -66 1395 -68 65 -132 429 -134 1855 -68 231 -100 5991 -366 629 -100 2525 -68 233 -264 295 -98 853 -132 4433 -66 163 -66 457 -98 657 -66 331 -66 825 -66 785 -166 293 -66 559 -100 4747 -228 887 -66 895 -130 1457 -100 6935 -100 229 -66 329 -162 1051 -66 461 -100 97 -100 4427 -66 859 -68 299 -100 165 -100 1249 -132 197 -198 4129 -66 299 -268 927 -164 557 -98 261 -66 2419 -66 4739 -100 97 -66 2309 -66 97 -66 987 -198 325 -64 5109 -66 199 -100 1059 -66 723 -100 763 -100 5355 -98 201 -134 265 -100 165 -100 827 -66 595 -100 297 -100 231 -132 4787 -100 361 -100 429 -200 733 -66 633 -66 531 -330 4215 -68 1361 -200 5943 -66 1117 -132 953 -66 261 -132 199 -134 235 -200 261 -66 165 -132 65 -98 395 -266 197 -98 791 -134 229 -100 1195 -68 363 -100 99 -66 525 -98 759 -66 663 -98 163 -98 3411 -100 1033 -132 133 -134 133 -100 363 -100 663 -66 131 -66 331 -100 299 -134 65 -66 1025 -100 397 -66 2051 -66 697 -66 605 -390 415 -398 409 -400 393 -438 387 -410 387 -410 385 -448 383 -414 385 -416 387 -416 387 -448 387 -4030 787 -422 449 -810 425 -816 423 -838 417 -844 399 -852 805 -456 811 -412 837 -418 415 -814 823 -434 385 -878 385 -848 799 -474 383 -850 819 -452 405 -820 385 -844 415 -842 403 -848 809 -456 779 -446 417 -862 413 -814 417 -852 785 -452 799 -436 811 -450 415 -832 807 -444 411 -838 803 -446 407 -850 807 -410 807 -452 819 -418 817 -448 815 -434 809 -450 409 -840 817 -418 417 -818 417 -862 773 -476 773 -450 803 -446 409 -866 419 -848 793 -436 387 -838 419 -846 793 -438 415 -846 409 -846 811 -446 813 -422 423 -838 783 -448 413 -838 379 -866 807 -450 383 -862 807 -450 813 -412 415 -816 387 -15812 443 -358 433 -386 409 -386 447 -350 451 -382 419 -384 421 -384 419 -386 421 -386 421 -388 419 -388 419 -4024 837 -410 415 -814 421 -848 399 -822 413 -850 419 -824 841 -444 809 -422 815 -416 413 -842 801 -438 411 -844 409 -846 811 -446 413 -832 841 -414 409 -840 419 -812 425 -820 393 -866 811 -418 841 -430 415 -846 419 -820 405 -850 807 -418 819 -416 809 -454 395 -854 817 -450 419 -848 807 -412 413 -850 785 -450 811 -432 809 -412 837 -416 831 -458 811 -452 389 -854 787 -454 407 -824 413 -814 821 -448 815 -432 809 -450 395 -864 421 -814 827 -434 387 -840 421 -814\nRAW_Data: 825 -434 415 -846 421 -816 837 -444 809 -420 423 -806 813 -450 383 -840 417 -864 807 -444 381 -848 821 -454 805 -434 379 -806 417 -15798 421 -428 321 -504 321 -474 323 -508 293 -508 325 -468 323 -476 365 -444 365 -444 333 -466 359 -464 331 -4092 749 -490 351 -870 381 -882 361 -888 381 -838 411 -860 807 -450 815 -434 809 -414 417 -824 803 -442 413 -846 417 -854 773 -476 381 -882 793 -426 413 -844 387 -854 397 -826 417 -848 787 -462 813 -448 395 -864 419 -812 429 -818 821 -418 809 -448 783 -460 411 -846 785 -486 399 -850 811 -414 415 -828 801 -442 809 -450 789 -456 807 -452 785 -458 805 -450 411 -836 819 -418 419 -820 415 -830 805 -474 777 -450 815 -450 417 -852 413 -812 811 -424 411 -838 419 -844 795 -436 385 -878 409 -844 809 -444 813 -424 423 -838 777 -450 413 -842 399 -860 777 -448 413 -868 803 -448 791 -424 411 -812 413 -15768 435 -396 429 -358 435 -378 411 -410 413 -380 439 -394 409 -406 379 -412 407 -414 409 -382 413 -410 417 -3998 837 -414 409 -840 421 -810 427 -820 421 -838 421 -844 797 -472 805 -418 815 -408 419 -850 799 -438 415 -816 421 -852 799 -438 411 -874 803 -416 413 -842 403 -836 413 -842 395 -826 813 -452 807 -458 407 -846 419 -822 409 -816 839 -118766 99 -132 819 -98 917 -132 233 -100 167 -66 533 -66 525 -64 163 -98 947 -66 327 -66 425 -132 229 -66 331 -68 1949 -98 261 -132 261 -66 1187 -66 295 -100 5279 -68 595 -98 527 -66 1293 -68 97 -134 527 -68 4543 -66 229 -64 927 -332 199 -132 363 -264 825 -100 829 -68 565 -200 3743 -66 2027 -66 1425 -100 1301 -66 229 -196 99 -98 4645 -422 353 -446 385 -418 385 -416 387 -450 357 -450 355 -450 357 -450 359 -450 357 -450 359 -450 359 -4096 779 -448 771 -474 777 -444 779 -480 775 -482 367 -880 779 -446 387 -918 781 -418 819 -416 813 -454 779 -448 385 -882 379 -854 779 -484 385 -872 787 -454 793 -456 781 -448 385 -850 405 -854 779 -448 803 -458 785 -484 395 -854 787 -430 809 -446 383 -842 793 -486 383 -856 777 -478 379 -906 381 -824 803 -432 807 -446 785 -442 783 -482 785 -454 381 -872 375 -918 787 -418 815 -418 391 -860 785 -448 385 -876 787 -460 775 -484 387 -890 385 -834 777 -450 385 -852 417 -854 379 -856 381 -882 369 -876 777 -482 791 -454 771 -446 377 -886 779 -446 383 -874 769 -458 819 -450 785 -456 779 -420 383 -16402 383 -444 385 -416 385 -448 355 -452 355 -450 359 -450 355 -450 357 -452 359 -450\nRAW_Data: 357 -450 359 -450 357 -4096 777 -448 799 -446 761 -464 775 -478 769 -454 419 -846 799 -466 381 -876 799 -440 801 -440 775 -448 811 -448 379 -872 385 -848 803 -464 381 -876 781 -454 783 -448 783 -448 411 -836 421 -848 763 -494 775 -482 779 -480 379 -858 779 -448 777 -448 379 -866 787 -484 353 -886 785 -462 383 -878 411 -854 775 -444 769 -454 807 -454 789 -452 803 -446 377 -864 419 -878 779 -436 809 -414 415 -860 775 -434 383 -876 787 -486 765 -490 349 -906 379 -858 777 -446 383 -842 409 -856 387 -850 393 -856 391 -856 785 -518 763 -460 779 -446 385 -852 803 -430 381 -872 801 -452 783 -472 777 -450 785 -428 377 -16446 411 -422 355 -478 353 -448 353 -448 359 -448 361 -450 357 -450 357 -450 357 -450 359 -446 353 -450 361 -4086 807 -442 769 -454 809 -452 783 -448 773 -480 381 -860 777 -472 383 -908 769 -448 801 -452 779 -432 809 -448 365 -882 387 -846 795 -456 381 -910 787 -426 809 -446 779 -446 379 -862 407 -846 783 -472 779 -482 777 -482 391 -860 783 -446 779 -448 395 -852 777 -482 385 -872 773 -448 417 -888 385 -834 785 -456 777 -448 801 -456 769 -458 809 -444 391 -858 421 -878 777 -434 807 -444 377 -856 777 -448 387 -872 803 -450 801 -452 389 -886 391 -858 757 -454 381 -854 391 -856 387 -848 397 -866 377 -872 797 -488 781 -470 773 -446 369 -878 751 -476 383 -848 787 -482 763 -494 777 -448 789 -400 401 -16398 419 -418 355 -434 385 -442 351 -446 383 -450 353 -450 357 -450 355 -450 357 -448 359 -450 357 -450 357 -4088 805 -414 803 -458 781 -446 781 -482 783 -450 383 -882 771 -468 383 -884 783 -456 783 -446 779 -450 783 -456 417 -844 371 -888 777 -482 367 -910 769 -452 771 -460 777 -446 379 -886 385 -848 787 -456 807 -446 815 -482 379 -858 783 -446 779 -448 395 -852 775 -480 357 -872 803 -448 409 -890 383 -850 763 -460 779 -446 803 -460 777 -480 781 -448 381 -870 379 -914 769 -440 799 -434 383 -872 767 -486 383 -856 777 -478 771 -480 379 -890 383 -850 767 -462 385 -872 351 -882 367 -866 383 -878 365 -878 775 -516 767 -460 779 -448 383 -848 803 -464 349 -876 785 -486 763 -492 777 -412 787 -454 359 -16460 421 -392 415 -392 405 -412 379 -432 385 -440 351 -448 385 -416 385 -418 387 -450 355 -450 357 -450 357 -4092 775 -446 799 -454 769 -458 805 -444 769 -452 405 -882 785 -446 383 -884 813 -418 809 -454 785 -452 779 -448 375 -886 387 -848 787 -456 379 -908 801 -418 785 -452 813 -448\nRAW_Data: 379 -838 421 -846 767 -494 777 -480 777 -480 379 -858 779 -446 779 -448 379 -898 755 -482 385 -842 803 -446 409 -890 383 -850 765 -462 775 -446 801 -458 775 -484 783 -450 379 -876 383 -886 783 -474 775 -446 365 -880 779 -448 385 -850 793 -486 779 -468 381 -884 391 -856 779 -446 383 -850 383 -854 409 -854 355 -900 385 -848 805 -464 813 -452 779 -420 419 -844 763 -460 383 -876 799 -454 769 -494 775 -444 771 -426 411 -16408 355 -436 383 -444 383 -416 385 -418 385 -452 353 -450 357 -450 357 -452 357 -446 355 -446 387 -410 391 -4094 775 -448 789 -430 807 -446 777 -450 813 -450 385 -848 807 -468 381 -884 783 -456 777 -123134 99 -266 65 -594 99 -232 301 -134 2911 -134 861 -66 529 -98 569 -66 1593 -68 429 -66 5405 -132 263 -98 821 -66 397 -66 361 -66 491 -100 165 -66 495 -66 3677 -132 825 -68 529 -134 299 -66 165 -68 661 -68 203 -134 135 -100 429 -100 397 -98 859 -66 365 -166 399 -98 2811 -68 229 -66 567 -164 1153 -168 167 -134 265 -100 631 -66 1493 -132 65 -98 199 -68 4269 -132 365 -202 1493 -132 299 -66 131 -66 465 -66 231 -98 229 -96 165 -130 3593 -100 927 -100 231 -200 1825 -68 265 -66 1557 -100 65 -100 463 -68 495 -100 2591 -232 1027 -100 229 -98 461 -66 1847 -100 265 -66 4369 -270 131 -100 131 -66 1725 -66 397 -98 1659 -66 329 -132 6605 -234 397 -134 1427 -100 231 -166 5117 -66 663 -98 499 -66 1331 -66 931 -66 563 -166 4219 -66 265 -164 491 -166 263 -164 557 -100 1483 -100 361 -98 461 -66 497 -102 4575 -100 1327 -98 265 -234 131 -100 399 -100 565 -100 637 -100 463 -64 3975 -166 197 -202 333 -132 199 -132 693 -68 631 -66 165 -100 265 -66 297 -200 165 -166 199 -364 3717 -198 761 -66 197 -134 395 -66 99 -66 695 -198 797 -132 397 -200 431 -102 6087 -198 199 -100 399 -100 361 -100 333 -100 827 -166 1135 -132 333 -100 497 -68 3915 -130 363 -98 897 -66 99 -66 297 -230 395 -66 195 -66 5099 -100 687 -66 915 -66 165 -66 95 -230 1181 -98 593 -98 301 -198 4807 -132 431 -232 367 -198 629 -164 199 -68 65 -100 331 -100 431 -132 1397 -66 165 -332 297 -66 1131 -466 533 -132 463 -198 861 -98 293 -66 4561 -100 459 -66 691 -130 557 -292 393 -66 161 -66 425 -66 163 -130 1147 -96 4103 -100 399 -132 395 -66 429 -66 131 -68 197 -98 199 -98 167 -100 65 -232 67 -132 165 -68 235 -66 467 -68 1027 -66 4281 -198 261 -66 861 -166 231 -198 333 -66\nRAW_Data: 231 -196 1265 -100 133 -68 933 -100 7293 -66 331 -132 295 -68 1157 -98 261 -132 1223 -200 929 -98 1291 -68 3909 -132 199 -100 2729 -66 195 -134 829 -132 263 -262 329 -68 1559 -100 497 -66 5119 -64 949 -66 533 -166 397 -68 559 -98 263 -98 951 -66 3273 -134 665 -100 65 -100 231 -66 267 -132 267 -100 1131 -132 133 -66 129 -130 885 -66 193 -164 229 -264 65 -66 889 -130 259 -164 689 -66 4127 -130 229 -66 1545 -98 199 -100 131 -100 2059 -66 329 -98 197 -66 4261 -166 791 -98 199 -68 465 -66 233 -66 265 -66 2983 -64 327 -98 229 -66 2403 -132 259 -98 953 -100 1657 -100 761 -498 799 -66 433 -200 495 -98 229 -66 1179 -68 5479 -232 627 -66 693 -66 265 -66 265 -66 463 -100 231 -68 499 -66 133 -100 299 -66 4899 -66 195 -164 621 -130 229 -130 231 -98 261 -66 97 -334 397 -100 425 -98 729 -134 65 -134 367 -102 463 -66 3197 -132 893 -66 495 -164 461 -66 197 -164 1715 -100 663 -66 99 -166 265 -100 201 -98 233 -66 133 -100 829 -132 361 -68 561 -98 199 -66 431 -134 263 -100 2071 -100 661 -66 461 -98 227 -66 1149 -66 297 -98 631 -98 329 -66 463 -66 263 -66 951 -64 3587 -66 1413 -66 919 -66 689 -132 847 -132 459 -100 161 -196 4049 -66 301 -68 397 -134 427 -98 229 -100 263 -134 335 -68 397 -98 259 -100 823 -100 399 -66 165 -66 3571 -132 563 -166 231 -100 197 -132 867 -66 433 -68 761 -98 335 -100 99 -98 99 -66 1751 -230 1019 -100 765 -100 231 -68 365 -134 4643 -66 367 -68 297 -66 531 -166 1333 -66 531 -66 67 -98 959 -66 791 -66 3787 -66 261 -132 721 -362 133 -66 897 -134 629 -100 1395 -368 1127 -132 895 -100 463 -66 1793 -66 5239 -66 331 -66 1095 -68 433 -68 6899 -98 1527 -132 267 -66 67 -66 131 -100 495 -66 4027 -68 999 -132 959 -134 265 -66 199 -68 299 -132 999 -134 65 -66 497 -98 67 -66 265 -132 4751 -66 231 -98 263 -98 463 -66 99 -66 859 -66 327 -66 435 -66 2987 -100 993 -68 1025 -66 1491 -68 1095 -100 297 -66 4409 -98 787 -132 2569 -66 957 -68 795 -64 593 -100 335 -66 1675 -100 7041 -134 1727 -134 65 -100 1029 -132 1027 -66 4653 -66 65 -66 995 -100 1129 -66 267 -66 231 -68 469 -66 465 -134 4047 -68 165 -100 597 -166 729 -100 665 -66 1115 -68 293 -130 297 -66 1607 -398 393 -410 409 -410 415 -378 413 -412 413 -382 417 -396 419 -396 409 -382 409 -404 423 -408 385 -4052 417 -820 401 -820 809 -450\nRAW_Data: 823 -420 417 -848 797 -472 381 -876 791 -428 413 -844 817 -426 379 -878 385 -848 403 -852 807 -452 379 -878 801 -452 801 -438 385 -838 419 -846 399 -852 379 -850 315 -110416 645 -1548 1691 -486 1667 -518 1695 -488 1671 -498 1697 -508 571 -1588 583 -1570 561 -1574 1689 -504 583 -1560 1679 -520 587 -1574 1677 -488 1701 -484 1697 -520 1677 -492 1693 -488 581 -1566 591 -1574 601 -1546 607 -1568 577 -1566 581 -1576 579 -16394 591 -1548 1679 -518 1671 -520 1679 -516 1663 -514 1689 -486 583 -1580 579 -1578 579 -1566 1701 -494 571 -1568 1697 -514 563 -1570 1693 -506 1669 -516 1695 -496 1681 -518 1667 -506 573 -1576 603 -1544 613 -1568 597 -1538 607 -1566 581 -1580 577 -16400 619 -1534 1703 -494 1677 -480 1697 -506 1675 -518 1669 -518 587 -1580 581 -1562 579 -1576 1683 -488 589 -1582 1677 -524 583 -1570 1653 -510 1703 -518 1641 -518 1693 -502 1677 -516 579 -1570 579 -1578 581 -1566 615 -1566 585 -1564 585 -1580 585 -16408 595 -1546 1685 -518 1673 -490 1669 -548 1665 -518 1675 -494 587 -1552 595 -1570 605 -1556 1673 -520 581 -1578 1665 -520 581 -1558 1701 -484 1681 -516 1667 -516 1697 -508 1675 -490 603 -1570 577 -1566 593 -1572 603 -1576 575 -1568 579 -1578 579 -16400 625 -1554 1667 -500 1699 -482 1691 -506 1679 -518 1673 -520 585 -1566 579 -1560 611 -1548 1697 -488 577 -1602 1651 -518 583 -1578 1667 -508 1691 -522 1675 -512 1665 -502 1699 -482 605 -1574 585 -1566 601 -1570 577 -1576 581 -1576 579 -1566 579 -16452 591 -1534 1693 -508 1683 -518 1673 -520 1673 -488 1711 -514 575 -1562 589 -1570 603 -1568 1667 -518 589 -1578 1677 -486 605 -1570 1701 -484 1701 -504 1697 -514 1667 -510 1717 -518 581 -1578 615 -91426 233 -298 197 -332 593 -162 261 -98 131 -68 163 -692 821 -132 227 -64 131 -66 197 -132 163 -66 263 -68 363 -134 461 -98 65 -66 229 -298 295 -66 3173 -64 987 -164 425 -98 193 -98 693 -66 723 -196 391 -496 431 -100 229 -68 499 -100 63 -66 165 -66 397 -98 133 -100 533 -166 1179 -166 361 -100 3973 -98 825 -100 131 -98 723 -66 1053 -66 131 -68 197 -100 199 -100 331 -66 165 -100 429 -68 231 -66 567 -66 533 -100 593 -68 1463 -66 2093 -100 599 -66 361 -66 361 -132 261 -462 263 -66 621 -98 555 -100 131 -68 4609 -200 199 -164 1033 -66 863 -132 131 -68 331 -100 4565 -98 229 -66 1313 -130 589 -166 3959 -100 531 -100 263 -132 361 -166 229 -64 165 -66 231 -328 261 -66 229 -66 165 -100 235 -100 299 -132 265 -266 165 -66 4417 -100 261 -68 1625 -64 165 -98 229 -160 259 -98 99 -332 593 -166 167 -102\nRAW_Data: 199 -66 1063 -98 267 -100 263 -332 5513 -64 229 -232 685 -232 661 -98 431 -66 1021 -66 4085 -64 655 -66 2313 -66 531 -66 263 -100 197 -134 67 -132 901 -66 1591 -66 4217 -100 827 -100 165 -66 299 -166 1987 -166 4097 -66 365 -66 131 -332 131 -100 625 -66 361 -98 131 -196 1679 -66 163 -98 293 -100 2275 -98 1689 -66 825 -98 627 -164 297 -398 461 -66 4707 -66 795 -134 329 -132 597 -66 777 -6038 371 -1818 167 -668 693 -622 1573 -638 465 -1666 503 -1626 541 -1622 1635 -518 577 -1578 1669 -512 567 -1606 1667 -502 1677 -516 1693 -500 1673 -518 1675 -522 587 -1562 581 -1558 625 -1546 587 -1558 611 -1560 571 -1568 609 -16352 633 -1548 1689 -476 1707 -490 1687 -482 1709 -502 1675 -520 579 -1578 577 -1568 577 -1568 1705 -486 581 -1586 1695 -486 579 -1568 1679 -520 1673 -520 1669 -508 1707 -498 1677 -514 577 -1566 615 -1550 581 -1568 615 -1532 617 -1546 617 -1532 615 -16406 599 -1530 1691 -514 1695 -484 1679 -530 1659 -518 1669 -514 601 -1568 577 -1578 577 -1568 1699 -492 573 -1576 1707 -486 577 -1600 1655 -518 1669 -530 1691 -486 1673 -514 1691 -502 585 -1576 585 -1580 577 -1592 573 -1576 571 -1570 613 -1570 559 -16430 619 -1538 1703 -462 1723 -484 1697 -488 1691 -488 1715 -462 635 -1534 607 -1556 589 -1580 1685 -488 621 -1538 1711 -492 589 -1548 1717 -478 1693 -514 1687 -504 1693 -484 1705 -482 605 -1570 579 -1566 619 -1550 581 -1580 579 -1568 615 -1536 619 -16382 625 -1534 1709 -490 1683 -514 1673 -504 1677 -518 1671 -518 587 -1560 577 -1588 587 -1580 1681 -482 609 -1558 1677 -520 581 -1576 1663 -530 1659 -518 1673 -514 1691 -504 1697 -510 587 -1572 575 -1564 591 -1570 603 -1574 577 -1568 579 -1578 579 -16430 599 -1564 1693 -484 1695 -486 1675 -500 1713 -484 1709 -488 579 -1568 593 -1576 603 -1570 1665 -520 581 -1560 1699 -482 603 -1580 1677 -518 1667 -520 1671 -488 1711 -482 1699 -516 599 -1570 577 -1578 577 -1568 579 -1570 617 -1566 581 -1560 587 -16424 629 -1542 1677 -482 1695 -514 1687 -484 1711 -494 1675 -514 571 -1584 587 -1572 599 -1542 1695 -520 585 -1544 1701 -510 593 -1568 1665 -514 1689 -506 1669 -508 1679 -528 1669 -520 579 -1576 579 -1568 581 -1582 581 -1578 579 -1568 615 -1564 585 -16412 631 -1540 1683 -516 1671 -510 1653 -506 1691 -518 1671 -514 601 -1570 577 -1578 577 -1578 1665 -498 603 -1566 1693 -514 563 -1576 1699 -506 1663 -514 1701 -478 1687 -520 1669 -500 603 -1568 605 -1558 589 -1574 601 -1542 607 -1576 577 -1576 577 -16434 597 -1534 1695 -516 1687 -488 1707 -486 1683 -518 1693 -496 583 -1574 599 -1542 605 -1578 1665 -500 611 -1550 1711 -478 605 -1558\nRAW_Data: 1679 -518 1675 -502 1699 -482 1693 -506 1679 -510 613 -1562 581 -1590 569 -1570 605 -1560 587 -1572 599 -1542 607 -16400 625 -1560 1669 -508 1677 -518 1667 -518 1695 -486 1709 -464 601 -1566 607 -1560 589 -1576 1691 -476 615 -1562 1677 -486 625 -1542 1709 -486 1703 -484 1697 -520 1673 -506 1695 -498 585 -1570 603 -1544 609 -1572 579 -1570 583 -1580 581 -1582 579 -16410 629 -1524 1715 -484 1679 -518 1673 -516 1663 -518 1697 -504 583 -1576 577 -1592 587 -1552 1681 -514 579 -1580 1681 -516 577 -1566 1705 -494 1689 -488 1705 -486 1695 -520 1675 -522 587 -1574 579 -1584 581 -1576 599 -1570 575 -1578 579 -1564 613 -89724 99 -1166 129 -1158 167 -532 163 -98 361 -198 299 -298 197 -66 2489 -66 3797 -66 1937 -66 1479 -98 557 -66 361 -66 1709 -66 6741 -98 1889 -132 5693 -68 7367 -100 8643 -100 2655 -100 3727 -66 1023 -66 865 -66 1161 -100 6287 -66 3987 -66 1961 -132 3307 -68 2183 -66 2429 -132 4179 -66 753 -66 461 -66 621 -64 5761 -100 331 -66 1683 -198 465 -66 629 -68 7341 -66 357 -198 261 -66 593 -234 331 -100 593 -64 293 -64 1611 -100 459 -100 297 -100 561 -134 1725 -66 6901 -100 1407 -68 8017 -200 1989 -66 5721 -100 199 -166 1251 -66 359 -130 9965 -66 4361 -100 819 -100 1051 -66 2187 -64 555 -66 861 -66 5889 -66\nRAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554\nRAW_Data: 487 -1556 453 -1570 441 -1590 1467 -556 469 -1538 1501 -522 1501 -552 463 -22124 1513 -514 1509 -548 445 -1582 461 -1568 443 -1574 1467 -536 465 -1574 1499 -516 1503 -556 441 -22150 1537 -484 1495 -544 479 -1558 459 -1574 447 -1580 1471 -548 459 -1576 1471 -554 1471 -550 459 -22160 1517 -516 1503 -546 447 -1574 473 -1546 481 -1576 1479 -520 467 -1572 1497 -550 1469 -556 441 -22156 1533 -528 1495 -530 469 -1568 445 -1566 485 -1548 1505 -544 451 -1572 1471 -554 1503 -516 495 -22160 1511 -520 1477 -546 483 -1542 485 -1546 481 -1542 1509 -526 455 -1578 1507 -544 1481 -524 485 -22146 1543 -510 1481 -552 475 -1546 473 -1568 481 -1540 1503 -522 473 -1556 1507 -558 1491 -518 479 -22164 1523 -520 1503 -544 453 -1566 479 -1550 469 -1572 1493 -546 447 -1576 1477 -552 1509 -544 451 -22178 639 -596 65 -926 63 -1954 237 -1680 355 -1670 383 -1636 1423 -596 439 -1578 1471 -574 1465 -576 445 -22182 1439 -676 1335 -702 339 -1648 387 -1648 409 -1608 1429 -618 419 -1612 1447 -578 1439 -614 425 -22186 1497 -530 1505 -556 471 -1540 479 -1582 431 -1570 1497 -546 481 -1536 1497 -554 1501 -556 463 -22178 1503 -566 1433 -586 433 -1604 445 -1576 441 -1580 1503 -548 453 -1572 1473 -578 1467 -558 469 -149080 311 -1034 895 -1084 939 -1006 337 -78790 97 -6890 2535 -28048 2511 -64482 2573 -88138 65 -6956 2515 -95140 2509 -61986 1589 -522 1475 -548 449 -1576 475 -1548 481 -1546 1475 -564 443 -1564 1477 -580 1477 -554 449 -22166 1503 -532 1481 -544 447 -1576 449 -1572 473 -1548 1473 -580 451 -1568 1463 -552 1477 -552 461 -22164 1521 -528 1493 -548 443 -1580 461 -1540 475 -1574 1473 -554 471 -1568 1489 -516 1497 -538 471 -22154 1385 -786 1245 -796 261 -1702 341 -1650 391 -1644 1411 -608 409 -1620 1415 -624 1409 -614 413 -22178 1533 -498 1519 -546 445 -1576 449 -1578 457 -1568 1503 -546 451 -1570 1467 -554 1503 -552 463 -22162 1401 -774 1257 -754 297 -1678 353 -1650 383 -1666 1397 -618 385 -1642 1437 -612 1433 -586 427 -22194 1483 -552 1477 -546 485 -1544 453 -1576 481 -1576 1479 -560 449 -1548 1507 -546 1479 -558 447 -22170 1413 -786 1245 -776 295 -1664 337 -1680 387 -1652 1385 -638 407 -1614 1441 -582 1451 -606 409 -22204 1503 -528 1507 -550 465 -1534 481 -1584 429 -1572 1497 -548 447 -1580 1481 -550 1503 -546 455 -22166 1519 -552 1469 -580 419 -1612 419 -1596 447 -1582 1479 -554 447 -1582 1479 -550 1519 -518 479 -134730 169 -172 559 -194 579 -676 257 -87040 99 -7384 2269 -488 313 -37888 65 -12084 2515 -33298 2247 -474 309 -42656 63 -51970 2233 -456 309 -94658 2233 -430 325 -90692 357 -186 2417 -518 1517 -514 475 -1550 467 -1570 471 -1538 1499 -556 443 -1564 1483 -552 1501 -524\nRAW_Data: 481 -22144 1433 -668 1373 -666 337 -1680 355 -1634 383 -1634 1431 -622 399 -1606 1443 -582 1443 -586 431 -22166 1521 -512 1497 -548 447 -1580 459 -1568 447 -1574 1473 -554 465 -1572 1497 -516 1507 -554 453 -22148 1503 -540 1489 -546 479 -1542 449 -1578 457 -1576 1475 -546 483 -1540 1507 -552 1473 -548 453 -22154 1539 -516 1469 -586 431 -1574 447 -1576 479 -1558 1485 -554 439 -1580 1501 -550 1457 -552 473 -22154 1521 -550 1485 -540 447 -1578 457 -1578 447 -1572 1471 -556 485 -1540 1483 -552 1501 -556 443 -22186 1437 -662 1345 -704 339 -1688 361 -1646 375 -1640 1399 -616 421 -1612 1441 -614 1435 -586 429 -22200 1513 -520 1503 -544 453 -1572 447 -1580 483 -1550 1475 -552 475 -1548 1501 -554 1491 -540 475 -22184 1499 -530 1485 -548 457 -1570 477 -1578 443 -1576 1501 -546 449 -1562 1499 -538 1493 -572 443 -22166 1471 -612 1429 -624 401 -1608 417 -1620 431 -1600 1461 -582 419 -1610 1441 -582 1473 -548 459 -205658 1761 -436 863 -68558 2597 -23434 1775 -438 519 -94592 1803 -422 829 -86706 1727 -434 527 -66 319 -94536 1753 -444 865 -238 1489 -652 1385 -656 357 -1668 365 -1628 403 -1612 1439 -614 403 -1626 1425 -588 1443 -584 409 -22168 1531 -528 1491 -530 469 -1568 443 -1564 483 -1538 1505 -546 455 -1570 1469 -556 1503 -518 491 -22128 1455 -660 1359 -686 355 -1632 385 -1632 411 -1640 1411 -596 423 -1606 1437 -580 1443 -596 423 -22188 1495 -550 1467 -534 473 -1566 481 -1540 483 -1572 1477 -552 449 -1582 1477 -558 1471 -552 449 -22174 543 -3692 235 -1680 357 -1668 353 -1698 1375 -636 371 -1644 1409 -610 1437 -578 449 -22180 1499 -542 1491 -542 479 -1540 481 -1566 449 -1562 1499 -554 465 -1568 1463 -554 1493 -536 473 -22168 1405 -774 1257 -788 267 -1676 355 -1672 369 -1660 1387 -624 417 -1606 1427 -624 1425 -586 415 -22208 1507 -552 1489 -548 443 -1562 483 -1550 481 -1546 1505 -558 447 -1586 1473 -548 1481 -554 479 -22166 1383 -786 1245 -776 295 -1664 373 -1646 387 -1644 1403 -642 411 -1624 1417 -594 1445 -612 409 -22210 1515 -516 1503 -554 453 -1564 457 -1572 447 -1580 1501 -554 465 -1570 1467 -554 1493 -570 443 -195814 1353 -442 1267 -37900 99 -53710 411 -142 2207 -8336 2487 -46990 1301 -452 1317 -86162 65 -8376 1289 -444 1327 -94552 1311 -488 1251 -1726 1531 -544 1467 -580 417 -1578 457 -1578 443 -1576 1475 -566 441 -1568 1503 -512 1509 -526 455 -22168 1473 -582 1469 -538 483 -1538 459 -1574 447 -1580 1471 -582 429 -1572 1469 -552 1473 -548 461 -22170 1437 -640 1401 -620 393 -1636 385 -1634 409 -1604 1457 -588 415 -1608 1431 -590 1459 -554 449 -22188 1483 -556 1487 -542 445 -1578 459 -1576 443 -1574 1473 -556 469 -1570 1459 -550 1503 -554 443 -22160 1469 -590\nRAW_Data: 1469 -582 409 -1620 429 -1608 415 -1602 1455 -568 441 -1578 1475 -552 1479 -560 467 -22166 1483 -572 1463 -546 451 -1580 453 -1580 481 -1542 1475 -588 451 -1570 1453 -550 1503 -540 469 -22158 1505 -554 1475 -554 487 -1556 453 -1562 477 -1546 1495 -546 479 -1544 1511 -554 1477 -532 471 -22180 1471 -602 1437 -602 407 -1610 421 -1614 421 -1606 1433 -582 457 -1578 1475 -550 1469 -586 465 -22164 1515 -546 1469 -546 483 -1572 447 -1558 471 -1580 1471 -578 451 -1568 1495 -542 1497 -530 471 -22194 1433 -644 1393 -662 371 -1640 385 -1642 413 -1628 1431 -584 431 -1602 1469 -540 1501 -576 443 -22180 1479 -592 1439 -608 407 -1620 427 -1610 415 -1606 1465 -570 441 -1612 1441 -586 1451 -576 445 -22196 1533 -522 1489 -540 477 -1574 481 -1544 455 -1568 1505 -546 459 -1572 1501 -556 1497 -530 471 -210202 685 -540 475 -138 839 -138 239 -9950 2501 -71112 761 -462 511 -66 1297 -85614 97 -8850 745 -466 1821 -94546 827 -468 481 -140 815 -164 237 -32934 1583 -540 1477 -554 441 -1582 447 -1580 453 -1568 1465 -554 479 -1556 1487 -558 1477 -550 453 -22174 1503 -516 1483 -584 429 -1572 449 -1576 447 -1588 1475 -558 449 -1580 1473 -546 1485 -556 449 -22168 1469 -586 1443 -614 401 -1624 401 -1608 415 -1608 1463 -570 441 -1574 1471 -586 1449 -572 445 -22164 1401 -772 1255 -756 301 -1674 353 -1708 329 -1662 1397 -650 387 -1632 1405 -614 1433 -584 431 -22200 1489 -566 1463 -582 419 -1580 457 -1596 447 -1576 1473 -554 443 -1580 1469 -582 1451 -550 479 -22174 1387 -786 1251 -792 257 -1704 331 -1690 337 -1706 1377 -632 395 -1632 1433 -586 1451 -584 413 -22198 1541 -512 1485 -554 481 -1574 445 -1560 461 -1568 1495 -556 469 -1568 1495 -518 1503 -554 469 -22160 1541 -516 1501 -556 443 -1564 495 -1536 479 -1550 1517 -544 445 -1576 1507 -556 1485 -542 445 -22202 1501 -530 1503 -554 465 -1574 445 -1582 461 -1574 1469 -546 479 -1568 1471 -556 1513 -550 463 -22170 1473 -614 1419 -602 439 -1614 417 -1610 415 -1590 1465 -588 429 -1606 1469 -586 1455 -570 441 -22200 1407 -760 1277 -772 267 -1692 363 -1678 355 -1668 1393 -660 369 -1642 1441 -584 1449 -574 445 -22206 1533 -528 1497 -530 471 -1572 449 -1576 455 -1578 1507 -544 455 -1572 1503 -556 1467 -560 469 -22188 1439 -678 1347 -672 375 -1648 393 -1646 387 -1636 1431 -588 431 -1610 1439 -618 1437 -596 437 -143610 67 -510 99 -66 223 -272 173 -70 273 -204 235 -64 483 -26542 2521 -65448 297 -348 2417 -76720 65 -8388 65 -9450 203 -332 587 -72 885 -92 899 -27534 67 -39500 221 -406 521 -72 885 -98 919 -29920 1581 -534 1491 -548 445 -1580 461 -1544 477 -1574 1465 -556 473 -1568 1455 -554 1501 -524 483 -22136 1529 -498\nRAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017\nRAW_Data: 131 -534 99 -798 2211 -134 297 -66 361 -66 197 -198 97 -398 199 -100 163 -100 65 -464 133 -134 233 -68 229 -696 199 -200 265 -330 65 -1218 163 -166 97 -426 229 -698 165 -100 99 -134 165 -372 65 -1426 65 -266 99 -794 65 -832 199 -464 165 -698 99 -266 165 -794 99 -298 133 -734 165 -266 99 -864 131 -232 1893 -166 1465 -1484 263 -560 99 -230 65 -462 197 -66 97 -890 165 -130 97 -132 163 -232 97 -132 161 -162 263 -262 99 -362 65 -432 65 -830 97 -234 99 -628 199 -66 99 -166 101 -268 265 -594 131 -1030 97 -1156 595 -800 167 -1920 65 -464 165 -264 131 -66 167 -134 233 -632 329 -100 97 -166 199 -466 199 -98 267 -200 97 -266 199 -300 65 -496 563 -100 101 -166 165 -730 233 -166 131 -590 97 -232 131 -164 359 -428 3159 -792 65 -624 197 -98 229 -132 393 -398 199 -200 133 -366 99 -132 67 -528 265 -730 197 -166 65 -200 397 -1358 131 -1712 363 -1462 299 -132 67 -1792 65 -794 99 -368 131 -134 165 -134 67 -266 265 -430 2315 -98 1473 -100 129 -66 229 -198 261 -166 97 -426 97 -100 229 -496 197 -162 163 -392 231 -166 229 -660 131 -366 297 -166 299 -232 97 -332 301 -866 133 -596 267 -364 99 -502 99 -1988 131 -134 65 -66 165 -366 231 -266 165 -690 99 -234 65 -498 67 -166 133 -762 2459 -166 1019 -264 99 -200 265 -562 65 -328 295 -330 197 -360 131 -98 227 -232 65 -426 259 -166 99 -496 131 -500 399 -992 67 -432 133 -66 99 -1394 231 -1524 133 -430 65 -886 65 -1716 131 -2818 63 -430 229 -296 97 -1706 131 -164 65 -1478 131 -458 99 -1568 165 -756 197 -890 99 -1088 359 -660 97 -98 229 -166 65 -500 99 -266 65 -566 65 -432 365 -200 99 -798 99 -296 327 -1218 165 -132 99 -466 165 -168 133 -632 363 -1482 163 -802 131 -862 133 -1332 65 -100 431 -830 97 -596 165 -3422 99 -896 65 -766 97 -3766 65 -132 65 -1152 163 -1810 99 -230 97 -654 229 -458 165 -200 67 -166 297 -134 99 -4840 133 -762 67 -764 231 -1248 97 -1780 97 -3332 65 -100 261 -1186 99 -3360 99 -364 97 -1738 163 -294 97 -4918 129 -1550 295 -1028 199 -1264 99 -1560 99 -2156 167 -1556 231 -1852 167 -998 67 -1116 65 -562 167 -334 133 -266 165 -394 99 -166 97 -3082 99 -232 301 -5148 131 -502 67 -732 197 -434 199 -200 99 -432 65 -232 199 -464 133 -1060 97 -1582 97 -98 97 -924 129 -462 229 -560 65 -132 97 -166 161 -164 97 -166 229 -360 97 -132 63 -198 131 -162 525 -1714 67 -732 99 -958\nRAW_Data: 65 -1166 165 -200 199 -166 133 -100 65 -1294 99 -166 265 -992 297 -728 65 -332 431 -166 261 -132 65 -132 65 -98 363 -230 163 -430 165 -230 129 -660 129 -66 131 -398 2453 -132 1329 -166 1187 -232 1657 -166 499 -68 99 -132 233 -894 99 -232 99 -134 363 -628 67 -998 131 -234 99 -266 197 -398 97 -200 167 -334 197 -430 429 -168 263 -1160 99 -364 131 -264 97 -1318 131 -790 99 -432 131 -132 199 -100 131 -930 133 -1424 99 -398 165 -1486 299 -166 165 -334 2325 -66 795 -102 397 -100 533 -100 429 -98 131 -464 65 -532 329 -366 165 -332 99 -432 295 -132 99 -662 67 -332 331 -1226 131 -102 199 -562 469 -430 97 -232 63 -328 65 -1914 133 -466 131 -832 163 -1660 331 -464 199 -828 131 -696 65 -132 2139 -66 2051 -66 263 -790 99 -566 429 -428 99 -330 297 -698 65 -200 197 -68 461 -1292 131 -168 165 -634 131 -234 133 -594 163 -360 165 -700 99 -964 133 -1026 65 -532 197 -1520 263 -688 65 -888 295 -658 65 -824 67 -598 97 -426 65 -130 161 -132 63 -658 99 -64 165 -522 199 -298 65 -428 197 -560 65 -200 165 -100 167 -594 229 -100 297 -268 65 -100 459 -200 97 -334 2997 -200 597 -498 97 -626 97 -134 363 -166 333 -266 65 -664 331 -300 299 -568 529 -662 263 -626 67 -594 99 -266 165 -230 361 -462 67 -66 131 -694 397 -1490 167 -132 165 -768 165 -130 165 -466 99 -298 99 -952 163 -428 97 -296 97 -360 99 -196 65 -658 65 -4052 229 -6240 165 -228 65 -426 99 -994 99 -398 363 -1512 161 -2042 65 -592 131 -2866 65 -500 167 -5246 97 -462 67 -1156 99 -2428 99 -960 65 -132 99 -164 65 -166 329 -2484 263 -232 67 -1368 99 -996 267 -498 67 -4612 131 -4866 65 -100 363 -464 231 -264 231 -262 297 -1594 99 -1596 65 -136 133 -1662 67 -1694 429 -1290 197 -198 293 -858 99 -1976 165 -66 97 -460 131 -460 97 -328 163 -1624 397 -300 67 -166 97 -430 299 -300 299 -1792 65 -4438 131 -622 67 -500 131 -1064 65 -400 65 -426 261 -1644 131 -3468 165 -1684 99 -866 101 -66 231 -464 65 -498 133 -492 97 -1712 99 -98 197 -66 265 -1586 199 -1334 99 -1948 97 -200 99 -266 65 -666 131 -132 99 -264 195 -3066 99 -3512 99 -1746 65 -598 97 -6068 167 -600 99 -1262 65 -166 65 -134 65 -132 131 -1520 99 -1254 97 -1828 197 -1294 129 -66 65 -1248 97 -754 165 -6542 231 -662 197 -560 359 -98 97 -130 99 -3080 63 -952 129 -296 65 -230 63 -428 99 -1788 133 -100 65 -462 199 -66 165 -1094 99 -66\nRAW_Data: 167 -3190 265 -1128 195 -298 525 -166 65 -594 597 -664 131 -498 165 -232 65 -296 129 -296 65 -494 297 -498 297 -596 201 -332 2027 -132 593 -68 891 -66 893 -98 365 -396 97 -398 99 -300 199 -366 163 -598 99 -100 131 -234 65 -132 331 -532 165 -166 231 -1660 133 -532 265 -100 131 -100 131 -298 199 -396 199 -334 197 -166 67 -334 131 -1662 165 -298 97 -534 65 -628 65 -534 65 -266 97 -826 133 -696 65 -294 233 -198 165 -134 2081 -66 929 -198 167 -300 99 -232 297 -234 199 -662 67 -166 99 -66 233 -132 433 -232 265 -300 297 -132 65 -132 97 -496 199 -360 465 -132 65 -132 297 -460 293 -64 161 -262 97 -492 199 -200 233 -132 97 -396 99 -858 199 -864 65 -66 99 -532 131 -1608 297 -1550 163 -432 229 -66 163 -394 1953 -134 1491 -100 991 -66 165 -232 265 -130 299 -432 167 -724 363 -134 167 -200 231 -66 199 -98 395 -66 165 -266 99 -332 197 -268 265 -466 265 -166 493 -362 65 -132 99 -360 199 -200 133 -758 99 -330 165 -298 163 -428 199 -368 165 -334 131 -500 163 -986 97 -394 329 -196 97 -558 195 -230 97 -532 2373 -232 165 -232 795 -132 267 -168 363 -166 297 -298 131 -134 65 -466 131 -464 65 -232 67 -266 229 -364 231 -232 297 -464 65 -198 133 -398 529 -134 65 -364 163 -732 233 -298 299 -134 231 -66 129 -264 397 -132 131 -200 329 -430 131 -132 265 -1292 65 -362 131 -264 97 -1020 165 -398 131 -364 65 -300 265 -924 195 -396 331 -166 167 -132 433 -198 99 -464 297 -266 165 -396 131 -1226 165 -694 65 -230 197 -896 65 -594 2969 -164 461 -298 133 -330 133 -234 65 -134 131 -66 65 -68 97 -66 99 -132 231 -694 363 -298 267 -268 231 -368 131 -300 99 -134 65 -166 461 -396 263 -66 165 -298 131 -66 229 -1644 163 -1610 229 -658 97 -132 129 -162 97 -428 65 -66 129 -1912 65 -1194 233 -2218 133 -360 65 -132 229 -3428 65 -262 65 -3208 133 -1224 99 -296 65 -2212 65 -496 65 -200 199 -498 65 -1144 65 -1764 165 -1726 65 -434 65 -898 165 -2354 67 -134 99 -464 99 -792 263 -954 1161 -100 295 -296 131 -132 457 -462 363 -200 725 -298 131 -134 231 -68 531 -166 667 -298 99 -66 595 -794 165 -202 199 -68 99 -298 165 -68 231 -202 201 -200 97 -1360 197 -266 165 -334 99 -234 165 -200 165 -100 131 -664 65 -566 397 -100 165 -132 199 -132 165 -988 197 -394 99 -164 65 -294 99 -262 63 -1288 233 -132 199 -168 265 -64 99 -728 133 -1192 65 -334 165 -132 199 -200\nRAW_Data: 131 -400 65 -398 165 -866 131 -264 393 -1680 231 -1494 299 -894 265 -364 65 -132 131 -960 99 -296 165 -824 65 -1716 99 -664 97 -134 165 -1526 165 -634 131 -166 135 -466 231 -696 2331 -100 329 -100 961 -198 65 -164 199 -98 331 -132 65 -298 731 -66 163 -332 265 -894 65 -132 265 -300 97 -334 229 -100 65 -1026 233 -366 329 -922 99 -266 329 -428 97 -462 361 -758 163 -1886 65 -498 229 -558 65 -600 267 -68 65 -1592 133 -202 2299 -332 393 -132 329 -326 231 -232 97 -590 231 -136 201 -1226 265 -530 199 -134 65 -232 297 -398 65 -564 65 -1458 65 -500 99 -302 99 -1120 65 -66 131 -98 197 -66 133 -66 65 -730 99 -200 197 -730 165 -664 297 -1462 165 -664 133 -168 65 -168 133 -368 197 -466 1957 -66 1525 -266 131 -366 65 -394 65 -98 163 -296 99 -130 265 -696 65 -398 131 -1128 197 -134 67 -894 97 -296 231 -166 401 -98 131 -928 265 -398 131 -798 131 -298 165 -830 65 -230 231 -168 97 -1252 131 -590 65 -460 229 -692 97 -332 65 -366 265 -732 2297 -66 197 -66 131 -166 231 -100 99 -298 365 -168 361 -130 129 -66 161 -198 97 -626 593 -66 99 -98 99 -556 297 -132 65 -134 65 -526 295 -196 197 -68 163 -264 327 -130 855 -66 393 -132 131 -100 65 -98 227 -164 197 -758 131 -268 99 -430 65 -1686 131 -1652 197 -788 63 -726 165 -100 131 -264 197 -1026 197 -1250 131 -1650 65 -1980 97 -132 63 -1424 97 -198 99 -100 65 -1090 65 -398 99 -1686 99 -754 63 -1714 97 -1842 65 -2988 65 -4980 165 -1624 65 -232 67 -1464 65 -3048 65 -166 97 -1986 65 -1062 99 -862 131 -2732 99 -2360 97 -1620 65 -1326 65 -330 197 -3238 67 -266 131 -2968 67 -132 135 -1688 131 -1718 97 -1648 65 -166 65 -1292 65 -1908 99 -424 131 -1222 197 -798 99 -700 97 -1758 165 -1562 99 -396 165 -166 99 -98 65 -366 65 -762 131 -332 233 -498 133 -98 163 -562 65 -68 131 -858 65 -896 65 -366 233 -532 197 -168 65 -68 65 -630 195 -724 231 -296 361 -164 261 -2862 233 -232 99 -3186 67 -232 163 -496 165 -1620 97 -1622 131 -7022 267 -268 165 -466 63 -528 295 -200 197 -332 65 -1656 131 -5478 65 -1296 99 -130 161 -694 67 -1694 131 -734 99 -2590 99 -1056 65 -432 327 -66 165 -164 65 -1120 229 -1656 165 -1128 131 -1232 99 -1560 65 -568 131 -360 231 -1598 99 -3250 67 -1818 65 -1216 263 -3328 65 -1508 133 -2600 65 -528 65 -2418 65 -460 97 -100 97 -164 65 -1560 133 -1822 163 -296 165 -1090 165 -168 133 -1264\nRAW_Data: 229 -1458 165 -66 133 -1626 197 -132 65 -394 65 -724 229 -132 65 -200 263 -296 97 -264 131 -130 229 -132 161 -1230 431 -132 99 -234 233 -362 99 -698 67 -334 65 -366 299 -398 99 -328 131 -396 165 -1458 99 -926 2239 -66 763 -68 1727 -266 65 -364 65 -464 365 -366 97 -664 165 -200 265 -234 261 -264 395 -362 329 -690 261 -164 495 -232 131 -326 165 -264 99 -198 397 -928 299 -268 299 -166 99 -298 167 -436 65 -266 67 -498 231 -896 367 -428 97 -332 133 -628 165 -1394 231 -926 131 -232 3127 -132 921 -428 263 -66 97 -230 97 -1022 329 -430 97 -166 65 -198 63 -100 65 -66 97 -66 361 -132 67 -232 131 -268 99 -366 99 -98 331 -364 99 -428 97 -66 491 -164 99 -164 65 -428 131 -260 429 -1380 263 -1564 65 -300 65 -498 99 -364 167 -200 231 -264 67 -264 365 -266 231 -262 65 -724 65 -130 2213 -130 361 -166 2065 -166 165 -100 133 -264 299 -332 99 -98 229 -198 65 -328 131 -100 263 -628 131 -330 231 -98 65 -132 129 -264 65 -130 163 -692 131 -66 99 -532 133 -234 99 -232 367 -298 401 -702 165 -498 399 -166 65 -132 131 -400 165 -1232 165 -1296 65 -232 165 -300 97 -396 2033 -198 463 -230 67 -68 563 -262 295 -330 197 -1190 197 -66 167 -66 263 -430 263 -100 163 -198 167 -102 165 -100 99 -234 67 -66 267 -396 197 -296 131 -1190 331 -200 67 -234 231 -530 263 -1610 263 -264 329 -196 129 -98 131 -132 227 -132 425 -526 129 -694 299 -856 63 -132 165 -166 165 -436 297 -1430 65 -66 99 -198 167 -266 65 -864 267 -996 65 -500 265 -1456 297 -726 163 -66 579 -1508 523 -1504 509 -1006 1033 -486 1513 -1514 507 -510 1537 -982 1011 -528 1509 -512 1495 -1034 999 -1016 999 -516 1539 -1014 989 -1510 527 -486 1537 -1508 517 -1512 485 -1546 483 -524 1519 -484 1529 -60168 1539 -1506 507 -1006 1015 -1016 1007 -1512 509 -1006 1033 -506 1495 -1544 497 -1014 1007 -1008 1001 -1544 477 -1024 1011 -1538 485 -1512 525 -1010 1005 -482 1529 -1018 1005 -1020 1009 -1532 475 -540 1507 -512 1491 -59188 513 -1506 515 -1512 519 -1020 985 -506 1519 -1538 491 -504 1533 -998 1013 -516 1505 -522 1505 -1018 1005 -1002 1035 -494 1525 -1014 995 -1546 483 -524 1513 -1504 515 -1502 509 -1512 509 -514 1527 -514 1501 -60186 1539 -1506 509 -1008 1003 -1016 1009 -1520 515 -1014 989 -518 1509 -1544 485 -1016 999 -1016 1003 -1544 483 -1016 1017 -1506 515 -1534 475 -1036 987 -520 1507 -1012 1035 -1006 1017 -1502 503 -526 1503 -508 1533 -59160 523 -1508 507 -1502 537 -984 1013 -528 1505 -1504 503 -520 1537 -994\nRAW_Data: -1968 997 -4952 1011 -7896 1013 -1954 1005 -13860 985 -2000 979 -4952 1005 -4946 993 -7930 979 -1990 1009 -7908 1017 -1968 1011 -7900 985 -4946 1027 -4920 1009 -1964 1015 -4942 1009 -263646 67 -9708 99 -68890 97 -48712 65 -62028 65 -4652 65 -108870 99 -71758 99 -3200 97 -47548 65 -7036 65 -104448 97 -38184 99 -6502 97 -17756 65 -10136 20983 -1968 999 -4950 1005 -4940 1001 -7898 1025 -1966 1011 -7876 1017 -1966 1023 -7918 981 -4936 1027 -4946 975 -2006 979 -4946 1017 -4954 977 -7930 983 -1980 1003 -7920 989 -4952 1007 -1970 1013 -7896 1007 -1970 1015 -4954 987 -7900 1007 -1970 1007 -13860 993 -1972 1007 -4956 1013 -4926 1005 -7908 995 -1972 1029 -7914 987 -1986 1009 -7906 981 -4956 1009 -4930 1005 -1966 1039 -4938 1007 -4916 1009 -7918 1019 -1968 989 -7892 1017 -4934 1027 -1956 1039 -7898 985 -1974 1007 -4960 981 -7930 985 -1996 989 -13824 1045 -1946 1025 -4928 1009 -4942 997 -7926 1011 -1968 1003 -7894 1023 -1962 993 -7902 1017 -4948 999 -4948 1007 -1968 1015 -4918 1045 -4926 999 -7888 1039 -1972 1013 -7888 1005 -4948 1001 -1970 1011 -7886 1013 -1974 1023 -4942 1005 -7890 1033 -1952 1021 -13806 1047 -1964 1001 -4936 1009 -4950 1001 -7882 1031 -1962 1007 -7906 1021 -1966 1011 -7908 985 -4946 1027 -4918 1011 -1968 1017 -4948 1009 -4932 1015 -7882 1009 -1964 1013 -7900 1017 -4950 1001 -1970 1013 -7916 1013 -1944 1031 -4916 1029 -7888 1007 -1976 1005 -13848 1013 -1968 995 -4952 1003 -4946 991 -7920 1019 -1968 983 -7904 1015 -1966 1019 -7894 1011 -4942 1027 -4924 1011 -1966 1015 -4944 1005 -4926 1007 -7914 1007 -1972 1013 -7884 1013 -4940 1027 -1968 1007 -7906 1001 -1980 999 -4944 1013 -7880 1009 -2000 1003 -13830 997 -1974 1009 -4930 1029 -4938 1013 -7880 1011 -1998 1001 -7908 997 -1966 1025 -7902 1013 -4938 983 -4962 979 -1998 1011 -4932 1005 -4924 1011 -7922 1009 -1970 1015 -7890 1011 -4946 1003 -1970 1013 -7892 1013 -1964 1027 -4928 1021 -7886 1007 -1980 1001 -13846 1013 -1968 999 -4948 1009 -4938 993 -7926 1011 -1964 991 -7926 1011 -1962 993 -7898 1041 -4906 1009 -4960 999 -1964 1023 -4932 1007 -4928 1013 -7898 1015 -1968 1013 -7894 1017 -4948 997 -1968 1031 -7892 1027 -1970 1005 -4914 1039 -7884 1023 -1964 1011 -13838 1013 -1948 1023 -4924 1015 -4942 1025 -7872 1029 -1964 1007 -7898 1025 -1960 1025 -7898 1021 -4912 1023 -4920 1011 -1970 1013 -4954 1011 -4926 1003 -7910 1011 -1968 1013 -7890 1009 -4938 1023 -1982 1007 -7902 1013 -1940 1023 -4926 1011 -7922 1007 -1980 997 -13816 1045 -1964 1001 -4948 1005 -4938 993 -7904 1005 -1966 1035 -7888 1027 -1964 1011 -7906 1015 -4916 1027 -4920 1011 -1968 1019 -4944 1009 -4928 1001 -7924 1009 -1972 979 -7914 1013 -4948 1001 -1970 1013 -7892 1015 -1966 1027 -4928 1021 -7872 1035 -1968 1011 -13832 1011 -1980 983 -4956 987 -4948 1025 -7892 1005 -1966 1007 -7914 1021 -1962 1007\nRAW_Data: -7912 985 -4942 1023 -4926 1007 -1992 981 -4944 1035 -4920 1007 -7904 1025 -1962 991 -7934 981 -4948 995 -2004 1007 -7880 1007 -1992 983 -4946 1027 -7890 1007 -1982 999 -13830 1011 -1968 1025 -4926 1013 -4942 997 -7898 1027 -1960 1023 -7910 983 -2000 979 -7906 1015 -4940 1025 -4920 1011 -1968 1013 -225644 65 -2082 65 -155560 133 -5172 65 -1102 131 -48576 99 -24714 67 -6858 65 -1314 67 -38246 65 -64888 65 -4564 67 -59374 99 -20160 99 -17606 65 -42096 97 -11950 131 -29302 65 -19034 99 -32020 97 -366 65 -4430 131 -14620 99 -17318 65 -5556\nRAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236\nRAW_Data: 265 -100 133 -1464 99 -464 67 -592 229 -362 131 -262 163 -134 325 -164 163 -130 97 -164 65 -724 97 -132 97 -958 131 -594 131 -264 161 -98 229 -100 97 -890 131 -364 99 -198 97 -262 131 -234 2497 -134 65 -68 165 -400 563 -400 433 -496 99 -100 131 -266 99 -100 65 -200 231 -894 65 -166 463 -200 201 -1228 131 -266 65 -496 99 -300 97 -202 165 -896 365 -132 163 -1698 165 -132 99 -168 67 -166 67 -796 165 -400 131 -200 165 -796 165 -98 2161 -66 65 -166 293 -228 887 -66 395 -656 297 -100 97 -530 97 -66 97 -594 65 -428 97 -332 65 -400 131 -164 261 -66 299 -334 65 -596 97 -166 99 -1184 161 -460 131 -132 229 -398 299 -664 165 -722 165 -166 263 -300 99 -232 199 -266 267 -660 97 -758 133 -530 65 -632 99 -264 329 -1028 67 -166 2451 -562 427 -464 163 -198 229 -264 229 -790 295 -164 297 -132 263 -726 267 -66 99 -364 197 -564 99 -1162 67 -960 97 -198 163 -630 133 -66 163 -132 195 -1288 197 -430 197 -1546 65 -890 97 -394 231 -924 2173 -100 65 -594 165 -100 99 -528 99 -166 99 -298 497 -66 65 -100 131 -198 265 -362 131 -198 293 -294 129 -1150 263 -262 65 -100 131 -494 231 -166 231 -468 99 -66 165 -1262 197 -298 65 -298 397 -432 297 -498 361 -200 65 -466 131 -166 101 -496 231 -364 233 -64 197 -196 65 -426 265 -198 99 -1218 97 -200 67 -232 131 -200 167 -200 99 -66 165 -928 67 -1722 99 -764 65 -892 99 -232 133 -300 133 -1080 621 -1526 487 -1504 507 -506 1517 -1524 519 -1508 509 -1512 447 -546 1503 -1038 1035 -1482 547 -976 1007 -1208 823 -490 1503 -532 1481 -1048 989 -1534 499 -524 1507 -514 1499 -1014 1007 -524 1503 -1530 473 -60198 1533 -1516 481 -1040 985 -1044 981 -1554 479 -1530 479 -1044 1007 -1502 511 -1538 483 -516 1503 -1542 473 -1016 1033 -1510 483 -1542 505 -1002 1003 -508 1521 -1010 1015 -1512 507 -526 1515 -518 1505 -1016 1007 -59156 529 -1528 463 -1580 447 -542 1507 -1544 469 -1554 481 -1512 499 -518 1507 -1016 1007 -1512 511 -1012 1021 -1010 1007 -518 1529 -472 1555 -982 1023 -1502 505 -508 1537 -488 1545 -1012 1003 -480 1531 -1516 507 -60180 493 -12296 821 -1698 299 -1686 387 -590 1465 -1570 459 -1046 979 -1546 475 -1534 489 -1044 1009 -482 1529 -1032 973 -1538 513 -480 1537 -492 1523 -1012 1017 -59154 497 -11798 301 -704 1325 -1148 913 -1612 417 -1102 897 -1118 945 -586 1437 -568 1465 -1058 977 -1534 477 -550 1495 -502 1515 -1006 1037 -494 1527 -1510 489 -60174 531 -16794 355 -640 1399 -1608 449 -1050 989 -1540 461 -1546 477 -1068 973 -518\nRAW_Data: 1503 -1024 1009 -1536 473 -552 1503 -498 1517 -1010 1017 -59162 513 -9838 227 -1670 371 -604 1425 -1116 949 -1554 477 -1030 981 -1042 973 -550 1477 -532 1511 -1010 1003 -1544 511 -496 1515 -516 1509 -1018 1007 -488 1539 -1504 503 -60216 1525 -1530 485 -1018 999 -1012 1019 -1538 475 -1546 477 -1012 1025 -1506 517 -1530 505 -510 1507 -1512 513 -1010 999 -1516 507 -1520 513 -1012 1015 -506 1527 -1014 991 -1542 483 -524 1517 -482 1529 -1008 1005 -137640 165 -1660 165 -1362 231 -3114 67 -2286 131 -1652 197 -1448 195 -1648 97 -3534 163 -988 65 -556 65 -132 133 -2594 199 -2054 65 -1918 65 -1458 163 -690 65 -1134 131 -1194 67 -1158 131 -1656 199 -198 99 -298 199 -2676 67 -2122 231 -1762 263 -1560 131 -1228 99 -198 197 -830 99 -166 97 -6274 99 -1058 163 -1676 97 -1788 65 -5742 99 -3890 131 -2682 195 -166 65 -1394 265 -432 99 -368 99 -100 67 -198 163 -564 133 -992 131 -266 133 -962 233 -200 131 -196 231 -560 131 -66 65 -764 131 -1058 133 -564 99 -1792 133 -2820 65 -3480 165 -9536 99 -66 97 -626 97 -1828 199 -1860 99 -66 197 -1060 99 -1746 65 -1690 99 -198 99 -1660 165 -1764 65 -1558 199 -1456 99 -164 65 -1318 395 -5966 131 -928 165 -1026 99 -534 65 -434 99 -100 97 -100 97 -3272 65 -2168 131 -2904 231 -1564 67 -958 263 -3780 65 -166 99 -3506 99 -1594 165 -1492 197 -164 63 -1382 197 -1162 65 -364 133 -822 65 -498 495 -230 165 -366 65 -464 231 -666 231 -198 65 -332 163 -566 65 -164 67 -332 231 -232 265 -198 97 -1254 361 -200 65 -2056 129 -430 131 -100 65 -1588 263 -198 97 -2992 97 -528 297 -134 97 -368 427 -66 67 -132 65 -132 367 -330 65 -266 229 -66 65 -66 65 -198 231 -66 133 -528 363 -162 161 -230 525 -230 65 -230 461 -132 327 -330 295 -130 197 -230 165 -270 265 -464 65 -698 265 -264 229 -400 99 -3476 97 -1490 199 -134 99 -2970 165 -1618 167 -1598 65 -1162 99 -234 167 -1162 135 -464 65 -664 165 -1126 197 -362 97 -1222 65 -3496 65 -7024 131 -1592 131 -530 97 -6464 231 -998 263 -428 65 -698 131 -198 131 -1414 63 -1346 165 -1060 65 -1664 167 -566 263 -666 165 -566 331 -100 131 -1522 167 -368 199 -896 229 -232 99 -968 99 -168 231 -66 197 -68 131 -168 133 -400 131 -268 233 -596 233 -132 97 -494 131 -1710 197 -1616 67 -592 99 -1226 267 -268 65 -266 133 -1986 165 -332 99 -366 131 -2700 97 -1398 65 -130 269 -198 65 -1664 131 -3180 165 -992 65 -866 99 -166 163 -2782 65 -2354 265 -5176 65 -66 163 -1290 67 -164 99 -2584 67 -3084\nRAW_Data: 195 -364 65 -164 229 -958 63 -166 193 -130 65 -556 99 -332 199 -430 197 -996 297 -1426 235 -1160 2053 -166 1063 -100 501 -132 535 -198 67 -66 165 -98 165 -460 365 -366 97 -432 329 -264 133 -100 165 -328 197 -360 1087 -264 97 -166 63 -166 233 -98 195 -294 97 -264 163 -266 197 -100 359 -66 65 -1958 165 -694 99 -166 99 -596 299 -466 97 -66 99 -696 231 -1492 297 -1554 165 -1680 165 -368 99 -166 99 -168 65 -794 197 -194 2581 -98 1151 -592 99 -426 197 -328 295 -164 65 -232 163 -530 165 -264 129 -98 229 -294 493 -426 99 -66 163 -164 261 -264 129 -166 229 -68 65 -558 131 -132 65 -132 197 -1550 361 -98 67 -132 99 -132 131 -786 99 -198 301 -366 165 -530 99 -234 2159 -264 1691 -166 367 -132 231 -100 197 -166 97 -366 163 -68 131 -366 165 -268 133 -430 233 -100 133 -132 199 -932 561 -1416 231 -794 199 -1296 165 -564 165 -666 99 -490 97 -760 163 -1582 295 -464 267 -330 2561 -134 931 -66 65 -132 99 -264 63 -132 99 -198 97 -364 129 -460 65 -230 263 -164 163 -100 365 -100 131 -398 97 -530 65 -266 299 -1028 133 -100 97 -166 65 -296 65 -166 133 -498 331 -962 99 -98 199 -1328 165 -200 65 -234 99 -400 231 -632 65 -232 199 -530 65 -866 429 -958 197 -368 165 -668 65 -984 163 -100 129 -1088 259 -752 97 -522 131 -892 65 -298 67 -364 199 -132 65 -788 97 -396 363 -1052 99 -228 131 -98 99 -1526 463 -330 131 -898 263 -332 97 -996 163 -494 99 -2950 65 -798 131 -2490 165 -1424 65 -1694 65 -1822 65 -1756 67 -3820 65 -2536 97 -2410 65 -1030 131 -404 263 -732 165 -1566 197 -1554 199 -400 65 -100 99 -566 165 -3584 65 -764 101 -2630 65 -2896 163 -364 67 -100 65 -1228 263 -232 63 -884 65 -4092 133 -1622 325 -166 99 -2352 65 -500 65 -1324 99 -366 65 -1592 297 -5134 131 -1130 65 -1962 99 -66 99 -1454 67 -1130 99 -134 199 -134 165 -1222 229 -166 131 -464 197 -196 263 -234 99 -534 65 -132 131 -166 133 -134 199 -1590 231 -66 131 -1160 131 -300 65 -698 199 -462 133 -3446 99 -2876 65 -596 65 -1716 133 -2886 97 -134 199 -1628 131 -1790 67 -2556 615 -1510 453 -1584 443 -1566 485 -1014 977 -1054 1003 -516 1501 -1010 1007 -520 1495 -1034 1005 -1530 481 -514 1541 -492 1523 -1012 1001 -1508 521 -1016 999 -512 1527 -1506 509 -488 1521 -1040 989 -1538 473 -60194 1519 -1540 511 -1010 1001 -1020 1009 -1516 515 -1010 1001 -518 1507 -524 1503 -1534 493 -1510 501 -1022 1011 -516 1507 -522 1507 -1512 509 -1506 519 -516\nRAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297\nRAW_Data: -164 6323 -10770 65 -1420 227 -196 263 -198 197 -1086 99 -98 163 -164 163 -426 65 -362 97 -264 295 -132 197 -13586 65 -1808 131 -166 301 -66 465 -432 165 -330 65 -332 297 -962 99 -266 97 -166 265 -132 327 -198 329 -98 293 -14984 99 -862 131 -166 331 -68 165 -98 233 -132 201 -300 197 -364 133 -662 99 -398 99 -166 65 -432 133 -132 1447 -5882 197 -1082 65 -198 163 -1580 129 -264 67 -632 625 -134 165 -68 827 -100 165 -100 99 -164 3949 -9126 67 -164 131 -986 241 -534 309 -208 267 -226 247 -250 247 -248 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -246 247 -248 501 -258 247 -246 245 -248 247 -248 249 -248 249 -504 513 -226 243 -274 243 -500 513 -238 253 -256 253 -492 231 -262 485 -514 223 -244 527 -478 507 -274 255 -218 253 -498 261 -224 251 -250 503 -258 247 -246 243 -246 247 -504 515 -238 251 -472 277 -242 497 -246 289 -220 247 -498 513 -476 255 -68610 311 -208 235 -256 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 479 -274 253 -256 217 -254 247 -236 255 -250 247 -502 513 -240 251 -256 253 -254 249 -240 227 -252 249 -502 259 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -246 501 -226 245 -276 245 -246 245 -248 247 -504 515 -478 255 -244 495 -274 253 -476 511 -240 241 -69142 321 -218 243 -230 251 -248 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -506 257 -248 243 -246 247 -248 501 -258 247 -246 245 -246 247 -248 249 -248 249 -504 513 -238 251 -254 255 -490 499 -228 253 -250 249 -506 259 -248 499 -478 257 -244 495 -510 509 -238 255 -254 253 -494 227 -254 249 -248 505 -258 247 -246 245 -248 247 -504 513 -238 253 -472 279 -254 491 -226 253 -250 249 -504 513 -478 257 -68880 315 -216 241 -258 249 -248 247 -248 247 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 249 -504 257 -246 245 -246 247 -248 503 -516 477 -274 253 -256 217 -254 247 -236 255 -250 249 -502 481 -272 253 -254 255 -254 249 -238 227 -250 249 -504 257 -248 243 -246 501 -516 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -228 245 -274 245 -246 245 -248 247\nRAW_Data: -504 513 -478 257 -244 495 -274 253 -500 483 -232 265 -69136 287 -256 249 -238 227 -250 249 -248 249 -248 249 -248 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -250 247 -504 513 -238 251 -256 253 -496 499 -228 249 -250 249 -504 257 -248 499 -478 257 -244 495 -510 509 -238 255 -254 255 -492 227 -256 249 -248 505 -258 247 -246 245 -248 245 -504 513 -238 253 -472 279 -254 491 -228 253 -250 249 -504 513 -478 257 -68886 309 -208 235 -256 249 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 249 -504 257 -248 245 -246 245 -248 503 -514 477 -274 253 -254 217 -256 247 -238 255 -250 247 -504 513 -238 251 -256 253 -256 249 -240 227 -252 249 -504 257 -248 243 -246 501 -514 237 -252 473 -280 253 -250 241 -228 253 -248 249 -504 257 -246 499 -226 245 -276 245 -246 245 -248 247 -506 511 -478 257 -244 495 -272 255 -474 511 -240 241 -69150 323 -220 251 -242 229 -252 247 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 243 -248 247 -248 503 -258 245 -246 245 -248 245 -250 247 -250 247 -504 515 -236 253 -254 253 -496 499 -228 251 -250 247 -504 257 -248 499 -478 257 -244 495 -510 509 -240 253 -256 253 -492 229 -254 249 -250 505 -258 247 -246 245 -246 247 -504 513 -238 253 -472 247 -272 501 -246 253 -254 243 -494 513 -476 255 -68934 287 -254 213 -240 257 -250 249 -248 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -506 257 -248 245 -246 245 -248 503 -516 479 -272 255 -254 217 -256 245 -236 255 -250 247 -504 513 -238 253 -254 253 -256 249 -238 229 -252 249 -504 257 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -226 245 -276 245 -246 245 -248 247 -504 511 -478 255 -244 495 -256 243 -496 509 -274 253 -69130 309 -218 253 -256 249 -240 227 -252 249 -250 247 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -504 259 -246 245 -246 247 -248 501 -258 247 -244 245 -248 247 -248 247 -250 249 -504 511 -226 245 -276 243 -500 477 -272 253 -254 255 -494 231 -262 481 -514 225 -246 527 -478 507 -256 241 -244 243 -494 257 -244 243\nRAW_Data: -246 527 -228 243 -276 245 -246 245 -500 513 -238 253 -474 279 -254 489 -228 251 -250 249 -506 513 -478 255 -68936 287 -218 249 -236 255 -248 247 -248 247 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -506 257 -246 245 -246 245 -248 503 -514 477 -272 255 -254 255 -218 247 -240 255 -250 247 -502 511 -226 273 -244 243 -244 247 -246 247 -250 247 -506 259 -246 245 -246 501 -512 225 -244 527 -238 251 -256 253 -254 251 -242 227 -508 257 -216 527 -226 275 -244 243 -246 245 -248 247 -504 513 -478 257 -242 527 -238 255 -472 511 -242 281 -119614 299 -200 65 -470 65 -466 297 -630 97 -1592 133 -166 299 -66 231 -100 131 -98 265 -134 165 -166 433 -100 2287 -9916 231 -166 65 -98 199 -166 133 -166 165 -756 65 -724 195 -428 231 -260 263 -98 229 -130 261 -66 163 -264 65 -132 1181 -66 6315 -6798 131 -296 559 -334 131 -166 233 -132 165 -66 133 -264 99 -66 65 -366 99 -630 301 -166 97 -100 167 -164 535 -202 7269 -8266 197 -1022 131 -756 99 -98 99 -164 163 -990 65 -530 163 -230 297 -136 635 -66 2113 -8426 67 -6674 97 -722 197 -362 263 -232 165 -134 99 -234 297 -362 129 -198 131 -556 297 -68 167 -98 331 -200 165 -66 295 -66 8689 -4994 65 -1750 165 -762 163 -864 135 -100 167 -694 1093 -66 695 -102 99 -100 9899 -1650 297 -1216 97 -66 99 -396 65 -198 165 -164 233 -1658 199 -98 465 -134 463 -166 1883 -98 6283 -7302 99 -932 133 -696 263 -298 97 -98 165 -1708 131 -820 229 -98 231 -130 163 -590 131 -130 99 -66 97 -16220 261 -1062 265 -998 197 -1290 97 -362 165 -494 895 -264 7839 -7804 99 -66 99 -364 231 -630 133 -166 427 -496 131 -1252 263 -100 233 -66 133 -132 165 -66 259 -98 3109 -10438 101 -5322 99 -100 65 -666 65 -166 331 -98 197 -132 233 -662 261 -1516 559 -66 263 -130 689 -132 229 -64 3613 -15976 231 -166 133 -66 399 -264 99 -132 295 -366 97 -1692 99 -398 529 -68 397 -130 899 -164 3559 -98 1197 -12106 199 -98 65 -166 99 -266 99 -134 231 -100 133 -132 297 -430 99 -1394 299 -64 397 -166 99 -100 465 -200 331 -132 599 -100 2333 -15214 65 -1230 231 -266 265 -432 165 -398 65 -532 333 -632 65 -232 957 -98 9785 -6320 97 -830 167 -166 133 -732 299 -958 327 -98 197 -66 229 -164 327 -98 653 -66 7993 -6418 65 -1284 97 -458 129 -196 197 -166 393 -134 99 -332 427 -132 131 -66 133 -98 233 -66 133 -364 163 -566 4873 -16030 97\nRAW_Data: -360 65 -364 65 -68 857 -98 65 -232 131 -264 63 -98 391 -396 65 -130 99 -98 65 -66 861 -166 265 -166 7611 -10336 65 -1822 165 -300 165 -166 295 -134 199 -100 67 -264 165 -166 99 -500 99 -198 97 -200 165 -268 197 -130 65 -300 629 -166 561 -132 333 -132 7459 -6294 131 -1096 165 -964 197 -332 65 -166 129 -132 99 -130 99 -100 97 -134 65 -164 131 -494 165 -396 97 -164 131 -198 99 -232 229 -66 821 -64 131 -14954 97 -788 65 -100 263 -66 99 -300 65 -400 131 -198 293 -294 163 -132 65 -692 99 -132 131 -200 1847 -132 8773 -5968 133 -330 65 -66 295 -430 197 -166 565 -132 467 -98 65 -430 165 -262 131 -528 131 -296 131 -100 131 -66 557 -166 787 -98 3221 -16236 299 -166 133 -562 199 -1692 99 -66 65 -364 65 -366 231 -168 367 -100 5541 -14968 297 -164 97 -132 163 -328 99 -532 99 -134 131 -370 397 -66 397 -98 293 -98 197 -98 1151 -66 7019 -6746 129 -296 163 -954 261 -230 229 -64 231 -264 431 -100 99 -466 165 -100 333 -166 133 -666 695 -200 67 -134 397 -100 1667 -7686 97 -426 195 -266 97 -330 63 -98 99 -594 97 -132 133 -270 131 -600 131 -362 833 -98 297 -166 199 -66 99 -200 65 -66 197 -100 2963 -98 1125 -2238 199 -554 275 -242 273 -212 271 -242 241 -242 271 -244 241 -244 273 -244 245 -246 247 -248 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -246 247 -248 249 -250 247 -504 513 -228 243 -244 273 -246 245 -500 513 -478 257 -242 241 -244 273 -244 499 -512 225 -244 525 -228 273 -496 475 -508 507 -240 253 -254 255 -494 261 -222 505 -258 245 -246 245 -246 245 -250 247 -506 513 -478 257 -242 241 -244 273 -244 499 -68858 319 -218 245 -230 251 -248 245 -248 247 -250 247 -250 249 -248 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -250 247 -506 257 -246 245 -246 247 -248 503 -516 477 -274 253 -474 511 -478 527 -484 511 -240 253 -472 281 -254 489 -484 513 -478 513 -480 255 -242 523 -472 507 -276 253 -462 275 -240 491 -258 247 -244 245 -246 247 -502 515 -238 253 -254 253 -492 497 -230 253 -250 247 -250 247 -250 247 -250 247 -506 257 -68880 279 -248 241 -266 223 -252 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -506 225 -280 213 -278 245 -248 503 -260 215 -276 245 -246 247 -248 247 -250 247 -504 481 -272 251\nRAW_Data: -254 255 -254 213 -506 483 -514 271 -254 217 -292 217 -248 497 -516 237 -254 473 -278 251 -488 489 -516 477 -272 253 -254 219 -494 261 -260 481 -260 215 -278 247 -248 247 -248 247 -504 481 -514 235 -290 217 -256 253 -244 497 -69132 311 -212 235 -258 249 -248 247 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -512 479 -272 253 -474 513 -478 527 -482 513 -238 253 -472 281 -254 487 -486 515 -478 509 -480 255 -242 529 -480 509 -240 255 -496 271 -234 487 -260 247 -244 245 -248 247 -504 511 -226 243 -244 275 -498 479 -272 253 -254 255 -254 249 -238 225 -252 247 -504 257 -68882 319 -188 265 -280 235 -228 251 -250 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -250 249 -504 515 -238 251 -256 253 -256 249 -472 515 -480 271 -256 253 -218 255 -248 497 -482 271 -252 473 -280 253 -490 487 -516 477 -274 253 -256 217 -496 261 -224 507 -258 249 -246 245 -248 247 -248 249 -504 511 -478 255 -244 243 -244 275 -246 499 -69134 311 -208 231 -254 249 -246 247 -250 247 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -248 249 -506 259 -246 245 -246 247 -246 503 -516 479 -274 253 -464 519 -496 487 -516 477 -274 255 -498 249 -248 469 -514 479 -516 483 -488 277 -254 491 -498 487 -260 247 -498 227 -244 529 -228 243 -274 245 -246 245 -502 515 -238 251 -256 253 -496 497 -226 251 -248 249 -248 249 -248 249 -250 249 -504 257 -68898 247 -274 213 -312 185 -312 185 -312 185 -312 185 -312 185 -312 185 -310 185 -310 185 -310 215 -278 217 -278 215 -278 217 -278 217 -280 215 -532 237 -288 217 -290 215 -276 461 -260 219 -280 217 -280 217 -278 215 -280 247 -506 481 -270 251 -256 217 -290 213 -502 483 -514 271 -254 253 -218 255 -246 499 -482 271 -254 473 -282 253 -488 485 -516 477 -274 253 -256 217 -496 263 -224 507 -260 247 -246 245 -246 247 -248 249 -504 513 -478 255 -242 243 -244 275 -246 499 -69122 319 -220 243 -228 251 -248 247 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -246 505 -514 479 -274 253 -476 511 -476 493 -516 481 -270 253 -474 279 -254 487 -484 513\nRAW_Data: -478 507 -506 225 -272 495 -482 505 -254 241 -494 255 -242 525 -226 241 -274 243 -242 243 -496 509 -274 253 -254 219 -494 491 -258 249 -248 247 -248 249 -248 249 -248 249 -504 257 -68896 281 -214 241 -260 247 -248 247 -248 247 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -248 247 -248 247 -248 249 -504 513 -238 251 -254 255 -254 251 -474 515 -478 273 -254 255 -218 253 -248 499 -514 237 -254 473 -280 253 -490 485 -516 479 -274 253 -256 217 -496 263 -224 507 -258 247 -248 245 -246 247 -250 247 -504 513 -478 257 -244 241 -244 275 -246 499 -69128 321 -218 241 -230 249 -248 247 -246 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 245 -250 503 -514 477 -272 255 -474 515 -480 491 -518 479 -274 253 -474 281 -254 483 -486 479 -512 507 -506 223 -242 527 -482 509 -240 253 -496 273 -236 487 -260 247 -246 245 -246 247 -504 513 -238 251 -256 253 -492 495 -228 253 -250 249 -250 249 -250 249 -248 249 -504 257 -68860 273 -242 233 -256 249 -250 247 -248 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 247 -248 247 -248 249 -504 513 -238 253 -254 253 -256 249 -474 515 -480 273 -254 255 -218 253 -248 497 -516 237 -252 473 -280 253 -490 487 -514 479 -274 253 -254 219 -496 263 -224 507 -258 247 -246 245 -246 247 -248 249 -504 513 -476 257 -242 243 -244 273 -246 499 -68728 129 -604 249 -242 243 -276 213 -276 245 -244 245 -246 245 -278 215 -278 215 -278 247 -248 247 -248 247 -248 249 -248 247 -250 247 -250 247 -250 247 -504 259 -246 245 -246 247 -248 503 -514 477 -272 255 -474 511 -478 527 -484 481 -272 253 -472 281 -254 485 -486 511 -478 511 -480 255 -242 527 -482 505 -254 241 -494 253 -242 495 -254 241 -244 273 -244 243 -494 509 -238 255 -254 255 -492 489 -260 247 -248 245 -248 247 -248 249 -248 249 -504 257 -129658 99 -98 131 -132 97 -100 97 -132 131 -98 131 -496 297 -266 163 -198 99 -398 165 -626 133 -198 531 -166 67 -66 431 -132 331 -100 65 -132 99 -100 2725 -9960 65 -3686 295 -1552 99 -362 195 -100 887 -98 263 -100 1495 -8372 67 -298 99 -100 131 -332 133 -198 233 -398 65 -1060 99 -164 327\nRAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450\nRAW_Data: 937 -900 447 -454 969 -884 479 -466 939 -452 935 -454 981 -454 943 -452 955 -458 945 -452 979 -444 943 -486 945 -448 951 -464 977 -440 967 -478 935 -480 951 -456 969 -460 975 -450 973 -450 979 -470 977 -450 981 -910 485 -466 939 -928 489 -448 971 -940 445 -484 951 -928 485 -484 945 -948 479 -456 973 -918 481 -944 451 -928 471 -478 995 -912 487 -472 977 -15948 479 -444 1021 -910 521 -444 995 -942 471 -480 991 -450 1013 -452 1023 -446 1001 -484 981 -486 1007 -458 1015 -470 1007 -450 1033 -452 1005 -480 1005 -458 1013 -470 1007 -482 1015 -450 1023 -482 1009 -462 1015 -468 1011 -484 983 -482 1021 -944 487 -484 1015 -942 471 -486 1003 -948 503 -478 991 -948 501 -478 1031 -914 521 -486 991 -946 519 -916 511 -944 485 -474 1009 -974 487 -482 1015 -16224 521 -468 1005 -970 503 -478 1001 -954 505 -458 1035 -484 1019 -482 1039 -482 1019 -486 1031 -490 1009 -478 1033 -486 1033 -486 1011 -494 1039 -478 1039 -450 1049 -486 1033 -488 1005 -476 1071 -448 1067 -486 1017 -468 1045 -482 1045 -484 1015 -952 515 -482 1043 -944 519 -482 1049 -950 519 -454 1039 -956 523 -484 1011 -960 505 -486 1065 -956 509 -926 539 -944 519 -480 1017 -984 521 -454 1037 -16440 553 -440 1043 -976 507 -460 1069 -940 513 -486 1041 -480 1067 -482 1033 -476 1061 -472 1043 -510 1049 -486 1041 -482 1043 -482 1065 -476 1037 -486 1069 -492 1037 -484 1047 -504 1047 -486 1041 -484 1041 -514 1015 -520 1049 -476 1053 -490 1041 -980 519 -486 1043 -962 507 -482 1049 -994 507 -500 1043 -946 507 -516 1033 -982 517 -478 1049 -984 509 -976 505 -950 527 -490 1039 -980 519 -486 1047 -111258 195 -428 263 -162 163 -362 97 -132 65 -98 163 -132 825 -100 795 -100 1795 -134 587 -66 229 -100 1349 -164 3261 -66 2305 -132 2219 -66 5549 -234 497 -132 201 -66 667 -298 2369 -68 4381 -66 3909 -134 923 -98 723 -100 1651 -168 1197 -100 65 -66 199 -68 195 -100 197 -134 1135 -66 2787 -66 3163 -68 231 -68 197 -100 6675 -100 667 -98 1125 -66 67 -98 2423 -66 2017 -332 2949 -100 1129 -68 1655 -100 1229 -66 1285 -130 163 -132 1315 -66 525 -98 295 -100 131 -64 427 -132 2207 -98 1153 -66 99 -100 697 -98 1397 -166 863 -66 1393 -132 5005 -66 497 -100 1753 -100 597 -66 1667 -66 397 -100 961 -66 763 -134 859 -64 689 -98 1917 -134 199 -234 167 -100 131 -166 2061 -66 1521 -98 759 -100 983 -66 825 -166 459 -66 2049 -166 1615 -100 829 -234 631 -66 465 -66 1493 -68 433 -66 1623 -132 65 -100 1133 -132 3083 -66 199 -132 199 -68 1257 -66\nRAW_Data: 265 -68 1061 -98 533 -100 1233 -68 1721 -68 995 -100 2535 -66 4193 -232 727 -100 727 -100 2773 -66 133 -98 399 -134 233 -232 67 -66 497 -100 267 -132 1127 -134 1063 -66 565 -132 97 -132 523 -132 919 -66 891 -66 855 -98 495 -66 3363 -296 3199 -98 563 -66 133 -100 495 -98 1165 -134 1161 -166 1849 -98 853 -132 5647 -134 563 -98 1827 -100 131 -100 1125 -132 1659 -132 265 -68 1121 -66 465 -232 431 -68 3589 -98 197 -68 97 -164 1717 -66 1645 -66 397 -66 97 -68 231 -166 631 -100 627 -66 1757 -66 131 -164 527 -98 1285 -328 1213 -134 2059 -100 1791 -68 931 -66 1611 -66 1511 -66 2211 -66 2597 -100 2545 -98 197 -162 1089 -98 589 -360 495 -132 1685 -202 1095 -100 729 -100 2825 -100 231 -100 567 -100 231 -66 1027 -66 131 -68 525 -132 1613 -232 461 -232 1597 -66 627 -198 231 -98 131 -98 65 -100 1229 -68 2507 -64 1349 -66 195 -134 97 -66 1321 -100 855 -132 163 -132 1151 -100 1025 -164 329 -66 891 -98 951 -132 163 -166 591 -98 1149 -132 955 -66 1329 -98 923 -66 331 -64 4269 -66 797 -134 399 -98 267 -170 197 -100 429 -198 1225 -100 331 -100 231 -132 1463 -100 597 -164 331 -66 1863 -134 659 -98 5507 -100 719 -100 131 -64 655 -164 1579 -98 1423 -130 1381 -98 1317 -132 467 -66 495 -132 361 -132 4417 -98 631 -364 299 -100 1499 -132 267 -68 663 -98 691 -100 2433 -66 953 -98 721 -66 1355 -232 897 -134 897 -134 365 -100 267 -132 2059 -132 199 -102 797 -68 695 -66 601 -66 265 -68 499 -66 1327 -164 1355 -64 1279 -66 3257 -66 1351 -66 131 -96 359 -132 499 -232 623 -96 427 -68 1909 -98 591 -98 4671 -100 4541 -66 1491 -66 3347 -98 1277 -100 1679 -198 295 -130 357 -98 697 -98 865 -100 2817 -66 329 -98 787 -64 1117 -66 3313 -202 1721 -100 199 -100 399 -66 199 -132 1891 -100 235 -100 201 -134 765 -166 761 -132 1529 -66 629 -202 861 -130 3501 -98 1377 -100 1741 -164 1509 -66 735 -68 733 -66 265 -166 2015 -134 131 -100 663 -100 2995 -132 1577 -98 1885 -66 2461 -100 1189 -66 1425 -100 201 -100 1691 -100 199 -98 499 -166 233 -100 233 -134 661 -68 1393 -100 295 -164 2079 -66 1289 -66 329 -198 599 -100 465 -98 3995 -98 199 -268 2045 -264 199 -66 1593 -66 165 -68 1561 -164 629 -66 635 -100 1251 -230 2733 -66 1727 -66 629 -100 1229 -132 731 -66 163 -198 131 -100 693 -66 3223 -68 565 -132 1091 -134 531 -100 3223 -68 729 -100 1527 -134 895 -166 1265 -66 527 -100 201 -200 1463 -66 1233 -132\nRAW_Data: 2397 -200 167 -234 1803 -66 821 -100 1351 -66 1687 -100 165 -66 233 -66 1125 -100 2203 -132 197 -98 97 -66 593 -164 4187 -102 529 -66 1161 -68 799 -66 427 -232 263 -66 589 -68 495 -68 197 -100 525 -66 327 -98 427 -130 1551 -66 727 -102 133 -234 265 -98 459 -66 2337 -64 585 -68 297 -68 691 -98 1857 -134 665 -132 365 -66 931 -166 495 -430 689 -196 1191 -98 465 -100 931 -366 1351 -102 3185 -164 1151 -98 2465 -66 2193 -100 331 -134 165 -98 267 -166 1985 -98 889 -132 765 -66 531 -100 1449 -166 457 -98 1715 -66 299 -166 3131 -130 197 -98 817 -294 793 -100 97 -66 3415 -164 1019 -98 1675 -132 197 -100 133 -68 199 -134 3319 -298 297 -66 791 -66 1029 -134 2153 -100 1629 -132 1391 -68 1229 -100 665 -66 2039 -164 461 -64 261 -66 395 -202 395 -166 3159 -134 2253 -166 265 -132 395 -66 887 -98 163 -66 589 -98 227 -130 1151 -230 293 -66 591 -68 527 -132 2883 -100 231 -66 99 -232 761 -134 499 -64 929 -100 167 -300 2259 -100 691 -164 459 -66 493 -132 163 -64 1283 -164 757 -132 295 -264 1023 -100 197 -198 6635 -198 2407 -100 2091 -132 1531 -66 1889 -100 199 -134 3567 -100 2981 -100 263 -198 425 -164 595 -100 231 -68 2691 -66 965 -100 2907 -98 367 -132 885 -198 1721 -100 659 -100 97 -296 495 -166 299 -134 397 -132 699 -66 1165 -66 465 -68 197 -66 659 -66 1543 -66 819 -164 2913 -98 1061 -66 5475 -132 167 -100 1035 -66 3427 -298 429 -166 2723 -66 831 -98 133 -66 133 -66 495 -98 701 -66 1063 -98 1991 -100 3319 -66 263 -66 233 -66 695 -66 593 -132 595 -66 553 -66 459 -66 197 -164 2241 -66 165 -68 959 -98 1587 -166 65 -102 233 -66 465 -134 1227 -100 2359 -66 1959 -198 331 -232 165 -102 531 -100 63 -66 1999 -68 265 -100 429 -66 657 -166 297 -132 823 -100 129 -132 4511 -164 659 -68 299 -66 593 -66 99 -134 65 -100 397 -66 1561 -66 697 -100 429 -66 265 -134 361 -132 195 -130 1319 -66 133 -66 265 -100 397 -268 895 -100 363 -134 433 -66 133 -100 2321 -68\nRAW_Data: -98 3573 -98 533 -68 961 -68 729 -132 559 -166 2189 -100 131 -68 657 -100 1387 -132 133 -68 2255 -68 429 -66 231 -134 793 -100 887 -98 361 -166 2141 -66 227 -130 663 -100 759 -100 1161 -134 1821 -66 327 -98 985 -130 757 -132 131 -132 1693 -66 361 -98 1411 -100 591 -132 1025 -66 663 -66 1065 -166 1059 -166 365 -66 723 -100 1659 -132 1883 -98 785 -132 1031 -66 261 -66 2501 -98 297 -66 1195 -100 691 -134 3009 -100 3921 -66 861 -66 363 -132 3361 -132 723 -66 459 -164 163 -164 333 -66 1291 -98 821 -230 591 -164 97 -262 361 -66 689 -66 733 -66 233 -134 1627 -66 533 -66 195 -100 521 -100 493 -98 493 -98 2173 -66 2037 -132 165 -100 429 -132 695 -100 67 -132 465 -68 1491 -100 1257 -66 965 -100 365 -68 929 -132 561 -66 899 -132 597 -132 861 -100 2627 -166 197 -98 2079 -66 2223 -100 1791 -364 895 -132 1027 -132 235 -68 599 -132 829 -66 197 -132 695 -66 133 -66 531 -68 333 -64 563 -66 265 -132 369 -134 2239 -164 4269 -100 793 -66 1495 -198 821 -164 133 -66 867 -66 797 -66 429 -66 365 -166 1729 -168 959 -100 1417 -66 233 -100 2579 -166 993 -164 461 -66 1529 -68 961 -66 1049 -98 1061 -132 2847 -66 229 -66 397 -134 263 -100 3285 -66 4115 -66 1547 -134 297 -132 431 -100 2895 -100 563 -66 1491 -66 399 -100 721 -66 395 -68 399 -66 1289 -66 293 -164 2307 -98 525 -66 3663 -64 927 -132 499 -134 1127 -264 397 -98 399 -198 131 -100 333 -100 663 -164 921 -166 1481 -262 691 -64 659 -64 2167 -98 3689 -100 833 -100 2085 -66 697 -100 595 -66 923 -134 893 -232 265 -98 367 -66 1157 -66 263 -130 1017 -66 623 -66 753 -100 2873 -132 395 -198 2787 -100 861 -132 3847 -100 297 -66 233 -98 1333 -100 495 -100 1325 -134 367 -66 595 -66 361 -230 4931 -66 1821 -98 329 -98 365 -168 333 -300 897 -100 2777 -66 1945 -132 2601 -66 951 -66 425 -98 789 -98 359 -64 1051 -66 1443 -132 851 -98 625 -100 97 -66 731 -232 263 -134 2757 -68 3021 -166 265 -100 1633 -132 427 -66 233 -98 799 -100 1059 -100 263 -98 557 -68 1063 -66 461 -100 1023 -98 163 -198 1481 -132 1227 -98 327 -100 327 -66 1317 -66 1853 -66 1061 -134 1287 -66 1315 -66 1345 -132 723 -66 1225 -68 1463 -166 3261 -98 2883 -66 563 -100 821 -100 2077 -166 3137 -66 565 -66 1355 -234 1415 -132 165 -66 397 -132 493 -132 563 -166 893 -66 1193 -66 1249 -100 333 -132 2083 -66 921 -100 1225 -262 861 -166 1321 -100 895 -100 591 -98 1249\nRAW_Data: -98 97 -66 6825 -66 231 -68 14077 -66 1787 -66 1547 -64 2617 -66 2925 -66 1723 -132 1529 -66 865 -166 827 -198 431 -66 495 -66 1121 -198 1327 -100 397 -130 557 -66 97 -100 261 -98 723 -98 557 -98 463 -98 463 -100 325 -66 3703 -100 465 -198 1123 -98 2545 -66 361 -66 857 -64 3455 -132 663 -98 1991 -200 825 -100 919 -98 893 -164 1749 -66 7759 -132 3321 -66 1807 -132 527 -66 393 -100 817 -130 657 -164 1485 -98 2367 -66 4171 -100 197 -130 3665 -134 1059 -132 597 -66 533 -66 1023 -98 1253 -134 2021 -100 231 -100 233 -66 197 -66 199 -66 1961 -168 729 -100 531 -100 461 -98 1361 -100 11161 -100 659 -166 229 -98 1675 -98 1027 -100 2063 -298 431 -100 99 -134 1059 -66 199 -100 763 -134 231 -66 233 -102 1761 -98 331 -68 757 -132 425 -64 457 -132 99 -66 2091 -66 567 -164 2121 -68 2125 -132 595 -200 759 -102 797 -132 1345 -66 429 -132 1019 -66 195 -66 791 -68 1227 -68 797 -132 1591 -200 199 -134 165 -66 1053 -66 559 -98 853 -164 825 -100 329 -98 891 -196 689 -132 657 -100 2341 -98 1119 -66 1883 -100 2607 -100 467 -100 1067 -164 6935 -66 2409 -132 855 -66 1809 -98 1119 -164 65 -66 199 -100 233 -132 931 -132 563 -66 1393 -132 567 -66 301 -68 1295 -66 529 -98 793 -66 131 -134 533 -132 827 -132 731 -332 1251 -98 921 -98 327 -198 361 -234 529 -66 1577 -132 97 -134 199 -100 1099 -68 1193 -132 991 -100 953 -98 2895 -166 1679 -98 161 -130 129 -66 1019 -100 261 -264 531 -100 263 -134 299 -68 495 -98 831 -100 531 -66 1357 -100 2051 -100 229 -98 829 -66 427 -66 859 -134 995 -68 665 -66 1793 -134 361 -100 2349 -66 331 -100 197 -66 1591 -66 959 -66 431 -234 2219 -332 661 -66 1487 -100 3381 -68 261 -164 463 -134 3377 -68 1127 -134 691 -66 529 -132 99 -66 6687 -98 889 -132 197 -164 725 -100 963 -66 2947 -132 327 -132 889 -66 393 -98 1581 -100 193 -130 97 -66 293 -66 1675 -100 1887 -98 2017 -100 597 -66 293 -98 557 -100 259 -98 985 -100 1727 -100 165 -100 301 -232 329 -100 533 -98 727 -100 761 -66 961 -68 2759 -100 2019 -66 855 -230 859 -98 1215 -98 1887 -98 131 -98 819 -166 227 -130 723 -132 625 -66 501 -66 429 -66 831 -66 1291 -66 331 -132 431 -132 1389 -100 265 -166 1461 -66 1907 -490 911 -962 409 -508 919 -944 419 -486 913 -526 915 -480 925 -488 911 -488 943 -476 925 -486 947 -462 945 -482 933 -484 951 -460 977 -440 967 -484 945 -468 945 -466 975 -450 983\nRAW_Data: -444 977 -458 975 -456 975 -444 965 -940 485 -430 967 -928 487 -448 981 -910 489 -446 963 -944 485 -430 975 -930 489 -912 485 -896 487 -452 977 -916 509 -454 977 -922 479 -448 985 -15898 487 -456 973 -926 493 -448 1001 -918 481 -484 981 -478 979 -450 1013 -476 977 -450 1017 -476 983 -448 999 -480 985 -484 983 -478 985 -484 981 -480 983 -476 1001 -474 979 -496 973 -486 1015 -476 977 -476 1005 -484 973 -486 1007 -920 505 -456 1011 -918 509 -458 1011 -950 497 -452 1011 -920 507 -458 1007 -950 477 -958 483 -942 481 -480 1003 -918 515 -446 1039 -922 505 -458 1007 -16154 501 -462 1013 -934 517 -474 999 -958 483 -478 993 -484 1037 -450 1027 -482 1027 -452 1037 -482 1025 -444 1035 -490 1009 -482 1013 -484 1001 -484 1039 -474 1003 -484 1045 -464 1011 -480 1049 -446 1037 -492 1007 -482 1013 -510 1005 -494 1005 -970 485 -482 1017 -974 473 -498 1007 -974 481 -486 1047 -948 479 -482 1051 -948 497 -952 519 -938 485 -482 1041 -946 517 -478 1013 -944 503 -472 415 -83640 97 -200 65 -632 131 -300 365 -364 231 -132 1061 -100 3047 -100 1393 -66 499 -66 1091 -66 333 -66 2353 -100 2273 -66 1427 -166 563 -132 1559 -132 427 -100 925 -98 13469 -100 2019 -98 821 -132 1097 -68 297 -100 897 -100 1129 -166 465 -166 961 -66 597 -100 165 -66 267 -100 201 -100 765 -134 297 -66 165 -66 3081 -100 1293 -100 1289 -136 233 -66 357 -66 1155 -166 295 -100 1197 -68 1089 -98 425 -132 1187 -100 523 -98 463 -98 197 -98 131 -98 493 -66 393 -98 2797 -164 359 -232 325 -66 229 -164 625 -98 1215 -164 425 -66 589 -98 195 -66 1083 -100 197 -68 1557 -66 1427 -66 525 -66 429 -132 863 -66 1129 -166 831 -98 265 -98 1183 -66 3157 -100 2735 -98 2819 -166 4645 -66 301 -68 1395 -132 1097 -100 897 -198 629 -200 1419 -132 493 -66 521 -132 697 -100 695 -66 459 -298 859 -66 559 -100 1029 -100 4113 -66 1167 -66 14017 -66 2123 -68 525 -132 861 -100 329 -66 399 -134 1523 -132 327 -64 691 -98 463 -132 1803 -132 853 -166 715 -66 953 -66 525 -98 723 -132 989 -132 461 -98 459 -164 2239 -66 1185 -66 589 -100 1945 -230 1483 -66 399 -66 265 -168 965 -66 197 -168 699 -68 1125 -68 529 -98 491 -66 987 -130 525 -168 397 -66 597 -100 561 -132 1353 -66 391 -132 393 -66 591 -98 557 -98 787 -66 463 -100 199 -134 395 -100 759 -66 295 -130 261 -98 229 -100 99 -100 1595 -66 699 -100 499 -66 595 -98 327 -132 957 -132 331 -100 493 -100 1313 -66 295 -132 197 -198 1279\nRAW_Data: -66 9461 -100 329 -68 27921 -66 24331 -68 13415 -66 6439 -98 133 -66 4193 -98 395 -66 653 -66 983 -66 163 -66 955 -132 1791 -66 861 -100 363 -132 1659 -132 667 -166 467 -134 429 -166 265 -66 4065 -98 293 -98 3183 -130 555 -98 163 -162 259 -100 661 -100 7057 -100 931 -100 1297 -66 2559 -98 1193 -100 333 -100 563 -132 65 -100 793 -66 855 -64 659 -100 929 -102 893 -132 689 -66 3475 -68 1361 -198 331 -134 691 -66 295 -66 425 -164 731 -266 921 -100 599 -100 165 -66 227 -98 1091 -66 263 -66 1215 -100 227 -164 657 -66 953 -132 359 -66 1845 -66 1779 -132 753 -164 393 -66 731 -66 1195 -66 533 -66 797 -132 1623 -98 1281 -100 493 -98 659 -98 2417 -166 799 -132 1259 -100 559 -134 595 -166 199 -66 1461 -198 865 -100 459 -66 463 -166 165 -100 497 -66 1097 -66 1579 -100 1449 -98 885 -98 263 -100 1097 -132 627 -68 329 -132 487 -132 427 -132 361 -66 525 -98 687 -66 1161 -100 263 -66 729 -100 229 -98 559 -66 1213 -100 1015 -66 795 -66 5475 -66 4043 -66 1683 -166 1151 -132 429 -98 1447 -68 261 -98 985 -100 429 -100 1289 -198 2269 -132 7999 -98 1591 -132 3233 -66 861 -66 2087 -98 557 -98 719 -66 981 -98 563 -100 199 -100 523 -100 2319 -134 833 -100 495 -132 197 -66 295 -64 989 -66 1059 -198 7343 -66 2023 -66 963 -66 593 -66 2401 -100 491 -100 959 -66 297 -134 999 -132 99 -68 3609 -230 97 -198 1911 -66 265 -100 1195 -132 633 -132 595 -66 1381 -66 491 -66 1681 -100 297 -100 1827 -132 2269 -100 1351 -132 1513 -66 1225 -134 231 -66 1523 -100 363 -200 1227 -66 2943 -66 923 -134 2249 -66 1809 -100 1121 -132 265 -66 827 -98 199 -66 201 -100 3279 -100 565 -132 1689 -66 395 -66 2979 -134 1065 -66 367 -168 3585 -200 463 -100 563 -66 97 -166 2293 -66 265 -134 1255 -132 2401 -66 1579 -166 365 -100 861 -298 261 -98 761 -66 363 -132 657 -130 63 -130 557 -66 131 -130 2041 -100 233 -66 1791 -100 925 -134 265 -100 1063 -100 301 -168 661 -66 657 -64 263 -64 197 -66 1853 -100 663 -98 231 -66 731 -100 5539 -166 197 -68 1423 -134 361 -68 1727 -68 929 -100 1397 -134 1885 -66 1661 -66 265 -66 1183 -66 295 -166 263 -166 165 -66 329 -66 465 -100 1159 -134 697 -100 2443 -100 393 -98 1093 -66 953 -296 787 -132 425 -66 2019 -66 461 -98 1201 -100 397 -132 3551 -100 1431 -264 725 -330 1455 -66 263 -100 531 -296 499 -100 265 -100 163 -66 1145 -132 1313 -98 2101 -98 261 -132 1083 -66 5403\nRAW_Data: -66 2223 -66 11583 -66 131 -66 5071 -66 3723 -132 1415 -132 6905 -64 9685 -102 4739 -66 3355 -66 5301 -98 29993 -508 897 -950 437 -490 909 -974 423 -510 935 -486 925 -508 943 -490 937 -494 947 -482 941 -484 979 -440 979 -456 1003 -462 975 -460 971 -458 977 -468 973 -484 975 -472 971 -450 1011 -452 1003 -446 983 -480 979 -450 1017 -908 487 -472 971 -944 471 -450 999 -944 485 -468 977 -918 493 -448 1011 -934 499 -920 487 -914 487 -452 1011 -912 521 -446 1009 -904 517 -468 1007 -16000 531 -444 1009 -908 519 -468 1009 -912 515 -466 1009 -452 1021 -466 1011 -450 1019 -480 1017 -448 1045 -448 1019 -460 1041 -450 1019 -480 1019 -450 1039 -474 1001 -480 1021 -484 1005 -476 1015 -480 1017 -484 1011 -486 1017 -464 1041 -446 1047 -922 503 -458 1037 -946 513 -442 1047 -938 503 -480 1023 -916 537 -450 1049 -926 521 -904 519 -942 519 -450 1055 -910 519 -486 1033 -916 519 -486 1029 -16258 533 -464 1015 -940 515 -456 1053 -946 511 -482 1051 -434 1075 -442 1075 -448 1065 -440 1065 -450 1049 -480 1067 -462 1041 -446 1075 -450 1063 -460 1053 -480 1047 -450 1075 -446 1079 -452 1055 -478 1051 -448 1067 -444 1065 -480 753 -66842 99 -1090 465 -332 131 -68 131 -134 99 -132 167 -200 429 -100 1809 -132 2385 -230 265 -102 597 -134 1025 -66 365 -100 361 -66 825 -168 1331 -100 797 -132 431 -132 299 -198 661 -168 501 -100 463 -164 329 -66 559 -98 391 -98 1085 -198 1939 -66 1871 -164 2251 -134 493 -66 719 -198 361 -98 361 -64 197 -132 391 -164 691 -300 489 -98 2139 -66 1413 -66 1875 -196 557 -66 263 -132 1359 -66 1397 -66 631 -100 793 -132 723 -100 65 -66 529 -134 463 -68 789 -100 227 -66 923 -100 2649 -166 363 -66 395 -200 295 -130 1757 -68 2057 -100 1023 -66 359 -66 391 -132 1679 -66 359 -66 1217 -98 663 -98 463 -100 821 -98 165 -98 1589 -132 2367 -98 559 -132 1079 -100 9617 -66 3669 -134 1787 -68 1679 -132 361 -66 555 -100 661 -66 1523 -100 2057 -198 1025 -66 4177 -100 165 -66 265 -132 465 -134 299 -232 265 -100 1125 -132 1461 -132 1295 -100 499 -132 367 -68 263 -66 331 -66 365 -100 1643 -130 197 -132 997 -98 867 -98 1191 -100 2945 -100 2339 -98 1779 -66 295 -132 597 -66 165 -100 665 -100 463 -66 331 -66 593 -100 459 -68 489 -164 855 -66 261 -64 163 -100 4449 -100 859 -100 699 -132 199 -100 1685 -66 301 -132 2317 -68 231 -100 827 -66 1749 -132 99 -64 1185 -100 329 -100 1253 -66 1127 -98 827 -198 363 -132 265 -134 365 -66 297 -66 1125 -66 261\nRAW_Data: -266 29863 -66 2443 -66 5113 -100 5947 -21026 99 -134 301 -132 199 -132 131 -266 163 -196 131 -66 365 -66 465 -98 13819 -98 525 -98 329 -100 893 -66 1259 -66 431 -98 427 -130 1051 -392 463 -200 795 -164 399 -66 1489 -66 1377 -100 1423 -132 597 -100 689 -68 1559 -100 2263 -100 1327 -98 1059 -98 497 -66 595 -132 265 -66 299 -66 199 -66 563 -134 627 -66 165 -134 889 -66 2751 -232 893 -264 231 -66 299 -132 467 -132 861 -68 1263 -164 795 -66 2601 -100 429 -66 1525 -66 961 -98 265 -98 997 -66 233 -68 695 -100 697 -66 795 -66 1195 -66 1223 -68 2173 -66 467 -66 827 -66 535 -68 697 -100 1221 -166 165 -100 365 -132 723 -66 829 -132 2091 -232 265 -66 195 -66 459 -262 499 -100 461 -68 759 -100 1087 -66 259 -164 2845 -66 1365 -98 561 -200 331 -168 201 -166 1397 -198 197 -66 697 -68 1713 -68 293 -134 1317 -66 593 -328 395 -100 499 -132 2251 -100 563 -134 333 -134 1921 -134 1187 -68 561 -132 933 -66 797 -100 631 -100 399 -132 929 -66 2769 -66 851 -130 2047 -66 265 -100 7219 -66 1987 -66 299 -98 2199 -134 1063 -98 2843 -98 655 -132 231 -66 1123 -198 2137 -64 327 -66 3183 -66 1127 -66 631 -100 263 -102 3173 -132 267 -68 1289 -98 1593 -66 2415 -66 1185 -66 359 -132 1051 -66 2169 -66 427 -98 395 -132 793 -98 293 -166 727 -134 131 -100 1287 -98 427 -98 687 -164 823 -64 853 -66 865 -100 763 -66 2025 -100 959 -66 1891 -64 793 -100 763 -66 729 -166 99 -98 399 -134 763 -100 4203 -66 1321 -230 4023 -98 1053 -66 985 -98 1383 -66 3559 -164 1515 -100 2899 -66 797 -134 1169 -100 3055 -134 1615 -66 429 -100 495 -64 1583 -134 923 -66 921 -66 723 -68 1359 -98 787 -98 425 -100 393 -64 1189 -98 263 -98 491 -100 1455 -98\nRAW_Data: -202 531 -66 531 -66 1093 -66 1389 -66 1551 -134 2699 -66 1291 -132 65 -64 657 -98 1083 -164 393 -98 1359 -134 1461 -66 393 -100 561 -130 2113 -132 597 -66 431 -102 1759 -302 985 -66 235 -100 1395 -66 901 -66 1061 -100 463 -66 5673 -66 227 -66 225 -66 855 -66 1581 -132 2503 -100 657 -66 2535 -98 259 -64 1015 -66 231 -132 1197 -66 827 -166 9641 -66 1823 -132 1565 -132 299 -66 797 -66 1631 -132 327 -132 2227 -232 433 -68 499 -100 1793 -66 1161 -132 525 -66 129 -100 361 -66 1765 -132 229 -66 491 -132 2255 -100 3043 -332 299 -100 499 -100 267 -68 2967 -66 991 -100 729 -100 633 -66 529 -98 825 -100 1033 -100 331 -66 723 -100 725 -264 2987 -68 825 -66 2601 -134 333 -100 3181 -134 1059 -100 299 -134 3279 -100 1221 -132 659 -66 3157 -98 1595 -132 1561 -98 201 -134 465 -66 1843 -130 589 -66 1413 -66 331 -100 333 -66 661 -100 265 -68 201 -234 1027 -166 297 -100 1161 -132 1561 -134 629 -66 431 -66 1025 -98 427 -198 1527 -66 793 -66 1903 -66 131 -130 1285 -66 299 -134 397 -98 229 -132 499 -132 4747 -100 2355 -100 263 -132 1915 -132 1749 -132 759 -66 2253 -100 4545 -66 391 -100 521 -100 1083 -100 929 -134 565 -66 2355 -66 1331 -66 167 -100 465 -100 1727 -132 633 -330 433 -66 897 -132 165 -134 331 -98 627 -66 231 -66 167 -66 1397 -66 729 -132 1397 -68 165 -66 1627 -134 2187 -66 231 -134 795 -200 6469 -232 829 -66 3929 -66 891 -98 1977 -100 525 -68 859 -66 921 -264 1029 -68 959 -134 1555 -66 259 -100 687 -66 429 -264 663 -66 1559 -100 1127 -100 2327 -132 1913 -64 4193 -132 293 -98 99 -100 5613 -132 1351 -66 1545 -66 1677 -66 295 -64 1943 -100 595 -132 1959 -166 765 -66 1389 -100 823 -66 1749 -66 1217 -100 597 -100 297 -66 2019 -98 165 -100 4165 -100 67 -100 2477 -262 295 -66 919 -200 3555 -66 229 -66 2531 -98 557 -66 2525 -66 1463 -100 1293 -68 197 -68 1391 -66 1421 -66 595 -164 327 -68 2285 -66 593 -98 99 -68 463 -98 1063 -100 165 -68 99 -100 631 -66 1085 -66 859 -98 6599 -66 1429 -66 233 -66 397 -98 231 -132 1975 -132 333 -66 131 -134 3373 -100 4277 -66 1363 -232 2893 -166 3133 -64 951 -66 2815 -100 425 -98 327 -66 599 -68 1031 -98 133 -68 633 -68 429 -100 1129 -66 327 -130 2679 -66 1321 -100 463 -200 367 -98 667 -66 493 -132 885 -98 2183 -166 559 -98 981 -66 3201 -164 593 -66 493 -130 1923 -166 565 -100 2421 -98 461 -66 1427 -130 1955 -64 197 -66 1643\nRAW_Data: -132 2291 -66 3057 -68 2521 -166 333 -134 503 -400 3235 -66 2329 -68 995 -100 333 -100 97 -166 1757 -100 397 -100 165 -66 2755 -132 297 -134 163 -100 565 -100 1793 -100 1813 -162 1293 -98 97 -66 999 -66 1763 -68 261 -68 2391 -100 765 -364 859 -100 1855 -98 1399 -230 463 -134 301 -198 397 -100 961 -68 431 -134 695 -202 133 -100 365 -66 925 -98 165 -66 365 -132 663 -98 4573 -134 1479 -66 1019 -66 629 -66 233 -68 201 -66 569 -66 295 -134 1755 -296 3199 -100 3261 -168 3373 -132 1425 -100 759 -66 895 -98 201 -100 265 -166 99 -66 695 -66 1091 -66 855 -168 299 -100 229 -164 589 -66 521 -66 655 -134 329 -98 493 -200 429 -66 929 -66 673 -100 953 -66 823 -66 1283 -66 1979 -68 233 -66 1547 -164 589 -132 597 -66 131 -66 265 -100 761 -200 759 -66 689 -332 263 -100 1227 -68 1067 -164 2945 -100 959 -100 995 -100 399 -100 1193 -100 625 -66 399 -66 3021 -134 393 -66 4805 -66 1095 -68 231 -332 399 -166 1663 -68 561 -66 927 -98 1085 -164 1155 -98 627 -66 265 -132 263 -130 2211 -66 2159 -66 1029 -264 2669 -66 295 -66 8747 -100 329 -232 625 -134 429 -68 1329 -168 1355 -98 987 -66 1545 -98 1015 -98 699 -134 133 -134 1263 -66 4687 -166 8299 -66 1349 -434 933 -906 443 -452 949 -894 441 -480 909 -486 907 -456 947 -448 949 -434 969 -454 931 -460 941 -448 933 -450 979 -450 945 -450 935 -458 935 -486 927 -456 947 -450 951 -482 945 -428 975 -446 967 -452 955 -458 945 -912 485 -434 971 -902 481 -450 949 -926 451 -478 941 -920 481 -450 949 -920 473 -450 955 -916 471 -452 981 -918 449 -486 945 -910 483 -924 473 -15780 479 -450 967 -948 449 -482 943 -944 485 -468 941 -484 979 -446 1001 -444 999 -446 967 -484 969 -482 979 -478 947 -484 985 -470 973 -458 983 -492 971 -458 979 -494 971 -458 983 -494 973 -458 981 -498 975 -458 1013 -466 973 -952 475 -458 973 -950 489 -450 1011 -916 481 -478 1001 -918 481 -478 1001 -916 481 -478 1001 -920 483 -480 983 -946 479 -458 1013 -932 485 -952 479 -16040 493 -476 1009 -912 515 -464 1007 -910 517 -464 1007 -450 1001 -488 1001 -484 1011 -450 1019 -458 1039 -450 1031 -454 1005 -484 1009 -458 1015 -476 1009 -478 1035 -462 1015 -468 1007 -480 1001 -486 1015 -460 1041 -450 1017 -484 1019 -482 1009 -952 477 -494 1003 -944 485 -478 1013 -944 519 -482 1013 -942 487 -482 1013 -946 487 -484 1015 -944 487 -484 1015 -946 519 -454 1019 -942 505 -952 487 -16238 517 -468 1007 -942 521 -482 1011 -944 519\nRAW_Data: -450 1019 -482 1035 -480 1033 -460 1047 -476 1017 -484 1007 -484 1051 -484 1027 -452 1039 -478 1035 -458 1049 -480 1017 -480 1035 -480 1035 -472 1047 -484 1027 -454 1039 -480 1033 -488 1031 -488 1009 -962 521 -486 1017 -966 485 -490 1015 -139210 229 -98 461 -364 165 -334 131 -168 2121 -66 1049 -66 1215 -166 297 -136 1449 -100 3877 -100 1495 -234 331 -64 1345 -262 393 -100 529 -132 2921 -164 1223 -132 1807 -66 765 -66 397 -98 3405 -132 2123 -230 231 -66 2541 -100 2489 -98 4397 -132 461 -98 293 -64 991 -66 1125 -166 401 -100 131 -100 99 -100 265 -100 2555 -100 499 -98 1361 -134 265 -166 895 -100 2253 -100 1057 -100 129 -296 1147 -198 197 -66 1163 -66 1935 -98 1675 -66 1103 -100 891 -100 989 -164 1019 -66 2967 -68 1293 -166 3161 -66 133 -264 1065 -100 731 -66 1693 -66 529 -100 165 -68 865 -66 825 -232 1117 -196 2401 -66 3051 -296 229 -132 1843 -132 1687 -68 1119 -68 299 -68 97 -66 4741 -66 197 -200 2319 -100 1097 -66 3765 -66 131 -100 695 -132 2753 -66 2287 -100 1129 -68 331 -98 1433 -132 893 -100 465 -100 801 -66 529 -66 1515 -264 393 -98 263 -66 1831 -166 3533 -100 633 -100 1051 -100 331 -98 795 -134 959 -132 1229 -100 627 -132 2517 -66 165 -98 131 -66 2301 -166 163 -134 465 -66 2767 -66 1019 -66 401 -134 397 -232 893 -66 397 -66 833 -66 199 -66 303 -66 2775 -66 2069 -98 1841 -100 399 -66 793 -98 2793 -68 3769 -100 867 -66 861 -100 399 -66 1859 -100 631 -132 755 -100 689 -66 163 -64 2045 -64 2191 -102 1127 -68 727 -68 625 -164 1381 -66 1153 -132 1115 -98 1017 -100 491 -100 593 -132 991 -98 1415 -98 4813 -66 331 -98 131 -102 1847 -98 197 -68 263 -100 3265 -66 431 -100 493 -98 435 -134 133 -68 1185 -134 395 -100 131 -66 399 -134 767 -134 1125 -66 429 -198 3185 -100 2261 -66 523 -230 2475 -168 1297 -66 3243 -66 1853 -100 1657 -66 459 -66 827 -100 263 -66 303 -234 197 -166 1167 -100 2299 -66 1329 -68 461 -100 763 -132 3819 -366 757 -66 591 -164 621 -98 1445 -100 2155 -100 231 -100 631 -68 1161 -66 131 -166 67 -98 1915 -166 1891 -66 1261 -68 999 -164 165 -132 133 -168 2695 -68 1055 -198 97 -98 229 -66 229 -66 1215 -66 885 -100 303 -132 297 -164 619 -198 459 -64 989 -66 229 -66 597 -134 693 -64 1255 -100 65 -132 331 -66 199 -98 529 -100 2831 -98 1259 -66 4855 -100 1163 -166 299 -66 395 -98 3141 -66 1319 -66 2139 -100 161 -132 261 -130 821 -200 263 -134 931 -330 65 -98 99 -134 793\nRAW_Data: -66 597 -100 231 -68 167 -66 1659 -100 733 -66 1631 -100 165 -66 199 -66 233 -166 165 -100 1925 -68 595 -198 1785 -134 2177 -134 131 -66 1049 -98 3087 -132 195 -64 589 -66 397 -134 329 -66 2565 -164 327 -100 689 -64 1775 -100 5183 -132 1187 -66 329 -66 395 -132 165 -98 261 -98 1247 -64 1217 -66 927 -66 997 -66 199 -98 1419 -66 531 -166 1231 -66 697 -100 97 -66 563 -66 161 -264 3205 -200 525 -98 293 -100 291 -100 133 -66 759 -66 659 -100 983 -64 523 -130 431 -166 919 -66 1097 -100 1757 -66 1119 -66 917 -98 2647 -166 1247 -66 165 -264 1189 -100 899 -134 597 -68 2323 -66 1893 -66 1095 -100 533 -64 965 -100 1817 -130 1215 -66 1879 -64 821 -164 1117 -132 263 -132 131 -66 557 -66 431 -132 661 -100 1183 -98 629 -100 1679 -132 259 -66 623 -98 431 -66 399 -164 923 -100 297 -66 165 -166 2521 -198 99 -66 431 -132 1225 -66 1063 -68 131 -136 631 -66 163 -100 99 -298 965 -68 465 -68 465 -298 2545 -134 2639 -230 1489 -66 299 -66 1991 -234 65 -132 693 -134 429 -102 101 -68 461 -66 3333 -64 1229 -68 333 -66 265 -66 885 -64 3163 -100 467 -66 2651 -164 1221 -100 1527 -66 1259 -134 431 -232 1259 -100 6029 -164 297 -98 1151 -66 1415 -100 5289 -66 2467 -100 493 -132 495 -200 1121 -66 129 -66 757 -166 327 -130 5477 -66 1227 -230 395 -100 265 -132 497 -132 1133 -132 361 -100 1051 -164 3089 -132 1583 -100 65 -68 2315 -100 529 -132 2157 -68 1257 -66 1975 -98 427 -98 1347 -66 719 -164 857 -66 165 -66 1029 -132 297 -132 467 -100 731 -130 1985 -98 199 -166 899 -100 1391 -166 3425 -100 261 -132 721 -66 4845 -98 1193 -68 1225 -66 721 -100 1015 -64 983 -66 557 -130 693 -98 99 -64 1091 -98 197 -100 2321 -66 431 -134 727 -66 467 -102 891 -98 167 -134 2619 -66 393 -64 97 -100 589 -98 1583 -164 301 -68 1481 -98 295 -98 959 -66 365 -98 1253 -66 231 -100 1255 -132 1813 -132 1645 -100 361 -132 395 -100 427 -164 1197 -98 1001 -100 861 -66 1161 -98 195 -100 197 -66 1429 -66 663 -66 1427 -98 665 -66 699 -100 663 -66 855 -196 161 -100 361 -98 823 -66 227 -66 621 -132 1853 -230 461 -230 623 -100 557 -98 229 -98 133 -134 1291 -66 533 -166 627 -134 195 -134 593 -64 591 -66 1019 -66 1049 -262 297 -100 2921 -66 133 -66 963 -134 165 -100\nRAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219\nRAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733\nRAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307\nRAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035\nRAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001\nRAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476\nRAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428\nRAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140\nRAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566\nRAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100\nRAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132\nRAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330\nRAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66\nRAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98\nRAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100\nRAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699\nRAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419\nRAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395\nRAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134\nRAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307\nRAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035\nRAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001\nRAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300\nRAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312\nRAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312\nRAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292\nRAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318\nRAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150\nRAW_Data: 15041 -66 15883 -66 12643 -66 12681 -66 3413 -68 2713 -68 33389 -66 1445 -66 1279 -68 1027 -66 6911 -98 25229 -66 3967 -100 3019 -100 6131 -66 955 -66 3605 -66 12411 -98 1419 -66 3593 -68 2753 -66 2457 -66 6007 -66 627 -100 1597 -66 3071 -98 22749 -66 333 -66 12829 -66 4313 -132 855 -66 44097 -64 20391 -98 29999 -66 3539 -98 557 -66 1489 -100 4081 -100 3857 -64 2895 -132 2261 -166 3089 -66 2429 -68 34467 -66 3585 -66 3087 -66 3329 -132 5287 -66 1063 -98 15259 -100 2535 -66 995 -66 13057 -100 24233 -68 531 -100 26415 -66 1761 -100 2717 -66 4071 -100 12191 -66 23367 -68 2323 -66 19809 -248 245 -1388 255 -242 275 -1358 273 -1370 277 -246 277 -1368 275 -246 275 -1362 275 -244 275 -1364 275 -244 275 -1362 275 -244 275 -1328 273 -278 273 -1358 275 -246 275 -238 263 -1384 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1344 277 -246 275 -1358 275 -244 275 -234 263 -1382 277 -1344 277 -246 279 -1362 275 -246 271 -234 261 -1380 275 -246 273 -1360 275 -246 275 -1366 277 -1340 277 -248 279 -238 263 -1382 275 -1344 277 -246 279 -1364 277 -244 275 -234 263 -1382 277 -244 273 -1358 275 -1344 277 -248 279 -1368 275 -244 273 -1360 239 -280 271 -1358 275 -244 275 -1358 275 -174 269 -10298 289 -2660 267 -238 299 -1356 275 -244 275 -1356 275 -1344 277 -248 277 -1360 275 -246 275 -1328 309 -244 273 -1358 277 -244 275 -1356 275 -246 273 -1326 309 -244 275 -1356 275 -246 273 -234 263 -1380 277 -246 273 -1326 309 -244 273 -1356 277 -246 277 -1358 275 -1338 279 -248 279 -1364 275 -246 273 -234 261 -1380 277 -1344 279 -250 277 -1330 309 -244 273 -232 261 -1384 275 -246 273 -1356 275 -248 275 -1360 275 -1340 279 -248 277 -236 263 -1380 277 -1342 279 -248 279 -1366 275 -246 273 -234 263 -1380 275 -246 275 -1358 275 -1340 279 -248 281 -1336 309 -244 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -176 267 -10306 257 -2646 299 -234 301 -1354 277 -246 275 -1356 277 -1340 279 -250 279 -1332 309 -244 275 -1358 275 -248 273 -1326 309 -246 273 -1326 309 -244 275 -1356 277 -248 275 -1328 309 -246 273 -234 261 -1382 277 -246 277 -1326 309 -244 275 -1358 277 -246 277 -1356 277 -1346 277 -250 277 -1358 277 -246 275 -234 263 -1382 279 -1346 279 -248 281 -1330 307 -246 273 -236 261 -1380 277 -246 277 -1360 277 -246 277 -1360 275 -1344 279 -248 279 -236 263 -1384 277 -1340 279 -250 281 -1338 307 -246 271 -234 261 -1384 277 -246 275 -1356 277 -1340 279 -250 283 -1336 309 -246 273 -1356 277 -246 273 -1360 277 -246\nRAW_Data: 275 -1328 309 -174 269 -10296 289 -2648 267 -238 299 -1356 277 -246 275 -1324 307 -1342 279 -250 277 -1330 309 -244 275 -1362 277 -244 275 -1356 275 -248 273 -1328 309 -244 273 -1328 309 -244 275 -1360 277 -246 275 -234 259 -1384 277 -246 275 -1360 275 -246 273 -1358 277 -248 277 -1362 275 -1344 277 -248 277 -1328 307 -246 273 -236 261 -1384 277 -1348 279 -248 279 -1360 277 -246 273 -234 263 -1388 275 -246 275 -1360 277 -248 279 -1368 277 -1344 279 -248 279 -240 265 -1386 275 -1342 279 -286 247 -1372 275 -248 275 -238 265 -1386 277 -248 275 -1360 275 -1344 277 -286 247 -1374 275 -246 275 -1362 277 -246 275 -1360 277 -248 275 -1326 307 -174 269 -10290 287 -2654 269 -236 301 -1352 275 -248 273 -1326 311 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -244 275 -1326 309 -244 273 -1356 277 -244 273 -1356 275 -246 275 -1358 275 -244 275 -234 261 -1382 277 -246 273 -1358 275 -246 273 -1360 277 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 271 -234 259 -1382 277 -1346 279 -248 277 -1330 309 -244 271 -232 259 -1382 277 -244 275 -1356 277 -248 273 -1354 277 -1342 277 -248 275 -236 261 -1380 277 -1344 277 -248 279 -1330 307 -246 273 -234 261 -1378 277 -246 273 -1356 277 -1342 277 -248 277 -1330 309 -244 273 -1322 307 -246 273 -1326 309 -244 273 -1322 309 -176 267 -10298 257 -2682 265 -236 299 -1324 309 -248 273 -1324 311 -1342 277 -246 279 -1360 277 -244 275 -1362 275 -244 275 -1358 275 -244 275 -1360 275 -246 273 -1360 275 -244 277 -1360 275 -246 273 -234 263 -1384 275 -246 273 -1358 275 -246 275 -1360 277 -246 277 -1356 277 -1342 279 -248 277 -1364 275 -244 275 -234 261 -1384 275 -1344 277 -250 279 -1366 275 -246 273 -236 263 -1384 277 -246 275 -1358 277 -246 277 -1362 277 -1342 279 -248 279 -236 265 -1382 277 -1346 277 -248 281 -1366 275 -246 275 -234 265 -1384 275 -246 273 -1358 277 -1344 279 -248 279 -1364 275 -244 275 -1324 309 -246 273 -1324 307 -246 273 -1326 309 -174 267 -118796 133 -100 131 -892 329 -166 199 -132 131 -166 99 -100 265 -264 4663 -134 4889 -100 365 -98 5921 -100 5903 -68 4877 -98 2953 -98 1645 -64 1687 -66 981 -98 10769 -66 18319 -66 4831 -66 13301 -66 893 -132 5967 -100 15949 -66 3749 -66 497 -100 625 -66 1147 -66 469 -66 1261 -66 3651 -100 265 -100 26741 -68 6873 -66 4485 -100 2667 -68 3159 -68 2857 -132 2655 -66 12903 -66 1277 -66 1711 -66 787 -100 1327 -198 727 -64 1677 -100 1187 -66 1019 -66 891 -66 4303 -100 11297 -66 3923 -254 253 -1380 247 -292 253 -1344\nRAW_Data: 277 -1346 277 -250 279 -1364 275 -244 275 -1362 275 -244 275 -1356 275 -246 273 -1358 241 -278 273 -1356 275 -246 273 -1360 275 -246 273 -234 263 -1382 275 -244 273 -1358 275 -246 273 -1360 275 -246 273 -1358 275 -1340 277 -248 277 -1362 275 -246 273 -234 261 -1380 277 -1344 277 -248 279 -1362 275 -244 273 -236 261 -1380 275 -244 275 -1360 275 -246 275 -1358 275 -1346 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1364 277 -244 273 -234 261 -1378 277 -246 273 -1356 277 -1340 277 -248 281 -1334 307 -246 271 -1356 275 -246 273 -1358 275 -244 273 -1326 309 -174 267 -10296 257 -2650 297 -232 263 -1384 277 -244 273 -1358 275 -1340 279 -248 279 -1328 309 -244 275 -1328 307 -244 273 -1356 275 -244 275 -1358 275 -246 273 -1324 309 -244 275 -1328 307 -244 273 -234 261 -1382 275 -246 273 -1326 309 -244 273 -1358 275 -246 273 -1358 275 -1338 279 -248 279 -1330 309 -244 273 -232 261 -1380 277 -1344 279 -248 279 -1330 309 -244 271 -234 261 -1382 275 -246 273 -1358 277 -244 275 -1330 309 -1338 277 -246 277 -236 263 -1380 277 -1342 277 -248 279 -1364 275 -246 273 -232 261 -1380 275 -248 275 -1328 307 -1338 277 -248 279 -1334 309 -244 271 -1358 275 -244 275 -1324 307 -246 271 -1328 309 -174 265 -10270 291 -2640 297 -232 297 -1350 277 -248 275 -1326 309 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -246 273 -1326 309 -244 273 -1354 275 -246 273 -1330 307 -244 273 -1358 275 -246 273 -234 263 -1380 275 -246 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -1340 277 -248 279 -1364 275 -244 273 -232 261 -1380 277 -1342 279 -250 279 -1332 307 -244 271 -234 261 -1378 277 -246 273 -1358 275 -248 275 -1360 275 -1340 277 -248 275 -236 263 -1382 277 -1344 277 -246 277 -1364 275 -246 273 -234 259 -1380 275 -246 273 -1362 275 -1342 275 -248 277 -1334 309 -244 271 -1356 275 -244 275 -1326 307 -244 273 -1356 275 -176 267 -10290 289 -2644 267 -238 301 -1320 309 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 273 -1326 307 -246 273 -1324 309 -246 273 -1322 309 -246 273 -1322 307 -246 275 -1326 309 -246 273 -234 259 -1382 275 -246 275 -1322 309 -246 273 -1326 309 -246 273 -1326 309 -1340 277 -248 275 -1326 309 -246 273 -232 261 -1380 279 -1346 277 -250 277 -1328 309 -244 271 -232 261 -1380 277 -246 273 -1358 275 -248 273 -1328 307 -1340 277 -248 277 -236 261 -1380 277 -1344 277 -248 279 -1328 309 -244 275 -232 261 -1378 277 -248 273 -1326 309 -1344 277 -248 277 -1358 277 -246 273 -1328 307 -244 271 -1324 309 -244\nRAW_Data: 273 -1324 309 -174 267 -10270 289 -2638 297 -234 297 -1352 275 -248 275 -1328 307 -1340 277 -248 275 -1330 309 -244 273 -1358 275 -244 275 -1326 309 -244 271 -1356 275 -244 275 -1326 307 -246 273 -1326 309 -244 273 -234 261 -1378 275 -248 275 -1326 309 -244 271 -1356 277 -248 273 -1328 309 -1338 277 -248 277 -1328 309 -244 271 -232 261 -1380 277 -1348 279 -248 277 -1328 307 -246 271 -234 259 -1384 275 -244 275 -1356 277 -246 275 -1326 309 -1344 275 -248 275 -236 261 -1378 277 -1342 277 -250 279 -1334 309 -244 271 -232 261 -1380 277 -246 273 -1326 307 -1344 277 -248 277 -1328 309 -246 273 -1326 309 -244 271 -1324 309 -244 273 -1324 307 -176 267 -10288 287 -2618 299 -236 299 -1354 277 -244 273 -1326 307 -1340 279 -248 275 -1328 309 -244 275 -1326 309 -246 273 -1324 307 -246 273 -1322 309 -244 273 -1322 309 -244 275 -1328 309 -246 273 -232 261 -1380 277 -246 275 -1324 309 -244 273 -1356 277 -246 275 -1324 309 -1340 279 -246 277 -1328 309 -244 273 -232 261 -1382 277 -1344 279 -250 277 -1324 309 -246 273 -234 261 -1380 277 -246 273 -1358 277 -246 273 -1328 309 -1340 277 -248 275 -236 261 -1380 275 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1354 277 -1344 277 -248 277 -1328 311 -246 273 -1324 307 -244 273 -1324 309 -244 273 -1320 309 -176 269 -118210 761 -168 267 -66 563 -132 99 -132 3543 -66 5345 -100 4355 -66 4617 -68 20503 -166 2379 -132 293 -98 4117 -66 1151 -98 3353 -66 3485 -66 2491 -66 6133 -66 233 -68 16307 -68 16959 -98 357 -66 5419 -134 799 -100 327 -100 791 -66 2481 -66 963 -100 3481 -98 1679 -134 2473 -100 227 -68 3087 -66 11527 -130 4305 -98 435 -66 563 -100 2887 -100 267 -66 1787 -66 9655 -66 4793 -100 2119 -66 359 -98 1313 -132 3393 -234 995 -66 2681 -98 99 -130 1379 -100 3757 -100 21695 -132 5135 -100 693 -98 4631 -100 2325 -68 4937 -66 10409 -98 897 -100 1287 -66 2565 -66 3753 -66 4055 -66 2023 -68 1961 -68 629 -66 431 -66 5039 -66 2155 -100 2673 -66 1163 -98 6539 -100 825 -66 1197 -100 3053 -66 13973 -68 15515 -100 1861 -66 1027 -66 797 -98 959 -98 787 -132 787 -64 3811 -132 1747 -66 6683 -66 1033 -68 24927 -66 1259 -100 1125 -68 663 -66 1687 -66 4357 -132 4567 -66 3969 -98 3317 -132 433 -134 6043 -66 3249 -100 431 -98 2367 -100 11265 -66 5085 -68 2355 -64 1815 -66 1395 -274 241 -1366 275 -244 275 -1362 275 -1338 277 -284 243 -1368 239 -278 275 -1362 275 -244 275 -1360 241 -278 273 -1356 275 -246 275 -1360 239 -280 275 -1360\nRAW_Data: 275 -244 275 -234 263 -1386 239 -280 273 -1356 275 -244 273 -1360 275 -244 277 -1364 275 -1336 277 -248 277 -1366 275 -244 273 -234 263 -1386 275 -1340 277 -248 279 -1364 275 -244 275 -234 263 -1384 273 -244 275 -1358 275 -244 275 -1364 275 -1342 275 -248 277 -236 265 -1384 275 -1340 277 -282 243 -1366 275 -246 273 -236 263 -1382 277 -244 275 -1358 275 -1342 277 -248 277 -1364 275 -246 275 -1360 239 -280 273 -1358 241 -278 275 -1356 275 -210 233 -10302 257 -2652 297 -232 297 -1354 277 -244 275 -1358 275 -1340 279 -248 279 -1360 275 -246 275 -1360 275 -246 273 -1360 275 -244 275 -1328 309 -242 273 -1324 309 -244 275 -1360 275 -246 273 -234 261 -1384 275 -246 273 -1358 275 -244 275 -1358 277 -248 273 -1358 275 -1340 279 -248 277 -1334 307 -242 273 -232 261 -1380 277 -1348 277 -250 277 -1364 275 -244 275 -234 261 -1380 277 -244 275 -1358 277 -246 277 -1360 277 -1342 275 -248 275 -236 263 -1380 277 -1344 277 -248 279 -1368 275 -244 275 -232 261 -1382 277 -244 275 -1356 275 -1344 277 -248 279 -1362 275 -246 275 -1360 275 -246 273 -1356 275 -246 273 -1356 275 -176 267 -10302 257 -2648 299 -234 297 -1352 277 -246 275 -1326 309 -1340 279 -248 277 -1330 309 -244 275 -1328 309 -244 273 -1324 309 -244 275 -1324 309 -246 273 -1324 307 -246 275 -1328 309 -244 273 -234 261 -1378 277 -248 275 -1328 309 -244 273 -1356 277 -248 275 -1326 309 -1344 277 -248 275 -1326 309 -246 273 -234 259 -1380 277 -1348 281 -248 279 -1328 307 -246 273 -234 259 -1382 277 -246 275 -1360 275 -248 275 -1324 309 -1340 279 -248 277 -238 261 -1382 277 -1344 277 -248 279 -1330 311 -244 273 -234 259 -1378 277 -248 275 -1326 309 -1340 279 -248 279 -1336 307 -246 271 -1324 309 -244 275 -1324 307 -246 273 -1326 309 -174 269 -10296 257 -2648 299 -234 297 -1352 277 -248 273 -1326 309 -1342 277 -248 277 -1328 309 -246 275 -1328 309 -244 273 -1326 309 -244 273 -1322 309 -244 273 -1328 307 -244 275 -1328 309 -246 273 -234 261 -1382 277 -246 275 -1326 309 -244 273 -1352 277 -248 275 -1330 309 -1340 277 -248 277 -1328 309 -244 275 -232 261 -1384 277 -1342 279 -250 279 -1328 309 -244 273 -234 263 -1380 277 -246 273 -1360 277 -246 275 -1326 309 -1340 277 -250 277 -236 263 -1382 277 -1342 277 -248 279 -1362 277 -246 273 -234 263 -1382 277 -244 275 -1356 277 -1340 279 -248 279 -1362 275 -246 275 -1328 307 -246 273 -1356 275 -246 273 -1356 275 -174 269 -10292 287 -2650 269 -236 301 -1354 275 -248 273 -1358 275 -1340 279 -248 277 -1332 307 -246 275 -1328\nRAW_Data: 309 -244 273 -1324 309 -244 273 -1356 275 -246 273 -1358 275 -244 277 -1330 309 -244 273 -234 261 -1382 277 -244 275 -1358 275 -246 273 -1356 277 -248 275 -1360 275 -1340 277 -248 277 -1360 275 -246 273 -236 261 -1382 279 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1360 277 -246 273 -1360 275 -1342 279 -248 275 -236 263 -1382 275 -1344 279 -248 279 -1362 277 -246 273 -234 263 -1380 277 -246 275 -1356 275 -1342 277 -248 281 -1336 307 -246 271 -1354 277 -246 275 -1328 307 -244 273 -1352 277 -176 269 -10300 257 -2650 299 -232 297 -1354 277 -246 275 -1356 277 -1342 277 -248 279 -1328 309 -244 275 -1360 275 -246 273 -1328 307 -246 273 -1356 277 -246 277 -1326 309 -244 277 -1360 277 -246 273 -234 263 -1384 277 -246 275 -1324 309 -246 275 -1358 277 -246 277 -1360 277 -1344 277 -248 277 -1326 309 -246 273 -236 261 -1382 277 -1348 279 -250 281 -1330 307 -246 273 -234 263 -1386 277 -244 275 -1356 277 -248 277 -1362 277 -1342 277 -250 277 -238 263 -1384 277 -1342 277 -250 281 -1332 309 -246 273 -234 263 -1380 277 -246 275 -1360 277 -1342 279 -248 281 -1334 307 -246 273 -1356 275 -248 275 -1328 309 -244 275 -1324 309 -176 269 -115034 163 -362 67 -894 529 -166 14663 -98 4135 -66 3681 -100 299 -68 9829 -66 3517 -64 21569 -66 3251 -66 2209 -64 23701 -66 3359 -68 1057 -66 723 -66 299 -134 765 -66 589 -98 1687 -134 2153 -66 3081 -68 10447 -66 11643 -66 2451 -66 2277 -66 2897 -66 755 -100 5539 -64 5117 -132 4867 -134 3931 -64 625 -66 1317 -98 11597 -66 2255 -66 1165 -66 1123 -66 6371 -100 699 -68 1811 -66 621 -68 2191 -64 1291 -134 3003 -66 2423 -64 1463 -66 663 -100 1127 -100 6169 -100 489 -100 6087 -100 2027 -66 1195 -66 13195 -66 557 -66 40423 -98 1919 -100 1061 -132 201 -66 2553 -132 12549 -66 1789 -100 921 -134 1067 -66 729 -66 10029 -66 3909 -100 265 -100 16017 -134 21177 -68 2461 -66 2215 -68 1197 -66 5911 -66 2645 -66 3419 -132 16275 -64 5091 -68 2123 -66 2677 -64 10305 -66 12381 -100 427 -166 25331 -66 2457 -66 11859 -248 279 -1368 275 -246 275 -1360 275 -1340 277 -246 279 -1364 239 -278 275 -1358 275 -244 275 -1362 239 -278 273 -1358 239 -280 271 -1360 241 -278 273 -1360 275 -244 275 -234 261 -1384 239 -280 273 -1356 275 -244 273 -1360 275 -244 275 -1358 275 -1344 277 -248 275 -1358 275 -244 273 -236 261 -1384 275 -1342 279 -246 279 -1360 275 -244 275 -234 263 -1384 239 -278 273 -1358 275 -244 275 -1362 275 -1342 275 -248 275 -238 263 -1382 275 -1344 275 -248\nRAW_Data: 277 -1364 275 -244 273 -234 263 -1380 275 -246 273 -1358 275 -1342 277 -246 279 -1366 275 -244 273 -1362 239 -278 239 -1386 275 -246 273 -1360 241 -208 269 -10290 257 -2686 265 -232 265 -1384 275 -246 275 -1358 275 -1344 277 -248 275 -1358 275 -246 275 -1360 277 -244 273 -1326 309 -244 271 -1354 275 -244 275 -1358 275 -246 273 -1358 275 -246 273 -234 263 -1378 275 -246 275 -1360 275 -244 273 -1356 275 -246 275 -1360 275 -1342 277 -246 277 -1360 275 -246 273 -232 261 -1382 277 -1342 279 -248 279 -1360 275 -244 275 -232 261 -1380 277 -244 275 -1356 277 -246 277 -1360 275 -1342 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1362 275 -246 273 -234 261 -1378 277 -246 275 -1328 307 -1340 277 -246 279 -1366 275 -244 273 -1326 307 -244 273 -1324 309 -244 273 -1356 275 -174 267 -10304 255 -2648 297 -230 263 -1382 277 -244 275 -1330 307 -1338 277 -248 277 -1330 309 -244 273 -1356 275 -246 273 -1362 275 -244 273 -1356 275 -244 273 -1326 307 -244 273 -1360 273 -246 273 -236 261 -1380 275 -244 275 -1328 307 -244 273 -1358 275 -244 275 -1360 275 -1342 277 -246 277 -1364 275 -244 271 -232 261 -1384 277 -1340 279 -248 279 -1360 275 -246 273 -234 261 -1380 275 -244 275 -1360 277 -244 275 -1356 275 -1342 279 -246 277 -236 263 -1382 275 -1340 277 -248 279 -1366 275 -246 271 -234 261 -1382 277 -244 275 -1354 275 -1342 277 -248 277 -1364 273 -246 273 -1362 275 -244 271 -1360 275 -244 273 -1358 275 -174 267 -10272 289 -2646 265 -262 261 -1382 277 -244 275 -1356 275 -1342 277 -248 277 -1364 275 -244 275 -1360 275 -244 273 -1358 275 -244 273 -1358 275 -244 273 -1326 307 -244 275 -1358 275 -246 273 -234 261 -1382 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1338 277 -248 277 -1362 277 -244 271 -234 261 -1380 277 -1344 279 -248 277 -1332 273 -278 271 -234 261 -1382 275 -244 275 -1356 277 -246 275 -1360 277 -1340 277 -246 277 -234 263 -1384 275 -1342 277 -248 277 -1366 275 -244 273 -234 261 -1380 275 -246 273 -1360 275 -1340 277 -246 279 -1334 307 -244 273 -1356 275 -246 273 -1360 275 -244 271 -1354 277 -174 269 -10300 257 -2648 297 -230 263 -1384 277 -244 273 -1356 277 -1342 277 -248 277 -1362 275 -244 275 -1330 307 -244 273 -1324 309 -244 273 -1324 307 -246 273 -1326 307 -244 273 -1358 275 -246 273 -234 261 -1380 277 -246 273 -1358 275 -244 275 -1354 277 -248 275 -1360 275 -1338 279 -246 277 -1360 275 -244 273 -234 261 -1378 279 -1344 279 -248 279 -1330 309 -244 271 -232 261 -1380 277 -246 273 -1360\nRAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 -248 277 -1362 275 -246 273 -234 263 -1380 275 -244 275 -1358 275 -1340 277 -248 279 -1334 309 -244 273 -1324 307 -246 273 -1356 275 -244 273 -1356 275 -174 269 -10302 257 -2644 297 -232 263 -1384 277 -246 275 -1354 275 -1344 277 -248 275 -1360 275 -246 275 -1358 275 -246 273 -1326 307 -246 273 -1324 307 -244 273 -1328 307 -244 273 -1358 275 -244 273 -236 261 -1380 275 -246 273 -1358 275 -244 273 -1358 275 -246 273 -1360 275 -1344 275 -248 275 -1360 275 -244 273 -234 261 -1378 277 -1344 279 -248 277 -1362 275 -246 273 -234 261 -1378 275 -244 275 -1360 275 -246 275 -1358 275 -1344 277 -246 277 -234 263 -1380 275 -1338 279 -246 281 -1368 275 -244 271 -234 261 -1386 275 -244 271 -1358 275 -1342 277 -246 279 -1362 275 -244 275 -1326 273 -278 273 -1358 239 -278 273 -1358 275 -174 267 -127478 195 -964 2317 -66 763 -98 1455 -100 16109 -66 5683 -98 11469 -66 34413 -66 5443 -66 11613 -66 2737 -66 12191 -66 2951 -68 1851 -68 1895 -68 2643 \nRAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153\nRAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65\nRAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751\nRAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263\nRAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131\nRAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341\nRAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379\nRAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197\nRAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671\nRAW_Data: 889 -130 325 -64 457 -560 165 -68 199 -170 67 -66 265 -132 133 -666 67 -166 431 -66 201 -98 297 -100 595 -66 199 -134 65 -100 795 -132 99 -168 501 -200 331 -132 265 -102 265 -134 423 -98 521 -226 65 -166 431 -134 99 -100 133 -464 195 -326 623 -100 673 -98 321 -200 65 -136 369 -166 65 -68 97 -166 165 -334 265 -102 231 -166 101 -170 65 -170 265 -136 931 -100 133 -134 563 -66 333 -100 427 -66 163 -390 231 -66 193 -130 461 -166 557 -100 99 -198 263 -100 197 -294 231 -232 299 -134 199 -170 267 -134 631 -98 235 -100 499 -68 463 -100 65 -134 335 -170 273 -134 297 -100 67 -66 197 -166 67 -134 301 -168 537 -470 99 -134 433 -132 199 -192 261 -100 523 -164 459 -132 259 -332 359 -64 227 -96 131 -132 687 -132 363 -136 329 -434 99 -334 133 -100 401 -132 233 -700 233 -170 337 -66 371 -68 233 -202 531 -266 731 -66 465 -100 167 -100 133 -232 335 -166 239 -102 367 -232 231 -100 167 -134 201 -136 301 -168 199 -300 231 -98 237 -134 233 -102 329 -132 261 -134 199 -66 265 -136 99 -170 167 -134 199 -166 167 -136 367 -298 197 -200 99 -166 469 -136 439 -66 303 -134 295 -100 433 -134 899 -266 363 -132 197 -160 555 -324 129 -96 97 -128 257 -132 97 -394 257 -98 195 -166 459 -332 395 -132 633 -134 301 -100 131 -332 169 -168 395 -166 263 -540 783 -100 287 -130 295 -96 225 -296 133 -98 99 -100 461 -164 545 -130 99 -66 301 -68 265 -100 235 -134 235 -70 333 -102 497 -66 233 -364 301 -170 103 -66 165 -336 733 -200 133 -100 263 -102 65 -136 465 -200 1035 -198 165 -170 67 -302 631 -100 429 -332 65 -128 129 -130 159 -128 159 -66 161 -96 325 -164 261 -100 197 -162 65 -96 99 -130 65 -102 333 -100 199 -98 389 -330 129 -128 229 -66 425 -366 229 -64 261 -100 227 -96 227 -526 301 -200 97 -66 699 -334 67 -100 399 -198 787 -98 297 -134 429 -100 3245 -64 527 -98 131 -526 633 -68 133 -302 1459 -164 971 -102 237 -136 1439 -266 1131 -66 599 -200 303 -332 325 -130 389 -166 371 -66 333 -102 65 -100 233 -234 327 -266 233 -166 297 -100 225 -130 163 -336 99 -596 199 -330 131 -66 331 -338 263 -358 197 -168 877 -66 227 -96 63 -130 263 -162 225 -290 197 -198 357 -132 297 -262 165 -456 227 -98 399 -296 95 -132 99 -98 457 -200 199 -168 535 -100 567 -134 327 -130 193 -130 683 -102 101 -132 233 -170 943 -166 827 -66 267 -102 503 -68 1325 -164\nRAW_Data: 1607 -68 233 -166 1167 -70 531 -134 335 -168 131 -66 299 -402 899 -66 461 -66 457 -98 953 -98 165 -66 293 -230 881 -64 393 -166 589 -66 289 -66 1093 -204 333 -98 2745 -132 2019 -170 925 -68 269 -102 1469 -136 2301 -68 1355 -100 527 -66 975 -68 1445 -98 2397 -100 1733 -66 703 -100 995 -100 135 -136 235 -202 167 -134 2071 -166 339 -170 201 -268 129 -66 465 -66 365 -100 197 -164 129 -98 161 -96 423 -66 675 -66 1543 -136 567 -200 767 -202 65 -100 1401 -66 623 -136 567 -234 67 -236 197 -194 97 -66 263 -66 1827 -392 1893 -98 165 -268 133 -132 231 -162 225 -98 695 -198 563 -100 301 -332 267 -102 341 -66 99 -132 1299 -130 525 -68 161 -96 357 -98 353 -100 131 -100 131 -98 163 -132 323 -100 535 -66 1323 -130 133 -66 235 -134 1497 -132 387 -98 129 -162 2623 -134 163 -68 167 -66 959 -232 495 -68 131 -134 867 -134 865 -66 333 -98 305 -134 231 -98 765 -198 397 -432 165 -66 165 -366 265 -102 541 -100 261 -162 331 -134 457 -66 491 -196 97 -266 193 -262 65 -166 231 -266 497 -360 263 -98 587 -164 259 -98 231 -66 359 -100 267 -102 271 -168 97 -262 63 -66 261 -130 227 -130 295 -164 65 -66 265 -200 597 -134 267 -170 603 -100 97 -466 231 -264 97 -168 99 -66 65 -200 199 -100 267 -404 303 -102 201 -204 235 -134 131 -198 335 -298 327 -130 291 -164 63 -162 295 -262 197 -130 95 -130 195 -96 159 -130 161 -66 231 -100 165 -66 199 -134 363 -66 267 -168 165 -168 167 -100 165 -530 363 -432 99 -232 65 -132 395 -328 229 -98 197 -132 161 -96 191 -292 197 -204 133 -100 399 -166 531 -332 235 -168 99 -66 325 -158 553 -132 129 -226 231 -134 99 -462 129 -64 289 -100 193 -66 355 -164 291 -198 131 -298 197 -198 373 -268 335 -234 427 -68 199 -132 267 -232 131 -66 783 -326 63 -162 161 -130 227 -66 259 -562 233 -464 303 -102 201 -334 301 -134 297 -198 229 -66 127 -166 99 -100 197 -198 571 -66 457 -134 361 -424 131 -328 163 -98 63 -100 505 -102 201 -1094 229 -164 65 -230 789 -236 2505 -166 201 -170 163 -64 1139 -66 927 -100 295 -198 723 -100 365 -66 459 -196 3033 -272 199 -66 499 -202 1319 -232 295 -298 131 -362 97 -164 129 -132 65 -98 197 -130 129 -98 261 -130 97 -98 229 -96 425 -66 227 -166 483 -66 163 -326 567 -68 235 -68 67 -66 167 -66 235 -330 425 -164 63 -66 427 -102 167 -66 669 -132 429 -200 65 -102 133 -100 197 -368\nRAW_Data: 65 -134 2481 -228 65 -130 229 -228 763 -136 603 -166 1619 -98 1763 -102 837 -166 321 -66 951 -130 2067 -66 259 -132 1835 -66 437 -102 701 -66 565 -68 363 -70 1113 -66 1989 -164 257 -128 351 -162 1055 -232 265 -170 309 -200 435 -166 833 -102 2467 -132 595 -66 773 -166 1615 -98 131 -96 485 -64 517 -166 197 -68 1231 -68 403 -100 263 -134 233 -100 503 -100 333 -266 729 -66 199 -100 369 -68 1239 -100 197 -68 299 -170 337 -100 825 -132 163 -66 4205 -64 161 -100 635 -66 907 -66 1017 -166 1709 -100 201 -266 657 -68 463 -166 331 -164 293 -64 259 -162 129 -262 597 -134 701 -136 67 -168 235 -136 303 -170 1417 -66 263 -98 857 -100 659 -166 97 -100 2497 -64 2495 -98 719 -128 227 -130 2217 -164 623 -264 719 -134 329 -98 1371 -100 553 -294 165 -66 1163 -100 329 -196 649 -200 1123 -68 263 -100 593 -266 333 -102 1133 -136 131 -132 603 -200 1819 -66 489 -66 563 -266 1113 -230 165 -66 423 -68 335 -100 101 -100 1073 -132 897 -100 101 -100 499 -134 173 -138 763 -238 371 -130 403 -166 203 -102 271 -136 269 -166 99 -168 263 -96 425 -66 331 -234 133 -400 231 -132 453 -66 459 -164 199 -68 237 -132 163 -198 161 -196 265 -132 65 -64 195 -130 357 -164 663 -68 167 -600 131 -98 133 -304 203 -134 433 -98 261 -130 199 -100 237 -100 229 -326 99 -98 331 -132 99 -294 165 -66 303 -134 99 -232 133 -136 99 -68 267 -198 233 -138 67 -166 367 -100 333 -168 267 -200 369 -266 135 -404 1939 -132 231 -160 161 -64 293 -98 331 -132 339 -104 135 -100 197 -430 263 -202 233 -64 195 -162 129 -64 227 -298 265 -68 697 -66 301 -68 231 -300 131 -368 769 -234 265 -98 195 -324 97 -752 229 -126 355 -98 257 -98 287 -64 427 -132 295 -262 197 -170 369 -102 267 -100 169 -68 201 -102 2551 -136 635 -134 639 -134 99 -132 197 -200 371 -66 731 -132 199 -138 733 -304 433 -68 729 -440 197 -68 99 -102 165 -266 261 -164 491 -296 489 -194 257 -164 133 -134 237 -68 335 -98 227 -130 229 -98 295 -98 231 -202 267 -236 233 -136 331 -130 195 -128 261 -430 261 -162 97 -224 99 -130 193 -96 197 -162 229 -396 97 -98 227 -364 267 -100 99 -100 233 -236 697 -164 227 -196 63 -98 327 -230 325 -66 129 -196 95 -98 195 -130 325 -430 131 -194 129 -454 161 -196 235 -68 433 -134 667 -164 355 -236 101 -98 2143 -134 1827 -198 63 -198 65 -64 2859 -64 619 -66 97 -130 3157 -66 679 -194 1491 -98\nRAW_Data: 951 -64 393 -100 955 -132 4715 -100 131 -66 199 -204 1541 -66 929 -130 1347 -166 665 -132 233 -132 67 -102 433 -100 595 -228 997 -66 505 -68 133 -98 231 -68 571 -134 1371 -232 231 -270 135 -102 97 -66 867 -100 269 -68 967 -100 1649 -66 65 -66 951 -68 65 -202 363 -200 779 -102 1449 -294 419 -130 361 -230 1079 -164 163 -260 893 -102 333 -100 533 -166 467 -100 135 -66 135 -202 369 -100 199 -100 269 -134 301 -166 229 -66 101 -134 199 -134 1293 -64 779 -62 831 -66 1243 -68 267 -102 197 -100 395 -98 455 -64 621 -132 877 -98 199 -100 2101 -134 503 -100 2035 -134 735 -236 475 -136 237 -132 133 -134 1229 -100 133 -66 167 -68 2655 -100 1807 -100 1095 -264 825 -98 163 -66 491 -98 161 -128 953 -100 773 -100 131 -66 67 -134 457 -130 63 -64 389 -98 715 -66 425 -300 97 -100 1515 -66 303 -68 99 -98 721 -64 887 -132 65 -132 165 -66 635 -68 2801 -66 1561 -100 751 -98 129 -64 725 -136 201 -100 333 -204 573 -104 1745 -134 99 -66 129 -64 595 -134 167 -102 337 -134 567 -134 1131 -138 1207 -100 269 -68 135 -100 1143 -134 2139 -68 1701 -162 991 -596 431 -66 99 -132 657 -66 391 -320 357 -260 259 -98 429 -66 163 -228 65 -130 227 -66 261 -166 99 -98 131 -366 199 -134 463 -102 201 -98 231 -102 639 -238 301 -568 169 -610 265 -102 841 -198 297 -100 335 -132 263 -266 265 -68 469 -134 267 -68 933 -298 333 -298 729 -168 135 -136 437 -132 1137 -134 199 -68 265 -132 463 -166 129 -130 227 -98 297 -98 65 -132 97 -202 199 -232 305 -66 165 -198 365 -66 99 -98 299 -170 65 -136 301 -232 99 -564 133 -132 233 -170 99 -102 131 -134 65 -204 101 -98 297 -98 167 -762 233 -298 99 -326 395 -66 299 -132 369 -504 333 -98 483 -200 457 -164 63 -164 329 -162 65 -622 231 -268 131 -132 133 -134 131 -134 131 -66 99 -100 231 -66 167 -336 165 -98 197 -100 97 -264 321 -98 521 -132 163 -130 129 -294 297 -134 101 -102 265 -168 497 -68 197 -68 499 -134 269 -398 267 -130 203 -302 65 -498 271 -136 465 -292 131 -294 163 -198 329 -96 129 -98 193 -130 391 -330 165 -134 167 -170 297 -102 133 -136 135 -366 199 -132 423 -132 395 -168 65 -166 401 -98 229 -98 329 -98 99 -130 129 -228 261 -160 127 -426 389 -162 193 -132 131 -100 231 -168 67 -304 201 -68 765 -132 161 -162 193 -64 195 -64 295 -130 787 -98 419 -528 429 -66 363 -134 131 -100 133 -200 331 -98\nRAW_Data: 431 -66 1167 -68 937 -68 1003 -66 99 -132 941 -134 65 -66 365 -274 165 -236 367 -96 557 -134 675 -66 261 -164 127 -96 391 -164 161 -98 391 -292 163 -98 519 -196 165 -98 523 -66 195 -160 3343 -66 661 -100 2589 -136 307 -100 629 -136 639 -100 133 -168 405 -100 267 -66 465 -132 1171 -64 749 -64 165 -98 983 -100 163 -202 537 -66 327 -100 669 -100 401 -236 2885 -164 439 -134 97 -426 1931 -66 1385 -98 715 -98 519 -66 289 -162 97 -360 297 -166 163 -66 289 -66 555 -334 167 -230 429 -102 267 -132 943 -136 401 -68 929 -130 193 -68 467 -198 335 -66 963 -100 597 -132 197 -260 523 -232 1115 -102 1935 -66 1395 -134 305 -100 99 -66 199 -66 1071 -66 2357 -66 367 -498 769 -234 163 -130 191 -64 1211 -200 133 -102 201 -100 561 -366 361 -98 195 -100 537 -64 165 -196 1041 -332 133 -102 441 -230 4217 -66 1033 -66 167 -66 933 -100 565 -66 331 -164 673 -104 441 -66 533 -66 2095 -164 525 -66 297 -170 965 -198 421 -100 663 -832 65 -100 331 -164 231 -166 135 -168 237 -466 761 -134 891 -196 791 -198 257 -160 161 -98 293 -66 1081 -98 229 -130 327 -66 1301 -200 331 -166 101 -66 461 -100 2619 -132 1663 -98 1609 -134 499 -332 165 -370 67 -264 97 -96 259 -98 701 -402 197 -128 527 -236 233 -102 167 -134 303 -134 99 -166 299 -132 165 -200 467 -68 305 -168 207 -102 465 -102 729 -136 101 -374 327 -96 259 -98 467 -202 65 -66 673 -98 335 -404 135 -66 339 -204 99 -366 233 -68 365 -166 133 -102 867 -198 163 -162 163 -294 463 -332 165 -68 269 -268 331 -100 131 -166 299 -132 231 -400 263 -164 131 -266 267 -264 367 -66 371 -134 229 -104 267 -232 67 -466 265 -100 101 -100 165 -200 65 -200 301 -66 199 -168 233 -98 267 -66 67 -134 261 -196 261 -234 427 -294 65 -194 193 -66 259 -132 849 -96 63 -198 167 -294 95 -98 361 -164 261 -196 131 -132 437 -100 597 -262 327 -162 295 -98 295 -164 259 -196 425 -230 321 -66 195 -66 261 -496 99 -200 529 -132 133 -966 133 -132 165 -66 63 -128 491 -402 65 -262 299 -66 299 -202 265 -100 99 -668 97 -134 65 -100 101 -66 65 -266 691 -66 431 -166 167 -134 199 -370 899 -134 99 -100 1093 -166 163 -166 399 -98 327 -100 99 -168 135 -200 133 -202 429 -98 65 -98 197 -556 65 -66 97 -326 331 -166 333 -200 135 -100 235 -234 265 -98 65 -68 135 -66 335 -66 133 -298 99 -66 233 -164 435 -232 97 -132 97 -392\nRAW_Data: 99 -198 819 -66 1235 -98 321 -132 1091 -66 1307 -98 3059 -164 3305 -64 227 -98 591 -98 129 -66 229 -98 2143 -98 939 -68 563 -100 361 -232 945 -164 257 -96 229 -230 387 -64 195 -130 981 -294 587 -162 193 -98 1337 -66 293 -98 2665 -66 297 -98 647 -66 459 -132 491 -164 489 -96 595 -66 899 -66 837 -64 1151 -196 259 -98 357 -164 891 -132 1359 -134 197 -98 97 -98 261 -64 229 -96 461 -136 693 -100 201 -98 865 -66 599 -100 517 -132 709 -66 293 -298 655 -66 197 -130 129 -66 197 -98 4291 -66 673 -66 667 -132 1473 -132 133 -104 99 -66 163 -168 333 -134 1743 -132 1097 -132 99 -68 167 -602 1323 -352 99 -166 753 -98 423 -98 97 -66 1317 -228 1309 -98 1849 -66 1939 -132 601 -100 665 -100 1875 -66 695 -132 425 -66 425 -66 263 -134 165 -134 99 -98 829 -66 601 -166 131 -102 565 -66 301 -100 1099 -100 601 -138 533 -66 667 -234 561 -66 99 -68 2741 -98 199 -100 531 -168 101 -434 1027 -68 431 -66 403 -132 99 -98 565 -132 135 -100 399 -166 271 -236 233 -166 197 -366 99 -66 99 -168 503 -66 199 -170 207 -100 673 -368 99 -66 263 -168 133 -98 397 -268 337 -66 131 -132 231 -132 501 -134 99 -168 567 -138 103 -136 267 -298 231 -134 197 -160 321 -332 231 -98 131 -164 257 -64 163 -328 395 -66 331 -202 65 -168 133 -68 167 -100 233 -102 335 -66 197 -326 1101 -132 589 -100 811 -132 399 -136 269 -102 497 -66 559 -100 129 -98 855 -68 637 -102 65 -200 875 -68 233 -166 167 -66 529 -202 235 -102 231 -66 1237 -66 733 -98 1723 -132 101 -100 297 -66 829 -232 197 -100 367 -134 169 -166 167 -434 633 -100 235 -200 131 -134 233 -100 131 -100 331 -134 495 -432 65 -528 161 -130 295 -132 337 -136 133 -166 165 -100 269 -240 201 -336 133 -166 165 -238 199 -202 431 -434 99 -134 501 -166 231 -96 559 -202 167 -66 717 -98 987 -198 65 -64 163 -64 227 -98 555 -164 199 -64 361 -66 163 -98 129 -162 97 -130 161 -460 197 -230 681 -98 197 -98 329 -100 267 -266 291 -264 65 -100 329 -100 459 -200 363 -98 165 -134 231 -134 301 -134 231 -302 99 -132 101 -134 267 -136 233 -68 393 -422 163 -166 361 -166 99 -134 365 -134 133 -336 401 -66 495 -132 401 -168 133 -402 501 -136 1093 -862 165 -132 293 -300 289 -66 131 -164 391 -134 99 -360 359 -130 323 -200 423 -98 195 -162 295 -132 161 -98 129 -782 131 -426 227 -64 259 -166 63 -160 323 -98 261 -230\nRAW_Data: 231 -66 921 -66 355 -64 1019 -98 227 -258 163 -66 597 -232 1313 -132 163 -404 467 -236 901 -164 483 -98 195 -96 489 -134 103 -238 169 -66 67 -68 299 -100 497 -68 65 -134 1635 -304 1153 -100 539 -168 265 -200 499 -166 535 -100 397 -168 931 -100 131 -66 631 -134 897 -270 1233 -100 65 -132 131 -334 663 -66 163 -66 131 -132 705 -98 571 -200 433 -100 237 -234 229 -132 1627 -66 569 -100 715 -66 1863 -272 265 -68 301 -98 465 -68 97 -134 99 -66 395 -136 1405 -66 529 -132 63 -196 579 -132 413 -260 129 -136 101 -166 1201 -134 833 -134 393 -66 335 -172 201 -68 1027 -96 753 -64 815 -66 97 -64 1341 -132 289 -160 127 -66 99 -228 1083 -96 163 -66 259 -64 159 -98 2409 -168 767 -200 367 -66 1675 -66 1067 -98 3407 -200 99 -66 1403 -166 99 -134 439 -200 329 -136 599 -66 637 -66 835 -66 1099 -98 99 -66 463 -166 165 -100 461 -164 3037 -66 655 -66 97 -98 229 -130 355 -132 1443 -66 527 -98 881 -98 229 -162 127 -96 583 -64 65 -162 489 -166 885 -194 257 -98 1539 -66 293 -166 229 -132 655 -98 757 -49522 271 -758 689 -1264 737 -670 293 -1152 811 -1144 341 -664 773 -678 327 -1118 807 -1144 835 -1146 781 -1126 873 -1096 347 -622 877 -624 321 -1106 843 -1098 871 -1098 843 -1106 379 -610 841 -584 381 -1122 365 -602 845 -1116 837 -610 381 -1056 889 -1078 383 -614 827 -1110 877 -592 353 -1108 845 -1120 839 -1120 347 -602 849 -1110 865 -612 361 -1072 869 -1114 351 -618 861 -618 343 -1090 853 -1106 387 -618 797 -674 347 -1084 389 -574 867 -584 381 -1114 841 -1102 845 -1116 839 -1112 843 -1098 875 -1086 383 -584 865 -588 375 -1100 861 -1112 851 -1084 853 -1108 847 -1106 381 -584 857 -610 383 -1080 357 -602 871 -602 385 -1084 383 -616 823 -610 373 -1086 381 -590 871 -1084 839 -628 353 -1102 875 -1100 349 -9404 875 -1060 871 -1086 887 -1088 879 -1058 863 -1086 855 -1132 845 -1078 871 -1076 857 -1098 881 -1082 861 -1088 843 -1120 853 -1074 879 -1074 879 -1068 889 -614 341 -1090 387 -616 863 -624 345 -1088 391 -590 857 -612 385 -1058 393 -596 843 -1088 889 -1078 879 -578 387 -1082 875 -1076 415 -550 881 -1070 877 -592 391 -1114 821 -1104 373 -620 821 -624 361 -1072 903 -1086 855 -1092 843 -1086 905 -1054 387 -614 863 -618 347 -1088 853 -1114 845 -1090 867 -1070 381 -610 885 -584 385 -1052 407 -578 877 -1052 899 -600 389 -1048 907 -1074 383 -586 877 -1072 877 -594 359 -1076 875 -1082 891 -1088 363 -616 855 -1084 857 -592 381 -1088 883 -1086 385 -572\nRAW_Data: 889 -624 353 -1082 853 -1096 379 -594 853 -624 353 -1092 417 -582 847 -612 385 -1076 847 -1080 883 -1052 913 -1044 907 -1076 849 -1088 383 -602 867 -616 361 -1068 901 -1072 865 -1104 831 -1080 879 -1098 397 -586 855 -626 355 -1084 381 -592 873 -616 351 -1084 385 -624 821 -620 359 -1086 387 -584 883 -1086 877 -592 355 -1106 853 -1086 387 -69570 97 -100 99 -2620 131 -636 333 -102 235 -236 67 -68 363 -66 201 -100 567 -102 267 -164 101 -134 65 -68 197 -68 297 -166 671 -100 469 -336 165 -100 201 -66 169 -230 169 -204 329 -624 67 -98 265 -232 193 -168 299 -100 235 -138 101 -370 165 -294 333 -622 231 -130 129 -130 353 -132 195 -162 359 -164 67 -68 333 -100 133 -688 235 -236 497 -198 293 -98 129 -296 293 -164 229 -128 229 -132 193 -400 165 -66 163 -98 361 -164 355 -196 587 -164 131 -98 263 -554 99 -130 129 -130 191 -464 99 -132 67 -100 167 -604 329 -66 199 -68 133 -102 163 -66 2971 -132 785 -66 329 -96 323 -100 201 -136 301 -66 1959 -166 867 -134 467 -66 297 -100 835 -100 753 -166 165 -64 67 -370 335 -66 559 -232 165 -334 65 -162 129 -354 163 -64 131 -134 265 -300 263 -132 267 -296 327 -198 99 -132 535 -132 469 -866 231 -860 99 -232 503 -134 99 -198 233 -134 267 -200 97 -358 297 -164 259 -98 227 -166 135 -66 323 -100 97 -294 131 -164 129 -98 295 -96 129 -426 299 -100 67 -102 623 -100 163 -194 127 -360 563 -134 199 -428 493 -98 229 -130 257 -64 165 -100 131 -98 163 -692 357 -64 161 -98 321 -64 389 -230 65 -692 227 -130 261 -132 231 -162 287 -298 97 -460 393 -130 301 -168 331 -100 269 -202 101 -134 201 -102 99 -132 199 -204 235 -664 65 -562 133 -328 463 -100 291 -194 159 -162 227 -98 293 -328 165 -128 227 -574 535 -332 197 -168 65 -300 131 -66 389 -1078 131 -64 259 -64 223 -98 257 -164 63 -328 433 -134 65 -602 131 -68 333 -136 369 -66 297 -264 427 -66 97 -130 429 -102 133 -136 203 -240 167 -236 329 -526 67 -132 133 -168 331 -360 65 -66 331 -296 267 -134 469 -132 595 -230 661 -662 299 -100 265 -200 203 -168 801 -100 133 -68 399 -132 99 -100 161 -390 65 -298 65 -98 261 -130 161 -128 257 -66 67 -134 621 -98 227 -328 99 -230 129 -294 193 -96 195 -318 425 -526 129 -196 163 -162 65 -132 293 -130 63 -66 325 -128 63 -130 293 -66 199 -200 269 -206 133 -198 325 -98 163 -100 97 -98 261 -164 67 -98 167 -430 131 -494 131 -164\nRAW_Data: 97 -98 861 -66 1199 -166 231 -100 651 -166 197 -104 439 -98 131 -64 493 -98 883 -96 99 -98 3327 -66 131 -264 733 -134 2133 -166 131 -102 303 -136 535 -134 701 -98 355 -228 131 -202 99 -134 99 -100 791 -166 169 -202 671 -100 741 -100 263 -66 165 -68 935 -132 197 -198 673 -100 605 -66 1457 -98 1195 -166 2347 -134 505 -100 1469 -66 391 -100 229 -100 1171 -98 939 -100 459 -170 369 -134 231 -162 127 -98 95 -66 195 -98 195 -66 299 -100 331 -98 65 -232 369 -132 201 -68 167 -166 1481 -102 501 -160 1257 -66 2307 -64 623 -164 2079 -66 1101 -98 423 -64 659 -68 431 -136 99 -100 435 -130 167 -168 835 -200 135 -104 133 -100 503 -68 1437 -232 821 -132 357 -96 463 -66 263 -64 683 -132 165 -96 655 -166 3939 -100 1169 -132 2443 -98 197 -132 425 -234 233 -162 1043 -66 197 -100 2793 -134 167 -104 675 -100 197 -134 1367 -102 763 -132 265 -230 133 -102 365 -100 167 -66 1069 -66 837 -100 295 -160 97 -64 129 -132 617 -164 197 -100 133 -136 337 -172 133 -66 557 -98 951 -66 263 -130 587 -66 729 -196 335 -166 933 -432 369 -100 199 -296 225 -98 355 -66 129 -64 557 -98 289 -66 355 -128 193 -162 267 -134 299 -98 165 -170 303 -640 1031 -134 99 -66 135 -68 771 -166 171 -104 201 -134 131 -68 635 -428 661 -292 749 -430 1161 -100 905 -98 65 -98 657 -262 2837 -132 67 -66 265 -132 631 -66 1037 -296 97 -98 1703 -302 367 -100 505 -232 497 -362 333 -134 591 -100 755 -232 67 -130 587 -66 231 -168 65 -332 99 -66 267 -232 393 -134 65 -132 131 -428 133 -200 165 -202 199 -168 165 -102 269 -100 333 -852 201 -134 233 -202 65 -200 563 -768 265 -136 169 -102 169 -598 333 -202 267 -134 267 -328 163 -130 625 -500 199 -200 99 -270 65 -134 65 -198 65 -100 99 -596 493 -66 99 -66 331 -232 103 -136 373 -168 831 -170 65 -672 163 -102 133 -136 331 -100 333 -234 101 -100 99 -200 99 -100 201 -302 199 -600 301 -202 135 -134 705 -166 435 -530 97 -198 131 -198 195 -66 163 -392 293 -66 295 -370 229 -198 65 -100 405 -134 165 -134 133 -170 337 -236 205 -274 267 -134 329 -132 195 -132 503 -132 133 -136 133 -334 197 -196 299 -168 101 -100 233 -100 439 -134 301 -332 331 -298 433 -406 433 -68 167 -100 203 -100 101 -102 99 -328 397 -234 205 -168 133 -364 63 -202 397 -198 95 -394 267 -134 569 -66 201 -102 133 -136 101 -102 99 -132 99 -196 197 -498 197 -102 135 -170\nRAW_Data: 331 -164 63 -162 1267 -66 163 -130 129 -66 725 -164 231 -64 853 -66 101 -134 199 -102 99 -68 365 -66 357 -130 815 -64 357 -98 97 -98 97 -66 65 -466 231 -172 3749 -66 849 -130 917 -64 327 -64 1013 -98 555 -332 795 -100 571 -132 769 -132 401 -134 1297 -134 377 -138 435 -100 401 -100 667 -100 1761 -66 667 -66 1533 -236 233 -98 885 -130 457 -66 999 -66 165 -66 833 -134 695 -166 501 -66 499 -200 329 -64 197 -134 441 -100 2099 -98 491 -134 197 -130 2225 -132 65 -100 689 -64 193 -160 159 -96 195 -98 323 -164 259 -98 535 -472 771 -66 665 -270 665 -66 595 -266 2191 -64 643 -98 1287 -98 741 -100 233 -200 569 -194 261 -68 637 -100 97 -66 491 -158 395 -138 1017 -66 627 -262 559 -64 327 -98 263 -134 99 -102 201 -102 337 -66 167 -68 679 -100 471 -134 195 -66 133 -202 693 -96 197 -98 391 -164 99 -98 3883 -194 461 -100 237 -168 1891 -68 301 -68 969 -166 1439 -294 551 -130 389 -98 99 -196 167 -102 505 -66 569 -234 901 -98 407 -136 469 -66 769 -98 769 -166 1263 -266 297 -98 1701 -200 203 -168 329 -232 65 -100 329 -164 803 -100 135 -200 233 -166 135 -272 265 -134 197 -100 133 -134 539 -232 197 -396 165 -366 263 -68 233 -102 365 -132 233 -100 135 -266 199 -234 167 -232 97 -524 127 -128 389 -98 305 -364 261 -130 257 -162 589 -464 361 -66 229 -134 161 -100 203 -432 265 -66 199 -66 199 -366 229 -236 99 -134 99 -100 131 -168 133 -100 131 -236 267 -132 297 -264 291 -132 167 -234 65 -100 199 -66 333 -730 237 -440 365 -102 99 -100 99 -132 99 -100 1429 -134 427 -100 97 -100 131 -164 799 -170 1077 -100 431 -66 133 -168 737 -134 197 -230 65 -102 803 -132 491 -98 429 -198 471 -134 365 -66 299 -236 65 -66 2837 -102 399 -64 585 -64 523 -196 97 -98 295 -196 555 -160 261 -500 299 -396 333 -236 133 -68 327 -100 199 -204 699 -66 701 -100 65 -164 65 -370 195 -196 97 -66 193 -130 129 -360 195 -130 231 -96 291 -64 455 -228 293 -196 291 -162 97 -194 621 -130 847 -66 395 -66 161 -128 193 -130 293 -98 231 -170 67 -134 297 -360 167 -266 263 -526 263 -132 229 -98 191 -160 159 -100 721 -234 101 -100 99 -130 259 -258 265 -632 687 -164 133 -134 631 -100 199 -102 165 -560 299 -200 265 -332 431 -870 99 -266 503 -364 135 -66 269 -68 499 -100 265 -102 263 -102 569 -234 719 -132 99 -196 419 -262 163 -688 95 -66 165 -128 95 -66\nRAW_Data: 295 -98 987 -196 517 -100 489 -66 355 -132 563 -198 867 -134 1413 -134 541 -134 767 -100 193 -98 1799 -102 467 -134 299 -96 323 -66 261 -100 259 -66 229 -96 851 -66 369 -266 469 -66 101 -98 163 -136 267 -432 859 -130 523 -66 197 -134 1027 -132 227 -194 393 -98 807 -166 235 -100 133 -66 165 -102 133 -136 371 -162 1411 -132 865 -200 471 -100 133 -68 299 -66 633 -98 329 -234 401 -98 1505 -132 133 -134 331 -262 163 -66 261 -98 289 -64 201 -68 1055 -96 391 -66 951 -298 265 -202 297 -66 401 -68 131 -100 1733 -98 941 -66 803 -98 847 -64 3701 -100 721 -160 357 -166 1799 -66 329 -100 99 -102 363 -198 167 -136 197 -66 567 -66 199 -236 1247 -166 2455 -68 1107 -200 235 -100 2355 -130 913 -98 877 -98 163 -196 97 -66 427 -100 801 -134 867 -98 263 -68 441 -134 561 -98 1671 -134 865 -68 935 -132 163 -102 975 -66 1343 -132 1339 -134 369 -100 1107 -66 1167 -168 631 -232 835 -66 1027 -132 333 -166 265 -98 1207 -98 223 -98 455 -64 2095 -134 933 -136 233 -68 335 -136 305 -100 1737 -66 427 -100 263 -130 323 -66 227 -66 717 -100 265 -100 65 -128 355 -66 367 -132 95 -230 229 -100 131 -64 493 -132 291 -396 393 -130 259 -196 227 -288 397 -68 229 -430 99 -302 237 -700 65 -66 65 -100 133 -200 101 -336 133 -166 237 -202 67 -302 67 -68 333 -132 263 -102 267 -296 163 -166 233 -168 363 -64 295 -298 537 -166 431 -200 431 -166 63 -258 363 -164 563 -234 199 -68 299 -100 325 -754 295 -196 65 -98 165 -132 301 -134 131 -134 97 -68 405 -68 233 -134 271 -134 67 -168 101 -136 133 -366 99 -132 67 -132 265 -200 233 -100 201 -136 101 -66 263 -132 129 -66 293 -582 263 -132 1103 -134 203 -168 97 -66 197 -264 131 -168 133 -132 65 -134 199 -134 101 -100 131 -436 99 -232 97 -398 231 -362 65 -202 301 -396 297 -98 199 -134 265 -164 101 -168 267 -102 405 -170 99 -102 397 -132 97 -98 295 -98 1179 -100 135 -136 131 -134 765 -134 465 -168 439 -232 403 -100 65 -134 931 -100 169 -136 237 -68 231 -234 199 -68 401 -134 541 -166 429 -166 1607 -368 533 -66 363 -66 133 -134 433 -166 297 -238 201 -100 201 -170 199 -134 273 -136 99 -134 167 -238 133 -66 265 -134 165 -132 165 -132 97 -228 723 -198 415 -64 491 -298 257 -66 231 -192 225 -96 227 -98 193 -96 521 -198 65 -66 231 -166 163 -98 465 -66 133 -132 195 -130 225 -162 521 -130 63 -66 199 -228\nRAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -100 131 -134 231 -66 329 -196 685 -96 557 -68 1263 -68 101 -68 397 -100 65 -66 625 -66 97 -132 1099 -66 493 -66 757 -98 1151 -66 303 -134 1901 -66 99 -100 665 -262 991 -98 791 -66 1925 -168 865 -232 835 -98 505 -102 99 -100 535 -100 169 -134 427 -132 863 -68 167 -134 975 -100 133 -268 1339 -100 1453 -66 1445 -162 195 -64 3623 -66 237 -68 1063 -308 1449 -98 1111 -132 167 -102 855 -270 199 -134 297 -134 267 -168 863 -234 637 -66 567 -230 99 -200 3325 -198 845 -66 289 -66 131 -66 815 -130 1093 -100 167 -100 429 -98 1703 -166 195 -64 971 -98 163 -192 195 -168 439 -132 329 -132 67 -134 67 -134 1591 -168 407 -100 867 -68 399 -134 661 -100 663 -66 237 -136 395 -232 131 -66 695 -100 627 -264 913 -66 1083 -98 287 -66 199 -132 335 -100 1031 -68 99 -100 3815 -98 165 -66 129 -98 163 -128 563 -98 779 -96 223 -64 161 -164 2025 -66 1741 -172 101 -136 203 -102 665 -100 475 -64 167 -100 637 -98 997 -170 1207 -136 233 -166 233 -168 635 -132 199 -100 235 -270 199 -98 131 -102 169 -170 293 -98 323 -164 427 -334 233 -168 267 -68 369 -100 263 -368 101 -66 665 -98 265 -100 133 -100 99 -168 133 -66 133 -132 133 -66 269 -134 435 -68 267 -136 271 -500 163 -100 163 -166 355 -132 97 -98 323 -194 63 -688 463 -130 97 -396 65 -100 357 -194 461 -98 161 -130 223 -162 165 -352 461 -300 267 -166 233 -464 329 -100 293 -362 163 -228 289 -66 229 -66 195 -162 325 -66 261 -98 127 -424 299 -302 367 -68 265 -272 429 -98 161 -98 393 -296 65 -130 161 -196 261 -66 473 -234 97 -98 263 -160 323 -98 67 -132 697 -298 99 -134 233 -202 97 -134 301 -200 307 -100 101 -134 865 -166 231 -202 233 -100 301 -170 169 -102 169 -200 65 -98 595 -166 231 -234 661 -66 473 -334 165 -304 365 -266 97 -502 363 -134 133 -236 65 -100 99 -134 99 -170 235 -66 333 -100 195 -100 133 -300 133 -102 301 -304 65 -100 99 -100 131 -202 135 -134 65 -200 363 -66 263 -498 67 -68 295 -194 321 -368 435 -100 97 -664 99 -100 569 -66 133 -66 67 -134 199 -136 101 -68 301 -68 405 -198 133 -132 581 -132 165 -98 159 -98 197 -66 229 -130 131 -294 133 -96 423 -100 427 -300 357 -132 291 -64 95 -194 455 -98 263 -100 359 -196 65 -162 227 -162 157 -96 157 -230 589 -132 325 -134 535 -66 267 -100 135 -302\nRAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366\nRAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164\nRAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98\nRAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510\nRAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172\nRAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477\nRAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100\nRAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86\nRAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144\nRAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940\nRAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617\nRAW_Data: 77 -76 131 -244 81 -210 55 -1428 53 -344 53 -238 51 -448 51 -804 125 -1490 51 -452 79 -1816 51 -176 197 -700 133 -563 51 -386 79 -474 109 -626 55 -266 103 -616 283 -1932 51 -1034 51 -2809 75 -244 83 -5339 77 -260 105 -839 107 -1806 53 -1408 81 -810 135 -488 187 -1469 73 -2596 75 -74 51 -726 113 -136 83 -406 55 -194 133 -606 55 -1018 55 -1774 51 -1954 75 -910 51 -944 137 -1337 51 -1606 101 -566 75 -584 51 -1470 133 -242 159 -2798 51 -1568 97 -100 71 -556 77 -1234 53 -320 53 -274 68337 -252 333 -278 333 -74 533 -280 307 -276 331 -6948 347 -262 329 -278 329 -278 329 -278 303 -304 303 -302 303 -304 303 -278 329 -278 329 -74 533 -294 325 -270 317 -6944 335 -282 309 -304 309 -304 281 -328 293 -310 281 -326 281 -326 281 -326 279 -302 305 -100 503 -306 305 -302 293 -6992 295 -294 291 -314 285 -336 283 -334 257 -328 283 -328 291 -310 281 -328 279 -328 279 -124 479 -332 279 -328 255 -7012 295 -324 271 -330 267 -346 261 -342 259 -334 257 -358 255 -356 257 -354 231 -354 255 -152 453 -356 257 -352 255 -7024 255 -352 257 -352 255 -354 255 -326 257 -352 255 -352 255 -354 255 -352 253 -352 255 -150 453 -356 255 -354 253 -7030 255 -352 267 -344 251 -354 253 -354 253 -354 267 -322 267 -350 261 -344 257 -364 231 -154 459 -360 257 -354 231 -7062 231 -380 231 -380 231 -352 257 -352 257 -352 257 -352 257 -352 257 -352 255 -354 231 -174 457 -360 229 -378 229 -7084 239 -364 239 -366 229 -380 229 -378 229 -380 229 -378 255 -354 255 -354 255 -352 255 -150 457 -364 253 -354 255 -9542 101 -3126 53 -814 109 -406 51 -162 109 -2219 183 -496 103 -1369 81 -603 99 -2172 79 -1103 75 -676 77 -560 103 -378 51 -654 95 -888 155 -1322 111 -1626 53 -182 51 -166 83 -52 181 -182 71 -2132 77 -2839 103 -4022 79 -362 81 -466 75 -970 203 -998 51 -2085 51 -1853 99 -328 75 -346 55 -1949 79 -2648 79 -434 75 -6757 51 -1920 109 -306 51 -612 101 -996 77 -764 81 -790 125 -1489 99 -430 77 -4142 165 -372 101 -198 71 -1688 51 -1636 99 -434 81 -794 135 -1973 79 -188 109 -2678 81 -196 109 -2099 51 -504 77 -1854 51 -910 107 -948 75 -122 131 -78 79 -1781 103 -3344 111 -406 79 -184 51 -408 103 -54 79 -1474 127 -1789 213 -683 131 -348 161 -5237 53 -2675 101 -52 105 -474 103 -1336 99 -3548 105 -1724 161 -2180 107 -2514 97 -3784 51 -910 77 -505 71 -494 131 -1154 79 -2295 75 -350 161 -274 81 -222\nRAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 187 -574 79 -332 51 -3765 51 -1042 75 -1413 103 -2163 75 -218 73 -4118 73 -716 51 -1720 51 -176 145 -817 79 -602 55 -1270 53 -2290 81 -346 79 -1840 53 -596 97 -1135 155 -1672 157 -1150 53 -52 101 -2753 153 -1546 158 -698 79 -2962 215 -490 161 -766 51 -2170 55 -811 51 -694 51 -1461 103 -2590 149 -3785 130 -54 103 -1108 103 -3978 51 -1626 81 -1825 109 -452 129 -1000 79 -1651 157 -276 53 -104 81 -2440 81 -1780 53 -1554 51 -512 131 -2508 99 -176 73 -914 51 -76 81 -202 111 -690 109 -790 109 -584 53 -244 79 -706 73 -550 129 -142 127 -546 77 -296 53 -874 105 -2623 51 -1004 77 -3131 79 -552 81 -2008 187 -1168 55 -1173 51 -2146 99 -700 51 -1580 101 -252 75 -474 77 -569 73 -5817 77 -614 77 -712 149 -228 73 -562 201 -274 127 -648 77 -578 75 -1810 109 -2106 171 -5996 81 -366 159 -274 55 -1228 77 -3386 53 -106 81 -1024 133 -2331 53 -2636 53 -780 149 -842 79 -2288 53 -2807 107 -1410 51 -620 75 -428 71 -272 75 -1140 103 -4912 55 -2261 53 -716 53 -3093 109 -502 111 -1492 53 -4317 51 -500 83 -338 129 -698 105 -1565 103 -1874 105 -344 77 -546 79 -2826 105 -260 75 -616 103 -1254 113 -2687 77 -977 73 -246 97 -1054 109 -4681 67101 -252 357 -252 333 -276 333 -252 355 -254 333 -276 331 -6932 347 -272 353 -242 343 -268 339 -286 309 -282 309 -306 307 -304 283 -328 281 -328 281 -102 505 -308 291 -314 279 -6996 281 -326 279 -328 277 -328 277 -328 279 -328 277 -328 279 -328 279 -328 277 -330 277 -124 483 -322 297 -298 293 -6972 283 -336 281 -332 257 -330 281 -328 283 -328 281 -330 265 -338 255 -352 253 -352 255 -150 455 -336 279 -328 279 -6992 295 -322 265 -348 261 -342 259 -334 259 -356 257 -356 255 -330 257 -354 257 -352 255 -152 455 -358 239 -368 253 -7026 243 -352 243 -350 265 -344 261 -362 231 -358 255 -356 257 -354 231 -380 229 -354 255 -150 455 -360 231 -376 231 -7050 229 -378 229 -378 229 -378 229 -378 229 -378 229 -378 229 -352 255 -352 253 -352 255 -150 455 -362 229 -378 227 -7050 253 -354 229 -378 229 -378 229 -378 227 -378 229 -378 229 -378 229 -378 229 -376 229 -174 431 -374 243 -378 241 -7032 261 -370 233 -362 233 -384 233 -356 233 -380 231 -380 231 -380 231 -380 231 -378 231 -176 431 -384 231 -380 211 -7114 231 -384 231 -384 205 -408 207 -408 207 -408 231 -384 231 -412 207 -412 181 -530 77 -1144\nRAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178\nRAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362\nRAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352\nRAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971\nRAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997\nRAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364\nRAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688\nRAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358\nRAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811\nRAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131\nRAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411\nRAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811\nRAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197\nRAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524\nRAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072\nRAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562\nRAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520\nRAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66\nRAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034\nRAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66\nRAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112\nRAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384\nRAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148\nRAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384\nRAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144\nRAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166\n"
  },
  {
    "path": "applications/debug/unit_tests/test_runner.c",
    "content": "#include \"test_runner.h\"\n\n#include \"tests/test_api.h\"\n\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/path.h>\n#include <toolbox/pipe.h>\n#include <loader/loader.h>\n#include <storage/storage.h>\n#include <notification/notification_messages.h>\n\n#include <loader/firmware_api/firmware_api.h>\n#include <flipper_application/flipper_application.h>\n#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/plugins/composite_resolver.h>\n\nextern const ElfApiInterface* const unit_tests_api_interface;\n\n#define TAG \"TestRunner\"\n\n#define PLUGINS_PATH \"/ext/apps_data/unit_tests/plugins\"\n\nstruct TestRunner {\n    Storage* storage;\n    Loader* loader;\n    NotificationApp* notification;\n\n    // Temporary used things\n    PipeSide* pipe;\n    FuriString* args;\n\n    // ELF related stuff\n    CompositeApiResolver* composite_resolver;\n\n    // Report data\n    int minunit_run;\n    int minunit_assert;\n    int minunit_fail;\n    int minunit_status;\n};\n\nTestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args) {\n    TestRunner* instance = malloc(sizeof(TestRunner));\n\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    instance->loader = furi_record_open(RECORD_LOADER);\n    instance->notification = furi_record_open(RECORD_NOTIFICATION);\n\n    instance->pipe = pipe;\n    instance->args = args;\n\n    instance->composite_resolver = composite_api_resolver_alloc();\n    composite_api_resolver_add(instance->composite_resolver, firmware_api_interface);\n    composite_api_resolver_add(instance->composite_resolver, unit_tests_api_interface);\n\n    return instance;\n}\n\nvoid test_runner_free(TestRunner* instance) {\n    furi_assert(instance);\n\n    composite_api_resolver_free(instance->composite_resolver);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    instance->notification = NULL;\n\n    furi_record_close(RECORD_LOADER);\n    instance->loader = NULL;\n\n    furi_record_close(RECORD_STORAGE);\n    instance->storage = NULL;\n\n    free(instance);\n}\n\n#define TEST_RUNNER_TMP_DIR            EXT_PATH(\".tmp\")\n#define TEST_RUNNER_TMP_UNIT_TESTS_DIR TEST_RUNNER_TMP_DIR \"/unit_tests\"\n\nstatic bool test_runner_run_plugin(TestRunner* instance, const char* path) {\n    furi_assert(instance);\n\n    FURI_LOG_D(TAG, \"Loading %s\", path);\n    FlipperApplication* lib = flipper_application_alloc(\n        instance->storage, composite_api_resolver_get(instance->composite_resolver));\n\n    bool result = false;\n    instance->minunit_fail = -1;\n    do {\n        FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path);\n\n        if(preload_res != FlipperApplicationPreloadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to preload %s, %d\", path, preload_res);\n            break;\n        }\n\n        if(!flipper_application_is_plugin(lib)) {\n            FURI_LOG_E(TAG, \"Not a plugin %s\", path);\n            break;\n        }\n\n        FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib);\n        if(load_status != FlipperApplicationLoadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to load %s\", path);\n            break;\n        }\n\n        const FlipperAppPluginDescriptor* app_descriptor =\n            flipper_application_plugin_get_descriptor(lib);\n\n        const TestApi* test = app_descriptor->entry_point;\n\n        instance->minunit_fail = test->run();\n\n        instance->minunit_run += test->get_minunit_run();\n        instance->minunit_assert += test->get_minunit_assert();\n        instance->minunit_status += test->get_minunit_status();\n\n        result = (instance->minunit_fail == 0);\n    } while(false);\n\n    flipper_application_free(lib);\n\n    return result;\n}\n\nstatic void test_runner_run_internal(TestRunner* instance) {\n    furi_assert(instance);\n\n    char file_name_buffer[256];\n    FuriString* file_name = furi_string_alloc();\n    FuriString* file_basename = furi_string_alloc();\n    File* directory = storage_file_alloc(instance->storage);\n\n    do {\n        if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_DIR)) {\n            FURI_LOG_E(TAG, \"Cannot create dir %s\", TEST_RUNNER_TMP_DIR);\n            break;\n        }\n\n        if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_UNIT_TESTS_DIR)) {\n            FURI_LOG_E(TAG, \"Cannot create dir %s\", TEST_RUNNER_TMP_UNIT_TESTS_DIR);\n            break;\n        }\n\n        if(!storage_dir_open(directory, PLUGINS_PATH)) {\n            FURI_LOG_E(TAG, \"Failed to open directory %s\", PLUGINS_PATH);\n            break;\n        }\n\n        while(true) {\n            if(cli_is_pipe_broken_or_is_etx_next_char(instance->pipe)) {\n                break;\n            }\n\n            if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) {\n                break;\n            }\n\n            furi_string_set(file_name, file_name_buffer);\n            if(!furi_string_end_with_str(file_name, \".fal\")) {\n                continue;\n            }\n\n            path_concat(PLUGINS_PATH, file_name_buffer, file_name);\n\n            path_extract_filename(file_name, file_basename, true);\n            const char* file_basename_cstr = furi_string_get_cstr(file_basename);\n\n            bool result = true;\n            if(furi_string_size(instance->args)) {\n                if(furi_string_cmp_str(instance->args, file_basename_cstr) == 0) {\n                    result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));\n                } else {\n                    printf(\"Skipping %s\\r\\n\", file_basename_cstr);\n                }\n            } else {\n                result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));\n            }\n\n            if(!result) {\n                printf(\"Failed to execute test: %s\\r\\n\", file_basename_cstr);\n                break;\n            }\n        }\n    } while(false);\n\n    storage_dir_close(directory);\n    storage_file_free(directory);\n    furi_string_free(file_name);\n    furi_string_free(file_basename);\n}\n\nvoid test_runner_run(TestRunner* instance) {\n    furi_assert(instance);\n\n    // TODO FL-3491: lock device while test running\n    if(loader_is_locked(instance->loader)) {\n        printf(\"RPC: stop all applications to run tests\\r\\n\");\n        notification_message(instance->notification, &sequence_blink_magenta_100);\n    } else {\n        notification_message_block(instance->notification, &sequence_set_only_blue_255);\n\n        uint32_t heap_before = memmgr_get_free_heap();\n        uint32_t cycle_counter = furi_get_tick();\n\n        test_runner_run_internal(instance);\n\n        if(instance->minunit_run != 0) {\n            printf(\"\\r\\nFailed tests: %d\\r\\n\", instance->minunit_fail);\n\n            // Time report\n            cycle_counter = (furi_get_tick() - cycle_counter);\n            printf(\"Consumed: %lu ms\\r\\n\", cycle_counter);\n\n            // Wait for tested services and apps to deallocate memory\n            furi_delay_ms(200);\n            uint32_t heap_after = memmgr_get_free_heap();\n            printf(\"Leaked: %ld\\r\\n\", heap_before - heap_after);\n\n            // Final Report\n            if(instance->minunit_fail == 0) {\n                notification_message(instance->notification, &sequence_success);\n                printf(\"Status: PASSED\\r\\n\");\n            } else {\n                notification_message(instance->notification, &sequence_error);\n                printf(\"Status: FAILED\\r\\n\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/test_runner.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <toolbox/pipe.h>\n\ntypedef struct TestRunner TestRunner;\n\nTestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args);\n\nvoid test_runner_free(TestRunner* instance);\n\nvoid test_runner_run(TestRunner* instance);\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/args/args_test.c",
    "content": "\n#include \"../test.h\" // IWYU pragma: keep\n#include <toolbox/args.h>\n\nconst uint32_t one_ms = 1;\nconst uint32_t one_s = 1000 * one_ms;\nconst uint32_t one_m = 60 * one_s;\nconst uint32_t one_h = 60 * one_m;\n\nMU_TEST(args_read_duration_default_values_test) {\n    FuriString* args_string;\n    uint32_t value = 0;\n\n    // Check default == NULL (ms)\n    args_string = furi_string_alloc_set_str(\"1\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, one_ms);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check default == ms\n    args_string = furi_string_alloc_set_str(\"1\");\n    mu_check(args_read_duration(args_string, &value, \"ms\"));\n    mu_assert_int_eq(value, one_ms);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check default == s\n    args_string = furi_string_alloc_set_str(\"1\");\n    mu_check(args_read_duration(args_string, &value, \"s\"));\n    mu_assert_int_eq(value, one_s);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check default == m\n    args_string = furi_string_alloc_set_str(\"1\");\n    mu_check(args_read_duration(args_string, &value, \"m\"));\n    mu_assert_int_eq(value, one_m);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check default == h\n    args_string = furi_string_alloc_set_str(\"1\");\n    mu_check(args_read_duration(args_string, &value, \"h\"));\n    mu_assert_int_eq(value, one_h);\n    furi_string_free(args_string);\n    value = 0;\n}\n\nMU_TEST(args_read_duration_suffix_values_test) {\n    FuriString* args_string;\n    uint32_t value = 0;\n\n    // Check ms\n    args_string = furi_string_alloc_set_str(\"1ms\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, one_ms);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check s\n    args_string = furi_string_alloc_set_str(\"1s\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, one_s);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check m\n    args_string = furi_string_alloc_set_str(\"1m\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, one_m);\n    furi_string_free(args_string);\n    value = 0;\n\n    // Check h\n    args_string = furi_string_alloc_set_str(\"1h\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, one_h);\n    furi_string_free(args_string);\n    value = 0;\n}\n\nMU_TEST(args_read_duration_values_test) {\n    FuriString* args_string;\n    uint32_t value = 0;\n\n    // Check for ms\n    args_string = furi_string_alloc_set_str(\"4294967295ms\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 4294967295U);\n    furi_string_free(args_string);\n\n    // Check for s\n    args_string = furi_string_alloc_set_str(\"4294967s\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 4294967U * one_s);\n    furi_string_free(args_string);\n\n    // Check for m\n    args_string = furi_string_alloc_set_str(\"71582m\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 71582U * one_m);\n    furi_string_free(args_string);\n\n    // Check for h\n    args_string = furi_string_alloc_set_str(\"1193h\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 1193U * one_h);\n    furi_string_free(args_string);\n\n    // Check for ms in float\n    args_string = furi_string_alloc_set_str(\"4.2ms\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 4);\n    furi_string_free(args_string);\n\n    // Check for s in float\n    args_string = furi_string_alloc_set_str(\"1.5s\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, (uint32_t)(1.5 * one_s));\n    furi_string_free(args_string);\n\n    // Check for m in float\n    args_string = furi_string_alloc_set_str(\"1.5m\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, (uint32_t)(1.5 * one_m));\n    furi_string_free(args_string);\n\n    // Check for h in float\n    args_string = furi_string_alloc_set_str(\"1.5h\");\n    mu_check(args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, (uint32_t)(1.5 * one_h));\n    furi_string_free(args_string);\n}\n\nMU_TEST(args_read_duration_errors_test) {\n    FuriString* args_string;\n    uint32_t value = 0;\n\n    // Check wrong suffix\n    args_string = furi_string_alloc_set_str(\"1x\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check wrong suffix\n    args_string = furi_string_alloc_set_str(\"1xs\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check negative value\n    args_string = furi_string_alloc_set_str(\"-1s\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check wrong values\n\n    // Check only suffix\n    args_string = furi_string_alloc_set_str(\"s\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check doubled point\n    args_string = furi_string_alloc_set_str(\"0.1.1\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check overflow values\n\n    // Check for ms\n    args_string = furi_string_alloc_set_str(\"4294967296ms\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check for s\n    args_string = furi_string_alloc_set_str(\"4294968s\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check for m\n    args_string = furi_string_alloc_set_str(\"71583m\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n\n    // Check for h\n    args_string = furi_string_alloc_set_str(\"1194h\");\n    mu_check(!args_read_duration(args_string, &value, NULL));\n    mu_assert_int_eq(value, 0);\n    furi_string_free(args_string);\n}\n\nMU_TEST_SUITE(toolbox_args_read_duration_suite) {\n    MU_RUN_TEST(args_read_duration_default_values_test);\n    MU_RUN_TEST(args_read_duration_suffix_values_test);\n    MU_RUN_TEST(args_read_duration_values_test);\n    MU_RUN_TEST(args_read_duration_errors_test);\n}\n\nint run_minunit_test_toolbox_args(void) {\n    MU_RUN_SUITE(toolbox_args_read_duration_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_toolbox_args)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n#include <bit_lib/bit_lib.h>\n\nMU_TEST(test_bit_lib_increment_index) {\n    uint32_t index = 0;\n\n    // test increment\n    for(uint32_t i = 0; i < 31; ++i) {\n        bit_lib_increment_index(index, 32);\n        mu_assert_int_eq(i + 1, index);\n    }\n\n    // test wrap around\n    for(uint32_t i = 0; i < 512; ++i) {\n        bit_lib_increment_index(index, 32);\n        mu_assert_int_less_than(32, index);\n    }\n}\n\nMU_TEST(test_bit_lib_is_set) {\n    uint32_t value = 0x0000FFFF;\n\n    for(uint32_t i = 0; i < 16; ++i) {\n        mu_check(bit_lib_bit_is_set(value, i));\n        mu_check(!bit_lib_bit_is_not_set(value, i));\n    }\n\n    for(uint32_t i = 16; i < 32; ++i) {\n        mu_check(!bit_lib_bit_is_set(value, i));\n        mu_check(bit_lib_bit_is_not_set(value, i));\n    }\n}\n\nMU_TEST(test_bit_lib_push) {\n#define TEST_BIT_LIB_PUSH_DATA_SIZE 4\n    uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0};\n    uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF};\n    uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00};\n    uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF};\n    uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF};\n    uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00};\n    uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC};\n\n    for(uint32_t i = 0; i < 12; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);\n    }\n    mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n\n    for(uint32_t i = 0; i < 12; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);\n    }\n    mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n\n    for(uint32_t i = 0; i < 4; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);\n    }\n    for(uint32_t i = 0; i < 8; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);\n    }\n    mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n\n    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);\n    }\n    mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n\n    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);\n    }\n    mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n\n    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) {\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);\n        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);\n    }\n    mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE);\n}\n\nMU_TEST(test_bit_lib_set_bit) {\n    uint8_t value[2] = {0x00, 0xFF};\n    bit_lib_set_bit(value, 15, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2);\n    bit_lib_set_bit(value, 14, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2);\n    bit_lib_set_bit(value, 13, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2);\n    bit_lib_set_bit(value, 12, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2);\n    bit_lib_set_bit(value, 11, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2);\n    bit_lib_set_bit(value, 10, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2);\n    bit_lib_set_bit(value, 9, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2);\n    bit_lib_set_bit(value, 8, false);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2);\n\n    bit_lib_set_bit(value, 7, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2);\n    bit_lib_set_bit(value, 6, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2);\n    bit_lib_set_bit(value, 5, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2);\n    bit_lib_set_bit(value, 4, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2);\n    bit_lib_set_bit(value, 3, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2);\n    bit_lib_set_bit(value, 2, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2);\n    bit_lib_set_bit(value, 1, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2);\n    bit_lib_set_bit(value, 0, true);\n    mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2);\n}\n\nMU_TEST(test_bit_lib_set_bits) {\n    uint8_t value[2] = {0b00000000, 0b11111111};\n    // set 4 bits to 0b0100 from 12 index\n    bit_lib_set_bits(value, 12, 0b0100, 4);\n    //                                                    [0100]\n    mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2);\n\n    // set 2 bits to 0b11 from 11 index\n    bit_lib_set_bits(value, 11, 0b11, 2);\n    //                                                    [11]\n    mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2);\n\n    // set 3 bits to 0b111 from 0 index\n    bit_lib_set_bits(value, 0, 0b111, 3);\n    //                                    [111]\n    mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2);\n\n    // set 8 bits to 0b11111000 from 3 index\n    bit_lib_set_bits(value, 3, 0b11111000, 8);\n    //                                       [11111    000]\n    mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2);\n}\n\nMU_TEST(test_bit_lib_get_bit) {\n    uint8_t value[2] = {0b00000000, 0b11111111};\n    for(uint32_t i = 0; i < 8; ++i) {\n        mu_check(bit_lib_get_bit(value, i) == false);\n    }\n    for(uint32_t i = 8; i < 16; ++i) {\n        mu_check(bit_lib_get_bit(value, i) == true);\n    }\n}\n\nMU_TEST(test_bit_lib_get_bits) {\n    uint8_t value[2] = {0b00000000, 0b11111111};\n    mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8));\n    mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8));\n    mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8));\n    mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8));\n    mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8));\n    mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8));\n    mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8));\n    mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8));\n    mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8));\n}\n\nMU_TEST(test_bit_lib_get_bits_16) {\n    uint8_t value[2] = {0b00001001, 0b10110001};\n    mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1));\n    mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2));\n    mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3));\n    mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4));\n    mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5));\n    mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6));\n    mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7));\n    mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8));\n    mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9));\n    mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10));\n    mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11));\n    mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12));\n    mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13));\n    mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14));\n    mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15));\n    mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16));\n}\n\nMU_TEST(test_bit_lib_get_bits_32) {\n    uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010};\n    mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1));\n    mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2));\n    mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3));\n    mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4));\n    mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5));\n    mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6));\n    mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7));\n    mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8));\n    mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9));\n    mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10));\n    mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11));\n    mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12));\n    mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13));\n    mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14));\n    mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15));\n    mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16));\n    mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17));\n    mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18));\n    mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19));\n    mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20));\n    mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21));\n    mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22));\n    mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23));\n    mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24));\n    mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25));\n    mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26));\n    mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27));\n    mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28));\n    mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29));\n    mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30));\n    mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31));\n    mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32));\n}\n\nMU_TEST(test_bit_lib_get_bits_64) {\n    uint8_t value[8] = {\n        0b00001001,\n        0b10110001,\n        0b10001100,\n        0b01100010,\n        0b00001001,\n        0b10110001,\n        0b10001100,\n        0b01100010};\n    mu_assert_int_eq(0b0, bit_lib_get_bits_64(value, 0, 1));\n    mu_assert_int_eq(0b00, bit_lib_get_bits_64(value, 0, 2));\n    mu_assert_int_eq(0b000, bit_lib_get_bits_64(value, 0, 3));\n    mu_assert_int_eq(0b0000, bit_lib_get_bits_64(value, 0, 4));\n    mu_assert_int_eq(0b00001, bit_lib_get_bits_64(value, 0, 5));\n    mu_assert_int_eq(0b000010, bit_lib_get_bits_64(value, 0, 6));\n    mu_assert_int_eq(0b0000100, bit_lib_get_bits_64(value, 0, 7));\n    mu_assert_int_eq(0b00001001, bit_lib_get_bits_64(value, 0, 8));\n    mu_assert_int_eq(0b000010011, bit_lib_get_bits_64(value, 0, 9));\n    mu_assert_int_eq(0b0000100110, bit_lib_get_bits_64(value, 0, 10));\n    mu_assert_int_eq(0b00001001101, bit_lib_get_bits_64(value, 0, 11));\n    mu_assert_int_eq(0b000010011011, bit_lib_get_bits_64(value, 0, 12));\n    mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_64(value, 0, 13));\n    mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_64(value, 0, 14));\n    mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_64(value, 0, 15));\n    mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_64(value, 0, 16));\n    mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_64(value, 0, 17));\n    mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_64(value, 0, 18));\n    mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_64(value, 0, 19));\n    mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_64(value, 0, 20));\n    mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_64(value, 0, 21));\n    mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_64(value, 0, 22));\n    mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_64(value, 0, 23));\n    mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_64(value, 0, 24));\n    mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_64(value, 0, 25));\n    mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_64(value, 0, 26));\n    mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_64(value, 0, 27));\n    mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_64(value, 0, 28));\n    mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_64(value, 0, 29));\n    mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_64(value, 0, 30));\n    mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_64(value, 0, 31));\n    mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_64(value, 0, 32));\n\n    uint64_t res = bit_lib_get_bits_64(value, 0, 33);\n    uint64_t expected = 0b000010011011000110001100011000100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 34);\n    expected = 0b0000100110110001100011000110001000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 35);\n    expected = 0b00001001101100011000110001100010000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 36);\n    expected = 0b000010011011000110001100011000100000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 37);\n    expected = 0b0000100110110001100011000110001000001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 38);\n    expected = 0b00001001101100011000110001100010000010;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 39);\n    expected = 0b000010011011000110001100011000100000100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 40);\n    expected = 0b0000100110110001100011000110001000001001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 41);\n    expected = 0b00001001101100011000110001100010000010011;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 42);\n    expected = 0b000010011011000110001100011000100000100110;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 43);\n    expected = 0b0000100110110001100011000110001000001001101;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 44);\n    expected = 0b00001001101100011000110001100010000010011011;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 45);\n    expected = 0b000010011011000110001100011000100000100110110;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 46);\n    expected = 0b0000100110110001100011000110001000001001101100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 47);\n    expected = 0b00001001101100011000110001100010000010011011000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 48);\n    expected = 0b000010011011000110001100011000100000100110110001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 49);\n    expected = 0b0000100110110001100011000110001000001001101100011;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 50);\n    expected = 0b00001001101100011000110001100010000010011011000110;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 51);\n    expected = 0b000010011011000110001100011000100000100110110001100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 52);\n    expected = 0b0000100110110001100011000110001000001001101100011000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 53);\n    expected = 0b00001001101100011000110001100010000010011011000110001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 54);\n    expected = 0b000010011011000110001100011000100000100110110001100011;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 55);\n    expected = 0b0000100110110001100011000110001000001001101100011000110;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 56);\n    expected = 0b00001001101100011000110001100010000010011011000110001100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 57);\n    expected = 0b000010011011000110001100011000100000100110110001100011000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 58);\n    expected = 0b0000100110110001100011000110001000001001101100011000110001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 59);\n    expected = 0b00001001101100011000110001100010000010011011000110001100011;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 60);\n    expected = 0b000010011011000110001100011000100000100110110001100011000110;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 61);\n    expected = 0b0000100110110001100011000110001000001001101100011000110001100;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 62);\n    expected = 0b00001001101100011000110001100010000010011011000110001100011000;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 63);\n    expected = 0b000010011011000110001100011000100000100110110001100011000110001;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n\n    res = bit_lib_get_bits_64(value, 0, 64);\n    expected = 0b0000100110110001100011000110001000001001101100011000110001100010;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n}\n\nMU_TEST(test_bit_lib_test_parity_u32) {\n    // test even parity\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1);\n\n    // test odd parity\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1);\n    mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0);\n}\n\nMU_TEST(test_bit_lib_test_parity) {\n    // next data contains valid parity for 1-3 nibble and invalid for 4 nibble\n    uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111};\n    uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000};\n    uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111};\n    uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011};\n\n    // test alawys 0 parity\n    mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4));\n    mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4));\n    mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4));\n    mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4));\n\n    mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4));\n    mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4));\n    mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4));\n    mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4));\n\n    // test alawys 1 parity\n    mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4));\n    mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4));\n    mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4));\n    mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4));\n\n    mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4));\n    mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4));\n    mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4));\n    mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4));\n\n    // test odd parity\n    mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4));\n    mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4));\n    mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4));\n    mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4));\n\n    mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4));\n    mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4));\n    mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4));\n    mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4));\n\n    // test even parity\n    mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4));\n    mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4));\n    mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4));\n    mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4));\n\n    mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4));\n    mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4));\n    mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4));\n    mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4));\n}\n\nMU_TEST(test_bit_lib_remove_bit_every_nth) {\n    // TODO FL-3494: more tests\n    uint8_t data_i[1] = {0b00001111};\n    uint8_t data_o[1] = {0b00011111};\n    size_t length;\n\n    length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3);\n    mu_assert_int_eq(6, length);\n    mu_assert_mem_eq(data_o, data_i, 1);\n}\n\nMU_TEST(test_bit_lib_reverse_bits) {\n    uint8_t data_1_i[2] = {0b11001010, 0b00011111};\n    uint8_t data_1_o[2] = {0b11111000, 0b01010011};\n\n    // reverse bits [0..15]\n    bit_lib_reverse_bits(data_1_i, 0, 16);\n    mu_assert_mem_eq(data_1_o, data_1_i, 2);\n\n    uint8_t data_2_i[2] = {0b11001010, 0b00011111};\n    uint8_t data_2_o[2] = {0b11001000, 0b01011111};\n\n    // reverse bits [4..11]\n    bit_lib_reverse_bits(data_2_i, 4, 8);\n    mu_assert_mem_eq(data_2_o, data_2_i, 2);\n}\n\nMU_TEST(test_bit_lib_copy_bits) {\n    uint8_t data_1_i[2] = {0b11001010, 0b00011111};\n    uint8_t data_1_o[2] = {0};\n\n    // data_1_o[0..15] = data_1_i[0..15]\n    bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0);\n    mu_assert_mem_eq(data_1_i, data_1_o, 2);\n\n    memset(data_1_o, 0, 2);\n    // data_1_o[4..11] = data_1_i[0..7]\n    bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0);\n    mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2);\n}\n\nMU_TEST(test_bit_lib_get_bit_count) {\n    mu_assert_int_eq(0, bit_lib_get_bit_count(0));\n    mu_assert_int_eq(1, bit_lib_get_bit_count(0b1));\n    mu_assert_int_eq(1, bit_lib_get_bit_count(0b10));\n    mu_assert_int_eq(2, bit_lib_get_bit_count(0b11));\n    mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011));\n    mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011));\n    mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111));\n    mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111));\n    mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111));\n}\n\nMU_TEST(test_bit_lib_reverse_16_fast) {\n    mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000));\n    mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001));\n    mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011));\n    mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000));\n}\n\nMU_TEST(test_bit_lib_crc16) {\n    uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};\n    uint8_t data_size = 9;\n\n    // Algorithm\n    // Check\tPoly\tInit\tRefIn\tRefOut\tXorOut\n    // CRC-16/CCITT-FALSE\n    // 0x29B1\t0x1021\t0xFFFF\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000));\n    // CRC-16/ARC\n    // 0xBB3D\t0x8005\t0x0000\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000));\n    // CRC-16/AUG-CCITT\n    // 0xE5CC\t0x1021\t0x1D0F\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000));\n    // CRC-16/BUYPASS\n    // 0xFEE8\t0x8005\t0x0000\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000));\n    // CRC-16/CDMA2000\n    // 0x4C06\t0xC867\t0xFFFF\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000));\n    // CRC-16/DDS-110\n    // 0x9ECF\t0x8005\t0x800D\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000));\n    // CRC-16/DECT-R\n    // 0x007E\t0x0589\t0x0000\tfalse\tfalse\t0x0001\n    mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001));\n    // CRC-16/DECT-X\n    // 0x007F\t0x0589\t0x0000\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000));\n    // CRC-16/DNP\n    // 0xEA82\t0x3D65\t0x0000\ttrue\ttrue\t0xFFFF\n    mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF));\n    // CRC-16/EN-13757\n    // 0xC2B7\t0x3D65\t0x0000\tfalse\tfalse\t0xFFFF\n    mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF));\n    // CRC-16/GENIBUS\n    // 0xD64E\t0x1021\t0xFFFF\tfalse\tfalse\t0xFFFF\n    mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF));\n    // CRC-16/MAXIM\n    // 0x44C2\t0x8005\t0x0000\ttrue\ttrue\t0xFFFF\n    mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF));\n    // CRC-16/MCRF4XX\n    // 0x6F91\t0x1021\t0xFFFF\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000));\n    // CRC-16/RIELLO\n    // 0x63D0\t0x1021\t0xB2AA\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000));\n    // CRC-16/T10-DIF\n    // 0xD0DB\t0x8BB7\t0x0000\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000));\n    // CRC-16/TELEDISK\n    // 0x0FB3\t0xA097\t0x0000\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000));\n    // CRC-16/TMS37157\n    // 0x26B1\t0x1021\t0x89EC\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000));\n    // CRC-16/USB\n    // 0xB4C8\t0x8005\t0xFFFF\ttrue\ttrue\t0xFFFF\n    mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF));\n    // CRC-A\n    // 0xBF05\t0x1021\t0xC6C6\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000));\n    // CRC-16/KERMIT\n    // 0x2189\t0x1021\t0x0000\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000));\n    // CRC-16/MODBUS\n    // 0x4B37\t0x8005\t0xFFFF\ttrue\ttrue\t0x0000\n    mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000));\n    // CRC-16/X-25\n    // 0x906E\t0x1021\t0xFFFF\ttrue\ttrue\t0xFFFF\n    mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF));\n    // CRC-16/XMODEM\n    // 0x31C3\t0x1021\t0x0000\tfalse\tfalse\t0x0000\n    mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000));\n}\n\nMU_TEST(test_bit_lib_num_to_bytes_be) {\n    uint8_t src[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};\n    uint8_t dest[8];\n\n    bit_lib_num_to_bytes_be(0x01, 1, dest);\n    mu_assert_mem_eq(src, dest, sizeof(src[0]));\n\n    bit_lib_num_to_bytes_be(0x0123456789ABCDEF, 4, dest);\n    mu_assert_mem_eq(src + 4, dest, 4 * sizeof(src[0]));\n\n    bit_lib_num_to_bytes_be(0x0123456789ABCDEF, 8, dest);\n    mu_assert_mem_eq(src, dest, 8 * sizeof(src[0]));\n\n    bit_lib_num_to_bytes_be(bit_lib_bytes_to_num_be(src, 8), 8, dest);\n    mu_assert_mem_eq(src, dest, 8 * sizeof(src[0]));\n}\n\nMU_TEST(test_bit_lib_num_to_bytes_le) {\n    uint8_t dest[8];\n\n    uint8_t n2b_le_expected_1[] = {0x01};\n    bit_lib_num_to_bytes_le(0x01, 1, dest);\n    mu_assert_mem_eq(n2b_le_expected_1, dest, sizeof(n2b_le_expected_1[0]));\n\n    uint8_t n2b_le_expected_2[] = {0xEF, 0xCD, 0xAB, 0x89};\n    bit_lib_num_to_bytes_le(0x0123456789ABCDEF, 4, dest);\n    mu_assert_mem_eq(n2b_le_expected_2, dest, 4 * sizeof(n2b_le_expected_2[0]));\n\n    uint8_t n2b_le_expected_3[] = {0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01};\n    bit_lib_num_to_bytes_le(0x0123456789ABCDEF, 8, dest);\n    mu_assert_mem_eq(n2b_le_expected_3, dest, 8 * sizeof(n2b_le_expected_3[0]));\n\n    bit_lib_num_to_bytes_le(bit_lib_bytes_to_num_le(n2b_le_expected_3, 8), 8, dest);\n    mu_assert_mem_eq(n2b_le_expected_3, dest, 8 * sizeof(n2b_le_expected_3[0]));\n}\n\nMU_TEST(test_bit_lib_bytes_to_num_be) {\n    uint8_t src[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};\n    uint64_t res;\n\n    res = bit_lib_bytes_to_num_be(src, 1);\n    mu_assert_int_eq(0x01, res);\n\n    res = bit_lib_bytes_to_num_be(src, 4);\n    mu_assert_int_eq(0x01234567, res);\n\n    res = bit_lib_bytes_to_num_be(src, 8);\n    uint64_t expected = 0x0123456789ABCDEF;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n}\n\nMU_TEST(test_bit_lib_bytes_to_num_le) {\n    uint8_t src[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};\n    uint64_t res;\n\n    res = bit_lib_bytes_to_num_le(src, 1);\n    mu_assert_int_eq(0x01, res);\n\n    res = bit_lib_bytes_to_num_le(src, 4);\n    mu_assert_int_eq(0x67452301, res);\n\n    res = bit_lib_bytes_to_num_le(src, 8);\n    uint64_t expected = 0xEFCDAB8967452301;\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n}\n\nMU_TEST(test_bit_lib_bytes_to_num_bcd) {\n    uint8_t src[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};\n    uint64_t res;\n    bool is_bcd_res;\n\n    res = bit_lib_bytes_to_num_bcd(src, 1, &is_bcd_res);\n    mu_assert_int_eq(01, res);\n    mu_assert_int_eq(true, is_bcd_res);\n\n    res = bit_lib_bytes_to_num_bcd(src, 4, &is_bcd_res);\n    mu_assert_int_eq(1234567, res);\n    mu_assert_int_eq(true, is_bcd_res);\n\n    uint8_t digits[5] = {0x98, 0x76, 0x54, 0x32, 0x10};\n    uint64_t expected = 9876543210;\n    res = bit_lib_bytes_to_num_bcd(digits, 5, &is_bcd_res);\n    mu_assert_mem_eq(&expected, &res, sizeof(expected));\n    mu_assert_int_eq(true, is_bcd_res);\n\n    res = bit_lib_bytes_to_num_bcd(src, 8, &is_bcd_res);\n    mu_assert_int_eq(false, is_bcd_res);\n}\n\nMU_TEST_SUITE(test_bit_lib) {\n    MU_RUN_TEST(test_bit_lib_increment_index);\n    MU_RUN_TEST(test_bit_lib_is_set);\n    MU_RUN_TEST(test_bit_lib_push);\n    MU_RUN_TEST(test_bit_lib_set_bit);\n    MU_RUN_TEST(test_bit_lib_set_bits);\n    MU_RUN_TEST(test_bit_lib_get_bit);\n    MU_RUN_TEST(test_bit_lib_get_bits);\n    MU_RUN_TEST(test_bit_lib_get_bits_16);\n    MU_RUN_TEST(test_bit_lib_get_bits_32);\n    MU_RUN_TEST(test_bit_lib_get_bits_64);\n    MU_RUN_TEST(test_bit_lib_test_parity_u32);\n    MU_RUN_TEST(test_bit_lib_test_parity);\n    MU_RUN_TEST(test_bit_lib_remove_bit_every_nth);\n    MU_RUN_TEST(test_bit_lib_copy_bits);\n    MU_RUN_TEST(test_bit_lib_reverse_bits);\n    MU_RUN_TEST(test_bit_lib_get_bit_count);\n    MU_RUN_TEST(test_bit_lib_reverse_16_fast);\n    MU_RUN_TEST(test_bit_lib_crc16);\n    MU_RUN_TEST(test_bit_lib_num_to_bytes_be);\n    MU_RUN_TEST(test_bit_lib_num_to_bytes_le);\n    MU_RUN_TEST(test_bit_lib_bytes_to_num_be);\n    MU_RUN_TEST(test_bit_lib_bytes_to_num_le);\n    MU_RUN_TEST(test_bit_lib_bytes_to_num_bcd);\n}\n\nint run_minunit_test_bit_lib(void) {\n    MU_RUN_SUITE(test_bit_lib);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_bit_lib)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/bt/bt_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#include <bt/bt_service/bt_keys_storage.h>\n#include <storage/storage.h>\n#include <toolbox/saved_struct.h>\n\n#define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH(\"unit_tests/bt_test.keys\")\n#define BT_TEST_MIGRATION_FILE_PATH   EXT_PATH(\"unit_tests/bt_migration_test.keys\")\n#define BT_TEST_NVM_RAM_BUFF_SIZE     (507 * 4) // The same as in ble NVM storage\n\n// Identity root key\nstatic const uint8_t gap_legacy_irk[16] =\n    {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};\n// Encryption root key\nstatic const uint8_t gap_legacy_erk[16] =\n    {0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21};\n\n// Test constants for migration (matching bt_keys_storage.c)\n#define BT_KEYS_STORAGE_MAGIC_TEST     (0x18)\n#define BT_KEYS_STORAGE_VERSION_1_TEST (1)\n\ntypedef struct {\n    Storage* storage;\n    BtKeysStorage* bt_keys_storage;\n    uint8_t* nvm_ram_buff_dut;\n    uint8_t* nvm_ram_buff_ref;\n} BtTest;\n\nBtTest* bt_test = NULL;\n\nvoid bt_test_alloc(void) {\n    bt_test = malloc(sizeof(BtTest));\n    bt_test->storage = furi_record_open(RECORD_STORAGE);\n    bt_test->nvm_ram_buff_dut = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);\n    bt_test->nvm_ram_buff_ref = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);\n    bt_test->bt_keys_storage = bt_keys_storage_alloc(BT_TEST_KEY_STORAGE_FILE_PATH);\n    bt_keys_storage_set_ram_params(\n        bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, BT_TEST_NVM_RAM_BUFF_SIZE);\n}\n\nvoid bt_test_free(void) {\n    furi_check(bt_test);\n    free(bt_test->nvm_ram_buff_ref);\n    free(bt_test->nvm_ram_buff_dut);\n    bt_keys_storage_free(bt_test->bt_keys_storage);\n    furi_record_close(RECORD_STORAGE);\n    free(bt_test);\n    bt_test = NULL;\n}\n\nstatic void bt_test_keys_storage_profile(void) {\n    // Emulate nvm change on initial connection\n    const int nvm_change_size_on_connection = 88;\n    for(size_t i = 0; i < nvm_change_size_on_connection; i++) {\n        bt_test->nvm_ram_buff_dut[i] = rand();\n        bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];\n    }\n    // Emulate update storage on initial connect\n    mu_assert(\n        bt_keys_storage_update(\n            bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection),\n        \"Failed to update key storage on initial connect\");\n    memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);\n    mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), \"Failed to load NVM\");\n    mu_assert(\n        memcmp(\n            bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection) ==\n            0,\n        \"Wrong buffer loaded\");\n\n    const int nvm_disconnect_update_offset = 84;\n    const int nvm_disconnect_update_size = 324;\n    const int nvm_total_size = nvm_change_size_on_connection -\n                               (nvm_change_size_on_connection - nvm_disconnect_update_offset) +\n                               nvm_disconnect_update_size;\n    // Emulate update storage on initial disconnect\n    for(size_t i = nvm_disconnect_update_offset;\n        i < nvm_disconnect_update_offset + nvm_disconnect_update_size;\n        i++) {\n        bt_test->nvm_ram_buff_dut[i] = rand();\n        bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];\n    }\n    mu_assert(\n        bt_keys_storage_update(\n            bt_test->bt_keys_storage,\n            &bt_test->nvm_ram_buff_dut[nvm_disconnect_update_offset],\n            nvm_disconnect_update_size),\n        \"Failed to update key storage on initial disconnect\");\n    memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);\n    mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), \"Failed to load NVM\");\n    mu_assert(\n        memcmp(bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_total_size) == 0,\n        \"Wrong buffer loaded\");\n}\n\nstatic void bt_test_keys_remove_test_file(void) {\n    mu_assert(\n        storage_simply_remove(bt_test->storage, BT_TEST_KEY_STORAGE_FILE_PATH),\n        \"Can't remove test file\");\n}\n\n// Helper function to create a version 0 file manually\nstatic bool\n    bt_test_create_v0_file(const char* file_path, const uint8_t* nvm_data, size_t nvm_size) {\n    // Version 0 files use saved_struct format with magic 0x18, version 0, containing only BLE pairing data\n    return saved_struct_save(\n        file_path,\n        nvm_data,\n        nvm_size,\n        BT_KEYS_STORAGE_MAGIC_TEST,\n        0); // Version 0\n}\n\n// Helper function to verify file format version\nstatic bool bt_test_verify_file_version(const char* file_path, uint32_t expected_version) {\n    uint8_t magic, version;\n    size_t size;\n\n    if(!saved_struct_get_metadata(file_path, &magic, &version, &size)) {\n        return false;\n    }\n\n    return (magic == BT_KEYS_STORAGE_MAGIC_TEST && version == expected_version);\n}\n\n// Test migration from version 0 to version 1, including root key preservation\nstatic void bt_test_migration_v0_to_v1(void) {\n    // Create test NVM data\n    const size_t test_nvm_size = 100;\n    uint8_t test_nvm_data[test_nvm_size];\n    for(size_t i = 0; i < test_nvm_size; i++) {\n        test_nvm_data[i] = (uint8_t)(i & 0xFF);\n    }\n\n    // Create a version 0 file\n    mu_assert(\n        bt_test_create_v0_file(BT_TEST_MIGRATION_FILE_PATH, test_nvm_data, test_nvm_size),\n        \"Failed to create version 0 test file\");\n\n    // Create BT keys storage and load the v0 file (should trigger migration)\n    BtKeysStorage* migration_storage = bt_keys_storage_alloc(BT_TEST_MIGRATION_FILE_PATH);\n    uint8_t loaded_buffer[BT_TEST_NVM_RAM_BUFF_SIZE];\n    memset(loaded_buffer, 0, sizeof(loaded_buffer));\n    bt_keys_storage_set_ram_params(migration_storage, loaded_buffer, sizeof(loaded_buffer));\n\n    // Load should succeed and migrate v0 to v1\n    mu_assert(bt_keys_storage_load(migration_storage), \"Failed to load and migrate v0 file\");\n\n    // Verify the file is now version 1\n    mu_assert(\n        bt_test_verify_file_version(BT_TEST_MIGRATION_FILE_PATH, BT_KEYS_STORAGE_VERSION_1_TEST),\n        \"File was not migrated to version 1\");\n\n    // Verify the NVM data was preserved during migration\n    mu_assert(\n        memcmp(test_nvm_data, loaded_buffer, test_nvm_size) == 0,\n        \"NVM data was corrupted during migration\");\n\n    // Verify that legacy root keys are used after migration\n    const GapRootSecurityKeys* migrated_keys = bt_keys_storage_get_root_keys(migration_storage);\n    mu_assert(\n        memcmp(migrated_keys->irk, gap_legacy_irk, sizeof(gap_legacy_irk)) == 0,\n        \"IRK not set to legacy after migration\");\n    mu_assert(\n        memcmp(migrated_keys->erk, gap_legacy_erk, sizeof(gap_legacy_erk)) == 0,\n        \"ERK not set to legacy after migration\");\n\n    bt_keys_storage_free(migration_storage);\n    storage_simply_remove(bt_test->storage, BT_TEST_MIGRATION_FILE_PATH);\n}\n\n// Test that migration preserves existing pairing data and root keys are not changed on reload\nstatic void bt_test_migration_preserves_pairings_and_keys(void) {\n    const size_t pairing_data_size = 200;\n    uint8_t pairing_data[pairing_data_size];\n    for(size_t i = 0; i < pairing_data_size; i++) {\n        pairing_data[i] = (uint8_t)((i * 7 + 42) & 0xFF);\n    }\n    mu_assert(\n        bt_test_create_v0_file(BT_TEST_MIGRATION_FILE_PATH, pairing_data, pairing_data_size),\n        \"Failed to create v0 file with pairing data\");\n\n    GapRootSecurityKeys keys_after_first_load;\n    for(int iteration = 0; iteration < 2; iteration++) {\n        BtKeysStorage* storage = bt_keys_storage_alloc(BT_TEST_MIGRATION_FILE_PATH);\n        uint8_t buffer[BT_TEST_NVM_RAM_BUFF_SIZE];\n        memset(buffer, 0, sizeof(buffer));\n        bt_keys_storage_set_ram_params(storage, buffer, sizeof(buffer));\n        mu_assert(bt_keys_storage_load(storage), \"Failed to load on iteration\");\n        mu_assert(\n            memcmp(pairing_data, buffer, pairing_data_size) == 0,\n            \"Pairing data corrupted on iteration\");\n        const GapRootSecurityKeys* keys = bt_keys_storage_get_root_keys(storage);\n        if(iteration == 0)\n            memcpy(&keys_after_first_load, keys, sizeof(GapRootSecurityKeys));\n        else\n            mu_assert(\n                memcmp(&keys_after_first_load, keys, sizeof(GapRootSecurityKeys)) == 0,\n                \"Root keys changed after reload\");\n        bt_keys_storage_free(storage);\n    }\n    storage_simply_remove(bt_test->storage, BT_TEST_MIGRATION_FILE_PATH);\n}\n\n// Test that delete operation generates new secure keys in v1 and does not match legacy\nstatic void bt_test_delete_generates_new_keys_and_not_legacy(void) {\n    BtKeysStorage* storage = bt_keys_storage_alloc(BT_TEST_MIGRATION_FILE_PATH);\n    uint8_t buffer[BT_TEST_NVM_RAM_BUFF_SIZE];\n    memset(buffer, 0x55, sizeof(buffer));\n    bt_keys_storage_set_ram_params(storage, buffer, sizeof(buffer));\n    mu_assert(bt_keys_storage_update(storage, buffer, 100), \"Failed to create initial v1 file\");\n    const GapRootSecurityKeys* original_keys = bt_keys_storage_get_root_keys(storage);\n    uint8_t original_keys_copy[sizeof(GapRootSecurityKeys)];\n    memcpy(original_keys_copy, original_keys, sizeof(original_keys_copy));\n    bt_keys_storage_delete(storage);\n    const GapRootSecurityKeys* new_keys = bt_keys_storage_get_root_keys(storage);\n    mu_assert(\n        memcmp(original_keys_copy, new_keys, sizeof(original_keys_copy)) != 0,\n        \"Root keys were not regenerated after delete\");\n    mu_assert(\n        memcmp(new_keys->irk, gap_legacy_irk, sizeof(gap_legacy_irk)) != 0,\n        \"IRK after delete should not match legacy\");\n    mu_assert(\n        memcmp(new_keys->erk, gap_legacy_erk, sizeof(gap_legacy_erk)) != 0,\n        \"ERK after delete should not match legacy\");\n    bt_keys_storage_free(storage);\n    storage_simply_remove(bt_test->storage, BT_TEST_MIGRATION_FILE_PATH);\n}\n\nMU_TEST(bt_test_keys_storage_serial_profile) {\n    furi_check(bt_test);\n\n    bt_test_keys_remove_test_file();\n    bt_test_keys_storage_profile();\n    bt_test_keys_remove_test_file();\n}\n\nMU_TEST(bt_test_migration_v0_to_v1_test) {\n    furi_check(bt_test);\n    bt_test_migration_v0_to_v1();\n}\n\nMU_TEST(bt_test_migration_preserves_pairings_and_keys_test) {\n    furi_check(bt_test);\n    bt_test_migration_preserves_pairings_and_keys();\n}\n\nMU_TEST(bt_test_delete_generates_new_keys_and_not_legacy_test) {\n    furi_check(bt_test);\n    bt_test_delete_generates_new_keys_and_not_legacy();\n}\n\nMU_TEST_SUITE(test_bt) {\n    bt_test_alloc();\n\n    MU_RUN_TEST(bt_test_keys_storage_serial_profile);\n    MU_RUN_TEST(bt_test_migration_v0_to_v1_test);\n    MU_RUN_TEST(bt_test_migration_preserves_pairings_and_keys_test);\n    MU_RUN_TEST(bt_test_delete_generates_new_keys_and_not_legacy_test);\n\n    bt_test_free();\n}\n\nint run_minunit_test_bt(void) {\n    MU_RUN_SUITE(test_bt);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_bt)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/common/common.c",
    "content": "#include \"../test.h\"\n#include \"../minunit_vars.h\"\n\n#include <furi.h>\n\nvoid minunit_print_progress(void) {\n    static const char progress[] = {'\\\\', '|', '/', '-'};\n    static uint8_t progress_counter = 0;\n    static uint32_t last_tick = 0;\n    uint32_t current_tick = furi_get_tick();\n    if(current_tick - last_tick > 20) {\n        last_tick = current_tick;\n        printf(\"[%c]\\033[3D\", progress[++progress_counter % COUNT_OF(progress)]);\n        fflush(stdout);\n    }\n}\n\nvoid minunit_print_fail(const char* str) {\n    printf(_FURI_LOG_CLR_E \"%s\\r\\n\" _FURI_LOG_CLR_RESET, str);\n}\n\nvoid minunit_printf_warning(const char* format, ...) {\n    FuriString* str = furi_string_alloc();\n    va_list args;\n    va_start(args, format);\n    furi_string_vprintf(str, format, args);\n    va_end(args);\n    printf(_FURI_LOG_CLR_W \"%s\\r\\n\" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));\n    furi_string_free(str);\n}\n\nint get_minunit_run(void) {\n    return minunit_run;\n}\n\nint get_minunit_assert(void) {\n    return minunit_assert;\n}\n\nint get_minunit_status(void) {\n    return minunit_status;\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/compress/compress_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n\n#include <toolbox/compress.h>\n#include <toolbox/md5_calc.h>\n#include <toolbox/tar/tar_archive.h>\n#include <toolbox/dir_walk.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_random.h>\n\n#include <storage/storage.h>\n\n#include <stdint.h>\n\n#define COMPRESS_UNIT_TESTS_PATH(path) EXT_PATH(\"unit_tests/compress/\" path)\n\nstatic void compress_test_reference_comp_decomp() {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    File* compressed_file = storage_file_alloc(storage);\n    File* decompressed_file = storage_file_alloc(storage);\n\n    mu_assert(\n        storage_file_open(\n            compressed_file,\n            COMPRESS_UNIT_TESTS_PATH(\"compressed.bin\"),\n            FSAM_READ,\n            FSOM_OPEN_EXISTING),\n        \"Failed to open compressed file\");\n    mu_assert(\n        storage_file_open(\n            decompressed_file,\n            COMPRESS_UNIT_TESTS_PATH(\"uncompressed.bin\"),\n            FSAM_READ,\n            FSOM_OPEN_EXISTING),\n        \"Failed to open decompressed file\");\n\n    uint64_t compressed_ref_size = storage_file_size(compressed_file);\n    uint64_t decompressed_ref_size = storage_file_size(decompressed_file);\n\n    mu_assert(compressed_ref_size > 0 && decompressed_ref_size > 0, \"Invalid file sizes\");\n\n    uint8_t* compressed_ref_buff = malloc(compressed_ref_size);\n    uint8_t* decompressed_ref_buff = malloc(decompressed_ref_size);\n\n    mu_assert(\n        storage_file_read(compressed_file, compressed_ref_buff, compressed_ref_size) ==\n            compressed_ref_size,\n        \"Failed to read compressed file\");\n\n    mu_assert(\n        storage_file_read(decompressed_file, decompressed_ref_buff, decompressed_ref_size) ==\n            decompressed_ref_size,\n        \"Failed to read decompressed file\");\n\n    storage_file_free(compressed_file);\n    storage_file_free(decompressed_file);\n    furi_record_close(RECORD_STORAGE);\n\n    uint8_t* temp_buffer = malloc(1024);\n    Compress* comp = compress_alloc(CompressTypeHeatshrink, &compress_config_heatshrink_default);\n\n    size_t encoded_size = 0;\n    mu_assert(\n        compress_encode(\n            comp, decompressed_ref_buff, decompressed_ref_size, temp_buffer, 1024, &encoded_size),\n        \"Compress failed\");\n\n    mu_assert(encoded_size == compressed_ref_size, \"Encoded size is not equal to reference size\");\n\n    mu_assert(\n        memcmp(temp_buffer, compressed_ref_buff, compressed_ref_size) == 0,\n        \"Encoded buffer is not equal to reference\");\n\n    size_t decoded_size = 0;\n    mu_assert(\n        compress_decode(\n            comp, compressed_ref_buff, compressed_ref_size, temp_buffer, 1024, &decoded_size),\n        \"Decompress failed\");\n\n    mu_assert(\n        decoded_size == decompressed_ref_size, \"Decoded size is not equal to reference size\");\n\n    mu_assert(\n        memcmp(temp_buffer, decompressed_ref_buff, decompressed_ref_size) == 0,\n        \"Decoded buffer is not equal to reference\");\n\n    compress_free(comp);\n\n    free(temp_buffer);\n    free(compressed_ref_buff);\n    free(decompressed_ref_buff);\n}\n\nstatic void compress_test_random_comp_decomp() {\n    static const size_t src_buffer_size = 1024;\n    static const size_t encoded_buffer_size = 1024;\n    static const size_t small_buffer_size = src_buffer_size / 32;\n\n    // We only fill half of the buffer with random data, so if anything goes wrong, there's no overflow\n    static const size_t src_data_size = src_buffer_size / 2;\n\n    Compress* comp = compress_alloc(CompressTypeHeatshrink, &compress_config_heatshrink_default);\n    uint8_t* src_buff = malloc(src_buffer_size);\n    uint8_t* encoded_buff = malloc(encoded_buffer_size);\n    uint8_t* decoded_buff = malloc(src_buffer_size);\n    uint8_t* small_buff = malloc(small_buffer_size);\n\n    furi_hal_random_fill_buf(src_buff, src_data_size);\n\n    size_t encoded_size = 0;\n\n    mu_assert(\n        compress_encode(\n            comp, src_buff, src_data_size, encoded_buff, encoded_buffer_size, &encoded_size),\n        \"Compress failed\");\n\n    mu_assert(encoded_size > 0, \"Encoded size is zero\");\n\n    size_t small_enc_dec_size = 0;\n    mu_assert(\n        compress_encode(\n            comp, src_buff, src_data_size, small_buff, small_buffer_size, &small_enc_dec_size) ==\n            false,\n        \"Compress to small buffer failed\");\n\n    size_t decoded_size = 0;\n    mu_assert(\n        compress_decode(\n            comp, encoded_buff, encoded_size, decoded_buff, src_buffer_size, &decoded_size),\n        \"Decompress failed\");\n    mu_assert(decoded_size == src_data_size, \"Decoded size is not equal to source size\");\n\n    mu_assert(\n        memcmp(src_buff, decoded_buff, src_data_size) == 0,\n        \"Decoded buffer is not equal to source\");\n\n    mu_assert(\n        compress_decode(\n            comp, encoded_buff, encoded_size, small_buff, small_buffer_size, &small_enc_dec_size) ==\n            false,\n        \"Decompress to small buffer failed\");\n\n    free(small_buff);\n    free(src_buff);\n    free(encoded_buff);\n    free(decoded_buff);\n    compress_free(comp);\n}\n\nstatic int32_t hs_unpacker_file_read(void* context, uint8_t* buffer, size_t size) {\n    File* file = (File*)context;\n    return storage_file_read(file, buffer, size);\n}\n\nstatic int32_t hs_unpacker_file_write(void* context, uint8_t* buffer, size_t size) {\n    File* file = (File*)context;\n    return storage_file_write(file, buffer, size);\n}\n/*\nSource file was generated with:\n```python3\nimport random, string\nrandom.seed(1337)\nwith open(\"hsstream.out.bin\", \"wb\") as f:\n    for c in random.choices(string.printable, k=1024):\n        for _ in range(random.randint(1, 10)):\n            f.write(c.encode())\n```\n\nIt was compressed with heatshrink using the following command:\n`python3 -m heatshrink2 compress -w 9 -l 4 hsstream.out.bin hsstream.in.bin`\n*/\n\n#define HSSTREAM_IN  COMPRESS_UNIT_TESTS_PATH(\"hsstream.in.bin\")\n#define HSSTREAM_OUT COMPRESS_UNIT_TESTS_PATH(\"hsstream.out.bin\")\n\nstatic void compress_test_heatshrink_stream() {\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* comp_file = storage_file_alloc(api);\n    File* dest_file = storage_file_alloc(api);\n\n    CompressConfigHeatshrink config = {\n        .window_sz2 = 9,\n        .lookahead_sz2 = 4,\n        .input_buffer_sz = 128,\n    };\n    Compress* compress = compress_alloc(CompressTypeHeatshrink, &config);\n\n    do {\n        storage_simply_remove(api, HSSTREAM_OUT);\n\n        mu_assert(\n            storage_file_open(comp_file, HSSTREAM_IN, FSAM_READ, FSOM_OPEN_EXISTING),\n            \"Failed to open compressed file\");\n\n        mu_assert(\n            storage_file_open(dest_file, HSSTREAM_OUT, FSAM_WRITE, FSOM_OPEN_ALWAYS),\n            \"Failed to open decompressed file\");\n\n        mu_assert(\n            compress_decode_streamed(\n                compress, hs_unpacker_file_read, comp_file, hs_unpacker_file_write, dest_file),\n            \"Decompression failed\");\n\n        storage_file_close(dest_file);\n\n        unsigned char md5[16];\n        FS_Error file_error;\n        mu_assert(\n            md5_calc_file(dest_file, HSSTREAM_OUT, md5, &file_error), \"Failed to calculate md5\");\n\n        const unsigned char expected_md5[16] = {\n            0xa3,\n            0x70,\n            0xe8,\n            0x8b,\n            0xa9,\n            0x42,\n            0x74,\n            0xf4,\n            0xaa,\n            0x12,\n            0x8d,\n            0x41,\n            0xd2,\n            0xb6,\n            0x71,\n            0xc9};\n        mu_assert(memcmp(md5, expected_md5, sizeof(md5)) == 0, \"MD5 mismatch after decompression\");\n\n        storage_simply_remove(api, HSSTREAM_OUT);\n    } while(false);\n\n    compress_free(compress);\n    storage_file_free(comp_file);\n    storage_file_free(dest_file);\n    furi_record_close(RECORD_STORAGE);\n}\n\n#define HS_TAR_PATH         COMPRESS_UNIT_TESTS_PATH(\"test.ths\")\n#define HS_TAR_EXTRACT_PATH COMPRESS_UNIT_TESTS_PATH(\"tar_out\")\n\nstatic bool file_counter(const char* name, bool is_dir, void* context) {\n    UNUSED(name);\n    UNUSED(is_dir);\n    int32_t* n_entries = (int32_t*)context;\n    (*n_entries)++;\n    return true;\n}\n\n/*\nHeatshrink tar file contents and MD5 sums:\nfile1.txt:                      64295676ceed5cce2d0dcac402e4bda4\nfile2.txt:                      188f67f297eedd7bf3d6a4d3c2fc31c4\ndir/file3.txt:                  34d98ad8135ffe502dba374690136d16\ndir/big_file.txt:               ee169c1e1791a4d319dbfaefaa850e98\ndir/nested_dir/file4.txt:       e099fcb2aaa0672375eaedc549247ee6\ndir/nested_dir/empty_file.txt:  d41d8cd98f00b204e9800998ecf8427e \n\nXOR of all MD5 sums:            92ed5729786d0e1176d047e35f52d376\n*/\n\nstatic void compress_test_heatshrink_tar() {\n    Storage* api = furi_record_open(RECORD_STORAGE);\n\n    TarArchive* archive = tar_archive_alloc(api);\n    FuriString* path = furi_string_alloc();\n    FileInfo fileinfo;\n    File* file = storage_file_alloc(api);\n\n    do {\n        storage_simply_remove_recursive(api, HS_TAR_EXTRACT_PATH);\n\n        mu_assert(storage_simply_mkdir(api, HS_TAR_EXTRACT_PATH), \"Failed to create extract dir\");\n\n        mu_assert(\n            tar_archive_get_mode_for_path(HS_TAR_PATH) == TarOpenModeReadHeatshrink,\n            \"Invalid mode for heatshrink tar\");\n\n        mu_assert(\n            tar_archive_open(archive, HS_TAR_PATH, TarOpenModeReadHeatshrink),\n            \"Failed to open heatshrink tar\");\n\n        int32_t n_entries = 0;\n        tar_archive_set_file_callback(archive, file_counter, &n_entries);\n\n        mu_assert(\n            tar_archive_unpack_to(archive, HS_TAR_EXTRACT_PATH, NULL),\n            \"Failed to unpack heatshrink tar\");\n\n        mu_assert(n_entries == 9, \"Invalid number of entries in heatshrink tar\");\n\n        uint8_t md5_total[16] = {0}, md5_file[16];\n\n        DirWalk* dir_walk = dir_walk_alloc(api);\n        mu_assert(dir_walk_open(dir_walk, HS_TAR_EXTRACT_PATH), \"Failed to open dirwalk\");\n        while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {\n            if(file_info_is_dir(&fileinfo)) {\n                continue;\n            }\n            mu_assert(\n                md5_calc_file(file, furi_string_get_cstr(path), md5_file, NULL),\n                \"Failed to calc md5\");\n\n            for(size_t i = 0; i < 16; i++) {\n                md5_total[i] ^= md5_file[i];\n            }\n        }\n        dir_walk_free(dir_walk);\n\n        static const unsigned char expected_md5[16] = {\n            0x92,\n            0xed,\n            0x57,\n            0x29,\n            0x78,\n            0x6d,\n            0x0e,\n            0x11,\n            0x76,\n            0xd0,\n            0x47,\n            0xe3,\n            0x5f,\n            0x52,\n            0xd3,\n            0x76};\n        mu_assert(memcmp(md5_total, expected_md5, sizeof(md5_total)) == 0, \"MD5 mismatch\");\n\n        storage_simply_remove_recursive(api, HS_TAR_EXTRACT_PATH);\n    } while(false);\n\n    storage_file_free(file);\n    furi_string_free(path);\n    tar_archive_free(archive);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(test_compress) {\n    MU_RUN_TEST(compress_test_random_comp_decomp);\n    MU_RUN_TEST(compress_test_reference_comp_decomp);\n    MU_RUN_TEST(compress_test_heatshrink_stream);\n    MU_RUN_TEST(compress_test_heatshrink_tar);\n}\n\nint run_minunit_test_compress(void) {\n    MU_RUN_SUITE(test_compress);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_compress)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/datetime/datetimelib_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#include <datetime/datetime.h>\n\nMU_TEST(test_datetime_validate_datetime_correct_min) {\n    DateTime correct_min = {0, 0, 0, 1, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&correct_min);\n\n    mu_assert_int_eq(true, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_correct_max) {\n    DateTime correct_max = {23, 59, 59, 31, 12, 2099, 7};\n    bool result = datetime_validate_datetime(&correct_max);\n\n    mu_assert_int_eq(true, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_second) {\n    DateTime incorrect_sec = {0, 0, 60, 1, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_sec);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_minute) {\n    DateTime incorrect_min = {0, 60, 0, 1, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_min);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_hour) {\n    DateTime incorrect_hour = {24, 0, 0, 1, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_hour);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_day_min) {\n    DateTime incorrect_day_min = {0, 0, 0, 0, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_day_min);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_day_max) {\n    DateTime incorrect_day_max = {0, 0, 0, 32, 1, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_day_max);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_month_min) {\n    DateTime incorrect_month_min = {0, 0, 0, 1, 0, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_month_min);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_month_max) {\n    DateTime incorrect_month_max = {0, 0, 0, 1, 13, 2000, 1};\n    bool result = datetime_validate_datetime(&incorrect_month_max);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_year_min) {\n    DateTime incorrect_year_min = {0, 0, 0, 1, 1, 1999, 1};\n    bool result = datetime_validate_datetime(&incorrect_year_min);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_year_max) {\n    DateTime incorrect_year_max = {0, 0, 0, 1, 1, 2100, 1};\n    bool result = datetime_validate_datetime(&incorrect_year_max);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_weekday_min) {\n    DateTime incorrect_weekday_min = {0, 0, 0, 1, 1, 2000, 0};\n    bool result = datetime_validate_datetime(&incorrect_weekday_min);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST(test_datetime_validate_datetime_incorrect_weekday_max) {\n    DateTime incorrect_weekday_max = {0, 0, 0, 1, 1, 2000, 8};\n    bool result = datetime_validate_datetime(&incorrect_weekday_max);\n\n    mu_assert_int_eq(false, result);\n}\n\nMU_TEST_SUITE(test_datetime_validate_datetime) {\n    MU_RUN_TEST(test_datetime_validate_datetime_correct_min);\n    MU_RUN_TEST(test_datetime_validate_datetime_correct_max);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_second);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_minute);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_hour);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_day_min);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_day_max);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_month_min);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_month_max);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_year_min);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_year_max);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_weekday_min);\n    MU_RUN_TEST(test_datetime_validate_datetime_incorrect_weekday_max);\n}\n\nMU_TEST(test_datetime_timestamp_to_datetime_min) {\n    uint32_t test_value = 0;\n    DateTime min_datetime_expected = {0, 0, 0, 1, 1, 1970, 4};\n\n    DateTime result = {0};\n    datetime_timestamp_to_datetime(test_value, &result);\n\n    mu_assert_mem_eq(&min_datetime_expected, &result, sizeof(result));\n}\n\nMU_TEST(test_datetime_timestamp_to_datetime_max) {\n    uint32_t test_value = UINT32_MAX;\n    DateTime max_datetime_expected = {6, 28, 15, 7, 2, 2106, 7};\n\n    DateTime result = {0};\n    datetime_timestamp_to_datetime(test_value, &result);\n\n    mu_assert_mem_eq(&max_datetime_expected, &result, sizeof(result));\n}\n\nMU_TEST(test_datetime_timestamp_to_datetime_to_timestamp) {\n    uint32_t test_value = random();\n\n    DateTime datetime = {0};\n    datetime_timestamp_to_datetime(test_value, &datetime);\n\n    uint32_t result = datetime_datetime_to_timestamp(&datetime);\n\n    mu_assert_int_eq(test_value, result);\n}\n\nMU_TEST(test_datetime_timestamp_to_datetime_weekday) {\n    uint32_t test_value = 1709748421; // Wed Mar 06 18:07:01 2024 UTC\n\n    DateTime datetime = {0};\n    datetime_timestamp_to_datetime(test_value, &datetime);\n\n    mu_assert_int_eq(datetime.hour, 18);\n    mu_assert_int_eq(datetime.minute, 7);\n    mu_assert_int_eq(datetime.second, 1);\n    mu_assert_int_eq(datetime.day, 6);\n    mu_assert_int_eq(datetime.month, 3);\n    mu_assert_int_eq(datetime.weekday, 3);\n    mu_assert_int_eq(datetime.year, 2024);\n}\n\nMU_TEST_SUITE(test_datetime_timestamp_to_datetime_suite) {\n    MU_RUN_TEST(test_datetime_timestamp_to_datetime_min);\n    MU_RUN_TEST(test_datetime_timestamp_to_datetime_max);\n    MU_RUN_TEST(test_datetime_timestamp_to_datetime_to_timestamp);\n    MU_RUN_TEST(test_datetime_timestamp_to_datetime_weekday);\n}\n\nMU_TEST(test_datetime_datetime_to_timestamp_min) {\n    DateTime min_datetime = {0, 0, 0, 1, 1, 1970, 0};\n\n    uint32_t result = datetime_datetime_to_timestamp(&min_datetime);\n    mu_assert_int_eq(0, result);\n}\n\nMU_TEST(test_datetime_datetime_to_timestamp_max) {\n    DateTime max_datetime = {6, 28, 15, 7, 2, 2106, 0};\n\n    uint32_t result = datetime_datetime_to_timestamp(&max_datetime);\n    mu_assert_int_eq(UINT32_MAX, result);\n}\n\nMU_TEST_SUITE(test_datetime_datetime_to_timestamp_suite) {\n    MU_RUN_TEST(test_datetime_datetime_to_timestamp_min);\n    MU_RUN_TEST(test_datetime_datetime_to_timestamp_max);\n}\n\nint run_minunit_test_datetime(void) {\n    MU_RUN_SUITE(test_datetime_timestamp_to_datetime_suite);\n    MU_RUN_SUITE(test_datetime_datetime_to_timestamp_suite);\n    MU_RUN_SUITE(test_datetime_validate_datetime);\n\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_datetime)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c",
    "content": "#include <dialogs/dialogs.h>\n\n#include \"../test.h\" // IWYU pragma: keep\n\nMU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) {\n    mu_assert(\n        sizeof(DialogsFileBrowserOptions) == 28,\n        \"Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`\");\n\n    DialogsFileBrowserOptions options;\n    dialog_file_browser_set_basic_options(&options, \".fap\", NULL);\n    // note: this assertions can safely be changed, their primary purpose is to remind the maintainer\n    // to update `dialog_file_browser_set_basic_options` by including all structure fields in it\n    mu_assert_string_eq(\".fap\", options.extension);\n    mu_assert_null(options.base_path);\n    mu_assert(options.skip_assets, \"`skip_assets` should default to `true\");\n    mu_assert(options.hide_dot_files, \"`hide_dot_files` should default to `true\");\n    mu_assert_null(options.icon);\n    mu_assert(options.hide_ext, \"`hide_ext` should default to `true\");\n    mu_assert_null(options.item_loader_callback);\n    mu_assert_null(options.item_loader_context);\n}\n\nMU_TEST_SUITE(dialogs_file_browser_options) {\n    MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields);\n}\n\nint run_minunit_test_dialogs_file_browser_options(void) {\n    MU_RUN_SUITE(dialogs_file_browser_options);\n\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_dialogs_file_browser_options)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n#include <furi.h>\n#include <m-dict.h>\n#include <toolbox/dir_walk.h>\n\nstatic const char* const storage_test_dirwalk_paths[] = {\n    \"1\",\n    \"11\",\n    \"111\",\n    \"1/2\",\n    \"1/22\",\n    \"1/222\",\n    \"11/2\",\n    \"111/2\",\n    \"111/22\",\n    \"111/22/33\",\n};\n\nstatic const char* const storage_test_dirwalk_files[] = {\n    \"file1.test\",\n    \"file2.test\",\n    \"file3.ext_test\",\n    \"1/file1.test\",\n    \"111/22/33/file1.test\",\n    \"111/22/33/file2.test\",\n    \"111/22/33/file3.ext_test\",\n    \"111/22/33/file4.ext_test\",\n};\n\ntypedef struct {\n    const char* const path;\n    bool is_dir;\n} StorageTestPathDesc;\n\nconst StorageTestPathDesc storage_test_dirwalk_full[] = {\n    {.path = \"1\", .is_dir = true},\n    {.path = \"11\", .is_dir = true},\n    {.path = \"111\", .is_dir = true},\n    {.path = \"1/2\", .is_dir = true},\n    {.path = \"1/22\", .is_dir = true},\n    {.path = \"1/222\", .is_dir = true},\n    {.path = \"11/2\", .is_dir = true},\n    {.path = \"111/2\", .is_dir = true},\n    {.path = \"111/22\", .is_dir = true},\n    {.path = \"111/22/33\", .is_dir = true},\n    {.path = \"file1.test\", .is_dir = false},\n    {.path = \"file2.test\", .is_dir = false},\n    {.path = \"file3.ext_test\", .is_dir = false},\n    {.path = \"1/file1.test\", .is_dir = false},\n    {.path = \"111/22/33/file1.test\", .is_dir = false},\n    {.path = \"111/22/33/file2.test\", .is_dir = false},\n    {.path = \"111/22/33/file3.ext_test\", .is_dir = false},\n    {.path = \"111/22/33/file4.ext_test\", .is_dir = false},\n};\n\nconst StorageTestPathDesc storage_test_dirwalk_no_recursive[] = {\n    {.path = \"1\", .is_dir = true},\n    {.path = \"11\", .is_dir = true},\n    {.path = \"111\", .is_dir = true},\n    {.path = \"file1.test\", .is_dir = false},\n    {.path = \"file2.test\", .is_dir = false},\n    {.path = \"file3.ext_test\", .is_dir = false},\n};\n\nconst StorageTestPathDesc storage_test_dirwalk_filter[] = {\n    {.path = \"file1.test\", .is_dir = false},\n    {.path = \"file2.test\", .is_dir = false},\n    {.path = \"1/file1.test\", .is_dir = false},\n    {.path = \"111/22/33/file1.test\", .is_dir = false},\n    {.path = \"111/22/33/file2.test\", .is_dir = false},\n};\n\ntypedef struct {\n    bool is_dir;\n    bool visited;\n} StorageTestPath;\n\nDICT_DEF2(StorageTestPathDict, FuriString*, FURI_STRING_OPLIST, StorageTestPath, M_POD_OPLIST)\n\nstatic StorageTestPathDict_t*\n    storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) {\n    StorageTestPathDict_t* data = malloc(sizeof(StorageTestPathDict_t));\n    StorageTestPathDict_init(*data);\n\n    for(size_t i = 0; i < paths_count; i++) {\n        FuriString* key;\n        key = furi_string_alloc_set(paths[i].path);\n        StorageTestPath value = {\n            .is_dir = paths[i].is_dir,\n            .visited = false,\n        };\n\n        StorageTestPathDict_set_at(*data, key, value);\n        furi_string_free(key);\n    }\n\n    return data;\n}\n\nstatic void storage_test_paths_free(StorageTestPathDict_t* data) {\n    StorageTestPathDict_clear(*data);\n    free(data);\n}\n\nstatic bool storage_test_paths_mark(StorageTestPathDict_t* data, FuriString* path, bool is_dir) {\n    bool found = false;\n\n    StorageTestPath* record = StorageTestPathDict_get(*data, path);\n    if(record) {\n        if(is_dir == record->is_dir) {\n            if(record->visited == false) {\n                record->visited = true;\n                found = true;\n            }\n        }\n    }\n\n    return found;\n}\n\nstatic bool storage_test_paths_check(StorageTestPathDict_t* data) {\n    bool error = false;\n\n    StorageTestPathDict_it_t it;\n    for(StorageTestPathDict_it(it, *data); !StorageTestPathDict_end_p(it);\n        StorageTestPathDict_next(it)) {\n        const StorageTestPathDict_itref_t* itref = StorageTestPathDict_cref(it);\n\n        if(itref->value.visited == false) {\n            error = true;\n            break;\n        }\n    }\n\n    return error;\n}\n\nstatic bool write_file_13DA(Storage* storage, const char* path) {\n    File* file = storage_file_alloc(storage);\n    bool result = false;\n    if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n        result = (storage_file_write(file, \"13DA\", 4) == 4);\n    }\n    storage_file_close(file);\n    storage_file_free(file);\n\n    return result;\n}\n\nstatic void storage_dirs_create(Storage* storage, const char* base) {\n    FuriString* path;\n    path = furi_string_alloc();\n\n    storage_common_mkdir(storage, base);\n\n    for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) {\n        furi_string_printf(path, \"%s/%s\", base, storage_test_dirwalk_paths[i]);\n        storage_common_mkdir(storage, furi_string_get_cstr(path));\n    }\n\n    for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) {\n        furi_string_printf(path, \"%s/%s\", base, storage_test_dirwalk_files[i]);\n        write_file_13DA(storage, furi_string_get_cstr(path));\n    }\n\n    furi_string_free(path);\n}\n\nMU_TEST_1(test_dirwalk_full, Storage* storage) {\n    FuriString* path;\n    path = furi_string_alloc();\n    FileInfo fileinfo;\n\n    StorageTestPathDict_t* paths =\n        storage_test_paths_alloc(storage_test_dirwalk_full, COUNT_OF(storage_test_dirwalk_full));\n\n    DirWalk* dir_walk = dir_walk_alloc(storage);\n    mu_check(dir_walk_open(dir_walk, EXT_PATH(\"dirwalk\")));\n\n    while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {\n        furi_string_right(path, strlen(EXT_PATH(\"dirwalk/\")));\n        mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));\n    }\n\n    dir_walk_free(dir_walk);\n    furi_string_free(path);\n\n    mu_check(storage_test_paths_check(paths) == false);\n\n    storage_test_paths_free(paths);\n}\n\nMU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {\n    FuriString* path;\n    path = furi_string_alloc();\n    FileInfo fileinfo;\n\n    StorageTestPathDict_t* paths = storage_test_paths_alloc(\n        storage_test_dirwalk_no_recursive, COUNT_OF(storage_test_dirwalk_no_recursive));\n\n    DirWalk* dir_walk = dir_walk_alloc(storage);\n    dir_walk_set_recursive(dir_walk, false);\n    mu_check(dir_walk_open(dir_walk, EXT_PATH(\"dirwalk\")));\n\n    while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {\n        furi_string_right(path, strlen(EXT_PATH(\"dirwalk/\")));\n        mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));\n    }\n\n    dir_walk_free(dir_walk);\n    furi_string_free(path);\n\n    mu_check(storage_test_paths_check(paths) == false);\n\n    storage_test_paths_free(paths);\n}\n\nstatic bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* fileinfo, void* ctx) {\n    UNUSED(ctx);\n\n    // only files\n    if(!file_info_is_dir(fileinfo)) {\n        // with \".test\" in name\n        if(strstr(name, \".test\") != NULL) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nMU_TEST_1(test_dirwalk_filter, Storage* storage) {\n    FuriString* path;\n    path = furi_string_alloc();\n    FileInfo fileinfo;\n\n    StorageTestPathDict_t* paths = storage_test_paths_alloc(\n        storage_test_dirwalk_filter, COUNT_OF(storage_test_dirwalk_filter));\n\n    DirWalk* dir_walk = dir_walk_alloc(storage);\n    dir_walk_set_filter_cb(dir_walk, test_dirwalk_filter_no_folder_ext, NULL);\n    mu_check(dir_walk_open(dir_walk, EXT_PATH(\"dirwalk\")));\n\n    while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {\n        furi_string_right(path, strlen(EXT_PATH(\"dirwalk/\")));\n        mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));\n    }\n\n    dir_walk_free(dir_walk);\n    furi_string_free(path);\n\n    mu_check(storage_test_paths_check(paths) == false);\n\n    storage_test_paths_free(paths);\n}\n\nMU_TEST_SUITE(test_dirwalk_suite) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    storage_dirs_create(storage, EXT_PATH(\"dirwalk\"));\n\n    MU_RUN_TEST_1(test_dirwalk_full, storage);\n    MU_RUN_TEST_1(test_dirwalk_no_recursive, storage);\n    MU_RUN_TEST_1(test_dirwalk_filter, storage);\n\n    storage_simply_remove_recursive(storage, EXT_PATH(\"dirwalk\"));\n    furi_record_close(RECORD_STORAGE);\n}\n\nint run_minunit_test_dirwalk(void) {\n    MU_RUN_SUITE(test_dirwalk_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_dirwalk)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/expansion/expansion_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n\n#include <furi.h>\n#include <furi_hal_random.h>\n\n#include <expansion/expansion_protocol.h>\n\n#define EXPANSION_TEST_GARBAGE_MAGIC      (0xB19AF)\n#define EXPANSION_TEST_GARBAGE_BUF_SIZE   (0x100U)\n#define EXPANSION_TEST_GARBAGE_ITERATIONS (100U)\n\nMU_TEST(test_expansion_encoded_size) {\n    ExpansionFrame frame = {};\n\n    frame.header.type = ExpansionFrameTypeHeartbeat;\n    mu_assert_int_eq(1, expansion_frame_get_encoded_size(&frame));\n\n    frame.header.type = ExpansionFrameTypeStatus;\n    mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame));\n\n    frame.header.type = ExpansionFrameTypeBaudRate;\n    mu_assert_int_eq(5, expansion_frame_get_encoded_size(&frame));\n\n    frame.header.type = ExpansionFrameTypeControl;\n    mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame));\n\n    frame.header.type = ExpansionFrameTypeData;\n    for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) {\n        frame.content.data.size = i;\n        mu_assert_int_eq(i + 2, expansion_frame_get_encoded_size(&frame));\n    }\n}\n\nMU_TEST(test_expansion_remaining_size) {\n    ExpansionFrame frame = {};\n\n    size_t remaining_size;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n\n    frame.header.type = ExpansionFrameTypeHeartbeat;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n\n    frame.header.type = ExpansionFrameTypeStatus;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n\n    frame.header.type = ExpansionFrameTypeBaudRate;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size));\n    mu_assert_int_eq(4, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 5, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n\n    frame.header.type = ExpansionFrameTypeControl;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n\n    frame.header.type = ExpansionFrameTypeData;\n    frame.content.data.size = EXPANSION_PROTOCOL_MAX_DATA_SIZE;\n    mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size));\n    mu_assert_int_eq(1, remaining_size);\n    mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size));\n    mu_assert_int_eq(EXPANSION_PROTOCOL_MAX_DATA_SIZE, remaining_size);\n    for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) {\n        mu_check(expansion_frame_get_remaining_size(&frame, i + 2, &remaining_size));\n        mu_assert_int_eq(EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, remaining_size);\n    }\n    mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size));\n    mu_assert_int_eq(0, remaining_size);\n}\n\ntypedef struct {\n    void* data_out;\n    size_t size_available;\n    size_t size_sent;\n} TestExpansionSendStream;\n\nstatic size_t test_expansion_send_callback(const uint8_t* data, size_t data_size, void* context) {\n    TestExpansionSendStream* stream = context;\n    const size_t size_sent = MIN(data_size, stream->size_available);\n\n    memcpy(stream->data_out + stream->size_sent, data, size_sent);\n\n    stream->size_available -= size_sent;\n    stream->size_sent += size_sent;\n\n    return size_sent;\n}\n\ntypedef struct {\n    const void* data_in;\n    size_t size_available;\n    size_t size_received;\n} TestExpansionReceiveStream;\n\nstatic size_t test_expansion_receive_callback(uint8_t* data, size_t data_size, void* context) {\n    TestExpansionReceiveStream* stream = context;\n    const size_t size_received = MIN(data_size, stream->size_available);\n\n    memcpy(data, stream->data_in + stream->size_received, size_received);\n\n    stream->size_available -= size_received;\n    stream->size_received += size_received;\n\n    return size_received;\n}\n\nMU_TEST(test_expansion_encode_decode_frame) {\n    const ExpansionFrame frame_in = {\n        .header.type = ExpansionFrameTypeData,\n        .content.data.size = 8,\n        .content.data.bytes = {0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xca, 0xfe},\n    };\n\n    uint8_t encoded_data[sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)];\n    memset(encoded_data, 0, sizeof(encoded_data));\n\n    TestExpansionSendStream send_stream = {\n        .data_out = &encoded_data,\n        .size_available = sizeof(encoded_data),\n        .size_sent = 0,\n    };\n\n    const size_t encoded_size = expansion_frame_get_encoded_size(&frame_in);\n\n    mu_assert_int_eq(\n        expansion_protocol_encode(&frame_in, test_expansion_send_callback, &send_stream),\n        ExpansionProtocolStatusOk);\n    mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), send_stream.size_sent);\n    mu_assert_int_eq(\n        expansion_protocol_get_checksum((const uint8_t*)&frame_in, encoded_size),\n        encoded_data[encoded_size]);\n    mu_assert_mem_eq(&frame_in, &encoded_data, encoded_size);\n\n    TestExpansionReceiveStream stream = {\n        .data_in = encoded_data,\n        .size_available = send_stream.size_sent,\n        .size_received = 0,\n    };\n\n    ExpansionFrame frame_out;\n\n    mu_assert_int_eq(\n        expansion_protocol_decode(&frame_out, test_expansion_receive_callback, &stream),\n        ExpansionProtocolStatusOk);\n    mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), stream.size_received);\n    mu_assert_mem_eq(&frame_in, &frame_out, encoded_size);\n}\n\nMU_TEST(test_expansion_garbage_input) {\n    uint8_t garbage_data[EXPANSION_TEST_GARBAGE_BUF_SIZE];\n    for(uint32_t i = 0; i < EXPANSION_TEST_GARBAGE_ITERATIONS; ++i) {\n        furi_hal_random_fill_buf(garbage_data, sizeof(garbage_data));\n        size_t remaining_size = EXPANSION_TEST_GARBAGE_MAGIC;\n        if(expansion_frame_get_remaining_size(\n               (ExpansionFrame*)garbage_data, sizeof(garbage_data), &remaining_size)) {\n            // If by chance the garbage data is a valid frame, then the result\n            // must be 0 because the amount of data provided is more than enough\n            mu_assert_int_eq(0, remaining_size);\n        } else {\n            // If the frame is invalid, the remaining_size parameter should be untouched\n            mu_assert_int_eq(EXPANSION_TEST_GARBAGE_MAGIC, remaining_size);\n        }\n    }\n}\n\nMU_TEST_SUITE(test_expansion_suite) {\n    MU_RUN_TEST(test_expansion_encoded_size);\n    MU_RUN_TEST(test_expansion_remaining_size);\n    MU_RUN_TEST(test_expansion_encode_decode_frame);\n    MU_RUN_TEST(test_expansion_garbage_input);\n}\n\nint run_minunit_test_expansion(void) {\n    MU_RUN_SUITE(test_expansion_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_expansion)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c",
    "content": "#include <furi.h>\n#include <flipper_format/flipper_format.h>\n#include <flipper_format/flipper_format_i.h>\n#include <toolbox/stream/stream.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define TEST_DIR_NAME EXT_PATH(\".tmp/unit_tests/ff\")\n#define TEST_DIR      TEST_DIR_NAME \"/\"\n\nstatic const char* test_filetype = \"Flipper File test\";\nstatic const uint32_t test_version = 666;\n\nstatic const char* test_string_key = \"String data\";\nstatic const char* test_string_data = \"String\";\nstatic const char* test_string_updated_data = \"New string\";\n\nstatic const char* test_int_key = \"Int32 data\";\nstatic const int32_t test_int_data[] = {1234, -6345, 7813, 0};\nstatic const int32_t test_int_updated_data[] = {-1337, 69};\n\nstatic const char* test_uint_key = \"Uint32 data\";\nstatic const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};\nstatic const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};\n\nstatic const char* test_float_key = \"Float data\";\nstatic const float test_float_data[] = {1.5f, 1000.0f};\nstatic const float test_float_updated_data[] = {1.2f};\n\nstatic const char* test_bool_key = \"Bool data\";\nstatic const bool test_bool_data[] = {true, false};\nstatic const bool test_bool_updated_data[] = {false, true, true};\n\nstatic const char* test_hex_key = \"Hex data\";\nstatic const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};\nstatic const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};\n\n#define READ_TEST_NIX \"ff_nix.test\"\nstatic const char* test_data_nix = \"Filetype: Flipper File test\\n\"\n                                   \"Version: 666\\n\"\n                                   \"# This is comment\\n\"\n                                   \"String data: String\\n\"\n                                   \"Int32 data: 1234 -6345 7813 0\\n\"\n                                   \"Uint32 data: 1234 0 5678 9098 7654321\\n\"\n                                   \"Float data: 1.5 1000.0\\n\"\n                                   \"Bool data: true false\\n\"\n                                   \"Hex data: DE AD BE\";\n\n#define READ_TEST_WIN \"ff_win.test\"\nstatic const char* test_data_win = \"Filetype: Flipper File test\\r\\n\"\n                                   \"Version: 666\\r\\n\"\n                                   \"# This is comment\\r\\n\"\n                                   \"String data: String\\r\\n\"\n                                   \"Int32 data: 1234 -6345 7813 0\\r\\n\"\n                                   \"Uint32 data: 1234 0 5678 9098 7654321\\r\\n\"\n                                   \"Float data: 1.5 1000.0\\r\\n\"\n                                   \"Bool data: true false\\r\\n\"\n                                   \"Hex data: DE AD BE\";\n\n#define READ_TEST_FLP \"ff_flp.test\"\n#define READ_TEST_ODD \"ff_oddities.test\"\nstatic const char* test_data_odd = \"Filetype: Flipper File test\\n\"\n                                   // Tabs before newline\n                                   \"Version: 666\\t\\t\\n\"\n                                   \"# This is comment\\n\"\n                                   // Windows newline in a UNIX file\n                                   \"String data: String\\r\\n\"\n                                   // Trailing whitespace\n                                   \"Int32 data: 1234 -6345 7813 0 \\n\"\n                                   // Extra whitespace\n                                   \"Uint32 data:   1234  0   5678   9098  7654321  \\n\"\n                                   // Mixed whitespace\n                                   \"Float data: 1.5\\t \\t1000.0\\n\"\n                                   // Leading tabs after key\n                                   \"Bool data:\\t\\ttrue   false\\n\"\n                                   // Mixed trailing whitespace\n                                   \"Hex data: DE AD BE\\t    \";\n\n// data created by user on linux machine\nstatic const char* test_file_linux = TEST_DIR READ_TEST_NIX;\n// data created by user on windows machine\nstatic const char* test_file_windows = TEST_DIR READ_TEST_WIN;\n// data created by flipper itself\nstatic const char* test_file_flipper = TEST_DIR READ_TEST_FLP;\n// data containing odd user input\nstatic const char* test_file_oddities = TEST_DIR READ_TEST_ODD;\n\nstatic bool storage_write_string(const char* path, const char* data) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n    bool result = false;\n\n    do {\n        if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;\n        if(storage_file_write(file, data, strlen(data)) != strlen(data)) break;\n\n        result = true;\n    } while(false);\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic void tests_setup(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), \"Cannot clean data\");\n    mu_assert(storage_simply_mkdir(storage, TEST_DIR_NAME), \"Cannot create dir\");\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void tests_teardown(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), \"Cannot clean data\");\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic bool test_read(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    FuriString* string_value;\n    string_value = furi_string_alloc();\n    uint32_t uint32_value;\n    void* scratchpad = malloc(512);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n\n        if(!flipper_format_read_header(file, string_value, &uint32_value)) break;\n        if(furi_string_cmp_str(string_value, test_filetype) != 0) break;\n        if(uint32_value != test_version) break;\n\n        if(!flipper_format_read_string(file, test_string_key, string_value)) break;\n        if(furi_string_cmp_str(string_value, test_string_data) != 0) break;\n\n        if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_int_data)) break;\n        if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;\n        if(memcmp(scratchpad, test_int_data, sizeof(int32_t) * COUNT_OF(test_int_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_uint_data)) break;\n        if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;\n        if(memcmp(scratchpad, test_uint_data, sizeof(uint32_t) * COUNT_OF(test_uint_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_float_data)) break;\n        if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;\n        if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_bool_data)) break;\n        if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;\n        if(memcmp(scratchpad, test_bool_data, sizeof(bool) * COUNT_OF(test_bool_data)) != 0) break;\n\n        if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_hex_data)) break;\n        if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;\n        if(memcmp(scratchpad, test_hex_data, sizeof(uint8_t) * COUNT_OF(test_hex_data)) != 0)\n            break;\n\n        result = true;\n    } while(false);\n\n    free(scratchpad);\n    furi_string_free(string_value);\n\n    flipper_format_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_read_updated(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    FuriString* string_value;\n    string_value = furi_string_alloc();\n    uint32_t uint32_value;\n    void* scratchpad = malloc(512);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n\n        if(!flipper_format_read_header(file, string_value, &uint32_value)) break;\n        if(furi_string_cmp_str(string_value, test_filetype) != 0) break;\n        if(uint32_value != test_version) break;\n\n        if(!flipper_format_read_string(file, test_string_key, string_value)) break;\n        if(furi_string_cmp_str(string_value, test_string_updated_data) != 0) break;\n\n        if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_int_updated_data)) break;\n        if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;\n        if(memcmp(\n               scratchpad,\n               test_int_updated_data,\n               sizeof(int32_t) * COUNT_OF(test_int_updated_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_uint_updated_data)) break;\n        if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;\n        if(memcmp(\n               scratchpad,\n               test_uint_updated_data,\n               sizeof(uint32_t) * COUNT_OF(test_uint_updated_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_float_updated_data)) break;\n        if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;\n        if(memcmp(\n               scratchpad,\n               test_float_updated_data,\n               sizeof(float) * COUNT_OF(test_float_updated_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_bool_updated_data)) break;\n        if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;\n        if(memcmp(\n               scratchpad,\n               test_bool_updated_data,\n               sizeof(bool) * COUNT_OF(test_bool_updated_data)) != 0)\n            break;\n\n        if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;\n        if(uint32_value != COUNT_OF(test_hex_updated_data)) break;\n        if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;\n        if(memcmp(\n               scratchpad,\n               test_hex_updated_data,\n               sizeof(uint8_t) * COUNT_OF(test_hex_updated_data)) != 0)\n            break;\n\n        result = true;\n    } while(false);\n\n    free(scratchpad);\n    furi_string_free(string_value);\n\n    flipper_format_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_write(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_always(file, file_name)) break;\n        if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;\n        if(!flipper_format_write_comment_cstr(file, \"This is comment\")) break;\n        if(!flipper_format_write_empty_line(file)) break;\n        if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break;\n        if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))\n            break;\n        if(!flipper_format_write_uint32(\n               file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))\n            break;\n        if(!flipper_format_write_float(\n               file, test_float_key, test_float_data, COUNT_OF(test_float_data)))\n            break;\n        if(!flipper_format_write_bool(\n               file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))\n            break;\n        if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))\n            break;\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_delete_last_key(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        if(!flipper_format_delete_key(file, test_hex_key)) break;\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_append_key(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_append(file, file_name)) break;\n        if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))\n            break;\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_update(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        if(!flipper_format_update_string_cstr(file, test_string_key, test_string_updated_data))\n            break;\n        if(!flipper_format_update_int32(\n               file, test_int_key, test_int_updated_data, COUNT_OF(test_int_updated_data)))\n            break;\n        if(!flipper_format_update_uint32(\n               file, test_uint_key, test_uint_updated_data, COUNT_OF(test_uint_updated_data)))\n            break;\n        if(!flipper_format_update_float(\n               file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data)))\n            break;\n        if(!flipper_format_update_bool(\n               file, test_bool_key, test_bool_updated_data, COUNT_OF(test_bool_updated_data)))\n            break;\n        if(!flipper_format_update_hex(\n               file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data)))\n            break;\n\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_update_backward(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        if(!flipper_format_update_string_cstr(file, test_string_key, test_string_data)) break;\n        if(!flipper_format_update_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))\n            break;\n        if(!flipper_format_update_uint32(\n               file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))\n            break;\n        if(!flipper_format_update_float(\n               file, test_float_key, test_float_data, COUNT_OF(test_float_data)))\n            break;\n        if(!flipper_format_update_bool(\n               file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))\n            break;\n        if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))\n            break;\n\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_write_multikey(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_always(file, file_name)) break;\n        if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;\n\n        bool error = false;\n        for(uint8_t index = 0; index < 100; index++) {\n            if(!flipper_format_write_hex(file, test_hex_key, &index, 1)) {\n                error = true;\n                break;\n            }\n        }\n        if(error) break;\n\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nstatic bool test_read_multikey(const char* file_name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    FuriString* string_value;\n    string_value = furi_string_alloc();\n    uint32_t uint32_value;\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        if(!flipper_format_read_header(file, string_value, &uint32_value)) break;\n        if(furi_string_cmp_str(string_value, test_filetype) != 0) break;\n        if(uint32_value != test_version) break;\n\n        bool error = false;\n        uint8_t uint8_value;\n        for(uint8_t index = 0; index < 100; index++) {\n            if(!flipper_format_read_hex(file, test_hex_key, &uint8_value, 1)) {\n                error = true;\n                break;\n            }\n\n            if(uint8_value != index) {\n                error = true;\n                break;\n            }\n        }\n        if(error) break;\n\n        result = true;\n    } while(false);\n\n    furi_string_free(string_value);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nMU_TEST(flipper_format_write_test) {\n    mu_assert(storage_write_string(test_file_linux, test_data_nix), \"Write test error [Linux]\");\n    mu_assert(\n        storage_write_string(test_file_windows, test_data_win), \"Write test error [Windows]\");\n    mu_assert(test_write(test_file_flipper), \"Write test error [Flipper]\");\n}\n\nMU_TEST(flipper_format_read_test) {\n    mu_assert(test_read(test_file_linux), \"Read test error [Linux]\");\n    mu_assert(test_read(test_file_windows), \"Read test error [Windows]\");\n    mu_assert(test_read(test_file_flipper), \"Read test error [Flipper]\");\n}\n\nMU_TEST(flipper_format_delete_test) {\n    mu_assert(test_delete_last_key(test_file_linux), \"Cannot delete key [Linux]\");\n    mu_assert(test_delete_last_key(test_file_windows), \"Cannot delete key [Windows]\");\n    mu_assert(test_delete_last_key(test_file_flipper), \"Cannot delete key [Flipper]\");\n}\n\nMU_TEST(flipper_format_delete_result_test) {\n    mu_assert(!test_read(test_file_linux), \"Key deleted incorrectly [Linux]\");\n    mu_assert(!test_read(test_file_windows), \"Key deleted incorrectly [Windows]\");\n    mu_assert(!test_read(test_file_flipper), \"Key deleted incorrectly [Flipper]\");\n}\n\nMU_TEST(flipper_format_append_test) {\n    mu_assert(test_append_key(test_file_linux), \"Cannot append data [Linux]\");\n    mu_assert(test_append_key(test_file_windows), \"Cannot append data [Windows]\");\n    mu_assert(test_append_key(test_file_flipper), \"Cannot append data [Flipper]\");\n}\n\nMU_TEST(flipper_format_append_result_test) {\n    mu_assert(test_read(test_file_linux), \"Data appended incorrectly [Linux]\");\n    mu_assert(test_read(test_file_windows), \"Data appended incorrectly [Windows]\");\n    mu_assert(test_read(test_file_flipper), \"Data appended incorrectly [Flipper]\");\n}\n\nMU_TEST(flipper_format_update_1_test) {\n    mu_assert(test_update(test_file_linux), \"Cannot update data #1 [Linux]\");\n    mu_assert(test_update(test_file_windows), \"Cannot update data #1 [Windows]\");\n    mu_assert(test_update(test_file_flipper), \"Cannot update data #1 [Flipper]\");\n}\n\nMU_TEST(flipper_format_update_1_result_test) {\n    mu_assert(test_read_updated(test_file_linux), \"Data #1 updated incorrectly [Linux]\");\n    mu_assert(test_read_updated(test_file_windows), \"Data #1 updated incorrectly [Windows]\");\n    mu_assert(test_read_updated(test_file_flipper), \"Data #1 updated incorrectly [Flipper]\");\n}\n\nMU_TEST(flipper_format_update_2_test) {\n    mu_assert(test_update_backward(test_file_linux), \"Cannot update data #2 [Linux]\");\n    mu_assert(test_update_backward(test_file_windows), \"Cannot update data #2 [Windows]\");\n    mu_assert(test_update_backward(test_file_flipper), \"Cannot update data #2 [Flipper]\");\n}\n\nMU_TEST(flipper_format_update_2_result_test) {\n    mu_assert(test_read(test_file_linux), \"Data #2 updated incorrectly [Linux]\");\n    mu_assert(test_read(test_file_windows), \"Data #2 updated incorrectly [Windows]\");\n    mu_assert(test_read(test_file_flipper), \"Data #2 updated incorrectly [Flipper]\");\n}\n\nMU_TEST(flipper_format_multikey_test) {\n    mu_assert(test_write_multikey(TEST_DIR \"ff_multiline.test\"), \"Multikey write test error\");\n    mu_assert(test_read_multikey(TEST_DIR \"ff_multiline.test\"), \"Multikey read test error\");\n}\n\nMU_TEST(flipper_format_oddities_test) {\n    mu_assert(\n        storage_write_string(test_file_oddities, test_data_odd), \"Write test error [Oddities]\");\n    mu_assert(test_read(test_file_linux), \"Read test error [Oddities]\");\n}\n\nMU_TEST_SUITE(flipper_format) {\n    tests_setup();\n    MU_RUN_TEST(flipper_format_write_test);\n    MU_RUN_TEST(flipper_format_read_test);\n    MU_RUN_TEST(flipper_format_delete_test);\n    MU_RUN_TEST(flipper_format_delete_result_test);\n    MU_RUN_TEST(flipper_format_append_test);\n    MU_RUN_TEST(flipper_format_append_result_test);\n    MU_RUN_TEST(flipper_format_update_1_test);\n    MU_RUN_TEST(flipper_format_update_1_result_test);\n    MU_RUN_TEST(flipper_format_update_2_test);\n    MU_RUN_TEST(flipper_format_update_2_result_test);\n    MU_RUN_TEST(flipper_format_multikey_test);\n    MU_RUN_TEST(flipper_format_oddities_test);\n    tests_teardown();\n}\n\nint run_minunit_test_flipper_format(void) {\n    MU_RUN_SUITE(flipper_format);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_flipper_format)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c",
    "content": "#include <furi.h>\n#include <flipper_format/flipper_format.h>\n#include <flipper_format/flipper_format_i.h>\n#include <toolbox/stream/stream.h>\n#include <storage/storage.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nstatic const char* test_filetype = \"Flipper Format test\";\nstatic const uint32_t test_version = 666;\n\nstatic const char* test_string_key = \"String data\";\nstatic const char* test_string_data = \"String\";\nstatic const char* test_string_updated_data = \"New string\";\nstatic const char* test_string_updated_2_data = \"And some more\";\n\nstatic const char* test_int_key = \"Int32 data\";\nstatic const int32_t test_int_data[] = {1234, -6345, 7813, 0};\nstatic const int32_t test_int_updated_data[] = {-1337, 69};\nstatic const int32_t test_int_updated_2_data[] = {-3, -2, -1, 0, 1, 2, 3};\n\nstatic const char* test_uint_key = \"Uint32 data\";\nstatic const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};\nstatic const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};\nstatic const uint32_t test_uint_updated_2_data[] = {20, 21};\n\nstatic const char* test_float_key = \"Float data\";\nstatic const float test_float_data[] = {1.5f, 1000.0f};\nstatic const float test_float_updated_data[] = {1.2f};\nstatic const float test_float_updated_2_data[] = {0.01f, 0.0f, -51.6f};\n\nstatic const char* test_hex_key = \"Hex data\";\nstatic const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};\nstatic const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};\nstatic const uint8_t test_hex_updated_2_data[] = {0xCA, 0xCA, 0x05};\n\nstatic const char* test_hex_new_key = \"New Hex data\";\nstatic const uint8_t test_hex_new_data[] = {0xFF, 0x6A, 0x91};\n\nstatic const char* test_data_nix = \"Filetype: Flipper Format test\\n\"\n                                   \"Version: 666\\n\"\n                                   \"# This is comment\\n\"\n                                   \"String data: String\\n\"\n                                   \"Int32 data: 1234 -6345 7813 0\\n\"\n                                   \"Uint32 data: 1234 0 5678 9098 7654321\\n\"\n                                   \"Float data: 1.5 1000.0\\n\"\n                                   \"Hex data: DE AD BE\";\n\nstatic const char* test_data_win = \"Filetype: Flipper Format test\\r\\n\"\n                                   \"Version: 666\\r\\n\"\n                                   \"# This is comment\\r\\n\"\n                                   \"String data: String\\r\\n\"\n                                   \"Int32 data: 1234 -6345 7813 0\\r\\n\"\n                                   \"Uint32 data: 1234 0 5678 9098 7654321\\r\\n\"\n                                   \"Float data: 1.5 1000.0\\r\\n\"\n                                   \"Hex data: DE AD BE\";\n\n#define ARRAY_W_COUNT(x) (x), (COUNT_OF(x))\n#define ARRAY_W_BSIZE(x) (x), (sizeof(x))\n\nMU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {\n    FuriString* tmpstr;\n    uint32_t version;\n    uint32_t uint32_data[COUNT_OF(test_uint_data)];\n    int32_t int32_data[COUNT_OF(test_int_data)];\n    float float_data[COUNT_OF(test_float_data)];\n    uint8_t hex_data[COUNT_OF(test_hex_data)];\n\n    uint32_t count;\n\n    // key exist test\n    size_t position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));\n    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    mu_check(!flipper_format_key_exist(flipper_format, \"invalid key\"));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    // stream seek to end test\n    mu_check(flipper_format_seek_to_end(flipper_format));\n    mu_assert_int_eq(\n        stream_size(flipper_format_get_raw_stream(flipper_format)),\n        stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    // key exist test\n    position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));\n    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    mu_check(!flipper_format_key_exist(flipper_format, \"invalid key\"));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    // rewind\n    mu_check(flipper_format_rewind(flipper_format));\n\n    // key exist test\n    position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));\n    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    mu_check(!flipper_format_key_exist(flipper_format, \"invalid key\"));\n    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));\n\n    // read test\n    tmpstr = furi_string_alloc();\n\n    mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));\n    mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));\n    mu_assert_int_eq(test_version, version);\n\n    mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));\n    mu_assert_string_eq(test_string_data, furi_string_get_cstr(tmpstr));\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_int_data), count);\n    mu_check(flipper_format_read_int32(flipper_format, test_int_key, ARRAY_W_COUNT(int32_data)));\n    mu_check(memcmp(test_int_data, ARRAY_W_BSIZE(int32_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_uint_data), count);\n    mu_check(\n        flipper_format_read_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_data)));\n    mu_check(memcmp(test_uint_data, ARRAY_W_BSIZE(uint32_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_float_data), count);\n    mu_check(flipper_format_read_float(flipper_format, test_float_key, ARRAY_W_COUNT(float_data)));\n    mu_check(memcmp(test_float_data, ARRAY_W_BSIZE(float_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_hex_data), count);\n    mu_check(flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_data)));\n    mu_check(memcmp(test_hex_data, ARRAY_W_BSIZE(hex_data)) == 0);\n\n    mu_check(!flipper_format_read_string(flipper_format, \"Key that doesn't exist\", tmpstr));\n\n    furi_string_free(tmpstr);\n\n    // update data\n    mu_check(flipper_format_rewind(flipper_format));\n    mu_check(flipper_format_update_string_cstr(\n        flipper_format, test_string_key, test_string_updated_data));\n    mu_check(flipper_format_update_int32(\n        flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_data)));\n    mu_check(flipper_format_update_uint32(\n        flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_data)));\n    mu_check(flipper_format_update_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_data)));\n    mu_check(flipper_format_update_hex(\n        flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_data)));\n\n    // read updated data test\n    uint32_t uint32_updated_data[COUNT_OF(test_uint_updated_data)];\n    int32_t int32_updated_data[COUNT_OF(test_int_updated_data)];\n    float float_updated_data[COUNT_OF(test_float_updated_data)];\n    uint8_t hex_updated_data[COUNT_OF(test_hex_updated_data)];\n\n    mu_check(flipper_format_rewind(flipper_format));\n    tmpstr = furi_string_alloc();\n\n    mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));\n    mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));\n    mu_assert_int_eq(test_version, version);\n\n    mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));\n    mu_assert_string_eq(test_string_updated_data, furi_string_get_cstr(tmpstr));\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_int_updated_data), count);\n    mu_check(flipper_format_read_int32(\n        flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_data)));\n    mu_check(memcmp(test_int_updated_data, ARRAY_W_BSIZE(int32_updated_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_uint_updated_data), count);\n    mu_check(flipper_format_read_uint32(\n        flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));\n    mu_check(memcmp(test_uint_updated_data, ARRAY_W_BSIZE(uint32_updated_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_float_updated_data), count);\n    mu_check(flipper_format_read_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_data)));\n    mu_check(memcmp(test_float_updated_data, ARRAY_W_BSIZE(float_updated_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_hex_updated_data), count);\n    mu_check(\n        flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_data)));\n    mu_check(memcmp(test_hex_updated_data, ARRAY_W_BSIZE(hex_updated_data)) == 0);\n\n    mu_check(!flipper_format_read_string(flipper_format, \"Key that doesn't exist\", tmpstr));\n\n    furi_string_free(tmpstr);\n\n    // update data\n    mu_check(flipper_format_rewind(flipper_format));\n    mu_check(flipper_format_insert_or_update_string_cstr(\n        flipper_format, test_string_key, test_string_updated_2_data));\n    mu_check(flipper_format_insert_or_update_int32(\n        flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_2_data)));\n    mu_check(flipper_format_insert_or_update_uint32(\n        flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_2_data)));\n    mu_check(flipper_format_insert_or_update_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_2_data)));\n    mu_check(flipper_format_insert_or_update_hex(\n        flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_2_data)));\n    mu_check(flipper_format_insert_or_update_hex(\n        flipper_format, test_hex_new_key, ARRAY_W_COUNT(test_hex_new_data)));\n\n    uint32_t uint32_updated_2_data[COUNT_OF(test_uint_updated_2_data)];\n    int32_t int32_updated_2_data[COUNT_OF(test_int_updated_2_data)];\n    float float_updated_2_data[COUNT_OF(test_float_updated_2_data)];\n    uint8_t hex_updated_2_data[COUNT_OF(test_hex_updated_2_data)];\n    uint8_t hex_new_data[COUNT_OF(test_hex_new_data)];\n\n    mu_check(flipper_format_rewind(flipper_format));\n    tmpstr = furi_string_alloc();\n\n    mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));\n    mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));\n    mu_assert_int_eq(test_version, version);\n\n    mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));\n    mu_assert_string_eq(test_string_updated_2_data, furi_string_get_cstr(tmpstr));\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count);\n    mu_check(flipper_format_read_int32(\n        flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_2_data)));\n    mu_check(memcmp(test_int_updated_2_data, ARRAY_W_BSIZE(int32_updated_2_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_uint_updated_2_data), count);\n    mu_check(flipper_format_read_uint32(\n        flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_2_data)));\n    mu_check(memcmp(test_uint_updated_2_data, ARRAY_W_BSIZE(uint32_updated_2_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_float_updated_2_data), count);\n    mu_check(flipper_format_read_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_2_data)));\n    mu_check(memcmp(test_float_updated_2_data, ARRAY_W_BSIZE(float_updated_2_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_hex_updated_2_data), count);\n    mu_check(\n        flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_2_data)));\n    mu_check(memcmp(test_hex_updated_2_data, ARRAY_W_BSIZE(hex_updated_2_data)) == 0);\n\n    mu_check(flipper_format_get_value_count(flipper_format, test_hex_new_key, &count));\n    mu_assert_int_eq(COUNT_OF(test_hex_new_data), count);\n    mu_check(\n        flipper_format_read_hex(flipper_format, test_hex_new_key, ARRAY_W_COUNT(hex_new_data)));\n    mu_check(memcmp(test_hex_new_data, ARRAY_W_BSIZE(hex_new_data)) == 0);\n\n    mu_check(!flipper_format_read_string(flipper_format, \"Key that doesn't exist\", tmpstr));\n\n    furi_string_free(tmpstr);\n\n    // delete key test\n    mu_check(flipper_format_rewind(flipper_format));\n    mu_check(flipper_format_delete_key(flipper_format, test_uint_key));\n\n    // deleted key read test\n    mu_check(flipper_format_rewind(flipper_format));\n    mu_check(!flipper_format_read_uint32(\n        flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));\n}\n\nMU_TEST(flipper_format_string_test) {\n    FlipperFormat* flipper_format = flipper_format_string_alloc();\n    Stream* stream = flipper_format_get_raw_stream(flipper_format);\n\n    mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));\n    mu_check(flipper_format_write_comment_cstr(flipper_format, \"This is comment\"));\n    mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));\n    mu_check(\n        flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));\n    mu_check(\n        flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));\n    mu_check(flipper_format_write_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));\n    mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));\n\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    stream_clean(stream);\n    stream_write_cstring(stream, test_data_nix);\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    stream_clean(stream);\n    stream_write_cstring(stream, test_data_win);\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    flipper_format_free(flipper_format);\n}\n\nMU_TEST(flipper_format_file_test) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n    mu_check(\n        flipper_format_file_open_always(flipper_format, EXT_PATH(\".tmp/unit_tests/flipper.fff\")));\n    Stream* stream = flipper_format_get_raw_stream(flipper_format);\n\n    mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));\n    mu_check(flipper_format_write_comment_cstr(flipper_format, \"This is comment\"));\n    mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));\n    mu_check(\n        flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));\n    mu_check(\n        flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));\n    mu_check(flipper_format_write_float(\n        flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));\n    mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));\n\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    stream_clean(stream);\n    stream_write_cstring(stream, test_data_nix);\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    stream_clean(stream);\n    stream_write_cstring(stream, test_data_win);\n    MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);\n\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(flipper_format_string_suite) {\n    MU_RUN_TEST(flipper_format_string_test);\n    MU_RUN_TEST(flipper_format_file_test);\n}\n\nint run_minunit_test_flipper_format_string(void) {\n    MU_RUN_SUITE(flipper_format_string_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_flipper_format_string)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/float_tools/float_tools_test.c",
    "content": "#include <float.h>\n#include <float_tools.h>\n\n#include \"../test.h\" // IWYU pragma: keep\n\nMU_TEST(float_tools_equal_test) {\n    mu_check(float_is_equal(FLT_MAX, FLT_MAX));\n    mu_check(float_is_equal(FLT_MIN, FLT_MIN));\n    mu_check(float_is_equal(-FLT_MAX, -FLT_MAX));\n    mu_check(float_is_equal(-FLT_MIN, -FLT_MIN));\n\n    mu_check(!float_is_equal(FLT_MIN, FLT_MAX));\n    mu_check(!float_is_equal(-FLT_MIN, FLT_MAX));\n    mu_check(!float_is_equal(FLT_MIN, -FLT_MAX));\n    mu_check(!float_is_equal(-FLT_MIN, -FLT_MAX));\n\n    const float pi = 3.14159f;\n    mu_check(float_is_equal(pi, pi));\n    mu_check(float_is_equal(-pi, -pi));\n    mu_check(!float_is_equal(pi, -pi));\n    mu_check(!float_is_equal(-pi, pi));\n\n    const float one_third = 1.f / 3.f;\n    const float one_third_dec = 0.3333333f;\n    mu_check(one_third != one_third_dec);\n    mu_check(float_is_equal(one_third, one_third_dec));\n\n    const float big_num = 1.e12f;\n    const float med_num = 95.389f;\n    const float smol_num = 1.e-12f;\n    mu_check(float_is_equal(big_num, big_num));\n    mu_check(float_is_equal(med_num, med_num));\n    mu_check(float_is_equal(smol_num, smol_num));\n    mu_check(!float_is_equal(smol_num, big_num));\n    mu_check(!float_is_equal(med_num, smol_num));\n    mu_check(!float_is_equal(big_num, med_num));\n\n    const float more_than_one = 1.f + FLT_EPSILON;\n    const float less_than_one = 1.f - FLT_EPSILON;\n    mu_check(!float_is_equal(more_than_one, less_than_one));\n    mu_check(!float_is_equal(more_than_one, -less_than_one));\n    mu_check(!float_is_equal(-more_than_one, less_than_one));\n    mu_check(!float_is_equal(-more_than_one, -less_than_one));\n\n    const float slightly_more_than_one = 1.f + FLT_EPSILON / 2.f;\n    const float slightly_less_than_one = 1.f - FLT_EPSILON / 2.f;\n    mu_check(float_is_equal(slightly_more_than_one, slightly_less_than_one));\n    mu_check(float_is_equal(-slightly_more_than_one, -slightly_less_than_one));\n    mu_check(!float_is_equal(slightly_more_than_one, -slightly_less_than_one));\n    mu_check(!float_is_equal(-slightly_more_than_one, slightly_less_than_one));\n}\n\nMU_TEST_SUITE(float_tools_suite) {\n    MU_RUN_TEST(float_tools_equal_test);\n}\n\nint run_minunit_test_float_tools(void) {\n    MU_RUN_SUITE(float_tools_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_float_tools)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_errno_test.c",
    "content": "#include <furi.h>\n#include <errno.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define TAG        \"ErrnoTest\"\n#define THREAD_CNT 16\n#define ITER_CNT   1000\n\nstatic int32_t errno_fuzzer(void* context) {\n    int start_value = (int)context;\n    int32_t fails = 0;\n\n    for(int i = start_value; i < start_value + ITER_CNT; i++) {\n        errno = i;\n        furi_thread_yield();\n        if(errno != i) fails++;\n    }\n\n    for(int i = 0; i < ITER_CNT; i++) {\n        errno = 0;\n        furi_thread_yield();\n        UNUSED(strtol(\"123456\", NULL, 10)); // -V530\n        furi_thread_yield();\n        if(errno != 0) fails++;\n\n        errno = 0;\n        furi_thread_yield();\n        UNUSED(strtol(\"123456123456123456123456123456123456123456123456\", NULL, 10)); // -V530\n        furi_thread_yield();\n        if(errno != ERANGE) fails++;\n    }\n\n    return fails;\n}\n\nvoid test_errno_saving(void) {\n    FuriThread* threads[THREAD_CNT];\n\n    for(int i = 0; i < THREAD_CNT; i++) {\n        int start_value = i * ITER_CNT;\n        threads[i] = furi_thread_alloc_ex(\"ErrnoFuzzer\", 1024, errno_fuzzer, (void*)start_value);\n        furi_thread_set_priority(threads[i], FuriThreadPriorityNormal);\n        furi_thread_start(threads[i]);\n    }\n\n    for(int i = 0; i < THREAD_CNT; i++) {\n        furi_thread_join(threads[i]);\n        mu_assert_int_eq(0, furi_thread_get_return_code(threads[i]));\n        furi_thread_free(threads[i]);\n    }\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_event_loop_test.c",
    "content": "#include \"../test.h\"\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n#define TAG \"TestFuriEventLoop\"\n\n#define MESSAGE_COUNT    (256UL)\n#define EVENT_FLAG_COUNT (23UL)\n#define PRIMITIVE_COUNT  (4UL)\n#define RUN_COUNT        (2UL)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    uint32_t message_queue_count;\n    uint32_t stream_buffer_count;\n    uint32_t event_flag_count;\n    uint32_t semaphore_count;\n    uint32_t primitives_tested;\n} TestFuriEventLoopThread;\n\ntypedef struct {\n    FuriMessageQueue* message_queue;\n    FuriStreamBuffer* stream_buffer;\n    FuriEventFlag* event_flag;\n    FuriSemaphore* semaphore;\n\n    TestFuriEventLoopThread producer;\n    TestFuriEventLoopThread consumer;\n} TestFuriEventLoopData;\n\nstatic void test_furi_event_loop_pending_callback(void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopThread* test_thread = context;\n    furi_check(test_thread->primitives_tested < PRIMITIVE_COUNT);\n\n    test_thread->primitives_tested++;\n    FURI_LOG_I(TAG, \"primitives tested: %lu\", test_thread->primitives_tested);\n\n    if(test_thread->primitives_tested == PRIMITIVE_COUNT) {\n        furi_event_loop_stop(test_thread->event_loop);\n    }\n}\n\nstatic void test_furi_event_loop_thread_init(TestFuriEventLoopThread* test_thread) {\n    memset(test_thread, 0, sizeof(TestFuriEventLoopThread));\n    test_thread->event_loop = furi_event_loop_alloc();\n}\n\nstatic void test_furi_event_loop_thread_run_and_cleanup(TestFuriEventLoopThread* test_thread) {\n    furi_event_loop_run(test_thread->event_loop);\n    // 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags\n    xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);\n    furi_event_loop_free(test_thread->event_loop);\n}\n\nstatic void test_furi_event_loop_producer_message_queue_callback(\n    FuriEventLoopObject* object,\n    void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->message_queue == object);\n\n    FURI_LOG_I(\n        TAG,\n        \"producer MessageQueue: %lu %lu\",\n        data->producer.message_queue_count,\n        data->consumer.message_queue_count);\n\n    if(data->producer.message_queue_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);\n        furi_event_loop_subscribe_message_queue(\n            data->producer.event_loop,\n            data->message_queue,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_message_queue_callback,\n            data);\n\n    } else if(data->producer.message_queue_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);\n        furi_event_loop_pend_callback(\n            data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);\n        return;\n    }\n\n    data->producer.message_queue_count++;\n\n    furi_check(\n        furi_message_queue_put(data->message_queue, &data->producer.message_queue_count, 0) ==\n        FuriStatusOk);\n\n    furi_delay_us(furi_hal_random_get() % 100);\n}\n\nstatic void test_furi_event_loop_producer_stream_buffer_callback(\n    FuriEventLoopObject* object,\n    void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->stream_buffer == object);\n\n    TestFuriEventLoopThread* producer = &data->producer;\n    TestFuriEventLoopThread* consumer = &data->consumer;\n\n    FURI_LOG_I(\n        TAG,\n        \"producer StreamBuffer: %lu %lu\",\n        producer->stream_buffer_count,\n        consumer->stream_buffer_count);\n\n    if(producer->stream_buffer_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);\n        furi_event_loop_subscribe_stream_buffer(\n            producer->event_loop,\n            data->stream_buffer,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_stream_buffer_callback,\n            data);\n\n    } else if(producer->stream_buffer_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);\n        furi_event_loop_pend_callback(\n            producer->event_loop, test_furi_event_loop_pending_callback, producer);\n        return;\n    }\n\n    producer->stream_buffer_count++;\n\n    furi_check(\n        furi_stream_buffer_send(\n            data->stream_buffer, &producer->stream_buffer_count, sizeof(uint32_t), 0) ==\n        sizeof(uint32_t));\n\n    furi_delay_us(furi_hal_random_get() % 100);\n}\n\nstatic void\n    test_furi_event_loop_producer_event_flag_callback(FuriEventLoopObject* object, void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->event_flag == object);\n\n    const uint32_t producer_flags = (1UL << data->producer.event_flag_count);\n    const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);\n\n    FURI_LOG_I(TAG, \"producer EventFlag: 0x%06lX 0x%06lX\", producer_flags, consumer_flags);\n\n    furi_check(furi_event_flag_set(data->event_flag, producer_flags) & producer_flags);\n\n    if(data->producer.event_flag_count == EVENT_FLAG_COUNT / 2) {\n        furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);\n        furi_event_loop_subscribe_event_flag(\n            data->producer.event_loop,\n            data->event_flag,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_event_flag_callback,\n            data);\n\n    } else if(data->producer.event_flag_count == EVENT_FLAG_COUNT) {\n        furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);\n        furi_event_loop_pend_callback(\n            data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);\n        return;\n    }\n\n    data->producer.event_flag_count++;\n\n    furi_delay_us(furi_hal_random_get() % 100);\n}\n\nstatic void\n    test_furi_event_loop_producer_semaphore_callback(FuriEventLoopObject* object, void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->semaphore == object);\n\n    TestFuriEventLoopThread* producer = &data->producer;\n    TestFuriEventLoopThread* consumer = &data->consumer;\n\n    FURI_LOG_I(\n        TAG, \"producer Semaphore: %lu %lu\", producer->semaphore_count, consumer->semaphore_count);\n    furi_check(furi_semaphore_release(data->semaphore) == FuriStatusOk);\n\n    if(producer->semaphore_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);\n        furi_event_loop_subscribe_semaphore(\n            producer->event_loop,\n            data->semaphore,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_semaphore_callback,\n            data);\n\n    } else if(producer->semaphore_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);\n        furi_event_loop_pend_callback(\n            producer->event_loop, test_furi_event_loop_pending_callback, producer);\n        return;\n    }\n\n    data->producer.semaphore_count++;\n\n    furi_delay_us(furi_hal_random_get() % 100);\n}\n\nstatic int32_t test_furi_event_loop_producer(void* p) {\n    furi_check(p);\n\n    TestFuriEventLoopData* data = p;\n    TestFuriEventLoopThread* producer = &data->producer;\n\n    for(uint32_t i = 0; i < RUN_COUNT; ++i) {\n        FURI_LOG_I(TAG, \"producer start run %lu\", i);\n\n        test_furi_event_loop_thread_init(producer);\n\n        furi_event_loop_subscribe_message_queue(\n            producer->event_loop,\n            data->message_queue,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_message_queue_callback,\n            data);\n        furi_event_loop_subscribe_stream_buffer(\n            producer->event_loop,\n            data->stream_buffer,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_stream_buffer_callback,\n            data);\n        furi_event_loop_subscribe_event_flag(\n            producer->event_loop,\n            data->event_flag,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_event_flag_callback,\n            data);\n        furi_event_loop_subscribe_semaphore(\n            producer->event_loop,\n            data->semaphore,\n            FuriEventLoopEventOut,\n            test_furi_event_loop_producer_semaphore_callback,\n            data);\n\n        test_furi_event_loop_thread_run_and_cleanup(producer);\n    }\n\n    FURI_LOG_I(TAG, \"producer end\");\n\n    return 0;\n}\n\nstatic void test_furi_event_loop_consumer_message_queue_callback(\n    FuriEventLoopObject* object,\n    void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->message_queue == object);\n\n    furi_delay_us(furi_hal_random_get() % 100);\n\n    furi_check(\n        furi_message_queue_get(data->message_queue, &data->consumer.message_queue_count, 0) ==\n        FuriStatusOk);\n\n    FURI_LOG_I(\n        TAG,\n        \"consumer MessageQueue: %lu %lu\",\n        data->producer.message_queue_count,\n        data->consumer.message_queue_count);\n\n    if(data->consumer.message_queue_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);\n        furi_event_loop_subscribe_message_queue(\n            data->consumer.event_loop,\n            data->message_queue,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_message_queue_callback,\n            data);\n\n    } else if(data->consumer.message_queue_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);\n        furi_event_loop_pend_callback(\n            data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);\n    }\n}\n\nstatic void test_furi_event_loop_consumer_stream_buffer_callback(\n    FuriEventLoopObject* object,\n    void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->stream_buffer == object);\n\n    TestFuriEventLoopThread* producer = &data->producer;\n    TestFuriEventLoopThread* consumer = &data->consumer;\n\n    furi_delay_us(furi_hal_random_get() % 100);\n\n    furi_check(\n        furi_stream_buffer_receive(\n            data->stream_buffer, &consumer->stream_buffer_count, sizeof(uint32_t), 0) ==\n        sizeof(uint32_t));\n\n    FURI_LOG_I(\n        TAG,\n        \"consumer StreamBuffer: %lu %lu\",\n        producer->stream_buffer_count,\n        consumer->stream_buffer_count);\n\n    if(consumer->stream_buffer_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(consumer->event_loop, data->stream_buffer);\n        furi_event_loop_subscribe_stream_buffer(\n            consumer->event_loop,\n            data->stream_buffer,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_stream_buffer_callback,\n            data);\n\n    } else if(consumer->stream_buffer_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(data->consumer.event_loop, data->stream_buffer);\n        furi_event_loop_pend_callback(\n            consumer->event_loop, test_furi_event_loop_pending_callback, consumer);\n    }\n}\n\nstatic void\n    test_furi_event_loop_consumer_event_flag_callback(FuriEventLoopObject* object, void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->event_flag == object);\n\n    furi_delay_us(furi_hal_random_get() % 100);\n\n    const uint32_t producer_flags = (1UL << data->producer.event_flag_count);\n    const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);\n\n    furi_check(\n        furi_event_flag_wait(data->event_flag, consumer_flags, FuriFlagWaitAny, 0) &\n        consumer_flags);\n\n    FURI_LOG_I(TAG, \"consumer EventFlag: 0x%06lX 0x%06lX\", producer_flags, consumer_flags);\n\n    if(data->consumer.event_flag_count == EVENT_FLAG_COUNT / 2) {\n        furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);\n        furi_event_loop_subscribe_event_flag(\n            data->consumer.event_loop,\n            data->event_flag,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_event_flag_callback,\n            data);\n\n    } else if(data->consumer.event_flag_count == EVENT_FLAG_COUNT) {\n        furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);\n        furi_event_loop_pend_callback(\n            data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);\n        return;\n    }\n\n    data->consumer.event_flag_count++;\n}\n\nstatic void\n    test_furi_event_loop_consumer_semaphore_callback(FuriEventLoopObject* object, void* context) {\n    furi_check(context);\n\n    TestFuriEventLoopData* data = context;\n    furi_check(data->semaphore == object);\n\n    furi_delay_us(furi_hal_random_get() % 100);\n\n    TestFuriEventLoopThread* producer = &data->producer;\n    TestFuriEventLoopThread* consumer = &data->consumer;\n\n    furi_check(furi_semaphore_acquire(data->semaphore, 0) == FuriStatusOk);\n\n    FURI_LOG_I(\n        TAG, \"consumer Semaphore: %lu %lu\", producer->semaphore_count, consumer->semaphore_count);\n\n    if(consumer->semaphore_count == MESSAGE_COUNT / 2) {\n        furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);\n        furi_event_loop_subscribe_semaphore(\n            consumer->event_loop,\n            data->semaphore,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_semaphore_callback,\n            data);\n\n    } else if(consumer->semaphore_count == MESSAGE_COUNT) {\n        furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);\n        furi_event_loop_pend_callback(\n            consumer->event_loop, test_furi_event_loop_pending_callback, consumer);\n        return;\n    }\n\n    data->consumer.semaphore_count++;\n}\n\nstatic int32_t test_furi_event_loop_consumer(void* p) {\n    furi_check(p);\n\n    TestFuriEventLoopData* data = p;\n    TestFuriEventLoopThread* consumer = &data->consumer;\n\n    for(uint32_t i = 0; i < RUN_COUNT; ++i) {\n        FURI_LOG_I(TAG, \"consumer start run %lu\", i);\n\n        test_furi_event_loop_thread_init(consumer);\n\n        furi_event_loop_subscribe_message_queue(\n            consumer->event_loop,\n            data->message_queue,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_message_queue_callback,\n            data);\n        furi_event_loop_subscribe_stream_buffer(\n            consumer->event_loop,\n            data->stream_buffer,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_stream_buffer_callback,\n            data);\n        furi_event_loop_subscribe_event_flag(\n            consumer->event_loop,\n            data->event_flag,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_event_flag_callback,\n            data);\n        furi_event_loop_subscribe_semaphore(\n            consumer->event_loop,\n            data->semaphore,\n            FuriEventLoopEventIn,\n            test_furi_event_loop_consumer_semaphore_callback,\n            data);\n\n        test_furi_event_loop_thread_run_and_cleanup(consumer);\n    }\n\n    FURI_LOG_I(TAG, \"consumer end\");\n\n    return 0;\n}\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriSemaphore* semaphore;\n    size_t counter;\n} SelfUnsubTestTimerContext;\n\nstatic void test_self_unsub_semaphore_callback(FuriEventLoopObject* object, void* context) {\n    furi_event_loop_unsubscribe(context, object); // shouldn't crash here\n}\n\nstatic void test_self_unsub_timer_callback(void* arg) {\n    SelfUnsubTestTimerContext* context = arg;\n\n    if(context->counter == 0) {\n        furi_semaphore_release(context->semaphore);\n    } else if(context->counter == 1) {\n        furi_event_loop_stop(context->event_loop);\n    }\n\n    context->counter++;\n}\n\nvoid test_furi_event_loop_self_unsubscribe(void) {\n    FuriEventLoop* event_loop = furi_event_loop_alloc();\n\n    FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);\n    furi_event_loop_subscribe_semaphore(\n        event_loop,\n        semaphore,\n        FuriEventLoopEventIn,\n        test_self_unsub_semaphore_callback,\n        event_loop);\n\n    SelfUnsubTestTimerContext timer_context = {\n        .event_loop = event_loop,\n        .semaphore = semaphore,\n        .counter = 0,\n    };\n    FuriEventLoopTimer* timer = furi_event_loop_timer_alloc(\n        event_loop, test_self_unsub_timer_callback, FuriEventLoopTimerTypePeriodic, &timer_context);\n    furi_event_loop_timer_start(timer, furi_ms_to_ticks(20));\n\n    furi_event_loop_run(event_loop);\n\n    furi_event_loop_timer_free(timer);\n    furi_semaphore_free(semaphore);\n    furi_event_loop_free(event_loop);\n}\n\nvoid test_furi_event_loop(void) {\n    TestFuriEventLoopData data = {};\n\n    data.message_queue = furi_message_queue_alloc(16, sizeof(uint32_t));\n    data.stream_buffer = furi_stream_buffer_alloc(16, sizeof(uint32_t));\n    data.event_flag = furi_event_flag_alloc();\n    data.semaphore = furi_semaphore_alloc(8, 0);\n\n    FuriThread* producer_thread =\n        furi_thread_alloc_ex(\"producer_thread\", 1 * 1024, test_furi_event_loop_producer, &data);\n    furi_thread_start(producer_thread);\n\n    FuriThread* consumer_thread =\n        furi_thread_alloc_ex(\"consumer_thread\", 1 * 1024, test_furi_event_loop_consumer, &data);\n    furi_thread_start(consumer_thread);\n\n    // Wait for thread to complete their tasks\n    furi_thread_join(producer_thread);\n    furi_thread_join(consumer_thread);\n\n    TestFuriEventLoopThread* producer = &data.producer;\n    TestFuriEventLoopThread* consumer = &data.consumer;\n\n    // The test itself\n    mu_assert_int_eq(producer->message_queue_count, consumer->message_queue_count);\n    mu_assert_int_eq(producer->message_queue_count, MESSAGE_COUNT);\n    mu_assert_int_eq(producer->stream_buffer_count, consumer->stream_buffer_count);\n    mu_assert_int_eq(producer->stream_buffer_count, MESSAGE_COUNT);\n    mu_assert_int_eq(producer->event_flag_count, consumer->event_flag_count);\n    mu_assert_int_eq(producer->event_flag_count, EVENT_FLAG_COUNT);\n    mu_assert_int_eq(producer->semaphore_count, consumer->semaphore_count);\n    mu_assert_int_eq(producer->semaphore_count, MESSAGE_COUNT);\n\n    // Release memory\n    furi_thread_free(consumer_thread);\n    furi_thread_free(producer_thread);\n\n    furi_message_queue_free(data.message_queue);\n    furi_stream_buffer_free(data.stream_buffer);\n    furi_event_flag_free(data.event_flag);\n    furi_semaphore_free(data.semaphore);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_memmgr_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n#include <stdint.h>\n\nvoid test_furi_memmgr(void) {\n    void* ptr;\n\n    // allocate memory case\n    ptr = malloc(100);\n    mu_check(ptr != NULL);\n    // test that memory is zero-initialized after allocation\n    for(int i = 0; i < 100; i++) {\n        mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);\n    }\n    free(ptr);\n\n    // reallocate memory case\n    ptr = malloc(100);\n    memset(ptr, 66, 100);\n    ptr = realloc(ptr, 200);\n    mu_check(ptr != NULL);\n\n    // test that memory is really reallocated\n    for(int i = 0; i < 100; i++) {\n        mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);\n    }\n\n    free(ptr);\n\n    // allocate and zero-initialize array (calloc)\n    ptr = calloc(100, 2);\n    mu_check(ptr != NULL);\n    for(int i = 0; i < 100 * 2; i++) {\n        mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);\n    }\n    free(ptr);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_primitives_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define MESSAGE_QUEUE_CAPACITY     (16U)\n#define MESSAGE_QUEUE_ELEMENT_SIZE (sizeof(uint32_t))\n\n#define STREAM_BUFFER_SIZE      (32U)\n#define STREAM_BUFFER_TRG_LEVEL (STREAM_BUFFER_SIZE / 2U)\n\ntypedef struct {\n    FuriMessageQueue* message_queue;\n    FuriStreamBuffer* stream_buffer;\n} TestFuriPrimitivesData;\n\nstatic void test_furi_message_queue(TestFuriPrimitivesData* data) {\n    FuriMessageQueue* message_queue = data->message_queue;\n\n    mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));\n    mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));\n    mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_capacity(message_queue));\n    mu_assert_int_eq(\n        MESSAGE_QUEUE_ELEMENT_SIZE, furi_message_queue_get_message_size(message_queue));\n\n    for(uint32_t i = 0;; ++i) {\n        mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_space(message_queue));\n        mu_assert_int_eq(i, furi_message_queue_get_count(message_queue));\n\n        if(furi_message_queue_put(message_queue, &i, 0) != FuriStatusOk) {\n            break;\n        }\n    }\n\n    mu_assert_int_eq(0, furi_message_queue_get_space(message_queue));\n    mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_count(message_queue));\n\n    for(uint32_t i = 0;; ++i) {\n        mu_assert_int_eq(i, furi_message_queue_get_space(message_queue));\n        mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_count(message_queue));\n\n        uint32_t value;\n        if(furi_message_queue_get(message_queue, &value, 0) != FuriStatusOk) {\n            break;\n        }\n\n        mu_assert_int_eq(i, value);\n    }\n\n    mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));\n    mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));\n}\n\nstatic void test_furi_stream_buffer(TestFuriPrimitivesData* data) {\n    FuriStreamBuffer* stream_buffer = data->stream_buffer;\n\n    mu_assert(furi_stream_buffer_is_empty(stream_buffer), \"Must be empty\");\n    mu_assert(!furi_stream_buffer_is_full(stream_buffer), \"Must be not full\");\n    mu_assert_int_eq(0, furi_stream_buffer_bytes_available(stream_buffer));\n    mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_spaces_available(stream_buffer));\n\n    for(uint8_t i = 0;; ++i) {\n        mu_assert_int_eq(i, furi_stream_buffer_bytes_available(stream_buffer));\n        mu_assert_int_eq(\n            STREAM_BUFFER_SIZE - i, furi_stream_buffer_spaces_available(stream_buffer));\n\n        if(furi_stream_buffer_send(stream_buffer, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {\n            break;\n        }\n    }\n\n    mu_assert(!furi_stream_buffer_is_empty(stream_buffer), \"Must be not empty\");\n    mu_assert(furi_stream_buffer_is_full(stream_buffer), \"Must be full\");\n    mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_bytes_available(stream_buffer));\n    mu_assert_int_eq(0, furi_stream_buffer_spaces_available(stream_buffer));\n\n    for(uint8_t i = 0;; ++i) {\n        mu_assert_int_eq(\n            STREAM_BUFFER_SIZE - i, furi_stream_buffer_bytes_available(stream_buffer));\n        mu_assert_int_eq(i, furi_stream_buffer_spaces_available(stream_buffer));\n\n        uint8_t value;\n        if(furi_stream_buffer_receive(stream_buffer, &value, sizeof(uint8_t), 0) !=\n           sizeof(uint8_t)) {\n            break;\n        }\n\n        mu_assert_int_eq(i, value);\n    }\n}\n\n// This is a stub that needs expanding\nvoid test_furi_primitives(void) {\n    TestFuriPrimitivesData data = {\n        .message_queue =\n            furi_message_queue_alloc(MESSAGE_QUEUE_CAPACITY, MESSAGE_QUEUE_ELEMENT_SIZE),\n        .stream_buffer = furi_stream_buffer_alloc(STREAM_BUFFER_SIZE, STREAM_BUFFER_TRG_LEVEL),\n    };\n\n    test_furi_message_queue(&data);\n    test_furi_stream_buffer(&data);\n\n    furi_message_queue_free(data.message_queue);\n    furi_stream_buffer_free(data.stream_buffer);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_pubsub_test.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nconst uint32_t context_value = 0xdeadbeef;\nconst uint32_t notify_value_0 = 0x12345678;\nconst uint32_t notify_value_1 = 0x11223344;\n\nuint32_t pubsub_value = 0;\nuint32_t pubsub_context_value = 0;\n\nvoid test_pubsub_handler(const void* arg, void* ctx) {\n    pubsub_value = *(uint32_t*)arg;\n    pubsub_context_value = *(uint32_t*)ctx;\n}\n\nvoid test_furi_pubsub(void) {\n    FuriPubSub* test_pubsub = NULL;\n    FuriPubSubSubscription* test_pubsub_subscription = NULL;\n\n    // init pubsub case\n    test_pubsub = furi_pubsub_alloc();\n    mu_assert_pointers_not_eq(test_pubsub, NULL);\n\n    // subscribe pubsub case\n    test_pubsub_subscription =\n        furi_pubsub_subscribe(test_pubsub, test_pubsub_handler, (void*)&context_value);\n    mu_assert_pointers_not_eq(test_pubsub_subscription, NULL);\n\n    /// notify pubsub case\n    furi_pubsub_publish(test_pubsub, (void*)&notify_value_0);\n    mu_assert_int_eq(pubsub_value, notify_value_0);\n    mu_assert_int_eq(pubsub_context_value, context_value);\n\n    // unsubscribe pubsub case\n    furi_pubsub_unsubscribe(test_pubsub, test_pubsub_subscription);\n\n    /// notify unsubscribed pubsub case\n    furi_pubsub_publish(test_pubsub, (void*)&notify_value_1);\n    mu_assert_int_not_eq(pubsub_value, notify_value_1);\n\n    // delete pubsub case\n    furi_pubsub_free(test_pubsub);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_record_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define TEST_RECORD_NAME \"test/holding\"\n\nvoid test_furi_create_open(void) {\n    // Test that record does not exist\n    mu_check(furi_record_exists(TEST_RECORD_NAME) == false);\n\n    // Create record\n    uint8_t test_data = 0;\n    furi_record_create(TEST_RECORD_NAME, (void*)&test_data);\n\n    // Test that record exists\n    mu_check(furi_record_exists(TEST_RECORD_NAME) == true);\n\n    // Open it\n    void* record = furi_record_open(TEST_RECORD_NAME);\n    mu_assert_pointers_eq(record, &test_data);\n\n    // Close it\n    furi_record_close(TEST_RECORD_NAME);\n\n    // Clean up\n    furi_record_destroy(TEST_RECORD_NAME);\n\n    // Test that record does not exist\n    mu_check(furi_record_exists(TEST_RECORD_NAME) == false);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_stdio_test.c",
    "content": "#include <furi.h>\n#include <errno.h>\n#include <stdio.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define TAG \"StdioTest\"\n\n#define CONTEXT_MAGIC ((void*)0xDEADBEEF)\n\n// stdin\n\nstatic char mock_in[256];\nstatic size_t mock_in_len, mock_in_pos;\n\nstatic void set_mock_in(const char* str) {\n    size_t len = strlen(str);\n    strcpy(mock_in, str);\n    mock_in_len = len;\n    mock_in_pos = 0;\n}\n\nstatic size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context) {\n    UNUSED(wait);\n    furi_check(context == CONTEXT_MAGIC);\n    size_t remaining = mock_in_len - mock_in_pos;\n    size = MIN(remaining, size);\n    memcpy(buffer, mock_in + mock_in_pos, size);\n    mock_in_pos += size;\n    return size;\n}\n\nvoid test_stdin(void) {\n    FuriThreadStdinReadCallback in_cb;\n    void* in_ctx;\n    furi_thread_get_stdin_callback(&in_cb, &in_ctx);\n    furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC);\n    char buf[256];\n\n    // plain in\n    set_mock_in(\"Hello, World!\\n\");\n    fgets(buf, sizeof(buf), stdin);\n    mu_assert_string_eq(\"Hello, World!\\n\", buf);\n    mu_assert_int_eq(EOF, getchar());\n\n    // ungetc\n    ungetc('i', stdin);\n    ungetc('H', stdin);\n    fgets(buf, sizeof(buf), stdin);\n    mu_assert_string_eq(\"Hi\", buf);\n    mu_assert_int_eq(EOF, getchar());\n\n    // ungetc + plain in\n    set_mock_in(\" World\");\n    ungetc('i', stdin);\n    ungetc('H', stdin);\n    fgets(buf, sizeof(buf), stdin);\n    mu_assert_string_eq(\"Hi World\", buf);\n    mu_assert_int_eq(EOF, getchar());\n\n    // partial plain in\n    set_mock_in(\"Hello, World!\\n\");\n    fgets(buf, strlen(\"Hello\") + 1, stdin);\n    mu_assert_string_eq(\"Hello\", buf);\n    mu_assert_int_eq(',', getchar());\n    fgets(buf, sizeof(buf), stdin);\n    mu_assert_string_eq(\" World!\\n\", buf);\n\n    furi_thread_set_stdin_callback(in_cb, in_ctx);\n}\n\n// stdout\n\nstatic FuriString* mock_out;\nstatic FuriThreadStdoutWriteCallback original_out_cb;\nstatic void* original_out_ctx;\n\nstatic void mock_out_cb(const char* data, size_t size, void* context) {\n    furi_check(context == CONTEXT_MAGIC);\n    // there's no furi_string_cat_strn :(\n    for(size_t i = 0; i < size; i++) {\n        furi_string_push_back(mock_out, data[i]);\n    }\n}\n\nstatic void assert_and_clear_mock_out(const char* expected) {\n    // return the original stdout callback for the duration of the check\n    // if the check fails, we don't want the error to end up in our buffer,\n    // we want to be able to see it!\n    furi_thread_set_stdout_callback(original_out_cb, original_out_ctx);\n    mu_assert_string_eq(expected, furi_string_get_cstr(mock_out));\n    furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);\n\n    furi_string_reset(mock_out);\n}\n\nvoid test_stdout(void) {\n    furi_thread_get_stdout_callback(&original_out_cb, &original_out_ctx);\n    furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);\n    mock_out = furi_string_alloc();\n\n    puts(\"Hello, World!\");\n    assert_and_clear_mock_out(\"Hello, World!\\n\");\n\n    printf(\"He\");\n    printf(\"llo!\");\n    fflush(stdout);\n    assert_and_clear_mock_out(\"Hello!\");\n\n    furi_string_free(mock_out);\n    furi_thread_set_stdout_callback(original_out_cb, original_out_ctx);\n}\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi/furi_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n// v2 tests\nvoid test_furi_create_open(void);\nvoid test_furi_concurrent_access(void);\nvoid test_furi_pubsub(void);\nvoid test_furi_memmgr(void);\nvoid test_furi_event_loop(void);\nvoid test_furi_event_loop_self_unsubscribe(void);\nvoid test_errno_saving(void);\nvoid test_furi_primitives(void);\nvoid test_stdin(void);\nvoid test_stdout(void);\n\nstatic int foo = 0;\n\nvoid test_setup(void) {\n    foo = 7;\n}\n\nvoid test_teardown(void) {\n    /* Nothing */\n}\n\nMU_TEST(test_check) {\n    mu_check(foo != 6);\n}\n\n// v2 tests\nMU_TEST(mu_test_furi_create_open) {\n    test_furi_create_open();\n}\n\nMU_TEST(mu_test_furi_pubsub) {\n    test_furi_pubsub();\n}\n\nMU_TEST(mu_test_furi_memmgr) {\n    // this test is not accurate, but gives a basic understanding\n    // that memory management is working fine\n    test_furi_memmgr();\n}\n\nMU_TEST(mu_test_furi_event_loop) {\n    test_furi_event_loop();\n}\n\nMU_TEST(mu_test_furi_event_loop_self_unsubscribe) {\n    test_furi_event_loop_self_unsubscribe();\n}\n\nMU_TEST(mu_test_errno_saving) {\n    test_errno_saving();\n}\n\nMU_TEST(mu_test_furi_primitives) {\n    test_furi_primitives();\n}\n\nMU_TEST(mu_test_stdio) {\n    test_stdin();\n    test_stdout();\n}\n\nMU_TEST_SUITE(test_suite) {\n    MU_SUITE_CONFIGURE(&test_setup, &test_teardown);\n    MU_RUN_TEST(test_check);\n\n    // v2 tests\n    MU_RUN_TEST(mu_test_furi_create_open);\n    MU_RUN_TEST(mu_test_furi_pubsub);\n    MU_RUN_TEST(mu_test_furi_memmgr);\n    MU_RUN_TEST(mu_test_furi_event_loop);\n    MU_RUN_TEST(mu_test_furi_event_loop_self_unsubscribe);\n    MU_RUN_TEST(mu_test_stdio);\n    MU_RUN_TEST(mu_test_errno_saving);\n    MU_RUN_TEST(mu_test_furi_primitives);\n}\n\nint run_minunit_test_furi(void) {\n    MU_RUN_SUITE(test_suite);\n\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_furi)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <lp5562_reg.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define DATA_SIZE             4\n#define EEPROM_ADDRESS        0b10101000\n#define EEPROM_ADDRESS_HIGH   (EEPROM_ADDRESS | 0b10)\n#define EEPROM_SIZE           512\n#define EEPROM_PAGE_SIZE      16\n#define EEPROM_WRITE_DELAY_MS 6\n\nstatic void furi_hal_i2c_int_setup(void) {\n    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);\n}\n\nstatic void furi_hal_i2c_int_teardown(void) {\n    furi_hal_i2c_release(&furi_hal_i2c_handle_power);\n}\n\nstatic void furi_hal_i2c_ext_setup(void) {\n    furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);\n}\n\nstatic void furi_hal_i2c_ext_teardown(void) {\n    furi_hal_i2c_release(&furi_hal_i2c_handle_external);\n}\n\nMU_TEST(furi_hal_i2c_int_1b) {\n    bool ret = false;\n    uint8_t data_one = 0;\n\n    // 1 byte: read, write, read\n    ret = furi_hal_i2c_read_reg_8(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        LP5562_CHANNEL_BLUE_CURRENT_REGISTER,\n        &data_one,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"0 read_reg_8 failed\");\n    mu_assert(data_one != 0, \"0 invalid data\");\n    ret = furi_hal_i2c_write_reg_8(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        LP5562_CHANNEL_BLUE_CURRENT_REGISTER,\n        data_one,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"1 write_reg_8 failed\");\n    ret = furi_hal_i2c_read_reg_8(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        LP5562_CHANNEL_BLUE_CURRENT_REGISTER,\n        &data_one,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"2 read_reg_8 failed\");\n    mu_assert(data_one != 0, \"2 invalid data\");\n}\n\nMU_TEST(furi_hal_i2c_int_3b) {\n    bool ret = false;\n    uint8_t data_many[DATA_SIZE] = {0};\n\n    // 3 byte: read, write, read\n    data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;\n    ret = furi_hal_i2c_tx(\n        &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"3 tx failed\");\n    ret = furi_hal_i2c_rx(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        data_many + 1,\n        DATA_SIZE - 1,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"4 rx failed\");\n    for(size_t i = 0; i < DATA_SIZE; i++)\n        mu_assert(data_many[i] != 0, \"4 invalid data_many\");\n\n    ret = furi_hal_i2c_tx(\n        &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, DATA_SIZE, LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"5 tx failed\");\n\n    ret = furi_hal_i2c_tx(\n        &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"6 tx failed\");\n    ret = furi_hal_i2c_rx(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        data_many + 1,\n        DATA_SIZE - 1,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"7 rx failed\");\n    for(size_t i = 0; i < DATA_SIZE; i++)\n        mu_assert(data_many[i] != 0, \"7 invalid data_many\");\n}\n\nMU_TEST(furi_hal_i2c_int_1b_fail) {\n    bool ret = false;\n    uint8_t data_one = 0;\n\n    // 1 byte: fail, read, fail, write, fail, read\n    data_one = 0;\n    ret = furi_hal_i2c_read_reg_8(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS + 0x10,\n        LP5562_CHANNEL_BLUE_CURRENT_REGISTER,\n        &data_one,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(!ret, \"8 read_reg_8 failed\");\n    mu_assert(data_one == 0, \"8 invalid data\");\n    ret = furi_hal_i2c_read_reg_8(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        LP5562_CHANNEL_BLUE_CURRENT_REGISTER,\n        &data_one,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"9 read_reg_8 failed\");\n    mu_assert(data_one != 0, \"9 invalid data\");\n}\n\nMU_TEST(furi_hal_i2c_int_ext_3b) {\n    bool ret = false;\n    uint8_t data_many[DATA_SIZE] = {0};\n\n    // 3 byte: read\n    data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;\n    ret = furi_hal_i2c_tx_ext(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        false,\n        data_many,\n        1,\n        FuriHalI2cBeginStart,\n        FuriHalI2cEndAwaitRestart,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"3 tx failed\");\n\n    // Send a RESTART condition, then read the 3 bytes one after the other\n    ret = furi_hal_i2c_rx_ext(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        false,\n        data_many + 1,\n        1,\n        FuriHalI2cBeginRestart,\n        FuriHalI2cEndPause,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"4 rx failed\");\n    mu_assert(data_many[1] != 0, \"4 invalid data\");\n    ret = furi_hal_i2c_rx_ext(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        false,\n        data_many + 2,\n        1,\n        FuriHalI2cBeginResume,\n        FuriHalI2cEndPause,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"5 rx failed\");\n    mu_assert(data_many[2] != 0, \"5 invalid data\");\n    ret = furi_hal_i2c_rx_ext(\n        &furi_hal_i2c_handle_power,\n        LP5562_ADDRESS,\n        false,\n        data_many + 3,\n        1,\n        FuriHalI2cBeginResume,\n        FuriHalI2cEndStop,\n        LP5562_I2C_TIMEOUT);\n    mu_assert(ret, \"6 rx failed\");\n    mu_assert(data_many[3] != 0, \"6 invalid data\");\n}\n\nMU_TEST(furi_hal_i2c_ext_eeprom) {\n    if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 100)) {\n        printf(\"no device connected, skipping\\r\\n\");\n        return;\n    }\n\n    bool ret = false;\n    uint8_t buffer[EEPROM_SIZE] = {0};\n\n    for(size_t page = 0; page < (EEPROM_SIZE / EEPROM_PAGE_SIZE); ++page) {\n        // Fill page buffer\n        for(size_t page_byte = 0; page_byte < EEPROM_PAGE_SIZE; ++page_byte) {\n            // Each byte is its position in the EEPROM modulo 256\n            uint8_t byte = ((page * EEPROM_PAGE_SIZE) + page_byte) % 256;\n\n            buffer[page_byte] = byte;\n        }\n\n        uint8_t address = (page < 16) ? EEPROM_ADDRESS : EEPROM_ADDRESS_HIGH;\n\n        ret = furi_hal_i2c_write_mem(\n            &furi_hal_i2c_handle_external,\n            address,\n            page * EEPROM_PAGE_SIZE,\n            buffer,\n            EEPROM_PAGE_SIZE,\n            20);\n\n        mu_assert(ret, \"EEPROM write failed\");\n        furi_delay_ms(EEPROM_WRITE_DELAY_MS);\n    }\n\n    ret = furi_hal_i2c_read_mem(\n        &furi_hal_i2c_handle_external, EEPROM_ADDRESS, 0, buffer, EEPROM_SIZE, 100);\n\n    mu_assert(ret, \"EEPROM read failed\");\n\n    for(size_t pos = 0; pos < EEPROM_SIZE; ++pos) {\n        mu_assert_int_eq(pos % 256, buffer[pos]);\n    }\n}\n\nMU_TEST_SUITE(furi_hal_i2c_int_suite) {\n    MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown);\n    MU_RUN_TEST(furi_hal_i2c_int_1b);\n    MU_RUN_TEST(furi_hal_i2c_int_3b);\n    MU_RUN_TEST(furi_hal_i2c_int_ext_3b);\n    MU_RUN_TEST(furi_hal_i2c_int_1b_fail);\n}\n\nMU_TEST_SUITE(furi_hal_i2c_ext_suite) {\n    MU_SUITE_CONFIGURE(&furi_hal_i2c_ext_setup, &furi_hal_i2c_ext_teardown);\n    MU_RUN_TEST(furi_hal_i2c_ext_eeprom);\n}\n\nint run_minunit_test_furi_hal(void) {\n    MU_RUN_SUITE(furi_hal_i2c_int_suite);\n    MU_RUN_SUITE(furi_hal_i2c_ext_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_furi_hal)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nstatic const uint8_t key_ctr_1[32] = {\n    0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,\n    0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,\n};\nstatic const uint8_t iv_ctr_1[16] = {\n    0x00,\n    0x00,\n    0x00,\n    0x60,\n    0xDB,\n    0x56,\n    0x72,\n    0xC9,\n    0x7A,\n    0xA8,\n    0xF0,\n    0xB2,\n    0x00,\n    0x00,\n    0x00,\n    0x01,\n};\nstatic const uint8_t pt_ctr_1[16] = {\n    0x53,\n    0x69,\n    0x6E,\n    0x67,\n    0x6C,\n    0x65,\n    0x20,\n    0x62,\n    0x6C,\n    0x6F,\n    0x63,\n    0x6B,\n    0x20,\n    0x6D,\n    0x73,\n    0x67,\n};\nstatic const uint8_t tv_ctr_ct_1[16] = {\n    0x14,\n    0x5A,\n    0xD0,\n    0x1D,\n    0xBF,\n    0x82,\n    0x4E,\n    0xC7,\n    0x56,\n    0x08,\n    0x63,\n    0xDC,\n    0x71,\n    0xE3,\n    0xE0,\n    0xC0,\n};\n\nstatic const uint8_t key_ctr_2[32] = {\n    0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,\n    0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,\n};\nstatic const uint8_t iv_ctr_2[16] = {\n    0x00,\n    0x00,\n    0x00,\n    0x60,\n    0xDB,\n    0x56,\n    0x72,\n    0xC9,\n    0x7A,\n    0xA8,\n    0xF0,\n    0xB2,\n    0x00,\n    0x00,\n    0x00,\n    0x01,\n};\nstatic const uint8_t pt_ctr_2[0] = {};\n//static const uint8_t tv_ctr_ct_2[0] = {};\n\nstatic const uint8_t key_ctr_3[32] = {\n    0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86,\n    0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84,\n};\nstatic const uint8_t iv_ctr_3[16] = {\n    0x00,\n    0xFA,\n    0xAC,\n    0x24,\n    0xC1,\n    0x58,\n    0x5E,\n    0xF1,\n    0x5A,\n    0x43,\n    0xD8,\n    0x75,\n    0x00,\n    0x00,\n    0x00,\n    0x01,\n};\nstatic const uint8_t pt_ctr_3[32] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,\n    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,\n};\nstatic const uint8_t tv_ctr_ct_3[32] = {\n    0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9,\n    0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C,\n};\n\nstatic const uint8_t key_ctr_4[32] = {\n    0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,\n    0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,\n};\nstatic const uint8_t iv_ctr_4[16] = {\n    0x00,\n    0x1C,\n    0xC5,\n    0xB7,\n    0x51,\n    0xA5,\n    0x1D,\n    0x70,\n    0xA1,\n    0xC1,\n    0x11,\n    0x48,\n    0x00,\n    0x00,\n    0x00,\n    0x01,\n};\nstatic const uint8_t pt_ctr_4[36] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,\n    0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\n    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,\n};\nstatic const uint8_t tv_ctr_ct_4[36] = {\n    0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46,\n    0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07,\n    0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8,\n};\n\nstatic const uint8_t key_ctr_5[32] = {\n    0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,\n    0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,\n};\nstatic const uint8_t iv_ctr_5[16] = {\n    0x00,\n    0x1C,\n    0xC5,\n    0xB7,\n    0x51,\n    0xA5,\n    0x1D,\n    0x70,\n    0xA1,\n    0xC1,\n    0x11,\n    0x48,\n    0x00,\n    0x00,\n    0x00,\n    0x01,\n};\nstatic const uint8_t pt_ctr_5[0] = {};\n//static const uint8_t tv_ctr_ct_5[0] = {};\n\nstatic const uint8_t key_gcm_1[32] = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n};\nstatic const uint8_t iv_gcm_1[16] = {\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\nstatic const uint8_t pt_gcm_1[0] = {};\n//static const uint8_t tv_gcm_ct_1[0] = {};\nstatic const uint8_t aad_gcm_1[0] = {};\nstatic const uint8_t tv_gcm_tag_1[16] = {\n    0x53,\n    0x0F,\n    0x8A,\n    0xFB,\n    0xC7,\n    0x45,\n    0x36,\n    0xB9,\n    0xA9,\n    0x63,\n    0xB4,\n    0xF1,\n    0xC4,\n    0xCB,\n    0x73,\n    0x8B,\n};\n\nstatic const uint8_t key_gcm_2[32] = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n};\nstatic const uint8_t iv_gcm_2[16] = {\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\nstatic const uint8_t pt_gcm_2[16] = {\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\nstatic const uint8_t tv_gcm_ct_2[16] = {\n    0xCE,\n    0xA7,\n    0x40,\n    0x3D,\n    0x4D,\n    0x60,\n    0x6B,\n    0x6E,\n    0x07,\n    0x4E,\n    0xC5,\n    0xD3,\n    0xBA,\n    0xF3,\n    0x9D,\n    0x18,\n};\nstatic const uint8_t aad_gcm_2[0] = {};\nstatic const uint8_t tv_gcm_tag_2[16] = {\n    0xD0,\n    0xD1,\n    0xC8,\n    0xA7,\n    0x99,\n    0x99,\n    0x6B,\n    0xF0,\n    0x26,\n    0x5B,\n    0x98,\n    0xB5,\n    0xD4,\n    0x8A,\n    0xB9,\n    0x19,\n};\n\nstatic const uint8_t key_gcm_3[32] = {\n    0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,\n    0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,\n};\nstatic const uint8_t iv_gcm_3[16] = {\n    0xCA,\n    0xFE,\n    0xBA,\n    0xBE,\n    0xFA,\n    0xCE,\n    0xDB,\n    0xAD,\n    0xDE,\n    0xCA,\n    0xF8,\n    0x88,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\nstatic const uint8_t pt_gcm_3[64] = {\n    0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A,\n    0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72,\n    0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25,\n    0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55,\n};\nstatic const uint8_t tv_gcm_ct_3[64] = {\n    0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D,\n    0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA,\n    0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38,\n    0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD,\n};\nstatic const uint8_t aad_gcm_3[0] = {};\nstatic const uint8_t tv_gcm_tag_3[16] = {\n    0xB0,\n    0x94,\n    0xDA,\n    0xC5,\n    0xD9,\n    0x34,\n    0x71,\n    0xBD,\n    0xEC,\n    0x1A,\n    0x50,\n    0x22,\n    0x70,\n    0xE3,\n    0xCC,\n    0x6C,\n};\n\nstatic const uint8_t key_gcm_4[32] = {\n    0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,\n    0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,\n};\nstatic const uint8_t iv_gcm_4[16] = {\n    0xCA,\n    0xFE,\n    0xBA,\n    0xBE,\n    0xFA,\n    0xCE,\n    0xDB,\n    0xAD,\n    0xDE,\n    0xCA,\n    0xF8,\n    0x88,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\nstatic const uint8_t pt_gcm_4[60] = {\n    0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26,\n    0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31,\n    0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49,\n    0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39,\n};\nstatic const uint8_t tv_gcm_ct_4[60] = {\n    0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42,\n    0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55,\n    0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56,\n    0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62,\n};\nstatic const uint8_t aad_gcm_4[20] = {\n    0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED,\n    0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2,\n};\nstatic const uint8_t tv_gcm_tag_4[16] = {\n    0x76,\n    0xFC,\n    0x6E,\n    0xCE,\n    0x0F,\n    0x4E,\n    0x17,\n    0x68,\n    0xCD,\n    0xDF,\n    0x88,\n    0x53,\n    0xBB,\n    0x2D,\n    0x55,\n    0x1B,\n};\n\nstatic void furi_hal_crypto_ctr_setup(void) {\n}\n\nstatic void furi_hal_crypto_ctr_teardown(void) {\n}\n\nstatic void furi_hal_crypto_gcm_setup(void) {\n}\n\nstatic void furi_hal_crypto_gcm_teardown(void) {\n}\n\nMU_TEST(furi_hal_crypto_ctr_1) {\n    bool ret = false;\n    uint8_t ct[sizeof(pt_ctr_1)];\n\n    ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1));\n    mu_assert(ret, \"CTR 1 failed\");\n    mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1));\n}\n\nMU_TEST(furi_hal_crypto_ctr_2) {\n    bool ret = false;\n    uint8_t ct[sizeof(pt_ctr_2)];\n\n    ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2));\n    mu_assert(ret, \"CTR 2 failed\");\n    //mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2));\n}\n\nMU_TEST(furi_hal_crypto_ctr_3) {\n    bool ret = false;\n    uint8_t ct[sizeof(pt_ctr_3)];\n\n    ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3));\n    mu_assert(ret, \"CTR 3 failed\");\n    mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3));\n}\n\nMU_TEST(furi_hal_crypto_ctr_4) {\n    bool ret = false;\n    uint8_t ct[sizeof(pt_ctr_4)];\n\n    ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4));\n    mu_assert(ret, \"CTR 4 failed\");\n    mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4));\n}\n\nMU_TEST(furi_hal_crypto_ctr_5) {\n    bool ret = false;\n    uint8_t ct[sizeof(pt_ctr_5)];\n\n    ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5));\n    mu_assert(ret, \"CTR 5 failed\");\n    //mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5));\n}\n\nMU_TEST(furi_hal_crypto_gcm_1) {\n    bool ret = false;\n    uint8_t pt[sizeof(pt_gcm_1)];\n    uint8_t ct[sizeof(pt_gcm_1)];\n    uint8_t tag_enc[16];\n    uint8_t tag_dec[16];\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_1,\n        iv_gcm_1,\n        aad_gcm_1,\n        sizeof(aad_gcm_1),\n        pt_gcm_1,\n        ct,\n        sizeof(pt_gcm_1),\n        tag_enc,\n        false);\n    mu_assert(ret, \"GCM 1 encryption failed\");\n    //mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1));\n    mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16);\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true);\n    mu_assert(ret, \"GCM 1 decryption failed\");\n    //mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1));\n    mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16);\n}\n\nMU_TEST(furi_hal_crypto_gcm_2) {\n    bool ret = false;\n    uint8_t pt[sizeof(pt_gcm_2)];\n    uint8_t ct[sizeof(pt_gcm_2)];\n    uint8_t tag_enc[16];\n    uint8_t tag_dec[16];\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_2,\n        iv_gcm_2,\n        aad_gcm_2,\n        sizeof(aad_gcm_2),\n        pt_gcm_2,\n        ct,\n        sizeof(pt_gcm_2),\n        tag_enc,\n        false);\n    mu_assert(ret, \"GCM 2 encryption failed\");\n    mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2));\n    mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16);\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true);\n    mu_assert(ret, \"GCM 2 decryption failed\");\n    mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2));\n    mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16);\n}\n\nMU_TEST(furi_hal_crypto_gcm_3) {\n    bool ret = false;\n    uint8_t pt[sizeof(pt_gcm_3)];\n    uint8_t ct[sizeof(pt_gcm_3)];\n    uint8_t tag_enc[16];\n    uint8_t tag_dec[16];\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_3,\n        iv_gcm_3,\n        aad_gcm_3,\n        sizeof(aad_gcm_3),\n        pt_gcm_3,\n        ct,\n        sizeof(pt_gcm_3),\n        tag_enc,\n        false);\n    mu_assert(ret, \"GCM 3 encryption failed\");\n    mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3));\n    mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16);\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true);\n    mu_assert(ret, \"GCM 3 decryption failed\");\n    mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3));\n    mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16);\n}\n\nMU_TEST(furi_hal_crypto_gcm_4) {\n    bool ret = false;\n    uint8_t pt[sizeof(pt_gcm_4)];\n    uint8_t ct[sizeof(pt_gcm_4)];\n    uint8_t tag_enc[16];\n    uint8_t tag_dec[16];\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_4,\n        iv_gcm_4,\n        aad_gcm_4,\n        sizeof(aad_gcm_4),\n        pt_gcm_4,\n        ct,\n        sizeof(pt_gcm_4),\n        tag_enc,\n        false);\n    mu_assert(ret, \"GCM 4 encryption failed\");\n    mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4));\n    mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16);\n\n    ret = furi_hal_crypto_gcm(\n        key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true);\n    mu_assert(ret, \"GCM 4 decryption failed\");\n    mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4));\n    mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16);\n}\n\nMU_TEST_SUITE(furi_hal_crypto_ctr_test) {\n    MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown);\n    MU_RUN_TEST(furi_hal_crypto_ctr_1);\n    MU_RUN_TEST(furi_hal_crypto_ctr_2);\n    MU_RUN_TEST(furi_hal_crypto_ctr_3);\n    MU_RUN_TEST(furi_hal_crypto_ctr_4);\n    MU_RUN_TEST(furi_hal_crypto_ctr_5);\n}\n\nMU_TEST_SUITE(furi_hal_crypto_gcm_test) {\n    MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown);\n    MU_RUN_TEST(furi_hal_crypto_gcm_1);\n    MU_RUN_TEST(furi_hal_crypto_gcm_2);\n    MU_RUN_TEST(furi_hal_crypto_gcm_3);\n    MU_RUN_TEST(furi_hal_crypto_gcm_4);\n}\n\nint run_minunit_test_furi_hal_crypto(void) {\n    MU_RUN_SUITE(furi_hal_crypto_ctr_test);\n    MU_RUN_SUITE(furi_hal_crypto_gcm_test);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_furi_hal_crypto)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/furi_string/furi_string_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nstatic void test_setup(void) {\n}\n\nstatic void test_teardown(void) {\n}\n\nstatic FuriString* furi_string_alloc_vprintf_test(const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    FuriString* string = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n    return string;\n}\n\nMU_TEST(mu_test_furi_string_alloc_free) {\n    FuriString* tmp;\n    FuriString* string;\n\n    // test alloc and free\n    string = furi_string_alloc();\n    mu_check(string != NULL);\n    mu_check(furi_string_empty(string));\n    furi_string_free(string);\n\n    // test furi_string_alloc_set_str and free\n    string = furi_string_alloc_set_str(\"test\");\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n    mu_check(furi_string_cmp(string, \"test\") == 0);\n    furi_string_free(string);\n\n    // test furi_string_alloc_set and free\n    tmp = furi_string_alloc_set(\"more\");\n    string = furi_string_alloc_set(tmp);\n    furi_string_free(tmp);\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n    mu_check(furi_string_cmp(string, \"more\") == 0);\n    furi_string_free(string);\n\n    // test alloc_printf and free\n    string = furi_string_alloc_printf(\"test %d %s %c 0x%02x\", 1, \"two\", '3', 0x04);\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n    mu_check(furi_string_cmp(string, \"test 1 two 3 0x04\") == 0);\n    furi_string_free(string);\n\n    // test alloc_vprintf and free\n    string = furi_string_alloc_vprintf_test(\"test %d %s %c 0x%02x\", 4, \"five\", '6', 0x07);\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n    mu_check(furi_string_cmp(string, \"test 4 five 6 0x07\") == 0);\n    furi_string_free(string);\n\n    // test alloc_move and free\n    tmp = furi_string_alloc_set(\"move\");\n    string = furi_string_alloc_move(tmp);\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n    mu_check(furi_string_cmp(string, \"move\") == 0);\n    furi_string_free(string);\n}\n\nMU_TEST(mu_test_furi_string_mem) {\n    FuriString* string = furi_string_alloc_set(\"test\");\n    mu_check(string != NULL);\n    mu_check(!furi_string_empty(string));\n\n    // TODO FL-3493: how to test furi_string_reserve?\n\n    // test furi_string_reset\n    furi_string_reset(string);\n    mu_check(furi_string_empty(string));\n\n    // test furi_string_swap\n    furi_string_set(string, \"test\");\n    FuriString* swap_string = furi_string_alloc_set(\"swap\");\n    furi_string_swap(string, swap_string);\n    mu_check(furi_string_cmp(string, \"swap\") == 0);\n    mu_check(furi_string_cmp(swap_string, \"test\") == 0);\n    furi_string_free(swap_string);\n\n    // test furi_string_move\n    FuriString* move_string = furi_string_alloc_set(\"move\");\n    furi_string_move(string, move_string);\n    mu_check(furi_string_cmp(string, \"move\") == 0);\n    // move_string is now empty\n    // and tested by leaked memory check at the end of the tests\n\n    furi_string_set(string, \"abracadabra\");\n\n    // test furi_string_hash\n    mu_assert_int_eq(0xc3bc16d7, furi_string_hash(string));\n\n    // test furi_string_size\n    mu_assert_int_eq(11, furi_string_size(string));\n\n    // test furi_string_empty\n    mu_check(!furi_string_empty(string));\n    furi_string_reset(string);\n    mu_check(furi_string_empty(string));\n\n    furi_string_free(string);\n}\n\nMU_TEST(mu_test_furi_string_getters) {\n    FuriString* string = furi_string_alloc_set(\"test\");\n\n    // test furi_string_get_char\n    mu_check(furi_string_get_char(string, 0) == 't');\n    mu_check(furi_string_get_char(string, 1) == 'e');\n    mu_check(furi_string_get_char(string, 2) == 's');\n    mu_check(furi_string_get_char(string, 3) == 't');\n\n    // test furi_string_get_cstr\n    mu_assert_string_eq(\"test\", furi_string_get_cstr(string));\n    furi_string_free(string);\n}\n\nstatic FuriString* furi_string_vprintf_test(FuriString* string, const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    furi_string_vprintf(string, format, args);\n    va_end(args);\n    return string;\n}\n\nMU_TEST(mu_test_furi_string_setters) {\n    FuriString* tmp;\n    FuriString* string = furi_string_alloc();\n\n    // test furi_string_set_str\n    furi_string_set_str(string, \"test\");\n    mu_assert_string_eq(\"test\", furi_string_get_cstr(string));\n\n    // test furi_string_set\n    tmp = furi_string_alloc_set(\"more\");\n    furi_string_set(string, tmp);\n    furi_string_free(tmp);\n    mu_assert_string_eq(\"more\", furi_string_get_cstr(string));\n\n    // test furi_string_set_strn\n    furi_string_set_strn(string, \"test\", 2);\n    mu_assert_string_eq(\"te\", furi_string_get_cstr(string));\n\n    // test furi_string_set_char\n    furi_string_set_char(string, 0, 'a');\n    furi_string_set_char(string, 1, 'b');\n    mu_assert_string_eq(\"ab\", furi_string_get_cstr(string));\n\n    // test furi_string_set_n\n    tmp = furi_string_alloc_set(\"dodecahedron\");\n    furi_string_set_n(string, tmp, 4, 5);\n    furi_string_free(tmp);\n    mu_assert_string_eq(\"cahed\", furi_string_get_cstr(string));\n\n    // test furi_string_printf\n    furi_string_printf(string, \"test %d %s %c 0x%02x\", 1, \"two\", '3', 0x04);\n    mu_assert_string_eq(\"test 1 two 3 0x04\", furi_string_get_cstr(string));\n\n    // test furi_string_vprintf\n    furi_string_vprintf_test(string, \"test %d %s %c 0x%02x\", 4, \"five\", '6', 0x07);\n    mu_assert_string_eq(\"test 4 five 6 0x07\", furi_string_get_cstr(string));\n\n    furi_string_free(string);\n}\n\nstatic FuriString* furi_string_cat_vprintf_test(FuriString* string, const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    furi_string_cat_vprintf(string, format, args);\n    va_end(args);\n    return string;\n}\n\nMU_TEST(mu_test_furi_string_appends) {\n    FuriString* tmp;\n    FuriString* string = furi_string_alloc();\n\n    // test furi_string_push_back\n    furi_string_push_back(string, 't');\n    furi_string_push_back(string, 'e');\n    furi_string_push_back(string, 's');\n    furi_string_push_back(string, 't');\n    mu_assert_string_eq(\"test\", furi_string_get_cstr(string));\n    furi_string_push_back(string, '!');\n    mu_assert_string_eq(\"test!\", furi_string_get_cstr(string));\n\n    // test furi_string_cat_str\n    furi_string_cat_str(string, \"test\");\n    mu_assert_string_eq(\"test!test\", furi_string_get_cstr(string));\n\n    // test furi_string_cat\n    tmp = furi_string_alloc_set(\"more\");\n    furi_string_cat(string, tmp);\n    furi_string_free(tmp);\n    mu_assert_string_eq(\"test!testmore\", furi_string_get_cstr(string));\n\n    // test furi_string_cat_printf\n    furi_string_cat_printf(string, \"test %d %s %c 0x%02x\", 1, \"two\", '3', 0x04);\n    mu_assert_string_eq(\"test!testmoretest 1 two 3 0x04\", furi_string_get_cstr(string));\n\n    // test furi_string_cat_vprintf\n    furi_string_cat_vprintf_test(string, \"test %d %s %c 0x%02x\", 4, \"five\", '6', 0x07);\n    mu_assert_string_eq(\n        \"test!testmoretest 1 two 3 0x04test 4 five 6 0x07\", furi_string_get_cstr(string));\n\n    furi_string_free(string);\n}\n\nMU_TEST(mu_test_furi_string_compare) {\n    FuriString* string_1 = furi_string_alloc_set(\"string_1\");\n    FuriString* string_2 = furi_string_alloc_set(\"string_2\");\n\n    // test furi_string_cmp\n    mu_assert_int_eq(0, furi_string_cmp(string_1, string_1));\n    mu_assert_int_eq(0, furi_string_cmp(string_2, string_2));\n    mu_assert_int_eq(-1, furi_string_cmp(string_1, string_2));\n    mu_assert_int_eq(1, furi_string_cmp(string_2, string_1));\n\n    // test furi_string_cmp_str\n    mu_assert_int_eq(0, furi_string_cmp_str(string_1, \"string_1\"));\n    mu_assert_int_eq(0, furi_string_cmp_str(string_2, \"string_2\"));\n    mu_assert_int_eq(-1, furi_string_cmp_str(string_1, \"string_2\"));\n    mu_assert_int_eq(1, furi_string_cmp_str(string_2, \"string_1\"));\n\n    // test furi_string_cmpi\n    furi_string_set(string_1, \"string\");\n    furi_string_set(string_2, \"StrIng\");\n    mu_assert_int_eq(0, furi_string_cmpi(string_1, string_1));\n    mu_assert_int_eq(0, furi_string_cmpi(string_2, string_2));\n    mu_assert_int_eq(0, furi_string_cmpi(string_1, string_2));\n    mu_assert_int_eq(0, furi_string_cmpi(string_2, string_1));\n    furi_string_set(string_1, \"string_1\");\n    furi_string_set(string_2, \"StrIng_2\");\n    mu_assert_int_eq(32, furi_string_cmp(string_1, string_2));\n    mu_assert_int_eq(-32, furi_string_cmp(string_2, string_1));\n    mu_assert_int_eq(-1, furi_string_cmpi(string_1, string_2));\n    mu_assert_int_eq(1, furi_string_cmpi(string_2, string_1));\n\n    // test furi_string_cmpi_str\n    furi_string_set(string_1, \"string\");\n    mu_assert_int_eq(0, furi_string_cmp_str(string_1, \"string\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"String\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"STring\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"STRing\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"STRIng\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"STRINg\"));\n    mu_assert_int_eq(32, furi_string_cmp_str(string_1, \"STRING\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"string\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"String\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"STring\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"STRing\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"STRIng\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"STRINg\"));\n    mu_assert_int_eq(0, furi_string_cmpi_str(string_1, \"STRING\"));\n\n    furi_string_free(string_1);\n    furi_string_free(string_2);\n}\n\nMU_TEST(mu_test_furi_string_search) {\n    //                                            012345678901234567\n    FuriString* haystack = furi_string_alloc_set(\"test321test123test\");\n    FuriString* needle = furi_string_alloc_set(\"test\");\n\n    // test furi_string_search\n    mu_assert_int_eq(0, furi_string_search(haystack, needle));\n    mu_assert_int_eq(7, furi_string_search(haystack, needle, 1));\n    mu_assert_int_eq(14, furi_string_search(haystack, needle, 8));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, needle, 15));\n\n    FuriString* tmp = furi_string_alloc_set(\"testnone\");\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, tmp));\n    furi_string_free(tmp);\n\n    // test furi_string_search_str\n    mu_assert_int_eq(0, furi_string_search_str(haystack, \"test\"));\n    mu_assert_int_eq(7, furi_string_search_str(haystack, \"test\", 1));\n    mu_assert_int_eq(14, furi_string_search_str(haystack, \"test\", 8));\n    mu_assert_int_eq(4, furi_string_search_str(haystack, \"321\"));\n    mu_assert_int_eq(11, furi_string_search_str(haystack, \"123\"));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, \"testnone\"));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, \"test\", 15));\n\n    // test furi_string_search_char\n    mu_assert_int_eq(0, furi_string_search_char(haystack, 't'));\n    mu_assert_int_eq(1, furi_string_search_char(haystack, 'e'));\n    mu_assert_int_eq(2, furi_string_search_char(haystack, 's'));\n    mu_assert_int_eq(3, furi_string_search_char(haystack, 't', 1));\n    mu_assert_int_eq(7, furi_string_search_char(haystack, 't', 4));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_char(haystack, 'x'));\n\n    // test furi_string_search_rchar\n    mu_assert_int_eq(17, furi_string_search_rchar(haystack, 't'));\n    mu_assert_int_eq(15, furi_string_search_rchar(haystack, 'e'));\n    mu_assert_int_eq(16, furi_string_search_rchar(haystack, 's'));\n    mu_assert_int_eq(13, furi_string_search_rchar(haystack, '3'));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, '3', 14));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, 'x'));\n\n    furi_string_free(haystack);\n    furi_string_free(needle);\n}\n\nMU_TEST(mu_test_furi_string_equality) {\n    FuriString* string = furi_string_alloc_set(\"test\");\n    FuriString* string_eq = furi_string_alloc_set(\"test\");\n    FuriString* string_neq = furi_string_alloc_set(\"test2\");\n\n    // test furi_string_equal\n    mu_check(furi_string_equal(string, string_eq));\n    mu_check(!furi_string_equal(string, string_neq));\n\n    // test furi_string_equal_str\n    mu_check(furi_string_equal_str(string, \"test\"));\n    mu_check(!furi_string_equal_str(string, \"test2\"));\n    mu_check(furi_string_equal_str(string_neq, \"test2\"));\n    mu_check(!furi_string_equal_str(string_neq, \"test\"));\n\n    furi_string_free(string);\n    furi_string_free(string_eq);\n    furi_string_free(string_neq);\n}\n\nMU_TEST(mu_test_furi_string_replace) {\n    FuriString* needle = furi_string_alloc_set(\"test\");\n    FuriString* replace = furi_string_alloc_set(\"replace\");\n    FuriString* string = furi_string_alloc_set(\"test123test\");\n\n    // test furi_string_replace_at\n    furi_string_replace_at(string, 4, 3, \"!biglongword!\");\n    mu_assert_string_eq(\"test!biglongword!test\", furi_string_get_cstr(string));\n\n    // test furi_string_replace\n    mu_assert_int_eq(17, furi_string_replace(string, needle, replace, 1));\n    mu_assert_string_eq(\"test!biglongword!replace\", furi_string_get_cstr(string));\n    mu_assert_int_eq(0, furi_string_replace(string, needle, replace));\n    mu_assert_string_eq(\"replace!biglongword!replace\", furi_string_get_cstr(string));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace(string, needle, replace));\n    mu_assert_string_eq(\"replace!biglongword!replace\", furi_string_get_cstr(string));\n\n    // test furi_string_replace_str\n    mu_assert_int_eq(20, furi_string_replace_str(string, \"replace\", \"test\", 1));\n    mu_assert_string_eq(\"replace!biglongword!test\", furi_string_get_cstr(string));\n    mu_assert_int_eq(0, furi_string_replace_str(string, \"replace\", \"test\"));\n    mu_assert_string_eq(\"test!biglongword!test\", furi_string_get_cstr(string));\n    mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace_str(string, \"replace\", \"test\"));\n    mu_assert_string_eq(\"test!biglongword!test\", furi_string_get_cstr(string));\n\n    // test furi_string_replace_all\n    furi_string_replace_all(string, needle, replace);\n    mu_assert_string_eq(\"replace!biglongword!replace\", furi_string_get_cstr(string));\n\n    // test furi_string_replace_all_str\n    furi_string_replace_all_str(string, \"replace\", \"test\");\n    mu_assert_string_eq(\"test!biglongword!test\", furi_string_get_cstr(string));\n\n    furi_string_free(string);\n    furi_string_free(needle);\n    furi_string_free(replace);\n}\n\nMU_TEST(mu_test_furi_string_start_end) {\n    FuriString* string = furi_string_alloc_set(\"start_end\");\n    FuriString* start = furi_string_alloc_set(\"start\");\n    FuriString* end = furi_string_alloc_set(\"end\");\n\n    // test furi_string_start_with\n    mu_check(furi_string_start_with(string, start));\n    mu_check(!furi_string_start_with(string, end));\n\n    // test furi_string_start_with_str\n    mu_check(furi_string_start_with_str(string, \"start\"));\n    mu_check(!furi_string_start_with_str(string, \"end\"));\n\n    // test furi_string_end_with\n    mu_check(furi_string_end_with(string, end));\n    mu_check(!furi_string_end_with(string, start));\n\n    // test furi_string_end_with_str\n    mu_check(furi_string_end_with_str(string, \"end\"));\n    mu_check(!furi_string_end_with_str(string, \"start\"));\n\n    furi_string_free(string);\n    furi_string_free(start);\n    furi_string_free(end);\n}\n\nMU_TEST(mu_test_furi_string_trim) {\n    FuriString* string = furi_string_alloc_set(\"biglongstring\");\n\n    // test furi_string_left\n    furi_string_left(string, 7);\n    mu_assert_string_eq(\"biglong\", furi_string_get_cstr(string));\n\n    // test furi_string_right\n    furi_string_right(string, 3);\n    mu_assert_string_eq(\"long\", furi_string_get_cstr(string));\n\n    // test furi_string_mid\n    furi_string_mid(string, 1, 2);\n    mu_assert_string_eq(\"on\", furi_string_get_cstr(string));\n\n    // test furi_string_trim\n    furi_string_set(string, \"   \\n\\r\\tbiglongstring \\n\\r\\t  \");\n    furi_string_trim(string);\n    mu_assert_string_eq(\"biglongstring\", furi_string_get_cstr(string));\n    furi_string_set(string, \"aaaabaaaabbaaabaaaabbtestaaaaaabbaaabaababaa\");\n    furi_string_trim(string, \"ab\");\n    mu_assert_string_eq(\"test\", furi_string_get_cstr(string));\n\n    furi_string_free(string);\n}\n\nMU_TEST(mu_test_furi_string_utf8) {\n    FuriString* utf8_string = furi_string_alloc_set(\"イルカ\");\n\n    // test furi_string_utf8_length\n    mu_assert_int_eq(9, furi_string_size(utf8_string));\n    mu_assert_int_eq(3, furi_string_utf8_length(utf8_string));\n\n    // test furi_string_utf8_decode\n    const uint8_t dolphin_emoji_array[4] = {0xF0, 0x9F, 0x90, 0xAC};\n    FuriStringUTF8State state = FuriStringUTF8StateStarting;\n    FuriStringUnicodeValue value = 0;\n    furi_string_utf8_decode(dolphin_emoji_array[0], &state, &value);\n    mu_assert_int_eq(FuriStringUTF8StateDecoding3, state);\n    furi_string_utf8_decode(dolphin_emoji_array[1], &state, &value);\n    mu_assert_int_eq(FuriStringUTF8StateDecoding2, state);\n    furi_string_utf8_decode(dolphin_emoji_array[2], &state, &value);\n    mu_assert_int_eq(FuriStringUTF8StateDecoding1, state);\n    furi_string_utf8_decode(dolphin_emoji_array[3], &state, &value);\n    mu_assert_int_eq(FuriStringUTF8StateStarting, state);\n    mu_assert_int_eq(0x1F42C, value);\n\n    // test furi_string_utf8_push\n    furi_string_set(utf8_string, \"\");\n    furi_string_utf8_push(utf8_string, value);\n    mu_assert_string_eq(\"🐬\", furi_string_get_cstr(utf8_string));\n\n    furi_string_free(utf8_string);\n}\n\nMU_TEST_SUITE(test_suite) {\n    MU_SUITE_CONFIGURE(&test_setup, &test_teardown);\n\n    MU_RUN_TEST(mu_test_furi_string_alloc_free);\n    MU_RUN_TEST(mu_test_furi_string_mem);\n    MU_RUN_TEST(mu_test_furi_string_getters);\n    MU_RUN_TEST(mu_test_furi_string_setters);\n    MU_RUN_TEST(mu_test_furi_string_appends);\n    MU_RUN_TEST(mu_test_furi_string_compare);\n    MU_RUN_TEST(mu_test_furi_string_search);\n    MU_RUN_TEST(mu_test_furi_string_equality);\n    MU_RUN_TEST(mu_test_furi_string_replace);\n    MU_RUN_TEST(mu_test_furi_string_start_end);\n    MU_RUN_TEST(mu_test_furi_string_trim);\n    MU_RUN_TEST(mu_test_furi_string_utf8);\n}\n\nint run_minunit_test_furi_string(void) {\n    MU_RUN_SUITE(test_suite);\n\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_furi_string)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/infrared/infrared_test.c",
    "content": "#include <furi.h>\n#include <flipper_format.h>\n#include <infrared.h>\n#include <common/infrared_common_i.h>\n#include <lib/infrared/signal/infrared_brute_force.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#define IR_TEST_FILES_DIR   EXT_PATH(\"unit_tests/infrared/\")\n#define IR_TEST_FILE_PREFIX \"test_\"\n#define IR_TEST_FILE_SUFFIX \".irtest\"\n\ntypedef struct {\n    InfraredDecoderHandler* decoder_handler;\n    InfraredEncoderHandler* encoder_handler;\n    FuriString* file_path;\n    FlipperFormat* ff;\n    InfraredBruteForce* brutedb;\n} InfraredTest;\n\nstatic InfraredTest* test;\n\nstatic void infrared_test_alloc(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    test = malloc(sizeof(InfraredTest));\n    test->decoder_handler = infrared_alloc_decoder();\n    test->encoder_handler = infrared_alloc_encoder();\n    test->ff = flipper_format_buffered_file_alloc(storage);\n    test->file_path = furi_string_alloc();\n    test->brutedb = infrared_brute_force_alloc();\n}\n\nstatic void infrared_test_free(void) {\n    furi_check(test);\n    infrared_free_decoder(test->decoder_handler);\n    infrared_free_encoder(test->encoder_handler);\n    infrared_brute_force_free(test->brutedb);\n    flipper_format_free(test->ff);\n    furi_string_free(test->file_path);\n    furi_record_close(RECORD_STORAGE);\n    free(test);\n    test = NULL;\n}\n\nstatic bool infrared_test_prepare_file(const char* protocol_name) {\n    FuriString* file_type;\n    file_type = furi_string_alloc();\n    bool success = false;\n\n    furi_string_printf(\n        test->file_path,\n        \"%s%s%s%s\",\n        IR_TEST_FILES_DIR,\n        IR_TEST_FILE_PREFIX,\n        protocol_name,\n        IR_TEST_FILE_SUFFIX);\n\n    do {\n        uint32_t format_version;\n        if(!flipper_format_buffered_file_open_existing(\n               test->ff, furi_string_get_cstr(test->file_path)))\n            break;\n        if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;\n        if(furi_string_cmp_str(file_type, \"IR tests file\") || format_version != 1) break;\n        success = true;\n    } while(false);\n\n    furi_string_free(file_type);\n    return success;\n}\n\nstatic bool infrared_test_load_raw_signal(\n    FlipperFormat* ff,\n    const char* signal_name,\n    uint32_t** timings,\n    uint32_t* timings_count) {\n    FuriString* buf;\n    buf = furi_string_alloc();\n    bool success = false;\n\n    do {\n        bool is_name_found = false;\n        for(; !is_name_found && flipper_format_read_string(ff, \"name\", buf);\n            is_name_found = !furi_string_cmp(buf, signal_name))\n            ;\n\n        if(!is_name_found) break;\n        if(!flipper_format_read_string(ff, \"type\", buf) || furi_string_cmp_str(buf, \"raw\")) break;\n        if(!flipper_format_get_value_count(ff, \"data\", timings_count)) break;\n        if(!*timings_count) break;\n\n        *timings = malloc(*timings_count * sizeof(uint32_t*));\n        if(!flipper_format_read_uint32(ff, \"data\", *timings, *timings_count)) {\n            free(*timings);\n            break;\n        }\n        success = true;\n    } while(false);\n\n    furi_string_free(buf);\n    return success;\n}\n\nstatic bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) {\n    FuriString* buf;\n    buf = furi_string_alloc();\n    bool success = false;\n\n    do {\n        if(!flipper_format_read_string(ff, \"protocol\", buf)) break;\n        message->protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));\n        if(!infrared_is_protocol_valid(message->protocol)) break;\n        if(!flipper_format_read_hex(ff, \"address\", (uint8_t*)&message->address, sizeof(uint32_t)))\n            break;\n        if(!flipper_format_read_hex(ff, \"command\", (uint8_t*)&message->command, sizeof(uint32_t)))\n            break;\n        if(!flipper_format_read_bool(ff, \"repeat\", &message->repeat, 1)) break;\n        success = true;\n    } while(false);\n\n    furi_string_free(buf);\n    return success;\n}\n\nstatic bool infrared_test_load_messages(\n    FlipperFormat* ff,\n    const char* signal_name,\n    InfraredMessage** messages,\n    uint32_t* messages_count) {\n    FuriString* buf;\n    buf = furi_string_alloc();\n    bool success = false;\n\n    do {\n        bool is_name_found = false;\n        for(; !is_name_found && flipper_format_read_string(ff, \"name\", buf);\n            is_name_found = !furi_string_cmp(buf, signal_name))\n            ;\n\n        if(!is_name_found) break;\n        if(!flipper_format_read_string(ff, \"type\", buf) ||\n           furi_string_cmp_str(buf, \"parsed_array\"))\n            break;\n        if(!flipper_format_read_uint32(ff, \"count\", messages_count, 1)) break;\n        if(!*messages_count) break;\n\n        *messages = malloc(*messages_count * sizeof(InfraredMessage));\n        uint32_t i;\n        for(i = 0; i < *messages_count; ++i) {\n            if(!infrared_test_read_message(ff, (*messages) + i)) {\n                break;\n            }\n        }\n        if(*messages_count != i) {\n            free(*messages);\n            break;\n        }\n        success = true;\n    } while(false);\n\n    furi_string_free(buf);\n    return success;\n}\n\nstatic void infrared_test_compare_message_results(\n    const InfraredMessage* message_decoded,\n    const InfraredMessage* message_expected) {\n    mu_check(message_decoded->protocol == message_expected->protocol);\n    mu_check(message_decoded->command == message_expected->command);\n    mu_check(message_decoded->address == message_expected->address);\n    if((message_expected->protocol == InfraredProtocolSIRC) ||\n       (message_expected->protocol == InfraredProtocolSIRC15) ||\n       (message_expected->protocol == InfraredProtocolSIRC20)) {\n        mu_check(message_decoded->repeat == false);\n    } else {\n        mu_check(message_decoded->repeat == message_expected->repeat);\n    }\n}\n\n/* Encodes signal and merges same levels (high+high, low+low) */\nstatic void infrared_test_run_encoder_fill_array(\n    InfraredEncoderHandler* handler,\n    uint32_t* timings,\n    uint32_t* timings_len,\n    bool* start_level) {\n    uint32_t duration = 0;\n    bool level = false;\n    bool level_read;\n    InfraredStatus status = InfraredStatusError;\n    size_t i = 0;\n    bool first = true;\n\n    while(1) {\n        status = infrared_encode(handler, &duration, &level_read);\n        if(first) {\n            if(start_level) *start_level = level_read;\n            first = false;\n            timings[0] = 0;\n        } else if(level_read != level) {\n            ++i;\n            furi_check(i < *timings_len);\n            timings[i] = 0;\n        }\n        level = level_read;\n        timings[i] += duration;\n\n        furi_check((status == InfraredStatusOk) || (status == InfraredStatusDone));\n        if(status == InfraredStatusDone) break;\n    }\n\n    *timings_len = i + 1;\n}\n\n// messages in input array for encoder should have one protocol\nstatic void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_index) {\n    uint32_t* timings;\n    uint32_t timings_count = 200;\n    uint32_t* expected_timings;\n    uint32_t expected_timings_count;\n    InfraredMessage* input_messages;\n    uint32_t input_messages_count;\n\n    FuriString* buf;\n    buf = furi_string_alloc();\n\n    const char* protocol_name = infrared_get_protocol_name(protocol);\n    mu_assert(infrared_test_prepare_file(protocol_name), \"Failed to prepare test file\");\n\n    furi_string_printf(buf, \"encoder_input%ld\", test_index);\n    mu_assert(\n        infrared_test_load_messages(\n            test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),\n        \"Failed to load messages from file\");\n\n    furi_string_printf(buf, \"encoder_expected%ld\", test_index);\n    mu_assert(\n        infrared_test_load_raw_signal(\n            test->ff, furi_string_get_cstr(buf), &expected_timings, &expected_timings_count),\n        \"Failed to load raw signal from file\");\n\n    flipper_format_buffered_file_close(test->ff);\n    furi_string_free(buf);\n\n    uint32_t j = 0;\n    timings = malloc(sizeof(uint32_t) * timings_count);\n\n    for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {\n        const InfraredMessage* message = &input_messages[message_counter];\n        if(!message->repeat) {\n            infrared_reset_encoder(test->encoder_handler, message);\n        }\n\n        timings_count = 200;\n        infrared_test_run_encoder_fill_array(test->encoder_handler, timings, &timings_count, NULL);\n        furi_check(timings_count <= 200);\n\n        for(size_t i = 0; i < timings_count; ++i, ++j) {\n            mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));\n            mu_assert(j < expected_timings_count, \"encoded more timings than expected\");\n        }\n    }\n\n    free(input_messages);\n    free(expected_timings);\n    free(timings);\n\n    mu_assert(j == expected_timings_count, \"encoded less timings than expected\");\n}\n\nstatic void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_t test_index) {\n    uint32_t* timings = 0;\n    uint32_t timings_count = 200;\n    InfraredMessage* input_messages;\n    uint32_t input_messages_count;\n    bool level = false;\n\n    FuriString* buf;\n    buf = furi_string_alloc();\n\n    timings = malloc(sizeof(uint32_t) * timings_count);\n\n    const char* protocol_name = infrared_get_protocol_name(protocol);\n    mu_assert(infrared_test_prepare_file(protocol_name), \"Failed to prepare test file\");\n\n    furi_string_printf(buf, \"encoder_decoder_input%ld\", test_index);\n    mu_assert(\n        infrared_test_load_messages(\n            test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),\n        \"Failed to load messages from file\");\n\n    flipper_format_buffered_file_close(test->ff);\n    furi_string_free(buf);\n\n    for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {\n        const InfraredMessage* message_encoded = &input_messages[message_counter];\n        if(!message_encoded->repeat) {\n            infrared_reset_encoder(test->encoder_handler, message_encoded);\n        }\n\n        timings_count = 200;\n        infrared_test_run_encoder_fill_array(\n            test->encoder_handler, timings, &timings_count, &level);\n        furi_check(timings_count <= 200);\n\n        const InfraredMessage* message_decoded = 0;\n        for(size_t i = 0; i < timings_count; ++i) {\n            message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);\n            if((i == timings_count - 2) && level && message_decoded) {\n                /* In case we end with space timing - message can be decoded at last mark */\n                break;\n            } else if(i < timings_count - 1) {\n                mu_check(!message_decoded);\n            } else {\n                if(!message_decoded) {\n                    message_decoded = infrared_check_decoder_ready(test->decoder_handler);\n                }\n                mu_check(message_decoded);\n            }\n            level = !level;\n        }\n        if(message_decoded) {\n            infrared_test_compare_message_results(message_decoded, message_encoded);\n        } else {\n            mu_check(0);\n        }\n    }\n    free(input_messages);\n    free(timings);\n}\n\nstatic void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_index) {\n    uint32_t* timings;\n    uint32_t timings_count;\n    InfraredMessage* messages;\n    uint32_t messages_count;\n\n    FuriString* buf;\n    buf = furi_string_alloc();\n\n    mu_assert(\n        infrared_test_prepare_file(infrared_get_protocol_name(protocol)),\n        \"Failed to prepare test file\");\n\n    furi_string_printf(buf, \"decoder_input%ld\", test_index);\n    mu_assert(\n        infrared_test_load_raw_signal(\n            test->ff, furi_string_get_cstr(buf), &timings, &timings_count),\n        \"Failed to load raw signal from file\");\n\n    furi_string_printf(buf, \"decoder_expected%ld\", test_index);\n    mu_assert(\n        infrared_test_load_messages(\n            test->ff, furi_string_get_cstr(buf), &messages, &messages_count),\n        \"Failed to load messages from file\");\n\n    flipper_format_buffered_file_close(test->ff);\n    furi_string_free(buf);\n\n    InfraredMessage message_decoded_check_local;\n    bool level = 0;\n    uint32_t message_counter = 0;\n    const InfraredMessage* message_decoded = 0;\n\n    for(uint32_t i = 0; i < timings_count; ++i) {\n        const InfraredMessage* message_decoded_check = 0;\n\n        if(timings[i] > INFRARED_RAW_RX_TIMING_DELAY_US) {\n            message_decoded_check = infrared_check_decoder_ready(test->decoder_handler);\n            if(message_decoded_check) {\n                /* infrared_decode() can reset message, but we have to call infrared_decode() to perform real\n                 * simulation: infrared_check() by timeout, then infrared_decode() when meet edge */\n                message_decoded_check_local = *message_decoded_check;\n                message_decoded_check = &message_decoded_check_local;\n            }\n        }\n\n        message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);\n\n        if(message_decoded_check || message_decoded) {\n            mu_assert(\n                !(message_decoded_check && message_decoded),\n                \"both messages decoded: check_ready() and infrared_decode()\");\n\n            if(message_decoded_check) {\n                message_decoded = message_decoded_check;\n            }\n\n            mu_assert(message_counter < messages_count, \"decoded more than expected\");\n            infrared_test_compare_message_results(message_decoded, &messages[message_counter]);\n\n            ++message_counter;\n        }\n        level = !level;\n    }\n\n    message_decoded = infrared_check_decoder_ready(test->decoder_handler);\n    if(message_decoded) {\n        infrared_test_compare_message_results(message_decoded, &messages[message_counter]);\n        ++message_counter;\n    }\n\n    free(timings);\n    free(messages);\n\n    mu_assert(message_counter == messages_count, \"decoded less than expected\");\n}\n\nMU_TEST(infrared_test_decoder_samsung32) {\n    infrared_test_run_decoder(InfraredProtocolSamsung32, 1);\n}\n\nMU_TEST(infrared_test_decoder_mixed) {\n    infrared_test_run_decoder(InfraredProtocolRC5, 2);\n    infrared_test_run_decoder(InfraredProtocolSIRC, 1);\n    infrared_test_run_decoder(InfraredProtocolNECext, 1);\n    infrared_test_run_decoder(InfraredProtocolRC6, 2);\n    infrared_test_run_decoder(InfraredProtocolSamsung32, 1);\n    infrared_test_run_decoder(InfraredProtocolRC6, 1);\n    infrared_test_run_decoder(InfraredProtocolSamsung32, 1);\n    infrared_test_run_decoder(InfraredProtocolRC5, 1);\n    infrared_test_run_decoder(InfraredProtocolSIRC, 2);\n    infrared_test_run_decoder(InfraredProtocolNECext, 1);\n    infrared_test_run_decoder(InfraredProtocolSIRC, 4);\n    infrared_test_run_decoder(InfraredProtocolNEC, 2);\n    infrared_test_run_decoder(InfraredProtocolRC6, 1);\n    infrared_test_run_decoder(InfraredProtocolNECext, 1);\n    infrared_test_run_decoder(InfraredProtocolSIRC, 5);\n    infrared_test_run_decoder(InfraredProtocolNEC, 3);\n    infrared_test_run_decoder(InfraredProtocolRC5, 5);\n    infrared_test_run_decoder(InfraredProtocolSamsung32, 1);\n    infrared_test_run_decoder(InfraredProtocolSIRC, 3);\n    infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);\n    infrared_test_run_decoder(InfraredProtocolRCA, 1);\n    infrared_test_run_decoder(InfraredProtocolPioneer, 6);\n}\n\nMU_TEST(infrared_test_decoder_nec) {\n    for(uint32_t i = 1; i <= 3; ++i) {\n        infrared_test_run_decoder(InfraredProtocolNEC, i);\n    }\n}\n\nMU_TEST(infrared_test_decoder_unexpected_end_in_sequence) {\n    for(uint32_t i = 1; i <= 2; ++i) {\n        infrared_test_run_decoder(InfraredProtocolNEC, i);\n        infrared_test_run_decoder(InfraredProtocolNEC, i);\n    }\n}\n\nMU_TEST(infrared_test_decoder_necext1) {\n    for(uint32_t i = 0; i < 2; ++i) {\n        UNUSED(i);\n        infrared_test_run_decoder(InfraredProtocolNECext, 1);\n    }\n}\n\nMU_TEST(infrared_test_decoder_long_packets_with_nec_start) {\n    for(uint32_t i = 1; i <= 2; ++i) {\n        infrared_test_run_decoder(InfraredProtocolNEC42ext, i);\n    }\n}\n\nMU_TEST(infrared_test_encoder_sirc) {\n    for(uint32_t i = 1; i <= 2; ++i) {\n        infrared_test_run_encoder(InfraredProtocolSIRC, i);\n    }\n}\n\nMU_TEST(infrared_test_decoder_sirc) {\n    for(uint32_t i = 1; i <= 5; ++i) {\n        infrared_test_run_decoder(InfraredProtocolSIRC, 5);\n    }\n}\n\nMU_TEST(infrared_test_decoder_rc5) {\n    infrared_test_run_decoder(InfraredProtocolRC5X, 1);\n\n    for(uint32_t i = 1; i <= 7; ++i) {\n        infrared_test_run_decoder(InfraredProtocolRC5, i);\n    }\n}\n\nMU_TEST(infrared_test_encoder_rc5x) {\n    infrared_test_run_encoder(InfraredProtocolRC5X, 1);\n}\n\nMU_TEST(infrared_test_encoder_rc5) {\n    infrared_test_run_encoder(InfraredProtocolRC5, 1);\n}\n\nMU_TEST(infrared_test_decoder_rc6) {\n    infrared_test_run_decoder(InfraredProtocolRC6, 1);\n}\n\nMU_TEST(infrared_test_encoder_rc6) {\n    infrared_test_run_encoder(InfraredProtocolRC6, 1);\n}\n\nMU_TEST(infrared_test_decoder_kaseikyo) {\n    for(uint32_t i = 1; i <= 6; ++i) {\n        infrared_test_run_decoder(InfraredProtocolKaseikyo, i);\n    }\n}\n\nMU_TEST(infrared_test_decoder_rca) {\n    for(uint32_t i = 1; i <= 6; ++i) {\n        infrared_test_run_decoder(InfraredProtocolRCA, i);\n    }\n}\n\nMU_TEST(infrared_test_decoder_pioneer) {\n    for(uint32_t i = 1; i <= 11; ++i) {\n        infrared_test_run_decoder(InfraredProtocolPioneer, i);\n    }\n}\n\nMU_TEST(infrared_test_encoder_decoder_all) {\n    infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolNEC42, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolNEC42ext, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolSamsung32, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolRCA, 1);\n    infrared_test_run_encoder_decoder(InfraredProtocolPioneer, 1);\n}\n\nMU_TEST(infrared_test_ac_database) {\n    infrared_brute_force_set_db_filename(test->brutedb, EXT_PATH(\"infrared/assets/ac.ir\"));\n    uint32_t i = 0;\n    infrared_brute_force_add_record(test->brutedb, i++, \"Off\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Dh\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Cool_hi\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Heat_hi\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Cool_lo\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Heat_lo\");\n\n    mu_assert(\n        infrared_brute_force_calculate_messages(test->brutedb) == InfraredErrorCodeNone,\n        \"universal ac database is invalid\");\n\n    infrared_brute_force_reset(test->brutedb);\n}\n\nMU_TEST(infrared_test_audio_database) {\n    infrared_brute_force_set_db_filename(test->brutedb, EXT_PATH(\"infrared/assets/audio.ir\"));\n    uint32_t i = 0;\n    infrared_brute_force_add_record(test->brutedb, i++, \"Power\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Mute\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Play\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Pause\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Prev\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Next\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_dn\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_up\");\n\n    mu_assert(\n        infrared_brute_force_calculate_messages(test->brutedb) == InfraredErrorCodeNone,\n        \"universal audio database is invalid\");\n\n    infrared_brute_force_reset(test->brutedb);\n}\n\nMU_TEST(infrared_test_projector_database) {\n    infrared_brute_force_set_db_filename(test->brutedb, EXT_PATH(\"infrared/assets/projector.ir\"));\n    uint32_t i = 0;\n    infrared_brute_force_add_record(test->brutedb, i++, \"Power\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Mute\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_up\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_dn\");\n\n    mu_assert(\n        infrared_brute_force_calculate_messages(test->brutedb) == InfraredErrorCodeNone,\n        \"universal projector database is invalid\");\n\n    infrared_brute_force_reset(test->brutedb);\n}\n\nMU_TEST(infrared_test_tv_database) {\n    infrared_brute_force_set_db_filename(test->brutedb, EXT_PATH(\"infrared/assets/tv.ir\"));\n    uint32_t i = 0;\n    infrared_brute_force_add_record(test->brutedb, i++, \"Power\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Mute\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_up\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Ch_next\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Vol_dn\");\n    infrared_brute_force_add_record(test->brutedb, i++, \"Ch_prev\");\n\n    mu_assert(\n        infrared_brute_force_calculate_messages(test->brutedb) == InfraredErrorCodeNone,\n        \"universal tv database is invalid\");\n\n    infrared_brute_force_reset(test->brutedb);\n}\n\nMU_TEST_SUITE(infrared_test) {\n    MU_SUITE_CONFIGURE(&infrared_test_alloc, &infrared_test_free);\n\n    MU_RUN_TEST(infrared_test_encoder_sirc);\n    MU_RUN_TEST(infrared_test_decoder_sirc);\n    MU_RUN_TEST(infrared_test_encoder_rc5x);\n    MU_RUN_TEST(infrared_test_encoder_rc5);\n    MU_RUN_TEST(infrared_test_decoder_rc5);\n    MU_RUN_TEST(infrared_test_decoder_rc6);\n    MU_RUN_TEST(infrared_test_encoder_rc6);\n    MU_RUN_TEST(infrared_test_decoder_unexpected_end_in_sequence);\n    MU_RUN_TEST(infrared_test_decoder_long_packets_with_nec_start);\n    MU_RUN_TEST(infrared_test_decoder_nec);\n    MU_RUN_TEST(infrared_test_decoder_samsung32);\n    MU_RUN_TEST(infrared_test_decoder_necext1);\n    MU_RUN_TEST(infrared_test_decoder_kaseikyo);\n    MU_RUN_TEST(infrared_test_decoder_rca);\n    MU_RUN_TEST(infrared_test_decoder_pioneer);\n    MU_RUN_TEST(infrared_test_decoder_mixed);\n    MU_RUN_TEST(infrared_test_encoder_decoder_all);\n    MU_RUN_TEST(infrared_test_ac_database);\n    MU_RUN_TEST(infrared_test_audio_database);\n    MU_RUN_TEST(infrared_test_projector_database);\n    MU_RUN_TEST(infrared_test_tv_database);\n}\n\nint run_minunit_test_infrared(void) {\n    MU_RUN_SUITE(infrared_test);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_infrared)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/js/js_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_random.h>\n\n#include <storage/storage.h>\n#include <applications/system/js_app/js_thread.h>\n#include <applications/system/js_app/js_value.h>\n\n#include <stdint.h>\n\n#define TAG \"JsUnitTests\"\n\n#define JS_SCRIPT_PATH(name) EXT_PATH(\"unit_tests/js/\" name \".js\")\n\ntypedef enum {\n    JsTestsFinished = 1,\n    JsTestsError = 2,\n} JsTestFlag;\n\ntypedef struct {\n    FuriEventFlag* event_flags;\n    FuriString* error_string;\n} JsTestCallbackContext;\n\nstatic void js_test_callback(JsThreadEvent event, const char* msg, void* param) {\n    JsTestCallbackContext* context = param;\n    if(event == JsThreadEventPrint) {\n        FURI_LOG_I(\"js_test\", \"%s\", msg);\n    } else if(event == JsThreadEventError || event == JsThreadEventErrorTrace) {\n        context->error_string = furi_string_alloc_set_str(msg);\n        furi_event_flag_set(context->event_flags, JsTestsFinished | JsTestsError);\n    } else if(event == JsThreadEventDone) {\n        furi_event_flag_set(context->event_flags, JsTestsFinished);\n    }\n}\n\nstatic void js_test_run(const char* script_path) {\n    JsTestCallbackContext* context = malloc(sizeof(JsTestCallbackContext));\n    context->event_flags = furi_event_flag_alloc();\n\n    JsThread* thread = js_thread_run(script_path, js_test_callback, context);\n    uint32_t flags = furi_event_flag_wait(\n        context->event_flags, JsTestsFinished, FuriFlagWaitAny, FuriWaitForever);\n    if(flags & FuriFlagError) {\n        // getting the flags themselves should not fail\n        furi_crash();\n    }\n\n    FuriString* error_string = context->error_string;\n\n    js_thread_stop(thread);\n    furi_event_flag_free(context->event_flags);\n    free(context);\n\n    if(flags & JsTestsError) {\n        // memory leak: not freeing the FuriString if the tests fail,\n        // because mu_fail executes a return\n        //\n        // who cares tho?\n        mu_fail(furi_string_get_cstr(error_string));\n    }\n}\n\nMU_TEST(js_test_basic) {\n    js_test_run(JS_SCRIPT_PATH(\"basic\"));\n}\nMU_TEST(js_test_math) {\n    js_test_run(JS_SCRIPT_PATH(\"math\"));\n}\nMU_TEST(js_test_event_loop) {\n    js_test_run(JS_SCRIPT_PATH(\"event_loop\"));\n}\nMU_TEST(js_test_storage) {\n    js_test_run(JS_SCRIPT_PATH(\"storage\"));\n}\n\nstatic void js_value_test_compatibility_matrix(struct mjs* mjs) {\n    static const JsValueType types[] = {\n        JsValueTypeAny,\n        JsValueTypeAnyArray,\n        JsValueTypeAnyObject,\n        JsValueTypeFunction,\n        JsValueTypeRawPointer,\n        JsValueTypeInt32,\n        JsValueTypeDouble,\n        JsValueTypeString,\n        JsValueTypeBool,\n    };\n\n    mjs_val_t values[] = {\n        mjs_mk_undefined(),\n        mjs_mk_foreign(mjs, (void*)0xDEADBEEF),\n        mjs_mk_array(mjs),\n        mjs_mk_object(mjs),\n        mjs_mk_number(mjs, 123.456),\n        mjs_mk_string(mjs, \"test\", ~0, false),\n        mjs_mk_boolean(mjs, true),\n    };\n\n// for proper matrix formatting and better readability\n#define YES true\n#define NO_ false\n    static const bool success_matrix[COUNT_OF(types)][COUNT_OF(values)] = {\n        //                                      types:\n        {YES, YES, YES, YES, YES, YES, YES}, // any\n        {NO_, NO_, YES, NO_, NO_, NO_, NO_}, // array\n        {NO_, NO_, YES, YES, NO_, NO_, NO_}, // obj\n        {NO_, NO_, NO_, NO_, NO_, NO_, NO_}, // fn\n        {NO_, YES, NO_, NO_, NO_, NO_, NO_}, // ptr\n        {NO_, NO_, NO_, NO_, YES, NO_, NO_}, // int32\n        {NO_, NO_, NO_, NO_, YES, NO_, NO_}, // double\n        {NO_, NO_, NO_, NO_, NO_, YES, NO_}, // str\n        {NO_, NO_, NO_, NO_, NO_, NO_, YES}, // bool\n        //\n        //und ptr  arr  obj  num  str  bool <- values\n    };\n#undef NO_\n#undef YES\n\n    for(size_t i = 0; i < COUNT_OF(types); i++) {\n        for(size_t j = 0; j < COUNT_OF(values); j++) {\n            const JsValueDeclaration declaration = {\n                .type = types[i],\n                .n_children = 0,\n            };\n            // we only care about the status, not the result. double has the largest size out of\n            // all the results\n            uint8_t result[sizeof(double)];\n            JsValueParseStatus status;\n            JS_VALUE_PARSE(\n                mjs,\n                JS_VALUE_PARSE_SOURCE_VALUE(&declaration),\n                JsValueParseFlagNone,\n                &status,\n                &values[j],\n                result);\n            if((status == JsValueParseStatusOk) != success_matrix[i][j]) {\n                FURI_LOG_E(TAG, \"type %zu, value %zu\", i, j);\n                mu_fail(\"see serial logs\");\n            }\n        }\n    }\n}\n\nstatic void js_value_test_literal(struct mjs* mjs) {\n    static const JsValueType types[] = {\n        JsValueTypeAny,\n        JsValueTypeAnyArray,\n        JsValueTypeAnyObject,\n    };\n\n    mjs_val_t values[] = {\n        mjs_mk_undefined(),\n        mjs_mk_array(mjs),\n        mjs_mk_object(mjs),\n    };\n\n    mu_assert_int_eq(COUNT_OF(types), COUNT_OF(values));\n    for(size_t i = 0; i < COUNT_OF(types); i++) {\n        const JsValueDeclaration declaration = {\n            .type = types[i],\n            .n_children = 0,\n        };\n        mjs_val_t result;\n        JsValueParseStatus status;\n        JS_VALUE_PARSE(\n            mjs,\n            JS_VALUE_PARSE_SOURCE_VALUE(&declaration),\n            JsValueParseFlagNone,\n            &status,\n            &values[i],\n            &result);\n        mu_assert_int_eq(JsValueParseStatusOk, status);\n        mu_assert(result == values[i], \"wrong result\");\n    }\n}\n\nstatic void js_value_test_primitive(\n    struct mjs* mjs,\n    JsValueType type,\n    const void* c_value,\n    size_t c_value_size,\n    mjs_val_t js_val) {\n    const JsValueDeclaration declaration = {\n        .type = type,\n        .n_children = 0,\n    };\n    uint8_t result[c_value_size];\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&declaration),\n        JsValueParseFlagNone,\n        &status,\n        &js_val,\n        result);\n    mu_assert_int_eq(JsValueParseStatusOk, status);\n    if(type == JsValueTypeString) {\n        const char* result_str = *(const char**)&result;\n        mu_assert_string_eq(c_value, result_str);\n    } else {\n        mu_assert_mem_eq(c_value, result, c_value_size);\n    }\n}\n\nstatic void js_value_test_primitives(struct mjs* mjs) {\n    int32_t i32 = 123;\n    js_value_test_primitive(mjs, JsValueTypeInt32, &i32, sizeof(i32), mjs_mk_number(mjs, i32));\n\n    double dbl = 123.456;\n    js_value_test_primitive(mjs, JsValueTypeDouble, &dbl, sizeof(dbl), mjs_mk_number(mjs, dbl));\n\n    const char* str = \"test\";\n    js_value_test_primitive(\n        mjs, JsValueTypeString, str, strlen(str) + 1, mjs_mk_string(mjs, str, ~0, false));\n\n    bool boolean = true;\n    js_value_test_primitive(\n        mjs, JsValueTypeBool, &boolean, sizeof(boolean), mjs_mk_boolean(mjs, boolean));\n}\n\nstatic uint32_t\n    js_value_test_enum(struct mjs* mjs, const JsValueDeclaration* decl, const char* value) {\n    mjs_val_t str = mjs_mk_string(mjs, value, ~0, false);\n    uint32_t result;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs, JS_VALUE_PARSE_SOURCE_VALUE(decl), JsValueParseFlagNone, &status, &str, &result);\n    if(status != JsValueParseStatusOk) return 0;\n    return result;\n}\n\nstatic void js_value_test_enums(struct mjs* mjs) {\n    static const JsValueEnumVariant enum_1_variants[] = {\n        {\"variant 1\", 1},\n        {\"variant 2\", 2},\n        {\"variant 3\", 3},\n    };\n    static const JsValueDeclaration enum_1 = JS_VALUE_ENUM(uint32_t, enum_1_variants);\n\n    static const JsValueEnumVariant enum_2_variants[] = {\n        {\"read\", 4},\n        {\"write\", 8},\n    };\n    static const JsValueDeclaration enum_2 = JS_VALUE_ENUM(uint32_t, enum_2_variants);\n\n    mu_assert_int_eq(1, js_value_test_enum(mjs, &enum_1, \"variant 1\"));\n    mu_assert_int_eq(2, js_value_test_enum(mjs, &enum_1, \"variant 2\"));\n    mu_assert_int_eq(3, js_value_test_enum(mjs, &enum_1, \"variant 3\"));\n    mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_1, \"not a thing\"));\n\n    mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, \"variant 1\"));\n    mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, \"variant 2\"));\n    mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, \"variant 3\"));\n    mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, \"not a thing\"));\n    mu_assert_int_eq(4, js_value_test_enum(mjs, &enum_2, \"read\"));\n    mu_assert_int_eq(8, js_value_test_enum(mjs, &enum_2, \"write\"));\n}\n\nstatic void js_value_test_object(struct mjs* mjs) {\n    static const JsValueDeclaration int_decl = JS_VALUE_SIMPLE(JsValueTypeInt32);\n\n    static const JsValueDeclaration str_decl = JS_VALUE_SIMPLE(JsValueTypeString);\n\n    static const JsValueEnumVariant enum_variants[] = {\n        {\"variant 1\", 1},\n        {\"variant 2\", 2},\n        {\"variant 3\", 3},\n    };\n    static const JsValueDeclaration enum_decl = JS_VALUE_ENUM(uint32_t, enum_variants);\n\n    static const JsValueObjectField fields[] = {\n        {\"int\", &int_decl},\n        {\"str\", &str_decl},\n        {\"enum\", &enum_decl},\n    };\n    static const JsValueDeclaration object_decl = JS_VALUE_OBJECT(fields);\n\n    mjs_val_t object = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, object) {\n        JS_FIELD(\"str\", mjs_mk_string(mjs, \"Helloooo!\", ~0, false));\n        JS_FIELD(\"int\", mjs_mk_number(mjs, 123));\n        JS_FIELD(\"enum\", mjs_mk_string(mjs, \"variant 2\", ~0, false));\n    }\n\n    const char* result_str;\n    int32_t result_int;\n    uint32_t result_enum;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&object_decl),\n        JsValueParseFlagNone,\n        &status,\n        &object,\n        &result_int,\n        &result_str,\n        &result_enum);\n    mu_assert_int_eq(JsValueParseStatusOk, status);\n    mu_assert_string_eq(\"Helloooo!\", result_str);\n    mu_assert_int_eq(123, result_int);\n    mu_assert_int_eq(2, result_enum);\n}\n\nstatic void js_value_test_default(struct mjs* mjs) {\n    static const JsValueDeclaration int_decl =\n        JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 123);\n    static const JsValueDeclaration str_decl = JS_VALUE_SIMPLE(JsValueTypeString);\n\n    static const JsValueObjectField fields[] = {\n        {\"int\", &int_decl},\n        {\"str\", &str_decl},\n    };\n    static const JsValueDeclaration object_decl = JS_VALUE_OBJECT(fields);\n\n    mjs_val_t object = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, object) {\n        JS_FIELD(\"str\", mjs_mk_string(mjs, \"Helloooo!\", ~0, false));\n        JS_FIELD(\"int\", mjs_mk_undefined());\n    }\n\n    const char* result_str;\n    int32_t result_int;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&object_decl),\n        JsValueParseFlagNone,\n        &status,\n        &object,\n        &result_int,\n        &result_str);\n    mu_assert_string_eq(\"Helloooo!\", result_str);\n    mu_assert_int_eq(123, result_int);\n}\n\nstatic void js_value_test_args_fn(struct mjs* mjs) {\n    static const JsValueDeclaration arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments args = JS_VALUE_ARGS(arg_list);\n\n    int32_t a, b, c;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &args, &a, &b, &c);\n\n    mu_assert_int_eq(123, a);\n    mu_assert_int_eq(456, b);\n    mu_assert_int_eq(-420, c);\n}\n\nstatic void js_value_test_args(struct mjs* mjs) {\n    mjs_val_t function = MJS_MK_FN(js_value_test_args_fn);\n\n    mjs_val_t result;\n    mjs_val_t args[] = {\n        mjs_mk_number(mjs, 123),\n        mjs_mk_number(mjs, 456),\n        mjs_mk_number(mjs, -420),\n    };\n    mu_assert_int_eq(\n        MJS_OK, mjs_apply(mjs, &result, function, MJS_UNDEFINED, COUNT_OF(args), args));\n}\n\nMU_TEST(js_value_test) {\n    struct mjs* mjs = mjs_create(NULL);\n\n    js_value_test_compatibility_matrix(mjs);\n    js_value_test_literal(mjs);\n    js_value_test_primitives(mjs);\n    js_value_test_enums(mjs);\n    js_value_test_object(mjs);\n    js_value_test_default(mjs);\n    js_value_test_args(mjs);\n\n    mjs_destroy(mjs);\n}\n\nMU_TEST_SUITE(test_js) {\n    MU_RUN_TEST(js_value_test);\n    MU_RUN_TEST(js_test_basic);\n    MU_RUN_TEST(js_test_math);\n    MU_RUN_TEST(js_test_event_loop);\n    MU_RUN_TEST(js_test_storage);\n}\n\nint run_minunit_test_js(void) {\n    MU_RUN_SUITE(test_js);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_js)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n#include <toolbox/protocols/protocol_dict.h>\n#include <lfrfid/protocols/lfrfid_protocols.h>\n#include <toolbox/pulse_protocols/pulse_glue.h>\n\n#define LF_RFID_READ_TIMING_MULTIPLIER 8\n\n#define EM_TEST_DATA                    {0x58, 0x00, 0x85, 0x64, 0x02}\n#define EM_TEST_DATA_SIZE               5\n#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2)\n\nconst int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = {\n    32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, -32,\n    32,  32,  -32, -32, 32,  32,  -32, -32, 32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32,\n    -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32,\n    32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32, -32, 32,  32,  -32, -32, 32,  32,  -32,\n    -32, 32,  -32, 32,  32,  -32, 32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32, -32, 32,  -32,\n    32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  32,  -32,\n    -32, 32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,\n};\n\n#define HID10301_TEST_DATA                    {0x8D, 0x48, 0xA8}\n#define HID10301_TEST_DATA_SIZE               3\n#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2)\n\nconst int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = {\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n};\n\n#define IOPROX_XSF_TEST_DATA                    {0x65, 0x01, 0x05, 0x39}\n#define IOPROX_XSF_TEST_DATA_SIZE               4\n#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2)\n\nconst int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = {\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,\n    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,\n    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,\n};\n\n#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2)\n#define INDALA26_TEST_DATA               {0x3B, 0x73, 0x64, 0xA8}\n#define INDALA26_TEST_DATA_SIZE          4\n\nconst int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = {\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,\n    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,\n    -1, 1,  -1, 1,  -1, 1,  -1, 1,\n};\n\n#define FDXB_TEST_DATA                    {0x44, 0x88, 0x23, 0xF2, 0x5A, 0x6F, 0x00, 0x01, 0x00, 0x00, 0x00}\n#define FDXB_TEST_DATA_SIZE               11\n#define FDXB_TEST_EMULATION_TIMINGS_COUNT (206)\n\nconst int8_t fdxb_test_timings[FDXB_TEST_EMULATION_TIMINGS_COUNT] = {\n    32,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,\n    -16, 16,  -32, 16,  -16, 32,  -16, 16,  -16, 16,  -16, 16,  -32, 16,  -16, 16,  -16, 32,  -32,\n    16,  -16, 16,  -16, 16,  -16, 32,  -16, 16,  -16, 16,  -16, 16,  -32, 16,  -16, 16,  -16, 32,\n    -16, 16,  -16, 16,  -16, 16,  -32, 32,  -32, 32,  -32, 32,  -32, 16,  -16, 16,  -16, 32,  -16,\n    16,  -32, 16,  -16, 32,  -16, 16,  -32, 32,  -16, 16,  -32, 16,  -16, 32,  -16, 16,  -32, 32,\n    -16, 16,  -32, 32,  -32, 32,  -32, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16,\n    16,  -16, 16,  -16, 32,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,\n    -32, 32,  -32, 32,  -32, 32,  -32, 16,  -16, 32,  -32, 32,  -16, 16,  -16, 16,  -32, 32,  -32,\n    32,  -32, 32,  -32, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,\n    -16, 32,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -32,\n    16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16, 16,  -16,\n};\n\nMU_TEST(test_lfrfid_protocol_em_read_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));\n    mu_assert_string_eq(\"EM4100\", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));\n    mu_assert_string_eq(\"EM-Micro\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));\n\n    const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;\n\n    protocol_dict_decoders_start(dict);\n\n    ProtocolId protocol = PROTOCOL_NO;\n    PulseGlue* pulse_glue = pulse_glue_alloc();\n\n    for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {\n        bool pulse_pop = pulse_glue_push(\n            pulse_glue,\n            em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0,\n            abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) *\n                LF_RFID_READ_TIMING_MULTIPLIER);\n\n        if(pulse_pop) {\n            uint32_t length, period;\n            pulse_glue_pop(pulse_glue, &length, &period);\n\n            protocol = protocol_dict_decoders_feed(dict, true, period);\n            if(protocol != PROTOCOL_NO) break;\n\n            protocol = protocol_dict_decoders_feed(dict, false, length - period);\n            if(protocol != PROTOCOL_NO) break;\n        }\n    }\n\n    pulse_glue_free(pulse_glue);\n\n    mu_assert_int_eq(LFRFIDProtocolEM4100, protocol);\n    uint8_t received_data[EM_TEST_DATA_SIZE] = {0};\n    protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE);\n\n    mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE);\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_em_emulate_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));\n    mu_assert_string_eq(\"EM4100\", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));\n    mu_assert_string_eq(\"EM-Micro\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));\n\n    const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;\n\n    protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE);\n    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100));\n\n    for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) {\n        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100);\n\n        if(level_duration_get_level(level_duration)) {\n            mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration));\n        } else {\n            mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration));\n        }\n    }\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_h10301_read_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(\n        HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));\n    mu_assert_string_eq(\"H10301\", protocol_dict_get_name(dict, LFRFIDProtocolH10301));\n    mu_assert_string_eq(\"HID\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));\n\n    const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;\n\n    protocol_dict_decoders_start(dict);\n\n    ProtocolId protocol = PROTOCOL_NO;\n    PulseGlue* pulse_glue = pulse_glue_alloc();\n\n    for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {\n        bool pulse_pop = pulse_glue_push(\n            pulse_glue,\n            hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0,\n            abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) *\n                LF_RFID_READ_TIMING_MULTIPLIER);\n\n        if(pulse_pop) {\n            uint32_t length, period;\n            pulse_glue_pop(pulse_glue, &length, &period);\n\n            protocol = protocol_dict_decoders_feed(dict, true, period);\n            if(protocol != PROTOCOL_NO) break;\n\n            protocol = protocol_dict_decoders_feed(dict, false, length - period);\n            if(protocol != PROTOCOL_NO) break;\n        }\n    }\n\n    pulse_glue_free(pulse_glue);\n\n    mu_assert_int_eq(LFRFIDProtocolH10301, protocol);\n    uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0};\n    protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE);\n\n    mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE);\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_h10301_emulate_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(\n        HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));\n    mu_assert_string_eq(\"H10301\", protocol_dict_get_name(dict, LFRFIDProtocolH10301));\n    mu_assert_string_eq(\"HID\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));\n\n    const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;\n\n    protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE);\n    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301));\n\n    for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) {\n        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301);\n\n        if(level_duration_get_level(level_duration)) {\n            mu_assert_int_eq(\n                hid10301_test_timings[i], level_duration_get_duration(level_duration));\n        } else {\n            mu_assert_int_eq(\n                hid10301_test_timings[i], -level_duration_get_duration(level_duration));\n        }\n    }\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(\n        IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));\n    mu_assert_string_eq(\"IoProxXSF\", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));\n    mu_assert_string_eq(\"Kantech\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));\n\n    const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;\n\n    protocol_dict_decoders_start(dict);\n\n    ProtocolId protocol = PROTOCOL_NO;\n    PulseGlue* pulse_glue = pulse_glue_alloc();\n\n    for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {\n        bool pulse_pop = pulse_glue_push(\n            pulse_glue,\n            ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0,\n            abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) *\n                LF_RFID_READ_TIMING_MULTIPLIER);\n\n        if(pulse_pop) {\n            uint32_t length, period;\n            pulse_glue_pop(pulse_glue, &length, &period);\n\n            protocol = protocol_dict_decoders_feed(dict, true, period);\n            if(protocol != PROTOCOL_NO) break;\n\n            protocol = protocol_dict_decoders_feed(dict, false, length - period);\n            if(protocol != PROTOCOL_NO) break;\n        }\n    }\n\n    pulse_glue_free(pulse_glue);\n\n    mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol);\n    uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0};\n    protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE);\n\n    mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE);\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(\n        IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));\n    mu_assert_string_eq(\"IoProxXSF\", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));\n    mu_assert_string_eq(\"Kantech\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));\n\n    const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;\n\n    protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE);\n    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF));\n\n    for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) {\n        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF);\n\n        if(level_duration_get_level(level_duration)) {\n            mu_assert_int_eq(\n                ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration));\n        } else {\n            mu_assert_int_eq(\n                ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration));\n        }\n    }\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(\n        INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26));\n    mu_assert_string_eq(\"Indala26\", protocol_dict_get_name(dict, LFRFIDProtocolIndala26));\n    mu_assert_string_eq(\"Motorola\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26));\n\n    const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA;\n\n    protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE);\n    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26));\n\n    for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) {\n        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26);\n\n        if(level_duration_get_level(level_duration)) {\n            mu_assert_int_eq(\n                indala26_test_timings[i], level_duration_get_duration(level_duration));\n        } else {\n            mu_assert_int_eq(\n                indala26_test_timings[i], -level_duration_get_duration(level_duration));\n        }\n    }\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_fdxb_emulate_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB));\n    mu_assert_string_eq(\"FDX-B\", protocol_dict_get_name(dict, LFRFIDProtocolFDXB));\n    mu_assert_string_eq(\"ISO\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB));\n\n    const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA;\n\n    protocol_dict_set_data(dict, LFRFIDProtocolFDXB, data, FDXB_TEST_DATA_SIZE);\n    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolFDXB));\n\n    for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT; i++) {\n        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolFDXB);\n\n        if(level_duration_get_level(level_duration)) {\n            mu_assert_int_eq(fdxb_test_timings[i], level_duration_get_duration(level_duration));\n        } else {\n            mu_assert_int_eq(fdxb_test_timings[i], -level_duration_get_duration(level_duration));\n        }\n    }\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST(test_lfrfid_protocol_fdxb_read_simple) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB));\n    mu_assert_string_eq(\"FDX-B\", protocol_dict_get_name(dict, LFRFIDProtocolFDXB));\n    mu_assert_string_eq(\"ISO\", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB));\n\n    const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA;\n\n    protocol_dict_decoders_start(dict);\n\n    ProtocolId protocol = PROTOCOL_NO;\n    PulseGlue* pulse_glue = pulse_glue_alloc();\n\n    for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {\n        bool pulse_pop = pulse_glue_push(\n            pulse_glue,\n            fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT] >= 0,\n            abs(fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT]) *\n                LF_RFID_READ_TIMING_MULTIPLIER);\n\n        if(pulse_pop) {\n            uint32_t length, period;\n            pulse_glue_pop(pulse_glue, &length, &period);\n\n            protocol = protocol_dict_decoders_feed(dict, true, period);\n            if(protocol != PROTOCOL_NO) break;\n\n            protocol = protocol_dict_decoders_feed(dict, false, length - period);\n            if(protocol != PROTOCOL_NO) break;\n        }\n    }\n\n    pulse_glue_free(pulse_glue);\n\n    mu_assert_int_eq(LFRFIDProtocolFDXB, protocol);\n    uint8_t received_data[FDXB_TEST_DATA_SIZE] = {0};\n    protocol_dict_get_data(dict, protocol, received_data, FDXB_TEST_DATA_SIZE);\n\n    mu_assert_mem_eq(data, received_data, FDXB_TEST_DATA_SIZE);\n\n    protocol_dict_free(dict);\n}\n\nMU_TEST_SUITE(test_lfrfid_protocols_suite) {\n    MU_RUN_TEST(test_lfrfid_protocol_em_read_simple);\n    MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple);\n\n    MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple);\n    MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple);\n\n    MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple);\n    MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple);\n\n    MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple);\n\n    MU_RUN_TEST(test_lfrfid_protocol_fdxb_read_simple);\n    MU_RUN_TEST(test_lfrfid_protocol_fdxb_emulate_simple);\n}\n\nint run_minunit_test_lfrfid_protocols(void) {\n    MU_RUN_SUITE(test_lfrfid_protocols_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_lfrfid_protocols)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/manifest/manifest.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n#include <update_util/resources/manifest.h>\n\n#define TAG \"Manifest\"\n\nMU_TEST(manifest_type_test) {\n    mu_assert(ResourceManifestEntryTypeUnknown == 0, \"ResourceManifestEntryTypeUnknown != 0\\r\\n\");\n    mu_assert(ResourceManifestEntryTypeVersion == 1, \"ResourceManifestEntryTypeVersion != 1\\r\\n\");\n    mu_assert(\n        ResourceManifestEntryTypeTimestamp == 2, \"ResourceManifestEntryTypeTimestamp != 2\\r\\n\");\n    mu_assert(\n        ResourceManifestEntryTypeDirectory == 3, \"ResourceManifestEntryTypeDirectory != 3\\r\\n\");\n    mu_assert(ResourceManifestEntryTypeFile == 4, \"ResourceManifestEntryTypeFile != 4\\r\\n\");\n}\n\nMU_TEST(manifest_iteration_test) {\n    bool result = true;\n    size_t counters[5] = {0};\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage);\n    do {\n        // Open manifest file\n        if(!resource_manifest_reader_open(manifest_reader, EXT_PATH(\"unit_tests/Manifest_test\"))) {\n            result = false;\n            break;\n        }\n\n        // Iterate forward\n        ResourceManifestEntry* entry_ptr = NULL;\n        while((entry_ptr = resource_manifest_reader_next(manifest_reader))) {\n            FURI_LOG_D(TAG, \"F:%u:%s\", entry_ptr->type, furi_string_get_cstr(entry_ptr->name));\n            if(entry_ptr->type > 4) {\n                mu_fail(\"entry_ptr->type > 4\\r\\n\");\n                result = false;\n                break;\n            }\n            counters[entry_ptr->type]++;\n        }\n        if(!result) break;\n\n        // Iterate backward\n        while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) {\n            FURI_LOG_D(TAG, \"B:%u:%s\", entry_ptr->type, furi_string_get_cstr(entry_ptr->name));\n            if(entry_ptr->type > 4) {\n                mu_fail(\"entry_ptr->type > 4\\r\\n\");\n                result = false;\n                break;\n            }\n            counters[entry_ptr->type]--;\n        }\n    } while(false);\n\n    resource_manifest_reader_free(manifest_reader);\n    furi_record_close(RECORD_STORAGE);\n\n    mu_assert(counters[ResourceManifestEntryTypeUnknown] == 0, \"Unknown counter != 0\\r\\n\");\n    mu_assert(counters[ResourceManifestEntryTypeVersion] == 0, \"Version counter != 0\\r\\n\");\n    mu_assert(counters[ResourceManifestEntryTypeTimestamp] == 0, \"Timestamp counter != 0\\r\\n\");\n    mu_assert(counters[ResourceManifestEntryTypeDirectory] == 0, \"Directory counter != 0\\r\\n\");\n    mu_assert(counters[ResourceManifestEntryTypeFile] == 0, \"File counter != 0\\r\\n\");\n\n    mu_assert(result, \"Manifest forward iterate failed\\r\\n\");\n}\n\nMU_TEST_SUITE(manifest_suite) {\n    MU_RUN_TEST(manifest_type_test);\n    MU_RUN_TEST(manifest_iteration_test);\n}\n\nint run_minunit_test_manifest(void) {\n    MU_RUN_SUITE(manifest_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_manifest)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/minunit.h",
    "content": "/*\n * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef MINUNIT_MINUNIT_H\n#define MINUNIT_MINUNIT_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(_WIN32)\n#include <Windows.h>\n#if defined(_MSC_VER) && _MSC_VER < 1900\n#define snprintf _snprintf\n#define __func__ __FUNCTION__ //-V1059\n#endif\n\n#elif defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n\n/* Change POSIX C SOURCE version for pure c99 compilers */\n#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L\n#undef _POSIX_C_SOURCE\n#define _POSIX_C_SOURCE 200112L\n#endif\n\n#include <unistd.h> /* POSIX flags */\n#include <time.h> /* clock_gettime(), time() */\n#include <sys/time.h> /* gethrtime(), gettimeofday() */\n#include <sys/resource.h>\n#include <sys/times.h>\n#include <string.h>\n\n#if defined(__MACH__) && defined(__APPLE__)\n#include <mach/mach.h>\n#include <mach/mach_time.h>\n#endif\n\n#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)\n#define __func__ __extension__ __FUNCTION__ //-V1059\n#endif\n\n#else\n\n// #error \"Unable to define timers for an unknown OS.\"\n\n#endif\n\n#include <stdio.h>\n#include <math.h>\n\n/*  Maximum length of last message */\n#define MINUNIT_MESSAGE_LEN 1024\n/*  Accuracy with which floats are compared */\n#define MINUNIT_EPSILON     1E-12\n\n#include \"minunit_vars_ex.h\"\n\n/*  Test setup and teardown function pointers */\n__attribute__((unused)) static void (*minunit_setup)(void) = NULL;\n__attribute__((unused)) static void (*minunit_teardown)(void) = NULL;\n\nvoid minunit_print_progress(void);\nvoid minunit_print_fail(const char* error);\nvoid minunit_printf_warning(const char* format, ...);\n\n/*  Definitions */\n#define MU_TEST(method_name)          static void method_name(void)\n#define MU_TEST_1(method_name, arg_1) static void method_name(arg_1)\n#define MU_TEST_SUITE(suite_name)     static void suite_name(void)\n\n#define MU__SAFE_BLOCK(block) \\\n    do {                      \\\n        block                 \\\n    } while(0)\n\n/*  Run test suite and unset setup and teardown functions */\n#define MU_RUN_SUITE(suite_name) \\\n    MU__SAFE_BLOCK(suite_name(); minunit_setup = NULL; minunit_teardown = NULL;)\n\n/*  Configure setup and teardown functions */\n#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) \\\n    MU__SAFE_BLOCK(minunit_setup = setup_fun; minunit_teardown = teardown_fun;)\n\n/*  Test runner */\n//-V:MU_RUN_TEST:550\n#define MU_RUN_TEST(test)                                        \\\n    MU__SAFE_BLOCK(                                              \\\n        if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \\\n            minunit_real_timer = mu_timer_real();                \\\n            minunit_proc_timer = mu_timer_cpu();                 \\\n        } if(minunit_setup) (*minunit_setup)();                  \\\n        minunit_status = 0;                                      \\\n        printf(#test \"()\\r\\n\");                                  \\\n        test();                                                  \\\n        minunit_run++;                                           \\\n        if(minunit_status) {                                     \\\n            minunit_fail++;                                      \\\n            minunit_print_fail(minunit_last_message);            \\\n            minunit_status = 0;                                  \\\n        } fflush(stdout);                                        \\\n        if(minunit_teardown)(*minunit_teardown)();)\n\n#define MU_RUN_TEST_1(test, arg_1)                               \\\n    MU__SAFE_BLOCK(                                              \\\n        if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \\\n            minunit_real_timer = mu_timer_real();                \\\n            minunit_proc_timer = mu_timer_cpu();                 \\\n        } if(minunit_setup) (*minunit_setup)();                  \\\n        minunit_status = 0;                                      \\\n        printf(#test \"(\" #arg_1 \")\\r\\n\");                        \\\n        test(arg_1);                                             \\\n        minunit_run++;                                           \\\n        if(minunit_status) {                                     \\\n            minunit_fail++;                                      \\\n            minunit_print_fail(minunit_last_message);            \\\n            minunit_status = 0;                                  \\\n        } fflush(stdout);                                        \\\n        if(minunit_teardown)(*minunit_teardown)();)\n\n/*  Report */\n#define MU_REPORT()                                                                      \\\n    MU__SAFE_BLOCK(double minunit_end_real_timer; double minunit_end_proc_timer; printf( \\\n                       \"\\n\\n%d tests, %d assertions, %d failures\\n\",                     \\\n                       minunit_run,                                                      \\\n                       minunit_assert,                                                   \\\n                       minunit_fail);                                                    \\\n                   minunit_end_real_timer = mu_timer_real();                             \\\n                   minunit_end_proc_timer = mu_timer_cpu();                              \\\n                   printf(                                                               \\\n                       \"\\nFinished in %.8f seconds (real) %.8f seconds (proc)\\n\\n\",      \\\n                       minunit_end_real_timer - minunit_real_timer,                      \\\n                       minunit_end_proc_timer - minunit_proc_timer);)\n#define MU_EXIT_CODE minunit_fail\n\n/* Warnings */\n#define mu_warn(message) \\\n    MU__SAFE_BLOCK(minunit_printf_warning(\"%s:%d: %s\", __FILE__, __LINE__, message);)\n\n/*  Assertions */\n#define mu_check(test)                       \\\n    MU__SAFE_BLOCK(                          \\\n        minunit_assert++; if(!(test)) {      \\\n            snprintf(                        \\\n                minunit_last_message,        \\\n                MINUNIT_MESSAGE_LEN,         \\\n                \"%s failed:\\r\\n\\t%s:%d: %s\", \\\n                __func__,                    \\\n                __FILE__,                    \\\n                __LINE__,                    \\\n                #test);                      \\\n            minunit_status = 1;              \\\n            return;                          \\\n        } else { minunit_print_progress(); })\n\n#define mu_fail(message)                            \\\n    MU__SAFE_BLOCK(minunit_assert++; snprintf(      \\\n                       minunit_last_message,        \\\n                       MINUNIT_MESSAGE_LEN,         \\\n                       \"%s failed:\\r\\n\\t%s:%d: %s\", \\\n                       __func__,                    \\\n                       __FILE__,                    \\\n                       __LINE__,                    \\\n                       message);                    \\\n                   minunit_status = 1;              \\\n                   return;)\n\n#define mu_assert(test, message)             \\\n    MU__SAFE_BLOCK(                          \\\n        minunit_assert++; if(!(test)) {      \\\n            snprintf(                        \\\n                minunit_last_message,        \\\n                MINUNIT_MESSAGE_LEN,         \\\n                \"%s failed:\\r\\n\\t%s:%d: %s\", \\\n                __func__,                    \\\n                __FILE__,                    \\\n                __LINE__,                    \\\n                message);                    \\\n            minunit_status = 1;              \\\n            return;                          \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_eq(expected, result)                                                  \\\n    MU__SAFE_BLOCK(                                                                         \\\n        int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \\\n        minunit_tmp_r = (result);                                                           \\\n        if(minunit_tmp_e != minunit_tmp_r) {                                                \\\n            snprintf(                                                                       \\\n                minunit_last_message,                                                       \\\n                MINUNIT_MESSAGE_LEN,                                                        \\\n                \"%s failed:\\r\\n\\t%s:%d: %d expected but was %d\",                            \\\n                __func__,                                                                   \\\n                __FILE__,                                                                   \\\n                __LINE__,                                                                   \\\n                minunit_tmp_e,                                                              \\\n                minunit_tmp_r);                                                             \\\n            minunit_status = 1;                                                             \\\n            return;                                                                         \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_not_eq(expected, result)                                              \\\n    MU__SAFE_BLOCK(                                                                         \\\n        int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \\\n        minunit_tmp_r = (result);                                                           \\\n        if(minunit_tmp_e == minunit_tmp_r) {                                                \\\n            snprintf(                                                                       \\\n                minunit_last_message,                                                       \\\n                MINUNIT_MESSAGE_LEN,                                                        \\\n                \"%s failed:\\r\\n\\t%s:%d: expected different results but both were %d\",       \\\n                __func__,                                                                   \\\n                __FILE__,                                                                   \\\n                __LINE__,                                                                   \\\n                minunit_tmp_e);                                                             \\\n            minunit_status = 1;                                                             \\\n            return;                                                                         \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_greater_than(val, result)                                        \\\n    MU__SAFE_BLOCK(                                                                    \\\n        int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \\\n        minunit_tmp_r = (result);                                                      \\\n        if(val >= minunit_tmp_r) {                                                     \\\n            snprintf(                                                                  \\\n                minunit_last_message,                                                  \\\n                MINUNIT_MESSAGE_LEN,                                                   \\\n                \"%s failed:\\r\\n\\t%s:%d: %d <= %d\",                                     \\\n                __func__,                                                              \\\n                __FILE__,                                                              \\\n                __LINE__,                                                              \\\n                minunit_tmp_r,                                                         \\\n                minunit_tmp_e);                                                        \\\n            minunit_status = 1;                                                        \\\n            return;                                                                    \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_less_than(val, result)                                           \\\n    MU__SAFE_BLOCK(                                                                    \\\n        int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \\\n        minunit_tmp_r = (result);                                                      \\\n        if(val <= minunit_tmp_r) {                                                     \\\n            snprintf(                                                                  \\\n                minunit_last_message,                                                  \\\n                MINUNIT_MESSAGE_LEN,                                                   \\\n                \"%s failed:\\r\\n\\t%s:%d: %d >= %d\",                                     \\\n                __func__,                                                              \\\n                __FILE__,                                                              \\\n                __LINE__,                                                              \\\n                minunit_tmp_r,                                                         \\\n                minunit_tmp_e);                                                        \\\n            minunit_status = 1;                                                        \\\n            return;                                                                    \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_between(expected_lower, expected_upper, result)              \\\n    MU__SAFE_BLOCK(                                                                \\\n        int minunit_tmp_e; int minunit_tmp_m; int minunit_tmp_r; minunit_assert++; \\\n        minunit_tmp_e = (expected_lower);                                          \\\n        minunit_tmp_m = (expected_upper);                                          \\\n        minunit_tmp_r = (result);                                                  \\\n        if(result < minunit_tmp_e || result > minunit_tmp_m) {                     \\\n            snprintf(                                                              \\\n                minunit_last_message,                                              \\\n                MINUNIT_MESSAGE_LEN,                                               \\\n                \"%s failed:\\r\\n\\t%s:%d: %d was not between (inclusive) %d and %d\", \\\n                __func__,                                                          \\\n                __FILE__,                                                          \\\n                __LINE__,                                                          \\\n                minunit_tmp_e,                                                     \\\n                minunit_tmp_r,                                                     \\\n                minunit_tmp_m);                                                    \\\n            minunit_status = 1;                                                    \\\n            return;                                                                \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_int_in(expected, array_length, result)                                 \\\n    MU__SAFE_BLOCK(                                                                      \\\n        int minunit_tmp_r; minunit_assert++; minunit_tmp_r = (result); int t = 0; int i; \\\n        for(i = 0; i < array_length; i++) {                                              \\\n            if(expected[i] == minunit_tmp_r) t = 1;                                      \\\n        } if(t == 0) {                                                                   \\\n            char tmp[500] = {0};                                                         \\\n            tmp[0] = '[';                                                                \\\n            for(i = 0; i < array_length; i++) {                                          \\\n                sprintf(tmp + strlen(tmp), \"%d, \", expected[i]);                         \\\n            }                                                                            \\\n            int len = strlen(tmp);                                                       \\\n            tmp[len - 2] = ']';                                                          \\\n            tmp[len - 1] = '\\0';                                                         \\\n            snprintf(                                                                    \\\n                minunit_last_message,                                                    \\\n                MINUNIT_MESSAGE_LEN,                                                     \\\n                \"%s failed:\\r\\n\\t%s:%d: expected to be one of %s but was %d\",            \\\n                __func__,                                                                \\\n                __FILE__,                                                                \\\n                __LINE__,                                                                \\\n                tmp,                                                                     \\\n                minunit_tmp_r);                                                          \\\n            minunit_status = 1;                                                          \\\n            return;                                                                      \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_double_eq(expected, result)                                                     \\\n    MU__SAFE_BLOCK(                                                                               \\\n        double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \\\n        minunit_tmp_r = (result);                                                                 \\\n        if(fabs(minunit_tmp_e - minunit_tmp_r) > (double)MINUNIT_EPSILON) {                       \\\n            int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON);                         \\\n            snprintf(                                                                             \\\n                minunit_last_message,                                                             \\\n                MINUNIT_MESSAGE_LEN,                                                              \\\n                \"%s failed:\\r\\n\\t%s:%d: %.*g expected but was %.*g\",                              \\\n                __func__,                                                                         \\\n                __FILE__,                                                                         \\\n                __LINE__,                                                                         \\\n                minunit_significant_figures,                                                      \\\n                minunit_tmp_e,                                                                    \\\n                minunit_significant_figures,                                                      \\\n                minunit_tmp_r);                                                                   \\\n            minunit_status = 1;                                                                   \\\n            return;                                                                               \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_double_greater_than(val, result)                                           \\\n    MU__SAFE_BLOCK(                                                                          \\\n        double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \\\n        minunit_tmp_r = (result);                                                            \\\n        if(val >= minunit_tmp_r) {                                                           \\\n            snprintf(                                                                        \\\n                minunit_last_message,                                                        \\\n                MINUNIT_MESSAGE_LEN,                                                         \\\n                \"%s failed:\\r\\n\\t%s:%d: %f <= %f\",                                           \\\n                __func__,                                                                    \\\n                __FILE__,                                                                    \\\n                __LINE__,                                                                    \\\n                minunit_tmp_r,                                                               \\\n                minunit_tmp_e);                                                              \\\n            minunit_status = 1;                                                              \\\n            return;                                                                          \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_double_less_than(val, result)                                              \\\n    MU__SAFE_BLOCK(                                                                          \\\n        double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \\\n        minunit_tmp_r = (result);                                                            \\\n        if(val <= minunit_tmp_r) {                                                           \\\n            snprintf(                                                                        \\\n                minunit_last_message,                                                        \\\n                MINUNIT_MESSAGE_LEN,                                                         \\\n                \"%s failed:\\r\\n\\t%s:%d: %f >= %f\",                                           \\\n                __func__,                                                                    \\\n                __FILE__,                                                                    \\\n                __LINE__,                                                                    \\\n                minunit_tmp_r,                                                               \\\n                minunit_tmp_e);                                                              \\\n            minunit_status = 1;                                                              \\\n            return;                                                                          \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_double_between(expected_lower, expected_upper, result)                    \\\n    MU__SAFE_BLOCK(                                                                         \\\n        double minunit_tmp_e; double minunit_tmp_m; double minunit_tmp_r; minunit_assert++; \\\n        minunit_tmp_e = (expected_lower);                                                   \\\n        minunit_tmp_m = (expected_upper);                                                   \\\n        minunit_tmp_r = (result);                                                           \\\n        if(result < minunit_tmp_e || result > minunit_tmp_m) {                              \\\n            snprintf(                                                                       \\\n                minunit_last_message,                                                       \\\n                MINUNIT_MESSAGE_LEN,                                                        \\\n                \"%s failed:\\r\\n\\t%s:%d: %f was not between (inclusive) %f and %f\",          \\\n                __func__,                                                                   \\\n                __FILE__,                                                                   \\\n                __LINE__,                                                                   \\\n                minunit_tmp_r,                                                              \\\n                minunit_tmp_e,                                                              \\\n                minunit_tmp_m);                                                             \\\n            minunit_status = 1;                                                             \\\n            return;                                                                         \\\n        } else { minunit_print_progress(); })\n\n//-V:mu_assert_string_eq:526, 547\n\n#define mu_assert_string_eq(expected, result)                                         \\\n    MU__SAFE_BLOCK(                                                                   \\\n        const char* minunit_tmp_e = expected; const char* minunit_tmp_r = result;     \\\n        minunit_assert++;                                                             \\\n        if(!minunit_tmp_e) { minunit_tmp_e = \"<null pointer>\"; } if(!minunit_tmp_r) { \\\n            minunit_tmp_r = \"<null pointer>\";                                         \\\n        } if(strcmp(minunit_tmp_e, minunit_tmp_r)) {                                  \\\n            snprintf(                                                                 \\\n                minunit_last_message,                                                 \\\n                MINUNIT_MESSAGE_LEN,                                                  \\\n                \"%s failed:\\r\\n\\t%s:%d: '%s' expected but was '%s'\",                  \\\n                __func__,                                                             \\\n                __FILE__,                                                             \\\n                __LINE__,                                                             \\\n                minunit_tmp_e,                                                        \\\n                minunit_tmp_r);                                                       \\\n            minunit_status = 1;                                                       \\\n            return;                                                                   \\\n        } else { minunit_print_progress(); })\n\n//-V:mu_assert_mem_eq:526\n\n#define mu_assert_mem_eq(expected, result, size)                                   \\\n    MU__SAFE_BLOCK(                                                                \\\n        const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result;  \\\n        minunit_assert++;                                                          \\\n        if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) {                           \\\n            snprintf(                                                              \\\n                minunit_last_message,                                              \\\n                MINUNIT_MESSAGE_LEN,                                               \\\n                \"%s failed:\\r\\n\\t%s:%d: mem not equal\\r\\n\\tEXP  RES\",              \\\n                __func__,                                                          \\\n                __FILE__,                                                          \\\n                __LINE__);                                                         \\\n            for(size_t __index = 0; __index < size; __index++) {                   \\\n                if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \\\n                uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index];                  \\\n                uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index];                  \\\n                snprintf(                                                          \\\n                    minunit_last_message + strlen(minunit_last_message),           \\\n                    MINUNIT_MESSAGE_LEN - strlen(minunit_last_message),            \\\n                    \"\\r\\n\\t%02X %s %02X\",                                          \\\n                    __e,                                                           \\\n                    ((__e == __r) ? \"..\" : \"!=\"),                                  \\\n                    __r);                                                          \\\n            }                                                                      \\\n            minunit_status = 1;                                                    \\\n            return;                                                                \\\n        } else { minunit_print_progress(); })\n\n#define mu_assert_null(result)                                                    \\\n    MU__SAFE_BLOCK(                                                               \\\n        minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \\\n            snprintf(                                                             \\\n                minunit_last_message,                                             \\\n                MINUNIT_MESSAGE_LEN,                                              \\\n                \"%s failed:\\r\\n\\t%s:%d: Expected result was not NULL\",            \\\n                __func__,                                                         \\\n                __FILE__,                                                         \\\n                __LINE__);                                                        \\\n            minunit_status = 1;                                                   \\\n            return;                                                               \\\n        })\n\n#define mu_assert_not_null(result)                                                \\\n    MU__SAFE_BLOCK(                                                               \\\n        minunit_assert++; if(result != NULL) { minunit_print_progress(); } else { \\\n            snprintf(                                                             \\\n                minunit_last_message,                                             \\\n                MINUNIT_MESSAGE_LEN,                                              \\\n                \"%s failed:\\r\\n\\t%s:%d: Expected result was not NULL\",            \\\n                __func__,                                                         \\\n                __FILE__,                                                         \\\n                __LINE__);                                                        \\\n            minunit_status = 1;                                                   \\\n            return;                                                               \\\n        })\n\n#define mu_assert_pointers_eq(pointer1, pointer2)                                                    \\\n    MU__SAFE_BLOCK(                                                                                  \\\n        minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else {              \\\n            snprintf(                                                                                \\\n                minunit_last_message,                                                                \\\n                MINUNIT_MESSAGE_LEN,                                                                 \\\n                \"%s failed:\\r\\n\\t%s:%d: Expected the pointers to point to the same memory location\", \\\n                __func__,                                                                            \\\n                __FILE__,                                                                            \\\n                __LINE__);                                                                           \\\n            minunit_status = 1;                                                                      \\\n            return;                                                                                  \\\n        })\n\n#define mu_assert_pointers_not_eq(pointer1, pointer2)                                                \\\n    MU__SAFE_BLOCK(                                                                                  \\\n        minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else {              \\\n            snprintf(                                                                                \\\n                minunit_last_message,                                                                \\\n                MINUNIT_MESSAGE_LEN,                                                                 \\\n                \"%s failed:\\r\\n\\t%s:%d: Expected the pointers to point to the same memory location\", \\\n                __func__,                                                                            \\\n                __FILE__,                                                                            \\\n                __LINE__);                                                                           \\\n            minunit_status = 1;                                                                      \\\n            return;                                                                                  \\\n        })\n\n/*\n * The following two functions were written by David Robert Nadeau\n * from http://NadeauSoftware.com/ and distributed under the\n * Creative Commons Attribution 3.0 Unported License\n */\n\n/**\n * Returns the real time, in seconds, or -1.0 if an error occurred.\n *\n * Time is measured since an arbitrary and OS-dependent start time.\n * The returned real time is only useful for computing an elapsed time\n * between two calls to this function.\n */\n__attribute__((unused)) static double mu_timer_real(void) {\n#if defined(_WIN32)\n    /* Windows 2000 and later. ---------------------------------- */\n    LARGE_INTEGER Time;\n    LARGE_INTEGER Frequency;\n\n    QueryPerformanceFrequency(&Frequency);\n    QueryPerformanceCounter(&Time);\n\n    Time.QuadPart *= 1000000;\n    Time.QuadPart /= Frequency.QuadPart;\n\n    return (double)Time.QuadPart / 1000000.0;\n\n#elif(defined(__hpux) || defined(hpux)) ||                   \\\n    ((defined(__sun__) || defined(__sun) || defined(sun)) && \\\n     (defined(__SVR4) || defined(__svr4__)))\n    /* HP-UX, Solaris. ------------------------------------------ */\n    return (double)gethrtime() / 1000000000.0;\n\n#elif defined(__MACH__) && defined(__APPLE__)\n    /* OSX. ----------------------------------------------------- */\n    static double timeConvert = 0.0;\n    if(timeConvert == 0.0) {\n        mach_timebase_info_data_t timeBase;\n        (void)mach_timebase_info(&timeBase);\n        timeConvert = (double)timeBase.numer / (double)timeBase.denom / 1000000000.0;\n    }\n    return (double)mach_absolute_time() * timeConvert;\n\n#elif defined(_POSIX_VERSION)\n    /* POSIX. --------------------------------------------------- */\n    struct timeval tm;\n#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)\n    {\n        struct timespec ts;\n#if defined(CLOCK_MONOTONIC_PRECISE)\n        /* BSD. --------------------------------------------- */\n        const clockid_t id = CLOCK_MONOTONIC_PRECISE;\n#elif defined(CLOCK_MONOTONIC_RAW)\n        /* Linux. ------------------------------------------- */\n        const clockid_t id = CLOCK_MONOTONIC_RAW;\n#elif defined(CLOCK_HIGHRES)\n        /* Solaris. ----------------------------------------- */\n        const clockid_t id = CLOCK_HIGHRES;\n#elif defined(CLOCK_MONOTONIC)\n        /* AIX, BSD, Linux, POSIX, Solaris. ----------------- */\n        const clockid_t id = CLOCK_MONOTONIC;\n#elif defined(CLOCK_REALTIME)\n        /* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */\n        const clockid_t id = CLOCK_REALTIME;\n#else\n        const clockid_t id = (clockid_t)-1; /* Unknown. */\n#endif /* CLOCK_* */\n        if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)\n            return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;\n        /* Fall thru. */\n    }\n#endif /* _POSIX_TIMERS */\n\n    /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */\n    gettimeofday(&tm, NULL);\n    return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0;\n#else\n    return -1.0; /* Failed. */\n#endif\n}\n\n/**\n * Returns the amount of CPU time used by the current process,\n * in seconds, or -1.0 if an error occurred.\n */\n__attribute__((unused)) static double mu_timer_cpu(void) {\n#if defined(_WIN32)\n    /* Windows -------------------------------------------------- */\n    FILETIME createTime;\n    FILETIME exitTime;\n    FILETIME kernelTime;\n    FILETIME userTime;\n\n    /* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */\n    if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime) != 0) {\n        ULARGE_INTEGER userSystemTime;\n        memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER));\n        return (double)userSystemTime.QuadPart / 10000000.0;\n    }\n\n#elif defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n    /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */\n\n#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)\n    /* Prefer high-res POSIX timers, when available. */\n    {\n        clockid_t id;\n        struct timespec ts;\n#if _POSIX_CPUTIME > 0\n        /* Clock ids vary by OS.  Query the id, if possible. */\n        if(clock_getcpuclockid(0, &id) == -1)\n#endif\n#if defined(CLOCK_PROCESS_CPUTIME_ID)\n            /* Use known clock id for AIX, Linux, or Solaris. */\n            id = CLOCK_PROCESS_CPUTIME_ID;\n#elif defined(CLOCK_VIRTUAL)\n        /* Use known clock id for BSD or HP-UX. */\n        id = CLOCK_VIRTUAL;\n#else\n        id = (clockid_t)-1;\n#endif\n        if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)\n            return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;\n    }\n#endif\n\n#if defined(RUSAGE_SELF)\n    {\n        struct rusage rusage;\n        if(getrusage(RUSAGE_SELF, &rusage) != -1)\n            return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0;\n    }\n#endif\n\n#if defined(_SC_CLK_TCK)\n    {\n        const double ticks = (double)sysconf(_SC_CLK_TCK);\n        struct tms tms;\n        if(times(&tms) != (clock_t)-1) return (double)tms.tms_utime / ticks;\n    }\n#endif\n\n#if defined(CLOCKS_PER_SEC)\n    {\n        clock_t cl = clock();\n        if(cl != (clock_t)-1) return (double)cl / (double)CLOCKS_PER_SEC;\n    }\n#endif\n\n#endif\n\n    return -1; /* Failed. */\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* MINUNIT_MINUNIT_H */\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/minunit_vars.h",
    "content": "#pragma once\n#include \"minunit.h\"\n\n/*  Misc. counters */\nint minunit_run = 0;\nint minunit_assert = 0;\nint minunit_fail = 0;\nint minunit_status = 0;\n\n/*  Timers */\ndouble minunit_real_timer = 0;\ndouble minunit_proc_timer = 0;\n\n/*  Last message */\nchar minunit_last_message[MINUNIT_MESSAGE_LEN];\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/minunit_vars_ex.h",
    "content": "#pragma once\n#include \"minunit.h\"\n\n/*  Misc. counters */\nextern int minunit_run;\nextern int minunit_assert;\nextern int minunit_fail;\nextern int minunit_status;\n\n/*  Timers */\nextern double minunit_real_timer;\nextern double minunit_proc_timer;\n\n/*  Last message */\nextern char minunit_last_message[MINUNIT_MESSAGE_LEN];\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/nfc/nfc_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n\n#include <nfc/nfc_device.h>\n#include <nfc/helpers/nfc_data_generator.h>\n#include <nfc/nfc_poller.h>\n#include <nfc/nfc_listener.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include <nfc/protocols/felica/felica.h>\n#include <nfc/protocols/felica/felica_poller_sync.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>\n#include <nfc/protocols/slix/slix.h>\n#include <nfc/protocols/slix/slix_i.h>\n#include <nfc/protocols/slix/slix_poller.h>\n#include <nfc/protocols/slix/slix_poller_i.h>\n\n#include <nfc/nfc_poller.h>\n\n#include <toolbox/keys_dict.h>\n#include <nfc/nfc.h>\n\n#include \"../test.h\" // IWYU pragma: keep\n\n#define TAG \"NfcTest\"\n\n#define NFC_TEST_NFC_DEV_PATH                  EXT_PATH(\"unit_tests/nfc/nfc_device_test.nfc\")\n#define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH(\"unit_tests/mf_dict.nfc\")\n\n#define NFC_TEST_FLAG_WORKER_DONE (1)\n\ntypedef enum {\n    NfcTestMfClassicSendFrameTestStateAuth,\n    NfcTestMfClassicSendFrameTestStateReadBlock,\n\n    NfcTestMfClassicSendFrameTestStateFail,\n    NfcTestMfClassicSendFrameTestStateSuccess,\n} NfcTestMfClassicSendFrameTestState;\n\ntypedef struct {\n    NfcTestMfClassicSendFrameTestState state;\n    BitBuffer* tx_buf;\n    BitBuffer* rx_buf;\n    FuriThreadId thread_id;\n} NfcTestMfClassicSendFrameTest;\n\ntypedef enum {\n    NfcTestSlixPollerSetPasswordStateGetRandomNumber,\n    NfcTestSlixPollerSetPasswordStateSetPassword,\n} NfcTestSlixPollerSetPasswordState;\n\ntypedef struct {\n    FuriThreadId thread_id;\n    NfcTestSlixPollerSetPasswordState state;\n    SlixRandomNumber random_number;\n    SlixPassword password;\n    SlixError error;\n} NfcTestSlixPollerSetPasswordContext;\n\ntypedef struct {\n    Storage* storage;\n} NfcTest;\n\nstatic NfcTest* nfc_test = NULL;\n\nstatic void nfc_test_alloc(void) {\n    nfc_test = malloc(sizeof(NfcTest));\n    nfc_test->storage = furi_record_open(RECORD_STORAGE);\n}\n\nstatic void nfc_test_free(void) {\n    furi_check(nfc_test);\n\n    furi_record_close(RECORD_STORAGE);\n    free(nfc_test);\n    nfc_test = NULL;\n}\n\nstatic void nfc_test_save_and_load(NfcDevice* nfc_device_ref) {\n    NfcDevice* nfc_device_dut = nfc_device_alloc();\n\n    mu_assert(\n        nfc_device_save(nfc_device_ref, NFC_TEST_NFC_DEV_PATH), \"nfc_device_save() failed\\r\\n\");\n\n    mu_assert(\n        nfc_device_load(nfc_device_dut, NFC_TEST_NFC_DEV_PATH), \"nfc_device_load() failed\\r\\n\");\n\n    mu_assert(\n        nfc_device_is_equal(nfc_device_ref, nfc_device_dut),\n        \"nfc_device_data_dut != nfc_device_data_ref\\r\\n\");\n\n    mu_assert(\n        storage_simply_remove(nfc_test->storage, NFC_TEST_NFC_DEV_PATH),\n        \"storage_simply_remove() failed\\r\\n\");\n\n    nfc_device_free(nfc_device_dut);\n}\n\nstatic void iso14443_3a_file_test(uint8_t uid_len) {\n    NfcDevice* nfc_device = nfc_device_alloc();\n\n    Iso14443_3aData* data = iso14443_3a_alloc();\n    data->uid_len = uid_len;\n    furi_hal_random_fill_buf(data->uid, uid_len);\n    furi_hal_random_fill_buf(data->atqa, sizeof(data->atqa));\n    furi_hal_random_fill_buf(&data->sak, 1);\n\n    nfc_device_set_data(nfc_device, NfcProtocolIso14443_3a, data);\n    nfc_test_save_and_load(nfc_device);\n\n    iso14443_3a_free(data);\n    nfc_device_free(nfc_device);\n}\n\nstatic void nfc_file_test_with_generator(NfcDataGeneratorType type) {\n    NfcDevice* nfc_device_ref = nfc_device_alloc();\n\n    nfc_data_generator_fill_data(type, nfc_device_ref);\n    nfc_test_save_and_load(nfc_device_ref);\n\n    nfc_device_free(nfc_device_ref);\n}\n\nMU_TEST(iso14443_3a_4b_file_test) {\n    iso14443_3a_file_test(4);\n}\n\nMU_TEST(iso14443_3a_7b_file_test) {\n    iso14443_3a_file_test(7);\n}\n\nMU_TEST(mf_ultralight_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralight);\n}\n\nMU_TEST(mf_ultralight_ev1_11_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_11);\n}\n\nMU_TEST(mf_ultralight_ev1_h11_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H11);\n}\n\nMU_TEST(mf_ultralight_ev1_21_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_21);\n}\n\nMU_TEST(mf_ultralight_ev1_h21_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H21);\n}\n\nMU_TEST(mf_ultralight_ntag_203_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG203);\n}\n\nMU_TEST(mf_ultralight_ntag_213_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG213);\n}\n\nMU_TEST(mf_ultralight_ntag_215_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG215);\n}\n\nMU_TEST(mf_ultralight_ntag_216_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG216);\n}\n\nMU_TEST(mf_ultralight_ntag_i2c_1k_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C1k);\n}\n\nMU_TEST(mf_ultralight_ntag_i2c_2k_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C2k);\n}\n\nMU_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus1k);\n}\n\nMU_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus2k);\n}\n\nMU_TEST(mf_classic_mini_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassicMini);\n}\n\nMU_TEST(mf_classic_1k_4b_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_4b);\n}\n\nMU_TEST(mf_classic_1k_7b_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_7b);\n}\n\nMU_TEST(mf_classic_4k_4b_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_4b);\n}\n\nMU_TEST(mf_classic_4k_7b_file_test) {\n    nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_7b);\n}\n\nMU_TEST(iso14443_3a_reader) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    Iso14443_3aData iso14443_3a_listener_data = {\n        .uid_len = 7,\n        .uid = {0x04, 0x51, 0x5C, 0xFA, 0x6F, 0x73, 0x81},\n        .atqa = {0x44, 0x00},\n        .sak = 0x00,\n    };\n    NfcListener* iso3_listener =\n        nfc_listener_alloc(listener, NfcProtocolIso14443_3a, &iso14443_3a_listener_data);\n    nfc_listener_start(iso3_listener, NULL, NULL);\n\n    Iso14443_3aData iso14443_3a_poller_data = {};\n    mu_assert(\n        iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,\n        \"iso14443_3a_poller_sync_read() failed\");\n\n    nfc_listener_stop(iso3_listener);\n    mu_assert(\n        iso14443_3a_is_equal(&iso14443_3a_poller_data, &iso14443_3a_listener_data),\n        \"Data not matches\");\n\n    nfc_listener_free(iso3_listener);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nstatic void mf_ultralight_reader_test(const char* path) {\n    FURI_LOG_I(TAG, \"Testing file: %s\", path);\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    mu_assert(nfc_device_load(nfc_device, path), \"nfc_device_load() failed\\r\\n\");\n\n    MfUltralightData* data =\n        (MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);\n\n    uint32_t features = mf_ultralight_get_feature_support_set(data->type);\n    bool pwd_supported =\n        mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth);\n    uint8_t pwd_num = mf_ultralight_get_pwd_page_num(data->type);\n    const uint8_t zero_pwd[4] = {0, 0, 0, 0};\n\n    if(pwd_supported && !memcmp(data->page[pwd_num].data, zero_pwd, sizeof(zero_pwd))) {\n        data->pages_read -= 2;\n    }\n\n    NfcListener* mfu_listener = nfc_listener_alloc(listener, NfcProtocolMfUltralight, data);\n\n    nfc_listener_start(mfu_listener, NULL, NULL);\n\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n    MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data, NULL);\n    mu_assert(error == MfUltralightErrorNone, \"mf_ultralight_poller_sync_read_card() failed\");\n\n    nfc_listener_stop(mfu_listener);\n    nfc_listener_free(mfu_listener);\n\n    MfUltralightData* mfu_other_data =\n        (MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);\n    mu_assert(mf_ultralight_is_equal(mfu_data, mfu_other_data), \"Data mismatch\");\n\n    mf_ultralight_free(mfu_data);\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nMU_TEST(mf_ultralight_11_reader) {\n    mf_ultralight_reader_test(EXT_PATH(\"unit_tests/nfc/Ultralight_11.nfc\"));\n}\n\nMU_TEST(mf_ultralight_21_reader) {\n    mf_ultralight_reader_test(EXT_PATH(\"unit_tests/nfc/Ultralight_21.nfc\"));\n}\n\nMU_TEST(mf_ultralight_c_reader) {\n    mf_ultralight_reader_test(EXT_PATH(\"unit_tests/nfc/Ultralight_C.nfc\"));\n}\n\nMU_TEST(ntag_215_reader) {\n    mf_ultralight_reader_test(EXT_PATH(\"unit_tests/nfc/Ntag215.nfc\"));\n}\n\nMU_TEST(ntag_216_reader) {\n    mf_ultralight_reader_test(EXT_PATH(\"unit_tests/nfc/Ntag216.nfc\"));\n}\n\nMU_TEST(ntag_213_locked_reader) {\n    FURI_LOG_I(TAG, \"Testing Ntag215 locked file\");\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDeviceData* nfc_device = nfc_device_alloc();\n    mu_assert(\n        nfc_device_load(nfc_device, EXT_PATH(\"unit_tests/nfc/Ntag213_locked.nfc\")),\n        \"nfc_device_load() failed\\r\\n\");\n\n    NfcListener* mfu_listener = nfc_listener_alloc(\n        listener,\n        NfcProtocolMfUltralight,\n        nfc_device_get_data(nfc_device, NfcProtocolMfUltralight));\n    nfc_listener_start(mfu_listener, NULL, NULL);\n\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n    MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data, NULL);\n    mu_assert(error == MfUltralightErrorNone, \"mf_ultralight_poller_sync_read_card() failed\");\n\n    nfc_listener_stop(mfu_listener);\n    nfc_listener_free(mfu_listener);\n\n    MfUltralightConfigPages* config = NULL;\n    const MfUltralightData* mfu_ref_data =\n        nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);\n    mu_assert(\n        mf_ultralight_get_config_page(mfu_ref_data, &config),\n        \"mf_ultralight_get_config_page() failed\");\n    uint16_t pages_locked = config->auth0;\n\n    mu_assert(mfu_data->pages_read == pages_locked, \"Unexpected pages read\");\n\n    mf_ultralight_free(mfu_data);\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nstatic void mf_ultralight_write(void) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    nfc_data_generator_fill_data(NfcDataGeneratorTypeMfUltralightEV1_21, nfc_device);\n\n    NfcListener* mfu_listener = nfc_listener_alloc(\n        listener,\n        NfcProtocolMfUltralight,\n        nfc_device_get_data(nfc_device, NfcProtocolMfUltralight));\n    nfc_listener_start(mfu_listener, NULL, NULL);\n\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    // Initial read\n    MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data, NULL);\n    mu_assert(error == MfUltralightErrorNone, \"mf_ultralight_poller_sync_read_card() failed\");\n\n    mu_assert(\n        mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)),\n        \"Data not matches\");\n\n    // Write random data\n    for(size_t i = 5; i < 15; i++) {\n        MfUltralightPage page = {};\n        FURI_LOG_D(TAG, \"Writing page %d\", i);\n        furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage));\n        mfu_data->page[i] = page;\n        error = mf_ultralight_poller_sync_write_page(poller, i, &page);\n        mu_assert(error == MfUltralightErrorNone, \"mf_ultralight_poller_sync_write_page() failed\");\n    }\n\n    // Verification read\n    error = mf_ultralight_poller_sync_read_card(poller, mfu_data, NULL);\n    mu_assert(error == MfUltralightErrorNone, \"mf_ultralight_poller_sync_read_card() failed\");\n\n    nfc_listener_stop(mfu_listener);\n    const MfUltralightData* mfu_listener_data =\n        nfc_listener_get_data(mfu_listener, NfcProtocolMfUltralight);\n\n    mu_assert(mf_ultralight_is_equal(mfu_data, mfu_listener_data), \"Data not matches\");\n\n    nfc_listener_free(mfu_listener);\n    mf_ultralight_free(mfu_data);\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nstatic void mf_classic_reader(void) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);\n    NfcListener* mfc_listener = nfc_listener_alloc(\n        listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));\n    nfc_listener_start(mfc_listener, NULL, NULL);\n\n    MfClassicBlock block = {};\n    MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};\n\n    mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);\n\n    nfc_listener_stop(mfc_listener);\n    nfc_listener_free(mfc_listener);\n\n    const MfClassicData* mfc_data = nfc_device_get_data(nfc_device, NfcProtocolMfClassic);\n    mu_assert(memcmp(&mfc_data->block[0], &block, sizeof(MfClassicBlock)) == 0, \"Data mismatch\");\n\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nstatic void mf_classic_write(void) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);\n    NfcListener* mfc_listener = nfc_listener_alloc(\n        listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));\n    nfc_listener_start(mfc_listener, NULL, NULL);\n\n    MfClassicBlock block_write = {};\n    MfClassicBlock block_read = {};\n    furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock));\n    MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};\n\n    mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);\n    mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);\n\n    nfc_listener_stop(mfc_listener);\n    nfc_listener_free(mfc_listener);\n\n    mu_assert(memcmp(&block_read, &block_write, sizeof(MfClassicBlock)) == 0, \"Data mismatch\");\n\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nstatic void mf_classic_value_block(void) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);\n    NfcListener* mfc_listener = nfc_listener_alloc(\n        listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));\n    nfc_listener_start(mfc_listener, NULL, NULL);\n\n    MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};\n\n    int32_t value = 228;\n    MfClassicBlock block_write = {};\n    mf_classic_value_to_block(value, 1, &block_write);\n\n    MfClassicError error = MfClassicErrorNone;\n    error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);\n    mu_assert(error == MfClassicErrorNone, \"Write failed\");\n\n    int32_t data = 200;\n    int32_t new_value = 0;\n    error =\n        mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);\n    mu_assert(error == MfClassicErrorNone, \"Value increment failed\");\n    mu_assert(new_value == value + data, \"Value not match\");\n\n    error =\n        mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);\n    mu_assert(error == MfClassicErrorNone, \"Value decrement failed\");\n    mu_assert(new_value == value, \"Value not match\");\n\n    nfc_listener_stop(mfc_listener);\n    nfc_listener_free(mfc_listener);\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nNfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void* context) {\n    furi_check(event.poller);\n    furi_check(event.parent_event_data);\n    furi_check(context);\n\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPoller* instance = event.poller;\n    NfcTestMfClassicSendFrameTest* frame_test = context;\n    Iso14443_3aPollerEvent* iso3_event = event.parent_event_data;\n\n    MfClassicError error = MfClassicErrorNone;\n    if(iso3_event->type == Iso14443_3aPollerEventTypeReady) {\n        if(frame_test->state == NfcTestMfClassicSendFrameTestStateAuth) {\n            MfClassicKey key = {\n                .data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},\n            };\n            error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL, false);\n            frame_test->state = (error == MfClassicErrorNone) ?\n                                    NfcTestMfClassicSendFrameTestStateReadBlock :\n                                    NfcTestMfClassicSendFrameTestStateFail;\n        } else if(frame_test->state == NfcTestMfClassicSendFrameTestStateReadBlock) {\n            do {\n                const uint8_t read_block_cmd[] = {\n                    0x30,\n                    0x01,\n                    0x8b,\n                    0xb9,\n                };\n                bit_buffer_copy_bytes(frame_test->tx_buf, read_block_cmd, sizeof(read_block_cmd));\n\n                error = mf_classic_poller_send_encrypted_frame(\n                    instance, frame_test->tx_buf, frame_test->rx_buf, 200000);\n                if(error != MfClassicErrorNone) break;\n                if(bit_buffer_get_size_bytes(frame_test->rx_buf) != 18) {\n                    error = MfClassicErrorProtocol;\n                    break;\n                }\n\n                const uint8_t* rx_data = bit_buffer_get_data(frame_test->rx_buf);\n                const uint8_t rx_data_ref[16] = {0};\n                if(memcmp(rx_data, rx_data_ref, sizeof(rx_data_ref)) != 0) {\n                    error = MfClassicErrorProtocol;\n                    break;\n                }\n            } while(false);\n\n            frame_test->state = (error == MfClassicErrorNone) ?\n                                    NfcTestMfClassicSendFrameTestStateSuccess :\n                                    NfcTestMfClassicSendFrameTestStateFail;\n        } else if(frame_test->state == NfcTestMfClassicSendFrameTestStateSuccess) {\n            command = NfcCommandStop;\n        } else if(frame_test->state == NfcTestMfClassicSendFrameTestStateFail) {\n            command = NfcCommandStop;\n        }\n    } else {\n        frame_test->state = NfcTestMfClassicSendFrameTestStateFail;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_thread_flags_set(frame_test->thread_id, NFC_TEST_FLAG_WORKER_DONE);\n    }\n\n    return command;\n}\n\nMU_TEST(mf_classic_send_frame_test) {\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);\n    NfcListener* mfc_listener = nfc_listener_alloc(\n        listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));\n    nfc_listener_start(mfc_listener, NULL, NULL);\n\n    NfcPoller* mfc_poller = nfc_poller_alloc(poller, NfcProtocolMfClassic);\n    NfcTestMfClassicSendFrameTest context = {\n        .state = NfcTestMfClassicSendFrameTestStateAuth,\n        .thread_id = furi_thread_get_current_id(),\n        .tx_buf = bit_buffer_alloc(32),\n        .rx_buf = bit_buffer_alloc(32),\n    };\n    nfc_poller_start_ex(mfc_poller, mf_classic_poller_send_frame_callback, &context);\n\n    uint32_t flag =\n        furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);\n    mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, \"Wrong thread flag\");\n    nfc_poller_stop(mfc_poller);\n    nfc_poller_free(mfc_poller);\n\n    mu_assert(\n        context.state == NfcTestMfClassicSendFrameTestStateSuccess, \"Wrong test state at the end\");\n\n    bit_buffer_free(context.tx_buf);\n    bit_buffer_free(context.rx_buf);\n    nfc_listener_stop(mfc_listener);\n    nfc_listener_free(mfc_listener);\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nMU_TEST(mf_classic_dict_test) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) {\n        mu_assert(\n            storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH),\n            \"Remove test dict failed\");\n    }\n\n    KeysDict* dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n    mu_assert(dict != NULL, \"keys_dict_alloc() failed\");\n\n    size_t dict_keys_total = keys_dict_get_total_keys(dict);\n    mu_assert(dict_keys_total == 0, \"keys_dict_keys_total() failed\");\n\n    const uint32_t test_key_num = 30;\n    MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey));\n    for(size_t i = 0; i < test_key_num; i++) {\n        furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey));\n        mu_assert(\n            keys_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), \"add key failed\");\n\n        size_t dict_keys_total = keys_dict_get_total_keys(dict);\n        mu_assert(dict_keys_total == (i + 1), \"keys_dict_keys_total() failed\");\n    }\n\n    keys_dict_free(dict);\n\n    dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n    mu_assert(dict != NULL, \"keys_dict_alloc() failed\");\n\n    dict_keys_total = keys_dict_get_total_keys(dict);\n    mu_assert(dict_keys_total == test_key_num, \"keys_dict_keys_total() failed\");\n\n    MfClassicKey key_dut = {};\n    size_t key_idx = 0;\n    while(keys_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) {\n        mu_assert(\n            memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0,\n            \"Loaded key data mismatch\");\n        key_idx++;\n    }\n\n    uint32_t delete_keys_idx[] = {1, 3, 9, 11, 19, 27};\n\n    for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) {\n        MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]];\n        mu_assert(\n            keys_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)),\n            \"keys_dict_is_key_present() failed\");\n        mu_assert(\n            keys_dict_delete_key(dict, key->data, sizeof(MfClassicKey)),\n            \"keys_dict_delete_key() failed\");\n    }\n\n    dict_keys_total = keys_dict_get_total_keys(dict);\n    mu_assert(\n        dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx),\n        \"keys_dict_keys_total() failed\");\n\n    keys_dict_free(dict);\n    free(key_arr_ref);\n\n    mu_assert(\n        storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH),\n        \"Remove test dict failed\");\n}\n\nstatic FelicaError\n    felica_do_request_response(FelicaData* felica_data, const FelicaCardKey* card_key) {\n    NfcDeviceData* nfc_device = nfc_device_alloc();\n\n    FelicaError error = FelicaErrorNone;\n    if(!nfc_device_load(nfc_device, EXT_PATH(\"unit_tests/nfc/Felica.nfc\"))) {\n        error = FelicaErrorNotPresent;\n    } else {\n        Nfc* poller = nfc_alloc();\n        Nfc* listener = nfc_alloc();\n        NfcListener* felica_listener = nfc_listener_alloc(\n            listener, NfcProtocolFelica, nfc_device_get_data(nfc_device, NfcProtocolFelica));\n        nfc_listener_start(felica_listener, NULL, NULL);\n\n        error = felica_poller_sync_read(poller, felica_data, card_key);\n\n        nfc_listener_stop(felica_listener);\n        nfc_listener_free(felica_listener);\n\n        nfc_free(listener);\n        nfc_free(poller);\n    }\n\n    nfc_device_free(nfc_device);\n    return error;\n}\n\nMU_TEST(felica_read) {\n    FelicaData* felica_data = felica_alloc();\n    FelicaError error = felica_do_request_response(felica_data, NULL);\n    mu_assert(error == FelicaErrorNone, \"felica_poller() failed\");\n    mu_assert(felica_data->data.fs.spad[4].SF1 == 0x01, \"block[4].SF1 != 0x01\");\n    mu_assert(felica_data->data.fs.spad[4].SF2 == 0xB1, \"block[4].SF2 != 0xB1\");\n\n    felica_free(felica_data);\n}\n\nMU_TEST(felica_read_auth) {\n    FelicaData* felica_data = felica_alloc();\n    FelicaCardKey card_key;\n    memset(card_key.data, 0xFF, FELICA_DATA_BLOCK_SIZE);\n\n    FelicaError error = felica_do_request_response(felica_data, &card_key);\n    mu_assert(error == FelicaErrorNone, \"felica_poller() failed\");\n    mu_assert(felica_data->data.fs.spad[4].SF1 == 0x00, \"block[4].SF1 != 0x00\");\n    mu_assert(felica_data->data.fs.spad[4].SF2 == 0x00, \"block[4].SF2 != 0x00\");\n\n    felica_free(felica_data);\n}\n\nMU_TEST(slix_file_with_capabilities_test) {\n    NfcDevice* nfc_device_missed_cap = nfc_device_alloc();\n    mu_assert(\n        nfc_device_load(nfc_device_missed_cap, EXT_PATH(\"unit_tests/nfc/Slix_cap_missed.nfc\")),\n        \"nfc_device_load() failed\\r\\n\");\n\n    NfcDevice* nfc_device_default_cap = nfc_device_alloc();\n    mu_assert(\n        nfc_device_load(nfc_device_default_cap, EXT_PATH(\"unit_tests/nfc/Slix_cap_default.nfc\")),\n        \"nfc_device_load() failed\\r\\n\");\n\n    mu_assert(\n        nfc_device_is_equal(nfc_device_missed_cap, nfc_device_default_cap),\n        \"nfc_device_is_equal() failed\\r\\n\");\n\n    nfc_device_free(nfc_device_default_cap);\n    nfc_device_free(nfc_device_missed_cap);\n}\n\nNfcCommand slix_poller_set_password_callback(NfcGenericEventEx event, void* context) {\n    furi_check(event.poller);\n    furi_check(event.parent_event_data);\n    furi_check(context);\n\n    NfcCommand command = NfcCommandContinue;\n    Iso15693_3PollerEvent* iso15_event = event.parent_event_data;\n    SlixPoller* poller = event.poller;\n    NfcTestSlixPollerSetPasswordContext* slix_ctx = context;\n\n    if(iso15_event->type == Iso15693_3PollerEventTypeReady) {\n        iso15693_3_copy(\n            poller->data->iso15693_3_data, iso15693_3_poller_get_data(poller->iso15693_3_poller));\n\n        if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateGetRandomNumber) {\n            slix_ctx->error = slix_poller_get_random_number(poller, &slix_ctx->random_number);\n            if(slix_ctx->error != SlixErrorNone) {\n                furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);\n                command = NfcCommandStop;\n            } else {\n                slix_ctx->state = NfcTestSlixPollerSetPasswordStateSetPassword;\n            }\n        } else if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateSetPassword) {\n            slix_ctx->error = slix_poller_set_password(\n                poller, SlixPasswordTypeRead, slix_ctx->password, slix_ctx->random_number);\n            furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);\n            command = NfcCommandStop;\n        }\n    } else {\n        slix_ctx->error = slix_process_iso15693_3_error(iso15_event->data->error);\n        furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nstatic void slix_set_password_test(const char* file_path, SlixPassword pass, bool correct_pass) {\n    FURI_LOG_I(TAG, \"Testing file: %s\", file_path);\n\n    Nfc* poller = nfc_alloc();\n    Nfc* listener = nfc_alloc();\n\n    NfcDevice* nfc_device = nfc_device_alloc();\n    mu_assert(nfc_device_load(nfc_device, file_path), \"nfc_device_load() failed\\r\\n\");\n\n    const SlixData* slix_data = nfc_device_get_data(nfc_device, NfcProtocolSlix);\n    NfcListener* slix_listener = nfc_listener_alloc(listener, NfcProtocolSlix, slix_data);\n    nfc_listener_start(slix_listener, NULL, NULL);\n\n    SlixCapabilities slix_capabilities = slix_data->capabilities;\n\n    NfcPoller* slix_poller = nfc_poller_alloc(poller, NfcProtocolSlix);\n\n    NfcTestSlixPollerSetPasswordContext slix_poller_context = {\n        .thread_id = furi_thread_get_current_id(),\n        .state = NfcTestSlixPollerSetPasswordStateGetRandomNumber,\n        .password = pass,\n        .error = SlixErrorNone,\n    };\n\n    nfc_poller_start_ex(slix_poller, slix_poller_set_password_callback, &slix_poller_context);\n\n    uint32_t flag =\n        furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);\n    mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, \"Wrong thread flag\\r\\n\");\n\n    nfc_poller_stop(slix_poller);\n    nfc_poller_free(slix_poller);\n    nfc_listener_stop(slix_listener);\n    nfc_listener_free(slix_listener);\n\n    mu_assert(\n        slix_poller_context.state == NfcTestSlixPollerSetPasswordStateSetPassword,\n        \"Poller failed before setting password\\r\\n\");\n\n    if((slix_capabilities == SlixCapabilitiesAcceptAllPasswords) || (correct_pass)) {\n        mu_assert(slix_poller_context.error == SlixErrorNone, \"Failed to set password\\r\\n\");\n    } else {\n        mu_assert(\n            slix_poller_context.error == SlixErrorTimeout,\n            \"Must have received SlixErrorTimeout\\r\\n\");\n    }\n\n    nfc_device_free(nfc_device);\n    nfc_free(listener);\n    nfc_free(poller);\n}\n\nMU_TEST(slix_set_password_default_cap_correct_pass) {\n    slix_set_password_test(EXT_PATH(\"unit_tests/nfc/Slix_cap_default.nfc\"), 0x00000000, true);\n}\n\nMU_TEST(slix_set_password_default_cap_incorrect_pass) {\n    slix_set_password_test(EXT_PATH(\"unit_tests/nfc/Slix_cap_default.nfc\"), 0x12341234, false);\n}\n\nMU_TEST(slix_set_password_access_all_passwords_cap) {\n    slix_set_password_test(\n        EXT_PATH(\"unit_tests/nfc/Slix_cap_accept_all_pass.nfc\"), 0x12341234, false);\n}\n\nMU_TEST_SUITE(nfc) {\n    nfc_test_alloc();\n\n    MU_RUN_TEST(iso14443_3a_reader);\n    MU_RUN_TEST(mf_ultralight_11_reader);\n    MU_RUN_TEST(mf_ultralight_21_reader);\n    MU_RUN_TEST(ntag_215_reader);\n    MU_RUN_TEST(ntag_216_reader);\n    MU_RUN_TEST(ntag_213_locked_reader);\n    MU_RUN_TEST(mf_ultralight_c_reader);\n\n    MU_RUN_TEST(mf_ultralight_write);\n\n    MU_RUN_TEST(iso14443_3a_4b_file_test);\n    MU_RUN_TEST(iso14443_3a_7b_file_test);\n\n    MU_RUN_TEST(mf_ultralight_file_test);\n    MU_RUN_TEST(mf_ultralight_ev1_11_file_test);\n    MU_RUN_TEST(mf_ultralight_ev1_h11_file_test);\n    MU_RUN_TEST(mf_ultralight_ev1_21_file_test);\n    MU_RUN_TEST(mf_ultralight_ev1_h21_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_203_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_213_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_215_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_216_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_i2c_1k_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_i2c_2k_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test);\n    MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test);\n\n    MU_RUN_TEST(mf_classic_mini_file_test);\n    MU_RUN_TEST(mf_classic_1k_4b_file_test);\n    MU_RUN_TEST(mf_classic_1k_7b_file_test);\n    MU_RUN_TEST(mf_classic_4k_4b_file_test);\n    MU_RUN_TEST(mf_classic_4k_7b_file_test);\n\n    MU_RUN_TEST(mf_classic_reader);\n    MU_RUN_TEST(mf_classic_write);\n    MU_RUN_TEST(mf_classic_value_block);\n    MU_RUN_TEST(mf_classic_send_frame_test);\n    MU_RUN_TEST(mf_classic_dict_test);\n    MU_RUN_TEST(felica_read);\n    MU_RUN_TEST(felica_read_auth);\n\n    MU_RUN_TEST(slix_file_with_capabilities_test);\n    MU_RUN_TEST(slix_set_password_default_cap_correct_pass);\n    MU_RUN_TEST(slix_set_password_default_cap_incorrect_pass);\n    MU_RUN_TEST(slix_set_password_access_all_passwords_cap);\n\n    nfc_test_free();\n}\n\nint run_minunit_test_nfc(void) {\n    MU_RUN_SUITE(nfc);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_nfc)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/notification/notes_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n#include <float_tools.h>\n#include <notification/notification_messages_notes.h>\n\nvoid frequency_assert(const char* note_name, const NotificationMessage* message) {\n    double a = notification_messages_notes_frequency_from_name(note_name);\n    double b = message->data.sound.frequency;\n    const double epsilon = message->data.sound.frequency > 5000 ? 0.02f : 0.01f;\n    mu_assert_double_between(b - epsilon, b + epsilon, a);\n}\n\nMU_TEST(notification_messages_notes_frequency_from_name_test) {\n    // Upper case\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"C0\"),\n        notification_messages_notes_frequency_from_name(\"c0\")));\n\n    // Mixed case\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"Cs0\"),\n        notification_messages_notes_frequency_from_name(\"cs0\")));\n\n    // Check errors\n    mu_check(\n        float_is_equal(notification_messages_notes_frequency_from_name(\"0\"), 0.0)); // Without note\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"C\"), 0.0)); // Without octave\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"C9\"), 0.0)); // Unsupported octave\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"C10\"), 0.0)); // Unsupported octave\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"X0\"), 0.0)); // Unknown note\n    mu_check(float_is_equal(\n        notification_messages_notes_frequency_from_name(\"CCC0\"), 0.0)); // Note name overflow\n\n    // Notes and structures\n    frequency_assert(\"c0\", &message_note_c0);\n    frequency_assert(\"cs0\", &message_note_cs0);\n    frequency_assert(\"d0\", &message_note_d0);\n    frequency_assert(\"ds0\", &message_note_ds0);\n    frequency_assert(\"e0\", &message_note_e0);\n    frequency_assert(\"f0\", &message_note_f0);\n    frequency_assert(\"fs0\", &message_note_fs0);\n    frequency_assert(\"g0\", &message_note_g0);\n    frequency_assert(\"gs0\", &message_note_gs0);\n    frequency_assert(\"a0\", &message_note_a0);\n    frequency_assert(\"as0\", &message_note_as0);\n    frequency_assert(\"b0\", &message_note_b0);\n\n    frequency_assert(\"c1\", &message_note_c1);\n    frequency_assert(\"cs1\", &message_note_cs1);\n    frequency_assert(\"d1\", &message_note_d1);\n    frequency_assert(\"ds1\", &message_note_ds1);\n    frequency_assert(\"e1\", &message_note_e1);\n    frequency_assert(\"f1\", &message_note_f1);\n    frequency_assert(\"fs1\", &message_note_fs1);\n    frequency_assert(\"g1\", &message_note_g1);\n    frequency_assert(\"gs1\", &message_note_gs1);\n    frequency_assert(\"a1\", &message_note_a1);\n    frequency_assert(\"as1\", &message_note_as1);\n    frequency_assert(\"b1\", &message_note_b1);\n\n    frequency_assert(\"c2\", &message_note_c2);\n    frequency_assert(\"cs2\", &message_note_cs2);\n    frequency_assert(\"d2\", &message_note_d2);\n    frequency_assert(\"ds2\", &message_note_ds2);\n    frequency_assert(\"e2\", &message_note_e2);\n    frequency_assert(\"f2\", &message_note_f2);\n    frequency_assert(\"fs2\", &message_note_fs2);\n    frequency_assert(\"g2\", &message_note_g2);\n    frequency_assert(\"gs2\", &message_note_gs2);\n    frequency_assert(\"a2\", &message_note_a2);\n    frequency_assert(\"as2\", &message_note_as2);\n    frequency_assert(\"b2\", &message_note_b2);\n\n    frequency_assert(\"c3\", &message_note_c3);\n    frequency_assert(\"cs3\", &message_note_cs3);\n    frequency_assert(\"d3\", &message_note_d3);\n    frequency_assert(\"ds3\", &message_note_ds3);\n    frequency_assert(\"e3\", &message_note_e3);\n    frequency_assert(\"f3\", &message_note_f3);\n    frequency_assert(\"fs3\", &message_note_fs3);\n    frequency_assert(\"g3\", &message_note_g3);\n    frequency_assert(\"gs3\", &message_note_gs3);\n    frequency_assert(\"a3\", &message_note_a3);\n    frequency_assert(\"as3\", &message_note_as3);\n    frequency_assert(\"b3\", &message_note_b3);\n\n    frequency_assert(\"c4\", &message_note_c4);\n    frequency_assert(\"cs4\", &message_note_cs4);\n    frequency_assert(\"d4\", &message_note_d4);\n    frequency_assert(\"ds4\", &message_note_ds4);\n    frequency_assert(\"e4\", &message_note_e4);\n    frequency_assert(\"f4\", &message_note_f4);\n    frequency_assert(\"fs4\", &message_note_fs4);\n    frequency_assert(\"g4\", &message_note_g4);\n    frequency_assert(\"gs4\", &message_note_gs4);\n    frequency_assert(\"a4\", &message_note_a4);\n    frequency_assert(\"as4\", &message_note_as4);\n    frequency_assert(\"b4\", &message_note_b4);\n\n    frequency_assert(\"c5\", &message_note_c5);\n    frequency_assert(\"cs5\", &message_note_cs5);\n    frequency_assert(\"d5\", &message_note_d5);\n    frequency_assert(\"ds5\", &message_note_ds5);\n    frequency_assert(\"e5\", &message_note_e5);\n    frequency_assert(\"f5\", &message_note_f5);\n    frequency_assert(\"fs5\", &message_note_fs5);\n    frequency_assert(\"g5\", &message_note_g5);\n    frequency_assert(\"gs5\", &message_note_gs5);\n    frequency_assert(\"a5\", &message_note_a5);\n    frequency_assert(\"as5\", &message_note_as5);\n    frequency_assert(\"b5\", &message_note_b5);\n\n    frequency_assert(\"c6\", &message_note_c6);\n    frequency_assert(\"cs6\", &message_note_cs6);\n    frequency_assert(\"d6\", &message_note_d6);\n    frequency_assert(\"ds6\", &message_note_ds6);\n    frequency_assert(\"e6\", &message_note_e6);\n    frequency_assert(\"f6\", &message_note_f6);\n    frequency_assert(\"fs6\", &message_note_fs6);\n    frequency_assert(\"g6\", &message_note_g6);\n    frequency_assert(\"gs6\", &message_note_gs6);\n    frequency_assert(\"a6\", &message_note_a6);\n    frequency_assert(\"as6\", &message_note_as6);\n    frequency_assert(\"b6\", &message_note_b6);\n\n    frequency_assert(\"c7\", &message_note_c7);\n    frequency_assert(\"cs7\", &message_note_cs7);\n    frequency_assert(\"d7\", &message_note_d7);\n    frequency_assert(\"ds7\", &message_note_ds7);\n    frequency_assert(\"e7\", &message_note_e7);\n    frequency_assert(\"f7\", &message_note_f7);\n    frequency_assert(\"fs7\", &message_note_fs7);\n    frequency_assert(\"g7\", &message_note_g7);\n    frequency_assert(\"gs7\", &message_note_gs7);\n    frequency_assert(\"a7\", &message_note_a7);\n    frequency_assert(\"as7\", &message_note_as7);\n    frequency_assert(\"b7\", &message_note_b7);\n\n    frequency_assert(\"c8\", &message_note_c8);\n    frequency_assert(\"cs8\", &message_note_cs8);\n    frequency_assert(\"d8\", &message_note_d8);\n    frequency_assert(\"ds8\", &message_note_ds8);\n    frequency_assert(\"e8\", &message_note_e8);\n    frequency_assert(\"f8\", &message_note_f8);\n    frequency_assert(\"fs8\", &message_note_fs8);\n    frequency_assert(\"g8\", &message_note_g8);\n    frequency_assert(\"gs8\", &message_note_gs8);\n    frequency_assert(\"a8\", &message_note_a8);\n    frequency_assert(\"as8\", &message_note_as8);\n    frequency_assert(\"b8\", &message_note_b8);\n}\n\nMU_TEST_SUITE(notes_suite) {\n    MU_RUN_TEST(notification_messages_notes_frequency_from_name_test);\n}\n\nint run_minunit_test_notes(void) {\n    MU_RUN_SUITE(notes_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_notes)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/pipe/pipe_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n\n#include <furi.h>\n#include <lib/toolbox/pipe.h>\n\n#define PIPE_SIZE      128U\n#define PIPE_TRG_LEVEL 1U\n\nMU_TEST(pipe_test_trivial) {\n    PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL);\n    PipeSide* alice = bundle.alices_side;\n    PipeSide* bob = bundle.bobs_side;\n\n    mu_assert_int_eq(PipeRoleAlice, pipe_role(alice));\n    mu_assert_int_eq(PipeRoleBob, pipe_role(bob));\n    mu_assert_int_eq(PipeStateOpen, pipe_state(alice));\n    mu_assert_int_eq(PipeStateOpen, pipe_state(bob));\n\n    mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(alice));\n    mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(bob));\n    mu_assert_int_eq(0, pipe_bytes_available(alice));\n    mu_assert_int_eq(0, pipe_bytes_available(bob));\n\n    for(uint8_t i = 0;; ++i) {\n        mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(alice));\n        mu_assert_int_eq(i, pipe_bytes_available(bob));\n\n        if(pipe_spaces_available(alice) == 0) break;\n        furi_check(pipe_send(alice, &i, sizeof(uint8_t)) == sizeof(uint8_t));\n\n        mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(bob));\n        mu_assert_int_eq(i, pipe_bytes_available(alice));\n\n        furi_check(pipe_send(bob, &i, sizeof(uint8_t)) == sizeof(uint8_t));\n    }\n\n    pipe_free(alice);\n    mu_assert_int_eq(PipeStateBroken, pipe_state(bob));\n\n    for(uint8_t i = 0;; ++i) {\n        mu_assert_int_eq(PIPE_SIZE - i, pipe_bytes_available(bob));\n\n        if(pipe_bytes_available(bob) == 0) break;\n        uint8_t value;\n        furi_check(pipe_receive(bob, &value, sizeof(uint8_t)) == sizeof(uint8_t));\n\n        mu_assert_int_eq(i, value);\n    }\n\n    pipe_free(bob);\n}\n\ntypedef enum {\n    TestFlagDataArrived = 1 << 0,\n    TestFlagSpaceFreed = 1 << 1,\n    TestFlagBecameBroken = 1 << 2,\n} TestFlag;\n\ntypedef struct {\n    TestFlag flag;\n    FuriEventLoop* event_loop;\n} AncillaryThreadContext;\n\nstatic void on_data_arrived(PipeSide* pipe, void* context) {\n    AncillaryThreadContext* ctx = context;\n    ctx->flag |= TestFlagDataArrived;\n    uint8_t input;\n    size_t size = pipe_receive(pipe, &input, sizeof(input));\n    pipe_send(pipe, &input, size);\n}\n\nstatic void on_space_freed(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    AncillaryThreadContext* ctx = context;\n    ctx->flag |= TestFlagSpaceFreed;\n}\n\nstatic void on_became_broken(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    AncillaryThreadContext* ctx = context;\n    ctx->flag |= TestFlagBecameBroken;\n    furi_event_loop_stop(ctx->event_loop);\n}\n\nstatic int32_t ancillary_thread(void* context) {\n    PipeSide* pipe = context;\n    AncillaryThreadContext thread_ctx = {\n        .flag = 0,\n        .event_loop = furi_event_loop_alloc(),\n    };\n\n    pipe_attach_to_event_loop(pipe, thread_ctx.event_loop);\n    pipe_set_callback_context(pipe, &thread_ctx);\n    pipe_set_data_arrived_callback(pipe, on_data_arrived, 0);\n    pipe_set_space_freed_callback(pipe, on_space_freed, FuriEventLoopEventFlagEdge);\n    pipe_set_broken_callback(pipe, on_became_broken, 0);\n\n    furi_event_loop_run(thread_ctx.event_loop);\n\n    pipe_detach_from_event_loop(pipe);\n    pipe_free(pipe);\n    furi_event_loop_free(thread_ctx.event_loop);\n    return thread_ctx.flag;\n}\n\nMU_TEST(pipe_test_event_loop) {\n    PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL);\n    PipeSide* alice = bundle.alices_side;\n    PipeSide* bob = bundle.bobs_side;\n\n    FuriThread* thread = furi_thread_alloc_ex(\"PipeTestAnc\", 2048, ancillary_thread, bob);\n    furi_thread_start(thread);\n\n    const char* message = \"Hello!\";\n    pipe_send(alice, message, strlen(message));\n\n    char buffer_1[16];\n    size_t size = pipe_receive(alice, buffer_1, strlen(message));\n    buffer_1[size] = 0;\n\n    pipe_free(alice);\n    furi_thread_join(thread);\n\n    mu_assert_string_eq(message, buffer_1);\n    mu_assert_int_eq(\n        TestFlagDataArrived | TestFlagSpaceFreed | TestFlagBecameBroken,\n        furi_thread_get_return_code(thread));\n\n    furi_thread_free(thread);\n}\n\nMU_TEST_SUITE(test_pipe) {\n    MU_RUN_TEST(pipe_test_trivial);\n    MU_RUN_TEST(pipe_test_event_loop);\n}\n\nint run_minunit_test_pipe(void) {\n    MU_RUN_SUITE(test_pipe);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_pipe)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/power/power_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nstatic void power_test_deinit(void) {\n    // Try to reset to default charge voltage limit\n    furi_hal_power_set_battery_charge_voltage_limit(4.208f);\n}\n\nMU_TEST(test_power_charge_voltage_limit_exact) {\n    // Power of 16mV charge voltage limits get applied exactly\n    // (bq25896 charge controller works in 16mV increments)\n    //\n    // This test may need adapted if other charge controllers are used in the future.\n    for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {\n        float charge_volt = (float)charge_mv / 1000;\n        furi_hal_power_set_battery_charge_voltage_limit(charge_volt);\n        mu_assert_double_eq(\n            (double)charge_volt, (double)furi_hal_power_get_battery_charge_voltage_limit());\n    }\n}\n\nMU_TEST(test_power_charge_voltage_limit_floating_imprecision) {\n    // 4.016f should act as 4.016 V, even with floating point imprecision\n    furi_hal_power_set_battery_charge_voltage_limit(4.016f);\n    mu_assert_double_eq(4.016, (double)furi_hal_power_get_battery_charge_voltage_limit());\n}\n\nMU_TEST(test_power_charge_voltage_limit_inexact) {\n    // Charge voltage limits that are not power of 16mV get truncated down\n    furi_hal_power_set_battery_charge_voltage_limit(3.841f);\n    mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit());\n\n    furi_hal_power_set_battery_charge_voltage_limit(3.900f);\n    mu_assert_double_eq(3.888, (double)furi_hal_power_get_battery_charge_voltage_limit());\n\n    furi_hal_power_set_battery_charge_voltage_limit(4.200f);\n    mu_assert_double_eq(4.192, (double)furi_hal_power_get_battery_charge_voltage_limit());\n}\n\nMU_TEST(test_power_charge_voltage_limit_invalid_clamped) {\n    // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V\n    furi_hal_power_set_battery_charge_voltage_limit(3.808f);\n    mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit());\n    furi_hal_power_set_battery_charge_voltage_limit(1.0f);\n    mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit());\n\n    // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an\n    // unhappy battery if this fails.\n    furi_hal_power_set_battery_charge_voltage_limit(4.240f);\n    mu_assert_double_eq(4.208, (double)furi_hal_power_get_battery_charge_voltage_limit());\n    // Likewise, picking a number that the uint8_t wraparound in the driver would result in a\n    // VREG value under 23 if this test fails.\n    // E.g. (uint8_t)((8105-3840)/16) -> 10\n    furi_hal_power_set_battery_charge_voltage_limit(8.105f);\n    mu_assert_double_eq(4.208, (double)furi_hal_power_get_battery_charge_voltage_limit());\n}\n\nMU_TEST_SUITE(test_power_suite) {\n    MU_RUN_TEST(test_power_charge_voltage_limit_exact);\n    MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision);\n    MU_RUN_TEST(test_power_charge_voltage_limit_inexact);\n    MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped);\n    power_test_deinit();\n}\n\nint run_minunit_test_power(void) {\n    MU_RUN_SUITE(test_power_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_power)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c",
    "content": "#include <furi.h>\n#include \"../test.h\" // IWYU pragma: keep\n#include <toolbox/protocols/protocol_dict.h>\n\ntypedef enum {\n    TestDictProtocol0,\n    TestDictProtocol1,\n\n    TestDictProtocolMax,\n} TestDictProtocols;\n\n/*********************** PROTOCOL 0 START ***********************/\n\ntypedef struct {\n    uint32_t data;\n    size_t encoder_counter;\n} Protocol0Data;\n\nstatic const uint32_t protocol_0_decoder_result = 0xDEADBEEF;\n\nstatic void* protocol_0_alloc(void) {\n    void* data = malloc(sizeof(Protocol0Data));\n    return data;\n}\n\nstatic void protocol_0_free(Protocol0Data* data) {\n    free(data);\n}\n\nstatic uint8_t* protocol_0_get_data(Protocol0Data* data) {\n    return (uint8_t*)&data->data;\n}\n\nstatic void protocol_0_decoder_start(Protocol0Data* data) {\n    data->data = 0;\n}\n\nstatic bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) {\n    if(level && duration == 666) {\n        data->data = protocol_0_decoder_result;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstatic bool protocol_0_encoder_start(Protocol0Data* data) {\n    data->encoder_counter = 0;\n    return true;\n}\n\nstatic LevelDuration protocol_0_encoder_yield(Protocol0Data* data) {\n    data->encoder_counter++;\n    return level_duration_make(data->encoder_counter % 2, data->data);\n}\n\n/*********************** PROTOCOL 1 START ***********************/\n\ntypedef struct {\n    uint64_t data;\n    size_t encoder_counter;\n} Protocol1Data;\n\nstatic const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF;\n\nstatic void* protocol_1_alloc(void) {\n    void* data = malloc(sizeof(Protocol1Data));\n    return data;\n}\n\nstatic void protocol_1_free(Protocol1Data* data) {\n    free(data);\n}\n\nstatic uint8_t* protocol_1_get_data(Protocol1Data* data) {\n    return (uint8_t*)&data->data;\n}\n\nstatic void protocol_1_decoder_start(Protocol1Data* data) {\n    data->data = 0;\n}\n\nstatic bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) {\n    if(level && duration == 543) {\n        data->data = 0x1234567890ABCDEF;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstatic bool protocol_1_encoder_start(Protocol1Data* data) {\n    data->encoder_counter = 0;\n    return true;\n}\n\nstatic LevelDuration protocol_1_encoder_yield(Protocol1Data* data) {\n    data->encoder_counter++;\n    return level_duration_make(!(data->encoder_counter % 2), 100);\n}\n\n/*********************** PROTOCOLS DESCRIPTION ***********************/\nstatic const ProtocolBase protocol_0 = {\n    .name = \"Protocol 0\",\n    .manufacturer = \"Manufacturer 0\",\n    .data_size = 4,\n    .alloc = (ProtocolAlloc)protocol_0_alloc,\n    .free = (ProtocolFree)protocol_0_free,\n    .get_data = (ProtocolGetData)protocol_0_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_0_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_0_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_0_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_0_encoder_yield,\n        },\n};\n\nstatic const ProtocolBase protocol_1 = {\n    .name = \"Protocol 1\",\n    .manufacturer = \"Manufacturer 1\",\n    .data_size = 8,\n    .alloc = (ProtocolAlloc)protocol_1_alloc,\n    .free = (ProtocolFree)protocol_1_free,\n    .get_data = (ProtocolGetData)protocol_1_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_1_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_1_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_1_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_1_encoder_yield,\n        },\n};\n\nstatic const ProtocolBase* test_protocols_base[] = {\n    [TestDictProtocol0] = &protocol_0,\n    [TestDictProtocol1] = &protocol_1,\n};\n\nMU_TEST(test_protocol_dict) {\n    ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax);\n    size_t max_data_size = protocol_dict_get_max_data_size(dict);\n    mu_assert_int_eq(8, max_data_size);\n    uint8_t* data = malloc(max_data_size);\n\n    protocol_dict_decoders_start(dict);\n    ProtocolId protocol_id = PROTOCOL_NO;\n\n    for(size_t i = 0; i < 100; i++) {\n        protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100);\n        mu_assert_int_eq(PROTOCOL_NO, protocol_id);\n    }\n\n    // trigger protocol 1\n    protocol_id = protocol_dict_decoders_feed(dict, true, 543);\n    mu_assert_int_eq(TestDictProtocol1, protocol_id);\n\n    mu_assert_string_eq(\"Protocol 1\", protocol_dict_get_name(dict, protocol_id));\n    mu_assert_string_eq(\"Manufacturer 1\", protocol_dict_get_manufacturer(dict, protocol_id));\n\n    size_t data_size = protocol_dict_get_data_size(dict, protocol_id);\n    mu_assert_int_eq(8, data_size);\n\n    protocol_dict_get_data(dict, protocol_id, data, data_size);\n    mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size);\n\n    // trigger protocol 0\n    protocol_id = protocol_dict_decoders_feed(dict, true, 666);\n    mu_assert_int_eq(TestDictProtocol0, protocol_id);\n\n    mu_assert_string_eq(\"Protocol 0\", protocol_dict_get_name(dict, protocol_id));\n    mu_assert_string_eq(\"Manufacturer 0\", protocol_dict_get_manufacturer(dict, protocol_id));\n\n    data_size = protocol_dict_get_data_size(dict, protocol_id);\n    mu_assert_int_eq(4, data_size);\n\n    protocol_dict_get_data(dict, protocol_id, data, data_size);\n    mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size);\n\n    protocol_dict_decoders_start(dict);\n\n    protocol_id = TestDictProtocol0;\n\n    const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0};\n    protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4);\n\n    mu_check(protocol_dict_encoder_start(dict, protocol_id));\n\n    LevelDuration level;\n    level = protocol_dict_encoder_yield(dict, protocol_id);\n    mu_assert_int_eq(true, level_duration_get_level(level));\n    mu_assert_int_eq(100, level_duration_get_duration(level));\n    level = protocol_dict_encoder_yield(dict, protocol_id);\n    mu_assert_int_eq(false, level_duration_get_level(level));\n    mu_assert_int_eq(100, level_duration_get_duration(level));\n    level = protocol_dict_encoder_yield(dict, protocol_id);\n    mu_assert_int_eq(true, level_duration_get_level(level));\n    mu_assert_int_eq(100, level_duration_get_duration(level));\n\n    mu_check(protocol_dict_encoder_start(dict, protocol_id));\n    level = protocol_dict_encoder_yield(dict, protocol_id);\n    mu_assert_int_eq(true, level_duration_get_level(level));\n    mu_assert_int_eq(100, level_duration_get_duration(level));\n\n    protocol_dict_free(dict);\n    free(data);\n}\n\nMU_TEST_SUITE(test_protocol_dict_suite) {\n    MU_RUN_TEST(test_protocol_dict);\n}\n\nint run_minunit_test_protocol_dict(void) {\n    MU_RUN_SUITE(test_protocol_dict_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_protocol_dict)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/rpc/rpc_test.c",
    "content": "#include <furi.h>\n#include <stdint.h>\n\n#include <rpc/rpc.h>\n#include <rpc/rpc_i.h>\n#include <storage/storage.h>\n#include <loader/loader.h>\n#include <storage/filesystem_api_defines.h>\n\n#include <lib/toolbox/api_lock.h>\n#include <lib/toolbox/md5_calc.h>\n#include <lib/toolbox/path.h>\n\n#include <m-list.h>\n#include \"../test.h\" // IWYU pragma: keep\n\n#include <protobuf_version.h>\n#include <pb.h>\n#include <pb_encode.h>\n#include <pb_decode.h>\n#include <storage.pb.h>\n#include <flipper.pb.h>\n\nLIST_DEF(MsgList, PB_Main, M_POD_OPLIST)\n#define M_OPL_MsgList_t() LIST_OPLIST(MsgList)\n\n#define TEST_RPC_SESSIONS 2\n\n/* MinUnit test framework doesn't allow passing context into tests,\n * so we have to use global variables\n */\nstatic Rpc* rpc = NULL;\nstatic uint32_t command_id = 0;\n\ntypedef struct {\n    RpcSession* session;\n    FuriStreamBuffer* output_stream;\n    FuriApiLock session_close_lock;\n    FuriApiLock session_terminate_lock;\n    uint32_t timeout;\n} RpcSessionContext;\n\nstatic RpcSessionContext rpc_session[TEST_RPC_SESSIONS];\n\n#define TAG \"UnitTestsRpc\"\n\n#define MAX_RECEIVE_OUTPUT_TIMEOUT 3000\n#define MAX_NAME_LENGTH            255\n#define MAX_DATA_SIZE              512u // have to be exact as in rpc_storage.c\n#define TEST_DIR_NAME              EXT_PATH(\".tmp/unit_tests/rpc\")\n#define TEST_DIR                   TEST_DIR_NAME \"/\"\n#define MD5SUM_SIZE                16\n\n#define PING_REQUEST  0\n#define PING_RESPONSE 1\n#define WRITE_REQUEST 0\n#define READ_RESPONSE 1\n\n#define DEBUG_PRINT 0\n\n#define BYTES(x) (x), sizeof(x)\n\n#define DISABLE_TEST(code)  \\\n    do {                    \\\n        volatile int a = 0; \\\n        if(a) {             \\\n            code            \\\n        }                   \\\n    } while(0)\n\nstatic void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size);\nstatic void\n    test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id);\nstatic void test_rpc_encode_and_feed(MsgList_t msg_list, uint8_t session);\nstatic void test_rpc_encode_and_feed_one(PB_Main* request, uint8_t session);\nstatic void test_rpc_compare_messages(PB_Main* result, PB_Main* expected);\nstatic void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t session);\nstatic void test_rpc_free_msg_list(MsgList_t msg_list);\nstatic void test_rpc_session_close_callback(void* context);\nstatic void test_rpc_session_terminated_callback(void* context);\n\nstatic void test_rpc_setup(void) {\n    furi_check(!rpc);\n    furi_check(!(rpc_session[0].session));\n\n    rpc = furi_record_open(RECORD_RPC);\n    for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) {\n        rpc_session[0].session = rpc_session_open(rpc, RpcOwnerUnknown);\n        furi_delay_tick(1);\n    }\n    furi_check(rpc_session[0].session);\n\n    rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);\n    rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);\n    rpc_session[0].session_close_lock = api_lock_alloc_locked();\n    rpc_session[0].session_terminate_lock = api_lock_alloc_locked();\n    rpc_session_set_close_callback(rpc_session[0].session, test_rpc_session_close_callback);\n    rpc_session_set_terminated_callback(\n        rpc_session[0].session, test_rpc_session_terminated_callback);\n    rpc_session_set_context(rpc_session[0].session, &rpc_session[0]);\n}\n\nstatic void test_rpc_setup_second_session(void) {\n    furi_check(rpc);\n    furi_check(!(rpc_session[1].session));\n\n    for(int i = 0; !(rpc_session[1].session) && (i < 10000); ++i) {\n        rpc_session[1].session = rpc_session_open(rpc, RpcOwnerUnknown);\n        furi_delay_tick(1);\n    }\n    furi_check(rpc_session[1].session);\n\n    rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1);\n    rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback);\n    rpc_session[1].session_close_lock = api_lock_alloc_locked();\n    rpc_session[1].session_terminate_lock = api_lock_alloc_locked();\n    rpc_session_set_close_callback(rpc_session[1].session, test_rpc_session_close_callback);\n    rpc_session_set_terminated_callback(\n        rpc_session[1].session, test_rpc_session_terminated_callback);\n    rpc_session_set_context(rpc_session[1].session, &rpc_session[1]);\n}\n\nstatic void test_rpc_teardown(void) {\n    furi_check(rpc_session[0].session_close_lock);\n    api_lock_relock(rpc_session[0].session_terminate_lock);\n    rpc_session_close(rpc_session[0].session);\n    api_lock_wait_unlock(rpc_session[0].session_terminate_lock);\n    furi_record_close(RECORD_RPC);\n    furi_stream_buffer_free(rpc_session[0].output_stream);\n    api_lock_free(rpc_session[0].session_close_lock);\n    api_lock_free(rpc_session[0].session_terminate_lock);\n    ++command_id;\n    rpc_session[0].output_stream = NULL;\n    rpc_session[0].session_close_lock = NULL;\n    rpc = NULL;\n    rpc_session[0].session = NULL;\n}\n\nstatic void test_rpc_teardown_second_session(void) {\n    furi_check(rpc_session[1].session_close_lock);\n    api_lock_relock(rpc_session[1].session_terminate_lock);\n    rpc_session_close(rpc_session[1].session);\n    api_lock_wait_unlock(rpc_session[1].session_terminate_lock);\n    furi_stream_buffer_free(rpc_session[1].output_stream);\n    api_lock_free(rpc_session[1].session_close_lock);\n    api_lock_free(rpc_session[1].session_terminate_lock);\n    ++command_id;\n    rpc_session[1].output_stream = NULL;\n    rpc_session[1].session_close_lock = NULL;\n    rpc_session[1].session = NULL;\n}\n\nstatic void test_rpc_storage_clean_directory(Storage* fs_api, const char* clean_dir) {\n    furi_check(fs_api);\n    furi_check(clean_dir);\n    storage_simply_remove_recursive(fs_api, clean_dir);\n    FS_Error error = storage_common_mkdir(fs_api, clean_dir);\n    furi_check(error == FSE_OK);\n}\n\nstatic void test_rpc_storage_create_file(Storage* fs_api, const char* path, size_t size) {\n    File* file = storage_file_alloc(fs_api);\n\n    bool success = false;\n    do {\n        if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;\n        if(!storage_file_seek(file, size, true)) break;\n        success = true;\n    } while(false);\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_check(success);\n}\n\nstatic void test_rpc_storage_setup(void) {\n    test_rpc_setup();\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file100\", 100);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file250\", 250);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file500\", 200);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file1000\", 1000);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file2500\", 2500);\n    test_rpc_storage_create_file(fs_api, TEST_DIR_NAME \"/file5000\", 5000);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void test_rpc_storage_teardown(void) {\n    test_rpc_teardown();\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void test_rpc_session_close_callback(void* context) {\n    furi_check(context);\n    RpcSessionContext* callbacks_context = context;\n\n    api_lock_unlock(callbacks_context->session_close_lock);\n}\n\nstatic void test_rpc_session_terminated_callback(void* context) {\n    furi_check(context);\n    RpcSessionContext* callbacks_context = context;\n\n    api_lock_unlock(callbacks_context->session_terminate_lock);\n}\n\nstatic void test_rpc_print_message_list(MsgList_t msg_list) {\n#if DEBUG_PRINT\n    MsgList_reverse(msg_list);\n    for\n        M_EACH(msg, msg_list, MsgList_t) {\n            rpc_debug_print_message(msg);\n        }\n    MsgList_reverse(msg_list);\n#else\n    UNUSED(msg_list);\n#endif\n}\n\nstatic PB_CommandStatus test_rpc_storage_get_file_error(File* file) {\n    FS_Error fs_error = storage_file_get_error(file);\n    PB_CommandStatus pb_error;\n    switch(fs_error) {\n    case FSE_OK:\n        pb_error = PB_CommandStatus_OK;\n        break;\n    case FSE_INVALID_NAME:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;\n        break;\n    case FSE_INVALID_PARAMETER:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;\n        break;\n    case FSE_INTERNAL:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INTERNAL;\n        break;\n    case FSE_ALREADY_OPEN:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN;\n        break;\n    case FSE_DENIED:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_DENIED;\n        break;\n    case FSE_EXIST:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_EXIST;\n        break;\n    case FSE_NOT_EXIST:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_EXIST;\n        break;\n    case FSE_NOT_READY:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_READY;\n        break;\n    case FSE_NOT_IMPLEMENTED:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED;\n        break;\n    default:\n        pb_error = PB_CommandStatus_ERROR;\n        break;\n    }\n\n    return pb_error;\n}\n\nstatic void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size) {\n    RpcSessionContext* callbacks_context = ctx;\n\n    size_t bytes_sent = furi_stream_buffer_send(\n        callbacks_context->output_stream, got_bytes, got_size, FuriWaitForever);\n    (void)bytes_sent;\n    furi_check(bytes_sent == got_size);\n}\n\nstatic void test_rpc_add_ping_to_list(MsgList_t msg_list, bool request, uint32_t command_id) {\n    PB_Main* response = MsgList_push_new(msg_list);\n    response->command_id = command_id;\n    response->command_status = PB_CommandStatus_OK;\n    response->cb_content.funcs.encode = NULL;\n    response->has_next = false;\n    response->which_content = (request == PING_REQUEST) ? PB_Main_system_ping_request_tag :\n                                                          PB_Main_system_ping_response_tag;\n}\nstatic void test_rpc_fill_basic_message(PB_Main* message, uint16_t tag, uint32_t command_id) {\n    message->command_id = command_id;\n    message->command_status = PB_CommandStatus_OK;\n    message->cb_content.funcs.encode = NULL;\n    message->which_content = tag;\n    message->has_next = false;\n}\n\nstatic void test_rpc_create_storage_list_request(\n    PB_Main* message,\n    const char* path,\n    bool include_md5,\n    uint32_t command_id,\n    uint32_t filter_max_size) {\n    furi_check(message);\n    furi_check(path);\n    test_rpc_fill_basic_message(message, PB_Main_storage_list_request_tag, command_id);\n    message->content.storage_list_request.path = strdup(path);\n    message->content.storage_list_request.include_md5 = include_md5;\n    message->content.storage_list_request.filter_max_size = filter_max_size;\n}\n\nstatic void test_rpc_create_simple_message(\n    PB_Main* message,\n    uint16_t tag,\n    const char* str,\n    uint32_t command_id) {\n    furi_check(message);\n\n    char* str_copy = NULL;\n    if(str) {\n        str_copy = strdup(str);\n    }\n    test_rpc_fill_basic_message(message, tag, command_id);\n    switch(tag) {\n    case PB_Main_storage_info_request_tag:\n        message->content.storage_info_request.path = str_copy;\n        break;\n    case PB_Main_storage_stat_request_tag:\n        message->content.storage_stat_request.path = str_copy;\n        break;\n    case PB_Main_storage_mkdir_request_tag:\n        message->content.storage_mkdir_request.path = str_copy;\n        break;\n    case PB_Main_storage_read_request_tag:\n        message->content.storage_read_request.path = str_copy;\n        break;\n    case PB_Main_storage_delete_request_tag:\n        message->content.storage_delete_request.path = str_copy;\n        break;\n    case PB_Main_storage_md5sum_request_tag:\n        message->content.storage_md5sum_request.path = str_copy;\n        break;\n    case PB_Main_storage_md5sum_response_tag: {\n        char* md5sum = message->content.storage_md5sum_response.md5sum;\n        size_t md5sum_size = sizeof(message->content.storage_md5sum_response.md5sum);\n        furi_check((strlen(str) + 1) <= md5sum_size);\n        memcpy(md5sum, str_copy, md5sum_size);\n        free(str_copy);\n        break;\n    }\n    default:\n        furi_check(0);\n        break;\n    }\n}\n\nstatic void test_rpc_add_read_or_write_to_list(\n    MsgList_t msg_list,\n    bool write,\n    const char* path,\n    const uint8_t* pattern,\n    size_t pattern_size,\n    size_t pattern_repeats,\n    uint32_t command_id) {\n    furi_check(pattern_repeats > 0);\n\n    do {\n        PB_Main* request = MsgList_push_new(msg_list);\n        PB_Storage_File* msg_file = NULL;\n\n        request->command_id = command_id;\n        request->command_status = PB_CommandStatus_OK;\n\n        if(write == WRITE_REQUEST) {\n            request->content.storage_write_request.path = strdup(path);\n            request->which_content = PB_Main_storage_write_request_tag;\n            request->content.storage_write_request.has_file = true;\n            msg_file = &request->content.storage_write_request.file;\n        } else {\n            request->which_content = PB_Main_storage_read_response_tag;\n            request->content.storage_read_response.has_file = true;\n            msg_file = &request->content.storage_read_response.file;\n        }\n\n        msg_file->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(pattern_size));\n        msg_file->data->size = pattern_size;\n\n        memcpy(msg_file->data->bytes, pattern, pattern_size);\n\n        --pattern_repeats;\n        request->has_next = (pattern_repeats > 0);\n    } while(pattern_repeats);\n}\n\nstatic void test_rpc_encode_and_feed_one(PB_Main* request, uint8_t session) {\n    furi_check(request);\n    furi_check(session < TEST_RPC_SESSIONS);\n\n    pb_ostream_t ostream = PB_OSTREAM_SIZING;\n\n    bool result = pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);\n    furi_check(result && ostream.bytes_written);\n\n    uint8_t* buffer = malloc(ostream.bytes_written);\n    ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);\n\n    pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);\n\n    size_t bytes_left = ostream.bytes_written;\n    uint8_t* buffer_ptr = buffer;\n    do {\n        size_t bytes_sent =\n            rpc_session_feed(rpc_session[session].session, buffer_ptr, bytes_left, 1000);\n        mu_check(bytes_sent > 0);\n\n        bytes_left -= bytes_sent;\n        buffer_ptr += bytes_sent;\n    } while(bytes_left);\n\n    free(buffer);\n    pb_release(&PB_Main_msg, request);\n}\n\nstatic void test_rpc_encode_and_feed(MsgList_t msg_list, uint8_t session) {\n    MsgList_reverse(msg_list);\n    for\n        M_EACH(request, msg_list, MsgList_t) {\n            test_rpc_encode_and_feed_one(request, session);\n        }\n    MsgList_reverse(msg_list);\n}\n\nstatic void\n    test_rpc_compare_file(PB_Storage_File* result_msg_file, PB_Storage_File* expected_msg_file) {\n    mu_check(!result_msg_file->name == !expected_msg_file->name);\n    if(result_msg_file->name) {\n        mu_check(!strcmp(result_msg_file->name, expected_msg_file->name));\n    }\n    mu_check(result_msg_file->size == expected_msg_file->size);\n    mu_check(result_msg_file->type == expected_msg_file->type);\n    mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum);\n\n    if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) {\n        mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF???\n        mu_check(result_msg_file->data->size == expected_msg_file->data->size);\n        for(int i = 0; i < result_msg_file->data->size; ++i) {\n            mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]);\n        }\n    }\n}\n\nstatic void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) {\n    mu_assert_int_eq(expected->command_id, result->command_id);\n    mu_assert_int_eq(expected->command_status, result->command_status);\n    mu_assert_int_eq(expected->has_next, result->has_next);\n    mu_assert_int_eq(expected->which_content, result->which_content);\n    if(result->command_status != PB_CommandStatus_OK) {\n        mu_check(result->which_content == PB_Main_empty_tag);\n    }\n\n    switch(result->which_content) {\n    case PB_Main_empty_tag:\n    case PB_Main_system_ping_response_tag:\n        /* nothing to check */\n        break;\n    case PB_Main_system_ping_request_tag:\n    case PB_Main_storage_list_request_tag:\n    case PB_Main_storage_read_request_tag:\n    case PB_Main_storage_write_request_tag:\n    case PB_Main_storage_delete_request_tag:\n    case PB_Main_storage_mkdir_request_tag:\n    case PB_Main_storage_md5sum_request_tag:\n        /* rpc doesn't send it */\n        mu_check(0);\n        break;\n    case PB_Main_app_lock_status_response_tag: {\n        bool result_locked = result->content.app_lock_status_response.locked;\n        bool expected_locked = expected->content.app_lock_status_response.locked;\n        mu_check(result_locked == expected_locked);\n        break;\n    }\n    case PB_Main_storage_info_response_tag: {\n        uint64_t result_total_space = result->content.storage_info_response.total_space;\n        uint64_t expected_total_space = expected->content.storage_info_response.total_space;\n        mu_check(result_total_space == expected_total_space);\n\n        uint64_t result_free_space = result->content.storage_info_response.free_space;\n        uint64_t expected_free_space = expected->content.storage_info_response.free_space;\n        mu_check(result_free_space == expected_free_space);\n    } break;\n    case PB_Main_storage_stat_response_tag: {\n        bool result_has_msg_file = result->content.storage_stat_response.has_file;\n        bool expected_has_msg_file = expected->content.storage_stat_response.has_file;\n        mu_check(result_has_msg_file == expected_has_msg_file);\n\n        if(result_has_msg_file) {\n            PB_Storage_File* result_msg_file = &result->content.storage_stat_response.file;\n            PB_Storage_File* expected_msg_file = &expected->content.storage_stat_response.file;\n            test_rpc_compare_file(result_msg_file, expected_msg_file);\n        } else {\n            mu_check(0);\n        }\n    } break;\n    case PB_Main_storage_read_response_tag: {\n        bool result_has_msg_file = result->content.storage_read_response.has_file;\n        bool expected_has_msg_file = expected->content.storage_read_response.has_file;\n        mu_check(result_has_msg_file == expected_has_msg_file);\n\n        if(result_has_msg_file) {\n            PB_Storage_File* result_msg_file = &result->content.storage_read_response.file;\n            PB_Storage_File* expected_msg_file = &expected->content.storage_read_response.file;\n            test_rpc_compare_file(result_msg_file, expected_msg_file);\n        } else {\n            mu_check(0);\n        }\n    } break;\n    case PB_Main_storage_list_response_tag: {\n        size_t expected_msg_files = expected->content.storage_list_response.file_count;\n        size_t result_msg_files = result->content.storage_list_response.file_count;\n        mu_assert_int_eq(expected_msg_files, result_msg_files);\n        for(size_t i = 0; i < expected_msg_files; ++i) {\n            PB_Storage_File* result_msg_file = &result->content.storage_list_response.file[i];\n            PB_Storage_File* expected_msg_file = &expected->content.storage_list_response.file[i];\n            test_rpc_compare_file(result_msg_file, expected_msg_file);\n        }\n        break;\n    }\n    case PB_Main_storage_md5sum_response_tag: {\n        char* result_md5sum = result->content.storage_md5sum_response.md5sum;\n        char* expected_md5sum = expected->content.storage_md5sum_response.md5sum;\n        mu_check(!strcmp(result_md5sum, expected_md5sum));\n        break;\n    }\n    case PB_Main_system_protobuf_version_response_tag: {\n        uint32_t major_version_expected = expected->content.system_protobuf_version_response.major;\n        uint32_t minor_version_expected = expected->content.system_protobuf_version_response.minor;\n        uint32_t major_version_result = result->content.system_protobuf_version_response.major;\n        uint32_t minor_version_result = result->content.system_protobuf_version_response.minor;\n        mu_check(major_version_expected == major_version_result);\n        mu_check(minor_version_expected == minor_version_result);\n        break;\n    }\n    default:\n        furi_check(0);\n        break;\n    }\n}\n\nstatic bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {\n    RpcSessionContext* session_context = istream->state;\n    size_t bytes_received = 0;\n\n    uint32_t now = furi_get_tick();\n    int32_t time_left = session_context->timeout - now;\n    time_left = MAX(time_left, 0);\n    bytes_received =\n        furi_stream_buffer_receive(session_context->output_stream, buf, count, time_left);\n    return count == bytes_received;\n}\n\nstatic void\n    test_rpc_storage_list_create_expected_list_root(MsgList_t msg_list, uint32_t command_id) {\n    PB_Main* message = MsgList_push_new(msg_list);\n    message->has_next = false;\n    message->cb_content.funcs.encode = NULL;\n    message->command_id = command_id;\n    message->which_content = PB_Main_storage_list_response_tag;\n\n    message->content.storage_list_response.file_count = 3;\n    message->content.storage_list_response.file[0].data = NULL;\n    message->content.storage_list_response.file[1].data = NULL;\n    message->content.storage_list_response.file[2].data = NULL;\n\n    message->content.storage_list_response.file[0].size = 0;\n    message->content.storage_list_response.file[1].size = 0;\n    message->content.storage_list_response.file[2].size = 0;\n\n    message->content.storage_list_response.file[0].type = PB_Storage_File_FileType_DIR;\n    message->content.storage_list_response.file[1].type = PB_Storage_File_FileType_DIR;\n    message->content.storage_list_response.file[2].type = PB_Storage_File_FileType_DIR;\n\n    char* str = malloc(4);\n    strcpy(str, \"any\");\n    message->content.storage_list_response.file[0].name = str;\n    str = malloc(4);\n    strcpy(str, \"int\");\n    message->content.storage_list_response.file[1].name = str;\n    str = malloc(4);\n    strcpy(str, \"ext\");\n    message->content.storage_list_response.file[2].name = str;\n}\n\nstatic bool test_rpc_system_storage_list_filter(\n    const FileInfo* fileinfo,\n    const char* name,\n    size_t filter_max_size) {\n    bool result = false;\n\n    do {\n        if(!path_contains_only_ascii(name)) break;\n        if(filter_max_size) {\n            if(fileinfo->size > filter_max_size) break;\n        }\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nstatic void test_rpc_storage_list_create_expected_list(\n    MsgList_t msg_list,\n    const char* path,\n    uint32_t command_id,\n    bool append_md5,\n    size_t filter_max_size) {\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* dir = storage_file_alloc(fs_api);\n\n    FuriString* md5 = furi_string_alloc();\n    FuriString* md5_path = furi_string_alloc();\n    File* file = storage_file_alloc(fs_api);\n\n    PB_Main response = {\n        .command_id = command_id,\n        .has_next = false,\n        .which_content = PB_Main_storage_list_response_tag,\n        /* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */\n    };\n    PB_Storage_ListResponse* list = &response.content.storage_list_response;\n\n    bool finish = false;\n    int i = 0;\n\n    if(storage_dir_open(dir, path)) {\n        response.command_status = PB_CommandStatus_OK;\n    } else {\n        response.command_status = test_rpc_storage_get_file_error(dir);\n        response.which_content = PB_Main_empty_tag;\n        finish = true;\n    }\n\n    while(!finish) {\n        FileInfo fileinfo;\n        char* name = malloc(MAX_NAME_LENGTH + 1);\n        if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {\n            if(i == COUNT_OF(list->file)) {\n                list->file_count = i;\n                response.has_next = true;\n                MsgList_push_back(msg_list, response);\n                i = 0;\n            }\n\n            if(test_rpc_system_storage_list_filter(&fileinfo, name, filter_max_size)) {\n                list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :\n                                                                   PB_Storage_File_FileType_FILE;\n                list->file[i].size = fileinfo.size;\n                list->file[i].data = NULL;\n                /* memory free inside rpc_encode_and_send() -> pb_release() */\n                list->file[i].name = name;\n\n                if(append_md5 && !file_info_is_dir(&fileinfo)) {\n                    furi_string_printf(md5_path, \"%s/%s\", path, name);\n\n                    if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) {\n                        char* md5sum = list->file[i].md5sum;\n                        size_t md5sum_size = sizeof(list->file[i].md5sum);\n                        snprintf(md5sum, md5sum_size, \"%s\", furi_string_get_cstr(md5));\n                    }\n                }\n\n                ++i;\n            }\n        } else {\n            finish = true;\n            free(name);\n        }\n    }\n\n    list->file_count = i;\n    response.has_next = false;\n    MsgList_push_back(msg_list, response);\n\n    furi_string_free(md5);\n    furi_string_free(md5_path);\n    storage_file_free(file);\n\n    storage_dir_close(dir);\n    storage_file_free(dir);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t session) {\n    furi_check(!MsgList_empty_p(expected_msg_list));\n    furi_check(session < TEST_RPC_SESSIONS);\n\n    rpc_session[session].timeout = furi_get_tick() + MAX_RECEIVE_OUTPUT_TIMEOUT;\n    pb_istream_t istream = {\n        .callback = test_rpc_pb_stream_read,\n        .state = &rpc_session[session],\n        .errmsg = NULL,\n        .bytes_left = 0x7FFFFFFF,\n    };\n    /* other fields explicitly initialized by 0 */\n    PB_Main result = {.cb_content.funcs.decode = NULL};\n\n    /* mlib adds msg_files into start of list, so reverse it */\n    MsgList_reverse(expected_msg_list);\n    for\n        M_EACH(expected_msg, expected_msg_list, MsgList_t) {\n            if(!pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {\n                mu_fail(\n                    \"not all expected messages decoded (maybe increase MAX_RECEIVE_OUTPUT_TIMEOUT)\");\n                break;\n            }\n\n            test_rpc_compare_messages(&result, expected_msg);\n            pb_release(&PB_Main_msg, &result);\n        }\n\n    rpc_session[session].timeout = furi_get_tick() + 50;\n    if(pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {\n        mu_fail(\"decoded more than expected\");\n    }\n    MsgList_reverse(expected_msg_list);\n}\n\nstatic void test_rpc_free_msg_list(MsgList_t msg_list) {\n    for\n        M_EACH(it, msg_list, MsgList_t) {\n            pb_release(&PB_Main_msg, it);\n        }\n    MsgList_clear(msg_list);\n}\n\nstatic void test_rpc_storage_list_run(\n    const char* path,\n    uint32_t command_id,\n    bool md5,\n    size_t filter_max_size) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_storage_list_request(&request, path, md5, command_id, filter_max_size);\n    if(!strcmp(path, \"/\")) {\n        test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);\n    } else {\n        test_rpc_storage_list_create_expected_list(\n            expected_msg_list, path, command_id, md5, filter_max_size);\n    }\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_list) {\n    test_rpc_storage_list_run(\"/\", ++command_id, false, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"nfc\"), ++command_id, false, 0);\n    test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false, 0);\n    test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"infrared\"), ++command_id, false, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"ibutton\"), ++command_id, false, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"lfrfid\"), ++command_id, false, 0);\n    test_rpc_storage_list_run(\"error_path\", ++command_id, false, 0);\n}\n\nMU_TEST(test_storage_list_md5) {\n    test_rpc_storage_list_run(\"/\", ++command_id, true, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"nfc\"), ++command_id, true, 0);\n    test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true, 0);\n    test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"infrared\"), ++command_id, true, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"ibutton\"), ++command_id, true, 0);\n    test_rpc_storage_list_run(EXT_PATH(\"lfrfid\"), ++command_id, true, 0);\n    test_rpc_storage_list_run(\"error_path\", ++command_id, true, 0);\n}\n\nMU_TEST(test_storage_list_size) {\n    test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 0);\n    test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1);\n    test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1000);\n    test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 2500);\n}\n\nstatic void\n    test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id) {\n    PB_Main* response = MsgList_push_new(msg_list);\n    response->command_id = command_id;\n    response->command_status = status;\n    response->cb_content.funcs.encode = NULL;\n    response->has_next = false;\n    response->which_content = PB_Main_empty_tag;\n}\n\nstatic void test_rpc_add_read_to_list_by_reading_real_file(\n    MsgList_t msg_list,\n    const char* path,\n    uint32_t command_id) {\n    furi_check(MsgList_empty_p(msg_list));\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    bool result = false;\n\n    if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        size_t size_left = storage_file_size(file);\n\n        do {\n            PB_Main* response = MsgList_push_new(msg_list);\n            response->command_id = command_id;\n            response->command_status = PB_CommandStatus_OK;\n            response->has_next = false;\n            response->which_content = PB_Main_storage_read_response_tag;\n            response->content.storage_read_response.has_file = true;\n\n            response->content.storage_read_response.file.data =\n                malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));\n            uint8_t* buffer = response->content.storage_read_response.file.data->bytes;\n            uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;\n            size_t read_size = MIN(size_left, MAX_DATA_SIZE);\n            *read_size_msg = storage_file_read(file, buffer, read_size);\n            size_left -= read_size;\n            result = (*read_size_msg == read_size);\n\n            if(result) {\n                response->has_next = (size_left > 0);\n            }\n        } while((size_left != 0) && result);\n\n        if(!result) {\n            test_rpc_add_empty_to_list(\n                msg_list, test_rpc_storage_get_file_error(file), command_id);\n        }\n    } else {\n        test_rpc_add_empty_to_list(msg_list, test_rpc_storage_get_file_error(file), command_id);\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void test_storage_read_run(const char* path, uint32_t command_id) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);\n    test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nstatic bool test_is_exists(const char* path) {\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    FileInfo fileinfo;\n    FS_Error result = storage_common_stat(fs_api, path, &fileinfo);\n    furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST));\n    furi_record_close(RECORD_STORAGE);\n    return result == FSE_OK;\n}\n\nstatic void test_create_dir(const char* path) {\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    FS_Error error = storage_common_mkdir(fs_api, path);\n    (void)error;\n    furi_check((error == FSE_OK) || (error == FSE_EXIST));\n    furi_record_close(RECORD_STORAGE);\n    furi_check(test_is_exists(path));\n}\n\nstatic void test_create_file(const char* path, size_t size) {\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n        uint8_t buf[128] = {0};\n        for(size_t i = 0; i < sizeof(buf); ++i) {\n            buf[i] = '0' + (i % 10);\n        }\n        while(size) {\n            size_t written = storage_file_write(file, buf, MIN(size, sizeof(buf)));\n            furi_check(written);\n            size -= written;\n        }\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n    furi_check(test_is_exists(path));\n}\n\nstatic void test_rpc_storage_info_run(const char* path, uint32_t command_id) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id);\n\n    PB_Main* response = MsgList_push_new(expected_msg_list);\n    response->command_id = command_id;\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n\n    FS_Error error = storage_common_fs_info(\n        fs_api,\n        path,\n        &response->content.storage_info_response.total_space,\n        &response->content.storage_info_response.free_space);\n\n    response->command_status = rpc_system_storage_get_error(error);\n    if(error == FSE_OK) {\n        response->which_content = PB_Main_storage_info_response_tag;\n    } else {\n        response->which_content = PB_Main_empty_tag;\n    }\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nstatic void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    FileInfo fileinfo;\n    FS_Error error = storage_common_stat(fs_api, path, &fileinfo);\n    furi_record_close(RECORD_STORAGE);\n\n    PB_Main* response = MsgList_push_new(expected_msg_list);\n    response->command_id = command_id;\n    response->command_status = rpc_system_storage_get_error(error);\n    response->has_next = false;\n    response->which_content = PB_Main_empty_tag;\n\n    if(error == FSE_OK) {\n        response->which_content = PB_Main_storage_stat_response_tag;\n        response->content.storage_stat_response.has_file = true;\n        response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ?\n                                                                PB_Storage_File_FileType_DIR :\n                                                                PB_Storage_File_FileType_FILE;\n        response->content.storage_stat_response.file.size = fileinfo.size;\n    }\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_info) {\n    test_rpc_storage_info_run(STORAGE_ANY_PATH_PREFIX, ++command_id);\n    test_rpc_storage_info_run(STORAGE_INT_PATH_PREFIX, ++command_id);\n    test_rpc_storage_info_run(STORAGE_EXT_PATH_PREFIX, ++command_id);\n}\n\n#define TEST_DIR_STAT_NAME TEST_DIR \"stat_dir\"\n#define TEST_DIR_STAT      TEST_DIR_STAT_NAME \"/\"\nMU_TEST(test_storage_stat) {\n    test_create_dir(TEST_DIR_STAT_NAME);\n    test_create_file(TEST_DIR_STAT \"empty.txt\", 0);\n    test_create_file(TEST_DIR_STAT \"l33t.txt\", 1337);\n\n    test_rpc_storage_stat_run(\"/\", ++command_id);\n    test_rpc_storage_stat_run(STORAGE_INT_PATH_PREFIX, ++command_id);\n    test_rpc_storage_stat_run(STORAGE_EXT_PATH_PREFIX, ++command_id);\n\n    test_rpc_storage_stat_run(TEST_DIR_STAT \"empty.txt\", ++command_id);\n    test_rpc_storage_stat_run(TEST_DIR_STAT \"l33t.txt\", ++command_id);\n    test_rpc_storage_stat_run(TEST_DIR_STAT \"missing\", ++command_id);\n    test_rpc_storage_stat_run(TEST_DIR_STAT_NAME, ++command_id);\n\n    test_rpc_storage_stat_run(TEST_DIR_STAT, ++command_id);\n}\n\nMU_TEST(test_storage_read) {\n    test_create_file(TEST_DIR \"empty.txt\", 0);\n    test_create_file(TEST_DIR \"file1.txt\", 1);\n    test_create_file(TEST_DIR \"file2.txt\", MAX_DATA_SIZE);\n    test_create_file(TEST_DIR \"file3.txt\", MAX_DATA_SIZE + 1);\n    test_create_file(TEST_DIR \"file4.txt\", (MAX_DATA_SIZE * 2) + 1);\n\n    test_storage_read_run(TEST_DIR \"empty.txt\", ++command_id);\n    test_storage_read_run(TEST_DIR \"file1.txt\", ++command_id);\n    test_storage_read_run(TEST_DIR \"file2.txt\", ++command_id);\n    test_storage_read_run(TEST_DIR \"file3.txt\", ++command_id);\n    test_storage_read_run(TEST_DIR \"file4.txt\", ++command_id);\n}\n\nstatic void test_storage_write_run(\n    const char* path,\n    size_t write_size,\n    size_t write_count,\n    uint32_t command_id,\n    PB_CommandStatus status) {\n    MsgList_t input_msg_list;\n    MsgList_init(input_msg_list);\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    uint8_t* buf = malloc(write_size);\n    for(size_t i = 0; i < write_size; ++i) {\n        buf[i] = '0' + (i % 10);\n    }\n\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list, WRITE_REQUEST, path, buf, write_size, write_count, command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n    test_rpc_encode_and_feed(input_msg_list, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(input_msg_list);\n    test_rpc_free_msg_list(expected_msg_list);\n\n    free(buf);\n}\n\nstatic void test_storage_write_read_run(\n    const char* path,\n    const uint8_t* pattern,\n    size_t pattern_size,\n    size_t pattern_repeats,\n    uint32_t* command_id) {\n    MsgList_t input_msg_list;\n    MsgList_init(input_msg_list);\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list, WRITE_REQUEST, path, pattern, pattern_size, pattern_repeats, ++*command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);\n\n    test_rpc_create_simple_message(\n        MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);\n    test_rpc_add_read_or_write_to_list(\n        expected_msg_list,\n        READ_RESPONSE,\n        path,\n        pattern,\n        pattern_size,\n        pattern_repeats,\n        *command_id);\n\n    test_rpc_print_message_list(input_msg_list);\n    test_rpc_print_message_list(expected_msg_list);\n\n    test_rpc_encode_and_feed(input_msg_list, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(input_msg_list);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_write_read) {\n    uint8_t pattern1[] = \"abcdefgh\";\n    test_storage_write_read_run(TEST_DIR \"test1.txt\", pattern1, sizeof(pattern1), 1, &command_id);\n    test_storage_write_read_run(TEST_DIR \"test2.txt\", pattern1, 1, 1, &command_id);\n    test_storage_write_read_run(TEST_DIR \"test3.txt\", pattern1, 0, 1, &command_id);\n}\n\nMU_TEST(test_storage_write) {\n    test_storage_write_run(\n        TEST_DIR \"afaefo/aefaef/aef/aef/test1.txt\",\n        1,\n        1,\n        ++command_id,\n        PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);\n    test_storage_write_run(TEST_DIR \"test1.txt\", 100, 1, ++command_id, PB_CommandStatus_OK);\n    test_storage_write_run(TEST_DIR \"test2.txt\", 100, 3, ++command_id, PB_CommandStatus_OK);\n    test_storage_write_run(TEST_DIR \"test1.txt\", 100, 3, ++command_id, PB_CommandStatus_OK);\n    test_storage_write_run(TEST_DIR \"test2.txt\", 100, 3, ++command_id, PB_CommandStatus_OK);\n    test_storage_write_run(\n        TEST_DIR \"afaefo/aefaef/aef/aef/test1.txt\",\n        1,\n        1,\n        ++command_id,\n        PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);\n    test_storage_write_run(TEST_DIR \"test2.txt\", 1, 50, ++command_id, PB_CommandStatus_OK);\n    test_storage_write_run(TEST_DIR \"test2.txt\", 512, 3, ++command_id, PB_CommandStatus_OK);\n}\n\nMU_TEST(test_storage_interrupt_continuous_same_system) {\n    MsgList_t input_msg_list;\n    MsgList_init(input_msg_list);\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    uint8_t pattern[16] = {0};\n\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list,\n        WRITE_REQUEST,\n        TEST_DIR \"test1.txt\",\n        pattern,\n        sizeof(pattern),\n        3,\n        command_id);\n\n    /* replace last packet (has_next == false) with another command */\n    PB_Main message_to_remove;\n    MsgList_pop_back(&message_to_remove, input_msg_list);\n    pb_release(&PB_Main_msg, &message_to_remove);\n    test_rpc_create_simple_message(\n        MsgList_push_new(input_msg_list),\n        PB_Main_storage_mkdir_request_tag,\n        TEST_DIR \"dir1\",\n        command_id + 1);\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list,\n        WRITE_REQUEST,\n        TEST_DIR \"test2.txt\",\n        pattern,\n        sizeof(pattern),\n        3,\n        command_id);\n\n    test_rpc_add_empty_to_list(\n        expected_msg_list, PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED, command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 1);\n\n    test_rpc_encode_and_feed(input_msg_list, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(input_msg_list);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_interrupt_continuous_another_system) {\n    MsgList_t input_msg_list;\n    MsgList_init(input_msg_list);\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    uint8_t pattern[16] = {0};\n\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list,\n        WRITE_REQUEST,\n        TEST_DIR \"test1.txt\",\n        pattern,\n        sizeof(pattern),\n        3,\n        command_id);\n\n    PB_Main message = {\n        .command_id = command_id + 1,\n        .command_status = PB_CommandStatus_OK,\n        .cb_content.funcs.encode = NULL,\n        .has_next = false,\n        .which_content = PB_Main_system_ping_request_tag,\n    };\n\n    MsgList_it_t it;\n    MsgList_it(it, input_msg_list);\n    MsgList_next(it);\n    MsgList_insert(input_msg_list, it, message);\n\n    test_rpc_add_read_or_write_to_list(\n        input_msg_list,\n        WRITE_REQUEST,\n        TEST_DIR \"test2.txt\",\n        pattern,\n        sizeof(pattern),\n        3,\n        command_id + 2);\n\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, command_id + 1);\n    test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 2);\n\n    test_rpc_encode_and_feed(input_msg_list, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(input_msg_list);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nstatic void test_storage_delete_run(\n    const char* path,\n    size_t command_id,\n    PB_CommandStatus status,\n    bool recursive) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);\n    request.content.storage_delete_request.recursive = recursive;\n    test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\n#define TEST_DIR_RMRF_NAME TEST_DIR \"rmrf_test\"\n#define TEST_DIR_RMRF      TEST_DIR_RMRF_NAME \"/\"\nMU_TEST(test_storage_delete_recursive) {\n    test_create_dir(TEST_DIR_RMRF_NAME);\n\n    test_create_dir(TEST_DIR_RMRF \"dir1\");\n    test_create_file(TEST_DIR_RMRF \"dir1/file1\", 1);\n\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir2\");\n    test_create_file(TEST_DIR_RMRF \"dir1/dir2/file1\", 1);\n    test_create_file(TEST_DIR_RMRF \"dir1/dir2/file2\", 1);\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir3\");\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir3/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir3/dir1/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir3/dir1/dir1/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir1/dir3/dir1/dir1/dir1/dir1\");\n\n    test_create_dir(TEST_DIR_RMRF \"dir2\");\n    test_create_dir(TEST_DIR_RMRF \"dir2/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir2/dir2\");\n    test_create_file(TEST_DIR_RMRF \"dir2/dir2/file1\", 1);\n\n    test_create_dir(TEST_DIR_RMRF \"dir2/dir2/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir2/dir2/dir1/dir1\");\n    test_create_dir(TEST_DIR_RMRF \"dir2/dir2/dir1/dir1/dir1\");\n    test_create_file(TEST_DIR_RMRF \"dir2/dir2/dir1/dir1/dir1/file1\", 1);\n\n    test_storage_delete_run(\n        TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, false);\n    mu_check(test_is_exists(TEST_DIR_RMRF_NAME));\n    test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);\n    mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));\n    test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, false);\n    mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));\n\n    test_create_dir(TEST_DIR_RMRF_NAME);\n    test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);\n    mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));\n\n    test_create_dir(TEST_DIR \"file1\");\n    test_storage_delete_run(TEST_DIR \"file1\", ++command_id, PB_CommandStatus_OK, true);\n    mu_check(!test_is_exists(TEST_DIR \"file1\"));\n}\n\nMU_TEST(test_storage_delete) {\n    test_storage_delete_run(NULL, ++command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS, false);\n\n    furi_check(!test_is_exists(TEST_DIR \"empty.txt\"));\n    test_storage_delete_run(TEST_DIR \"empty.txt\", ++command_id, PB_CommandStatus_OK, false);\n    mu_check(!test_is_exists(TEST_DIR \"empty.txt\"));\n\n    test_create_file(TEST_DIR \"empty.txt\", 0);\n    test_storage_delete_run(TEST_DIR \"empty.txt\", ++command_id, PB_CommandStatus_OK, false);\n    mu_check(!test_is_exists(TEST_DIR \"empty.txt\"));\n\n    furi_check(!test_is_exists(TEST_DIR \"dir1\"));\n    test_create_dir(TEST_DIR \"dir1\");\n    test_storage_delete_run(TEST_DIR \"dir1\", ++command_id, PB_CommandStatus_OK, false);\n    mu_check(!test_is_exists(TEST_DIR \"dir1\"));\n\n    test_storage_delete_run(TEST_DIR \"dir1\", ++command_id, PB_CommandStatus_OK, false);\n    mu_check(!test_is_exists(TEST_DIR \"dir1\"));\n}\n\nstatic void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_mkdir) {\n    furi_check(!test_is_exists(TEST_DIR \"dir1\"));\n    test_storage_mkdir_run(TEST_DIR \"dir1\", ++command_id, PB_CommandStatus_OK);\n    mu_check(test_is_exists(TEST_DIR \"dir1\"));\n\n    test_storage_mkdir_run(TEST_DIR \"dir1\", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);\n    mu_check(test_is_exists(TEST_DIR \"dir1\"));\n\n    furi_check(!test_is_exists(TEST_DIR \"dir2\"));\n    test_create_dir(TEST_DIR \"dir2\");\n    test_storage_mkdir_run(TEST_DIR \"dir2\", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);\n    mu_check(test_is_exists(TEST_DIR \"dir2\"));\n}\n\nstatic void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) {\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n    FuriString* md5 = furi_string_alloc();\n\n    if(md5_string_calc_file(file, path, md5, NULL)) {\n        snprintf(md5sum, md5sum_size, \"%s\", furi_string_get_cstr(md5));\n    } else {\n        furi_check(0);\n    }\n\n    furi_string_free(md5);\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void test_storage_md5sum_run(\n    const char* path,\n    uint32_t command_id,\n    const char* md5sum,\n    PB_CommandStatus status) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);\n    if(status == PB_CommandStatus_OK) {\n        PB_Main* response = MsgList_push_new(expected_msg_list);\n        test_rpc_create_simple_message(\n            response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);\n        response->command_status = status;\n    } else {\n        test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n    }\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_md5sum) {\n    char md5sum1[MD5SUM_SIZE * 2 + 1] = {0};\n    char md5sum2[MD5SUM_SIZE * 2 + 1] = {0};\n    char md5sum3[MD5SUM_SIZE * 2 + 1] = {0};\n\n    test_storage_md5sum_run(\n        TEST_DIR \"test1.txt\", ++command_id, \"\", PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);\n\n    test_create_file(TEST_DIR \"file1.txt\", 0);\n    test_create_file(TEST_DIR \"file2.txt\", 1);\n    test_create_file(TEST_DIR \"file3.txt\", 512);\n    test_storage_calculate_md5sum(TEST_DIR \"file1.txt\", md5sum1, MD5SUM_SIZE * 2 + 1);\n    test_storage_calculate_md5sum(TEST_DIR \"file2.txt\", md5sum2, MD5SUM_SIZE * 2 + 1);\n    test_storage_calculate_md5sum(TEST_DIR \"file3.txt\", md5sum3, MD5SUM_SIZE * 2 + 1);\n\n    test_storage_md5sum_run(TEST_DIR \"file1.txt\", ++command_id, md5sum1, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file1.txt\", ++command_id, md5sum1, PB_CommandStatus_OK);\n\n    test_storage_md5sum_run(TEST_DIR \"file2.txt\", ++command_id, md5sum2, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file2.txt\", ++command_id, md5sum2, PB_CommandStatus_OK);\n\n    test_storage_md5sum_run(TEST_DIR \"file3.txt\", ++command_id, md5sum3, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file3.txt\", ++command_id, md5sum3, PB_CommandStatus_OK);\n\n    test_storage_md5sum_run(TEST_DIR \"file2.txt\", ++command_id, md5sum2, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file3.txt\", ++command_id, md5sum3, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file1.txt\", ++command_id, md5sum1, PB_CommandStatus_OK);\n    test_storage_md5sum_run(TEST_DIR \"file2.txt\", ++command_id, md5sum2, PB_CommandStatus_OK);\n}\n\nstatic void test_rpc_storage_rename_run(\n    const char* old_path,\n    const char* new_path,\n    uint32_t command_id,\n    PB_CommandStatus status) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    char* str_old_path = strdup(old_path);\n    char* str_new_path = strdup(new_path);\n\n    request.command_id = command_id;\n    request.command_status = PB_CommandStatus_OK;\n    request.cb_content.funcs.encode = NULL;\n    request.which_content = PB_Main_storage_rename_request_tag;\n    request.has_next = false;\n    request.content.storage_rename_request.old_path = str_old_path;\n    request.content.storage_rename_request.new_path = str_new_path;\n\n    test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_storage_rename) {\n    test_rpc_storage_rename_run(\"\", \"\", ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME);\n\n    furi_check(!test_is_exists(TEST_DIR \"empty.txt\"));\n    test_create_file(TEST_DIR \"empty.txt\", 0);\n    test_rpc_storage_rename_run(\n        TEST_DIR \"empty.txt\", TEST_DIR \"empty2.txt\", ++command_id, PB_CommandStatus_OK);\n    mu_check(!test_is_exists(TEST_DIR \"empty.txt\"));\n    mu_check(test_is_exists(TEST_DIR \"empty2.txt\"));\n\n    furi_check(!test_is_exists(TEST_DIR \"dir1\"));\n    test_create_dir(TEST_DIR \"dir1\");\n    test_rpc_storage_rename_run(\n        TEST_DIR \"dir1\", TEST_DIR \"dir2\", ++command_id, PB_CommandStatus_OK);\n    mu_check(!test_is_exists(TEST_DIR \"dir1\"));\n    mu_check(test_is_exists(TEST_DIR \"dir2\"));\n}\n\nMU_TEST(test_ping) {\n    MsgList_t input_msg_list;\n    MsgList_init(input_msg_list);\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 500);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, (uint32_t)-1);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 700);\n    test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);\n\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 500);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, (uint32_t)-1);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 700);\n    test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);\n\n    test_rpc_encode_and_feed(input_msg_list, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(input_msg_list);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_system_protobuf_version) {\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    PB_Main request;\n    request.command_id = ++command_id;\n    request.command_status = PB_CommandStatus_OK;\n    request.cb_content.funcs.decode = NULL;\n    request.has_next = false;\n    request.which_content = PB_Main_system_protobuf_version_request_tag;\n\n    PB_Main* response = MsgList_push_new(expected_msg_list);\n    response->command_id = command_id;\n    response->command_status = PB_CommandStatus_OK;\n    response->cb_content.funcs.encode = NULL;\n    response->has_next = false;\n    response->which_content = PB_Main_system_protobuf_version_response_tag;\n    response->content.system_protobuf_version_response.major = PROTOBUF_MAJOR_VERSION;\n    response->content.system_protobuf_version_response.minor = PROTOBUF_MINOR_VERSION;\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST_SUITE(test_rpc_system) {\n    MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);\n\n    MU_RUN_TEST(test_ping);\n    MU_RUN_TEST(test_system_protobuf_version);\n}\n\nMU_TEST_SUITE(test_rpc_storage) {\n    MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);\n\n    MU_RUN_TEST(test_storage_info);\n    MU_RUN_TEST(test_storage_stat);\n    MU_RUN_TEST(test_storage_list);\n    MU_RUN_TEST(test_storage_list_md5);\n    MU_RUN_TEST(test_storage_list_size);\n    MU_RUN_TEST(test_storage_read);\n    MU_RUN_TEST(test_storage_write_read);\n    MU_RUN_TEST(test_storage_write);\n    MU_RUN_TEST(test_storage_delete);\n    MU_RUN_TEST(test_storage_delete_recursive);\n    MU_RUN_TEST(test_storage_mkdir);\n    MU_RUN_TEST(test_storage_md5sum);\n    MU_RUN_TEST(test_storage_rename);\n\n    DISABLE_TEST(MU_RUN_TEST(test_storage_interrupt_continuous_same_system););\n    MU_RUN_TEST(test_storage_interrupt_continuous_another_system);\n}\n\nstatic void test_app_create_request(\n    PB_Main* request,\n    const char* app_name,\n    const char* app_args,\n    uint32_t command_id) {\n    request->command_id = command_id;\n    request->command_status = PB_CommandStatus_OK;\n    request->cb_content.funcs.encode = NULL;\n    request->which_content = PB_Main_app_start_request_tag;\n    request->has_next = false;\n\n    if(app_name) {\n        char* msg_app_name = strdup(app_name);\n        request->content.app_start_request.name = msg_app_name;\n    } else {\n        request->content.app_start_request.name = NULL;\n    }\n\n    if(app_args) {\n        char* msg_app_args = strdup(app_args);\n        request->content.app_start_request.args = msg_app_args;\n    } else {\n        request->content.app_start_request.args = NULL;\n    }\n}\n\nstatic void test_app_start_run(\n    const char* app_name,\n    const char* app_args,\n    PB_CommandStatus status,\n    uint32_t command_id) {\n    PB_Main request;\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n\n    test_app_create_request(&request, app_name, app_args, command_id);\n    test_rpc_add_empty_to_list(expected_msg_list, status, command_id);\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nstatic void test_app_get_status_lock_run(bool locked_expected, uint32_t command_id) {\n    PB_Main request = {\n        .command_id = command_id,\n        .command_status = PB_CommandStatus_OK,\n        .which_content = PB_Main_app_lock_status_request_tag,\n        .has_next = false,\n    };\n\n    MsgList_t expected_msg_list;\n    MsgList_init(expected_msg_list);\n    PB_Main* response = MsgList_push_new(expected_msg_list);\n    response->command_id = command_id;\n    response->command_status = PB_CommandStatus_OK;\n    response->which_content = PB_Main_app_lock_status_response_tag;\n    response->has_next = false;\n    response->content.app_lock_status_response.locked = locked_expected;\n\n    test_rpc_encode_and_feed_one(&request, 0);\n    test_rpc_decode_and_compare(expected_msg_list, 0);\n\n    pb_release(&PB_Main_msg, &request);\n    test_rpc_free_msg_list(expected_msg_list);\n}\n\nMU_TEST(test_app_start_and_lock_status) {\n    test_app_get_status_lock_run(false, ++command_id);\n    test_app_start_run(\n        NULL, EXT_PATH(\"file\"), PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);\n    test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);\n    test_app_get_status_lock_run(false, ++command_id);\n    test_app_start_run(\n        \"skynet_destroy_world_app\", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);\n    test_app_get_status_lock_run(false, ++command_id);\n\n    test_app_start_run(\"Delay Test\", \"0\", PB_CommandStatus_OK, ++command_id);\n    furi_delay_ms(100);\n    test_app_get_status_lock_run(false, ++command_id);\n\n    test_app_start_run(\"Delay Test\", \"200\", PB_CommandStatus_OK, ++command_id);\n    test_app_get_status_lock_run(true, ++command_id);\n    furi_delay_ms(100);\n    test_app_get_status_lock_run(true, ++command_id);\n    test_app_start_run(\"Delay Test\", \"0\", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);\n    furi_delay_ms(200);\n    test_app_get_status_lock_run(false, ++command_id);\n\n    test_app_start_run(\"Delay Test\", \"500\", PB_CommandStatus_OK, ++command_id);\n    furi_delay_ms(100);\n    test_app_get_status_lock_run(true, ++command_id);\n    test_app_start_run(\"Infrared\", \"0\", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);\n    furi_delay_ms(100);\n    test_app_get_status_lock_run(true, ++command_id);\n    test_app_start_run(\n        \"2_girls_1_app\", \"0\", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);\n    furi_delay_ms(100);\n    test_app_get_status_lock_run(true, ++command_id);\n    furi_delay_ms(500);\n    test_app_get_status_lock_run(false, ++command_id);\n}\n\nMU_TEST_SUITE(test_rpc_app) {\n    MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);\n\n    DISABLE_TEST(MU_RUN_TEST(test_app_start_and_lock_status););\n}\n\nstatic void\n    test_send_rubbish(RpcSession* session, const char* pattern, size_t pattern_size, size_t size) {\n    UNUSED(session);\n    uint8_t* buf = malloc(size);\n    for(size_t i = 0; i < size; ++i) {\n        buf[i] = pattern[i % pattern_size];\n    }\n\n    size_t bytes_sent = rpc_session_feed(rpc_session[0].session, buf, size, 1000);\n    furi_check(bytes_sent == size);\n    free(buf);\n}\n\nstatic void test_rpc_feed_rubbish_run(\n    MsgList_t input_before,\n    MsgList_t input_after,\n    MsgList_t expected,\n    const char* pattern,\n    size_t pattern_size,\n    size_t size) {\n    test_rpc_setup();\n\n    test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_DECODE, 0);\n\n    furi_check(api_lock_is_locked(rpc_session[0].session_close_lock));\n    test_rpc_encode_and_feed(input_before, 0);\n    test_send_rubbish(rpc_session[0].session, pattern, pattern_size, size);\n    test_rpc_encode_and_feed(input_after, 0);\n\n    test_rpc_decode_and_compare(expected, 0);\n\n    test_rpc_teardown();\n}\n\n#define RUN_TEST_RPC_FEED_RUBBISH(ib, ia, e, b, c) \\\n    test_rpc_feed_rubbish_run(ib, ia, e, b, sizeof(b), c)\n\n#define INIT_LISTS()            \\\n    MsgList_init(input_before); \\\n    MsgList_init(input_after);  \\\n    MsgList_init(expected);\n\n#define FREE_LISTS()                      \\\n    test_rpc_free_msg_list(input_before); \\\n    test_rpc_free_msg_list(input_after);  \\\n    test_rpc_free_msg_list(expected);\n\nMU_TEST(test_rpc_feed_rubbish) {\n    MsgList_t input_before;\n    MsgList_t input_after;\n    MsgList_t expected;\n\n    INIT_LISTS();\n    // input is empty\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x12\\x30rubbi\\x42sh\", 50);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x2\\x2\\x2\\x5\\x99\\x1\", 30);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_after, PING_REQUEST, ++command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x12\\x30rubbi\\x42sh\", 50);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);\n    test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);\n    test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x99\\x2\\x2\\x5\\x99\\x1\", 300);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_after, PING_REQUEST, ++command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x1\\x99\\x2\\x5\\x99\\x1\", 300);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);\n    test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x2\\x2\\x2\\x5\\x99\\x1\", 30);\n    FREE_LISTS();\n\n    INIT_LISTS();\n    test_rpc_add_ping_to_list(input_before, PING_RESPONSE, ++command_id);\n    test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_NOT_IMPLEMENTED, command_id);\n    test_rpc_add_ping_to_list(input_before, PING_RESPONSE, ++command_id);\n    test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_NOT_IMPLEMENTED, command_id);\n    RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, \"\\x12\\x30rubbi\\x42sh\", 50);\n    FREE_LISTS();\n}\n\nMU_TEST(test_rpc_multisession_ping) {\n    MsgList_t input_0;\n    MsgList_init(input_0);\n    MsgList_t input_1;\n    MsgList_init(input_1);\n    MsgList_t expected_0;\n    MsgList_init(expected_0);\n    MsgList_t expected_1;\n    MsgList_init(expected_1);\n\n    test_rpc_setup();\n\n    test_rpc_setup_second_session();\n    test_rpc_teardown_second_session();\n\n    test_rpc_setup_second_session();\n\n    test_rpc_add_ping_to_list(input_0, PING_REQUEST, 0);\n    test_rpc_add_ping_to_list(input_1, PING_REQUEST, 1);\n    test_rpc_add_ping_to_list(expected_0, PING_RESPONSE, 0);\n    test_rpc_add_ping_to_list(expected_1, PING_RESPONSE, 1);\n\n    test_rpc_encode_and_feed(input_0, 0);\n    test_rpc_encode_and_feed(input_1, 1);\n    test_rpc_decode_and_compare(expected_0, 0);\n    test_rpc_decode_and_compare(expected_1, 1);\n\n    test_rpc_free_msg_list(input_0);\n    test_rpc_free_msg_list(input_1);\n    test_rpc_free_msg_list(expected_0);\n    test_rpc_free_msg_list(expected_1);\n\n    test_rpc_teardown_second_session();\n    test_rpc_teardown();\n}\n\nMU_TEST(test_rpc_multisession_storage) {\n    MsgList_t input_0;\n    MsgList_init(input_0);\n    MsgList_t input_1;\n    MsgList_init(input_1);\n    MsgList_t expected_0;\n    MsgList_init(expected_0);\n    MsgList_t expected_1;\n    MsgList_init(expected_1);\n\n    test_rpc_storage_setup();\n    test_rpc_setup_second_session();\n\n    uint8_t pattern[16] = \"0123456789abcdef\";\n\n    test_rpc_add_read_or_write_to_list(\n        input_0, WRITE_REQUEST, TEST_DIR \"file0.txt\", pattern, sizeof(pattern), 1, ++command_id);\n    test_rpc_add_empty_to_list(expected_0, PB_CommandStatus_OK, command_id);\n\n    test_rpc_add_read_or_write_to_list(\n        input_1, WRITE_REQUEST, TEST_DIR \"file1.txt\", pattern, sizeof(pattern), 1, ++command_id);\n    test_rpc_add_empty_to_list(expected_1, PB_CommandStatus_OK, command_id);\n\n    test_rpc_create_simple_message(\n        MsgList_push_raw(input_0),\n        PB_Main_storage_read_request_tag,\n        TEST_DIR \"file0.txt\",\n        ++command_id);\n    test_rpc_add_read_or_write_to_list(\n        expected_0, READ_RESPONSE, TEST_DIR \"file0.txt\", pattern, sizeof(pattern), 1, command_id);\n\n    test_rpc_create_simple_message(\n        MsgList_push_raw(input_1),\n        PB_Main_storage_read_request_tag,\n        TEST_DIR \"file1.txt\",\n        ++command_id);\n    test_rpc_add_read_or_write_to_list(\n        expected_1, READ_RESPONSE, TEST_DIR \"file1.txt\", pattern, sizeof(pattern), 1, command_id);\n\n    test_rpc_print_message_list(input_0);\n    test_rpc_print_message_list(input_1);\n    test_rpc_print_message_list(expected_0);\n    test_rpc_print_message_list(expected_1);\n\n    test_rpc_encode_and_feed(input_0, 0);\n    test_rpc_encode_and_feed(input_1, 1);\n\n    test_rpc_decode_and_compare(expected_0, 0);\n    test_rpc_decode_and_compare(expected_1, 1);\n\n    test_rpc_free_msg_list(input_0);\n    test_rpc_free_msg_list(input_1);\n    test_rpc_free_msg_list(expected_0);\n    test_rpc_free_msg_list(expected_1);\n\n    test_rpc_teardown_second_session();\n    test_rpc_storage_teardown();\n}\n\nMU_TEST_SUITE(test_rpc_session) {\n    MU_RUN_TEST(test_rpc_feed_rubbish);\n    MU_RUN_TEST(test_rpc_multisession_ping);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_E(TAG, \"SD card not mounted - skip storage tests\");\n    } else {\n        MU_RUN_TEST(test_rpc_multisession_storage);\n    }\n    furi_record_close(RECORD_STORAGE);\n}\n\nint run_minunit_test_rpc(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_E(TAG, \"SD card not mounted - skip storage tests\");\n    } else {\n        MU_RUN_SUITE(test_rpc_storage);\n    }\n    furi_record_close(RECORD_STORAGE);\n    MU_RUN_SUITE(test_rpc_system);\n    MU_RUN_SUITE(test_rpc_app);\n    MU_RUN_SUITE(test_rpc_session);\n\n    return MU_EXIT_CODE;\n}\n\nint32_t delay_test_app(void* p) {\n    int timeout = atoi((const char*)p);\n\n    if(timeout > 0) {\n        furi_delay_ms(timeout);\n    }\n\n    return 0;\n}\n\nTEST_API_DEFINE(run_minunit_test_rpc)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/storage/storage_test.c",
    "content": "#include \"../test.h\" // IWYU pragma: keep\n#include <furi.h>\n#include <storage/storage.h>\n\n// DO NOT USE THIS IN PRODUCTION CODE\n// This is a hack to access internal storage functions and definitions\n#include <storage/storage_i.h>\n\n#define UNIT_TESTS_RESOURCES_PATH(path) EXT_PATH(\"unit_tests/\" path)\n#define UNIT_TESTS_PATH(path)           EXT_PATH(\".tmp/unit_tests/\" path)\n\n#define STORAGE_LOCKED_FILE UNIT_TESTS_PATH(\"locked_file.test\")\n#define STORAGE_LOCKED_DIR  STORAGE_INT_PATH_PREFIX\n\n#define STORAGE_TEST_DIR UNIT_TESTS_PATH(\"test_dir\")\n\nstatic bool storage_file_create(Storage* storage, const char* path, const char* data) {\n    File* file = storage_file_alloc(storage);\n    bool result = false;\n    do {\n        if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_NEW)) {\n            break;\n        }\n\n        if(storage_file_write(file, data, strlen(data)) != strlen(data)) {\n            break;\n        }\n\n        if(!storage_file_close(file)) {\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    storage_file_free(file);\n    return result;\n}\n\nstatic void storage_file_open_lock_setup(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n    storage_simply_remove(storage, STORAGE_LOCKED_FILE);\n    mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW));\n    mu_check(storage_file_write(file, \"0123\", 4) == 4);\n    mu_check(storage_file_close(file));\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_file_open_lock_teardown(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE));\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic int32_t storage_file_locker(void* ctx) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriSemaphore* semaphore = ctx;\n    File* file = storage_file_alloc(storage);\n    furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));\n    furi_semaphore_release(semaphore);\n    furi_delay_ms(1000);\n\n    furi_check(storage_file_close(file));\n    furi_record_close(RECORD_STORAGE);\n    storage_file_free(file);\n    return 0;\n}\n\nMU_TEST(storage_file_open_lock) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);\n    File* file = storage_file_alloc(storage);\n\n    // file_locker thread start\n    FuriThread* locker_thread =\n        furi_thread_alloc_ex(\"StorageFileLocker\", 2048, storage_file_locker, semaphore);\n    furi_thread_start(locker_thread);\n\n    // wait for file lock\n    furi_semaphore_acquire(semaphore, FuriWaitForever);\n    furi_semaphore_free(semaphore);\n\n    result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING);\n    storage_file_close(file);\n\n    // file_locker thread stop\n    mu_check(furi_thread_join(locker_thread));\n    furi_thread_free(locker_thread);\n\n    // clean data\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    mu_assert(result, \"cannot open locked file\");\n}\n\nMU_TEST(storage_file_open_close) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file;\n\n    file = storage_file_alloc(storage);\n    mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));\n    storage_file_close(file);\n    storage_file_free(file);\n\n    for(size_t i = 0; i < 10; i++) {\n        file = storage_file_alloc(storage);\n        mu_check(\n            storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));\n        storage_file_free(file);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic bool storage_file_read_write_test(File* file, uint8_t* data, size_t test_size) {\n    const char* filename = UNIT_TESTS_PATH(\"storage_chunk.test\");\n\n    // fill with pattern\n    for(size_t i = 0; i < test_size; i++) {\n        data[i] = (i % 113);\n    }\n\n    bool result = false;\n    do {\n        if(!storage_file_open(file, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;\n        if(test_size != storage_file_write(file, data, test_size)) break;\n        storage_file_close(file);\n\n        // reset data\n        memset(data, 0, test_size);\n\n        if(!storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        if(test_size != storage_file_read(file, data, test_size)) break;\n        storage_file_close(file);\n\n        // check that data is correct\n        for(size_t i = 0; i < test_size; i++) {\n            if(data[i] != (i % 113)) {\n                break;\n            }\n        }\n\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nMU_TEST(storage_file_read_write_64k) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    size_t size_1k = 1024;\n    size_t size_64k = size_1k + size_1k * 63;\n    size_t size_65k = size_64k + size_1k;\n    size_t size_max = size_65k + 8;\n\n    size_t max_ram_block = memmgr_heap_get_max_free_block();\n\n    if(max_ram_block < size_max) {\n        mu_warn(\"Not enough RAM for >64k block test\");\n    } else {\n        uint8_t* data = malloc(size_max);\n        mu_check(storage_file_read_write_test(file, data, size_1k));\n        mu_check(storage_file_read_write_test(file, data, size_64k));\n        mu_check(storage_file_read_write_test(file, data, size_65k));\n        mu_check(storage_file_read_write_test(file, data, size_max));\n        free(data);\n    }\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(storage_file) {\n    storage_file_open_lock_setup();\n    MU_RUN_TEST(storage_file_open_close);\n    MU_RUN_TEST(storage_file_open_lock);\n    storage_file_open_lock_teardown();\n}\n\nMU_TEST_SUITE(storage_file_64k) {\n    MU_RUN_TEST(storage_file_read_write_64k);\n}\n\nMU_TEST(storage_dir_open_close) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file;\n\n    file = storage_file_alloc(storage);\n    mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));\n    storage_dir_close(file);\n    storage_file_free(file);\n\n    for(size_t i = 0; i < 10; i++) {\n        file = storage_file_alloc(storage);\n        mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));\n        storage_file_free(file);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic int32_t storage_dir_locker(void* ctx) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriSemaphore* semaphore = ctx;\n    File* file = storage_file_alloc(storage);\n    furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));\n    furi_semaphore_release(semaphore);\n    furi_delay_ms(100);\n\n    furi_check(storage_dir_close(file));\n    furi_record_close(RECORD_STORAGE);\n    storage_file_free(file);\n    return 0;\n}\n\nMU_TEST(storage_dir_open_lock) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = false;\n    FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);\n    File* file = storage_file_alloc(storage);\n\n    // file_locker thread start\n    FuriThread* locker_thread =\n        furi_thread_alloc_ex(\"StorageDirLocker\", 2048, storage_dir_locker, semaphore);\n    furi_thread_start(locker_thread);\n\n    // wait for dir lock\n    furi_semaphore_acquire(semaphore, FuriWaitForever);\n    furi_semaphore_free(semaphore);\n\n    result = storage_dir_open(file, STORAGE_LOCKED_DIR);\n    storage_dir_close(file);\n\n    // file_locker thread stop\n    mu_check(furi_thread_join(locker_thread));\n    furi_thread_free(locker_thread);\n\n    // clean data\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    mu_assert(result, \"cannot open locked dir\");\n}\n\nMU_TEST(storage_dir_exists_test) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    mu_check(!storage_dir_exists(storage, STORAGE_TEST_DIR));\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, STORAGE_TEST_DIR));\n    mu_check(storage_dir_exists(storage, STORAGE_TEST_DIR));\n    mu_assert_int_eq(FSE_OK, storage_common_remove(storage, STORAGE_TEST_DIR));\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(storage_dir) {\n    MU_RUN_TEST(storage_dir_open_close);\n    MU_RUN_TEST(storage_dir_open_lock);\n    MU_RUN_TEST(storage_dir_exists_test);\n}\n\nstatic const char* const storage_copy_test_paths[] = {\n    \"1\",\n    \"11\",\n    \"111\",\n    \"1/2\",\n    \"1/22\",\n    \"1/222\",\n    \"11/1\",\n    \"111/2\",\n    \"111/22\",\n    \"111/22/33\",\n};\n\nstatic const char* const storage_copy_test_files[] = {\n    \"file.test\",\n    \"1/file.test\",\n    \"111/22/33/file.test\",\n};\n\nstatic bool write_file_13DA(Storage* storage, const char* path) {\n    File* file = storage_file_alloc(storage);\n    bool result = false;\n    if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n        result = storage_file_write(file, \"13DA\", 4) == 4;\n    }\n    storage_file_close(file);\n    storage_file_free(file);\n\n    return result;\n}\n\nstatic bool check_file_13DA(Storage* storage, const char* path) {\n    File* file = storage_file_alloc(storage);\n    bool result = false;\n    if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        char data[10] = {0};\n        result = storage_file_read(file, data, 4) == 4;\n        if(result) {\n            result = memcmp(data, \"13DA\", 4) == 0;\n        }\n    }\n    storage_file_close(file);\n    storage_file_free(file);\n\n    return result;\n}\n\nstatic void storage_dir_create(Storage* storage, const char* base) {\n    FuriString* path;\n    path = furi_string_alloc();\n\n    storage_common_mkdir(storage, base);\n\n    for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {\n        furi_string_printf(path, \"%s/%s\", base, storage_copy_test_paths[i]);\n        storage_common_mkdir(storage, furi_string_get_cstr(path));\n    }\n\n    for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {\n        furi_string_printf(path, \"%s/%s\", base, storage_copy_test_files[i]);\n        write_file_13DA(storage, furi_string_get_cstr(path));\n    }\n\n    furi_string_free(path);\n}\n\nstatic void storage_dir_remove(Storage* storage, const char* base) {\n    storage_simply_remove_recursive(storage, base);\n}\n\nstatic bool storage_dir_rename_check(Storage* storage, const char* base) {\n    bool result = false;\n    FuriString* path;\n    path = furi_string_alloc();\n\n    result = (storage_common_stat(storage, base, NULL) == FSE_OK);\n\n    if(result) {\n        for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {\n            furi_string_printf(path, \"%s/%s\", base, storage_copy_test_paths[i]);\n            result = (storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK);\n            if(!result) {\n                break;\n            }\n        }\n    }\n\n    if(result) {\n        for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {\n            furi_string_printf(path, \"%s/%s\", base, storage_copy_test_files[i]);\n            result = check_file_13DA(storage, furi_string_get_cstr(path));\n            if(!result) {\n                break;\n            }\n        }\n    }\n\n    furi_string_free(path);\n    return result;\n}\n\nMU_TEST(storage_file_rename) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    mu_check(write_file_13DA(storage, UNIT_TESTS_PATH(\"file.old\")));\n    mu_check(check_file_13DA(storage, UNIT_TESTS_PATH(\"file.old\")));\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_rename(storage, UNIT_TESTS_PATH(\"file.old\"), UNIT_TESTS_PATH(\"file.new\")));\n    mu_assert_int_eq(\n        FSE_NOT_EXIST, storage_common_stat(storage, UNIT_TESTS_PATH(\"file.old\"), NULL));\n    mu_assert_int_eq(FSE_OK, storage_common_stat(storage, UNIT_TESTS_PATH(\"file.new\"), NULL));\n    mu_check(check_file_13DA(storage, UNIT_TESTS_PATH(\"file.new\")));\n    mu_assert_int_eq(FSE_OK, storage_common_remove(storage, UNIT_TESTS_PATH(\"file.new\")));\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic const char* dir_rename_tests[][2] = {\n    {UNIT_TESTS_PATH(\"dir.old\"), UNIT_TESTS_PATH(\"dir.new\")},\n    {UNIT_TESTS_PATH(\"test_dir\"), UNIT_TESTS_PATH(\"test_dir-new\")},\n    {UNIT_TESTS_PATH(\"test\"), UNIT_TESTS_PATH(\"test-test\")},\n};\n\nMU_TEST(storage_dir_rename) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {\n        const char* old_path = dir_rename_tests[i][0];\n        const char* new_path = dir_rename_tests[i][1];\n\n        storage_dir_create(storage, old_path);\n        mu_check(storage_dir_rename_check(storage, old_path));\n\n        mu_assert_int_eq(FSE_OK, storage_common_rename(storage, old_path, new_path));\n        mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, old_path, NULL));\n        mu_check(storage_dir_rename_check(storage, new_path));\n\n        storage_dir_remove(storage, new_path);\n        mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, new_path, NULL));\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST(storage_equiv_and_subdir) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    mu_assert_int_eq(\n        true,\n        storage_common_equivalent_path(storage, UNIT_TESTS_PATH(\"blah\"), UNIT_TESTS_PATH(\"blah\")));\n    mu_assert_int_eq(\n        true,\n        storage_common_equivalent_path(\n            storage, UNIT_TESTS_PATH(\"blah/\"), UNIT_TESTS_PATH(\"blah/\")));\n    mu_assert_int_eq(\n        false,\n        storage_common_equivalent_path(\n            storage, UNIT_TESTS_PATH(\"blah\"), UNIT_TESTS_PATH(\"blah-blah\")));\n    mu_assert_int_eq(\n        false,\n        storage_common_equivalent_path(\n            storage, UNIT_TESTS_PATH(\"blah/\"), UNIT_TESTS_PATH(\"blah-blah/\")));\n\n    mu_assert_int_eq(\n        true, storage_common_is_subdir(storage, UNIT_TESTS_PATH(\"blah\"), UNIT_TESTS_PATH(\"blah\")));\n    mu_assert_int_eq(\n        true,\n        storage_common_is_subdir(storage, UNIT_TESTS_PATH(\"blah\"), UNIT_TESTS_PATH(\"blah/blah\")));\n    mu_assert_int_eq(\n        false,\n        storage_common_is_subdir(storage, UNIT_TESTS_PATH(\"blah/blah\"), UNIT_TESTS_PATH(\"blah\")));\n    mu_assert_int_eq(\n        false,\n        storage_common_is_subdir(storage, UNIT_TESTS_PATH(\"blah\"), UNIT_TESTS_PATH(\"blah-blah\")));\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(storage_rename) {\n    MU_RUN_TEST(storage_file_rename);\n    MU_RUN_TEST(storage_dir_rename);\n    MU_RUN_TEST(storage_equiv_and_subdir);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {\n        storage_dir_remove(storage, dir_rename_tests[i][0]);\n        storage_dir_remove(storage, dir_rename_tests[i][1]);\n    }\n    furi_record_close(RECORD_STORAGE);\n}\n\n#define APPSDATA_APP_PATH(path) APPS_DATA_PATH \"/\" path\n\nstatic const char* storage_test_apps[] = {\n    \"-_twilight_-\",\n    \"-_rainbow_-\",\n    \"-_pinkie_-\",\n    \"-_apple_-\",\n    \"-_flutter_-\",\n    \"-_rare_-\",\n};\n\nstatic size_t storage_test_apps_count = COUNT_OF(storage_test_apps);\n\nstatic int32_t storage_test_app(void* arg) {\n    UNUSED(arg);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    storage_common_remove(storage, \"/data/test\");\n    int32_t ret = storage_file_create(storage, \"/data/test\", \"test\");\n    furi_record_close(RECORD_STORAGE);\n    return ret;\n}\n\nMU_TEST(test_storage_data_path_apps) {\n    for(size_t i = 0; i < storage_test_apps_count; i++) {\n        FuriThread* thread =\n            furi_thread_alloc_ex(storage_test_apps[i], 1024, storage_test_app, NULL);\n        furi_thread_set_appid(thread, storage_test_apps[i]);\n        furi_thread_start(thread);\n        furi_thread_join(thread);\n\n        mu_assert_int_eq(true, furi_thread_get_return_code(thread));\n\n        // Check if app data dir and file exists\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n        FuriString* expected = furi_string_alloc();\n        furi_string_printf(expected, APPSDATA_APP_PATH(\"%s\"), storage_test_apps[i]);\n\n        mu_check(storage_dir_exists(storage, furi_string_get_cstr(expected)));\n        furi_string_cat(expected, \"/test\");\n        mu_check(storage_file_exists(storage, furi_string_get_cstr(expected)));\n\n        furi_string_printf(expected, APPSDATA_APP_PATH(\"%s\"), storage_test_apps[i]);\n        storage_simply_remove_recursive(storage, furi_string_get_cstr(expected));\n\n        furi_record_close(RECORD_STORAGE);\n\n        furi_string_free(expected);\n        furi_thread_free(thread);\n    }\n}\n\nMU_TEST(test_storage_data_path) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    File* file = storage_file_alloc(storage);\n    mu_check(storage_dir_open(file, \"/data\"));\n    mu_check(storage_dir_close(file));\n    storage_file_free(file);\n\n    // check that appsdata folder exists\n    mu_check(storage_dir_exists(storage, APPS_DATA_PATH));\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST(test_storage_common_migrate) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    // Setup test folders\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n\n    // Test migration from non existing\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    // Test migration from existing folder to non existing\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file1\"), \"test1\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file2.ext\"), \"test2\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file3.ext.ext\"), \"test3\"));\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file1\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file2.ext\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file3.ext.ext\")));\n    mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n\n    // Test migration from existing folder to existing folder\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file1\"), \"test1\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file2.ext\"), \"test2\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old/file3.ext.ext\"), \"test3\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file1\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file2.ext\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file3.ext.ext\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file11\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file21.ext\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new/file3.ext1.ext\")));\n    mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n\n    // Test migration from empty folder to existing file\n    // Expected result: FSE_OK, folder removed, file untouched\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_new\"), \"test1\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n\n    // Test migration from empty folder to existing folder\n    // Expected result: FSE_OK, old folder removed, new folder untouched\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n\n    // Test migration from existing file to non existing, no extension\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old\"), \"test1\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n\n    // Test migration from existing file to non existing, with extension\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old.file\"), \"test1\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old.file\"), UNIT_TESTS_PATH(\"migrate_new.file\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new.file\")));\n    mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_old.file\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old.file\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new.file\"));\n\n    // Test migration from existing file to existing file, no extension\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old\"), \"test1\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_new\"), \"test2\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new1\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new1\"));\n\n    // Test migration from existing file to existing file, with extension\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old.file\"), \"test1\"));\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_new.file\"), \"test2\"));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old.file\"), UNIT_TESTS_PATH(\"migrate_new.file\")));\n\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new.file\")));\n    mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_old.file\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new1.file\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old.file\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new.file\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new1.file\"));\n\n    // Test migration from existing file to existing folder\n    mu_check(storage_file_create(storage, UNIT_TESTS_PATH(\"migrate_old\"), \"test1\"));\n    mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_assert_int_eq(\n        FSE_OK,\n        storage_common_migrate(\n            storage, UNIT_TESTS_PATH(\"migrate_old\"), UNIT_TESTS_PATH(\"migrate_new\")));\n\n    mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH(\"migrate_new\")));\n    mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_old\")));\n    mu_check(storage_file_exists(storage, UNIT_TESTS_PATH(\"migrate_new1\")));\n\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_old\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new\"));\n    storage_simply_remove_recursive(storage, UNIT_TESTS_PATH(\"migrate_new1\"));\n\n    furi_record_close(RECORD_STORAGE);\n}\n\n#define MD5_HASH_SIZE (16)\n#include <lib/toolbox/md5_calc.h>\n\nMU_TEST(test_md5_calc) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    const char* path = UNIT_TESTS_RESOURCES_PATH(\"storage/md5.txt\");\n    const char* md5_cstr = \"2a456fa43e75088fdde41c93159d62a2\";\n    const uint8_t md5[MD5_HASH_SIZE] = {\n        0x2a,\n        0x45,\n        0x6f,\n        0xa4,\n        0x3e,\n        0x75,\n        0x08,\n        0x8f,\n        0xdd,\n        0xe4,\n        0x1c,\n        0x93,\n        0x15,\n        0x9d,\n        0x62,\n        0xa2,\n    };\n\n    uint8_t md5_output[MD5_HASH_SIZE];\n    FuriString* md5_output_str = furi_string_alloc();\n    memset(md5_output, 0, MD5_HASH_SIZE);\n\n    mu_check(md5_calc_file(file, path, md5_output, NULL));\n    mu_check(md5_string_calc_file(file, path, md5_output_str, NULL));\n\n    mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE);\n    mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str));\n\n    storage_file_free(file);\n    furi_string_free(md5_output_str);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_SUITE(test_data_path) {\n    MU_RUN_TEST(test_storage_data_path);\n    MU_RUN_TEST(test_storage_data_path_apps);\n}\n\nMU_TEST_SUITE(test_storage_common) {\n    MU_RUN_TEST(test_storage_common_migrate);\n}\n\nMU_TEST_SUITE(test_md5_calc_suite) {\n    MU_RUN_TEST(test_md5_calc);\n}\n\nint run_minunit_test_storage(void) {\n    MU_RUN_SUITE(storage_file);\n    MU_RUN_SUITE(storage_file_64k);\n    MU_RUN_SUITE(storage_dir);\n    MU_RUN_SUITE(storage_rename);\n    MU_RUN_SUITE(test_data_path);\n    MU_RUN_SUITE(test_storage_common);\n    MU_RUN_SUITE(test_md5_calc_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_storage)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/stream/stream_test.c",
    "content": "#include <furi.h>\n#include <toolbox/stream/stream.h>\n#include <toolbox/stream/string_stream.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/stream/buffered_file_stream.h>\n#include <storage/storage.h>\n#include \"../test.h\" // IWYU pragma: keep\n\nstatic const char* stream_test_data = \"I write differently from what I speak, \"\n                                      \"I speak differently from what I think, \"\n                                      \"I think differently from the way I ought to think, \"\n                                      \"and so it all proceeds into deepest darkness.\";\n\nstatic const char* stream_test_left_data = \"There are two cardinal human sins \";\nstatic const char* stream_test_right_data =\n    \"from which all others derive: impatience and indolence.\";\n\n#define FILESTREAM_PATH EXT_PATH(\".tmp/unit_tests/filestream.str\")\n\nMU_TEST_1(stream_composite_subtest, Stream* stream) {\n    const size_t data_size = 128;\n    uint8_t data[data_size];\n    FuriString* string_lee;\n    string_lee = furi_string_alloc_set(\"lee\");\n\n    // test that stream is empty\n    // \"\" -> \"\"\n    mu_check(stream_size(stream) == 0);\n    mu_check(stream_eof(stream));\n    mu_check(stream_tell(stream) == 0);\n    mu_check(stream_read(stream, data, data_size) == 0);\n    mu_check(stream_eof(stream));\n    mu_check(stream_tell(stream) == 0);\n\n    // write char\n    // \"\" -> \"2\"\n    mu_check(stream_write_char(stream, '2') == 1);\n    mu_check(stream_size(stream) == 1);\n    mu_check(stream_tell(stream) == 1);\n    mu_check(stream_eof(stream));\n\n    // test rewind and eof\n    stream_rewind(stream);\n    mu_check(stream_size(stream) == 1);\n    mu_check(stream_tell(stream) == 0);\n    mu_check(!stream_eof(stream));\n\n    // add another char with replacement\n    // \"2\" -> \"1\"\n    mu_check(stream_write_char(stream, '1') == 1);\n    mu_check(stream_size(stream) == 1);\n    mu_check(stream_tell(stream) == 1);\n    mu_check(stream_eof(stream));\n\n    // write string\n    // \"1\" -> \"1337_69\"\n    mu_check(stream_write_cstring(stream, \"337_69\") == 6);\n    mu_check(stream_size(stream) == 7);\n    mu_check(stream_tell(stream) == 7);\n    mu_check(stream_eof(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_check(stream_read(stream, data, data_size) == 7);\n    mu_check(strcmp((char*)data, \"1337_69\") == 0);\n\n    // test misc seeks\n    mu_check(stream_seek(stream, 2, StreamOffsetFromStart));\n    mu_check(stream_tell(stream) == 2);\n    mu_check(!stream_seek(stream, 9000, StreamOffsetFromStart));\n    mu_check(stream_tell(stream) == 7);\n    mu_check(stream_eof(stream));\n    mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));\n    mu_check(stream_tell(stream) == 4);\n\n    // test seeks to char. content: '1337_69'\n    stream_rewind(stream);\n    mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));\n    mu_check(stream_tell(stream) == 1);\n    mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));\n    mu_check(stream_tell(stream) == 2);\n    mu_check(stream_seek_to_char(stream, '_', StreamDirectionForward));\n    mu_check(stream_tell(stream) == 4);\n    mu_check(stream_seek_to_char(stream, '9', StreamDirectionForward));\n    mu_check(stream_tell(stream) == 6);\n    mu_check(!stream_seek_to_char(stream, '9', StreamDirectionForward));\n    mu_check(stream_tell(stream) == 6);\n    mu_check(stream_seek_to_char(stream, '_', StreamDirectionBackward));\n    mu_check(stream_tell(stream) == 4);\n    mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));\n    mu_check(stream_tell(stream) == 2);\n    mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));\n    mu_check(stream_tell(stream) == 1);\n    mu_check(!stream_seek_to_char(stream, '3', StreamDirectionBackward));\n    mu_check(stream_tell(stream) == 1);\n    mu_check(stream_seek_to_char(stream, '1', StreamDirectionBackward));\n    mu_check(stream_tell(stream) == 0);\n\n    // write string with replacement\n    // \"1337_69\" -> \"1337lee\"\n    mu_check(stream_seek(stream, 4, StreamOffsetFromStart));\n    mu_check(stream_write_string(stream, string_lee) == 3);\n    mu_check(stream_size(stream) == 7);\n    mu_check(stream_tell(stream) == 7);\n    mu_check(stream_eof(stream));\n\n    // append char\n    // \"1337lee\" -> \"1337leet\"\n    mu_check(stream_write(stream, (uint8_t*)\"t\", 1) == 1);\n    mu_check(stream_size(stream) == 8);\n    mu_check(stream_tell(stream) == 8);\n    mu_check(stream_eof(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_check(stream_read(stream, data, data_size) == 8);\n    mu_check(strcmp((char*)data, \"1337leet\") == 0);\n    mu_check(stream_tell(stream) == 8);\n    mu_check(stream_eof(stream));\n\n    // negative seek from current position -> clamp to 0\n    mu_check(!stream_seek(stream, -9000, StreamOffsetFromCurrent));\n    mu_check(stream_tell(stream) == 0);\n\n    // negative seek from start position -> clamp to 0\n    stream_rewind(stream);\n    mu_check(!stream_seek(stream, -3, StreamOffsetFromStart));\n    mu_check(stream_tell(stream) == 0);\n\n    // zero seek from current position -> clamp to stream size\n    mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));\n    mu_check(stream_tell(stream) == 8);\n\n    // negative seek from end position -> clamp to 0\n    mu_check(!stream_seek(stream, -9000, StreamOffsetFromEnd));\n    mu_check(stream_tell(stream) == 0);\n\n    // clean stream\n    stream_clean(stream);\n    mu_check(stream_size(stream) == 0);\n    mu_check(stream_eof(stream));\n    mu_check(stream_tell(stream) == 0);\n\n    // write format\n    // \"\" -> \"dio666\"\n    mu_check(stream_write_format(stream, \"%s%d\", \"dio\", 666) == 6);\n    mu_check(stream_size(stream) == 6);\n    mu_check(stream_eof(stream));\n    mu_check(stream_tell(stream) == 6);\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_check(stream_read(stream, data, data_size) == 6);\n    mu_check(strcmp((char*)data, \"dio666\") == 0);\n\n    // clean and write cstring\n    // \"dio666\" -> \"\" -> \"1234567890\"\n    stream_clean(stream);\n    mu_check(stream_write_cstring(stream, \"1234567890\") == 10);\n\n    // delete 4 bytes from 1 pos\n    // \"1xxxx67890\" -> \"167890\"\n    mu_check(stream_seek(stream, 1, StreamOffsetFromStart));\n    mu_check(stream_delete(stream, 4));\n    mu_assert_int_eq(6, stream_size(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_assert_int_eq(6, stream_read(stream, data, data_size));\n    mu_check(strcmp((char*)data, \"167890\") == 0);\n\n    // write cstring\n    // \"167890\" -> \"167890It Was Me, Dio!\"\n    mu_check(stream_write_cstring(stream, \"It Was Me, Dio!\") == 15);\n\n    // delete 1337 bytes from 1 pos\n    // and check that we can delete only 20 bytes\n    // \"1xxxxxxxxxxxxxxxxxxxx\" -> \"1\"\n    mu_check(stream_seek(stream, 1, StreamOffsetFromStart));\n    mu_check(stream_delete(stream, 1337));\n    mu_assert_int_eq(1, stream_size(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_check(stream_read(stream, data, data_size) == 1);\n    mu_check(strcmp((char*)data, \"1\") == 0);\n\n    // write cstring from 0 pos, replacing 1 byte\n    // \"1\" -> \"Oh? You're roaching me?\"\n    mu_check(stream_rewind(stream));\n    mu_assert_int_eq(23, stream_write_cstring(stream, \"Oh? You're roaching me?\"));\n\n    // insert 11 bytes to 0 pos\n    // \"Oh? You're roaching me?\" -> \"Za Warudo! Oh? You're roaching me?\"\n    mu_check(stream_rewind(stream));\n    mu_check(stream_insert(stream, (uint8_t*)\"Za Warudo! \", 11));\n    mu_assert_int_eq(34, stream_size(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_assert_int_eq(34, stream_read(stream, data, data_size));\n    mu_assert_string_eq(\"Za Warudo! Oh? You're roaching me?\", (char*)data);\n\n    // insert cstring to 22 pos\n    // \"Za Warudo! Oh? You're roaching me?\" -> \"Za Warudo! Oh? You're approaching me?\"\n    mu_check(stream_seek(stream, 22, StreamOffsetFromStart));\n    mu_check(stream_insert_cstring(stream, \"app\"));\n    mu_assert_int_eq(37, stream_size(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_assert_int_eq(37, stream_read(stream, data, data_size));\n    mu_assert_string_eq(\"Za Warudo! Oh? You're approaching me?\", (char*)data);\n\n    // insert cstring to the end of the stream\n    // \"Za Warudo! Oh? You're approaching me?\" -> \"Za Warudo! Oh? You're approaching me? It was me, Dio!\"\n    mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));\n    mu_check(stream_insert_cstring(stream, \" It was me, Dio!\"));\n    mu_assert_int_eq(53, stream_size(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_assert_int_eq(53, stream_read(stream, data, data_size));\n    mu_assert_string_eq(\"Za Warudo! Oh? You're approaching me? It was me, Dio!\", (char*)data);\n\n    // delete 168430090 bytes from stream\n    // and test that we can delete only 53\n    mu_check(stream_rewind(stream));\n    mu_check(stream_delete(stream, 0x0A0A0A0A));\n    mu_assert_int_eq(0, stream_size(stream));\n    mu_check(stream_eof(stream));\n    mu_assert_int_eq(0, stream_tell(stream));\n\n    // clean stream\n    stream_clean(stream);\n    mu_assert_int_eq(0, stream_size(stream));\n    mu_check(stream_eof(stream));\n    mu_assert_int_eq(0, stream_tell(stream));\n\n    // insert formatted string at the end of stream\n    // \"\" -> \"dio666\"\n    mu_check(stream_insert_format(stream, \"%s%d\", \"dio\", 666));\n    mu_assert_int_eq(6, stream_size(stream));\n    mu_check(stream_eof(stream));\n    mu_assert_int_eq(6, stream_tell(stream));\n\n    // insert formatted string at the end of stream\n    // \"dio666\" -> \"dio666zlo555\"\n    mu_check(stream_insert_format(stream, \"%s%d\", \"zlo\", 555));\n    mu_assert_int_eq(12, stream_size(stream));\n    mu_check(stream_eof(stream));\n    mu_assert_int_eq(12, stream_tell(stream));\n\n    // insert formatted string at the 6 pos\n    // \"dio666\" -> \"dio666baba13zlo555\"\n    mu_check(stream_seek(stream, 6, StreamOffsetFromStart));\n    mu_check(stream_insert_format(stream, \"%s%d\", \"baba\", 13));\n    mu_assert_int_eq(18, stream_size(stream));\n    mu_assert_int_eq(12, stream_tell(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_assert_int_eq(18, stream_read(stream, data, data_size));\n    mu_assert_string_eq(\"dio666baba13zlo555\", (char*)data);\n\n    // delete 6 chars from pos 6 and insert 1 chars\n    // \"dio666baba13zlo555\" -> \"dio666xzlo555\"\n    mu_check(stream_seek(stream, 6, StreamOffsetFromStart));\n    mu_check(stream_delete_and_insert_char(stream, 6, 'x'));\n    mu_assert_int_eq(13, stream_size(stream));\n    mu_assert_int_eq(7, stream_tell(stream));\n\n    // read data\n    memset(data, 0, data_size);\n    stream_rewind(stream);\n    mu_check(stream_read(stream, data, data_size) == 13);\n    mu_assert_string_eq(\"dio666xzlo555\", (char*)data);\n\n    // delete 9000 chars from pos 6 and insert 3 chars from string\n    // \"dio666xzlo555\" -> \"dio666777\"\n    mu_check(stream_seek(stream, 6, StreamOffsetFromStart));\n    mu_check(stream_delete_and_insert_cstring(stream, 9000, \"777\"));\n    mu_assert_int_eq(9, stream_size(stream));\n    mu_assert_int_eq(9, stream_tell(stream));\n    mu_check(stream_eof(stream));\n\n    furi_string_free(string_lee);\n}\n\nMU_TEST(stream_composite_test) {\n    // test string stream\n    Stream* stream;\n    stream = string_stream_alloc();\n    MU_RUN_TEST_1(stream_composite_subtest, stream);\n    stream_free(stream);\n\n    // test file stream\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    stream = file_stream_alloc(storage);\n    mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    MU_RUN_TEST_1(stream_composite_subtest, stream);\n    stream_free(stream);\n\n    // test buffered file stream\n    stream = buffered_file_stream_alloc(storage);\n    mu_check(\n        buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    MU_RUN_TEST_1(stream_composite_subtest, stream);\n    stream_free(stream);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_1(stream_write_subtest, Stream* stream) {\n    mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));\n}\n\nMU_TEST_1(stream_read_subtest, Stream* stream) {\n    uint8_t data[256] = {0};\n    mu_check(stream_rewind(stream));\n    mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));\n    mu_assert_string_eq(stream_test_data, (const char*)data);\n}\n\nMU_TEST(stream_write_read_save_load_test) {\n    Stream* stream_orig = string_stream_alloc();\n    Stream* stream_copy = string_stream_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    // write, read\n    MU_RUN_TEST_1(stream_write_subtest, stream_orig);\n    MU_RUN_TEST_1(stream_read_subtest, stream_orig);\n\n    // copy, read\n    mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));\n    MU_RUN_TEST_1(stream_read_subtest, stream_orig);\n\n    // save to file\n    mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));\n    mu_assert_int_eq(\n        strlen(stream_test_data),\n        stream_save_to_file(stream_orig, storage, FILESTREAM_PATH, FSOM_CREATE_ALWAYS));\n\n    stream_free(stream_copy);\n    stream_free(stream_orig);\n\n    // load from file, read\n    Stream* stream_new = string_stream_alloc();\n    mu_assert_int_eq(\n        strlen(stream_test_data), stream_load_from_file(stream_new, storage, FILESTREAM_PATH));\n    MU_RUN_TEST_1(stream_read_subtest, stream_new);\n    stream_free(stream_new);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST_1(stream_split_subtest, Stream* stream) {\n    stream_clean(stream);\n    stream_write_cstring(stream, stream_test_left_data);\n    stream_write_cstring(stream, stream_test_right_data);\n\n    Stream* stream_left = string_stream_alloc();\n    Stream* stream_right = string_stream_alloc();\n\n    mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));\n    mu_check(stream_split(stream, stream_left, stream_right));\n\n    uint8_t data[256] = {0};\n    mu_check(stream_rewind(stream_left));\n    mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));\n    mu_assert_string_eq(stream_test_left_data, (const char*)data);\n\n    mu_check(stream_rewind(stream_right));\n    mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));\n    mu_assert_string_eq(stream_test_right_data, (const char*)data);\n\n    stream_free(stream_right);\n    stream_free(stream_left);\n}\n\nMU_TEST(stream_split_test) {\n    // test string stream\n    Stream* stream;\n    stream = string_stream_alloc();\n    MU_RUN_TEST_1(stream_split_subtest, stream);\n    stream_free(stream);\n\n    // test file stream\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    stream = file_stream_alloc(storage);\n    mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    MU_RUN_TEST_1(stream_split_subtest, stream);\n    stream_free(stream);\n\n    // test buffered stream\n    stream = buffered_file_stream_alloc(storage);\n    mu_check(\n        buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    MU_RUN_TEST_1(stream_split_subtest, stream);\n    stream_free(stream);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST(stream_buffered_write_after_read_test) {\n    const char* prefix = \"I write \";\n    const char* substr = \"Hello there\";\n\n    const size_t substr_len = strlen(substr);\n    const size_t prefix_len = strlen(prefix);\n    const size_t buf_size = substr_len + 1;\n\n    char buf[buf_size];\n    memset(buf, 0, buf_size);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    Stream* stream = buffered_file_stream_alloc(storage);\n    mu_check(\n        buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));\n    mu_check(stream_rewind(stream));\n    mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));\n    mu_assert_string_eq(prefix, buf);\n    mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));\n    mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));\n    mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));\n    mu_assert_string_eq(substr, buf);\n\n    stream_free(stream);\n    furi_record_close(RECORD_STORAGE);\n}\n\nMU_TEST(stream_buffered_large_file_test) {\n    FuriString* input_data;\n    FuriString* output_data;\n    input_data = furi_string_alloc();\n    output_data = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    // generate test data consisting of several identical lines\n    const size_t data_size = 4096;\n    const size_t line_size = strlen(stream_test_data);\n    const size_t rep_count = data_size / line_size + 1;\n\n    for(size_t i = 0; i < rep_count; ++i) {\n        furi_string_cat_printf(input_data, \"%s\\n\", stream_test_data);\n    }\n\n    // write test data to file\n    Stream* stream = buffered_file_stream_alloc(storage);\n    mu_check(\n        buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));\n    mu_assert_int_eq(0, stream_size(stream));\n    mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data));\n    mu_assert_int_eq(furi_string_size(input_data), stream_size(stream));\n\n    const size_t substr_start = 8;\n    const size_t substr_len = 11;\n\n    mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));\n    mu_assert_int_eq(substr_start, stream_tell(stream));\n\n    // copy one substring from test data\n    char test_substr[substr_len + 1];\n    memset(test_substr, 0, substr_len + 1);\n    memcpy(test_substr, stream_test_data + substr_start, substr_len);\n\n    char buf[substr_len + 1];\n    memset(buf, 0, substr_len + 1);\n\n    // read substring\n    mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));\n    mu_assert_string_eq(test_substr, buf);\n    memset(buf, 0, substr_len + 1);\n\n    // forward seek to cause a cache miss\n    mu_check(stream_seek(\n        stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));\n    // read same substring from a different line\n    mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));\n    mu_assert_string_eq(test_substr, buf);\n    memset(buf, 0, substr_len + 1);\n\n    // backward seek to cause a cache miss\n    mu_check(stream_seek(\n        stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));\n    mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));\n    mu_assert_string_eq(test_substr, buf);\n\n    // read the whole file\n    mu_check(stream_rewind(stream));\n    FuriString* tmp;\n    tmp = furi_string_alloc();\n    while(stream_read_line(stream, tmp)) {\n        furi_string_cat(output_data, tmp);\n    }\n    furi_string_free(tmp);\n\n    // check against generated data\n    mu_assert_int_eq(furi_string_size(input_data), furi_string_size(output_data));\n    mu_check(furi_string_equal(input_data, output_data));\n    mu_check(stream_eof(stream));\n\n    stream_free(stream);\n\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(input_data);\n    furi_string_free(output_data);\n}\n\nMU_TEST_SUITE(stream_suite) {\n    MU_RUN_TEST(stream_write_read_save_load_test);\n    MU_RUN_TEST(stream_composite_test);\n    MU_RUN_TEST(stream_split_test);\n    MU_RUN_TEST(stream_buffered_write_after_read_test);\n    MU_RUN_TEST(stream_buffered_large_file_test);\n}\n\nint run_minunit_test_stream(void) {\n    MU_RUN_SUITE(stream_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_stream)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/strint/strint_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include \"../test.h\" // IWYU pragma: keep\n\n#include <toolbox/strint.h>\n\nMU_TEST(strint_test_basic) {\n    uint32_t result = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"123456\", NULL, &result, 10));\n    mu_assert_int_eq(123456, result);\n}\n\nMU_TEST(strint_test_junk) {\n    uint32_t result = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"   123456   \", NULL, &result, 10));\n    mu_assert_int_eq(123456, result);\n    mu_assert_int_eq(\n        StrintParseNoError, strint_to_uint32(\"   \\r\\n\\r\\n   123456     \", NULL, &result, 10));\n    mu_assert_int_eq(123456, result);\n}\n\nMU_TEST(strint_test_tail) {\n    uint32_t result = 0;\n    char* tail;\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"123456tail\", &tail, &result, 10));\n    mu_assert_int_eq(123456, result);\n    mu_assert_string_eq(\"tail\", tail);\n    mu_assert_int_eq(\n        StrintParseNoError, strint_to_uint32(\"   \\r\\n  123456tail\", &tail, &result, 10));\n    mu_assert_int_eq(123456, result);\n    mu_assert_string_eq(\"tail\", tail);\n}\n\nMU_TEST(strint_test_errors) {\n    uint32_t result = 123;\n    mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32(\"\", NULL, &result, 10));\n    mu_assert_int_eq(123, result);\n    mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32(\"   asd\\r\\n\", NULL, &result, 10));\n    mu_assert_int_eq(123, result);\n    mu_assert_int_eq(StrintParseSignError, strint_to_uint32(\"+++123456\", NULL, &result, 10));\n    mu_assert_int_eq(123, result);\n    mu_assert_int_eq(StrintParseSignError, strint_to_uint32(\"-1\", NULL, &result, 10));\n    mu_assert_int_eq(123, result);\n    mu_assert_int_eq(\n        StrintParseOverflowError,\n        strint_to_uint32(\"0xAAAAAAAAAAAAAAAADEADBEEF!!!!!!\", NULL, &result, 0));\n    mu_assert_int_eq(123, result);\n    mu_assert_int_eq(StrintParseOverflowError, strint_to_uint32(\"4294967296\", NULL, &result, 0));\n    mu_assert_int_eq(123, result);\n\n    int32_t result_i32 = 123;\n    mu_assert_int_eq(\n        StrintParseOverflowError, strint_to_int32(\"-2147483649\", NULL, &result_i32, 0));\n    mu_assert_int_eq(123, result_i32);\n}\n\nMU_TEST(strint_test_bases) {\n    uint32_t result = 0;\n\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0x123\", NULL, &result, 0));\n    mu_assert_int_eq(0x123, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0X123\", NULL, &result, 0));\n    mu_assert_int_eq(0x123, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0xDEADBEEF\", NULL, &result, 0));\n    mu_assert_int_eq(0xDEADBEEF, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0xDEADBEEF\", NULL, &result, 16));\n    mu_assert_int_eq(0xDEADBEEF, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"123\", NULL, &result, 16));\n    mu_assert_int_eq(0x123, result);\n\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"123\", NULL, &result, 0));\n    mu_assert_int_eq(123, result);\n\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0123\", NULL, &result, 0));\n    mu_assert_int_eq(0123, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0123\", NULL, &result, 8));\n    mu_assert_int_eq(0123, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"123\", NULL, &result, 8));\n    mu_assert_int_eq(0123, result);\n\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0b101\", NULL, &result, 0));\n    mu_assert_int_eq(0b101, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0b101\", NULL, &result, 2));\n    mu_assert_int_eq(0b101, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"0B101\", NULL, &result, 0));\n    mu_assert_int_eq(0b101, result);\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"101\", NULL, &result, 2));\n    mu_assert_int_eq(0b101, result);\n}\n\nMU_TEST_SUITE(strint_test_limits) {\n    uint64_t result_u64 = 0;\n    mu_assert_int_eq(\n        StrintParseNoError, strint_to_uint64(\"18446744073709551615\", NULL, &result_u64, 0));\n    // `mu_assert_int_eq' does not support longs :(\n    mu_assert(UINT64_MAX == result_u64, \"result does not equal UINT64_MAX\");\n\n    int64_t result_i64 = 0;\n    mu_assert_int_eq(\n        StrintParseNoError, strint_to_int64(\"9223372036854775807\", NULL, &result_i64, 0));\n    mu_assert(INT64_MAX == result_i64, \"result does not equal INT64_MAX\");\n    mu_assert_int_eq(\n        StrintParseNoError, strint_to_int64(\"-9223372036854775808\", NULL, &result_i64, 0));\n    mu_assert(INT64_MIN == result_i64, \"result does not equal INT64_MIN\");\n\n    uint32_t result_u32 = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint32(\"4294967295\", NULL, &result_u32, 0));\n    mu_assert_int_eq(UINT32_MAX, result_u32);\n\n    int32_t result_i32 = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_int32(\"2147483647\", NULL, &result_i32, 0));\n    mu_assert_int_eq(INT32_MAX, result_i32);\n    mu_assert_int_eq(StrintParseNoError, strint_to_int32(\"-2147483648\", NULL, &result_i32, 0));\n    mu_assert_int_eq(INT32_MIN, result_i32);\n\n    uint16_t result_u16 = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_uint16(\"65535\", NULL, &result_u16, 0));\n    mu_assert_int_eq(UINT16_MAX, result_u16);\n\n    int16_t result_i16 = 0;\n    mu_assert_int_eq(StrintParseNoError, strint_to_int16(\"32767\", NULL, &result_i16, 0));\n    mu_assert_int_eq(INT16_MAX, result_i16);\n    mu_assert_int_eq(StrintParseNoError, strint_to_int16(\"-32768\", NULL, &result_i16, 0));\n    mu_assert_int_eq(INT16_MIN, result_i16);\n}\n\nMU_TEST_SUITE(test_strint_suite) {\n    MU_RUN_TEST(strint_test_basic);\n    MU_RUN_TEST(strint_test_junk);\n    MU_RUN_TEST(strint_test_tail);\n    MU_RUN_TEST(strint_test_errors);\n    MU_RUN_TEST(strint_test_bases);\n    MU_RUN_TEST(strint_test_limits);\n}\n\nint run_minunit_test_strint(void) {\n    MU_RUN_SUITE(test_strint_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_strint)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/subghz/subghz_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"../test.h\" // IWYU pragma: keep\n#include <lib/subghz/receiver.h>\n#include <lib/subghz/transmitter.h>\n#include <lib/subghz/subghz_keystore.h>\n#include <lib/subghz/subghz_file_encoder_worker.h>\n#include <lib/subghz/protocols/protocol_items.h>\n#include <flipper_format/flipper_format_i.h>\n#include <lib/subghz/devices/devices.h>\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#define TAG \"SubGhzTest\"\n\n#define KEYSTORE_DIR_NAME       EXT_PATH(\"subghz/assets/keeloq_mfcodes\")\n#define CAME_ATOMO_DIR_NAME     EXT_PATH(\"subghz/assets/came_atomo\")\n#define NICE_FLOR_S_DIR_NAME    EXT_PATH(\"subghz/assets/nice_flor_s\")\n#define ALUTECH_AT_4N_DIR_NAME  EXT_PATH(\"subghz/assets/alutech_at_4n\")\n#define TEST_RANDOM_DIR_NAME    EXT_PATH(\"unit_tests/subghz/test_random_raw.sub\")\n#define TEST_RANDOM_COUNT_PARSE 328\n#define TEST_TIMEOUT            10000\n\nstatic SubGhzEnvironment* environment_handler;\nstatic SubGhzReceiver* receiver_handler;\n//static SubGhzTransmitter* transmitter_handler;\nstatic SubGhzFileEncoderWorker* file_worker_encoder_handler;\nstatic uint16_t subghz_test_decoder_count = 0;\n\nstatic void subghz_test_rx_callback(\n    SubGhzReceiver* receiver,\n    SubGhzProtocolDecoderBase* decoder_base,\n    void* context) {\n    UNUSED(receiver);\n    UNUSED(context);\n    FuriString* text;\n    text = furi_string_alloc();\n    subghz_protocol_decoder_base_get_string(decoder_base, text);\n    subghz_receiver_reset(receiver_handler);\n    FURI_LOG_T(TAG, \"\\r\\n%s\", furi_string_get_cstr(text));\n    furi_string_free(text);\n    subghz_test_decoder_count++;\n}\n\nstatic void subghz_test_init(void) {\n    environment_handler = subghz_environment_alloc();\n    subghz_environment_set_came_atomo_rainbow_table_file_name(\n        environment_handler, CAME_ATOMO_DIR_NAME);\n    subghz_environment_set_nice_flor_s_rainbow_table_file_name(\n        environment_handler, NICE_FLOR_S_DIR_NAME);\n    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(\n        environment_handler, ALUTECH_AT_4N_DIR_NAME);\n    subghz_environment_set_protocol_registry(\n        environment_handler, (void*)&subghz_protocol_registry);\n\n    subghz_devices_init();\n\n    receiver_handler = subghz_receiver_alloc_init(environment_handler);\n    subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);\n    subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL);\n}\n\nstatic void subghz_test_deinit(void) {\n    subghz_devices_deinit();\n    subghz_receiver_free(receiver_handler);\n    subghz_environment_free(environment_handler);\n}\n\nstatic bool subghz_decoder_test(const char* path, const char* name_decoder) {\n    subghz_test_decoder_count = 0;\n    uint32_t test_start = furi_get_tick();\n\n    SubGhzProtocolDecoderBase* decoder =\n        subghz_receiver_search_decoder_base_by_name(receiver_handler, name_decoder);\n\n    if(decoder) {\n        file_worker_encoder_handler = subghz_file_encoder_worker_alloc();\n        if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) {\n            // the worker needs a file in order to open and read part of the file\n            furi_delay_ms(100);\n\n            LevelDuration level_duration;\n            while(furi_get_tick() - test_start < TEST_TIMEOUT) {\n                level_duration =\n                    subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);\n                if(!level_duration_is_reset(level_duration)) {\n                    bool level = level_duration_get_level(level_duration);\n                    uint32_t duration = level_duration_get_duration(level_duration);\n                    // Yield, to load data inside the worker\n                    furi_thread_yield();\n                    decoder->protocol->decoder->feed(decoder, level, duration);\n                } else {\n                    break;\n                }\n            }\n            furi_delay_ms(10);\n        }\n        if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {\n            subghz_file_encoder_worker_stop(file_worker_encoder_handler);\n        }\n        subghz_file_encoder_worker_free(file_worker_encoder_handler);\n    }\n    FURI_LOG_T(TAG, \"Decoder count parse %d\", subghz_test_decoder_count);\n    if(furi_get_tick() - test_start > TEST_TIMEOUT) {\n        printf(\"Test decoder %s ERROR TimeOut\\r\\n\", name_decoder);\n        return false;\n    } else {\n        return subghz_test_decoder_count ? true : false;\n    }\n}\n\nstatic bool subghz_decode_random_test(const char* path) {\n    subghz_test_decoder_count = 0;\n    subghz_receiver_reset(receiver_handler);\n    uint32_t test_start = furi_get_tick();\n\n    file_worker_encoder_handler = subghz_file_encoder_worker_alloc();\n    if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) {\n        // the worker needs a file in order to open and read part of the file\n        furi_delay_ms(100);\n\n        LevelDuration level_duration;\n        while(furi_get_tick() - test_start < TEST_TIMEOUT * 10) {\n            level_duration =\n                subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);\n            if(!level_duration_is_reset(level_duration)) {\n                bool level = level_duration_get_level(level_duration);\n                uint32_t duration = level_duration_get_duration(level_duration);\n                // Yield, to load data inside the worker\n                furi_thread_yield();\n                subghz_receiver_decode(receiver_handler, level, duration);\n            } else {\n                break;\n            }\n        }\n        furi_delay_ms(10);\n        if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {\n            subghz_file_encoder_worker_stop(file_worker_encoder_handler);\n        }\n        subghz_file_encoder_worker_free(file_worker_encoder_handler);\n    }\n    FURI_LOG_D(TAG, \"Decoder count parse %d\", subghz_test_decoder_count);\n    if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) {\n        printf(\"Random test ERROR TimeOut\\r\\n\");\n        return false;\n    } else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstatic bool subghz_encoder_test(const char* path) {\n    subghz_test_decoder_count = 0;\n    uint32_t test_start = furi_get_tick();\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    bool file_load = false;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(fff_data_file, path)) {\n            FURI_LOG_E(TAG, \"Error open file %s\", path);\n            break;\n        }\n\n        if(!flipper_format_read_string(fff_data_file, \"Preset\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Preset\");\n            break;\n        }\n\n        if(!flipper_format_read_string(fff_data_file, \"Protocol\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Protocol\");\n            break;\n        }\n        file_load = true;\n    } while(false);\n    if(file_load) {\n        SubGhzTransmitter* transmitter =\n            subghz_transmitter_alloc_init(environment_handler, furi_string_get_cstr(temp_str));\n        subghz_transmitter_deserialize(transmitter, fff_data_file);\n\n        SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name(\n            receiver_handler, furi_string_get_cstr(temp_str));\n\n        if(decoder) {\n            LevelDuration level_duration;\n            while(furi_get_tick() - test_start < TEST_TIMEOUT) {\n                level_duration = subghz_transmitter_yield(transmitter);\n                if(!level_duration_is_reset(level_duration)) {\n                    bool level = level_duration_get_level(level_duration);\n                    uint32_t duration = level_duration_get_duration(level_duration);\n                    decoder->protocol->decoder->feed(decoder, level, duration);\n                } else {\n                    break;\n                }\n            }\n            furi_delay_ms(10);\n        }\n        subghz_transmitter_free(transmitter);\n    }\n    flipper_format_free(fff_data_file);\n    FURI_LOG_T(TAG, \"Decoder count parse %d\", subghz_test_decoder_count);\n    if(furi_get_tick() - test_start > TEST_TIMEOUT) {\n        printf(\"Test encoder %s ERROR TimeOut\\r\\n\", furi_string_get_cstr(temp_str));\n        subghz_test_decoder_count = 0;\n    }\n    furi_string_free(temp_str);\n\n    return subghz_test_decoder_count ? true : false;\n}\n\nMU_TEST(subghz_keystore_test) {\n    mu_assert(\n        subghz_environment_load_keystore(environment_handler, KEYSTORE_DIR_NAME),\n        \"Test keystore error\");\n}\n\ntypedef enum {\n    SubGhzHalAsyncTxTestTypeNormal,\n    SubGhzHalAsyncTxTestTypeInvalidStart,\n    SubGhzHalAsyncTxTestTypeInvalidMid,\n    SubGhzHalAsyncTxTestTypeInvalidEnd,\n    SubGhzHalAsyncTxTestTypeResetStart,\n    SubGhzHalAsyncTxTestTypeResetMid,\n    SubGhzHalAsyncTxTestTypeResetEnd,\n} SubGhzHalAsyncTxTestType;\n\ntypedef struct {\n    SubGhzHalAsyncTxTestType type;\n    size_t pos;\n} SubGhzHalAsyncTxTest;\n\n#define SUBGHZ_HAL_TEST_DURATION 3\n\nstatic LevelDuration subghz_hal_async_tx_test_yield(void* context) {\n    SubGhzHalAsyncTxTest* test = context;\n    bool is_odd = test->pos % 2;\n\n    if(test->type == SubGhzHalAsyncTxTestTypeNormal) {\n        if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {\n        if(test->pos == 0) {\n            test->pos++;\n            return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {\n        if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {\n            test->pos++;\n            return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {\n        if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {\n            test->pos++;\n            return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {\n        if(test->pos == 0) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {\n        if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {\n        if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {\n            test->pos++;\n            return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);\n        } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {\n            test->pos++;\n            return level_duration_reset();\n        } else {\n            furi_crash(\"Yield after reset\");\n        }\n    } else {\n        furi_crash();\n    }\n}\n\nbool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {\n    SubGhzHalAsyncTxTest test = {0};\n    test.type = type;\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n    furi_hal_subghz_set_frequency_and_path(433920000);\n\n    if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) {\n        mu_warn(\"SubGHZ transmission is prohibited\");\n        return false;\n    }\n\n    FuriHalCortexTimer timer = furi_hal_cortex_timer_get(30000000);\n\n    while(!furi_hal_subghz_is_async_tx_complete()) {\n        if(furi_hal_cortex_timer_is_expired(timer)) {\n            furi_hal_subghz_stop_async_tx();\n            furi_hal_subghz_sleep();\n            return false;\n        }\n        furi_delay_ms(10);\n    }\n    furi_hal_subghz_stop_async_tx();\n    furi_hal_subghz_sleep();\n\n    return true;\n}\n\nMU_TEST(subghz_hal_async_tx_test) {\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),\n        \"Test furi_hal_async_tx normal\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),\n        \"Test furi_hal_async_tx invalid start\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),\n        \"Test furi_hal_async_tx invalid mid\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),\n        \"Test furi_hal_async_tx invalid end\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),\n        \"Test furi_hal_async_tx reset start\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),\n        \"Test furi_hal_async_tx reset mid\");\n    mu_assert(\n        subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),\n        \"Test furi_hal_async_tx reset end\");\n}\n\n//test decoders\nMU_TEST(subghz_decoder_came_atomo_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/came_atomo_raw.sub\"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_CAME_ATOMO_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_came_test) {\n    mu_assert(\n        subghz_decoder_test(EXT_PATH(\"unit_tests/subghz/came_raw.sub\"), SUBGHZ_PROTOCOL_CAME_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_CAME_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_came_twee_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/came_twee_raw.sub\"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_CAME_TWEE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_faac_slh_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/faac_slh_raw.sub\"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_FAAC_SLH_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_gate_tx_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/gate_tx_raw.sub\"), SUBGHZ_PROTOCOL_GATE_TX_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_GATE_TX_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_hormann_hsm_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/hormann_hsm_raw.sub\"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HORMANN_HSM_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_ido_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/ido_117_111_raw.sub\"), SUBGHZ_PROTOCOL_IDO_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_IDO_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_keeloq_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/doorhan_raw.sub\"), SUBGHZ_PROTOCOL_KEELOQ_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_KEELOQ_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_kia_seed_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/kia_seed_raw.sub\"), SUBGHZ_PROTOCOL_KIA_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_KIA_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_nero_radio_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/nero_radio_raw.sub\"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_NERO_RADIO_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_nero_sketch_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/nero_sketch_raw.sub\"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_NERO_SKETCH_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_nice_flo_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/nice_flo_raw.sub\"), SUBGHZ_PROTOCOL_NICE_FLO_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_NICE_FLO_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_nice_flor_s_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/nice_flor_s_raw.sub\"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_princeton_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/Princeton_raw.sub\"), SUBGHZ_PROTOCOL_PRINCETON_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_PRINCETON_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_scher_khan_magic_code_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/scher_khan_magic_code.sub\"),\n            SUBGHZ_PROTOCOL_SCHER_KHAN_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SCHER_KHAN_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_somfy_keytis_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/Somfy_keytis_raw.sub\"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_somfy_telis_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/somfy_telis_raw.sub\"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_star_line_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/cenmax_raw.sub\"), SUBGHZ_PROTOCOL_STAR_LINE_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_STAR_LINE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_linear_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/linear_raw.sub\"), SUBGHZ_PROTOCOL_LINEAR_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_LINEAR_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_linear_delta3_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/linear_delta3_raw.sub\"),\n            SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_megacode_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/megacode_raw.sub\"), SUBGHZ_PROTOCOL_MEGACODE_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_MEGACODE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_secplus_v1_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/security_pls_1_0_raw.sub\"),\n            SUBGHZ_PROTOCOL_SECPLUS_V1_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SECPLUS_V1_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_secplus_v2_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/security_pls_2_0_raw.sub\"),\n            SUBGHZ_PROTOCOL_SECPLUS_V2_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SECPLUS_V2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_holtek_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/holtek_raw.sub\"), SUBGHZ_PROTOCOL_HOLTEK_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HOLTEK_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_power_smart_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/power_smart_raw.sub\"), SUBGHZ_PROTOCOL_POWER_SMART_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_POWER_SMART_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_marantec_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/marantec_raw.sub\"), SUBGHZ_PROTOCOL_MARANTEC_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_MARANTEC_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_bett_test) {\n    mu_assert(\n        subghz_decoder_test(EXT_PATH(\"unit_tests/subghz/bett_raw.sub\"), SUBGHZ_PROTOCOL_BETT_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_BETT_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_doitrand_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/doitrand_raw.sub\"), SUBGHZ_PROTOCOL_DOITRAND_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_DOITRAND_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_phoenix_v2_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/phoenix_v2_raw.sub\"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_PHOENIX_V2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_honeywell_wdb_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/honeywell_wdb_raw.sub\"),\n            SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_magellan_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/magellan_raw.sub\"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_MAGELLAN_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_intertechno_v3_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/intertechno_v3_raw.sub\"),\n            SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_clemsa_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/clemsa_raw.sub\"), SUBGHZ_PROTOCOL_CLEMSA_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_CLEMSA_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_ansonic_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/ansonic_raw.sub\"), SUBGHZ_PROTOCOL_ANSONIC_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_ANSONIC_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_smc5326_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/smc5326_raw.sub\"), SUBGHZ_PROTOCOL_SMC5326_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_SMC5326_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_holtek_ht12x_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/holtek_ht12x_raw.sub\"), SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_dooya_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/dooya_raw.sub\"), SUBGHZ_PROTOCOL_DOOYA_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_DOOYA_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_alutech_at_4n_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/alutech_at_4n_raw.sub\"),\n            SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_nice_one_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/nice_one_raw.sub\"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_kinggates_stylo4k_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/kinggates_stylo4k_raw.sub\"),\n            SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_mastercode_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/mastercode_raw.sub\"), SUBGHZ_PROTOCOL_MASTERCODE_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_MASTERCODE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_dickert_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/dickert_raw.sub\"), SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_legrand_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/legrand_raw.sub\"), SUBGHZ_PROTOCOL_LEGRAND_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_LEGRAND_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_marantec24_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/marantec24_raw.sub\"), SUBGHZ_PROTOCOL_MARANTEC24_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_MARANTEC24_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_roger_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/roger_raw.sub\"), SUBGHZ_PROTOCOL_ROGER_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_ROGER_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_feron_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/feron_raw.sub\"), SUBGHZ_PROTOCOL_FERON_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_FERON_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_gangqi_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/gangqi_raw.sub\"), SUBGHZ_PROTOCOL_GANGQI_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_GANGQI_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_hollarm_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/hollarm_raw.sub\"), SUBGHZ_PROTOCOL_HOLLARM_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HOLLARM_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_reversrb2_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/revers_rb2_raw.sub\"), SUBGHZ_PROTOCOL_REVERSRB2_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_REVERSRB2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_decoder_hay21_test) {\n    mu_assert(\n        subghz_decoder_test(\n            EXT_PATH(\"unit_tests/subghz/hay21_raw.sub\"), SUBGHZ_PROTOCOL_HAY21_NAME),\n        \"Test decoder \" SUBGHZ_PROTOCOL_HAY21_NAME \" error\\r\\n\");\n}\n\n//test encoders\nMU_TEST(subghz_encoder_princeton_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/princeton.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_PRINCETON_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_came_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/came.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_CAME_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_came_twee_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/came_twee.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_CAME_TWEE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_gate_tx_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/gate_tx.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_GATE_TX_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_nice_flo_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/nice_flo.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_NICE_FLO_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_keeloq_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/doorhan.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_KEELOQ_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_linear_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/linear.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_LINEAR_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_linear_delta3_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/linear_delta3.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_megacode_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/megacode.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_MEGACODE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_holtek_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/holtek.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_HOLTEK_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_secplus_v1_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/security_pls_1_0.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_SECPLUS_V1_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_secplus_v2_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/security_pls_2_0.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_SECPLUS_V2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_power_smart_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/power_smart.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_POWER_SMART_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_marantec_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/marantec.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_MARANTEC_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_bett_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/bett.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_BETT_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_doitrand_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/doitrand.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_DOITRAND_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_phoenix_v2_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/phoenix_v2.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_PHOENIX_V2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_honeywell_wdb_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/honeywell_wdb.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_magellan_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/magellan.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_MAGELLAN_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_intertechno_v3_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/intertechno_v3.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_clemsa_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/clemsa.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_CLEMSA_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_ansonic_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/ansonic.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_ANSONIC_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_smc5326_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/smc5326.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_SMC5326_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_holtek_ht12x_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/holtek_ht12x.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_dooya_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/dooya.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_DOOYA_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_mastercode_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/mastercode.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_MASTERCODE_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_dickert_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/dickert_mahs.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_legrand_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/legrand.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_LEGRAND_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_feron_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/feron.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_FERON_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_gangqi_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/gangqi.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_GANGQI_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_hollarm_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/hollarm.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_HOLLARM_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_reversrb2_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/revers_rb2.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_REVERSRB2_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_roger_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/roger.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_ROGER_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_encoder_marantec24_test) {\n    mu_assert(\n        subghz_encoder_test(EXT_PATH(\"unit_tests/subghz/marantec24.sub\")),\n        \"Test encoder \" SUBGHZ_PROTOCOL_MARANTEC24_NAME \" error\\r\\n\");\n}\n\nMU_TEST(subghz_random_test) {\n    mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), \"Random test error\\r\\n\");\n}\n\nMU_TEST_SUITE(subghz) {\n    subghz_test_init();\n    MU_RUN_TEST(subghz_keystore_test);\n\n    MU_RUN_TEST(subghz_hal_async_tx_test);\n\n    MU_RUN_TEST(subghz_decoder_came_atomo_test);\n    MU_RUN_TEST(subghz_decoder_came_test);\n    MU_RUN_TEST(subghz_decoder_came_twee_test);\n    MU_RUN_TEST(subghz_decoder_faac_slh_test);\n    MU_RUN_TEST(subghz_decoder_gate_tx_test);\n    MU_RUN_TEST(subghz_decoder_hormann_hsm_test);\n    MU_RUN_TEST(subghz_decoder_ido_test);\n    MU_RUN_TEST(subghz_decoder_keeloq_test);\n    MU_RUN_TEST(subghz_decoder_kia_seed_test);\n    MU_RUN_TEST(subghz_decoder_nero_radio_test);\n    MU_RUN_TEST(subghz_decoder_nero_sketch_test);\n    MU_RUN_TEST(subghz_decoder_nice_flo_test);\n    MU_RUN_TEST(subghz_decoder_nice_flor_s_test);\n    MU_RUN_TEST(subghz_decoder_princeton_test);\n    MU_RUN_TEST(subghz_decoder_scher_khan_magic_code_test);\n    MU_RUN_TEST(subghz_decoder_somfy_keytis_test);\n    MU_RUN_TEST(subghz_decoder_somfy_telis_test);\n    MU_RUN_TEST(subghz_decoder_star_line_test);\n    MU_RUN_TEST(subghz_decoder_linear_test);\n    MU_RUN_TEST(subghz_decoder_linear_delta3_test);\n    MU_RUN_TEST(subghz_decoder_megacode_test);\n    MU_RUN_TEST(subghz_decoder_secplus_v1_test);\n    MU_RUN_TEST(subghz_decoder_secplus_v2_test);\n    MU_RUN_TEST(subghz_decoder_holtek_test);\n    MU_RUN_TEST(subghz_decoder_power_smart_test);\n    MU_RUN_TEST(subghz_decoder_marantec_test);\n    MU_RUN_TEST(subghz_decoder_bett_test);\n    MU_RUN_TEST(subghz_decoder_doitrand_test);\n    MU_RUN_TEST(subghz_decoder_phoenix_v2_test);\n    MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);\n    MU_RUN_TEST(subghz_decoder_magellan_test);\n    MU_RUN_TEST(subghz_decoder_intertechno_v3_test);\n    MU_RUN_TEST(subghz_decoder_clemsa_test);\n    MU_RUN_TEST(subghz_decoder_ansonic_test);\n    MU_RUN_TEST(subghz_decoder_smc5326_test);\n    MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);\n    MU_RUN_TEST(subghz_decoder_dooya_test);\n    MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);\n    MU_RUN_TEST(subghz_decoder_nice_one_test);\n    MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);\n    MU_RUN_TEST(subghz_decoder_mastercode_test);\n    MU_RUN_TEST(subghz_decoder_dickert_test);\n    MU_RUN_TEST(subghz_decoder_roger_test);\n    MU_RUN_TEST(subghz_decoder_hollarm_test);\n    MU_RUN_TEST(subghz_decoder_reversrb2_test);\n    MU_RUN_TEST(subghz_decoder_gangqi_test);\n    MU_RUN_TEST(subghz_decoder_hay21_test);\n    MU_RUN_TEST(subghz_decoder_feron_test);\n    MU_RUN_TEST(subghz_decoder_legrand_test);\n    MU_RUN_TEST(subghz_decoder_marantec24_test);\n\n    MU_RUN_TEST(subghz_encoder_princeton_test);\n    MU_RUN_TEST(subghz_encoder_came_test);\n    MU_RUN_TEST(subghz_encoder_came_twee_test);\n    MU_RUN_TEST(subghz_encoder_gate_tx_test);\n    MU_RUN_TEST(subghz_encoder_nice_flo_test);\n    MU_RUN_TEST(subghz_encoder_keeloq_test);\n    MU_RUN_TEST(subghz_encoder_linear_test);\n    MU_RUN_TEST(subghz_encoder_linear_delta3_test);\n    MU_RUN_TEST(subghz_encoder_megacode_test);\n    MU_RUN_TEST(subghz_encoder_holtek_test);\n    MU_RUN_TEST(subghz_encoder_secplus_v1_test);\n    MU_RUN_TEST(subghz_encoder_secplus_v2_test);\n    MU_RUN_TEST(subghz_encoder_power_smart_test);\n    MU_RUN_TEST(subghz_encoder_marantec_test);\n    MU_RUN_TEST(subghz_encoder_bett_test);\n    MU_RUN_TEST(subghz_encoder_doitrand_test);\n    MU_RUN_TEST(subghz_encoder_phoenix_v2_test);\n    MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);\n    MU_RUN_TEST(subghz_encoder_magellan_test);\n    MU_RUN_TEST(subghz_encoder_intertechno_v3_test);\n    MU_RUN_TEST(subghz_encoder_clemsa_test);\n    MU_RUN_TEST(subghz_encoder_ansonic_test);\n    MU_RUN_TEST(subghz_encoder_smc5326_test);\n    MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);\n    MU_RUN_TEST(subghz_encoder_dooya_test);\n    MU_RUN_TEST(subghz_encoder_mastercode_test);\n    MU_RUN_TEST(subghz_encoder_dickert_test);\n    MU_RUN_TEST(subghz_encoder_feron_test);\n    MU_RUN_TEST(subghz_encoder_roger_test);\n    MU_RUN_TEST(subghz_encoder_gangqi_test);\n    MU_RUN_TEST(subghz_encoder_marantec24_test);\n    MU_RUN_TEST(subghz_encoder_hollarm_test);\n    MU_RUN_TEST(subghz_encoder_reversrb2_test);\n    MU_RUN_TEST(subghz_encoder_legrand_test);\n\n    MU_RUN_TEST(subghz_random_test);\n    subghz_test_deinit();\n}\n\nint run_minunit_test_subghz(void) {\n    MU_RUN_SUITE(subghz);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_subghz)\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/test.h",
    "content": "#pragma once\n\n// Framework\n#include \"minunit.h\"\n\n#include \"test_api.h\"\n\nint get_minunit_run(void);\n\nint get_minunit_assert(void);\n\nint get_minunit_status(void);\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/test_api.h",
    "content": "#pragma once\n\n#include <flipper_application/flipper_application.h>\n\n#define APPID       \"UnitTest\"\n#define API_VERSION (0u)\n\ntypedef struct {\n    int (*run)(void);\n    int (*get_minunit_run)(void);\n    int (*get_minunit_assert)(void);\n    int (*get_minunit_status)(void);\n} TestApi;\n\n#define TEST_API_DEFINE(entrypoint)                     \\\n    const TestApi test_api = {                          \\\n        .run = entrypoint,                              \\\n        .get_minunit_run = get_minunit_run,             \\\n        .get_minunit_assert = get_minunit_assert,       \\\n        .get_minunit_status = get_minunit_status,       \\\n    };                                                  \\\n    const FlipperAppPluginDescriptor app_descriptor = { \\\n        .appid = APPID,                                 \\\n        .ep_api_version = API_VERSION,                  \\\n        .entry_point = &test_api,                       \\\n    };                                                  \\\n    const FlipperAppPluginDescriptor* get_api(void) {   \\\n        return &app_descriptor;                         \\\n    }\n"
  },
  {
    "path": "applications/debug/unit_tests/tests/varint/varint_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include \"../test.h\" // IWYU pragma: keep\n\n#include <toolbox/varint.h>\n#include <toolbox/profiler.h>\n\nMU_TEST(test_varint_basic_u) {\n    mu_assert_int_eq(1, varint_uint32_length(0));\n    mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX));\n\n    uint8_t data[8] = {};\n    uint32_t out_value;\n\n    mu_assert_int_eq(1, varint_uint32_pack(0, data));\n    mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(0, out_value);\n\n    mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data));\n    mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(UINT32_MAX, out_value);\n}\n\nMU_TEST(test_varint_basic_i) {\n    mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2));\n    mu_assert_int_eq(1, varint_int32_length(0));\n    mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2));\n\n    mu_assert_int_eq(2, varint_int32_length(127));\n    mu_assert_int_eq(2, varint_int32_length(-127));\n\n    uint8_t data[8] = {};\n    int32_t out_value;\n    mu_assert_int_eq(1, varint_int32_pack(0, data));\n    mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(0, out_value);\n\n    mu_assert_int_eq(2, varint_int32_pack(127, data));\n    mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(127, out_value);\n\n    mu_assert_int_eq(2, varint_int32_pack(-127, data));\n    mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(-127, out_value);\n\n    mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data));\n    mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(INT32_MAX, out_value);\n\n    mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data));\n    mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));\n    mu_assert_int_eq(INT32_MIN / 2 + 1, out_value);\n}\n\nMU_TEST(test_varint_rand_u) {\n    uint8_t data[8] = {};\n    uint32_t out_value;\n\n    for(size_t i = 0; i < 200000; i++) {\n        uint32_t rand_value = rand();\n        mu_assert_int_eq(\n            varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8));\n        mu_assert_int_eq(rand_value, out_value);\n    }\n}\n\nMU_TEST(test_varint_rand_i) {\n    uint8_t data[8] = {};\n    int32_t out_value;\n\n    for(size_t i = 0; i < 200000; i++) {\n        int32_t rand_value = rand() + (INT32_MIN / 2 + 1);\n        mu_assert_int_eq(\n            varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8));\n        mu_assert_int_eq(rand_value, out_value);\n    }\n}\n\nMU_TEST_SUITE(test_varint_suite) {\n    MU_RUN_TEST(test_varint_basic_u);\n    MU_RUN_TEST(test_varint_basic_i);\n    MU_RUN_TEST(test_varint_rand_u);\n    MU_RUN_TEST(test_varint_rand_i);\n}\n\nint run_minunit_test_varint(void) {\n    MU_RUN_SUITE(test_varint_suite);\n    return MU_EXIT_CODE;\n}\n\nTEST_API_DEFINE(run_minunit_test_varint)\n"
  },
  {
    "path": "applications/debug/unit_tests/unit_test_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n#include \"unit_test_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(unit_tests_api_table), \"Detected API method hash collision!\");\n\nconstexpr HashtableApiInterface unit_tests_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    unit_tests_api_table.cbegin(),\n    unit_tests_api_table.cend(),\n};\n\nextern \"C\" const ElfApiInterface* const unit_tests_api_interface =\n    &unit_tests_hashtable_api_interface;\n"
  },
  {
    "path": "applications/debug/unit_tests/unit_test_api_table_i.h",
    "content": "#include <update_util/resources/manifest.h>\n#include <nfc/protocols/slix/slix_i.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>\n#include <FreeRTOS.h>\n#include <FreeRTOS-Kernel/include/queue.h>\n#include <task.h>\n\n#include <rpc/rpc_i.h>\n#include <flipper.pb.h>\n#include <applications/system/js_app/js_thread.h>\n#include <applications/system/js_app/js_value.h>\n\nstatic constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(\n    API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)),\n    API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)),\n    API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char*)),\n    API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)),\n    API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)),\n    API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)),\n    API_METHOD(iso15693_3_poller_get_data, const Iso15693_3Data*, (Iso15693_3Poller*)),\n    API_METHOD(rpc_system_storage_get_error, PB_CommandStatus, (FS_Error)),\n    API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)),\n    API_METHOD(\n        xTaskGenericNotify,\n        BaseType_t,\n        (TaskHandle_t, UBaseType_t, uint32_t, eNotifyAction, uint32_t*)),\n    API_METHOD(xTaskGetCurrentTaskHandle, TaskHandle_t, ()),\n    API_METHOD(vQueueDelete, void, (QueueHandle_t)),\n    API_METHOD(\n        xQueueGenericCreateStatic,\n        QueueHandle_t,\n        (const UBaseType_t, const UBaseType_t, uint8_t*, StaticQueue_t*, const uint8_t)),\n    API_METHOD(\n        xQueueGenericSend,\n        BaseType_t,\n        (QueueHandle_t, const void* const, TickType_t, const BaseType_t)),\n    API_METHOD(\n        js_thread_run,\n        JsThread*,\n        (const char* script_path, JsThreadCallback callback, void* context)),\n    API_METHOD(js_thread_stop, void, (JsThread * worker)),\n    API_METHOD(js_value_buffer_size, size_t, (const JsValueParseDeclaration declaration)),\n    API_METHOD(\n        js_value_parse,\n        JsValueParseStatus,\n        (struct mjs * mjs,\n         const JsValueParseDeclaration declaration,\n         JsValueParseFlag flags,\n         mjs_val_t* buffer,\n         size_t buf_size,\n         mjs_val_t* source,\n         size_t n_c_vals,\n         ...)),\n    API_VARIABLE(PB_Main_msg, PB_Main_msg_t)));\n"
  },
  {
    "path": "applications/debug/unit_tests/unit_tests.c",
    "content": "#include <furi.h>\n#include <toolbox/pipe.h>\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/cli/cli_registry.h>\n#include <cli/cli_main_commands.h>\n\n#include \"test_runner.h\"\n\nvoid unit_tests_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n\n    TestRunner* test_runner = test_runner_alloc(pipe, args);\n    test_runner_run(test_runner);\n    test_runner_free(test_runner);\n}\n\nvoid unit_tests_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(\n        registry, \"unit_tests\", CliCommandFlagParallelSafe, unit_tests_cli, NULL);\n    furi_record_close(RECORD_CLI);\n#endif\n}\n"
  },
  {
    "path": "applications/debug/usb_mouse/application.fam",
    "content": "App(\n    appid=\"usb_mouse\",\n    name=\"USB Mouse Demo\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"usb_mouse_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/usb_mouse/usb_mouse.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/gui.h>\n#include <input/input.h>\n\n#define MOUSE_MOVE_SHORT 5\n#define MOUSE_MOVE_LONG  20\n\ntypedef enum {\n    EventTypeInput,\n} EventType;\n\ntypedef struct {\n    union {\n        InputEvent input;\n    };\n    EventType type;\n} UsbMouseEvent;\n\nstatic void usb_mouse_render_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n    canvas_clear(canvas);\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, 10, \"USB Mouse Demo\");\n\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 0, 63, \"Hold [back] to exit\");\n}\n\nstatic void usb_mouse_input_callback(InputEvent* input_event, void* ctx) {\n    FuriMessageQueue* event_queue = ctx;\n\n    UsbMouseEvent event;\n    event.type = EventTypeInput;\n    event.input = *input_event;\n    furi_message_queue_put(event_queue, &event, FuriWaitForever);\n}\n\nint32_t usb_mouse_app(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent));\n    ViewPort* view_port = view_port_alloc();\n\n    FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();\n    furi_hal_usb_unlock();\n    furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);\n\n    view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL);\n    view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue);\n\n    // Open GUI and register view_port\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    UsbMouseEvent event;\n    while(1) {\n        FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);\n\n        if(event_status == FuriStatusOk) {\n            if(event.type == EventTypeInput) {\n                if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {\n                    break;\n                }\n\n                if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {\n                    furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT);\n                    furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);\n                }\n\n                if(event.input.key == InputKeyOk) {\n                    if(event.input.type == InputTypePress) {\n                        furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT);\n                    } else if(event.input.type == InputTypeRelease) {\n                        furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);\n                    }\n                }\n\n                if(event.input.key == InputKeyRight) {\n                    if(event.input.type == InputTypePress) {\n                        furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);\n                    } else if(event.input.type == InputTypeRepeat) {\n                        furi_hal_hid_mouse_move(MOUSE_MOVE_LONG, 0);\n                    }\n                }\n\n                if(event.input.key == InputKeyLeft) {\n                    if(event.input.type == InputTypePress) {\n                        furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);\n                    } else if(event.input.type == InputTypeRepeat) {\n                        furi_hal_hid_mouse_move(-MOUSE_MOVE_LONG, 0);\n                    }\n                }\n\n                if(event.input.key == InputKeyDown) {\n                    if(event.input.type == InputTypePress) {\n                        furi_hal_hid_mouse_move(0, MOUSE_MOVE_SHORT);\n                    } else if(event.input.type == InputTypeRepeat) {\n                        furi_hal_hid_mouse_move(0, MOUSE_MOVE_LONG);\n                    }\n                }\n\n                if(event.input.key == InputKeyUp) {\n                    if(event.input.type == InputTypePress) {\n                        furi_hal_hid_mouse_move(0, -MOUSE_MOVE_SHORT);\n                    } else if(event.input.type == InputTypeRepeat) {\n                        furi_hal_hid_mouse_move(0, -MOUSE_MOVE_LONG);\n                    }\n                }\n            }\n        }\n        view_port_update(view_port);\n    }\n\n    furi_hal_usb_set_config(usb_mode_prev, NULL);\n\n    // remove & free all stuff created by app\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/usb_test/application.fam",
    "content": "App(\n    appid=\"usb_test\",\n    name=\"USB Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"usb_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/usb_test/usb_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n#include <gui/gui.h>\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Submenu* submenu;\n    FuriHalUsbHidConfig hid_cfg;\n} UsbTestApp;\n\ntypedef enum {\n    UsbTestSubmenuIndexEnable,\n    UsbTestSubmenuIndexDisable,\n    UsbTestSubmenuIndexRestart,\n    UsbTestSubmenuIndexVcpSingle,\n    UsbTestSubmenuIndexVcpDual,\n    UsbTestSubmenuIndexHid,\n    UsbTestSubmenuIndexHidWithParams,\n    UsbTestSubmenuIndexHidU2F,\n} SubmenuIndex;\n\nvoid usb_test_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    UsbTestApp* app = context;\n    if(index == UsbTestSubmenuIndexEnable) {\n        furi_hal_usb_enable();\n    } else if(index == UsbTestSubmenuIndexDisable) {\n        furi_hal_usb_disable();\n    } else if(index == UsbTestSubmenuIndexRestart) {\n        furi_hal_usb_reinit();\n    } else if(index == UsbTestSubmenuIndexVcpSingle) {\n        furi_hal_usb_set_config(&usb_cdc_single, NULL);\n    } else if(index == UsbTestSubmenuIndexVcpDual) {\n        furi_hal_usb_set_config(&usb_cdc_dual, NULL);\n    } else if(index == UsbTestSubmenuIndexHid) {\n        furi_hal_usb_set_config(&usb_hid, NULL);\n    } else if(index == UsbTestSubmenuIndexHidWithParams) {\n        app->hid_cfg.vid = 0x1234;\n        app->hid_cfg.pid = 0xabcd;\n        strlcpy(app->hid_cfg.manuf, \"WEN\", sizeof(app->hid_cfg.manuf));\n        strlcpy(app->hid_cfg.product, \"FLIP\", sizeof(app->hid_cfg.product));\n        furi_hal_usb_set_config(&usb_hid, &app->hid_cfg);\n    } else if(index == UsbTestSubmenuIndexHidU2F) {\n        furi_hal_usb_set_config(&usb_hid_u2f, NULL);\n    }\n}\n\nuint32_t usb_test_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nUsbTestApp* usb_test_app_alloc(void) {\n    UsbTestApp* app = malloc(sizeof(UsbTestApp));\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->submenu = submenu_alloc();\n    submenu_add_item(\n        app->submenu, \"Enable\", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Disable\", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Restart\", UsbTestSubmenuIndexRestart, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Single VCP\", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Dual VCP\", UsbTestSubmenuIndexVcpDual, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"HID KB+Mouse\", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app);\n    submenu_add_item(\n        app->submenu,\n        \"HID KB+Mouse custom ID\",\n        UsbTestSubmenuIndexHidWithParams,\n        usb_test_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu, \"HID U2F\", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app);\n    view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit);\n    view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu));\n\n    // Switch to menu\n    view_dispatcher_switch_to_view(app->view_dispatcher, 0);\n\n    return app;\n}\n\nvoid usb_test_app_free(UsbTestApp* app) {\n    furi_assert(app);\n\n    // Free views\n    view_dispatcher_remove_view(app->view_dispatcher, 0);\n    submenu_free(app->submenu);\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Close gui record\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    // Free rest\n    free(app);\n}\n\nint32_t usb_test_app(void* p) {\n    UNUSED(p);\n    UsbTestApp* app = usb_test_app_alloc();\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    usb_test_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/debug/vibro_test/application.fam",
    "content": "App(\n    appid=\"vibro_test\",\n    name=\"Vibro Test\",\n    apptype=FlipperAppType.DEBUG,\n    entry_point=\"vibro_test_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Debug\",\n)\n"
  },
  {
    "path": "applications/debug/vibro_test/vibro_test.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <input/input.h>\n#include <notification/notification_messages.h>\n\nvoid vibro_test_draw_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n    canvas_clear(canvas);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 2, 10, \"Vibro application\");\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 2, 22, \"Press OK turns on vibro\");\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 2, 34, \"Release OK turns off vibro\");\n}\n\nvoid vibro_test_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t vibro_test_app(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, vibro_test_draw_callback, NULL);\n    view_port_input_callback_set(view_port, vibro_test_input_callback, event_queue);\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n\n    InputEvent event;\n\n    while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {\n        if(event.type == InputTypeShort && event.key == InputKeyBack) {\n            notification_message(notification, &sequence_reset_vibro);\n            notification_message(notification, &sequence_reset_green);\n            break;\n        }\n        if(event.key == InputKeyOk) {\n            if(event.type == InputTypePress) {\n                notification_message(notification, &sequence_set_vibro_on);\n                notification_message(notification, &sequence_set_green_255);\n            } else if(event.type == InputTypeRelease) {\n                notification_message(notification, &sequence_reset_vibro);\n                notification_message(notification, &sequence_reset_green);\n            }\n        }\n    }\n\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/drivers/application.fam",
    "content": "# Placeholder\nApp(\n    appid=\"drivers\",\n    name=\"Drivers device\",\n    apptype=FlipperAppType.METAPACKAGE,\n)\n"
  },
  {
    "path": "applications/drivers/subghz/application.fam",
    "content": "App(\n    appid=\"radio_device_cc1101_ext\",\n    apptype=FlipperAppType.PLUGIN,\n    targets=[\"f7\"],\n    entry_point=\"subghz_device_cc1101_ext_ep\",\n    requires=[\"subghz\"],\n    sdk_headers=[\"cc1101_ext/cc1101_ext_interconnect.h\"],\n    fap_libs=[\"hwdrivers\"],\n)\n"
  },
  {
    "path": "applications/drivers/subghz/cc1101_ext/cc1101_ext.c",
    "content": "#include \"cc1101_ext.h\"\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#include <furi_hal_region.h>\n#include <furi_hal_version.h>\n#include <furi_hal_rtc.h>\n#include <furi_hal_spi.h>\n#include <furi_hal_interrupt.h>\n#include <furi_hal_resources.h>\n#include <furi_hal_bus.h>\n\n#include <stm32wbxx_ll_dma.h>\n#include <furi_hal_cortex.h>\n\n#include <furi.h>\n#include <cc1101.h>\n#include <stdio.h>\n\n#define TAG \"SubGhzDeviceCc1101Ext\"\n\n#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO (&gpio_ext_pb2)\n\n/* DMA Channels definition */\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA             (DMA2)\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL (LL_DMA_CHANNEL_3)\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL (LL_DMA_CHANNEL_4)\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL (LL_DMA_CHANNEL_5)\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ     (FuriHalInterruptIdDma2Ch3)\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \\\n    SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \\\n    SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL\n#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF \\\n    SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL\n\n/** Low level buffer dimensions and guard times */\n#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256u)\n#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \\\n    (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2)\n#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME (999u >> 1)\n\n/** SubGhz state */\ntypedef enum {\n    SubGhzDeviceCC1101ExtStateInit, /**< Init pending */\n    SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */\n    SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */\n    SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */\n} SubGhzDeviceCC1101ExtState;\n\n/** SubGhz regulation, receive transmission on the current frequency for the\n * region */\ntypedef enum {\n    SubGhzDeviceCC1101ExtRegulationOnlyRx, /**only Rx*/\n    SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/\n} SubGhzDeviceCC1101ExtRegulation;\n\ntypedef enum {\n    SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle,\n    SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset,\n    SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun,\n} SubGhzDeviceCC1101ExtAsyncTxMiddlewareState;\n\ntypedef struct {\n    SubGhzDeviceCC1101ExtAsyncTxMiddlewareState state;\n    bool is_odd_level;\n    uint32_t adder_duration;\n} SubGhzDeviceCC1101ExtAsyncTxMiddleware;\n\ntypedef struct {\n    uint32_t* buffer;\n    SubGhzDeviceCC1101ExtCallback callback;\n    void* callback_context;\n    uint32_t gpio_tx_buff[2];\n    uint32_t debug_gpio_buff[2];\n    SubGhzDeviceCC1101ExtAsyncTxMiddleware middleware;\n} SubGhzDeviceCC1101ExtAsyncTx;\n\ntypedef struct {\n    uint32_t capture_delta_duration;\n    SubGhzDeviceCC1101ExtCaptureCallback capture_callback;\n    void* capture_callback_context;\n} SubGhzDeviceCC1101ExtAsyncRx;\n\ntypedef struct {\n    volatile SubGhzDeviceCC1101ExtState state;\n    volatile SubGhzDeviceCC1101ExtRegulation regulation;\n    const GpioPin* async_mirror_pin;\n    const FuriHalSpiBusHandle* spi_bus_handle;\n    const GpioPin* g0_pin;\n    SubGhzDeviceCC1101ExtAsyncTx async_tx;\n    SubGhzDeviceCC1101ExtAsyncRx async_rx;\n} SubGhzDeviceCC1101Ext;\n\nstatic SubGhzDeviceCC1101Ext* subghz_device_cc1101_ext = NULL;\n\nstatic bool subghz_device_cc1101_ext_check_init(void) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateInit);\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle;\n\n    bool ret = false;\n    CC1101Status cc1101_status = {0};\n\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    FuriHalCortexTimer timer = furi_hal_cortex_timer_get(100 * 1000);\n    do {\n        // Reset\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->spi_bus_handle->miso,\n            GpioModeInput,\n            GpioPullUp,\n            GpioSpeedLow);\n\n        cc1101_status = cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        cc1101_status = cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        // Prepare GD0 for power on self test\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullUp, GpioSpeedLow);\n\n        // GD0 low\n        cc1101_status = cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != false) {\n            if(furi_hal_cortex_timer_is_expired(timer)) {\n                //timeout\n                break;\n            }\n        }\n        if(furi_hal_cortex_timer_is_expired(timer)) {\n            //timeout\n            break;\n        }\n\n        // GD0 high\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullDown, GpioSpeedLow);\n        cc1101_status = cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle,\n            CC1101_IOCFG0,\n            CC1101IocfgHW | CC1101_IOCFG_INV);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != true) {\n            if(furi_hal_cortex_timer_is_expired(timer)) {\n                //timeout\n                break;\n            }\n        }\n        if(furi_hal_cortex_timer_is_expired(timer)) {\n            //timeout\n            break;\n        }\n\n        // Reset GD0 to floating state\n        cc1101_status = cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n\n        // Reset GDO2 (!TX/RX) to floating state\n        cc1101_status = cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n\n        // Go to sleep\n        cc1101_status = cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle);\n        if(cc1101_status.CHIP_RDYn != 0) {\n            //timeout or error\n            break;\n        }\n        ret = true;\n    } while(false);\n\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n\n    if(ret) {\n        FURI_LOG_I(TAG, \"Init OK\");\n    } else {\n        FURI_LOG_E(TAG, \"Init failed\");\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n    }\n    return ret;\n}\n\nbool subghz_device_cc1101_ext_alloc(void) {\n    furi_assert(subghz_device_cc1101_ext == NULL);\n    subghz_device_cc1101_ext = malloc(sizeof(SubGhzDeviceCC1101Ext));\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateInit;\n    subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx;\n    subghz_device_cc1101_ext->async_mirror_pin = NULL;\n    subghz_device_cc1101_ext->spi_bus_handle = &furi_hal_spi_bus_handle_external;\n    subghz_device_cc1101_ext->g0_pin = SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO;\n\n    subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0;\n\n    furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle);\n    return subghz_device_cc1101_ext_check_init();\n}\n\nvoid subghz_device_cc1101_ext_free(void) {\n    furi_assert(subghz_device_cc1101_ext != NULL);\n    furi_hal_spi_bus_handle_deinit(subghz_device_cc1101_ext->spi_bus_handle);\n    free(subghz_device_cc1101_ext);\n    subghz_device_cc1101_ext = NULL;\n}\n\nvoid subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin) {\n    subghz_device_cc1101_ext->async_mirror_pin = pin;\n}\n\nconst GpioPin* subghz_device_cc1101_ext_get_data_gpio(void) {\n    return subghz_device_cc1101_ext->g0_pin;\n}\n\nbool subghz_device_cc1101_ext_is_connect(void) {\n    bool ret = false;\n\n    if(subghz_device_cc1101_ext == NULL) { // not initialized\n        ret = subghz_device_cc1101_ext_alloc();\n        subghz_device_cc1101_ext_free();\n    } else { // initialized\n        furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n        uint8_t partnumber = cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle);\n        furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n        ret = (partnumber != 0) && (partnumber != 0xFF);\n    }\n\n    return ret;\n}\n\nvoid subghz_device_cc1101_ext_sleep(void) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle);\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n\n    cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);\n\n    cc1101_write_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);\n    furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n\n    cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle);\n\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_dump_state(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    printf(\n        \"[subghz_device_cc1101_ext] cc1101 chip %d, version %d\\r\\n\",\n        cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle),\n        cc1101_get_version(subghz_device_cc1101_ext->spi_bus_handle));\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) {\n    //load config\n    subghz_device_cc1101_ext_reset();\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    uint32_t i = 0;\n    uint8_t pa[8] = {0};\n    while(preset_data[i]) {\n        cc1101_write_reg(\n            subghz_device_cc1101_ext->spi_bus_handle, preset_data[i], preset_data[i + 1]);\n        i += 2;\n    }\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n\n    //load pa table\n    memcpy(&pa[0], &preset_data[i + 2], 8);\n    subghz_device_cc1101_ext_load_patable(pa);\n\n    //show debug\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        i = 0;\n        FURI_LOG_D(TAG, \"Loading custom preset\");\n        while(preset_data[i]) {\n            FURI_LOG_D(TAG, \"Reg[%lu]: %02X=%02X\", i, preset_data[i], preset_data[i + 1]);\n            i += 2;\n        }\n        for(uint8_t y = i; y < i + 10; y++) {\n            FURI_LOG_D(TAG, \"PA[%u]:  %02X\", y, preset_data[y]);\n        }\n    }\n}\n\nvoid subghz_device_cc1101_ext_load_registers(const uint8_t* data) {\n    subghz_device_cc1101_ext_reset();\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    uint32_t i = 0;\n    while(data[i]) {\n        cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]);\n        i += 2;\n    }\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_load_patable(const uint8_t data[8]) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_set_pa_table(subghz_device_cc1101_ext->spi_bus_handle, data);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_FIFO, size);\n    cc1101_write_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_flush_rx(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_flush_rx(subghz_device_cc1101_ext->spi_bus_handle);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_flush_tx(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nbool subghz_device_cc1101_ext_rx_pipe_not_empty(void) {\n    CC1101RxBytes status[1];\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_read_reg(\n        subghz_device_cc1101_ext->spi_bus_handle,\n        (CC1101_STATUS_RXBYTES) | CC1101_BURST,\n        (uint8_t*)status);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n    if(status->NUM_RXBYTES > 0) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool subghz_device_cc1101_ext_is_rx_data_crc_valid(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    uint8_t data[1];\n    cc1101_read_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n    if((data[0] >> 7) & 0x01) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_read_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_shutdown(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    // Reset and shutdown\n    cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_reset(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n    cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);\n    // Warning: push pull cc1101 clock output on GD0\n    cc1101_write_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);\n    // Reset GDO2 (!TX/RX) to floating state\n    cc1101_write_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_idle(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);\n    //waiting for the chip to switch to IDLE mode\n    furi_check(cc1101_wait_status_state(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000));\n    // Reset GDO2 (!TX/RX) to floating state\n    cc1101_write_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nvoid subghz_device_cc1101_ext_rx(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle);\n    //waiting for the chip to switch to Rx mode\n    furi_check(\n        cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000));\n    // Go GDO2 (!TX/RX) to high (RX state)\n    cc1101_write_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);\n\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n}\n\nbool subghz_device_cc1101_ext_tx(void) {\n    if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false;\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle);\n    //waiting for the chip to switch to Tx mode\n    furi_check(\n        cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000));\n    // Go GDO2 (!TX/RX) to low (TX state)\n    cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n    return true;\n}\n\nfloat subghz_device_cc1101_ext_get_rssi(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    int32_t rssi_dec = cc1101_get_rssi(subghz_device_cc1101_ext->spi_bus_handle);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n\n    float rssi = rssi_dec;\n    if(rssi_dec >= 128) {\n        rssi = ((rssi - 256.0f) / 2.0f) - 74.0f;\n    } else {\n        rssi = (rssi / 2.0f) - 74.0f;\n    }\n\n    return rssi;\n}\n\nuint8_t subghz_device_cc1101_ext_get_lqi(void) {\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    uint8_t data[1];\n    cc1101_read_reg(\n        subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data);\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n    return data[0] & 0x7F;\n}\n\nbool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value) {\n    if(!(value >= 299999755 && value <= 348000335) &&\n       !(value >= 386999938 && value <= 464000000) &&\n       !(value >= 778999847 && value <= 928000000)) {\n        return false;\n    }\n\n    return true;\n}\n\nuint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value) {\n    if(furi_hal_region_is_frequency_allowed(value)) {\n        subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx;\n    } else {\n        subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationOnlyRx;\n    }\n\n    furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);\n    uint32_t real_frequency =\n        cc1101_set_frequency(subghz_device_cc1101_ext->spi_bus_handle, value);\n    cc1101_calibrate(subghz_device_cc1101_ext->spi_bus_handle);\n\n    while(true) {\n        CC1101Status status = cc1101_get_status(subghz_device_cc1101_ext->spi_bus_handle);\n        if(status.STATE == CC1101StateIDLE) break;\n    }\n\n    furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);\n    return real_frequency;\n}\n\nstatic bool subghz_device_cc1101_ext_start_debug(void) {\n    bool ret = false;\n    if(subghz_device_cc1101_ext->async_mirror_pin != NULL) {\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->async_mirror_pin,\n            GpioModeOutputPushPull,\n            GpioPullNo,\n            GpioSpeedVeryHigh);\n        ret = true;\n    }\n    return ret;\n}\n\nstatic bool subghz_device_cc1101_ext_stop_debug(void) {\n    bool ret = false;\n    if(subghz_device_cc1101_ext->async_mirror_pin != NULL) {\n        furi_hal_gpio_init(\n            subghz_device_cc1101_ext->async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n        ret = true;\n    }\n    return ret;\n}\n\nstatic void subghz_device_cc1101_ext_capture_ISR(void* context) {\n    UNUSED(context);\n    if(!furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin)) {\n        if(subghz_device_cc1101_ext->async_rx.capture_callback) {\n            if(subghz_device_cc1101_ext->async_mirror_pin != NULL)\n                furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false);\n\n            subghz_device_cc1101_ext->async_rx.capture_callback(\n                true,\n                LL_TIM_GetCounter(TIM17) << 1,\n                (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context);\n        }\n    } else {\n        if(subghz_device_cc1101_ext->async_rx.capture_callback) {\n            if(subghz_device_cc1101_ext->async_mirror_pin != NULL)\n                furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, true);\n\n            subghz_device_cc1101_ext->async_rx.capture_callback(\n                false,\n                LL_TIM_GetCounter(TIM17) << 1,\n                (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context);\n        }\n    }\n    LL_TIM_SetCounter(TIM17, 4); //8>>1\n}\n\nvoid subghz_device_cc1101_ext_start_async_rx(\n    SubGhzDeviceCC1101ExtCaptureCallback callback,\n    void* context) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle);\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncRx;\n\n    subghz_device_cc1101_ext->async_rx.capture_callback = callback;\n    subghz_device_cc1101_ext->async_rx.capture_callback_context = context;\n\n    furi_hal_bus_enable(FuriHalBusTIM17);\n\n    // Configure TIM\n    LL_TIM_InitTypeDef TIM_InitStruct = {0};\n    //Set the timer resolution to 2 us\n    TIM_InitStruct.Prescaler = (64 << 1) - 1;\n    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;\n    TIM_InitStruct.Autoreload = 0xFFFF;\n    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;\n    LL_TIM_Init(TIM17, &TIM_InitStruct);\n\n    // Timer: advanced\n    LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);\n    LL_TIM_DisableARRPreload(TIM17);\n    LL_TIM_DisableDMAReq_TRIG(TIM17);\n    LL_TIM_DisableIT_TRIG(TIM17);\n\n    furi_hal_gpio_init(\n        subghz_device_cc1101_ext->g0_pin, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);\n    furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin);\n    furi_hal_gpio_add_int_callback(\n        subghz_device_cc1101_ext->g0_pin,\n        subghz_device_cc1101_ext_capture_ISR,\n        subghz_device_cc1101_ext->async_rx.capture_callback);\n\n    // Start timer\n    LL_TIM_SetCounter(TIM17, 0);\n    LL_TIM_EnableCounter(TIM17);\n\n    // Start debug\n    subghz_device_cc1101_ext_start_debug();\n\n    // Switch to RX\n    subghz_device_cc1101_ext_rx();\n\n    //Clear the variable after the end of the session\n    subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0;\n}\n\nvoid subghz_device_cc1101_ext_stop_async_rx(void) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncRx);\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle;\n\n    // Shutdown radio\n    subghz_device_cc1101_ext_idle();\n\n    FURI_CRITICAL_ENTER();\n    furi_hal_bus_disable(FuriHalBusTIM17);\n\n    // Stop debug\n    subghz_device_cc1101_ext_stop_debug();\n\n    FURI_CRITICAL_EXIT();\n    furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin);\n    furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n}\n\nvoid subghz_device_cc1101_ext_async_tx_middleware_idle(\n    SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware) {\n    middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle;\n    middleware->is_odd_level = false;\n    middleware->adder_duration = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;\n}\n\nstatic inline uint32_t subghz_device_cc1101_ext_async_tx_middleware_get_duration(\n    SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware,\n    SubGhzDeviceCC1101ExtCallback callback) {\n    uint32_t ret = 0;\n    bool is_level = false;\n\n    if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset) return 0;\n\n    while(1) {\n        LevelDuration ld = callback(subghz_device_cc1101_ext->async_tx.callback_context);\n        if(level_duration_is_reset(ld)) {\n            middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset;\n            if(!middleware->is_odd_level) {\n                return 0;\n            } else {\n                return middleware->adder_duration;\n            }\n        } else if(level_duration_is_wait(ld)) {\n            middleware->is_odd_level = !middleware->is_odd_level;\n            ret = middleware->adder_duration + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;\n            middleware->adder_duration = 0;\n            return ret;\n        }\n\n        is_level = level_duration_get_level(ld);\n\n        if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle) {\n            if(is_level != middleware->is_odd_level) {\n                middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun;\n                middleware->is_odd_level = is_level;\n                middleware->adder_duration = level_duration_get_duration(ld);\n                return SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;\n            } else {\n                continue;\n            }\n        }\n\n        if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun) {\n            if(is_level == middleware->is_odd_level) {\n                middleware->adder_duration += level_duration_get_duration(ld);\n                continue;\n            } else {\n                middleware->is_odd_level = is_level;\n                ret = middleware->adder_duration;\n                middleware->adder_duration = level_duration_get_duration(ld);\n                return ret;\n            }\n        }\n    }\n}\n\nstatic void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);\n\n    while(samples > 0) {\n        volatile uint32_t duration = subghz_device_cc1101_ext_async_tx_middleware_get_duration(\n            &subghz_device_cc1101_ext->async_tx.middleware,\n            subghz_device_cc1101_ext->async_tx.callback);\n        if(duration == 0) {\n            *buffer = 0;\n            buffer++;\n            samples--;\n            LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n            LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n            if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {\n                LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA);\n            }\n            if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {\n                LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA);\n            }\n            break;\n        } else {\n            // Lowest possible value is 4us\n            if(duration < 4) duration = 4;\n            // Divide by 2 since timer resolution is 2us\n            // Subtract 1 since we counting from 0\n            *buffer = (duration >> 1) - 1;\n            buffer++;\n            samples--;\n        }\n    }\n}\n\nstatic void subghz_device_cc1101_ext_async_tx_dma_isr(void* context) {\n    UNUSED(context);\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);\n\n#if SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL == LL_DMA_CHANNEL_3\n    if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {\n        LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA);\n        subghz_device_cc1101_ext_async_tx_refill(\n            subghz_device_cc1101_ext->async_tx.buffer,\n            SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF);\n    }\n    if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {\n        LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA);\n        subghz_device_cc1101_ext_async_tx_refill(\n            subghz_device_cc1101_ext->async_tx.buffer +\n                SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF,\n            SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF);\n    }\n#else\n#error Update this code. Would you kindly?\n#endif\n}\n\nbool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle);\n    furi_assert(callback);\n\n    //If transmission is prohibited by regional settings\n    if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false;\n\n    subghz_device_cc1101_ext->async_tx.callback = callback;\n    subghz_device_cc1101_ext->async_tx.callback_context = context;\n\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTx;\n\n    subghz_device_cc1101_ext->async_tx.buffer =\n        malloc(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t));\n\n    //Signal generation with mem-to-mem DMA\n    furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);\n    furi_hal_gpio_init(\n        subghz_device_cc1101_ext->g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);\n\n    // Configure DMA  update timer\n    LL_DMA_SetMemoryAddress(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t)subghz_device_cc1101_ext->async_tx.buffer);\n    LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t) & (TIM17->ARR));\n    LL_DMA_ConfigTransfer(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF,\n        LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |\n            LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD |\n            LL_DMA_PRIORITY_VERYHIGH);\n    LL_DMA_SetDataLength(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL);\n    LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP);\n\n    LL_DMA_EnableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n    LL_DMA_EnableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n    LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n\n    furi_hal_interrupt_set_isr(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, subghz_device_cc1101_ext_async_tx_dma_isr, NULL);\n\n    furi_hal_bus_enable(FuriHalBusTIM17);\n\n    // Configure TIM\n    // Set the timer resolution to 2 us\n    LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);\n    LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);\n    LL_TIM_SetAutoReload(TIM17, 500);\n    LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);\n    LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);\n    LL_TIM_DisableARRPreload(TIM17);\n\n    subghz_device_cc1101_ext_async_tx_middleware_idle(\n        &subghz_device_cc1101_ext->async_tx.middleware);\n    subghz_device_cc1101_ext_async_tx_refill(\n        subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL);\n\n    // Configure tx gpio dma\n    const GpioPin* gpio = subghz_device_cc1101_ext->g0_pin;\n\n    subghz_device_cc1101_ext->async_tx.gpio_tx_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER;\n    subghz_device_cc1101_ext->async_tx.gpio_tx_buff[1] = gpio->pin;\n\n    LL_DMA_SetMemoryAddress(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF,\n        (uint32_t)subghz_device_cc1101_ext->async_tx.gpio_tx_buff);\n    LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, (uint32_t) & (gpio->port->BSRR));\n    LL_DMA_ConfigTransfer(\n        SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF,\n        LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |\n            LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD |\n            LL_DMA_PRIORITY_HIGH);\n    LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, 2);\n    LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, LL_DMAMUX_REQ_TIM17_UP);\n    LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF);\n\n    // Start debug\n    if(subghz_device_cc1101_ext_start_debug()) {\n        gpio = subghz_device_cc1101_ext->async_mirror_pin;\n        subghz_device_cc1101_ext->async_tx.debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER;\n        subghz_device_cc1101_ext->async_tx.debug_gpio_buff[1] = gpio->pin;\n\n        LL_DMA_SetMemoryAddress(\n            SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF,\n            (uint32_t)subghz_device_cc1101_ext->async_tx.debug_gpio_buff);\n        LL_DMA_SetPeriphAddress(\n            SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, (uint32_t) & (gpio->port->BSRR));\n        LL_DMA_ConfigTransfer(\n            SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF,\n            LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |\n                LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD |\n                LL_DMA_PRIORITY_LOW);\n        LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, 2);\n        LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, LL_DMAMUX_REQ_TIM17_UP);\n        LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF);\n    }\n\n    // Start counter\n    LL_TIM_EnableDMAReq_UPDATE(TIM17);\n\n    subghz_device_cc1101_ext_tx();\n\n    LL_TIM_SetCounter(TIM17, 0);\n    LL_TIM_EnableCounter(TIM17);\n\n    return true;\n}\n\nbool subghz_device_cc1101_ext_is_async_tx_complete(void) {\n    return (subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) &&\n           (LL_TIM_GetAutoReload(TIM17) == 0);\n}\n\nvoid subghz_device_cc1101_ext_stop_async_tx(void) {\n    furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);\n\n    // Shutdown radio\n    subghz_device_cc1101_ext_idle();\n\n    // Deinitialize GPIO\n    furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);\n    furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n\n    // Deinitialize Timer\n    furi_hal_bus_disable(FuriHalBusTIM17);\n    furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL);\n\n    // Deinitialize DMA\n    LL_DMA_DeInit(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);\n    LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF);\n    furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL);\n\n    // Stop debug\n    if(subghz_device_cc1101_ext_stop_debug()) {\n        LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF);\n    }\n\n    free(subghz_device_cc1101_ext->async_tx.buffer);\n\n    subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle;\n}\n"
  },
  {
    "path": "applications/drivers/subghz/cc1101_ext/cc1101_ext.h",
    "content": "/**\n * @file cc1101_ext.h\n * @brief External CC1101 transceiver access API.\n */\n\n#pragma once\n#include <lib/subghz/devices/preset.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <toolbox/level_duration.h>\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Mirror RX/TX async modulation signal to specified pin\n *\n * @warning    Configures pin to output mode. Make sure it is not connected\n *             directly to power or ground.\n *\n * @param[in]  pin   pointer to the gpio pin structure or NULL to disable\n */\nvoid subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin);\n\n/** Get data GPIO\n *\n * @return     pointer to the gpio pin structure\n */\nconst GpioPin* subghz_device_cc1101_ext_get_data_gpio(void);\n\n/** Initialize device\n *\n * @return     true if success\n */\nbool subghz_device_cc1101_ext_alloc(void);\n\n/** Deinitialize device\n */\nvoid subghz_device_cc1101_ext_free(void);\n\n/** Check and switch to power save mode Used by internal API-HAL\n * initialization routine Can be used to reinitialize device to safe state and\n * send it to sleep\n */\nbool subghz_device_cc1101_ext_is_connect(void);\n\n/** Send device to sleep mode\n */\nvoid subghz_device_cc1101_ext_sleep(void);\n\n/** Dump info to stdout\n */\nvoid subghz_device_cc1101_ext_dump_state(void);\n\n/** Load custom registers from preset\n *\n * @param      preset_data   registers to load\n */\nvoid subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data);\n\n/** Load registers\n *\n * @param      data  Registers data\n */\nvoid subghz_device_cc1101_ext_load_registers(const uint8_t* data);\n\n/** Load PATABLE\n *\n * @param      data  8 uint8_t values\n */\nvoid subghz_device_cc1101_ext_load_patable(const uint8_t data[8]);\n\n/** Write packet to FIFO\n *\n * @param      data  bytes array\n * @param      size  size\n */\nvoid subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size);\n\n/** Check if receive pipe is not empty\n *\n * @return     true if not empty\n */\nbool subghz_device_cc1101_ext_rx_pipe_not_empty(void);\n\n/** Check if received data crc is valid\n *\n * @return     true if valid\n */\nbool subghz_device_cc1101_ext_is_rx_data_crc_valid(void);\n\n/** Read packet from FIFO\n *\n * @param      data  pointer\n * @param      size  size\n */\nvoid subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size);\n\n/** Flush rx FIFO buffer\n */\nvoid subghz_device_cc1101_ext_flush_rx(void);\n\n/** Flush tx FIFO buffer\n */\nvoid subghz_device_cc1101_ext_flush_tx(void);\n\n/** Shutdown Issue SPWD command\n * @warning    registers content will be lost\n */\nvoid subghz_device_cc1101_ext_shutdown(void);\n\n/** Reset Issue reset command\n * @warning    registers content will be lost\n */\nvoid subghz_device_cc1101_ext_reset(void);\n\n/** Switch to Idle\n */\nvoid subghz_device_cc1101_ext_idle(void);\n\n/** Switch to Receive\n */\nvoid subghz_device_cc1101_ext_rx(void);\n\n/** Switch to Transmit\n *\n * @return     true if the transfer is allowed by belonging to the region\n */\nbool subghz_device_cc1101_ext_tx(void);\n\n/** Get RSSI value in dBm\n *\n * @return     RSSI value\n */\nfloat subghz_device_cc1101_ext_get_rssi(void);\n\n/** Get LQI\n *\n * @return     LQI value\n */\nuint8_t subghz_device_cc1101_ext_get_lqi(void);\n\n/** Check if frequency is in valid range\n *\n * @param      value  frequency in Hz\n *\n * @return     true if frequency is valid, otherwise false\n */\nbool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value);\n\n/** Set frequency\n *\n * @param      value  frequency in Hz\n *\n * @return     real frequency in Hz\n */\nuint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value);\n\n/* High Level API */\n\n/** Signal Timings Capture callback */\ntypedef void (*SubGhzDeviceCC1101ExtCaptureCallback)(bool level, uint32_t duration, void* context);\n\n/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture\n *\n * @param      callback  SubGhzDeviceCC1101ExtCaptureCallback\n * @param      context   callback context\n */\nvoid subghz_device_cc1101_ext_start_async_rx(\n    SubGhzDeviceCC1101ExtCaptureCallback callback,\n    void* context);\n\n/** Disable signal timings capture Resets GPIO and TIM2\n */\nvoid subghz_device_cc1101_ext_stop_async_rx(void);\n\n/** Async TX callback type\n * @param      context  callback context\n * @return     LevelDuration\n */\ntypedef LevelDuration (*SubGhzDeviceCC1101ExtCallback)(void* context);\n\n/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output\n *\n * @param      callback  SubGhzDeviceCC1101ExtCallback\n * @param      context   callback context\n *\n * @return     true if the transfer is allowed by belonging to the region\n */\nbool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context);\n\n/** Wait for async transmission to complete\n *\n * @return     true if TX complete\n */\nbool subghz_device_cc1101_ext_is_async_tx_complete(void);\n\n/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1\n */\nvoid subghz_device_cc1101_ext_stop_async_tx(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c",
    "content": "#include \"cc1101_ext_interconnect.h\"\n#include \"cc1101_ext.h\"\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#define TAG \"SubGhzDeviceCc1101Ext\"\n\nstatic bool subghz_device_cc1101_ext_interconnect_is_frequency_valid(uint32_t frequency) {\n    bool ret = subghz_device_cc1101_ext_is_frequency_valid(frequency);\n    if(!ret) {\n        furi_crash(\"SubGhz: Incorrect frequency.\");\n    }\n    return ret;\n}\n\nstatic uint32_t subghz_device_cc1101_ext_interconnect_set_frequency(uint32_t frequency) {\n    subghz_device_cc1101_ext_interconnect_is_frequency_valid(frequency);\n    return subghz_device_cc1101_ext_set_frequency(frequency);\n}\n\nstatic bool subghz_device_cc1101_ext_interconnect_start_async_tx(void* callback, void* context) {\n    return subghz_device_cc1101_ext_start_async_tx(\n        (SubGhzDeviceCC1101ExtCallback)callback, context);\n}\n\nstatic void subghz_device_cc1101_ext_interconnect_start_async_rx(void* callback, void* context) {\n    subghz_device_cc1101_ext_start_async_rx(\n        (SubGhzDeviceCC1101ExtCaptureCallback)callback, context);\n}\n\nstatic void subghz_device_cc1101_ext_interconnect_load_preset(\n    FuriHalSubGhzPreset preset,\n    uint8_t* preset_data) {\n    switch(preset) {\n    case FuriHalSubGhzPresetOok650Async:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_ook_650khz_async_regs);\n        break;\n    case FuriHalSubGhzPresetOok270Async:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_ook_270khz_async_regs);\n        break;\n    case FuriHalSubGhzPreset2FSKDev238Async:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);\n        break;\n    case FuriHalSubGhzPreset2FSKDev476Async:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);\n        break;\n    case FuriHalSubGhzPresetMSK99_97KbAsync:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_msk_99_97kb_async_regs);\n        break;\n    case FuriHalSubGhzPresetGFSK9_99KbAsync:\n        subghz_device_cc1101_ext_load_custom_preset(\n            subghz_device_cc1101_preset_gfsk_9_99kb_async_regs);\n        break;\n\n    default:\n        subghz_device_cc1101_ext_load_custom_preset(preset_data);\n    }\n}\n\nconst SubGhzDeviceInterconnect subghz_device_cc1101_ext_interconnect = {\n    .begin = subghz_device_cc1101_ext_alloc,\n    .end = subghz_device_cc1101_ext_free,\n    .is_connect = subghz_device_cc1101_ext_is_connect,\n    .reset = subghz_device_cc1101_ext_reset,\n    .sleep = subghz_device_cc1101_ext_sleep,\n    .idle = subghz_device_cc1101_ext_idle,\n    .load_preset = subghz_device_cc1101_ext_interconnect_load_preset,\n    .set_frequency = subghz_device_cc1101_ext_interconnect_set_frequency,\n    .is_frequency_valid = subghz_device_cc1101_ext_is_frequency_valid,\n    .set_async_mirror_pin = subghz_device_cc1101_ext_set_async_mirror_pin,\n    .get_data_gpio = subghz_device_cc1101_ext_get_data_gpio,\n\n    .set_tx = subghz_device_cc1101_ext_tx,\n    .flush_tx = subghz_device_cc1101_ext_flush_tx,\n    .start_async_tx = subghz_device_cc1101_ext_interconnect_start_async_tx,\n    .is_async_complete_tx = subghz_device_cc1101_ext_is_async_tx_complete,\n    .stop_async_tx = subghz_device_cc1101_ext_stop_async_tx,\n\n    .set_rx = subghz_device_cc1101_ext_rx,\n    .flush_rx = subghz_device_cc1101_ext_flush_rx,\n    .start_async_rx = subghz_device_cc1101_ext_interconnect_start_async_rx,\n    .stop_async_rx = subghz_device_cc1101_ext_stop_async_rx,\n\n    .get_rssi = subghz_device_cc1101_ext_get_rssi,\n    .get_lqi = subghz_device_cc1101_ext_get_lqi,\n\n    .rx_pipe_not_empty = subghz_device_cc1101_ext_rx_pipe_not_empty,\n    .is_rx_data_crc_valid = subghz_device_cc1101_ext_is_rx_data_crc_valid,\n    .read_packet = subghz_device_cc1101_ext_read_packet,\n    .write_packet = subghz_device_cc1101_ext_write_packet,\n};\n\nconst SubGhzDevice subghz_device_cc1101_ext = {\n    .name = SUBGHZ_DEVICE_CC1101_EXT_NAME,\n    .interconnect = &subghz_device_cc1101_ext_interconnect,\n};\n\nstatic const FlipperAppPluginDescriptor subghz_device_cc1101_ext_descriptor = {\n    .appid = SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID,\n    .ep_api_version = SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION,\n    .entry_point = &subghz_device_cc1101_ext,\n};\n\nconst FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep(void) {\n    return &subghz_device_cc1101_ext_descriptor;\n}\n"
  },
  {
    "path": "applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h",
    "content": "#pragma once\n#include <lib/subghz/devices/types.h>\n\n#define SUBGHZ_DEVICE_CC1101_EXT_NAME \"cc1101_ext\"\n\ntypedef struct SubGhzDeviceCC1101Ext SubGhzDeviceCC1101Ext;\n\nconst FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep(void);\n"
  },
  {
    "path": "applications/examples/application.fam",
    "content": "# Placeholder\nApp(\n    appid=\"example_apps\",\n    name=\"Example apps bundle\",\n    apptype=FlipperAppType.METAPACKAGE,\n)\n"
  },
  {
    "path": "applications/examples/example_adc/application.fam",
    "content": "App(\n    appid=\"example_adc\",\n    name=\"Example: ADC\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_adc_main\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_adc/example_adc.c",
    "content": "/**\n * @file example_adc.c\n * @brief ADC example.\n */\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/elements.h>\n#include <input/input.h>\n\nconst uint8_t font[] =\n    \"`\\2\\3\\2\\3\\4\\1\\2\\4\\5\\11\\0\\376\\6\\376\\7\\377\\1M\\2\\263\\3\\370 \\6\\315\\364\\371\\6!\\12\\315\"\n    \"\\364\\201\\260\\35\\312Q\\0\\42\\11\\315tJI\\316\\13\\0#\\14\\315\\264\\223dP*\\203R'\\1$\\15\\315\\264\"\n    \"\\262A\\311\\266D\\251l\\71\\0%\\15\\315\\264\\7%\\61)J\\42\\345 \\0&\\14\\315\\264\\263$\\13\\223\\266$\"\n    \"\\7\\1'\\10\\315\\364\\201\\60\\347\\10(\\10\\315\\364\\32[\\313\\0)\\11\\315\\64\\322b[\\35\\2*\\12\\315\\264\\263\"\n    \"(\\222j\\71\\15+\\11\\315\\364I\\331\\226\\23\\1,\\10\\315\\364\\271\\205Y\\10-\\10\\315\\364\\31t\\26\\0.\\10\"\n    \"\\315\\364\\71\\346(\\0/\\14\\315\\364\\221\\60\\13\\263\\60\\13C\\0\\60\\13\\315\\264\\245Jb)E:\\12\\61\\12\\315\"\n    \"\\364\\201Ll\\333A\\0\\62\\12\\315\\264\\245bV\\33r\\20\\63\\13\\315\\264\\245Z\\232D\\221\\216\\2\\64\\14\\315\\364\"\n    \"\\201LJ\\242!\\313v\\20\\65\\14\\315t\\207$\\134\\223(\\322Q\\0\\66\\13\\315\\264\\245p\\252D\\221\\216\\2\\67\"\n    \"\\12\\315t\\207\\60+\\326a\\0\\70\\13\\315\\264\\245\\222T\\211\\42\\35\\5\\71\\13\\315\\264\\245J\\24\\215\\221\\216\\2:\"\n    \"\\11\\315\\364i\\71!G\\1;\\12\\315\\364I\\71!\\314B\\0<\\11\\315\\364\\341\\254Z\\7\\1=\\12\\315\\364)\"\n    \"C<\\344$\\0>\\11\\315\\364\\301\\264V\\207\\1\\77\\12\\315\\264\\245Z\\35\\312a\\0@\\14\\315\\264\\245J\\242$\"\n    \"J\\272\\203\\0A\\15\\315\\264\\245J\\224\\14I\\224D\\71\\10B\\13\\315t\\247\\312T\\211\\222\\35\\5C\\12\\315\\264\"\n    \"\\245JX\\212t\\24D\\15\\315t\\247J\\224DI\\224\\354(\\0E\\14\\315t\\207$\\234\\302p\\310A\\0F\"\n    \"\\12\\315t\\207$\\234\\302:\\1G\\14\\315\\264\\245J\\230(Q\\244\\243\\0H\\17\\315t\\243$J\\206$J\\242\"\n    \"$\\312A\\0I\\11\\315\\264\\267\\260m\\7\\1J\\12\\315\\364\\221\\260%\\212t\\24K\\14\\315t\\243\\244\\244iI\"\n    \"T\\7\\1L\\11\\315t\\303\\216C\\16\\2M\\17\\315t\\243dH\\206$J\\242$\\312A\\0N\\16\\315t\\243\"\n    \"D\\251(Q\\22%Q\\16\\2O\\15\\315\\264\\245J\\224DI\\24\\351(\\0P\\12\\315t\\247J\\224LaN\"\n    \"Q\\15\\315\\264\\245J\\224DI\\42\\251\\61\\0R\\14\\315t\\247J\\224L\\225(\\7\\1S\\13\\315\\264\\245\\222\\232\"\n    \"D\\221\\216\\2T\\10\\315\\264\\267\\260;\\12U\\16\\315t\\243$J\\242$J\\242HG\\1V\\15\\315t\\243$\"\n    \"J\\242$Jj\\71\\14W\\17\\315t\\243$J\\242dH\\206$\\312A\\0X\\15\\315t\\243$\\212\\64\\251\\22\"\n    \"\\345 \\0Y\\13\\315t\\243$Jja\\35\\6Z\\12\\315t\\207\\60k\\34r\\20[\\10\\315\\264\\264\\260G\\31\"\n    \"\\134\\12\\315\\264\\303\\64L\\303\\64\\14]\\10\\315t\\304\\276\\351\\0^\\11\\315\\364\\201,\\311\\271\\1_\\7\\315\\364y\"\n    \"\\35\\4`\\10\\315t\\322\\234'\\0a\\14\\315\\364IK\\224$R\\222\\203\\0b\\13\\315t\\303p\\252D\\311\\216\"\n    \"\\2c\\12\\315\\364IR%\\335A\\0d\\14\\315\\364\\221\\60Z\\242$\\212v\\20e\\12\\315\\364I\\322\\220\\244;\"\n    \"\\10f\\12\\315\\364\\221,\\333\\302:\\12g\\14\\315\\364IK\\224D\\321\\30I\\0h\\14\\315t\\303p\\252DI\"\n    \"\\224\\203\\0i\\12\\315\\364\\201\\34\\21k;\\10j\\12\\315\\364\\201\\34\\21\\273e\\0k\\13\\315t\\303J\\244%Q\"\n    \"\\35\\4l\\10\\315\\264\\305n;\\10m\\14\\315\\364)CRQ\\22\\245\\216\\1n\\13\\315\\364)%\\245\\224D\\71\"\n    \"\\10o\\12\\315\\364IR%\\212t\\24p\\13\\315\\364)S%J\\246\\60\\4q\\13\\315\\364IK\\224D\\321X\"\n    \"\\1r\\11\\315\\364)%\\245\\230\\23s\\12\\315\\364I\\313\\232\\354(\\0t\\13\\315\\364\\201\\60\\333\\302\\64\\7\\1u\"\n    \"\\15\\315\\364)Q\\22%\\211\\224\\344 \\0v\\13\\315\\364)Q\\22%\\265\\34\\6w\\13\\315\\364)\\25%Q\\272\"\n    \"\\203\\0x\\12\\315\\364)Q\\244Iu\\20y\\15\\315\\364)Q\\22%Q\\64F\\22\\0z\\12\\315\\364)CV\"\n    \"\\33r\\20{\\12\\315\\364\\212\\265\\64\\254&\\0|\\7\\315\\264\\302~\\7}\\12\\315t\\322\\260\\232\\205\\265\\14~\\11\"\n    \"\\315\\364II;\\13\\0\\177\\6\\315\\364\\371\\6\\0\\0\\0\\4\\377\\377\\0\";\n\n#define FONT_HEIGHT (8u)\n\ntypedef float (*ValueConverter)(FuriHalAdcHandle* handle, uint16_t value);\n\ntypedef struct {\n    const GpioPinRecord* pin;\n    float value;\n    ValueConverter converter;\n    const char* suffix;\n} DataItem;\n\ntypedef struct {\n    size_t count;\n    DataItem* items;\n} Data;\n\nconst GpioPinRecord item_vref = {.name = \"VREF\", .channel = FuriHalAdcChannelVREFINT};\nconst GpioPinRecord item_temp = {.name = \"TEMP\", .channel = FuriHalAdcChannelTEMPSENSOR};\nconst GpioPinRecord item_vbat = {.name = \"VBAT\", .channel = FuriHalAdcChannelVBAT};\n\nstatic void app_draw_callback(Canvas* canvas, void* ctx) {\n    furi_assert(ctx);\n    Data* data = ctx;\n\n    canvas_set_custom_u8g2_font(canvas, font);\n    char buffer[64];\n    int32_t x = 0, y = FONT_HEIGHT;\n    for(size_t i = 0; i < data->count; i++) {\n        if(i == canvas_height(canvas) / FONT_HEIGHT) {\n            x = 64;\n            y = FONT_HEIGHT;\n        }\n\n        snprintf(\n            buffer,\n            sizeof(buffer),\n            \"%4s: %4.0f%s\\n\",\n            data->items[i].pin->name,\n            (double)data->items[i].value,\n            data->items[i].suffix);\n        canvas_draw_str(canvas, x, y, buffer);\n        y += FONT_HEIGHT;\n    }\n}\n\nstatic void app_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t example_adc_main(void* p) {\n    UNUSED(p);\n\n    // Data\n    Data data = {};\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(gpio_pins[i].channel != FuriHalAdcChannelNone) {\n            data.count++;\n        }\n    }\n    data.count += 3; // Special channels\n    data.items = malloc(data.count * sizeof(DataItem));\n    size_t item_pos = 0;\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(gpio_pins[i].channel != FuriHalAdcChannelNone) {\n            furi_hal_gpio_init(gpio_pins[i].pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n            data.items[item_pos].pin = &gpio_pins[i];\n            data.items[item_pos].converter = furi_hal_adc_convert_to_voltage;\n            data.items[item_pos].suffix = \"mV\";\n            item_pos++;\n        }\n    }\n    data.items[item_pos].pin = &item_vref;\n    data.items[item_pos].converter = furi_hal_adc_convert_vref;\n    data.items[item_pos].suffix = \"mV\";\n    item_pos++;\n    data.items[item_pos].pin = &item_temp;\n    data.items[item_pos].converter = furi_hal_adc_convert_temp;\n    data.items[item_pos].suffix = \"C\";\n    item_pos++;\n    data.items[item_pos].pin = &item_vbat;\n    data.items[item_pos].converter = furi_hal_adc_convert_vbat;\n    data.items[item_pos].suffix = \"mV\";\n    item_pos++;\n    furi_assert(item_pos == data.count);\n\n    // Alloc message queue\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, app_draw_callback, &data);\n    view_port_input_callback_set(view_port, app_input_callback, event_queue);\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    // Initialize ADC\n    FuriHalAdcHandle* adc_handle = furi_hal_adc_acquire();\n    furi_hal_adc_configure(adc_handle);\n\n    // Process events\n    InputEvent event;\n    bool running = true;\n    while(running) {\n        if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {\n            if(event.type == InputTypePress && event.key == InputKeyBack) {\n                running = false;\n            }\n        } else {\n            for(size_t i = 0; i < data.count; i++) {\n                data.items[i].value = data.items[i].converter(\n                    adc_handle, furi_hal_adc_read(adc_handle, data.items[i].pin->channel));\n            }\n            view_port_update(view_port);\n        }\n    }\n\n    furi_hal_adc_release(adc_handle);\n    view_port_enabled_set(view_port, false);\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n    furi_record_close(RECORD_GUI);\n    free(data.items);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_apps_assets/README.md",
    "content": "# Apps Assets folder Example {#example_app_assets}\n\nThis example shows how to use the Apps Assets folder to store data that is not part of the application itself, but is required for its operation, and that data is provided with the application.\n\n## Source code\n\nSource code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_assets).\n\n## What is the Apps Assets Folder?\n\nThe **Apps Assets** folder is a folder where external applications unpack their assets.\n\nThe path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file. \nThe Apps Assets folder is located only on the external storage, the SD card.\n\nFor example, if the `appid` of the app is `snake_game`, the path to the Apps Assets folder will be `/ext/apps_assets/snake_game`. But using raw paths is not recommended, because the path to the Apps Assets folder can change in the future. Use the `/assets` alias instead.\n\n## How to get the path to the Apps Assets folder?\n\nYou can use `/assets` alias to get the path to the current application data folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `/data/database.txt`. But this way is not recommended, because even the `/assets` alias can change in the future.\n\nWe recommend to use the `APP_ASSETS_PATH` macro to get the path to the Apps Assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `APP_ASSETS_PATH(\"database.txt\")`.\n\n## What is the difference between the Apps Assets folder and the Apps Data folder?\n\nThe Apps Assets folder is used to store the data <u>provided</u> with the application. For example, if you want to create a game, you can store game levels (content data) in the Apps Assets folder.\n\nThe Apps Data folder is used to store data <u>generated</u> by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder.\n\n## How to provide the data with the app?\n\nTo provide data with an application, you need to create a folder inside your application folder (eg \"files\") and place the data in it. After that, you need to add `fap_file_assets=\"files\"` to your application.fam file.\n\nFor example, if you want to provide game levels with the application, you need to create a \"levels\" folder inside the \"files\" folder and put the game levels in it. After that, you need to add `fap_file_assets=\"files\"` to your application.fam file. The final application folder structure will look like this:\n\n```\nsnake_game\n├── application.fam\n├── snake_game.c\n└── files\n    └── levels\n        ├── level1.txt\n        ├── level2.txt\n        └── level3.txt\n```\n\nWhen app is launched, the `files` folder will be unpacked to the Apps Assets folder. The final structure of the Apps Assets folder will look like this:\n\n```\n/assets\n├── .assets.signature\n└── levels\n    ├── level1.txt\n    ├── level2.txt\n    └── level3.txt\n```\n\n## When will the data be unpacked?\n\nThe data is unpacked when the application starts, if the application is launched for the first time, or if the data within the application is updated.\n\nWhen an application is compiled, the contents of the \"files\" folder are hashed and stored within the application itself. When the application starts, this hash is compared to the hash stored in the `.assets.signature` file. If the hashes differ or the `.assets.signature` file does not exist, the application folder is deleted and the new data is unpacked.\n"
  },
  {
    "path": "applications/examples/example_apps_assets/application.fam",
    "content": "App(\n    appid=\"example_apps_assets\",\n    name=\"Example: Apps Assets\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_apps_assets_main\",\n    requires=[\"gui\"],\n    stack_size=4 * 1024,\n    fap_category=\"Examples\",\n    fap_file_assets=\"files\",\n)\n"
  },
  {
    "path": "applications/examples/example_apps_assets/example_apps_assets.c",
    "content": "/**\n * @file example_apps_assets.c\n * @brief Application assets example.\n */\n#include <furi.h>\n#include <storage/storage.h>\n#include <toolbox/stream/stream.h>\n#include <toolbox/stream/file_stream.h>\n\n// Define log tag\n#define TAG \"ExampleAppsAssets\"\n\nstatic void example_apps_data_print_file_content(Storage* storage, const char* path) {\n    Stream* stream = file_stream_alloc(storage);\n    FuriString* line = furi_string_alloc();\n\n    FURI_LOG_I(TAG, \"----------------------------------------\");\n    FURI_LOG_I(TAG, \"File \\\"%s\\\" content:\", path);\n    if(file_stream_open(stream, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        while(stream_read_line(stream, line)) {\n            furi_string_replace_all(line, \"\\r\", \"\");\n            furi_string_replace_all(line, \"\\n\", \"\");\n            FURI_LOG_I(TAG, \"%s\", furi_string_get_cstr(line));\n        }\n    } else {\n        FURI_LOG_E(TAG, \"Failed to open file\");\n    }\n    FURI_LOG_I(TAG, \"----------------------------------------\");\n\n    furi_string_free(line);\n    file_stream_close(stream);\n    stream_free(stream);\n}\n\n// Application entry point\nint32_t example_apps_assets_main(void* p) {\n    // Mark argument as unused\n    UNUSED(p);\n\n    // Open storage\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    example_apps_data_print_file_content(storage, APP_ASSETS_PATH(\"test_asset.txt\"));\n    example_apps_data_print_file_content(storage, APP_ASSETS_PATH(\"poems/a jelly-fish.txt\"));\n    example_apps_data_print_file_content(storage, APP_ASSETS_PATH(\"poems/theme in yellow.txt\"));\n    example_apps_data_print_file_content(storage, APP_ASSETS_PATH(\"poems/my shadow.txt\"));\n\n    // Close storage\n    furi_record_close(RECORD_STORAGE);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_apps_assets/files/poems/a jelly-fish.txt",
    "content": "A Jelly-Fish by Marianne Moore\n\nVisible, invisible,\nA fluctuating charm,\nAn amber-colored amethyst\nInhabits it; your arm\nApproaches, and\nIt opens and\nIt closes;\nYou have meant\nTo catch it,\nAnd it shrivels;\nYou abandon\nYour intent—\nIt opens, and it\nCloses and you\nReach for it—\nThe blue\nSurrounding it\nGrows cloudy, and\nIt floats away\nFrom you.\n\nsource: \"https://poets.org/anthology/poems-your-poetry-project-public-domain\""
  },
  {
    "path": "applications/examples/example_apps_assets/files/poems/my shadow.txt",
    "content": "My Shadow by Robert Louis Stevenson\n\nI have a little shadow that goes in and out with me,\nAnd what can be the use of him is more than I can see.\nHe is very, very like me from the heels up to the head;\nAnd I see him jump before me, when I jump into my bed.\n\nThe funniest thing about him is the way he likes to grow—\nNot at all like proper children, which is always very slow;\nFor he sometimes shoots up taller like an India-rubber ball,\nAnd he sometimes gets so little that there’s none of him at all.\n\nHe hasn’t got a notion of how children ought to play,\nAnd can only make a fool of me in every sort of way.\nHe stays so close beside me, he’s a coward you can see;\nI’d think shame to stick to nursie as that shadow sticks to me!\n\nOne morning, very early, before the sun was up,\nI rose and found the shining dew on every buttercup;\nBut my lazy little shadow, like an arrant sleepy-head,\nHad stayed at home behind me and was fast asleep in bed.\n\nsource: \"https://poets.org/anthology/poems-your-poetry-project-public-domain\""
  },
  {
    "path": "applications/examples/example_apps_assets/files/poems/theme in yellow.txt",
    "content": "Theme in Yellow by Carl Sandburg\n\nI spot the hills \nWith yellow balls in autumn. \nI light the prairie cornfields \nOrange and tawny gold clusters \nAnd I am called pumpkins. \nOn the last of October \nWhen dusk is fallen \nChildren join hands \nAnd circle round me \nSinging ghost songs \nAnd love to the harvest moon; \nI am a jack-o'-lantern \nWith terrible teeth \nAnd the children know \nI am fooling.\n\nsource: \"https://poets.org/anthology/poems-your-poetry-project-public-domain\""
  },
  {
    "path": "applications/examples/example_apps_assets/files/test_asset.txt",
    "content": "## This is test file content"
  },
  {
    "path": "applications/examples/example_apps_data/README.md",
    "content": "# Apps Data folder Example {#example_app_data}\n\nThis example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth.\n\n## Source code\n\nSource code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_data).\n\n## What is the Apps Data Folder?\n\nThe **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware. \n\nThe path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file. \nThe Apps Data folder is located only on the external storage, the SD card.\n\nFor example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/data` alias instead.\n\n## How to get the path to the Apps Data folder?\n\nYou can use `/data` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/data/config.txt`. But this way is not recommended, because even the `/data` alias can change in the future.\n\nWe recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH(\"config.txt\")`.\n\n## What is the difference between the Apps Assets folder and the Apps Data folder?\n\nThe Apps Assets folder is used to store the data <u>provided</u> with the application. For example, if you want to create a game, you can store game levels (content data) in the Apps Assets folder.\n\nThe Apps Data folder is used to store data <u>generated</u> by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder.\n"
  },
  {
    "path": "applications/examples/example_apps_data/application.fam",
    "content": "App(\n    appid=\"example_apps_data\",\n    name=\"Example: Apps Data\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_apps_data_main\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_apps_data/example_apps_data.c",
    "content": "/**\n * @file example_apps_data.c\n * @brief Application data example.\n */\n#include <furi.h>\n#include <storage/storage.h>\n\n// Define log tag\n#define TAG \"ExampleAppsData\"\n\n// Application entry point\nint32_t example_apps_data_main(void* p) {\n    // Mark argument as unused\n    UNUSED(p);\n\n    // Open storage\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    // Allocate file\n    File* file = storage_file_alloc(storage);\n\n    // Get the path to the current application data folder\n    // That is: /ext/apps_data/<app_name>\n    // And it will create folders in the path if they don't exist\n    // In this example it will create /ext/apps_data/example_apps_data\n    // And file will be /ext/apps_data/example_apps_data/test.txt\n\n    // Open file, write data and close it\n    if(!storage_file_open(file, APP_DATA_PATH(\"test.txt\"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n        FURI_LOG_E(TAG, \"Failed to open file\");\n    }\n    if(!storage_file_write(file, \"Hello World!\", strlen(\"Hello World!\"))) {\n        FURI_LOG_E(TAG, \"Failed to write to file\");\n    }\n    storage_file_close(file);\n\n    // Deallocate file\n    storage_file_free(file);\n\n    // Close storage\n    furi_record_close(RECORD_STORAGE);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/application.fam",
    "content": "App(\n    appid=\"example_ble_beacon\",\n    name=\"Example: BLE Beacon\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"ble_beacon_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_icon=\"example_ble_beacon_10px.png\",\n    fap_category=\"Examples\",\n    fap_icon_assets=\"images\",\n)\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/ble_beacon_app.c",
    "content": "#include \"ble_beacon_app.h\"\n\n#include <extra_beacon.h>\n#include <furi_hal_version.h>\n\n#include <string.h>\n\n#define TAG \"BleBeaconApp\"\n\nstatic bool ble_beacon_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    BleBeaconApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool ble_beacon_app_back_event_callback(void* context) {\n    furi_assert(context);\n    BleBeaconApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void ble_beacon_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    BleBeaconApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nstatic void ble_beacon_app_restore_beacon_state(BleBeaconApp* app) {\n    // Restore beacon data from service\n    GapExtraBeaconConfig* local_config = &app->beacon_config;\n    const GapExtraBeaconConfig* config = furi_hal_bt_extra_beacon_get_config();\n    if(config) {\n        // We have a config, copy it\n        memcpy(local_config, config, sizeof(app->beacon_config));\n    } else {\n        // No config, set up default values - they will stay until overriden or device is reset\n        local_config->min_adv_interval_ms = 50;\n        local_config->max_adv_interval_ms = 150;\n\n        local_config->adv_channel_map = GapAdvChannelMapAll;\n        local_config->adv_power_level = GapAdvPowerLevel_0dBm;\n\n        local_config->address_type = GapAddressTypePublic;\n        memcpy(\n            local_config->address, furi_hal_version_get_ble_mac(), sizeof(local_config->address));\n        // Modify MAC address to make it different from the one used by the main app\n        local_config->address[0] ^= 0xFF;\n        local_config->address[3] ^= 0xFF;\n\n        furi_check(furi_hal_bt_extra_beacon_set_config(local_config));\n    }\n\n    // Get beacon state\n    app->is_beacon_active = furi_hal_bt_extra_beacon_is_active();\n\n    // Restore last beacon data\n    app->beacon_data_len = furi_hal_bt_extra_beacon_get_data(app->beacon_data);\n}\n\nstatic BleBeaconApp* ble_beacon_app_alloc(void) {\n    BleBeaconApp* app = malloc(sizeof(BleBeaconApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n\n    app->scene_manager = scene_manager_alloc(&ble_beacon_app_scene_handlers, app);\n    app->view_dispatcher = view_dispatcher_alloc();\n\n    app->status_string = furi_string_alloc();\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, ble_beacon_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, ble_beacon_app_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, ble_beacon_app_tick_event_callback, 100);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BleBeaconAppViewSubmenu, submenu_get_view(app->submenu));\n\n    app->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BleBeaconAppViewDialog, dialog_ex_get_view(app->dialog_ex));\n\n    app->byte_input = byte_input_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BleBeaconAppViewByteInput, byte_input_get_view(app->byte_input));\n\n    ble_beacon_app_restore_beacon_state(app);\n\n    return app;\n}\n\nstatic void ble_beacon_app_free(BleBeaconApp* app) {\n    view_dispatcher_remove_view(app->view_dispatcher, BleBeaconAppViewByteInput);\n    view_dispatcher_remove_view(app->view_dispatcher, BleBeaconAppViewSubmenu);\n    view_dispatcher_remove_view(app->view_dispatcher, BleBeaconAppViewDialog);\n\n    free(app->byte_input);\n    free(app->submenu);\n    free(app->dialog_ex);\n\n    free(app->scene_manager);\n    free(app->view_dispatcher);\n\n    free(app->status_string);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    free(app);\n}\n\nint32_t ble_beacon_app(void* args) {\n    UNUSED(args);\n\n    BleBeaconApp* app = ble_beacon_app_alloc();\n\n    scene_manager_next_scene(app->scene_manager, BleBeaconAppSceneRunBeacon);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    ble_beacon_app_free(app);\n    return 0;\n}\n\nvoid ble_beacon_app_update_state(BleBeaconApp* app) {\n    furi_hal_bt_extra_beacon_stop();\n\n    furi_check(furi_hal_bt_extra_beacon_set_config(&app->beacon_config));\n\n    app->beacon_data_len = 0;\n    while((app->beacon_data[app->beacon_data_len] != 0) &&\n          (app->beacon_data_len < sizeof(app->beacon_data))) {\n        app->beacon_data_len++;\n    }\n\n    FURI_LOG_I(TAG, \"beacon_data_len: %d\", app->beacon_data_len);\n\n    furi_check(furi_hal_bt_extra_beacon_set_data(app->beacon_data, app->beacon_data_len));\n\n    if(app->is_beacon_active) {\n        furi_check(furi_hal_bt_extra_beacon_start());\n    }\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/ble_beacon_app.h",
    "content": "/**\n * @file ble_beacon_app.h\n * @brief BLE beacon example.\n */\n#pragma once\n\n#include \"extra_beacon.h\"\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n\n#include <gui/modules/widget.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/byte_input.h>\n#include <gui/modules/dialog_ex.h>\n\n#include <rpc/rpc_app.h>\n#include <notification/notification_messages.h>\n\n#include <furi_hal_bt.h>\n\n#include \"scenes/scenes.h\"\n#include <stdint.h>\n\ntypedef struct {\n    Gui* gui;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    Submenu* submenu;\n    ByteInput* byte_input;\n    DialogEx* dialog_ex;\n\n    FuriString* status_string;\n\n    GapExtraBeaconConfig beacon_config;\n    uint8_t beacon_data[EXTRA_BEACON_MAX_DATA_SIZE];\n    uint8_t beacon_data_len;\n    bool is_beacon_active;\n} BleBeaconApp;\n\ntypedef enum {\n    BleBeaconAppViewSubmenu,\n    BleBeaconAppViewByteInput,\n    BleBeaconAppViewDialog,\n} BleBeaconAppView;\n\ntypedef enum {\n    BleBeaconAppCustomEventDataEditResult = 100,\n} BleBeaconAppCustomEvent;\n\nvoid ble_beacon_app_update_state(BleBeaconApp* app);\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scene_config.h",
    "content": "ADD_SCENE(ble_beacon_app, menu, Menu)\nADD_SCENE(ble_beacon_app, input_mac_addr, InputMacAddress)\nADD_SCENE(ble_beacon_app, input_beacon_data, InputBeaconData)\nADD_SCENE(ble_beacon_app, run_beacon, RunBeacon)\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scene_input_beacon_data.c",
    "content": "#include \"../ble_beacon_app.h\"\n\nstatic void ble_beacon_app_scene_add_type_byte_input_callback(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    view_dispatcher_send_custom_event(\n        ble_beacon->view_dispatcher, BleBeaconAppCustomEventDataEditResult);\n}\n\nvoid ble_beacon_app_scene_input_beacon_data_on_enter(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    byte_input_set_header_text(ble_beacon->byte_input, \"Enter beacon data\");\n\n    byte_input_set_result_callback(\n        ble_beacon->byte_input,\n        ble_beacon_app_scene_add_type_byte_input_callback,\n        NULL,\n        context,\n        ble_beacon->beacon_data,\n        sizeof(ble_beacon->beacon_data));\n\n    view_dispatcher_switch_to_view(ble_beacon->view_dispatcher, BleBeaconAppViewByteInput);\n}\n\nbool ble_beacon_app_scene_input_beacon_data_on_event(void* context, SceneManagerEvent event) {\n    BleBeaconApp* ble_beacon = context;\n    SceneManager* scene_manager = ble_beacon->scene_manager;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == BleBeaconAppCustomEventDataEditResult) {\n            ble_beacon_app_update_state(ble_beacon);\n            scene_manager_previous_scene(scene_manager);\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid ble_beacon_app_scene_input_beacon_data_on_exit(void* context) {\n    BleBeaconApp* ble_beacon = context;\n\n    byte_input_set_result_callback(ble_beacon->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(ble_beacon->byte_input, NULL);\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scene_input_mac_addr.c",
    "content": "#include \"../ble_beacon_app.h\"\n\nstatic void ble_beacon_app_scene_add_type_byte_input_callback(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    view_dispatcher_send_custom_event(\n        ble_beacon->view_dispatcher, BleBeaconAppCustomEventDataEditResult);\n}\n\nvoid ble_beacon_app_scene_input_mac_addr_on_enter(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    byte_input_set_header_text(ble_beacon->byte_input, \"Enter MAC (reversed)\");\n\n    byte_input_set_result_callback(\n        ble_beacon->byte_input,\n        ble_beacon_app_scene_add_type_byte_input_callback,\n        NULL,\n        context,\n        ble_beacon->beacon_config.address,\n        sizeof(ble_beacon->beacon_config.address));\n\n    view_dispatcher_switch_to_view(ble_beacon->view_dispatcher, BleBeaconAppViewByteInput);\n}\n\nbool ble_beacon_app_scene_input_mac_addr_on_event(void* context, SceneManagerEvent event) {\n    BleBeaconApp* ble_beacon = context;\n    SceneManager* scene_manager = ble_beacon->scene_manager;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == BleBeaconAppCustomEventDataEditResult) {\n            ble_beacon_app_update_state(ble_beacon);\n            scene_manager_previous_scene(scene_manager);\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid ble_beacon_app_scene_input_mac_addr_on_exit(void* context) {\n    BleBeaconApp* ble_beacon = context;\n\n    byte_input_set_result_callback(ble_beacon->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(ble_beacon->byte_input, NULL);\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scene_menu.c",
    "content": "#include \"../ble_beacon_app.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexSetMac,\n    SubmenuIndexSetData,\n};\n\nstatic void ble_beacon_app_scene_menu_submenu_callback(void* context, uint32_t index) {\n    BleBeaconApp* ble_beacon = context;\n    view_dispatcher_send_custom_event(ble_beacon->view_dispatcher, index);\n}\n\nvoid ble_beacon_app_scene_menu_on_enter(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    Submenu* submenu = ble_beacon->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Set MAC\",\n        SubmenuIndexSetMac,\n        ble_beacon_app_scene_menu_submenu_callback,\n        ble_beacon);\n    submenu_add_item(\n        submenu,\n        \"Set Data\",\n        SubmenuIndexSetData,\n        ble_beacon_app_scene_menu_submenu_callback,\n        ble_beacon);\n\n    view_dispatcher_switch_to_view(ble_beacon->view_dispatcher, BleBeaconAppViewSubmenu);\n}\n\nbool ble_beacon_app_scene_menu_on_event(void* context, SceneManagerEvent event) {\n    BleBeaconApp* ble_beacon = context;\n    SceneManager* scene_manager = ble_beacon->scene_manager;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        if(submenu_index == SubmenuIndexSetMac) {\n            scene_manager_next_scene(scene_manager, BleBeaconAppSceneInputMacAddress);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexSetData) {\n            scene_manager_next_scene(scene_manager, BleBeaconAppSceneInputBeaconData);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid ble_beacon_app_scene_menu_on_exit(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    submenu_reset(ble_beacon->submenu);\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scene_run_beacon.c",
    "content": "#include \"../ble_beacon_app.h\"\n#include <example_ble_beacon_icons.h>\n\nstatic void\n    ble_beacon_app_scene_run_beacon_confirm_dialog_callback(DialogExResult result, void* context) {\n    BleBeaconApp* ble_beacon = context;\n\n    view_dispatcher_send_custom_event(ble_beacon->view_dispatcher, result);\n}\n\nstatic void update_status_text(BleBeaconApp* ble_beacon) {\n    DialogEx* dialog_ex = ble_beacon->dialog_ex;\n\n    dialog_ex_set_header(dialog_ex, \"BLE Beacon Demo\", 64, 0, AlignCenter, AlignTop);\n\n    FuriString* status = ble_beacon->status_string;\n\n    furi_string_reset(status);\n\n    furi_string_cat_str(status, \"Status: \");\n    if(ble_beacon->is_beacon_active) {\n        furi_string_cat_str(status, \"Running\\n\");\n    } else {\n        furi_string_cat_str(status, \"Stopped\\n\");\n    }\n\n    // Output MAC in reverse order\n    for(int i = sizeof(ble_beacon->beacon_config.address) - 1; i >= 0; i--) {\n        furi_string_cat_printf(status, \"%02X\", ble_beacon->beacon_config.address[i]);\n        if(i > 0) {\n            furi_string_cat_str(status, \":\");\n        }\n    }\n\n    furi_string_cat_printf(status, \"\\nData length: %d\", ble_beacon->beacon_data_len);\n\n    dialog_ex_set_text(dialog_ex, furi_string_get_cstr(status), 0, 29, AlignLeft, AlignCenter);\n\n    dialog_ex_set_icon(dialog_ex, 93, 20, &I_lighthouse_35x44);\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Config\");\n\n    dialog_ex_set_center_button_text(dialog_ex, ble_beacon->is_beacon_active ? \"Stop\" : \"Start\");\n\n    dialog_ex_set_result_callback(\n        dialog_ex, ble_beacon_app_scene_run_beacon_confirm_dialog_callback);\n    dialog_ex_set_context(dialog_ex, ble_beacon);\n}\n\nvoid ble_beacon_app_scene_run_beacon_on_enter(void* context) {\n    BleBeaconApp* ble_beacon = context;\n\n    update_status_text(ble_beacon);\n\n    view_dispatcher_switch_to_view(ble_beacon->view_dispatcher, BleBeaconAppViewDialog);\n}\n\nbool ble_beacon_app_scene_run_beacon_on_event(void* context, SceneManagerEvent event) {\n    BleBeaconApp* ble_beacon = context;\n    SceneManager* scene_manager = ble_beacon->scene_manager;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            scene_manager_next_scene(scene_manager, BleBeaconAppSceneMenu);\n            return true;\n        } else if(event.event == DialogExResultCenter) {\n            ble_beacon->is_beacon_active = !ble_beacon->is_beacon_active;\n            ble_beacon_app_update_state(ble_beacon);\n            update_status_text(ble_beacon);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid ble_beacon_app_scene_run_beacon_on_exit(void* context) {\n    BleBeaconApp* ble_beacon = context;\n    UNUSED(ble_beacon);\n}\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scenes.c",
    "content": "#include \"scenes.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const ble_beacon_app_on_enter_handlers[])(void*) = {\n#include \"scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const ble_beacon_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const ble_beacon_app_on_exit_handlers[])(void* context) = {\n#include \"scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers ble_beacon_app_scene_handlers = {\n    .on_enter_handlers = ble_beacon_app_on_enter_handlers,\n    .on_event_handlers = ble_beacon_app_on_event_handlers,\n    .on_exit_handlers = ble_beacon_app_on_exit_handlers,\n    .scene_num = BleBeaconAppSceneNum,\n};\n"
  },
  {
    "path": "applications/examples/example_ble_beacon/scenes/scenes.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) BleBeaconAppScene##id,\ntypedef enum {\n#include \"scene_config.h\"\n    BleBeaconAppSceneNum,\n} BleBeaconAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers ble_beacon_app_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/examples/example_custom_font/application.fam",
    "content": "App(\n    appid=\"example_custom_font\",\n    name=\"Example: custom font\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_custom_font_main\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_custom_font/example_custom_font.c",
    "content": "/**\n * @file example_custom_font.c\n * @brief Custom font example.\n */\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <input/input.h>\n\n//This arrays contains the font itself. You can use any u8g2 font you want\n\n/*\n  Fontname: -Misc-Fixed-Medium-R-Normal--6-60-75-75-C-40-ISO10646-1\n  Copyright: Public domain font.  Share and enjoy.\n  Glyphs: 191/919\n  BBX Build Mode: 0\n*/\nconst uint8_t u8g2_font_4x6_t_cyrillic[] =\n    \"\\277\\0\\2\\2\\3\\3\\2\\4\\4\\4\\6\\0\\377\\5\\377\\5\\377\\0\\356\\1\\334\\2\\301 \\5\\200\\315\\0!\\6\\351\\310\"\n    \"\\254\\0\\42\\6\\223\\313$\\25#\\12\\254\\310\\244\\64T\\32*\\1$\\11\\263\\307\\245\\241\\301H\\11%\\10\\253\\310d\"\n    \"\\324F\\1&\\11\\254\\310\\305\\24\\253\\230\\2'\\5\\321\\313\\10(\\7\\362\\307\\251f\\0)\\10\\262\\307\\304T)\\0\"\n    \"*\\7\\253\\310\\244j\\65+\\10\\253\\310\\305\\264b\\2,\\6\\222\\307)\\0-\\5\\213\\312\\14.\\5\\311\\310\\4/\"\n    \"\\7\\253\\310Ve\\4\\60\\10\\253\\310UCU\\0\\61\\7\\253\\310%Y\\15\\62\\7\\253\\310\\65S\\32\\63\\10\\253\\310\"\n    \"\\314\\224\\301\\2\\64\\10\\253\\310$\\65b\\1\\65\\10\\253\\310\\214\\250\\301\\2\\66\\7\\253\\310M\\325\\2\\67\\10\\253\\310\\314\"\n    \"TF\\0\\70\\7\\253\\310\\255\\326\\2\\71\\7\\253\\310\\265\\344\\2:\\6\\341\\310\\304\\0;\\7\\252\\307e\\250\\0<\\10\"\n    \"\\253\\310\\246\\32d\\20=\\6\\233\\311l\\60>\\11\\253\\310d\\220A*\\1\\77\\11\\253\\310h\\220\\62L\\0@\\7\"\n    \"\\253\\310-\\33\\10A\\10\\253\\310UC\\251\\0B\\10\\253\\310\\250\\264\\322\\2C\\10\\253\\310U\\62U\\0D\\10\\253\"\n    \"\\310\\250d-\\0E\\10\\253\\310\\214\\250\\342\\0F\\10\\253\\310\\214\\250b\\4G\\10\\253\\310\\315\\244\\222\\0H\\10\\253\"\n    \"\\310$\\65\\224\\12I\\7\\253\\310\\254X\\15J\\7\\253\\310\\226\\252\\2K\\10\\253\\310$\\265\\222\\12L\\7\\253\\310\\304\"\n    \"\\346\\0M\\10\\253\\310\\244\\61\\224\\12N\\10\\253\\310\\252\\241$\\0O\\7\\253\\310UV\\5P\\10\\253\\310\\250\\264b\"\n    \"\\4Q\\10\\263\\307UV\\15\\2R\\10\\253\\310\\250\\264\\222\\12S\\10\\253\\310m\\220\\301\\2T\\7\\253\\310\\254\\330\\2\"\n    \"U\\7\\253\\310$\\327\\10V\\10\\253\\310$k\\244\\4W\\10\\253\\310$\\65\\206\\12X\\10\\253\\310$\\325R\\1Y\"\n    \"\\10\\253\\310$UV\\0Z\\7\\253\\310\\314T\\16[\\6\\352\\310\\254J\\134\\11\\253\\310\\304\\14\\62\\210\\1]\\6\\252\"\n    \"\\310\\250j^\\5\\223\\313\\65_\\5\\213\\307\\14`\\6\\322\\313\\304\\0a\\7\\243\\310-\\225\\4b\\10\\253\\310D\\225\"\n    \"\\324\\2c\\7\\243\\310\\315\\14\\4d\\10\\253\\310\\246\\245\\222\\0e\\6\\243\\310USf\\10\\253\\310\\246\\264b\\2g\"\n    \"\\10\\253\\307\\255$\\27\\0h\\10\\253\\310D\\225\\254\\0i\\10\\253\\310e$\\323\\0j\\10\\263\\307fX.\\0k\"\n    \"\\10\\253\\310\\304\\264\\222\\12l\\7\\253\\310\\310\\326\\0m\\10\\243\\310\\244\\241T\\0n\\7\\243\\310\\250d\\5o\\7\\243\"\n    \"\\310U\\252\\2p\\10\\253\\307\\250\\264b\\4q\\10\\253\\307-\\225d\\0r\\10\\243\\310\\244\\25#\\0s\\10\\243\\310\"\n    \"\\215\\14\\26\\0t\\10\\253\\310\\245\\25\\63\\10u\\7\\243\\310$+\\11v\\7\\243\\310$\\253\\2w\\10\\243\\310$\\65\"\n    \"T\\0x\\7\\243\\310\\244\\62\\25y\\10\\253\\307$\\225\\344\\2z\\7\\243\\310\\314\\224\\6{\\10\\263\\307\\246$k\\20\"\n    \"|\\6\\351\\310\\14\\1}\\11\\263\\307d\\20UL\\21~\\7\\224\\313%\\225\\0\\0\\0\\0\\4\\377\\377\\4\\1\\11\\253\"\n    \"\\310\\244\\261\\342\\0\\4\\2\\11\\253\\310\\214\\250\\222\\12\\4\\3\\10\\253\\310\\16Y\\2\\4\\4\\11\\253\\310M\\225\\201\\0\\4\"\n    \"\\5\\11\\253\\310m\\220\\301\\2\\4\\6\\10\\253\\310\\254X\\15\\4\\7\\11\\253\\310\\244\\221b\\32\\4\\10\\10\\253\\310\\226\\252\"\n    \"\\2\\4\\11\\11\\254\\310L\\325Z\\2\\4\\12\\11\\254\\310\\244\\326JK\\4\\13\\11\\253\\310\\250\\250\\222\\12\\4\\14\\10\\253\"\n    \"\\310\\312\\264\\12\\4\\16\\11\\263\\307\\244\\32u\\2\\4\\17\\11\\263\\307$\\327H\\11\\4\\20\\11\\253\\310UC\\251\\0\\4\"\n    \"\\21\\11\\253\\310\\214\\250\\322\\2\\4\\22\\11\\253\\310\\250\\264\\322\\2\\4\\23\\10\\253\\310\\214\\330\\4\\4\\24\\11\\263\\307\\254\\245\"\n    \"\\206\\12\\4\\25\\11\\253\\310\\214\\250\\342\\0\\4\\26\\12\\253\\310\\244\\221\\322H\\1\\4\\27\\12\\253\\310h\\220\\62X\\0\\4\"\n    \"\\30\\11\\253\\310\\304\\64T\\14\\4\\31\\11\\263\\307\\315\\64T\\14\\4\\32\\11\\253\\310$\\265\\222\\12\\4\\33\\10\\253\\310-\"\n    \"W\\0\\4\\34\\11\\253\\310\\244\\241\\254\\0\\4\\35\\11\\253\\310$\\65\\224\\12\\4\\36\\10\\253\\310UV\\5\\4\\37\\10\\253\"\n    \"\\310\\214\\344\\12\\4 \\11\\253\\310\\250\\264b\\4\\4!\\11\\253\\310U\\62U\\0\\4\\42\\10\\253\\310\\254\\330\\2\\4#\"\n    \"\\11\\263\\307$\\253L\\21\\4$\\12\\253\\310\\245\\221FJ\\0\\4%\\11\\253\\310$\\325R\\1\\4&\\10\\253\\310$\"\n    \"\\327\\10\\4'\\11\\253\\310$\\225d\\1\\4(\\11\\253\\310$\\65\\216\\0\\4)\\12\\264\\307\\244\\326#\\203\\0\\4*\"\n    \"\\13\\254\\310h\\220\\201LI\\1\\4+\\12\\254\\310D\\271\\324H\\1\\4,\\11\\253\\310\\304\\250\\322\\2\\4-\\11\\253\"\n    \"\\310h\\220\\344\\2\\4.\\12\\254\\310\\244\\244.\\225\\0\\4/\\11\\253\\310\\255\\264T\\0\\4\\60\\10\\243\\310-\\225\\4\"\n    \"\\4\\61\\11\\253\\310\\315\\221*\\0\\4\\62\\11\\243\\310\\14\\225\\26\\0\\4\\63\\10\\243\\310\\214X\\2\\4\\64\\11\\253\\307-\"\n    \"\\65T\\0\\4\\65\\7\\243\\310US\\4\\66\\11\\244\\310$S%\\1\\4\\67\\11\\243\\310\\254\\14\\26\\0\\4\\70\\11\\243\"\n    \"\\310\\244\\61T\\0\\4\\71\\11\\253\\310\\244\\326P\\1\\4:\\10\\243\\310$\\265\\12\\4;\\7\\243\\310-+\\4<\\11\"\n    \"\\243\\310\\244\\241T\\0\\4=\\11\\243\\310\\244\\241T\\0\\4>\\10\\243\\310U\\252\\2\\4\\77\\10\\243\\310\\214d\\5\\4\"\n    \"@\\11\\253\\307\\250\\264b\\4\\4A\\10\\243\\310\\315\\14\\4\\4B\\10\\243\\310\\254X\\1\\4C\\11\\253\\307$\\225\\344\"\n    \"\\2\\4D\\12\\263\\307\\305\\224T\\231\\0\\4E\\10\\243\\310\\244\\62\\25\\4F\\11\\253\\307$k\\304\\0\\4G\\11\\243\"\n    \"\\310$\\225d\\0\\4H\\10\\243\\310\\244q\\4\\4I\\11\\254\\307\\244\\364\\310 \\4J\\12\\244\\310h SR\\0\"\n    \"\\4K\\11\\244\\310\\304\\245F\\12\\4L\\11\\243\\310D\\225\\26\\0\\4M\\10\\243\\310H\\271\\0\\4N\\12\\244\\310\\244\"\n    \"\\244\\226J\\0\\4O\\10\\243\\310\\255\\264\\2\\4Q\\10\\253\\310\\244\\326\\24\\4R\\11\\263\\307D\\25U\\31\\4S\\11\"\n    \"\\253\\310\\246\\64b\\4\\4T\\11\\243\\310\\215\\224\\201\\0\\4U\\11\\243\\310\\215\\14\\26\\0\\4V\\11\\253\\310e$\\323\"\n    \"\\0\\4W\\11\\253\\310\\244\\14d\\32\\4X\\11\\263\\307fX.\\0\\4Y\\10\\244\\310\\251\\326\\22\\4Z\\11\\244\\310\"\n    \"\\244\\264\\322\\22\\4[\\11\\253\\310D\\25U\\1\\4\\134\\10\\253\\310\\312\\264\\12\\4^\\11\\263\\307\\244\\32u\\2\\4_\"\n    \"\\11\\253\\307$k\\244\\4\\4\\220\\10\\253\\310\\16Y\\2\\4\\221\\10\\243\\310\\16\\31\\1\\4\\222\\11\\253\\310\\251\\264b\\2\"\n    \"\\4\\223\\11\\243\\310\\251\\264\\22\\0\\0\";\n\n// Screen is 128x64 px\nstatic void app_draw_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n\n    canvas_clear(canvas);\n\n    canvas_set_custom_u8g2_font(canvas, u8g2_font_4x6_t_cyrillic);\n\n    canvas_draw_str(canvas, 0, 6, \"This is a tiny custom font\");\n    canvas_draw_str(canvas, 0, 12, \"012345.?! ,:;\\\"\\'@#$%\");\n    canvas_draw_str(canvas, 0, 18, \"И немного юникода\");\n}\n\nstatic void app_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t example_custom_font_main(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, app_draw_callback, NULL);\n    view_port_input_callback_set(view_port, app_input_callback, event_queue);\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    InputEvent event;\n\n    bool running = true;\n\n    while(running) {\n        if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {\n            if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {\n                switch(event.key) {\n                case InputKeyBack:\n                    running = false;\n                    break;\n                default:\n                    break;\n                }\n            }\n        }\n    }\n\n    view_port_enabled_set(view_port, false);\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_event_loop/application.fam",
    "content": "App(\n    appid=\"example_event_loop_event_flags\",\n    name=\"Example: Event Loop Event Flags\",\n    apptype=FlipperAppType.EXTERNAL,\n    sources=[\"example_event_loop_event_flags.c\"],\n    entry_point=\"example_event_loop_event_flags_app\",\n    fap_category=\"Examples\",\n)\n\nApp(\n    appid=\"example_event_loop_timer\",\n    name=\"Example: Event Loop Timer\",\n    apptype=FlipperAppType.EXTERNAL,\n    sources=[\"example_event_loop_timer.c\"],\n    entry_point=\"example_event_loop_timer_app\",\n    fap_category=\"Examples\",\n)\n\nApp(\n    appid=\"example_event_loop_mutex\",\n    name=\"Example: Event Loop Mutex\",\n    apptype=FlipperAppType.EXTERNAL,\n    sources=[\"example_event_loop_mutex.c\"],\n    entry_point=\"example_event_loop_mutex_app\",\n    fap_category=\"Examples\",\n)\n\nApp(\n    appid=\"example_event_loop_stream_buffer\",\n    name=\"Example: Event Loop Stream Buffer\",\n    apptype=FlipperAppType.EXTERNAL,\n    sources=[\"example_event_loop_stream_buffer.c\"],\n    entry_point=\"example_event_loop_stream_buffer_app\",\n    fap_category=\"Examples\",\n)\n\nApp(\n    appid=\"example_event_loop_multi\",\n    name=\"Example: Event Loop Multi\",\n    apptype=FlipperAppType.EXTERNAL,\n    sources=[\"example_event_loop_multi.c\"],\n    entry_point=\"example_event_loop_multi_app\",\n    requires=[\"gui\"],\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_event_loop/example_event_loop_event_flags.c",
    "content": "/**\n * @file example_event_loop_event_flags.c\n * @brief Example application demonstrating the use of the FuriEventFlag primitive in FuriEventLoop instances.\n *\n * This application receives keystrokes from the input service and sets the appropriate flags,\n * which are subsequently processed in the event loop\n */\n\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_port.h>\n\n#include <furi_hal_random.h>\n\n#define TAG \"ExampleEventLoopEventFlags\"\n\ntypedef struct {\n    Gui* gui;\n    ViewPort* view_port;\n    FuriEventLoop* event_loop;\n    FuriEventFlag* event_flag;\n} EventLoopEventFlagsApp;\n\ntypedef enum {\n    EventLoopEventFlagsOk = (1 << 0),\n    EventLoopEventFlagsUp = (1 << 1),\n    EventLoopEventFlagsDown = (1 << 2),\n    EventLoopEventFlagsLeft = (1 << 3),\n    EventLoopEventFlagsRight = (1 << 4),\n    EventLoopEventFlagsBack = (1 << 5),\n    EventLoopEventFlagsExit = (1 << 6),\n} EventLoopEventFlags;\n\n#define EVENT_LOOP_EVENT_FLAGS_MASK                                                 \\\n    (EventLoopEventFlagsOk | EventLoopEventFlagsUp | EventLoopEventFlagsDown |      \\\n     EventLoopEventFlagsLeft | EventLoopEventFlagsRight | EventLoopEventFlagsBack | \\\n     EventLoopEventFlagsExit)\n\n// This function is executed in the GUI context each time an input event occurs (e.g. the user pressed a key)\nstatic void event_loop_event_flags_app_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    EventLoopEventFlagsApp* app = context;\n    UNUSED(app);\n\n    if(event->type == InputTypePress) {\n        if(event->key == InputKeyOk) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsOk);\n        } else if(event->key == InputKeyUp) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsUp);\n        } else if(event->key == InputKeyDown) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsDown);\n        } else if(event->key == InputKeyLeft) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsLeft);\n        } else if(event->key == InputKeyRight) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsRight);\n        } else if(event->key == InputKeyBack) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsBack);\n        }\n    } else if(event->type == InputTypeLong) {\n        if(event->key == InputKeyBack) {\n            furi_event_flag_set(app->event_flag, EventLoopEventFlagsExit);\n        }\n    }\n}\n\n// This function is executed each time a new event flag is inserted in the input event flag.\nstatic void\n    event_loop_event_flags_app_event_flags_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    EventLoopEventFlagsApp* app = context;\n\n    furi_assert(object == app->event_flag);\n\n    EventLoopEventFlags events =\n        furi_event_flag_wait(app->event_flag, EVENT_LOOP_EVENT_FLAGS_MASK, FuriFlagWaitAny, 0);\n    furi_check((events) != 0);\n\n    if(events & EventLoopEventFlagsOk) {\n        FURI_LOG_I(TAG, \"Press \\\"Ok\\\"\");\n    }\n    if(events & EventLoopEventFlagsUp) {\n        FURI_LOG_I(TAG, \"Press \\\"Up\\\"\");\n    }\n    if(events & EventLoopEventFlagsDown) {\n        FURI_LOG_I(TAG, \"Press \\\"Down\\\"\");\n    }\n    if(events & EventLoopEventFlagsLeft) {\n        FURI_LOG_I(TAG, \"Press \\\"Left\\\"\");\n    }\n    if(events & EventLoopEventFlagsRight) {\n        FURI_LOG_I(TAG, \"Press \\\"Right\\\"\");\n    }\n    if(events & EventLoopEventFlagsBack) {\n        FURI_LOG_I(TAG, \"Press \\\"Back\\\"\");\n    }\n    if(events & EventLoopEventFlagsExit) {\n        FURI_LOG_I(TAG, \"Exit App\");\n        furi_event_loop_stop(app->event_loop);\n    }\n}\n\nstatic EventLoopEventFlagsApp* event_loop_event_flags_app_alloc(void) {\n    EventLoopEventFlagsApp* app = malloc(sizeof(EventLoopEventFlagsApp));\n\n    // Create event loop instances.\n    app->event_loop = furi_event_loop_alloc();\n    // Create event flag instances.\n    app->event_flag = furi_event_flag_alloc();\n\n    // Create GUI instance.\n    app->gui = furi_record_open(RECORD_GUI);\n    app->view_port = view_port_alloc();\n    // Gain exclusive access to the input events\n    view_port_input_callback_set(app->view_port, event_loop_event_flags_app_input_callback, app);\n    gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);\n\n    // Notify the event loop about incoming messages in the event flag\n    furi_event_loop_subscribe_event_flag(\n        app->event_loop,\n        app->event_flag,\n        FuriEventLoopEventIn | FuriEventLoopEventFlagEdge,\n        event_loop_event_flags_app_event_flags_callback,\n        app);\n\n    return app;\n}\n\nstatic void event_loop_event_flags_app_free(EventLoopEventFlagsApp* app) {\n    gui_remove_view_port(app->gui, app->view_port);\n\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    // Delete all instances\n    view_port_free(app->view_port);\n    app->view_port = NULL;\n\n    // IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_unsubscribe(app->event_loop, app->event_flag);\n\n    furi_event_flag_free(app->event_flag);\n    app->event_flag = NULL;\n\n    furi_event_loop_free(app->event_loop);\n    app->event_loop = NULL;\n\n    free(app);\n}\n\nstatic void event_loop_event_flags_app_run(EventLoopEventFlagsApp* app) {\n    FURI_LOG_I(TAG, \"Press keys to see them printed here.\");\n    FURI_LOG_I(TAG, \"Quickly press different keys to generate events.\");\n    FURI_LOG_I(TAG, \"Long press \\\"Back\\\" to exit app.\");\n\n    // Run the application event loop. This call will block until the application is about to exit.\n    furi_event_loop_run(app->event_loop);\n}\n\n/*******************************************************************\n *                     vvv START HERE vvv\n *\n * The application's entry point - referenced in application.fam\n *******************************************************************/\nint32_t example_event_loop_event_flags_app(void* arg) {\n    UNUSED(arg);\n\n    EventLoopEventFlagsApp* app = event_loop_event_flags_app_alloc();\n    event_loop_event_flags_app_run(app);\n    event_loop_event_flags_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_event_loop/example_event_loop_multi.c",
    "content": "/**\n * @file example_event_loop_multi.c\n * @brief Example application that demonstrates multiple primitives used with two FuriEventLoop instances.\n *\n * This application simulates a complex use case of having two concurrent event loops (each one executing in\n * its own thread) using a stream buffer for communication and additional timers and message passing to handle\n * the keypad input. Additionally, it shows how to use thread signals to stop an event loop in another thread.\n * The GUI functionality is there only for the purpose of exclusive access to the input events.\n *\n * The application's functionality consists of the following:\n * - Print keypad key names and types when pressed,\n * - If the Back key is long-pressed, a countdown starts upon completion of which the app exits,\n * - The countdown can be cancelled by long-pressing the Ok button, it also resets the counter,\n * - Blocks of random data are periodically generated in a separate thread,\n * - When ready, the main application thread gets notified and prints the data.\n */\n\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_port.h>\n\n#include <furi_hal_random.h>\n\n#define TAG \"ExampleEventLoopMulti\"\n\n#define COUNTDOWN_START_VALUE   (5UL)\n#define COUNTDOWN_INTERVAL_MS   (1000UL)\n#define WORKER_DATA_INTERVAL_MS (1500UL)\n\n#define INPUT_QUEUE_SIZE   (8)\n#define STREAM_BUFFER_SIZE (16)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriEventLoopTimer* timer;\n    FuriStreamBuffer* stream_buffer;\n} EventLoopMultiAppWorker;\n\ntypedef struct {\n    Gui* gui;\n    ViewPort* view_port;\n    FuriThread* worker_thread;\n    FuriEventLoop* event_loop;\n    FuriMessageQueue* input_queue;\n    FuriEventLoopTimer* exit_timer;\n    FuriStreamBuffer* stream_buffer;\n    uint32_t exit_countdown_value;\n} EventLoopMultiApp;\n\n/*\n * Worker functions\n */\n\n// This function is executed each time the data is taken out of the stream buffer. It is used to restart the worker timer.\nstatic void\n    event_loop_multi_app_stream_buffer_worker_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    EventLoopMultiAppWorker* worker = context;\n\n    furi_assert(object == worker->stream_buffer);\n\n    FURI_LOG_I(TAG, \"Data was removed from buffer\");\n    // Restart the timer to generate another block of random data.\n    furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);\n}\n\n// This function is executed when the worker timer expires. The timer will NOT restart automatically\n// since it is of one-shot type.\nstatic void event_loop_multi_app_worker_timer_callback(void* context) {\n    furi_assert(context);\n    EventLoopMultiAppWorker* worker = context;\n\n    // Generate a block of random data.\n    uint8_t data[STREAM_BUFFER_SIZE];\n    furi_hal_random_fill_buf(data, sizeof(data));\n    // Put the generated data in the stream buffer.\n    // IMPORTANT: No waiting in the event handlers!\n    furi_check(\n        furi_stream_buffer_send(worker->stream_buffer, &data, sizeof(data), 0) == sizeof(data));\n}\n\nstatic EventLoopMultiAppWorker*\n    event_loop_multi_app_worker_alloc(FuriStreamBuffer* stream_buffer) {\n    EventLoopMultiAppWorker* worker = malloc(sizeof(EventLoopMultiAppWorker));\n    // Create the worker event loop.\n    worker->event_loop = furi_event_loop_alloc();\n    // Create the timer governing the data generation.\n    // It is of one-shot type, i.e. it will not restart automatically upon expiration.\n    worker->timer = furi_event_loop_timer_alloc(\n        worker->event_loop,\n        event_loop_multi_app_worker_timer_callback,\n        FuriEventLoopTimerTypeOnce,\n        worker);\n\n    // Using the same stream buffer as the main thread (it was already created beforehand).\n    worker->stream_buffer = stream_buffer;\n    // Notify the worker event loop about data being taken out of the stream buffer.\n    furi_event_loop_subscribe_stream_buffer(\n        worker->event_loop,\n        worker->stream_buffer,\n        FuriEventLoopEventOut | FuriEventLoopEventFlagEdge,\n        event_loop_multi_app_stream_buffer_worker_callback,\n        worker);\n\n    return worker;\n}\n\nstatic void event_loop_multi_app_worker_free(EventLoopMultiAppWorker* worker) {\n    // IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_unsubscribe(worker->event_loop, worker->stream_buffer);\n    // IMPORTANT: All timers MUST be deleted before deleting the associated event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_timer_free(worker->timer);\n    // Now it is okay to delete the event loop.\n    furi_event_loop_free(worker->event_loop);\n\n    free(worker);\n}\n\nstatic void event_loop_multi_app_worker_run(EventLoopMultiAppWorker* worker) {\n    furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);\n    furi_event_loop_run(worker->event_loop);\n}\n\n// This function is the worker thread body and (obviously) is executed in the worker thread.\nstatic int32_t event_loop_multi_app_worker_thread(void* context) {\n    furi_assert(context);\n    EventLoopMultiApp* app = context;\n\n    // Because an event loop is used, it MUST be created in the thread it will be run in.\n    // Therefore, the worker creation and deletion is handled in the worker thread.\n    EventLoopMultiAppWorker* worker = event_loop_multi_app_worker_alloc(app->stream_buffer);\n    event_loop_multi_app_worker_run(worker);\n    event_loop_multi_app_worker_free(worker);\n\n    return 0;\n}\n\n/*\n * Main application functions\n */\n\n// This function is executed in the GUI context each time an input event occurs (e.g. the user pressed a key)\nstatic void event_loop_multi_app_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    EventLoopMultiApp* app = context;\n    // Pass the event to the the application's input queue\n    furi_check(furi_message_queue_put(app->input_queue, event, FuriWaitForever) == FuriStatusOk);\n}\n\n// This function is executed each time new data is available in the stream buffer.\nstatic void\n    event_loop_multi_app_stream_buffer_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    EventLoopMultiApp* app = context;\n\n    furi_assert(object == app->stream_buffer);\n    // Get the data from the stream buffer\n    uint8_t data[STREAM_BUFFER_SIZE];\n    // IMPORTANT: No waiting in the event handlers!\n    furi_check(\n        furi_stream_buffer_receive(app->stream_buffer, &data, sizeof(data), 0) == sizeof(data));\n\n    // Format the data for printing and print it to the debug output.\n    FuriString* tmp_str = furi_string_alloc();\n    for(uint32_t i = 0; i < sizeof(data); ++i) {\n        furi_string_cat_printf(tmp_str, \"%02X \", data[i]);\n    }\n\n    FURI_LOG_I(TAG, \"Received data: %s\", furi_string_get_cstr(tmp_str));\n    furi_string_free(tmp_str);\n}\n\n// This function is executed each time a new message is inserted in the input queue.\nstatic void event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    EventLoopMultiApp* app = context;\n\n    furi_assert(object == app->input_queue);\n\n    InputEvent event;\n    // IMPORTANT: No waiting in the event handlers!\n    furi_check(furi_message_queue_get(app->input_queue, &event, 0) == FuriStatusOk);\n\n    if(event.type == InputTypeLong) {\n        // The user has long-pressed the Back key, try starting the countdown.\n        if(event.key == InputKeyBack) {\n            if(!furi_event_loop_timer_is_running(app->exit_timer)) {\n                // Actually start the countdown\n                FURI_LOG_I(TAG, \"Starting exit countdown!\");\n                furi_event_loop_timer_start(app->exit_timer, COUNTDOWN_INTERVAL_MS);\n\n            } else {\n                // The countdown is already in progress, print a warning message\n                FURI_LOG_W(TAG, \"Countdown has already been started\");\n            }\n\n            // The user has long-pressed the Ok key, try stopping the countdown.\n        } else if(event.key == InputKeyOk) {\n            if(furi_event_loop_timer_is_running(app->exit_timer)) {\n                // Actually cancel the countdown\n                FURI_LOG_I(TAG, \"Exit countdown cancelled!\");\n                app->exit_countdown_value = COUNTDOWN_START_VALUE;\n                furi_event_loop_timer_stop(app->exit_timer);\n\n            } else {\n                // The countdown is not running, print a warning message\n                FURI_LOG_W(TAG, \"Countdown has not been started yet\");\n            }\n\n        } else {\n            // Not a Back or Ok key, just print its name.\n            FURI_LOG_I(TAG, \"Long press: %s\", input_get_key_name(event.key));\n        }\n\n    } else if(event.type == InputTypeShort) {\n        // Not a long press, just print the key's name.\n        FURI_LOG_I(TAG, \"Short press: %s\", input_get_key_name(event.key));\n    }\n}\n\n// This function is executed each time the countdown timer expires.\nstatic void event_loop_multi_app_exit_timer_callback(void* context) {\n    furi_assert(context);\n    EventLoopMultiApp* app = context;\n\n    FURI_LOG_I(TAG, \"Exiting in %lu ...\", app->exit_countdown_value);\n\n    // If the coundown value has reached 0, exit the application\n    if(app->exit_countdown_value == 0) {\n        FURI_LOG_I(TAG, \"Exiting NOW!\");\n\n        // Send a signal to the worker thread to exit.\n        // A signal handler that handles FuriSignalExit is already set by default.\n        furi_thread_signal(app->worker_thread, FuriSignalExit, NULL);\n        // Request the application event loop to stop.\n        furi_event_loop_stop(app->event_loop);\n\n        // Otherwise just decrement it and wait for the next time the timer expires.\n    } else {\n        app->exit_countdown_value -= 1;\n    }\n}\n\nstatic EventLoopMultiApp* event_loop_multi_app_alloc(void) {\n    EventLoopMultiApp* app = malloc(sizeof(EventLoopMultiApp));\n    // Create event loop instances.\n    app->event_loop = furi_event_loop_alloc();\n\n    // Create a worker thread instance. The worker event loop will execute inside it.\n    app->worker_thread = furi_thread_alloc_ex(\n        \"EventLoopMultiWorker\", 1024, event_loop_multi_app_worker_thread, app);\n    // Create a message queue to receive the input events.\n    app->input_queue = furi_message_queue_alloc(INPUT_QUEUE_SIZE, sizeof(InputEvent));\n    // Create a stream buffer to receive the generated data.\n    app->stream_buffer = furi_stream_buffer_alloc(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE);\n    // Create a timer to run the countdown.\n    app->exit_timer = furi_event_loop_timer_alloc(\n        app->event_loop,\n        event_loop_multi_app_exit_timer_callback,\n        FuriEventLoopTimerTypePeriodic,\n        app);\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->view_port = view_port_alloc();\n    // Start the countdown from this value\n    app->exit_countdown_value = COUNTDOWN_START_VALUE;\n    // Gain exclusive access to the input events\n    view_port_input_callback_set(app->view_port, event_loop_multi_app_input_callback, app);\n    gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);\n    // Notify the event loop about incoming messages in the queue\n    furi_event_loop_subscribe_message_queue(\n        app->event_loop,\n        app->input_queue,\n        FuriEventLoopEventIn,\n        event_loop_multi_app_input_queue_callback,\n        app);\n    // Notify the event loop about new data in the stream buffer\n    furi_event_loop_subscribe_stream_buffer(\n        app->event_loop,\n        app->stream_buffer,\n        FuriEventLoopEventIn | FuriEventLoopEventFlagEdge,\n        event_loop_multi_app_stream_buffer_callback,\n        app);\n\n    return app;\n}\n\nstatic void event_loop_multi_app_free(EventLoopMultiApp* app) {\n    gui_remove_view_port(app->gui, app->view_port);\n    furi_record_close(RECORD_GUI);\n    // IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_unsubscribe(app->event_loop, app->input_queue);\n    furi_event_loop_unsubscribe(app->event_loop, app->stream_buffer);\n    // Delete all instances\n    view_port_free(app->view_port);\n    furi_message_queue_free(app->input_queue);\n    furi_stream_buffer_free(app->stream_buffer);\n    // IMPORTANT: All timers MUST be deleted before deleting the associated event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_timer_free(app->exit_timer);\n    furi_thread_free(app->worker_thread);\n    furi_event_loop_free(app->event_loop);\n\n    free(app);\n}\n\nstatic void event_loop_multi_app_run(EventLoopMultiApp* app) {\n    FURI_LOG_I(TAG, \"Press keys to see them printed here.\");\n    FURI_LOG_I(TAG, \"Long press \\\"Back\\\" to exit after %lu seconds.\", COUNTDOWN_START_VALUE);\n    FURI_LOG_I(TAG, \"Long press \\\"Ok\\\" to cancel the countdown.\");\n\n    // Start the worker thread\n    furi_thread_start(app->worker_thread);\n    // Run the application event loop. This call will block until the application is about to exit.\n    furi_event_loop_run(app->event_loop);\n    // Wait for the worker thread to finish.\n    furi_thread_join(app->worker_thread);\n}\n\n/*******************************************************************\n *                     vvv START HERE vvv\n *\n * The application's entry point - referenced in application.fam\n *******************************************************************/\nint32_t example_event_loop_multi_app(void* arg) {\n    UNUSED(arg);\n\n    EventLoopMultiApp* app = event_loop_multi_app_alloc();\n    event_loop_multi_app_run(app);\n    event_loop_multi_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_event_loop/example_event_loop_mutex.c",
    "content": "/**\n * @file example_event_loop_mutex.c\n * @brief Example application that demonstrates the FuriEventLoop and FuriMutex integration.\n *\n * This application simulates a use case where a time-consuming blocking operation is executed\n * in a separate thread and a mutex is being used for synchronization. The application runs 10 iterations\n * of the above mentioned simulated work and prints the results to the debug output each time, then exits.\n */\n\n#include <furi.h>\n#include <furi_hal_random.h>\n\n#define TAG \"ExampleEventLoopMutex\"\n\n#define WORKER_ITERATION_COUNT (10)\n// We are interested in IN events (for the mutex, that means that the mutex has been released),\n// using edge trigger mode (reacting only to changes in mutex state) and\n// employing one-shot mode to automatically unsubscribe before the event is processed.\n#define MUTEX_EVENT_AND_FLAGS \\\n    (FuriEventLoopEventIn | FuriEventLoopEventFlagEdge | FuriEventLoopEventFlagOnce)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriThread* worker_thread;\n    FuriMutex* worker_mutex;\n    uint8_t worker_result;\n} EventLoopMutexApp;\n\n// This funciton is being run in a separate thread to simulate lenghty blocking operations\nstatic int32_t event_loop_mutex_app_worker_thread(void* context) {\n    furi_assert(context);\n    EventLoopMutexApp* app = context;\n\n    FURI_LOG_I(TAG, \"Worker thread started\");\n\n    // Run 10 iterations of simulated work\n    for(uint32_t i = 0; i < WORKER_ITERATION_COUNT; ++i) {\n        FURI_LOG_I(TAG, \"Doing work ...\");\n        // Take the mutex so that no-one can access the worker_result variable\n        furi_check(furi_mutex_acquire(app->worker_mutex, FuriWaitForever) == FuriStatusOk);\n        // Simulate a blocking operation with a random delay between 900 and 1100 ms\n        const uint32_t work_time_ms = 900 + furi_hal_random_get() % 200;\n        furi_delay_ms(work_time_ms);\n        // Simulate a result with a random number between 0 and 255\n        app->worker_result = furi_hal_random_get() % 0xFF;\n\n        FURI_LOG_I(TAG, \"Work done in %lu ms\", work_time_ms);\n        // Release the mutex, which will notify the event loop that the result is ready\n        furi_check(furi_mutex_release(app->worker_mutex) == FuriStatusOk);\n        // Return control to the scheduler so that the event loop can take the mutex in its turn\n        furi_thread_yield();\n    }\n\n    FURI_LOG_I(TAG, \"All work done, worker thread out!\");\n    // Request the event loop to stop\n    furi_event_loop_stop(app->event_loop);\n\n    return 0;\n}\n\n// This function is being run each time when the mutex gets released\nstatic void event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n\n    EventLoopMutexApp* app = context;\n    furi_assert(object == app->worker_mutex);\n\n    // Take the mutex so that no-one can access the worker_result variable\n    // IMPORTANT: the wait time MUST be 0, i.e. the event loop event callbacks\n    // must NOT ever block. If it is possible that the mutex will be taken by\n    // others, then the event callback code must take it into account.\n    furi_check(furi_mutex_acquire(app->worker_mutex, 0) == FuriStatusOk);\n    // Access the worker_result variable and print it.\n    FURI_LOG_I(TAG, \"Result available! Value: %u\", app->worker_result);\n    // Release the mutex, enabling the worker thread to continue when it's ready\n    furi_check(furi_mutex_release(app->worker_mutex) == FuriStatusOk);\n    // Subscribe for the mutex release events again, since we were unsubscribed automatically\n    // before processing the event.\n    furi_event_loop_subscribe_mutex(\n        app->event_loop,\n        app->worker_mutex,\n        MUTEX_EVENT_AND_FLAGS,\n        event_loop_mutex_app_event_callback,\n        app);\n}\n\nstatic EventLoopMutexApp* event_loop_mutex_app_alloc(void) {\n    EventLoopMutexApp* app = malloc(sizeof(EventLoopMutexApp));\n\n    // Create an event loop instance.\n    app->event_loop = furi_event_loop_alloc();\n    // Create a worker thread instance.\n    app->worker_thread = furi_thread_alloc_ex(\n        \"EventLoopMutexWorker\", 1024, event_loop_mutex_app_worker_thread, app);\n    // Create a mutex instance.\n    app->worker_mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n    // Subscribe for the mutex release events.\n    // Note that since FuriEventLoopEventFlagOneShot is used, we will be automatically unsubscribed\n    // from events before entering the event processing callback. This is necessary in order to not\n    // trigger on events caused by releasing the mutex in the callback.\n    furi_event_loop_subscribe_mutex(\n        app->event_loop,\n        app->worker_mutex,\n        MUTEX_EVENT_AND_FLAGS,\n        event_loop_mutex_app_event_callback,\n        app);\n\n    return app;\n}\n\nstatic void event_loop_mutex_app_free(EventLoopMutexApp* app) {\n    // IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_unsubscribe(app->event_loop, app->worker_mutex);\n    // Delete all instances\n    furi_thread_free(app->worker_thread);\n    furi_mutex_free(app->worker_mutex);\n    furi_event_loop_free(app->event_loop);\n\n    free(app);\n}\n\nstatic void event_loop_mutex_app_run(EventLoopMutexApp* app) {\n    furi_thread_start(app->worker_thread);\n    furi_event_loop_run(app->event_loop);\n    furi_thread_join(app->worker_thread);\n}\n\n// The application's entry point - referenced in application.fam\nint32_t example_event_loop_mutex_app(void* arg) {\n    UNUSED(arg);\n\n    EventLoopMutexApp* app = event_loop_mutex_app_alloc();\n    event_loop_mutex_app_run(app);\n    event_loop_mutex_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_event_loop/example_event_loop_stream_buffer.c",
    "content": "/**\n * @file example_event_loop_stream_buffer.c\n * @brief Example application that demonstrates the FuriEventLoop and FuriStreamBuffer integration.\n *\n * This application simulates a use case where some data data stream comes from a separate thread (or hardware)\n * and a stream buffer is used to act as an intermediate buffer. The worker thread produces 10 iterations of 32\n * bytes of simulated data, and each time when the buffer is half-filled, the data is taken out of it and printed\n * to the debug output. After completing all iterations, the application exits.\n */\n\n#include <furi.h>\n#include <furi_hal_random.h>\n\n#define TAG \"ExampleEventLoopStreamBuffer\"\n\n#define WORKER_ITERATION_COUNT (10)\n\n#define STREAM_BUFFER_SIZE            (32)\n#define STREAM_BUFFER_TRIG_LEVEL      (STREAM_BUFFER_SIZE / 2)\n#define STREAM_BUFFER_EVENT_AND_FLAGS (FuriEventLoopEventIn | FuriEventLoopEventFlagEdge)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriThread* worker_thread;\n    FuriStreamBuffer* stream_buffer;\n} EventLoopStreamBufferApp;\n\n// This funciton is being run in a separate thread to simulate data coming from a producer thread or some device.\nstatic int32_t event_loop_stream_buffer_app_worker_thread(void* context) {\n    furi_assert(context);\n    EventLoopStreamBufferApp* app = context;\n\n    FURI_LOG_I(TAG, \"Worker thread started\");\n\n    for(uint32_t i = 0; i < WORKER_ITERATION_COUNT; ++i) {\n        // Produce 32 bytes of simulated data.\n        for(uint32_t j = 0; j < STREAM_BUFFER_SIZE; ++j) {\n            // Simulate incoming data by generating a random byte.\n            uint8_t data = furi_hal_random_get() % 0xFF;\n            // Put the byte in the buffer. Depending on the use case, it may or may be not acceptable\n            // to wait for free space to become available.\n            furi_check(\n                furi_stream_buffer_send(app->stream_buffer, &data, 1, FuriWaitForever) == 1);\n            // Delay between 30 and 50 ms to slow down the output for clarity.\n            furi_delay_ms(30 + furi_hal_random_get() % 20);\n        }\n    }\n\n    FURI_LOG_I(TAG, \"All work done, worker thread out!\");\n    // Request the event loop to stop\n    furi_event_loop_stop(app->event_loop);\n\n    return 0;\n}\n\n// This function is being run each time when the number of bytes in the buffer is above its trigger level.\nstatic void\n    event_loop_stream_buffer_app_event_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    EventLoopStreamBufferApp* app = context;\n\n    furi_assert(object == app->stream_buffer);\n\n    // Temporary buffer that can hold at most half of the stream buffer's capacity.\n    uint8_t data[STREAM_BUFFER_TRIG_LEVEL];\n    // Receive the data. It is guaranteed that the amount of data in the buffer will be equal to\n    // or greater than the trigger level, therefore, no waiting delay is necessary.\n    furi_check(\n        furi_stream_buffer_receive(app->stream_buffer, data, sizeof(data), 0) == sizeof(data));\n\n    // Format the data for printing and print it to the debug output.\n    FuriString* tmp_str = furi_string_alloc();\n    for(uint32_t i = 0; i < sizeof(data); ++i) {\n        furi_string_cat_printf(tmp_str, \"%02X \", data[i]);\n    }\n\n    FURI_LOG_I(TAG, \"Received data: %s\", furi_string_get_cstr(tmp_str));\n    furi_string_free(tmp_str);\n}\n\nstatic EventLoopStreamBufferApp* event_loop_stream_buffer_app_alloc(void) {\n    EventLoopStreamBufferApp* app = malloc(sizeof(EventLoopStreamBufferApp));\n\n    // Create an event loop instance.\n    app->event_loop = furi_event_loop_alloc();\n    // Create a worker thread instance.\n    app->worker_thread = furi_thread_alloc_ex(\n        \"EventLoopStreamBufferWorker\", 1024, event_loop_stream_buffer_app_worker_thread, app);\n    // Create a stream_buffer instance.\n    app->stream_buffer = furi_stream_buffer_alloc(STREAM_BUFFER_SIZE, STREAM_BUFFER_TRIG_LEVEL);\n    // Subscribe for the stream buffer IN events in edge triggered mode.\n    furi_event_loop_subscribe_stream_buffer(\n        app->event_loop,\n        app->stream_buffer,\n        STREAM_BUFFER_EVENT_AND_FLAGS,\n        event_loop_stream_buffer_app_event_callback,\n        app);\n\n    return app;\n}\n\nstatic void event_loop_stream_buffer_app_free(EventLoopStreamBufferApp* app) {\n    // IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.\n    // Failure to do so will result in a crash.\n    furi_event_loop_unsubscribe(app->event_loop, app->stream_buffer);\n    // Delete all instances\n    furi_thread_free(app->worker_thread);\n    furi_stream_buffer_free(app->stream_buffer);\n    furi_event_loop_free(app->event_loop);\n\n    free(app);\n}\n\nstatic void event_loop_stream_buffer_app_run(EventLoopStreamBufferApp* app) {\n    furi_thread_start(app->worker_thread);\n    furi_event_loop_run(app->event_loop);\n    furi_thread_join(app->worker_thread);\n}\n\n// The application's entry point - referenced in application.fam\nint32_t example_event_loop_stream_buffer_app(void* arg) {\n    UNUSED(arg);\n\n    EventLoopStreamBufferApp* app = event_loop_stream_buffer_app_alloc();\n    event_loop_stream_buffer_app_run(app);\n    event_loop_stream_buffer_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_event_loop/example_event_loop_timer.c",
    "content": "/**\n * @file example_event_loop_timer.c\n * @brief Example application that demonstrates FuriEventLoop's software timer capability.\n *\n * This application prints a countdown from 10 to 0 to the debug output and then exits.\n * Despite only one timer being used in this example for clarity, an event loop instance can have\n * an arbitrary number of independent timers of any type (periodic or one-shot).\n *\n */\n#include <furi.h>\n\n#define TAG \"ExampleEventLoopTimer\"\n\n#define COUNTDOWN_START_VALUE (10)\n#define COUNTDOWN_INTERVAL_MS (1000)\n\ntypedef struct {\n    FuriEventLoop* event_loop;\n    FuriEventLoopTimer* timer;\n    uint32_t countdown_value;\n} EventLoopTimerApp;\n\n// This function is called each time the timer expires (i.e. once per 1000 ms (1s) in this example)\nstatic void event_loop_timer_callback(void* context) {\n    furi_assert(context);\n    EventLoopTimerApp* app = context;\n\n    // Print the countdown value\n    FURI_LOG_I(TAG, \"T-00:00:%02lu\", app->countdown_value);\n\n    if(app->countdown_value == 0) {\n        // If the countdown reached 0, print the final line and stop the event loop\n        FURI_LOG_I(TAG, \"Blast off to adventure!\");\n        // After this call, the control will be returned back to event_loop_timers_app_run()\n        furi_event_loop_stop(app->event_loop);\n\n    } else {\n        // Decrement the countdown value\n        app->countdown_value -= 1;\n    }\n}\n\nstatic EventLoopTimerApp* event_loop_timer_app_alloc(void) {\n    EventLoopTimerApp* app = malloc(sizeof(EventLoopTimerApp));\n\n    // Create an event loop instance.\n    app->event_loop = furi_event_loop_alloc();\n    // Create a software timer instance.\n    // The timer is bound to the event loop instance and will execute in its context.\n    // Here, the timer type is periodic, i.e. it will restart automatically after expiring.\n    app->timer = furi_event_loop_timer_alloc(\n        app->event_loop, event_loop_timer_callback, FuriEventLoopTimerTypePeriodic, app);\n    // The countdown value will be tracked in this variable.\n    app->countdown_value = COUNTDOWN_START_VALUE;\n\n    return app;\n}\n\nstatic void event_loop_timer_app_free(EventLoopTimerApp* app) {\n    // IMPORTANT: All event loop timers MUST be deleted BEFORE deleting the event loop itself.\n    // Failure to do so will result in a crash.\n    furi_event_loop_timer_free(app->timer);\n    // With all timers deleted, it's safe to delete the event loop.\n    furi_event_loop_free(app->event_loop);\n    free(app);\n}\n\nstatic void event_loop_timer_app_run(EventLoopTimerApp* app) {\n    FURI_LOG_I(TAG, \"All systems go! Prepare for countdown!\");\n\n    // Timers can be started either before the event loop is run, or in any\n    // callback function called by a running event loop.\n    furi_event_loop_timer_start(app->timer, COUNTDOWN_INTERVAL_MS);\n    // This call will block until furi_event_loop_stop() is called.\n    furi_event_loop_run(app->event_loop);\n}\n\n// The application's entry point - referenced in application.fam\nint32_t example_event_loop_timer_app(void* arg) {\n    UNUSED(arg);\n\n    EventLoopTimerApp* app = event_loop_timer_app_alloc();\n    event_loop_timer_app_run(app);\n    event_loop_timer_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_images/ReadMe.md",
    "content": "# Application icons {#example_app_images}\n\n## Source code\n\nSource code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_images).\n\n## General principle\n\nTo use icons, do the following:\n\n* Add a line to the application manifest: `fap_icon_assets=\"folder\"`, where `folder` points to the folder where your icons are located\n* Add `#include \"application_id_icons.h\"` to the application code, where `application_id` is the appid from the manifest\n* Every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension\n\n## Example\n\nWe have an application with the following manifest:\n\n```\nApp(\n    appid=\"example_images\",\n    ...\n    fap_icon_assets=\"images\",\n)\n```\n\nSo the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file.\n\nThe example code is located in `example_images_main.c` and contains the following line:\n\n```\n#include \"example_images_icons.h\"\n```\n\nImage `dolphin_71x25.png` is available as `I_dolphin_71x25`.\n"
  },
  {
    "path": "applications/examples/example_images/application.fam",
    "content": "App(\n    appid=\"example_images\",\n    name=\"Example: Images\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_images_main\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_category=\"Examples\",\n    fap_icon_assets=\"images\",\n)\n"
  },
  {
    "path": "applications/examples/example_images/example_images.c",
    "content": "/**\n * @file example_images.c\n * @brief Custom images example.\n */\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <input/input.h>\n\n/* Magic happens here -- this file is generated by fbt.\n * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */\n#include \"example_images_icons.h\"\n\ntypedef struct {\n    int32_t x, y;\n} ImagePosition;\n\nstatic ImagePosition image_position = {.x = 0, .y = 0};\n\n// Screen is 128x64 px\nstatic void app_draw_callback(Canvas* canvas, void* ctx) {\n    UNUSED(ctx);\n\n    canvas_clear(canvas);\n    canvas_draw_icon(canvas, image_position.x, image_position.y, &I_dolphin_71x25);\n}\n\nstatic void app_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nint32_t example_images_main(void* p) {\n    UNUSED(p);\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, app_draw_callback, NULL);\n    view_port_input_callback_set(view_port, app_input_callback, event_queue);\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    InputEvent event;\n\n    bool running = true;\n    while(running) {\n        if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {\n            if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {\n                switch(event.key) {\n                case InputKeyLeft:\n                    image_position.x -= 2;\n                    break;\n                case InputKeyRight:\n                    image_position.x += 2;\n                    break;\n                case InputKeyUp:\n                    image_position.y -= 2;\n                    break;\n                case InputKeyDown:\n                    image_position.y += 2;\n                    break;\n                default:\n                    running = false;\n                    break;\n                }\n            }\n        }\n        view_port_update(view_port);\n    }\n\n    view_port_enabled_set(view_port, false);\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_number_input/ReadMe.md",
    "content": "# Number Input {#example_number_input}\n\nSimple keyboard that limits user inputs to a full number (integer). Useful to enforce correct entries without the need for intense validations after a user input. \n\n## Source code\n\nSource code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_number_input).\n\n## General principle\n\nDefinition of min/max values is required. Numbers are of type int32_t. If negative numbers are allowed within min - max, an additional button is displayed to switch the sign between + and -. \n\nIt is also possible to define a header text, as shown in this example app with the 3 different input options. "
  },
  {
    "path": "applications/examples/example_number_input/application.fam",
    "content": "App(\n    appid=\"example_number_input\",\n    name=\"Example: Number Input\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_number_input\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_icon=\"example_number_input_10px.png\",\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_number_input/example_number_input.c",
    "content": "#include \"example_number_input.h\"\n\nbool example_number_input_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool example_number_input_back_event_callback(void* context) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic ExampleNumberInput* example_number_input_alloc() {\n    ExampleNumberInput* app = malloc(sizeof(ExampleNumberInput));\n    app->gui = furi_record_open(RECORD_GUI);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n\n    app->scene_manager = scene_manager_alloc(&example_number_input_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, example_number_input_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, example_number_input_back_event_callback);\n\n    app->number_input = number_input_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        ExampleNumberInputViewIdNumberInput,\n        number_input_get_view(app->number_input));\n\n    app->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        ExampleNumberInputViewIdShowNumber,\n        dialog_ex_get_view(app->dialog_ex));\n\n    app->current_number = 5;\n    app->min_value = INT32_MIN;\n    app->max_value = INT32_MAX;\n\n    return app;\n}\n\nstatic void example_number_input_free(ExampleNumberInput* app) {\n    furi_assert(app);\n\n    view_dispatcher_remove_view(app->view_dispatcher, ExampleNumberInputViewIdShowNumber);\n    dialog_ex_free(app->dialog_ex);\n\n    view_dispatcher_remove_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);\n    number_input_free(app->number_input);\n\n    scene_manager_free(app->scene_manager);\n    view_dispatcher_free(app->view_dispatcher);\n\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n\n    //Remove whatever is left\n    free(app);\n}\n\nint32_t example_number_input(void* p) {\n    UNUSED(p);\n    ExampleNumberInput* app = example_number_input_alloc();\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneShowNumber);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    example_number_input_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_number_input/example_number_input.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/elements.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/number_input.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <input/input.h>\n\n#include \"scenes/example_number_input_scene.h\"\n\ntypedef struct ExampleNumberInputShowNumber ExampleNumberInputShowNumber;\n\ntypedef enum {\n    ExampleNumberInputViewIdShowNumber,\n    ExampleNumberInputViewIdNumberInput,\n} ExampleNumberInputViewId;\n\ntypedef struct {\n    Gui* gui;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    NumberInput* number_input;\n    DialogEx* dialog_ex;\n\n    int32_t current_number;\n    int32_t min_value;\n    int32_t max_value;\n} ExampleNumberInput;\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene.c",
    "content": "#include \"example_number_input_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const example_number_input_on_enter_handlers[])(void*) = {\n#include \"example_number_input_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const example_number_input_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"example_number_input_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const example_number_input_on_exit_handlers[])(void* context) = {\n#include \"example_number_input_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers example_number_input_scene_handlers = {\n    .on_enter_handlers = example_number_input_on_enter_handlers,\n    .on_event_handlers = example_number_input_on_event_handlers,\n    .on_exit_handlers = example_number_input_on_exit_handlers,\n    .scene_num = ExampleNumberInputSceneNum,\n};\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) ExampleNumberInputScene##id,\ntypedef enum {\n#include \"example_number_input_scene_config.h\"\n    ExampleNumberInputSceneNum,\n} ExampleNumberInputScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers example_number_input_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"example_number_input_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"example_number_input_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"example_number_input_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene_config.h",
    "content": "ADD_SCENE(example_number_input, input_number, InputNumber)\nADD_SCENE(example_number_input, show_number, ShowNumber)\nADD_SCENE(example_number_input, input_max, InputMax)\nADD_SCENE(example_number_input, input_min, InputMin)\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene_input_max.c",
    "content": "#include \"../example_number_input.h\"\n\nvoid example_number_input_scene_input_max_callback(void* context, int32_t number) {\n    ExampleNumberInput* app = context;\n    app->max_value = number;\n    view_dispatcher_send_custom_event(app->view_dispatcher, 0);\n}\n\nvoid example_number_input_scene_input_max_on_enter(void* context) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n    NumberInput* number_input = app->number_input;\n\n    number_input_set_header_text(number_input, \"Enter the maximum value\");\n    number_input_set_result_callback(\n        number_input,\n        example_number_input_scene_input_max_callback,\n        context,\n        app->max_value,\n        app->min_value,\n        INT32_MAX);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);\n}\n\nbool example_number_input_scene_input_max_on_event(void* context, SceneManagerEvent event) {\n    ExampleNumberInput* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_previous_scene(app->scene_manager);\n        return true;\n    }\n    return consumed;\n}\n\nvoid example_number_input_scene_input_max_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene_input_min.c",
    "content": "#include \"../example_number_input.h\"\n\nvoid example_number_input_scene_input_min_callback(void* context, int32_t number) {\n    ExampleNumberInput* app = context;\n    app->min_value = number;\n    view_dispatcher_send_custom_event(app->view_dispatcher, 0);\n}\n\nvoid example_number_input_scene_input_min_on_enter(void* context) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n    NumberInput* number_input = app->number_input;\n\n    number_input_set_header_text(number_input, \"Enter the minimum value\");\n    number_input_set_result_callback(\n        number_input,\n        example_number_input_scene_input_min_callback,\n        context,\n        app->min_value,\n        INT32_MIN,\n        app->max_value);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);\n}\n\nbool example_number_input_scene_input_min_on_event(void* context, SceneManagerEvent event) {\n    ExampleNumberInput* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_previous_scene(app->scene_manager);\n        return true;\n    }\n    return consumed;\n}\n\nvoid example_number_input_scene_input_min_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene_input_number.c",
    "content": "#include \"../example_number_input.h\"\n\nvoid example_number_input_scene_input_number_callback(void* context, int32_t number) {\n    ExampleNumberInput* app = context;\n    app->current_number = number;\n    view_dispatcher_send_custom_event(app->view_dispatcher, 0);\n}\n\nvoid example_number_input_scene_input_number_on_enter(void* context) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n    NumberInput* number_input = app->number_input;\n\n    char str[50];\n    snprintf(str, sizeof(str), \"Set Number (%ld - %ld)\", app->min_value, app->max_value);\n\n    number_input_set_header_text(number_input, str);\n    number_input_set_result_callback(\n        number_input,\n        example_number_input_scene_input_number_callback,\n        context,\n        app->current_number,\n        app->min_value,\n        app->max_value);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);\n}\n\nbool example_number_input_scene_input_number_on_event(void* context, SceneManagerEvent event) {\n    ExampleNumberInput* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) { //Back button pressed\n        scene_manager_previous_scene(app->scene_manager);\n        return true;\n    }\n    return consumed;\n}\n\nvoid example_number_input_scene_input_number_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c",
    "content": "#include \"../example_number_input.h\"\n\nstatic void\n    example_number_input_scene_confirm_dialog_callback(DialogExResult result, void* context) {\n    ExampleNumberInput* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nstatic void example_number_input_scene_update_view(void* context) {\n    ExampleNumberInput* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_set_header(dialog_ex, \"The number is\", 64, 0, AlignCenter, AlignTop);\n\n    char buffer[12] = {};\n    snprintf(buffer, sizeof(buffer), \"%ld\", app->current_number);\n    dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter);\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Min\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Max\");\n    dialog_ex_set_center_button_text(dialog_ex, \"Change\");\n\n    dialog_ex_set_result_callback(dialog_ex, example_number_input_scene_confirm_dialog_callback);\n    dialog_ex_set_context(dialog_ex, app);\n}\n\nvoid example_number_input_scene_show_number_on_enter(void* context) {\n    furi_assert(context);\n    ExampleNumberInput* app = context;\n\n    example_number_input_scene_update_view(app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdShowNumber);\n}\n\nbool example_number_input_scene_show_number_on_event(void* context, SceneManagerEvent event) {\n    ExampleNumberInput* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultCenter:\n            scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputNumber);\n            consumed = true;\n            break;\n        case DialogExResultLeft:\n            scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputMin);\n            consumed = true;\n            break;\n        case DialogExResultRight:\n            scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputMax);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid example_number_input_scene_show_number_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/examples/example_plugins/application.fam",
    "content": "App(\n    appid=\"example_plugins\",\n    name=\"Example: App w/plugin\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_plugins_app\",\n    stack_size=2 * 1024,\n    fap_category=\"Examples\",\n    sources=[\"*.c\", \"!plugin*.c\"],\n)\n\nApp(\n    appid=\"example_plugins_multi\",\n    name=\"Example: App w/plugins\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_plugins_multi_app\",\n    stack_size=2 * 1024,\n    fap_category=\"Examples\",\n)\n\nApp(\n    appid=\"example_plugin1\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"example_plugin1_ep\",\n    requires=[\"example_plugins\", \"example_plugins_multi\"],\n    sources=[\"plugin1.c\"],\n)\n\nApp(\n    appid=\"example_plugin2\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"example_plugin2_ep\",\n    requires=[\"example_plugins_multi\"],\n    sources=[\"plugin2.c\"],\n)\n"
  },
  {
    "path": "applications/examples/example_plugins/example_plugins.c",
    "content": "/**\n * @file example_plugins.c\n * @brief Plugin host application example.\n *\n * Loads a single plugin and calls its methods.\n */\n\n#include \"plugin_interface.h\"\n\n#include <furi.h>\n\n#include <flipper_application/flipper_application.h>\n#include <loader/firmware_api/firmware_api.h>\n#include <storage/storage.h>\n\n#define TAG \"ExamplePlugins\"\n\nint32_t example_plugins_app(void* p) {\n    UNUSED(p);\n\n    FURI_LOG_I(TAG, \"Starting\");\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);\n\n    do {\n        FlipperApplicationPreloadStatus preload_res =\n            flipper_application_preload(app, APP_DATA_PATH(\"plugins/example_plugin1.fal\"));\n\n        if(preload_res != FlipperApplicationPreloadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to preload plugin\");\n            break;\n        }\n\n        if(!flipper_application_is_plugin(app)) {\n            FURI_LOG_E(TAG, \"Plugin file is not a library\");\n            break;\n        }\n\n        FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app);\n        if(load_status != FlipperApplicationLoadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to load plugin file\");\n            break;\n        }\n\n        const FlipperAppPluginDescriptor* app_descriptor =\n            flipper_application_plugin_get_descriptor(app);\n\n        FURI_LOG_I(\n            TAG,\n            \"Loaded plugin for appid '%s', API %lu\",\n            app_descriptor->appid,\n            app_descriptor->ep_api_version);\n\n        furi_check(app_descriptor->ep_api_version == PLUGIN_API_VERSION);\n        furi_check(strcmp(app_descriptor->appid, PLUGIN_APP_ID) == 0);\n\n        const ExamplePlugin* plugin = app_descriptor->entry_point;\n\n        FURI_LOG_I(TAG, \"Plugin name: %s\", plugin->name);\n        FURI_LOG_I(TAG, \"Plugin method1: %d\", plugin->method1());\n        FURI_LOG_I(TAG, \"Plugin method2(7,8): %d\", plugin->method2(7, 8));\n        FURI_LOG_I(TAG, \"Plugin method2(1337,228): %d\", plugin->method2(1337, 228));\n    } while(false);\n    flipper_application_free(app);\n\n    furi_record_close(RECORD_STORAGE);\n    FURI_LOG_I(TAG, \"Goodbye!\");\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins/example_plugins_multi.c",
    "content": "/**\n * @file example_plugins_multi.c\n * @brief Advanced plugin host application example.\n *\n * It uses PluginManager to load all plugins from a directory\n */\n\n#include \"plugin_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <flipper_application/plugins/plugin_manager.h>\n#include <loader/firmware_api/firmware_api.h>\n\n#include <furi.h>\n\n#define TAG \"ExamplePlugins\"\n\nint32_t example_plugins_multi_app(void* p) {\n    UNUSED(p);\n\n    FURI_LOG_I(TAG, \"Starting\");\n\n    PluginManager* manager =\n        plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface);\n\n    if(plugin_manager_load_all(manager, APP_DATA_PATH(\"plugins\")) != PluginManagerErrorNone) {\n        FURI_LOG_E(TAG, \"Failed to load all libs\");\n        return 0;\n    }\n\n    uint32_t plugin_count = plugin_manager_get_count(manager);\n    FURI_LOG_I(TAG, \"Loaded %lu plugin(s)\", plugin_count);\n\n    for(uint32_t i = 0; i < plugin_count; i++) {\n        const ExamplePlugin* plugin = plugin_manager_get_ep(manager, i);\n        FURI_LOG_I(TAG, \"plugin name: %s\", plugin->name);\n        FURI_LOG_I(TAG, \"plugin method1: %d\", plugin->method1());\n        FURI_LOG_I(TAG, \"plugin method2(7,8): %d\", plugin->method2(7, 8));\n    }\n\n    plugin_manager_free(manager);\n    FURI_LOG_I(TAG, \"Goodbye!\");\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins/plugin1.c",
    "content": "/**\n * @file plugin1.c\n * @brief Plugin example 1.\n *\n * A simple plugin implementing example_plugins application's plugin interface\n */\n\n#include \"plugin_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n\nstatic int example_plugin1_method1(void) {\n    return 42;\n}\n\nstatic int example_plugin1_method2(int arg1, int arg2) {\n    return arg1 + arg2;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const ExamplePlugin example_plugin1 = {\n    .name = \"Demo App Plugin 1\",\n    .method1 = &example_plugin1_method1,\n    .method2 = &example_plugin1_method2,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor example_plugin1_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &example_plugin1,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* example_plugin1_ep(void) {\n    return &example_plugin1_descriptor;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins/plugin2.c",
    "content": "/**\n * @file plugin2.c\n * @brief Plugin example 2.\n *\n * Second plugin implementing example_plugins application's plugin interface\n */\n\n#include \"plugin_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n\nstatic int example_plugin2_method1(void) {\n    return 1337;\n}\n\nstatic int example_plugin2_method2(int arg1, int arg2) {\n    return arg1 - arg2;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const ExamplePlugin example_plugin2 = {\n    .name = \"Demo App Plugin 2\",\n    .method1 = &example_plugin2_method1,\n    .method2 = &example_plugin2_method2,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor example_plugin2_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &example_plugin2,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor */\nconst FlipperAppPluginDescriptor* example_plugin2_ep(void) {\n    return &example_plugin2_descriptor;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins/plugin_interface.h",
    "content": "/**\n * @file plugin_interface.h\n * @brief Example plugin interface.\n *\n * Common interface between a plugin and host application\n */\n#pragma once\n\n#define PLUGIN_APP_ID      \"example_plugins\"\n#define PLUGIN_API_VERSION 1\n\ntypedef struct {\n    const char* name;\n    int (*method1)(void);\n    int (*method2)(int, int);\n} ExamplePlugin;\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/app_api.c",
    "content": "#include \"app_api.h\"\n\n/* Actual implementation of app's API and its private state */\n\nstatic uint32_t accumulator = 0;\n\nvoid app_api_accumulator_set(uint32_t value) {\n    accumulator = value;\n}\n\nuint32_t app_api_accumulator_get(void) {\n    return accumulator;\n}\n\nvoid app_api_accumulator_add(uint32_t value) {\n    accumulator += value;\n}\n\nvoid app_api_accumulator_sub(uint32_t value) {\n    accumulator -= value;\n}\n\nvoid app_api_accumulator_mul(uint32_t value) {\n    accumulator *= value;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/app_api.h",
    "content": "/**\n * @file app_api.h\n * @brief Application API example.\n *\n * This file contains an API that is internally implemented by the application\n * It is also exposed to plugins to allow them to use the application's API.\n */\n#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid app_api_accumulator_set(uint32_t value);\n\nuint32_t app_api_accumulator_get(void);\n\nvoid app_api_accumulator_add(uint32_t value);\n\nvoid app_api_accumulator_sub(uint32_t value);\n\nvoid app_api_accumulator_mul(uint32_t value);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/app_api_interface.h",
    "content": "#pragma once\n\n#include <flipper_application/api_hashtable/api_hashtable.h>\n\n/* \n * Resolver interface with private application's symbols. \n * Implementation is contained in app_api_table.c\n */\nextern const ElfApiInterface* const application_api_interface;\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/app_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n/* \n * This file contains an implementation of a symbol table \n * with private app's symbols. It is used by composite API resolver\n * to load plugins that use internal application's APIs.\n */\n#include \"app_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(app_api_table), \"Detected API method hash collision!\");\n\nconstexpr HashtableApiInterface applicaton_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        /* generic resolver using pre-sorted array */\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    /* pointers to application's API table boundaries */\n    app_api_table.cbegin(),\n    app_api_table.cend(),\n};\n\n/* Casting to generic resolver to use in Composite API resolver */\nextern \"C\" const ElfApiInterface* const application_api_interface =\n    &applicaton_hashtable_api_interface;\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/app_api_table_i.h",
    "content": "#include \"app_api.h\"\n\n/* \n * A list of app's private functions and objects to expose for plugins.\n * It is used to generate a table of symbols for import resolver to use.\n * TBD: automatically generate this table from app's header files\n */\nstatic constexpr auto app_api_table = sort(create_array_t<sym_entry>(\n    API_METHOD(app_api_accumulator_set, void, (uint32_t)),\n    API_METHOD(app_api_accumulator_get, uint32_t, ()),\n    API_METHOD(app_api_accumulator_add, void, (uint32_t)),\n    API_METHOD(app_api_accumulator_sub, void, (uint32_t)),\n    API_METHOD(app_api_accumulator_mul, void, (uint32_t))));\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/application.fam",
    "content": "App(\n    appid=\"example_advanced_plugins\",\n    name=\"Example: advanced plugins\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_advanced_plugins_app\",\n    stack_size=2 * 1024,\n    fap_category=\"Examples\",\n    sources=[\"*.c*\", \"!plugin*.c\"],\n)\n\nApp(\n    appid=\"advanced_plugin1\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"advanced_plugin1_ep\",\n    requires=[\"example_advanced_plugins\"],\n    sources=[\"plugin1.c\"],\n    fal_embedded=True,\n)\n\nApp(\n    appid=\"advanced_plugin2\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"advanced_plugin2_ep\",\n    requires=[\"example_advanced_plugins\"],\n    sources=[\"plugin2.c\"],\n    fal_embedded=True,\n)\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/example_advanced_plugins.c",
    "content": "#include \"app_api.h\"\n#include \"plugin_interface.h\"\n#include \"app_api_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <flipper_application/plugins/plugin_manager.h>\n#include <flipper_application/plugins/composite_resolver.h>\n\n#include <loader/firmware_api/firmware_api.h>\n\n#define TAG \"ExampleAdvancedPlugins\"\n\nint32_t example_advanced_plugins_app(void* p) {\n    UNUSED(p);\n\n    FURI_LOG_I(TAG, \"Starting\");\n\n    CompositeApiResolver* resolver = composite_api_resolver_alloc();\n    composite_api_resolver_add(resolver, firmware_api_interface);\n    composite_api_resolver_add(resolver, application_api_interface);\n\n    PluginManager* manager = plugin_manager_alloc(\n        PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver));\n\n    do {\n        // For built-in .fals (fal_embedded==True), use APP_ASSETS_PATH\n        // Otherwise, use APP_DATA_PATH\n        if(plugin_manager_load_all(manager, APP_ASSETS_PATH(\"plugins\")) !=\n           PluginManagerErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to load all libs\");\n            break;\n        }\n\n        uint32_t plugin_count = plugin_manager_get_count(manager);\n        FURI_LOG_I(TAG, \"Loaded libs: %lu\", plugin_count);\n\n        for(uint32_t i = 0; i < plugin_count; i++) {\n            const AdvancedPlugin* plugin = plugin_manager_get_ep(manager, i);\n            FURI_LOG_I(TAG, \"plugin name: %s. Calling methods\", plugin->name);\n            plugin->method1(228);\n            plugin->method2();\n            FURI_LOG_I(TAG, \"Accumulator: %lu\", app_api_accumulator_get());\n        }\n    } while(0);\n\n    plugin_manager_free(manager);\n    composite_api_resolver_free(resolver);\n    FURI_LOG_I(TAG, \"Goodbye!\");\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/plugin1.c",
    "content": "/**\n * @file plugin1.c\n * @brief Plugin example 1.\n *\n * This plugin uses both firmware's API interface and private application headers.\n * It can be loaded by a plugin manager that uses CompoundApiInterface,\n * which combines both interfaces.\n */\n\n#include \"app_api.h\"\n#include \"plugin_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <furi.h>\n\nstatic void advanced_plugin1_method1(int arg1) {\n    /* This function is implemented inside host application */\n    app_api_accumulator_add(arg1);\n}\n\nstatic void advanced_plugin1_method2(void) {\n    /* Accumulator value is stored inside host application */\n    FURI_LOG_I(\"TEST\", \"Plugin 1, accumulator: %lu\", app_api_accumulator_get());\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const AdvancedPlugin advanced_plugin1 = {\n    .name = \"Advanced Plugin 1\",\n    .method1 = &advanced_plugin1_method1,\n    .method2 = &advanced_plugin1_method2,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor advanced_plugin1_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &advanced_plugin1,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor */\nconst FlipperAppPluginDescriptor* advanced_plugin1_ep(void) {\n    return &advanced_plugin1_descriptor;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/plugin2.c",
    "content": "/**\n * @file plugin2.c\n * @brief Plugin example 2.\n *\n * This plugin uses both firmware's API interface and private application headers.\n * It can be loaded by a plugin manager that uses CompoundApiInterface,\n * which combines both interfaces.\n */\n\n#include \"app_api.h\"\n#include \"plugin_interface.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <furi.h>\n\nstatic void advanced_plugin2_method1(int arg1) {\n    /* This function is implemented inside host application */\n    app_api_accumulator_mul(arg1);\n}\n\nstatic void advanced_plugin2_method2(void) {\n    /* Accumulator value is stored inside host application */\n    FURI_LOG_I(\"TEST\", \"Plugin 2, accumulator: %lu\", app_api_accumulator_get());\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const AdvancedPlugin advanced_plugin2 = {\n    .name = \"Advanced Plugin 2\",\n    .method1 = &advanced_plugin2_method1,\n    .method2 = &advanced_plugin2_method2,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor advanced_plugin2_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &advanced_plugin2,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor */\nconst FlipperAppPluginDescriptor* advanced_plugin2_ep(void) {\n    return &advanced_plugin2_descriptor;\n}\n"
  },
  {
    "path": "applications/examples/example_plugins_advanced/plugin_interface.h",
    "content": "/**\n * @file plugin_interface.h\n * @brief Example plugin interface.\n *\n * Common interface between a plugin and host application\n */\n#pragma once\n\n#define PLUGIN_APP_ID      \"example_plugins_advanced\"\n#define PLUGIN_API_VERSION 1\n\ntypedef struct {\n    const char* name;\n    void (*method1)(int);\n    void (*method2)(void);\n} AdvancedPlugin;\n"
  },
  {
    "path": "applications/examples/example_thermo/README.md",
    "content": "# 1-Wire Thermometer {#example_thermo}\n\nThis example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. \nIt also covers basic GUI, input handling, threads and localisation.\n\n## Source code\n\nSource code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_thermo).\n\n## Electrical connections\n\nBefore launching the application, connect the sensor to Flipper's external GPIO according to the table below:\n| DS18B20 | Flipper |\n| :-----: | :-----: |\n| VDD | 9 |\n| GND | 18 |\n| DQ  | 17 |\n\n*NOTE 1*: GND is also available on pins 8 and 11.\n\n*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9.\n\n## Launching the application\n\nIn order to launch this demo, follow the steps below:\n1. Make sure your Flipper has an SD card installed.\n2. Connect your Flipper to the computer via a USB cable.\n3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice.\n\n## Changing the data pin\n\nIt is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:\n\n```c\n/* Possible GPIO pin choices:\n - gpio_ext_pc0\n - gpio_ext_pc1\n - gpio_ext_pc3\n - gpio_ext_pb2\n - gpio_ext_pb3\n - gpio_ext_pa4\n - gpio_ext_pa6\n - gpio_ext_pa7\n - gpio_ibutton\n*/\n\n#define THERMO_GPIO_PIN (gpio_ibutton)\n```\nDo not forget about the external pull-up resistor as these pins do not have one built-in.\n\nWith the changes been made, recompile and launch the application again. \nThe on-screen text should reflect it by asking to connect the thermometer to another pin.\n"
  },
  {
    "path": "applications/examples/example_thermo/application.fam",
    "content": "App(\n    appid=\"example_thermo\",\n    name=\"Example: Thermometer\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_thermo_main\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_icon=\"example_thermo_10px.png\",\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_thermo/example_thermo.c",
    "content": "/**\n * @file example_thermo.c\n * @brief 1-Wire thermometer example.\n *\n * This file contains an example application that reads and displays\n * the temperature from a DS18B20 1-wire thermometer.\n *\n * It also covers basic GUI, input handling, threads and localisation.\n *\n * References:\n * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf\n */\n\n#include <gui/gui.h>\n#include <gui/view_port.h>\n\n#include <core/thread.h>\n#include <core/kernel.h>\n\n#include <locale/locale.h>\n\n#include <one_wire/maxim_crc.h>\n#include <one_wire/one_wire_host.h>\n\n#include <power/power_service/power.h>\n\n#define UPDATE_PERIOD_MS 1000UL\n#define TEXT_STORE_SIZE  64U\n\n#define DS18B20_CMD_SKIP_ROM        0xccU\n#define DS18B20_CMD_CONVERT         0x44U\n#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU\n\n#define DS18B20_CFG_RESOLUTION_POS  5U\n#define DS18B20_CFG_RESOLUTION_MASK 0x03U\n#define DS18B20_DECIMAL_PART_MASK   0x0fU\n\n#define DS18B20_SIGN_MASK 0xf0U\n\n/* Possible GPIO pin choices:\n - gpio_ext_pc0\n - gpio_ext_pc1\n - gpio_ext_pc3\n - gpio_ext_pb2\n - gpio_ext_pb3\n - gpio_ext_pa4\n - gpio_ext_pa6\n - gpio_ext_pa7\n - gpio_ibutton\n*/\n\n#define THERMO_GPIO_PIN (gpio_ibutton)\n\n/* Flags which the reader thread responds to */\ntypedef enum {\n    ReaderThreadFlagExit = 1,\n} ReaderThreadFlag;\n\ntypedef union {\n    struct {\n        uint8_t temp_lsb; /* Least significant byte of the temperature */\n        uint8_t temp_msb; /* Most significant byte of the temperature */\n        uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */\n        uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */\n        uint8_t config; /* Configuration register */\n        uint8_t reserved[3]; /* Not used */\n        uint8_t crc; /* CRC checksum for error detection */\n    } fields;\n    uint8_t bytes[9];\n} DS18B20Scratchpad;\n\n/* Application context structure */\ntypedef struct {\n    Gui* gui;\n    ViewPort* view_port;\n    FuriThread* reader_thread;\n    FuriMessageQueue* event_queue;\n    OneWireHost* onewire;\n    Power* power;\n    float temp_celsius;\n    bool has_device;\n} ExampleThermoContext;\n\n/*************** 1-Wire Communication and Processing *****************/\n\n/* Commands the thermometer to begin measuring the temperature. */\nstatic void example_thermo_request_temperature(ExampleThermoContext* context) {\n    OneWireHost* onewire = context->onewire;\n\n    /* All 1-wire transactions must happen in a critical section, i.e\n       not interrupted by other threads. */\n    FURI_CRITICAL_ENTER();\n\n    bool success = false;\n    do {\n        /* Each communication with a 1-wire device starts by a reset.\n           The function will return true if a device responded with a presence pulse. */\n        if(!onewire_host_reset(onewire)) break;\n        /* After the reset, a ROM operation must follow.\n           If there is only one device connected, the \"Skip ROM\" command is most appropriate\n           (it can also be used to address all of the connected devices in some cases).*/\n        onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);\n        /* After the ROM operation, a device-specific command is issued.\n           In this case, it's a request to start measuring the temperature. */\n        onewire_host_write(onewire, DS18B20_CMD_CONVERT);\n\n        success = true;\n    } while(false);\n\n    context->has_device = success;\n\n    FURI_CRITICAL_EXIT();\n}\n\n/* Reads the measured temperature from the thermometer. */\nstatic void example_thermo_read_temperature(ExampleThermoContext* context) {\n    /* If there was no device detected, don't try to read the temperature */\n    if(!context->has_device) {\n        return;\n    }\n\n    OneWireHost* onewire = context->onewire;\n\n    /* All 1-wire transactions must happen in a critical section, i.e\n       not interrupted by other threads. */\n    FURI_CRITICAL_ENTER();\n\n    bool success = false;\n\n    do {\n        DS18B20Scratchpad buf;\n\n        /* Attempt reading the temperature 10 times before giving up */\n        size_t attempts_left = 10;\n        do {\n            /* Each communication with a 1-wire device starts by a reset.\n            The function will return true if a device responded with a presence pulse. */\n            if(!onewire_host_reset(onewire)) continue;\n\n            /* After the reset, a ROM operation must follow.\n            If there is only one device connected, the \"Skip ROM\" command is most appropriate\n            (it can also be used to address all of the connected devices in some cases).*/\n            onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);\n\n            /* After the ROM operation, a device-specific command is issued.\n            This time, it will be the \"Read Scratchpad\" command which will\n            prepare the device's internal buffer memory for reading. */\n            onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD);\n\n            /* The actual reading happens here. A total of 9 bytes is read. */\n            onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes));\n\n            /* Calculate the checksum and compare it with one provided by the device. */\n            const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT);\n\n            /* Checksums match, exit the loop */\n            if(crc == buf.fields.crc) break;\n\n        } while(--attempts_left);\n\n        if(attempts_left == 0) break;\n\n        /* Get the measurement resolution from the configuration register. (See [1] page 9) */\n        const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) &\n                                        DS18B20_CFG_RESOLUTION_MASK;\n\n        /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */\n        const uint8_t decimal_mask =\n            (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) &\n            DS18B20_DECIMAL_PART_MASK;\n\n        /* Get the integer and decimal part of the temperature (See [1] page 6) */\n        const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U);\n        const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask;\n\n        /* Calculate the sign of the temperature (See [1] page 6) */\n        const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0;\n\n        /* Combine the integer and decimal part together */\n        const float temp_celsius_abs = integer_part + decimal_part / 16.f;\n\n        /* Set the appropriate sign */\n        context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs;\n\n        success = true;\n    } while(false);\n\n    context->has_device = success;\n\n    FURI_CRITICAL_EXIT();\n}\n\n/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */\nstatic int32_t example_thermo_reader_thread_callback(void* ctx) {\n    ExampleThermoContext* context = ctx;\n\n    for(;;) {\n        /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */\n        example_thermo_request_temperature(context);\n\n        /* Wait for the measurement to finish. At the same time wait for an exit signal. */\n        const uint32_t flags =\n            furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS);\n\n        /* If an exit signal was received, return from this thread. */\n        if(flags != (unsigned)FuriFlagErrorTimeout) break;\n\n        /* The measurement is now ready, read it from the termometer. */\n        example_thermo_read_temperature(context);\n    }\n\n    return 0;\n}\n\n/*************** GUI, Input and Main Loop *****************/\n\n/* Draw the GUI of the application. The screen is completely redrawn during each call. */\nstatic void example_thermo_draw_callback(Canvas* canvas, void* ctx) {\n    ExampleThermoContext* context = ctx;\n    char text_store[TEXT_STORE_SIZE];\n    const size_t middle_x = canvas_width(canvas) / 2U;\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, \"Thermometer Demo\");\n    canvas_draw_line(canvas, 0, 16, 128, 16);\n\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str_aligned(canvas, middle_x, 30, AlignCenter, AlignBottom, \"Connect thermometer\");\n\n    snprintf(\n        text_store,\n        TEXT_STORE_SIZE,\n        \"to GPIO pin %ld\",\n        furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN));\n    canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store);\n\n    canvas_set_font(canvas, FontKeyboard);\n\n    if(context->has_device) {\n        float temp;\n        char temp_units;\n\n        /* The application is locale-aware.\n           Change Settings->System->Units to check it out. */\n        switch(locale_get_measurement_unit()) {\n        case LocaleMeasurementUnitsMetric:\n            temp = context->temp_celsius;\n            temp_units = 'C';\n            break;\n        case LocaleMeasurementUnitsImperial:\n            temp = locale_celsius_to_fahrenheit(context->temp_celsius);\n            temp_units = 'F';\n            break;\n        default:\n            furi_crash(\"Illegal measurement units\");\n        }\n        /* If a reading is available, display it */\n        snprintf(text_store, TEXT_STORE_SIZE, \"Temperature: %+.1f%c\", (double)temp, temp_units);\n    } else {\n        /* Or show a message that no data is available */\n        strlcpy(text_store, \"-- No data --\", TEXT_STORE_SIZE);\n    }\n\n    canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store);\n}\n\n/* This function is called from the GUI thread. All it does is put the event\n   into the application's queue so it can be processed later. */\nstatic void example_thermo_input_callback(InputEvent* event, void* ctx) {\n    ExampleThermoContext* context = ctx;\n    furi_message_queue_put(context->event_queue, event, FuriWaitForever);\n}\n\n/* Starts the reader thread and handles the input */\nstatic void example_thermo_run(ExampleThermoContext* context) {\n    /* Enable power on external pins */\n    power_enable_otg(context->power, true);\n\n    /* Configure the hardware in host mode */\n    onewire_host_start(context->onewire);\n\n    /* Start the reader thread. It will talk to the thermometer in the background. */\n    furi_thread_start(context->reader_thread);\n\n    /* An endless loop which handles the input*/\n    for(bool is_running = true; is_running;) {\n        InputEvent event;\n        /* Wait for an input event. Input events come from the GUI thread via a callback. */\n        const FuriStatus status =\n            furi_message_queue_get(context->event_queue, &event, FuriWaitForever);\n\n        /* This application is only interested in short button presses. */\n        if((status != FuriStatusOk) || (event.type != InputTypeShort)) {\n            continue;\n        }\n\n        /* When the user presses the \"Back\" button, break the loop and exit the application. */\n        if(event.key == InputKeyBack) {\n            is_running = false;\n        }\n    }\n\n    /* Signal the reader thread to cease operation and exit */\n    furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit);\n\n    /* Wait for the reader thread to finish */\n    furi_thread_join(context->reader_thread);\n\n    /* Reset the hardware */\n    onewire_host_stop(context->onewire);\n\n    /* Disable power on external pins */\n    power_enable_otg(context->power, false);\n}\n\n/******************** Initialisation & startup *****************************/\n\n/* Allocate the memory and initialise the variables */\nstatic ExampleThermoContext* example_thermo_context_alloc(void) {\n    ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext));\n\n    context->view_port = view_port_alloc();\n    view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context);\n    view_port_input_callback_set(context->view_port, example_thermo_input_callback, context);\n\n    context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    context->reader_thread = furi_thread_alloc();\n    furi_thread_set_stack_size(context->reader_thread, 1024U);\n    furi_thread_set_context(context->reader_thread, context);\n    furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback);\n\n    context->gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen);\n\n    context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);\n\n    context->power = furi_record_open(RECORD_POWER);\n\n    return context;\n}\n\n/* Release the unused resources and deallocate memory */\nstatic void example_thermo_context_free(ExampleThermoContext* context) {\n    view_port_enabled_set(context->view_port, false);\n    gui_remove_view_port(context->gui, context->view_port);\n\n    onewire_host_free(context->onewire);\n    furi_thread_free(context->reader_thread);\n    furi_message_queue_free(context->event_queue);\n    view_port_free(context->view_port);\n\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_POWER);\n}\n\n/* The application's entry point. Execution starts from here. */\nint32_t example_thermo_main(void* p) {\n    UNUSED(p);\n\n    /* Allocate all of the necessary structures */\n    ExampleThermoContext* context = example_thermo_context_alloc();\n\n    /* Start the application's main loop. It won't return until the application was requested to exit. */\n    example_thermo_run(context);\n\n    /* Release all unneeded resources */\n    example_thermo_context_free(context);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_view_dispatcher/application.fam",
    "content": "App(\n    appid=\"example_view_dispatcher\",\n    name=\"Example: ViewDispatcher\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_view_dispatcher_app\",\n    requires=[\"gui\"],\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_view_dispatcher/example_view_dispatcher.c",
    "content": "/**\n * @file example_view_dispatcher.c\n * @brief Example application demonstrating the usage of the ViewDispatcher library.\n *\n * This application can display one of two views: either a Widget or a Submenu.\n * Each view has its own way of switching to another one:\n *\n * - A center button in the Widget view.\n * - A submenu item in the Submenu view\n *\n * Press either to switch to a different view. Press Back to exit the application.\n *\n */\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n\n#include <gui/modules/widget.h>\n#include <gui/modules/submenu.h>\n\n// Enumeration of the view indexes.\ntypedef enum {\n    ViewIndexWidget,\n    ViewIndexSubmenu,\n    ViewIndexCount,\n} ViewIndex;\n\n// Enumeration of submenu items.\ntypedef enum {\n    SubmenuIndexNothing,\n    SubmenuIndexSwitchView,\n} SubmenuIndex;\n\n// Main application structure.\ntypedef struct {\n    ViewDispatcher* view_dispatcher;\n    Widget* widget;\n    Submenu* submenu;\n} ExampleViewDispatcherApp;\n\n// This function is called when the user has pressed the Back key.\nstatic bool example_view_dispatcher_app_navigation_callback(void* context) {\n    furi_assert(context);\n    ExampleViewDispatcherApp* app = context;\n    // Back means exit the application, which can be done by stopping the ViewDispatcher.\n    view_dispatcher_stop(app->view_dispatcher);\n    return true;\n}\n\n// This function is called when there are custom events to process.\nstatic bool example_view_dispatcher_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    ExampleViewDispatcherApp* app = context;\n    // The event numerical value can mean different things (the application is responsible to uphold its chosen convention)\n    // In this example, the only possible meaning is the view index to switch to.\n    furi_assert(event < ViewIndexCount);\n    // Switch to the requested view.\n    view_dispatcher_switch_to_view(app->view_dispatcher, event);\n\n    return true;\n}\n\n// This function is called when the user presses the \"Switch View\" button on the Widget view.\nstatic void example_view_dispatcher_app_button_callback(\n    GuiButtonType button_type,\n    InputType input_type,\n    void* context) {\n    furi_assert(context);\n    ExampleViewDispatcherApp* app = context;\n    // Only request the view switch if the user short-presses the Center button.\n    if(button_type == GuiButtonTypeCenter && input_type == InputTypeShort) {\n        // Request switch to the Submenu view via the custom event queue.\n        view_dispatcher_send_custom_event(app->view_dispatcher, ViewIndexSubmenu);\n    }\n}\n\n// This function is called when the user activates the \"Switch View\" submenu item.\nstatic void example_view_dispatcher_app_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    ExampleViewDispatcherApp* app = context;\n    // Only request the view switch if the user activates the \"Switch View\" item.\n    if(index == SubmenuIndexSwitchView) {\n        // Request switch to the Widget view via the custom event queue.\n        view_dispatcher_send_custom_event(app->view_dispatcher, ViewIndexWidget);\n    }\n}\n\n// Application constructor function.\nstatic ExampleViewDispatcherApp* example_view_dispatcher_app_alloc() {\n    ExampleViewDispatcherApp* app = malloc(sizeof(ExampleViewDispatcherApp));\n    // Access the GUI API instance.\n    Gui* gui = furi_record_open(RECORD_GUI);\n    // Create and initialize the Widget view.\n    app->widget = widget_alloc();\n    widget_add_string_multiline_element(\n        app->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, \"Press the Button below\");\n    widget_add_button_element(\n        app->widget,\n        GuiButtonTypeCenter,\n        \"Switch View\",\n        example_view_dispatcher_app_button_callback,\n        app);\n    // Create and initialize the Submenu view.\n    app->submenu = submenu_alloc();\n    submenu_add_item(app->submenu, \"Do Nothing\", SubmenuIndexNothing, NULL, NULL);\n    submenu_add_item(\n        app->submenu,\n        \"Switch View\",\n        SubmenuIndexSwitchView,\n        example_view_dispatcher_app_submenu_callback,\n        app);\n    // Create the ViewDispatcher instance.\n    app->view_dispatcher = view_dispatcher_alloc();\n    // Let the GUI know about this ViewDispatcher instance.\n    view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);\n    // Register the views within the ViewDispatcher instance. This alone will not show any of them on the screen.\n    // Each view must have its own index to refer to it later (it is best done via an enumeration as shown here).\n    view_dispatcher_add_view(app->view_dispatcher, ViewIndexWidget, widget_get_view(app->widget));\n    view_dispatcher_add_view(\n        app->view_dispatcher, ViewIndexSubmenu, submenu_get_view(app->submenu));\n    // Set the custom event callback. It will be called each time a custom event is scheduled\n    // using the view_dispatcher_send_custom_callback() function.\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, example_view_dispatcher_app_custom_event_callback);\n    // Set the navigation, or back button callback. It will be called if the user pressed the Back button\n    // and the event was not handled in the currently displayed view.\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, example_view_dispatcher_app_navigation_callback);\n    // The context will be passed to the callbacks as a parameter, so we have access to our application object.\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    return app;\n}\n\n// Application destructor function.\nstatic void example_view_dispatcher_app_free(ExampleViewDispatcherApp* app) {\n    // All views must be un-registered (removed) from a ViewDispatcher instance\n    // before deleting it. Failure to do so will result in a crash.\n    view_dispatcher_remove_view(app->view_dispatcher, ViewIndexWidget);\n    view_dispatcher_remove_view(app->view_dispatcher, ViewIndexSubmenu);\n    // Now it is safe to delete the ViewDispatcher instance.\n    view_dispatcher_free(app->view_dispatcher);\n    // Delete the views\n    widget_free(app->widget);\n    submenu_free(app->submenu);\n    // End access to hte the GUI API.\n    furi_record_close(RECORD_GUI);\n    // Free the remaining memory.\n    free(app);\n}\n\nstatic void example_view_dispatcher_app_run(ExampleViewDispatcherApp* app) {\n    // Display the Widget view on the screen.\n    view_dispatcher_switch_to_view(app->view_dispatcher, ViewIndexWidget);\n    // This function will block until view_dispatcher_stop() is called.\n    // Internally, it uses a FuriEventLoop (see FuriEventLoop examples for more info on this).\n    view_dispatcher_run(app->view_dispatcher);\n}\n\n/*******************************************************************\n *                     vvv START HERE vvv\n *\n * The application's entry point - referenced in application.fam\n *******************************************************************/\nint32_t example_view_dispatcher_app(void* arg) {\n    UNUSED(arg);\n\n    ExampleViewDispatcherApp* app = example_view_dispatcher_app_alloc();\n    example_view_dispatcher_app_run(app);\n    example_view_dispatcher_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/examples/example_view_holder/application.fam",
    "content": "App(\n    appid=\"example_view_holder\",\n    name=\"Example: ViewHolder\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"example_view_holder_app\",\n    requires=[\"gui\"],\n    fap_category=\"Examples\",\n)\n"
  },
  {
    "path": "applications/examples/example_view_holder/example_view_holder.c",
    "content": "/**\n * @file example_view_holder.c\n * @brief Example application demonstrating the usage of the ViewHolder library.\n *\n * This application will display a text box with some scrollable text in it.\n * Press the Back key to exit the application.\n */\n\n#include <gui/gui.h>\n#include <gui/view_holder.h>\n#include <gui/modules/text_box.h>\n\n#include <api_lock.h>\n\n// This function will be called when the user presses the Back button.\nstatic void example_view_holder_back_callback(void* context) {\n    FuriApiLock exit_lock = context;\n    // Unlock the exit lock, thus enabling the app to exit.\n    api_lock_unlock(exit_lock);\n}\n\nint32_t example_view_holder_app(void* arg) {\n    UNUSED(arg);\n\n    // Access the GUI API instance.\n    Gui* gui = furi_record_open(RECORD_GUI);\n    // Create a TextBox view. The Gui object only accepts\n    // ViewPort instances, so we will need to address that later.\n    TextBox* text_box = text_box_alloc();\n    // Set some text so that the text box is not empty.\n    text_box_set_text(\n        text_box,\n        \"ViewHolder is being used\\n\"\n        \"to show this TextBox view.\\n\\n\"\n        \"Scroll down to see more.\\n\\n\\n\"\n        \"Press \\\"Back\\\" to exit.\");\n\n    // Create a ViewHolder instance. It will serve as an adapter to convert\n    // between the View type provided by the TextBox view and the ViewPort type\n    // that the GUI can actually display.\n    ViewHolder* view_holder = view_holder_alloc();\n    // Let the GUI know about this ViewHolder instance.\n    view_holder_attach_to_gui(view_holder, gui);\n    // Set the view that we want to display.\n    view_holder_set_view(view_holder, text_box_get_view(text_box));\n\n    // The part below is not really related to this example, but is necessary for it to function.\n    // We need to somehow stall the application thread so that the view stays on the screen (otherwise\n    // the app will just exit and won't display anything) and at the same time we need a way to quit out\n    // of the application.\n\n    // In this example, a simple FuriApiLock instance is used. A real-world application is likely to have some\n    // kind of event handling loop here instead. (see the ViewDispatcher example or one of FuriEventLoop\n    // examples for that).\n\n    // Create a pre-locked FuriApiLock instance.\n    FuriApiLock exit_lock = api_lock_alloc_locked();\n    // Set a Back event callback for the ViewHolder instance. It will be called when the user\n    // presses the Back button. We pass the exit lock instance as the context to be able to access\n    // it inside the callback function.\n    view_holder_set_back_callback(view_holder, example_view_holder_back_callback, exit_lock);\n\n    // This call will block the application thread from running until the exit lock gets unlocked somehow\n    // (the only way it can happen in this example is via the back callback).\n    api_lock_wait_unlock_and_free(exit_lock);\n\n    // The back key has been pressed, which unlocked the exit lock. The application is about to exit.\n\n    // The view must be removed from a ViewHolder instance before deleting it.\n    view_holder_set_view(view_holder, NULL);\n    // Delete everything to prevent memory leaks.\n    view_holder_free(view_holder);\n    text_box_free(text_box);\n    // End access to the GUI API.\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/application.fam",
    "content": "App(\n    appid=\"main_apps\",\n    name=\"Basic applications for main menu\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"gpio\",\n        \"ibutton\",\n        \"infrared\",\n        \"lfrfid\",\n        \"nfc\",\n        \"subghz\",\n        \"bad_usb\",\n        \"u2f\",\n        \"archive\",\n        \"main_apps_on_start\",\n    ],\n)\n\nApp(\n    appid=\"main_apps_on_start\",\n    name=\"On start hooks\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"cli\",\n    ],\n)\n"
  },
  {
    "path": "applications/main/archive/application.fam",
    "content": "App(\n    appid=\"archive\",\n    name=\"Archive\",\n    apptype=FlipperAppType.ARCHIVE,\n    entry_point=\"archive_app\",\n    cdefines=[\"APP_ARCHIVE\"],\n    requires=[\"gui\"],\n    stack_size=4 * 1024,\n    icon=\"A_FileManager_14\",\n    order=10,\n)\n"
  },
  {
    "path": "applications/main/archive/archive.c",
    "content": "#include \"archive_i.h\"\n\nbool archive_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    ArchiveApp* archive = (ArchiveApp*)context;\n    return scene_manager_handle_custom_event(archive->scene_manager, event);\n}\n\nbool archive_back_event_callback(void* context) {\n    furi_assert(context);\n    ArchiveApp* archive = (ArchiveApp*)context;\n    return scene_manager_handle_back_event(archive->scene_manager);\n}\n\nArchiveApp* archive_alloc(void) {\n    ArchiveApp* archive = malloc(sizeof(ArchiveApp));\n\n    archive->gui = furi_record_open(RECORD_GUI);\n    archive->loader = furi_record_open(RECORD_LOADER);\n    archive->text_input = text_input_alloc();\n    archive->fav_move_str = furi_string_alloc();\n\n    archive->view_dispatcher = view_dispatcher_alloc();\n    archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive);\n\n    view_dispatcher_attach_to_gui(\n        archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen);\n\n    view_dispatcher_set_event_callback_context(archive->view_dispatcher, archive);\n    view_dispatcher_set_custom_event_callback(\n        archive->view_dispatcher, archive_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        archive->view_dispatcher, archive_back_event_callback);\n\n    archive->browser = browser_alloc();\n\n    view_dispatcher_add_view(\n        archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser));\n\n    view_dispatcher_add_view(\n        archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));\n\n    archive->widget = widget_alloc();\n    view_dispatcher_add_view(\n        archive->view_dispatcher, ArchiveViewWidget, widget_get_view(archive->widget));\n\n    return archive;\n}\n\nvoid archive_free(ArchiveApp* archive) {\n    furi_assert(archive);\n\n    view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser);\n    view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);\n    view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewWidget);\n    widget_free(archive->widget);\n    view_dispatcher_free(archive->view_dispatcher);\n    scene_manager_free(archive->scene_manager);\n    browser_free(archive->browser);\n    furi_string_free(archive->fav_move_str);\n\n    text_input_free(archive->text_input);\n\n    furi_record_close(RECORD_LOADER);\n    archive->loader = NULL;\n    furi_record_close(RECORD_GUI);\n    archive->gui = NULL;\n\n    free(archive);\n}\n\nint32_t archive_app(void* p) {\n    UNUSED(p);\n    ArchiveApp* archive = archive_alloc();\n    scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);\n    view_dispatcher_run(archive->view_dispatcher);\n    archive_free(archive);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/archive/archive.h",
    "content": "#pragma once\n\ntypedef struct ArchiveApp ArchiveApp;\n"
  },
  {
    "path": "applications/main/archive/archive_i.h",
    "content": "#pragma once\n\n#include \"archive.h\"\n#include <stdint.h>\n#include <furi.h>\n#include <gui/gui_i.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/widget.h>\n#include <loader/loader.h>\n\n#include \"views/archive_browser_view.h\"\n#include \"scenes/archive_scene.h\"\n\ntypedef enum {\n    ArchiveViewBrowser,\n    ArchiveViewTextInput,\n    ArchiveViewWidget,\n    ArchiveViewTotal,\n} ArchiveViewEnum;\n\nstruct ArchiveApp {\n    Gui* gui;\n    Loader* loader;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    ArchiveBrowserView* browser;\n    TextInput* text_input;\n    Widget* widget;\n    FuriPubSubSubscription* loader_stop_subscription;\n    FuriString* fav_move_str;\n    char text_store[MAX_NAME_LEN];\n    char file_extension[MAX_EXT_LEN + 1];\n};\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_apps.c",
    "content": "#include \"archive_apps.h\"\n#include \"archive_browser.h\"\n\nstatic const char* const known_apps[] = {\n    [ArchiveAppTypeU2f] = \"u2f\",\n    [ArchiveAppTypeSetting] = \"setting\",\n};\n\nArchiveAppTypeEnum archive_get_app_type(const char* path) {\n    const char* app_name = strchr(path, ':');\n    if(app_name == NULL) {\n        return ArchiveAppTypeUnknown;\n    }\n    app_name++;\n\n    for(size_t i = 0; i < COUNT_OF(known_apps); i++) { //-V1008\n        if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) {\n            return i;\n        }\n    }\n    return ArchiveAppTypeUnknown;\n}\n\nbool archive_app_is_available(void* context, const char* path) {\n    UNUSED(context);\n    furi_assert(path);\n\n    ArchiveAppTypeEnum app = archive_get_app_type(path);\n\n    if(app == ArchiveAppTypeU2f) {\n        bool file_exists = false;\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n\n        if(storage_file_exists(storage, EXT_PATH(\"u2f/key.u2f\"))) {\n            file_exists = storage_file_exists(storage, EXT_PATH(\"u2f/cnt.u2f\"));\n        }\n\n        furi_record_close(RECORD_STORAGE);\n        return file_exists;\n    } else if(app == ArchiveAppTypeSetting) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool archive_app_read_dir(void* context, const char* path) {\n    furi_assert(context);\n    furi_assert(path);\n    ArchiveBrowserView* browser = context;\n\n    archive_file_array_rm_all(browser);\n\n    ArchiveAppTypeEnum app = archive_get_app_type(path);\n\n    if(app == ArchiveAppTypeU2f) {\n        archive_add_app_item(browser, \"/app:u2f/U2F Token\");\n        return true;\n    } else if(app == ArchiveAppTypeSetting) {\n        archive_add_app_item(browser, path);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid archive_app_delete_file(void* context, const char* path) {\n    furi_assert(context);\n    furi_assert(path);\n    ArchiveBrowserView* browser = context;\n\n    ArchiveAppTypeEnum app = archive_get_app_type(path);\n    bool res = false;\n\n    if(app == ArchiveAppTypeU2f) {\n        Storage* fs_api = furi_record_open(RECORD_STORAGE);\n        res = (storage_common_remove(fs_api, EXT_PATH(\"u2f/key.u2f\")) == FSE_OK);\n        res |= (storage_common_remove(fs_api, EXT_PATH(\"u2f/cnt.u2f\")) == FSE_OK);\n        furi_record_close(RECORD_STORAGE);\n\n        if(archive_is_favorite(\"/app:u2f/U2F Token\")) {\n            archive_favorites_delete(\"/app:u2f/U2F Token\");\n        }\n    } else if(app == ArchiveAppTypeSetting) {\n        // can't delete a setting!\n    }\n\n    if(res) {\n        archive_file_array_rm_selected(browser);\n    }\n}\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_apps.h",
    "content": "#pragma once\n\n#include \"archive_files.h\"\n\ntypedef enum {\n    ArchiveAppTypeU2f,\n    ArchiveAppTypeSetting,\n    ArchiveAppTypeUnknown,\n    ArchiveAppsTotal,\n} ArchiveAppTypeEnum;\n\nstatic const ArchiveFileTypeEnum app_file_types[] = {\n    [ArchiveAppTypeU2f] = ArchiveFileTypeU2f,\n    [ArchiveAppTypeSetting] = ArchiveFileTypeSetting,\n    [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown,\n};\n\nstatic inline ArchiveFileTypeEnum archive_get_app_filetype(ArchiveAppTypeEnum app) {\n    return app_file_types[app];\n}\n\nArchiveAppTypeEnum archive_get_app_type(const char* path);\nbool archive_app_is_available(void* context, const char* path);\nbool archive_app_read_dir(void* context, const char* path);\nvoid archive_app_delete_file(void* context, const char* path);\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_browser.c",
    "content": "#include \"archive_files.h\"\n#include \"archive_apps.h\"\n#include \"archive_browser.h\"\n#include \"../views/archive_browser_view.h\"\n\n#include <core/common_defines.h>\n#include <core/log.h>\n#include <gui/modules/file_browser_worker.h>\n#include <flipper_application/flipper_application.h>\n\nstatic void\n    archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = (ArchiveBrowserView*)context;\n\n    int32_t load_offset = 0;\n    browser->is_root = is_root;\n    ArchiveTabEnum tab = archive_get_tab(browser);\n\n    if((item_cnt == 0) && (archive_is_home(browser)) && (tab != ArchiveTabBrowser)) {\n        archive_switch_tab(browser, browser->last_tab_switch_dir);\n    } else if(!furi_string_start_with_str(browser->path, \"/app:\")) {\n        with_view_model(\n            browser->view,\n            ArchiveBrowserViewModel * model,\n            {\n                files_array_reset(model->files);\n                model->item_cnt = item_cnt;\n                model->item_idx = (file_idx > 0) ? file_idx : 0;\n                load_offset =\n                    CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0);\n                model->array_offset = 0;\n                model->list_offset = 0;\n                model->list_loading = true;\n                model->folder_loading = false;\n            },\n            false);\n        archive_update_offset(browser);\n\n        file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN);\n    }\n}\n\nstatic void archive_list_load_cb(void* context, uint32_t list_load_offset) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = (ArchiveBrowserView*)context;\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            files_array_reset(model->files);\n            model->array_offset = list_load_offset;\n        },\n        false);\n}\n\nstatic void\n    archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = (ArchiveBrowserView*)context;\n\n    if(!is_last) {\n        archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));\n    } else {\n        bool load_again = false;\n        with_view_model(\n            browser->view,\n            ArchiveBrowserViewModel * model,\n            {\n                model->list_loading = false;\n                if(archive_is_file_list_load_required(model)) {\n                    load_again = true;\n                }\n            },\n            true);\n        if(load_again) {\n            archive_file_array_load(browser, 0);\n        }\n    }\n}\n\nstatic void archive_long_load_cb(void* context) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = (ArchiveBrowserView*)context;\n\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { model->folder_loading = true; }, true);\n}\n\nstatic void archive_file_browser_set_path(\n    ArchiveBrowserView* browser,\n    FuriString* path,\n    const char* filter_ext,\n    bool skip_assets,\n    bool hide_dot_files) {\n    furi_assert(browser);\n    if(!browser->worker_running) {\n        browser->worker =\n            file_browser_worker_alloc(path, NULL, filter_ext, skip_assets, hide_dot_files);\n        file_browser_worker_set_callback_context(browser->worker, browser);\n        file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);\n        file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);\n        file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb);\n        file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb);\n        browser->worker_running = true;\n    } else {\n        furi_assert(browser->worker);\n        file_browser_worker_set_config(\n            browser->worker, path, filter_ext, skip_assets, hide_dot_files);\n    }\n}\n\nbool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) {\n    size_t array_size = files_array_size(model->files);\n\n    if((idx >= (uint32_t)model->array_offset + array_size) ||\n       (idx < (uint32_t)model->array_offset)) {\n        return false;\n    }\n\n    return true;\n}\n\nbool archive_is_file_list_load_required(ArchiveBrowserViewModel* model) {\n    size_t array_size = files_array_size(model->files);\n\n    if((model->list_loading) || (array_size >= model->item_cnt)) {\n        return false;\n    }\n\n    if((model->array_offset > 0) &&\n       (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) {\n        return true;\n    }\n\n    if(((model->array_offset + array_size) < model->item_cnt) &&\n       (model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) {\n        return true;\n    }\n\n    return false;\n}\n\nvoid archive_update_offset(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt;\n\n            if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) {\n                model->list_offset = model->item_idx - 3;\n            } else if(model->list_offset < model->item_idx - bounds) {\n                model->list_offset =\n                    CLAMP(model->item_idx - 2, (int32_t)model->item_cnt - bounds, 0);\n            } else if(model->list_offset > model->item_idx - bounds) {\n                model->list_offset =\n                    CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0);\n            }\n        },\n        true);\n}\n\nvoid archive_update_focus(ArchiveBrowserView* browser, const char* target) {\n    furi_assert(browser);\n    furi_assert(target);\n\n    archive_get_items(browser, furi_string_get_cstr(browser->path));\n\n    if(!archive_file_get_array_size(browser) && archive_is_home(browser)) {\n        archive_switch_tab(browser, TAB_RIGHT);\n    } else {\n        with_view_model(\n            browser->view,\n            ArchiveBrowserViewModel * model,\n            {\n                uint16_t idx = 0;\n                while(idx < files_array_size(model->files)) {\n                    ArchiveFile_t* current = files_array_get(model->files, idx);\n                    if(!furi_string_search(current->path, target)) {\n                        model->item_idx = idx + model->array_offset;\n                        break;\n                    }\n                    ++idx;\n                }\n            },\n            false);\n\n        archive_update_offset(browser);\n    }\n}\n\nsize_t archive_file_get_array_size(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    uint16_t size = 0;\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        { size = files_array_size(model->files); },\n        false);\n    return size;\n}\n\nvoid archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            model->item_cnt = count;\n            model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);\n        },\n        false);\n    archive_update_offset(browser);\n}\n\nvoid archive_file_array_rm_selected(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n    uint32_t items_cnt = 0;\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            files_array_remove_v(\n                model->files,\n                model->item_idx - model->array_offset,\n                model->item_idx - model->array_offset + 1);\n            model->item_cnt--;\n            model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);\n            items_cnt = model->item_cnt;\n        },\n        false);\n\n    if((items_cnt == 0) && (archive_is_home(browser))) {\n        archive_switch_tab(browser, TAB_RIGHT);\n    }\n\n    archive_update_offset(browser);\n}\n\nvoid archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            ArchiveFile_t temp;\n            size_t array_size = files_array_size(model->files) - 1;\n            uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u);\n\n            if(model->item_idx == 0 && dir < 0) {\n                ArchiveFile_t_init(&temp);\n                files_array_pop_at(&temp, model->files, array_size);\n                files_array_push_at(model->files, model->item_idx, temp);\n                ArchiveFile_t_clear(&temp);\n            } else if(((uint32_t)model->item_idx == array_size) && (dir > 0)) {\n                ArchiveFile_t_init(&temp);\n                files_array_pop_at(&temp, model->files, 0);\n                files_array_push_at(model->files, array_size, temp);\n                ArchiveFile_t_clear(&temp);\n            } else {\n                files_array_swap_at(model->files, model->item_idx, swap_idx);\n            }\n        },\n        false);\n}\n\nvoid archive_file_array_rm_all(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        { files_array_reset(model->files); },\n        false);\n}\n\nvoid archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {\n    furi_assert(browser);\n\n    int32_t offset_new = 0;\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            if(model->item_cnt > FILE_LIST_BUF_LEN) {\n                if(dir < 0) {\n                    offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3;\n                } else if(dir == 0) {\n                    offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 2;\n                } else {\n                    offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;\n                }\n                if(offset_new > 0) {\n                    offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);\n                } else {\n                    offset_new = 0;\n                }\n            }\n        },\n        false);\n\n    file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN);\n}\n\nArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    ArchiveFile_t* selected = NULL;\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            selected = files_array_size(model->files) ?\n                           files_array_get(model->files, model->item_idx - model->array_offset) :\n                           NULL;\n        },\n        false);\n    return selected;\n}\n\nArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) {\n    furi_assert(browser);\n\n    ArchiveFile_t* selected = NULL;\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u);\n            selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL;\n        },\n        false);\n    return selected;\n}\n\nArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    ArchiveTabEnum tab_id = 0;\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { tab_id = model->tab_idx; }, false);\n    return tab_id;\n}\n\nbool archive_is_home(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    if(browser->is_root) {\n        return true;\n    }\n\n    const char* default_path = archive_get_default_path(archive_get_tab(browser));\n    return furi_string_cmp_str(browser->path, default_path) == 0;\n}\n\nconst char* archive_get_name(ArchiveBrowserView* browser) {\n    ArchiveFile_t* selected = archive_get_current_file(browser);\n    return furi_string_get_cstr(selected->path);\n}\n\nvoid archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { model->tab_idx = tab; }, false);\n}\n\nvoid archive_add_app_item(ArchiveBrowserView* browser, const char* name) {\n    furi_assert(browser);\n    furi_assert(name);\n\n    ArchiveFile_t item;\n    ArchiveFile_t_init(&item);\n    furi_string_set(item.path, name);\n    archive_set_file_type(&item, name, false, true);\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            files_array_push_back(model->files, item);\n            model->item_cnt = files_array_size(model->files);\n        },\n        false);\n    ArchiveFile_t_clear(&item);\n}\n\nstatic bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool success = false;\n    if(flipper_application_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) {\n        success = true;\n    }\n    furi_record_close(RECORD_STORAGE);\n    return success;\n}\n\nvoid archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) {\n    furi_assert(browser);\n    furi_assert(name);\n\n    ArchiveFile_t item;\n    ArchiveFile_t_init(&item);\n\n    furi_string_set(item.path, name);\n    archive_set_file_type(&item, furi_string_get_cstr(browser->path), is_folder, false);\n    if(item.type == ArchiveFileTypeApplication) {\n        item.custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);\n        if(!archive_get_fap_meta(item.path, item.custom_name, &item.custom_icon_data)) {\n            free(item.custom_icon_data);\n            item.custom_icon_data = NULL;\n        }\n    }\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        { files_array_push_back(model->files, item); },\n        false);\n    ArchiveFile_t_clear(&item);\n}\n\nvoid archive_show_file_menu(ArchiveBrowserView* browser, bool show) {\n    furi_assert(browser);\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            if(show) {\n                if(archive_is_item_in_array(model, model->item_idx)) {\n                    model->menu = true;\n                    model->menu_idx = 0;\n                    ArchiveFile_t* selected =\n                        files_array_get(model->files, model->item_idx - model->array_offset);\n                    selected->fav =\n                        archive_is_favorite(\"%s\", furi_string_get_cstr(selected->path));\n                }\n            } else {\n                model->menu = false;\n                model->menu_idx = 0;\n            }\n        },\n        true);\n}\n\nvoid archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) {\n    furi_assert(browser);\n\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { model->move_fav = active; }, true);\n}\n\nstatic bool archive_is_dir_exists(FuriString* path) {\n    bool state = false;\n    FileInfo file_info;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    if(furi_string_equal(path, STORAGE_EXT_PATH_PREFIX)) {\n        state = storage_sd_status(storage) == FSE_OK;\n    } else if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {\n        state = file_info_is_dir(&file_info);\n    }\n    furi_record_close(RECORD_STORAGE);\n    return state;\n}\n\nvoid archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {\n    furi_assert(browser);\n    ArchiveTabEnum tab = archive_get_tab(browser);\n\n    browser->last_tab_switch_dir = key;\n\n    if(key == InputKeyLeft) {\n        tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;\n    } else {\n        tab = (tab + 1) % ArchiveTabTotal;\n    }\n\n    browser->is_root = true;\n    archive_set_tab(browser, tab);\n\n    furi_string_set(browser->path, archive_get_default_path(tab));\n    bool tab_empty = true;\n    if(tab == ArchiveTabFavorites) {\n        if(archive_favorites_count(browser) > 0) {\n            tab_empty = false;\n        }\n    } else if(furi_string_start_with_str(browser->path, \"/app:\")) {\n        char* app_name = strchr(furi_string_get_cstr(browser->path), ':');\n        if(app_name != NULL) {\n            if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) {\n                tab_empty = false;\n            }\n        }\n    } else {\n        tab = archive_get_tab(browser);\n        if(archive_is_dir_exists(browser->path)) {\n            bool skip_assets = (strcmp(archive_get_tab_ext(tab), \"*\") == 0) ? false : true;\n            // Hide dot files everywhere except Browser\n            bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), \"*\") == 0) ? false : true;\n            archive_file_browser_set_path(\n                browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files);\n            tab_empty = false; // Empty check will be performed later\n        }\n    }\n\n    if((tab_empty) && (tab != ArchiveTabBrowser)) {\n        archive_switch_tab(browser, key);\n    } else {\n        with_view_model(\n            browser->view,\n            ArchiveBrowserViewModel * model,\n            {\n                model->item_idx = 0;\n                model->array_offset = 0;\n            },\n            false);\n        archive_get_items(browser, furi_string_get_cstr(browser->path));\n        archive_update_offset(browser);\n    }\n}\n\nvoid archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) {\n    furi_assert(browser);\n    furi_assert(path);\n\n    int32_t idx_temp = 0;\n\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);\n\n    furi_string_set(browser->path, path);\n\n    file_browser_worker_folder_enter(browser->worker, path, idx_temp);\n}\n\nvoid archive_leave_dir(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    size_t dirname_start = furi_string_search_rchar(browser->path, '/');\n    furi_string_left(browser->path, dirname_start);\n\n    file_browser_worker_folder_exit(browser->worker);\n}\n\nvoid archive_refresh_dir(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    int32_t idx_temp = 0;\n\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);\n    file_browser_worker_folder_refresh(browser->worker, idx_temp);\n}\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_browser.h",
    "content": "#pragma once\n\n#include \"../archive_i.h\"\n#include <storage/storage.h>\n\n#define TAB_RIGHT         InputKeyRight // Default tab switch direction\n#define TAB_DEFAULT       ArchiveTabFavorites // Start tab\n#define FILE_LIST_BUF_LEN 50\n\nstatic const char* const tab_default_paths[] = {\n    [ArchiveTabFavorites] = \"/app:favorites\",\n    [ArchiveTabIButton] = EXT_PATH(\"ibutton\"),\n    [ArchiveTabNFC] = EXT_PATH(\"nfc\"),\n    [ArchiveTabSubGhz] = EXT_PATH(\"subghz\"),\n    [ArchiveTabLFRFID] = EXT_PATH(\"lfrfid\"),\n    [ArchiveTabInfrared] = EXT_PATH(\"infrared\"),\n    [ArchiveTabBadUsb] = EXT_PATH(\"badusb\"),\n    [ArchiveTabU2f] = \"/app:u2f\",\n    [ArchiveTabApplications] = EXT_PATH(\"apps\"),\n    [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX,\n};\n\nstatic const char* const known_ext[] = {\n    [ArchiveFileTypeIButton] = \".ibtn\",\n    [ArchiveFileTypeNFC] = \".nfc\",\n    [ArchiveFileTypeSubGhz] = \".sub\",\n    [ArchiveFileTypeLFRFID] = \".rfid\",\n    [ArchiveFileTypeInfrared] = \".ir\",\n    [ArchiveFileTypeBadUsb] = \".txt\",\n    [ArchiveFileTypeU2f] = \"?\",\n    [ArchiveFileTypeApplication] = \".fap\",\n    [ArchiveFileTypeJS] = \".js\",\n    [ArchiveFileTypeUpdateManifest] = \".fuf\",\n    [ArchiveFileTypeFolder] = \"?\",\n    [ArchiveFileTypeUnknown] = \"*\",\n    [ArchiveFileTypeAppOrJs] = \".fap|.js\",\n    [ArchiveFileTypeSetting] = \"?\",\n};\n\nstatic const ArchiveFileTypeEnum known_type[] = {\n    [ArchiveTabFavorites] = ArchiveFileTypeUnknown,\n    [ArchiveTabIButton] = ArchiveFileTypeIButton,\n    [ArchiveTabNFC] = ArchiveFileTypeNFC,\n    [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz,\n    [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID,\n    [ArchiveTabInfrared] = ArchiveFileTypeInfrared,\n    [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,\n    [ArchiveTabU2f] = ArchiveFileTypeU2f,\n    [ArchiveTabApplications] = ArchiveFileTypeAppOrJs,\n    [ArchiveTabBrowser] = ArchiveFileTypeUnknown,\n};\n\nstatic inline ArchiveFileTypeEnum archive_get_tab_filetype(ArchiveTabEnum tab) {\n    return known_type[tab];\n}\n\nstatic inline const char* archive_get_tab_ext(ArchiveTabEnum tab) {\n    return known_ext[archive_get_tab_filetype(tab)];\n}\n\nstatic inline const char* archive_get_default_path(ArchiveTabEnum tab) {\n    return tab_default_paths[tab];\n}\n\ninline bool archive_is_known_app(ArchiveFileTypeEnum type) {\n    return type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown;\n}\n\nbool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx);\nbool archive_is_file_list_load_required(ArchiveBrowserViewModel* model);\nvoid archive_update_offset(ArchiveBrowserView* browser);\nvoid archive_update_focus(ArchiveBrowserView* browser, const char* target);\n\nvoid archive_file_array_load(ArchiveBrowserView* browser, int8_t dir);\nsize_t archive_file_get_array_size(ArchiveBrowserView* browser);\nvoid archive_file_array_rm_selected(ArchiveBrowserView* browser);\nvoid archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir);\nvoid archive_file_array_rm_all(ArchiveBrowserView* browser);\n\nvoid archive_set_item_count(ArchiveBrowserView* browser, uint32_t count);\n\nArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser);\nArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx);\nArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser);\nbool archive_is_home(ArchiveBrowserView* browser);\nconst char* archive_get_name(ArchiveBrowserView* browser);\n\nvoid archive_add_app_item(ArchiveBrowserView* browser, const char* name);\nvoid archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name);\nvoid archive_show_file_menu(ArchiveBrowserView* browser, bool show);\nvoid archive_favorites_move_mode(ArchiveBrowserView* browser, bool active);\n\nvoid archive_switch_tab(ArchiveBrowserView* browser, InputKey key);\nvoid archive_enter_dir(ArchiveBrowserView* browser, FuriString* name);\nvoid archive_leave_dir(ArchiveBrowserView* browser);\nvoid archive_refresh_dir(ArchiveBrowserView* browser);\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_favorites.c",
    "content": "#include \"archive_favorites.h\"\n#include \"archive_files.h\"\n#include \"archive_apps.h\"\n#include \"archive_browser.h\"\n\n#include <dialogs/dialogs.h>\n\n#define ARCHIVE_FAV_FILE_BUF_LEN 32\n\nstatic bool archive_favorites_read_line(File* file, FuriString* str_result) {\n    furi_string_reset(str_result);\n    uint8_t buffer[ARCHIVE_FAV_FILE_BUF_LEN];\n    bool result = false;\n\n    do {\n        size_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN);\n        if(storage_file_get_error(file) != FSE_OK) {\n            return false;\n        }\n\n        for(size_t i = 0; i < read_count; i++) {\n            if(buffer[i] == '\\n') {\n                uint32_t position = storage_file_tell(file);\n                if(storage_file_get_error(file) != FSE_OK) {\n                    return false;\n                }\n\n                position = position - read_count + i + 1;\n\n                storage_file_seek(file, position, true);\n                if(storage_file_get_error(file) != FSE_OK) {\n                    return false;\n                }\n\n                result = true;\n                break;\n            } else {\n                furi_string_push_back(str_result, buffer[i]);\n            }\n        }\n\n        if(result || read_count == 0) {\n            break;\n        }\n    } while(true);\n\n    return result;\n}\n\nuint16_t archive_favorites_count(void* context) {\n    furi_assert(context);\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    FuriString* buffer;\n    buffer = furi_string_alloc();\n\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n    uint16_t lines = 0;\n\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue; // Skip empty lines\n            }\n            ++lines;\n        }\n    }\n\n    storage_file_close(file);\n\n    furi_string_free(buffer);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return lines;\n}\n\nstatic bool archive_favourites_rescan(void) {\n    FuriString* buffer;\n    buffer = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue;\n            }\n\n            if(furi_string_search(buffer, \"/app:\") == 0) {\n                if(archive_app_is_available(NULL, furi_string_get_cstr(buffer))) {\n                    archive_file_append(\n                        ARCHIVE_FAV_TEMP_PATH, \"%s\\n\", furi_string_get_cstr(buffer));\n                }\n            } else {\n                if(storage_file_exists(storage, furi_string_get_cstr(buffer))) {\n                    archive_file_append(\n                        ARCHIVE_FAV_TEMP_PATH, \"%s\\n\", furi_string_get_cstr(buffer));\n                }\n            }\n        }\n    }\n\n    furi_string_free(buffer);\n\n    storage_file_close(file);\n    storage_common_remove(storage, ARCHIVE_FAV_PATH);\n    storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);\n    storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nbool archive_favorites_read(void* context) {\n    furi_assert(context);\n\n    ArchiveBrowserView* browser = context;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    FuriString* buffer;\n    FileInfo file_info;\n    buffer = furi_string_alloc();\n\n    bool need_refresh = false;\n    uint16_t file_count = 0;\n\n    archive_file_array_rm_all(browser);\n\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue;\n            }\n\n            if(furi_string_search(buffer, \"/app:\") == 0) {\n                if(archive_app_is_available(browser, furi_string_get_cstr(buffer))) {\n                    archive_add_app_item(browser, furi_string_get_cstr(buffer));\n                    file_count++;\n                } else {\n                    need_refresh = true;\n                }\n            } else {\n                if(storage_file_exists(storage, furi_string_get_cstr(buffer))) {\n                    storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info);\n                    archive_add_file_item(\n                        browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer));\n                    file_count++;\n                } else {\n                    need_refresh = true;\n                }\n            }\n\n            furi_string_reset(buffer);\n        }\n    }\n    storage_file_close(file);\n    furi_string_free(buffer);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    archive_set_item_count(browser, file_count);\n\n    if(need_refresh) { //-V547\n        archive_favourites_rescan();\n    }\n\n    return result;\n}\n\nbool archive_favorites_delete(const char* format, ...) {\n    FuriString* buffer;\n    FuriString* filename;\n    va_list args;\n    va_start(args, format);\n    filename = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n\n    buffer = furi_string_alloc();\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue;\n            }\n\n            if(furi_string_search(buffer, filename)) {\n                archive_file_append(ARCHIVE_FAV_TEMP_PATH, \"%s\\n\", furi_string_get_cstr(buffer));\n            }\n        }\n    }\n\n    furi_string_free(buffer);\n    furi_string_free(filename);\n\n    storage_file_close(file);\n    storage_common_remove(fs_api, ARCHIVE_FAV_PATH);\n    storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);\n    storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nbool archive_is_favorite(const char* format, ...) {\n    FuriString* buffer;\n    FuriString* filename;\n    va_list args;\n    va_start(args, format);\n    filename = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n\n    buffer = furi_string_alloc();\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    bool found = false;\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue;\n            }\n            if(!furi_string_search(buffer, filename)) {\n                found = true;\n                break;\n            }\n        }\n    }\n\n    storage_file_close(file);\n    furi_string_free(buffer);\n    furi_string_free(filename);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return found;\n}\n\nbool archive_favorites_rename(const char* src, const char* dst) {\n    furi_assert(src);\n    furi_assert(dst);\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    FuriString* path;\n    FuriString* buffer;\n\n    buffer = furi_string_alloc();\n    path = furi_string_alloc();\n\n    furi_string_printf(path, \"%s\", src);\n    bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(result) {\n        while(1) {\n            if(!archive_favorites_read_line(file, buffer)) {\n                break;\n            }\n            if(!furi_string_size(buffer)) {\n                continue;\n            }\n\n            archive_file_append(\n                ARCHIVE_FAV_TEMP_PATH,\n                \"%s\\n\",\n                furi_string_search(buffer, path) ? furi_string_get_cstr(buffer) : dst);\n        }\n    }\n\n    furi_string_free(buffer);\n    furi_string_free(path);\n\n    storage_file_close(file);\n    storage_common_remove(fs_api, ARCHIVE_FAV_PATH);\n    storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);\n    storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nvoid archive_add_to_favorites(const char* file_path) {\n    furi_assert(file_path);\n\n    archive_file_append(ARCHIVE_FAV_PATH, \"%s\\n\", file_path);\n}\n\nvoid archive_favorites_save(void* context) {\n    furi_assert(context);\n\n    ArchiveBrowserView* browser = context;\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    for(size_t i = 0; i < archive_file_get_array_size(browser); i++) {\n        ArchiveFile_t* item = archive_get_file_at(browser, i);\n        archive_file_append(ARCHIVE_FAV_TEMP_PATH, \"%s\\n\", furi_string_get_cstr(item->path));\n    }\n\n    storage_common_remove(fs_api, ARCHIVE_FAV_PATH);\n    storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);\n    storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nvoid archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting) {\n    DialogMessage* message = dialog_message_alloc();\n\n    FuriString* setting_path = furi_string_alloc_set_str(app_name);\n    if(setting) {\n        furi_string_push_back(setting_path, '/');\n        furi_string_cat_str(setting_path, setting);\n    }\n    const char* setting_path_str = furi_string_get_cstr(setting_path);\n\n    bool is_favorite = archive_is_favorite(\"/app:setting/%s\", setting_path_str);\n    dialog_message_set_header(\n        message,\n        is_favorite ? \"Unpin This Setting?\" : \"Pin This Setting?\",\n        64,\n        0,\n        AlignCenter,\n        AlignTop);\n    dialog_message_set_text(\n        message,\n        is_favorite ? \"It will no longer be\\naccessible from the\\nFavorites menu\" :\n                      \"It will be accessible from the\\nFavorites menu\",\n        64,\n        32,\n        AlignCenter,\n        AlignCenter);\n    dialog_message_set_buttons(\n        message, is_favorite ? \"Unpin\" : \"Go back\", NULL, is_favorite ? \"Keep pinned\" : \"Pin\");\n\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n    DialogMessageButton button = dialog_message_show(dialogs, message);\n    furi_record_close(RECORD_DIALOGS);\n\n    if(is_favorite && button == DialogMessageButtonLeft) {\n        archive_favorites_delete(\"/app:setting/%s\", setting_path_str);\n    } else if(!is_favorite && button == DialogMessageButtonRight) {\n        archive_file_append(ARCHIVE_FAV_PATH, \"/app:setting/%s\\n\", setting_path_str);\n    }\n\n    furi_string_free(setting_path);\n    dialog_message_free(message);\n}\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_favorites.h",
    "content": "#pragma once\n\n#include <storage/storage.h>\n\n#define ARCHIVE_FAV_PATH      EXT_PATH(\"favorites.txt\")\n#define ARCHIVE_FAV_TEMP_PATH EXT_PATH(\"favorites.tmp\")\n\nuint16_t archive_favorites_count(void* context);\nbool archive_favorites_read(void* context);\nbool archive_favorites_delete(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));\nbool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));\nbool archive_favorites_rename(const char* src, const char* dst);\nvoid archive_add_to_favorites(const char* file_path);\nvoid archive_favorites_save(void* context);\n\n/**\n * Intended to be called by settings apps to handle long presses, as well as\n * internally from within the archive\n * \n * @param app_name name of the referring application\n * @param setting  name of the setting, which will be both displayed to the user\n *                 and passed to the application as an argument upon recall\n */\nvoid archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting);\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_files.c",
    "content": "#include \"archive_files.h\"\n#include \"archive_apps.h\"\n#include \"archive_browser.h\"\n\n#define TAG \"Archive\"\n\n#define ASSETS_DIR \"assets\"\n\nvoid archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app) {\n    furi_assert(file);\n\n    file->is_app = is_app;\n    if(is_app) {\n        file->type = archive_get_app_filetype(archive_get_app_type(path));\n    } else {\n        for(size_t i = 0; i < COUNT_OF(known_ext); i++) {\n            if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue;\n            if(furi_string_end_withi(file->path, known_ext[i])) {\n                if(i == ArchiveFileTypeBadUsb) {\n                    if(furi_string_search(\n                           file->path, archive_get_default_path(ArchiveTabBadUsb)) == 0) {\n                        file->type = i;\n                        return; // *.txt file is a BadUSB script only if it is in BadUSB folder\n                    }\n                } else {\n                    file->type = i;\n                    return;\n                }\n            }\n        }\n\n        if(is_folder) {\n            file->type = ArchiveFileTypeFolder;\n        } else {\n            file->type = ArchiveFileTypeUnknown;\n        }\n    }\n}\n\nbool archive_get_items(void* context, const char* path) {\n    furi_assert(context);\n\n    bool res = false;\n    ArchiveBrowserView* browser = context;\n\n    if(archive_get_tab(browser) == ArchiveTabFavorites) {\n        res = archive_favorites_read(browser);\n    } else if(strncmp(path, \"/app:\", 5) == 0) {\n        res = archive_app_read_dir(browser, path);\n    }\n    return res;\n}\n\nvoid archive_file_append(const char* path, const char* format, ...) {\n    furi_assert(path);\n\n    FuriString* string;\n    va_list args;\n    va_start(args, format);\n    string = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    bool res = storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND);\n\n    if(res) {\n        storage_file_write(file, furi_string_get_cstr(string), furi_string_size(string));\n    }\n\n    furi_string_free(string);\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nvoid archive_delete_file(void* context, const char* format, ...) {\n    furi_assert(context);\n\n    FuriString* filename;\n    va_list args;\n    va_start(args, format);\n    filename = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n\n    ArchiveBrowserView* browser = context;\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n\n    FileInfo fileinfo;\n    storage_common_stat(fs_api, furi_string_get_cstr(filename), &fileinfo);\n\n    bool res = false;\n\n    if(file_info_is_dir(&fileinfo)) {\n        res = storage_simply_remove_recursive(fs_api, furi_string_get_cstr(filename));\n    } else {\n        res = (storage_common_remove(fs_api, furi_string_get_cstr(filename)) == FSE_OK);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n\n    if(archive_is_favorite(\"%s\", furi_string_get_cstr(filename))) {\n        archive_favorites_delete(\"%s\", furi_string_get_cstr(filename));\n    }\n\n    if(res) {\n        archive_file_array_rm_selected(browser);\n    }\n\n    furi_string_free(filename);\n}\n"
  },
  {
    "path": "applications/main/archive/helpers/archive_files.h",
    "content": "#pragma once\n\n#include <m-array.h>\n#include <furi.h>\n#include <storage/storage.h>\n\n#define FAP_MANIFEST_MAX_ICON_SIZE 32\n\ntypedef enum {\n    ArchiveFileTypeIButton,\n    ArchiveFileTypeNFC,\n    ArchiveFileTypeSubGhz,\n    ArchiveFileTypeLFRFID,\n    ArchiveFileTypeInfrared,\n    ArchiveFileTypeBadUsb,\n    ArchiveFileTypeU2f,\n    ArchiveFileTypeUpdateManifest,\n    ArchiveFileTypeApplication,\n    ArchiveFileTypeJS,\n    ArchiveFileTypeFolder,\n    ArchiveFileTypeUnknown,\n    ArchiveFileTypeAppOrJs,\n    ArchiveFileTypeSetting,\n    ArchiveFileTypeLoading,\n} ArchiveFileTypeEnum;\n\ntypedef struct {\n    FuriString* path;\n    ArchiveFileTypeEnum type;\n    uint8_t* custom_icon_data;\n    FuriString* custom_name;\n    bool fav;\n    bool is_app;\n} ArchiveFile_t;\n\nstatic void ArchiveFile_t_init(ArchiveFile_t* obj) {\n    obj->path = furi_string_alloc();\n    obj->type = ArchiveFileTypeUnknown;\n    obj->custom_icon_data = NULL;\n    obj->custom_name = furi_string_alloc();\n    obj->fav = false;\n    obj->is_app = false;\n}\n\nstatic void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {\n    obj->path = furi_string_alloc_set(src->path);\n    obj->type = src->type;\n    if(src->custom_icon_data) {\n        obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);\n        memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE);\n    } else {\n        obj->custom_icon_data = NULL;\n    }\n    obj->custom_name = furi_string_alloc_set(src->custom_name);\n    obj->fav = src->fav;\n    obj->is_app = src->is_app;\n}\n\nstatic void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {\n    furi_string_set(obj->path, src->path);\n    obj->type = src->type;\n    if(src->custom_icon_data) {\n        obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);\n        memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE);\n    } else {\n        obj->custom_icon_data = NULL;\n    }\n    furi_string_set(obj->custom_name, src->custom_name);\n    obj->fav = src->fav;\n    obj->is_app = src->is_app;\n}\n\nstatic void ArchiveFile_t_clear(ArchiveFile_t* obj) {\n    furi_string_free(obj->path);\n    if(obj->custom_icon_data) {\n        free(obj->custom_icon_data);\n        obj->custom_icon_data = NULL;\n    }\n    furi_string_free(obj->custom_name);\n}\n\nARRAY_DEF(\n    files_array,\n    ArchiveFile_t,\n    (INIT(API_2(ArchiveFile_t_init)),\n     SET(API_6(ArchiveFile_t_set)),\n     INIT_SET(API_6(ArchiveFile_t_init_set)),\n     CLEAR(API_2(ArchiveFile_t_clear))))\n\nvoid archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app);\nbool archive_get_items(void* context, const char* path);\nvoid archive_file_append(const char* path, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\nvoid archive_delete_file(void* context, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene.c",
    "content": "#include \"archive_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const archive_on_enter_handlers[])(void*) = {\n#include \"archive_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const archive_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"archive_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const archive_on_exit_handlers[])(void* context) = {\n#include \"archive_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers archive_scene_handlers = {\n    .on_enter_handlers = archive_on_enter_handlers,\n    .on_event_handlers = archive_on_event_handlers,\n    .on_exit_handlers = archive_on_exit_handlers,\n    .scene_num = ArchiveAppSceneNum,\n};\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) ArchiveAppScene##id,\ntypedef enum {\n#include \"archive_scene_config.h\"\n    ArchiveAppSceneNum,\n} ArchiveAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers archive_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"archive_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"archive_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"archive_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene_browser.c",
    "content": "#include \"../archive_i.h\"\n#include \"../helpers/archive_files.h\"\n#include \"../helpers/archive_favorites.h\"\n#include \"../helpers/archive_browser.h\"\n#include \"../views/archive_browser_view.h\"\n#include \"archive/scenes/archive_scene.h\"\n#include <applications.h>\n\n#define TAG \"ArchiveSceneBrowser\"\n\n#define SCENE_STATE_DEFAULT      (0)\n#define SCENE_STATE_NEED_REFRESH (1)\n\nstatic const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) {\n    switch(file_type) {\n    case ArchiveFileTypeIButton:\n        return \"iButton\";\n    case ArchiveFileTypeNFC:\n        return \"NFC\";\n    case ArchiveFileTypeSubGhz:\n        return \"Sub-GHz\";\n    case ArchiveFileTypeLFRFID:\n        return \"125 kHz RFID\";\n    case ArchiveFileTypeInfrared:\n        return \"Infrared\";\n    case ArchiveFileTypeBadUsb:\n        return \"Bad USB\";\n    case ArchiveFileTypeU2f:\n        return \"U2F\";\n    case ArchiveFileTypeUpdateManifest:\n        return \"UpdaterApp\";\n    case ArchiveFileTypeJS:\n        return \"JS Runner\";\n    default:\n        return NULL;\n    }\n}\n\nstatic void archive_loader_callback(const void* message, void* context) {\n    furi_assert(message);\n    furi_assert(context);\n    const LoaderEvent* event = message;\n    ArchiveApp* archive = (ArchiveApp*)context;\n\n    if(event->type == LoaderEventTypeNoMoreAppsInQueue) {\n        view_dispatcher_send_custom_event(\n            archive->view_dispatcher, ArchiveBrowserEventListRefresh);\n    }\n}\n\nstatic void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {\n    UNUSED(browser);\n    Loader* loader = furi_record_open(RECORD_LOADER);\n\n    if(selected->type == ArchiveFileTypeSetting) {\n        FuriString* app_name = furi_string_alloc_set(selected->path);\n        furi_string_right(app_name, furi_string_search_char(app_name, '/', 1) + 1);\n        size_t slash = furi_string_search_char(app_name, '/', 1);\n        if(slash != FURI_STRING_FAILURE) {\n            furi_string_left(app_name, slash);\n            FuriString* app_args =\n                furi_string_alloc_set_str(furi_string_get_cstr(app_name) + slash + 1);\n            loader_start_with_gui_error(\n                loader, furi_string_get_cstr(app_name), furi_string_get_cstr(app_args));\n            furi_string_free(app_args);\n        } else {\n            loader_start_with_gui_error(loader, furi_string_get_cstr(app_name), NULL);\n        }\n        furi_string_free(app_name);\n    } else {\n        const char* app_name = archive_get_flipper_app_name(selected->type);\n        if(app_name) {\n            if(selected->is_app) {\n                char* param = strrchr(furi_string_get_cstr(selected->path), '/');\n                if(param != NULL) {\n                    param++;\n                }\n                loader_start_with_gui_error(loader, app_name, param);\n            } else {\n                loader_start_with_gui_error(\n                    loader, app_name, furi_string_get_cstr(selected->path));\n            }\n        } else {\n            loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL);\n        }\n    }\n\n    furi_record_close(RECORD_LOADER);\n}\n\nvoid archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    view_dispatcher_send_custom_event(archive->view_dispatcher, event);\n}\n\nvoid archive_scene_browser_on_enter(void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    ArchiveBrowserView* browser = archive->browser;\n    browser->is_root = true;\n\n    archive_browser_set_callback(browser, archive_scene_browser_callback, archive);\n    archive_update_focus(browser, archive->text_store);\n    view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);\n\n    archive->loader_stop_subscription = furi_pubsub_subscribe(\n        loader_get_pubsub(archive->loader), archive_loader_callback, archive);\n\n    uint32_t state = scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneBrowser);\n\n    if(state == SCENE_STATE_NEED_REFRESH) {\n        view_dispatcher_send_custom_event(\n            archive->view_dispatcher, ArchiveBrowserEventListRefresh);\n    }\n\n    scene_manager_set_scene_state(\n        archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT);\n}\n\nbool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    ArchiveBrowserView* browser = archive->browser;\n    ArchiveFile_t* selected = archive_get_current_file(browser);\n\n    bool favorites = archive_get_tab(browser) == ArchiveTabFavorites;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case ArchiveBrowserEventFileMenuOpen:\n            archive_show_file_menu(browser, true);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFileMenuClose:\n            archive_show_file_menu(browser, false);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFileMenuRun:\n            if(archive_is_known_app(selected->type)) {\n                archive_run_in_app(browser, selected);\n                archive_show_file_menu(browser, false);\n            }\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFileMenuPin: {\n            const char* name = archive_get_name(browser);\n            if(favorites) {\n                archive_favorites_delete(\"%s\", name);\n                archive_file_array_rm_selected(browser);\n                archive_show_file_menu(browser, false);\n            } else if(archive_is_known_app(selected->type)) {\n                if(archive_is_favorite(\"%s\", name)) {\n                    archive_favorites_delete(\"%s\", name);\n                } else {\n                    archive_file_append(ARCHIVE_FAV_PATH, \"%s\\n\", name);\n                }\n                archive_show_file_menu(browser, false);\n            }\n            consumed = true;\n        } break;\n\n        case ArchiveBrowserEventFileMenuRename:\n            if(favorites) {\n                browser->callback(ArchiveBrowserEventEnterFavMove, browser->context);\n            } else if(selected->is_app == false) {\n                archive_show_file_menu(browser, false);\n                scene_manager_set_scene_state(\n                    archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH);\n                scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);\n            }\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFileMenuDelete:\n            if(archive_get_tab(browser) != ArchiveTabFavorites) {\n                archive_show_file_menu(browser, false);\n                scene_manager_set_scene_state(\n                    archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH);\n                scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);\n            }\n            consumed = true;\n            break;\n        case ArchiveBrowserEventEnterDir:\n            archive_enter_dir(browser, selected->path);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFavMoveUp:\n            archive_file_array_swap(browser, 1);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventFavMoveDown:\n            archive_file_array_swap(browser, -1);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventEnterFavMove:\n            furi_string_set(archive->fav_move_str, selected->path);\n            archive_show_file_menu(browser, false);\n            archive_favorites_move_mode(archive->browser, true);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventExitFavMove:\n            archive_update_focus(browser, furi_string_get_cstr(archive->fav_move_str));\n            archive_favorites_move_mode(archive->browser, false);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventSaveFavMove:\n            archive_favorites_move_mode(archive->browser, false);\n            archive_favorites_save(archive->browser);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventLoadPrevItems:\n            archive_file_array_load(archive->browser, -1);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventLoadNextItems:\n            archive_file_array_load(archive->browser, 1);\n            consumed = true;\n            break;\n        case ArchiveBrowserEventListRefresh:\n            if(!favorites) {\n                archive_refresh_dir(browser);\n            } else {\n                archive_favorites_read(browser);\n            }\n            consumed = true;\n            break;\n\n        case ArchiveBrowserEventExit:\n            if(!archive_is_home(browser)) {\n                archive_leave_dir(browser);\n            } else {\n                if(archive->loader_stop_subscription) {\n                    furi_pubsub_unsubscribe(\n                        loader_get_pubsub(archive->loader), archive->loader_stop_subscription);\n                    archive->loader_stop_subscription = NULL;\n                }\n\n                view_dispatcher_stop(archive->view_dispatcher);\n            }\n            consumed = true;\n            break;\n\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid archive_scene_browser_on_exit(void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    if(archive->loader_stop_subscription) {\n        furi_pubsub_unsubscribe(\n            loader_get_pubsub(archive->loader), archive->loader_stop_subscription);\n        archive->loader_stop_subscription = NULL;\n    }\n}\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene_config.h",
    "content": "ADD_SCENE(archive, browser, Browser)\nADD_SCENE(archive, rename, Rename)\nADD_SCENE(archive, delete, Delete)\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene_delete.c",
    "content": "#include \"../archive_i.h\"\n#include \"../helpers/archive_files.h\"\n#include \"../helpers/archive_apps.h\"\n#include \"../helpers/archive_browser.h\"\n#include \"toolbox/path.h\"\n\n#define SCENE_DELETE_CUSTOM_EVENT (0UL)\n#define MAX_TEXT_INPUT_LEN        22\n\nvoid archive_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    ArchiveApp* app = (ArchiveApp*)context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, result);\n    }\n}\n\nvoid archive_scene_delete_on_enter(void* context) {\n    furi_assert(context);\n    ArchiveApp* app = (ArchiveApp*)context;\n\n    widget_add_button_element(\n        app->widget, GuiButtonTypeLeft, \"Cancel\", archive_scene_delete_widget_callback, app);\n    widget_add_button_element(\n        app->widget, GuiButtonTypeRight, \"Delete\", archive_scene_delete_widget_callback, app);\n\n    FuriString* filename;\n    filename = furi_string_alloc();\n\n    ArchiveFile_t* current = archive_get_current_file(app->browser);\n\n    FuriString* filename_no_ext = furi_string_alloc();\n    path_extract_filename(current->path, filename_no_ext, true);\n    strlcpy(app->text_store, furi_string_get_cstr(filename_no_ext), MAX_NAME_LEN);\n    furi_string_free(filename_no_ext);\n\n    path_extract_filename(current->path, filename, false);\n\n    char delete_str[64];\n    snprintf(delete_str, sizeof(delete_str), \"\\e#Delete %s?\\e#\", furi_string_get_cstr(filename));\n    widget_add_text_box_element(\n        app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false);\n\n    furi_string_free(filename);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget);\n}\n\nbool archive_scene_delete_on_event(void* context, SceneManagerEvent event) {\n    furi_assert(context);\n    ArchiveApp* app = (ArchiveApp*)context;\n\n    ArchiveBrowserView* browser = app->browser;\n    ArchiveFile_t* selected = archive_get_current_file(browser);\n    const char* name = archive_get_name(browser);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            if(selected->is_app) {\n                archive_app_delete_file(browser, name);\n            } else {\n                archive_delete_file(browser, \"%s\", name);\n            }\n            archive_show_file_menu(browser, false);\n            return scene_manager_previous_scene(app->scene_manager);\n        } else if(event.event == GuiButtonTypeLeft) {\n            return scene_manager_previous_scene(app->scene_manager);\n        }\n    }\n    return false;\n}\n\nvoid archive_scene_delete_on_exit(void* context) {\n    furi_assert(context);\n    ArchiveApp* app = (ArchiveApp*)context;\n\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/archive/scenes/archive_scene_rename.c",
    "content": "#include \"../archive_i.h\"\n#include \"../helpers/archive_favorites.h\"\n#include \"../helpers/archive_files.h\"\n#include \"../helpers/archive_browser.h\"\n#include \"archive/views/archive_browser_view.h\"\n#include \"toolbox/path.h\"\n\n#define SCENE_RENAME_CUSTOM_EVENT (0UL)\n#define MAX_TEXT_INPUT_LEN        22\n\nvoid archive_scene_rename_text_input_callback(void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT);\n}\n\nvoid archive_scene_rename_on_enter(void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n\n    TextInput* text_input = archive->text_input;\n    ArchiveFile_t* current = archive_get_current_file(archive->browser);\n    const bool is_file = current->type != ArchiveFileTypeFolder;\n\n    FuriString* filename;\n    filename = furi_string_alloc();\n    path_extract_filename(current->path, filename, is_file);\n    strlcpy(archive->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN);\n\n    if(is_file) {\n        path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN);\n    } else {\n        memset(archive->file_extension, 0, sizeof(archive->file_extension));\n    }\n\n    text_input_set_header_text(text_input, \"Rename:\");\n\n    text_input_set_result_callback(\n        text_input,\n        archive_scene_rename_text_input_callback,\n        archive,\n        archive->text_store,\n        MAX_TEXT_INPUT_LEN,\n        false);\n\n    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n        furi_string_get_cstr(archive->browser->path), archive->file_extension, \"\");\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    furi_string_free(filename);\n\n    view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);\n}\n\nbool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SCENE_RENAME_CUSTOM_EVENT) {\n            Storage* fs_api = furi_record_open(RECORD_STORAGE);\n\n            const char* path_src = archive_get_name(archive->browser);\n            ArchiveFile_t* file = archive_get_current_file(archive->browser);\n\n            FuriString* path_dst;\n\n            path_dst = furi_string_alloc();\n            path_extract_dirname(path_src, path_dst);\n            furi_string_cat_printf(\n                path_dst, \"/%s%s\", archive->text_store, archive->file_extension);\n\n            storage_common_rename(fs_api, path_src, furi_string_get_cstr(path_dst));\n            furi_record_close(RECORD_STORAGE);\n\n            if(file->fav) {\n                archive_favorites_rename(path_src, furi_string_get_cstr(path_dst));\n            }\n\n            furi_string_free(path_dst);\n\n            scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid archive_scene_rename_on_exit(void* context) {\n    ArchiveApp* archive = (ArchiveApp*)context;\n\n    // Clear view\n    void* validator_context = text_input_get_validator_callback_context(archive->text_input);\n    text_input_set_validator(archive->text_input, NULL, NULL);\n    validator_is_file_free(validator_context);\n\n    text_input_reset(archive->text_input);\n}\n"
  },
  {
    "path": "applications/main/archive/views/archive_browser_view.c",
    "content": "#include \"assets_icons.h\"\n#include \"toolbox/path.h\"\n#include <furi.h>\n#include \"archive_browser_view.h\"\n#include \"../helpers/archive_browser.h\"\n\n#define SCROLL_INTERVAL (333)\n#define SCROLL_DELAY    (2)\n\nstatic const char* ArchiveTabNames[] = {\n    [ArchiveTabFavorites] = \"Favorites\",\n    [ArchiveTabIButton] = \"iButton\",\n    [ArchiveTabNFC] = \"NFC\",\n    [ArchiveTabSubGhz] = \"Sub-GHz\",\n    [ArchiveTabLFRFID] = \"RFID LF\",\n    [ArchiveTabInfrared] = \"Infrared\",\n    [ArchiveTabBadUsb] = \"Bad USB\",\n    [ArchiveTabU2f] = \"U2F\",\n    [ArchiveTabApplications] = \"Apps\",\n    [ArchiveTabBrowser] = \"Browser\",\n};\n\nstatic const Icon* ArchiveItemIcons[] = {\n    [ArchiveFileTypeIButton] = &I_ibutt_10px,\n    [ArchiveFileTypeNFC] = &I_Nfc_10px,\n    [ArchiveFileTypeSubGhz] = &I_sub1_10px,\n    [ArchiveFileTypeLFRFID] = &I_125_10px,\n    [ArchiveFileTypeInfrared] = &I_ir_10px,\n    [ArchiveFileTypeBadUsb] = &I_badusb_10px,\n    [ArchiveFileTypeU2f] = &I_u2f_10px,\n    [ArchiveFileTypeSetting] = &I_settings_10px,\n    [ArchiveFileTypeUpdateManifest] = &I_update_10px,\n    [ArchiveFileTypeFolder] = &I_dir_10px,\n    [ArchiveFileTypeUnknown] = &I_unknown_10px,\n    [ArchiveFileTypeLoading] = &I_loading_10px,\n    [ArchiveFileTypeApplication] = &I_unknown_10px,\n    [ArchiveFileTypeJS] = &I_js_script_10px,\n    [ArchiveFileTypeAppOrJs] = &I_unknown_10px,\n};\n\nvoid archive_browser_set_callback(\n    ArchiveBrowserView* browser,\n    ArchiveBrowserViewCallback callback,\n    void* context) {\n    furi_assert(browser);\n    furi_assert(callback);\n    browser->callback = callback;\n    browser->context = context;\n}\n\nstatic void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, 71, 17, 57, 46);\n    canvas_set_color(canvas, ColorBlack);\n    elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);\n\n    FuriString* menu[MENU_ITEMS];\n\n    menu[0] = furi_string_alloc_set(\"Run in app\");\n    menu[1] = furi_string_alloc_set(\"Pin\");\n    menu[2] = furi_string_alloc_set(\"Rename\");\n    menu[3] = furi_string_alloc_set(\"Delete\");\n\n    ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset);\n\n    if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) {\n        furi_string_set(menu[1], \"Unpin\");\n    }\n\n    if(!archive_is_known_app(selected->type)) {\n        furi_string_set(menu[0], \"---\");\n        furi_string_set(menu[1], \"---\");\n    } else {\n        if(model->tab_idx == ArchiveTabFavorites) {\n            furi_string_set(menu[2], \"Move\");\n            furi_string_set(menu[3], \"---\");\n        } else if(selected->is_app) {\n            furi_string_set(menu[2], \"---\");\n        }\n    }\n\n    for(size_t i = 0; i < MENU_ITEMS; i++) {\n        canvas_draw_str(canvas, 82, 27 + i * 11, furi_string_get_cstr(menu[i]));\n        furi_string_free(menu[i]);\n    }\n\n    canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);\n}\n\nstatic void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) {\n    uint8_t x_offset = moving ? MOVE_OFFSET : 0;\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_box(\n        canvas,\n        0 + x_offset,\n        15 + idx * FRAME_HEIGHT,\n        (scrollbar ? 122 : 127) - x_offset,\n        FRAME_HEIGHT);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_dot(canvas, 0 + x_offset, 15 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 1 + x_offset, 15 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 1);\n\n    canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 11);\n    canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);\n}\n\nstatic void archive_draw_loading(Canvas* canvas, ArchiveBrowserViewModel* model) {\n    furi_assert(model);\n\n    uint8_t x = 128 / 2 - 24 / 2;\n    uint8_t y = 64 / 2 - 24 / 2;\n\n    canvas_draw_icon(canvas, x, y, &A_Loading_24);\n}\n\nstatic void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {\n    furi_assert(model);\n\n    size_t array_size = files_array_size(model->files);\n    bool scrollbar = model->item_cnt > 4;\n\n    for(uint32_t i = 0; i < MIN(model->item_cnt, MENU_ITEMS); ++i) {\n        FuriString* str_buf;\n        str_buf = furi_string_alloc();\n        int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u);\n        uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0;\n\n        ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading;\n        uint8_t* custom_icon_data = NULL;\n\n        if(archive_is_item_in_array(model, idx)) {\n            ArchiveFile_t* file = files_array_get(\n                model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0));\n            file_type = file->type;\n            if(file_type == ArchiveFileTypeApplication) {\n                if(file->custom_icon_data) {\n                    custom_icon_data = file->custom_icon_data;\n                    furi_string_set(str_buf, file->custom_name);\n                } else {\n                    file_type = ArchiveFileTypeUnknown;\n                    path_extract_filename(file->path, str_buf, archive_is_known_app(file->type));\n                }\n            } else {\n                path_extract_filename(file->path, str_buf, archive_is_known_app(file->type));\n            }\n        } else {\n            furi_string_set(str_buf, \"---\");\n        }\n\n        size_t scroll_counter = model->scroll_counter;\n\n        if(model->item_idx == idx) {\n            archive_draw_frame(canvas, i, scrollbar, model->move_fav);\n            if(scroll_counter < SCROLL_DELAY) {\n                scroll_counter = 0;\n            } else {\n                scroll_counter -= SCROLL_DELAY;\n            }\n        } else {\n            canvas_set_color(canvas, ColorBlack);\n            scroll_counter = 0;\n        }\n\n        if(custom_icon_data) {\n            canvas_draw_bitmap(\n                canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, 11, 10, custom_icon_data);\n        } else {\n            canvas_draw_icon(\n                canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]);\n        }\n\n        elements_scrollable_text_line(\n            canvas,\n            15 + x_offset,\n            24 + i * FRAME_HEIGHT,\n            ((scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset),\n            str_buf,\n            scroll_counter,\n            (model->item_idx != idx));\n\n        furi_string_free(str_buf);\n    }\n\n    if(scrollbar) {\n        elements_scrollbar_pos(canvas, 126, 15, 49, model->item_idx, model->item_cnt);\n    }\n\n    if(model->menu) {\n        render_item_menu(canvas, model);\n    }\n}\n\nstatic void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* model) {\n    furi_assert(model);\n\n    const char* tab_name = ArchiveTabNames[model->tab_idx];\n\n    canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, 0, 0, 50, 13);\n    canvas_draw_box(canvas, 107, 0, 20, 13);\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_rframe(canvas, 0, 0, 51, 13, 1); // frame\n    canvas_draw_line(canvas, 49, 1, 49, 11); // shadow right\n    canvas_draw_line(canvas, 1, 11, 49, 11); // shadow bottom\n    canvas_draw_str_aligned(canvas, 25, 9, AlignCenter, AlignBottom, tab_name);\n\n    canvas_draw_rframe(canvas, 107, 0, 21, 13, 1);\n    canvas_draw_line(canvas, 126, 1, 126, 11);\n    canvas_draw_line(canvas, 108, 11, 126, 11);\n\n    if(model->move_fav) {\n        canvas_draw_icon(canvas, 110, 4, &I_ButtonUp_7x4);\n        canvas_draw_icon(canvas, 117, 4, &I_ButtonDown_7x4);\n    } else {\n        canvas_draw_icon(canvas, 111, 2, &I_ButtonLeft_4x7);\n        canvas_draw_icon(canvas, 119, 2, &I_ButtonRight_4x7);\n    }\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_dot(canvas, 50, 0);\n    canvas_draw_dot(canvas, 127, 0);\n\n    canvas_set_color(canvas, ColorBlack);\n}\n\nstatic void archive_view_render(Canvas* canvas, void* mdl) {\n    ArchiveBrowserViewModel* model = mdl;\n\n    archive_render_status_bar(canvas, mdl);\n\n    if(model->folder_loading) {\n        archive_draw_loading(canvas, model);\n    } else if(model->item_cnt > 0) {\n        draw_list(canvas, model);\n    } else {\n        canvas_draw_str_aligned(\n            canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, \"Empty\");\n    }\n}\n\nView* archive_browser_get_view(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n    return browser->view;\n}\n\nstatic void file_list_rollover(ArchiveBrowserViewModel* model) {\n    if(!model->list_loading && files_array_size(model->files) < model->item_cnt) {\n        files_array_reset(model->files);\n    }\n}\n\nstatic bool archive_view_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    ArchiveBrowserView* browser = context;\n\n    bool in_menu;\n    bool move_fav_mode;\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            in_menu = model->menu;\n            move_fav_mode = model->move_fav;\n        },\n        false);\n\n    if(in_menu) {\n        if(event->type == InputTypeShort) {\n            if(event->key == InputKeyUp || event->key == InputKeyDown) {\n                with_view_model(\n                    browser->view,\n                    ArchiveBrowserViewModel * model,\n                    {\n                        if(event->key == InputKeyUp) {\n                            model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;\n                        } else if(event->key == InputKeyDown) {\n                            model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;\n                        }\n                    },\n                    true);\n            }\n\n            if(event->key == InputKeyOk) {\n                uint8_t idx;\n                with_view_model(\n                    browser->view,\n                    ArchiveBrowserViewModel * model,\n                    { idx = model->menu_idx; },\n                    false);\n                browser->callback(file_menu_actions[idx], browser->context);\n            } else if(event->key == InputKeyBack) {\n                browser->callback(ArchiveBrowserEventFileMenuClose, browser->context);\n            }\n        }\n\n    } else {\n        if(event->type == InputTypeShort) {\n            if(event->key == InputKeyLeft || event->key == InputKeyRight) {\n                if(move_fav_mode) return false;\n                archive_switch_tab(browser, event->key);\n            } else if(event->key == InputKeyBack) {\n                if(move_fav_mode) {\n                    browser->callback(ArchiveBrowserEventExitFavMove, browser->context);\n                } else {\n                    browser->callback(ArchiveBrowserEventExit, browser->context);\n                }\n            }\n        }\n\n        if((event->key == InputKeyUp || event->key == InputKeyDown) &&\n           (event->type == InputTypeShort || event->type == InputTypeRepeat)) {\n            with_view_model(\n                browser->view,\n                ArchiveBrowserViewModel * model,\n                {\n                    int32_t scroll_speed = 1;\n                    if(model->button_held_for_ticks > 5) {\n                        if(model->button_held_for_ticks % 2) {\n                            scroll_speed = 0;\n                        } else {\n                            scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2;\n                        }\n                    }\n\n                    if(event->key == InputKeyUp) {\n                        if(model->item_idx < scroll_speed) {\n                            model->button_held_for_ticks = 0;\n                            model->item_idx = model->item_cnt - 1;\n                            file_list_rollover(model);\n                        } else {\n                            model->item_idx =\n                                ((model->item_idx - scroll_speed) + model->item_cnt) %\n                                model->item_cnt;\n                        }\n                        if(archive_is_file_list_load_required(model)) {\n                            model->list_loading = true;\n                            browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context);\n                        }\n                        if(move_fav_mode) {\n                            browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);\n                        }\n                        model->scroll_counter = 0;\n                        model->button_held_for_ticks += 1;\n                    } else if(event->key == InputKeyDown) {\n                        int32_t count = model->item_cnt;\n                        if(model->item_idx + scroll_speed >= count) {\n                            model->button_held_for_ticks = 0;\n                            model->item_idx = 0;\n                            file_list_rollover(model);\n                        } else {\n                            model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt;\n                        }\n                        if(archive_is_file_list_load_required(model)) {\n                            model->list_loading = true;\n                            browser->callback(ArchiveBrowserEventLoadNextItems, browser->context);\n                        }\n                        if(move_fav_mode) {\n                            browser->callback(ArchiveBrowserEventFavMoveDown, browser->context);\n                        }\n                        model->scroll_counter = 0;\n                        model->button_held_for_ticks += 1;\n                    }\n                },\n                true);\n            archive_update_offset(browser);\n        }\n\n        if(event->key == InputKeyOk) {\n            ArchiveFile_t* selected = archive_get_current_file(browser);\n\n            if(selected) {\n                bool favorites = archive_get_tab(browser) == ArchiveTabFavorites;\n                bool folder = selected->type == ArchiveFileTypeFolder;\n\n                if(event->type == InputTypeShort) {\n                    if(favorites) {\n                        if(move_fav_mode) {\n                            browser->callback(ArchiveBrowserEventSaveFavMove, browser->context);\n                        } else {\n                            browser->callback(ArchiveBrowserEventFileMenuRun, browser->context);\n                        }\n                    } else if(folder) {\n                        browser->callback(ArchiveBrowserEventEnterDir, browser->context);\n                    } else {\n                        browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context);\n                    }\n                } else if(event->type == InputTypeLong) {\n                    if(move_fav_mode) {\n                        browser->callback(ArchiveBrowserEventSaveFavMove, browser->context);\n                    } else if(folder || favorites) {\n                        browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context);\n                    }\n                }\n            }\n        }\n    }\n\n    if(event->type == InputTypeRelease) {\n        with_view_model(\n            browser->view,\n            ArchiveBrowserViewModel * model,\n            { model->button_held_for_ticks = 0; },\n            true);\n    }\n\n    return true;\n}\n\nstatic void browser_scroll_timer(void* context) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = context;\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter++; }, true);\n}\n\nstatic void browser_view_enter(void* context) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = context;\n    with_view_model(\n        browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter = 0; }, true);\n    furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL);\n}\n\nstatic void browser_view_exit(void* context) {\n    furi_assert(context);\n    ArchiveBrowserView* browser = context;\n    furi_timer_stop(browser->scroll_timer);\n}\n\nArchiveBrowserView* browser_alloc(void) {\n    ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView));\n    browser->view = view_alloc();\n    view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(ArchiveBrowserViewModel));\n    view_set_context(browser->view, browser);\n    view_set_draw_callback(browser->view, archive_view_render);\n    view_set_input_callback(browser->view, archive_view_input);\n    view_set_enter_callback(browser->view, browser_view_enter);\n    view_set_exit_callback(browser->view, browser_view_exit);\n\n    browser->scroll_timer = furi_timer_alloc(browser_scroll_timer, FuriTimerTypePeriodic, browser);\n\n    browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT));\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        {\n            files_array_init(model->files);\n            model->tab_idx = TAB_DEFAULT;\n        },\n        true);\n\n    return browser;\n}\n\nvoid browser_free(ArchiveBrowserView* browser) {\n    furi_assert(browser);\n\n    furi_timer_free(browser->scroll_timer);\n\n    if(browser->worker_running) {\n        file_browser_worker_free(browser->worker);\n    }\n\n    with_view_model(\n        browser->view,\n        ArchiveBrowserViewModel * model,\n        { files_array_clear(model->files); },\n        false);\n\n    furi_string_free(browser->path);\n\n    view_free(browser->view);\n    free(browser);\n}\n"
  },
  {
    "path": "applications/main/archive/views/archive_browser_view.h",
    "content": "#pragma once\n\n#include \"../helpers/archive_files.h\"\n#include \"../helpers/archive_favorites.h\"\n\n#include <gui/gui_i.h>\n#include <gui/view.h>\n#include <gui/canvas.h>\n#include <gui/elements.h>\n#include <gui/modules/file_browser_worker.h>\n#include <storage/storage.h>\n#include <furi.h>\n\n#define MAX_LEN_PX   110\n#define MAX_NAME_LEN 255\n#define MAX_EXT_LEN  6\n#define FRAME_HEIGHT 12\n#define MENU_ITEMS   4u\n#define MOVE_OFFSET  5u\n\ntypedef enum {\n    ArchiveTabFavorites,\n    ArchiveTabSubGhz,\n    ArchiveTabLFRFID,\n    ArchiveTabNFC,\n    ArchiveTabInfrared,\n    ArchiveTabIButton,\n    ArchiveTabBadUsb,\n    ArchiveTabU2f,\n    ArchiveTabApplications,\n    ArchiveTabBrowser,\n    ArchiveTabTotal,\n} ArchiveTabEnum;\n\ntypedef enum {\n    ArchiveBrowserEventFileMenuOpen,\n    ArchiveBrowserEventFileMenuClose,\n    ArchiveBrowserEventFileMenuRun,\n    ArchiveBrowserEventFileMenuPin,\n    ArchiveBrowserEventFileMenuRename,\n    ArchiveBrowserEventFileMenuDelete,\n\n    ArchiveBrowserEventEnterDir,\n\n    ArchiveBrowserEventFavMoveUp,\n    ArchiveBrowserEventFavMoveDown,\n    ArchiveBrowserEventEnterFavMove,\n    ArchiveBrowserEventExitFavMove,\n    ArchiveBrowserEventSaveFavMove,\n\n    ArchiveBrowserEventLoadPrevItems,\n    ArchiveBrowserEventLoadNextItems,\n\n    ArchiveBrowserEventListRefresh,\n\n    ArchiveBrowserEventExit,\n} ArchiveBrowserEvent;\n\nstatic const uint8_t file_menu_actions[MENU_ITEMS] = {\n    [0] = ArchiveBrowserEventFileMenuRun,\n    [1] = ArchiveBrowserEventFileMenuPin,\n    [2] = ArchiveBrowserEventFileMenuRename,\n    [3] = ArchiveBrowserEventFileMenuDelete,\n};\n\ntypedef struct ArchiveBrowserView ArchiveBrowserView;\n\ntypedef void (*ArchiveBrowserViewCallback)(ArchiveBrowserEvent event, void* context);\n\ntypedef enum {\n    BrowserActionBrowse,\n    BrowserActionItemMenu,\n    BrowserActionTotal,\n} BrowserActionEnum;\n\nstruct ArchiveBrowserView {\n    View* view;\n    BrowserWorker* worker;\n    bool worker_running;\n    ArchiveBrowserViewCallback callback;\n    void* context;\n    FuriString* path;\n    InputKey last_tab_switch_dir;\n    bool is_root;\n    FuriTimer* scroll_timer;\n};\n\ntypedef struct {\n    ArchiveTabEnum tab_idx;\n    files_array_t files;\n\n    uint8_t menu_idx;\n    bool menu;\n    bool move_fav;\n    bool list_loading;\n    bool folder_loading;\n\n    uint32_t item_cnt;\n    int32_t item_idx;\n    int32_t array_offset;\n    int32_t list_offset;\n    size_t scroll_counter;\n\n    uint32_t button_held_for_ticks;\n} ArchiveBrowserViewModel;\n\nvoid archive_browser_set_callback(\n    ArchiveBrowserView* browser,\n    ArchiveBrowserViewCallback callback,\n    void* context);\n\nView* archive_browser_get_view(ArchiveBrowserView* browser);\n\nArchiveBrowserView* browser_alloc(void);\nvoid browser_free(ArchiveBrowserView* browser);\n"
  },
  {
    "path": "applications/main/bad_usb/application.fam",
    "content": "App(\n    appid=\"bad_usb\",\n    name=\"Bad USB\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    entry_point=\"bad_usb_app\",\n    stack_size=2 * 1024,\n    icon=\"A_BadUsb_14\",\n    order=70,\n    resources=\"resources\",\n    fap_libs=[\"assets\", \"ble_profile\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"USB\",\n)\n"
  },
  {
    "path": "applications/main/bad_usb/bad_usb_app.c",
    "content": "#include \"bad_usb_app_i.h\"\n#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <lib/toolbox/path.h>\n#include <flipper_format/flipper_format.h>\n\n#define BAD_USB_SETTINGS_PATH           BAD_USB_APP_BASE_FOLDER \"/.badusb.settings\"\n#define BAD_USB_SETTINGS_FILE_TYPE      \"Flipper BadUSB Settings File\"\n#define BAD_USB_SETTINGS_VERSION        1\n#define BAD_USB_SETTINGS_DEFAULT_LAYOUT BAD_USB_APP_PATH_LAYOUT_FOLDER \"/en-US.kl\"\n\nstatic bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    BadUsbApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool bad_usb_app_back_event_callback(void* context) {\n    furi_assert(context);\n    BadUsbApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void bad_usb_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    BadUsbApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nstatic void bad_usb_load_settings(BadUsbApp* app) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff = flipper_format_file_alloc(storage);\n    bool state = false;\n\n    FuriString* temp_str = furi_string_alloc();\n    uint32_t version = 0;\n    uint32_t interface = 0;\n\n    if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) {\n        do {\n            if(!flipper_format_read_header(fff, temp_str, &version)) break;\n            if((strcmp(furi_string_get_cstr(temp_str), BAD_USB_SETTINGS_FILE_TYPE) != 0) ||\n               (version != BAD_USB_SETTINGS_VERSION))\n                break;\n\n            if(!flipper_format_read_string(fff, \"layout\", temp_str)) break;\n            if(!flipper_format_read_uint32(fff, \"interface\", &interface, 1)) break;\n            if(interface > BadUsbHidInterfaceBle) break;\n\n            state = true;\n        } while(0);\n    }\n    flipper_format_free(fff);\n    furi_record_close(RECORD_STORAGE);\n\n    if(state) {\n        furi_string_set(app->keyboard_layout, temp_str);\n        app->interface = interface;\n\n        Storage* fs_api = furi_record_open(RECORD_STORAGE);\n        FileInfo layout_file_info;\n        FS_Error file_check_err = storage_common_stat(\n            fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);\n        furi_record_close(RECORD_STORAGE);\n        if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) {\n            furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);\n        }\n    } else {\n        furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);\n        app->interface = BadUsbHidInterfaceUsb;\n    }\n\n    furi_string_free(temp_str);\n}\n\nstatic void bad_usb_save_settings(BadUsbApp* app) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_always(fff, BAD_USB_SETTINGS_PATH)) {\n        do {\n            if(!flipper_format_write_header_cstr(\n                   fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION))\n                break;\n            if(!flipper_format_write_string(fff, \"layout\", app->keyboard_layout)) break;\n            uint32_t interface_id = app->interface;\n            if(!flipper_format_write_uint32(fff, \"interface\", (const uint32_t*)&interface_id, 1))\n                break;\n        } while(0);\n    }\n\n    flipper_format_free(fff);\n    furi_record_close(RECORD_STORAGE);\n}\n\nvoid bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface) {\n    app->interface = interface;\n    bad_usb_view_set_interface(app->bad_usb_view, interface);\n}\n\nBadUsbApp* bad_usb_app_alloc(char* arg) {\n    BadUsbApp* app = malloc(sizeof(BadUsbApp));\n\n    app->bad_usb_script = NULL;\n\n    app->file_path = furi_string_alloc();\n    app->keyboard_layout = furi_string_alloc();\n    if(arg && strlen(arg)) {\n        furi_string_set(app->file_path, arg);\n    }\n\n    bad_usb_load_settings(app);\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n    app->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app);\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, bad_usb_app_tick_event_callback, 500);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, bad_usb_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, bad_usb_app_back_event_callback);\n\n    // Custom Widget\n    app->widget = widget_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BadUsbAppViewWidget, widget_get_view(app->widget));\n\n    // Popup\n    app->popup = popup_alloc();\n    view_dispatcher_add_view(app->view_dispatcher, BadUsbAppViewPopup, popup_get_view(app->popup));\n\n    app->var_item_list = variable_item_list_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        BadUsbAppViewConfig,\n        variable_item_list_get_view(app->var_item_list));\n\n    app->bad_usb_view = bad_usb_view_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BadUsbAppViewWork, bad_usb_view_get_view(app->bad_usb_view));\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    if(furi_hal_usb_is_locked()) {\n        app->error = BadUsbAppErrorCloseRpc;\n        app->usb_if_prev = NULL;\n        scene_manager_next_scene(app->scene_manager, BadUsbSceneError);\n    } else {\n        app->usb_if_prev = furi_hal_usb_get_config();\n        furi_check(furi_hal_usb_set_config(NULL, NULL));\n\n        if(!furi_string_empty(app->file_path)) {\n            scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);\n        } else {\n            furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER);\n            scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect);\n        }\n    }\n\n    return app;\n}\n\nvoid bad_usb_app_free(BadUsbApp* app) {\n    furi_assert(app);\n\n    if(app->bad_usb_script) {\n        bad_usb_script_close(app->bad_usb_script);\n        app->bad_usb_script = NULL;\n    }\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);\n    bad_usb_view_free(app->bad_usb_view);\n\n    // Custom Widget\n    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWidget);\n    widget_free(app->widget);\n\n    // Popup\n    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewPopup);\n    popup_free(app->popup);\n\n    // Config menu\n    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);\n    variable_item_list_free(app->var_item_list);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_DIALOGS);\n\n    bad_usb_save_settings(app);\n\n    furi_string_free(app->file_path);\n    furi_string_free(app->keyboard_layout);\n\n    if(app->usb_if_prev) {\n        furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL));\n    }\n\n    free(app);\n}\n\nint32_t bad_usb_app(void* p) {\n    BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p);\n\n    view_dispatcher_run(bad_usb_app->view_dispatcher);\n\n    bad_usb_app_free(bad_usb_app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/bad_usb_app.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct BadUsbApp BadUsbApp;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/bad_usb/bad_usb_app_i.h",
    "content": "#pragma once\n\n#include \"bad_usb_app.h\"\n#include \"scenes/bad_usb_scene.h\"\n#include \"helpers/ducky_script.h\"\n#include \"helpers/bad_usb_hid.h\"\n\n#include <gui/gui.h>\n#include <assets_icons.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <dialogs/dialogs.h>\n#include <notification/notification_messages.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/widget.h>\n#include <gui/modules/popup.h>\n#include \"views/bad_usb_view.h\"\n#include <furi_hal_usb.h>\n\n#define BAD_USB_APP_BASE_FOLDER        EXT_PATH(\"badusb\")\n#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER \"/assets/layouts\"\n#define BAD_USB_APP_SCRIPT_EXTENSION   \".txt\"\n#define BAD_USB_APP_LAYOUT_EXTENSION   \".kl\"\n\ntypedef enum {\n    BadUsbAppErrorNoFiles,\n    BadUsbAppErrorCloseRpc,\n} BadUsbAppError;\n\nstruct BadUsbApp {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    NotificationApp* notifications;\n    DialogsApp* dialogs;\n    Widget* widget;\n    Popup* popup;\n    VariableItemList* var_item_list;\n\n    BadUsbAppError error;\n    FuriString* file_path;\n    FuriString* keyboard_layout;\n    BadUsb* bad_usb_view;\n    BadUsbScript* bad_usb_script;\n\n    BadUsbHidInterface interface;\n    FuriHalUsbInterface* usb_if_prev;\n};\n\ntypedef enum {\n    BadUsbAppViewWidget,\n    BadUsbAppViewPopup,\n    BadUsbAppViewWork,\n    BadUsbAppViewConfig,\n} BadUsbAppView;\n\nvoid bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/bad_usb_hid.c",
    "content": "#include \"bad_usb_hid.h\"\n#include <extra_profiles/hid_profile.h>\n#include <bt/bt_service/bt.h>\n#include <storage/storage.h>\n\n#define TAG \"BadUSB HID\"\n\n#define HID_BT_KEYS_STORAGE_NAME \".bt_hid.keys\"\n\nvoid* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) {\n    furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg));\n    return NULL;\n}\n\nvoid hid_usb_deinit(void* inst) {\n    UNUSED(inst);\n    furi_check(furi_hal_usb_set_config(NULL, NULL));\n}\n\nvoid hid_usb_set_state_callback(void* inst, HidStateCallback cb, void* context) {\n    UNUSED(inst);\n    furi_hal_hid_set_state_callback(cb, context);\n}\n\nbool hid_usb_is_connected(void* inst) {\n    UNUSED(inst);\n    return furi_hal_hid_is_connected();\n}\n\nbool hid_usb_kb_press(void* inst, uint16_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_kb_press(button);\n}\n\nbool hid_usb_kb_release(void* inst, uint16_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_kb_release(button);\n}\n\nbool hid_usb_mouse_press(void* inst, uint8_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_mouse_press(button);\n}\n\nbool hid_usb_mouse_release(void* inst, uint8_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_mouse_release(button);\n}\n\nbool hid_usb_mouse_scroll(void* inst, int8_t delta) {\n    UNUSED(inst);\n    return furi_hal_hid_mouse_scroll(delta);\n}\n\nbool hid_usb_mouse_move(void* inst, int8_t dx, int8_t dy) {\n    UNUSED(inst);\n    return furi_hal_hid_mouse_move(dx, dy);\n}\n\nbool hid_usb_mouse_release_all(void* inst) {\n    UNUSED(inst);\n    return furi_hal_hid_mouse_release(0);\n}\n\nbool hid_usb_consumer_press(void* inst, uint16_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_consumer_key_press(button);\n}\n\nbool hid_usb_consumer_release(void* inst, uint16_t button) {\n    UNUSED(inst);\n    return furi_hal_hid_consumer_key_release(button);\n}\n\nbool hid_usb_release_all(void* inst) {\n    UNUSED(inst);\n    bool state = furi_hal_hid_kb_release_all();\n    state &= furi_hal_hid_consumer_key_release_all();\n    state &= hid_usb_mouse_release_all(inst);\n    return state;\n}\n\nuint8_t hid_usb_get_led_state(void* inst) {\n    UNUSED(inst);\n    return furi_hal_hid_get_led_state();\n}\n\nstatic const BadUsbHidApi hid_api_usb = {\n    .init = hid_usb_init,\n    .deinit = hid_usb_deinit,\n    .set_state_callback = hid_usb_set_state_callback,\n    .is_connected = hid_usb_is_connected,\n\n    .kb_press = hid_usb_kb_press,\n    .kb_release = hid_usb_kb_release,\n    .mouse_press = hid_usb_mouse_press,\n    .mouse_release = hid_usb_mouse_release,\n    .mouse_scroll = hid_usb_mouse_scroll,\n    .mouse_move = hid_usb_mouse_move,\n    .consumer_press = hid_usb_consumer_press,\n    .consumer_release = hid_usb_consumer_release,\n    .release_all = hid_usb_release_all,\n    .get_led_state = hid_usb_get_led_state,\n};\n\ntypedef struct {\n    Bt* bt;\n    FuriHalBleProfileBase* profile;\n    HidStateCallback state_callback;\n    void* callback_context;\n    bool is_connected;\n} BleHidInstance;\n\nstatic const BleProfileHidParams ble_hid_params = {\n    .device_name_prefix = \"BadUSB\",\n    .mac_xor = 0x0002,\n};\n\nstatic void hid_ble_connection_status_callback(BtStatus status, void* context) {\n    furi_assert(context);\n    BleHidInstance* ble_hid = context;\n    ble_hid->is_connected = (status == BtStatusConnected);\n    if(ble_hid->state_callback) {\n        ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context);\n    }\n}\n\nvoid* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {\n    UNUSED(hid_cfg);\n    BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));\n    ble_hid->bt = furi_record_open(RECORD_BT);\n    bt_disconnect(ble_hid->bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n\n    bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));\n\n    ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, (void*)&ble_hid_params);\n    furi_check(ble_hid->profile);\n\n    furi_hal_bt_start_advertising();\n\n    bt_set_status_changed_callback(ble_hid->bt, hid_ble_connection_status_callback, ble_hid);\n\n    return ble_hid;\n}\n\nvoid hid_ble_deinit(void* inst) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n\n    bt_set_status_changed_callback(ble_hid->bt, NULL, NULL);\n    bt_disconnect(ble_hid->bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n    bt_keys_storage_set_default_path(ble_hid->bt);\n\n    furi_check(bt_profile_restore_default(ble_hid->bt));\n    furi_record_close(RECORD_BT);\n    free(ble_hid);\n}\n\nvoid hid_ble_set_state_callback(void* inst, HidStateCallback cb, void* context) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    ble_hid->state_callback = cb;\n    ble_hid->callback_context = context;\n}\n\nbool hid_ble_is_connected(void* inst) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_hid->is_connected;\n}\n\nbool hid_ble_kb_press(void* inst, uint16_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_kb_press(ble_hid->profile, button);\n}\n\nbool hid_ble_kb_release(void* inst, uint16_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_kb_release(ble_hid->profile, button);\n}\n\nbool hid_ble_mouse_press(void* inst, uint8_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_mouse_press(ble_hid->profile, button);\n}\nbool hid_ble_mouse_release(void* inst, uint8_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_mouse_release(ble_hid->profile, button);\n}\nbool hid_ble_mouse_scroll(void* inst, int8_t delta) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_mouse_scroll(ble_hid->profile, delta);\n}\nbool hid_ble_mouse_move(void* inst, int8_t dx, int8_t dy) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_mouse_move(ble_hid->profile, dx, dy);\n}\n\nbool hid_ble_consumer_press(void* inst, uint16_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_consumer_key_press(ble_hid->profile, button);\n}\n\nbool hid_ble_consumer_release(void* inst, uint16_t button) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    return ble_profile_hid_consumer_key_release(ble_hid->profile, button);\n}\n\nbool hid_ble_release_all(void* inst) {\n    BleHidInstance* ble_hid = inst;\n    furi_assert(ble_hid);\n    bool state = ble_profile_hid_kb_release_all(ble_hid->profile);\n    state &= ble_profile_hid_consumer_key_release_all(ble_hid->profile);\n    state &= ble_profile_hid_mouse_release_all(ble_hid->profile);\n    return state;\n}\n\nuint8_t hid_ble_get_led_state(void* inst) {\n    UNUSED(inst);\n    FURI_LOG_W(TAG, \"hid_ble_get_led_state not implemented\");\n    return 0;\n}\n\nstatic const BadUsbHidApi hid_api_ble = {\n    .init = hid_ble_init,\n    .deinit = hid_ble_deinit,\n    .set_state_callback = hid_ble_set_state_callback,\n    .is_connected = hid_ble_is_connected,\n\n    .kb_press = hid_ble_kb_press,\n    .kb_release = hid_ble_kb_release,\n    .mouse_press = hid_ble_mouse_press,\n    .mouse_release = hid_ble_mouse_release,\n    .mouse_scroll = hid_ble_mouse_scroll,\n    .mouse_move = hid_ble_mouse_move,\n    .consumer_press = hid_ble_consumer_press,\n    .consumer_release = hid_ble_consumer_release,\n    .release_all = hid_ble_release_all,\n    .get_led_state = hid_ble_get_led_state,\n};\n\nconst BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface) {\n    if(interface == BadUsbHidInterfaceUsb) {\n        return &hid_api_usb;\n    } else {\n        return &hid_api_ble;\n    }\n}\n\nvoid bad_usb_hid_ble_remove_pairing(void) {\n    Bt* bt = furi_record_open(RECORD_BT);\n    bt_disconnect(bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n\n    furi_hal_bt_stop_advertising();\n\n    bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));\n    bt_forget_bonded_devices(bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n    bt_keys_storage_set_default_path(bt);\n\n    furi_check(bt_profile_restore_default(bt));\n    furi_record_close(RECORD_BT);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/bad_usb_hid.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n#include <furi_hal.h>\n\ntypedef enum {\n    BadUsbHidInterfaceUsb,\n    BadUsbHidInterfaceBle,\n} BadUsbHidInterface;\n\ntypedef struct {\n    void* (*init)(FuriHalUsbHidConfig* hid_cfg);\n    void (*deinit)(void* inst);\n    void (*set_state_callback)(void* inst, HidStateCallback cb, void* context);\n    bool (*is_connected)(void* inst);\n\n    bool (*kb_press)(void* inst, uint16_t button);\n    bool (*kb_release)(void* inst, uint16_t button);\n    bool (*mouse_press)(void* inst, uint8_t button);\n    bool (*mouse_release)(void* inst, uint8_t button);\n    bool (*mouse_scroll)(void* inst, int8_t delta);\n    bool (*mouse_move)(void* inst, int8_t dx, int8_t dy);\n    bool (*consumer_press)(void* inst, uint16_t button);\n    bool (*consumer_release)(void* inst, uint16_t button);\n    bool (*release_all)(void* inst);\n    uint8_t (*get_led_state)(void* inst);\n} BadUsbHidApi;\n\nconst BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface);\n\nvoid bad_usb_hid_ble_remove_pairing(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/ducky_script.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/gui.h>\n#include <input/input.h>\n#include <lib/toolbox/args.h>\n#include <lib/toolbox/strint.h>\n#include <storage/storage.h>\n#include \"ducky_script.h\"\n#include \"ducky_script_i.h\"\n#include <dolphin/dolphin.h>\n\n#define TAG \"BadUsb\"\n\n#define WORKER_TAG TAG \"Worker\"\n\n#define BADUSB_ASCII_TO_KEY(script, x) \\\n    (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)\n\ntypedef enum {\n    WorkerEvtStartStop = (1 << 0),\n    WorkerEvtPauseResume = (1 << 1),\n    WorkerEvtEnd = (1 << 2),\n    WorkerEvtConnect = (1 << 3),\n    WorkerEvtDisconnect = (1 << 4),\n} WorkerEvtFlags;\n\nstatic const char ducky_cmd_id[] = {\"ID\"};\n\nstatic const uint8_t numpad_keys[10] = {\n    HID_KEYPAD_0,\n    HID_KEYPAD_1,\n    HID_KEYPAD_2,\n    HID_KEYPAD_3,\n    HID_KEYPAD_4,\n    HID_KEYPAD_5,\n    HID_KEYPAD_6,\n    HID_KEYPAD_7,\n    HID_KEYPAD_8,\n    HID_KEYPAD_9,\n};\n\nuint32_t ducky_get_command_len(const char* line) {\n    char* first_space = strchr(line, ' ');\n    return first_space ? (first_space - line) : 0;\n}\n\nbool ducky_is_line_end(const char chr) {\n    return (chr == ' ') || (chr == '\\0') || (chr == '\\r') || (chr == '\\n');\n}\n\nuint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers) {\n    uint16_t keycode = ducky_get_keycode_by_name(param);\n    if(keycode != HID_KEYBOARD_NONE) {\n        return keycode;\n    }\n\n    if(accept_modifiers) {\n        uint16_t keycode = ducky_get_modifier_keycode_by_name(param);\n        if(keycode != HID_KEYBOARD_NONE) {\n            return keycode;\n        }\n    }\n\n    if(strlen(param) > 0) {\n        return BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF;\n    }\n    return 0;\n}\n\nbool ducky_get_number(const char* param, uint32_t* val) {\n    uint32_t value = 0;\n    if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) {\n        *val = value;\n        return true;\n    }\n    return false;\n}\n\nvoid ducky_numlock_on(BadUsbScript* bad_usb) {\n    if((bad_usb->hid->get_led_state(bad_usb->hid_inst) & HID_KB_LED_NUM) == 0) {\n        bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK);\n        bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK);\n    }\n}\n\nbool ducky_numpad_press(BadUsbScript* bad_usb, const char num) {\n    if((num < '0') || (num > '9')) return false;\n\n    uint16_t key = numpad_keys[num - '0'];\n    bad_usb->hid->kb_press(bad_usb->hid_inst, key);\n    bad_usb->hid->kb_release(bad_usb->hid_inst, key);\n\n    return true;\n}\n\nbool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) {\n    uint8_t i = 0;\n    bool state = false;\n\n    bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT);\n\n    while(!ducky_is_line_end(charcode[i])) {\n        state = ducky_numpad_press(bad_usb, charcode[i]);\n        if(state == false) break;\n        i++;\n    }\n\n    bad_usb->hid->kb_release(bad_usb->hid_inst, KEY_MOD_LEFT_ALT);\n    return state;\n}\n\nbool ducky_altstring(BadUsbScript* bad_usb, const char* param) {\n    uint32_t i = 0;\n    bool state = false;\n\n    while(param[i] != '\\0') {\n        if((param[i] < ' ') || (param[i] > '~')) {\n            i++;\n            continue; // Skip non-printable chars\n        }\n\n        char temp_str[4];\n        snprintf(temp_str, 4, \"%u\", param[i]);\n\n        state = ducky_altchar(bad_usb, temp_str);\n        if(state == false) break;\n        i++;\n    }\n    return state;\n}\n\nint32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...) {\n    va_list args;\n    va_start(args, text);\n\n    vsnprintf(bad_usb->st.error, sizeof(bad_usb->st.error), text, args);\n\n    va_end(args);\n    return SCRIPT_STATE_ERROR;\n}\n\nbool ducky_string(BadUsbScript* bad_usb, const char* param) {\n    uint32_t i = 0;\n\n    while(param[i] != '\\0') {\n        if(param[i] != '\\n') {\n            uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);\n            if(keycode != HID_KEYBOARD_NONE) {\n                bad_usb->hid->kb_press(bad_usb->hid_inst, keycode);\n                bad_usb->hid->kb_release(bad_usb->hid_inst, keycode);\n            }\n        } else {\n            bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_RETURN);\n            bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_RETURN);\n        }\n        i++;\n    }\n    bad_usb->stringdelay = 0;\n    return true;\n}\n\nstatic bool ducky_string_next(BadUsbScript* bad_usb) {\n    if(bad_usb->string_print_pos >= furi_string_size(bad_usb->string_print)) {\n        return true;\n    }\n\n    char print_char = furi_string_get_char(bad_usb->string_print, bad_usb->string_print_pos);\n\n    if(print_char != '\\n') {\n        uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, print_char);\n        if(keycode != HID_KEYBOARD_NONE) {\n            bad_usb->hid->kb_press(bad_usb->hid_inst, keycode);\n            bad_usb->hid->kb_release(bad_usb->hid_inst, keycode);\n        }\n    } else {\n        bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_RETURN);\n        bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_RETURN);\n    }\n\n    bad_usb->string_print_pos++;\n\n    return false;\n}\n\nstatic int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {\n    uint32_t line_len = furi_string_size(line);\n    const char* line_cstr = furi_string_get_cstr(line);\n\n    if(line_len == 0) {\n        return SCRIPT_STATE_NEXT_LINE; // Skip empty lines\n    }\n    FURI_LOG_D(WORKER_TAG, \"line:%s\", line_cstr);\n\n    // Ducky Lang Functions\n    int32_t cmd_result = ducky_execute_cmd(bad_usb, line_cstr);\n    if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {\n        return cmd_result;\n    }\n\n    // Mouse Keys\n    uint16_t key = ducky_get_mouse_keycode_by_name(line_cstr);\n    if(key != HID_MOUSE_INVALID) {\n        bad_usb->hid->mouse_press(bad_usb->hid_inst, key);\n        bad_usb->hid->mouse_release(bad_usb->hid_inst, key);\n        return 0;\n    }\n\n    // Parse chain of modifiers linked by spaces and hyphens\n    uint16_t modifiers = 0;\n    while(1) {\n        key = ducky_get_next_modifier_keycode_by_name(&line_cstr);\n        if(key == HID_KEYBOARD_NONE) break;\n\n        modifiers |= key;\n        char next_char = *line_cstr;\n        if(next_char == ' ' || next_char == '-') line_cstr++;\n    }\n\n    // Main key\n    char next_char = *line_cstr;\n    key = modifiers | ducky_get_keycode(bad_usb, line_cstr, false);\n\n    if(key == 0 && next_char) ducky_error(bad_usb, \"No keycode defined for %s\", line_cstr);\n\n    bad_usb->hid->kb_press(bad_usb->hid_inst, key);\n    bad_usb->hid->kb_release(bad_usb->hid_inst, key);\n    return 0;\n}\n\nstatic bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {\n    if(sscanf(line, \"%lX:%lX\", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) {\n        bad_usb->hid_cfg.manuf[0] = '\\0';\n        bad_usb->hid_cfg.product[0] = '\\0';\n\n        uint8_t id_len = ducky_get_command_len(line);\n        if(!ducky_is_line_end(line[id_len + 1])) {\n            sscanf(\n                &line[id_len + 1],\n                \"%31[^\\r\\n:]:%31[^\\r\\n]\",\n                bad_usb->hid_cfg.manuf,\n                bad_usb->hid_cfg.product);\n        }\n        FURI_LOG_D(\n            WORKER_TAG,\n            \"set id: %04lX:%04lX mfr:%s product:%s\",\n            bad_usb->hid_cfg.vid,\n            bad_usb->hid_cfg.pid,\n            bad_usb->hid_cfg.manuf,\n            bad_usb->hid_cfg.product);\n        return true;\n    }\n    return false;\n}\n\nstatic void bad_usb_hid_state_callback(bool state, void* context) {\n    furi_assert(context);\n    BadUsbScript* bad_usb = context;\n\n    if(state == true) {\n        furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect);\n    } else {\n        furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect);\n    }\n}\n\nstatic bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {\n    uint8_t ret = 0;\n    uint32_t line_len = 0;\n\n    furi_string_reset(bad_usb->line);\n\n    do {\n        ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);\n        for(uint16_t i = 0; i < ret; i++) {\n            if(bad_usb->file_buf[i] == '\\n' && line_len > 0) {\n                bad_usb->st.line_nb++;\n                line_len = 0;\n            } else {\n                if(bad_usb->st.line_nb == 0) { // Save first line\n                    furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]);\n                }\n                line_len++;\n            }\n        }\n        if(storage_file_eof(script_file)) {\n            if(line_len > 0) {\n                bad_usb->st.line_nb++;\n                break;\n            }\n        }\n    } while(ret > 0);\n\n    const char* line_tmp = furi_string_get_cstr(bad_usb->line);\n    bool id_set = false; // Looking for ID command at first line\n    if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {\n        id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]);\n    }\n\n    if(id_set) {\n        bad_usb->hid_inst = bad_usb->hid->init(&bad_usb->hid_cfg);\n    } else {\n        bad_usb->hid_inst = bad_usb->hid->init(NULL);\n    }\n    bad_usb->hid->set_state_callback(bad_usb->hid_inst, bad_usb_hid_state_callback, bad_usb);\n\n    storage_file_seek(script_file, 0, true);\n    furi_string_reset(bad_usb->line);\n\n    return true;\n}\n\nstatic int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) {\n    int32_t delay_val = 0;\n\n    if(bad_usb->repeat_cnt > 0) {\n        bad_usb->repeat_cnt--;\n        delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);\n        if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line\n            return 0;\n        } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays\n            return delay_val;\n        } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button\n            return delay_val;\n        } else if(delay_val < 0) { // Script error\n            bad_usb->st.error_line = bad_usb->st.line_cur - 1;\n            FURI_LOG_E(WORKER_TAG, \"Unknown command at line %zu\", bad_usb->st.line_cur - 1U);\n            return SCRIPT_STATE_ERROR;\n        } else {\n            return delay_val + bad_usb->defdelay;\n        }\n    }\n\n    furi_string_set(bad_usb->line_prev, bad_usb->line);\n    furi_string_reset(bad_usb->line);\n\n    while(1) {\n        if(bad_usb->buf_len == 0) {\n            bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);\n            if(storage_file_eof(script_file)) {\n                if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) {\n                    bad_usb->file_buf[bad_usb->buf_len] = '\\n';\n                    bad_usb->buf_len++;\n                    bad_usb->file_end = true;\n                }\n            }\n\n            bad_usb->buf_start = 0;\n            if(bad_usb->buf_len == 0) return SCRIPT_STATE_END;\n        }\n        for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) {\n            if(bad_usb->file_buf[i] == '\\n' && furi_string_size(bad_usb->line) > 0) {\n                bad_usb->st.line_cur++;\n                bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);\n                bad_usb->buf_start = i + 1;\n                furi_string_trim(bad_usb->line);\n                delay_val = ducky_parse_line(bad_usb, bad_usb->line);\n                if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line\n                    return 0;\n                } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays\n                    return delay_val;\n                } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button\n                    return delay_val;\n                } else if(delay_val < 0) {\n                    bad_usb->st.error_line = bad_usb->st.line_cur;\n                    FURI_LOG_E(WORKER_TAG, \"Unknown command at line %zu\", bad_usb->st.line_cur);\n                    return SCRIPT_STATE_ERROR;\n                } else {\n                    return delay_val + bad_usb->defdelay;\n                }\n            } else {\n                furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]);\n            }\n        }\n        bad_usb->buf_len = 0;\n        if(bad_usb->file_end) return SCRIPT_STATE_END;\n    }\n\n    return 0;\n}\n\nstatic uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) {\n    uint32_t flags = furi_thread_flags_get();\n    furi_check((flags & FuriFlagError) == 0);\n    if(flags == 0) {\n        flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);\n        furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));\n    } else {\n        uint32_t state = furi_thread_flags_clear(flags);\n        furi_check((state & FuriFlagError) == 0);\n    }\n    return flags;\n}\n\nstatic int32_t bad_usb_worker(void* context) {\n    BadUsbScript* bad_usb = context;\n\n    BadUsbWorkerState worker_state = BadUsbStateInit;\n    BadUsbWorkerState pause_state = BadUsbStateRunning;\n    int32_t delay_val = 0;\n\n    FURI_LOG_I(WORKER_TAG, \"Init\");\n    File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));\n    bad_usb->line = furi_string_alloc();\n    bad_usb->line_prev = furi_string_alloc();\n    bad_usb->string_print = furi_string_alloc();\n\n    while(1) {\n        if(worker_state == BadUsbStateInit) { // State: initialization\n            if(storage_file_open(\n                   script_file,\n                   furi_string_get_cstr(bad_usb->file_path),\n                   FSAM_READ,\n                   FSOM_OPEN_EXISTING)) {\n                if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) {\n                    if(bad_usb->hid->is_connected(bad_usb->hid_inst)) {\n                        worker_state = BadUsbStateIdle; // Ready to run\n                    } else {\n                        worker_state = BadUsbStateNotConnected; // USB not connected\n                    }\n                } else {\n                    worker_state = BadUsbStateScriptError; // Script preload error\n                }\n            } else {\n                FURI_LOG_E(WORKER_TAG, \"File open error\");\n                worker_state = BadUsbStateFileError; // File open error\n            }\n            bad_usb->st.state = worker_state;\n\n        } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,\n                FuriWaitForever);\n\n            if(flags & WorkerEvtEnd) {\n                break;\n            } else if(flags & WorkerEvtConnect) {\n                worker_state = BadUsbStateIdle; // Ready to run\n            } else if(flags & WorkerEvtStartStop) {\n                worker_state = BadUsbStateWillRun; // Will run when USB is connected\n            }\n            bad_usb->st.state = worker_state;\n\n        } else if(worker_state == BadUsbStateIdle) { // State: ready to start\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever);\n\n            if(flags & WorkerEvtEnd) {\n                break;\n            } else if(flags & WorkerEvtStartStop) { // Start executing script\n                dolphin_deed(DolphinDeedBadUsbPlayScript);\n                delay_val = 0;\n                bad_usb->buf_len = 0;\n                bad_usb->st.line_cur = 0;\n                bad_usb->defdelay = 0;\n                bad_usb->stringdelay = 0;\n                bad_usb->defstringdelay = 0;\n                bad_usb->repeat_cnt = 0;\n                bad_usb->key_hold_nb = 0;\n                bad_usb->file_end = false;\n                storage_file_seek(script_file, 0, true);\n                worker_state = BadUsbStateRunning;\n            } else if(flags & WorkerEvtDisconnect) {\n                worker_state = BadUsbStateNotConnected; // USB disconnected\n            }\n            bad_usb->st.state = worker_state;\n\n        } else if(worker_state == BadUsbStateWillRun) { // State: start on connection\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);\n\n            if(flags & WorkerEvtEnd) {\n                break;\n            } else if(flags & WorkerEvtConnect) { // Start executing script\n                dolphin_deed(DolphinDeedBadUsbPlayScript);\n                delay_val = 0;\n                bad_usb->buf_len = 0;\n                bad_usb->st.line_cur = 0;\n                bad_usb->defdelay = 0;\n                bad_usb->stringdelay = 0;\n                bad_usb->defstringdelay = 0;\n                bad_usb->repeat_cnt = 0;\n                bad_usb->file_end = false;\n                storage_file_seek(script_file, 0, true);\n                // extra time for PC to recognize Flipper as keyboard\n                flags = furi_thread_flags_wait(\n                    WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop,\n                    FuriFlagWaitAny | FuriFlagNoClear,\n                    1500);\n                if(flags == (unsigned)FuriFlagErrorTimeout) {\n                    // If nothing happened - start script execution\n                    worker_state = BadUsbStateRunning;\n                } else if(flags & WorkerEvtStartStop) {\n                    worker_state = BadUsbStateIdle;\n                    furi_thread_flags_clear(WorkerEvtStartStop);\n                }\n            } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution\n                worker_state = BadUsbStateNotConnected;\n            }\n            bad_usb->st.state = worker_state;\n\n        } else if(worker_state == BadUsbStateRunning) { // State: running\n            uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);\n            uint32_t flags = furi_thread_flags_wait(\n                WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,\n                FuriFlagWaitAny,\n                delay_cur);\n\n            delay_val -= delay_cur;\n            if(!(flags & FuriFlagError)) {\n                if(flags & WorkerEvtEnd) {\n                    break;\n                } else if(flags & WorkerEvtStartStop) {\n                    worker_state = BadUsbStateIdle; // Stop executing script\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtDisconnect) {\n                    worker_state = BadUsbStateNotConnected; // USB disconnected\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtPauseResume) {\n                    pause_state = BadUsbStateRunning;\n                    worker_state = BadUsbStatePaused; // Pause\n                }\n                bad_usb->st.state = worker_state;\n                continue;\n            } else if(\n                (flags == (unsigned)FuriFlagErrorTimeout) ||\n                (flags == (unsigned)FuriFlagErrorResource)) {\n                if(delay_val > 0) {\n                    bad_usb->st.delay_remain--;\n                    continue;\n                }\n                bad_usb->st.state = BadUsbStateRunning;\n                delay_val = ducky_script_execute_next(bad_usb, script_file);\n                if(delay_val == SCRIPT_STATE_ERROR) { // Script error\n                    delay_val = 0;\n                    worker_state = BadUsbStateScriptError;\n                    bad_usb->st.state = worker_state;\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(delay_val == SCRIPT_STATE_END) { // End of script\n                    delay_val = 0;\n                    worker_state = BadUsbStateIdle;\n                    bad_usb->st.state = BadUsbStateDone;\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                    continue;\n                } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays\n                    delay_val = bad_usb->defdelay;\n                    bad_usb->string_print_pos = 0;\n                    worker_state = BadUsbStateStringDelay;\n                } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input\n                    worker_state = BadUsbStateWaitForBtn;\n                    bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays\n                } else if(delay_val > 1000) {\n                    bad_usb->st.state = BadUsbStateDelay; // Show long delays\n                    bad_usb->st.delay_remain = delay_val / 1000;\n                }\n            } else {\n                furi_check((flags & FuriFlagError) == 0);\n            }\n        } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,\n                FuriWaitForever);\n            if(!(flags & FuriFlagError)) {\n                if(flags & WorkerEvtEnd) {\n                    break;\n                } else if(flags & WorkerEvtStartStop) {\n                    delay_val = 0;\n                    worker_state = BadUsbStateRunning;\n                } else if(flags & WorkerEvtDisconnect) {\n                    worker_state = BadUsbStateNotConnected; // USB disconnected\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                }\n                bad_usb->st.state = worker_state;\n                continue;\n            }\n        } else if(worker_state == BadUsbStatePaused) { // State: Paused\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,\n                FuriWaitForever);\n            if(!(flags & FuriFlagError)) {\n                if(flags & WorkerEvtEnd) {\n                    break;\n                } else if(flags & WorkerEvtStartStop) {\n                    worker_state = BadUsbStateIdle; // Stop executing script\n                    bad_usb->st.state = worker_state;\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtDisconnect) {\n                    worker_state = BadUsbStateNotConnected; // USB disconnected\n                    bad_usb->st.state = worker_state;\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtPauseResume) {\n                    if(pause_state == BadUsbStateRunning) {\n                        if(delay_val > 0) {\n                            bad_usb->st.state = BadUsbStateDelay;\n                            bad_usb->st.delay_remain = delay_val / 1000;\n                        } else {\n                            bad_usb->st.state = BadUsbStateRunning;\n                            delay_val = 0;\n                        }\n                        worker_state = BadUsbStateRunning; // Resume\n                    } else if(pause_state == BadUsbStateStringDelay) {\n                        bad_usb->st.state = BadUsbStateRunning;\n                        worker_state = BadUsbStateStringDelay; // Resume\n                    }\n                }\n                continue;\n            }\n        } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays\n            uint32_t delay = (bad_usb->stringdelay == 0) ? bad_usb->defstringdelay :\n                                                           bad_usb->stringdelay;\n            uint32_t flags = bad_usb_flags_get(\n                WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,\n                delay);\n\n            if(!(flags & FuriFlagError)) {\n                if(flags & WorkerEvtEnd) {\n                    break;\n                } else if(flags & WorkerEvtStartStop) {\n                    worker_state = BadUsbStateIdle; // Stop executing script\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtDisconnect) {\n                    worker_state = BadUsbStateNotConnected; // USB disconnected\n                    bad_usb->hid->release_all(bad_usb->hid_inst);\n                } else if(flags & WorkerEvtPauseResume) {\n                    pause_state = BadUsbStateStringDelay;\n                    worker_state = BadUsbStatePaused; // Pause\n                }\n                bad_usb->st.state = worker_state;\n                continue;\n            } else if(\n                (flags == (unsigned)FuriFlagErrorTimeout) ||\n                (flags == (unsigned)FuriFlagErrorResource)) {\n                bool string_end = ducky_string_next(bad_usb);\n                if(string_end) {\n                    bad_usb->stringdelay = 0;\n                    worker_state = BadUsbStateRunning;\n                }\n            } else {\n                furi_check((flags & FuriFlagError) == 0);\n            }\n        } else if(\n            (worker_state == BadUsbStateFileError) ||\n            (worker_state == BadUsbStateScriptError)) { // State: error\n            uint32_t flags =\n                bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command\n\n            if(flags & WorkerEvtEnd) {\n                break;\n            }\n        }\n    }\n\n    bad_usb->hid->set_state_callback(bad_usb->hid_inst, NULL, NULL);\n    bad_usb->hid->deinit(bad_usb->hid_inst);\n\n    storage_file_close(script_file);\n    storage_file_free(script_file);\n    furi_string_free(bad_usb->line);\n    furi_string_free(bad_usb->line_prev);\n    furi_string_free(bad_usb->string_print);\n\n    FURI_LOG_I(WORKER_TAG, \"End\");\n\n    return 0;\n}\n\nstatic void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {\n    furi_assert(bad_usb);\n    memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout));\n    memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));\n}\n\nBadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface) {\n    furi_assert(file_path);\n\n    BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));\n    bad_usb->file_path = furi_string_alloc();\n    furi_string_set(bad_usb->file_path, file_path);\n    bad_usb_script_set_default_keyboard_layout(bad_usb);\n\n    bad_usb->st.state = BadUsbStateInit;\n    bad_usb->st.error[0] = '\\0';\n    bad_usb->hid = bad_usb_hid_get_interface(interface);\n\n    bad_usb->thread = furi_thread_alloc_ex(\"BadUsbWorker\", 2048, bad_usb_worker, bad_usb);\n    furi_thread_start(bad_usb->thread);\n    return bad_usb;\n} //-V773\n\nvoid bad_usb_script_close(BadUsbScript* bad_usb) {\n    furi_assert(bad_usb);\n    furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd);\n    furi_thread_join(bad_usb->thread);\n    furi_thread_free(bad_usb->thread);\n    furi_string_free(bad_usb->file_path);\n    free(bad_usb);\n}\n\nvoid bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) {\n    furi_assert(bad_usb);\n\n    if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) {\n        // do not update keyboard layout while a script is running\n        return;\n    }\n\n    File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));\n    if(!furi_string_empty(layout_path)) { //-V1051\n        if(storage_file_open(\n               layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {\n            uint16_t layout[128];\n            if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {\n                memcpy(bad_usb->layout, layout, sizeof(layout));\n            }\n        }\n        storage_file_close(layout_file);\n    } else {\n        bad_usb_script_set_default_keyboard_layout(bad_usb);\n    }\n    storage_file_free(layout_file);\n}\n\nvoid bad_usb_script_start_stop(BadUsbScript* bad_usb) {\n    furi_assert(bad_usb);\n    furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtStartStop);\n}\n\nvoid bad_usb_script_pause_resume(BadUsbScript* bad_usb) {\n    furi_assert(bad_usb);\n    furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume);\n}\n\nBadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) {\n    furi_assert(bad_usb);\n    return &(bad_usb->st);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/ducky_script.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n#include <furi_hal.h>\n#include \"bad_usb_hid.h\"\n\ntypedef enum {\n    BadUsbStateInit,\n    BadUsbStateNotConnected,\n    BadUsbStateIdle,\n    BadUsbStateWillRun,\n    BadUsbStateRunning,\n    BadUsbStateDelay,\n    BadUsbStateStringDelay,\n    BadUsbStateWaitForBtn,\n    BadUsbStatePaused,\n    BadUsbStateDone,\n    BadUsbStateScriptError,\n    BadUsbStateFileError,\n} BadUsbWorkerState;\n\ntypedef struct {\n    BadUsbWorkerState state;\n    size_t line_cur;\n    size_t line_nb;\n    uint32_t delay_remain;\n    size_t error_line;\n    char error[64];\n} BadUsbState;\n\ntypedef struct BadUsbScript BadUsbScript;\n\nBadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface);\n\nvoid bad_usb_script_close(BadUsbScript* bad_usb);\n\nvoid bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path);\n\nvoid bad_usb_script_start(BadUsbScript* bad_usb);\n\nvoid bad_usb_script_stop(BadUsbScript* bad_usb);\n\nvoid bad_usb_script_start_stop(BadUsbScript* bad_usb);\n\nvoid bad_usb_script_pause_resume(BadUsbScript* bad_usb);\n\nBadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/ducky_script_commands.c",
    "content": "#include <furi_hal.h>\n#include <lib/toolbox/strint.h>\n#include \"ducky_script.h\"\n#include \"ducky_script_i.h\"\n\ntypedef int32_t (*DuckyCmdCallback)(BadUsbScript* bad_usb, const char* line, int32_t param);\n\ntypedef struct {\n    char* name;\n    DuckyCmdCallback callback;\n    int32_t param;\n} DuckyCmd;\n\nstatic int32_t ducky_fnc_delay(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    uint32_t delay_val = 0;\n    bool state = ducky_get_number(line, &delay_val);\n    if((state) && (delay_val > 0)) {\n        return (int32_t)delay_val;\n    }\n\n    return ducky_error(bad_usb, \"Invalid number %s\", line);\n}\n\nstatic int32_t ducky_fnc_defdelay(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    bool state = ducky_get_number(line, &bad_usb->defdelay);\n    if(!state) {\n        return ducky_error(bad_usb, \"Invalid number %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_strdelay(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    bool state = ducky_get_number(line, &bad_usb->stringdelay);\n    if(!state) {\n        return ducky_error(bad_usb, \"Invalid number %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_defstrdelay(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    bool state = ducky_get_number(line, &bad_usb->defstringdelay);\n    if(!state) {\n        return ducky_error(bad_usb, \"Invalid number %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_string(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    line = &line[ducky_get_command_len(line) + 1];\n    furi_string_set_str(bad_usb->string_print, line);\n    if(param == 1) {\n        furi_string_cat(bad_usb->string_print, \"\\n\");\n    }\n\n    if(bad_usb->stringdelay == 0 &&\n       bad_usb->defstringdelay == 0) { // stringdelay not set - run command immediately\n        bool state = ducky_string(bad_usb, furi_string_get_cstr(bad_usb->string_print));\n        if(!state) {\n            return ducky_error(bad_usb, \"Invalid string %s\", line);\n        }\n    } else { // stringdelay is set - run command in thread to keep handling external events\n        return SCRIPT_STATE_STRING_START;\n    }\n\n    return 0;\n}\n\nstatic int32_t ducky_fnc_repeat(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    bool state = ducky_get_number(line, &bad_usb->repeat_cnt);\n    if((!state) || (bad_usb->repeat_cnt == 0)) {\n        return ducky_error(bad_usb, \"Invalid number %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    uint16_t key = ducky_get_keycode(bad_usb, line, false);\n    bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);\n    bad_usb->hid->kb_press(bad_usb->hid_inst, key);\n    bad_usb->hid->release_all(bad_usb->hid_inst);\n    return 0;\n}\n\nstatic int32_t ducky_fnc_altchar(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    ducky_numlock_on(bad_usb);\n    bool state = ducky_altchar(bad_usb, line);\n    if(!state) {\n        return ducky_error(bad_usb, \"Invalid altchar %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_altstring(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    ducky_numlock_on(bad_usb);\n    bool state = ducky_altstring(bad_usb, line);\n    if(!state) {\n        return ducky_error(bad_usb, \"Invalid altstring %s\", line);\n    }\n    return 0;\n}\n\nstatic int32_t ducky_fnc_hold(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n    line = &line[ducky_get_command_len(line) + 1];\n\n    if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {\n        return ducky_error(bad_usb, \"Too many keys are held\");\n    }\n\n    // Handle Mouse Keys here\n    uint16_t key = ducky_get_mouse_keycode_by_name(line);\n    if(key != HID_MOUSE_NONE) {\n        bad_usb->key_hold_nb++;\n        bad_usb->hid->mouse_press(bad_usb->hid_inst, key);\n        return 0;\n    }\n\n    // Handle Keyboard keys here\n    key = ducky_get_keycode(bad_usb, line, true);\n    if(key != HID_KEYBOARD_NONE) {\n        bad_usb->key_hold_nb++;\n        bad_usb->hid->kb_press(bad_usb->hid_inst, key);\n        return 0;\n    }\n\n    // keyboard and mouse were none\n    return ducky_error(bad_usb, \"Unknown keycode for %s\", line);\n}\n\nstatic int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n    line = &line[ducky_get_command_len(line) + 1];\n\n    if(bad_usb->key_hold_nb == 0) {\n        return ducky_error(bad_usb, \"No keys are held\");\n    }\n\n    // Handle Mouse Keys here\n    uint16_t key = ducky_get_mouse_keycode_by_name(line);\n    if(key != HID_MOUSE_NONE) {\n        bad_usb->key_hold_nb--;\n        bad_usb->hid->mouse_release(bad_usb->hid_inst, key);\n        return 0;\n    }\n\n    //Handle Keyboard Keys here\n    key = ducky_get_keycode(bad_usb, line, true);\n    if(key != HID_KEYBOARD_NONE) {\n        bad_usb->key_hold_nb--;\n        bad_usb->hid->kb_release(bad_usb->hid_inst, key);\n        return 0;\n    }\n\n    // keyboard and mouse were none\n    return ducky_error(bad_usb, \"No keycode defined for %s\", line);\n}\n\nstatic int32_t ducky_fnc_media(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    uint16_t key = ducky_get_media_keycode_by_name(line);\n    if(key == HID_CONSUMER_UNASSIGNED) {\n        return ducky_error(bad_usb, \"No keycode defined for %s\", line);\n    }\n    bad_usb->hid->consumer_press(bad_usb->hid_inst, key);\n    bad_usb->hid->consumer_release(bad_usb->hid_inst, key);\n    return 0;\n}\n\nstatic int32_t ducky_fnc_globe(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[ducky_get_command_len(line) + 1];\n    uint16_t key = ducky_get_keycode(bad_usb, line, false);\n    if(key == HID_KEYBOARD_NONE) {\n        return ducky_error(bad_usb, \"No keycode defined for %s\", line);\n    }\n\n    bad_usb->hid->consumer_press(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE);\n    bad_usb->hid->kb_press(bad_usb->hid_inst, key);\n    bad_usb->hid->kb_release(bad_usb->hid_inst, key);\n    bad_usb->hid->consumer_release(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE);\n    return 0;\n}\n\nstatic int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n    UNUSED(bad_usb);\n    UNUSED(line);\n\n    return SCRIPT_STATE_WAIT_FOR_BTN;\n}\n\nstatic int32_t ducky_fnc_mouse_scroll(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[strcspn(line, \" \") + 1];\n    int32_t mouse_scroll_dist = 0;\n\n    if(strint_to_int32(line, NULL, &mouse_scroll_dist, 10) != StrintParseNoError) {\n        return ducky_error(bad_usb, \"Invalid Number %s\", line);\n    }\n\n    bad_usb->hid->mouse_scroll(bad_usb->hid_inst, mouse_scroll_dist);\n\n    return 0;\n}\n\nstatic int32_t ducky_fnc_mouse_move(BadUsbScript* bad_usb, const char* line, int32_t param) {\n    UNUSED(param);\n\n    line = &line[strcspn(line, \" \") + 1];\n    int32_t mouse_move_x = 0;\n    int32_t mouse_move_y = 0;\n\n    if(strint_to_int32(line, NULL, &mouse_move_x, 10) != StrintParseNoError) {\n        return ducky_error(bad_usb, \"Invalid Number %s\", line);\n    }\n\n    line = &line[strcspn(line, \" \") + 1];\n\n    if(strint_to_int32(line, NULL, &mouse_move_y, 10) != StrintParseNoError) {\n        return ducky_error(bad_usb, \"Invalid Number %s\", line);\n    }\n\n    bad_usb->hid->mouse_move(bad_usb->hid_inst, mouse_move_x, mouse_move_y);\n\n    return 0;\n}\n\nstatic const DuckyCmd ducky_commands[] = {\n    {\"REM\", NULL, -1},\n    {\"ID\", NULL, -1},\n    {\"DELAY\", ducky_fnc_delay, -1},\n    {\"STRING\", ducky_fnc_string, 0},\n    {\"STRINGLN\", ducky_fnc_string, 1},\n    {\"DEFAULT_DELAY\", ducky_fnc_defdelay, -1},\n    {\"DEFAULTDELAY\", ducky_fnc_defdelay, -1},\n    {\"STRINGDELAY\", ducky_fnc_strdelay, -1},\n    {\"STRING_DELAY\", ducky_fnc_strdelay, -1},\n    {\"DEFAULT_STRING_DELAY\", ducky_fnc_defstrdelay, -1},\n    {\"DEFAULTSTRINGDELAY\", ducky_fnc_defstrdelay, -1},\n    {\"REPEAT\", ducky_fnc_repeat, -1},\n    {\"SYSRQ\", ducky_fnc_sysrq, -1},\n    {\"ALTCHAR\", ducky_fnc_altchar, -1},\n    {\"ALTSTRING\", ducky_fnc_altstring, -1},\n    {\"ALTCODE\", ducky_fnc_altstring, -1},\n    {\"HOLD\", ducky_fnc_hold, -1},\n    {\"RELEASE\", ducky_fnc_release, -1},\n    {\"WAIT_FOR_BUTTON_PRESS\", ducky_fnc_waitforbutton, -1},\n    {\"MEDIA\", ducky_fnc_media, -1},\n    {\"GLOBE\", ducky_fnc_globe, -1},\n    {\"MOUSEMOVE\", ducky_fnc_mouse_move, -1},\n    {\"MOUSE_MOVE\", ducky_fnc_mouse_move, -1},\n    {\"MOUSESCROLL\", ducky_fnc_mouse_scroll, -1},\n    {\"MOUSE_SCROLL\", ducky_fnc_mouse_scroll, -1},\n};\n\n#define TAG \"BadUsb\"\n\n#define WORKER_TAG TAG \"Worker\"\n\nint32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {\n    size_t cmd_word_len = strcspn(line, \" \");\n    for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {\n        size_t cmd_compare_len = strlen(ducky_commands[i].name);\n\n        if(cmd_compare_len != cmd_word_len) {\n            continue;\n        }\n\n        if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {\n            if(ducky_commands[i].callback == NULL) {\n                return 0;\n            } else {\n                return (ducky_commands[i].callback)(bad_usb, line, ducky_commands[i].param);\n            }\n        }\n    }\n\n    return SCRIPT_STATE_CMD_UNKNOWN;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/ducky_script_i.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n#include <furi_hal.h>\n#include \"ducky_script.h\"\n#include \"bad_usb_hid.h\"\n\n#define SCRIPT_STATE_ERROR        (-1)\n#define SCRIPT_STATE_END          (-2)\n#define SCRIPT_STATE_NEXT_LINE    (-3)\n#define SCRIPT_STATE_CMD_UNKNOWN  (-4)\n#define SCRIPT_STATE_STRING_START (-5)\n#define SCRIPT_STATE_WAIT_FOR_BTN (-6)\n\n#define FILE_BUFFER_LEN 16\n\n#define HID_MOUSE_INVALID 0\n#define HID_MOUSE_NONE    0\n\nstruct BadUsbScript {\n    FuriHalUsbHidConfig hid_cfg;\n    const BadUsbHidApi* hid;\n    void* hid_inst;\n    FuriThread* thread;\n    BadUsbState st;\n\n    FuriString* file_path;\n    uint8_t file_buf[FILE_BUFFER_LEN + 1];\n    uint8_t buf_start;\n    uint8_t buf_len;\n    bool file_end;\n\n    uint32_t defdelay;\n    uint32_t stringdelay;\n    uint32_t defstringdelay;\n    uint16_t layout[128];\n\n    FuriString* line;\n    FuriString* line_prev;\n    uint32_t repeat_cnt;\n    uint8_t key_hold_nb;\n\n    FuriString* string_print;\n    size_t string_print_pos;\n};\n\nuint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers);\n\nuint32_t ducky_get_command_len(const char* line);\n\nbool ducky_is_line_end(const char chr);\n\nuint16_t ducky_get_next_modifier_keycode_by_name(const char** param);\n\nuint16_t ducky_get_modifier_keycode_by_name(const char* param);\n\nuint16_t ducky_get_keycode_by_name(const char* param);\n\nuint16_t ducky_get_media_keycode_by_name(const char* param);\n\nuint8_t ducky_get_mouse_keycode_by_name(const char* param);\n\nbool ducky_get_number(const char* param, uint32_t* val);\n\nvoid ducky_numlock_on(BadUsbScript* bad_usb);\n\nbool ducky_numpad_press(BadUsbScript* bad_usb, const char num);\n\nbool ducky_altchar(BadUsbScript* bad_usb, const char* charcode);\n\nbool ducky_altstring(BadUsbScript* bad_usb, const char* param);\n\nbool ducky_string(BadUsbScript* bad_usb, const char* param);\n\nint32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line);\n\nint32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/bad_usb/helpers/ducky_script_keycodes.c",
    "content": "#include <furi_hal.h>\n#include \"ducky_script_i.h\"\n\ntypedef struct {\n    char* name;\n    uint16_t keycode;\n} DuckyKey;\n\nstatic const DuckyKey ducky_modifier_keys[] = {\n    {\"CTRL\", KEY_MOD_LEFT_CTRL},\n    {\"CONTROL\", KEY_MOD_LEFT_CTRL},\n    {\"SHIFT\", KEY_MOD_LEFT_SHIFT},\n    {\"ALT\", KEY_MOD_LEFT_ALT},\n    {\"GUI\", KEY_MOD_LEFT_GUI},\n    {\"WINDOWS\", KEY_MOD_LEFT_GUI},\n};\n\nstatic const DuckyKey ducky_keys[] = {\n    {\"DOWNARROW\", HID_KEYBOARD_DOWN_ARROW},\n    {\"DOWN\", HID_KEYBOARD_DOWN_ARROW},\n    {\"LEFTARROW\", HID_KEYBOARD_LEFT_ARROW},\n    {\"LEFT\", HID_KEYBOARD_LEFT_ARROW},\n    {\"RIGHTARROW\", HID_KEYBOARD_RIGHT_ARROW},\n    {\"RIGHT\", HID_KEYBOARD_RIGHT_ARROW},\n    {\"UPARROW\", HID_KEYBOARD_UP_ARROW},\n    {\"UP\", HID_KEYBOARD_UP_ARROW},\n\n    {\"ENTER\", HID_KEYBOARD_RETURN},\n    {\"BREAK\", HID_KEYBOARD_PAUSE},\n    {\"PAUSE\", HID_KEYBOARD_PAUSE},\n    {\"CAPSLOCK\", HID_KEYBOARD_CAPS_LOCK},\n    {\"DELETE\", HID_KEYBOARD_DELETE_FORWARD},\n    {\"BACKSPACE\", HID_KEYBOARD_DELETE},\n    {\"END\", HID_KEYBOARD_END},\n    {\"ESC\", HID_KEYBOARD_ESCAPE},\n    {\"ESCAPE\", HID_KEYBOARD_ESCAPE},\n    {\"HOME\", HID_KEYBOARD_HOME},\n    {\"INSERT\", HID_KEYBOARD_INSERT},\n    {\"NUMLOCK\", HID_KEYPAD_NUMLOCK},\n    {\"PAGEUP\", HID_KEYBOARD_PAGE_UP},\n    {\"PAGEDOWN\", HID_KEYBOARD_PAGE_DOWN},\n    {\"PRINTSCREEN\", HID_KEYBOARD_PRINT_SCREEN},\n    {\"SCROLLLOCK\", HID_KEYBOARD_SCROLL_LOCK},\n    {\"SPACE\", HID_KEYBOARD_SPACEBAR},\n    {\"TAB\", HID_KEYBOARD_TAB},\n    {\"MENU\", HID_KEYBOARD_APPLICATION},\n    {\"APP\", HID_KEYBOARD_APPLICATION},\n\n    {\"F1\", HID_KEYBOARD_F1},\n    {\"F2\", HID_KEYBOARD_F2},\n    {\"F3\", HID_KEYBOARD_F3},\n    {\"F4\", HID_KEYBOARD_F4},\n    {\"F5\", HID_KEYBOARD_F5},\n    {\"F6\", HID_KEYBOARD_F6},\n    {\"F7\", HID_KEYBOARD_F7},\n    {\"F8\", HID_KEYBOARD_F8},\n    {\"F9\", HID_KEYBOARD_F9},\n    {\"F10\", HID_KEYBOARD_F10},\n    {\"F11\", HID_KEYBOARD_F11},\n    {\"F12\", HID_KEYBOARD_F12},\n    {\"F13\", HID_KEYBOARD_F13},\n    {\"F14\", HID_KEYBOARD_F14},\n    {\"F15\", HID_KEYBOARD_F15},\n    {\"F16\", HID_KEYBOARD_F16},\n    {\"F17\", HID_KEYBOARD_F17},\n    {\"F18\", HID_KEYBOARD_F18},\n    {\"F19\", HID_KEYBOARD_F19},\n    {\"F20\", HID_KEYBOARD_F20},\n    {\"F21\", HID_KEYBOARD_F21},\n    {\"F22\", HID_KEYBOARD_F22},\n    {\"F23\", HID_KEYBOARD_F23},\n    {\"F24\", HID_KEYBOARD_F24},\n};\n\nstatic const DuckyKey ducky_media_keys[] = {\n    {\"POWER\", HID_CONSUMER_POWER},\n    {\"REBOOT\", HID_CONSUMER_RESET},\n    {\"SLEEP\", HID_CONSUMER_SLEEP},\n    {\"LOGOFF\", HID_CONSUMER_AL_LOGOFF},\n\n    {\"EXIT\", HID_CONSUMER_AC_EXIT},\n    {\"HOME\", HID_CONSUMER_AC_HOME},\n    {\"BACK\", HID_CONSUMER_AC_BACK},\n    {\"FORWARD\", HID_CONSUMER_AC_FORWARD},\n    {\"REFRESH\", HID_CONSUMER_AC_REFRESH},\n\n    {\"SNAPSHOT\", HID_CONSUMER_SNAPSHOT},\n\n    {\"PLAY\", HID_CONSUMER_PLAY},\n    {\"PAUSE\", HID_CONSUMER_PAUSE},\n    {\"PLAY_PAUSE\", HID_CONSUMER_PLAY_PAUSE},\n    {\"NEXT_TRACK\", HID_CONSUMER_SCAN_NEXT_TRACK},\n    {\"PREV_TRACK\", HID_CONSUMER_SCAN_PREVIOUS_TRACK},\n    {\"STOP\", HID_CONSUMER_STOP},\n    {\"EJECT\", HID_CONSUMER_EJECT},\n\n    {\"MUTE\", HID_CONSUMER_MUTE},\n    {\"VOLUME_UP\", HID_CONSUMER_VOLUME_INCREMENT},\n    {\"VOLUME_DOWN\", HID_CONSUMER_VOLUME_DECREMENT},\n\n    {\"FN\", HID_CONSUMER_FN_GLOBE},\n    {\"BRIGHT_UP\", HID_CONSUMER_BRIGHTNESS_INCREMENT},\n    {\"BRIGHT_DOWN\", HID_CONSUMER_BRIGHTNESS_DECREMENT},\n};\n\nstatic const DuckyKey ducky_mouse_keys[] = {\n    {\"LEFTCLICK\", HID_MOUSE_BTN_LEFT},\n    {\"LEFT_CLICK\", HID_MOUSE_BTN_LEFT},\n    {\"RIGHTCLICK\", HID_MOUSE_BTN_RIGHT},\n    {\"RIGHT_CLICK\", HID_MOUSE_BTN_RIGHT},\n    {\"MIDDLECLICK\", HID_MOUSE_BTN_WHEEL},\n    {\"MIDDLE_CLICK\", HID_MOUSE_BTN_WHEEL},\n    {\"WHEELCLICK\", HID_MOUSE_BTN_WHEEL},\n    {\"WHEEL_CLICK\", HID_MOUSE_BTN_WHEEL},\n};\n\nuint16_t ducky_get_next_modifier_keycode_by_name(const char** param) {\n    const char* input_str = *param;\n\n    for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) {\n        size_t key_cmd_len = strlen(ducky_modifier_keys[i].name);\n        if((strncmp(input_str, ducky_modifier_keys[i].name, key_cmd_len) == 0)) {\n            char next_char_after_key = input_str[key_cmd_len];\n            if(ducky_is_line_end(next_char_after_key) || (next_char_after_key == '-')) {\n                *param = &input_str[key_cmd_len];\n                return ducky_modifier_keys[i].keycode;\n            }\n        }\n    }\n\n    return HID_KEYBOARD_NONE;\n}\n\nuint16_t ducky_get_modifier_keycode_by_name(const char* param) {\n    for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) {\n        size_t key_cmd_len = strlen(ducky_modifier_keys[i].name);\n        if((strncmp(param, ducky_modifier_keys[i].name, key_cmd_len) == 0) &&\n           (ducky_is_line_end(param[key_cmd_len]))) {\n            return ducky_modifier_keys[i].keycode;\n        }\n    }\n\n    return HID_KEYBOARD_NONE;\n}\n\nuint16_t ducky_get_keycode_by_name(const char* param) {\n    for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {\n        size_t key_cmd_len = strlen(ducky_keys[i].name);\n        if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&\n           (ducky_is_line_end(param[key_cmd_len]))) {\n            return ducky_keys[i].keycode;\n        }\n    }\n\n    return HID_KEYBOARD_NONE;\n}\n\nuint16_t ducky_get_media_keycode_by_name(const char* param) {\n    for(size_t i = 0; i < COUNT_OF(ducky_media_keys); i++) {\n        size_t key_cmd_len = strlen(ducky_media_keys[i].name);\n        if((strncmp(param, ducky_media_keys[i].name, key_cmd_len) == 0) &&\n           (ducky_is_line_end(param[key_cmd_len]))) {\n            return ducky_media_keys[i].keycode;\n        }\n    }\n\n    return HID_CONSUMER_UNASSIGNED;\n}\n\nuint8_t ducky_get_mouse_keycode_by_name(const char* param) {\n    for(size_t i = 0; i < COUNT_OF(ducky_mouse_keys); i++) {\n        size_t key_cmd_len = strlen(ducky_mouse_keys[i].name);\n        if((strncmp(param, ducky_mouse_keys[i].name, key_cmd_len) == 0) &&\n           (ducky_is_line_end(param[key_cmd_len]))) {\n            return ducky_mouse_keys[i].keycode;\n        }\n    }\n\n    return HID_MOUSE_INVALID;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/Install_qFlipper_gnome.txt",
    "content": "ID 1234:abcd Generic:USB Keyboard\nREM Declare ourselves as a generic usb keyboard\n\nREM This will install qFlipper on Linux/Gnome, using the latest AppImage package\n\nREM Open a terminal\nALT F2\nDELAY 1000\nSTRINGLN gnome-terminal --maximize\nDELAY 1000\n\nREM Ensure we have a folder to run executables from\nSTRINGLN mkdir -p $HOME/.local/bin\n\nREM Download the latest AppImage\nSTRINGLN curl -fsSL \"https://update.flipperzero.one/qFlipper/release/linux-amd64/AppImage\" -o \"$HOME/.local/bin/qFlipper\"\nDELAY 1000\n\nREM Make it executable\nSTRINGLN chmod +x $HOME/.local/bin/qFlipper\n\nREM Extract the appimage in /tmp to install icon and .desktop file\nSTRINGLN cd /tmp\nSTRINGLN $HOME/.local/bin/qFlipper --appimage-extract > /dev/null\nSTRINGLN sed \"s@Exec=qFlipper@Exec=$HOME/.local/bin/qFlipper@\" squashfs-root/usr/share/applications/qFlipper.desktop > $HOME/.local/share/applications/qFlipper.desktop\nSTRINGLN mkdir -p $HOME/.local/share/icons/hicolor/512x512/apps\nSTRINGLN cp squashfs-root/usr/share/icons/hicolor/512x512/apps/qFlipper.png $HOME/.local/share/icons/hicolor/512x512/apps/qFlipper.png\nSTRINGLN rm -rf squashfs-root\nSTRINGLN cd\n\nREM Depending on the Linux distribution and display manager\nREM there might be several ways to update desktop entries\nREM try all\nSTRINGLN xdg-desktop-menu forceupdate || true\nSTRINGLN update-desktop-database ~/.local/share/applications || true\n\nSTRINGLN echo \"\nENTER\nREPEAT 60\nSTRINGLN ==========================================================================================\nSTRINGLN qFlipper has been installed to $HOME/.local/bin/\nSTRINGLN It should appear in your Applications menu.\nSTRINGLN If it does not, you might want to log out and log in again.\nENTER\nSTRINGLN If you prefer to run qFlipper from your terminal, either use the absolute path\nSTRINGLN or make sure $HOME/.local/bin/ is included in your PATH environment variable.\nENTER\nSTRINGLN Additional configurations might be required by your Linux distribution such as \nSTRINGLN group membership, udev rules or else.\nSTRINGLN ==========================================================================================\nSTRINGLN \"\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/Install_qFlipper_macOS.txt",
    "content": "ID 05ac:021e Apple:Keyboard\nREM Keep these 3 lines IF (and only if) it's the first time you are performing a badKB attack against a specific macOS target.\nREM In fact, it helps Flipper Zero bypass the macOS keyboard setup assistant. Otherwise the attack will not start.\nREM Author: 47LeCoste\nREM Version 1.0 (Flipper Ducky)\nREM Target: macOS\nDELAY 3000\nF4\nDELAY 2500\nSTRING Terminal\nDELAY 2500\nENTER\nDELAY 1500\nSTRING (cd /tmp && curl -L -o qFlipper.dmg https://update.flipperzero.one/qFlipper/release/macos-amd64/dmg && hdiutil attach qFlipper.dmg && app_volume=$(ls /Volumes | grep -i \"qFlipper\") && (test -e /Applications/qFlipper.app && rm -rf /Applications/qFlipper.app ); cp -R \"/Volumes/$app_volume/qFlipper.app\" /Applications/ && hdiutil detach \"/Volumes/$app_volume\" && rm qFlipper.dmg && open /Applications/qFlipper.app)\nDELAY 1000\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/Install_qFlipper_windows.txt",
    "content": "REM Written by @dexv\nDELAY 2000\nGUI r\nDELAY 500\nSTRING powershell\nENTER\nDELAY 1000\nSTRING $url = \"https://update.flipperzero.one/qFlipper/release/windows-amd64/portable\"\nENTER\nSTRING $output = \"$env:USERPROFILE\\Documents\\qFlipper.zip\"\nENTER\nSTRING $destination = \"$env:USERPROFILE\\Documents\\qFlipper\"\nENTER\nSTRING $shortcutPath = \"$env:USERPROFILE\\Desktop\\qFlipper.lnk\"\nENTER\nSTRING $scriptPath = \"$env:USERPROFILE\\Documents\\qFlipperInstall.ps1\"\nENTER\nSTRING $driverPath = \"$destination\\STM32 Driver\"\nENTER\nSTRING $installBat = \"$driverPath\\install.bat\"\nENTER\nSTRING (New-Object System.Net.WebClient).DownloadFile($url, $output)\nENTER\nSTRING Expand-Archive -Path $output -DestinationPath $destination -Force\nENTER\nSTRING Set-Location -Path $destination\nENTER\nSTRING Start-Process -FilePath \".\\qFlipper.exe\"\nENTER\nSTRING Start-Process -Wait -FilePath \"cmd.exe\" -ArgumentList \"/c $installBat\"\nENTER\nSTRING $shell = New-Object -ComObject WScript.Shell\nENTER\nSTRING $shortcut = $shell.CreateShortcut($shortcutPath)\nENTER\nSTRING $shortcut.TargetPath = \"$destination\\qFlipper.exe\"\nENTER\nSTRING $shortcut.Save()\nENTER\nDELAY 500\nSTRING \"powershell -ExecutionPolicy Bypass -File $scriptPath\"\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/demo_chromeos.txt",
    "content": "REM This is BadUSB demo script for Chrome and ChromeOS by kowalski7cc\n\nREM Exit from Overview\nESC\nREM Open a new tab\nCTRL t\nREM wait for some slower chromebooks\nDELAY 1000\nREM Make sure we have omnibox focus\nCTRL l\nDELAY 200\nREM Open an empty editable page\nDEFAULT_DELAY 50\nSTRING data:text/html, <html contenteditable autofocus><title>Flipper Zero BadUSB Demo</title><style>body{font-family:monospace;}\nENTER\nDELAY 500\n\nSTRING Hello World!\nENTER\n\nREM Copy-Paste previous string\nUP\nHOME\nSHIFT DOWN\nCTRL c\nRIGHT\nCTRL v\nCTRL v\n\n\nSTRING =\nREPEAT 59\nENTER\nENTER\n\nSTRING               _.-------.._                    -,\nENTER\nHOME\nSTRING           .-\"```\"--..,,_/ /`-,               -,  \\ \nENTER\nHOME\nSTRING        .:\"          /:/  /'\\  \\     ,_...,  `. |  |\nENTER\nHOME\nSTRING       /       ,----/:/  /`\\ _\\~`_-\"`     _;\nENTER\nHOME\nSTRING      '      / /`\"\"\"'\\ \\ \\.~`_-'      ,-\"'/ \nENTER\nHOME\nSTRING     |      | |  0    | | .-'      ,/`  /\nENTER\nHOME\nSTRING    |    ,..\\ \\     ,.-\"`       ,/`    /\nENTER\nHOME\nSTRING   ;    :    `/`\"\"\\`           ,/--==,/-----,\nENTER\nHOME\nSTRING   |    `-...|        -.___-Z:_______J...---;\nENTER\nHOME\nSTRING   :         `                           _-'\nENTER\nHOME\nSTRING  _L_  _     ___  ___  ___  ___  ____--\"`\nENTER\nHOME\nSTRING | __|| |   |_ _|| _ \\| _ \\| __|| _ \\ \nENTER\nHOME\nSTRING | _| | |__  | | |  _/|  _/| _| |   / \nENTER\nHOME\nSTRING |_|  |____||___||_|  |_|  |___||_|_\\ \nENTER\nHOME\nENTER\n\nSTRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format\nENTER\nSTRING More information about script syntax can be found here:\nENTER\nSTRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/demo_gnome.txt",
    "content": "ID 1234:abcd Generic:USB Keyboard\nREM Declare ourselves as a generic usb keyboard\nREM You can override this to use something else\nREM Check the `lsusb` command to know your own devices IDs\n\nREM This is BadUSB demo script for Linux/Gnome\n\nREM Exit from Overview\nESC\nDELAY 200\nREM Open terminal window\nALT F2\nDELAY 1000\nREM Let's guess user terminal, based on (almost) glib order with ptyxis now default in Fedora 41\nSTRING sh -c \"xdg-terminal-exec||kgx||ptyxis||gnome-terminal||mate-terminal||xfce4-terminal||tilix||konsole||xterm\"\nDELAY 300\nENTER\nREM It can take a bit to open the correct terminal\nDELAY 1500\n\nREM Make sure we are running in a POSIX-compliant shell\nSTRING env sh\nENTER\n\nREM Clear the screen in case some banner was displayed\nSTRING clear\nENTER\n\nREM Bigger shell script example\nSTRING cat > /dev/null << EOF\nENTER\n\nSTRING Hello World!\nENTER\n\nDEFAULT_DELAY 50\n\nSTRING =\nREPEAT 59\nENTER\nENTER\n\nSTRING               _.-------.._                    -,\nENTER\nHOME\nSTRING           .-\"'''\"--..,,_/ /'-,               -,  \\\nENTER\nHOME\nSTRING        .:\"          /:/  /'\\  \\     ,_...,  '. |  |\nENTER\nHOME\nSTRING       /       ,----/:/  /'\\ _\\~'_-\"'     _;\nENTER\nHOME\nSTRING      '      / /'\"\"\"'\\ \\ \\.~'_-'      ,-\"'/\nENTER\nHOME\nSTRING     |      | |  0    | | .-'      ,/'  /\nENTER\nHOME\nSTRING    |    ,..\\ \\     ,.-\"'       ,/'    /\nENTER\nHOME\nSTRING   ;    :    '/'\"\"\\'           ,/--==,/-----,\nENTER\nHOME\nSTRING   |    '-...|        -.___-Z:_______J...---;\nENTER\nHOME\nSTRING   :         '                           _-'\nENTER\nHOME\nSTRING  _L_  _     ___  ___  ___  ___  ____--\"'\nENTER\nHOME\nSTRING | __|| |   |_ _|| _ \\| _ \\| __|| _ \\\nENTER\nHOME\nSTRING | _| | |__  | | |  _/|  _/| _| |   /\nENTER\nHOME\nSTRING |_|  |____||___||_|  |_|  |___||_|_\\\nENTER\nHOME\nENTER\n\nSTRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format\nENTER\nSTRING More information about script syntax can be found here:\nENTER\nSTRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md\nENTER\n\nSTRING EOF\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/demo_macos.txt",
    "content": "ID 1234:5678 Apple:Keyboard\nREM You can change these values to VID/PID of original Apple keyboard\nREM to bypass Keyboard Setup Assistant\n\nREM This is BadUSB demo script for macOS\n\nREM Open terminal window\nDELAY 1000\nGUI SPACE\nDELAY 500\nSTRING terminal\nDELAY 500\nENTER\nDELAY 750\n\nREM Copy-Paste previous string\nUP\nCTRL c\n\nREM Bigger shell script example\nSTRING cat > /dev/null << EOF\nENTER\n\nSTRING Hello World!\nENTER\n\nDEFAULT_DELAY 50\n\nSTRING =\nREPEAT 59\nENTER\nENTER\n\nSTRING               _.-------.._                    -,\nENTER\nHOME\nSTRING           .-\"'''\"--..,,_/ /'-,               -,  \\\nENTER\nHOME\nSTRING        .:\"          /:/  /'\\  \\     ,_...,  '. |  |\nENTER\nHOME\nSTRING       /       ,----/:/  /'\\ _\\~'_-\"'     _;\nENTER\nHOME\nSTRING      '      / /'\"\"\"'\\ \\ \\.~'_-'      ,-\"'/\nENTER\nHOME\nSTRING     |      | |  0    | | .-'      ,/'  /\nENTER\nHOME\nSTRING    |    ,..\\ \\     ,.-\"'       ,/'    /\nENTER\nHOME\nSTRING   ;    :    '/'\"\"\\'           ,/--==,/-----,\nENTER\nHOME\nSTRING   |    '-...|        -.___-Z:_______J...---;\nENTER\nHOME\nSTRING   :         '                           _-'\nENTER\nHOME\nSTRING  _L_  _     ___  ___  ___  ___  ____--\"'\nENTER\nHOME\nSTRING | __|| |   |_ _|| _ \\| _ \\| __|| _ \\\nENTER\nHOME\nSTRING | _| | |__  | | |  _/|  _/| _| |   /\nENTER\nHOME\nSTRING |_|  |____||___||_|  |_|  |___||_|_\\\nENTER\nHOME\nENTER\n\nSTRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format\nENTER\nSTRING More information about script syntax can be found here:\nENTER\nSTRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md\nENTER\n\nSTRING EOF\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/demo_windows.txt",
    "content": "REM This is BadUSB demo script for windows\n\nREM set slightly slower delay to ensure notepad picks up input without skipping characters\nDEFAULT_STRING_DELAY 10\nREM Open windows notepad\nDELAY 1000\nGUI r\nDELAY 500\nSTRING notepad\nDELAY 500\nENTER\nDELAY 750\n\nSTRING Hello World!\nENTER\nDEFAULT_DELAY 50\n\nREM Copy-Paste previous string\nUP\nHOME\nSHIFT DOWN\nCTRL c\nRIGHT\nCTRL v\nCTRL v\n\nREM Alt code input demo\nALTCHAR 7\nALTSTRING This line was print using Alt+Numpad input method. It works even if non-US keyboard layout is selected\nENTER\n\nSTRING =\nREPEAT 59\nENTER\nENTER\n\nSTRING               _.-------.._                    -,\nENTER\nHOME\nSTRING           .-\"```\"--..,,_/ /`-,               -,  \\ \nENTER\nHOME\nSTRING        .:\"          /:/  /'\\  \\     ,_...,  `. |  |\nENTER\nHOME\nSTRING       /       ,----/:/  /`\\ _\\~`_-\"`     _;\nENTER\nHOME\nSTRING      '      / /`\"\"\"'\\ \\ \\.~`_-'      ,-\"'/ \nENTER\nHOME\nSTRING     |      | |  0    | | .-'      ,/`  /\nENTER\nHOME\nSTRING    |    ,..\\ \\     ,.-\"`       ,/`    /\nENTER\nHOME\nSTRING   ;    :    `/`\"\"\\`           ,/--==,/-----,\nENTER\nHOME\nSTRING   |    `-...|        -.___-Z:_______J...---;\nENTER\nHOME\nSTRING   :         `                           _-'\nENTER\nHOME\nSTRING  _L_  _     ___  ___  ___  ___  ____--\"`\nENTER\nHOME\nSTRING | __|| |   |_ _|| _ \\| _ \\| __|| _ \\ \nENTER\nHOME\nSTRING | _| | |__  | | |  _/|  _/| _| |   / \nENTER\nHOME\nSTRING |_|  |____||___||_|  |_|  |___||_|_\\ \nENTER\nHOME\nENTER\n\nSTRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format\nENTER\nSTRING More information about script syntax can be found here:\nENTER\nSTRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md\nENTER\n"
  },
  {
    "path": "applications/main/bad_usb/resources/badusb/test_mouse.txt",
    "content": "ID 1234:abcd Generic:USB Keyboard\nREM Declare ourselves as a generic usb keyboard\nREM You can override this to use something else\nREM Check the `lsusb` command to know your own devices IDs\n\nDEFAULT_DELAY 200\nDEFAULT_STRING_DELAY 100\n\nDELAY 1000\n\nREM Test all mouse functions\nLEFTCLICK\nRIGHTCLICK\nMIDDLECLICK\n\nDELAY 1000\n\nMOUSEMOVE -10 0\nREPEAT 20\nMOUSEMOVE 0 10\nREPEAT 20\nMOUSEMOVE 10 0\nREPEAT 20\nMOUSEMOVE 0 -10\nREPEAT 20\n\nDELAY 1000\n\nMOUSESCROLL -50\nMOUSESCROLL 50\n\nDELAY 1000\n\nREM Verify Mouse hold working\nHOLD LEFTCLICK\nDELAY 2000\nRELEASE LEFTCLICK\n\nDELAY 1000\n\nREM Verify KB hold working\nHOLD M\nDELAY 2000\nRELEASE M\n\nENTER"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene.c",
    "content": "#include \"bad_usb_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const bad_usb_scene_on_enter_handlers[])(void*) = {\n#include \"bad_usb_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const bad_usb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"bad_usb_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const bad_usb_scene_on_exit_handlers[])(void* context) = {\n#include \"bad_usb_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers bad_usb_scene_handlers = {\n    .on_enter_handlers = bad_usb_scene_on_enter_handlers,\n    .on_event_handlers = bad_usb_scene_on_event_handlers,\n    .on_exit_handlers = bad_usb_scene_on_exit_handlers,\n    .scene_num = BadUsbSceneNum,\n};\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) BadUsbScene##id,\ntypedef enum {\n#include \"bad_usb_scene_config.h\"\n    BadUsbSceneNum,\n} BadUsbScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers bad_usb_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"bad_usb_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"bad_usb_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"bad_usb_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_config.c",
    "content": "#include \"../bad_usb_app_i.h\"\n\nenum SubmenuIndex {\n    ConfigIndexKeyboardLayout,\n    ConfigIndexBleUnpair,\n};\n\nvoid bad_usb_scene_config_select_callback(void* context, uint32_t index) {\n    BadUsbApp* bad_usb = context;\n\n    view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);\n}\n\nstatic void draw_menu(BadUsbApp* bad_usb) {\n    VariableItemList* var_item_list = bad_usb->var_item_list;\n\n    variable_item_list_reset(var_item_list);\n\n    variable_item_list_add(var_item_list, \"Keyboard Layout (global)\", 0, NULL, NULL);\n\n    variable_item_list_add(var_item_list, \"Remove Pairing\", 0, NULL, NULL);\n}\n\nvoid bad_usb_scene_config_on_enter(void* context) {\n    BadUsbApp* bad_usb = context;\n    VariableItemList* var_item_list = bad_usb->var_item_list;\n\n    variable_item_list_set_enter_callback(\n        var_item_list, bad_usb_scene_config_select_callback, bad_usb);\n    draw_menu(bad_usb);\n    variable_item_list_set_selected_item(var_item_list, 0);\n\n    view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);\n}\n\nbool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {\n    BadUsbApp* bad_usb = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == ConfigIndexKeyboardLayout) {\n            scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);\n        } else if(event.event == ConfigIndexBleUnpair) {\n            scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);\n        } else {\n            furi_crash(\"Unknown key type\");\n        }\n    }\n\n    return consumed;\n}\n\nvoid bad_usb_scene_config_on_exit(void* context) {\n    BadUsbApp* bad_usb = context;\n    VariableItemList* var_item_list = bad_usb->var_item_list;\n\n    variable_item_list_reset(var_item_list);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_config.h",
    "content": "ADD_SCENE(bad_usb, file_select, FileSelect)\nADD_SCENE(bad_usb, work, Work)\nADD_SCENE(bad_usb, error, Error)\nADD_SCENE(bad_usb, config, Config)\nADD_SCENE(bad_usb, config_layout, ConfigLayout)\nADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)\nADD_SCENE(bad_usb, unpair_done, UnpairDone)\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c",
    "content": "#include \"../bad_usb_app_i.h\"\n#include <storage/storage.h>\n\nstatic bool bad_usb_layout_select(BadUsbApp* bad_usb) {\n    furi_assert(bad_usb);\n\n    FuriString* predefined_path;\n    predefined_path = furi_string_alloc();\n    if(!furi_string_empty(bad_usb->keyboard_layout)) {\n        furi_string_set(predefined_path, bad_usb->keyboard_layout);\n    } else {\n        furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER);\n    }\n\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(\n        &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);\n    browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER;\n    browser_options.skip_assets = false;\n\n    // Input events and views are managed by file_browser\n    bool res = dialog_file_browser_show(\n        bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options);\n\n    furi_string_free(predefined_path);\n    return res;\n}\n\nvoid bad_usb_scene_config_layout_on_enter(void* context) {\n    BadUsbApp* bad_usb = context;\n\n    if(bad_usb_layout_select(bad_usb)) {\n        scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneWork);\n    } else {\n        scene_manager_previous_scene(bad_usb->scene_manager);\n    }\n}\n\nbool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    // BadUsbApp* bad_usb = context;\n    return false;\n}\n\nvoid bad_usb_scene_config_layout_on_exit(void* context) {\n    UNUSED(context);\n    // BadUsbApp* bad_usb = context;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c",
    "content": "#include \"../bad_usb_app_i.h\"\n\nvoid bad_usb_scene_confirm_unpair_widget_callback(\n    GuiButtonType type,\n    InputType input_type,\n    void* context) {\n    UNUSED(input_type);\n    SceneManagerEvent event = {.type = SceneManagerEventTypeCustom, .event = type};\n    bad_usb_scene_confirm_unpair_on_event(context, event);\n}\n\nvoid bad_usb_scene_confirm_unpair_on_enter(void* context) {\n    BadUsbApp* bad_usb = context;\n    Widget* widget = bad_usb->widget;\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Cancel\", bad_usb_scene_confirm_unpair_widget_callback, context);\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeRight,\n        \"Unpair\",\n        bad_usb_scene_confirm_unpair_widget_callback,\n        context);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 64, AlignCenter, AlignTop, \"\\e#Unpair the Device?\\e#\\n\", false);\n\n    view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewWidget);\n}\n\nbool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) {\n    BadUsbApp* bad_usb = context;\n    SceneManager* scene_manager = bad_usb->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone);\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid bad_usb_scene_confirm_unpair_on_exit(void* context) {\n    BadUsbApp* bad_usb = context;\n    Widget* widget = bad_usb->widget;\n\n    widget_reset(widget);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_error.c",
    "content": "#include \"../bad_usb_app_i.h\"\n\ntypedef enum {\n    BadUsbCustomEventErrorBack,\n} BadUsbCustomEvent;\n\nstatic void\n    bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    BadUsbApp* app = context;\n\n    if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack);\n    }\n}\n\nvoid bad_usb_scene_error_on_enter(void* context) {\n    BadUsbApp* app = context;\n\n    if(app->error == BadUsbAppErrorNoFiles) {\n        widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);\n        widget_add_string_multiline_element(\n            app->widget,\n            81,\n            4,\n            AlignCenter,\n            AlignTop,\n            FontSecondary,\n            \"No SD card or\\napp data found.\\nThis app will not\\nwork without\\nrequired files.\");\n        widget_add_button_element(\n            app->widget, GuiButtonTypeLeft, \"Back\", bad_usb_scene_error_event_callback, app);\n    } else if(app->error == BadUsbAppErrorCloseRpc) {\n        widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);\n        widget_add_string_multiline_element(\n            app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, \"Connection\\nIs Active!\");\n        widget_add_string_multiline_element(\n            app->widget,\n            3,\n            30,\n            AlignLeft,\n            AlignTop,\n            FontSecondary,\n            \"Disconnect from\\nPC or phone to\\nuse this function.\");\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWidget);\n}\n\nbool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {\n    BadUsbApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == BadUsbCustomEventErrorBack) {\n            view_dispatcher_stop(app->view_dispatcher);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid bad_usb_scene_error_on_exit(void* context) {\n    BadUsbApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_file_select.c",
    "content": "#include \"../bad_usb_app_i.h\"\n#include <furi_hal_power.h>\n#include <storage/storage.h>\n\nstatic bool bad_usb_file_select(BadUsbApp* bad_usb) {\n    furi_assert(bad_usb);\n\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(\n        &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px);\n    browser_options.base_path = BAD_USB_APP_BASE_FOLDER;\n    browser_options.skip_assets = true;\n\n    // Input events and views are managed by file_browser\n    bool res = dialog_file_browser_show(\n        bad_usb->dialogs, bad_usb->file_path, bad_usb->file_path, &browser_options);\n\n    return res;\n}\n\nvoid bad_usb_scene_file_select_on_enter(void* context) {\n    BadUsbApp* bad_usb = context;\n\n    if(bad_usb->bad_usb_script) {\n        bad_usb_script_close(bad_usb->bad_usb_script);\n        bad_usb->bad_usb_script = NULL;\n    }\n\n    if(bad_usb_file_select(bad_usb)) {\n        scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);\n    } else {\n        view_dispatcher_stop(bad_usb->view_dispatcher);\n    }\n}\n\nbool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    // BadUsbApp* bad_usb = context;\n    return false;\n}\n\nvoid bad_usb_scene_file_select_on_exit(void* context) {\n    UNUSED(context);\n    // BadUsbApp* bad_usb = context;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c",
    "content": "#include \"../bad_usb_app_i.h\"\n\nstatic void bad_usb_scene_unpair_done_popup_callback(void* context) {\n    BadUsbApp* bad_usb = context;\n    scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);\n}\n\nvoid bad_usb_scene_unpair_done_on_enter(void* context) {\n    BadUsbApp* bad_usb = context;\n    Popup* popup = bad_usb->popup;\n\n    bad_usb_hid_ble_remove_pairing();\n\n    popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);\n    popup_set_header(popup, \"Done\", 20, 19, AlignLeft, AlignBottom);\n    popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback);\n    popup_set_context(popup, bad_usb);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);\n}\n\nbool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {\n    BadUsbApp* bad_usb = context;\n    UNUSED(bad_usb);\n    UNUSED(event);\n    bool consumed = false;\n\n    return consumed;\n}\n\nvoid bad_usb_scene_unpair_done_on_exit(void* context) {\n    BadUsbApp* bad_usb = context;\n    Popup* popup = bad_usb->popup;\n    UNUSED(popup);\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/scenes/bad_usb_scene_work.c",
    "content": "#include \"../helpers/ducky_script.h\"\n#include \"../bad_usb_app_i.h\"\n#include \"../views/bad_usb_view.h\"\n#include <furi_hal.h>\n#include \"toolbox/path.h\"\n\nvoid bad_usb_scene_work_button_callback(InputKey key, void* context) {\n    furi_assert(context);\n    BadUsbApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, key);\n}\n\nbool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {\n    BadUsbApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InputKeyLeft) {\n            if(bad_usb_view_is_idle_state(app->bad_usb_view)) {\n                bad_usb_script_close(app->bad_usb_script);\n                app->bad_usb_script = NULL;\n\n                if(app->interface == BadUsbHidInterfaceBle) {\n                    scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);\n                } else {\n                    scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);\n                }\n            }\n            consumed = true;\n        } else if(event.event == InputKeyOk) {\n            bad_usb_script_start_stop(app->bad_usb_script);\n            consumed = true;\n        } else if(event.event == InputKeyRight) {\n            if(bad_usb_view_is_idle_state(app->bad_usb_view)) {\n                bad_usb_set_interface(\n                    app,\n                    app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :\n                                                              BadUsbHidInterfaceBle);\n                bad_usb_script_close(app->bad_usb_script);\n                app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);\n            } else {\n                bad_usb_script_pause_resume(app->bad_usb_script);\n            }\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeTick) {\n        bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));\n    }\n    return consumed;\n}\n\nvoid bad_usb_scene_work_on_enter(void* context) {\n    BadUsbApp* app = context;\n\n    bad_usb_view_set_interface(app->bad_usb_view, app->interface);\n\n    app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);\n    bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);\n\n    FuriString* file_name;\n    file_name = furi_string_alloc();\n    path_extract_filename(app->file_path, file_name, true);\n    bad_usb_view_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name));\n    furi_string_free(file_name);\n\n    FuriString* layout;\n    layout = furi_string_alloc();\n    path_extract_filename(app->keyboard_layout, layout, true);\n    bad_usb_view_set_layout(app->bad_usb_view, furi_string_get_cstr(layout));\n    furi_string_free(layout);\n\n    bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));\n\n    bad_usb_view_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork);\n}\n\nvoid bad_usb_scene_work_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/bad_usb/views/bad_usb_view.c",
    "content": "#include \"bad_usb_view.h\"\n#include \"../helpers/ducky_script.h\"\n#include <toolbox/path.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\n#define MAX_NAME_LEN 64\n\nstruct BadUsb {\n    View* view;\n    BadUsbButtonCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    char file_name[MAX_NAME_LEN];\n    char layout[MAX_NAME_LEN];\n    BadUsbState state;\n    bool pause_wait;\n    uint8_t anim_frame;\n    BadUsbHidInterface interface;\n} BadUsbModel;\n\nstatic void bad_usb_draw_callback(Canvas* canvas, void* _model) {\n    BadUsbModel* model = _model;\n\n    FuriString* disp_str;\n    disp_str = furi_string_alloc_set(model->file_name);\n    elements_string_fit_width(canvas, disp_str, 128 - 2);\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));\n\n    if(strlen(model->layout) == 0) {\n        furi_string_set(disp_str, \"(default)\");\n    } else {\n        furi_string_printf(disp_str, \"(%s)\", model->layout);\n    }\n    elements_string_fit_width(canvas, disp_str, 128 - 2);\n    canvas_draw_str(\n        canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));\n\n    furi_string_reset(disp_str);\n\n    if(model->interface == BadUsbHidInterfaceBle) {\n        canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22);\n    } else {\n        canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);\n    }\n\n    BadUsbWorkerState state = model->state.state;\n\n    if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||\n       (state == BadUsbStateNotConnected)) {\n        elements_button_center(canvas, \"Run\");\n        if(model->interface == BadUsbHidInterfaceBle) {\n            elements_button_right(canvas, \"USB\");\n            elements_button_left(canvas, \"Config\");\n        } else {\n            elements_button_right(canvas, \"BLE\");\n            elements_button_left(canvas, \"Layout\");\n        }\n    } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {\n        elements_button_center(canvas, \"Stop\");\n        if(!model->pause_wait) {\n            elements_button_right(canvas, \"Pause\");\n        }\n    } else if(state == BadUsbStatePaused) {\n        elements_button_center(canvas, \"End\");\n        elements_button_right(canvas, \"Resume\");\n    } else if(state == BadUsbStateWaitForBtn) {\n        elements_button_center(canvas, \"Press to continue\");\n    } else if(state == BadUsbStateWillRun) {\n        elements_button_center(canvas, \"Cancel\");\n    }\n\n    if(state == BadUsbStateNotConnected) {\n        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, \"Connect\");\n        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, \"to device\");\n    } else if(state == BadUsbStateWillRun) {\n        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, \"Will run\");\n        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, \"on connect\");\n    } else if(state == BadUsbStateFileError) {\n        canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, \"File\");\n        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, \"ERROR\");\n    } else if(state == BadUsbStateScriptError) {\n        canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, \"ERROR:\");\n        canvas_set_font(canvas, FontSecondary);\n        furi_string_printf(disp_str, \"line %zu\", model->state.error_line);\n        canvas_draw_str_aligned(\n            canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n\n        furi_string_set_str(disp_str, model->state.error);\n        elements_string_fit_width(canvas, disp_str, canvas_width(canvas));\n        canvas_draw_str_aligned(\n            canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n    } else if(state == BadUsbStateIdle) {\n        canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);\n        canvas_set_font(canvas, FontBigNumbers);\n        canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, \"0\");\n        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);\n    } else if(state == BadUsbStateRunning) {\n        if(model->anim_frame == 0) {\n            canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);\n        } else {\n            canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);\n        }\n        canvas_set_font(canvas, FontBigNumbers);\n        furi_string_printf(\n            disp_str, \"%zu\", ((model->state.line_cur - 1) * 100) / model->state.line_nb);\n        canvas_draw_str_aligned(\n            canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);\n    } else if(state == BadUsbStateDone) {\n        canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);\n        canvas_set_font(canvas, FontBigNumbers);\n        canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, \"100\");\n        furi_string_reset(disp_str);\n        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);\n    } else if(state == BadUsbStateDelay) {\n        if(model->anim_frame == 0) {\n            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);\n        } else {\n            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);\n        }\n        canvas_set_font(canvas, FontBigNumbers);\n        furi_string_printf(\n            disp_str, \"%zu\", ((model->state.line_cur - 1) * 100) / model->state.line_nb);\n        canvas_draw_str_aligned(\n            canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);\n        canvas_set_font(canvas, FontSecondary);\n        furi_string_printf(disp_str, \"delay %lus\", model->state.delay_remain);\n        canvas_draw_str_aligned(\n            canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n    } else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) {\n        if(model->anim_frame == 0) {\n            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);\n        } else {\n            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);\n        }\n        canvas_set_font(canvas, FontBigNumbers);\n        furi_string_printf(\n            disp_str, \"%zu\", ((model->state.line_cur - 1) * 100) / model->state.line_nb);\n        canvas_draw_str_aligned(\n            canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));\n        furi_string_reset(disp_str);\n        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, \"Paused\");\n        furi_string_reset(disp_str);\n    } else {\n        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);\n    }\n\n    furi_string_free(disp_str);\n}\n\nstatic bool bad_usb_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    BadUsb* bad_usb = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyLeft) {\n            consumed = true;\n            furi_assert(bad_usb->callback);\n            bad_usb->callback(event->key, bad_usb->context);\n        } else if(event->key == InputKeyOk) {\n            with_view_model(\n                bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true);\n            consumed = true;\n            furi_assert(bad_usb->callback);\n            bad_usb->callback(event->key, bad_usb->context);\n        } else if(event->key == InputKeyRight) {\n            with_view_model(\n                bad_usb->view,\n                BadUsbModel * model,\n                {\n                    if((model->state.state == BadUsbStateRunning) ||\n                       (model->state.state == BadUsbStateDelay)) {\n                        model->pause_wait = true;\n                    }\n                },\n                true);\n            consumed = true;\n            furi_assert(bad_usb->callback);\n            bad_usb->callback(event->key, bad_usb->context);\n        }\n    }\n\n    return consumed;\n}\n\nBadUsb* bad_usb_view_alloc(void) {\n    BadUsb* bad_usb = malloc(sizeof(BadUsb));\n\n    bad_usb->view = view_alloc();\n    view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel));\n    view_set_context(bad_usb->view, bad_usb);\n    view_set_draw_callback(bad_usb->view, bad_usb_draw_callback);\n    view_set_input_callback(bad_usb->view, bad_usb_input_callback);\n\n    return bad_usb;\n}\n\nvoid bad_usb_view_free(BadUsb* bad_usb) {\n    furi_assert(bad_usb);\n    view_free(bad_usb->view);\n    free(bad_usb);\n}\n\nView* bad_usb_view_get_view(BadUsb* bad_usb) {\n    furi_assert(bad_usb);\n    return bad_usb->view;\n}\n\nvoid bad_usb_view_set_button_callback(\n    BadUsb* bad_usb,\n    BadUsbButtonCallback callback,\n    void* context) {\n    furi_assert(bad_usb);\n    furi_assert(callback);\n    with_view_model(\n        bad_usb->view,\n        BadUsbModel * model,\n        {\n            UNUSED(model);\n            bad_usb->callback = callback;\n            bad_usb->context = context;\n        },\n        true);\n}\n\nvoid bad_usb_view_set_file_name(BadUsb* bad_usb, const char* name) {\n    furi_assert(name);\n    with_view_model(\n        bad_usb->view,\n        BadUsbModel * model,\n        { strlcpy(model->file_name, name, MAX_NAME_LEN); },\n        true);\n}\n\nvoid bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout) {\n    furi_assert(layout);\n    with_view_model(\n        bad_usb->view,\n        BadUsbModel * model,\n        { strlcpy(model->layout, layout, MAX_NAME_LEN); },\n        true);\n}\n\nvoid bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st) {\n    furi_assert(st);\n    with_view_model(\n        bad_usb->view,\n        BadUsbModel * model,\n        {\n            memcpy(&(model->state), st, sizeof(BadUsbState));\n            model->anim_frame ^= 1;\n            if(model->state.state == BadUsbStatePaused) {\n                model->pause_wait = false;\n            }\n        },\n        true);\n}\n\nvoid bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface) {\n    with_view_model(bad_usb->view, BadUsbModel * model, { model->interface = interface; }, true);\n}\n\nbool bad_usb_view_is_idle_state(BadUsb* bad_usb) {\n    bool is_idle = false;\n    with_view_model(\n        bad_usb->view,\n        BadUsbModel * model,\n        {\n            if((model->state.state == BadUsbStateIdle) ||\n               (model->state.state == BadUsbStateDone) ||\n               (model->state.state == BadUsbStateNotConnected)) {\n                is_idle = true;\n            }\n        },\n        false);\n    return is_idle;\n}\n"
  },
  {
    "path": "applications/main/bad_usb/views/bad_usb_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../helpers/ducky_script.h\"\n\ntypedef struct BadUsb BadUsb;\ntypedef void (*BadUsbButtonCallback)(InputKey key, void* context);\n\nBadUsb* bad_usb_view_alloc(void);\n\nvoid bad_usb_view_free(BadUsb* bad_usb);\n\nView* bad_usb_view_get_view(BadUsb* bad_usb);\n\nvoid bad_usb_view_set_button_callback(\n    BadUsb* bad_usb,\n    BadUsbButtonCallback callback,\n    void* context);\n\nvoid bad_usb_view_set_file_name(BadUsb* bad_usb, const char* name);\n\nvoid bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout);\n\nvoid bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st);\n\nvoid bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface);\n\nbool bad_usb_view_is_idle_state(BadUsb* bad_usb);\n"
  },
  {
    "path": "applications/main/gpio/application.fam",
    "content": "App(\n    appid=\"gpio\",\n    name=\"GPIO\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    entry_point=\"gpio_app\",\n    stack_size=2 * 1024,\n    icon=\"A_GPIO_14\",\n    order=50,\n    fap_libs=[\"assets\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"GPIO\",\n)\n"
  },
  {
    "path": "applications/main/gpio/gpio_app.c",
    "content": "#include \"gpio_app_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\nstatic bool gpio_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    GpioApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool gpio_app_back_event_callback(void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void gpio_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nGpioApp* gpio_app_alloc(void) {\n    GpioApp* app = malloc(sizeof(GpioApp));\n\n    app->expansion = furi_record_open(RECORD_EXPANSION);\n    expansion_disable(app->expansion);\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->gpio_items = gpio_items_alloc();\n\n    app->power = furi_record_open(RECORD_POWER);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, gpio_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, gpio_app_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, gpio_app_tick_event_callback, 100);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // Dialog view\n    app->dialog = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, GpioAppViewExitConfirm, dialog_ex_get_view(app->dialog));\n\n    app->var_item_list = variable_item_list_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        GpioAppViewVarItemList,\n        variable_item_list_get_view(app->var_item_list));\n    app->gpio_test = gpio_test_alloc(app->gpio_items);\n    view_dispatcher_add_view(\n        app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));\n\n    app->widget = widget_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget));\n\n    app->gpio_usb_uart = gpio_usb_uart_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart));\n\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        GpioAppViewUsbUartCfg,\n        variable_item_list_get_view(app->var_item_list));\n\n    scene_manager_next_scene(app->scene_manager, GpioSceneStart);\n\n    return app;\n}\n\nvoid gpio_app_free(GpioApp* app) {\n    furi_assert(app);\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);\n    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewExitConfirm);\n    variable_item_list_free(app->var_item_list);\n    widget_free(app->widget);\n    gpio_test_free(app->gpio_test);\n    gpio_usb_uart_free(app->gpio_usb_uart);\n    dialog_ex_free(app->dialog);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    furi_record_close(RECORD_POWER);\n\n    expansion_enable(app->expansion);\n    furi_record_close(RECORD_EXPANSION);\n\n    gpio_items_free(app->gpio_items);\n    free(app);\n}\n\nint32_t gpio_app(void* p) {\n    UNUSED(p);\n    GpioApp* gpio_app = gpio_app_alloc();\n\n    view_dispatcher_run(gpio_app->view_dispatcher);\n\n    gpio_app_free(gpio_app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/gpio/gpio_app.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct GpioApp GpioApp;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/gpio/gpio_app_i.h",
    "content": "#pragma once\n\n#include \"gpio_app.h\"\n#include \"gpio_items.h\"\n#include \"scenes/gpio_scene.h\"\n#include \"gpio_custom_event.h\"\n#include \"usb_uart_bridge.h\"\n#include <power/power_service/power.h>\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <notification/notification_messages.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/widget.h>\n#include <gui/modules/dialog_ex.h>\n#include \"views/gpio_test.h\"\n#include \"views/gpio_usb_uart.h\"\n#include <assets_icons.h>\n#include <expansion/expansion.h>\n\nstruct GpioApp {\n    Expansion* expansion;\n    Gui* gui;\n    NotificationApp* notifications;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    Widget* widget;\n    DialogEx* dialog;\n    Power* power;\n\n    VariableItemList* var_item_list;\n    VariableItem* var_item_flow;\n    GpioTest* gpio_test;\n    GpioUsbUart* gpio_usb_uart;\n    GPIOItems* gpio_items;\n    UsbUartBridge* usb_uart_bridge;\n    UsbUartConfig* usb_uart_cfg;\n};\n\ntypedef enum {\n    GpioAppViewVarItemList,\n    GpioAppViewGpioTest,\n    GpioAppViewUsbUart,\n    GpioAppViewUsbUartCfg,\n    GpioAppViewUsbUartCloseRpc,\n    GpioAppViewExitConfirm,\n} GpioAppView;\n"
  },
  {
    "path": "applications/main/gpio/gpio_custom_event.h",
    "content": "#pragma once\n\ntypedef enum {\n    GpioStartEventOtgOff = 0,\n    GpioStartEventOtgOn,\n    GpioStartEventManualControl,\n    GpioStartEventUsbUart,\n\n    GpioCustomEventErrorBack,\n\n    GpioUsbUartEventConfig,\n    GpioUsbUartEventConfigSet,\n} GpioCustomEvent;\n"
  },
  {
    "path": "applications/main/gpio/gpio_items.c",
    "content": "#include \"gpio_items.h\"\n\n#include <furi_hal_resources.h>\n\nstruct GPIOItems {\n    GpioPinRecord* pins;\n    size_t count;\n};\n\nGPIOItems* gpio_items_alloc(void) {\n    GPIOItems* items = malloc(sizeof(GPIOItems));\n\n    items->count = 0;\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(!gpio_pins[i].debug) {\n            items->count++;\n        }\n    }\n\n    items->pins = malloc(sizeof(GpioPinRecord) * items->count);\n    size_t index = 0;\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(!gpio_pins[i].debug) {\n            items->pins[index].pin = gpio_pins[i].pin;\n            items->pins[index].name = gpio_pins[i].name;\n            index++;\n        }\n    }\n    return items;\n}\n\nvoid gpio_items_free(GPIOItems* items) {\n    free(items->pins);\n    free(items);\n}\n\nuint8_t gpio_items_get_count(GPIOItems* items) {\n    return items->count;\n}\n\nvoid gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) {\n    furi_assert(index < items->count);\n    furi_hal_gpio_write(items->pins[index].pin, false);\n    furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);\n}\n\nvoid gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) {\n    for(uint8_t i = 0; i < items->count; i++) {\n        gpio_items_configure_pin(items, i, mode);\n    }\n}\n\nvoid gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) {\n    furi_assert(index < items->count);\n    furi_hal_gpio_write(items->pins[index].pin, level);\n}\n\nvoid gpio_items_set_all_pins(GPIOItems* items, bool level) {\n    for(uint8_t i = 0; i < items->count; i++) {\n        gpio_items_set_pin(items, i, level);\n    }\n}\n\nconst char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) {\n    furi_assert(index < items->count + 1);\n    if(index == items->count) {\n        return \"ALL\";\n    } else {\n        return items->pins[index].name;\n    }\n}\n"
  },
  {
    "path": "applications/main/gpio/gpio_items.h",
    "content": "#pragma once\n\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct GPIOItems GPIOItems;\n\nGPIOItems* gpio_items_alloc(void);\n\nvoid gpio_items_free(GPIOItems* items);\n\nuint8_t gpio_items_get_count(GPIOItems* items);\n\nvoid gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode);\n\nvoid gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode);\n\nvoid gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level);\n\nvoid gpio_items_set_all_pins(GPIOItems* items, bool level);\n\nconst char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene.c",
    "content": "#include \"gpio_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const gpio_scene_on_enter_handlers[])(void*) = {\n#include \"gpio_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const gpio_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"gpio_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const gpio_scene_on_exit_handlers[])(void* context) = {\n#include \"gpio_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers gpio_scene_handlers = {\n    .on_enter_handlers = gpio_scene_on_enter_handlers,\n    .on_event_handlers = gpio_scene_on_event_handlers,\n    .on_exit_handlers = gpio_scene_on_exit_handlers,\n    .scene_num = GpioSceneNum,\n};\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) GpioScene##id,\ntypedef enum {\n#include \"gpio_scene_config.h\"\n    GpioSceneNum,\n} GpioScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers gpio_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"gpio_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"gpio_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"gpio_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_config.h",
    "content": "ADD_SCENE(gpio, start, Start)\nADD_SCENE(gpio, test, Test)\nADD_SCENE(gpio, usb_uart, UsbUart)\nADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)\nADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_start.c",
    "content": "#include \"../gpio_app_i.h\"\n#include <furi_hal_power.h>\n#include <furi_hal_usb.h>\n#include <dolphin/dolphin.h>\n\nenum GpioItem {\n    GpioItemUsbUart,\n    GpioItemTest,\n    GpioItemOtg,\n};\n\nenum GpioOtg {\n    GpioOtgOff,\n    GpioOtgOn,\n    GpioOtgSettingsNum,\n};\n\nconst char* const gpio_otg_text[GpioOtgSettingsNum] = {\n    \"OFF\",\n    \"ON\",\n};\n\nstatic void gpio_scene_start_var_list_enter_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    GpioApp* app = context;\n    if(index == GpioItemTest) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl);\n    } else if(index == GpioItemUsbUart) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart);\n    }\n}\n\nstatic void gpio_scene_start_var_list_change_callback(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, gpio_otg_text[index]);\n    if(index == GpioOtgOff) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOff);\n    } else if(index == GpioOtgOn) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOn);\n    }\n}\n\nvoid gpio_scene_start_on_enter(void* context) {\n    GpioApp* app = context;\n    VariableItemList* var_item_list = app->var_item_list;\n\n    VariableItem* item;\n    variable_item_list_set_enter_callback(\n        var_item_list, gpio_scene_start_var_list_enter_callback, app);\n\n    variable_item_list_add(var_item_list, \"USB-UART Bridge\", 0, NULL, NULL);\n\n    variable_item_list_add(var_item_list, \"GPIO Manual Control\", 0, NULL, NULL);\n\n    item = variable_item_list_add(\n        var_item_list,\n        \"5V on GPIO\",\n        GpioOtgSettingsNum,\n        gpio_scene_start_var_list_change_callback,\n        app);\n    if(power_is_otg_enabled(app->power)) {\n        variable_item_set_current_value_index(item, GpioOtgOn);\n        variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]);\n    } else {\n        variable_item_set_current_value_index(item, GpioOtgOff);\n        variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);\n    }\n\n    variable_item_list_set_selected_item(\n        var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);\n}\n\nbool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {\n    GpioApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GpioStartEventOtgOn) {\n            power_enable_otg(app->power, true);\n        } else if(event.event == GpioStartEventOtgOff) {\n            power_enable_otg(app->power, false);\n        } else if(event.event == GpioStartEventManualControl) {\n            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);\n            scene_manager_next_scene(app->scene_manager, GpioSceneTest);\n        } else if(event.event == GpioStartEventUsbUart) {\n            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);\n            if(!furi_hal_usb_is_locked()) {\n                dolphin_deed(DolphinDeedGpioUartBridge);\n                scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);\n            } else {\n                scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);\n            }\n        }\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid gpio_scene_start_on_exit(void* context) {\n    GpioApp* app = context;\n    variable_item_list_reset(app->var_item_list);\n}\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_test.c",
    "content": "#include \"../gpio_app_i.h\"\n\nvoid gpio_scene_test_ok_callback(InputType type, void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n\n    if(type == InputTypePress) {\n        notification_message(app->notifications, &sequence_set_green_255);\n    } else if(type == InputTypeRelease) {\n        notification_message(app->notifications, &sequence_reset_green);\n    }\n}\n\nvoid gpio_scene_test_on_enter(void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n    gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull);\n    gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);\n}\n\nbool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid gpio_scene_test_on_exit(void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n    gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog);\n}\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_usb_uart.c",
    "content": "#include \"../gpio_app_i.h\"\n#include \"../usb_uart_bridge.h\"\n\ntypedef struct {\n    UsbUartConfig cfg;\n    UsbUartState state;\n} SceneUsbUartBridge;\n\nstatic SceneUsbUartBridge* scene_usb_uart = NULL;\n\nvoid gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {\n    furi_assert(context);\n    GpioApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid gpio_scene_usb_uart_dialog_callback(DialogExResult result, void* context) {\n    GpioApp* app = context;\n    if(result == DialogExResultLeft) {\n        usb_uart_disable(app->usb_uart_bridge);\n        free(scene_usb_uart);\n        scene_usb_uart = NULL;\n        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);\n    } else {\n        view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);\n    }\n}\n\nvoid gpio_scene_usb_uart_on_enter(void* context) {\n    GpioApp* app = context;\n    if(!scene_usb_uart) {\n        scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));\n        scene_usb_uart->cfg.vcp_ch = 0;\n        scene_usb_uart->cfg.uart_ch = 0;\n        scene_usb_uart->cfg.flow_pins = 0;\n        scene_usb_uart->cfg.baudrate_mode = 0;\n        scene_usb_uart->cfg.baudrate = 0;\n        app->usb_uart_bridge = usb_uart_enable(&scene_usb_uart->cfg);\n    }\n\n    usb_uart_get_config(app->usb_uart_bridge, &scene_usb_uart->cfg);\n    usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);\n\n    gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);\n    notification_message(app->notifications, &sequence_display_backlight_enforce_on);\n}\n\nbool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {\n    GpioApp* app = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);\n        return true;\n    } else if(event.type == SceneManagerEventTypeBack) {\n        DialogEx* dialog = app->dialog;\n        dialog_ex_set_context(dialog, app);\n        dialog_ex_set_left_button_text(dialog, \"Exit\");\n        dialog_ex_set_right_button_text(dialog, \"Stay\");\n        dialog_ex_set_header(dialog, \"Exit USB-UART?\", 22, 12, AlignLeft, AlignTop);\n        dialog_ex_set_result_callback(dialog, gpio_scene_usb_uart_dialog_callback);\n        view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);\n        return true;\n    } else if(event.type == SceneManagerEventTypeTick) {\n        uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;\n        uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt;\n        usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);\n        gpio_usb_uart_update_state(\n            app->gpio_usb_uart, &scene_usb_uart->cfg, &scene_usb_uart->state);\n        if(tx_cnt_last != scene_usb_uart->state.tx_cnt)\n            notification_message(app->notifications, &sequence_blink_blue_10);\n        if(rx_cnt_last != scene_usb_uart->state.rx_cnt)\n            notification_message(app->notifications, &sequence_blink_green_10);\n    }\n    return false;\n}\n\nvoid gpio_scene_usb_uart_on_exit(void* context) {\n    GpioApp* app = context;\n    notification_message(app->notifications, &sequence_display_backlight_enforce_auto);\n}\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_usb_uart_close_rpc.c",
    "content": "#include \"../gpio_app_i.h\"\n#include \"../gpio_custom_event.h\"\n\nvoid gpio_scene_usb_uart_close_rpc_on_enter(void* context) {\n    GpioApp* app = context;\n\n    widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);\n    widget_add_string_multiline_element(\n        app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, \"Connection\\nIs Active!\");\n    widget_add_string_multiline_element(\n        app->widget,\n        3,\n        30,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Disconnect from\\nPC or phone to\\nuse this function.\");\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);\n}\n\nbool gpio_scene_usb_uart_close_rpc_on_event(void* context, SceneManagerEvent event) {\n    GpioApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GpioCustomEventErrorBack) {\n            if(!scene_manager_previous_scene(app->scene_manager)) {\n                scene_manager_stop(app->scene_manager);\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid gpio_scene_usb_uart_close_rpc_on_exit(void* context) {\n    GpioApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/gpio/scenes/gpio_scene_usb_uart_config.c",
    "content": "#include \"../usb_uart_bridge.h\"\n#include \"../gpio_app_i.h\"\n#include <furi_hal.h>\n\ntypedef enum {\n    UsbUartLineIndexVcp,\n    UsbUartLineIndexBaudrate,\n    UsbUartLineIndexUart,\n    UsbUartLineIndexFlow,\n} LineIndex;\n\nstatic const char* vcp_ch[] = {\"0 (CLI)\", \"1\"};\nstatic const char* uart_ch[] = {\"13,14\", \"15,16\"};\nstatic const char* flow_pins[] = {\"None\", \"2,3\", \"6,7\", \"16,15\"};\nstatic const char* baudrate_mode[] = {\"Host\"};\nstatic const uint32_t baudrate_list[] = {\n    1200,\n    2400,\n    4800,\n    9600,\n    19200,\n    28800,\n    38400,\n    57600,\n    115200,\n    230400,\n    460800,\n    921600,\n};\nstatic const char* software_de_re[] = {\"None\", \"4\"};\n\nbool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {\n    GpioApp* app = context;\n    furi_assert(app);\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GpioUsbUartEventConfigSet) {\n            usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid line_ensure_flow_invariant(GpioApp* app) {\n    // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is\n    // selected. This function enforces that invariant by resetting flow_pins\n    // to None if it is configured to 16,15 when LPUART is selected.\n\n    uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalSerialIdLpuart ? 3 : 4;\n    VariableItem* item = app->var_item_flow;\n    variable_item_set_values_count(item, available_flow_pins);\n\n    if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {\n        app->usb_uart_cfg->flow_pins = 0;\n\n        variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);\n        variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);\n    }\n}\n\nstatic void line_vcp_cb(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    furi_assert(app);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, vcp_ch[index]);\n\n    app->usb_uart_cfg->vcp_ch = index;\n    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);\n}\n\nstatic void line_port_cb(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    furi_assert(app);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, uart_ch[index]);\n\n    if(index == 0)\n        app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart;\n    else if(index == 1)\n        app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart;\n\n    line_ensure_flow_invariant(app);\n    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);\n}\n\nstatic void line_software_de_re_cb(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    furi_assert(app);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, software_de_re[index]);\n\n    app->usb_uart_cfg->software_de_re = index;\n    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);\n}\n\nstatic void line_flow_cb(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    furi_assert(app);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, flow_pins[index]);\n\n    app->usb_uart_cfg->flow_pins = index;\n    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);\n}\n\nstatic void line_baudrate_cb(VariableItem* item) {\n    GpioApp* app = variable_item_get_context(item);\n    furi_assert(app);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    char br_text[8];\n\n    if(index > 0) {\n        snprintf(br_text, 7, \"%lu\", baudrate_list[index - 1]);\n        variable_item_set_current_value_text(item, br_text);\n        app->usb_uart_cfg->baudrate = baudrate_list[index - 1];\n    } else {\n        variable_item_set_current_value_text(item, baudrate_mode[index]);\n        app->usb_uart_cfg->baudrate = 0;\n    }\n    app->usb_uart_cfg->baudrate_mode = index;\n    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);\n}\n\nvoid gpio_scene_usb_uart_cfg_on_enter(void* context) {\n    GpioApp* app = context;\n    furi_assert(app);\n    VariableItemList* var_item_list = app->var_item_list;\n\n    app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));\n    usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);\n\n    VariableItem* item;\n    char br_text[8];\n\n    item = variable_item_list_add(var_item_list, \"USB Channel\", 2, line_vcp_cb, app);\n    variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);\n    variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);\n\n    item = variable_item_list_add(\n        var_item_list,\n        \"Baudrate\",\n        sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,\n        line_baudrate_cb,\n        app);\n    variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);\n    if(app->usb_uart_cfg->baudrate_mode > 0) {\n        snprintf(br_text, 7, \"%lu\", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);\n        variable_item_set_current_value_text(item, br_text);\n    } else {\n        variable_item_set_current_value_text(\n            item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);\n    }\n\n    item = variable_item_list_add(var_item_list, \"UART Pins\", 2, line_port_cb, app);\n    variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);\n    variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);\n\n    item = variable_item_list_add(\n        var_item_list, \"RTS/DTR Pins\", COUNT_OF(flow_pins), line_flow_cb, app);\n    variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);\n    variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);\n    app->var_item_flow = item;\n    line_ensure_flow_invariant(app);\n\n    item = variable_item_list_add(\n        var_item_list, \"DE/RE Pin\", COUNT_OF(software_de_re), line_software_de_re_cb, app);\n    variable_item_set_current_value_index(item, app->usb_uart_cfg->software_de_re);\n    variable_item_set_current_value_text(item, software_de_re[app->usb_uart_cfg->software_de_re]);\n\n    variable_item_list_set_selected_item(\n        var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg);\n}\n\nvoid gpio_scene_usb_uart_cfg_on_exit(void* context) {\n    GpioApp* app = context;\n    scene_manager_set_scene_state(\n        app->scene_manager,\n        GpioAppViewUsbUartCfg,\n        variable_item_list_get_selected_item_index(app->var_item_list));\n    variable_item_list_reset(app->var_item_list);\n    free(app->usb_uart_cfg);\n}\n"
  },
  {
    "path": "applications/main/gpio/usb_uart_bridge.c",
    "content": "#include \"usb_uart_bridge.h\"\n#include \"usb_cdc.h\"\n#include <cli/cli_vcp.h>\n#include <toolbox/api_lock.h>\n#include <furi_hal.h>\n#include <furi_hal_usb_cdc.h>\n\n//TODO: FL-3276 port to new USART API\n#include <stm32wbxx_ll_lpuart.h>\n#include <stm32wbxx_ll_usart.h>\n\n#define USB_CDC_PKT_LEN      CDC_DATA_SZ\n#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)\n\n#define USB_CDC_BIT_DTR     (1 << 0)\n#define USB_CDC_BIT_RTS     (1 << 1)\n#define USB_USART_DE_RE_PIN &gpio_ext_pa4\n\nstatic const GpioPin* flow_pins[][2] = {\n    {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3\n    {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7\n    {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15\n};\n\ntypedef enum {\n    WorkerEvtStop = (1 << 0),\n    WorkerEvtRxDone = (1 << 1),\n\n    WorkerEvtTxStop = (1 << 2),\n    WorkerEvtCdcRx = (1 << 3),\n    WorkerEvtCdcTxComplete = (1 << 4),\n\n    WorkerEvtCfgChange = (1 << 5),\n\n    WorkerEvtLineCfgSet = (1 << 6),\n    WorkerEvtCtrlLineSet = (1 << 7),\n\n} WorkerEvtFlags;\n\n#define WORKER_ALL_RX_EVENTS                                                      \\\n    (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \\\n     WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete)\n#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)\n\nstruct UsbUartBridge {\n    UsbUartConfig cfg;\n    UsbUartConfig cfg_new;\n\n    FuriThread* thread;\n    FuriThread* tx_thread;\n\n    FuriStreamBuffer* rx_stream;\n    FuriHalSerialHandle* serial_handle;\n\n    FuriMutex* usb_mutex;\n\n    FuriSemaphore* tx_sem;\n\n    UsbUartState st;\n\n    FuriApiLock cfg_lock;\n\n    CliVcp* cli_vcp;\n\n    uint8_t rx_buf[USB_CDC_PKT_LEN];\n};\n\nstatic void vcp_on_cdc_tx_complete(void* context);\nstatic void vcp_on_cdc_rx(void* context);\nstatic void vcp_state_callback(void* context, uint8_t state);\nstatic void vcp_on_cdc_control_line(void* context, uint8_t state);\nstatic void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);\n\nstatic const CdcCallbacks cdc_cb = {\n    vcp_on_cdc_tx_complete,\n    vcp_on_cdc_rx,\n    vcp_state_callback,\n    vcp_on_cdc_control_line,\n    vcp_on_line_config,\n};\n\n/* USB UART worker */\n\nstatic int32_t usb_uart_tx_thread(void* context);\n\nstatic void usb_uart_on_irq_rx_dma_cb(\n    FuriHalSerialHandle* handle,\n    FuriHalSerialRxEvent ev,\n    size_t size,\n    void* context) {\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n\n    if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) {\n        uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0};\n        while(size) {\n            size_t ret = furi_hal_serial_dma_rx(\n                handle,\n                data,\n                (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size);\n            furi_stream_buffer_send(usb_uart->rx_stream, data, ret, 0);\n            size -= ret;\n        };\n        furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone);\n    }\n}\n\nstatic void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {\n    furi_hal_usb_unlock();\n    if(vcp_ch == 0) {\n        cli_vcp_disable(usb_uart->cli_vcp);\n        furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);\n    } else {\n        furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);\n        cli_vcp_enable(usb_uart->cli_vcp);\n    }\n    furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);\n}\n\nstatic void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {\n    UNUSED(usb_uart);\n    furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);\n    if(vcp_ch != 0) {\n        cli_vcp_disable(usb_uart->cli_vcp);\n    }\n}\n\nstatic void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {\n    furi_assert(!usb_uart->serial_handle);\n\n    usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch);\n    furi_assert(usb_uart->serial_handle);\n\n    furi_hal_serial_init(usb_uart->serial_handle, 115200);\n    furi_hal_serial_dma_rx_start(\n        usb_uart->serial_handle, usb_uart_on_irq_rx_dma_cb, usb_uart, false);\n}\n\nstatic void usb_uart_serial_deinit(UsbUartBridge* usb_uart) {\n    furi_assert(usb_uart->serial_handle);\n\n    furi_hal_serial_deinit(usb_uart->serial_handle);\n    furi_hal_serial_control_release(usb_uart->serial_handle);\n    usb_uart->serial_handle = NULL;\n}\n\nstatic void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {\n    if(baudrate != 0) {\n        furi_hal_serial_set_br(usb_uart->serial_handle, baudrate);\n        usb_uart->st.baudrate_cur = baudrate;\n    } else {\n        struct usb_cdc_line_coding* line_cfg =\n            furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);\n        if(line_cfg->dwDTERate > 0) {\n            furi_hal_serial_set_br(usb_uart->serial_handle, line_cfg->dwDTERate);\n            usb_uart->st.baudrate_cur = line_cfg->dwDTERate;\n        }\n    }\n}\n\nstatic void usb_uart_update_ctrl_lines(UsbUartBridge* usb_uart) {\n    if(usb_uart->cfg.flow_pins != 0) {\n        furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins));\n        uint8_t state = furi_hal_cdc_get_ctrl_line_state(usb_uart->cfg.vcp_ch);\n\n        furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][0], !(state & USB_CDC_BIT_RTS));\n        furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][1], !(state & USB_CDC_BIT_DTR));\n    }\n}\n\nstatic int32_t usb_uart_worker(void* context) {\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n\n    memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig));\n\n    usb_uart->cli_vcp = furi_record_open(RECORD_CLI_VCP);\n\n    usb_uart->rx_stream = furi_stream_buffer_alloc(USB_UART_RX_BUF_SIZE, 1);\n\n    usb_uart->tx_sem = furi_semaphore_alloc(1, 1);\n    usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    usb_uart->tx_thread =\n        furi_thread_alloc_ex(\"UsbUartTxWorker\", 768, usb_uart_tx_thread, usb_uart);\n\n    usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);\n    usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);\n    usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);\n    if(usb_uart->cfg.flow_pins != 0) {\n        furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins));\n        furi_hal_gpio_init_simple(\n            flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeOutputPushPull);\n        furi_hal_gpio_init_simple(\n            flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeOutputPushPull);\n        usb_uart_update_ctrl_lines(usb_uart);\n    }\n\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx);\n\n    furi_thread_start(usb_uart->tx_thread);\n\n    while(1) {\n        uint32_t events =\n            furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);\n        furi_check(!(events & FuriFlagError));\n        if(events & WorkerEvtStop) break;\n        if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) {\n            size_t len = furi_stream_buffer_receive(\n                usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);\n            if(len > 0) {\n                if(furi_semaphore_acquire(usb_uart->tx_sem, 100) == FuriStatusOk) {\n                    usb_uart->st.rx_cnt += len;\n                    furi_check(\n                        furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);\n                    furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);\n                    furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk);\n                } else {\n                    furi_stream_buffer_reset(usb_uart->rx_stream);\n                }\n            }\n        }\n        if(events & WorkerEvtCfgChange) {\n            if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) {\n                furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);\n                furi_thread_join(usb_uart->tx_thread);\n\n                usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);\n                usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch);\n\n                usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch;\n                furi_thread_start(usb_uart->tx_thread);\n                events |= WorkerEvtCtrlLineSet;\n                events |= WorkerEvtLineCfgSet;\n            }\n            if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) {\n                furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);\n                furi_thread_join(usb_uart->tx_thread);\n\n                usb_uart_serial_deinit(usb_uart);\n                usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch);\n\n                usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch;\n                usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);\n\n                furi_thread_start(usb_uart->tx_thread);\n            }\n            if(usb_uart->cfg.baudrate != usb_uart->cfg_new.baudrate) {\n                usb_uart_set_baudrate(usb_uart, usb_uart->cfg_new.baudrate);\n                usb_uart->cfg.baudrate = usb_uart->cfg_new.baudrate;\n            }\n            if(usb_uart->cfg.flow_pins != usb_uart->cfg_new.flow_pins) {\n                if(usb_uart->cfg.flow_pins != 0) {\n                    furi_hal_gpio_init_simple(\n                        flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);\n                    furi_hal_gpio_init_simple(\n                        flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);\n                }\n                if(usb_uart->cfg_new.flow_pins != 0) {\n                    furi_assert((size_t)(usb_uart->cfg_new.flow_pins - 1) < COUNT_OF(flow_pins));\n                    furi_hal_gpio_init_simple(\n                        flow_pins[usb_uart->cfg_new.flow_pins - 1][0], GpioModeOutputPushPull);\n                    furi_hal_gpio_init_simple(\n                        flow_pins[usb_uart->cfg_new.flow_pins - 1][1], GpioModeOutputPushPull);\n                }\n                usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;\n                events |= WorkerEvtCtrlLineSet;\n            }\n            if(usb_uart->cfg.software_de_re != usb_uart->cfg_new.software_de_re) {\n                usb_uart->cfg.software_de_re = usb_uart->cfg_new.software_de_re;\n                if(usb_uart->cfg.software_de_re != 0) {\n                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);\n                    furi_hal_gpio_init(\n                        USB_USART_DE_RE_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedMedium);\n                } else {\n                    furi_hal_gpio_init(\n                        USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n                }\n            }\n            api_lock_unlock(usb_uart->cfg_lock);\n        }\n        if(events & WorkerEvtLineCfgSet) {\n            if(usb_uart->cfg.baudrate == 0)\n                usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);\n        }\n        if(events & WorkerEvtCtrlLineSet) {\n            usb_uart_update_ctrl_lines(usb_uart);\n        }\n    }\n\n    furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n\n    if(usb_uart->cfg.flow_pins != 0) {\n        furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);\n        furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);\n    }\n\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);\n    furi_thread_join(usb_uart->tx_thread);\n    furi_thread_free(usb_uart->tx_thread);\n\n    usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);\n    usb_uart_serial_deinit(usb_uart);\n\n    furi_stream_buffer_free(usb_uart->rx_stream);\n    furi_mutex_free(usb_uart->usb_mutex);\n    furi_semaphore_free(usb_uart->tx_sem);\n\n    furi_hal_usb_unlock();\n    furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);\n    cli_vcp_enable(usb_uart->cli_vcp);\n\n    furi_record_close(RECORD_CLI_VCP);\n\n    return 0;\n}\n\nstatic int32_t usb_uart_tx_thread(void* context) {\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n\n    uint8_t data[USB_CDC_PKT_LEN];\n    while(1) {\n        uint32_t events =\n            furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);\n        furi_check(!(events & FuriFlagError));\n        if(events & WorkerEvtTxStop) break;\n        if(events & WorkerEvtCdcRx) {\n            furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);\n            size_t len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN);\n            furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk);\n\n            if(len > 0) {\n                usb_uart->st.tx_cnt += len;\n\n                if(usb_uart->cfg.software_de_re != 0)\n                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, false);\n\n                furi_hal_serial_tx(usb_uart->serial_handle, data, len);\n\n                if(usb_uart->cfg.software_de_re != 0) {\n                    furi_hal_serial_tx_wait_complete(usb_uart->serial_handle);\n                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);\n                }\n            }\n        }\n    }\n    return 0;\n}\n\n/* VCP callbacks */\n\nstatic void vcp_on_cdc_tx_complete(void* context) {\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n    furi_semaphore_release(usb_uart->tx_sem);\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcTxComplete);\n}\n\nstatic void vcp_on_cdc_rx(void* context) {\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx);\n}\n\nstatic void vcp_state_callback(void* context, uint8_t state) {\n    UNUSED(context);\n    UNUSED(state);\n}\n\nstatic void vcp_on_cdc_control_line(void* context, uint8_t state) {\n    UNUSED(state);\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCtrlLineSet);\n}\n\nstatic void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {\n    UNUSED(config);\n    UsbUartBridge* usb_uart = (UsbUartBridge*)context;\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtLineCfgSet);\n}\n\nUsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {\n    UsbUartBridge* usb_uart = malloc(sizeof(UsbUartBridge));\n\n    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));\n\n    usb_uart->thread = furi_thread_alloc_ex(\"UsbUartWorker\", 1024, usb_uart_worker, usb_uart);\n\n    furi_thread_start(usb_uart->thread);\n    return usb_uart;\n}\n\nvoid usb_uart_disable(UsbUartBridge* usb_uart) {\n    furi_assert(usb_uart);\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop);\n    furi_thread_join(usb_uart->thread);\n    furi_thread_free(usb_uart->thread);\n    free(usb_uart);\n}\n\nvoid usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {\n    furi_assert(usb_uart);\n    furi_assert(cfg);\n    usb_uart->cfg_lock = api_lock_alloc_locked();\n    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));\n    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);\n    api_lock_wait_unlock_and_free(usb_uart->cfg_lock);\n}\n\nvoid usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {\n    furi_assert(usb_uart);\n    furi_assert(cfg);\n    memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig));\n}\n\nvoid usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st) {\n    furi_assert(usb_uart);\n    furi_assert(st);\n    memcpy(st, &(usb_uart->st), sizeof(UsbUartState));\n}\n"
  },
  {
    "path": "applications/main/gpio/usb_uart_bridge.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\ntypedef struct UsbUartBridge UsbUartBridge;\n\ntypedef struct {\n    uint8_t vcp_ch;\n    uint8_t uart_ch;\n    uint8_t flow_pins;\n    uint8_t baudrate_mode;\n    uint32_t baudrate;\n    uint8_t software_de_re;\n} UsbUartConfig;\n\ntypedef struct {\n    uint32_t rx_cnt;\n    uint32_t tx_cnt;\n    uint32_t baudrate_cur;\n} UsbUartState;\n\nUsbUartBridge* usb_uart_enable(UsbUartConfig* cfg);\n\nvoid usb_uart_disable(UsbUartBridge* usb_uart);\n\nvoid usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);\n\nvoid usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);\n\nvoid usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st);\n"
  },
  {
    "path": "applications/main/gpio/views/gpio_test.c",
    "content": "#include \"gpio_test.h\"\n#include \"../gpio_items.h\"\n\n#include <gui/elements.h>\n\nstruct GpioTest {\n    View* view;\n    GpioTestOkCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint8_t pin_idx;\n    GPIOItems* gpio_items;\n} GpioTestModel;\n\nstatic bool gpio_test_process_left(GpioTest* gpio_test);\nstatic bool gpio_test_process_right(GpioTest* gpio_test);\nstatic bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event);\n\nstatic void gpio_test_draw_callback(Canvas* canvas, void* _model) {\n    GpioTestModel* model = _model;\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, \"GPIO Output Mode Test\");\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(\n        canvas, 64, 16, AlignCenter, AlignTop, \"Press < or > to change pin\");\n    elements_multiline_text_aligned(\n        canvas,\n        64,\n        32,\n        AlignCenter,\n        AlignTop,\n        gpio_items_get_pin_name(model->gpio_items, model->pin_idx));\n}\n\nstatic bool gpio_test_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    GpioTest* gpio_test = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyRight) {\n            consumed = gpio_test_process_right(gpio_test);\n        } else if(event->key == InputKeyLeft) {\n            consumed = gpio_test_process_left(gpio_test);\n        }\n    } else if(event->key == InputKeyOk) {\n        consumed = gpio_test_process_ok(gpio_test, event);\n    }\n\n    return consumed;\n}\n\nstatic bool gpio_test_process_left(GpioTest* gpio_test) {\n    with_view_model(\n        gpio_test->view,\n        GpioTestModel * model,\n        {\n            if(model->pin_idx) {\n                model->pin_idx--;\n            }\n        },\n        true);\n    return true;\n}\n\nstatic bool gpio_test_process_right(GpioTest* gpio_test) {\n    with_view_model(\n        gpio_test->view,\n        GpioTestModel * model,\n        {\n            if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {\n                model->pin_idx++;\n            }\n        },\n        true);\n    return true;\n}\n\nstatic bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {\n    bool consumed = false;\n\n    with_view_model(\n        gpio_test->view,\n        GpioTestModel * model,\n        {\n            if(event->type == InputTypePress) {\n                if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {\n                    gpio_items_set_pin(model->gpio_items, model->pin_idx, true);\n                } else {\n                    gpio_items_set_all_pins(model->gpio_items, true);\n                }\n                consumed = true;\n            } else if(event->type == InputTypeRelease) {\n                if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {\n                    gpio_items_set_pin(model->gpio_items, model->pin_idx, false);\n                } else {\n                    gpio_items_set_all_pins(model->gpio_items, false);\n                }\n                consumed = true;\n            }\n            gpio_test->callback(event->type, gpio_test->context);\n        },\n        true);\n\n    return consumed;\n}\n\nGpioTest* gpio_test_alloc(GPIOItems* gpio_items) {\n    GpioTest* gpio_test = malloc(sizeof(GpioTest));\n\n    gpio_test->view = view_alloc();\n    view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));\n\n    with_view_model(\n        gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false);\n\n    view_set_context(gpio_test->view, gpio_test);\n    view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);\n    view_set_input_callback(gpio_test->view, gpio_test_input_callback);\n\n    return gpio_test;\n}\n\nvoid gpio_test_free(GpioTest* gpio_test) {\n    furi_assert(gpio_test);\n    view_free(gpio_test->view);\n    free(gpio_test);\n}\n\nView* gpio_test_get_view(GpioTest* gpio_test) {\n    furi_assert(gpio_test);\n    return gpio_test->view;\n}\n\nvoid gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) {\n    furi_assert(gpio_test);\n    furi_assert(callback);\n    with_view_model(\n        gpio_test->view,\n        GpioTestModel * model,\n        {\n            UNUSED(model);\n            gpio_test->callback = callback;\n            gpio_test->context = context;\n        },\n        false);\n}\n"
  },
  {
    "path": "applications/main/gpio/views/gpio_test.h",
    "content": "#pragma once\n\n#include \"../gpio_items.h\"\n\n#include <gui/view.h>\n\ntypedef struct GpioTest GpioTest;\ntypedef void (*GpioTestOkCallback)(InputType type, void* context);\n\nGpioTest* gpio_test_alloc(GPIOItems* gpio_items);\n\nvoid gpio_test_free(GpioTest* gpio_test);\n\nView* gpio_test_get_view(GpioTest* gpio_test);\n\nvoid gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context);\n"
  },
  {
    "path": "applications/main/gpio/views/gpio_usb_uart.c",
    "content": "#include \"gpio_usb_uart.h\"\n#include \"../usb_uart_bridge.h\"\n#include <furi_hal.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\nstruct GpioUsbUart {\n    View* view;\n    GpioUsbUartCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint32_t baudrate;\n    uint32_t tx_cnt;\n    uint32_t rx_cnt;\n    uint8_t vcp_port;\n    uint8_t tx_pin;\n    uint8_t rx_pin;\n    bool tx_active;\n    bool rx_active;\n} GpioUsbUartModel;\n\nstatic void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {\n    GpioUsbUartModel* model = _model;\n    char temp_str[18];\n    elements_button_left(canvas, \"Config\");\n    canvas_draw_line(canvas, 2, 10, 125, 10);\n    canvas_draw_line(canvas, 44, 52, 123, 52);\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 2, 9, \"USB Serial\");\n    canvas_draw_str(canvas, 3, 25, \"TX:\");\n    canvas_draw_str(canvas, 3, 42, \"RX:\");\n\n    canvas_set_font(canvas, FontSecondary);\n    snprintf(temp_str, 18, \"COM PORT:%u\", model->vcp_port);\n    canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str);\n    snprintf(temp_str, 18, \"Pin %u\", model->tx_pin);\n    canvas_draw_str(canvas, 22, 25, temp_str);\n    snprintf(temp_str, 18, \"Pin %u\", model->rx_pin);\n    canvas_draw_str(canvas, 22, 42, temp_str);\n\n    if(model->baudrate == 0)\n        snprintf(temp_str, 18, \"Baud: ????\");\n    else\n        snprintf(temp_str, 18, \"Baud: %lu\", model->baudrate);\n    canvas_draw_str(canvas, 45, 62, temp_str);\n\n    if(model->tx_cnt < 100000000) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, \"B.\");\n        canvas_set_font(canvas, FontKeyboard);\n        snprintf(temp_str, 18, \"%lu\", model->tx_cnt);\n        canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str);\n    } else {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, \"KiB.\");\n        canvas_set_font(canvas, FontKeyboard);\n        snprintf(temp_str, 18, \"%lu\", model->tx_cnt / 1024);\n        canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str);\n    }\n\n    if(model->rx_cnt < 100000000) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, \"B.\");\n        canvas_set_font(canvas, FontKeyboard);\n        snprintf(temp_str, 18, \"%lu\", model->rx_cnt);\n        canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str);\n    } else {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, \"KiB.\");\n        canvas_set_font(canvas, FontKeyboard);\n        snprintf(temp_str, 18, \"%lu\", model->rx_cnt / 1024);\n        canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str);\n    }\n\n    if(model->tx_active)\n        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15);\n    else\n        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);\n\n    if(model->rx_active)\n        canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);\n    else\n        canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);\n}\n\nstatic bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    GpioUsbUart* usb_uart = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyLeft) {\n            consumed = true;\n            furi_assert(usb_uart->callback);\n            usb_uart->callback(GpioUsbUartEventConfig, usb_uart->context);\n        }\n    }\n\n    return consumed;\n}\n\nGpioUsbUart* gpio_usb_uart_alloc(void) {\n    GpioUsbUart* usb_uart = malloc(sizeof(GpioUsbUart));\n\n    usb_uart->view = view_alloc();\n    view_allocate_model(usb_uart->view, ViewModelTypeLocking, sizeof(GpioUsbUartModel));\n    view_set_context(usb_uart->view, usb_uart);\n    view_set_draw_callback(usb_uart->view, gpio_usb_uart_draw_callback);\n    view_set_input_callback(usb_uart->view, gpio_usb_uart_input_callback);\n\n    return usb_uart;\n}\n\nvoid gpio_usb_uart_free(GpioUsbUart* usb_uart) {\n    furi_assert(usb_uart);\n    view_free(usb_uart->view);\n    free(usb_uart);\n}\n\nView* gpio_usb_uart_get_view(GpioUsbUart* usb_uart) {\n    furi_assert(usb_uart);\n    return usb_uart->view;\n}\n\nvoid gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context) {\n    furi_assert(usb_uart);\n    furi_assert(callback);\n\n    with_view_model(\n        usb_uart->view,\n        GpioUsbUartModel * model,\n        {\n            UNUSED(model);\n            usb_uart->callback = callback;\n            usb_uart->context = context;\n        },\n        false);\n}\n\nvoid gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) {\n    furi_assert(instance);\n    furi_assert(cfg);\n    furi_assert(st);\n\n    with_view_model(\n        instance->view,\n        GpioUsbUartModel * model,\n        {\n            model->baudrate = st->baudrate_cur;\n            model->vcp_port = cfg->vcp_ch;\n            model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15);\n            model->rx_pin = (cfg->uart_ch == 0) ? (14) : (16);\n            model->tx_active = (model->tx_cnt != st->tx_cnt);\n            model->rx_active = (model->rx_cnt != st->rx_cnt);\n            model->tx_cnt = st->tx_cnt;\n            model->rx_cnt = st->rx_cnt;\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/main/gpio/views/gpio_usb_uart.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../gpio_custom_event.h\"\n#include \"../usb_uart_bridge.h\"\n\ntypedef struct GpioUsbUart GpioUsbUart;\ntypedef void (*GpioUsbUartCallback)(GpioCustomEvent event, void* context);\n\nGpioUsbUart* gpio_usb_uart_alloc(void);\n\nvoid gpio_usb_uart_free(GpioUsbUart* usb_uart);\n\nView* gpio_usb_uart_get_view(GpioUsbUart* usb_uart);\n\nvoid gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context);\n\nvoid gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st);\n"
  },
  {
    "path": "applications/main/ibutton/application.fam",
    "content": "App(\n    appid=\"ibutton\",\n    name=\"iButton\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    targets=[\"f7\"],\n    entry_point=\"ibutton_app\",\n    icon=\"A_iButton_14\",\n    stack_size=2 * 1024,\n    order=60,\n    fap_libs=[\"assets\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"iButton\",\n)\n\nApp(\n    appid=\"cli_ikey\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_ikey_ep\",\n    requires=[\"cli\"],\n    sources=[\"ibutton_cli.c\"],\n)\n"
  },
  {
    "path": "applications/main/ibutton/ibutton.c",
    "content": "#include \"ibutton_i.h\"\n\n#include <toolbox/path.h>\n#include <dolphin/dolphin.h>\n\n#define TAG \"IButtonApp\"\n\nstatic const NotificationSequence sequence_blink_set_yellow = {\n    &message_blink_set_color_yellow,\n    NULL,\n};\n\nstatic const NotificationSequence sequence_blink_set_magenta = {\n    &message_blink_set_color_magenta,\n    NULL,\n};\n\nstatic const NotificationSequence* ibutton_notification_sequences[] = {\n    &sequence_error,\n    &sequence_success,\n    &sequence_blink_start_cyan,\n    &sequence_blink_start_magenta,\n    &sequence_blink_set_yellow,\n    &sequence_blink_set_magenta,\n    &sequence_set_red_255,\n    &sequence_reset_red,\n    &sequence_set_green_255,\n    &sequence_reset_green,\n    &sequence_blink_stop,\n};\n\nstatic void ibutton_make_app_folder(iButton* ibutton) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) {\n        dialog_message_show_storage_error(ibutton->dialogs, \"Cannot create\\napp folder\");\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void ibutton_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    iButton* ibutton = context;\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        view_dispatcher_send_custom_event(\n            ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);\n        rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);\n        ibutton->rpc = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);\n    } else if(event->type == RpcAppEventTypeLoadFile) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeString);\n        furi_string_set(ibutton->file_path, event->data.string);\n        view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile);\n    } else {\n        rpc_system_app_confirm(ibutton->rpc, false);\n    }\n}\n\nbool ibutton_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    iButton* ibutton = context;\n    return scene_manager_handle_custom_event(ibutton->scene_manager, event);\n}\n\nbool ibutton_back_event_callback(void* context) {\n    furi_assert(context);\n    iButton* ibutton = context;\n    return scene_manager_handle_back_event(ibutton->scene_manager);\n}\n\nvoid ibutton_tick_event_callback(void* context) {\n    furi_assert(context);\n    iButton* ibutton = context;\n    scene_manager_handle_tick_event(ibutton->scene_manager);\n}\n\niButton* ibutton_alloc(void) {\n    iButton* ibutton = malloc(sizeof(iButton));\n\n    ibutton->file_path = furi_string_alloc();\n\n    ibutton->scene_manager = scene_manager_alloc(&ibutton_scene_handlers, ibutton);\n\n    ibutton->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_set_event_callback_context(ibutton->view_dispatcher, ibutton);\n    view_dispatcher_set_custom_event_callback(\n        ibutton->view_dispatcher, ibutton_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        ibutton->view_dispatcher, ibutton_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        ibutton->view_dispatcher, ibutton_tick_event_callback, 100);\n\n    ibutton->gui = furi_record_open(RECORD_GUI);\n\n    ibutton->dialogs = furi_record_open(RECORD_DIALOGS);\n    ibutton->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    ibutton->protocols = ibutton_protocols_alloc();\n    ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols));\n    ibutton->worker = ibutton_worker_alloc(ibutton->protocols);\n    ibutton_worker_start_thread(ibutton->worker);\n\n    ibutton->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewSubmenu, submenu_get_view(ibutton->submenu));\n\n    ibutton->byte_input = byte_input_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewByteInput, byte_input_get_view(ibutton->byte_input));\n\n    ibutton->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewTextInput, text_input_get_view(ibutton->text_input));\n\n    ibutton->popup = popup_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewPopup, popup_get_view(ibutton->popup));\n\n    ibutton->widget = widget_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget));\n\n    ibutton->loading = loading_alloc();\n    view_dispatcher_add_view(\n        ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading));\n\n    return ibutton;\n}\n\nvoid ibutton_free(iButton* ibutton) {\n    furi_assert(ibutton);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading);\n    loading_free(ibutton->loading);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget);\n    widget_free(ibutton->widget);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewPopup);\n    popup_free(ibutton->popup);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewTextInput);\n    text_input_free(ibutton->text_input);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewByteInput);\n    byte_input_free(ibutton->byte_input);\n\n    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewSubmenu);\n    submenu_free(ibutton->submenu);\n\n    view_dispatcher_free(ibutton->view_dispatcher);\n    scene_manager_free(ibutton->scene_manager);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    ibutton->notifications = NULL;\n\n    furi_record_close(RECORD_DIALOGS);\n    ibutton->dialogs = NULL;\n\n    furi_record_close(RECORD_GUI);\n    ibutton->gui = NULL;\n\n    ibutton_worker_stop_thread(ibutton->worker);\n    ibutton_worker_free(ibutton->worker);\n    ibutton_key_free(ibutton->key);\n    ibutton_protocols_free(ibutton->protocols);\n\n    furi_string_free(ibutton->file_path);\n\n    free(ibutton);\n}\n\nbool ibutton_load_key(iButton* ibutton, bool show_error) {\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);\n\n    const bool success = ibutton_protocols_load(\n        ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path));\n\n    if(success) {\n        FuriString* tmp = furi_string_alloc();\n\n        path_extract_filename(ibutton->file_path, tmp, true);\n        strlcpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE);\n\n        furi_string_free(tmp);\n    } else if(show_error) {\n        dialog_message_show_storage_error(ibutton->dialogs, \"Cannot load\\nkey file\");\n    }\n\n    return success;\n}\n\nbool ibutton_select_and_load_key(iButton* ibutton) {\n    DialogsFileBrowserOptions browser_options;\n    bool success = false;\n    dialog_file_browser_set_basic_options(\n        &browser_options, IBUTTON_APP_FILENAME_EXTENSION, &I_ibutt_10px);\n    browser_options.base_path = IBUTTON_APP_FOLDER;\n\n    if(furi_string_empty(ibutton->file_path)) {\n        furi_string_set(ibutton->file_path, browser_options.base_path);\n    }\n\n    do {\n        if(!dialog_file_browser_show(\n               ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options))\n            break;\n        success = ibutton_load_key(ibutton, true);\n    } while(!success);\n\n    return success;\n}\n\nbool ibutton_save_key(iButton* ibutton) {\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);\n\n    ibutton_make_app_folder(ibutton);\n\n    iButtonKey* key = ibutton->key;\n    const bool success =\n        ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path));\n\n    if(!success) {\n        dialog_message_show_storage_error(ibutton->dialogs, \"Cannot save\\nkey file\");\n    }\n\n    return success;\n}\n\nbool ibutton_delete_key(iButton* ibutton) {\n    bool result = false;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path));\n    furi_record_close(RECORD_STORAGE);\n\n    ibutton_reset_key(ibutton);\n\n    return result;\n}\n\nvoid ibutton_reset_key(iButton* ibutton) {\n    ibutton->key_name[0] = '\\0';\n    furi_string_reset(ibutton->file_path);\n    ibutton_key_reset(ibutton->key);\n}\n\nvoid ibutton_notification_message(iButton* ibutton, uint32_t message) {\n    furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*));\n    notification_message(ibutton->notifications, ibutton_notification_sequences[message]);\n}\n\nvoid ibutton_submenu_callback(void* context, uint32_t index) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);\n}\n\nvoid ibutton_widget_callback(GuiButtonType result, InputType type, void* context) {\n    iButton* ibutton = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);\n    }\n}\n\nint32_t ibutton_app(void* arg) {\n    iButton* ibutton = ibutton_alloc();\n\n    ibutton_make_app_folder(ibutton);\n\n    bool key_loaded = false;\n\n    if((arg != NULL) && (strlen(arg) != 0)) {\n        if(sscanf(arg, \"RPC %lX\", (uint32_t*)&ibutton->rpc) == 1) {\n            FURI_LOG_D(TAG, \"Running in RPC mode\");\n\n            rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton);\n            rpc_system_app_send_started(ibutton->rpc);\n\n        } else {\n            furi_string_set(ibutton->file_path, (const char*)arg);\n            key_loaded = ibutton_load_key(ibutton, true);\n        }\n    }\n\n    if(ibutton->rpc != NULL) {\n        view_dispatcher_attach_to_gui(\n            ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop);\n        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc);\n        dolphin_deed(DolphinDeedIbuttonEmulate);\n\n    } else {\n        view_dispatcher_attach_to_gui(\n            ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);\n        if(key_loaded) { //-V547\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);\n            dolphin_deed(DolphinDeedIbuttonEmulate);\n        } else {\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);\n        }\n    }\n\n    view_dispatcher_run(ibutton->view_dispatcher);\n\n    if(ibutton->rpc) {\n        rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);\n        rpc_system_app_send_exited(ibutton->rpc);\n    }\n    ibutton_free(ibutton);\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/ibutton/ibutton.h",
    "content": "#pragma once\n\ntypedef struct iButton iButton;\n"
  },
  {
    "path": "applications/main/ibutton/ibutton_cli.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <cli/cli_main_commands.h>\n#include <toolbox/args.h>\n#include <toolbox/pipe.h>\n\n#include <ibutton/ibutton_key.h>\n#include <ibutton/ibutton_worker.h>\n#include <ibutton/ibutton_protocols.h>\n\nstatic void ibutton_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"ikey read\\r\\n\");\n    printf(\"ikey emulate <key_type> <key_data>\\r\\n\");\n    printf(\"ikey write Dallas <key_data>\\r\\n\");\n    printf(\"\\t<key_type> choose from:\\r\\n\");\n    printf(\"\\tDallas (8 bytes key_data)\\r\\n\");\n    printf(\"\\tCyfral (2 bytes key_data)\\r\\n\");\n    printf(\"\\tMetakom (4 bytes key_data), must contain correct parity\\r\\n\");\n    printf(\"\\t<key_data> are hex-formatted\\r\\n\");\n}\n\nstatic bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) {\n    bool result = false;\n    FuriString* name = furi_string_alloc();\n\n    do {\n        // Read protocol name\n        if(!args_read_string_and_trim(args, name)) break;\n\n        // Make the protocol name uppercase\n        const char first = furi_string_get_char(name, 0);\n        furi_string_set_char(name, 0, toupper((int)first));\n\n        const iButtonProtocolId id =\n            ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name));\n        if(id == iButtonProtocolIdInvalid) break;\n\n        ibutton_key_set_protocol_id(key, id);\n\n        // Get the data pointer\n        iButtonEditableData data;\n        ibutton_protocols_get_editable_data(protocols, key, &data);\n\n        // Read data\n        if(!args_read_hex_bytes(args, data.ptr, data.size)) break;\n\n        result = true;\n    } while(false);\n\n    furi_string_free(name);\n    return result;\n}\n\nstatic void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) {\n    const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key));\n\n    if(strncmp(name, \"DS\", 2) == 0) {\n        name = \"Dallas\";\n    }\n\n    printf(\"%s \", name);\n\n    iButtonEditableData data;\n    ibutton_protocols_get_editable_data(protocols, key, &data);\n\n    for(size_t i = 0; i < data.size; i++) {\n        printf(\"%02X\", data.ptr[i]);\n    }\n\n    printf(\"\\r\\n\");\n}\n\n#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0)\n\nstatic void ibutton_cli_worker_read_cb(void* context) {\n    furi_assert(context);\n    FuriEventFlag* event = context;\n    furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);\n}\n\nstatic void ibutton_cli_read(PipeSide* pipe) {\n    iButtonProtocols* protocols = ibutton_protocols_alloc();\n    iButtonWorker* worker = ibutton_worker_alloc(protocols);\n    iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));\n    FuriEventFlag* event = furi_event_flag_alloc();\n\n    ibutton_worker_start_thread(worker);\n    ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event);\n\n    printf(\"Reading iButton...\\r\\nPress Ctrl+C to abort\\r\\n\");\n    ibutton_worker_read_start(worker, key);\n\n    while(true) {\n        uint32_t flags =\n            furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);\n\n        if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {\n            ibutton_cli_print_key(protocols, key);\n            break;\n        }\n\n        if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n    }\n\n    ibutton_worker_stop(worker);\n    ibutton_worker_stop_thread(worker);\n\n    ibutton_key_free(key);\n    ibutton_worker_free(worker);\n    ibutton_protocols_free(protocols);\n\n    furi_event_flag_free(event);\n}\n\ntypedef struct {\n    FuriEventFlag* event;\n    iButtonWorkerWriteResult result;\n} iButtonWriteContext;\n\nstatic void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) {\n    furi_assert(context);\n    iButtonWriteContext* write_context = (iButtonWriteContext*)context;\n    write_context->result = result;\n    furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);\n}\n\nvoid ibutton_cli_write(PipeSide* pipe, FuriString* args) {\n    iButtonProtocols* protocols = ibutton_protocols_alloc();\n    iButtonWorker* worker = ibutton_worker_alloc(protocols);\n    iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));\n\n    iButtonWriteContext write_context;\n    write_context.event = furi_event_flag_alloc();\n\n    ibutton_worker_start_thread(worker);\n    ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);\n\n    do {\n        if(!ibutton_cli_parse_key(protocols, key, args)) {\n            ibutton_cli_print_usage();\n            break;\n        }\n\n        if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) &\n             iButtonProtocolFeatureWriteId)) {\n            ibutton_cli_print_usage();\n            break;\n        }\n\n        printf(\"Writing key \");\n        ibutton_cli_print_key(protocols, key);\n        printf(\"Press Ctrl+C to abort\\r\\n\");\n\n        ibutton_worker_write_id_start(worker, key);\n        while(true) {\n            uint32_t flags = furi_event_flag_wait(\n                write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);\n\n            if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {\n                if(write_context.result == iButtonWorkerWriteSameKey ||\n                   write_context.result == iButtonWorkerWriteOK) {\n                    printf(\"Write success\\r\\n\");\n                    break;\n                } else if(write_context.result == iButtonWorkerWriteCannotWrite) {\n                    printf(\"Write fail\\r\\n\");\n                    break;\n                }\n            }\n\n            if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n        }\n    } while(false);\n\n    ibutton_worker_stop(worker);\n    ibutton_worker_stop_thread(worker);\n\n    ibutton_key_free(key);\n    ibutton_worker_free(worker);\n    ibutton_protocols_free(protocols);\n\n    furi_event_flag_free(write_context.event);\n}\n\nvoid ibutton_cli_emulate(PipeSide* pipe, FuriString* args) {\n    iButtonProtocols* protocols = ibutton_protocols_alloc();\n    iButtonWorker* worker = ibutton_worker_alloc(protocols);\n    iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));\n\n    ibutton_worker_start_thread(worker);\n\n    do {\n        if(!ibutton_cli_parse_key(protocols, key, args)) {\n            ibutton_cli_print_usage();\n            break;\n        }\n\n        printf(\"Emulating key \");\n        ibutton_cli_print_key(protocols, key);\n        printf(\"Press Ctrl+C to abort\\r\\n\");\n\n        ibutton_worker_emulate_start(worker, key);\n\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(100);\n        };\n\n    } while(false);\n\n    ibutton_worker_stop(worker);\n    ibutton_worker_stop_thread(worker);\n\n    ibutton_key_free(key);\n    ibutton_worker_free(worker);\n    ibutton_protocols_free(protocols);\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    if(!args_read_string_and_trim(args, cmd)) {\n        furi_string_free(cmd);\n        ibutton_cli_print_usage();\n        return;\n    }\n\n    if(furi_string_cmp_str(cmd, \"read\") == 0) {\n        ibutton_cli_read(pipe);\n    } else if(furi_string_cmp_str(cmd, \"write\") == 0) {\n        ibutton_cli_write(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"emulate\") == 0) {\n        ibutton_cli_emulate(pipe, args);\n    } else {\n        ibutton_cli_print_usage();\n    }\n\n    furi_string_free(cmd);\n}\n\nCLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024, CLI_APPID);\n"
  },
  {
    "path": "applications/main/ibutton/ibutton_custom_event.h",
    "content": "#pragma once\n\ntypedef enum {\n    // Reserve first 100 events for button types and indexes, starting from 0\n    iButtonCustomEventReserved = 100,\n\n    iButtonCustomEventBack,\n    iButtonCustomEventTextEditResult,\n    iButtonCustomEventByteEditChanged,\n    iButtonCustomEventByteEditResult,\n    iButtonCustomEventWorkerEmulated,\n    iButtonCustomEventWorkerRead,\n    iButtonCustomEventWorkerWriteOK,\n    iButtonCustomEventWorkerWriteSameKey,\n    iButtonCustomEventWorkerWriteNoDetect,\n    iButtonCustomEventWorkerWriteCannotWrite,\n\n    iButtonCustomEventRpcLoadFile,\n    iButtonCustomEventRpcExit,\n    iButtonCustomEventRpcSessionClose,\n} iButtonCustomEvent;\n"
  },
  {
    "path": "applications/main/ibutton/ibutton_i.h",
    "content": "#pragma once\n\n#include \"ibutton.h\"\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n\n#include <ibutton/ibutton_worker.h>\n#include <ibutton/ibutton_protocols.h>\n\n#include <rpc/rpc_app.h>\n#include <storage/storage.h>\n#include <dialogs/dialogs.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\n#include <gui/modules/submenu.h>\n#include <gui/modules/popup.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/byte_input.h>\n#include <gui/modules/widget.h>\n#include <gui/modules/loading.h>\n\n#include <assets_icons.h>\n\n#include \"ibutton_custom_event.h\"\n#include \"scenes/ibutton_scene.h\"\n\n#define IBUTTON_APP_FOLDER             EXT_PATH(\"ibutton\")\n#define IBUTTON_APP_FILENAME_PREFIX    \"iBtn\"\n#define IBUTTON_APP_FILENAME_EXTENSION \".ibtn\"\n\n#define IBUTTON_KEY_NAME_SIZE 23\n\ntypedef enum {\n    iButtonWriteModeInvalid,\n    iButtonWriteModeId,\n    iButtonWriteModeCopy,\n} iButtonWriteMode;\n\nstruct iButton {\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    Gui* gui;\n    Storage* storage;\n    DialogsApp* dialogs;\n    NotificationApp* notifications;\n    RpcAppSystem* rpc;\n\n    iButtonKey* key;\n    iButtonWorker* worker;\n    iButtonProtocols* protocols;\n    iButtonWriteMode write_mode;\n\n    FuriString* file_path;\n    char key_name[IBUTTON_KEY_NAME_SIZE];\n\n    Submenu* submenu;\n    ByteInput* byte_input;\n    TextInput* text_input;\n    Popup* popup;\n    Widget* widget;\n    Loading* loading;\n};\n\ntypedef enum {\n    iButtonViewSubmenu,\n    iButtonViewByteInput,\n    iButtonViewTextInput,\n    iButtonViewPopup,\n    iButtonViewWidget,\n    iButtonViewLoading,\n} iButtonView;\n\ntypedef enum {\n    iButtonNotificationMessageError,\n    iButtonNotificationMessageSuccess,\n    iButtonNotificationMessageReadStart,\n    iButtonNotificationMessageEmulateStart,\n    iButtonNotificationMessageYellowBlink,\n    iButtonNotificationMessageEmulateBlink,\n    iButtonNotificationMessageRedOn,\n    iButtonNotificationMessageRedOff,\n    iButtonNotificationMessageGreenOn,\n    iButtonNotificationMessageGreenOff,\n    iButtonNotificationMessageBlinkStop,\n} iButtonNotificationMessage;\n\nbool ibutton_select_and_load_key(iButton* ibutton);\nbool ibutton_load_key(iButton* ibutton, bool show_error);\nbool ibutton_save_key(iButton* ibutton);\nbool ibutton_delete_key(iButton* ibutton);\nvoid ibutton_reset_key(iButton* ibutton);\nvoid ibutton_notification_message(iButton* ibutton, uint32_t message);\n\nvoid ibutton_submenu_callback(void* context, uint32_t index);\nvoid ibutton_widget_callback(GuiButtonType result, InputType type, void* context);\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene.c",
    "content": "#include \"ibutton_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const ibutton_on_enter_handlers[])(void*) = {\n#include \"ibutton_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const ibutton_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"ibutton_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const ibutton_on_exit_handlers[])(void* context) = {\n#include \"ibutton_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers ibutton_scene_handlers = {\n    .on_enter_handlers = ibutton_on_enter_handlers,\n    .on_event_handlers = ibutton_on_event_handlers,\n    .on_exit_handlers = ibutton_on_exit_handlers,\n    .scene_num = iButtonSceneNum,\n};\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) iButtonScene##id,\ntypedef enum {\n#include \"ibutton_scene_config.h\"\n    iButtonSceneNum,\n} iButtonScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers ibutton_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"ibutton_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"ibutton_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"ibutton_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_add_type.c",
    "content": "#include \"../ibutton_i.h\"\n\nvoid ibutton_scene_add_type_on_enter(void* context) {\n    iButton* ibutton = context;\n    Submenu* submenu = ibutton->submenu;\n\n    FuriString* tmp = furi_string_alloc();\n\n    for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count();\n        ++protocol_id) {\n        furi_string_printf(\n            tmp,\n            \"%s %s\",\n            ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),\n            ibutton_protocols_get_name(ibutton->protocols, protocol_id));\n\n        submenu_add_item(\n            submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context);\n    }\n\n    const uint32_t prev_protocol_id =\n        scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType);\n    submenu_set_selected_item(submenu, prev_protocol_id);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const iButtonProtocolId protocol_id = event.event;\n\n        ibutton_key_reset(key);\n        ibutton_key_set_protocol_id(key, protocol_id);\n        ibutton_protocols_apply_edits(ibutton->protocols, key);\n\n        scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id);\n        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);\n\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_add_type_on_exit(void* context) {\n    iButton* ibutton = context;\n    submenu_reset(ibutton->submenu);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_add_value.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_add_type_byte_input_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);\n}\n\nstatic void ibutton_scene_add_type_byte_changed_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged);\n}\n\nvoid ibutton_scene_add_value_on_enter(void* context) {\n    iButton* ibutton = context;\n    byte_input_set_header_text(ibutton->byte_input, \"Enter the key\");\n\n    iButtonEditableData editable_data;\n    ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data);\n\n    byte_input_set_result_callback(\n        ibutton->byte_input,\n        ibutton_scene_add_type_byte_input_callback,\n        ibutton_scene_add_type_byte_changed_callback,\n        context,\n        editable_data.ptr,\n        editable_data.size);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput);\n}\n\nbool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventByteEditResult) {\n            if(scene_manager_has_previous_scene(scene_manager, iButtonSceneAddType)) {\n                ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);\n                scene_manager_next_scene(scene_manager, iButtonSceneSaveName);\n            } else {\n                furi_string_printf(\n                    ibutton->file_path,\n                    \"%s/%s%s\",\n                    IBUTTON_APP_FOLDER,\n                    ibutton->key_name,\n                    IBUTTON_APP_FILENAME_EXTENSION);\n\n                if(ibutton_save_key(ibutton)) {\n                    scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);\n\n                } else {\n                    const uint32_t possible_scenes[] = {\n                        iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};\n                    scene_manager_search_and_switch_to_previous_scene_one_of(\n                        ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n                }\n            }\n        } else if(event.event == iButtonCustomEventByteEditChanged) {\n            ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        // User cancelled editing, reload the key from storage\n        if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) {\n            if(!ibutton_load_key(ibutton, true)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    scene_manager, iButtonSceneStart);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_add_value_on_exit(void* context) {\n    iButton* ibutton = context;\n\n    byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(ibutton->byte_input, NULL);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_config.h",
    "content": "ADD_SCENE(ibutton, start, Start)\nADD_SCENE(ibutton, emulate, Emulate)\nADD_SCENE(ibutton, write, Write)\nADD_SCENE(ibutton, write_success, WriteSuccess)\nADD_SCENE(ibutton, info, Info)\nADD_SCENE(ibutton, read, Read)\nADD_SCENE(ibutton, read_key_menu, ReadKeyMenu)\nADD_SCENE(ibutton, read_success, ReadSuccess)\nADD_SCENE(ibutton, read_error, ReadError)\nADD_SCENE(ibutton, select_key, SelectKey)\nADD_SCENE(ibutton, add_type, AddType)\nADD_SCENE(ibutton, add_value, AddValue)\nADD_SCENE(ibutton, saved_key_menu, SavedKeyMenu)\nADD_SCENE(ibutton, save_name, SaveName)\nADD_SCENE(ibutton, save_success, SaveSuccess)\nADD_SCENE(ibutton, delete_confirm, DeleteConfirm)\nADD_SCENE(ibutton, delete_success, DeleteSuccess)\nADD_SCENE(ibutton, retry_confirm, RetryConfirm)\nADD_SCENE(ibutton, exit_confirm, ExitConfirm)\nADD_SCENE(ibutton, view_data, ViewData)\nADD_SCENE(ibutton, rpc, Rpc)\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c",
    "content": "#include \"../ibutton_i.h\"\n#include <toolbox/path.h>\n\nvoid ibutton_scene_delete_confirm_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n    Widget* widget = ibutton->widget;\n\n    FuriString* tmp = furi_string_alloc();\n    FuriString* uid = furi_string_alloc();\n\n    widget_add_button_element(widget, GuiButtonTypeLeft, \"Back\", ibutton_widget_callback, context);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"Delete\", ibutton_widget_callback, context);\n\n    furi_string_printf(tmp, \"\\e#Delete %s?\\e#\\n\", ibutton->key_name);\n\n    ibutton_protocols_render_uid(ibutton->protocols, key, uid);\n\n    furi_string_cat_printf(\n        uid,\n        \"\\n%s %s\",\n        ibutton_protocols_get_manufacturer(ibutton->protocols, ibutton_key_get_protocol_id(key)),\n        ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));\n\n    furi_string_cat(tmp, uid);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);\n\n    furi_string_reset(tmp);\n    furi_string_reset(uid);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n    furi_string_free(tmp);\n    furi_string_free(uid);\n}\n\nbool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeRight) {\n            if(ibutton_delete_key(ibutton)) {\n                scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess);\n            } else {\n                dialog_message_show_storage_error(ibutton->dialogs, \"Cannot delete\\nkey file\");\n                scene_manager_previous_scene(scene_manager);\n            }\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_delete_confirm_on_exit(void* context) {\n    iButton* ibutton = context;\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_delete_success.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_delete_success_popup_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);\n}\n\nvoid ibutton_scene_delete_success_on_enter(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(popup, \"Deleted\", 80, 19, AlignLeft, AlignBottom);\n    popup_set_callback(popup, ibutton_scene_delete_success_popup_callback);\n    popup_set_context(popup, ibutton);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);\n}\n\nbool ibutton_scene_delete_success_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventBack) {\n            scene_manager_search_and_switch_to_previous_scene(\n                ibutton->scene_manager, iButtonSceneSelectKey);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_delete_success_on_exit(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_emulate.c",
    "content": "#include \"../ibutton_i.h\"\n#include <core/log.h>\n#include <toolbox/path.h>\n\n#define EMULATE_TIMEOUT_TICKS 10\n\nstatic void ibutton_scene_emulate_callback(void* context, bool emulated) {\n    iButton* ibutton = context;\n    if(emulated) {\n        view_dispatcher_send_custom_event(\n            ibutton->view_dispatcher, iButtonCustomEventWorkerEmulated);\n    }\n}\n\nvoid ibutton_scene_emulate_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n\n    Widget* widget = ibutton->widget;\n    FuriString* tmp = furi_string_alloc();\n\n    widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);\n\n    if(furi_string_empty(ibutton->file_path)) {\n        furi_string_printf(\n            tmp,\n            \"Unsaved\\n%s\",\n            ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));\n    } else {\n        furi_string_printf(tmp, \"%s\", ibutton->key_name);\n    }\n\n    widget_add_text_box_element(\n        widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);\n\n    widget_add_string_multiline_element(\n        widget, 88, 10, AlignCenter, AlignTop, FontPrimary, \"Emulating\");\n\n    ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);\n    ibutton_worker_emulate_start(ibutton->worker, key);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate);\n        if(cnt > 0) {\n            if(--cnt == 0) {\n                ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);\n            }\n            scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt);\n        }\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventWorkerEmulated) {\n            if(scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate) == 0) {\n                ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);\n            }\n            scene_manager_set_scene_state(\n                ibutton->scene_manager, iButtonSceneEmulate, EMULATE_TIMEOUT_TICKS);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_emulate_on_exit(void* context) {\n    iButton* ibutton = context;\n    ibutton_worker_stop(ibutton->worker);\n    widget_reset(ibutton->widget);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_exit_confirm_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    iButton* ibutton = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);\n    }\n}\n\nvoid ibutton_scene_exit_confirm_on_enter(void* context) {\n    iButton* ibutton = context;\n    Widget* widget = ibutton->widget;\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Exit\", ibutton_scene_exit_confirm_widget_callback, ibutton);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"Stay\", ibutton_scene_exit_confirm_widget_callback, ibutton);\n    widget_add_string_element(\n        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, \"Exit to iButton Menu?\");\n    widget_add_string_element(\n        widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, \"All unsaved data will be lost!\");\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n}\n\nbool ibutton_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true; // Ignore Back button presses\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneStart);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_exit_confirm_on_exit(void* context) {\n    iButton* ibutton = context;\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_info.c",
    "content": "#include \"../ibutton_i.h\"\n\nvoid ibutton_scene_info_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n    Widget* widget = ibutton->widget;\n\n    const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);\n\n    FuriString* tmp = furi_string_alloc();\n    FuriString* brief_data = furi_string_alloc();\n\n    furi_string_printf(\n        tmp,\n        \"Name:%s\\n\\e#%s %s\\e#\\n\",\n        ibutton->key_name,\n        ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),\n        ibutton_protocols_get_name(ibutton->protocols, protocol_id));\n\n    ibutton_protocols_render_brief_data(ibutton->protocols, key, brief_data);\n\n    furi_string_cat(tmp, brief_data);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), false);\n\n    furi_string_reset(tmp);\n    furi_string_reset(brief_data);\n\n    if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &\n       iButtonProtocolFeatureExtData) {\n        widget_add_button_element(\n            widget, GuiButtonTypeRight, \"More\", ibutton_widget_callback, context);\n    }\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n    furi_string_free(tmp);\n    furi_string_free(brief_data);\n}\n\nbool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_info_on_exit(void* context) {\n    iButton* ibutton = context;\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_read.c",
    "content": "#include \"../ibutton_i.h\"\n#include <dolphin/dolphin.h>\n\nstatic void ibutton_scene_read_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventWorkerRead);\n}\n\nvoid ibutton_scene_read_on_enter(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n    iButtonKey* key = ibutton->key;\n    iButtonWorker* worker = ibutton->worker;\n\n    popup_set_header(popup, \"Reading\", 95, 26, AlignCenter, AlignBottom);\n    popup_set_text(popup, \"Connect key\\nwith pogo pins\", 95, 30, AlignCenter, AlignTop);\n    popup_set_icon(popup, 0, 10, &I_DolphinWait_59x54);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);\n\n    ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton);\n    ibutton_worker_read_start(worker, key);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageReadStart);\n}\n\nbool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventWorkerRead) {\n            if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) {\n                ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);\n                scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);\n\n                dolphin_deed(DolphinDeedIbuttonReadSuccess);\n\n            } else {\n                scene_manager_next_scene(scene_manager, iButtonSceneReadError);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_read_on_exit(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n    ibutton_worker_stop(ibutton->worker);\n    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);\n    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);\n    popup_set_icon(popup, 0, 0, NULL);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_read_error.c",
    "content": "#include \"../ibutton_i.h\"\n#include <one_wire/maxim_crc.h>\n\nvoid ibutton_scene_read_error_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n\n    Widget* widget = ibutton->widget;\n\n    FuriString* tmp = furi_string_alloc();\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Retry\", ibutton_widget_callback, context);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"More\", ibutton_widget_callback, context);\n\n    ibutton_protocols_render_error(ibutton->protocols, key, tmp);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 48, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageError);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n        scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);\n\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(scene_manager);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_read_error_on_exit(void* context) {\n    iButton* ibutton = context;\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c",
    "content": "#include \"../ibutton_i.h\"\n#include <dolphin/dolphin.h>\n\ntypedef enum {\n    SubmenuIndexSave,\n    SubmenuIndexEmulate,\n    SubmenuIndexViewData,\n    SubmenuIndexWriteId,\n    SubmenuIndexWriteCopy,\n} SubmenuIndex;\n\nvoid ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);\n}\n\nvoid ibutton_scene_read_key_menu_on_enter(void* context) {\n    iButton* ibutton = context;\n    Submenu* submenu = ibutton->submenu;\n\n    const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key);\n    const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id);\n\n    submenu_add_item(\n        submenu, \"Save\", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton);\n    submenu_add_item(\n        submenu,\n        \"Emulate\",\n        SubmenuIndexEmulate,\n        ibutton_scene_read_key_menu_submenu_callback,\n        ibutton);\n\n    if(features & iButtonProtocolFeatureWriteId) {\n        submenu_add_item(\n            submenu,\n            \"Write ID\",\n            SubmenuIndexWriteId,\n            ibutton_scene_read_key_menu_submenu_callback,\n            ibutton);\n    }\n\n    if(features & iButtonProtocolFeatureWriteCopy) {\n        submenu_add_item(\n            submenu,\n            \"Full Write on Same Type\",\n            SubmenuIndexWriteCopy,\n            ibutton_scene_read_key_menu_submenu_callback,\n            ibutton);\n    }\n\n    if(features & iButtonProtocolFeatureExtData) {\n        submenu_add_item(\n            submenu,\n            \"Data Info\",\n            SubmenuIndexViewData,\n            ibutton_scene_read_key_menu_submenu_callback,\n            ibutton);\n    }\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);\n}\n\nbool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event);\n        consumed = true;\n\n        if(event.event == SubmenuIndexSave) {\n            scene_manager_next_scene(scene_manager, iButtonSceneSaveName);\n        } else if(event.event == SubmenuIndexEmulate) {\n            scene_manager_next_scene(scene_manager, iButtonSceneEmulate);\n            dolphin_deed(DolphinDeedIbuttonEmulate);\n        } else if(event.event == SubmenuIndexViewData) {\n            scene_manager_next_scene(scene_manager, iButtonSceneViewData);\n        } else if(event.event == SubmenuIndexWriteId) {\n            ibutton->write_mode = iButtonWriteModeId;\n            scene_manager_next_scene(scene_manager, iButtonSceneWrite);\n        } else if(event.event == SubmenuIndexWriteCopy) {\n            ibutton->write_mode = iButtonWriteModeCopy;\n            scene_manager_next_scene(scene_manager, iButtonSceneWrite);\n        }\n    } else if(event.event == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(\n            ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave);\n        // Event is not consumed\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_read_key_menu_on_exit(void* context) {\n    iButton* ibutton = context;\n    submenu_reset(ibutton->submenu);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_read_success.c",
    "content": "#include \"../ibutton_i.h\"\n\n#include <dolphin/dolphin.h>\n\nvoid ibutton_scene_read_success_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n    Widget* widget = ibutton->widget;\n\n    FuriString* tmp = furi_string_alloc();\n\n    const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Retry\", ibutton_widget_callback, context);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"More\", ibutton_widget_callback, context);\n\n    furi_string_printf(\n        tmp,\n        \"%s %s\",\n        ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),\n        ibutton_protocols_get_name(ibutton->protocols, protocol_id));\n\n    widget_add_string_element(\n        widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));\n\n    furi_string_reset(tmp);\n    ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);\n\n    widget_add_string_multiline_element(\n        widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);\n\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n        scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_read_success_on_exit(void* context) {\n    iButton* ibutton = context;\n\n    widget_reset(ibutton->widget);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_retry_confirm_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    iButton* ibutton = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);\n    }\n}\n\nvoid ibutton_scene_retry_confirm_on_enter(void* context) {\n    iButton* ibutton = context;\n    Widget* widget = ibutton->widget;\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Retry\", ibutton_scene_retry_confirm_widget_callback, ibutton);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"Stay\", ibutton_scene_retry_confirm_widget_callback, ibutton);\n    widget_add_string_element(\n        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, \"Retry Reading?\");\n    widget_add_string_element(\n        widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, \"All unsaved data will be lost!\");\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n}\n\nbool ibutton_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true; // Ignore Back button presses\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneRead);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_retry_confirm_on_exit(void* context) {\n    iButton* ibutton = context;\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_rpc.c",
    "content": "#include \"../ibutton_i.h\"\n\nvoid ibutton_scene_rpc_on_enter(void* context) {\n    UNUSED(context);\n}\n\nstatic void ibutton_rpc_start_emulation(iButton* ibutton) {\n    Popup* popup = ibutton->popup;\n\n    popup_set_header(popup, \"iButton\", 82, 28, AlignCenter, AlignBottom);\n    popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);\n    popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);\n\n    ibutton_worker_emulate_start(ibutton->worker, ibutton->key);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);\n    notification_message(ibutton->notifications, &sequence_display_backlight_on);\n}\n\nbool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n\n        if(event.event == iButtonCustomEventRpcLoadFile) {\n            bool result = false;\n\n            if(ibutton_load_key(ibutton, false)) {\n                ibutton_rpc_start_emulation(ibutton);\n                result = true;\n            } else {\n                rpc_system_app_set_error_code(ibutton->rpc, RpcAppSystemErrorCodeParseFile);\n                rpc_system_app_set_error_text(ibutton->rpc, \"Cannot load key file\");\n            }\n            rpc_system_app_confirm(ibutton->rpc, result);\n        } else if(event.event == iButtonCustomEventRpcExit) {\n            rpc_system_app_confirm(ibutton->rpc, true);\n            scene_manager_stop(ibutton->scene_manager);\n            view_dispatcher_stop(ibutton->view_dispatcher);\n\n        } else if(event.event == iButtonCustomEventRpcSessionClose) {\n            scene_manager_stop(ibutton->scene_manager);\n            view_dispatcher_stop(ibutton->view_dispatcher);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_rpc_on_exit(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);\n    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);\n    popup_set_icon(popup, 0, 0, NULL);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_save_name.c",
    "content": "#include \"../ibutton_i.h\"\n\n#include <toolbox/name_generator.h>\n#include <toolbox/path.h>\n\n#include <dolphin/dolphin.h>\n\nstatic void ibutton_scene_save_name_text_input_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventTextEditResult);\n}\n\nvoid ibutton_scene_save_name_on_enter(void* context) {\n    iButton* ibutton = context;\n    TextInput* text_input = ibutton->text_input;\n\n    const bool is_new_file = furi_string_empty(ibutton->file_path);\n\n    if(is_new_file) {\n        name_generator_make_auto(\n            ibutton->key_name, IBUTTON_KEY_NAME_SIZE, IBUTTON_APP_FILENAME_PREFIX);\n    }\n\n    text_input_set_header_text(text_input, \"Name the key\");\n    text_input_set_result_callback(\n        text_input,\n        ibutton_scene_save_name_text_input_callback,\n        ibutton,\n        ibutton->key_name,\n        IBUTTON_KEY_NAME_SIZE,\n        is_new_file);\n\n    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n        IBUTTON_APP_FOLDER, IBUTTON_APP_FILENAME_EXTENSION, ibutton->key_name);\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);\n}\n\nbool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    const bool is_new_file = furi_string_empty(ibutton->file_path);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventTextEditResult) {\n            if(!is_new_file) {\n                Storage* storage = furi_record_open(RECORD_STORAGE);\n                storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path));\n                furi_record_close(RECORD_STORAGE);\n            }\n\n            furi_string_printf(\n                ibutton->file_path,\n                \"%s/%s%s\",\n                IBUTTON_APP_FOLDER,\n                ibutton->key_name,\n                IBUTTON_APP_FILENAME_EXTENSION);\n\n            if(ibutton_save_key(ibutton)) {\n                scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);\n\n                if(scene_manager_has_previous_scene(\n                       ibutton->scene_manager, iButtonSceneSavedKeyMenu)) {\n                    // Nothing, do not count editing as saving\n                } else if(scene_manager_has_previous_scene(\n                              ibutton->scene_manager, iButtonSceneAddType)) {\n                    dolphin_deed(DolphinDeedIbuttonAdd);\n                } else {\n                    dolphin_deed(DolphinDeedIbuttonSave);\n                }\n\n            } else {\n                const uint32_t possible_scenes[] = {\n                    iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};\n                scene_manager_search_and_switch_to_previous_scene_one_of(\n                    ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_save_name_on_exit(void* context) {\n    iButton* ibutton = context;\n    TextInput* text_input = ibutton->text_input;\n\n    void* validator_context = text_input_get_validator_callback_context(text_input);\n    text_input_set_validator(text_input, NULL, NULL);\n    validator_is_file_free((ValidatorIsFile*)validator_context);\n\n    text_input_reset(text_input);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_save_success.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_save_success_popup_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);\n}\n\nvoid ibutton_scene_save_success_on_enter(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n    popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    popup_set_callback(popup, ibutton_scene_save_success_popup_callback);\n    popup_set_context(popup, ibutton);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);\n}\n\nbool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventBack) {\n            scene_manager_search_and_switch_to_another_scene(\n                ibutton->scene_manager, iButtonSceneSelectKey);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_save_success_on_exit(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c",
    "content": "#include \"../ibutton_i.h\"\n#include <dolphin/dolphin.h>\n\nenum SubmenuIndex {\n    SubmenuIndexEmulate,\n    SubmenuIndexWriteId,\n    SubmenuIndexWriteCopy,\n    SubmenuIndexEdit,\n    SubmenuIndexRename,\n    SubmenuIndexDelete,\n    SubmenuIndexInfo,\n};\n\nvoid ibutton_scene_saved_key_menu_on_enter(void* context) {\n    iButton* ibutton = context;\n    Submenu* submenu = ibutton->submenu;\n\n    const uint32_t features = ibutton_protocols_get_features(\n        ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key));\n\n    submenu_add_item(submenu, \"Emulate\", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton);\n\n    if(features & iButtonProtocolFeatureWriteId) {\n        submenu_add_item(\n            submenu, \"Write ID\", SubmenuIndexWriteId, ibutton_submenu_callback, ibutton);\n    }\n\n    if(features & iButtonProtocolFeatureWriteCopy) {\n        submenu_add_item(\n            submenu,\n            \"Full Write on Same Type\",\n            SubmenuIndexWriteCopy,\n            ibutton_submenu_callback,\n            ibutton);\n    }\n\n    submenu_add_item(submenu, \"Edit\", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);\n    submenu_add_item(submenu, \"Rename\", SubmenuIndexRename, ibutton_submenu_callback, ibutton);\n    submenu_add_item(submenu, \"Delete\", SubmenuIndexDelete, ibutton_submenu_callback, ibutton);\n    submenu_add_item(submenu, \"Info\", SubmenuIndexInfo, ibutton_submenu_callback, ibutton);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu));\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);\n}\n\nbool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event);\n        consumed = true;\n        if(event.event == SubmenuIndexEmulate) {\n            scene_manager_next_scene(scene_manager, iButtonSceneEmulate);\n            dolphin_deed(DolphinDeedIbuttonEmulate);\n        } else if(event.event == SubmenuIndexWriteId) {\n            ibutton->write_mode = iButtonWriteModeId;\n            scene_manager_next_scene(scene_manager, iButtonSceneWrite);\n        } else if(event.event == SubmenuIndexWriteCopy) {\n            ibutton->write_mode = iButtonWriteModeCopy;\n            scene_manager_next_scene(scene_manager, iButtonSceneWrite);\n        } else if(event.event == SubmenuIndexEdit) {\n            scene_manager_next_scene(scene_manager, iButtonSceneAddValue);\n        } else if(event.event == SubmenuIndexRename) {\n            scene_manager_next_scene(scene_manager, iButtonSceneSaveName);\n        } else if(event.event == SubmenuIndexDelete) {\n            scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm);\n        } else if(event.event == SubmenuIndexInfo) {\n            scene_manager_next_scene(scene_manager, iButtonSceneInfo);\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(\n            scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate);\n        // Event is not consumed\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_saved_key_menu_on_exit(void* context) {\n    iButton* ibutton = context;\n    submenu_reset(ibutton->submenu);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_select_key.c",
    "content": "#include \"../ibutton_i.h\"\n\nvoid ibutton_scene_select_key_on_enter(void* context) {\n    iButton* ibutton = context;\n\n    if(ibutton_select_and_load_key(ibutton)) {\n        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);\n    } else {\n        scene_manager_search_and_switch_to_previous_scene(\n            ibutton->scene_manager, iButtonSceneStart);\n    }\n}\n\nbool ibutton_scene_select_key_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid ibutton_scene_select_key_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_start.c",
    "content": "#include \"../ibutton_i.h\"\n#include \"ibutton/scenes/ibutton_scene.h\"\n#include <dolphin/dolphin.h>\n\nenum SubmenuIndex {\n    SubmenuIndexRead,\n    SubmenuIndexSaved,\n    SubmenuIndexAdd,\n};\n\nvoid ibutton_scene_start_on_enter(void* context) {\n    iButton* ibutton = context;\n    Submenu* submenu = ibutton->submenu;\n\n    ibutton_reset_key(ibutton);\n\n    submenu_add_item(submenu, \"Read\", SubmenuIndexRead, ibutton_submenu_callback, ibutton);\n    submenu_add_item(submenu, \"Saved\", SubmenuIndexSaved, ibutton_submenu_callback, ibutton);\n    submenu_add_item(submenu, \"Add Manually\", SubmenuIndexAdd, ibutton_submenu_callback, ibutton);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart));\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);\n}\n\nbool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneStart, event.event);\n        consumed = true;\n        if(event.event == SubmenuIndexRead) {\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);\n            dolphin_deed(DolphinDeedIbuttonRead);\n        } else if(event.event == SubmenuIndexSaved) {\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);\n        } else if(event.event == SubmenuIndexAdd) {\n            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_start_on_exit(void* context) {\n    iButton* ibutton = context;\n    submenu_reset(ibutton->submenu);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_view_data.c",
    "content": "#include \"../ibutton_i.h\"\n\nvoid ibutton_scene_view_data_on_enter(void* context) {\n    iButton* ibutton = context;\n    iButtonKey* key = ibutton->key;\n    Widget* widget = ibutton->widget;\n\n    FuriString* tmp = furi_string_alloc();\n    ibutton_protocols_render_data(ibutton->protocols, key, tmp);\n\n    widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp));\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid ibutton_scene_view_data_on_exit(void* context) {\n    iButton* ibutton = context;\n    widget_reset(ibutton->widget);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_write.c",
    "content": "#include \"../ibutton_i.h\"\n\ntypedef enum {\n    iButtonSceneWriteStateDefault,\n    iButtonSceneWriteStateBlinkYellow,\n} iButtonSceneWriteState;\n\nstatic inline iButtonCustomEvent\n    ibutton_scene_write_to_custom_event(iButtonWorkerWriteResult result) {\n    switch(result) {\n    case iButtonWorkerWriteOK:\n        return iButtonCustomEventWorkerWriteOK;\n    case iButtonWorkerWriteSameKey:\n        return iButtonCustomEventWorkerWriteSameKey;\n    case iButtonWorkerWriteNoDetect:\n        return iButtonCustomEventWorkerWriteNoDetect;\n    case iButtonWorkerWriteCannotWrite:\n        return iButtonCustomEventWorkerWriteCannotWrite;\n    default:\n        furi_crash();\n    }\n}\n\nstatic void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(\n        ibutton->view_dispatcher, ibutton_scene_write_to_custom_event(result));\n}\n\nvoid ibutton_scene_write_on_enter(void* context) {\n    iButton* ibutton = context;\n    furi_assert(ibutton->write_mode != iButtonWriteModeInvalid);\n\n    iButtonKey* key = ibutton->key;\n    iButtonWorker* worker = ibutton->worker;\n    const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);\n\n    Widget* widget = ibutton->widget;\n    FuriString* tmp = furi_string_alloc();\n\n    widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);\n\n    if(furi_string_empty(ibutton->file_path)) {\n        furi_string_printf(\n            tmp, \"Unsaved\\n%s\", ibutton_protocols_get_name(ibutton->protocols, protocol_id));\n    } else {\n        furi_string_printf(tmp, \"%s\", ibutton->key_name);\n    }\n\n    widget_add_text_box_element(\n        widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);\n\n    ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);\n\n    if(ibutton->write_mode == iButtonWriteModeId) {\n        furi_string_set(tmp, \"Writing ID\");\n        ibutton_worker_write_id_start(worker, key);\n\n    } else if(ibutton->write_mode == iButtonWriteModeCopy) {\n        furi_string_set(tmp, \"Full Writing\");\n        ibutton_worker_write_copy_start(worker, key);\n    }\n\n    widget_add_string_multiline_element(\n        widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);\n\n    furi_string_free(tmp);\n}\n\nbool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    SceneManager* scene_manager = ibutton->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if((event.event == iButtonCustomEventWorkerWriteOK) ||\n           (event.event == iButtonCustomEventWorkerWriteSameKey)) {\n            scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess);\n        } else if(event.event == iButtonCustomEventWorkerWriteNoDetect) {\n            ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);\n        } else if(event.event == iButtonCustomEventWorkerWriteCannotWrite) {\n            ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_write_on_exit(void* context) {\n    iButton* ibutton = context;\n    ibutton->write_mode = iButtonWriteModeInvalid;\n\n    ibutton_worker_stop(ibutton->worker);\n    widget_reset(ibutton->widget);\n\n    ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);\n}\n"
  },
  {
    "path": "applications/main/ibutton/scenes/ibutton_scene_write_success.c",
    "content": "#include \"../ibutton_i.h\"\n\nstatic void ibutton_scene_write_success_popup_callback(void* context) {\n    iButton* ibutton = context;\n    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);\n}\n\nvoid ibutton_scene_write_success_on_enter(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_set_icon(popup, 0, 9, &I_iButtonDolphinVerySuccess_92x55);\n    popup_set_text(popup, \"Successfully written!\", 40, 12, AlignLeft, AlignBottom);\n\n    popup_set_callback(popup, ibutton_scene_write_success_popup_callback);\n    popup_set_context(popup, ibutton);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);\n    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);\n}\n\nbool ibutton_scene_write_success_on_event(void* context, SceneManagerEvent event) {\n    iButton* ibutton = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == iButtonCustomEventBack) {\n            const uint32_t possible_scenes[] = {iButtonSceneReadKeyMenu, iButtonSceneStart};\n            scene_manager_search_and_switch_to_previous_scene_one_of(\n                ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n        }\n    }\n\n    return consumed;\n}\n\nvoid ibutton_scene_write_success_on_exit(void* context) {\n    iButton* ibutton = context;\n    Popup* popup = ibutton->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/infrared/application.fam",
    "content": "App(\n    appid=\"infrared\",\n    name=\"Infrared\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    entry_point=\"infrared_app\",\n    targets=[\"f7\"],\n    icon=\"A_Infrared_14\",\n    stack_size=3 * 1024,\n    order=40,\n    sources=[\"*.c\", \"!infrared_cli.c\"],\n    resources=\"resources\",\n    fap_libs=[\"assets\", \"infrared\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"Infrared\",\n)\n\nApp(\n    appid=\"cli_ir\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_ir_ep\",\n    requires=[\"cli\"],\n    sources=[\"infrared_cli.c\"],\n    fap_libs=[\"infrared\"],\n)\n"
  },
  {
    "path": "applications/main/infrared/infrared_app.c",
    "content": "#include \"infrared_app_i.h\"\n\n#include <power/power_service/power.h>\n\n#include <string.h>\n#include <toolbox/path.h>\n#include <toolbox/saved_struct.h>\n#include <dolphin/dolphin.h>\n\n#define TAG \"InfraredApp\"\n\n#define INFRARED_TX_MIN_INTERVAL_MS (50U)\n#define INFRARED_TASK_STACK_SIZE    (2048UL)\n\n#define INFRARED_SETTINGS_PATH    INT_PATH(\".infrared.settings\")\n#define INFRARED_SETTINGS_VERSION (1)\n#define INFRARED_SETTINGS_MAGIC   (0x1F)\n\ntypedef struct {\n    FuriHalInfraredTxPin tx_pin;\n    bool otg_enabled;\n} InfraredSettings;\n\nstatic const NotificationSequence*\n    infrared_notification_sequences[InfraredNotificationMessageCount] = {\n        &sequence_success,\n        &sequence_set_only_green_255,\n        &sequence_reset_green,\n        &sequence_solid_yellow,\n        &sequence_reset_rgb,\n        &sequence_blink_start_cyan,\n        &sequence_blink_start_magenta,\n        &sequence_blink_stop,\n};\n\nstatic void infrared_make_app_folder(InfraredApp* infrared) {\n    if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) {\n        infrared_show_error_message(infrared, \"Cannot create\\napp folder\");\n    }\n}\n\nstatic bool infrared_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    return scene_manager_handle_custom_event(infrared->scene_manager, event);\n}\n\nstatic bool infrared_back_event_callback(void* context) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    return scene_manager_handle_back_event(infrared->scene_manager);\n}\n\nstatic void infrared_tick_event_callback(void* context) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    scene_manager_handle_tick_event(infrared->scene_manager);\n}\n\nstatic void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    furi_assert(infrared->rpc_ctx);\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        view_dispatcher_send_custom_event(\n            infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);\n        rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);\n        infrared->rpc_ctx = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        view_dispatcher_send_custom_event(\n            infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);\n    } else if(event->type == RpcAppEventTypeLoadFile) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeString);\n        furi_string_set(infrared->file_path, event->data.string);\n        view_dispatcher_send_custom_event(\n            infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile);\n    } else if(event->type == RpcAppEventTypeButtonPress) {\n        furi_assert(\n            event->data.type == RpcAppSystemEventDataTypeString ||\n            event->data.type == RpcAppSystemEventDataTypeInt32);\n        if(event->data.type == RpcAppSystemEventDataTypeString) {\n            furi_string_set(infrared->button_name, event->data.string);\n            view_dispatcher_send_custom_event(\n                infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressName);\n        } else {\n            infrared->app_state.current_button_index = event->data.i32;\n            view_dispatcher_send_custom_event(\n                infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex);\n        }\n    } else if(event->type == RpcAppEventTypeButtonPressRelease) {\n        furi_assert(\n            event->data.type == RpcAppSystemEventDataTypeString ||\n            event->data.type == RpcAppSystemEventDataTypeInt32);\n        if(event->data.type == RpcAppSystemEventDataTypeString) {\n            furi_string_set(infrared->button_name, event->data.string);\n            view_dispatcher_send_custom_event(\n                infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseName);\n        } else {\n            infrared->app_state.current_button_index = event->data.i32;\n            view_dispatcher_send_custom_event(\n                infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseIndex);\n        }\n    } else if(event->type == RpcAppEventTypeButtonRelease) {\n        view_dispatcher_send_custom_event(\n            infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);\n    } else {\n        rpc_system_app_confirm(infrared->rpc_ctx, false);\n    }\n}\n\nstatic void infrared_find_vacant_remote_name(FuriString* name, const char* path) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FuriString* base_path;\n    base_path = furi_string_alloc_set(path);\n\n    if(furi_string_end_with(base_path, INFRARED_APP_EXTENSION)) {\n        size_t filename_start = furi_string_search_rchar(base_path, '/');\n        furi_string_left(base_path, filename_start);\n    }\n\n    furi_string_printf(\n        base_path, \"%s/%s%s\", path, furi_string_get_cstr(name), INFRARED_APP_EXTENSION);\n\n    FS_Error status = storage_common_stat(storage, furi_string_get_cstr(base_path), NULL);\n\n    if(status == FSE_OK) {\n        /* If the suggested name is occupied, try another one (name2, name3, etc) */\n        size_t dot = furi_string_search_rchar(base_path, '.');\n        furi_string_left(base_path, dot);\n\n        FuriString* path_temp;\n        path_temp = furi_string_alloc();\n\n        uint32_t i = 1;\n        do {\n            furi_string_printf(\n                path_temp, \"%s%lu%s\", furi_string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION);\n            status = storage_common_stat(storage, furi_string_get_cstr(path_temp), NULL);\n        } while(status == FSE_OK);\n\n        furi_string_free(path_temp);\n\n        if(status == FSE_NOT_EXIST) {\n            furi_string_cat_printf(name, \"%lu\", i);\n        }\n    }\n\n    furi_string_free(base_path);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic InfraredApp* infrared_alloc(void) {\n    InfraredApp* infrared = malloc(sizeof(InfraredApp));\n\n    infrared->task_thread =\n        furi_thread_alloc_ex(\"InfraredTask\", INFRARED_TASK_STACK_SIZE, NULL, infrared);\n    infrared->file_path = furi_string_alloc();\n    infrared->button_name = furi_string_alloc();\n\n    InfraredAppState* app_state = &infrared->app_state;\n    app_state->is_learning_new_remote = false;\n    app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);\n    app_state->edit_target = InfraredEditTargetNone;\n    app_state->edit_mode = InfraredEditModeNone;\n    app_state->current_button_index = InfraredButtonIndexNone;\n\n    infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared);\n    infrared->view_dispatcher = view_dispatcher_alloc();\n\n    infrared->gui = furi_record_open(RECORD_GUI);\n\n    ViewDispatcher* view_dispatcher = infrared->view_dispatcher;\n    view_dispatcher_set_event_callback_context(view_dispatcher, infrared);\n    view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback);\n    view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100);\n\n    infrared->storage = furi_record_open(RECORD_STORAGE);\n    infrared->dialogs = furi_record_open(RECORD_DIALOGS);\n    infrared->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    infrared->worker = infrared_worker_alloc();\n    infrared->remote = infrared_remote_alloc();\n    infrared->current_signal = infrared_signal_alloc();\n    infrared->brute_force = infrared_brute_force_alloc();\n\n    infrared->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu));\n\n    infrared->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));\n\n    infrared->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));\n\n    infrared->button_menu = button_menu_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu));\n\n    infrared->popup = popup_alloc();\n    view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup));\n\n    infrared->var_item_list = variable_item_list_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher,\n        InfraredViewVariableList,\n        variable_item_list_get_view(infrared->var_item_list));\n\n    infrared->view_stack = view_stack_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack));\n\n    infrared->move_view = infrared_move_view_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewMove, infrared_move_view_get_view(infrared->move_view));\n\n    infrared->loading = loading_alloc();\n    view_dispatcher_add_view(\n        view_dispatcher, InfraredViewLoading, loading_get_view(infrared->loading));\n\n    if(app_state->is_debug_enabled) {\n        infrared->debug_view = infrared_debug_view_alloc();\n        view_dispatcher_add_view(\n            view_dispatcher,\n            InfraredViewDebugView,\n            infrared_debug_view_get_view(infrared->debug_view));\n    }\n\n    infrared->button_panel = button_panel_alloc();\n    infrared->progress = infrared_progress_view_alloc();\n\n    return infrared;\n}\n\nstatic void infrared_free(InfraredApp* infrared) {\n    furi_assert(infrared);\n\n    furi_thread_join(infrared->task_thread);\n    furi_thread_free(infrared->task_thread);\n\n    ViewDispatcher* view_dispatcher = infrared->view_dispatcher;\n    InfraredAppState* app_state = &infrared->app_state;\n\n    if(infrared->rpc_ctx) {\n        rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);\n        rpc_system_app_send_exited(infrared->rpc_ctx);\n        infrared->rpc_ctx = NULL;\n    }\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu);\n    submenu_free(infrared->submenu);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);\n    text_input_free(infrared->text_input);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);\n    dialog_ex_free(infrared->dialog_ex);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu);\n    button_menu_free(infrared->button_menu);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup);\n    popup_free(infrared->popup);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewVariableList);\n    variable_item_list_free(infrared->var_item_list);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewStack);\n    view_stack_free(infrared->view_stack);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewMove);\n    infrared_move_view_free(infrared->move_view);\n\n    view_dispatcher_remove_view(view_dispatcher, InfraredViewLoading);\n    loading_free(infrared->loading);\n\n    if(app_state->is_debug_enabled) {\n        view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView);\n        infrared_debug_view_free(infrared->debug_view);\n    }\n\n    button_panel_free(infrared->button_panel);\n    infrared_progress_view_free(infrared->progress);\n\n    view_dispatcher_free(view_dispatcher);\n    scene_manager_free(infrared->scene_manager);\n\n    infrared_brute_force_free(infrared->brute_force);\n    infrared_signal_free(infrared->current_signal);\n    infrared_remote_free(infrared->remote);\n    infrared_worker_free(infrared->worker);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    infrared->notifications = NULL;\n\n    furi_record_close(RECORD_DIALOGS);\n    infrared->dialogs = NULL;\n\n    furi_record_close(RECORD_GUI);\n    infrared->gui = NULL;\n\n    furi_string_free(infrared->file_path);\n    furi_string_free(infrared->button_name);\n\n    free(infrared);\n}\n\nInfraredErrorCode infrared_add_remote_with_button(\n    const InfraredApp* infrared,\n    const char* button_name,\n    const InfraredSignal* signal) {\n    InfraredRemote* remote = infrared->remote;\n\n    FuriString* new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME);\n    FuriString* new_path = furi_string_alloc_set(INFRARED_APP_FOLDER);\n\n    infrared_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path));\n    furi_string_cat_printf(\n        new_path, \"/%s%s\", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        error = infrared_remote_create(remote, furi_string_get_cstr(new_path));\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        error = infrared_remote_append_signal(remote, signal, button_name);\n    } while(false);\n\n    furi_string_free(new_name);\n    furi_string_free(new_path);\n\n    return error;\n}\n\nInfraredErrorCode\n    infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) {\n    InfraredRemote* remote = infrared->remote;\n    const char* old_path = infrared_remote_get_path(remote);\n\n    if(!strcmp(infrared_remote_get_name(remote), new_name)) {\n        return true;\n    }\n\n    FuriString* new_name_fstr = furi_string_alloc_set(new_name);\n    FuriString* new_path_fstr = furi_string_alloc_set(old_path);\n\n    infrared_find_vacant_remote_name(new_name_fstr, old_path);\n\n    if(furi_string_end_with(new_path_fstr, INFRARED_APP_EXTENSION)) {\n        path_extract_dirname(old_path, new_path_fstr);\n    }\n\n    path_append(new_path_fstr, furi_string_get_cstr(new_name_fstr));\n    furi_string_cat(new_path_fstr, INFRARED_APP_EXTENSION);\n\n    const InfraredErrorCode error =\n        infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr));\n\n    furi_string_free(new_name_fstr);\n    furi_string_free(new_path_fstr);\n\n    return error;\n}\n\nvoid infrared_tx_start(InfraredApp* infrared) {\n    if(infrared->app_state.is_transmitting) {\n        return;\n    }\n\n    const uint32_t time_elapsed = furi_get_tick() - infrared->app_state.last_transmit_time;\n\n    if(time_elapsed < INFRARED_TX_MIN_INTERVAL_MS) {\n        return;\n    }\n\n    if(infrared_signal_is_raw(infrared->current_signal)) {\n        const InfraredRawSignal* raw = infrared_signal_get_raw_signal(infrared->current_signal);\n        infrared_worker_set_raw_signal(\n            infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);\n    } else {\n        const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal);\n        infrared_worker_set_decoded_signal(infrared->worker, message);\n    }\n\n    dolphin_deed(DolphinDeedIrSend);\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);\n\n    infrared_worker_tx_set_get_signal_callback(\n        infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);\n    infrared_worker_tx_start(infrared->worker);\n\n    infrared->app_state.is_transmitting = true;\n}\n\nInfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) {\n    furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));\n\n    InfraredErrorCode error =\n        infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index);\n\n    if(!INFRARED_ERROR_PRESENT(error)) {\n        infrared_tx_start(infrared);\n    }\n    return error;\n}\n\nvoid infrared_tx_stop(InfraredApp* infrared) {\n    if(!infrared->app_state.is_transmitting) {\n        return;\n    }\n\n    infrared_worker_tx_stop(infrared->worker);\n    infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL);\n\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);\n\n    infrared->app_state.is_transmitting = false;\n    infrared->app_state.last_transmit_time = furi_get_tick();\n}\n\nvoid infrared_tx_send_once(InfraredApp* infrared) {\n    if(infrared->app_state.is_transmitting) {\n        return;\n    }\n\n    dolphin_deed(DolphinDeedIrSend);\n    infrared_signal_transmit(infrared->current_signal);\n}\n\nInfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index) {\n    furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));\n\n    InfraredErrorCode error = infrared_remote_load_signal(\n        infrared->remote, infrared->current_signal, infrared->app_state.current_button_index);\n    if(!INFRARED_ERROR_PRESENT(error)) {\n        infrared_tx_send_once(infrared);\n    }\n\n    return error;\n}\nvoid infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) {\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewLoading);\n    furi_thread_set_callback(infrared->task_thread, callback);\n    furi_thread_start(infrared->task_thread);\n}\n\nInfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared) {\n    furi_thread_join(infrared->task_thread);\n    return furi_thread_get_return_code(infrared->task_thread);\n}\n\nvoid infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n\n    vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, fmt, args);\n\n    va_end(args);\n}\n\nvoid infrared_text_store_clear(InfraredApp* infrared, uint32_t bank) {\n    memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1);\n}\n\nvoid infrared_play_notification_message(\n    const InfraredApp* infrared,\n    InfraredNotificationMessage message) {\n    furi_assert(message < InfraredNotificationMessageCount);\n    notification_message(infrared->notifications, infrared_notification_sequences[message]);\n}\n\nvoid infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n\n    FuriString* message = furi_string_alloc_vprintf(fmt, args);\n    dialog_message_show_storage_error(infrared->dialogs, furi_string_get_cstr(message));\n\n    furi_string_free(message);\n    va_end(args);\n}\n\nvoid infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) {\n    if(tx_pin < FuriHalInfraredTxPinMax) {\n        furi_hal_infrared_set_tx_output(tx_pin);\n    } else {\n        FuriHalInfraredTxPin tx_pin_detected = furi_hal_infrared_detect_tx_output();\n        furi_hal_infrared_set_tx_output(tx_pin_detected);\n        if(tx_pin_detected != FuriHalInfraredTxPinInternal) {\n            infrared_enable_otg(infrared, true);\n        }\n    }\n\n    infrared->app_state.tx_pin = tx_pin;\n}\n\nvoid infrared_enable_otg(InfraredApp* infrared, bool enable) {\n    Power* power = furi_record_open(RECORD_POWER);\n\n    power_enable_otg(power, enable);\n    infrared->app_state.is_otg_enabled = enable;\n\n    furi_record_close(RECORD_POWER);\n}\n\nstatic void infrared_load_settings(InfraredApp* infrared) {\n    InfraredSettings settings = {0};\n\n    if(!saved_struct_load(\n           INFRARED_SETTINGS_PATH,\n           &settings,\n           sizeof(InfraredSettings),\n           INFRARED_SETTINGS_MAGIC,\n           INFRARED_SETTINGS_VERSION)) {\n        FURI_LOG_D(TAG, \"Failed to load settings, using defaults\");\n        infrared_save_settings(infrared);\n    }\n\n    infrared_set_tx_pin(infrared, settings.tx_pin);\n    if(settings.tx_pin < FuriHalInfraredTxPinMax) {\n        infrared_enable_otg(infrared, settings.otg_enabled);\n    }\n}\n\nvoid infrared_save_settings(InfraredApp* infrared) {\n    InfraredSettings settings = {\n        .tx_pin = infrared->app_state.tx_pin,\n        .otg_enabled = infrared->app_state.is_otg_enabled,\n    };\n\n    if(!saved_struct_save(\n           INFRARED_SETTINGS_PATH,\n           &settings,\n           sizeof(InfraredSettings),\n           INFRARED_SETTINGS_MAGIC,\n           INFRARED_SETTINGS_VERSION)) {\n        FURI_LOG_E(TAG, \"Failed to save settings\");\n    }\n}\n\nvoid infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n\n    if(infrared_worker_signal_is_decoded(received_signal)) {\n        infrared_signal_set_message(\n            infrared->current_signal, infrared_worker_get_decoded_signal(received_signal));\n    } else {\n        const uint32_t* timings;\n        size_t timings_size;\n        infrared_worker_get_raw_signal(received_signal, &timings, &timings_size);\n        infrared_signal_set_raw_signal(\n            infrared->current_signal,\n            timings,\n            timings_size,\n            INFRARED_COMMON_CARRIER_FREQUENCY,\n            INFRARED_COMMON_DUTY_CYCLE);\n    }\n\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived);\n}\n\nvoid infrared_text_input_callback(void* context) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone);\n}\n\nvoid infrared_popup_closed_callback(void* context) {\n    furi_assert(context);\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);\n}\n\nint32_t infrared_app(void* p) {\n    InfraredApp* infrared = infrared_alloc();\n\n    infrared_load_settings(infrared);\n    infrared_make_app_folder(infrared);\n\n    bool is_remote_loaded = false;\n    bool is_rpc_mode = false;\n\n    if(p && strlen(p)) {\n        uint32_t rpc_ctx = 0;\n        if(sscanf(p, \"RPC %lX\", &rpc_ctx) == 1) {\n            infrared->rpc_ctx = (void*)rpc_ctx;\n            rpc_system_app_set_callback(\n                infrared->rpc_ctx, infrared_rpc_command_callback, infrared);\n            rpc_system_app_send_started(infrared->rpc_ctx);\n            is_rpc_mode = true;\n        } else {\n            const char* file_path = (const char*)p;\n            InfraredErrorCode error = infrared_remote_load(infrared->remote, file_path);\n\n            if(!INFRARED_ERROR_PRESENT(error)) {\n                is_remote_loaded = true;\n            } else {\n                is_remote_loaded = false;\n                bool wrong_file_type = INFRARED_ERROR_CHECK(error, InfraredErrorCodeWrongFileType);\n                const char* format = wrong_file_type ?\n                                         \"Library file\\n\\\"%s\\\" can't be openned as a remote\" :\n                                         \"Failed to load\\n\\\"%s\\\"\";\n\n                infrared_show_error_message(infrared, format, file_path);\n                return -1;\n            }\n\n            furi_string_set(infrared->file_path, file_path);\n        }\n    }\n\n    if(is_rpc_mode) {\n        view_dispatcher_attach_to_gui(\n            infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop);\n        scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc);\n    } else {\n        view_dispatcher_attach_to_gui(\n            infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen);\n        if(is_remote_loaded) { //-V547\n            scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);\n        } else {\n            scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart);\n        }\n    }\n\n    view_dispatcher_run(infrared->view_dispatcher);\n\n    infrared_set_tx_pin(infrared, FuriHalInfraredTxPinInternal);\n    infrared_enable_otg(infrared, false);\n    infrared_free(infrared);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/infrared/infrared_app.h",
    "content": "/**\n * @file infrared_app.h\n * @brief Infrared application - start here.\n *\n * @see infrared_app_i.h for the main application data structure and functions.\n * @see infrared_remote.h for the infrared remote library - loading, storing and manipulating remotes\n */\n#pragma once\n\n/**\n * @brief InfraredApp opaque type declaration.\n */\ntypedef struct InfraredApp InfraredApp;\n"
  },
  {
    "path": "applications/main/infrared/infrared_app_i.h",
    "content": "/**\n * @file infrared_app_i.h\n * @brief Main Infrared application types and functions.\n */\n#pragma once\n\n#include <furi_hal_infrared.h>\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <assets_icons.h>\n#include <gui/view_stack.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n\n#include <gui/modules/popup.h>\n#include <gui/modules/loading.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/button_menu.h>\n#include <gui/modules/button_panel.h>\n#include <gui/modules/variable_item_list.h>\n\n#include <rpc/rpc_app.h>\n#include <storage/storage.h>\n#include <dialogs/dialogs.h>\n\n#include <notification/notification_messages.h>\n#include <infrared/worker/infrared_worker.h>\n\n#include \"infrared_app.h\"\n#include \"infrared_remote.h\"\n#include <lib/infrared/signal/infrared_brute_force.h>\n#include \"infrared_custom_event.h\"\n\n#include \"scenes/infrared_scene.h\"\n#include \"views/infrared_progress_view.h\"\n#include \"views/infrared_debug_view.h\"\n#include \"views/infrared_move_view.h\"\n\n#define INFRARED_FILE_NAME_SIZE  100\n#define INFRARED_TEXT_STORE_NUM  2\n#define INFRARED_TEXT_STORE_SIZE 128\n\n#define INFRARED_MAX_BUTTON_NAME_LENGTH 23\n#define INFRARED_MAX_REMOTE_NAME_LENGTH 23\n\n#define INFRARED_APP_FOLDER    EXT_PATH(\"infrared\")\n#define INFRARED_APP_EXTENSION \".ir\"\n\n#define INFRARED_DEFAULT_REMOTE_NAME \"Remote\"\n#define INFRARED_LOG_TAG             \"InfraredApp\"\n\n/**\n * @brief Enumeration of invalid remote button indices.\n */\ntypedef enum {\n    InfraredButtonIndexNone = -1, /**< No button is currently selected. */\n} InfraredButtonIndex;\n\n/**\n * @brief Enumeration of editing targets.\n */\ntypedef enum {\n    InfraredEditTargetNone, /**< No editing target is selected. */\n    InfraredEditTargetRemote, /**< Whole remote is selected as editing target. */\n    InfraredEditTargetButton, /**< Single button is selected as editing target. */\n} InfraredEditTarget;\n\n/**\n * @brief Enumeration of editing modes.\n */\ntypedef enum {\n    InfraredEditModeNone, /**< No editing mode is selected. */\n    InfraredEditModeRename, /**< Rename mode is selected. */\n    InfraredEditModeDelete, /**< Delete mode is selected. */\n} InfraredEditMode;\n\n/**\n * @brief Infrared application state type.\n */\ntypedef struct {\n    bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */\n    bool is_debug_enabled; /**< Whether to enable or disable debugging features. */\n    bool is_transmitting; /**< Whether a signal is currently being transmitted. */\n    bool is_otg_enabled; /**< Whether OTG power (external 5V) is enabled. */\n    InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */\n    InfraredEditMode edit_mode     : 8; /**< Selected editing operation (rename or delete). */\n    int32_t current_button_index; /**< Selected button index (move destination). */\n    int32_t prev_button_index; /**< Previous button index (move source). */\n    uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */\n    FuriHalInfraredTxPin tx_pin;\n} InfraredAppState;\n\n/**\n * @brief Infrared application type.\n */\nstruct InfraredApp {\n    SceneManager* scene_manager; /**< Pointer to a SceneManager instance. */\n    ViewDispatcher* view_dispatcher; /**< Pointer to a ViewDispatcher instance. */\n\n    Gui* gui; /**< Pointer to a Gui instance. */\n    Storage* storage; /**< Pointer to a Storage instance. */\n    DialogsApp* dialogs; /**< Pointer to a DialogsApp instance. */\n    NotificationApp* notifications; /**< Pointer to a NotificationApp instance. */\n    InfraredWorker* worker; /**< Used to send or receive signals. */\n    InfraredRemote* remote; /**< Holds the currently loaded remote. */\n    InfraredSignal* current_signal; /**< Holds the currently loaded signal. */\n    InfraredBruteForce* brute_force; /**< Used for the Universal Remote feature. */\n\n    Submenu* submenu; /**< Standard view for displaying application menus. */\n    TextInput* text_input; /**< Standard view for receiving user text input. */\n    DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */\n    ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */\n    Popup* popup; /**< Standard view for displaying messages. */\n    VariableItemList* var_item_list; /**< Standard view for displaying menus of choice items. */\n\n    ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */\n    InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */\n    InfraredMoveView* move_view; /**< Custom view for rearranging buttons in a remote. */\n\n    ButtonPanel* button_panel; /**< Standard view for displaying control panels. */\n    Loading* loading; /**< Standard view for informing about long operations. */\n    InfraredProgressView* progress; /**< Custom view for showing brute force progress. */\n\n    FuriThread* task_thread; /**< Pointer to a FuriThread instance for concurrent tasks. */\n    FuriString* file_path; /**< Full path to the currently loaded file. */\n    FuriString* button_name; /**< Name of the button requested in RPC mode. */\n    /** Arbitrary text storage for various inputs. */\n    char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];\n    InfraredAppState app_state; /**< Application state. */\n\n    void* rpc_ctx; /**< Pointer to the RPC context object. */\n};\n\n/**\n * @brief Enumeration of all used view types.\n */\ntypedef enum {\n    InfraredViewSubmenu,\n    InfraredViewTextInput,\n    InfraredViewDialogEx,\n    InfraredViewButtonMenu,\n    InfraredViewPopup,\n    InfraredViewVariableList,\n    InfraredViewStack,\n    InfraredViewDebugView,\n    InfraredViewMove,\n    InfraredViewLoading,\n} InfraredView;\n\n/**\n * @brief Enumeration of all notification message types.\n */\ntypedef enum {\n    InfraredNotificationMessageSuccess, /**< Play a short happy tune. */\n    InfraredNotificationMessageGreenOn, /**< Turn green LED on. */\n    InfraredNotificationMessageGreenOff, /**< Turn green LED off. */\n    InfraredNotificationMessageYellowOn, /**< Turn yellow LED on. */\n    InfraredNotificationMessageYellowOff, /**< Turn yellow LED off. */\n    InfraredNotificationMessageBlinkStartRead, /**< Blink the LED to indicate receiver mode. */\n    InfraredNotificationMessageBlinkStartSend, /**< Blink the LED to indicate transmitter mode. */\n    InfraredNotificationMessageBlinkStop, /**< Stop blinking the LED. */\n    InfraredNotificationMessageCount, /**< Special value equal to the message type count. */\n} InfraredNotificationMessage;\n\n/**\n * @brief Add a new remote with a single signal.\n *\n * The filename will be automatically generated depending on\n * the names and number of other files in the infrared data directory.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] name pointer to a zero-terminated string containing the signal name.\n * @param[in] signal pointer to the signal to be added.\n * @return InfraredErrorCodeNone if the remote was successfully created, otherwise error code.\n */\nInfraredErrorCode infrared_add_remote_with_button(\n    const InfraredApp* infrared,\n    const char* name,\n    const InfraredSignal* signal);\n\n/**\n * @brief Rename the currently loaded remote.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] new_name pointer to a zero-terminated string containing the new remote name.\n * @return InfraredErrorCodeNone if the remote was successfully renamed, otherwise error code.\n */\nInfraredErrorCode\n    infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name);\n\n/**\n * @brief Begin transmission of the currently loaded signal.\n *\n * The signal will be repeated indefinitely until stopped.\n *\n * @param[in,out] infrared pointer to the application instance.\n */\nvoid infrared_tx_start(InfraredApp* infrared);\n\n/**\n * @brief Load a signal under the given index and begin transmission.\n *\n * The signal will be repeated indefinitely until stopped.\n *\n * @param[in,out] infrared pointer to the application instance.\n * @param[in] button_index index of the signal to be loaded.\n * @returns InfraredErrorCodeNone if the signal could be loaded, otherwise error code.\n */\nInfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index);\n\n/**\n * @brief Stop transmission of the currently loaded signal.\n *\n * @param[in,out] infrared pointer to the application instance.\n */\nvoid infrared_tx_stop(InfraredApp* infrared);\n\n/**\n * @brief Transmit the currently loaded signal once.\n * \n * @param[in,out] infrared pointer to the application instance.\n */\nvoid infrared_tx_send_once(InfraredApp* infrared);\n\n/**\n * @brief Load the signal under the given index and transmit it once.\n *\n * @param[in,out] infrared pointer to the application instance.\n */\nInfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index);\n\n/**\n * @brief Start a blocking task in a separate thread.\n *\n * Before starting a blocking task, the current view will be replaced\n * with a busy animation. All subsequent user input will be ignored.\n *\n * @param[in,out] infrared pointer to the application instance.\n * @param[in] callback pointer to the function to be run in the thread.\n */\nvoid infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback);\n\n/**\n * @brief Wait for a blocking task to finish and get the result.\n *\n * The busy animation shown during the infrared_blocking_task_start() call\n * will NOT be hidden and WILL remain on screen. If another view is needed\n * (e.g. to display the results), the caller code MUST set it explicitly.\n *\n * @param[in,out] infrared pointer to the application instance.\n * @return InfraredErrorCodeNone if the blocking task finished successfully, otherwise error code.\n */\nInfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared);\n\n/**\n * @brief Set the internal text store with formatted text.\n *\n * @param[in,out] infrared pointer to the application instance.\n * @param[in] bank index of text store bank (0 or 1).\n * @param[in] fmt pointer to a zero-terminated string containing the format text.\n * @param[in] ... additional arguments.\n */\nvoid infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...)\n    _ATTRIBUTE((__format__(__printf__, 3, 4)));\n\n/**\n * @brief Clear the internal text store.\n *\n * @param[in,out] infrared pointer to the application instance.\n * @param[in] bank index of text store bank (0 or 1).\n */\nvoid infrared_text_store_clear(InfraredApp* infrared, uint32_t bank);\n\n/**\n * @brief Play a sound and/or blink the LED.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] message type of the message to play.\n */\nvoid infrared_play_notification_message(\n    const InfraredApp* infrared,\n    InfraredNotificationMessage message);\n\n/**\n * @brief Show a formatted error messsage.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] fmt pointer to a zero-terminated string containing the format text.\n * @param[in] ... additional arguments.\n */\nvoid infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/**\n * @brief Set which pin will be used to transmit infrared signals.\n *\n * Setting tx_pin to InfraredTxPinInternal will enable transmission via\n * the built-in infrared LEDs.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] tx_pin pin to be used for signal transmission.\n */\nvoid infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin);\n\n/**\n * @brief Enable or disable 5V at the GPIO pin 1.\n *\n * @param[in] infrared pointer to the application instance.\n * @param[in] enable boolean value corresponding to OTG state (true = enable, false = disable)\n */\nvoid infrared_enable_otg(InfraredApp* infrared, bool enable);\n\n/**\n * @brief Save current settings to a file.\n *\n * @param[in] infrared pointer to the application instance.\n */\nvoid infrared_save_settings(InfraredApp* infrared);\n\n/**\n * @brief Common received signal callback.\n *\n * Called when the worker has received a complete infrared signal.\n *\n * @param[in,out] context pointer to the user-specified context object.\n * @param[in] received_signal pointer to the received signal.\n */\nvoid infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);\n\n/**\n * @brief Common text input callback.\n *\n * Called when the input has been accepted by the user.\n *\n * @param[in,out] context pointer to the user-specified context object.\n */\nvoid infrared_text_input_callback(void* context);\n\n/**\n * @brief Common popup close callback.\n *\n * Called when the popup has been closed either by the user or after a timeout.\n *\n * @param[in,out] context pointer to the user-specified context object.\n */\nvoid infrared_popup_closed_callback(void* context);\n"
  },
  {
    "path": "applications/main/infrared/infrared_cli.c",
    "content": "#include <cli/cli_main_commands.h>\n#include <infrared.h>\n#include <infrared_worker.h>\n#include <furi_hal_infrared.h>\n#include <flipper_format.h>\n#include <toolbox/args.h>\n#include <toolbox/strint.h>\n#include <toolbox/pipe.h>\n#include <m-dict.h>\n\n#include <lib/infrared/signal/infrared_signal.h>\n#include <lib/infrared/signal/infrared_brute_force.h>\n\n#define INFRARED_CLI_BUF_SIZE            (10U)\n#define INFRARED_CLI_FILE_NAME_SIZE      (256U)\n#define INFRARED_FILE_EXTENSION          \".ir\"\n#define INFRARED_ASSETS_FOLDER           EXT_PATH(\"infrared/assets\")\n#define INFRARED_BRUTE_FORCE_DUMMY_INDEX 0\n\nDICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST)\n\nstatic void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args);\nstatic void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args);\nstatic void infrared_cli_process_decode(PipeSide* pipe, FuriString* args);\nstatic void infrared_cli_process_universal(PipeSide* pipe, FuriString* args);\n\nstatic const struct {\n    const char* cmd;\n    void (*process_function)(PipeSide* pipe, FuriString* args);\n} infrared_cli_commands[] = {\n    {.cmd = \"rx\", .process_function = infrared_cli_start_ir_rx},\n    {.cmd = \"tx\", .process_function = infrared_cli_start_ir_tx},\n    {.cmd = \"decode\", .process_function = infrared_cli_process_decode},\n    {.cmd = \"universal\", .process_function = infrared_cli_process_universal},\n};\n\nstatic void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {\n    furi_assert(received_signal);\n    char buf[100];\n    size_t buf_cnt;\n    PipeSide* pipe = (PipeSide*)context;\n\n    if(infrared_worker_signal_is_decoded(received_signal)) {\n        const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);\n        buf_cnt = snprintf(\n            buf,\n            sizeof(buf),\n            \"%s, A:0x%0*lX, C:0x%0*lX%s\\r\\n\",\n            infrared_get_protocol_name(message->protocol),\n            ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),\n            message->address,\n            ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),\n            message->command,\n            message->repeat ? \" R\" : \"\");\n        pipe_send(pipe, buf, buf_cnt);\n    } else {\n        const uint32_t* timings;\n        size_t timings_cnt;\n        infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);\n\n        buf_cnt = snprintf(buf, sizeof(buf), \"RAW, %zu samples:\\r\\n\", timings_cnt);\n        pipe_send(pipe, buf, buf_cnt);\n        for(size_t i = 0; i < timings_cnt; ++i) {\n            buf_cnt = snprintf(buf, sizeof(buf), \"%lu \", timings[i]);\n            pipe_send(pipe, buf, buf_cnt);\n        }\n        buf_cnt = snprintf(buf, sizeof(buf), \"\\r\\n\");\n        pipe_send(pipe, buf, buf_cnt);\n    }\n}\n\nstatic void infrared_cli_print_universal_remotes(void) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* dir = storage_file_alloc(storage);\n\n    do {\n        if(!storage_dir_open(dir, INFRARED_ASSETS_FOLDER)) break;\n\n        FileInfo file_info;\n        char file_name[INFRARED_CLI_FILE_NAME_SIZE];\n\n        while(storage_dir_read(dir, &file_info, file_name, sizeof(file_name))) {\n            if(file_info.flags & FSF_DIRECTORY) {\n                continue;\n            }\n\n            char* file_ext = strstr(file_name, INFRARED_FILE_EXTENSION);\n            if((file_ext == NULL) || (strcmp(file_ext, INFRARED_FILE_EXTENSION) != 0)) {\n                continue;\n            }\n\n            *file_ext = '\\0';\n            printf(\"%s \", file_name);\n        }\n\n        printf(\"\\r\\n\");\n    } while(false);\n\n    storage_file_free(dir);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void infrared_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"\\tir rx [raw]\\r\\n\");\n    printf(\"\\tir tx <protocol> <address> <command>\\r\\n\");\n    printf(\"\\t<command> and <address> are hex-formatted\\r\\n\");\n    printf(\"\\tAvailable protocols:\");\n    for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) {\n        printf(\" %s\", infrared_get_protocol_name((InfraredProtocol)i));\n    }\n    printf(\"\\r\\n\");\n    printf(\"\\tRaw format:\\r\\n\");\n    printf(\"\\tir tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\\r\\n\");\n    printf(\n        \"\\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\\r\\n\",\n        INFRARED_MIN_FREQUENCY,\n        INFRARED_MAX_FREQUENCY);\n    printf(\"\\tir decode <input_file> [<output_file>]\\r\\n\");\n    printf(\"\\tir universal <remote_name> <signal_name>\\r\\n\");\n    printf(\"\\tir universal list <remote_name>\\r\\n\");\n    printf(\"\\tAvailable universal remotes: \");\n\n    infrared_cli_print_universal_remotes();\n}\n\nstatic void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args) {\n    bool enable_decoding = true;\n\n    if(!furi_string_empty(args)) {\n        if(!furi_string_cmp_str(args, \"raw\")) {\n            enable_decoding = false;\n        } else {\n            printf(\"Wrong arguments.\\r\\n\");\n            infrared_cli_print_usage();\n            return;\n        }\n    }\n\n    InfraredWorker* worker = infrared_worker_alloc();\n    infrared_worker_rx_enable_signal_decoding(worker, enable_decoding);\n    infrared_worker_rx_start(worker);\n    infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, pipe);\n\n    printf(\"Receiving %s INFRARED...\\r\\nPress Ctrl+C to abort\\r\\n\", enable_decoding ? \"\" : \"RAW\");\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        furi_delay_ms(50);\n    }\n\n    infrared_worker_rx_stop(worker);\n    infrared_worker_free(worker);\n}\n\nstatic bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {\n    char protocol_name[32];\n    InfraredMessage message;\n    int parsed = sscanf(str, \"%31s %lX %lX\", protocol_name, &message.address, &message.command);\n\n    if(parsed != 3) {\n        return false;\n    }\n\n    message.protocol = infrared_get_protocol_by_name(protocol_name);\n    message.repeat = false;\n    infrared_signal_set_message(signal, &message);\n    return infrared_signal_is_valid(signal);\n}\n\nstatic bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) {\n    char frequency_str[INFRARED_CLI_BUF_SIZE];\n    char duty_cycle_str[INFRARED_CLI_BUF_SIZE];\n    int parsed = sscanf(str, \"RAW F:%9s DC:%9s\", frequency_str, duty_cycle_str);\n\n    if(parsed != 2) {\n        return false;\n    }\n\n    uint32_t frequency;\n    uint32_t duty_cycle_u32;\n    if(strint_to_uint32(frequency_str, NULL, &frequency, 10) != StrintParseNoError ||\n       strint_to_uint32(duty_cycle_str, NULL, &duty_cycle_u32, 10) != StrintParseNoError)\n        return false;\n    float duty_cycle = duty_cycle_u32 / 100.0f;\n\n    str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE;\n\n    uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT);\n    size_t timings_size = 0;\n    while(1) {\n        while(*str == ' ') {\n            ++str;\n        }\n\n        uint32_t timing;\n        char* next_token;\n        if(strint_to_uint32(str, &next_token, &timing, 10) != StrintParseNoError) {\n            break;\n        }\n        str = next_token;\n\n        if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) {\n            break;\n        }\n\n        timings[timings_size] = timing;\n        ++timings_size;\n    }\n\n    infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);\n    free(timings);\n\n    return infrared_signal_is_valid(signal);\n}\n\nstatic void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    const char* str = furi_string_get_cstr(args);\n    InfraredSignal* signal = infrared_signal_alloc();\n\n    bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal);\n    if(success) {\n        infrared_signal_transmit(signal);\n    } else {\n        printf(\"Wrong arguments.\\r\\n\");\n        infrared_cli_print_usage();\n    }\n\n    infrared_signal_free(signal);\n}\n\nstatic bool\n    infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {\n    bool ret = true;\n    InfraredErrorCode error = infrared_signal_save(signal, file, name);\n    if(INFRARED_ERROR_PRESENT(error)) {\n        printf(\n            \"Failed to save signal: \\\"%s\\\" code: 0x%X index: 0x%02X\\r\\n\",\n            name,\n            INFRARED_ERROR_GET_CODE(error),\n            INFRARED_ERROR_GET_INDEX(error));\n        ret = false;\n    }\n    return ret;\n}\n\nstatic bool infrared_cli_decode_raw_signal(\n    const InfraredRawSignal* raw_signal,\n    InfraredDecoderHandler* decoder,\n    FlipperFormat* output_file,\n    const char* signal_name) {\n    InfraredSignal* signal = infrared_signal_alloc();\n    bool ret = false, level = true, is_decoded = false;\n\n    size_t i;\n    for(i = 0; i < raw_signal->timings_size; ++i) {\n        const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);\n\n        if(message) {\n            is_decoded = true;\n            printf(\n                \"Protocol: %s address: 0x%lX command: 0x%lX %s\\r\\n\",\n                infrared_get_protocol_name(message->protocol),\n                message->address,\n                message->command,\n                (message->repeat ? \"R\" : \"\"));\n            if(output_file && !message->repeat) {\n                infrared_signal_set_message(signal, message);\n                if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;\n            }\n        }\n\n        level = !level;\n    }\n\n    if(i == raw_signal->timings_size) {\n        if(!is_decoded && output_file) {\n            infrared_signal_set_raw_signal(\n                signal,\n                raw_signal->timings,\n                raw_signal->timings_size,\n                raw_signal->frequency,\n                raw_signal->duty_cycle);\n            ret = infrared_cli_save_signal(signal, output_file, signal_name);\n        } else {\n            ret = true;\n        }\n    }\n\n    infrared_reset_decoder(decoder);\n    infrared_signal_free(signal);\n    return ret;\n}\n\nstatic bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {\n    bool ret = false;\n\n    InfraredSignal* signal = infrared_signal_alloc();\n    InfraredDecoderHandler* decoder = infrared_alloc_decoder();\n\n    FuriString* tmp;\n    tmp = furi_string_alloc();\n\n    while(infrared_signal_read(signal, input_file, tmp) == InfraredErrorCodeNone) {\n        ret = false;\n        if(!infrared_signal_is_valid(signal)) {\n            printf(\"Invalid signal\\r\\n\");\n            break;\n        }\n        if(!infrared_signal_is_raw(signal)) {\n            if(output_file &&\n               !infrared_cli_save_signal(signal, output_file, furi_string_get_cstr(tmp))) {\n                break;\n            } else {\n                printf(\"Skipping decoded signal\\r\\n\");\n                continue;\n            }\n        }\n        const InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);\n        printf(\n            \"Raw signal: %s, %zu samples\\r\\n\",\n            furi_string_get_cstr(tmp),\n            raw_signal->timings_size);\n        if(!infrared_cli_decode_raw_signal(\n               raw_signal, decoder, output_file, furi_string_get_cstr(tmp)))\n            break;\n        ret = true;\n    }\n\n    infrared_free_decoder(decoder);\n    infrared_signal_free(signal);\n    furi_string_free(tmp);\n\n    return ret;\n}\n\nstatic void infrared_cli_process_decode(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);\n    FlipperFormat* output_file = NULL;\n\n    uint32_t version;\n    FuriString *tmp, *header, *input_path, *output_path;\n    tmp = furi_string_alloc();\n    header = furi_string_alloc();\n    input_path = furi_string_alloc();\n    output_path = furi_string_alloc();\n\n    do {\n        if(!args_read_probably_quoted_string_and_trim(args, input_path)) {\n            printf(\"Wrong arguments.\\r\\n\");\n            infrared_cli_print_usage();\n            break;\n        }\n        args_read_probably_quoted_string_and_trim(args, output_path);\n        if(!flipper_format_buffered_file_open_existing(\n               input_file, furi_string_get_cstr(input_path))) {\n            printf(\n                \"Failed to open file for reading: \\\"%s\\\"\\r\\n\", furi_string_get_cstr(input_path));\n            break;\n        }\n        if(!flipper_format_read_header(input_file, header, &version) ||\n           (!furi_string_start_with_str(header, \"IR\")) || version != 1) {\n            printf(\n                \"Invalid or corrupted input file: \\\"%s\\\"\\r\\n\", furi_string_get_cstr(input_path));\n            break;\n        }\n        if(!furi_string_empty(output_path)) {\n            printf(\"Writing output to file: \\\"%s\\\"\\r\\n\", furi_string_get_cstr(output_path));\n            output_file = flipper_format_file_alloc(storage);\n        }\n        if(output_file &&\n           !flipper_format_file_open_always(output_file, furi_string_get_cstr(output_path))) {\n            printf(\n                \"Failed to open file for writing: \\\"%s\\\"\\r\\n\", furi_string_get_cstr(output_path));\n            break;\n        }\n        if(output_file && !flipper_format_write_header(output_file, header, version)) {\n            printf(\n                \"Failed to write to the output file: \\\"%s\\\"\\r\\n\",\n                furi_string_get_cstr(output_path));\n            break;\n        }\n        if(!infrared_cli_decode_file(input_file, output_file)) {\n            break;\n        }\n        printf(\"File successfully decoded.\\r\\n\");\n    } while(false);\n\n    furi_string_free(tmp);\n    furi_string_free(header);\n    furi_string_free(input_path);\n    furi_string_free(output_path);\n\n    flipper_format_free(input_file);\n    if(output_file) flipper_format_free(output_file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void infrared_cli_list_remote_signals(FuriString* remote_name) {\n    if(furi_string_empty(remote_name)) {\n        printf(\"Missing remote name.\\r\\n\");\n        return;\n    }\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n    FuriString* remote_path = furi_string_alloc_printf(\n        \"%s/%s%s\",\n        INFRARED_ASSETS_FOLDER,\n        furi_string_get_cstr(remote_name),\n        INFRARED_FILE_EXTENSION);\n\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(remote_path))) {\n            printf(\"Invalid remote name.\\r\\n\");\n            break;\n        }\n\n        dict_signals_t signals_dict;\n        dict_signals_init(signals_dict);\n\n        FuriString* key = furi_string_alloc();\n        FuriString* signal_name = furi_string_alloc();\n\n        printf(\"Valid signals:\\r\\n\");\n        int max = 1;\n        while(flipper_format_read_string(ff, \"name\", signal_name)) {\n            furi_string_set_str(key, furi_string_get_cstr(signal_name));\n            int* v = dict_signals_get(signals_dict, key);\n            if(v != NULL) { //-V547\n                (*v)++;\n                max = M_MAX(*v, max);\n            } else {\n                dict_signals_set_at(signals_dict, key, 1);\n            }\n        }\n\n        dict_signals_it_t it;\n        for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) {\n            const struct dict_signals_pair_s* pair = dict_signals_cref(it);\n            printf(\"\\t%s\\r\\n\", furi_string_get_cstr(pair->key));\n        }\n\n        furi_string_free(key);\n        furi_string_free(signal_name);\n        dict_signals_clear(signals_dict);\n\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_string_free(remote_path);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void infrared_cli_brute_force_signals(\n    PipeSide* pipe,\n    FuriString* remote_name,\n    FuriString* signal_name) {\n    InfraredBruteForce* brute_force = infrared_brute_force_alloc();\n    FuriString* remote_path = furi_string_alloc_printf(\n        \"%s/%s.ir\", INFRARED_ASSETS_FOLDER, furi_string_get_cstr(remote_name));\n\n    infrared_brute_force_set_db_filename(brute_force, furi_string_get_cstr(remote_path));\n    infrared_brute_force_add_record(\n        brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, furi_string_get_cstr(signal_name));\n\n    do {\n        if(furi_string_empty(signal_name)) {\n            printf(\"Missing signal name.\\r\\n\");\n            break;\n        }\n        if(infrared_brute_force_calculate_messages(brute_force) != InfraredErrorCodeNone) {\n            printf(\"Invalid remote name.\\r\\n\");\n            break;\n        }\n\n        uint32_t signal_count, current_signal = 0;\n        bool running = infrared_brute_force_start(\n            brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &signal_count);\n\n        if(signal_count <= 0) {\n            printf(\"Invalid signal name.\\r\\n\");\n            break;\n        }\n\n        printf(\"Sending %lu signal(s)...\\r\\n\", signal_count);\n        printf(\"Press Ctrl-C to stop.\\r\\n\");\n\n        while(running) {\n            running = infrared_brute_force_send(brute_force, current_signal);\n\n            if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n\n            printf(\"\\r%d%% complete.\", (int)((float)current_signal++ / (float)signal_count * 100));\n            fflush(stdout);\n        }\n\n        infrared_brute_force_stop(brute_force);\n    } while(false);\n\n    furi_string_free(remote_path);\n    infrared_brute_force_reset(brute_force);\n    infrared_brute_force_free(brute_force);\n}\n\nstatic void infrared_cli_process_universal(PipeSide* pipe, FuriString* args) {\n    FuriString* arg1 = furi_string_alloc();\n    FuriString* arg2 = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, arg1)) break;\n        if(!args_read_string_and_trim(args, arg2)) break;\n    } while(false);\n\n    if(furi_string_empty(arg1)) {\n        printf(\"Wrong arguments.\\r\\n\");\n        infrared_cli_print_usage();\n    } else if(furi_string_equal_str(arg1, \"list\")) {\n        infrared_cli_list_remote_signals(arg2);\n    } else {\n        infrared_cli_brute_force_signals(pipe, arg1, arg2);\n    }\n\n    furi_string_free(arg1);\n    furi_string_free(arg2);\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    if(furi_hal_infrared_is_busy()) {\n        printf(\"INFRARED is busy. Exiting.\");\n        return;\n    }\n\n    FuriString* command;\n    command = furi_string_alloc();\n    args_read_string_and_trim(args, command);\n\n    size_t i = 0;\n    for(; i < COUNT_OF(infrared_cli_commands); ++i) {\n        size_t cmd_len = strlen(infrared_cli_commands[i].cmd);\n        if(!strncmp(furi_string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {\n            break;\n        }\n    }\n\n    if(i < COUNT_OF(infrared_cli_commands)) {\n        infrared_cli_commands[i].process_function(pipe, args);\n    } else {\n        infrared_cli_print_usage();\n    }\n\n    furi_string_free(command);\n}\n\nCLI_COMMAND_INTERFACE(ir, execute, CliCommandFlagDefault, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/main/infrared/infrared_custom_event.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\ntypedef enum {\n    // Reserve first 100 events for button types and indexes, starting from 0\n    InfraredCustomEventTypeReserved = 100,\n    InfraredCustomEventTypeMenuSelected,\n    InfraredCustomEventTypeTransmitStarted,\n    InfraredCustomEventTypeTransmitStopped,\n    InfraredCustomEventTypeSignalReceived,\n    InfraredCustomEventTypeTextEditDone,\n    InfraredCustomEventTypePopupClosed,\n    InfraredCustomEventTypeButtonSelected,\n    InfraredCustomEventTypePopupInput,\n    InfraredCustomEventTypeTaskFinished,\n\n    InfraredCustomEventTypeRpcLoadFile,\n    InfraredCustomEventTypeRpcExit,\n    InfraredCustomEventTypeRpcButtonPressName,\n    InfraredCustomEventTypeRpcButtonPressIndex,\n    InfraredCustomEventTypeRpcButtonRelease,\n    InfraredCustomEventTypeRpcButtonPressReleaseName,\n    InfraredCustomEventTypeRpcButtonPressReleaseIndex,\n    InfraredCustomEventTypeRpcSessionClose,\n\n    InfraredCustomEventTypeGpioTxPinChanged,\n    InfraredCustomEventTypeGpioOtgChanged,\n} InfraredCustomEventType;\n\n#pragma pack(push, 1)\ntypedef union {\n    uint32_t packed_value;\n    struct {\n        uint16_t type;\n        int16_t value;\n    } content;\n} InfraredCustomEvent;\n#pragma pack(pop)\n\nstatic inline uint32_t infrared_custom_event_pack(uint16_t type, int16_t value) {\n    InfraredCustomEvent event = {.content = {.type = type, .value = value}};\n    return event.packed_value;\n}\n\nstatic inline void\n    infrared_custom_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) {\n    InfraredCustomEvent event = {.packed_value = packed_value};\n    if(type) *type = event.content.type;\n    if(value) *value = event.content.value;\n}\n\nstatic inline uint16_t infrared_custom_event_get_type(uint32_t packed_value) {\n    uint16_t type;\n    infrared_custom_event_unpack(packed_value, &type, NULL);\n    return type;\n}\n\nstatic inline int16_t infrared_custom_event_get_value(uint32_t packed_value) {\n    int16_t value;\n    infrared_custom_event_unpack(packed_value, NULL, &value);\n    return value;\n}\n"
  },
  {
    "path": "applications/main/infrared/infrared_remote.c",
    "content": "#include \"infrared_remote.h\"\n\n#include <m-array.h>\n\n#include <toolbox/m_cstr_dup.h>\n#include <toolbox/path.h>\n#include <storage/storage.h>\n\n#define TAG \"InfraredRemote\"\n\n#define INFRARED_FILE_HEADER    \"IR signals file\"\n#define INFRARED_LIBRARY_HEADER \"IR library file\"\n#define INFRARED_FILE_VERSION   (1)\n\nARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575\n\nstruct InfraredRemote {\n    StringArray_t signal_names;\n    FuriString* name;\n    FuriString* path;\n};\n\ntypedef struct {\n    InfraredRemote* remote;\n    FlipperFormat* ff_in;\n    FlipperFormat* ff_out;\n    FuriString* signal_name;\n    InfraredSignal* signal;\n    size_t signal_index;\n} InfraredBatch;\n\ntypedef struct {\n    size_t signal_index;\n    const char* signal_name;\n    const InfraredSignal* signal;\n} InfraredBatchTarget;\n\ntypedef InfraredErrorCode (\n    *InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target);\n\nInfraredRemote* infrared_remote_alloc(void) {\n    InfraredRemote* remote = malloc(sizeof(InfraredRemote));\n    StringArray_init(remote->signal_names);\n    remote->name = furi_string_alloc();\n    remote->path = furi_string_alloc();\n    return remote;\n}\n\nvoid infrared_remote_free(InfraredRemote* remote) {\n    StringArray_clear(remote->signal_names);\n    furi_string_free(remote->path);\n    furi_string_free(remote->name);\n    free(remote);\n}\n\nvoid infrared_remote_reset(InfraredRemote* remote) {\n    StringArray_reset(remote->signal_names);\n    furi_string_reset(remote->name);\n    furi_string_reset(remote->path);\n}\n\nconst char* infrared_remote_get_name(const InfraredRemote* remote) {\n    return furi_string_get_cstr(remote->name);\n}\n\nstatic void infrared_remote_set_path(InfraredRemote* remote, const char* path) {\n    furi_string_set(remote->path, path);\n    path_extract_filename(remote->path, remote->name, true);\n}\n\nconst char* infrared_remote_get_path(const InfraredRemote* remote) {\n    return furi_string_get_cstr(remote->path);\n}\n\nsize_t infrared_remote_get_signal_count(const InfraredRemote* remote) {\n    return StringArray_size(remote->signal_names);\n}\n\nconst char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index) {\n    furi_assert(index < infrared_remote_get_signal_count(remote));\n    return *StringArray_cget(remote->signal_names, index);\n}\n\nInfraredErrorCode infrared_remote_load_signal(\n    const InfraredRemote* remote,\n    InfraredSignal* signal,\n    size_t index) {\n    furi_assert(index < infrared_remote_get_signal_count(remote));\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        const char* path = furi_string_get_cstr(remote->path);\n        if(!flipper_format_buffered_file_open_existing(ff, path)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        error = infrared_signal_search_by_index_and_read(signal, ff, index);\n        if(INFRARED_ERROR_PRESENT(error)) {\n            const char* signal_name = infrared_remote_get_signal_name(remote, index);\n            FURI_LOG_E(TAG, \"Failed to load signal '%s' from file '%s'\", signal_name, path);\n            break;\n        }\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return error;\n}\n\nbool infrared_remote_get_signal_index(\n    const InfraredRemote* remote,\n    const char* name,\n    size_t* index) {\n    uint32_t i = 0;\n    StringArray_it_t it;\n\n    for(StringArray_it(it, remote->signal_names); !StringArray_end_p(it);\n        StringArray_next(it), ++i) {\n        if(strcmp(*StringArray_cref(it), name) == 0) {\n            *index = i;\n            return true;\n        }\n    }\n\n    return false;\n}\n\nInfraredErrorCode infrared_remote_append_signal(\n    InfraredRemote* remote,\n    const InfraredSignal* signal,\n    const char* name) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_file_alloc(storage);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    const char* path = furi_string_get_cstr(remote->path);\n\n    do {\n        if(!flipper_format_file_open_append(ff, path)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        error = infrared_signal_save(signal, ff, name);\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        StringArray_push_back(remote->signal_names, name);\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return error;\n}\n\nstatic InfraredErrorCode infrared_remote_batch_start(\n    InfraredRemote* remote,\n    InfraredBatchCallback batch_callback,\n    const InfraredBatchTarget* target) {\n    FuriString* tmp = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    InfraredBatch batch_context = {\n        .remote = remote,\n        .ff_in = flipper_format_buffered_file_alloc(storage),\n        .ff_out = flipper_format_buffered_file_alloc(storage),\n        .signal_name = furi_string_alloc(),\n        .signal = infrared_signal_alloc(),\n        .signal_index = 0,\n    };\n\n    const char* path_in = furi_string_get_cstr(remote->path);\n    const char* path_out;\n\n    FS_Error status;\n\n    do {\n        furi_string_printf(tmp, \"%s.temp%08x.swp\", path_in, rand());\n        path_out = furi_string_get_cstr(tmp);\n        status = storage_common_stat(storage, path_out, NULL);\n    } while(status == FSE_OK || status == FSE_EXIST);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    StringArray_t buf_names;\n    StringArray_init_set(buf_names, remote->signal_names);\n    do {\n        if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in) ||\n           !flipper_format_buffered_file_open_always(batch_context.ff_out, path_out) ||\n           !flipper_format_write_header_cstr(\n               batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n        const size_t signal_count = infrared_remote_get_signal_count(remote);\n\n        for(; batch_context.signal_index < signal_count; ++batch_context.signal_index) {\n            error = infrared_signal_read(\n                batch_context.signal, batch_context.ff_in, batch_context.signal_name);\n            if(INFRARED_ERROR_PRESENT(error)) {\n                INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index);\n                break;\n            }\n\n            error = batch_callback(&batch_context, target);\n            if(INFRARED_ERROR_PRESENT(error)) {\n                INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index);\n                break;\n            }\n        }\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        if(!flipper_format_buffered_file_close(batch_context.ff_out) ||\n           !flipper_format_buffered_file_close(batch_context.ff_in)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        const FS_Error status = storage_common_rename(storage, path_out, path_in);\n        error = (status == FSE_OK || status == FSE_EXIST) ? InfraredErrorCodeNone :\n                                                            InfraredErrorCodeFileOperationFailed;\n    } while(false);\n\n    if(INFRARED_ERROR_PRESENT(error)) {\n        //Remove all temp data and rollback signal names\n        flipper_format_buffered_file_close(batch_context.ff_out);\n        flipper_format_buffered_file_close(batch_context.ff_in);\n        status = storage_common_stat(storage, path_out, NULL);\n        if(status == FSE_OK || status == FSE_EXIST) storage_common_remove(storage, path_out);\n\n        StringArray_reset(remote->signal_names);\n        StringArray_set(remote->signal_names, buf_names);\n    }\n\n    StringArray_clear(buf_names);\n    infrared_signal_free(batch_context.signal);\n    furi_string_free(batch_context.signal_name);\n    flipper_format_free(batch_context.ff_out);\n    flipper_format_free(batch_context.ff_in);\n    furi_string_free(tmp);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return error;\n}\n\nstatic InfraredErrorCode infrared_remote_insert_signal_callback(\n    const InfraredBatch* batch,\n    const InfraredBatchTarget* target) {\n    // Insert a signal under the specified index\n    if(batch->signal_index == target->signal_index) {\n        InfraredErrorCode error =\n            infrared_signal_save(target->signal, batch->ff_out, target->signal_name);\n        if(INFRARED_ERROR_PRESENT(error)) return error;\n\n        StringArray_push_at(\n            batch->remote->signal_names, target->signal_index, target->signal_name);\n    }\n\n    // Write the rest normally\n    return infrared_signal_save(\n        batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));\n}\n\nInfraredErrorCode infrared_remote_insert_signal(\n    InfraredRemote* remote,\n    const InfraredSignal* signal,\n    const char* name,\n    size_t index) {\n    if(index >= infrared_remote_get_signal_count(remote)) {\n        return infrared_remote_append_signal(remote, signal, name);\n    }\n\n    const InfraredBatchTarget insert_target = {\n        .signal_index = index,\n        .signal_name = name,\n        .signal = signal,\n    };\n\n    return infrared_remote_batch_start(\n        remote, infrared_remote_insert_signal_callback, &insert_target);\n}\n\nstatic InfraredErrorCode infrared_remote_rename_signal_callback(\n    const InfraredBatch* batch,\n    const InfraredBatchTarget* target) {\n    const char* signal_name;\n\n    if(batch->signal_index == target->signal_index) {\n        // Rename the signal at requested index\n        signal_name = target->signal_name;\n        StringArray_set_at(batch->remote->signal_names, batch->signal_index, signal_name);\n    } else {\n        // Use the original name otherwise\n        signal_name = furi_string_get_cstr(batch->signal_name);\n    }\n\n    return infrared_signal_save(batch->signal, batch->ff_out, signal_name);\n}\n\nInfraredErrorCode\n    infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) {\n    furi_assert(index < infrared_remote_get_signal_count(remote));\n\n    const InfraredBatchTarget rename_target = {\n        .signal_index = index,\n        .signal_name = new_name,\n        .signal = NULL,\n    };\n\n    return infrared_remote_batch_start(\n        remote, infrared_remote_rename_signal_callback, &rename_target);\n}\n\nstatic InfraredErrorCode infrared_remote_delete_signal_callback(\n    const InfraredBatch* batch,\n    const InfraredBatchTarget* target) {\n    if(batch->signal_index == target->signal_index) {\n        // Do not save the signal to be deleted, remove it from the signal name list instead\n        StringArray_remove_v(\n            batch->remote->signal_names, batch->signal_index, batch->signal_index + 1);\n    } else {\n        // Pass other signals through\n        return infrared_signal_save(\n            batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));\n    }\n\n    return InfraredErrorCodeNone;\n}\n\nInfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index) {\n    furi_assert(index < infrared_remote_get_signal_count(remote));\n\n    const InfraredBatchTarget delete_target = {\n        .signal_index = index,\n        .signal_name = NULL,\n        .signal = NULL,\n    };\n\n    return infrared_remote_batch_start(\n        remote, infrared_remote_delete_signal_callback, &delete_target);\n}\n\nInfraredErrorCode\n    infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) {\n    const size_t signal_count = infrared_remote_get_signal_count(remote);\n    furi_assert(index < signal_count);\n    furi_assert(new_index < signal_count);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    if(index == new_index) return error;\n\n    InfraredSignal* signal = infrared_signal_alloc();\n    char* signal_name = strdup(infrared_remote_get_signal_name(remote, index));\n\n    do {\n        error = infrared_remote_load_signal(remote, signal, index);\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        error = infrared_remote_delete_signal(remote, index);\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        error = infrared_remote_insert_signal(remote, signal, signal_name, new_index);\n    } while(false);\n\n    free(signal_name);\n    infrared_signal_free(signal);\n\n    return error;\n}\n\nInfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path) {\n    FURI_LOG_I(TAG, \"Creating new file: '%s'\", path);\n\n    infrared_remote_reset(remote);\n    infrared_remote_set_path(remote, path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_file_alloc(storage);\n\n    bool success = false;\n\n    do {\n        if(!flipper_format_file_open_always(ff, path)) break;\n        if(!flipper_format_write_header_cstr(ff, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION))\n            break;\n\n        success = true;\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed;\n}\n\nInfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path) {\n    FURI_LOG_I(TAG, \"Loading file: '%s'\", path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    FuriString* tmp = furi_string_alloc();\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, path)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        uint32_t version;\n        if(!flipper_format_read_header(ff, tmp, &version)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        if(furi_string_equal(tmp, INFRARED_LIBRARY_HEADER)) {\n            FURI_LOG_E(TAG, \"Library file can't be loaded in this context\");\n            error = InfraredErrorCodeWrongFileType;\n            break;\n        }\n\n        if(!furi_string_equal(tmp, INFRARED_FILE_HEADER)) {\n            error = InfraredErrorCodeWrongFileType;\n            FURI_LOG_E(TAG, \"Filetype unknown\");\n            break;\n        }\n\n        if(version != INFRARED_FILE_VERSION) {\n            error = InfraredErrorCodeWrongFileVersion;\n            FURI_LOG_E(TAG, \"Wrong file version\");\n            break;\n        }\n\n        infrared_remote_set_path(remote, path);\n        StringArray_reset(remote->signal_names);\n\n        while(infrared_signal_read_name(ff, tmp) == InfraredErrorCodeNone) {\n            StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp));\n        }\n    } while(false);\n\n    furi_string_free(tmp);\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return error;\n}\n\nInfraredErrorCode infrared_remote_rename(InfraredRemote* remote, const char* new_path) {\n    const char* old_path = infrared_remote_get_path(remote);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    const FS_Error status = storage_common_rename(storage, old_path, new_path);\n    furi_record_close(RECORD_STORAGE);\n\n    const bool success = (status == FSE_OK || status == FSE_EXIST);\n\n    if(success) {\n        infrared_remote_set_path(remote, new_path);\n    }\n\n    return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed;\n}\n\nInfraredErrorCode infrared_remote_remove(InfraredRemote* remote) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote));\n    furi_record_close(RECORD_STORAGE);\n\n    const bool success = (status == FSE_OK || status == FSE_NOT_EXIST);\n\n    if(success) {\n        infrared_remote_reset(remote);\n    }\n\n    return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed;\n}\n"
  },
  {
    "path": "applications/main/infrared/infrared_remote.h",
    "content": "/**\n * @file infrared_remote.h\n * @brief Infrared remote library.\n *\n * An infrared remote contains zero or more infrared signals which\n * have a (possibly non-unique) name each.\n *\n * The current implementation does load only the names into the memory,\n * while the signals themselves are loaded on-demand one by one. In theory,\n * this should allow for quite large remotes with relatively bulky signals.\n */\n#pragma once\n\n#include <lib/infrared/signal/infrared_signal.h>\n\n/**\n * @brief InfraredRemote opaque type declaration.\n */\ntypedef struct InfraredRemote InfraredRemote;\n\n/**\n * @brief Create a new InfraredRemote instance.\n *\n * @returns pointer to the created instance.\n */\nInfraredRemote* infrared_remote_alloc(void);\n\n/**\n * @brief Delete an InfraredRemote instance.\n *\n * @param[in,out] remote pointer to the instance to be deleted.\n */\nvoid infrared_remote_free(InfraredRemote* remote);\n\n/**\n * @brief Reset an InfraredRemote instance.\n *\n * Resetting a remote clears its signal name list and\n * the associated file path.\n *\n * @param[in,out] remote pointer to the instance to be deleted.\n */\nvoid infrared_remote_reset(InfraredRemote* remote);\n\n/**\n * @brief Get an InfraredRemote instance's name.\n *\n * The name is deduced from the file path.\n *\n * The return value remains valid unless one of the following functions is called:\n * - infrared_remote_reset()\n * - infrared_remote_load()\n * - infrared_remote_create()\n *\n * @param[in] remote pointer to the instance to be queried.\n * @returns pointer to a zero-terminated string containing the name.\n */\nconst char* infrared_remote_get_name(const InfraredRemote* remote);\n\n/**\n * @brief Get an InfraredRemote instance's file path.\n *\n * Same return value validity considerations as infrared_remote_get_name().\n *\n * @param[in] remote pointer to the instance to be queried.\n * @returns pointer to a zero-terminated string containing the path.\n */\nconst char* infrared_remote_get_path(const InfraredRemote* remote);\n\n/**\n * @brief Get the number of signals listed in an InfraredRemote instance.\n *\n * @param[in] remote pointer to the instance to be queried.\n * @returns number of signals, zero or more\n */\nsize_t infrared_remote_get_signal_count(const InfraredRemote* remote);\n\n/**\n * @brief Get the name of a signal listed in an InfraredRemote instance.\n *\n * @param[in] remote pointer to the instance to be queried.\n * @param[in] index index of the signal in question. Must be less than the total signal count.\n */\nconst char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index);\n\n/**\n * @brief Get the index of a signal listed in an InfraredRemote instance by its name.\n *\n * @param[in] remote pointer to the instance to be queried.\n * @param[in] name pointer to a zero-terminated string containig the name of the signal in question.\n * @param[out] index pointer to the variable to hold the signal index.\n * @returns true if a signal with the given name was found, false otherwise.\n */\nbool infrared_remote_get_signal_index(\n    const InfraredRemote* remote,\n    const char* name,\n    size_t* index);\n\n/**\n * @brief Load a signal listed in an InfraredRemote instance.\n *\n * As mentioned above, the signals are loaded on-demand. The user code must call this function\n * each time it wants to interact with a new signal.\n *\n * @param[in] remote pointer to the instance to load from.\n * @param[out] signal pointer to the signal to load into. Must be allocated.\n * @param[in] index index of the signal to be loaded. Must be less than the total signal count.\n * @return InfraredErrorCodeNone if the signal was successfully loaded, otherwise error code.\n */\nInfraredErrorCode\n    infrared_remote_load_signal(const InfraredRemote* remote, InfraredSignal* signal, size_t index);\n\n/**\n * @brief Append a signal to the file associated with an InfraredRemote instance.\n *\n * The file path must be somehow initialised first by calling either infrared_remote_load() or\n * infrared_remote_create(). As the name suggests, the signal will be put in the end of the file.\n *\n * @param[in,out] remote pointer to the instance to append to.\n * @param[in] signal pointer to the signal to be appended.\n * @param[in] name pointer to a zero-terminated string containing the name of the signal.\n * @returns InfraredErrorCodeNone if the signal was successfully appended, otherwise error code.\n */\nInfraredErrorCode infrared_remote_append_signal(\n    InfraredRemote* remote,\n    const InfraredSignal* signal,\n    const char* name);\n\n/**\n * @brief Insert a signal to the file associated with an InfraredRemote instance.\n *\n * Same behaviour as infrared_remote_append_signal(), but the user code can decide where to\n * put the signal in the file.\n *\n * Index values equal to or greater than the total signal count will result in behaviour\n * identical to infrared_remote_append_signal().\n *\n * @param[in,out] remote pointer to the instance to insert to.\n * @param[in] signal pointer to the signal to be inserted.\n * @param[in] name pointer to a zero-terminated string containing the name of the signal.\n * @param[in] index the index under which the signal shall be inserted.\n * @returns InfraredErrorCodeNone if the signal was successfully inserted, otherwise error\n * code describing what error happened ORed with index pointing which signal caused an error.\n */\nInfraredErrorCode infrared_remote_insert_signal(\n    InfraredRemote* remote,\n    const InfraredSignal* signal,\n    const char* name,\n    size_t index);\n\n/**\n * @brief Rename a signal in the file associated with an InfraredRemote instance.\n *\n * Only changes the signal's name, but neither its position nor contents.\n *\n * @param[in,out] remote pointer to the instance to be modified.\n * @param[in] index index of the signal to be renamed. Must be less than the total signal count.\n * @param[in] new_name pointer to a zero-terminated string containig the signal's new name.\n * @returns InfraredErrorCodeNone if the signal was successfully renamed, otherwise error code.\n */\nInfraredErrorCode\n    infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name);\n\n/**\n * @brief Change a signal's position in the file associated with an InfraredRemote instance.\n *\n * Only changes the signal's position (index), but neither its name nor contents.\n *\n * @param[in,out] remote pointer to the instance to be modified.\n * @param[in] index index of the signal to be moved. Must be less than the total signal count.\n * @param[in] new_index index of the signal to be moved. Must be less than the total signal count.\n * @returns InfraredErrorCodeNone if the signal was moved successfully, otherwise error\n * code describing what error happened ORed with index pointing which signal caused an error.\n */\nInfraredErrorCode\n    infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index);\n\n/**\n * @brief Delete a signal in the file associated with an InfraredRemote instance.\n *\n * @param[in,out] remote pointer to the instance to be modified.\n * @param[in] index index of the signal to be deleted. Must be less than the total signal count.\n * @returns InfraredErrorCodeNone if the signal was successfully deleted, otherwise error\n * code describing what error happened ORed with index pointing which signal caused an error.\n */\nInfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index);\n\n/**\n * @brief Create a new file and associate it with an InfraredRemote instance.\n *\n * The instance will be reset and given a new empty file with just the header.\n *\n * @param[in,out] remote pointer to the instance to be assigned with a new file.\n * @param[in] path pointer to a zero-terminated string containing the full file path.\n * @returns InfraredErrorCodeNone if the file was successfully created, otherwise error code.\n */\nInfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path);\n\n/**\n * @brief Associate an InfraredRemote instance with a file and load the signal names from it.\n *\n * The instance will be reset and fill its signal name list from the given file.\n * The file must already exist and be valid.\n *\n * @param[in,out] remote pointer to the instance to be assigned with an existing file.\n * @param[in] path pointer to a zero-terminated string containing the full file path.\n * @returns InfraredErrorCodeNone if the file was successfully loaded, otherwise error code.\n */\nInfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path);\n\n/**\n * @brief Rename the file associated with an InfraredRemote instance.\n *\n * Only renames the file, no signals are added, moved or deleted.\n *\n * @param[in,out] remote pointer to the instance to be modified.\n * @param[in] new_path pointer to a zero-terminated string containing the new full file path.\n * @returns InfraredErrorCodeNone if the file was successfully renamed, otherwise error code.\n */\nInfraredErrorCode infrared_remote_rename(InfraredRemote* remote, const char* new_path);\n\n/**\n * @brief Remove the file associated with an InfraredRemote instance.\n *\n * This operation is irreversible and fully deletes the remote file\n * from the underlying filesystem.\n * After calling this function, the instance becomes invalid until\n * infrared_remote_create() or infrared_remote_load() are successfully executed.\n *\n * @param[in,out] remote pointer to the instance to be modified.\n * @returns InfraredErrorCodeNone if the file was successfully removed, otherwise error code.\n */\nInfraredErrorCode infrared_remote_remove(InfraredRemote* remote);\n"
  },
  {
    "path": "applications/main/infrared/resources/infrared/assets/ac.ir",
    "content": "Filetype: IR library file\nVersion: 1\n#\n# Model: Electrolux EACM-16 HP/N3\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 502 3436 510 475 509 476 508 477 507 477 507 479 505 480 504 480 504 490 504 481 502 482 501 483 563 420 511 474 510 475 509 476 508 485 561 423 508 476 508 477 507 478 506 479 505 480 504 481 503 517 508 476 508 478 506 479 505 479 505 481 503 483 521 1456 501 498 507 479 505 480 504 481 503 482 501 483 563 421 562 422 509 499 506 479 505 480 504 481 503 482 502 484 510 1451 506 479 505 1542 562 1396 509 471 502 476 508 469 504 3425 511\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 507 3430 506 479 505 480 504 481 503 481 503 483 501 485 509 1453 504 1465 503 482 502 483 511 473 500 485 509 476 508 477 507 478 506 487 507 477 507 478 506 479 505 480 504 482 502 483 501 484 500 523 503 482 502 484 500 485 509 476 508 476 508 478 506 1456 501 501 504 482 502 483 501 484 500 485 509 476 508 477 507 1455 502 509 506 479 505 1457 500 485 509 476 508 1454 503 482 502 483 501 568 499 1459 509 1450 507 471 502 474 510 3421 505\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 504 3433 503 482 502 484 510 474 510 475 509 476 508 478 506 1456 564 1405 510 475 509 476 508 502 482 477 507 478 506 479 505 480 504 489 505 480 504 481 503 482 502 483 511 473 511 474 510 475 509 509 506 479 505 480 504 481 503 482 512 473 511 474 510 476 508 1469 509 475 509 476 508 477 507 478 506 479 505 480 504 481 503 505 510 475 509 502 482 503 481 504 480 505 478 507 477 1459 509 560 507 1451 506 473 511 493 480 1450 507 3422 503\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 525 3615 530 506 561 474 562 474 562 473 563 473 531 505 562 1502 528 1542 562 474 562 474 530 505 531 504 532 504 532 504 616 419 533 510 589 447 526 509 527 509 527 509 527 508 528 508 528 507 529 542 525 510 526 509 527 509 527 509 527 508 528 508 528 1535 527 524 533 503 533 503 533 502 534 502 534 502 534 501 535 501 525 534 533 502 534 502 534 501 535 502 534 1529 533 503 533 503 533 587 533 497 528 501 524 1536 526 501 524 3609 526\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 531 3406 530 455 529 456 528 457 537 447 537 448 535 450 534 1429 528 1442 536 448 536 449 534 451 532 452 532 453 530 454 530 455 529 464 530 454 529 456 528 457 537 448 536 449 535 450 533 451 533 490 535 449 534 450 534 451 533 452 532 453 531 455 529 1433 534 1443 535 449 535 450 534 452 531 453 530 454 530 455 529 456 538 472 532 452 532 454 530 1433 535 1427 530 1432 536 1427 530 1431 537 1511 530 448 536 1422 535 1423 534 1422 535 3395 530\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 506 3430 506 478 506 479 505 480 504 481 503 482 502 484 500 1463 505 1465 503 482 502 483 501 484 500 485 509 476 508 477 507 478 506 486 508 477 507 478 506 479 505 480 504 481 503 482 502 483 500 523 502 482 502 483 501 484 500 485 509 476 508 478 506 1455 502 498 507 478 506 479 505 481 503 482 501 483 500 484 500 485 509 500 505 481 502 482 502 1461 507 1455 502 1459 509 476 508 477 507 563 504 1453 504 1454 503 1454 503 1453 504 3426 499\n#\n# Model: Hisense Generic\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8974 4505 598 1647 595 1651 591 539 592 542 600 537 594 547 595 549 593 1662 591 532 599 1649 593 1659 594 540 602 538 593 548 594 552 600 535 596 528 593 535 596 536 595 540 602 538 593 548 593 551 601 532 599 525 596 1651 591 539 592 543 599 1660 593 1669 594 1672 601 533 598 526 595 533 598 533 598 536 595 543 599 543 599 547 595 540 602 524 597 530 601 530 601 534 597 541 601 541 601 545 597 522 599 7938 591 532 599 528 593 537 594 541 601 537 594 546 596 549 593 1663 600 524 597 530 601 530 601 533 598 540 591 550 592 553 599 536 595 528 593 536 595 536 595 540 591 547 595 548 593 552 600 535 596 529 592 536 595 535 596 538 593 545 597 546 596 550 592 542 600 524 597 531 600 531 600 534 597 542 600 542 600 545 597 538 593 530 601 526 595 537 594 540 591 547 595 546 595 550 592 543 599 526 595 532 599 531 601 535 596 542 600 542 600 546 596 538 593 531 600 1648 594 536 595 539 592 1669 594 1669 594 1671 602 1637 595 7947 592 532 599 529 592 539 592 543 599 540 591 551 601 544 598 537 594 531 600 1647 595 535 596 539 592 545 597 546 596 550 592 543 599 526 595 533 598 534 597 538 593 545 597 546 596 550 602 534 597 527 594 533 598 533 598 536 595 544 598 543 599 547 595 540 591 533 598 529 592 539 592 1663 600 538 593 547 595 551 591 543 599 524 597 530 591 539 592 542 600 537 594 547 595 550 592 542 600 525 596 1651 592 538 593 1662 591 546 596 545 597 548 594 523 629\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8990 4494 599 1648 595 1654 599 533 598 537 594 544 598 544 598 548 594 1662 601 523 598 1651 592 1660 593 542 600 539 593 550 592 553 599 536 596 527 594 531 601 1648 595 538 593 542 600 540 592 551 601 532 600 1643 600 1650 593 538 593 542 600 1660 593 1671 592 1673 601 534 598 527 594 534 597 533 599 536 595 543 599 542 600 545 597 537 595 530 591 536 596 535 596 538 593 544 598 543 599 546 596 522 599 7935 595 530 591 536 596 536 596 539 592 545 597 544 598 547 595 1660 593 530 591 536 596 535 596 536 595 542 600 541 591 552 600 534 597 525 596 531 601 529 592 541 601 537 595 546 596 548 594 540 591 532 600 527 594 536 595 538 594 544 598 543 599 546 596 538 594 531 600 527 594 536 595 539 593 545 597 543 599 546 596 538 593 530 591 535 596 532 599 532 600 536 595 543 599 544 598 535 596 525 596 530 591 538 593 538 593 542 600 540 591 551 601 532 600 1640 593 1651 592 1655 598 535 596 1657 596 1663 601 1661 592 1641 592 7941 599 526 595 533 599 532 600 535 597 541 601 541 601 544 598 537 595 1651 592 535 597 535 597 538 594 545 597 545 597 548 594 540 592 532 600 528 593 539 593 542 600 539 592 549 593 551 601 533 598 524 597 528 593 536 595 538 593 544 598 543 599 546 596 539 593 531 601 528 593 538 593 1661 592 546 596 545 597 547 595 539 592 532 600 527 594 536 596 538 593 543 599 542 600 544 598 535 596 1646 597 531 601 529 592 1663 601 537 595 547 595 550 592 524 597\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8982 4489 604 1643 600 1649 594 537 594 540 602 537 594 547 595 550 592 1664 599 525 596 1652 601 1651 592 542 600 538 593 549 593 552 600 534 597 1646 597 530 601 1651 602 534 597 541 601 541 601 544 598 537 594 529 592 1655 598 533 598 536 595 542 600 542 600 546 596 539 592 532 599 527 594 537 594 540 591 546 596 545 597 548 594 540 602 523 598 529 592 539 592 542 600 539 592 549 593 550 592 524 597 7929 600 525 596 532 599 533 598 537 594 543 599 542 600 544 598 1654 599 524 597 530 591 538 593 540 591 544 598 543 599 544 598 535 596 526 595 531 600 529 592 544 598 541 601 542 600 546 596 540 602 522 599 529 602 530 601 534 597 541 601 541 601 545 597 539 592 532 599 528 593 539 592 541 601 537 594 547 595 551 601 535 596 529 592 536 595 537 594 541 601 538 593 549 593 553 599 536 595 529 592 536 595 534 597 537 594 544 598 543 599 546 596 539 592 1653 600 1650 593 1660 593 543 599 541 601 541 601 546 596 1643 600 7943 596 529 602 527 594 538 593 541 601 538 593 549 593 553 599 536 595 1649 594 535 596 535 596 539 592 546 596 546 596 549 593 542 600 525 596 531 600 530 601 533 598 540 602 539 592 552 600 535 596 527 594 533 598 533 598 536 595 543 599 543 599 545 597 537 594 530 601 526 595 536 595 1660 593 546 596 547 595 550 602 533 598 526 595 533 598 534 597 538 593 546 596 547 595 551 601 534 597 1647 596 532 599 532 599 1656 597 541 601 542 600 545 597 522 599\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8967 4495 597 1645 597 1648 594 535 596 537 594 542 600 541 601 543 599 1655 598 525 596 1651 592 1658 595 539 592 545 597 544 598 546 596 538 593 1648 595 532 599 1648 594 539 592 545 597 543 599 545 597 536 595 528 593 1651 592 538 593 541 601 1654 599 1661 592 1671 592 541 601 523 598 529 592 538 593 541 601 536 595 544 598 547 595 539 592 531 600 526 595 535 596 537 594 543 599 541 601 543 598 518 593 7937 602 522 599 528 593 537 594 539 592 545 597 544 598 546 596 1656 597 525 596 530 601 528 593 539 592 544 598 543 599 544 598 535 596 526 595 531 600 530 601 532 599 538 593 547 595 549 593 540 602 521 600 526 595 535 596 537 594 543 599 541 601 543 599 536 595 527 594 532 600 530 601 532 599 538 593 546 596 548 594 539 592 531 600 525 596 534 597 535 596 540 591 549 593 551 601 532 599 524 597 529 592 537 594 538 593 543 599 540 591 551 601 532 599 1641 591 1654 599 1650 593 540 591 1664 599 1660 593 1671 592 1643 600 7922 596 528 593 533 598 532 599 535 596 540 591 549 593 552 600 533 598 1644 599 529 592 538 593 539 592 544 598 541 590 550 592 539 592 528 593 531 590 537 594 536 595 539 592 546 596 546 596 536 595 526 595 529 592 535 596 535 596 538 593 546 596 546 596 535 596 524 597 527 594 533 598 1649 593 541 601 538 593 549 593 538 593 528 593 532 599 528 593 539 592 542 600 538 593 548 594 538 593 1643 599 525 596 532 599 1649 593 541 601 538 593 548 594 520 591\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8970 4496 597 1648 595 1652 601 530 602 533 598 541 601 541 601 543 599 1652 601 523 598 1649 594 1656 597 538 593 545 597 545 597 549 593 541 601 523 598 529 592 1658 595 540 592 546 596 545 597 548 594 541 601 523 598 529 592 539 593 542 600 538 593 1668 595 1670 593 1662 591 533 599 529 592 539 593 542 600 538 593 547 595 549 593 542 600 524 597 530 602 529 592 543 599 539 593 549 593 551 601 516 595 7937 593 532 599 527 594 536 596 539 592 546 596 545 597 548 594 1661 592 532 600 528 593 538 593 541 601 537 594 547 595 550 602 533 599 526 595 533 599 533 599 536 595 543 599 541 601 544 598 536 595 529 592 535 596 535 596 538 594 544 598 544 598 547 595 541 601 523 598 528 593 537 594 540 602 536 595 546 596 550 602 533 598 526 595 532 600 531 600 534 597 541 601 541 601 545 597 539 593 532 600 528 593 538 593 541 601 537 594 547 595 550 602 532 599 523 598 527 594 1653 600 533 598 538 593 1664 599 1662 591 523 598 7926 593 529 592 534 597 532 599 534 597 538 593 546 596 547 595 537 594 1648 595 532 600 532 599 536 595 543 599 543 599 546 596 540 602 522 599 529 592 538 594 541 601 536 595 546 596 549 593 542 600 523 598 529 592 538 593 541 601 538 593 548 594 552 600 534 597 527 594 534 597 534 597 1657 596 543 599 543 599 548 594 542 600 524 597 530 601 530 602 533 598 538 593 547 595 551 601 533 599 1644 599 528 593 538 593 1661 592 545 597 545 597 548 594 524 597\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8972 4491 592 1651 592 1655 598 532 599 535 597 542 600 541 601 544 598 1656 597 526 595 1652 591 1658 595 539 593 545 597 545 597 546 596 537 594 529 592 535 596 1653 600 534 597 541 601 539 592 552 600 533 598 525 596 530 591 538 593 539 592 1665 598 1662 591 1673 601 533 598 526 595 533 598 532 600 534 597 540 591 548 594 550 592 542 600 523 598 528 593 536 595 537 594 543 599 542 600 543 599 517 594 7937 593 531 601 526 595 535 597 537 594 542 600 541 601 543 599 1654 599 523 598 528 593 536 596 538 594 542 600 541 590 552 600 532 599 524 597 528 593 536 595 537 595 541 601 539 593 551 591 542 600 522 599 527 594 536 595 537 594 543 599 540 591 552 600 532 600 523 598 527 594 535 596 537 595 542 600 540 591 552 600 532 600 523 598 528 593 536 595 538 593 543 599 541 601 543 599 535 596 527 594 532 600 531 601 534 597 540 592 549 593 552 600 534 597 525 596 529 592 1655 598 534 597 1656 597 1661 592 1671 592 1644 599 7934 596 529 592 535 597 535 597 538 593 544 598 543 599 545 597 538 593 1650 593 535 596 534 597 536 595 540 591 547 595 547 595 536 595 526 595 529 592 536 595 535 596 539 593 546 596 547 595 538 593 528 593 531 601 529 592 541 601 536 596 545 597 548 594 540 592 532 600 526 595 535 596 1656 597 541 601 540 592 553 599 534 597 526 595 532 599 531 600 533 598 539 593 548 594 552 600 535 596 1647 596 531 590 538 593 1656 597 538 594 545 597 545 597 518 593\n#\n# Model: Daichi DA25AVQS1-W\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9131 4318 815 389 817 1523 784 391 815 1523 784 1494 813 422 784 391 816 421 785 1524 784 392 814 422 783 1525 782 424 782 422 784 422 784 422 784 396 809 423 783 422 782 423 783 423 783 1496 812 1525 782 424 783 423 783 422 783 423 783 394 811 1526 782 424 782 1524 782 423 783 423 782 1525 782 423 675 19938 810 425 781 424 674 502 811 423 675 531 675 531 675 531 676 530 675 531 675 531 675 502 704 530 675 530 676 530 676 502 704 530 676 530 676 530 675 530 675 530 676 530 675 530 676 530 676 529 677 529 677 529 677 530 677 529 677 1631 676 529 677 1630 678 1631 677\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9076 4455 677 1629 678 528 677 528 677 1630 677 529 677 530 676 530 676 531 675 531 675 531 675 531 675 531 674 531 675 532 674 531 674 532 674 531 675 532 674 531 674 531 674 532 674 1633 674 1633 674 532 674 532 674 532 674 532 674 532 674 1634 673 533 673 1634 673 533 674 532 674 1633 674 533 673 19965 674 531 674 532 675 531 675 531 675 531 675 531 675 531 675 531 675 532 674 531 675 531 676 531 674 531 675 531 675 531 674 531 675 531 675 532 674 532 674 531 675 532 674 532 674 531 675 531 675 532 674 532 674 532 674 532 674 1633 674 1633 674 532 674 532 674\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9076 4453 679 1628 703 503 703 503 702 1607 700 506 699 507 699 507 699 507 699 1609 698 1610 698 1609 699 507 699 508 698 508 698 508 698 508 698 508 698 507 699 508 698 508 698 508 697 1609 698 1610 698 508 698 508 698 508 698 508 698 509 697 1610 697 508 698 1610 698 508 698 508 698 1610 697 509 697 19941 699 507 699 507 699 507 699 508 698 508 698 508 698 508 698 508 697 508 698 508 698 507 699 508 698 508 698 508 698 508 698 508 698 508 698 508 697 509 697 509 697 508 698 508 698 508 698 508 698 509 697 508 697 509 697 509 696 509 697 1635 673 533 673 1611 696\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9081 4423 707 499 706 500 704 1604 702 1606 701 505 701 506 700 506 700 506 700 1608 700 1607 700 1609 699 506 700 506 700 506 700 506 700 506 700 507 699 506 700 506 700 507 699 507 699 1608 700 1608 699 507 699 507 699 507 699 507 699 507 699 1608 699 507 698 1609 698 507 699 507 699 1609 699 507 699 19938 699 506 700 506 700 507 699 506 699 506 700 506 699 506 700 507 699 507 699 507 700 506 699 507 699 507 699 507 699 507 699 507 698 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 700 507 699 507 698 507 699 507 699 1609 699 507 698 1609 699 1609 699\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700\n#\n# Model: Saturn CS-TL09CHR\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3014 1708 488 1059 464 1085 461 362 461 363 460 387 436 1085 462 362 461 402 436 1084 463 1083 463 364 459 1083 463 363 460 386 437 1082 518 1044 464 386 437 1108 439 1108 438 386 464 360 463 1082 464 360 490 352 459 1084 462 362 460 363 460 363 460 364 459 364 459 364 459 380 459 364 459 364 459 364 460 364 459 364 459 364 459 364 459 380 459 364 459 365 458 1088 459 364 459 365 458 1088 458 365 458 380 459 365 458 1088 459 365 458 364 459 365 459 365 458 365 458 380 458 1088 459 1088 459 1088 458 365 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 459 365 458 365 458 381 458 366 457 366 457 366 457 366 457 366 458 365 458 366 458 381 457 366 457 366 457 366 457 366 457 366 457 366 457 366 458 382 457 366 457 366 457 367 456 367 457 367 456 367 456 390 433 406 433 367 456 367 456 390 433 391 433 390 433 390 433 390 433 406 433 391 432 1114 432 391 432 391 432 391 432 391 432 1114 433 396 433\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3011 1710 462 1084 462 1085 486 356 467 356 444 364 484 1060 462 364 483 357 458 1085 461 1085 461 388 435 1085 461 388 435 388 435 1084 462 1099 463 387 436 1084 462 1085 461 388 461 362 461 1084 462 362 461 378 460 1087 459 365 458 365 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 367 456 367 456 367 456 367 456 382 457 367 456 367 456 1090 457 367 456 367 456 1090 457 367 456 382 457 1090 456 1090 457 367 456 367 456 367 456 367 456 367 456 382 457 1091 456 1091 456 1091 456 1090 456 367 456 367 456 367 457 382 457 367 456 367 456 367 456 367 456 368 456 367 456 368 455 383 456 367 456 367 456 368 455 368 455 368 455 368 456 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 384 455 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 1091 456 1091 456 368 456 1091 455 368 455 368 455 1091 456 373 456\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3012 1709 462 1083 463 1084 463 361 462 362 484 355 469 1059 463 387 436 402 437 1083 464 1083 464 362 462 1080 520 354 469 354 469 1026 521 1068 518 354 390 1108 439 1108 438 386 463 360 464 1081 465 359 464 375 463 1083 463 361 462 362 461 363 460 363 460 364 459 364 459 380 459 364 459 364 459 365 458 365 458 365 458 365 458 365 458 380 459 365 458 365 458 1089 458 365 459 365 458 1089 458 366 457 382 456 1090 457 1113 433 390 433 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 1113 434 390 433 390 433 390 433 405 434 390 433 390 433 390 434 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 390 433 390 433 390 433 390 434 405 433 390 433 390 433 390 433 390 433 390 433 390 433 390 433 406 433 390 433 390 433 390 433 390 433 390 433 391 432 391 432 406 433 390 433 391 432 391 432 391 433 390 433 390 433 391 432 406 433 391 432 391 432 1114 433 391 432 391 432 391 432 1114 433 396 433\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3011 1712 461 1087 459 1088 459 364 459 389 434 366 458 1086 461 388 435 404 435 1086 460 1085 461 388 435 1085 461 365 458 388 435 1084 462 1098 464 387 436 1084 462 1084 462 388 461 362 461 1084 462 362 461 378 460 1086 460 364 459 365 458 365 458 365 458 366 457 365 458 381 458 366 457 366 458 366 457 366 457 366 457 366 457 365 458 381 458 366 458 366 457 1089 458 366 457 366 457 1089 458 366 457 381 458 1089 458 366 457 366 457 366 457 366 458 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 367 456 367 456 367 457 366 457 366 457 382 457 367 457 366 457 367 457 366 457 367 457 366 457 366 457 382 457 367 456 367 456 367 456 367 456 367 456 367 456 367 456 382 457 367 456 1090 457 367 456 1091 456 1090 457 1090 456 367 456 373 456\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3045 1677 493 1023 524 1024 522 354 469 354 469 354 469 1026 576 353 470 350 487 1001 492 1054 492 354 469 1054 492 355 468 354 469 1054 492 1070 491 354 468 1056 438 1109 438 386 437 385 438 1107 439 385 438 400 464 1081 465 359 464 360 463 361 462 362 461 388 435 388 435 404 434 389 434 389 434 389 434 389 434 389 434 389 434 389 434 405 434 389 434 389 434 1112 434 389 434 389 434 1113 434 389 434 405 434 1112 435 389 434 366 458 365 458 365 458 365 458 365 458 381 458 365 458 366 457 365 458 1089 457 366 457 366 457 366 457 382 456 367 433 390 433 391 432 391 432 391 432 391 432 391 432 406 433 391 432 391 432 390 433 391 432 391 432 391 432 391 432 406 433 391 432 391 433 391 432 391 432 391 432 391 432 391 432 407 432 391 432 391 432 391 432 392 431 391 433 391 432 391 432 430 409 393 430 392 431 392 431 392 432 391 457 367 432 392 431 408 431 392 431 1138 433 391 408 392 431 392 456 367 456 1113 408 421 433\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457\n#\n# Model: Olimpia Splendid OS-SEAMH09EI\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4402 4336 561 1641 538 554 535 1616 563 1640 539 554 535 557 532 1645 534 560 539 551 538 1639 540 553 536 556 533 1644 535 1642 537 556 533 1620 558 558 531 561 538 554 535 1641 538 1639 540 1638 530 1620 559 1647 532 1643 536 1641 538 1613 566 553 536 557 532 560 539 553 536 558 531 560 529 1647 532 535 564 1613 555 537 563 1614 565 554 535 559 530 1645 534 559 530 1647 532 560 539 1612 557 562 537 1640 539 1611 557 5189 4398 4344 564 1638 530 563 537 1640 539 1639 529 563 537 530 559 1644 535 560 529 535 564 1639 529 537 563 556 533 1644 535 1643 536 557 532 1647 532 559 530 536 564 555 534 1643 536 1616 563 1640 539 1613 555 1624 565 1610 558 1619 560 1617 562 557 532 560 539 554 535 557 532 562 537 553 536 1641 538 555 534 1643 536 530 559 1644 535 558 531 564 536 1639 539 553 536 1641 537 555 534 1617 562 557 532 1645 534 1645 534\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4404 4345 563 1613 566 553 536 1641 538 1614 565 528 561 557 532 1645 534 560 539 551 538 1639 529 563 536 556 533 1618 561 1642 537 556 533 1645 534 557 532 561 538 1638 530 1646 533 1645 554 1623 556 1621 558 1596 562 1613 586 1591 588 531 537 555 534 559 530 562 537 555 534 561 538 552 537 555 534 559 530 562 537 555 534 559 530 562 537 557 532 1643 536 1615 564 1639 560 1591 588 1616 563 1588 580 1623 556 1621 537 5183 4404 4339 579 1597 581 538 530 1646 533 1619 580 539 529 563 536 1615 564 557 532 532 557 1646 533 560 539 553 536 1641 538 1639 539 554 535 1643 536 555 534 559 530 1647 532 1646 533 1644 535 1617 582 1620 559 1621 537 1612 587 1590 589 530 538 554 535 558 531 561 538 554 535 560 539 551 538 554 535 558 531 561 538 554 535 557 532 561 538 556 533 1642 537 1640 538 1613 565 1638 561 1590 589 1588 580 1623 556 1598 560\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4400 4357 561 1616 563 530 559 1619 559 1644 535 557 532 535 564 1613 555 565 534 556 533 1645 534 533 556 536 563 1614 565 1639 529 537 563 1643 536 529 560 558 531 1647 532 1645 533 1618 561 1643 536 1642 537 1642 537 1613 565 1638 530 537 563 556 533 559 530 563 536 556 533 562 538 527 562 1615 563 555 534 1643 536 531 558 561 538 554 535 560 539 1610 558 560 539 1612 556 563 537 1640 538 1639 539 1612 556 1620 558 5189 4398 4345 562 1614 564 529 560 1643 536 1616 563 531 558 534 565 1639 529 565 534 556 533 1619 559 559 530 563 537 1641 538 1640 538 554 535 1645 534 531 558 560 539 1639 529 1622 556 1621 558 1646 532 1619 559 1646 533 1617 562 1642 537 557 532 560 539 554 535 557 532 561 538 556 533 558 531 1620 559 560 539 1638 530 563 536 556 533 560 539 555 534 1642 537 556 533 1619 559 559 530 1622 557 1621 558 1620 558 1647 532\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4402 4346 561 1615 564 555 534 1644 535 1643 536 531 558 561 538 1639 539 555 534 531 558 1645 533 560 529 563 536 1641 537 1640 538 528 561 1645 533 531 558 561 538 1639 539 1638 530 1622 557 1647 532 1646 564 1590 557 1619 559 1618 561 558 531 562 537 555 534 559 530 563 536 558 531 1645 533 559 530 1648 530 1647 531 1646 532 1646 532 560 539 556 533 558 531 1621 557 561 538 555 534 558 531 562 537 1641 537 1639 539 5183 4405 4339 558 1645 533 559 530 1648 530 1647 532 561 538 555 534 1644 535 560 539 551 538 1614 564 555 534 559 530 1647 532 1620 558 561 538 1615 563 553 536 531 558 1620 558 1644 534 1643 536 1642 537 1641 537 1642 536 1613 565 1638 530 563 536 557 532 534 565 554 535 557 532 563 536 1613 565 554 535 1642 537 1641 537 1640 538 1640 538 554 535 560 539 551 538 1614 564 528 561 558 531 562 537 555 534 1644 534 1645 533\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4404 4354 564 1614 564 528 561 1617 562 1642 537 530 559 534 565 1613 555 539 560 556 533 1619 559 534 555 563 536 1642 537 1641 537 555 534 1646 533 532 557 562 537 1640 538 1613 565 1613 555 1648 531 1647 532 1622 557 1619 559 1618 560 559 530 563 536 556 533 560 539 554 535 559 530 561 538 1639 539 554 535 1616 563 1615 564 1615 563 555 534 561 538 1612 556 563 536 1641 538 529 560 533 556 563 537 1641 538 1614 564 5183 4404 4342 555 1622 557 562 537 1641 537 1640 538 529 560 533 556 1648 531 538 562 555 534 1618 560 559 530 562 537 1615 563 1640 538 529 560 1646 532 532 557 562 537 1640 538 1639 539 1639 539 1638 530 1648 530 1649 540 1611 557 1646 532 561 538 554 535 558 531 562 537 555 534 561 538 552 537 1641 537 555 534 1644 534 1643 536 1617 562 557 532 563 536 1639 539 553 536 1642 537 556 533 560 539 554 535 1642 537 1643 535\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562\n#\n# Model: Sharp AH-X9VEW. Doesn't have heat function\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 307 125509 3831 1867 489 482 491 1392 461 468 485 1397 456 489 464 1411 463 466 487 1397 456 488 465 1390 463 482 481 1402 462 1402 483 471 461 1412 462 468 485 1400 464 1401 484 1397 488 1395 458 471 461 485 457 1408 487 1393 460 485 457 473 459 486 456 474 489 1394 459 471 461 485 457 473 459 488 465 466 455 490 463 467 465 480 462 468 464 482 460 470 483 1399 465 464 457 488 465 465 488 1393 460 484 458 471 461 485 457 1415 459 1405 511 408 482 489 464 466 487 1377 456 489 464 481 461 469 463 482 460 470 462 483 459 470 462 484 458 472 460 485 457 473 459 487 455 474 489 1395 458 471 461 485 457 472 460 486 456 473 459 486 456 474 458 487 455 474 458 488 465 466 487 1396 457 487 434 496 457 488 433 496 457 489 432 497 466 479 432 498 465 480 431 499 464 482 439 490 463 482 460 1394 491 1389 485 1395 490 1392 461 468 464 482 460 469 484 1399 465 1399 465 480 462 483 491 77962 300\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 345 2685 478 1170 176 29680 3832 1891 465 481 482 1400 464 465 488 1395 458 470 483 1396 457 487 466 1406 458 471 492 1389 464 481 461 1411 463 1408 456 489 464 1407 457 473 490 1392 482 1383 481 1399 486 1396 458 462 459 497 456 1409 486 1394 459 459 483 473 459 487 455 474 489 1392 461 468 464 481 461 469 463 483 459 471 482 1398 487 1379 464 481 461 485 457 1416 458 487 466 1407 457 472 460 460 482 474 489 1393 460 469 463 483 459 471 461 485 457 1408 456 488 465 481 461 469 484 1381 462 483 459 486 456 473 459 487 455 474 458 487 455 475 457 489 464 467 465 481 461 1410 485 1380 484 1397 488 1392 461 483 459 471 461 485 457 473 459 486 456 474 458 488 454 476 456 490 463 467 465 481 461 1411 464 481 440 491 462 484 437 492 461 485 436 494 459 487 434 495 458 488 433 497 456 489 432 498 486 450 482 1408 467 1389 485 1394 480 1384 459 486 456 489 464 466 487 1393 460 485 457 1415 459 1406 510\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 181 11813 3819 1899 457 488 486 1421 433 472 491 1416 438 481 461 1417 437 482 492 1416 437 467 486 1419 435 485 457 1439 435 1403 461 483 480 1408 435 484 490 1416 438 1401 484 1395 490 1392 461 467 465 480 462 1427 458 1396 457 486 456 473 459 486 456 472 481 1426 438 466 466 480 462 467 465 480 462 467 465 479 463 1426 438 481 461 484 458 1438 436 1409 486 1378 465 480 462 483 459 471 482 1425 428 475 457 487 455 474 458 487 455 1434 430 489 464 481 461 468 485 1404 439 480 462 483 459 470 462 483 459 469 463 482 460 470 462 483 459 470 462 483 459 1437 458 1381 483 1395 490 1390 463 481 461 469 463 482 460 468 464 481 461 468 464 481 461 469 463 482 460 469 463 481 461 1434 430 474 458 486 456 473 459 486 456 473 459 486 456 473 459 486 456 473 459 485 457 472 460 485 489 449 462 1434 461 1378 486 1393 481 1397 519 400 479 476 456 489 464 1431 433 486 435 495 458 487 487\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3826 1866 490 481 482 1382 461 484 490 1392 461 467 486 1396 457 471 482 1400 464 479 463 1390 463 481 482 1399 465 1398 518 401 510 1379 464 480 483 1398 456 1408 487 1392 482 1399 517 386 483 487 466 1399 486 1392 461 483 459 470 462 482 460 469 484 1397 456 473 459 459 483 472 460 484 458 471 461 483 459 1411 463 480 462 467 486 1377 487 1392 482 1396 457 486 456 472 460 486 456 473 480 1400 464 465 456 488 454 475 488 1393 460 468 464 480 462 467 486 1395 458 470 462 483 459 485 436 493 460 469 463 481 461 486 435 500 463 463 458 486 456 1397 488 1390 484 1394 480 1400 464 465 456 488 465 464 457 487 455 474 458 487 455 473 459 485 457 472 460 485 457 472 481 1399 517 401 458 497 456 473 459 485 457 487 434 495 458 471 461 484 458 488 433 502 430 507 435 500 432 498 455 1409 486 1392 482 1399 454 1409 455 488 465 480 462 467 465 480 462 1408 456 473 459 486 488\n#\n# Model: Electrolux ESV09CRO-B21. Doesn't have heat function\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3092 3057 3092 4438 579 1675 545 534 576 1650 571 535 575 531 569 1656 575 1652 579 527 573 533 577 1648 572 1655 576 1651 580 526 573 532 578 1647 573 532 578 1647 573 1654 577 529 571 535 575 530 570 535 575 529 571 534 576 529 571 534 576 528 571 534 576 528 572 533 577 528 572 533 577 527 573 1651 580 526 573 532 578 527 572 532 568 537 573 531 568 536 574 1650 571 535 575 531 569 536 574 530 569 535 575 529 571 534 576 529 571 533 577 528 572 533 577 527 572 532 568 536 574 531 569 1655 576 530 570 535 575 530 570 535 575 529 571 534 576 528 572 533 577 527 573 532 578 527 572 531 569 536 574 531 569 535 575 530 570 534 576 529 571 534 576 528 571 533 577 527 573 532 567 537 573 531 569 536 574 530 570 535 575 529 571 534 576 528 571 533 577 527 572 532 578 526 573 531 569 536 574 530 570 535 575 529 571 534 576 528 572 533 577 1646 574 532 578 1646 574 1652 579 527 572 533 577 1647 573 1653 578 1675 545 534 576 1649 571\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3093 3058 3090 4441 576 1652 579 528 571 1654 577 531 579 526 573 1652 579 1649 582 525 574 1652 579 528 571 1654 577 1651 580 527 572 533 577 528 571 533 577 1649 582 1646 574 1653 578 529 581 525 574 530 580 525 574 530 580 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 532 578 1647 573 533 577 1648 572 535 575 530 569 535 575 530 580 525 574 531 579 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 1651 580 527 572 533 577 528 571 533 577 528 571 533 577 528 571 533 577 528 571 534 576 528 571 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 1649 582 525 574 1650 581 1647 573 1654 577 1651 580 1647 573 1654 577 531 579 1646 574 1653 578\n#\n# Model: Daikin FTE35KV1. Doesn't have heat function\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5045 2158 335 1768 358 690 357 723 335 716 331 1771 355 694 364 686 361 720 327 723 335 1767 359 690 357 1775 362 1770 356 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 690 357 1776 361 687 360 690 357 693 365 716 331 719 328 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 1773 364 1767 360 689 358 692 366 685 362 688 359 721 326 724 334 717 330 720 327 723 335 715 332 718 329 721 326 1777 360 1771 355 1776 361 1770 356 692 366 715 332 718 329 721 326 29460 5042 2161 332 1770 356 692 366 685 362 688 359 1774 363 685 362 688 359 721 326 694 364 1769 357 691 367 1767 360 1771 355 693 365 1769 358 1773 364 1768 359 1772 365 1766 361 688 359 691 367 1767 360 689 358 692 366 684 363 717 330 720 327 693 365 686 361 689 358 722 336 684 363 1770 357 1774 363 686 361 689 358 1774 363 686 361 1771 356 1776 361 1770 357 692 366 685 362 688 359 690 357 1776 361 687 360 690 357 1776 361 688 359 691 356 694 364 716 331 689 358 1775 362 686 361 689 358 692 366 685 362 688 359 691 356 724 334 716 331 689 358 722 336 685 362 688 359 721 326 693 365 716 331 689 358 692 366 684 363 718 329 690 357 693 365 716 331 689 358 722 336 1767 360 689 358 1774 363 686 361 1771 356 693 365 686 361 689 358 722 336 684 363 717 330 720 327 1776 361 687 360 690 357 693 365 716 331 1771 355 693 365 686 361 1772 355 1776 361 688 359 1773 364 1768 359\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5038 2165 328 1772 365 686 361 689 358 692 366 1765 362 719 328 692 366 684 363 717 330 1771 366 684 363 1768 359 1774 363 686 361 1771 355 1776 361 1770 357 1775 362 1769 358 691 356 694 364 1767 360 691 356 694 364 686 361 689 358 692 366 685 362 1769 358 1775 362 1769 358 1774 363 1768 359 690 357 1776 361 1770 357 692 366 684 363 687 360 690 357 693 365 686 361 689 358 692 366 684 363 687 360 690 357 693 365 1766 361 1773 364 1767 360 1772 355 694 364 686 361 689 358 692 366 25151 319 3980 5041 2131 362 1769 358 693 365 686 361 689 358 1772 365 686 361 689 358 692 366 684 363 1768 359 692 366 1765 361 1772 354 694 364 1769 358 1774 363 1768 359 1773 364 1767 359 719 328 692 366 1796 331 719 328 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1768 359 1775 362 686 361 689 358 1773 364 686 361 1770 357 1777 360 1771 355 693 365 686 361 689 358 1772 365 1769 358 690 357 694 364 1767 360 691 356 694 364 686 361 689 358 723 335 1766 361 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 1771 366 684 363 687 360 690 357 693 365 1767 360 690 357 1804 333 687 360 690 357 693 365 686 361 689 358 692 366 685 362 1768 359 692 366 685 362 688 359 690 357 1774 363 688 359 691 356 1774 363 1770 356 1775 362 1769 358 691 356\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5043 2132 361 1770 356 723 335 715 332 718 329 1774 363 715 332 719 328 722 336 714 333 1770 356 722 336 1767 360 1772 354 724 334 1769 357 1774 363 1768 358 1773 364 1767 359 720 327 723 335 1768 359 720 327 723 335 716 331 719 328 722 336 714 333 1770 356 1774 363 1769 357 1773 364 1767 360 720 327 1775 362 1769 357 721 326 725 333 717 330 720 327 723 335 716 331 719 328 722 336 714 333 717 330 720 327 723 335 1768 359 1773 364 1767 360 1772 354 724 334 717 330 720 327 723 335 29451 5041 2134 359 1772 354 724 334 717 330 720 327 1775 362 717 330 720 327 723 335 715 332 1771 355 723 335 1768 358 1773 364 715 332 1770 357 1775 362 1769 357 1774 363 1768 359 720 327 723 335 1768 359 720 327 724 334 716 331 719 328 722 336 715 332 718 329 720 327 723 335 716 331 1771 355 1776 361 718 329 721 326 1776 361 718 329 1773 364 1767 360 720 327 723 335 715 332 718 329 1774 363 1768 359 720 327 723 335 1768 358 721 326 724 334 716 331 719 328 722 336 1767 360 719 328 722 336 715 332 718 329 721 326 724 334 717 330 720 327 723 335 715 332 719 328 722 325 725 333 717 330 720 327 723 335 716 331 719 328 1774 363 716 331 1771 355 1776 361 718 329 721 326 724 334 717 330 1772 365 714 333 1770 356 722 336 715 332 718 329 721 326 724 334 717 330 719 328 1775 362 717 330 720 327 723 335 715 332 718 329 1774 363 715 332 718 329 721 326 725 333 717 330 1772 365\n#\n# Model: Dyson Purifier Hot+Cool\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2309 665 781 672 803 672 803 695 832 643 833 1355 804 694 836 640 781 695 804 1409 778 697 777 702 797 678 798 677 799 678 799 701 803 674 802 1412 801 674 801 674 801 674 802 674 801 51317 2284 670 775 1413 802 51252 2283 670 801 1412 775 51275 2258 673 798 1414 802 51248 2284 670 802 1412 774 51246 2259 695 775 1413 801\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2316 610 806 671 781 695 806 695 781 695 782 1405 782 694 808 694 780 693 808 1381 779 697 802 1412 776 1438 800 1437 776 1438 775 700 775 1412 776 700 801 701 775 700 776 1438 776 700 776 51695 2258 695 776 1437 776 51248 2258 672 798 1439 776 51240 2258 670 801 1436 776\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2342 612 781 695 805 668 810 666 810 665 811 1402 811 666 811 692 781 670 781 1432 781 696 778 1436 802 1412 802 1412 802 1413 801 1412 802 1412 801 1412 777 1411 776 1463 776 1412 800 1414 801 51041 2257 697 802 1411 777 51240 2283 671 776 1437 801 51209 2255 672 799 1412 801\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2317 637 832 644 830 669 805 670 805 672 803 1411 803 673 802 674 802 673 803 1411 803 674 801 1411 802 675 775 1415 800 701 774 1440 801 1412 775 702 799 1414 774 1413 801 701 801 675 800 51681 2257 695 803 1411 801 51226 2283 671 799 1412 803 51246 2257 696 803 1411 775 51255 2282 668 803 1410 802 51243 2258 695 802 1387 798\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2315 618 853 643 832 644 834 641 833 643 833 1356 805 695 835 640 808 667 809 1404 808 668 806 1409 803 674 801 1412 802 1388 799 677 799 701 775 701 801 1389 799 677 799 676 800 1439 802 51426 2283 671 800 1412 802 51251 2258 697 801 1387 800 51248 2283 669 802 1411 802 51230 2258 696 799 1387 801 51225 2283 670 801 1411 801 51200 2280 695 775 1411 802 51227 2258 696 802 1411 775 51204 2281 669 801 1411 800\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802\n#\n# Model: Daikin FTXM20M\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 500 393 473 392 473 368 498 368 497 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1263 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1268 464 1269 463 1269 463 1292 440 1269 463 404 462 426 439 1293 439 426 440 426 440 426 440 426 440 426 440 427 439 426 440 427 439 427 438 427 439 1294 438 427 439 1294 438 427 439 427 439 428 438 1294 438 1294 438 428 438 428 437 428 438 428 438 1294 438 428 438 428 438 429 437 429 437 429 436 429 437 429 437 429 437 429 436 429 437 430 436 1296 436 1296 436 1296 436 430 436 430 435 1297 435 1297 435 1298 434 35482 3500 1699 464 1268 464 402 463 402 463 403 463 1269 463 426 440 426 439 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1294 438 427 438 427 439 1294 438 427 439 428 438 428 438 428 438 427 438 428 438 428 437 428 438 428 437 428 438 428 438 1295 437 429 437 429 437 429 436 429 437 1296 436 429 437 429 436 429 437 430 436 430 436 430 435 430 436 430 435 431 435 431 435 431 435 455 410 456 409 1322 410 456 409 456 410 456 410 456 410 456 410 1323 409 457 409 457 409 1323 409 1323 409 457 409 35483 3500 1699 464 1268 464 402 464 402 463 403 463 1269 463 426 440 426 440 426 440 426 439 1293 439 426 439 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1294 438 1293 438 427 439 427 438 1294 438 428 438 427 438 428 438 428 438 428 438 428 437 428 438 428 438 428 438 428 438 428 438 428 438 429 437 429 437 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 430 436 1297 435 430 436 430 436 431 435 431 435 456 410 456 410 456 410 456 409 1323 409 1322 410 456 410 456 409 457 409 457 409 457 409 457 409 457 408 457 409 1324 408 1324 408 1324 408 1324 408 458 408 1325 406 459 407 1351 356 1376 356 1376 356 1376 356 1377 355 510 355 510 356 511 355 511 354 511 355 537 328 537 329 538 328 538 327 538 328 539 327 565 300 566 300 1432 300 1433 298 593 272 594 271 621 245 621 244 622 243\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 500 368 498 369 497 367 499 366 499 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1264 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1269 463 1269 463 1269 463 1292 440 1292 440 426 440 426 440 1293 439 426 440 426 439 426 440 426 440 427 439 427 438 427 439 427 439 427 439 427 439 1293 439 427 439 1294 438 427 439 427 439 428 437 1294 438 1294 438 428 437 428 438 428 437 429 437 1295 437 428 437 429 437 429 437 429 436 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 1297 435 1297 435 431 435 455 410 1298 434 1322 410 1322 410 35482 3500 1700 464 1269 463 403 463 403 463 403 462 1292 440 426 440 426 440 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1293 439 427 439 427 439 1294 438 427 439 427 439 427 439 428 438 427 438 428 438 428 438 428 437 428 438 428 438 428 438 1295 437 429 437 429 436 429 437 429 437 1296 436 429 437 429 437 429 437 430 436 430 436 430 436 431 435 431 435 431 435 455 410 456 410 456 410 456 410 1322 410 456 410 456 410 456 409 457 409 456 409 1323 409 457 409 457 409 1323 409 1324 408 458 408 35483 3500 1700 464 1268 464 402 464 403 463 402 464 1292 440 426 440 426 440 426 439 426 440 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 438 1293 439 1293 439 427 439 427 438 1294 438 428 438 428 438 427 438 428 438 428 438 428 438 428 438 428 438 428 438 428 437 428 438 429 436 429 437 429 437 429 437 429 437 429 437 429 437 1296 436 430 435 430 436 1297 435 431 435 1297 435 431 435 456 409 456 410 456 409 456 410 456 410 456 410 456 410 1323 409 1323 409 457 409 457 409 457 408 457 409 457 409 458 408 458 407 458 408 1325 407 1326 405 1327 406 1351 381 485 356 1376 356 510 355 1377 355 1377 355 1377 355 1378 354 1378 354 512 354 512 354 538 327 538 328 538 328 539 326 565 300 566 300 566 299 567 299 593 272 621 244 596 270 1488 244\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 504 389 477 364 501 364 502 364 501 364 502 25048 3511 1684 505 1228 504 389 475 390 474 392 472 1260 470 396 469 396 470 396 469 397 469 1263 469 397 469 1263 469 1263 469 397 468 1263 469 1263 469 1264 468 1263 469 1264 468 397 468 398 468 1264 468 398 468 398 468 398 468 398 468 398 468 399 467 398 468 399 467 423 443 400 466 1289 443 423 443 1289 443 423 443 423 443 423 443 1289 443 1290 442 424 442 423 442 423 443 424 442 1290 442 424 442 423 443 424 442 424 441 424 442 424 441 424 442 424 442 424 441 424 442 424 442 1291 441 1291 441 1291 441 425 441 425 441 1291 441 1291 441 1291 441 35478 3505 1695 468 1264 468 398 467 398 468 398 468 1265 467 398 467 398 468 398 468 399 467 1265 467 399 467 1266 466 1266 466 423 443 1289 443 1290 442 1290 442 1290 442 1290 442 424 442 423 442 1290 442 423 443 424 442 424 442 424 442 424 441 424 442 424 442 424 441 424 442 424 442 424 442 1291 441 425 441 425 441 424 442 424 441 1291 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 440 426 440 426 440 1292 440 426 440 426 440 426 440 426 439 427 439 1293 439 427 439 427 438 1294 438 1294 438 428 437 35479 3504 1695 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 466 399 467 1265 467 399 467 1289 443 1290 442 423 443 1290 442 1290 442 1290 442 1290 442 1290 442 423 443 424 442 1290 442 424 442 424 442 424 441 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1291 441 425 441 425 441 1292 440 1292 440 1292 440 426 440 425 441 426 440 426 440 1292 440 426 440 426 440 1292 440 426 440 426 440 426 440 427 439 426 440 426 440 427 438 427 439 427 439 427 439 1294 438 1295 437 1294 438 1319 413 453 413 1319 413 453 412 1320 412 1319 413 1319 413 1319 413 1320 412 453 412 453 413 453 412 454 412 454 411 454 412 454 412 454 412 454 412 454 412 455 411 454 412 455 411 1321 411 1321 411 456 409 456 410 481 384 482 384 482 383 482 359 506 360 507 359 507 359 507 358 1374 358 1374 358 508 357 508 358 509 357 534 332 534 332 534 332 534 332 535 331 535 331 535 331 535 330 536 330 536 330 562 303 563 303 563 302 564 302 1430 302 565 301 564 302 591 274 619 247 619 247 1512 219 1539 190\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 502 390 476 365 501 365 500 365 500 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 469 397 468 398 468 398 468 398 468 1264 468 398 468 1265 467 1265 467 399 467 1265 467 1265 467 1265 467 1266 466 1265 467 400 466 401 465 1266 466 401 465 401 465 401 465 424 442 424 441 424 442 424 441 424 442 424 442 424 442 1291 441 424 442 1291 441 424 442 425 440 425 441 1291 441 1291 441 425 441 425 441 425 440 425 441 1292 440 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 1293 439 1293 439 426 439 427 439 1293 439 1293 439 1293 439 35480 3503 1696 467 1265 467 399 467 398 468 399 466 1266 466 399 467 399 467 399 467 399 467 1266 466 400 466 1267 465 1290 442 424 442 1290 442 1290 442 1290 442 1290 441 1291 441 424 442 424 442 1291 441 424 442 424 442 425 441 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 440 426 440 1292 440 426 440 426 439 426 440 426 439 426 440 426 440 426 440 426 440 426 440 427 438 427 439 427 439 427 439 1293 439 427 438 427 439 428 438 428 438 428 438 1294 438 428 438 428 438 1295 437 1320 412 453 413 35480 3503 1696 467 1265 467 399 467 399 467 399 467 1266 466 399 467 399 467 400 466 400 466 1290 442 424 442 1289 443 1290 442 424 442 1291 441 1290 442 1290 442 1291 441 1291 441 424 442 424 442 1291 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 427 439 426 439 427 439 1293 439 1293 439 1293 439 427 438 1294 438 427 439 427 438 428 438 427 438 453 413 429 437 429 437 429 437 453 412 454 412 1320 412 1320 412 1320 412 1320 412 454 412 1321 411 454 412 1320 412 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 411 455 411 456 410 456 410 457 409 481 384 457 409 482 383 483 383 483 358 1374 358 1374 358 508 357 508 358 509 357 509 357 509 357 509 357 510 356 535 330 536 330 536 330 1402 330 1403 329 537 329 536 329 563 302 564 302 564 302 565 300 565 300 592 273 619 246 620 246 619 246 620 245 674 188\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 504 365 500 365 500 365 500 365 501 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 470 397 468 397 469 398 468 398 468 1264 468 398 467 1265 467 1265 467 398 468 1265 467 1265 467 1265 467 1266 466 1266 466 399 466 399 467 1266 466 400 466 400 466 400 465 400 466 424 442 424 442 424 442 424 441 424 442 424 442 1291 441 424 442 1291 441 424 442 424 442 425 441 1291 441 1291 441 425 440 425 441 425 441 425 440 1292 440 425 441 425 440 425 441 425 441 425 441 425 441 425 441 426 440 426 440 426 440 426 439 1293 439 1293 439 1293 439 426 440 427 439 1293 439 1293 439 1293 439 35480 3503 1696 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 467 399 467 1267 465 400 466 1266 466 1267 465 424 441 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 442 1291 441 424 442 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 441 425 441 1292 440 426 440 426 439 426 440 426 440 426 439 426 440 426 440 426 439 427 439 426 440 427 439 427 439 427 438 1293 439 427 439 427 439 427 439 428 438 428 438 1295 437 428 438 429 436 1296 436 1296 436 453 412 35481 3502 1696 467 1265 467 399 466 399 467 399 467 1265 467 399 467 399 467 399 466 400 466 1266 466 400 466 1290 442 1267 465 424 442 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 441 1291 441 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 425 441 425 441 425 441 426 440 1292 440 426 440 426 440 1292 440 426 440 426 439 1293 439 427 439 427 439 427 439 1293 439 1294 438 1294 438 1293 439 427 439 428 438 428 438 428 438 429 436 429 437 429 437 453 412 453 413 453 412 1320 412 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1322 410 455 411 455 411 456 410 456 410 455 410 456 410 456 409 481 384 458 408 482 383 482 384 483 358 508 358 1374 358 1374 358 508 357 509 357 509 357 510 355 535 330 536 330 536 330 536 329 536 330 536 330 1403 329 1430 301 564 302 564 302 564 301 591 274 566 301 592 273 619 247 619 247 620 245 647 219\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189\n#\n# Model: Mitsubishi SRK63HE\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3232 1526 463 334 462 1127 464 334 460 334 461 1128 463 336 459 1128 464 359 436 359 436 1133 458 1156 434 1157 434 362 433 1159 432 363 432 1159 432 1159 432 1159 432 363 433 363 432 363 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 363 432 364 432 1160 431 363 432 363 432 1160 432 363 432 364 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 432 364 432 364 431 364 431 1160 432 364 432 364 431 364 431 1160 431 1160 431 1160 431 364 431 1161 430 1161 430 1160 431 1161 430 364 432 364 431 365 431 1161 430 364 431 365 431 364 431 365 430 365 431 1161 430 1161 430 1161 430 365 430 1161 430 365 430 365 430 1161 430 365 430 365 431 365 430 1161 430 365 430 1161 430 1161 430\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3229 1528 461 335 461 1128 463 334 461 335 460 1129 463 337 458 1129 463 359 436 360 434 1156 434 1157 433 1159 432 364 431 1161 430 364 431 1161 430 1161 430 1160 431 364 431 364 431 365 430 365 430 1161 430 1161 430 365 430 1161 430 1161 431 365 430 365 431 1161 430 365 430 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 1161 430 1161 430 1161 430 1161 430 1161 431 1161 430 365 430 1161 430 1161 430 1161 431 365 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 1162 429 1162 429 1162 430 365 430 1162 429 1162 429 1162 429 1162 429 366 430 366 429 366 430 1162 429 366 429 366 430 366 429 366 429 1162 429 366 429 1163 428 366 429 1162 429 366 429 366 429 1162 429 366 430 1162 429 366 429 1163 429 366 430 1163 428 1163 428 367 428\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3230 1526 463 335 460 1128 464 333 461 335 460 1128 463 337 458 1128 463 359 436 359 436 1155 435 1157 433 1158 432 363 432 1160 431 364 431 1161 430 1160 431 1160 431 364 431 364 431 364 431 364 431 1161 430 1161 430 364 431 1161 430 1160 431 365 430 364 431 1161 431 364 431 364 431 1161 430 365 430 365 431 1161 430 1161 431 364 431 1161 430 1161 430 1161 430 1161 431 1161 430 1161 431 365 430 1161 431 1161 430 1162 430 365 430 365 430 365 431 365 430 1161 430 365 430 365 430 365 431 1161 430 1162 430 1162 429 365 430 1162 429 1162 429 1162 429 1162 429 366 429 366 430 366 430 1162 429 366 430 366 429 366 429 366 430 366 429 1162 429 1163 429 366 429 366 429 1163 428 1163 428 1163 429 1163 428 366 429 366 430 1163 428 1163 428 367 428 367 429 366 429\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3231 1526 463 334 461 1127 465 333 461 334 460 1128 463 336 459 1129 463 359 436 359 436 1133 458 1156 434 1157 433 362 433 1159 432 363 432 1159 432 1160 431 1159 433 363 432 363 432 363 432 363 432 1159 433 1159 432 363 432 1159 432 1160 432 363 432 363 432 1159 432 363 432 363 433 1159 432 364 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 1160 431 1160 432 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 364 431 364 431 364 432 364 431 1160 432 364 431 364 431 364 432 1160 431 1161 430 1161 430 364 431 1161 431 1161 430 1161 430 1161 430 364 432 364 431 364 431 1161 430 365 430 365 430 365 430 365 431 365 430 1161 430 1161 431 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 365 430 1162 430 365 431 1161 430 1162 429 365 430\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3230 1526 463 334 461 1128 464 333 461 334 461 1128 463 336 459 1129 463 359 436 359 436 1155 435 1156 434 1158 433 363 432 1159 432 363 432 1159 432 1160 431 1160 431 363 433 363 432 363 432 363 432 1160 432 1160 431 364 431 1160 431 1160 432 364 431 364 431 1160 431 364 431 364 432 1160 431 364 431 364 431 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 432 1160 431 1160 431 364 432 364 431 364 431 364 431 1161 430 364 431 364 432 364 431 1161 430 1161 430 1161 430 365 430 1161 431 1161 430 1161 430 1161 430 365 430 365 430 365 430 1161 430 365 431 365 431 365 430 365 431 1161 430 1161 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 365 430 1162 429 1162 430 1162 429 366 429 1162 429 1162 429\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430\n#\n# Model: Airwell Prime DCI Series\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3078 3852 2004 888 1054 1824 1045 894 1051 865 2062 861 1078 860 1080 865 1046 894 1015 1883 1945 899 1013 901 1013 901 1012 902 1012 900 1042 927 986 902 1012 927 987 901 1012 902 1012 927 986 903 1011 902 1012 931 1011 931 1011 904 1010 933 1009 928 985 928 986 1885 1944 927 3017 3943 1943 927 985 1915 984 929 985 929 1943 957 984 929 985 928 985 929 985 1886 1943 899 984 930 983 930 984 957 986 929 985 929 985 930 984 930 983 930 984 930 1013 930 984 959 983 931 982 931 983 930 984 930 984 930 984 960 1011 931 984 1918 1939 929 3016 3917 1940 930 982 1916 954 959 954 959 1913 957 955 933 981 959 928 1015 954 1916 1913 959 953 960 957 986 927 1015 928 1015 953 961 927 987 927 986 927 987 927 986 927 1016 927 987 927 1016 926 1044 928 987 926 1015 928 988 926 987 926 988 926 1946 1883 987 3974\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3060 3870 1026 888 1984 886 1026 888 1053 888 1026 887 1055 858 1055 858 1052 860 1024 891 1044 1854 1016 897 1975 1853 1975 925 1015 898 1015 898 1016 897 1016 898 1015 898 1016 898 1015 898 1015 898 1015 899 1015 899 1014 899 1014 899 1016 927 1014 900 1014 899 1014 1856 1974 926 3048 3883 1015 898 1975 897 1014 899 1015 899 1014 900 1014 899 1015 900 1013 900 1014 899 1014 1857 1014 900 1973 1855 1973 899 1012 901 1013 902 1011 926 987 927 987 926 987 927 987 955 987 926 988 926 987 927 1015 927 987 927 987 926 988 926 1016 927 1015 1884 1945 925 3020 3911 986 928 1946 925 986 927 986 928 986 928 986 927 987 927 987 928 986 928 986 1884 987 956 1946 1882 1946 925 986 928 986 927 986 928 985 928 986 928 986 928 986 928 985 928 985 929 959 954 984 930 984 987 931 955 959 956 958 955 959 1968 1891 980 3982\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3054 3879 1977 892 1020 1846 1083 838 1073 840 2031 859 1051 864 1047 864 1077 896 1014 927 986 928 986 928 986 1885 1943 927 985 929 1013 928 986 929 985 928 985 928 985 928 985 929 985 929 985 957 985 928 1015 928 1015 928 985 929 984 929 985 929 985 1886 1943 927 3017 3914 1943 928 984 1886 985 958 984 929 1972 957 984 958 984 929 984 930 984 929 984 930 984 930 983 1887 1942 929 983 930 984 930 984 930 983 959 984 930 984 930 983 930 984 930 984 930 984 930 983 931 983 931 983 959 984 931 983 931 983 1888 1941 930 3014 3943 1914 931 981 1915 955 959 955 959 1913 958 955 959 954 959 955 959 955 959 955 988 955 960 953 1917 1941 959 953 960 953 961 953 961 953 961 927 987 927 987 927 987 926 987 927 1017 926 988 926 988 925 988 925 988 926 988 925 1018 925 1972 1857 1014 3946\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3080 3850 2007 863 1049 1851 1048 888 1054 887 2012 886 1026 887 1025 888 1050 865 1045 1854 2029 871 1041 872 1040 873 1042 899 1043 899 1043 872 1015 898 1016 899 1041 872 1042 872 1015 926 1017 898 1016 927 1016 926 1016 899 1014 898 1015 926 1016 899 1015 898 1015 1856 1973 897 3048 3910 1947 898 1015 1884 1015 925 988 899 2003 953 988 900 1014 954 988 926 987 1857 1972 896 988 899 1014 900 1014 900 1013 927 987 929 1014 929 1013 926 987 927 986 927 987 927 987 927 987 927 986 927 987 927 986 985 958 928 986 927 986 927 1012 1860 1943 927 3018 3914 1943 955 986 1914 986 929 984 928 1944 955 957 930 984 957 985 956 958 1913 1915 956 957 957 956 957 931 1012 957 930 983 958 955 931 982 958 956 960 983 958 956 958 955 958 930 984 930 984 930 984 930 984 930 1013 930 984 930 985 928 1942 1887 983 3977\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3083 3873 2012 1843 2013 1873 1054 888 2011 887 1025 887 1025 887 1052 890 1048 1852 1017 896 1018 895 1018 896 1976 895 1017 898 1016 898 1015 898 1016 897 1016 897 1016 897 1017 897 1016 897 1017 900 1043 899 1014 897 1016 897 1016 898 1016 898 1015 898 1015 1857 1972 896 3048 3911 1947 1853 1975 1854 1015 898 1973 897 1016 927 1015 926 987 926 1016 1857 1014 926 987 899 1015 901 1970 926 987 927 987 926 988 955 988 1013 958 900 1014 926 987 900 1014 900 1014 900 1013 927 986 927 987 927 1016 955 987 927 987 955 987 1884 1946 928 3045 3912 1974 1883 1974 1883 987 927 1974 926 986 927 987 928 987 956 987 1942 986 956 986 928 986 928 1944 926 986 928 985 929 984 929 985 928 986 956 987 928 986 958 984 929 984 930 984 930 983 929 985 958 984 930 984 957 957 956 957 1887 1971 956 4003\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3108 3851 2062 1793 2006 1821 1103 839 2031 859 1085 829 1081 833 1079 836 1045 1911 1973 897 1015 898 1016 899 1041 871 1016 899 1014 898 1015 899 1015 899 1014 899 1041 872 1015 899 1041 872 1041 872 1015 899 1015 899 1041 873 1014 899 1041 873 1014 899 1014 1883 1975 900 3045 3886 1997 1856 1945 1857 1012 927 1945 900 1013 901 1012 901 1013 901 1012 1859 1999 901 1012 930 1012 903 1011 903 1010 903 1011 902 1012 960 1011 928 986 932 1010 903 1011 928 1015 928 985 929 985 928 1014 928 985 929 985 929 984 929 985 928 986 1915 1971 928 3017 3915 1942 1885 1943 1885 985 930 1971 929 984 930 983 930 984 930 984 1887 1942 929 983 960 983 931 982 931 983 932 981 958 985 958 956 958 984 959 954 931 983 932 981 959 955 932 982 959 954 960 982 961 983 933 955 988 955 985 929 1943 1915 958 4003\n#\n# Model: Danby DAC060EB7WDB\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4402 4442 527 1629 529 549 529 1628 530 550 529 548 531 549 530 550 529 1630 528 549 530 549 529 549 530 1630 527 1629 529 1630 528 548 530 550 529 547 531 1628 530 1629 529 1628 530 1628 529 1628 530 1628 530 549 530 1628 530 1628 530 1627 531 1628 530 1627 531 1631 527 1629 529 1628 530 1627 531 1628 530 1628 530 1629 529 1627 531 1627 531 1628 529 1627 531 1627 531 1629 529 1627 530 549 529 549 530 549 530 1628 529 1627 530 5234 4401 4440 530 550 528 1628 529 549 530 1628 529 1629 528 1626 531 1628 529 550 529 1628 530 1627 531 1629 529 549 529 548 530 549 530 1628 529 1628 530 1627 530 547 532 547 532 547 531 548 531 548 530 548 531 1626 531 549 530 547 531 547 531 548 530 548 530 549 530 548 531 546 532 578 500 547 532 548 531 548 531 548 530 548 531 548 530 548 531 547 531 547 532 547 531 1627 531 1627 530 1626 532 547 531 547 532\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4454 4387 586 1573 585 494 584 1571 586 494 584 493 585 524 554 493 531 1626 586 1573 585 494 585 492 586 492 586 494 585 493 585 493 586 1571 586 493 585 1572 531 1627 530 549 584 494 586 1571 587 1572 529 1628 529 1628 530 1627 530 1626 532 1627 531 1626 532 1628 529 1627 531 1626 531 1627 531 1627 587 1569 532 1625 533 1626 532 1626 532 1627 531 1627 530 548 531 1626 532 1627 531 548 531 1627 531 548 531 546 532 547 531 5233 4401 4443 530 548 530 1627 530 547 531 1627 531 1626 531 1626 531 1627 530 549 530 548 530 1627 531 1627 531 1627 530 1627 531 1627 531 1626 531 548 530 1627 530 547 532 547 532 1627 531 1627 532 546 531 547 531 548 530 548 530 548 531 548 531 548 530 548 530 549 530 548 531 548 531 548 530 547 532 549 529 548 530 548 531 547 532 548 530 548 531 1627 530 548 530 548 531 1626 533 546 531 1627 530 1627 532 1626 530\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4492 4354 619 1537 619 459 621 1537 622 455 623 456 623 457 622 456 623 1535 622 1536 622 458 621 456 623 1536 621 1535 623 456 622 456 623 457 621 456 531 1627 621 1536 622 456 622 457 622 455 623 458 621 458 621 1537 621 1536 620 1539 618 1538 531 1628 531 1627 530 1628 530 1628 530 1627 531 1628 530 1628 530 1628 531 1627 530 1629 529 1628 529 1628 530 549 530 1627 531 1628 530 1628 530 1627 586 494 530 1627 530 549 530 5232 4400 4443 586 492 587 1571 587 493 585 1572 530 1628 586 1572 586 1572 586 492 587 493 586 1572 586 1571 586 492 587 493 585 1572 531 1627 585 1573 585 1572 585 492 586 494 585 1572 586 1571 531 1627 531 1628 530 1627 587 491 531 548 587 492 530 548 530 547 532 548 531 547 532 547 531 548 531 547 532 547 531 548 588 491 530 547 589 490 531 547 532 1626 532 548 531 548 531 547 532 547 532 1627 531 548 531 1626 532\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4401 4441 528 1629 529 550 528 1628 529 551 528 550 528 551 527 551 528 1629 529 1629 529 550 529 1630 528 551 528 549 530 550 529 551 528 549 529 550 529 1629 529 1628 530 549 530 1629 529 550 529 1628 529 1629 529 1631 527 1628 530 1628 529 1629 528 1628 530 1629 529 1629 529 1629 529 1629 528 1629 529 1629 529 1630 528 1629 529 1629 529 1628 529 1629 528 551 528 1629 529 550 529 550 530 548 529 1631 527 551 528 1629 529 5235 4402 4439 530 550 528 1629 529 549 530 1628 529 1629 529 1628 530 1629 529 549 530 550 529 1628 530 552 526 1628 529 1628 530 1628 530 1627 531 1628 529 1629 528 551 528 550 529 1628 530 550 528 1628 529 549 529 550 528 550 529 549 530 548 530 551 528 550 528 578 500 550 529 550 529 551 527 549 530 549 529 549 529 550 528 548 530 550 528 549 529 1629 528 550 529 1630 528 1628 530 1628 530 549 530 1628 529 549 529\n#\n# Model: Carrier 42QHB12D8S\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4467 4363 599 1556 599 478 599 1556 599 1558 597 505 572 505 572 1583 571 505 572 506 624 1531 653 423 651 426 624 1530 622 1532 572 505 572 1583 571 506 571 1584 571 1583 571 1584 571 1584 571 507 570 1585 570 1585 570 1585 570 508 569 508 569 508 592 485 570 1585 593 484 570 508 569 1586 569 1586 592 1563 593 485 569 508 592 485 592 485 570 508 569 508 570 508 569 508 569 1586 569 1586 569 1586 569 1586 569 1586 569 5187 4461 4371 568 1586 569 508 593 1562 593 1563 569 509 591 486 591 1563 569 508 592 486 592 1563 592 485 592 486 591 1563 592 1563 591 486 592 1564 591 486 592 1563 593 1563 591 1563 592 1564 592 485 592 1563 592 1563 592 1564 591 486 591 486 592 485 592 485 592 1563 592 486 591 486 592 1564 591 1563 592 1563 592 486 592 485 592 486 591 486 592 485 592 486 591 486 591 486 591 1563 592 1563 593 1563 591 1564 591 1563 591\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4440 4390 571 1583 572 505 572 1583 596 1559 571 505 572 505 572 1583 596 481 596 481 597 1559 625 451 654 423 625 1529 595 1560 596 480 572 1583 572 505 596 482 594 483 571 1583 571 1584 571 1584 571 1584 571 1585 569 1585 593 1562 594 1561 593 485 569 508 593 484 592 485 593 484 592 485 592 1563 569 508 592 1562 592 485 570 1586 592 485 592 486 569 1586 592 485 569 1585 570 508 569 1586 569 508 569 1585 570 1586 569 5186 4438 4393 569 1585 593 485 592 1562 569 1585 569 508 569 508 569 1585 591 486 593 484 591 1563 591 486 591 486 590 1564 569 1585 569 508 593 1562 592 486 592 485 569 508 591 1563 592 1562 569 1586 569 1586 591 1563 592 1563 590 1564 591 1563 592 486 569 508 569 508 569 508 569 508 592 486 592 1563 568 508 593 1563 591 486 592 1563 569 508 592 486 592 1563 591 486 592 1563 592 485 592 1563 592 486 592 1563 591 1563 592\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4465 4365 598 1557 598 479 598 1557 598 1557 598 479 598 479 598 1556 599 479 598 507 593 1562 652 424 624 452 595 1559 595 1560 594 483 571 1584 594 1561 593 484 593 1562 593 1562 593 1562 593 1562 593 1562 593 1563 592 485 592 1562 593 484 593 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1562 592 1563 592 1562 593 1562 592 1563 592 1563 592 1562 592 1563 592 5165 4439 4394 569 1586 592 485 569 1586 569 1586 569 508 569 508 593 1562 592 485 570 508 592 1563 592 485 594 484 569 1586 569 1586 569 508 569 1586 592 1563 569 508 592 1563 569 1586 592 1563 592 1563 569 1586 569 1585 569 508 569 1586 569 508 569 508 569 508 569 508 592 485 569 508 592 485 569 508 593 485 569 508 569 508 569 508 593 484 570 508 569 1586 593 1562 570 1586 569 1586 592 1563 569 1586 592 1563 592 1563 592\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4465 4364 599 1556 599 479 598 1556 599 1556 599 478 599 479 598 1559 596 505 572 506 571 1584 653 424 624 452 571 1583 572 1583 571 506 571 1583 571 1584 571 506 571 1584 571 1585 570 1585 570 1585 592 1563 592 1562 593 485 592 1563 592 485 593 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 5164 4460 4372 592 1563 592 485 593 1563 592 1563 592 486 591 486 591 1563 592 485 592 485 592 1563 592 485 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 1563 592 1564 591 486 591 1563 591 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 486 591 485 592 486 591 485 592 1563 592 485 592 1563 592 485 592 1564 591 1563 592 1563 592 1563 592\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4440 4391 572 1583 572 505 572 1584 571 1583 572 505 572 505 572 1583 596 481 573 505 596 1559 626 451 655 422 625 1529 596 1559 572 505 572 1583 572 1582 572 505 572 1583 572 1583 571 1584 571 1584 571 1585 570 1584 594 484 593 1562 592 485 592 485 592 485 593 485 593 484 593 484 593 1562 593 484 593 1562 593 1563 592 1562 592 1563 592 485 593 484 592 485 593 1562 593 484 593 485 592 485 592 485 592 1562 593 1563 592 5163 4462 4370 592 1563 592 485 592 1563 592 1563 592 485 592 485 592 1563 592 485 592 485 593 1562 593 485 593 485 592 1563 592 1563 592 485 592 1562 593 1563 592 484 593 1563 592 1563 592 1563 592 1563 592 1563 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 485 592 485 592 485 592 1563 591 485 592 486 591 485 592 485 593 1563 591 1563 593\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591\n#\n# Model: Mitsubishi MSZ-AP25VGK\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495\n#\n# Model: Hitachi RAK-50PEB\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486\n#\n# Model: LG PC07SQR\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3169 9836 535 1553 511 551 491 543 491 544 490 1586 490 532 510 530 511 543 491 1579 489 1577 490 543 490 543 491 543 491 544 490 544 489 544 490 543 491 544 490 550 492 544 490 543 491 1586 489 542 492 1583 492 552 490 532 510 537 512 1585 491\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3385 9888 512 1544 512 544 489 544 489 544 490 1586 489 545 489 544 489 545 489 531 511 541 508 533 508 542 507 544 489 546 487 544 490 1587 489 1573 510 543 490 543 491 1578 490 545 489 1574 509 545 488 1587 489 1573 510 1586 490 1579 489 1568 507\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3288 9816 598 1504 574 472 569 463 571 463 571 1515 568 472 569 461 573 471 571 459 590 462 571 463 571 447 594 462 572 462 571 464 570 459 590 463 570 464 570 1496 571 1497 570 463 571 1514 569 464 570 1504 572 1514 569 471 571 473 568 462 572\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3205 9865 616 1473 536 518 516 517 517 517 517 1575 517 527 568 464 515 517 571 463 570 462 572 462 572 469 573 461 573 463 570 462 572 469 573 1504 572 451 590 451 591 472 569 463 571 1494 573 461 573 1497 570 1505 570 1503 572 471 571 1491 592\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3363 9846 596 1495 514 518 515 517 517 515 518 1552 516 502 539 517 517 516 518 516 518 517 517 517 571 462 517 518 516 1554 568 461 589 462 572 1505 571 1507 568 1514 514 1568 570 461 573 1504 572 472 570 1507 592 1490 593 460 574 462 572 447 594\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3204 9889 537 1587 491 529 512 544 489 545 489 1573 510 530 511 543 491 552 490 538 511 543 491 543 491 532 509 543 491 1587 489 537 512 543 491 1577 490 543 491 543 491 544 489 543 491 1586 489 544 490 1587 489 539 510 543 491 543 491 1586 490\n#\n# Model: Daikin FTXC35DV1B\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 530 315 532 314 532 314 532 314 532 314 506 340 561 24780 3568 1647 507 1213 535 313 534 340 506 340 506 1212 507 339 507 339 506 339 506 339 506 1214 505 341 504 1215 505 1216 504 343 503 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 345 501 344 502 345 501 1219 501 1219 501 345 501 344 502 344 502 345 501 344 502 345 501 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34807 3563 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1219 501 344 502 1218 502 1219 501 344 502 1219 501 1218 502 1219 501 1219 501 1219 501 345 501 344 502 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1220 500 346 500 345 501 346 500 346 500 34806 3564 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 345 501 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 346 500 346 500 346 500 346 500 346 500 1220 499 346 500 1220 499 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 1221 499 1221 499 1221 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1245 475 1222 498 1245 475 371 475 348 498 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 371 475 1246 474 371 475 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 1246 473 372 474 372 474 1246 474 372 474\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 536 339 507 311 535 312 534 312 534 312 534 339 507 24807 3710 1507 594 1156 563 305 543 304 542 304 570 1126 592 304 515 304 542 304 542 304 542 1155 564 304 541 1158 505 1215 505 342 504 1217 503 1217 503 1218 502 1218 502 1218 502 344 502 343 503 1218 502 343 503 343 503 344 502 344 502 344 502 344 502 344 502 343 503 343 503 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 1218 502 1218 501 344 502 1218 502 344 502 1218 502 1218 502 34800 3564 1651 503 1217 503 343 503 344 502 343 503 1217 503 343 503 343 503 344 502 344 502 1218 502 344 502 1217 503 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 344 502 1218 502 1218 502 344 502 1218 502 1219 501 1219 501 345 501 344 502 344 502 344 502 344 502 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1650 503 1216 503 343 503 343 503 344 502 1217 503 343 503 344 502 343 503 343 503 1217 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 344 502 1218 501 344 502 344 502 344 502 345 501 344 502 345 501 344 502 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 346 500 345 501 345 501 346 500 370 476 346 500 346 500 346 500 370 476 346 500 346 500 346 500 346 500 1221 499 1244 476 1244 476 370 476 346 500 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 370 476 1245 475 370 476 371 475 370 476 1245 475 1245 475 371 475 371 475 370 476 370 476 370 476 370 476 370 476 371 475 370 476 371 475 371 475 371 475 371 475 370 476 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 371 475 1245 475 371 475\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 507 340 559 306 487 341 530 315 531 313 561 305 541 24776 3569 1646 508 1212 537 340 506 340 506 340 506 1213 507 339 507 339 506 339 506 340 505 1214 505 341 504 1215 504 1216 503 343 503 1217 502 1218 502 1218 502 1218 502 1218 501 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34800 3563 1652 502 1217 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 501 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 345 501 345 501 344 502 345 501 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 500 1219 500 1219 500 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1220 500 346 500 346 500 346 500 346 500 34808 3564 1652 503 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 345 501 344 502 1218 502 345 501 1218 502 1219 501 344 502 1219 501 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 500 345 501 345 501 1219 500 1219 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 345 501 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 347 499 346 500 1221 499 1245 475 1221 499 347 499 347 499 347 499 348 498 347 499 348 498 348 498 371 475 348 498 1222 498 1245 475 1245 475 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 1245 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 372 474 372 474 372 474 1247 473 1247 473\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 533 311 535 312 534 312 534 313 533 310 536 312 534 24804 3705 1509 593 1129 590 305 542 305 542 305 569 1127 590 304 514 305 541 304 542 305 541 1155 564 305 540 1157 561 1159 504 341 504 1217 503 1217 503 1217 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 1218 502 1218 502 1218 502 344 502 1219 501 345 501 1219 501 1219 501 34806 3565 1650 503 1217 502 343 503 343 503 343 503 1217 503 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 344 502 1218 502 344 502 1219 501 1218 501 345 501 1218 501 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1651 503 1217 502 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 1219 501 1218 502 345 501 344 502 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 1221 499 1221 498 346 500 346 500 370 476 346 500 347 499 347 499 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 371 475 370 476 370 476 370 476 371 475 370 476 370 476 370 476 370 476 1244 476 371 475 1245 475 370 476 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 1245 475 1245 475 371 475 371 475 1246 474 1245 475\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 535 313 533 311 535 311 535 312 534 312 534 313 533 24805 3711 1506 592 1129 592 305 543 305 540 305 569 1126 592 304 514 304 542 304 541 304 485 1213 507 340 506 1214 506 1215 505 342 504 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 503 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 503 344 502 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 502 1219 502 34818 3566 1651 503 1217 503 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 503 1218 502 344 502 1218 503 1218 502 344 502 1218 502 1218 503 1218 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 345 501 344 502 344 502 1219 501 345 501 345 502 1219 501 345 501 1219 502 1219 502 1219 502 344 502 1219 501 1219 502 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 34812 3566 1651 503 1217 503 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 344 502 345 501 345 501 1219 502 345 501 345 501 1219 502 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 345 501 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 347 499 347 499 370 476 1244 476 1221 499 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1245 475 1245 476 1245 475 371 475 370 476 370 476 371 475 371 475 370 476 370 476 371 475 371 476 370 476 370 476 371 475 371 475 371 475 371 475 371 475 1245 475 371 475 1245 475 371 475 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 1245 475 1246 474\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 535 314 532 314 533 313 533 312 534 313 533 312 508 24840 3569 1647 508 1213 536 342 505 342 504 341 505 1214 506 340 506 340 506 340 505 340 506 1214 506 341 504 1216 504 1217 503 343 503 1218 502 1219 501 1219 502 1219 502 1219 502 345 502 344 502 1219 502 345 501 345 502 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 1219 502 345 501 1219 501 345 502 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 500 1219 501 1220 500 345 501 1220 500 345 501 1220 501 1220 500 34815 3564 1653 502 1218 502 344 502 345 501 344 502 1219 501 345 501 345 501 345 501 345 501 1219 502 345 501 1219 502 1220 501 345 502 1219 501 1219 502 1219 501 1219 502 1219 502 345 501 345 501 1220 501 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 502 345 501 345 501 1220 501 345 501 345 501 345 501 345 501 1220 500 345 501 346 501 1220 500 1220 501 345 501 345 501 346 500 1220 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 345 501 346 500 345 501 1220 500 346 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 34816 3565 1653 502 1219 501 344 502 345 501 345 501 1219 502 345 501 345 502 345 501 345 501 1219 501 345 501 1219 502 1219 501 345 501 1219 502 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 501 346 501 345 501 1220 501 346 500 346 500 1220 500 346 500 345 501 346 500 1220 500 1220 500 1220 501 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 347 499 346 500 346 500 346 500 347 499 347 499 346 500 346 500 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1222 498 1222 499 1222 498 347 499 348 498 348 498 347 499 371 475 348 498 348 498 348 498 371 475 1222 498 1246 474 1246 474 372 475 371 475 372 474 372 474 348 498 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1247 473 372 474 1246 474 1246 474 1246 474\n#\n# Model: AUX YKR-H/006E\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565\n#\n# Model: Carrier 42QG5A580SC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487\n#\n# Model: Samsung DB93\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 667 17837 3089 8903 555 445 578 1411 583 442 554 442 529 468 528 468 554 443 554 442 554 444 552 1441 552 472 523 475 522 1473 522 1473 522 475 547 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 450 547 451 546 474 523 476 521 476 521 476 522 476 521 475 546 451 547 450 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 450 547 451 546 474 523 474 523 476 521 477 520 477 520 477 521 476 521 476 546 450 547 450 547 450 546 450 547 451 546 451 546 1448 546 1472 522 2982 3005 8963 522 1499 495 502 495 502 495 502 496 501 496 501 521 476 522 475 522 475 522 1472 522 475 522 475 522 1473 521 475 522 1473 522 1499 495 1500 494 1500 496 1499 521 1473 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 502 495 503 494 503 495 502 495 501 496 501 521 476 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 493 504 494 502 495 502 495 502 520 477 520 2984 3003 8966 519 1475 520 477 520 477 520 477 520 478 519 477 520 478 519 479 518 504 493 1502 493 504 493 503 494 503 494 1501 519 1476 518 1475 519 478 519 1476 518 1476 519 1477 517 1501 493 1503 491 1503 493 1502 493 1502 518 479 518 479 518 479 518 1476 518 1477 517 1477 517 504 493 504 493 504 493 531 466 507 490 1529 467 506 491 529 468 1527 492 1502 492 505 493 1502 492 1502 492 505 492 504 493 504 492 505 492 506 491 532 465 532 465 531 467 530 467 530 467 1528 467 1527 492\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 667 17829 3090 8902 556 444 579 1410 584 441 529 468 529 468 554 443 554 442 555 443 553 444 551 1442 551 474 522 475 522 1473 522 475 522 475 547 1448 547 1447 547 1447 547 1447 548 1447 547 449 548 450 547 474 523 474 523 476 521 476 521 476 521 477 521 475 522 476 546 450 547 449 548 449 548 450 547 449 548 450 547 450 547 450 547 451 546 474 523 474 523 474 523 479 518 479 518 479 518 501 496 501 496 477 545 475 522 452 545 474 523 474 523 1472 522 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 496 501 522 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 475 522 1473 521 1500 494 1500 495 1499 520 1474 522 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 478 519 478 519 478 519 479 518 503 494 505 492 505 492 1503 493 504 493 504 493 504 518 1477 518 1476 518 1476 518 479 518 1477 517 1477 517 1501 493 1504 490 1528 468 1527 468 1527 493 1501 493 504 493 504 493 504 493 1501 493 1502 492 1502 492 504 493 505 491 532 440 556 466 531 467 530 468 530 467 530 467 1527 492 1503 491 505 492 504 493 504 493 505 491 1503 492 505 467 530 492 506 466 557 464 533 464 532 466 1528 467 1528 467 1528 466 1528 491\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 667 17831 3089 8903 581 420 578 1411 583 442 529 468 528 468 554 443 554 442 554 443 553 445 551 1443 550 475 521 475 522 1473 522 475 547 449 548 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 449 548 450 547 473 524 476 521 475 522 476 520 476 522 475 522 475 547 449 549 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 476 521 476 521 476 520 477 521 476 546 451 547 450 547 450 547 449 548 450 547 1447 547 1448 546 1448 546 1472 523 2957 3029 8967 518 1477 517 502 495 501 496 501 521 475 522 475 522 475 522 475 522 475 522 1472 522 474 523 475 522 1473 521 475 522 1472 522 1499 495 1500 495 1499 520 1474 522 1473 521 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 502 495 502 495 503 494 502 496 501 496 501 521 476 522 476 521 475 522 475 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 2984 3003 8965 520 1474 521 477 520 477 520 477 520 477 520 477 520 478 519 504 493 504 493 1502 494 503 494 502 495 1500 520 1475 519 1475 519 1475 519 478 519 1475 519 1476 518 1501 493 1502 492 1503 493 1501 494 1501 518 1476 518 478 519 478 519 478 519 1476 518 1476 518 1477 517 504 493 506 491 506 491 506 491 506 492 505 492 504 493 504 518 481 516 1501 493 504 493 504 493 480 517 1501 493 504 493 504 493 504 493 505 491 532 466 531 466 532 466 1528 467 1527 468 1527 492 1502 492\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 667 17832 3088 8903 575 448 555 1411 583 441 529 468 529 468 554 443 554 442 554 443 553 444 551 1443 550 474 522 475 522 1473 522 475 522 475 547 1447 548 1446 548 1446 548 1446 548 1447 547 449 548 449 548 474 523 474 523 476 521 476 521 476 521 477 521 475 522 475 547 450 548 449 548 450 547 449 548 449 548 450 547 450 547 449 547 451 546 452 545 474 523 474 523 477 520 478 519 477 519 478 520 477 545 452 545 451 547 451 546 474 523 474 523 1450 544 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 521 476 521 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 476 521 1473 521 1500 494 1500 495 1499 495 1499 521 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 477 520 478 519 503 493 504 493 504 494 503 494 502 519 478 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 477 520 478 519 478 519 478 519 503 494 504 493 505 492 1503 493 504 493 504 493 503 519 478 519 1476 518 1476 518 478 519 1476 518 1477 517 1501 493 1504 490 1504 490 1503 493 1502 493 1502 517 480 517 504 493 480 517 1477 517 1502 492 1502 492 504 493 504 493 504 493 531 466 531 466 1529 467 1527 467 1527 492 504 493 1502 492 505 492 504 493 505 492 1503 492 505 492 505 492 505 467 557 464 532 441 556 466 532 466 1528 466 1528 491 1503 491 1503 466\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 665 17827 3090 8902 556 444 579 1412 583 441 530 467 529 468 554 442 555 442 554 442 554 444 551 1443 550 473 523 475 522 1472 521 475 523 474 523 1471 549 1446 548 1445 549 1446 548 1445 549 448 549 448 549 449 548 473 523 473 524 475 522 475 522 475 523 475 522 474 548 449 548 449 549 448 549 448 549 448 549 448 549 448 549 449 548 449 548 449 548 450 547 473 524 474 523 476 521 476 521 476 521 476 522 475 547 450 547 450 548 449 548 449 548 1447 547 1447 547 1447 547 1448 546 2957 3030 8962 522 1475 519 501 496 501 497 478 519 500 522 475 522 475 523 474 523 474 523 1472 522 474 523 474 523 1472 522 475 522 1472 522 1499 495 1499 496 1499 496 1498 522 1473 522 474 522 475 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 502 495 502 495 502 495 501 496 501 496 501 521 476 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 476 521 502 495 502 495 502 495 503 495 502 495 501 521 476 521 476 521 2983 3004 8964 521 1474 520 476 521 476 521 477 520 476 521 477 520 477 520 503 494 503 494 1501 494 502 495 502 495 502 520 477 521 1474 520 1474 520 477 520 1474 520 1475 519 1475 519 1500 494 1502 492 1502 493 1501 494 1500 519 478 519 477 520 477 520 1475 519 1475 519 1475 519 478 519 478 519 503 494 505 492 505 492 505 492 1503 493 1502 493 1502 517 1476 518 479 518 479 518 479 518 480 517 479 518 1501 493 504 493 504 493 530 467 531 466 531 467 1527 468 1527 491 1502 493 1501 493\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 668 17822 3064 8902 582 442 555 1413 581 442 528 468 529 494 528 469 502 495 527 470 526 471 525 1468 552 446 550 448 549 1446 548 448 549 448 549 1446 548 1448 546 1471 523 1473 521 1473 521 476 521 475 522 475 547 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 474 523 477 520 477 520 477 520 477 521 476 521 476 546 450 548 450 547 474 523 474 523 450 547 474 523 474 523 452 545 474 523 474 523 474 523 1500 494 1499 495 1499 496 1498 522 2955 3032 8963 521 1472 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 1499 495 502 495 502 495 1499 496 501 521 1473 522 1473 522 1472 522 1472 522 1473 521 1473 522 475 522 475 522 502 495 502 495 502 495 503 495 501 496 501 496 501 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 521 476 521 476 521 476 521 477 520 477 520 477 520 476 521 477 520 2984 3003 8967 518 1502 492 504 493 504 494 503 494 503 494 503 519 478 519 478 519 478 519 1476 518 478 518 479 518 478 519 478 519 1501 493 1501 493 506 491 1504 491 1527 468 1503 492 1526 493 1501 493 1501 494 1501 493 1501 493 504 493 504 493 504 493 1528 466 1529 466 1528 467 529 468 529 493 504 493 504 493 504 493 1502 492 1502 492 1502 467 529 493 1502 491 533 465 532 465 532 465 531 467 530 467 1528 467 530 467 530 467 530 467 530 467 530 467 1527 467 1528 466 1528 466 1529 492\n#\n# Model: Samsung AR-EH04\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 663 17769 3057 8901 529 492 527 1434 556 465 529 464 529 464 555 439 555 438 556 438 554 440 553 1435 552 444 549 470 524 1465 524 1466 522 473 522 1467 522 1466 549 1440 549 1440 548 1440 548 445 549 445 549 446 548 447 547 471 523 470 524 471 523 472 522 472 521 473 522 472 522 472 523 472 548 446 549 445 548 446 548 447 547 447 547 446 548 447 547 470 524 471 523 471 523 471 523 471 523 498 496 475 519 498 496 497 498 497 522 472 523 471 524 470 523 471 523 1443 546 1442 547 2947 3023 8935 522 1466 522 471 523 472 522 474 520 498 496 498 496 498 497 497 497 497 523 1466 523 471 523 471 523 1466 523 471 523 1466 522 1467 522 1467 522 1493 495 1493 496 1493 497 497 522 472 523 471 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 499 495 499 495 499 495 499 495 499 496 498 522 472 523 472 523 471 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 473 521 499 495 500 494 499 495 499 496 499 521 2947 3024 8937 521 1467 521 473 521 473 521 472 521 473 521 473 521 473 521 473 521 474 520 1469 520 500 494 500 494 1495 495 500 495 499 520 474 521 1468 520 1468 521 1468 521 1468 521 1468 521 1469 519 1470 518 1495 493 1496 493 500 495 499 520 474 521 1468 520 1469 520 1469 520 473 521 474 520 474 520 475 519 476 518 500 494 502 492 502 491 1497 493 1495 495 499 495 499 520 474 520 475 519 475 519 475 519 475 519 475 519 500 494 501 493 501 493 501 493 501 493 1498 491 1497 519\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 665 17760 3083 8875 553 468 527 1432 584 438 555 439 529 464 555 439 555 438 556 438 555 439 553 1435 552 443 550 470 524 1466 523 471 522 473 521 1467 523 1466 549 1439 549 1440 549 1439 549 445 549 445 549 445 549 446 548 470 524 470 524 471 523 472 522 472 522 473 521 472 522 472 547 447 548 445 549 445 549 445 549 445 549 446 548 445 549 445 549 447 547 470 524 471 523 471 523 471 523 473 521 473 521 473 521 473 521 472 523 472 548 446 548 1441 547 1441 548 1441 547 1441 547 2947 3023 8935 522 1466 522 472 522 474 519 475 519 475 519 474 521 473 546 448 547 448 546 1465 523 449 545 448 546 1466 522 471 523 1467 522 1466 522 1493 495 1493 495 1493 496 1493 522 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 499 495 498 495 499 496 498 496 498 496 498 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 500 494 500 495 499 495 499 495 498 522 2947 3023 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 495 500 494 1494 520 1468 521 1467 521 473 521 1468 521 1468 520 1469 519 1469 519 1471 517 1495 494 1495 494 1494 520 474 521 473 521 473 520 1468 521 1468 521 1468 520 474 520 475 519 475 519 500 493 500 494 501 493 501 493 501 493 1495 495 1494 520 474 520 474 520 474 520 475 519 1470 518 475 519 476 518 500 493 501 493 501 493 501 493 1497 492 1497 492 1496 493 1495 518\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 666 17765 3056 8901 529 492 526 1434 556 465 529 464 530 464 555 438 556 438 555 439 554 440 552 1436 551 443 550 470 524 1465 524 471 523 472 522 1467 521 1467 548 1441 549 1440 548 1440 548 445 549 445 549 445 549 446 548 447 547 470 524 471 523 471 523 473 521 472 522 473 521 473 521 473 522 472 548 446 549 446 548 446 548 447 547 447 547 470 524 447 547 471 523 471 523 471 523 471 523 471 523 475 519 498 496 498 496 498 496 497 498 497 523 1466 524 1443 545 1441 548 1442 546 2947 3023 8935 522 1466 522 472 522 472 522 498 496 498 496 498 496 498 496 498 522 472 523 1466 522 471 523 471 523 1466 523 471 523 1466 523 1467 522 1467 521 1493 496 1493 495 1493 497 497 522 472 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 496 498 522 472 522 472 523 472 521 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 474 520 500 494 500 494 500 495 500 495 499 521 2947 3024 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1469 520 500 494 500 493 1496 494 1494 495 1494 520 1468 520 473 521 1469 519 1468 521 1469 519 1470 519 1470 519 1495 493 1496 493 1495 495 499 520 474 520 474 520 1469 520 1469 519 1469 519 474 520 475 519 500 494 500 494 500 494 502 492 502 492 502 493 501 493 1496 494 500 519 475 520 475 518 1470 519 475 518 476 518 475 519 476 494 525 493 501 493 501 493 1498 491 1498 491 1497 493 1496 518\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 664 17763 3082 8874 553 444 552 1433 583 438 529 464 530 464 555 439 555 438 556 438 555 440 553 1435 552 443 550 470 524 1466 523 472 521 472 522 1467 523 1466 549 1439 549 1439 549 1439 550 444 550 445 549 445 549 447 547 470 524 471 523 471 523 472 522 472 522 473 521 472 523 472 522 471 549 446 549 445 549 446 548 446 548 446 548 446 548 446 548 448 546 471 523 471 523 471 523 471 523 475 519 497 497 474 520 475 520 497 497 496 524 471 524 1465 523 1442 547 1442 547 1442 547 2946 3024 8935 522 1466 522 471 523 474 520 474 520 498 496 474 521 497 522 471 523 471 523 1465 523 471 523 471 523 1466 522 471 523 1466 523 1466 523 1493 495 1493 495 1493 496 1492 523 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 496 498 497 498 497 497 523 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 499 495 499 495 499 496 499 496 498 496 498 522 2946 3024 8937 521 1467 521 472 522 472 522 473 521 473 521 473 521 473 521 474 520 474 520 1495 493 500 494 499 495 499 495 499 496 1493 521 1468 520 473 521 1468 520 1468 521 1468 520 1469 519 1470 518 1495 494 1495 494 1494 495 499 520 473 521 473 521 1468 520 1469 520 1468 521 474 520 474 520 475 519 475 519 476 518 1496 492 1496 494 1495 495 499 520 1469 520 474 520 474 520 474 519 1470 520 474 520 476 518 476 519 477 517 500 494 500 494 503 491 1497 492 1496 493 1495 519 1470 519\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 666 17758 3056 8901 528 493 526 1434 580 441 554 439 529 465 554 439 555 439 555 439 554 440 553 1435 552 443 549 470 524 1465 524 472 522 472 521 1467 523 1467 548 1440 549 1440 548 1440 549 445 549 445 549 446 548 446 548 471 523 471 523 471 523 471 523 473 521 473 521 473 521 473 522 472 547 446 549 446 549 445 548 446 548 446 548 446 548 446 548 447 547 471 523 471 523 471 523 471 523 473 521 474 520 473 521 474 521 473 522 473 546 447 548 1441 548 1442 547 1442 547 1442 546 2948 3021 8936 521 1466 522 472 522 498 496 499 494 476 519 476 519 497 497 497 523 472 522 1466 523 471 523 472 522 1466 522 472 522 1466 522 1467 522 1467 522 1493 494 1495 495 1493 522 472 522 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 499 495 499 495 499 496 498 521 473 522 472 523 472 522 472 522 472 522 473 521 472 522 472 522 472 522 473 521 473 521 474 520 473 521 499 495 500 494 500 495 499 495 499 521 2947 3023 8938 520 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 494 500 495 500 494 1494 520 1468 521 474 520 1468 520 1469 520 1468 520 1469 520 1470 518 1496 493 1496 493 1495 495 499 519 475 520 474 520 1468 520 1469 519 1469 519 474 520 475 519 475 519 476 518 500 494 500 494 1497 492 1497 492 1496 493 1495 519 475 519 475 519 475 519 475 519 475 519 1471 518 476 518 500 494 501 493 501 493 501 493 1498 491 1498 491 1496 518 1472 517\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 664 17757 3057 8901 528 469 550 1434 556 465 553 440 529 465 554 439 555 439 555 438 555 440 553 1435 552 443 550 470 524 1466 522 472 522 472 521 1467 523 1466 549 1440 549 1440 548 1440 548 445 549 445 549 445 549 446 548 470 524 471 523 471 523 471 523 472 522 472 522 473 522 472 523 472 548 446 549 445 550 445 548 446 548 446 548 446 548 446 548 447 547 470 524 471 523 471 523 471 523 471 523 474 519 474 521 474 521 473 522 473 546 447 547 1441 548 1442 546 1442 547 1442 546 2947 3023 8935 522 1466 522 472 522 498 496 498 495 499 496 498 496 498 521 473 522 471 523 1466 522 471 523 471 522 1466 523 471 523 1467 522 1467 522 1467 521 1493 495 1494 496 1493 521 473 522 472 522 471 523 472 522 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 522 473 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 499 495 500 494 500 495 499 496 498 522 2947 3023 8937 521 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1470 518 500 494 500 494 500 494 500 494 1494 521 1468 521 473 521 1468 520 1468 520 1469 520 1469 520 1470 518 1495 493 1496 493 1495 494 500 519 475 520 474 520 1469 519 1469 520 1469 519 474 520 474 520 475 519 477 517 500 494 1496 493 1496 493 1496 493 500 495 1495 519 475 518 475 519 475 518 476 518 475 519 1470 519 500 494 500 494 501 493 501 493 501 493 1499 490 1497 493 1497 492 1496 518\n#\n# Model: Fujitsu ASYG24KMTB\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3302 1639 405 423 407 420 410 1234 405 421 409 1210 440 387 432 420 410 417 413 1231 408 1238 412 388 431 422 408 392 438 1233 406 1238 412 389 430 396 434 419 411 390 440 413 406 394 436 391 439 388 431 421 409 417 413 414 405 421 409 392 438 1233 406 421 409 417 413 414 405 395 435 418 412 388 432 421 409 1236 414 387 432 420 410 390 440 388 431 1213 437 1208 431 1239 411 1234 405 1213 437 1208 431 1214 436 1235 415 386 433 420 410 1234 405 422 408 418 412 389 431 396 434 1211 439 388 432 422 408 418 412 1233 406 1238 412 415 404 422 408 1237 413 388 432 422 408 1236 414 1205 434 1210 440 1205 434 392 438 390 440 1231 408 392 438 415 404 396 434 392 438 415 404 422 408 419 411 416 414 412 407 394 436 417 413 414 405 421 409 417 413 1232 407 419 411 390 440 1204 435 418 412 415 415 412 407 419 411 1208 431 421 409 418 412 388 432 421 409 392 438 415 404 395 435 1211 439 388 432 1213 437 416 414 386 433 1212 438 415 404 396 434 392 438 416 414 413 406 420 410 417 413 1231 409 418 412 389 430 1240 410 391 439 1205 434 420 410 390 440 387 432 420 410 417 413\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3293 1649 405 396 434 419 411 1207 432 395 435 1236 414 413 406 394 436 417 413 1232 407 1212 438 415 404 396 434 419 411 1234 405 1239 411 417 413 414 405 421 409 419 411 389 430 423 407 393 437 416 414 387 432 394 436 417 413 388 431 421 409 1236 414 387 432 421 409 418 412 414 405 422 408 418 412 415 404 1240 410 391 439 414 405 421 409 419 411 1234 405 1239 411 1208 431 1239 411 1235 404 1214 436 1210 440 1205 434 419 411 416 414 1204 435 418 412 415 404 396 434 393 437 1235 404 396 434 420 410 416 414 1231 408 1210 440 387 432 421 409 1235 415 414 405 395 435 418 412 1232 407 420 410 1208 431 422 408 1237 413 415 404 396 434 419 411 416 414 413 406 420 410 417 413 414 405 421 409 418 412 415 404 422 408 419 411 416 414 413 406 1238 412 415 404 422 408 1237 413 414 405 421 409 418 412 416 414 1231 408 418 412 415 404 422 408 419 411 416 414 413 406 420 410 1235 404 423 407 420 410 1234 405 422 408 1210 440 414 405 421 409 392 438 415 404 422 408 420 410 417 413 1231 408 419 411 416 414 413 406 1237 413 415 404 1239 411 417 413 1232 407 420 410 417 413\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3302 1614 440 414 405 421 409 1235 414 413 406 1238 411 415 404 422 408 419 411 1234 405 1240 409 418 412 415 404 422 408 1237 412 1232 407 420 410 417 413 414 405 422 408 419 411 416 414 413 406 420 410 417 413 414 405 421 409 418 412 415 404 1239 410 418 412 415 404 422 408 419 411 416 414 413 406 420 410 1234 405 422 408 419 411 416 414 414 405 1239 411 1207 432 1239 410 1235 414 1230 409 1236 413 1232 407 1237 412 415 415 412 407 1237 412 414 405 422 408 419 411 416 414 1231 408 419 411 416 414 413 406 1238 411 1206 433 421 409 418 412 1206 433 421 409 418 412 1206 433 1238 412 1207 432 1213 436 390 440 1232 407 420 410 417 413 414 405 421 409 418 412 415 404 422 408 419 411 416 414 413 406 421 409 418 412 415 404 422 408 419 411 1233 406 421 409 418 412 1232 407 420 410 418 412 389 430 422 408 1210 440 414 405 395 435 418 412 415 404 423 407 420 410 416 414 414 405 422 408 418 412 415 404 1240 409 1235 414 413 406 420 410 417 413 414 405 422 408 419 411 416 414 1231 408 418 412 415 404 1240 409 1209 440 387 432 1212 437 1234 405 1214 435 1209 440 1204 435\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3301 1615 439 415 404 422 408 1210 439 414 405 1239 410 416 414 414 405 395 435 1209 440 1205 434 419 411 417 413 414 405 1212 437 1207 432 422 408 419 411 416 414 414 405 421 409 418 412 415 404 422 408 393 437 416 414 413 406 420 410 417 413 1205 434 420 410 417 413 414 405 421 409 418 412 415 404 422 408 1210 439 414 405 422 408 419 411 417 413 1205 434 1236 413 1206 433 1211 438 1207 432 1212 437 1209 440 1204 435 418 412 415 415 1204 435 418 412 415 404 422 408 419 411 1208 441 412 407 420 410 417 413 1205 434 1237 412 414 405 422 408 1210 439 415 404 396 434 419 411 1207 432 1213 436 417 413 1205 434 420 410 417 413 1231 408 419 411 416 414 413 406 421 409 418 412 414 405 422 408 419 411 415 404 424 406 421 409 418 412 415 404 1239 410 417 413 414 405 1239 410 416 414 413 406 422 408 419 411 1233 406 421 409 418 412 415 404 423 407 419 411 416 414 413 406 1238 411 416 414 413 406 421 409 1235 414 1230 409 418 412 415 415 412 407 420 410 417 413 414 405 422 408 1236 413 413 406 421 409 1235 414 1204 435 1210 439 1206 433 1212 437 1207 432 395 435 1236 413\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3295 1646 408 420 410 390 440 1205 434 393 437 1234 405 421 409 419 411 415 415 1230 409 1210 440 414 405 421 409 418 412 1233 406 1212 437 416 414 413 406 394 436 418 412 415 404 422 408 393 437 416 414 413 406 420 410 417 413 414 405 421 409 1236 413 414 405 421 409 418 412 415 404 423 407 419 411 416 414 1231 408 418 412 415 415 412 407 421 409 1235 414 1204 435 1209 441 1205 434 1210 440 1205 434 1212 437 1207 432 395 435 419 411 1233 406 421 409 418 412 415 404 422 408 1238 411 415 404 396 434 419 411 1234 405 1239 410 417 413 414 405 1213 436 417 413 414 405 1213 436 1235 404 1214 435 1209 441 413 406 421 409 418 412 1206 433 394 436 418 412 388 431 422 408 419 411 415 415 386 433 420 410 418 412 414 405 422 408 419 411 389 430 1241 408 418 412 415 404 1240 409 417 413 414 405 395 435 419 411 1233 406 395 435 418 412 415 404 422 408 419 411 416 414 413 406 421 409 1236 413 413 406 394 436 1235 414 1230 409 418 412 415 404 422 408 420 410 417 413 414 405 421 409 1236 413 413 406 420 410 417 413 1232 407 1237 412 416 414 1230 409 1236 413 1205 434 1210 439\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3302 1640 404 423 407 420 410 1212 437 390 440 1234 405 395 435 392 438 415 415 1207 432 1242 407 420 410 391 439 414 405 1243 406 1241 408 392 438 415 415 386 433 393 437 390 440 414 405 396 434 419 411 389 441 412 407 420 410 390 440 387 432 1242 407 393 437 390 440 414 405 395 435 392 438 389 430 396 434 1240 409 417 413 414 405 395 435 419 411 1237 412 389 430 396 434 393 437 416 414 387 432 394 436 1212 437 389 441 1234 405 1217 432 1241 408 1213 436 1212 437 1210 439\n#\n# Model: Toshiba RAS-2518D\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4349 4437 549 1615 551 1614 551 1614 551 1617 549 531 550 530 551 1615 550 531 550 532 549 530 551 531 550 530 551 1615 550 1614 551 531 550 1615 551 529 552 531 550 530 551 533 548 530 551 530 551 1616 549 1615 550 1616 550 1615 550 1614 551 1616 550 1615 551 1615 550 531 550 531 550 530 551 529 552 530 551 530 551 529 552 530 551 531 550 1615 550 532 549 1615 550 1616 550 531 550 531 550 530 551 530 551 529 552 532 549 530 551 530 551 531 550 529 552 531 550 1615 551 530 551 530 551 530 551 531 550 530 551 531 550 530 551 531 550 531 550 531 550 1616 550 1618 547 532 549 529 552 530 551 1615 551 1615 550 5379 4350 4436 550 1616 549 1615 551 1614 552 1615 550 529 552 530 551 1614 552 530 551 529 552 531 550 531 550 531 550 1614 552 1614 551 530 551 1615 550 530 551 530 551 530 551 530 551 531 550 532 549 1616 549 1615 551 1614 552 1615 550 1614 551 1616 550 1614 552 1615 550 529 552 530 551 530 551 530 551 531 550 531 550 530 551 530 551 531 550 1615 550 530 551 1615 550 1615 551 530 551 530 551 530 551 530 551 530 551 530 551 529 552 530 551 531 550 532 549 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 531 550 531 550 531 550 530 551 531 550 1615 551 1615 551 532 549 531 550 531 550 1616 549 1614 552\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4350 4438 549 1615 551 1614 552 1616 549 1616 550 530 551 531 550 1615 551 530 551 529 552 531 550 530 551 531 550 1614 551 1616 550 531 550 1616 549 530 551 531 550 530 551 529 552 530 551 531 550 1616 549 1616 550 1616 549 1616 550 1615 551 1614 551 1614 552 1615 551 530 551 531 550 530 551 531 550 531 550 529 552 532 549 531 550 530 551 1613 552 530 551 531 550 529 552 532 549 530 551 530 551 531 550 531 550 530 551 530 551 530 551 531 550 530 551 531 550 531 550 1615 551 529 552 530 551 530 551 530 551 530 551 530 551 530 551 530 551 532 549 531 550 531 550 532 549 531 550 531 550 530 551 530 551 5132 4351 4435 552 1616 550 1615 550 1615 551 1613 553 531 550 530 551 1615 550 530 551 531 550 531 550 530 551 532 549 1616 550 1616 549 530 551 1615 551 530 551 531 550 530 551 530 551 530 551 531 550 1615 551 1615 551 1614 551 1615 550 1615 551 1615 550 1615 550 1616 550 530 551 530 551 531 550 532 549 530 551 530 551 531 550 531 550 531 550 1615 550 530 551 530 551 530 551 529 552 531 550 530 551 531 550 531 550 530 551 530 551 531 550 530 551 530 551 530 551 531 550 1616 550 530 551 529 552 530 551 531 550 532 549 530 551 530 551 529 552 531 550 529 552 530 551 530 551 531 550 531 550 529 552 531 550\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4350 4436 550 1617 549 1615 550 1615 550 1617 548 530 551 531 550 1615 551 531 550 531 550 530 551 530 551 531 550 1614 552 1615 550 530 551 1614 551 531 550 531 550 531 550 529 552 532 549 530 551 1617 549 1616 549 1615 551 1619 547 1615 550 1615 550 1616 549 1616 550 530 551 531 550 530 551 530 551 531 550 530 551 529 552 529 552 530 551 1617 548 533 548 1615 551 1613 552 530 551 531 550 531 550 530 551 530 551 532 549 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 531 550 531 550 532 549 531 550 530 551 531 550 533 548 531 550 530 551 1617 548 1616 549 530 551 531 550 532 549 532 549 532 549 5200 4349 4436 550 1615 551 1615 551 1615 550 1616 550 531 550 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 1616 549 1615 551 530 551 1615 551 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 1616 550 1616 550 1615 550 1617 548 1616 549 1616 550 1615 550 531 550 530 551 531 550 531 550 532 549 530 551 531 550 531 550 532 549 1616 550 531 550 1616 550 1615 550 531 550 530 551 531 550 531 550 531 550 531 550 531 550 532 549 532 549 531 550 532 549 531 550 1616 550 531 550 530 551 532 549 532 549 530 551 532 549 531 550 532 549 531 550 1616 549 1617 549 531 550 530 551 531 550 532 549 532 549\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4350 4437 547 1618 548 1620 546 1620 546 1619 547 534 547 535 546 1619 547 534 547 536 545 536 545 535 546 535 546 1619 547 1620 545 534 523 1644 546 535 522 559 546 535 546 534 547 535 546 535 545 1620 546 1620 546 1620 546 1619 547 1619 546 1619 547 1620 545 1620 546 535 546 534 547 537 520 558 523 558 547 534 547 536 521 559 522 559 522 1644 546 535 546 535 522 560 545 536 521 559 522 559 522 558 523 559 522 560 521 559 522 559 522 560 521 559 522 561 520 1644 521 1645 520 559 522 559 522 559 522 559 522 559 522 560 521 560 521 560 521 561 520 559 522 560 521 559 522 559 522 559 522 1644 522 559 522 5341 4349 4439 520 1645 521 1645 521 1646 519 1645 521 560 521 561 520 1645 521 560 521 560 521 559 522 560 521 561 520 1646 520 1645 521 561 520 1645 521 561 520 560 521 560 521 560 521 560 521 561 520 1644 522 1644 522 1645 520 1645 521 1645 521 1645 520 1646 520 1644 522 561 520 560 521 560 521 561 520 560 521 561 520 561 520 561 520 560 521 1646 520 562 519 561 520 561 520 562 519 560 521 560 521 561 520 561 520 560 521 560 521 561 520 560 521 560 521 562 519 1646 520 1645 521 561 520 561 520 561 520 560 521 560 521 561 520 560 521 559 522 560 521 561 520 561 520 560 521 562 519 559 522 1645 521 561 520\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4348 4439 520 1646 520 1646 520 1646 519 1646 520 561 520 561 520 1646 519 561 520 561 520 562 519 562 519 561 520 1646 520 1647 518 563 518 1646 519 562 519 561 520 561 520 562 519 562 519 561 520 1648 517 1647 519 1646 519 1647 519 1646 520 1646 520 1645 520 1647 519 561 520 561 520 562 519 562 519 562 519 562 519 561 520 562 519 561 520 1646 520 562 519 1647 518 1646 520 562 519 560 521 561 520 561 520 561 520 562 519 562 519 560 521 562 519 562 519 560 521 1646 520 1646 520 561 520 562 519 561 520 562 519 561 520 561 520 561 520 561 520 561 520 1647 518 1646 520 562 519 562 519 561 520 1646 520 561 520 5409 4348 4440 519 1645 521 1646 519 1645 521 1645 521 561 520 561 520 1644 522 561 520 561 520 561 520 560 521 562 519 1646 520 1646 520 562 519 1644 522 561 520 561 520 561 520 561 520 561 520 561 520 1646 520 1645 520 1646 520 1645 521 1646 520 1646 520 1644 522 1645 521 560 521 560 521 561 520 561 520 560 521 560 521 561 520 561 520 561 520 1645 521 562 519 1645 521 1645 520 561 520 562 519 561 520 561 520 561 520 560 521 560 521 560 521 560 521 561 520 560 521 1646 520 1646 520 561 520 560 521 559 522 560 521 561 520 561 520 560 521 560 521 560 521 1646 520 1645 520 561 520 560 521 560 521 1645 521 561 520\n#\n# Model: Airwell AW-HKD012-N91\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4387 4398 547 1609 547 530 547 1610 547 1611 545 530 547 530 547 1608 547 530 548 530 547 1611 545 532 546 532 547 1609 547 1610 547 531 547 1608 548 530 547 530 548 530 547 1610 546 1609 547 1610 547 1609 547 1609 547 1611 545 1609 548 1610 546 530 547 530 548 529 549 531 547 531 546 531 547 1608 548 1610 547 1608 548 533 545 1608 548 532 546 532 546 1611 545 532 547 532 545 530 548 1608 547 530 549 1608 547 1609 548 5203 4386 4398 547 1609 546 530 547 1609 546 1607 548 531 547 531 547 1609 547 530 548 531 547 1609 547 531 547 531 547 1608 547 1613 544 531 546 1609 547 531 547 531 547 532 546 1609 547 1609 546 1609 547 1609 547 1608 547 1608 548 1608 548 1609 547 530 547 530 547 530 547 532 546 530 547 530 548 1610 546 1608 547 1609 547 530 547 1609 547 530 547 530 548 1609 546 530 548 530 547 532 546 1610 546 531 546 1608 548 1608 548\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4388 4398 547 1608 548 531 546 1610 546 1609 547 530 547 529 548 1608 548 532 547 530 548 1612 544 529 549 530 548 1608 547 1609 547 531 546 1608 548 1607 549 529 549 1608 549 1609 548 1608 548 1608 548 1611 545 1608 548 530 548 1609 547 531 547 530 548 530 548 531 547 529 549 530 548 530 547 531 547 530 548 530 547 529 549 530 548 532 547 530 548 1609 547 1610 547 1608 548 1609 547 1608 548 1608 548 1608 548 1608 548 5203 4388 4396 549 1609 547 529 549 1610 546 1608 548 529 549 530 547 1609 547 530 548 529 549 1608 548 531 547 532 546 1609 547 1609 547 530 548 1609 548 1609 548 529 548 1608 548 1609 548 1609 547 1609 547 1608 548 1609 547 532 546 1608 548 531 548 531 548 530 548 530 548 531 547 530 548 531 548 531 547 530 548 530 548 530 548 531 547 529 549 529 549 1609 548 1608 548 1609 547 1608 548 1608 548 1608 548 1607 549 1607 549\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4384 4400 572 1585 571 505 572 1583 573 1584 572 508 570 503 575 1584 572 505 572 506 572 1583 573 504 573 506 571 1586 570 1585 572 532 546 1586 570 1585 571 506 571 1585 571 1583 573 1586 570 1583 573 1584 572 1589 569 505 572 1585 571 506 571 506 573 506 572 505 573 532 545 504 574 509 570 1611 545 506 572 1582 574 506 572 507 571 507 571 507 570 1584 572 507 571 1587 569 506 572 1584 572 1585 571 1583 573 1612 544 5179 4386 4400 570 1584 572 507 571 1583 572 1585 571 506 572 506 572 1584 572 505 572 504 574 1584 572 507 571 504 574 1583 573 1585 572 507 571 1584 572 1610 545 508 571 1587 569 1583 573 1583 573 1585 571 1585 572 1585 572 505 572 1584 572 505 573 507 572 506 571 504 574 505 573 505 574 508 571 1585 571 507 571 1585 571 506 571 506 572 504 574 505 572 1586 570 507 571 1586 570 505 573 1584 572 1585 571 1587 569 1584 573\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4386 4398 575 1582 574 503 575 1583 573 1582 574 505 573 504 574 1582 574 506 572 508 570 1583 573 504 574 505 573 1583 573 1584 573 505 573 1582 575 1583 574 504 574 1582 574 1583 573 1583 573 1583 573 1585 571 1586 572 504 573 1584 572 504 573 505 573 505 573 505 573 504 573 506 571 1583 574 505 573 1583 573 1583 573 1584 572 1583 573 505 572 505 573 504 574 1583 574 505 573 505 573 504 574 505 572 1584 572 1584 573 5178 4387 4400 571 1583 573 504 574 1584 572 1584 572 507 572 504 574 1582 574 505 572 505 573 1583 573 504 574 504 574 1582 574 1584 573 503 574 1583 573 1582 574 505 573 1583 573 1582 575 1583 573 1610 546 1584 572 1583 573 505 573 1610 546 506 572 505 573 504 574 504 574 505 573 505 573 1584 573 505 573 1582 574 1584 572 1583 573 1583 573 504 574 503 575 504 574 1585 571 507 571 504 573 506 572 505 572 1584 572 1585 571\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4388 4399 547 1608 548 530 548 1610 547 1610 547 529 549 529 548 1608 548 530 548 530 548 1607 549 533 545 531 548 1608 548 1610 546 531 547 1609 547 1608 548 529 549 1609 547 1609 547 1609 547 1609 547 1609 547 1608 548 529 548 1638 519 530 548 530 548 530 548 529 550 528 549 530 548 530 548 1608 548 530 548 1609 548 1610 547 1609 547 531 546 529 549 1608 548 530 548 1609 548 530 548 529 548 530 548 1609 548 1609 548 5205 4387 4398 547 1609 548 531 546 1609 547 1609 547 530 548 531 546 1609 547 531 548 530 573 1583 573 507 571 506 572 1583 573 1582 574 504 574 1581 575 1582 574 506 572 1583 574 1583 573 1583 573 1585 571 1584 572 1585 570 507 571 1582 574 505 574 532 545 505 573 505 572 506 571 505 573 505 573 1584 572 506 572 1583 573 1584 572 1583 573 505 572 504 573 1583 573 505 573 1586 571 506 572 505 573 507 572 1583 573 1584 572\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4388 4399 572 1583 573 532 546 1585 571 1583 574 503 575 505 573 1584 572 504 574 505 573 1584 573 506 572 504 573 1584 573 1584 572 505 574 1611 545 506 573 1583 573 1585 571 1586 545 1609 547 531 547 1611 545 1608 548 1607 549 530 548 529 548 531 548 532 546 1610 546 533 545 530 547 1609 547 1610 547 1609 547 533 545 529 548 530 548 530 547 531 546 530 547 530 548 533 544 1608 548 1608 548 1610 546 1606 550 1609 547 5203 4388 4397 548 1609 547 531 547 1608 548 1608 548 530 548 530 548 1608 548 531 547 531 547 1610 546 531 547 530 548 1609 547 1611 546 532 547 1609 547 531 547 1608 548 1610 546 1609 547 1608 548 530 547 1609 547 1608 548 1609 547 531 546 530 548 530 547 530 547 1608 548 532 547 534 545 1608 548 1608 548 1609 547 530 548 531 547 531 547 532 546 531 546 531 547 532 546 530 548 1608 547 1608 548 1610 546 1608 548 1608 548\n#\n# Model: Panasonic CS-E9HKR\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3466 1748 445 367 502 1292 444 422 444 420 445 422 443 420 445 421 445 420 445 363 503 420 445 422 444 420 445 424 445 1292 445 421 445 451 414 421 444 421 444 421 445 422 443 423 446 1292 443 1292 445 1292 445 421 497 372 444 1293 445 420 445 421 444 421 444 421 445 421 444 421 444 420 445 422 444 420 445 421 444 421 444 421 445 422 443 356 509 420 445 421 445 429 436 421 444 421 444 414 452 420 445 421 444 421 444 423 443 421 444 421 444 421 444 423 443 420 445 424 445 1291 444 1294 444 421 444 421 444 421 444 422 444 423 445 9989 3463 1752 444 424 445 1293 443 422 444 392 473 421 444 416 449 423 443 421 444 421 444 421 444 423 443 422 443 425 444 1292 444 423 443 422 443 419 446 421 444 423 443 421 444 426 443 1292 444 1294 443 1294 443 421 444 425 444 1294 444 422 443 421 444 422 443 424 442 421 444 421 444 421 444 423 444 421 444 422 443 421 444 423 443 425 444 1293 444 421 444 421 444 1293 444 423 446 1292 445 321 546 421 444 421 444 385 480 423 443 425 444 1291 445 1293 443 422 445 422 443 421 444 421 496 370 444 422 443 421 496 370 443 421 444 1291 497 1239 444 1284 504 1237 447 1293 444 425 444 1293 496 371 494 1241 495 370 496 369 497 297 517 421 496 370 495 369 496 370 496 370 495 370 495 369 496 372 494 370 495 369 496 370 495 370 496 370 495 373 495 1240 495 1241 495 1240 497 369 496 371 494 371 443 422 496 369 496 368 497 370 496 369 496 372 497 1239 496 1241 496 1241 496 370 495 310 555 371 495 371 494 368 497 370 495 370 496 361 504 370 495 369 496 372 494 372 494 369 496 371 494 371 495 373 495 1240 497 370 495 371 495 370 495 369 496 370 495 370 495 1242 495 373 492 370 495 366 500 369 496 371 495 370 495 371 495 369 496 371 494 370 495 371 495 371 494 371 494 370 495 372 494 380 488 1241 496 374 495 1241 495 1240 495 1240 495 1241 495 1242 494 1244 494\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3467 1751 442 425 444 1294 443 424 442 422 443 423 442 422 443 422 444 421 444 421 444 424 441 423 443 421 444 425 444 1294 443 424 442 423 442 422 443 425 440 423 443 423 442 427 442 1292 443 1322 415 1293 444 422 443 425 443 1295 443 424 441 421 444 423 442 424 442 425 440 423 442 423 442 424 442 292 573 421 444 423 442 422 444 422 443 422 443 423 442 425 441 423 442 425 440 421 444 424 442 422 443 422 443 422 443 424 442 422 443 422 443 422 443 424 442 421 444 426 443 1292 443 1294 444 423 442 422 443 423 442 424 442 426 442 9989 3465 1753 442 425 444 1292 445 424 442 424 441 421 444 422 443 422 444 423 442 423 442 352 513 423 443 423 442 425 443 1296 441 423 443 422 443 422 443 424 441 423 443 422 443 427 442 1293 442 1322 415 1293 444 421 444 425 443 1294 444 422 443 422 443 421 444 425 441 423 442 422 443 421 444 423 443 421 444 422 443 422 443 424 442 425 444 1261 475 422 443 422 443 1292 444 1293 442 1295 441 424 442 422 443 422 443 427 442 1322 415 1292 444 1292 444 1294 442 423 443 422 443 421 444 422 443 423 443 421 444 421 444 423 442 421 444 1291 444 1293 443 1294 442 1195 541 1293 444 425 443 1293 443 422 444 1322 414 421 444 422 443 423 443 422 443 423 442 422 443 425 441 422 443 422 443 423 442 423 443 422 443 422 443 421 444 423 444 421 444 424 444 1292 444 1293 444 1294 442 422 443 421 444 423 443 422 443 421 444 422 443 424 442 421 444 426 443 1291 444 1293 443 1293 444 422 443 421 444 422 444 421 444 422 443 421 444 423 443 423 442 421 444 422 443 423 444 421 444 421 444 421 444 422 444 425 444 1294 443 422 443 423 444 424 441 422 443 422 443 422 443 1294 443 422 443 421 444 424 442 421 444 413 452 421 444 423 443 421 444 422 443 422 443 422 444 421 444 422 443 422 443 423 443 426 443 1294 442 421 444 423 442 1293 443 1293 443 421 444 422 444 362 505\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3467 1751 444 424 444 1291 446 422 444 420 445 420 445 419 446 421 445 378 487 421 444 420 445 420 446 421 444 423 446 1321 415 451 415 420 445 420 445 419 446 421 445 419 446 422 447 1290 445 1291 445 1291 446 420 445 424 445 1293 445 420 445 420 445 420 445 421 445 420 445 420 445 419 446 421 445 420 445 421 444 420 445 421 445 420 445 420 445 420 445 421 445 419 446 420 445 420 445 420 446 420 445 421 444 419 446 422 444 420 445 420 445 421 444 421 445 421 444 424 445 1290 445 1292 446 420 445 421 444 420 445 423 443 424 444 9988 3465 1751 446 423 445 1291 446 421 445 420 445 420 445 420 445 422 444 420 445 420 445 421 444 421 446 421 444 424 445 1291 446 421 445 421 444 420 445 420 445 420 446 421 444 423 446 1291 444 1293 444 1292 445 420 445 423 446 1294 444 419 446 420 445 421 444 422 444 420 445 421 444 420 445 421 445 421 444 420 445 420 445 422 444 421 444 420 445 420 445 421 444 1320 415 1291 445 1292 444 422 444 420 445 419 446 420 445 421 445 421 444 424 445 1291 446 421 445 420 445 420 445 421 444 421 445 421 444 421 444 420 445 420 445 1291 445 1292 443 1290 445 1291 446 1292 445 424 444 1293 444 421 444 1292 445 422 443 421 444 381 485 420 445 420 445 420 445 422 444 421 444 420 445 421 444 421 445 421 444 421 444 420 445 421 445 421 444 424 445 1291 444 1292 445 1291 446 420 445 419 446 422 444 421 444 420 445 420 445 421 445 420 445 423 446 1291 444 1291 446 1292 445 420 445 420 445 422 444 420 445 420 445 420 445 422 444 421 444 420 445 419 446 420 446 420 445 419 446 419 446 421 445 423 445 1293 444 419 446 421 445 420 445 419 446 420 445 420 445 1292 445 420 445 420 445 421 445 420 445 419 446 419 446 420 446 419 446 420 445 420 445 420 446 420 445 420 445 420 445 421 445 419 446 420 445 423 446 1292 445 1290 445 1290 445 1292 444 1291 445 1293 445\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3463 1751 443 424 445 1293 443 422 444 420 445 421 444 421 444 422 444 421 444 421 444 421 444 353 513 422 443 425 444 1293 444 422 444 421 444 420 445 421 444 422 444 421 444 425 444 1291 444 1294 443 1293 444 421 444 424 445 1294 444 420 445 421 444 416 449 422 444 421 444 421 444 421 444 422 444 420 445 421 444 422 443 422 444 421 444 421 444 420 445 373 493 420 445 421 444 420 445 422 444 420 445 422 443 421 444 422 444 421 444 421 444 422 443 421 445 421 444 424 445 1291 444 1294 444 421 444 420 445 420 445 422 444 424 444 9991 3463 1751 445 425 444 1293 444 422 444 421 444 420 445 421 444 423 443 421 444 421 444 421 444 422 444 421 444 424 445 1293 444 422 444 420 445 422 443 421 444 424 442 421 444 425 444 1292 443 1292 445 1292 445 421 444 424 445 1293 445 421 444 421 444 421 444 422 444 421 444 421 444 422 443 422 444 423 442 421 444 423 442 423 443 425 443 1293 444 423 442 421 444 1292 444 422 443 425 443 1295 443 421 444 368 549 373 443 1293 496 1240 495 1240 495 1241 496 371 495 370 495 370 495 370 495 371 495 370 495 369 496 371 494 371 494 1240 442 1293 495 1240 496 1241 496 1240 497 373 496 1241 496 371 494 1184 553 370 495 370 495 370 496 370 443 423 494 370 443 424 495 370 442 423 442 422 443 424 442 423 442 423 442 425 440 424 442 424 441 427 442 1294 441 1294 443 1294 443 424 441 423 442 425 442 421 444 423 442 424 441 425 441 424 441 426 443 1293 442 1295 442 1295 441 424 441 394 471 425 441 423 442 423 442 424 441 424 442 424 441 423 442 423 442 424 442 423 442 424 441 423 442 424 442 428 440 1295 442 424 441 425 441 424 441 424 441 423 442 424 441 1294 443 424 441 424 441 424 442 423 442 423 442 423 442 425 441 424 441 424 441 424 441 424 442 424 441 449 416 423 442 424 442 427 442 1295 442 424 441 424 441 1295 442 427 442 1295 441 425 441 426 441\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3461 1753 439 429 440 1298 439 427 439 426 439 426 439 426 439 427 439 426 439 426 439 426 439 427 439 426 439 430 438 1298 438 428 438 426 439 427 438 427 438 427 439 427 438 430 414 1321 439 1299 413 1323 414 451 414 455 414 1324 414 452 413 451 414 453 413 436 430 452 413 452 413 451 414 453 413 452 413 451 414 452 413 454 412 452 413 452 413 452 413 453 413 452 413 452 413 452 413 454 413 453 412 452 413 452 413 453 413 452 413 452 413 452 413 453 413 452 413 456 412 1323 412 1325 413 452 413 453 412 453 412 454 412 455 390 10044 3438 1779 412 457 412 1324 413 454 412 453 412 453 412 453 412 454 412 453 412 453 412 454 411 455 411 453 412 457 389 1347 390 477 389 476 389 476 389 476 389 402 465 475 390 479 390 1346 389 1348 389 1347 390 476 389 479 390 1348 390 476 389 476 389 476 389 477 389 475 390 476 389 476 389 477 389 476 389 476 389 476 389 478 388 480 389 1347 390 475 390 476 389 1347 390 476 389 479 389 1349 389 476 389 476 389 476 389 477 389 476 389 479 390 1348 389 477 389 476 389 476 389 476 389 476 390 476 389 476 389 476 389 476 389 1346 389 1347 389 1346 389 1347 390 1346 390 479 390 1347 390 476 389 1374 363 474 415 454 388 480 386 475 414 454 387 474 391 476 414 455 387 473 392 476 414 453 389 476 389 475 390 475 390 476 390 476 390 479 389 1346 389 1347 390 1348 389 476 389 476 389 477 389 476 389 476 389 475 390 477 389 440 425 479 389 1345 390 1347 390 1347 390 476 389 475 390 477 389 476 389 476 389 476 389 477 389 476 389 476 389 475 390 477 389 476 389 476 389 476 389 477 389 479 390 1347 390 476 389 477 389 476 389 476 389 476 389 476 389 1347 390 476 389 476 389 477 389 503 362 476 389 476 389 477 389 475 390 476 389 476 389 477 389 476 389 476 389 476 389 477 389 479 389 1347 390 479 390 1347 390 1348 389 476 389 476 389 477 389 477 390\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3489 1725 493 375 493 1167 569 375 491 373 492 372 493 374 492 373 493 372 493 373 442 423 492 375 493 372 492 378 490 1245 443 423 493 374 491 375 490 374 442 424 492 374 491 379 440 1293 492 1245 443 1294 442 423 491 379 441 1296 491 375 441 423 442 423 442 425 441 423 442 425 440 424 441 425 441 424 441 421 444 424 441 425 441 423 442 423 442 423 442 453 413 424 442 451 414 424 441 426 440 424 441 424 441 424 441 425 441 424 441 424 441 424 441 426 440 425 440 427 442 1322 413 1296 442 424 441 423 442 424 441 424 442 427 441 9994 3463 1755 440 428 441 1296 441 426 440 425 440 426 439 425 440 426 440 423 442 425 440 425 440 372 494 425 440 428 440 1297 440 426 440 425 440 426 439 426 439 427 439 425 440 429 439 1297 439 1297 439 1297 440 426 439 429 440 1299 439 426 439 426 439 428 437 427 439 426 439 426 439 427 438 427 439 427 438 427 438 427 438 429 437 427 414 451 414 451 414 418 447 1323 414 452 413 455 414 1325 413 452 413 452 413 452 413 453 413 452 413 456 413 1324 413 453 413 452 413 453 412 452 413 454 412 453 412 453 412 453 412 452 413 1324 412 1323 412 1324 412 1324 412 1325 412 456 413 1324 412 404 461 1325 412 453 412 453 412 454 412 453 412 453 412 454 411 455 411 453 412 453 412 453 412 454 412 453 412 453 412 453 412 454 413 453 412 457 412 1323 413 1324 413 1324 412 453 412 453 412 454 413 453 412 453 412 453 412 454 412 452 413 456 412 1323 413 1324 413 1323 414 453 412 452 413 453 413 452 413 452 413 452 413 453 413 450 415 452 413 452 413 453 413 452 413 451 439 427 438 428 439 430 439 1298 439 426 439 428 438 426 439 428 437 426 439 427 438 1298 439 427 438 426 439 427 439 426 439 426 439 425 440 427 440 426 439 426 439 426 439 425 441 426 439 425 440 425 440 427 439 426 439 425 440 429 439 1297 440 1297 440 425 440 425 440 426 441 427 440\n#\n# Model: Maytag M6X06F2A\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 02 00 00 00\n#\n# Model: Airmax\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4453 4313 586 1554 585 483 586 1554 587 1552 587 483 587 484 586 1552 587 482 588 483 586 1553 586 483 587 483 586 1554 586 1553 585 484 587 1553 586 484 587 1553 586 1554 586 1553 586 1554 586 483 586 1553 586 1553 586 1555 585 483 586 482 588 483 585 484 585 1554 586 483 587 484 585 1553 587 1554 584 1554 585 484 586 484 586 483 586 483 586 484 586 482 588 483 584 484 586 1553 585 1555 584 1555 586 1553 586 1554 585 5129 4428 4312 585 1553 586 483 586 1555 584 1553 586 484 583 486 584 1555 585 484 585 483 586 1554 585 483 586 484 585 1554 585 1554 585 484 585 1554 585 484 584 1554 585 1554 584 1554 585 1554 586 483 585 1554 585 1555 584 1553 584 484 586 483 586 483 585 484 586 1554 583 484 586 483 585 1553 585 1553 585 1553 585 484 584 485 585 485 584 484 585 484 585 485 584 484 585 483 586 1554 585 1553 585 1554 584 1553 585 1553 585\n#\n# Model: Airmet ac\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8971 4489 568 584 535 564 565 561 537 588 541 558 561 565 534 592 537 562 567 1659 559 566 563 563 536 1664 584 567 542 584 535 1664 564 587 542 558 561 564 535 591 538 562 567 558 541 585 534 566 563 562 537 589 540 559 560 566 533 593 536 563 566 560 539 587 532 567 562 564 535 591 538 1661 567 1658 590 1662 566 1659 559 1667 592 1660 558 567 562 563 536 590 539 561 558 567 542 584 535 1664 564 1662 586 1665 563 1662 566 1659 589 1663 565 560 559 566 533 593 536 564 565 560 538 587 542 557 562 564 535 591 538 1661 567 585 534 1666 562 1663 585 566 533 1667 592 560 538\n#\n# Model: Airwell\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3089 3684 1991 910 1011 1854 983 847 1992 911 982 965 956 874 984 938 982 939 973 1866 1889 948 973 950 972 950 972 859 971 950 972 950 972 954 968 887 943 979 943 979 943 979 943 887 944 979 943 979 943 979 943 887 943 979 943 950 972 979 943 1803 1952 977 3051 3724 1951 978 943 1894 943 888 1951 978 943 979 943 888 942 980 942 979 943 1895 1859 978 943 979 943 979 943 888 943 979 943 979 943 980 942 888 943 980 942 980 942 980 942 888 942 980 942 980 942 980 942 888 943 979 943 980 942 980 942 1804 1950 978 3050 3725 1950 978 942 1896 941 889 1950 979 941 980 942 888 942 980 942 980 942 1896 1858 979 942 980 942 980 942 889 941 980 942 981 941 980 942 889 942 980 942 981 941 981 941 889 941 981 941 981 941 981 941 889 941 981 941 981 941 981 941 1805 1949 980 3964\n#\n# Model: Airwell AWSI-PNXA012-N11\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3031 3980 1912 1945 1969 1950 1912 973 994 981 965 951 975 1027 909 1950 975 965 971 974 1941 1006 940 977 969 1003 964 951 975 1000 936 1006 940 973 973 1974 941 973 1942 1006 941 974 993 950 975 999 937 1036 910 975 992 952 974 998 938 1950 975 967 3947 3987 1916 2002 1881 1975 1939 947 999 943 972 971 996 948 967 1949 997 949 976 997 1907 980 966 1006 941 975 971 1001 945 999 968 945 970 976 970 1974 972 942 1942 975 992 952 994 949 977 970 997 948 967 979 967 1005 942 999 968 1923 971 1000 3914 3958 1965 1919 1995 1893 1969 984 962 954 972 996 971 973 942 1949 976 992 965 947 1947 1002 965 950 976 1001 945 997 939 1007 939 1006 940 998 969 1915 1000 944 2002 946 969 944 992 956 1001 943 972 1000 967 949 966 977 969 1001 966 1926 968 1003 4889\n#\n# Model: Easy Home_Portable_Air_Cooler\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1281 401 1275 431 406 1248 1276 431 1244 409 439 1242 433 1247 439 1242 433 1248 438 1243 443 1239 1285 7120 1280 407 1279 406 442 1239 1285 400 1275 434 414 1241 434 1247 439 1243 443 1239 436 1246 440 1242 1282 8228 1282 405 1281 404 433 1248 1286 399 1276 406 442 1240 435 1245 441 1241 434 1247 439 1243 443 1239 1285 7121 1279 407 1279 432 416 1239 1285 400 1275 434 414 1241 434 1246 440 1242 444 1238 437 1245 441 1241 1283 8225 1285 402 1284 401 436 1245 1279 406 1280 403 434 1246 440 1241 434 1246 440 1242 444 1237 438 1244 1280 7124 1286 399 1276 408 440 1241 1283 401 1274 434 414 1240 435 1245 441 1241 434 1246 440 1242 444 1237 1287 8220 1279 408 1278 407 441 1240 1284 401 1274 409 439 1242 433 1247 439 1242 444 1238 437 1244 442 1240 1284 7121 1278 408 1278 433 415 1240 1284 399 1276 406 442 1239 436 1244 442 1239 436 1244 442 1239 436 1245 1279 8227 1283 403 1283 400 437 1244 1280 403 1283 399 438 1242 433 1247 439 1242 433 1247 439 1242 444 1238 1275 7127 1283 402 1284 426 412 1243 1281 402 1274 408 440 1240 435 1245 441 1239 436 1245 441 1240 435 1245 1279 8226 1284 402 1284 399 438 1242 1282 400 1275 407 441 1239 436 1243 443 1237 438 1242 444 1237 438 1242 1282 7120 1280 405 1281 428 409 1244 1280 403 1272 409 439 1241 435 1245 441 1239 436 1244 442 1239 436 1244 1280 8225 1285 400 1275 408 440 1241 1283 399 1276 406 432 1248 438 1242 433 1247 439 1242 444 1237 438 1242 1282 7120 1280 406 1280 403 434 1246 1278 405 1281 400 437 1243 432 1247 439 1241 434 1247 439 1242 444 1237 1276 8228 1282 405 1281 402 435 1244 1280 404 1282 400 437 1241 434 1246 440 1241 434 1246 440 1242 433 1247 1277 7126 1284 403 1283 400 437 1241 1283 402 1273 409 439 1239 436 1244 442 1239 436 1244 442 1239 436 1245 1279 8227 1283 403 1283 401 436 1242 1282 403 1283 399 438 1241 434 1246 440 1240 435 1246 440 1241 434 1246 1278 7126 1284 402 1284 399 438 1241 1283 401 1306 376 440 1239 436 1244 442 1238 437 1244 442 1239 436 1245 1279 8225 1285 402 1305 379 437 1242 1313 371 1304 378 438 1240 435 1245 441 1240 435 1245 441 1240 435 1246 1278 7125 1285 401 1306 378 470 1209 1304 380 1306 377 460 1218 436 1244 442 1238 437 1244 442 1239 436 1245 1310 8195 1315 371 1304 379 469 1211 1313 371 1304 378 470 1209 435 1245 441 1240 435 1245 441 1240 435 1245 1310 7092 1308 378 1308 375 462 1216 1308 376 1299 382 466 1213 462 1218 436 1244 442 1238 437 1244 442 1239 1306\n#\n# Model: Amcor AC\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 9C 00 00 00\n#\n# Model: Arctic King_RG15B1\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 01 FF 00 00\ncommand: 12 ED 00 00\n#\n# Model: Argo ac\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3455 1584 486 347 483 348 482 347 482 348 480 350 430 401 429 1229 432 401 429 400 430 400 430 399 431 399 431 398 432 398 432 399 431 399 456 375 455 377 453 1207 453 378 452 1209 452 379 451 379 451 379 451 379 451 379 451 379 452 379 451 379 451 379 451 403 427 1233 427 1234 427 380 450 1234 426 1234 427 403 427 1233 427 403 427 1234 427 403 427 1234 426 404 426 403 427 403 427 403 427 403 427 1234 427 403 427 403 427 403 427 403 427 403 427 1234 427 403 427 403 427 403 427 403 427 403 427 1234 427 403 427 1234 426 1234 427 1234 426 1234 427 1234 427 403 427 403 427 403 427 1234 427 404 426 403 427 403 427 403 427 403 427 403 427 403 428 403 427 403 427 403 427 403 427 403 427 403 427 403 427 1234 427 1234 427 404 426 404 426 404 426 404 426 1234 426 404 427 404 426 404 426 1234 426 404 426 404 426 1234 427 404 426 404 426 404 426 404 426 404 426 404 426 1235 426 1235 426 1235 426 404 426 1235 425 405 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 427 404 426 404 426 404 426 404 427 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 404 426 1235 426 1235 426 1235 426 404 426 1235 426 404 426\n#\n# Model: Ariston AC_A-MW09-IGX\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4481 4414 595 1596 594 527 562 1602 588 1604 586 535 565 530 570 1594 596 526 563 532 568 1596 594 528 561 534 566 1598 592 1600 590 531 569 1596 594 527 562 1603 587 1604 586 1605 648 1543 594 527 562 1603 587 1604 586 1605 595 525 564 531 569 526 563 532 568 1596 594 528 561 534 566 1598 592 1600 590 1601 589 532 568 527 562 533 567 528 561 534 566 529 560 535 565 530 570 1594 596 1596 594 1597 593 1598 592 1599 591 5252 4503 4418 590 1601 589 532 568 1597 593 1598 592 529 560 535 565 1600 590 531 569 526 563 1602 588 533 567 529 560 1604 596 1595 595 526 563 1602 588 533 567 1598 592 1599 591 1600 590 1601 589 532 568 1598 592 1599 591 1600 590 531 569 527 562 532 568 528 561 1603 587 534 566 530 559 1605 595 1596 594 1597 593 528 561 534 566 529 560 535 565 530 570 525 564 531 569 527 562 1602 588 1603 587 1604 596 1595 595 1596 594\n#\n# Model: Ballu R05-BGE\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4467 4296 626 1511 627 469 626 1512 626 1538 652 442 625 469 601 1538 600 495 599 469 599 1565 598 471 597 498 596 1542 596 1569 595 474 595 1570 594 474 595 1570 594 1544 594 1570 594 1570 594 475 594 1570 594 1544 594 1570 594 475 594 501 594 475 594 500 595 1544 594 501 594 475 594 1570 594 1544 594 1570 594 475 594 501 594 475 594 501 594 475 594 501 594 475 594 475 620 1544 594 1570 594 1544 594 1570 594 1545 594 5144 4435 4328 593 1545 593 501 594 1545 593 1571 593 475 594 501 594 1545 593 501 593 475 594 1571 593 475 594 501 594 1545 593 1571 593 475 594 1571 593 476 619 1545 593 1571 593 1545 593 1571 593 476 593 1571 593 1545 593 1571 594 502 593 476 593 476 593 502 593 1545 593 502 593 476 593 1572 592 1572 592 1546 592 502 593 476 593 476 593 502 593 477 592 503 592 477 592 503 592 1546 592 1572 592 1546 592 1572 592 1572 592\n#\n# Model: Portable\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3120 1593 488 1180 489 1177 492 342 492 342 492 367 467 1174 485 349 485 349 485 1181 488 1179 490 343 491 1177 492 341 493 366 458 1183 486 1181 488 346 488 1179 490 1177 492 342 492 341 493 1174 485 349 485 347 487 1180 489 345 489 344 490 370 464 368 466 341 493 341 483 376 458 375 459 348 486 374 460 372 462 345 489 370 464 369 465 368 466 341 493 367 457 348 486 374 460 348 486 1179 490 343 491 343 491 1176 493 1174 485 349 485 349 485 374 460 373 461 373 461 346 488 371 463 371 463 369 465 1175 484 350 484 350 484 348 486 374 460 346 488 372 462 345 489 371 463 370 464 368 466 340 484 376 458 376 458 375 459 348 486 347 487 345 489 370 464 370 464 369 465 342 492 368 466 367 457 375 459 348 486 374 460 347 487 372 462 345 489 371 463 343 491 368 466 341 493 367 457 376 458 375 459 1181 488 346 488 345 489 1178 491 342 492 342 492 1175 494 1173 486 1182 487 346 488 346 488 1179 490 343 491 342 492 368 466 367 457\n#\n# Model: Bonaire DurangoAC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1305 435 1280 432 415 1255 1307 432 1272 439 418 1252 442 1255 1307 431 416 1255 439 1258 447 1251 443 8174 1302 437 1278 433 414 1255 1307 432 1273 438 419 1250 444 1254 1298 440 417 1253 441 1256 438 1259 445 8170 1306 433 1271 439 418 1251 1301 438 1277 434 413 1256 449 1249 1303 435 412 1258 446 1251 443 1254 440 8176 1300 438 1277 434 413 1283 1279 433 1271 440 417 1278 416 1255 1307 431 416 1253 441 1257 447 1250 444 8171 1305 433 1272 439 418 1278 1274 438 1277 433 414 1282 412 1259 1303 434 413 1256 448 1249 445 1252 442 8173 1303 435 1270 440 417 1279 1273 438 1277 433 414 1282 412 1258 1304 433 414 1282 412 1258 446 1250 444 8171 1305 433 1272 438 419 1276 1276 435 1270 441 416 1252 442 1255 1297 440 417 1279 415 1255 439 1257 447 8168 1297 439 1276 434 413 1256 1306 431 1273 436 411 1258 446 1250 1302 409 438 1284 421 1249 445 1252 442\n#\n# Model: Boston Bay_MSAB_09CR\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4468 4412 543 1593 541 502 539 1596 548 1589 545 496 545 498 543 1593 541 501 540 502 539 1598 546 495 546 497 544 1591 543 1594 540 501 539 1597 547 494 547 1590 544 1591 543 1594 540 1595 539 504 547 1588 546 1590 544 1593 540 501 539 503 538 503 538 505 546 1588 546 497 544 496 545 1592 542 1592 542 1594 540 501 539 503 538 503 538 505 546 494 547 496 545 496 545 497 544 1590 544 1593 541 1594 540 1597 547 1589 545 5188 4430 4421 544 1592 541 499 541 1595 539 1596 538 505 546 494 547 1589 545 497 544 496 545 1592 542 500 540 499 542 1594 540 1596 538 503 538 1599 545 497 544 1591 543 1593 541 1595 539 1596 538 504 547 1588 546 1588 546 1590 544 498 542 498 543 499 541 500 541 1593 541 501 540 502 539 1597 547 1586 548 1587 547 496 545 495 546 497 544 498 543 497 544 498 543 499 542 498 542 1592 542 1594 540 1595 539 1597 547 1589 545\n#\n# Model: Botti BL-168DLR\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9577 4536 622 576 621 577 620 577 620 578 619 578 619 578 619 579 618 1667 625 1660 622 1664 618 1668 624 1662 620 1666 616 1670 622 1664 618 580 617 581 616 1669 623 575 622 575 622 1664 618 580 617 580 617 581 616 1669 623 575 622 1664 618 1668 624 573 624 1662 620 1666 616 1670 622 39798 9556 2250 626 96841 9564 2245 620 96843 9560 2253 622 96836 9569 2244 621 96844 9571 2244 621 96843 9573 2247 619 96844 9572 2248 617 96847 9570 2248 617 96848 9568 2250 626 96833 9564 2251 625 96834 9564 2252 624 96843 9595 2223 653 96809 9597 2221 644 96822 9604 2215 650 96814 9591 2226 650 96813 9591 2225 650 96811 9600 2218 647 96816 9597 2221 654 96812 9601 2219 646 96815 9598 2220 645 96813 9601 2216 649 96815 9599 2219 657 96809 9565 2252 624 96842 9564 2254 622 96841 9565 2254 622 96838 9568 2249 616 96844 9571 2247 619 96842 9574 2245 620 96840 9566 2251 625 96838 9567 2251 625 96835 9570 2250 615 96848 9566 2254 621 96843 9572 2247 618 96847 9567 2253 623 96842 9572 2249 616 96843 9572 2247 618 96843 9572 2249 616 96849 9576 2245 621 96842 9574 2246 619 96842 9575 2246 619 96843 9573 2248 617 96843 9572 2249 616 96847 9571 2249 617 96839 9569 2251 625 96837 9569 2251 624 96837 9568 2250 626 96835 9572 2248 618 96844 9572 2246 619 96843 9564 2253 623 96834 9572 2245 620 96835 9569 2246 619\n#\n# Model: Boulanger AC\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 81 00 00 00\ncommand: 6B 00 00 00\n#\n# Model: Chigo CS-21H3A-B155AF\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6133 7359 575 561 550 560 577 532 576 559 550 534 602 507 549 536 573 536 573 564 546 539 571 538 545 540 570 538 570 566 545 539 571 589 545 590 521 540 570 562 521 563 547 562 545 592 521 563 548 588 522 615 521 563 547 589 521 589 548 589 547 562 521 589 548 588 548 589 521 563 547 563 546 590 521 589 547 562 546 590 521 589 547 589 547 563 521 563 548 589 521 589 548 588 549 589 545 539 548 589 548 563 521 562 549 589 521 563 548 563 547 590 521 589 548 589 548 563 521 589 547 1667 521 1641 521 563 547 589 548 589 521 589 548 562 548 590 520 589 548 589 548 589 521 590 547 588 548 1667 520 1668 545 1642 547 563 520 563 547 1667 520 589 547 589 547 1641 546 563 547 1641 546 563 546 538 546 1668 544 539 547 1668 520 590 546 590 546 1668 520 564 546 590 520 1668 547 1642 546 1668 542 7378 545\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6107 7355 578 556 579 530 551 532 605 529 604 505 550 559 602 507 574 561 522 563 573 561 546 537 548 561 548 561 522 561 549 561 522 538 572 587 549 587 522 561 548 561 547 563 522 561 548 561 522 561 549 588 546 563 521 561 549 561 522 561 549 561 522 561 549 561 547 590 521 561 549 562 521 562 548 562 545 538 548 562 547 562 522 588 548 588 548 588 522 562 548 588 548 589 521 562 548 588 547 589 521 562 548 562 521 562 548 563 546 590 521 562 548 589 546 591 520 1641 544 566 547 1667 544 1616 548 1613 547 1666 520 588 548 588 548 589 545 538 547 562 547 564 519 562 548 563 520 563 547 1667 520 589 547 564 545 1641 547 590 546 590 520 1666 548 590 543 1642 548 563 520 1666 548 590 544 538 547 1667 544 592 520 1668 543 592 520 562 548 564 519 1667 547 590 544 1642 547 1614 546 588 548 7396 543\n#\n# Model: Chigo KRF-51G_79F\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6060 7357 592 1633 593 1633 593 1633 593 1633 593 1633 593 1633 592 1634 592 1634 592 515 591 515 591 515 591 516 590 517 590 516 590 517 590 517 589 1636 590 1636 590 1636 590 1636 590 1636 590 1636 590 1636 590 1636 590 517 590 517 590 517 590 517 590 517 590 517 589 517 590 517 590 1636 590 1637 589 1637 589 1637 589 1636 615 1612 589 1637 589 1636 590 517 590 517 589 517 590 517 590 517 590 517 589 517 590 517 590 1637 589 1637 589 1637 589 517 589 517 589 517 590 1637 589 1637 589 517 590 517 590 517 589 1637 589 1637 589 1637 590 517 589 517 589 517 589 518 589 517 589 1637 589 1637 589 517 590 1637 589 1637 589 1637 589 1637 589 1637 590 517 589 517 589 1637 589 517 589 517 590 517 589 1638 589 517 590 1637 589 518 589 1637 589 518 588 518 589 1637 589 518 588 1637 589 518 588 1637 589 518 589 1637 589 1637 589 7359 589\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6059 7355 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 618 1608 617 489 617 490 616 490 616 491 590 517 589 517 614 492 590 517 590 1636 589 1636 590 1636 590 1636 589 1636 590 1636 590 1636 590 1636 590 517 589 517 589 517 589 517 590 517 589 517 589 517 589 517 589 517 589 1636 614 492 590 1636 590 1636 590 1636 589 1636 590 1636 589 1637 589 517 589 1636 590 517 589 517 589 517 614 492 590 517 589 1636 590 517 589 1636 590 517 589 517 589 517 589 517 590 1636 590 517 589 1636 590 517 589 1636 589 1636 590 1636 590 1637 589 517 589 517 589 1637 588 1637 589 517 589 1637 589 1636 590 517 589 1636 590 1636 590 517 589 517 589 1637 589 517 589 517 589 1637 589 517 589 518 588 1637 589 518 588 1637 589 517 590 1637 588 518 588 518 588 1637 589 518 588 1637 589 518 588 1637 589 518 588 1637 589 1637 589 7357 589\n#\n# Model: Comfort Aire_RG57A6\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4381 4345 579 1558 579 488 580 1559 578 488 580 489 579 488 580 489 579 1558 579 489 579 488 580 488 580 489 579 488 580 489 579 1557 579 488 580 488 580 1559 577 1557 580 488 580 1557 579 1557 580 1557 580 488 580 1558 579 1558 579 1558 579 1557 580 1558 579 1559 578 1557 580 1557 580 1556 580 1558 579 1557 579 1559 578 1557 579 1560 577 1561 549 1584 579 1557 553 1584 552 1584 552 515 553 516 551 516 551 1585 551 1583 553 5156 4384 4343 552 515 552 1584 552 515 552 1584 552 1584 552 1584 552 1585 551 515 553 1585 551 1583 553 1584 552 1583 553 1584 552 1583 553 516 551 1584 552 1585 551 515 552 517 550 1583 553 515 552 515 552 516 551 1584 552 515 552 516 551 516 551 515 552 515 552 515 552 516 551 516 551 515 552 515 553 515 552 515 552 515 552 516 551 515 552 516 551 515 552 515 552 516 551 1583 553 1584 552 1585 551 515 552 515 552\n#\n# Model: Cortlitec portable_ac\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 00 FF 00 00\n#\nname: Dh\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 0C F3 00 00\n#\n# Model: COTech MPPH-08CRN7-QB6_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4412 4354 570 1575 568 504 572 1574 569 503 563 510 567 505 572 527 539 1580 563 509 568 505 572 1574 569 503 563 509 568 505 572 527 539 507 569 503 563 1582 571 500 566 507 569 503 563 535 542 505 561 511 566 1579 564 1582 571 1574 569 1576 567 1578 565 1581 562 1583 570 1575 568 1577 566 1580 563 1582 571 1574 569 1576 567 1579 564 1581 562 1583 570 1575 568 1577 566 1579 564 509 568 1577 566 1580 563 1582 571 501 565 5165 4412 4352 572 500 566 1579 564 509 568 1577 566 1579 564 1581 562 1583 570 502 564 1581 562 1582 571 501 565 1580 563 1582 571 1574 569 1576 567 1578 565 1579 564 509 567 1577 566 1579 564 1581 562 1583 570 1575 568 1577 566 506 571 501 565 534 542 529 537 535 542 531 535 537 539 532 545 527 539 507 569 529 537 509 568 504 562 510 566 532 544 527 539 507 569 528 538 534 542 1576 567 504 562 510 566 506 571 1574 569\n#\n# Model: Daikin AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9830 9789 9825 9795 4618 2487 381 348 384 929 381 936 384 353 379 934 386 350 382 350 382 362 380 349 383 929 381 355 387 346 386 926 384 353 379 354 388 355 387 918 382 354 388 348 384 349 383 353 379 357 385 924 386 358 384 921 389 347 385 351 381 929 381 932 388 348 384 349 383 361 381 347 385 351 381 355 387 345 387 925 385 352 380 353 379 365 388 340 382 354 388 348 384 349 383 929 381 355 387 346 386 358 384 921 379 357 385 351 381 352 380 356 386 930 380 357 385 358 385 344 388 348 384 933 387 349 383 353 379 938 382 354 388 352 380 20353 4625\n#\n# Model: Daikin AC_industrial_TB\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5055 2191 335 1799 360 752 338 714 365 716 364 1800 359 723 367 715 365 718 361 720 359 1805 365 747 332 1802 357 1806 364 719 360 1803 367 1798 361 1803 367 1797 362 1802 368 744 335 1799 360 721 369 714 365 716 363 719 360 721 369 713 366 1798 361 1802 368 715 364 717 362 720 359 723 367 715 364 1799 360 722 368 714 365 717 363 719 361 722 368 714 365 716 363 719 360 721 369 713 366 716 363 718 361 721 359 723 367 1797 362 1802 368 1796 363 1801 358 754 336 716 363 719 361 29583 5057 2160 366 1798 361 750 329 723 367 715 364 1800 359 753 337 715 364 717 363 720 359 1804 366 747 332 1801 358 1806 364 718 361 1803 356 1798 1802 368 1797 362 1802 368 714 365 1799 360 722 368 714 365 717 362 719 360 722 368 714 365 1798 361 1803 367 715 364 718 362 721 359 723 367 715 364 718 361 720 359 723 367 715 364 717 362 720 360 1804 366 1799 360 721 369 714 365 1798 361 1803 367 1798 361 720 359 723 367 715 364 718 361 720 360 723 367 715 364 717 362 720 359 722 368 714 365 717 362 719 360 722 368 1796 363 719 360 721 369 714 365 716 363 719 361 721 369 713 366 716 363 718 361 721 358 723 367 716 363 718 362 720 359 723 367 715 364 718 361 720 359 723 367 715 364 1799 360 1804 366 1799 360 721 369 714 365 716 363 1801 358 754 336 1798 361 720 360 1805 365 748 331 720 359 723 367 715 364 717 363 720 360 722 368 714 365 717 362 720 359 722 368 714 365 717 363 719 361 722 368 714 365 1798 361 751 339 713 366 716 363 1801 359 1805 365 1800 359 1805 365 1800 359 1805 365 1799 360\n#\n# Model: Daikin ARC480A53\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 445 455 421 449 416 453 422 447 418 451 424 25400 3494 1753 416 1322 419 450 415 456 420 449 416 1325 416 451 425 444 421 448 417 453 423 1318 423 444 421 1320 421 1316 414 457 419 1320 421 1318 423 1314 417 1322 419 1320 421 449 416 453 422 1317 424 445 420 450 415 456 419 447 418 451 414 456 420 449 416 452 424 448 417 453 422 445 420 450 415 456 419 448 417 452 423 447 418 449 416 453 423 448 417 453 423 446 419 449 416 453 423 447 418 1321 420 451 414 454 422 449 416 1322 419 1319 422 449 416 1322 419 451 424 445 420 449 416 455 421 445 420 449 416 455 420 448 417 452 424 446 419 1321 420 1317 424 1315 416 1324 417 1322 419 1320 421 1320 421 446 419 1319 422 1319 422 1318 423 1313 417 453 423 445 420 450 415 454 421 449 416 454 422 446 419 449 416 456 420 448 417 453 423 444 421 450 415 454 422 447 418 451 424 445 420 450 415 455 420 447 418 452 424 447 418 451 424 445 420 448 417 453 423 446 419 451 424 444 421 448 417 452 423 446 419 451 424 445 420 448 417 451 414 456 420 452 424 443 422 449 416 451 424 448 417 449 416 455 420 1318 423 446 419 1319 422 448 417 451 424 447 418 1320 421 1316 415 455 421 451 414 455 421 449 416 451 424 446 419 1319 422 448 417 454 422 446 419 451 414 1325 416 452 423 447 418 451 424 447 418 1318 423 446 419 449 416 1327 424 1312 419 449 416 455 421 449 416\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 444 456 419 450 415 455 420 448 417 455 420 25402 3491 1755 424 1315 415 455 420 449 416 458 417 1313 417 453 422 447 418 452 423 444 421 1320 421 447 418 1321 420 1319 422 448 417 1322 419 1320 421 1318 423 1315 415 1332 419 442 423 448 417 1320 421 448 417 454 422 445 420 450 415 461 414 450 415 455 420 446 419 452 423 446 419 451 424 444 421 447 418 454 421 445 420 449 416 454 422 447 418 1322 419 449 416 454 421 447 418 451 414 1325 416 453 422 447 418 453 422 446 419 453 422 445 420 448 417 453 422 1315 415 1324 417 453 422 447 418 450 415 454 422 448 417 454 421 446 419 451 424 1314 416 1326 415 1321 420 1318 423 446 419 1321 420 449 416 1322 419 1323 418 1320 421 1317 424 1318 423 445 420 449 416 452 423 449 416 452 423 444 421 449 416 459 416 446 419 458 417 448 417 456 420 443 422 447 418 452 423 445 420 450 415 452 424 447 418 452 423 446 419 449 416 460 415 448 417 452 423 445 420 450 415 455 420 449 416 452 423 445 420 451 414 454 421 449 416 455 420 446 419 451 414 454 422 449 416 452 424 445 420 451 424 443 422 448 417 1323 418 451 424 1315 415 453 422 446 419 450 415 1325 416 1324 417 451 414 456 419 450 415 454 421 447 418 452 423 446 419 450 415 453 423 450 415 451 424 1315 416 453 422 447 418 452 424 447 418 450 415 1323 418 1321 420 1319 422 1318 423 1317 424 1316 414 453 422\n#\n# Model: Daikin FTE35KV1\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5041 2133 360 1770 356 693 364 685 362 689 358 1772 364 686 361 689 358 692 365 684 363 1768 358 692 365 1767 359 1771 355 694 363 1769 357 1773 363 1768 358 1772 364 1767 359 689 358 692 365 1765 361 689 358 692 365 685 362 688 359 691 366 684 363 1767 359 1773 363 1768 358 1772 364 1767 359 689 358 1774 362 1769 357 691 366 684 363 687 360 690 357 693 364 685 362 689 358 691 366 684 363 687 360 690 357 693 364 1766 360 1773 363 1767 359 1771 355 694 363 687 360 690 357 693 364 29443 5038 2134 359 1774 362 686 361 689 358 692 365 1765 361 689 358 692 365 685 362 688 359 1771 365 685 362 1769 357 1775 361 687 360 1772 364 1767 359 1771 355 1776 360 1770 356 693 364 686 361 1799 327 693 364 686 361 689 358 692 365 685 362 688 359 691 356 694 363 686 361 689 358 1773 363 1769 357 691 366 684 363 1798 328 692 365 1767 359 1771 355 694 363 686 361 689 358 692 365 1765 361 1772 364 684 363 687 360 1801 335 684 363 687 360 690 357 693 364 686 361 1769 357 694 363 686 361 689 358 692 365 685 362 688 359 691 356 694 363 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 690 357 693 365 685 362 688 359 1772 364 1768 358 690 357 693 364 686 361 689 358 692 365 1765 361 689 358 692 365 685 362 688 359 691 356 694 363 687 360 689 358 1773 363 687 360 690 357 693 364 685 362 688 359 1772 364 686 361 1769 357 1776 360 1770 356 1775 361 687 360\n#\n# Model: Daikin FTX50GV1B\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 190 26844 503 368 475 395 475 395 475 367 476 395 475 25347 3527 1695 451 1290 451 420 476 394 476 394 475 1264 475 370 471 399 470 400 470 400 443 1298 443 400 470 1270 471 1270 471 400 470 1270 471 1271 470 1270 471 1270 471 1271 470 400 470 373 470 1271 470 400 470 400 470 400 443 400 470 400 470 400 443 400 470 400 470 400 470 374 469 1271 470 400 470 1271 470 400 470 401 442 401 469 1272 469 1272 469 401 469 401 469 374 469 401 469 401 469 401 442 401 469 401 469 401 469 374 469 401 469 401 469 374 469 401 469 401 469 401 442 1300 441 1299 442 1300 441 402 468 1272 469 402 468 1272 469 1273 468 34903 3522 1703 471 1270 471 400 470 373 470 400 470 1270 471 400 470 400 443 400 470 400 470 1270 471 400 443 1298 443 1297 444 400 470 1270 471 1270 471 1270 471 1270 471 1270 471 400 470 400 470 1270 471 373 470 400 470 400 470 400 443 400 470 400 470 400 470 373 470 400 470 400 470 400 443 1298 443 400 470 400 470 400 443 400 470 1271 470 400 470 1271 470 1271 470 400 470 1271 470 1271 470 1271 470 373 470 400 470 1271 470 1271 470 400 470 1271 470 1271 470 400 443 400 470 400 470 400 470 1271 470 373 470 1271 470 401 469 1271 470 400 470 1271 470 34903 3522 1702 471 1269 472 373 470 399 471 399 471 1270 471 399 444 399 471 399 471 399 471 1270 471 372 471 1270 471 1270 471 399 471 1270 471 1270 471 1270 471 1270 471 1270 471 399 471 399 444 1297 444 399 471 399 471 399 471 372 471 399 471 399 471 373 470 399 471 399 471 399 444 400 470 399 471 399 471 373 470 400 470 400 470 399 444 400 470 1270 471 400 470 400 470 1270 471 1271 470 1271 470 372 471 400 470 400 470 400 443 1298 443 400 470 1271 470 1271 470 400 470 400 443 401 469 400 470 401 469 373 470 401 469 401 469 401 442 401 469 401 469 401 469 375 468 402 468 401 469 1273 468 1296 445 426 416 426 445 426 444 426 417 426 444 426 444 426 444 399 444 426 444 426 444 426 417 426 444 426 444 426 444 399 444 426 444 426 444 426 417 1324 417 1325 416 426 444 426 444 427 416 427 444 426 444 426 444 399 444 427 443 427 443 426 417 1325 416 1325 416 427 443 427 443 426 444 399 444 427 443 427 444 427 416 427 444 427 443 427 443 400 443 427 443 427 443 400 443 427 443 427 443 427 416 1325 416 427 443 427 443 427 443 400 443 427 443 1298 443 1298 443 427 443 427 416 427 443 427 443 427 443 400 443 427 443 1298 443 427 443 400 443 428 442 427 443 427 416 428 443 427 443 427 443 400 443 1298 443 1298 443 428 442 428 442 428 415 428 442 1299 442\n#\n# Model: Daikin FTXM95PVMA\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 536 358 508 358 508 358 507 359 506 335 530 25024 3539 1656 534 1198 534 360 534 331 477 389 477 1228 533 360 507 358 508 358 508 358 507 1224 508 359 506 1225 506 1226 505 361 504 1229 502 1231 474 1258 474 1258 474 1258 474 392 474 392 474 1258 474 392 474 392 474 392 474 392 474 392 474 392 474 392 474 392 474 392 474 392 474 1259 473 392 474 1259 473 392 474 392 474 392 474 1259 473 1259 473 393 473 392 474 393 473 392 473 1259 473 393 473 393 473 393 473 392 474 393 473 393 473 393 472 393 473 393 473 393 473 393 473 1259 473 1260 472 1259 473 393 473 393 473 1259 473 1260 472 1260 472 35478 3510 1688 474 1259 473 392 474 392 474 392 474 1259 473 392 474 393 473 392 474 392 474 1259 473 393 473 1259 473 1259 473 392 474 1259 473 1259 473 1259 473 1259 473 1260 472 393 473 393 473 1259 473 393 473 393 473 393 473 393 473 393 473 393 473 393 473 393 473 394 472 394 472 394 472 1260 472 394 472 394 472 394 472 394 472 1261 471 395 471 395 471 395 471 418 448 395 471 418 448 418 448 419 447 418 448 419 447 418 448 419 447 418 448 1285 447 419 447 418 448 419 447 418 448 419 446 1285 447 419 447 419 447 1285 447 1285 447 419 447 35479 3510 1689 473 1259 473 393 473 393 473 392 474 1259 473 393 473 393 473 393 473 393 473 1259 473 393 473 1259 473 1259 473 393 473 1259 473 1260 472 1259 473 1259 473 1260 472 393 473 393 473 1260 472 393 473 394 472 394 472 393 473 393 472 393 473 393 473 393 473 393 473 393 473 394 472 394 472 394 472 394 472 394 472 394 472 394 472 394 472 1285 447 418 448 418 448 1285 447 1285 447 1285 447 419 447 419 447 419 447 1285 447 419 447 419 447 1285 447 1285 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 1286 446 1285 447 419 447 1286 446 1286 446 1286 446 1286 446 1286 446 419 447 420 446 420 446 419 447 420 446 419 447 420 446 419 447 420 446 420 446 420 446 420 445 420 446 1286 446 1286 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 1287 445 1287 445 420 446 420 446 420 446 421 445 420 446 421 445 420 446 420 446 420 446 420 445 421 445 421 445 421 445 421 445 421 445 421 445 421 445 1288 444 421 445 421 445 421 445 421 445 421 445 1288 444 1288 444 422 444 1288 444 422 444 421 445 422 443 422 444 422 444 1288 444 422 444 422 444 422 444 422 444 422 444 422 444 422 444 422 444 1288 444 422 444 1289 443 423 443 423 443 1289 443 1289 443 1290 442\n#\n# Model: Daikin FTXS25KVMA\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 427 419 454 418 454 417 455 391 454 418 454 25460 3513 1723 428 1317 428 418 481 391 454 418 427 1317 428 418 454 418 454 418 454 391 454 1290 455 418 454 1291 479 1266 478 395 477 1268 476 1270 474 1272 473 1297 448 1297 448 423 422 424 449 1297 448 423 449 424 448 396 449 423 449 424 449 424 421 424 448 424 449 423 422 424 449 1297 448 424 449 1297 448 424 448 397 448 424 448 1297 448 1297 448 424 448 424 421 424 448 424 448 424 448 397 448 424 448 424 448 424 421 424 448 424 448 424 421 1325 420 425 448 424 448 424 448 1298 447 1298 447 1298 447 397 448 425 447 1298 447 1298 447 1298 447 34988 3533 1730 448 1297 448 424 448 424 421 424 448 1297 448 424 448 424 448 396 449 424 448 1297 448 424 448 1297 448 1297 448 397 448 1297 448 1297 448 1297 448 1297 448 1297 448 424 448 424 448 1297 448 424 421 424 448 424 448 424 448 397 448 424 448 424 448 425 420 424 449 424 448 424 449 1297 448 397 448 424 448 424 448 424 421 1325 420 425 448 424 448 1297 448 424 421 425 447 1297 448 424 448 1297 448 425 448 397 448 1298 447 425 448 424 448 1298 447 425 420 425 447 425 447 425 447 397 448 425 447 1298 447 1298 447 1298 447 425 447 1298 447 35014 3507 1730 448 1297 448 397 448 424 448 424 448 1297 448 424 421 424 448 424 448 424 448 1297 448 397 448 1298 447 1298 447 425 447 1299 445 1300 445 1299 446 1299 446 1299 446 426 446 426 419 1326 419 426 447 426 447 425 447 398 447 426 446 426 447 399 446 426 446 426 446 426 419 427 446 426 447 427 445 399 446 427 445 451 421 428 417 452 420 1325 420 453 420 452 420 1325 420 425 420 1325 420 453 420 452 420 425 420 452 420 452 420 452 393 453 420 452 420 1325 420 1326 419 453 419 426 420 452 420 452 420 453 392 453 419 453 419 452 421 425 420 452 420 452 420 453 392 453 419 1326 419 453 420 1326 419 1326 419 1327 418 1326 419 1327 418 453 392 454 419 453 420 453 420 425 420 453 419 453 419 453 392 453 419 453 420 453 419 426 419 453 419 1326 418 1327 418 454 419 454 391 454 419 453 419 453 392 454 419 453 419 454 419 426 419 454 418 1327 418 1327 418 454 418 455 391 454 419 454 418 454 418 426 419 454 418 454 419 454 391 454 418 454 419 454 391 454 419 454 418 454 418 427 418 454 418 1327 418 455 418 454 391 454 419 454 418 454 418 1328 417 1328 417 428 417 454 419 454 418 454 391 455 418 454 418 455 417 427 418 454 418 454 418 428 417 454 418 455 417 455 390 455 418 455 417 1328 417 455 418 428 417 455 417 1328 417 455 418 1328 417 1328 417\n#\n# Model: Delonghi portable_Pinguino-Air-to-Air-PAC-N81_ac\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 48 12 00 00\ncommand: 88 08 00 00\n#\n# Model: Pinguino PAC_EL275HGRKC\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 82 00 00 00\ncommand: 6B 00 00 00\n#\n# Model: electriQ P15C-V2\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 43 00 00 00\n#\n# Model: Electrol ESV09CRO_B2I\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3086 3063 3089 4438 571 1657 575 529 571 1656 576 529 571 534 576 1650 571 1655 577 527 573 1654 578 526 574 1653 569 1657 575 530 570 536 574 530 570 534 576 1648 573 1653 569 1658 574 530 570 535 575 530 570 534 576 527 573 531 569 535 575 529 571 533 577 526 574 530 570 534 576 528 572 532 568 536 574 529 571 533 578 526 574 530 570 534 576 528 572 1652 569 534 577 1649 573 532 579 526 574 530 570 534 576 527 573 531 569 535 575 528 572 532 568 536 574 529 571 533 577 526 574 530 570 534 577 1647 574 530 570 535 575 529 571 532 568 536 575 529 571 533 577 526 574 530 570 533 577 527 573 530 570 534 576 527 573 531 569 535 575 528 572 532 568 535 575 529 571 532 568 536 574 529 571 533 577 526 574 530 570 533 577 527 573 530 570 534 576 527 573 531 569 534 576 527 573 531 569 535 575 528 572 531 569 535 575 528 572 532 568 535 576 1648 573 531 569 1655 577 1648 573 1651 570 1654 578 1647 574 1651 570 533 577 1648 573 1651 571\n#\n# Model: Emerson EARC8RE1_ac\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3110 1566 525 1084 497 356 471 1085 497 356 472 356 471 1085 497 356 470 357 474 1084 498 356 471 356 471 356 471 356 471 356 496 357 470 357 473 1061 520 357 470 1063 519 357 470 1064 518 1064 518 357 470 357 471\n#\n# Model: Eurom_PAC_9.2\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 04 00 00 00\n#\n# Model: Firstline AAS2500\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3405 1710 463 387 464 388 464 387 464 388 464 389 462 386 465 1224 464 388 463 388 463 389 463 387 464 388 464 387 465 387 464 389 462 389 463 388 464 388 463 1226 462 388 463 1225 463 388 463 387 464 389 462 389 462 388 463 387 464 387 464 389 463 388 463 388 464 1226 462 1224 464 1225 462 387 464 388 464 388 463 388 463 1226 462 387 464 1225 463 388 463 1225 463 388 463 389 463 387 464 1226 462 1224 464 388 463 388 463 387 465 1224 464 388 463 1224 463 388 464 387 464 1224 464 1226 461 387 464 1224 464 388 463 1225 463 1224 464 1224 463 1224 464 1223 464 388 464 387 464 387 464 387 464 389 463 389 462 388 464 388 463 388 463 387 464 387 465 387 464 388 463 387 464 388 463 388 464 388 463 1225 463 388 463 1225 463 1224 463 388 463 388 463 388 464 388 463 387 464 388 464 389 462 389 463 387 464 388 464 388 464 388 463 388 464 387 464 387 465 388 463 387 464 1225 463 1225 463 1225 462 387 464 389 462 388 463 387 465 389 462 388 463 388 464 389 463 388 463 388 464 387 464 389 462 389 463 387 464 388 463 388 463 388 464 389 462 389 463 389 462 389 463 389 462 388 463 1224 464 1225 463 1224 464 389 462 1224 464 394 437\n#\n# Model: Frico PA2510E08\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 272 76189 1130 2004 1129 2008 2141 2008 1127 966 1125 967 2138 992 1094 999 2101 1004 1085\n#\n# Model: Friedrich\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5624 5582 567 553 568 550 571 550 561 557 564 584 537 554 567 1672 571 1671 562 554 567 553 568 1696 537 1676 567 1673 570 1668 565 554 567 556 565 1671 562 557 564 1676 567 1671 562 584 537 555 566 1673 570 552 569 1666 567 1671 562 1676 567 1671 572 548 563 556 565 1672 571 554 567 547 564 556 565 1674 569 549 562 558 563 1676 567 551 570 554 568 548 563 557 564 555 566 554 567 1672 571 1667 566 1674 569 1673 570 545 566 554 567 1671 572 1667 566 554 567 1672 561 557 564 1677 566\n#\n# Model: Friedrich 4235h\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3232 1451 581 1008 580 1007 581 326 494 326 494 326 494 1009 579 325 495 326 494 1034 579 1010 578 325 494 1012 576 326 494 326 494 1013 575 1013 576 326 494 1013 575 1013 575 326 494 326 494 1013 575 326 494 326 494 326 494 1013 575 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 1013 575 326 494 326 494 326 494 326 494 326 494 326 494 326 494 1013 575 1014 574 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 1014 574 1014 574 326 494 326 494 326 494 326 494 1014 574 1014 574 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 1015 573 326 494 1015 573 1015 573 1015 573 70108 3201 1510 574 1014 574 1015 574 326 494 326 494 326 494 1014 574 326 494 326 494 1014 574 1014 574 326 494 1014 574 326 494 326 494 1014 574 1015 573 326 495 1015 573 1015 573 326 494 326 494 1015 573 326 494 326 494 1015 573 326 494 326 494 326 494 327 493 326 494 326 494 326 494 325 495 326 494 325 495 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 1015 573 326 494 326 494 1015 573 1015 573 326 494 326 494 326 494 326 494 1015 573 326 494 1016 572 1016 572 1016 572 1016 572 326 494 326 494 326 494 326 494 1016 572 326 494 1016 572 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 494 326 495 326 494 327 493 326 494 1018 570 326 494 326 494 1018 570 1017 571 326 495 326 494 326 493 326 496\n#\n# Model: Friedrich AKB73756214\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3267 9589 679 1380 626 416 546 470 546 469 547 1512 547 469 571 445 571 445 572 1462 569 1490 570 447 544 473 566 450 567 450 568 449 594 448 568 448 568 448 568 448 569 448 569 448 567 1468 566 450 567 1493 568 449 567 449 568 448 568 1466 567\n#\n# Model: Frigidaire RG15D_AC\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 08 F5 00 00\ncommand: 11 EE 00 00\n#\n# Model: Fujidenzo FEA5001_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9464 4442 637 583 601 584 601 584 601 584 600 582 603 580 604 580 630 555 629 1637 627 1665 598 1668 571 1696 569 1696 594 1672 594 1672 592 1674 593 1673 593 591 594 1673 593 1672 593 591 591 593 569 616 568 616 593 593 591 1674 593 592 593 593 592 1675 592 1675 592 1700 568 1700 568 39559 9409 2272 567 96244 9461 2246 594\n#\n# Model: Fujitsu AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3302 1618 435 393 436 391 438 1207 431 396 433 1213 435 392 437 391 438 388 441 1206 432 1214 434 392 437 389 440 388 431 1215 433 1213 435 392 437 390 439 388 431 396 433 395 434 392 437 390 439 388 431 396 433 394 435 392 437 390 439 388 441 1205 433 395 434 392 437 389 440 388 431 396 433 394 435 392 437 1209 439 388 431 397 432 394 435 393 436 1210 438 387 432 396 433 394 435 392 437 390 439 388 431 1215 433 394 435 1211 437 1208 440 1205 433 1213 435 1211 437 1209 439\n#\n# Model: Fujitsu AC_ASU18RLF\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3306 1624 406 411 413 404 410 1224 414 403 411 1223 405 412 412 405 409 408 406 1228 410 1224 414 402 412 406 408 409 405 1229 409 1224 414 403 411 407 407 410 414 402 412 406 408 409 405 412 412 404 410 408 406 411 413 403 411 406 408 409 405 1230 408 408 406 411 413 404 410 407 407 410 414 403 411 406 408 1226 412 405 409 408 406 411 413 404 410 1224 414 403 411 406 408 409 405 412 412 405 409 408 406 1228 410 407 407 1227 411 1222 406 1229 409 1198 440 1220 408 1227 411\n#\n# Model: GE AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8521 4240 539 1600 542 1572 539 1600 542 567 514 1572 539 570 511 1602 519 1593 600 4181 515 567 514 1599 543 539 511 571 520 563 508 575 485 570 511 572 592 19298 8523 4263 536 1577 544 1595 537 1577 544 564 517 1597 535 548 512 1627 515 1599 512 4245 544 565 485 1627 515 569 512 571 489 567 514 569 512 571 490 567 596 19304 8527 4262 537 1576 545 1595 537 1577 544 565 516 1623 509 548 512 1601 541 1626 485 4245 544 565 485 1601 541 569 512 571 490 567 514 569 512 570 491 566 597 19297 8524 4265 534 1580 541 1598 544 1569 542 567 514 1626 516 541 519 1620 512 1629 492 4238 541 568 492 1594 538 572 509 574 486 571 510 572 509 574 487 570 594\n#\n# Model: GE Window_AC\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 98 6F 00 00\ncommand: 19 E6 00 00\n#\n# Model: Gree airco\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9018 4484 651 553 653 555 650 1657 650 554 652 554 652 1656 652 1656 651 553 653 1656 651 1656 652 1657 651 554 652 553 653 554 652 553 652 557 650 554 652 554 652 554 652 554 652 554 652 1655 652 555 651 555 652 555 651 553 652 556 650 554 652 1656 652 553 653 1655 653 552 654 555 651 1656 652 555 651 19990 652 1657 650 1658 650 553 652 1655 652 554 652 554 652 554 652 554 652 554 652 555 651 554 652 553 654 556 650 553 653 1655 652 554 652 554 652 554 652 555 651 556 651 554 651 553 653 555 651 554 652 554 651 554 652 554 652 556 651 1655 652 553 652 554 651 1656 652\n#\n# Model: Gree airco_lightoff\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9075 4405 725 1580 728 478 728 480 726 481 725 478 728 479 728 479 727 478 728 478 727 1583 724 479 727 1581 727 480 727 480 726 478 727 480 726 479 727 481 725 479 728 478 728 478 728 478 727 479 727 480 726 477 729 478 728 478 728 480 726 1581 726 480 726 1580 727 479 727 478 727 1582 725 480 726 19911 650 1657 650 1657 650 556 650 558 647 556 650 556 649 557 649 555 650 556 649 555 651 556 650 556 650 558 649 555 651 1658 650 555 650 555 650 556 650 555 651 556 650 557 649 556 650 557 649 553 653 555 651 554 651 583 649 555 651 1657 650 555 651 556 650 1656 651\n#\n# Model: Gree KFR-70G-A1\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9081 4414 685 1618 682 509 680 536 654 537 653 537 654 537 653 1675 627 564 626 1676 653 1649 653 1649 653 537 653 538 653 537 653 537 654 537 653 537 654 537 653 537 653 538 653 538 653 537 653 538 653 538 653 537 653 537 654 538 652 538 653 1649 653 538 652 1676 626 538 652 538 653 1676 653 538 652\n#\n# Model: GREE YAPOF\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8995 4545 627 1658 629 1660 627 581 628 554 629 1660 627 1659 628 1685 628 555 628 1659 628 581 628 554 629 1660 627 554 629 581 628 555 628 554 629 581 628 555 628 555 628 581 628 555 628 1660 627 581 628 554 629 555 627 581 628 554 628 556 627 1686 627 555 627 1660 627 557 626 581 628 1658 629 555 628 20037 627 555 628 581 628 555 628 556 627 1687 626 556 627 555 628 582 627 556 627 556 626 582 627 555 628 557 626 581 628 1659 628 555 628 555 628 581 628 554 628 556 627 584 625 556 626 556 627 582 627 557 625 555 627 582 627 555 627 1661 625 1686 627 556 653 1632 655 39992 8996 4518 650 1660 653 1635 627 556 627 583 625 1660 628 1661 626 1660 627 581 628 1658 629 557 625 557 626 1686 627 556 627 555 628 582 627 556 627 556 627 582 627 555 628 557 626 582 627 1661 626 557 626 583 626 556 627 556 627 581 628 556 627 1661 626 1660 627 1687 626 555 628 556 627 1688 625 557 626 20012 626 581 628 556 627 556 626 583 626 556 626 556 627 583 626 556 627 557 626 582 627 555 628 556 627 582 627 556 627 556 627 583 626 556 626 556 627 584 625 556 627 556 627 581 628 1661 626 1661 626 582 627 556 627 555 628 583 626 557 625 1660 627 557 626 583 626\n#\n# Model: Haier AC_HWE08XCR-L\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8714 4338 579 538 551 540 549 542 557 1613 555 1615 553 538 551 540 549 1619 549 1620 558 1612 556 1614 554 1617 551 540 559 1611 557 1612 556 531 558 1613 555 536 553 538 551 1620 558 1612 556 535 554 537 552 535 554 537 552 1618 550 1620 558 533 556 536 553 1617 551 1617 551 1608 549\n#\n# Model: Hisense DG11J1-99_celsius\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8987 4555 560 1694 561 1698 561 574 562 578 562 584 560 586 561 590 560 1705 561 567 563 1698 561 1703 559 577 563 579 564 586 561 588 563 578 562 568 562 572 561 574 563 580 561 582 562 587 561 591 560 579 561 568 562 1699 561 576 561 578 562 581 562 586 561 589 561 580 560 567 563 571 562 576 561 580 561 582 562 587 560 590 561 579 561 567 562 571 562 575 562 578 562 581 563 586 561 588 563 559 563 8017 563 568 561 571 562 574 562 578 562 582 561 585 561 590 561 1702 564 567 562 572 561 574 562 580 560 581 562 584 563 589 562 578 562 569 561 573 561 575 561 577 563 582 562 586 561 589 561 577 563 569 561 571 562 574 562 578 562 582 561 586 561 590 561 579 561 567 563 571 562 575 562 577 563 582 561 585 562 589 562 577 563 568 562 571 562 575 562 578 563 582 562 585 562 589 562 578 562 569 561 571 562 575 562 579 561 581 562 585 562 588 563 578 562 568 562 1697 563 576 561 578 562 580 564 585 562 588 562 1687 562 8017 561 568 561 571 562 576 561 578 562 581 562 585 562 588 563 579 562 569 561 572 562 575 562 579 562 582 562 585 563 589 562 578 562 568 562 571 562 574 563 579 562 582 562 585 563 589 562 580 561 569 561 572 562 574 563 578 562 582 562 584 564 589 562 578 562 567 563 571 563 575 562 1705 562 583 561 584 563 589 562 579 562 568 562 571 562 574 563 578 563 581 563 586 562 588 563 578 563 568 562 572 562 574 563 1706 561 581 563 585 562 588 563 561 561\n#\n# Model: Hisense room_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9115 4510 641 1653 641 1632 614 531 611 539 608 543 608 546 608 550 608 1673 608 529 608 1666 608 1670 607 540 607 544 607 570 584 574 584 564 584 553 584 557 584 1694 584 563 584 567 584 1704 584 1708 583 564 583 1686 584 557 584 560 584 564 583 1701 583 1704 584 1708 584 564 584 553 583 557 583 561 583 564 583 567 584 571 584 574 584 564 583 553 584 557 583 561 583 564 584 568 583 572 583 575 583 546 583 8052 583 554 583 557 583 561 583 565 583 568 583 572 582 575 583 1699 582 1688 583 558 583 561 583 565 582 1702 583 1706 582 575 583 565 583 555 582 558 582 562 582 565 582 569 582 572 582 576 582 566 582 555 582 558 582 562 581 565 582 569 581 573 581 576 581 566 581 555 582 559 581 562 582 566 581 570 581 573 581 577 581 566 581 556 581 560 581 563 581 567 580 570 581 574 580 578 580 1701 580 1690 581 560 580 564 580 1701 580 1704 580 575 580 579 579 568 579 1691 579 561 580 1698 579 1702 578 1705 579 1709 579 580 578 575 554 8080 554 582 555 586 554 589 555 593 554 596 555 600 554 604 554 593 555 1717 554 586 554 590 554 593 554 597 554 601 554 604 554 593 555 583 554 586 554 590 554 594 554 597 554 601 553 604 554 594 553 584 553 587 554 591 553 594 554 598 553 602 553 605 553 595 553 584 553 588 552 592 552 1729 553 599 552 602 553 606 552 595 552 585 552 588 552 592 552 620 527 599 552 628 526 631 527 621 526 1744 526 614 526 618 526 1754 526 624 527 628 526 631 527 604 526\n#\n# Model: Hisense window_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8963 4401 561 543 558 545 566 537 564 540 561 542 559 1652 561 1650 563 540 561 543 558 545 567 537 564 1647 566 1645 558 1654 559 544 567 536 565 1646 567 1644 559 545 567 537 564 1647 566 537 564 540 561 542 559 545 567 537 564 539 562 541 560 544 567 536 565 538 563 541 560 543 558 545 567 538 563 540 561 542 559 545 567 537 564 539 562 542 559 544 568 536 565 539 562 541 560 543 569 536 565 538 563 541 560 543 558 545 566 538 563 540 561 542 559 545 566 537 564 539 562 542 559 544 567 536 565 539 562 541 560 544 568 536 565 538 563 541 560 543 558 545 567 538 563 540 561 543 558 545 567 537 564 540 561 542 559 544 568 536 565 539 562 541 560 570 542 536 565 538 563 541 560 543 558 546 565 538 563 540 561 543 558 571 541 537 564 540 561 542 559 545 567 537 564 539 562 542 559 544 568 536 565 539 562 541 560 544 568 536 565 538 563 541 560 543 569 535 566 538 563 541 560 543 569 535 566 538 563 541 560 543 569 536 565 538 563 541 560 544 567 536 565 539 562 542 559 544 567 537 564 539 562 542 559 544 568 537 564 539 562 542 559 544 568 537 564 539 562 542 559 545 567 537 564 540 561 542 559 545 566 537 564 540 561 543 558 545 567 538 563 541 560 543 569 536 565 538 563 541 560 544 568 536 565 539 562 542 559 544 568 1645 558 546 566 1646 567 537 564 540 561 1651 562 541 560 544 568 563 538 539 562 541 560 544 567 536 565 539 562 541 560 544 567 537 564 539 562 542 559 544 568 537 564 540 561 542 559 545 566 1645 558 546 566 1646 567 537 564 540 561 1651 562 541 560 544 588\n#\n# Model: Hitachi RAK35\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 479 397 30131 50087 3427 1674 447 1253 447 483 446 483 446 483 420 483 447 483 447 483 447 483 447 483 420 510 419 483 447 483 446 1254 446 483 420 510 419 484 446 483 447 484 445 483 446 484 419 1255 445 1254 446 484 445 484 445 484 419 484 446 484 446 484 445 484 446 484 445 1255 418 484 446 1255 445 1255 418 1255 445 1255 445 1254 446 1255 419 484 446 1255 445 1255 418 511 418 485 445 484 445 484 446 484 445 485 418 511 418 484 446 1255 445 1255 418 1255 445 1255 444 1256 444 1256 417 1256 445 1255 443 487 418 511 419 485 444 1255 445 485 445 485 420 509 418 485 445 1255 445 485 444 485 445 1256 417 512 418 485 444 485 445 1256 417 1256 444 485 444 485 420 509 444 486 418 511 418 485 445 485 444 1256 444 486 418 512 417 485 420 509 445 485 445 485 445 485 417 1256 444 486 444 485 445 1256 417 1256 443 486 444 485 444 486 444 485 444 485 418 485 444 485 444 485 444 486 443 486 417 1257 420 509 420 510 444 485 444 486 444 485 418 485 445 485 443 487 444 1256 444 1256 417 485 442 487 420 510 444 485 444 486 417 1256 420 1280 443 1257 417 512 393 510 444 486 442 487 443 486 419 510 417 513 419 484 419 510 444 486 419 510 419 510 393 537 415 488 420 510 418 510 419 510 419 510 392 537 392 511 445 484 419 511 418 511 418 511 392 538 391 511 419 511 419 511 418 511 418 511 391 511 419 512 418 511 418 511 419 511 418 512 391 512 418 512 417 512 417 512 418 512 417 512 391 512 418 512 417 512 417 512 417 512 391 539 390 513 416 513 417 512 417 513 417 537 366 564 365 514 416 537 392 537 392 538 392 538 365 564 365 538 392 538 392 538 391 538 391 538 365 564 365 538 392 538 391 1309 391 1308 365 538 391 538 392 538 391 538 392 538 365 565 364 538 392 538 391 539 391 1309 364 1309 391 538 391 539 391 538 391 538 391 539 364 538 392 539 390 539 391 539 390 539 364 565 364 539 391 539 390 1309 391 539 390 1309 364 539 391 539 390 539 391 539 498\n#\n# Model: Hitach RAK-18QH8\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3295 1683 467 1196 440 407 467 404 470 407 467 405 469 406 467 405 468 409 468 407 467 406 468 405 469 405 469 1168 468 403 470 406 468 411 467 404 470 406 468 404 469 406 468 405 469 406 467 404 470 409 469 405 469 404 470 405 468 406 468 407 467 405 468 1167 469 410 467 1168 468 1167 469 1167 469 1167 468 1168 468 1167 469 407 467 1173 467 1168 468 1169 467 1166 470 1168 468 1168 468 1168 468 1167 469 1171 469 405 469 409 464 404 470 432 441 405 469 404 470 407 467 410 468 404 470 405 468 1169 467 1168 468 404 469 405 469 1167 469 1169 471 1167 469 1167 469 405 469 403 470 1167 469 1168 468 406 468 409 469 404 469 1168 468 404 469 404 469 1168 468 406 467 406 468 1171 469 1169 467 404 469 1167 469 1167 469 404 469 1169 467 1195 441 409 469 1169 467 1168 468 405 469 404 470 1168 468 404 470 405 469 409 469 405 469 404 470 1167 469 1169 467 403 470 1169 467 1168 468 1171 469 404 469 405 469 1168 468 1168 468 1168 468 404 470 1169 467 408 469 1169 467 1167 469 404 470 405 469 405 468 1168 468 405 468 1173 467 406 467 406 468 407 466 406 468 406 468 406 468 406 468 410 468 1168 468 1168 468 1168 468 1169 467 1169 467 1168 468 1169 467 1172 468 405 468 406 468 407 467 407 467 406 468 405 469 406 468 411 466 1168 468 1167 469 1168 468 1169 467 1195 440 1169 467 1168 468 1171 469 405 469 407 467 407 467 406 467 407 467 406 468 406 467 410 468 1168 468 1169 467 1169 467 1169 467 1168 468 1168 468 1167 469 1172 468 407 467 404 469 405 469 405 468 406 468 406 468 406 467 410 468 1169 467 1169 467 1168 468 1168 468 1167 469 1168 468 1167 469 1172 468 405 468 405 469 407 466 406 468 407 466 406 468 406 468 410 468 1168 468 1168 468 1170 466 1168 468 1168 468 1168 468 1168 468 1173 467 1168 468 405 468 1169 467 406 467 406 468 1168 468 406 468 410 467 408 466 1168 468 407 467 1168 468 1170 466 406 468 1169 467 1173 467 1169 467 405 468 406 467 407 467 1170 466 1170 466 1170 466 1173 467 406 468 1169 467 1171 465 1169 467 406 468 406 467 406 467 411 467 406 468 406 467 407 467 407 466 407 467 407 467 408 466 412 465 1170 466 1170 466 1169 467 1169 467 1170 466 1170 466 1169 467 1174 466 407 466 408 466 408 466 407 466 407 467 407 466 406 468 411 467 1170 466 1170 466 1171 465 1168 468 1170 466 1169 467 1170 466 1174 466 407 467 408 466 407 466 407 467 407 467 408 466 407 466 412 466 1171 465 1169 467 1170 465 1170 466 1171 465 1170 465 1171 465 1174 466 1170 466 1169 467 408 465 408 465 408 465 408 466 408 465 414 464 408 466 407 467 1170 466 1170 466 1170 466 1170 466 1171 465 1174 465\n#\nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3376 1605 463 1172 464 411 463 408 466 407 466 412 462 409 465 409 465 415 463 409 464 411 463 408 465 409 464 1171 465 410 463 410 464 413 464 437 437 411 462 410 464 409 464 410 463 410 464 410 463 413 465 409 465 413 460 410 464 409 465 410 464 409 465 1173 463 415 462 1171 465 1174 462 1174 462 1173 463 1173 463 1174 462 412 462 1177 463 1175 461 1173 462 1172 464 1173 463 1174 462 1172 464 1174 462 1177 463 411 463 409 464 410 464 411 463 410 464 410 463 411 463 414 463 410 463 412 462 1174 462 1174 462 410 464 412 462 1173 463 1177 463 1174 462 1173 463 411 462 411 463 1174 462 1174 462 413 460 417 461 414 459 1174 462 411 463 413 460 1174 462 412 461 413 461 1179 461 1175 461 412 462 1172 464 1174 462 414 460 1174 462 1172 464 414 464 1172 464 1172 464 409 465 409 465 1172 464 409 464 408 466 415 463 408 465 410 464 1171 465 1171 465 408 466 1172 464 1172 464 1176 464 409 464 409 465 409 465 409 465 409 465 409 465 1172 464 412 466 1172 464 1171 465 1174 462 1171 465 1172 464 1172 464 409 465 1176 464 411 463 408 465 408 466 408 466 409 465 409 465 409 464 414 464 1172 464 1172 464 1172 464 1172 464 1173 463 1171 465 1174 462 1177 462 409 465 409 465 411 462 410 464 410 464 409 465 409 464 414 464 1172 464 1172 464 1172 464 1171 465 1173 463 1173 463 1172 464 1176 464 409 464 410 464 410 463 410 463 410 464 410 464 409 464 414 464 1172 464 1173 463 1173 463 1172 464 1172 464 1173 463 1173 463 1176 464 410 463 411 463 411 463 409 465 409 464 411 462 411 463 414 463 1173 463 1172 464 1173 463 1172 464 1172 464 1173 463 1174 461 1178 462 410 463 410 463 412 462 411 463 410 463 409 464 411 463 415 463 1173 463 1174 462 1173 463 1173 462 1173 463 1174 462 1172 464 1178 462 1171 465 1174 462 411 462 411 463 1174 462 410 463 1173 463 414 463 412 462 410 463 1174 462 1173 463 411 463 1173 463 410 464 1177 463 1174 462 411 463 411 462 411 463 1174 462 1174 462 1175 461 1178 462 412 461 1173 463 1172 464 1175 461 412 462 411 462 411 463 415 462 411 463 412 462 412 461 412 462 412 462 411 463 412 462 416 461 1174 462 1173 463 1174 462 1174 461 1174 462 1175 461 1174 462 1181 459 412 462 411 463 412 462 412 461 411 462 411 463 413 460 415 463 1174 462 1175 461 1176 460 1176 460 1174 462 1174 461 1174 462 1178 461 413 461 413 460 413 460 414 460 412 461 412 462 412 462 417 460 1175 461 1175 461 1175 461 1175 460 1176 460 1175 461 1174 462 1179 461 1175 460 1177 435 436 461 413 460 413 437 437 460 412 438 440 461 414 436 437 437 1199 460 1177 459 1176 460 1176 437 1199 437 1202 437\n#\nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3376 1603 465 1198 438 408 489 384 490 382 468 407 467 408 466 407 467 439 438 408 490 384 489 384 466 435 439 1169 467 408 466 408 489 388 467 411 463 406 467 409 465 408 490 385 465 409 488 383 467 412 489 383 467 407 466 409 465 408 466 408 466 408 466 1173 463 412 466 1171 465 1170 466 1171 465 1170 466 1170 466 1170 466 408 466 1175 465 1169 467 1171 465 1171 465 1173 463 1172 464 1170 466 1170 466 1176 463 409 465 409 465 410 463 407 467 409 465 411 462 407 467 413 464 407 466 410 464 1172 464 1173 463 408 466 411 463 1171 465 1175 465 1171 465 1171 465 408 465 410 464 1171 465 1172 463 408 465 413 465 408 466 1171 465 409 465 408 465 1173 463 410 464 408 465 1175 465 1169 467 409 464 1172 464 1172 463 408 466 1172 464 1172 464 412 466 1171 465 1172 464 409 464 409 464 1171 465 411 462 410 464 414 463 409 464 411 463 1172 463 1174 462 411 463 1173 463 1173 463 1177 463 410 463 411 463 1172 464 1173 463 1178 458 411 462 1172 464 416 462 1174 462 1173 463 412 461 411 463 411 463 1174 462 411 462 1178 462 411 462 410 464 413 461 411 463 410 464 412 462 411 463 417 461 1173 463 1175 461 1173 463 1173 463 1172 464 1173 463 1175 461 1177 463 411 463 410 464 410 463 410 464 410 464 411 463 410 463 414 464 1173 463 1173 463 1174 462 1172 464 1175 461 1173 463 1175 461 1176 464 411 463 410 463 412 461 411 462 411 463 411 462 414 460 415 463 1174 462 1174 462 1173 463 1173 463 1174 462 1173 463 1173 463 1178 462 410 463 411 463 411 463 411 463 412 462 410 463 411 462 416 462 1173 463 1174 462 1174 462 1174 462 1173 463 1174 462 1173 463 1177 463 410 463 438 436 410 463 412 462 410 463 411 463 412 461 416 462 1174 462 1174 462 1175 461 1173 463 1174 462 1173 463 1174 462 1179 461 1175 461 1174 462 411 463 412 462 1176 459 412 462 1174 461 415 463 411 462 411 463 1175 461 1174 462 411 463 1175 460 412 462 1178 461 1176 460 411 463 412 461 412 462 1174 462 1174 462 1174 462 1179 461 412 461 1174 462 1174 462 1175 461 413 460 412 462 412 462 417 461 411 463 412 462 412 461 413 460 412 462 412 462 413 461 416 461 1176 461 1175 461 1174 462 1175 461 1174 462 1176 460 1174 461 1180 460 412 461 414 460 412 462 414 459 413 461 413 461 413 460 417 461 1176 459 1176 460 1177 459 1176 460 1175 460 1175 461 1176 437 1203 460 414 459 413 461 413 460 413 437 436 461 413 438 437 437 440 461 1176 437 1198 438 1198 438 1198 461 1175 461 1177 459 1177 436 1203 437 1201 458 1175 438 436 438 436 438 437 436 437 437 436 437 441 437 438 436 436 438 1198 438 1200 436 1199 437 1199 437 1199 437 1203 436\n#\nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3297 1684 411 1224 412 461 413 462 412 460 413 461 413 461 413 463 411 466 412 461 412 461 413 461 413 461 413 1223 413 461 412 465 409 466 412 461 412 462 467 406 412 461 413 462 467 407 466 406 412 465 467 406 468 406 468 406 468 405 468 408 466 406 467 1168 412 493 440 1167 469 1168 468 1168 468 1167 468 1168 468 1168 468 407 466 1171 469 1167 469 1170 466 1168 468 1168 468 1168 468 1168 468 1168 468 1173 467 407 466 407 467 408 465 406 468 406 468 404 470 405 469 409 469 405 468 406 468 1168 468 1167 469 406 468 405 468 1166 470 1172 468 1166 470 1171 465 406 467 406 468 1168 468 1168 468 405 469 410 467 405 469 1167 469 406 468 405 469 1168 468 405 468 406 468 1173 467 1169 467 406 467 1169 467 1169 467 407 467 1169 467 1169 467 412 466 1167 469 1169 467 408 466 407 467 1169 467 408 465 408 466 411 466 407 467 409 465 1171 465 1169 467 406 467 1197 439 1171 465 1174 466 434 440 405 468 406 468 405 469 407 467 407 466 406 468 1172 468 1170 466 1168 468 1168 468 1167 469 1169 467 1167 469 1171 465 411 467 405 469 405 469 408 466 405 469 407 466 405 469 405 469 410 468 1168 468 1168 468 1168 468 1169 467 1169 467 1168 468 1169 467 1173 467 406 468 405 468 406 467 407 467 405 469 406 468 406 467 411 467 1169 467 1168 468 1168 468 1167 469 1168 468 1168 468 1169 467 1173 467 406 468 405 468 404 470 405 469 406 468 405 468 406 468 409 468 1168 468 1168 468 1169 467 1169 467 1168 468 1170 465 1169 467 1172 468 405 468 407 467 406 467 406 467 407 467 406 467 406 468 410 467 1168 468 1170 466 1168 468 1168 468 1169 467 1169 467 1168 468 1173 467 406 468 406 468 407 467 406 468 408 466 406 468 406 468 411 466 1170 466 1170 466 1168 468 1169 467 1169 467 1170 466 1169 467 1174 466 406 468 1169 467 1168 468 406 468 1168 468 406 468 1169 467 410 467 1170 466 407 467 406 468 1169 467 406 467 1169 467 407 466 1172 468 1170 466 409 465 407 467 407 466 1169 467 1170 466 1169 467 1173 467 407 466 1172 464 1171 465 1169 467 407 466 407 467 406 467 411 467 406 468 408 465 407 467 407 467 407 467 407 467 407 466 412 466 1169 467 1170 466 1170 466 1169 467 1169 467 1169 467 1170 466 1174 466 408 465 408 466 408 466 407 466 409 465 406 467 409 465 411 466 1170 466 1170 466 1170 466 1171 465 1170 466 1171 465 1170 466 1174 466 407 466 408 466 408 466 408 465 407 467 408 466 407 467 411 466 1169 467 1170 466 1170 466 1170 466 1171 465 1170 466 1170 466 1174 466 1170 466 1170 466 408 466 409 465 407 467 407 466 409 465 412 466 409 465 409 465 1171 465 1170 466 1171 465 1171 465 1171 465 1175 464\n#\nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3376 1602 466 1169 467 410 463 406 468 405 469 406 468 408 466 407 466 412 466 407 467 407 467 407 466 408 466 1171 465 406 468 406 467 411 467 407 467 407 467 407 467 407 467 409 465 406 467 406 467 412 466 406 468 406 468 407 467 406 467 407 467 407 466 1170 466 410 467 1171 465 1170 466 1169 467 1169 467 1171 465 1171 465 408 466 1174 466 1170 466 1169 467 1171 465 1169 467 1171 465 1169 467 1172 464 1173 467 408 466 408 466 410 464 407 467 408 465 409 465 407 466 412 466 408 465 409 465 1172 464 1171 465 408 466 411 463 1170 466 1175 464 1173 463 1171 465 408 465 408 466 1171 465 1172 464 411 463 412 466 409 465 1170 466 408 466 408 466 1171 465 408 465 409 465 1175 464 1171 465 410 463 1171 465 1171 465 408 466 1171 465 1171 465 412 465 1169 467 1170 466 408 466 410 464 1170 466 409 464 408 466 412 466 408 466 407 467 1171 465 1170 466 407 467 1171 465 1171 465 1175 465 436 438 408 466 1170 466 1170 466 1171 465 408 466 1170 466 413 464 1172 464 1173 463 409 464 408 466 411 462 1172 464 409 465 1177 463 409 465 410 464 409 464 409 465 409 465 410 464 409 465 414 464 1172 464 1172 464 1173 463 1172 464 1171 465 1173 463 1170 466 1177 463 409 464 410 464 408 465 437 437 410 464 410 464 410 464 413 464 1171 465 1172 464 1171 465 1173 463 1172 464 1175 461 1173 463 1176 464 410 464 410 464 411 462 408 466 410 464 409 464 410 464 414 464 1173 463 1172 463 1173 463 1175 461 1172 464 1172 464 1173 463 1177 463 411 463 410 464 410 463 410 464 409 465 410 464 410 464 414 463 1172 464 1173 463 1174 462 1173 463 1173 463 1173 463 1174 462 1178 462 409 464 410 464 411 462 412 462 411 462 412 462 411 462 414 463 1173 463 1172 464 1173 463 1173 463 1174 462 1172 464 1173 462 1177 463 412 461 1174 462 1174 462 411 463 1173 462 411 463 1175 461 415 463 1173 463 411 462 412 462 1174 462 411 463 1173 463 411 463 1178 462 1173 463 412 462 410 463 411 463 1174 462 1173 463 1174 462 1178 462 411 462 1174 462 1174 462 1174 461 412 462 412 462 411 463 416 462 412 462 411 462 412 462 412 461 411 463 412 462 412 461 416 461 1174 462 1174 462 1175 461 1175 461 1175 461 1174 462 1174 462 1179 461 412 461 413 461 412 461 412 462 412 461 414 460 412 461 416 462 1176 460 1174 462 1174 462 1175 460 1174 462 1175 460 1177 459 1179 461 412 461 413 461 413 460 413 461 413 461 412 461 412 462 416 461 1176 460 1174 462 1174 462 1175 461 1176 459 1177 459 1175 437 1203 460 1175 461 1176 436 438 459 414 460 414 460 413 460 414 459 418 437 437 437 437 437 1199 437 1200 436 1200 436 1200 436 1200 436 1202 437\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3378 1603 466 1170 466 408 465 406 468 407 467 407 467 408 466 407 467 412 466 407 467 408 465 406 468 406 468 1170 466 407 467 406 468 413 465 410 464 407 466 409 465 407 467 406 468 409 464 408 466 411 467 407 466 405 469 407 467 406 467 408 466 406 468 1170 466 412 466 1169 466 1171 465 1171 465 1170 489 1146 466 1172 488 383 467 1177 487 1149 487 1145 466 1172 487 1147 465 1169 466 1174 486 1147 465 1174 466 409 465 409 465 409 464 409 464 408 465 409 465 409 464 415 463 409 464 409 465 1170 466 1171 465 409 465 410 463 1172 463 1177 463 1172 464 1199 436 413 461 410 464 1172 463 1172 464 411 463 415 463 411 463 1173 463 411 462 410 464 1174 461 409 465 411 463 1176 464 1173 463 410 464 1172 464 1172 464 437 436 1174 462 1173 462 415 463 1174 462 1173 463 411 463 413 461 1172 464 411 462 410 463 415 463 409 465 411 463 1172 464 1172 464 409 465 1173 463 1174 462 1176 464 409 465 409 465 1171 465 1170 466 1173 463 408 466 1172 464 412 465 1171 465 1172 464 407 467 408 465 408 466 1171 465 408 466 1175 465 408 465 407 466 408 466 409 465 408 465 408 466 407 467 412 466 1170 466 1173 463 1170 466 1170 466 1170 466 1170 466 1171 465 1174 466 408 465 407 467 409 465 408 465 409 464 408 466 408 466 414 464 1171 465 1170 466 1171 465 1170 466 1172 464 1171 465 1170 466 1176 464 409 465 410 463 408 466 411 463 409 464 409 465 408 466 413 464 1170 466 1172 464 1171 465 1170 466 1171 465 1172 464 1170 466 1178 462 408 465 410 464 409 465 408 466 409 465 409 465 409 465 413 465 1171 465 1172 464 1172 464 1173 463 1171 465 1172 464 1171 465 1176 464 407 467 409 465 409 465 410 463 409 465 409 465 411 463 413 464 1173 463 1174 462 1172 464 1172 464 1171 465 1171 465 1172 464 1175 465 410 464 1172 464 1174 462 409 465 1172 464 410 463 1170 466 414 463 1172 464 410 464 409 465 1172 464 411 462 1172 464 409 465 1175 465 1172 464 410 463 410 464 410 464 409 464 1173 463 1173 463 1176 464 410 464 1173 463 1171 465 1171 465 1172 464 409 464 410 464 414 464 410 464 410 464 410 464 409 465 410 463 410 464 411 463 414 464 1173 463 1172 464 1173 463 1172 464 1172 464 1173 463 1172 464 1177 463 410 464 410 463 410 463 411 463 410 464 411 463 409 464 414 464 1173 463 1173 462 1174 462 1173 463 1173 463 1173 463 1174 462 1177 463 411 463 412 462 410 463 411 463 411 462 412 462 410 463 414 464 1173 463 1174 462 1173 463 1172 464 1174 462 1173 463 1173 463 1178 462 1173 463 1174 462 411 462 411 463 411 462 410 464 411 463 415 463 411 463 412 462 1174 462 1173 463 1174 462 1175 461 1173 463 1178 461\n#\n# Model: Inventum AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4419 4378 562 1618 568 552 541 1619 567 1640 536 533 560 535 558 1649 537 559 534 537 566 1619 567 554 539 530 563 1618 568 1613 563 532 561 1626 560 537 566 554 539 530 563 557 536 536 1622 564 1616 560 1624 562 1625 561 1620 566 1615 561 1621 565 1616 560 534 559 536 567 529 564 559 534 1625 561 533 560 1625 561 1620 566 528 565 530 563 1620 566 530 563 532 561 1624 562 532 561 1650 536 1645 541 1614 562 1620 566 5226 4418 4372 558 1622 564 557 536 1623 563 1618 568 527 566 529 564 1617 559 564 539 531 562 1623 563 558 535 533 560 1621 565 1616 560 535 568 1619 567 529 564 557 536 532 561 560 533 561 542 1614 562 1619 567 1642 534 1628 569 1613 563 1619 567 1614 562 1619 567 528 565 530 563 533 560 563 540 1619 567 527 566 1619 567 1614 562 533 560 535 558 1625 561 536 567 527 566 1619 567 528 565 1620 566 1615 561 1620 566 1618 558\n#\n# Model: Kelon AS-24HR4SQJUL\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9159 4545 562 1674 562 1675 561 596 560 595 535 621 535 621 535 623 533 1676 560 597 535 1702 534 1703 533 622 535 622 535 622 509 648 508 597 560 596 560 596 558 1679 534 623 534 622 535 622 534 622 509 597 560 597 560 1678 558 598 558 599 532 1728 508 649 508 1729 507 597 560 597 558 598 534 623 534 623 533 623 531 625 508 649 507 597 560 597 534 623 534 623 534 623 534 623 508 649 508 649 508 571 508 8132 558 1703 533 1703 534 623 534 623 533 623 508 625 532 649 508 1677 560 597 558 599 533 1703 534 623 533 1703 533 1705 532 625 532 597 534 623 534 623 533 599 558 599 532 625 532 624 532 624 533 574 557 599 558 599 558 598 557 599 533 624 532 624 533 624 531 575 558 599 558 598 558 1703 534 1703 533 624 507 649 508 649 507 598 559 598 533 624 532 624 533 624 532 624 507 650 507 650 507 1678 559 1678 559 598 557 599 533 1705 532 1729 507 625 532 626 531 598 556 601 533 624 533 1704 532 624 532 1704 533 1704 533 1705 531 573 506 8133 558 598 558 598 533 624 532 624 533 624 532 624 507 650 507 599 558 1678 558 599 557 599 532 624 533 624 533 624 530 627 506 599 505 575 557 599 557 599 558 599 532 625 532 625 531 625 531 574 505 599 558 599 532 625 531 625 532 624 532 625 506 651 506 599 480 600 557 599 558 599 557 600 532 625 532 625 532 626 505 600 505 600 531 625 532 625 531 625 532 624 506 650 507 650 507 600 479 1681 556 600 557 600 556 600 531 626 530 650 507 626 530 574 506\n#\n# Model: Unknown Model_1\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3150 2991 3125 4400 619 503 617 520 591 525 591 525 591 1621 616 1613 615 1619 640 517 589 1640 588 1645 587 1643 585 1649 584 533 583 533 583 533 583 1650 583 533 583 533 583 533 583 533 583 1650 583 1646 583 1650 583 1655 583 533 583 533 584 1654 584 1646 583 1650 583 1650 583 533 584 533 583 533 583 538 583 529 583 533 583 1651 582 1651 582 1651 582 1655 583 1646 583 534 582 1655 583 1651 582 534 582 1655 583 530 582 534 582 534 582 1655 583 529 583 1651 582 1655 582 530 582 1651 582 534 582 40182 3091 3040 3116 4436 583 533 583 538 583 529 583 533 583 1650 583 1651 583 1650 583 533 583 1650 583 1650 583 1655 583 1646 583 533 583 534 583 533 583 1651 582 533 583 534 582 534 582 533 583 1651 582 1655 583 1646 583 1651 583 534 582 534 582 1651 582 1656 581 1647 582 1656 582 530 582 534 582 534 583 534 582 539 582 534 582 1647 582 1656 582 1647 582 1651 582 1652 581 535 581 1652 581 1647 582 535 582 1652 581 535 581 535 581 535 581 1652 581 535 581 1652 581 1652 581 535 581 1653 580 535 581\n#\n# Model: Legion LE-F30RH-IN\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6172 7369 602 1569 602 1569 602 1569 601 1570 573 1598 574 1598 573 1597 574 1598 574 526 573 526 573 527 572 528 571 529 570 529 570 530 569 530 568 1603 569 1603 569 1603 569 1603 569 1603 568 1603 568 1603 569 1604 568 531 568 531 568 531 568 532 567 531 568 555 544 532 567 555 543 1627 544 1628 543 1628 543 1628 543 1628 544 1628 543 1628 544 1629 543 556 543 556 543 556 543 556 543 556 543 556 543 556 543 555 543 1627 544 1628 543 1629 543 556 543 555 543 1628 543 1628 543 1629 543 556 543 556 544 555 544 1628 544 1629 543 556 543 556 543 556 543 556 543 556 543 555 543 1628 543 1629 543 555 543 1628 543 1628 543 1628 543 1628 543 1629 543 557 542 556 543 1629 543 556 543 556 543 555 543 1630 542 556 542 1629 543 557 543 1630 543 557 543 1630 543 1631 542 557 542 1631 542 557 543 1630 543 557 542 1630 543 558 542 7398 543\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6173 7370 600 1571 656 1515 603 1569 602 1570 600 1571 573 1598 574 1598 573 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 530 569 530 569 1603 569 1603 569 1603 569 1603 569 1603 568 1603 569 1603 569 1604 568 531 569 531 568 531 568 531 568 532 567 555 544 532 567 555 544 1603 568 1604 567 1604 567 1605 567 1628 544 1605 567 1628 543 1629 543 556 544 555 544 556 543 556 543 556 543 556 544 556 543 555 544 1629 543 555 544 1629 543 556 543 556 543 556 543 555 543 1629 543 556 544 1630 543 556 544 1629 544 1629 544 1629 544 1630 543 557 543 556 544 1629 543 1630 543 556 544 1629 543 1630 543 556 544 1629 543 1630 543 557 544 556 543 1630 543 557 543 557 543 1630 543 557 543 557 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 1629 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 1631 543 557 544 7399 543\n#\n# Model: Lennox AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4477 4354 606 1548 606 472 605 1551 603 474 578 501 576 524 553 524 553 1602 552 1602 553 525 552 1603 552 526 551 526 551 526 551 526 551 526 551 526 551 1604 551 1604 551 526 551 526 551 1604 551 1604 551 526 551 1604 551 526 551 526 551 526 551 526 551 526 551 526 551 1604 551 1604 551 526 551 526 551 526 551 526 551 1604 576 501 577 1579 576 501 551 1604 575 1579 576 1579 551 526 551 1604 551 1604 551 1604 551 5205 4446 4385 575 501 576 1579 576 501 576 1579 576 1579 576 1578 576 1579 576 501 576 501 576 1578 576 501 576 1579 576 1578 577 1578 577 1578 576 1578 577 1578 577 501 576 501 576 1578 577 1578 577 501 576 501 576 1578 576 501 576 1578 577 1579 576 1578 577 1579 576 1578 577 1578 576 501 576 501 576 1579 576 1579 576 1579 576 1579 576 501 576 1578 576 501 576 1579 576 501 576 501 576 501 576 1579 576 501 576 501 576 502 575\n#\n# Model: LG AC\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 81 66 00 00\ncommand: 81 7E 00 00\n#\n# Model: LG AC_2\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8455 4196 542 1566 541 539 519 535 513 541 517 1565 541 538 520 534 514 540 518 1563 544 1565 542 538 520 533 515 539 519 535 513 541 517 536 522 532 516 538 520 533 515 539 519 535 513 1569 538 542 516 1566 541 539 519 534 514 540 518 1564 542\n#\nname: Dh\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 14 EB 00 00\n#\n# Model: LG LP1417GSR_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8971 4413 598 1634 595 1641 599 526 599 529 596 533 592 541 594 541 594 1646 594 525 600 1633 596 1645 595 533 592 540 595 538 597 539 596 528 597 1629 600 520 594 526 599 526 599 530 595 537 598 538 597 530 595 523 591 1639 601 524 601 527 598 1643 597 537 598 539 596 531 594 522 592 528 597 527 598 530 595 536 599 534 591 546 600 526 599 517 597 521 593 528 597 528 597 532 593 541 594 544 591 519 595 7883 598 520 594 525 600 524 590 535 600 529 596 539 596 1651 599 1639 601 515 599 522 592 531 594 532 593 538 597 536 599 538 597 528 597 518 596 525 600 523 591 534 591 537 598 533 592 546 600 527 598 518 596 523 591 531 594 531 594 536 599 534 591 545 601 526 599 517 597 521 593 529 596 530 595 536 599 532 593 542 593 532 593 522 592 526 599 522 592 535 600 532 593 540 595 541 594 533 592 524 601 519 595 528 597 529 596 535 600 534 601 537 598 527 598 1627 592 1637 592 531 594 533 592 1652 598 537 598 1652 598 1623 596 7882 599 519 595 525 600 522 592 534 591 538 597 535 590 547 599 528 597 518 596 1634 595 1639 601 525 600 530 595 539 596 541 594 532 593 523 591 529 596 527 598 528 597 533 592 541 594 542 593 532 593 525 600 522 592 531 594 533 592 538 597 535 600 537 598 527 598 518 596 522 592 530 595 1646 594 1652 598 1650 600 538 597 530 595 521 593 527 598 526 599 529 596 534 591 543 592 546 600 527 598 519 595 1636 593 1640 600 1639 601 1644 596 1651 599 539 596 514 590\n#\n# Model: LG LP1419IVSM\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3196 9607 615 1444 589 428 588 428 588 430 613 1444 589 428 588 428 587 430 588 1446 614 1445 589 429 588 429 588 429 584 434 611 406 582 483 561 455 561 456 560 456 558 460 557 459 558 1503 559 457 533 1527 559 457 559 457 586 457 560 1475 584\n#\n# Model: LG R12AWN-NB11\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8838 3941 624 1427 514 500 515 497 541 473 540 1487 540 474 539 474 538 476 512 1538 514 1516 544 466 514 497 514 501 514 499 514 500 513 499 513 501 487 525 514 499 512 502 487 526 512 1517 542 470 487 1539 513 499 512 503 513 499 512 1513 512\n#\n# Model: LG SX122CL\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3160 1550 576 1092 576 1091 577 329 555 338 573 329 558 1065 601 329 557 329 556 1065 601 1066 575 338 547 1093 574 339 546 339 546 1095 573 1095 572 339 546 1096 572 1097 571 341 544 341 544 1124 544 342 543 342 543 1125 542 344 542 344 541 344 542 344 542 343 541 344 542 344 542 344 542 344 542 344 542 344 542 343 541 344 542 344 542 344 542 344 542 344 542 344 542 343 541 345 542 1126 542 343 541 345 541 1126 542 1126 542 344 542 344 542 344 542 343 541 344 542 345 541 1126 541 1126 541 1127 542 1127 541 343 542 345 541 344 542 345 542 1125 541 345 542 1127 541 343 542 345 541 345 541 345 541 345 541 345 541 345 541 343 541 345 541 345 541 345 541 345 541 345 541 345 541 344 541 345 541 345 541 345 541 345 541 345 541 345 541 344 540 345 541 345 541 345 541 345 541 345 541 344 541 345 541 345 541 345 541 346 540 345 541 344 540 345 541 346 540 345 541 345 541 345 540 1128 541 1126 541 345 541 346 541 1126 541 345 541\n#\n# Model: Lifetime Air\n#\nname: Off\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\n#\nname: Dh\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 57 00 00 00\n#\n# Model: Logik HLF-20R\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1286 428 1251 408 442 1241 1289 399 1280 431 408 1247 1283 431 408 1248 442 1242 437 1246 443 1240 439 8135 1279 434 1256 430 409 1246 1284 430 1260 426 413 1242 1287 426 413 1243 436 1247 443 1241 438 1245 445 8130 1284 428 1262 424 415 1240 1279 435 1255 430 409 1247 1283 430 409 1247 443 1241 438 1245 434 1249 441 8133 1281 432 1258 428 411 1244 1286 428 1251 407 443 1240 1279 434 416 1240 439 1244 435 1249 441 1243 436 8137 1287 400 1279 433 417 1239 1280 433 1257 430 409 1246 1284 429 410 1246 444 1240 439 1245 445 1239 440 8133 1281 405 1285 428 411 1245 1285 429 1261 425 414 1241 1289 425 414 1242 437 1246 444 1240 439 1245 434 8139 1285 428 1251 435 415 1240 1279 434 1256 430 409 1246 1283 430 409 1247 443 1241 438 1245 445 1239 440 8133 1281 432 1258 428 411 1244 1286 428 1251 407 443 1240 1279 434 416 1240 439 1244 435 1249 441 1243 436 8137 1287 426 1253 432 407 1248 1281 432 1258 401 438 1244 1285 427 412 1244 435 1248 442 1242 437 1247 442 8131 1283 403 1287 426 413 1242 1287 426 1253 433 417 1239 1280 432 418 1238 441 1243 436 1247 442 1241 438 8136 1288 398 1281 404 435 1248 1281 404 1286 400 439 1244 1285 400 439 1244 435 1249 441 1243 436 1247 442 8131 1283 404 1285 400 439 1243 1286 401 1278 407 443 1240 1289 397 442 1240 439 1245 434 1249 441 1243 436 8138 1286 401 1278 407 443 1240 1279 407 1283 403 436 1246 1283 403 436 1246 444 1240 439 1245 434 1249 441 8134 1280 406 1284 402 437 1246 1283 402 1288 398 441 1242 1288 399 440 1242 437 1247 443 1241 438 1245 434 8140 1284 402 1288 398 441 1242 1287 425 1254 432 407 1249 1281 432 407 1249 441 1243 436 1247 443 1241 438 8136 1288 425 1254 405 434 1248 1281 406 1284 401 438 1245 1285 402 437 1246 433 1250 440 1244 435 1248 442\n#\n# Model: Midea AC_MAW05R1WBL\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4459 4372 589 1565 597 480 595 1559 592 485 590 487 588 488 598 479 596 1558 593 1562 589 1565 597 1558 593 483 593 485 590 486 589 488 598 479 596 480 595 1559 592 485 590 486 589 1565 597 481 594 1559 592 485 590 1564 598 479 596 481 594 483 592 484 591 1563 588 489 597 1558 593 1561 590 486 589 488 598 479 596 481 594 483 592 484 591 1563 599 478 597 480 595 482 593 483 592 485 590 486 589 488 588 489 597 5158 4462 4367 594 483 592 1563 588 488 598 1557 594 1560 591 1563 599 1556 595 482 593 484 591 485 590 487 588 1566 596 1559 592 1562 589 1565 597 1558 593 1562 589 488 598 1556 595 1559 592 485 590 1564 598 479 596 1558 593 484 591 1563 588 1566 596 1559 592 1562 589 488 598 1557 594 482 593 484 591 1563 588 1566 596 1559 592 1563 588 1565 597 1558 593 484 591 1563 588 1566 596 1559 592 1562 589 1565 597 1558 593 1561 590 1564 598\n#\n# Model: Midea silent_cool_26_pro\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4528 4233 682 1461 684 389 683 1461 684 390 737 335 681 391 681 390 682 1461 683 1461 684 1461 683 1461 683 392 626 468 603 469 603 468 603 469 628 443 629 1515 629 443 628 444 627 445 626 1518 626 1519 625 1519 626 1519 626 446 626 446 625 446 625 446 626 446 625 1519 625 1519 626 1519 626 446 626 446 625 446 626 446 625 446 626 446 626 1519 625 446 625 446 626 446 625 446 625 1519 625 447 625 1519 626 446 626 5105 4471 4290 625 447 625 1520 625 447 625 1519 625 1520 624 1520 625 1520 625 447 625 447 625 447 624 447 624 1519 625 1520 625 1520 624 1520 625 1520 624 1520 625 447 625 1520 624 1520 624 1520 625 447 625 447 625 447 624 447 625 1520 624 1520 624 1520 624 1520 624 1520 624 447 625 447 625 447 625 1520 624 1520 625 1520 624 1521 624 1520 624 1521 624 447 624 1520 624 1521 624 1521 623 1520 624 448 624 1520 624 448 624 1521 624\n#\n# Model: mitsubishi-MSY-GE10VA\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3439 1755 439 1262 435 1286 432 438 441 423 456 418 441 1282 436 433 436 431 438 1285 433 1314 435 408 461 1293 435 408 440 426 433 1289 460 1296 412 455 414 1312 406 1319 409 429 440 429 461 1267 441 426 464 406 442 1281 437 430 439 427 432 436 464 405 433 432 437 458 411 456 434 422 437 428 462 412 436 428 441 422 457 411 437 456 413 454 405 436 433 434 435 427 432 435 465 404 434 435 434 431 459 413 435 429 440 422 437 433 457 1270 438 1288 461 410 438 426 464 404 434 1293 435 1315 413 423 436 1287 462 407 431 434 435 435 434 433 467 415 433 1318 410 1286 463 409 439 1284 455 1301 438 410 438 426 464 1262 456 1269 439 429 461 410 469 407 462 1268 440 1285 433 461 408 456 413 1284 465 1261 457 414 455 1271 468 1277 431 1321 438 405 433 460 409 430 439 426 464 407 462 407 441 450 409 434 435 430 439 452 438 431 438 405 464 405 464 405 464 409 460 409 439 430 439 427 442 423 436 460 430 415 433 431 438 425 434 444 435 456 413 432 437 427 442 425 434 436 464 429 430 439 430 417 442 428 441 428 462 411 437 452 438 405 433 434 435 456 413 427 442 425 434 455 414 427 432 435 434 433 436 427 463 406 432 437 442 449 430 1268 460 1265 463 1262 466 405 464 406 442 422 437 1289 439 428 441 17052 3577 1746 407 1321 407 1294 434 460 409 431 459 439 409 1288 440 429 440 431 438 1309 430 1272 456 415 464 1289 408 433 457 412 436 1289 439 1284 465 404 434 1292 436 1289 439 428 441 450 409 1315 413 427 442 451 408 1294 434 432 437 430 439 428 462 406 432 432 458 414 455 414 434 430 439 428 441 426 433 436 433 432 437 454 405 438 441 428 431 434 456 418 441 448 431 411 437 429 440 425 434 433 436 458 411 425 434 433 436 457 412 1287 462 1263 455 419 461 408 440 430 439 1310 429 1272 435 460 409 1288 440 429 461 408 441 425 465 405 464 407 441 1286 432 1291 437 432 437 1315 413 1314 414 450 440 406 442 1283 456 1270 458 413 456 413 456 413 435 1297 431 1294 465 404 434 433 436 1314 414 1285 433 433 436 1288 440 1285 464 1264 433 433 436 453 416 425 434 460 409 434 435 432 437 454 415 423 436 431 459 424 435 430 460 413 435 430 439 423 456 413 435 431 438 455 414 427 432 435 434 455 414 426 464 406 432 437 442 422 457 414 434 431 438 424 435 435 455 414 455 416 432 433 436 457 412 426 464 410 438 453 437 412 436 437 463 409 439 425 434 455 414 426 433 437 432 433 436 433 436 457 433 412 436 453 437 406 432 435 465 1266 462 1263 434 1318 410 426 464 410 438 426 433 1293 435 434 435\n#\n# Model: Mitsubishi MSH-30RV\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3520 1642 515 1199 514 1199 514 383 486 383 486 383 486 1199 514 383 486 383 486 1199 513 1199 514 382 487 1199 513 383 486 383 486 1226 486 1226 486 383 486 1226 486 1226 486 383 486 383 486 1226 486 384 510 359 510 1202 510 360 509 360 509 360 509 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 361 508 1204 508 361 508 361 508 361 508 361 508 361 508 1205 508 361 508 361 508 361 508 361 508 1205 507 1205 508 1205 507 361 508 362 507 362 507 361 508 362 507 361 508 1205 507 362 507 362 507 1205 508 362 507 362 507 362 507 362 507 362 507 361 508 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 362 507 1205 507 362 507 362 507 362 507 362 507 362 507 362 507 1205 507 362 507 1206 506 1206 506 362 507 1206 507 362 507\n#\n# Model: Mitsubishi MSH_GA71VB\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3480 1701 468 1248 467 1250 465 407 460 411 467 405 462 1255 460 412 466 405 462 1255 460 1257 468 404 463 1253 462 410 468 404 463 1253 462 1255 470 402 465 1252 463 1254 461 411 467 405 462 1254 461 411 467 405 462 1254 461 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 461 411 467 405 462 409 469 403 464 407 460 411 467 404 463 1254 461 410 468 404 463 1254 461 410 468 404 463 408 460 412 466 406 462 1255 460 412 466 406 461 410 468 404 463 1253 462 1255 470 1247 468 404 463 409 469 402 465 406 461 411 467 1250 465 407 460 1256 469 1248 467 1250 465 1252 463 410 468 403 464 408 460 412 466 406 461 410 468 404 463 408 460 412 466 406 461 410 468 404 463 408 459 412 466 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 402 465 407 460 411 467 404 463 1253 462 1255 470 402 465 406 461 1256 469 402 465 1252 463 408 470 1248 467 1250 465 407 460 1256 469\n#\n# Model: Mitsubishi SRK20ZJ-S\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3195 1551 429 365 428 1158 429 365 429 365 428 1158 429 365 428 1158 429 366 428 365 428 1159 428 1158 429 1159 428 364 429 1158 429 366 428 1158 429 1158 429 1158 429 365 428 365 428 365 429 365 428 1158 429 1158 429 365 428 1158 429 1158 458 354 440 353 441 1128 459 353 494 353 386 1128 486 353 441 353 440 1101 459 1128 459 353 440 1128 459 1127 460 1128 459 1128 459 353 440 1129 457 1129 458 1130 457 1131 456 353 440 354 439 354 439 1132 455 354 439 353 440 354 439 354 439 1132 455 1132 455 1132 455 1132 455 354 440 354 439 1133 454 1133 454 354 439 353 440 354 439 353 440 1133 454 1133 454 353 440 353 440 1133 454 353 440 1133 454 1133 454 1133 454 353 440 353 440 353 440 1133 454 1133 454 353 440 353 440 354 439 1133 454 1133 454 1133 454 354 439\n#\n# Model: Mitsubishi SRK35ZS-W\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3199 1594 380 414 380 1208 379 415 389 405 389 1201 386 407 386 1202 385 409 384 408 385 1203 384 1203 384 1204 383 410 383 1206 381 412 382 1208 379 1207 380 1209 389 404 390 406 387 405 389 406 387 1201 386 1202 385 408 385 1204 383 409 384 1204 383 1205 382 412 382 411 382 413 380 1206 382 414 380 1208 379 414 379 416 388 1201 386 1201 386 1201 386 408 385 1202 385 1204 383 1204 383 1206 381 1205 382 1207 380 1207 380 1208 390 405 388 405 389 406 387 406 387 406 387 407 386 408 385 1202 385 1204 383 1203 384 411 382 1205 382 1205 382 1207 380 1207 381 414 390 404 379 414 380 1210 388 406 387 408 385 406 387 408 385 1202 385 1203 384 409 384 1205 382 1205 382 1206 381 1206 381 1208 379 415 389 404 389 1199 388 407 386 406 387 407 386 408 385 409 384 1202 385 1204 383 1204 383 1204 383 1205 382 412 381 1208 379 1208 379 415 389 404 390 406 387 406 387 407 386 1200 387 409 384 408 385 1202 385 1205 383 410 383 1205 382 1205 382 412 381 1207 380 1207 380 415 389 404 379 1209 389 406 387 405 388 1200 387 408 385 408 385 1202 385 1204 383 1204 383 1204 383 1208 379 1207 380 1207 380 1208 379 416 388 405 388 407 386 407 386 407 386 407 386 407 386 408 385 408 385 1204 383 1204 383 1205 382 1205 382 1207 380 1208 379 414 379 1210 388 406 387 406 387 406 387 407 386 407 386 409 384 1203 384\n#\n# Model: Moretti Air_Cooler\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1299 412 1270 412 426 1225 1299 412 1244 438 399 1279 400 1254 451 1253 426 1253 426 1253 426 1254 1268 7130 1266 416 1266 416 421 1258 1267 416 1267 416 422 1258 421 1258 422 1258 421 1259 421 1258 422 1258 1266 7132 1266 417 1266 417 421 1259 1266 417 1266 417 421 1259 421 1259 421 1259 421 1259 421 1259 421 1259 1266 7133 1265 417 1266 417 421 1259 1266 418 1266 417 422 1259 421 1260 420 1259 421 1259 421 1259 420 1259 1266 7133 1265 418 1266 418 421 1260 1265 418 1266 418 421 1260 420 1260 420 1260 420 1260 420 1260 420 1260 1266 7135 1265 418 1266 419 420 1260 1266 419 1266 419 420 1261 420 1261 420 1261 420 1261 420 1261 420 1261 1265 7137 1264 420 1265 420 419 1262 1266 420 1265 420 420 1262 420 1262 419 1262 420 1262 419 1262 419 1262 1265 7139 1264 421 1265 421 419 1263 1265 421 1265 421 418 1264 418 1264 418 1263 419 1264 417 1264 418 1264 1264 7142 1263 423 1263 422 418 1288 1241 446 1240 446 394 1288 394 1289 393 1288 394 1288 394 1288 394 1288 1241 7168 1240 446 1241 446 394 1289 1241 447 1241 447 394 1289 394 1289 394 1289 394 1289 394 1289 394 1289 1241 7171 1239 447 1240 447 393 1290 1240 447 1241 447 393 1290 393 1290 393 1290 393 1290 393 1290 393 1290 1241 7174 1239 447 1241 448 392 1291 1240 448 1241 448 393 1291 393 1292 392 1292 392 1291 393 1292 392 1291 1240 7177 1239 449 1240 449 392 1292 1240 449 1240 449 392 1293 392 1293 392 1292 392 1293 391 1293 392 1293 1239 7179 1239 450 1239 450 391 1294 1239 450 1240 451 391 1294 391 1294 391 1295 390 1294 391 1295 390 1294 1239 7184 1237 452 1238 475 367 1319 1215 476 1215 476 366 1320 366 1320 366 1320 365 1320 365 1320 366 1320 1215 7211 1213 476 1190 501 366 1321 1215 477 1214 477 365 1322 364 1321 365 1322 364 1321 365 1321 365 1322 1213 7214 1213 478 1213 478 364 1322 1189 503 1189 503 364 1323 339 1348 338 1348 364 1323 363 1324 362 1323 1213 7218 1187 528 1164 529 313 1374 1163 529 1164 529 313 1375 313 1375 312 1375 312 1374 313 1374 313 1375 1163 7271 1162 530 1163 530 312 1376 1163 531 1162 530 312 1377 311 1378 310 1402 285 1378 310 1402 285 1403 1136 7300 1136 557 1136 557 285 1404 1136 558 1136 559 283 1404 285 1431 257 1405 284 1431 257 1431 257 1431 1109 7330 1108 585 1109 586 256 1432 1109 586 1109 638 177 1486 222 1468 221 1468 221 1468 221 1467 221 1495 1055 7413 1028 666 1029 2500 885\n#\n# Model: Ok AC_OAC_7020\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4371 4390 527 1618 527 545 526 1618 527 520 552 545 527 545 527 521 551 1593 552 545 527 545 527 545 527 546 526 545 527 545 526 1618 527 545 527 545 527 1618 527 545 527 545 527 520 552 1618 527 1593 551 1618 527 1618 527 1618 527 1618 526 1618 526 1594 579 1591 527 1618 526 1619 527 1618 526 1593 552 1618 527 1618 527 1593 552 1618 527 1618 527 1618 527 1618 527 1618 527 545 527 1618 527 1618 527 545 527 1619 554 518 527 5203 4373 4391 526 545 527 1618 527 545 527 1593 552 1618 527 1595 550 1618 528 544 528 1618 527 1618 526 1594 552 1617 528 1619 526 1618 527 545 527 1618 527 1619 527 519 553 1618 527 1618 527 1618 527 522 550 545 527 546 526 545 555 517 528 545 527 545 528 544 527 521 551 522 550 545 527 545 526 521 552 521 551 545 527 544 528 545 527 545 527 545 527 545 527 545 528 1618 527 545 527 545 527 1619 527 545 527 1619 526\n#\n# Model: Osaka CH_09_DSBP\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9034 4455 670 1627 695 507 693 508 692 510 690 514 688 514 688 514 688 514 687 514 688 1609 688 514 688 514 688 514 688 514 688 515 687 515 687 514 688 515 687 515 687 515 687 515 686 1611 686 515 687 515 687 516 686 517 685 517 685 516 686 1612 685 540 662 1635 662 540 662 540 662 1635 662 540 662 19937 688 514 688 514 688 514 688 514 688 514 688 514 688 514 688 514 687 515 687 515 687 515 687 515 687 515 686 1611 686 515 687 516 686 516 686 539 662 517 685 516 686 539 663 539 663 540 662 540 662 540 662 540 661 540 662 540 662 1635 662 1635 662 1636 661 1635 662\n#\n# Model: Panasonic A75C4187\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3498 1744 439 431 442 1305 442 431 442 432 441 431 442 431 442 432 441 432 441 433 440 431 442 432 441 431 442 432 441 1306 441 431 442 432 441 432 441 432 441 432 441 432 441 432 441 1308 439 1305 442 1305 442 431 442 433 440 1305 442 434 439 433 440 431 442 432 441 432 441 432 441 433 440 431 442 432 441 433 440 432 441 433 440 432 441 432 441 432 441 432 441 432 441 434 439 433 440 432 441 433 440 433 440 432 441 432 441 432 441 432 441 432 441 431 442 432 441 433 440 1308 439 1306 441 432 441 432 441 431 442 432 441 432 441 10333 3499 1741 442 432 441 1306 441 432 441 432 441 431 442 432 441 431 442 433 440 432 441 433 440 432 441 432 441 432 441 1308 439 431 442 432 441 432 441 432 441 433 440 432 441 432 441 1306 441 1307 440 1306 441 432 441 432 441 1307 440 433 440 432 441 433 440 432 441 431 442 432 441 432 441 432 441 432 441 432 441 432 441 433 440 1308 439 432 441 1305 442 1305 442 432 441 432 441 433 440 432 441 1307 440 1307 440 431 442 1306 441 432 441 1306 441 1307 440 432 441 433 440 1306 441 432 441 432 441 433 440 431 442 432 441 1308 439 432 441\n#\n# Model: Panasonic Climate_A75C2600\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3545 3410 937 803 936 2542 937 803 936 832 907 832 933 2545 933 2546 931 809 928 813 925 2580 899 839 900 840 900 839 900 2580 899 2580 899 840 899 840 900 2580 899 840 900 840 900 839 900 840 899 840 900 840 899 840 900 2580 899 840 899 840 899 840 900 840 900 840 899 840 3511 3449 898 841 898 2581 898 840 900 840 899 841 898 2581 898 2581 899 840 899 841 898 2581 898 841 898 841 898 841 898 2581 898 2581 898 841 898 841 898 2581 899 841 898 841 898 841 899 841 898 841 898 842 898 841 898 2581 898 841 898 842 897 842 897 842 897 842 898 842 3509 3450 897 13896 3509 3451 896 842 897 843 897 843 896 843 896 2583 896 843 897 843 896 2583 896 844 895 844 895 868 871 869 871 2609 870 869 871 869 870 2609 870 869 871 2609 870 2609 870 869 871 2609 870 2609 870 869 870 869 870 869 870 2609 870 2609 870 869 871 2609 870 2610 869 870 869 870 3482 3478 869 870 869 870 869 870 870 870 869 2610 869 870 869 870 870 2610 869 871 869 871 869 871 868 871 869 2611 868 871 868 871 869 2611 868 872 867 2611 869 2611 868 871 868 2612 867 2612 868 872 867 897 842 897 843 2637 843 2637 842 897 843 2637 843 2637 842 898 842 898 3454 3506 841\n#\n# Model: Panasonic CWA75C4179\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3472 1744 416 457 415 1306 418 463 419 466 416 473 419 474 418 478 414 457 415 458 414 463 419 462 420 465 417 472 420 1316 418 479 413 458 414 459 413 464 418 463 419 466 416 473 419 1318 416 1324 420 1295 418 454 418 459 413 1312 422 463 419 470 422 471 421 475 417 454 418 455 417 460 422 459 413 472 420 469 413 480 412 485 417 453 419 454 418 459 413 468 414 471 421 468 414 479 413 484 418 452 420 453 419 458 414 468 414 471 421 467 415 478 414 483 419 451 421 453 419 1301 412 1312 422 463 419 470 422 471 421 476 416 433 418 10535 3469 1746 414 459 413 1308 415 465 417 468 414 475 417 476 416 481 421 449 413 461 421 455 417 465 417 467 415 474 418 1319 415 482 420 450 422 451 421 457 415 466 416 469 413 476 416 1320 414 1327 417 1297 416 457 415 462 420 1305 419 466 416 473 419 474 418 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 1301 412 464 418 463 419 1310 413 1319 415 1321 413 485 417 453 419 454 418 459 413 1312 422 1307 416 472 420 1317 416 480 412 459 413 460 412 465 417 464 418 467 415 474 418 475 417 479 413 1302 422 1295 418 1302 421 1303 421 1308 415 473 419 1318 416 481 421 1293 420 1296 417 460 412 1313 421 1307 416 473 419 473 419 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 457 415 1306 418 1307 416 1312 422 467 415 478 414 483 419 451 421 452 420 457 415 467 415 469 413 476 416 1321 413 1328 416 1298 415 458 414 463 419 462 420 465 417 472 420 472 420 477 415 456 416 457 415 462 420 461 421 464 418 470 422 471 421 476 416 454 418 1299 414 463 419 461 421 464 418 471 421 472 420 477 415 1299 414 459 413 464 418 463 419 466 416 473 419 473 419 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 457 415 1306 417 1307 416 468 414 1319 415 478 414 483 419 430 421\n#\n# Model: Remko RKL\n#\nname: Off\ntype: parsed\nprotocol: NECext\naddress: 86 6B 00 00\ncommand: 12 ED 00 00\n#\n# Model: Rinnai AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9026 4519 590 1654 616 1657 623 574 561 582 563 582 563 583 562 1688 592 1656 675 1627 593 1650 620 1654 677 521 563 1660 620 1656 614 1664 616 581 564 578 567 575 560 582 563 581 564 579 566 1684 596 1655 615 1661 619 577 568 574 561 581 564 580 565 579 566 580 565 582 563 581 564 577 568 574 561 582 563 580 565 579 566 1657 613 1664 616 582 563 578 567 575 560 583 562 581 564 580 565 582 563 584 561 582 563 578 567 575 560 583 562 581 564 580 565 1659 621 579 566 578 567 574 561 581 564 579 566 577 568 576 569 577 568 579 566 578 567 574 561 580 565 578 567 576 569 575 560 587 568 579 566 577 568 573 562 580 565 578 567 576 569 575 560 586 569 552 593 577 568 573 562 580 565 577 568 576 559 585 560 586 569 552 593 576 569 1703 567 575 560 1713 567 577 568 576 569 577 568 553 592 578 567 1704 566 1707 563 1656 614 1662 618 1659 621 577 568 553 592 1706 564\n#\n# Model: Royal Clima_RC-TWN55HN\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4412 4413 548 1606 548 530 601 1552 602 1552 601 475 601 475 548 1606 548 528 548 528 548 1605 548 528 548 529 547 1607 546 1608 546 531 545 1610 544 534 542 1635 519 1635 519 1635 519 1635 518 558 519 1635 542 1612 518 1636 518 558 519 558 519 558 519 558 542 1612 518 558 519 558 519 1636 517 1636 518 1636 518 558 519 558 518 558 542 535 518 559 518 559 541 535 518 559 518 1635 518 1635 518 1636 518 1635 519 1636 518 5235 4384 4443 517 1636 541 535 518 1636 518 1636 518 559 518 559 518 1635 518 559 518 559 517 1636 518 559 518 558 518 1636 541 1612 518 559 518 1636 517 559 518 1636 518 1636 518 1636 518 1636 517 559 518 1636 518 1636 518 1636 541 535 518 559 518 559 541 536 517 1636 518 559 518 559 518 1636 517 1636 518 1636 518 559 517 559 517 559 517 559 517 559 518 559 518 559 517 559 518 1636 518 1636 518 1636 517 1636 517 1636 517\n#\n# Model: Samsung AC_AR12K\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 270 18152 3021 8955 523 499 495 1497 492 504 500 468 526 496 498 498 496 499 495 501 493 502 492 1499 500 496 498 524 470 1521 498 497 497 499 495 1496 492 1499 500 1491 497 1494 494 1497 502 494 500 495 499 497 497 498 496 500 494 528 466 530 464 531 473 522 493 503 491 504 500 495 499 497 497 498 496 500 494 501 493 503 491 504 500 495 499 496 498 498 496 499 495 501 493 529 465 531 473 522 472 523 492 504 490 505 499 496 498 498 496 499 495 1496 492 1499 500 1492 496 1494 525 2947 2999 8953 525 1519 469 499 516 507 497 498 496 500 494 501 493 503 491 504 500 495 499 1492 496 499 495 501 493 1498 501 495 499 1492 496 1522 466 1524 496 1496 492 1499 500 1492 496 499 495 500 494 502 492 504 500 495 499 496 498 498 496 499 495 500 494 502 492 530 474 521 473 523 523 472 522 474 489 506 498 497 497 499 495 500 494 502 492 503 501 494 500 496 498 497 497 499 495 500 494 502 492 503 501 521 473 523 471 524 522 474 520 475 498 497 497 499 495 500 494 2978 2999 8952 525 1492 496 499 495 501 493 503 491 504 500 495 499 497 497 525 469 526 468 1524 516 479 494 501 493 503 491 504 500 495 499 497 497 1494 494 1497 492 1500 499 1492 496 499 495 1497 491 531 473 1518 522 1469 499 496 498 498 496 500 494 1497 491 1500 499 1492 496 499 495 501 493 502 492 504 500 495 499 523 471 525 469 526 520 1472 516 1475 493 502 492 504 500 495 499 1492 496 499 495 501 493 502 492 504 500 495 499 496 498 498 496 1522 466 1525 525 1466 491 1500 499\n#\n# Model: Samsung AR13TYHZCWKN\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 645 17766 3059 8884 533 461 557 1427 560 435 584 433 534 458 534 458 534 458 588 404 587 405 586 1399 586 407 584 408 558 1429 555 1457 502 491 525 1460 528 1458 554 1431 554 1431 555 1431 554 438 555 438 554 439 553 440 553 464 528 464 528 464 529 465 528 465 526 467 526 467 527 465 553 439 554 439 554 438 554 439 554 439 553 439 554 439 553 439 553 439 554 440 552 464 528 464 528 465 528 466 527 466 501 492 525 468 526 466 527 466 553 439 554 439 553 440 552 1433 553 1433 553 2936 3024 8893 552 1458 527 465 528 465 527 467 526 467 500 493 524 468 526 467 526 467 552 1434 552 441 552 441 552 1435 550 465 528 1458 527 1458 528 1459 527 1485 501 1485 500 1486 501 491 527 466 528 465 528 465 527 465 528 465 527 465 528 465 528 465 527 465 528 465 527 466 527 466 527 492 500 492 501 492 500 493 500 493 500 492 526 466 527 465 527 465 527 465 527 466 527 466 527 466 527 465 527 466 526 466 527 466 526 467 526 493 499 493 474 518 499 494 500 493 526 2936 3025 8918 526 1459 527 466 527 466 527 466 526 466 527 466 526 467 526 467 525 467 526 1460 525 493 499 493 498 495 498 495 499 493 500 493 525 1460 527 1460 525 1460 526 1460 525 1460 526 1460 526 468 525 1487 499 1487 497 495 498 495 499 493 500 1486 525 1460 525 1460 525 467 525 467 526 467 525 467 526 468 524 1462 524 468 524 494 498 1488 497 1488 499 494 499 493 525 468 525 1461 524 468 525 468 524 468 525 468 525 468 524 468 525 469 524 494 498 494 499 1488 473 1514 524\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 619 17878 3008 8908 535 458 535 1451 534 459 534 459 533 459 534 485 507 485 587 404 532 460 533 1451 536 456 537 456 535 1450 561 432 560 433 533 1454 531 1455 530 1456 529 1457 529 1508 501 491 502 491 502 490 503 490 503 489 504 488 504 489 503 489 504 489 504 489 504 488 504 489 504 464 529 464 529 464 529 464 528 516 476 516 477 516 502 491 502 490 503 490 503 489 504 489 503 489 504 489 504 489 504 489 503 489 504 489 503 489 504 464 529 1458 528 1509 476 1509 501 1484 504 2934 3028 8939 504 1482 504 489 503 489 504 489 504 489 503 489 504 489 503 489 504 465 528 1459 527 516 476 516 501 1485 502 1484 503 1483 503 1482 503 489 504 1482 504 1483 503 1482 503 1459 527 1459 527 516 476 1509 501 1485 501 491 502 490 503 489 504 1482 503 1482 503 1483 503 489 504 489 503 489 504 489 503 489 503 490 503 516 477 516 501 1485 502 1484 502 490 503 490 503 490 503 490 503 1483 503 490 503 490 503 489 503 490 503 490 502 490 503 1510 475 1510 500 1485 502 1484 503\n#\n# Model: Samsung Wind-Free\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 606 17832 2994 8936 520 501 496 1491 494 501 496 498 489 532 465 529 488 505 492 502 495 499 488 1499 496 499 498 495 492 1496 499 1463 522 499 498 1463 522 1467 517 1469 526 1491 493 1495 520 500 487 507 490 504 493 500 497 497 490 504 493 501 496 498 489 505 492 501 496 498 499 495 492 502 495 499 498 522 465 529 468 526 492 502 495 499 488 506 491 503 494 499 498 496 491 503 494 500 497 497 490 504 493 500 497 497 490 504 493 500 497 497 490 531 466 528 469 1518 487 1475 520 2947 3018 8939 517 1471 524 496 491 503 494 500 497 497 490 504 493 500 497 497 490 504 493 1521 464 530 467 527 491 1471 514 507 490 1471 524 1465 519 1468 517 1472 523 1465 520 1468 517 477 520 501 496 497 490 531 466 528 469 525 493 501 496 498 489 505 492 502 495 498 499 495 492 502 495 499 498 495 492 502 495 499 498 496 491 502 495 499 498 496 491 529 468 526 471 523 495 499 488 506 491 503 494 499 498 496 491 503 494 500 497 496 491 503 494 500 497 497 490 503 494 2973 2992 8939 517 1469 526 522 465 529 488 506 491 503 494 499 488 506 491 503 494 500 497 1490 495 499 498 496 491 1497 498 1464 520 1468 517 1470 525 524 463 1498 517 1471 524 1465 520 1468 517 1471 524 1465 520 1468 517 1472 523 497 490 504 493 501 496 1491 494 1496 519 1469 516 504 493 501 496 498 489 505 492 501 496 498 499 495 492 502 495 1492 493 1469 526 495 492 529 468 1492 513 1476 519 502 495 498 489 505 492 502 495 499 498 496 491 503 494 499 498 496 491 1497 498 1464 541\n#\n# Model: Sharp AH-A9UCD\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9111 4374 765 1548 712 496 713 497 713 496 710 1598 711 1602 712 1602 712 496 712 497 712 498 710 495 709 501 729 496 711 496 707 498 707 498 707 498 708 498 707 499 707 499 707 498 707 1600 706 499 708 497 709 498 706 500 706 499 707 499 707 1597 705 499 710 1596 705 497 707 500 708 1601 708 500 709 19935 707 1604 706 498 707 500 709 500 708 499 709 500 710 500 709 500 708 1600 708 500 709 500 710 498 705 499 708 1597 704 498 709 500 707 498 709 498 705 499 709 498 709 498 708 498 708 499 707 499 707 498 707 498 708 499 707 498 708 1600 706 497 707 1597 705 1600 709\n#\n# Model: Sharp AH_X9VEW_AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3821 1901 456 507 456 1424 430 490 484 1425 429 475 488 1420 434 471 482 1427 437 467 486 1420 434 486 457 1416 459 1432 432 487 456 1436 439 480 483 1425 439 1425 460 1420 455 1419 435 485 457 472 491 1417 458 1407 437 483 459 487 456 474 458 487 466 1432 432 471 461 485 457 472 491 1415 460 1378 466 480 483 1424 430 474 458 487 466 1424 461 1419 456 1426 438 465 457 489 464 466 456 489 464 1426 438 481 461 484 437 493 460 1429 435 482 460 485 458 1440 435 484 458 1438 437 467 465 480 462 467 465 481 461 468 464 481 461 469 463 481 461 468 485 1424 430 1434 462 1418 457 1425 439 465 457 489 464 465 457 489 464 466 456 490 463 467 465 480 463 467 465 480 463 467 486 1422 432 471 461 485 457 472 460 485 457 472 460 485 457 472 460 486 456 488 434 497 456 473 459 486 456 488 465 1414 461 1393 482 1423 462 1419 435 469 463 482 460 469 463 482 460 1436 439 1425 439 480 483\n#\n# Model: Sharp CVP10MX\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3827 1854 505 433 507 1382 506 434 506 1383 505 434 506 1410 478 433 507 1381 507 433 507 1383 505 435 505 1382 505 1383 505 435 505 1382 506 435 505 1383 505 1382 506 1384 504 1381 507 434 506 434 506 1382 506 1382 506 432 508 434 506 436 504 433 507 1382 506 435 505 434 506 436 504 1381 507 1383 505 434 506 1382 506 1381 507 434 506 1381 507 1381 507 1383 505 435 505 434 506 435 505 433 507 1383 505 435 505 435 505 434 506 1381 507 433 507 435 505 434 506 1382 506 433 507 434 506 433 507 438 502 434 506 433 507 434 506 434 506 432 508 435 505 434 506 433 507 433 507 1387 501 435 505 436 504 433 507 433 507 435 505 433 507 434 506 434 506 435 505 435 505 432 508 1410 478 461 479 432 508 435 505 434 506 433 507 436 504 434 506 434 506 433 507 435 505 1383 505 435 505 1381 507 1381 507 1382 506 1382 506 1381 507 435 505 433 507 433 507 461 478 1383 505 433 507 434 506\n#\n# Model: Shivaki SSA18002\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3197 1545 581 1033 553 1006 606 338 463 342 485 339 488 1033 553 342 485 342 485 1034 551 1035 550 342 485 1037 548 342 485 340 487 1040 546 1040 546 340 487 1040 546 1040 546 340 487 340 488 1040 546 340 487 340 488 1040 546 340 487 340 487 340 487 342 485 340 487 340 487 340 487 342 485 342 485 342 485 340 487 342 485 340 487 340 487 342 485 342 485 342 485 342 485 340 488 342 485 1041 545 340 487 340 487 1041 545 1041 545 340 488 340 487 340 487 340 487 340 488 342 485 342 485 340 487 340 488 1041 545 340 487 340 487 342 486 342 485 340 487 340 487 340 487 340 487 342 485 341 486 1041 545 340 487 340 487 340 487 340 487 340 488 342 485 340 487 342 485 342 485 340 487 342 485 340 487 340 488 342 485 343 484 340 488 340 487 340 487 340 487 340 487 342 485 340 487 340 487 341 486 342 485 340 488 342 485 342 485 342 485 340 487 340 487 340 488 1042 544 340 487 340 487 340 487 340 487 340 487 340 487 340 488 342 485\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3196 1545 581 1005 580 1008 604 337 464 338 488 338 489 1033 553 339 488 338 489 1033 552 1034 551 339 487 1036 549 339 488 339 488 1039 547 1039 547 340 487 1040 546 1040 546 339 488 339 488 1040 546 340 487 339 488 1040 546 339 488 339 488 339 488 339 488 339 488 339 488 340 487 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 340 487 339 488 1040 546 339 488 339 488 1040 546 339 488 339 488 339 488 1040 546 339 488 339 488 339 488 339 488 339 488 339 488 1040 546 1041 545 1041 545 340 487 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 340 487 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 340 487 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 339 488 340 487 339 488 339 488 340 487 339 488 1042 544 339 488 1042 544 339 488 339 488 339 488 339 488 1042 544 1042 544\n#\n# Model: SINCLAIR ASH13BIF2\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9048 4430 705 500 706 1602 705 1602 729 476 730 1579 728 478 728 1580 727 480 726 480 726 480 726 480 726 1606 702 505 701 481 725 481 724 505 701 481 725 504 702 505 701 504 701 505 701 1606 702 504 702 504 701 504 701 504 702 505 701 505 701 1606 701 504 702 1607 701 504 701 504 701 1607 701 505 701 19915 726 1582 726 481 725 504 701 505 701 1606 701 505 701 505 701 505 701 505 700 1606 701 505 701 505 701 505 701 504 702 1606 701 505 701 504 701 505 701 504 702 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 505 701 1607 700 1606 701 39924 9075 4406 728 478 728 1581 727 1581 726 481 725 1582 725 480 725 1606 701 504 702 481 725 505 701 505 701 1606 701 504 702 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 1607 701 1607 701 505 701 505 701 1607 701 506 701 19913 726 480 726 480 726 480 726 480 726 479 727 479 727 480 725 479 727 479 726 480 726 480 726 480 726 480 726 480 726 480 726 504 701 480 726 480 725 505 702 481 725 1606 701 505 701 481 725 481 725 505 700 505 701 504 701 505 701 1607 701 504 701 505 700 1607 701\n#\n# Model: SoleusAir\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 6149 7347 601 535 573 560 602 505 602 480 597 509 600 534 599 487 593 541 591 568 569 539 568 490 594 539 570 513 568 540 570 566 570 540 568 543 594 540 569 566 543 515 594 540 569 514 567 542 570 514 568 543 594 540 569 490 592 542 570 514 568 541 570 541 542 541 569 567 570 515 567 542 570 515 567 542 570 568 568 516 570 568 570 542 568 542 570 515 569 542 570 568 568 517 569 541 569 517 569 568 570 515 568 570 569 569 543 542 569 569 569 516 569 542 570 543 568 1640 570 516 568 543 570 569 543 1613 569 516 568 1613 570 569 543 570 569 542 543 543 569 543 542 544 569 516 569 544 570 543 570 570 570 543 543 544 569 1615 569 571 569 570 568 519 569 1615 569 544 569 1615 568 572 569 571 542 1642 569 571 568 1617 569 1642 542 1642 569 1589 568 545 569 1590 569 518 568 1617 569 545 568 7372 568\n#\n# Model: Subtropic in-07HN1\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9019 4453 576 1665 603 1639 602 504 602 505 601 507 599 508 598 1644 573 1669 573 1669 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 572 535 572 535 572 535 572 535 572 1670 572 1670 572 1670 571 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 536 571 1670 572 535 572 1670 572 535 572 535 572 536 571 536 571 536 571 536 571 535 572 536 571 536 571 536 571 536 571 536 571 536 571 1670 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 536 571 537 570 536 571 537 570 536 571 537 570 537 570 537 570 537 570 537 570 537 570 538 569 538 569 538 569 561 546 562 545 562 545 562 545 1696 546 562 545 1697 545 562 545 562 545 562 545 562 545 562 545 1696 546 1697 545 1697 545 562 545 562 545 1697 545 1697 545 1697 545\n#\n# Model: Tcl\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3087 1607 488 1064 517 335 492 1087 484 315 512 340 487 1091 490 336 491 338 489 1089 492 333 494 332 485 341 486 340 487 338 489 336 491 339 488 1090 491 334 493 1085 486 340 487 1091 490 1088 493 333 484 343 484 44227 178\n#\n# Model: Timberk RG05D4-BGE\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4414 4312 565 1591 565 517 565 1596 564 1592 565 517 565 517 565 1591 566 519 565 519 565 1595 565 517 565 517 565 1592 564 1593 563 519 563 1599 563 521 563 1594 562 1594 563 1594 563 1595 562 520 562 1598 563 1597 562 1600 562 520 562 520 562 520 562 520 562 1595 562 520 562 522 562 1601 561 1595 562 1595 562 521 561 521 561 521 561 521 561 523 561 523 561 521 561 521 561 1600 561 1596 561 1596 561 1596 561 1597 561 5165 4384 4310 560 1596 561 521 561 1600 561 1596 561 521 561 521 561 1595 562 523 561 523 561 1600 561 521 562 521 561 1595 562 1595 562 520 562 1601 562 523 561 1595 562 1595 562 1595 562 1595 562 521 561 1599 562 1597 562 1601 562 521 561 521 561 521 561 521 561 1595 562 521 561 522 562 1601 562 1595 562 1595 562 521 561 521 561 521 561 521 561 523 561 523 561 521 561 521 561 1599 562 1596 561 1596 561 1595 562 1598 562\n#\n# Model: Tora TS_16-Classic\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3121 1585 524 1061 524 1036 549 346 473 346 471 346 499 1036 549 346 473 346 472 1061 524 1061 524 346 472 1064 545 347 473 346 472 1092 493 1092 493 346 473 1092 493 1092 493 346 498 346 473 1067 518 346 473 346 497 1068 518 346 474 346 473 346 498 346 474 346 473 346 497 346 475 346 473 346 499 347 472 346 473 346 499 346 473 346 472 353 492 346 473 346 498 346 474 346 473 346 473 1093 492 346 499 346 473 1094 491 1094 491 346 473 346 499 346 473 346 473 346 499 346 473 346 497 346 474 346 473 1094 491 346 473 346 499 346 473 346 498 347 473 346 473 346 498 346 474 346 473 346 497 346 475 346 472 347 498 346 473 346 473 347 498 346 473 346 473 347 498 346 473 346 498 347 474 346 473 346 497 346 475 346 473 346 473 354 491 346 473 346 499 346 473 347 472 346 498 347 472 346 473 353 492 347 472 346 473 347 498 353 465 346 473 347 498 353 465 346 499 353 466 353 466 347 497 354 465 354 465 347 472 1095 490 354 491\n#\nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3112 1543 540 1041 544 1039 546 346 500 346 473 346 473 1063 522 346 494 347 475 1064 546 1039 543 346 448 1068 517 346 472 347 497 1046 514 1096 516 346 473 1069 516 1069 516 346 473 346 473 1069 516 347 498 346 473 1069 516 346 472 347 498 346 473 346 473 346 499 346 472 346 498 346 473 346 473 346 498 346 474 346 473 346 498 347 473 346 473 347 498 346 473 1070 515 346 472 347 471 1071 538 346 500 346 448 347 496 1070 515 346 474 346 472 346 497 347 474 346 497 346 474 346 473 346 473 346 499 346 473 346 473 346 499 346 473 346 498 347 473 347 472 346 497 346 474 346 473 346 496 347 474 346 473 347 498 346 473 346 473 346 499 346 473 346 473 347 498 346 473 346 497 346 475 346 473 347 495 346 476 346 473 346 472 355 490 346 473 346 498 346 473 347 472 346 498 347 473 346 473 355 490 346 473 347 472 346 499 347 471 346 473 346 499 346 473 346 496 1072 490 1095 490 347 497 1070 490 1096 489 1095 490 346 499 346 473\n#\n# Model: Toshiba RAS13SKV2E\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4387 4349 553 1609 553 1607 555 1605 546 1613 549 531 550 530 551 1610 552 527 554 526 555 526 555 525 556 524 557 1602 549 1611 551 529 552 1608 554 526 555 525 556 525 556 524 546 533 548 532 549 1611 551 1608 554 1606 556 1604 558 1603 548 1611 551 1609 553 1607 555 525 556 525 556 524 557 523 547 533 548 533 548 532 549 531 550 530 551 1607 555 526 555 1605 557 1603 548 531 550 531 550 530 551 529 552 528 553 527 554 526 555 526 555 525 556 524 546 1612 550 1610 552 1608 554 527 554 526 555 526 555 525 556 524 557 523 547 533 548 532 549 531 550 1609 553 1607 555 525 556 525 556 1603 548 1612 550 530 551 7454 4385 4352 549 1611 551 1609 553 1608 554 1606 556 525 556 524 557 1602 549 531 550 531 550 530 551 530 551 529 552 1633 529 1604 558 523 558 1601 550 531 550 530 551 530 551 529 552 528 553 527 554 1604 558 1603 548 1611 551 1609 553 1608 554 1606 556 1604 558 1602 549 532 549 531 550 530 551 530 551 529 552 528 553 527 554 526 555 525 556 1603 548 532 549 1610 552 1608 554 527 554 527 554 526 555 525 556 524 557 523 547 533 548 532 549 531 550 530 551 1608 554 1606 556 1604 558 523 547 533 548 532 549 531 550 530 551 529 552 528 553 527 554 526 555 1605 557 1602 549 531 550 531 550 1609 553 1607 555 526 555\n#\n# Model: Toshiba RG57H4\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4415 4348 568 1578 569 503 570 1575 572 500 563 510 563 508 565 533 540 1578 569 1576 571 527 546 500 563 509 564 533 540 532 541 1603 544 502 571 500 563 1582 565 1580 567 1578 569 504 569 502 571 1574 563 509 564 1581 566 506 567 504 569 529 544 501 572 500 563 509 564 1580 567 1578 569 529 544 501 572 500 563 509 564 507 566 1578 569 1576 571 501 572 1573 564 508 565 1606 541 504 569 1576 571 501 572 1573 564 5169 4410 4351 565 507 566 1579 568 504 569 1575 572 1573 564 1582 565 1580 567 505 568 503 570 1575 572 1573 564 1581 566 1580 567 1578 569 503 570 1575 572 1573 564 534 539 507 566 505 568 1577 570 1575 572 500 563 1582 565 507 566 1579 568 1577 570 1575 572 1573 564 1582 565 1580 567 505 568 504 569 1576 571 1574 563 1582 565 1580 567 1579 568 504 569 503 570 1574 563 509 564 1581 566 506 567 1578 569 503 570 1574 563 510 563\n#\n# Model: Tosot T24H-ILF\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9072 4445 602 1586 603 477 601 478 600 479 599 480 598 481 598 482 597 482 572 1617 597 1593 572 1617 597 483 572 507 572 507 572 507 572 507 572 507 597 482 597 482 572 507 597 482 572 1618 571 507 572 507 572 507 572 507 572 507 572 507 572 1618 571 507 572 1618 572 507 572 507 572 1618 572 507 652 20153 572 507 597 482 572 507 572 507 597 482 597 482 572 507 596 483 572 507 572 507 572 507 572 507 572 507 572 1618 596 483 572 507 596 483 595 484 572 507 597 482 595 484 597 482 597 482 572 507 572 507 597 482 572 507 597 482 572 507 597 483 571 1618 597 482 675 40391 9179 4421 599 1590 599 481 597 482 597 482 597 481 598 482 597 482 597 482 597 1592 597 1592 598 1592 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 572 507 597 1593 597 482 572 507 572 507 572 507 572 507 572 508 571 1618 597 1593 596 1593 572 507 572 507 572 1618 597 482 678 20152 597 483 596 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 597 482 572 507 597 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 572 507 597 482 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 596 482 572 507 572\n#\n# Model: Tropic AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1373 348 1310 376 463 1190 1318 400 1286 401 439 1244 442 1244 1288 400 465 1218 468 1218 468 1219 467 7970 1307 404 1281 405 435 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 434 1252 434 1252 434 8000 1280 406 1281 406 434 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 433 1253 433 1253 434 8000 1280 406 1280 406 434 1253 1280 406 1280 406 434 1253 433 1253 1280 406 434 1253 433 1253 433 1253 433 8001 1279 406 1280 406 434 1253 1280 407 1279 407 433 1253 434 1253 1280 407 433 1253 433 1253 433 1253 433 8001 1279 407 1279 407 433 1253 1280 407 1280 407 433 1253 433 1253 1280 407 433 1253 433 1253 434 1253 433\n#\n# Model: Trotec PAC2600X\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4329 4399 534 1607 511 561 534 1607 535 535 536 537 533 537 533 563 507 1606 535 1610 507 559 535 563 506 536 534 563 507 535 511 557 536 1605 535 537 533 1607 533 563 507 536 534 535 535 1605 535 1606 534 564 507 1634 506 1605 535 1635 506 1634 483 1629 512 1630 534 1607 511 1631 533 1635 506 1634 506 1608 533 1606 535 1607 511 1630 534 1609 532 1608 511 557 513 1658 483 560 510 1631 510 1631 532 565 484 562 508 1631 510 5210 4353 4396 510 559 511 1631 510 561 509 1628 513 1630 511 1658 483 1631 511 561 509 560 510 1634 508 1631 511 1658 484 1631 511 1633 509 1658 484 563 508 1658 484 559 512 1658 484 1633 509 1632 510 559 512 559 511 1631 511 559 512 562 508 560 510 561 509 558 512 559 511 559 511 560 510 560 510 560 510 560 510 560 510 560 510 560 510 587 483 587 483 1630 511 558 512 1631 510 559 511 562 508 1658 483 1630 512 560 510\n#\n# Model: Vornado\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1347 405 1322 422 410 1332 1300 448 1289 480 383 1303 445 1298 439 1331 417 1299 438 1332 416 1302 1320 6834 1292 456 1292 450 413 1304 1328 445 1292 452 411 1302 446 1297 440 1303 445 1298 439 1303 445 1301 1321 6806 1320 453 1295 422 441 1301 1321 454 1294 422 441 1301 436 1305 443 1300 448 1295 442 1301 447 1298 1324 6802 1324 451 1297 420 443 1299 1323 424 1324 419 444 1298 439 1304 444 1298 439 1304 444 1299 438 1306 1326 6815 1321 453 1295 421 442 1302 1320 426 1322 421 442 1301 436 1306 442 1301 447 1296 441 1301 447 1299 1323 6802 1324 424 1324 418 445 1300 1322 424 1324 419 444 1298 439 1304 444 1299 438 1304 444 1299 438 1306 1326 6809 1328 420 1317 424 439 1306 1326 419 1318 424 439 1304 444 1299 438 1305 443 1299 438 1305 443 1302 1320\n#\n# Model: Whynter AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 781 710 2930 2876 775 716 781 735 752 2175 779 711 776 2177 746 745 773 2232 753 2173 749 768 750 2177 745 745 773 743 754 737 750 741 746 744 754 737 771 746 751 739 748 2178 776 742 755 2172 771 719 778 2227 747 744 754 2200 754 737 750 2203 751 741 746 2181 773 744 753 737 750 741 746\n#\n# Model: Windfree Actest2\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 548 17943 2990 9008 467 560 469 1533 469 560 468 559 468 561 467 560 468 562 467 561 466 559 468 1536 467 559 468 560 467 1535 468 1533 469 560 467 1536 467 1534 468 1533 468 1534 468 1534 467 560 468 559 468 559 469 560 469 559 469 559 468 559 468 560 468 560 467 562 467 559 469 558 469 559 468 559 469 561 468 559 468 559 468 559 468 559 469 560 469 559 468 559 468 560 468 559 468 561 467 560 469 559 468 559 468 558 469 559 469 561 468 560 467 559 469 559 468 1535 468 1533 491 2976 3014 8997 492 1509 493 536 493 535 492 534 494 535 492 535 493 537 491 536 493 533 494 1509 493 536 493 536 491 1508 494 536 493 1509 492 1510 493 1510 491 1509 494 1508 493 1511 492 534 494 534 494 534 494 535 492 536 493 535 493 533 495 534 493 536 491 535 493 537 492 534 493 535 493 534 494 534 494 536 493 535 493 535 493 534 493 534 493 536 493 534 493 533 495 534 494 534 494 535 493 536 493 534 493 535 493 534 494 534 493 536 493 534 494 534 493 534 494 535 492 2973 3017 8996 493 1508 494 536 493 535 493 535 493 533 495 535 493 536 492 536 493 534 493 1508 494 535 494 535 492 1509 493 535 494 1507 494 1510 493 533 494 1509 494 1505 496 1509 494 1508 494 1512 491 1507 494 1509 494 1508 494 533 494 535 493 534 495 1507 494 1509 494 1509 493 533 494 535 493 536 493 534 494 535 492 1508 519 509 495 1508 493 533 495 1508 493 1509 493 534 494 1508 518 1484 494 535 492 533 519 508 495 534 493 535 494 533 495 534 494 534 494 534 495 1508 493 1507 494 76867 4382 4447 533 1656 532 509 533 1655 533 1655 533 510 533 509 533 1655 533 509 532 509 533 1656 533 509 532 510 532 1657 531 1657 531 509 533 1657 532 509 532 1656 532 1656 532 1655 533 1658 531 511 530 1656 533 1657 531 1656 532 510 533 510 531 510 532 510 532 1656 532 508 534 511 532 1656 532 1655 533 1655 533 509 532 512 531 509 532 511 531 508 533 508 534 511 532 509 533 1656 532 1655 533 1657 531 1656 532 1657 532 5202 4355 4447 534 1656 533 510 531 1655 533 1655 533 508 534 508 533 1657 532 508 533 509 533 1657 531 508 533 511 532 1656 532 1656 532 509 532 1656 531 511 532 1655 533 1656 531 1655 532 1656 531 509 532 1658 531 1658 530 1656 531 510 532 510 531 509 534 510 532 1657 530 511 530 510 531 1658 531 1656 532 1656 531 509 533 509 532 510 532 510 533 510 531 509 533 508 533 509 532 1659 531 1656 532 1656 532 1655 533 1655 533\n#\n# Model: Windfree Ac_test\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 549 17944 2988 9004 491 537 492 1510 491 537 490 537 490 537 492 536 491 537 491 536 491 537 491 1510 491 536 491 537 491 1509 492 1511 491 537 491 1509 492 1510 492 1509 492 1512 491 1510 491 536 491 535 492 537 492 535 492 535 492 535 493 535 493 537 492 535 493 536 491 534 494 534 493 537 492 536 492 535 493 535 493 536 492 536 492 536 493 536 491 535 492 535 493 535 493 537 492 536 491 535 492 535 493 535 492 536 493 535 492 536 492 534 493 535 493 537 492 1508 493 1510 493 2970 3017 8996 493 1508 494 533 494 534 494 534 493 534 493 535 494 533 494 533 494 537 491 1509 492 535 493 534 493 1510 493 533 494 1509 493 1508 492 1510 492 1507 494 1509 493 1507 494 534 493 536 491 533 494 535 493 534 493 534 494 535 492 535 492 537 492 535 492 535 493 534 493 534 493 535 494 533 494 534 494 534 494 536 491 537 490 535 494 534 493 535 493 534 493 534 493 535 494 535 493 535 492 534 493 534 493 536 493 534 493 535 492 534 494 534 493 535 493 536 493 2972 3015 8994 493 1508 493 535 492 534 493 535 493 535 492 536 493 536 492 534 494 537 491 1509 493 534 493 535 492 1510 493 535 492 1511 491 1509 492 534 493 1510 492 1509 492 1510 492 1509 492 1510 492 1509 492 1510 492 1509 492 535 492 536 493 533 494 1512 491 1509 492 1509 492 535 494 535 492 537 491 535 492 536 493 1510 491 535 492 1509 492 536 492 1510 492 1510 491 536 491 1510 492 1509 492 534 493 536 491 536 493 535 493 535 492 535 492 537 490 535 492 536 492 1508 493 1511 492 76863 4380 4448 507 1680 508 534 507 1682 507 1680 508 535 506 534 508 1680 507 536 507 533 508 1679 508 534 507 534 507 1680 508 1682 507 534 507 1678 509 534 507 1680 508 1682 507 1680 507 1679 508 533 508 1679 509 1681 508 1681 507 532 510 534 507 535 507 532 510 1682 507 534 508 533 508 1679 509 1680 508 1681 508 534 507 534 507 533 509 534 507 534 509 535 506 535 507 533 509 1680 508 1679 509 1682 507 1679 509 1678 509 5226 4354 4451 507 1679 509 533 508 1678 510 1682 508 534 507 532 509 1680 507 534 507 534 509 1680 508 533 509 534 507 1680 508 1680 508 534 509 1681 506 533 508 1680 508 1679 509 1681 508 1679 509 534 507 1679 509 1679 509 1681 508 533 508 533 509 533 509 534 508 1680 507 535 508 533 508 1680 508 1679 509 1680 508 535 508 534 507 534 507 534 507 533 508 535 508 534 508 533 509 1682 506 1681 507 1680 508 1682 507 1680 508\n#\n# Model: Windfree Remote3\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 602 17896 3013 8976 522 505 522 1481 521 507 522 507 521 505 522 507 520 505 523 506 522 504 523 1481 521 505 522 508 521 1481 520 506 521 505 523 1478 522 1482 520 1479 522 1480 522 1480 521 506 521 507 520 509 520 506 521 505 523 506 521 506 521 507 522 506 521 506 521 506 522 503 524 508 521 505 523 506 521 504 523 506 521 506 521 507 522 505 522 504 523 505 522 506 521 507 522 505 522 506 522 506 521 505 522 508 521 506 522 507 520 506 521 1483 519 1479 522 1482 520 1480 521 2944 3041 8969 520 1482 521 506 521 507 520 506 521 507 520 506 521 507 522 506 521 505 522 1483 519 506 521 506 521 1483 518 506 522 1481 519 1482 520 506 521 1482 520 1481 519 1482 520 1479 521 1481 521 1482 518 1482 520 1482 519 508 519 508 519 508 520 1481 519 1483 494 1508 493 533 494 533 496 534 493 533 494 532 495 535 493 1506 494 1506 495 534 494 1506 495 534 493 533 494 534 494 1507 519 508 519 509 493 534 495 534 493 532 495 533 494 534 494 1507 493 1507 495 1506 495 1509 493 76841 4380 4447 508 1680 507 533 508 1678 510 1681 508 533 508 533 508 1679 508 533 508 534 508 1679 508 533 508 533 508 1679 508 1678 509 535 508 1680 507 533 508 1679 508 1679 508 536 507 1679 508 533 508 1679 508 1679 508 1680 507 535 508 534 507 1680 507 532 509 1680 507 535 506 535 507 1681 506 1679 508 1680 508 533 508 534 509 533 508 532 509 533 508 533 508 535 508 533 508 1680 508 1679 508 1679 509 1679 509 1680 509 5225 4354 4450 507 1679 508 533 508 1679 508 1679 509 532 509 535 508 1677 510 531 510 534 507 1679 508 535 508 534 507 1679 508 1679 508 533 508 1680 508 532 509 1678 509 1678 509 533 508 1679 508 534 509 1679 508 1679 508 1679 508 534 507 534 509 1679 509 532 509 1678 509 532 509 534 508 1679 509 1679 508 1680 507 532 509 534 507 534 509 532 509 532 509 533 508 532 509 534 509 1679 508 1680 507 1678 509 1679 508 1680 509\n#\n# Model: Xiaomi AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 988 606 586 2210 587 1472 586 882 587 2211 586 375 584 2210 587 374 584 1472 586 374 585 376 582 375 583 400 559 400 558 883 587 375 584 375 583 2210 587 374 584 884 586 882 587 374 584 376 583 2211 586 884 585 375 584 375 583 374 584 374 584 375 584 1472 585 882 587 376 583 2211 586 884 585 2211 586 375 583 375 583 400 558 884 586 374 584 375 583 376 583 375 583 375 583 2211 586 2211 585 375 583 883 586\n#\n# Model: York AC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9024 4481 655 551 655 1653 654 550 656 1652 655 1652 656 550 656 550 656 550 656 550 656 551 655 1652 655 551 655 1652 655 550 656 551 655 1654 654 550 656 550 656 551 655 1652 655 550 656 551 654 550 656 1652 655 1651 657 550 655 551 655 550 656 1654 654 550 656 1653 654 551 654 551 655 1651 656 551 655 19984 655 550 656 551 655 550 656 551 655 550 655 551 655 551 655 550 656 1654 653 1653 655 1653 655 550 655 551 655 550 656 551 655 551 655 550 656 551 655 551 655 551 655 551 655 551 655 550 656 550 656 550 656 551 655 551 655 551 655 1652 656 550 656 551 655 550 656 39996 8999 4479 656 551 655 1652 656 550 656 1653 655 1653 655 550 656 551 655 550 656 551 655 551 655 1652 655 551 655 1652 655 550 656 551 655 1653 655 551 655 550 656 550 656 1652 655 551 654 551 655 551 655 1652 655 1652 656 551 655 551 655 552 654 551 655 1653 655 1653 655 551 655 549 656 1653 655 552 654 19984 655 1652 655 551 655 550 656 1652 656 551 655 551 655 551 655 1652 655 1652 655 551 656 1652 656 1653 655 1653 655 551 655 1652 655 551 655 551 655 551 654 551 654 551 655 551 655 1653 655 550 656 551 655 1652 656 1653 654 551 655 551 655 551 655 550 655 550 656 551 655\n#\n# Model: Fujitsu ASTG12LVCC\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3258 1573 427 404 426 404 425 1180 428 403 427 1183 425 402 427 402 428 402 427 1180 428 1181 427 404 426 403 427 402 428 1181 427 1181 427 402 427 405 425 402 427 402 427 403 427 402 428 402 428 403 426 401 429 403 427 402 428 403 427 403 427 1180 428 401 428 404 425 401 428 402 427 402 427 402 427 402 428 1180 427 401 428 403 427 402 427 401 428 1180 427 401 428 402 427 402 428 402 427 401 428 403 427 1180 427 402 427 1180 427 1180 427 1177 429 1179 427 1179 427 1178 428\n# \nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 39677 99167 3233 1570 425 405 425 404 425 1184 424 405 425 1182 426 405 424 404 426 404 425 1181 427 1182 426 403 427 403 426 404 426 1183 425 1183 425 403 426 404 426 406 424 404 425 405 425 402 427 405 425 404 425 403 426 404 426 404 425 402 427 405 424 1182 425 402 427 404 426 403 426 404 425 404 426 404 425 404 425 1183 424 406 423 404 426 403 426 404 425 1181 427 1182 426 1181 426 1181 426 1181 425 1182 425 1181 426 1182 426 403 426 404 425 1182 426 404 425 404 425 405 425 404 426 403 426 403 427 404 426 404 426 1182 426 1182 426 403 426 404 426 1182 426 405 424 404 426 403 426 1182 426 405 425 403 426 1182 426 404 426 1183 425 403 426 403 426 404 425 403 426 405 425 403 426 1182 425 1182 425 403 427 404 425 1181 426 403 427 403 426 404 425 406 424 404 426 404 425 404 426 404 425 404 426 404 426 404 426 404 426 404 425 404 426 404 426 403 426 403 427 404 425 402 427 405 425 403 426 404 425 404 425 404 425 405 425 404 425 404 425 404 426 403 426 402 427 403 427 403 426 1182 425 404 426 404 425 403 426 1182 425 403 426 1181 426 403 427 403 426 404 425 405 425\n# \nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 39674 99137 3228 1573 422 407 421 408 422 1185 423 408 422 1187 421 410 419 409 421 408 421 1186 422 1187 421 408 421 408 422 409 421 1187 420 1186 422 408 421 410 419 407 424 408 421 407 421 410 420 409 421 408 422 408 421 408 422 407 422 410 419 408 422 1187 420 408 421 408 422 408 421 408 421 408 421 408 420 409 421 1187 444 382 422 408 422 408 421 408 445 1162 419 1187 420 1184 423 1185 421 1184 423 1186 421 1186 421 1187 422 409 419 409 420 1186 422 407 420 409 422 409 420 407 422 407 422 411 419 406 421 409 422 1185 446 1162 420 409 421 409 421 1189 418 407 421 408 422 407 422 409 420 409 421 408 420 412 417 1187 421 407 422 408 420 410 421 408 421 409 421 409 445 384 420 410 421 407 421 407 422 409 421 1187 420 409 419 409 421 408 422 408 421 410 419 409 420 410 419 410 420 407 422 409 420 408 421 407 422 408 421 408 421 410 419 409 420 407 423 407 422 409 421 410 419 411 418 408 421 408 422 410 420 407 421 409 419 409 421 409 419 408 422 407 422 407 422 409 420 1188 419 409 421 409 420 409 419 1189 419 1186 421 1188 419 1187 420 408 421 407 422 1188 419\n# \nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 39689 99188 3229 1576 421 407 422 409 420 1188 419 409 421 1187 422 408 422 409 419 410 419 1187 422 1186 423 410 419 409 421 409 420 1187 420 1188 420 410 447 382 420 409 422 410 446 383 422 409 420 407 422 410 395 435 420 408 422 407 422 410 420 409 445 1162 420 410 420 409 420 410 420 409 421 410 419 409 421 409 419 1189 420 407 422 409 395 437 419 410 418 1186 422 1186 423 1187 420 1185 422 1188 420 1184 421 1188 419 1188 419 408 420 410 419 1186 421 408 420 410 419 410 419 409 419 411 418 409 421 410 419 409 420 1187 445 1163 419 412 417 409 420 1188 419 409 419 410 420 409 444 1164 418 1187 419 1189 419 409 419 1187 420 408 422 409 419 410 420 409 419 410 419 434 393 411 420 409 421 408 421 409 419 409 421 1188 418 410 419 410 420 410 418 412 417 409 445 385 419 409 420 410 420 408 419 409 421 410 419 411 419 408 446 382 421 409 420 409 420 410 418 409 420 409 419 410 419 412 442 384 419 411 416 412 419 409 420 410 419 410 419 410 418 410 420 409 420 409 420 434 394 1187 419 412 417 410 418 410 420 1188 419 1187 420 1188 419 410 419 1188 419 411 418 434 396\n# \nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 39692 99118 3226 1571 423 406 423 405 423 1185 421 405 424 1183 423 406 422 408 422 405 423 1184 446 1160 422 409 420 406 424 405 424 1185 422 1183 424 404 425 406 422 408 421 406 423 407 422 406 423 407 421 406 424 407 422 407 422 404 425 407 421 409 420 1185 422 406 423 408 421 404 424 405 424 407 447 383 421 406 424 1184 447 380 424 406 422 409 421 407 423 1183 447 1159 424 1185 422 1185 421 1184 422 1185 422 1185 421 1185 423 407 423 406 423 1185 423 406 424 406 423 408 446 381 424 406 423 408 421 406 424 406 423 1185 423 1184 422 407 423 407 422 1187 421 408 421 407 423 407 423 405 424 1185 422 1186 421 1184 423 407 422 407 422 1186 422 407 422 406 423 408 422 405 423 408 447 383 420 409 421 406 423 407 423 1184 423 407 423 407 422 408 421 408 423 406 424 406 422 409 422 406 423 408 421 408 421 406 422 406 424 407 422 406 423 409 421 407 422 408 423 406 423 406 423 409 446 382 447 384 420 407 423 405 424 406 423 406 423 407 423 407 422 406 423 405 422 407 424 406 422 1185 422 406 423 407 422 1183 423 1184 422 407 422 1185 423 1186 421 1184 424 407 422 1185 422\n# \nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 39670 99106 3227 1570 424 406 422 407 422 1183 424 405 424 1184 448 380 423 407 421 406 424 1183 424 1185 421 406 423 404 424 405 424 1184 423 1182 425 407 448 380 423 407 422 405 423 406 422 406 424 405 423 407 421 406 423 407 422 405 424 405 423 406 423 1184 422 408 421 408 422 405 424 406 421 407 422 406 423 405 423 1183 424 406 423 405 423 405 423 405 423 1186 421 1184 422 1184 422 1185 422 1184 447 1159 423 1184 422 1184 422 408 421 407 423 1184 421 407 448 381 422 405 423 409 421 406 422 406 422 407 422 406 423 1183 423 1185 422 406 423 405 424 1184 423 408 421 405 424 405 424 1184 422 1185 422 1184 422 407 423 408 420 409 420 1185 447 382 423 405 423 408 421 406 423 407 422 406 423 406 423 408 421 406 423 1183 424 407 422 406 424 405 424 406 423 407 423 406 423 408 422 407 422 405 424 408 421 407 422 407 422 406 423 406 423 407 422 406 423 406 422 408 421 407 422 408 421 407 422 406 423 408 422 406 423 405 423 409 422 406 422 406 423 406 423 407 422 407 423 405 424 1184 423 407 421 406 424 1184 423 1184 422 407 423 1183 423 405 424 1184 423 409 420 407 422\n#\n# Model: Daikin FTXN25LV1B9\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9876 9703 9956 9681 4738 2366 489 310 423 822 488 822 488 311 422 823 487 311 423 310 424 310 423 310 424 823 487 311 422 310 424 823 487 310 424 311 422 310 424 311 422 823 487 311 422 310 423 310 424 823 488 311 423 310 423 310 423 823 488 310 423 310 424 310 424 823 487 310 424 311 423 824 487 823 487 823 487 310 423 823 487 311 423 311 423 310 424 311 422 824 487 310 423 311 422 824 487 311 422 310 424 310 423 824 486 824 487 310 424 310 423 311 423 824 487 311 422 310 423 310 424 310 423 824 486 824 486 310 424 824 486 824 486 824 486 20121 4685\n# \nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9907 9648 9958 9677 4742 2362 492 311 422 820 490 820 435 312 477 821 434 312 421 312 422 312 422 876 434 312 422 312 422 312 422 876 434 312 422 312 422 312 421 312 422 876 434 312 422 312 421 312 422 876 434 312 422 311 422 312 477 821 434 312 422 312 421 312 477 821 434 312 422 312 422 876 434 877 434 876 434 312 422 876 434 312 422 311 422 312 422 312 422 876 434 312 422 312 422 876 434 312 422 312 421 312 422 877 433 876 434 312 422 312 422 312 422 876 434 312 422 312 421 312 422 312 422 877 433 312 422 877 433 312 422 877 433 312 422 20174 4740\n# \nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9827 9757 9876 9732 4687 2418 435 311 422 876 434 876 434 311 423 877 433 312 421 311 423 311 422 311 423 311 423 311 423 877 433 877 433 311 423 311 423 312 422 877 433 877 433 311 423 311 423 311 423 877 434 311 422 311 423 311 422 877 433 311 423 312 421 312 422 877 433 311 423 312 421 877 433 877 433 877 434 311 423 877 433 311 423 311 423 311 422 311 423 877 433 312 422 311 423 877 433 311 423 312 422 311 423 877 433 878 433 311 423 312 422 312 422 877 433 311 423 311 422 311 423 312 422 878 432 312 422 877 433 311 423 877 433 878 433 20174 4685\n# \nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9898 9680 9901 9734 4685 2420 434 312 421 876 434 876 434 312 422 876 434 312 422 312 422 312 422 312 421 312 422 312 422 877 433 877 433 312 422 312 421 312 422 877 433 877 433 312 422 312 422 312 422 876 434 312 422 312 422 312 422 877 434 312 422 312 421 312 422 877 433 312 421 312 422 877 433 877 434 877 433 312 422 877 433 312 422 312 421 312 422 312 422 877 433 312 421 312 422 877 433 312 422 312 422 312 421 312 422 312 422 312 422 312 421 877 433 877 434 312 421 312 422 312 422 312 422 877 433 312 422 877 433 877 433 312 421 877 433 20174 4684\n# \nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9932 9649 9959 9677 4742 2362 492 311 422 819 491 819 491 311 422 820 490 311 423 311 423 311 423 311 422 820 490 310 423 310 424 820 490 311 423 311 423 310 424 820 490 311 422 820 491 311 423 311 422 820 491 311 422 311 423 311 423 820 490 311 423 311 423 311 423 820 490 310 424 311 423 820 490 820 490 820 490 310 424 820 490 311 423 310 424 310 424 311 422 820 491 311 422 310 424 820 490 310 424 311 423 310 423 311 423 820 490 820 490 311 423 820 490 311 422 311 423 311 423 311 423 311 422 821 490 311 422 821 490 820 490 311 422 821 490 20118 4740\n# \nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9878 9704 9903 9733 4687 2418 435 311 422 875 435 875 435 311 423 875 435 311 423 311 422 311 423 311 423 875 435 311 423 311 423 875 435 311 423 311 423 311 422 876 434 311 423 876 434 311 423 311 423 876 434 311 423 311 423 311 423 876 434 311 423 311 423 311 423 875 435 311 422 311 423 876 434 876 435 876 434 311 423 876 434 311 423 311 423 311 422 311 423 876 434 311 422 311 423 876 434 311 423 311 422 311 423 876 434 876 434 311 423 311 422 311 423 876 435 311 422 311 422 311 423 311 423 876 434 311 423 876 434 311 423 311 423 876 434 20174 4685\n#\n# Model: Toyotomi KTN22-12R32\n#\nname: Off\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9067 4414 724 1584 753 452 754 453 753 478 728 478 727 479 726 480 726 481 725 1584 725 1585 724 1584 725 482 724 482 725 482 725 482 724 483 724 483 723 482 725 483 723 483 723 482 724 1585 724 482 724 482 724 483 724 482 725 483 723 483 723 1584 724 482 724 1585 723 482 724 483 723 1585 723 483 723 19925 724 483 724 1585 724 482 724 482 724 482 724 482 724 483 724 482 724 482 724 1585 724 483 724 483 724 482 725 483 724 1585 723 1585 724 483 723 483 724 483 724 483 724 482 723 483 724 482 724 482 724 483 723 483 723 483 724 483 723 483 724 1585 724 1585 723 1585 724 39949 9071 4415 725 482 725 481 725 482 725 483 723 481 725 482 724 483 724 482 724 482 724 482 724 482 724 482 725 483 723 483 723 482 725 483 722 483 724 483 723 482 724 483 724 483 723 482 724 483 723 483 724 483 723 483 723 483 723 483 724 482 724 1585 723 483 724 1585 725 482 724 1585 724 483 724 19926 724 483 723 482 725 482 724 482 724 482 725 482 724 482 725 483 724 482 723 482 725 483 723 482 725 482 725 483 723 483 724 483 723 483 724 483 723 482 724 483 723 483 724 482 725 482 724 483 724 483 723 482 724 483 723 483 723 482 725 1585 723 483 724 1585 724\n# \nname: Dh\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9042 4416 751 454 753 1555 753 453 754 1556 753 1558 750 479 726 480 726 482 724 1584 725 481 724 482 725 1585 725 482 725 482 724 482 724 482 725 482 725 483 723 483 724 482 724 482 724 1586 723 1585 723 482 724 483 724 483 724 483 724 482 725 1585 724 482 724 1585 724 484 723 482 724 1585 723 483 724 19928 723 482 725 482 724 482 725 483 724 483 724 482 725 482 724 483 724 483 723 1585 723 482 724 483 723 482 724 483 724 1585 724 1585 724 482 724 482 724 482 725 482 725 482 724 483 724 483 723 483 724 482 724 483 724 483 724 483 725 1585 724 483 723 482 724 1585 723 39946 9070 4414 725 481 725 481 725 482 724 482 724 482 724 481 725 482 724 482 724 482 725 483 723 482 725 483 724 482 725 482 724 482 724 482 724 483 723 483 724 483 724 482 725 483 724 482 724 482 725 483 723 482 725 483 723 482 724 483 724 482 724 1584 724 482 724 1585 724 483 724 1586 723 482 725 19926 724 482 724 482 724 482 725 483 723 482 725 482 725 482 724 483 724 482 724 482 725 483 724 482 725 483 723 483 723 483 723 482 724 483 724 483 723 482 724 483 724 482 724 483 723 483 723 483 724 483 723 483 723 482 724 483 723 483 724 1585 724 483 723 1586 723\n# \nname: Heat_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9040 4431 702 503 702 503 702 1604 758 1548 756 448 729 475 730 475 729 477 728 1579 726 1579 727 1580 726 479 726 481 724 480 724 481 724 482 724 482 723 482 723 482 723 482 723 483 722 1583 724 1584 722 483 723 483 722 482 723 483 722 483 723 1584 722 482 724 1584 722 482 723 483 722 1585 721 483 723 19907 724 481 723 482 723 482 723 482 723 482 723 482 723 483 723 483 722 482 723 1584 722 482 722 482 723 483 723 482 723 1584 723 1584 721 483 722 483 722 483 722 484 721 482 723 483 723 483 722 483 722 483 723 483 722 484 721 484 721 1607 699 483 722 483 722 1608 698 39905 9065 4406 726 479 726 480 724 480 725 481 725 481 724 482 723 482 723 482 724 482 723 483 723 482 723 482 723 482 723 482 724 483 722 482 723 483 723 483 723 483 722 483 723 483 722 483 722 483 722 507 699 482 723 484 722 483 722 483 722 483 722 1607 699 483 722 1585 722 483 722 1584 723 506 698 19908 723 481 724 482 723 482 724 482 722 482 724 482 724 482 723 483 722 482 723 483 722 482 723 483 722 483 722 483 722 482 723 506 699 483 722 484 722 482 723 507 700 484 721 483 722 484 722 483 722 507 698 506 699 507 699 507 698 507 698 1608 698 507 698 1607 699\n# \nname: Heat_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9074 4437 701 506 700 506 700 1607 702 1607 702 505 701 505 701 505 726 480 727 480 726 1582 726 1584 725 1583 724 481 726 481 725 482 724 483 724 482 724 482 725 482 724 482 725 483 724 1584 725 1584 725 482 724 482 726 482 724 482 725 483 724 1584 724 483 723 1585 723 483 724 482 725 1584 725 483 723 19925 724 482 724 482 724 482 725 482 725 482 725 483 724 482 724 482 724 482 724 1584 724 483 724 482 724 481 724 482 724 1585 724 1585 724 482 724 482 724 482 725 483 723 483 724 483 724 482 724 482 724 483 723 482 725 482 724 482 724 483 724 482 724 483 724 483 723 39945 9072 4414 725 481 726 481 725 482 724 482 725 481 725 482 724 482 724 482 725 482 724 483 724 483 724 483 723 482 725 482 724 482 724 483 723 482 724 483 724 483 723 483 724 483 724 482 725 482 725 483 724 483 724 482 725 483 724 482 724 482 725 1585 724 483 724 1584 724 482 724 1585 724 483 723 19929 723 482 724 482 725 482 724 483 724 483 723 483 723 482 724 482 724 482 724 482 724 482 725 482 724 482 724 483 724 483 723 482 724 482 724 483 724 483 723 482 725 483 724 482 724 482 725 482 724 483 724 483 724 483 724 483 724 482 724 1585 723 483 724 1585 724\n# \nname: Cool_hi\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9150 4356 783 1525 815 419 787 419 759 1549 760 447 758 447 758 449 756 451 755 451 755 451 754 452 754 453 754 452 754 453 753 453 753 454 753 454 752 453 753 454 752 454 753 455 751 1557 725 1585 725 481 725 481 725 482 725 482 725 482 724 1584 725 482 724 1584 725 482 724 481 726 1584 724 482 724 19927 724 481 726 482 724 482 725 481 725 481 725 482 725 482 724 481 726 481 725 1584 724 481 725 481 725 482 725 481 725 1584 725 1585 724 482 725 482 724 482 725 482 724 482 725 482 724 481 725 482 725 482 724 482 725 482 724 482 724 1584 724 1584 724 1584 724 1585 725 39945 9071 4412 726 480 726 480 726 481 725 481 725 481 725 481 725 481 725 482 725 481 724 481 726 482 725 481 725 482 724 482 724 482 725 482 724 481 725 482 725 482 725 482 724 482 724 482 725 481 725 482 725 482 725 481 726 482 724 482 724 481 726 1584 725 482 724 1584 724 482 724 1585 724 482 725 19924 725 481 725 481 726 481 725 481 725 481 726 481 724 482 725 482 724 481 725 481 725 481 725 481 726 482 725 481 725 481 725 482 724 481 752 455 752 454 753 455 751 454 752 454 753 454 751 453 753 454 752 454 753 454 753 454 752 455 752 1556 753 453 754 1557 752\n# \nname: Cool_lo\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9049 4436 702 1606 703 504 730 476 759 1548 758 448 730 476 730 476 730 477 729 1580 727 1581 727 1581 727 480 726 481 725 481 725 481 724 482 724 507 699 483 724 483 723 507 699 506 701 1609 699 1609 699 507 700 507 699 507 700 507 699 507 700 1610 699 507 700 1609 700 507 699 507 700 1610 699 507 700 19928 723 482 724 483 723 483 723 484 722 507 699 507 700 507 699 507 699 507 700 1609 700 508 699 507 700 507 699 507 699 1609 700 1609 699 507 699 508 699 507 699 507 700 507 700 507 700 507 699 507 699 507 700 507 699 507 700 507 699 507 700 1610 699 1610 699 507 700 39944 9074 4410 727 479 727 480 726 481 726 481 725 482 725 483 723 483 724 507 699 507 700 484 722 507 699 507 699 507 699 506 700 507 699 507 700 507 699 507 698 507 699 508 699 507 699 507 699 507 700 507 700 507 698 507 699 508 699 507 699 507 699 1610 699 507 699 1610 699 507 699 1609 699 507 699 19926 724 483 723 506 700 507 699 507 700 507 700 507 700 507 699 507 700 507 700 507 699 507 700 507 699 507 699 507 700 506 700 508 699 507 700 507 700 507 699 507 699 507 700 507 700 507 700 508 699 507 699 507 699 508 699 507 699 507 700 1610 698 507 699 1609 699\n#"
  },
  {
    "path": "applications/main/infrared/resources/infrared/assets/audio.ir",
    "content": "Filetype: IR library file\nVersion: 1\n#\n# Model: Yamaha RAV15 and NoName Unknown Audio remote\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 43 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 07 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 40 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 44 00 00 00\n#\n# Model: Western Digital Unknown\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 12 ED 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 05 FA 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 00 FF 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 01 FE 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 84 79 00 00\ncommand: 02 FD 00 00\n#\n# Model: Yamaha RX-V375\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 7E 00 00 00\ncommand: 2A 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 7A 00 00 00\ncommand: 1A 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 7A 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 7A 00 00 00\ncommand: 1B 00 00 00\n#\n# Model: SVEN HT-415\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 41 00 00 00\ncommand: 42 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 41 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 41 00 00 00\ncommand: 1B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 41 00 00 00\ncommand: 56 00 00 00\n#\n# Model: HUAYU AKB74475490\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 08 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: B0 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: BA 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 03 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 8F 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 8E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 09 00 00 00\n#\n# Model: Samsung HW-K450 Soundbar\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4637 4376 612 419 584 420 584 420 583 421 582 1427 531 1477 531 472 532 472 557 1452 556 1451 557 1451 557 1452 556 447 557 448 556 449 555 449 555 4453 554 450 554 450 554 451 553 450 554 451 553 451 553 451 553 450 554 1455 553 1454 554 1454 554 451 553 1454 554 1454 554 1455 553 1454 554 451 553 450 554 450 554 1455 553 55439 4554 4458 555 449 555 449 555 450 554 450 554 1455 553 1454 554 451 553 450 554 1454 554 1454 554 1454 554 1455 553 450 554 451 553 451 553 451 553 4453 554 451 553 451 552 451 553 451 553 451 553 451 553 451 553 451 553 1455 553 1455 553 1455 553 451 553 1455 553 1455 553 1455 553 1455 553 451 553 451 552 451 553 1455 553\n#\nname: Play\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4636 4380 612 392 612 394 610 394 610 419 583 1399 557 1452 556 473 556 448 556 1428 581 1453 555 1453 555 1453 555 449 555 450 554 451 553 452 552 4457 551 452 552 452 552 452 552 452 552 452 552 1457 551 452 552 1457 551 452 552 452 552 453 551 1457 552 1457 551 452 552 1457 551 452 552 1457 551 1457 551 1457 552 452 552 55450 4551 4461 553 451 553 452 552 452 552 452 552 1456 552 1456 552 452 552 452 552 1456 552 1456 552 1456 552 1456 552 452 552 452 552 453 551 453 551 4456 551 453 551 453 551 453 551 453 551 453 551 1457 551 453 551 1457 552 453 551 454 550 454 550 1457 552 1457 551 454 550 1457 551 454 550 1458 551 1457 551 1458 550 454 550\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4640 4405 583 420 583 421 582 421 583 422 581 1427 531 1478 530 473 531 472 557 1452 557 1452 556 1452 556 1452 556 448 556 448 556 449 555 450 554 4454 554 451 553 451 553 451 553 451 553 1455 554 1455 553 1455 553 451 553 1455 553 1456 553 1456 553 451 553 451 553 451 553 451 554 1455 554 451 553 452 553 451 553 1456 553 55447 4556 4458 555 449 555 450 554 450 554 450 554 1455 553 1455 553 451 553 451 553 1455 553 1455 553 1455 553 1455 553 451 553 451 553 451 553 451 553 4454 553 451 553 450 554 451 553 450 554 1455 553 1455 553 1455 553 451 553 1455 553 1455 553 1455 553 451 553 451 553 451 553 450 554 1455 553 451 553 451 553 451 553 1455 553\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4636 4378 613 393 611 392 612 393 611 393 557 1451 558 1450 610 420 583 421 557 1427 581 1452 556 1452 556 1452 556 448 555 449 555 450 554 450 554 4455 553 451 553 451 553 451 553 451 553 451 553 451 553 452 552 1456 553 1456 552 1456 553 1456 552 451 553 1456 553 1456 552 1456 553 451 553 451 553 452 552 451 553 1456 552 55452 4553 4461 553 450 554 451 553 451 553 451 553 1456 553 1456 552 451 553 451 553 1456 552 1455 553 1455 553 1455 553 451 553 451 553 451 553 451 553 4456 552 451 553 451 553 451 553 451 553 451 553 451 553 451 553 1456 552 1455 553 1455 553 1455 553 451 553 1455 553 1456 552 1456 552 451 553 451 553 451 553 451 553 1456 552\n#\nname: Prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 255 113623 4638 4378 613 391 612 392 559 446 558 446 558 1477 531 1477 532 472 532 472 532 1476 532 1476 532 1476 532 1477 531 473 555 449 555 449 555 450 554 4455 554 450 554 450 554 450 554 450 554 1455 554 1455 554 450 554 1455 554 450 555 450 554 450 554 1455 554 451 553 451 553 1455 554 450 554 1455 554 1456 553 1455 554 450 554\n#\nname: Next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4557 4430 611 392 610 394 559 445 559 446 558 1451 558 1477 531 448 556 472 532 1476 532 1477 532 1477 531 1477 531 473 556 449 555 449 555 450 554 4454 554 450 554 450 554 450 554 450 555 450 554 450 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 451 553 450 554 1455 554 1455 554 1455 554 450 554 55458 4555 4459 554 450 554 450 554 450 554 450 554 1455 553 1455 553 450 554 450 554 1455 553 1455 553 1455 553 1455 553 450 554 450 554 450 554 450 554 4454 554 450 554 450 554 450 554 451 553 450 554 450 554 1455 553 1455 553 451 553 450 554 450 554 1455 553 1455 553 1455 553 450 554 451 553 1455 554 1455 553 1455 553 450 554\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4639 4406 586 418 585 393 559 447 557 447 557 1477 532 1477 532 472 532 472 532 1476 533 1476 532 1476 532 1476 532 473 555 449 555 449 555 449 555 4455 554 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 553 1455 553 450 554 450 554 1455 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 55454 4557 4458 555 449 555 449 555 450 554 450 554 1455 554 1455 553 450 554 450 554 1455 554 1455 554 1454 554 1455 554 450 554 450 554 450 555 450 554 4455 553 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 450 554 450 554 1455 554 1455 553 1455 554 450 554 450 554 450 554 1455 554\n#\n# Model: Edifier R1850DB\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 46 B9 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 5E A1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 05 FA 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 49 B6 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 02 FD 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 1E E1 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 41 BE 00 00\n#\n# Model: Grundig CMS 5000\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 10 EF 00 00\n# Also Pause\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 02 FD 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 0D F2 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 17 E8 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 13 EC 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 11 EE 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 0C F3 00 00\n#\n# Model: Panasonic SA-PM193\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 00\ncommand: D1 03 00 00\n# CD play/pause, tape play also exists but probably less commonly used\nname: Play\ntype: parsed\nprotocol: Kaseikyo\naddress: AA 02 20 00\ncommand: A0 00 00 00\n# same as above\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: AA 02 20 00\ncommand: A0 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: A0 02 20 00\ncommand: 00 02 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: A0 02 20 00\ncommand: 10 02 00 00\n#\nname: Next\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 01\ncommand: A1 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 01\ncommand: 91 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Kaseikyo\naddress: A0 02 20 00\ncommand: 20 03 00 00\n#\n# Model: NAD C316BEE\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 25 DA 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 94 6B 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 88 77 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 8C 73 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 01 FE 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 4A B5 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 05 FA 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 06 F9 00 00\n#\n# Model: Dutch Originals Sound Bar\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 07 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 6A 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 6A 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 5E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 85 00 00 00\n#\n# Sony audio remote RM-SC3\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 32 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 39 00 00 00\n#\n# Model: Sony MHC_GSX75\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 38 00 00 00\n#\n# Model: Elac EA101EQ-G\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 46 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 16 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 18 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 55 00 00 00\n#\n# Model: Philips FW750C\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 10 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 10 00 00 00\ncommand: 11 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 35 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 36 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 10 00 00 00\ncommand: 0D 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Pioneer VSX-D1-S\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 0A F5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5C A3 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1E E1 00 00\n# # Model: Adastra WA215\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 45 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 19 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 44 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 07 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 49 00 00 00\n#\n# Model: Adcom GTP500II\n#\nname: Power\ntype: parsed\nprotocol: NEC42\naddress: 51 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC42\naddress: 51 00 00 00\ncommand: 1F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC42\naddress: 51 00 00 00\ncommand: 1E 00 00 00\n#\n# Model: Aiwa RC-T506\n#\nname: Power\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 4D 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 4E 00 00 00\n#\n# Model: AIWA XR-EM300\n#\nname: Mute\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 4C 00 00 00\n#\n# Model: ASR EMITTER1HD\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 10 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Auna AV2_CD508\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 16 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 12 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 4B 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 04 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 13 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 5E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 06 00 00 00\n#\n# Model: CambridgeAudio 650A\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 2B 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 30 00 00 00\n#\n# Model: AEG\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0E 00 00 00\n#\n# Model: AIWA NSXR71\n#\nname: Play\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 40 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 44 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC42\naddress: 6E 00 00 00\ncommand: 41 00 00 00\n#\n# Model: Denon RC_1199\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 03 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 04 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 05 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 06 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 47 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 46 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 0F 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 0E 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: 51 54 32 01\ncommand: 3F 00 00 00\n#\n# Model: Fisher SLIM-1500\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 06 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 13 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 13 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 1B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: A0 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 20 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: A2 00 00 00\ncommand: 60 00 00 00\n#\n# Model: GPX cd_radio\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: 81 00 00 00\ncommand: 09 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: Samsung32\naddress: 81 00 00 00\ncommand: 11 00 00 00\n#\n# Model: GPX HC221B\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 04 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 02 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 01 00 00 00\n#\n# Model: LG\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 10 00 00 00\ncommand: 1E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 10 00 00 00\ncommand: 1F 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: 10 00 00 00\ncommand: 05 00 00 00\n#\n# Model: Marantz RC2100DR_CD\n#\nname: Prev\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 21 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 20 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 34 00 00 00\n#\n# Model: Marantz RC2100DR_CDR\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 0C 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 21 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 20 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 34 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 30 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 36 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: RC5\naddress: 1A 00 00 00\ncommand: 35 00 00 00\n#\n# Model: Onkyo DX-7333\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 1F E0 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 1C E3 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 1B E4 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 01 FE 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 00 FF 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 1E E1 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 2C 00 00\ncommand: 1D E2 00 00\n#\n# Model: Panasonic SC-HC58\n#\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 00\ncommand: 01 00 00 00\n#\n# Model: Philips CD_720\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 10 00 00 00\n#\n# Model: Philips CD_Player_723\n#\nname: Prev\ntype: parsed\nprotocol: RC5\naddress: 14 00 00 00\ncommand: 32 00 00 00\n#\n# Model: Rockola juke\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 4D 00 00 00\ncommand: 00 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 4D 00 00 00\ncommand: 0F 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 4D 00 00 00\ncommand: 10 00 00 00\n#\n# Model: Sony CFD-S35CP\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC15\naddress: 44 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC15\naddress: 44 00 00 00\ncommand: 13 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: SIRC15\naddress: 64 00 00 00\ncommand: 32 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC15\naddress: 64 00 00 00\ncommand: 39 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC15\naddress: 64 00 00 00\ncommand: 38 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC15\naddress: 64 00 00 00\ncommand: 3A 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC15\naddress: 64 00 00 00\ncommand: 3B 00 00 00\n#\n# Model: SONY minidisk-deck\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 15 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 2A 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 29 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 28 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 20 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC\naddress: 0F 00 00 00\ncommand: 2B 00 00 00\n#\n# Model: Sony RM-R52_RCD-W500C_Deck-A\n#\nname: Play\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 32 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 39 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 38 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 30 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 31 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC\naddress: 11 00 00 00\ncommand: 34 00 00 00\n#\n# Model: Sony RM-R52_RCD-W500C_Deck-B\n#\nname: Play\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 2A 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 29 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 28 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 20 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 21 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 5A 19 00 00\ncommand: 2C 00 00 00\n#\n# Model: TEAC RC-505_Remote_Control_Unit\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 86 61 00 00\ncommand: 08 F7 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 86 61 00 00\ncommand: 05 FA 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 86 61 00 00\ncommand: 09 F6 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 86 61 00 00\ncommand: 0D F2 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 86 61 00 00\ncommand: 0C F3 00 00\n#\n# Model: Unkown KC-806\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 00 FF 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 04 FB 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 17 E8 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 02 FD 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 01 FE 00 00\n#\n# Model: Winnes KC809\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 16 E9 00 00\n#\n# Model: Denon RC1253\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 05 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 70 01 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 71 01 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 72 01 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 22 00 00 00\n#\n# Model: Douk ST-01_Pro\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 45 FF 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 44 FF 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 40 FF 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 43 FF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 09 FF 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 15 FF 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: AA 33 00 00\ncommand: 47 FF 00 00\n#\n# Model: Edifier r1280t\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 3C C3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 4D B2 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 2B D4 00 00\n#\n# Model: Edifier r1700bt\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 09 F6 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 0C F3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 00 FF 00 00\n#\n# Model: Edifier r1855db\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 06 F9 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 47 B8 00 00\n#\n# Model: Edifier r2000db\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 00 FF 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 01 FE 00 00\n#\n# Model: Emotiva PT100_TA100\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 79 00 00\ncommand: 80 7F 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 79 00 00\ncommand: 99 66 00 00\n#\n# Model: Grundig CMS_5000\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 30 FC 00 00\ncommand: 05 FA 00 00\n#\n# Model: Harman Kardon AVI200\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 70 00 00\ncommand: C0 3F 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 80 70 00 00\ncommand: C1 3E 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 80 70 00 00\ncommand: C7 38 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 80 70 00 00\ncommand: C8 37 00 00\n#\n# Model: DTR-7\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D2 6D 00 00\ncommand: 04 FB 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D2 6C 00 00\ncommand: 47 B8 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: D2 6D 00 00\ncommand: 02 FD 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: D2 6D 00 00\ncommand: 03 FC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: D2 6D 00 00\ncommand: 05 FA 00 00\n#\n# Model: Logitech Z906\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 02 A0 00 00\ncommand: 80 7F 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 02 A0 00 00\ncommand: EA 15 00 00\n#\n# Model: NAD712\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 80 7F 00 00\n#\n# Model: NAD Amp_1\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: C8 37 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 02 FD 00 00\n#\n# Model: Onkyo\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D2 6C 00 00\ncommand: CB 34 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 6C 00 00\ncommand: 54 AB 00 00\n#\n# Model: Onkyo RC627S\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 04 FB 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D2 04 00 00\ncommand: 47 B8 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 02 FD 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 03 FC 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 1B E4 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 1F E0 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 1C E3 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 8B 74 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 8A 75 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 1E E1 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 03 00 00\ncommand: 1D E2 00 00\n#\n# Model: Onkyo RC866M_Receiver\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 90 6F 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 80 7F 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 8F 70 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 91 6E 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 8D 72 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 02 00 00\ncommand: 8E 71 00 00\n#\n# Model: Onkyo RC898M\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: D2 11 00 00\ncommand: 05 FA 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: D2 19 00 00\ncommand: 82 7D 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: D2 19 00 00\ncommand: 83 7C 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 90 6F 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 81 7E 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 80 7F 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 8F 70 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 91 6E 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 8D 72 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: D2 14 00 00\ncommand: 8E 71 00 00\n#\n# Model: Panasonic N2QAYB000210\n#\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: A2 02 20 00\ncommand: 02 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: A2 02 20 00\ncommand: 62 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: Kaseikyo\naddress: A2 02 20 00\ncommand: A2 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 02\ncommand: B1 02 00 00\n#\n# Model: Panasonic SA_PM602\n#\nname: Play\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 00\ncommand: 61 00 00 00\n#\n# Model: Panasonic SC-PMX92-94\n#\nname: Next\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 00\ncommand: 31 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Kaseikyo\naddress: AC 02 20 00\ncommand: 21 00 00 00\n#\n# Model: Philips BTD2180\n#\nname: Power\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 0C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 10 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 2C 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 11 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 21 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 31 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 20 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: RC6\naddress: 04 00 00 00\ncommand: 83 00 00 00\n#\n# Model: Pioneer AXD7741\n#\nname: Power\ntype: parsed\nprotocol: Pioneer\naddress: A5 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Pioneer\naddress: A5 00 00 00\ncommand: 0A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Pioneer\naddress: A5 00 00 00\ncommand: 0B 00 00 00\n#\n# Model: Pioneer VSX-D1-S\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 06 F9 00 00\n#\n# Model: Pioneer XXD3105\n#\nname: Play\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 0C 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 5B 00 00 00\n#\n# Model: Portta Toslink_Audio_Switcher\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 17 00 00 00\n#\n# Model: Pyle P2203ABTU\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 05 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0B 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 4D 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 54 00 00 00\n#\n# Model: Rega IO\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 08 00 00 00\n#\n# Model: Revo Superconnect_BZAWDFB0315H2\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 12 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 59 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 18 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 58 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 5B 00 00 00\n#\n# Model: Rio Sonic_Blue_059PXC\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 84 7B 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 85 7A 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 90 6F 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 83 7C 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 87 78 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 80 7F 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 8A 75 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 82 13 00 00\ncommand: 81 7E 00 00\n#\n# Model: SiriusXM Onyx_EZR\n#\nname: Power\ntype: parsed\nprotocol: RC5X\naddress: 1B 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5X\naddress: 1B 00 00 00\ncommand: 0D 00 00 00\n#\n# Model: SMSL RC-8A\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 12 34 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 12 34 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 12 34 00 00\ncommand: 0B F4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 12 34 00 00\ncommand: 09 F6 00 00\n#\n# Model: SMSL RC-8C\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 12 36 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 12 36 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 12 36 00 00\ncommand: 0B F4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 12 36 00 00\ncommand: 09 F6 00 00\n#\n# Model: Sony Amp\n#\nname: Power\ntype: parsed\nprotocol: SIRC15\naddress: 30 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC15\naddress: 30 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC15\naddress: 30 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: SIRC15\naddress: 30 00 00 00\ncommand: 14 00 00 00\n#\n# Model: Sony I-WXH-80\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 0B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 06 00 00 00\n#\n# Model: Sony MHC-GS300AV\n#\nname: Power\ntype: parsed\nprotocol: SIRC15\naddress: 10 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC15\naddress: 10 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC15\naddress: 10 00 00 00\ncommand: 13 00 00 00\n#\n# Model: Sony RMT-AA320U_AV\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 38 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 31 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 30 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 7D 00 00 00\n#\n# Model: Sony RM_AMU009_Sony_Audio_System_CMT\n#\nname: Prev\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 30 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 31 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 3A 07 00 00\ncommand: 34 00 00 00\n#\n# Model: Sony STR-DH590\n#\nname: Prev\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 33 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 34 00 00 00\n#\n# Model: Firetv EVG487\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 67 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 38 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 3C 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 3D 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 8C 00 00 00\n#\n# Model: Audac IMEO2\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 21 DE 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 2B D4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 1D E2 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 2A D5 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 13 EC 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 14 EB 00 00\n#\n# Model: Bestisan7020\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 29 A1 00 00\ncommand: 7F 80 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 29 A1 00 00\ncommand: 9B 64 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 29 A1 00 00\ncommand: 9E 61 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 29 A1 00 00\ncommand: 9F 60 00 00\n#\n# Model: Bose CINEMATE_1_SR\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 4C B3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 03 FC 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 02 FD 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 01 FE 00 00\n#\n# Model: Bose Solo_5\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 5A A5 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 59 A6 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 56 A9 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 1A E5 00 00\n#\n# Model: Bose Solo_Soundbar\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 01 FD 00 00\n#\n# Model: Bose Soundbar_300\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: BA 4B 00 00\ncommand: 4C B3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: BA 4B 00 00\ncommand: 03 FC 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: BA 4B 00 00\ncommand: 02 FD 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: BA 4B 00 00\ncommand: 01 FE 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: BA 4B 00 00\ncommand: 57 A8 00 00\n#\n# Model: Amz snd_bar\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 35 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 35 00 00 00\ncommand: 45 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 35 00 00 00\ncommand: 1B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 35 00 00 00\ncommand: 51 00 00 00\n#\n# Model: Soundblasterx\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 08 F7 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 01 FE 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 07 F8 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 06 F9 00 00\n#\n# Model: Canton Smart10\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 1E E1 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 1D E2 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 18 E7 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 0A F5 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 0D F2 00 00\n#\n# Model: YARRA 3DX\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 0A 1D 00 00\ncommand: 01 FE 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 0A 1D 00 00\ncommand: 03 FC 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 0A 1D 00 00\ncommand: 08 F7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 0A 1D 00 00\ncommand: 0A F5 00 00\n#\n# Model: Craig CHT912\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 0C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 05 00 00 00\n#\n# Model: Craig CHT921\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 03 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1E 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1F 00 00 00\n#\n# Model: Craig CHT939\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\n#\n# Model: Soundbar creative_stage\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 06 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 07 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 1E 00 00 00\n#\n# Model: Denon RC-1230\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 52 54 32 00\ncommand: 83 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Kaseikyo\naddress: 52 54 32 00\ncommand: 86 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: 52 54 32 00\ncommand: 84 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: 52 54 32 00\ncommand: 85 00 00 00\n#\n# Model: Hisense HS215\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 20 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 34 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 34 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 21 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 22 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 23 00 00 00\n#\n# Model: Hitachi HSB40B16\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 5B 00 00 00\ncommand: 80 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 5B 00 00 00\ncommand: 88 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 5B 00 00 00\ncommand: 82 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 5B 00 00 00\ncommand: 83 00 00 00\n#\n# Model: iLive ITP280B_Remote\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1E 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 42 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 43 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 41 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 45 00 00 00\n#\n# Model: iLive Soundbar\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 14 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 10 00 00 00\n#\n# Model: JBL 5_1_Soundbar\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 84 74 00 00\ncommand: FF 00 00 00\n#\n# Model: JBL CINEMA_SB120\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 53 AC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: AD 52 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 26 D9 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 28 D7 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 27 D8 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 25 DA 00 00\n#\n# Model: JVC THBY370A\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 00 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 08 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 01 00 00 00\n#\n# Model: Klipsch Soundbar\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 03 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 06 00 00 00\n#\n# Model: Larksound L200\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 19 E6 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 16 E9 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 09 F6 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 5E A1 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 0C F3 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 18 E7 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 30 0F 00 00\ncommand: 45 BA 00 00\n#\n# Model: LG SJ4\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 1E 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 17 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 16 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 1F 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 4F 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 06 00 00 00\n#\n# Model: LG AKB74815321\n#\nname: Next\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 07 00 00 00\n#\n# Model: LG NB5541\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 4F 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Samsung32\naddress: 2C 00 00 00\ncommand: 05 00 00 00\n#\n# Model: Majority K2_SoundBar\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 41 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 42 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 43 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 1A 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 4F 00 00 00\n#\n# Model: Onn sound_bar_and_subwoofer\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 16 E9 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 0F F0 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 15 EA 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 22 00 00\ncommand: 0C F3 00 00\n#\n# Model: Panasonic SCH-TB8\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: A0 02 20 00\ncommand: D0 03 00 00\n#\n# Model: Pheanoo P27\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 B6 00 00\ncommand: 4D B2 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 B6 00 00\ncommand: 43 BC 00 00\n#\n# Model: Philips HTL2161B_Soundbar\n#\nname: Power\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 0C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 11 00 00 00\n#\n# Model: Philips HTL2163_Soundbar\n#\nname: Mute\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 0D 00 00 00\n#\n# Model: Philips TAB5105_79\n#\nname: Power\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: C7 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 20 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 21 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: RC6\naddress: 10 00 00 00\ncommand: 2C 00 00 00\n#\n# Model: Polk RE9114_1\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 00 FF 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 20 DF 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 26 D9 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 21 DE 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 24 DB 00 00\n#\n# Model: POLK RE9641_1\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: C8 91 00 00\ncommand: 1F E0 00 00\n#\n# Model: Promethean ActivSoundBar_ASB-40\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 05 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 08 00 00 00\n#\n# Model: Quantis LSW-1\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 12 ED 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 1D E2 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 1A E5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 03 FC 00 00\n#\n# Model: RCA RTS7110B2\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 5C 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 45 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 45 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 4E 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 55 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 8A 00 00 00\ncommand: 59 00 00 00\n#\n# Model: Sennheiser Ambeo_Soundbar\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 0E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 13 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Sonos ARC_Beam_Playbar_Playbase\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 80 D9 00 00\ncommand: 8A 75 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 80 D9 00 00\ncommand: 88 77 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 80 D9 00 00\ncommand: 8C 73 00 00\n#\n# Model: Sony Old_XBR\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 14 00 00 00\n#\n# Model: Sony Soundbar_HT-XT3\n#\nname: Play\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 32 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 39 00 00 00\n#\n# Model: TaoTronics TT-SK023_Sound_Bar\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 0D 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1E 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 0A 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1F 00 00 00\n#\n# Model: Taotronics TT-SK026\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: D9 14 00 00\ncommand: 6D 92 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: D9 14 00 00\ncommand: 6E 91 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: D9 14 00 00\ncommand: 4F B0 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: D9 14 00 00\ncommand: 50 AF 00 00\n#\n# Model: TCL TS5010\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 2A D5 00 00\n#\n# Model: Teufel Cinebase_Soundbar\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: EF 01 00 00\ncommand: 25 DA 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: EF 01 00 00\ncommand: 14 EB 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: EF 01 00 00\ncommand: 13 EC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: EF 01 00 00\ncommand: 28 D7 00 00\n#\n# Model: Toshiba SBX4250\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: 12 ED 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: 1F E0 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: 1F E0 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: BD 42 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: 60 9F 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 45 BD 00 00\ncommand: 61 9E 00 00\n#\n# Model: Vizio SB3651_E6\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 40 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 41 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 45 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 48 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8B 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8A 00 00 00\n#\n# Model: Vizio V51-H6\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8E 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8E 00 00 00\n#\n# Model: Binnifa Live1T\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 50 00 00 00\n#\n# Model: Yamaha BAR400\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: CC 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 1E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 1F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 9C 00 00 00\n#\n# Model: Audioengine a5\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 FD 00 00\ncommand: 03 FC 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 FD 00 00\ncommand: 09 F6 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 FD 00 00\ncommand: 07 F8 00 00\n#\n# Model: Biseoamz DY29S\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1C 00 00 00\n#\n# Model: Bumpboxx\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 46 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 45 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 09 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 19 00 00 00\n#\n# Model: Como audio\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 77 00 00 00\ncommand: F1 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 77 00 00 00\ncommand: F3 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 77 00 00 00\ncommand: F7 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 77 00 00 00\ncommand: FB 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 77 00 00 00\ncommand: FC 00 00 00\n#\n# Model: Craig CHT729\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 16 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 47 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\n# Model: Craig CHT904\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 0E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 1A 00 00 00\n#\n# Model: Creative z5400\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 1A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 0E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 16 00 00 00\n#\n# Model: DollarTec BT5AmpBoard\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 43 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 40 00 00 00\n#\n# Model: EASTERN DA_9000\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 1D 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 18 00 00 00\n#\n# Model: Edifier AirPulse_A80\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 00 FF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 19 E6 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 1C E3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 18 E7 00 00\n#\n# Model: Edifier R1280DB\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 06 F9 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 08 F7 00 00\n#\n# Model: Edifier R1700BT\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 2B D4 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 3C C3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 1A E5 00 00\n#\n# Model: Edifier R1700BTs\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 44 BB 00 00\n#\n# Model: Edifier R2800\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 09 F6 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 02 FD 00 00\n#\n# Model: Edifier RC80B\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 10 E7 00 00\ncommand: 5D A2 00 00\n#\n# Model: Edifier S360DB\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 0F F0 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 03 FC 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 78 0E 00 00\ncommand: 40 BF 00 00\n#\n# Model: Fluance AI40\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 3F 5C 00 00\ncommand: 18 E7 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 3F 5C 00 00\ncommand: 55 AA 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 3F 5C 00 00\ncommand: 59 A6 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 3F 5C 00 00\ncommand: 15 EA 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 3F 5C 00 00\ncommand: 16 E9 00 00\n#\n# Model: Dream Aurora_AC6923\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 57 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 03 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 10 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 13 00 00 00\n#\n# Model: Quacker LED_Speaker\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 00 EF 00 00\ncommand: 0D F2 00 00\n#\n# Model: Geneva Sound_System_Model_L\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 06 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0C 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0B 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0D 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\n# Model: IBIZA PORT_15_UHF\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 48 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1D 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\n# Model: JBL Cinema_CB150\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 1B E4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 2A D5 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 14 EB 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 86 FF 00 00\ncommand: 13 EC 00 00\n#\n# Model: JBL LSR4326P\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 14 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 08 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 00 00 00 00\n#\n# Model: JBL On_Stage_IIIP_Speaker\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 40 AF 00 00\ncommand: 19 E6 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 40 AF 00 00\ncommand: 12 ED 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 40 AF 00 00\ncommand: 05 FA 00 00\n#\n# Model: JVC D62BM\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 02 BD 00 00\ncommand: 0D F2 00 00\n#\n# Model: KEF LSX_Speakers\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 02 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 04 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 4B 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 4A 00 00 00\n#\n# Model: Klipsch fives\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1A 00 00 00\n#\n# Model: Klipsch r15pm\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 0A 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 07 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 02 00 00 00\n#\n# Model: Logi WD216XM\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 02 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 08 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 01 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 05 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 04 00 00 00\n#\n# Model: Z906\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 02 A0 00 00\ncommand: AA 55 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 02 A0 00 00\ncommand: 6A 95 00 00\n#\n# Model: Microlab RC071\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 02 00 00 00\n#\n# Model: Microlab SOLO11\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 5A 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 5B 00 00 00\n#\n# Model: Naim Muso\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 11 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: RC5\naddress: 15 00 00 00\ncommand: 20 00 00 00\n#\n# Model: Sangean RC30\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC42\naddress: 01 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Sony Amp_STR-DE875\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 2E 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 2F 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 0C 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Sony SRS-GU10iP\n#\nname: Power\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 15 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 14 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 28 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 13 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 29 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: SIRC15\naddress: 99 00 00 00\ncommand: 2A 00 00 00\n#\n# Model: Steljes Desktop_Speakers\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 85 23 00 00\ncommand: 99 66 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 85 23 00 00\ncommand: 57 A8 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 85 23 00 00\ncommand: 47 B8 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 85 23 00 00\ncommand: 97 68 00 00\n#\n# Model: Teufel 3SIXTY\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: FD 00 00 00\ncommand: E2 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: FD 00 00 00\ncommand: E1 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: FD 00 00 00\ncommand: E7 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: FD 00 00 00\ncommand: B9 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: FD 00 00 00\ncommand: EA 00 00 00\n#\n# Model: Teufel CEM500RC\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 13 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 17 00 00 00\n#\n# Model: Toshiba RM-V329\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 12 ED 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 11 EE 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 10 EF 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 13 EC 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 03 FC 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 07 F8 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 2D D3 00 00\ncommand: 06 F9 00 00\n#\n# Model: Technics EUR646496\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: D0 03 00 00\n#\nname: Play\ntype: parsed\nprotocol: Kaseikyo\naddress: 90 02 20 00\ncommand: A0 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: Kaseikyo\naddress: AA 02 20 01\ncommand: A0 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: Kaseikyo\naddress: AA 02 20 00\ncommand: 00 00 00 00\n#\n# Model: terratec m3po\n#\nname: Mute\ntype: parsed\nprotocol: RC5X\naddress: 0A 00 00 00\ncommand: 2F 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: RC5X\naddress: 0A 00 00 00\ncommand: 31 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5X\naddress: 0A 00 00 00\ncommand: 0F 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5X\naddress: 0A 00 00 00\ncommand: 2C 00 00 00\n#\n# Model: X4 TECH_TU-1200\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 04 00 00 00\n#\n# Model: YAMAHA AX-380\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 7A 00 00 00\ncommand: 1F 00 00 00\n#\n# Model: Yamaha RAS5\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 7E 81 00 00\ncommand: 2A D4 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 7A 85 00 00\ncommand: 1A E4 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 7A 85 00 00\ncommand: 1B E5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 7A 85 00 00\ncommand: 1C E2 00 00\n#\n# Model: Yamaha RAV203_V473170_US\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 0D 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: RC5\naddress: 05 00 00 00\ncommand: 29 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 7A 00 00 00\ncommand: 59 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: RC5\naddress: 05 00 00 00\ncommand: 35 00 00 00\n#\n# Model: Yamaha WS19340\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 0F 00 00 00\n#\nname: Play\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 02 00 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 04 00 00 00\n#\nname: Next\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 03 00 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NEC\naddress: 78 00 00 00\ncommand: 01 00 00 00\n#\n# Model: Yamaha ZP45780\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 7F 01 00 00\ncommand: 6A 95 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 7F 01 00 00\ncommand: 6B 94 00 00\n#\nname: Play\ntype: parsed\nprotocol: NECext\naddress: 7F 01 00 00\ncommand: 68 97 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 7F 01 00 00\ncommand: 67 98 00 00\n#\nname: Pause\ntype: parsed\nprotocol: NECext\naddress: 7F 01 00 00\ncommand: 69 96 00 00\n#\n# Model : NAD DR2 remote for NAD D7050 and D3020\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 25 DA 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 88 77 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 8C 73 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 94 6B 00 00\n#\nname: Next\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 1A E5 00 00\n#\nname: Prev\ntype: parsed\nprotocol: NECext\naddress: 87 7C 00 00\ncommand: 1D E2 00 00\n#\n# Model: JVC RM-SRVNB1A for JVC RV-NB1 boombox (2004)\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.333333\ndata: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.333333\ndata: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.333333\ndata: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 33664\n#\nname: Next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.333333\ndata: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664\n#\nname: Prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.333333\ndata: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664\n#\n"
  },
  {
    "path": "applications/main/infrared/resources/infrared/assets/projector.ir",
    "content": "Filetype: IR library file\nVersion: 1\n#\n# Model: Smart\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8A 00 00 00\n#\n# Model: Epson\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: 90 6F 00 00\n#\n# Model: Epson\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 81 03 00 00\ncommand: F0 0F 00 00\n#\n# Model: Hitatchi\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 45 00 00\ncommand: 17 E8 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 48 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 49 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 14 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 40 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 48 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 44 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 83 7C 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 82 7D 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 08 13 00 00\ncommand: 87 78 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 87 4E 00 00\ncommand: 29 D6 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 87 4E 00 00\ncommand: 08 F7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 87 4E 00 00\ncommand: 04 FB 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: 93 6C 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 02 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 1D 00 00 00\n#\n# ON\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616\n# AV-Mute\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 4C 00 00 00\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633\n# ON\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410\n# ON\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 529 7218 126 6585 219 703 180 5362 427 18618 177\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620\n# ON\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 4F B0 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 19 00 00\ncommand: 10 EF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 80 19 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 80 19 00 00\ncommand: 46 B9 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 51 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 0A F5 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 4E B1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 0E F1 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 0D F2 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 4F B0 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 14 EB 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 08 16 00 00\ncommand: 87 78 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 08 16 00 00\ncommand: C8 37 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 01 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 28 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 29 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 84 F4 00 00\ncommand: 0B F4 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 33 00 00 00\ncommand: 00 FF 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 33 00 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 33 00 00 00\ncommand: 1D E2 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 33 00 00 00\ncommand: 0B F4 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: 99 66 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: 98 67 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4F B0 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4B B4 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 02 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 2E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 52 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 41 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 51 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 56 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 5A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: SIRC15\naddress: 54 00 00 00\ncommand: 15 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 82 7D 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 83 7C 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 14 EB 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 31 00 00 00\ncommand: 91 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 31 00 00 00\ncommand: 90 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 31 00 00 00\ncommand: D0 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 31 00 00 00\ncommand: 89 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 86 00 00 00\ncommand: 30 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 86 00 00 00\ncommand: 31 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 86 00 00 00\ncommand: 32 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 30 00 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 4E 00 00\ncommand: 0D 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9031 4479 596 560 572 558 564 566 566 1693 571 1688 566 563 569 561 571 559 563 1696 568 561 571 559 563 1697 567 562 570 1689 565 1694 570 1688 566 1693 571 1661 593 1693 571 558 564 566 566 564 568 1691 563 541 591 564 568 562 570 560 562 1697 567 1692 562 1696 568 562 570 1689 565 564 568 561 571 559 563 567 565 564 568 562 570 560 562 567 565 1694 570 1689 565 1694 570 1688 566 1693 571 1688 566 1693 571 1662 592 13987 9031 2220 590 96231 9034 2217 593 96234 9030 2222 588 96247 9037 2215 595\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 14 00 00 00\n# OFF\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 4E B1 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 03 00 00 00\ncommand: 1D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 03 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 03 00 00 00\ncommand: 15 00 00 00\n# OFF\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: E6 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 07 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0F 00 00 00\n# OFF\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410\n# OFF\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616\n# OFF\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 18 E9 00 00\ncommand: 49 B6 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 14 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 48 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 40 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 18 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: B8 57 00 00\ncommand: 0C F3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: B8 57 00 00\ncommand: 0D F2 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: B8 57 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: B8 57 00 00\ncommand: 1F E0 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 81 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 8F 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 8C 00 00 00\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: A8 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 88 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 9C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 8C 00 00 00\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 87 45 00 00\ncommand: 50 AF 00 00\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: FF FF 00 00\ncommand: E8 17 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: FF FF 00 00\ncommand: BD 42 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: FF FF 00 00\ncommand: F2 0D 00 00\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 05 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 70 01 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: 41 54 32 00\ncommand: 71 01 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 31 00 00 00\ncommand: 81 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 F4 00 00\ncommand: 17 E8 00 00\n#\n# Model: ViewSonic X1_Projector\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.33\ndata: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556\n# PLUS U5/V3-200R Standby\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9033 4255 562 543 564 1701 562 1674 563 567 565 541 564 567 565 567 537 1700 564 1700 537 568 564 1699 564 541 565 1699 565 540 565 567 565 570 535 567 565 567 565 1673 564 567 565 1673 564 567 565 540 618 514 565 1700 563 1674 564 567 564 1674 563 569 563 1672 565 1700 589 1648 564\n# PLUS U5/V3-200R Mute\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9034 4255 564 567 564 1671 566 1699 564 540 565 567 565 540 564 566 566 1699 563 1672 567 565 566 1673 565 567 565 1672 565 566 566 539 565 567 565 1698 539 566 566 1698 564 541 565 565 567 1672 565 566 566 1672 565 567 565 1699 538 566 566 1698 564 1673 566 566 565 1672 566 566 566\n# PLUS U5/V3-200R 1/Volume up\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9035 4254 565 566 565 1672 566 1699 564 539 567 566 566 540 564 567 565 1698 539 1698 566 566 564 1673 565 566 565 1672 565 566 566 539 566 566 566 567 563 541 565 566 566 538 566 566 566 1671 566 566 566 1697 565 1675 563 1699 563 1674 565 1699 538 1700 564 565 565 1674 564 567 565\n# PLUS U5/V3-200R 3/Volume down\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9010 4253 564 566 566 1671 566 1699 565 565 539 568 564 566 565 539 566 1699 565 1672 565 566 566 1672 564 567 565 1672 565 567 565 567 564 541 564 1698 566 539 565 567 565 567 562 542 565 1699 564 539 567 1699 565 540 564 1698 566 1672 565 1698 566 1672 565 567 565 1671 565 566 566\n#\n# Model: Apeman LC650_\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 01 FE 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 6A 95 00 00\n#\n# Model: BenQ MH856UST\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 30 00 00\ncommand: 97 68 00 00\n#\n# Model: BenQ TRY01\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 04 B1 00 00\ncommand: 58 A7 00 00\n#\n# Model: Generic Universal_Remote\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 48 50 00 00\ncommand: 02 FD 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 48 50 00 00\ncommand: 27 D8 00 00\n#\n# Model: Coolux X3S\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 00 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 1A 00 00 00\n#\n# Model: Dell projector\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 4F 50 00 00\ncommand: 02 FD 00 00\n#\n# Model: Dell tsfm_ir01\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 4F 50 00 00\ncommand: 0F F0 00 00\n#\n# Model: Epson 4650\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2288 611 571 587 1153 587 572 587 572 1167 572 1168 571 587 573 587 572 587 572 589 570 587 572 587 572 587 572 588 571 77346 2287 611 571 588 1152 588 571 588 571 1168 571 1168 571 588 571 588 571 588 571 589 570 587 572 588 572 587 572 587 572\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2288 610 572 588 1151 587 573 588 571 1169 570 586 573 587 572 587 1152 587 573 586 1153 588 572 587 573 586 573 587 572 76771 2289 610 572 588 1151 587 573 587 572 1166 573 587 572 587 572 587 1152 588 571 588 1151 587 573 587 572 587 573 588 571\n#\n# Model: Epson EHTW5650\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: AD 52 00 00\n#\n# Model: Epson EMP822H\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: B1 4E 00 00\n#\n# Model: Epson projector_Power_Only\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500\n#\n# Model: Gateway 210_projextor\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 30 00 00 00\ncommand: 0B 00 00 00\n#\n# Model: Groview\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 6B 00 00\ncommand: 0A F5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 86 6B 00 00\ncommand: 4A B5 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 86 6B 00 00\ncommand: 0E F1 00 00\n#\n# Model: Infocus Navigator_3\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 4E 00 00\ncommand: 17 E8 00 00\n#\n# Model: JVC LX-UH1B\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 6A 00 00\ncommand: 40 BF 00 00\n#\n# Model: LG PH300-NA\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 04 0F 00 00\ncommand: AD 52 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 04 0F 00 00\ncommand: 02 FD 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 04 0F 00 00\ncommand: 03 FC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 04 0F 00 00\ncommand: 09 F6 00 00\n#\n# Model: Maxell MC-EU5001\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 87 45 00 00\ncommand: 52 AD 00 00\n#\n# Model: NexiGo-PJ20\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 03 00 00 00\ncommand: 02 00 00 00\n#\n# Model: Optoma projector\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 4F 50 00 00\ncommand: 07 F8 00 00\n#\n# Model: Optoma Remote_HOF04K276D6\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 4F 50 00 00\ncommand: 0A F5 00 00\n#\n# Model: Optoma UHZ45\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 03 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 32 00 00 00\ncommand: 0C 00 00 00\n#\n# Model: Philips PicoPix_Max_PPX620_Projector\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 12 00 00 00\n#\n# Model: PVO YG300Pro\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 40 00 00 00\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9107 4376 681 1573 681 472 655 472 654 474 652 475 652 476 651 476 652 476 651 476 651 1604 651 1604 651 1604 651 1603 652 1604 651 1604 651 1604 651 1604 650 476 651 477 650 477 650 476 651 477 651 1604 650 476 651 477 650 1604 651 1604 650 1604 651 1604 650 1604 651 477 650 1604 650 39498 9079 2178 651\n#\n# Model: RIF6-cube-projector-raw\n#\nname: Power\ntype: raw\nfrequency: 36045\nduty_cycle: 0.330000\ndata: 9024 4506 570 582 542 582 518 606 518 606 518 606 518 606 518 606 518 610 518 1698 548 1698 546 1700 546 1700 546 1698 546 1700 546 1696 548 1702 570 1674 546 610 516 1698 546 606 542 582 542 582 544 1674 546 610 520 606 542 1674 570 582 542 1676 546 1698 570 1674 572 582 520 1698 546\n#\nname: Mute\ntype: raw\nfrequency: 36045\nduty_cycle: 0.330000\ndata: 9044 4484 572 580 544 580 544 580 544 580 542 582 544 580 520 604 542 586 544 1674 570 1674 570 1676 570 1674 572 1672 570 1674 546 1700 568 1678 570 582 542 1672 572 580 542 580 542 1674 566 582 542 1672 570 584 538 1674 564 580 534 1674 556 1674 556 580 530 1672 558 582 524 1676 552\n#\n# Model: Samsung Freestyle_Gen2\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 02 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: D1 00 00 00\n#\n# Model: Samsung VG-TM2360E\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1221 1171 433 566 433 881 433 2381 433 1486 434 565 434 1486 433 1776 433 2380 433 565 434 2381 433 1170 434 87358 1220 1171 433 566 433 883 431 2381 433 1486 433 567 432 1487 432 1775 434 2381 432 566 433 2380 434 1171 433 86252 1221 1172 432 565 434 880 435 2381 433 1486 433 565 434 1487 432 1776 433 2380 434 566 433 2379 434 1170 434\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1221 1173 431 566 433 882 432 2382 432 1487 432 566 433 565 434 566 433 2671 432 2670 433 2381 433 566 433 87411 324 937 325 358 325 647 326\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1221 1172 432 566 433 882 433 2381 432 1486 434 566 433 565 434 882 433 1486 434 2669 434 2065 433 566 433 87779 324 936 326 358 325 647 326\n#\n# Model: Sharp RRMCGA664WJSA_Notevision XR-32S-L\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 293 1801 296 753 295 1801 296 1801 296 752 296 754 294 1801 296 1800 297 752 296 1802 295 752 296 1801 296 753 295 1800 297 752 296 42709 296 1800 297 753 295 1800 297 1800 297 753 295 1802 295 753 295 753 295 1801 296 753 295 1801 296 754 294 1802 295 753 295 1801 296 42694 295 1800 297 752 296 1803 294 1803 294 753 295 753 295 1801 296 1802 295 752 296 1802 295 752 296 1801 296 753 295 1802 295 753 295 42709 295 1802 295 753 295 1803 294 1801 296 753 295 1802 295 752 296 752 296 1801 296 752 296 1803 294 754 294 1803 294 754 294 1804 293 42694 294 1802 294 755 293 1803 294 1804 268 779 269 779 269 1828 269 1828 269 780 268 1829 268 778 270 1829 323 725 268 1829 268 781 324\n#\n# Model: SMART Projectors\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 8B CA 00 00\ncommand: 12 ED 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 8B CA 00 00\ncommand: 11 EE 00 00\n#\n# Model: Sony RM_PJ27\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC15\naddress: 54 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC15\naddress: 54 00 00 00\ncommand: 13 00 00 00\n#\n# Model: TopVision\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 11 00 00 00\n"
  },
  {
    "path": "applications/main/infrared/resources/infrared/assets/tv.ir",
    "content": "Filetype: IR library file\nVersion: 1\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 15 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 15 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 05 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 00 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 01 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 02 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 03 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 0B 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4B 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4F 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 09 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 05 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 14 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 15 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 12 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 0E 00 00 00\ncommand: 13 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 02 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: E6 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 07 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0B 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 12 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 10 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 17 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 31 49 00 00\ncommand: 63 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: AA 00 00 00\ncommand: 1C 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 1C 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 53 00 00 00\ncommand: 17 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 18 18 00 00\ncommand: C0 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 10 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: AA 00 00 00\ncommand: C5 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 18 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 71 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 6F 00 00\ncommand: 0A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 48 00 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 7B 00 00\ncommand: 13 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 7E 00 00\ncommand: 18 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 75 00 00\ncommand: 0A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 57 00 00\ncommand: 0A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 0B 00 00 00\ncommand: 0A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: AA 00 00 00\ncommand: 1B 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 85 46 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 05 00 00 00\ncommand: 02 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 08 00 00 00\ncommand: 0F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 01 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 634 2571 505 519 479 519 479 518 480 518 480 518 480 518 480 517 481 547 481 517 481 20040 590 2555 501 1007 999 997 510 548 480 486 512 486 512 486 542 485 513 516 482 116758 593 2552 504 1004 992 1004 514 514 514 483 515 513 485 483 545 482 516 482 516\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 525 1955 449 1999 476 4545 446 4544 478 2032 443 2006 469 2011 444 4577 445 4545 447 4574 448 2002 473 4547 444 34913 447 2032 443 2007 478 4542 449 4541 471 2039 446 2004 471 2008 447 4574 448 4543 448 4572 450 2030 445 4545 446\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: D0 03 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 389 1737 280 796 253 744 295 754 274 775 274 776 273 1827 271 1828 270 805 254 1820 278 796 253 797 252 745 294 1806 302 773 245 48942 305 1821 277 798 251 746 303 747 271 778 271 1829 279 796 253 796 253 1821 277 798 251 1823 275 1824 274 1825 273 802 247 1827 271 42824 381 1745 272 804 245 752 297 753 275 773 276 774 275 1825 273 1826 272 803 246 1828 270 805 254 795 244 753 296 1804 294 781 247 48939 379 1746 271 804 245 779 270 753 275 774 275 1825 273 802 247 802 247 1827 271 804 245 1829 279 1820 278 1821 277 798 251 1823 275\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 562 1721 561 594 557 597 564 617 513 615 536 618 543 1715 566 1716 566 1692 559 594 567 588 563 618 543 611 540 615 536 618 543 1715 556 623 538 617 534 621 530 624 516 638 513 642 509 1722 560 620 541 1717 565 1692 559 1724 568 1715 556 1701 560 1723 559\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8436 4189 538 1563 566 1559 539 510 559 543 516 507 542 560 509 540 509 567 512 1586 512 1562 567 1559 539 536 533 1566 542 507 562 513 536 540 509 22102 647 1478 559 1568 540 508 541 535 534 515 534 568 511 538 511 539 540 1585 513 1560 559 1567 541 534 535 1564 534 515 534 568 511 538 511 22125 644 1482 565 1561 537 511 538 564 515 508 541 535 534 541 508 516 563 1588 510 1563 556 1570 538 510 559 1567 541 534 515 535 534 541 508\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 448 2031 444 2005 480 4540 452 4539 473 2037 438 2011 474 2006 449 4571 451 4539 453 4568 444 2036 449 4541 451 34906 527 1953 451 1998 477 4543 449 4542 480 2030 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 4545 446\n#\nname: Power\ntype: parsed\nprotocol: NEC42\naddress: 7B 00 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8041 3979 513 536 482 515 513 1559 509 515 514 1560 508 515 514 509 509 514 484 4000 533 1566 512 537 481 1566 512 537 481 1566 512 537 481 516 513 537 481 24150 8044 3977 505 518 510 539 479 1567 511 512 506 1568 510 539 479 543 485 538 480 3977 536 1564 514 534 484 1563 515 508 510 1563 515 508 510 540 489 534 484\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 383 2027 295 2112 271 2138 266 890 271 887 294 926 235 2114 300 887 274 915 236 2145 269 887 274 884 297 923 238 920 241 917 264 895 266 26573 384 2026 296 2111 273 2136 268 889 272 886 295 924 237 2113 301 886 275 914 237 2144 270 886 275 914 267 921 240 919 242 916 265 924 237\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 177 8474 175 5510 174 8476 173 8477 177 8504 171 5515 175 8476 178 8472 177 8473 176 5541 174 8476 173 45583 171 8481 178 5507 177 8473 176 8474 175 8506 173 5512 172 8478 176 8475 174 8476 178 5538 177 8474 175\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8044 3976 506 517 511 1563 505 517 511 538 480 517 511 538 460 563 455 568 460 3993 530 545 483 1564 514 1559 509 1564 514 509 509 540 488 535 483 513 505 24150 8043 3978 514 509 509 1564 514 509 509 540 478 519 509 540 458 565 464 559 459 3994 529 546 482 1565 513 1560 508 1565 513 510 508 541 487 536 482 541 477\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 558 2942 450 10021 482 2989 484 10015 447 3024 449 10021 472 3100 485 2986 477 2994 500 2999 475 2996 477 2994 479 2992 482 3018 476 2995 479 6464 484 36270 477 3023 450 10020 473 2999 485 10014 448 3022 452 10019 474 3098 478 2994 480 2991 503 2996 477 2994 480 2992 482 2990 484 3015 479 2992 481 6462 485\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 587 2407 476 1062 965 547 482 547 451 577 452 546 452 22108 590 2404 479 1060 967 1028 510 519 509 548 480 487 511 120791 645 2411 472 1066 961 1065 483 515 514 514 504 493 515\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 172 7439 171 7441 169 7443 177 7434 176 7462 178 4887 176 4916 177 4914 169 7469 171 4920 174 4918 175 55174 176 7436 174 7437 173 7439 171 7440 175 7463 172 4894 174 4917 171 4921 172 7465 175 4916 178 4914 169\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 589 2556 500 524 474 554 454 544 454 543 455 543 455 543 455 543 445 583 446 552 446 20046 584 2561 505 1033 485 514 963 518 480 1032 516 512 476 522 506 491 507 522 476 116758 586 2560 506 1033 484 513 964 548 450 1031 507 522 476 521 507 490 508 521 477\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 586 2407 476 1063 964 578 450 548 450 547 481 517 481 577 451 546 452 546 472 556 452 545 453 575 453 514 484 544 484 543 455 14954 510 2483 481 1058 480 548 959 552 446 1067 481 516 512 546 482 515 992 1003 535 493 515 543 486 513 475 522 506 552 446 111671 589 2405 478 1061 477 551 967 514 484 1059 479 549 479 548 480 517 990 1036 512 516 482 546 483 515 503 525 483 544 454\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8444 4180 537 1564 565 1560 538 1561 557 1568 540 1559 539 536 533 542 517 559 510 1563 535 1564 565 1561 537 512 567 1558 540 535 534 1566 542 1557 562 23122 564 1562 557 1569 539 1560 538 1587 542 1558 540 534 535 515 534 541 538 1587 511 1563 566 1560 538 536 533 1567 541 534 515 1585 533 1566 542 23166 561 1565 564 1561 537 1563 535 1590 539 1561 537 538 541 534 515 535 534 1564 534 1566 563 1563 535 540 539 1560 538 511 538 1588 541 1559 539\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 527 1923 481 1998 477 4543 449 4542 470 2040 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 2034 441 34899 524 1956 448 2001 474 4546 446 4545 477 2033 442 2007 478 2002 443 4578 444 4546 445 4575 447 2003 472 2037 448\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 533 1356 437 3474 427 3483 429 3455 436 1454 430 1459 405 28168 510 1379 434 3477 434 3476 425 3459 432 1457 427 1462 402\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 179 7433 177 4915 178 7434 175 7436 174 7464 176 7435 175 4916 177 4915 173 4918 170 4922 171 4920 173 55174 175 7437 173 4919 174 7437 173 7439 171 7467 173 7438 172 4920 173 4919 174 4917 176 4915 178 4914 169\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 169 6731 176 6748 169 6730 177 6748 169 6755 172 4427 177 4447 178 6721 175 6749 178 4446 168 4456 169 54704 176 6723 174 6750 177 6723 173 6750 177 6747 170 4429 175 4449 176 6723 174 6751 176 4448 177 4447 178\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3506 3494 876 830 840 2576 847 2568 844 862 819 2570 842 864 816 863 818 2570 842 836 844 2572 840 866 815 865 815 2573 839 867 813 866 814 2573 850 857 813 2575 847 2568 844 834 847 2569 843 835 845 2571 872 2571 842 32654 3512 3488 872 834 847 2570 842 2573 839 867 814 2574 849 858 822 857 813 2575 848 859 821 2566 846 832 848 860 821 2567 845 833 848 860 820 2568 844 834 847 2569 843 2572 840 838 843 2574 849 829 841 2575 868 2575 837\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 560 2939 453 10018 475 2996 477 10022 450 3021 452 10018 475 3097 478 6464 483 6460 477 6466 502 6469 479 2993 480 2990 484 36274 564 2936 456 10015 478 2993 481 10020 534 2936 456 10014 479 3093 482 6461 476 6466 482 6461 476 6495 473 2999 485 2986 477\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 10726 41047 10727\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1617 4604 1559 1537 1560 1537 1560 4661 1533 33422 1613 4607 1566 1530 1556 1540 1536 4685 1539\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 4972 177 4910 173 4944 170 6988 174 6984 177 6951 175 6983 174 14269 177 4969 175 4912 176 4941 178 6979 172 6986 175 6953 178 6980 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 7067 176 10544 1055 719 1053 739 1054 738 953 822 1052 2566 169 3435 171 3431 175 3446 170 3432 174 3446 170 3432 174 3428 178 3442 174 3429 177 3425 171 39320 2323 4900 178 10543 1056 719 1053 739 1054 738 953 821 1053 2566 169 3435 171 3431 175 3445 171 3432 174 3446 170 3432 174 3428 178 3442 174 3428 178 3425 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3506 3492 868 839 841 2575 848 858 822 2566 846 832 848 859 821 858 812 2576 847 860 820 2567 845 833 847 860 821 2568 844 862 818 2570 842 864 817 2571 841 2574 849 2567 845 861 820 2568 845 834 847 2570 873 2570 842 34395 3503 3496 874 833 847 2568 845 834 847 2570 842 864 816 835 846 862 819 2569 843 835 846 2571 841 865 816 864 816 2571 841 837 844 2573 850 857 813 2575 848 2567 845 2570 842 864 816 2572 840 838 842 2574 869 2574 838\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 8479 170 5516 178 8472 177 8474 175 8506 173 5513 171 8479 175 8476 178 8472 177 5540 175 8475 174 45584 177 8473 176 5509 175 8476 173 8477 176 8504 170 5516 178 8472 177 8474 175 8476 173 5543 172 8479 170\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 4969 170 6958 173 4944 175 6983 173 6956 174 6984 177 6980 171 16308 180 4966 173 6955 176 4941 172 6985 176 6982 169 6960 176 6982 174\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 585 2409 474 550 478 550 448 1033 994 1063 485 512 506 79916 585 2410 473 551 477 550 448 1034 993 1033 515 543 475\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1192 1012 6649 26844 1192 1013 6648\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3134 6105 6263 82963 3134 6105 6263\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 588 1511 567 1533 565 589 531 597 513 589 541 587 533 1565 533 569 541 1533 565 562 568 1531 537 1537 561 593 537 591 539 1507 561 1539 569 22152 586 1513 565 1535 563 590 540 562 538 564 566 588 542 1557 531 597 513 1534 564 590 540 1533 535 1539 559 568 562 592 538 1509 569 1531 567\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 20 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 689 1461 566 1534 564 589 531 597 513 1534 564 564 566 1533 535 620 510 1537 561 592 538 1535 543 1531 567 587 533 595 535 1511 567 1533 565 22161 588 1512 566 1534 564 564 556 598 512 1535 563 565 565 1534 534 594 536 1538 560 593 537 1536 542 1532 566 587 543 585 535 1511 567 1534 564\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 588 2558 498 526 482 515 483 546 452 545 453 545 453 545 453 545 453 544 474 554 444 20047 583 2562 504 519 479 519 479 1003 515 483 1024 548 450 1001 995 1001 506 116771 593 2552 504 520 478 551 447 1004 513 514 993 549 449 1002 994 1002 516\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 587 2559 507 517 481 517 481 547 451 516 482 546 452 546 452 546 452 545 473 525 483 20038 592 2553 503 521 477 552 446 521 477 1004 514 515 992 1034 483 514 484 513 485 116769 593 2552 504 520 478 550 448 520 478 1003 515 514 993 1032 486 513 475 522 476\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4558 4576 558 596 565 97881 4554 4579 565 589 562\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1039 7202 958 4713 981 4713 961 7255 956 4739 955 16077 1038 7204 956 4714 980 4715 959 7256 954 4741 954\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 506 2638 510 516 482 516 482 546 452 546 452 545 453 545 453 545 453 575 443 554 444 20048 592 2554 502 1036 481 517 481 517 511 516 482 516 961 1035 513 515 483 514 484 116757 594 2552 504 1034 484 514 484 514 504 524 484 513 964 1032 506 522 476 492 506\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 7441 169 4924 174 7437 193 7444 171 7441 174 4918 170 7441 174 4917 171 7440 195 7443 172 7439 171 55187 174 7437 173 4919 175 7437 173 7438 172 7466 175 4891 172 7439 171 4921 172 7439 171 7441 174 7464 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 179 4967 172 4915 178 6980 171 4945 169 4948 176 4912 176 4941 178 16307 176 4969 175 4912 176 6982 174 4942 171 4945 169 4919 174 4943 176\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1042 793 898 883 869 858 924 884 878 877 875 879 873 855 928 1717 901 854 1794 878 894 887 875 1716 902 89114 985 798 903 878 874 880 902 852 900 855 897 857 905 876 896 1722 906 849 1789 883 899 855 897 1721 897\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 8480 169 8481 178 8472 177 8474 175 8476 173 5513 176 8474 175 8476 173 8507 178 5509 175 8505 174 45558 175 8476 173 8476 173 8477 172 8478 171 8480 169 5517 177 8473 176 8474 175 8506 174 5512 172 8509 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 628 2577 499 528 480 545 453 544 454 544 454 544 454 544 454 543 455 543 475 553 445 20047 593 2553 503 521 477 551 447 1004 514 515 992 519 479 1003 515 513 485 543 455 116768 585 2561 505 519 479 519 479 1002 516 513 994 548 450 1001 506 522 476 521 477\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 63 00 00\ncommand: 0F 15 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 64 00 00\ncommand: 49 08 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 298 1828 270 804 245 1829 279 1820 278 797 252 771 278 1822 276 1824 274 800 249 1825 273 802 247 776 252 771 278 1822 276 799 250 48931 388 1737 280 796 253 1821 277 1822 276 798 251 1823 275 800 249 774 275 1825 273 776 273 1801 297 1828 270 1829 279 796 253 1821 277 42813 301 1825 273 801 248 1826 272 1827 271 804 245 805 244 1830 278 1821 277 798 251 1823 275 799 250 774 244 779 280 1820 278 796 253 48926 382 1744 354 696 271 1828 270 1829 279 796 253 1821 277 797 252 746 303 1823 275 774 275 1799 299 1826 272 1827 271 804 245 1829 279\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 177 5508 176 5539 176 8475 174 5542 173 8478 171 5545 170 8481 168 8482 177 8473 176 5541 174 8476 173 45573 169 5517 177 5538 177 8473 176 5541 174 8476 173 5544 171 8479 170 8481 178 8472 177 5540 175 8475 174\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 175 8474 175 8475 174 8477 172 8478 171 8480 169 8481 178 5538 177 5510 174 5542 173 5543 172 5544 171 45575 177 8472 177 8474 175 8476 173 8477 172 8478 171 8481 178 5507 177 5539 176 5540 175 5542 173 5543 172\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8050 3971 511 1562 516 1558 510 539 490 1557 511 1563 515 533 485 538 490 533 485 3983 530 1569 509 1564 514 1559 509 1565 513 1560 508 515 513 536 482 541 488 24152 8042 3979 514 1560 508 1565 513 510 508 1565 513 1560 508 515 513 536 482 541 488 3980 533 1567 511 1562 516 1557 511 1563 515 1558 510 539 489 534 484 539 490\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 175 8475 174 5512 177 8473 176 8475 174 8506 179 5508 176 8474 175 8475 174 8477 172 5544 171 8480 169 45587 176 8475 174 5511 173 8477 177 8473 176 8504 170 5516 178 8472 177 8473 176 8475 174 5542 173 8478 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7436 174 7438 172 7439 171 7441 174 7463 172 7440 170 4921 172 4919 174 4917 176 4916 177 4914 174 55176 175 7437 173 7439 191 7446 174 7438 177 7434 176 7435 175 4917 176 4915 179 4914 174 4917 171 4946 178\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7435 175 4917 177 7435 175 7436 189 7449 176 7435 175 7437 173 4918 175 7436 174 4918 175 4916 178 55171 175 7436 174 4918 170 7441 174 7438 177 7460 170 7441 174 7438 177 4914 174 7437 178 4914 169 4922 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8049 3973 509 1564 514 509 509 1564 514 535 483 1564 514 535 483 540 489 535 483 3980 533 1566 512 537 481 1566 512 511 507 1566 512 537 481 542 486 511 507 24149 8045 3976 516 1558 510 512 516 1557 511 512 516 1558 510 512 516 507 511 512 506 3984 539 1560 508 515 513 1560 508 541 488 1560 508 541 488 536 482 514 504\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 366 202 867 527 170 130516 343 227 863 529 168\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 5992 176 1372 176 1320 177 1345 172 1349 179 1370 178 1317 175 4444 176 1346 171 1351 177 1345 172 1349 179 1344 174 5963 175 65574 172 5995 178 1344 174 1348 175 1347 175 1347 170 1378 170 1325 172 4447 178 1344 173 1349 169 1353 175 1347 170 1351 177 5961 177\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 497 1478 488 511 467 1482 494 531 447 1477 499 501 466 533 445 530 468 507 471 529 438 12857 488 1485 491 509 469 1481 495 529 448 1476 490 510 468 532 445 529 469 480 498 502 465\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 982 6313 961 2662 903 2718 929 2719 907 6363 900 2722 904 6365 909 6361 903 6368 906 2742 905 46364 989 6307 906 2716 911 2712 925 2723 903 6367 907 2715 901 6369 905 6365 909 6361 903 2745 902\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 599 257 975 317 177 130649 308 228 974 319 175\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 4969 170 6958 173 6985 177 6981 171 6958 178 6980 177 6981 170 16308 180 4966 173 6955 176 6982 169 6988 174 6956 175 6983 173 6984 172\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 5990 173 1349 174 1348 175 1348 174 4444 176 1346 171 1351 177 4416 178 1344 173 1348 169 1353 175 1347 170 1351 177 9048 177 65573 173 5995 173 1348 175 1348 174 1347 176 4444 171 1351 177 1345 173 4421 173 1348 169 1352 176 1347 170 1351 177 1345 172 9053 177\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 4972 177 4910 173 6985 177 4940 174 6984 178 6951 175 6983 174 14269 177 4968 176 4912 176 6981 175 4941 173 6985 177 6953 178 6979 172\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1318 225 177 130305 1609 231 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 585 2410 473 1065 962 581 447 519 479 580 448 549 449 579 449 518 480 548 480 517 481 517 481 577 451 516 482 576 452 545 453 14955 510 2483 481 1058 480 549 969 543 445 1037 511 547 481 546 482 516 482 545 484 545 483 514 484 544 963 1063 485 543 445 111612 626 2427 557 954 513 512 995 547 451 1031 507 521 508 551 477 520 478 550 478 549 479 488 510 548 969 1057 481 517 481\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 203 272 1215 302 171 130229 1423 275 178\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7436 174 7438 192 7446 174 7437 178 7434 176 7435 175 4917 176 4915 178 4913 175 4917 197 7440 175 55130 286 7377 177 7435 175 7437 173 7438 172 7466 174 7411 178 4913 170 4921 172 4919 174 4918 175 7436 174\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 175 7437 173 4918 170 7441 174 7437 178 7460 170 7441 179 7433 177 4915 178 4913 170 4922 171 4920 173 55124 291 7372 172 4921 172 7439 171 7441 174 7463 172 7440 170 7442 178 4913 170 4922 171 4920 173 4918 175\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 172 8477 172 8478 171 8480 169 5516 179 8502 178 5509 175 8475 174 8476 173 8479 170 5545 170 8480 169 45588 176 8473 176 8474 175 8476 173 5513 177 8504 171 5515 174 8476 178 8472 177 8473 176 5541 174 8476 173\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 177 7436 174 4918 175 7436 174 4918 175 7462 178 4887 176 4916 177 4914 174 4917 171 4921 172 4919 175 55184 175 7435 175 4918 175 7436 174 4918 175 7462 178 4914 174 4917 171 4920 173 4919 174 4917 176 4915 178\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 508 2484 480 1060 967 544 485 544 454 574 454 543 455 573 445 553 445 522 506 552 446 552 446 551 477 551 447 581 447 520 478 550 478 15908 626 2427 476 1062 476 522 995 547 451 1061 477 520 508 550 478 519 479 519 999 1058 959 1037 990 582 446 1035 483 111666 590 2404 479 1059 479 549 968 543 455 1027 511 548 480 517 511 486 512 546 961 1065 962 1034 993 579 449 1032 486\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 175 8475 174 8475 174 8477 172 8478 171 8480 169 5517 178 8473 175 8475 179 8501 173 5513 176 8505 169 45562 179 8472 177 8473 176 8474 175 8476 173 8478 171 5515 174 8476 178 8473 176 8505 174 5512 172 8508 171 120769 178 93377 175 7437 173 4919 174 7437 173 7439 171 7467 173 7439 171 7441 174 4918 170 4921 172 7439 171 7467 173 55167 171 7440 170 4922 171 7440 195 7443 172 7439 171 7441 174 7438 177 4915 173 4918 195 7442 173 7439 170\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 295 1805 273 776 242 1808 270 754 244 1806 272 777 241 758 270 754 264 760 268 756 272 14149 297 1802 266 784 244 1805 263 762 246 1803 265 785 244 780 248 751 267 758 271 753 265\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 535 1723 569 585 566 615 536 619 542 586 565 616 535 1722 539 615 536 1721 561 620 541 587 564 617 534 621 540 588 563 618 543 1714 537 617 534 621 540 615 536 618 543 612 539 615 536 1722 560 594 567 1717 534 1723 569 1714 557 1700 643 1639 643 1641 559\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 502 2521 504 521 997 545 453 575 453 545 453 575 454 22097 591 2433 511 513 994 1062 476 552 476 522 476 552 476 120839 628 2455 509 515 992 1064 484 513 505 493 515 543 475\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 2404 479 1060 967 1029 509 519 509 549 479 518 480 79926 585 2408 556 956 989 1033 515 544 484 543 475 522 476\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 586 2560 506 518 480 548 450 548 450 517 481 517 481 517 481 547 451 546 482 516 482 20040 590 2556 500 1038 480 518 969 543 455 543 475 1006 511 517 481 517 511 486 481 116778 584 2561 505 1033 485 513 964 548 450 548 480 1001 506 522 476 522 506 521 446\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 917 206 175 186 170 21561 170 2280 175 2274 502 1929 174 2276 169 5178 170 2261 173 3724 498 1932 171 2279 176 2273 172 3709 172 2277 178 3720 171 17223 177 7619 174 2275 170 2279 176 2256 168 2280 175 5172 176 2256 168 3729 173 2276 179 2253 171 2278 177 3703 178 2271 174 3724 177 17251 170 7627 177 2272 173 2276 169 2263 171 2277 178 5169 169 2262 172 3726 175 2256 168 2280 175 2274 171 3710 171 2278 177 3720 171\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 565 233 653 313 170 130328 752 235 233 107 229 398 177\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 586 2439 505 549 968 1027 511 517 511 517 481 547 481 21583 584 2439 505 520 997 1059 479 549 479 518 480 548 480 120894 593 2432 501 522 995 1061 477 521 507 520 478 550 478\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 558 8032 474 8115 503 8116 482 8108 480 8141 477 5239 476 8114 504 8115 483 8107 481 5236 509 8111 477 45290 554 8036 481 8108 510 8110 478 8112 476 8145 473 5243 482 8107 511 8109 479 8111 477 5240 505 8115 473\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 173 7438 172 4920 173 7438 172 4920 173 7465 175 4890 173 7439 171 4920 173 4919 174 4917 176 4915 178 55179 170 7441 169 4924 174 7437 178 4913 175 7463 172 4893 170 7441 174 4918 170 4922 171 4920 173 4918 175\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 225 745 222 774 193 778 200 797 175 769 193 777 201 771 196 774 224 747 220 776 202 769 198 248 220 252 196 250 218 253 225 746 221 250 198 248 220 252 226 246 222 223 225 248 220 252 216 229 219 253 225 247 221 277 176 243 220 279 189 230 228 244 224 248 220 252 196 250 218 253 215 257 201 770 197 799 189 257 201 271 197 37716 222 749 218 778 200 771 196 801 172 772 200 771 196 774 193 777 221 750 217 780 197 773 194 251 217 255 193 279 199 273 195 749 218 254 194 252 226 245 223 275 178 242 221 277 201 245 193 253 225 247 221 250 218 254 194 252 226 272 196 223 225 248 220 251 217 255 193 253 225 247 221 251 197 773 194 803 174 297 176 244 219\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3503 2655 197 642 876 2568 844 834 846 833 848 860 821 831 839 2576 847 2569 843 2572 841 2574 849 858 823 857 813 866 815 865 815 2572 841 2575 848 2568 844 2570 842 836 844 863 818 834 847 861 820 2568 845 2571 872 2571 842 32651 3505 3495 875 2567 845 861 820 832 849 859 811 840 840 2576 847 2568 844 2571 842 2574 849 830 840 867 814 838 842 865 815 2572 840 2575 848 2568 845 2571 841 865 815 864 817 834 846 861 819 2569 843 2572 871 2572 840\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 347 677 219 252 196 276 202 742 225 798 174 770 192 778 200 771 196 775 223 748 219 777 200 770 197 249 219 253 195 251 227 271 197 747 220 252 216 229 219 253 225 273 195 277 176 244 219 253 215 230 228 244 224 248 220 252 196 250 218 280 198 247 201 245 223 249 219 253 195 251 227 245 223 248 200 797 175 795 198 248 200 246 222 37666 344 678 228 245 223 222 226 771 196 800 198 747 220 750 217 753 224 773 194 776 202 769 198 799 173 246 227 245 223 249 199 247 221 749 218 280 198 247 201 245 223 249 219 253 195 251 227 244 224 248 200 272 196 250 218 254 194 252 226 245 223 249 199 274 194 251 227 245 193 253 225 273 195 251 217 753 225 746 221 251 197 275 193\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 7437 173 7440 174 7437 178 7433 177 7461 169 7442 178 4914 174 4917 171 4921 172 4919 174 7463 177 55163 177 7435 174 7437 173 7438 172 7440 175 7463 172 7413 176 4915 178 4913 175 4917 171 4920 174 7438 172\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8042 3979 513 510 508 541 487 1559 509 515 513 1560 508 515 513 510 508 541 477 3981 532 1568 510 1563 515 1558 510 1564 514 1559 509 514 514 535 483 540 488 24151 8042 3979 513 536 482 541 487 1560 508 541 488 1560 508 541 487 536 482 541 477 3980 533 1566 512 1561 507 1566 512 1562 516 1557 511 538 491 533 485 538 490\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8988 4504 640 487 562 592 538 590 510 592 538 590 540 588 532 596 514 614 516 1689 562 1668 563 1695 536 1695 566 1664 567 1690 612 1618 643 1430 801 1613 648 481 558 570 540 589 541 587 533 595 535 592 508 594 536 592 538 1667 564 1693 558 1672 569 1688 563 1668 644 1586 563 1694 567 40630 8994 2265 557 96833 8987 2273 538\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 173 7439 171 4922 172 7439 171 4921 172 7466 174 4917 176 4915 173 4918 170 7441 174 7438 192 7445 175 55181 174 7437 173 4918 175 7437 173 4919 175 7463 177 4888 175 4917 176 4915 178 7460 170 7440 175 7437 178\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1636 4610 1563 1533 1584 7760 1561 28769 1641 4606 1557 1539 1588 7757 1564\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 590 2404 479 1059 968 575 454 544 454 574 454 543 455 543 475 522 476 552 476 552 446 551 447 581 448 520 478 581 447 519 479 549 479 15967 587 2407 476 1062 476 522 995 547 451 1030 508 520 509 550 478 519 479 519 998 1028 999 1057 481 547 960 1066 482 111641 587 2407 476 1063 485 513 994 547 451 1031 507 551 477 551 477 520 478 550 968 1059 968 1027 511 548 959 1036 512\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 588 2406 477 1061 966 577 451 516 482 545 483 545 453 575 443 524 484 514 504 554 454 543 455 543 475 553 445 583 445 521 477 582 446 15969 585 2409 474 1065 483 514 993 549 449 1033 515 543 475 553 475 522 476 552 965 1030 997 576 452 1029 998 1028 479 111668 587 2407 475 1063 485 543 964 517 481 1031 507 552 476 551 477 520 478 550 968 1028 999 574 454 1027 990 1036 481\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 7439 196 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 7442 178 7433 177 4915 173 4918 170 55186 174 7438 172 7440 170 7441 174 7438 177 7461 169 7416 173 7464 176 7435 175 7437 173 4918 175 4917 176\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 173 7438 172 7441 174 7438 177 7434 176 7462 168 7443 177 7435 175 4917 176 4915 173 7438 177 4941 173 55166 174 7438 197 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 4922 171 4893 195 7443 172 4920 173\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 175 7436 179 4913 175 4917 171 4920 173 4945 169 4896 177 7435 175 7436 174 7464 176 4916 177 4914 169 55180 170 7441 169 4924 169 4922 171 4920 173 4919 174 4917 176 7435 175 7463 177 7434 176 4916 177 4914 174\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 02 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 5991 177 1372 176 1320 177 1344 173 1349 174 1374 169 1327 170 4449 176 1346 171 1351 177 1345 172 1349 168 1353 175 5963 175 65573 173 5995 178 1344 174 1348 175 1348 174 1347 170 1378 170 1325 172 4447 178 1344 174 1349 169 1353 175 1347 170 1352 176 5961 177\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 71 00 00 00\ncommand: 4A 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 60 00 00 00\ncommand: 03 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 60 00 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 42 00 00 00\ncommand: 01 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 50 AD 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 50 AD 00 00\ncommand: 02 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 3F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 06 00 00 00\ncommand: 0F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 08 00 00 00\ncommand: 0B 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 83 55 00 00\ncommand: C2 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 51 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 01 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 00 00 00 00\ncommand: 0F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 16 00 00 00\ncommand: 0F 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 01 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 80 68 00 00\ncommand: 49 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 02 00 00\ncommand: 49 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 7761 176 11308 546 957 540 1958 538 970 537 1955 541 962 545 1953 543 964 543 962 545 957 540 970 548 960 547 1945 541 1950 546 965 542 1953 543 962 545 1945 540 970 537 1958 538 7881 172 7744 172 11318 536 971 536 1956 540 963 534 1964 542 966 541 1951 535 968 539 971 536 971 536 969 538 964 533 1965 541 1954 542 963 534 1956 540 971 536 1959 537 968 539 1951 535\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 278 1845 274 808 271 806 273 812 278 805 275 805 274 1840 279 1844 275 809 281 1836 272 806 274 812 278 805 274 1842 277 802 277 44956 279 1842 277 804 275 802 277 808 271 811 279 1838 281 798 271 814 276 1844 275 806 273 1841 278 1845 274 1846 273 808 271 1843 276 44959 275 1845 274 807 272 805 275 811 279 804 275 805 274 1839 280 1844 275 808 271 1845 274 805 274 811 279 804 275 1841 278 801 278 44955 280 1841 278 802 277 801 278 807 272 810 280 1837 271 807 272 813 277 1843 276 805 274 1839 280 1843 276 1845 274 807 272 1842 277\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 509 1717 504 629 512 631 510 627 504 633 508 633 508 1713 508 1719 512 1713 508 625 505 637 504 633 508 629 512 629 512 623 508 1719 512 626 505 628 513 631 510 627 514 623 507 632 509 1713 508 632 509 1716 505 1715 506 1724 507 1716 505 1719 512 1715 506\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 506 493 505 4059 505 5051 501 506 502 4065 499 511 497 4063 501 5058 504 504 504 4060 504 5052 500 507 501 4066 498 5056 506 5042 500 515 503 119614 504 505 503 4065 499 5051 501 511 497 4069 505 499 499 4072 502 5050 502 506 502 4066 498 5053 499 512 506 4060 504 5044 498 5061 501 508 500\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 87 22 00 00\ncommand: E0 1F 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8313 4161 515 1574 514 1571 507 569 510 562 507 563 506 562 507 568 511 562 507 1580 508 1577 511 1582 506 567 513 1575 513 554 505 571 509 564 505 22604 513 1573 505 1589 509 563 506 564 505 562 507 569 511 562 507 563 506 1579 509 1584 514 1576 512 558 511 1574 514 562 507 565 515 556 513 22593 514 1581 507 1583 505 564 505 563 506 570 510 563 506 564 505 562 507 1586 512 1578 510 1577 511 557 512 1581 507 566 514 556 513 555 514\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8735 4383 558 573 557 550 560 568 562 544 566 1722 560 540 560 1732 560 544 566 1719 563 1701 560 1723 559 1704 557 574 556 1699 562 574 556 1703 559 1727 565 1698 563 1721 561 546 564 1723 559 541 559 577 564 541 559 571 560 548 562 565 566 1697 565 567 564 1693 558 1733 559 1701 560 39926 8754 2247 565 92341 8758 2243 589\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3298 3336 821 2506 825 881 820 2505 826 2529 823 856 825 2524 817 866 825 2528 813 2513 818 888 813 862 819 887 814 865 826 2522 820 864 827 2526 815 861 820 887 814 2510 821 885 816 863 818 2531 821 2512 819 2559 793 32401 3298 3349 818 2507 824 882 819 2509 822 2527 814 868 823 2530 821 855 826 2531 821 2504 827 879 822 857 824 875 816 867 824 2529 823 854 827 2530 821 853 817 889 822 2506 825 873 818 866 825 2527 814 2513 818 2564 788\n#\nname: Power\ntype: parsed\nprotocol: NEC42\naddress: 1C 01 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 289 2112 261 2109 295 2101 262 918 294 912 259 915 297 2098 265 916 296 909 262 2107 297 905 266 913 289 918 263 910 292 909 262 918 294 24789 263 2106 298 2098 265 2110 294 913 258 916 296 905 266 2108 296 911 260 914 288 2107 266 914 298 908 263 911 291 910 261 919 293 913 258\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3215 1637 410 430 405 439 406 1242 408 435 410 1242 408 428 407 440 405 435 410 1240 410 1243 407 431 404 440 405 437 408 1238 402 1254 406 434 401 439 406 438 407 431 404 440 405 437 408 428 407 439 406 434 401 439 406 438 407 1241 409 434 401 441 404 432 403 444 401 1249 401 439 406 438 407 1241 409 434 401 441 404 433 402 444 401 1249 401 439 406 1248 402 436 409 434 401 441 404 433 402 444 401 439 406 45471 3239 1614 403 435 400 444 401 1250 400 437 408 1248 402 438 407 433 402 442 403 1245 405 1248 423 420 405 431 404 443 402 1248 402 1248 422 421 404 435 400 443 402 440 405 431 404 443 402 438 407 433 402 442 403 435 400 443 402 1250 400 436 399 447 408 432 403 438 407 1246 404 434 401 443 402 1250 400 436 399 447 408 432 403 437 408 1246 404 434 401 1253 407 434 401 436 399 447 408 432 403 437 408 436 399\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 7928 3948 504 515 503 518 500 1583 505 516 502 1584 504 510 508 516 502 516 502 3952 510 1579 509 507 501 1587 501 519 510 1571 507 518 500 517 501 517 501 23073 7931 3943 509 514 504 515 503 1578 500 524 505 1580 508 510 508 514 504 511 507 3951 511 1576 502 513 505 1586 502 515 503 1582 506 515 503 513 505 516 502\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 18 18 00 00\ncommand: C0 3F 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 492 4975 496 4993 498 498 500 4056 498 504 494 4055 499 4963 497 532 496 4031 492 4996 495 502 496 4060 494 4972 499 4990 491 506 492 4063 491 511 497 4052 492 4970 490 539 500 4027 496 103358 500 4961 500 4995 496 505 493 4056 498 499 499 4057 497 4969 491 533 496 4026 497 4997 494 508 490 4059 495 4967 494 5001 490 511 497 4053 491 505 493 4063 491 4975 496 528 490 4032 491\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 2F 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3425 3482 848 2607 846 2639 845 2608 846 2639 845 2612 852 2624 850 2613 851 911 851 885 847 919 843 891 851 915 847 890 852 907 845 897 845 917 845 891 851 915 847 887 845 2639 845 2612 852 2625 849 2613 851 2630 844 34282 3455 3478 852 2601 852 2633 851 2605 848 2629 845 2617 847 2633 851 2604 849 917 845 890 852 913 849 889 843 915 847 896 846 916 846 890 852 914 848 885 847 919 843 895 847 2630 844 2617 847 2634 850 2605 848 2636 848\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 500 500 498 4066 498 5058 504 502 506 4061 503 508 500 4060 504 5055 497 5055 497 511 497 4071 503 5047 505 507 501 4065 499 5049 503 512 496 124314 501 508 500 4067 507 5043 499 513 505 4060 504 501 497 4073 501 5052 500 5052 500 512 496 4066 498 5058 504 506 502 4058 506 5053 499 509 499\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7763 174 8817 169 10842 544 1955 541 967 540 1952 544 959 538 972 546 962 545 1947 538 1952 544 967 540 1955 541 964 543 1947 539 972 546 1949 547 7873 170 7746 170 10350 175 11811 543 960 537 1961 535 972 535 970 537 965 542 1956 540 1956 540 965 542 1947 539 973 534 1960 536 969 538 1952 534\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2570 2682 1189 1208 1186 2665 1186 1217 1187 2668 1183 1215 1179 2671 1190 2695 1186 1187 1187 2692 1179 2671 1190 1213 1181 1193 1191 1207 1187 2663 1188 1215 1179 2676 1185 46941 2563 2685 1186 1191 1183 2698 1184 1188 1186 2691 1180 1197 1187 2694 1187 2666 1185 1210 1184 2674 1187 2695 1186 1185 1189 1206 1188 1189 1185 2696 1186 1186 1187 2689 1182\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 26 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3325 1560 406 444 401 453 402 1226 404 449 406 1226 404 443 402 454 401 449 406 1224 406 1228 402 446 409 444 401 451 404 1223 407 1230 410 440 405 445 410 443 402 446 409 444 401 451 404 442 403 453 402 448 407 443 402 451 404 1224 406 448 407 444 401 445 410 446 409 1221 409 442 403 1231 409 439 406 1227 403 450 405 441 404 452 403 1227 403 448 407 446 409 439 406 447 408 444 401 445 400 456 410 440 405 52348 3320 1553 403 445 400 454 401 1230 400 447 408 1228 402 449 406 444 401 452 403 1225 405 1229 431 421 404 442 403 454 401 1229 401 1229 431 423 402 446 399 455 400 451 404 442 403 453 402 448 407 443 402 451 404 444 401 452 403 1229 401 445 400 457 398 451 404 446 399 1235 405 443 402 1232 408 444 401 1225 405 452 403 447 398 452 403 1231 399 449 406 447 408 443 402 444 401 456 399 450 405 445 400 453 402\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 990 915 980 909 986 924 981 2829 981 934 981 2823 987 923 982 2828 982 933 982 906 989 33895 989 907 988 927 988 900 985 2841 979 915 980 2851 980 909 986 2840 980 914 981 934 981\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 821 5754 848 2490 841 2492 819 2524 817 5726 845 2492 839 5727 844 5757 845 5727 854 2483 848\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5092 1621 406 2599 406 2598 407 2605 1653 1616 411 2619 1609 1606 1654 1618 409 2623 382 2600 405\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 15 00 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1144 1010 6795 26754 1151 997 6798\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1144 1009 1120 1006 1143 1991 1116 26758 1146 1006 1123 1003 1146 1988 1119\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 46238 169\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8906 4165 572 1672 569 1678 563 640 572 637 565 643 569 633 569 643 569 1674 567 639 563 1684 567 637 565 1681 570 1675 566 1673 568 1681 570 636 566 640 572 637 565 639 563 1684 567 640 572 630 572 640 572 634 568 1675 566 1681 570 1670 571 638 564 1681 570 1669 572 1678 563 1680 571 40485 8898 2252 570 85621 8955 2194 567\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 446 1191 449 1194 446 1195 445 1204 446 1200 1316 459 447 1193 447 1202 448 1197 443 1201 449 1191 449 34491 443 1204 446 1197 443 1198 442 1207 443 1202 1314 436 440 1201 449 1199 441 1205 445 1198 442 1199 441\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4268 4327 522 1593 516 1603 516 1597 522 519 520 520 519 515 514 531 518 520 519 519 520 521 518 519 520 1597 522 1595 514 1597 522 1599 520 1595 514 524 515 1604 515 521 518 523 516 524 515 519 520 525 514 524 515 1599 520 522 517 1596 513 1605 514 1602 517 1595 514 1607 522 1592 516 40481 8748 2187 523 93986 8721 2189 521\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 247 3006 244 153 178 1007 251 117 637 597 381 678 218 156 175 529 382 679 217 157 174 24963 247 3038 252 117 219 994 249 119 217 483 250 117 173 531 278 779 173 167 169 569 383 679 217 156 175 126538 246 3039 251 118 218 995 247 120 195 504 249 118 172 532 277 780 172 168 178 560 382 680 216 157 174\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 10 00 00 00\ncommand: EF 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4278 4318 521 517 522 520 519 517 522 520 519 521 518 516 513 531 518 520 519 1595 514 1605 514 1599 520 1598 521 1595 513 1598 521 1600 519 1596 513 1602 517 1601 518 1595 514 1604 515 525 514 520 519 526 513 525 514 524 515 527 522 513 516 526 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93985 8722 2189 521\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2746 8423 2744 19607 2746 19601 2742 8431 2745 8424 2742 19608 2745 8419 2747 19608 2745 19607 2746 8422 2744\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 6683 174 4889 175 10382 372 849 821 2498 176 10378 264 2479 842 2492 839 2475 846 2483 838 2481 840 2495 836 2477 844 845 846 37009 172 6681 176 4888 175 10381 373 848 924 2395 844 2490 174 10383 248 2492 172 10386 245 2495 169\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: C4 00 00 00\ncommand: 18 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 37 00 00 00\ncommand: 12 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 179 2644 178 8174 170 2646 176 8181 173 2648 174 8177 177 2640 172 5419 174 5413 170 2649 173 2644 178 2646 176 2646 176 5409 174 28831 174 2651 171 8183 171 2648 174 8174 170 2655 177 8177 177 2642 170 5412 171 5420 173 2649 173 2646 176 2640 172 2653 169 5419 174\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 174 2648 174 8178 176 2640 171 8185 169 2653 169 8182 172 2645 177 2647 557 2264 558 5027 174 5410 173 5418 175 5413 170 28837 168 2649 173 8184 170 2652 170 8181 173 2643 169 8188 176 2646 176 2643 168 2648 174 5417 176 5411 172 5413 170 5413 170\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 706 266 139 268 137 173 173 198 234 178 126768 175 299 169 86 275 130 267 138 172 230 177\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 554 1922 584 3809 582 3782 578 3821 580 1920 555 1941 544 1922 574\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 476 1470 465 3479 474 3469 474 3477 475 1467 468 1471 474 27473 472 1474 472 3475 467 3478 475 3468 474 1471 475 1468 467\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 301 2188 236 2169 235 2205 230 1009 234 1070 183 1064 179 2198 206 1046 207 1069 173 2207 207 1042 211 1062 201 1043 210 1032 231 1005 237 1039 234 1006 236\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 6D 00 00 00\ncommand: 14 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 11 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 0B 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 0B 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: FF 00 00 00\ncommand: 3F 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7764 173 11361 544 1951 545 1948 176 8815 171 2319 177 2322 174 2321 175 2318 178 2313 173 18281 170 7746 170 11369 536 1954 542 1957 539 969 538 1954 542 1949 536 1962 534 1960 174 2320 176 2315 170 2328 178 2317 168\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 172 23035 176 2267 178 2285 170 2270 175 2288 177 11602 222 2218 216 2246 173 183 173 935 221 218 174 864 221 1250 171 1310 223 2219 216 2247 218 1244 223 216 176 869 170 1296 217 2239 216 1254 223 216 176 866 219 9127 169 7642 173 2284 171 2273 172 2290 175 2263 171 10143 178 10623 222 1245 222 217 175 1843 220 2226 219 1264 223 1237 174 183 178 952 219 2222 223 216 176 866 219 1249 172 9190 173 7637 178 2266 169 2286 169 2280 175 2284 171 10125 175 10622 224 215 177 868 216 2228 217 2238 171 1299 224 215 177 865 174 183 173 934 222 216 176 1848 215 1247 220 1265 222 762 170\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 7410 1482 382 2742 375 2747 380 2751 1630 1535 400 2724 1657 1529 1629 1538 407 2720 407 2716 401 2722 4125 1549 376 2751 376 2748 379 2744 1626 1541 405 2723 1658 1530 1628 1531 404 2726 401 2726 401 2723 4124 1542 383 2747 380 2747 380 2745 1626 1534 401 2729 1652 1539 1629 1532 403 2719 408 2722 405\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4277 4319 520 518 521 521 518 518 521 1597 522 1594 515 1597 522 1599 520 1594 515 1600 519 1600 519 1594 515 526 513 527 522 512 517 528 521 517 522 516 513 1605 514 522 517 525 514 525 514 521 518 526 513 525 514 1600 519 523 516 1597 522 1596 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93986 8721 2188 522\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1325 431 445 1199 1317 455 441 1207 443 1202 1294 457 449 1190 440 1209 441 1205 445 1198 1318 454 442 93237 1320 434 442 1201 1325 448 448 1200 440 1205 1291 460 446 1193 447 1202 448 1198 442 1201 1325 447 449\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 0B 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 300 1791 297 744 295 743 296 751 298 745 294 747 302 736 293 1837 271 745 294 747 302 1793 295 752 297 1802 296 1801 297 742 297 1806 302 31592 296 1801 297 742 297 749 300 744 295 745 294 745 294 752 297 1829 269 746 293 745 294 1809 300 744 295 1802 296 1799 299 748 301\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 2003 177 2899 177 1996 174 2908 168 2910 177 2000 170 2004 176\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8898 4173 564 642 570 1677 564 1677 564 645 567 640 572 631 571 641 571 635 567 639 563 1683 568 1673 568 641 571 636 566 637 565 647 565 641 571 634 568 642 570 1671 570 639 563 1682 569 632 570 643 569 636 566 1677 564 1683 568 636 566 1680 571 636 566 1674 567 1682 569 1674 567 40489 8904 2246 566 85626 8902 2248 564\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: AA 00 00 00\ncommand: 80 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 51 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3892 3856 525 978 529 977 530 969 528 977 530 974 523 975 532 1924 531 971 526 1924 531 975 532 1916 529 976 531 1921 524 1947 508 1925 530 1920 525 1926 529 1925 530 970 527 1927 528 976 531 1915 530 978 529 1921 1033 9201 3871 3867 524 977 530 975 522 981 526 972 525 983 524 978 529 1921 524 982 525 1923 532 973 524 1928 527 971 526 1931 524 1950 505 1922 523 1931 524 1924 531 1923 532 972 525 1922 523 985 533 1918 527 975 532 1922 1032\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 14 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 07 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4758 1543 403 2731 407 2726 402 2739 1601 1514 401 2760 1580 1530 1577 1540 406 2758 400 2708 1602 1535 400 2740 408 2729 409 2726 401 2731 1599 1520 405 2758 1572 1540 1578 1532 403 2737 431 2706 1604 1535 411 2722 405 2734 403 2734 404 2731 1599 1512 403 2764 1576 1539 1578 1533 402 2730 428 2712 1608 1534 402\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: AA 00 00 00\ncommand: A7 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 171 313 176 798 337 133 600 232 170 126777 176 65 169 496 176 200 609\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 500 527 491 4033 500 4986 495 510 498 4054 500 499 499 4048 496 4974 497 530 499 4026 497 4989 492 513 495 4057 497 4967 493 4992 499 506 492 4060 494 505 493 4054 500 98563 497 529 500 4025 498 4988 493 512 496 4056 498 501 497 4050 493 4976 495 532 497 4028 495 4991 500 505 493 4059 495 4969 491 4994 497 508 490 4062 492 507 491 4056 498\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 879 901 871 1796 1770 903 869 917 876 916 877 913 880 906 877 918 875 914 879 1788 1768 1784 875 87826 871 921 872 1797 1769 897 875 919 874 915 878 910 873 920 873 914 879 913 870 1800 1776 1767 871\n#\nname: Power\ntype: parsed\nprotocol: Kaseikyo\naddress: 90 02 20 00\ncommand: D0 03 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4411 4332 558 1660 561 596 565 612 559 597 564 617 565 585 566 620 561 591 560 620 561 595 566 611 560 597 564 1653 558 592 559 627 565 589 562 617 564 1630 560 617 564 592 559 22591 4439 4322 558 1639 561 618 563 590 561 622 560 592 559 624 557 597 564 612 559 600 561 618 563 590 561 622 559 1628 562 621 561 594 567 609 562 597 564 1652 559 595 566 617 564\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3444 1767 413 487 419 1274 417 481 415 1277 414 488 418 1267 414 492 414 1276 415 484 412 1282 419 478 418 1275 416 1276 415 480 416 1280 421 479 417 1272 419 1275 416 1272 419 1274 417 484 412 484 412 493 413 1277 414 485 421 1273 418 479 417 486 420 1271 420 476 420 486 420 479 417 482 414 1280 411 1276 415 488 418 1273 418 478 418 488 418 481 415 1275 416 487 419 478 418 485 411 1280 421 475 421 1275 416 1273 418 69071 3439 1759 441 456 440 1253 438 463 443 1243 438 468 438 1251 440 460 446 1247 444 454 442 1251 440 461 445 1241 440 1256 445 455 441 1248 443 461 445 1242 439 1254 447 1245 446 1240 441 465 441 458 438 462 444 1249 442 456 440 1252 439 463 443 452 444 1252 439 461 445 454 442 461 445 452 444 1249 442 1250 441 454 442 1254 437 463 443 456 440 463 443 1245 446 456 440 462 444 451 445 1251 440 460 436 1253 438 1256 445\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8705 4317 583 682 581 688 585 678 585 1721 581 1722 580 681 582 690 583 682 581 1721 581 1725 587 1713 579 689 584 683 580 1718 584 1724 588 1714 588 677 586 683 580 683 580 1726 586 680 583 678 585 687 586 679 584 1718 584 1721 581 1719 583 685 588 1716 586 1713 579 1729 583 1719 583 41145 8706 2217 585 94686 8705 2217 584\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 10 2D 00 00\ncommand: 1F E0 00 00\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 05 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 178 7753 174 2308 178 2284 171 2316 170 2298 177 10228 174 10724 223 1256 221 217 175 1868 169 2293 172 1327 216 1263 178 1316 217 2245 220 218 174 887 218 1261 170 10707 174 7752 175 2296 169 2314 171 2293 172 2307 179 10216 176 6265 174 10729 219 1258 219 1272 215 1268 173 2310 221 1255 222 217 175 877 172\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 532 1692 559 561 529 574 567 559 531 566 564 555 535 1694 557 568 532 1691 560 560 530 573 557 568 532 565 565 555 535 567 563 1688 533 564 567 554 536 567 563 562 538 558 562 558 532 1697 565 561 539 1683 558 1689 532 1697 564 1687 534 1689 562 1684 537\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 0C 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 12 FF 00 00\ncommand: 0E F1 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 446 1694 455 1706 453 1705 424 628 452 597 452 622 458 1673 456 625 455 594 455 1706 454 590 459 621 459 591 458 616 453 591 458 622 539 22750 459 1675 454 1733 427 1712 427 622 458 588 451 622 458 1681 458 618 451 595 454 1705 455 598 451 625 454 592 457 616 453 598 451 626 535\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3429 3445 875 2555 868 875 867 871 871 2560 873 868 874 2551 872 874 868 871 871 869 873 870 872 865 867 2565 868 873 869 2556 867 2567 866 874 868 2560 873 870 872 2555 868 2564 869 2586 847 2577 846 2589 844 870 872 32983 3470 3430 869 2559 874 868 874 867 875 2550 873 873 869 2559 874 865 867 877 875 862 870 873 869 872 870 2555 868 877 875 2554 869 2559 874 869 873 2554 869 873 869 2562 871 2553 870 2590 843 2586 847 2581 842 876 866\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 83 00 00 00\ncommand: FF 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4275 4320 519 520 519 523 516 520 519 1599 520 520 519 515 524 521 518 520 519 1595 524 1595 524 1588 520 521 518 1599 520 1591 517 1603 516 1599 520 1595 524 1594 525 1588 521 1597 522 518 521 513 526 519 520 518 521 517 522 520 519 517 522 519 520 1597 522 1589 519 1601 518 1597 522 40475 8724 2186 514 93995 8752 2183 516\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 10380 4892 599 620 592 605 597 620 592 604 598 623 589 2081 598 627 595 2080 599 2101 598 2105 574 2099 590 2088 591 2111 599 591 590 2116 594 599 593 626 596 2082 597 619 593 2086 593 626 596 594 598 627 595 598 594 2106 593 604 598 2100 589 607 595 2107 593 2079 590 2116 594 2082 750 41149 8722 2109 590 94682 8727 2104 596\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 83 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4271 4324 515 1600 519 1600 519 1594 515 1603 516 1601 518 1593 516 1605 514 1601 518 520 519 522 517 520 519 522 517 523 516 518 521 523 516 522 517 1598 521 1597 522 1591 518 1600 519 521 518 516 523 522 517 521 518 520 519 523 516 520 519 522 517 1599 520 1591 518 1603 516 1599 520 40477 8753 2183 516 93993 8724 2185 515\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 7847 3931 470 1448 467 495 472 1443 472 490 467 1451 464 491 466 499 468 491 466 4413 467 1455 470 486 471 1449 466 495 472 1441 464 501 466 492 465 494 463 22093 7851 3934 467 1454 471 490 467 1446 469 496 471 1446 469 489 468 494 473 484 473 4410 470 1449 466 489 468 1455 470 489 468 1449 466 496 471 486 471 490 467\n#\nname: Power\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 0C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 20 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 21 00 00 00\n#\n# Model: VIZIO\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 03 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3462 1592 490 332 513 1200 489 331 514 1201 489 355 490 1201 489 356 512 1178 489 356 512 1178 512 334 487 1202 488 1202 488 357 512 1178 512 334 486 1203 487 1202 488 1203 487 1204 486 383 461 1228 488 357 488 357 487 357 487 1203 486 1204 486 359 485 1205 485 360 485 361 484 360 485 360 485 361 484 361 484 361 484 1206 484 360 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 1206 484 361 484 71543 3434 1620 486 359 485 1205 485 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1205 485 1206 484 360 485 1206 484 360 485 1206 484 1206 484 1206 484 1206 484 361 484 1206 484 360 485 360 485 361 484 1206 484 1206 484 360 484 1206 484 360 485 361 484 361 484 360 485 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1207 483 361 484 1206 484 361 484 71543 3435 1619 486 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 484 1205 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 1205 485 1206 484 360 485 1205 485 360 485 360 485 360 485 1205 485 1206 484 360 485 1206 484 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1205 485 360 485 1206 484 360 485 71542 3436 1619 486 358 487 1204 486 359 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 1206 484 1206 484 1206 484 1206 484 360 485 1206 484 360 485 360 485 361 484 1206 484 1206 484 361 484 1206 484 361 484 361 484 361 484 361 484 361 484 361 484 360 485 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 360 485 1206 484 361 484 1206 484 361 484 71542 3437 1618 487 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1206 484 360 485 1205 485 360 485 1206 484 1205 485 1206 484 1205 485 360 485 1205 485 360 485 360 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 1205 485 360 485\n#\n# Thomson RC3000E02\n#\nname: Power\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 54 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: F4 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 74 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: B4 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: 34 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RCA\naddress: 0F 00 00 00\ncommand: FC 00 00 00\n#\n# VOX Electronics 43ADS316B\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 17 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 11 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 10 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 20 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 21 00 00 00\n#\n# Model: JTC Genesis 5.5\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 5C A3 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 06 F9 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 48 B7 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 01 72 00 00\ncommand: 44 BB 00 00\n#\n# Koro Box\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 18 00 00 00\n#\n# Toshiba Amazon TV\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 0E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 0F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 17 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 00 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 01 00 00 00\n#\n# Emerson TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 20 DF 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 50 AF 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 51 AE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 60 9F 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 61 9E 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 84 E0 00 00\ncommand: 64 9B 00 00\n#\n# TCL 75S451\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 17 E8 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 20 DF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 0F F0 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 10 EF 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 19 E6 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 33 CC 00 00\n#\n# Model: Soniq E32W13B\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 0E F1 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 1A E5 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 49 B6 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 43 BC 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 51 AE 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 4D B2 00 00\n#\n# Model: Hisense EN2B27\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 0D F2 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 0E F1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 44 BB 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 43 BC 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 4A B5 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 4B B4 00 00\n#\n# Model: Viano STV65UHD4K\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 06 F9 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5B A4 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 18 E7 00 00\n# # Model: FireTV Omni_Series_4K\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 46 B9 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 19 E6 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 4C B3 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 0F F0 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 5A A5 00 00\n#\n# Model: Android TV_MXQ\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 40 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 18 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 41 00 00 00\n#\n# Model: APEX LE4643T\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 0A F5 00 00\n#\n# Model: BAIRD T15011DLEDDS_RC-6\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 0C 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 0D 00 00 00\n#\n# Model: BBK TV_LEM-1071\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 1C E3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4B B4 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 4F B0 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 08 F7 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 09 F6 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 DF 00 00\ncommand: 05 FA 00 00\n#\n# Model: BGH BLE2814D\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 03 FC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 15 EA 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 16 E9 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 1A E5 00 00\n#\n# Model: Blaupunkt\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 50 AF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5F A0 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1F E0 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5C A3 00 00\n#\n# Model: Blitzwolf BWPCM2\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: A0 B7 00 00\ncommand: E9 16 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: A0 B7 00 00\ncommand: AF 50 00 00\n#\n# Model: Bolva TV\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 41 BE 00 00\n#\n# Model: Bose TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 4C B3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: BA A0 00 00\ncommand: 01 FE 00 00\n#\n# Model: BUSH TV_VL32HDLED\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: D7 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 80 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 8E 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 83 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: 86 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 08 00 00 00\ncommand: DF 00 00 00\n#\n# Model: CCE RC512_Remote\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 40 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 04 00 00 00\ncommand: 12 00 00 00\n#\n# Model: ContinentalEdison\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5C A3 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 48 B7 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 44 BB 00 00\n#\n# Model: ContinentalEdison CELED32JBL7\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 12 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 14 00 00 00\n#\n# Model: Crown 22111\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 52 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 53 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 09 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 03 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 41 00 00 00\n#\n# Model: Daewood Parsed\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 82 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 9F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 8B 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 9B 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 8F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: D0 00 00 00\n#\n# Model: Dual DL-32HD-002\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 0F F0 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 5A A5 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 0C F3 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 02 7D 00 00\ncommand: 19 E6 00 00\n#\n# Model: Dynex DX-RC01A-12\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0F F0 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0D F2 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0A F5 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0B F4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 86 05 00 00\ncommand: 0E F1 00 00\n#\n# Model: DYON Movie_Smart_32_XT\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 0A F5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 0F F0 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 15 EA 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 1C E3 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 1F E0 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 40 40 00 00\ncommand: 1E E1 00 00\n#\n# Model: EdenWood TV\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5\naddress: 01 00 00 00\ncommand: 14 00 00 00\n#\n# Model: Elitelux L32HD1000 / Vivax TV-32LE114T2S2SM / Sansui\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 15 EA 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1B E4 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 1A E5 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 19 E6 00 00\n#\n# Model: Enseo\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 02 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 06 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 0C 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 08 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 0E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 6E 00 00 00\ncommand: 04 00 00 00\n#\n# Model: Fetch TV_Box_AUS\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 64 46 00 00\ncommand: 5D A2 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 64 46 00 00\ncommand: DE 21 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 64 46 00 00\ncommand: DB 24 00 00\n#\n# Model: Furrion\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 02 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 03 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 20 00 00 00\ncommand: 41 00 00 00\n#\n# Model: AORUS Monitor\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 1A 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 00 00 00 00\ncommand: 33 00 00 00\n#\n# Model: Grandin\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 1E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 80 00 00 00\ncommand: 10 00 00 00\n#\n# Model: Grandin Unknown_Model\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 1A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 1E 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 1B 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 40 00 00 00\ncommand: 1F 00 00 00\n#\n# Model: GRUNDIG\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 20 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 21 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 0D 00 00 00\n#\n# Model: Hitachi 43140\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 11 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 0D 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 20 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC5\naddress: 03 00 00 00\ncommand: 21 00 00 00\n#\n# Model: Hitachi LE46H508\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 15 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 19 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 50 00 00 00\ncommand: 18 00 00 00\n#\n# Model: KRAFT KTV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 0A F5 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 0B F4 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 1E E1 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 5F A0 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 1F E0 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 01 3E 00 00\ncommand: 5C A3 00 00\n#\n# Model: LG 27GR95QE_TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 04 F4 00 00\ncommand: 08 F7 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 04 F4 00 00\ncommand: 02 FD 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 04 F4 00 00\ncommand: 03 FC 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 04 F4 00 00\ncommand: 09 F6 00 00\n#\n# Model: LG Hotel_TV_Home2\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 69 69 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 69 69 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 69 69 00 00\ncommand: 0B F4 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 69 69 00 00\ncommand: 0E F1 00 00\n#\n# Model: LG MR21GC_Magic_Remote\n#\nname: Ch_next\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 34 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: SIRC20\naddress: 10 01 00 00\ncommand: 33 00 00 00\n#\n# Model: lodgenet lrc3220\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 80 7F 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 8F 70 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 93 6C 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 8D 72 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 91 6E 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 85 7C 00 00\ncommand: 97 68 00 00\n#\n# Model: LOEWE TV\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 18 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC5\naddress: 00 00 00 00\ncommand: 17 00 00 00\n#\n# Model: Manta\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 48 B7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 49 B6 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 01 FE 00 00\n#\n# Model: Manta TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BF 00 00\ncommand: 00 FF 00 00\n#\n# Model: Manta TV_2\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 5F 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 40 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 5D 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 03 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 1F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 1C 00 00 00\n#\n# Model: Matsui 1435b\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 14 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 11 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 10 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 13 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 12 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 17 00 00 00\ncommand: 15 00 00 00\n#\n# Model: Medion MD21302\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 18 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 56 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 4F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 0D 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 4C 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 19 00 00 00\ncommand: 0F 00 00 00\n#\n# Model: Mivar LCD_TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 0A F5 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 58 A7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 4B B4 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 1F E0 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 1E E1 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 FB 00 00\ncommand: 0F F0 00 00\n#\n# Model: NEC E425\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 01 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 0C 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 18 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 1C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 04 00 00 00\n#\n# Model: Neo TV\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 16 E9 00 00\n#\n# Model: Panasonic 58JX800_Series\n#\nname: Vol_up\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: 00 02 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: 10 02 00 00\n#\n# Model: Panasonic N2QAYA_152\n#\nname: Ch_next\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: 40 03 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: 20 03 00 00\n#\n# Model: Panasonic N2QAYB000705\n#\nname: Ch_prev\ntype: parsed\nprotocol: Kaseikyo\naddress: 80 02 20 00\ncommand: 50 03 00 00\n#\n# Model: Panasonic N2QAYB000752_Full\n#\nname: Ch_prev\ntype: parsed\nprotocol: Kaseikyo\naddress: B0 02 20 00\ncommand: 50 03 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: Kaseikyo\naddress: B0 02 20 00\ncommand: 40 03 00 00\n#\n# Model: Panasonic TH-43HS550K\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 0C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 14 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 15 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 12 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 3E 00 00 00\ncommand: 0D 00 00 00\n#\n# Model: Philips 22IT_TV_Monitor\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 01 FE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 10 EF 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 18 E7 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 1C E3 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 BD 00 00\ncommand: 04 FB 00 00\n#\n# Model: Philips 32PFL4208T\n#\nname: Ch_next\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 4C 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC6\naddress: 00 00 00 00\ncommand: 4D 00 00 00\n#\n# Model: Philips TV_14PV172_08\n#\nname: Ch_next\ntype: parsed\nprotocol: RC5X\naddress: 00 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: RC5X\naddress: 00 00 00 00\ncommand: 11 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: RC5X\naddress: 00 00 00 00\ncommand: 16 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: RC5X\naddress: 00 00 00 00\ncommand: 15 00 00 00\n#\n# Model: Pioneer Kuro_PDP_LX508A\n#\nname: Power\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 0A 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 0B 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Pioneer\naddress: AA 00 00 00\ncommand: 49 00 00 00\n#\n# Model: Samsung\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 10 00 00 00\n#\n# Model: Samsung AA59-00741A\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 12 00 00 00\n#\n# Model: Samsung BN59-01180A\n#\nname: Power\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 98 00 00 00\n#\n# Model: Samsung Broadband_Hospitality\n#\nname: Ch_next\ntype: parsed\nprotocol: SIRC20\naddress: 5A 0E 00 00\ncommand: 10 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: SIRC20\naddress: 5A 0E 00 00\ncommand: 11 00 00 00\n#\n# Model: Sanyo DP26640\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 0A 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 0B 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 0E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 0F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 38 00 00 00\ncommand: 18 00 00 00\n#\n# Model: Sceptre 8142026670003C\n#\nname: Ch_next\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 14 00 00 00\n#\n# Model: Sharp g0684cesa_NES_TV\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 0C 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 0D 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 0E 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 0F 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 28 00 00 00\ncommand: 10 00 00 00\n#\n# Model: Silver LE410004\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 00 F7 00 00\ncommand: 0C F3 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 F7 00 00\ncommand: 10 EF 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 F7 00 00\ncommand: 0E F1 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 F7 00 00\ncommand: 11 EE 00 00\n#\n# Model: Soniq QSP500TV6\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 10 EF 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 11 EE 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 58 A7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 5B A4 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 5E A1 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 72 DD 00 00\ncommand: 56 A9 00 00\n#\n# Model: Sony RM-V310\n#\nname: Vol_up\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 12 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 13 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: SIRC\naddress: 10 00 00 00\ncommand: 14 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: SIRC\naddress: 0D 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: SIRC\naddress: 0D 00 00 00\ncommand: 11 00 00 00\n#\n# Model: Sony XBR\n#\nname: Power\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 2E 00 00 00\n#\n# Model: Strong RCU-Z400N\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 1C 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: A0 00 00 00\ncommand: 5F 00 00 00\n#\n# Model: Sunbrite\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 1F 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 1E 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 02 00 00 00\ncommand: 1C 00 00 00\n#\n# Model: SWEEX Generic_Monitor\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\n# Model: TCL Roku_TV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 97 68 00 00\n#\n# Model: Vitec Exterity_IPTV\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: B5 4A 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: BA 45 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: BB 44 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: B0 4F 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: B1 4E 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: AD ED 00 00\ncommand: C5 3A 00 00\n#\n# Model: AKAI ATE_22Y604W\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9292 4518 635 522 635 522 635 523 634 523 662 494 664 494 663 1625 661 519 662 1602 660 1626 660 1650 636 1650 636 1651 635 1651 635 522 634 1653 633 524 633 1655 631 526 631 527 630 1657 630 527 630 527 630 527 630 1658 630 527 630 1658 630 1658 630 527 631 1658 629 1658 629 1658 629 40773 9295 2239 630 98164 9297 2241 630\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9319 4500 657 499 658 498 660 498 659 500 658 498 660 499 688 1600 685 472 634 1654 634 1680 607 1681 607 1681 607 1681 607 1680 608 549 609 1680 608 550 608 1681 607 550 607 1682 631 1657 631 527 631 527 631 527 631 1658 631 527 631 1659 630 527 631 528 630 1659 630 1659 630 1658 631 40798 9302 2242 630 98247 9298 2243 631 98266 9301 2243 630\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9335 4533 632 501 659 499 660 499 660 500 660 501 658 500 689 1604 685 474 635 1683 608 1659 632 1683 608 1684 607 1684 608 1684 608 551 609 1683 609 551 609 1684 608 1684 608 1685 607 1685 632 528 632 528 632 528 632 1661 631 529 631 529 631 529 631 529 631 1662 631 1662 631 1662 631 40877 9313 2247 631 98424 9314 2248 631 98437 9314 2247 632\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9305 4503 660 498 690 470 688 470 687 473 635 524 635 524 635 1657 634 525 663 1629 662 1630 659 1655 609 1682 609 1682 609 1682 633 525 635 1657 634 525 634 525 634 526 633 527 632 1660 631 528 632 528 631 528 631 1661 631 1661 631 1661 631 1660 631 528 631 1661 631 1661 630 1661 630 40849 9309 2243 631 98361 9313 2244 631\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9338 4507 658 500 689 470 688 471 636 525 635 525 635 524 636 1658 635 525 663 1655 637 1630 661 1655 635 1657 635 1657 636 1657 635 524 635 1658 635 1658 634 1658 634 526 634 1660 632 1661 632 528 632 528 632 528 632 528 632 528 632 1662 631 528 632 528 632 1662 631 1662 631 1662 631 40877 9317 2245 631 98426 9319 2246 631\n#\n# Model: Brandt B3230HD_TV\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9344 4504 663 494 666 494 666 495 665 496 663 521 638 522 637 1634 660 522 638 1633 660 1655 638 1656 636 1656 636 1657 636 1658 635 526 633 1661 632 1662 631 1662 631 1663 631 1663 631 1662 632 530 631 530 631 530 630 530 631 530 631 530 631 530 631 530 631 1663 631 1664 630 1663 631 40893 9321 2247 631 98484 9323 2247 632\n#\n# Model: BUSH TV_VL32HDLED\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9028 4480 593 1667 589 541 597 532 596 534 594 562 566 564 564 539 589 567 571 558 570 1663 594 1666 591 1696 571 1662 595 1666 591 1669 598 1662 595 562 566 563 565 539 589 567 571 1661 596 561 567 563 565 564 564 1669 598 1662 595 1691 566 1668 599 558 570 1663 594 1692 565 575 1669\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9200 4445 656 510 628 511 626 1616 626 514 623 542 597 542 597 542 597 542 597 1648 596 1648 596 543 596 1648 596 1648 596 1648 596 1648 596 1648 596 543 596 543 595 1649 595 1648 596 543 595 544 596 543 594 545 596 1648 595 1649 593 545 595 543 595 1650 595 1648 595 1649 595 1649 595 39850 9193 2218 595 95963 9218 2218 596 95989 9193 2219 595\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9226 4417 657 508 628 511 625 1617 624 515 622 517 622 517 621 517 622 517 622 1622 621 1623 620 518 621 1622 622 1623 620 1623 621 1623 621 1623 620 518 621 518 620 518 621 1623 621 1623 619 519 620 518 620 518 621 1624 620 1623 595 1649 619 519 620 519 619 1625 619 1648 596 1625 594 39874 9194 2192 620 95968 9197 2242 571\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9205 4446 630 536 628 511 627 1616 626 539 598 542 597 542 597 542 597 542 597 1647 597 1647 597 542 597 1647 597 1648 596 1648 596 1648 596 1648 596 1648 596 1648 596 542 597 1648 596 542 597 542 597 543 597 542 597 542 597 543 597 1648 596 543 596 1648 596 1648 596 1648 596 1648 596 39850 9202 2218 596 95967 9230 2217 597\n#\n# Model: ContinentalEdison\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9209 4446 655 512 628 510 628 1616 626 515 622 542 597 542 597 542 597 542 597 1648 596 1648 597 542 597 1647 597 1647 597 1647 597 1647 597 1647 597 1648 596 542 597 1648 596 542 597 1648 597 542 597 542 597 542 597 542 597 1648 596 543 596 1648 597 543 596 1648 596 1648 597 1648 596 39847 9203 2218 596 95986 9203 2218 596 95964 9230 2218 596 95965 9232 2218 596\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9225 4476 586 1675 613 527 612 529 610 530 609 532 607 558 581 558 582 559 581 559 581 1681 581 1681 581 1681 581 1681 581 1681 581 1681 581 1681 581 1683 581 1681 581 1681 581 1681 581 559 581 559 581 558 582 559 580 559 581 559 581 559 581 559 581 1681 581 1681 581 1682 580 1682 581\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 337 1695 339 664 340 664 339 665 338 664 339 693 310 1695 339 1696 338 693 310 1697 337 666 337 693 310 693 310 1723 311 692 311 44457 309 1723 310 692 311 692 311 692 311 693 310 1723 310 694 334 669 334 1700 333 672 331 1703 330 1727 306 1704 330 697 306 1727 306 42378 309 1724 333 670 333 670 333 672 331 673 330 673 330 1704 329 1704 329 674 329 1727 306 698 305 698 305 698 305 1728 305 698 305 44436 309 1724 334 670 333 671 332 672 331 697 306 1728 306 674 329 697 306 1728 306 698 306 1728 306 1727 306 1728 305 698 305 1727 306 42378 309 1724 334 669 334 671 332 672 330 673 330 697 306 1703 330 1727 306 697 306 1727 306 697 306 698 305 697 306 1728 305 697 306 44431 309 1724 334 670 333 670 333 672 331 696 306 1727 306 697 306 697 306 1727 306 697 306 1727 306 1727 306 1727 306 697 306 1728 305 42373 309 1724 334 670 333 670 333 671 332 697 306 697 306 1727 305 1727 306 697 306 1728 305 697 306 697 306 697 306 1727 306 697 306 44427 309 1724 334 669 334 670 333 672 331 697 306 1728 306 697 306 697 306 1727 306 697 306 1727 306 1727 306 1727 306 697 306 1728 305\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 336 1697 336 667 336 668 335 668 336 667 337 1696 338 1696 337 1696 337 667 336 1697 337 668 335 668 335 669 334 1699 334 693 309 43422 335 1723 310 694 309 694 309 695 333 671 332 672 331 673 331 673 331 1704 330 673 330 1703 331 1704 330 1703 331 673 331 1704 330 43424 309 1725 308 695 308 696 332 672 332 673 331 1703 331 1703 330 1703 331 673 331 1703 331 673 331 673 330 673 331 1703 331 673 331 43418 308 1725 308 695 333 671 332 672 331 672 331 673 331 673 330 673 330 1703 331 673 331 1703 331 1704 330 1703 331 673 330 1703 331 43418 309 1725 308 695 308 696 332 672 331 673 330 1703 331 1703 331 1703 331 673 331 1703 330 673 330 673 330 673 331 1703 331 673 331 43419 308 1725 308 695 308 696 307 697 331 673 331 672 331 672 331 673 330 1703 331 673 330 1703 331 1703 331 1703 331 673 331 1703 331 43419 309 1725 308 695 308 696 332 672 331 673 330 1703 331 1703 331 1703 331 673 330 1704 330 673 330 673 330 673 330 1703 331 673 330 43424 309 1725 309 695 333 671 332 672 331 672 331 673 331 673 330 673 331 1703 331 673 331 1703 331 1704 330 1703 331 672 332 1703 331\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 310 1696 337 664 339 693 310 693 310 665 338 666 337 665 338 1696 338 665 338 1723 310 666 337 693 310 693 310 1723 310 693 310 45493 309 1724 309 693 310 693 310 692 311 692 311 1722 311 1723 310 693 310 1724 334 669 334 1700 331 1701 308 1727 331 673 330 1703 330 41342 333 1699 334 670 333 670 333 671 332 672 331 671 332 672 331 1702 331 671 332 1702 331 672 331 672 331 672 331 1702 331 672 331 45459 333 1699 334 670 333 671 332 696 306 673 331 1701 332 1702 331 672 331 1702 331 672 331 1702 331 1702 331 1701 332 672 331 1702 331\n#\n# Model: Grandin\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 337 1698 336 667 336 665 338 666 337 665 392 1644 337 666 338 1696 338 666 338 1723 310 667 337 667 336 693 310 1723 311 692 311 44474 310 1723 310 692 311 692 311 693 310 693 310 694 309 1725 334 670 333 1701 332 672 332 1702 331 1702 332 1702 331 672 332 1702 331\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 970 719 972 718 1822 718 944 747 920 771 921 799 893 802 893 799 893 1644 893 799 1767 770 918 86138 893 798 894 798 1768 772 915 778 913 779 913 804 888 807 888 804 888 1627 911 805 1737 801 888 86143 893 798 918 774 1766 775 913 780 912 780 912 779 913 783 912 780 912 1625 913 780 1763 777 912 86141 892 798 918 774 1766 776 912 780 911 780 912 780 912 783 912 780 912 1626 912 780 1762 801 888 86137 892 799 917 774 1766 775 913 780 912 780 912 779 913 783 912 781 911 1650 888 805 1738 801 887 86148 892 799 918 775 1765 800 888 804 888 804 888 804 888 808 888 804 888 1651 888 805 1738 801 888 86133 944 772 919 773 1767 774 914 778 914 778 914 778 914 781 915 777 915 1624 914 778 1766 774 914\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 972 719 1823 716 973 719 972 720 945 747 920 772 920 802 893 1645 1743 796 918 773 918 774 916 86148 917 774 1767 772 916 777 914 778 913 779 913 779 913 783 913 1625 1763 776 913 779 913 779 913 86148 918 773 1767 773 915 778 914 779 913 779 913 780 912 783 913 1625 1764 777 912 780 912 780 912\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 946 745 947 745 1850 688 945 746 944 748 893 798 893 802 893 1644 1743 794 894 797 894 1643 918 85284 892 798 894 798 1769 769 918 775 915 778 914 778 914 782 913 1649 1739 775 913 778 914 1625 913 85274 943 771 919 773 1766 773 915 778 914 777 915 777 915 781 914 1623 1765 774 914 778 914 1623 914\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 972 719 1822 715 972 717 973 719 970 721 919 772 919 1647 1743 794 894 797 894 797 918 773 918 86132 892 798 1768 770 918 774 916 778 913 803 889 804 888 1653 1739 800 889 804 888 804 888 804 888 86144 918 773 1769 771 915 777 915 777 915 778 914 778 914 1627 1765 774 914 777 915 777 915 778 914\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 975 746 946 717 1879 660 974 719 972 721 920 798 894 1648 1744 794 894 798 894 797 894 1644 918 85281 920 773 945 749 1793 769 917 775 916 777 915 777 915 1628 1765 774 915 778 914 778 914 1624 914 85282 943 772 919 773 1767 774 914 778 914 778 914 778 914 1627 1766 774 914 778 914 778 914 1624 914 85311 919 773 919 774 1766 773 915 778 914 778 914 778 914 1627 1766 774 915 778 914 778 914 1624 914\n#\n# Model: Grundig AndroidTV\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 920 772 920 772 1771 768 920 771 920 772 920 771 948 748 948 743 948 1590 946 746 1795 1613 919 85291 892 799 893 799 1767 772 916 777 914 778 914 779 913 783 913 779 913 1625 913 780 1762 1623 912\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 872 800 873 797 1723 808 875 798 875 827 873 772 900 799 873 799 873 1614 873 800 1718 816 867 86386 844 828 844 828 1744 786 845 828 845 828 845 828 845 828 844 828 845 1643 844 828 1693 838 844\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 896 775 845 828 1746 785 845 828 846 827 848 826 845 1641 1693 838 844 828 845 828 845 828 845\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 957 716 1725 805 878 826 847 826 846 827 875 797 847 1641 1695 834 874 799 873 800 872 1617 870 85382 847 824 1695 835 873 800 871 803 869 804 868 806 867 1645 1691 841 842 831 842 831 842 1646 842\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 868 804 868 805 1717 812 870 804 896 777 869 803 870 804 869 1618 1744 790 867 805 866 807 866 86344 867 807 866 807 1716 816 868 806 867 805 867 806 868 805 869 1622 1713 841 843 804 867 806 869\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 925 747 1743 787 926 747 925 747 926 746 953 719 872 801 927 1562 1772 785 897 776 894 1594 867 85501 926 747 1771 785 897 776 894 779 867 806 867 807 866 807 866 1622 1713 818 866 807 866 1622 866 85508 926 748 1770 786 869 804 868 806 867 807 866 807 866 807 866 1623 1713 819 865 807 866 1623 866 85517 925 748 1770 786 896 777 868 806 867 807 866 807 866 807 866 1622 1714 818 866 807 866 1623 865\n#\n# Model: GRUNDIG UNKNOWN\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 846 826 847 797 1723 837 846 826 847 826 847 826 846 826 846 826 846 1641 847 827 1717 1631 867 85430 845 827 845 827 1718 813 869 805 867 806 867 808 865 809 864 809 864 1624 864 832 1689 1635 863\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 926 751 1760 756 922 756 921 782 897 782 897 783 897 784 897 784 897 1624 897 784 1736 787 895 85521 921 780 1734 784 896 783 894 785 895 784 895 784 895 785 895 784 896 1625 895 785 1735 787 896 85546 894 783 1732 763 917 783 896 783 896 784 895 784 895 785 894 785 897 1624 897 784 1734 786 897\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 953 724 927 750 1764 755 924 755 924 758 922 782 898 782 899 1624 1737 786 898 784 897 784 898 85552 923 756 921 757 1759 783 898 782 898 782 898 783 897 783 897 1623 1737 785 897 784 897 784 897 85557 927 750 926 753 1762 782 898 782 898 759 921 782 898 783 898 1623 1737 785 898 784 898 783 898\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 952 725 1763 752 927 753 925 756 922 780 899 781 899 782 899 1623 1737 786 897 784 897 1626 895 84746 892 785 1731 787 894 785 894 785 895 786 895 789 891 786 895 1627 1732 789 894 788 893 1628 894 84705 929 748 1766 751 930 750 929 750 929 750 929 752 928 753 927 1593 1766 756 926 756 925 1597 924\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 896 782 895 784 1733 785 896 785 868 812 894 784 920 762 871 812 918 1605 893 787 1735 1629 892 84752 893 784 895 781 1735 762 919 783 895 786 897 783 896 785 896 786 895 1625 897 784 1738 1625 896 84727 916 784 896 781 1736 785 897 783 896 784 896 785 897 784 895 785 897 1624 897 783 1737 1627 897\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 921 753 1759 785 896 782 897 782 897 783 895 784 896 1623 1733 786 895 784 895 784 893 785 895 85568 893 783 1734 786 893 785 895 783 895 785 894 785 895 1622 1733 786 894 784 896 784 895 784 895\n#\n# Model: GuestTek Marriot_Hotel\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 5349 102096 3672 530 1194 102545 5171 102367 969 658 1613 481 1499 103984 503 388 210 337 161\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 48 B7 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 44 BB 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 0A F5 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 06 F9 00 00\n#\n# Model: Haier L42C1180\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 00 7F 00 00\ncommand: 5A A5 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9021 4377 655 452 654 452 654 1567 654 453 653 453 653 453 652 454 651 455 651 1568 654 1569 653 455 651 1570 652 1570 652 1570 652 1571 651 1572 649 480 626 481 624 482 623 1599 623 483 623 484 622 484 622 484 622 1601 621 1601 621 1601 621 484 622 1601 621 1601 621 1601 621 1601 621 39912 8910 2137 622 95435 8933 2137 622 95434 8934 2137 622 95434 8934 2137 622 95434 8934 2137 622 95434 8933 2137 622 95434 8933 2138 621 95436 8932 2138 621 95435 8933 2138 621 95435 8933 2137 622\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8990 4407 626 479 627 479 627 1596 626 479 627 480 681 425 681 425 681 425 681 1541 680 1542 679 428 626 1596 626 1596 626 1596 626 1596 626 1596 626 480 626 1597 625 481 625 482 624 506 600 506 600 506 600 507 599 1624 622 483 623 1599 623 1599 623 1599 623 1600 622 1600 622 1600 622 39912 8909 2138 623 95460 8906 2140 623\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8990 4406 626 481 625 481 625 1595 627 481 625 481 653 453 654 452 654 452 654 1567 654 1568 654 453 653 1568 654 1569 653 1569 653 1569 653 1570 652 1594 627 1595 626 479 626 480 625 481 624 482 624 483 623 483 623 483 623 484 622 1600 622 1600 622 1600 622 1600 622 1600 622 1600 622 39913 8911 2137 622 95438 8934 2136 623\n#\n# Model: Hisense ER22601A\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9020 4375 657 450 656 451 654 1566 656 451 655 451 653 453 626 480 653 452 655 1567 654 1568 654 454 651 1568 654 1568 654 1569 653 1570 651 1572 650 1595 626 479 626 480 625 1597 624 482 624 482 624 483 623 483 623 483 623 1599 623 1599 623 483 623 1599 623 1599 623 1599 623 1599 623 39900 8912 2136 623\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 976 723 1776 796 925 804 897 804 897 803 897 803 897 803 897 803 922 1610 919 783 1767 807 914 86103 921 778 1769 805 916 786 914 788 913 787 914 787 914 788 913 811 890 1617 914 787 1764 832 889 86082 920 778 1768 804 916 786 914 786 914 786 914 786 914 786 915 786 915 1616 915 786 1763 809 913\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 950 750 980 720 1800 770 926 774 926 774 926 775 925 1632 1775 796 922 779 920 782 919 782 919 86232 897 802 923 777 1772 801 918 782 919 783 917 783 918 1613 1769 802 918 783 917 783 918 783 917\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 979 721 1776 794 926 775 925 775 925 775 925 775 925 1606 1800 796 922 779 919 781 919 1612 919 85400 924 776 1772 800 919 781 919 782 918 781 919 782 919 1612 1770 801 919 782 918 782 919 1612 919 85390 923 776 1772 799 919 781 919 781 919 781 919 781 919 1612 1770 801 919 782 918 782 918 1613 918\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 979 720 977 724 1776 795 925 775 924 775 951 750 950 750 950 1582 1796 799 919 781 919 782 919 86138 949 752 946 777 1771 801 919 782 919 782 918 782 918 782 918 1612 1769 802 918 782 918 782 918\n#\n# Model: Hisense K321UW\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 951 749 1832 741 978 721 927 774 926 776 924 776 924 801 899 1632 1773 798 920 782 918 1613 917 85257 898 802 1773 800 919 782 918 782 919 782 919 782 918 782 918 1613 1769 802 918 783 918 1614 917 85260 898 802 1772 800 919 782 919 782 919 782 919 782 918 782 919 1613 1769 802 918 782 918 1613 918\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8510 4237 528 1592 528 1592 528 526 529 526 529 526 529 526 529 526 529 527 528 1591 529 1591 529 1592 528 527 528 1590 530 526 529 526 529 525 528 22533 529 1590 529 1592 528 526 529 526 529 526 529 526 529 526 529 526 529 1592 528 1591 555 1564 556 500 555 1565 554 500 529 526 529 524 529\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8507 4232 528 1591 528 1590 529 527 528 527 527 526 528 527 528 526 529 525 530 526 529 1591 528 1591 528 1590 529 1591 528 528 527 527 527 525 528 22529 525 1592 527 1592 526 528 527 527 528 529 525 527 528 527 527 527 528 527 527 1592 527 1593 526 1592 527 1592 527 527 528 554 500 527 526\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8505 4231 528 1593 526 1595 524 525 529 526 529 526 528 525 529 527 527 526 528 1590 529 1590 529 1590 529 1590 529 1591 528 527 527 527 527 525 528 21460 528 1590 529 1590 529 525 529 526 528 526 528 526 528 526 528 527 527 1592 527 1592 527 1591 528 1591 528 1591 528 526 528 526 528 525 528\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8506 4232 528 1591 528 1591 528 526 528 526 528 528 526 526 528 527 527 526 528 526 528 526 528 1591 528 1592 527 1591 528 525 529 526 528 526 527 23593 528 1591 528 1592 527 526 528 526 528 526 528 526 529 527 527 528 526 525 529 527 527 1591 528 1591 528 1591 528 526 528 526 528 526 527\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8505 4231 528 1591 528 1591 527 527 527 526 528 526 552 503 527 528 526 526 528 1591 528 527 527 526 529 1592 527 1592 527 525 529 527 527 526 527 23590 527 1590 528 1590 528 525 529 526 528 526 528 526 528 526 528 526 528 1590 529 526 528 526 528 1590 529 1591 528 526 528 526 528 523 530\n#\n# Model: Kendo CP20M36VT\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8505 4233 526 1591 528 1591 528 526 528 526 528 527 527 527 527 526 528 528 526 528 526 526 528 525 529 1591 528 1591 528 527 527 526 528 524 529 24651 528 1595 524 1591 527 526 528 528 526 526 528 526 528 528 526 527 527 526 528 526 528 527 527 1592 527 1591 527 527 527 528 526 525 552\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9121 4377 685 475 658 476 656 1610 656 479 654 482 651 483 651 483 651 483 651 1618 650 1618 650 506 627 1640 628 1641 627 1641 627 1640 628 1641 627 1641 627 1641 627 506 627 506 628 507 627 506 627 507 627 507 626 507 627 507 627 1641 626 1641 627 1641 627 1641 627 1641 627 1641 627 39937 9096 2169 651\n#\n# Model: LG OLED48C37LA (LG_OLED C3 models)\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9097 4402 685 475 659 475 658 1609 657 477 656 480 653 480 654 480 653 481 653 1615 653 1614 654 481 653 1615 653 1615 653 1615 653 1615 653 1615 653 1615 653 481 652 481 653 1616 652 482 652 482 652 482 652 482 652 482 652 1616 652 1616 651 482 652 1617 651 1617 651 1640 628 1640 628 39937 9097 2167 652\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 09 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 02 40 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 03 40 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 01 00 00 00\n#\n# Model: Manta\n#\nname: Ch_next\ntype: parsed\nprotocol: NECext\naddress: 83 7A 00 00\ncommand: 00 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8073 3997 524 502 495 505 492 1508 498 503 494 1505 501 1500 495 1504 491 1510 496 3988 522 502 495 1505 501 501 496 504 493 1507 499 502 495 1505 501 501 496 18806 8072 3997 524 502 495 505 492 1507 499 502 495 1505 490 1509 497 1504 491 1510 496 3988 522 502 495 1505 501 500 497 503 494 1506 500 501 496 1504 491 510 498 18806 8072 3998 523 503 494 506 491 1509 497 504 493 1506 499 1501 494 1506 500 1502 493 3989 522 504 493 1507 499 502 495 505 492 1508 498 503 494 1506 499 502 495 18807 8072 3998 523 503 494 506 491 1509 497 504 493 1506 500 1501 494 1506 500 1502 493 3989 521 503 494 1506 500 502 495 505 492 1508 498 503 494 1506 500 502 495 18807 8072 3998 523 502 495 505 492 1508 498 503 494 1505 501 1500 495 1504 491 1510 496 3988 523 502 495 1505 501 501 496 503 494 1506 500 501 496 1504 491 510 498\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8069 3998 522 503 494 506 491 1509 496 505 492 1507 498 1501 494 1506 499 1502 493 3989 521 1503 492 1508 497 1503 492 508 500 1501 494 506 491 510 498 504 493 17810 8067 4003 517 508 500 501 496 1504 491 510 498 1502 493 1507 498 1501 494 1508 497 3984 526 1474 521 1504 501 1500 495 505 492 1509 496 505 492 508 500 502 495 17809 8069 4000 520 506 491 509 499 1501 494 507 501 1499 496 1503 492 1508 497 1504 491 3991 519 1480 525 1500 495 1505 500 500 497 1503 492 509 499 503 494 507 490 17809 8069 3999 521 505 492 508 500 1500 495 506 491 1508 497 1502 493 1507 498 1503 492 3990 520 1504 491 1509 496 1504 491 509 499 1501 494 507 501 501 496 505 492 17808 8070 3998 523 503 494 506 491 1509 496 504 493 1507 498 1501 494 1506 499 1502 493 3988 522 1502 493 1507 498 1502 493 507 501 1500 495 505 492 509 499 502 495\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8066 4002 519 507 501 500 497 1503 492 508 500 1500 495 1480 525 1475 520 1506 499 3983 517 508 500 1500 495 1481 524 501 496 1504 491 510 498 503 494 507 501 18803 8073 3997 524 503 494 506 491 1483 522 504 493 1506 499 1476 519 1482 523 1478 517 3989 521 504 493 1482 523 1478 517 508 500 1476 519 507 501 500 497 505 492 18809 8066 4003 517 508 500 501 496 1503 492 509 499 1501 494 1480 525 1475 520 1481 524 3983 516 509 499 1501 494 1482 523 502 495 1505 500 500 497 504 493 508 500\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8069 4000 520 480 517 508 500 1500 495 505 492 1508 497 1502 493 1506 499 1502 493 3989 521 1503 492 1509 496 503 494 1506 499 1501 494 506 491 510 498 504 493 17807 8072 3997 524 501 496 505 492 1508 497 502 495 1505 500 1499 496 1504 491 1510 495 3986 524 1500 495 1506 499 500 497 1503 492 1508 497 503 494 507 501 501 496 17804 8064 4004 517 509 499 501 496 1504 491 509 499 1500 495 1505 500 1499 496 1505 500 3980 520 1505 500 1500 495 505 492 1508 497 1502 493 508 500 501 496 505 492 17807 8072 3995 526 500 497 503 494 1506 499 501 496 1504 491 1508 497 1503 492 1509 496 3985 515 1509 496 1503 492 508 500 1500 495 1506 499 501 496 505 492 509 499 17803 8065 4003 518 508 500 501 496 1504 491 509 499 1502 493 1507 498 1502 493 1508 497 3985 525 1500 495 1505 500 500 497 1503 492 1508 497 504 493 508 500 501 496\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8064 4006 525 501 496 504 493 1507 498 502 495 1505 500 1499 496 1504 501 1500 495 3987 523 1501 494 1507 498 502 495 505 492 1508 497 1503 492 509 499 503 494 17808 8069 4000 520 505 492 508 500 1500 495 506 491 1508 497 1503 492 1508 497 1504 491 3991 519 1505 500 1501 494 507 501 500 497 1502 493 1508 497 503 494 508 500 17803 8064 4006 525 501 496 504 493 1507 498 503 494 1505 500 1500 495 1505 500 1500 495 3988 522 1502 493 1507 498 503 494 506 491 1508 497 1503 492 509 499 503 494\n#\n# Model: NEC E425\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8068 3999 521 504 493 507 501 1498 497 504 493 1505 500 1475 520 1479 526 1500 495 3987 523 502 495 1505 500 501 496 504 493 1506 499 1501 494 507 501 500 497 18802 8072 3996 524 502 495 505 492 1507 498 503 494 1505 500 1475 520 1480 525 1476 519 3987 523 501 496 1504 491 510 498 502 495 1504 501 1475 520 505 492 509 498 18798 8065 4001 519 507 501 499 498 1502 493 507 501 1499 496 1504 501 1473 522 1504 501 3981 518 506 491 1509 496 505 492 508 499 1500 495 1505 500 500 497 505 492 18808 8065 4001 519 507 501 500 497 1503 492 508 499 1500 495 1505 500 1499 496 1506 499 3983 516 508 499 1501 494 507 501 500 497 1502 493 1508 497 504 493 508 500\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8953 4403 601 417 701 516 602 515 602 411 706 515 603 515 575 1635 603 514 628 1553 628 1609 600 1583 627 1606 598 1610 601 1608 600 495 624 1607 599 516 603 515 601 517 572 547 599 1608 599 518 601 516 598 545 573 1610 573 1637 572 1636 597 1612 599 518 600 1610 598 1610 599 1611 598 39250 8972 2171 599 94711 8976 2167 602 94731 8955 2168 602 94737 8927 2198 598\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3554 1678 495 403 468 1248 495 403 497 374 497 375 497 374 497 374 497 375 496 375 496 375 495 377 493 378 492 380 490 1254 489 383 488 383 489 383 488 383 488 383 488 383 489 383 488 383 489 383 488 1255 488 383 488 384 488 383 488 384 487 384 487 384 487 384 488 384 488 384 487 1256 487 384 488 384 487 1256 488 1256 488 384 487 384 487 384 488 1256 487 384 488 384 487 1257 487 1257 487 385 486 1257 487\n#\n# Model: Panasonic N2QAYA_152\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3526 1678 495 403 468 1248 496 403 496 375 496 375 497 375 496 375 496 376 495 376 495 376 495 377 493 378 492 380 490 1254 488 384 488 383 488 384 488 384 487 384 487 384 487 384 488 384 487 384 488 1257 486 385 486 385 486 385 487 385 486 386 486 409 462 409 462 410 461 410 462 410 461 1282 462 409 462 1282 462 1282 461 410 462 410 461 410 461 410 462 1282 461 410 462 1282 461 1282 462 410 461 1282 462\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3519 1775 426 448 422 1332 418 455 425 449 421 459 421 452 418 456 424 450 420 459 421 452 418 456 424 450 420 460 420 1339 422 458 422 451 419 455 425 449 421 459 421 452 418 455 425 449 421 459 421 1339 422 457 423 451 419 454 426 448 422 458 422 451 419 454 426 448 422 1331 419 454 426 1327 423 450 420 1307 454 1299 451 449 421 453 427 1326 424 449 421 1333 417 456 424 1329 421 1304 446 454 426 1327 423\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3516 1747 444 456 424 1329 421 453 427 446 424 451 419 460 420 454 426 447 423 451 419 461 419 454 426 447 423 451 419 1334 427 448 422 458 422 451 419 455 425 450 420 459 421 453 417 456 424 450 420 1333 417 457 423 456 424 449 421 453 427 447 423 457 423 450 420 454 426 1328 422 451 419 456 424 455 425 448 422 1331 419 455 425 448 422 1332 418 455 425 449 421 459 421 452 418 1336 425 449 421 1332 418\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3482 1730 448 425 450 1296 444 429 446 427 448 424 451 422 453 420 445 428 447 425 450 423 452 421 444 429 446 426 449 1297 454 419 446 427 448 425 450 423 452 420 445 428 447 426 449 424 451 421 444 1303 448 425 450 422 453 420 445 428 447 425 450 423 452 421 444 429 446 427 448 424 451 422 453 419 446 428 447 1298 453 420 445 428 447 426 449 424 451 421 444 429 446 427 448 1298 453 420 445 1301 450\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3512 1700 478 395 480 1266 474 399 476 396 479 394 481 392 473 400 475 397 478 395 480 393 482 390 475 398 477 396 479 1267 473 399 476 397 478 395 480 392 473 400 475 398 477 396 479 394 481 391 474 1272 479 394 481 392 473 400 475 398 477 395 480 393 482 391 474 399 476 1269 482 391 474 399 476 397 478 395 480 1266 474 398 477 396 479 1267 473 399 476 397 478 394 481 392 473 1273 478 395 480 1266 474\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3596 1604 513 388 429 1282 459 442 428 442 428 442 428 442 428 442 428 441 429 441 429 442 428 442 428 443 426 445 449 1289 452 446 423 447 423 448 422 448 422 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 448 422 448 422 448 422 448 422 448 422 448 423 448 422 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 449 422 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 1320 421\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3480 1715 457 441 429 1284 457 442 428 442 428 442 428 442 428 442 428 441 429 442 428 442 453 417 453 417 453 418 451 1289 451 421 449 447 423 447 423 447 423 447 423 448 422 448 422 448 423 447 423 1318 423 447 423 448 422 448 423 447 423 448 423 448 422 448 422 448 422 1319 422 448 422 448 422 448 423 448 422 1319 422 448 423 448 422 1319 422 448 422 448 422 448 422 448 422 1319 422 448 423 1319 422\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3537 1660 458 441 429 1311 430 442 428 442 428 442 428 442 428 442 428 442 428 442 428 442 428 442 453 417 453 417 453 1287 453 420 449 422 447 447 423 448 422 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 447 423 448 422 448 422 448 422 448 422 448 422 448 423 448 422 1319 422 448 422 448 423 1319 422 1319 423 448 422 448 422 448 422 1319 422 448 422 448 422 1319 422 1319 422 448 422 1319 422\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3481 1715 457 441 429 1311 430 442 429 442 428 442 428 442 428 442 428 441 429 442 428 442 453 417 453 417 452 419 450 1289 451 422 447 447 423 447 423 447 423 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 448 422 448 422 448 422 448 423 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 1319 422 1319 422 448 422 448 422 448 422 448 422 1319 422 448 422 1319 422 1319 422 448 422 1319 422\n#\n# Model: Panasonic TC-P50S2\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3505 1690 483 416 454 1258 483 416 454 416 454 417 428 442 428 442 428 441 455 416 454 416 454 416 454 417 452 419 450 1289 451 421 448 423 447 448 422 448 422 448 422 448 422 448 422 448 422 448 422 1319 422 448 422 448 422 448 422 448 423 448 422 448 423 448 422 448 422 1319 422 448 422 1319 422 448 422 1319 422 1319 422 448 422 448 423 1319 422 448 422 1319 422 448 422 1319 422 1320 421 449 421 1319 422\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 228 144285 3545 1690 497 411 495 1256 496 382 465 410 466 410 493 383 493 386 489 411 464 413 462 415 460 417 459 417 459 418 459 1295 458 418 459 418 459 418 458 418 458 418 459 418 458 418 459 418 459 418 459 1295 459 418 459 418 458 418 459 418 458 418 458 418 458 419 458 418 458 419 458 419 458 418 458 419 458 419 458 1296 458 418 458 418 458 419 457 419 458 419 457 419 458 419 458 1296 457 419 457 1296 458\n#\n# Model: Panasonic Unknown_Full\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3404 1652 462 422 432 1230 457 427 437 420 434 424 440 417 436 420 434 424 440 417 436 421 433 424 440 417 436 421 432 1228 459 425 439 419 434 422 432 426 438 419 434 422 431 426 438 419 435 422 431 1230 457 426 438 420 433 423 431 426 438 420 433 423 430 427 437 420 434 1228 459 424 440 1221 466 418 435 1225 462 1226 461 422 432 426 438 1224 463 420 433 1228 459 399 465 1222 465 1223 464 420 433 1225 462\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 2737 825 494 832 494 390 492 392 1352 1328 461 450 405 450 435 477 297 587 296 644 295 92395 2734 829 491 836 490 394 491 419 1286 1395 296 587 378 1474 239\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 182 7827 172 2332 177 2328 181 2323 176 2330 179 1309 175 1331 174 2331 178 1328 177 2328 181 1307 177 2327 182 1325 180 1326 179 1309 176 1331 174 1332 173 2333 176 2310 178 1328 177 2328 181 1325 180 2306 182 1325 180 2325 174 8340 183 7825 175 2330 179 2326 173 2333 176 2310 178 1328 177 1329 176 2329 180 1308 176 2329 180 1326 179 2326 173 1334 182 1306 179 1328 177 1329 176 1312 183 2322 177 2329 180 1326 179 2325 174 1315 180 2326 173 1333 183 2323 176 8339 183 7824 175 2330 179 2307 181 2324 175 2331 178 1328 177 1330 175 2311 177 1329 176 2329 180 1326 179 2327 182 1305 179 1328 177 1328 177 1311 173 1334 182 2323 176 2330 179 1326 571 1916 180 1327 178 2325 587 920 575 1930 579\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 179 7828 182 2323 176 2328 181 2323 176 2329 180 1308 176 1330 175 2329 180 1326 179 2307 181 2323 176 2329 180 2325 174 1332 173 1315 180 1327 179 1328 177 2327 182 2322 177 1311 173 2332 177 1329 176 1330 175 1312 183 1324 181 8332 180 7826 174 2331 178 2326 173 2313 176 2330 179 1327 178 1328 177 2327 182 1306 179 2326 183 2322 177 2328 181 2323 176 1312 183 1324 181 1325 180 1325 180 2307 181 2323 176 1331 174 2330 179 1327 178 1310 175 1331 174 1332 173 8323 179 7845 176 2311 177 2327 182 2322 177 2328 181 1325 180 1308 177 2328 181 1325 180 2325 174 2330 179 2326 173 2314 174 1332 173 1332 173 1333 183 1306 178 2326 183 2322 177 1329 176 2328 181 1307 177 1329 176 1330 175 1313 182\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 183 7824 176 2329 180 2324 175 2329 180 2324 175 1314 181 1325 180 2324 175 1331 174 2331 178 2307 181 2324 175 1331 174 1331 174 1314 181 1326 179 1326 179 2325 174 2332 177 1310 174 2330 179 1327 178 1328 177 1310 174 2330 179 8334 178 7827 173 2332 177 2327 182 2323 176 2310 178 1328 177 1328 177 2327 182 1306 179 2326 183 2322 177 2327 182 1324 181 1307 177 1329 176 1330 176 1331 174 2311 177 2328 181 1325 180 2324 175 1332 173 1313 182 1325 180 2324 175 8339 173 1383 2522 3925 179 2325 576 1909 590 1915 594 1910 589 918 588 919 576 1911 175 1332 173 2330 592 1912 587 1918 581 907 588 918 598 908 587 920 575 913 592 1914 182 2320 592 914 581 1927 180 1307 178 1327 592 915 580 1925 574\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 176 7830 180 2324 175 2329 180 2324 175 2329 180 1308 176 1329 176 2328 181 1325 180 2305 183 2321 178 1329 177 2327 182 1324 181 1306 178 1328 177 1329 176 2327 182 2304 174 1332 173 2332 177 1328 177 1310 174 2330 179 1327 178 8333 179 7826 174 2330 179 2325 174 2313 175 2329 180 1326 179 1326 179 2325 174 1315 180 2324 175 2329 180 1326 179 2325 174 1314 181 1325 180 1326 179 1308 177 2328 181 2323 176 1330 175 2329 180 1308 176 1330 175 2328 181 1325 180 8314 177 7845 176 2310 178 2327 182 2322 177 2327 182 1323 182 1306 179 2326 173 1333 183 2322 177 2327 182 1305 179 2326 173 1333 183 1323 182 1305 179 1327 178 2326 173 2331 178 1327 178 2308 180 1326 179 1327 178 2325 174 1315 180\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 182 7824 176 2328 181 2323 176 2328 181 2324 175 1312 183 1323 182 2322 177 1330 175 2309 179 2325 174 1332 173 1333 183 1305 179 1327 178 1328 177 1328 177 2308 180 262 177 1885 175 295 175 861 174 2330 179 99 313 911 573 915 590 1916 180 64 349 1910 176 265 174 7896 177 3081 835 3912 182 2322 177 207 179 1942 180 2304 174 268 181 1881 179 263 176 889 177 264 175 886 583 1925 182 271 178 855 180 240 178 1907 174 2331 178 1327 178 1309 176 1329 590 916 589 919 173 306 175 833 181 317 174 1832 175 2329 180 1325 180 309 182 1833 174 1313 182 1325 180 289 181 1853 175 2329 180 8313 178 7845 176 2309 179 2325 174 2331 178 2326 173 1333 183 1304 180 2324 175 1332 173 2330 179 2325 174 1314 181 1325 180 1326 179 1327 178 1308 177 1330 175 2329 180 2324 175 1312 183 2322 177 1329 176 1329 176 2329 180 2304 174 8339 173 351 3575 3902 594 1911 588 1916 593 1911 588 1916 583 904 591 916 589 1915 594 911 584 1920 579 1907 592 915 590 915 591 916 579 908 597 909 596 909 586 1919 580 1907 179 1326 592 1912 597 908 587 901 594 1911 588 1916 593\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9047 4385 682 474 682 1578 708 476 679 1581 706 477 679 1582 705 1582 705 1582 678 1583 679 1607 679 1582 678 478 679 478 678 477 679 1582 705 1582 679 1583 679 1608 704 1582 705 478 678 1582 705 478 678 478 678 478 679 477 679 478 679 478 678 1582 705 478 678 1583 704 1582 705 1582 679 39574 9073 4387 679 478 678 1583 679 503 677 1584 705 478 678 1582 705 1582 705 1582 705 1582 705 1582 705 1582 678 479 677 479 678 479 677 1583 679 1608 704 1582 705 1582 705 1582 705 478 678 1583 704 478 678 478 678 1582 680 478 703 453 704 453 703 1557 703 480 676 1584 704 1583 704 478 678\n#\nname: Ch_next\ntype: parsed\nprotocol: Samsung32\naddress: 05 00 00 00\ncommand: 12 00 00 00\n#\n# Model: Samsung BN59-01081A\n#\nname: Ch_prev\ntype: parsed\nprotocol: Samsung32\naddress: 05 00 00 00\ncommand: 10 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4499 4472 566 1662 565 1664 563 1665 562 565 538 564 539 562 541 560 543 558 545 1683 544 1658 569 1686 541 559 544 557 536 565 538 563 540 561 542 559 544 1684 543 558 545 556 537 564 539 562 541 560 543 558 545 1684 543 558 545 1683 544 1684 543 1660 567 1688 539 1689 538 1664 563 565 538 563 540 561 542 559 544 42973 4495 4472 566 1662 565 1663 564 1664 563 564 539 562 541 560 543 558 545 556 537 1691 536 1691 536 1666 572 555 538 564 539 561 542 559 544 557 536 565 538 1689 538 563 540 560 543 558 545 555 538 563 540 561 542 1686 541 559 544 1684 543 1684 543 1658 569 1685 542 1686 541 1660 567 559 544 557 536 565 538 563 540 42959 4499 4466 562 1666 572 1656 571 1656 571 556 537 564 539 562 541 559 544 556 537 1691 536 1690 537 1664 563 564 539 562 541 560 543 557 536 565 538 563 540 1688 539 561 542 559 544 556 537 564 539 562 541 560 543 1684 543 558 545 1682 545 1683 544 1657 570 1684 543 1659 568 1659 568 559 544 557 536 565 538 563 540 42955 4503 4463 565 1663 564 1663 564 1664 563 563 540 561 542 558 545 556 537 564 539 1688 539 1688 539 1662 565 562 541 559 544 557 536 565 538 563 540 560 543 1685 542 559 544 556 537 564 539 562 541 559 544 557 536 1692 535 565 538 1690 537 1690 537 1664 563 1691 536 1666 572 1656 571 556 537 564 539 562 541 559 544\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4507 4464 564 1690 537 1691 536 1666 572 555 538 538 565 562 541 559 544 557 536 1693 545 1656 571 1657 570 557 536 565 538 563 540 561 542 558 545 1683 544 1657 570 1684 543 558 545 555 538 563 540 561 542 559 544 557 536 565 538 563 540 1688 539 1662 565 1663 564 1663 564 1664 563 564 539 536 567 560 543 558 545 42962 4496 4470 568 1686 541 1660 567 1661 566 560 543 558 545 556 537 564 539 561 542 1686 541 1660 567 1687 540 534 569 558 545 555 538 563 540 561 542 1686 541 1686 541 1686 541 533 570 557 536 565 538 563 540 560 543 558 545 555 538 563 540 1661 566 1687 540 1661 566 1661 566 1662 565 561 542 533 570 557 536 564 539\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4506 4465 563 1692 546 1658 569 1659 568 559 544 557 536 565 538 563 540 561 542 1687 540 1662 565 1689 538 563 540 561 542 560 543 558 545 556 537 1665 562 1693 545 530 563 1692 546 555 538 564 539 562 541 560 543 558 545 556 537 1665 562 539 564 1690 537 1691 536 1692 535 1693 545 556 537 539 564 563 540 561 542 42974 4505 4464 564 1690 537 1665 562 1667 571 530 563 565 538 563 540 561 542 559 544 1684 543 1659 568 1661 566 561 542 559 544 557 536 565 538 563 540 1663 564 1690 537 563 540 1662 565 559 558 545 556 537 564 539 562 541 1688 539 561 542 1687 540 1688 539 1688 539 1663 564 563 540 561 542 559 544 557 536\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4498 4471 567 1661 566 1662 565 1664 563 564 539 563 540 561 542 559 544 557 536 1693 545 1684 543 1659 568 559 544 557 536 566 537 564 539 562 541 560 543 1686 541 560 543 558 545 1684 543 558 545 556 537 564 539 1690 537 564 539 1689 538 1690 537 564 539 1689 538 1665 562 1666 572 556 537 564 539 563 540 560 543 42964 4504 4464 564 1690 537 1665 562 1666 572 555 538 564 539 562 541 560 543 558 545 1683 544 1684 543 1659 568 558 545 556 537 565 538 563 540 560 543 558 545 1683 544 556 537 565 538 1690 537 564 539 561 542 560 543 1685 542 558 545 1683 544 1657 570 557 536 1692 546 1683 544 1684 543 557 536 566 537 563 540 561 542\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4506 4464 564 1691 536 1692 546 1656 571 556 537 564 539 562 541 560 543 558 545 1683 544 1684 543 1685 542 558 545 556 537 565 538 563 540 561 542 558 545 556 537 564 539 562 541 1687 540 561 542 558 545 556 537 1691 536 1665 562 1692 546 1656 571 556 537 1691 536 1692 546 1683 544 557 536 565 538 563 540 560 543 42966 4502 4466 562 1693 545 1683 544 1684 543 558 545 556 537 564 539 561 542 559 544 1684 543 1684 543 1658 569 558 545 530 563 564 539 562 541 560 543 558 535 566 537 564 539 562 541 1687 540 560 543 558 545 555 538 1691 536 1665 562 1693 545 1657 570 557 536 1666 572 1683 544 1658 569 557 546 529 564 563 540 561 542\n#\n# Model: Samsung LE37S71B\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4497 4474 564 1690 537 1691 536 1666 572 556 537 564 539 562 541 560 543 558 545 1683 544 1683 544 1684 543 532 571 556 537 564 539 562 541 560 543 1659 568 1686 541 1661 566 1662 565 562 541 560 543 558 535 565 538 563 540 561 542 558 545 556 537 1665 562 1666 572 1656 571 1684 543 557 546 555 538 564 539 562 541 42966 4502 4466 562 1692 535 1693 545 1657 570 556 537 565 538 562 541 560 543 543 1656 571 1656 571 1657 570 557 536 565 538 563 540 560 543 558 545 1683 544 1683 544 1657 570 1684 543 531 562 565 538 563 540 560 543 558 545 556 537 564 539 561 542 1660 567 1686 541 1687 540 1662 565 562 541 533 570 557 536 565 538\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4567 4475 732 1555 703 1608 705 1608 705 478 676 460 669 507 651 506 649 508 621 1640 672 1616 673 1639 674 508 648 508 648 508 647 485 644 511 647 508 648 1639 674 508 649 508 647 485 645 511 647 508 648 509 647 1639 674 509 647 1613 673 1641 673 1640 673 1640 672 1617 671 1640 673 48544 4566 4505 648 1639 674 1639 674 1639 647 510 648 508 648 509 648 508 648 508 648 1639 647 1642 673 1639 674 508 648 509 648 508 647 485 645 511 647 509 647 1640 673 509 647 509 646 486 643 511 647 509 647 509 647 1640 673 509 647 1615 672 1640 673 1640 673\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4583 4485 687 1600 690 1623 716 1596 690 493 660 470 634 521 662 495 659 497 658 1629 684 1630 631 1657 682 500 656 500 656 500 656 500 656 475 653 1633 682 1631 682 1631 682 500 629 502 630 526 656 500 656 500 656 500 656 500 657 500 630 1632 682 1631 682 1631 682 1631 654 1633 682 48536 4553 4518 656 1632 682 1631 682 1631 682 500 630 502 629 526 656 500 656 501 656 1631 682 1631 631 1657 656 526 655 501 655 501 655 501 655 501 629 1634 680 1632 681 1632 681 501 629 502 629 527 654 501 655\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4551 4491 683 1630 658 1628 685 1628 685 500 654 499 631 525 657 499 656 500 656 1632 681 1632 652 1635 681 501 655 501 655 501 655 501 655 501 628 1634 680 1632 681 501 655 1632 680 479 626 528 628 528 654 502 654 502 654 502 655 1632 654 504 627 1659 680 1632 681 1633 654 1634 679 48515 4596 4498 654 1634 653 1633 654 1633 628 529 653 502 654 502 654 502 654 502 654 1633 653 1635 679 1633 680 503 653 503 653 479 651 504 627 528 654 1633 680 1633 680 503 627 1634 679 503 653 503 653 503 653 503 653 503 627 504 627 1660 679 503 653 1634 679 1634 652 1636 678 1634 679\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1221 1189 435 588 436 890 433 2388 435 590 434 1489 434 1789 434 1190 434 1188 436 2689 435 1488 435 1190 434 86920 327 929 326 377 327 652 328\n#\n# Model: Samsung Royal_Caribbean\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1195 1216 407 616 408 917 406 2414 410 617 406 1518 405 1815 409 1215 434 590 408 2717 406 1516 408 2417 407 86346 375 881 375 326 378 602 377\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1253 1157 464 560 465 858 460 2364 460 563 462 1462 463 1761 467 1156 465 1159 462 2661 467 1456 469 1155 466 86886 331 925 330 373 328 652 331\n#\n# Model: Samsung TV_1\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 1224 1186 467 556 437 887 441 2382 463 560 433 1490 466 1757 461 1163 458 566 438 2686 463 1460 465 2359 434 86319 301 953 302 402 330 649 334\n#\nname: Vol_dn\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0b 00 00 00\n#\nname: Mute\ntype: parsed\nprotocol: Samsung32\naddress: 07 00 00 00\ncommand: 0f 00 00 00\n#\n# Model: Sencor 25801\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9040 4407 659 463 659 1579 661 461 685 437 658 463 658 463 658 464 657 470 656 1585 654 491 629 1611 628 1613 626 1614 625 1615 625 1615 625 502 625 1615 625 497 625 496 625 1614 625 1615 625 497 625 497 624 503 624 496 625 1615 624 1615 624 497 625 496 625 1615 624 1615 624 1614 624 41051 9036 2175 625\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 792 264 1848 264 1848 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 1848 264 792 264 792 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 792 264 1848 264 792 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 1848 264 792 264 1848 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 792 264 792 264 1848 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 1848 264 1848 264 792 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 1848 264 792 264 1848 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 792 264 1848 264 792 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\n# Model: Sharp Aquos_32BG3E\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 264 1848 264 792 264 792 264 792 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 792 264 792 264 792 264 1848 264 792 264 43560 264 1848 264 792 264 792 264 792 264 792 264 792 264 792 264 792 264 1848 264 792 264 1848 264 1848 264 1848 264 792 264 1848 264 43560\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3381 1656 439 401 438 1239 439 401 438 1240 439 401 438 1239 440 401 438 1239 440 400 439 1238 440 401 438 1239 440 1240 438 426 438 1240 439 400 439 1240 439 1240 438 1240 438 1240 438 401 438 401 438 402 437 1242 436 402 437 1242 437 402 437 403 436 1242 437 402 437 403 436 403 436 403 436 1242 437 1242 437 403 436 1242 437 403 436 403 436 403 436 1242 437 403 436 403 436 403 436 1243 436 403 436 1242 437 1242 437\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3378 1656 440 400 439 1239 440 400 439 1239 439 400 439 1239 440 399 440 1238 440 400 439 1239 521 371 412 1215 464 1215 464 399 440 1239 439 399 440 1239 439 1240 438 1240 438 1241 438 401 438 401 438 402 437 1242 437 402 437 1242 437 402 437 402 437 1242 437 402 437 402 437 402 437 1242 437 1242 437 1242 437 402 437 1242 437 402 437 402 437 402 437 1242 437 402 437 402 437 402 437 402 437 402 437 1242 437 1242 437\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3374 1661 436 403 436 1243 436 404 435 1243 436 404 435 1243 436 403 435 1242 464 376 463 1215 464 378 461 1216 463 1219 460 402 437 1242 436 403 436 1243 435 1244 435 1244 434 1245 433 405 434 406 433 406 433 1246 433 406 433 1246 433 406 433 406 433 1246 433 406 433 406 433 406 433 406 433 406 433 1246 433 406 433 1246 433 406 433 406 433 406 433 1246 433 406 433 406 433 406 433 1246 433 1246 433 1246 433 1246 433\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3516 1521 438 401 438 1240 439 401 438 1240 439 401 438 1239 440 400 439 1238 466 375 465 1213 465 375 522 1156 522 1158 520 372 466 1184 494 372 412 1240 439 1240 438 1241 438 1241 437 402 437 402 437 402 437 1242 437 402 437 1242 437 403 436 402 437 1243 436 403 436 403 436 403 436 1243 436 403 436 1243 436 403 436 1243 436 403 436 403 436 403 436 1243 436 403 436 403 436 403 436 403 436 1243 436 1243 436 1243 436\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3382 1654 467 374 440 1238 465 375 440 1238 441 398 441 1238 466 374 466 1211 442 424 415 1238 441 424 415 1240 465 1238 441 398 441 1238 441 398 441 1238 440 1238 440 1239 439 1240 439 400 439 400 439 401 438 1241 438 401 438 1241 438 401 438 401 438 1241 438 401 438 401 438 401 438 1241 438 401 439 401 438 401 438 1241 438 401 438 401 438 401 438 1241 438 401 438 401 438 401 438 401 438 1241 438 401 438 1241 438\n#\n# Model: Sharp g0684cesa_NES_TV\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3361 1649 443 423 416 1237 442 398 441 1235 444 398 441 1237 442 398 441 1237 442 397 442 1236 443 397 442 1236 443 1237 442 399 440 1236 443 398 441 1263 416 1263 472 1207 471 1207 470 371 414 423 416 423 416 1263 416 423 416 1263 416 423 416 423 416 1262 417 422 417 422 417 422 417 422 417 1262 416 422 417 422 417 1262 416 423 441 398 441 398 441 1238 441 398 441 398 441 399 440 1239 440 399 440 399 440 1238 441\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 278 1811 277 788 246 794 250 764 280 786 248 792 252 1813 275 1815 273 791 253 1812 276 789 255 785 249 791 253 1812 276 789 255 45322 280 1809 279 786 248 766 278 788 246 794 250 1815 273 792 252 788 246 1819 280 785 249 1817 271 1819 280 1810 278 787 247 1818 281 43217 274 1818 270 794 250 764 280 786 248 792 252 788 256 1809 279 1811 277 788 246 1819 280 785 249 766 278 762 272 1819 280 785 248\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 278 1812 276 762 282 758 276 765 279 761 273 1818 281 1809 279 1811 277 762 282 1809 279 760 274 766 278 762 282 1809 279 760 274 44279 276 1813 275 763 281 759 275 766 278 762 272 768 276 764 280 760 274 1817 271 768 276 1815 273 1817 271 1819 280 759 275 1816 272 44276 279 1812 276 763 281 758 276 765 279 761 273 1818 281 1810 278 1811 277 762 272 1819 279 760 274 766 278 762 282 1809 279 760 274\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 272 1817 271 794 250 790 254 786 248 792 252 762 272 794 250 1815 273 792 252 1813 275 790 254 785 249 766 278 1813 275 789 255 46372 273 1817 271 794 250 763 281 785 248 792 252 1813 275 1814 274 791 253 1812 276 789 255 1810 278 1812 276 1813 275 790 254 1811 277 42170 277 1814 274 791 253 787 247 793 251 763 281 759 275 791 253 1812 276 789 255 1810 278 787 247 793 251 789 255 1810 278 787 247\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 275 1814 274 791 253 787 247 793 251 789 255 1810 278 787 247 1818 281 785 249 1816 272 793 251 789 255 785 249 1816 272 766 278 45325 274 1815 273 792 252 762 272 794 250 790 254 786 247 1818 270 794 250 1815 273 792 252 1813 275 1815 273 1816 272 793 251 1814 274 43224 277 1814 274 764 280 786 248 792 252 788 246 1820 279 786 247 1817 271 768 276 1815 273 792 252 761 273 794 250 1815 273 791 253\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 272 1817 271 794 250 790 254 786 248 792 252 1813 275 790 254 759 275 792 252 1813 275 789 255 785 248 792 252 1813 275 789 255 46372 273 1817 271 793 251 763 281 759 275 791 253 787 247 1818 281 1810 278 1812 276 789 255 1809 279 1811 277 1813 275 790 254 1811 277 42169 277 1815 273 792 252 787 247 794 250 789 255 1810 278 787 247 794 250 789 255 1810 278 761 273 793 251 789 255 1810 278 787 247\n#\n# Model: Sharp LC-RC1-16\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 273 1816 272 767 277 789 255 785 249 791 253 787 246 1818 281 785 248 765 279 1812 276 789 255 759 275 791 253 1812 276 789 255 46372 281 1808 280 785 249 791 253 787 247 793 251 1814 274 791 253 1812 276 1814 274 791 253 1812 276 1814 274 1815 273 792 252 1813 275 42172 272 1819 280 785 249 765 279 761 273 768 276 764 280 1811 277 788 246 768 276 1815 273 792 252 788 246 794 250 1815 273 791 253\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 352 1747 353 694 354 694 353 694 354 694 354 693 354 1747 354 1745 355 693 355 1746 354 693 355 691 357 691 356 1745 355 689 356 46388 358 1740 359 689 358 688 360 689 358 689 359 1741 358 688 359 689 359 1741 358 691 356 1743 356 1743 356 1742 357 690 357 1741 356 44286 261 1839 260 786 262 786 262 785 263 785 263 786 262 1838 262 1838 262 786 262 1839 261 786 262 784 264 787 261 1837 263 783 262 46491 261 1839 261 786 262 786 262 786 261 786 262 1839 261 786 262 786 262 1839 261 786 262 1838 262 1838 262 1838 262 786 262 1835 262\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 308 1788 312 735 313 734 313 735 313 736 312 735 313 736 312 1788 312 735 313 1786 314 735 313 735 313 734 314 1787 313 731 314 47491 314 1786 314 734 314 734 314 734 314 732 316 1786 315 1784 316 733 315 1786 315 732 316 1785 315 1787 314 1785 315 733 315 1781 317 43284 314 1784 316 731 317 730 318 732 316 732 316 732 316 731 317 1783 317 732 316 1784 316 733 315 731 317 731 317 1784 317 730 315 47500 313 1785 315 733 315 733 315 733 315 734 314 1787 313 1786 315 733 315 1787 313 733 315 1787 313 1786 314 1787 313 734 314 1783 314\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 357 1739 361 688 360 690 357 688 360 691 356 1741 359 689 359 1741 358 690 358 1742 357 691 357 690 357 693 355 1744 356 689 356 46399 355 1743 357 691 357 691 357 691 357 692 355 692 356 1744 356 692 356 1744 356 692 356 1744 355 1745 355 1745 263 785 263 1835 263 44390 261 1839 262 786 262 787 261 787 261 786 262 1839 261 787 261 1840 261 786 262 1839 262 786 262 785 263 785 263 1839 261 784 261 46497 262 1839 261 786 262 786 262 787 261 786 262 786 262 1838 262 786 262 1840 260 787 261 1839 261 1840 260 1840 260 787 261 1835 263\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 260 1838 262 787 261 786 262 787 261 786 261 1838 262 1838 262 1838 262 786 262 1839 261 786 262 786 262 786 262 1839 261 784 261 45433 261 1839 261 785 263 785 263 785 263 785 263 786 262 786 262 785 263 1838 262 786 262 1839 261 1837 263 1838 262 786 262 1835 263 45436 356 1744 356 691 356 691 356 692 356 691 356 1743 357 1744 356 1745 355 691 356 1745 354 692 356 693 354 691 356 1745 355 691 353 45343 359 1742 358 688 360 688 359 689 359 687 361 688 360 689 359 688 360 1741 359 688 360 1742 358 1739 361 1741 359 689 359 1739 359 45337 286 1813 287 761 287 761 287 761 287 760 288 1813 287 1813 287 1813 287 760 288 1813 287 760 288 760 288 760 288 1812 288 757 288 45413 287 1814 286 761 287 761 287 759 289 760 288 760 288 761 287 760 288 1813 287 762 286 1813 287 1813 287 1813 361 685 288 1810 288\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 356 1743 358 689 359 690 358 689 359 689 359 1741 359 688 360 688 360 688 360 1739 362 687 361 687 361 686 362 1739 361 684 361 47444 287 1812 288 760 288 759 289 760 288 759 288 760 288 1813 287 1812 288 1812 312 736 288 1812 288 1812 288 1813 287 760 288 1810 287 43309 286 1812 288 761 310 736 288 759 289 762 286 1812 312 737 311 736 312 736 312 1790 311 737 311 736 312 736 312 1789 311 734 312 47501 313 1786 314 733 315 734 314 733 315 734 314 733 315 1785 315 1785 315 1786 314 733 315 1785 315 1786 314 1786 314 731 317 1782 316 43279 339 1760 317 731 339 709 316 730 318 733 315 1783 317 732 316 731 317 731 317 1784 316 732 316 731 317 731 317 1785 315 729 316\n#\n# Model: Sharp Roku_TV\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 287 1812 288 760 288 761 287 761 287 761 287 760 288 1814 286 760 288 761 287 1813 287 760 288 760 288 760 288 1813 287 757 312 47498 340 1759 341 707 341 709 315 731 340 708 340 1760 340 707 341 1759 318 1783 317 730 318 1782 318 1783 317 1784 316 731 317 1782 316 43284 314 1787 314 736 312 734 314 734 314 734 314 735 313 1787 314 735 313 734 314 1787 313 735 313 734 314 733 315 1787 314 732 313 47500 285 1813 361 685 363 687 361 687 361 686 362 1739 360 687 361 1740 359 1740 360 689 358 1741 359 1743 356 1746 353 690 357 1741 356\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 195 1833 300 766 280 760 275 790 276 737 309 731 304 1801 301 1804 309 731 304 1801 270 795 282 758 277 762 273 1832 270 769 246 45851 326 1780 302 739 307 785 282 732 303 736 310 1795 307 732 303 763 303 1775 307 733 334 1798 273 1832 270 1810 251 814 273 1780 281 43762 302 1804 309 758 277 737 330 762 284 730 305 734 301 1803 310 1796 306 733 302 1829 273 767 279 734 301 791 275 1804 278 762 253 45870 307 1798 304 763 272 767 279 787 279 760 275 1829 284 730 305 734 301 1804 309 757 278 1827 275 1804 278 1828 274 765 270 1835 278 43740 303 1776 306 787 279 760 275 765 281 759 307 758 277 1775 307 1799 303 736 299 1832 281 759 276 763 304 736 299 1832 281 733 302 45820 306 1800 302 764 282 758 277 788 278 762 284 1821 281 732 303 736 310 1796 307 733 302 1829 273 1806 276 1830 272 767 268 1837 245 43772 302 1778 304 789 277 762 284 756 279 786 249 765 301 1777 336 1770 301 764 282 1824 278 761 274 765 301 738 308 1824 278 761 274\n#\n# Model: Sharp TV2\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 254 1721 360 681 354 738 308 706 329 711 355 1774 307 1772 361 1744 327 687 359 1772 299 742 335 705 330 736 279 1825 298 742 283 44773 384 1721 360 707 308 707 359 733 302 711 335 705 361 704 331 708 338 1766 336 704 331 1773 329 1776 306 1773 360 681 323 1782 331 44726 411 1722 328 686 360 733 302 711 335 705 361 1742 329 1803 330 1749 332 708 327 1777 335 705 330 710 325 741 274 1830 303 737 278 44778 359 1747 355 712 303 711 355 711 335 705 330 709 337 703 363 703 332 1770 332 709 337 1767 335 1771 300 1752 360 733 302 1776 326 44731 355 1751 330 711 355 737 309 705 330 710 336 1793 309 1771 331 1774 307 706 360 1771 300 740 326 714 332 735 280 1798 325 741 274\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 277 1806 274 775 281 776 279 770 275 774 281 768 277 1814 277 1806 274 775 280 1803 277 780 276 773 282 766 279 1831 249 781 274 45962 281 1803 277 771 274 783 273 802 253 770 275 1834 257 801 255 768 277 1807 273 775 280 1811 280 1804 276 1806 274 774 282 1811 280 43887 275 1809 282 767 278 779 276 799 256 766 279 771 274 1843 248 1810 281 767 278 1806 274 782 274 776 279 796 249 1807 273 784 282 45962 279 1804 276 772 273 784 282 768 277 798 247 1836 255 802 253 796 249 1808 283 766 279 1813 278 1805 275 1808 272 776 279 1813 278 43890 282 1801 279 769 276 781 274 775 280 769 276 773 283 1834 257 1801 279 769 276 1808 272 784 282 767 278 772 273 1810 281 776 279\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 282 1801 280 769 276 781 275 774 282 768 277 1805 276 781 275 775 281 769 276 1832 249 809 247 776 280 770 275 1834 247 784 282 47004 273 1811 280 768 277 780 276 773 283 767 278 771 274 1816 275 1809 272 1811 280 768 277 1815 276 1807 274 1809 282 767 278 1813 278 42841 284 1799 282 768 277 780 276 774 282 767 278 1805 276 781 275 774 282 768 277 1806 275 782 274 775 281 769 276 1807 274 783 283\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 273 1810 281 768 277 780 276 799 257 767 278 797 248 1816 275 774 282 768 277 1806 275 808 248 801 255 795 250 1807 274 809 247 46989 278 1805 276 799 246 784 282 767 278 798 247 1835 256 775 281 1803 278 1806 275 773 283 1809 272 1811 280 1803 278 771 274 1817 274 42868 278 1806 275 799 257 775 281 768 277 798 247 776 280 1811 280 770 275 773 283 1801 280 777 279 771 274 801 255 1802 279 778 278\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 282 1801 280 769 276 781 275 775 281 768 277 772 273 784 282 1801 280 770 275 1807 274 784 282 767 278 771 274 1809 282 775 281 47005 282 1801 280 770 275 782 274 776 280 769 276 1807 274 1817 274 775 280 1803 278 771 274 1817 274 1809 282 1801 280 770 275 1815 276 42841 273 1811 280 769 276 781 275 774 282 768 277 772 273 783 273 1811 280 794 251 1806 275 782 274 776 280 769 276 1808 273 783 283\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 277 1805 276 799 246 785 281 768 277 798 247 1810 281 775 281 1804 277 771 274 1810 281 801 255 795 250 772 273 1811 280 776 280 45957 274 1809 272 777 279 778 278 772 273 776 280 769 276 1815 276 799 246 1811 280 769 276 1815 276 1807 274 1809 282 767 278 1813 278 43890 280 1803 278 797 248 783 273 776 280 796 249 1834 247 784 282 1802 279 796 249 1808 273 783 283 793 252 770 275 1808 273 784 282\n#\n# Model: Silver LE410004\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 281 1803 278 771 274 782 274 775 281 769 276 1807 274 1817 274 1835 256 766 280 1804 277 780 276 773 283 767 278 1831 250 780 276 44910 276 1835 256 766 280 777 279 771 275 774 282 767 278 779 277 773 283 1800 281 768 277 1814 277 1806 275 1808 283 766 279 1811 280 44937 280 1803 278 771 274 783 283 766 279 770 275 1808 273 1819 272 1811 280 768 277 1807 274 782 274 776 280 769 276 1833 248 784 282\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8958 4449 510 4475 515 4444 515 2213 508 4477 513 2215 516 2212 509 2219 512 2217 514 2214 517 2211 540 2214 517 2211 510 4449 510 2218 513 4472 507 2220 511 30572 8960 2218 513 87698 8966 2211 510 87701 8963 2214 568\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 8956 4451 508 2220 511 2217 514 4470 510 4449 510 2218 544 2211 510 2218 513 2215 516 2212 509 2220 511 2217 514 2214 517 2211 510 2245 517 4441 508 2220 511 35049 8961 2215 516 87696 8959 2217 514 87698 8956 2220 511 87701 8964 2213 508\n#\nname: Ch_prev\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 10 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: SIRC\naddress: 01 00 00 00\ncommand: 11 00 00 00\n#\nname: Power\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 1C 00 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 4B 00 00 00\n#\nname: Vol_dn\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 4F 00 00 00\n#\nname: Ch_next\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 09 00 00 00\n#\nname: Ch_prev\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 05 00 00 00\n#\n# Model: Strong TVD221_B1825\n#\nname: Mute\ntype: parsed\nprotocol: NEC\naddress: 01 00 00 00\ncommand: 08 00 00 00\n#\nname: Power\ntype: raw\nfrequency: 36700\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 872 872 872 872 872 872 872 872 872 872 2616 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 2616 872 2616 872 2616 872 2616 872 2616 872 872 872 34008\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 872 872 872 872 872 872 872 872 2616 872 2616 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 2616 872 2616 872 2616 872 2616 872 872 872 872 872 34008\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 2616 872 872 872 872 872 872 872 2616 872 2616 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 872 872 2616 872 2616 872 2616 872 872 872 872 872 34008\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 872 872 872 872 872 872 872 872 2616 872 872 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 2616 872 2616 872 2616 872 2616 872 872 872 2616 872 34008\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 872 872 872 872 872 872 2616 872 872 872 872 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 2616 872 2616 872 2616 872 872 872 2616 872 2616 872 34008\n#\n# Model: TCL 32S327\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 3488 3488 872 2616 872 872 872 872 872 2616 872 872 872 2616 872 2616 872 2616 872 872 872 872 872 2616 872 2616 872 872 872 2616 872 2616 872 872 872 2616 872 872 872 872 872 872 872 2616 872 2616 872 872 872 872 872 34008\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 4012 3982 514 1984 519 1979 514 1984 519 1979 514 983 519 980 512 1988 516 982 520 1979 514 1984 519 1978 515 983 519 980 511 987 515 983 519 980 511 1988 516 1982 511 987 515 1983 521 978 514 985 517 981 521 1978 515\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 171 91301 168 17617 175 4907 167 15077 177 55723 170 4910 174 7448 174 15071 173 4908 176 17609 172 7450 172 7451 171 68432 169 7454 168 7454 168 4913 171 7451 171 4911 173 4908 176 7446 176 7446 176 50643 176 4905 169 4912 172 7450 172 7450 172 7451 171 4911 173 7448 174 4908 176 4905 169 7452 170 7453 169 50649 169 4912 173 4909 175 7447 175 7448 174 7448 174 4908 176 7446 176 4906 168 4913 171 7450 172 7451 171 50647 171 4911 173 4908 176 7445 177 7445 177 7446 176 4906 168 7454 168 4913 171 4910 174 7448 174 7449 173 50645 173 9990 169 7453 169 7454 168 7455 177 4905 169 7452 170 4912 172 4909 175 7447 175 7447 175 50644 174 4908 176 4905 169 7453 169 7454 168 7454 178 4904 170 7452 170 4912 172 4909 175 7447 175 7448 174 55726 177 4905 169 7453 169 7452 170 7453 169 4914 170 7451 171 4910 174 4907 177 7446 176 7446 176 50642 176 4906 168 4913 171 7452 170 7452 170 7453 169 4912 172 7450 172 4910 174 4907 177 7445 177 7446 176 50642 176 9987 172 7450 171 15074 170 4911 173 7450 172 4909 175 4906 168 7455 177 7446 176 50641 176 9988 171 7451 171 7451 171 7452 170 4912 172 7450 172 4909 175 4906 168 7454 178 7444 177 50641 167 4914 170 4911 174 7449 173 7449 173 7449 173 4909 175 7447 175 4907 177 4904 170 15075 169 50650 168 4913 171 4910 174 7448 174 7448 174 7448 174 4908 176 7447 175 4907 177 4903 171 7452 170 7452 170 50648 170 4912 172 4909 175 7446 176 7447 175 7448 174 4907 177 7446 176 4906 168 4912 172 7450 172 7451 171 50648 170 4912 172 4908 176 7446 176 7446 176 7447 175 4906 168 7454 178 4904 170 4911 173 15072 172 50646 173 4908 176 4906 168 7454 168 7455 177 7444 168 4915 169 7453 169 4913 171 4910 174 7448 174 7448 174 50644 174 4908 176 4904 170 7453 169 7453 169 7454 178 4904 170 40 198 7213 170 4910 174 4907 177 7446 176 7446 176 50643 175 4905 169 4913 171 7451 171 7452 170 7452 170 4911 173 7449 173 4909 175 4905 169 7454 168 7454 168 50650 178 4904 170 4911 173 7449 173 7450 172 7451 171 4910 174 7448 174 4907 177 4905 169 7453 169 7453 169 50649 169 4913 172 4910 174 15071 173 7449 173 4908 176 7447 175 4906 168 4913 171 7451 171 7451 171 50648 170 4911 173 4909 175 7446 176 7447 175 7448 174 4908 176 7446 176 4905 169 4913 171 15074 170 50648 170 4912 172 20154 175 7448 174 4907 177 7445 177 4904 170 4912 172 7450 172 7450 172 50647 170 4911 173 4908 176 7447 174 7447 174 7448 174 4908 176 7446 175 4906 168 4913 171 7451 171 7452 170 50649 168 4912 172 4910 174 7447 175 7448 174 7449 172 4909 175 7447 174 4907 177 4904 170 7452 170 7453 169 50649 169 4913 171 4910 174 7449 173 7449 173 7450 172 4909 175 7447 175 4906 168 4914 170 7452 170 7453 169\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 170 86221 170 7452 169 7453 168 12536 170 7452 169 43026 168 15077 177 7445 176 20150 168 15076 178 4904 170 7452 170 73516 168 7454 168 12536 171 7452 170 12534 172 7450 171 7451 171 43025 170 7452 170 15075 169 15077 177 4904 170 7452 170 7452 170 12535 172 7450 172 81138 178 12526 170 27779 172\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 169 12535 174 88758 177 7445 168 4913 172 7450 173 4908 177 7445 168 48108 171 4910 175 4907 168 7454 169 7454 169 7453 170 7454 169 7452 171 4911 174 7448 175 4907 168 7454 169\n#\n# Model: Telefunken d32f660x5cwi\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 173 7448 175 7445 178 7443 170 7451 172 7449 174 7447 176 7445 168 4912 173 7447 176 7446 177 4903 172 43015 177 7443 170 7451 172 7448 175 7445 178 7444 169 7452 171 7449 174 4907 168 7452 171 7450 173 4907 168 43018 175 7447 176 7444 169 7453 170 7450 173 7448 175 7446 177 399 174 6870 169 4911 174 7447 176 7445 168 4912 173 43013 169 7451 172 7451 172 7448 175 7445 168 7453 170 7451 172 7449 174 4906 169 7452 171 7450 173 4907 168 43019 174 7447 176 7445 178 7443 170 7450 173 7448 175 7446 177 7444 169 4910 175 7447 176 7444 169 4913 172 43012 170 7452 171 7450 173 7448 175 7445 168 7453 170 7450 173 7449 174 4907 168 7452 171 7450 173 4908 177 43010 171 7448 175 7446 177 7444 169 7451 172 7448 175\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 328 605 321 283 643 290 313 589 316 287 639 596 642 589 316 285 318 285 641 591 637 294 309 587 328\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 331 601 314 289 647 285 308 595 320 282 644 591 647 584 321 282 644 587 318 285 641 290 313 583 332\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 330 632 294 281 645 288 315 585 320 284 642 593 645 585 320 284 642 589 649 583 644 586 329\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 333 600 315 288 638 294 309 594 321 281 645 591 636 595 643 589 638 592 313 290 646 585 330\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 334 628 287 289 647 285 308 593 322 307 619 589 649 582 323 280 646 586 641 589 316 287 639 89967 331 602 313 290 646 286 307 595 320 283 643 591 647 584 321 282 644 588 639 591 314 288 638\n#\n# Model: Thomson 40FS3003\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 332 601 314 290 646 286 307 594 321 282 644 590 648 582 323 281 645 586 641 290 313 583 644\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9219 4484 662 469 661 469 661 1627 660 471 658 474 656 499 631 499 631 499 631 1657 630 1657 631 500 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 500 630 1657 630 500 631 500 630 500 631 500 630 1657 630 1658 630 1657 631 500 630 1657 631 1658 630 1658 630 1658 630 40107 9106 2202 631\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9218 4484 636 495 660 469 661 1627 660 471 658 472 658 474 656 475 655 474 656 1632 655 1632 656 474 657 1632 656 1631 657 1632 656 1631 656 1632 656 474 656 1632 655 475 656 474 657 474 656 474 656 474 656 474 656 1632 655 474 656 1632 656 1632 656 1632 656 1632 656 1632 656 1632 656 40103 9107 2177 655\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9245 4429 689 467 662 468 661 1626 660 471 658 473 657 474 656 474 656 474 656 1631 657 1631 657 474 656 1631 656 1632 656 1631 657 1631 657 1631 656 1632 656 1631 657 474 656 474 656 474 657 474 656 474 656 474 657 474 656 474 656 1631 656 1632 656 1632 656 1632 656 1632 656 1631 656 40082 9109 2175 656\n#\n# Model: Vizio D43-C1\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9219 4485 636 495 660 469 661 1626 661 471 658 473 657 474 656 499 631 500 630 1657 630 1657 631 500 630 1657 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 1657 631 500 630 500 630 500 630 500 631 500 630 1657 631 1657 631 500 630 1657 631 1658 630 1657 631 1658 630 39868 9106 2178 655\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 9016 4408 603 512 602 512 629 1599 686 428 630 483 630 484 629 485 628 492 627 1604 625 1605 624 491 623 1630 599 1631 598 1631 598 1631 598 1636 599 515 599 515 599 515 599 1631 599 515 599 515 599 515 599 521 599 1631 599 1631 599 1631 598 515 599 1631 598 1631 598 1631 598 1632 598 39999 9016 2166 626 95735 9012 2168 625 95735 9013 2167 626\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 988 605 587 1176 588 589 586 882 587 1178 585 588 588 589 587 1470 587 1175 589 588 587 590 586 13118 986 608 587 1177 587 588 587 882 587 1178 586 588 587 589 587 1470 587 1177 587 590 585 588 588 13117 987 609 586 1177 587 590 585 882 587 1177 587 589 586 588 588 1472 585 1176 588 588 587 588 587 13118 986 608 587 1177 586 589 587 881 588 1177 587 588 587 589 587 1470 587 1177 587 589 586 588 588 13116 988 609 586 1178 586 589 586 883 586 1177 587 589 586 590 586 1471 586 1178 585 588 587 588 588 13117 987 609 586 1177 587 589 587 883 586 1177 586 589 587 589 586 1472 585 1176 588 589 587 589 587 13116 988 610 585 1178 586 588 588 882 587 1176 588 590 586 588 587 1471 586 1177 586 589 587 589 586\n#\n# Model: Zenith SC3492Z\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 990 603 589 1175 589 587 588 882 587 1175 588 588 588 589 586 1470 587 1471 587 588 588 881 588 12528 988 609 586 1177 587 589 586 882 587 1177 586 588 587 589 587 1470 587 1471 587 588 588 881 588 12527 989 607 588 1175 589 588 588 881 588 1175 588 588 588 587 589 1470 587 1470 588 589 587 881 588 12528 988 607 588 1175 589 588 587 882 587 1178 586 587 588 588 588 1472 586 1470 588 588 587 881 588 12529 987 608 587 1175 589 587 589 882 587 1175 589 589 587 587 589 1469 588 1470 588 587 589 881 588 12528 988 607 588 1175 589 590 585 883 586 1176 587 587 589 588 588 1471 586 1470 588 587 589 883 586 12527 989 607 588 1176 588 588 588 880 589 1176 588 588 588 589 586 1469 588 1469 589 588 587 881 588\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 611 386 612 3984 612 4984 612 387 611 3985 612 387 611 3986 611 4985 611 387 611 3986 611 4985 611 387 611 3986 610 4987 609 4985 611 389 609\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 595 403 595 4002 594 5001 595 404 594 4002 594 405 593 4003 594 5001 595 5001 595 405 593 4003 594 405 593 4004 592 5004 592 406 592 4004 593\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 596 401 597 4000 596 5000 596 402 596 4001 596 402 596 4002 595 5000 596 5000 596 403 595 4002 595 402 596 4003 594 5002 594 5000 596 404 594\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 484 514 485 4112 485 5111 485 514 485 4112 484 5110 486 513 486 4111 485 5110 486 514 485 4112 484 513 486 4112 484 5112 484 5113 483 513 486\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 405 593 4005 591 5005 591 407 591 4006 591 408 590 4006 591 5006 590 5004 592 406 591 4005 592 5005 591 407 591 4006 591 407 591 4007 590\n#\n# Model: Zenith tv\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 598 400 598 3999 597 4999 596 401 597 4000 597 5000 596 402 596 3999 598 4999 597 401 597 4000 597 4998 598 402 596 4000 597 402 596 4000 597\n#\nname: Power\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 561 442 562 4055 591 5032 592 413 591 4026 593 411 593 4026 593 5030 618 387 617 4001 592 5032 591 440 564 4028 591 5033 590 5034 589 440 563\n#\nname: Mute\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 411 593 4025 593 5030 593 411 593 4025 593 411 593 4025 618 5005 619 5003 593 439 564 4027 592 439 564 4029 590 5033 590 439 564 4055 563 123130 617 387 618 3999 593 5031 592 439 564 4027 592 439 564 4029 590 5034 589 5059 563 440 563 4056 563 442 561 4058 561 5063 560 443 560 4059 560\n#\nname: Ch_next\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 412 592 4026 593 5030 593 411 593 4025 594 411 617 4001 618 5005 619 5004 592 439 565 4028 590 5032 591 439 564 4055 564 440 563 4056 563\n#\nname: Ch_prev\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 412 592 4026 592 5029 594 411 593 4025 593 5030 593 411 617 4001 619 5003 617 415 565 4027 592 5032 591 439 564 4030 589 439 564 4055 564\n#\nname: Vol_up\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 591 413 591 4027 592 5030 593 411 593 4026 593 412 592 4025 618 5005 620 5004 592 439 564 4028 591 439 564 4029 590 5033 590 5035 588 440 563 122125 617 387 619 4000 592 5032 591 439 564 4028 591 439 565 4030 589 5034 589 5059 564 441 562 4057 562 442 561 4058 561 5063 560 5063 560 444 560\n#\nname: Vol_dn\ntype: raw\nfrequency: 38000\nduty_cycle: 0.330000\ndata: 590 412 592 4026 593 5030 593 412 592 4025 594 5029 594 412 616 4002 618 5003 593 439 564 4027 592 439 564 4029 590 5033 590 5035 588 440 563\n#\n# Model: ONN Roku TV\n#\nname: Vol_dn\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 10 EF 00 00\n#\nname: Vol_up\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 0F F0 00 00\n#\nname: Mute\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 20 DF 00 00\n#\nname: Power\ntype: parsed\nprotocol: NECext\naddress: EA C7 00 00\ncommand: 17 E8 00 00\n"
  },
  {
    "path": "applications/main/infrared/scenes/common/infrared_scene_universal_common.c",
    "content": "#include \"../../infrared_app_i.h\"\n\n#include <dolphin/dolphin.h>\n\n#pragma pack(push, 1)\ntypedef union {\n    uint32_t packed_value;\n    struct {\n        bool is_paused;\n        uint8_t padding;\n        uint16_t signal_index;\n    };\n} InfraredSceneState;\n#pragma pack(pop)\n\nvoid infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type) {\n    InfraredApp* infrared = context;\n    if(type == InputTypeShort) {\n        uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);\n        view_dispatcher_send_custom_event(infrared->view_dispatcher, event);\n    }\n}\n\nstatic void infrared_scene_universal_common_progress_input_callback(\n    void* context,\n    InfraredProgressViewInput input) {\n    InfraredApp* infrared = context;\n    uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypePopupInput, input);\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, event);\n}\n\nstatic void\n    infrared_scene_universal_common_show_popup(InfraredApp* infrared, uint32_t record_count) {\n    ViewStack* view_stack = infrared->view_stack;\n    InfraredProgressView* progress = infrared->progress;\n    infrared_progress_view_set_progress_total(progress, record_count);\n    infrared_progress_view_set_input_callback(\n        progress, infrared_scene_universal_common_progress_input_callback, infrared);\n    view_stack_add_view(view_stack, infrared_progress_view_get_view(progress));\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);\n}\n\nstatic void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) {\n    ViewStack* view_stack = infrared->view_stack;\n    InfraredProgressView* progress = infrared->progress;\n    view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress));\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);\n}\n\nstatic int32_t infrared_scene_universal_common_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    const InfraredErrorCode error = infrared_brute_force_calculate_messages(infrared->brute_force);\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher,\n        infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0));\n\n    return error;\n}\n\nvoid infrared_scene_universal_common_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);\n    view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));\n\n    // Load universal remote data in background\n    infrared_blocking_task_start(infrared, infrared_scene_universal_common_task_callback);\n}\n\nstatic void infrared_scene_universal_common_handle_popup_input(\n    InfraredApp* infrared,\n    InfraredProgressViewInput input) {\n    InfraredBruteForce* brute_force = infrared->brute_force;\n    SceneManager* scene_manager = infrared->scene_manager;\n    uint32_t scene_id = scene_manager_get_current_scene(infrared->scene_manager);\n    switch(input) {\n    case InfraredProgressViewInputStop: {\n        infrared_brute_force_stop(brute_force);\n        infrared_scene_universal_common_hide_popup(infrared);\n        break;\n    }\n\n    case InfraredProgressViewInputPause: {\n        infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);\n        infrared_progress_view_set_paused(infrared->progress, true);\n        InfraredSceneState scene_state = {\n            .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n        scene_state.is_paused = true;\n        if(scene_state.signal_index)\n            scene_state.signal_index--; // when running, the state stores the next index\n        scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value);\n        break;\n    }\n\n    case InfraredProgressViewInputResume: {\n        infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);\n        infrared_progress_view_set_paused(infrared->progress, false);\n        InfraredSceneState scene_state = {\n            .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n        scene_state.is_paused = false;\n        scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value);\n        break;\n    }\n\n    case InfraredProgressViewInputNextSignal: {\n        InfraredSceneState scene_state = {\n            .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n        scene_state.signal_index++;\n        if(infrared_progress_view_set_progress(infrared->progress, scene_state.signal_index + 1))\n            scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value);\n        break;\n    }\n\n    case InfraredProgressViewInputPreviousSignal: {\n        InfraredSceneState scene_state = {\n            .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n        if(scene_state.signal_index) {\n            scene_state.signal_index--;\n            if(infrared_progress_view_set_progress(\n                   infrared->progress, scene_state.signal_index + 1))\n                scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value);\n        }\n        break;\n    }\n\n    case InfraredProgressViewInputSendSingle: {\n        InfraredSceneState scene_state = {\n            .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n        infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);\n        infrared_brute_force_send(infrared->brute_force, scene_state.signal_index);\n        infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);\n        break;\n    }\n\n    default:\n        furi_crash();\n    }\n}\n\nbool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    InfraredBruteForce* brute_force = infrared->brute_force;\n    uint32_t scene_id = scene_manager_get_current_scene(infrared->scene_manager);\n    bool consumed = false;\n\n    if(infrared_brute_force_is_started(brute_force)) {\n        if(event.type == SceneManagerEventTypeTick) {\n            InfraredSceneState scene_state = {\n                .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)};\n\n            if(!scene_state.is_paused) {\n                bool success = infrared_brute_force_send(brute_force, scene_state.signal_index);\n                if(success) {\n                    success = infrared_progress_view_set_progress(\n                        infrared->progress, scene_state.signal_index + 1);\n                    scene_state.signal_index++;\n                    scene_manager_set_scene_state(\n                        scene_manager, scene_id, scene_state.packed_value);\n                }\n                if(!success) {\n                    infrared_brute_force_stop(brute_force);\n                    infrared_scene_universal_common_hide_popup(infrared);\n                }\n                consumed = true;\n            }\n        } else if(event.type == SceneManagerEventTypeCustom) {\n            uint16_t event_type;\n            int16_t event_value;\n            infrared_custom_event_unpack(event.event, &event_type, &event_value);\n            if(event_type == InfraredCustomEventTypePopupInput) {\n                infrared_scene_universal_common_handle_popup_input(infrared, event_value);\n                consumed = true;\n            }\n        }\n    } else {\n        if(event.type == SceneManagerEventTypeBack) {\n            scene_manager_previous_scene(scene_manager);\n            consumed = true;\n        } else if(event.type == SceneManagerEventTypeCustom) {\n            uint16_t event_type;\n            int16_t event_value;\n            infrared_custom_event_unpack(event.event, &event_type, &event_value);\n\n            if(event_type == InfraredCustomEventTypeButtonSelected) {\n                uint32_t record_count;\n                if(infrared_brute_force_start(brute_force, event_value, &record_count)) {\n                    scene_manager_set_scene_state(infrared->scene_manager, scene_id, 0);\n                    dolphin_deed(DolphinDeedIrSend);\n                    infrared_scene_universal_common_show_popup(infrared, record_count);\n                } else {\n                    scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);\n                }\n            } else if(event_type == InfraredCustomEventTypeTaskFinished) {\n                const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n\n                if(INFRARED_ERROR_PRESENT(task_error)) {\n                    scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);\n                } else {\n                    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);\n                }\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_universal_common_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    ButtonPanel* button_panel = infrared->button_panel;\n    view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));\n    infrared_brute_force_reset(infrared->brute_force);\n    button_panel_reset(button_panel);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/common/infrared_scene_universal_common.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\nvoid infrared_scene_universal_common_on_enter(void* context);\nbool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event);\nvoid infrared_scene_universal_common_on_exit(void* context);\nvoid infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type);\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene.c",
    "content": "#include \"infrared_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const infrared_on_enter_handlers[])(void*) = {\n#include \"infrared_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const infrared_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"infrared_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const infrared_on_exit_handlers[])(void* context) = {\n#include \"infrared_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers infrared_scene_handlers = {\n    .on_enter_handlers = infrared_on_enter_handlers,\n    .on_event_handlers = infrared_on_event_handlers,\n    .on_exit_handlers = infrared_on_exit_handlers,\n    .scene_num = InfraredSceneNum,\n};\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) InfraredScene##id,\ntypedef enum {\n#include \"infrared_scene_config.h\"\n    InfraredSceneNum,\n} InfraredScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers infrared_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"infrared_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"infrared_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"infrared_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_ask_back.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, result);\n}\n\nvoid infrared_scene_ask_back_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    DialogEx* dialog_ex = infrared->dialog_ex;\n\n    if(infrared->app_state.is_learning_new_remote) {\n        dialog_ex_set_header(dialog_ex, \"Exit to Infrared Menu?\", 64, 11, AlignCenter, AlignTop);\n    } else {\n        dialog_ex_set_header(dialog_ex, \"Exit to Remote Menu?\", 64, 11, AlignCenter, AlignTop);\n    }\n\n    dialog_ex_set_text(\n        dialog_ex, \"All unsaved data\\nwill be lost!\", 64, 25, AlignCenter, AlignTop);\n    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);\n    dialog_ex_set_left_button_text(dialog_ex, \"Exit\");\n    dialog_ex_set_center_button_text(dialog_ex, NULL);\n    dialog_ex_set_right_button_text(dialog_ex, \"Stay\");\n    dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);\n    dialog_ex_set_context(dialog_ex, context);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);\n}\n\nbool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            if(infrared->app_state.is_learning_new_remote) {\n                scene_manager_search_and_switch_to_previous_scene(\n                    scene_manager, InfraredSceneStart);\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    scene_manager, InfraredSceneRemote);\n            }\n            consumed = true;\n        } else if(event.event == DialogExResultRight) {\n            scene_manager_previous_scene(scene_manager);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_ask_back_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    dialog_ex_reset(infrared->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_ask_retry.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, result);\n}\n\nvoid infrared_scene_ask_retry_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    DialogEx* dialog_ex = infrared->dialog_ex;\n\n    dialog_ex_set_header(dialog_ex, \"Retry Reading?\", 64, 11, AlignCenter, AlignTop);\n    dialog_ex_set_text(\n        dialog_ex, \"All unsaved data\\nwill be lost!\", 64, 25, AlignCenter, AlignTop);\n    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);\n    dialog_ex_set_left_button_text(dialog_ex, \"Exit\");\n    dialog_ex_set_center_button_text(dialog_ex, NULL);\n    dialog_ex_set_right_button_text(dialog_ex, \"Stay\");\n    dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback);\n    dialog_ex_set_context(dialog_ex, context);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);\n}\n\nbool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, InfraredSceneLearn);\n            consumed = true;\n        } else if(event.event == DialogExResultRight) {\n            scene_manager_previous_scene(scene_manager);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_ask_retry_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    dialog_ex_reset(infrared->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_config.h",
    "content": "ADD_SCENE(infrared, start, Start)\nADD_SCENE(infrared, ask_back, AskBack)\nADD_SCENE(infrared, ask_retry, AskRetry)\nADD_SCENE(infrared, edit, Edit)\nADD_SCENE(infrared, edit_delete, EditDelete)\nADD_SCENE(infrared, edit_delete_done, EditDeleteDone)\nADD_SCENE(infrared, edit_button_select, EditButtonSelect)\nADD_SCENE(infrared, edit_rename, EditRename)\nADD_SCENE(infrared, edit_rename_done, EditRenameDone)\nADD_SCENE(infrared, edit_move, EditMove)\nADD_SCENE(infrared, learn, Learn)\nADD_SCENE(infrared, learn_done, LearnDone)\nADD_SCENE(infrared, learn_enter_name, LearnEnterName)\nADD_SCENE(infrared, learn_success, LearnSuccess)\nADD_SCENE(infrared, remote, Remote)\nADD_SCENE(infrared, remote_list, RemoteList)\nADD_SCENE(infrared, universal, Universal)\nADD_SCENE(infrared, universal_tv, UniversalTV)\nADD_SCENE(infrared, universal_ac, UniversalAC)\nADD_SCENE(infrared, universal_audio, UniversalAudio)\nADD_SCENE(infrared, universal_projector, UniversalProjector)\nADD_SCENE(infrared, gpio_settings, GpioSettings)\nADD_SCENE(infrared, debug, Debug)\nADD_SCENE(infrared, error_databases, ErrorDatabases)\nADD_SCENE(infrared, rpc, Rpc)\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_debug.c",
    "content": "#include \"../infrared_app_i.h\"\n\nvoid infrared_scene_debug_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    InfraredWorker* worker = infrared->worker;\n\n    infrared_worker_rx_set_received_signal_callback(\n        worker, infrared_signal_received_callback, context);\n    infrared_worker_rx_enable_blink_on_receiving(worker, true);\n    infrared_worker_rx_start(worker);\n\n    infrared_debug_view_set_text(infrared->debug_view, \"Received signals\\nwill appear here\");\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDebugView);\n}\n\nbool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeSignalReceived) {\n            InfraredDebugView* debug_view = infrared->debug_view;\n            InfraredSignal* signal = infrared->current_signal;\n\n            if(infrared_signal_is_raw(signal)) {\n                const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);\n                infrared_debug_view_set_text(debug_view, \"RAW\\n%d samples\\n\", raw->timings_size);\n\n                printf(\"RAW, %zu samples:\\r\\n\", raw->timings_size);\n                for(size_t i = 0; i < raw->timings_size; ++i) {\n                    printf(\"%lu \", raw->timings[i]);\n                }\n                printf(\"\\r\\n\");\n\n            } else {\n                const InfraredMessage* message = infrared_signal_get_message(signal);\n                infrared_debug_view_set_text(\n                    debug_view,\n                    \"%s\\nA:0x%0*lX\\nC:0x%0*lX\\n%s\\n\",\n                    infrared_get_protocol_name(message->protocol),\n                    ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),\n                    message->address,\n                    ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),\n                    message->command,\n                    message->repeat ? \" R\" : \"\");\n\n                printf(\n                    \"== %s, A:0x%0*lX, C:0x%0*lX%s ==\\r\\n\",\n                    infrared_get_protocol_name(message->protocol),\n                    ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),\n                    message->address,\n                    ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),\n                    message->command,\n                    message->repeat ? \" R\" : \"\");\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_debug_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    InfraredWorker* worker = infrared->worker;\n    infrared_worker_rx_stop(worker);\n    infrared_worker_rx_enable_blink_on_receiving(worker, false);\n    infrared_worker_rx_set_received_signal_callback(worker, NULL, NULL);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit.c",
    "content": "#include \"../infrared_app_i.h\"\n\ntypedef enum {\n    SubmenuIndexAddButton,\n    SubmenuIndexRenameButton,\n    SubmenuIndexMoveButton,\n    SubmenuIndexDeleteButton,\n    SubmenuIndexRenameRemote,\n    SubmenuIndexDeleteRemote,\n} SubmenuIndex;\n\nstatic void infrared_scene_edit_submenu_callback(void* context, uint32_t index) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, index);\n}\n\nvoid infrared_scene_edit_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Submenu* submenu = infrared->submenu;\n    SceneManager* scene_manager = infrared->scene_manager;\n\n    submenu_add_item(\n        submenu,\n        \"Add Button\",\n        SubmenuIndexAddButton,\n        infrared_scene_edit_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Rename Button\",\n        SubmenuIndexRenameButton,\n        infrared_scene_edit_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Move Button\",\n        SubmenuIndexMoveButton,\n        infrared_scene_edit_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Delete Button\",\n        SubmenuIndexDeleteButton,\n        infrared_scene_edit_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Rename Remote\",\n        SubmenuIndexRenameRemote,\n        infrared_scene_edit_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Delete Remote\",\n        SubmenuIndexDeleteRemote,\n        infrared_scene_edit_submenu_callback,\n        context);\n\n    const uint32_t submenu_index = scene_manager_get_scene_state(scene_manager, InfraredSceneEdit);\n    submenu_set_selected_item(submenu, submenu_index);\n    scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, SubmenuIndexAddButton);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);\n}\n\nbool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, submenu_index);\n\n        if(submenu_index == SubmenuIndexAddButton) {\n            infrared->app_state.is_learning_new_remote = false;\n            scene_manager_next_scene(scene_manager, InfraredSceneLearn);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexRenameButton) {\n            infrared->app_state.edit_target = InfraredEditTargetButton;\n            infrared->app_state.edit_mode = InfraredEditModeRename;\n            scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexMoveButton) {\n            scene_manager_next_scene(scene_manager, InfraredSceneEditMove);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexDeleteButton) {\n            infrared->app_state.edit_target = InfraredEditTargetButton;\n            infrared->app_state.edit_mode = InfraredEditModeDelete;\n            scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexRenameRemote) {\n            infrared->app_state.edit_target = InfraredEditTargetRemote;\n            infrared->app_state.edit_mode = InfraredEditModeRename;\n            scene_manager_next_scene(scene_manager, InfraredSceneEditRename);\n            consumed = true;\n        } else if(submenu_index == SubmenuIndexDeleteRemote) {\n            infrared->app_state.edit_target = InfraredEditTargetRemote;\n            infrared->app_state.edit_mode = InfraredEditModeDelete;\n            scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    submenu_reset(infrared->submenu);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_button_select.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, index);\n}\n\nvoid infrared_scene_edit_button_select_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Submenu* submenu = infrared->submenu;\n    InfraredRemote* remote = infrared->remote;\n    InfraredAppState* app_state = &infrared->app_state;\n\n    const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ?\n                             \"Rename Button:\" :\n                             \"Delete Button:\";\n    submenu_set_header(submenu, header);\n\n    const size_t button_count = infrared_remote_get_signal_count(remote);\n    for(size_t i = 0; i < button_count; ++i) {\n        submenu_add_item(\n            submenu,\n            infrared_remote_get_signal_name(remote, i),\n            i,\n            infrared_scene_edit_button_select_submenu_callback,\n            context);\n    }\n\n    if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {\n        submenu_set_selected_item(submenu, app_state->current_button_index);\n        app_state->current_button_index = InfraredButtonIndexNone;\n    }\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);\n}\n\nbool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    InfraredAppState* app_state = &infrared->app_state;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        app_state->current_button_index = event.event;\n        const InfraredEditMode edit_mode = app_state->edit_mode;\n        if(edit_mode == InfraredEditModeRename) {\n            scene_manager_next_scene(scene_manager, InfraredSceneEditRename);\n        } else if(edit_mode == InfraredEditModeDelete) {\n            scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);\n        } else {\n            furi_crash();\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_button_select_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    submenu_reset(infrared->submenu);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_delete.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic void\n    infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, result);\n}\n\nstatic int32_t infrared_scene_edit_delete_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    InfraredAppState* app_state = &infrared->app_state;\n    const InfraredEditTarget edit_target = app_state->edit_target;\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    if(edit_target == InfraredEditTargetButton) {\n        furi_assert(app_state->current_button_index != InfraredButtonIndexNone);\n        error = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index);\n    } else if(edit_target == InfraredEditTargetRemote) {\n        error = infrared_remote_remove(infrared->remote);\n    } else {\n        furi_crash();\n    }\n\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);\n\n    return error;\n}\n\nvoid infrared_scene_edit_delete_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    DialogEx* dialog_ex = infrared->dialog_ex;\n    InfraredRemote* remote = infrared->remote;\n    const InfraredEditTarget edit_target = infrared->app_state.edit_target;\n\n    if(edit_target == InfraredEditTargetButton) {\n        dialog_ex_set_header(dialog_ex, \"Delete Button?\", 64, 0, AlignCenter, AlignTop);\n\n        const int32_t current_button_index = infrared->app_state.current_button_index;\n        furi_check(current_button_index != InfraredButtonIndexNone);\n\n        InfraredErrorCode error =\n            infrared_remote_load_signal(remote, infrared->current_signal, current_button_index);\n        if(INFRARED_ERROR_PRESENT(error)) {\n            const char* format =\n                (INFRARED_ERROR_CHECK(error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) ?\n                    \"Failed to delete\\n\\\"%s\\\" is too long.\\nTry to edit file from pc\" :\n                    \"Failed to load\\n\\\"%s\\\"\";\n            infrared_show_error_message(\n                infrared, format, infrared_remote_get_signal_name(remote, current_button_index));\n            scene_manager_previous_scene(infrared->scene_manager);\n            return;\n        }\n\n        if(infrared_signal_is_raw(infrared->current_signal)) {\n            const InfraredRawSignal* raw =\n                infrared_signal_get_raw_signal(infrared->current_signal);\n            infrared_text_store_set(\n                infrared,\n                0,\n                \"%s\\nRAW\\n%zu samples\",\n                infrared_remote_get_signal_name(remote, current_button_index),\n                raw->timings_size);\n\n        } else {\n            const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal);\n            infrared_text_store_set(\n                infrared,\n                0,\n                \"%s\\n%s\\nA=0x%0*lX C=0x%0*lX\",\n                infrared_remote_get_signal_name(remote, current_button_index),\n                infrared_get_protocol_name(message->protocol),\n                ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),\n                message->address,\n                ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),\n                message->command);\n        }\n\n    } else if(edit_target == InfraredEditTargetRemote) {\n        dialog_ex_set_header(dialog_ex, \"Delete Remote?\", 64, 0, AlignCenter, AlignTop);\n        infrared_text_store_set(\n            infrared,\n            0,\n            \"%s\\n with %zu buttons\",\n            infrared_remote_get_name(remote),\n            infrared_remote_get_signal_count(remote));\n    } else {\n        furi_crash();\n    }\n\n    dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter);\n    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);\n    dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Delete\");\n    dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback);\n    dialog_ex_set_context(dialog_ex, context);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);\n}\n\nbool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            scene_manager_previous_scene(scene_manager);\n        } else if(event.event == DialogExResultRight) {\n            // Delete a button or a remote in a separate thread\n            infrared_blocking_task_start(infrared, infrared_scene_edit_delete_task_callback);\n\n        } else if(event.event == InfraredCustomEventTypeTaskFinished) {\n            const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n\n            InfraredAppState* app_state = &infrared->app_state;\n\n            if(!INFRARED_ERROR_PRESENT(task_error)) {\n                scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);\n            } else {\n                if(INFRARED_ERROR_CHECK(\n                       task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) {\n                    const uint8_t index = INFRARED_ERROR_GET_INDEX(task_error);\n                    const char* format =\n                        \"Failed to delete\\n\\\"%s\\\" is too long.\\nTry to edit file from pc\";\n                    infrared_show_error_message(\n                        infrared,\n                        format,\n                        infrared_remote_get_signal_name(infrared->remote, index));\n                } else {\n                    const char* edit_target_text =\n                        app_state->edit_target == InfraredEditTargetButton ? \"button\" : \"file\";\n                    infrared_show_error_message(\n                        infrared, \"Failed to\\ndelete %s\", edit_target_text);\n                }\n\n                const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote};\n                scene_manager_search_and_switch_to_previous_scene_one_of(\n                    scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            }\n\n            app_state->current_button_index = InfraredButtonIndexNone;\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_delete_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    dialog_ex_reset(infrared->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_delete_done.c",
    "content": "#include \"../infrared_app_i.h\"\n\nvoid infrared_scene_edit_delete_done_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n\n    popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(popup, \"Deleted\", 80, 19, AlignLeft, AlignBottom);\n    popup_set_callback(popup, infrared_popup_closed_callback);\n    popup_set_context(popup, context);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n}\n\nbool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypePopupClosed) {\n            const InfraredEditTarget edit_target = infrared->app_state.edit_target;\n            if(edit_target == InfraredEditTargetButton) {\n                scene_manager_search_and_switch_to_previous_scene(\n                    scene_manager, InfraredSceneRemote);\n            } else if(edit_target == InfraredEditTargetRemote) {\n                const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList};\n                if(!scene_manager_search_and_switch_to_previous_scene_one_of(\n                       scene_manager, possible_scenes, COUNT_OF(possible_scenes))) {\n                    view_dispatcher_stop(infrared->view_dispatcher);\n                }\n            } else {\n                furi_crash();\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_delete_done_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    UNUSED(infrared);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_move.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic int32_t infrared_scene_edit_move_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    const InfraredErrorCode error = infrared_remote_move_signal(\n        infrared->remote,\n        infrared->app_state.prev_button_index,\n        infrared->app_state.current_button_index);\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);\n\n    return error;\n}\n\nstatic void infrared_scene_edit_move_button_callback(\n    uint32_t index_old,\n    uint32_t index_new,\n    void* context) {\n    InfraredApp* infrared = context;\n    furi_assert(infrared);\n\n    infrared->app_state.prev_button_index = index_old;\n    infrared->app_state.current_button_index = index_new;\n\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeButtonSelected);\n}\n\nvoid infrared_scene_edit_move_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    InfraredRemote* remote = infrared->remote;\n\n    for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) {\n        infrared_move_view_add_item(\n            infrared->move_view, infrared_remote_get_signal_name(remote, i));\n    }\n\n    infrared_move_view_set_callback(\n        infrared->move_view, infrared_scene_edit_move_button_callback, infrared);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove);\n}\n\nbool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeButtonSelected) {\n            // Move the button in a separate thread\n            infrared_blocking_task_start(infrared, infrared_scene_edit_move_task_callback);\n\n        } else if(event.event == InfraredCustomEventTypeTaskFinished) {\n            const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n\n            if(INFRARED_ERROR_PRESENT(task_error)) {\n                const char* format = \"Failed to move\\n\\\"%s\\\"\";\n                uint8_t signal_index = infrared->app_state.prev_button_index;\n\n                if(INFRARED_ERROR_CHECK(\n                       task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) {\n                    signal_index = INFRARED_ERROR_GET_INDEX(task_error);\n                    format = \"Failed to move\\n\\\"%s\\\" is too long.\\nTry to edit file from pc\";\n                }\n                furi_assert(format);\n\n                const char* signal_name =\n                    infrared_remote_get_signal_name(infrared->remote, signal_index);\n                infrared_show_error_message(infrared, format, signal_name);\n\n                const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote};\n                scene_manager_search_and_switch_to_previous_scene_one_of(\n                    infrared->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            } else {\n                view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove);\n            }\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_move_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    infrared_move_view_reset(infrared->move_view);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_rename.c",
    "content": "#include \"../infrared_app_i.h\"\n\n#include <string.h>\n#include <toolbox/path.h>\n\nstatic int32_t infrared_scene_edit_rename_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    InfraredAppState* app_state = &infrared->app_state;\n    const InfraredEditTarget edit_target = app_state->edit_target;\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    if(edit_target == InfraredEditTargetButton) {\n        furi_assert(app_state->current_button_index != InfraredButtonIndexNone);\n        error = infrared_remote_rename_signal(\n            infrared->remote, app_state->current_button_index, infrared->text_store[0]);\n    } else if(edit_target == InfraredEditTargetRemote) {\n        error = infrared_rename_current_remote(infrared, infrared->text_store[0]);\n    } else {\n        furi_crash();\n    }\n\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);\n\n    return error;\n}\n\nvoid infrared_scene_edit_rename_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    InfraredRemote* remote = infrared->remote;\n    TextInput* text_input = infrared->text_input;\n    size_t enter_name_length = 0;\n\n    const InfraredEditTarget edit_target = infrared->app_state.edit_target;\n    if(edit_target == InfraredEditTargetButton) {\n        text_input_set_header_text(text_input, \"Name the button\");\n\n        const int32_t current_button_index = infrared->app_state.current_button_index;\n        furi_check(current_button_index != InfraredButtonIndexNone);\n\n        enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH;\n        strlcpy(\n            infrared->text_store[0],\n            infrared_remote_get_signal_name(remote, current_button_index),\n            enter_name_length);\n\n    } else if(edit_target == InfraredEditTargetRemote) {\n        text_input_set_header_text(text_input, \"Name the remote\");\n        enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH;\n        strlcpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length);\n\n        FuriString* folder_path;\n        folder_path = furi_string_alloc();\n\n        if(furi_string_end_with(infrared->file_path, INFRARED_APP_EXTENSION)) {\n            path_extract_dirname(furi_string_get_cstr(infrared->file_path), folder_path);\n        }\n\n        ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n            furi_string_get_cstr(folder_path),\n            INFRARED_APP_EXTENSION,\n            infrared_remote_get_name(remote));\n        text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n        furi_string_free(folder_path);\n    } else {\n        furi_crash();\n    }\n\n    text_input_set_result_callback(\n        text_input,\n        infrared_text_input_callback,\n        context,\n        infrared->text_store[0],\n        enter_name_length,\n        false);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);\n}\n\nbool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeTextEditDone) {\n            // Rename a button or a remote in a separate thread\n            infrared_blocking_task_start(infrared, infrared_scene_edit_rename_task_callback);\n\n        } else if(event.event == InfraredCustomEventTypeTaskFinished) {\n            const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n            InfraredAppState* app_state = &infrared->app_state;\n\n            if(!INFRARED_ERROR_PRESENT(task_error)) {\n                scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);\n            } else {\n                bool long_signal = INFRARED_ERROR_CHECK(\n                    task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData);\n\n                const char* format = \"Failed to rename\\n%s\";\n                const char* target = infrared->app_state.edit_target == InfraredEditTargetButton ?\n                                         \"button\" :\n                                         \"file\";\n                if(long_signal) {\n                    format = \"Failed to rename\\n\\\"%s\\\" is too long.\\nTry to edit file from pc\";\n                    target = infrared_remote_get_signal_name(\n                        infrared->remote, INFRARED_ERROR_GET_INDEX(task_error));\n                }\n\n                infrared_show_error_message(infrared, format, target);\n\n                const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote};\n                scene_manager_search_and_switch_to_previous_scene_one_of(\n                    scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            }\n\n            app_state->current_button_index = InfraredButtonIndexNone;\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_rename_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    TextInput* text_input = infrared->text_input;\n\n    ValidatorIsFile* validator_context = text_input_get_validator_callback_context(text_input);\n    if(validator_context) {\n        validator_is_file_free(validator_context);\n    }\n\n    text_input_reset(text_input);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_edit_rename_done.c",
    "content": "#include \"../infrared_app_i.h\"\n\nvoid infrared_scene_edit_rename_done_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n\n    popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n    popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    popup_set_callback(popup, infrared_popup_closed_callback);\n    popup_set_context(popup, context);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n}\n\nbool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypePopupClosed) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   infrared->scene_manager, InfraredSceneRemote)) {\n                scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_edit_rename_done_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    UNUSED(infrared);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_error_databases.c",
    "content": "#include \"../infrared_app_i.h\"\n\nvoid infrared_scene_error_databases_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n\n    popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43);\n    popup_set_text(\n        popup, \"Function requires\\nSD card with fresh\\ndatabases.\", 47, 17, AlignLeft, AlignTop);\n\n    popup_set_context(popup, context);\n    popup_set_callback(popup, infrared_popup_closed_callback);\n\n    infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn);\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n}\n\nbool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypePopupClosed) {\n            scene_manager_search_and_switch_to_previous_scene(\n                infrared->scene_manager, InfraredSceneUniversal);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_error_databases_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    popup_reset(infrared->popup);\n    infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_gpio_settings.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic const char* infrared_scene_gpio_settings_pin_text[] = {\n    \"Flipper\",\n    \"2 (A7)\",\n    \"Detect\",\n};\n\nstatic const char* infrared_scene_gpio_settings_otg_text[] = {\n    \"OFF\",\n    \"ON\",\n};\n\nstatic void infrared_scene_gpio_settings_pin_change_callback(VariableItem* item) {\n    InfraredApp* infrared = variable_item_get_context(item);\n    const uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[index]);\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher,\n        infrared_custom_event_pack(InfraredCustomEventTypeGpioTxPinChanged, index));\n}\n\nstatic void infrared_scene_gpio_settings_otg_change_callback(VariableItem* item) {\n    InfraredApp* infrared = variable_item_get_context(item);\n    const uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, infrared_scene_gpio_settings_otg_text[index]);\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher,\n        infrared_custom_event_pack(InfraredCustomEventTypeGpioOtgChanged, index));\n}\n\nstatic void infrared_scene_gpio_settings_init(InfraredApp* infrared) {\n    VariableItemList* var_item_list = infrared->var_item_list;\n    VariableItem* item;\n    uint8_t value_index;\n\n    item = variable_item_list_add(\n        var_item_list,\n        \"Signal Output\",\n        COUNT_OF(infrared_scene_gpio_settings_pin_text),\n        infrared_scene_gpio_settings_pin_change_callback,\n        infrared);\n\n    value_index = infrared->app_state.tx_pin;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[value_index]);\n\n    item = variable_item_list_add(\n        var_item_list,\n        \"5V on GPIO\",\n        COUNT_OF(infrared_scene_gpio_settings_otg_text),\n        infrared_scene_gpio_settings_otg_change_callback,\n        infrared);\n\n    if(infrared->app_state.tx_pin < FuriHalInfraredTxPinMax) {\n        value_index = infrared->app_state.is_otg_enabled;\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(\n            item, infrared_scene_gpio_settings_otg_text[value_index]);\n    } else {\n        variable_item_set_values_count(item, 1);\n        variable_item_set_current_value_index(item, 0);\n        variable_item_set_current_value_text(item, \"Auto\");\n    }\n}\n\nvoid infrared_scene_gpio_settings_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    infrared_scene_gpio_settings_init(infrared);\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableList);\n}\n\nbool infrared_scene_gpio_settings_on_event(void* context, SceneManagerEvent event) {\n    bool consumed = false;\n\n    InfraredApp* infrared = context;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint16_t custom_event_type = infrared_custom_event_get_type(event.event);\n        const uint16_t custom_event_value = infrared_custom_event_get_value(event.event);\n\n        if(custom_event_type == InfraredCustomEventTypeGpioTxPinChanged) {\n            infrared_set_tx_pin(infrared, custom_event_value);\n            variable_item_list_reset(infrared->var_item_list);\n            infrared_scene_gpio_settings_init(infrared);\n        } else if(custom_event_type == InfraredCustomEventTypeGpioOtgChanged) {\n            infrared_enable_otg(infrared, custom_event_value);\n        }\n\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_gpio_settings_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    variable_item_list_reset(infrared->var_item_list);\n    infrared_save_settings(infrared);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_learn.c",
    "content": "#include \"../infrared_app_i.h\"\n#include <dolphin/dolphin.h>\n\nvoid infrared_scene_learn_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n    InfraredWorker* worker = infrared->worker;\n\n    infrared_worker_rx_set_received_signal_callback(\n        worker, infrared_signal_received_callback, context);\n    infrared_worker_rx_start(worker);\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartRead);\n\n    popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31);\n    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter);\n    popup_set_text(\n        popup, \"Point the remote at IR port\\nand push the button\", 5, 10, AlignLeft, AlignCenter);\n    popup_set_callback(popup, NULL);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n}\n\nbool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeSignalReceived) {\n            infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);\n            scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);\n            dolphin_deed(DolphinDeedIrLearnSuccess);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_learn_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n    infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);\n    infrared_worker_rx_stop(infrared->worker);\n    infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);\n    popup_set_icon(popup, 0, 0, NULL);\n    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_learn_done.c",
    "content": "#include \"../infrared_app_i.h\"\n\nvoid infrared_scene_learn_done_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Popup* popup = infrared->popup;\n\n    if(infrared->app_state.is_learning_new_remote) {\n        popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);\n        popup_set_header(popup, \"Success!\", 10, 12, AlignLeft, AlignTop);\n    } else {\n        popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n        popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    }\n\n    popup_set_callback(popup, infrared_popup_closed_callback);\n    popup_set_context(popup, context);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n}\n\nbool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypePopupClosed) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   infrared->scene_manager, InfraredSceneRemote)) {\n                scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_learn_done_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    infrared->app_state.is_learning_new_remote = false;\n    popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_learn_enter_name.c",
    "content": "#include \"../infrared_app_i.h\"\n#include <dolphin/dolphin.h>\n\nvoid infrared_scene_learn_enter_name_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    TextInput* text_input = infrared->text_input;\n    InfraredSignal* signal = infrared->current_signal;\n\n    if(infrared_signal_is_raw(signal)) {\n        const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);\n        infrared_text_store_set(infrared, 0, \"RAW_%zu\", raw->timings_size);\n    } else {\n        const InfraredMessage* message = infrared_signal_get_message(signal);\n        infrared_text_store_set(\n            infrared,\n            0,\n            \"%.4s_%0*lX\",\n            infrared_get_protocol_name(message->protocol),\n            ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),\n            message->command);\n    }\n\n    text_input_set_header_text(text_input, \"Name the button\");\n    text_input_set_result_callback(\n        text_input,\n        infrared_text_input_callback,\n        context,\n        infrared->text_store[0],\n        INFRARED_MAX_BUTTON_NAME_LENGTH,\n        true);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);\n}\n\nbool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    InfraredSignal* signal = infrared->current_signal;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeTextEditDone) {\n            const char* signal_name = infrared->text_store[0];\n            const InfraredErrorCode error =\n                infrared->app_state.is_learning_new_remote ?\n                    infrared_add_remote_with_button(infrared, signal_name, signal) :\n                    infrared_remote_append_signal(infrared->remote, signal, signal_name);\n\n            if(!INFRARED_ERROR_PRESENT(error)) {\n                scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);\n                dolphin_deed(DolphinDeedIrSave);\n            } else {\n                infrared_show_error_message(\n                    infrared,\n                    \"Failed to\\n%s\",\n                    infrared->app_state.is_learning_new_remote ? \"create file\" : \"add signal\");\n                const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};\n                scene_manager_search_and_switch_to_previous_scene_one_of(\n                    scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_learn_enter_name_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    UNUSED(infrared);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_learn_success.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic void\n    infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, result);\n}\n\nvoid infrared_scene_learn_success_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    DialogEx* dialog_ex = infrared->dialog_ex;\n    InfraredSignal* signal = infrared->current_signal;\n\n    infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);\n\n    if(infrared_signal_is_raw(signal)) {\n        const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);\n        dialog_ex_set_header(dialog_ex, \"Unknown\", 95, 10, AlignCenter, AlignCenter);\n        infrared_text_store_set(infrared, 0, \"%zu samples\", raw->timings_size);\n        dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop);\n\n    } else {\n        const InfraredMessage* message = infrared_signal_get_message(signal);\n        uint8_t addr_digits =\n            ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);\n        uint8_t cmd_digits =\n            ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4);\n        uint8_t max_digits = MAX(addr_digits, cmd_digits);\n        max_digits = MIN(max_digits, 7);\n        size_t label_x_offset = 63 + (7 - max_digits) * 3;\n\n        infrared_text_store_set(infrared, 0, \"%s\", infrared_get_protocol_name(message->protocol));\n        infrared_text_store_set(\n            infrared,\n            1,\n            \"A: 0x%0*lX\\nC: 0x%0*lX\\n\",\n            addr_digits,\n            message->address,\n            cmd_digits,\n            message->command);\n\n        dialog_ex_set_header(dialog_ex, infrared->text_store[0], 95, 7, AlignCenter, AlignCenter);\n        dialog_ex_set_text(\n            dialog_ex, infrared->text_store[1], label_x_offset, 34, AlignLeft, AlignCenter);\n    }\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Retry\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Save\");\n    dialog_ex_set_center_button_text(dialog_ex, \"Send\");\n    dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);\n    dialog_ex_set_result_callback(dialog_ex, infrared_scene_learn_success_dialog_result_callback);\n    dialog_ex_set_context(dialog_ex, context);\n    dialog_ex_enable_extended_events(dialog_ex);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);\n}\n\nbool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    const bool is_transmitter_idle = !infrared->app_state.is_transmitting;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        if(is_transmitter_idle) {\n            infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);\n        }\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(is_transmitter_idle) {\n            scene_manager_next_scene(scene_manager, InfraredSceneAskBack);\n        }\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            if(is_transmitter_idle) {\n                scene_manager_next_scene(scene_manager, InfraredSceneAskRetry);\n            }\n            consumed = true;\n        } else if(event.event == DialogExResultRight) {\n            if(is_transmitter_idle) {\n                scene_manager_next_scene(scene_manager, InfraredSceneLearnEnterName);\n            }\n            consumed = true;\n        } else if(event.event == DialogExPressCenter) {\n            infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);\n            infrared_tx_start(infrared);\n            consumed = true;\n        } else if(event.event == DialogExReleaseCenter) {\n            infrared_tx_stop(infrared);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_learn_success_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    dialog_ex_reset(infrared->dialog_ex);\n    infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_remote.c",
    "content": "#include \"../infrared_app_i.h\"\n\ntypedef enum {\n    ButtonIndexLearn = -2,\n    ButtonIndexEdit = -1,\n    ButtonIndexNA = 0,\n} ButtonIndex;\n\nstatic void\n    infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {\n    InfraredApp* infrared = context;\n\n    uint16_t custom_type;\n    if(type == InputTypePress) {\n        custom_type = InfraredCustomEventTypeTransmitStarted;\n    } else if(type == InputTypeRelease) {\n        custom_type = InfraredCustomEventTypeTransmitStopped;\n    } else if(type == InputTypeShort) {\n        custom_type = InfraredCustomEventTypeMenuSelected;\n    } else {\n        furi_crash(\"Unexpected input type\");\n    }\n\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, infrared_custom_event_pack(custom_type, index));\n}\n\nvoid infrared_scene_remote_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    InfraredRemote* remote = infrared->remote;\n    ButtonMenu* button_menu = infrared->button_menu;\n    SceneManager* scene_manager = infrared->scene_manager;\n\n    for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) {\n        button_menu_add_item(\n            button_menu,\n            infrared_remote_get_signal_name(remote, i),\n            i,\n            infrared_scene_remote_button_menu_callback,\n            ButtonMenuItemTypeCommon,\n            context);\n    }\n\n    button_menu_add_item(\n        button_menu,\n        \"+\",\n        ButtonIndexLearn,\n        infrared_scene_remote_button_menu_callback,\n        ButtonMenuItemTypeControl,\n        context);\n    button_menu_add_item(\n        button_menu,\n        \"Edit\",\n        ButtonIndexEdit,\n        infrared_scene_remote_button_menu_callback,\n        ButtonMenuItemTypeControl,\n        context);\n\n    button_menu_set_header(button_menu, infrared_remote_get_name(remote));\n    const int16_t button_index =\n        (signed)scene_manager_get_scene_state(scene_manager, InfraredSceneRemote);\n    button_menu_set_selected_item(button_menu, button_index);\n    scene_manager_set_scene_state(scene_manager, InfraredSceneRemote, ButtonIndexNA);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewButtonMenu);\n}\n\nbool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    const bool is_transmitter_idle = !infrared->app_state.is_transmitting;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        if(is_transmitter_idle) {\n            const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};\n            consumed = scene_manager_search_and_switch_to_previous_scene_one_of(\n                scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n        } else {\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        const uint16_t custom_type = infrared_custom_event_get_type(event.event);\n        const int16_t button_index = infrared_custom_event_get_value(event.event);\n\n        if(custom_type == InfraredCustomEventTypeTransmitStarted) {\n            furi_assert(button_index >= 0);\n            InfraredErrorCode error = infrared_tx_start_button_index(infrared, button_index);\n            if(INFRARED_ERROR_PRESENT(error)) {\n                infrared_show_error_message(\n                    infrared,\n                    \"Failed to load\\n\\\"%s\\\"\",\n                    infrared_remote_get_signal_name(infrared->remote, button_index));\n            }\n            consumed = true;\n        } else if(custom_type == InfraredCustomEventTypeTransmitStopped) {\n            infrared_tx_stop(infrared);\n            consumed = true;\n        } else if(custom_type == InfraredCustomEventTypeMenuSelected) {\n            furi_assert(button_index < 0);\n            if(is_transmitter_idle) {\n                scene_manager_set_scene_state(\n                    scene_manager, InfraredSceneRemote, (unsigned)button_index);\n                if(button_index == ButtonIndexLearn) {\n                    infrared->app_state.is_learning_new_remote = false;\n                    scene_manager_next_scene(scene_manager, InfraredSceneLearn);\n                    consumed = true;\n                } else if(button_index == ButtonIndexEdit) {\n                    scene_manager_next_scene(scene_manager, InfraredSceneEdit);\n                    consumed = true;\n                }\n\n            } else {\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_remote_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    button_menu_reset(infrared->button_menu);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_remote_list.c",
    "content": "#include \"../infrared_app_i.h\"\n\nstatic int32_t infrared_scene_remote_list_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    const InfraredErrorCode error =\n        infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path));\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);\n    return error;\n}\n\nstatic void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) {\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);\n    browser_options.base_path = INFRARED_APP_FOLDER;\n\n    const bool file_selected = dialog_file_browser_show(\n        infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);\n\n    if(file_selected) {\n        // Load the remote in a separate thread\n        infrared_blocking_task_start(infrared, infrared_scene_remote_list_task_callback);\n\n    } else {\n        scene_manager_previous_scene(infrared->scene_manager);\n    }\n}\n\nvoid infrared_scene_remote_list_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    infrared_scene_remote_list_select_and_load(infrared);\n}\n\nbool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == InfraredCustomEventTypeTaskFinished) {\n            const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n\n            if(!INFRARED_ERROR_PRESENT(task_error)) {\n                scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);\n            } else {\n                bool wrong_file_type =\n                    INFRARED_ERROR_CHECK(task_error, InfraredErrorCodeWrongFileType);\n                const char* format = wrong_file_type ?\n                                         \"Library file\\n\\\"%s\\\" can't be openned as a remote\" :\n                                         \"Failed to load\\n\\\"%s\\\"\";\n\n                infrared_show_error_message(\n                    infrared, format, furi_string_get_cstr(infrared->file_path));\n                infrared_scene_remote_list_select_and_load(infrared);\n            }\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_remote_list_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_rpc.c",
    "content": "#include \"../infrared_app_i.h\"\n#include <gui/canvas.h>\n\n#define TAG \"InfraredApp\"\n\ntypedef enum {\n    InfraredRpcStateIdle,\n    InfraredRpcStateLoaded,\n    InfraredRpcStateSending,\n} InfraredRpcState;\n\nstatic int32_t infrared_scene_rpc_task_callback(void* context) {\n    InfraredApp* infrared = context;\n    const InfraredErrorCode error =\n        infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path));\n    view_dispatcher_send_custom_event(\n        infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished);\n    return error;\n}\n\nvoid infrared_scene_rpc_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle);\n}\n\nstatic void infrared_scene_rpc_show(InfraredApp* infrared) {\n    Popup* popup = infrared->popup;\n\n    popup_set_header(popup, \"Infrared\", 89, 42, AlignCenter, AlignBottom);\n    popup_set_text(popup, \"RPC mode\", 89, 44, AlignCenter, AlignTop);\n    popup_set_text(popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);\n\n    popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);\n\n    popup_set_context(popup, infrared);\n    popup_set_callback(popup, infrared_popup_closed_callback);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);\n    scene_manager_set_scene_state(\n        infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);\n    notification_message(infrared->notifications, &sequence_display_backlight_on);\n}\n\nbool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        InfraredAppState* app_state = &infrared->app_state;\n        InfraredRpcState rpc_state =\n            scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc);\n\n        if(event.event == InfraredCustomEventTypeRpcLoadFile) {\n            if(rpc_state == InfraredRpcStateIdle) {\n                // Load the remote in a separate thread\n                infrared_blocking_task_start(infrared, infrared_scene_rpc_task_callback);\n            }\n\n        } else if(event.event == InfraredCustomEventTypeTaskFinished) {\n            const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared);\n\n            if(!INFRARED_ERROR_PRESENT(task_error)) {\n                const char* remote_name = infrared_remote_get_name(infrared->remote);\n                infrared_text_store_set(infrared, 0, \"loaded\\n%s\", remote_name);\n                scene_manager_set_scene_state(\n                    infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);\n            } else {\n                FuriString* str = furi_string_alloc();\n                furi_string_printf(\n                    str, \"Failed to load\\n%s\", furi_string_get_cstr(infrared->file_path));\n\n                rpc_system_app_set_error_code(infrared->rpc_ctx, RpcAppSystemErrorCodeParseFile);\n                rpc_system_app_set_error_text(infrared->rpc_ctx, furi_string_get_cstr(str));\n\n                furi_string_free(str);\n            }\n            rpc_system_app_confirm(infrared->rpc_ctx, !INFRARED_ERROR_PRESENT(task_error));\n        } else if(\n            event.event == InfraredCustomEventTypeRpcButtonPressName ||\n            event.event == InfraredCustomEventTypeRpcButtonPressIndex) {\n            bool result = false;\n            if(rpc_state == InfraredRpcStateLoaded) {\n                if(event.event == InfraredCustomEventTypeRpcButtonPressName) {\n                    const char* button_name = furi_string_get_cstr(infrared->button_name);\n                    size_t index;\n                    const bool index_found =\n                        infrared_remote_get_signal_index(infrared->remote, button_name, &index);\n                    app_state->current_button_index = index_found ? (signed)index :\n                                                                    InfraredButtonIndexNone;\n                    FURI_LOG_D(TAG, \"Sending signal with name \\\"%s\\\"\", button_name);\n                } else {\n                    FURI_LOG_D(\n                        TAG, \"Sending signal with index \\\"%ld\\\"\", app_state->current_button_index);\n                }\n                if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {\n                    InfraredErrorCode error =\n                        infrared_tx_start_button_index(infrared, app_state->current_button_index);\n                    if(!INFRARED_ERROR_PRESENT(error)) {\n                        const char* remote_name = infrared_remote_get_name(infrared->remote);\n                        infrared_text_store_set(infrared, 0, \"emulating\\n%s\", remote_name);\n\n                        infrared_scene_rpc_show(infrared);\n                        result = true;\n                    } else {\n                        rpc_system_app_set_error_code(\n                            infrared->rpc_ctx, RpcAppSystemErrorCodeInternalParse);\n                        rpc_system_app_set_error_text(\n                            infrared->rpc_ctx, \"Cannot load button data\");\n                        result = false;\n                    }\n                }\n            }\n            rpc_system_app_confirm(infrared->rpc_ctx, result);\n\n        } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {\n            bool result = false;\n\n            if(rpc_state == InfraredRpcStateSending) {\n                infrared_tx_stop(infrared);\n                result = true;\n                scene_manager_set_scene_state(\n                    infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);\n            }\n\n            rpc_system_app_confirm(infrared->rpc_ctx, result);\n\n        } else if(\n            event.event == InfraredCustomEventTypeRpcButtonPressReleaseName ||\n            event.event == InfraredCustomEventTypeRpcButtonPressReleaseIndex) {\n            bool result = false;\n\n            // Send the signal once and stop\n            if(rpc_state == InfraredRpcStateLoaded) {\n                if(event.event == InfraredCustomEventTypeRpcButtonPressReleaseName) {\n                    const char* button_name = furi_string_get_cstr(infrared->button_name);\n                    size_t index;\n                    const bool index_found =\n                        infrared_remote_get_signal_index(infrared->remote, button_name, &index);\n                    app_state->current_button_index = index_found ? (signed)index :\n                                                                    InfraredButtonIndexNone;\n                    FURI_LOG_D(TAG, \"Sending signal with name \\\"%s\\\"\", button_name);\n                } else {\n                    FURI_LOG_D(\n                        TAG, \"Sending signal with index \\\"%ld\\\"\", app_state->current_button_index);\n                }\n                if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {\n                    InfraredErrorCode error = infrared_tx_send_once_button_index(\n                        infrared, app_state->current_button_index);\n                    if(!INFRARED_ERROR_PRESENT(error)) {\n                        const char* remote_name = infrared_remote_get_name(infrared->remote);\n                        infrared_text_store_set(infrared, 0, \"emulating\\n%s\", remote_name);\n\n                        infrared_scene_rpc_show(infrared);\n                        result = true;\n                    } else {\n                        rpc_system_app_set_error_code(\n                            infrared->rpc_ctx, RpcAppSystemErrorCodeInternalParse);\n                        rpc_system_app_set_error_text(\n                            infrared->rpc_ctx, \"Cannot load button data\");\n                        result = false;\n                    }\n                }\n            }\n\n            if(result) {\n                scene_manager_set_scene_state(\n                    infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);\n            }\n            rpc_system_app_confirm(infrared->rpc_ctx, result);\n        } else if(\n            event.event == InfraredCustomEventTypeRpcExit ||\n            event.event == InfraredCustomEventTypeRpcSessionClose ||\n            event.event == InfraredCustomEventTypePopupClosed) {\n            scene_manager_stop(infrared->scene_manager);\n            view_dispatcher_stop(infrared->view_dispatcher);\n\n            if(event.event == InfraredCustomEventTypeRpcExit) {\n                rpc_system_app_confirm(infrared->rpc_ctx, true);\n            }\n        }\n\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_rpc_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==\n       InfraredRpcStateSending) {\n        infrared_tx_stop(infrared);\n    }\n\n    popup_reset(infrared->popup);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_start.c",
    "content": "#include \"../infrared_app_i.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexUniversalRemotes,\n    SubmenuIndexLearnNewRemote,\n    SubmenuIndexSavedRemotes,\n    SubmenuIndexGpioSettings,\n    SubmenuIndexDebug\n};\n\nstatic void infrared_scene_start_submenu_callback(void* context, uint32_t index) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, index);\n}\n\nvoid infrared_scene_start_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Submenu* submenu = infrared->submenu;\n    SceneManager* scene_manager = infrared->scene_manager;\n\n    submenu_add_item(\n        submenu,\n        \"Universal Remotes\",\n        SubmenuIndexUniversalRemotes,\n        infrared_scene_start_submenu_callback,\n        infrared);\n    submenu_add_item(\n        submenu,\n        \"Learn New Remote\",\n        SubmenuIndexLearnNewRemote,\n        infrared_scene_start_submenu_callback,\n        infrared);\n    submenu_add_item(\n        submenu,\n        \"Saved Remotes\",\n        SubmenuIndexSavedRemotes,\n        infrared_scene_start_submenu_callback,\n        infrared);\n    submenu_add_item(\n        submenu,\n        \"GPIO Settings\",\n        SubmenuIndexGpioSettings,\n        infrared_scene_start_submenu_callback,\n        infrared);\n\n    if(infrared->app_state.is_debug_enabled) {\n        submenu_add_item(\n            submenu, \"Debug\", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);\n    }\n\n    const uint32_t submenu_index =\n        scene_manager_get_scene_state(scene_manager, InfraredSceneStart);\n    submenu_set_selected_item(submenu, submenu_index);\n    scene_manager_set_scene_state(scene_manager, InfraredSceneStart, SubmenuIndexUniversalRemotes);\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);\n}\n\nbool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t submenu_index = event.event;\n        scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);\n        if(submenu_index == SubmenuIndexUniversalRemotes) {\n            scene_manager_next_scene(scene_manager, InfraredSceneUniversal);\n        } else if(submenu_index == SubmenuIndexLearnNewRemote) {\n            infrared->app_state.is_learning_new_remote = true;\n            scene_manager_next_scene(scene_manager, InfraredSceneLearn);\n        } else if(submenu_index == SubmenuIndexSavedRemotes) {\n            furi_string_set(infrared->file_path, INFRARED_APP_FOLDER);\n            scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);\n        } else if(submenu_index == SubmenuIndexGpioSettings) {\n            scene_manager_next_scene(scene_manager, InfraredSceneGpioSettings);\n        } else if(submenu_index == SubmenuIndexDebug) {\n            scene_manager_next_scene(scene_manager, InfraredSceneDebug);\n        }\n\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_start_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    submenu_reset(infrared->submenu);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_universal.c",
    "content": "#include \"../infrared_app_i.h\"\n\ntypedef enum {\n    SubmenuIndexUniversalTV,\n    SubmenuIndexUniversalAC,\n    SubmenuIndexUniversalAudio,\n    SubmenuIndexUniversalProjector,\n} SubmenuIndex;\n\nstatic void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {\n    InfraredApp* infrared = context;\n    view_dispatcher_send_custom_event(infrared->view_dispatcher, index);\n}\n\nvoid infrared_scene_universal_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    Submenu* submenu = infrared->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"TVs\",\n        SubmenuIndexUniversalTV,\n        infrared_scene_universal_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Audio Players\",\n        SubmenuIndexUniversalAudio,\n        infrared_scene_universal_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Projectors\",\n        SubmenuIndexUniversalProjector,\n        infrared_scene_universal_submenu_callback,\n        context);\n    submenu_add_item(\n        submenu,\n        \"Air Conditioners\",\n        SubmenuIndexUniversalAC,\n        infrared_scene_universal_submenu_callback,\n        context);\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal));\n\n    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);\n}\n\nbool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {\n    InfraredApp* infrared = context;\n    SceneManager* scene_manager = infrared->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexUniversalTV) {\n            scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV);\n            consumed = true;\n        } else if(event.event == SubmenuIndexUniversalAC) {\n            scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);\n            consumed = true;\n        } else if(event.event == SubmenuIndexUniversalAudio) {\n            scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio);\n            consumed = true;\n        } else if(event.event == SubmenuIndexUniversalProjector) {\n            scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event);\n    }\n\n    return consumed;\n}\n\nvoid infrared_scene_universal_on_exit(void* context) {\n    InfraredApp* infrared = context;\n    submenu_reset(infrared->submenu);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_universal_ac.c",
    "content": "#include \"../infrared_app_i.h\" // IWYU pragma: keep\n\n#include \"common/infrared_scene_universal_common.h\"\n\nvoid infrared_scene_universal_ac_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    ButtonPanel* button_panel = infrared->button_panel;\n    InfraredBruteForce* brute_force = infrared->brute_force;\n\n    infrared_brute_force_set_db_filename(brute_force, EXT_PATH(\"infrared/assets/ac.ir\"));\n\n    button_panel_reserve(button_panel, 2, 3);\n    uint32_t i = 0;\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        0,\n        6,\n        15,\n        &I_off_19x20,\n        &I_off_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 10, 37, &I_off_text_12x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Off\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        0,\n        39,\n        15,\n        &I_dry_19x20,\n        &I_dry_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 41, 37, &I_dry_text_15x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Dh\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        1,\n        3,\n        49,\n        &I_max_24x23,\n        &I_max_hover_24x23,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Cool_hi\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        1,\n        37,\n        49,\n        &I_max_24x23,\n        &I_max_hover_24x23,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Heat_hi\");\n    if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {\n        button_panel_add_item(\n            button_panel,\n            i,\n            0,\n            2,\n            3,\n            100,\n            &I_celsius_24x23,\n            &I_celsius_hover_24x23,\n            infrared_scene_universal_common_item_callback,\n            context);\n    } else {\n        button_panel_add_item(\n            button_panel,\n            i,\n            0,\n            2,\n            3,\n            100,\n            &I_fahren_24x23,\n            &I_fahren_hover_24x23,\n            infrared_scene_universal_common_item_callback,\n            context);\n    }\n    infrared_brute_force_add_record(brute_force, i++, \"Cool_lo\");\n\n    if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {\n        button_panel_add_item(\n            button_panel,\n            i,\n            1,\n            2,\n            37,\n            100,\n            &I_celsius_24x23,\n            &I_celsius_hover_24x23,\n            infrared_scene_universal_common_item_callback,\n            context);\n    } else {\n        button_panel_add_item(\n            button_panel,\n            i,\n            1,\n            2,\n            37,\n            100,\n            &I_fahren_24x23,\n            &I_fahren_hover_24x23,\n            infrared_scene_universal_common_item_callback,\n            context);\n    }\n    infrared_brute_force_add_record(brute_force, i++, \"Heat_lo\");\n\n    button_panel_add_icon(button_panel, 0, 60, &I_cool_30x51);\n    button_panel_add_icon(button_panel, 34, 60, &I_heat_30x51);\n\n    button_panel_add_label(button_panel, 24, 10, FontPrimary, \"AC\");\n\n    infrared_scene_universal_common_on_enter(context);\n}\n\nbool infrared_scene_universal_ac_on_event(void* context, SceneManagerEvent event) {\n    return infrared_scene_universal_common_on_event(context, event);\n}\n\nvoid infrared_scene_universal_ac_on_exit(void* context) {\n    infrared_scene_universal_common_on_exit(context);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_universal_audio.c",
    "content": "#include \"../infrared_app_i.h\"\n\n#include \"common/infrared_scene_universal_common.h\"\n\nvoid infrared_scene_universal_audio_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    ButtonPanel* button_panel = infrared->button_panel;\n    InfraredBruteForce* brute_force = infrared->brute_force;\n\n    infrared_brute_force_set_db_filename(brute_force, EXT_PATH(\"infrared/assets/audio.ir\"));\n\n    button_panel_reserve(button_panel, 2, 4);\n    uint32_t i = 0;\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        0,\n        6,\n        13,\n        &I_power_19x20,\n        &I_power_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 4, 35, &I_power_text_24x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Power\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        0,\n        39,\n        13,\n        &I_mute_19x20,\n        &I_mute_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 39, 35, &I_mute_text_19x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Mute\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        1,\n        6,\n        42,\n        &I_play_19x20,\n        &I_play_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 6, 64, &I_play_text_19x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Play\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        2,\n        6,\n        71,\n        &I_pause_19x20,\n        &I_pause_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 4, 93, &I_pause_text_23x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Pause\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        3,\n        6,\n        101,\n        &I_prev_19x20,\n        &I_prev_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 6, 123, &I_prev_text_19x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Prev\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        3,\n        39,\n        101,\n        &I_next_19x20,\n        &I_next_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 39, 123, &I_next_text_19x6);\n    infrared_brute_force_add_record(brute_force, i++, \"Next\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        2,\n        37,\n        77,\n        &I_voldown_24x21,\n        &I_voldown_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_dn\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        1,\n        37,\n        43,\n        &I_volup_24x21,\n        &I_volup_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_up\");\n\n    button_panel_add_label(button_panel, 1, 10, FontPrimary, \"Audio player\");\n    button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30);\n\n    infrared_scene_universal_common_on_enter(context);\n}\n\nbool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) {\n    return infrared_scene_universal_common_on_event(context, event);\n}\n\nvoid infrared_scene_universal_audio_on_exit(void* context) {\n    infrared_scene_universal_common_on_exit(context);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_universal_projector.c",
    "content": "#include \"../infrared_app_i.h\"\n\n#include \"common/infrared_scene_universal_common.h\"\n\nvoid infrared_scene_universal_projector_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    ButtonPanel* button_panel = infrared->button_panel;\n    InfraredBruteForce* brute_force = infrared->brute_force;\n\n    infrared_brute_force_set_db_filename(brute_force, EXT_PATH(\"infrared/assets/projector.ir\"));\n\n    button_panel_reserve(button_panel, 2, 3);\n    uint32_t i = 0;\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        0,\n        6,\n        23,\n        &I_power_19x20,\n        &I_power_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 4, 45, &I_power_text_24x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Power\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        0,\n        39,\n        23,\n        &I_mute_19x20,\n        &I_mute_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 39, 45, &I_mute_text_19x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Mute\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        1,\n        20,\n        59,\n        &I_volup_24x21,\n        &I_volup_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_up\");\n\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        2,\n        20,\n        93,\n        &I_voldown_24x21,\n        &I_voldown_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_dn\");\n\n    button_panel_add_label(button_panel, 10, 11, FontPrimary, \"Projector\");\n    button_panel_add_icon(button_panel, 17, 72, &I_vol_ac_text_30x30);\n\n    infrared_scene_universal_common_on_enter(context);\n}\n\nbool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) {\n    return infrared_scene_universal_common_on_event(context, event);\n}\n\nvoid infrared_scene_universal_projector_on_exit(void* context) {\n    infrared_scene_universal_common_on_exit(context);\n}\n"
  },
  {
    "path": "applications/main/infrared/scenes/infrared_scene_universal_tv.c",
    "content": "#include \"../infrared_app_i.h\"\n\n#include \"common/infrared_scene_universal_common.h\"\n\nvoid infrared_scene_universal_tv_on_enter(void* context) {\n    InfraredApp* infrared = context;\n    ButtonPanel* button_panel = infrared->button_panel;\n    InfraredBruteForce* brute_force = infrared->brute_force;\n\n    infrared_brute_force_set_db_filename(brute_force, EXT_PATH(\"infrared/assets/tv.ir\"));\n\n    button_panel_reserve(button_panel, 2, 3);\n    uint32_t i = 0;\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        0,\n        6,\n        16,\n        &I_power_19x20,\n        &I_power_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 4, 38, &I_power_text_24x5);\n    infrared_brute_force_add_record(brute_force, i++, \"Power\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        0,\n        39,\n        16,\n        &I_mute_19x20,\n        &I_mute_hover_19x20,\n        infrared_scene_universal_common_item_callback,\n        context);\n    button_panel_add_icon(button_panel, 39, 38, &I_mute_text_19x5);\n\n    button_panel_add_icon(button_panel, 0, 66, &I_ch_text_31x34);\n    button_panel_add_icon(button_panel, 35, 66, &I_vol_tv_text_29x34);\n\n    infrared_brute_force_add_record(brute_force, i++, \"Mute\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        1,\n        38,\n        53,\n        &I_volup_24x21,\n        &I_volup_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_up\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        1,\n        3,\n        53,\n        &I_ch_up_24x21,\n        &I_ch_up_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Ch_next\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        1,\n        2,\n        38,\n        91,\n        &I_voldown_24x21,\n        &I_voldown_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Vol_dn\");\n    button_panel_add_item(\n        button_panel,\n        i,\n        0,\n        2,\n        3,\n        91,\n        &I_ch_down_24x21,\n        &I_ch_down_hover_24x21,\n        infrared_scene_universal_common_item_callback,\n        context);\n    infrared_brute_force_add_record(brute_force, i++, \"Ch_prev\");\n\n    button_panel_add_label(button_panel, 25, 10, FontPrimary, \"TV\");\n\n    infrared_scene_universal_common_on_enter(context);\n}\n\nbool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) {\n    return infrared_scene_universal_common_on_event(context, event);\n}\n\nvoid infrared_scene_universal_tv_on_exit(void* context) {\n    infrared_scene_universal_common_on_exit(context);\n}\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_debug_view.c",
    "content": "#include \"infrared_debug_view.h\"\n\n#include <gui/canvas.h>\n#include <gui/elements.h>\n\n#include <stdlib.h>\n#include <string.h>\n\n#define INFRARED_DEBUG_TEXT_LENGTH 64\n\nstruct InfraredDebugView {\n    View* view;\n};\n\ntypedef struct {\n    char text[INFRARED_DEBUG_TEXT_LENGTH];\n} InfraredDebugViewModel;\n\nstatic void infrared_debug_view_draw_callback(Canvas* canvas, void* model) {\n    InfraredDebugViewModel* debug_view_model = model;\n\n    canvas_clear(canvas);\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, \"INFRARED monitor\\n\");\n    canvas_set_font(canvas, FontKeyboard);\n\n    if(strlen(debug_view_model->text)) {\n        elements_multiline_text_aligned(\n            canvas, 64, 43, AlignCenter, AlignCenter, debug_view_model->text);\n    }\n}\n\nInfraredDebugView* infrared_debug_view_alloc(void) {\n    InfraredDebugView* debug_view = malloc(sizeof(InfraredDebugView));\n    debug_view->view = view_alloc();\n    view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(InfraredDebugViewModel));\n    view_set_draw_callback(debug_view->view, infrared_debug_view_draw_callback);\n    view_set_context(debug_view->view, debug_view);\n    return debug_view;\n}\nvoid infrared_debug_view_free(InfraredDebugView* debug_view) {\n    view_free(debug_view->view);\n    free(debug_view);\n}\n\nView* infrared_debug_view_get_view(InfraredDebugView* debug_view) {\n    return debug_view->view;\n}\n\nvoid infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n\n    InfraredDebugViewModel* model = view_get_model(debug_view->view);\n    vsnprintf(model->text, INFRARED_DEBUG_TEXT_LENGTH, fmt, args);\n    view_commit_model(debug_view->view, true);\n\n    va_end(args);\n}\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_debug_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct InfraredDebugView InfraredDebugView;\n\nInfraredDebugView* infrared_debug_view_alloc(void);\nvoid infrared_debug_view_free(InfraredDebugView* debug_view);\n\nView* infrared_debug_view_get_view(InfraredDebugView* debug_view);\nvoid infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...);\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_move_view.c",
    "content": "#include \"infrared_move_view.h\"\n\n#include <m-array.h>\n\n#include <gui/canvas.h>\n#include <gui/elements.h>\n\n#include <toolbox/m_cstr_dup.h>\n\n#define LIST_ITEMS    4U\n#define LIST_LINE_H   13U\n#define HEADER_H      12U\n#define MOVE_X_OFFSET 5U\n\nstruct InfraredMoveView {\n    View* view;\n    InfraredMoveCallback callback;\n    void* callback_context;\n};\n\nARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575\n\ntypedef struct {\n    InfraredMoveViewItemArray_t labels;\n    int32_t list_offset;\n    int32_t current_idx;\n    int32_t start_idx;\n    bool is_moving;\n} InfraredMoveViewModel;\n\nstatic void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {\n    InfraredMoveViewModel* model = _model;\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(\n        canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, \"Select a Button to Move\");\n\n    const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);\n    const bool show_scrollbar = btn_number > LIST_ITEMS;\n\n    canvas_set_font(canvas, FontSecondary);\n\n    for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) {\n        int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U);\n        uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0;\n        uint8_t y_offset = HEADER_H + i * LIST_LINE_H;\n        uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1);\n\n        canvas_set_color(canvas, ColorBlack);\n        if(model->current_idx == idx) {\n            canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H);\n\n            canvas_set_color(canvas, ColorWhite);\n            canvas_draw_dot(canvas, x_offset, y_offset);\n            canvas_draw_dot(canvas, x_offset + 1, y_offset);\n            canvas_draw_dot(canvas, x_offset, y_offset + 1);\n            canvas_draw_dot(canvas, x_offset, y_offset + LIST_LINE_H - 1);\n            canvas_draw_dot(canvas, box_end_x - 1, y_offset);\n            canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1);\n        }\n        canvas_draw_str_aligned(\n            canvas,\n            x_offset + 3,\n            y_offset + 3,\n            AlignLeft,\n            AlignTop,\n            *InfraredMoveViewItemArray_cget(model->labels, idx));\n    }\n\n    if(show_scrollbar) {\n        elements_scrollbar_pos(\n            canvas,\n            canvas_width(canvas),\n            HEADER_H,\n            canvas_height(canvas) - HEADER_H,\n            model->current_idx,\n            btn_number);\n    }\n}\n\nstatic void update_list_offset(InfraredMoveViewModel* model) {\n    const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);\n    const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number;\n\n    if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) {\n        model->list_offset = model->current_idx - (LIST_ITEMS - 1);\n    } else if(model->list_offset < model->current_idx - bounds) {\n        model->list_offset =\n            CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0);\n    } else if(model->list_offset > model->current_idx - bounds) {\n        model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0);\n    }\n}\n\nstatic bool infrared_move_view_input_callback(InputEvent* event, void* context) {\n    InfraredMoveView* move_view = context;\n\n    bool consumed = false;\n\n    if((event->type == InputTypeShort || event->type == InputTypeRepeat) &&\n       ((event->key == InputKeyUp) || (event->key == InputKeyDown))) {\n        with_view_model(\n            move_view->view,\n            InfraredMoveViewModel * model,\n            {\n                const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);\n                const int32_t item_idx_prev = model->current_idx;\n\n                if(event->key == InputKeyUp) {\n                    if(model->current_idx <= 0) {\n                        model->current_idx = btn_number;\n                    }\n                    model->current_idx--;\n\n                } else if(event->key == InputKeyDown) {\n                    model->current_idx++;\n                    if(model->current_idx >= (int32_t)(btn_number)) {\n                        model->current_idx = 0;\n                    }\n                }\n\n                if(model->is_moving) {\n                    InfraredMoveViewItemArray_swap_at(\n                        model->labels, item_idx_prev, model->current_idx);\n                }\n\n                update_list_offset(model);\n            },\n            true);\n\n        consumed = true;\n\n    } else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {\n        with_view_model(\n            move_view->view,\n            InfraredMoveViewModel * model,\n            {\n                if(!model->is_moving) {\n                    model->start_idx = model->current_idx;\n                } else if(move_view->callback) {\n                    move_view->callback(\n                        model->start_idx, model->current_idx, move_view->callback_context);\n                }\n                model->is_moving = !(model->is_moving);\n            },\n            true);\n\n        consumed = true;\n\n    } else if(event->key == InputKeyBack) {\n        with_view_model(\n            move_view->view,\n            InfraredMoveViewModel * model,\n            {\n                if(model->is_moving && move_view->callback) {\n                    move_view->callback(\n                        model->start_idx, model->current_idx, move_view->callback_context);\n                }\n                model->is_moving = false;\n            },\n            false);\n\n        // Not consuming, Back event is passed thru\n    }\n\n    return consumed;\n}\n\nvoid infrared_move_view_set_callback(\n    InfraredMoveView* move_view,\n    InfraredMoveCallback callback,\n    void* context) {\n    furi_assert(move_view);\n    move_view->callback = callback;\n    move_view->callback_context = context;\n}\n\nvoid infrared_move_view_add_item(InfraredMoveView* move_view, const char* label) {\n    with_view_model(\n        move_view->view,\n        InfraredMoveViewModel * model,\n        { InfraredMoveViewItemArray_push_back(model->labels, label); },\n        true);\n}\n\nvoid infrared_move_view_reset(InfraredMoveView* move_view) {\n    with_view_model(\n        move_view->view,\n        InfraredMoveViewModel * model,\n        {\n            InfraredMoveViewItemArray_reset(model->labels);\n            model->list_offset = 0;\n            model->start_idx = 0;\n            model->current_idx = 0;\n            model->is_moving = false;\n        },\n        false);\n    move_view->callback_context = NULL;\n}\n\nInfraredMoveView* infrared_move_view_alloc(void) {\n    InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView));\n\n    move_view->view = view_alloc();\n    view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel));\n    view_set_draw_callback(move_view->view, infrared_move_view_draw_callback);\n    view_set_input_callback(move_view->view, infrared_move_view_input_callback);\n    view_set_context(move_view->view, move_view);\n\n    with_view_model(\n        move_view->view,\n        InfraredMoveViewModel * model,\n        { InfraredMoveViewItemArray_init(model->labels); },\n        true);\n\n    return move_view;\n}\n\nvoid infrared_move_view_free(InfraredMoveView* move_view) {\n    with_view_model(\n        move_view->view,\n        InfraredMoveViewModel * model,\n        { InfraredMoveViewItemArray_clear(model->labels); },\n        true);\n\n    view_free(move_view->view);\n    free(move_view);\n}\n\nView* infrared_move_view_get_view(InfraredMoveView* move_view) {\n    return move_view->view;\n}\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_move_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct InfraredMoveView InfraredMoveView;\n\ntypedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context);\n\nInfraredMoveView* infrared_move_view_alloc(void);\n\nvoid infrared_move_view_free(InfraredMoveView* debug_view);\n\nView* infrared_move_view_get_view(InfraredMoveView* debug_view);\n\nvoid infrared_move_view_set_callback(\n    InfraredMoveView* move_view,\n    InfraredMoveCallback callback,\n    void* context);\n\nvoid infrared_move_view_add_item(InfraredMoveView* move_view, const char* label);\n\nvoid infrared_move_view_reset(InfraredMoveView* move_view);\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_progress_view.c",
    "content": "#include \"infrared_progress_view.h\"\n\n#include <assets_icons.h>\n#include <gui/canvas.h>\n#include <gui/view.h>\n#include <gui/elements.h>\n#include <gui/modules/button_panel.h>\n#include <input/input.h>\n\n#include <furi.h>\n#include <furi_hal_resources.h>\n#include <core/check.h>\n#include <stdint.h>\n\nstruct InfraredProgressView {\n    View* view;\n    InfraredProgressViewInputCallback input_callback;\n    void* context;\n};\n\ntypedef struct {\n    size_t progress;\n    size_t progress_total;\n    bool is_paused;\n} InfraredProgressViewModel;\n\nstatic void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) {\n    InfraredProgressViewModel* model = (InfraredProgressViewModel*)_model;\n\n    uint8_t x = 0;\n    uint8_t y = 25;\n    uint8_t width = 63;\n    uint8_t height = 81;\n\n    elements_bold_rounded_frame(canvas, x, y, width, height);\n\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(\n        canvas,\n        x + 32,\n        y + 9,\n        AlignCenter,\n        AlignCenter,\n        model->is_paused ? \"Paused\" : \"Sending...\");\n\n    float progress_value = (float)model->progress / model->progress_total;\n    elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value);\n\n    char progress_string[16] = {0};\n    if(model->is_paused) {\n        snprintf(\n            progress_string,\n            sizeof(progress_string),\n            \"%zu/%zu\",\n            model->progress,\n            model->progress_total);\n    } else {\n        uint8_t percent_value = 100 * model->progress / model->progress_total;\n        snprintf(progress_string, sizeof(progress_string), \"%d%%\", percent_value);\n    }\n    elements_multiline_text_aligned(\n        canvas, x + 33, y + 37, AlignCenter, AlignCenter, progress_string);\n\n    uint8_t buttons_x = x + (model->is_paused ? 10 : 14);\n    uint8_t buttons_y = y + (model->is_paused ? 46 : 50);\n\n    canvas_draw_icon(canvas, buttons_x + 0, buttons_y + 0, &I_Pin_back_arrow_10x8);\n    canvas_draw_str(canvas, buttons_x + 14, buttons_y + 8, model->is_paused ? \"resume\" : \"stop\");\n\n    canvas_draw_icon(canvas, buttons_x + 1, buttons_y + 10, &I_Ok_btn_9x9);\n    canvas_draw_str(canvas, buttons_x + 14, buttons_y + 17, model->is_paused ? \"send 1\" : \"pause\");\n\n    if(model->is_paused) {\n        canvas_draw_icon(canvas, buttons_x + 2, buttons_y + 21, &I_ButtonLeftSmall_3x5);\n        canvas_draw_icon(canvas, buttons_x + 7, buttons_y + 21, &I_ButtonRightSmall_3x5);\n        canvas_draw_str(canvas, buttons_x + 14, buttons_y + 26, \"select\");\n    }\n}\n\nbool infrared_progress_view_set_progress(InfraredProgressView* instance, uint16_t progress) {\n    bool result;\n    with_view_model(\n        instance->view,\n        InfraredProgressViewModel * model,\n        {\n            result = progress <= model->progress_total;\n            if(result) model->progress = progress;\n        },\n        true);\n    return result;\n}\n\nvoid infrared_progress_view_set_progress_total(\n    InfraredProgressView* progress,\n    uint16_t progress_total) {\n    furi_assert(progress);\n    InfraredProgressViewModel* model = view_get_model(progress->view);\n    model->progress = 0;\n    model->progress_total = progress_total;\n    view_commit_model(progress->view, false);\n}\n\nvoid infrared_progress_view_set_paused(InfraredProgressView* instance, bool is_paused) {\n    with_view_model(\n        instance->view, InfraredProgressViewModel * model, { model->is_paused = is_paused; }, true);\n}\n\nbool infrared_progress_view_input_callback(InputEvent* event, void* context) {\n    InfraredProgressView* instance = context;\n\n    if(event->type == InputTypePress || event->type == InputTypeRelease) {\n        return false;\n    }\n\n    if(!instance->input_callback) return false;\n\n    with_view_model(\n        instance->view,\n        InfraredProgressViewModel * model,\n        {\n            if(model->is_paused) {\n                if(event->key == InputKeyLeft)\n                    instance->input_callback(\n                        instance->context, InfraredProgressViewInputPreviousSignal);\n                else if(event->key == InputKeyRight)\n                    instance->input_callback(\n                        instance->context, InfraredProgressViewInputNextSignal);\n                else if(event->key == InputKeyOk)\n                    instance->input_callback(\n                        instance->context, InfraredProgressViewInputSendSingle);\n                else if(event->key == InputKeyBack)\n                    instance->input_callback(instance->context, InfraredProgressViewInputResume);\n            } else {\n                if(event->key == InputKeyOk)\n                    instance->input_callback(instance->context, InfraredProgressViewInputPause);\n                else if(event->key == InputKeyBack)\n                    instance->input_callback(instance->context, InfraredProgressViewInputStop);\n            }\n        },\n        false);\n\n    return true;\n}\n\nInfraredProgressView* infrared_progress_view_alloc(void) {\n    InfraredProgressView* instance = malloc(sizeof(InfraredProgressView));\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(InfraredProgressViewModel));\n    InfraredProgressViewModel* model = view_get_model(instance->view);\n    model->progress = 0;\n    model->progress_total = 0;\n    view_commit_model(instance->view, false);\n    view_set_draw_callback(instance->view, infrared_progress_view_draw_callback);\n    view_set_input_callback(instance->view, infrared_progress_view_input_callback);\n    view_set_context(instance->view, instance);\n\n    return instance;\n}\n\nvoid infrared_progress_view_free(InfraredProgressView* progress) {\n    view_free(progress->view);\n    free(progress);\n}\n\nvoid infrared_progress_view_set_input_callback(\n    InfraredProgressView* instance,\n    InfraredProgressViewInputCallback callback,\n    void* context) {\n    furi_assert(instance);\n    instance->input_callback = callback;\n    instance->context = context;\n}\n\nView* infrared_progress_view_get_view(InfraredProgressView* instance) {\n    furi_assert(instance);\n    furi_assert(instance->view);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/main/infrared/views/infrared_progress_view.h",
    "content": "/**\n  * @file infrared_progress_view.h\n  * Infrared: Custom Infrared view module.\n  * It shows popup progress bar during brute force.\n  */\n#pragma once\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Anonymous instance */\ntypedef struct InfraredProgressView InfraredProgressView;\n\ntypedef enum {\n    InfraredProgressViewInputStop,\n    InfraredProgressViewInputPause,\n    InfraredProgressViewInputResume,\n    InfraredProgressViewInputPreviousSignal,\n    InfraredProgressViewInputNextSignal,\n    InfraredProgressViewInputSendSingle,\n} InfraredProgressViewInput;\n\n/** Callback for input handling */\ntypedef void (*InfraredProgressViewInputCallback)(void* context, InfraredProgressViewInput event);\n\n/** Allocate and initialize Infrared view\n *\n * @retval new allocated instance\n */\nInfraredProgressView* infrared_progress_view_alloc(void);\n\n/** Free previously allocated Progress view module instance\n *\n * @param instance to free\n */\nvoid infrared_progress_view_free(InfraredProgressView* instance);\n\n/** Get progress view module view\n *\n * @param instance view module\n * @retval view\n */\nView* infrared_progress_view_get_view(InfraredProgressView* instance);\n\n/** Set progress of progress view module\n *\n * @param instance view module\n * @param progress progress value\n */\nbool infrared_progress_view_set_progress(InfraredProgressView* instance, uint16_t progress);\n\n/** Set maximum progress value\n *\n * @param instance - view module\n * @param progress_max - maximum value of progress\n */\nvoid infrared_progress_view_set_progress_total(\n    InfraredProgressView* instance,\n    uint16_t progress_max);\n\n/** Selects the variant of the View\n * \n * @param instance view instance\n * @param is_paused the \"paused\" variant is displayed if true; the \"sending\" one if false\n */\nvoid infrared_progress_view_set_paused(InfraredProgressView* instance, bool is_paused);\n\n/** Set input callback\n *\n * @param instance - view module\n * @param callback - callback to call for input\n * @param context - context to pass to callback\n */\nvoid infrared_progress_view_set_input_callback(\n    InfraredProgressView* instance,\n    InfraredProgressViewInputCallback callback,\n    void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/lfrfid/application.fam",
    "content": "App(\n    appid=\"lfrfid\",\n    name=\"125 kHz RFID\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    targets=[\"f7\"],\n    entry_point=\"lfrfid_app\",\n    icon=\"A_125khz_14\",\n    stack_size=2 * 1024,\n    order=20,\n    resources=\"resources\",\n    fap_libs=[\"assets\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"RFID\",\n)\n\nApp(\n    appid=\"cli_rfid\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_rfid_ep\",\n    requires=[\"cli\"],\n    sources=[\"lfrfid_cli.c\"],\n)\n"
  },
  {
    "path": "applications/main/lfrfid/lfrfid.c",
    "content": "#include \"lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\nstatic bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    LfRfid* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool lfrfid_debug_back_event_callback(void* context) {\n    furi_assert(context);\n    LfRfid* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    LfRfid* app = (LfRfid*)context;\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose);\n        // Detach RPC\n        rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);\n        app->rpc_ctx = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit);\n    } else if(event->type == RpcAppEventTypeLoadFile) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeString);\n        furi_string_set(app->file_path, event->data.string);\n        view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile);\n    } else {\n        rpc_system_app_confirm(app->rpc_ctx, false);\n    }\n}\n\nstatic LfRfid* lfrfid_alloc(void) {\n    LfRfid* lfrfid = malloc(sizeof(LfRfid));\n\n    lfrfid->storage = furi_record_open(RECORD_STORAGE);\n    lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    lfrfid->file_name = furi_string_alloc();\n    lfrfid->raw_file_name = furi_string_alloc();\n    lfrfid->file_path = furi_string_alloc_set(LFRFID_APP_FOLDER);\n\n    lfrfid->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n\n    size_t size = protocol_dict_get_max_data_size(lfrfid->dict);\n    lfrfid->new_key_data = (uint8_t*)malloc(size);\n    lfrfid->old_key_data = (uint8_t*)malloc(size);\n\n    lfrfid->lfworker = lfrfid_worker_alloc(lfrfid->dict);\n\n    lfrfid->view_dispatcher = view_dispatcher_alloc();\n    lfrfid->scene_manager = scene_manager_alloc(&lfrfid_scene_handlers, lfrfid);\n    view_dispatcher_set_event_callback_context(lfrfid->view_dispatcher, lfrfid);\n    view_dispatcher_set_custom_event_callback(\n        lfrfid->view_dispatcher, lfrfid_debug_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        lfrfid->view_dispatcher, lfrfid_debug_back_event_callback);\n\n    // Open GUI record\n    lfrfid->gui = furi_record_open(RECORD_GUI);\n\n    // Open Notification record\n    lfrfid->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // Submenu\n    lfrfid->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewSubmenu, submenu_get_view(lfrfid->submenu));\n\n    // Dialog\n    lfrfid->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewDialogEx, dialog_ex_get_view(lfrfid->dialog_ex));\n\n    // Popup\n    lfrfid->popup = popup_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewPopup, popup_get_view(lfrfid->popup));\n\n    // Widget\n    lfrfid->widget = widget_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewWidget, widget_get_view(lfrfid->widget));\n\n    // Text Input\n    lfrfid->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewTextInput, text_input_get_view(lfrfid->text_input));\n\n    // Byte Input\n    lfrfid->byte_input = byte_input_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewByteInput, byte_input_get_view(lfrfid->byte_input));\n\n    // Read custom view\n    lfrfid->read_view = lfrfid_view_read_alloc();\n    view_dispatcher_add_view(\n        lfrfid->view_dispatcher, LfRfidViewRead, lfrfid_view_read_get_view(lfrfid->read_view));\n\n    return lfrfid;\n} //-V773\n\nstatic void lfrfid_free(LfRfid* lfrfid) {\n    furi_assert(lfrfid);\n\n    furi_string_free(lfrfid->raw_file_name);\n    furi_string_free(lfrfid->file_name);\n    furi_string_free(lfrfid->file_path);\n    protocol_dict_free(lfrfid->dict);\n\n    lfrfid_worker_free(lfrfid->lfworker);\n\n    if(lfrfid->rpc_ctx) {\n        rpc_system_app_set_callback(lfrfid->rpc_ctx, NULL, NULL);\n        rpc_system_app_send_exited(lfrfid->rpc_ctx);\n    }\n\n    free(lfrfid->new_key_data);\n    free(lfrfid->old_key_data);\n\n    // Submenu\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewSubmenu);\n    submenu_free(lfrfid->submenu);\n\n    // DialogEx\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewDialogEx);\n    dialog_ex_free(lfrfid->dialog_ex);\n\n    // Popup\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewPopup);\n    popup_free(lfrfid->popup);\n\n    // Widget\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewWidget);\n    widget_free(lfrfid->widget);\n\n    // TextInput\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewTextInput);\n    text_input_free(lfrfid->text_input);\n\n    // ByteInput\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewByteInput);\n    byte_input_free(lfrfid->byte_input);\n\n    // Read custom view\n    view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewRead);\n    lfrfid_view_read_free(lfrfid->read_view);\n\n    // View Dispatcher\n    view_dispatcher_free(lfrfid->view_dispatcher);\n\n    // Scene Manager\n    scene_manager_free(lfrfid->scene_manager);\n\n    // GUI\n    furi_record_close(RECORD_GUI);\n    lfrfid->gui = NULL;\n\n    // Notifications\n    furi_record_close(RECORD_NOTIFICATION);\n    lfrfid->notifications = NULL;\n\n    furi_record_close(RECORD_STORAGE);\n    furi_record_close(RECORD_DIALOGS);\n\n    free(lfrfid);\n}\n\nint32_t lfrfid_app(void* p) {\n    LfRfid* app = lfrfid_alloc();\n    char* args = p;\n\n    lfrfid_make_app_folder(app);\n\n    if(args && strlen(args)) {\n        uint32_t rpc_ctx_ptr = 0;\n        if(sscanf(args, \"RPC %lX\", &rpc_ctx_ptr) == 1) {\n            app->rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr;\n            rpc_system_app_set_callback(app->rpc_ctx, rpc_command_callback, app);\n            rpc_system_app_send_started(app->rpc_ctx);\n            view_dispatcher_attach_to_gui(\n                app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc);\n            dolphin_deed(DolphinDeedRfidEmulate);\n        } else {\n            furi_string_set(app->file_path, args);\n            if(lfrfid_load_key_data(app, app->file_path, true)) {\n                view_dispatcher_attach_to_gui(\n                    app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n                scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);\n                dolphin_deed(DolphinDeedRfidEmulate);\n            } else {\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n        }\n    } else {\n        view_dispatcher_attach_to_gui(\n            app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n        scene_manager_next_scene(app->scene_manager, LfRfidSceneStart);\n    }\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    lfrfid_free(app);\n\n    return 0;\n}\n\nbool lfrfid_save_key(LfRfid* app) {\n    furi_assert(app);\n\n    bool result = false;\n\n    lfrfid_make_app_folder(app);\n\n    if(furi_string_end_with(app->file_path, LFRFID_APP_FILENAME_EXTENSION)) {\n        size_t filename_start = furi_string_search_rchar(app->file_path, '/');\n        furi_string_left(app->file_path, filename_start);\n    }\n\n    furi_string_cat_printf(\n        app->file_path,\n        \"/%s%s\",\n        furi_string_get_cstr(app->file_name),\n        LFRFID_APP_FILENAME_EXTENSION);\n\n    result = lfrfid_save_key_data(app, app->file_path);\n    return result;\n}\n\nbool lfrfid_load_key_from_file_select(LfRfid* app) {\n    furi_assert(app);\n\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(\n        &browser_options, LFRFID_APP_FILENAME_EXTENSION, &I_125_10px);\n    browser_options.base_path = LFRFID_APP_FOLDER;\n\n    // Input events and views are managed by file_browser\n    bool result =\n        dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);\n\n    if(result) {\n        result = lfrfid_load_key_data(app, app->file_path, true);\n    }\n\n    return result;\n}\n\nbool lfrfid_delete_key(LfRfid* app) {\n    furi_assert(app);\n\n    return storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path));\n}\n\nbool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog) {\n    bool result = false;\n\n    do {\n        app->protocol_id = lfrfid_dict_file_load(app->dict, furi_string_get_cstr(path));\n        if(app->protocol_id == PROTOCOL_NO) break;\n\n        path_extract_filename(path, app->file_name, true);\n        result = true;\n    } while(0);\n\n    if((!result) && (show_dialog)) {\n        dialog_message_show_storage_error(app->dialogs, \"Cannot load\\nkey file\");\n    }\n\n    return result;\n}\n\nbool lfrfid_save_key_data(LfRfid* app, FuriString* path) {\n    bool result = lfrfid_dict_file_save(app->dict, app->protocol_id, furi_string_get_cstr(path));\n\n    if(!result) {\n        dialog_message_show_storage_error(app->dialogs, \"Cannot save\\nkey file\");\n    }\n\n    return result;\n}\n\nvoid lfrfid_make_app_folder(LfRfid* app) {\n    furi_assert(app);\n\n    if(!storage_simply_mkdir(app->storage, LFRFID_APP_FOLDER)) {\n        dialog_message_show_storage_error(app->dialogs, \"Cannot create\\napp folder\");\n    }\n}\n\nvoid lfrfid_text_store_set(LfRfid* app, const char* text, ...) {\n    furi_assert(app);\n    va_list args;\n    va_start(args, text);\n\n    vsnprintf(app->text_store, LFRFID_TEXT_STORE_SIZE, text, args);\n\n    va_end(args);\n}\n\nvoid lfrfid_text_store_clear(LfRfid* app) {\n    furi_assert(app);\n    memset(app->text_store, 0, sizeof(app->text_store));\n}\n\nvoid lfrfid_popup_timeout_callback(void* context) {\n    LfRfid* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventPopupClosed);\n}\n\nvoid lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) {\n    LfRfid* app = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, result);\n    }\n}\n\nvoid lfrfid_text_input_callback(void* context) {\n    LfRfid* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/lfrfid_cli.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <stdarg.h>\n#include <cli/cli_main_commands.h>\n#include <lib/toolbox/args.h>\n#include <lib/lfrfid/lfrfid_worker.h>\n#include <storage/storage.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/pipe.h>\n\n#include <toolbox/varint.h>\n\n#include <toolbox/protocols/protocol_dict.h>\n#include <lfrfid/protocols/lfrfid_protocols.h>\n#include <lfrfid/lfrfid_raw_file.h>\n#include <toolbox/pulse_protocols/pulse_glue.h>\n\nstatic void lfrfid_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"rfid read <optional: normal | indala>         - read in ASK/PSK mode\\r\\n\");\n    printf(\"rfid <write | emulate> <key_type> <key_data>  - write or emulate a card\\r\\n\");\n    printf(\"rfid raw_read <ask | psk> <filename>          - read and save raw data to a file\\r\\n\");\n    printf(\n        \"rfid raw_emulate <filename>                   - emulate raw data (not very useful, but helps debug protocols)\\r\\n\");\n    printf(\n        \"rfid raw_analyze <filename>                   - outputs raw data to the cli and tries to decode it (useful for protocol development)\\r\\n\");\n}\n\ntypedef struct {\n    ProtocolId protocol;\n    FuriEventFlag* event;\n} LFRFIDCliReadContext;\n\nstatic void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) {\n    furi_assert(ctx);\n    LFRFIDCliReadContext* context = ctx;\n    if(result == LFRFIDWorkerReadDone) {\n        context->protocol = proto;\n        FURI_SW_MEMBARRIER();\n    }\n    furi_event_flag_set(context->event, 1 << result);\n}\n\nstatic void lfrfid_cli_read(PipeSide* pipe, FuriString* args) {\n    FuriString* type_string;\n    type_string = furi_string_alloc();\n    LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;\n\n    if(args_read_string_and_trim(args, type_string)) {\n        if(furi_string_cmp_str(type_string, \"normal\") == 0 ||\n           furi_string_cmp_str(type_string, \"ask\") == 0) {\n            // ask\n            type = LFRFIDWorkerReadTypeASKOnly;\n        } else if(\n            furi_string_cmp_str(type_string, \"indala\") == 0 ||\n            furi_string_cmp_str(type_string, \"psk\") == 0) {\n            // psk\n            type = LFRFIDWorkerReadTypePSKOnly;\n        } else {\n            lfrfid_cli_print_usage();\n            furi_string_free(type_string);\n            return;\n        }\n    }\n    furi_string_free(type_string);\n\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);\n    LFRFIDCliReadContext context;\n    context.protocol = PROTOCOL_NO;\n    context.event = furi_event_flag_alloc();\n\n    lfrfid_worker_start_thread(worker);\n\n    printf(\"Reading RFID...\\r\\nPress Ctrl+C to abort\\r\\n\");\n\n    const uint32_t available_flags = (1 << LFRFIDWorkerReadDone);\n\n    lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context);\n\n    while(true) {\n        uint32_t flags =\n            furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100);\n\n        if(flags != (unsigned)FuriFlagErrorTimeout) {\n            if(FURI_BIT(flags, LFRFIDWorkerReadDone)) {\n                break;\n            }\n        }\n\n        if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n    }\n\n    lfrfid_worker_stop(worker);\n    lfrfid_worker_stop_thread(worker);\n    lfrfid_worker_free(worker);\n\n    if(context.protocol != PROTOCOL_NO) {\n        printf(\"%s \", protocol_dict_get_name(dict, context.protocol));\n\n        size_t size = protocol_dict_get_data_size(dict, context.protocol);\n        uint8_t* data = malloc(size);\n        protocol_dict_get_data(dict, context.protocol, data, size);\n        for(size_t i = 0; i < size; i++) {\n            printf(\"%02X\", data[i]);\n        }\n        printf(\"\\r\\n\");\n        free(data);\n\n        FuriString* info;\n        info = furi_string_alloc();\n        protocol_dict_render_data(dict, info, context.protocol);\n        if(!furi_string_empty(info)) {\n            printf(\"%s\\r\\n\", furi_string_get_cstr(info));\n        }\n        furi_string_free(info);\n    }\n\n    printf(\"Reading stopped\\r\\n\");\n    protocol_dict_free(dict);\n\n    furi_event_flag_free(context.event);\n}\n\nstatic bool lfrfid_cli_parse_args(FuriString* args, ProtocolDict* dict, ProtocolId* protocol) {\n    bool result = false;\n    FuriString *protocol_name, *data_text;\n    protocol_name = furi_string_alloc();\n    data_text = furi_string_alloc();\n    size_t data_size = protocol_dict_get_max_data_size(dict);\n    uint8_t* data = malloc(data_size);\n\n    do {\n        // load args\n        if(!args_read_string_and_trim(args, protocol_name) ||\n           !args_read_string_and_trim(args, data_text)) {\n            lfrfid_cli_print_usage();\n            break;\n        }\n\n        // check protocol arg\n        *protocol = protocol_dict_get_protocol_by_name(dict, furi_string_get_cstr(protocol_name));\n        if(*protocol == PROTOCOL_NO) {\n            printf(\n                \"Unknown protocol: %s\\r\\n\"\n                \"Available protocols:\\r\\n\",\n                furi_string_get_cstr(protocol_name));\n\n            for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) {\n                printf(\n                    \"\\t%s, %zu bytes long\\r\\n\",\n                    protocol_dict_get_name(dict, i),\n                    protocol_dict_get_data_size(dict, i));\n            }\n            break;\n        }\n\n        data_size = protocol_dict_get_data_size(dict, *protocol);\n\n        // check data arg\n        if(!args_read_hex_bytes(data_text, data, data_size)) {\n            printf(\n                \"%s data needs to be %zu bytes long\\r\\n\",\n                protocol_dict_get_name(dict, *protocol),\n                data_size);\n            break;\n        }\n\n        // load data to protocol\n        protocol_dict_set_data(dict, *protocol, data, data_size);\n\n        result = true;\n    } while(false);\n\n    free(data);\n    furi_string_free(protocol_name);\n    furi_string_free(data_text);\n    return result;\n}\n\nstatic void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) {\n    furi_assert(ctx);\n    FuriEventFlag* events = ctx;\n    furi_event_flag_set(events, 1 << result);\n}\n\nstatic void lfrfid_cli_write(PipeSide* pipe, FuriString* args) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    ProtocolId protocol;\n\n    if(!lfrfid_cli_parse_args(args, dict, &protocol)) {\n        protocol_dict_free(dict);\n        return;\n    }\n\n    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);\n    FuriEventFlag* event = furi_event_flag_alloc();\n\n    lfrfid_worker_start_thread(worker);\n    lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event);\n\n    printf(\"Writing RFID...\\r\\nPress Ctrl+C to abort\\r\\n\");\n    const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) |\n                                     (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) |\n                                     (1 << LFRFIDWorkerWriteFobCannotBeWritten);\n\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);\n        if(flags != (unsigned)FuriFlagErrorTimeout) {\n            if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) {\n                printf(\"Written!\\r\\n\");\n                break;\n            }\n\n            if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) {\n                printf(\"This protocol cannot be written.\\r\\n\");\n                break;\n            }\n\n            if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) {\n                printf(\"Seems this fob cannot be written.\\r\\n\");\n            }\n        }\n    }\n    printf(\"Writing stopped\\r\\n\");\n\n    lfrfid_worker_stop(worker);\n    lfrfid_worker_stop_thread(worker);\n    lfrfid_worker_free(worker);\n    protocol_dict_free(dict);\n    furi_event_flag_free(event);\n}\n\nstatic void lfrfid_cli_emulate(PipeSide* pipe, FuriString* args) {\n    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n    ProtocolId protocol;\n\n    if(!lfrfid_cli_parse_args(args, dict, &protocol)) {\n        protocol_dict_free(dict);\n        return;\n    }\n\n    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);\n\n    lfrfid_worker_start_thread(worker);\n    lfrfid_worker_emulate_start(worker, protocol);\n\n    printf(\"Emulating RFID...\\r\\nPress Ctrl+C to abort\\r\\n\");\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        furi_delay_ms(100);\n    }\n    printf(\"Emulation stopped\\r\\n\");\n\n    lfrfid_worker_stop(worker);\n    lfrfid_worker_stop_thread(worker);\n    lfrfid_worker_free(worker);\n    protocol_dict_free(dict);\n}\n\nstatic void lfrfid_cli_raw_analyze(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    FuriString *filepath, *info_string;\n    filepath = furi_string_alloc();\n    info_string = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);\n\n    do {\n        float frequency = 0;\n        float duty_cycle = 0;\n\n        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {\n            lfrfid_cli_print_usage();\n            break;\n        }\n\n        if(!lfrfid_raw_file_open_read(file, furi_string_get_cstr(filepath))) {\n            printf(\"Failed to open file\\r\\n\");\n            break;\n        }\n\n        if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) {\n            printf(\"Invalid header\\r\\n\");\n            break;\n        }\n\n        bool file_end = false;\n        uint32_t total_warns = 0;\n        uint32_t total_duration = 0;\n        uint32_t total_pulse = 0;\n        ProtocolId total_protocol = PROTOCOL_NO;\n\n        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n        protocol_dict_decoders_start(dict);\n\n        while(!file_end) {\n            uint32_t pulse = 0;\n            uint32_t duration = 0;\n            if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) {\n                bool warn = false;\n\n                if(pulse > duration || pulse <= 0 || duration <= 0) {\n                    total_warns += 1;\n                    warn = true;\n                }\n\n                furi_string_printf(info_string, \"[%lu %lu]\", pulse, duration);\n                printf(\"%-16s\", furi_string_get_cstr(info_string));\n                furi_string_printf(info_string, \"[%lu %lu]\", pulse, duration - pulse);\n                printf(\"%-16s\", furi_string_get_cstr(info_string));\n\n                if(warn) {\n                    printf(\" <<----\");\n                }\n\n                if(total_protocol == PROTOCOL_NO) {\n                    total_protocol = protocol_dict_decoders_feed(dict, true, pulse);\n                    if(total_protocol == PROTOCOL_NO) {\n                        total_protocol =\n                            protocol_dict_decoders_feed(dict, false, duration - pulse);\n                    }\n\n                    if(total_protocol != PROTOCOL_NO) {\n                        printf(\" <FOUND %s>\", protocol_dict_get_name(dict, total_protocol));\n                    }\n                }\n\n                printf(\"\\r\\n\");\n\n                total_pulse += pulse;\n                total_duration += duration;\n\n                if(total_protocol != PROTOCOL_NO) { //-V1051\n                    break;\n                }\n            } else {\n                printf(\"Failed to read pair\\r\\n\");\n                break;\n            }\n        }\n\n        printf(\"   Frequency: %f\\r\\n\", (double)frequency);\n        printf(\"  Duty Cycle: %f\\r\\n\", (double)duty_cycle);\n        printf(\"       Warns: %lu\\r\\n\", total_warns);\n        printf(\"   Pulse sum: %lu\\r\\n\", total_pulse);\n        printf(\"Duration sum: %lu\\r\\n\", total_duration);\n        printf(\"     Average: %f\\r\\n\", (double)((float)total_pulse / (float)total_duration));\n        printf(\"    Protocol: \");\n\n        if(total_protocol != PROTOCOL_NO) {\n            size_t data_size = protocol_dict_get_data_size(dict, total_protocol);\n            uint8_t* data = malloc(data_size);\n            protocol_dict_get_data(dict, total_protocol, data, data_size);\n\n            printf(\"%s [\", protocol_dict_get_name(dict, total_protocol));\n            for(size_t i = 0; i < data_size; i++) {\n                printf(\"%02X\", data[i]);\n                if(i < data_size - 1) {\n                    printf(\" \");\n                }\n            }\n            printf(\"]\\r\\n\");\n\n            protocol_dict_render_data(dict, info_string, total_protocol);\n            printf(\"%s\\r\\n\", furi_string_get_cstr(info_string));\n\n            free(data);\n        } else {\n            printf(\"not found\\r\\n\");\n        }\n\n        protocol_dict_free(dict);\n    } while(false);\n\n    furi_string_free(filepath);\n    furi_string_free(info_string);\n    lfrfid_raw_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) {\n    furi_assert(context);\n    FuriEventFlag* event = context;\n    furi_event_flag_set(event, 1 << result);\n}\n\nstatic void lfrfid_cli_raw_read(PipeSide* pipe, FuriString* args) {\n    FuriString *filepath, *type_string;\n    filepath = furi_string_alloc();\n    type_string = furi_string_alloc();\n    LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;\n\n    do {\n        if(args_read_string_and_trim(args, type_string)) {\n            if(furi_string_cmp_str(type_string, \"normal\") == 0 ||\n               furi_string_cmp_str(type_string, \"ask\") == 0) {\n                // ask\n                type = LFRFIDWorkerReadTypeASKOnly;\n            } else if(\n                furi_string_cmp_str(type_string, \"indala\") == 0 ||\n                furi_string_cmp_str(type_string, \"psk\") == 0) {\n                // psk\n                type = LFRFIDWorkerReadTypePSKOnly;\n            } else {\n                lfrfid_cli_print_usage();\n                break;\n            }\n        }\n\n        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {\n            lfrfid_cli_print_usage();\n            break;\n        }\n\n        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n        LFRFIDWorker* worker = lfrfid_worker_alloc(dict);\n        FuriEventFlag* event = furi_event_flag_alloc();\n\n        lfrfid_worker_start_thread(worker);\n\n        bool overrun = false;\n\n        const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) |\n                                         (1 << LFRFIDWorkerReadRawOverrun);\n\n        lfrfid_worker_read_raw_start(\n            worker, furi_string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event);\n        while(true) {\n            uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);\n\n            if(flags != (unsigned)FuriFlagErrorTimeout) {\n                if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) {\n                    printf(\"File is not RFID raw file\\r\\n\");\n                    break;\n                }\n\n                if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) {\n                    if(!overrun) {\n                        printf(\"Overrun\\r\\n\");\n                        overrun = true;\n                    }\n                }\n            }\n\n            if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n        }\n\n        if(overrun) {\n            printf(\"An overrun occurred during read\\r\\n\");\n        }\n\n        lfrfid_worker_stop(worker);\n\n        lfrfid_worker_stop_thread(worker);\n        lfrfid_worker_free(worker);\n        protocol_dict_free(dict);\n\n        furi_event_flag_free(event);\n\n    } while(false);\n\n    furi_string_free(filepath);\n    furi_string_free(type_string);\n}\n\nstatic void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) {\n    furi_assert(context);\n    FuriEventFlag* event = context;\n    furi_event_flag_set(event, 1 << result);\n}\n\nstatic void lfrfid_cli_raw_emulate(PipeSide* pipe, FuriString* args) {\n    FuriString* filepath;\n    filepath = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    do {\n        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {\n            lfrfid_cli_print_usage();\n            break;\n        }\n\n        if(!storage_file_exists(storage, furi_string_get_cstr(filepath))) {\n            printf(\"File not found: \\\"%s\\\"\\r\\n\", furi_string_get_cstr(filepath));\n            break;\n        }\n\n        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);\n        LFRFIDWorker* worker = lfrfid_worker_alloc(dict);\n        FuriEventFlag* event = furi_event_flag_alloc();\n\n        lfrfid_worker_start_thread(worker);\n\n        bool overrun = false;\n\n        const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) |\n                                         (1 << LFRFIDWorkerEmulateRawOverrun);\n\n        lfrfid_worker_emulate_raw_start(\n            worker, furi_string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event);\n        while(true) {\n            uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);\n\n            if(flags != (unsigned)FuriFlagErrorTimeout) {\n                if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) {\n                    printf(\"File is not RFID raw file\\r\\n\");\n                    break;\n                }\n\n                if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) {\n                    if(!overrun) {\n                        printf(\"Overrun\\r\\n\");\n                        overrun = true;\n                    }\n                }\n            }\n\n            if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n        }\n\n        if(overrun) {\n            printf(\"An overrun occurred during emulation\\r\\n\");\n        }\n\n        lfrfid_worker_stop(worker);\n\n        lfrfid_worker_stop_thread(worker);\n        lfrfid_worker_free(worker);\n        protocol_dict_free(dict);\n\n        furi_event_flag_free(event);\n\n    } while(false);\n\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(filepath);\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    if(!args_read_string_and_trim(args, cmd)) {\n        furi_string_free(cmd);\n        lfrfid_cli_print_usage();\n        return;\n    }\n\n    if(furi_string_cmp_str(cmd, \"read\") == 0) {\n        lfrfid_cli_read(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"write\") == 0) {\n        lfrfid_cli_write(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"emulate\") == 0) {\n        lfrfid_cli_emulate(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"raw_read\") == 0) {\n        lfrfid_cli_raw_read(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"raw_emulate\") == 0) {\n        lfrfid_cli_raw_emulate(pipe, args);\n    } else if(furi_string_cmp_str(cmd, \"raw_analyze\") == 0) {\n        lfrfid_cli_raw_analyze(pipe, args);\n    } else {\n        lfrfid_cli_print_usage();\n    }\n\n    furi_string_free(cmd);\n}\n\nCLI_COMMAND_INTERFACE(rfid, execute, CliCommandFlagDefault, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/main/lfrfid/lfrfid_i.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <assets_icons.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <notification/notification_messages.h>\n\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/popup.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/byte_input.h>\n#include <gui/modules/widget.h>\n\n#include <lfrfid/views/lfrfid_view_read.h>\n\n#include <notification/notification_messages.h>\n#include <dialogs/dialogs.h>\n#include <storage/storage.h>\n#include <flipper_format/flipper_format.h>\n\n#include <rpc/rpc_app.h>\n\n#include <toolbox/protocols/protocol_dict.h>\n#include <toolbox/path.h>\n#include <lfrfid/lfrfid_dict_file.h>\n#include <lfrfid/protocols/lfrfid_protocols.h>\n#include <lfrfid/lfrfid_worker.h>\n\n#include <lfrfid/scenes/lfrfid_scene.h>\n\n#define LFRFID_KEY_NAME_SIZE   22\n#define LFRFID_TEXT_STORE_SIZE 40\n\n#define LFRFID_APP_FOLDER                    EXT_PATH(\"lfrfid\")\n#define LFRFID_SD_FOLDER                     EXT_PATH(\"lfrfid\")\n#define LFRFID_APP_FILENAME_PREFIX           \"RFID\"\n#define LFRFID_APP_FILENAME_EXTENSION        \".rfid\"\n#define LFRFID_APP_SHADOW_FILENAME_EXTENSION \".shd\"\n\n#define LFRFID_APP_RAW_ASK_EXTENSION \".ask.raw\"\n#define LFRFID_APP_RAW_PSK_EXTENSION \".psk.raw\"\n\nenum LfRfidCustomEvent {\n    LfRfidEventNext = 100,\n    LfRfidEventExit,\n    LfRfidEventPopupClosed,\n    LfRfidEventReadSenseStart,\n    LfRfidEventReadSenseEnd,\n    LfRfidEventReadSenseCardStart,\n    LfRfidEventReadSenseCardEnd,\n    LfRfidEventReadStartASK,\n    LfRfidEventReadStartPSK,\n    LfRfidEventReadDone,\n    LfRfidEventReadOverrun,\n    LfRfidEventReadError,\n    LfRfidEventWriteOK,\n    LfRfidEventWriteProtocolCannotBeWritten,\n    LfRfidEventWriteFobCannotBeWritten,\n    LfRfidEventWriteTooLongToWrite,\n    LfRfidEventRpcLoadFile,\n    LfRfidEventRpcSessionClose,\n};\n\ntypedef enum {\n    LfRfidRpcStateIdle,\n    LfRfidRpcStateEmulating,\n} LfRfidRpcState;\n\ntypedef struct LfRfid LfRfid;\n\nstruct LfRfid {\n    LFRFIDWorker* lfworker;\n    ViewDispatcher* view_dispatcher;\n    Gui* gui;\n    NotificationApp* notifications;\n    SceneManager* scene_manager;\n    Storage* storage;\n    DialogsApp* dialogs;\n    Widget* widget;\n\n    char text_store[LFRFID_TEXT_STORE_SIZE + 1];\n    FuriString* file_path;\n    FuriString* file_name;\n    FuriString* raw_file_name;\n\n    ProtocolDict* dict;\n    ProtocolId protocol_id;\n    ProtocolId protocol_id_next;\n    LFRFIDWorkerReadType read_type;\n\n    uint8_t* old_key_data;\n    uint8_t* new_key_data;\n\n    RpcAppSystem* rpc_ctx;\n    LfRfidRpcState rpc_state;\n\n    // Common Views\n    Submenu* submenu;\n    DialogEx* dialog_ex;\n    Popup* popup;\n    TextInput* text_input;\n    ByteInput* byte_input;\n\n    // Custom views\n    LfRfidReadView* read_view;\n};\n\ntypedef enum {\n    LfRfidViewSubmenu,\n    LfRfidViewDialogEx,\n    LfRfidViewPopup,\n    LfRfidViewWidget,\n    LfRfidViewTextInput,\n    LfRfidViewByteInput,\n    LfRfidViewRead,\n} LfRfidView;\n\ntypedef enum {\n    LfRfidMenuIndexRead,\n    LfRfidMenuIndexSaved,\n    LfRfidMenuIndexAddManually,\n    LfRfidMenuIndexExtraActions,\n} LfRfidMenuIndex;\n\nbool lfrfid_save_key(LfRfid* app);\n\nbool lfrfid_load_key_from_file_select(LfRfid* app);\n\nbool lfrfid_delete_key(LfRfid* app);\n\nbool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog);\n\nbool lfrfid_save_key_data(LfRfid* app, FuriString* path);\n\nvoid lfrfid_make_app_folder(LfRfid* app);\n\nvoid lfrfid_text_store_set(LfRfid* app, const char* text, ...);\n\nvoid lfrfid_text_store_clear(LfRfid* app);\n\nvoid lfrfid_popup_timeout_callback(void* context);\n\nvoid lfrfid_widget_callback(GuiButtonType result, InputType type, void* context);\n\nvoid lfrfid_text_input_callback(void* context);\n"
  },
  {
    "path": "applications/main/lfrfid/resources/lfrfid/assets/iso3166.lfrfid",
    "content": "Filetype: Flipper LFRFID resources\nVersion: 1\n# country code: 2-letter name  3-letter name  full name\n0004: AF AFG Afghanistan \n0248: AX ALA Åland Islands \n0008: AL ALB Albania \n0012: DZ DZA Algeria \n0016: AS ASM American Samoa \n0020: AD AND Andorra \n0024: AO AGO Angola \n0660: AI AIA Anguilla \n0010: AQ ATA Antarctica \n0028: AG ATG Antigua and Barbuda \n0032: AR ARG Argentina \n0051: AM ARM Armenia \n0533: AW ABW Aruba \n0036: AU AUS Australia \n0040: AT AUT Austria \n0031: AZ AZE Azerbaijan \n0044: BS BHS Bahamas \n0048: BH BHR Bahrain \n0050: BD BGD Bangladesh \n0052: BB BRB Barbados \n0112: BY BLR Belarus \n0056: BE BEL Belgium \n0084: BZ BLZ Belize \n0204: BJ BEN Benin \n0060: BM BMU Bermuda \n0064: BT BTN Bhutan \n0068: BO BOL Bolivia, Plurinational State of \n0535: BQ BES Bonaire, Sint Eustatius and Saba \n0070: BA BIH Bosnia and Herzegovina \n0072: BW BWA Botswana \n0074: BV BVT Bouvet Island \n0076: BR BRA Brazil \n0086: IO IOT British Indian Ocean Territory \n0096: BN BRN Brunei Darussalam \n0100: BG BGR Bulgaria \n0854: BF BFA Burkina Faso \n0108: BI BDI Burundi \n0132: CV CPV Cabo Verde \n0116: KH KHM Cambodia \n0120: CM CMR Cameroon \n0124: CA CAN Canada \n0136: KY CYM Cayman Islands \n0140: CF CAF Central African Republic \n0148: TD TCD Chad \n0152: CL CHL Chile \n0156: CN CHN China \n0162: CX CXR Christmas Island \n0166: CC CCK Cocos (Keeling) Islands \n0170: CO COL Colombia \n0174: KM COM Comoros \n0178: CG COG Congo \n0180: CD COD Congo, Democratic Republic of the \n0184: CK COK Cook Islands \n0188: CR CRI Costa Rica \n0384: CI CIV Côte d'Ivoire \n0191: HR HRV Croatia \n0192: CU CUB Cuba \n0531: CW CUW Curaçao \n0196: CY CYP Cyprus \n0203: CZ CZE Czechia \n0208: DK DNK Denmark \n0262: DJ DJI Djibouti \n0212: DM DMA Dominica \n0214: DO DOM Dominican Republic \n0218: EC ECU Ecuador \n0818: EG EGY Egypt \n0222: SV SLV El Salvador \n0226: GQ GNQ Equatorial Guinea \n0232: ER ERI Eritrea \n0233: EE EST Estonia \n0748: SZ SWZ Eswatini \n0231: ET ETH Ethiopia \n0238: FK FLK Falkland Islands (Malvinas) \n0234: FO FRO Faroe Islands \n0242: FJ FJI Fiji \n0246: FI FIN Finland \n0250: FR FRA France \n0254: GF GUF French Guiana \n0258: PF PYF French Polynesia \n0260: TF ATF French Southern Territories \n0266: GA GAB Gabon \n0270: GM GMB Gambia \n0268: GE GEO Georgia \n0276: DE DEU Germany \n0288: GH GHA Ghana \n0292: GI GIB Gibraltar \n0300: GR GRC Greece \n0304: GL GRL Greenland \n0308: GD GRD Grenada \n0312: GP GLP Guadeloupe \n0316: GU GUM Guam \n0320: GT GTM Guatemala \n0831: GG GGY Guernsey \n0324: GN GIN Guinea \n0624: GW GNB Guinea-Bissau \n0328: GY GUY Guyana \n0332: HT HTI Haiti \n0334: HM HMD Heard Island and McDonald Islands \n0336: VA VAT Holy See \n0340: HN HND Honduras \n0344: HK HKG Hong Kong \n0348: HU HUN Hungary \n0352: IS ISL Iceland \n0356: IN IND India \n0360: ID IDN Indonesia \n0364: IR IRN Iran, Islamic Republic of \n0368: IQ IRQ Iraq \n0372: IE IRL Ireland \n0833: IM IMN Isle of Man \n0376: IL ISR Israel \n0380: IT ITA Italy \n0388: JM JAM Jamaica \n0392: JP JPN Japan \n0832: JE JEY Jersey \n0400: JO JOR Jordan \n0398: KZ KAZ Kazakhstan \n0404: KE KEN Kenya \n0296: KI KIR Kiribati \n0408: KP PRK Korea, Democratic People's Republic of \n0410: KR KOR Korea, Republic of \n0414: KW KWT Kuwait \n0417: KG KGZ Kyrgyzstan \n0418: LA LAO Lao People's Democratic Republic \n0428: LV LVA Latvia \n0422: LB LBN Lebanon \n0426: LS LSO Lesotho \n0430: LR LBR Liberia \n0434: LY LBY Libya \n0438: LI LIE Liechtenstein \n0440: LT LTU Lithuania \n0442: LU LUX Luxembourg \n0446: MO MAC Macao \n0450: MG MDG Madagascar \n0454: MW MWI Malawi \n0458: MY MYS Malaysia \n0462: MV MDV Maldives \n0466: ML MLI Mali \n0470: MT MLT Malta \n0584: MH MHL Marshall Islands \n0474: MQ MTQ Martinique \n0478: MR MRT Mauritania \n0480: MU MUS Mauritius \n0175: YT MYT Mayotte \n0484: MX MEX Mexico \n0583: FM FSM Micronesia, Federated States of \n0498: MD MDA Moldova, Republic of \n0492: MC MCO Monaco \n0496: MN MNG Mongolia \n0499: ME MNE Montenegro \n0500: MS MSR Montserrat \n0504: MA MAR Morocco \n0508: MZ MOZ Mozambique \n0104: MM MMR Myanmar \n0516: NA NAM Namibia \n0520: NR NRU Nauru \n0524: NP NPL Nepal \n0528: NL NLD Netherlands, Kingdom of the \n0540: NC NCL New Caledonia \n0554: NZ NZL New Zealand \n0558: NI NIC Nicaragua \n0562: NE NER Niger \n0566: NG NGA Nigeria \n0570: NU NIU Niue \n0574: NF NFK Norfolk Island \n0807: MK MKD North Macedonia \n0580: MP MNP Northern Mariana Islands \n0578: NO NOR Norway \n0512: OM OMN Oman \n0586: PK PAK Pakistan \n0585: PW PLW Palau \n0275: PS PSE Palestine, State of \n0591: PA PAN Panama \n0598: PG PNG Papua New Guinea \n0600: PY PRY Paraguay \n0604: PE PER Peru \n0608: PH PHL Philippines \n0612: PN PCN Pitcairn \n0616: PL POL Poland \n0620: PT PRT Portugal \n0630: PR PRI Puerto Rico \n0634: QA QAT Qatar \n0638: RE REU Réunion \n0642: RO ROU Romania \n0643: RU RUS Russian Federation \n0646: RW RWA Rwanda \n0652: BL BLM Saint Barthélemy \n0654: SH SHN Saint Helena, Ascension and Tristan da Cunha \n0659: KN KNA Saint Kitts and Nevis \n0662: LC LCA Saint Lucia \n0663: MF MAF Saint Martin (French part) \n0666: PM SPM Saint Pierre and Miquelon \n0670: VC VCT Saint Vincent and the Grenadines \n0882: WS WSM Samoa \n0674: SM SMR San Marino \n0678: ST STP Sao Tome and Principe \n0682: SA SAU Saudi Arabia \n0686: SN SEN Senegal \n0688: RS SRB Serbia \n0690: SC SYC Seychelles \n0694: SL SLE Sierra Leone \n0702: SG SGP Singapore \n0534: SX SXM Sint Maarten (Dutch part) \n0703: SK SVK Slovakia \n0705: SI SVN Slovenia \n0090: SB SLB Solomon Islands \n0706: SO SOM Somalia \n0710: ZA ZAF South Africa \n0239: GS SGS South Georgia and the South Sandwich Islands \n0728: SS SSD South Sudan \n0724: ES ESP Spain \n0144: LK LKA Sri Lanka \n0729: SD SDN Sudan \n0740: SR SUR Suriname \n0744: SJ SJM Svalbard and Jan Mayen \n0752: SE SWE Sweden \n0756: CH CHE Switzerland \n0760: SY SYR Syrian Arab Republic \n0158: TW TWN Taiwan, Province of China \n0762: TJ TJK Tajikistan \n0834: TZ TZA Tanzania, United Republic of \n0764: TH THA Thailand \n0626: TL TLS Timor-Leste \n0768: TG TGO Togo \n0772: TK TKL Tokelau \n0776: TO TON Tonga \n0780: TT TTO Trinidad and Tobago \n0788: TN TUN Tunisia \n0792: TR TUR Türkiye \n0795: TM TKM Turkmenistan \n0796: TC TCA Turks and Caicos Islands \n0798: TV TUV Tuvalu \n0800: UG UGA Uganda \n0804: UA UKR Ukraine \n0784: AE ARE United Arab Emirates \n0826: GB GBR United Kingdom of Great Britain and Northern Ireland \n0840: US USA United States of America \n0581: UM UMI United States Minor Outlying Islands \n0858: UY URY Uruguay \n0860: UZ UZB Uzbekistan \n0548: VU VUT Vanuatu \n0862: VE VEN Venezuela, Bolivarian Republic of \n0704: VN VNM Viet Nam \n0092: VG VGB Virgin Islands (British) \n0850: VI VIR Virgin Islands (U.S.) \n0876: WF WLF Wallis and Futuna \n0732: EH ESH Western Sahara \n0887: YE YEM Yemen \n0894: ZM ZMB Zambia \n0716: ZW ZWE Zimbabwe "
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene.c",
    "content": "#include \"lfrfid_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const lfrfid_on_enter_handlers[])(void*) = {\n#include \"lfrfid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const lfrfid_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"lfrfid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const lfrfid_on_exit_handlers[])(void* context) = {\n#include \"lfrfid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers lfrfid_scene_handlers = {\n    .on_enter_handlers = lfrfid_on_enter_handlers,\n    .on_event_handlers = lfrfid_on_event_handlers,\n    .on_exit_handlers = lfrfid_on_exit_handlers,\n    .scene_num = LfRfidSceneNum,\n};\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) LfRfidScene##id,\ntypedef enum {\n#include \"lfrfid_scene_config.h\"\n    LfRfidSceneNum,\n} LfRfidScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers lfrfid_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"lfrfid_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"lfrfid_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"lfrfid_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_config.h",
    "content": "ADD_SCENE(lfrfid, start, Start)\nADD_SCENE(lfrfid, read, Read)\nADD_SCENE(lfrfid, read_success, ReadSuccess)\nADD_SCENE(lfrfid, retry_confirm, RetryConfirm)\nADD_SCENE(lfrfid, exit_confirm, ExitConfirm)\nADD_SCENE(lfrfid, delete_confirm, DeleteConfirm)\nADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu)\nADD_SCENE(lfrfid, write, Write)\nADD_SCENE(lfrfid, write_success, WriteSuccess)\nADD_SCENE(lfrfid, emulate, Emulate)\nADD_SCENE(lfrfid, save_name, SaveName)\nADD_SCENE(lfrfid, save_success, SaveSuccess)\nADD_SCENE(lfrfid, select_key, SelectKey)\nADD_SCENE(lfrfid, saved_key_menu, SavedKeyMenu)\nADD_SCENE(lfrfid, save_data, SaveData)\nADD_SCENE(lfrfid, save_type, SaveType)\nADD_SCENE(lfrfid, saved_info, SavedInfo)\nADD_SCENE(lfrfid, delete_success, DeleteSuccess)\nADD_SCENE(lfrfid, extra_actions, ExtraActions)\nADD_SCENE(lfrfid, raw_info, RawInfo)\nADD_SCENE(lfrfid, raw_name, RawName)\nADD_SCENE(lfrfid, raw_read, RawRead)\nADD_SCENE(lfrfid, raw_success, RawSuccess)\nADD_SCENE(lfrfid, rpc, Rpc)\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c",
    "content": "#include \"../lfrfid_i.h\"\n\n#define LFRFID_SCENE_DELETE_MAX_HEX_WIDTH (7UL)\n\nvoid lfrfid_scene_delete_confirm_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    FuriString* display_text = furi_string_alloc_printf(\n        \"\\e#Delete %s?\\e#\\n\"\n        \"Hex: \",\n        furi_string_get_cstr(app->file_name));\n\n    const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n    uint8_t* data = malloc(data_size);\n\n    protocol_dict_get_data(app->dict, app->protocol_id, data, data_size);\n\n    for(size_t i = 0; i < data_size; i++) {\n        if(i == LFRFID_SCENE_DELETE_MAX_HEX_WIDTH) {\n            furi_string_cat(display_text, \" ...\");\n            break;\n        }\n\n        furi_string_cat_printf(display_text, \"%s%02X\", i != 0 ? \" \" : \"\", data[i]);\n    }\n\n    furi_string_push_back(display_text, '\\n');\n\n    free(data);\n\n    const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id);\n    const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id);\n\n    if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, \"N/A\") != 0) {\n        furi_string_cat_printf(display_text, \"%s \", manufacturer);\n    }\n\n    furi_string_cat(display_text, protocol);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true);\n    widget_add_button_element(widget, GuiButtonTypeLeft, \"Cancel\", lfrfid_widget_callback, app);\n    widget_add_button_element(widget, GuiButtonTypeRight, \"Delete\", lfrfid_widget_callback, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n    furi_string_free(display_text);\n}\n\nbool lfrfid_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true; // Ignore Back button presses\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(scene_manager);\n        } else if(event.event == GuiButtonTypeRight) {\n            lfrfid_delete_key(app);\n            scene_manager_next_scene(scene_manager, LfRfidSceneDeleteSuccess);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_delete_confirm_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_delete_success_on_enter(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(popup, \"Deleted\", 80, 19, AlignLeft, AlignBottom);\n    popup_set_context(popup, app);\n    popup_set_callback(popup, lfrfid_popup_timeout_callback);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n}\n\nbool lfrfid_scene_delete_success_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) {\n        // Always return to SceneSelectKey from here\n        scene_manager_search_and_switch_to_another_scene(app->scene_manager, LfRfidSceneSelectKey);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_delete_success_on_exit(void* context) {\n    LfRfid* app = context;\n\n    popup_reset(app->popup);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_emulate.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_emulate_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    FuriString* display_text = furi_string_alloc_set(\"\\e#Emulating\\e#\\n\");\n\n    if(furi_string_empty(app->file_name)) {\n        furi_string_cat(display_text, \"Unsaved\\n\");\n        furi_string_cat(display_text, protocol_dict_get_name(app->dict, app->protocol_id));\n    } else {\n        furi_string_cat(display_text, app->file_name);\n    }\n\n    widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);\n    widget_add_text_box_element(\n        widget, 55, 16, 67, 48, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true);\n\n    furi_string_free(display_text);\n\n    lfrfid_worker_start_thread(app->lfworker);\n    lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);\n    notification_message(app->notifications, &sequence_blink_start_magenta);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n}\n\nbool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    bool consumed = false;\n    return consumed;\n}\n\nvoid lfrfid_scene_emulate_on_exit(void* context) {\n    LfRfid* app = context;\n    notification_message(app->notifications, &sequence_blink_stop);\n    widget_reset(app->widget);\n    lfrfid_worker_stop(app->lfworker);\n    lfrfid_worker_stop_thread(app->lfworker);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_exit_confirm_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    widget_add_button_element(widget, GuiButtonTypeLeft, \"Exit\", lfrfid_widget_callback, app);\n    widget_add_button_element(widget, GuiButtonTypeRight, \"Stay\", lfrfid_widget_callback, app);\n    widget_add_string_element(\n        widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"Exit to RFID Menu?\");\n    widget_add_string_element(\n        widget, 64, 13, AlignCenter, AlignTop, FontSecondary, \"All unsaved data will be lost\");\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n}\n\nbool lfrfid_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true; // Ignore Back button presses\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneStart);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_exit_confirm_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\ntypedef enum {\n    SubmenuIndexASK,\n    SubmenuIndexPSK,\n    SubmenuIndexRAW,\n} SubmenuIndex;\n\nstatic void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_scene_extra_actions_on_enter(void* context) {\n    LfRfid* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Read ASK (Animal, Ordinary Card)\",\n        SubmenuIndexASK,\n        lfrfid_scene_extra_actions_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Read PSK (Indala)\",\n        SubmenuIndexPSK,\n        lfrfid_scene_extra_actions_submenu_callback,\n        app);\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        submenu_add_item(\n            submenu,\n            \"Read RAW RFID data\",\n            SubmenuIndexRAW,\n            lfrfid_scene_extra_actions_submenu_callback,\n            app);\n    }\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneExtraActions));\n\n    // clear key\n    furi_string_reset(app->file_name);\n    app->protocol_id = PROTOCOL_NO;\n    app->read_type = LFRFIDWorkerReadTypeAuto;\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);\n}\n\nbool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexASK) {\n            app->read_type = LFRFIDWorkerReadTypeASKOnly;\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);\n            dolphin_deed(DolphinDeedRfidRead);\n            consumed = true;\n        } else if(event.event == SubmenuIndexPSK) {\n            app->read_type = LFRFIDWorkerReadTypePSKOnly;\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);\n            dolphin_deed(DolphinDeedRfidRead);\n            consumed = true;\n        } else if(event.event == SubmenuIndexRAW) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event);\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, 0);\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_extra_actions_on_exit(void* context) {\n    LfRfid* app = context;\n\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_raw_info_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    if(storage_sd_status(app->storage) != FSE_OK) {\n        widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n        widget_add_string_element(\n            widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"No SD Card!\");\n        widget_add_string_multiline_element(\n            widget,\n            0,\n            13,\n            AlignLeft,\n            AlignTop,\n            FontSecondary,\n            \"Insert an SD card\\n\"\n            \"to use this function\");\n\n    } else {\n        widget_add_text_box_element(\n            widget,\n            0,\n            0,\n            128,\n            64,\n            AlignLeft,\n            AlignTop,\n            \"\\e#RAW RFID Data Reader\\e#\\n\"\n            \"1. Hold card next to Flipper\\n\"\n            \"2. Press OK\\n\"\n            \"3. Wait until data is read\",\n            false);\n\n        widget_add_button_element(widget, GuiButtonTypeCenter, \"OK\", lfrfid_widget_callback, app);\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n}\n\nbool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n        scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneExtraActions);\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeCenter) {\n            scene_manager_next_scene(scene_manager, LfRfidSceneRawRead);\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_search_and_switch_to_previous_scene(\n                scene_manager, LfRfidSceneExtraActions);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_raw_info_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_raw_name.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_raw_name_on_enter(void* context) {\n    LfRfid* app = context;\n    TextInput* text_input = app->text_input;\n\n    const char* key_name = furi_string_get_cstr(app->raw_file_name);\n\n    bool key_name_is_empty = furi_string_empty(app->file_name);\n    if(key_name_is_empty) {\n        lfrfid_text_store_set(app, \"RfidRecord\");\n    } else {\n        lfrfid_text_store_set(app, \"%s\", key_name);\n    }\n\n    text_input_set_header_text(text_input, \"Name the raw file\");\n\n    text_input_set_result_callback(\n        text_input,\n        lfrfid_text_input_callback,\n        app,\n        app->text_store,\n        LFRFID_KEY_NAME_SIZE,\n        key_name_is_empty);\n\n    ValidatorIsFile* validator_is_file =\n        validator_is_file_alloc_init(LFRFID_SD_FOLDER, LFRFID_APP_RAW_ASK_EXTENSION, NULL);\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput);\n}\n\nbool lfrfid_scene_raw_name_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventNext) {\n            consumed = true;\n            furi_string_set(app->raw_file_name, app->text_store);\n            scene_manager_next_scene(scene_manager, LfRfidSceneRawInfo);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_raw_name_on_exit(void* context) {\n    LfRfid* app = context;\n    TextInput* text_input = app->text_input;\n\n    void* validator_context = text_input_get_validator_callback_context(text_input);\n    text_input_set_validator(text_input, NULL, NULL);\n    validator_is_file_free((ValidatorIsFile*)validator_context);\n\n    text_input_reset(text_input);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c",
    "content": "#include \"../lfrfid_i.h\"\n\n#define RAW_READ_TIME_MS (5000UL)\n\ntypedef struct {\n    FuriString* string_file_name;\n    FuriTimer* timer;\n    bool is_psk;\n    bool error;\n} LfRfidReadRawState;\n\nstatic void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* context) {\n    LfRfid* app = context;\n\n    if(result == LFRFIDWorkerReadRawFileError) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError);\n    } else if(result == LFRFIDWorkerReadRawOverrun) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun);\n    }\n}\n\nstatic void timer_callback(void* context) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadDone);\n}\n\nvoid lfrfid_scene_raw_read_on_enter(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    popup_set_icon(popup, 0, 0, &I_NFC_dolphin_emulation_51x64);\n    popup_set_header(popup, \"Reading ASK\", 91, 16, AlignCenter, AlignTop);\n    popup_set_text(popup, \"Don't move\\nfor 5 sec.\", 91, 29, AlignCenter, AlignTop);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n\n    LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState));\n    state->string_file_name = furi_string_alloc();\n    state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app);\n\n    scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state);\n\n    furi_string_printf(\n        state->string_file_name,\n        \"%s/%s%s\",\n        LFRFID_SD_FOLDER,\n        furi_string_get_cstr(app->raw_file_name),\n        LFRFID_APP_RAW_ASK_EXTENSION);\n\n    lfrfid_make_app_folder(app);\n\n    lfrfid_worker_start_thread(app->lfworker);\n    lfrfid_worker_read_raw_start(\n        app->lfworker,\n        furi_string_get_cstr(state->string_file_name),\n        LFRFIDWorkerReadTypeASKOnly,\n        lfrfid_read_callback,\n        app);\n\n    furi_timer_start(state->timer, RAW_READ_TIME_MS);\n    notification_message(app->notifications, &sequence_blink_start_cyan);\n\n    state->is_psk = false;\n    state->error = false;\n}\n\nbool lfrfid_scene_raw_read_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n    LfRfidReadRawState* state =\n        (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead);\n    bool consumed = false;\n\n    furi_assert(state);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventReadError || event.event == LfRfidEventReadOverrun) {\n            furi_timer_stop(state->timer);\n\n            popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n            popup_set_header(popup, \"RAW Reading error!\", 64, 0, AlignCenter, AlignTop);\n            popup_set_text(\n                popup, \"This may be\\ncaused by SD\\ncard issues\", 0, 13, AlignLeft, AlignTop);\n\n            notification_message(app->notifications, &sequence_blink_start_red);\n            state->error = true;\n\n        } else if(event.event == LfRfidEventReadDone) {\n            if(!state->error) {\n                if(state->is_psk) {\n                    notification_message(app->notifications, &sequence_success);\n                    scene_manager_next_scene(app->scene_manager, LfRfidSceneRawSuccess);\n\n                } else {\n                    lfrfid_worker_stop(app->lfworker);\n                    lfrfid_worker_stop_thread(app->lfworker);\n\n                    state->is_psk = true;\n\n                    furi_string_printf(\n                        state->string_file_name,\n                        \"%s/%s%s\",\n                        LFRFID_SD_FOLDER,\n                        furi_string_get_cstr(app->raw_file_name),\n                        LFRFID_APP_RAW_PSK_EXTENSION);\n\n                    lfrfid_worker_start_thread(app->lfworker);\n                    lfrfid_worker_read_raw_start(\n                        app->lfworker,\n                        furi_string_get_cstr(state->string_file_name),\n                        LFRFIDWorkerReadTypePSKOnly,\n                        lfrfid_read_callback,\n                        app);\n\n                    furi_timer_start(state->timer, RAW_READ_TIME_MS);\n\n                    popup_set_header(popup, \"Reading PSK\", 91, 16, AlignCenter, AlignTop);\n                    notification_message(app->notifications, &sequence_blink_start_yellow);\n                }\n            }\n        }\n\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_raw_read_on_exit(void* context) {\n    LfRfid* app = context;\n    LfRfidReadRawState* state =\n        (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead);\n\n    lfrfid_worker_stop(app->lfworker);\n    lfrfid_worker_stop_thread(app->lfworker);\n\n    furi_timer_free(state->timer);\n    furi_string_free(state->string_file_name);\n    free(state);\n\n    popup_reset(app->popup);\n    notification_message(app->notifications, &sequence_blink_stop);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_raw_success_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    widget_add_text_box_element(\n        widget,\n        0,\n        0,\n        128,\n        64,\n        AlignLeft,\n        AlignTop,\n        \"\\e#RAW RFID Read Success\\e#\\n\"\n        \"Now you can analyze files or\\n\"\n        \"send them to developers\",\n        false);\n\n    widget_add_button_element(widget, GuiButtonTypeCenter, \"OK\", lfrfid_widget_callback, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n}\n\nbool lfrfid_scene_raw_success_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeCenter) {\n            scene_manager_search_and_switch_to_previous_scene(\n                scene_manager, LfRfidSceneExtraActions);\n        }\n        consumed = true;\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_raw_success_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_read.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\nstatic const NotificationSequence sequence_blink_set_yellow = {\n    &message_blink_set_color_yellow,\n    NULL,\n};\n\nstatic const NotificationSequence sequence_blink_set_green = {\n    &message_blink_set_color_green,\n    NULL,\n};\n\nstatic const NotificationSequence sequence_blink_set_cyan = {\n    &message_blink_set_color_cyan,\n    NULL,\n};\n\nstatic void\n    lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) {\n    LfRfid* app = context;\n    uint32_t event = 0;\n\n    if(result == LFRFIDWorkerReadSenseStart) {\n        event = LfRfidEventReadSenseStart;\n    } else if(result == LFRFIDWorkerReadSenseEnd) {\n        event = LfRfidEventReadSenseEnd;\n    } else if(result == LFRFIDWorkerReadSenseCardStart) {\n        event = LfRfidEventReadSenseCardStart;\n    } else if(result == LFRFIDWorkerReadSenseCardEnd) {\n        event = LfRfidEventReadSenseCardEnd;\n    } else if(result == LFRFIDWorkerReadDone) {\n        event = LfRfidEventReadDone;\n        app->protocol_id_next = protocol;\n    } else if(result == LFRFIDWorkerReadStartASK) {\n        event = LfRfidEventReadStartASK;\n    } else if(result == LFRFIDWorkerReadStartPSK) {\n        event = LfRfidEventReadStartPSK;\n    } else {\n        return;\n    }\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid lfrfid_scene_read_on_enter(void* context) {\n    LfRfid* app = context;\n\n    if(app->read_type == LFRFIDWorkerReadTypePSKOnly) {\n        lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly);\n    } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) {\n        lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly);\n    }\n\n    lfrfid_worker_start_thread(app->lfworker);\n    lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app);\n\n    notification_message(app->notifications, &sequence_blink_start_cyan);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewRead);\n}\n\nbool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventReadSenseStart) {\n            notification_message(app->notifications, &sequence_blink_set_yellow);\n            consumed = true;\n        } else if(event.event == LfRfidEventReadSenseCardStart) {\n            notification_message(app->notifications, &sequence_blink_set_green);\n            consumed = true;\n        } else if(\n            (event.event == LfRfidEventReadSenseEnd) ||\n            (event.event == LfRfidEventReadSenseCardEnd)) {\n            notification_message(app->notifications, &sequence_blink_set_cyan);\n            consumed = true;\n        } else if(event.event == LfRfidEventReadDone) {\n            app->protocol_id = app->protocol_id_next;\n            notification_message(app->notifications, &sequence_success);\n            furi_string_reset(app->file_name);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess);\n            dolphin_deed(DolphinDeedRfidReadSuccess);\n            consumed = true;\n        } else if(event.event == LfRfidEventReadStartPSK) {\n            if(app->read_type == LFRFIDWorkerReadTypeAuto) {\n                lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPsk);\n            }\n            consumed = true;\n        } else if(event.event == LfRfidEventReadStartASK) {\n            if(app->read_type == LFRFIDWorkerReadTypeAuto) {\n                lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk);\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_read_on_exit(void* context) {\n    LfRfid* app = context;\n    notification_message(app->notifications, &sequence_blink_stop);\n    popup_reset(app->popup);\n    lfrfid_worker_stop(app->lfworker);\n    lfrfid_worker_stop_thread(app->lfworker);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\ntypedef enum {\n    SubmenuIndexSave,\n    SubmenuIndexEmulate,\n    SubmenuIndexWrite,\n} SubmenuIndex;\n\nvoid lfrfid_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_scene_read_key_menu_on_enter(void* context) {\n    LfRfid* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu, \"Save\", SubmenuIndexSave, lfrfid_scene_read_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Emulate\", SubmenuIndexEmulate, lfrfid_scene_read_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Write\", SubmenuIndexWrite, lfrfid_scene_read_key_menu_submenu_callback, app);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);\n}\n\nbool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexWrite) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSave) {\n            furi_string_reset(app->file_name);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveName);\n            consumed = true;\n        } else if(event.event == SubmenuIndexEmulate) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);\n            dolphin_deed(DolphinDeedRfidEmulate);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event);\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, 0);\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_read_key_menu_on_exit(void* context) {\n    LfRfid* app = context;\n\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_read_success.c",
    "content": "#include \"../lfrfid_i.h\"\n\n#define LFRFID_SCENE_READ_SUCCESS_MAX_HEX_WIDTH (7UL)\n\nvoid lfrfid_scene_read_success_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n    FuriString* display_text = furi_string_alloc();\n\n    const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id);\n    const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id);\n\n    if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, \"N/A\") != 0) {\n        furi_string_printf(display_text, \"\\e#%s %s\\e#\", manufacturer, protocol);\n    } else {\n        furi_string_printf(display_text, \"\\e#%s\\e#\", protocol);\n    }\n\n    furi_string_cat(display_text, \"\\nHex: \");\n\n    const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n    uint8_t* data = malloc(data_size);\n\n    protocol_dict_get_data(app->dict, app->protocol_id, data, data_size);\n\n    for(size_t i = 0; i < data_size; i++) {\n        if(i == LFRFID_SCENE_READ_SUCCESS_MAX_HEX_WIDTH) {\n            furi_string_cat(display_text, \" ...\");\n            break;\n        }\n\n        furi_string_cat_printf(display_text, \"%s%02X\", i != 0 ? \" \" : \"\", data[i]);\n    }\n\n    free(data);\n\n    FuriString* rendered_data = furi_string_alloc();\n    protocol_dict_render_brief_data(app->dict, rendered_data, app->protocol_id);\n    furi_string_cat_printf(display_text, \"\\n%s\", furi_string_get_cstr(rendered_data));\n    furi_string_free(rendered_data);\n\n    widget_add_text_box_element(\n        widget, 0, 0, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true);\n    widget_add_button_element(widget, GuiButtonTypeLeft, \"Retry\", lfrfid_widget_callback, app);\n    widget_add_button_element(widget, GuiButtonTypeRight, \"More\", lfrfid_widget_callback, app);\n\n    notification_message_block(app->notifications, &sequence_set_green_255);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n    furi_string_free(display_text);\n}\n\nbool lfrfid_scene_read_success_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_next_scene(scene_manager, LfRfidSceneExitConfirm);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_next_scene(scene_manager, LfRfidSceneRetryConfirm);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(scene_manager, LfRfidSceneReadKeyMenu);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_read_success_on_exit(void* context) {\n    LfRfid* app = context;\n    notification_message_block(app->notifications, &sequence_reset_green);\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_retry_confirm_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    widget_add_button_element(widget, GuiButtonTypeLeft, \"Retry\", lfrfid_widget_callback, app);\n    widget_add_button_element(widget, GuiButtonTypeRight, \"Stay\", lfrfid_widget_callback, app);\n    widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"Retry Reading?\");\n    widget_add_string_element(\n        widget, 64, 13, AlignCenter, AlignTop, FontSecondary, \"All unsaved data will be lost\");\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n}\n\nbool lfrfid_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        consumed = true; // Ignore Back button presses\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneRead);\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_previous_scene(scene_manager);\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_retry_confirm_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_rpc.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_rpc_on_enter(void* context) {\n    LfRfid* app = context;\n    app->rpc_state = LfRfidRpcStateIdle;\n}\n\nstatic void lfrfid_rpc_start_emulation(LfRfid* app) {\n    Popup* popup = app->popup;\n\n    lfrfid_text_store_set(app, \"emulating\\n%s\", furi_string_get_cstr(app->file_name));\n\n    popup_set_header(popup, \"LF RFID\", 89, 42, AlignCenter, AlignBottom);\n    popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop);\n    popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n\n    lfrfid_worker_start_thread(app->lfworker);\n    lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);\n\n    notification_message(app->notifications, &sequence_display_backlight_on);\n    notification_message(app->notifications, &sequence_blink_start_magenta);\n    app->rpc_state = LfRfidRpcStateEmulating;\n}\n\nbool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == LfRfidEventExit) {\n            rpc_system_app_confirm(app->rpc_ctx, true);\n            scene_manager_stop(app->scene_manager);\n            view_dispatcher_stop(app->view_dispatcher);\n        } else if(event.event == LfRfidEventRpcSessionClose) {\n            scene_manager_stop(app->scene_manager);\n            view_dispatcher_stop(app->view_dispatcher);\n        } else if(event.event == LfRfidEventRpcLoadFile) {\n            bool result = false;\n            if(app->rpc_state == LfRfidRpcStateIdle) {\n                if(lfrfid_load_key_data(app, app->file_path, false)) {\n                    lfrfid_rpc_start_emulation(app);\n                    result = true;\n                } else {\n                    rpc_system_app_set_error_code(app->rpc_ctx, RpcAppSystemErrorCodeParseFile);\n                    rpc_system_app_set_error_text(app->rpc_ctx, \"Cannot load key file\");\n                }\n            }\n            rpc_system_app_confirm(app->rpc_ctx, result);\n        }\n    }\n    return consumed;\n}\n\nvoid lfrfid_scene_rpc_on_exit(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    if(app->rpc_state == LfRfidRpcStateEmulating) {\n        lfrfid_worker_stop(app->lfworker);\n        lfrfid_worker_stop_thread(app->lfworker);\n        notification_message(app->notifications, &sequence_blink_stop);\n    }\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_save_data.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_save_data_on_enter(void* context) {\n    LfRfid* app = context;\n    ByteInput* byte_input = app->byte_input;\n\n    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n\n    bool need_restore = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveData);\n\n    if(!need_restore) {\n        protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);\n        protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size);\n    }\n\n    byte_input_set_header_text(byte_input, \"Enter the data in hex\");\n\n    byte_input_set_result_callback(\n        byte_input, lfrfid_text_input_callback, NULL, app, app->new_key_data, size);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput);\n}\n\nbool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventNext) {\n            consumed = true;\n            size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n            protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size);\n\n            if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) {\n                scene_manager_next_scene(scene_manager, LfRfidSceneSaveName);\n            } else {\n                if(!furi_string_empty(app->file_name)) {\n                    lfrfid_delete_key(app);\n                }\n\n                if(lfrfid_save_key(app)) {\n                    scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess);\n                } else {\n                    scene_manager_search_and_switch_to_previous_scene(\n                        scene_manager, LfRfidSceneSavedKeyMenu);\n                }\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 0);\n        size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n        protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_save_data_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_save_name.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n#include <toolbox/name_generator.h>\n\nvoid lfrfid_scene_save_name_on_enter(void* context) {\n    LfRfid* app = context;\n    TextInput* text_input = app->text_input;\n    FuriString* folder_path;\n    folder_path = furi_string_alloc();\n\n    bool key_name_is_empty = furi_string_empty(app->file_name);\n    if(key_name_is_empty) {\n        furi_string_set(app->file_path, LFRFID_APP_FOLDER);\n\n        name_generator_make_auto(\n            app->text_store, LFRFID_TEXT_STORE_SIZE, LFRFID_APP_FILENAME_PREFIX);\n\n        furi_string_set(folder_path, LFRFID_APP_FOLDER);\n    } else {\n        lfrfid_text_store_set(app, \"%s\", furi_string_get_cstr(app->file_name));\n        path_extract_dirname(furi_string_get_cstr(app->file_path), folder_path);\n    }\n\n    text_input_set_header_text(text_input, \"Name the card\");\n    text_input_set_result_callback(\n        text_input,\n        lfrfid_text_input_callback,\n        app,\n        app->text_store,\n        LFRFID_KEY_NAME_SIZE,\n        key_name_is_empty);\n\n    FURI_LOG_I(\"\", \"%s %s\", furi_string_get_cstr(folder_path), app->text_store);\n\n    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n        furi_string_get_cstr(folder_path),\n        LFRFID_APP_FILENAME_EXTENSION,\n        furi_string_get_cstr(app->file_name));\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    furi_string_free(folder_path);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput);\n}\n\nbool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    SceneManager* scene_manager = app->scene_manager;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventNext) {\n            consumed = true;\n            if(!furi_string_empty(app->file_name)) {\n                lfrfid_delete_key(app);\n            }\n\n            furi_string_set(app->file_name, app->text_store);\n\n            if(lfrfid_save_key(app)) {\n                scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess);\n                if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) {\n                    // Nothing, do not count editing as saving\n                } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) {\n                    dolphin_deed(DolphinDeedRfidAdd);\n                } else {\n                    dolphin_deed(DolphinDeedRfidSave);\n                }\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    scene_manager, LfRfidSceneReadKeyMenu);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_save_name_on_exit(void* context) {\n    LfRfid* app = context;\n    TextInput* text_input = app->text_input;\n\n    void* validator_context = text_input_get_validator_callback_context(text_input);\n    text_input_set_validator(text_input, NULL, NULL);\n    validator_is_file_free((ValidatorIsFile*)validator_context);\n\n    text_input_reset(text_input);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_save_success.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_save_success_on_enter(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    // Clear state of data enter scene\n    scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0);\n\n    popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n    popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    popup_set_context(popup, app);\n    popup_set_callback(popup, lfrfid_popup_timeout_callback);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n}\n\nbool lfrfid_scene_save_success_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) {\n        // Always return to SceneSelectKey from here\n        scene_manager_search_and_switch_to_another_scene(app->scene_manager, LfRfidSceneSelectKey);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_save_success_on_exit(void* context) {\n    LfRfid* app = context;\n\n    popup_reset(app->popup);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_save_type.c",
    "content": "#include \"../lfrfid_i.h\"\n\ntypedef struct {\n    uint32_t line_sel;\n} SaveTypeCtx;\n\nstatic void lfrfid_scene_save_type_submenu_callback(void* context, uint32_t index) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_scene_save_type_on_enter(void* context) {\n    LfRfid* app = context;\n    Submenu* submenu = app->submenu;\n\n    SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx));\n    FuriString* protocol_string = furi_string_alloc();\n    for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) {\n        if(strcmp(\n               protocol_dict_get_manufacturer(app->dict, i),\n               protocol_dict_get_name(app->dict, i)) != 0) {\n            furi_string_printf(\n                protocol_string,\n                \"%s %s\",\n                protocol_dict_get_manufacturer(app->dict, i),\n                protocol_dict_get_name(app->dict, i));\n        } else {\n            furi_string_printf(protocol_string, \"%s\", protocol_dict_get_name(app->dict, i));\n        }\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(protocol_string),\n            i,\n            lfrfid_scene_save_type_submenu_callback,\n            app);\n    }\n    furi_string_free(protocol_string);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType));\n\n    scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, (uint32_t)state);\n\n    // clear key name\n    furi_string_reset(app->file_name);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);\n}\n\nbool lfrfid_scene_save_type_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    SaveTypeCtx* state =\n        (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType);\n    furi_check(state);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        app->protocol_id = event.event;\n        state->line_sel = event.event;\n        scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_save_type_on_exit(void* context) {\n    LfRfid* app = context;\n    SaveTypeCtx* state =\n        (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType);\n    furi_check(state);\n\n    submenu_reset(app->submenu);\n\n    uint32_t line_sel = state->line_sel;\n    free(state);\n    scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, line_sel);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_saved_info_on_enter(void* context) {\n    LfRfid* app = context;\n    Widget* widget = app->widget;\n\n    FuriString* display_text = furi_string_alloc();\n\n    furi_string_printf(display_text, \"Name: %s\\n\", furi_string_get_cstr(app->file_name));\n\n    const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id);\n    const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id);\n\n    if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, \"N/A\") != 0) {\n        furi_string_cat_printf(display_text, \"\\e#%s %s\", manufacturer, protocol);\n    } else {\n        furi_string_cat_printf(display_text, \"\\e#%s\", protocol);\n    }\n\n    furi_string_cat(display_text, \"\\nHex: \");\n\n    const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n    uint8_t* data = malloc(data_size);\n\n    protocol_dict_get_data(app->dict, app->protocol_id, data, data_size);\n\n    for(size_t i = 0; i < data_size; i++) {\n        furi_string_cat_printf(display_text, \"%s%02X\", i != 0 ? \" \" : \"\", data[i]);\n    }\n\n    free(data);\n\n    FuriString* rendered_data;\n    rendered_data = furi_string_alloc();\n    protocol_dict_render_data(app->dict, rendered_data, app->protocol_id);\n    furi_string_cat_printf(display_text, \"\\n%s\", furi_string_get_cstr(rendered_data));\n    furi_string_free(rendered_data);\n\n    widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(display_text));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);\n    furi_string_free(display_text);\n}\n\nbool lfrfid_scene_saved_info_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    bool consumed = false;\n    return consumed;\n}\n\nvoid lfrfid_scene_saved_info_on_exit(void* context) {\n    LfRfid* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\ntypedef enum {\n    SubmenuIndexEmulate,\n    SubmenuIndexWrite,\n    SubmenuIndexEdit,\n    SubmenuIndexRename,\n    SubmenuIndexDelete,\n    SubmenuIndexInfo,\n} SubmenuIndex;\n\nstatic void lfrfid_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_scene_saved_key_menu_on_enter(void* context) {\n    LfRfid* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu, \"Emulate\", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Write\", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Edit\", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Rename\", SubmenuIndexRename, lfrfid_scene_saved_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Delete\", SubmenuIndexDelete, lfrfid_scene_saved_key_menu_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Info\", SubmenuIndexInfo, lfrfid_scene_saved_key_menu_submenu_callback, app);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);\n}\n\nbool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexEmulate) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);\n            dolphin_deed(DolphinDeedRfidEmulate);\n            consumed = true;\n        } else if(event.event == SubmenuIndexWrite) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);\n            consumed = true;\n        } else if(event.event == SubmenuIndexEdit) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);\n            consumed = true;\n        } else if(event.event == SubmenuIndexRename) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveName);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDelete) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneDeleteConfirm);\n            consumed = true;\n        } else if(event.event == SubmenuIndexInfo) {\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedInfo);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, event.event);\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, 0);\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_saved_key_menu_on_exit(void* context) {\n    LfRfid* app = context;\n\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_select_key.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_select_key_on_enter(void* context) {\n    LfRfid* app = context;\n\n    if(lfrfid_load_key_from_file_select(app)) {\n        scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedKeyMenu);\n    } else {\n        // Always select \"Saved\" menu item when returning from this scene\n        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexSaved);\n        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, LfRfidSceneStart);\n    }\n}\n\nbool lfrfid_scene_select_key_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    bool consumed = false;\n    return consumed;\n}\n\nvoid lfrfid_scene_select_key_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_start.c",
    "content": "#include \"../lfrfid_i.h\"\n#include <dolphin/dolphin.h>\n\nstatic void lfrfid_scene_start_submenu_callback(void* context, uint32_t index) {\n    LfRfid* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid lfrfid_scene_start_on_enter(void* context) {\n    LfRfid* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_add_item(\n        submenu, \"Read\", LfRfidMenuIndexRead, lfrfid_scene_start_submenu_callback, app);\n    submenu_add_item(\n        submenu, \"Saved\", LfRfidMenuIndexSaved, lfrfid_scene_start_submenu_callback, app);\n    submenu_add_item(\n        submenu,\n        \"Add Manually\",\n        LfRfidMenuIndexAddManually,\n        lfrfid_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Extra Actions\",\n        LfRfidMenuIndexExtraActions,\n        lfrfid_scene_start_submenu_callback,\n        app);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneStart));\n\n    // clear key\n    furi_string_reset(app->file_name);\n    app->protocol_id = PROTOCOL_NO;\n    app->read_type = LFRFIDWorkerReadTypeAuto;\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);\n}\n\nbool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidMenuIndexRead) {\n            scene_manager_set_scene_state(\n                app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexRead);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);\n            dolphin_deed(DolphinDeedRfidRead);\n            consumed = true;\n        } else if(event.event == LfRfidMenuIndexSaved) {\n            // Like in the other apps, explicitly save the scene state\n            // in each branch in case the user cancels loading a file.\n            scene_manager_set_scene_state(\n                app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexSaved);\n            furi_string_set(app->file_path, LFRFID_APP_FOLDER);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);\n            consumed = true;\n        } else if(event.event == LfRfidMenuIndexAddManually) {\n            scene_manager_set_scene_state(\n                app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexAddManually);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);\n            consumed = true;\n        } else if(event.event == LfRfidMenuIndexExtraActions) {\n            scene_manager_set_scene_state(\n                app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexExtraActions);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_start_on_exit(void* context) {\n    LfRfid* app = context;\n\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_write.c",
    "content": "#include \"../lfrfid_i.h\"\n\nstatic void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) {\n    LfRfid* app = context;\n    uint32_t event = 0;\n\n    if(result == LFRFIDWorkerWriteOK) {\n        event = LfRfidEventWriteOK;\n    } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {\n        event = LfRfidEventWriteProtocolCannotBeWritten;\n    } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {\n        event = LfRfidEventWriteFobCannotBeWritten;\n    } else if(result == LFRFIDWorkerWriteTooLongToWrite) {\n        event = LfRfidEventWriteTooLongToWrite;\n    }\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, event);\n}\n\nvoid lfrfid_scene_write_on_enter(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);\n    popup_set_header(popup, \"Writing\", 94, 16, AlignCenter, AlignTop);\n\n    if(!furi_string_empty(app->file_name)) {\n        popup_set_text(popup, furi_string_get_cstr(app->file_name), 94, 29, AlignCenter, AlignTop);\n    } else {\n        snprintf(\n            app->text_store,\n            LFRFID_TEXT_STORE_SIZE,\n            \"Unsaved\\n%s\",\n            protocol_dict_get_name(app->dict, app->protocol_id));\n        popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop);\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n\n    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n    protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);\n\n    lfrfid_worker_start_thread(app->lfworker);\n    lfrfid_worker_write_start(\n        app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app);\n    notification_message(app->notifications, &sequence_blink_start_magenta);\n}\n\nbool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == LfRfidEventWriteOK) {\n            notification_message(app->notifications, &sequence_success);\n            scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);\n            consumed = true;\n        } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {\n            popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n            popup_set_header(popup, \"Error\", 64, 3, AlignCenter, AlignTop);\n            popup_set_text(popup, \"This protocol\\ncannot be written\", 3, 17, AlignLeft, AlignTop);\n            notification_message(app->notifications, &sequence_blink_start_red);\n            consumed = true;\n        } else if(\n            (event.event == LfRfidEventWriteFobCannotBeWritten) ||\n            (event.event == LfRfidEventWriteTooLongToWrite)) {\n            popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n            popup_set_header(popup, \"Still Trying to Write...\", 64, 0, AlignCenter, AlignTop);\n            popup_set_text(\n                popup,\n                \"Make sure this\\n\"\n                \"card is writable\\n\"\n                \"and not protected\",\n                0,\n                13,\n                AlignLeft,\n                AlignTop);\n            notification_message(app->notifications, &sequence_blink_start_yellow);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_write_on_exit(void* context) {\n    LfRfid* app = context;\n    notification_message(app->notifications, &sequence_blink_stop);\n    popup_reset(app->popup);\n    lfrfid_worker_stop(app->lfworker);\n    lfrfid_worker_stop_thread(app->lfworker);\n\n    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);\n    protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/scenes/lfrfid_scene_write_success.c",
    "content": "#include \"../lfrfid_i.h\"\n\nvoid lfrfid_scene_write_success_on_enter(void* context) {\n    LfRfid* app = context;\n    Popup* popup = app->popup;\n\n    popup_set_header(popup, \"Success!\", 75, 10, AlignLeft, AlignTop);\n    popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);\n    popup_set_context(popup, app);\n    popup_set_callback(popup, lfrfid_popup_timeout_callback);\n    popup_set_timeout(popup, 1500);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);\n    notification_message_block(app->notifications, &sequence_set_green_255);\n}\n\nbool lfrfid_scene_write_success_on_event(void* context, SceneManagerEvent event) {\n    LfRfid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) {\n        if(!scene_manager_search_and_switch_to_previous_scene(\n               app->scene_manager, LfRfidSceneReadKeyMenu)) {\n            scene_manager_search_and_switch_to_another_scene(\n                app->scene_manager, LfRfidSceneSelectKey);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid lfrfid_scene_write_success_on_exit(void* context) {\n    LfRfid* app = context;\n    notification_message_block(app->notifications, &sequence_reset_green);\n    popup_reset(app->popup);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/views/lfrfid_view_read.c",
    "content": "#include \"lfrfid_view_read.h\"\n#include <gui/elements.h>\n#include <assets_icons.h>\n\n#define TEMP_STR_LEN 128\n\nstruct LfRfidReadView {\n    View* view;\n};\n\ntypedef struct {\n    IconAnimation* icon;\n    LfRfidReadViewMode read_mode;\n} LfRfidReadViewModel;\n\nstatic void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) {\n    LfRfidReadViewModel* model = _model;\n    canvas_set_color(canvas, ColorBlack);\n\n    canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50);\n\n    canvas_set_font(canvas, FontPrimary);\n\n    if(model->read_mode == LfRfidReadAsk) {\n        canvas_draw_str(canvas, 70, 16, \"Reading 1/2\");\n\n        canvas_draw_str(canvas, 77, 29, \"ASK\");\n        canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7);\n        canvas_draw_icon_animation(canvas, 102, 21, model->icon);\n\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str(canvas, 77, 43, \"PSK\");\n    } else if(model->read_mode == LfRfidReadPsk) {\n        canvas_draw_str(canvas, 70, 16, \"Reading 2/2\");\n\n        canvas_draw_str(canvas, 77, 43, \"PSK\");\n        canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7);\n        canvas_draw_icon_animation(canvas, 102, 35, model->icon);\n\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str(canvas, 77, 29, \"ASK\");\n    } else {\n        canvas_draw_str(canvas, 72, 16, \"Reading\");\n\n        if(model->read_mode == LfRfidReadAskOnly) {\n            canvas_draw_str(canvas, 77, 35, \"ASK\");\n        } else {\n            canvas_draw_str(canvas, 77, 35, \"PSK\");\n        }\n        canvas_draw_icon_animation(canvas, 102, 27, model->icon);\n    }\n\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 61, 56, \"Don't move card\");\n}\n\nvoid lfrfid_view_read_enter(void* context) {\n    LfRfidReadView* read_view = context;\n    with_view_model(\n        read_view->view, LfRfidReadViewModel * model, { icon_animation_start(model->icon); }, true);\n}\n\nvoid lfrfid_view_read_exit(void* context) {\n    LfRfidReadView* read_view = context;\n    with_view_model(\n        read_view->view, LfRfidReadViewModel * model, { icon_animation_stop(model->icon); }, false);\n}\n\nLfRfidReadView* lfrfid_view_read_alloc(void) {\n    LfRfidReadView* read_view = malloc(sizeof(LfRfidReadView));\n    read_view->view = view_alloc();\n    view_set_context(read_view->view, read_view);\n    view_allocate_model(read_view->view, ViewModelTypeLocking, sizeof(LfRfidReadViewModel));\n\n    with_view_model(\n        read_view->view,\n        LfRfidReadViewModel * model,\n        {\n            model->icon = icon_animation_alloc(&A_Round_loader_8x8);\n            view_tie_icon_animation(read_view->view, model->icon);\n        },\n        false);\n\n    view_set_draw_callback(read_view->view, lfrfid_view_read_draw_callback);\n    view_set_enter_callback(read_view->view, lfrfid_view_read_enter);\n    view_set_exit_callback(read_view->view, lfrfid_view_read_exit);\n\n    return read_view;\n}\n\nvoid lfrfid_view_read_free(LfRfidReadView* read_view) {\n    with_view_model(\n        read_view->view, LfRfidReadViewModel * model, { icon_animation_free(model->icon); }, false);\n\n    view_free(read_view->view);\n    free(read_view);\n}\n\nView* lfrfid_view_read_get_view(LfRfidReadView* read_view) {\n    return read_view->view;\n}\n\nvoid lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode) {\n    with_view_model(\n        read_view->view,\n        LfRfidReadViewModel * model,\n        {\n            icon_animation_stop(model->icon);\n            icon_animation_start(model->icon);\n            model->read_mode = mode;\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/main/lfrfid/views/lfrfid_view_read.h",
    "content": "#pragma once\n#include <gui/view.h>\n\ntypedef enum {\n    LfRfidReadAsk,\n    LfRfidReadPsk,\n    LfRfidReadAskOnly,\n    LfRfidReadPskOnly\n} LfRfidReadViewMode;\n\ntypedef struct LfRfidReadView LfRfidReadView;\n\nLfRfidReadView* lfrfid_view_read_alloc(void);\n\nvoid lfrfid_view_read_free(LfRfidReadView* read_view);\n\nView* lfrfid_view_read_get_view(LfRfidReadView* read_view);\n\nvoid lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode);\n"
  },
  {
    "path": "applications/main/nfc/api/gallagher/gallagher_util.c",
    "content": "/* gallagher_util.c - Utilities for parsing Gallagher cards (New Zealand).\n * Author: Nick Mooney (nick@mooney.nz)\n * \n * Reference: https://github.com/megabug/gallagher-research\n*/\n\n#include \"gallagher_util.h\"\n\n#define GALLAGHER_CREDENTIAL_SECTOR 15\n\n/* The Gallagher obfuscation algorithm is a 256-byte substitution table. The below array is generated from\n * https://github.com/megabug/gallagher-research/blob/master/formats/cardholder/substitution-table.bin.\n*/\nconst uint8_t GALLAGHER_DECODE_TABLE[256] = {\n    0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0xf,  0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11,\n    0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1,\n    0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3,\n    0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19,\n    0xe,  0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0,\n    0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0xc,  0xc3, 0x3c, 0x61, 0xcc,\n    0x40, 0x9e, 0x6,  0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0xd,  0xc2,\n    0x88, 0xd1, 0xe0, 0x7,  0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b,\n    0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x2,  0x8,  0x30, 0xa7, 0x22, 0xc9, 0x65,\n    0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f,\n    0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x0,  0x2e,\n    0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x1,  0x48, 0x4,  0xc1,\n    0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0xa,  0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37,\n    0xa2, 0x83, 0xec, 0x3,  0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9,\n    0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89,\n    0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0xb,  0xf1, 0xea, 0xfd, 0xdb,\n    0xbd, 0x9,  0xb5, 0x5b, 0x5,  0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e,\n    0x36};\n\n// The second block of a Gallagher credential sector is the literal\n// \"www.cardax.com  \" (note two padding spaces)\nconst uint8_t GALLAGHER_CARDAX_ASCII[MF_CLASSIC_BLOCK_SIZE] =\n    {'w', 'w', 'w', '.', 'c', 'a', 'r', 'd', 'a', 'x', '.', 'c', 'o', 'm', ' ', ' '};\n\n/* Precondition: cardholder_data_obfuscated points to at least 8 safe-to-read bytes of memory.\n*/\nvoid gallagher_deobfuscate_and_parse_credential(\n    GallagherCredential* credential,\n    const uint8_t* cardholder_data_obfuscated) {\n    uint8_t cardholder_data_deobfuscated[8];\n    for(int i = 0; i < 8; i++) {\n        cardholder_data_deobfuscated[i] = GALLAGHER_DECODE_TABLE[cardholder_data_obfuscated[i]];\n    }\n\n    // Pull out values from the deobfuscated data\n    credential->region = (cardholder_data_deobfuscated[3] >> 1) & 0x0F;\n    credential->facility = ((uint16_t)(cardholder_data_deobfuscated[5] & 0x0F) << 12) +\n                           ((uint16_t)cardholder_data_deobfuscated[1] << 4) +\n                           (((uint16_t)cardholder_data_deobfuscated[7] >> 4) & 0x0F);\n    credential->card = ((uint32_t)cardholder_data_deobfuscated[0] << 16) +\n                       ((uint32_t)(cardholder_data_deobfuscated[4] & 0x1F) << 11) +\n                       ((uint32_t)cardholder_data_deobfuscated[2] << 3) +\n                       (((uint32_t)cardholder_data_deobfuscated[3] >> 5) & 0x07);\n    credential->issue = cardholder_data_deobfuscated[7] & 0x0F;\n}\n"
  },
  {
    "path": "applications/main/nfc/api/gallagher/gallagher_util.h",
    "content": "/* gallagher_util.h - Utilities for parsing Gallagher cards (New Zealand).\n * Author: Nick Mooney (nick@mooney.nz)\n * \n * Reference: https://github.com/megabug/gallagher-research\n*/\n\n#pragma once\n\n#include <lib/nfc/protocols/mf_classic/mf_classic.h>\n\n#define GALLAGHER_CREDENTIAL_SECTOR 15\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const uint8_t GALLAGHER_DECODE_TABLE[256];\nextern const uint8_t GALLAGHER_CARDAX_ASCII[MF_CLASSIC_BLOCK_SIZE];\n\ntypedef struct GallagherCredential {\n    uint8_t region;\n    uint8_t issue;\n    uint16_t facility;\n    uint32_t card;\n} GallagherCredential;\n\nvoid gallagher_deobfuscate_and_parse_credential(\n    GallagherCredential* credential,\n    const uint8_t* cardholder_data_obfuscated);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/api/mosgortrans/mosgortrans_util.c",
    "content": "#include \"mosgortrans_util.h\"\n\n#define TAG \"Mosgortrans\"\n\nvoid render_section_header(\n    FuriString* str,\n    const char* name,\n    uint8_t prefix_separator_cnt,\n    uint8_t suffix_separator_cnt) {\n    for(uint8_t i = 0; i < prefix_separator_cnt; i++) {\n        furi_string_cat_printf(str, \":\");\n    }\n    furi_string_cat_printf(str, \"[ %s ]\", name);\n    for(uint8_t i = 0; i < suffix_separator_cnt; i++) {\n        furi_string_cat_printf(str, \":\");\n    }\n}\n\nvoid from_days_to_datetime(uint32_t days, DateTime* datetime, uint16_t start_year) {\n    uint32_t timestamp = days * 24 * 60 * 60;\n    DateTime start_datetime = {0};\n    start_datetime.year = start_year - 1;\n    start_datetime.month = 12;\n    start_datetime.day = 31;\n    timestamp += datetime_datetime_to_timestamp(&start_datetime);\n    datetime_timestamp_to_datetime(timestamp, datetime);\n}\n\nvoid from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {\n    uint32_t timestamp = minutes * 60;\n    DateTime start_datetime = {0};\n    start_datetime.year = start_year - 1;\n    start_datetime.month = 12;\n    start_datetime.day = 31;\n    timestamp += datetime_datetime_to_timestamp(&start_datetime);\n    datetime_timestamp_to_datetime(timestamp, datetime);\n}\n\nvoid from_seconds_to_datetime(uint32_t seconds, DateTime* datetime, uint16_t start_year) {\n    uint32_t timestamp = seconds;\n    DateTime start_datetime = {0};\n    start_datetime.year = start_year - 1;\n    start_datetime.month = 12;\n    start_datetime.day = 31;\n    timestamp += datetime_datetime_to_timestamp(&start_datetime);\n    datetime_timestamp_to_datetime(timestamp, datetime);\n}\n\ntypedef struct {\n    uint16_t view; //101\n    uint16_t type; //102\n    uint8_t layout; //111\n    uint8_t layout2; //112\n    uint16_t blank_type; //121\n    uint16_t type_of_extended; //122\n    uint8_t extended; //123\n    uint8_t benefit_code; //124\n    uint32_t number; //201\n    uint16_t use_before_date; //202\n    uint16_t use_before_date2; //202.2\n    uint16_t use_with_date; //205\n    uint8_t requires_activation; //301\n    uint16_t activate_during; //302\n    uint16_t extension_counter; //304\n    uint8_t blocked; //303\n    uint32_t valid_from_date; //311\n    uint16_t valid_to_date; //312\n    uint8_t valid_for_days; //313\n    uint32_t valid_for_minutes; //314\n    uint16_t valid_for_time; //316\n    uint16_t valid_for_time2; //316.2\n    uint32_t valid_to_time; //317\n    uint16_t remaining_trips; //321\n    uint8_t remaining_trips1; //321.1\n    uint32_t remaining_funds; //322\n    uint16_t total_trips; //331\n    uint16_t start_trip_date; //402\n    uint16_t start_trip_time; //403\n    uint32_t start_trip_neg_minutes; //404\n    uint32_t start_trip_minutes; //405\n    uint8_t start_trip_seconds; //406\n    uint8_t minutes_pass; //412\n    uint8_t passage_5_minutes; //413\n    uint8_t metro_ride_with; //414\n    uint8_t transport_type; //421\n    uint8_t transport_type_flag; //421.0\n    uint8_t transport_type1; //421.1\n    uint8_t transport_type2; //421.2\n    uint8_t transport_type3; //421.3\n    uint8_t transport_type4; //421.4\n    uint16_t validator; //422\n    uint8_t validator1; //422.1\n    uint16_t validator2; //422.2\n    uint16_t route; //424\n    uint8_t passage_in_metro; //431\n    uint8_t transfer_in_metro; //432\n    uint8_t passages_ground_transport; //433\n    uint8_t fare_trip; //441\n    uint16_t crc16; //501.1\n    uint16_t crc16_2; //501.2\n    uint32_t hash; //502\n    uint16_t hash1; //502.1\n    uint32_t hash2; //502.2\n    uint8_t geozone_a; //GeoZoneA\n    uint8_t geozone_b; //GeoZoneB\n    uint8_t company; //Company\n    uint8_t units; //Units\n    uint64_t rfu1; //rfu1\n    uint16_t rfu2; //rfu2\n    uint32_t rfu3; //rfu3\n    uint8_t rfu4; //rfu4\n    uint8_t rfu5; //rfu5\n    uint8_t write_enabled; //write_enabled\n    uint32_t tech_code; //TechCode\n    uint8_t interval; //Interval\n    uint16_t app_code1; //AppCode1\n    uint16_t app_code2; //AppCode2\n    uint16_t app_code3; //AppCode3\n    uint16_t app_code4; //AppCode4\n    uint16_t type1; //Type1\n    uint16_t type2; //Type2\n    uint16_t type3; //Type3\n    uint16_t type4; //Type4\n    uint8_t zoo; //zoo\n} BlockData;\n\nvoid parse_layout_2(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202\n    data_block->benefit_code = bit_lib_get_bits(block->data, 0x48, 8); //124\n    data_block->rfu1 = bit_lib_get_bits_32(block->data, 0x50, 32); //rfu1\n    data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1\n    data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303\n    data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403\n    data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311\n    data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312\n    data_block->start_trip_seconds = bit_lib_get_bits(block->data, 0xDB, 6); //406\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0xC3, 2); //421.1\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0xC5, 2); //421.2\n    data_block->transport_type3 = bit_lib_get_bits(block->data, 0xC7, 2); //421.3\n    data_block->transport_type4 = bit_lib_get_bits(block->data, 0xC9, 2); //421.4\n    data_block->use_with_date = bit_lib_get_bits_16(block->data, 0xBD, 16); //205\n    data_block->route = bit_lib_get_bits(block->data, 0xCD, 1); //424\n    data_block->validator1 = bit_lib_get_bits_16(block->data, 0xCE, 15); //422.1\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xCD, 16);\n    data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDD, 16); //331\n    data_block->write_enabled = bit_lib_get_bits(block->data, 0xED, 1); //write_enabled\n    data_block->rfu2 = bit_lib_get_bits(block->data, 0xEE, 2); //rfu2\n    data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2\n}\n\nvoid parse_layout_6(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202\n    data_block->geozone_a = bit_lib_get_bits(block->data, 0x48, 4); //GeoZoneA\n    data_block->geozone_b = bit_lib_get_bits(block->data, 0x4C, 4); //GeoZoneB\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x50, 10); //121\n    data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x5A, 10); //122\n    data_block->rfu1 = bit_lib_get_bits_16(block->data, 0x64, 12); //rfu1\n    data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1\n    data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303\n    data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403\n    data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311\n    data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312\n    data_block->company = bit_lib_get_bits(block->data, 0xBD, 4); //Company\n    data_block->validator1 = bit_lib_get_bits(block->data, 0xC1, 4); //422.1\n    data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xC5, 10); //321\n    data_block->units = bit_lib_get_bits(block->data, 0xCF, 6); //Units\n    data_block->validator2 = bit_lib_get_bits_16(block->data, 0xD5, 10); //422.2\n    data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDF, 16); //331\n    data_block->extended = bit_lib_get_bits(block->data, 0xEF, 1); //123\n    data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2\n}\n\nvoid parse_layout_8(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202\n    data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311\n    data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301\n    data_block->rfu2 = bit_lib_get_bits(block->data, 0x99, 7); //rfu2\n    data_block->remaining_trips1 = bit_lib_get_bits(block->data, 0xA0, 8); //321.1\n    data_block->remaining_trips = bit_lib_get_bits(block->data, 0xA8, 8); //321\n    data_block->validator1 = bit_lib_get_bits(block->data, 0xB0, 2); //422.1\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 15); //422\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502\n    data_block->rfu3 = bit_lib_get_bits_32(block->data, 0xE0, 32); //rfu3\n}\n\nvoid parse_layout_A(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x40, 12); //311\n    data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x4C, 19); //314\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0x5F, 1); //301\n    data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x60, 19); //405\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0x77, 7); //412\n    data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x7E, 2); //421.0\n    data_block->remaining_trips = bit_lib_get_bits(block->data, 0x80, 8); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0x88, 16); //422\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0x98, 2); //421.1\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0x9A, 2); //421.2\n    data_block->transport_type3 = bit_lib_get_bits(block->data, 0x9C, 2); //421.3\n    data_block->transport_type4 = bit_lib_get_bits(block->data, 0x9E, 2); //421.4\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502\n}\n\nvoid parse_layout_C(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202\n    data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311\n    data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301\n    data_block->rfu2 = bit_lib_get_bits_16(block->data, 0x99, 13); //rfu2\n    data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502\n    data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402\n    data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403\n    data_block->transport_type = bit_lib_get_bits(block->data, 0xFB, 2); //421\n    data_block->rfu3 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu3\n    data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432\n}\n\nvoid parse_layout_D(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->rfu1 = bit_lib_get_bits(block->data, 0x38, 8); //rfu1\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x40, 16); //202\n    data_block->valid_for_time = bit_lib_get_bits_16(block->data, 0x50, 11); //316\n    data_block->rfu2 = bit_lib_get_bits(block->data, 0x5B, 5); //rfu2\n    data_block->use_before_date2 = bit_lib_get_bits_16(block->data, 0x60, 16); //202.2\n    data_block->valid_for_time2 = bit_lib_get_bits_16(block->data, 0x70, 11); //316.2\n    data_block->rfu3 = bit_lib_get_bits(block->data, 0x7B, 5); //rfu3\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311\n    data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301\n    data_block->rfu4 = bit_lib_get_bits(block->data, 0x99, 2); //rfu4\n    data_block->passage_5_minutes = bit_lib_get_bits(block->data, 0x9B, 5); //413\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA0, 2); //421.1\n    data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA2, 1); //431\n    data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xA3, 3); //433\n    data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502\n    data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402\n    data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0xFB, 2); //421.2\n    data_block->rfu5 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu5\n    data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432\n}\n\nvoid parse_layout_E1(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 16); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121\n    data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422\n    data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x90, 16); //402\n    data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xA0, 11); //403\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0xAB, 2); //421.1\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0xAD, 2); //421.2\n    data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xB1, 1); //432\n    data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xB2, 1); //431\n    data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xB3, 3); //433\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0xB9, 8); //412\n    data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xC4, 19); //322\n    data_block->fare_trip = bit_lib_get_bits(block->data, 0xD7, 2); //441\n    data_block->blocked = bit_lib_get_bits(block->data, 0x9D, 1); //303\n    data_block->zoo = bit_lib_get_bits(block->data, 0xDA, 1); //zoo\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502\n}\n\nvoid parse_layout_E2(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 16); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x57, 10); //121\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x61, 16); //311\n    data_block->activate_during = bit_lib_get_bits_16(block->data, 0x71, 9); //302\n    data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x83, 20); //314\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9A, 8); //412\n    data_block->transport_type = bit_lib_get_bits(block->data, 0xA3, 2); //421\n    data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA5, 1); //431\n    data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xA6, 1); //432\n    data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA7, 10); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 16); //422\n    data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC4, 20); //404\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0xD8, 1); //301\n    data_block->blocked = bit_lib_get_bits(block->data, 0xD9, 1); //303\n    data_block->extended = bit_lib_get_bits(block->data, 0xDA, 1); //123\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502\n}\n\nvoid parse_layout_E3(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121\n    data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xBC, 22); //322\n    data_block->hash = bit_lib_get_bits_32(block->data, 224, 32); //502\n    data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422\n    data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x90, 23); //405\n    data_block->fare_trip = bit_lib_get_bits(block->data, 0xD2, 2); //441\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAB, 7); //412\n    data_block->transport_type_flag = bit_lib_get_bits(block->data, 0xB2, 2); //421.0\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0xB4, 2); //421.1\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0xB6, 2); //421.2\n    data_block->transport_type3 = bit_lib_get_bits(block->data, 0xB8, 2); //421.3\n    data_block->transport_type4 = bit_lib_get_bits(block->data, 0xBA, 2); //421.4\n    data_block->blocked = bit_lib_get_bits(block->data, 0xD4, 1); //303\n}\n\nvoid parse_layout_E4(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x5E, 13); //311\n    data_block->activate_during = bit_lib_get_bits_16(block->data, 0x6B, 9); //302\n    data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x74, 10); //304\n    data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0x98, 7); //412\n    data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x9F, 2); //421.0\n    data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA1, 2); //421.1\n    data_block->transport_type2 = bit_lib_get_bits(block->data, 0xA3, 2); //421.2\n    data_block->transport_type3 = bit_lib_get_bits(block->data, 0xA5, 2); //421.3\n    data_block->transport_type4 = bit_lib_get_bits(block->data, 0xA7, 2); //421.4\n    data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA9, 10); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xB3, 16); //422\n    data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC3, 20); //404\n    data_block->requires_activation = bit_lib_get_bits(block->data, 0xD7, 1); //301\n    data_block->blocked = bit_lib_get_bits(block->data, 0xD8, 1); //303\n    data_block->extended = bit_lib_get_bits(block->data, 0xD9, 1); //123\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502\n}\n\nvoid parse_layout_E5(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 13); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4A, 10); //121\n    data_block->valid_to_time = bit_lib_get_bits_32(block->data, 0x54, 23); //317\n    data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x6B, 10); //304\n    data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x80, 23); //405\n    data_block->metro_ride_with = bit_lib_get_bits(block->data, 0x97, 7); //414\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9E, 7); //412\n    data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xA7, 19); //322\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xBA, 16); //422\n    data_block->blocked = bit_lib_get_bits(block->data, 0xCA, 1); //303\n    data_block->route = bit_lib_get_bits_16(block->data, 0xCC, 12); //424\n    data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xD8, 7); //433\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502\n}\n\nvoid parse_layout_E6(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112\n    data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122\n    data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202\n    data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121\n    data_block->valid_from_date = bit_lib_get_bits_32(block->data, 0x5E, 23); //311\n    data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x75, 10); //304\n    data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314\n    data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0x94, 20); //404\n    data_block->metro_ride_with = bit_lib_get_bits(block->data, 0xA8, 7); //414\n    data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAF, 7); //412\n    data_block->remaining_trips = bit_lib_get_bits(block->data, 0xB6, 7); //321\n    data_block->validator = bit_lib_get_bits_16(block->data, 0xBD, 16); //422\n    data_block->blocked = bit_lib_get_bits(block->data, 0xCD, 1); //303\n    data_block->extended = bit_lib_get_bits(block->data, 0xCE, 1); //123\n    data_block->route = bit_lib_get_bits_16(block->data, 0xD4, 12); //424\n    data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502\n}\n\nvoid parse_layout_FCB(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311\n    data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312\n    data_block->interval = bit_lib_get_bits(block->data, 0x62, 4); //interval\n    data_block->app_code1 = bit_lib_get_bits_16(block->data, 0x66, 10); //app_code1\n    data_block->hash1 = bit_lib_get_bits_16(block->data, 0x70, 16); //502.1\n    data_block->type1 = bit_lib_get_bits_16(block->data, 0x80, 10); //type1\n    data_block->app_code2 = bit_lib_get_bits_16(block->data, 0x8A, 10); //app_code2\n    data_block->type2 = bit_lib_get_bits_16(block->data, 0x94, 10); //type2\n    data_block->app_code3 = bit_lib_get_bits_16(block->data, 0x9E, 10); //app_code3\n    data_block->type3 = bit_lib_get_bits_16(block->data, 0xA8, 10); //type3\n    data_block->app_code4 = bit_lib_get_bits_16(block->data, 0xB2, 10); //app_code4\n    data_block->type4 = bit_lib_get_bits_16(block->data, 0xBC, 10); //type4\n    data_block->hash2 = bit_lib_get_bits_32(block->data, 0xE0, 32); //502.2\n}\n\nvoid parse_layout_F0B(BlockData* data_block, const MfClassicBlock* block) {\n    data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101\n    data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102\n    data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201\n    data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111\n    data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code\n    data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311\n    data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312\n    data_block->hash1 = bit_lib_get_bits_32(block->data, 0x70, 16); //502.1\n}\n\nvoid parse_transport_type(BlockData* data_block, FuriString* transport) {\n    switch(data_block->transport_type_flag) {\n    case 1:\n        uint8_t transport_type =\n            (data_block->transport_type1 || data_block->transport_type2 ||\n             data_block->transport_type3 || data_block->transport_type4);\n        switch(transport_type) {\n        case 1:\n            furi_string_cat(transport, \"Metro\");\n            break;\n        case 2:\n            furi_string_cat(transport, \"Monorail\");\n            break;\n        case 3:\n            furi_string_cat(transport, \"MCC\");\n            break;\n        default:\n            furi_string_cat(transport, \"Unknown\");\n            break;\n        }\n        break;\n    case 2:\n        furi_string_cat(transport, \"Ground\");\n        break;\n    default:\n        furi_string_cat(transport, \"\");\n        break;\n    }\n}\n\nbool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result) {\n    BlockData data_block = {};\n    const uint16_t valid_departments[] = {0x106, 0x108, 0x10A, 0x10E, 0x110, 0x117};\n    uint16_t transport_department = bit_lib_get_bits_16(block->data, 0, 10);\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        furi_string_cat_printf(result, \"Transport department: %x\\n\", transport_department);\n    }\n    bool department_valid = false;\n    for(uint8_t i = 0; i < 6; i++) {\n        if(transport_department == valid_departments[i]) {\n            department_valid = true;\n            break;\n        }\n    }\n    if(!department_valid) {\n        return false;\n    }\n    FURI_LOG_D(TAG, \"Transport department: %x\", transport_department);\n    uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4);\n    if(layout_type == 0xE) {\n        layout_type = bit_lib_get_bits_16(block->data, 52, 9);\n    } else if(layout_type == 0xF) {\n        layout_type = bit_lib_get_bits_16(block->data, 52, 14);\n    }\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        furi_string_cat_printf(result, \"Layout: %x\\n\", layout_type);\n    }\n    FURI_LOG_D(TAG, \"Layout type %x\", layout_type);\n    switch(layout_type) {\n    case 0x02: {\n        parse_layout_2(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n\n        if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) {\n            furi_string_cat(result, \"\\e#No ticket\");\n            return false;\n        }\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips: %d\\n\", data_block.total_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\\n\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        //trip_number\n        furi_string_cat_printf(result, \"Trips: %d\\n\", data_block.total_trips);\n        //trip_from\n        DateTime card_start_trip_minutes_s = {0};\n        from_seconds_to_datetime(\n            data_block.start_trip_date * 24 * 60 * 60 + data_block.start_trip_time * 60 +\n                data_block.start_trip_seconds,\n            &card_start_trip_minutes_s,\n            1992);\n        furi_string_cat_printf(\n            result,\n            \"Trip from: %02d.%02d.%04d %02d:%02d\",\n            card_start_trip_minutes_s.day,\n            card_start_trip_minutes_s.month,\n            card_start_trip_minutes_s.year,\n            card_start_trip_minutes_s.hour,\n            card_start_trip_minutes_s.minute);\n        break;\n    }\n    case 0x06: {\n        parse_layout_6(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\\n\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        //trip_number\n        furi_string_cat_printf(result, \"Trips: %d\\n\", data_block.total_trips);\n        //trip_from\n        DateTime card_start_trip_minutes_s = {0};\n        from_minutes_to_datetime(\n            (data_block.start_trip_date) * 24 * 60 + data_block.start_trip_time,\n            &card_start_trip_minutes_s,\n            1992);\n        furi_string_cat_printf(\n            result,\n            \"Trip from: %02d.%02d.%04d %02d:%02d\\n\",\n            card_start_trip_minutes_s.day,\n            card_start_trip_minutes_s.month,\n            card_start_trip_minutes_s.year,\n            card_start_trip_minutes_s.hour,\n            card_start_trip_minutes_s.minute);\n        //validator\n        furi_string_cat_printf(\n            result, \"Validator: %05d\", data_block.validator1 * 1024 + data_block.validator2);\n        break;\n    }\n    case 0x08: {\n        parse_layout_8(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_days_to_datetime(\n            data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        break;\n    }\n    case 0x0A: {\n        parse_layout_A(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 2016);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_minutes_to_datetime(\n            data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,\n            &card_valid_to_date_s,\n            2016);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        //trip_from\n        if(data_block.start_trip_minutes) {\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes,\n                &card_start_trip_minutes_s,\n                2016);\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip from: %02d.%02d.%04d %02d:%02d\",\n                card_start_trip_minutes_s.day,\n                card_start_trip_minutes_s.month,\n                card_start_trip_minutes_s.year,\n                card_start_trip_minutes_s.hour,\n                card_start_trip_minutes_s.minute);\n        }\n        //trip_switch\n        if(data_block.minutes_pass) {\n            DateTime card_start_switch_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes +\n                    data_block.minutes_pass,\n                &card_start_switch_trip_minutes_s,\n                2016);\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip switch: %02d.%02d.%04d %02d:%02d\",\n                card_start_switch_trip_minutes_s.day,\n                card_start_switch_trip_minutes_s.month,\n                card_start_switch_trip_minutes_s.year,\n                card_start_switch_trip_minutes_s.hour,\n                card_start_switch_trip_minutes_s.minute);\n        }\n        //transport\n        FuriString* transport = furi_string_alloc();\n        parse_transport_type(&data_block, transport);\n        furi_string_cat_printf(result, \"\\nTransport: %s\", furi_string_get_cstr(transport));\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        furi_string_free(transport);\n        break;\n    }\n    case 0x0C: {\n        parse_layout_C(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_days_to_datetime(\n            data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\\n\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\", data_block.remaining_trips);\n        //trip_from\n        if(data_block.start_trip_date) { // TODO: (-nofl) unused\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,\n                &card_start_trip_minutes_s,\n                1992);\n        }\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        break;\n    }\n    case 0x0D: {\n        parse_layout_D(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        DateTime card_valid_to_date_s = {0};\n        from_days_to_datetime(\n            data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_valid_to_date_s.day,\n            card_valid_to_date_s.month,\n            card_valid_to_date_s.year);\n        //trip_from\n        if(data_block.start_trip_date) { // TODO: (-nofl) unused\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,\n                &card_start_trip_minutes_s,\n                1992);\n        }\n        //trip_switch\n        if(data_block.passage_5_minutes) { // TODO: (-nofl) unused\n            DateTime card_start_switch_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_date * 24 * 60 + data_block.start_trip_time +\n                    data_block.passage_5_minutes,\n                &card_start_switch_trip_minutes_s,\n                1992);\n        }\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        break;\n    }\n    case 0xE1:\n    case 0x1C1: {\n        parse_layout_E1(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_funds\n        furi_string_cat_printf(result, \"Balance: %ld rub\\n\", data_block.remaining_funds / 100);\n        //trip_from\n        if(data_block.start_trip_date) {\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,\n                &card_start_trip_minutes_s,\n                1992);\n            furi_string_cat_printf(\n                result,\n                \"Trip from: %02d.%02d.%04d %02d:%02d\\n\",\n                card_start_trip_minutes_s.day,\n                card_start_trip_minutes_s.month,\n                card_start_trip_minutes_s.year,\n                card_start_trip_minutes_s.hour,\n                card_start_trip_minutes_s.minute);\n        }\n        //transport\n        FuriString* transport = furi_string_alloc();\n        switch(data_block.transport_type1) {\n        case 1:\n            switch(data_block.transport_type2) {\n            case 1:\n                furi_string_cat(transport, \"Metro\");\n                break;\n            case 2:\n                furi_string_cat(transport, \"Monorail\");\n                break;\n            default:\n                furi_string_cat(transport, \"Unknown\");\n                break;\n            }\n            break;\n        case 2:\n            furi_string_cat(transport, \"Ground\");\n            break;\n        case 3:\n            furi_string_cat(transport, \"MCC\");\n            break;\n        default:\n            furi_string_cat(transport, \"\");\n            break;\n        }\n        furi_string_cat_printf(result, \"Transport: %s\", furi_string_get_cstr(transport));\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        furi_string_free(transport);\n        break;\n    }\n    case 0xE2:\n    case 0x1C2: {\n        parse_layout_E2(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_valid_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\",\n            card_valid_from_date_s.day,\n            card_valid_from_date_s.month,\n            card_valid_from_date_s.year);\n        //valid_to_date\n        if(data_block.activate_during) {\n            DateTime card_valid_to_date_s = {0};\n            from_days_to_datetime(\n                data_block.valid_from_date + data_block.activate_during,\n                &card_valid_to_date_s,\n                1992);\n            furi_string_cat_printf(\n                result,\n                \"\\nValid to: %02d.%02d.%04d\",\n                card_valid_to_date_s.day,\n                card_valid_to_date_s.month,\n                card_valid_to_date_s.year);\n        } else {\n            DateTime card_valid_to_date_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,\n                &card_valid_to_date_s,\n                1992);\n            furi_string_cat_printf(\n                result,\n                \"\\nValid to: %02d.%02d.%04d\",\n                card_valid_to_date_s.day,\n                card_valid_to_date_s.month,\n                card_valid_to_date_s.year);\n        }\n        //trip_from\n        if(data_block.start_trip_neg_minutes) {\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_to_date * 24 * 60 + data_block.valid_for_minutes -\n                    data_block.start_trip_neg_minutes,\n                &card_start_trip_minutes_s,\n                1992); //-time\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip from: %02d.%02d.%04d %02d:%02d\",\n                card_start_trip_minutes_s.day,\n                card_start_trip_minutes_s.month,\n                card_start_trip_minutes_s.year,\n                card_start_trip_minutes_s.hour,\n                card_start_trip_minutes_s.minute);\n        }\n        //trip_switch\n        if(data_block.minutes_pass) {\n            DateTime card_start_switch_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -\n                    data_block.start_trip_neg_minutes + data_block.minutes_pass,\n                &card_start_switch_trip_minutes_s,\n                1992);\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip switch: %02d.%02d.%04d %02d:%02d\",\n                card_start_switch_trip_minutes_s.day,\n                card_start_switch_trip_minutes_s.month,\n                card_start_switch_trip_minutes_s.year,\n                card_start_switch_trip_minutes_s.hour,\n                card_start_switch_trip_minutes_s.minute);\n        }\n        //transport\n        FuriString* transport = furi_string_alloc();\n        switch(data_block.transport_type) { // TODO: (-nofl) unused\n        case 1:\n            furi_string_cat(transport, \"Metro\");\n            break;\n        case 2:\n            furi_string_cat(transport, \"Monorail\");\n            break;\n        case 3:\n            furi_string_cat(transport, \"Ground\");\n            break;\n        default:\n            furi_string_cat(transport, \"Unknown\");\n            break;\n        }\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        furi_string_free(transport);\n        break;\n    }\n    case 0xE3:\n    case 0x1C3: {\n        parse_layout_E3(&data_block, block);\n        // number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        // use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        // remaining_funds\n        furi_string_cat_printf(result, \"Balance: %lu rub\\n\", data_block.remaining_funds);\n        // start_trip_minutes\n        DateTime card_start_trip_minutes_s = {0};\n        from_minutes_to_datetime(data_block.start_trip_minutes, &card_start_trip_minutes_s, 2016);\n        furi_string_cat_printf(\n            result,\n            \"Trip from: %02d.%02d.%04d %02d:%02d\\n\",\n            card_start_trip_minutes_s.day,\n            card_start_trip_minutes_s.month,\n            card_start_trip_minutes_s.year,\n            card_start_trip_minutes_s.hour,\n            card_start_trip_minutes_s.minute);\n        // transport\n        FuriString* transport = furi_string_alloc();\n        parse_transport_type(&data_block, transport);\n        furi_string_cat_printf(result, \"Transport: %s\\n\", furi_string_get_cstr(transport));\n        // validator\n        furi_string_cat_printf(result, \"Validator: %05d\\n\", data_block.validator);\n        // fare\n        FuriString* fare = furi_string_alloc();\n        switch(data_block.fare_trip) {\n        case 0:\n            furi_string_cat(fare, \"\");\n            break;\n        case 1:\n            furi_string_cat(fare, \"Single\");\n            break;\n        case 2:\n            furi_string_cat(fare, \"90 minutes\");\n            break;\n        default:\n            furi_string_cat(fare, \"Unknown\");\n            break;\n        }\n        furi_string_cat_printf(result, \"Fare: %s\", furi_string_get_cstr(fare));\n        furi_string_free(fare);\n        furi_string_free(transport);\n        break;\n    }\n    case 0xE4:\n    case 0x1C4: {\n        parse_layout_E4(&data_block, block);\n\n        // number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        // use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        // remaining_funds\n        furi_string_cat_printf(result, \"Balance: %lu rub\\n\", data_block.remaining_funds);\n        // valid_from_date\n        DateTime card_use_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2016);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_use_from_date_s.day,\n            card_use_from_date_s.month,\n            card_use_from_date_s.year);\n        // valid_to_date\n        DateTime card_use_to_date_s = {0};\n        if(data_block.requires_activation) {\n            from_days_to_datetime(\n                data_block.valid_from_date + data_block.activate_during,\n                &card_use_to_date_s,\n                2016);\n        } else {\n            from_minutes_to_datetime(\n                data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,\n                &card_use_to_date_s,\n                2016);\n        }\n\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\\n\",\n            card_use_to_date_s.day,\n            card_use_to_date_s.month,\n            card_use_to_date_s.year);\n        // trip_number\n        // furi_string_cat_printf(result, \"Trips left: %d\", data_block.remaining_trips);\n        // trip_from\n        DateTime card_start_trip_minutes_s = {0};\n        from_minutes_to_datetime(\n            data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -\n                data_block.start_trip_neg_minutes,\n            &card_start_trip_minutes_s,\n            2016); // TODO: (-nofl) unused\n        //transport\n        FuriString* transport = furi_string_alloc();\n        parse_transport_type(&data_block, transport);\n        furi_string_cat_printf(result, \"Transport: %s\", furi_string_get_cstr(transport));\n        // validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        furi_string_free(transport);\n        break;\n    }\n    case 0xE5:\n    case 0x1C5: {\n        parse_layout_E5(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_funds\n        furi_string_cat_printf(result, \"Balance: %ld rub\", data_block.remaining_funds / 100);\n        //start_trip_minutes\n        if(data_block.start_trip_minutes) {\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_minutes, &card_start_trip_minutes_s, 2019);\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip from: %02d.%02d.%04d %02d:%02d\",\n                card_start_trip_minutes_s.day,\n                card_start_trip_minutes_s.month,\n                card_start_trip_minutes_s.year,\n                card_start_trip_minutes_s.hour,\n                card_start_trip_minutes_s.minute);\n        }\n        //start_m_trip_minutes\n        if(data_block.metro_ride_with) {\n            DateTime card_start_m_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_minutes + data_block.metro_ride_with,\n                &card_start_m_trip_minutes_s,\n                2019);\n            furi_string_cat_printf(\n                result,\n                \"\\n(M) from: %02d.%02d.%04d %02d:%02d\",\n                card_start_m_trip_minutes_s.day,\n                card_start_m_trip_minutes_s.month,\n                card_start_m_trip_minutes_s.year,\n                card_start_m_trip_minutes_s.hour,\n                card_start_m_trip_minutes_s.minute);\n        }\n        if(data_block.minutes_pass) {\n            DateTime card_start_change_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.start_trip_minutes + data_block.minutes_pass,\n                &card_start_change_trip_minutes_s,\n                2019);\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip edit: %02d.%02d.%04d %02d:%02d\",\n                card_start_change_trip_minutes_s.day,\n                card_start_change_trip_minutes_s.month,\n                card_start_change_trip_minutes_s.year,\n                card_start_change_trip_minutes_s.hour,\n                card_start_change_trip_minutes_s.minute);\n        }\n        //transport\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        break;\n    }\n    case 0xE6:\n    case 0x1C6: {\n        parse_layout_E6(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //use_before_date\n        DateTime card_use_before_date_s = {0};\n        from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);\n        furi_string_cat_printf(\n            result,\n            \"Use before: %02d.%02d.%04d\\n\",\n            card_use_before_date_s.day,\n            card_use_before_date_s.month,\n            card_use_before_date_s.year);\n        //remaining_trips\n        furi_string_cat_printf(result, \"Trips left: %d\\n\", data_block.remaining_trips);\n        //valid_from_date\n        DateTime card_use_from_date_s = {0};\n        from_minutes_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2019);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_use_from_date_s.day,\n            card_use_from_date_s.month,\n            card_use_from_date_s.year);\n        //valid_to_date\n        DateTime card_use_to_date_s = {0};\n        from_minutes_to_datetime(\n            data_block.valid_from_date + data_block.valid_for_minutes - 1,\n            &card_use_to_date_s,\n            2019);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_use_to_date_s.day,\n            card_use_to_date_s.month,\n            card_use_to_date_s.year);\n        //start_trip_minutes\n        if(data_block.start_trip_neg_minutes) {\n            DateTime card_start_trip_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date + data_block.valid_for_minutes -\n                    data_block.start_trip_neg_minutes,\n                &card_start_trip_minutes_s,\n                2019); //-time\n            furi_string_cat_printf(\n                result,\n                \"\\nTrip from: %02d.%02d.%04d %02d:%02d\",\n                card_start_trip_minutes_s.day,\n                card_start_trip_minutes_s.month,\n                card_start_trip_minutes_s.year,\n                card_start_trip_minutes_s.hour,\n                card_start_trip_minutes_s.minute);\n        }\n        //start_trip_m_minutes\n        if(data_block.metro_ride_with) {\n            DateTime card_start_trip_m_minutes_s = {0};\n            from_minutes_to_datetime(\n                data_block.valid_from_date + data_block.valid_for_minutes -\n                    data_block.start_trip_neg_minutes + data_block.metro_ride_with,\n                &card_start_trip_m_minutes_s,\n                2019);\n            furi_string_cat_printf(\n                result,\n                \"\\n(M) from: %02d.%02d.%04d %02d:%02d\",\n                card_start_trip_m_minutes_s.day,\n                card_start_trip_m_minutes_s.month,\n                card_start_trip_m_minutes_s.year,\n                card_start_trip_m_minutes_s.hour,\n                card_start_trip_m_minutes_s.minute);\n        }\n        //transport\n        //validator\n        if(data_block.validator) {\n            furi_string_cat_printf(result, \"\\nValidator: %05d\", data_block.validator);\n        }\n        break;\n    }\n    case 0x3CCB: {\n        parse_layout_FCB(&data_block, block);\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //valid_from_date\n        DateTime card_use_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_use_from_date_s.day,\n            card_use_from_date_s.month,\n            card_use_from_date_s.year);\n        //valid_to_date\n        DateTime card_use_to_date_s = {0};\n        from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_use_to_date_s.day,\n            card_use_to_date_s.month,\n            card_use_to_date_s.year);\n        break;\n    }\n    case 0x3C0B: {\n        //number\n        furi_string_cat_printf(result, \"Number: %010lu\\n\", data_block.number);\n        //valid_from_date\n        DateTime card_use_from_date_s = {0};\n        from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid from: %02d.%02d.%04d\\n\",\n            card_use_from_date_s.day,\n            card_use_from_date_s.month,\n            card_use_from_date_s.year);\n        //valid_to_date\n        DateTime card_use_to_date_s = {0};\n        from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);\n        furi_string_cat_printf(\n            result,\n            \"Valid to: %02d.%02d.%04d\",\n            card_use_to_date_s.day,\n            card_use_to_date_s.month,\n            card_use_to_date_s.year);\n        break;\n    }\n    default:\n        result = NULL;\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "applications/main/nfc/api/mosgortrans/mosgortrans_util.h",
    "content": "#pragma once\n\n#include <bit_lib.h>\n#include <datetime.h>\n#include <furi/core/string.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <furi_hal_rtc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid render_section_header(\n    FuriString* str,\n    const char* name,\n    uint8_t prefix_separator_cnt,\n    uint8_t suffix_separator_cnt);\nbool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/api/nfc_app_api_interface.h",
    "content": "#pragma once\n\n#include <flipper_application/api_hashtable/api_hashtable.h>\n\n/* \n * Resolver interface with private application's symbols. \n * Implementation is contained in app_api_table.c\n */\nextern const ElfApiInterface* const nfc_application_api_interface;\n"
  },
  {
    "path": "applications/main/nfc/api/nfc_app_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n/* \n * This file contains an implementation of a symbol table \n * with private app's symbols. It is used by composite API resolver\n * to load plugins that use internal application's APIs.\n */\n#include \"nfc_app_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(nfc_app_api_table), \"Detected API method hash collision!\");\n\nconstexpr HashtableApiInterface nfc_application_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        /* generic resolver using pre-sorted array */\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    /* pointers to application's API table boundaries */\n    nfc_app_api_table.cbegin(),\n    nfc_app_api_table.cend(),\n};\n\n/* Casting to generic resolver to use in Composite API resolver */\nextern \"C\" const ElfApiInterface* const nfc_application_api_interface =\n    &nfc_application_hashtable_api_interface;\n"
  },
  {
    "path": "applications/main/nfc/api/nfc_app_api_table_i.h",
    "content": "#include \"gallagher/gallagher_util.h\"\n#include \"mosgortrans/mosgortrans_util.h\"\n\n/* \n * A list of app's private functions and objects to expose for plugins.\n * It is used to generate a table of symbols for import resolver to use.\n * TBD: automatically generate this table from app's header files\n */\nstatic constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(\n    API_METHOD(\n        gallagher_deobfuscate_and_parse_credential,\n        void,\n        (GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)),\n    API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*),\n    API_METHOD(\n        mosgortrans_parse_transport_block,\n        bool,\n        (const MfClassicBlock* block, FuriString* result)),\n    API_METHOD(\n        render_section_header,\n        void,\n        (FuriString * str,\n         const char* name,\n         uint8_t prefix_separator_cnt,\n         uint8_t suffix_separator_cnt))));\n"
  },
  {
    "path": "applications/main/nfc/application.fam",
    "content": "App(\n    appid=\"nfc\",\n    name=\"NFC\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    targets=[\"f7\"],\n    entry_point=\"nfc_app\",\n    icon=\"A_NFC_14\",\n    stack_size=5 * 1024,\n    order=30,\n    resources=\"resources\",\n    sources=[\"*.c*\", \"!plugins\", \"!nfc_cli.c\", \"!cli\"],\n    fap_libs=[\"assets\", \"mbedtls\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"NFC\",\n)\n\n# Parser plugins\n\nApp(\n    appid=\"all_in_one_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"all_in_one_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/all_in_one.c\"],\n)\n\nApp(\n    appid=\"microel_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"microel_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/microel.c\"],\n)\n\nApp(\n    appid=\"mizip_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"mizip_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/mizip.c\"],\n)\n\nApp(\n    appid=\"hi_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"hi_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/hi.c\"],\n)\n\nApp(\n    appid=\"opal_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"opal_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/opal.c\"],\n)\n\nApp(\n    appid=\"mykey_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"mykey_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/mykey.c\"],\n)\n\nApp(\n    appid=\"myki_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"myki_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/myki.c\"],\n)\n\nApp(\n    appid=\"troika_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"troika_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/troika.c\"],\n)\n\nApp(\n    appid=\"social_moscow_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"social_moscow_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/social_moscow.c\"],\n)\n\nApp(\n    appid=\"washcity_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"washcity_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/washcity.c\"],\n)\n\nApp(\n    appid=\"plantain_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"plantain_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/plantain.c\"],\n)\n\nApp(\n    appid=\"two_cities_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"two_cities_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/two_cities.c\"],\n)\n\nApp(\n    appid=\"aime_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"aime_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/aime.c\"],\n)\n\nApp(\n    appid=\"bip_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"bip_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/bip.c\"],\n)\n\nApp(\n    appid=\"umarsh_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"umarsh_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/umarsh.c\"],\n)\n\nApp(\n    appid=\"gallagher_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"gallagher_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/gallagher.c\"],\n)\n\nApp(\n    appid=\"clipper_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"clipper_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/clipper.c\"],\n)\n\nApp(\n    appid=\"hid_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"hid_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/hid.c\"],\n)\n\nApp(\n    appid=\"itso_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"itso_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/itso.c\"],\n)\n\nApp(\n    appid=\"skylanders_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"skylanders_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/skylanders.c\"],\n)\n\nApp(\n    appid=\"hworld_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"hworld_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/hworld.c\"],\n)\n\nApp(\n    appid=\"trt_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"trt_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/trt.c\"],\n)\n\nApp(\n    appid=\"ndef_ul_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    cdefines=[(\"NDEF_PROTO\", \"NDEF_PROTO_UL\")],\n    entry_point=\"ndef_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/ndef.c\"],\n)\nApp(\n    appid=\"ndef_mfc_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    cdefines=[(\"NDEF_PROTO\", \"NDEF_PROTO_MFC\")],\n    entry_point=\"ndef_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/ndef.c\"],\n)\n\nApp(\n    appid=\"ndef_slix_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    cdefines=[(\"NDEF_PROTO\", \"NDEF_PROTO_SLIX\")],\n    entry_point=\"ndef_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/ndef.c\"],\n)\n\nApp(\n    appid=\"disney_infinity_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"disney_infinity_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    fap_libs=[\"mbedtls\"],\n    sources=[\"plugins/supported_cards/disney_infinity.c\"],\n)\n\nApp(\n    appid=\"cli_nfc\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_nfc_ep\",\n    requires=[\"cli\"],\n    sources=[\n        \"helpers/mf_classic_key_cache.c\",\n        \"helpers/protocol_support/iso14443_3a/iso14443_3a_render.c\",\n        \"helpers/protocol_support/mf_ultralight/mf_ultralight_render.c\",\n        \"cli/nfc_cli.c\",\n        \"cli/nfc_cli_commands.c\",\n        \"cli/nfc_cli_command_processor.c\",\n        \"cli/commands/helpers/nfc_cli_format.c\",\n        \"cli/commands/helpers/nfc_cli_scanner.c\",\n        \"cli/commands/helpers/nfc_cli_protocol_parser.c\",\n        \"cli/commands/raw/nfc_cli_command_raw.c\",\n        \"cli/commands/raw/protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.c\",\n        \"cli/commands/raw/protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.c\",\n        \"cli/commands/raw/protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.c\",\n        \"cli/commands/raw/protocol_handlers/felica/nfc_cli_raw_felica.c\",\n        \"cli/commands/apdu/nfc_cli_command_apdu.c\",\n        \"cli/commands/apdu/protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.c\",\n        \"cli/commands/apdu/protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.c\",\n        \"cli/commands/apdu/protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.c\",\n        \"cli/commands/dump/nfc_cli_command_dump.c\",\n        \"cli/commands/dump/protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.c\",\n        \"cli/commands/dump/protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.c\",\n        \"cli/commands/dump/protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.c\",\n        \"cli/commands/dump/protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.c\",\n        \"cli/commands/dump/protocols/iso15693_3/nfc_cli_dump_iso15693_3.c\",\n        \"cli/commands/dump/protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.c\",\n        \"cli/commands/dump/protocols/mf_classic/nfc_cli_dump_mf_classic.c\",\n        \"cli/commands/dump/protocols/mf_plus/nfc_cli_dump_mf_plus.c\",\n        \"cli/commands/dump/protocols/mf_desfire/nfc_cli_dump_mf_desfire.c\",\n        \"cli/commands/dump/protocols/slix/nfc_cli_dump_slix.c\",\n        \"cli/commands/dump/protocols/st25tb/nfc_cli_dump_st25tb.c\",\n        \"cli/commands/dump/protocols/felica/nfc_cli_dump_felica.c\",\n        \"cli/commands/mfu/nfc_cli_command_mfu.c\",\n        \"cli/commands/mfu/nfc_cli_action_info.c\",\n        \"cli/commands/mfu/nfc_cli_action_rdbl.c\",\n        \"cli/commands/mfu/nfc_cli_action_wrbl.c\",\n        \"cli/commands/nfc_cli_command_emulate.c\",\n        \"cli/commands/nfc_cli_command_scanner.c\",\n        \"cli/commands/nfc_cli_command_field.c\",\n    ],\n)\n\nApp(\n    appid=\"banapass_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"banapass_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/banapass.c\"],\n)\n\nApp(\n    appid=\"aic_parser\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"aic_plugin_ep\",\n    targets=[\"f7\"],\n    requires=[\"nfc\"],\n    sources=[\"plugins/supported_cards/aic.c\"],\n)\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.c",
    "content": "#include \"nfc_cli_command_apdu.h\"\n#include \"../helpers/nfc_cli_format.h\"\n#include \"../helpers/nfc_cli_protocol_parser.h\"\n\n#include \"protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.h\"\n#include \"protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.h\"\n#include \"protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.h\"\n\n#include <furi.h>\n#include <nfc/nfc.h>\n#include <nfc/nfc_poller.h>\n\n#include <toolbox/args.h>\n#include <m-array.h>\n#include <m-algo.h>\n\n#define TAG \"APDU\"\n\n#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)\n\ntypedef NfcCommand (\n    *NfcCliApduProtocolHandler)(NfcGenericEvent event, NfcCliApduRequestResponse* instance);\n\nstatic const char* raw_error_names[] = {\n    [NfcCliApduErrorNone] = \"None\",\n    [NfcCliApduErrorTimeout] = \"Timeout\",\n    [NfcCliApduErrorProtocol] = \"Internal protocol\",\n    [NfcCliApduErrorWrongCrc] = \"Wrong CRC\",\n    [NfcCliApduErrorNotPresent] = \"No card\",\n};\n\ntypedef enum {\n    NfcCliProtocolRequestTypeNormalExecute,\n    NfcCliProtocolRequestTypeAbort,\n} NfcCliProtocolRequestType;\n\ntypedef struct {\n    uint8_t* data;\n    size_t size;\n} NfcCliApduData;\n\nstatic void ApduItem_init(NfcCliApduData* item) {\n    item->size = 0;\n    item->data = NULL;\n}\n\nstatic void ApduItem_init_set(NfcCliApduData* item, const NfcCliApduData* src) {\n    item->data = malloc(src->size);\n    item->size = src->size;\n    memcpy(item->data, src->data, src->size);\n}\n\nstatic void ApduItem_set(NfcCliApduData* item, const NfcCliApduData* src) {\n    if(item->data == NULL) {\n        item->data = malloc(src->size);\n    } else if(item->size != src->size) {\n        uint8_t* buf = realloc(item->data, src->size);\n        furi_check(buf);\n        item->data = buf;\n    }\n\n    item->size = src->size;\n    memcpy(item->data, src->data, src->size);\n}\n\nstatic void ApduItem_clear(NfcCliApduData* item) {\n    if(item->data) free(item->data);\n    item->data = NULL;\n    item->size = 0;\n}\n\nARRAY_DEF(\n    NfcCliApduItemArray,\n    NfcCliApduData,\n    (INIT(API_2(ApduItem_init)),\n     SET(API_6(ApduItem_set)),\n     INIT_SET(API_6(ApduItem_init_set)),\n     CLEAR(API_2(ApduItem_clear))))\n\ntypedef struct {\n    Nfc* nfc;\n    bool auto_detect;\n    NfcCliApduItemArray_t apdu;\n    NfcCliApduRequestResponse data;\n    FuriSemaphore* sem_done;\n    FuriMessageQueue* input_queue;\n} NfcCliApduContext;\n\nstatic NfcCliActionContext* nfc_cli_apdu_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliApduContext* instance = malloc(sizeof(NfcCliApduContext));\n    instance->nfc = nfc;\n    instance->data.protocol = NfcProtocolInvalid;\n    instance->auto_detect = true;\n    NfcCliApduItemArray_init(instance->apdu);\n    instance->data.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);\n    instance->data.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);\n\n    instance->sem_done = furi_semaphore_alloc(1, 0);\n    instance->input_queue = furi_message_queue_alloc(1, sizeof(NfcCliProtocolRequestType));\n    return instance;\n}\n\nstatic void nfc_cli_apdu_free_ctx(NfcCliActionContext* action_ctx) {\n    furi_assert(action_ctx);\n    NfcCliApduContext* instance = action_ctx;\n\n    instance->nfc = NULL;\n    NfcCliApduItemArray_clear(instance->apdu);\n\n    bit_buffer_free(instance->data.rx_buffer);\n    bit_buffer_free(instance->data.tx_buffer);\n    furi_semaphore_free(instance->sem_done);\n    furi_message_queue_free(instance->input_queue);\n    free(instance);\n}\n\nstatic inline void nfc_cli_apdu_print_result(const NfcCliApduContext* instance) {\n    nfc_cli_printf_array(\n        bit_buffer_get_data(instance->data.tx_buffer),\n        bit_buffer_get_size_bytes(instance->data.tx_buffer),\n        \"\\r\\nTx: \");\n\n    if(instance->data.result != NfcCliApduErrorNone)\n        printf(\"\\r\\nError: \\\"%s\\\"\\r\\n\", raw_error_names[instance->data.result]);\n\n    size_t rx_size = bit_buffer_get_size_bytes(instance->data.rx_buffer);\n    if(rx_size > 0) {\n        nfc_cli_printf_array(\n            bit_buffer_get_data(instance->data.rx_buffer),\n            bit_buffer_get_size_bytes(instance->data.rx_buffer),\n            \"\\r\\nRx: \");\n        printf(\"\\r\\n\");\n    }\n}\n\nstatic NfcProtocol nfc_cli_apdu_protocol_autodetect(Nfc* nfc) {\n    const NfcProtocol supported_protocols[] = {\n        NfcProtocolIso14443_4a,\n        NfcProtocolIso14443_4b,\n        NfcProtocolIso15693_3,\n    };\n\n    const char* supported_names[] = {\"Iso14443_4a\", \"Iso14443_4b\", \"Iso15693_3\"};\n\n    NfcProtocol protocol = NfcProtocolInvalid;\n    for(uint8_t i = 0; i < COUNT_OF(supported_protocols); i++) {\n        NfcPoller* poller = nfc_poller_alloc(nfc, supported_protocols[i]);\n        bool is_detected = nfc_poller_detect(poller);\n        nfc_poller_free(poller);\n        if(is_detected) {\n            protocol = supported_protocols[i];\n            printf(\"Detected tag: %s\\r\\n\", supported_names[i]);\n            break;\n        }\n    }\n    return protocol;\n}\n\nstatic NfcCliApduProtocolHandler nfc_cli_apdu_poller_get_handler(NfcProtocol protocol) {\n    if(protocol == NfcProtocolIso14443_4a)\n        return nfc_cli_apdu_iso14443_4a_handler;\n    else if(protocol == NfcProtocolIso14443_4b)\n        return nfc_cli_apdu_iso14443_4b_handler;\n    else if(protocol == NfcProtocolIso15693_3)\n        return nfc_cli_apdu_iso15693_3_handler;\n    else\n        return NULL;\n}\n\nstatic NfcCommand nfc_cli_apdu_poller_callback(NfcGenericEvent event, void* context) {\n    NfcCliApduContext* instance = context;\n\n    FURI_LOG_D(TAG, \"Poller callback\");\n    NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;\n    furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);\n\n    NfcCommand command = NfcCommandStop;\n    if(request_type == NfcCliProtocolRequestTypeAbort) {\n        FURI_LOG_D(TAG, \"Aborting poller callback\");\n    } else {\n        NfcCliApduProtocolHandler handler =\n            nfc_cli_apdu_poller_get_handler(instance->data.protocol);\n        if(handler) command = handler(event, &instance->data);\n    }\n    furi_semaphore_release(instance->sem_done);\n    return command;\n}\n\nstatic void nfc_cli_apdu_execute(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    furi_assert(context);\n    NfcCliApduContext* instance = context;\n\n    if(instance->auto_detect) {\n        instance->data.protocol = nfc_cli_apdu_protocol_autodetect(instance->nfc);\n    }\n\n    if(instance->data.protocol != NfcProtocolInvalid) {\n        NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->data.protocol);\n\n        NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeNormalExecute;\n        nfc_poller_start(poller, nfc_cli_apdu_poller_callback, instance);\n\n        NfcCliApduItemArray_it_t it;\n        for(NfcCliApduItemArray_it(it, instance->apdu); !NfcCliApduItemArray_end_p(it);\n            NfcCliApduItemArray_next(it)) {\n            const NfcCliApduData* item = NfcCliApduItemArray_cref(it);\n            bit_buffer_copy_bytes(instance->data.tx_buffer, item->data, item->size);\n            bit_buffer_reset(instance->data.rx_buffer);\n\n            furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);\n            furi_semaphore_acquire(instance->sem_done, FuriWaitForever);\n            nfc_cli_apdu_print_result(instance);\n            if(instance->data.result != NfcCliApduErrorNone) break;\n        }\n\n        request_type = NfcCliProtocolRequestTypeAbort;\n        furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);\n        nfc_poller_stop(poller);\n        nfc_poller_free(poller);\n    }\n}\n\nstatic const NfcProtocolNameValuePair supported_protocols[] = {\n    {.name = \"4a\", .value = NfcProtocolIso14443_4a},\n    {.name = \"4b\", .value = NfcProtocolIso14443_4b},\n    {.name = \"15\", .value = NfcProtocolIso15693_3},\n};\n\nstatic bool nfc_cli_apdu_parse_protocol(FuriString* value, void* output) {\n    NfcCliApduContext* ctx = output;\n    ctx->auto_detect = false;\n\n    NfcCliProtocolParser* parser =\n        nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));\n\n    bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->data.protocol);\n\n    nfc_cli_protocol_parser_free(parser);\n    return result;\n}\n\nstatic bool nfc_cli_apdu_parse_data(FuriString* value, void* output) {\n    NfcCliApduContext* ctx = output;\n\n    bool result = false;\n    FuriString* word = furi_string_alloc();\n\n    while(args_read_string_and_trim(value, word)) {\n        size_t len = furi_string_size(word);\n        if(len % 2 != 0) break;\n\n        size_t data_length = len / 2;\n\n        const size_t max_len = UINT16_MAX;\n        if(data_length > max_len) {\n            printf(\n                ANSI_FG_RED \"\\r\\nData payload is too long, max length = %d bytes\\r\\n\" ANSI_RESET,\n                max_len);\n            break;\n        }\n\n        NfcCliApduData* item = NfcCliApduItemArray_push_new(ctx->apdu);\n        item->size = data_length;\n        item->data = malloc(data_length);\n        result = args_read_hex_bytes(word, item->data, item->size);\n    }\n    furi_string_free(word);\n\n    return result;\n}\n\nconst NfcCliKeyDescriptor apdu_keys[] = {\n    {\n        .long_name = \"protocol\",\n        .short_name = \"p\",\n        .description = \"set protocol (4a, 4b, 15) directly, otherwise autodetected\",\n        .features = {.parameter = true, .required = false},\n        .parse = nfc_cli_apdu_parse_protocol,\n    },\n    {\n        .long_name = \"data\",\n        .short_name = \"d\",\n        .description = \"apdu payloads in format p1 p2 p3\",\n        .features = {.parameter = true, .multivalue = true, .required = true},\n        .parse = nfc_cli_apdu_parse_data,\n    },\n};\n\nconst NfcCliActionDescriptor apdu_action = {\n    .name = \"apdu\",\n    .description = \"Send APDU data to iso14443_4a, iso14443_4b or iso15693_3\",\n    .alloc = nfc_cli_apdu_alloc_ctx,\n    .free = nfc_cli_apdu_free_ctx,\n    .execute = nfc_cli_apdu_execute,\n    .key_count = COUNT_OF(apdu_keys),\n    .keys = apdu_keys,\n};\n\nconst NfcCliActionDescriptor* apdu_actions_collection[] = {&apdu_action};\n\n//Command descriptor\nADD_NFC_CLI_COMMAND(apdu, \"\", apdu_actions_collection);\n\n//Command usage: apdu <protocol> <data>\n//Command examples:\n//apdu -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102\n//apdu -p 4a -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102\n//apdu -p 4b -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102\n//apdu -p 15 -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor apdu_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.c",
    "content": "#include \"nfc_cli_apdu_iso14443_4a.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/protocols/iso14443_4a/iso14443_4a.h>\n#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>\n\n#define TAG \"ISO14A_4A\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliApduError nfc_cli_apdu_iso14443_4a_process_error(Iso14443_4aError error) {\n    switch(error) {\n    case Iso14443_4aErrorNone:\n        return NfcCliApduErrorNone;\n    case Iso14443_4aErrorTimeout:\n        return NfcCliApduErrorTimeout;\n    case Iso14443_4aErrorNotPresent:\n        return NfcCliApduErrorNotPresent;\n    default:\n        return NfcCliApduErrorProtocol;\n    }\n}\n\nNfcCommand\n    nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {\n    Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        Iso14443_4aError err = iso14443_4a_poller_send_block(\n            event.instance, instance->tx_buffer, instance->rx_buffer);\n        instance->result = nfc_cli_apdu_iso14443_4a_process_error(err);\n        if(err != Iso14443_4aErrorNone) command = NfcCommandStop;\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_apdu_common_types.h\"\n\nNfcCommand\n    nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.c",
    "content": "#include \"nfc_cli_apdu_iso14443_4b.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/protocols/iso14443_4b/iso14443_4b.h>\n#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>\n\n#define TAG \"ISO14A_4B\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliApduError nfc_cli_apdu_iso14443_4b_process_error(Iso14443_4bError error) {\n    switch(error) {\n    case Iso14443_4bErrorNone:\n        return NfcCliApduErrorNone;\n    case Iso14443_4bErrorTimeout:\n        return NfcCliApduErrorTimeout;\n    case Iso14443_4bErrorNotPresent:\n        return NfcCliApduErrorNotPresent;\n    default:\n        return NfcCliApduErrorProtocol;\n    }\n}\n\nNfcCommand\n    nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {\n    Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {\n        Iso14443_4bError err = iso14443_4b_poller_send_block(\n            event.instance, instance->tx_buffer, instance->rx_buffer);\n        instance->result = nfc_cli_apdu_iso14443_4b_process_error(err);\n        if(err != Iso14443_4bErrorNone) command = NfcCommandStop;\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_apdu_common_types.h\"\n\nNfcCommand\n    nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.c",
    "content": "#include \"nfc_cli_apdu_iso15693_3.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/protocols/iso15693_3/iso15693_3.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>\n\n#define TAG \"ISO15\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliApduError nfc_cli_apdu_iso15693_3_process_error(Iso15693_3Error error) {\n    switch(error) {\n    case Iso15693_3ErrorNone:\n        return NfcCliApduErrorNone;\n    case Iso15693_3ErrorTimeout:\n        return NfcCliApduErrorTimeout;\n    case Iso15693_3ErrorNotPresent:\n        return NfcCliApduErrorNotPresent;\n    default:\n        return NfcCliApduErrorProtocol;\n    }\n}\n\nNfcCommand\n    nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {\n    Iso15693_3PollerEvent* iso15693_3_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {\n        Iso15693_3Error err = iso15693_3_poller_send_frame(\n            event.instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        instance->result = nfc_cli_apdu_iso15693_3_process_error(err);\n        if(err != Iso15693_3ErrorNone) command = NfcCommandStop;\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_apdu_common_types.h\"\n\nNfcCommand\n    nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/apdu/protocol_handlers/nfc_cli_apdu_common_types.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <nfc/nfc.h>\n#include <nfc/nfc_poller.h>\n\ntypedef enum {\n    NfcCliApduErrorNone,\n    NfcCliApduErrorTimeout,\n    NfcCliApduErrorNotPresent,\n    NfcCliApduErrorWrongCrc,\n    NfcCliApduErrorProtocol,\n} NfcCliApduError;\n\ntypedef struct {\n    NfcProtocol protocol;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    NfcCliApduError result;\n} NfcCliApduRequestResponse;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.c",
    "content": "#include \"nfc_cli_command_dump.h\"\n#include \"protocols/nfc_cli_dump_common_types.h\"\n#include \"../helpers/nfc_cli_format.h\"\n#include \"../helpers/nfc_cli_protocol_parser.h\"\n#include \"../helpers/nfc_cli_scanner.h\"\n\n#include \"protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.h\"\n#include \"protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.h\"\n#include \"protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.h\"\n#include \"protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.h\"\n#include \"protocols/iso15693_3/nfc_cli_dump_iso15693_3.h\"\n#include \"protocols/mf_classic/nfc_cli_dump_mf_classic.h\"\n#include \"protocols/mf_desfire/nfc_cli_dump_mf_desfire.h\"\n#include \"protocols/mf_plus/nfc_cli_dump_mf_plus.h\"\n#include \"protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.h\"\n#include \"protocols/slix/nfc_cli_dump_slix.h\"\n#include \"protocols/st25tb/nfc_cli_dump_st25tb.h\"\n#include \"protocols/felica/nfc_cli_dump_felica.h\"\n\n#include <datetime.h>\n#include <furi_hal_rtc.h>\n#include <toolbox/strint.h>\n#include <toolbox/path.h>\n#include <toolbox/args.h>\n\n#define NFC_DEFAULT_FOLDER              EXT_PATH(\"nfc\")\n#define NFC_FILE_EXTENSION              \".nfc\"\n#define NFC_CLI_DEFAULT_FILENAME_PREFIX \"dump\"\n\n#define NFC_CLI_DUMP_DEFAULT_TIMEOUT (5000)\n\n#define TAG \"DUMP\"\n\nstatic const char* nfc_cli_dump_error_names[NfcCliDumpErrorNum] = {\n    [NfcCliDumpErrorNone] = \"\",\n    [NfcCliDumpErrorNotPresent] = \"card not present\",\n    [NfcCliDumpErrorAuthFailed] = \"authentication failed\",\n    [NfcCliDumpErrorTimeout] = \"timeout\",\n    [NfcCliDumpErrorFailedToRead] = \"failed to read\",\n};\n\nstatic NfcCliActionContext* nfc_cli_dump_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliDumpContext* instance = malloc(sizeof(NfcCliDumpContext));\n    instance->nfc = nfc;\n    instance->file_path = furi_string_alloc();\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    instance->sem_done = furi_semaphore_alloc(1, 0);\n    instance->nfc_device = nfc_device_alloc();\n    instance->desired_protocol = NfcProtocolInvalid;\n    instance->auth_ctx.skip_auth = true;\n    instance->auth_ctx.key_size = 0;\n    instance->timeout = NFC_CLI_DUMP_DEFAULT_TIMEOUT;\n\n    instance->mfc_key_cache = mf_classic_key_cache_alloc();\n    instance->scanner = nfc_cli_scanner_alloc(nfc);\n    return instance;\n}\n\nstatic void nfc_cli_dump_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliDumpContext* instance = ctx;\n    instance->desired_protocol = NfcProtocolInvalid;\n    furi_string_free(instance->file_path);\n    instance->nfc = NULL;\n    furi_record_close(RECORD_STORAGE);\n    furi_semaphore_free(instance->sem_done);\n    nfc_device_free(instance->nfc_device);\n\n    mf_classic_key_cache_free(instance->mfc_key_cache);\n    nfc_cli_scanner_free(instance->scanner);\n    free(instance);\n}\n\nstatic bool nfc_cli_dump_parse_filename_key(FuriString* value, void* output) {\n    furi_assert(value);\n    furi_assert(output);\n    NfcCliDumpContext* ctx = output;\n    furi_string_set(ctx->file_path, value);\n    return true;\n}\n\nNfcGenericCallback protocol_poller_callbacks[NfcProtocolNum] = {\n    [NfcProtocolMfUltralight] = nfc_cli_dump_poller_callback_mf_ultralight,\n    [NfcProtocolMfClassic] = nfc_cli_dump_poller_callback_mf_classic,\n    [NfcProtocolFelica] = nfc_cli_dump_poller_callback_felica,\n    [NfcProtocolIso14443_3a] = nfc_cli_dump_poller_callback_iso14443_3a,\n    [NfcProtocolIso14443_3b] = nfc_cli_dump_poller_callback_iso14443_3b,\n    [NfcProtocolIso14443_4a] = nfc_cli_dump_poller_callback_iso14443_4a,\n    [NfcProtocolIso14443_4b] = nfc_cli_dump_poller_callback_iso14443_4b,\n    [NfcProtocolIso15693_3] = nfc_cli_dump_poller_callback_iso15693_3,\n    [NfcProtocolSlix] = nfc_cli_dump_poller_callback_slix,\n    [NfcProtocolMfDesfire] = nfc_cli_dump_poller_callback_mf_desfire,\n    [NfcProtocolMfPlus] = nfc_cli_dump_poller_callback_mf_plus,\n    [NfcProtocolSt25tb] = nfc_cli_dump_poller_callback_st25tb,\n};\n\nstatic void nfc_cli_dump_generate_filename(FuriString* file_path) {\n    furi_string_set_str(file_path, NFC_DEFAULT_FOLDER);\n    DateTime dt;\n    furi_hal_rtc_get_datetime(&dt);\n    furi_string_cat_printf(\n        file_path,\n        \"/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%s\",\n        NFC_CLI_DEFAULT_FILENAME_PREFIX,\n        dt.year,\n        dt.month,\n        dt.day,\n        dt.hour,\n        dt.minute,\n        dt.second,\n        NFC_FILE_EXTENSION);\n}\n\nstatic bool nfc_cli_dump_check_filepath_valid(FuriString* file_path, Storage* storage) {\n    bool file_exists = false;\n    bool dir_exists = false;\n\n    FuriString* buf = furi_string_alloc();\n\n    path_extract_dirname(furi_string_get_cstr(file_path), buf);\n    dir_exists = storage_dir_exists(storage, furi_string_get_cstr(buf));\n    file_exists = storage_file_exists(storage, furi_string_get_cstr(file_path));\n\n    bool result = true;\n    if(!dir_exists) {\n        printf(ANSI_FG_RED \"Path \\'%s\\' doesn't exist\\r\\n\" ANSI_RESET, furi_string_get_cstr(buf));\n        result = false;\n    } else if(file_exists) {\n        printf(\n            ANSI_FG_RED \"File \\'%s\\' already exists\\r\\n\" ANSI_RESET,\n            furi_string_get_cstr(file_path));\n        result = false;\n    }\n    furi_string_free(buf);\n\n    return result;\n}\n\nstatic bool nfc_cli_dump_process_filename(NfcCliDumpContext* instance) {\n    bool result = false;\n    if(furi_string_empty(instance->file_path)) {\n        nfc_cli_dump_generate_filename(instance->file_path);\n        result = true;\n    } else {\n        result = nfc_cli_dump_check_filepath_valid(instance->file_path, instance->storage);\n    }\n    return result;\n}\n\nstatic size_t nfc_cli_dump_set_protocol(NfcCliDumpContext* instance) {\n    size_t protocol_count = 0;\n    if(instance->desired_protocol != NfcProtocolInvalid) {\n        protocol_count = 1;\n    } else {\n        if(!nfc_cli_scanner_detect_protocol(instance->scanner, instance->timeout)) {\n            NfcCliDumpError error = NfcCliDumpErrorTimeout;\n            printf(ANSI_FG_RED \"Error: %s\\r\\n\" ANSI_RESET, nfc_cli_dump_error_names[error]);\n        } else {\n            nfc_cli_scanner_list_detected_protocols(instance->scanner);\n            protocol_count = nfc_cli_scanner_detected_protocol_num(instance->scanner);\n            instance->desired_protocol = nfc_cli_scanner_get_protocol(instance->scanner, 0);\n        }\n    }\n    return protocol_count;\n}\n\nstatic bool nfc_cli_dump_card(NfcCliDumpContext* instance) {\n    instance->poller = nfc_poller_alloc(instance->nfc, instance->desired_protocol);\n    NfcGenericCallback callback = protocol_poller_callbacks[instance->desired_protocol];\n    if(callback) {\n        nfc_poller_start(instance->poller, callback, instance);\n        FuriStatus status = furi_semaphore_acquire(instance->sem_done, instance->timeout);\n\n        if(status == FuriStatusErrorTimeout) instance->result = NfcCliDumpErrorTimeout;\n        nfc_poller_stop(instance->poller);\n    }\n    nfc_poller_free(instance->poller);\n\n    return instance->result == NfcCliDumpErrorNone;\n}\n\nstatic void nfc_cli_dump_execute(PipeSide* pipe, NfcCliActionContext* context) {\n    UNUSED(pipe);\n    furi_assert(context);\n    NfcCliDumpContext* instance = context;\n    do {\n        if(!nfc_cli_dump_process_filename(instance)) break;\n\n        size_t protocol_count = nfc_cli_dump_set_protocol(instance);\n        if(instance->desired_protocol == NfcProtocolInvalid) break;\n\n        printf(\"Dumping as \\\"%s\\\"\\r\\n\", nfc_cli_get_protocol_name(instance->desired_protocol));\n        if(protocol_count > 1) printf(\"Use \\'-p\\' key to specify another protocol\\r\\n\");\n\n        if(nfc_cli_dump_card(instance)) {\n            const char* path = furi_string_get_cstr(instance->file_path);\n            if(nfc_device_save(instance->nfc_device, path)) {\n                printf(\"Dump saved to \\'%s\\'\\r\\n\", path);\n            }\n        } else {\n            printf(\n                ANSI_FG_RED \"Error: %s\\r\\n\" ANSI_RESET,\n                nfc_cli_dump_error_names[instance->result]);\n        }\n    } while(false);\n}\n\nstatic const NfcProtocolNameValuePair supported_protocols[] = {\n    {.name = \"14_3a\", .value = NfcProtocolIso14443_3a},\n    {.name = \"14_3b\", .value = NfcProtocolIso14443_3b},\n    {.name = \"14_4a\", .value = NfcProtocolIso14443_4a},\n    {.name = \"14_4b\", .value = NfcProtocolIso14443_4b},\n    {.name = \"15\", .value = NfcProtocolIso15693_3},\n    {.name = \"felica\", .value = NfcProtocolFelica},\n    {.name = \"mfu\", .value = NfcProtocolMfUltralight},\n    {.name = \"mfc\", .value = NfcProtocolMfClassic},\n    {.name = \"mfp\", .value = NfcProtocolMfPlus},\n    {.name = \"des\", .value = NfcProtocolMfDesfire},\n    {.name = \"slix\", .value = NfcProtocolSlix},\n    {.name = \"st25\", .value = NfcProtocolSt25tb},\n};\n\nstatic bool nfc_cli_dump_parse_protocol(FuriString* value, void* output) {\n    furi_assert(value);\n    furi_assert(output);\n    NfcCliDumpContext* ctx = output;\n\n    NfcCliProtocolParser* parser =\n        nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));\n\n    bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->desired_protocol);\n\n    nfc_cli_protocol_parser_free(parser);\n    return result;\n}\n\nstatic bool nfc_cli_dump_parse_key(FuriString* value, void* output) {\n    furi_assert(value);\n    furi_assert(output);\n    NfcCliDumpContext* ctx = output;\n    NfcCliDumpAuthContext* auth_ctx = &ctx->auth_ctx;\n\n    bool result = false;\n    do {\n        size_t len = furi_string_size(value);\n        if(len % 2 != 0) break;\n        size_t data_length = len / 2;\n\n        if(data_length != MF_ULTRALIGHT_AUTH_PASSWORD_SIZE &&\n           data_length != MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE) {\n            printf(ANSI_FG_RED \"Error: Wrong key size\" ANSI_RESET);\n            break;\n        }\n\n        if(!args_read_hex_bytes(value, auth_ctx->key.key, data_length)) break;\n        auth_ctx->key_size = data_length;\n        auth_ctx->skip_auth = false;\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nbool nfc_cli_dump_parse_timeout(FuriString* value, NfcCliActionContext* output) {\n    NfcCliDumpContext* ctx = output;\n\n    StrintParseError err = strint_to_uint32(furi_string_get_cstr(value), NULL, &ctx->timeout, 10);\n    return err == StrintParseNoError;\n}\n\nconst NfcCliKeyDescriptor dump_keys[] = {\n    {\n        .long_name = \"key\",\n        .short_name = \"k\",\n        .description = \"key to path auth in protocols which requires it\",\n        .features = {.required = false, .parameter = true},\n        .parse = nfc_cli_dump_parse_key,\n    },\n    {\n        .long_name = \"protocol\",\n        .short_name = \"p\",\n        .description = \"desired protocol\",\n        .features = {.required = false, .parameter = true},\n        .parse = nfc_cli_dump_parse_protocol,\n    },\n    {\n        .features = {.required = false, .parameter = true},\n        .long_name = \"file\",\n        .short_name = \"f\",\n        .description = \"path to new file\",\n        .parse = nfc_cli_dump_parse_filename_key,\n    },\n    {\n        .features = {.required = false, .parameter = true},\n        .long_name = \"timeout\",\n        .short_name = \"t\",\n        .description = \"timeout value in milliseconds\",\n        .parse = nfc_cli_dump_parse_timeout,\n    },\n};\n\nconst NfcCliActionDescriptor dump_action = {\n    .name = \"dump\",\n    .description = \"Dump tag to .nfc file\",\n    .alloc = nfc_cli_dump_alloc_ctx,\n    .free = nfc_cli_dump_free_ctx,\n    .execute = nfc_cli_dump_execute,\n    .key_count = COUNT_OF(dump_keys),\n    .keys = dump_keys,\n};\n\nconst NfcCliActionDescriptor* dump_actions_collection[] = {&dump_action};\n\n//Command descriptor\nADD_NFC_CLI_COMMAND(dump, \"\", dump_actions_collection);\n\n//Command examples:\n//dump -f ext/nfc/test.nfc\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor dump_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/felica/nfc_cli_dump_felica.c",
    "content": "#include \"nfc_cli_dump_felica.h\"\n#include <nfc/protocols/felica/felica_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolFelica);\n\n    NfcCliDumpContext* instance = context;\n    const FelicaPollerEvent* felica_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(felica_event->type == FelicaPollerEventTypeReady ||\n       felica_event->type == FelicaPollerEventTypeIncomplete) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));\n        command = NfcCommandStop;\n        instance->result = NfcCliDumpErrorNone;\n    } else if(felica_event->type == FelicaPollerEventTypeError) {\n        command = NfcCommandStop;\n        instance->result = NfcCliDumpErrorFailedToRead;\n    } else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {\n        FelicaAuthenticationContext* ctx = felica_event->data->auth_context;\n        const NfcCliDumpAuthContext* dump_auth_ctx = &instance->auth_ctx;\n        ctx->skip_auth = dump_auth_ctx->skip_auth;\n        ctx->card_key = dump_auth_ctx->key.felica_key;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/felica/nfc_cli_dump_felica.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.c",
    "content": "#include \"nfc_cli_dump_iso14443_3a.h\"\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    NfcCliDumpContext* instance = context;\n    const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller));\n        command = NfcCommandStop;\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        command = NfcCommandStop;\n        instance->result = NfcCliDumpErrorFailedToRead;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.c",
    "content": "#include \"nfc_cli_dump_iso14443_3b.h\"\n#include <nfc/protocols/iso14443_3b/iso14443_3b_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3b);\n\n    NfcCliDumpContext* instance = context;\n    const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller));\n\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return NfcCommandContinue;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.c",
    "content": "#include \"nfc_cli_dump_iso14443_4a.h\"\n#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n\n    NfcCliDumpContext* instance = context;\n    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.c",
    "content": "#include \"nfc_cli_dump_iso14443_4b.h\"\n#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4b);\n\n    NfcCliDumpContext* instance = context;\n    const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(instance->poller));\n        command = NfcCommandStop;\n    } else if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeError) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso15693_3/nfc_cli_dump_iso15693_3.c",
    "content": "#include \"nfc_cli_dump_iso15693_3.h\"\n#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n\n    NfcCliDumpContext* instance = context;\n    const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    } else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/iso15693_3/nfc_cli_dump_iso15693_3.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_classic/nfc_cli_dump_mf_classic.c",
    "content": "#include \"nfc_cli_dump_mf_classic.h\"\n#include <nfc/protocols/mf_classic/mf_classic_poller.h>\n\n#define TAG \"MFC\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcCliDumpContext* instance = context;\n    const MfClassicPollerEvent* mfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));\n        size_t uid_len = 0;\n        const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);\n        if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) {\n            FURI_LOG_D(TAG, \"Key cache found\");\n            mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;\n        } else {\n            FURI_LOG_D(TAG, \"Key cache not found\");\n            instance->result = NfcCliDumpErrorFailedToRead;\n            command = NfcCommandStop;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {\n        uint8_t sector_num = 0;\n        MfClassicKey key = {};\n        MfClassicKeyType key_type = MfClassicKeyTypeA;\n        if(mf_classic_key_cache_get_next_key(\n               instance->mfc_key_cache, &sector_num, &key, &key_type)) {\n            mfc_event->data->read_sector_request_data.sector_num = sector_num;\n            mfc_event->data->read_sector_request_data.key = key;\n            mfc_event->data->read_sector_request_data.key_type = key_type;\n            mfc_event->data->read_sector_request_data.key_provided = true;\n        } else {\n            mfc_event->data->read_sector_request_data.key_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(mfc_event->type == MfClassicPollerEventTypeFail) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_classic/nfc_cli_dump_mf_classic.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_desfire/nfc_cli_dump_mf_desfire.c",
    "content": "#include \"nfc_cli_dump_mf_desfire.h\"\n#include <nfc/protocols/mf_desfire/mf_desfire_poller.h>\n\n#define TAG \"MFDES\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfDesfire);\n\n    NfcCommand command = NfcCommandContinue;\n\n    NfcCliDumpContext* instance = context;\n    const MfDesfirePollerEvent* mf_desfire_event = event.event_data;\n\n    if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorNone;\n        furi_semaphore_release(instance->sem_done);\n        command = NfcCommandStop;\n    } else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandReset;\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_desfire/nfc_cli_dump_mf_desfire.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_plus/nfc_cli_dump_mf_plus.c",
    "content": "#include \"nfc_cli_dump_mf_plus.h\"\n#include <nfc/protocols/mf_plus/mf_plus_poller.h>\n\n#define TAG \"MFPLUS\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolMfPlus);\n    furi_assert(event.event_data);\n\n    NfcCliDumpContext* instance = context;\n    const MfPlusPollerEvent* mf_plus_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandReset;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_plus/nfc_cli_dump_mf_plus.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.c",
    "content": "#include \"nfc_cli_dump_mf_ultralight.h\"\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>\n\n#define TAG \"MFU\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfUltralight);\n    NfcCliDumpContext* instance = context;\n    const MfUltralightPollerEvent* mf_ultralight_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));\n\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));\n\n        const NfcCliDumpAuthContext* auth_ctx = &instance->auth_ctx;\n        mf_ultralight_event->data->auth_context.skip_auth = auth_ctx->skip_auth;\n        mf_ultralight_event->data->auth_context.password = auth_ctx->key.password;\n        mf_ultralight_event->data->auth_context.tdes_key = auth_ctx->key.tdes_key;\n    } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthFailed) {\n        instance->result = NfcCliDumpErrorAuthFailed;\n        command = NfcCommandStop;\n    } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadFailed) {\n        instance->result = NfcCliDumpErrorAuthFailed;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/nfc_cli_dump_common_types.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include \"../../../../helpers/mf_classic_key_cache.h\"\n#include \"../../helpers/nfc_cli_scanner.h\"\n\n#include <nfc/nfc.h>\n#include <nfc/protocols/nfc_protocol.h>\n#include <nfc/nfc_device.h>\n#include <nfc/nfc_poller.h>\n\n#include <nfc/protocols/felica/felica.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n\n#include <storage/storage.h>\n\n#define NFC_CLI_DUMP_KEY_MAX_SIZE (16)\n\ntypedef union {\n    MfUltralightAuthPassword password;\n    FelicaCardKey felica_key;\n    MfUltralightC3DesAuthKey tdes_key;\n    uint8_t key[NFC_CLI_DUMP_KEY_MAX_SIZE];\n} NfcCliDumpKeyUnion;\n\ntypedef struct {\n    NfcCliDumpKeyUnion key;\n    uint8_t key_size;\n    bool skip_auth;\n} NfcCliDumpAuthContext;\n\ntypedef enum {\n    NfcCliDumpErrorNone,\n    NfcCliDumpErrorTimeout,\n    NfcCliDumpErrorNotPresent,\n    NfcCliDumpErrorFailedToRead,\n    NfcCliDumpErrorAuthFailed,\n\n    NfcCliDumpErrorNum,\n} NfcCliDumpError;\n\ntypedef struct {\n    Nfc* nfc;\n    FuriString* file_path;\n    Storage* storage;\n    NfcCliScanner* scanner;\n    NfcProtocol desired_protocol;\n    uint32_t timeout;\n    FuriSemaphore* sem_done;\n\n    NfcCliDumpError result;\n\n    NfcCliDumpAuthContext auth_ctx;\n    MfClassicKeyCache* mfc_key_cache;\n\n    NfcPoller* poller;\n    NfcDevice* nfc_device;\n} NfcCliDumpContext;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/slix/nfc_cli_dump_slix.c",
    "content": "#include \"nfc_cli_dump_slix.h\"\n#include <nfc/protocols/slix/slix_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolSlix);\n\n    NfcCliDumpContext* instance = context;\n    const SlixPollerEvent* slix_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(slix_event->type == SlixPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller));\n        command = NfcCommandStop;\n    } else if(slix_event->type == SlixPollerEventTypeError) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/slix/nfc_cli_dump_slix.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/st25tb/nfc_cli_dump_st25tb.c",
    "content": "#include \"nfc_cli_dump_st25tb.h\"\n#include <nfc/protocols/st25tb/st25tb_poller.h>\n\nNfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolSt25tb);\n\n    NfcCliDumpContext* instance = context;\n    const St25tbPollerEvent* st25tb_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {\n        st25tb_event->data->mode_request.mode = St25tbPollerModeRead;\n    } else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));\n        instance->result = NfcCliDumpErrorNone;\n        command = NfcCommandStop;\n    } else if(st25tb_event->type == St25tbPollerEventTypeFailure) {\n        instance->result = NfcCliDumpErrorFailedToRead;\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_semaphore_release(instance->sem_done);\n    }\n\n    return command;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/dump/protocols/st25tb/nfc_cli_dump_st25tb.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_dump_common_types.h\"\n\nNfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_format.c",
    "content": "#include \"nfc_cli_format.h\"\n\nstatic const char* protocol_names[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] = \"Iso14443-3a\",\n    [NfcProtocolIso14443_3b] = \"Iso14443-3b\",\n    [NfcProtocolIso14443_4a] = \"Iso14443-4a\",\n    [NfcProtocolIso14443_4b] = \"Iso14443-4b\",\n    [NfcProtocolIso15693_3] = \"Iso15693-3\",\n    [NfcProtocolFelica] = \"FeliCa\",\n    [NfcProtocolMfUltralight] = \"Mifare Ultralight\",\n    [NfcProtocolMfClassic] = \"Mifare Classic\",\n    [NfcProtocolMfDesfire] = \"Mifare DESFire\",\n    [NfcProtocolMfPlus] = \"Mifare Plus\",\n    [NfcProtocolSlix] = \"Slix\",\n    [NfcProtocolSt25tb] = \"St25tb\",\n};\n\nconst char* nfc_cli_get_protocol_name(NfcProtocol protocol) {\n    furi_assert(protocol < NfcProtocolNum);\n    return protocol_names[protocol];\n}\n\nstatic const char* mf_ultralight_error_names[] = {\n    [MfUltralightErrorNone] = \"OK\",\n    [MfUltralightErrorNotPresent] = \"Card not present\",\n    [MfUltralightErrorProtocol] = \"Protocol failure\",\n    [MfUltralightErrorAuth] = \"Auth failed\",\n    [MfUltralightErrorTimeout] = \"Timeout\",\n};\n\nconst char* nfc_cli_mf_ultralight_get_error(MfUltralightError error) {\n    furi_assert(error < COUNT_OF(mf_ultralight_error_names));\n    return mf_ultralight_error_names[error];\n}\n\nvoid nfc_cli_format_array(\n    const uint8_t* data,\n    const size_t data_size,\n    const char* header,\n    FuriString* output) {\n    furi_assert(data);\n    furi_assert(data_size > 0);\n    furi_assert(header);\n    furi_assert(output);\n\n    furi_string_cat_printf(output, \"%s\", header);\n    for(size_t i = 0; i < data_size; i++) {\n        furi_string_cat_printf(output, \"%02X \", data[i]);\n    }\n}\n\nvoid nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header) {\n    furi_assert(data);\n    furi_assert(data_size > 0);\n    furi_assert(header);\n\n    printf(\"%s\", header);\n    for(size_t i = 0; i < data_size; i++) {\n        printf(\"%02X \", data[i]);\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_format.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <nfc/protocols/nfc_protocol.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n\nconst char* nfc_cli_get_protocol_name(NfcProtocol protocol);\nconst char* nfc_cli_mf_ultralight_get_error(MfUltralightError error);\n\nvoid nfc_cli_format_array(\n    const uint8_t* data,\n    const size_t data_size,\n    const char* header,\n    FuriString* output);\n\nvoid nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_protocol_parser.c",
    "content": "#include \"nfc_cli_protocol_parser.h\"\n\n#include <m-bptree.h>\n\n#define PROTOCOLS_TREE_RANK 4\n\nBPTREE_DEF2(\n    ProtocolTree,\n    PROTOCOLS_TREE_RANK,\n    FuriString*,\n    FURI_STRING_OPLIST,\n    NfcProtocol,\n    M_POD_OPLIST);\n\n#define M_OPL_ProtocolTree_t() BPTREE_OPLIST(ProtocolTree, M_POD_OPLIST)\n\nstruct NfcCliProtocolParser {\n    ProtocolTree_t protocols;\n};\n\nNfcCliProtocolParser* nfc_cli_protocol_parser_alloc(\n    const NfcProtocolNameValuePair* valid_protocols,\n    const size_t valid_count) {\n    furi_assert(valid_protocols);\n    furi_assert(valid_count > 0);\n\n    NfcCliProtocolParser* instance = malloc(sizeof(NfcCliProtocolParser));\n\n    FuriString* name = furi_string_alloc();\n    ProtocolTree_init(instance->protocols);\n    for(size_t i = 0; i < valid_count; i++) {\n        const NfcProtocolNameValuePair* item = &valid_protocols[i];\n        furi_string_set_str(name, item->name);\n        ProtocolTree_set_at(instance->protocols, name, item->value);\n    }\n\n    furi_string_free(name);\n    return instance;\n}\n\nvoid nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance) {\n    furi_assert(instance);\n    ProtocolTree_clear(instance->protocols);\n    free(instance);\n}\n\nbool nfc_cli_protocol_parser_get(\n    NfcCliProtocolParser* instance,\n    FuriString* key,\n    NfcProtocol* result) {\n    furi_assert(instance);\n    furi_assert(key);\n\n    NfcProtocol* protocol = ProtocolTree_get(instance->protocols, key);\n    if(protocol) *result = *protocol;\n\n    return protocol != NULL;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_protocol_parser.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <nfc/protocols/nfc_protocol.h>\n\ntypedef struct {\n    const char* name;\n    NfcProtocol value;\n} NfcProtocolNameValuePair;\n\ntypedef struct NfcCliProtocolParser NfcCliProtocolParser;\n\nNfcCliProtocolParser* nfc_cli_protocol_parser_alloc(\n    const NfcProtocolNameValuePair* valid_protocols,\n    const size_t valid_count);\n\nvoid nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance);\nbool nfc_cli_protocol_parser_get(\n    NfcCliProtocolParser* instance,\n    FuriString* key,\n    NfcProtocol* result);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.c",
    "content": "#include \"nfc_cli_scanner.h\"\n#include <nfc/nfc_scanner.h>\n#include \"nfc_cli_format.h\"\n\n#define NFC_CLI_SCANNER_FLAG_DETECTED (1UL << 0)\n\nstruct NfcCliScanner {\n    Nfc* nfc;\n    size_t protocols_detected_num;\n    NfcProtocol protocols_detected[NfcProtocolNum];\n    FuriThreadId thread_id;\n    NfcScanner* scanner;\n};\n\nNfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc) {\n    NfcCliScanner* instance = malloc(sizeof(NfcCliScanner));\n    instance->nfc = nfc;\n    instance->thread_id = furi_thread_get_current_id();\n    return instance;\n}\n\nvoid nfc_cli_scanner_free(NfcCliScanner* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nstatic void nfc_cli_scanner_detect_callback(NfcScannerEvent event, void* context) {\n    furi_assert(context);\n    NfcCliScanner* instance = context;\n\n    if(event.type == NfcScannerEventTypeDetected) {\n        instance->protocols_detected_num = event.data.protocol_num;\n        memcpy(\n            instance->protocols_detected,\n            event.data.protocols,\n            event.data.protocol_num * sizeof(NfcProtocol));\n        furi_thread_flags_set(instance->thread_id, NFC_CLI_SCANNER_FLAG_DETECTED);\n    }\n}\n\nbool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout) {\n    instance->scanner = nfc_scanner_alloc(instance->nfc);\n    nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);\n    uint32_t event =\n        furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);\n    nfc_scanner_stop(instance->scanner);\n    nfc_scanner_free(instance->scanner);\n    return (event == NFC_CLI_SCANNER_FLAG_DETECTED);\n}\n\nvoid nfc_cli_scanner_begin_scan(NfcCliScanner* instance) {\n    instance->scanner = nfc_scanner_alloc(instance->nfc);\n    nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);\n}\n\nbool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout) {\n    UNUSED(instance);\n    uint32_t event =\n        furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);\n    return (event == NFC_CLI_SCANNER_FLAG_DETECTED);\n}\n\nvoid nfc_cli_scanner_end_scan(NfcCliScanner* instance) {\n    nfc_scanner_stop(instance->scanner);\n    nfc_scanner_free(instance->scanner);\n}\n\nvoid nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance) {\n    printf(\"Protocols detected: \");\n    size_t n = instance->protocols_detected_num;\n    for(size_t i = 0; i < n; i++) {\n        const char* name = nfc_cli_get_protocol_name(instance->protocols_detected[i]);\n        printf((i == (n - 1)) ? \"%s\\r\\n\" : \"%s, \", name);\n    }\n}\n\nbool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol) {\n    furi_assert(instance);\n    furi_assert(protocol < NfcProtocolNum);\n\n    for(size_t i = 0; i < instance->protocols_detected_num; i++) {\n        if(instance->protocols_detected[i] == protocol) return true;\n    }\n    return false;\n}\n\nNfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx) {\n    furi_assert(instance);\n    furi_assert(idx < instance->protocols_detected_num);\n    return instance->protocols_detected[idx];\n}\n\nsize_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance) {\n    furi_assert(instance);\n    return instance->protocols_detected_num;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <nfc.h>\n#include <nfc/protocols/nfc_protocol.h>\n\ntypedef struct NfcCliScanner NfcCliScanner;\n\nNfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc);\nvoid nfc_cli_scanner_free(NfcCliScanner* instance);\n\nbool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout);\n\nvoid nfc_cli_scanner_begin_scan(NfcCliScanner* instance);\nbool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout);\nvoid nfc_cli_scanner_end_scan(NfcCliScanner* instance);\n\nvoid nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance);\nsize_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance);\nbool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol);\nNfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.c",
    "content": "\n#include \"nfc_cli_action_info.h\"\n\n#include \"../../../helpers/protocol_support/mf_ultralight/mf_ultralight_render.h\"\n#include \"../helpers/nfc_cli_format.h\"\n\n#include <furi.h>\n#include <flipper_format/flipper_format.h>\n#include <storage/storage.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>\n\n#define TAG \"INFO\"\n\ntypedef struct {\n    uint8_t magic;\n    union {\n        uint8_t value;\n        struct {\n            uint8_t minor : 4;\n            uint8_t major : 4;\n        };\n    } version;\n\n    uint8_t size;\n\n    union {\n        uint8_t value;\n        struct {\n            uint8_t write : 4;\n            uint8_t read  : 4;\n        };\n    } access;\n} FURI_PACKED MfUltralightCapabilityContainer;\n\ntypedef struct {\n    Nfc* nfc;\n    MfUltralightData* data;\n} NfcCliMfuContext;\n\nstatic void nfc_cli_mfu_info_get_vendor(const uint8_t vendor_key, FuriString* output) {\n    furi_assert(output);\n\n    FuriString* buf = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(ff, EXT_PATH(\"nfc/assets/vendors.nfc\"))) {\n            FURI_LOG_W(TAG, \"NFC Vendors dict not found\");\n            break;\n        }\n\n        char uid_str[5];\n        snprintf(uid_str, sizeof(uid_str), \"%d\", vendor_key);\n\n        if(flipper_format_read_string(ff, uid_str, buf))\n            furi_string_printf(output, \"%s, %s\", uid_str, furi_string_get_cstr(buf));\n        else\n            furi_string_printf(output, \"unknown\");\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(buf);\n}\n\nconst char*\n    nfc_cli_mfu_capability_container_get_access_description(const uint8_t value, bool read) {\n    const char* description = \"RFU\"; //value 0x01 - 0x07, and 0xF when read\n    if(value == 0x00)\n        description = \"access fully granted\";\n    else if(value >= 0x08 && value <= 0x0E)\n        description = \"proprietary\";\n    else if(value == 0x0F && !read)\n        description = \"no access granted at all\";\n\n    return description;\n}\n\nstatic void nfc_cli_mfu_info_print_common(const MfUltralightData* data) {\n    FuriString* str = furi_string_alloc();\n\n    printf(ANSI_FG_GREEN \"\\r\\n\\tTag information\\r\\n\" ANSI_RESET);\n    printf(\n        \"Type: \" ANSI_FG_YELLOW \"%s\\r\\n\" ANSI_RESET,\n        mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull));\n\n    nfc_cli_mfu_info_get_vendor(data->iso14443_3a_data->uid[0], str);\n    printf(\"Vendor ID: %s\\r\\n\", furi_string_get_cstr(str));\n\n    furi_string_reset(str);\n    nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, str);\n    printf(\"%s\\r\\n\", furi_string_get_cstr(str));\n    printf(\"BCC0: %02X\\r\\nBCC1: %02X\\r\\n\", data->page[0].data[3], data->page[2].data[0]);\n\n    furi_string_free(str);\n}\n\nstatic void nfc_cli_mfu_info_print_ndef(const MfUltralightData* data) {\n    const MfUltralightCapabilityContainer* cc =\n        (const MfUltralightCapabilityContainer*)data->page[3].data;\n    if(cc->magic == 0xE1) {\n        printf(ANSI_FG_GREEN \"\\r\\n\\tNDEF Message\\r\\n\" ANSI_RESET);\n        nfc_cli_printf_array(data->page[3].data, 4, \"Capability container: \");\n        printf(\n            \"\\r\\nMagic number: %02X\\r\\nVersion %d.%d\\r\\nSize: [%02X] - %d bytes\\r\\n\",\n            cc->magic,\n            cc->version.major,\n            cc->version.minor,\n            cc->size,\n            cc->size * 8);\n        printf(\n            \"Access read: [%02X] - %s\",\n            cc->access.read,\n            nfc_cli_mfu_capability_container_get_access_description(cc->access.read, true));\n        printf(\n            \"Access write: [%02X] - %s\",\n            cc->access.write,\n            nfc_cli_mfu_capability_container_get_access_description(cc->access.write, false));\n    }\n}\n\nstatic void nfc_cli_mfu_info_print_counter(const MfUltralightData* data) {\n    uint32_t features = mf_ultralight_get_feature_support_set(data->type);\n    if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadCounter)) return;\n\n    printf(ANSI_FG_GREEN \"\\r\\n\\n\\tTag counters\\r\\n\" ANSI_RESET);\n    uint8_t i =\n        mf_ultralight_support_feature(features, MfUltralightFeatureSupportSingleCounter) ? 2 : 0;\n\n    for(; i < MF_ULTRALIGHT_COUNTER_NUM; i++) {\n        printf(\"Counter [%d]: \", i);\n        nfc_cli_printf_array(data->counter[i].data, MF_ULTRALIGHT_COUNTER_SIZE, \"\");\n        printf(\" Value: %lu\\r\\n\", data->counter[i].counter);\n\n        const uint8_t tf = data->tearing_flag[i].data;\n        printf(\n            \"Tearing [%d]: [%02X] %s\",\n            i,\n            tf,\n            tf == MF_ULTRALIGHT_TEARING_FLAG_DEFAULT ? \"(ok)\" : \"\");\n    }\n}\n\nstatic void nfc_cli_mfu_info_print_signature(const MfUltralightData* data) {\n    uint32_t features = mf_ultralight_get_feature_support_set(data->type);\n    if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadSignature)) return;\n\n    const MfUltralightSignature* signature = &data->signature;\n    printf(ANSI_FG_GREEN \"\\r\\n\\n\\tTag signature\\r\\n\" ANSI_RESET);\n    nfc_cli_printf_array(signature->data, sizeof(signature->data), \"ECC signature: \");\n}\n\nstatic void nfc_cli_mfu_info_print_version_storage_size(uint8_t storage_size) {\n    uint16_t max_size = 1 << ((storage_size >> 1) + 1);\n    uint16_t min_exact_size = 1 << (storage_size >> 1);\n\n    bool exact_size = !(storage_size & 0x01);\n    if(exact_size)\n        printf(\"[%02X], (%u bytes)\", storage_size, min_exact_size);\n    else\n        printf(\"[%02X], (%u <-> %u bytes)\", storage_size, min_exact_size, max_size);\n}\n\nstatic void nfc_cli_mfu_info_print_version(const MfUltralightData* data) {\n    uint32_t features = mf_ultralight_get_feature_support_set(data->type);\n    if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadVersion)) return;\n\n    const MfUltralightVersion* version = &data->version;\n    printf(ANSI_FG_GREEN \"\\r\\n\\n\\tTag Version\\r\\n\" ANSI_RESET);\n    nfc_cli_printf_array((uint8_t*)version, sizeof(MfUltralightVersion), \"Raw bytes: \");\n\n    FuriString* str = furi_string_alloc();\n    nfc_cli_mfu_info_get_vendor(version->vendor_id, str);\n    printf(\"\\r\\nVendor ID: %s\\r\\n\", furi_string_get_cstr(str));\n    furi_string_free(str);\n\n    printf(\"Product type: %02X\\r\\n\", version->prod_type);\n\n    printf(\n        \"Protocol type: %02X%s\\r\\n\",\n        version->protocol_type,\n        (version->protocol_type == 0x3) ? \", ISO14443-3 Compliant\" : \"\");\n\n    printf(\n        \"Product subtype: [%02X], %s\\r\\n\",\n        version->prod_subtype,\n        (version->prod_subtype == 1) ? \"17 pF\" : \"50pF\");\n    printf(\n        \"Major version: %02X\\r\\nMinor version: %02X\\r\\nSize: \",\n        version->prod_ver_major,\n        version->prod_ver_minor);\n    nfc_cli_mfu_info_print_version_storage_size(version->storage_size);\n}\n\nNfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc) {\n    NfcCliMfuContext* instance = malloc(sizeof(NfcCliMfuContext));\n    instance->nfc = nfc;\n    instance->data = mf_ultralight_alloc();\n    return instance;\n}\n\nvoid nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx) {\n    NfcCliMfuContext* instance = ctx;\n    mf_ultralight_free(instance->data);\n    free(instance);\n}\n\nvoid nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx) {\n    furi_assert(pipe);\n    furi_assert(ctx);\n\n    NfcCliMfuContext* instance = ctx;\n\n    MfUltralightError error =\n        mf_ultralight_poller_sync_read_card(instance->nfc, instance->data, NULL);\n    if(error == MfUltralightErrorNone) {\n        const MfUltralightData* data = instance->data;\n        nfc_cli_mfu_info_print_common(data);\n        nfc_cli_mfu_info_print_ndef(data);\n        nfc_cli_mfu_info_print_counter(data);\n        nfc_cli_mfu_info_print_signature(data);\n        nfc_cli_mfu_info_print_version(data);\n    } else {\n        printf(ANSI_FG_RED \"Error: %s\" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nNfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc);\nvoid nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx);\nvoid nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.c",
    "content": "#include \"nfc_cli_action_rdbl.h\"\n\n#include \"../helpers/nfc_cli_format.h\"\n\n#include <furi.h>\n#include <toolbox/strint.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>\n\n#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)\n\ntypedef struct {\n    Nfc* nfc;\n    uint16_t block;\n} NfcCliMfuRdblContext;\n\nNfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliMfuRdblContext* instance = malloc(sizeof(NfcCliMfuRdblContext));\n    instance->nfc = nfc;\n    return instance;\n}\n\nvoid nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliMfuRdblContext* instance = ctx;\n    free(instance);\n}\n\nvoid nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {\n    furi_assert(pipe);\n\n    NfcCliMfuRdblContext* instance = ctx;\n    MfUltralightPage page = {0};\n\n    MfUltralightError error =\n        mf_ultralight_poller_sync_read_page(instance->nfc, instance->block, &page);\n\n    if(error == MfUltralightErrorNone) {\n        printf(\"\\r\\nBlock: %d \", instance->block);\n        nfc_cli_printf_array(page.data, sizeof(MfUltralightPage), \"Data: \");\n        printf(\"\\r\\n\");\n    } else {\n        printf(ANSI_FG_RED \"Error: %s\" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));\n    }\n}\n\nbool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* output) {\n    NfcCliMfuRdblContext* ctx = output;\n\n    StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);\n    return err == StrintParseNoError;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nNfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc);\nvoid nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx);\nvoid nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);\nbool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* ctx);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.c",
    "content": "#include \"nfc_cli_action_rdbl.h\"\n\n#include \"../helpers/nfc_cli_format.h\"\n\n#include <furi.h>\n#include <toolbox/args.h>\n#include <toolbox/strint.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>\n\n#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)\n\ntypedef struct {\n    Nfc* nfc;\n    uint16_t block;\n    MfUltralightPage page;\n} NfcCliMfuWrblContext;\n\nNfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliMfuWrblContext* instance = malloc(sizeof(NfcCliMfuWrblContext));\n    instance->nfc = nfc;\n    return instance;\n}\n\nvoid nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliMfuWrblContext* instance = ctx;\n    free(instance);\n}\n\nvoid nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {\n    furi_assert(pipe);\n\n    NfcCliMfuWrblContext* instance = ctx;\n\n    MfUltralightError error =\n        mf_ultralight_poller_sync_write_page(instance->nfc, instance->block, &instance->page);\n\n    if(error == MfUltralightErrorNone) {\n        printf(ANSI_FG_BR_GREEN \"\\r\\nSuccess\\r\\n\" ANSI_RESET);\n        printf(\"Block: %d \", instance->block);\n        nfc_cli_printf_array(instance->page.data, sizeof(MfUltralightPage), \"Data: \");\n        printf(\"\\r\\n\");\n    } else {\n        printf(ANSI_FG_RED \"Error: %s\" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));\n    }\n}\n\nbool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* output) {\n    NfcCliMfuWrblContext* ctx = output;\n\n    StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);\n    return err == StrintParseNoError;\n}\n\nbool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output) {\n    NfcCliMfuWrblContext* ctx = output;\n\n    bool result = false;\n    do {\n        size_t len = furi_string_size(value);\n        if(len % 2 != 0) break;\n\n        size_t data_length = len / 2;\n        if(data_length != MF_ULTRALIGHT_PAGE_SIZE) break;\n\n        result = args_read_hex_bytes(value, ctx->page.data, data_length);\n    } while(false);\n\n    return result;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nNfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc);\nvoid nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx);\nvoid nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);\nbool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* ctx);\nbool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.c",
    "content": "#include \"nfc_cli_command_mfu.h\"\n#include \"nfc_cli_action_info.h\"\n#include \"nfc_cli_action_rdbl.h\"\n#include \"nfc_cli_action_wrbl.h\"\n\n#define TAG \"MFU\"\n\n//mfu info\nconst NfcCliActionDescriptor info_action = {\n    .name = \"info\",\n    .description = \"Get basic information about the card\",\n    .alloc = nfc_cli_mfu_info_alloc_ctx,\n    .free = nfc_cli_mfu_info_free_ctx,\n    .execute = nfc_cli_mfu_info_execute,\n    .key_count = 0,\n    .keys = NULL,\n};\n\nconst NfcCliKeyDescriptor rdbl_action_keys[] = {\n    {\n        .short_name = \"b\",\n        .long_name = \"block\",\n        .features = {.required = true, .parameter = true},\n        .description = \"desired block number\",\n        .parse = nfc_cli_mfu_rdbl_parse_block,\n    },\n};\n\n//mfu rdbl -b 0\n//mfu rdbl --block 0\nconst NfcCliActionDescriptor rdbl_action = {\n    .name = \"rdbl\",\n    .description = \"Read block from ultralight card\",\n    .alloc = nfc_cli_mfu_rdbl_alloc_ctx,\n    .free = nfc_cli_mfu_rdbl_free_ctx,\n    .execute = nfc_cli_mfu_rdbl_execute,\n    .key_count = COUNT_OF(rdbl_action_keys),\n    .keys = rdbl_action_keys,\n};\n\nconst NfcCliKeyDescriptor wrbl_action_keys[] = {\n    {\n        .short_name = \"b\",\n        .long_name = \"block\",\n        .features = {.required = true, .parameter = true},\n        .description = \"desired block number\",\n        .parse = nfc_cli_mfu_wrbl_parse_block,\n    },\n    {\n        .short_name = \"d\",\n        .long_name = \"data\",\n        .features = {.required = true, .parameter = true},\n        .description = \"new data for block\",\n        .parse = nfc_cli_mfu_wrbl_parse_data,\n    },\n};\n\n//mfu wrbl -b 0 -d DEADBEAF\n//mfu rdbl --block 0 -- data DEADBEEF\nconst NfcCliActionDescriptor wrbl_action = {\n    .name = \"wrbl\",\n    .description = \"Write block to ultralight card\",\n    .alloc = nfc_cli_mfu_wrbl_alloc_ctx,\n    .free = nfc_cli_mfu_wrbl_free_ctx,\n    .execute = nfc_cli_mfu_wrbl_execute,\n    .key_count = COUNT_OF(wrbl_action_keys),\n    .keys = wrbl_action_keys,\n};\n\nconst NfcCliActionDescriptor* mfu_actions[] = {\n    &rdbl_action,\n    &info_action,\n    &wrbl_action,\n};\n\n//Command descriptor\nADD_NFC_CLI_COMMAND(mfu, \"Mifare Ultralight specific commands\", mfu_actions);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor mfu_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_emulate.c",
    "content": "#include \"nfc_cli_command_emulate.h\"\n#include \"helpers/nfc_cli_format.h\"\n\n#include <nfc.h>\n#include <nfc_listener.h>\n#include <nfc_device.h>\n\n#include <storage/storage.h>\n\ntypedef struct {\n    Nfc* nfc;\n    NfcDevice* nfc_device;\n    FuriString* file_path;\n    Storage* storage;\n} NfcCliEmulateContext;\n\nstatic NfcCliActionContext* nfc_cli_emulate_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliEmulateContext* instance = malloc(sizeof(NfcCliEmulateContext));\n    instance->nfc = nfc;\n    instance->file_path = furi_string_alloc();\n    instance->nfc_device = nfc_device_alloc();\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    return instance;\n}\n\nstatic void nfc_cli_emulate_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliEmulateContext* instance = ctx;\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(instance->file_path);\n    nfc_device_free(instance->nfc_device);\n    free(instance);\n}\n\nstatic const NfcProtocol supported_protocols[] = {\n    NfcProtocolIso14443_3a,\n    NfcProtocolIso14443_4a,\n    NfcProtocolIso15693_3,\n    NfcProtocolMfUltralight,\n    NfcProtocolMfClassic,\n    NfcProtocolSlix,\n    NfcProtocolFelica,\n};\n\nstatic bool nfc_cli_emulate_protocol_supports_emulation(NfcProtocol protocol) {\n    for(size_t i = 0; i < COUNT_OF(supported_protocols); i++) {\n        if(supported_protocols[i] == protocol) return true;\n    }\n    return false;\n}\n\nstatic void nfc_cli_emulate_execute(PipeSide* pipe, NfcCliActionContext* context) {\n    UNUSED(pipe);\n    furi_assert(context);\n    NfcCliEmulateContext* instance = context;\n    do {\n        const char* path = furi_string_get_cstr(instance->file_path);\n        if(!storage_common_exists(instance->storage, path)) {\n            printf(ANSI_FG_RED \"Wrong path \\'%s\\'.\\r\\n\" ANSI_RESET, path);\n            break;\n        }\n\n        if(!nfc_device_load(instance->nfc_device, path)) {\n            printf(ANSI_FG_RED \"Failed to load \\'%s\\'.\\r\\n\" ANSI_RESET, path);\n            break;\n        }\n\n        const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n\n        if(!nfc_cli_emulate_protocol_supports_emulation(protocol)) {\n            printf(\n                ANSI_FG_RED \"Error. Emulation for %s is not supported\\r\\n\" ANSI_RESET,\n                nfc_cli_get_protocol_name(protocol));\n            break;\n        }\n\n        const NfcDeviceData* data = nfc_device_get_data(instance->nfc_device, protocol);\n        NfcListener* listener = nfc_listener_alloc(instance->nfc, protocol, data);\n\n        nfc_listener_start(listener, NULL, NULL);\n        printf(\"\\r\\nEmulating. Press Ctrl+C to abort\\r\\n\");\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(100);\n        }\n        nfc_listener_stop(listener);\n        nfc_listener_free(listener);\n    } while(false);\n}\n\nstatic bool nfc_cli_emulate_parse_filename_key(FuriString* value, void* output) {\n    furi_assert(value);\n    furi_assert(output);\n    NfcCliEmulateContext* ctx = output;\n    furi_string_set(ctx->file_path, value);\n    return true;\n}\n\nconst NfcCliKeyDescriptor emulate_keys[] = {\n    {\n        .features = {.required = true, .parameter = true},\n        .long_name = \"file\",\n        .short_name = \"f\",\n        .description = \"path to new file\",\n        .parse = nfc_cli_emulate_parse_filename_key,\n    },\n};\n\nconst NfcCliActionDescriptor emulate_action = {\n    .name = \"emulate\",\n    .description = \"Emulate .nfc file content\",\n    .alloc = nfc_cli_emulate_alloc_ctx,\n    .free = nfc_cli_emulate_free_ctx,\n    .execute = nfc_cli_emulate_execute,\n    .key_count = COUNT_OF(emulate_keys),\n    .keys = emulate_keys,\n};\n\nconst NfcCliActionDescriptor* emulate_actions_collection[] = {&emulate_action};\n\n//Command descriptor\nADD_NFC_CLI_COMMAND(emulate, \"\", emulate_actions_collection);\n\n//Command usage: emulate [-f <file>]\n//Command examples:\n//emulate -f ext/nfc/test.nfc\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_emulate.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor emulate_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_field.c",
    "content": "#include \"nfc_cli_command_field.h\"\n\n#include <furi_hal_nfc.h>\n\nstatic void nfc_cli_field(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(args);\n    UNUSED(context);\n\n    furi_hal_nfc_low_power_mode_stop();\n    furi_hal_nfc_poller_field_on();\n\n    printf(\"Field is on. Don't leave device in this mode for too long.\\r\\n\");\n    printf(\"Press Ctrl+C to abort\\r\\n\");\n\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        furi_delay_ms(50);\n    }\n\n    furi_hal_nfc_low_power_mode_start();\n}\n\nconst NfcCliCommandDescriptor field_cmd = {\n    .name = \"field\",\n    .description = \"Turns NFC field on\",\n    .callback = nfc_cli_field,\n    .action_count = 0,\n    .actions = NULL,\n};\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_field.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor field_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_scanner.c",
    "content": "\n#include \"nfc_cli_command_scanner.h\"\n#include \"helpers/nfc_cli_scanner.h\"\n#include \"helpers/nfc_cli_format.h\"\n\ntypedef struct {\n    NfcCliScanner* scanner;\n    bool display_tree;\n} NfcCliCmdScannerContext;\n\nstatic NfcCliActionContext* nfc_cli_command_scanner_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliCmdScannerContext* instance = malloc(sizeof(NfcCliCmdScannerContext));\n    instance->scanner = nfc_cli_scanner_alloc(nfc);\n    instance->display_tree = false;\n\n    return instance;\n}\n\nstatic void nfc_cli_command_scanner_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliCmdScannerContext* instance = ctx;\n    nfc_cli_scanner_free(instance->scanner);\n\n    free(instance);\n}\n\nstatic void\n    nfc_cli_command_scanner_format_protocol_tree(NfcProtocol protocol, FuriString* output) {\n    const char* names[10] = {0};\n    uint8_t cnt = 0;\n    while(protocol != NfcProtocolInvalid) {\n        names[cnt++] = nfc_cli_get_protocol_name(protocol);\n        protocol = nfc_protocol_get_parent(protocol);\n    }\n\n    for(int8_t i = cnt - 1; i >= 0; i--) {\n        furi_string_cat_printf(output, (i == 0) ? \"%s\" : \"%s -> \", names[i]);\n    }\n}\n\nstatic void nfc_cli_command_scanner_format_detected_protocols(NfcCliScanner* instance) {\n    FuriString* str = furi_string_alloc();\n    printf(\"Protocols detected: \\r\\n\");\n    for(size_t i = 0; i < nfc_cli_scanner_detected_protocol_num(instance); i++) {\n        furi_string_reset(str);\n        NfcProtocol protocol = nfc_cli_scanner_get_protocol(instance, i);\n        nfc_cli_command_scanner_format_protocol_tree(protocol, str);\n        printf(\"Protocol [%zu]: %s\\r\\n\", i + 1, furi_string_get_cstr(str));\n    }\n    furi_string_free(str);\n}\n\nstatic void nfc_cli_command_scanner_execute(PipeSide* pipe, void* context) {\n    NfcCliCmdScannerContext* instance = context;\n\n    printf(\"Press Ctrl+C to abort\\r\\n\\n\");\n    nfc_cli_scanner_begin_scan(instance->scanner);\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) &&\n          !nfc_cli_scanner_wait_scan(instance->scanner, 50))\n        ;\n    nfc_cli_scanner_end_scan(instance->scanner);\n\n    if(!instance->display_tree)\n        nfc_cli_scanner_list_detected_protocols(instance->scanner);\n    else\n        nfc_cli_command_scanner_format_detected_protocols(instance->scanner);\n}\n\nstatic bool nfc_cli_command_scanner_parse_tree(FuriString* value, void* output) {\n    UNUSED(value);\n    NfcCliCmdScannerContext* ctx = output;\n    ctx->display_tree = true;\n    return true;\n}\n\nconst NfcCliKeyDescriptor tree_key = {\n    .short_name = \"t\",\n    .long_name = \"tree\",\n    .features = {.parameter = false, .required = false, .multivalue = false},\n    .description = \"displays protocol hierarchy for each detected protocol\",\n    .parse = nfc_cli_command_scanner_parse_tree,\n};\n\nconst NfcCliActionDescriptor scanner_action = {\n    .name = \"scanner\",\n    .description = \"Detect tag type\",\n    .key_count = 1,\n    .keys = &tree_key,\n    .execute = nfc_cli_command_scanner_execute,\n    .alloc = nfc_cli_command_scanner_alloc_ctx,\n    .free = nfc_cli_command_scanner_free_ctx,\n};\n\nconst NfcCliActionDescriptor* scanner_actions_collection[] = {&scanner_action};\n\nADD_NFC_CLI_COMMAND(scanner, \"\", scanner_actions_collection);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/nfc_cli_command_scanner.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor scanner_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.c",
    "content": "#include \"nfc_cli_command_raw.h\"\n#include \"../helpers/nfc_cli_format.h\"\n#include \"../helpers/nfc_cli_protocol_parser.h\"\n\n#include \"protocol_handlers/nfc_cli_raw_common_types.h\"\n#include \"protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.h\"\n#include \"protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.h\"\n#include \"protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.h\"\n#include \"protocol_handlers/felica/nfc_cli_raw_felica.h\"\n\n#include <toolbox/args.h>\n\n#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)\n\n#define TAG \"RAW\"\n\ntypedef enum {\n    NfcCliProtocolRequestTypeNormalExecute,\n    NfcCliProtocolRequestTypeAbort,\n} NfcCliProtocolRequestType;\n\ntypedef enum {\n    NfcPollerStateStopped,\n    NfcPollerStateStarted,\n} NfcPollerState;\n\ntypedef NfcCommand (*NfcCliRawProtocolSpecificHandler)(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response);\n\ntypedef struct {\n    Nfc* nfc;\n    NfcCliRawRequest request;\n    NfcCliRawResponse response;\n\n    NfcPoller* poller;\n    NfcPollerState poller_state;\n\n    NfcCliProtocolRequestType request_type;\n    FuriMessageQueue* input_queue;\n    FuriSemaphore* sem_done;\n\n} NfcCliRawCmdContext;\n\nstatic const char* raw_error_names[] = {\n    [NfcCliRawErrorNone] = \"None\",\n    [NfcCliRawErrorTimeout] = \"Timeout\",\n    [NfcCliRawErrorProtocol] = \"Internal protocol\",\n    [NfcCliRawErrorWrongCrc] = \"Wrong CRC\",\n    [NfcCliRawErrorNotPresent] = \"No card\",\n};\n\nstatic NfcCliActionContext* nfc_cli_raw_alloc_ctx(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliRawCmdContext* instance = malloc(sizeof(NfcCliRawCmdContext));\n    instance->nfc = nfc;\n\n    instance->request.protocol = NfcProtocolInvalid;\n\n    instance->request.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);\n    instance->response.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);\n\n    instance->input_queue = furi_message_queue_alloc(5, sizeof(NfcCliProtocolRequestType));\n    instance->sem_done = furi_semaphore_alloc(1, 0);\n    instance->response.activation_string = furi_string_alloc();\n    instance->request.timeout = 0;\n    return instance;\n}\n\nstatic void nfc_cli_raw_abort_nfc_thread(NfcCliRawCmdContext* instance) {\n    if(instance->poller_state == NfcPollerStateStarted) {\n        instance->request_type = NfcCliProtocolRequestTypeAbort;\n        furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);\n        furi_semaphore_acquire(instance->sem_done, FuriWaitForever);\n        instance->poller_state = NfcPollerStateStopped;\n    }\n    if(instance->poller) nfc_poller_stop(instance->poller);\n}\n\nstatic void nfc_cli_raw_free_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliRawCmdContext* instance = ctx;\n\n    nfc_cli_raw_abort_nfc_thread(instance);\n    if(instance->poller) nfc_poller_free(instance->poller);\n\n    furi_message_queue_free(instance->input_queue);\n    furi_semaphore_free(instance->sem_done);\n\n    furi_string_free(instance->response.activation_string);\n    bit_buffer_free(instance->response.rx_buffer);\n    bit_buffer_free(instance->request.tx_buffer);\n    instance->nfc = NULL;\n    free(instance);\n}\n\nstatic bool nfc_cli_raw_can_reuse_ctx(NfcCliActionContext* ctx) {\n    furi_assert(ctx);\n    NfcCliRawCmdContext* instance = ctx;\n    NfcCliRawRequest* request = &instance->request;\n\n    bool result = request->keep_field;\n    request->keep_field = false;\n    request->append_crc = false;\n    request->select = false;\n    instance->request.timeout = 0;\n    return result;\n}\n\nconst NfcCliRawProtocolSpecificHandler nfc_cli_raw_protocol_handlers[] = {\n    [NfcProtocolIso14443_3a] = nfc_cli_raw_iso14443_3a_handler,\n    [NfcProtocolIso14443_3b] = nfc_cli_raw_iso14443_3b_handler,\n    [NfcProtocolIso14443_4a] = NULL,\n    [NfcProtocolIso14443_4b] = NULL,\n    [NfcProtocolIso15693_3] = nfc_cli_raw_iso15693_3_handler,\n    [NfcProtocolFelica] = nfc_cli_raw_felica_handler,\n    [NfcProtocolMfUltralight] = NULL,\n    [NfcProtocolMfClassic] = NULL,\n    [NfcProtocolMfDesfire] = NULL,\n    [NfcProtocolSlix] = NULL,\n    [NfcProtocolSt25tb] = NULL,\n};\n\nstatic NfcCommand nfc_cli_raw_poller_callback(NfcGenericEventEx event, void* context) {\n    NfcEvent* nfc_event = event.parent_event_data;\n    NfcCliRawCmdContext* instance = context;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        FURI_LOG_D(TAG, \"Poller callback\");\n        NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;\n        furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);\n\n        if(request_type == NfcCliProtocolRequestTypeAbort) {\n            command = NfcCommandStop;\n        } else {\n            const NfcCliRawProtocolSpecificHandler handler =\n                nfc_cli_raw_protocol_handlers[instance->request.protocol];\n            if(handler) handler(event.poller, &instance->request, &instance->response);\n        }\n    }\n    furi_semaphore_release(instance->sem_done);\n    if(command == NfcCommandStop) {\n        FURI_LOG_D(TAG, \"Aborting poller callback\");\n        instance->poller_state = NfcPollerStateStopped;\n    }\n    return command;\n}\n\nstatic inline void nfc_cli_raw_print_result(const NfcCliRawCmdContext* instance) {\n    if(!furi_string_empty(instance->response.activation_string))\n        printf(\"%s\\r\\n\", furi_string_get_cstr(instance->response.activation_string));\n\n    nfc_cli_printf_array(\n        bit_buffer_get_data(instance->request.tx_buffer),\n        bit_buffer_get_size_bytes(instance->request.tx_buffer),\n        \"Tx: \");\n\n    if(instance->response.result != NfcCliRawErrorNone)\n        printf(\"\\r\\nError: \\\"%s\\\"\\r\\n\", raw_error_names[instance->response.result]);\n\n    size_t rx_size = bit_buffer_get_size_bytes(instance->response.rx_buffer);\n    if(rx_size > 0) {\n        nfc_cli_printf_array(\n            bit_buffer_get_data(instance->response.rx_buffer),\n            bit_buffer_get_size_bytes(instance->response.rx_buffer),\n            \"\\r\\nRx: \");\n    }\n}\n\nstatic void nfc_cli_raw_execute(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    furi_assert(context);\n    NfcCliRawCmdContext* instance = context;\n\n    furi_string_reset(instance->response.activation_string);\n\n    if(instance->poller_state == NfcPollerStateStopped) {\n        if(instance->poller == NULL)\n            instance->poller = nfc_poller_alloc(instance->nfc, instance->request.protocol);\n\n        nfc_poller_start_ex(instance->poller, nfc_cli_raw_poller_callback, instance);\n        instance->poller_state = NfcPollerStateStarted;\n    }\n\n    instance->request_type = NfcCliProtocolRequestTypeNormalExecute;\n    furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);\n    furi_semaphore_acquire(instance->sem_done, FuriWaitForever);\n\n    nfc_cli_raw_print_result(instance);\n}\n\nstatic const NfcProtocolNameValuePair supported_protocols[] = {\n    {.name = \"14a\", .value = NfcProtocolIso14443_3a},\n    {.name = \"iso14a\", .value = NfcProtocolIso14443_3a},\n\n    {.name = \"14b\", .value = NfcProtocolIso14443_3b},\n    {.name = \"iso14b\", .value = NfcProtocolIso14443_3b},\n\n    {.name = \"15\", .value = NfcProtocolIso15693_3},\n    {.name = \"felica\", .value = NfcProtocolFelica},\n};\n\nstatic bool nfc_cli_raw_parse_protocol(FuriString* value, void* output) {\n    NfcCliRawCmdContext* ctx = output;\n    NfcProtocol new_protocol = NfcProtocolInvalid;\n\n    NfcCliProtocolParser* parser =\n        nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));\n\n    bool result = nfc_cli_protocol_parser_get(parser, value, &new_protocol);\n\n    nfc_cli_protocol_parser_free(parser);\n\n    if(result && ctx->request.protocol != NfcProtocolInvalid &&\n       ctx->request.protocol != new_protocol) {\n        printf(\n            ANSI_FG_RED \"Error: previous %s != new %s. Unable to continue.\" ANSI_RESET,\n            nfc_cli_get_protocol_name(ctx->request.protocol),\n            nfc_cli_get_protocol_name(new_protocol));\n        result = false;\n    }\n\n    if(result) {\n        ctx->request.protocol = new_protocol;\n    }\n    return result;\n}\n\nstatic bool nfc_cli_raw_parse_data(FuriString* value, void* output) {\n    NfcCliRawCmdContext* ctx = output;\n\n    bool result = false;\n    do {\n        size_t len = furi_string_size(value);\n        if(len % 2 != 0) break;\n\n        size_t data_length = len / 2;\n        uint8_t* data = malloc(data_length);\n\n        if(args_read_hex_bytes(value, data, data_length)) {\n            bit_buffer_reset(ctx->request.tx_buffer);\n            bit_buffer_copy_bytes(ctx->request.tx_buffer, data, data_length);\n            result = true;\n        }\n\n        free(data);\n    } while(false);\n\n    return result;\n}\n\nstatic bool nfc_cli_raw_parse_timeout(FuriString* value, void* output) {\n    furi_assert(value);\n    furi_assert(output);\n    NfcCliRawCmdContext* ctx = output;\n\n    bool result = false;\n\n    int timeout = 0;\n    if(args_read_int_and_trim(value, &timeout)) {\n        ctx->request.timeout = timeout;\n        result = true;\n    }\n    return result;\n}\n\nstatic bool nfc_cli_raw_parse_select(FuriString* value, void* output) {\n    UNUSED(value);\n    NfcCliRawCmdContext* ctx = output;\n    ctx->request.select = true;\n    return true;\n}\n\nstatic bool nfc_cli_raw_parse_crc(FuriString* value, void* output) {\n    UNUSED(value);\n    NfcCliRawCmdContext* ctx = output;\n    ctx->request.append_crc = true;\n    return true;\n}\n\nstatic bool nfc_cli_raw_parse_keep(FuriString* value, void* output) {\n    UNUSED(value);\n    NfcCliRawCmdContext* ctx = output;\n    ctx->request.keep_field = true;\n    return true;\n}\n\nconst NfcCliKeyDescriptor raw_action_keys[] = {\n    {\n        .long_name = NULL,\n        .short_name = \"t\",\n        .features = {.parameter = true, .required = false},\n        .description = \"timeout in fc\",\n        .parse = nfc_cli_raw_parse_timeout,\n    },\n    {\n        .long_name = NULL,\n        .short_name = \"k\",\n        .description = \"keep signal field ON after receive\",\n        .parse = nfc_cli_raw_parse_keep,\n    },\n    {\n        .long_name = NULL,\n        .short_name = \"c\",\n        .description = \"calculate and append CRC\",\n        .parse = nfc_cli_raw_parse_crc,\n    },\n    {\n        .long_name = NULL,\n        .short_name = \"s\",\n        .description = \"Select on FieldOn\",\n        .parse = nfc_cli_raw_parse_select,\n    },\n    {\n        .long_name = \"protocol\",\n        .short_name = \"p\",\n        .description = \"desired protocol. Possible values: 14a, iso14a, 14b, iso14b, 15, felica\",\n        .features = {.parameter = true, .required = true},\n        .parse = nfc_cli_raw_parse_protocol,\n    },\n    {\n        .long_name = \"data\",\n        .short_name = \"d\",\n        .description = \"Raw bytes to send in HEX format\",\n        .features = {.parameter = true, .required = true},\n        .parse = nfc_cli_raw_parse_data,\n    },\n};\n\nconst NfcCliActionDescriptor raw_action = {\n    .name = \"raw\",\n    .description = \"Sends raw bytes using different protocols\",\n    .key_count = COUNT_OF(raw_action_keys),\n    .keys = raw_action_keys,\n    .execute = nfc_cli_raw_execute,\n    .alloc = nfc_cli_raw_alloc_ctx,\n    .free = nfc_cli_raw_free_ctx,\n    .can_reuse = nfc_cli_raw_can_reuse_ctx,\n};\n\nconst NfcCliActionDescriptor* raw_actions_collection[] = {&raw_action};\n\nADD_NFC_CLI_COMMAND(raw, \"\", raw_actions_collection);\n\n//Command usage: raw <protocol> [keys] <data>\n//Command examples:\n//raw iso14a -sc 3000\n//raw iso14a 3000\n//raw iso14a 3000 -sc\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.h",
    "content": "#pragma once\n\n#include \"../../nfc_cli_command_base_i.h\"\n\nextern const NfcCliCommandDescriptor raw_cmd;\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/felica/nfc_cli_raw_felica.c",
    "content": "#include \"nfc_cli_raw_felica.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/helpers/felica_crc.h>\n#include <nfc/protocols/felica/felica.h>\n#include <nfc/protocols/felica/felica_poller_i.h>\n\n#define TAG \"FELICA\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic inline void felica_format_activation_data(const FelicaData* data, FuriString* output) {\n    nfc_cli_format_array(data->idm.data, FELICA_IDM_SIZE, \"IDm: \", output);\n    nfc_cli_format_array(data->pmm.data, FELICA_PMM_SIZE, \" PMm: \", output);\n}\n\nstatic NfcCliRawError nfc_cli_raw_felica_process_error(FelicaError error) {\n    switch(error) {\n    case FelicaErrorNone:\n        return NfcCliRawErrorNone;\n    case FelicaErrorTimeout:\n        return NfcCliRawErrorTimeout;\n    case FelicaErrorWrongCrc:\n        return NfcCliRawErrorWrongCrc;\n    case FelicaErrorNotPresent:\n        return NfcCliRawErrorNotPresent;\n    default:\n        return NfcCliRawErrorProtocol;\n    }\n}\n\nstatic FelicaError nfc_cli_raw_felica_poller_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return FelicaErrorNone;\n    case NfcErrorTimeout:\n        return FelicaErrorTimeout;\n    default:\n        return FelicaErrorNotPresent;\n    }\n}\n\nstatic inline NfcCliRawError\n    nfc_cli_raw_felica_activate(NfcGenericInstance* poller, FuriString* activation_string) {\n    FelicaData felica_data = {};\n    FelicaPoller* felica_poller = poller;\n    FURI_LOG_D(TAG, \"Activating...\");\n\n    FelicaError error = felica_poller_activate(felica_poller, &felica_data);\n    if(error == FelicaErrorNone) {\n        felica_format_activation_data(&felica_data, activation_string);\n    }\n\n    return nfc_cli_raw_felica_process_error(error);\n}\n\nstatic inline NfcCliRawError nfc_cli_raw_felica_txrx(\n    NfcGenericInstance* poller,\n    BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t timeout) {\n    FURI_LOG_D(TAG, \"TxRx\");\n    FelicaPoller* felica_poller = poller;\n\n    bit_buffer_reset(rx_buffer);\n\n    FelicaError error = FelicaErrorNone;\n\n    NfcError nfc_error = nfc_poller_trx(felica_poller->nfc, tx_buffer, rx_buffer, timeout);\n    if(nfc_error != NfcErrorNone) {\n        error = nfc_cli_raw_felica_poller_process_error(nfc_error);\n    } else if(!felica_crc_check(rx_buffer)) {\n        error = FelicaErrorWrongCrc;\n    }\n\n    return nfc_cli_raw_felica_process_error(error);\n}\n\nNfcCommand nfc_cli_raw_felica_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response) {\n    do {\n        if(request->select) {\n            response->result = nfc_cli_raw_felica_activate(poller, response->activation_string);\n        }\n\n        if(response->result != NfcCliRawErrorNone) break;\n        if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;\n\n        if(request->append_crc) {\n            FURI_LOG_D(TAG, \"Add CRC\");\n            felica_crc_append(request->tx_buffer);\n        }\n\n        uint32_t timeout = request->timeout > 0 ? request->timeout : FELICA_FDT_POLL_FC;\n        response->result =\n            nfc_cli_raw_felica_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);\n    } while(false);\n    return request->keep_field ? NfcCommandContinue : NfcCommandStop;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/felica/nfc_cli_raw_felica.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_raw_common_types.h\"\n\nNfcCommand nfc_cli_raw_felica_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.c",
    "content": "#include \"nfc_cli_raw_iso14443_3a.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/helpers/iso14443_crc.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\n#define TAG \"ISO14A\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliRawError nfc_cli_raw_iso14443_3a_process_error(Iso14443_3aError error) {\n    switch(error) {\n    case Iso14443_3aErrorNone:\n        return NfcCliRawErrorNone;\n    case Iso14443_3aErrorTimeout:\n        return NfcCliRawErrorTimeout;\n    case Iso14443_3aErrorWrongCrc:\n        return NfcCliRawErrorWrongCrc;\n    case Iso14443_3aErrorNotPresent:\n        return NfcCliRawErrorNotPresent;\n    default:\n        return NfcCliRawErrorProtocol;\n    }\n}\n\nstatic void iso14443_3a_format_activation_data(const Iso14443_3aData* data, FuriString* output) {\n    nfc_cli_format_array(data->uid, data->uid_len, \"UID: \", output);\n    furi_string_cat_printf(\n        output, \" ATQA: %02X%02X SAK: %02X\", data->atqa[0], data->atqa[1], data->sak);\n}\n\nstatic inline NfcCliRawError\n    nfc_cli_raw_iso14443_3a_activate(NfcGenericInstance* poller, FuriString* activation_string) {\n    Iso14443_3aData iso3_data = {};\n    FURI_LOG_D(TAG, \"Activating...\");\n\n    Iso14443_3aError error = iso14443_3a_poller_activate(poller, &iso3_data);\n    if(error == Iso14443_3aErrorNone)\n        iso14443_3a_format_activation_data(&iso3_data, activation_string);\n\n    return nfc_cli_raw_iso14443_3a_process_error(error);\n}\n\nstatic inline NfcCliRawError nfc_cli_raw_iso14443_3a_txrx(\n    NfcGenericInstance* poller,\n    BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t timeout) {\n    FURI_LOG_D(TAG, \"TxRx\");\n    bit_buffer_reset(rx_buffer);\n    Iso14443_3aError error = iso14443_3a_poller_txrx(poller, tx_buffer, rx_buffer, timeout);\n    return nfc_cli_raw_iso14443_3a_process_error(error);\n}\n\nNfcCommand nfc_cli_raw_iso14443_3a_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response) {\n    do {\n        response->result = NfcCliRawErrorNone;\n        if(request->select) {\n            response->result =\n                nfc_cli_raw_iso14443_3a_activate(poller, response->activation_string);\n        }\n\n        if(response->result != NfcCliRawErrorNone) break;\n        if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;\n\n        if(request->append_crc) {\n            FURI_LOG_D(TAG, \"Add CRC\");\n            iso14443_crc_append(Iso14443CrcTypeA, request->tx_buffer);\n        }\n\n        uint32_t timeout = request->timeout > 0 ? request->timeout : ISO14443_3A_FDT_LISTEN_FC;\n        response->result =\n            nfc_cli_raw_iso14443_3a_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);\n    } while(false);\n\n    return request->keep_field ? NfcCommandContinue : NfcCommandStop;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_raw_common_types.h\"\n\nNfcCommand nfc_cli_raw_iso14443_3a_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.c",
    "content": "#include \"nfc_cli_raw_iso14443_3b.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/helpers/iso14443_crc.h>\n#include <nfc/protocols/iso14443_3b/iso14443_3b_i.h>\n#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>\n\n#define TAG \"ISO14B\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliRawError nfc_cli_raw_iso14443_3b_process_error(Iso14443_3bError error) {\n    switch(error) {\n    case Iso14443_3bErrorNone:\n        return NfcCliRawErrorNone;\n    case Iso14443_3bErrorTimeout:\n        return NfcCliRawErrorTimeout;\n    case Iso14443_3bErrorWrongCrc:\n        return NfcCliRawErrorWrongCrc;\n    case Iso14443_3bErrorNotPresent:\n        return NfcCliRawErrorNotPresent;\n    default:\n        return NfcCliRawErrorProtocol;\n    }\n}\n\nstatic Iso14443_3bError nfc_cli_raw_iso14443_3b_poller_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return Iso14443_3bErrorNone;\n    case NfcErrorTimeout:\n        return Iso14443_3bErrorTimeout;\n    default:\n        return Iso14443_3bErrorNotPresent;\n    }\n}\n\nstatic void iso14443_3b_format_activation_data(const Iso14443_3bData* data, FuriString* output) {\n    nfc_cli_format_array(data->uid, ISO14443_3B_UID_SIZE, \"UID: \", output);\n\n    const Iso14443_3bProtocolInfo* info = &data->protocol_info;\n    furi_string_cat_printf(\n        output,\n        \" BitRate: %d, Protocol: %d, Max Frame Size: %d, Fo: %d, Adc: %d, Fwi: %d\",\n        info->bit_rate_capability,\n        info->protocol_type,\n        info->max_frame_size,\n        info->fo,\n        info->adc,\n        info->fwi);\n}\n\nstatic inline NfcCliRawError nfc_cli_raw_iso14443_3b_activate(\n    NfcGenericInstance* poller,\n    Iso14443_3bData* iso3b_data,\n    FuriString* activation_string) {\n    FURI_LOG_D(TAG, \"Activating...\");\n\n    Iso14443_3bError error = iso14443_3b_poller_activate(poller, iso3b_data);\n    if(error == Iso14443_3bErrorNone)\n        iso14443_3b_format_activation_data(iso3b_data, activation_string);\n\n    return nfc_cli_raw_iso14443_3b_process_error(error);\n}\n\nstatic inline NfcCliRawError nfc_cli_raw_iso14443_3b_txrx(\n    NfcGenericInstance* poller,\n    BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t timeout) {\n    FURI_LOG_D(TAG, \"TxRx\");\n    Iso14443_3bPoller* iso14b_poller = poller;\n\n    bit_buffer_reset(rx_buffer);\n\n    Iso14443_3bError error = Iso14443_3bErrorNone;\n\n    NfcError nfc_error = nfc_poller_trx(iso14b_poller->nfc, tx_buffer, rx_buffer, timeout);\n    if(nfc_error != NfcErrorNone) {\n        error = nfc_cli_raw_iso14443_3b_poller_process_error(nfc_error);\n    } else if(!iso14443_crc_check(Iso14443CrcTypeB, rx_buffer)) {\n        error = Iso14443_3bErrorWrongCrc;\n    }\n\n    return nfc_cli_raw_iso14443_3b_process_error(error);\n}\n\nNfcCommand nfc_cli_raw_iso14443_3b_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response) {\n    Iso14443_3bData iso3b_data = {0};\n    bool activated = false;\n    do {\n        response->result = NfcCliRawErrorNone;\n        if(request->select) {\n            response->result =\n                nfc_cli_raw_iso14443_3b_activate(poller, &iso3b_data, response->activation_string);\n            activated = response->result == NfcCliRawErrorNone;\n        }\n\n        if(response->result != NfcCliRawErrorNone) break;\n        if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;\n\n        uint32_t timeout = ISO14443_3B_FDT_POLL_FC;\n        if(request->timeout > 0) {\n            timeout = request->timeout;\n        } else if(activated) {\n            timeout = iso14443_3b_get_fwt_fc_max(&iso3b_data);\n        }\n\n        if(request->append_crc) {\n            FURI_LOG_D(TAG, \"Add CRC\");\n            iso14443_crc_append(Iso14443CrcTypeB, request->tx_buffer);\n        }\n\n        response->result =\n            nfc_cli_raw_iso14443_3b_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);\n    } while(false);\n    return request->keep_field ? NfcCommandContinue : NfcCommandStop;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_raw_common_types.h\"\n\nNfcCommand nfc_cli_raw_iso14443_3b_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.c",
    "content": "#include \"nfc_cli_raw_iso15693_3.h\"\n#include \"../../../helpers/nfc_cli_format.h\"\n\n#include <nfc/helpers/iso13239_crc.h>\n#include <nfc/protocols/iso15693_3/iso15693_3.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>\n\n#define TAG \"ISO15\"\n\n#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))\n\nstatic NfcCliRawError nfc_cli_raw_iso15693_3_process_error(Iso15693_3Error error) {\n    switch(error) {\n    case Iso15693_3ErrorNone:\n        return NfcCliRawErrorNone;\n    case Iso15693_3ErrorTimeout:\n        return NfcCliRawErrorTimeout;\n    case Iso15693_3ErrorWrongCrc:\n        return NfcCliRawErrorWrongCrc;\n    case Iso15693_3ErrorNotPresent:\n        return NfcCliRawErrorNotPresent;\n    default:\n        return NfcCliRawErrorProtocol;\n    }\n}\n\nstatic Iso15693_3Error nfc_cli_raw_iso15693_3_poller_process_nfc_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return Iso15693_3ErrorNone;\n    case NfcErrorTimeout:\n        return Iso15693_3ErrorTimeout;\n    default:\n        return Iso15693_3ErrorNotPresent;\n    }\n}\n\nstatic inline void iso15693_3_format_activation_data(const uint8_t* data, FuriString* output) {\n    nfc_cli_format_array(data, ISO15693_3_UID_SIZE, \"UID: \", output);\n}\n\nstatic inline NfcCliRawError\n    nfc_cli_raw_iso15693_3_activate(NfcGenericInstance* poller, FuriString* activation_string) {\n    FURI_LOG_D(TAG, \"Activating...\");\n\n    Iso15693_3Poller* iso15_poller = poller;\n    uint8_t uid[ISO15693_3_UID_SIZE] = {0};\n\n    Iso15693_3Error error = iso15693_3_poller_inventory(iso15_poller, uid);\n    if(error == Iso15693_3ErrorNone) {\n        iso15693_3_format_activation_data(uid, activation_string);\n    }\n    return nfc_cli_raw_iso15693_3_process_error(error);\n}\n\nstatic inline NfcCliRawError nfc_cli_raw_iso15693_3_txrx(\n    NfcGenericInstance* poller,\n    BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t timeout) {\n    FURI_LOG_D(TAG, \"TxRx\");\n\n    Iso15693_3Poller* iso15_poller = poller;\n\n    bit_buffer_reset(rx_buffer);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    NfcError nfc_error = nfc_poller_trx(iso15_poller->nfc, tx_buffer, rx_buffer, timeout);\n    if(nfc_error != NfcErrorNone) {\n        error = nfc_cli_raw_iso15693_3_poller_process_nfc_error(nfc_error);\n    } else if(!iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {\n        error = Iso15693_3ErrorWrongCrc;\n    }\n\n    return nfc_cli_raw_iso15693_3_process_error(error);\n}\n\nNfcCommand nfc_cli_raw_iso15693_3_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response) {\n    do {\n        if(request->select) {\n            response->result =\n                nfc_cli_raw_iso15693_3_activate(poller, response->activation_string);\n        }\n\n        if(response->result != NfcCliRawErrorNone) break;\n        if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;\n\n        if(request->append_crc) {\n            FURI_LOG_D(TAG, \"Add CRC\");\n            iso13239_crc_append(Iso13239CrcTypeDefault, request->tx_buffer);\n        }\n\n        uint32_t timeout = request->timeout > 0 ? request->timeout : ISO15693_3_FDT_POLL_FC;\n        response->result =\n            nfc_cli_raw_iso15693_3_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);\n    } while(false);\n    return request->keep_field ? NfcCommandContinue : NfcCommandStop;\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.h",
    "content": "#pragma once\n\n#include \"../nfc_cli_raw_common_types.h\"\n\nNfcCommand nfc_cli_raw_iso15693_3_handler(\n    NfcGenericInstance* poller,\n    const NfcCliRawRequest* request,\n    NfcCliRawResponse* const response);\n"
  },
  {
    "path": "applications/main/nfc/cli/commands/raw/protocol_handlers/nfc_cli_raw_common_types.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <nfc/nfc.h>\n#include <nfc/nfc_poller.h>\n\ntypedef enum {\n    NfcCliRawErrorNone,\n    NfcCliRawErrorTimeout,\n    NfcCliRawErrorNotPresent,\n    NfcCliRawErrorWrongCrc,\n    NfcCliRawErrorProtocol,\n} NfcCliRawError;\n\ntypedef struct {\n    bool select;\n    bool keep_field;\n    bool append_crc;\n    NfcProtocol protocol;\n    BitBuffer* tx_buffer;\n    uint32_t timeout;\n} NfcCliRawRequest;\n\ntypedef struct {\n    NfcCliRawError result;\n    BitBuffer* rx_buffer;\n    FuriString* activation_string;\n} NfcCliRawResponse;\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli.c",
    "content": "#include \"nfc_cli_commands.h\"\n#include \"nfc_cli_command_processor.h\"\n\n#include \"applications/services/loader/loader.h\"\n#include \"applications/services/cli/cli_main_commands.h\"\n#include <toolbox/cli/shell/cli_shell.h>\n#include <toolbox/cli/cli_registry.h>\n\n#define NFC_DESKTOP_APP_NAME \"NFC\"\n\n#define TAG \"NfcCli\"\n\n#define NFC_PROMPT \"[\" ANSI_FG_GREEN \"nfc\" ANSI_RESET \"]\"\n\ntypedef struct {\n    Nfc* nfc;\n    CliRegistry* registry;\n    CliShell* shell;\n    NfcCliProcessorContext* processor_context;\n} NfcCliContext;\n\nstatic void nfc_cli_shell_motd(void* context) {\n    UNUSED(context);\n    printf(ANSI_FG_BR_BLUE \"\\r\\n\"\n                           \"                                     0000      \\r\\n\"\n                           \"                                     0000      \\r\\n\"\n                           \"                             000      0000     \\r\\n\"\n                           \"                             0000     00000    \\r\\n\"\n                           \"                    000      00000     0000    \\r\\n\"\n                           \"     0              0000      0000     00000   \\r\\n\"\n                           \"   000000           0000      00000     0000   \\r\\n\"\n                           \"   00000000          0000      0000     0000   \\r\\n\"\n                           \"   0000000000        0000      00000    0000   \\r\\n\"\n                           \"   0000 00000000     00000     00000    0000   \\r\\n\"\n                           \"   0000    0000000   00000     00000    0000   \\r\\n\"\n                           \"   0000      000000000000      0000     0000   \\r\\n\"\n                           \"   00000        000000000     00000     0000   \\r\\n\"\n                           \"     00           000000      0000     00000   \\r\\n\"\n                           \"                     00      00000     0000    \\r\\n\"\n                           \"                             0000     00000    \\r\\n\"\n                           \"                             000      0000     \\r\\n\"\n                           \"                                     0000      \\r\\n\"\n                           \"                                     0005      \\r\\n\"\n                           \"\\r\\n\" ANSI_FG_BR_WHITE \"Welcome to NFC Command Line Interface!\\r\\n\"\n                           \"Run `help` or `?` to list available commands\\r\\n\" ANSI_RESET);\n}\n\nstatic void nfc_cli_subscribe_commands(NfcCliContext* instance) {\n    size_t cnt = nfc_cli_command_get_count();\n    for(size_t i = 0; i < cnt; i++) {\n        const NfcCliCommandDescriptor* cmd = nfc_cli_command_get_by_index(i);\n        CliCommandExecuteCallback callback = nfc_cli_command_get_execute(cmd);\n        if(callback == NULL) continue;\n        const char* name = nfc_cli_command_get_name(cmd);\n        cli_registry_add_command(\n            instance->registry,\n            name,\n            CliCommandFlagParallelSafe,\n            callback,\n            instance->processor_context);\n    }\n}\n\nstatic bool nfc_cli_desktop_app_is_running() {\n    FuriString* app_name = furi_string_alloc();\n    Loader* ldr = furi_record_open(RECORD_LOADER);\n    bool result = false;\n\n    if(loader_get_application_name(ldr, app_name)) {\n        result = furi_string_equal_str(app_name, NFC_DESKTOP_APP_NAME);\n    }\n\n    furi_record_close(RECORD_LOADER);\n    furi_string_free(app_name);\n    return result;\n}\n\nstatic NfcCliContext* nfc_cli_alloc(PipeSide* pipe) {\n    NfcCliContext* instance = malloc(sizeof(NfcCliContext));\n    instance->nfc = nfc_alloc();\n    instance->processor_context = nfc_cli_command_processor_alloc(instance->nfc);\n\n    instance->registry = cli_registry_alloc();\n\n    nfc_cli_subscribe_commands(instance);\n\n    instance->shell =\n        cli_shell_alloc(nfc_cli_shell_motd, instance, pipe, instance->registry, NULL);\n\n    cli_shell_set_prompt(instance->shell, NFC_PROMPT);\n    return instance;\n}\n\nvoid nfc_cli_free(NfcCliContext* instance) {\n    furi_assert(instance);\n    nfc_cli_command_processor_free(instance->processor_context);\n\n    cli_shell_free(instance->shell);\n    cli_registry_free(instance->registry);\n\n    nfc_free(instance->nfc);\n    free(instance);\n}\n\nvoid nfc_cli_execute(PipeSide* pipe, FuriString* args, void* context) {\n    furi_assert(pipe);\n    UNUSED(args);\n    UNUSED(context);\n\n    if(nfc_cli_desktop_app_is_running()) {\n        printf(ANSI_FG_YELLOW\n               \"NFC app is running, unable to run NFC CLI at the same time!\\r\\n\" ANSI_RESET);\n        return;\n    }\n\n    NfcCliContext* instance = nfc_cli_alloc(pipe);\n\n    cli_shell_start(instance->shell);\n    cli_shell_join(instance->shell);\n\n    nfc_cli_free(instance);\n}\n\nCLI_COMMAND_INTERFACE(nfc, nfc_cli_execute, CliCommandFlagParallelSafe, 1024, CLI_APPID);\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_command_base.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <toolbox/cli/cli_command.h>\n#include <nfc/nfc.h>\n\n/**\n * @brief Type for action context to be created before action execution\n * must be hanlded through callbacks in each action separately\n */\ntypedef void NfcCliActionContext;\n\n/**\n * @brief Callback type for function of action context allocation\n * @param nfc Instance of NFC subsystem, will be used during action execution \n * @return Pointer to action context\n */\ntypedef NfcCliActionContext* (*NfcCliActionContextAlloc)(Nfc* nfc);\n\n/**\n * @brief Callback for action context deleting\n * @param action_ctx Action context to be freed\n */\ntypedef void (*NfcCliActionContextFree)(NfcCliActionContext* action_ctx);\n\n/**\n * @brief Callback invoked by command processor to determine whether already\n * existing context (from previously executed command) can be reused for the new one.\n * \n * @param action_ctx Action context\n * \n * In most cases re-creating of a new context is not needed.  \n * It is used in 'raw' command, where nfc field sometimes need to stay turned on between\n * commands. \n * \n * Handling of this situation and decision about reusing action context is on developer, \n * who need to decide, can this command reuse context. \n * \n * It can be done by comparing parameters of previously executed command and a new one, \n * or by some args in command.\n * \n * See implementation of 'keep_field' flag in 'raw' command for example.\n */\ntypedef bool (*NfcCliActionContextCanReuse)(NfcCliActionContext* ctx);\n\n/**\n * @brief Action execution callback\n * @param pipe provided by cli shell, can be used for command termination\n * @param ctx Action context\n */\ntypedef void (*NfcCliActionHandlerCallback)(PipeSide* pipe, NfcCliActionContext* ctx);\n\n/**\n * @brief Callback used for parsing argument key.\n * Each key added to command must have this, otherwise parsing result will always be\n * false and command will never be executed.\n * \n * @param value Text value for the key to be parsed\n * @param ctx Action context\n * @return true when parsing was fine, otherwise false. If any argument in command input\n * generates false during parsing, command will not be executed and error will be shown \n */\ntypedef bool (*NfcCliArgParseCallback)(FuriString* value, NfcCliActionContext* ctx);\n\ntypedef struct NfcCliKeyDescriptor NfcCliKeyDescriptor;\n\ntypedef struct NfcCliActionDescriptor NfcCliActionDescriptor;\n\ntypedef struct NfcCliCommandDescriptor NfcCliCommandDescriptor;\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_command_base_i.h",
    "content": "#pragma once\n\n#include \"nfc_cli_command_base.h\"\n#include <toolbox/cli/cli_ansi.h>\n#include <nfc/nfc.h>\n#include <nfc/protocols/nfc_protocol.h>\n#include \"nfc_cli_command_processor.h\"\n\n/**\n * @brief How to add command.\n * \n * There are 3 possible option on how to add new command to nfc_cli: \n * \n * @see Option 1 \"Add action command directly to nfc_shell\"\n * \n * In this case command will be invoked with argument string from nfc_shell.\n * Command registration must be performed directly by user.\n * \n * Steps: \n * 1. Add new function for command to nfc_cli.c\n * 2. In nfc_cli_alloc function register command using cli_registry_add_command after nfc_cli_subscribe_commands \n *\n * This option is NOT RECOMENDED, because such command will not have any 'help'\n * processing and parsing error checks. Argument parsing must also be done by hand. \n * \n * --------------------------------------------------------------------------\n *  \n * @see Option 2 \"Add action command to collection without further processing\"\n * \n * In this case command will be invoked with argument string from nfc_shell.\n * nfc_cli_command_processor is skipped, so argument handling is up to the developer.\n * \n * Steps:\n * 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder\n * 2. Define const NfcCliCommandDescriptor instance in .c file and its extern definition in .h file\n * 3. Include .h file to nfc_cli_commands.c file below comment \"Include new commands here\"\n * 4. Add new command reference to nfc_cli_commands array\n * 5. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file \n *\n * This option suites for simple commands with no any parameters. \n * @see nfc_cli_command_field.c implementation as an example.\n * \n * --------------------------------------------------------------------------\n * \n * @see Option 3 \"Add action command to collection with full processing\"\n * \n * In this nfc_cli_command_processor will be invoked for parsing command arguments\n * and action execution. Also it will handle errors and help printing.\n * \n * Steps:\n * 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder\n * 2. Use macro ADD_NFC_CLI_COMMAND to define command in .c file \n * 3. Define command extern in .h file, using command name from macro in form of \"<name>_cmd\"\n * 4. Add all desired actions and keys to your command\n * 5. Include .h file to nfc_cli_commands.c file below comment \"Include new commands here\"\n * 6. Add new command reference to nfc_cli_commands array\n * 7. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file \n *\n * This option suites for \"difficult\" commands which has actions with lots of keys. \n * @see nfc_cli_command_emulate.c implementation as an example.\n * \n */\n\n/**\n * @brief Used to decorate argument with some properties\n */\ntypedef struct {\n    bool required : 1; /**< Command always needs this argument. Missing arguments with this set to true will result execution error.*/\n    bool parameter : 1; /**< Such argument requires value after its name, otherwise it is a simple on/off switch */\n    bool multivalue : 1; /**< Such argument can take multiple values after its name, like this \"-key value1 value2 .. valueN\" */\n} FURI_PACKED NfcCliKeyFeatureSupport;\n\n/**\n * @brief Describes key for action\n */\nstruct NfcCliKeyDescriptor {\n    NfcCliKeyFeatureSupport features; /**< Features supported defining key behaviour */\n    const char* long_name; /**< Long key name starts with '--' symbol in argument string */\n    const char* short_name; /**< Short key name starts with '-' symbol in argument string */\n    const char* description; /**< Key description showed in help */\n    NfcCliArgParseCallback parse; /**< Parsing callback */\n};\n\n/**\n * @brief Describes action\n */\nstruct NfcCliActionDescriptor {\n    const char* name; /**< Action name MUST be the first argument after command.*/\n    const char* description; /**< Description showed in help */\n    size_t key_count; /**< Amount of key entries in keys array */\n    const NfcCliKeyDescriptor* keys; /**< Keys available for action */\n\n    NfcCliActionHandlerCallback execute; /**< Action callback, invoked if parsing is ok */\n    NfcCliActionContextAlloc alloc; /**< Allocates action context during command processing */\n    NfcCliActionContextFree free; /**< Frees action context */\n    NfcCliActionContextCanReuse can_reuse; /**< Checks context reuse possibility */\n};\n\n/**\n * @brief Describes command\n */\nstruct NfcCliCommandDescriptor {\n    const char* name; /** Used to register command in cli shell */\n    const char* description; /**< Description showed in help */\n    size_t action_count; /** Amount of actions available in scope of this particular command */\n    const NfcCliActionDescriptor** actions; /**< Actions available for command */\n    CliCommandExecuteCallback callback; /** Entry point for command */\n};\n\n/**\n * @brief This macro simplifies command creation. It fills instance of \n * NfcCliCommandDescriptor and generates a callback which invokes \n * nfc_cli_command_processor inside\n */\n#define ADD_NFC_CLI_COMMAND(name, description, actions)                  \\\n    static void nfc_cli_command_##name##_callback(                       \\\n        PipeSide* pipe, FuriString* args, void* context);                \\\n                                                                         \\\n    const NfcCliCommandDescriptor name##_cmd = {                         \\\n        #name,                                                           \\\n        #description,                                                    \\\n        COUNT_OF(actions),                                               \\\n        actions,                                                         \\\n        nfc_cli_command_##name##_callback,                               \\\n    };                                                                   \\\n                                                                         \\\n    static void nfc_cli_command_##name##_callback(                       \\\n        PipeSide* pipe, FuriString* args, void* context) {               \\\n        nfc_cli_command_processor_run(&name##_cmd, pipe, args, context); \\\n    }\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_command_processor.c",
    "content": "#include \"nfc_cli_command_processor.h\"\n#include \"nfc_cli_commands.h\"\n#include \"nfc_cli_command_base_i.h\"\n\n#include <m-string.h>\n#include <args.h>\n#include <hex.h>\n\n#define TAG \"NfcCliProcessor\"\n\n#define NFC_CLI_KEYS_FOUND_SIZE_BYTES (10 * sizeof(NfcCliKeyDescriptor*))\n\ntypedef enum {\n    NfcCliArgumentTypeShortNameKey,\n    NfcCliArgumentTypeShortNameKeyGroup,\n    NfcCliArgumentTypeLongNameKey,\n\n    NfcCliArgumentTypeUnknown\n} NfcCliArgumentType;\n\n/**\n * @brief Error codes for different processing states\n */\ntypedef enum {\n    NfcCliProcessorErrorNone, /**< Command was parsed successfully and execute callback will be invoked*/\n    NfcCliProcessorErrorNoneButHelp, /**< There was no error, but help needs to be printed. Command wil not be executed */\n    NfcCliProcessorErrorActionNotFound, /**< Wrong action was passed as first command parameter */\n    NfcCliProcessorErrorKeyNotSupported, /**< Unsupported key was passed in arguments. Details will be printed in erro_message*/\n    NfcCliProcessorErrorKeyParameterInGroup, /**< Parameter which requires value was passed in group. Example: -sckd */\n    NfcCliProcessorErrorKeyParameterValueMissing, /**< Value is missing for the parameter which requires it */\n    NfcCliProcessorErrorKeyDuplication, /**< Some argument key was duplicated in input parameters */\n    NfcCliProcessorErrorKeyParseError, /**< Error happened during argument value parsing */\n    NfcCliProcessorErrorKeyRequiredMissing, /**< Some keys required for command execution is missing*/\n\n    NfcCliProcessorErrorNum\n} NfcCliProcessorError;\n\nstruct NfcCliProcessorContext {\n    const NfcCliCommandDescriptor* cmd;\n    const NfcCliActionDescriptor* action;\n    const NfcCliKeyDescriptor** keys_found;\n    uint8_t total_keys_found;\n    uint8_t required_keys_expected;\n    uint8_t required_keys_found;\n\n    Nfc* nfc;\n    void* action_context;\n\n    FuriString* error_message;\n};\n\nstatic const NfcCliActionDescriptor*\n    nfc_cli_get_action_from_args(const NfcCliCommandDescriptor* cmd, FuriString* args) {\n    const NfcCliActionDescriptor* action = cmd->actions[0];\n\n    bool multiple_action_cmd = nfc_cli_command_has_multiple_actions(cmd);\n    if(multiple_action_cmd) {\n        action = NULL;\n        FuriString* arg_str = furi_string_alloc();\n        if(args_read_string_and_trim(args, arg_str)) {\n            action = nfc_cli_command_get_action_by_name(cmd, arg_str);\n        }\n        furi_string_free(arg_str);\n    }\n\n    return action;\n}\n\nstatic bool nfc_cli_action_can_reuse_context(\n    NfcCliProcessorContext* instance,\n    const NfcCliActionDescriptor* new_action) {\n    bool result = false;\n    do {\n        if(instance->action != new_action) break;\n        if(new_action->can_reuse == NULL) break;\n        result = new_action->can_reuse(instance->action_context);\n    } while(false);\n    return result;\n}\n\nstatic void nfc_cli_action_free(NfcCliProcessorContext* instance) {\n    if(instance->action && instance->action->free) {\n        FURI_LOG_D(TAG, \"Free previous \\\"%s\\\" action context\", instance->action->name);\n        instance->action->free(instance->action_context);\n    }\n    instance->action = NULL;\n}\n\nstatic NfcCliProcessorError\n    nfc_cli_action_alloc(NfcCliProcessorContext* instance, FuriString* args) {\n    const NfcCliCommandDescriptor* cmd = instance->cmd;\n\n    NfcCliProcessorError result = NfcCliProcessorErrorNone;\n    do {\n        const NfcCliActionDescriptor* action = nfc_cli_get_action_from_args(cmd, args);\n        if(action == NULL) {\n            result = NfcCliProcessorErrorActionNotFound;\n            furi_string_printf(instance->error_message, \"Action not found\");\n            break;\n        }\n\n        if(!nfc_cli_action_can_reuse_context(instance, action)) {\n            nfc_cli_action_free(instance);\n\n            instance->action = action;\n            if(action->alloc && action->free) {\n                FURI_LOG_D(TAG, \"Allocating context for action \\\"%s\\\"\", action->name);\n                instance->action_context = instance->action->alloc(instance->nfc);\n            } else if(action->alloc && (action->free == NULL)) {\n                FURI_LOG_W(\n                    TAG,\n                    \"Free callback not defined for action \\\"%s\\\". Skip allocation to avoid memory leak.\",\n                    action->name);\n                instance->action_context = NULL;\n            } else {\n                FURI_LOG_D(TAG, \"No alloc context callback for action \\\"%s\\\"\", action->name);\n                instance->action_context = NULL;\n            }\n        } else\n            FURI_LOG_D(TAG, \"Reusing context from previous \\\"%s\\\" action\", action->name);\n\n        memset(instance->keys_found, 0, NFC_CLI_KEYS_FOUND_SIZE_BYTES);\n        instance->required_keys_expected = nfc_cli_action_get_required_keys_count(action);\n        instance->required_keys_found = 0;\n        instance->total_keys_found = 0;\n    } while(false);\n\n    return result;\n}\n\nstatic NfcCliArgumentType nfc_cli_get_argument_type(FuriString* argument) {\n    size_t arg_len = furi_string_size(argument);\n    NfcCliArgumentType type = NfcCliArgumentTypeUnknown;\n\n    if(arg_len > 2) {\n        char ch1 = furi_string_get_char(argument, 0);\n        char ch2 = furi_string_get_char(argument, 1);\n        if(ch1 == '-') {\n            type = (ch2 == '-') ? NfcCliArgumentTypeLongNameKey :\n                                  NfcCliArgumentTypeShortNameKeyGroup;\n        }\n    } else if(arg_len == 2) {\n        char ch1 = furi_string_get_char(argument, 0);\n        type = (ch1 == '-') ? NfcCliArgumentTypeShortNameKey : NfcCliArgumentTypeUnknown;\n    }\n\n    return type;\n}\n\nstatic bool\n    nfc_cli_check_duplicate_keys(NfcCliProcessorContext* instance, const NfcCliKeyDescriptor* key) {\n    bool result = false;\n    for(size_t i = 0; i < instance->total_keys_found; i++) {\n        const NfcCliKeyDescriptor* buf = instance->keys_found[i];\n        if(buf != key) continue;\n        result = true;\n        break;\n    }\n\n    return result;\n}\n\nstatic void nfc_cli_trim_multivalue_arg(FuriString* args, FuriString* value) {\n    furi_string_set(value, args);\n    size_t index = furi_string_search_char(value, '-', 0);\n    if(index != STRING_FAILURE) {\n        furi_string_left(value, index);\n        furi_string_right(args, index);\n    } else {\n        furi_string_reset(args);\n    }\n}\n\nstatic NfcCliProcessorError nfc_cli_parse_single_key(\n    NfcCliProcessorContext* instance,\n    FuriString* argument,\n    FuriString* args,\n    bool from_group) {\n    FuriString* value_str = furi_string_alloc();\n\n    NfcCliProcessorError result = NfcCliProcessorErrorNone;\n    do {\n        const NfcCliKeyDescriptor* key =\n            nfc_cli_action_get_key_descriptor(instance->action, argument);\n        if(key == NULL) {\n            if(furi_string_equal_str(argument, \"h\"))\n                result = NfcCliProcessorErrorNoneButHelp;\n            else {\n                furi_string_printf(\n                    instance->error_message,\n                    \"Key \\'%s\\' is not supported\",\n                    furi_string_get_cstr(argument));\n                result = NfcCliProcessorErrorKeyNotSupported;\n            }\n            break;\n        }\n\n        if(key->features.parameter && from_group) {\n            furi_string_printf(\n                instance->error_message,\n                \"Parameter key \\'%s\\' can\\'t be grouped\",\n                furi_string_get_cstr(argument));\n            result = NfcCliProcessorErrorKeyParameterInGroup;\n            break;\n        }\n\n        if(nfc_cli_check_duplicate_keys(instance, key)) {\n            furi_string_printf(\n                instance->error_message, \"Duplicated key \\'%s\\'\", furi_string_get_cstr(argument));\n            result = NfcCliProcessorErrorKeyDuplication;\n            break;\n        }\n\n        if(key->features.multivalue && !key->features.parameter) break;\n        if(key->features.multivalue) {\n            nfc_cli_trim_multivalue_arg(args, value_str);\n            FURI_LOG_D(TAG, \"Multivalue: %s\", furi_string_get_cstr(value_str));\n        } else if(key->features.parameter && !args_read_string_and_trim(args, value_str)) {\n            result = NfcCliProcessorErrorKeyParameterValueMissing;\n            furi_string_printf(\n                instance->error_message,\n                \"Missing value for \\'%s\\'\",\n                furi_string_get_cstr(argument));\n            break;\n        }\n\n        if(key->parse == NULL) {\n            furi_string_printf(\n                instance->error_message,\n                \"Parse callback for key \\'%s\\' not defined\",\n                furi_string_get_cstr(argument));\n            result = NfcCliProcessorErrorKeyParseError;\n            break;\n        }\n\n        FURI_LOG_D(TAG, \"Parsing key \\\"%s\\\"\", furi_string_get_cstr(argument));\n        if(!key->parse(value_str, instance->action_context)) {\n            furi_string_printf(\n                instance->error_message,\n                \"Unable to parse value \\'%s\\' for key \\'%s\\'\",\n                furi_string_get_cstr(value_str),\n                furi_string_get_cstr(argument));\n            result = NfcCliProcessorErrorKeyParseError;\n            break;\n        }\n\n        instance->keys_found[instance->total_keys_found] = key;\n        instance->total_keys_found++;\n        if(key->features.required) instance->required_keys_found++;\n    } while(false);\n    furi_string_free(value_str);\n\n    return result;\n}\n\nstatic NfcCliProcessorError\n    nfc_cli_parse_group_key(NfcCliProcessorContext* instance, FuriString* argument) {\n    NfcCliProcessorError result = NfcCliProcessorErrorNone;\n    FURI_LOG_D(TAG, \"Parsing key group\\\"%s\\\"\", furi_string_get_cstr(argument));\n\n    FuriString* arg_buf = furi_string_alloc();\n    for(size_t i = 0; i < furi_string_size(argument); i++) {\n        furi_string_set_n(arg_buf, argument, i, 1);\n        result = nfc_cli_parse_single_key(instance, arg_buf, NULL, true);\n        if(result != NfcCliProcessorErrorNone) break;\n    }\n    furi_string_free(arg_buf);\n\n    return result;\n}\n\nstatic NfcCliProcessorError nfc_cli_parse_argument(\n    NfcCliProcessorContext* instance,\n    FuriString* argument,\n    FuriString* args) {\n    NfcCliArgumentType type = nfc_cli_get_argument_type(argument);\n\n    furi_string_trim(argument, \"-\");\n\n    NfcCliProcessorError result = NfcCliProcessorErrorNone;\n\n    if(type == NfcCliArgumentTypeShortNameKeyGroup)\n        result = nfc_cli_parse_group_key(instance, argument);\n    else if((type == NfcCliArgumentTypeShortNameKey) || (type == NfcCliArgumentTypeLongNameKey)) {\n        result = nfc_cli_parse_single_key(instance, argument, args, false);\n    } else if(type == NfcCliArgumentTypeUnknown) { //-V547\n        result = NfcCliProcessorErrorKeyNotSupported;\n        furi_string_printf(\n            instance->error_message,\n            \"Key \\'%s\\' is not supported\",\n            furi_string_get_cstr(argument));\n    }\n\n    return result;\n}\n\nstatic NfcCliProcessorError\n    nfc_cli_process_arguments(NfcCliProcessorContext* instance, FuriString* args) {\n    NfcCliProcessorError result = NfcCliProcessorErrorNone;\n\n    FuriString* argument = furi_string_alloc();\n    while(args_read_string_and_trim(args, argument)) {\n        result = nfc_cli_parse_argument(instance, argument, args);\n        if(result != NfcCliProcessorErrorNone) break;\n    }\n    furi_string_free(argument);\n\n    if((result == NfcCliProcessorErrorNone) &&\n       (instance->required_keys_expected != instance->required_keys_found)) {\n        furi_string_printf(instance->error_message, \"Some required keys missing\");\n        result = NfcCliProcessorErrorKeyRequiredMissing;\n    }\n\n    return result;\n}\n\nstatic inline void nfc_cli_command_process_error(\n    const NfcCliProcessorContext* instance,\n    NfcCliProcessorError error) {\n    do {\n        if(error == NfcCliProcessorErrorNone) break;\n\n        if(error != NfcCliProcessorErrorNoneButHelp)\n            printf(\n                ANSI_FG_RED \"Error: %s\\r\\n\" ANSI_RESET,\n                furi_string_get_cstr(instance->error_message));\n\n        if(error == NfcCliProcessorErrorActionNotFound)\n            nfc_cli_command_format_info(instance->cmd, instance->error_message);\n        else\n            nfc_cli_action_format_info(instance->action, instance->error_message);\n\n        printf(\"\\n%s\", furi_string_get_cstr(instance->error_message));\n    } while(false);\n}\n\nvoid nfc_cli_command_processor_run(\n    const NfcCliCommandDescriptor* cmd,\n    PipeSide* pipe,\n    FuriString* args,\n    void* context) {\n    furi_assert(pipe);\n    furi_assert(cmd);\n    furi_assert(args);\n    NfcCliProcessorContext* instance = context;\n    furi_string_reset(instance->error_message);\n\n    NfcCliProcessorError error = NfcCliProcessorErrorNone;\n    instance->cmd = cmd;\n    do {\n        error = nfc_cli_action_alloc(instance, args);\n        if(error != NfcCliProcessorErrorNone) break;\n\n        error = nfc_cli_process_arguments(instance, args);\n        if(error != NfcCliProcessorErrorNone) break;\n\n        if(instance->action && instance->action->execute) {\n            instance->action->execute(pipe, instance->action_context);\n        } else {\n            FURI_LOG_W(TAG, \"Action execute callback missing\");\n        }\n    } while(false);\n\n    nfc_cli_command_process_error(instance, error);\n}\n\nNfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n    NfcCliProcessorContext* instance = malloc(sizeof(NfcCliProcessorContext));\n    instance->nfc = nfc;\n    instance->keys_found = malloc(NFC_CLI_KEYS_FOUND_SIZE_BYTES);\n    instance->total_keys_found = 0;\n    instance->required_keys_found = 0;\n    instance->required_keys_expected = 0;\n\n    instance->error_message = furi_string_alloc();\n    return instance;\n}\n\nvoid nfc_cli_command_processor_free(NfcCliProcessorContext* instance) {\n    furi_assert(instance);\n    nfc_cli_action_free(instance);\n    free(instance->keys_found);\n    furi_string_free(instance->error_message);\n\n    instance->nfc = NULL;\n    free(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_command_processor.h",
    "content": "#pragma once\n#include <furi.h>\n#include <nfc/nfc.h>\n#include \"nfc_cli_command_base.h\"\n\ntypedef struct NfcCliProcessorContext NfcCliProcessorContext;\n\nNfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc);\nvoid nfc_cli_command_processor_free(NfcCliProcessorContext* instance);\n\nvoid nfc_cli_command_processor_run(\n    const NfcCliCommandDescriptor* cmd,\n    PipeSide* pipe,\n    FuriString* args,\n    void* context);\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_commands.c",
    "content": "#include \"nfc_cli_commands.h\"\n#include \"nfc_cli_command_base_i.h\"\n\n/** Include new commands here */\n#include \"commands/raw/nfc_cli_command_raw.h\"\n#include \"commands/apdu/nfc_cli_command_apdu.h\"\n#include \"commands/dump/nfc_cli_command_dump.h\"\n#include \"commands/mfu/nfc_cli_command_mfu.h\"\n#include \"commands/nfc_cli_command_emulate.h\"\n#include \"commands/nfc_cli_command_scanner.h\"\n#include \"commands/nfc_cli_command_field.h\"\n\n#define TAG \"NfcCliCommands\"\n\n/** Add new commands here */\nstatic const NfcCliCommandDescriptor* nfc_cli_commands[] = {\n    &apdu_cmd,\n    &raw_cmd,\n    &emulate_cmd,\n    &mfu_cmd,\n    &scanner_cmd,\n    &dump_cmd,\n    &field_cmd,\n};\n\nsize_t nfc_cli_command_get_count() {\n    return COUNT_OF(nfc_cli_commands);\n}\n\nconst NfcCliActionDescriptor*\n    nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name) {\n    furi_assert(cmd);\n    furi_assert(name);\n\n    for(size_t i = 0; i < cmd->action_count; i++) {\n        const NfcCliActionDescriptor* action = cmd->actions[i];\n        if(furi_string_equal_str(name, action->name)) return action;\n    }\n    return NULL;\n}\n\nconst NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index) {\n    furi_assert(index < COUNT_OF(nfc_cli_commands));\n    return nfc_cli_commands[index];\n}\n\nbool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd) {\n    furi_assert(cmd);\n    furi_check(cmd->action_count > 0);\n    return (cmd->action_count > 1);\n}\n\nconst char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd) {\n    furi_assert(cmd);\n    return cmd->name;\n}\n\nCliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd) {\n    furi_assert(cmd);\n    return cmd->callback;\n}\n\nstatic inline const NfcCliKeyDescriptor* nfc_cli_action_get_key_by_name(\n    const NfcCliActionDescriptor* action,\n    const FuriString* name,\n    bool long_name) {\n    for(size_t i = 0; i < action->key_count; i++) {\n        const NfcCliKeyDescriptor* key = &action->keys[i];\n        const char* buf = long_name ? key->long_name : key->short_name;\n        if((buf != NULL) && furi_string_equal_str(name, buf)) return key;\n    }\n    return NULL;\n}\n\nconst NfcCliKeyDescriptor*\n    nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument) {\n    furi_assert(action);\n    furi_assert(argument);\n\n    return nfc_cli_action_get_key_by_name(action, argument, furi_string_size(argument) > 1);\n}\n\nsize_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action) {\n    furi_assert(action);\n\n    size_t required_key_count = 0;\n    for(size_t i = 0; i < action->key_count; i++) {\n        const NfcCliKeyDescriptor* key = &action->keys[i];\n        if(!key->features.required) continue;\n        required_key_count++;\n    }\n    return required_key_count;\n}\n\nstatic int nfc_cli_action_format_key_name(const NfcCliKeyDescriptor* key, FuriString* output) {\n    int len = 0;\n    FuriString* name = furi_string_alloc();\n    if(key->short_name && key->long_name) {\n        len = furi_string_printf(name, \"-%s, --%s\", key->short_name, key->long_name);\n    } else if(key->short_name && (key->long_name == NULL)) {\n        len = furi_string_printf(name, \"-%s\", key->short_name);\n    } else if((key->short_name == NULL) && key->long_name) {\n        len = furi_string_printf(name, \"--%s\", key->long_name);\n    }\n\n    const char* color = key->features.required ? ANSI_FLIPPER_BRAND_ORANGE : ANSI_RESET;\n    furi_string_printf(output, \"%s%s%s\", color, furi_string_get_cstr(name), ANSI_RESET);\n    furi_string_free(name);\n    return len;\n}\n\nvoid nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output) {\n    furi_assert(action);\n    furi_assert(output);\n    furi_string_printf(\n        output,\n        action->description ? \"%s - %s\\r\\n\\n\" : \"%s\\r\\n\\n\",\n        action->name,\n        action->description);\n\n    if(action->key_count == 0) return;\n\n    FuriString* buf = furi_string_alloc();\n    furi_string_cat_printf(\n        output,\n        ANSI_FG_BR_GREEN \"Keys \" ANSI_FLIPPER_BRAND_ORANGE \"(required) \" ANSI_RESET\n                         \"(optional):\\r\\n\");\n\n    for(size_t i = 0; i < action->key_count; i++) {\n        const NfcCliKeyDescriptor* key = &action->keys[i];\n\n        int len = nfc_cli_action_format_key_name(key, buf);\n        furi_string_cat_printf(output, \"%s\", furi_string_get_cstr(buf));\n\n        if(key->description) {\n            const int offset = 20;\n            furi_string_cat_printf(\n                output, ANSI_CURSOR_RIGHT_BY(\"%d\") \"%s\", offset - len, key->description);\n        }\n        furi_string_cat_printf(output, \"\\r\\n\");\n    }\n    furi_string_free(buf);\n}\n\nvoid nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output) {\n    furi_assert(cmd);\n    furi_assert(output);\n    furi_string_printf(output, \"%s - %s\\r\\n\", cmd->name, cmd->description);\n    if(cmd->action_count > 1) {\n        furi_string_cat_printf(output, \"Possible actions: \\r\\n\");\n\n        for(size_t i = 0; i < cmd->action_count; i++) {\n            const NfcCliActionDescriptor* action = cmd->actions[i];\n            furi_string_cat_printf(\n                output,\n                action->description ? \"\\t%s\\t-\\t%s\\r\\n\" : \"%s\\r\\n\",\n                action->name,\n                action->description);\n        }\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/cli/nfc_cli_commands.h",
    "content": "#pragma once\n\n#include \"nfc_cli_command_base.h\"\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/cli/cli_ansi.h>\n\nsize_t nfc_cli_command_get_count();\n\nconst NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index);\n\nconst char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd);\n\nCliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd);\n\nbool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd);\n\nconst NfcCliActionDescriptor*\n    nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name);\n\nsize_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action);\n\nconst NfcCliKeyDescriptor*\n    nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument);\n\nvoid nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output);\n\nvoid nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output);\n"
  },
  {
    "path": "applications/main/nfc/helpers/felica_auth.c",
    "content": "#include \"felica_auth.h\"\n\nFelicaAuthenticationContext* felica_auth_alloc() {\n    FelicaAuthenticationContext* instance = malloc(sizeof(FelicaAuthenticationContext));\n    memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);\n    instance->skip_auth = true;\n    return instance;\n}\n\nvoid felica_auth_free(FelicaAuthenticationContext* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nvoid felica_auth_reset(FelicaAuthenticationContext* instance) {\n    furi_assert(instance);\n    memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);\n    instance->skip_auth = true;\n    instance->auth_status.external = 0;\n    instance->auth_status.internal = 0;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/felica_auth.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/felica/felica.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nFelicaAuthenticationContext* felica_auth_alloc();\n\nvoid felica_auth_free(FelicaAuthenticationContext* instance);\n\nvoid felica_auth_reset(FelicaAuthenticationContext* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_classic_key_cache.c",
    "content": "#include \"mf_classic_key_cache.h\"\n\n#include <furi/furi.h>\n#include <storage/storage.h>\n\n#define NFC_APP_KEYS_EXTENSION   \".keys\"\n#define NFC_APP_KEY_CACHE_FOLDER \"/ext/nfc/.cache\"\n\nstatic const char* mf_classic_key_cache_file_header = \"Flipper NFC keys\";\nstatic const uint32_t mf_classic_key_cache_file_version = 1;\n\nstruct MfClassicKeyCache {\n    MfClassicDeviceKeys keys;\n    MfClassicKeyType current_key_type;\n    uint8_t current_sector;\n};\n\nstatic void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, FuriString* path) {\n    furi_string_printf(path, \"%s/\", NFC_APP_KEY_CACHE_FOLDER);\n    for(size_t i = 0; i < uid_len; i++) {\n        furi_string_cat_printf(path, \"%02X\", uid[i]);\n    }\n    furi_string_cat_printf(path, \"%s\", NFC_APP_KEYS_EXTENSION);\n}\n\nMfClassicKeyCache* mf_classic_key_cache_alloc(void) {\n    MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache));\n\n    return instance;\n}\n\nvoid mf_classic_key_cache_free(MfClassicKeyCache* instance) {\n    furi_assert(instance);\n\n    free(instance);\n}\n\nbool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data) {\n    UNUSED(instance);\n    furi_assert(data);\n\n    size_t uid_len = 0;\n    const uint8_t* uid = mf_classic_get_uid(data, &uid_len);\n    FuriString* file_path = furi_string_alloc();\n    nfc_get_key_cache_file_path(uid, uid_len, file_path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool save_success = false;\n    do {\n        if(!storage_simply_mkdir(storage, NFC_APP_KEY_CACHE_FOLDER)) break;\n        if(!storage_simply_remove(storage, furi_string_get_cstr(file_path))) break;\n        if(!flipper_format_buffered_file_open_always(ff, furi_string_get_cstr(file_path))) break;\n\n        if(!flipper_format_write_header_cstr(\n               ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version))\n            break;\n        if(!flipper_format_write_string_cstr(\n               ff, \"Mifare Classic type\", mf_classic_get_device_name(data, NfcDeviceNameTypeShort)))\n            break;\n        if(!flipper_format_write_hex_uint64(ff, \"Key A map\", &data->key_a_mask, 1)) break;\n        if(!flipper_format_write_hex_uint64(ff, \"Key B map\", &data->key_b_mask, 1)) break;\n\n        uint8_t sector_num = mf_classic_get_total_sectors_num(data->type);\n        bool key_save_success = true;\n        for(size_t i = 0; (i < sector_num) && (key_save_success); i++) {\n            MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);\n            if(FURI_BIT(data->key_a_mask, i)) {\n                furi_string_printf(temp_str, \"Key A sector %d\", i);\n                key_save_success = flipper_format_write_hex(\n                    ff, furi_string_get_cstr(temp_str), sec_tr->key_a.data, sizeof(MfClassicKey));\n            }\n            if(!key_save_success) break;\n            if(FURI_BIT(data->key_b_mask, i)) {\n                furi_string_printf(temp_str, \"Key B sector %d\", i);\n                key_save_success = flipper_format_write_hex(\n                    ff, furi_string_get_cstr(temp_str), sec_tr->key_b.data, sizeof(MfClassicKey));\n            }\n        }\n        save_success = key_save_success;\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_string_free(temp_str);\n    furi_string_free(file_path);\n    furi_record_close(RECORD_STORAGE);\n\n    return save_success;\n}\n\nbool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len) {\n    furi_assert(instance);\n    furi_assert(uid);\n\n    mf_classic_key_cache_reset(instance);\n\n    FuriString* file_path = furi_string_alloc();\n    nfc_get_key_cache_file_path(uid, uid_len, file_path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool load_success = false;\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(file_path))) break;\n\n        uint32_t version = 0;\n        if(!flipper_format_read_header(ff, temp_str, &version)) break;\n        if(furi_string_cmp_str(temp_str, mf_classic_key_cache_file_header)) break;\n        if(version != mf_classic_key_cache_file_version) break;\n\n        if(!flipper_format_read_hex_uint64(ff, \"Key A map\", &instance->keys.key_a_mask, 1)) break;\n        if(!flipper_format_read_hex_uint64(ff, \"Key B map\", &instance->keys.key_b_mask, 1)) break;\n\n        bool key_read_success = true;\n        for(size_t i = 0; (i < MF_CLASSIC_TOTAL_SECTORS_MAX) && (key_read_success); i++) {\n            if(FURI_BIT(instance->keys.key_a_mask, i)) {\n                furi_string_printf(temp_str, \"Key A sector %d\", i);\n                key_read_success = flipper_format_read_hex(\n                    ff,\n                    furi_string_get_cstr(temp_str),\n                    instance->keys.key_a[i].data,\n                    sizeof(MfClassicKey));\n            }\n            if(!key_read_success) break;\n            if(FURI_BIT(instance->keys.key_b_mask, i)) {\n                furi_string_printf(temp_str, \"Key B sector %d\", i);\n                key_read_success = flipper_format_read_hex(\n                    ff,\n                    furi_string_get_cstr(temp_str),\n                    instance->keys.key_b[i].data,\n                    sizeof(MfClassicKey));\n            }\n        }\n        load_success = key_read_success;\n    } while(false);\n\n    flipper_format_buffered_file_close(ff);\n    flipper_format_free(ff);\n    furi_string_free(temp_str);\n    furi_string_free(file_path);\n    furi_record_close(RECORD_STORAGE);\n\n    return load_success;\n}\n\nvoid mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    mf_classic_key_cache_reset(instance);\n    instance->keys.key_a_mask = data->key_a_mask;\n    instance->keys.key_b_mask = data->key_b_mask;\n    for(size_t i = 0; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);\n\n        if(FURI_BIT(data->key_a_mask, i)) {\n            instance->keys.key_a[i] = sec_tr->key_a;\n        }\n        if(FURI_BIT(data->key_b_mask, i)) {\n            instance->keys.key_b[i] = sec_tr->key_b;\n        }\n    }\n}\n\nbool mf_classic_key_cache_get_next_key(\n    MfClassicKeyCache* instance,\n    uint8_t* sector_num,\n    MfClassicKey* key,\n    MfClassicKeyType* key_type) {\n    furi_assert(instance);\n    furi_assert(sector_num);\n    furi_assert(key);\n    furi_assert(key_type);\n\n    bool next_key_found = false;\n    for(uint8_t i = instance->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {\n        if(FURI_BIT(instance->keys.key_a_mask, i)) {\n            FURI_BIT_CLEAR(instance->keys.key_a_mask, i);\n            *key = instance->keys.key_a[i];\n            *key_type = MfClassicKeyTypeA;\n            *sector_num = i;\n\n            next_key_found = true;\n            break;\n        }\n        if(FURI_BIT(instance->keys.key_b_mask, i)) {\n            FURI_BIT_CLEAR(instance->keys.key_b_mask, i);\n            *key = instance->keys.key_b[i];\n            *key_type = MfClassicKeyTypeB;\n            *sector_num = i;\n\n            next_key_found = true;\n            instance->current_sector = i;\n            break;\n        }\n    }\n\n    return next_key_found;\n}\n\nvoid mf_classic_key_cache_reset(MfClassicKeyCache* instance) {\n    furi_assert(instance);\n\n    instance->current_key_type = MfClassicKeyTypeA;\n    instance->current_sector = 0;\n    instance->keys.key_a_mask = 0;\n    instance->keys.key_b_mask = 0;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_classic_key_cache.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_classic/mf_classic.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct MfClassicKeyCache MfClassicKeyCache;\n\nMfClassicKeyCache* mf_classic_key_cache_alloc(void);\n\nvoid mf_classic_key_cache_free(MfClassicKeyCache* instance);\n\nbool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len);\n\nvoid mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data);\n\nbool mf_classic_key_cache_get_next_key(\n    MfClassicKeyCache* instance,\n    uint8_t* sector_num,\n    MfClassicKey* key,\n    MfClassicKeyType* key_type);\n\nbool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data);\n\nvoid mf_classic_key_cache_reset(MfClassicKeyCache* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_ultralight_auth.c",
    "content": "#include \"mf_ultralight_auth.h\"\n\n#include <furi.h>\n#include <mbedtls/sha1.h>\n\nMfUltralightAuth* mf_ultralight_auth_alloc(void) {\n    MfUltralightAuth* instance = malloc(sizeof(MfUltralightAuth));\n\n    return instance;\n}\n\nvoid mf_ultralight_auth_free(MfUltralightAuth* instance) {\n    furi_assert(instance);\n\n    free(instance);\n}\n\nvoid mf_ultralight_auth_reset(MfUltralightAuth* instance) {\n    furi_assert(instance);\n\n    instance->type = MfUltralightAuthTypeNone;\n    memset(&instance->password, 0, sizeof(MfUltralightAuthPassword));\n    memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey));\n    memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));\n}\n\nbool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) {\n    furi_assert(instance);\n    furi_assert(uid);\n\n    bool generated = false;\n    if(uid_len == 7) {\n        instance->password.data[0] = uid[1] ^ uid[3] ^ 0xAA;\n        instance->password.data[1] = uid[2] ^ uid[4] ^ 0x55;\n        instance->password.data[2] = uid[3] ^ uid[5] ^ 0xAA;\n        instance->password.data[3] = uid[4] ^ uid[6] ^ 0x55;\n        generated = true;\n    }\n\n    return generated;\n}\n\nbool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) {\n    furi_assert(instance);\n    furi_assert(uid);\n\n    uint8_t hash[20];\n    bool generated = false;\n    if(uid_len == 7) {\n        mbedtls_sha1(uid, uid_len, hash);\n        instance->password.data[0] = (hash[hash[0] % 20]);\n        instance->password.data[1] = (hash[(hash[0] + 5) % 20]);\n        instance->password.data[2] = (hash[(hash[0] + 13) % 20]);\n        instance->password.data[3] = (hash[(hash[0] + 17) % 20]);\n        generated = true;\n    }\n\n    return generated;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_ultralight_auth.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/mf_ultralight/mf_ultralight.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    MfUltralightAuthTypeNone,\n    MfUltralightAuthTypeReader,\n    MfUltralightAuthTypeManual,\n    MfUltralightAuthTypeXiaomi,\n    MfUltralightAuthTypeAmiibo,\n} MfUltralightAuthType;\n\ntypedef struct {\n    MfUltralightAuthType type;\n    MfUltralightAuthPassword password;\n    MfUltralightC3DesAuthKey tdes_key;\n    MfUltralightAuthPack pack;\n} MfUltralightAuth;\n\nMfUltralightAuth* mf_ultralight_auth_alloc(void);\n\nvoid mf_ultralight_auth_free(MfUltralightAuth* instance);\n\nvoid mf_ultralight_auth_reset(MfUltralightAuth* instance);\n\nbool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len);\n\nbool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_user_dict.c",
    "content": "#include \"mf_user_dict.h\"\n\n#include <toolbox/keys_dict.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <furi/furi.h>\n\n#define NFC_APP_FOLDER                    EXT_PATH(\"nfc\")\n#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER \"/assets/mf_classic_dict_user.nfc\")\n\nstruct MfUserDict {\n    size_t keys_num;\n    MfClassicKey* keys_arr;\n};\n\nMfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) {\n    MfUserDict* instance = malloc(sizeof(MfUserDict));\n\n    KeysDict* dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n\n    size_t dict_keys_num = keys_dict_get_total_keys(dict);\n    instance->keys_num = MIN(max_keys_to_load, dict_keys_num);\n\n    if(instance->keys_num > 0) {\n        instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey));\n        for(size_t i = 0; i < instance->keys_num; i++) {\n            bool key_loaded =\n                keys_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));\n            furi_assert(key_loaded);\n        }\n    }\n    keys_dict_free(dict);\n\n    return instance;\n}\n\nvoid mf_user_dict_free(MfUserDict* instance) {\n    furi_assert(instance);\n\n    if(instance->keys_num > 0) {\n        free(instance->keys_arr);\n    }\n    free(instance);\n}\n\nsize_t mf_user_dict_get_keys_cnt(MfUserDict* instance) {\n    furi_assert(instance);\n\n    return instance->keys_num;\n}\n\nvoid mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str) {\n    furi_assert(instance);\n    furi_assert(str);\n    furi_assert(index < instance->keys_num);\n    furi_assert(instance->keys_arr);\n\n    furi_string_reset(str);\n    for(size_t i = 0; i < sizeof(MfClassicKey); i++) {\n        furi_string_cat_printf(str, \"%02X\", instance->keys_arr[index].data[i]);\n    }\n}\n\nbool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) {\n    furi_assert(instance);\n    furi_assert(index < instance->keys_num);\n    furi_assert(instance->keys_arr);\n\n    KeysDict* dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n\n    bool key_delete_success =\n        keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));\n    keys_dict_free(dict);\n\n    if(key_delete_success) {\n        instance->keys_num--;\n    }\n\n    return key_delete_success;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/mf_user_dict.h",
    "content": "#pragma once\n\n#include <furi/core/string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct MfUserDict MfUserDict;\n\nMfUserDict* mf_user_dict_alloc(size_t max_keys_to_load);\n\nvoid mf_user_dict_free(MfUserDict* instance);\n\nsize_t mf_user_dict_get_keys_cnt(MfUserDict* instance);\n\nvoid mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str);\n\nbool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/mfkey32_logger.c",
    "content": "#include \"mfkey32_logger.h\"\n\n#include <m-array.h>\n\n#include <bit_lib/bit_lib.h>\n#include <stream/stream.h>\n#include <stream/buffered_file_stream.h>\n\n#define MFKEY32_LOGGER_MAX_NONCES_SAVED (100)\n\ntypedef struct {\n    bool is_filled;\n    uint32_t cuid;\n    uint8_t sector_num;\n    MfClassicKeyType key_type;\n    uint32_t nt0;\n    uint32_t nr0;\n    uint32_t ar0;\n    uint32_t nt1;\n    uint32_t nr1;\n    uint32_t ar1;\n} Mfkey32LoggerParams;\n\nARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST); //-V658\n\nstruct Mfkey32Logger {\n    uint32_t cuid;\n    Mfkey32LoggerParams_t params_arr;\n    size_t nonces_saves;\n    size_t params_collected;\n};\n\nMfkey32Logger* mfkey32_logger_alloc(uint32_t cuid) {\n    Mfkey32Logger* instance = malloc(sizeof(Mfkey32Logger));\n    instance->cuid = cuid;\n    Mfkey32LoggerParams_init(instance->params_arr);\n\n    return instance;\n}\n\nvoid mfkey32_logger_free(Mfkey32Logger* instance) {\n    furi_assert(instance);\n    furi_assert(instance->params_arr);\n\n    Mfkey32LoggerParams_clear(instance->params_arr);\n    free(instance);\n}\n\nstatic bool mfkey32_logger_add_nonce_to_existing_params(\n    Mfkey32Logger* instance,\n    MfClassicAuthContext* auth_context) {\n    bool nonce_added = false;\n    do {\n        if(Mfkey32LoggerParams_size(instance->params_arr) == 0) break;\n\n        Mfkey32LoggerParams_it_t it;\n        for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);\n            Mfkey32LoggerParams_next(it)) {\n            Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);\n            if(params->is_filled) continue;\n\n            uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num);\n            if(params->sector_num != sector_num) continue;\n            if(params->key_type != auth_context->key_type) continue;\n\n            params->nt1 = bit_lib_bytes_to_num_be(auth_context->nt.data, sizeof(MfClassicNt));\n            params->nr1 = bit_lib_bytes_to_num_be(auth_context->nr.data, sizeof(MfClassicNr));\n            params->ar1 = bit_lib_bytes_to_num_be(auth_context->ar.data, sizeof(MfClassicAr));\n            params->is_filled = true;\n\n            instance->params_collected++;\n            nonce_added = true;\n            break;\n        }\n\n    } while(false);\n\n    return nonce_added;\n}\n\nvoid mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context) {\n    furi_assert(instance);\n    furi_assert(auth_context);\n\n    bool nonce_added = mfkey32_logger_add_nonce_to_existing_params(instance, auth_context);\n    if(!nonce_added && (instance->nonces_saves < MFKEY32_LOGGER_MAX_NONCES_SAVED)) {\n        uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num);\n        Mfkey32LoggerParams params = {\n            .is_filled = false,\n            .cuid = instance->cuid,\n            .sector_num = sector_num,\n            .key_type = auth_context->key_type,\n            .nt0 = bit_lib_bytes_to_num_be(auth_context->nt.data, sizeof(MfClassicNt)),\n            .nr0 = bit_lib_bytes_to_num_be(auth_context->nr.data, sizeof(MfClassicNr)),\n            .ar0 = bit_lib_bytes_to_num_be(auth_context->ar.data, sizeof(MfClassicAr)),\n        };\n        Mfkey32LoggerParams_push_back(instance->params_arr, params);\n        instance->nonces_saves++;\n    }\n}\n\nsize_t mfkey32_logger_get_params_num(Mfkey32Logger* instance) {\n    furi_assert(instance);\n\n    return instance->params_collected;\n}\n\nbool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path) {\n    furi_assert(instance);\n    furi_assert(path);\n    furi_assert(instance->params_collected > 0);\n    furi_assert(instance->params_arr);\n\n    bool params_saved = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    Stream* stream = buffered_file_stream_alloc(storage);\n    FuriString* temp_str = furi_string_alloc();\n\n    do {\n        if(!buffered_file_stream_open(stream, path, FSAM_WRITE, FSOM_OPEN_APPEND)) break;\n\n        bool params_write_success = true;\n        Mfkey32LoggerParams_it_t it;\n        for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);\n            Mfkey32LoggerParams_next(it)) {\n            Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);\n            if(!params->is_filled) continue;\n            furi_string_printf(\n                temp_str,\n                \"Sec %d key %c cuid %08lx nt0 %08lx nr0 %08lx ar0 %08lx nt1 %08lx nr1 %08lx ar1 %08lx\\n\",\n                params->sector_num,\n                params->key_type == MfClassicKeyTypeA ? 'A' : 'B',\n                params->cuid,\n                params->nt0,\n                params->nr0,\n                params->ar0,\n                params->nt1,\n                params->nr1,\n                params->ar1);\n            if(!stream_write_string(stream, temp_str)) {\n                params_write_success = false;\n                break;\n            }\n        }\n        if(!params_write_success) break;\n\n        params_saved = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    buffered_file_stream_close(stream);\n    stream_free(stream);\n    furi_record_close(RECORD_STORAGE);\n\n    return params_saved;\n}\n\nvoid mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str) {\n    furi_assert(instance);\n    furi_assert(str);\n    furi_assert(instance->params_collected > 0);\n\n    furi_string_reset(str);\n    Mfkey32LoggerParams_it_t it;\n    for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);\n        Mfkey32LoggerParams_next(it)) {\n        Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);\n        if(!params->is_filled) continue;\n\n        char key_char = params->key_type == MfClassicKeyTypeA ? 'A' : 'B';\n        furi_string_cat_printf(str, \"Sector %d, key %c\\n\", params->sector_num, key_char);\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/mfkey32_logger.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_classic/mf_classic.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Mfkey32Logger Mfkey32Logger;\n\nMfkey32Logger* mfkey32_logger_alloc(uint32_t cuid);\n\nvoid mfkey32_logger_free(Mfkey32Logger* instance);\n\nvoid mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context);\n\nsize_t mfkey32_logger_get_params_num(Mfkey32Logger* instance);\n\nbool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path);\n\nvoid mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_custom_event.h",
    "content": "#pragma once\n\ntypedef enum {\n    // Reserve first 100 events for button types and indexes, starting from 0\n    NfcCustomEventReserved = 100,\n\n    // Mf classic dict attack events\n    NfcCustomEventDictAttackComplete,\n    NfcCustomEventDictAttackSkip,\n    NfcCustomEventDictAttackDataUpdate,\n\n    NfcCustomEventCardDetected,\n    NfcCustomEventCardLost,\n\n    NfcCustomEventViewExit,\n    NfcCustomEventWorkerExit,\n    NfcCustomEventWorkerUpdate,\n    NfcCustomEventWrongCard,\n    NfcCustomEventTimerExpired,\n    NfcCustomEventByteInputDone,\n    NfcCustomEventTextInputDone,\n    NfcCustomEventDictAttackDone,\n\n    NfcCustomEventRpcLoadFile,\n    NfcCustomEventRpcExit,\n    NfcCustomEventRpcSessionClose,\n\n    NfcCustomEventPollerSuccess,\n    NfcCustomEventPollerIncomplete,\n    NfcCustomEventPollerFailure,\n\n    NfcCustomEventListenerUpdate,\n} NfcCustomEvent;\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_detected_protocols.c",
    "content": "#include \"nfc_detected_protocols.h\"\n\n#include <furi.h>\n\nstruct NfcDetectedProtocols {\n    uint32_t protocols_detected_num;\n    NfcProtocol protocols_detected[NfcProtocolNum];\n    uint32_t selected_idx;\n};\n\nNfcDetectedProtocols* nfc_detected_protocols_alloc(void) {\n    NfcDetectedProtocols* instance = malloc(sizeof(NfcDetectedProtocols));\n\n    instance->protocols_detected_num = 0;\n    instance->selected_idx = 0;\n\n    return instance;\n}\n\nvoid nfc_detected_protocols_free(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    free(instance);\n}\n\nvoid nfc_detected_protocols_reset(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    instance->protocols_detected_num = 0;\n    memset(instance->protocols_detected, 0, sizeof(instance->protocols_detected));\n    instance->selected_idx = 0;\n}\n\nvoid nfc_detected_protocols_select(NfcDetectedProtocols* instance, uint32_t idx) {\n    furi_assert(instance);\n\n    instance->selected_idx = idx;\n}\n\nvoid nfc_detected_protocols_set(\n    NfcDetectedProtocols* instance,\n    const NfcProtocol* types,\n    uint32_t count) {\n    furi_assert(instance);\n    furi_assert(types);\n    furi_assert(count < NfcProtocolNum);\n\n    memcpy(instance->protocols_detected, types, count);\n    instance->protocols_detected_num = count;\n    instance->selected_idx = 0;\n}\n\nuint32_t nfc_detected_protocols_get_num(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    return instance->protocols_detected_num;\n}\n\nNfcProtocol nfc_detected_protocols_get_protocol(NfcDetectedProtocols* instance, uint32_t idx) {\n    furi_assert(instance);\n    furi_assert(idx < instance->protocols_detected_num);\n\n    return instance->protocols_detected[idx];\n}\n\nvoid nfc_detected_protocols_fill_all_protocols(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    instance->protocols_detected_num = NfcProtocolNum;\n    for(uint32_t i = 0; i < NfcProtocolNum; i++) {\n        instance->protocols_detected[i] = i;\n    }\n}\n\nNfcProtocol nfc_detected_protocols_get_selected(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    return instance->protocols_detected[instance->selected_idx];\n}\n\nuint32_t nfc_detected_protocols_get_selected_idx(NfcDetectedProtocols* instance) {\n    furi_assert(instance);\n\n    return instance->selected_idx;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_detected_protocols.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <nfc/protocols/nfc_protocol.h>\n\ntypedef struct NfcDetectedProtocols NfcDetectedProtocols;\n\nNfcDetectedProtocols* nfc_detected_protocols_alloc(void);\n\nvoid nfc_detected_protocols_free(NfcDetectedProtocols* instance);\n\nvoid nfc_detected_protocols_reset(NfcDetectedProtocols* instance);\n\nvoid nfc_detected_protocols_select(NfcDetectedProtocols* instance, uint32_t idx);\n\nvoid nfc_detected_protocols_set(\n    NfcDetectedProtocols* instance,\n    const NfcProtocol* types,\n    uint32_t count);\n\nuint32_t nfc_detected_protocols_get_num(NfcDetectedProtocols* instance);\n\nNfcProtocol nfc_detected_protocols_get_protocol(NfcDetectedProtocols* instance, uint32_t idx);\n\nvoid nfc_detected_protocols_fill_all_protocols(NfcDetectedProtocols* instance);\n\nNfcProtocol nfc_detected_protocols_get_selected(NfcDetectedProtocols* instance);\n\nuint32_t nfc_detected_protocols_get_selected_idx(NfcDetectedProtocols* instance);\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_emv_parser.c",
    "content": "#include \"nfc_emv_parser.h\"\n#include <flipper_format/flipper_format.h>\n\nstatic const char* nfc_resources_header = \"Flipper EMV resources\";\nstatic const uint32_t nfc_resources_file_version = 1;\n\nstatic bool nfc_emv_parser_search_data(\n    Storage* storage,\n    const char* file_name,\n    FuriString* key,\n    FuriString* data) {\n    bool parsed = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n\n    do {\n        // Open file\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        // Read file header and version\n        uint32_t version = 0;\n        if(!flipper_format_read_header(file, temp_str, &version)) break;\n        if(furi_string_cmp_str(temp_str, nfc_resources_header) ||\n           (version != nfc_resources_file_version))\n            break;\n        if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break;\n        parsed = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    flipper_format_free(file);\n    return parsed;\n}\n\nbool nfc_emv_parser_get_aid_name(\n    Storage* storage,\n    uint8_t* aid,\n    uint8_t aid_len,\n    FuriString* aid_name) {\n    furi_assert(storage);\n    bool parsed = false;\n    FuriString* key;\n    key = furi_string_alloc();\n    for(uint8_t i = 0; i < aid_len; i++) {\n        furi_string_cat_printf(key, \"%02X\", aid[i]);\n    }\n    if(nfc_emv_parser_search_data(storage, EXT_PATH(\"nfc/assets/aid.nfc\"), key, aid_name)) {\n        parsed = true;\n    }\n    furi_string_free(key);\n    return parsed;\n}\n\nbool nfc_emv_parser_get_country_name(\n    Storage* storage,\n    uint16_t country_code,\n    FuriString* country_name) {\n    bool parsed = false;\n    FuriString* key;\n    key = furi_string_alloc_printf(\"%04X\", country_code);\n    if(nfc_emv_parser_search_data(\n           storage, EXT_PATH(\"nfc/assets/country_code.nfc\"), key, country_name)) {\n        parsed = true;\n    }\n    furi_string_free(key);\n    return parsed;\n}\n\nbool nfc_emv_parser_get_currency_name(\n    Storage* storage,\n    uint16_t currency_code,\n    FuriString* currency_name) {\n    bool parsed = false;\n    FuriString* key;\n    key = furi_string_alloc_printf(\"%04X\", currency_code);\n    if(nfc_emv_parser_search_data(\n           storage, EXT_PATH(\"nfc/assets/currency_code.nfc\"), key, currency_name)) {\n        parsed = true;\n    }\n    furi_string_free(key);\n    return parsed;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_emv_parser.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <storage/storage.h>\n\n/** Get EMV application name by number\n * @param storage Storage instance\n * @param aid - AID number array\n * @param aid_len - AID length\n * @param aid_name - string to keep AID name\n * @return - true if AID found, false otherwies\n */\nbool nfc_emv_parser_get_aid_name(\n    Storage* storage,\n    uint8_t* aid,\n    uint8_t aid_len,\n    FuriString* aid_name);\n\n/** Get country name by country code\n * @param storage Storage instance\n * @param country_code - ISO 3166 country code\n * @param country_name - string to keep country name\n * @return - true if country found, false otherwies\n */\nbool nfc_emv_parser_get_country_name(\n    Storage* storage,\n    uint16_t country_code,\n    FuriString* country_name);\n\n/** Get currency name by currency code\n * @param storage Storage instance\n * @param currency_code - ISO 3166 currency code\n * @param currency_name - string to keep currency name\n * @return - true if currency found, false otherwies\n */\nbool nfc_emv_parser_get_currency_name(\n    Storage* storage,\n    uint16_t currency_code,\n    FuriString* currency_name);\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_supported_cards.c",
    "content": "#include \"nfc_supported_cards.h\"\n#include \"../api/nfc_app_api_interface.h\"\n\n#include \"../plugins/supported_cards/nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <flipper_application/plugins/plugin_manager.h>\n#include <flipper_application/plugins/composite_resolver.h>\n#include <loader/firmware_api/firmware_api.h>\n\n#include <furi.h>\n#include <path.h>\n#include <m-array.h>\n\n#define TAG \"NfcSupportedCards\"\n\n#define NFC_SUPPORTED_CARDS_PLUGINS_PATH  APP_DATA_PATH(\"plugins\")\n#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX \"_parser.fal\"\n\ntypedef enum {\n    NfcSupportedCardsPluginFeatureHasVerify = (1U << 0),\n    NfcSupportedCardsPluginFeatureHasRead = (1U << 1),\n    NfcSupportedCardsPluginFeatureHasParse = (1U << 2),\n} NfcSupportedCardsPluginFeature;\n\ntypedef struct {\n    FuriString* name;\n    NfcProtocol protocol;\n    NfcSupportedCardsPluginFeature feature;\n} NfcSupportedCardsPluginCache;\n\nARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST); //-V658\n\ntypedef enum {\n    NfcSupportedCardsLoadStateIdle,\n    NfcSupportedCardsLoadStateInProgress,\n    NfcSupportedCardsLoadStateSuccess,\n    NfcSupportedCardsLoadStateFail,\n} NfcSupportedCardsLoadState;\n\ntypedef struct {\n    Storage* storage;\n    File* directory;\n    char file_name[256];\n    FlipperApplication* app;\n} NfcSupportedCardsLoadContext;\n\nstruct NfcSupportedCards {\n    CompositeApiResolver* api_resolver;\n    NfcSupportedCardsPluginCache_t plugins_cache_arr;\n    NfcSupportedCardsLoadState load_state;\n    NfcSupportedCardsLoadContext* load_context;\n};\n\nNfcSupportedCards* nfc_supported_cards_alloc(void) {\n    NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));\n\n    instance->api_resolver = composite_api_resolver_alloc();\n    composite_api_resolver_add(instance->api_resolver, firmware_api_interface);\n    composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface);\n\n    NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);\n\n    return instance;\n}\n\nvoid nfc_supported_cards_free(NfcSupportedCards* instance) {\n    furi_assert(instance);\n\n    NfcSupportedCardsPluginCache_it_t iter;\n    for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);\n        !NfcSupportedCardsPluginCache_end_p(iter);\n        NfcSupportedCardsPluginCache_next(iter)) {\n        NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);\n        furi_string_free(plugin_cache->name);\n    }\n    NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);\n\n    composite_api_resolver_free(instance->api_resolver);\n    free(instance);\n}\n\nstatic NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc(void) {\n    NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));\n\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    instance->directory = storage_file_alloc(instance->storage);\n\n    if(!storage_dir_open(instance->directory, NFC_SUPPORTED_CARDS_PLUGINS_PATH)) {\n        FURI_LOG_D(TAG, \"Failed to open directory: %s\", NFC_SUPPORTED_CARDS_PLUGINS_PATH);\n    }\n\n    return instance;\n}\n\nstatic void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) {\n    if(instance->app) {\n        flipper_application_free(instance->app);\n    }\n\n    storage_dir_close(instance->directory);\n    storage_file_free(instance->directory);\n\n    furi_record_close(RECORD_STORAGE);\n    free(instance);\n}\n\nstatic const NfcSupportedCardsPlugin* nfc_supported_cards_get_plugin(\n    NfcSupportedCardsLoadContext* instance,\n    const char* name,\n    const ElfApiInterface* api_interface) {\n    furi_assert(instance);\n    furi_assert(name);\n\n    const NfcSupportedCardsPlugin* plugin = NULL;\n    FuriString* plugin_path = furi_string_alloc_printf(\n        \"%s/%s%s\", NFC_SUPPORTED_CARDS_PLUGINS_PATH, name, NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX);\n    do {\n        if(instance->app) flipper_application_free(instance->app);\n        instance->app = flipper_application_alloc(instance->storage, api_interface);\n\n        if(flipper_application_preload(instance->app, furi_string_get_cstr(plugin_path)) !=\n           FlipperApplicationPreloadStatusSuccess)\n            break;\n        if(!flipper_application_is_plugin(instance->app)) break;\n        if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)\n            break;\n        const FlipperAppPluginDescriptor* descriptor =\n            flipper_application_plugin_get_descriptor(instance->app);\n\n        if(descriptor == NULL) break;\n\n        if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break;\n        if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break;\n\n        plugin = descriptor->entry_point;\n    } while(false);\n    furi_string_free(plugin_path);\n\n    return plugin;\n}\n\nstatic const NfcSupportedCardsPlugin* nfc_supported_cards_get_next_plugin(\n    NfcSupportedCardsLoadContext* instance,\n    const ElfApiInterface* api_interface) {\n    const NfcSupportedCardsPlugin* plugin = NULL;\n\n    do {\n        if(!storage_file_is_open(instance->directory)) break;\n        if(!storage_dir_read(\n               instance->directory, NULL, instance->file_name, sizeof(instance->file_name)))\n            break;\n\n        const size_t suffix_len = strlen(NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX);\n        const size_t file_name_len = strlen(instance->file_name);\n        if(file_name_len <= suffix_len) break;\n\n        size_t suffix_start_pos = file_name_len - suffix_len;\n        if(memcmp(\n               &instance->file_name[suffix_start_pos],\n               NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX,\n               suffix_len) != 0) //-V1051\n            break;\n\n        // Trim suffix from file_name to save memory. The suffix will be concatenated on plugin load.\n        instance->file_name[suffix_start_pos] = '\\0';\n\n        plugin = nfc_supported_cards_get_plugin(instance, instance->file_name, api_interface);\n    } while(plugin == NULL); //-V654\n\n    return plugin;\n}\n\nvoid nfc_supported_cards_load_cache(NfcSupportedCards* instance) {\n    furi_assert(instance);\n\n    do {\n        if((instance->load_state == NfcSupportedCardsLoadStateSuccess) ||\n           (instance->load_state == NfcSupportedCardsLoadStateFail))\n            break;\n\n        instance->load_context = nfc_supported_cards_load_context_alloc();\n\n        while(true) {\n            const ElfApiInterface* api_interface =\n                composite_api_resolver_get(instance->api_resolver);\n            const NfcSupportedCardsPlugin* plugin =\n                nfc_supported_cards_get_next_plugin(instance->load_context, api_interface);\n            if(plugin == NULL) break; //-V547\n\n            NfcSupportedCardsPluginCache plugin_cache = {}; //-V779\n            plugin_cache.name = furi_string_alloc_set(instance->load_context->file_name);\n            plugin_cache.protocol = plugin->protocol;\n            if(plugin->verify) {\n                plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify;\n            }\n            if(plugin->read) {\n                plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead;\n            }\n            if(plugin->parse) {\n                plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse;\n            }\n            NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache);\n        }\n\n        nfc_supported_cards_load_context_free(instance->load_context);\n\n        size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr);\n        if(plugins_loaded == 0) {\n            FURI_LOG_D(TAG, \"Plugins not found\");\n            instance->load_state = NfcSupportedCardsLoadStateFail;\n        } else {\n            FURI_LOG_D(TAG, \"Loaded %zu plugins\", plugins_loaded);\n            instance->load_state = NfcSupportedCardsLoadStateSuccess;\n        }\n\n    } while(false);\n}\n\nbool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) {\n    furi_assert(instance);\n    furi_assert(device);\n    furi_assert(nfc);\n\n    bool card_read = false;\n    NfcProtocol protocol = nfc_device_get_protocol(device);\n\n    do {\n        if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;\n\n        instance->load_context = nfc_supported_cards_load_context_alloc();\n\n        NfcSupportedCardsPluginCache_it_t iter;\n        for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);\n            !NfcSupportedCardsPluginCache_end_p(iter);\n            NfcSupportedCardsPluginCache_next(iter)) {\n            NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);\n            if(plugin_cache->protocol != protocol) continue;\n            if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue;\n\n            const ElfApiInterface* api_interface =\n                composite_api_resolver_get(instance->api_resolver);\n            const NfcSupportedCardsPlugin* plugin = nfc_supported_cards_get_plugin(\n                instance->load_context, furi_string_get_cstr(plugin_cache->name), api_interface);\n            if(plugin == NULL) continue;\n\n            if(plugin->verify) {\n                if(!plugin->verify(nfc)) continue;\n            }\n\n            if(plugin->read) {\n                if(plugin->read(nfc, device)) {\n                    card_read = true;\n                    break;\n                }\n            }\n        }\n\n        nfc_supported_cards_load_context_free(instance->load_context);\n    } while(false);\n\n    return card_read;\n}\n\nbool nfc_supported_cards_parse(\n    NfcSupportedCards* instance,\n    NfcDevice* device,\n    FuriString* parsed_data) {\n    furi_assert(instance);\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    bool card_parsed = false;\n    NfcProtocol protocol = nfc_device_get_protocol(device);\n\n    do {\n        if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;\n\n        instance->load_context = nfc_supported_cards_load_context_alloc();\n\n        NfcSupportedCardsPluginCache_it_t iter;\n        for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);\n            !NfcSupportedCardsPluginCache_end_p(iter);\n            NfcSupportedCardsPluginCache_next(iter)) {\n            NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);\n            if(plugin_cache->protocol != protocol) continue;\n            if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue;\n\n            const ElfApiInterface* api_interface =\n                composite_api_resolver_get(instance->api_resolver);\n            const NfcSupportedCardsPlugin* plugin = nfc_supported_cards_get_plugin(\n                instance->load_context, furi_string_get_cstr(plugin_cache->name), api_interface);\n            if(plugin == NULL) continue;\n\n            if(plugin->parse) {\n                if(plugin->parse(device, parsed_data)) {\n                    card_parsed = true;\n                    break;\n                }\n            }\n        }\n\n        nfc_supported_cards_load_context_free(instance->load_context);\n    } while(false);\n\n    return card_parsed;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/nfc_supported_cards.h",
    "content": "/**\n * @file nfc_supported_cards.h\n * @brief Supported card plugin loader interface.\n *\n * @see nfc_supported_card_plugin.h for instructions on adding a new plugin.\n */\n#pragma once\n\n#include <core/string.h>\n\n#include <nfc/nfc.h>\n#include <nfc/nfc_device.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcSupportedCards opaque type definition.\n */\ntypedef struct NfcSupportedCards NfcSupportedCards;\n\n/**\n * @brief Allocate NfcSupportedCards instance.\n *\n * @return pointer to allocated NfcSupportedCards instance.\n */\nNfcSupportedCards* nfc_supported_cards_alloc(void);\n\n/**\n * @brief Delete an NfcSupportedCards instance\n * \n * @param[in] instance pointer to instance to be deleted.\n */\nvoid nfc_supported_cards_free(NfcSupportedCards* instance);\n\n/**\n * @brief Load plugins information to cache.\n *\n * @note This function must be called before calling read and parse fanctions.\n *\n * @param[in, out] instance pointer to NfcSupportedCards instance.\n */\nvoid nfc_supported_cards_load_cache(NfcSupportedCards* instance);\n\n/**\n * @brief Read the card using a custom procedure.\n *\n * This function will load all suitable supported card plugins one by one and\n * try to execute the custom read procedure specified in each. Upon first success,\n * no further attempts will be made and the function will return.\n *\n * @param[in, out] instance pointer to NfcSupportedCards instance.\n * @param[in,out] device pointer to a device instance to hold the read data.\n * @param[in,out] nfc pointer to an Nfc instance.\n * @returns true if the card was successfully read, false otherwise.\n *\n * @see NfcSupportedCardPluginRead for detailed description.\n */\nbool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc);\n\n/**\n * @brief Parse raw data into human-readable representation.\n *\n * This function will load all suitable supported card plugins one by one and\n * try to parse the data according to each implementation. Upon first success,\n * no further attempts will be made and the function will return.\n *\n * @param[in, out] instance pointer to NfcSupportedCards instance.\n * @param[in] device pointer to a device instance holding the data is to be parsed.\n * @param[out] parsed_data pointer to the string to contain the formatted result.\n * @returns true if the card was successfully parsed, false otherwise.\n *\n * @see NfcSupportedCardPluginParse for detailed description.\n */\nbool nfc_supported_cards_parse(\n    NfcSupportedCards* instance,\n    NfcDevice* device,\n    FuriString* parsed_data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/felica/felica.c",
    "content": "#include \"felica.h\"\n#include \"felica_render.h\"\n\n#include <nfc/protocols/felica/felica_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n#include \"../nfc_protocol_support_unlock_helper.h\"\n\nenum {\n    SubmenuIndexUnlock = SubmenuIndexCommonMax,\n};\n\nstatic void nfc_scene_info_on_enter_felica(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);\n        return true;\n    }\n\n    return false;\n}\n\nstatic void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {\n    // Jump to advanced scene right away\n    scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolFelica);\n\n    NfcApp* instance = context;\n    const FelicaPollerEvent* felica_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(felica_event->type == FelicaPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    } else if(\n        felica_event->type == FelicaPollerEventTypeError ||\n        felica_event->type == FelicaPollerEventTypeIncomplete) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventPollerIncomplete);\n        command = NfcCommandStop;\n    } else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n        FelicaAuthenticationContext* ctx = felica_event->data->auth_context;\n        ctx->skip_auth = instance->felica_auth->skip_auth;\n        memcpy(ctx->card_key.data, instance->felica_auth->card_key.data, FELICA_DATA_BLOCK_SIZE);\n    }\n\n    return command;\n}\n\nstatic void nfc_scene_read_on_enter_felica(NfcApp* instance) {\n    nfc_unlock_helper_setup_from_state(instance);\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);\n}\n\nbool nfc_scene_read_on_event_felica(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventCardDetected) {\n            nfc_unlock_helper_card_detected_handler(instance);\n        } else if(event.event == NfcCustomEventPollerIncomplete) {\n            notification_message(instance->notifications, &sequence_semi_success);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n            dolphin_deed(DolphinDeedNfcReadSuccess);\n        }\n    }\n    return true;\n}\n\nstatic void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);\n\n    FuriString* temp_str = furi_string_alloc();\n\n    if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn)) {\n        furi_string_cat_printf(\n            temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n        nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);\n    } else {\n        if(data->workflow_type == FelicaLite) {\n            bool all_unlocked = data->blocks_read == data->blocks_total;\n            furi_string_cat_printf(\n                temp_str,\n                \"\\e#%s\\n\",\n                all_unlocked ? \"All Blocks Are Unlocked\" : \"Some Blocks Are Locked\");\n            nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);\n            uint8_t* ck_data = instance->felica_auth->card_key.data;\n            furi_string_cat_printf(temp_str, \"Key:\");\n            for(uint8_t i = 0; i < 7; i++) {\n                furi_string_cat_printf(temp_str, \" %02X\", ck_data[i]);\n                if(i == 6) furi_string_cat_printf(temp_str, \"...\");\n            }\n            nfc_render_felica_blocks_count(data, temp_str, false);\n        }\n    }\n    felica_auth_reset(instance->felica_auth);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n        return true;\n    }\n\n    return false;\n}\n\nstatic void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {\n    const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data);\n    nfc_listener_start(instance->listener, NULL, NULL);\n}\n\nstatic void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) {\n    const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);\n    if(data->blocks_read != data->blocks_total) {\n        submenu_add_item(\n            instance->submenu,\n            \"Unlock\",\n            SubmenuIndexUnlock,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n}\n\nstatic bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexUnlock) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneDesAuthKeyInput);\n            return true;\n        }\n    }\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_felica = {\n    .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_felica,\n            .on_event = nfc_scene_info_on_event_felica,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_felica,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_felica,\n            .on_event = nfc_scene_read_on_event_felica,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_scene_read_menu_on_enter_felica,\n            .on_event = nfc_scene_read_menu_on_event_felica,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_felica,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_saved_menu_on_event_felica,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_felica,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/felica/felica.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_felica;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/felica/felica_render.c",
    "content": "#include \"felica_render.h\"\n\nvoid nfc_render_felica_blocks_count(\n    const FelicaData* data,\n    FuriString* str,\n    bool render_auth_notification) {\n    if(data->workflow_type == FelicaLite) {\n        furi_string_cat_printf(str, \"Blocks: %u\\n\", data->blocks_total);\n        furi_string_cat_printf(str, \"\\nBlocks Read: %u/%u\", data->blocks_read, data->blocks_total);\n        if(render_auth_notification && data->blocks_read != data->blocks_total) {\n            furi_string_cat_printf(str, \"\\nAuth-protected blocks!\");\n        }\n    }\n}\n\nvoid nfc_render_felica_idm(\n    const FelicaData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    furi_string_cat_printf(str, (format_type == NfcProtocolFormatTypeFull) ? \"IDm:\\n\" : \"IDm:\");\n\n    for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {\n        furi_string_cat_printf(\n            str,\n            (format_type == NfcProtocolFormatTypeFull) ? \"%02X \" : \" %02X\",\n            data->idm.data[i]);\n    }\n}\n\nvoid nfc_render_felica_info(\n    const FelicaData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    if(format_type == NfcProtocolFormatTypeFull) {\n        furi_string_cat_printf(str, \"Tech: JIS X 6319-4,\\nISO 18092 [NFC-F]\\n\");\n    }\n\n    FuriString* ic_type_str = furi_string_alloc();\n    felica_get_ic_name(data, ic_type_str);\n    furi_string_cat_printf(str, \"IC Type:\\n%s\\n\", furi_string_get_cstr(ic_type_str));\n    furi_string_free(ic_type_str);\n\n    nfc_render_felica_idm(data, format_type, str);\n\n    if(format_type == NfcProtocolFormatTypeFull) {\n        furi_string_cat_printf(str, \"\\nPMm:\\n\");\n        for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {\n            furi_string_cat_printf(str, \"%02X \", data->pmm.data[i]);\n        }\n    }\n\n    furi_string_cat_printf(str, \"\\n\");\n    furi_string_cat_printf(str, \"Systems found: %lu \\n\", simple_array_get_count(data->systems));\n\n    nfc_render_felica_blocks_count(data, str, true);\n}\n\nstatic void nfc_render_felica_block_name(\n    const char* name,\n    FuriString* str,\n    uint8_t prefix_separator_cnt,\n    uint8_t suffix_separator_cnt) {\n    for(uint8_t i = 0; i < prefix_separator_cnt; i++) {\n        furi_string_cat_printf(str, \":\");\n    }\n    furi_string_cat_printf(str, \"[ %s ]\", name);\n    for(uint8_t i = 0; i < suffix_separator_cnt; i++) {\n        furi_string_cat_printf(str, \":\");\n    }\n}\n\nstatic void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {\n    furi_string_cat_printf(str, \"\\nSF1=%02X; SF2=%02X\\n\", block->SF1, block->SF2);\n    for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {\n        furi_string_cat_printf(str, \"%02X%02X \", block->data[i], block->data[i + 1]);\n    }\n    furi_string_cat_printf(str, \"\\n\");\n}\n\nstatic void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {\n    for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {\n        furi_string_cat_printf(str, \"%02X%02X \", block->data[i], block->data[i + 1]);\n    }\n}\n\nstatic void nfc_render_felica_block(\n    const FelicaBlock* block,\n    FuriString* str,\n    const char* name,\n    uint8_t prefix_separator_cnt,\n    uint8_t suffix_separator_cnt) {\n    nfc_render_felica_block_name(name, str, prefix_separator_cnt, suffix_separator_cnt);\n    nfc_render_felica_block_data(block, str);\n}\n\nvoid nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {\n    FuriString* name = furi_string_alloc();\n\n    furi_string_cat_printf(str, \"\\e#Blocks read:\\n\");\n\n    furi_string_cat_printf(str, \"Blocks: %u\\n\", data->blocks_total);\n\n    for(size_t i = 0; i < 14; i++) {\n        furi_string_printf(name, \"S_PAD%d\", i);\n        uint8_t suf_cnt = 18;\n        if(i == 1) {\n            suf_cnt = 19;\n        } else if((i == 10) || (i == 12) || (i == 13)) {\n            suf_cnt = 16;\n        }\n        nfc_render_felica_block(\n            &data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);\n    }\n    furi_string_free(name);\n    nfc_render_felica_block(&data->data.fs.reg, str, \"REG\", 23, 23);\n    nfc_render_felica_block(&data->data.fs.rc, str, \"RC\", 25, 25);\n    nfc_render_felica_block(&data->data.fs.mac, str, \"MAC\", 23, 23);\n    nfc_render_felica_block(&data->data.fs.id, str, \"ID\", 25, 25);\n    nfc_render_felica_block(&data->data.fs.d_id, str, \"D_ID\", 22, 24);\n    nfc_render_felica_block(&data->data.fs.ser_c, str, \"SER_C\", 20, 21);\n    nfc_render_felica_block(&data->data.fs.sys_c, str, \"SYS_C\", 20, 21);\n    nfc_render_felica_block(&data->data.fs.ckv, str, \"CKV\", 23, 23);\n    nfc_render_felica_block(&data->data.fs.ck, str, \"CK\", 25, 25);\n    nfc_render_felica_block(&data->data.fs.mc, str, \"MC\", 25, 24);\n    nfc_render_felica_block(&data->data.fs.wcnt, str, \"WCNT\", 22, 20);\n    nfc_render_felica_block(&data->data.fs.mac_a, str, \"MAC_A\", 20, 20);\n    nfc_render_felica_block(&data->data.fs.state, str, \"STATE\", 20, 21);\n    nfc_render_felica_block(&data->data.fs.crc_check, str, \"CRC_CHCK\", 15, 17);\n}\n\nvoid nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str) {\n    const size_t area_count = simple_array_get_count(system->areas);\n    const size_t service_count = simple_array_get_count(system->services);\n\n    furi_string_cat_printf(str, \"\\e#Directory Tree:\\n\");\n\n    if(area_count == 0 || service_count == 0) {\n        furi_string_cat_printf(str, \"No services or areas found.\\n\");\n    } else {\n        furi_string_cat_printf(\n            str, \"%zu areas found.\\n%zu services found.\\n\\n\", area_count, service_count);\n        furi_string_cat_printf(\n            str, \"::: ... are readable services\\n||| ... are locked services\\n\");\n    }\n    felica_write_directory_tree(system, str);\n}\n\nvoid nfc_more_info_render_felica_blocks(\n    const FelicaData* data,\n    const FelicaSystem* system,\n    FuriString* str,\n    const uint16_t service_code_key) {\n    furi_string_cat_printf(str, \"\\n\");\n    if(data->workflow_type == FelicaLite) {\n        furi_string_cat_printf(str, \"Blocks: %u\\n\", data->blocks_total);\n        FuriString* name = furi_string_alloc();\n\n        for(size_t i = 0; i < 14; i++) {\n            furi_string_printf(name, \"S_PAD%d\", i);\n            uint8_t suf_cnt = 18;\n            if(i == 1) {\n                suf_cnt = 19;\n            } else if((i == 10) || (i == 12) || (i == 13)) {\n                suf_cnt = 16;\n            }\n            nfc_render_felica_block(\n                &data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);\n        }\n        furi_string_free(name);\n        nfc_render_felica_block(&data->data.fs.reg, str, \"REG\", 23, 23);\n        nfc_render_felica_block(&data->data.fs.rc, str, \"RC\", 25, 25);\n        nfc_render_felica_block(&data->data.fs.mac, str, \"MAC\", 23, 23);\n        nfc_render_felica_block(&data->data.fs.id, str, \"ID\", 25, 25);\n        nfc_render_felica_block(&data->data.fs.d_id, str, \"D_ID\", 22, 24);\n        nfc_render_felica_block(&data->data.fs.ser_c, str, \"SER_C\", 20, 21);\n        nfc_render_felica_block(&data->data.fs.sys_c, str, \"SYS_C\", 20, 21);\n        nfc_render_felica_block(&data->data.fs.ckv, str, \"CKV\", 23, 23);\n        nfc_render_felica_block(&data->data.fs.ck, str, \"CK\", 25, 25);\n        nfc_render_felica_block(&data->data.fs.mc, str, \"MC\", 25, 24);\n        nfc_render_felica_block(&data->data.fs.wcnt, str, \"WCNT\", 22, 20);\n        nfc_render_felica_block(&data->data.fs.mac_a, str, \"MAC_A\", 20, 20);\n        nfc_render_felica_block(&data->data.fs.state, str, \"STATE\", 20, 21);\n        nfc_render_felica_block(&data->data.fs.crc_check, str, \"CRC_CHCK\", 15, 17);\n\n    } else if(data->workflow_type == FelicaStandard) {\n        uint32_t public_blocks_count = simple_array_get_count(system->public_blocks);\n        for(size_t i = 0; i < public_blocks_count; i++) {\n            FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);\n            if(public_block->service_code != service_code_key) {\n                continue; // Skip blocks not matching the requested service code\n            }\n            furi_string_cat_printf(str, \"-----Block 0x%02X-----\\n\", public_block->block_idx);\n            nfc_render_felica_block_data_simple(&public_block->block, str);\n            furi_string_cat_printf(str, \"\\n\");\n        }\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/felica/felica_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/felica/felica.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_felica_blocks_count(\n    const FelicaData* data,\n    FuriString* str,\n    bool render_auth_notification);\n\nvoid nfc_render_felica_info(\n    const FelicaData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);\n\nvoid nfc_render_felica_idm(\n    const FelicaData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str);\n\nvoid nfc_more_info_render_felica_blocks(\n    const FelicaData* data,\n    const FelicaSystem* system,\n    FuriString* str,\n    const uint16_t service_code_key);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c",
    "content": "#include \"iso14443_3a.h\"\n#include \"iso14443_3a_render.h\"\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_read_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    NfcApp* instance = context;\n    const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_iso14443_3a(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3a, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_iso14443_3a(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_emulate_listener_callback_iso14443_3a(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n    furi_assert(event.event_data);\n\n    NfcApp* nfc = context;\n    Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;\n\n    if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {\n        if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {\n            furi_string_cat_printf(nfc->text_box_store, \"R:\");\n            for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer);\n                i++) {\n                furi_string_cat_printf(\n                    nfc->text_box_store,\n                    \" %02X\",\n                    bit_buffer_get_byte(iso14443_3a_event->data->buffer, i));\n            }\n            furi_string_push_back(nfc->text_box_store, '\\n');\n            view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);\n        }\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) {\n    const Iso14443_3aData* data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a);\n\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_3a, data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);\n}\n\nstatic bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);\n        return true;\n    }\n\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {\n    .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_iso14443_3a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_iso14443_3a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_read_menu_on_event_iso14443_3a,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_iso14443_3a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_iso14443_3a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c",
    "content": "#include \"iso14443_3a_render.h\"\n\nvoid nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* data, size_t size) {\n    for(size_t i = 0; i < size; i++) {\n        furi_string_cat_printf(str, \" %02X\", data[i]);\n    }\n}\n\nvoid nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str) {\n    const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3';\n    furi_string_cat_printf(str, \"Tech: ISO 14443-%c (NFC-A)\\n\", iso_type);\n}\n\nvoid nfc_render_iso14443_3a_info(\n    const Iso14443_3aData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    if(format_type == NfcProtocolFormatTypeFull) {\n        nfc_render_iso14443_tech_type(data, str);\n    }\n\n    nfc_render_iso14443_3a_brief(data, str);\n\n    if(format_type == NfcProtocolFormatTypeFull) {\n        nfc_render_iso14443_3a_extra(data, str);\n    }\n}\n\nvoid nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str) {\n    furi_string_cat_printf(str, \"UID:\");\n\n    nfc_render_iso14443_3a_format_bytes(str, data->uid, data->uid_len);\n}\n\nvoid nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) {\n    furi_string_cat_printf(str, \"\\nATQA: %02X %02X  \", data->atqa[1], data->atqa[0]);\n    furi_string_cat_printf(str, \"\\nSAK: %02X\", data->sak);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_iso14443_3a_info(\n    const Iso14443_3aData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str);\n\nvoid nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* data, size_t size);\n\nvoid nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str);\n\nvoid nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c",
    "content": "#include \"iso14443_3b.h\"\n#include \"iso14443_3b_render.h\"\n\n#include <nfc/protocols/iso14443_3b/iso14443_3b_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_read_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3b);\n\n    NfcApp* instance = context;\n    const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;\n\n    if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_iso14443_3b(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3b, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nbool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n        return true;\n    }\n\n    return false;\n}\n\nstatic bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) {\n    return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {\n    .features = NfcProtocolFeatureNone,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_iso14443_3b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_iso14443_3b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_iso14443_3b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_saved_menu_on_event_iso14443_3b,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3b/iso14443_3b.h>\n\n#include \"iso14443_3b.h\"\n\nbool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c",
    "content": "#include \"iso14443_3b_render.h\"\n\nvoid nfc_render_iso14443_3b_info(\n    const Iso14443_3bData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    if(format_type == NfcProtocolFormatTypeFull) {\n        const char iso_type = iso14443_3b_supports_iso14443_4(data) ? '4' : '3';\n        furi_string_cat_printf(str, \"Tech: ISO 14443-%c (NFC-B)\\n\", iso_type);\n    }\n\n    furi_string_cat_printf(str, \"UID:\");\n\n    size_t uid_size;\n    const uint8_t* uid = iso14443_3b_get_uid(data, &uid_size);\n\n    for(size_t i = 0; i < uid_size; i++) {\n        furi_string_cat_printf(str, \" %02X\", uid[i]);\n    }\n\n    if(format_type != NfcProtocolFormatTypeFull) return;\n\n    furi_string_cat_printf(str, \"\\n::::::::::::::::[Protocol info]:::::::::::::::\\n\");\n\n    if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRateBoth106Kbit)) {\n        furi_string_cat(str, \"Bit rate PICC <-> PCD:\\n  106 kBit/s supported\\n\");\n    } else {\n        furi_string_cat(str, \"Bit rate PICC -> PCD:\\n\");\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd212Kbit)) {\n            furi_string_cat(str, \"  212 kBit/s supported\\n\");\n        }\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd424Kbit)) {\n            furi_string_cat(str, \"  424 kBit/s supported\\n\");\n        }\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd848Kbit)) {\n            furi_string_cat(str, \"  848 kBit/s supported\\n\");\n        }\n\n        furi_string_cat(str, \"Bit rate PICC <- PCD:\\n\");\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc212Kbit)) {\n            furi_string_cat(str, \"  212 kBit/s supported\\n\");\n        }\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc424Kbit)) {\n            furi_string_cat(str, \"  424 kBit/s supported\\n\");\n        }\n        if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc848Kbit)) {\n            furi_string_cat(str, \"  848 kBit/s supported\\n\");\n        }\n    }\n\n    furi_string_cat(str, \"Max frame size: \");\n\n    const uint16_t max_frame_size = iso14443_3b_get_frame_size_max(data);\n    if(max_frame_size != 0) {\n        furi_string_cat_printf(str, \"%u bytes\\n\", max_frame_size);\n    } else {\n        furi_string_cat(str, \"? (RFU)\\n\");\n    }\n\n    const double fwt = iso14443_3b_get_fwt_fc_max(data) / 13.56e6;\n    furi_string_cat_printf(str, \"Max waiting time: %4.2g s\\n\", fwt);\n\n    const char* nad_support_str =\n        iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionNad) ? \"\" : \"not \";\n    furi_string_cat_printf(str, \"NAD: %ssupported\\n\", nad_support_str);\n\n    const char* cid_support_str =\n        iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionCid) ? \"\" : \"not \";\n    furi_string_cat_printf(str, \"CID: %ssupported\", cid_support_str);\n\n    furi_string_cat_printf(str, \"\\n::::::::::::[Application data]::::::::::::\\nRaw:\");\n\n    size_t app_data_size;\n    const uint8_t* app_data = iso14443_3b_get_application_data(data, &app_data_size);\n    for(size_t i = 0; i < app_data_size; ++i) {\n        furi_string_cat_printf(str, \" %02X\", app_data[i]);\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3b/iso14443_3b.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_iso14443_3b_info(\n    const Iso14443_3bData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c",
    "content": "#include \"iso14443_4a.h\"\n#include \"iso14443_4a_render.h\"\n\n#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>\n#include <nfc/protocols/iso14443_4a/iso14443_4a_listener.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_read_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n\n    NfcApp* instance = context;\n    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_iso14443_4a(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_4a, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_iso14443_4a(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_saved_menu_on_enter_iso14443_4a(NfcApp* instance) {\n    UNUSED(instance);\n}\n\nNfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n    furi_assert(event.event_data);\n\n    NfcApp* nfc = context;\n    Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data;\n\n    if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) {\n        if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {\n            furi_string_cat_printf(nfc->text_box_store, \"R:\");\n            for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer);\n                i++) {\n                furi_string_cat_printf(\n                    nfc->text_box_store,\n                    \" %02X\",\n                    bit_buffer_get_byte(iso14443_4a_event->data->buffer, i));\n            }\n            furi_string_push_back(nfc->text_box_store, '\\n');\n            view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);\n        }\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) {\n    const Iso14443_4aData* data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);\n\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);\n}\n\nstatic bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);\n        return true;\n    }\n\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {\n    .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_iso14443_4a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_iso14443_4a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_read_menu_on_event_iso14443_4a,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_iso14443_4a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_iso14443_4a,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_4a/iso14443_4a.h>\n\n#include \"iso14443_4a.h\"\n\nNfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c",
    "content": "#include \"iso14443_4a_render.h\"\n\n#include \"../iso14443_3a/iso14443_3a_render.h\"\n\nvoid nfc_render_iso14443_4a_info(\n    const Iso14443_4aData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_4a_brief(data, str);\n\n    if(format_type != NfcProtocolFormatTypeFull) return;\n\n    nfc_render_iso14443_4a_extra(data, str);\n}\n\nvoid nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str) {\n    nfc_render_iso14443_tech_type(iso14443_4a_get_base_data(data), str);\n    nfc_render_iso14443_3a_brief(iso14443_4a_get_base_data(data), str);\n}\n\nvoid nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) {\n    furi_string_cat_printf(str, \"\\n::::::::::::::::[Protocol info]:::::::::::::::\\n\");\n\n    if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRateBoth106Kbit)) {\n        furi_string_cat(str, \"Bit rate PICC <-> PCD:\\n  106 kBit/s supported\\n\");\n    } else {\n        furi_string_cat(str, \"Bit rate PICC -> PCD:\\n\");\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd212Kbit)) {\n            furi_string_cat(str, \"  212 kBit/s supported\\n\");\n        }\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd424Kbit)) {\n            furi_string_cat(str, \"  424 kBit/s supported\\n\");\n        }\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd848Kbit)) {\n            furi_string_cat(str, \"  848 kBit/s supported\\n\");\n        }\n\n        furi_string_cat(str, \"Bit rate PICC <- PCD:\\n\");\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc212Kbit)) {\n            furi_string_cat(str, \"  212 kBit/s supported\\n\");\n        }\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc424Kbit)) {\n            furi_string_cat(str, \"  424 kBit/s supported\\n\");\n        }\n        if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc848Kbit)) {\n            furi_string_cat(str, \"  848 kBit/s supported\\n\");\n        }\n    }\n\n    furi_string_cat(str, \"Max frame size: \");\n\n    const uint16_t max_frame_size = iso14443_4a_get_frame_size_max(data);\n    if(max_frame_size != 0) {\n        furi_string_cat_printf(str, \"%u bytes\\n\", max_frame_size);\n    } else {\n        furi_string_cat(str, \"? (RFU)\\n\");\n    }\n\n    const uint32_t fwt_fc = iso14443_4a_get_fwt_fc_max(data);\n    if(fwt_fc != 0) {\n        furi_string_cat_printf(str, \"Max waiting time: %4.2g s\\n\", (double)(fwt_fc / 13.56e6));\n    }\n\n    const char* nad_support_str =\n        iso14443_4a_supports_frame_option(data, Iso14443_4aFrameOptionNad) ? \"\" : \"not \";\n    furi_string_cat_printf(str, \"NAD: %ssupported\\n\", nad_support_str);\n\n    const char* cid_support_str =\n        iso14443_4a_supports_frame_option(data, Iso14443_4aFrameOptionCid) ? \"\" : \"not \";\n    furi_string_cat_printf(str, \"CID: %ssupported\", cid_support_str);\n\n    uint32_t hist_bytes_count;\n    const uint8_t* hist_bytes = iso14443_4a_get_historical_bytes(data, &hist_bytes_count);\n\n    if(hist_bytes_count > 0) {\n        furi_string_cat_printf(str, \"\\n:::::::::::::[Historical bytes]:::::::::::::\\nRaw:\");\n\n        for(size_t i = 0; i < hist_bytes_count; ++i) {\n            furi_string_cat_printf(str, \" %02X\", hist_bytes[i]);\n        }\n    }\n\n    furi_string_cat(str, \"\\n\\e#ISO14443-3A data\");\n    nfc_render_iso14443_3a_extra(iso14443_4a_get_base_data(data), str);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_4a/iso14443_4a.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_iso14443_4a_info(\n    const Iso14443_4aData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str);\n\nvoid nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c",
    "content": "#include \"iso14443_4b.h\"\n#include \"iso14443_4b_render.h\"\n\n#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n#include \"../iso14443_3b/iso14443_3b_i.h\"\n\nstatic void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_read_poller_callback_iso14443_4b(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4b);\n\n    NfcApp* instance = context;\n    const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;\n\n    if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_iso14443_4b(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_4b, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) {\n    UNUSED(instance);\n}\n\nstatic bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);\n        return true;\n    }\n\n    return false;\n}\n\nstatic bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {\n    return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {\n    .features = NfcProtocolFeatureNone,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_iso14443_4b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_iso14443_4b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_read_menu_on_event_iso14443_4b,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_iso14443_4b,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b,\n            .on_event = nfc_scene_saved_menu_on_event_iso14443_4b,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.c",
    "content": "#include \"iso14443_4b_render.h\"\n\n#include \"../iso14443_3b/iso14443_3b_render.h\"\n\nvoid nfc_render_iso14443_4b_info(\n    const Iso14443_4bData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_3b_info(iso14443_4b_get_base_data(data), format_type, str);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_4b/iso14443_4b.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_iso14443_4b_info(\n    const Iso14443_4bData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c",
    "content": "#include \"iso15693_3.h\"\n#include \"iso15693_3_render.h\"\n\n#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_listener.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_reset(instance->widget);\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_more_info_on_enter_iso15693_3(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_render_iso15693_3_system_info(data, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n\n    NfcApp* instance = context;\n    const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;\n\n    if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_iso15693_3(NfcApp* instance) {\n    UNUSED(instance);\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso15693_3, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_iso15693_3(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand\n    nfc_scene_emulate_listener_callback_iso15693_3(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n    furi_assert(event.event_data);\n\n    NfcApp* nfc = context;\n    Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;\n\n    if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {\n        if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {\n            furi_string_cat_printf(nfc->text_box_store, \"R:\");\n            for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) {\n                furi_string_cat_printf(\n                    nfc->text_box_store,\n                    \" %02X\",\n                    bit_buffer_get_byte(iso15693_3_event->data->buffer, i));\n            }\n            furi_string_push_back(nfc->text_box_store, '\\n');\n            view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);\n        }\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) {\n    const Iso15693_3Data* data = nfc_device_get_data(instance->nfc_device, NfcProtocolIso15693_3);\n\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso15693_3, data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance);\n}\n\nstatic bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n        return true;\n    }\n\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {\n    .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid |\n                NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_iso15693_3,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_iso15693_3,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_iso15693_3,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_iso15693_3,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_saved_menu_on_event_iso15693_3,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_iso15693_3,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_iso15693_3;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c",
    "content": "#include \"iso15693_3_render.h\"\n\n#define NFC_RENDER_ISO15693_3_MAX_BYTES (128U)\n\nvoid nfc_render_iso15693_3_info(\n    const Iso15693_3Data* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    if(format_type == NfcProtocolFormatTypeFull) {\n        furi_string_cat(str, \"ISO15693-3 (NFC-V)\\n\");\n    }\n\n    nfc_render_iso15693_3_brief(data, str);\n\n    if(format_type == NfcProtocolFormatTypeFull) {\n        nfc_render_iso15693_3_extra(data, str);\n    }\n}\n\nvoid nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) {\n    furi_string_cat_printf(str, \"UID:\\n\");\n\n    size_t uid_len;\n    const uint8_t* uid = iso15693_3_get_uid(data, &uid_len);\n\n    for(size_t i = 0; i < uid_len; i++) {\n        furi_string_cat_printf(str, \"%02X \", uid[i]);\n    }\n\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {\n        const uint16_t block_count = iso15693_3_get_block_count(data);\n        const uint8_t block_size = iso15693_3_get_block_size(data);\n\n        furi_string_cat_printf(str, \"\\nMemory: %u bytes\\n\", block_count * block_size);\n        furi_string_cat_printf(str, \"(%u blocks x %u bytes)\", block_count, block_size);\n    }\n}\n\nvoid nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str) {\n    const uint16_t block_count = iso15693_3_get_block_count(data);\n    const uint8_t block_size = iso15693_3_get_block_size(data);\n\n    if((data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) &&\n       (block_count > 0 && block_size > 0)) {\n        furi_string_cat(str, \"\\e#Memory data\\n\\e*--------------------\\n\");\n\n        const uint16_t display_block_count =\n            MIN(NFC_RENDER_ISO15693_3_MAX_BYTES / block_size, block_count);\n\n        for(uint32_t i = 0; i < display_block_count; ++i) {\n            furi_string_cat(str, \"\\e*\");\n\n            const uint8_t* block_data = iso15693_3_get_block_data(data, i);\n            for(uint32_t j = 0; j < block_size; ++j) {\n                furi_string_cat_printf(str, \"%02X \", block_data[j]);\n            }\n\n            const char* lock_str = iso15693_3_is_block_locked(data, i) ? \"[LOCK]\" : \"\";\n            furi_string_cat_printf(str, \"| %s\\n\", lock_str);\n        }\n\n        if(block_count != display_block_count) {\n            furi_string_cat_printf(\n                str,\n                \"(Data is too big. Showing only the first %u bytes.)\",\n                display_block_count * block_size);\n        }\n    } else {\n        furi_string_cat(str, \"\\e#No available data\\n\");\n    }\n}\n\nvoid nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) {\n    furi_string_cat(str, \"\\n::::::::::::::::[General info]:::::::::::::::::\\n\");\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {\n        furi_string_cat_printf(str, \"DSFID: %02X\\n\", data->system_info.dsfid);\n    }\n\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {\n        furi_string_cat_printf(str, \"AFI: %02X\\n\", data->system_info.afi);\n    }\n\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {\n        furi_string_cat_printf(str, \"IC Reference: %02X\\n\", data->system_info.ic_ref);\n    }\n\n    furi_string_cat(str, \":::::::::::::::::::[Lock bits]::::::::::::::::::::\\n\");\n\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {\n        furi_string_cat_printf(\n            str, \"DSFID: %s locked\\n\", data->settings.lock_bits.dsfid ? \"\" : \"not\");\n    }\n\n    if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {\n        furi_string_cat_printf(\n            str, \"AFI: %s locked\\n\", data->settings.lock_bits.dsfid ? \"\" : \"not\");\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso15693_3/iso15693_3.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_iso15693_3_info(\n    const Iso15693_3Data* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str);\n\nvoid nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str);\n\nvoid nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c",
    "content": "#include \"mf_classic.h\"\n#include \"mf_classic_render.h\"\n\n#include <nfc/protocols/mf_classic/mf_classic_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\n#define TAG \"MfClassicApp\"\n\nenum {\n    SubmenuIndexDetectReader = SubmenuIndexCommonMax,\n    SubmenuIndexWrite,\n    SubmenuIndexUpdate,\n    SubmenuIndexDictAttack,\n    SubmenuIndexCrackNonces,\n};\n\nstatic void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n\n    nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_more_info_on_enter_mf_classic(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfClassicData* mfc_data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    furi_string_reset(instance->text_box_store);\n    nfc_render_mf_classic_dump(mfc_data, instance->text_box_store);\n\n    text_box_set_font(instance->text_box, TextBoxFontHex);\n    text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_mf_classic(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcApp* instance = context;\n    const MfClassicPollerEvent* mfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));\n        size_t uid_len = 0;\n        const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);\n        if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) {\n            FURI_LOG_I(TAG, \"Key cache found\");\n            mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;\n        } else {\n            FURI_LOG_I(TAG, \"Key cache not found\");\n            view_dispatcher_send_custom_event(\n                instance->view_dispatcher, NfcCustomEventPollerIncomplete);\n            command = NfcCommandStop;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {\n        uint8_t sector_num = 0;\n        MfClassicKey key = {};\n        MfClassicKeyType key_type = MfClassicKeyTypeA;\n        if(mf_classic_key_cache_get_next_key(\n               instance->mfc_key_cache, &sector_num, &key, &key_type)) {\n            mfc_event->data->read_sector_request_data.sector_num = sector_num;\n            mfc_event->data->read_sector_request_data.key = key;\n            mfc_event->data->read_sector_request_data.key_type = key_type;\n            mfc_event->data->read_sector_request_data.key_provided = true;\n        } else {\n            mfc_event->data->read_sector_request_data.key_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));\n        const MfClassicData* mfc_data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n        NfcCustomEvent custom_event = mf_classic_is_card_read(mfc_data) ?\n                                          NfcCustomEventPollerSuccess :\n                                          NfcCustomEventPollerIncomplete;\n        view_dispatcher_send_custom_event(instance->view_dispatcher, custom_event);\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nstatic void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) {\n    mf_classic_key_cache_reset(instance->mfc_key_cache);\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance);\n}\n\nstatic bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom &&\n       event.event == NfcCustomEventPollerIncomplete) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);\n    }\n\n    return true;\n}\n\nstatic void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) {\n    Submenu* submenu = instance->submenu;\n    const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n\n    if(!mf_classic_is_card_read(data)) {\n        submenu_add_item(\n            submenu,\n            \"Extract MF Keys\",\n            SubmenuIndexDetectReader,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n\n        submenu_add_item(\n            submenu,\n            \"Unlock with Dictionary\",\n            SubmenuIndexDictAttack,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n\n        submenu_add_item(\n            submenu,\n            \"Crack nonces in MFKey32\",\n            SubmenuIndexCrackNonces,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n}\n\nstatic void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524\n    const NfcDevice* device = instance->nfc_device;\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n\n    nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {\n    Submenu* submenu = instance->submenu;\n    const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n\n    if(!mf_classic_is_card_read(data)) {\n        submenu_add_item(\n            submenu,\n            \"Extract MF Keys\",\n            SubmenuIndexDetectReader,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n\n        submenu_add_item(\n            submenu,\n            \"Unlock with Dictionary\",\n            SubmenuIndexDictAttack,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n    submenu_add_item(\n        submenu,\n        \"Write to Initial Card\",\n        SubmenuIndexWrite,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n\n    submenu_add_item(\n        submenu,\n        \"Update from Initial Card\",\n        SubmenuIndexUpdate,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n}\n\nstatic void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) {\n    const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, data);\n    nfc_listener_start(instance->listener, NULL, NULL);\n}\n\nstatic bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexDetectReader) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneSaveConfirm,\n                NfcSceneSaveConfirmStateDetectReader);\n\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm);\n            dolphin_deed(DolphinDeedNfcDetectReader);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDictAttack) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   instance->scene_manager, NfcSceneMfClassicDictAttack)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);\n            }\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonEdit) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCrackNonces) {\n            scene_manager_set_scene_state(\n                instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nstatic bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexDetectReader) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);\n            consumed = true;\n        } else if(event.event == SubmenuIndexWrite) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial);\n            consumed = true;\n        } else if(event.event == SubmenuIndexUpdate) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDictAttack) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   instance->scene_manager, NfcSceneMfClassicDictAttack)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nstatic bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom && event.event == NfcCustomEventTextInputDone) {\n        mf_classic_key_cache_save(\n            instance->mfc_key_cache,\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic));\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_mf_classic = {\n    .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_mf_classic,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_mf_classic,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_mf_classic,\n            .on_event = nfc_scene_read_on_event_mf_classic,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_scene_read_menu_on_enter_mf_classic,\n            .on_event = nfc_scene_read_menu_on_event_mf_classic,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_mf_classic,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_scene_saved_menu_on_enter_mf_classic,\n            .on_event = nfc_scene_saved_menu_on_event_mf_classic,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_save_name_on_event_mf_classic,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_mf_classic,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_mf_classic;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c",
    "content": "#include \"mf_classic_render.h\"\n\n#include \"../iso14443_3a/iso14443_3a_render.h\"\n\nvoid nfc_render_mf_classic_info(\n    const MfClassicData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str);\n\n    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);\n    uint8_t keys_total = sectors_total * 2;\n    uint8_t keys_found = 0;\n    uint8_t sectors_read = 0;\n    mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);\n\n    furi_string_cat_printf(str, \"\\nKeys Found: %u/%u\", keys_found, keys_total);\n    furi_string_cat_printf(str, \"\\nSectors Read: %u/%u\", sectors_read, sectors_total);\n}\n\nstatic void\n    mf_classic_render_raw_data(const uint8_t* data, size_t size, bool data_read, FuriString* str) {\n    furi_assert((size % 2) == 0);\n\n    for(size_t i = 0; i < size; i += 2) {\n        if(data_read) {\n            furi_string_cat_printf(str, \"%02X%02X \", data[i], data[i + 1]);\n        } else {\n            furi_string_cat_printf(str, \"???? \");\n        }\n    }\n}\n\nstatic void\n    mf_classic_render_block(const MfClassicData* data, uint8_t block_num, FuriString* str) {\n    if(mf_classic_is_sector_trailer(block_num)) {\n        uint8_t sec_num = mf_classic_get_sector_by_block(block_num);\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num);\n\n        // Render key A\n        bool key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeA);\n        mf_classic_render_raw_data(sec_tr->key_a.data, sizeof(MfClassicKey), key_read, str);\n\n        // Render access bits\n        bool access_bits_read = mf_classic_is_block_read(data, block_num);\n        mf_classic_render_raw_data(\n            sec_tr->access_bits.data, sizeof(MfClassicAccessBits), access_bits_read, str);\n\n        // Render key B\n        key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeB);\n        mf_classic_render_raw_data(sec_tr->key_b.data, sizeof(MfClassicKey), key_read, str);\n    } else {\n        const uint8_t* block_data = data->block[block_num].data;\n        bool block_read = mf_classic_is_block_read(data, block_num);\n        mf_classic_render_raw_data(block_data, sizeof(MfClassicBlock), block_read, str);\n    }\n}\n\nvoid nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) {\n    uint16_t total_blocks = mf_classic_get_total_block_num(data->type);\n\n    for(size_t i = 0; i < total_blocks; i++) {\n        mf_classic_render_block(data, i, str);\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_classic/mf_classic.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_mf_classic_info(\n    const MfClassicData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c",
    "content": "#include \"mf_desfire.h\"\n#include \"mf_desfire_render.h\"\n\n#include <nfc/protocols/mf_desfire/mf_desfire_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n#include \"../iso14443_4a/iso14443_4a_i.h\"\n\nstatic void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n    nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) {\n    // Jump to advanced scene right away\n    scene_manager_next_scene(instance->scene_manager, NfcSceneMfDesfireMoreInfo);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfDesfire);\n\n    NfcCommand command = NfcCommandContinue;\n\n    NfcApp* instance = context;\n    const MfDesfirePollerEvent* mf_desfire_event = event.event_data;\n\n    if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    } else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {\n        command = NfcCommandReset;\n    }\n\n    return command;\n}\n\nstatic void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_desfire, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n    nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_emulate_on_enter_mf_desfire(NfcApp* instance) {\n    const Iso14443_4aData* iso14443_4a_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);\n\n    instance->listener =\n        nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_mf_desfire = {\n    .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_mf_desfire,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_mf_desfire,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_mf_desfire,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_mf_desfire,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_mf_desfire,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_mf_desfire;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c",
    "content": "#include \"mf_desfire_render.h\"\n\n#include \"../iso14443_4a/iso14443_4a_render.h\"\n\n#define MF_DESFIRE_RENDER_MAX_RECORD_SIZE (256U)\n\nvoid nfc_render_mf_desfire_info(\n    const MfDesfireData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_4a_brief(mf_desfire_get_base_data(data), str);\n\n    const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1);\n    const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0;\n\n    if(data->master_key_settings.is_free_directory_list) {\n        const uint32_t app_count = simple_array_get_count(data->applications);\n        uint32_t file_count = 0;\n\n        for(uint32_t i = 0; i < app_count; ++i) {\n            const MfDesfireApplication* app = simple_array_cget(data->applications, i);\n            if(app->key_settings.is_free_directory_list) {\n                file_count += simple_array_get_count(app->file_ids);\n            }\n        }\n\n        furi_string_cat_printf(str, \"\\n%lu Application%s\", app_count, app_count != 1 ? \"s\" : \"\");\n        furi_string_cat_printf(str, \", %lu File%s\", file_count, file_count != 1 ? \"s\" : \"\");\n    } else {\n        furi_string_cat_printf(str, \"\\nAuth required to read apps!\");\n    }\n\n    furi_string_cat_printf(str, \"\\n%lu\", bytes_total);\n\n    if(data->version.sw_storage & 1) {\n        furi_string_push_back(str, '+');\n    }\n    furi_string_cat_printf(str, \" bytes, %lu bytes free\", bytes_free);\n\n    if(format_type != NfcProtocolFormatTypeFull) return;\n\n    furi_string_cat(str, \"\\n\\e#ISO14443-4 data\");\n    nfc_render_iso14443_4a_extra(mf_desfire_get_base_data(data), str);\n}\n\nvoid nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str) {\n    nfc_render_mf_desfire_version(&data->version, str);\n    nfc_render_mf_desfire_free_memory(&data->free_memory, str);\n    nfc_render_mf_desfire_key_settings(&data->master_key_settings, str);\n\n    for(uint32_t i = 0; i < simple_array_get_count(data->master_key_versions); ++i) {\n        nfc_render_mf_desfire_key_version(simple_array_cget(data->master_key_versions, i), i, str);\n    }\n}\n\nvoid nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str) {\n    furi_string_cat_printf(\n        str,\n        \"%02x:%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n        data->uid[0],\n        data->uid[1],\n        data->uid[2],\n        data->uid[3],\n        data->uid[4],\n        data->uid[5],\n        data->uid[6]);\n    furi_string_cat_printf(\n        str,\n        \"hw %02x type %02x sub %02x\\n\"\n        \" maj %02x min %02x\\n\"\n        \" size %02x proto %02x\\n\",\n        data->hw_vendor,\n        data->hw_type,\n        data->hw_subtype,\n        data->hw_major,\n        data->hw_minor,\n        data->hw_storage,\n        data->hw_proto);\n    furi_string_cat_printf(\n        str,\n        \"sw %02x type %02x sub %02x\\n\"\n        \" maj %02x min %02x\\n\"\n        \" size %02x proto %02x\\n\",\n        data->sw_vendor,\n        data->sw_type,\n        data->sw_subtype,\n        data->sw_major,\n        data->sw_minor,\n        data->sw_storage,\n        data->sw_proto);\n    furi_string_cat_printf(\n        str,\n        \"batch %02x:%02x:%02x:%02x:%02x\\n\"\n        \"week %02x year 20%02x\\n\",\n        data->batch[0],\n        data->batch[1],\n        data->batch[2],\n        data->batch[3],\n        data->batch[4],\n        data->prod_week,\n        data->prod_year);\n}\n\nvoid nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str) {\n    if(data->is_present) {\n        furi_string_cat_printf(str, \"freeMem %lu\\n\", data->bytes_free);\n    }\n}\n\nvoid nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) {\n    if(data->is_free_directory_list) {\n        furi_string_cat_printf(str, \"changeKeyID %d\\n\", data->change_key_id);\n        furi_string_cat_printf(str, \"configChangeable %d\\n\", data->is_config_changeable);\n        furi_string_cat_printf(str, \"freeCreateDelete %d\\n\", data->is_free_create_delete);\n        furi_string_cat_printf(str, \"freeDirectoryList %d\\n\", data->is_free_directory_list);\n        furi_string_cat_printf(str, \"masterChangeable %d\\n\", data->is_master_key_changeable);\n    } else {\n        furi_string_cat_printf(str, \"changeKeyID ??\\n\");\n        furi_string_cat_printf(str, \"configChangeable ??\\n\");\n        furi_string_cat_printf(str, \"freeCreateDelete ??\\n\");\n        furi_string_cat_printf(str, \"freeDirectoryList 0\\n\");\n        furi_string_cat_printf(str, \"masterChangeable ??\\n\");\n    }\n\n    if(data->flags) {\n        furi_string_cat_printf(str, \"flags %d\\n\", data->flags);\n    }\n\n    if(data->is_free_directory_list) {\n        furi_string_cat_printf(str, \"maxKeys %d\\n\", data->max_keys);\n    } else {\n        furi_string_cat_printf(str, \"maxKeys ??\\n\");\n    }\n}\n\nvoid nfc_render_mf_desfire_key_version(\n    const MfDesfireKeyVersion* data,\n    uint32_t index,\n    FuriString* str) {\n    furi_string_cat_printf(str, \"key %lu version %u\\n\", index, *data);\n}\n\nvoid nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) {\n    const uint8_t* app_id = data->data;\n    furi_string_cat_printf(str, \"Application %02x%02x%02x\\n\", app_id[2], app_id[1], app_id[0]);\n}\n\nvoid nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) {\n    nfc_render_mf_desfire_key_settings(&data->key_settings, str);\n\n    if(data->key_settings.is_free_directory_list) {\n        for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {\n            nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);\n        }\n    }\n}\n\nvoid nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str) {\n    furi_string_cat_printf(str, \"File %d\\n\", *data);\n}\n\nvoid nfc_render_mf_desfire_file_settings_data(\n    const MfDesfireFileSettings* settings,\n    const MfDesfireFileData* data,\n    FuriString* str) {\n    const char* type;\n    switch(settings->type) {\n    case MfDesfireFileTypeStandard:\n        type = \"standard\";\n        break;\n    case MfDesfireFileTypeBackup:\n        type = \"backup\";\n        break;\n    case MfDesfireFileTypeValue:\n        type = \"value\";\n        break;\n    case MfDesfireFileTypeLinearRecord:\n        type = \"linear\";\n        break;\n    case MfDesfireFileTypeCyclicRecord:\n        type = \"cyclic\";\n        break;\n    case MfDesfireFileTypeTransactionMac:\n        type = \"txn-mac\";\n        break;\n    default:\n        type = \"unknown\";\n    }\n\n    const char* comm;\n    switch(settings->comm) {\n    case MfDesfireFileCommunicationSettingsPlaintext:\n        comm = \"plain\";\n        break;\n    case MfDesfireFileCommunicationSettingsAuthenticated:\n        comm = \"auth\";\n        break;\n    case MfDesfireFileCommunicationSettingsEnciphered:\n        comm = \"enciphered\";\n        break;\n    default:\n        comm = \"unknown\";\n    }\n\n    furi_string_cat_printf(str, \"%s %s\\n\", type, comm);\n\n    for(size_t i = 0; i < settings->access_rights_len; i++) {\n        furi_string_cat_printf(\n            str,\n            \"r %d w %d rw %d c %d\\n\",\n            settings->access_rights[i] >> 12 & 0xF,\n            settings->access_rights[i] >> 8 & 0xF,\n            settings->access_rights[i] >> 4 & 0xF,\n            settings->access_rights[i] & 0xF);\n    }\n\n    uint32_t record_count = 1;\n    uint32_t record_size = 0;\n\n    switch(settings->type) {\n    case MfDesfireFileTypeStandard:\n    case MfDesfireFileTypeBackup:\n        record_size = settings->data.size;\n        furi_string_cat_printf(str, \"size %lu\\n\", record_size);\n        break;\n    case MfDesfireFileTypeValue:\n        record_size = MF_DESFIRE_VALUE_SIZE;\n        furi_string_cat_printf(\n            str, \"lo %lu hi %lu\\n\", settings->value.lo_limit, settings->value.hi_limit);\n        furi_string_cat_printf(\n            str,\n            \"limit %lu enabled %d\\n\",\n            settings->value.limited_credit_value,\n            settings->value.limited_credit_enabled);\n        break;\n    case MfDesfireFileTypeLinearRecord:\n    case MfDesfireFileTypeCyclicRecord:\n        record_count = settings->record.cur;\n        record_size = settings->record.size;\n        furi_string_cat_printf(str, \"size %lu\\n\", record_size);\n        furi_string_cat_printf(str, \"num %lu max %lu\\n\", record_count, settings->record.max);\n        break;\n    case MfDesfireFileTypeTransactionMac:\n        record_count = 0;\n        furi_string_cat_printf(\n            str,\n            \"key opt %02X ver %02X\\n\",\n            settings->transaction_mac.key_option,\n            settings->transaction_mac.key_version);\n        furi_string_cat_printf(str, \"cnt limit %lu\\n\", settings->transaction_mac.counter_limit);\n        break;\n    }\n\n    bool is_auth_required = true;\n    for(size_t i = 0; i < settings->access_rights_len; i++) {\n        uint8_t read_rights = (settings->access_rights[i] >> 12) & 0x0f;\n        uint8_t read_write_rights = (settings->access_rights[i] >> 4) & 0x0f;\n        if((read_rights == 0x0e) || (read_write_rights == 0x0e)) {\n            is_auth_required = false;\n            break;\n        }\n    }\n    if(is_auth_required) {\n        furi_string_cat_printf(str, \"Auth required to read file data\\n\");\n        return;\n    }\n\n    if(simple_array_get_count(data->data) == 0) {\n        return;\n    }\n\n    // Limit record size\n    bool trim_data = record_size > MF_DESFIRE_RENDER_MAX_RECORD_SIZE;\n    if(trim_data) {\n        record_size = MF_DESFIRE_RENDER_MAX_RECORD_SIZE;\n    }\n\n    for(uint32_t rec = 0; rec < record_count; rec++) {\n        furi_string_cat_printf(str, \"record %lu\\n\", rec);\n\n        for(uint32_t ch = 0; ch < record_size; ch += 4) {\n            furi_string_cat_printf(str, \"%03lx|\", ch);\n\n            for(uint32_t i = 0; i < 4; i++) {\n                if(ch + i < record_size) {\n                    const uint32_t data_index = rec * record_size + ch + i;\n                    const uint8_t data_byte =\n                        *(const uint8_t*)simple_array_cget(data->data, data_index);\n                    furi_string_cat_printf(str, \"%02x \", data_byte);\n                } else {\n                    furi_string_cat_printf(str, \"   \");\n                }\n            }\n\n            for(uint32_t i = 0; i < 4 && ch + i < record_size; i++) {\n                const uint32_t data_index = rec * record_size + ch + i;\n                const uint8_t data_byte =\n                    *(const uint8_t*)simple_array_cget(data->data, data_index);\n                if(isprint(data_byte)) {\n                    furi_string_cat_printf(str, \"%c\", data_byte);\n                } else {\n                    furi_string_cat_printf(str, \".\");\n                }\n            }\n\n            furi_string_push_back(str, '\\n');\n        }\n        if(trim_data) {\n            furi_string_cat_str(str, \"...\");\n        }\n\n        furi_string_push_back(str, '\\n');\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_desfire/mf_desfire.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_mf_desfire_info(\n    const MfDesfireData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_key_version(\n    const MfDesfireKeyVersion* data,\n    uint32_t index,\n    FuriString* str);\n\nvoid nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str);\n\nvoid nfc_render_mf_desfire_file_settings_data(\n    const MfDesfireFileSettings* settings,\n    const MfDesfireFileData* data,\n    FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c",
    "content": "#include \"mf_plus.h\"\n#include \"mf_plus_render.h\"\n\n#include <nfc/protocols/mf_plus/mf_plus_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n#include \"../iso14443_4a/iso14443_4a_i.h\"\n\nstatic void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n    nfc_render_mf_plus_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\nstatic NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolMfPlus);\n    furi_assert(event.event_data);\n\n    NfcApp* instance = context;\n    const MfPlusPollerEvent* mf_plus_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    } else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {\n        command = NfcCommandReset;\n    }\n\n    return command;\n}\n\nstatic void nfc_scene_read_on_enter_mf_plus(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_plus, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n    nfc_render_mf_plus_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {\n    const Iso14443_4aData* iso14443_4a_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);\n\n    instance->listener =\n        nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_mf_plus = {\n    .features = NfcProtocolFeatureEmulateUid,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_mf_plus,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_mf_plus,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_mf_plus,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_mf_plus,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_mf_plus;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c",
    "content": "#include \"mf_plus_render.h\"\n\n#include \"../iso14443_4a/iso14443_4a_render.h\"\n\nvoid nfc_render_mf_plus_info(\n    const MfPlusData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_4a_brief(mf_plus_get_base_data(data), str);\n\n    if(format_type != NfcProtocolFormatTypeFull) return;\n\n    furi_string_cat(str, \"\\n\\e#ISO14443-4 data\");\n    nfc_render_iso14443_4a_extra(mf_plus_get_base_data(data), str);\n}\n\nvoid nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) {\n    nfc_render_mf_plus_version(&data->version, str);\n}\n\nvoid nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) {\n    furi_string_cat_printf(\n        str,\n        \"%02x:%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n        data->uid[0],\n        data->uid[1],\n        data->uid[2],\n        data->uid[3],\n        data->uid[4],\n        data->uid[5],\n        data->uid[6]);\n    furi_string_cat_printf(\n        str,\n        \"hw %02x type %02x sub %02x\\n\"\n        \" maj %02x min %02x\\n\"\n        \" size %02x proto %02x\\n\",\n        data->hw_vendor,\n        data->hw_type,\n        data->hw_subtype,\n        data->hw_major,\n        data->hw_minor,\n        data->hw_storage,\n        data->hw_proto);\n    furi_string_cat_printf(\n        str,\n        \"sw %02x type %02x sub %02x\\n\"\n        \" maj %02x min %02x\\n\"\n        \" size %02x proto %02x\\n\",\n        data->sw_vendor,\n        data->sw_type,\n        data->sw_subtype,\n        data->sw_major,\n        data->sw_minor,\n        data->sw_storage,\n        data->sw_proto);\n    furi_string_cat_printf(\n        str,\n        \"batch %02x:%02x:%02x:%02x:%02x\\n\"\n        \"week %d year %d\\n\",\n        data->batch[0],\n        data->batch[1],\n        data->batch[2],\n        data->batch[3],\n        data->batch[4],\n        data->prod_week,\n        data->prod_year);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_plus/mf_plus.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_mf_plus_info(\n    const MfPlusData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str);\n\nvoid nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c",
    "content": "#include \"mf_ultralight.h\"\n#include \"mf_ultralight_render.h\"\n\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>\n#include <toolbox/pretty_format.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n#include \"../nfc_protocol_support_unlock_helper.h\"\n\nenum {\n    SubmenuIndexUnlock = SubmenuIndexCommonMax,\n    SubmenuIndexUnlockByReader,\n    SubmenuIndexUnlockByPassword,\n    SubmenuIndexWrite,\n    SubmenuIndexDictAttack\n};\n\nenum {\n    NfcSceneMoreInfoStateASCII,\n    NfcSceneMoreInfoStateRawData,\n};\n\nstatic void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n\n    nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    furi_string_reset(instance->text_box_store);\n    uint32_t scene_state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo);\n\n    if(scene_state == NfcSceneMoreInfoStateASCII) {\n        pretty_format_bytes_hex_canonical(\n            instance->text_box_store,\n            MF_ULTRALIGHT_PAGE_SIZE,\n            PRETTY_FORMAT_FONT_MONOSPACE,\n            (uint8_t*)mfu->page,\n            mfu->pages_read * MF_ULTRALIGHT_PAGE_SIZE);\n\n        widget_add_text_scroll_element(\n            instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"Raw Data\",\n            nfc_protocol_support_common_widget_callback,\n            instance);\n\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeLeft,\n            \"Info\",\n            nfc_protocol_support_common_widget_callback,\n            instance);\n    } else if(scene_state == NfcSceneMoreInfoStateRawData) {\n        nfc_render_mf_ultralight_dump(mfu, instance->text_box_store);\n        widget_add_text_scroll_element(\n            instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));\n\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeLeft,\n            \"ASCII\",\n            nfc_protocol_support_common_widget_callback,\n            instance);\n    }\n}\n\nstatic bool nfc_scene_more_info_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||\n       (event.type == SceneManagerEventTypeBack)) {\n        scene_manager_set_scene_state(\n            instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII);\n        scene_manager_previous_scene(instance->scene_manager);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {\n        scene_manager_set_scene_state(\n            instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData);\n        scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);\n        consumed = true;\n    }\n    return consumed;\n}\n\nstatic NfcCommand\n    nfc_scene_read_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolMfUltralight);\n\n    NfcApp* instance = context;\n    const MfUltralightPollerEvent* mf_ultralight_event = event.event_data;\n\n    if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));\n\n        const MfUltralightData* data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n        uint32_t event = (data->pages_read == data->pages_total) ? NfcCustomEventPollerSuccess :\n                                                                   NfcCustomEventPollerIncomplete;\n        view_dispatcher_send_custom_event(instance->view_dispatcher, event);\n        return NfcCommandStop;\n    } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));\n        const MfUltralightData* data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n        if(instance->mf_ul_auth->type == MfUltralightAuthTypeXiaomi) {\n            if(mf_ultralight_generate_xiaomi_pass(\n                   instance->mf_ul_auth,\n                   data->iso14443_3a_data->uid,\n                   data->iso14443_3a_data->uid_len)) {\n                mf_ultralight_event->data->auth_context.skip_auth = false;\n            }\n        } else if(instance->mf_ul_auth->type == MfUltralightAuthTypeAmiibo) {\n            if(mf_ultralight_generate_amiibo_pass(\n                   instance->mf_ul_auth,\n                   data->iso14443_3a_data->uid,\n                   data->iso14443_3a_data->uid_len)) {\n                mf_ultralight_event->data->auth_context.skip_auth = false;\n            }\n        } else if(\n            instance->mf_ul_auth->type == MfUltralightAuthTypeManual ||\n            instance->mf_ul_auth->type == MfUltralightAuthTypeReader) {\n            mf_ultralight_event->data->auth_context.skip_auth = false;\n        } else {\n            mf_ultralight_event->data->auth_context.skip_auth = true;\n        }\n        if(!mf_ultralight_event->data->auth_context.skip_auth) {\n            mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password;\n\n            if(data->type == MfUltralightTypeMfulC) {\n                // Only set tdes_key for Manual/Reader auth types, not for dictionary attacks\n                if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual ||\n                   instance->mf_ul_auth->type == MfUltralightAuthTypeReader) {\n                    mf_ultralight_event->data->key_request_data.key =\n                        instance->mf_ul_auth->tdes_key;\n                    mf_ultralight_event->data->key_request_data.key_provided = true;\n                } else {\n                    mf_ultralight_event->data->key_request_data.key_provided = false;\n                }\n            }\n        }\n    } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) {\n        instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {\n    nfc_unlock_helper_setup_from_state(instance);\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);\n}\n\nbool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventPollerSuccess) {\n            notification_message(instance->notifications, &sequence_success);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n            dolphin_deed(DolphinDeedNfcReadSuccess);\n            return true;\n        } else if(event.event == NfcCustomEventPollerIncomplete) {\n            const MfUltralightData* data =\n                nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n            if(data->type == MfUltralightTypeMfulC &&\n               instance->mf_ul_auth->type == MfUltralightAuthTypeNone) {\n                // Start dict attack for MFUL C cards only if no specific auth was attempted\n                scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);\n            } else {\n                if(data->pages_read == data->pages_total) {\n                    notification_message(instance->notifications, &sequence_success);\n                } else {\n                    notification_message(instance->notifications, &sequence_semi_success);\n                }\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n            }\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) {\n    Submenu* submenu = instance->submenu;\n\n    const MfUltralightData* data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n\n    if(!mf_ultralight_is_all_data_read(data)) {\n        submenu_add_item(\n            submenu,\n            \"Unlock\",\n            SubmenuIndexUnlock,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n        if(data->type == MfUltralightTypeMfulC) {\n            submenu_add_item(\n                submenu,\n                \"Unlock with Dictionary\",\n                SubmenuIndexDictAttack,\n                nfc_protocol_support_common_submenu_callback,\n                instance);\n        }\n    } else if(\n        data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||\n        data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 ||\n        data->type == MfUltralightTypeUL21 || data->type == MfUltralightTypeOrigin) {\n        submenu_add_item(\n            submenu,\n            \"Write\",\n            SubmenuIndexWrite,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n}\n\nstatic void nfc_scene_read_success_on_enter_mf_ultralight(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    FuriString* temp_str = furi_string_alloc();\n\n    bool unlocked =\n        scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);\n    if(unlocked) {\n        nfc_render_mf_ultralight_pwd_pack(data, temp_str);\n    } else {\n        furi_string_cat_printf(\n            temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n\n        furi_string_replace(temp_str, \"Mifare\", \"MIFARE\");\n\n        nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeShort, temp_str);\n    }\n\n    mf_ultralight_auth_reset(instance->mf_ul_auth);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) {\n    const MfUltralightData* data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, data);\n    nfc_listener_start(instance->listener, NULL, NULL);\n}\n\nstatic bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(\n    NfcApp* instance,\n    SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexUnlock) {\n            const MfUltralightData* data =\n                nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n\n            uint32_t next_scene = (data->type == MfUltralightTypeMfulC) ?\n                                      NfcSceneDesAuthKeyInput :\n                                      NfcSceneMfUltralightUnlockMenu;\n            scene_manager_next_scene(instance->scene_manager, next_scene);\n            consumed = true;\n        } else if(event.event == SubmenuIndexWrite) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonEdit) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDictAttack) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   instance->scene_manager, NfcSceneMfUltralightCDictAttack)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);\n            }\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {\n    .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_mf_ultralight,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_mf_ultralight,\n            .on_event = nfc_scene_more_info_on_event_mf_ultralight,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_mf_ultralight,\n            .on_event = nfc_scene_read_on_event_mf_ultralight,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_scene_read_and_saved_menu_on_enter_mf_ultralight,\n            .on_event = nfc_scene_read_and_saved_menu_on_event_mf_ultralight,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_mf_ultralight,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_scene_read_and_saved_menu_on_enter_mf_ultralight,\n            .on_event = nfc_scene_read_and_saved_menu_on_event_mf_ultralight,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_mf_ultralight,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c",
    "content": "#include \"mf_ultralight_render.h\"\n\n#include \"../iso14443_3a/iso14443_3a_render.h\"\n\nstatic void nfc_render_mf_ultralight_pages_count(const MfUltralightData* data, FuriString* str) {\n    furi_string_cat_printf(str, \"\\nPages Read: %u/%u\", data->pages_read, data->pages_total);\n    if(data->pages_read != data->pages_total) {\n        furi_string_cat_printf(str, \"\\nPassword-protected pages!\");\n    }\n}\n\nvoid nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str) {\n    MfUltralightConfigPages* config;\n\n    bool all_pages = mf_ultralight_is_all_data_read(data);\n    bool has_config = mf_ultralight_get_config_page(data, &config);\n\n    if(!has_config) {\n        furi_string_cat_printf(str, \"\\e#Already Unlocked!\");\n    } else if(all_pages) {\n        furi_string_cat_printf(str, \"\\e#All Pages Are Unlocked!\");\n    } else {\n        furi_string_cat_printf(str, \"\\e#Some Pages Are Locked!\");\n    }\n\n    if(has_config) {\n        furi_string_cat_printf(str, \"\\nPassword: \");\n        nfc_render_iso14443_3a_format_bytes(\n            str, config->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE);\n\n        furi_string_cat_printf(str, \"\\nPACK: \");\n        nfc_render_iso14443_3a_format_bytes(str, config->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE);\n    } else {\n        furi_string_cat_printf(str, \"\\nThis card does not support\\npassword protection!\");\n    }\n\n    nfc_render_mf_ultralight_pages_count(data, str);\n}\n\nvoid nfc_render_mf_ultralight_info(\n    const MfUltralightData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str);\n\n    nfc_render_mf_ultralight_pages_count(data, str);\n}\n\nvoid nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) {\n    furi_string_cat_printf(str, \"\\e*\");\n    for(size_t i = 0; i < data->pages_read; i++) {\n        const uint8_t* page_data = data->page[i].data;\n        for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) {\n            furi_string_cat_printf(str, \" %02X%02X\", page_data[j], page_data[j + 1]);\n        }\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_mf_ultralight_info(\n    const MfUltralightData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n\nvoid nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str);\n\nvoid nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c",
    "content": "/**\n * @file nfc_protocol_support.c\n * @brief Common implementation of application-level protocol support.\n *\n * @see nfc_protocol_support_base.h\n * @see nfc_protocol_support_common.h\n */\n#include \"nfc_protocol_support.h\"\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"nfc_protocol_support_defs.h\"\n#include \"nfc_protocol_support_gui_common.h\"\n\n/**\n * @brief Common scene entry handler.\n *\n * @param[in,out] instance pointer to the NFC application instance.\n */\ntypedef void (*NfcProtocolSupportCommonOnEnter)(NfcApp* instance);\n\n/**\n * @brief Common scene custom event handler.\n *\n * @param[in,out] instance pointer to the NFC application instance.\n * @param[in] event custom event to be handled.\n * @returns true if the event was handled, false otherwise.\n */\ntypedef bool (*NfcProtocolSupportCommonOnEvent)(NfcApp* instance, SceneManagerEvent event);\n\n/**\n * @brief Common scene exit handler.\n *\n * @param[in,out] instance pointer to the NFC application instance.\n */\ntypedef void (*NfcProtocolSupportCommonOnExit)(NfcApp* instance);\n\n/**\n * @brief Structure containing common scene handler pointers.\n */\ntypedef struct {\n    NfcProtocolSupportCommonOnEnter on_enter; /**< Pointer to the on_enter() function. */\n    NfcProtocolSupportCommonOnEvent on_event; /**< Pointer to the on_event() function. */\n    NfcProtocolSupportCommonOnExit on_exit; /**< Pointer to the on_exit() function. */\n} NfcProtocolSupportCommonSceneBase;\n\nstatic const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[];\n\n// Interface functions\nvoid nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) {\n    furi_assert(scene < NfcProtocolSupportSceneCount);\n    furi_assert(context);\n\n    NfcApp* instance = context;\n    nfc_protocol_support_scenes[scene].on_enter(instance);\n}\n\nbool nfc_protocol_support_on_event(\n    NfcProtocolSupportScene scene,\n    void* context,\n    SceneManagerEvent event) {\n    furi_assert(scene < NfcProtocolSupportSceneCount);\n    furi_assert(context);\n\n    NfcApp* instance = context;\n    return nfc_protocol_support_scenes[scene].on_event(instance, event);\n}\n\nvoid nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context) {\n    furi_assert(scene < NfcProtocolSupportSceneCount);\n    furi_assert(context);\n\n    NfcApp* instance = context;\n    nfc_protocol_support_scenes[scene].on_exit(instance);\n}\n\nbool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) {\n    return nfc_protocol_support[protocol]->features & feature;\n}\n\n// Common scene handlers\n// SceneInfo\nstatic void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) {\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n    nfc_protocol_support[protocol]->scene_info.on_enter(instance);\n\n    if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) {\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"More\",\n            nfc_protocol_support_common_widget_callback,\n            instance);\n    }\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nstatic bool nfc_protocol_support_scene_info_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        // If the card could not be parsed, return to the respective menu\n        if(!scene_manager_get_scene_state(instance->scene_manager, NfcSceneSupportedCard)) {\n            const uint32_t scenes[] = {NfcSceneSavedMenu, NfcSceneReadMenu};\n            scene_manager_search_and_switch_to_previous_scene_one_of(\n                instance->scene_manager, scenes, COUNT_OF(scenes));\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) {\n    widget_reset(instance->widget);\n}\n\n// SceneMoreInfo\nstatic void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) {\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n    nfc_protocol_support[protocol]->scene_more_info.on_enter(instance);\n}\n\nstatic bool\n    nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n    consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event);\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) {\n    text_box_reset(instance->text_box);\n    widget_reset(instance->widget);\n    furi_string_reset(instance->text_box_store);\n}\n\n// SceneRead\nstatic void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {\n    popup_set_header(instance->popup, \"Don't move\", 85, 27, AlignCenter, AlignTop);\n    popup_set_icon(instance->popup, 12, 23, &A_Loading_24);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n\n    const NfcProtocol protocol = nfc_detected_protocols_get_selected(instance->detected_protocols);\n    instance->poller = nfc_poller_alloc(instance->nfc, protocol);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n    nfc_supported_cards_load_cache(instance->nfc_supported_cards);\n\n    // Start poller with the appropriate callback\n    nfc_protocol_support[protocol]->scene_read.on_enter(instance);\n\n    nfc_blink_read_start(instance);\n}\n\nstatic bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventPollerSuccess) {\n            nfc_poller_stop(instance->poller);\n            nfc_poller_free(instance->poller);\n            notification_message(instance->notifications, &sequence_success);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n            dolphin_deed(DolphinDeedNfcReadSuccess);\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerIncomplete) {\n            nfc_poller_stop(instance->poller);\n            nfc_poller_free(instance->poller);\n            bool card_read = nfc_supported_cards_read(\n                instance->nfc_supported_cards, instance->nfc_device, instance->nfc);\n            if(card_read) {\n                notification_message(instance->notifications, &sequence_success);\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n                consumed = true;\n            } else {\n                const NfcProtocol protocol =\n                    nfc_detected_protocols_get_selected(instance->detected_protocols);\n                consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);\n            }\n        } else if(event.event == NfcCustomEventPollerFailure) {\n            nfc_poller_stop(instance->poller);\n            nfc_poller_free(instance->poller);\n            if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) {\n                scene_manager_search_and_switch_to_previous_scene(\n                    instance->scene_manager, NfcSceneDetect);\n            }\n            consumed = true;\n        } else if(event.event == NfcCustomEventCardDetected) {\n            const NfcProtocol protocol =\n                nfc_detected_protocols_get_selected(instance->detected_protocols);\n            consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        nfc_poller_stop(instance->poller);\n        nfc_poller_free(instance->poller);\n        static const uint32_t possible_scenes[] = {NfcSceneSelectProtocol, NfcSceneStart};\n        scene_manager_search_and_switch_to_previous_scene_one_of(\n            instance->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_read_on_exit(NfcApp* instance) {\n    popup_reset(instance->popup);\n\n    nfc_blink_stop(instance);\n}\n\n// SceneReadMenu\nstatic void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n\n    Submenu* submenu = instance->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Save\",\n        SubmenuIndexCommonSave,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n\n    if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneGenerateInfo)) {\n        submenu_add_item(\n            submenu,\n            \"Change UID\",\n            SubmenuIndexCommonEdit,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n\n    if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {\n        submenu_add_item(\n            submenu,\n            \"Emulate UID\",\n            SubmenuIndexCommonEmulate,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n\n    } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) {\n        submenu_add_item(\n            submenu,\n            \"Emulate\",\n            SubmenuIndexCommonEmulate,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n\n    nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance);\n\n    submenu_add_item(\n        submenu,\n        \"Info\",\n        SubmenuIndexCommonInfo,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n\n    submenu_set_selected_item(\n        instance->submenu,\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneReadMenu));\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nstatic bool\n    nfc_protocol_support_scene_read_menu_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneReadMenu, event.event);\n\n        if(event.event == SubmenuIndexCommonSave) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonInfo) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneInfo);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonEmulate) {\n            dolphin_deed(DolphinDeedNfcEmulate);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);\n            consumed = true;\n        } else {\n            const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n            consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event);\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0);\n    }\n\n    return consumed;\n}\n\n// Same for read_menu and saved_menu\nstatic void nfc_protocol_support_scene_read_saved_menu_on_exit(NfcApp* instance) {\n    submenu_reset(instance->submenu);\n}\n\n// SceneReadSuccess\nstatic void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {\n    Widget* widget = instance->widget;\n\n    FuriString* temp_str = furi_string_alloc();\n    if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {\n        widget_add_text_scroll_element(\n            instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n    } else {\n        const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n        nfc_protocol_support[protocol]->scene_read_success.on_enter(instance);\n    }\n\n    furi_string_free(temp_str);\n\n    widget_add_button_element(\n        widget, GuiButtonTypeLeft, \"Retry\", nfc_protocol_support_common_widget_callback, instance);\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"More\", nfc_protocol_support_common_widget_callback, instance);\n\n    notification_message_block(instance->notifications, &sequence_set_green_255);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nstatic bool\n    nfc_protocol_support_scene_read_success_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneRetryConfirm);\n            consumed = true;\n\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu);\n            consumed = true;\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_read_success_on_exit(NfcApp* instance) {\n    notification_message_block(instance->notifications, &sequence_reset_green);\n    widget_reset(instance->widget);\n}\n\n// SceneSavedMenu\nstatic void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n\n    Submenu* submenu = instance->submenu;\n\n    // Header submenu items\n    if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {\n        submenu_add_item(\n            submenu,\n            \"Emulate UID\",\n            SubmenuIndexCommonEmulate,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n\n    } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) {\n        submenu_add_item(\n            submenu,\n            \"Emulate\",\n            SubmenuIndexCommonEmulate,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n\n    if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) {\n        submenu_add_item(\n            submenu,\n            \"Edit UID\",\n            SubmenuIndexCommonEdit,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n\n    // Protocol-dependent menu items\n    nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance);\n\n    // Trailer submenu items\n    if(nfc_has_shadow_file(instance)) {\n        submenu_add_item(\n            submenu,\n            \"Restore to Original State\",\n            SubmenuIndexCommonRestore,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n    }\n\n    submenu_add_item(\n        submenu,\n        \"Rename\",\n        SubmenuIndexCommonRename,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"Delete\",\n        SubmenuIndexCommonDelete,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"Info\",\n        SubmenuIndexCommonInfo,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n\n    submenu_set_selected_item(\n        instance->submenu,\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneSavedMenu));\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nstatic bool\n    nfc_protocol_support_scene_saved_menu_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, event.event);\n\n        if(event.event == SubmenuIndexCommonRestore) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneRestoreOriginalConfirm);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonInfo) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSupportedCard);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonRename) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonDelete) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneDelete);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonEmulate) {\n            const bool is_added =\n                scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType);\n            dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);\n            consumed = true;\n        } else if(event.event == SubmenuIndexCommonEdit) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n            consumed = true;\n        } else {\n            const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n            consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event);\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0);\n    }\n\n    return consumed;\n}\n\n// SceneSaveName\n\nstatic void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) {\n    FuriString* folder_path = furi_string_alloc();\n    TextInput* text_input = instance->text_input;\n\n    bool name_is_empty = furi_string_empty(instance->file_name);\n    if(name_is_empty) {\n        furi_string_set(instance->file_path, NFC_APP_FOLDER);\n        name_generator_make_auto(\n            instance->text_store, NFC_TEXT_STORE_SIZE, NFC_APP_FILENAME_PREFIX);\n        furi_string_set(folder_path, NFC_APP_FOLDER);\n    } else {\n        nfc_text_store_set(instance, \"%s\", furi_string_get_cstr(instance->file_name));\n        path_extract_dirname(furi_string_get_cstr(instance->file_path), folder_path);\n    }\n\n    text_input_set_header_text(text_input, \"Name the card\");\n    text_input_set_result_callback(\n        text_input,\n        nfc_protocol_support_common_text_input_done_callback,\n        instance,\n        instance->text_store,\n        NFC_NAME_SIZE,\n        name_is_empty);\n\n    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n        furi_string_get_cstr(folder_path),\n        NFC_APP_EXTENSION,\n        furi_string_get_cstr(instance->file_name));\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    furi_string_free(folder_path);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextInput);\n}\n\nstatic bool\n    nfc_protocol_support_scene_save_name_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventTextInputDone) {\n            if(!furi_string_empty(instance->file_name)) {\n                nfc_delete(instance);\n            }\n            furi_string_set(instance->file_name, instance->text_store);\n\n            if(nfc_save(instance)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);\n                dolphin_deed(\n                    scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType) ?\n                        DolphinDeedNfcAddSave :\n                        DolphinDeedNfcSave);\n\n                const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n                consumed =\n                    nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event);\n            } else {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    instance->scene_manager, NfcSceneStart);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_save_name_on_exit(NfcApp* instance) {\n    void* validator_context = text_input_get_validator_callback_context(instance->text_input);\n    text_input_set_validator(instance->text_input, NULL, NULL);\n    validator_is_file_free(validator_context);\n\n    text_input_reset(instance->text_input);\n}\n\n// SceneEmulate\n/**\n * @brief Current view displayed on the emulation scene.\n *\n * The emulation scehe has two states: the default one showing information about\n * the card being emulated, and the logs which show the raw data received from the reader.\n *\n * The user has the ability to switch betweeen these two scenes, however the prompt to switch is\n * only shown after some information had appered in the log view.\n */\nenum {\n    NfcSceneEmulateStateWidget, /**< Widget view is displayed. */\n    NfcSceneEmulateStateWidgetLog, /**< Widget view with Log button is displayed */\n    NfcSceneEmulateStateTextBox, /**< TextBox view is displayed. */\n};\n\nstatic void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {\n    Widget* widget = instance->widget;\n    TextBox* text_box = instance->text_box;\n\n    FuriString* temp_str = furi_string_alloc();\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n\n    widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64);\n\n    if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {\n        widget_add_string_element(\n            widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, \"Emulating UID\");\n\n        size_t uid_len;\n        const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);\n\n        for(size_t i = 0; i < uid_len; ++i) {\n            furi_string_cat_printf(temp_str, \"%02X \", uid[i]);\n        }\n\n        furi_string_trim(temp_str);\n\n    } else {\n        widget_add_string_element(\n            widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, \"Emulating\");\n        if(!furi_string_empty(instance->file_name)) {\n            furi_string_set(temp_str, instance->file_name);\n        } else {\n            furi_string_printf(\n                temp_str,\n                \"Unsaved\\n%s\",\n                nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));\n            furi_string_replace_str(temp_str, \"Mifare\", \"MIFARE\");\n        }\n    }\n\n    widget_add_text_box_element(\n        widget, 50, 33, 78, 31, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false);\n\n    furi_string_free(temp_str);\n\n    text_box_set_font(text_box, TextBoxFontHex);\n    text_box_set_focus(text_box, TextBoxFocusEnd);\n    furi_string_reset(instance->text_box_store);\n\n    // instance->listener is allocated in the respective on_enter() handler\n    nfc_protocol_support[protocol]->scene_emulate.on_enter(instance);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n    nfc_blink_emulate_start(instance);\n}\n\nstatic bool\n    nfc_protocol_support_scene_emulate_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    const uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneEmulate);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventListenerUpdate) {\n            // Add data button to widget if data is received for the first time\n            if(furi_string_size(instance->text_box_store)) {\n                widget_add_button_element(\n                    instance->widget,\n                    GuiButtonTypeCenter,\n                    \"Log\",\n                    nfc_protocol_support_common_widget_callback,\n                    instance);\n                scene_manager_set_scene_state(\n                    instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidgetLog);\n            }\n            // Update TextBox data\n            text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));\n            consumed = true;\n        } else if(event.event == GuiButtonTypeCenter) {\n            if(state == NfcSceneEmulateStateWidgetLog) {\n                view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);\n                scene_manager_set_scene_state(\n                    instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateTextBox);\n                consumed = true;\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(state == NfcSceneEmulateStateTextBox) {\n            view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n            scene_manager_set_scene_state(\n                instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidgetLog);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_emulate_stop_listener(NfcApp* instance) {\n    nfc_listener_stop(instance->listener);\n\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n\n    if(protocol == nfc_listener_get_protocol(instance->listener)) {\n        const NfcDeviceData* data = nfc_listener_get_data(instance->listener, protocol);\n\n        if(!nfc_device_is_equal_data(instance->nfc_device, protocol, data)) {\n            nfc_device_set_data(instance->nfc_device, protocol, data);\n            nfc_save_shadow_file(instance);\n        }\n    }\n\n    nfc_listener_free(instance->listener);\n}\n\nstatic void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) {\n    nfc_protocol_support_scene_emulate_stop_listener(instance);\n\n    // Clear view\n    widget_reset(instance->widget);\n    text_box_reset(instance->text_box);\n    furi_string_reset(instance->text_box_store);\n\n    nfc_blink_stop(instance);\n}\n\nstatic void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) {\n    UNUSED(instance);\n}\n\nstatic void nfc_protocol_support_scene_rpc_setup_ui_and_emulate(NfcApp* instance) {\n    nfc_text_store_set(instance, \"emulating\\n%s\", furi_string_get_cstr(instance->file_name));\n\n    popup_set_header(instance->popup, \"NFC\", 89, 42, AlignCenter, AlignBottom);\n    popup_set_text(instance->popup, instance->text_store, 89, 44, AlignCenter, AlignTop);\n    popup_set_icon(instance->popup, 0, 12, &I_RFIDDolphinSend_97x61);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n\n    notification_message(instance->notifications, &sequence_display_backlight_on);\n    nfc_blink_emulate_start(instance);\n\n    const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n    nfc_protocol_support[protocol]->scene_emulate.on_enter(instance);\n\n    instance->rpc_state = NfcRpcStateEmulating;\n}\n\nstatic bool nfc_protocol_support_scene_rpc_on_event(NfcApp* instance, SceneManagerEvent event) {\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventRpcLoadFile) {\n            bool success = false;\n            if(instance->rpc_state == NfcRpcStateIdle) {\n                if(nfc_load_file(instance, instance->file_path, false)) {\n                    nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance);\n                    success = true;\n                } else {\n                    rpc_system_app_set_error_code(\n                        instance->rpc_ctx, RpcAppSystemErrorCodeParseFile);\n                    rpc_system_app_set_error_text(instance->rpc_ctx, \"Cannot load key file\");\n                }\n            }\n            rpc_system_app_confirm(instance->rpc_ctx, success);\n        } else if(event.event == NfcCustomEventRpcExit) {\n            rpc_system_app_confirm(instance->rpc_ctx, true);\n            scene_manager_stop(instance->scene_manager);\n            view_dispatcher_stop(instance->view_dispatcher);\n        } else if(event.event == NfcCustomEventRpcSessionClose) {\n            scene_manager_stop(instance->scene_manager);\n            view_dispatcher_stop(instance->view_dispatcher);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nstatic void nfc_protocol_support_scene_rpc_on_exit(NfcApp* instance) {\n    if(instance->rpc_state == NfcRpcStateEmulating) {\n        nfc_protocol_support_scene_emulate_stop_listener(instance);\n    }\n\n    popup_reset(instance->popup);\n    text_box_reset(instance->text_box);\n    furi_string_reset(instance->text_box_store);\n\n    nfc_blink_stop(instance);\n}\n\nstatic const NfcProtocolSupportCommonSceneBase\n    nfc_protocol_support_scenes[NfcProtocolSupportSceneCount] = {\n        [NfcProtocolSupportSceneInfo] =\n            {\n                .on_enter = nfc_protocol_support_scene_info_on_enter,\n                .on_event = nfc_protocol_support_scene_info_on_event,\n                .on_exit = nfc_protocol_support_scene_info_on_exit,\n            },\n        [NfcProtocolSupportSceneMoreInfo] =\n            {\n                .on_enter = nfc_protocol_support_scene_more_info_on_enter,\n                .on_event = nfc_protocol_support_scene_more_info_on_event,\n                .on_exit = nfc_protocol_support_scene_more_info_on_exit,\n            },\n        [NfcProtocolSupportSceneRead] =\n            {\n                .on_enter = nfc_protocol_support_scene_read_on_enter,\n                .on_event = nfc_protocol_support_scene_read_on_event,\n                .on_exit = nfc_protocol_support_scene_read_on_exit,\n            },\n        [NfcProtocolSupportSceneReadMenu] =\n            {\n                .on_enter = nfc_protocol_support_scene_read_menu_on_enter,\n                .on_event = nfc_protocol_support_scene_read_menu_on_event,\n                .on_exit = nfc_protocol_support_scene_read_saved_menu_on_exit,\n            },\n        [NfcProtocolSupportSceneReadSuccess] =\n            {\n                .on_enter = nfc_protocol_support_scene_read_success_on_enter,\n                .on_event = nfc_protocol_support_scene_read_success_on_event,\n                .on_exit = nfc_protocol_support_scene_read_success_on_exit,\n            },\n        [NfcProtocolSupportSceneSavedMenu] =\n            {\n                .on_enter = nfc_protocol_support_scene_saved_menu_on_enter,\n                .on_event = nfc_protocol_support_scene_saved_menu_on_event,\n                .on_exit = nfc_protocol_support_scene_read_saved_menu_on_exit,\n            },\n        [NfcProtocolSupportSceneSaveName] =\n            {\n                .on_enter = nfc_protocol_support_scene_save_name_on_enter,\n                .on_event = nfc_protocol_support_scene_save_name_on_event,\n                .on_exit = nfc_protocol_support_scene_save_name_on_exit,\n            },\n        [NfcProtocolSupportSceneEmulate] =\n            {\n                .on_enter = nfc_protocol_support_scene_emulate_on_enter,\n                .on_event = nfc_protocol_support_scene_emulate_on_event,\n                .on_exit = nfc_protocol_support_scene_emulate_on_exit,\n            },\n        [NfcProtocolSupportSceneRpc] =\n            {\n                .on_enter = nfc_protocol_support_scene_rpc_on_enter,\n                .on_event = nfc_protocol_support_scene_rpc_on_event,\n                .on_exit = nfc_protocol_support_scene_rpc_on_exit,\n            },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h",
    "content": "/**\n * @file nfc_protocol_support.h\n * @brief Interface for application-level protocol support.\n *\n * NFC protocol support helper abstracts common scenes with a single interface\n * and lets each protocol decide on concrete implementation.\n *\n * # Integrating a new protocol into the application\n *\n * Most of the scenes in the NFC application work through abstract APIs, so they do not need\n * protocol-specific versions of themselves. However, when such a situation\n * occurs, the protocol support helper provides another level of abstraction to hide\n * the protocol-specific details and isolate them to separate modules.\n *\n * @see nfc_protocol.h for more information on adding library protocols.\n *\n * The steps for adding support for a library protocol are described below.\n *\n * ## 1. Create the files\n *\n * ### 1.1 Recommended file structure\n *\n * The recommended file structure for a protocol support is as follows:\n *\n * ```text\n * protocol_support\n *        |\n *        +- protocol_name\n *                |\n *                +- protocol_name.h\n *                |\n *                +- protocol_name.c\n *                |\n *                +- protocol_name_render.h\n *                |\n *                +- protocol_name_render.c\n *                |\n * ```\n * ### 1.2 File structure explanation\n *\n * | Filename               | Explanation |\n * |:-----------------------|:------------|\n * | protocol_name.h        | Interface structure declaration used in `nfc_protocol_support_defs.c`. |\n * | protocol_name.c        | Protocol-specific scene implemenatations and definitions. |\n * | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. |\n * | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.|\n *\n * ## 2. Implement the code\n *\n * ### 2.1 Features\n *\n * Decide what features the protocol will be providing. The features can be combined using bitwise OR (`\"|\"`).\n * This choice influences which scenes will have to be implemented in step 2.2.\n *\n * @see NfcProtocolFeature for the enumeration of possible features to implement.\n *\n * ### 2.2 Scenes\n *\n * If a particular scene is not implemented, its empty placeholder from nfc_protocol_support_gui_common.h must be used instead.\n *\n * @see nfc_protocol_support_common.h for the enumeration of all scenes that can be implemented.\n * @see nfc_protocol_support_base.h for the scene implementation details.\n *\n * ### 2.3. Registering the protocol support\n *\n * After completing the protocol support, it must be registered within the application in order for it to be usable.\n *\n * In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]`\n * array under the appropriate index.\n *\n * ## Done!\n *\n * @note It will not always be possible to abstract all of the protocol's functionality using the protocol support helper.\n * In such cases, creating separate protocol-specific scenes is okay (as an example, note the `nfc/scenes/nfc_scene_mf_classic_*` scenes which didn't fit this paradigm).\n */\n#pragma once\n\n#include <gui/scene_manager.h>\n#include <lib/nfc/protocols/nfc_protocol.h>\n\n#include \"nfc_protocol_support_common.h\"\n\n/**\n * @brief Abstract interface for on_enter() scene handler.\n *\n * Is to be called whenever a scene is entered to.\n *\n * @param[in] scene identifier of the scene associated with the handler.\n * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler).\n */\nvoid nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context);\n\n/**\n * @brief Abstract interface for on_event() scene handler.\n *\n * @param[in] scene identifier of the scene associated with the handler.\n * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler).\n * @param[in] event SceneManager event to be handled by the scene.\n * @returns true if the event was consumed, false otherwise.\n */\nbool nfc_protocol_support_on_event(\n    NfcProtocolSupportScene scene,\n    void* context,\n    SceneManagerEvent event);\n\n/**\n * @brief Abstract interface for on_exit() scene handler.\n *\n * Is to be called whenever a scene is exited from.\n *\n * @param[in] scene identifier of the scene associated with the handler.\n * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler).\n */\nvoid nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context);\n\nbool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h",
    "content": "/**\n * @file nfc_protocol_support_base.h\n * @brief Abstract interface for application-level protocol support.\n */\n#pragma once\n\n#include <core/string.h>\n\n#include \"../../nfc_app.h\"\n#include \"../../nfc_app_i.h\"\n\n/**\n * @brief Scene entry handler.\n *\n * @param[in,out] instance pointer to the NFC application instance.\n */\ntypedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance);\n\n/**\n * @brief Scene event handler.\n *\n * @param[in,out] instance pointer to the NFC application instance.\n * @param[in] event scene manager event that has occurred.\n * @returns true if the event was handled, false otherwise.\n */\ntypedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, SceneManagerEvent event);\n\n/**\n * @brief Abstract scene interface.\n *\n * on_exit() handler is not implemented due to being redundant.\n */\ntypedef struct {\n    NfcProtocolSupportOnEnter on_enter; /**< Pointer to the on_enter() function. */\n    NfcProtocolSupportOnEvent on_event; /**< Pointer to the on_event() function. */\n} NfcProtocolSupportSceneBase;\n\n/**\n * @brief Abstract protocol support interface.\n */\ntypedef struct {\n    const uint32_t features; /**< Feature bitmask supported by the protocol. */\n\n    /**\n     * @brief Handlers for protocol-specific info scene.\n     *\n     * This scene displays general information about a saved or recently read card.\n     * It may include a button that will lead to more information being shown.\n     */\n    NfcProtocolSupportSceneBase scene_info;\n\n    /**\n     * @brief Handlers for protocol-specific extended info scene.\n     *\n     * This scene shows more information about a saved or\n     * recently read card, such as memory dumps.\n     *\n     * It may include (a) button(s) and/or menu(s) that will lead to\n     * protocol-specific scenes not covered in this helper.\n     */\n    NfcProtocolSupportSceneBase scene_more_info;\n\n    /**\n     * @brief Handlers for protocol-specific read scene.\n     *\n     * This scene is activated when a read operation is in progress.\n     * It is responsible for creating a poller and for handling its events.\n     */\n    NfcProtocolSupportSceneBase scene_read;\n\n    /**\n     * @brief Handlers for protocol-specific read menu scene.\n     *\n     * This scene presents the user with options available for the\n     * recenly read card. Such options may include:\n     * * Saving\n     * * Getting information\n     * * Emulating etc.\n     */\n    NfcProtocolSupportSceneBase scene_read_menu;\n\n    /**\n     * @brief Handlers for protocol-specific read success scene.\n     *\n     * This scene is activated after a successful read operation.\n     * It is responsible for displaying a very short summary about\n     * the card that was just read.\n     */\n    NfcProtocolSupportSceneBase scene_read_success;\n\n    /**\n     * @brief Handlers for protocol-specific saved file menu scene.\n     *\n     * This scene presents the user with options available for a\n     * card loaded from file. Such options may include:\n     * * Renaming\n     * * Deleting\n     * * Getting information\n     * * Emulating etc.\n     */\n    NfcProtocolSupportSceneBase scene_saved_menu;\n\n    /**\n     * @brief Handlers for protocol-specific name entry scene.\n     *\n     * This scene is used to enter a file name when saving or renaming a file.\n     */\n    NfcProtocolSupportSceneBase scene_save_name;\n\n    /**\n     * @brief Handlers for protocol-specific emulate scene.\n     *\n     * This scene is activated when an emulation operation is in progress.\n     * It is responsible for creating a listener and for handling its events.\n     */\n    NfcProtocolSupportSceneBase scene_emulate;\n} NfcProtocolSupportBase;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h",
    "content": "/**\n * @file nfc_protocol_support_common.h\n * @brief Common application-level protocol support definitions.\n */\n#pragma once\n\n/**\n * @brief Enumeration of protocol features.\n */\ntypedef enum {\n    NfcProtocolFeatureNone = 0, /**< No features are supported. */\n    NfcProtocolFeatureEmulateUid = 1UL << 0, /**< Partial emulation is supported. */\n    NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */\n    NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */\n    NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */\n} NfcProtocolFeature;\n\n/**\n * @brief Enumeration of protocol-aware scenes.\n *\n * These are the scenes that are common to all protocols, but require\n * a protocol-specific implementation.\n */\ntypedef enum {\n    NfcProtocolSupportSceneInfo, /**< Display general card information. */\n    NfcProtocolSupportSceneMoreInfo, /**< Display more card information. */\n    NfcProtocolSupportSceneRead, /**< Shown when reading a card. */\n    NfcProtocolSupportSceneReadMenu, /**< Menu with options available for the recently read card. */\n    NfcProtocolSupportSceneReadSuccess, /**< Shown after having successfully read a card. */\n    NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */\n    NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */\n    NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */\n    NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */\n\n    NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */\n} NfcProtocolSupportScene;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c",
    "content": "/**\n * @file nfc_protocol_support_defs.c\n * @brief Application-level protocol support definitions.\n *\n * This file is to be modified whenever support for\n * a new protocol is to be added.\n */\n#include \"nfc_protocol_support_base.h\"\n\n#include <nfc/protocols/nfc_protocol.h>\n\n#include \"iso14443_3a/iso14443_3a.h\"\n#include \"iso14443_3b/iso14443_3b.h\"\n#include \"iso14443_4a/iso14443_4a.h\"\n#include \"iso14443_4b/iso14443_4b.h\"\n#include \"iso15693_3/iso15693_3.h\"\n#include \"felica/felica.h\"\n#include \"mf_ultralight/mf_ultralight.h\"\n#include \"mf_classic/mf_classic.h\"\n#include \"mf_plus/mf_plus.h\"\n#include \"mf_desfire/mf_desfire.h\"\n#include \"slix/slix.h\"\n#include \"st25tb/st25tb.h\"\n\n/**\n * @brief Array of pointers to concrete protocol support implementations.\n *\n * When adding support for a new protocol, add it to the end of this array\n * under its respective index.\n *\n * @see nfc_protocol.h\n */\nconst NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a,\n    [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b,\n    [NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a,\n    [NfcProtocolIso14443_4b] = &nfc_protocol_support_iso14443_4b,\n    [NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3,\n    [NfcProtocolFelica] = &nfc_protocol_support_felica,\n    [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight,\n    [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic,\n    [NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus,\n    [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,\n    [NfcProtocolSlix] = &nfc_protocol_support_slix,\n    [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,\n    /* Add new protocol support implementations here */\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h",
    "content": "/**\n * @file nfc_protocol_support_defs.h\n * @brief Application-level protocol support declarations.\n */\n#pragma once\n\n#include \"nfc_protocol_support_base.h\"\n\n/**\n * @brief Declaraion of array of pointers to protocol support implementations.\n */\nextern const NfcProtocolSupportBase* nfc_protocol_support[];\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c",
    "content": "#include \"nfc_protocol_support_gui_common.h\"\n\n#include \"nfc/nfc_app_i.h\"\n\nvoid nfc_protocol_support_common_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    NfcApp* instance = context;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_protocol_support_common_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    furi_assert(context);\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_protocol_support_common_byte_input_done_callback(void* context) {\n    furi_assert(context);\n    NfcApp* instance = context;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_protocol_support_common_text_input_done_callback(void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);\n}\n\nvoid nfc_protocol_support_common_on_enter_empty(NfcApp* instance) {\n    UNUSED(instance);\n}\n\nbool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event) {\n    UNUSED(instance);\n    UNUSED(event);\n    return event.type != SceneManagerEventTypeBack;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h",
    "content": "/**\n * @file nfc_protocol_support_gui_common.h\n * @brief Common GUI functions and definitions.\n */\n#pragma once\n\n#include <gui/modules/widget.h>\n\n#include \"nfc/nfc_app.h\"\n#include \"nfc/nfc_app_i.h\"\n\n/**\n * @brief Common submenu indices.\n */\nenum {\n    SubmenuIndexCommonSave, /**< Save menu option. */\n    SubmenuIndexCommonEmulate, /**< Emulate menu option. */\n    SubmenuIndexCommonEdit, /**< Edit menu option. */\n    SubmenuIndexCommonInfo, /**< Info menu option. */\n    SubmenuIndexCommonRename, /**< Rename menu option. */\n    SubmenuIndexCommonDelete, /**< Delete menu option. */\n    SubmenuIndexCommonRestore, /**< Restore menu option. */\n    SubmenuIndexCommonMax, /**< Special value, internal use. */\n};\n\n/**\n * @brief Common submenu callback.\n *\n * Called each time the user presses on a selected submenu item.\n *\n * @param[in,out] context pointer to a user-defined context object.\n * @param[in] index index of the item that was activated.\n */\nvoid nfc_protocol_support_common_submenu_callback(void* context, uint32_t index);\n\n/**\n * @brief Common widget callback.\n *\n * Called each time the user presses on a selected widget element.\n *\n * @param[in] result identifier of the activated button.\n * @param[in] type type of press action.\n * @param[in,out] context pointer to a user-defined context object.\n */\nvoid nfc_protocol_support_common_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context);\n\n/**\n * @brief Common byte input callback.\n *\n * Called each time the user accepts the byte input.\n *\n * @param[in,out] context pointer to a user-defined context object.\n */\nvoid nfc_protocol_support_common_byte_input_done_callback(void* context);\n\n/**\n * @brief Common text input callback.\n *\n * Called each time the user accepts the text input.\n *\n * @param[in,out] context pointer to a user-defined context object.\n */\nvoid nfc_protocol_support_common_text_input_done_callback(void* context);\n\n/**\n * @brief Empty on_enter() handler.\n *\n * Does nothing.\n *\n * @param[in] instance pointer to the NFC application instance.\n */\nvoid nfc_protocol_support_common_on_enter_empty(NfcApp* instance);\n\n/**\n * @brief Empty on_event() handler.\n *\n * Does nothing and returns true.\n *\n * @param[in] instance pointer to the NFC application instance.\n * @param[in] event custom event type that has occurred.\n * @returns always true.\n */\nbool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h",
    "content": "/**\n * @file nfc_protocol_support_render_common.h\n * @brief Common formatting-related defines.\n */\n#pragma once\n\n/**\n * @brief Displayed information verbosity level.\n */\ntypedef enum {\n    NfcProtocolFormatTypeShort, /**< Short format, terse info. */\n    NfcProtocolFormatTypeFull, /**< Full format, verbose info. */\n} NfcProtocolFormatType;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c",
    "content": "#include \"nfc_protocol_support_unlock_helper.h\"\n\nstatic void nfc_scene_read_setup_view(NfcApp* instance) {\n    Popup* popup = instance->popup;\n    popup_reset(popup);\n    uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);\n\n    if(state == NfcSceneReadMenuStateCardSearch) {\n        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n        popup_set_header(instance->popup, \"Unlocking\", 97, 15, AlignCenter, AlignTop);\n        popup_set_text(\n            instance->popup, \"Hold card next\\nto Flipper's back\", 94, 27, AlignCenter, AlignTop);\n    } else {\n        popup_set_header(instance->popup, \"Don't move\", 85, 27, AlignCenter, AlignTop);\n        popup_set_icon(instance->popup, 12, 20, &A_Loading_24);\n    }\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nvoid nfc_unlock_helper_setup_from_state(NfcApp* instance) {\n    bool unlocking =\n        scene_manager_has_previous_scene(\n            instance->scene_manager, NfcSceneMfUltralightUnlockWarn) ||\n        scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn);\n\n    uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound;\n\n    scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);\n\n    nfc_scene_read_setup_view(instance);\n}\n\nvoid nfc_unlock_helper_card_detected_handler(NfcApp* instance) {\n    scene_manager_set_scene_state(\n        instance->scene_manager, NfcSceneRead, NfcSceneReadMenuStateCardFound);\n    nfc_scene_read_setup_view(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.h",
    "content": "#include \"nfc/nfc_app_i.h\"\n\ntypedef enum {\n    NfcSceneReadMenuStateCardSearch,\n    NfcSceneReadMenuStateCardFound,\n} NfcSceneUnlockReadState;\n\nvoid nfc_unlock_helper_setup_from_state(NfcApp* instance);\nvoid nfc_unlock_helper_card_detected_handler(NfcApp* instance);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/slix/slix.c",
    "content": "#include \"slix.h\"\n#include \"slix_render.h\"\n\n#include <nfc/protocols/slix/slix_poller.h>\n#include <nfc/protocols/slix/slix_listener.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_slix(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_reset(instance->widget);\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic void nfc_scene_more_info_on_enter_slix(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_render_iso15693_3_system_info(slix_get_base_data(data), temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolSlix);\n\n    NfcApp* instance = context;\n    const SlixPollerEvent* slix_event = event.event_data;\n\n    if(slix_event->type == SlixPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_slix(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_slix, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_slix(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_slix_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolSlix);\n    furi_assert(event.event_data);\n\n    NfcApp* nfc = context;\n    SlixListenerEvent* slix_event = event.event_data;\n\n    if(slix_event->type == SlixListenerEventTypeCustomCommand) {\n        if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {\n            furi_string_cat_printf(nfc->text_box_store, \"R:\");\n            for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) {\n                furi_string_cat_printf(\n                    nfc->text_box_store,\n                    \" %02X\",\n                    bit_buffer_get_byte(slix_event->data->buffer, i));\n            }\n            furi_string_push_back(nfc->text_box_store, '\\n');\n            view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);\n        }\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_emulate_on_enter_slix(NfcApp* instance) {\n    const SlixData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolSlix);\n\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolSlix, data);\n    nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance);\n}\n\nstatic bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n        return true;\n    }\n\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_slix = {\n    .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_slix,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_more_info =\n        {\n            .on_enter = nfc_scene_more_info_on_enter_slix,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_slix,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_slix,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_saved_menu_on_event_slix,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_scene_emulate_on_enter_slix,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/slix/slix.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_slix;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/slix/slix_render.c",
    "content": "#include \"slix_render.h\"\n\nvoid nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) {\n    nfc_render_iso15693_3_brief(slix_get_base_data(data), str);\n\n    if(format_type != NfcProtocolFormatTypeFull) return;\n    const SlixType slix_type = slix_get_type(data);\n\n    furi_string_cat(str, \"\\n::::::::::::::::::[Passwords]:::::::::::::::::\\n\");\n\n    static const char* slix_password_names[] = {\n        \"Read\",\n        \"Write\",\n        \"Privacy\",\n        \"Destroy\",\n        \"EAS/AFI\",\n    };\n\n    for(uint32_t i = 0; i < SlixPasswordTypeCount; ++i) {\n        if(slix_type_supports_password(slix_type, i)) {\n            furi_string_cat_printf(\n                str, \"%s :  %08lX\\n\", slix_password_names[i], data->passwords[i]);\n        }\n    }\n\n    furi_string_cat(str, \":::::::::::::::::::[Lock bits]::::::::::::::::::::\\n\");\n\n    if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {\n        furi_string_cat_printf(\n            str, \"EAS: %s locked\\n\", data->system_info.lock_bits.eas ? \"\" : \"not\");\n    }\n\n    if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n        furi_string_cat_printf(\n            str, \"PPL: %s locked\\n\", data->system_info.lock_bits.ppl ? \"\" : \"not\");\n\n        const SlixProtection protection = data->system_info.protection;\n\n        furi_string_cat(str, \"::::::::::::[Page protection]::::::::::::\\n\");\n        furi_string_cat_printf(str, \"Pointer: H >= %02X\\n\", protection.pointer);\n\n        const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? \"\" : \"un\";\n        const char* rl = (protection.condition & SLIX_PP_CONDITION_RL) ? \"\" : \"un\";\n\n        const char* wh = (protection.condition & SLIX_PP_CONDITION_WH) ? \"\" : \"un\";\n        const char* wl = (protection.condition & SLIX_PP_CONDITION_WL) ? \"\" : \"un\";\n\n        furi_string_cat_printf(str, \"R:  H %sprotec. L %sprotec.\\n\", rh, rl);\n        furi_string_cat_printf(str, \"W: H %sprotec. L %sprotec.\\n\", wh, wl);\n    }\n\n    if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {\n        furi_string_cat(str, \"::::::::::::::::::::[Privacy]::::::::::::::::::::::\\n\");\n        furi_string_cat_printf(str, \"Privacy mode: %sabled\\n\", data->privacy ? \"en\" : \"dis\");\n    }\n\n    if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {\n        furi_string_cat(str, \":::::::::::::::::::[Signature]::::::::::::::::::\\n\");\n        for(uint32_t i = 0; i < 4; ++i) {\n            furi_string_cat_printf(str, \"%02X \", data->signature[i]);\n        }\n\n        furi_string_cat(str, \"[ ... ]\");\n\n        for(uint32_t i = 0; i < 3; ++i) {\n            furi_string_cat_printf(str, \" %02X\", data->signature[sizeof(SlixSignature) - i - 1]);\n        }\n    }\n\n    furi_string_cat(str, \"\\n\\e#ISO15693-3 data\");\n    nfc_render_iso15693_3_extra(slix_get_base_data(data), str);\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/slix/slix_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/slix/slix.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n#include \"../iso15693_3/iso15693_3_render.h\"\n\nvoid nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c",
    "content": "#include \"st25tb.h\"\n#include \"st25tb_render.h\"\n\n#include <nfc/protocols/st25tb/st25tb_poller.h>\n\n#include \"nfc/nfc_app_i.h\"\n\n#include \"../nfc_protocol_support_common.h\"\n#include \"../nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_info_on_enter_st25tb(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);\n\n    FuriString* temp_str = furi_string_alloc();\n    nfc_append_filename_string_when_present(instance, temp_str);\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_st25tb_info(data, NfcProtocolFormatTypeFull, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolSt25tb);\n\n    NfcApp* instance = context;\n    const St25tbPollerEvent* st25tb_event = event.event_data;\n\n    if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {\n        st25tb_event->data->mode_request.mode = St25tbPollerModeRead;\n    } else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        return NfcCommandStop;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic void nfc_scene_read_on_enter_st25tb(NfcApp* instance) {\n    nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_st25tb, instance);\n}\n\nstatic void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) {\n    const NfcDevice* device = instance->nfc_device;\n    const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);\n\n    FuriString* temp_str = furi_string_alloc();\n    furi_string_cat_printf(\n        temp_str, \"\\e#%s\\n\", nfc_device_get_name(device, NfcDeviceNameTypeFull));\n    nfc_render_st25tb_info(data, NfcProtocolFormatTypeShort, temp_str);\n\n    widget_add_text_scroll_element(\n        instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n\n    furi_string_free(temp_str);\n}\n\nstatic bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) {\n    if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n        return true;\n    }\n\n    return false;\n}\n\nconst NfcProtocolSupportBase nfc_protocol_support_st25tb = {\n    .features = NfcProtocolFeatureNone,\n\n    .scene_info =\n        {\n            .on_enter = nfc_scene_info_on_enter_st25tb,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read =\n        {\n            .on_enter = nfc_scene_read_on_enter_st25tb,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_read_success =\n        {\n            .on_enter = nfc_scene_read_success_on_enter_st25tb,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_saved_menu =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_scene_saved_menu_on_event_st25tb,\n        },\n    .scene_save_name =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n    .scene_emulate =\n        {\n            .on_enter = nfc_protocol_support_common_on_enter_empty,\n            .on_event = nfc_protocol_support_common_on_event_empty,\n        },\n};\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/st25tb/st25tb.h",
    "content": "#pragma once\n\n#include \"../nfc_protocol_support_base.h\"\n\nextern const NfcProtocolSupportBase nfc_protocol_support_st25tb;\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c",
    "content": "#include \"st25tb_render.h\"\n#include <nfc/protocols/st25tb/st25tb.h>\n#include <machine/endian.h>\n\nvoid nfc_render_st25tb_info(\n    const St25tbData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str) {\n    furi_string_cat_printf(str, \"UID:\");\n\n    for(size_t i = 0; i < ST25TB_UID_SIZE; i++) {\n        furi_string_cat_printf(str, \" %02X\", data->uid[i]);\n    }\n\n    if(format_type == NfcProtocolFormatTypeFull) {\n        furi_string_cat_printf(\n            str, \"\\nSys. OTP: %08lX\", (uint32_t)__bswap32(data->system_otp_block));\n        furi_string_cat_printf(str, \"\\n::::::::::::::::::::::[Blocks]::::::::::::::::::::::\");\n        for(size_t i = 0; i < st25tb_get_block_count(data->type); i += 2) {\n            furi_string_cat_printf(\n                str,\n                \"\\n %02X   %08lX  %08lX\",\n                i,\n                (uint32_t)__bswap32(data->blocks[i]),\n                (uint32_t)__bswap32(data->blocks[i + 1]));\n        }\n    }\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.h",
    "content": "#pragma once\n\n#include <nfc/protocols/st25tb/st25tb.h>\n\n#include \"../nfc_protocol_support_render_common.h\"\n\nvoid nfc_render_st25tb_info(\n    const St25tbData* data,\n    NfcProtocolFormatType format_type,\n    FuriString* str);\n"
  },
  {
    "path": "applications/main/nfc/helpers/slix_unlock.c",
    "content": "#include \"slix_unlock.h\"\n\n#include <furi/furi.h>\n\n#define SLIX_UNLOCK_PASSWORD_NUM_MAX (2)\n\nstruct SlixUnlock {\n    SlixUnlockMethod method;\n    SlixPassword password_arr[SLIX_UNLOCK_PASSWORD_NUM_MAX];\n    size_t password_arr_len;\n    size_t password_idx;\n};\n\nstatic const SlixPassword tonie_box_pass_arr[] = {0x5B6EFD7F, 0x0F0F0F0F};\n\nSlixUnlock* slix_unlock_alloc(void) {\n    SlixUnlock* instance = malloc(sizeof(SlixUnlock));\n\n    return instance;\n}\n\nvoid slix_unlock_free(SlixUnlock* instance) {\n    furi_assert(instance);\n\n    free(instance);\n}\n\nvoid slix_unlock_reset(SlixUnlock* instance) {\n    furi_assert(instance);\n\n    memset(instance, 0, sizeof(SlixUnlock));\n}\n\nvoid slix_unlock_set_method(SlixUnlock* instance, SlixUnlockMethod method) {\n    furi_assert(instance);\n\n    instance->method = method;\n    if(method == SlixUnlockMethodTonieBox) {\n        instance->password_arr_len = COUNT_OF(tonie_box_pass_arr);\n        memcpy(instance->password_arr, tonie_box_pass_arr, sizeof(tonie_box_pass_arr));\n    }\n}\n\nvoid slix_unlock_set_password(SlixUnlock* instance, SlixPassword password) {\n    furi_assert(instance);\n    furi_assert(instance->method == SlixUnlockMethodManual);\n\n    instance->password_arr[0] = password;\n    instance->password_arr_len = 1;\n}\n\nbool slix_unlock_get_next_password(SlixUnlock* instance, SlixPassword* password) {\n    furi_assert(instance);\n    furi_assert(password);\n\n    bool password_set = false;\n    if(instance->password_arr_len) {\n        *password = instance->password_arr[instance->password_idx++];\n        instance->password_idx %= instance->password_arr_len;\n        password_set = true;\n    }\n\n    return password_set;\n}\n"
  },
  {
    "path": "applications/main/nfc/helpers/slix_unlock.h",
    "content": "#pragma once\n\n#include <nfc/protocols/slix/slix.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    SlixUnlockMethodManual,\n    SlixUnlockMethodTonieBox,\n} SlixUnlockMethod;\n\ntypedef struct SlixUnlock SlixUnlock;\n\nSlixUnlock* slix_unlock_alloc(void);\n\nvoid slix_unlock_free(SlixUnlock* instance);\n\nvoid slix_unlock_reset(SlixUnlock* instance);\n\nvoid slix_unlock_set_method(SlixUnlock* instance, SlixUnlockMethod method);\n\nvoid slix_unlock_set_password(SlixUnlock* instance, SlixPassword password);\n\nbool slix_unlock_get_next_password(SlixUnlock* instance, SlixPassword* password);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/nfc/nfc_app.c",
    "content": "#include \"nfc_app_i.h\"\n#include \"helpers/protocol_support/nfc_protocol_support.h\"\n\n#include <dolphin/dolphin.h>\n\nbool nfc_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    NfcApp* nfc = context;\n    return scene_manager_handle_custom_event(nfc->scene_manager, event);\n}\n\nbool nfc_back_event_callback(void* context) {\n    furi_assert(context);\n    NfcApp* nfc = context;\n    return scene_manager_handle_back_event(nfc->scene_manager);\n}\n\nstatic void nfc_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    NfcApp* nfc = (NfcApp*)context;\n\n    furi_assert(nfc->rpc_ctx);\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose);\n        rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);\n        nfc->rpc_ctx = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcExit);\n    } else if(event->type == RpcAppEventTypeLoadFile) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeString);\n        furi_string_set(nfc->file_path, event->data.string);\n        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoadFile);\n    } else {\n        rpc_system_app_confirm(nfc->rpc_ctx, false);\n    }\n}\n\nNfcApp* nfc_app_alloc(void) {\n    NfcApp* instance = malloc(sizeof(NfcApp));\n\n    instance->view_dispatcher = view_dispatcher_alloc();\n    instance->scene_manager = scene_manager_alloc(&nfc_scene_handlers, instance);\n    view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);\n    view_dispatcher_set_custom_event_callback(\n        instance->view_dispatcher, nfc_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        instance->view_dispatcher, nfc_back_event_callback);\n\n    instance->nfc = nfc_alloc();\n\n    instance->detected_protocols = nfc_detected_protocols_alloc();\n    instance->felica_auth = felica_auth_alloc();\n    instance->mf_ul_auth = mf_ultralight_auth_alloc();\n    instance->slix_unlock = slix_unlock_alloc();\n    instance->mfc_key_cache = mf_classic_key_cache_alloc();\n    instance->nfc_supported_cards = nfc_supported_cards_alloc();\n\n    // Nfc device\n    instance->nfc_device = nfc_device_alloc();\n    nfc_device_set_loading_callback(instance->nfc_device, nfc_show_loading_popup, instance);\n\n    // Open GUI record\n    instance->gui = furi_record_open(RECORD_GUI);\n\n    // Open Notification record\n    instance->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // Open Storage record\n    instance->storage = furi_record_open(RECORD_STORAGE);\n\n    // Open Dialogs record\n    instance->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    // Submenu\n    instance->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewMenu, submenu_get_view(instance->submenu));\n\n    // Dialog\n    instance->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(instance->dialog_ex));\n\n    // Popup\n    instance->popup = popup_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewPopup, popup_get_view(instance->popup));\n\n    // Loading\n    instance->loading = loading_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewLoading, loading_get_view(instance->loading));\n\n    // Text Input\n    instance->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewTextInput, text_input_get_view(instance->text_input));\n\n    // Byte Input\n    instance->byte_input = byte_input_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewByteInput, byte_input_get_view(instance->byte_input));\n\n    // TextBox\n    instance->text_box = text_box_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewTextBox, text_box_get_view(instance->text_box));\n    instance->text_box_store = furi_string_alloc();\n\n    // Custom Widget\n    instance->widget = widget_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget));\n\n    // Dict attack\n    instance->dict_attack = dict_attack_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack));\n\n    // Detect Reader\n    instance->detect_reader = detect_reader_alloc();\n    view_dispatcher_add_view(\n        instance->view_dispatcher,\n        NfcViewDetectReader,\n        detect_reader_get_view(instance->detect_reader));\n\n    instance->iso14443_3a_edit_data = iso14443_3a_alloc();\n    instance->file_path = furi_string_alloc_set(NFC_APP_FOLDER);\n    instance->file_name = furi_string_alloc();\n\n    return instance;\n}\n\nvoid nfc_app_free(NfcApp* instance) {\n    furi_assert(instance);\n\n    if(instance->rpc_ctx) {\n        rpc_system_app_send_exited(instance->rpc_ctx);\n        rpc_system_app_set_callback(instance->rpc_ctx, NULL, NULL);\n    }\n\n    nfc_free(instance->nfc);\n\n    nfc_detected_protocols_free(instance->detected_protocols);\n    felica_auth_free(instance->felica_auth);\n    mf_ultralight_auth_free(instance->mf_ul_auth);\n    slix_unlock_free(instance->slix_unlock);\n    mf_classic_key_cache_free(instance->mfc_key_cache);\n    nfc_supported_cards_free(instance->nfc_supported_cards);\n\n    // Nfc device\n    nfc_device_free(instance->nfc_device);\n\n    // Submenu\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewMenu);\n    submenu_free(instance->submenu);\n\n    // DialogEx\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDialogEx);\n    dialog_ex_free(instance->dialog_ex);\n\n    // Popup\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewPopup);\n    popup_free(instance->popup);\n\n    // Loading\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewLoading);\n    loading_free(instance->loading);\n\n    // TextInput\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextInput);\n    text_input_free(instance->text_input);\n\n    // ByteInput\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewByteInput);\n    byte_input_free(instance->byte_input);\n\n    // TextBox\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextBox);\n    text_box_free(instance->text_box);\n    furi_string_free(instance->text_box_store);\n\n    // Custom Widget\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewWidget);\n    widget_free(instance->widget);\n\n    // Dict attack\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDictAttack);\n    dict_attack_free(instance->dict_attack);\n\n    // Detect reader\n    view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDetectReader);\n    detect_reader_free(instance->detect_reader);\n\n    // View Dispatcher\n    view_dispatcher_free(instance->view_dispatcher);\n\n    // Scene Manager\n    scene_manager_free(instance->scene_manager);\n\n    furi_record_close(RECORD_DIALOGS);\n    furi_record_close(RECORD_STORAGE);\n    furi_record_close(RECORD_NOTIFICATION);\n    // GUI\n    furi_record_close(RECORD_GUI);\n    instance->gui = NULL;\n\n    instance->notifications = NULL;\n\n    iso14443_3a_free(instance->iso14443_3a_edit_data);\n    furi_string_free(instance->file_path);\n    furi_string_free(instance->file_name);\n\n    free(instance);\n}\n\nvoid nfc_text_store_set(NfcApp* nfc, const char* text, ...) {\n    va_list args;\n    va_start(args, text);\n\n    vsnprintf(nfc->text_store, sizeof(nfc->text_store), text, args);\n\n    va_end(args);\n}\n\nvoid nfc_text_store_clear(NfcApp* nfc) {\n    memset(nfc->text_store, 0, sizeof(nfc->text_store));\n}\n\nvoid nfc_blink_read_start(NfcApp* nfc) {\n    notification_message(nfc->notifications, &sequence_blink_start_yellow);\n}\n\nvoid nfc_blink_emulate_start(NfcApp* nfc) {\n    notification_message(nfc->notifications, &sequence_blink_start_magenta);\n}\n\nvoid nfc_blink_detect_start(NfcApp* nfc) {\n    notification_message(nfc->notifications, &sequence_blink_start_cyan);\n}\n\nvoid nfc_blink_stop(NfcApp* nfc) {\n    notification_message(nfc->notifications, &sequence_blink_stop);\n}\n\nvoid nfc_make_app_folders(NfcApp* instance) {\n    furi_assert(instance);\n\n    if(!storage_simply_mkdir(instance->storage, NFC_APP_FOLDER)) {\n        dialog_message_show_storage_error(instance->dialogs, \"Cannot create\\napp folder\");\n    }\n}\n\nbool nfc_save_file(NfcApp* instance, FuriString* path) {\n    furi_assert(instance);\n    furi_assert(path);\n\n    bool result = nfc_device_save(instance->nfc_device, furi_string_get_cstr(instance->file_path));\n\n    if(!result) {\n        dialog_message_show_storage_error(instance->dialogs, \"Cannot save\\nkey file\");\n    }\n\n    return result;\n}\n\nstatic bool nfc_set_shadow_file_path(FuriString* file_path, FuriString* shadow_file_path) {\n    furi_assert(file_path);\n    furi_assert(shadow_file_path);\n\n    bool shadow_file_path_set = false;\n    if(furi_string_end_with(file_path, NFC_APP_SHADOW_EXTENSION)) {\n        furi_string_set(shadow_file_path, file_path);\n        shadow_file_path_set = true;\n    } else if(furi_string_end_with(file_path, NFC_APP_EXTENSION)) {\n        size_t path_len = furi_string_size(file_path);\n        // Cut .nfc\n        furi_string_set_n(shadow_file_path, file_path, 0, path_len - 4);\n        furi_string_cat_printf(shadow_file_path, \"%s\", NFC_APP_SHADOW_EXTENSION);\n        shadow_file_path_set = true;\n    }\n\n    return shadow_file_path_set;\n}\n\nstatic bool nfc_has_shadow_file_internal(NfcApp* instance, FuriString* path) {\n    furi_assert(path);\n\n    bool has_shadow_file = false;\n    FuriString* shadow_file_path = furi_string_alloc();\n    do {\n        if(furi_string_empty(path)) break;\n        if(!nfc_set_shadow_file_path(path, shadow_file_path)) break;\n        has_shadow_file =\n            storage_common_exists(instance->storage, furi_string_get_cstr(shadow_file_path));\n    } while(false);\n\n    furi_string_free(shadow_file_path);\n\n    return has_shadow_file;\n}\n\nbool nfc_has_shadow_file(NfcApp* instance) {\n    furi_assert(instance);\n\n    return nfc_has_shadow_file_internal(instance, instance->file_path);\n}\n\nstatic bool nfc_save_internal(NfcApp* instance, const char* extension) {\n    furi_assert(instance);\n    furi_assert(extension);\n\n    bool result = false;\n\n    nfc_make_app_folders(instance);\n\n    if(furi_string_end_with(instance->file_path, NFC_APP_EXTENSION) ||\n       (furi_string_end_with(instance->file_path, NFC_APP_SHADOW_EXTENSION))) {\n        size_t filename_start = furi_string_search_rchar(instance->file_path, '/');\n        furi_string_left(instance->file_path, filename_start);\n    }\n\n    furi_string_cat_printf(\n        instance->file_path, \"/%s%s\", furi_string_get_cstr(instance->file_name), extension);\n\n    result = nfc_save_file(instance, instance->file_path);\n\n    return result;\n}\n\nbool nfc_save_shadow_file(NfcApp* instance) {\n    furi_assert(instance);\n\n    return nfc_save_internal(instance, NFC_APP_SHADOW_EXTENSION);\n}\n\nbool nfc_save(NfcApp* instance) {\n    furi_assert(instance);\n\n    return nfc_save_internal(instance, NFC_APP_EXTENSION);\n}\n\nbool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) {\n    furi_assert(instance);\n    furi_assert(path);\n    bool result = false;\n\n    nfc_supported_cards_load_cache(instance->nfc_supported_cards);\n\n    FuriString* load_path = furi_string_alloc();\n    if(nfc_has_shadow_file_internal(instance, path)) { //-V1051\n        nfc_set_shadow_file_path(path, load_path);\n    } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) {\n        size_t path_len = furi_string_size(path);\n        furi_string_set_n(load_path, path, 0, path_len - 4);\n        furi_string_cat_printf(load_path, \"%s\", NFC_APP_EXTENSION);\n    } else {\n        furi_string_set(load_path, path);\n    }\n\n    result = nfc_device_load(instance->nfc_device, furi_string_get_cstr(load_path));\n\n    if(result) {\n        path_extract_filename(load_path, instance->file_name, true);\n    }\n\n    if((!result) && (show_dialog)) {\n        dialog_message_show_storage_error(instance->dialogs, \"Cannot load\\nkey file\");\n    }\n\n    furi_string_free(load_path);\n\n    return result;\n}\n\nbool nfc_delete(NfcApp* instance) {\n    furi_assert(instance);\n\n    if(nfc_has_shadow_file(instance)) {\n        nfc_delete_shadow_file(instance);\n    }\n\n    if(furi_string_end_with_str(instance->file_path, NFC_APP_SHADOW_EXTENSION)) {\n        size_t path_len = furi_string_size(instance->file_path);\n        furi_string_replace_at(instance->file_path, path_len - 4, 4, NFC_APP_EXTENSION);\n    }\n\n    return storage_simply_remove(instance->storage, furi_string_get_cstr(instance->file_path));\n}\n\nbool nfc_delete_shadow_file(NfcApp* instance) {\n    furi_assert(instance);\n\n    FuriString* shadow_file_path = furi_string_alloc();\n\n    bool result = nfc_set_shadow_file_path(instance->file_path, shadow_file_path) &&\n                  storage_simply_remove(instance->storage, furi_string_get_cstr(shadow_file_path));\n\n    furi_string_free(shadow_file_path);\n    return result;\n}\n\nbool nfc_load_from_file_select(NfcApp* instance) {\n    furi_assert(instance);\n\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(&browser_options, NFC_APP_EXTENSION, &I_Nfc_10px);\n    browser_options.base_path = NFC_APP_FOLDER;\n    browser_options.hide_dot_files = true;\n\n    bool success = false;\n    do {\n        // Input events and views are managed by file_browser\n        if(!dialog_file_browser_show(\n               instance->dialogs, instance->file_path, instance->file_path, &browser_options))\n            break;\n        success = nfc_load_file(instance, instance->file_path, true);\n    } while(!success);\n\n    return success;\n}\n\nvoid nfc_show_loading_popup(void* context, bool show) {\n    NfcApp* nfc = context;\n\n    if(show) {\n        // Raise timer priority so that animations can play\n        furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);\n        view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewLoading);\n    } else {\n        // Restore default timer priority\n        furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal);\n    }\n}\n\nvoid nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string) {\n    furi_assert(instance);\n    furi_assert(string);\n\n    if(!furi_string_empty(instance->file_name)) {\n        furi_string_cat_printf(string, \"Name: %s\\n\", furi_string_get_cstr(instance->file_name));\n    }\n}\n\nstatic bool nfc_is_hal_ready(void) {\n    if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {\n        // No connection to the chip, show an error screen\n        DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n        DialogMessage* message = dialog_message_alloc();\n        dialog_message_set_header(message, \"Error: NFC Chip Failed\", 64, 0, AlignCenter, AlignTop);\n        dialog_message_set_text(\n            message, \"Send error photo via\\nsupport.flipper.net\", 0, 63, AlignLeft, AlignBottom);\n        dialog_message_set_icon(message, &I_err_09, 128 - 25, 64 - 25);\n        dialog_message_show(dialogs, message);\n        dialog_message_free(message);\n        furi_record_close(RECORD_DIALOGS);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nstatic void nfc_show_initial_scene_for_device(NfcApp* nfc) {\n    NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device);\n    uint32_t scene = nfc_protocol_support_has_feature(\n                         prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ?\n                         NfcSceneEmulate :\n                         NfcSceneSavedMenu;\n    scene_manager_next_scene(nfc->scene_manager, scene);\n}\n\nvoid nfc_app_run_external(NfcApp* nfc, const char* app_path) {\n    furi_assert(nfc);\n    furi_assert(app_path);\n\n    Loader* loader = furi_record_open(RECORD_LOADER);\n\n    loader_enqueue_launch(loader, app_path, NULL, LoaderDeferredLaunchFlagGui);\n\n    FuriString* self_path = furi_string_alloc();\n    furi_check(loader_get_application_launch_path(loader, self_path));\n\n    loader_enqueue_launch(\n        loader, furi_string_get_cstr(self_path), NULL, LoaderDeferredLaunchFlagGui);\n    furi_string_free(self_path);\n\n    furi_record_close(RECORD_LOADER);\n\n    view_dispatcher_stop(nfc->view_dispatcher);\n}\n\nint32_t nfc_app(void* p) {\n    if(!nfc_is_hal_ready()) return 0;\n\n    NfcApp* nfc = nfc_app_alloc();\n    const char* args = p;\n\n    if(args && strlen(args)) {\n        if(sscanf(args, \"RPC %p\", &nfc->rpc_ctx) == 1) {\n            rpc_system_app_set_callback(nfc->rpc_ctx, nfc_app_rpc_command_callback, nfc);\n            rpc_system_app_send_started(nfc->rpc_ctx);\n            view_dispatcher_attach_to_gui(\n                nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeDesktop);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc);\n        } else {\n            view_dispatcher_attach_to_gui(\n                nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);\n\n            furi_string_set(nfc->file_path, args);\n            if(nfc_load_file(nfc, nfc->file_path, true)) {\n                nfc_show_initial_scene_for_device(nfc);\n            } else {\n                view_dispatcher_stop(nfc->view_dispatcher);\n            }\n        }\n    } else {\n        view_dispatcher_attach_to_gui(\n            nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);\n        scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);\n    }\n\n    view_dispatcher_run(nfc->view_dispatcher);\n\n    nfc_app_free(nfc);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/nfc/nfc_app.h",
    "content": "/**\n * @file nfc_app.h\n * @brief NFC application -- start here.\n *\n * Application for interfacing with NFC cards and other devices via Flipper's built-in NFC hardware.\n *\n * Main features:\n * * Multiple protocols support\n * * Card emulation\n * * Shadow file support\n * * Dynamically loaded parser plugins\n *\n * @see nfc_protocol.h for information on adding a new library protocol.\n * @see nfc_protocol_support.h for information on integrating a library protocol into the app.\n * @see nfc_supported_card_plugin.h for information on adding supported card plugins (parsers).\n */\n#pragma once\n\ntypedef struct NfcApp NfcApp;\n"
  },
  {
    "path": "applications/main/nfc/nfc_app_i.h",
    "content": "#pragma once\n\n#include \"nfc_app.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <assets_icons.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <notification/notification_messages.h>\n\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/popup.h>\n#include <gui/modules/loading.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/byte_input.h>\n#include <gui/modules/text_box.h>\n#include <gui/modules/widget.h>\n#include \"views/dict_attack.h\"\n#include \"views/detect_reader.h\"\n#include \"views/dict_attack.h\"\n\n#include <nfc/scenes/nfc_scene.h>\n#include \"helpers/nfc_detected_protocols.h\"\n#include \"helpers/nfc_custom_event.h\"\n#include \"helpers/mf_ultralight_auth.h\"\n#include \"helpers/mf_user_dict.h\"\n#include \"helpers/mfkey32_logger.h\"\n#include \"helpers/mf_classic_key_cache.h\"\n#include \"helpers/nfc_supported_cards.h\"\n#include \"helpers/felica_auth.h\"\n#include \"helpers/slix_unlock.h\"\n\n#include <loader/loader.h>\n#include <dialogs/dialogs.h>\n#include <storage/storage.h>\n#include <toolbox/path.h>\n\n#include \"rpc/rpc_app.h\"\n\n#include <m-array.h>\n\n#include <lib/nfc/nfc.h>\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>\n#include <lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h>\n#include <lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h>\n\n#include <nfc/nfc_poller.h>\n#include <nfc/nfc_scanner.h>\n#include <nfc/nfc_listener.h>\n\n#include <nfc/nfc_device.h>\n#include <nfc/helpers/nfc_data_generator.h>\n#include <toolbox/keys_dict.h>\n\n#include <gui/modules/validators.h>\n#include <toolbox/path.h>\n#include <toolbox/name_generator.h>\n#include <dolphin/dolphin.h>\n\n#define NFC_NAME_SIZE             22\n#define NFC_TEXT_STORE_SIZE       128\n#define NFC_BYTE_INPUT_STORE_SIZE 16\n#define NFC_LOG_SIZE_MAX          (1024)\n#define NFC_APP_FOLDER            EXT_PATH(\"nfc\")\n#define NFC_APP_EXTENSION         \".nfc\"\n#define NFC_APP_SHADOW_EXTENSION  \".shd\"\n#define NFC_APP_FILENAME_PREFIX   \"NFC\"\n\n#define NFC_APP_MFKEY32_LOGS_FILE_NAME \".mfkey32.log\"\n#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER \"/\" NFC_APP_MFKEY32_LOGS_FILE_NAME)\n\n#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER \"/assets/mf_classic_dict_user.nfc\")\n#define NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH \\\n    (NFC_APP_FOLDER \"/assets/mf_classic_dict_user_nested.nfc\")\n#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER \"/assets/mf_classic_dict.nfc\")\n#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \\\n    (NFC_APP_FOLDER \"/assets/mf_classic_dict_nested.nfc\")\n#define NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH \\\n    (NFC_APP_FOLDER \"/assets/mf_ultralight_c_dict_user.nfc\")\n#define NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH \\\n    (NFC_APP_FOLDER \"/assets/mf_ultralight_c_dict.nfc\")\n\n#define NFC_MFKEY32_APP_PATH (EXT_PATH(\"apps/NFC/mfkey.fap\"))\n\ntypedef enum {\n    NfcRpcStateIdle,\n    NfcRpcStateEmulating,\n} NfcRpcState;\n\ntypedef struct {\n    KeysDict* dict;\n    uint8_t sectors_total;\n    uint8_t sectors_read;\n    uint8_t current_sector;\n    uint8_t keys_found;\n    size_t dict_keys_total;\n    size_t dict_keys_current;\n    bool is_key_attack;\n    uint8_t key_attack_current_sector;\n    bool is_card_present;\n    MfClassicNestedPhase nested_phase;\n    MfClassicPrngType prng_type;\n    MfClassicBackdoor backdoor;\n    uint16_t nested_target_key;\n    uint16_t msb_count;\n    bool enhanced_dict;\n} NfcMfClassicDictAttackContext;\n\ntypedef struct {\n    KeysDict* dict;\n    bool auth_success;\n    bool is_card_present;\n    size_t dict_keys_total;\n    size_t dict_keys_current;\n} NfcMfUltralightCDictContext;\n\nstruct NfcApp {\n    DialogsApp* dialogs;\n    Storage* storage;\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    NotificationApp* notifications;\n    SceneManager* scene_manager;\n\n    char text_store[NFC_TEXT_STORE_SIZE + 1];\n    FuriString* text_box_store;\n    uint8_t byte_input_store[NFC_BYTE_INPUT_STORE_SIZE];\n\n    NfcDetectedProtocols* detected_protocols;\n\n    RpcAppSystem* rpc_ctx;\n    NfcRpcState rpc_state;\n\n    // Common Views\n    Submenu* submenu;\n    DialogEx* dialog_ex;\n    Popup* popup;\n    Loading* loading;\n    TextInput* text_input;\n    ByteInput* byte_input;\n    TextBox* text_box;\n    Widget* widget;\n    DetectReader* detect_reader;\n    DictAttack* dict_attack;\n\n    Nfc* nfc;\n    NfcPoller* poller;\n    NfcScanner* scanner;\n    NfcListener* listener;\n\n    FelicaAuthenticationContext* felica_auth;\n    MfUltralightAuth* mf_ul_auth;\n    SlixUnlock* slix_unlock;\n    NfcMfClassicDictAttackContext nfc_dict_context;\n    NfcMfUltralightCDictContext mf_ultralight_c_dict_context;\n    Mfkey32Logger* mfkey32_logger;\n    MfUserDict* mf_user_dict;\n    MfClassicKeyCache* mfc_key_cache;\n    NfcSupportedCards* nfc_supported_cards;\n\n    NfcDevice* nfc_device;\n    Iso14443_3aData* iso14443_3a_edit_data;\n    FuriString* file_path;\n    FuriString* file_name;\n    FuriTimer* timer;\n};\n\ntypedef enum {\n    NfcViewMenu,\n    NfcViewDialogEx,\n    NfcViewPopup,\n    NfcViewLoading,\n    NfcViewTextInput,\n    NfcViewByteInput,\n    NfcViewTextBox,\n    NfcViewWidget,\n    NfcViewDictAttack,\n    NfcViewDetectReader,\n} NfcView;\n\ntypedef enum {\n    NfcSceneSaveConfirmStateDetectReader,\n    NfcSceneSaveConfirmStateCrackNonces,\n} NfcSceneSaveConfirmState;\n\nint32_t nfc_task(void* p);\n\nvoid nfc_text_store_set(NfcApp* nfc, const char* text, ...);\n\nvoid nfc_text_store_clear(NfcApp* nfc);\n\nvoid nfc_blink_read_start(NfcApp* nfc);\n\nvoid nfc_blink_emulate_start(NfcApp* nfc);\n\nvoid nfc_blink_detect_start(NfcApp* nfc);\n\nvoid nfc_blink_stop(NfcApp* nfc);\n\nvoid nfc_show_loading_popup(void* context, bool show);\n\nbool nfc_has_shadow_file(NfcApp* instance);\n\nbool nfc_save_shadow_file(NfcApp* instance);\n\nbool nfc_delete_shadow_file(NfcApp* instance);\n\nbool nfc_save(NfcApp* instance);\n\nbool nfc_delete(NfcApp* instance);\n\nbool nfc_load_from_file_select(NfcApp* instance);\n\nbool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog);\n\nbool nfc_save_file(NfcApp* instance, FuriString* path);\n\nvoid nfc_make_app_folder(NfcApp* instance);\n\nvoid nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);\n\nvoid nfc_app_run_external(NfcApp* nfc, const char* app_path);\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/aic.c",
    "content": "// https://sega.bsnk.me/allnet/amusement_ic/\n\n#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application.h>\n#include <nfc/protocols/felica/felica.h>\n#include <bit_lib.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define TAG \"Amusement IC\"\n\n#define N_TABLES      8\n#define ITERATION_ADD 5\n\nstatic const uint8_t s_box[9][256] = {\n    {0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9,\n     0xf1, 0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3,\n     0xe1, 0xf5, 0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb,\n     0x5c, 0xa8, 0xac, 0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d,\n     0x1e, 0xd2, 0x81, 0x78, 0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0,\n     0xdc, 0xc1, 0x6a, 0xf2, 0xbc, 0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40,\n     0x62, 0x52, 0x41, 0xe2, 0xad, 0x49, 0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0,\n     0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f, 0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5,\n     0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44, 0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1,\n     0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74, 0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50,\n     0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d, 0xf4, 0x72, 0x18, 0xb7, 0xef,\n     0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00, 0xe8, 0x14, 0xaf, 0x09,\n     0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d, 0x05, 0xd8, 0x4b,\n     0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88, 0xc6, 0x79,\n     0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6, 0x87,\n     0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3,\n     0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa,\n     0x33},\n    {0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb,\n     0xcd, 0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3,\n     0x4e, 0xc8, 0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5,\n     0x8f, 0x55, 0xef, 0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28,\n     0xe4, 0x45, 0xcc, 0x35, 0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64,\n     0x01, 0x7b, 0x44, 0xf5, 0xf3, 0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40,\n     0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11, 0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4,\n     0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b, 0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76,\n     0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99, 0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc,\n     0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05, 0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce,\n     0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c, 0x0d, 0x66, 0xd6, 0x73, 0x58,\n     0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33, 0xdb, 0x60, 0x27, 0xbd,\n     0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd, 0xdd, 0x5b, 0xec,\n     0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31, 0xa3, 0x29,\n     0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c, 0xd0,\n     0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18,\n     0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e,\n     0x0c},\n    {0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06,\n     0xd5, 0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6,\n     0x0c, 0x2d, 0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16,\n     0x52, 0xf0, 0x07, 0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0,\n     0x84, 0xcf, 0x2f, 0xa3, 0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46,\n     0x22, 0x28, 0x37, 0x34, 0x13, 0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d,\n     0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71, 0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a,\n     0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5, 0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83,\n     0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a, 0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d,\n     0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18, 0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c,\n     0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f, 0x93, 0x85, 0xf8, 0x2c, 0x74,\n     0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f, 0x0d, 0x58, 0x33, 0x44,\n     0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b, 0x17, 0x54, 0x86,\n     0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35, 0xe2, 0x96,\n     0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10, 0xe3,\n     0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea,\n     0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36,\n     0x05},\n    {0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86,\n     0x6b, 0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7,\n     0xc0, 0x51, 0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd,\n     0x97, 0x8c, 0xd6, 0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87,\n     0x66, 0xad, 0x9d, 0x85, 0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40,\n     0xac, 0x6c, 0x5c, 0x08, 0x5d, 0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32,\n     0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91, 0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4,\n     0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15, 0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6,\n     0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03, 0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e,\n     0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37, 0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06,\n     0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b, 0x62, 0x27, 0x7b, 0xb0, 0xe5,\n     0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11, 0xbd, 0xe0, 0x88, 0x0d,\n     0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3, 0x42, 0x60, 0x2f,\n     0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d, 0xde, 0xec,\n     0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb, 0x35,\n     0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30,\n     0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa,\n     0x2e},\n    {0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c,\n     0x4b, 0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24,\n     0xcf, 0x18, 0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a,\n     0x7a, 0x71, 0x0a, 0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf,\n     0x70, 0x5e, 0x73, 0x8a, 0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46,\n     0xd0, 0x2f, 0xde, 0x2a, 0x4f, 0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31,\n     0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93, 0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68,\n     0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b, 0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a,\n     0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0, 0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9,\n     0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34, 0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6,\n     0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b, 0xfb, 0xb7, 0xc5, 0xeb, 0x57,\n     0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96, 0xbd, 0xbf, 0xe3, 0x36,\n     0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3, 0xf5, 0x8e, 0x23,\n     0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74, 0xcc, 0xb2,\n     0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca, 0xd1,\n     0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f,\n     0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94,\n     0x17},\n    {0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33,\n     0x9f, 0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a,\n     0x1e, 0x7c, 0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f,\n     0x26, 0x65, 0x73, 0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91,\n     0x04, 0x51, 0x22, 0xfc, 0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37,\n     0xc2, 0xa0, 0x41, 0x1d, 0xd4, 0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab,\n     0x01, 0xba, 0xf0, 0x66, 0x68, 0xac, 0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15,\n     0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a, 0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8,\n     0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc, 0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9,\n     0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef, 0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda,\n     0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae, 0x13, 0xbf, 0xb9, 0x06, 0x8b,\n     0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46, 0x6c, 0x2c, 0xf2, 0xf8,\n     0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5, 0x57, 0x7e, 0x8c,\n     0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32, 0x64, 0x63,\n     0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1, 0x93,\n     0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45,\n     0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e,\n     0xcc},\n    {0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b,\n     0xbd, 0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59,\n     0x54, 0xf2, 0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6,\n     0x43, 0x01, 0x0a, 0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3,\n     0x74, 0xb3, 0x3f, 0xa3, 0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91,\n     0x64, 0x4d, 0x3c, 0xf8, 0x9d, 0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12,\n     0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13, 0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26,\n     0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7, 0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b,\n     0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b, 0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c,\n     0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7, 0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e,\n     0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16, 0xce, 0x37, 0x44, 0xab, 0x99,\n     0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58, 0x2c, 0x6c, 0xfe, 0x9e,\n     0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f, 0xe7, 0xd9, 0x0e,\n     0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee, 0xfc, 0xc7,\n     0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f, 0x61,\n     0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08,\n     0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96,\n     0xc1},\n    {0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc,\n     0x45, 0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc,\n     0xa8, 0xd6, 0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19,\n     0xca, 0x89, 0x67, 0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae,\n     0x9c, 0x3f, 0x6b, 0x78, 0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea,\n     0xf1, 0x18, 0x87, 0x7b, 0x62, 0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16,\n     0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a, 0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76,\n     0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b, 0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb,\n     0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b, 0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81,\n     0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80, 0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3,\n     0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1, 0x40, 0x5c, 0x13, 0xb0, 0xe5,\n     0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a, 0x2f, 0x37, 0xa1, 0xfc,\n     0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a, 0x75, 0x61, 0xb3,\n     0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c, 0x31, 0x94,\n     0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d, 0xdc,\n     0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c,\n     0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41,\n     0xf6},\n    {0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92,\n     0xed, 0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91,\n     0x1f, 0xb1, 0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59,\n     0x9d, 0xd5, 0x22, 0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28,\n     0xfb, 0x06, 0xdd, 0x5d, 0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93,\n     0x7b, 0x6c, 0x69, 0x54, 0xe7, 0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87,\n     0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa, 0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e,\n     0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6, 0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf,\n     0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40, 0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1,\n     0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53, 0x33, 0x46, 0x20, 0x13, 0x34, 0x1b,\n     0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f, 0x70, 0x2e, 0x55, 0x41, 0x05,\n     0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78, 0x35, 0x8b, 0xef, 0xd2,\n     0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9, 0x51, 0x4e, 0xeb,\n     0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6, 0x2b, 0xcc,\n     0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7, 0x8e,\n     0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32,\n     0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9,\n     0xb8},\n};\n\nstatic const uint8_t access_code_table_1[5][10][100] = {\n    {\n        {11, 38, 3,  63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94,\n         45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9,  15, 92, 14,\n         82, 97, 78, 37, 49, 0,  80, 57, 58, 7,  1,  89, 98, 53, 64, 5,  6,  66, 43, 83,\n         10, 50, 47, 84, 62, 71, 4,  67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30,\n         16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2,  24, 72, 93, 26, 8},\n        {28, 58, 74, 34, 6,  14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13,\n         49, 99, 8,  94, 15, 19, 0,  96, 41, 95, 45, 35, 1,  91, 2,  52, 42, 76, 90, 84,\n         20, 54, 4,  63, 25, 5,  47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80,\n         98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9,  60, 7,  72, 21, 43, 3,\n         33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83},\n        {4,  16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93,\n         81, 69, 28, 90, 78, 39, 63, 88, 0,  75, 2,  53, 23, 99, 94, 68, 54, 74, 41, 29,\n         8,  26, 50, 98, 82, 95, 42, 7,  24, 77, 30, 9,  21, 1,  87, 62, 46, 15, 43, 38,\n         36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6,  73, 97, 33, 83, 27, 35, 25, 40, 32,\n         18, 31, 86, 48, 45, 3,  79, 76, 72, 64, 44, 20, 80, 19, 10, 5,  61, 34, 67, 12},\n        {80, 91, 71, 26, 75, 8,  22, 92, 7,  70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85,\n         55, 19, 57, 87, 74, 6,  73, 40, 49, 32, 52, 78, 0,  12, 54, 15, 64, 28, 4,  24,\n         68, 17, 72, 53, 1,  94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33,\n         18, 5,  41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3,\n         93, 46, 50, 39, 14, 9,  2,  13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84},\n        {48, 16, 54, 47, 9,  39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74,\n         51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7,  77, 12, 31, 46, 1,  5,  14, 3,  30,\n         94, 95, 2,  34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0,  24,\n         71, 18, 60, 4,  80, 96, 27, 15, 6,  13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66,\n         11, 8,  86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32},\n        {23, 19, 88, 44, 5,  39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92,\n         6,  95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65,\n         79, 11, 68, 84, 24, 97, 64, 47, 9,  22, 43, 86, 37, 8,  33, 17, 93, 61, 25, 72,\n         57, 98, 42, 80, 13, 89, 2,  12, 31, 56, 26, 85, 3,  29, 66, 63, 82, 10, 20, 94,\n         51, 0,  1,  4,  21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7,  96, 91, 40, 70, 73},\n        {89, 62, 33, 19, 21, 97, 15, 2,  83, 22, 65, 30, 55, 7,  63, 73, 39, 61, 44, 37,\n         11, 26, 31, 79, 91, 0,  72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42,\n         35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3,  67, 81,\n         90, 58, 99, 5,  82, 85, 25, 52, 12, 8,  48, 49, 76, 96, 80, 20, 29, 34, 53, 51,\n         86, 93, 38, 84, 9,  36, 6,  60, 1,  78, 13, 95, 41, 17, 47, 18, 43, 40, 4,  56},\n        {40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3,  12, 27, 79, 25, 6,  55, 19, 80,\n         81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2,  13, 45,\n         93, 49, 75, 72, 29, 87, 5,  7,  17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65,\n         57, 66, 78, 86, 11, 9,  71, 98, 53, 56, 15, 46, 92, 0,  26, 21, 32, 38, 77, 91,\n         4,  24, 61, 85, 94, 1,  96, 64, 8,  41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89},\n        {96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2,  63, 49,\n         95, 60, 48, 97, 5,  54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8,  55,\n         90, 62, 50, 87, 65, 82, 84, 21, 0,  14, 45, 44, 20, 38, 17, 3,  81, 16, 58, 4,\n         31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6,  22, 61, 7,  83, 71, 30, 75,\n         85, 78, 24, 27, 89, 9,  80, 34, 66, 77, 36, 93, 56, 59, 1,  12, 51, 32, 11, 74},\n        {75, 99, 97, 8,  15, 48, 6,  25, 60, 90, 91, 95, 23, 3,  89, 77, 22, 78, 94, 55,\n         98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9,  17, 13, 57, 44, 14, 47, 86, 56,\n         88, 41, 29, 32, 70, 1,  62, 37, 85, 38, 59, 36, 5,  68, 71, 69, 67, 27, 39, 4,\n         54, 0,  84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7,  28, 2,\n         11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63},\n    },\n    {\n        {98, 58, 34, 65, 83, 74, 88, 5,  89, 46, 80, 43, 18, 87, 9,  28, 71, 90, 4,  36,\n         40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3,  7,\n         62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38,\n         53, 2,  63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8,\n         91, 12, 42, 6,  84, 81, 19, 25, 32, 51, 1,  17, 52, 56, 60, 0,  59, 30, 21, 20},\n        {26, 63, 99, 57, 77, 53, 9,  75, 76, 80, 46, 97, 37, 14, 1,  12, 35, 20, 0,  15,\n         28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7,  98, 39, 96,\n         8,  62, 42, 3,  95, 5,  11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54,\n         45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2,  40, 16, 24, 4,  71, 49, 33, 23, 92,\n         84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6,  50, 27, 93, 91, 18},\n        {22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5,  98, 1,  76, 59, 20,\n         86, 32, 61, 49, 63, 42, 74, 53, 84, 3,  95, 23, 15, 36, 4,  81, 6,  21, 19, 27,\n         17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8,  13,\n         58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9,  55, 57, 2,  39, 92, 0,  44, 18, 62,\n         73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7,  12, 87, 51, 26, 47, 64, 35, 67, 38},\n        {0,  80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42,\n         82, 23, 29, 18, 21, 2,  19, 7,  68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88,\n         14, 51, 79, 71, 66, 76, 34, 24, 30, 9,  62, 44, 77, 65, 6,  27, 4,  97, 38, 36,\n         59, 3,  1,  50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8,  83, 75, 40, 98,\n         17, 99, 85, 32, 92, 67, 84, 5,  25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33},\n        {64, 57, 56, 7,  86, 9,  65, 16, 1,  63, 71, 26, 53, 98, 69, 5,  84, 28, 36, 58,\n         54, 99, 42, 74, 68, 3,  88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87,\n         31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8,  23, 2,\n         18, 73, 94, 6,  38, 44, 13, 40, 48, 4,  81, 37, 96, 46, 19, 49, 34, 50, 0,  20,\n         14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55},\n        {24, 99, 28, 52, 64, 3,  63, 16, 15, 87, 47, 6,  59, 98, 13, 78, 57, 81, 56, 94,\n         0,  37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2,  33, 85, 21, 54,\n         12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66,\n         45, 19, 8,  58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7,  10,\n         11, 35, 23, 9,  26, 4,  62, 44, 1,  43, 46, 14, 41, 77, 97, 27, 68, 32, 5,  50},\n        {90, 80, 22, 27, 16, 23, 11, 31, 8,  75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42,\n         56, 88, 12, 55, 38, 6,  79, 84, 26, 92, 4,  67, 48, 5,  46, 77, 19, 53, 97, 34,\n         64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1,  25, 9,  72, 69, 61, 60,\n         86, 3,  24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0,  33, 17, 7,  94, 15, 43, 2,\n         44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76},\n        {95, 50, 72, 0,  68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30,\n         62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7,  63, 91, 28, 93, 38, 90, 71,\n         34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9,  45, 60, 39, 44, 16, 17, 37,\n         47, 79, 33, 36, 59, 8,  80, 23, 1,  18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65,\n         12, 4,  19, 85, 96, 6,  2,  58, 27, 43, 5,  87, 20, 15, 3,  24, 84, 67, 74, 69},\n        {38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86,\n         81, 40, 93, 97, 67, 2,  94, 9,  61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63,\n         39, 43, 16, 71, 46, 91, 13, 7,  20, 72, 41, 59, 70, 48, 10, 52, 5,  34, 76, 15,\n         87, 6,  98, 18, 57, 77, 24, 79, 27, 19, 3,  0,  8,  95, 74, 64, 42, 4,  68, 31,\n         84, 54, 99, 25, 1,  80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96},\n        {26, 12, 62, 5,  78, 53, 31, 46, 51, 8,  69, 80, 32, 68, 76, 42, 29, 87, 28, 75,\n         40, 91, 58, 7,  88, 92, 82, 96, 70, 50, 47, 65, 85, 3,  48, 73, 90, 89, 97, 84,\n         81, 39, 43, 79, 64, 54, 2,  41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95,\n         72, 74, 15, 11, 55, 98, 99, 0,  9,  16, 18, 20, 63, 30, 57, 19, 4,  17, 67, 35,\n         60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1,  13, 34, 21, 61, 6,  94, 52, 83},\n    },\n    {\n        {10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46,\n         19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9,  41, 79, 13, 95, 48, 85, 54, 61, 94,\n         0,  74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6,  16,\n         75, 81, 43, 47, 7,  3,  2,  37, 67, 8,  66, 22, 30, 32, 71, 44, 70, 1,  36, 26,\n         33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5,  4,  51, 45},\n        {73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5,  75, 25, 59, 95, 58, 79, 4,  89,\n         96, 17, 38, 6,  39, 9,  68, 81, 3,  90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65,\n         63, 98, 11, 51, 0,  33, 91, 22, 69, 34, 31, 20, 46, 2,  76, 54, 15, 62, 13, 70,\n         92, 55, 82, 47, 78, 7,  24, 27, 86, 1,  56, 61, 12, 44, 85, 57, 49, 19, 18, 67,\n         66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8,  97, 29, 36},\n        {60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8,  25, 6,  58, 3,  98, 44, 82,\n         96, 14, 1,  59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2,  74, 93, 85, 12, 10,\n         43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0,  34, 19, 70, 84, 54, 9,\n         23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5,  4,  88, 41, 22, 46, 76, 48, 61,\n         81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7,  80, 62, 95, 69, 26},\n        {49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6,  17, 47, 29, 84, 11, 18,\n         67, 45, 78, 4,  52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1,  62, 9,  53,\n         10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7,  24, 61, 77, 5,  14, 93, 16, 21,\n         85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2,  97, 42, 33, 13, 68, 90, 25, 69, 99,\n         98, 74, 3,  8,  12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0,  44, 22, 72, 70},\n        {30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6,  63, 39, 61, 4,  93, 43, 23, 99,\n         38, 66, 88, 18, 1,  86, 33, 9,  82, 76, 70, 51, 48, 69, 85, 11, 5,  91, 59, 77,\n         49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95,\n         0,  72, 80, 73, 92, 36, 96, 37, 68, 2,  35, 83, 90, 25, 13, 24, 34, 42, 45, 74,\n         67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3,  8,  17, 89, 81, 94, 7,  29},\n        {1,  18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7,  28, 55, 11, 5,  58,\n         8,  9,  74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3,  39, 81, 19,\n         2,  22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60,\n         66, 26, 77, 63, 37, 35, 23, 6,  4,  87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43,\n         0,  15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29},\n        {30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5,  53, 61, 1,  96, 68, 45, 95, 66,\n         36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0,  34, 7,  2,  41, 21, 31, 32,\n         76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6,  44, 52, 48, 73, 24, 29, 74,\n         85, 99, 80, 28, 47, 9,  39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8,\n         54, 25, 91, 78, 3,  50, 65, 70, 11, 4,  92, 60, 40, 58, 15, 89, 82, 18, 79, 20},\n        {60, 88, 79, 35, 40, 43, 52, 4,  17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62,\n         14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33,\n         47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9,  48, 21, 1,  63, 22, 58, 95, 10, 12,\n         26, 74, 85, 13, 8,  27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0,  20,\n         16, 6,  77, 45, 5,  93, 76, 39, 57, 2,  96, 46, 51, 3,  55, 7,  81, 70, 36, 72},\n        {35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71,\n         37, 26, 54, 48, 27, 43, 0,  12, 95, 97, 1,  63, 67, 98, 59, 40, 13, 19, 31, 87,\n         99, 16, 57, 30, 17, 92, 55, 68, 5,  46, 64, 8,  7,  42, 90, 33, 29, 9,  85, 24,\n         2,  69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6,  78, 4,  47, 28, 3,  75,\n         41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72},\n        {6,  90, 16, 43, 20, 55, 94, 8,  62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49,\n         2,  88, 38, 5,  53, 77, 85, 11, 73, 34, 3,  72, 95, 58, 82, 64, 44, 29, 93, 14,\n         56, 98, 4,  37, 45, 75, 86, 60, 87, 7,  31, 17, 89, 97, 10, 70, 36, 99, 46, 63,\n         76, 41, 48, 84, 9,  52, 1,  24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0,  91, 33,\n         51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66},\n    },\n    {\n        {93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64,\n         24, 84, 77, 8,  97, 50, 4,  53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14,\n         34, 60, 54, 12, 94, 9,  41, 2,  26, 10, 6,  57, 75, 63, 32, 43, 13, 52, 82, 73,\n         20, 30, 17, 5,  3,  68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0,  91, 76,\n         38, 7,  99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1},\n        {78, 91, 97, 77, 75, 85, 32, 2,  40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,\n         98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4,  48, 95, 8,  51, 3,\n         37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0,  9,\n         94, 67, 1,  60, 81, 61, 11, 6,  90, 5,  49, 10, 34, 7,  35, 30, 25, 88, 79, 93,\n         66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},\n        {78, 91, 97, 77, 75, 85, 32, 2,  40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,\n         98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4,  48, 95, 8,  51, 3,\n         37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0,  9,\n         94, 67, 1,  60, 81, 61, 11, 6,  90, 5,  49, 10, 34, 7,  35, 30, 25, 88, 79, 93,\n         66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},\n        {41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9,  4,  97, 39, 36,\n         26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7,  93, 56, 84, 40, 61,\n         89, 21, 74, 1,  54, 44, 34, 6,  28, 8,  43, 76, 48, 71, 85, 38, 83, 18, 24, 79,\n         68, 80, 45, 33, 91, 27, 32, 70, 95, 3,  58, 60, 73, 10, 75, 52, 64, 2,  94, 30,\n         17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0,  37, 72, 92, 15, 5,  29, 69, 96},\n        {74, 50, 44, 80, 11, 46, 39, 54, 24, 3,  7,  62, 41, 8,  75, 23, 57, 82, 51, 86,\n         97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2,\n         58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31,\n         83, 5,  99, 49, 78, 71, 96, 53, 36, 90, 1,  66, 15, 0,  85, 65, 27, 4,  98, 94,\n         6,  84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9,  52, 88, 81, 33, 19, 40, 18},\n        {35, 67, 27, 53, 26, 79, 20, 46, 38, 4,  72, 84, 70, 58, 65, 91, 92, 21, 32, 71,\n         54, 24, 89, 0,  37, 8,  22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51,\n         99, 3,  55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6,\n         61, 34, 41, 59, 49, 10, 39, 2,  75, 80, 15, 94, 7,  23, 95, 78, 90, 5,  1,  28,\n         56, 30, 9,  98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85},\n        {8,  51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76,\n         57, 5,  19, 56, 89, 66, 27, 94, 14, 6,  21, 58, 43, 74, 10, 2,  17, 62, 47, 16,\n         69, 84, 63, 39, 4,  92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40,\n         11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9,  97, 38,\n         75, 25, 44, 7,  26, 12, 22, 0,  33, 1,  31, 68, 70, 67, 98, 18, 77, 36, 3,  81},\n        {34, 17, 91, 45, 63, 64, 1,  71, 98, 78, 94, 3,  99, 12, 15, 93, 69, 19, 61, 80,\n         92, 81, 33, 51, 40, 10, 75, 24, 5,  87, 73, 46, 52, 86, 4,  70, 89, 56, 2,  67,\n         72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8,  76, 7,  27, 28,\n         55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9,  23,\n         59, 50, 47, 16, 83, 25, 6,  30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0},\n        {7,  51, 77, 80, 0,  53, 56, 27, 2,  78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99,\n         60, 50, 21, 11, 39, 42, 81, 8,  1,  79, 61, 5,  15, 31, 59, 33, 20, 94, 23, 88,\n         69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9,  83, 67, 38, 90, 82,\n         29, 10, 37, 91, 97, 74, 6,  86, 30, 63, 24, 49, 3,  13, 4,  57, 76, 70, 98, 34,\n         58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92},\n        {96, 2,  76, 15, 31, 22, 71, 38, 88, 26, 67, 0,  21, 10, 58, 39, 63, 77, 23, 50,\n         48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20,\n         56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7,  43, 16, 37, 99, 72, 62, 13, 4,\n         47, 8,  98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49,\n         92, 9,  90, 36, 41, 81, 59, 54, 34, 6,  42, 5,  80, 3,  1,  86, 27, 52, 24, 75},\n    },\n    {\n        {61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9,  40, 20,\n         58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6,  36, 50, 74, 26,\n         84, 95, 7,  68, 97, 79, 51, 16, 80, 3,  82, 2,  93, 21, 31, 98, 18, 60, 39, 72,\n         54, 65, 73, 32, 5,  70, 99, 10, 14, 71, 34, 4,  64, 33, 55, 48, 75, 87, 57, 81,\n         22, 46, 69, 63, 90, 23, 38, 56, 8,  29, 83, 88, 11, 0,  47, 53, 91, 94, 49, 1},\n        {35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7,  79, 14, 2,\n         44, 60, 54, 47, 51, 83, 89, 3,  20, 11, 40, 85, 37, 48, 49, 8,  73, 15, 90, 93,\n         70, 84, 34, 4,  86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41,\n         53, 88, 77, 21, 0,  23, 64, 76, 95, 33, 62, 1,  75, 87, 10, 66, 57, 6,  28, 25,\n         42, 9,  50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5,  92, 52, 32, 16},\n        {70, 29, 50, 57, 31, 9,  59, 45, 87, 71, 98, 89, 77, 97, 86, 5,  21, 61, 47, 41,\n         76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4,  11, 82, 23, 39, 40,\n         42, 68, 69, 48, 54, 13, 99, 33, 26, 0,  66, 79, 46, 72, 49, 44, 25, 74, 73, 28,\n         38, 10, 2,  85, 15, 19, 52, 94, 7,  36, 17, 32, 96, 3,  20, 30, 92, 51, 14, 90,\n         24, 67, 43, 34, 93, 84, 62, 81, 1,  56, 53, 88, 80, 35, 22, 6,  18, 16, 8,  60},\n        {62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1,  65, 13, 30, 36,\n         57, 16, 97, 91, 51, 33, 80, 67, 5,  25, 8,  96, 94, 84, 6,  89, 88, 20, 10, 85,\n         41, 56, 9,  34, 63, 0,  18, 12, 42, 15, 87, 4,  61, 43, 82, 17, 98, 60, 92, 70,\n         49, 71, 47, 54, 79, 83, 27, 74, 58, 2,  76, 93, 22, 69, 78, 44, 28, 95, 72, 68,\n         7,  55, 29, 77, 14, 23, 31, 32, 3,  37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45},\n        {65, 78, 7,  83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67,\n         58, 21, 4,  5,  26, 27, 75, 31, 99, 52, 92, 64, 28, 8,  19, 98, 84, 34, 9,  48,\n         60, 0,  56, 1,  62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13,\n         96, 81, 47, 36, 46, 93, 2,  69, 33, 3,  20, 6,  35, 18, 74, 68, 53, 37, 29, 61,\n         90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94},\n        {11, 17, 21, 48, 63, 0,  73, 14, 10, 88, 2,  28, 6,  9,  19, 69, 81, 13, 35, 95,\n         60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8,  5,  58, 94, 98, 20,\n         74, 77, 4,  52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1,  39, 22, 89,\n         53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66,\n         64, 3,  67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7,  40, 70, 68, 65, 57},\n        {43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41,\n         34, 36, 33, 94, 56, 87, 25, 6,  1,  8,  63, 96, 93, 44, 40, 65, 13, 77, 61, 85,\n         99, 80, 11, 51, 14, 46, 5,  39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95,\n         17, 37, 0,  15, 97, 4,  42, 86, 84, 3,  26, 81, 98, 9,  73, 22, 28, 78, 60, 76,\n         16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7,  2,  70, 69},\n        {78, 35, 94, 59, 8,  23, 10, 76, 96, 80, 93, 9,  27, 95, 61, 84, 88, 91, 2,  33,\n         1,  85, 3,  36, 79, 18, 92, 5,  34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6,\n         77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7,\n         25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51,\n         37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0,  4,  19, 52, 97, 12, 26},\n        {88, 86, 41, 1,  70, 10, 19, 2,  11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56,\n         85, 75, 25, 5,  97, 7,  36, 67, 76, 79, 90, 65, 91, 0,  98, 89, 20, 55, 93, 58,\n         28, 63, 6,  83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54,\n         57, 80, 32, 94, 3,  66, 30, 35, 8,  46, 16, 51, 24, 17, 4,  84, 45, 60, 34, 15,\n         72, 77, 42, 12, 64, 9,  33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68},\n        {90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92,\n         99, 65, 27, 5,  24, 1,  69, 7,  31, 35, 33, 64, 12, 14, 45, 11, 98, 0,  57, 72,\n         74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2,  70, 20, 60, 84, 52, 25, 43, 54, 17,\n         36, 19, 97, 73, 94, 62, 3,  23, 34, 88, 29, 80, 50, 22, 81, 87, 6,  30, 78, 48,\n         79, 44, 66, 8,  51, 86, 4,  15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9,  95, 59},\n    },\n};\n\nstatic const uint8_t access_code_table_2[10][10] = {\n    {4, 7, 8, 0, 9, 1, 3, 6, 2, 5},\n    {5, 0, 4, 1, 3, 8, 9, 7, 6, 2},\n    {0, 2, 7, 8, 9, 1, 6, 3, 5, 4},\n    {5, 9, 8, 4, 1, 7, 0, 2, 3, 6},\n    {7, 3, 0, 1, 8, 9, 6, 5, 2, 4},\n    {7, 2, 4, 0, 1, 9, 6, 3, 5, 8},\n    {4, 1, 6, 3, 5, 8, 0, 7, 2, 9},\n    {6, 8, 4, 1, 5, 3, 7, 2, 9, 0},\n    {4, 1, 8, 3, 7, 2, 0, 6, 5, 9},\n    {7, 4, 5, 6, 2, 3, 9, 1, 8, 0}};\n\nstatic void rotate_right(uint8_t* data, int n_bytes, int n_bits) {\n    uint8_t prior = data[n_bytes - 1];\n    for(int i = 0; i < n_bytes; i++) {\n        uint8_t tmp = data[i];\n        data[i] = (data[i] >> n_bits) | ((prior & ((1 << n_bits) - 1)) << (8 - n_bits));\n        prior = tmp;\n    }\n}\n\nstatic void decrypt_spad_0(const uint8_t* spad, uint8_t* decrypted) {\n    for(int i = 0; i < 16; i++) {\n        decrypted[i] = s_box[N_TABLES][spad[i]];\n    }\n\n    int count = (decrypted[15] >> 4) + 7;\n    int table = decrypted[15] + ITERATION_ADD * count;\n\n    for(int iter = 0; iter < count; iter++) {\n        table -= ITERATION_ADD;\n        rotate_right(decrypted, 15, 5); // only the first 15 bytes\n        for(int i = 0; i < 15; i++) {\n            decrypted[i] = s_box[table % N_TABLES][decrypted[i]];\n        }\n    }\n}\n\nstatic uint16_t crc16(uint64_t data, int bits, uint16_t init, uint16_t poly) {\n    uint16_t v = init;\n    for(int i = 0; i < bits; ++i) {\n        v = (v >> 1) ^ (((v ^ data) & 1ULL) ? poly : 0);\n        data >>= 1;\n    }\n    return v;\n}\n\nstatic bool\n    check_access_code_crc(const uint8_t ac[3], const uint8_t body[6], const uint8_t crc[5]) {\n    uint64_t msg = 0;\n    for(int i = 0; i < 3; ++i) {\n        if(ac[i] > 0xF) return false;\n        msg = (msg << 4) | ac[i];\n    }\n    for(int i = 0; i < 6; ++i) {\n        uint8_t v = body[i];\n        if(v > 99) return false;\n        msg = (msg << 4) | (v / 10);\n        msg = (msg << 4) | (v % 10);\n    }\n    uint16_t calculated_crc = crc16(msg, 60, 0xFFFF, 0x8408);\n    uint16_t expected_crc = crc[0] * 10000 + crc[1] * 1000 + crc[2] * 100 + crc[3] * 10 + crc[4];\n    return (calculated_crc == expected_crc);\n}\n\nstatic void parse_access_code(const uint8_t* access_code, FuriString* parsed_data) {\n    furi_assert(access_code);\n    furi_assert(parsed_data);\n\n    uint8_t decrypted[6];\n    // decrypted contains the decoded serial bytes (as 6 BCD bytes)\n    for(int i = 0, j = 3; i < 6; ++i, j += 2) {\n        decrypted[i] = (access_code[j]) * 10 + (access_code[j + 1]);\n    }\n\n    uint8_t crc[5] = {0};\n    memcpy(crc, access_code + 15, 5);\n\n    int boxes1[6] = {\n        (crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]};\n\n    for(int n = 0; n < 6; ++n) {\n        decrypted[n] = access_code_table_1[4][boxes1[n]][decrypted[n]];\n    }\n\n    int rv = decrypted[0] / 10;\n\n    int boxes2[6] = {\n        (crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10};\n    for(int n = 0; n < 6; ++n) {\n        if(n == 0) {\n            decrypted[n] = (decrypted[n] & 0xF0) |\n                           access_code_table_2[boxes2[n]][decrypted[n] & 0x0F];\n        } else {\n            decrypted[n] = access_code_table_1[rv][boxes2[n]][decrypted[n]];\n        }\n    }\n\n    furi_string_cat_printf(\n        parsed_data,\n        \"Decrypted serial number:\\n%02d%02d%02d%02d%02d%02d\\n\",\n        decrypted[0],\n        decrypted[1],\n        decrypted[2],\n        decrypted[3],\n        decrypted[4],\n        decrypted[5]);\n\n    furi_string_cat_printf(\n        parsed_data,\n        \"CRC check: %s\\n\",\n        check_access_code_crc(access_code, decrypted, crc) ? \"Passed\" : \"Invalid\");\n}\n\nbool aic_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n    bool parsed = false;\n\n    if(nfc_device_get_protocol(device) != NfcProtocolFelica) return false;\n\n    const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);\n\n    const uint8_t ic_type = data->pmm.data[1];\n    if(ic_type != 0xF0 && ic_type != 0xF1) {\n        // Must be Felica Lite (0xF0) or Lite-S (0xF1) to parse\n        return false;\n    }\n\n    const uint8_t data_format_code_1 = data->data.fs.id.data[8];\n    if(data_format_code_1 != 0) {\n        // We only know Data Format Code {0x00, 0xXX}\n        return false;\n    }\n\n    parsed = true;\n    furi_string_printf(parsed_data, \"\\e#Amusement IC Card\\n\");\n    furi_string_cat_str(\n        parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n    furi_string_cat_str(parsed_data, \"\\nType:\\n\");\n\n    // Determine card brand and type\n    const uint8_t data_format_code_2 = data->data.fs.id.data[9];\n    switch(data_format_code_2) {\n    case 0x2A:\n        furi_string_cat_str(parsed_data, \"Bandai Namco Passport\\n\");\n        break;\n    case 0x3A:\n        furi_string_cat_str(parsed_data, \"Bandai Namco Passport (Old)\\n\");\n        break;\n    case 0x68:\n        furi_string_cat_str(parsed_data, \"Konami e-amusement pass\\n\");\n        break;\n    case 0x78:\n        furi_string_cat_str(parsed_data, \"SEGA Aime\\n\");\n        break;\n    case 0x79:\n        furi_string_cat_str(parsed_data, \"Taito NESiCA\\n\");\n        break;\n    default:\n        parsed = false;\n        return parsed; // Unknown vendor\n    }\n    furi_string_cat_str(\n        parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n    // decrypt_spad_0 S-PAD 0\n    uint8_t decrypted[16] = {0};\n    decrypt_spad_0(data->data.fs.spad[0].data, decrypted);\n\n    // Get Access Code\n    uint8_t access_code[20] = {0};\n    for(int i = 0; i < 10; i++) {\n        access_code[i * 2] = (decrypted[i + 6] & 0xF0) >> 4; // Get upper nibble\n        access_code[i * 2 + 1] = decrypted[i + 6] & 0x0F; // Get lower nibble\n    }\n    furi_string_cat_str(parsed_data, \"\\nAccess Code:\\n\");\n    bool access_code_is_bcd = true;\n    for(int i = 0; i < 20; i++) {\n        furi_string_cat_printf(parsed_data, \"%d\", access_code[i]);\n        if(i % 4 == 3) {\n            furi_string_cat_str(parsed_data, \" \");\n        }\n        if(access_code[i] > 9) access_code_is_bcd = false;\n    }\n    furi_string_cat_str(parsed_data, \"\\n\");\n\n    furi_string_cat_printf(parsed_data, \"BCD valid: %s\\n\", access_code_is_bcd ? \"Yes\" : \"No\");\n    furi_string_cat_str(\n        parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n    // Parse Access Code\n    if(access_code_is_bcd && access_code[0] == 5) {\n        furi_string_cat_str(parsed_data, \"\\n\");\n        parse_access_code(access_code, parsed_data);\n    } else {\n        furi_string_cat_printf(\n            parsed_data, \"\\nAccess code preamble wrong: expected 5, got %d\\n\", access_code[0]);\n    }\n\n    furi_string_cat_str(\n        parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n    furi_string_cat_str(parsed_data, \"\\nDecrypted S-PAD 0:\\n\");\n    for(int i = 0; i < 16; i++) {\n        furi_string_cat_printf(parsed_data, \"%02X \", decrypted[i]);\n        if(i == 7) {\n            furi_string_cat_str(parsed_data, \"\\n\");\n        }\n    }\n    furi_string_cat_str(parsed_data, \"\\n\");\n    furi_string_cat_str(\n        parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin aic_plugin = {\n    .protocol = NfcProtocolFelica,\n    .verify = NULL,\n    .read = NULL,\n    .parse = aic_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor aic_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &aic_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* aic_plugin_ep(void) {\n    return &aic_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/aime.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"Aime\"\n\nstatic const uint64_t aime_key = 0x574343467632;\n\nbool aime_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t verify_sector = 0;\n        uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n        MfClassicKey key = {};\n        bit_lib_num_to_bytes_be(aime_key, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool aime_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(aime_key, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(aime_key, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = true;\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool aime_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // verify key\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        if(key != aime_key) break;\n\n        // Aime Magic is stored at block 1, starts from byte 0, len 4 bytes\n        const uint8_t* aime_magic = &data->block[1].data[0];\n\n        // verify aime magic\n        if(aime_magic[0] != 'S' || aime_magic[1] != 'B' || aime_magic[2] != 'S' ||\n           aime_magic[3] != 'D')\n            break;\n\n        // Aime checksum is stored at block 1, starts from byte 13, len 3 bytes\n        // seems like only old games checks this? e.g., old versions of Chunithm\n        const uint8_t* aime_checksum = &data->block[1].data[13];\n\n        // Aime access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes\n        const uint8_t* aime_accesscode = &data->block[2].data[6];\n\n        char aime_accesscode_str[24 + 1];\n        snprintf(\n            aime_accesscode_str,\n            sizeof(aime_accesscode_str),\n            \"%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\",\n            aime_accesscode[0],\n            aime_accesscode[1],\n            aime_accesscode[2],\n            aime_accesscode[3],\n            aime_accesscode[4],\n            aime_accesscode[5],\n            aime_accesscode[6],\n            aime_accesscode[7],\n            aime_accesscode[8],\n            aime_accesscode[9]);\n\n        // validate decimal hex representation\n        bool code_is_hex = true;\n        for(int i = 0; i < 24; i++) {\n            if(aime_accesscode_str[i] == ' ') continue;\n            if(aime_accesscode_str[i] < '0' || aime_accesscode_str[i] > '9') {\n                code_is_hex = false;\n                break;\n            }\n        }\n        if(!code_is_hex) break;\n\n        // Note: Aime access code has some other self-check algorithms that are not public.\n        // This parser does not try to verify the number.\n\n        furi_string_printf(\n            parsed_data,\n            \"\\e#Aime Card\\nAccess Code: \\n%s\\nChecksum: %02X%02X%02X\\n\",\n            aime_accesscode_str,\n            aime_checksum[0],\n            aime_checksum[1],\n            aime_checksum[2]);\n\n        parsed = true;\n\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin aime_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = aime_verify,\n    .read = aime_read,\n    .parse = aime_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor aime_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &aime_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* aime_plugin_ep(void) {\n    return &aime_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/all_in_one.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n\n#define TAG \"AllInOne\"\n\ntypedef enum {\n    AllInOneLayoutTypeA,\n    AllInOneLayoutTypeD,\n    AllInOneLayoutTypeE2,\n    AllInOneLayoutTypeE3,\n    AllInOneLayoutTypeE5,\n    AllInOneLayoutType2,\n    AllInOneLayoutTypeUnknown,\n} AllInOneLayoutType;\n\nstatic AllInOneLayoutType all_in_one_get_layout(const MfUltralightData* data) {\n    // Switch on the second half of the third byte of page 5\n    const uint8_t layout_byte = data->page[5].data[2];\n    const uint8_t layout_half_byte = data->page[5].data[2] & 0x0F;\n\n    FURI_LOG_I(TAG, \"Layout byte: %02x\", layout_byte);\n    FURI_LOG_I(TAG, \"Layout half-byte: %02x\", layout_half_byte);\n\n    switch(layout_half_byte) {\n    // If it is A, the layout type is a type A layout\n    case 0x0A:\n        return AllInOneLayoutTypeA;\n    case 0x0D:\n        return AllInOneLayoutTypeD;\n    case 0x02:\n        return AllInOneLayoutType2;\n    default:\n        FURI_LOG_I(TAG, \"Unknown layout type: %d\", layout_half_byte);\n        return AllInOneLayoutTypeUnknown;\n    }\n}\n\nstatic bool all_in_one_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    bool parsed = false;\n\n    do {\n        if(data->page[4].data[0] != 0x45 || data->page[4].data[1] != 0xD9) {\n            FURI_LOG_I(TAG, \"Pass not verified\");\n            break;\n        }\n\n        uint8_t ride_count = 0;\n        uint32_t serial = 0;\n\n        const AllInOneLayoutType layout_type = all_in_one_get_layout(data);\n\n        if(layout_type == AllInOneLayoutTypeA) {\n            // If the layout is A then the ride count is stored in the first byte of page 8\n            ride_count = data->page[8].data[0];\n        } else if(layout_type == AllInOneLayoutTypeD) {\n            // If the layout is D, the ride count is stored in the second byte of page 9\n            ride_count = data->page[9].data[1];\n        } else {\n            FURI_LOG_I(TAG, \"Unknown layout: %d\", layout_type);\n            ride_count = 137;\n        }\n\n        // // The number starts at the second half of the third byte on page 4, and is 32 bits long\n        // // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte\n        // // B8 17 A2 A4 BD becomes 81 7A 2A 4B\n        const uint8_t* serial_data_lo = data->page[4].data;\n        const uint8_t* serial_data_hi = data->page[5].data;\n\n        serial = (serial_data_lo[2] & 0x0F) << 28 | serial_data_lo[3] << 20 |\n                 serial_data_hi[0] << 12 | serial_data_hi[1] << 4 | serial_data_hi[2] >> 4;\n\n        // Format string for rides count\n        furi_string_printf(\n            parsed_data, \"\\e#All-In-One\\nNumber: %lu\\nRides left: %u\", serial, ride_count);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin all_in_one_plugin = {\n    .protocol = NfcProtocolMfUltralight,\n    .verify = NULL,\n    .read = NULL,\n    .parse = all_in_one_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor all_in_one_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &all_in_one_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* all_in_one_plugin_ep(void) {\n    return &all_in_one_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/banapass.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"Banapass\"\n\nstatic const uint64_t banapass_key_b_value_block = 0x019761AA8082;\nstatic const uint64_t banapass_key_b_access_code = 0x574343467632;\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\nstatic MfClassicKeyPair banapass_keys_if_value_block[] = {\n    {.a = 0x6090D00632F5, .b = 0x019761AA8082},\n    {.a = 0xA99164400748, .b = 0x62742819AD7C},\n    {.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},\n    {.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},\n    {.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},\n    {.a = 0x30424C029001, .b = 0x024E4E44001F},\n    {.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},\n    {.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},\n    {.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},\n    {.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},\n    {.a = 0x000390014D41, .b = 0x0800F9917CB0},\n    {.a = 0x730050555253, .b = 0x4146D4A956C4},\n    {.a = 0x131157FBB126, .b = 0xE69DD9015A43},\n    {.a = 0x337237F254D5, .b = 0x9A8389F32FBF},\n    {.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},\n    {.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},\n};\n\nstatic MfClassicKeyPair banapass_keys_if_access_code[] = {\n    {.a = 0x6090D00632F5, .b = 0x574343467632},\n    {.a = 0xA99164400748, .b = 0x62742819AD7C},\n    {.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},\n    {.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},\n    {.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},\n    {.a = 0x30424C029001, .b = 0x024E4E44001F},\n    {.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},\n    {.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},\n    {.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},\n    {.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},\n    {.a = 0x000390014D41, .b = 0x0800F9917CB0},\n    {.a = 0x730050555253, .b = 0x4146D4A956C4},\n    {.a = 0x131157FBB126, .b = 0xE69DD9015A43},\n    {.a = 0x337237F254D5, .b = 0x9A8389F32FBF},\n    {.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},\n    {.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},\n};\n\nstatic bool banapass_verify(Nfc* nfc) {\n    bool verified = true;\n\n    const uint8_t verify_sector = 0;\n    uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n    FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n    MfClassicKey key_a_0 = {};\n    bit_lib_num_to_bytes_be(\n        banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data);\n\n    MfClassicAuthContext auth_ctx = {};\n    MfClassicError error =\n        mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);\n\n    if(error != MfClassicErrorNone) {\n        FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n        verified = false;\n    }\n\n    return verified;\n}\n\nstatic bool banapass_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n        if(type != MfClassicType1k) {\n            FURI_LOG_E(TAG, \"Card not MIFARE Classic 1k\");\n            break;\n        }\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n\n        // Access Code Read Attempt\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data);\n\n        if(error_access_code == MfClassicErrorNone) {\n            nfc_device_set_data(device, NfcProtocolMfClassic, data);\n            is_read = true;\n            break;\n        }\n\n        // Value Block Read Attempt\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data);\n\n        if(error_value_block == MfClassicErrorNone) {\n            nfc_device_set_data(device, NfcProtocolMfClassic, data);\n            is_read = true;\n            break;\n        }\n        FURI_LOG_E(TAG, \"Failed to read data. Bad keys?\");\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // verify key\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);\n        uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);\n        if(key_a != banapass_keys_if_value_block[0].a) break;\n\n        furi_string_set_str(parsed_data, \"\\e#Banapass\\n\");\n        furi_string_cat_str(\n            parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n        furi_string_cat_str(parsed_data, \"\\nBandai Namco Passport\\n\");\n\n        // banapass Magic is stored at block 1, byte 2-7\n        uint8_t magic_bytes[6];\n        for(int i = 0; i < 6; i++) {\n            magic_bytes[i] = data->block[1].data[2 + i];\n        }\n\n        // verify banapass magic\n        if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' ||\n           magic_bytes[3] != 'I' || magic_bytes[4] != 'C')\n            break;\n\n        // banapass checksum is stored at block 1, starts from byte 8-15\n        uint8_t check_sum[8];\n        for(int i = 0; i < 8; i++) {\n            check_sum[i] = data->block[1].data[8 + i];\n        }\n\n        furi_string_cat_str(\n            parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n        bool is_block_2_null = true;\n        for(int i = 0; i < 16; i++) {\n            if(data->block[2].data[i] != 0) {\n                is_block_2_null = false;\n                break;\n            }\n        }\n        if(is_block_2_null) {\n            furi_string_cat_str(\n                parsed_data,\n                \"\\nPlease scan the clone at the\\nnearest CHUNITHM or\\nmaimai Cabinet for the\\nAccess Code.\\n\");\n            furi_string_cat_str(\n                parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n        } else {\n            switch(key_b) {\n            case banapass_key_b_value_block:\n                int32_t value = 0;\n                uint8_t addr = 0;\n                bool value_found = mf_classic_block_to_value(\n                    &data->block[2], &value, &addr); // block 2 is value block\n                if(value_found) {\n                    furi_string_cat_printf(parsed_data, \"\\nValue: %08lX\", value);\n                } else {\n                    furi_string_cat_str(parsed_data, \"\\nPotential clone:\\nInvalid value block.\");\n                }\n                furi_string_cat_str(\n                    parsed_data,\n                    \"\\nPlease check the back of\\nyour Bandai Namco Passport\\nfor the Access Code.\\n\");\n                furi_string_cat_str(\n                    parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n                break;\n\n            case banapass_key_b_access_code:\n                // banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes\n                uint8_t access_code[10];\n\n                furi_string_cat_printf(parsed_data, \"\\nAccess Code:\\n\");\n                bool access_code_is_bcd = true;\n\n                for(int i = 0; i < 10; i++) {\n                    access_code[i] = data->block[2].data[6 + i];\n                    furi_string_cat_printf(parsed_data, \"%02X\", access_code[i]);\n                    if(i % 2 == 1) {\n                        furi_string_cat_str(parsed_data, \" \");\n                    }\n                    if((access_code[i] >> 4) > 9) access_code_is_bcd = false;\n                    if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false;\n                }\n                furi_string_cat_printf(\n                    parsed_data, \"\\nBCD valid: %s\\n\", access_code_is_bcd ? \"Yes\" : \"No\");\n                if((access_code[0] >> 4) != 3) {\n                    furi_string_cat_printf(\n                        parsed_data,\n                        \"Potential clone:\\nAccess Code preamble\\nexpected 3, got %d\\n\",\n                        (access_code[0] >> 4));\n                }\n                furi_string_cat_str(\n                    parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n                break;\n            default:\n                break;\n            }\n        }\n        furi_string_cat_str(parsed_data, \"\\nMagic:\\n\");\n        for(int i = 0; i < 6; i++) {\n            furi_string_cat_printf(parsed_data, \"%c\", magic_bytes[i]);\n        }\n        furi_string_cat_str(parsed_data, \"\\nChecksum:\\n\");\n        for(int i = 0; i < 8; i++) {\n            furi_string_cat_printf(parsed_data, \"%02X \", check_sum[i]);\n        }\n\n        furi_string_cat_str(parsed_data, \"\\n\");\n        furi_string_cat_str(\n            parsed_data, \"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\");\n\n        parsed = true;\n\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin banapass_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = banapass_verify,\n    .read = banapass_read,\n    .parse = banapass_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor banapass_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &banapass_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* banapass_plugin_ep(void) {\n    return &banapass_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/bip.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <bit_lib/bit_lib.h>\n#include <flipper_application/flipper_application.h>\n#include <nfc/nfc_device.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"Bip\"\n\n#define SECTOR_BLOCK_OFFSET(sector, block) (((sector) * 4) + (block))\n\nstatic const uint64_t bip_keys_a[] = {\n    0x3a42f33af429,\n    0x6338a371c0ed,\n    0xf124c2578ad0,\n    0x32ac3b90ac13,\n    0x4ad1e273eaf1,\n    0xe2c42591368a,\n    0x2a3c347a1200,\n    0x16f3d5ab1139,\n    0x937a4fff3011,\n    0x35c3d2caee88,\n    0x693143f10368,\n    0xa3f97428dd01,\n    0x63f17a449af0,\n    0xc4652c54261c,\n    0xd49e2826664f,\n    0x3df14c8000a1,\n};\n\nstatic const uint64_t bip_keys_b[] = {\n    0x1fc235ac1309,\n    0x243f160918d1,\n    0x9afc42372af1,\n    0x682d401abb09,\n    0x067db45454a9,\n    0x15fc4c7613fe,\n    0x68d30288910a,\n    0xf59a36a2546d,\n    0x64e3c10394c2,\n    0xb736412614af,\n    0x324f5df65310,\n    0x643fb6de2217,\n    0x82f435dedf01,\n    0x0263de1278f3,\n    0x51284c3686a6,\n    0x6a470d54127c,\n};\n\nbool bip_verify(Nfc* nfc) {\n    bool verified = true;\n\n    const uint8_t verify_sector = 0;\n    uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n    FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n    MfClassicKey key_a_0 = {};\n    bit_lib_num_to_bytes_be(bip_keys_a[0], COUNT_OF(key_a_0.data), key_a_0.data);\n\n    MfClassicAuthContext auth_ctx = {};\n    MfClassicError error =\n        mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);\n\n    if(error != MfClassicErrorNone) {\n        FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n        verified = false;\n    }\n\n    return verified;\n}\n\nstatic bool bip_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n        if(type != MfClassicType1k) {\n            FURI_LOG_W(TAG, \"Card not MIFARE Classic 1k\");\n            break;\n        }\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data. Bad keys?\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = true;\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\ntypedef struct {\n    uint16_t year;\n    uint8_t month;\n    uint8_t day;\n    uint8_t hour;\n    uint8_t minute;\n    uint8_t second;\n} BipTimestamp;\n\nstatic void parse_bip_timestamp(const MfClassicBlock* block, BipTimestamp* timestamp) {\n    furi_assert(block);\n    furi_assert(timestamp);\n\n    timestamp->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f;\n    timestamp->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf;\n    timestamp->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f);\n    timestamp->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f;\n    timestamp->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f;\n    timestamp->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f;\n}\n\nstatic int compare_bip_timestamp(const BipTimestamp* t1, const BipTimestamp* t2) {\n    furi_assert(t1);\n    furi_assert(t2);\n    if(t1->year != t2->year) {\n        return t1->year - t2->year;\n    }\n    if(t1->month != t2->month) {\n        return t1->month - t2->month;\n    }\n    if(t1->day != t2->day) {\n        return t1->day - t2->day;\n    }\n    if(t1->hour != t2->hour) {\n        return t1->hour - t2->hour;\n    }\n    if(t1->minute != t2->minute) {\n        return t1->minute - t2->minute;\n    }\n    if(t1->second != t2->second) {\n        return t1->second - t2->second;\n    }\n    return 0;\n}\n\nstatic void print_bip_timestamp(const BipTimestamp* timestamp, FuriString* str) {\n    furi_assert(timestamp);\n    furi_assert(str);\n    furi_string_cat_printf(\n        str,\n        \"%04u-%02u-%02u %02u:%02u:%02u\",\n        timestamp->year,\n        timestamp->month,\n        timestamp->day,\n        timestamp->hour,\n        timestamp->minute,\n        timestamp->second);\n}\n\nstatic bool is_bip_block_empty(const MfClassicBlock* block) {\n    furi_assert(block);\n    // check if all but last byte are zero (last is checksum)\n    for(size_t i = 0; i < sizeof(block->data) - 1; i++) {\n        if(block->data[i] != 0) {\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic void parse_uint16_le(const uint8_t* data, uint16_t* value) {\n    furi_assert(data);\n    furi_assert(value);\n\n    *value = (data[0]) | (data[1] << 8);\n}\n\nstatic void parse_uint32_le(const uint8_t* data, uint32_t* value) {\n    furi_assert(data);\n    furi_assert(value);\n\n    *value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);\n}\n\nstatic void parse_uint16_txn_amount(const uint8_t* data, uint16_t* value) {\n    furi_assert(data);\n    furi_assert(value);\n\n    parse_uint16_le(data, value);\n    *value = *value >> 2;\n}\n\ntypedef struct {\n    BipTimestamp timestamp;\n    uint16_t amount;\n} BipTransaction;\n\nstatic bool bip_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    bool parsed = true;\n\n    struct {\n        uint32_t card_id;\n        uint16_t balance;\n        uint16_t flags;\n        BipTimestamp trip_time_window;\n        BipTransaction top_ups[3];\n        BipTransaction charges[3];\n    } bip_data = {\n        .card_id = 0,\n        .balance = 0,\n        .flags = 0,\n        .trip_time_window = {0, 0, 0, 0, 0, 0},\n        .top_ups =\n            {\n                {{0, 0, 0, 0, 0, 0}, 0},\n                {{0, 0, 0, 0, 0, 0}, 0},\n                {{0, 0, 0, 0, 0, 0}, 0},\n            },\n        .charges =\n            {\n                {{0, 0, 0, 0, 0, 0}, 0},\n                {{0, 0, 0, 0, 0, 0}, 0},\n                {{0, 0, 0, 0, 0, 0}, 0},\n            },\n    };\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    do {\n        // verify first sector keys\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        if(key != bip_keys_a[0]) {\n            parsed = false;\n            break;\n        }\n        key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);\n        if(key != bip_keys_b[0]) {\n            parsed = false;\n            break;\n        }\n\n        // Get Card ID, little-endian 4 bytes at sector 0 block 1, bytes 4-7\n        parse_uint32_le(&data->block[SECTOR_BLOCK_OFFSET(0, 1)].data[4], &bip_data.card_id);\n\n        // Get balance, little-endian 2 bytes at sector 8 block 1, bytes 0-1\n        parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[0], &bip_data.balance);\n\n        // Get balance flags (negative balance, etc.), little-endian 2 bytes at sector 8 block 1, bytes 2-3\n        parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[2], &bip_data.flags);\n\n        // Get trip time window, proprietary format, at sector 5 block 1, bytes 0-7\n        parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(5, 1)], &bip_data.trip_time_window);\n\n        // Last 3 top-ups: sector 10, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 9-10\n        for(size_t i = 0; i < 3; i++) {\n            if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(10, i)])) {\n                continue;\n            }\n            BipTransaction* top_up = &bip_data.top_ups[i];\n            parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(10, i)], &top_up->timestamp);\n            parse_uint16_txn_amount(\n                &data->block[SECTOR_BLOCK_OFFSET(10, i)].data[9], &top_up->amount);\n        }\n\n        // Last 3 charges (i.e. trips), sector 11, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 10-11\n        for(size_t i = 0; i < 3; i++) {\n            if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(11, i)])) {\n                continue;\n            }\n            BipTransaction* charge = &bip_data.charges[i];\n            parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(11, i)], &charge->timestamp);\n            parse_uint16_txn_amount(\n                &data->block[SECTOR_BLOCK_OFFSET(11, i)].data[10], &charge->amount);\n        }\n\n        // All data is now parsed and stored in bip_data, now print it\n\n        // Print basic info\n        furi_string_printf(\n            parsed_data,\n            \"\\e#Tarjeta Bip!\\n\"\n            \"Card Number: %lu\\n\"\n            \"Balance: $%hu (flags %hu)\\n\"\n            \"Current Trip Window Ends:\\n  @\",\n            bip_data.card_id,\n            bip_data.balance,\n            bip_data.flags);\n\n        print_bip_timestamp(&bip_data.trip_time_window, parsed_data);\n\n        // Find newest top-up\n        size_t newest_top_up = 0;\n        for(size_t i = 1; i < 3; i++) {\n            const BipTimestamp* newest = &bip_data.top_ups[newest_top_up].timestamp;\n            const BipTimestamp* current = &bip_data.top_ups[i].timestamp;\n            if(compare_bip_timestamp(current, newest) > 0) {\n                newest_top_up = i;\n            }\n        }\n\n        // Print top-ups, newest first\n        furi_string_cat_printf(parsed_data, \"\\n\\e#Last Top-ups\");\n        for(size_t i = 0; i < 3; i++) {\n            const BipTransaction* top_up = &bip_data.top_ups[(3u + newest_top_up - i) % 3];\n            furi_string_cat_printf(parsed_data, \"\\n+$%d\\n  @\", top_up->amount);\n            print_bip_timestamp(&top_up->timestamp, parsed_data);\n        }\n\n        // Find newest charge\n        size_t newest_charge = 0;\n        for(size_t i = 1; i < 3; i++) {\n            const BipTimestamp* newest = &bip_data.charges[newest_charge].timestamp;\n            const BipTimestamp* current = &bip_data.charges[i].timestamp;\n            if(compare_bip_timestamp(current, newest) > 0) {\n                newest_charge = i;\n            }\n        }\n\n        // Print charges\n        furi_string_cat_printf(parsed_data, \"\\n\\e#Last Charges (Trips)\");\n        for(size_t i = 0; i < 3; i++) {\n            const BipTransaction* charge = &bip_data.charges[(3u + newest_charge - i) % 3];\n            furi_string_cat_printf(parsed_data, \"\\n-$%d\\n  @\", charge->amount);\n            print_bip_timestamp(&charge->timestamp, parsed_data);\n        }\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin bip_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = bip_verify,\n    .read = bip_read,\n    .parse = bip_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor bip_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &bip_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* bip_plugin_ep(void) {\n    return &bip_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/clipper.c",
    "content": "/*\n * clipper.c - Parser for Clipper cards (San Francisco, California).\n *\n * Based on research, some of which dates to 2007!\n *\n * Copyright 2024 Jeremy Cooper <jeremy.gthb@baymoo.org>\n *\n * This program is free software: you can redistribute it and/or modify it\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <lib/nfc/protocols/mf_desfire/mf_desfire.h>\n#include <bit_lib/bit_lib.h>\n#include <applications/services/locale/locale.h>\n#include <datetime/datetime.h>\n#include <inttypes.h>\n\n//\n// Table of application ids observed in the wild, and their sources.\n//\nstatic const struct {\n    const MfDesfireApplicationId app;\n    const char* type;\n} clipper_types[] = {\n    // Application advertised on classic, plastic cards.\n    {.app = {.data = {0x90, 0x11, 0xf2}}, .type = \"Card\"},\n    // Application advertised on a mobile device.\n    {.app = {.data = {0x91, 0x11, 0xf2}}, .type = \"Mobile Device\"},\n};\nstatic const size_t kNumCardTypes = sizeof(clipper_types) / sizeof(clipper_types[0]);\n\nstruct IdMapping_struct {\n    uint16_t id;\n    const char* name;\n};\ntypedef struct IdMapping_struct IdMapping;\n\n#define COUNT(_array) sizeof(_array) / sizeof(_array[0])\n\n//\n// Known transportation agencies and their identifiers.\n//\nstatic const IdMapping agency_names[] = {\n    {.id = 0x0001, .name = \"AC Transit\"},\n    {.id = 0x0004, .name = \"BART\"},\n    {.id = 0x0006, .name = \"Caltrain\"},\n    {.id = 0x0008, .name = \"CCTA\"},\n    {.id = 0x000b, .name = \"GGT\"},\n    {.id = 0x000f, .name = \"SamTrans\"},\n    {.id = 0x0011, .name = \"VTA\"},\n    {.id = 0x0012, .name = \"Muni\"},\n    {.id = 0x0019, .name = \"GG Ferry\"},\n    {.id = 0x001b, .name = \"SF Bay Ferry\"},\n};\nstatic const size_t kNumAgencies = COUNT(agency_names);\n\n//\n// Known station names for various agencies.\n//\nstatic const IdMapping bart_zones[] = {\n    {.id = 0x0001, .name = \"Colma\"},\n    {.id = 0x0002, .name = \"Daly City\"},\n    {.id = 0x0003, .name = \"Balboa Park\"},\n    {.id = 0x0004, .name = \"Glen Park\"},\n    {.id = 0x0005, .name = \"24th St Mission\"},\n    {.id = 0x0006, .name = \"16th St Mission\"},\n    {.id = 0x0007, .name = \"Civic Center/UN Plaza\"},\n    {.id = 0x0008, .name = \"Powell St\"},\n    {.id = 0x0009, .name = \"Montgomery St\"},\n    {.id = 0x000a, .name = \"Embarcadero\"},\n    {.id = 0x000b, .name = \"West Oakland\"},\n    {.id = 0x000c, .name = \"12th St/Oakland City Center\"},\n    {.id = 0x000d, .name = \"19th St/Oakland\"},\n    {.id = 0x000e, .name = \"MacArthur\"},\n    {.id = 0x000f, .name = \"Rockridge\"},\n    {.id = 0x0010, .name = \"Orinda\"},\n    {.id = 0x0011, .name = \"Lafayette\"},\n    {.id = 0x0012, .name = \"Walnut Creek\"},\n    {.id = 0x0013, .name = \"Pleasant Hill/Contra Costa Centre\"},\n    {.id = 0x0014, .name = \"Concord\"},\n    {.id = 0x0015, .name = \"North Concord/Martinez\"},\n    {.id = 0x0016, .name = \"Pittsburg/Bay Point\"},\n    {.id = 0x0017, .name = \"Ashby\"},\n    {.id = 0x0018, .name = \"Downtown Berkeley\"},\n    {.id = 0x0019, .name = \"North Berkeley\"},\n    {.id = 0x001a, .name = \"El Cerrito Plaza\"},\n    {.id = 0x001b, .name = \"El Cerrito Del Norte\"},\n    {.id = 0x001c, .name = \"Richmond\"},\n    {.id = 0x001d, .name = \"Lake Merrit\"},\n    {.id = 0x001e, .name = \"Fruitvale\"},\n    {.id = 0x001f, .name = \"Coliseum\"},\n    {.id = 0x0020, .name = \"San Leandro\"},\n    {.id = 0x0021, .name = \"Bay Fair\"},\n    {.id = 0x0022, .name = \"Hayward\"},\n    {.id = 0x0023, .name = \"South Hayward\"},\n    {.id = 0x0024, .name = \"Union City\"},\n    {.id = 0x0025, .name = \"Fremont\"},\n    {.id = 0x0026, .name = \"Castro Valley\"},\n    {.id = 0x0027, .name = \"Dublin/Pleasanton\"},\n    {.id = 0x0028, .name = \"South San Francisco\"},\n    {.id = 0x0029, .name = \"San Bruno\"},\n    {.id = 0x002a, .name = \"SFO Airport\"},\n    {.id = 0x002b, .name = \"Millbrae\"},\n    {.id = 0x002c, .name = \"West Dublin/Pleasanton\"},\n    {.id = 0x002d, .name = \"OAK Airport\"},\n    {.id = 0x002e, .name = \"Warm Springs/South Fremont\"},\n    {.id = 0x002f, .name = \"Milpitas\"},\n    {.id = 0x0030, .name = \"Berryessa/North San Jose\"},\n};\nstatic const size_t kNumBARTZones = COUNT(bart_zones);\n\nstatic const IdMapping muni_zones[] = {\n    {.id = 0x0000, .name = \"City Street\"},\n    {.id = 0x0005, .name = \"Embarcadero\"},\n    {.id = 0x0006, .name = \"Montgomery\"},\n    {.id = 0x0007, .name = \"Powell\"},\n    {.id = 0x0008, .name = \"Civic Center\"},\n    {.id = 0x0009, .name = \"Van Ness\"}, // Guessed\n    {.id = 0x000a, .name = \"Church\"},\n    {.id = 0x000b, .name = \"Castro\"},\n    {.id = 0x000c, .name = \"Forest Hill\"}, // Guessed\n    {.id = 0x000d, .name = \"West Portal\"},\n    {.id = 0x0019, .name = \"Union Square/Market Street\"},\n    {.id = 0x001a, .name = \"Chinatown - Rose Pak\"},\n    {.id = 0x001b, .name = \"Yerba Buena/Moscone\"},\n};\nstatic const size_t kNumMUNIZones = COUNT(muni_zones);\n\nstatic const IdMapping actransit_zones[] = {\n    {.id = 0x0000, .name = \"City Street\"},\n};\nstatic const size_t kNumACTransitZones = COUNT(actransit_zones);\n\n// Instead of persisting individual Station IDs, Caltrain saves Zone numbers.\n// https://www.caltrain.com/stations-zones\nstatic const IdMapping caltrain_zones[] = {\n    {.id = 0x0001, .name = \"Zone 1\"},\n    {.id = 0x0002, .name = \"Zone 2\"},\n    {.id = 0x0003, .name = \"Zone 3\"},\n    {.id = 0x0004, .name = \"Zone 4\"},\n    {.id = 0x0005, .name = \"Zone 5\"},\n    {.id = 0x0006, .name = \"Zone 6\"},\n};\n\nstatic const size_t kNumCaltrainZones = COUNT(caltrain_zones);\n\n//\n// Full agency+zone mapping.\n//\nstatic const struct {\n    uint16_t agency_id;\n    const IdMapping* zone_map;\n    size_t zone_count;\n} agency_zone_map[] = {\n    {.agency_id = 0x0001, .zone_map = actransit_zones, .zone_count = kNumACTransitZones},\n    {.agency_id = 0x0004, .zone_map = bart_zones, .zone_count = kNumBARTZones},\n    {.agency_id = 0x0006, .zone_map = caltrain_zones, .zone_count = kNumCaltrainZones},\n    {.agency_id = 0x0012, .zone_map = muni_zones, .zone_count = kNumMUNIZones}};\nstatic const size_t kNumAgencyZoneMaps = COUNT(agency_zone_map);\n\n// File ids of important files on the card.\nstatic const MfDesfireFileId clipper_ecash_file_id = 2;\nstatic const MfDesfireFileId clipper_histidx_file_id = 6;\nstatic const MfDesfireFileId clipper_identity_file_id = 8;\nstatic const MfDesfireFileId clipper_history_file_id = 14;\n\nstruct ClipperCardInfo_struct {\n    uint32_t serial_number;\n    uint16_t counter;\n    uint16_t last_txn_id;\n    uint32_t last_updated_tm_1900;\n    uint16_t last_terminal_id;\n    int16_t balance_cents;\n};\ntypedef struct ClipperCardInfo_struct ClipperCardInfo;\n\n// Forward declarations for helper functions.\nstatic void furi_string_cat_timestamp(\n    FuriString* str,\n    const char* date_hdr,\n    const char* time_hdr,\n    uint32_t tmst_1900);\nstatic void epoch_1900_datetime_to_furi(uint32_t seconds, DateTime* out);\nstatic bool get_file_contents(\n    const MfDesfireApplication* app,\n    const MfDesfireFileId* id,\n    MfDesfireFileType type,\n    size_t min_size,\n    const uint8_t** out);\nstatic bool decode_id_file(const uint8_t* ef8_data, ClipperCardInfo* info);\nstatic bool decode_cash_file(const uint8_t* ef2_data, ClipperCardInfo* info);\nstatic bool get_map_item(uint16_t id, const IdMapping* map, size_t sz, const char** out);\nstatic bool get_agency_zone_name(uint16_t agency_id, uint16_t zone_id, const char** out);\nstatic void\n    decode_usd(int16_t amount_cents, bool* out_is_negative, int16_t* out_usd, uint16_t* out_cents);\nstatic bool dump_ride_history(\n    const uint8_t* index_file,\n    const uint8_t* history_file,\n    size_t len,\n    FuriString* parsed_data);\nstatic bool dump_ride_event(const uint8_t* record, FuriString* parsed_data);\n\n// Unmarshal a 32-bit integer, big endian, unsigned\nstatic inline uint32_t get_u32be(const uint8_t* field) {\n    return bit_lib_bytes_to_num_be(field, 4);\n}\n\n// Unmarshal a 16-bit integer, big endian, unsigned\nstatic uint16_t get_u16be(const uint8_t* field) {\n    return bit_lib_bytes_to_num_be(field, 2);\n}\n\n// Unmarshal a 16-bit integer, big endian, signed, two's-complement\nstatic int16_t get_i16be(const uint8_t* field) {\n    uint16_t raw = get_u16be(field);\n    if(raw > 0x7fff)\n        return -((uint32_t)0x10000 - raw);\n    else\n        return raw;\n}\n\nstatic bool clipper_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    bool parsed = false;\n\n    do {\n        const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n        const MfDesfireApplication* app = NULL;\n        const char* device_description = NULL;\n\n        for(size_t i = 0; i < kNumCardTypes; i++) {\n            app = mf_desfire_get_application(data, &clipper_types[i].app);\n            device_description = clipper_types[i].type;\n            if(app != NULL) break;\n        }\n\n        // If no matching application was found, abort this parser.\n        if(app == NULL) break;\n\n        ClipperCardInfo info;\n        const uint8_t* id_data;\n        if(!get_file_contents(\n               app, &clipper_identity_file_id, MfDesfireFileTypeStandard, 5, &id_data))\n            break;\n        if(!decode_id_file(id_data, &info)) break;\n\n        const uint8_t* cash_data;\n        if(!get_file_contents(app, &clipper_ecash_file_id, MfDesfireFileTypeBackup, 32, &cash_data))\n            break;\n        if(!decode_cash_file(cash_data, &info)) break;\n\n        int16_t balance_usd;\n        uint16_t balance_cents;\n        bool _balance_is_negative;\n        decode_usd(info.balance_cents, &_balance_is_negative, &balance_usd, &balance_cents);\n\n        furi_string_cat_printf(\n            parsed_data,\n            \"\\e#Clipper\\n\"\n            \"Serial: %\" PRIu32 \"\\n\"\n            \"Balance: $%d.%02u\\n\"\n            \"Type: %s\\n\"\n            \"\\e#Last Update\\n\",\n            info.serial_number,\n            balance_usd,\n            balance_cents,\n            device_description);\n        if(info.last_updated_tm_1900 != 0)\n            furi_string_cat_timestamp(\n                parsed_data, \"Date: \", \"\\nTime: \", info.last_updated_tm_1900);\n        else\n            furi_string_cat_str(parsed_data, \"Never\");\n        furi_string_cat_printf(\n            parsed_data,\n            \"\\nTerminal: 0x%04x\\n\"\n            \"Transaction Id: %u\\n\"\n            \"Counter: %u\\n\",\n            info.last_terminal_id,\n            info.last_txn_id,\n            info.counter);\n\n        const uint8_t *history_index, *history;\n\n        if(!get_file_contents(\n               app, &clipper_histidx_file_id, MfDesfireFileTypeBackup, 16, &history_index))\n            break;\n        if(!get_file_contents(\n               app, &clipper_history_file_id, MfDesfireFileTypeStandard, 512, &history))\n            break;\n\n        if(!dump_ride_history(history_index, history, 512, parsed_data)) break;\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\nstatic bool get_file_contents(\n    const MfDesfireApplication* app,\n    const MfDesfireFileId* id,\n    MfDesfireFileType type,\n    size_t min_size,\n    const uint8_t** out) {\n    const MfDesfireFileSettings* settings = mf_desfire_get_file_settings(app, id);\n    if(settings == NULL) return false;\n    if(settings->type != type) return false;\n\n    const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, id);\n    if(file_data == NULL) return false;\n\n    if(simple_array_get_count(file_data->data) < min_size) return false;\n\n    *out = simple_array_cget_data(file_data->data);\n\n    return true;\n}\n\nstatic bool decode_id_file(const uint8_t* ef8_data, ClipperCardInfo* info) {\n    // Identity file (8)\n    //\n    // Byte view\n    //\n    //       0    1    2    3    4    5    6    7    8\n    //       +----+----.----.----.----+----.----.----+\n    // 0x00  | uk | card_id           | unknown      |\n    //       +----+----.----.----.----+----.----.----+\n    // 0x08  | unknown                               |\n    //       +----.----.----.----.----.----.----.----+\n    // 0x10    ...\n    //\n    //\n    // Field          Datatype   Description\n    // -----          --------   -----------\n    // uk             ?8??       Unknown, 8-bit byte\n    // card_id        U32BE      Card identifier\n    //\n    info->serial_number = bit_lib_bytes_to_num_be(&ef8_data[1], 4);\n    return true;\n}\n\nstatic bool decode_cash_file(const uint8_t* ef2_data, ClipperCardInfo* info) {\n    // ECash file (2)\n    //\n    // Byte view\n    //\n    //       0    1    2    3    4    5    6    7    8\n    //       +----.----+----.----+----.----.----.----+\n    // 0x00  |  unk00  | counter | timestamp_1900    |\n    //       +----.----+----.----+----.----.----.----+\n    // 0x08  | term_id |     unk01                   |\n    //       +----.----+----.----+----.----.----.----+\n    // 0x10  | txn_id  | balance |      unknown      |\n    //       +----.----+----.----+----.----.----.----+\n    // 0x18  |               unknown                 |\n    //       +---------------------------------------+\n    //\n    // Field          Datatype Description\n    // -----          -------- -----------\n    // unk00          U8[2]     Unknown bytes\n    // counter        U16BE     Unknown, appears to be a counter\n    // timestamp_1900 U32BE     Timestamp of last transaction, in seconds\n    //                          since 1900-01-01 GMT.\n    // unk01          U8[6]     Unknown bytes\n    // txn_id         U16BE     Id of last transaction.\n    // balance        S16BE     Card cash balance, in cents.\n    //                          Cards can obtain negative balances in this\n    //                          system, so balances are signed integers.\n    //                          Maximum card balance is therefore\n    //                          $327.67.\n    // unk02          U8[12]    Unknown bytes.\n    //\n    info->counter = get_u16be(&ef2_data[2]);\n    info->last_updated_tm_1900 = get_u32be(&ef2_data[4]);\n    info->last_terminal_id = get_u16be(&ef2_data[8]);\n    info->last_txn_id = get_u16be(&ef2_data[0x10]);\n    info->balance_cents = get_i16be(&ef2_data[0x12]);\n    return true;\n}\n\nstatic bool dump_ride_history(\n    const uint8_t* index_file,\n    const uint8_t* history_file,\n    size_t len,\n    FuriString* parsed_data) {\n    static const size_t kRideRecordSize = 0x20;\n\n    for(size_t i = 0; i < 16; i++) {\n        uint8_t record_num = index_file[i];\n        if(record_num == 0xff) break;\n\n        size_t record_offset = record_num * kRideRecordSize;\n\n        if(record_offset + kRideRecordSize > len) break;\n\n        const uint8_t* record = &history_file[record_offset];\n        if(!dump_ride_event(record, parsed_data)) break;\n    }\n\n    return true;\n}\n\nstatic bool dump_ride_event(const uint8_t* record, FuriString* parsed_data) {\n    // Ride record\n    //\n    //       0    1    2    3    4    5    6    7    8\n    //       +----+----+----.----+----.----+----.----+\n    // 0x00  |0x10| ?  | agency  | ?       | fare    |\n    //       +----.----+----.----+----.----.----.----+\n    // 0x08  | ?       | vehicle | time_on           |\n    //       +----.----.----.----+----.----+----.----+\n    // 0x10  | time_off          | zone_on | zone_off|\n    //       +----+----.----.----.----+----+----+----+\n    // 0x18  | ?  | ?                 | ?  | ?  | ?  |\n    //       +----+----.----.----.----+----+----+----+\n    //\n    // Field          Datatype Description\n    // -----          -------- -----------\n    // agency         U16BE    Transportation agency identifier.\n    //                         Known ids:\n    //                         1  == AC Transit\n    //                         4  == BART\n    //                         18 == SF MUNI\n    // fare           I16BE    Fare deducted, in cents.\n    // vehicle        U16BE    Vehicle id (0 == not provided)\n    // time_on        U32BE    Boarding time, in seconds since 1900-01-01 GMT.\n    // time_off       U32BE    Off-boarding time, if present, in seconds\n    //                         since 1900-01-01 GMT. Set to zero if no offboard\n    //                         has been recorded.\n    // zone_on        U16BE    Id of boarding zone or station. Agency-specific.\n    // zone_off       U16BE    Id of offboarding zone or station. Agency-\n    //                         specific.\n    if(record[0] != 0x10) return false;\n\n    uint16_t agency_id = get_u16be(&record[2]);\n    if(agency_id == 0)\n        // Likely empty record. Skip.\n        return false;\n    const char* agency_name;\n    bool ok = get_map_item(agency_id, agency_names, kNumAgencies, &agency_name);\n    if(!ok) agency_name = \"Unknown\";\n\n    uint16_t vehicle_id = get_u16be(&record[0x0a]);\n\n    int16_t fare_raw_cents = get_i16be(&record[6]);\n    bool _fare_is_negative;\n    int16_t fare_usd;\n    uint16_t fare_cents;\n    decode_usd(fare_raw_cents, &_fare_is_negative, &fare_usd, &fare_cents);\n\n    uint32_t time_on_raw = get_u32be(&record[0x0c]);\n    uint32_t time_off_raw = get_u32be(&record[0x10]);\n    uint16_t zone_id_on = get_u16be(&record[0x14]);\n    uint16_t zone_id_off = get_u16be(&record[0x16]);\n\n    const char *zone_on, *zone_off;\n    if(!get_agency_zone_name(agency_id, zone_id_on, &zone_on)) {\n        zone_on = \"Unknown\";\n    }\n    if(!get_agency_zone_name(agency_id, zone_id_off, &zone_off)) {\n        zone_off = \"Unknown\";\n    }\n\n    furi_string_cat_str(parsed_data, \"\\e#Ride Record\\n\");\n    furi_string_cat_timestamp(parsed_data, \"Date: \", \"\\nTime: \", time_on_raw);\n    furi_string_cat_printf(\n        parsed_data,\n        \"\\n\"\n        \"Fare: $%d.%02u\\n\"\n        \"Agency: %s (%04x)\\n\"\n        \"On: %s (%04x)\\n\",\n        fare_usd,\n        fare_cents,\n        agency_name,\n        agency_id,\n        zone_on,\n        zone_id_on);\n    if(vehicle_id != 0) {\n        furi_string_cat_printf(parsed_data, \"Vehicle id: %d\\n\", vehicle_id);\n    }\n    if(time_off_raw != 0) {\n        furi_string_cat_printf(parsed_data, \"Off: %s (%04x)\\n\", zone_off, zone_id_off);\n        furi_string_cat_timestamp(parsed_data, \"Date Off: \", \"\\nTime Off: \", time_off_raw);\n        furi_string_cat_str(parsed_data, \"\\n\");\n    }\n\n    return true;\n}\n\nstatic bool get_map_item(uint16_t id, const IdMapping* map, size_t sz, const char** out) {\n    for(size_t i = 0; i < sz; i++) {\n        if(map[i].id == id) {\n            *out = map[i].name;\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic bool get_agency_zone_name(uint16_t agency_id, uint16_t zone_id, const char** out) {\n    for(size_t i = 0; i < kNumAgencyZoneMaps; i++) {\n        if(agency_zone_map[i].agency_id == agency_id) {\n            return get_map_item(\n                zone_id, agency_zone_map[i].zone_map, agency_zone_map[i].zone_count, out);\n        }\n    }\n\n    return false;\n}\n\n// Split a balance/fare amount from raw cents to dollars and cents portion,\n// automatically adjusting the cents portion so that it is always positive,\n// for easier display.\nstatic void\n    decode_usd(int16_t amount_cents, bool* out_is_negative, int16_t* out_usd, uint16_t* out_cents) {\n    *out_usd = amount_cents / 100;\n\n    if(amount_cents >= 0) {\n        *out_is_negative = false;\n        *out_cents = amount_cents % 100;\n    } else {\n        *out_is_negative = true;\n        *out_cents = (amount_cents * -1) % 100;\n    }\n}\n\n// Decode a raw 1900-based timestamp and append a human-readable form to a\n// FuriString.\nstatic void furi_string_cat_timestamp(\n    FuriString* str,\n    const char* date_hdr,\n    const char* time_hdr,\n    uint32_t tmst_1900) {\n    DateTime tm;\n\n    epoch_1900_datetime_to_furi(tmst_1900, &tm);\n\n    FuriString* date_str = furi_string_alloc();\n    locale_format_date(date_str, &tm, locale_get_date_format(), \"-\");\n\n    FuriString* time_str = furi_string_alloc();\n    locale_format_time(time_str, &tm, locale_get_time_format(), true);\n\n    furi_string_cat_printf(\n        str,\n        \"%s%s%s%s (UTC)\",\n        date_hdr,\n        furi_string_get_cstr(date_str),\n        time_hdr,\n        furi_string_get_cstr(time_str));\n\n    furi_string_free(date_str);\n    furi_string_free(time_str);\n}\n\n// Convert a \"1900\"-based timestamp to Furi time, assuming a UTC/GMT timezone.\nstatic void epoch_1900_datetime_to_furi(uint32_t seconds, DateTime* out) {\n    uint16_t year, month, day, hour, minute, second;\n\n    // Calculate absolute number of days elapsed since the 1900 epoch\n    // and save the residual for the time within the day.\n    uint32_t absolute_days = seconds / 86400;\n    uint32_t seconds_within_day = seconds % 86400;\n\n    // Calculate day of the week.\n    // January 1, 1900 was a Monday (\"day of week\" = 1)\n    uint8_t dow = (absolute_days + 1) % 7;\n\n    //\n    // Compute the date by simply marching through time in as large chunks\n    // as possible.\n    //\n\n    for(year = 1900;; year++) {\n        uint16_t year_days = datetime_get_days_per_year(year);\n        if(absolute_days >= year_days)\n            absolute_days -= year_days;\n        else\n            break;\n    }\n\n    bool is_leap = datetime_is_leap_year(year);\n\n    for(month = 1;; month++) {\n        uint8_t days_in_month = datetime_get_days_per_month(is_leap, month);\n        if(absolute_days >= days_in_month)\n            absolute_days -= days_in_month;\n        else\n            break;\n    }\n\n    day = absolute_days + 1;\n    hour = seconds_within_day / 3600;\n    uint16_t sub_hour = seconds_within_day % 3600;\n    minute = sub_hour / 60;\n    second = sub_hour % 60;\n\n    out->year = year;\n    out->month = month;\n    out->day = day;\n    out->hour = hour;\n    out->minute = minute;\n    out->second = second;\n    out->weekday = dow;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin clipper_plugin = {\n    .protocol = NfcProtocolMfDesfire,\n    .verify = NULL,\n    .read = NULL,\n    .parse = clipper_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor clipper_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &clipper_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* clipper_plugin_ep(void) {\n    return &clipper_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/disney_infinity.c",
    "content": "#include <mbedtls/sha1.h>\n#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include <flipper_format/flipper_format.h>\n\n#define TAG     \"DisneyInfinity\"\n#define UID_LEN 7\n\n// Derived from https://nfc.toys/#new-interoperability-for-infinity\nstatic uint8_t seed[38] = {0x0A, 0x14, 0xFD, 0x05, 0x07, 0xFF, 0x4B, 0xCD, 0x02, 0x6B,\n                           0xA8, 0x3F, 0x0A, 0x3B, 0x89, 0xA9, 0x00, 0x00, 0x00, 0x00,\n                           0x00, 0x00, 0x00, 0x28, 0x63, 0x29, 0x20, 0x44, 0x69, 0x73,\n                           0x6E, 0x65, 0x79, 0x20, 0x32, 0x30, 0x31, 0x33};\n\nvoid di_key(const uint8_t* uid, MfClassicKey* key) {\n    uint8_t hash[20];\n    memcpy(seed + 16, uid, UID_LEN);\n    mbedtls_sha1(seed, sizeof(seed), hash);\n    key->data[0] = hash[3];\n    key->data[1] = hash[2];\n    key->data[2] = hash[1];\n    key->data[3] = hash[0];\n    key->data[4] = hash[7];\n    key->data[5] = hash[6];\n}\n\nstatic bool disney_infinity_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n    size_t* uid_len = 0;\n    bool is_read = false;\n    MfClassicData* data = mf_classic_alloc();\n\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n    const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len);\n    MfClassicDeviceKeys keys = {};\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            di_key(uid_bytes, &keys.key_a[i]);\n            di_key(uid_bytes, &keys.key_b[i]);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data: %d\", error);\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = mf_classic_is_card_read(data);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool disney_infinity_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    size_t* uid_len = 0;\n    bool parsed = false;\n    FuriString* name = furi_string_alloc();\n    const uint8_t verify_sector = 0;\n    MfClassicKey key = {};\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n    const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len);\n\n    do {\n        // verify key\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, verify_sector);\n\n        di_key(uid_bytes, &key);\n        if(memcmp(key.data, sec_tr->key_a.data, 6) != 0) break;\n\n        // At some point I'd like to add name lookup like Skylanders\n        furi_string_printf(parsed_data, \"\\e#Disney Infinity\\n\");\n\n        parsed = true;\n\n    } while(false);\n\n    furi_string_free(name);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin disney_infinity_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = NULL, // Need UID to verify key(s)\n    .read = disney_infinity_read,\n    .parse = disney_infinity_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor disney_infinity_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &disney_infinity_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* disney_infinity_plugin_ep(void) {\n    return &disney_infinity_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/gallagher.c",
    "content": "/* gallagher.c - NFC supported cards plugin for Gallagher access control cards (New Zealand).\n * Author: Nick Mooney (nick@mooney.nz)\n * \n * Reference: https://github.com/megabug/gallagher-research\n*/\n\n#include \"nfc_supported_card_plugin.h\"\n#include \"../../api/gallagher/gallagher_util.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include <bit_lib/bit_lib.h>\n\nstatic bool gallagher_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    if(!(data->type == MfClassicType1k || data->type == MfClassicType4k)) {\n        return false;\n    }\n\n    // It's possible for a single tag to contain multiple credentials,\n    // but this is currently unimplementecd.\n    const uint8_t credential_sector_start_block_number =\n        mf_classic_get_first_block_num_of_sector(GALLAGHER_CREDENTIAL_SECTOR);\n\n    // Test 1: The first 8 bytes and the second 8 bytes should be bitwise inverses.\n    const uint8_t* credential_block_start_ptr =\n        &data->block[credential_sector_start_block_number].data[0];\n    uint64_t cardholder_credential = bit_lib_bytes_to_num_be(credential_block_start_ptr, 8);\n    uint64_t cardholder_credential_inverse =\n        bit_lib_bytes_to_num_be(credential_block_start_ptr + 8, 8);\n    // Due to endianness, this is testing the bytes in the wrong order,\n    // but the result still should be correct.\n    if(cardholder_credential != ~cardholder_credential_inverse) {\n        return false;\n    }\n\n    // Test 2: The contents of the second block should be equal to the GALLAGHER_CARDAX_ASCII constant.\n    const uint8_t* cardax_block_start_ptr =\n        &data->block[credential_sector_start_block_number + 1].data[0];\n    if(memcmp(cardax_block_start_ptr, GALLAGHER_CARDAX_ASCII, MF_CLASSIC_BLOCK_SIZE) != 0) {\n        return false;\n    }\n\n    // Deobfuscate the credential data\n    GallagherCredential credential;\n    gallagher_deobfuscate_and_parse_credential(&credential, credential_block_start_ptr);\n\n    char display_region = 'A';\n    // Per https://github.com/megabug/gallagher-research/blob/master/formats/cardholder/cardholder.md,\n    // regions are generally A-P.\n    if(credential.region < 16) {\n        display_region = display_region + (char)credential.region;\n    } else {\n        display_region = '?';\n    }\n\n    furi_string_cat_printf(\n        parsed_data,\n        \"\\e#Gallagher NZ\\nFacility %c%u\\nCard %lu (IL %u)\",\n        display_region,\n        credential.facility,\n        credential.card,\n        credential.issue);\n    return true;\n}\n\nstatic const NfcSupportedCardsPlugin gallagher_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = NULL,\n    .read = NULL,\n    .parse = gallagher_parse,\n};\n\nstatic const FlipperAppPluginDescriptor gallagher_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &gallagher_plugin,\n};\n\n/* Plugin entry point */\nconst FlipperAppPluginDescriptor* gallagher_plugin_ep(void) {\n    return &gallagher_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/hi.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application/flipper_application.h>\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"HI!\"\n\n#define KEY_LENGTH    6\n#define HI_KEY_TO_GEN 5\n#define UID_LENGTH    7\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\ntypedef struct {\n    MfClassicKeyPair* keys;\n    uint32_t verify_sector;\n} HiCardConfig;\n\nstatic MfClassicKeyPair hi_1k_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0x30871CF60CF1}, // 000\n    {.a = 0x000000000000, .b = 0x000000000000}, // 001\n    {.a = 0x000000000000, .b = 0x000000000000}, // 002\n    {.a = 0x000000000000, .b = 0x000000000000}, // 003\n    {.a = 0x000000000000, .b = 0x000000000000}, // 004\n    {.a = 0x42FFE4C76209, .b = 0x7B30CFD04CBD}, // 005\n    {.a = 0x01ED8145BDF8, .b = 0x92257F472FCE}, // 006\n    {.a = 0x7583A07D21A6, .b = 0x51CA6EA8EE26}, // 007\n    {.a = 0x1E10BF5D6A1D, .b = 0x87B9B9BFABA6}, // 008\n    {.a = 0xF9DB1B2B21BA, .b = 0x80A781F4134C}, // 009\n    {.a = 0x7F5283FACB72, .b = 0x73250009D75A}, // 010\n    {.a = 0xE48E86A03078, .b = 0xCFFBBF08A254}, // 011\n    {.a = 0x39AB26301F60, .b = 0xC71A6E532C83}, // 012\n    {.a = 0xAD656C6C639F, .b = 0xFD9819CBD20A}, // 013\n    {.a = 0xF0E15160DB3E, .b = 0x3F622D515ADD}, // 014\n    {.a = 0x03F44E033C42, .b = 0x61E897875F46}, // 015\n};\n\n//KDF\nvoid hi_generate_key(uint8_t* uid, uint8_t keyA[5][KEY_LENGTH], uint8_t keyB[5][KEY_LENGTH]) {\n    // Static XOR table for key generation\n    static const uint8_t xor_table_keyB[4][6] = {\n        {0x1F, 0xC4, 0x4D, 0x94, 0x6A, 0x31},\n        {0x12, 0xC1, 0x5C, 0x70, 0xDF, 0x31},\n        {0x56, 0xF0, 0x13, 0x1B, 0x63, 0xF2},\n        {0x4E, 0xFA, 0xC2, 0xF8, 0xC9, 0xCC}};\n\n    static const uint8_t xor_table_keyA[4][6] = {\n        {0xB6, 0xE6, 0xAE, 0x72, 0x91, 0x0D},\n        {0x6D, 0x38, 0x50, 0xFB, 0x42, 0x89},\n        {0x1E, 0x5F, 0xC7, 0xED, 0xAA, 0x02},\n        {0x7E, 0xB9, 0xCA, 0xF1, 0x9C, 0x59}};\n\n    // Permutation table for rearranging elements in uid\n    static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 2};\n    static const uint8_t xorOrderB[6] = {1, 3, 3, 2, 1, 0};\n\n    // Generate key based on uid and XOR table\n    for(uint8_t j = 1; j < 5; j++) {\n        for(uint8_t i = 0; i < 6; i++) {\n            keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i];\n            keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i];\n        }\n    }\n}\n\nstatic bool hi_get_card_config(HiCardConfig* config, MfClassicType type) {\n    bool success = true;\n\n    if(type == MfClassicType1k) {\n        config->verify_sector = 0;\n        config->keys = hi_1k_keys;\n    } else {\n        success = false;\n    }\n\n    return success;\n}\n\nstatic bool hi_verify_type(Nfc* nfc, MfClassicType type) {\n    bool verified = false;\n\n    do {\n        HiCardConfig cfg = {};\n        if(!hi_get_card_config(&cfg, type)) break;\n\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %lu\", cfg.verify_sector);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(cfg.keys[cfg.verify_sector].b, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(\n                TAG, \"Failed to read block %u: %d, this is not a HI card\", block_num, error);\n            break;\n        }\n        FURI_LOG_D(TAG, \"Found a HI Card\");\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool hi_verify(Nfc* nfc) {\n    return hi_verify_type(nfc, MfClassicType1k);\n}\n\nstatic bool hi_read(Nfc* nfc, NfcDevice* device) {\n    FURI_LOG_D(TAG, \"Entering HI KDF\");\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        HiCardConfig cfg = {};\n        if(!hi_get_card_config(&cfg, data->type)) break;\n\n        uint8_t uid[UID_LENGTH];\n        memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH);\n\n        uint8_t keyA[HI_KEY_TO_GEN][KEY_LENGTH];\n        uint8_t keyB[HI_KEY_TO_GEN][KEY_LENGTH];\n        hi_generate_key(uid, keyA, keyB);\n\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) {\n                cfg.keys[i].a = bit_lib_bytes_to_num_be(keyA[i], KEY_LENGTH);\n                cfg.keys[i].b = bit_lib_bytes_to_num_be(keyB[i], KEY_LENGTH);\n            }\n        }\n\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = mf_classic_is_card_read(data);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool hi_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        HiCardConfig cfg = {};\n        if(!hi_get_card_config(&cfg, data->type)) break;\n\n        // Verify key\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);\n        if(key != cfg.keys[cfg.verify_sector].b) return false;\n\n        //Get UID\n        uint8_t uid[UID_LENGTH];\n        memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH);\n\n        //parse data\n        furi_string_cat_printf(parsed_data, \"\\e#HI! Card\\n\");\n        furi_string_cat_printf(parsed_data, \"UID:\");\n        for(size_t i = 0; i < UID_LENGTH; i++) {\n            furi_string_cat_printf(parsed_data, \" %02X\", uid[i]);\n        }\n        furi_string_cat_printf(parsed_data, \"\\n\");\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin hi_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = hi_verify,\n    .read = hi_read,\n    .parse = hi_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor hi_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &hi_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* hi_plugin_ep(void) {\n    return &hi_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/hid.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"HID\"\n\nstatic const uint64_t hid_key = 0x484944204953;\n\nbool hid_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t verify_sector = 1;\n        uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n        MfClassicKey key = {};\n        bit_lib_num_to_bytes_be(hid_key, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool hid_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(hid_key, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(hid_key, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = true;\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic uint8_t get_bit_length(const uint8_t* half_block) {\n    uint8_t bitLength = 0;\n    uint32_t* halves = (uint32_t*)half_block;\n    if(halves[0] == 0) {\n        uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));\n        bitLength = 31 - leading0s;\n    } else {\n        uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));\n        bitLength = 63 - leading0s;\n    }\n\n    return bitLength;\n}\n\nstatic uint64_t get_pacs_bits(const uint8_t* block, uint8_t bitLength) {\n    // Remove sentinel bit from credential.  Byteswapping to handle array of bytes vs 64bit value\n    uint64_t sentinel = __builtin_bswap64(1ULL << bitLength);\n    uint64_t swapped = 0;\n    memcpy(&swapped, block, sizeof(uint64_t));\n    swapped = __builtin_bswap64(swapped ^ sentinel);\n    FURI_LOG_D(TAG, \"PACS: (%d) %016llx\", bitLength, swapped);\n    return swapped;\n}\n\nstatic bool hid_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // verify key\n        const uint8_t verify_sector = 1;\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, verify_sector);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        if(key != hid_key) break;\n\n        // Currently doesn't support bit length > 63\n        const uint8_t* credential_block = data->block[5].data + 8;\n\n        uint8_t bitLength = get_bit_length(credential_block);\n        if(bitLength == 0) break;\n\n        uint64_t credential = get_pacs_bits(credential_block, bitLength);\n        if(credential == 0) break;\n\n        furi_string_printf(parsed_data, \"\\e#HID Card\\n%dbit\\n%llx\", bitLength, credential);\n\n        parsed = true;\n\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin hid_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = hid_verify,\n    .read = hid_read,\n    .parse = hid_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor hid_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &hid_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* hid_plugin_ep(void) {\n    return &hid_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/hworld.c",
    "content": "// Flipper Zero parser for H World Hotel Key Cards\n// H World operates around 10,000 hotels, most of which in mainland China\n// Reverse engineering and parser written by @Torron (Github: @zinongli)\n#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include <bit_lib.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define TAG                    \"H World\"\n#define ROOM_SECTOR            1\n#define VIP_SECTOR             5\n#define ROOM_SECTOR_KEY_BLOCK  7\n#define VIP_SECTOR_KEY_BLOCK   23\n#define ACCESS_INFO_BLOCK      5\n#define ROOM_NUM_DECIMAL_BLOCK 6\n#define H_WORLD_YEAR_OFFSET    2000\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\nstatic MfClassicKeyPair hworld_standard_keys[] = {\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 000\n    {.a = 0x543071543071, .b = 0x5F01015F0101}, // 001\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 002\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 003\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 004\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 005\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 006\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 007\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 008\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 009\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 010\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 011\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 012\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 013\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 014\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 015\n};\n\nstatic MfClassicKeyPair hworld_vip_keys[] = {\n    {.a = 0x000000000000, .b = 0xFFFFFFFFFFFF}, // 000\n    {.a = 0x543071543071, .b = 0x5F01015F0101}, // 001\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 002\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 003\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 004\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 005\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 006\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 007\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 008\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 009\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 010\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 011\n    {.a = 0xFFFFFFFFFFFF, .b = 0x200510241234}, // 012\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 013\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 014\n    {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, // 015\n};\n\nstatic bool hworld_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(ROOM_SECTOR);\n\n        MfClassicKey standard_key = {0};\n        bit_lib_num_to_bytes_be(\n            hworld_standard_keys[ROOM_SECTOR].a, COUNT_OF(standard_key.data), standard_key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError standard_error = mf_classic_poller_sync_auth(\n            nfc, block_num, &standard_key, MfClassicKeyTypeA, &auth_context);\n\n        if(standard_error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed static key check for block %u\", block_num);\n            break;\n        }\n\n        MfClassicKey vip_key = {0};\n        bit_lib_num_to_bytes_be(\n            hworld_vip_keys[VIP_SECTOR].b, COUNT_OF(vip_key.data), vip_key.data);\n\n        MfClassicError vip_error = mf_classic_poller_sync_auth(\n            nfc, block_num, &vip_key, MfClassicKeyTypeB, &auth_context);\n\n        if(vip_error == MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"VIP card detected\");\n        } else {\n            FURI_LOG_D(TAG, \"Standard card detected\");\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool hworld_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError standard_error = mf_classic_poller_sync_detect_type(nfc, &type);\n        MfClassicError vip_error = MfClassicErrorNotPresent;\n        if(standard_error != MfClassicErrorNone) break;\n        data->type = type;\n\n        MfClassicDeviceKeys standard_keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                hworld_standard_keys[i].a, sizeof(MfClassicKey), standard_keys.key_a[i].data);\n            FURI_BIT_SET(standard_keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                hworld_standard_keys[i].b, sizeof(MfClassicKey), standard_keys.key_b[i].data);\n            FURI_BIT_SET(standard_keys.key_b_mask, i);\n        }\n\n        standard_error = mf_classic_poller_sync_read(nfc, &standard_keys, data);\n        if(standard_error == MfClassicErrorNone) {\n            FURI_LOG_I(TAG, \"Standard card successfully read\");\n        } else {\n            MfClassicDeviceKeys vip_keys = {};\n            for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n                bit_lib_num_to_bytes_be(\n                    hworld_vip_keys[i].a, sizeof(MfClassicKey), vip_keys.key_a[i].data);\n                FURI_BIT_SET(vip_keys.key_a_mask, i);\n                bit_lib_num_to_bytes_be(\n                    hworld_vip_keys[i].b, sizeof(MfClassicKey), vip_keys.key_b[i].data);\n                FURI_BIT_SET(vip_keys.key_b_mask, i);\n            }\n\n            vip_error = mf_classic_poller_sync_read(nfc, &vip_keys, data);\n\n            if(vip_error == MfClassicErrorNone) {\n                FURI_LOG_I(TAG, \"VIP card successfully read\");\n            } else {\n                break;\n            }\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = (standard_error == MfClassicErrorNone) | (vip_error == MfClassicErrorNone);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nbool hworld_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n    bool parsed = false;\n\n    do {\n        // Check card type\n        if(data->type != MfClassicType1k) break;\n\n        // Check static key for verificaiton\n        const uint8_t* data_room_sec_key_a_ptr = &data->block[ROOM_SECTOR_KEY_BLOCK].data[0];\n        const uint8_t* data_room_sec_key_b_ptr = &data->block[ROOM_SECTOR_KEY_BLOCK].data[10];\n        uint64_t data_room_sec_key_a = bit_lib_get_bits_64(data_room_sec_key_a_ptr, 0, 48);\n        uint64_t data_room_sec_key_b = bit_lib_get_bits_64(data_room_sec_key_b_ptr, 0, 48);\n        if((data_room_sec_key_a != hworld_standard_keys[ROOM_SECTOR].a) |\n           (data_room_sec_key_b != hworld_standard_keys[ROOM_SECTOR].b))\n            break;\n\n        // Check whether this card is VIP\n        const uint8_t* data_vip_sec_key_b_ptr = &data->block[VIP_SECTOR_KEY_BLOCK].data[10];\n        uint64_t data_vip_sec_key_b = bit_lib_get_bits_64(data_vip_sec_key_b_ptr, 0, 48);\n        bool is_hworld_vip = (data_vip_sec_key_b == hworld_vip_keys[VIP_SECTOR].b);\n        uint8_t room_floor = data->block[ACCESS_INFO_BLOCK].data[13];\n        uint8_t room_num = data->block[ACCESS_INFO_BLOCK].data[14];\n\n        // Check in date & time\n        uint16_t check_in_year = data->block[ACCESS_INFO_BLOCK].data[2] + H_WORLD_YEAR_OFFSET;\n        uint8_t check_in_month = data->block[ACCESS_INFO_BLOCK].data[3];\n        uint8_t check_in_day = data->block[ACCESS_INFO_BLOCK].data[4];\n        uint8_t check_in_hour = data->block[ACCESS_INFO_BLOCK].data[5];\n        uint8_t check_in_minute = data->block[ACCESS_INFO_BLOCK].data[6];\n\n        // Expire date & time\n        uint16_t expire_year = data->block[ACCESS_INFO_BLOCK].data[7] + H_WORLD_YEAR_OFFSET;\n        uint8_t expire_month = data->block[ACCESS_INFO_BLOCK].data[8];\n        uint8_t expire_day = data->block[ACCESS_INFO_BLOCK].data[9];\n        uint8_t expire_hour = data->block[ACCESS_INFO_BLOCK].data[10];\n        uint8_t expire_minute = data->block[ACCESS_INFO_BLOCK].data[11];\n\n        furi_string_cat_printf(parsed_data, \"\\e#H World Card\\n\");\n        furi_string_cat_printf(\n            parsed_data, \"%s\\n\", is_hworld_vip ? \"VIP card\" : \"Standard room key\");\n        furi_string_cat_printf(parsed_data, \"Room Num: %u%02u\\n\", room_floor, room_num);\n        furi_string_cat_printf(\n            parsed_data,\n            \"Check-in Date: \\n%04u-%02d-%02d\\n%02d:%02d:00\\n\",\n            check_in_year,\n            check_in_month,\n            check_in_day,\n            check_in_hour,\n            check_in_minute);\n        furi_string_cat_printf(\n            parsed_data,\n            \"Expiration Date: \\n%04u-%02d-%02d\\n%02d:%02d:00\",\n            expire_year,\n            expire_month,\n            expire_day,\n            expire_hour,\n            expire_minute);\n        parsed = true;\n    } while(false);\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin hworld_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = hworld_verify,\n    .read = hworld_read,\n    .parse = hworld_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor hworld_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &hworld_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* hworld_plugin_ep(void) {\n    return &hworld_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/itso.c",
    "content": "/* itso.c - Parser for ITSO cards (United Kingdom). */\n#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <lib/nfc/protocols/mf_desfire/mf_desfire.h>\n#include <lib/toolbox/strint.h>\n\n#include <applications/services/locale/locale.h>\n#include <datetime/datetime.h>\n\nstatic const MfDesfireApplicationId itso_app_id = {.data = {0x16, 0x02, 0xa0}};\nstatic const MfDesfireFileId itso_file_id = 0x0f;\n\nint64_t swap_int64(int64_t val) {\n    val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);\n    val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);\n    return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);\n}\n\nuint64_t swap_uint64(uint64_t val) {\n    val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);\n    val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);\n    return (val << 32) | (val >> 32);\n}\n\nstatic bool itso_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    bool parsed = false;\n\n    do {\n        const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n        const MfDesfireApplication* app = mf_desfire_get_application(data, &itso_app_id);\n        if(app == NULL) break;\n\n        typedef struct {\n            uint64_t part1;\n            uint64_t part2;\n            uint64_t part3;\n            uint64_t part4;\n        } ItsoFile;\n\n        const MfDesfireFileSettings* file_settings =\n            mf_desfire_get_file_settings(app, &itso_file_id);\n\n        if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard ||\n           file_settings->data.size < sizeof(ItsoFile))\n            break;\n\n        const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &itso_file_id);\n        if(file_data == NULL) break;\n\n        const ItsoFile* itso_file = simple_array_cget_data(file_data->data);\n\n        uint64_t x1 = swap_uint64(itso_file->part1);\n        uint64_t x2 = swap_uint64(itso_file->part2);\n\n        char cardBuff[32];\n        char dateBuff[18];\n\n        snprintf(cardBuff, sizeof(cardBuff), \"%llx%llx\", x1, x2);\n        snprintf(dateBuff, sizeof(dateBuff), \"%llx\", x2);\n\n        char* cardp = cardBuff + 4;\n        cardp[18] = '\\0';\n\n        // All itso card numbers are prefixed with \"633597\"\n        if(strncmp(cardp, \"633597\", 6) != 0) break;\n\n        char* datep = dateBuff + 12;\n        dateBuff[17] = '\\0';\n\n        // DateStamp is defined in BS EN 1545 - Days passed since 01/01/1997\n        uint32_t dateStamp;\n        if(strint_to_uint32(datep, NULL, &dateStamp, 16) != StrintParseNoError) {\n            return false;\n        }\n        uint32_t unixTimestamp = dateStamp * 24 * 60 * 60 + 852076800U;\n\n        furi_string_set(parsed_data, \"\\e#ITSO Card\\n\");\n\n        // Digit count in each space-separated group\n        static const uint8_t digit_count[] = {6, 4, 4, 4};\n\n        for(uint32_t i = 0, k = 0; i < COUNT_OF(digit_count); k += digit_count[i++]) {\n            for(uint32_t j = 0; j < digit_count[i]; ++j) {\n                furi_string_push_back(parsed_data, cardp[j + k]);\n            }\n            furi_string_push_back(parsed_data, ' ');\n        }\n\n        DateTime timestamp = {0};\n        datetime_timestamp_to_datetime(unixTimestamp, &timestamp);\n        FuriString* timestamp_str = furi_string_alloc();\n        locale_format_date(timestamp_str, &timestamp, locale_get_date_format(), \"-\");\n\n        furi_string_cat(parsed_data, \"\\nExpiry: \");\n        furi_string_cat(parsed_data, timestamp_str);\n\n        furi_string_free(timestamp_str);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin itso_plugin = {\n    .protocol = NfcProtocolMfDesfire,\n    .verify = NULL,\n    .read = NULL,\n    .parse = itso_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor itso_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &itso_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* itso_plugin_ep(void) {\n    return &itso_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/microel.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application/flipper_application.h>\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"Microel\"\n\n#define KEY_LENGTH 6\n#define UID_LENGTH 4\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\nstatic MfClassicKeyPair microel_1k_keys[] = {\n    {.a = 0x000000000000, .b = 0x000000000000}, // 000\n    {.a = 0x000000000000, .b = 0x000000000000}, // 001\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 004\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 005\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 006\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 007\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 008\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 009\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 010\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 011\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 012\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 013\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 014\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 015\n};\n\nconst uint8_t verify_sector = 1;\n\nvoid calculateSumHex(const uint8_t* uid, size_t uidSize, uint8_t sumHex[]) {\n    const uint8_t xorKey[] = {0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9};\n    int sum = 0;\n\n    for(size_t i = 0; i < uidSize; i++) {\n        sum += uid[i];\n    }\n\n    int sumTwoDigits = sum % 256;\n\n    if(sumTwoDigits % 2 == 1) {\n        sumTwoDigits += 2;\n    }\n\n    for(size_t i = 0; i < sizeof(xorKey); i++) {\n        sumHex[i] = sumTwoDigits ^ xorKey[i];\n    }\n}\n\nvoid generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) {\n    uint8_t sumHex[6];\n    calculateSumHex(uid, uidSize, sumHex);\n    uint8_t firstCharacter = (sumHex[0] >> 4) & 0xF;\n\n    if(firstCharacter == 0x2 || firstCharacter == 0x3 || firstCharacter == 0xA ||\n       firstCharacter == 0xB) {\n        // XOR WITH 0x40\n        for(size_t i = 0; i < sizeof(sumHex); i++) {\n            keyA[i] = 0x40 ^ sumHex[i];\n        }\n    } else if(\n        firstCharacter == 0x6 || firstCharacter == 0x7 || firstCharacter == 0xE ||\n        firstCharacter == 0xF) {\n        // XOR WITH 0xC0\n        for(size_t i = 0; i < sizeof(sumHex); i++) {\n            keyA[i] = 0xC0 ^ sumHex[i];\n        }\n    } else {\n        //Key a is the same as sumHex\n        for(size_t i = 0; i < sizeof(sumHex); i++) {\n            keyA[i] = sumHex[i];\n        }\n    }\n}\n\nvoid generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) {\n    for(size_t i = 0; i < keyASize; i++) {\n        keyB[i] = 0xFF ^ keyA[i];\n    }\n}\n\nstatic bool microel_read(Nfc* nfc, NfcDevice* device) {\n    FURI_LOG_D(TAG, \"Entering Microel KDF\");\n\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        //Get UID and check if it is 4 bytes\n        size_t uid_len;\n        const uint8_t* uid = mf_classic_get_uid(data, &uid_len);\n        FURI_LOG_D(TAG, \"UID identified: %02X%02X%02X%02X\", uid[0], uid[1], uid[2], uid[3]);\n        if(uid_len != UID_LENGTH) break;\n\n        // Generate keys\n        uint8_t keyA[KEY_LENGTH];\n        uint8_t keyB[KEY_LENGTH];\n        generateKeyA(uid, UID_LENGTH, keyA);\n        generateKeyB(keyA, KEY_LENGTH, keyB);\n\n        // Check key 0a to verify if it is a microel card\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(\n            bit_lib_bytes_to_num_be(keyA, KEY_LENGTH), COUNT_OF(key.data), key.data);\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(0); // This is 0\n        MfClassicAuthContext auth_context;\n        error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);\n        if(error != MfClassicErrorNone) {\n            break;\n        }\n\n        // Save keys generated to stucture\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            if(microel_1k_keys[i].a == 0x000000000000) {\n                microel_1k_keys[i].a = bit_lib_bytes_to_num_be(keyA, KEY_LENGTH);\n            }\n            if(microel_1k_keys[i].b == 0x000000000000) {\n                microel_1k_keys[i].b = bit_lib_bytes_to_num_be(keyB, KEY_LENGTH);\n            }\n        }\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                microel_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = mf_classic_is_card_read(data);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool microel_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        //Get UID\n        size_t uid_len;\n        const uint8_t* uid = mf_classic_get_uid(data, &uid_len);\n        if(uid_len != UID_LENGTH) break;\n\n        // Generate key from uid\n        uint8_t keyA[KEY_LENGTH];\n        generateKeyA(uid, UID_LENGTH, keyA);\n\n        // Verify key\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, verify_sector);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        uint64_t key_for_check_from_array = bit_lib_bytes_to_num_be(keyA, KEY_LENGTH);\n        if(key != key_for_check_from_array) break;\n\n        //Get credit in block number 8\n        const uint8_t* temp_ptr = data->block[4].data;\n        uint16_t balance = (temp_ptr[6] << 8) | (temp_ptr[5]);\n        uint16_t previus_balance = (data->block[5].data[6] << 8) | (data->block[5].data[5]);\n        furi_string_cat_printf(parsed_data, \"\\e#Microel Card\\n\");\n        furi_string_cat_printf(parsed_data, \"UID:\");\n        for(size_t i = 0; i < UID_LENGTH; i++) {\n            furi_string_cat_printf(parsed_data, \" %02X\", uid[i]);\n        }\n        furi_string_cat_printf(\n            parsed_data, \"\\nCurrent Credit: %d.%02d E \\n\", balance / 100, balance % 100);\n        furi_string_cat_printf(\n            parsed_data,\n            \"Previus Credit: %d.%02d E \\n\",\n            previus_balance / 100,\n            previus_balance % 100);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin microel_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify =\n        NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 but added verify in read function\n    .read = microel_read,\n    .parse = microel_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor microel_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &microel_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* microel_plugin_ep(void) {\n    return &microel_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/mizip.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application/flipper_application.h>\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"MiZIP\"\n\n#define KEY_LENGTH       6\n#define MIZIP_KEY_TO_GEN 5\n#define UID_LENGTH       4\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\ntypedef struct {\n    MfClassicKeyPair* keys;\n    uint32_t verify_sector;\n} MizipCardConfig;\n\nstatic MfClassicKeyPair mizip_1k_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000\n    {.a = 0x000000000000, .b = 0x000000000000}, // 001\n    {.a = 0x000000000000, .b = 0x000000000000}, // 002\n    {.a = 0x000000000000, .b = 0x000000000000}, // 003\n    {.a = 0x000000000000, .b = 0x000000000000}, // 004\n    {.a = 0x0222179AB995, .b = 0x13321774F9B5}, // 005\n    {.a = 0xB25CBD76A7B4, .b = 0x7571359B4274}, // 006\n    {.a = 0xDA857B4907CC, .b = 0xD26B856175F7}, // 007\n    {.a = 0x16D85830C443, .b = 0x8F790871A21E}, // 008\n    {.a = 0x88BD5098FC82, .b = 0xFCD0D77745E4}, // 009\n    {.a = 0x983349449D78, .b = 0xEA2631FBDEDD}, // 010\n    {.a = 0xC599F962F3D9, .b = 0x949B70C14845}, // 011\n    {.a = 0x72E668846BE8, .b = 0x45490B5AD707}, // 012\n    {.a = 0xBCA105E5685E, .b = 0x248DAF9D674D}, // 013\n    {.a = 0x4F6FE072D1FD, .b = 0x4250A05575FA}, // 014\n    {.a = 0x56438ABE8152, .b = 0x59A45912B311}, // 015\n};\n\nstatic MfClassicKeyPair mizip_mini_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000\n    {.a = 0x000000000000, .b = 0x000000000000}, // 001\n    {.a = 0x000000000000, .b = 0x000000000000}, // 002\n    {.a = 0x000000000000, .b = 0x000000000000}, // 003\n    {.a = 0x000000000000, .b = 0x000000000000}, // 004\n};\n\n//KDF\nvoid mizip_generate_key(uint8_t* uid, uint8_t keyA[5][KEY_LENGTH], uint8_t keyB[5][KEY_LENGTH]) {\n    // Static XOR table for key generation\n    static const uint8_t xor_table_keyA[4][6] = {\n        {0x09, 0x12, 0x5A, 0x25, 0x89, 0xE5},\n        {0xAB, 0x75, 0xC9, 0x37, 0x92, 0x2F},\n        {0xE2, 0x72, 0x41, 0xAF, 0x2C, 0x09},\n        {0x31, 0x7A, 0xB7, 0x2F, 0x44, 0x90}};\n\n    static const uint8_t xor_table_keyB[4][6] = {\n        {0xF1, 0x2C, 0x84, 0x53, 0xD8, 0x21},\n        {0x73, 0xE7, 0x99, 0xFE, 0x32, 0x41},\n        {0xAA, 0x4D, 0x13, 0x76, 0x56, 0xAE},\n        {0xB0, 0x13, 0x27, 0x27, 0x2D, 0xFD}};\n\n    // Permutation table for rearranging elements in uid\n    static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 1};\n    static const uint8_t xorOrderB[6] = {2, 3, 0, 1, 2, 3};\n\n    // Generate key based on uid and XOR table\n    for(uint8_t j = 1; j < 5; j++) {\n        for(uint8_t i = 0; i < 6; i++) {\n            keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i];\n            keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i];\n        }\n    }\n}\n\nstatic bool mizip_get_card_config(MizipCardConfig* config, MfClassicType type) {\n    bool success = true;\n\n    if(type == MfClassicType1k) {\n        config->verify_sector = 0;\n        config->keys = mizip_1k_keys;\n    } else if(type == MfClassicTypeMini) {\n        config->verify_sector = 0;\n        config->keys = mizip_mini_keys;\n    } else {\n        success = false;\n    }\n\n    return success;\n}\n\nstatic bool mizip_verify_type(Nfc* nfc, MfClassicType type) {\n    bool verified = false;\n\n    do {\n        MizipCardConfig cfg = {};\n        if(!mizip_get_card_config(&cfg, type)) break;\n\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %lu\", cfg.verify_sector);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(cfg.keys[cfg.verify_sector].b, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(\n                TAG, \"Failed to read block %u: %d, this is not a MiZIP card\", block_num, error);\n            break;\n        }\n        FURI_LOG_D(TAG, \"Found a MiZIP Card\");\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool mizip_verify(Nfc* nfc) {\n    return mizip_verify_type(nfc, MfClassicType1k) || mizip_verify_type(nfc, MfClassicTypeMini);\n}\n\nstatic bool mizip_read(Nfc* nfc, NfcDevice* device) {\n    FURI_LOG_D(TAG, \"Entering MiZIP KDF\");\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        //temp fix but fix mf_classic_poller_sync_detect_type because view type mfclassic1k and not verify mfmini\n        data->type = MfClassicTypeMini;\n        MizipCardConfig cfg = {};\n        if(!mizip_get_card_config(&cfg, data->type)) break;\n\n        uint8_t uid[UID_LENGTH];\n        memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH);\n\n        uint8_t keyA[MIZIP_KEY_TO_GEN][KEY_LENGTH];\n        uint8_t keyB[MIZIP_KEY_TO_GEN][KEY_LENGTH];\n        mizip_generate_key(uid, keyA, keyB);\n\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) {\n                cfg.keys[i].a = bit_lib_bytes_to_num_be(keyA[i], KEY_LENGTH);\n                cfg.keys[i].b = bit_lib_bytes_to_num_be(keyB[i], KEY_LENGTH);\n            }\n        }\n\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = mf_classic_is_card_read(data);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        MizipCardConfig cfg = {};\n        if(!mizip_get_card_config(&cfg, data->type)) break;\n\n        // Verify key\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);\n        if(key != cfg.keys[cfg.verify_sector].b) return false;\n\n        //Get UID\n        uint8_t uid[UID_LENGTH];\n        memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH);\n\n        //Get credit\n        uint8_t credit_pointer = 0x08;\n        uint8_t previus_credit_pointer = 0x09;\n        if(data->block[10].data[0] == 0x55) {\n            credit_pointer = 0x09;\n            previus_credit_pointer = 0x08;\n        }\n        uint16_t balance = (data->block[credit_pointer].data[2] << 8) |\n                           (data->block[credit_pointer].data[1]);\n        uint16_t previus_balance = (data->block[previus_credit_pointer].data[2] << 8) |\n                                   (data->block[previus_credit_pointer].data[1]);\n\n        //parse data\n        furi_string_cat_printf(parsed_data, \"\\e#MiZIP Card\\n\");\n        furi_string_cat_printf(parsed_data, \"UID:\");\n        for(size_t i = 0; i < UID_LENGTH; i++) {\n            furi_string_cat_printf(parsed_data, \" %02X\", uid[i]);\n        }\n        furi_string_cat_printf(\n            parsed_data, \"\\nCurrent Credit: %d.%02d E \\n\", balance / 100, balance % 100);\n        furi_string_cat_printf(\n            parsed_data,\n            \"Previus Credit: %d.%02d E \\n\",\n            previus_balance / 100,\n            previus_balance % 100);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin mizip_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = mizip_verify,\n    .read = mizip_read,\n    .parse = mizip_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor mizip_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &mizip_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* mizip_plugin_ep(void) {\n    return &mizip_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/mykey.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <machine/endian.h>\n#include <nfc/protocols/st25tb/st25tb.h>\n\n#define TAG \"MyKey\"\n\nconst uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0;\n\nstatic bool mykey_is_blank(const St25tbData* data) {\n    return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19;\n}\n\nstatic bool mykey_has_lockid(const St25tbData* data) {\n    return (data->blocks[5] >> 24) == 0x7F;\n}\n\nstatic bool check_invalid_low_nibble(uint8_t value) {\n    uint8_t value_lo = value & 0xF;\n    return value_lo >= 0xA;\n}\n\nstatic bool mykey_get_production_date(\n    const St25tbData* data,\n    uint16_t* year_ptr,\n    uint8_t* month_ptr,\n    uint8_t* day_ptr) {\n    uint32_t date_block = data->blocks[8];\n    uint8_t year = date_block >> 16 & 0xFF;\n    uint8_t month = date_block >> 8 & 0xFF;\n    uint8_t day = date_block & 0xFF;\n    // dates are coded in a peculiar way, the hexadecimal value should in fact be interpreted as a decimal value\n    // so anything in range A-F is invalid.\n    if(day > 0x31 || month > 0x12 || day == 0 || month == 0 || year == 0) {\n        return false;\n    }\n    if(check_invalid_low_nibble(day) || check_invalid_low_nibble(month) ||\n       check_invalid_low_nibble(year) || check_invalid_low_nibble(year >> 4)) {\n        return false;\n    }\n    *year_ptr = year + 0x2000;\n    *month_ptr = month;\n    *day_ptr = day;\n    return true;\n}\n\nstatic bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);\n\n    if(data->type != St25tbType04k && data->type != St25tbTypeX4k) {\n        FURI_LOG_D(TAG, \"bad type\");\n        return false;\n    }\n\n    for(int i = 0; i < 5; i++) {\n        if(data->blocks[i] != 0xFFFFFFFF) {\n            FURI_LOG_D(TAG, \"bad otp block %d\", i);\n            return false;\n        }\n    }\n\n    uint16_t mfg_year;\n    uint8_t mfg_month, mfg_day;\n\n    if(!mykey_get_production_date(data, &mfg_year, &mfg_month, &mfg_day)) {\n        FURI_LOG_D(TAG, \"bad mfg date\");\n        return false;\n    }\n\n    if(data->system_otp_block != 0xFEFFFFFF) {\n        FURI_LOG_D(TAG, \"bad sys otp block\");\n        return false;\n    }\n\n    furi_string_cat(parsed_data, \"\\e#MyKey\\n\");\n\n    if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader\n        furi_string_cat(parsed_data, \"\\e#Bricked!\\nBlock 6 is 0!\");\n        return true;\n    }\n\n    bool is_blank = mykey_is_blank(data);\n    furi_string_cat_printf(parsed_data, \"Serial#: %08lX\\n\", (uint32_t)__bswap32(data->blocks[7]));\n    furi_string_cat_printf(\n        parsed_data, \"Prod. date: %02X/%02X/%04X\\n\", mfg_day, mfg_month, mfg_year);\n    furi_string_cat_printf(parsed_data, \"Blank: %s\\n\", is_blank ? \"yes\" : \"no\");\n    furi_string_cat_printf(parsed_data, \"LockID: %s\", mykey_has_lockid(data) ? \"maybe\" : \"no\");\n\n    if(!is_blank) {\n        furi_string_cat_printf(\n            parsed_data, \"\\nOp. count: %zu\\n\", (size_t)__bswap32(data->blocks[0x12] & 0xFFFFFF00));\n\n        uint32_t block3C = data->blocks[0x3C];\n        if(block3C == 0xFFFFFFFF) {\n            furi_string_cat(parsed_data, \"No history available!\");\n        } else {\n            block3C ^= data->blocks[0x07];\n            uint32_t startingOffset = ((block3C & 0x30000000) >> 28) |\n                                      ((block3C & 0x00100000) >> 18);\n            furi_check(startingOffset < 8); //-V547\n            for(int txnOffset = 8; txnOffset > 0; txnOffset--) {\n                uint32_t txnBlock =\n                    __bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]);\n\n                if(txnBlock == 0xFFFFFFFF) {\n                    break;\n                }\n\n                uint8_t day = txnBlock >> 27;\n                uint8_t month = txnBlock >> 23 & 0xF;\n                uint16_t year = 2000 + (txnBlock >> 16 & 0x7F);\n                uint16_t credit = txnBlock & 0xFFFF;\n\n                if(txnOffset == 8) {\n                    furi_string_cat_printf(\n                        parsed_data, \"Current credit: %d.%02d euros\\n\", credit / 100, credit % 100);\n                    furi_string_cat(parsed_data, \"Op. history (newest first):\");\n                }\n\n                furi_string_cat_printf(\n                    parsed_data,\n                    \"\\n    %02d/%02d/%04d %d.%02d\",\n                    day,\n                    month,\n                    year,\n                    credit / 100,\n                    credit % 100);\n            }\n        }\n    }\n    return true;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin mykey_plugin = {\n    .protocol = NfcProtocolSt25tb,\n    .verify = NULL,\n    .read = NULL,\n    .parse = mykey_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor mykey_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &mykey_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* mykey_plugin_ep(void) {\n    return &mykey_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/myki.c",
    "content": "/* myki.c - Parser for myki cards (Melbourne, Australia).\n *\n * Based on the code by Emily Trau (https://github.com/emilytrau)\n * Original pull request URL: https://github.com/flipperdevices/flipperzero-firmware/pull/2326\n * Reference: https://github.com/metrodroid/metrodroid/wiki/Myki\n */\n#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <lib/nfc/protocols/mf_desfire/mf_desfire.h>\n\nstatic const MfDesfireApplicationId myki_app_id = {.data = {0x00, 0x11, 0xf2}};\nstatic const MfDesfireFileId myki_file_id = 0x0f;\n\nstatic uint8_t myki_calculate_luhn(uint64_t number) {\n    // https://en.wikipedia.org/wiki/Luhn_algorithm\n    // Drop existing check digit to form payload\n    uint64_t payload = number / 10;\n    int sum = 0;\n    int position = 0;\n\n    while(payload > 0) {\n        int digit = payload % 10;\n        if(position % 2 == 0) {\n            digit *= 2;\n        }\n        if(digit > 9) {\n            digit = (digit / 10) + (digit % 10);\n        }\n        sum += digit;\n        payload /= 10;\n        position++;\n    }\n\n    return (10 - (sum % 10)) % 10;\n}\n\nstatic bool myki_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    bool parsed = false;\n\n    do {\n        const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n        const MfDesfireApplication* app = mf_desfire_get_application(data, &myki_app_id);\n        if(app == NULL) break;\n\n        typedef struct {\n            uint32_t top;\n            uint32_t bottom;\n        } MykiFile;\n\n        const MfDesfireFileSettings* file_settings =\n            mf_desfire_get_file_settings(app, &myki_file_id);\n\n        if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard ||\n           file_settings->data.size < sizeof(MykiFile))\n            break;\n\n        const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &myki_file_id);\n        if(file_data == NULL) break;\n\n        const MykiFile* myki_file = simple_array_cget_data(file_data->data);\n\n        // All myki card numbers are prefixed with \"308425\"\n        if(myki_file->top != 308425UL) break;\n        // Card numbers are always 15 digits in length\n        if(myki_file->bottom < 10000000UL || myki_file->bottom >= 100000000UL) break;\n\n        uint64_t card_number = myki_file->top * 1000000000ULL + myki_file->bottom * 10UL;\n        // Stored card number doesn't include check digit\n        card_number += myki_calculate_luhn(card_number);\n\n        furi_string_set(parsed_data, \"\\e#myki\\nNo.: \");\n\n        // Stylise card number according to the physical card\n        char card_string[20];\n        snprintf(card_string, sizeof(card_string), \"%llu\", card_number);\n\n        // Digit count in each space-separated group\n        static const uint8_t digit_count[] = {1, 5, 4, 4, 1};\n\n        for(uint32_t i = 0, k = 0; i < COUNT_OF(digit_count); k += digit_count[i++]) {\n            for(uint32_t j = 0; j < digit_count[i]; ++j) {\n                furi_string_push_back(parsed_data, card_string[j + k]);\n            }\n            furi_string_push_back(parsed_data, ' ');\n        }\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin myki_plugin = {\n    .protocol = NfcProtocolMfDesfire,\n    .verify = NULL,\n    .read = NULL,\n    .parse = myki_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor myki_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &myki_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* myki_plugin_ep(void) {\n    return &myki_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/ndef.c",
    "content": "// Parser for NDEF format data\n// Supports multiple NDEF messages and records in same tag\n// Parsed types: URI (+ Phone, Mail), Text, BT MAC, Contact, WiFi, Empty, SmartPoster\n// Documentation and sources indicated where relevant\n// Made by @Willy-JL\n// Mifare Ultralight support by @Willy-JL\n// Mifare Classic support by @luu176 & @Willy-JL\n// SLIX support by @Willy-JL\n\n// We use an arbitrary position system here, in order to support more protocols.\n// Each protocol parses basic structure of the card, then starts ndef_parse_tlv()\n// using an arbitrary position value that it can understand. When accessing data\n// to parse NDEF content, ndef_get() will then map this arbitrary value to the\n// card using state in Ndef struct, skipping blocks or sectors as needed. This\n// way, NDEF parsing code does not need to know details of card layout.\n\n#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application.h>\n\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <nfc/protocols/slix/slix.h>\n\n#include <bit_lib.h>\n#include <toolbox/pretty_format.h>\n\n#define TAG \"NDEF\"\n\n#define NDEF_PROTO_INVALID (-1)\n#define NDEF_PROTO_RAW     (0) // For parsing data fed manually\n#define NDEF_PROTO_UL      (1)\n#define NDEF_PROTO_MFC     (2)\n#define NDEF_PROTO_SLIX    (3)\n#define NDEF_PROTO_TOTAL   (4)\n\n#ifndef NDEF_PROTO\n#error Must specify what protocol to use with NDEF_PROTO define!\n#endif\n#if NDEF_PROTO <= NDEF_PROTO_INVALID || NDEF_PROTO >= NDEF_PROTO_TOTAL\n#error Invalid NDEF_PROTO specified!\n#endif\n\n#define NDEF_TITLE(device, parsed_data)         \\\n    furi_string_printf(                         \\\n        parsed_data,                            \\\n        \"\\e#NDEF Format Data\\nCard type: %s\\n\", \\\n        nfc_device_get_name(device, NfcDeviceNameTypeFull))\n\n// ---=== structures ===---\n\n// TLV structure:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nfc/doc/type_2_tag.html#data\ntypedef enum FURI_PACKED {\n    NdefTlvPadding = 0x00,\n    NdefTlvLockControl = 0x01,\n    NdefTlvMemoryControl = 0x02,\n    NdefTlvNdefMessage = 0x03,\n    NdefTlvProprietary = 0xFD,\n    NdefTlvTerminator = 0xFE,\n} NdefTlv;\n\n// Type Name Format values:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/nfc/index.html#flags-and-tnf\ntypedef enum FURI_PACKED {\n    NdefTnfEmpty = 0x00,\n    NdefTnfWellKnownType = 0x01,\n    NdefTnfMediaType = 0x02,\n    NdefTnfAbsoluteUri = 0x03,\n    NdefTnfExternalType = 0x04,\n    NdefTnfUnknown = 0x05,\n    NdefTnfUnchanged = 0x06,\n    NdefTnfReserved = 0x07,\n} NdefTnf;\n\n// Flags and TNF structure:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/nfc/index.html#flags-and-tnf\ntypedef struct FURI_PACKED {\n    // Reversed due to endianness\n    NdefTnf type_name_format : 3;\n    bool id_length_present   : 1;\n    bool short_record        : 1;\n    bool chunk_flag          : 1;\n    bool message_end         : 1;\n    bool message_begin       : 1;\n} NdefFlagsTnf;\n_Static_assert(sizeof(NdefFlagsTnf) == 1);\n\n// URI payload format:\n// https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763\nstatic const char* ndef_uri_prepends[] = {\n    [0x00] = NULL, // Allows detecting no prepend and checking schema for type\n    [0x01] = \"http://www.\",\n    [0x02] = \"https://www.\",\n    [0x03] = \"http://\",\n    [0x04] = \"https://\",\n    [0x05] = \"tel:\",\n    [0x06] = \"mailto:\",\n    [0x07] = \"ftp://anonymous:anonymous@\",\n    [0x08] = \"ftp://ftp.\",\n    [0x09] = \"ftps://\",\n    [0x0A] = \"sftp://\",\n    [0x0B] = \"smb://\",\n    [0x0C] = \"nfs://\",\n    [0x0D] = \"ftp://\",\n    [0x0E] = \"dav://\",\n    [0x0F] = \"news:\",\n    [0x10] = \"telnet://\",\n    [0x11] = \"imap:\",\n    [0x12] = \"rtsp://\",\n    [0x13] = \"urn:\",\n    [0x14] = \"pop:\",\n    [0x15] = \"sip:\",\n    [0x16] = \"sips:\",\n    [0x17] = \"tftp:\",\n    [0x18] = \"btspp://\",\n    [0x19] = \"btl2cap://\",\n    [0x1A] = \"btgoep://\",\n    [0x1B] = \"tcpobex://\",\n    [0x1C] = \"irdaobex://\",\n    [0x1D] = \"file://\",\n    [0x1E] = \"urn:epc:id:\",\n    [0x1F] = \"urn:epc:tag:\",\n    [0x20] = \"urn:epc:pat:\",\n    [0x21] = \"urn:epc:raw:\",\n    [0x22] = \"urn:epc:\",\n    [0x23] = \"urn:nfc:\",\n};\n\n// ---=== card memory layout abstraction ===---\n\n// Shared context and state, read above\ntypedef struct {\n    FuriString* output;\n#if NDEF_PROTO == NDEF_PROTO_RAW\n    struct {\n        const uint8_t* data;\n        size_t size;\n    } raw;\n#elif NDEF_PROTO == NDEF_PROTO_UL\n    struct {\n        const uint8_t* start;\n        size_t size;\n    } ul;\n#elif NDEF_PROTO == NDEF_PROTO_MFC\n    struct {\n        const MfClassicBlock* blocks;\n        size_t size;\n    } mfc;\n#elif NDEF_PROTO == NDEF_PROTO_SLIX\n    struct {\n        const uint8_t* start;\n        size_t size;\n    } slix;\n#endif\n} Ndef;\n\nstatic bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) {\n#if NDEF_PROTO == NDEF_PROTO_RAW\n\n    // Using user-provided pointer, simply need to remap to it\n    if(pos + len > ndef->raw.size) return false;\n    memcpy(buf, ndef->raw.data + pos, len);\n    return true;\n\n#elif NDEF_PROTO == NDEF_PROTO_UL\n\n    // Memory space is contiguous, simply need to remap to data pointer\n    if(pos + len > ndef->ul.size) return false;\n    memcpy(buf, ndef->ul.start + pos, len);\n    return true;\n\n#elif NDEF_PROTO == NDEF_PROTO_MFC\n\n    // We need to skip sector trailers and MAD2, NDEF parsing just uses\n    // a position offset in data space, as if it were contiguous.\n\n    // Start with a simple data space size check\n    if(pos + len > ndef->mfc.size) return false;\n\n    // First 128 blocks are 32 sectors: 3 data blocks, 1 sector trailer.\n    // Sector 16 contains MAD2 and we need to skip this.\n    // So the first 93 (31*3) data blocks correspond to 128 real blocks.\n    // Last 128 blocks are 8 sectors: 15 data blocks, 1 sector trailer.\n    // So the last 120 (8*15) data blocks correspond to 128 real blocks.\n    const size_t real_block_data_offset = pos % MF_CLASSIC_BLOCK_SIZE;\n    size_t small_sector_data_blocks = pos / MF_CLASSIC_BLOCK_SIZE;\n    size_t large_sector_data_blocks = 0;\n    if(small_sector_data_blocks > 93) {\n        large_sector_data_blocks = small_sector_data_blocks - 93;\n        small_sector_data_blocks = 93;\n    }\n\n    const size_t small_sector_block_offset = small_sector_data_blocks % 3;\n    const size_t small_sectors = small_sector_data_blocks / 3;\n    size_t real_block = small_sectors * 4 + small_sector_block_offset;\n    if(small_sectors >= 16) {\n        real_block += 4; // Skip MAD2\n    }\n    if(large_sector_data_blocks) {\n        const size_t large_sector_block_offset = large_sector_data_blocks % 15;\n        const size_t large_sectors = large_sector_data_blocks / 15;\n        real_block += large_sectors * 16 + large_sector_block_offset;\n    }\n\n    const uint8_t* cur = &ndef->mfc.blocks[real_block].data[real_block_data_offset];\n    while(len) {\n        size_t sector_trailer = mf_classic_get_sector_trailer_num_by_block(real_block);\n        const uint8_t* end = &ndef->mfc.blocks[sector_trailer].data[0];\n\n        const size_t chunk_len = MIN((size_t)(end - cur), len);\n        memcpy(buf, cur, chunk_len);\n        buf += chunk_len;\n        len -= chunk_len;\n\n        if(len) {\n            real_block = sector_trailer + 1;\n            if(real_block == 64) {\n                real_block += 4; // Skip MAD2\n            }\n            cur = &ndef->mfc.blocks[real_block].data[0];\n        }\n    }\n\n    return true;\n\n#elif NDEF_PROTO == NDEF_PROTO_SLIX\n\n    // Memory space is contiguous, simply need to remap to data pointer\n    if(pos + len > ndef->slix.size) return false;\n    memcpy(buf, ndef->slix.start + pos, len);\n    return true;\n\n#else\n\n    UNUSED(ndef);\n    UNUSED(pos);\n    UNUSED(len);\n    UNUSED(buf);\n    return false;\n\n#endif\n}\n\n// ---=== output helpers ===---\n\nstatic inline bool is_printable(char c) {\n    return (c >= ' ' && c <= '~') || c == '\\r' || c == '\\n';\n}\n\nstatic bool is_text(const uint8_t* buf, size_t len) {\n    for(size_t i = 0; i < len; i++) {\n        if(!is_printable(buf[i]) && !(buf[i] == '\\0' && i == len - 1)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool ndef_dump(Ndef* ndef, const char* prefix, size_t pos, size_t len, bool force_hex) {\n    if(prefix) furi_string_cat_printf(ndef->output, \"%s: \", prefix);\n    // We don't have direct access to memory chunks due to different card layouts\n    // Making a temporary buffer is wasteful of RAM and we can't afford this\n    // So while iterating like this is inefficient, it saves RAM and works between multiple card types\n    if(!force_hex) {\n        // If we find a non-printable character along the way, reset string to prev state and re-do as hex\n        size_t string_prev = furi_string_size(ndef->output);\n        for(size_t i = 0; i < len; i++) {\n            char c;\n            if(!ndef_get(ndef, pos + i, 1, &c)) return false;\n            if(!is_printable(c) && !(c == '\\0' && i == len - 1)) {\n                furi_string_left(ndef->output, string_prev);\n                force_hex = true;\n                break;\n            }\n            furi_string_push_back(ndef->output, c);\n        }\n    }\n    if(!force_hex) {\n        furi_string_cat(ndef->output, \"\\n\");\n    } else {\n        uint8_t buf[4];\n        for(size_t i = 0; i < len; i += sizeof(buf)) {\n            uint8_t buf_len = MIN(sizeof(buf), len - i);\n            if(!ndef_get(ndef, pos + i, buf_len, &buf)) return false;\n            pretty_format_bytes_hex_canonical(\n                ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, buf_len);\n            furi_string_cat(ndef->output, \"\\n\");\n        }\n    }\n    return true;\n}\n\nstatic void\n    ndef_print(Ndef* ndef, const char* prefix, const void* buf, size_t len, bool force_hex) {\n    if(prefix) furi_string_cat_printf(ndef->output, \"%s: \", prefix);\n    if(!force_hex && is_text(buf, len)) {\n        furi_string_cat_printf(ndef->output, \"%.*s\", len, (const char*)buf);\n    } else {\n        pretty_format_bytes_hex_canonical(ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, len);\n    }\n    furi_string_cat(ndef->output, \"\\n\");\n}\n\n// ---=== payload parsing ===---\n\nstatic inline uint8_t hex_to_int(char c) {\n    if(c >= '0' && c <= '9') return c - '0';\n    if(c >= 'A' && c <= 'F') return c - 'A' + 10;\n    if(c >= 'a' && c <= 'f') return c - 'a' + 10;\n    return 0;\n}\n\nstatic char url_decode_char(const char* str) {\n    return (hex_to_int(str[0]) << 4) | hex_to_int(str[1]);\n}\n\nstatic bool ndef_parse_uri(Ndef* ndef, size_t pos, size_t len) {\n    const char* type = \"URI\";\n\n    // Parse URI prepend type\n    const char* prepend = NULL;\n    uint8_t prepend_type;\n    if(!ndef_get(ndef, pos++, 1, &prepend_type)) return false;\n    len--;\n    if(prepend_type < COUNT_OF(ndef_uri_prepends)) {\n        prepend = ndef_uri_prepends[prepend_type];\n    }\n    if(prepend) {\n        if(strncmp(prepend, \"http\", 4) == 0) {\n            type = \"URL\";\n        } else if(strncmp(prepend, \"tel:\", 4) == 0) {\n            type = \"Phone\";\n            prepend = \"\"; // Not NULL to avoid schema check below, only want to hide it from output\n        } else if(strncmp(prepend, \"mailto:\", 7) == 0) {\n            type = \"Mail\";\n            prepend = \"\"; // Not NULL to avoid schema check below, only want to hide it from output\n        }\n    }\n\n    // Parse and optionally skip schema, if no prepend was specified\n    if(!prepend) {\n        char schema[7] = {0}; // Longest schema we check is 7 char long without terminator\n        if(!ndef_get(ndef, pos, MIN(sizeof(schema), len), schema)) return false;\n        if(strncmp(schema, \"http\", 4) == 0) {\n            type = \"URL\";\n        } else if(strncmp(schema, \"tel:\", 4) == 0) {\n            type = \"Phone\";\n            pos += 4;\n            len -= 4;\n        } else if(strncmp(schema, \"mailto:\", 7) == 0) {\n            type = \"Mail\";\n            pos += 7;\n            len -= 7;\n        }\n    }\n\n    // Print static data as-is\n    furi_string_cat_printf(ndef->output, \"%s\\n\", type);\n    if(prepend) {\n        furi_string_cat(ndef->output, prepend);\n    }\n\n    // Print URI one char at a time and perform URL decode\n    while(len) {\n        char c;\n        if(!ndef_get(ndef, pos++, 1, &c)) return false;\n        len--;\n        if(c != '%' || len < 2) { // Not encoded, or not enough remaining text for encoded char\n            furi_string_push_back(ndef->output, c);\n            continue;\n        }\n        char enc[2];\n        if(!ndef_get(ndef, pos, 2, enc)) return false;\n        enc[0] = toupper(enc[0]);\n        enc[1] = toupper(enc[1]);\n        // Only consume and print these 2 characters if they're valid URL encoded\n        // Otherwise they're processed in next iterations and we output the % char\n        if(((enc[0] >= 'A' && enc[0] <= 'F') || (enc[0] >= '0' && enc[0] <= '9')) &&\n           ((enc[1] >= 'A' && enc[1] <= 'F') || (enc[1] >= '0' && enc[1] <= '9'))) {\n            pos += 2;\n            len -= 2;\n            c = url_decode_char(enc);\n        }\n        furi_string_push_back(ndef->output, c);\n    }\n\n    return true;\n}\n\nstatic bool ndef_parse_text(Ndef* ndef, size_t pos, size_t len) {\n    furi_string_cat(ndef->output, \"Text\\n\");\n    if(!ndef_dump(ndef, NULL, pos + 3, len - 3, false)) return false;\n    return true;\n}\n\nstatic bool ndef_parse_bt(Ndef* ndef, size_t pos, size_t len) {\n    furi_string_cat(ndef->output, \"BT MAC\\n\");\n    if(len != 8) return false;\n    if(!ndef_dump(ndef, NULL, pos + 2, len - 2, true)) return false;\n    return true;\n}\n\nstatic bool ndef_parse_vcard(Ndef* ndef, size_t pos, size_t len) {\n    // We hide redundant tags the user is probably not interested in.\n    // Would be easier with FuriString checks for start_with() and end_with()\n    // but to do that would waste lots of RAM on a cloned string buffer\n    // so instead we just look for these markers at start/end and shift\n    // pos and len then use ndef_dump() to output one char at a time.\n    // Results in minimal stack and no heap usage at all.\n    static const char* const begin_tag = \"BEGIN:VCARD\";\n    static const uint8_t begin_len = strlen(begin_tag);\n    static const char* const version_tag = \"VERSION:\";\n    static const uint8_t version_len = strlen(version_tag);\n    static const char* const end_tag = \"END:VCARD\";\n    static const uint8_t end_len = strlen(end_tag);\n    char tmp[13] = {0}; // Enough for BEGIN:VCARD\\r\\n\n    uint8_t skip = 0;\n\n    // Skip BEGIN tag\n    if(len >= sizeof(tmp)) {\n        if(!ndef_get(ndef, pos, sizeof(tmp), tmp)) return false;\n        if(strncmp(begin_tag, tmp, begin_len) == 0) {\n            skip = begin_len;\n            if(tmp[skip] == '\\r') skip++;\n            if(tmp[skip] == '\\n') skip++;\n            pos += skip;\n            len -= skip;\n        }\n    }\n\n    // Skip VERSION tag\n    if(len >= sizeof(tmp)) {\n        if(!ndef_get(ndef, pos, sizeof(tmp), tmp)) return false;\n        if(strncmp(version_tag, tmp, version_len) == 0) {\n            skip = version_len;\n            while(skip < len) {\n                if(!ndef_get(ndef, pos + skip, 1, &tmp[0])) return false;\n                skip++;\n                if(tmp[0] == '\\n') break;\n            }\n            pos += skip;\n            len -= skip;\n        }\n    }\n\n    // Skip END tag\n    if(len >= sizeof(tmp)) {\n        if(!ndef_get(ndef, pos + len - sizeof(tmp), sizeof(tmp), tmp)) return false;\n        // Read more than length of END tag and check multiple offsets, might have some padding after\n        // Worst case: there is END:VCARD\\r\\n\\r\\n which is same length as tmp buffer (13)\n        // Not sure if this is in spec but might aswell check\n        static const uint8_t offsets = sizeof(tmp) - end_len + 1;\n        for(uint8_t offset = 0; offset < offsets; offset++) {\n            if(strncmp(end_tag, tmp + offset, end_len) == 0) {\n                skip = sizeof(tmp) - offset;\n                len -= skip;\n                break;\n            }\n        }\n    }\n\n    furi_string_cat(ndef->output, \"Contact\\n\");\n    ndef_dump(ndef, NULL, pos, len, false);\n\n    return true;\n}\n\n// Loosely based on Android WiFi NDEF implementation:\n// https://android.googlesource.com/platform/packages/apps/Nfc/+/025560080737b43876c9d81feff3151f497947e8/src/com/android/nfc/NfcWifiProtectedSetup.java\nstatic bool ndef_parse_wifi(Ndef* ndef, size_t pos, size_t len) {\n#define CREDENTIAL_FIELD_ID        (0x100E)\n#define SSID_FIELD_ID              (0x1045)\n#define NETWORK_KEY_FIELD_ID       (0x1027)\n#define AUTH_TYPE_FIELD_ID         (0x1003)\n#define AUTH_TYPE_EXPECTED_SIZE    (2)\n#define AUTH_TYPE_OPEN             (0x0001)\n#define AUTH_TYPE_WPA_PSK          (0x0002)\n#define AUTH_TYPE_WPA_EAP          (0x0008)\n#define AUTH_TYPE_WPA2_EAP         (0x0010)\n#define AUTH_TYPE_WPA2_PSK         (0x0020)\n#define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022)\n#define MAX_NETWORK_KEY_SIZE_BYTES (64)\n\n    furi_string_cat(ndef->output, \"WiFi\\n\");\n    size_t end = pos + len;\n\n    uint8_t tmp_buf[2];\n    while(pos < end) {\n        if(!ndef_get(ndef, pos, 2, &tmp_buf)) return false;\n        uint16_t field_id = bit_lib_bytes_to_num_be(tmp_buf, 2);\n        pos += 2;\n        if(!ndef_get(ndef, pos, 2, &tmp_buf)) return false;\n        uint16_t field_len = bit_lib_bytes_to_num_be(tmp_buf, 2);\n        pos += 2;\n        FURI_LOG_D(TAG, \"wifi field: %04X len: %d\", field_id, field_len);\n\n        if(pos + field_len > end) {\n            return false;\n        }\n\n        if(field_id == CREDENTIAL_FIELD_ID) {\n            size_t field_end = pos + field_len;\n            while(pos < field_end) {\n                if(!ndef_get(ndef, pos, 2, &tmp_buf)) return false;\n                uint16_t cfg_id = bit_lib_bytes_to_num_be(tmp_buf, 2);\n                pos += 2;\n                if(!ndef_get(ndef, pos, 2, &tmp_buf)) return false;\n                uint16_t cfg_len = bit_lib_bytes_to_num_be(tmp_buf, 2);\n                pos += 2;\n                FURI_LOG_D(TAG, \"wifi cfg: %04X len: %d\", cfg_id, cfg_len);\n\n                if(pos + cfg_len > field_end) {\n                    return false;\n                }\n\n                switch(cfg_id) {\n                case SSID_FIELD_ID:\n                    if(!ndef_dump(ndef, \"SSID\", pos, cfg_len, false)) return false;\n                    pos += cfg_len;\n                    break;\n                case NETWORK_KEY_FIELD_ID:\n                    if(cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) {\n                        return false;\n                    }\n                    if(!ndef_dump(ndef, \"PWD\", pos, cfg_len, false)) return false;\n                    pos += cfg_len;\n                    break;\n                case AUTH_TYPE_FIELD_ID:\n                    if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) {\n                        return false;\n                    }\n                    if(!ndef_get(ndef, pos, 2, &tmp_buf)) return false;\n                    uint16_t auth_type = bit_lib_bytes_to_num_be(tmp_buf, 2);\n                    pos += 2;\n                    const char* auth;\n                    switch(auth_type) {\n                    case AUTH_TYPE_OPEN:\n                        auth = \"Open\";\n                        break;\n                    case AUTH_TYPE_WPA_PSK:\n                        auth = \"WPA Personal\";\n                        break;\n                    case AUTH_TYPE_WPA_EAP:\n                        auth = \"WPA Enterprise\";\n                        break;\n                    case AUTH_TYPE_WPA2_EAP:\n                        auth = \"WPA2 Enterprise\";\n                        break;\n                    case AUTH_TYPE_WPA2_PSK:\n                        auth = \"WPA2 Personal\";\n                        break;\n                    case AUTH_TYPE_WPA_AND_WPA2_PSK:\n                        auth = \"WPA/WPA2 Personal\";\n                        break;\n                    default:\n                        auth = \"Unknown\";\n                        break;\n                    }\n                    ndef_print(ndef, \"AUTH\", auth, strlen(auth), false);\n                    break;\n                default:\n                    pos += cfg_len;\n                    break;\n                }\n            }\n            return true;\n        }\n        pos += field_len;\n    }\n\n    furi_string_cat(ndef->output, \"No data parsed\\n\");\n    return true;\n}\n\n// ---=== ndef layout parsing ===---\n\nbool ndef_parse_record(\n    Ndef* ndef,\n    size_t pos,\n    size_t len,\n    NdefTnf tnf,\n    const char* type,\n    uint8_t type_len);\nbool ndef_parse_message(Ndef* ndef, size_t pos, size_t len, size_t message_num, bool smart_poster);\nsize_t ndef_parse_tlv(Ndef* ndef, size_t pos, size_t len, size_t already_parsed);\n\nbool ndef_parse_record(\n    Ndef* ndef,\n    size_t pos,\n    size_t len,\n    NdefTnf tnf,\n    const char* type,\n    uint8_t type_len) {\n    FURI_LOG_D(TAG, \"payload type: %.*s len: %hu pos: %zu\", type_len, type, len, pos);\n    if(!len) {\n        furi_string_cat(ndef->output, \"Empty\\n\");\n        return true;\n    }\n\n    switch(tnf) {\n    case NdefTnfWellKnownType:\n        if(strncmp(\"Sp\", type, type_len) == 0) {\n            furi_string_cat(ndef->output, \"SmartPoster\\nContained records below\\n\\n\");\n            return ndef_parse_message(ndef, pos, len, 0, true);\n        } else if(strncmp(\"U\", type, type_len) == 0) {\n            return ndef_parse_uri(ndef, pos, len);\n        } else if(strncmp(\"T\", type, type_len) == 0) {\n            return ndef_parse_text(ndef, pos, len);\n        }\n        // Dump data without parsing\n        furi_string_cat(ndef->output, \"Unknown\\n\");\n        ndef_print(ndef, \"Well-known Type\", type, type_len, false);\n        if(!ndef_dump(ndef, \"Payload\", pos, len, false)) return false;\n        return true;\n\n    case NdefTnfMediaType:\n        if(strncmp(\"application/vnd.bluetooth.ep.oob\", type, type_len) == 0) {\n            return ndef_parse_bt(ndef, pos, len);\n        } else if(strncmp(\"text/vcard\", type, type_len) == 0) {\n            return ndef_parse_vcard(ndef, pos, len);\n        } else if(strncmp(\"application/vnd.wfa.wsc\", type, type_len) == 0) {\n            return ndef_parse_wifi(ndef, pos, len);\n        }\n        // Dump data without parsing\n        furi_string_cat(ndef->output, \"Unknown\\n\");\n        ndef_print(ndef, \"Media Type\", type, type_len, false);\n        if(!ndef_dump(ndef, \"Payload\", pos, len, false)) return false;\n        return true;\n\n    case NdefTnfEmpty:\n    case NdefTnfAbsoluteUri:\n    case NdefTnfExternalType:\n    case NdefTnfUnknown:\n    case NdefTnfUnchanged:\n    case NdefTnfReserved:\n    default:\n        // Dump data without parsing\n        furi_string_cat(ndef->output, \"Unsupported\\n\");\n        ndef_print(ndef, \"Type name format\", &tnf, 1, true);\n        ndef_print(ndef, \"Type\", type, type_len, false);\n        if(!ndef_dump(ndef, \"Payload\", pos, len, false)) return false;\n        return true;\n    }\n}\n\n// NDEF message structure:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/nfc/index.html#ndef_message_and_record_format\nbool ndef_parse_message(Ndef* ndef, size_t pos, size_t len, size_t message_num, bool smart_poster) {\n    size_t end = pos + len;\n\n    size_t record_num = 0;\n    bool last_record = false;\n    while(pos < end) {\n        // Flags and TNF\n        NdefFlagsTnf flags_tnf;\n        if(!ndef_get(ndef, pos++, 1, &flags_tnf)) return false;\n        FURI_LOG_D(TAG, \"flags_tnf: %02X\", *(uint8_t*)&flags_tnf);\n        FURI_LOG_D(TAG, \"flags_tnf.message_begin: %d\", flags_tnf.message_begin);\n        FURI_LOG_D(TAG, \"flags_tnf.message_end: %d\", flags_tnf.message_end);\n        FURI_LOG_D(TAG, \"flags_tnf.chunk_flag: %d\", flags_tnf.chunk_flag);\n        FURI_LOG_D(TAG, \"flags_tnf.short_record: %d\", flags_tnf.short_record);\n        FURI_LOG_D(TAG, \"flags_tnf.id_length_present: %d\", flags_tnf.id_length_present);\n        FURI_LOG_D(TAG, \"flags_tnf.type_name_format: %02X\", flags_tnf.type_name_format);\n        // Message Begin should only be set on first record\n        if(record_num++ && flags_tnf.message_begin) return false;\n        // Message End should only be set on last record\n        if(last_record) return false;\n        if(flags_tnf.message_end) last_record = true;\n        // Chunk Flag not supported\n        if(flags_tnf.chunk_flag) return false;\n\n        // Type Length\n        uint8_t type_len;\n        if(!ndef_get(ndef, pos++, 1, &type_len)) return false;\n\n        // Payload Length field of 1 or 4 bytes\n        uint32_t payload_len;\n        if(flags_tnf.short_record) {\n            uint8_t payload_len_short;\n            if(!ndef_get(ndef, pos++, 1, &payload_len_short)) return false;\n            payload_len = payload_len_short;\n        } else {\n            if(!ndef_get(ndef, pos, sizeof(payload_len), &payload_len)) return false;\n            payload_len = bit_lib_bytes_to_num_be((void*)&payload_len, sizeof(payload_len));\n            pos += sizeof(payload_len);\n        }\n\n        // ID Length\n        uint8_t id_len = 0;\n        if(flags_tnf.id_length_present) {\n            if(!ndef_get(ndef, pos++, 1, &id_len)) return false;\n        }\n\n        // Payload Type\n        char type_buf[32]; // Longest type supported in ndef_parse_record() is 32 chars excl terminator\n        char* type = type_buf;\n        bool type_was_allocated = false;\n        if(type_len) {\n            if(type_len > sizeof(type_buf)) {\n                type = malloc(type_len);\n                type_was_allocated = true;\n            }\n            if(!ndef_get(ndef, pos, type_len, type)) {\n                if(type_was_allocated) free(type);\n                return false;\n            }\n            pos += type_len;\n        }\n\n        // Payload ID\n        pos += id_len;\n\n        if(smart_poster) {\n            furi_string_cat_printf(ndef->output, \"\\e*> SP-R%zu: \", record_num);\n        } else {\n            furi_string_cat_printf(ndef->output, \"\\e*> M%zu-R%zu: \", message_num, record_num);\n        }\n        if(!ndef_parse_record(ndef, pos, payload_len, flags_tnf.type_name_format, type, type_len)) {\n            if(type_was_allocated) free(type);\n            return false;\n        }\n        pos += payload_len;\n\n        if(type_was_allocated) free(type);\n        furi_string_trim(ndef->output, \"\\n\");\n        furi_string_cat(ndef->output, \"\\n\\n\");\n    }\n\n    if(record_num == 0) {\n        if(smart_poster) {\n            furi_string_cat(ndef->output, \"\\e*> SP: Empty\\n\\n\");\n        } else {\n            furi_string_cat_printf(ndef->output, \"\\e*> M%zu: Empty\\n\\n\", message_num);\n        }\n    }\n\n    return pos == end && (last_record || record_num == 0);\n}\n\n// TLV structure:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nfc/doc/type_2_tag.html#data\nsize_t ndef_parse_tlv(Ndef* ndef, size_t pos, size_t len, size_t already_parsed) {\n    size_t end = pos + len;\n    size_t message_num = 0;\n\n    while(pos < end) {\n        NdefTlv tlv;\n        if(!ndef_get(ndef, pos++, 1, &tlv)) return 0;\n        FURI_LOG_D(TAG, \"tlv: %02X\", tlv);\n\n        switch(tlv) {\n        default:\n            // Unknown, bail to avoid problems\n            return 0;\n\n        case NdefTlvPadding:\n            // Has no length, skip to next byte\n            break;\n\n        case NdefTlvTerminator:\n            // NDEF message finished, return whether we parsed something\n            return message_num;\n\n        case NdefTlvLockControl:\n        case NdefTlvMemoryControl:\n        case NdefTlvProprietary:\n        case NdefTlvNdefMessage: {\n            // Calculate length\n            uint16_t len;\n            uint8_t len_type;\n            if(!ndef_get(ndef, pos++, 1, &len_type)) return 0;\n            if(len_type < 0xFF) { // 1 byte length\n                len = len_type;\n            } else { // 3 byte length (0xFF marker + 2 byte integer)\n                if(!ndef_get(ndef, pos, sizeof(len), &len)) return 0;\n                len = bit_lib_bytes_to_num_be((void*)&len, sizeof(len));\n                pos += sizeof(len);\n            }\n\n            if(tlv != NdefTlvNdefMessage) {\n                // We don't care, skip this TLV block to next one\n                pos += len;\n                break;\n            }\n\n            if(!ndef_parse_message(ndef, pos, len, ++message_num + already_parsed, false))\n                return 0;\n            pos += len;\n\n            break;\n        }\n        }\n    }\n\n    // Reached data end with no TLV terminator,\n    // but also no errors, treat this as a success\n    return message_num;\n}\n\n#if NDEF_PROTO != NDEF_PROTO_RAW\n\n// ---=== protocol entry-points ===---\n\n#if NDEF_PROTO == NDEF_PROTO_UL\n\n// MF UL memory layout:\n// https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nfc/doc/type_2_tag.html#memory_layout\nstatic bool ndef_ul_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    // Check card type can contain NDEF\n    if(data->type != MfUltralightTypeNTAG203 && data->type != MfUltralightTypeNTAG213 &&\n       data->type != MfUltralightTypeNTAG215 && data->type != MfUltralightTypeNTAG216 &&\n       data->type != MfUltralightTypeNTAGI2C1K && data->type != MfUltralightTypeNTAGI2C2K &&\n       data->type != MfUltralightTypeNTAGI2CPlus1K &&\n       data->type != MfUltralightTypeNTAGI2CPlus2K) {\n        return false;\n    }\n\n    // Check Capability Container (CC) values\n    struct {\n        uint8_t nfc_magic_number;\n        uint8_t document_version_number;\n        uint8_t data_area_size; // Usable byte size / 8, only includes user memory\n        uint8_t read_write_access;\n    }* cc = (void*)&data->page[3].data[0];\n    if(cc->nfc_magic_number != 0xE1) return false;\n    if(cc->document_version_number != 0x10) return false;\n\n    // Calculate usable data area\n    const uint8_t* start = &data->page[4].data[0];\n    const uint8_t* end = start + (cc->data_area_size * 8);\n    size_t max_size = mf_ultralight_get_pages_total(data->type) * MF_ULTRALIGHT_PAGE_SIZE;\n    end = MIN(end, &data->page[0].data[0] + max_size);\n    size_t data_start = 0;\n    size_t data_size = end - start;\n\n    NDEF_TITLE(device, parsed_data);\n\n    Ndef ndef = {\n        .output = parsed_data,\n        .ul =\n            {\n                .start = start,\n                .size = data_size,\n            },\n    };\n    size_t parsed = ndef_parse_tlv(&ndef, data_start, data_size - data_start, 0);\n\n    if(parsed) {\n        furi_string_trim(parsed_data, \"\\n\");\n        furi_string_cat(parsed_data, \"\\n\");\n    } else {\n        furi_string_reset(parsed_data);\n    }\n\n    return parsed > 0;\n}\n\n#elif NDEF_PROTO == NDEF_PROTO_MFC\n\n// MFC MAD datasheet:\n// https://www.nxp.com/docs/en/application-note/AN10787.pdf\n#define AID_SIZE (2)\nstatic const uint64_t mad_key = 0xA0A1A2A3A4A5;\n\n// NDEF on MFC breakdown:\n// https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#storing-ndef-messages-in-mifare-sectors-607778\nstatic const uint8_t ndef_aid[AID_SIZE] = {0x03, 0xE1};\n\nstatic bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    // Check card type can contain NDEF\n    if(data->type != MfClassicType1k && data->type != MfClassicType4k &&\n       data->type != MfClassicTypeMini) {\n        return false;\n    }\n\n    // Check MADs for what sectors contain NDEF data AIDs\n    bool sectors_with_ndef[MF_CLASSIC_TOTAL_SECTORS_MAX] = {0};\n    const size_t sector_count = mf_classic_get_total_sectors_num(data->type);\n    const struct {\n        size_t block;\n        uint8_t aid_count;\n    } mads[2] = {\n        {1, 15},\n        {64, 23},\n    };\n    for(uint8_t mad = 0; mad < COUNT_OF(mads); mad++) {\n        const size_t block = mads[mad].block;\n        const size_t sector = mf_classic_get_sector_by_block(block);\n        if(sector_count <= sector) continue; // Skip this MAD if not present\n        // Check MAD key\n        const MfClassicSectorTrailer* sector_trailer =\n            mf_classic_get_sector_trailer_by_sector(data, sector);\n        const uint64_t sector_key_a = bit_lib_bytes_to_num_be(\n            sector_trailer->key_a.data, COUNT_OF(sector_trailer->key_a.data));\n        if(sector_key_a != mad_key) continue;\n        // Find NDEF AIDs\n        for(uint8_t aid_index = 0; aid_index < mads[mad].aid_count; aid_index++) {\n            const uint8_t* aid = &data->block[block].data[2 + aid_index * AID_SIZE];\n            if(memcmp(aid, ndef_aid, AID_SIZE) == 0) {\n                sectors_with_ndef[aid_index + 1] = true;\n            }\n        }\n    }\n\n    NDEF_TITLE(device, parsed_data);\n\n    // Calculate how large the data space is, so excluding sector trailers and MAD2.\n    // Makes sure we stay within this card's actual content when parsing.\n    // First 32 sectors: 3 data blocks, 1 sector trailer.\n    // Sector 16 contains MAD2 and we need to skip this.\n    // So the first 32 sectors correspond to 93 (31*3) data blocks.\n    // Last 8 sectors: 15 data blocks, 1 sector trailer.\n    // So the last 8 sectors correspond to 120 (8*15) data blocks.\n    size_t data_size;\n    if(sector_count > 32) {\n        data_size = 93 + (sector_count - 32) * 15;\n    } else {\n        data_size = sector_count * 3;\n        if(sector_count > 16) {\n            data_size -= 3; // Skip MAD2\n        }\n    }\n    data_size *= MF_CLASSIC_BLOCK_SIZE;\n\n    Ndef ndef = {\n        .output = parsed_data,\n        .mfc =\n            {\n                .blocks = data->block,\n                .size = data_size,\n            },\n    };\n    size_t total_parsed = 0;\n\n    for(size_t sector = 0; sector < sector_count; sector++) {\n        if(!sectors_with_ndef[sector]) continue;\n        FURI_LOG_D(TAG, \"sector: %d\", sector);\n        size_t string_prev = furi_string_size(parsed_data);\n\n        // Convert real sector number to data block number\n        // to skip sector trailers and MAD2\n        size_t data_block;\n        if(sector < 32) {\n            data_block = sector * 3;\n            if(sector >= 16) {\n                data_block -= 3; // Skip MAD2\n            }\n        } else {\n            data_block = 93 + (sector - 32) * 15;\n        }\n        FURI_LOG_D(TAG, \"data_block: %zu\", data_block);\n        size_t data_start = data_block * MF_CLASSIC_BLOCK_SIZE;\n        size_t parsed = ndef_parse_tlv(&ndef, data_start, data_size - data_start, total_parsed);\n\n        if(parsed) {\n            total_parsed += parsed;\n            furi_string_trim(parsed_data, \"\\n\");\n            furi_string_cat(parsed_data, \"\\n\");\n        } else {\n            furi_string_left(parsed_data, string_prev);\n        }\n    }\n\n    if(!total_parsed) {\n        furi_string_reset(parsed_data);\n    }\n\n    return total_parsed > 0;\n}\n\n#elif NDEF_PROTO == NDEF_PROTO_SLIX\n\n// SLIX NDEF memory layout:\n// https://community.nxp.com/pwmxy87654/attachments/pwmxy87654/nfc/7583/1/EEOL_2011FEB16_EMS_RFD_AN_01.pdf\nstatic bool ndef_slix_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);\n    const uint8_t block_size = iso15693_3_get_block_size(data);\n    const uint16_t block_count = iso15693_3_get_block_count(data);\n    const uint8_t* blocks = simple_array_cget_data(data->block_data);\n\n    // TODO(-nofl): Find some way to check for other iso15693 NDEF cards and\n    // split this to also support non-slix iso15693 NDEF tags\n    // Rest of the code works on iso15693 too, but uses SLIX layout assumptions\n    if(block_size != SLIX_BLOCK_SIZE) {\n        return false;\n    }\n\n    // Check Capability Container (CC) values\n    struct {\n        uint8_t nfc_magic_number;\n        uint8_t read_write_access       : 4; // Reversed due to endianness\n        uint8_t document_version_number : 4;\n        uint8_t data_area_size; // Total byte size / 8, includes block 0\n        uint8_t mbread_ipread;\n    }* cc = (void*)&blocks[0 * block_size];\n    if(cc->nfc_magic_number != 0xE1) return false;\n    if(cc->document_version_number != 0x4) return false;\n\n    // Calculate usable data area\n    const uint8_t* start = &blocks[1 * block_size];\n    const uint8_t* end = blocks + (cc->data_area_size * 8);\n    size_t max_size = block_count * block_size;\n    end = MIN(end, blocks + max_size);\n    size_t data_start = 0;\n    size_t data_size = end - start;\n\n    NDEF_TITLE(device, parsed_data);\n\n    Ndef ndef = {\n        .output = parsed_data,\n        .slix =\n            {\n                .start = start,\n                .size = data_size,\n            },\n    };\n    size_t parsed = ndef_parse_tlv(&ndef, data_start, data_size - data_start, 0);\n\n    if(parsed) {\n        furi_string_trim(parsed_data, \"\\n\");\n        furi_string_cat(parsed_data, \"\\n\");\n    } else {\n        furi_string_reset(parsed_data);\n    }\n\n    return parsed > 0;\n}\n\n#endif\n\n// ---=== boilerplate ===---\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin ndef_plugin = {\n    .verify = NULL,\n    .read = NULL,\n#if NDEF_PROTO == NDEF_PROTO_UL\n    .parse = ndef_ul_parse,\n    .protocol = NfcProtocolMfUltralight,\n#elif NDEF_PROTO == NDEF_PROTO_MFC\n    .parse = ndef_mfc_parse,\n    .protocol = NfcProtocolMfClassic,\n#elif NDEF_PROTO == NDEF_PROTO_SLIX\n    .parse = ndef_slix_parse,\n    .protocol = NfcProtocolSlix,\n#endif\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor ndef_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &ndef_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* ndef_plugin_ep(void) {\n    return &ndef_plugin_descriptor;\n}\n\n#endif\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h",
    "content": "/**\n * @file nfc_supported_card_plugin.h\n * @brief Supported card plugin abstract interface.\n *\n * Supported card plugins are dynamically loaded libraries that help making sense of\n * a particular card's raw data, if a suitable plugin exists.\n *\n * For example, if some card serves as a bus ticket, instead of just displaying a data dump,\n * a suitable plugin will transform that data into a human-readable format, showing the number\n * of rides or balance left.\n * Because of the highly specialised nature of application-specific cards, a separate plugin\n * for each such card type must be implemented.\n *\n * To add a new plugin, create a uniquely-named .c file in the `supported_cards` directory\n * and implement at least the parse() function in the NfcSupportedCardsPlugin structure.\n * Then, register the plugin in the `application.fam` file in the `nfc` directory. Use the existing\n * entries as an example. After being registered, the plugin will be automatically deployed with the application.\n *\n * @note the APPID field MUST end with `_parser` so the applicaton would know that this particular file\n * is a supported card plugin.\n *\n * @see nfc_supported_cards.h\n */\n#pragma once\n\n#include <furi/core/string.h>\n\n#include <nfc/nfc.h>\n#include <nfc/nfc_device.h>\n\n/**\n * @brief Unique string identifier for supported card plugins.\n */\n#define NFC_SUPPORTED_CARD_PLUGIN_APP_ID \"NfcSupportedCardPlugin\"\n\n/**\n * @brief Currently supported plugin API version.\n */\n#define NFC_SUPPORTED_CARD_PLUGIN_API_VERSION 1\n\n/**\n * @brief Verify that the card is of a supported type.\n *\n * This function should be implemented if a quick check exists\n * allowing to verify that the plugin is working with the appropriate card type.\n * Such checks may include, but are not limited to: reading a specific sector,\n * performing a certain read operation, etc.\n *\n * @param[in,out] nfc pointer to an Nfc instance.\n * @returns true if the card was successfully verified, false otherwise.\n */\ntypedef bool (*NfcSupportedCardPluginVerify)(Nfc* nfc);\n\n/**\n * @brief Read the card using a custom procedure.\n *\n * This function should be implemented if a card requires some special reading\n * procedure not covered in the vanilla poller. Examples include, but are not\n * limited to: reading with particular security keys, mandatory order of read\n * operations, etc.\n *\n * @param[in,out] nfc pointer to an Nfc instance.\n * @param[in,out] device pointer to a device instance to hold the read data.\n * @returns true if the card was successfully read, false otherwise.\n */\ntypedef bool (*NfcSupportedCardPluginRead)(Nfc* nfc, NfcDevice* device);\n\n/**\n * @brief Parse raw data into human-readable representation.\n *\n * A supported card plugin may contain only this function, if no special verification\n * or reading procedures are not required. In any case, the data must be complete and\n * available through the `device` parameter at the time of calling.\n *\n * The output format is free and application-dependent. Multiple lines should\n * be separated by newline character.\n *\n * @param[in] device pointer to a device instance holding the data is to be parsed.\n * @param[out] parsed_data pointer to the string to contain the formatted result.\n * @returns true if the card was successfully parsed, false otherwise.\n */\ntypedef bool (*NfcSupportedCardPluginParse)(const NfcDevice* device, FuriString* parsed_data);\n\n/**\n * @brief Supported card plugin interface.\n *\n * For a minimally functional plugin, only the parse() function must be implemented.\n */\ntypedef struct {\n    NfcProtocol protocol; /**< Identifier of the protocol this card type works on top of. */\n    NfcSupportedCardPluginVerify verify; /**< Pointer to the verify() function. */\n    NfcSupportedCardPluginRead read; /**< Pointer to the read() function. */\n    NfcSupportedCardPluginParse parse; /**< Pointer to the parse() function. */\n} NfcSupportedCardsPlugin;\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/opal.c",
    "content": "/*\n * opal.c - Parser for Opal card (Sydney, Australia).\n *\n * Copyright 2023 Michael Farrell <micolous+git@gmail.com>\n *\n * This will only read \"standard\" MIFARE DESFire-based Opal cards. Free travel\n * cards (including School Opal cards, veteran, vision-impaired persons and\n * TfNSW employees' cards) and single-trip tickets are MIFARE Ultralight C\n * cards and not supported.\n *\n * Reference: https://github.com/metrodroid/metrodroid/wiki/Opal\n *\n * Note: The card values are all little-endian (like Flipper), but the above\n * reference was originally written based on Java APIs, which are big-endian.\n * This implementation presumes a little-endian system.\n *\n * This program is free software: you can redistribute it and/or modify it\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n#include <applications/services/locale/locale.h>\n#include <datetime/datetime.h>\n\n#include <lib/nfc/protocols/mf_desfire/mf_desfire.h>\n\nstatic const MfDesfireApplicationId opal_app_id = {.data = {0x31, 0x45, 0x53}};\n\nstatic const MfDesfireFileId opal_file_id = 0x07;\n\nstatic const char* opal_modes[5] =\n    {\"Rail / Metro\", \"Ferry / Light Rail\", \"Bus\", \"Unknown mode\", \"Manly Ferry\"};\n\nstatic const char* opal_usages[14] = {\n    \"New / Unused\",\n    \"Tap on: new journey\",\n    \"Tap on: transfer from same mode\",\n    \"Tap on: transfer from other mode\",\n    NULL, // Manly Ferry: new journey\n    NULL, // Manly Ferry: transfer from ferry\n    NULL, // Manly Ferry: transfer from other\n    \"Tap off: distance fare\",\n    \"Tap off: flat fare\",\n    \"Automated tap off: failed to tap off\",\n    \"Tap off: end of trip without start\",\n    \"Tap off: reversal\",\n    \"Tap on: rejected\",\n    \"Unknown usage\",\n};\n\n// Opal file 0x7 structure. Assumes a little-endian CPU.\ntypedef struct FURI_PACKED {\n    uint32_t serial         : 32;\n    uint8_t check_digit     : 4;\n    bool blocked            : 1;\n    uint16_t txn_number     : 16;\n    int32_t balance         : 21;\n    uint16_t days           : 15;\n    uint16_t minutes        : 11;\n    uint8_t mode            : 3;\n    uint16_t usage          : 4;\n    bool auto_topup         : 1;\n    uint8_t weekly_journeys : 4;\n    uint16_t checksum       : 16;\n} OpalFile;\n\nstatic_assert(sizeof(OpalFile) == 16, \"OpalFile\");\n\n// Converts an Opal timestamp to DateTime.\n//\n// Opal measures days since 1980-01-01 and minutes since midnight, and presumes\n// all days are 1440 minutes.\nstatic void opal_date_time_to_furi(uint16_t days, uint16_t minutes, DateTime* out) {\n    out->year = 1980;\n    out->month = 1;\n    // 1980-01-01 is a Tuesday\n    out->weekday = ((days + 1) % 7) + 1;\n    out->hour = minutes / 60;\n    out->minute = minutes % 60;\n    out->second = 0;\n\n    // What year is it?\n    for(;;) {\n        const uint16_t num_days_in_year = datetime_get_days_per_year(out->year);\n        if(days < num_days_in_year) break;\n        days -= num_days_in_year;\n        out->year++;\n    }\n\n    // 1-index the day of the year\n    days++;\n\n    for(;;) {\n        // What month is it?\n        const bool is_leap = datetime_is_leap_year(out->year);\n        const uint8_t num_days_in_month = datetime_get_days_per_month(is_leap, out->month);\n        if(days <= num_days_in_month) break;\n        days -= num_days_in_month;\n        out->month++;\n    }\n\n    out->day = days;\n}\n\nstatic bool opal_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);\n\n    bool parsed = false;\n\n    do {\n        const MfDesfireApplication* app = mf_desfire_get_application(data, &opal_app_id);\n        if(app == NULL) break;\n\n        const MfDesfireFileSettings* file_settings =\n            mf_desfire_get_file_settings(app, &opal_file_id);\n        if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard ||\n           file_settings->data.size != sizeof(OpalFile))\n            break;\n\n        const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &opal_file_id);\n        if(file_data == NULL) break;\n\n        const OpalFile* opal_file = simple_array_cget_data(file_data->data);\n\n        const uint8_t serial2 = opal_file->serial / 10000000;\n        const uint16_t serial3 = (opal_file->serial / 1000) % 10000;\n        const uint16_t serial4 = (opal_file->serial % 1000);\n\n        if(opal_file->check_digit > 9) break;\n\n        // Negative balance. Make this a positive value again and record the\n        // sign separately, because then we can handle balances of -99..-1\n        // cents, as the \"dollars\" division below would result in a positive\n        // zero value.\n        const bool is_negative_balance = (opal_file->balance < 0);\n        const char* sign = is_negative_balance ? \"-\" : \"\";\n        const int32_t balance = is_negative_balance ? labs(opal_file->balance) : //-V1081\n                                                      opal_file->balance;\n        const uint8_t balance_cents = balance % 100;\n        const int32_t balance_dollars = balance / 100;\n\n        DateTime timestamp;\n        opal_date_time_to_furi(opal_file->days, opal_file->minutes, &timestamp);\n\n        // Usages 4..6 associated with the Manly Ferry, which correspond to\n        // usages 1..3 for other modes.\n        const bool is_manly_ferry = (opal_file->usage >= 4) && (opal_file->usage <= 6);\n\n        // 3..7 are \"reserved\", but we use 4 to indicate the Manly Ferry.\n        const uint8_t mode = is_manly_ferry ? 4 : opal_file->mode;\n        const uint8_t usage = is_manly_ferry ? opal_file->usage - 3 : opal_file->usage;\n\n        const char* mode_str = opal_modes[mode > 4 ? 3 : mode];\n        const char* usage_str = opal_usages[usage > 12 ? 13 : usage];\n\n        furi_string_printf(\n            parsed_data,\n            \"\\e#Opal: $%s%ld.%02hu\\nNo.: 3085 22%02hhu %04hu %03hu%01hhu\\n%s, %s\\n\",\n            sign,\n            balance_dollars,\n            balance_cents,\n            serial2,\n            serial3,\n            serial4,\n            opal_file->check_digit,\n            mode_str,\n            usage_str);\n\n        FuriString* timestamp_str = furi_string_alloc();\n\n        locale_format_date(timestamp_str, &timestamp, locale_get_date_format(), \"-\");\n        furi_string_cat(parsed_data, timestamp_str);\n        furi_string_cat(parsed_data, \" at \");\n\n        locale_format_time(timestamp_str, &timestamp, locale_get_time_format(), false);\n        furi_string_cat(parsed_data, timestamp_str);\n\n        furi_string_free(timestamp_str);\n\n        furi_string_cat_printf(\n            parsed_data,\n            \"\\nWeekly journeys: %hhu, Txn #%hu\\n\",\n            opal_file->weekly_journeys,\n            opal_file->txn_number);\n\n        if(opal_file->auto_topup) {\n            furi_string_cat_str(parsed_data, \"Auto-topup enabled\\n\");\n        }\n\n        if(opal_file->blocked) {\n            furi_string_cat_str(parsed_data, \"Card blocked\\n\");\n        }\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin opal_plugin = {\n    .protocol = NfcProtocolMfDesfire,\n    .verify = NULL,\n    .read = NULL,\n    .parse = opal_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor opal_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &opal_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* opal_plugin_ep(void) {\n    return &opal_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/plantain.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <datetime.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"Plantain\"\n\nvoid from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {\n    uint32_t timestamp = minutes * 60;\n    DateTime start_datetime = {0};\n    start_datetime.year = start_year - 1;\n    start_datetime.month = 12;\n    start_datetime.day = 31;\n    timestamp += datetime_datetime_to_timestamp(&start_datetime);\n    datetime_timestamp_to_datetime(timestamp, datetime);\n}\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\ntypedef struct {\n    const MfClassicKeyPair* keys;\n    uint32_t data_sector;\n} PlantainCardConfig;\n\nstatic const MfClassicKeyPair plantain_1k_keys[] = {\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b},\n    {.a = 0x77dabc9825e1, .b = 0x9764fec3154a},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x26973ea74321, .b = 0xd27058c6e2c7},\n    {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3},\n    {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb},\n    {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56},\n    {.a = 0xacffffffffff, .b = 0x71f3a315ad26},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff},\n};\n\nstatic const MfClassicKeyPair plantain_4k_keys[] = {\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3},\n    {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56},\n    {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a},\n    {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3},\n    {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056},\n    {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf},\n    {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406},\n    {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA},\n    {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3},\n    {.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290},\n    {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9},\n    {.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461},\n    {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a},\n    {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},\n};\n\nstatic const MfClassicKeyPair plantain_4k_keys_legacy[] = {\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3},\n    {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56},\n    {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a},\n    {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3},\n    {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056},\n    {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf},\n    {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x46d78e850a7e, .b = 0xa470f8130991},\n    {.a = 0x42e9b54e51ab, .b = 0x0231b86df52e}, {.a = 0x0f01ceff2742, .b = 0x6fec74559ca7},\n    {.a = 0xb81f2b0c2f66, .b = 0xa7e2d95f0003}, {.a = 0x9ea3387a63c1, .b = 0x437e59f57561},\n    {.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290},\n    {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9},\n    {.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461},\n    {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a},\n    {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},\n};\n\nstatic bool plantain_get_card_config(PlantainCardConfig* config, MfClassicType type) {\n    bool success = true;\n\n    if(type == MfClassicType1k) {\n        config->data_sector = 8;\n        config->keys = plantain_1k_keys;\n    } else if(type == MfClassicType4k) {\n        config->data_sector = 8;\n        config->keys = plantain_4k_keys;\n    } else {\n        success = false;\n    }\n\n    return success;\n}\n\nstatic bool plantain_verify_type(Nfc* nfc, MfClassicType type) {\n    bool verified = false;\n\n    do {\n        PlantainCardConfig cfg = {};\n        if(!plantain_get_card_config(&cfg, type)) break;\n\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %lu\", cfg.data_sector);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool plantain_verify(Nfc* nfc) {\n    return plantain_verify_type(nfc, MfClassicType1k) ||\n           plantain_verify_type(nfc, MfClassicType4k);\n}\n\nstatic bool plantain_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        PlantainCardConfig cfg = {};\n        if(!plantain_get_card_config(&cfg, data->type)) break;\n\n        const uint8_t legacy_check_sec_num = 26;\n        const uint8_t legacy_check_block_num =\n            mf_classic_get_first_block_num_of_sector(legacy_check_sec_num);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(\n            plantain_4k_keys_legacy[legacy_check_sec_num].a, COUNT_OF(key.data), key.data);\n\n        error = mf_classic_poller_sync_auth(\n            nfc, legacy_check_block_num, &key, MfClassicKeyTypeA, NULL);\n        if(error == MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Legacy keys detected\");\n            cfg.keys = plantain_4k_keys_legacy;\n        }\n\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = (error == MfClassicErrorNone);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    size_t uid_len = 0;\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n    const uint8_t* uid = mf_classic_get_uid(data, &uid_len);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        PlantainCardConfig cfg = {};\n        if(!plantain_get_card_config(&cfg, data->type)) break;\n\n        // Verify key\n        const MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector);\n\n        const uint64_t key =\n            bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));\n        if(key != cfg.keys[cfg.data_sector].a) break;\n\n        furi_string_printf(parsed_data, \"\\e#Plantain card\\n\");\n\n        const uint8_t* temp_ptr = &uid[0];\n\n        // UID is read from last to first byte\n        uint8_t card_number_tmp[uid_len];\n\n        if(uid_len == 4) {\n            for(size_t i = 0; i < 4; i++) {\n                card_number_tmp[i] = temp_ptr[3 - i];\n            }\n        } else if(uid_len == 7) {\n            for(size_t i = 0; i < 7; i++) {\n                card_number_tmp[i] = temp_ptr[6 - i];\n            }\n        } else {\n            break;\n        }\n        //UID is converted to a card number\n        uint64_t card_number = 0;\n        for(size_t i = 0; i < uid_len; i++) {\n            card_number = (card_number << 8) | card_number_tmp[i];\n        }\n\n        // Print card number with 4-digit groups. \"3\" in \"3078\" denotes a ticket type \"3 - full ticket\", will differ on discounted cards.\n        furi_string_cat_printf(parsed_data, \"Number: \");\n        FuriString* card_number_s = furi_string_alloc();\n        furi_string_cat_printf(card_number_s, \"%llu\", card_number);\n        FuriString* tmp_s = furi_string_alloc_set_str(\"9643 3078 \");\n        for(uint8_t i = 0; i < 24; i += 4) {\n            for(uint8_t j = 0; j < 4; j++) {\n                furi_string_push_back(tmp_s, furi_string_get_char(card_number_s, i + j));\n            }\n            furi_string_push_back(tmp_s, ' ');\n        }\n        furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(tmp_s));\n        // this works for 2K Plantain\n        if(data->type == MfClassicType1k) {\n            //balance\n            uint32_t balance = 0;\n            for(uint8_t i = 0; i < 4; i++) {\n                balance = (balance << 8) | data->block[16].data[3 - i];\n            }\n            furi_string_cat_printf(parsed_data, \"Balance: %ld rub\\n\", balance / 100);\n\n            //trips\n            uint8_t trips_metro = data->block[21].data[0];\n            uint8_t trips_ground = data->block[21].data[1];\n            furi_string_cat_printf(parsed_data, \"Trips: %d\\n\", trips_metro + trips_ground);\n            //trip time\n            uint32_t last_trip_timestamp = 0;\n            for(uint8_t i = 0; i < 3; i++) {\n                last_trip_timestamp = (last_trip_timestamp << 8) | data->block[21].data[4 - i];\n            }\n            DateTime last_trip = {0};\n            from_minutes_to_datetime(last_trip_timestamp + 24 * 60, &last_trip, 2010);\n            furi_string_cat_printf(\n                parsed_data,\n                \"Trip start: %02d.%02d.%04d %02d:%02d\\n\",\n                last_trip.day,\n                last_trip.month,\n                last_trip.year,\n                last_trip.hour,\n                last_trip.minute);\n            //validator\n            uint16_t validator = (data->block[20].data[5] << 8) | data->block[20].data[4];\n            furi_string_cat_printf(parsed_data, \"Validator: %d\\n\", validator);\n            //tariff\n            uint16_t fare = (data->block[20].data[7] << 8) | data->block[20].data[6];\n            furi_string_cat_printf(parsed_data, \"Tariff: %d rub\\n\", fare / 100);\n            //trips in metro\n            furi_string_cat_printf(parsed_data, \"Trips (Metro): %d\\n\", trips_metro);\n            //trips on ground\n            furi_string_cat_printf(parsed_data, \"Trips (Ground): %d\\n\", trips_ground);\n            //last payment\n            uint32_t last_payment_timestamp = 0;\n            for(uint8_t i = 0; i < 3; i++) {\n                last_payment_timestamp = (last_payment_timestamp << 8) |\n                                         data->block[18].data[4 - i];\n            }\n            DateTime last_payment_date = {0};\n            from_minutes_to_datetime(last_payment_timestamp + 24 * 60, &last_payment_date, 2010);\n            furi_string_cat_printf(\n                parsed_data,\n                \"Last pay: %02d.%02d.%04d %02d:%02d\\n\",\n                last_payment_date.day,\n                last_payment_date.month,\n                last_payment_date.year,\n                last_payment_date.hour,\n                last_payment_date.minute);\n            //Last payment amount.\n            uint16_t last_payment = ((data->block[18].data[10] << 16) |\n                                     (data->block[18].data[9] << 8) | (data->block[18].data[8])) /\n                                    100;\n            furi_string_cat_printf(parsed_data, \"Amount: %d rub\", last_payment);\n            furi_string_free(card_number_s);\n            furi_string_free(tmp_s);\n            //This is for 4K Plantains.\n        } else if(data->type == MfClassicType4k) {\n            //balance\n            uint32_t balance = 0;\n            for(uint8_t i = 0; i < 4; i++) {\n                balance = (balance << 8) | data->block[16].data[3 - i];\n            }\n            furi_string_cat_printf(parsed_data, \"Balance: %ld rub\\n\", balance / 100);\n\n            //trips\n            uint8_t trips_metro = data->block[21].data[0];\n            uint8_t trips_ground = data->block[21].data[1];\n            furi_string_cat_printf(parsed_data, \"Trips: %d\\n\", trips_metro + trips_ground);\n            //trip time\n            uint32_t last_trip_timestamp = 0;\n            for(uint8_t i = 0; i < 3; i++) {\n                last_trip_timestamp = (last_trip_timestamp << 8) | data->block[21].data[4 - i];\n            }\n            DateTime last_trip = {0};\n            from_minutes_to_datetime(last_trip_timestamp + 24 * 60, &last_trip, 2010);\n            furi_string_cat_printf(\n                parsed_data,\n                \"Trip start: %02d.%02d.%04d %02d:%02d\\n\",\n                last_trip.day,\n                last_trip.month,\n                last_trip.year,\n                last_trip.hour,\n                last_trip.minute);\n            //validator\n            uint16_t validator = (data->block[20].data[5] << 8) | data->block[20].data[4];\n            furi_string_cat_printf(parsed_data, \"Validator: %d\\n\", validator);\n            //tariff\n            uint16_t fare = (data->block[20].data[7] << 8) | data->block[20].data[6];\n            furi_string_cat_printf(parsed_data, \"Tariff: %d rub\\n\", fare / 100);\n            //trips in metro\n            furi_string_cat_printf(parsed_data, \"Trips (Metro): %d\\n\", trips_metro);\n            //trips on ground\n            furi_string_cat_printf(parsed_data, \"Trips (Ground): %d\\n\", trips_ground);\n            //last payment\n            uint32_t last_payment_timestamp = 0;\n            for(uint8_t i = 0; i < 3; i++) {\n                last_payment_timestamp = (last_payment_timestamp << 8) |\n                                         data->block[18].data[4 - i];\n            }\n            DateTime last_payment_date = {0};\n            from_minutes_to_datetime(last_payment_timestamp + 24 * 60, &last_payment_date, 2010);\n            furi_string_cat_printf(\n                parsed_data,\n                \"Last pay: %02d.%02d.%04d %02d:%02d\\n\",\n                last_payment_date.day,\n                last_payment_date.month,\n                last_payment_date.year,\n                last_payment_date.hour,\n                last_payment_date.minute);\n            //Last payment amount\n            uint16_t last_payment = ((data->block[18].data[10] << 16) |\n                                     (data->block[18].data[9] << 8) | (data->block[18].data[8])) /\n                                    100;\n            furi_string_cat_printf(parsed_data, \"Amount: %d rub\", last_payment);\n            furi_string_free(card_number_s);\n            furi_string_free(tmp_s);\n        }\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin plantain_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = plantain_verify,\n    .read = plantain_read,\n    .parse = plantain_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor plantain_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &plantain_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* plantain_plugin_ep(void) {\n    return &plantain_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/skylanders.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include <flipper_format/flipper_format.h>\n\n#define TAG      \"Skylanders\"\n#define POLY     UINT64_C(0x42f0e1eba9ea3693)\n#define TOP      UINT64_C(0x800000000000)\n#define UID_LEN  4\n#define KEY_MASK 0xFFFFFFFFFFFF\n\nstatic const uint64_t skylanders_key = 0x4b0b20107ccb;\n\nstatic const char* nfc_resources_header = \"Flipper NFC resources\";\nstatic const uint32_t nfc_resources_file_version = 1;\n\nuint64_t crc64_like(uint64_t result, uint8_t sector) {\n    result ^= (uint64_t)sector << 40;\n    for(int i = 0; i < 8; i++) {\n        result = (result & TOP) ? (result << 1) ^ POLY : result << 1;\n    }\n    return result;\n}\n\nuint64_t taghash(uint32_t uid) {\n    uint64_t result = 0x9AE903260CC4;\n    uint8_t uidBytes[UID_LEN] = {0};\n    memcpy(uidBytes, &uid, UID_LEN);\n\n    for(int i = 0; i < UID_LEN; i++) {\n        result = crc64_like(result, uidBytes[i]);\n    }\n    return result;\n}\n\nstatic bool skylanders_search_data(\n    Storage* storage,\n    const char* file_name,\n    FuriString* key,\n    FuriString* data) {\n    bool parsed = false;\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n\n    do {\n        // Open file\n        if(!flipper_format_file_open_existing(file, file_name)) break;\n        // Read file header and version\n        uint32_t version = 0;\n        if(!flipper_format_read_header(file, temp_str, &version)) break;\n        if(furi_string_cmp_str(temp_str, nfc_resources_header) ||\n           (version != nfc_resources_file_version))\n            break;\n        if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break;\n        parsed = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    flipper_format_free(file);\n    return parsed;\n}\n\nbool skylanders_get_name(Storage* storage, uint16_t id, FuriString* name) {\n    bool parsed = false;\n    FuriString* key;\n    key = furi_string_alloc_printf(\"%04X\", id);\n    if(skylanders_search_data(storage, EXT_PATH(\"nfc/assets/skylanders.nfc\"), key, name)) {\n        parsed = true;\n    }\n    furi_string_free(key);\n    return parsed;\n}\n\nbool skylanders_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t verify_sector = 0;\n        uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n        MfClassicKey key = {};\n        bit_lib_num_to_bytes_be(skylanders_key, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool skylanders_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    size_t* uid_len = 0;\n    const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len);\n    uint32_t uid = 0;\n    memcpy(&uid, uid_bytes, sizeof(uid));\n    uint64_t hash = taghash(uid);\n\n    do {\n        MfClassicType type = MfClassicType1k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            if(i == 0) {\n                bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data);\n                FURI_BIT_SET(keys.key_a_mask, i);\n            } else {\n                uint64_t sectorhash = crc64_like(hash, i);\n                uint64_t key = sectorhash & KEY_MASK;\n                uint8_t* keyBytes = (uint8_t*)&key;\n                memcpy(keys.key_a[i].data, keyBytes, sizeof(MfClassicKey));\n                FURI_BIT_SET(keys.key_a_mask, i);\n                memset(keys.key_b[i].data, 0, sizeof(MfClassicKey));\n                FURI_BIT_SET(keys.key_b_mask, i);\n            }\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = mf_classic_is_card_read(data);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n    FuriString* name = furi_string_alloc();\n\n    do {\n        // verify key\n        const uint8_t verify_sector = 0;\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, verify_sector);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        if(key != skylanders_key) break;\n\n        const uint16_t id = data->block[1].data[1] << 8 | data->block[1].data[0];\n        if(id == 0) break;\n\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n\n        bool success = skylanders_get_name(storage, id, name);\n\n        furi_record_close(RECORD_STORAGE);\n        if(!success) break;\n\n        furi_string_printf(parsed_data, \"\\e#Skylanders\\n%s\", furi_string_get_cstr(name));\n\n        parsed = true;\n\n    } while(false);\n\n    furi_string_free(name);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin skylanders_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = skylanders_verify,\n    .read = skylanders_read,\n    .parse = skylanders_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor skylanders_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &skylanders_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* skylanders_plugin_ep(void) {\n    return &skylanders_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/social_moscow.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n#include <core/check.h>\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include \"../../api/mosgortrans/mosgortrans_util.h\"\n#include \"furi_hal_rtc.h\"\n\n#define TAG \"Social_Moscow\"\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\ntypedef struct {\n    const MfClassicKeyPair* keys;\n    uint32_t data_sector;\n} SocialMoscowCardConfig;\n\nstatic const MfClassicKeyPair social_moscow_1k_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025},\n    {.a = 0x2735fc181807, .b = 0xbf23a53c1f63},\n    {.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368},\n    {.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f},\n    {.a = 0x73068f118c13, .b = 0x2b7f3253fac5},\n    {.a = 0x186d8c4b93f9, .b = 0x9f131d8c2057},\n    {.a = 0x3a4bba8adaf0, .b = 0x67362d90f973},\n    {.a = 0x8765b17968a2, .b = 0x6202a38f69e2},\n    {.a = 0x40ead80721ce, .b = 0x100533b89331},\n    {.a = 0x0db5e6523f7c, .b = 0x653a87594079},\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026},\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026},\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026},\n    {.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368},\n    {.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f},\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}};\n\nstatic const MfClassicKeyPair social_moscow_4k_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //1\n    {.a = 0x2735fc181807, .b = 0xbf23a53c1f63}, //2\n    {.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //3\n    {.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //4\n    {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, //5\n    {.a = 0x186d8c4b93f9, .b = 0x9f131d8c2057}, //6\n    {.a = 0x3a4bba8adaf0, .b = 0x67362d90f973}, //7\n    {.a = 0x8765b17968a2, .b = 0x6202a38f69e2}, //8\n    {.a = 0x40ead80721ce, .b = 0x100533b89331}, //9\n    {.a = 0x0db5e6523f7c, .b = 0x653a87594079}, //10\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //11\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //12\n    {.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //13\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //14\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //15\n    {.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //16\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //17\n    {.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //18\n    {.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //19\n    {.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //20\n    {.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //21\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //22\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //23\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //24\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //25\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //26\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //27\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //28\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //29\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //30\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //31\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //32\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //33\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //34\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //35\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //36\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //37\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //38\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //39\n    {.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //40\n};\n\nstatic bool social_moscow_get_card_config(SocialMoscowCardConfig* config, MfClassicType type) {\n    bool success = true;\n    if(type == MfClassicType1k) {\n        config->data_sector = 15;\n        config->keys = social_moscow_1k_keys;\n    } else if(type == MfClassicType4k) {\n        config->data_sector = 15;\n        config->keys = social_moscow_4k_keys;\n    } else {\n        success = false;\n    }\n\n    return success;\n}\n\nstatic bool social_moscow_verify_type(Nfc* nfc, MfClassicType type) {\n    bool verified = false;\n\n    do {\n        SocialMoscowCardConfig cfg = {};\n        if(!social_moscow_get_card_config(&cfg, type)) break;\n\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %lu\", cfg.data_sector);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n        FURI_LOG_D(TAG, \"Verify success!\");\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool social_moscow_verify(Nfc* nfc) {\n    return social_moscow_verify_type(nfc, MfClassicType1k) ||\n           social_moscow_verify_type(nfc, MfClassicType4k);\n}\n\nstatic bool social_moscow_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicType4k;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        SocialMoscowCardConfig cfg = {};\n        if(!social_moscow_get_card_config(&cfg, data->type)) break;\n\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = (error == MfClassicErrorNone);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic uint8_t calculate_luhn(uint64_t number) {\n    // https://en.wikipedia.org/wiki/Luhn_algorithm\n    // Drop existing check digit to form payload\n    uint64_t payload = number / 10;\n    int sum = 0;\n    int position = 0;\n\n    while(payload > 0) {\n        int digit = payload % 10;\n        if(position % 2 == 0) {\n            digit *= 2;\n        }\n        if(digit > 9) {\n            digit = (digit / 10) + (digit % 10);\n        }\n        sum += digit;\n        payload /= 10;\n        position++;\n    }\n\n    return (10 - (sum % 10)) % 10;\n}\n\nstatic uint64_t hex_num(uint64_t hex) {\n    uint64_t result = 0;\n    for(uint8_t i = 0; i < 8; ++i) {\n        uint8_t half_byte = hex & 0x0F;\n        uint64_t num = 0;\n        for(uint8_t j = 0; j < 4; ++j) {\n            num += (half_byte & 0x1) * (1 << j);\n            half_byte = half_byte >> 1;\n        }\n        result += num * pow(10, i);\n        hex = hex >> 4;\n    }\n    return result;\n}\n\nstatic bool social_moscow_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        SocialMoscowCardConfig cfg = {};\n        if(!social_moscow_get_card_config(&cfg, data->type)) break;\n\n        // Verify key\n        const MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector);\n\n        const uint64_t key_a =\n            bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));\n        const uint64_t key_b =\n            bit_lib_bytes_to_num_be(sec_tr->key_b.data, COUNT_OF(sec_tr->key_b.data));\n        if((key_a != cfg.keys[cfg.data_sector].a) || (key_b != cfg.keys[cfg.data_sector].b)) break;\n\n        uint32_t card_code = bit_lib_get_bits_32(data->block[60].data, 8, 24);\n        uint8_t card_region = bit_lib_get_bits(data->block[60].data, 32, 8);\n        uint64_t card_number = bit_lib_get_bits_64(data->block[60].data, 40, 40);\n        uint8_t card_control = bit_lib_get_bits(data->block[60].data, 80, 4);\n        uint64_t omc_number = bit_lib_get_bits_64(data->block[21].data, 8, 64);\n        uint8_t year = data->block[60].data[11];\n        uint8_t month = data->block[60].data[12];\n\n        uint64_t number = hex_num(card_control) + hex_num(card_number) * 10 +\n                          hex_num(card_region) * 10 * 10000000000 +\n                          hex_num(card_code) * 10 * 10000000000 * 100;\n\n        uint8_t luhn = calculate_luhn(number);\n        if(luhn != card_control) break;\n\n        FuriString* metro_result = furi_string_alloc();\n        FuriString* ground_result = furi_string_alloc();\n        bool is_metro_data_present =\n            mosgortrans_parse_transport_block(&data->block[4], metro_result);\n        bool is_ground_data_present =\n            mosgortrans_parse_transport_block(&data->block[16], ground_result);\n        furi_string_cat_printf(\n            parsed_data,\n            \"\\e#Social \\ecard\\nNumber: %lx %x %llx %x\\nOMC: %llx\\nValid for: %02x/%02x %02x%02x\\n\",\n            card_code,\n            card_region,\n            card_number,\n            card_control,\n            omc_number,\n            month,\n            year,\n            data->block[60].data[13],\n            data->block[60].data[14]);\n        if(is_metro_data_present && !furi_string_empty(metro_result)) {\n            render_section_header(parsed_data, \"Metro\", 22, 21);\n            furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(metro_result));\n        }\n        if(is_ground_data_present && !furi_string_empty(ground_result)) {\n            render_section_header(parsed_data, \"Ground\", 21, 20);\n            furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(ground_result));\n        }\n        furi_string_free(ground_result);\n        furi_string_free(metro_result);\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin social_moscow_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = social_moscow_verify,\n    .read = social_moscow_read,\n    .parse = social_moscow_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor social_moscow_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &social_moscow_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* social_moscow_plugin_ep() {\n    return &social_moscow_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/troika.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n#include <core/check.h>\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n#include \"../../api/mosgortrans/mosgortrans_util.h\"\n\n#define TAG \"Troika\"\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\ntypedef struct {\n    const MfClassicKeyPair* keys;\n    uint32_t data_sector;\n} TroikaCardConfig;\n\nstatic const MfClassicKeyPair troika_1k_keys[] = {\n    {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58},\n    {.a = 0xa82607b01c0d, .b = 0x2910989b6880},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0x73068f118c13, .b = 0x2b7f3253fac5},\n    {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dba},\n    {.a = 0xa73f5dc1d333, .b = 0xe35173494a81},\n    {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763},\n    {.a = 0x9becdf3d9273, .b = 0xf8493407799d},\n    {.a = 0x08b386463229, .b = 0x5efbaecef46b},\n    {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0},\n    {.a = 0xa82607b01c0d, .b = 0x2910989b6880},\n    {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n};\n\nstatic const MfClassicKeyPair troika_4k_keys[] = {\n    {.a = 0xEC29806D9738, .b = 0xFBF225DC5D58}, //1\n    {.a = 0xA0A1A2A3A4A5, .b = 0x7DE02A7F6025}, //2\n    {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //3\n    {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //4\n    {.a = 0x73068F118C13, .b = 0x2B7F3253FAC5}, //5\n    {.a = 0xFBC2793D540B, .b = 0xD3A297DC2698}, //6\n    {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //7\n    {.a = 0xAE3D65A3DAD4, .b = 0x0F1C63013DBA}, //8\n    {.a = 0xA73F5DC1D333, .b = 0xE35173494A81}, //9\n    {.a = 0x69A32F1C2F19, .b = 0x6B8BD9860763}, //10\n    {.a = 0x9BECDF3D9273, .b = 0xF8493407799D}, //11\n    {.a = 0x08B386463229, .b = 0x5EFBAECEF46B}, //12\n    {.a = 0xCD4C61C26E3D, .b = 0x31C7610DE3B0}, //13\n    {.a = 0xA82607B01C0D, .b = 0x2910989B6880}, //14\n    {.a = 0x0E8F64340BA4, .b = 0x4ACEC1205D75}, //15\n    {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //16\n    {.a = 0x6B02733BB6EC, .b = 0x7038CD25C408}, //17\n    {.a = 0x403D706BA880, .b = 0xB39D19A280DF}, //18\n    {.a = 0xC11F4597EFB5, .b = 0x70D901648CB9}, //19\n    {.a = 0x0DB520C78C1C, .b = 0x73E5B9D9D3A4}, //20\n    {.a = 0x3EBCE0925B2F, .b = 0x372CC880F216}, //21\n    {.a = 0x16A27AF45407, .b = 0x9868925175BA}, //22\n    {.a = 0xABA208516740, .b = 0xCE26ECB95252}, //23\n    {.a = 0xCD64E567ABCD, .b = 0x8F79C4FD8A01}, //24\n    {.a = 0x764CD061F1E6, .b = 0xA74332F74994}, //25\n    {.a = 0x1CC219E9FEC1, .b = 0xB90DE525CEB6}, //26\n    {.a = 0x2FE3CB83EA43, .b = 0xFBA88F109B32}, //27\n    {.a = 0x07894FFEC1D6, .b = 0xEFCB0E689DB3}, //28\n    {.a = 0x04C297B91308, .b = 0xC8454C154CB5}, //29\n    {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30\n    {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31\n    {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32\n    {.a = 0x7A86AA203788, .b = 0xE41242278CA2}, //33\n    {.a = 0xAFCEF64C9913, .b = 0x9DB96DCA4324}, //34\n    {.a = 0x04EAA462F70B, .b = 0xAC17B93E2FAE}, //35\n    {.a = 0xE734C210F27E, .b = 0x29BA8C3E9FDA}, //36\n    {.a = 0xD5524F591EED, .b = 0x5DAF42861B4D}, //37\n    {.a = 0xE4821A377B75, .b = 0xE8709E486465}, //38\n    {.a = 0x518DC6EEA089, .b = 0x97C64AC98CA4}, //39\n    {.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40\n};\n\nstatic bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {\n    bool success = true;\n\n    if(type == MfClassicType1k) {\n        config->data_sector = 11;\n        config->keys = troika_1k_keys;\n    } else if(type == MfClassicType4k) {\n        config->data_sector = 8; // Further testing needed\n        config->keys = troika_4k_keys;\n    } else {\n        success = false;\n    }\n\n    return success;\n}\n\nstatic bool troika_verify_type(Nfc* nfc, MfClassicType type) {\n    bool verified = false;\n\n    do {\n        TroikaCardConfig cfg = {};\n        if(!troika_get_card_config(&cfg, type)) break;\n\n        const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %lu\", cfg.data_sector);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n        FURI_LOG_D(TAG, \"Verify success!\");\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool troika_verify(Nfc* nfc) {\n    return troika_verify_type(nfc, MfClassicType1k) || troika_verify_type(nfc, MfClassicType4k);\n}\n\nstatic bool troika_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        TroikaCardConfig cfg = {};\n        if(!troika_get_card_config(&cfg, data->type)) break;\n\n        MfClassicDeviceKeys keys = {\n            .key_a_mask = 0,\n            .key_b_mask = 0,\n        };\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = (error == MfClassicErrorNone);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        TroikaCardConfig cfg = {};\n        if(!troika_get_card_config(&cfg, data->type)) break;\n\n        // Verify key\n        const MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector);\n\n        const uint64_t key =\n            bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));\n        if(key != cfg.keys[cfg.data_sector].a) break;\n\n        FuriString* metro_result = furi_string_alloc();\n        FuriString* ground_result = furi_string_alloc();\n        FuriString* tat_result = furi_string_alloc();\n\n        bool is_metro_data_present =\n            mosgortrans_parse_transport_block(&data->block[32], metro_result);\n        bool is_ground_data_present =\n            mosgortrans_parse_transport_block(&data->block[28], ground_result);\n        bool is_tat_data_present = mosgortrans_parse_transport_block(&data->block[16], tat_result);\n\n        furi_string_cat_printf(parsed_data, \"\\e#Troyka card\\n\");\n        if(is_metro_data_present && !furi_string_empty(metro_result)) {\n            render_section_header(parsed_data, \"Metro\", 22, 21);\n            furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(metro_result));\n        }\n\n        if(is_ground_data_present && !furi_string_empty(ground_result)) {\n            render_section_header(parsed_data, \"Ediny\", 22, 22);\n            furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(ground_result));\n        }\n\n        if(is_tat_data_present && !furi_string_empty(tat_result)) {\n            render_section_header(parsed_data, \"TAT\", 24, 23);\n            furi_string_cat_printf(parsed_data, \"%s\\n\", furi_string_get_cstr(tat_result));\n        }\n\n        furi_string_free(tat_result);\n        furi_string_free(ground_result);\n        furi_string_free(metro_result);\n\n        parsed = is_metro_data_present || is_ground_data_present || is_tat_data_present;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin troika_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = troika_verify,\n    .read = troika_read,\n    .parse = troika_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor troika_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &troika_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* troika_plugin_ep(void) {\n    return &troika_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/trt.c",
    "content": "// Flipper Zero parser for for Tianjin Railway Transit (TRT)\n// https://en.wikipedia.org/wiki/Tianjin_Metro\n// Reverse engineering and parser development by @Torron (Github: @zinongli)\n\n#include \"nfc_supported_card_plugin.h\"\n#include <flipper_application.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n#include <bit_lib.h>\n\n#define TAG                       \"TrtParser\"\n#define LATEST_SALE_MARKER        0x02\n#define FULL_SALE_TIME_STAMP_PAGE 0x09\n#define BALANCE_PAGE              0x08\n#define SALE_RECORD_TIME_STAMP_A  0x0C\n#define SALE_RECORD_TIME_STAMP_B  0x0E\n#define SALE_YEAR_OFFSET          2000\n\nstatic bool trt_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n    furi_assert(parsed_data);\n\n    const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);\n\n    bool parsed = false;\n\n    do {\n        uint8_t latest_sale_page = 0;\n\n        // Look for sale record signature\n        if(data->page[SALE_RECORD_TIME_STAMP_A].data[0] == LATEST_SALE_MARKER) {\n            latest_sale_page = SALE_RECORD_TIME_STAMP_A;\n        } else if(data->page[SALE_RECORD_TIME_STAMP_B].data[0] == LATEST_SALE_MARKER) {\n            latest_sale_page = SALE_RECORD_TIME_STAMP_B;\n        } else {\n            break;\n        }\n\n        // Check if the sale record was backed up\n        const uint8_t* partial_record_pointer = &data->page[latest_sale_page - 1].data[0];\n        const uint8_t* full_record_pointer = &data->page[FULL_SALE_TIME_STAMP_PAGE].data[0];\n        uint32_t latest_sale_record = bit_lib_get_bits_32(partial_record_pointer, 3, 20);\n        uint32_t latest_sale_full_record = bit_lib_get_bits_32(full_record_pointer, 0, 27);\n        if(latest_sale_record != (latest_sale_full_record & 0xFFFFF))\n            break; // check if the copy matches\n        if((latest_sale_record == 0) || (latest_sale_full_record == 0))\n            break; // prevent false positive\n\n        // Parse date\n        // yyy yyyymmmm dddddhhh hhnnnnnn\n        uint16_t sale_year = ((latest_sale_full_record & 0x7F00000) >> 20) + SALE_YEAR_OFFSET;\n        uint8_t sale_month = (latest_sale_full_record & 0xF0000) >> 16;\n        uint8_t sale_day = (latest_sale_full_record & 0xF800) >> 11;\n        uint8_t sale_hour = (latest_sale_full_record & 0x7C0) >> 6;\n        uint8_t sale_minute = latest_sale_full_record & 0x3F;\n\n        // Parse balance\n        uint16_t balance = bit_lib_get_bits_16(&data->page[BALANCE_PAGE].data[2], 0, 16);\n        uint16_t balance_yuan = balance / 100;\n        uint8_t balance_cent = balance % 100;\n\n        // Format string for rendering\n        furi_string_cat_printf(parsed_data, \"\\e#TRT Tianjin Metro\\n\");\n        furi_string_cat_printf(parsed_data, \"Single-Use Ticket\\n\");\n        furi_string_cat_printf(parsed_data, \"Balance: %u.%02u RMB\\n\", balance_yuan, balance_cent);\n        furi_string_cat_printf(\n            parsed_data,\n            \"Sale Date: \\n%04u-%02d-%02d %02d:%02d\",\n            sale_year,\n            sale_month,\n            sale_day,\n            sale_hour,\n            sale_minute);\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin trt_plugin = {\n    .protocol = NfcProtocolMfUltralight,\n    .verify = NULL,\n    .read = NULL,\n    .parse = trt_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor trt_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &trt_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* trt_plugin_ep(void) {\n    return &trt_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/two_cities.c",
    "content": "#include \"nfc_supported_card_plugin.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"TwoCities\"\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\nstatic const MfClassicKeyPair two_cities_4k_keys[] = {\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763},\n    {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56},\n    {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff},\n    {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a},\n    {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3},\n    {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056},\n    {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf},\n    {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406},\n    {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA},\n    {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3},\n    {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3},\n    {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9},\n    {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},\n    {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a},\n    {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},\n};\n\nbool two_cities_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t verify_sector = 4;\n        uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);\n        FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector);\n\n        MfClassicKey key = {};\n        bit_lib_num_to_bytes_be(two_cities_4k_keys[verify_sector].a, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error =\n            mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", block_num, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool two_cities_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        MfClassicDeviceKeys keys = {};\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                two_cities_4k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                two_cities_4k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error == MfClassicErrorNotPresent) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = (error == MfClassicErrorNone);\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify key\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);\n        uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);\n        if(key != two_cities_4k_keys[4].a) return false;\n\n        // =====\n        // PLANTAIN\n        // =====\n\n        // Point to block 0 of sector 4, value 0\n        const uint8_t* temp_ptr = data->block[16].data;\n        // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t\n        // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal\n        uint32_t balance =\n            ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;\n        // Read card number\n        // Point to block 0 of sector 0, value 0\n        temp_ptr = data->block[0].data;\n        // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t\n        // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal\n        uint8_t card_number_arr[7];\n        for(size_t i = 0; i < 7; i++) {\n            card_number_arr[i] = temp_ptr[6 - i];\n        }\n        // Copy card number to uint64_t\n        uint64_t card_number = 0;\n        for(size_t i = 0; i < 7; i++) {\n            card_number = (card_number << 8) | card_number_arr[i];\n        }\n\n        // =====\n        // --PLANTAIN--\n        // =====\n        // TROIKA\n        // =====\n\n        const uint8_t* troika_temp_ptr = &data->block[33].data[5];\n        uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25;\n        troika_temp_ptr = &data->block[32].data[2];\n        uint32_t troika_number = 0;\n        for(size_t i = 0; i < 4; i++) {\n            troika_number <<= 8;\n            troika_number |= troika_temp_ptr[i];\n        }\n        troika_number >>= 4;\n\n        furi_string_printf(\n            parsed_data,\n            \"\\e#Troika+Plantain\\nPN: %lluX\\nPB: %lu rur.\\nTN: %lu\\nTB: %u rur.\\n\",\n            card_number,\n            balance,\n            troika_number,\n            troika_balance);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin two_cities_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = two_cities_verify,\n    .read = two_cities_read,\n    .parse = two_cities_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor two_cities_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &two_cities_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* two_cities_plugin_ep(void) {\n    return &two_cities_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/umarsh.c",
    "content": "/*\n * Parser for Umarsh card (Russia).\n *\n * Copyright 2023 Leptoptilos <leptoptilos@icloud.com>\n * Thanks https://github.com/krolchonok for the provided dumps and their analysis\n *\n * Note: All meaningful data is stored in sectors 0, 8 and 12, reading data \n * from which is possible only with the B key. The key B for these sectors \n * is unique for each card. To get it, you should use a nested attack.\n * More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh\n *\n * This program is free software: you can redistribute it and/or modify it\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"nfc_supported_card_plugin.h\"\n\n#include \"protocols/mf_classic/mf_classic.h\"\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#include <datetime/datetime.h>\n\n#define TAG \"Umarsh\"\n\nbool parse_datetime(uint16_t date, DateTime* result) {\n    result->year = 2000 + (date >> 9);\n    result->month = date >> 5 & 0x0F;\n    result->day = date & 0x1F;\n    return date != 0;\n}\n\nstatic bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify card type\n        if(data->type != MfClassicType1k) break;\n\n        const uint8_t ticket_sector = 8;\n\n        const uint8_t ticket_sector_start_block_number =\n            mf_classic_get_first_block_num_of_sector(ticket_sector);\n\n        // Validate specific for Umarsh ticket sector header\n        const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0];\n\n        const uint32_t header_part_0 = bit_lib_bytes_to_num_be(block_start_ptr, 4);\n        const uint32_t header_part_1 = bit_lib_bytes_to_num_be(block_start_ptr + 4, 4);\n        if((header_part_0 + header_part_1) != 0xFFFFFFFF) break;\n\n        // Data parsing from block 1\n        block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0];\n        const uint16_t expiry_date = bit_lib_bytes_to_num_be(block_start_ptr + 1, 2);\n        const uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) |\n                                      (block_start_ptr[12] & 0x0F);\n        const uint8_t refill_counter = bit_lib_bytes_to_num_be(block_start_ptr + 7, 1);\n        const uint32_t card_number = bit_lib_bytes_to_num_be(block_start_ptr + 8, 4) & 0x3FFFFFFF;\n\n        if(card_number == 0) break;\n\n        // Data parsing from block 2\n        block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0];\n        const uint16_t valid_to = bit_lib_bytes_to_num_be(block_start_ptr, 2);\n        const uint32_t terminal_number = bit_lib_bytes_to_num_be(block_start_ptr + 3, 3);\n        const uint16_t last_refill_date = bit_lib_bytes_to_num_be(block_start_ptr + 6, 2);\n        const uint16_t balance_rub = (bit_lib_bytes_to_num_be(block_start_ptr + 8, 2)) & 0x7FFF;\n        const uint8_t balance_kop = bit_lib_bytes_to_num_be(block_start_ptr + 10, 1) & 0x7F;\n\n        DateTime expiry_datetime;\n        bool is_expiry_datetime_valid = parse_datetime(expiry_date, &expiry_datetime);\n\n        DateTime valid_to_datetime;\n        bool is_valid_to_datetime_valid = parse_datetime(valid_to, &valid_to_datetime);\n\n        DateTime last_refill_datetime;\n        bool is_last_refill_datetime_valid =\n            parse_datetime(last_refill_date, &last_refill_datetime);\n\n        furi_string_cat_printf(\n            parsed_data,\n            \"\\e#Umarsh\\nCard number: %lu\\nRegion: %02u\\nTerminal number: %lu\\nRefill counter: %u\\nBalance: %u.%02u RUR\",\n            card_number,\n            region_number,\n            terminal_number,\n            refill_counter,\n            balance_rub,\n            balance_kop);\n\n        if(is_expiry_datetime_valid)\n            furi_string_cat_printf(\n                parsed_data,\n                \"\\nExpires: %02u.%02u.%u\",\n                expiry_datetime.day,\n                expiry_datetime.month,\n                expiry_datetime.year);\n        if(is_valid_to_datetime_valid)\n            furi_string_cat_printf(\n                parsed_data,\n                \"\\nValid to: %02u.%02u.%u\",\n                valid_to_datetime.day,\n                valid_to_datetime.month,\n                valid_to_datetime.year);\n        if(is_last_refill_datetime_valid)\n            furi_string_cat_printf(\n                parsed_data,\n                \"\\nLast refill: %02u.%02u.%u\",\n                last_refill_datetime.day,\n                last_refill_datetime.month,\n                last_refill_datetime.year);\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin umarsh_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = NULL,\n    .read = NULL,\n    .parse = umarsh_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor umarsh_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &umarsh_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* umarsh_plugin_ep(void) {\n    return &umarsh_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/plugins/supported_cards/washcity.c",
    "content": "/*\n * Parser for WashCity MarkItaly Card (Europe).\n *\n * Copyright 2023 Filipe Polido (YaBaPT) <polido@gmail.com>\n * \n * Based on MetroMoney by Leptoptilos <leptoptilos@icloud.com>\n *\n * This program is free software: you can redistribute it and/or modify it\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"nfc_supported_card_plugin.h\"\n\n#include \"protocols/mf_classic/mf_classic.h\"\n#include <flipper_application/flipper_application.h>\n\n#include <nfc/nfc_device.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>\n\n#define TAG \"WashCity\"\n\ntypedef struct {\n    uint64_t a;\n    uint64_t b;\n} MfClassicKeyPair;\n\nstatic const MfClassicKeyPair washcity_1k_keys[] = {\n    {.a = 0xA0A1A2A3A4A5, .b = 0x010155010100}, // Sector 00\n    {.a = 0xC78A3D0E1BCD, .b = 0xFFFFFFFFFFFF}, // Sector 01\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 02\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 03\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 04\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 05\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 06\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 07\n    {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 08\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 09\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 10\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 11\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 12\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 13\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 14\n    {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 15\n};\n\nstatic bool washcity_verify(Nfc* nfc) {\n    bool verified = false;\n\n    do {\n        const uint8_t verify_sector_number = 1;\n        const uint8_t verify_block_number =\n            mf_classic_get_first_block_num_of_sector(verify_sector_number);\n        FURI_LOG_D(TAG, \"Verifying sector %u\", verify_sector_number);\n\n        MfClassicKey key = {0};\n        bit_lib_num_to_bytes_be(\n            washcity_1k_keys[verify_sector_number].a, COUNT_OF(key.data), key.data);\n\n        MfClassicAuthContext auth_context;\n        MfClassicError error = mf_classic_poller_sync_auth(\n            nfc, verify_block_number, &key, MfClassicKeyTypeA, &auth_context);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %u: %d\", verify_block_number, error);\n            break;\n        }\n\n        verified = true;\n    } while(false);\n\n    return verified;\n}\n\nstatic bool washcity_read(Nfc* nfc, NfcDevice* device) {\n    furi_assert(nfc);\n    furi_assert(device);\n\n    bool is_read = false;\n\n    MfClassicData* data = mf_classic_alloc();\n    nfc_device_copy_data(device, NfcProtocolMfClassic, data);\n\n    do {\n        MfClassicType type = MfClassicTypeMini;\n        MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);\n        if(error != MfClassicErrorNone) break;\n\n        data->type = type;\n        if(type != MfClassicType1k) break;\n\n        MfClassicDeviceKeys keys = {\n            .key_a_mask = 0,\n            .key_b_mask = 0,\n        };\n        for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {\n            bit_lib_num_to_bytes_be(\n                washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);\n            FURI_BIT_SET(keys.key_a_mask, i);\n            bit_lib_num_to_bytes_be(\n                washcity_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);\n            FURI_BIT_SET(keys.key_b_mask, i);\n        }\n\n        error = mf_classic_poller_sync_read(nfc, &keys, data);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_W(TAG, \"Failed to read data\");\n            break;\n        }\n\n        nfc_device_set_data(device, NfcProtocolMfClassic, data);\n\n        is_read = true;\n    } while(false);\n\n    mf_classic_free(data);\n\n    return is_read;\n}\n\nstatic bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) {\n    furi_assert(device);\n\n    const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);\n\n    bool parsed = false;\n\n    do {\n        // Verify key\n        const uint8_t ticket_sector_number = 1;\n        const uint8_t ticket_block_number = 0;\n\n        const MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);\n\n        const uint64_t key =\n            bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));\n        if(key != washcity_1k_keys[ticket_sector_number].a) break;\n\n        // Parse data\n        const uint8_t start_block_num =\n            mf_classic_get_first_block_num_of_sector(ticket_sector_number);\n\n        const uint8_t* block_start_ptr =\n            &data->block[start_block_num + ticket_block_number].data[0];\n\n        uint32_t balance = bit_lib_bytes_to_num_be(block_start_ptr + 2, 2);\n\n        uint32_t balance_eur = balance / 100;\n        uint8_t balance_cents = balance % 100;\n\n        size_t uid_len = 0;\n        const uint8_t* uid = mf_classic_get_uid(data, &uid_len);\n\n        // Card Number is printed in HEX (equal to UID)\n        uint64_t card_number = bit_lib_bytes_to_num_be(uid, uid_len);\n\n        furi_string_printf(\n            parsed_data,\n            \"\\e#WashCity\\nCard number: %0*llX\\nBalance: %lu.%02u EUR\",\n            uid_len * 2,\n            card_number,\n            balance_eur,\n            balance_cents);\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\n/* Actual implementation of app<>plugin interface */\nstatic const NfcSupportedCardsPlugin washcity_plugin = {\n    .protocol = NfcProtocolMfClassic,\n    .verify = washcity_verify,\n    .read = washcity_read,\n    .parse = washcity_parse,\n};\n\n/* Plugin descriptor to comply with basic plugin specification */\nstatic const FlipperAppPluginDescriptor washcity_plugin_descriptor = {\n    .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,\n    .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,\n    .entry_point = &washcity_plugin,\n};\n\n/* Plugin entry point - must return a pointer to const descriptor  */\nconst FlipperAppPluginDescriptor* washcity_plugin_ep(void) {\n    return &washcity_plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/aid.nfc",
    "content": "Filetype: Flipper EMV resources\nVersion: 1\n# EMV Application ID code: Application ID name\nA00000000305076010: VISA ELO Credit\nA0000000031010: VISA Debit/Credit (Classic)\nA000000003101001: VISA Credit\nA000000003101002: VISA Debit\nA0000000032010: VISA Electron\nA0000000032020: VISA\nA0000000033010: VISA Interlink\nA0000000034010: VISA Specific\nA0000000035010: VISA Specific\nA0000000036010: Domestic Visa Cash\nA0000000036020: International Visa Cash\nA0000000038002: VISA Auth EMV-CAP (DPA)\nA0000000038010: VISA Plus\nA0000000039010: VISA Loyalty\nA000000003999910: VISA Proprietary ATM\nA00000000401: MasterCard PayPass\nA0000000041010: MasterCard Global\nA00000000410101213: MasterCard Credit\nA00000000410101215: MasterCard Credit\nA0000000042010: MasterCard Specific\nA0000000043010: MasterCard Specific\nA0000000043060: Maestro (Debit)\nA000000004306001: Maestro (Debit)\nA0000000044010: MasterCard Specific\nA0000000045010: MasterCard Specific\nA0000000046000: Cirrus\nA0000000048002: SecureCode EMV-CAP\nA0000000049999: MasterCard PayPass\nA0000000050001: Maestro UK\nA0000000050002: Solo\nA00000002401: Self Service\nA000000025: American Express\nA0000000250000: American Express\nA00000002501: American Express\nA000000025010402: American Express\nA000000025010701: ExpressPay\nA000000025010801: American Express\nA0000000291010: Link / American Express\nA0000000421010: Cartes Bancaire EMV Card\nA0000000426010: Apple Pay\nA00000006510: JCB\nA0000000651010: JCB J Smart Credit\nA00000006900: Moneo\nA000000077010000021000000000003B: Visa AEPN\nA000000098: Debit Card\nA0000000980848: Debit Card\nA0000001211010: Dankort VISA GEM Vision\nA0000001410001: PagoBANCOMAT\nA0000001523010: Discover, Pulse D Pas\nA0000001524010: Discover\nA0000001544442: Banricompras Debito\nA000000172950001: BAROC Taiwan\nA0000002281010: SPAN (M/Chip)\nA0000002282010: SPAN (VIS)\nA0000002771010: INTERAC\nA00000031510100528: Currence PuC\nA0000003156020: Chipknip\nA0000003591010028001: Girocard EAPS\nA0000003710001: InterSwitch Verve Card\nA0000004540010: Etranzact Genesis Card\nA0000004540011: Etranzact Genesis Card 2\nA0000004766C: GOOGLE_PAYMENT\nA0000005241010: RuPay\nA0000006472F0001: FIDO U2F\nA0000006723010: TROY chip credit card\nA0000006723020: TROY chip debit card\nA0000007705850: XTRAPOWER\nB012345678: Maestro TEST\nD27600002545500100: Girocard\nD5780000021010: Bankaxept\nF0000000030001: BRADESCO\nA000000003000000: (VISA) Card Manager\nA000000003534441: Schlumberger SD\nA0000000035350: Security Domain\nA000000003535041: Security Domain\nA0000000040000: MasterCard Card Manager\nA000000018434D: Gemplus card manager\nA000000018434D00: Gemplus Security Domain\nA0000000960200: Proton WISD\nA0000001510000: Global Platform SD\nA00000015153504341534400: CASD_AID\nA000000476A010: GSD_MANAGER_AID\nA000000476A110: GSD_MANAGER_AID\n315041592E5359532E4444463031: Visa PSE \n325041592E5359532E4444463031: Visa PPSE\nA0000000042203: MasterCard Specific\nA0000000045555: APDULogger\nA0000000090001FF44FF1289: Orange\nA0000000101030: Maestro-CH\nA00000001800: Gemplus\nA0000000181001: gemplus util packages\nA000000025010104: American Express\nA00000002949034010100001: HSBC\nA00000002949282010100000: Barclay\nA00000005945430100: Girocard Electronic Cash\nA0000000980840: Visa Common Debit\nA0000001570010: AMEX\nA0000001570020: MasterCard\nA0000001570021: Maestro\nA0000001570022: Maestro\nA0000001570023: CASH\nA0000001570030: VISA\nA0000001570031: VISA\nA0000001570040: JCB\nA0000001570050: Postcard\nA0000001570051: Postcard\nA0000001570100: MCard\nA0000001570104: MyOne\nA000000157010C: WIRCard\nA000000157010D: Power Card\nA0000001574443: DINERS CLUB\nA0000001574444: Supercard Plus\nA00000022820101010: SPAN\nA000000308000010000100: ID-ONE PIV BIO\nA0000003241010: Discover Zip\nA000000333010101: UnionPay Debit\nA000000333010102: UnionPay Credit\nA000000333010103: UnionPay Quasi Credit\nA000000333010106: UnionPay Electronic Cash\nA000000333010108: U.S. UnionPay Common Debit\nA000000337102000: Classic\nA000000337101001: Prepaye Online\nA000000337102001: Prepaye Possibile Offiline\nA000000337601001: Porte Monnaie Electronique\nA0000006581010: MIR Credit\nA0000006581011: MIR Credit\nA0000006582010: MIR Debit\nD040000001000002: Paylife Quick IEP\nD040000002000002: RFU\nD040000003000002: POS\nD040000004000002: ATM\nD04000000B000002: Retail\nD04000000C000002: Bank_Data\nD04000000D000002: Shopping\nD040000013000001: DF_UNI_Kepler1\nD040000013000001: DF_Schüler1\nD040000013000002: DF_UNI_Kepler2\nD040000013000002: DF_Schüler2\nD040000014000001: DF_Mensa\nD040000015000001: DF_UNI_Ausweis\nD040000015000001: DF_Ausweis\nD0400000190001: EMV ATM Maestro\nD0400000190002: EMV POS Maestro\nD0400000190003: EMV ATM MasterCard\nD0400000190004: EMV POS MasterCard\nD276000025: Girocard\nD27600002547410100: Girocard ATM\nD7560000010101: Reka Card\nD7560000300101: M Budget"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/country_code.nfc",
    "content": "Filetype: Flipper EMV resources\nVersion: 1\n# EMV country code: country name\n0004: AFG\n0008: ALB\n0010: ATA\n0012: DZA\n0016: ASM\n0020: AND\n0024: AGO\n0028: ATG\n0031: AZE\n0032: ARG\n0036: AUS\n0040: AUT\n0044: BHS\n0048: BHR\n0050: BGD\n0051: ARM\n0052: BRB\n0056: BEL\n0060: BMU\n0064: BTN\n0068: BOL\n0070: BIH\n0072: BWA\n0074: BVT\n0076: BRA\n0084: BLZ\n0086: IOT\n0090: SLB\n0092: VGB\n0096: BRN\n0100: BGR\n0104: MMR\n0108: BDI\n0112: BLR\n0116: KHM\n0120: CMR\n0124: CAN\n0132: CPV\n0136: CYM\n0140: CAF\n0144: LKA\n0148: TCD\n0152: CHL\n0156: CHN\n0158: TWN\n0162: CXR\n0166: CCK\n0170: COL\n0174: COM\n0175: MYT\n0178: COG\n0180: COD\n0184: COK\n0188: CRI\n0191: HRV\n0192: CUB\n0196: CYP\n0203: CZE\n0204: BEN\n0208: DNK\n0212: DMA\n0214: DOM\n0218: ECU\n0222: SLV\n0226: GNQ\n0231: ETH\n0232: ERI\n0233: EST\n0234: FRO\n0238: FLK\n0239: SGS\n0242: FJI\n0246: FIN\n0248: ALA\n0250: FRA\n0254: GUF\n0258: PYF\n0260: ATF\n0262: DJI\n0266: GAB\n0268: GEO\n0270: GMB\n0275: PSE\n0276: DEU\n0288: GHA\n0292: GIB\n0296: KIR\n0300: GRC\n0304: GRL\n0308: GRD\n0312: GLP\n0316: GUM\n0320: GTM\n0324: GIN\n0328: GUY\n0332: HTI\n0334: HMD\n0336: VAT\n0340: HND\n0344: HKG\n0348: HUN\n0352: ISL\n0356: IND\n0360: IDN\n0364: IRN\n0368: IRQ\n0372: IRL\n0376: ISR\n0380: ITA\n0384: CIV\n0388: JAM\n0392: JPN\n0398: KAZ\n0400: JOR\n0404: KEN\n0408: PRK\n0410: KOR\n0414: KWT\n0417: KGZ\n0418: LAO\n0422: LBN\n0426: LSO\n0428: LVA\n0430: LBR\n0434: LBY\n0438: LIE\n0440: LTU\n0442: LUX\n0446: MAC\n0450: MDG\n0454: MWI\n0458: MYS\n0462: MDV\n0466: MLI\n0470: MLT\n0474: MTQ\n0478: MRT\n0480: MUS\n0484: MEX\n0492: MCO\n0496: MNG\n0498: MDA\n0499: MNE\n0500: MSR\n0504: MAR\n0508: MOZ\n0512: OMN\n0516: NAM\n0520: NRU\n0524: NPL\n0528: NLD\n0531: CUW\n0533: ABW\n0534: SXM\n0535: BES\n0540: NCL\n0548: VUT\n0554: NZL\n0558: NIC\n0562: NER\n0566: NGA\n0570: NIU\n0574: NFK\n0578: NOR\n0580: MNP\n0581: UMI\n0583: FSM\n0584: MHL\n0585: PLW\n0586: PAK\n0591: PAN\n0598: PNG\n0600: PRY\n0604: PER\n0608: PHL\n0612: PCN\n0616: POL\n0620: PRT\n0624: GNB\n0626: TLS\n0630: PRI\n0634: QAT\n0638: REU\n0642: ROU\n0643: RUS\n0646: RWA\n0652: BLM\n0654: SHN\n0659: KNA\n0660: AIA\n0662: LCA\n0663: MAF\n0666: SPM\n0670: VCT\n0674: SMR\n0678: STP\n0682: SAU\n0686: SEN\n0688: SRB\n0690: SYC\n0694: SLE\n0702: SGP\n0703: SVK\n0704: VNM\n0705: SVN\n0706: SOM\n0710: ZAF\n0716: ZWE\n0724: ESP\n0728: SSD\n0729: SDN\n0732: ESH\n0740: SUR\n0744: SJM\n0748: SWZ\n0752: SWE\n0756: CHE\n0760: SYR\n0762: TJK\n0764: THA\n0768: TGO\n0772: TKL\n0776: TON\n0780: TTO\n0784: ARE\n0788: TUN\n0792: TUR\n0795: TKM\n0796: TCA\n0798: TUV\n0800: UGA\n0804: UKR\n0807: MKD\n0818: EGY\n0826: GBR\n0831: GGY\n0832: JEY\n0833: IMN\n0834: TZA\n0840: USA\n0850: VIR\n0854: BFA\n0858: URY\n0860: UZB\n0862: VEN\n0876: WLF\n0882: WSM\n0887: YEM\n0894: ZMB"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/currency_code.nfc",
    "content": "Filetype: Flipper EMV resources\nVersion: 1\n# EMV currency code: currency name\n0997: USN\n0994: XSU\n0990: CLF\n0986: BRL\n0985: PLN\n0984: BOV\n0981: GEL\n0980: UAH\n0979: MXV\n0978: EUR\n0977: BAM\n0976: CDF\n0975: BGN\n0973: AOA\n0972: TJS\n0971: AFN\n0970: COU\n0969: MGA\n0968: SRD\n0967: ZMW\n0965: XUA\n0960: XDR\n0953: XPF\n0952: XOF\n0951: XCD\n0950: XAF\n0949: TRY\n0948: CHW\n0947: CHE\n0946: RON\n0944: AZN\n0943: MZN\n0941: RSD\n0940: UYI\n0938: SDG\n0937: VEF\n0936: GHS\n0934: TMT\n0933: BYN\n0932: ZWL\n0931: CUC\n0930: STN\n0929: MRU\n0901: TWD\n0886: YER\n0882: WST\n0860: UZS\n0858: UYU\n0840: USD\n0834: TZS\n0826: GBP\n0818: EGP\n0807: MKD\n0800: UGX\n0788: TND\n0784: AED\n0780: TTD\n0776: TOP\n0764: THB\n0760: SYP\n0756: CHF\n0752: SEK\n0748: SZL\n0728: SSP\n0710: ZAR\n0706: SOS\n0704: VND\n0702: SGD\n0694: SLL\n0690: SCR\n0682: SAR\n0654: SHP\n0646: RWF\n0643: RUB\n0634: QAR\n0608: PHP\n0604: PEN\n0600: PYG\n0598: PGK\n0590: PAB\n0586: PKR\n0578: NOK\n0566: NGN\n0558: NIO\n0554: NZD\n0548: VUV\n0533: AWG\n0532: ANG\n0524: NPR\n0516: NAD\n0512: OMR\n0504: MAD\n0498: MDL\n0496: MNT\n0484: MXN\n0480: MUR\n0462: MVR\n0458: MYR\n0454: MWK\n0446: MOP\n0434: LYD\n0430: LRD\n0426: LSL\n0422: LBP\n0418: LAK\n0417: KGS\n0414: KWD\n0410: KRW\n0408: KPW\n0404: KES\n0400: JOD\n0398: KZT\n0392: JPY\n0388: JMD\n0376: ILS\n0368: IQD\n0364: IRR\n0360: IDR\n0356: INR\n0352: ISK\n0348: HUF\n0344: HKD\n0340: HNL\n0332: HTG\n0328: GYD\n0324: GNF\n0320: GTQ\n0292: GIP\n0270: GMD\n0262: DJF\n0242: FJD\n0238: FKP\n0232: ERN\n0230: ETB\n0222: SVC\n0214: DOP\n0208: DKK\n0203: CZK\n0192: CUP\n0191: HRK\n0188: CRC\n0174: KMF\n0170: COP\n0156: CNY\n0152: CLP\n0144: LKR\n0136: KYD\n0132: CVE\n0124: CAD\n0116: KHR\n0108: BIF\n0104: MMK\n0096: BND\n0090: SBD\n0084: BZD\n0072: BWP\n0068: BOB\n0064: BTN\n0060: BMD\n0052: BBD\n0051: AMD\n0050: BDT\n0048: BHD\n0044: BSD\n0036: AUD\n0032: ARS\n0012: DZD\n0008: ALL"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc",
    "content": "# Key dictionary from https://github.com/RfidResearchGroup/proxmark3/\n#\n# Mifare Default Keys\n#   -- iceman fork version --\n#   -- contribute to this list, sharing is caring --\n#\n# Default key\nFFFFFFFFFFFF\n#\n# Blank key\n000000000000\n#\n# NFC Forum MADkey\nA0A1A2A3A4A5\n#\n# MAD access key A (reversed)\nA5A4A3A2A1A0\n#\n# MAD access key B\n89ECA97F8C2A\n#\n# Mifare 1k EV1 (S50) hidden blocks, Signature data\n# 16 A\n5C8FF9990DA2\n#\n# 17 A\n75CCB59C9BED\n#\n# 16 B\nD01AFEEB890A\n#\n# 17 B\n4B791BEA7BCC\n#\n# QL88 keys\n# 17 A/B\n2612C6DE84CA\n707B11FC1481\n#\n# QL88 diversifed\n03F9067646AE\n2352C5B56D85\n#\nB0B1B2B3B4B5\nC0C1C2C3C4C5\nD0D1D2D3D4D5\nAABBCCDDEEFF\n4D3A99C351DD\n1A982C7E459A\n#\n# key A Wien\nD3F7D3F7D3F7\n#\n# key B Wien\n5A1B85FCE20A\n#\n#\n714C5C886E97\n587EE5F9350F\nA0478CC39091\n533CB6C723F6\n8FD0A4F256E9\n#\n# iCopy-X\nE00000000000\n#\n#\nE7D6064C5860\nB27CCAB30DBD\n#\n# lib / Nat Bieb\nD2ECE8B9395E\n# NSCP default key\n1494E81663D7\n#\n# NFC tools\n7c9fb8474242\n#\n# Kiev keys\n569369C5A0E5\n632193BE1C3C\n644672BD4AFE\n8FE644038790\n9DE89E070277\nB5FF67CBA951\nEFF603E1EFE9\nF14EE7CAE863\n#\n# ICT S14 A/B\n9C28A60F7249\nC9826AF02794\n#\n# RKF\n# Västtrafiken KeyA, RKF ÖstgötaTrafiken KeyA\nFC00018778F7\n#\n# Västtrafiken KeyA\n0297927C0F77\n54726176656C\n#\n# Västtrafiken KeyB\n00000FFE2488\n776974687573\nEE0042F88840\n#\n# RKF SLKeyA\n26940B21FF5D\nA64598A77478\n#\n# RKF SLKeyB\n5C598C9C58B5\nE4D2770A89BE\n#\n# RKF Rejskort Danmark KeyA\n722BFCC5375F\n#\n# RKF Rejskort Danmark KeyB\nF1D83F964314\n#\n# RKF JOJOPRIVA KeyA\n505249564141\n#\n# RKF JOJOPRIVA KeyB\n505249564142\n#\n# RKF JOJOGROUP KeyA\n47524F555041\n434F4D4D4F41\n#\n# RKF JOJOGROUP KeyB\n47524F555042\n434F4D4D4F42\n#\n# TNP3xxx\n4B0B20107CCB\n#\n# Access control system\n605F5E5D5C5B\n#\n# NSP Global keys A and B (uk housing access control)\n199404281970\n199404281998\n#\n# Data from http://www.proxmark.org/forum/viewtopic.php?pid=25925#p25925\n# Tengo Cards Key A\nFFF011223358\nFF9F11223358\n#\n# Elevator system Kherson, Ukraine\nAC37E76385F5\n576DCFFF2F25\n#\n# Car wash system\n1EE38419EF39\n26578719DCD9\n#\n# more Keys from mfc_default_keys.lua\n000000000001\n000000000002\n00000000000A\n00000000000B\n010203040506\n0123456789AB\n100000000000\n111111111111\n123456789ABC\n12F2EE3478C1\n14D446E33363\n1999A3554A55\n200000000000\n222222222222\n27DD91F1FCF1\n#\n# Hotel system\n505209016A1F\n#\n# Directory and eventlog KeyB\n2BA9621E0A36\n#\n# Directory and eventlog KeyA\n4AF9D7ADEBE4\n#\n#\n333333333333\n33F974B42769\n34D1DF9934C5\n43AB19EF5C31\n444444444444\n505249565441\n505249565442\n555555555555\n55F5A5DD38C9\n666666666666\n777777777777\n888888888888\n999999999999\n99C636334433\nA00000000000\nA053A292A4AF\nA94133013401\nAAAAAAAAAAAA\n#\n# Key from ladyada.net\nABCDEF123456\n#\n#\nB00000000000\nB127C6F41436\nBBBBBBBBBBBB\nBD493A3962B6\nC934FE34D934\nCCCCCCCCCCCC\nDDDDDDDDDDDD\nEEEEEEEEEEEE\n#\n# elevator\n# data from forum\nFFFFFF545846\n#\n#\nF1A97341A9FC\n#\n# hotel system\n44AB09010845\n85FED980EA5A\n#\n# ARD (fr) key A\n43454952534E\n# ARD (fr) key B\n4A2B29111213\n#\n#\n4143414F5250\n#\n# Tehran Railway\nA9B43414F585\n1FB235AC1388\n#\n# Data from http://irq5.io/2013/04/13/decoding-bcard-conference-badges/\n# BCARD KeyB\nF4A9EF2AFC6D\n#\n#\n# S0 B\n89EAC97F8C2A\n#\n# S4 A\n43C7600DEE6B\n#\n# S6 A\n0120BF672A64\n#\n# S6 B\nFB0B20DF1F34\n#\n#\nA9F953DEF0A3\n#\n# Data from forum\n74A386AD0A6D\n3F7A5C2DBD81\n21EDF95E7433\nC121FF19F681\n3D5D9996359A\n#\n# Here be BIP keys...\n3A42F33AF429\n1FC235AC1309\n6338A371C0ED\n243F160918D1\nF124C2578AD0\n9AFC42372AF1\n32AC3B90AC13\n682D401ABB09\n4AD1E273EAF1\n067DB45454A9\nE2C42591368A\n15FC4C7613FE\n2A3C347A1200\n68D30288910A\n16F3D5AB1139\nF59A36A2546D\n937A4FFF3011\n64E3C10394C2\n35C3D2CAEE88\nB736412614AF\n693143F10368\n324F5DF65310\nA3F97428DD01\n643FB6DE2217\n63F17A449AF0\n82F435DEDF01\nC4652C54261C\n0263DE1278F3\nD49E2826664F\n51284C3686A6\n3DF14C8000A1\n6A470D54127C\n#\n# Data from http://pastebin.com/AK9Bftpw\n# Länstrafiken i Västerbotten\n48FFE71294A0\nE3429281EFC1\n16F21A82EC84\n460722122510\n#\n# 3dprinter\n# EPI Envisionte\nAAFB06045877\n#\n# Gyms / Fitness Clubs / Health Clubs / Wellness Centres\n#\n# Fysiken A\n3E65E4FB65B3\n#\n# Fysiken B\n25094DF6F148\n#\n#\n# https://mattionline.de/fitnessstudio-armband-reverse-engineering/\n# https://mattionline.de/milazycracker/\n# Gym Wristband A - Same as Fysiken A\n# Gym Wristband B\n81CC25EBBB6A\n195DC63DB3A3\n#\n# CleverFit\nA05DBD98E0FC\n#\n# GoFit\nAA4DDA458EBB\nEAB8066C7479\n#\n# Nordic Wellness A - Same as Fysiken A\n# Nordic Wellness B\nE5519E1CC92B\n# \n# Jett's 24 Hour Fitness S0 KA/B\n# 049979614077\n# 829338771705\n#\n# Hotel KeyCard\nD3B595E9DD63\nAFBECD121004\n#\n# SimonsVoss\n6471A5EF2D1A\n#\n# ID06\n4E3552426B32\n22BDACF5A33F\n6E7747394E63\n763958704B78\n#\n# Onity  S1 A/B\n8A19D40CF2B5\n#\n3961EA82C46D\n#\n# 24-7\nD21762B2DE3B\n0E83A374B513\n1F1FFE000000\nA10F303FC879\n1322285230B8\n0C71BCFB7E72\nC3C88C6340B8\nF101622750B7\n1F107328DC8D\n710732200D34\n7C335FB121B5\nB39AE17435DC\n#\n# key A\n454841585443\n#\n# Lift system\n190819842023\n#\n# Data from http://pastebin.com/gQ6nk38G\nD39BB83F5297\n85675B200017\n528C9DFFE28C\nC82EC29E3235\n3E3554AF0E12\n491CDCFB7752\n22C1BAE1AACD\n5F146716E373\n740E9A4F9AAF\nAC0E24C75527\n97184D136233\nE444D53D359F\n17758856B182\nA8966C7CC54B\nC6AD00254562\nAE3FF4EEA0DB\n5EB8F884C8D1\nFEE470A4CB58\n75D8690F21B6\n871B8C085997\n97D1101F18B0\n75EDE6A84460\nDF27A8F1CB8E\nB0C9DD55DD4D\n#\n# Data from http://bit.ly/1bdSbJl\nA0B0C0D0E0F0\nA1B1C1D1E1F1\n#\n# Data from msk social\n2735FC181807\n2ABA9519F574\n84FD7F7A12B6\n186D8C4B93F9\n3A4BBA8ADAF0\n8765B17968A2\n40EAD80721CE\n0DB5E6523F7C\n51119DAE5216\n83E3549CE42D\n136BDB246CAC\n7DE02A7F6025\nBF23A53C1F63\nCB9A1F2D7368\nC7C0ADB3284F\n9F131D8C2057\n67362D90F973\n6202A38F69E2\n100533B89331\n653A87594079\nD8A274B2E026\nB20B83CB145C\n9AFA6CB4FC3D\nA229E68AD9E5\n49C2B5296EF4\n#\n# Data from http://pastebin.com/RRJUEDCM\n0D258FE90296\nE55A3CA71826\nA4F204203F56\nEEB420209D0C\n911E52FD7CE4\n752FBB5B7B45\n66B03ACA6EE9\n48734389EDC3\n17193709ADF4\n1ACC3189578C\nC2B7EC7D4EB1\n369A4663ACD2\n#\n# Data from https://github.com/zhangjingye03/zxcardumper\n# zxcard Key A/B\n668770666644\n003003003003\n#\n# Data from http://phreakerclub.com/forum/showthread.php?p=41266\n26973EA74321\n71F3A315AD26\n51044EFB5AAB\nAC70CA327A04\nEB0A8FF88ADE\n#\n# Transport system Metromoney\n2803BCB0C7E1\n9C616585E26D\n4FA9EB49F75E\n2DADE48942C5\nA160FCD5EC4C\n112233445566\n361A62F35BC9\n#\n# Transport system Spain\n83F3CB98C258\n070D486BC555\nA9B018868CC1\n9DCDB136110C\n749934CC8ED3\n506DB955F161\nF088A85E71D7\n72B458D60363\n70C714869DC7\nB32464412EE3\nF253C30568C4\n1C68315674AC\nCFE63749080A\nC1E6F8AFC9EC\nDD0DE3BA08A6\n3D923EB73534\nFF94F86B09A6\nD61707FFDFB1\n8223205047B6\n9951A273DEE7\nC9449301AF93\n66695A45C9FA\n89AA9D743812\nC41514DEFC07\nC52876869800\n5353B3AECB53\n2E4169A5C79D\n4BB747E48C2A\n6285A1C8EB5C\n5145C34DBA19\n25352912CD8D\n81B20C274C3F\n00B70875AF1D\n04B787B2F3A5\n05412723F1B6\n05C301C8795A\n066F5AF3CCEE\n0A1B6C50E04E\n0AD0956DF6EE\n0AD6B7E37183\n0F3A4D48757B\n1417E5671417\n18AB07270506\n18E887D625B4\n1ABC15934F5A\n1AF66F83F5BE\n260480290483\n2900AAC52BC3\n2910AFE15C99\n374521A38BCC\n3A4C47757B07\n3A524B7A7B37\n3C4ABB877EAF\n3F3A534B7B7B\n4B787B273A50\n4B92DF1BF25D\n4F0E4AE8051A\n514B797B2F3A\n529CF51F05C5\n52B26C199862\n57A18BFEC381\n5A7D87876EA8\n64CBADC7A313\n65B6C3200736\n67B1B3A4E497\n6B0454D5D3C3\n6B3B7AF45777\n6C273F431564\n702C1BF025DD\n738385948494\n76E450094393\n777B1F3A4F4A\n7B173A4E4976\n81504133B13C\n826576A1AB68\n8A55194F6587\n8DFACF11E778\n8FD6D76742DC\n9AFEE1F65742\n9D56D83658AC\n9FAC23197904\nA1AB3A08712C\nA514B797B373\nA58AB5619631\nA5BB18152EF1\nA777B233A4F4\nAB19BC885A29\nAB91BDA25F00\nAE98BA1E6F2C\nB133A4D48757\nB3A4C47757B0\nB6803136F5AF\nB793ADA6DB0C\nB95BFDEBA7E4\nC0AA2BBD27CD\nC27F5C1A9C2B\nC9BE49675FE4\nCCCE24102003\nCDE668FDCDBA\nD23A31A4AAB9\nDEDD7688BC38\nE9AE90885C39\nF0A3C5182007\nF3A524B7A7B3\n#\n# Data from mall\n# playland balikesir\nABBA1234FCB0\n#\n# A trio bowling bahcelievler\n314F495254FF\n4152414B4E41\n#\n# karinca park nigde\n4E474434FFFF\n#\n# hotel system\n537930363139\n#\n# Data from https://github.com/RadioWar/NFCGUI\n44DD5A385AAF\n21A600056CB0\nB1ACA33180A5\nDD61EB6BCE22\n1565A172770F\n3E84D2612E2A\nF23442436765\n79674F96C771\n87DF99D496CB\nC5132C8980BC\nA21680C27773\nF26E21EDCEE2\n675557ECC92E\nF4396E468114\n6DB17C16B35B\n4186562A5BB2\n2FEAE851C199\nDB1A3338B2EB\n157B10D84C6B\nA643F952EA57\nDF37DCB6AFB3\n4C32BAF326E0\n91CE16C07AC5\n3C5D1C2BCD18\nC3F19EC592A2\nF72A29005459\n185FA3438949\n321A695BD266\nD327083A60A7\n45635EF66EF3\n5481986D2D62\nCBA6AE869AD5\n645A166B1EEB\nA7ABBC77CC9E\nF792C4C76A5C\nBFB6796A11DB\n#\n# Data from Salto A/B\n6A1987C40A21\n7F33625BC129\n6BE9314930D8\n#\n# Data from forum\n2338B4913111\n#\n# Data from stoye\nCB779C50E1BD\nA27D3804C259\n003CC420001A\nF9861526130F\n381ECE050FBD\nA57186BDD2B9\n48C739E21A04\n36ABF5874ED7\n649D2ABBBD20\nBBE8FFFCF363\nAB4E7045E97D\n340E40F81CD8\nE4F65C0EF32C\nD2A597D76936\nA920F32FE93A\n86AFD95200F7\n9B832A9881FF\n26643965B16E\n0C669993C776\nB468D1991AF9\nD9A37831DCE5\n2FC1F32F51B1\n0FFBF65B5A14\nC5CFE06D9EA3\nC0DECE673829\n#\n#\nA56C2DF9A26D\n#\n# Data from https://pastebin.com/vbwast74\n68D3F7307C89\n#\n# Smart Rider. Western Australian Public Transport Cards\n568C9083F71C\n117E5C165B10\n24BB421C7973\n3E3A546650EA\n41F262D3AB66\n514956AB3142\n863933AE8388\n#\n# Bangkok metro key\n97F5DA640B18\n#\n# Metro Valencia key\nA8844B0BCA06\n#\n# HTC Eindhoven key\n857464D3AAD1\n#\n# Vigik Keys\n# Various sources :\n# * https://github.com/DumpDos/Vigik\n# * http://newffr.com/viewtopic.php?&forum=235&topic=11559\n# * Own dumps\n#\n# French VIGIK\n# VIGIK1 A\n314B49474956\n#\n# VIGIK1 B\n564C505F4D41\nBA5B895DA162\n#\n# BTCINO UNDETERMINED SPREAKD 0x01->0x13 key\n021209197591\n#\n#\n2EF720F2AF76\n414C41524F4E\n424C41524F4E\n4A6352684677\nBF1F4424AF76\n536653644C65\n#\n# Intratone Cogelec\n# Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html\n484558414354\nA22AE129C013\n49FAE4E3849F\n38FCF33072E0\n8AD5517B4B18\n509359F131B1\n6C78928E1317\nAA0720018738\nA6CAC2886412\n62D0C424ED8E\nE64A986A5D94\n8FA1D601D0A2\n89347350BD36\n66D2B7DC39EF\n6BC1E1AE547D\n22729A9BD40F\n#\n# Data from https://dfir.lu/blog/cloning-a-mifare-classic-1k-tag.html\n925B158F796F\nFAD63ECB5891\nBBA840BA1C57\nCC6B3B3CD263\n6245E47352E6\n8ED41E8B8056\n2DD39A54E1F3\n6D4C5B3658D2\n1877ED29435A\n52264716EFDE\n961C0DB4A7ED\n703140FD6D86\n157C9A513FA5\nE2A5DC8E066F\n#\n# Data from forum,  schlage 9691T fob\nEF1232AB18A0\n#\n# Data from a oyster card\n374BF468607F\nBFC8E353AF63\n15CAFD6159F6\n62EFD80AB715\n987A7F7F1A35\nC4104FA3C526\n4C961F23E6BE\n67546972BC69\nF4CD5D4C13FF\n94414C1A07DC\n16551D52FD20\n9CB290282F7D\n77A84170B574\nED646C83A4F3\nE703589DB50B\n513C85D06CDE\n95093F0B2E22\n543B01B27A95\nC6D375B99972\nEE4CC572B40E\n5106CA7E4A69\nC96BD1CE607F\n167A1BE102E0\nA8D0D850A606\nA2ABB693CE34\n7B296C40C486\n91F93A5564C9\nE10623E7A016\nB725F9CBF183\n#\n# Data from FDi tag\n8829DA9DAF76\n#\n# Data from GitHub issue\n0A7932DC7E65\n11428B5BCE06\n11428B5BCE07\n11428B5BCE08\n11428B5BCE09\n11428B5BCE0A\n11428B5BCE0F\n18971D893494\n25D60050BF6E\n44F0B5FBE344\n7B296F353C6B\n8553263F4FF0\n8E5D33A6ED51\n9F42971E8322\nC620318EF179\nD4FE03CE5B06\nD4FE03CE5B07\nD4FE03CE5B08\nD4FE03CE5B09\nD4FE03CE5B0A\nD4FE03CE5B0F\nE241E8AFCBAF\n# Transport system Argentina - SUBE\n# Shared key - sec 3 blk 15\n3FA7217EC575\n#\n# Data from forum post\n123F8888F322\n050908080008\n#\n# Data from hoist\n4F9F59C9C875\n#\n# Data from pastebin\n66F3ED00FED7\nF7A39753D018\n#\n# Data from https://pastebin.com/Z7pEeZif\n386B4D634A65\n666E564F4A44\n564777315276\n476242304C53\n6A696B646631\n4D3248735131\n425A73484166\n57784A533069\n345547514B4D\n4C6B69723461\n4E4175623670\n4D5076656D58\n686A736A356E\n484A57696F4A\n6F4B6D644178\n744E326B3441\n70564650584F\n584F66326877\n6D4E334B6C48\n6A676C315142\n77494C526339\n623055724556\n356D46474348\n4E32336C6E38\n57734F6F6974\n436A46587552\n5544564E6E67\n6F506F493353\n31646241686C\n77646B633657\n#\n# Data from TransPert\n2031D1E57A3B\n53C11F90822A\n9189449EA24E\n#\n# data from Github\n410B9B40B872\n2CB1A90071C8\n#\n#\n8697389ACA26\n1AB23CD45EF6\n013889343891\n#\n#\n0000000018DE\n16DDCB6B3F24\n#\n# Data from https://pastebin.com/vwDRZW7d\n# Vingcard Mifare 4k Staff card\nEC0A9B1A9E06\n6C94E1CED026\n0F230695923F\n0000014B5C31\n#\n#\nBEDB604CC9D1\nB8A1F613CF3D\nB578F38A5C61\nB66AC040203A\n6D0B6A2A0003\n2E641D99AD5B\nAD4FB33388BF\n69FB7B7CD8EE\n#\n# Hotel\n2A6D9205E7CA\n13B91C226E56\n#\n# KABA Hotel Locks\n2A2C13CC242A\n#\n#\n27FBC86A00D0\n01FA3FC68349\n#\n# Smart Rider. Western Australian Public Transport Cards\n6D44B5AAF464\n1717E34A7A8A\n#\n# RFIDeas\n6B6579737472\n#\n# HID MIFARE Classic 1k Key\n484944204953\n204752454154\n# HID MIFARE SO\n3B7E4FD575AD\n11496F97752A\n#\n# Luxeo/Aztek cashless vending\n415A54454B4D\n#\n# BQT\n321958042333\n#\n# Aperio KEY_A Sector 1, 12, 13, 14, 15 Data Start 0 Length 48\n160A91D29A9C\n#\n# Gallagher\nB7BF0C13066E\n#\n# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K)\n009FB42D98ED\n002E626E2820\n#\n# Boston, MA, USA Transit - MBTA Charlie Card\n3060206F5B0A\n5EC39B022F2B\n3A09594C8587\nF1B9F5669CC8\nF662248E7E89\n62387B8D250D\nF238D78FF48F\n9DC282D46217\nAFD0BA94D624\n92EE4DC87191\nB35A0E4ACC09\n756EF55E2507\n447AB7FD5A6B\n932B9CB730EF\n1F1A0A111B5B\nAD9E0A1CA2F7\nD58023BA2BDC\n62CED42A6D87\n2548A443DF28\n2ED3B15E7C0F\nF66224EE1E89\n#\n#\n60012E9BA3FA\n#\n#\nDE1FCBEC764B\n81BFBE8CACBA\nBFF123126C9B\n2F47741062A0\nB4166B0A27EA\nA170D9B59F95\n400BC9BE8976\nD80511FC2AB4\n1FCEF3005BCF\nBB467463ACD6\nE67C8010502D\nFF58BA1B4478\n#\n# Data from https://pastebin.com/Kz8xp4ev\nFBF225DC5D58\n#\n# Data  https://pastebin.com/BEm6bdAE\n# vingcard.txt\n# Note: most likely diversified\n96A301BCE267\n4708111C8604\n3D50D902EA48\n6700F10FEC09\n7A09CC1DB70A\n560F7CFF2D81\n66B31E64CA4B\n9E53491F685B\n3A09911D860C\n8A036920AC0C\n361F69D2C462\nD9BCDE7FC489\n0C03A720F208\n6018522FAC02\n#\n# Data from https://pastebin.com/4t2yFMgt\n# Mifare technische Universität Graz TUG\nD58660D1ACDE\n50A11381502C\nC01FC822C6E5\n0854BF31111E\n#\n# More keys - Found 8A at Sebel Hotel in Canberra, Australia\nAE8587108640\n#\n# SafLock standalone door locks\n135B88A94B8B\n#\n# Russian Troika card\nEC29806D9738\n08B386463229\n0E8F64340BA4\n0F1C63013DBA\n2AA05ED1856F\n2B7F3253FAC5\n69A32F1C2F19\n73068F118C13\n9BECDF3D9273\nA73F5DC1D333\nA82607B01C0D\nAE3D65A3DAD4\nCD4C61C26E3D\nD3EAFB5DF46D\nE35173494A81\nFBC2793D540B\n5125974CD391\nECF751084A80\n7545DF809202\nAB16584C972A\n7A38E3511A38\nC8454C154CB5\n04C297B91308\nEFCB0E689DB3\n07894FFEC1D6\nFBA88F109B32\n2FE3CB83EA43\nB90DE525CEB6\n1CC219E9FEC1\nA74332F74994\n764CD061F1E6\n8F79C4FD8A01\nCD64E567ABCD\nCE26ECB95252\nABA208516740\n9868925175BA\n16A27AF45407\n372CC880F216\n3EBCE0925B2F\n73E5B9D9D3A4\n0DB520C78C1C\n70D901648CB9\nC11F4597EFB5\nB39D19A280DF\n403D706BA880\n7038CD25C408\n6B02733BB6EC\nEAAC88E5DC99\n4ACEC1205D75\n2910989B6880\n31C7610DE3B0\n5EFBAECEF46B\nF8493407799D\n6B8BD9860763\nD3A297DC2698\n#\n# Data from reddit\n34635A313344\n593367486137\n#\n# Keys from Mifare Classic Tool project\n044CE1872BC3\n045CECA15535\n0BE5FAC8B06A\n0CE7CD2CC72B\n0EB23CC8110B\n0F01CEFF2742\n0F318130ED18\n114D6BE9440C\n18E3A02B5EFF\n19FC84A3784B\n1B61B2E78C75\n22052B480D11\n3367BFAA91DB\n3A8A139C20B4\n42E9B54E51AB\n46D78E850A7E\n4B609876BBA3\n518DC6EEA089\n6B07877E2C5C\n7259FA0197C6\n72F96BDD3714\n7413B599C4EA\n77DABC9825E1\n7A396F0D633D\n7A86AA203788\n8791B2CCB5C4\n8A8D88151A00\n8C97CD7A0E56\n8E26E45E7D65\n9D993C5D4EF4\n9EA3387A63C1\nA3FAA6DAFF67\nA7141147D430\nACFFFFFFFFFF\nAFCEF64C9913\nB27ADDFB64B0\nB81F2B0C2F66\nB9F8A7D83978\nBAFF3053B496\nBB52F8CCE07F\nBC2D1791DEC1\nBC4580B7F20B\nC65D4EAA645B\nC76BF71A2509\nD5524F591EED\nE328A1C7156D\nE4821A377B75\nE56AC127DD45\nEA0FD73CB149\nFC0001877BF7\nFD8705E721B0\n00ADA2CD516D\n518108E061E2\n558AAD64EB5B\n001122334455\n6CA761AB6CA7\nB1C4A8F7F6E3\nFF75AFDA5A3C\nFCDDF7767C10\nA6B3F6C8F1D4\n#\n#\n237A4D0D9119\n0ED7846C2BC9\nFFFFD06F83E3\nFFFFAE82366C\nF89C86B2A961\nF83466888612\nED3A7EFBFF56\nE96246531342\nE1DD284379D4\nDFED39FFBB76\nDB5181C92CBE\nCFC738403AB0\nBCFE01BCFE01\nBA28CFD15EE8\nB0699AD03D17\nAABBCC660429\nA4EF6C3BB692\nA2B2C9D187FB\n9B1DD7C030A1\n9AEDF9931EC1\n8F9B229047AC\n872B71F9D15A\n833FBD3CFE51\n5D293AFC8D7E\n5554AAA96321\n474249437569\n435330666666\n1A2B3C4D5E6F\n123456ABCDEF\n83BAB5ACAD62\n64E2283FCF5E\n64A2EE93B12B\n46868F6D5677\n40E5EA1EFC00\n37D4DCA92451\n2012053082AD\n2011092119F1\n200306202033\n1795902DBAF9\n17505586EF02\n022FE48B3072\n013940233313\n#\n# Hotel Adina\n9EBC3EB37130\n#\n# Misc. keys from hotels & library cards in Germany\n914f57280ce3\n324a82200018\n370aee95cd69\n2e032ad6850d\n1feda39d38ec\n288b7a34dbf8\n0965e3193497\n18c628493f7f\n064d9423938a\n995fd2a2351e\n7c7d672bc62e\n217250fb7014\nae7478ccaee7\nabbf6d116eaf\n05862c58edfb\ne43b7f185460\n6a59aa9a959b\nb79e5b175227\n7bc9ebb8274b\nb2afbf2331d4\n223e5847dd79\n640524d2a39b\naee297cb2fd6\n3da5dfa54604\n0cf1a2aa1f8d\n#\n# most likely diversifed individual keys.\n# data from https://github.com/korsehindi/proxmark3/commit/24fdbfa9a1d5c996aaa5c192bc07e4ab28db4c5c\n491CDC863104\nA2F63A485632\n98631ED2B229\n19F1FFE02563\n#\n# Argentina\n563A22C01FC8\n43CA22C13091\n25094DF2C1BD\n#\n# OMNITEC.ES HOTEL TIMECARD / MAINTENANCECARD\nAFBECD120454\n#\n# OMNITEC.ES HOTEL EMERGENCYCARD\n842146108088\n#\n# TAPCARD PUBLIC TRANSPORT LA\nEA1B88DF0A76\nD1991E71E2C5\n05F89678CFCF\nD31463A7AB6D\nC38197C36420\n772219470B38\n1C1532A6F1BC\nFA38F70215AD\nE907470D31CC\n160F4B7AB806\n1D28C58BBE8A\nB3830B95CA34\n6A0E215D1EEB\nE41E6199318F\nC4F271F5F0B3\n1E352F9E19E5\n0E0E8C6D8EB6\nC342F825B01B\nCB911A1A1929\nE65B66089AFC\nB81846F06EDF\n37FC71221B46\n880C09CFA23C\n6476FA0746E7\n419A13811554\n2C60E904539C\n4ECCA6236400\n10F2BBAA4D1C\n4857DD68ECD9\nC6A76CB2F3B5\nE3AD9E9BA5D4\n6C9EC046C1A4\n#\n# ROC HIGHSCHOOL ACCESSCARD\nB021669B44BB\nB18CDCDE52B7\nA22647F422AE\nB268F7C9CA63\nA37A30004AC9\nB3630C9F11C8\nA4CDFF3B1848\nB42C4DFD7A90\nA541538F1416\nB5F454568271\nA6C028A12FBB\nB6323F550F54\nA7D71AC06DC2\nB7C344A36D88\nA844F4F52385\nB8457ACC5F5D\nA9A4045DCE77\nB9B8B7B6B5B3\nAA4D051954AC\nBA729428E808\nAB28A44AD5F5\nBB320A757099\nAC45AD2D620D\nBCF5A6B5E13F\nAD5645062534\nBDF837787A71\nAE43F36C1A9A\nBE7C4F6C7A9A\n5EC7938F140A\n82D58AA49CCB\n#\n# MELON CARD\n323334353637\n#\n#\nCEE3632EEFF5\n827ED62B31A7\n03EA4053C6ED\nC0BEEFEC850B\nF57F410E18FF\n0AF7DB99AEE4\nA7FB4824ACBF\n207FFED492FD\n1CFA22DBDFC3\n30FFB6B056F5\n39CF885474DD\n00F0BD116D70\n4CFF128FA3EF\n10F3BEBC01DF\n#\n# Transportes Insular La Palma\n0172066B2F03\n0000085F0000\n1A80B93F7107\n70172066B2F0\nB1A80C94F710\n0B0172066B2F\n0F1A81C95071\nF0F0172066B2\n1131A81D9507\n2F130172066B\n71171A82D951\nB2F170172066\n1711B1A82E96\n6B2F1B017206\n62711F1A83E9\n66B2F1F01720\n97271231A83F\n066B2F230172\nF97371271A84\n2066B2F27017\n50983712B1A8\n72066B2F2B01\n850984712F1A\n172066B2F2F0\nA85198481331\n0172066B2F33\n1A8619858137\n70172066B2F3\nB1A862985913\n3B0172066B2F\n3F1A87298691\nF3F0172066B2\n#\n# Tehran ezpay\n38A88AEC1C43\nCBD2568BC7C6\n7BCB4774EC8F\n22ECE9316461\nAE4B497A2527\nEEC0626B01A1\n2C71E22A32FE\n91142568B22F\n7D56759A974A\nD3B1C7EA5C53\n41C82D231497\n0B8B21C692C2\n604AC8D87C7E\n8E7B29460F12\nBB3D7B11D224\n#\n# Chaco\nB210CFA436D2\nB8B1CFA646A8\nA9F95891F0A4\n#\n# Keys from APK application \"Scan Badge\"\n4A4C474F524D\n444156494442\n434143445649\n434456495243\nA00002000021\nEF61A3D48E2A\nA23456789123\n010000000000\n363119000001\nA00003000084\n675A32413770\n395244733978\nA0004A000036\n2C9F3D45BA13\n4243414F5250\nDFE73BE48AC6\n#\n#\nB069D0D03D17\n000131B93F28\n#\n# From the DFW Area, TX, USA\nA506370E7C0F\n26396F2042E7\n70758FDD31E0\n9F9D8EEDDCCE\n06FF5F03AA1A\n4098653289D3\n904735F00F9E\nB4C36C79DA8D\n68F9A1F0B424\n5A85536395B3\n7DD399D4E897\nEF4C5A7AC6FC\nB47058139187\n8268046CD154\n67CC03B7D577\n#\n# From the HTL Mödling, NÖ, AT\nA5524645CD91\nD964406E67B4\n99858A49C119\n7B7E752B6A2D\nC27D999912EA\n66A163BA82B4\n4C60F4B15BA8\n#\n# CAFE + CO, AT\n35D850D10A24\n4B511F4D28DD\nE45230E7A9E8\n535F47D35E39\nFB6C88B7E279\n#\n# Metro Card, AT\n223C3427108A\n#\n# Unknown, AT\n23D4CDFF8DA3\nE6849FCC324B\n12FD3A94DF0E\n#\n# Unknown, AT\n0B83797A9C64\n39AD2963D3D1\n#\n# Hotel Berlin Classic room A KEY\n34B16CD59FF8\n#\n# Hotel Berlin Classic room B KEY\nBB2C0007D022\n#\n# Coinmatic laundry Smart card\n# data from: https://pastebin.com/XZQiLtUf\n0734BFB93DAB\n85A438F72A8A\n#\n# Data from forum, Chinese hotel\n58AC17BF3629\nB62307B62307\n#\n#\nA2A3CCA2A3CC\n#\n# Granada, ES Transport Card\n000000270000\n0F385FFB6529\n29173860FC76\n2FCA8492F386\n385EFA542907\n3864FCBA5937\n3F3865FCCB69\n6291B3860FC8\n63FCA9492F38\n863FCB959373\n87291F3861FC\n913385FFB752\nB385EFA64290\nC9739233861F\nF3864FCCA693\nFC9839273862\n#\n# various hotel keys\n34D3C568B348\n91FF18E63887\n4D8B8B95FDEE\n354A787087F1\n4A306E62E9B6\nB9C874AE63D0\n#\n# Data from official repo\nF00DFEEDD0D0\n0BB31DC123E5\n7578BF2C66A9\nCD212889C3ED\n6936C035AE1B\nC6C866AA421E\n590BD659CDD2\nAA734D2F40E0\n09800FF94AAF\n5A12F83326E7\nC554EF6A6015\n0D8CA561BDF3\nB8937130B6BA\nD7744A1A0C44\n82908B57EF4F\nFE04ECFE5577\n#\n# comfort inn hotel\n4D57414C5648\n4D48414C5648\n#\n# unknown hotel key\n6D9B485A4845\n#\n# Bosch Solution 6000\n5A7A52D5E20D\n#\n# Found in TagInfo app\n# RATB key\nC1E51C63B8F5\n1DB710648A65\n# E-GO card key\n18F34C92A56E\n#\n# Library Card MFP - SL1\n4A832584637D\nCA679D6291B0\n30D9690FC5BC\n5296C26109D4\nE77952748484\n91C2376005A1\n30B7680B2BC9\nE2A9E88BFE16\n43B04995D234\nAADE86B1F9C1\n5EA088C824C9\nC67BEB41FFBF\nB84D52971107\n52B0D3F6116E\n#\n# Data from https://pastebin.com/cLSQQ9xN\nCA3A24669D45\n4087C6A75A96\n403F09848B87\nD73438698EEA\n5F31F6FCD3A0\nA0974382C4C5\nA82045A10949\n#\n# Data from https://pastebin.com/2iV8h93h\n#\n# funnivarium\n# forum ankara\n2602FFFFFFFF\n#\n# macera adasi\n# ankara kentpark\n# INACTIVE\n0A4600FF00FF\nDFF293979FA7\n4D6F62692E45\n4118D7EF0902\n#\n# petrol ofisi\n# positive card\n# ode-gec\n0406080A0C0E\n#\n# konya elkart\n988ACDECDFB0\n120D00FFFFFF\n#\n# bowlingo\n# serdivan avym\n4AE23A562A80\n#\n#  kart 54\n2AFFD6F88B97\nA9F3F289B70C\nDB6819558A25\n6130DFA578A0\nB16B2E573235\n42EF7BF572AB\n274E6101FC5E\n#\n# crazy park\n# kizilay avm\n00DD300F4F10\n#\n# kartsistem B\nFEE2A3FBC5B6\n#\n# toru ent\n# taurus avm\n005078565703\n#\n# Ving?\n0602721E8F06\nFC0B50AF8700\nF7BA51A9434E\n#\n# eskart\n# eskisehir transport card\nE902395C1744\n4051A85E7F2D\n7357EBD483CC\nD8BA1AA9ABA0\n76939DDD9E97\n3BF391815A8D\n#\n# muzekart\n# museum card for turkey\n7C87013A648A\nE8794FB14C63\n9F97C182585B\nEC070A52E539\nC229CE5123D5\nE495D6E69D9C\n26BF1A68B00F\nB1D3BC5A7CCA\n734EBE504CE8\n974A36E2B1BA\nC197AE6D6990\n4D80A10649DF\n037F64F470AD\nC9CD8D7C65E5\nB70B1957FE71\nCE7712C5071D\nC0AD1B72921A\n45FEE09C1D06\nE592ED478E59\nF3C1F1DB1D83\n704A81DDACED\n89E00BC444EF\nAFAAFCC40DEC\nECC58C5D34CA\n57D83754711D\nD0DDDF2933EC\n240F0BB84681\n9E7168064993\n2F8A867B06B4\n#\n# bursakart\n# bursa transport card\n755D49191A78\nDAC7E0CBA8FD\n68D3263A8CD6\n865B6472B1C0\n0860318A3A89\n1927A45A83D3\nB2FE3B2875A6\n#\n# playland\n# maltepe park\nABCC1276FCB0\nAABAFFCC7612\n#\n# lunasan\n# kocaeli fair\n26107E7006A0\n#\n# gamefactory\n# ozdilek\n17D071403C20\n#\n#\n534F4C415249\n534F4C303232\n#\n# Nespresso, smart card\n# key-gen algo, these keys are for one card (keys diversified)\nFF9A84635BD2\n6F30126EE7E4\n6039ABB101BB\nF1A1239A4487\n#\n#\nB882FD4A9F78\nCD7FFFF81C4A\nAA0857C641A3\nC8AACD7CF3D1\n9FFDA233B496\n26B85DCA4321\nD4B2D140CB2D\nA7395CCB42A0\n541C417E57C0\nD14E615E0545\n69D92108C8B5\n703265497350\nD75971531042\n10510049D725\n35C649004000\n5B0C7EC83645\n05F5EC05133C\n521B517352C7\n94B6A644DFF6\n2CA4A4D68B8E\nA7765C952DDF\nE2F14D0A0E28\nDC018FC1D126\n4927C97F1D57\n046154274C11\n155332417E00\n6B13935CD550\nC151D998C669\nD973D917A4C7\n130662240200\n9386E2A48280\n52750A0E592A\n075D1A4DD323\n32CA52054416\n460661C93045\n5429D67E1F57\n0C734F230E13\n1F0128447C00\n411053C05273\n42454C4C4147\nC428C4550A75\n730956C72BC2\n28D70900734C\n4F75030AD12B\n6307417353C1\nD65561530174\nD1F71E05AD9D\nF7FA2F629BB1\n0E620691B9FE\n43E69C28F08C\n735175696421\n424C0FFBF657\n51E97FFF51E9\nE7316853E731\n00460740D722\n35D152154017\n5D0762D13401\n0F35D5660653\n1170553E4304\n0C4233587119\nF678905568C3\n50240A68D1D8\n2E71D3BD262A\n540D5E6355CC\nD1417E431949\n4BF6DE347FB6\n#\n#\n3A471B2192BF\nA297CEB7D34B\nAE76242931F1\n#\n#\n124578ABFEDC\nABFEDC124578\n4578ABFEDC12\n#\n# Data from\n# premier inn hotel chain\n5E594208EF02\nAF9E38D36582\n#\n# Norwegian building site identication card. (HMS KORT)\n# Key a\n10DF4D1859C8\n#\n# Key B\nB5244E79B0C8\n#\n# Ukraine hotel\nF5C1C4C5DE34\n#\n# Data from Mifare Classic Tool repo\n# Rotterdam University of applied sciences campus card\nBB7923232725\nA95BD5BB4FC5\nB099335628DF\nA34DA4FAC6C8\nAD7C2A07114B\n53864975068A\n549945110B6C\nB6303CD5B2C6\nAFE444C4BCAA\nB80CC6DE9A03\nA833FE5A4B55\nB533CCD5F6BF\nB7513BFF587C\nB6DF25353654\n9128A4EF4C05\nA9D4B933B07A\nA000D42D2445\nAA5B6C7D88B4\nB5ADEFCA46C4\nBF3FE47637EC\nB290401B0CAD\nAD11006B0601\n#\n# Data from Mifare Classic Tool repo\n# Armenian Metro\nE4410EF8ED2D\n6A68A7D83E11\n0D6057E8133B\nD3F3B958B8A3\n3E120568A35C\n2196FAD8115B\n7C469FE86855\nCE99FBC8BD26\n#\n# keys from Eurothermes group (Switzerland)\nD66D91829013\n75B691829013\n83E391829013\nA23C91829013\nE46A91829013\nD9E091829013\nFED791829013\n155F91829013\n06CC91829013\n8DDC91829013\n54AF91829013\n29A791829013\n668091829013\n00008627C10A\n#\n# easycard\n310D51E539CA\n2CCDA1358323\n03E0094CEDFE\n562E6EF73DB6\nF53E9F4114A9\nAD38C17DE7D2\n#\n# SUBE cards keys (new)\n2DEB57A3EA8F\n32C1BB023F87\n70E3AD3F2D29\n202ECDCCC642\n3686192D813F\n24501C422387\n2C7813A721C3\nFFE04BE3D995\nD28F090677A1\nDE2D83E2DCCC\nA66A478712EA\n643232ADB2D5\nC7F4A4478415\n95C013B70D99\n3C383889362A\n3C6D9C4A90FA\n51BEDBA005E5\n74BF7363F354\n53B09DB89111\nE98075318085\n2F904641D75F\n7F60AEF68136\nF5C1B3F62FDA\n3E6E5713BA10\n8B75A29D4AB2\n7E6545076619\n#\n# SUBE cards keys (old)\n4C5A766DFE3A\n32C6768847F5\nF68930789631\n8B42B6D64B02\nB627A3CB13F8\n562A4FB8260B\n88DDC24E1671\n91CB7802A559\n7A3E0F5B63FC\n8CA2C9DC8292\n5CCC6D50EAAC\nDE4F5AA9A7F3\n52D0145E1AF5\nC10F92A4E57E\n7D6E7AF43C97\nDE1E7D5F6DF1\nF4CB751B031A\nC54474936B59\n2A1F900D4533\n6303CDCBB233\nF115E91357B3\nBFE25035B0C8\n62FF943EB069\n7C82EF592001\nD5C172325DD3\n992B152E834A\nCE75D7EADEAF\n#\n# Russian Podorozhnik card (Saint-Petersburg transport)\n# may be combined with Troika\n038B5F9B5A2A\n04DC35277635\n0C420A20E056\n152FD0C420A7\n296FC317A513\n29C35FA068FB\n31BEC3D9E510\n462225CD34CF\n4B7CB25354D3\n5583698DF085\n578A9ADA41E3\n6F95887A4FD3\n7600E889ADF9\n86120E488ABF\n8818A9C5D406\n8C90C70CFF4A\n8E65B3AF7D22\n9764FEC3154A\n9BA241DB3F56\nAD2BDC097023\nB0A2AAF3A1BA\nB69D40D1A439\nC956C3B80DA3\nCA96A487DE0B\nD0A4131FB290\nD27058C6E2C7\nE19504C39461\nFA1FBB3F0F1F\nFF16014FEFC7\n#\n# Food GEM\n6686FADE5566\n#\n# Samsung Data Systems (SDS) — Electronic Locks\n# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks\n#\n# SDS Gen 2 S10 KB\nC22E04247D9A\n#\n# Data from Discord, French pool\n# SDS Gen 2 S10 KA\n9B7C25052FC3\n494446555455\n#\n# Data from Discord, seems to be related to ASSA\n427553754D47\n# Keys found on Edith Cowan University Smart Riders\n9A677289564D\n186C59E6AFC9\nDDDAA35A9749\n9D0D0A829F49\n# Mercator Pika Card, Slovenia\n97D77FAE77D3\n5AF445D2B87A\n#\n# Vilniečio/JUDU kortelė, Lithuania\n# A\n16901CB400BC\nF0FE56621A42\n8C187E78EE9C\nFE2A42E85CA8\n# B\n6A6C80423226\nF4CE4AF888AE\n307448829EBC\nC2A0105EB028\n#\n# Keys from Flipper Zero Community\n# Last update: Aug 13, 2022\n#\n# unknown if keys are diversified or static default\n#\n# Strelka Extension\n5C83859F2224\n66B504430416\n70D1CF2C6843\nC4B3BD0ED5F1\nC4D3911AD1B3\nCAD7D4A6A996\nDA898ACBB854\nFEA1295774F9\n#\n# Moscow Public Toilets Card\n807119F81418\n22C8BCD10AAA\n0AAABA420191\nE51B4C22C8BC\nDBF9F79AB7A2\n34EDE51B4C22\nC8BCD10AAABA\nBCD10AAABA42\n#\n# Moscow Social Card\n2F87F74090D1\nE53EAEFE478F\nCE2797E73070\n328A034B93DB\n81E1529AE22B\nFC55C50E579F\n1A72E2337BC3\n5DB52676BE07\nF64FBF085098\n8FE758A8F039\nBB1484CC155D\n41990A529AE2\nCD2E9EE62F77\n69C1327AC20B\n3C9C0D559DE5\n67BF3880C811\n48A01159A1E9\n2B83FB448CD4\nF24BBB044C94\n94F46DB5FD46\nC31C8CD41D65\nBB1684CC155D\nCA2393DB246C\n1D75E52E76BE\n81D9529AE223\n0159C9125AA2\n52AA1B6BB3FB\n97EF60A8F031\n6FC73888D011\n3A92FA438BD3\n74CC3D85CD0E\n025ACA1B63A3\nAF0878C81151\n9BFB6CB4FC45\nF750C0095199\n075FCF1860A8\n2686EE3F87C7\n277FEF3880C0\n82DA4B93DB1C\n9CF46DB5FD46\n93EB64ACF43D\n#\n# Iron Logic RU\nA3A26EF4C6B0\n2C3FEAAE99FC\nE85B73382E1F\nF4ED24C2B998\nCB574C6D3B19\nE092081D724B\nB38D82CF7B6C\n8228D2AA6EFA\n2C7E983588A3\nCF7A7B77E232\n32A7F5EAF87D\n7453A687B5F0\n01A0C008A5B9\nDEC0CEB0CE24\n413BED2AE45B\nD6261A9A4B3F\nCB9D507CE56D\n#\n# Armenian Underground Ticket\nA0A1A2A8A4A5\n#\n# Badge Maker Leaked from https://github.com/UberGuidoZ\n1A1B1C1D1E1F\n1665FE2AE945\n158B51947A8E\nE167EC67C7FF\nD537320FF90E\n5E56BFA9E2C9\nF81CED821B63\nC81584EF5EDF\n9551F8F9259D\n36E1765CE3E8\n509052C8E42E\n776C9B03BE71\nC608E13ADD50\nBEE8B345B949\nED0EC56EEFDD\n9716D5241E28\n05D1FC14DC31\n3321FB75A356\nF22A78E29880\nEC211D12C98D\n8CCA8F62A551\nB637E46AD674\n39605B3C8917\n3882719778A1\n9F27D36C4230\nDB32A6811327\n8AA8544A2207\n8C5819E780A4\n7549E90353A2\n2E52ABE0CE95\nE46210ED98AB\n61D030C0D7A8\n18E20102821E\nDA59354DFB88\n040047C12B75\nD10008074A6F\n686E736F6E20\n446176696453\n6F6674776172\n6520446F7665\n#\n# Apartment keyfobs (USA) (Corvette830)\nE60F8387F0B9\nFFD46FF6C5EE\n4F9661ED2E70\n576A798C9904\n1C5179C4A8A1\n16CA203B811B\n11AC8C8F3AF2\n#\n# The Westin Jakarta Indonesia (D4DB0D)\n# Peppers Hotel Unknown location (D4D0D)\n6E0DD4136B0A\n141940E9B71B\n3B1D3AAC866E\n95E9EE4CCF8F\nFEA6B332F04A\nBE0EC5155806\n0500D6BFCC4F\nFC5AC7678BE3\nF09BB8DD142D\nB4B3FFEDBE0A\n540E0D2D1D08\n#\n# Schlage 9691T Keyfob (seasnaill)\n7579B671051A\n4F4553746B41\n#\n# Vigik ScanBadge App (fr.badgevigik.scanbadge)\n# Website https://badge-vigik.fr/  (Alex)\n0000A2B3C86F\n021200C20307\n021209197507\n1E34B127AF9C\n303041534956\n4143532D494E\n41454E521985\n43412D627400\n455249524345\n456666456666\n45B722C63319\n484585414354\n4D414C414741\n536563644C65\n57D27B730760\n593DD8FE167A\n6472616E7265\n65626F726369\n680E95F3C287\n709BA7D4F920\n8829DAD9AF76\n92D0A0999CBA\n948EE7CFC9DB\n9EB7C8A6D4E3\nA22AE12C9013\nAFC984A3576E\n#\n# Vigik verified by quantum-x\n# https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976\nA00027000099\nA00016000028\nA00003000028\nA0000F000345\nA00001000030\nA00002000086\nA00002000036\nA00002000088\nA00000000058\nA00000000096\nA00000000008\nA00000043D79\nA00000000064\nA00025000030\nA00003000057\n#\n# BH USA 2013 conference\n012279BAD3E5\n#\n# iGuard Simple (and reverse) keys\nAAAAAAFFFFFF\nFFFFFFAAAAAA\n#\n# Random Hotel A Key Sec 0 Blk 3 - KABA Lock (VideoMan)\n3111A3A303EB\n# Transport system Uruguay - STM\n# Shared key - sec 0 blk 3\nD144BD193063\n#\n# Data from http://www.proxmark.org/forum/viewtopic.php?pid=45659#p45659\n3515AE068CAD\n#\n# Keys Catering\n6A0D531DA1A7\n4BB29463DC29\n#\n# Keys Swim\n8627C10A7014\n453857395635\n#\n# Unknown hotel system Sec 0 / A\n353038383134\n#\n# Brazil transport Sec 8 / A\n50d4c54fcdf5\n#\n# Bandai Namco Passport [fka Banapassport] / Sega Aime Card\n# Dumped on the Flipper Devices Discord Server\n6090D00632F5\n019761AA8082\n574343467632\nA99164400748\n62742819AD7C\nCC5075E42BA1\nB9DF35A0814C\n8AF9C718F23D\n58CD5C3673CB\nFC80E88EB88C\n7A3CDAD7C023\n30424C029001\n024E4E44001F\nECBBFA57C6AD\n4757698143BD\n1D30972E6485\nF8526D1A8D6D\n1300EC8C7E80\nF80A65A87FFA\nDEB06ED4AF8E\n4AD96BF28190\n000390014D41\n0800F9917CB0\n730050555253\n4146D4A956C4\n131157FBB126\nE69DD9015A43\n337237F254D5\n9A8389F32FBF\n7B8FB4A7100B\nC8382A233993\n7B304F2A12A6\nFC9418BF788B\n#\n# Guest Cashless Prepaid Arcade Payment Cards\n168168168168\n198407157610\n4E4F584D2101\n4E4F584D2105\n686B35333376\n861861861861\n#\n# Transport System Cracow / Polen\nB071A76BA2E9\nB3A181BCA5F2\n3225942F7717\n80D00703C5FB\n6DD510E080B1\n87529F30FC58\nB75C4FA614AE\n42DC568C64F4\n# Data from \"the more the marriott\" mifare project (colonelborkmundus)\n# aka The Horde\n#\n# These keys seem to be from Vingcard / Saflok system which means they are diversified\n# and not static default keys.  To verify this, the UID from such a card is needed.\n#\n# 20230125-01, Elite Member Marriott Rewards\n43012BD9EB87\n# 20230125-02, Elite Member Marriott Rewards\n3119A70628EB\n# 20230125-03, Elite Member Marriott Rewards\n23C9FDD9A366\n# 20230125-04, Elite Member Marriott Rewards\n7B4DFC6D6525\n# 20230125-05, Elite Member Marriott Rewards\n1330824CD356\n# 20230125-06, Elite Member Marriott Rewards\n30AAD6A711EF\n# 20230125-07, Fairfield Inn & Suites Marriott\n7B3B589A5525\n# 20230125-08, Moxy Hotels\n20C166C00ADB\n# 20230125-09, Westin Hotels & Resorts\n7D0A1C277C05\n2058580A941F\n8C29F8320617\n# 20230125-10, Westin Hotels & Resorts\nC40964215509\nD44CFC178460\n5697519A8F02\n# 20230125-12, AC Hotels Marriott\n7B56B2B38725\n# 20230125-13, AC Hotels Marriott\n8EA8EC3F2320\n# 20230125-14, Waldorf Astoria Chicago\n011C6CF459E8\n# 20230125-24, Aria Resort & Casino\nA18D9F4E75AF\n# 20230125-25, Aria Resort & Casino\n316B8FAA12EF\n# 20230125-26, Residence Inn Mariott\n3122AE5341EB\n# 20230125-27, Residence Inn Mariott\nF72CD208FDF9\n# 20230125-28, Marriott\n035C70558D7B\n# 20230125-29, Marriott\n12AB4C37BB8B\n# 20230125-30, Marriott\n9966588CB9A0\n# 20230125-31, Sheraton\n42FC522DE987\n# 20230125-32, The Industrialist\n2158E314C3DF\n# 20230125-39, The Ritz-Carlton Balharbour\n30FB20D0EFEF\n# 20230125-40, The Ritz-Carlton Balharbour\n66A3B064CC4B\n# 20230125-41, The Ritz-Carlton Balharbour\nD18296CD9E6E\n# 20230125-42, The Ritz-Carlton Balharbour\nD20289CD9E6E\n# 20230125-44, Graduate Hotels\n209A2B910545\nC49DAE1C6049\n# 20230125-46, AmericInn\n8AC04C1A4A25\n# 20230129-53, Marriott Bonvoy\n6E029927600D\n3E173F64C01C\nC670A9AD6066\n# 20230413-69, Westin\n487339FA02E0\n# 20230413-70, Marriott Bonvoy\nDBD5CA4EE467\nA0B1F234006C\n180DE12B700E\n# 20230413-71, Westin\n1352C68F7A56\n# 20230413-76, Ritz Carlton\n318BD98C1CEF\n# 20230413-77, Marriott\nD23C1CB1216E\n# 20230413-78, Caesars\nA1D92F808CAF\n# 20230413-79, The Cosmopolitan, Vegas\n# 20230413-80, Aria\n1153C319B4F8\n# 20230413-81, Aria\n110C819BBEF8\n# 20230413-82, Aria\n1332117E8756\n# 20230413-83, Kimpton\n500AE915F50A\n5032E362B484\n8B63AB712753\n# 20230413-85, Kimpton\n06106E187106\n2E45C23DC541\nD9FF8BEE7550\n# 20230413-87, Marriott\n42F7A186BF87\n# 20230413-88, Meritage Resort\nD213B093B79A\n# 20230413-89, Meritage Resort\n216024C49EDF\n# 20230413-90, Gaylord Palms\nD201DBB6AB6E\n# 20230413-91, Residence Inn\n9F4AD875BB30\n# 20230413-92, Marriott\n3352DB1E8777\n# 20230413-94, Marriott\n09074A146605\n151F3E85EC46\n#\n# Travelodge by Wyndham Berkeley\n0000FFFFFFFF\n4663ACD2FFFF\nEDC317193709\n# Hotel Santa Cruz\n75FAB77E2E5B\n# saflok brand HOTEL key\n32F093536677\n# A WaterFront Hotel in  Oakland\n3351916B5A77\n# Ballys (2018)\n336E34CC2177\n# Random Hawaiian Hotel\nA1670589B2AF\n# SF Hotel (SoMa area)\n2E0F00700000\n#\n# Unknown PACS from Western Australia\nCA80E51FA52B\nA71E80EA35E1\n05597810D63D\n#\n# Hotel Key from Las Vegas\nEA0CA627FD06\n80BB8436024C\n5044068C5183\n#\n# Key from Hotel M Montreal (probably diversified)\n7E5E05866ED6\n661ABF99AFAD\n#\n# Key from evo Montreal (probably diversified)\n1064BA5D6DF8\n# Hotel key\nCE0F4F15E909\nD60DE9436219\n#\n# ATM Area de Girona, spanish transport card\nA01000000000\nA02000000000\nA03000000000\nA04000000000\nA05000000000\nA06000000000\nA07000000000\nA08000000000\nA09000000000\nA10000000000\nA11000000000\nA12000000000\nA13000000000\nA14000000000\nA15000000000\nB01000000000\nB02000000000\nB03000000000\nB04000000000\nB05000000000\nB06000000000\nB07000000000\nB08000000000\nB09000000000\nB10000000000\nB11000000000\nB12000000000\nB13000000000\nB14000000000\nB15000000000\n#\n# Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard\nA7AE4A5A33DC\n6B857B568C10\nE2CE9A674CBE\nA4896B2EBA4E\n0724DF9AEDE8\n0E368FB140C1\n874EB25C8721\n5C313F4539CD\nC5498606E0A8\n79C69F7EC7C0\nDA7DD0044DA2\n1B8189BD966B\n765584147990\n4B7C7C315E6E\n46CAAD12C524\n53BD03DEA5C9\nD2D72CB60F59\n14D258786538\nE2E89A375B36\nB3FA87DB0C45\n44D3B1561B34\n2817C6E02F97\nA513FF1232E9\nBD454BD52792\n391771654DC8\n5162797F8E1C\nF700BD8E042D\n3973ABFD8B66\nCE8BFF3728EE\n09938D05DA78\nEACDA4DBE420\nEC2B9FD483CA\n#\n# Hotel Intelier Orange - Benicasim, Spain\n# block 1 - key A\n04256CFE0425\n#\n# InsideWash Membership Card - Portugal\nC18063858BB9\n#\n# An apartment building in Sydney Olympic Park\n13254608D0AB\n24A2971BC0B2\n14264709D1AC\n25A3981CC1B3\n1527480AD2AD\n26A4991DC2B4\n1628490BD3AE\n27A59A1EC3B5\n17294A0CD4AF\n28A69B1FC4B6\n182A4B0DD5B0\n29A79C20C5B7\n192B4C0ED6B1\n2AA89D21C6B8\n1A2C4D0FD7B2\n2BA99E22C7B9\n1B2D4E10D8B3\n2CAA9F23C8BA\n1C2E4F11D9B4\n2DABA024C9BB\n1D2F5012DAB5\n2EACA125CABC\n1E305113DBB6\n2FADA226CBBD\n1F315214DCB7\n30AEA327CCBE\n20325315DDB8\n31AFA428CDBF\n21335416DEB9\n32B0A529CEC0\n22345517DFBA\n33B1A62ACFC1\n#\n# Universidade de São Paulo (USP) student card\n17B50E38F1B0\n24E311F594CE\n3794FBFB1A54\n43B229069F6A\n4531952F765F\n4943F2F35E0A\n4985E681EF88\n4F56C88E0337\n710070E92C79\n8A036C5C35D4\nA027BD830A06\nD33673C19243\nD89A506542F2\nE5813CD228F1\nFAB943906E9C\n#\n# R.A.T.T transport card key A/B\nAA034F342A55\n456776908C48\n\n# BusFacil - Brazilian public transport card for some cities\n7b296f353c6b\n3fa7217ec575\nfae9b14365a9\nc567dd4a6004\nc567dd4a6005\nc567dd4a6006\nc567dd4a6007\nc567dd4a6008\nc567dd4a6009\nc567dd4a600a\nc567dd4a600d\nc567dd4a600e\nc567dd4a600f\n5ef014ec5d7f\n5086052022ac\nbd6af9754c18\n5d67d4732a7d\n17fe45604a04\n17fe45604a05\n17fe45604a06\n17fe45604a07\n17fe45604a08\n17fe45604a09\n17fe45604a0a\n17fe45604a0d\n17fe45604a0e\n17fe45604a0f\n# keys for swimming pool cards in Reykjavík Iceland\n28220F14BEF0\n# key for Orkan keyfobs\n300724070486\n# key for Atlantsolía keyfobs\n60FCB3C42ABF\n# key for hotel in greece\n722F24F0722F\n# STS Hotel 2A\n535453535453\n# Public transport in Sofia, Bulgaria (SKGT)\n# upgraded to DESFire since January 2024\n# SKGT common\n# Sector 15, key A\nf618b3d7855a\n# Sector 15, key B\nf1afa4da949f\n# SKGT multi-use ticket\n# Sector 0\n67362dace527\n633a010fa3c3\n# Sector 1\nf93c98655b9c\n67ec0a47b0fb\n# Sector 2\n54a028818ac7\nb2e87e53c5a0\n# Sector 3\n3e93cf0644b6\n79e12280e219\n# Sector 4\n2204b9fbf033\n4537fd238c8e\n# Sector 5\nd1b44a9df05f\ncfa526835a1f\n# Sector 6\n21cc007ad81c\nc097d0a85446\n# Sector 7\nd2268262710f\n730bb7b8b3de\n# Sector 8\n9fe7c5be7dff\n61ae2d920c79\n# Sector 9\n78fcd4470c50\nb638caf7357b\n# Sector 10\n0dc1dd7c8ea2\n4c6a6866b934\n# Sector 11\n03de2ceb2ea1\n93e0118b21ed\n# Sector 12\n8fbced387bf4\nf57ca95c6edd\n# Sector 13\nef24fe3b4cf7\n8b44d303d62f\n# Sector 14\nb1ea40b2caa6\n3abf8431003b\n# Sector 15 - see above\n# SKGT personalised subscription card\n# Sector 0, 2, 16, key A\na0a1a2a3a4a5\n# Sector 8-14, 17-39, key A\nffffffffffff\n# Sector 1, key A\n# blue\nf1df0ca8948b\n# yellow\n7747b4912984\n# Sector 3, key A\n# blue\n09d556d57a4b\n# yellow\n3ed158c6934e\n# Sector 4-7, key A\n# blue\n839dedbfec0d\n# yellow\nc694a9ed2f9e\n# Sector 15 - see above\n# Sector 0, 16, key B (blue)\n81d55f4551b9\n# Sector 0, key B (yellow)\n6e9a040c3c91\n# Sector 1, key B\n# blue\n5b72c63fb416\n# yellow\na3cdced46371\n# Sector 2, key B\n# blue\n87a61433d026\n# yellow\n9cd3a81f11ab\n# Sector 3, key B\n# blue\n7070d331360c\n# yellow\n836c790f6e2c\n# Sector 4-7, key B\n# blue\n7fe057787c4f\n# yellow\nff59c6d13f88\n# Sector 8-14, 17-39, key B\n536f6669614d\n# Sector 15 - see above\n# End of SKGT\n# Hanoi Bus Rapid Transit - 1/2/3 A\nAAAFBA10FC37\nC61F2C28DADF\n23AACA30CBF2\n#\n# Keys dumped from student ID (AGH Cracow - Poland), may be diversified\n# Need to be verified\n833E4F32589E\n432D02DA59F3\n5C161CA2716F\nF60B5F9666B8\n98EAC5321D2F\nCC945E3FE5C4\n70783C436CF4\n2D186C7149A9\n5D60AC0939FC\n93A5CE63C873\n87174550E900\n45675B25A3DA\nF91750E629D5\nA3E662ABCDC8\n33D99E9FFA6A\nFF7AABA39C61\nA8248C049BEA\nC2AF731771C4\n9263B2E0DD80\nCE7FCCBBA5D8\nF8E385E5A2A0\nB27678B5C4AE\nD68D7EBB9551\n7AB63F082328\n#\n# Payment cards used by Eurest on certain campuses\n7E2BC58168EB\n#\n# Shower cards provided by Seijsener\n291A65CBEA7B\n344A359BBAD9\n476572726974\n4D696368656C\n4F3748E6C826\n69D40AF8B353\n72DEA10F21DF\n74845AA8E3F1\n8C3C43EDCC55\nACD30DFFB434\nD1A27C8EC5DF\nF14D329CBDBE\n#\n# Hotel cards from Austria\nAB287B3B4903\n7B0DEDA7E162\n#\n#Metro Q transit cards from Huston, Texas\n373B72D34B80\nBF3FFC245C9b\n7D1D9E7CF8A7\n6A917BF357E6\nB1D461EC62CA\nC6BECABEBE8A\n66026782D435\n4547E34E40D9\n753897b99AAE\n1C36761E8ABD\n6D8FBD8CC524\n5A3274779706\n23F13602CC1A\n511C1C2C9804\nF8B2B926555E\n2593E37D9B2E\n41A1F17EE990\n64DD48AEDE88\n7915ED4D9903\nD139DD71DB92\n216D97D46E88\nD9D1C447E427\n911E789433CB\n93B43D689F85\n525A869053F1\n69B25667E0B4\n6AACA2D97645\n# UK London Office\n435DF6296EC4\n2338B4913222\n# Acces card of students, and more in Occitanie, France\nE9A553102EA5\nF982E971CFED\n1F42AB9159EE\nBBFB836A48B8\nB5D170B2E8F5\nE76978A05F10\n0B1A995DD007\n650DB9CEDB6B\n13E54B4448B7\n3E3540C2C273\nA76152840117\n066CCC7666BC\n3C0B3AC3AFA3\nCCB541598D72\n1988B5D48EC3\n892EEF0D30FB\n0FE5CE5CC640\n# Volgograd (Russia) Volna transport cards keys\n2B787A063D5D\nD37C8F1793F7\n# H World Hotel Chain Room Keys\n543071543071\n5F01015F0101\n200510241234\n"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc",
    "content": "# Sample Key (BREAKMEIFYOUCAN!)\n425245414B4D454946594F5543414E21\n# Hexadecimal-Reversed Sample Key\n12E4143455F495649454D4B414542524\n# Byte-Reversed Sample Key (!NACUOYFIEMKAERB)\n214E4143554F594649454D4B41455242\n# Semnox Key (IEMKAERB!NACUOY )\n49454D4B41455242214E4143554F5900\n# Modified Semnox Key (IEMKAERB!NACUOYF)\n49454D4B41455242214E4143554F5946\n\n# Mix of Proxmark and ChameleonMiniLiveDebugger\n00000000000000000000000000000000\n000102030405060708090A0B0C0D0E0F\n01010101010101010101010101010101\nFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n00112233445566778899AABBCCDDEEFF\n47454D5850524553534F53414D504C45\n79702553797025537970255379702553\n4E617468616E2E4C6920546564647920\n43464F494D48504E4C4359454E528841\n6AC292FAA1315B4D858AB3A3D7D5933A\n404142434445464748494A4B4C4D4E4F\n2B7E151628AED2A6ABF7158809CF4F3C\nFBEED618357133667C85E08F7236A8DE\nF7DDAC306AE266CCF90BC11EE46D513B\n54686973206973206D79206B65792020\nA0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7\nB0B1B2B3B4B5B6B7B0B1B2B3B4B5B6B7\nB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF\nD3F7D3F7D3F7D3F7D3F7D3F7D3F7D3F7\n11111111111111111111111111111111\n22222222222222222222222222222222\n33333333333333333333333333333333\n44444444444444444444444444444444\n55555555555555555555555555555555\n66666666666666666666666666666666\n77777777777777777777777777777777\n88888888888888888888888888888888\n99999999999999999999999999999999\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\nCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n0102030405060708090A0B0C0D0E0F10\n00010203040506070809101112131415\n01020304050607080910111213141516\n16151413121110090807060504030201\n15141312111009080706050403020100\n0F0E0D0C0B0A09080706050403020100\n100F0E0D0C0B0A090807060504030201\n303132333435363738393A3B3C3D3E3F\n9CABF398358405AE2F0E2B3D31C99A8A\n605F5E5D5C5B5A59605F5E5D5C5B5A59\n"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/skylanders.nfc",
    "content": "Filetype: Flipper NFC resources\nVersion: 1\n# ID: Name\n0000: Whirlwind\n0001: Sonic Boom\n0002: Warnado\n0003: Lightning Rod\n0004: Bash\n0194: Bash\n0005: Terrafin\n0006: Dino-Rang\n0007: Prism Break\n0008: Sunburn\n0009: Eruptor\n000A: Ignitor\n000B: Flameslinger\n000C: Zap\n000D: Wham-Shell\n000E: Gill Grunt\n000F: Slam Bam\n0010: Spyro\n01A0: Spyro\n0011: Voodood\n0012: Double Trouble\n0013: Trigger Happy\n01A3: Trigger Happy\n0014: Drobot\n0015: Drill Sergeant\n0016: Boomer\n0017: Wrecking Ball\n0018: Camo\n0019: Zook\n001A: Stealth Elf\n001B: Stump Smash\n001C: Dark Spyro\n001D: Hex\n001E: Chop Chop\n01AE: Chop Chop\n001F: Ghost Roaster\n0020: Cynder\n0064: Jet Vac\n0065: Swarm\n0066: Crusher\n0067: Flashwing\n0068: Hot Head\n0069: Hot Dog\n006A: Chill\n006B: Thumpback\n006C: Pop Fizz\n006D: Ninjini\n006E: Bouncer\n006F: Sprocket\n0070: Tree Rex\n0071: Shroomboom\n0072: Eye-Brawl\n0073: Fright Rider\n00C8: Anvil Rain\n00C9: Treasure Chest\n00CA: Healing Elixer\n00CB: Ghost Swords\n00CC: Time Twister\n00CD: Sky-Iron Shield\n00CE: Winged Boots\n00CF: Sparx Dragonfly\n00D0: Dragonfire Cannon\n00D1: Scorpion Striker Catapult\n00D2: Trap - Magic\n00D3: Trap - Water\n00D4: Trap - Air\n00D5: Trap - Undead\n00D6: Trap - Tech\n00D7: Trap - Fire\n00D8: Trap - Earth\n00D9: Trap - Life\n00DA: Trap - Light\n00DB: Trap - Dark\n00DC: Trap - Kaos\n00E6: Hand Of Fate\n00E7: Piggy Bank\n00E8: Rocket Ram\n00E9: Tiki Speaky\n00EB: Imaginite Mystery Chest\n012C: Dragons Peak\n012D: Empire of Ice\n012E: Pirate Seas\n012F: Darklight Crypt\n0130: Volcanic Vault\n0131: Mirror Of Mystery\n0132: Nightmare Express\n0133: Sunscraper Spire\n0134: Midnight Museum\n01C2: Gusto\n01C3: Thunderbolt\n01C4: Fling Kong\n01C5: Blades\n01C6: Wallop\n01C7: Head Rush\n01C8: Fist Bump\n01C9: Rocky Roll\n01CA: Wildfire\n01CB: Ka Boom\n01CC: Trail Blazer\n01CD: Torch\n01CE: Snap Shot\n01CF: Lob Star\n01D0: Flip Wreck\n01D1: Echo\n01D2: Blastermind\n01D3: Enigma\n01D4: Deja Vu\n01D5: Cobra Cadabra\n01D6: Jawbreaker\n01D7: Gearshift\n01D8: Chopper\n01D9: Tread Head\n01DA: Bushwhack\n01DB: Tuff Luck\n01DC: Food Fight\n01DD: High Five\n01DE: Krypt King\n01DF: Short Cut\n01E0: Bat Spin\n01E1: Funny Bone\n01E2: Knight light\n01E3: Spotlight\n01E4: Knight Mare\n01E5: Blackout\n01F6: Bop\n01F7: Spry\n01F8: Hijinx\n01F9: Terrabite\n01FA: Breeze\n01FB: Weeruptor\n01FC: Pet Vac\n01FD: Small Fry\n01FE: Drobit\n0202: Gill Runt\n0207: Trigger Snappy\n020E: Whisper Elf\n021C: Barkley\n021D: Thumpling\n021E: Mini Jini\n021F: Eye Small\n0259: King Pen\n0265: Golden Queen\n02AD: Fire Acorn\n03E8: (Boom) Jet\n03E9: (Free) Ranger\n03EA: (Rubble) Rouser\n03EB: (Doom) Stone\n03EC: Blast Zone\n03ED: (Fire) Kraken\n03EE: (Stink) Bomb\n03EF: (Grilla) Drilla\n03F0: (Hoot) Loop\n03F1: (Trap) Shadow\n03F2: (Magna) Charge\n03F3: (Spy) Rise\n03F4: (Night) Shift\n03F5: (Rattle) Shake\n03F6: (Freeze) Blade\n03F7: Wash Buckler\n07D0: Boom (Jet)\n07D1: Free (Ranger)\n07D2: Rubble (Rouser)\n07D3: Doom (Stone)\n07D4: Blast Zone (Head)\n07D5: Fire (Kraken)\n07D6: Stink (Bomb)\n07D7: Grilla (Drilla)\n07D8: Hoot (Loop)\n07D9: Trap (Shadow)\n07DA: Magna (Charge)\n07DB: Spy (Rise)\n07DC: Night (Shift)\n07DD: Rattle (Shake)\n07DE: Freeze (Blade)\n07DF: Wash Buckler (Head)\n0BB8: Scratch\n0BB9: Pop Thorn\n0BBA: Slobber Tooth\n0BBB: Scorp\n0BBC: Fryno\n0BBD: Smolderdash\n0BBE: Bumble Blast\n0BBF: Zoo Lou\n0BC0: Dune Bug\n0BC1: Star Strike\n0BC2: Countdown\n0BC3: Wind Up\n0BC4: Roller Brawl\n0BC5: Grim Creeper\n0BC6: Rip Tide\n0BC7: Punk Shock\n0C80: Battle Hammer\n0C81: Sky Diamond\n0C82: Platinum Sheep\n0C83: Groove Machine\n0C84: UFO Hat\n0C94: Jet Stream\n0C95: Tomb Buggy\n0C96: Reef Ripper\n0C97: Burn Cycle\n0C98: Hot Streak\n0C99: Shark Tank\n0C9A: Thump Truck\n0C9B: Crypt Crusher\n0C9C: Stealth Stinger\n0C9F: Dive Bomber\n0CA0: Sky Slicer\n0CA1: Clown Cruiser\n0CA2: Gold Rusher\n0CA3: Shield Striker\n0CA4: Sun Runner\n0CA5: Sea Shadow\n0CA6: Splatter Splasher\n0CA7: Soda Skimmer\n0CA8: Barrel Blaster\n0CA9: Buzz Wing\n0CE4: Sheep Wreck Island\n0CE5: Tower of Time\n0CE6: Fiery Forge\n0CE7: Arkeyan Crossbow\n0D48: Fiesta\n0D49: High Volt\n0D4A: Splat\n0D4E: Stormblade\n0D53: Smash It\n0D54: Spitfire\n0D55: Hurricane Jet-Vac\n0D56: Double Dare Trigger Happy\n0D57: Super Shot Stealth Elf\n0D58: Shark Shooter Terrafin\n0D59: Bone Bash Roller Brawl\n0D5C: Big Bubble Pop Fizz\n0D5D: Lava Lance Eruptor\n0D5E: Deep Dive Gill Grunt\n0D5F: Turbo Charge Donkey Kong\n0D60: Hammer Slam Bowser\n0D61: Dive-Clops\n0D62: Astroblast\n0D63: Nightfall\n0D64: Thrillipede\n0DAC: Sky Trophy\n0DAD: Land Trophy\n0DAE: Sea Trophy\n0DAF: Kaos Trophy\n"
  },
  {
    "path": "applications/main/nfc/resources/nfc/assets/vendors.nfc",
    "content": "Filetype: NFC Vendors\nVersion: 1\n# Please do not change IDs in this list. Add new to the end if necessary.\n# ID: \"Vendor Country\"\n1: Motorola UK\n2: ST Microelectronics SA France\n3: Hitachi, Ltd Japan\n4: NXP Semiconductors Germany\n5: Infineon Technologies AG Germany\n6: Cylink USA\n7: Texas Instrument France\n8: Fujitsu Limited Japan\n9: Matsushita Electronics Corporation, Semiconductor Company Japan\n10: NEC Japan\n11: Oki Electric Industry Co. Ltd Japan\n12: Toshiba Corp. Japan\n13: Mitsubishi Electric Corp. Japan\n14: Samsung Electronics Co. Ltd Korea\n15: Hynix / Hyundai, Korea\n16: LG-Semiconductors Co. Ltd Korea\n17: Emosyn-EM Microelectronics USA\n18: INSIDE Technology France\n19: ORGA Kartensysteme GmbH Germany\n20: SHARP Corporation Japan\n21: ATMEL France\n22: EM Microelectronic-Marin SA Switzerland\n23: KSW Microtec GmbH Germany\n24: ZMD AG Germany\n25: XICOR, Inc. USA\n26: Sony Corporation Japan\n27: Malaysia Microelectronic Solutions Sdn. Bhd Malaysia\n28: Emosyn USA\n29: Shanghai Fudan Microelectronics Co. Ltd. P.R. China\n30: Magellan Technology Pty Limited Australia\n31: Melexis NV BO Switzerland\n32: Renesas Technology Corp. Japan\n33: TAGSYS France\n34: Transcore USA\n35: Shanghai belling corp., ltd. China\n36: Masktech Germany Gmbh Germany\n37: Innovision Research and Technology Plc UK\n38: Hitachi ULSI Systems Co., Ltd. Japan\n39: Cypak AB Sweden\n40: Ricoh Japan\n41: ASK France\n42: Unicore Microsystems, LLC Russian Federation\n43: Dallas Semiconductor/Maxim USA\n44: Impinj, Inc. USA\n45: RightPlug Alliance USA\n46: Broadcom Corporation USA\n47: MStar Semiconductor, Inc Taiwan, ROC\n48: BeeDar Technology Inc. USA\n49: RFIDsec Denmark\n50: Schweizer Electronic AG Germany\n51: AMIC Technology Corp Taiwan\n52: Mikron JSC Russia\n53: Fraunhofer Institute for Photonic Microsystems Germany\n54: IDS Microchip AG Switzerland\n55: Thinfilm - Kovio USA\n56: HMT Microelectronic Ltd Switzerland\n57: Silicon Craft Technology Thailand\n58: Advanced Film Device Inc. Japan\n59: Nitecrest Ltd UK\n60: Verayo Inc. USA\n61: HID Global USA\n62: Productivity Engineering Gmbh Germany\n63: Austriamicrosystems AG (reserved) Austria\n64: Gemalto SA France\n65: Renesas Electronics Corporation Japan\n66: 3Alogics Inc Korea\n67: Top TroniQ Asia Limited Hong Kong\n68: Gentag Inc. USA\n69: Invengo Information Technology Co.Ltd China\n70: Guangzhou Sysur Microelectronics, Inc China\n71: CEITEC S.A. Brazil\n72: Shanghai Quanray Electronics Co. Ltd. China\n73: MediaTek Inc Taiwan\n74: Angstrem PJSC Russia\n75: Celisic Semiconductor (Hong Kong) Limited China\n76: LEGIC Identsystems AG Switzerland\n77: Balluff GmbH Germany\n78: Oberthur Technologies France\n79: Silterra Malaysia Sdn. Bhd. Malaysia\n80: DELTA Danish Electronics, Light & Acoustics Denmark\n81: Giesecke & Devrient GmbH Germany\n82: Shenzhen China Vision Microelectronics Co., Ltd. China\n83: Shanghai Feiju Microelectronics Co. Ltd. China\n84: Intel Corporation USA\n85: Microsensys GmbH Germany\n86: Sonix Technology Co., Ltd. Taiwan\n87: Qualcomm Technologies Inc USA\n88: Realtek Semiconductor Corp Taiwan\n89: Freevision Technologies Co. Ltd China\n90: Giantec Semiconductor Inc. China\n91: JSC Angstrem-T Russia\n92: STARCHIP France\n93: SPIRTECH France\n94: GANTNER Electronic GmbH Austria\n95: Nordic Semiconductor Norway\n96: Verisiti Inc USA\n97: Wearlinks Technology Inc. China\n98: Userstar Information Systems Co., Ltd Taiwan\n99: Pragmatic Printing Ltd. UK\n100: Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil\n101: Tendyron Corporation China\n102: MUTO Smart Co., Ltd. Korea\n103: ON Semiconductor USA\n104: TUBITAK BILGEM Turkey\n105: Huada Semiconductor Co., Ltd China\n106: SEVENEY France\n107: ISSM France\n108: Wisesec Ltd Israel\n124: DB HiTek Co Ltd Korea\n125: SATO Vicinity Australia\n126: Holtek Taiwan\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene.c",
    "content": "#include \"nfc_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const nfc_on_enter_handlers[])(void*) = {\n#include \"nfc_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const nfc_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"nfc_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const nfc_on_exit_handlers[])(void* context) = {\n#include \"nfc_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers nfc_scene_handlers = {\n    .on_enter_handlers = nfc_on_enter_handlers,\n    .on_event_handlers = nfc_on_event_handlers,\n    .on_exit_handlers = nfc_on_exit_handlers,\n    .scene_num = NfcSceneNum,\n};\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) NfcScene##id,\ntypedef enum {\n#include \"nfc_scene_config.h\"\n    NfcSceneNum,\n} NfcScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers nfc_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"nfc_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"nfc_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"nfc_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_config.h",
    "content": "ADD_SCENE(nfc, start, Start)\nADD_SCENE(nfc, file_select, FileSelect)\nADD_SCENE(nfc, saved_menu, SavedMenu)\nADD_SCENE(nfc, save_name, SaveName)\nADD_SCENE(nfc, save_success, SaveSuccess)\nADD_SCENE(nfc, delete, Delete)\nADD_SCENE(nfc, delete_success, DeleteSuccess)\nADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm)\nADD_SCENE(nfc, restore_original, RestoreOriginal)\n\nADD_SCENE(nfc, detect, Detect)\nADD_SCENE(nfc, read, Read)\nADD_SCENE(nfc, info, Info)\nADD_SCENE(nfc, more_info, MoreInfo)\nADD_SCENE(nfc, supported_card, SupportedCard)\nADD_SCENE(nfc, select_protocol, SelectProtocol)\nADD_SCENE(nfc, extra_actions, ExtraActions)\nADD_SCENE(nfc, read_success, ReadSuccess)\nADD_SCENE(nfc, read_menu, ReadMenu)\nADD_SCENE(nfc, emulate, Emulate)\nADD_SCENE(nfc, rpc, Rpc)\nADD_SCENE(nfc, debug, Debug)\nADD_SCENE(nfc, field, Field)\nADD_SCENE(nfc, retry_confirm, RetryConfirm)\nADD_SCENE(nfc, exit_confirm, ExitConfirm)\nADD_SCENE(nfc, save_confirm, SaveConfirm)\n\nADD_SCENE(nfc, mf_ultralight_c_dict_attack, MfUltralightCDictAttack)\nADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite)\nADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess)\nADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail)\nADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard)\nADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)\nADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)\nADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)\nADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)\nADD_SCENE(nfc, des_auth_key_input, DesAuthKeyInput)\nADD_SCENE(nfc, des_auth_unlock_warn, DesAuthUnlockWarn)\n\nADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)\nADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)\n\nADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)\nADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader)\nADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo)\nADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete)\nADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial)\nADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess)\nADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard)\nADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial)\nADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess)\nADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail)\nADD_SCENE(nfc, mf_classic_write_initial_wrong_card, MfClassicWriteInitialWrongCard)\n\nADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)\nADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)\nADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)\nADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)\nADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)\n\nADD_SCENE(nfc, mf_ultralight_c_keys, MfUltralightCKeys)\nADD_SCENE(nfc, mf_ultralight_c_keys_list, MfUltralightCKeysList)\nADD_SCENE(nfc, mf_ultralight_c_keys_delete, MfUltralightCKeysDelete)\nADD_SCENE(nfc, mf_ultralight_c_keys_add, MfUltralightCKeysAdd)\nADD_SCENE(nfc, mf_ultralight_c_keys_warn_duplicate, MfUltralightCKeysWarnDuplicate)\n\nADD_SCENE(nfc, set_type, SetType)\nADD_SCENE(nfc, set_sak, SetSak)\nADD_SCENE(nfc, set_atqa, SetAtqa)\nADD_SCENE(nfc, set_uid, SetUid)\n\nADD_SCENE(nfc, slix_unlock_menu, SlixUnlockMenu)\nADD_SCENE(nfc, slix_key_input, SlixKeyInput)\nADD_SCENE(nfc, slix_unlock, SlixUnlock)\nADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)\n\nADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)\nADD_SCENE(nfc, felica_system, FelicaSystem)\n\nADD_SCENE(nfc, generate_info, GenerateInfo)\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_debug.c",
    "content": "#include \"../nfc_app_i.h\"\n\nenum SubmenuDebugIndex {\n    SubmenuDebugIndexField,\n    SubmenuDebugIndexApdu,\n};\n\nvoid nfc_scene_debug_submenu_callback(void* context, uint32_t index) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);\n}\n\nvoid nfc_scene_debug_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n\n    submenu_add_item(\n        submenu, \"Field\", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc);\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug));\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuDebugIndexField) {\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneField);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_debug_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_delete.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {\n    NfcApp* nfc = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_delete_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup Custom Widget view\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n\n    furi_string_printf(temp_str, \"\\e#Delete %s?\\e#\", furi_string_get_cstr(nfc->file_name));\n    widget_add_text_box_element(\n        nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false);\n    widget_add_button_element(\n        nfc->widget, GuiButtonTypeLeft, \"Cancel\", nfc_scene_delete_widget_callback, nfc);\n    widget_add_button_element(\n        nfc->widget, GuiButtonTypeRight, \"Delete\", nfc_scene_delete_widget_callback, nfc);\n\n    size_t uid_len;\n    const uint8_t* uid = nfc_device_get_uid(nfc->nfc_device, &uid_len);\n\n    furi_string_set(temp_str, \"UID:\");\n    for(size_t i = 0; i < uid_len; i++) {\n        furi_string_cat_printf(temp_str, \" %02X\", uid[i]);\n    }\n    widget_add_string_element(\n        nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));\n\n    furi_string_set_str(temp_str, nfc_device_get_name(nfc->nfc_device, NfcDeviceNameTypeFull));\n    widget_add_string_element(\n        nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        } else if(event.event == GuiButtonTypeRight) {\n            if(nfc_delete(nfc)) {\n                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       nfc->scene_manager, NfcSceneStart)) {\n                    scene_manager_stop(nfc->scene_manager);\n                    view_dispatcher_stop(nfc->view_dispatcher);\n                }\n            }\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_delete_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    widget_reset(nfc->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_delete_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_delete_success_popup_callback(void* context) {\n    NfcApp* nfc = context;\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_delete_success_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup view\n    Popup* popup = nfc->popup;\n    popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(popup, \"Deleted\", 80, 19, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, nfc);\n    popup_set_callback(popup, nfc_scene_delete_success_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfClassicKeys);\n            } else if(scene_manager_has_previous_scene(\n                          nfc->scene_manager, NfcSceneMfUltralightCKeys)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfUltralightCKeys);\n            } else {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n\n                if(!consumed) {\n                    scene_manager_stop(nfc->scene_manager);\n                    view_dispatcher_stop(nfc->view_dispatcher);\n                }\n            }\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_delete_success_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear view\n    popup_reset(nfc->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_des_auth_key_input_byte_input_callback(void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_scene_des_auth_key_input_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup view\n    NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);\n    uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data :\n                                                     nfc->mf_ul_auth->tdes_key.data;\n\n    ByteInput* byte_input = nfc->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter key in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_scene_des_auth_key_input_byte_input_callback,\n        NULL,\n        nfc,\n        key,\n        FELICA_DATA_BLOCK_SIZE);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_des_auth_key_input_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    UNUSED(event);\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);\n\n            if(protocol == NfcProtocolFelica) {\n                nfc->felica_auth->skip_auth = false;\n            } else {\n                nfc->mf_ul_auth->type = MfUltralightAuthTypeManual;\n            }\n\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneDesAuthUnlockWarn);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_des_auth_key_input_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear view\n    byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(nfc->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_des_auth_unlock_warn_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_des_auth_unlock_warn_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    const char* message = \"Risky Action!\";\n    DialogEx* dialog_ex = nfc->dialog_ex;\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_des_auth_unlock_warn_dialog_callback);\n\n    dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);\n\n    FuriString* str = furi_string_alloc();\n    furi_string_cat_printf(str, \"Unlock with key: \");\n\n    NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);\n    uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data :\n                                                     nfc->mf_ul_auth->tdes_key.data;\n\n    for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++)\n        furi_string_cat_printf(str, \"%02X \", key[i]);\n    furi_string_cat_printf(str, \"?\");\n\n    nfc_text_store_set(nfc, furi_string_get_cstr(str));\n    furi_string_free(str);\n\n    dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 12, AlignLeft, AlignTop);\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Unlock\");\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_des_auth_unlock_warn_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    UNUSED(event);\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultRight) {\n            nfc->felica_auth->skip_auth = false;\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);\n            consumed = true;\n        } else if(event.event == DialogExResultLeft) {\n            scene_manager_previous_scene(nfc->scene_manager);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_des_auth_unlock_warn_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    dialog_ex_reset(nfc->dialog_ex);\n    nfc_text_store_clear(nfc);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_detect.c",
    "content": "#include \"../nfc_app_i.h\"\n#include <dolphin/dolphin.h>\n\nvoid nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) {\n    furi_assert(context);\n\n    NfcApp* instance = context;\n\n    if(event.type == NfcScannerEventTypeDetected) {\n        nfc_detected_protocols_set(\n            instance->detected_protocols, event.data.protocols, event.data.protocol_num);\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit);\n    }\n}\n\nvoid nfc_scene_detect_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    popup_reset(instance->popup);\n    popup_set_header(instance->popup, \"Reading\", 97, 15, AlignCenter, AlignTop);\n    popup_set_text(\n        instance->popup, \"Hold card next\\nto Flipper's back\", 94, 27, AlignCenter, AlignTop);\n    popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n\n    nfc_detected_protocols_reset(instance->detected_protocols);\n\n    instance->scanner = nfc_scanner_alloc(instance->nfc);\n    nfc_scanner_start(instance->scanner, nfc_scene_detect_scan_callback, instance);\n\n    nfc_blink_detect_start(instance);\n}\n\nbool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventWorkerExit) {\n            if(nfc_detected_protocols_get_num(instance->detected_protocols) > 1) {\n                notification_message(instance->notifications, &sequence_single_vibro);\n                scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol);\n            } else {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneRead);\n            }\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_detect_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_scanner_stop(instance->scanner);\n    nfc_scanner_free(instance->scanner);\n    popup_reset(instance->popup);\n\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_emulate.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_emulate_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneEmulate, context);\n}\n\nbool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneEmulate, context, event);\n}\n\nvoid nfc_scene_emulate_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneEmulate, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_exit_confirm.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_exit_confirm_on_enter(void* context) {\n    NfcApp* nfc = context;\n    DialogEx* dialog_ex = nfc->dialog_ex;\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Exit\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Stay\");\n    dialog_ex_set_header(dialog_ex, \"Exit to NFC Menu?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(dialog_ex, \"All unsaved data will be lost\", 64, 12, AlignCenter, AlignTop);\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultRight) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        } else if(event.event == DialogExResultLeft) {\n            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) &&\n               (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) ||\n                scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) {\n                const uint32_t possible_scenes[] = {NfcSceneReadMenu, NfcSceneSavedMenu};\n                consumed = scene_manager_search_and_switch_to_previous_scene_one_of(\n                    nfc->scene_manager, possible_scenes, COUNT_OF(possible_scenes));\n            } else {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneStart);\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_exit_confirm_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clean view\n    dialog_ex_reset(nfc->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_extra_actions.c",
    "content": "#include \"../nfc_app_i.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexReadCardType,\n    SubmenuIndexMfClassicKeys,\n    SubmenuIndexMfUltralightCKeys,\n    SubmenuIndexMfUltralightUnlock,\n    SubmenuIndexSlixUnlock,\n};\n\nvoid nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_scene_extra_actions_on_enter(void* context) {\n    NfcApp* instance = context;\n    Submenu* submenu = instance->submenu;\n\n    submenu_add_item(\n        submenu,\n        \"Read Specific Card Type\",\n        SubmenuIndexReadCardType,\n        nfc_scene_extra_actions_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"MIFARE Classic Keys\",\n        SubmenuIndexMfClassicKeys,\n        nfc_scene_extra_actions_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"MIFARE Ultralight C Keys\",\n        SubmenuIndexMfUltralightCKeys,\n        nfc_scene_extra_actions_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"Unlock NTAG/Ultralight\",\n        SubmenuIndexMfUltralightUnlock,\n        nfc_scene_extra_actions_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"Unlock SLIX-L\",\n        SubmenuIndexSlixUnlock,\n        nfc_scene_extra_actions_submenu_callback,\n        instance);\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(instance->scene_manager, NfcSceneExtraActions));\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexMfClassicKeys) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);\n            consumed = true;\n        } else if(event.event == SubmenuIndexMfUltralightCKeys) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeys);\n            consumed = true;\n        } else if(event.event == SubmenuIndexMfUltralightUnlock) {\n            mf_ultralight_auth_reset(instance->mf_ul_auth);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);\n            consumed = true;\n        } else if(event.event == SubmenuIndexReadCardType) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSlixUnlock) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlockMenu);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneExtraActions, event.event);\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_extra_actions_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    submenu_reset(instance->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_felica_more_info.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n#include \"../helpers/protocol_support/felica/felica_render.h\"\n\nenum {\n    FelicaMoreInfoStateMenu,\n    FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index\n};\n\nenum SubmenuIndex {\n    SubmenuIndexDynamic, // dynamic indices start here\n};\n\nvoid nfc_scene_felica_more_info_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n\n    const uint32_t state =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);\n    const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);\n\n    switch(data->workflow_type) {\n    case FelicaLite:\n        widget_reset(nfc->widget);\n        FuriString* temp_str = furi_string_alloc();\n        nfc_more_info_render_felica_lite_dump(data, temp_str);\n        widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n        furi_string_free(temp_str);\n        view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);\n        return;\n        break;\n    case FelicaStandard:\n        FuriString* label = furi_string_alloc();\n\n        for(uint32_t i = 0; i < simple_array_get_count(data->systems); ++i) {\n            const FelicaSystem* system = simple_array_cget(data->systems, i);\n            furi_string_printf(label, \"System %04X\", system->system_code);\n            submenu_add_item(\n                submenu,\n                furi_string_get_cstr(label),\n                i + SubmenuIndexDynamic,\n                nfc_protocol_support_common_submenu_callback,\n                nfc);\n        }\n        furi_string_free(label);\n        break;\n    default:\n        break;\n    }\n\n    if(state >= FelicaMoreInfoStateItem) {\n        submenu_set_selected_item(\n            nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);\n        scene_manager_set_scene_state(\n            nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);\n    }\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    const uint32_t state =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        const uint32_t index = event.event - SubmenuIndexDynamic;\n\n        scene_manager_set_scene_state(\n            nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + index);\n        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, index << 4);\n        scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaSystem);\n        consumed = true;\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(state >= FelicaMoreInfoStateItem) {\n            widget_reset(nfc->widget);\n            text_box_reset(nfc->text_box);\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);\n        } else {\n            widget_reset(nfc->widget);\n            text_box_reset(nfc->text_box);\n            // Return directly to the Info scene\n            scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_felica_more_info_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear views\n    widget_reset(nfc->widget);\n    text_box_reset(nfc->text_box);\n    furi_string_reset(nfc->text_box_store);\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_felica_system.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n#include \"../helpers/protocol_support/felica/felica_render.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexDirectory,\n    SubmenuIndexDynamic, // dynamic indices start here\n};\n\nstatic void nfc_scene_felica_system_submenu_callback(void* context, uint32_t index) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);\n}\n\nvoid nfc_scene_felica_system_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n    submenu_reset(nfc->submenu);\n\n    const uint32_t system_index =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem) >> 4;\n    const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);\n\n    submenu_add_item(\n        submenu, \"Directory\", SubmenuIndexDirectory, nfc_scene_felica_system_submenu_callback, nfc);\n\n    FuriString* label = furi_string_alloc();\n\n    const FelicaSystem* system = simple_array_cget(data->systems, system_index);\n    for(uint32_t i = 0; i < simple_array_get_count(system->services); ++i) {\n        const FelicaService* service = simple_array_cget(system->services, i);\n        bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;\n        if(!is_public) {\n            continue;\n        }\n        furi_string_printf(label, \"Readable serv %04X\", service->code);\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(label),\n            i + SubmenuIndexDynamic,\n            nfc_protocol_support_common_submenu_callback,\n            nfc);\n    }\n\n    furi_string_free(label);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_felica_system_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem);\n    const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);\n\n    const uint32_t system_index = state >> 4;\n    const FelicaSystem* system = simple_array_cget(data->systems, system_index);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        } else {\n            if(event.event == SubmenuIndexDirectory) {\n                FuriString* temp_str = furi_string_alloc();\n                nfc_more_info_render_felica_dir(system, temp_str);\n\n                widget_add_text_scroll_element(\n                    nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));\n                furi_string_free(temp_str);\n\n                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);\n            } else {\n                const uint32_t service_ind =\n                    event.event - SubmenuIndexDynamic; // offset the three enums above\n\n                text_box_reset(nfc->text_box);\n                furi_string_reset(nfc->text_box_store);\n\n                const FelicaService* service = simple_array_cget(system->services, service_ind);\n                furi_string_cat_printf(nfc->text_box_store, \"Service 0x%04X\\n\", service->code);\n                nfc_more_info_render_felica_blocks(\n                    data, system, nfc->text_box_store, service->code);\n\n                text_box_set_font(nfc->text_box, TextBoxFontHex);\n                text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));\n                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);\n            }\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state | 1);\n            consumed = true;\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(state & 1) {\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state & ~1);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_felica_system_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear views\n    widget_reset(nfc->widget);\n    text_box_reset(nfc->text_box);\n    furi_string_reset(nfc->text_box_store);\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_field.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_field_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    furi_hal_nfc_low_power_mode_stop();\n    furi_hal_nfc_poller_field_on();\n    Popup* popup = nfc->popup;\n    popup_set_header(\n        popup,\n        \"Field is on\\nDon't leave device\\nin this mode for too long.\",\n        64,\n        11,\n        AlignCenter,\n        AlignTop);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);\n\n    notification_internal_message(nfc->notifications, &sequence_set_blue_255);\n}\n\nbool nfc_scene_field_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid nfc_scene_field_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    furi_hal_nfc_low_power_mode_start();\n    notification_internal_message(nfc->notifications, &sequence_reset_blue);\n    popup_reset(nfc->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_file_select.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_file_select_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    if(nfc_load_from_file_select(instance)) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneSavedMenu);\n    } else {\n        scene_manager_previous_scene(instance->scene_manager);\n    }\n}\n\nbool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    bool consumed = false;\n    return consumed;\n}\n\nvoid nfc_scene_file_select_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_generate_info.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_generate_info_widget_callback(GuiButtonType result, InputType type, void* context) {\n    NfcApp* instance = context;\n\n    if(type == InputTypeShort) {\n        if(result == GuiButtonTypeRight) {\n            view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n        }\n    }\n}\n\nvoid nfc_scene_generate_info_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);\n    furi_assert((protocol == NfcProtocolMfUltralight) || (protocol == NfcProtocolMfClassic));\n\n    const Iso14443_3aData* iso14443_3a_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a);\n\n    // Setup dialog view\n    Widget* widget = instance->widget;\n    widget_add_button_element(\n        widget, GuiButtonTypeRight, \"More\", nfc_scene_generate_info_widget_callback, instance);\n\n    // Create info text\n    NfcDataGeneratorType type =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneGenerateInfo);\n    const char* name = nfc_data_generator_get_name(type);\n    widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, name);\n    widget_add_string_element(widget, 0, 13, AlignLeft, AlignTop, FontSecondary, \"NFC-A\");\n\n    FuriString* temp_str = furi_string_alloc_printf(\"UID:\");\n    // Append UID\n    for(int i = 0; i < iso14443_3a_data->uid_len; i++) {\n        furi_string_cat_printf(temp_str, \" %02X\", iso14443_3a_data->uid[i]);\n    }\n    widget_add_string_element(\n        widget, 0, 25, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_generate_info_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clean views\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_info.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_info_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneInfo, context);\n}\n\nbool nfc_scene_info_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneInfo, context, event);\n}\n\nvoid nfc_scene_info_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneInfo, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <nfc/protocols/mf_classic/mf_classic_listener.h>\n\n#define NXP_MANUFACTURER_ID (0x04)\n\n#define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX        (10U)\n#define NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS (1000)\n\nstatic const NotificationSequence sequence_detect_reader = {\n    &message_green_255,\n    &message_blue_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nvoid nfc_scene_mf_classic_detect_reader_view_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nNfcCommand nfc_scene_mf_classic_detect_listener_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcApp* instance = context;\n    MfClassicListenerEvent* mfc_event = event.event_data;\n\n    if(mfc_event->type == MfClassicListenerEventTypeAuthContextPartCollected) {\n        MfClassicAuthContext* auth_ctx = &mfc_event->data->auth_context;\n        mfkey32_logger_add_nonce(instance->mfkey32_logger, auth_ctx);\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerUpdate);\n    }\n\n    return NfcCommandContinue;\n}\n\nvoid nfc_scene_mf_classic_timer_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTimerExpired);\n}\n\nvoid nfc_scene_mf_classic_detect_reader_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolInvalid) {\n        Iso14443_3aData iso3_data = {\n            .uid_len = 7,\n            .uid = {0},\n            .atqa = {0x44, 0x00},\n            .sak = 0x08,\n        };\n        iso3_data.uid[0] = NXP_MANUFACTURER_ID;\n        furi_hal_random_fill_buf(&iso3_data.uid[1], iso3_data.uid_len - 1);\n        MfClassicData* mfc_data = mf_classic_alloc();\n\n        mfc_data->type = MfClassicType4k;\n        iso14443_3a_copy(mfc_data->iso14443_3a_data, &iso3_data);\n        nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);\n\n        mf_classic_free(mfc_data);\n    }\n\n    const Iso14443_3aData* iso3_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a);\n    uint32_t cuid = iso14443_3a_get_cuid(iso3_data);\n\n    instance->mfkey32_logger = mfkey32_logger_alloc(cuid);\n    instance->timer =\n        furi_timer_alloc(nfc_scene_mf_classic_timer_callback, FuriTimerTypeOnce, instance);\n\n    detect_reader_set_nonces_max(instance->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);\n    detect_reader_set_callback(\n        instance->detect_reader, nfc_scene_mf_classic_detect_reader_view_callback, instance);\n\n    notification_message(instance->notifications, &sequence_detect_reader);\n\n    instance->listener = nfc_listener_alloc(\n        instance->nfc,\n        NfcProtocolMfClassic,\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic));\n    nfc_listener_start(\n        instance->listener, nfc_scene_mf_classic_detect_listener_callback, instance);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDetectReader);\n}\n\nbool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventWorkerUpdate) {\n            furi_timer_stop(instance->timer);\n            notification_message(instance->notifications, &sequence_blink_start_cyan);\n\n            size_t nonces_pairs = 2 * mfkey32_logger_get_params_num(instance->mfkey32_logger);\n            detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderDetected);\n            detect_reader_set_nonces_collected(instance->detect_reader, nonces_pairs);\n            if(nonces_pairs >= NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) {\n                if(instance->listener) {\n                    nfc_listener_stop(instance->listener);\n                    nfc_listener_free(instance->listener);\n                    instance->listener = NULL;\n                }\n                detect_reader_set_state(instance->detect_reader, DetectReaderStateDone);\n                nfc_blink_stop(instance);\n                notification_message(instance->notifications, &sequence_single_vibro);\n                notification_message(instance->notifications, &sequence_set_green_255);\n            } else {\n                furi_timer_start(instance->timer, NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS);\n            }\n            consumed = true;\n        } else if(event.event == NfcCustomEventTimerExpired) {\n            detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderLost);\n            nfc_blink_stop(instance);\n            notification_message(instance->notifications, &sequence_detect_reader);\n        } else if(event.event == NfcCustomEventViewExit) {\n            if(instance->listener) {\n                nfc_listener_stop(instance->listener);\n                nfc_listener_free(instance->listener);\n                instance->listener = NULL;\n            }\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyNoncesInfo);\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(instance->listener) {\n            nfc_listener_stop(instance->listener);\n            nfc_listener_free(instance->listener);\n            instance->listener = NULL;\n        }\n        mfkey32_logger_free(instance->mfkey32_logger);\n        if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSaveSuccess)) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneStart);\n        } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadSuccess)) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneReadSuccess);\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_detect_reader_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    detect_reader_reset(instance->detect_reader);\n\n    furi_timer_stop(instance->timer);\n    furi_timer_free(instance->timer);\n\n    // Stop notifications\n    nfc_blink_stop(instance);\n    notification_message(instance->notifications, &sequence_reset_green);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <bit_lib/bit_lib.h>\n#include <dolphin/dolphin.h>\n\n#define TAG \"NfcMfClassicDictAttack\"\n\n// TODO FL-3926: Fix lag when leaving the dictionary attack view after Hardnested\n// TODO FL-3926: Re-enters backdoor detection between user and system dictionary if no backdoor is found\n\ntypedef enum {\n    DictAttackStateCUIDDictInProgress,\n    DictAttackStateUserDictInProgress,\n    DictAttackStateSystemDictInProgress,\n} DictAttackState;\n\nNfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerEvent* mfc_event = event.event_data;\n\n    NfcApp* instance = context;\n    if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {\n        instance->nfc_dict_context.is_card_present = true;\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n    } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {\n        instance->nfc_dict_context.is_card_present = false;\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        const MfClassicData* mfc_data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n        mfc_event->data->poller_mode.mode = (instance->nfc_dict_context.enhanced_dict) ?\n                                                MfClassicPollerModeDictAttackEnhanced :\n                                                MfClassicPollerModeDictAttackStandard;\n        mfc_event->data->poller_mode.data = mfc_data;\n        instance->nfc_dict_context.sectors_total =\n            mf_classic_get_total_sectors_num(mfc_data->type);\n        mf_classic_get_read_sectors_and_keys(\n            mfc_data,\n            &instance->nfc_dict_context.sectors_read,\n            &instance->nfc_dict_context.keys_found);\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) {\n        MfClassicKey key = {};\n        if(keys_dict_get_next_key(\n               instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {\n            mfc_event->data->key_request_data.key = key;\n            mfc_event->data->key_request_data.key_provided = true;\n            instance->nfc_dict_context.dict_keys_current++;\n            if(instance->nfc_dict_context.dict_keys_current % 10 == 0) {\n                view_dispatcher_send_custom_event(\n                    instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n            }\n        } else {\n            mfc_event->data->key_request_data.key_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeDataUpdate) {\n        MfClassicPollerEventDataUpdate* data_update = &mfc_event->data->data_update;\n        instance->nfc_dict_context.sectors_read = data_update->sectors_read;\n        instance->nfc_dict_context.keys_found = data_update->keys_found;\n        instance->nfc_dict_context.current_sector = data_update->current_sector;\n        instance->nfc_dict_context.nested_phase = data_update->nested_phase;\n        instance->nfc_dict_context.prng_type = data_update->prng_type;\n        instance->nfc_dict_context.backdoor = data_update->backdoor;\n        instance->nfc_dict_context.nested_target_key = data_update->nested_target_key;\n        instance->nfc_dict_context.msb_count = data_update->msb_count;\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {\n        keys_dict_rewind(instance->nfc_dict_context.dict);\n        instance->nfc_dict_context.dict_keys_current = 0;\n        instance->nfc_dict_context.current_sector =\n            mfc_event->data->next_sector_data.current_sector;\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyA) {\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyB) {\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStart) {\n        instance->nfc_dict_context.key_attack_current_sector =\n            mfc_event->data->key_attack_data.current_sector;\n        instance->nfc_dict_context.is_key_attack = true;\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) {\n        keys_dict_rewind(instance->nfc_dict_context.dict);\n        instance->nfc_dict_context.is_key_attack = false;\n        instance->nfc_dict_context.dict_keys_current = 0;\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);\n        nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackComplete);\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nvoid nfc_dict_attack_dict_attack_result_callback(DictAttackEvent event, void* context) {\n    furi_assert(context);\n    NfcApp* instance = context;\n\n    if(event == DictAttackEventSkipPressed) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip);\n    }\n}\n\nstatic void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) {\n    NfcMfClassicDictAttackContext* mfc_dict = &instance->nfc_dict_context;\n\n    if(mfc_dict->is_key_attack) {\n        dict_attack_set_key_attack(instance->dict_attack, mfc_dict->key_attack_current_sector);\n    } else {\n        dict_attack_reset_key_attack(instance->dict_attack);\n        dict_attack_set_sectors_total(instance->dict_attack, mfc_dict->sectors_total);\n        dict_attack_set_sectors_read(instance->dict_attack, mfc_dict->sectors_read);\n        dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found);\n        dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current);\n        dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector);\n        dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase);\n        dict_attack_set_prng_type(instance->dict_attack, mfc_dict->prng_type);\n        dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);\n        dict_attack_set_nested_target_key(instance->dict_attack, mfc_dict->nested_target_key);\n        dict_attack_set_msb_count(instance->dict_attack, mfc_dict->msb_count);\n    }\n}\n\nstatic void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);\n    if(state == DictAttackStateCUIDDictInProgress) {\n        size_t cuid_len = 0;\n        const uint8_t* cuid = nfc_device_get_uid(instance->nfc_device, &cuid_len);\n        FuriString* cuid_dict_path = furi_string_alloc_printf(\n            \"%s/mf_classic_dict_%08lx.nfc\",\n            EXT_PATH(\"nfc/assets\"),\n            (uint32_t)bit_lib_bytes_to_num_be(cuid + (cuid_len - 4), 4));\n\n        do {\n            if(!keys_dict_check_presence(furi_string_get_cstr(cuid_dict_path))) {\n                state = DictAttackStateUserDictInProgress;\n                break;\n            }\n\n            instance->nfc_dict_context.dict = keys_dict_alloc(\n                furi_string_get_cstr(cuid_dict_path),\n                KeysDictModeOpenExisting,\n                sizeof(MfClassicKey));\n\n            if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {\n                keys_dict_free(instance->nfc_dict_context.dict);\n                state = DictAttackStateUserDictInProgress;\n                break;\n            }\n\n            dict_attack_set_header(instance->dict_attack, \"MF Classic CUID Dictionary\");\n        } while(false);\n\n        furi_string_free(cuid_dict_path);\n    }\n    if(state == DictAttackStateUserDictInProgress) {\n        do {\n            instance->nfc_dict_context.enhanced_dict = true;\n\n            if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {\n                storage_common_remove(\n                    instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);\n            }\n            if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH)) {\n                storage_common_copy(\n                    instance->storage,\n                    NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH,\n                    NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);\n            }\n\n            if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {\n                state = DictAttackStateSystemDictInProgress;\n                break;\n            }\n\n            if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {\n                storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);\n            }\n            storage_common_copy(\n                instance->storage,\n                NFC_APP_MF_CLASSIC_DICT_USER_PATH,\n                NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);\n\n            instance->nfc_dict_context.dict = keys_dict_alloc(\n                NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n            if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {\n                keys_dict_free(instance->nfc_dict_context.dict);\n                state = DictAttackStateSystemDictInProgress;\n                break;\n            }\n\n            dict_attack_set_header(instance->dict_attack, \"MF Classic User Dictionary\");\n        } while(false);\n    }\n    if(state == DictAttackStateSystemDictInProgress) {\n        instance->nfc_dict_context.dict = keys_dict_alloc(\n            NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));\n        dict_attack_set_header(instance->dict_attack, \"MF Classic System Dictionary\");\n    }\n\n    instance->nfc_dict_context.dict_keys_total =\n        keys_dict_get_total_keys(instance->nfc_dict_context.dict);\n    dict_attack_set_total_dict_keys(\n        instance->dict_attack, instance->nfc_dict_context.dict_keys_total);\n    instance->nfc_dict_context.dict_keys_current = 0;\n\n    dict_attack_set_callback(\n        instance->dict_attack, nfc_dict_attack_dict_attack_result_callback, instance);\n    nfc_scene_mf_classic_dict_attack_update_view(instance);\n\n    scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack, state);\n}\n\nvoid nfc_scene_mf_classic_dict_attack_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    scene_manager_set_scene_state(\n        instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);\n    nfc_scene_mf_classic_dict_attack_prepare_view(instance);\n    dict_attack_set_card_state(instance->dict_attack, true);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);\n    nfc_blink_read_start(instance);\n    notification_message(instance->notifications, &sequence_display_backlight_enforce_on);\n\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n    nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);\n}\n\nstatic void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) {\n    const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);\n    bool is_card_fully_read = mf_classic_is_card_read(mfc_data);\n    if(is_card_fully_read) {\n        notification_message(instance->notifications, &sequence_success);\n    } else {\n        notification_message(instance->notifications, &sequence_semi_success);\n    }\n}\n\nbool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventDictAttackComplete) {\n            bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=\n                                   MfClassicNestedPhaseNone;\n            if(state == DictAttackStateCUIDDictInProgress) {\n                nfc_poller_stop(instance->poller);\n                nfc_poller_free(instance->poller);\n                keys_dict_free(instance->nfc_dict_context.dict);\n                scene_manager_set_scene_state(\n                    instance->scene_manager,\n                    NfcSceneMfClassicDictAttack,\n                    DictAttackStateUserDictInProgress);\n                nfc_scene_mf_classic_dict_attack_prepare_view(instance);\n                instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n                nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);\n                consumed = true;\n            } else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {\n                nfc_poller_stop(instance->poller);\n                nfc_poller_free(instance->poller);\n                keys_dict_free(instance->nfc_dict_context.dict);\n                scene_manager_set_scene_state(\n                    instance->scene_manager,\n                    NfcSceneMfClassicDictAttack,\n                    DictAttackStateSystemDictInProgress);\n                nfc_scene_mf_classic_dict_attack_prepare_view(instance);\n                instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n                nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);\n                consumed = true;\n            } else {\n                nfc_scene_mf_classic_dict_attack_notify_read(instance);\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n                consumed = true;\n            }\n        } else if(event.event == NfcCustomEventCardDetected) {\n            dict_attack_set_card_state(instance->dict_attack, true);\n            consumed = true;\n        } else if(event.event == NfcCustomEventCardLost) {\n            dict_attack_set_card_state(instance->dict_attack, false);\n            consumed = true;\n        } else if(event.event == NfcCustomEventDictAttackDataUpdate) {\n            nfc_scene_mf_classic_dict_attack_update_view(instance);\n        } else if(event.event == NfcCustomEventDictAttackSkip) {\n            const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);\n            nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);\n            bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=\n                                   MfClassicNestedPhaseNone;\n            if(state == DictAttackStateCUIDDictInProgress) {\n                if(instance->nfc_dict_context.is_card_present) {\n                    nfc_poller_stop(instance->poller);\n                    nfc_poller_free(instance->poller);\n                    keys_dict_free(instance->nfc_dict_context.dict);\n                    scene_manager_set_scene_state(\n                        instance->scene_manager,\n                        NfcSceneMfClassicDictAttack,\n                        DictAttackStateUserDictInProgress);\n                    nfc_scene_mf_classic_dict_attack_prepare_view(instance);\n                    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n                    nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);\n                } else {\n                    nfc_scene_mf_classic_dict_attack_notify_read(instance);\n                    scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                    dolphin_deed(DolphinDeedNfcReadSuccess);\n                }\n                consumed = true;\n            } else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {\n                if(instance->nfc_dict_context.is_card_present) {\n                    nfc_poller_stop(instance->poller);\n                    nfc_poller_free(instance->poller);\n                    keys_dict_free(instance->nfc_dict_context.dict);\n                    scene_manager_set_scene_state(\n                        instance->scene_manager,\n                        NfcSceneMfClassicDictAttack,\n                        DictAttackStateSystemDictInProgress);\n                    nfc_scene_mf_classic_dict_attack_prepare_view(instance);\n                    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n                    nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);\n                } else {\n                    nfc_scene_mf_classic_dict_attack_notify_read(instance);\n                    scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                    dolphin_deed(DolphinDeedNfcReadSuccess);\n                }\n                consumed = true;\n            } else {\n                nfc_scene_mf_classic_dict_attack_notify_read(instance);\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n                consumed = true;\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm);\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_dict_attack_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n\n    dict_attack_reset(instance->dict_attack);\n    scene_manager_set_scene_state(\n        instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);\n\n    keys_dict_free(instance->nfc_dict_context.dict);\n\n    instance->nfc_dict_context.current_sector = 0;\n    instance->nfc_dict_context.sectors_total = 0;\n    instance->nfc_dict_context.sectors_read = 0;\n    instance->nfc_dict_context.keys_found = 0;\n    instance->nfc_dict_context.dict_keys_total = 0;\n    instance->nfc_dict_context.dict_keys_current = 0;\n    instance->nfc_dict_context.is_key_attack = false;\n    instance->nfc_dict_context.key_attack_current_sector = 0;\n    instance->nfc_dict_context.is_card_present = false;\n    instance->nfc_dict_context.nested_phase = MfClassicNestedPhaseNone;\n    instance->nfc_dict_context.prng_type = MfClassicPrngTypeUnknown;\n    instance->nfc_dict_context.backdoor = MfClassicBackdoorUnknown;\n    instance->nfc_dict_context.nested_target_key = 0;\n    instance->nfc_dict_context.msb_count = 0;\n    instance->nfc_dict_context.enhanced_dict = false;\n\n    // Clean up temporary files used for nested dictionary attack\n    if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {\n        storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);\n    }\n    if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {\n        storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);\n    }\n\n    nfc_blink_stop(instance);\n    notification_message(instance->notifications, &sequence_display_backlight_enforce_auto);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_keys_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Load flipper dict keys total\n    uint32_t flipper_dict_keys_total = 0;\n    KeysDict* dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));\n    flipper_dict_keys_total = keys_dict_get_total_keys(dict);\n    keys_dict_free(dict);\n\n    // Load user dict keys total\n    uint32_t user_dict_keys_total = 0;\n    dict = keys_dict_alloc(\n        NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n    user_dict_keys_total = keys_dict_get_total_keys(dict);\n    keys_dict_free(dict);\n\n    FuriString* temp_str = furi_string_alloc();\n    widget_add_string_element(\n        instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, \"MIFARE Classic Keys\");\n    furi_string_printf(temp_str, \"System dict: %lu\", flipper_dict_keys_total);\n    widget_add_string_element(\n        instance->widget,\n        0,\n        20,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        furi_string_get_cstr(temp_str));\n    furi_string_printf(temp_str, \"User dict: %lu\", user_dict_keys_total);\n    widget_add_string_element(\n        instance->widget,\n        0,\n        32,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        furi_string_get_cstr(temp_str));\n    widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36);\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeCenter,\n        \"Add\",\n        nfc_scene_mf_classic_keys_widget_callback,\n        instance);\n    if(user_dict_keys_total > 0) {\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"List\",\n            nfc_scene_mf_classic_keys_widget_callback,\n            instance);\n    }\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeCenter) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysAdd);\n            consumed = true;\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysList);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_keys_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_scene_mf_classic_keys_add_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter the key in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_scene_mf_classic_keys_add_byte_input_callback,\n        NULL,\n        instance,\n        instance->byte_input_store,\n        sizeof(MfClassicKey));\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            // Add key to dict\n            KeysDict* dict = keys_dict_alloc(\n                NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));\n\n            MfClassicKey key = {};\n            memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey));\n            if(keys_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) {\n                scene_manager_next_scene(\n                    instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);\n            } else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);\n                dolphin_deed(DolphinDeedNfcKeyAdd);\n            } else {\n                scene_manager_previous_scene(instance->scene_manager);\n            }\n\n            keys_dict_free(dict);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_keys_add_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_keys_delete_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_keys_delete_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    uint32_t key_index =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicKeysDelete);\n    FuriString* key_str = furi_string_alloc();\n\n    widget_add_string_element(\n        instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"Delete this key?\");\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeLeft,\n        \"Cancel\",\n        nfc_scene_mf_classic_keys_delete_widget_callback,\n        instance);\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeRight,\n        \"Delete\",\n        nfc_scene_mf_classic_keys_delete_widget_callback,\n        instance);\n\n    mf_user_dict_get_key_str(instance->mf_user_dict, key_index, key_str);\n    widget_add_string_element(\n        instance->widget,\n        64,\n        32,\n        AlignCenter,\n        AlignCenter,\n        FontSecondary,\n        furi_string_get_cstr(key_str));\n\n    furi_string_free(key_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            uint32_t key_index = scene_manager_get_scene_state(\n                instance->scene_manager, NfcSceneMfClassicKeysDelete);\n            if(mf_user_dict_delete_key(instance->mf_user_dict, key_index)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess);\n            } else {\n                scene_manager_previous_scene(instance->scene_manager);\n            }\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(instance->scene_manager);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_keys_delete_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    mf_user_dict_free(instance->mf_user_dict);\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100)\n\nvoid nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_scene_mf_classic_keys_list_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    instance->mf_user_dict = mf_user_dict_alloc(NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX);\n\n    submenu_set_header(instance->submenu, \"Select key to delete:\");\n    FuriString* temp_str = furi_string_alloc();\n    for(size_t i = 0; i < mf_user_dict_get_keys_cnt(instance->mf_user_dict); i++) {\n        mf_user_dict_get_key_str(instance->mf_user_dict, i, temp_str);\n        submenu_add_item(\n            instance->submenu,\n            furi_string_get_cstr(temp_str),\n            i,\n            nfc_scene_mf_classic_keys_list_submenu_callback,\n            instance);\n    }\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n\n    bool consumed = false;\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(\n            instance->scene_manager, NfcSceneMfClassicKeysDelete, event.event);\n        scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysDelete);\n    } else if(event.type == SceneManagerEventTypeBack) {\n        mf_user_dict_free(instance->mf_user_dict);\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_keys_list_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    submenu_reset(instance->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    Popup* popup = instance->popup;\n    popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n    popup_set_header(popup, \"Key Already Exists!\", 64, 3, AlignCenter, AlignTop);\n    popup_set_text(\n        popup,\n        \"Please enter a\\n\"\n        \"different key.\",\n        4,\n        24,\n        AlignLeft,\n        AlignTop);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, instance);\n    popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneMfClassicKeysAdd);\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c",
    "content": "#include \"../nfc_app_i.h\"\n#include \"loader/loader.h\"\n\ntypedef enum {\n    NfcSceneMfClassicMfKeyCompleteStateAppMissing,\n    NfcSceneMfClassicMfKeyCompleteStateAppPresent,\n} NfcSceneMfClassicMfKeyCompleteState;\n\nvoid nfc_scene_mf_classic_mfkey_complete_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    widget_add_string_element(\n        instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"Completed!\");\n\n    NfcSceneMfClassicMfKeyCompleteState scene_state =\n        storage_common_exists(instance->storage, NFC_MFKEY32_APP_PATH) ?\n            NfcSceneMfClassicMfKeyCompleteStateAppPresent :\n            NfcSceneMfClassicMfKeyCompleteStateAppMissing;\n    scene_manager_set_scene_state(\n        instance->scene_manager, NfcSceneMfClassicMfkeyComplete, scene_state);\n\n    if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) {\n        widget_add_string_multiline_element(\n            instance->widget,\n            64,\n            13,\n            AlignCenter,\n            AlignTop,\n            FontSecondary,\n            \"Now use Mfkey32 to extract \\nkeys: r.flipper.net/nfc-tools\");\n        widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25);\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"Finish\",\n            nfc_scene_mf_classic_mfkey_complete_callback,\n            instance);\n    } else {\n        widget_add_string_multiline_element(\n            instance->widget,\n            60,\n            16,\n            AlignLeft,\n            AlignTop,\n            FontSecondary,\n            \"Now run Mfkey32\\n to extract \\nkeys\");\n        widget_add_icon_element(instance->widget, 5, 18, &I_WarningDolphin_45x42);\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"Run\",\n            nfc_scene_mf_classic_mfkey_complete_callback,\n            instance);\n    }\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            NfcSceneMfClassicMfKeyCompleteState scene_state = scene_manager_get_scene_state(\n                instance->scene_manager, NfcSceneMfClassicMfkeyComplete);\n            if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    instance->scene_manager, NfcSceneStart);\n            } else {\n                nfc_app_run_external(instance, NFC_MFKEY32_APP_PATH);\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart};\n        consumed = scene_manager_search_and_switch_to_previous_scene_one_of(\n            instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes));\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_mfkey_complete_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_mfkey_nonces_info_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_mfkey_nonces_info_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    FuriString* temp_str = furi_string_alloc();\n\n    size_t mfkey_params_saved = mfkey32_logger_get_params_num(instance->mfkey32_logger);\n    furi_string_printf(temp_str, \"Nonce pairs saved: %zu\\n\", mfkey_params_saved);\n    widget_add_string_element(\n        instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(temp_str));\n    widget_add_string_element(\n        instance->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, \"Authenticated sectors:\");\n\n    mfkey32_logger_get_params_data(instance->mfkey32_logger, temp_str);\n    widget_add_text_scroll_element(\n        instance->widget, 0, 22, 128, 42, furi_string_get_cstr(temp_str));\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeCenter,\n        \"OK\",\n        nfc_scene_mf_classic_mfkey_nonces_info_callback,\n        instance);\n\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeCenter) {\n            if(mfkey32_logger_save_params(\n                   instance->mfkey32_logger, NFC_APP_MFKEY32_LOGS_FILE_PATH)) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyComplete);\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    instance->scene_manager, NfcSceneStart);\n            }\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart};\n        consumed = scene_manager_search_and_switch_to_previous_scene_one_of(\n            instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes));\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_mfkey_nonces_info_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    mfkey32_logger_free(instance->mfkey32_logger);\n\n    // Clear view\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <nfc/protocols/mf_classic/mf_classic_poller.h>\n\nenum {\n    NfcSceneMfClassicUpdateInitialStateCardSearch,\n    NfcSceneMfClassicUpdateInitialStateCardFound,\n};\n\nNfcCommand nfc_mf_classic_update_initial_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcCommand command = NfcCommandContinue;\n    const MfClassicPollerEvent* mfc_event = event.event_data;\n    NfcApp* instance = context;\n\n    if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n    } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        const MfClassicData* updated_data = nfc_poller_get_data(instance->poller);\n        const MfClassicData* old_data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n        if(iso14443_3a_is_equal(updated_data->iso14443_3a_data, old_data->iso14443_3a_data)) {\n            mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;\n        } else {\n            view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);\n            command = NfcCommandStop;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {\n        uint8_t sector_num = 0;\n        MfClassicKey key = {};\n        MfClassicKeyType key_type = MfClassicKeyTypeA;\n        if(mf_classic_key_cache_get_next_key(\n               instance->mfc_key_cache, &sector_num, &key, &key_type)) {\n            mfc_event->data->read_sector_request_data.sector_num = sector_num;\n            mfc_event->data->read_sector_request_data.key = key;\n            mfc_event->data->read_sector_request_data.key_type = key_type;\n            mfc_event->data->read_sector_request_data.key_provided = true;\n        } else {\n            mfc_event->data->read_sector_request_data.key_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        const MfClassicData* updated_data = nfc_poller_get_data(instance->poller);\n        nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, updated_data);\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit);\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nstatic void nfc_scene_mf_classic_update_initial_setup_view(NfcApp* instance) {\n    Popup* popup = instance->popup;\n    popup_reset(popup);\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicUpdateInitial);\n\n    if(state == NfcSceneMfClassicUpdateInitialStateCardSearch) {\n        popup_set_text(\n            instance->popup, \"Use the source\\ncard only\", 128, 32, AlignRight, AlignCenter);\n        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n    } else {\n        popup_set_header(popup, \"Updating\\nDon't move...\", 52, 32, AlignLeft, AlignCenter);\n        popup_set_icon(popup, 12, 23, &A_Loading_24);\n    }\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nvoid nfc_scene_mf_classic_update_initial_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcEmulate);\n\n    const MfClassicData* mfc_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n    mf_classic_key_cache_load_from_data(instance->mfc_key_cache, mfc_data);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfClassicUpdateInitial,\n        NfcSceneMfClassicUpdateInitialStateCardSearch);\n    nfc_scene_mf_classic_update_initial_setup_view(instance);\n\n    // Setup and start worker\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n    nfc_poller_start(instance->poller, nfc_mf_classic_update_initial_worker_callback, instance);\n    nfc_blink_emulate_start(instance);\n}\n\nbool nfc_scene_mf_classic_update_initial_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventCardDetected) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneMfClassicUpdateInitial,\n                NfcSceneMfClassicUpdateInitialStateCardFound);\n            nfc_scene_mf_classic_update_initial_setup_view(instance);\n            consumed = true;\n        } else if(event.event == NfcCustomEventCardLost) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneMfClassicUpdateInitial,\n                NfcSceneMfClassicUpdateInitialStateCardSearch);\n            nfc_scene_mf_classic_update_initial_setup_view(instance);\n            consumed = true;\n        } else if(event.event == NfcCustomEventWrongCard) {\n            scene_manager_next_scene(\n                instance->scene_manager, NfcSceneMfClassicUpdateInitialWrongCard);\n            consumed = true;\n        } else if(event.event == NfcCustomEventWorkerExit) {\n            if(nfc_save_shadow_file(instance)) {\n                scene_manager_next_scene(\n                    instance->scene_manager, NfcSceneMfClassicUpdateInitialSuccess);\n            } else {\n                scene_manager_next_scene(\n                    instance->scene_manager, NfcSceneMfClassicUpdateInitialWrongCard);\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_update_initial_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfClassicUpdateInitial,\n        NfcSceneMfClassicUpdateInitialStateCardSearch);\n    // Clear view\n    popup_reset(instance->popup);\n\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_update_initial_success_popup_callback(void* context) {\n    NfcApp* instance = context;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_mf_classic_update_initial_success_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcSave);\n\n    notification_message(instance->notifications, &sequence_success);\n\n    Popup* popup = instance->popup;\n    popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);\n    popup_set_header(popup, \"Updated\", 11, 20, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, instance);\n    popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_mf_classic_update_initial_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneSavedMenu);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_update_initial_success_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_update_initial_wrong_card_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_update_initial_wrong_card_on_enter(void* context) {\n    NfcApp* instance = context;\n    Widget* widget = instance->widget;\n\n    notification_message(instance->notifications, &sequence_error);\n\n    widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, \"Wrong Card!\");\n    widget_add_string_multiline_element(\n        widget,\n        4,\n        17,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Data management\\nis only possible\\nwith source card\");\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Retry\",\n        nfc_scene_mf_classic_update_initial_wrong_card_widget_callback,\n        instance);\n\n    // Setup and start worker\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_update_initial_wrong_card_on_event(\n    void* context,\n    SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = scene_manager_previous_scene(instance->scene_manager);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_update_initial_wrong_card_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <nfc/protocols/mf_classic/mf_classic_poller.h>\n\nenum {\n    NfcSceneMfClassicWriteInitialStateCardSearch,\n    NfcSceneMfClassicWriteInitialStateCardFound,\n};\n\nNfcCommand\n    nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcCommand command = NfcCommandContinue;\n    NfcApp* instance = context;\n    MfClassicPollerEvent* mfc_event = event.event_data;\n    const MfClassicData* write_data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);\n\n    if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n    } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        const MfClassicData* tag_data = nfc_poller_get_data(instance->poller);\n        if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) {\n            mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite;\n        } else {\n            view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);\n            command = NfcCommandStop;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) {\n        uint8_t sector = mfc_event->data->sec_tr_data.sector_num;\n        uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector);\n        if(mf_classic_is_block_read(write_data, sec_tr)) {\n            mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr];\n            mfc_event->data->sec_tr_data.sector_trailer_provided = true;\n        } else {\n            mfc_event->data->sec_tr_data.sector_trailer_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) {\n        uint8_t block_num = mfc_event->data->write_block_data.block_num;\n        if(mf_classic_is_block_read(write_data, block_num)) {\n            mfc_event->data->write_block_data.write_block = write_data->block[block_num];\n            mfc_event->data->write_block_data.write_block_provided = true;\n        } else {\n            mfc_event->data->write_block_data.write_block_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    } else if(mfc_event->type == MfClassicPollerEventTypeFail) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);\n        command = NfcCommandStop;\n    }\n    return command;\n}\n\nstatic void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) {\n    Popup* popup = instance->popup;\n    popup_reset(popup);\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial);\n\n    if(state == NfcSceneMfClassicWriteInitialStateCardSearch) {\n        popup_set_header(instance->popup, \"Writing\", 95, 20, AlignCenter, AlignCenter);\n        popup_set_text(\n            instance->popup, \"Use the source\\ncard only\", 95, 38, AlignCenter, AlignCenter);\n        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n    } else {\n        popup_set_header(popup, \"Writing\\nDon't move...\", 52, 32, AlignLeft, AlignCenter);\n        popup_set_icon(popup, 12, 23, &A_Loading_24);\n    }\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nvoid nfc_scene_mf_classic_write_initial_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcEmulate);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfClassicWriteInitial,\n        NfcSceneMfClassicWriteInitialStateCardSearch);\n    nfc_scene_mf_classic_write_initial_setup_view(instance);\n\n    // Setup and start worker\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);\n    nfc_poller_start(\n        instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance);\n\n    nfc_blink_emulate_start(instance);\n}\n\nbool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventCardDetected) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneMfClassicWriteInitial,\n                NfcSceneMfClassicWriteInitialStateCardFound);\n            nfc_scene_mf_classic_write_initial_setup_view(instance);\n            consumed = true;\n        } else if(event.event == NfcCustomEventCardLost) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneMfClassicWriteInitial,\n                NfcSceneMfClassicWriteInitialStateCardSearch);\n            nfc_scene_mf_classic_write_initial_setup_view(instance);\n            consumed = true;\n        } else if(event.event == NfcCustomEventWrongCard) {\n            scene_manager_next_scene(\n                instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard);\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerSuccess) {\n            scene_manager_next_scene(\n                instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess);\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerFailure) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_write_initial_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfClassicWriteInitial,\n        NfcSceneMfClassicWriteInitialStateCardSearch);\n    // Clear view\n    popup_reset(instance->popup);\n\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_write_initial_fail_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) {\n    NfcApp* instance = context;\n    Widget* widget = instance->widget;\n\n    notification_message(instance->notifications, &sequence_error);\n\n    widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n    widget_add_string_element(\n        widget, 7, 4, AlignLeft, AlignTop, FontPrimary, \"Writing gone wrong!\");\n    widget_add_string_multiline_element(\n        widget,\n        7,\n        17,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Not all sectors\\nwere written\\ncorrectly.\");\n\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Finish\",\n        nfc_scene_mf_classic_write_initial_fail_widget_callback,\n        instance);\n\n    // Setup and start worker\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneSavedMenu);\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = scene_manager_search_and_switch_to_previous_scene(\n            instance->scene_manager, NfcSceneSavedMenu);\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) {\n    NfcApp* instance = context;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_mf_classic_write_initial_success_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcSave);\n\n    notification_message(instance->notifications, &sequence_success);\n\n    Popup* popup = instance->popup;\n    popup_set_header(popup, \"Success!\", 75, 10, AlignLeft, AlignTop);\n    popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, instance);\n    popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneSavedMenu);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_write_initial_success_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_classic_write_initial_wrong_card_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_classic_write_initial_wrong_card_on_enter(void* context) {\n    NfcApp* instance = context;\n    Widget* widget = instance->widget;\n\n    notification_message(instance->notifications, &sequence_error);\n\n    widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n    widget_add_string_element(\n        widget, 3, 4, AlignLeft, AlignTop, FontPrimary, \"Use The Source Card!\");\n    widget_add_string_multiline_element(\n        widget,\n        4,\n        17,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Go to NFC Magic\\napp if you want to\\nwrite blanks\");\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Retry\",\n        nfc_scene_mf_classic_write_initial_wrong_card_widget_callback,\n        instance);\n\n    // Setup and start worker\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_classic_write_initial_wrong_card_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = scene_manager_previous_scene(instance->scene_manager);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_classic_write_initial_wrong_card_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/mf_desfire/mf_desfire_render.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexAppInfo,\n    SubmenuIndexDynamic, // dynamic indexes start here\n};\n\nstatic void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);\n}\n\nvoid nfc_scene_mf_desfire_app_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    text_box_set_font(nfc->text_box, TextBoxFontHex);\n    submenu_add_item(\n        nfc->submenu,\n        \"App info\",\n        SubmenuIndexAppInfo,\n        nfc_scene_mf_desfire_app_submenu_callback,\n        nfc);\n\n    const uint32_t app_idx =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1;\n\n    const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire);\n    const MfDesfireApplication* app = simple_array_cget(data->applications, app_idx);\n\n    FuriString* label = furi_string_alloc();\n\n    for(uint32_t i = 0; i < simple_array_get_count(app->file_ids); ++i) {\n        const MfDesfireFileId file_id =\n            *(const MfDesfireFileId*)simple_array_cget(app->file_ids, i);\n        furi_string_printf(label, \"File %d\", file_id);\n        submenu_add_item(\n            nfc->submenu,\n            furi_string_get_cstr(label),\n            i + SubmenuIndexDynamic,\n            nfc_scene_mf_desfire_app_submenu_callback,\n            nfc);\n    }\n\n    furi_string_free(label);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        } else {\n            const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire);\n\n            const uint32_t app_index =\n                scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1;\n            const MfDesfireApplication* app = simple_array_cget(data->applications, app_index);\n\n            TextBox* text_box = nfc->text_box;\n            furi_string_reset(nfc->text_box_store);\n            if(event.event == SubmenuIndexAppInfo) {\n                const MfDesfireApplicationId* app_id =\n                    simple_array_cget(data->application_ids, app_index);\n                nfc_render_mf_desfire_application_id(app_id, nfc->text_box_store);\n                nfc_render_mf_desfire_application(app, nfc->text_box_store);\n            } else {\n                const uint32_t file_index = event.event - SubmenuIndexDynamic;\n                const MfDesfireFileId* file_id = simple_array_cget(app->file_ids, file_index);\n                const MfDesfireFileSettings* file_settings =\n                    simple_array_cget(app->file_settings, file_index);\n                const MfDesfireFileData* file_data = simple_array_cget(app->file_data, file_index);\n                nfc_render_mf_desfire_file_id(file_id, nfc->text_box_store);\n                nfc_render_mf_desfire_file_settings_data(\n                    file_settings, file_data, nfc->text_box_store);\n            }\n            text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store));\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1);\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);\n            consumed = true;\n        }\n\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(state & 1) {\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_desfire_app_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear views\n    text_box_reset(nfc->text_box);\n    furi_string_reset(nfc->text_box_store);\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n#include \"../helpers/protocol_support/mf_desfire/mf_desfire_render.h\"\n\nenum {\n    MifareDesfireMoreInfoStateMenu,\n    MifareDesfireMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index\n};\n\nenum SubmenuIndex {\n    SubmenuIndexCardInfo,\n    SubmenuIndexDynamic, // dynamic indices start here\n};\n\nvoid nfc_scene_mf_desfire_more_info_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n\n    const uint32_t state =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo);\n    const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire);\n\n    text_box_set_font(nfc->text_box, TextBoxFontHex);\n\n    submenu_add_item(\n        submenu,\n        \"Card info\",\n        SubmenuIndexCardInfo,\n        nfc_protocol_support_common_submenu_callback,\n        nfc);\n\n    FuriString* label = furi_string_alloc();\n\n    for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {\n        const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);\n        furi_string_printf(\n            label, \"App %02x%02x%02x\", app_id->data[2], app_id->data[1], app_id->data[0]);\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(label),\n            i + SubmenuIndexDynamic,\n            nfc_protocol_support_common_submenu_callback,\n            nfc);\n    }\n\n    furi_string_free(label);\n\n    if(state >= MifareDesfireMoreInfoStateItem) {\n        submenu_set_selected_item(\n            nfc->submenu, state - MifareDesfireMoreInfoStateItem + SubmenuIndexDynamic);\n        scene_manager_set_scene_state(\n            nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu);\n    }\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_mf_desfire_more_info_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    const uint32_t state =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo);\n    const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        TextBox* text_box = nfc->text_box;\n        furi_string_reset(nfc->text_box_store);\n\n        if(event.event == SubmenuIndexCardInfo) {\n            nfc_render_mf_desfire_data(data, nfc->text_box_store);\n            text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store));\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);\n            scene_manager_set_scene_state(\n                nfc->scene_manager,\n                NfcSceneMfDesfireMoreInfo,\n                MifareDesfireMoreInfoStateItem + SubmenuIndexCardInfo);\n            consumed = true;\n        } else {\n            const uint32_t index = event.event - SubmenuIndexDynamic;\n            scene_manager_set_scene_state(\n                nfc->scene_manager,\n                NfcSceneMfDesfireMoreInfo,\n                MifareDesfireMoreInfoStateItem + index);\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp);\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(state >= MifareDesfireMoreInfoStateItem) {\n            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu);\n        } else {\n            // Return directly to the Info scene\n            scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_desfire_more_info_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear views\n    text_box_reset(nfc->text_box);\n    furi_string_reset(nfc->text_box_store);\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_dict_attack.c",
    "content": "#include \"../nfc_app_i.h\"\n#include <dolphin/dolphin.h>\n\n#define TAG \"NfcMfUlCDictAttack\"\n\n// TODO: Support card_detected properly -nofl\n\nenum {\n    DictAttackStateUserDictInProgress,\n    DictAttackStateSystemDictInProgress,\n};\n\nNfcCommand nfc_mf_ultralight_c_dict_attack_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfUltralight);\n    NfcCommand command = NfcCommandContinue;\n    NfcApp* instance = context;\n    MfUltralightPollerEvent* poller_event = event.event_data;\n\n    if(poller_event->type == MfUltralightPollerEventTypeRequestMode) {\n        poller_event->data->poller_mode = MfUltralightPollerModeDictAttack;\n        command = NfcCommandContinue;\n    } else if(poller_event->type == MfUltralightPollerEventTypeRequestKey) {\n        MfUltralightC3DesAuthKey key = {};\n        if(keys_dict_get_next_key(\n               instance->mf_ultralight_c_dict_context.dict,\n               key.data,\n               sizeof(MfUltralightC3DesAuthKey))) {\n            poller_event->data->key_request_data.key = key;\n            poller_event->data->key_request_data.key_provided = true;\n            instance->mf_ultralight_c_dict_context.dict_keys_current++;\n\n            if(instance->mf_ultralight_c_dict_context.dict_keys_current % 10 == 0) {\n                view_dispatcher_send_custom_event(\n                    instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);\n            }\n        } else {\n            poller_event->data->key_request_data.key_provided = false;\n        }\n    } else if(poller_event->type == MfUltralightPollerEventTypeReadSuccess) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));\n        // Check if this is a successful authentication by looking at the poller's auth context\n        const MfUltralightData* data = nfc_poller_get_data(instance->poller);\n\n        // Update page information\n        dict_attack_set_pages_read(instance->dict_attack, data->pages_read);\n        dict_attack_set_pages_total(instance->dict_attack, data->pages_total);\n\n        if(data->pages_read == data->pages_total) {\n            // Full read indicates successful authentication in dict attack mode\n            instance->mf_ultralight_c_dict_context.auth_success = true;\n            dict_attack_set_key_found(instance->dict_attack, true);\n        }\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, NfcCustomEventDictAttackComplete);\n        command = NfcCommandStop;\n    }\n    return command;\n}\n\nvoid nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback(\n    DictAttackEvent event,\n    void* context) {\n    furi_assert(context);\n    NfcApp* instance = context;\n    if(event == DictAttackEventSkipPressed) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip);\n    }\n}\n\nvoid nfc_scene_mf_ultralight_c_dict_attack_prepare_view(NfcApp* instance) {\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack);\n\n    // Set attack type to Ultralight C\n    dict_attack_set_type(instance->dict_attack, DictAttackTypeMfUltralightC);\n\n    if(state == DictAttackStateUserDictInProgress) {\n        do {\n            if(!keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {\n                state = DictAttackStateSystemDictInProgress;\n                break;\n            }\n            instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(\n                NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n                KeysDictModeOpenAlways,\n                sizeof(MfUltralightC3DesAuthKey));\n            if(keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict) == 0) {\n                keys_dict_free(instance->mf_ultralight_c_dict_context.dict);\n                state = DictAttackStateSystemDictInProgress;\n                break;\n            }\n            dict_attack_set_header(instance->dict_attack, \"MFUL C User Dictionary\");\n        } while(false);\n    }\n    if(state == DictAttackStateSystemDictInProgress) {\n        instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(\n            NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,\n            KeysDictModeOpenExisting,\n            sizeof(MfUltralightC3DesAuthKey));\n        dict_attack_set_header(instance->dict_attack, \"MFUL C System Dictionary\");\n    }\n\n    instance->mf_ultralight_c_dict_context.dict_keys_total =\n        keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict);\n    dict_attack_set_total_dict_keys(\n        instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_total);\n    instance->mf_ultralight_c_dict_context.dict_keys_current = 0;\n    dict_attack_set_current_dict_key(\n        instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);\n\n    // Set initial Ultralight C specific values\n    dict_attack_set_key_found(instance->dict_attack, false);\n    dict_attack_set_pages_total(instance->dict_attack, 48); // Ultralight C page count\n    dict_attack_set_pages_read(instance->dict_attack, 0);\n\n    dict_attack_set_callback(\n        instance->dict_attack,\n        nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback,\n        instance);\n    scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack, state);\n}\n\nvoid nfc_scene_mf_ultralight_c_dict_attack_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfUltralightCDictAttack,\n        DictAttackStateUserDictInProgress);\n    nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);\n\n    // Setup and start worker\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);\n    nfc_poller_start(instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance);\n\n    dict_attack_set_card_state(instance->dict_attack, true);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);\n    nfc_blink_read_start(instance);\n}\n\nvoid nfc_scene_mf_ul_c_dict_attack_update_view(NfcApp* instance) {\n    dict_attack_set_card_state(\n        instance->dict_attack, instance->mf_ultralight_c_dict_context.is_card_present);\n    dict_attack_set_current_dict_key(\n        instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);\n}\n\nbool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventDictAttackComplete) {\n            if(state == DictAttackStateUserDictInProgress) {\n                if(instance->mf_ultralight_c_dict_context.auth_success) {\n                    notification_message(instance->notifications, &sequence_success);\n                    scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                    dolphin_deed(DolphinDeedNfcReadSuccess);\n                    consumed = true;\n                } else {\n                    nfc_poller_stop(instance->poller);\n                    nfc_poller_free(instance->poller);\n                    keys_dict_free(instance->mf_ultralight_c_dict_context.dict);\n                    scene_manager_set_scene_state(\n                        instance->scene_manager,\n                        NfcSceneMfUltralightCDictAttack,\n                        DictAttackStateSystemDictInProgress);\n                    nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);\n                    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);\n                    nfc_poller_start(\n                        instance->poller,\n                        nfc_mf_ultralight_c_dict_attack_worker_callback,\n                        instance);\n                    consumed = true;\n                }\n            } else {\n                // Could check if card is fully read here like MFC dict attack, but found key means fully read\n                if(instance->mf_ultralight_c_dict_context.auth_success) {\n                    notification_message(instance->notifications, &sequence_success);\n                } else {\n                    notification_message(instance->notifications, &sequence_semi_success);\n                }\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n                consumed = true;\n            }\n        } else if(event.event == NfcCustomEventDictAttackDataUpdate) {\n            dict_attack_set_current_dict_key(\n                instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);\n            consumed = true;\n        } else if(event.event == NfcCustomEventDictAttackSkip) {\n            if(state == DictAttackStateUserDictInProgress) {\n                nfc_poller_stop(instance->poller);\n                nfc_poller_free(instance->poller);\n                keys_dict_free(instance->mf_ultralight_c_dict_context.dict);\n                scene_manager_set_scene_state(\n                    instance->scene_manager,\n                    NfcSceneMfUltralightCDictAttack,\n                    DictAttackStateSystemDictInProgress);\n                nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);\n                instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);\n                nfc_poller_start(\n                    instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance);\n            } else {\n                notification_message(instance->notifications, &sequence_semi_success);\n                scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);\n                dolphin_deed(DolphinDeedNfcReadSuccess);\n            }\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm);\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_dict_attack_on_exit(void* context) {\n    NfcApp* instance = context;\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfUltralightCDictAttack,\n        DictAttackStateUserDictInProgress);\n    keys_dict_free(instance->mf_ultralight_c_dict_context.dict);\n    instance->mf_ultralight_c_dict_context.dict_keys_total = 0;\n    instance->mf_ultralight_c_dict_context.dict_keys_current = 0;\n    instance->mf_ultralight_c_dict_context.auth_success = false;\n    instance->mf_ultralight_c_dict_context.is_card_present = false;\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_c_keys_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Load flipper dict keys total\n    uint32_t flipper_dict_keys_total = 0;\n    KeysDict* dict = keys_dict_alloc(\n        NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,\n        KeysDictModeOpenExisting,\n        sizeof(MfUltralightC3DesAuthKey));\n    flipper_dict_keys_total = keys_dict_get_total_keys(dict);\n    keys_dict_free(dict);\n\n    // Load user dict keys total\n    uint32_t user_dict_keys_total = 0;\n    dict = keys_dict_alloc(\n        NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n        KeysDictModeOpenAlways,\n        sizeof(MfUltralightC3DesAuthKey));\n    user_dict_keys_total = keys_dict_get_total_keys(dict);\n    keys_dict_free(dict);\n\n    FuriString* temp_str = furi_string_alloc();\n    widget_add_string_element(\n        instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, \"MIFARE Ultralight C Keys\");\n    furi_string_printf(temp_str, \"System dict: %lu\", flipper_dict_keys_total);\n    widget_add_string_element(\n        instance->widget,\n        0,\n        20,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        furi_string_get_cstr(temp_str));\n    furi_string_printf(temp_str, \"User dict: %lu\", user_dict_keys_total);\n    widget_add_string_element(\n        instance->widget,\n        0,\n        32,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        furi_string_get_cstr(temp_str));\n    widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36);\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeCenter,\n        \"Add\",\n        nfc_scene_mf_ultralight_c_keys_widget_callback,\n        instance);\n    if(user_dict_keys_total > 0) {\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"List\",\n            nfc_scene_mf_ultralight_c_keys_widget_callback,\n            instance);\n    }\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_ultralight_c_keys_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeCenter) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysAdd);\n            consumed = true;\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysList);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_add.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_c_keys_add_byte_input_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_add_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter the key in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_scene_mf_ultralight_c_keys_add_byte_input_callback,\n        NULL,\n        instance,\n        instance->byte_input_store,\n        sizeof(MfUltralightC3DesAuthKey));\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_mf_ultralight_c_keys_add_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            // Add key to dict\n            KeysDict* dict = keys_dict_alloc(\n                NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n                KeysDictModeOpenAlways,\n                sizeof(MfUltralightC3DesAuthKey));\n\n            MfUltralightC3DesAuthKey key = {};\n            memcpy(key.data, instance->byte_input_store, sizeof(MfUltralightC3DesAuthKey));\n            if(keys_dict_is_key_present(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) {\n                scene_manager_next_scene(\n                    instance->scene_manager, NfcSceneMfUltralightCKeysWarnDuplicate);\n            } else if(keys_dict_add_key(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);\n                dolphin_deed(DolphinDeedNfcKeyAdd);\n            } else {\n                scene_manager_previous_scene(instance->scene_manager);\n            }\n\n            keys_dict_free(dict);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_add_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_delete.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_c_keys_delete_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_delete_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    uint32_t key_index =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCKeysDelete);\n    FuriString* key_str = furi_string_alloc();\n\n    widget_add_string_element(\n        instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, \"Delete this key?\");\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeLeft,\n        \"Cancel\",\n        nfc_scene_mf_ultralight_c_keys_delete_widget_callback,\n        instance);\n    widget_add_button_element(\n        instance->widget,\n        GuiButtonTypeRight,\n        \"Delete\",\n        nfc_scene_mf_ultralight_c_keys_delete_widget_callback,\n        instance);\n\n    KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(\n        NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n        KeysDictModeOpenAlways,\n        sizeof(MfUltralightC3DesAuthKey));\n    size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);\n    furi_assert(key_index < dict_keys_num);\n    MfUltralightC3DesAuthKey stack_key;\n    for(size_t i = 0; i < (key_index + 1); i++) {\n        bool key_loaded = keys_dict_get_next_key(\n            mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));\n        furi_assert(key_loaded);\n    }\n    furi_string_reset(key_str);\n    for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) {\n        furi_string_cat_printf(key_str, \"%02X\", stack_key.data[i]);\n    }\n\n    widget_add_string_element(\n        instance->widget,\n        64,\n        32,\n        AlignCenter,\n        AlignCenter,\n        FontSecondary,\n        furi_string_get_cstr(key_str));\n\n    keys_dict_free(mf_ultralight_c_user_dict);\n    furi_string_free(key_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_ultralight_c_keys_delete_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            uint32_t key_index = scene_manager_get_scene_state(\n                instance->scene_manager, NfcSceneMfUltralightCKeysDelete);\n            KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(\n                NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n                KeysDictModeOpenAlways,\n                sizeof(MfUltralightC3DesAuthKey));\n            size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);\n            furi_assert(key_index < dict_keys_num);\n            MfUltralightC3DesAuthKey stack_key;\n            for(size_t i = 0; i < (key_index + 1); i++) {\n                bool key_loaded = keys_dict_get_next_key(\n                    mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));\n                furi_assert(key_loaded);\n            }\n            bool key_delete_success = keys_dict_delete_key(\n                mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));\n            keys_dict_free(mf_ultralight_c_user_dict);\n            if(key_delete_success) {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess);\n            } else {\n                scene_manager_previous_scene(instance->scene_manager);\n            }\n        } else if(event.event == GuiButtonTypeLeft) {\n            scene_manager_previous_scene(instance->scene_manager);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_delete_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_list.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#define NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX (100)\n\nvoid nfc_scene_mf_ultralight_c_keys_list_submenu_callback(void* context, uint32_t index) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_list_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(\n        NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,\n        KeysDictModeOpenAlways,\n        sizeof(MfUltralightC3DesAuthKey));\n\n    submenu_set_header(instance->submenu, \"Select key to delete:\");\n    FuriString* temp_str = furi_string_alloc();\n\n    size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);\n    size_t keys_num = MIN((size_t)NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX, dict_keys_num);\n    MfUltralightC3DesAuthKey stack_key;\n\n    if(keys_num > 0) {\n        for(size_t i = 0; i < keys_num; i++) {\n            bool key_loaded = keys_dict_get_next_key(\n                mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));\n            furi_assert(key_loaded);\n            furi_string_reset(temp_str);\n            for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) {\n                furi_string_cat_printf(temp_str, \"%02X\", stack_key.data[i]);\n            }\n            submenu_add_item(\n                instance->submenu,\n                furi_string_get_cstr(temp_str),\n                i,\n                nfc_scene_mf_ultralight_c_keys_list_submenu_callback,\n                instance);\n        }\n    }\n    keys_dict_free(mf_ultralight_c_user_dict);\n    furi_string_free(temp_str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_mf_ultralight_c_keys_list_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n\n    bool consumed = false;\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_set_scene_state(\n            instance->scene_manager, NfcSceneMfUltralightCKeysDelete, event.event);\n        scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysDelete);\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_list_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    submenu_reset(instance->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_warn_duplicate.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback(void* context) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    Popup* popup = instance->popup;\n    popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n    popup_set_header(popup, \"Key Already Exists!\", 64, 3, AlignCenter, AlignTop);\n    popup_set_text(\n        popup,\n        \"Please enter a\\n\"\n        \"different key.\",\n        4,\n        24,\n        AlignLeft,\n        AlignTop);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, instance);\n    popup_set_callback(popup, nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneMfUltralightCKeysAdd);\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c",
    "content": "#include \"../nfc_app_i.h\"\n\nNfcCommand\n    nfc_scene_mf_ultralight_capture_pass_worker_callback(NfcGenericEvent event, void* context) {\n    NfcApp* instance = context;\n    MfUltralightListenerEvent* mfu_event = event.event_data;\n    MfUltralightAuth* mauth = instance->mf_ul_auth;\n\n    if(mfu_event->type == MfUltralightListenerEventTypeAuth) {\n        mauth->password = mfu_event->data->password;\n        view_dispatcher_send_custom_event(\n            instance->view_dispatcher, MfUltralightListenerEventTypeAuth);\n    }\n\n    return NfcCommandContinue;\n}\n\nvoid nfc_scene_mf_ultralight_capture_pass_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    widget_add_string_multiline_element(\n        instance->widget,\n        54,\n        30,\n        AlignLeft,\n        AlignCenter,\n        FontPrimary,\n        \"Touch the\\nreader to get\\npassword...\");\n    widget_add_icon_element(instance->widget, 0, 15, &I_Modern_reader_18x34);\n    widget_add_icon_element(instance->widget, 20, 12, &I_Move_flipper_26x39);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n\n    // Start worker\n    const MfUltralightData* data =\n        nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n    instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, data);\n    nfc_listener_start(\n        instance->listener, nfc_scene_mf_ultralight_capture_pass_worker_callback, instance);\n\n    nfc_blink_read_start(instance);\n}\n\nbool nfc_scene_mf_ultralight_capture_pass_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == MfUltralightListenerEventTypeAuth) {\n            notification_message(instance->notifications, &sequence_success);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_capture_pass_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    nfc_listener_stop(instance->listener);\n    nfc_listener_free(instance->listener);\n    widget_reset(instance->widget);\n\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_scene_mf_ultralight_key_input_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup view\n    ByteInput* byte_input = nfc->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter the password in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_scene_mf_ultralight_key_input_byte_input_callback,\n        NULL,\n        nfc,\n        nfc->mf_ul_auth->password.data,\n        4);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_key_input_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear view\n    byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(nfc->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c",
    "content": "#include \"../nfc_app_i.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexMfUlUnlockMenuReader,\n    SubmenuIndexMfUlUnlockMenuAmeebo,\n    SubmenuIndexMfUlUnlockMenuXiaomi,\n    SubmenuIndexMfUlUnlockMenuManual,\n};\n\nvoid nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);\n}\n\nvoid nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n\n    uint32_t state =\n        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);\n    if(nfc_device_get_protocol(nfc->nfc_device) == NfcProtocolMfUltralight) {\n        const MfUltralightData* mfu_data =\n            nfc_device_get_data(nfc->nfc_device, NfcProtocolMfUltralight);\n        // Hide for MFU-C since it uses 3DES\n        if(mfu_data->type != MfUltralightTypeMfulC) {\n            submenu_add_item(\n                submenu,\n                \"Unlock With Reader\",\n                SubmenuIndexMfUlUnlockMenuReader,\n                nfc_scene_mf_ultralight_unlock_menu_submenu_callback,\n                nfc);\n        }\n    }\n    submenu_add_item(\n        submenu,\n        \"Auth As Ameebo\",\n        SubmenuIndexMfUlUnlockMenuAmeebo,\n        nfc_scene_mf_ultralight_unlock_menu_submenu_callback,\n        nfc);\n    submenu_add_item(\n        submenu,\n        \"Auth As Xiaomi Air Purifier\",\n        SubmenuIndexMfUlUnlockMenuXiaomi,\n        nfc_scene_mf_ultralight_unlock_menu_submenu_callback,\n        nfc);\n    submenu_add_item(\n        submenu,\n        \"Enter Password Manually\",\n        SubmenuIndexMfUlUnlockMenuManual,\n        nfc_scene_mf_ultralight_unlock_menu_submenu_callback,\n        nfc);\n    submenu_set_selected_item(submenu, state);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexMfUlUnlockMenuManual) {\n            nfc->mf_ul_auth->type = MfUltralightAuthTypeManual;\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput);\n            consumed = true;\n        } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) {\n            nfc->mf_ul_auth->type = MfUltralightAuthTypeAmiibo;\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);\n            consumed = true;\n        } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) {\n            nfc->mf_ul_auth->type = MfUltralightAuthTypeXiaomi;\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);\n            consumed = true;\n        } else if(event.event == SubmenuIndexMfUlUnlockMenuReader) {\n            nfc->mf_ul_auth->type = MfUltralightAuthTypeReader;\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightCapturePass);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(\n            nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event);\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c",
    "content": "#include \"../nfc_app_i.h\"\n#include <dolphin/dolphin.h>\n\nvoid nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {\n    NfcApp* nfc = context;\n    DialogEx* dialog_ex = nfc->dialog_ex;\n\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback);\n\n    MfUltralightAuthType type = nfc->mf_ul_auth->type;\n    if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) {\n        // Build dialog text\n        FuriString* password_str =\n            furi_string_alloc_set_str(\"Try to unlock the card with\\npassword: \");\n        for(size_t i = 0; i < sizeof(nfc->mf_ul_auth->password.data); i++) {\n            furi_string_cat_printf(password_str, \"%02X \", nfc->mf_ul_auth->password.data[i]);\n        }\n        furi_string_cat_str(password_str, \"\\nWarning: incorrect password\\nwill block the card!\");\n        nfc_text_store_set(nfc, furi_string_get_cstr(password_str));\n        furi_string_free(password_str);\n\n        const char* message = (type == MfUltralightAuthTypeReader) ? \"Password Captured!\" :\n                                                                     \"Risky Action!\";\n        dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);\n        dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 10, AlignCenter, AlignTop);\n        dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n        dialog_ex_set_right_button_text(dialog_ex, \"Continue\");\n\n        if(type == MfUltralightAuthTypeReader) {\n            notification_message(nfc->notifications, &sequence_set_green_255);\n        }\n    } else {\n        dialog_ex_set_header(dialog_ex, \"Risky action!\", 64, 4, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex, \"Wrong password\\ncan block your\\ncard.\", 4, 18, AlignLeft, AlignTop);\n        dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n        dialog_ex_set_center_button_text(dialog_ex, \"OK\");\n    }\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n\n    bool consumed = false;\n\n    MfUltralightAuthType type = nfc->mf_ul_auth->type;\n    if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) {\n        if(event.type == SceneManagerEventTypeCustom) {\n            if(event.event == DialogExResultRight) {\n                const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight};\n                nfc_detected_protocols_set(\n                    nfc->detected_protocols, mfu_protocol, COUNT_OF(mfu_protocol));\n                scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);\n                dolphin_deed(DolphinDeedNfcRead);\n                consumed = true;\n            } else if(event.event == DialogExResultLeft) {\n                if(type == MfUltralightAuthTypeReader) {\n                    consumed = scene_manager_search_and_switch_to_previous_scene(\n                        nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);\n                } else {\n                    consumed = scene_manager_previous_scene(nfc->scene_manager);\n                }\n            }\n        } else if(event.type == SceneManagerEventTypeBack) {\n            // Cannot press back\n            consumed = true;\n        }\n    } else {\n        if(event.type == SceneManagerEventTypeCustom) {\n            if(event.event == DialogExResultCenter) {\n                const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight};\n                nfc_detected_protocols_set(\n                    nfc->detected_protocols, mfu_protocol, COUNT_OF(mfu_protocol));\n                scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);\n                dolphin_deed(DolphinDeedNfcRead);\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    dialog_ex_reset(nfc->dialog_ex);\n    nfc_text_store_clear(nfc);\n\n    notification_message_block(nfc->notifications, &sequence_reset_green);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>\n\nenum {\n    NfcSceneMfUltralightWriteStateCardSearch,\n    NfcSceneMfUltralightWriteStateCardFound,\n};\n\nNfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfUltralight);\n\n    NfcCommand command = NfcCommandContinue;\n    NfcApp* instance = context;\n    MfUltralightPollerEvent* mfu_event = event.event_data;\n\n    if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) {\n        mfu_event->data->poller_mode = MfUltralightPollerModeWrite;\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);\n    } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {\n        mfu_event->data->auth_context.skip_auth = true;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) {\n        mfu_event->data->write_data =\n            nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);\n    } else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);\n        command = NfcCommandStop;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);\n        command = NfcCommandStop;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) {\n        command = NfcCommandStop;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    }\n    return command;\n}\n\nstatic void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) {\n    Popup* popup = instance->popup;\n    popup_reset(popup);\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite);\n\n    if(state == NfcSceneMfUltralightWriteStateCardSearch) {\n        popup_set_header(instance->popup, \"Writing\", 95, 20, AlignCenter, AlignCenter);\n        popup_set_text(\n            instance->popup, \"Apply the initial\\ncard only\", 95, 38, AlignCenter, AlignCenter);\n        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n    } else {\n        popup_set_header(popup, \"Writing\\nDon't move...\", 52, 32, AlignLeft, AlignCenter);\n        popup_set_icon(popup, 12, 23, &A_Loading_24);\n    }\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nvoid nfc_scene_mf_ultralight_write_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcEmulate);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfUltralightWrite,\n        NfcSceneMfUltralightWriteStateCardSearch);\n    nfc_scene_mf_ultralight_write_setup_view(instance);\n\n    // Setup and start worker\n    FURI_LOG_D(\"WMFU\", \"Card searching...\");\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);\n    nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance);\n\n    nfc_blink_emulate_start(instance);\n}\n\nbool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventCardDetected) {\n            scene_manager_set_scene_state(\n                instance->scene_manager,\n                NfcSceneMfUltralightWrite,\n                NfcSceneMfUltralightWriteStateCardFound);\n            nfc_scene_mf_ultralight_write_setup_view(instance);\n            consumed = true;\n        } else if(event.event == NfcCustomEventWrongCard) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard);\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerSuccess) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess);\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerFailure) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_write_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n\n    scene_manager_set_scene_state(\n        instance->scene_manager,\n        NfcSceneMfUltralightWrite,\n        NfcSceneMfUltralightWriteStateCardSearch);\n    // Clear view\n    popup_reset(instance->popup);\n\n    nfc_blink_stop(instance);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_write_fail_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_ultralight_write_fail_on_enter(void* context) {\n    NfcApp* instance = context;\n    Widget* widget = instance->widget;\n\n    notification_message(instance->notifications, &sequence_error);\n\n    widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n    widget_add_string_element(\n        widget, 7, 4, AlignLeft, AlignTop, FontPrimary, \"Writing gone wrong!\");\n    widget_add_string_multiline_element(\n        widget,\n        7,\n        17,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Card protected by\\npassword, AUTH0\\nor lock bits\");\n\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Finish\",\n        nfc_scene_mf_ultralight_write_fail_widget_callback,\n        instance);\n\n    // Setup and start worker\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nstatic bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) {\n    bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);\n    uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu;\n\n    return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id);\n}\n\nbool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_write_fail_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_write_success_popup_callback(void* context) {\n    NfcApp* instance = context;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_mf_ultralight_write_success_on_enter(void* context) {\n    NfcApp* instance = context;\n    dolphin_deed(DolphinDeedNfcSave);\n\n    notification_message(instance->notifications, &sequence_success);\n\n    Popup* popup = instance->popup;\n    popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);\n    popup_set_header(popup, \"Successfully\\nwritten\", 5, 22, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, instance);\n    popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback);\n    popup_enable_timeout(popup);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            bool was_saved =\n                scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);\n\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_write_success_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_mf_ultralight_wrong_card_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) {\n    NfcApp* instance = context;\n    Widget* widget = instance->widget;\n\n    notification_message(instance->notifications, &sequence_error);\n\n    widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);\n    widget_add_string_element(\n        widget, 3, 4, AlignLeft, AlignTop, FontPrimary, \"This is wrong card\");\n    widget_add_string_multiline_element(\n        widget,\n        4,\n        17,\n        AlignLeft,\n        AlignTop,\n        FontSecondary,\n        \"Card of the same\\ntype should be\\n presented\");\n    //\"Data management\\nis only possible\\nwith card of same type\");\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Retry\",\n        nfc_scene_mf_ultralight_wrong_card_widget_callback,\n        instance);\n\n    // Setup and start worker\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            consumed = scene_manager_previous_scene(instance->scene_manager);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_more_info.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_more_info_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneMoreInfo, context);\n}\n\nbool nfc_scene_more_info_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneMoreInfo, context, event);\n}\n\nvoid nfc_scene_more_info_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneMoreInfo, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_read.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_read_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context);\n}\n\nbool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneRead, context, event);\n}\n\nvoid nfc_scene_read_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneRead, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_read_menu.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_read_menu_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadMenu, context);\n}\n\nbool nfc_scene_read_menu_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadMenu, context, event);\n}\n\nvoid nfc_scene_read_menu_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadMenu, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_read_success.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_read_success_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadSuccess, context);\n}\n\nbool nfc_scene_read_success_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadSuccess, context, event);\n}\n\nvoid nfc_scene_read_success_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadSuccess, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_restore_original.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_restore_original_popup_callback(void* context) {\n    NfcApp* nfc = context;\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_restore_original_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup view\n    Popup* popup = nfc->popup;\n    popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);\n    popup_set_header(popup, \"Original file\\nrestored\", 5, 22, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, nfc);\n    popup_set_callback(popup, nfc_scene_restore_original_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            if(nfc_load_file(nfc, nfc->file_path, false)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneSavedMenu);\n            } else {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n            }\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_restore_original_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear view\n    popup_reset(nfc->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_restore_original_confirm_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_restore_original_confirm_on_enter(void* context) {\n    NfcApp* nfc = context;\n    DialogEx* dialog_ex = nfc->dialog_ex;\n\n    dialog_ex_set_header(dialog_ex, \"Restore Card Data?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36);\n    dialog_ex_set_text(\n        dialog_ex, \"It will be returned\\nto its original state.\", 47, 21, AlignLeft, AlignTop);\n    dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Restore\");\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_restore_original_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultRight) {\n            if(nfc_delete_shadow_file(nfc)) {\n                scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal);\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneStart);\n            }\n            consumed = true;\n        } else if(event.event == DialogExResultLeft) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_restore_original_confirm_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clean view\n    dialog_ex_reset(nfc->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_retry_confirm.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_retry_confirm_on_enter(void* context) {\n    NfcApp* nfc = context;\n    DialogEx* dialog_ex = nfc->dialog_ex;\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Retry\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Stay\");\n    dialog_ex_set_header(dialog_ex, \"Retry Reading?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(dialog_ex, \"All unsaved data will be lost\", 64, 12, AlignCenter, AlignTop);\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultRight) {\n            consumed = scene_manager_previous_scene(nfc->scene_manager);\n        } else if(event.event == DialogExResultLeft) {\n            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSlixUnlock)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneSlixUnlock);\n            } else if(\n                scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) &&\n                (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) ||\n                 scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfClassicDictAttack);\n            } else if(scene_manager_has_previous_scene(\n                          nfc->scene_manager, NfcSceneMfUltralightUnlockWarn)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneDetect);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneRead);\n            }\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_retry_confirm_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clean view\n    dialog_ex_reset(nfc->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_rpc.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_rpc_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneRpc, context);\n}\n\nbool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneRpc, context, event);\n}\n\nvoid nfc_scene_rpc_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneRpc, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_save_confirm.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_save_confirm_dialog_callback(DialogExResult result, void* context) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);\n}\n\nvoid nfc_scene_save_confirm_on_enter(void* context) {\n    NfcApp* nfc = context;\n    DialogEx* dialog_ex = nfc->dialog_ex;\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Skip\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Save\");\n    dialog_ex_set_header(dialog_ex, \"Save the Key?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(dialog_ex, \"All unsaved data will be lost\", 64, 12, AlignCenter, AlignTop);\n    dialog_ex_set_context(dialog_ex, nfc);\n    dialog_ex_set_result_callback(dialog_ex, nfc_scene_save_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);\n}\n\nbool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultRight) {\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);\n            consumed = true;\n        } else if(event.event == DialogExResultLeft) {\n            NfcSceneSaveConfirmState scene_state =\n                scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm);\n\n            NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ?\n                                 NfcSceneMfClassicMfkeyComplete :\n                                 NfcSceneMfClassicDetectReader;\n\n            scene_manager_next_scene(nfc->scene_manager, scene);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_save_confirm_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clean view\n    dialog_ex_reset(nfc->dialog_ex);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_save_name.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_save_name_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneSaveName, context);\n}\n\nbool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneSaveName, context, event);\n}\n\nvoid nfc_scene_save_name_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneSaveName, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_save_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_save_success_popup_callback(void* context) {\n    NfcApp* nfc = context;\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);\n}\n\nvoid nfc_scene_save_success_on_enter(void* context) {\n    NfcApp* nfc = context;\n\n    // Setup view\n    Popup* popup = nfc->popup;\n    popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n    popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, nfc);\n    popup_set_callback(popup, nfc_scene_save_success_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);\n}\n\nbool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventViewExit) {\n            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfClassicKeys);\n            } else if(scene_manager_has_previous_scene(\n                          nfc->scene_manager, NfcSceneMfUltralightCKeys)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneMfUltralightCKeys);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) {\n                NfcSceneSaveConfirmState scene_state =\n                    scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm);\n\n                NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ?\n                                     NfcSceneMfClassicMfkeyComplete :\n                                     NfcSceneMfClassicDetectReader;\n                scene_manager_next_scene(nfc->scene_manager, scene);\n                consumed = true;\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {\n                consumed = scene_manager_search_and_switch_to_another_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadSuccess)) {\n                consumed = scene_manager_search_and_switch_to_another_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneFileSelect)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    nfc->scene_manager, NfcSceneSavedMenu);\n            } else {\n                consumed = scene_manager_search_and_switch_to_another_scene(\n                    nfc->scene_manager, NfcSceneFileSelect);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_save_success_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    // Clear view\n    popup_reset(nfc->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_saved_menu.c",
    "content": "#include \"../helpers/protocol_support/nfc_protocol_support.h\"\n\nvoid nfc_scene_saved_menu_on_enter(void* context) {\n    nfc_protocol_support_on_enter(NfcProtocolSupportSceneSavedMenu, context);\n}\n\nbool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {\n    return nfc_protocol_support_on_event(NfcProtocolSupportSceneSavedMenu, context, event);\n}\n\nvoid nfc_scene_saved_menu_on_exit(void* context) {\n    nfc_protocol_support_on_exit(NfcProtocolSupportSceneSavedMenu, context);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_select_protocol.c",
    "content": "#include \"../nfc_app_i.h\"\n\nvoid nfc_scene_select_protocol_submenu_callback(void* context, uint32_t index) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_scene_select_protocol_on_enter(void* context) {\n    NfcApp* instance = context;\n    Submenu* submenu = instance->submenu;\n\n    FuriString* temp_str = furi_string_alloc();\n    const char* prefix;\n    if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneExtraActions)) {\n        prefix = \"Read\";\n        nfc_detected_protocols_fill_all_protocols(instance->detected_protocols);\n    } else {\n        prefix = \"Read as\";\n        submenu_set_header(submenu, \"Multi-protocol card\");\n    }\n\n    for(uint32_t i = 0; i < nfc_detected_protocols_get_num(instance->detected_protocols); i++) {\n        furi_string_printf(\n            temp_str,\n            \"%s %s\",\n            prefix,\n            nfc_device_get_protocol_name(\n                nfc_detected_protocols_get_protocol(instance->detected_protocols, i)));\n\n        furi_string_replace_str(temp_str, \"Mifare\", \"MIFARE\");\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(temp_str),\n            i,\n            nfc_scene_select_protocol_submenu_callback,\n            instance);\n    }\n    furi_string_free(temp_str);\n\n    submenu_set_selected_item(\n        submenu, nfc_detected_protocols_get_selected_idx(instance->detected_protocols));\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_select_protocol_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        nfc_detected_protocols_select(instance->detected_protocols, event.event);\n        scene_manager_next_scene(instance->scene_manager, NfcSceneRead);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                instance->scene_manager, NfcSceneStart);\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_select_protocol_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_set_atqa.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_set_atqa_byte_input_changed_callback(void* context) {\n    NfcApp* instance = context;\n    iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store);\n}\n\nvoid nfc_scene_set_atqa_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    iso14443_3a_get_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store);\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter ATQA in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_protocol_support_common_byte_input_done_callback,\n        nfc_scene_set_atqa_byte_input_changed_callback,\n        instance,\n        instance->byte_input_store,\n        2);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            nfc_device_set_data(\n                instance->nfc_device, NfcProtocolIso14443_3a, instance->iso14443_3a_edit_data);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_set_atqa_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_set_sak.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n\nstatic void nfc_scene_set_sak_byte_input_changed_callback(void* context) {\n    NfcApp* instance = context;\n    iso14443_3a_set_sak(instance->iso14443_3a_edit_data, instance->byte_input_store[0]);\n}\n\nvoid nfc_scene_set_sak_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    instance->byte_input_store[0] = iso14443_3a_get_sak(instance->iso14443_3a_edit_data);\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter SAK in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_protocol_support_common_byte_input_done_callback,\n        nfc_scene_set_sak_byte_input_changed_callback,\n        instance,\n        instance->byte_input_store,\n        1);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetAtqa);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_set_sak_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_set_type.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexGeneratorsStart,\n    SubmenuIndexNFCA4 = NfcDataGeneratorTypeNum,\n    SubmenuIndexNFCA7,\n};\n\nstatic void nfc_scene_set_type_init_edit_data(Iso14443_3aData* data, size_t uid_len) {\n    // Easiest way to create a zero'd buffer of given length\n    uint8_t* uid = calloc(1, uid_len);\n    iso14443_3a_set_uid(data, uid, uid_len);\n    free(uid);\n}\n\nvoid nfc_scene_set_type_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    Submenu* submenu = instance->submenu;\n    submenu_add_item(\n        submenu,\n        \"NFC-A 7-bytes UID\",\n        SubmenuIndexNFCA7,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"NFC-A 4-bytes UID\",\n        SubmenuIndexNFCA4,\n        nfc_protocol_support_common_submenu_callback,\n        instance);\n\n    FuriString* str = furi_string_alloc();\n    for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) {\n        furi_string_cat_str(str, nfc_data_generator_get_name(i));\n        furi_string_replace_str(str, \"Mifare\", \"MIFARE\");\n\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(str),\n            i,\n            nfc_protocol_support_common_submenu_callback,\n            instance);\n        furi_string_reset(str);\n    }\n    furi_string_free(str);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexNFCA7) {\n            nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 7);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak);\n            consumed = true;\n        } else if(event.event == SubmenuIndexNFCA4) {\n            nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 4);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak);\n            consumed = true;\n        } else {\n            nfc_data_generator_fill_data(event.event, instance->nfc_device);\n            scene_manager_set_scene_state(\n                instance->scene_manager, NfcSceneGenerateInfo, event.event);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneGenerateInfo);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_set_type_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    submenu_reset(instance->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_set_uid.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include \"../helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n\nvoid nfc_scene_set_uid_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    size_t uid_len;\n    const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);\n    memcpy(instance->byte_input_store, uid, uid_len);\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter UID in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_protocol_support_common_byte_input_done_callback,\n        NULL,\n        instance,\n        instance->byte_input_store,\n        uid_len);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            size_t uid_len = 0;\n            nfc_device_get_uid(instance->nfc_device, &uid_len);\n            nfc_device_set_uid(instance->nfc_device, instance->byte_input_store, uid_len);\n            if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu)) {\n                if(nfc_save(instance)) {\n                    scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);\n                    consumed = true;\n                }\n            } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadMenu)) {\n                scene_manager_search_and_switch_to_previous_scene(\n                    instance->scene_manager, NfcSceneReadMenu);\n                consumed = true;\n            } else {\n                scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName);\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_set_uid_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_slix_key_input.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <bit_lib/bit_lib.h>\n\nvoid nfc_scene_slix_key_input_byte_input_callback(void* context) {\n    NfcApp* instance = context;\n\n    SlixPassword password =\n        bit_lib_bytes_to_num_be(instance->byte_input_store, sizeof(SlixPassword));\n    slix_unlock_set_password(instance->slix_unlock, password);\n    view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone);\n}\n\nvoid nfc_scene_slix_key_input_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    // Setup view\n    ByteInput* byte_input = instance->byte_input;\n    byte_input_set_header_text(byte_input, \"Enter the password in hex\");\n    byte_input_set_result_callback(\n        byte_input,\n        nfc_scene_slix_key_input_byte_input_callback,\n        NULL,\n        instance,\n        instance->byte_input_store,\n        sizeof(SlixPassword));\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);\n}\n\nbool nfc_scene_slix_key_input_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventByteInputDone) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlock);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_slix_key_input_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    // Clear view\n    byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);\n    byte_input_set_header_text(instance->byte_input, \"\");\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_slix_unlock.c",
    "content": "#include \"../nfc_app_i.h\"\n\n#include <nfc/protocols/slix/slix_poller.h>\n\nNfcCommand nfc_scene_slix_unlock_worker_callback(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolSlix);\n\n    NfcCommand command = NfcCommandContinue;\n\n    NfcApp* instance = context;\n    SlixPollerEvent* slix_event = event.event_data;\n    if(slix_event->type == SlixPollerEventTypePrivacyUnlockRequest) {\n        SlixPassword pwd = 0;\n        bool get_password_success = slix_unlock_get_next_password(instance->slix_unlock, &pwd);\n        slix_event->data->privacy_password.password = pwd;\n        slix_event->data->privacy_password.password_set = get_password_success;\n    } else if(slix_event->type == SlixPollerEventTypeError) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);\n    } else if(slix_event->type == SlixPollerEventTypeReady) {\n        nfc_device_set_data(\n            instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller));\n        view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nvoid nfc_scene_slix_unlock_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);\n    popup_set_header(instance->popup, \"Unlocking\", 97, 15, AlignCenter, AlignTop);\n    popup_set_text(\n        instance->popup, \"Hold card next\\nto Flipper's back\", 94, 27, AlignCenter, AlignTop);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);\n\n    instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolSlix);\n    nfc_poller_start(instance->poller, nfc_scene_slix_unlock_worker_callback, instance);\n}\n\nbool nfc_scene_slix_unlock_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    UNUSED(instance);\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == NfcCustomEventPollerFailure) {\n            consumed = true;\n        } else if(event.event == NfcCustomEventPollerSuccess) {\n            notification_message(instance->notifications, &sequence_success);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlockSuccess);\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = scene_manager_search_and_switch_to_previous_scene(\n            instance->scene_manager, NfcSceneSlixUnlockMenu);\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_slix_unlock_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    nfc_poller_stop(instance->poller);\n    nfc_poller_free(instance->poller);\n\n    popup_reset(instance->popup);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_slix_unlock_menu.c",
    "content": "#include \"../nfc_app_i.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexSlixUnlockMenuManual,\n    SubmenuIndexSlixUnlockMenuTonieBox,\n};\n\nvoid nfc_scene_slix_unlock_menu_submenu_callback(void* context, uint32_t index) {\n    NfcApp* instance = context;\n\n    view_dispatcher_send_custom_event(instance->view_dispatcher, index);\n}\n\nvoid nfc_scene_slix_unlock_menu_on_enter(void* context) {\n    NfcApp* instance = context;\n    Submenu* submenu = instance->submenu;\n\n    uint32_t state =\n        scene_manager_get_scene_state(instance->scene_manager, NfcSceneSlixUnlockMenu);\n    submenu_add_item(\n        submenu,\n        \"Enter Password Manually\",\n        SubmenuIndexSlixUnlockMenuManual,\n        nfc_scene_slix_unlock_menu_submenu_callback,\n        instance);\n    submenu_add_item(\n        submenu,\n        \"Auth As TommyBox\",\n        SubmenuIndexSlixUnlockMenuTonieBox,\n        nfc_scene_slix_unlock_menu_submenu_callback,\n        instance);\n    submenu_set_selected_item(submenu, state);\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_slix_unlock_menu_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexSlixUnlockMenuManual) {\n            slix_unlock_set_method(instance->slix_unlock, SlixUnlockMethodManual);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSlixKeyInput);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSlixUnlockMenuTonieBox) {\n            slix_unlock_set_method(instance->slix_unlock, SlixUnlockMethodTonieBox);\n            scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlock);\n            consumed = true;\n        }\n        scene_manager_set_scene_state(\n            instance->scene_manager, NfcSceneSlixUnlockMenu, event.event);\n    }\n    return consumed;\n}\n\nvoid nfc_scene_slix_unlock_menu_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    submenu_reset(instance->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_slix_unlock_success.c",
    "content": "#include \"../nfc_app_i.h\"\n\nstatic void nfc_scene_slix_unlock_success_widget_callback(\n    GuiButtonType result,\n    InputType type,\n    void* context) {\n    NfcApp* instance = context;\n\n    if(type == InputTypeShort) {\n        view_dispatcher_send_custom_event(instance->view_dispatcher, result);\n    }\n}\n\nvoid nfc_scene_slix_unlock_success_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    Widget* widget = instance->widget;\n    widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, \"SLIX Unlocked!\");\n\n    FuriString* temp_str = furi_string_alloc_set_str(\"UID:\");\n    size_t uid_len = 0;\n    const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);\n    for(size_t i = 0; i < uid_len; i++) {\n        furi_string_cat_printf(temp_str, \" %02X\", uid[i]);\n    }\n    furi_string_cat_printf(temp_str, \"\\nPrivacy Mode: Disabled\");\n    widget_add_string_multiline_element(\n        widget, 0, 12, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));\n    furi_string_free(temp_str);\n\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeLeft,\n        \"Retry\",\n        nfc_scene_slix_unlock_success_widget_callback,\n        instance);\n    widget_add_button_element(\n        widget,\n        GuiButtonTypeRight,\n        \"More\",\n        nfc_scene_slix_unlock_success_widget_callback,\n        instance);\n\n    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n}\n\nbool nfc_scene_slix_unlock_success_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeLeft) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneRetryConfirm);\n            consumed = true;\n        } else if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu);\n            consumed = true;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_slix_unlock_success_on_exit(void* context) {\n    NfcApp* instance = context;\n\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_start.c",
    "content": "#include \"../nfc_app_i.h\"\n#include <dolphin/dolphin.h>\n\nenum SubmenuIndex {\n    SubmenuIndexRead,\n    SubmenuIndexDetectReader,\n    SubmenuIndexSaved,\n    SubmenuIndexExtraAction,\n    SubmenuIndexAddManually,\n    SubmenuIndexDebug,\n};\n\nvoid nfc_scene_start_submenu_callback(void* context, uint32_t index) {\n    NfcApp* nfc = context;\n\n    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);\n}\n\nvoid nfc_scene_start_on_enter(void* context) {\n    NfcApp* nfc = context;\n    Submenu* submenu = nfc->submenu;\n\n    // Clear file name and device contents\n    furi_string_reset(nfc->file_name);\n    nfc_device_clear(nfc->nfc_device);\n    iso14443_3a_reset(nfc->iso14443_3a_edit_data);\n    // Reset detected protocols list\n    nfc_detected_protocols_reset(nfc->detected_protocols);\n\n    submenu_add_item(submenu, \"Read\", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc);\n    submenu_add_item(\n        submenu,\n        \"Extract MF Keys\",\n        SubmenuIndexDetectReader,\n        nfc_scene_start_submenu_callback,\n        nfc);\n    submenu_add_item(submenu, \"Saved\", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc);\n    submenu_add_item(\n        submenu, \"Extra Actions\", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc);\n    submenu_add_item(\n        submenu, \"Add Manually\", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc);\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        submenu_add_item(\n            submenu, \"Debug\", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);\n    }\n\n    submenu_set_selected_item(\n        submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));\n\n    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);\n}\n\nbool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* nfc = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexRead) {\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect);\n            dolphin_deed(DolphinDeedNfcRead);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDetectReader) {\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader);\n            consumed = true;\n        } else if(event.event == SubmenuIndexSaved) {\n            // Save the scene state explicitly in each branch, so that\n            // if the user cancels loading a file, the Saved menu item\n            // is properly reselected.\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);\n            consumed = true;\n        } else if(event.event == SubmenuIndexExtraAction) {\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);\n            consumed = true;\n        } else if(event.event == SubmenuIndexAddManually) {\n            scene_manager_set_scene_state(\n                nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);\n            consumed = true;\n        } else if(event.event == SubmenuIndexDebug) {\n            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);\n            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid nfc_scene_start_on_exit(void* context) {\n    NfcApp* nfc = context;\n\n    submenu_reset(nfc->submenu);\n}\n"
  },
  {
    "path": "applications/main/nfc/scenes/nfc_scene_supported_card.c",
    "content": "#include \"nfc/nfc_app_i.h\"\n\n#include \"nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h\"\n\nvoid nfc_scene_supported_card_on_enter(void* context) {\n    NfcApp* instance = context;\n\n    FuriString* temp_str = furi_string_alloc();\n\n    if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {\n        widget_add_text_scroll_element(\n            instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));\n        widget_add_button_element(\n            instance->widget,\n            GuiButtonTypeRight,\n            \"More\",\n            nfc_protocol_support_common_widget_callback,\n            instance);\n\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, true);\n        view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);\n\n    } else {\n        scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, false);\n        scene_manager_next_scene(instance->scene_manager, NfcSceneInfo);\n    }\n\n    furi_string_free(temp_str);\n}\n\nbool nfc_scene_supported_card_on_event(void* context, SceneManagerEvent event) {\n    NfcApp* instance = context;\n\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == GuiButtonTypeRight) {\n            scene_manager_next_scene(instance->scene_manager, NfcSceneInfo);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid nfc_scene_supported_card_on_exit(void* context) {\n    NfcApp* instance = context;\n    widget_reset(instance->widget);\n}\n"
  },
  {
    "path": "applications/main/nfc/views/detect_reader.c",
    "content": "#include \"detect_reader.h\"\n#include <assets_icons.h>\n#include <gui/elements.h>\n\n#define DETECT_READER_UID_MAX_LEN (10)\n\nstruct DetectReader {\n    View* view;\n    DetectReaderDoneCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint16_t nonces;\n    uint16_t nonces_max;\n    DetectReaderState state;\n    FuriString* uid_str;\n} DetectReaderViewModel;\n\nstatic void detect_reader_draw_callback(Canvas* canvas, void* model) {\n    DetectReaderViewModel* m = model;\n    char text[32] = {};\n\n    // Draw header and icon\n    canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34);\n    if(m->state == DetectReaderStateStart) {\n        snprintf(text, sizeof(text), \"Touch the reader\");\n        canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);\n        if(furi_string_size(m->uid_str)) {\n            elements_multiline_text_aligned(\n                canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str));\n        }\n    } else if(m->state == DetectReaderStateReaderDetected) {\n        snprintf(text, sizeof(text), \"Move the Flipper away\");\n        canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);\n    } else if(m->state == DetectReaderStateReaderLost) {\n        snprintf(text, sizeof(text), \"Touch the reader again\");\n        canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);\n    }\n\n    canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, text);\n\n    // Draw collected nonces\n    if(m->state == DetectReaderStateStart) {\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, \"Emulating...\");\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, \"MIFARE MFkey32\");\n    } else {\n        if(m->state == DetectReaderStateDone) {\n            canvas_set_font(canvas, FontPrimary);\n            canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, \"Completed!\");\n            canvas_draw_icon(canvas, 24, 23, &I_check_big_20x17);\n        } else {\n            canvas_set_font(canvas, FontPrimary);\n            canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, \"Collecting...\");\n        }\n        canvas_set_font(canvas, FontSecondary);\n        snprintf(text, sizeof(text), \"Nonce pairs: %d/%d\", m->nonces, m->nonces_max);\n        canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, text);\n    }\n    // Draw button\n    if(m->nonces > 0) {\n        elements_button_center(canvas, \"Done\");\n    }\n}\n\nstatic bool detect_reader_input_callback(InputEvent* event, void* context) {\n    DetectReader* detect_reader = context;\n    furi_assert(detect_reader->callback);\n    bool consumed = false;\n\n    uint8_t nonces = 0;\n    with_view_model(\n        detect_reader->view, DetectReaderViewModel * model, { nonces = model->nonces; }, false);\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyOk) {\n            if(nonces > 0) {\n                detect_reader->callback(detect_reader->context);\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nDetectReader* detect_reader_alloc(void) {\n    DetectReader* detect_reader = malloc(sizeof(DetectReader));\n    detect_reader->view = view_alloc();\n    view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel));\n    view_set_draw_callback(detect_reader->view, detect_reader_draw_callback);\n    view_set_input_callback(detect_reader->view, detect_reader_input_callback);\n    view_set_context(detect_reader->view, detect_reader);\n\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        { model->uid_str = furi_string_alloc(); },\n        false);\n\n    return detect_reader;\n}\n\nvoid detect_reader_free(DetectReader* detect_reader) {\n    furi_assert(detect_reader);\n\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        { furi_string_free(model->uid_str); },\n        false);\n\n    view_free(detect_reader->view);\n    free(detect_reader);\n}\n\nvoid detect_reader_reset(DetectReader* detect_reader) {\n    furi_assert(detect_reader);\n\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        {\n            model->nonces = 0;\n            model->nonces_max = 0;\n            model->state = DetectReaderStateStart;\n            furi_string_reset(model->uid_str);\n        },\n        false);\n}\n\nView* detect_reader_get_view(DetectReader* detect_reader) {\n    furi_assert(detect_reader);\n\n    return detect_reader->view;\n}\n\nvoid detect_reader_set_callback(\n    DetectReader* detect_reader,\n    DetectReaderDoneCallback callback,\n    void* context) {\n    furi_assert(detect_reader);\n    furi_assert(callback);\n\n    detect_reader->callback = callback;\n    detect_reader->context = context;\n}\n\nvoid detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max) {\n    furi_assert(detect_reader);\n\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        { model->nonces_max = nonces_max; },\n        false);\n}\n\nvoid detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected) {\n    furi_assert(detect_reader);\n\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        {\n            model->nonces = nonces_collected;\n            model->state = DetectReaderStateReaderDetected;\n        },\n        false);\n}\n\nvoid detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state) {\n    furi_assert(detect_reader);\n    with_view_model(\n        detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true);\n}\n\nvoid detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) {\n    furi_assert(detect_reader);\n    furi_assert(uid);\n    furi_assert(uid_len < DETECT_READER_UID_MAX_LEN);\n    with_view_model(\n        detect_reader->view,\n        DetectReaderViewModel * model,\n        {\n            furi_string_set_str(model->uid_str, \"UID:\");\n            for(size_t i = 0; i < uid_len; i++) {\n                furi_string_cat_printf(model->uid_str, \" %02X\", uid[i]);\n            }\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/main/nfc/views/detect_reader.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <gui/view.h>\n#include <gui/modules/widget.h>\n\ntypedef struct DetectReader DetectReader;\n\ntypedef enum {\n    DetectReaderStateStart,\n    DetectReaderStateReaderDetected,\n    DetectReaderStateReaderLost,\n    DetectReaderStateDone,\n} DetectReaderState;\n\ntypedef void (*DetectReaderDoneCallback)(void* context);\n\nDetectReader* detect_reader_alloc(void);\n\nvoid detect_reader_free(DetectReader* detect_reader);\n\nvoid detect_reader_reset(DetectReader* detect_reader);\n\nView* detect_reader_get_view(DetectReader* detect_reader);\n\nvoid detect_reader_set_callback(\n    DetectReader* detect_reader,\n    DetectReaderDoneCallback callback,\n    void* context);\n\nvoid detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max);\n\nvoid detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);\n\nvoid detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);\n\nvoid detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len);\n"
  },
  {
    "path": "applications/main/nfc/views/dict_attack.c",
    "content": "#include \"dict_attack.h\"\n\n#include <gui/elements.h>\n\n#define NFC_CLASSIC_KEYS_PER_SECTOR 2\n\nstruct DictAttack {\n    View* view;\n    DictAttackCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    FuriString* header;\n    bool card_detected;\n    DictAttackType attack_type;\n\n    // MIFARE Classic specific\n    uint8_t sectors_total;\n    uint8_t sectors_read;\n    uint8_t current_sector;\n    uint8_t keys_found;\n    bool is_key_attack;\n    uint8_t key_attack_current_sector;\n    MfClassicNestedPhase nested_phase;\n    MfClassicPrngType prng_type;\n    MfClassicBackdoor backdoor;\n    uint16_t nested_target_key;\n    uint16_t msb_count;\n\n    // Ultralight C specific\n    uint8_t pages_total;\n    uint8_t pages_read;\n    bool key_found;\n\n    // Common\n    size_t dict_keys_total;\n    size_t dict_keys_current;\n} DictAttackViewModel;\n\nstatic void dict_attack_draw_mf_classic(Canvas* canvas, DictAttackViewModel* m) {\n    char draw_str[32] = {};\n    canvas_set_font(canvas, FontSecondary);\n\n    switch(m->nested_phase) {\n    case MfClassicNestedPhaseAnalyzePRNG:\n        furi_string_set(m->header, \"PRNG Analysis\");\n        break;\n    case MfClassicNestedPhaseDictAttack:\n    case MfClassicNestedPhaseDictAttackVerify:\n    case MfClassicNestedPhaseDictAttackResume:\n        furi_string_set(m->header, \"Nested Dictionary\");\n        break;\n    case MfClassicNestedPhaseCalibrate:\n    case MfClassicNestedPhaseRecalibrate:\n        furi_string_set(m->header, \"Calibration\");\n        break;\n    case MfClassicNestedPhaseCollectNtEnc:\n        furi_string_set(m->header, \"Nonce Collection\");\n        break;\n    default:\n        break;\n    }\n\n    if(m->prng_type == MfClassicPrngTypeHard) {\n        furi_string_cat(m->header, \" (Hard)\");\n    }\n\n    if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) {\n        if(m->nested_phase != MfClassicNestedPhaseNone) {\n            furi_string_cat(m->header, \" (Backdoor)\");\n        } else {\n            furi_string_set(m->header, \"Backdoor Read\");\n        }\n    }\n\n    canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));\n    if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {\n        uint8_t nonce_sector =\n            m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);\n        snprintf(draw_str, sizeof(draw_str), \"Collecting from sector: %d\", nonce_sector);\n        canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);\n    } else if(m->is_key_attack) {\n        snprintf(\n            draw_str,\n            sizeof(draw_str),\n            \"Reuse key check for sector: %d\",\n            m->key_attack_current_sector);\n    } else {\n        snprintf(draw_str, sizeof(draw_str), \"Unlocking sector: %d\", m->current_sector);\n    }\n    canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);\n    float dict_progress = 0;\n    if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||\n       m->nested_phase == MfClassicNestedPhaseDictAttack ||\n       m->nested_phase == MfClassicNestedPhaseDictAttackVerify ||\n       m->nested_phase == MfClassicNestedPhaseDictAttackResume) {\n        // Phase: Nested dictionary attack\n        uint8_t target_sector =\n            m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16);\n        dict_progress = (float)(target_sector) / (float)(m->sectors_total);\n        snprintf(draw_str, sizeof(draw_str), \"%d/%d\", target_sector, m->sectors_total);\n    } else if(\n        m->nested_phase == MfClassicNestedPhaseCalibrate ||\n        m->nested_phase == MfClassicNestedPhaseRecalibrate ||\n        m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {\n        // Phase: Nonce collection\n        if(m->prng_type == MfClassicPrngTypeWeak) {\n            uint8_t target_sector = m->nested_target_key / 4;\n            dict_progress = (float)(target_sector) / (float)(m->sectors_total);\n            snprintf(draw_str, sizeof(draw_str), \"%d/%d\", target_sector, m->sectors_total);\n        } else {\n            uint16_t max_msb = UINT8_MAX + 1;\n            dict_progress = (float)(m->msb_count) / (float)(max_msb);\n            snprintf(draw_str, sizeof(draw_str), \"%d/%d\", m->msb_count, max_msb);\n        }\n    } else {\n        dict_progress = m->dict_keys_total == 0 ?\n                            0 :\n                            (float)(m->dict_keys_current) / (float)(m->dict_keys_total);\n        if(m->dict_keys_current == 0) {\n            // Cause when people see 0 they think it's broken\n            snprintf(draw_str, sizeof(draw_str), \"%d/%zu\", 1, m->dict_keys_total);\n        } else {\n            snprintf(\n                draw_str, sizeof(draw_str), \"%zu/%zu\", m->dict_keys_current, m->dict_keys_total);\n        }\n    }\n    if(dict_progress > 1.0f) {\n        dict_progress = 1.0f;\n    }\n    elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);\n    canvas_set_font(canvas, FontSecondary);\n    snprintf(\n        draw_str,\n        sizeof(draw_str),\n        \"Keys found: %d/%d\",\n        m->keys_found,\n        m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR);\n    canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);\n    snprintf(draw_str, sizeof(draw_str), \"Sectors Read: %d/%d\", m->sectors_read, m->sectors_total);\n    canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);\n}\n\nstatic void dict_attack_draw_mf_ultralight_c(Canvas* canvas, DictAttackViewModel* m) {\n    char draw_str[32] = {};\n    canvas_set_font(canvas, FontSecondary);\n\n    canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));\n\n    snprintf(draw_str, sizeof(draw_str), \"Trying keys\");\n    canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);\n\n    float dict_progress =\n        m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total);\n    if(m->dict_keys_current == 0) {\n        snprintf(draw_str, sizeof(draw_str), \"%d/%zu\", 1, m->dict_keys_total);\n    } else {\n        snprintf(draw_str, sizeof(draw_str), \"%zu/%zu\", m->dict_keys_current, m->dict_keys_total);\n    }\n    if(dict_progress > 1.0f) {\n        dict_progress = 1.0f;\n    }\n    elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);\n\n    canvas_set_font(canvas, FontSecondary);\n    snprintf(draw_str, sizeof(draw_str), \"Key found: %s\", m->key_found ? \"Yes\" : \"No\");\n    canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);\n\n    snprintf(draw_str, sizeof(draw_str), \"Pages read: %d/%d\", m->pages_read, m->pages_total);\n    canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);\n}\n\nstatic void dict_attack_draw_callback(Canvas* canvas, void* model) {\n    DictAttackViewModel* m = model;\n    if(!m->card_detected) {\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, \"Lost the tag!\");\n        canvas_set_font(canvas, FontSecondary);\n        elements_multiline_text_aligned(\n            canvas, 64, 23, AlignCenter, AlignTop, \"Make sure the tag is\\npositioned correctly.\");\n    } else {\n        if(m->attack_type == DictAttackTypeMfClassic) {\n            dict_attack_draw_mf_classic(canvas, m);\n        } else if(m->attack_type == DictAttackTypeMfUltralightC) {\n            dict_attack_draw_mf_ultralight_c(canvas, m);\n        }\n    }\n    elements_button_center(canvas, \"Skip\");\n}\n\nstatic bool dict_attack_input_callback(InputEvent* event, void* context) {\n    DictAttack* instance = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort && event->key == InputKeyOk) {\n        if(instance->callback) {\n            instance->callback(DictAttackEventSkipPressed, instance->context);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nDictAttack* dict_attack_alloc(void) {\n    DictAttack* instance = malloc(sizeof(DictAttack));\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DictAttackViewModel));\n    view_set_draw_callback(instance->view, dict_attack_draw_callback);\n    view_set_input_callback(instance->view, dict_attack_input_callback);\n    view_set_context(instance->view, instance);\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->header = furi_string_alloc(); },\n        false);\n\n    return instance;\n}\n\nvoid dict_attack_free(DictAttack* instance) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { furi_string_free(model->header); }, false);\n\n    view_free(instance->view);\n    free(instance);\n}\n\nvoid dict_attack_reset(DictAttack* instance) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        {\n            model->attack_type = DictAttackTypeMfClassic;\n\n            // MIFARE Classic fields\n            model->sectors_total = 0;\n            model->sectors_read = 0;\n            model->current_sector = 0;\n            model->keys_found = 0;\n            model->is_key_attack = false;\n            model->nested_phase = MfClassicNestedPhaseNone;\n            model->prng_type = MfClassicPrngTypeUnknown;\n            model->backdoor = MfClassicBackdoorUnknown;\n            model->nested_target_key = 0;\n            model->msb_count = 0;\n\n            // Ultralight C fields\n            model->pages_total = 0;\n            model->pages_read = 0;\n            model->key_found = false;\n\n            // Common fields\n            model->dict_keys_total = 0;\n            model->dict_keys_current = 0;\n            furi_string_reset(model->header);\n        },\n        false);\n}\n\nView* dict_attack_get_view(DictAttack* instance) {\n    furi_assert(instance);\n\n    return instance->view;\n}\n\nvoid dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nvoid dict_attack_set_header(DictAttack* instance, const char* header) {\n    furi_assert(instance);\n    furi_assert(header);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { furi_string_set(model->header, header); },\n        true);\n}\n\nvoid dict_attack_set_card_state(DictAttack* instance, bool detected) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->card_detected = detected; }, true);\n}\n\nvoid dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->sectors_total = sectors_total; },\n        true);\n}\n\nvoid dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->sectors_read = sectors_read; }, true);\n}\n\nvoid dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true);\n}\n\nvoid dict_attack_set_current_sector(DictAttack* instance, uint8_t current_sector) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->current_sector = current_sector; },\n        true);\n}\n\nvoid dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->dict_keys_total = dict_keys_total; },\n        true);\n}\n\nvoid dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->dict_keys_current = cur_key_num; },\n        true);\n}\n\nvoid dict_attack_set_key_attack(DictAttack* instance, uint8_t sector) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        {\n            model->is_key_attack = true;\n            model->key_attack_current_sector = sector;\n        },\n        true);\n}\n\nvoid dict_attack_reset_key_attack(DictAttack* instance) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true);\n}\n\nvoid dict_attack_set_nested_phase(DictAttack* instance, MfClassicNestedPhase nested_phase) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->nested_phase = nested_phase; }, true);\n}\n\nvoid dict_attack_set_prng_type(DictAttack* instance, MfClassicPrngType prng_type) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->prng_type = prng_type; }, true);\n}\n\nvoid dict_attack_set_backdoor(DictAttack* instance, MfClassicBackdoor backdoor) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true);\n}\n\nvoid dict_attack_set_nested_target_key(DictAttack* instance, uint16_t nested_target_key) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        DictAttackViewModel * model,\n        { model->nested_target_key = nested_target_key; },\n        true);\n}\n\nvoid dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true);\n}\n\nvoid dict_attack_set_type(DictAttack* instance, DictAttackType type) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->attack_type = type; }, true);\n}\n\nvoid dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->pages_total = pages_total; }, true);\n}\n\nvoid dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->pages_read = pages_read; }, true);\n}\n\nvoid dict_attack_set_key_found(DictAttack* instance, bool key_found) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view, DictAttackViewModel * model, { model->key_found = key_found; }, true);\n}\n"
  },
  {
    "path": "applications/main/nfc/views/dict_attack.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <gui/view.h>\n#include <lib/nfc/protocols/mf_classic/mf_classic_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    DictAttackTypeMfClassic,\n    DictAttackTypeMfUltralightC,\n} DictAttackType;\n\ntypedef struct DictAttack DictAttack;\n\ntypedef enum {\n    DictAttackEventSkipPressed,\n} DictAttackEvent;\n\ntypedef void (*DictAttackCallback)(DictAttackEvent event, void* context);\n\nDictAttack* dict_attack_alloc(void);\n\nvoid dict_attack_free(DictAttack* instance);\n\nvoid dict_attack_reset(DictAttack* instance);\n\nView* dict_attack_get_view(DictAttack* instance);\n\nvoid dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context);\n\nvoid dict_attack_set_header(DictAttack* instance, const char* header);\n\nvoid dict_attack_set_card_state(DictAttack* instance, bool detected);\n\nvoid dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total);\n\nvoid dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read);\n\nvoid dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found);\n\nvoid dict_attack_set_current_sector(DictAttack* instance, uint8_t curr_sec);\n\nvoid dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total);\n\nvoid dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num);\n\nvoid dict_attack_set_key_attack(DictAttack* instance, uint8_t sector);\n\nvoid dict_attack_reset_key_attack(DictAttack* instance);\n\nvoid dict_attack_set_nested_phase(DictAttack* instance, MfClassicNestedPhase nested_phase);\n\nvoid dict_attack_set_prng_type(DictAttack* instance, MfClassicPrngType prng_type);\n\nvoid dict_attack_set_backdoor(DictAttack* instance, MfClassicBackdoor backdoor);\n\nvoid dict_attack_set_nested_target_key(DictAttack* instance, uint16_t target_key);\n\nvoid dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count);\n\nvoid dict_attack_set_type(DictAttack* instance, DictAttackType type);\n\nvoid dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total);\n\nvoid dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read);\n\nvoid dict_attack_set_key_found(DictAttack* instance, bool key_found);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/onewire/application.fam",
    "content": "App(\n    appid=\"cli_onewire\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_onewire_ep\",\n    requires=[\"cli\"],\n    sources=[\"onewire_cli.c\"],\n)\n"
  },
  {
    "path": "applications/main/onewire/onewire_cli.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <cli/cli_main_commands.h>\n#include <power/power_service/power.h>\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/args.h>\n\n#include <one_wire/one_wire_host.h>\n\nstatic void onewire_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"onewire search\\r\\n\");\n}\n\nstatic void onewire_cli_search(PipeSide* pipe) {\n    UNUSED(pipe);\n    OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton);\n    Power* power = furi_record_open(RECORD_POWER);\n    uint8_t address[8];\n    bool done = false;\n\n    printf(\"Search started\\r\\n\");\n\n    onewire_host_start(onewire);\n    power_enable_otg(power, true);\n\n    while(!done) { //-V1044\n        if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {\n            printf(\"Search finished\\r\\n\");\n            onewire_host_reset_search(onewire);\n            done = true;\n        } else {\n            printf(\"Found: \");\n            for(uint8_t i = 0; i < 8; i++) {\n                printf(\"%02X\", address[i]);\n            }\n            printf(\"\\r\\n\");\n        }\n        furi_delay_ms(100);\n    }\n\n    power_enable_otg(power, false);\n\n    onewire_host_free(onewire);\n    furi_record_close(RECORD_POWER);\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    if(!args_read_string_and_trim(args, cmd)) {\n        furi_string_free(cmd);\n        onewire_cli_print_usage();\n        return;\n    }\n\n    if(furi_string_cmp_str(cmd, \"search\") == 0) {\n        onewire_cli_search(pipe);\n    }\n\n    furi_string_free(cmd);\n}\n\nCLI_COMMAND_INTERFACE(onewire, execute, CliCommandFlagDefault, 1024, CLI_APPID);\n"
  },
  {
    "path": "applications/main/subghz/application.fam",
    "content": "App(\n    appid=\"subghz\",\n    name=\"Sub-GHz\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    targets=[\"f7\"],\n    entry_point=\"subghz_app\",\n    icon=\"A_Sub1ghz_14\",\n    stack_size=3 * 1024,\n    order=10,\n    sources=[\n        \"*.c\",\n        \"!subghz_cli.c\",\n        \"!helpers/subghz_chat.c\",\n    ],\n    requires=[\"region\"],\n    resources=\"resources\",\n    fap_libs=[\"assets\", \"hwdrivers\"],\n    fap_icon=\"icon.png\",\n    fap_category=\"Sub-GHz\",\n)\n\nApp(\n    appid=\"cli_subghz\",\n    targets=[\"f7\"],\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_subghz_ep\",\n    requires=[\"cli\"],\n    sources=[\"subghz_cli.c\", \"helpers/subghz_chat.c\"],\n)\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_chat.c",
    "content": "#include \"subghz_chat.h\"\n#include <lib/subghz/subghz_tx_rx_worker.h>\n#include <toolbox/pipe.h>\n\n#define TAG \"SubGhzChat\"\n\n#define SUBGHZ_CHAT_WORKER_TIMEOUT_BETWEEN_MESSAGES 500\n\nstruct SubGhzChatWorker {\n    FuriThread* thread;\n    SubGhzTxRxWorker* subghz_txrx;\n\n    volatile bool worker_running;\n    volatile bool worker_stoping;\n    FuriMessageQueue* event_queue;\n    uint32_t last_time_rx_data;\n\n    PipeSide* pipe;\n};\n\n/** Worker thread\n * \n * @param context \n * @return exit code \n */\nstatic int32_t subghz_chat_worker_thread(void* context) {\n    SubGhzChatWorker* instance = context;\n    FURI_LOG_I(TAG, \"Worker start\");\n    char c;\n    SubGhzChatEvent event;\n    event.event = SubGhzChatEventUserEntrance;\n    furi_message_queue_put(instance->event_queue, &event, 0);\n    while(instance->worker_running) {\n        if(pipe_receive(instance->pipe, (uint8_t*)&c, 1) == 1) {\n            event.event = SubGhzChatEventInputData;\n            event.c = c;\n            furi_message_queue_put(instance->event_queue, &event, FuriWaitForever);\n        }\n    }\n\n    FURI_LOG_I(TAG, \"Worker stop\");\n    return 0;\n}\n\nstatic void subghz_chat_worker_update_rx_event_chat(void* context) {\n    furi_assert(context);\n    SubGhzChatWorker* instance = context;\n    SubGhzChatEvent event;\n    if((furi_get_tick() - instance->last_time_rx_data) >\n       SUBGHZ_CHAT_WORKER_TIMEOUT_BETWEEN_MESSAGES) {\n        event.event = SubGhzChatEventNewMessage;\n        furi_message_queue_put(instance->event_queue, &event, FuriWaitForever);\n    }\n    instance->last_time_rx_data = furi_get_tick();\n    event.event = SubGhzChatEventRXData;\n    furi_message_queue_put(instance->event_queue, &event, FuriWaitForever);\n}\n\nSubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe) {\n    SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker));\n\n    instance->pipe = pipe;\n\n    instance->thread =\n        furi_thread_alloc_ex(\"SubGhzChat\", 2048, subghz_chat_worker_thread, instance);\n    instance->subghz_txrx = subghz_tx_rx_worker_alloc();\n    instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));\n    return instance;\n}\n\nvoid subghz_chat_worker_free(SubGhzChatWorker* instance) {\n    furi_assert(instance);\n    furi_assert(!instance->worker_running);\n    furi_message_queue_free(instance->event_queue);\n    subghz_tx_rx_worker_free(instance->subghz_txrx);\n    furi_thread_free(instance->thread);\n\n    free(instance);\n}\n\nbool subghz_chat_worker_start(\n    SubGhzChatWorker* instance,\n    const SubGhzDevice* device,\n    uint32_t frequency) {\n    furi_assert(instance);\n    furi_assert(!instance->worker_running);\n    bool res = false;\n\n    if(subghz_tx_rx_worker_start(instance->subghz_txrx, device, frequency)) {\n        furi_message_queue_reset(instance->event_queue);\n        subghz_tx_rx_worker_set_callback_have_read(\n            instance->subghz_txrx, subghz_chat_worker_update_rx_event_chat, instance);\n\n        instance->worker_running = true;\n        instance->last_time_rx_data = 0;\n\n        furi_thread_start(instance->thread);\n\n        res = true;\n    }\n    return res;\n}\n\nvoid subghz_chat_worker_stop(SubGhzChatWorker* instance) {\n    furi_assert(instance);\n    furi_assert(instance->worker_running);\n    if(subghz_tx_rx_worker_is_running(instance->subghz_txrx)) {\n        subghz_tx_rx_worker_stop(instance->subghz_txrx);\n    }\n\n    instance->worker_running = false;\n\n    furi_thread_join(instance->thread);\n}\n\nbool subghz_chat_worker_is_running(SubGhzChatWorker* instance) {\n    furi_assert(instance);\n    return instance->worker_running;\n}\n\nSubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance) {\n    furi_assert(instance);\n    SubGhzChatEvent event;\n    if(furi_message_queue_get(instance->event_queue, &event, FuriWaitForever) == FuriStatusOk) {\n        return event;\n    } else {\n        event.event = SubGhzChatEventNoEvent;\n        return event;\n    }\n}\n\nvoid subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubGhzChatEvent* event) {\n    furi_assert(instance);\n    furi_message_queue_put(instance->event_queue, event, FuriWaitForever);\n}\n\nsize_t subghz_chat_worker_available(SubGhzChatWorker* instance) {\n    furi_assert(instance);\n    return subghz_tx_rx_worker_available(instance->subghz_txrx);\n}\n\nsize_t subghz_chat_worker_read(SubGhzChatWorker* instance, uint8_t* data, size_t size) {\n    furi_assert(instance);\n    return subghz_tx_rx_worker_read(instance->subghz_txrx, data, size);\n}\n\nbool subghz_chat_worker_write(SubGhzChatWorker* instance, uint8_t* data, size_t size) {\n    furi_assert(instance);\n    return subghz_tx_rx_worker_write(instance->subghz_txrx, data, size);\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_chat.h",
    "content": "#pragma once\n#include \"../subghz_i.h\"\n#include <lib/subghz/devices/devices.h>\n#include <toolbox/pipe.h>\n\ntypedef struct SubGhzChatWorker SubGhzChatWorker;\n\ntypedef enum {\n    SubGhzChatEventNoEvent,\n    SubGhzChatEventUserEntrance,\n    SubGhzChatEventUserExit,\n    SubGhzChatEventInputData,\n    SubGhzChatEventRXData,\n    SubGhzChatEventNewMessage,\n} SubGhzChatEventType;\n\ntypedef struct {\n    SubGhzChatEventType event;\n    char c;\n} SubGhzChatEvent;\n\nSubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe);\nvoid subghz_chat_worker_free(SubGhzChatWorker* instance);\nbool subghz_chat_worker_start(\n    SubGhzChatWorker* instance,\n    const SubGhzDevice* device,\n    uint32_t frequency);\nvoid subghz_chat_worker_stop(SubGhzChatWorker* instance);\nbool subghz_chat_worker_is_running(SubGhzChatWorker* instance);\nSubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance);\nvoid subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubGhzChatEvent* event);\nsize_t subghz_chat_worker_available(SubGhzChatWorker* instance);\nsize_t subghz_chat_worker_read(SubGhzChatWorker* instance, uint8_t* data, size_t size);\nbool subghz_chat_worker_write(SubGhzChatWorker* instance, uint8_t* data, size_t size);\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_custom_event.h",
    "content": "#pragma once\n\ntypedef enum {\n    SubGhzCustomEventManagerNoSet = 0,\n    SubGhzCustomEventManagerSet,\n    SubGhzCustomEventManagerSetRAW,\n\n    //SubmenuIndex\n    SubmenuIndexPricenton_433,\n    SubmenuIndexPricenton_315,\n    SubmenuIndexNiceFlo12bit,\n    SubmenuIndexNiceFlo24bit,\n    SubmenuIndexCAME12bit,\n    SubmenuIndexCAME24bit,\n    SubmenuIndexCAMETwee,\n    SubmenuIndexGateTX,\n    SubmenuIndexDoorHan_315_00,\n    SubmenuIndexDoorHan_433_92,\n    SubmenuIndexLinear_300_00,\n    SubmenuIndexLiftMaster_315_00,\n    SubmenuIndexLiftMaster_390_00,\n    SubmenuIndexSecPlus_v2_310_00,\n    SubmenuIndexSecPlus_v2_315_00,\n    SubmenuIndexSecPlus_v2_390_00,\n\n    //SubGhzCustomEvent\n    SubGhzCustomEventSceneDeleteSuccess = 100,\n    SubGhzCustomEventSceneDelete,\n    SubGhzCustomEventSceneDeleteBack,\n    SubGhzCustomEventSceneDeleteRAW,\n    SubGhzCustomEventSceneDeleteRAWBack,\n\n    SubGhzCustomEventSceneReceiverInfoTxStart,\n    SubGhzCustomEventSceneReceiverInfoTxStop,\n    SubGhzCustomEventSceneReceiverInfoSave,\n    SubGhzCustomEventSceneSaveName,\n    SubGhzCustomEventSceneSaveSuccess,\n    SubGhzCustomEventSceneShowErrorBack,\n    SubGhzCustomEventSceneShowErrorOk,\n    SubGhzCustomEventSceneShowErrorSub,\n    SubGhzCustomEventSceneShowOnlyRX,\n    SubGhzCustomEventSceneAnalyzerLock,\n    SubGhzCustomEventSceneAnalyzerUnlock,\n    SubGhzCustomEventSceneSettingLock,\n\n    SubGhzCustomEventSceneExit,\n    SubGhzCustomEventSceneStay,\n\n    SubGhzCustomEventSceneRpcLoad,\n    SubGhzCustomEventSceneRpcButtonPress,\n    SubGhzCustomEventSceneRpcButtonRelease,\n    SubGhzCustomEventSceneRpcButtonPressRelease,\n    SubGhzCustomEventSceneRpcSessionClose,\n\n    SubGhzCustomEventViewReceiverOK,\n    SubGhzCustomEventViewReceiverConfig,\n    SubGhzCustomEventViewReceiverBack,\n    SubGhzCustomEventViewReceiverOffDisplay,\n    SubGhzCustomEventViewReceiverUnlock,\n\n    SubGhzCustomEventViewReadRAWBack,\n    SubGhzCustomEventViewReadRAWIDLE,\n    SubGhzCustomEventViewReadRAWREC,\n    SubGhzCustomEventViewReadRAWConfig,\n    SubGhzCustomEventViewReadRAWErase,\n    SubGhzCustomEventViewReadRAWSendStart,\n    SubGhzCustomEventViewReadRAWSendStop,\n    SubGhzCustomEventViewReadRAWSave,\n    SubGhzCustomEventViewReadRAWTXRXStop,\n    SubGhzCustomEventViewReadRAWMore,\n\n    SubGhzCustomEventViewTransmitterBack,\n    SubGhzCustomEventViewTransmitterSendStart,\n    SubGhzCustomEventViewTransmitterSendStop,\n    SubGhzCustomEventViewTransmitterSendSave,\n    SubGhzCustomEventViewTransmitterError,\n} SubGhzCustomEvent;\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c",
    "content": "#include \"subghz_frequency_analyzer_log_item_array.h\"\n\nconst char*\n    subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by) {\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) {\n        return \"Seq. A\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) {\n        return \"Count D\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) {\n        return \"Count A\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) {\n        return \"RSSI D\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) {\n        return \"RSSI A\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) {\n        return \"Freq. D\";\n    }\n    if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) {\n        return \"Freq. A\";\n    }\n    return \"Seq. D\";\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h",
    "content": "#pragma once\n\n#include <m-tuple.h>\n#include <m-array.h>\n#include <m-algo.h>\n#include <m-funcobj.h>\n\ntypedef enum {\n    SubGhzFrequencyAnalyzerLogOrderBySeqDesc,\n    SubGhzFrequencyAnalyzerLogOrderBySeqAsc,\n    SubGhzFrequencyAnalyzerLogOrderByCountDesc,\n    SubGhzFrequencyAnalyzerLogOrderByCountAsc,\n    SubGhzFrequencyAnalyzerLogOrderByRSSIDesc,\n    SubGhzFrequencyAnalyzerLogOrderByRSSIAsc,\n    SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc,\n    SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc,\n} SubGhzFrequencyAnalyzerLogOrderBy;\n\nconst char*\n    subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by);\n\nTUPLE_DEF2( //-V1048\n    SubGhzFrequencyAnalyzerLogItem,\n    (seq, uint8_t),\n    (frequency, uint32_t),\n    (count, uint8_t),\n    (rssi_max, uint8_t))\n/* Register globally the oplist */\n#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \\\n    TUPLE_OPLIST(                                \\\n        SubGhzFrequencyAnalyzerLogItem,          \\\n        M_DEFAULT_OPLIST,                        \\\n        M_DEFAULT_OPLIST,                        \\\n        M_DEFAULT_OPLIST,                        \\\n        M_DEFAULT_OPLIST)\n\n/* Define the array, register the oplist and define further algorithms on it */\nARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) //-V779 //-V658\n#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \\\n    ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t())\nALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t)\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Warray-bounds\"\nFUNC_OBJ_INS_DEF(\n    SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */,\n    SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */,\n    (a,\n     b) /* name of the input parameters of the function like object. The type are inherited from the interface. */\n    ,\n    {\n        /* code of the function object */\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) {\n            return a->frequency < b->frequency ? -1 : a->frequency > b->frequency;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) {\n            return a->frequency > b->frequency ? -1 : a->frequency < b->frequency;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) {\n            return a->rssi_max < b->rssi_max ? -1 : a->rssi_max > b->rssi_max;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) {\n            return a->rssi_max > b->rssi_max ? -1 : a->rssi_max < b->rssi_max;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) {\n            return a->count < b->count ? -1 : a->count > b->count;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) {\n            return a->count > b->count ? -1 : a->count < b->count;\n        }\n        if(self->order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) {\n            return a->seq < b->seq ? -1 : a->seq > b->seq;\n        }\n\n        return a->seq > b->seq ? -1 : a->seq < b->seq;\n    },\n    /* Additional fields stored in the function object */\n    (order_by, SubGhzFrequencyAnalyzerLogOrderBy))\n#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \\\n    FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST)\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c",
    "content": "#include \"subghz_frequency_analyzer_worker.h\"\n#include <lib/drivers/cc1101.h>\n\n#include <furi.h>\n#include <float_tools.h>\n\n#define TAG \"SubghzFrequencyAnalyzerWorker\"\n\nstatic const uint8_t subghz_preset_ook_58khz[][2] = {\n    {CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz\n    /* End  */\n    {0, 0},\n};\n\nstatic const uint8_t subghz_preset_ook_650khz[][2] = {\n    {CC1101_MDMCFG4, 0b00010111}, // Rx BW filter is 650.000kHz\n    /* End  */\n    {0, 0},\n};\n\nstruct SubGhzFrequencyAnalyzerWorker {\n    FuriThread* thread;\n\n    volatile bool worker_running;\n    uint8_t sample_hold_counter;\n    FrequencyRSSI frequency_rssi_buf;\n    SubGhzSetting* setting;\n\n    float filVal;\n\n    SubGhzFrequencyAnalyzerWorkerPairCallback pair_callback;\n    void* context;\n};\n\nstatic void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) {\n    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);\n    size_t i = 0;\n    while(data[i][0]) {\n        cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]);\n        i++;\n    }\n    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);\n}\n\n// running average with adaptive coefficient\nstatic uint32_t subghz_frequency_analyzer_worker_expRunningAverageAdaptive(\n    SubGhzFrequencyAnalyzerWorker* instance,\n    uint32_t newVal) {\n    float k;\n    float newValFloat = newVal;\n    // the sharpness of the filter depends on the absolute value of the difference\n    if(fabsf(newValFloat - instance->filVal) > 500000.f)\n        k = 0.9;\n    else\n        k = 0.03;\n\n    instance->filVal += (newValFloat - instance->filVal) * k;\n    return (uint32_t)instance->filVal;\n}\n\n/** Worker thread\n * \n * @param context \n * @return exit code \n */\nstatic int32_t subghz_frequency_analyzer_worker_thread(void* context) {\n    SubGhzFrequencyAnalyzerWorker* instance = context;\n\n    FrequencyRSSI frequency_rssi = {\n        .frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0};\n    float rssi = 0;\n    uint32_t frequency = 0;\n    float rssi_temp = -127.0f;\n    uint32_t frequency_temp = 0;\n\n    //Start CC1101\n    furi_hal_subghz_reset();\n\n    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);\n    cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz);\n    cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);\n    cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW);\n    cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3,\n                     0b01111111); // symbol rate\n    cc1101_write_reg(\n        &furi_hal_spi_bus_handle_subghz,\n        CC1101_AGCCTRL2,\n        0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB\n    cc1101_write_reg(\n        &furi_hal_spi_bus_handle_subghz,\n        CC1101_AGCCTRL1,\n        0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled\n    cc1101_write_reg(\n        &furi_hal_spi_bus_handle_subghz,\n        CC1101_AGCCTRL0,\n        0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary\n\n    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);\n\n    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);\n\n    while(instance->worker_running) {\n        furi_delay_ms(10);\n\n        float rssi_min = 26.0f;\n        float rssi_avg = 0;\n        size_t rssi_avg_samples = 0;\n\n        frequency_rssi.rssi_coarse = -127.0f;\n        frequency_rssi.rssi_fine = -127.0f;\n        furi_hal_subghz_idle();\n        subghz_frequency_analyzer_worker_load_registers(subghz_preset_ook_650khz);\n\n        // First stage: coarse scan\n        for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {\n            if(furi_hal_subghz_is_frequency_valid(\n                   subghz_setting_get_frequency(instance->setting, i))) {\n                furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);\n                cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);\n                frequency = cc1101_set_frequency(\n                    &furi_hal_spi_bus_handle_subghz,\n                    subghz_setting_get_frequency(instance->setting, i));\n\n                cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);\n\n                furi_check(cc1101_wait_status_state(\n                    &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));\n\n                cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);\n                furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);\n\n                furi_delay_ms(2);\n\n                rssi = furi_hal_subghz_get_rssi();\n\n                rssi_avg += rssi;\n                rssi_avg_samples++;\n\n                if(rssi < rssi_min) rssi_min = rssi;\n\n                if(frequency_rssi.rssi_coarse < rssi) {\n                    frequency_rssi.rssi_coarse = rssi;\n                    frequency_rssi.frequency_coarse = frequency;\n                }\n            }\n        }\n\n        FURI_LOG_T(\n            TAG,\n            \"RSSI: avg %f, max %f at %lu, min %f\",\n            (double)(rssi_avg / rssi_avg_samples),\n            (double)frequency_rssi.rssi_coarse,\n            frequency_rssi.frequency_coarse,\n            (double)rssi_min);\n\n        // Second stage: fine scan\n        if(frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) {\n            furi_hal_subghz_idle();\n            subghz_frequency_analyzer_worker_load_registers(subghz_preset_ook_58khz);\n            //for example -0.3 ... 433.92 ... +0.3 step 20KHz\n            for(uint32_t i = frequency_rssi.frequency_coarse - 300000;\n                i < frequency_rssi.frequency_coarse + 300000;\n                i += 20000) {\n                if(furi_hal_subghz_is_frequency_valid(i)) {\n                    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);\n                    cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);\n                    frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i);\n\n                    cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);\n\n                    furi_check(cc1101_wait_status_state(\n                        &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));\n\n                    cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);\n                    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);\n\n                    furi_delay_ms(2);\n\n                    rssi = furi_hal_subghz_get_rssi();\n\n                    FURI_LOG_T(TAG, \"#:%lu:%f\", frequency, (double)rssi);\n\n                    if(frequency_rssi.rssi_fine < rssi) {\n                        frequency_rssi.rssi_fine = rssi;\n                        frequency_rssi.frequency_fine = frequency;\n                    }\n                }\n            }\n        }\n\n        // Deliver results fine\n        if(frequency_rssi.rssi_fine > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) {\n            FURI_LOG_D(\n                TAG, \"=:%lu:%f\", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine);\n\n            instance->sample_hold_counter = 20;\n            rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2;\n            frequency_temp = frequency_rssi.frequency_fine;\n\n            if(!float_is_equal(instance->filVal, 0.f)) {\n                frequency_rssi.frequency_fine =\n                    subghz_frequency_analyzer_worker_expRunningAverageAdaptive(\n                        instance, frequency_rssi.frequency_fine);\n            }\n            // Deliver callback\n            if(instance->pair_callback) {\n                instance->pair_callback(\n                    instance->context, frequency_rssi.frequency_fine, rssi_temp, true);\n            }\n        } else if( // Deliver results coarse\n            (frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) &&\n            (instance->sample_hold_counter < 10)) {\n            FURI_LOG_D(\n                TAG,\n                \"~:%lu:%f\",\n                frequency_rssi.frequency_coarse,\n                (double)frequency_rssi.rssi_coarse);\n\n            instance->sample_hold_counter = 20;\n            rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2;\n            frequency_temp = frequency_rssi.frequency_coarse;\n            if(!float_is_equal(instance->filVal, 0.f)) {\n                frequency_rssi.frequency_coarse =\n                    subghz_frequency_analyzer_worker_expRunningAverageAdaptive(\n                        instance, frequency_rssi.frequency_coarse);\n            }\n            // Deliver callback\n            if(instance->pair_callback) {\n                instance->pair_callback(\n                    instance->context, frequency_rssi.frequency_coarse, rssi_temp, true);\n            }\n        } else {\n            if(instance->sample_hold_counter > 0) {\n                instance->sample_hold_counter--;\n                if(instance->sample_hold_counter == 15) {\n                    if(instance->pair_callback) {\n                        instance->pair_callback(\n                            instance->context, frequency_temp, rssi_temp, false);\n                    }\n                }\n            } else {\n                instance->filVal = 0;\n                rssi_temp = -127.0f;\n                instance->pair_callback(instance->context, 0, 0, false);\n            }\n        }\n    }\n\n    //Stop CC1101\n    furi_hal_subghz_idle();\n    furi_hal_subghz_sleep();\n\n    return 0;\n}\n\nSubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* context) {\n    furi_assert(context);\n    SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker));\n\n    instance->thread = furi_thread_alloc_ex(\n        \"SubGhzFAWorker\", 2048, subghz_frequency_analyzer_worker_thread, instance);\n    SubGhz* subghz = context;\n    instance->setting = subghz_txrx_get_setting(subghz->txrx);\n    return instance;\n}\n\nvoid subghz_frequency_analyzer_worker_free(SubGhzFrequencyAnalyzerWorker* instance) {\n    furi_assert(instance);\n\n    furi_thread_free(instance->thread);\n    free(instance);\n}\n\nvoid subghz_frequency_analyzer_worker_set_pair_callback(\n    SubGhzFrequencyAnalyzerWorker* instance,\n    SubGhzFrequencyAnalyzerWorkerPairCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(context);\n    instance->pair_callback = callback;\n    instance->context = context;\n}\n\nvoid subghz_frequency_analyzer_worker_start(SubGhzFrequencyAnalyzerWorker* instance) {\n    furi_assert(instance);\n    furi_assert(!instance->worker_running);\n\n    instance->worker_running = true;\n\n    furi_thread_start(instance->thread);\n}\n\nvoid subghz_frequency_analyzer_worker_stop(SubGhzFrequencyAnalyzerWorker* instance) {\n    furi_assert(instance);\n    furi_assert(instance->worker_running);\n\n    instance->worker_running = false;\n\n    furi_thread_join(instance->thread);\n}\n\nbool subghz_frequency_analyzer_worker_is_running(SubGhzFrequencyAnalyzerWorker* instance) {\n    furi_assert(instance);\n    return instance->worker_running;\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h",
    "content": "#pragma once\n\n#include <furi_hal.h>\n#include \"../subghz_i.h\"\n\n#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f\n\ntypedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;\n\ntypedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)(\n    void* context,\n    uint32_t frequency,\n    float rssi,\n    bool signal);\n\ntypedef struct {\n    uint32_t frequency_coarse;\n    float rssi_coarse;\n    uint32_t frequency_fine;\n    float rssi_fine;\n} FrequencyRSSI;\n\n/** Allocate SubGhzFrequencyAnalyzerWorker\n * \n * @param context SubGhz* context\n * @return SubGhzFrequencyAnalyzerWorker* \n */\nSubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* context);\n\n/** Free SubGhzFrequencyAnalyzerWorker\n * \n * @param instance SubGhzFrequencyAnalyzerWorker instance\n */\nvoid subghz_frequency_analyzer_worker_free(SubGhzFrequencyAnalyzerWorker* instance);\n\n/** Pair callback SubGhzFrequencyAnalyzerWorker\n * \n * @param instance SubGhzFrequencyAnalyzerWorker instance\n * @param callback SubGhzFrequencyAnalyzerWorkerOverrunCallback callback\n * @param context \n */\nvoid subghz_frequency_analyzer_worker_set_pair_callback(\n    SubGhzFrequencyAnalyzerWorker* instance,\n    SubGhzFrequencyAnalyzerWorkerPairCallback callback,\n    void* context);\n\n/** Start SubGhzFrequencyAnalyzerWorker\n * \n * @param instance SubGhzFrequencyAnalyzerWorker instance\n */\nvoid subghz_frequency_analyzer_worker_start(SubGhzFrequencyAnalyzerWorker* instance);\n\n/** Stop SubGhzFrequencyAnalyzerWorker\n * \n * @param instance SubGhzFrequencyAnalyzerWorker instance\n */\nvoid subghz_frequency_analyzer_worker_stop(SubGhzFrequencyAnalyzerWorker* instance);\n\n/** Check if worker is running\n * @param instance SubGhzFrequencyAnalyzerWorker instance\n * @return bool - true if running\n */\nbool subghz_frequency_analyzer_worker_is_running(SubGhzFrequencyAnalyzerWorker* instance);\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_threshold_rssi.c",
    "content": "#include \"subghz_threshold_rssi.h\"\n#include \"../views/subghz_read_raw.h\"\n#include <float_tools.h>\n\n#define TAG \"SubGhzThresholdRssi\"\n\n#define THRESHOLD_RSSI_LOW_COUNT 10\n\nstruct SubGhzThresholdRssi {\n    float threshold_rssi;\n    uint8_t threshold_rssi_low_count;\n};\n\nSubGhzThresholdRssi* subghz_threshold_rssi_alloc(void) {\n    SubGhzThresholdRssi* instance = malloc(sizeof(SubGhzThresholdRssi));\n    instance->threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;\n    instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;\n    return instance;\n}\n\nvoid subghz_threshold_rssi_free(SubGhzThresholdRssi* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nvoid subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi) {\n    furi_assert(instance);\n    instance->threshold_rssi = rssi;\n}\n\nfloat subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) {\n    furi_assert(instance);\n    return instance->threshold_rssi;\n}\n\nSubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi) {\n    furi_assert(instance);\n    SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false};\n\n    if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) {\n        ret.is_above = true;\n    } else {\n        if(rssi < instance->threshold_rssi) {\n            instance->threshold_rssi_low_count++;\n            if(instance->threshold_rssi_low_count > THRESHOLD_RSSI_LOW_COUNT) {\n                instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;\n            }\n            ret.is_above = false;\n        } else {\n            instance->threshold_rssi_low_count = 0;\n        }\n\n        if(instance->threshold_rssi_low_count == THRESHOLD_RSSI_LOW_COUNT) {\n            ret.is_above = false;\n        } else {\n            ret.is_above = true;\n        }\n    }\n    return ret;\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_threshold_rssi.h",
    "content": "#pragma once\n\n#include <furi.h>\n\ntypedef struct {\n    float rssi; /**< Current RSSI */\n    bool is_above; /**< Exceeded threshold level */\n} SubGhzThresholdRssiData;\n\ntypedef struct SubGhzThresholdRssi SubGhzThresholdRssi;\n\n/** Allocate SubGhzThresholdRssi\n * \n * @return SubGhzThresholdRssi* \n */\nSubGhzThresholdRssi* subghz_threshold_rssi_alloc(void);\n\n/** Free SubGhzThresholdRssi\n * \n * @param instance Pointer to a SubGhzThresholdRssi\n */\nvoid subghz_threshold_rssi_free(SubGhzThresholdRssi* instance);\n\n/** Set threshold\n * \n * @param instance Pointer to a SubGhzThresholdRssi\n * @param rssi RSSI threshold\n */\nvoid subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi);\n\n/** Get threshold\n * \n * @param instance Pointer to a SubGhzThresholdRssi\n * @return float RSSI threshold\n */\nfloat subghz_threshold_rssi_get(SubGhzThresholdRssi* instance);\n\n/** Check threshold\n * \n * @param instance Pointer to a SubGhzThresholdRssi\n * @param rssi Current RSSI\n * @return SubGhzThresholdRssiData \n */\nSubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi);\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_txrx.c",
    "content": "#include \"subghz_txrx_i.h\" // IWYU pragma: keep\n\n#include <lib/subghz/protocols/protocol_items.h>\n#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>\n#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>\n\n#include <power/power_service/power.h>\n\n#define TAG \"SubGhz\"\n\nstatic void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {\n    UNUSED(instance);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, true);\n    furi_record_close(RECORD_POWER);\n}\n\nstatic void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {\n    UNUSED(instance);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, false);\n    furi_record_close(RECORD_POWER);\n}\n\nSubGhzTxRx* subghz_txrx_alloc(void) {\n    SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));\n    instance->setting = subghz_setting_alloc();\n    subghz_setting_load(instance->setting, EXT_PATH(\"subghz/assets/setting_user\"));\n\n    instance->preset = malloc(sizeof(SubGhzRadioPreset));\n    instance->preset->name = furi_string_alloc();\n    subghz_txrx_set_preset(\n        instance, \"AM650\", subghz_setting_get_default_frequency(instance->setting), NULL, 0);\n\n    instance->txrx_state = SubGhzTxRxStateSleep;\n\n    subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);\n    subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);\n\n    instance->worker = subghz_worker_alloc();\n    instance->fff_data = flipper_format_string_alloc();\n\n    instance->environment = subghz_environment_alloc();\n    instance->is_database_loaded =\n        subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME);\n    subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);\n    subghz_environment_set_came_atomo_rainbow_table_file_name(\n        instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME);\n    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(\n        instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);\n    subghz_environment_set_nice_flor_s_rainbow_table_file_name(\n        instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);\n    subghz_environment_set_protocol_registry(\n        instance->environment, (void*)&subghz_protocol_registry);\n    instance->receiver = subghz_receiver_alloc_init(instance->environment);\n\n    subghz_worker_set_overrun_callback(\n        instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);\n    subghz_worker_set_pair_callback(\n        instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);\n    subghz_worker_set_context(instance->worker, instance->receiver);\n\n    //set default device External\n    subghz_devices_init();\n    instance->radio_device_type = SubGhzRadioDeviceTypeInternal;\n    instance->radio_device_type =\n        subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101);\n\n    return instance;\n}\n\nvoid subghz_txrx_free(SubGhzTxRx* instance) {\n    furi_assert(instance);\n\n    if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {\n        subghz_txrx_radio_device_power_off(instance);\n        subghz_devices_end(instance->radio_device);\n    }\n\n    subghz_devices_deinit();\n\n    subghz_worker_free(instance->worker);\n    subghz_receiver_free(instance->receiver);\n    subghz_environment_free(instance->environment);\n    flipper_format_free(instance->fff_data);\n    furi_string_free(instance->preset->name);\n    subghz_setting_free(instance->setting);\n\n    free(instance->preset);\n    free(instance);\n}\n\nbool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->is_database_loaded;\n}\n\nvoid subghz_txrx_set_preset(\n    SubGhzTxRx* instance,\n    const char* preset_name,\n    uint32_t frequency,\n    uint8_t* preset_data,\n    size_t preset_data_size) {\n    furi_assert(instance);\n    furi_string_set(instance->preset->name, preset_name);\n    SubGhzRadioPreset* preset = instance->preset;\n    preset->frequency = frequency;\n    preset->data = preset_data;\n    preset->data_size = preset_data_size;\n}\n\nconst char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {\n    UNUSED(instance);\n    const char* preset_name = \"\";\n    if(!strcmp(preset, \"FuriHalSubGhzPresetOok270Async\")) {\n        preset_name = \"AM270\";\n    } else if(!strcmp(preset, \"FuriHalSubGhzPresetOok650Async\")) {\n        preset_name = \"AM650\";\n    } else if(!strcmp(preset, \"FuriHalSubGhzPreset2FSKDev238Async\")) {\n        preset_name = \"FM238\";\n    } else if(!strcmp(preset, \"FuriHalSubGhzPreset2FSKDev476Async\")) {\n        preset_name = \"FM476\";\n    } else if(!strcmp(preset, \"FuriHalSubGhzPresetCustom\")) {\n        preset_name = \"CUSTOM\";\n    } else {\n        FURI_LOG_E(TAG, \"Unknown preset\");\n    }\n    return preset_name;\n}\n\nSubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return *instance->preset;\n}\n\nvoid subghz_txrx_get_frequency_and_modulation(\n    SubGhzTxRx* instance,\n    FuriString* frequency,\n    FuriString* modulation) {\n    furi_assert(instance);\n    SubGhzRadioPreset* preset = instance->preset;\n    if(frequency != NULL) {\n        furi_string_printf(\n            frequency,\n            \"%03ld.%02ld\",\n            preset->frequency / 1000000 % 1000,\n            preset->frequency / 10000 % 100);\n    }\n    if(modulation != NULL) {\n        furi_string_printf(modulation, \"%.2s\", furi_string_get_cstr(preset->name));\n    }\n}\n\nstatic void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {\n    furi_assert(instance);\n    subghz_devices_reset(instance->radio_device);\n    subghz_devices_idle(instance->radio_device);\n    subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data);\n    instance->txrx_state = SubGhzTxRxStateIDLE;\n}\n\nstatic uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {\n    furi_assert(instance);\n\n    furi_assert(\n        instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);\n\n    subghz_devices_idle(instance->radio_device);\n\n    uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency);\n    subghz_devices_flush_rx(instance->radio_device);\n    subghz_txrx_speaker_on(instance);\n\n    subghz_devices_start_async_rx(\n        instance->radio_device, subghz_worker_rx_callback, instance->worker);\n    subghz_worker_start(instance->worker);\n    instance->txrx_state = SubGhzTxRxStateRx;\n    return value;\n}\n\nstatic void subghz_txrx_idle(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->txrx_state != SubGhzTxRxStateSleep) {\n        subghz_devices_idle(instance->radio_device);\n        subghz_txrx_speaker_off(instance);\n        instance->txrx_state = SubGhzTxRxStateIDLE;\n    }\n}\n\nstatic void subghz_txrx_rx_end(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    furi_assert(instance->txrx_state == SubGhzTxRxStateRx);\n\n    if(subghz_worker_is_running(instance->worker)) {\n        subghz_worker_stop(instance->worker);\n        subghz_devices_stop_async_rx(instance->radio_device);\n    }\n    subghz_devices_idle(instance->radio_device);\n    subghz_txrx_speaker_off(instance);\n    instance->txrx_state = SubGhzTxRxStateIDLE;\n}\n\nvoid subghz_txrx_sleep(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    subghz_devices_sleep(instance->radio_device);\n    instance->txrx_state = SubGhzTxRxStateSleep;\n}\n\nstatic bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {\n    furi_assert(instance);\n    furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);\n    subghz_devices_idle(instance->radio_device);\n    subghz_devices_set_frequency(instance->radio_device, frequency);\n\n    bool ret = subghz_devices_set_tx(instance->radio_device);\n    if(ret) {\n        subghz_txrx_speaker_on(instance);\n        instance->txrx_state = SubGhzTxRxStateTx;\n    }\n\n    return ret;\n}\n\nSubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {\n    furi_assert(instance);\n    furi_assert(flipper_format);\n\n    subghz_txrx_stop(instance);\n\n    SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;\n    FuriString* temp_str = furi_string_alloc();\n    uint32_t repeat = 200;\n    do {\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n        if(!flipper_format_read_string(flipper_format, \"Protocol\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Protocol\");\n            break;\n        }\n        if(!flipper_format_insert_or_update_uint32(flipper_format, \"Repeat\", &repeat, 1)) {\n            FURI_LOG_E(TAG, \"Unable Repeat\");\n            break;\n        }\n        ret = SubGhzTxRxStartTxStateOk;\n\n        SubGhzRadioPreset* preset = instance->preset;\n        instance->transmitter =\n            subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));\n\n        if(instance->transmitter) {\n            if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==\n               SubGhzProtocolStatusOk) {\n                if(strcmp(furi_string_get_cstr(preset->name), \"\") != 0) {\n                    subghz_txrx_begin(\n                        instance,\n                        subghz_setting_get_preset_data_by_name(\n                            instance->setting, furi_string_get_cstr(preset->name)));\n                    if(preset->frequency) {\n                        if(!subghz_txrx_tx(instance, preset->frequency)) {\n                            FURI_LOG_E(TAG, \"Only Rx\");\n                            ret = SubGhzTxRxStartTxStateErrorOnlyRx;\n                        }\n                    } else {\n                        ret = SubGhzTxRxStartTxStateErrorParserOthers;\n                    }\n\n                } else {\n                    FURI_LOG_E(\n                        TAG, \"Unknown name preset \\\" %s \\\"\", furi_string_get_cstr(preset->name));\n                    ret = SubGhzTxRxStartTxStateErrorParserOthers;\n                }\n\n                if(ret == SubGhzTxRxStartTxStateOk) {\n                    //Start TX\n                    subghz_devices_start_async_tx(\n                        instance->radio_device, subghz_transmitter_yield, instance->transmitter);\n                }\n            } else {\n                ret = SubGhzTxRxStartTxStateErrorParserOthers;\n            }\n        } else {\n            ret = SubGhzTxRxStartTxStateErrorParserOthers;\n        }\n        if(ret != SubGhzTxRxStartTxStateOk) {\n            subghz_transmitter_free(instance->transmitter);\n            if(instance->txrx_state != SubGhzTxRxStateIDLE) {\n                subghz_txrx_idle(instance);\n            }\n        }\n\n    } while(false);\n    furi_string_free(temp_str);\n    return ret;\n}\n\nvoid subghz_txrx_rx_start(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    subghz_txrx_stop(instance);\n    subghz_txrx_begin(\n        instance,\n        subghz_setting_get_preset_data_by_name(\n            subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));\n    subghz_txrx_rx(instance, instance->preset->frequency);\n}\n\nvoid subghz_txrx_set_need_save_callback(\n    SubGhzTxRx* instance,\n    SubGhzTxRxNeedSaveCallback callback,\n    void* context) {\n    furi_assert(instance);\n    instance->need_save_callback = callback;\n    instance->need_save_context = context;\n}\n\nstatic void subghz_txrx_tx_stop(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    furi_assert(instance->txrx_state == SubGhzTxRxStateTx);\n    //Stop TX\n    subghz_devices_stop_async_tx(instance->radio_device);\n    subghz_transmitter_stop(instance->transmitter);\n    subghz_transmitter_free(instance->transmitter);\n\n    //if protocol dynamic then we save the last upload\n    if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {\n        if(instance->need_save_callback) {\n            instance->need_save_callback(instance->need_save_context);\n        }\n    }\n    subghz_txrx_idle(instance);\n    subghz_txrx_speaker_off(instance);\n}\n\nFlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->fff_data;\n}\n\nSubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->setting;\n}\n\nvoid subghz_txrx_stop(SubGhzTxRx* instance) {\n    furi_assert(instance);\n\n    switch(instance->txrx_state) {\n    case SubGhzTxRxStateTx:\n        subghz_txrx_tx_stop(instance);\n        subghz_txrx_speaker_unmute(instance);\n        break;\n    case SubGhzTxRxStateRx:\n        subghz_txrx_rx_end(instance);\n        subghz_txrx_speaker_mute(instance);\n        break;\n\n    default:\n        break;\n    }\n}\n\nvoid subghz_txrx_hopper_update(SubGhzTxRx* instance) {\n    furi_assert(instance);\n\n    switch(instance->hopper_state) {\n    case SubGhzHopperStateOFF:\n    case SubGhzHopperStatePause:\n        return;\n    case SubGhzHopperStateRSSITimeOut:\n        if(instance->hopper_timeout != 0) {\n            instance->hopper_timeout--;\n            return;\n        }\n        break;\n    default:\n        break;\n    }\n    float rssi = -127.0f;\n    if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {\n        // See RSSI Calculation timings in CC1101 17.3 RSSI\n        rssi = subghz_devices_get_rssi(instance->radio_device);\n\n        // Stay if RSSI is high enough\n        if(rssi > -90.0f) {\n            instance->hopper_timeout = 10;\n            instance->hopper_state = SubGhzHopperStateRSSITimeOut;\n            return;\n        }\n    } else {\n        instance->hopper_state = SubGhzHopperStateRunnig;\n    }\n    // Select next frequency\n    if(instance->hopper_idx_frequency <\n       subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {\n        instance->hopper_idx_frequency++;\n    } else {\n        instance->hopper_idx_frequency = 0;\n    }\n\n    if(instance->txrx_state == SubGhzTxRxStateRx) {\n        subghz_txrx_rx_end(instance);\n    };\n    if(instance->txrx_state == SubGhzTxRxStateIDLE) {\n        subghz_receiver_reset(instance->receiver);\n        instance->preset->frequency =\n            subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);\n        subghz_txrx_rx(instance, instance->preset->frequency);\n    }\n}\n\nSubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->hopper_state;\n}\n\nvoid subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {\n    furi_assert(instance);\n    instance->hopper_state = state;\n}\n\nvoid subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->hopper_state == SubGhzHopperStatePause) {\n        instance->hopper_state = SubGhzHopperStateRunnig;\n    }\n}\n\nvoid subghz_txrx_hopper_pause(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->hopper_state == SubGhzHopperStateRunnig) {\n        instance->hopper_state = SubGhzHopperStatePause;\n    }\n}\n\nvoid subghz_txrx_speaker_on(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->speaker_state == SubGhzSpeakerStateEnable) {\n        if(furi_hal_speaker_acquire(30)) {\n            subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);\n        } else {\n            instance->speaker_state = SubGhzSpeakerStateDisable;\n        }\n    }\n}\n\nvoid subghz_txrx_speaker_off(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->speaker_state != SubGhzSpeakerStateDisable) {\n        if(furi_hal_speaker_is_mine()) {\n            subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);\n            furi_hal_speaker_release();\n            if(instance->speaker_state == SubGhzSpeakerStateShutdown)\n                instance->speaker_state = SubGhzSpeakerStateDisable;\n        }\n    }\n}\n\nvoid subghz_txrx_speaker_mute(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->speaker_state == SubGhzSpeakerStateEnable) {\n        if(furi_hal_speaker_is_mine()) {\n            subghz_devices_set_async_mirror_pin(instance->radio_device, NULL);\n        }\n    }\n}\n\nvoid subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    if(instance->speaker_state == SubGhzSpeakerStateEnable) {\n        if(furi_hal_speaker_is_mine()) {\n            subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker);\n        }\n    }\n}\n\nvoid subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {\n    furi_assert(instance);\n    instance->speaker_state = state;\n}\n\nSubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->speaker_state;\n}\n\nbool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {\n    furi_assert(instance);\n    furi_assert(name_protocol);\n    bool res = false;\n    instance->decoder_result =\n        subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);\n    if(instance->decoder_result) {\n        res = true;\n    }\n    return res;\n}\n\nSubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->decoder_result;\n}\n\nbool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return (instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==\n           SubGhzProtocolFlag_Save;\n}\n\nbool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {\n    furi_assert(instance);\n    const SubGhzProtocol* protocol = instance->decoder_result->protocol;\n    if(check_type) {\n        return ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&\n               protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic;\n    }\n    return ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&\n           protocol->encoder->deserialize;\n}\n\nvoid subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {\n    furi_assert(instance);\n    subghz_receiver_set_filter(instance->receiver, filter);\n}\n\nvoid subghz_txrx_set_rx_calback(\n    SubGhzTxRx* instance,\n    SubGhzReceiverCallback callback,\n    void* context) {\n    subghz_receiver_set_rx_callback(instance->receiver, callback, context);\n}\n\nvoid subghz_txrx_set_raw_file_encoder_worker_callback_end(\n    SubGhzTxRx* instance,\n    SubGhzProtocolEncoderRAWCallbackEnd callback,\n    void* context) {\n    subghz_protocol_raw_file_encoder_worker_set_callback_end(\n        (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),\n        callback,\n        context);\n}\n\nbool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) {\n    furi_assert(instance);\n\n    bool is_connect = false;\n    bool is_otg_enabled = furi_hal_power_is_otg_enabled();\n\n    if(!is_otg_enabled) {\n        subghz_txrx_radio_device_power_on(instance);\n    }\n\n    const SubGhzDevice* device = subghz_devices_get_by_name(name);\n    if(device) {\n        is_connect = subghz_devices_is_connect(device);\n    }\n\n    if(!is_otg_enabled) {\n        subghz_txrx_radio_device_power_off(instance);\n    }\n    return is_connect;\n}\n\nSubGhzRadioDeviceType\n    subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) {\n    furi_assert(instance);\n\n    if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&\n       subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) {\n        subghz_txrx_radio_device_power_on(instance);\n        instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);\n        subghz_devices_begin(instance->radio_device);\n        instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101;\n    } else {\n        subghz_txrx_radio_device_power_off(instance);\n        if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) {\n            subghz_devices_end(instance->radio_device);\n        }\n        instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);\n        instance->radio_device_type = SubGhzRadioDeviceTypeInternal;\n    }\n\n    return instance->radio_device_type;\n}\n\nSubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return instance->radio_device_type;\n}\n\nfloat subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return subghz_devices_get_rssi(instance->radio_device);\n}\n\nconst char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) {\n    furi_assert(instance);\n    return subghz_devices_get_name(instance->radio_device);\n}\n\nbool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) {\n    furi_assert(instance);\n    return subghz_devices_is_frequency_valid(instance->radio_device, frequency);\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_txrx.h",
    "content": "#pragma once\n\n#include \"subghz_types.h\"\n\n#include <lib/subghz/subghz_worker.h>\n#include <lib/subghz/subghz_setting.h>\n#include <lib/subghz/receiver.h>\n#include <lib/subghz/transmitter.h>\n#include <lib/subghz/protocols/raw.h>\n#include <lib/subghz/devices/devices.h>\n\ntypedef struct SubGhzTxRx SubGhzTxRx;\n\ntypedef void (*SubGhzTxRxNeedSaveCallback)(void* context);\n\ntypedef enum {\n    SubGhzTxRxStartTxStateOk,\n    SubGhzTxRxStartTxStateErrorOnlyRx,\n    SubGhzTxRxStartTxStateErrorParserOthers,\n} SubGhzTxRxStartTxState;\n\n/**\n * Allocate SubGhzTxRx\n * \n * @return SubGhzTxRx* pointer to SubGhzTxRx\n */\nSubGhzTxRx* subghz_txrx_alloc(void);\n\n/**\n * Free SubGhzTxRx\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_free(SubGhzTxRx* instance);\n\n/**\n * Check if the database is loaded\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return bool True if the database is loaded\n */\nbool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);\n\n/**\n * Set preset \n * \n * @param instance Pointer to a SubGhzTxRx\n * @param preset_name Name of preset\n * @param frequency Frequency in Hz\n * @param preset_data Data of preset\n * @param preset_data_size Size of preset data\n */\nvoid subghz_txrx_set_preset(\n    SubGhzTxRx* instance,\n    const char* preset_name,\n    uint32_t frequency,\n    uint8_t* preset_data,\n    size_t preset_data_size);\n\n/**\n * Get name of preset\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param preset String of preset \n * @return const char*  Name of preset\n */\nconst char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);\n\n/**\n * Get of preset\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return SubGhzRadioPreset Preset\n */\nSubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);\n\n/**\n * Get string frequency and modulation\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param frequency Pointer to a string frequency\n * @param modulation Pointer to a string modulation\n */\nvoid subghz_txrx_get_frequency_and_modulation(\n    SubGhzTxRx* instance,\n    FuriString* frequency,\n    FuriString* modulation);\n\n/**\n * Start TX CC1101\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param flipper_format Pointer to a FlipperFormat\n * @return SubGhzTxRxStartTxState \n */\nSubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);\n\n/**\n * Start RX CC1101\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_rx_start(SubGhzTxRx* instance);\n\n/**\n * Stop TX/RX CC1101\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_stop(SubGhzTxRx* instance);\n\n/**\n * Set sleep mode CC1101\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_sleep(SubGhzTxRx* instance);\n\n/**\n * Update frequency CC1101 in automatic mode (hopper)\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_hopper_update(SubGhzTxRx* instance);\n\n/**\n * Get state hopper\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return SubGhzHopperState \n */\nSubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);\n\n/**\n * Set state hopper\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param state State hopper\n */\nvoid subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);\n\n/**\n * Unpause hopper\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_hopper_unpause(SubGhzTxRx* instance);\n\n/**\n * Set pause hopper\n * \n * @param instance Pointer to a SubGhzTxRx\n */\nvoid subghz_txrx_hopper_pause(SubGhzTxRx* instance);\n\n/**\n * Speaker on\n * \n * @param instance Pointer to a SubGhzTxRx \n */\nvoid subghz_txrx_speaker_on(SubGhzTxRx* instance);\n\n/**\n * Speaker off\n * \n * @param instance Pointer to a SubGhzTxRx \n */\nvoid subghz_txrx_speaker_off(SubGhzTxRx* instance);\n\n/**\n * Speaker mute\n * \n * @param instance Pointer to a SubGhzTxRx \n */\nvoid subghz_txrx_speaker_mute(SubGhzTxRx* instance);\n\n/**\n * Speaker unmute\n * \n * @param instance Pointer to a SubGhzTxRx \n */\nvoid subghz_txrx_speaker_unmute(SubGhzTxRx* instance);\n\n/**\n * Set state speaker\n * \n * @param instance Pointer to a SubGhzTxRx \n * @param state State speaker\n */\nvoid subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);\n\n/**\n * Get state speaker\n * \n * @param instance Pointer to a SubGhzTxRx \n * @return SubGhzSpeakerState \n */\nSubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);\n\n/**\n * load decoder by name protocol\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param name_protocol Name protocol\n * @return bool True if the decoder is loaded \n */\nbool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);\n\n/**\n * Get decoder\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase\n */\nSubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);\n\n/**\n * Set callback for save data\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param callback Callback for save data\n * @param context Context for callback\n */\nvoid subghz_txrx_set_need_save_callback(\n    SubGhzTxRx* instance,\n    SubGhzTxRxNeedSaveCallback callback,\n    void* context);\n\n/**\n * Get pointer to a load data key\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return FlipperFormat* \n */\nFlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);\n\n/**\n * Get pointer to a SugGhzSetting\n * \n * @param instance Pointer to a SubGhzTxRx\n * @return SubGhzSetting* \n */\nSubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);\n\n/**\n * Is it possible to save this protocol\n * \n * @param instance Pointer to a SubGhzTxRx \n * @return bool True if it is possible to save this protocol\n */\nbool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);\n\n/**\n * Is it possible to send this protocol\n * \n * @param instance Pointer to a SubGhzTxRx \n * @return bool True if it is possible to send this protocol\n */\nbool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);\n\n/**\n * Set filter, what types of decoder to use \n * \n * @param instance Pointer to a SubGhzTxRx\n * @param filter Filter\n */\nvoid subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);\n\n/**\n * Set callback for receive data\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param callback Callback for receive data\n * @param context Context for callback\n */\nvoid subghz_txrx_set_rx_calback(\n    SubGhzTxRx* instance,\n    SubGhzReceiverCallback callback,\n    void* context);\n\n/**\n * Set callback for Raw decoder, end of data transfer  \n * \n * @param instance Pointer to a SubGhzTxRx\n * @param callback Callback for Raw decoder, end of data transfer \n * @param context Context for callback\n */\nvoid subghz_txrx_set_raw_file_encoder_worker_callback_end(\n    SubGhzTxRx* instance,\n    SubGhzProtocolEncoderRAWCallbackEnd callback,\n    void* context);\n\n/* Checking if an external radio device is connected\n* \n* @param instance Pointer to a SubGhzTxRx\n* @param name Name of external radio device\n* @return bool True if is connected to the external radio device\n*/\nbool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name);\n\n/* Set the selected radio device to use\n*\n* @param instance Pointer to a SubGhzTxRx\n* @param radio_device_type Radio device type\n* @return SubGhzRadioDeviceType Type of installed radio device\n*/\nSubGhzRadioDeviceType\n    subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type);\n\n/* Get the selected radio device to use\n*\n* @param instance Pointer to a SubGhzTxRx\n* @return SubGhzRadioDeviceType Type of installed radio device\n*/\nSubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance);\n\n/* Get RSSI the selected radio device to use\n*\n* @param instance Pointer to a SubGhzTxRx\n* @return float RSSI\n*/\nfloat subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance);\n\n/* Get name the selected radio device to use\n*\n* @param instance Pointer to a SubGhzTxRx\n* @return const char* Name of installed radio device\n*/\nconst char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance);\n\n/* Get get intelligence whether frequency the selected radio device to use\n*\n* @param instance Pointer to a SubGhzTxRx\n* @return bool True if the frequency is valid\n*/\nbool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency);\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c",
    "content": "#include \"subghz_txrx_i.h\" // IWYU pragma: keep\n#include \"subghz_txrx_create_protocol_key.h\"\n#include <lib/subghz/transmitter.h>\n#include <lib/subghz/protocols/protocol_items.h>\n#include <lib/subghz/protocols/protocol_items.h>\n#include <lib/subghz/protocols/keeloq.h>\n#include <lib/subghz/protocols/secplus_v1.h>\n#include <lib/subghz/protocols/secplus_v2.h>\n\n#include <flipper_format/flipper_format_i.h>\n#include <lib/toolbox/stream/stream.h>\n#include <lib/subghz/protocols/raw.h>\n\n#define TAG \"SubGhzCreateProtocolKey\"\n\nSubGhzProtocolStatus subghz_txrx_gen_data_protocol(\n    void* context,\n    const char* preset_name,\n    uint32_t frequency,\n    const char* protocol_name,\n    uint64_t key,\n    uint32_t bit) {\n    furi_assert(context);\n    SubGhzTxRx* instance = context;\n\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusOk;\n\n    subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0);\n    instance->decoder_result =\n        subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name);\n\n    if(instance->decoder_result == NULL) {\n        FURI_LOG_E(TAG, \"Protocol not found!\");\n        ret = SubGhzProtocolStatusErrorProtocolNotFound;\n        return ret;\n    }\n\n    do {\n        Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data);\n        stream_clean(fff_data_stream);\n        ret = subghz_protocol_decoder_base_serialize(\n            instance->decoder_result, instance->fff_data, instance->preset);\n        if(ret != SubGhzProtocolStatusOk) {\n            FURI_LOG_E(TAG, \"Unable to serialize\");\n            break;\n        }\n        if(!flipper_format_update_uint32(instance->fff_data, \"Bit\", &bit, 1)) {\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            FURI_LOG_E(TAG, \"Unable to update Bit\");\n            break;\n        }\n\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(instance->fff_data, \"Key\", key_data, sizeof(uint64_t))) {\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            FURI_LOG_E(TAG, \"Unable to update Key\");\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nSubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te(\n    SubGhzTxRx* instance,\n    const char* preset_name,\n    uint32_t frequency,\n    const char* protocol_name,\n    uint64_t key,\n    uint32_t bit,\n    uint32_t te) {\n    furi_assert(instance);\n    SubGhzProtocolStatus ret =\n        subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit);\n    if(ret == SubGhzProtocolStatusOk) {\n        if(!flipper_format_update_uint32(instance->fff_data, \"TE\", (uint32_t*)&te, 1)) {\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            FURI_LOG_E(TAG, \"Unable to update Te\");\n        }\n    }\n    if(ret == SubGhzProtocolStatusOk) {\n        uint32_t guard_time = 30;\n        if(!flipper_format_update_uint32(\n               instance->fff_data, \"Guard_time\", (uint32_t*)&guard_time, 1)) {\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            FURI_LOG_E(TAG, \"Unable to update Guard_time\");\n        }\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency,\n    const char* name_sysmem,\n    uint32_t serial,\n    uint8_t btn,\n    uint16_t cnt) {\n    furi_assert(instance);\n\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    serial &= 0x0FFFFFFF;\n    instance->transmitter =\n        subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);\n    subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);\n    if(instance->transmitter) {\n        subghz_protocol_keeloq_create_data(\n            subghz_transmitter_get_protocol_instance(instance->transmitter),\n            instance->fff_data,\n            serial,\n            btn,\n            cnt,\n            name_sysmem,\n            instance->preset);\n        ret = SubGhzProtocolStatusOk;\n    }\n    subghz_transmitter_free(instance->transmitter);\n    return ret;\n}\n\nSubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency,\n    uint32_t serial,\n    uint8_t btn,\n    uint32_t cnt) {\n    furi_assert(instance);\n\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    instance->transmitter =\n        subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);\n    subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);\n\n    if(instance->transmitter) {\n        subghz_protocol_secplus_v2_create_data(\n            subghz_transmitter_get_protocol_instance(instance->transmitter),\n            instance->fff_data,\n            serial,\n            btn,\n            cnt,\n            instance->preset);\n        ret = SubGhzProtocolStatusOk;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency) {\n    furi_assert(instance);\n\n    uint32_t serial = (uint32_t)rand();\n    while(!subghz_protocol_secplus_v1_check_fixed(serial)) {\n        serial = (uint32_t)rand();\n    }\n\n    return subghz_txrx_gen_data_protocol(\n        instance,\n        name_preset,\n        frequency,\n        SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,\n        (uint64_t)serial << 32 | 0xE6000000,\n        42);\n}\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h",
    "content": "#pragma once\n#include \"subghz_types.h\"\n#include \"subghz_txrx.h\"\n\n/**\n * Generate data for protocol\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param preset_name Name of preset\n * @param frequency Frequency in Hz\n * @param protocol_name Name of protocol\n * @param key Key\n * @param bit Bit\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_txrx_gen_data_protocol(\n    void* context,\n    const char* preset_name,\n    uint32_t frequency,\n    const char* protocol_name,\n    uint64_t key,\n    uint32_t bit);\n\n/**\n * Generate data for protocol and te\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param preset_name Name of preset\n * @param frequency Frequency in Hz\n * @param protocol_name Name of protocol\n * @param key Key\n * @param bit Bit\n * @param te Te\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te(\n    SubGhzTxRx* instance,\n    const char* preset_name,\n    uint32_t frequency,\n    const char* protocol_name,\n    uint64_t key,\n    uint32_t bit,\n    uint32_t te);\n\n/**\n * Generate data Keeloq protocol\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param name_preset Name of preset\n * @param frequency Frequency in Hz\n * @param name_sysmem Name of Keeloq sysmem\n * @param serial Serial number\n * @param btn Button\n * @param cnt Counter\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency,\n    const char* name_sysmem,\n    uint32_t serial,\n    uint8_t btn,\n    uint16_t cnt);\n\n/**\n * Generate data SecPlus v2 protocol\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param name_preset Name of preset\n * @param frequency Frequency in Hz\n * @param serial Serial number\n * @param btn Button\n * @param cnt Counter\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency,\n    uint32_t serial,\n    uint8_t btn,\n    uint32_t cnt);\n\n/**\n * Generate data SecPlus v1 protocol\n * \n * @param instance Pointer to a SubGhzTxRx\n * @param name_preset Name of preset\n * @param frequency Frequency in Hz\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol(\n    SubGhzTxRx* instance,\n    const char* name_preset,\n    uint32_t frequency);\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_txrx_i.h",
    "content": "#pragma once\n\n#include \"subghz_txrx.h\"\n\nstruct SubGhzTxRx {\n    SubGhzWorker* worker;\n\n    SubGhzEnvironment* environment;\n    SubGhzReceiver* receiver;\n    SubGhzTransmitter* transmitter;\n    SubGhzProtocolDecoderBase* decoder_result;\n    FlipperFormat* fff_data;\n\n    SubGhzRadioPreset* preset;\n    SubGhzSetting* setting;\n\n    uint8_t hopper_timeout;\n    uint8_t hopper_idx_frequency;\n    bool is_database_loaded;\n    SubGhzHopperState hopper_state;\n\n    SubGhzTxRxState txrx_state;\n    SubGhzSpeakerState speaker_state;\n    const SubGhzDevice* radio_device;\n    SubGhzRadioDeviceType radio_device_type;\n\n    SubGhzTxRxNeedSaveCallback need_save_callback;\n    void* need_save_context;\n};\n"
  },
  {
    "path": "applications/main/subghz/helpers/subghz_types.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n/** SubGhzNotification state */\ntypedef enum {\n    SubGhzNotificationStateStarting,\n    SubGhzNotificationStateIDLE,\n    SubGhzNotificationStateTx,\n    SubGhzNotificationStateRx,\n    SubGhzNotificationStateRxDone,\n} SubGhzNotificationState;\n\n/** SubGhzTxRx state */\ntypedef enum {\n    SubGhzTxRxStateIDLE,\n    SubGhzTxRxStateRx,\n    SubGhzTxRxStateTx,\n    SubGhzTxRxStateSleep,\n} SubGhzTxRxState;\n\n/** SubGhzHopperState state */\ntypedef enum {\n    SubGhzHopperStateOFF,\n    SubGhzHopperStateRunnig,\n    SubGhzHopperStatePause,\n    SubGhzHopperStateRSSITimeOut,\n} SubGhzHopperState;\n\n/** SubGhzSpeakerState state */\ntypedef enum {\n    SubGhzSpeakerStateDisable,\n    SubGhzSpeakerStateShutdown,\n    SubGhzSpeakerStateEnable,\n} SubGhzSpeakerState;\n\n/** SubGhzRadioDeviceType */\ntypedef enum {\n    SubGhzRadioDeviceTypeAuto,\n    SubGhzRadioDeviceTypeInternal,\n    SubGhzRadioDeviceTypeExternalCC1101,\n} SubGhzRadioDeviceType;\n\n/** SubGhzRxKeyState state */\ntypedef enum {\n    SubGhzRxKeyStateIDLE,\n    SubGhzRxKeyStateNoSave,\n    SubGhzRxKeyStateNeedSave,\n    SubGhzRxKeyStateBack,\n    SubGhzRxKeyStateStart,\n    SubGhzRxKeyStateAddKey,\n    SubGhzRxKeyStateExit,\n    SubGhzRxKeyStateRAWLoad,\n    SubGhzRxKeyStateRAWMore,\n    SubGhzRxKeyStateRAWSave,\n} SubGhzRxKeyState;\n\n/** SubGhzLoadKeyState state */\ntypedef enum {\n    SubGhzLoadKeyStateUnknown,\n    SubGhzLoadKeyStateOK,\n    SubGhzLoadKeyStateParseErr,\n    SubGhzLoadKeyStateProtocolDescriptionErr,\n} SubGhzLoadKeyState;\n\n/** SubGhzLock */\ntypedef enum {\n    SubGhzLockOff,\n    SubGhzLockOn,\n} SubGhzLock;\n\ntypedef enum {\n    SubGhzViewIdMenu,\n    SubGhzViewIdReceiver,\n    SubGhzViewIdPopup,\n    SubGhzViewIdTextInput,\n    SubGhzViewIdWidget,\n    SubGhzViewIdTransmitter,\n    SubGhzViewIdVariableItemList,\n    SubGhzViewIdFrequencyAnalyzer,\n    SubGhzViewIdReadRAW,\n\n} SubGhzViewId;\n\n/** SubGhz load type file */\ntypedef enum {\n    SubGhzLoadTypeFileNoLoad,\n    SubGhzLoadTypeFileKey,\n    SubGhzLoadTypeFileRaw,\n} SubGhzLoadTypeFile;\n"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/alutech_at_4n",
    "content": "Filetype: Flipper SubGhz Keystore RAW File\nVersion: 0\nEncryption: 1\nIV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31\nEncrypt_data: RAW\nE811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/came_atomo",
    "content": "Filetype: Flipper SubGhz Keystore RAW File\nVersion: 0\nEncryption: 1\nIV: 47 69 6D 6D 65 20 74 68 65 63 6F 6F 6B 69 65 73\nEncrypt_data: RAW\nE0BDF15D68F29AE787E7FCEE6C3611C90A92305D677B8FFFBE225196F5DC04CAEAE1102B4AB830E76C9C14DBA7FA5BD1F30864ABAF51387222FDCC0BA87E4FF709812D5C59DD953859AFD698A0EA2ECEFA0DC49652861EF4CF1864843F135DB8680E0C5C9EEC3BC548E2EB696DC8CA1B0F627347D2C476B410CF03A0F4D1EFB36DFB0574FE8F48FB61910CA539EC04583CA170A51822225A1D3E86C07219FE92E5DE9F557A45E611D74028BAF56E7F2C53DBA8DC53DA89B642FAC0A9A0BAC1756BDA0833ACB09F56E0FA3080CBE9E6A04B235F3AC82BB955FBFD779C59725E6D1875E04E0E84ABD0C3C1C8FAB247EC2755ACEC9961244E0D79AE710E4C7D33E9"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/keeloq_mfcodes",
    "content": "Filetype: Flipper SubGhz Keystore File\nVersion: 0\nEncryption: 1\nIV: 48 69 69 69 2C 20 4D 69 73 68 61 21 21 30 31 21\n5914BA15A974B44BBF5886001E9CA8C05FED2B9A46D16F862484BC4760201A72\n885AB8E254F1403894740B50C6943A5F0A426C79CD8FA11D16915455E6B9ACE8\n48106C10911CC932FCAB49A793B6777DE845AA0D383E1B8D9FAE04E4F943FC60\nC1B81F527CF227F8A8D342BBB86EFBD95C99BFE3E3F55FFF19CF66C03CDB8779\n3DAB5A367DC19D412E9D71A89852E246DDEC0E97D7F7CE77DEE4043D9A99A1DEC4BDCA38B728CDC7BCB9E112044DCB8B\n8E718417DD703259B6EBBB799EC8ED428C6D65115023C1421C349DDB70359929\nDB110804D813EB3D2F04F5723305CFA13778D46DD261D246B05DB257535C9520\n135BEFEEBEBF17665C27951E43639A82AD5B15EFAD7D1D776ACF1070E5E81F9C\n996528B0D85E81D1AB47FECB2A5C2B63AFF35281EE41E316D26E34BB7E0E5EFE\nBA220B2E219DE8045F6C5C4D8B4CABFB9944252ACB82755907C44E09071A0F90\nC4509ACDC858711F42B96AA40B0FEC58566E71D91E1D167835B97D13A9C6B4FD63F62D911E17C15081ED1FFBA00E08D0\n499D1031FDC6B5D6109D4A9046599FEC77F1DB26FCA809235803CE5F670F2BF1622042AB04F768CB4BBB8E93595B229A\n43107EA4D34AC00BD05F45CF7AB077742C06A3619B3E258389EFF2D8ED057570\nA2ED253FB0CE66CDE2FF3921BE78662A97A23C344A83493D36CE6CC5ACC3A821\n963B48D299D6935B228A3BFCB86CF75F11A30850A19E014E31E6559F7A9A7DA8\n35D23E9D1D85AA0273A13B887EFBE9F55DD2E6C85B14F7E1100A66892F2A05BE\nF3134F0E2545623B439D581F2A42F6F4CE0512622B6E53937DFBE353C26DF2E7E69871412C0221D8CCCBB1948715CB3C\nC7E5A6E301B8E18BD3B15748A2A74AA1A939073FFE2F4A126213C49567D5BE44\n6E60F1421A27CEA2521C1EFE014EE2036F706ED734C12A794EBE3EFFACEA59C4\n9B60C596CE660E6A582AEC2C2804188EDD3C7636A3BC0E423A8D969F083319BB5AB90BE0DF3470BADB8D23E5AE0832EC\n001C3E6E3A36954427626FC4F2CC9DA39983C945F745EC9F25B11814D8EDB233AFFA0FD3C0B9B4D22EAFA951E734FBFD\nC03C3CC316F88E71C2A41B4B947FDF3FFCB46E103AFC420B820D757DCF7C46B6\n5430C52B29FDE8A436C745C1821BA6CDE9FB42C297C0407F1BC185B137485ABB\nF9EE48BC297FE3866E2BFBFEC341CFD57150088F88A06725ADFCAD891F9FF9F1\nE0BB992BDBFA5A2369A1DAEF6316F1779C16A33FE03F1A4A42C86B7FAA80A8B1\n747863CCE4A663B2C4C7533733CCD604C271C8BF65066E219C427C7208DCA9D5\n8C05EF25D017E06ED6D2818B4EAC50E56727FA84039FFE119837C2E5933EB3CD\n17C31C9451349E42F010F0E7540D71567485FC7D5E9CFCFEAB0E7AA3FEA8E3AE\n76D5BF46CE39C37F09549D281C5FDB5EC12CAA73152E3B295E439091365EEEF1\n0B7B9F563A09D953BC6DD141A1313B9CCB52CE5406ED727DE965CE920F0CA5DC\n78C9F207EBE8942DCF78A345A8E56B9C6C75D63C110250377D6C04DBB4DC15EA\n3518584CFE8238E0AFA1E6F4CDF1E1A2A6349A7D2D641404067BA482DA4C6345\nF3CD339419BFF39EFF2F2F0E7C06BB0537B238427345011436EF4F28D2FB4D02\n96A25E27313CC92A48B515143643D235C28DDCB53AECCFCFC7792A417FC78CCC\n11D553C6363F1C53B0074DF36C5256C9695CEA3006AC8737689694848C14FE93F15925BE58AB9D063D06FD87C0F7E637\n81BE95390E912466EAA1465BA7E316D662D7FDCE4DA91D79551247BCBE0FD66E\nD2DA1301B149C17E4E579266D0AD810E8043AC1F16781F7743FAFB61B0A74D14\nC4921F5E174C988212CB46362FABB6B99599537C4E5B983016463ACF11AFA9F9\nF1333F4B1706326B776A6701E4457A18A0E262EDEC1758D3A696741094EAE7C8\n6F3647542899B48A53A829FF3CE82B22A75AA9AB3D3E01BF33C6B3D1F0C9BF3D\nC42ACCF63F19E529E0D6A9AD9D2505F103D734248DAC0E9F2FCA35EC3F86C0A3\n1C0370E20EDBF342A76B1B65F0DB7C66F870ABB56E848159A0DB6585749F57B36EF4E18F730B5D0A2732C03ED74ECF72\nC61D01023FBDD674B09C6FFDB38A6F152F3B5C3203AFE4CA014C28600670922C\n6C5507A0AF35860EA2EAD029A6ED19AEFE2AD90B15B2EEFD65B1F14747C1EA66\n1C908E924A1BE763868ADB985DE4BB157CEF7BA96E58CED46BE08816E7D92239\nA3E883AC3AE22373815F2C02F6B808B0DD36FCE27BDF22C262FDBFC5D1B03153\n7CC9A80338315C477B55B4024324F2EAA56A770675F2229D32B44E304214059D\nD9CF90A9C4B60860355301A842323D8E207473D4A5631067E5DF2BFFD5A0E38E\n824D256362207A507DD40C13D765B1BA172C1E2AAA09DB164028CE094BFF33A8\n8F04BFBD27084AC2FEA04D18F88E98E3F4AE979B40343E2D55C20173EB5030B6\nD28B98D9030AC29D79B974FCDF176C19EF6998DB776D5A8B067F1F6908B951F7\nB7AA60298EB22D45A182A6E8BC76C7AD04334BE7D26746455C5CA44BC7D34B38\nC79E0C38724D4ED317A0507C3981DC4E3AFA84F039FF9595FDE4C4C81F3332A3\nF30A3803C59558A0FD41B15E45B133B901E0C66D898B10A2EF5189BC36EAE904\n3F2CFFEE683A76EB9FDD84E99CE0C0FCFFC1B5ECA9C8677CACA2F6E33A773823\nEDBC9BF637AFFB3F8BC0A90A4B8F56D452F64949E865855DC51882B6F0889E64\nC4234CE6419E399A6415EBC80FEF6049A3EF345D4D0692784BB58626241291A37C080BCEEFB71B135368E4E3790C93B3\nC1231A97B5642D908BD9524B482801A63F6AF3E25EF69F51C495EABF5C967238\n651FF3C3EB76F90CD8BC02A0884E4C830A2794F8CE2467CC2FA2E74D081D6567\n65CCA76B05D06AC1346B23BFE435864B648247075385B74AAE53C4006560854F\n2C19A88D52E56B9E09574F3B0B05A1AB3F417D47F78DCEDB9F6593EE01FD9540\nD46CF8835857552C4E77C8B37D79A6DE133ECEFD234CCD2278C78872EBAFFF2DAD0571FEFDA998031A235673CF6F2EA2\n"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/keeloq_mfcodes_user.example",
    "content": "# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user\n# for adding manufacture keys\n# AABBCCDDEEFFAABB:X:NAME\\r\\n\n# AABBCCDDEEFFAABB - man 64 bit\n# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning,  4 - Magic_xor_type1 Learning\n#   \t0 - iterates over both previous and man in direct and reverse byte sequence\n# NAME - name (string without spaces) max 64 characters long\nFiletype: Flipper SubGhz Keystore File\nVersion: 0\nEncryption: 0\nAABBCCDDEEFFAABB:1:Test1\nAABBCCDDEEFFAABB:1:Test2\n"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/nice_flor_s",
    "content": "Filetype: Flipper SubGhz Keystore RAW File\nVersion: 0\nEncryption: 1\nIV: 33 69 62 1D AD 20 1B B8 A1 6C CF 6F 6B 6F D5 18\nEncrypt_data: RAW\n9C1D6E6733912B28AC0FF1A191660810BDFF00D19BF7839AFF5B2AAFBBC91A38"
  },
  {
    "path": "applications/main/subghz/resources/subghz/assets/setting_user.example",
    "content": "# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user\nFiletype: Flipper SubGhz Setting File\nVersion: 1\n\n# Add Standard frequencies for your region\n#Add_standard_frequencies: true\n\n# Default Frequency: used as default for \"Read\" and \"Read Raw\"\n#Default_frequency: 433920000\n\n# Frequencies used for \"Read\", \"Read Raw\" and \"Frequency Analyzer\"\n#Frequency: 300000000\n#Frequency: 310000000\n#Frequency: 320000000\n\n# Frequencies used for hopping mode (keep this list small or flipper will miss signal)\n#Hopper_frequency: 300000000\n#Hopper_frequency: 310000000\n#Hopper_frequency: 310000000\n\n# Custom preset\n# format for CC1101 \"Custom_preset_data:\" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register\n\n#Custom_preset_name: AM_1\n#Custom_preset_module: CC1101\n#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n\n#Custom_preset_name: AM_2\n#Custom_preset_module: CC1101\n#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene.c",
    "content": "#include \"subghz_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const subghz_on_enter_handlers[])(void*) = {\n#include \"subghz_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const subghz_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"subghz_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const subghz_on_exit_handlers[])(void* context) = {\n#include \"subghz_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers subghz_scene_handlers = {\n    .on_enter_handlers = subghz_on_enter_handlers,\n    .on_event_handlers = subghz_on_event_handlers,\n    .on_exit_handlers = subghz_on_exit_handlers,\n    .scene_num = SubGhzSceneNum,\n};\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) SubGhzScene##id,\ntypedef enum {\n#include \"subghz_scene_config.h\"\n    SubGhzSceneNum,\n} SubGhzScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers subghz_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"subghz_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"subghz_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"subghz_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_config.h",
    "content": "ADD_SCENE(subghz, start, Start)\nADD_SCENE(subghz, receiver, Receiver)\nADD_SCENE(subghz, receiver_config, ReceiverConfig)\nADD_SCENE(subghz, receiver_info, ReceiverInfo)\nADD_SCENE(subghz, save_name, SaveName)\nADD_SCENE(subghz, save_success, SaveSuccess)\nADD_SCENE(subghz, saved, Saved)\nADD_SCENE(subghz, transmitter, Transmitter)\nADD_SCENE(subghz, show_error, ShowError)\nADD_SCENE(subghz, show_error_sub, ShowErrorSub)\nADD_SCENE(subghz, saved_menu, SavedMenu)\nADD_SCENE(subghz, delete, Delete)\nADD_SCENE(subghz, delete_success, DeleteSuccess)\nADD_SCENE(subghz, set_type, SetType)\nADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)\nADD_SCENE(subghz, read_raw, ReadRAW)\nADD_SCENE(subghz, more_raw, MoreRAW)\nADD_SCENE(subghz, delete_raw, DeleteRAW)\nADD_SCENE(subghz, need_saving, NeedSaving)\nADD_SCENE(subghz, rpc, Rpc)\nADD_SCENE(subghz, region_info, RegionInfo)\nADD_SCENE(subghz, radio_settings, RadioSettings)\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_delete.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneDelete);\n    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneDeleteBack);\n    }\n}\n\nvoid subghz_scene_delete_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    FuriString* frequency_str;\n    FuriString* modulation_str;\n    FuriString* text_out;\n    FuriString* text;\n    text_out = furi_string_alloc();\n    text = furi_string_alloc();\n\n    path_extract_filename(subghz->file_path, text, true);\n    furi_string_cat_printf(text_out, \"\\e#Delete %s?\\e#\\n\", furi_string_get_cstr(text));\n\n    furi_string_reset(text);\n    subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text);\n\n    size_t dot = furi_string_search_char(text, '\\r');\n    if(dot > 0) {\n        furi_string_left(text, dot);\n    }\n    furi_string_cat_printf(text_out, \"%s\\n\", furi_string_get_cstr(text));\n\n    furi_string_free(text);\n\n    frequency_str = furi_string_alloc();\n    modulation_str = furi_string_alloc();\n    subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n\n    furi_string_cat_printf(\n        text_out,\n        \"%s %s\",\n        furi_string_get_cstr(frequency_str),\n        furi_string_get_cstr(modulation_str));\n\n    widget_add_text_box_element(\n        subghz->widget, 0, 0, 128, 54, AlignCenter, AlignTop, furi_string_get_cstr(text_out), false);\n\n    furi_string_free(frequency_str);\n    furi_string_free(modulation_str);\n    furi_string_free(text_out);\n\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeRight, \"Delete\", subghz_scene_delete_callback, subghz);\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeLeft, \"Cancel\", subghz_scene_delete_callback, subghz);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n}\n\nbool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneDelete) {\n            furi_string_set(subghz->file_path_tmp, subghz->file_path);\n            if(subghz_delete_file(subghz)) {\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    subghz->scene_manager, SubGhzSceneStart);\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneDeleteBack) {\n            return scene_manager_previous_scene(subghz->scene_manager);\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_delete_on_exit(void* context) {\n    SubGhz* subghz = context;\n    widget_reset(subghz->widget);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_delete_raw.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_delete_raw_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneDeleteRAW);\n    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneDeleteRAWBack);\n    }\n}\n\nvoid subghz_scene_delete_raw_on_enter(void* context) {\n    SubGhz* subghz = context;\n    FuriString* frequency_str;\n    FuriString* modulation_str;\n    FuriString* text_out;\n    FuriString* file_name;\n    text_out = furi_string_alloc();\n    file_name = furi_string_alloc();\n\n    path_extract_filename(subghz->file_path, file_name, true);\n    furi_string_cat_printf(\n        text_out, \"\\e#Delete %s?\\e#\\nRAW signal\\n\", furi_string_get_cstr(file_name));\n    furi_string_free(file_name);\n\n    frequency_str = furi_string_alloc();\n    modulation_str = furi_string_alloc();\n    subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n\n    furi_string_cat_printf(\n        text_out,\n        \"%s %s\",\n        furi_string_get_cstr(frequency_str),\n        furi_string_get_cstr(modulation_str));\n\n    widget_add_text_box_element(\n        subghz->widget, 0, 0, 128, 54, AlignCenter, AlignTop, furi_string_get_cstr(text_out), false);\n\n    furi_string_free(frequency_str);\n    furi_string_free(modulation_str);\n    furi_string_free(text_out);\n\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeRight, \"Delete\", subghz_scene_delete_raw_callback, subghz);\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeLeft, \"Cancel\", subghz_scene_delete_raw_callback, subghz);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n}\n\nbool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneDeleteRAW) {\n            furi_string_set(subghz->file_path_tmp, subghz->file_path);\n            if(subghz_delete_file(subghz)) {\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    subghz->scene_manager, SubGhzSceneStart);\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneDeleteRAWBack) {\n            return scene_manager_previous_scene(subghz->scene_manager);\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_delete_raw_on_exit(void* context) {\n    SubGhz* subghz = context;\n    widget_reset(subghz->widget);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_delete_success.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_delete_success_popup_callback(void* context) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(\n        subghz->view_dispatcher, SubGhzCustomEventSceneDeleteSuccess);\n}\n\nvoid subghz_scene_delete_success_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    // Setup view\n    Popup* popup = subghz->popup;\n    popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(popup, \"Deleted\", 80, 19, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, subghz);\n    popup_set_callback(popup, subghz_scene_delete_success_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);\n}\n\nbool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneDeleteSuccess) {\n            if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateRAWLoad) {\n                if(scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneSaved)) {\n                } else {\n                    scene_manager_search_and_switch_to_previous_scene(\n                        subghz->scene_manager, SubGhzSceneStart);\n                }\n            } else {\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n\n                if(scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneReadRAW)) {\n                } else {\n                    scene_manager_search_and_switch_to_previous_scene(\n                        subghz->scene_manager, SubGhzSceneStart);\n                }\n            }\n\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_delete_success_on_exit(void* context) {\n    SubGhz* subghz = context;\n    Popup* popup = subghz->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n#include \"../views/subghz_frequency_analyzer.h\"\n\nvoid subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);\n}\n\nvoid subghz_scene_frequency_analyzer_on_enter(void* context) {\n    SubGhz* subghz = context;\n    subghz_frequency_analyzer_set_callback(\n        subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer);\n}\n\nbool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneAnalyzerLock) {\n            notification_message(subghz->notifications, &sequence_set_green_255);\n            notification_message(subghz->notifications, &sequence_single_vibro);\n            notification_message(subghz->notifications, &sequence_display_backlight_on);\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) {\n            notification_message(subghz->notifications, &sequence_reset_rgb);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_frequency_analyzer_on_exit(void* context) {\n    SubGhz* subghz = context;\n    notification_message(subghz->notifications, &sequence_reset_rgb);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_more_raw.c",
    "content": "#include \"../subghz_i.h\"\n\nenum SubmenuIndex {\n    SubmenuIndexEdit,\n    SubmenuIndexDelete,\n};\n\nvoid subghz_scene_more_raw_submenu_callback(void* context, uint32_t index) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);\n}\n\nvoid subghz_scene_more_raw_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    submenu_add_item(\n        subghz->submenu,\n        \"Rename\",\n        SubmenuIndexEdit,\n        subghz_scene_more_raw_submenu_callback,\n        subghz);\n\n    submenu_add_item(\n        subghz->submenu,\n        \"Delete\",\n        SubmenuIndexDelete,\n        subghz_scene_more_raw_submenu_callback,\n        subghz);\n\n    submenu_set_selected_item(\n        subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneMoreRAW));\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);\n}\n\nbool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexDelete) {\n            if(subghz_file_available(subghz)) {\n                scene_manager_set_scene_state(\n                    subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);\n                scene_manager_set_scene_state(\n                    subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW);\n                return true;\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneStart)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            }\n        } else if(event.event == SubmenuIndexEdit) {\n            if(subghz_file_available(subghz)) {\n                furi_string_reset(subghz->file_path_tmp);\n                scene_manager_set_scene_state(\n                    subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);\n                return true;\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneStart)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            }\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_more_raw_on_exit(void* context) {\n    SubGhz* subghz = context;\n    submenu_reset(subghz->submenu);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_need_saving.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_need_saving_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n\n    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneStay);\n    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);\n    }\n}\n\nvoid subghz_scene_need_saving_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    widget_add_text_box_element(\n        subghz->widget,\n        0,\n        0,\n        128,\n        54,\n        AlignCenter,\n        AlignTop,\n        \"\\e#Exit to Sub-GHz Menu?\\e#\\nAll unsaved data will be lost\",\n        false);\n\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeRight, \"Stay\", subghz_scene_need_saving_callback, subghz);\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeLeft, \"Exit\", subghz_scene_need_saving_callback, subghz);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n}\n\nbool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeBack) {\n        subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);\n        scene_manager_previous_scene(subghz->scene_manager);\n        return true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneStay) {\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);\n            scene_manager_previous_scene(subghz->scene_manager);\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneExit) {\n            SubGhzRxKeyState state = subghz_rx_key_state_get(subghz);\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n\n            if(state == SubGhzRxKeyStateExit) {\n                subghz_set_default_preset(subghz);\n                scene_manager_search_and_switch_to_previous_scene(\n                    subghz->scene_manager, SubGhzSceneStart);\n            } else {\n                scene_manager_previous_scene(subghz->scene_manager);\n            }\n\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_need_saving_on_exit(void* context) {\n    SubGhz* subghz = context;\n    widget_reset(subghz->widget);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_radio_setting.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n#include <lib/toolbox/value_index.h>\n#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>\n#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>\n\nenum SubGhzRadioSettingIndex {\n    SubGhzRadioSettingIndexDevice,\n};\n\n#define RADIO_DEVICE_COUNT 2\nconst char* const radio_device_text[RADIO_DEVICE_COUNT] = {\n    \"Internal\",\n    \"External\",\n};\n\nconst uint32_t radio_device_value[RADIO_DEVICE_COUNT] = {\n    SubGhzRadioDeviceTypeInternal,\n    SubGhzRadioDeviceTypeExternalCC1101,\n};\n\nconst char* const radio_device_name[RADIO_DEVICE_COUNT] = {\n    SUBGHZ_DEVICE_CC1101_INT_NAME,\n    SUBGHZ_DEVICE_CC1101_EXT_NAME,\n};\n\nstatic uint8_t subghz_scene_radio_settings_next_index_connect_ext_device(\n    SubGhz* subghz,\n    uint8_t current_index) {\n    uint8_t index = 0;\n    for(index = current_index; index < RADIO_DEVICE_COUNT; index++) {\n        if(subghz_txrx_radio_device_is_external_connected(subghz->txrx, radio_device_name[index])) {\n            break;\n        }\n    }\n    if(index == RADIO_DEVICE_COUNT) index = 0;\n    return index;\n}\n\nstatic void subghz_scene_radio_settings_set_device(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t current_index = variable_item_get_current_value_index(item);\n    uint8_t index =\n        subghz_scene_radio_settings_next_index_connect_ext_device(subghz, current_index);\n    variable_item_set_current_value_text(item, radio_device_text[index]);\n    subghz_txrx_radio_device_set(subghz->txrx, radio_device_value[index]);\n}\n\nvoid subghz_scene_radio_settings_on_enter(void* context) {\n    SubGhz* subghz = context;\n    VariableItem* item;\n    uint8_t value_index;\n\n    uint8_t value_count_device = RADIO_DEVICE_COUNT;\n\n    if(subghz_txrx_radio_device_get(subghz->txrx) == SubGhzRadioDeviceTypeInternal &&\n       !subghz_scene_radio_settings_next_index_connect_ext_device(subghz, 1))\n        value_count_device = 1;\n\n    item = variable_item_list_add(\n        subghz->variable_item_list,\n        \"Module\",\n        value_count_device,\n        subghz_scene_radio_settings_set_device,\n        subghz);\n    value_index = value_index_uint32(\n        subghz_txrx_radio_device_get(subghz->txrx), radio_device_value, value_count_device);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, radio_device_text[value_index]);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);\n}\n\nbool subghz_scene_radio_settings_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    bool consumed = false;\n    UNUSED(subghz);\n    UNUSED(event);\n\n    return consumed;\n}\n\nvoid subghz_scene_radio_settings_on_exit(void* context) {\n    SubGhz* subghz = context;\n    variable_item_list_set_selected_item(subghz->variable_item_list, 0);\n    variable_item_list_reset(subghz->variable_item_list);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_read_raw.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../views/subghz_read_raw.h\"\n#include <dolphin/dolphin.h>\n#include <lib/subghz/protocols/raw.h>\n#include <toolbox/path.h>\n\n#define TAG \"SubGhzSceneReadRaw\"\n\n#define RAW_FILE_NAME \"Raw_signal_\"\n\nbool subghz_scene_read_raw_update_filename(SubGhz* subghz) {\n    bool ret = false;\n    //set the path to read the file\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    do {\n        FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);\n        if(!flipper_format_rewind(fff_data)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n\n        if(!flipper_format_read_string(fff_data, \"File_name\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing File_name\");\n            break;\n        }\n\n        furi_string_set(subghz->file_path, temp_str);\n\n        ret = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    return ret;\n}\n\nstatic void subghz_scene_read_raw_update_statusbar(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n\n    FuriString* frequency_str = furi_string_alloc();\n    FuriString* modulation_str = furi_string_alloc();\n\n    subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n    subghz_read_raw_add_data_statusbar(\n        subghz->subghz_read_raw,\n        furi_string_get_cstr(frequency_str),\n        furi_string_get_cstr(modulation_str));\n\n    furi_string_free(frequency_str);\n    furi_string_free(modulation_str);\n\n    subghz_read_raw_set_radio_device_type(\n        subghz->subghz_read_raw, subghz_txrx_radio_device_get(subghz->txrx));\n}\n\nvoid subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);\n}\n\nvoid subghz_scene_read_raw_callback_end_tx(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(\n        subghz->view_dispatcher, SubGhzCustomEventViewReadRAWSendStop);\n}\n\nvoid subghz_scene_read_raw_on_enter(void* context) {\n    SubGhz* subghz = context;\n    FuriString* file_name = furi_string_alloc();\n\n    float threshold_rssi = subghz_threshold_rssi_get(subghz->threshold_rssi);\n    switch(subghz_rx_key_state_get(subghz)) {\n    case SubGhzRxKeyStateBack:\n        subghz_read_raw_set_status(\n            subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, \"\", threshold_rssi);\n        break;\n    case SubGhzRxKeyStateRAWLoad:\n    case SubGhzRxKeyStateRAWMore:\n        path_extract_filename(subghz->file_path, file_name, true);\n        subghz_read_raw_set_status(\n            subghz->subghz_read_raw,\n            SubGhzReadRAWStatusLoadKeyTX,\n            furi_string_get_cstr(file_name),\n            threshold_rssi);\n        break;\n    case SubGhzRxKeyStateRAWSave:\n        path_extract_filename(subghz->file_path, file_name, true);\n        subghz_read_raw_set_status(\n            subghz->subghz_read_raw,\n            SubGhzReadRAWStatusSaveKey,\n            furi_string_get_cstr(file_name),\n            threshold_rssi);\n        break;\n    default:\n        subghz_read_raw_set_status(\n            subghz->subghz_read_raw, SubGhzReadRAWStatusStart, \"\", threshold_rssi);\n        break;\n    }\n\n    if((subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) &&\n       (subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad)) {\n        subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n    }\n    furi_string_free(file_name);\n    subghz_scene_read_raw_update_statusbar(subghz);\n\n    //set callback view raw\n    subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz);\n\n    furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME));\n\n    //set filter RAW feed\n    subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);\n}\n\nbool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    bool consumed = false;\n    SubGhzProtocolDecoderRAW* decoder_raw =\n        (SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx);\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case SubGhzCustomEventViewReadRAWBack:\n\n            subghz_txrx_stop(subghz->txrx);\n            //Stop save file\n            subghz_protocol_raw_save_to_file_stop(decoder_raw);\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            //needed save?\n            if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) ||\n               (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) {\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);\n            } else {\n                //Restore default setting\n                subghz_set_default_preset(subghz);\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneSaved)) {\n                    if(!scene_manager_search_and_switch_to_previous_scene(\n                           subghz->scene_manager, SubGhzSceneStart)) {\n                        scene_manager_stop(subghz->scene_manager);\n                        view_dispatcher_stop(subghz->view_dispatcher);\n                    }\n                }\n            }\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWTXRXStop:\n            subghz_txrx_stop(subghz->txrx);\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWConfig:\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWErase:\n            if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {\n                if(subghz_scene_read_raw_update_filename(subghz)) {\n                    furi_string_set(subghz->file_path_tmp, subghz->file_path);\n                    subghz_delete_file(subghz);\n                }\n            }\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n            notification_message(subghz->notifications, &sequence_reset_rgb);\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWMore:\n            if(subghz_file_available(subghz)) {\n                if(subghz_scene_read_raw_update_filename(subghz)) {\n                    scene_manager_set_scene_state(\n                        subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);\n                    if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad) {\n                        subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWMore);\n                    }\n                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);\n                    consumed = true;\n                } else {\n                    furi_crash(\"SubGhz: RAW file name update error.\");\n                }\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneStart)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            }\n            break;\n\n        case SubGhzCustomEventViewReadRAWSendStart:\n\n            if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {\n                //start send\n                subghz->state_notifications = SubGhzNotificationStateIDLE;\n                if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {\n                    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);\n                    subghz_read_raw_set_status(\n                        subghz->subghz_read_raw,\n                        SubGhzReadRAWStatusIDLE,\n                        \"\",\n                        subghz_threshold_rssi_get(subghz->threshold_rssi));\n                } else {\n                    if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) ||\n                       !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) {\n                        dolphin_deed(DolphinDeedSubGhzSend);\n                    }\n                    // set callback end tx\n                    subghz_txrx_set_raw_file_encoder_worker_callback_end(\n                        subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz);\n                    subghz->state_notifications = SubGhzNotificationStateTx;\n                }\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneStart)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            }\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWSendStop:\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz_txrx_stop(subghz->txrx);\n            subghz_read_raw_stop_send(subghz->subghz_read_raw);\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWIDLE:\n            subghz_txrx_stop(subghz->txrx);\n            size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw);\n\n            subghz_protocol_raw_save_to_file_stop(decoder_raw);\n\n            FuriString* temp_str = furi_string_alloc();\n            furi_string_printf(\n                temp_str,\n                \"%s/%s%s\",\n                SUBGHZ_RAW_FOLDER,\n                RAW_FILE_NAME,\n                SUBGHZ_APP_FILENAME_EXTENSION);\n            subghz_protocol_raw_gen_fff_data(\n                subghz_txrx_get_fff_data(subghz->txrx),\n                furi_string_get_cstr(temp_str),\n                subghz_txrx_radio_device_get_name(subghz->txrx));\n            furi_string_free(temp_str);\n\n            if(spl_count > 0) {\n                notification_message(subghz->notifications, &sequence_set_green_255);\n            } else {\n                notification_message(subghz->notifications, &sequence_reset_rgb);\n            }\n\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);\n\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWREC:\n            if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateIDLE) {\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);\n            } else {\n                SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n                if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) {\n                    dolphin_deed(DolphinDeedSubGhzRawRec);\n                    subghz_txrx_rx_start(subghz->txrx);\n                    subghz->state_notifications = SubGhzNotificationStateRx;\n                    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);\n                } else {\n                    furi_string_set(subghz->error_str, \"Function requires\\nan SD card.\");\n                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);\n                }\n            }\n            consumed = true;\n            break;\n\n        case SubGhzCustomEventViewReadRAWSave:\n            if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {\n                scene_manager_set_scene_state(\n                    subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW);\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);\n            } else {\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneStart)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            }\n            consumed = true;\n            break;\n\n        default:\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeTick) {\n        switch(subghz->state_notifications) {\n        case SubGhzNotificationStateRx:\n            notification_message(subghz->notifications, &sequence_blink_cyan_10);\n\n            subghz_read_raw_update_sample_write(\n                subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw));\n\n            SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(\n                subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx));\n            subghz_read_raw_add_data_rssi(\n                subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above);\n            subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above);\n            break;\n        case SubGhzNotificationStateTx:\n            notification_message(subghz->notifications, &sequence_blink_magenta_10);\n            subghz_read_raw_update_sin(subghz->subghz_read_raw);\n            break;\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid subghz_scene_read_raw_on_exit(void* context) {\n    SubGhz* subghz = context;\n\n    //Stop CC1101\n    subghz_txrx_stop(subghz->txrx);\n    subghz->state_notifications = SubGhzNotificationStateIDLE;\n    notification_message(subghz->notifications, &sequence_reset_rgb);\n\n    //filter restoration\n    subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_receiver.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../views/receiver.h\"\n#include <dolphin/dolphin.h>\n#include <lib/subghz/protocols/bin_raw.h>\n\nstatic const NotificationSequence subghs_sequence_rx = {\n    &message_green_255,\n\n    &message_vibro_on,\n    &message_note_c6,\n    &message_delay_50,\n    &message_sound_off,\n    &message_vibro_off,\n\n    &message_delay_50,\n    NULL,\n};\n\nstatic const NotificationSequence subghs_sequence_rx_locked = {\n    &message_green_255,\n\n    &message_display_backlight_on,\n\n    &message_vibro_on,\n    &message_note_c6,\n    &message_delay_50,\n    &message_sound_off,\n    &message_vibro_off,\n\n    &message_delay_500,\n\n    &message_display_backlight_off,\n    NULL,\n};\n\nstatic void subghz_scene_receiver_update_statusbar(void* context) {\n    SubGhz* subghz = context;\n    FuriString* history_stat_str = furi_string_alloc();\n    if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) {\n        FuriString* frequency_str = furi_string_alloc();\n        FuriString* modulation_str = furi_string_alloc();\n\n        subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n\n        subghz_view_receiver_add_data_statusbar(\n            subghz->subghz_receiver,\n            furi_string_get_cstr(frequency_str),\n            furi_string_get_cstr(modulation_str),\n            furi_string_get_cstr(history_stat_str));\n\n        furi_string_free(frequency_str);\n        furi_string_free(modulation_str);\n    } else {\n        subghz_view_receiver_add_data_statusbar(\n            subghz->subghz_receiver, furi_string_get_cstr(history_stat_str), \"\", \"\");\n        subghz->state_notifications = SubGhzNotificationStateIDLE;\n    }\n    furi_string_free(history_stat_str);\n\n    subghz_view_receiver_set_radio_device_type(\n        subghz->subghz_receiver, subghz_txrx_radio_device_get(subghz->txrx));\n}\n\nvoid subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);\n}\n\nstatic void subghz_scene_add_to_history_callback(\n    SubGhzReceiver* receiver,\n    SubGhzProtocolDecoderBase* decoder_base,\n    void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    SubGhzHistory* history = subghz->history;\n    FuriString* str_buff = furi_string_alloc();\n\n    SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n\n    if(subghz_history_add_to_history(history, decoder_base, &preset)) {\n        furi_string_reset(str_buff);\n\n        subghz->state_notifications = SubGhzNotificationStateRxDone;\n        uint16_t item_history = subghz_history_get_item(history);\n        subghz_history_get_text_item_menu(history, str_buff, item_history - 1);\n        subghz_view_receiver_add_item_to_menu(\n            subghz->subghz_receiver,\n            furi_string_get_cstr(str_buff),\n            subghz_history_get_type_protocol(history, item_history - 1));\n\n        subghz_scene_receiver_update_statusbar(subghz);\n    }\n    subghz_receiver_reset(receiver);\n    furi_string_free(str_buff);\n    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);\n}\n\nvoid subghz_scene_receiver_on_enter(void* context) {\n    SubGhz* subghz = context;\n    SubGhzHistory* history = subghz->history;\n\n    FuriString* str_buff;\n    str_buff = furi_string_alloc();\n\n    if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) {\n        subghz_set_default_preset(subghz);\n        subghz_history_reset(history);\n        subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart);\n    }\n\n    subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));\n\n    //Load history to receiver\n    subghz_view_receiver_exit(subghz->subghz_receiver);\n    for(uint8_t i = 0; i < subghz_history_get_item(history); i++) {\n        furi_string_reset(str_buff);\n        subghz_history_get_text_item_menu(history, str_buff, i);\n        subghz_view_receiver_add_item_to_menu(\n            subghz->subghz_receiver,\n            furi_string_get_cstr(str_buff),\n            subghz_history_get_type_protocol(history, i));\n        subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);\n    }\n    furi_string_free(str_buff);\n\n    subghz_view_receiver_set_callback(\n        subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);\n    subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);\n\n    subghz->state_notifications = SubGhzNotificationStateRx;\n    subghz_txrx_rx_start(subghz->txrx);\n    subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);\n\n    //to use a universal decoder, we are looking for a link to it\n    furi_check(\n        subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME));\n\n    subghz_scene_receiver_update_statusbar(subghz);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);\n}\n\nbool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    bool consumed = false;\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case SubGhzCustomEventViewReceiverBack:\n            // Stop CC1101 Rx\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz_txrx_stop(subghz->txrx);\n            subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);\n            subghz->idx_menu_chosen = 0;\n            subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);\n\n            if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);\n            } else {\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n                subghz_set_default_preset(subghz);\n                scene_manager_search_and_switch_to_previous_scene(\n                    subghz->scene_manager, SubGhzSceneStart);\n            }\n            consumed = true;\n            break;\n        case SubGhzCustomEventViewReceiverOK:\n            subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);\n            dolphin_deed(DolphinDeedSubGhzReceiverInfo);\n            consumed = true;\n            break;\n        case SubGhzCustomEventViewReceiverConfig:\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);\n            consumed = true;\n            break;\n        case SubGhzCustomEventViewReceiverOffDisplay:\n            notification_message(subghz->notifications, &sequence_display_backlight_off);\n            consumed = true;\n            break;\n        case SubGhzCustomEventViewReceiverUnlock:\n            subghz_unlock(subghz);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeTick) {\n        if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {\n            subghz_txrx_hopper_update(subghz->txrx);\n            subghz_scene_receiver_update_statusbar(subghz);\n        }\n\n        SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(\n            subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx));\n\n        subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi);\n        subghz_protocol_decoder_bin_raw_data_input_rssi(\n            (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi);\n\n        switch(subghz->state_notifications) {\n        case SubGhzNotificationStateRx:\n            notification_message(subghz->notifications, &sequence_blink_cyan_10);\n            break;\n        case SubGhzNotificationStateRxDone:\n            if(!subghz_is_locked(subghz)) {\n                notification_message(subghz->notifications, &subghs_sequence_rx);\n            } else {\n                notification_message(subghz->notifications, &subghs_sequence_rx_locked);\n            }\n            subghz->state_notifications = SubGhzNotificationStateRx;\n            break;\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid subghz_scene_receiver_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_receiver_config.c",
    "content": "#include \"../subghz_i.h\"\n#include <lib/toolbox/value_index.h>\n\nenum SubGhzSettingIndex {\n    SubGhzSettingIndexFrequency,\n    SubGhzSettingIndexHopping,\n    SubGhzSettingIndexModulation,\n    SubGhzSettingIndexBinRAW,\n    SubGhzSettingIndexSound,\n    SubGhzSettingIndexLock,\n    SubGhzSettingIndexRAWThesholdRSSI,\n};\n\n#define RAW_THRESHOLD_RSSI_COUNT 11\nconst char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {\n    \"-----\",\n    \"-85.0\",\n    \"-80.0\",\n    \"-75.0\",\n    \"-70.0\",\n    \"-65.0\",\n    \"-60.0\",\n    \"-55.0\",\n    \"-50.0\",\n    \"-45.0\",\n    \"-40.0\",\n\n};\nconst float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {\n    -90.0f,\n    -85.0f,\n    -80.0f,\n    -75.0f,\n    -70.0f,\n    -65.0f,\n    -60.0f,\n    -55.0f,\n    -50.0f,\n    -45.0f,\n    -40.0f,\n};\n\n#define HOPPING_COUNT 2\nconst char* const hopping_text[HOPPING_COUNT] = {\n    \"OFF\",\n    \"ON\",\n};\nconst uint32_t hopping_value[HOPPING_COUNT] = {\n    SubGhzHopperStateOFF,\n    SubGhzHopperStateRunnig,\n};\n\n#define SPEAKER_COUNT 2\nconst char* const speaker_text[SPEAKER_COUNT] = {\n    \"OFF\",\n    \"ON\",\n};\nconst uint32_t speaker_value[SPEAKER_COUNT] = {\n    SubGhzSpeakerStateShutdown,\n    SubGhzSpeakerStateEnable,\n};\n#define BIN_RAW_COUNT 2\nconst char* const bin_raw_text[BIN_RAW_COUNT] = {\n    \"OFF\",\n    \"ON\",\n};\nconst uint32_t bin_raw_value[BIN_RAW_COUNT] = {\n    SubGhzProtocolFlag_Decodable,\n    SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,\n};\n\nuint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n\n    uint8_t index = 0;\n    for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) {\n        if(value == subghz_setting_get_frequency(setting, i)) {\n            index = i;\n            break;\n        } else {\n            index = subghz_setting_get_frequency_default_index(setting);\n        }\n    }\n    return index;\n}\n\nuint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n\n    uint8_t index = 0;\n    for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) {\n        if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) {\n            index = i;\n            break;\n        } else {\n            //  index = subghz_setting_get_frequency_default_index(subghz_txrx_get_setting(subghz->txrx));\n        }\n    }\n    return index;\n}\n\nuint8_t subghz_scene_receiver_config_hopper_value_index(\n    const uint32_t value,\n    const uint32_t values[],\n    uint8_t values_count,\n    void* context) {\n    furi_assert(context);\n    UNUSED(values_count);\n    SubGhz* subghz = context;\n\n    if(value == values[0]) {\n        return 0;\n    } else {\n        variable_item_set_current_value_text(\n            (VariableItem*)scene_manager_get_scene_state(\n                subghz->scene_manager, SubGhzSceneReceiverConfig),\n            \" -----\");\n        return 1;\n    }\n}\n\nstatic void subghz_scene_receiver_config_set_frequency(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n\n    if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) {\n        char text_buf[10] = {0};\n        uint32_t frequency = subghz_setting_get_frequency(setting, index);\n        SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n\n        snprintf(\n            text_buf,\n            sizeof(text_buf),\n            \"%lu.%02lu\",\n            frequency / 1000000,\n            (frequency % 1000000) / 10000);\n        variable_item_set_current_value_text(item, text_buf);\n        subghz_txrx_set_preset(\n            subghz->txrx,\n            furi_string_get_cstr(preset.name),\n            frequency,\n            preset.data,\n            preset.data_size);\n    } else {\n        variable_item_set_current_value_index(\n            item, subghz_setting_get_frequency_default_index(setting));\n    }\n}\n\nstatic void subghz_scene_receiver_config_set_preset(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n\n    variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index));\n\n    SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n    subghz_txrx_set_preset(\n        subghz->txrx,\n        subghz_setting_get_preset_name(setting, index),\n        preset.frequency,\n        subghz_setting_get_preset_data(setting, index),\n        subghz_setting_get_preset_data_size(setting, index));\n}\n\nstatic void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n    VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state(\n        subghz->scene_manager, SubGhzSceneReceiverConfig);\n\n    variable_item_set_current_value_text(item, hopping_text[index]);\n    if(hopping_value[index] == SubGhzHopperStateOFF) {\n        char text_buf[10] = {0};\n        uint32_t frequency = subghz_setting_get_default_frequency(setting);\n        SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n\n        snprintf(\n            text_buf,\n            sizeof(text_buf),\n            \"%lu.%02lu\",\n            frequency / 1000000,\n            (frequency % 1000000) / 10000);\n        variable_item_set_current_value_text(frequency_item, text_buf);\n\n        subghz_txrx_set_preset(\n            subghz->txrx,\n            furi_string_get_cstr(preset.name),\n            frequency,\n            preset.data,\n            preset.data_size);\n        variable_item_set_current_value_index(\n            frequency_item, subghz_setting_get_frequency_default_index(setting));\n    } else {\n        variable_item_set_current_value_text(frequency_item, \" -----\");\n        variable_item_set_current_value_index(\n            frequency_item, subghz_setting_get_frequency_default_index(setting));\n    }\n\n    subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]);\n}\n\nstatic void subghz_scene_receiver_config_set_speaker(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, speaker_text[index]);\n    subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]);\n}\n\nstatic void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, bin_raw_text[index]);\n    subghz->filter = bin_raw_value[index];\n    subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);\n}\n\nstatic void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {\n    SubGhz* subghz = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]);\n    subghz_threshold_rssi_set(subghz->threshold_rssi, raw_theshold_rssi_value[index]);\n}\n\nstatic void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    if(index == SubGhzSettingIndexLock) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock);\n    }\n}\n\nvoid subghz_scene_receiver_config_on_enter(void* context) {\n    SubGhz* subghz = context;\n    VariableItem* item;\n    uint8_t value_index;\n    SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n    SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);\n\n    item = variable_item_list_add(\n        subghz->variable_item_list,\n        \"Frequency:\",\n        subghz_setting_get_frequency_count(setting),\n        subghz_scene_receiver_config_set_frequency,\n        subghz);\n    value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz);\n    scene_manager_set_scene_state(\n        subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);\n    variable_item_set_current_value_index(item, value_index);\n    char text_buf[10] = {0};\n    uint32_t frequency = subghz_setting_get_frequency(setting, value_index);\n    snprintf(\n        text_buf,\n        sizeof(text_buf),\n        \"%lu.%02lu\",\n        frequency / 1000000,\n        (frequency % 1000000) / 10000);\n    variable_item_set_current_value_text(item, text_buf);\n\n    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n       SubGhzCustomEventManagerSet) {\n        item = variable_item_list_add(\n            subghz->variable_item_list,\n            \"Hopping:\",\n            HOPPING_COUNT,\n            subghz_scene_receiver_config_set_hopping_running,\n            subghz);\n        value_index = subghz_scene_receiver_config_hopper_value_index(\n            subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz);\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, hopping_text[value_index]);\n    }\n\n    item = variable_item_list_add(\n        subghz->variable_item_list,\n        \"Modulation:\",\n        subghz_setting_get_preset_count(setting),\n        subghz_scene_receiver_config_set_preset,\n        subghz);\n    value_index =\n        subghz_scene_receiver_config_next_preset(furi_string_get_cstr(preset.name), subghz);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(\n        item, subghz_setting_get_preset_name(setting, value_index));\n\n    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n       SubGhzCustomEventManagerSet) {\n        item = variable_item_list_add(\n            subghz->variable_item_list,\n            \"Bin_Raw:\",\n            BIN_RAW_COUNT,\n            subghz_scene_receiver_config_set_bin_raw,\n            subghz);\n        value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT);\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, bin_raw_text[value_index]);\n    }\n\n    item = variable_item_list_add(\n        subghz->variable_item_list,\n        \"Sound:\",\n        SPEAKER_COUNT,\n        subghz_scene_receiver_config_set_speaker,\n        subghz);\n    value_index = value_index_uint32(\n        subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, speaker_text[value_index]);\n\n    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n       SubGhzCustomEventManagerSet) {\n        variable_item_list_add(subghz->variable_item_list, \"Lock Keyboard\", 1, NULL, NULL);\n        variable_item_list_set_enter_callback(\n            subghz->variable_item_list,\n            subghz_scene_receiver_config_var_list_enter_callback,\n            subghz);\n    }\n    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==\n       SubGhzCustomEventManagerSet) {\n        item = variable_item_list_add(\n            subghz->variable_item_list,\n            \"RSSI Threshold:\",\n            RAW_THRESHOLD_RSSI_COUNT,\n            subghz_scene_receiver_config_set_raw_threshold_rssi,\n            subghz);\n        value_index = value_index_float(\n            subghz_threshold_rssi_get(subghz->threshold_rssi),\n            raw_theshold_rssi_value,\n            RAW_THRESHOLD_RSSI_COUNT);\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);\n    }\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);\n}\n\nbool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneSettingLock) {\n            subghz_lock(subghz);\n            scene_manager_previous_scene(subghz->scene_manager);\n            consumed = true;\n        }\n    }\n    return consumed;\n}\n\nvoid subghz_scene_receiver_config_on_exit(void* context) {\n    SubGhz* subghz = context;\n    variable_item_list_set_selected_item(subghz->variable_item_list, 0);\n    variable_item_list_reset(subghz->variable_item_list);\n    scene_manager_set_scene_state(\n        subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_receiver_info.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_custom_event.h\"\n#include \"../views/transmitter.h\"\n\nvoid subghz_scene_receiver_info_callback(SubGhzCustomEvent event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);\n}\n\nstatic bool subghz_scene_receiver_info_update_parser(void* context) {\n    SubGhz* subghz = context;\n\n    if(subghz_txrx_load_decoder_by_name_protocol(\n           subghz->txrx,\n           subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {\n        // we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal\n        subghz_protocol_decoder_base_deserialize(\n            subghz_txrx_get_decoder(subghz->txrx),\n            subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));\n\n        SubGhzRadioPreset* preset =\n            subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);\n        subghz_txrx_set_preset(\n            subghz->txrx,\n            furi_string_get_cstr(preset->name),\n            preset->frequency,\n            preset->data,\n            preset->data_size);\n\n        FuriString* key_str = furi_string_alloc();\n        FuriString* frequency_str = furi_string_alloc();\n        FuriString* modulation_str = furi_string_alloc();\n\n        subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), key_str);\n        subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n        subghz_view_transmitter_add_data_to_show(\n            subghz->subghz_transmitter,\n            furi_string_get_cstr(key_str),\n            furi_string_get_cstr(frequency_str),\n            furi_string_get_cstr(modulation_str),\n            subghz_txrx_protocol_is_transmittable(subghz->txrx, true));\n\n        furi_string_free(frequency_str);\n        furi_string_free(modulation_str);\n        furi_string_free(key_str);\n\n        subghz_view_transmitter_set_radio_device_type(\n            subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx));\n        subghz_view_transmitter_set_model_type(\n            subghz->subghz_transmitter, SubGhzViewTransmitterModelTypeInfo);\n\n        return true;\n    }\n    return false;\n}\n\nvoid subghz_scene_receiver_info_on_enter(void* context) {\n    SubGhz* subghz = context;\n    if(subghz_scene_receiver_info_update_parser(subghz)) {\n    } else {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneShowErrorSub);\n    }\n\n    subghz_view_transmitter_set_callback(\n        subghz->subghz_transmitter, subghz_scene_receiver_info_callback, subghz);\n\n    subghz->state_notifications = SubGhzNotificationStateIDLE;\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter);\n}\n\nbool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventViewTransmitterSendStart) {\n            if(!subghz_scene_receiver_info_update_parser(subghz)) {\n                return false;\n            }\n            //CC1101 Stop RX -> Start TX\n            subghz_txrx_hopper_pause(subghz->txrx);\n            if(!subghz_tx_start(\n                   subghz,\n                   subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {\n                subghz_txrx_rx_start(subghz->txrx);\n                subghz_txrx_hopper_unpause(subghz->txrx);\n                subghz->state_notifications = SubGhzNotificationStateRx;\n            } else {\n                subghz->state_notifications = SubGhzNotificationStateTx;\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {\n            //CC1101 Stop Tx -> Start RX\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n\n            subghz_txrx_rx_start(subghz->txrx);\n\n            subghz_txrx_hopper_unpause(subghz->txrx);\n            subghz->state_notifications = SubGhzNotificationStateRx;\n            return true;\n        } else if(event.event == SubGhzCustomEventViewTransmitterSendSave) {\n            //CC1101 Stop RX -> Save\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);\n\n            subghz_txrx_stop(subghz->txrx);\n            if(!subghz_scene_receiver_info_update_parser(subghz)) {\n                return false;\n            }\n\n            if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {\n                subghz_file_name_clear(subghz);\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneShowErrorSub) {\n            furi_string_set(subghz->error_str, \"Error history parse.\");\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);\n        }\n\n    } else if(event.type == SceneManagerEventTypeTick) {\n        if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {\n            subghz_txrx_hopper_update(subghz->txrx);\n        }\n        switch(subghz->state_notifications) {\n        case SubGhzNotificationStateTx:\n            notification_message(subghz->notifications, &sequence_blink_magenta_10);\n            break;\n        case SubGhzNotificationStateRx:\n            notification_message(subghz->notifications, &sequence_blink_cyan_10);\n            break;\n        case SubGhzNotificationStateRxDone:\n            notification_message(subghz->notifications, &sequence_blink_green_100);\n            subghz->state_notifications = SubGhzNotificationStateRx;\n            break;\n        default:\n            break;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_receiver_info_on_exit(void* context) {\n    SubGhz* subghz = context;\n    widget_reset(subghz->widget);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_region_info.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n\n#include <furi_hal_region.h>\n\nvoid subghz_scene_region_info_on_enter(void* context) {\n    SubGhz* subghz = context;\n    const FuriHalRegion* const region = furi_hal_region_get();\n    FuriString* buffer = furi_string_alloc();\n\n    if(region) {\n        furi_string_cat_printf(buffer, \"Region: %s\\nBands:\\n\", region->country_code);\n        for(uint16_t i = 0; i < region->bands_count; ++i) {\n            furi_string_cat_printf(\n                buffer,\n                \"%lu-%lu kHz\\n\",\n                region->bands[i].start / 1000,\n                region->bands[i].end / 1000);\n        }\n    } else {\n        furi_string_cat_printf(buffer, \"Region: N/A\\n\");\n    }\n\n    widget_add_string_multiline_element(\n        subghz->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, \"Region Information\");\n\n    widget_add_string_multiline_element(\n        subghz->widget, 0, 13, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer));\n\n    furi_string_free(buffer);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n}\n\nbool subghz_scene_region_info_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid subghz_scene_region_info_on_exit(void* context) {\n    SubGhz* subghz = context;\n    widget_reset(subghz->widget);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_rpc.c",
    "content": "#include \"../subghz_i.h\"\n\ntypedef enum {\n    SubGhzRpcStateIdle,\n    SubGhzRpcStateLoaded,\n    SubGhzRpcStateTx,\n} SubGhzRpcState;\n\nvoid subghz_scene_rpc_on_enter(void* context) {\n    SubGhz* subghz = context;\n    scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle);\n}\n\nstatic void subghz_format_file_name_tmp(SubGhz* subghz) {\n    FuriString* file_name;\n    file_name = furi_string_alloc();\n    path_extract_filename(subghz->file_path, file_name, true);\n    snprintf(\n        subghz->file_name_tmp, SUBGHZ_MAX_LEN_NAME, \"loaded\\n%s\", furi_string_get_cstr(file_name));\n    furi_string_free(file_name);\n}\n\nstatic void subghz_scene_rpc_emulation_show(SubGhz* subghz) {\n    Popup* popup = subghz->popup;\n\n    subghz_format_file_name_tmp(subghz);\n    popup_set_header(popup, \"Sub-GHz\", 89, 42, AlignCenter, AlignBottom);\n    popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);\n    popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);\n\n    notification_message(subghz->notifications, &sequence_display_backlight_on);\n}\n\nbool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    bool consumed = false;\n    SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        consumed = true;\n        if(event.event == SubGhzCustomEventSceneExit) {\n            scene_manager_stop(subghz->scene_manager);\n            view_dispatcher_stop(subghz->view_dispatcher);\n            rpc_system_app_confirm(subghz->rpc_ctx, true);\n        } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) {\n            scene_manager_stop(subghz->scene_manager);\n            view_dispatcher_stop(subghz->view_dispatcher);\n        } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {\n            bool result = false;\n            if(state == SubGhzRpcStateLoaded) {\n                switch(\n                    subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) {\n                case SubGhzTxRxStartTxStateErrorOnlyRx:\n                    rpc_system_app_set_error_code(\n                        subghz->rpc_ctx, RpcAppSystemErrorCodeRegionLock);\n                    rpc_system_app_set_error_text(\n                        subghz->rpc_ctx,\n                        \"Transmission on this frequency is restricted in your region\");\n                    break;\n                case SubGhzTxRxStartTxStateErrorParserOthers:\n                    rpc_system_app_set_error_code(\n                        subghz->rpc_ctx, RpcAppSystemErrorCodeInternalParse);\n                    rpc_system_app_set_error_text(\n                        subghz->rpc_ctx, \"Error in protocol parameters description\");\n                    break;\n\n                default: //if(SubGhzTxRxStartTxStateOk)\n                    result = true;\n                    subghz_blink_start(subghz);\n                    scene_manager_set_scene_state(\n                        subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateTx);\n                    break;\n                }\n            }\n            rpc_system_app_confirm(subghz->rpc_ctx, result);\n        } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {\n            bool result = false;\n            if(state == SubGhzRpcStateTx) {\n                subghz_txrx_stop(subghz->txrx);\n                subghz_blink_stop(subghz);\n                result = true;\n            }\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle);\n            rpc_system_app_confirm(subghz->rpc_ctx, result);\n        } else if(event.event == SubGhzCustomEventSceneRpcButtonPressRelease) {\n            bool result = false;\n            if(state == SubGhzRpcStateLoaded) {\n                switch(\n                    subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) {\n                case SubGhzTxRxStartTxStateErrorOnlyRx:\n                    rpc_system_app_set_error_code(\n                        subghz->rpc_ctx, RpcAppSystemErrorCodeRegionLock);\n                    rpc_system_app_set_error_text(\n                        subghz->rpc_ctx,\n                        \"Transmission on this frequency is restricted in your region\");\n                    break;\n                case SubGhzTxRxStartTxStateErrorParserOthers:\n                    rpc_system_app_set_error_code(\n                        subghz->rpc_ctx, RpcAppSystemErrorCodeInternalParse);\n                    rpc_system_app_set_error_text(\n                        subghz->rpc_ctx, \"Error in protocol parameters description\");\n                    break;\n\n                default: //if(SubGhzTxRxStartTxStateOk)\n                    result = true;\n                    subghz_blink_start(subghz);\n                    scene_manager_set_scene_state(\n                        subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateTx);\n                    break;\n                }\n            }\n\n            // Stop transmission\n            if(state == SubGhzRpcStateTx) {\n                subghz_txrx_stop(subghz->txrx);\n                subghz_blink_stop(subghz);\n                result = true;\n            }\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle);\n            rpc_system_app_confirm(subghz->rpc_ctx, result);\n        } else if(event.event == SubGhzCustomEventSceneRpcLoad) {\n            bool result = false;\n            if(state == SubGhzRpcStateIdle) {\n                if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) {\n                    subghz_scene_rpc_emulation_show(subghz);\n                    scene_manager_set_scene_state(\n                        subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded);\n                    result = true;\n                } else {\n                    rpc_system_app_set_error_code(subghz->rpc_ctx, RpcAppSystemErrorCodeParseFile);\n                    rpc_system_app_set_error_text(subghz->rpc_ctx, \"Cannot parse file\");\n                }\n            }\n            rpc_system_app_confirm(subghz->rpc_ctx, result);\n        }\n    }\n    return consumed;\n}\n\nvoid subghz_scene_rpc_on_exit(void* context) {\n    SubGhz* subghz = context;\n    SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc);\n    if(state == SubGhzRpcStateTx) {\n        subghz_txrx_stop(subghz->txrx);\n        subghz_blink_stop(subghz);\n    }\n\n    Popup* popup = subghz->popup;\n\n    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);\n    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);\n    popup_set_icon(popup, 0, 0, NULL);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_save_name.c",
    "content": "#include \"../subghz_i.h\"\n#include \"subghz/types.h\"\n#include \"../helpers/subghz_custom_event.h\"\n#include <lib/subghz/protocols/raw.h>\n#include <gui/modules/validators.h>\n#include <dolphin/dolphin.h>\n#include <toolbox/name_generator.h>\n\n#define MAX_TEXT_INPUT_LEN 23\n\nvoid subghz_scene_save_name_text_input_callback(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName);\n}\n\nvoid subghz_scene_save_name_get_timefilename(FuriString* name) {\n    DateTime datetime = {0};\n    furi_hal_rtc_get_datetime(&datetime);\n    furi_string_printf(\n        name,\n        \"RAW-%.4d%.2d%.2d-%.2d%.2d%.2d\",\n        datetime.year,\n        datetime.month,\n        datetime.day,\n        datetime.hour,\n        datetime.minute,\n        datetime.second);\n}\n\nvoid subghz_scene_save_name_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    // Setup view\n    TextInput* text_input = subghz->text_input;\n    bool dev_name_empty = false;\n\n    FuriString* file_name = furi_string_alloc();\n    FuriString* dir_name = furi_string_alloc();\n\n    if(!subghz_path_is_file(subghz->file_path)) {\n        char file_name_buf[SUBGHZ_MAX_LEN_NAME];\n\n        name_generator_make_auto(file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX);\n\n        furi_string_set(file_name, file_name_buf);\n        furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);\n        //highlighting the entire filename by default\n        dev_name_empty = true;\n    } else {\n        furi_string_set(subghz->file_path_tmp, subghz->file_path);\n        path_extract_dirname(furi_string_get_cstr(subghz->file_path), dir_name);\n        path_extract_filename(subghz->file_path, file_name, true);\n        if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n           SubGhzCustomEventManagerNoSet) {\n            if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==\n               SubGhzCustomEventManagerSetRAW) {\n                dev_name_empty = true;\n                subghz_scene_save_name_get_timefilename(file_name);\n            }\n        }\n        furi_string_set(subghz->file_path, dir_name);\n    }\n\n    strlcpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME);\n    text_input_set_header_text(text_input, \"Name signal\");\n    text_input_set_result_callback(\n        text_input,\n        subghz_scene_save_name_text_input_callback,\n        subghz,\n        subghz->file_name_tmp,\n        MAX_TEXT_INPUT_LEN,\n        dev_name_empty);\n\n    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(\n        furi_string_get_cstr(subghz->file_path), SUBGHZ_APP_FILENAME_EXTENSION, \"\");\n    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);\n\n    furi_string_free(file_name);\n    furi_string_free(dir_name);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput);\n}\n\nbool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeBack) {\n        if(!strcmp(subghz->file_name_tmp, \"\") ||\n           scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n               SubGhzCustomEventManagerNoSet) {\n            furi_string_set(subghz->file_path, subghz->file_path_tmp);\n        }\n        scene_manager_previous_scene(subghz->scene_manager);\n        return true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneSaveName) {\n            if(strcmp(subghz->file_name_tmp, \"\") != 0) {\n                furi_string_cat_printf(\n                    subghz->file_path,\n                    \"/%s%s\",\n                    subghz->file_name_tmp,\n                    SUBGHZ_APP_FILENAME_EXTENSION);\n                if(subghz_path_is_file(subghz->file_path_tmp)) {\n                    if(!subghz_rename_file(subghz)) {\n                        return false;\n                    }\n                } else {\n                    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) !=\n                       SubGhzCustomEventManagerNoSet) {\n                        subghz_save_protocol_to_file(\n                            subghz,\n                            subghz_txrx_get_fff_data(subghz->txrx),\n                            furi_string_get_cstr(subghz->file_path));\n                        scene_manager_set_scene_state(\n                            subghz->scene_manager,\n                            SubGhzSceneSetType,\n                            SubGhzCustomEventManagerNoSet);\n                    } else {\n                        subghz_save_protocol_to_file(\n                            subghz,\n                            subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen),\n                            furi_string_get_cstr(subghz->file_path));\n                    }\n                }\n\n                if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=\n                   SubGhzCustomEventManagerNoSet) {\n                    subghz_protocol_raw_gen_fff_data(\n                        subghz_txrx_get_fff_data(subghz->txrx),\n                        furi_string_get_cstr(subghz->file_path),\n                        subghz_txrx_radio_device_get_name(subghz->txrx));\n                    scene_manager_set_scene_state(\n                        subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);\n                } else {\n                    furi_string_reset(subghz->file_path_tmp);\n                }\n\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);\n                if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSavedMenu)) {\n                    // Nothing, do not count editing as saving\n                } else if(scene_manager_has_previous_scene(\n                              subghz->scene_manager, SubGhzSceneMoreRAW)) {\n                    // Ditto, for RAW signals\n                } else if(scene_manager_has_previous_scene(\n                              subghz->scene_manager, SubGhzSceneSetType)) {\n                    dolphin_deed(DolphinDeedSubGhzAddManually);\n                } else {\n                    dolphin_deed(DolphinDeedSubGhzSave);\n                }\n                return true;\n            } else {\n                furi_string_set(subghz->error_str, \"No name file\");\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_save_name_on_exit(void* context) {\n    SubGhz* subghz = context;\n\n    // Clear validator\n    void* validator_context = text_input_get_validator_callback_context(subghz->text_input);\n    text_input_set_validator(subghz->text_input, NULL, NULL);\n    validator_is_file_free(validator_context);\n\n    // Clear view\n    text_input_reset(subghz->text_input);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_save_success.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_save_success_popup_callback(void* context) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveSuccess);\n}\n\nvoid subghz_scene_save_success_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    // Setup view\n    Popup* popup = subghz->popup;\n    popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);\n    popup_set_header(popup, \"Saved\", 15, 19, AlignLeft, AlignBottom);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, subghz);\n    popup_set_callback(popup, subghz_scene_save_success_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);\n}\n\nbool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneSaveSuccess) {\n            if(!scene_manager_search_and_switch_to_previous_scene(\n                   subghz->scene_manager, SubGhzSceneReceiver)) {\n                subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave);\n                if(!scene_manager_search_and_switch_to_previous_scene(\n                       subghz->scene_manager, SubGhzSceneReadRAW)) {\n                    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n                    if(!scene_manager_search_and_switch_to_previous_scene(\n                           subghz->scene_manager, SubGhzSceneSaved)) {\n                        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);\n                    }\n                }\n            }\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_save_success_on_exit(void* context) {\n    SubGhz* subghz = context;\n    Popup* popup = subghz->popup;\n\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_saved.c",
    "content": "#include \"../subghz_i.h\"\n\nvoid subghz_scene_saved_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    if(subghz_load_protocol_from_file(subghz)) {\n        if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);\n        } else {\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu);\n        }\n    } else {\n        scene_manager_search_and_switch_to_previous_scene(subghz->scene_manager, SubGhzSceneStart);\n    }\n}\n\nbool subghz_scene_saved_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n    return false;\n}\n\nvoid subghz_scene_saved_on_exit(void* context) {\n    SubGhz* subghz = context;\n    scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu, 0);\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_saved_menu.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n\nenum SubmenuIndex {\n    SubmenuIndexEmulate,\n    SubmenuIndexEdit,\n    SubmenuIndexDelete,\n};\n\nvoid subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);\n}\n\nvoid subghz_scene_saved_menu_on_enter(void* context) {\n    SubGhz* subghz = context;\n    submenu_add_item(\n        subghz->submenu,\n        \"Emulate\",\n        SubmenuIndexEmulate,\n        subghz_scene_saved_menu_submenu_callback,\n        subghz);\n\n    submenu_add_item(\n        subghz->submenu,\n        \"Rename\",\n        SubmenuIndexEdit,\n        subghz_scene_saved_menu_submenu_callback,\n        subghz);\n\n    submenu_add_item(\n        subghz->submenu,\n        \"Delete\",\n        SubmenuIndexDelete,\n        subghz_scene_saved_menu_submenu_callback,\n        subghz);\n\n    submenu_set_selected_item(\n        subghz->submenu,\n        scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu));\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);\n}\n\nbool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexEmulate) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEmulate);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);\n            return true;\n        } else if(event.event == SubmenuIndexDelete) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexDelete);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete);\n            return true;\n        } else if(event.event == SubmenuIndexEdit) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEdit);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_saved_menu_on_exit(void* context) {\n    SubGhz* subghz = context;\n    submenu_reset(subghz->submenu);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_set_type.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../helpers/subghz_txrx_create_protocol_key.h\"\n#include <lib/subghz/blocks/math.h>\n#include <lib/subghz/protocols/protocol_items.h>\n\n#define TAG \"SubGhzSetType\"\n\nvoid subghz_scene_set_type_submenu_callback(void* context, uint32_t index) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);\n}\n\nvoid subghz_scene_set_type_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    submenu_add_item(\n        subghz->submenu,\n        \"Princeton_433\",\n        SubmenuIndexPricenton_433,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Princeton_315\",\n        SubmenuIndexPricenton_315,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Nice Flo 12bit_433\",\n        SubmenuIndexNiceFlo12bit,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Nice Flo 24bit_433\",\n        SubmenuIndexNiceFlo24bit,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"CAME 12bit_433\",\n        SubmenuIndexCAME12bit,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"CAME 24bit_433\",\n        SubmenuIndexCAME24bit,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Linear_300\",\n        SubmenuIndexLinear_300_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"CAME TWEE\",\n        SubmenuIndexCAMETwee,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Gate TX_433\",\n        SubmenuIndexGateTX,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"DoorHan_315\",\n        SubmenuIndexDoorHan_315_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"DoorHan_433\",\n        SubmenuIndexDoorHan_433_92,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"LiftMaster_315\",\n        SubmenuIndexLiftMaster_315_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"LiftMaster_390\",\n        SubmenuIndexLiftMaster_390_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Security+2.0_310\",\n        SubmenuIndexSecPlus_v2_310_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Security+2.0_315\",\n        SubmenuIndexSecPlus_v2_315_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Security+2.0_390\",\n        SubmenuIndexSecPlus_v2_390_00,\n        subghz_scene_set_type_submenu_callback,\n        subghz);\n\n    submenu_set_selected_item(\n        subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType));\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);\n}\n\nbool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    SubGhzProtocolStatus generated_protocol = SubGhzProtocolStatusError;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        uint32_t key = (uint32_t)rand();\n        switch(event.event) {\n        case SubmenuIndexPricenton_433:\n            key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8\n            generated_protocol = subghz_txrx_gen_data_protocol_and_te(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);\n            break;\n        case SubmenuIndexPricenton_315:\n            key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8\n            generated_protocol = subghz_txrx_gen_data_protocol_and_te(\n                subghz->txrx, \"AM650\", 315000000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);\n            break;\n        case SubmenuIndexNiceFlo12bit:\n            key = (key & 0x00000FF0) | 0x1; //btn 0x1, 0x2, 0x4\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12);\n            break;\n        case SubmenuIndexNiceFlo24bit:\n            key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24);\n            break;\n        case SubmenuIndexCAME12bit:\n            key = (key & 0x00000FF0) | 0x1; //btn 0x1, 0x2, 0x4\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12);\n            break;\n        case SubmenuIndexCAME24bit:\n            key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24);\n            break;\n        case SubmenuIndexLinear_300_00:\n            key = (key & 0x3FF);\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10);\n            break;\n        case SubmenuIndexCAMETwee:\n            key = (key & 0x0FFFFFF0);\n            key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE);\n\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54);\n            break;\n        case SubmenuIndexGateTX:\n            key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)\n            uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24);\n            generated_protocol = subghz_txrx_gen_data_protocol(\n                subghz->txrx, \"AM650\", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24);\n            break;\n        case SubmenuIndexDoorHan_433_92:\n            generated_protocol = subghz_txrx_gen_keeloq_protocol(\n                subghz->txrx, \"AM650\", 433920000, \"DoorHan\", key, 0x2, 0x0003);\n            if(generated_protocol != SubGhzProtocolStatusOk) {\n                furi_string_set(\n                    subghz->error_str, \"Function requires\\nan SD card with\\nfresh databases.\");\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);\n            }\n            break;\n        case SubmenuIndexDoorHan_315_00:\n            generated_protocol = subghz_txrx_gen_keeloq_protocol(\n                subghz->txrx, \"AM650\", 315000000, \"DoorHan\", key, 0x2, 0x0003);\n            if(generated_protocol != SubGhzProtocolStatusOk) {\n                furi_string_set(\n                    subghz->error_str, \"Function requires\\nan SD card with\\nfresh databases.\");\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);\n            }\n            break;\n        case SubmenuIndexLiftMaster_315_00:\n            generated_protocol =\n                subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, \"AM650\", 315000000);\n            break;\n        case SubmenuIndexLiftMaster_390_00:\n            generated_protocol =\n                subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, \"AM650\", 390000000);\n            break;\n        case SubmenuIndexSecPlus_v2_310_00:\n            key = (key & 0x7FFFF3FC); // 850LM pairing\n            generated_protocol = subghz_txrx_gen_secplus_v2_protocol(\n                subghz->txrx, \"AM650\", 310000000, key, 0x68, 0xE500000);\n            break;\n        case SubmenuIndexSecPlus_v2_315_00:\n            key = (key & 0x7FFFF3FC); // 850LM pairing\n            generated_protocol = subghz_txrx_gen_secplus_v2_protocol(\n                subghz->txrx, \"AM650\", 315000000, key, 0x68, 0xE500000);\n            break;\n        case SubmenuIndexSecPlus_v2_390_00:\n            key = (key & 0x7FFFF3FC); // 850LM pairing\n            generated_protocol = subghz_txrx_gen_secplus_v2_protocol(\n                subghz->txrx, \"AM650\", 390000000, key, 0x68, 0xE500000);\n            break;\n        default:\n            return false;\n            break;\n        }\n\n        scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event);\n\n        if(generated_protocol == SubGhzProtocolStatusOk) {\n            subghz_file_name_clear(subghz);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid subghz_scene_set_type_on_exit(void* context) {\n    SubGhz* subghz = context;\n    submenu_reset(subghz->submenu);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_show_error.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n#include \"../helpers/subghz_custom_event.h\"\n\nstatic const NotificationSequence subghs_sequence_sd_error = {\n    &message_red_255,\n    &message_green_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nvoid subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n\n    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneShowErrorOk);\n    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneShowErrorBack);\n    }\n}\n\nvoid subghz_scene_show_error_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    widget_add_icon_element(subghz->widget, 0, 0, &I_SDQuestion_35x43);\n\n    widget_add_string_multiline_element(\n        subghz->widget,\n        81,\n        24,\n        AlignCenter,\n        AlignCenter,\n        FontSecondary,\n        furi_string_get_cstr(subghz->error_str));\n    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) ==\n       SubGhzCustomEventManagerSet) {\n        widget_add_button_element(\n            subghz->widget, GuiButtonTypeRight, \"OK\", subghz_scene_show_error_callback, subghz);\n    } else {\n        notification_message(subghz->notifications, &subghs_sequence_sd_error);\n    }\n\n    widget_add_button_element(\n        subghz->widget, GuiButtonTypeLeft, \"Back\", subghz_scene_show_error_callback, subghz);\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n}\n\nbool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    SubGhzCustomEvent scene_state =\n        scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError);\n    if(event.type == SceneManagerEventTypeBack) {\n        if(scene_state == SubGhzCustomEventManagerSet) {\n            return false;\n        } else {\n            scene_manager_search_and_switch_to_previous_scene(\n                subghz->scene_manager, SubGhzSceneStart);\n        }\n        return true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneShowErrorOk) {\n            if(scene_state == SubGhzCustomEventManagerSet) {\n                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventSceneShowErrorBack) {\n            if(scene_state == SubGhzCustomEventManagerSet) {\n                //exit app\n                if(!scene_manager_previous_scene(subghz->scene_manager)) {\n                    scene_manager_stop(subghz->scene_manager);\n                    view_dispatcher_stop(subghz->view_dispatcher);\n                }\n            } else {\n                scene_manager_search_and_switch_to_previous_scene(\n                    subghz->scene_manager, SubGhzSceneStart);\n            }\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_show_error_on_exit(void* context) {\n    SubGhz* subghz = context;\n    scene_manager_set_scene_state(\n        subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet);\n    widget_reset(subghz->widget);\n    furi_string_reset(subghz->error_str);\n    notification_message(subghz->notifications, &sequence_reset_rgb);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_show_error_sub.c",
    "content": "#include \"../subghz_i.h\" // IWYU pragma: keep\n#include \"../helpers/subghz_custom_event.h\"\n\nvoid subghz_scene_show_error_sub_popup_callback(void* context) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneShowErrorSub);\n}\n\nvoid subghz_scene_show_error_sub_on_enter(void* context) {\n    SubGhz* subghz = context;\n\n    // Setup view\n    Popup* popup = subghz->popup;\n    popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);\n    popup_set_header(popup, furi_string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, subghz);\n    popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);\n\n    notification_message(subghz->notifications, &sequence_set_red_255);\n}\n\nbool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventSceneShowErrorSub) {\n            scene_manager_search_and_switch_to_previous_scene(\n                subghz->scene_manager, SubGhzSceneStart);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_show_error_sub_on_exit(void* context) {\n    SubGhz* subghz = context;\n    Popup* popup = subghz->popup;\n\n    popup_reset(popup);\n\n    furi_string_reset(subghz->error_str);\n\n    notification_message(subghz->notifications, &sequence_reset_rgb);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_start.c",
    "content": "#include \"../subghz_i.h\"\n#include <dolphin/dolphin.h>\n\nenum SubmenuIndex {\n    SubmenuIndexRead = 10,\n    SubmenuIndexSaved,\n    SubmenuIndexAddManually,\n    SubmenuIndexFrequencyAnalyzer,\n    SubmenuIndexReadRAW,\n    SubmenuIndexShowRegionInfo,\n    SubmenuIndexRadioSetting,\n};\n\nvoid subghz_scene_start_submenu_callback(void* context, uint32_t index) {\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);\n}\n\nvoid subghz_scene_start_on_enter(void* context) {\n    SubGhz* subghz = context;\n    if(subghz->state_notifications == SubGhzNotificationStateStarting) {\n        subghz->state_notifications = SubGhzNotificationStateIDLE;\n    }\n    submenu_add_item(\n        subghz->submenu, \"Read\", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Read RAW\",\n        SubmenuIndexReadRAW,\n        subghz_scene_start_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu, \"Saved\", SubmenuIndexSaved, subghz_scene_start_submenu_callback, subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Add Manually\",\n        SubmenuIndexAddManually,\n        subghz_scene_start_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Frequency Analyzer\",\n        SubmenuIndexFrequencyAnalyzer,\n        subghz_scene_start_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Region Information\",\n        SubmenuIndexShowRegionInfo,\n        subghz_scene_start_submenu_callback,\n        subghz);\n    submenu_add_item(\n        subghz->submenu,\n        \"Radio Settings\",\n        SubmenuIndexRadioSetting,\n        subghz_scene_start_submenu_callback,\n        subghz);\n    submenu_set_selected_item(\n        subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneStart));\n\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);\n}\n\nbool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeBack) {\n        //exit app\n        scene_manager_stop(subghz->scene_manager);\n        view_dispatcher_stop(subghz->view_dispatcher);\n        return true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubmenuIndexReadRAW) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);\n            subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);\n            return true;\n        } else if(event.event == SubmenuIndexRead) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);\n            return true;\n        } else if(event.event == SubmenuIndexSaved) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);\n            return true;\n        } else if(event.event == SubmenuIndexAddManually) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType);\n            return true;\n        } else if(event.event == SubmenuIndexFrequencyAnalyzer) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);\n            dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer);\n            return true;\n        } else if(event.event == SubmenuIndexShowRegionInfo) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRegionInfo);\n            return true;\n        } else if(event.event == SubmenuIndexRadioSetting) {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRadioSetting);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRadioSettings);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_scene_start_on_exit(void* context) {\n    SubGhz* subghz = context;\n    submenu_reset(subghz->submenu);\n}\n"
  },
  {
    "path": "applications/main/subghz/scenes/subghz_scene_transmitter.c",
    "content": "#include \"../subghz_i.h\"\n#include \"../views/transmitter.h\"\n#include <dolphin/dolphin.h>\n\nvoid subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);\n}\n\nbool subghz_scene_transmitter_update_data_show(void* context) {\n    SubGhz* subghz = context;\n    bool ret = false;\n    SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx);\n\n    if(decoder) {\n        FuriString* key_str = furi_string_alloc();\n        FuriString* frequency_str = furi_string_alloc();\n        FuriString* modulation_str = furi_string_alloc();\n\n        if(subghz_protocol_decoder_base_deserialize(\n               decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) {\n            subghz_protocol_decoder_base_get_string(decoder, key_str);\n\n            subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);\n            subghz_view_transmitter_add_data_to_show(\n                subghz->subghz_transmitter,\n                furi_string_get_cstr(key_str),\n                furi_string_get_cstr(frequency_str),\n                furi_string_get_cstr(modulation_str),\n                subghz_txrx_protocol_is_transmittable(subghz->txrx, false));\n            ret = true;\n        }\n        furi_string_free(frequency_str);\n        furi_string_free(modulation_str);\n        furi_string_free(key_str);\n    }\n    subghz_view_transmitter_set_radio_device_type(\n        subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx));\n    subghz_view_transmitter_set_model_type(\n        subghz->subghz_transmitter, SubGhzViewTransmitterModelTypeTx);\n    return ret;\n}\n\nvoid subghz_scene_transmitter_on_enter(void* context) {\n    SubGhz* subghz = context;\n    if(!subghz_scene_transmitter_update_data_show(subghz)) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError);\n    }\n\n    subghz_view_transmitter_set_callback(\n        subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);\n\n    subghz->state_notifications = SubGhzNotificationStateIDLE;\n    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter);\n}\n\nbool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {\n    SubGhz* subghz = context;\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == SubGhzCustomEventViewTransmitterSendStart) {\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n\n            if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {\n                subghz->state_notifications = SubGhzNotificationStateTx;\n                subghz_scene_transmitter_update_data_show(subghz);\n                dolphin_deed(DolphinDeedSubGhzSend);\n            }\n            return true;\n        } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            subghz_txrx_stop(subghz->txrx);\n            return true;\n        } else if(event.event == SubGhzCustomEventViewTransmitterBack) {\n            subghz->state_notifications = SubGhzNotificationStateIDLE;\n            scene_manager_search_and_switch_to_previous_scene(\n                subghz->scene_manager, SubGhzSceneStart);\n            return true;\n        } else if(event.event == SubGhzCustomEventViewTransmitterError) {\n            furi_string_set(subghz->error_str, \"Protocol not\\nfound!\");\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);\n        }\n    } else if(event.type == SceneManagerEventTypeTick) {\n        if(subghz->state_notifications == SubGhzNotificationStateTx) {\n            notification_message(subghz->notifications, &sequence_blink_magenta_10);\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid subghz_scene_transmitter_on_exit(void* context) {\n    SubGhz* subghz = context;\n    subghz->state_notifications = SubGhzNotificationStateIDLE;\n}\n"
  },
  {
    "path": "applications/main/subghz/subghz.c",
    "content": "/* Abandon hope, all ye who enter here. */\n\n#include \"subghz_i.h\"\n\nbool subghz_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    return scene_manager_handle_custom_event(subghz->scene_manager, event);\n}\n\nbool subghz_back_event_callback(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    return scene_manager_handle_back_event(subghz->scene_manager);\n}\n\nvoid subghz_tick_event_callback(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    scene_manager_handle_tick_event(subghz->scene_manager);\n}\n\nstatic void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n\n    furi_assert(subghz->rpc_ctx);\n\n    if(event->type == RpcAppEventTypeSessionClose) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose);\n        rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);\n        subghz->rpc_ctx = NULL;\n    } else if(event->type == RpcAppEventTypeAppExit) {\n        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);\n    } else if(event->type == RpcAppEventTypeLoadFile) {\n        furi_assert(event->data.type == RpcAppSystemEventDataTypeString);\n        furi_string_set(subghz->file_path, event->data.string);\n        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);\n    } else if(event->type == RpcAppEventTypeButtonPress) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress);\n    } else if(event->type == RpcAppEventTypeButtonRelease) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease);\n    } else if(event->type == RpcAppEventTypeButtonPressRelease) {\n        view_dispatcher_send_custom_event(\n            subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPressRelease);\n    } else {\n        rpc_system_app_confirm(subghz->rpc_ctx, false);\n    }\n}\n\nSubGhz* subghz_alloc(void) {\n    SubGhz* subghz = malloc(sizeof(SubGhz));\n\n    subghz->file_path = furi_string_alloc();\n    subghz->file_path_tmp = furi_string_alloc();\n\n    // GUI\n    subghz->gui = furi_record_open(RECORD_GUI);\n\n    // View Dispatcher\n    subghz->view_dispatcher = view_dispatcher_alloc();\n\n    subghz->scene_manager = scene_manager_alloc(&subghz_scene_handlers, subghz);\n    view_dispatcher_set_event_callback_context(subghz->view_dispatcher, subghz);\n    view_dispatcher_set_custom_event_callback(\n        subghz->view_dispatcher, subghz_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        subghz->view_dispatcher, subghz_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        subghz->view_dispatcher, subghz_tick_event_callback, 100);\n\n    // Open Notification record\n    subghz->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // SubMenu\n    subghz->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher, SubGhzViewIdMenu, submenu_get_view(subghz->submenu));\n\n    // Receiver\n    subghz->subghz_receiver = subghz_view_receiver_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher,\n        SubGhzViewIdReceiver,\n        subghz_view_receiver_get_view(subghz->subghz_receiver));\n\n    // Popup\n    subghz->popup = popup_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher, SubGhzViewIdPopup, popup_get_view(subghz->popup));\n\n    // Text Input\n    subghz->text_input = text_input_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher, SubGhzViewIdTextInput, text_input_get_view(subghz->text_input));\n\n    // Custom Widget\n    subghz->widget = widget_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher, SubGhzViewIdWidget, widget_get_view(subghz->widget));\n\n    //Dialog\n    subghz->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    // Transmitter\n    subghz->subghz_transmitter = subghz_view_transmitter_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher,\n        SubGhzViewIdTransmitter,\n        subghz_view_transmitter_get_view(subghz->subghz_transmitter));\n\n    // Variable Item List\n    subghz->variable_item_list = variable_item_list_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher,\n        SubGhzViewIdVariableItemList,\n        variable_item_list_get_view(subghz->variable_item_list));\n\n    // Frequency Analyzer\n    subghz->subghz_frequency_analyzer = subghz_frequency_analyzer_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher,\n        SubGhzViewIdFrequencyAnalyzer,\n        subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer));\n\n    // Read RAW\n    subghz->subghz_read_raw = subghz_read_raw_alloc();\n    view_dispatcher_add_view(\n        subghz->view_dispatcher,\n        SubGhzViewIdReadRAW,\n        subghz_read_raw_get_view(subghz->subghz_read_raw));\n\n    //init threshold rssi\n    subghz->threshold_rssi = subghz_threshold_rssi_alloc();\n\n    subghz_unlock(subghz);\n    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);\n    subghz->history = subghz_history_alloc();\n    subghz->filter = SubGhzProtocolFlag_Decodable;\n\n    //init TxRx & History & KeyBoard\n    subghz->txrx = subghz_txrx_alloc();\n    subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);\n    subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz);\n\n    //Init Error_str\n    subghz->error_str = furi_string_alloc();\n\n    return subghz;\n}\n\nvoid subghz_free(SubGhz* subghz) {\n    furi_assert(subghz);\n\n    if(subghz->rpc_ctx) {\n        rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);\n        rpc_system_app_send_exited(subghz->rpc_ctx);\n        subghz_blink_stop(subghz);\n        subghz->rpc_ctx = NULL;\n    }\n\n    subghz_txrx_speaker_off(subghz->txrx);\n    subghz_txrx_stop(subghz->txrx);\n    subghz_txrx_sleep(subghz->txrx);\n\n    // Receiver\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver);\n    subghz_view_receiver_free(subghz->subghz_receiver);\n\n    // TextInput\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTextInput);\n    text_input_free(subghz->text_input);\n\n    // Custom Widget\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdWidget);\n    widget_free(subghz->widget);\n\n    //Dialog\n    furi_record_close(RECORD_DIALOGS);\n\n    // Transmitter\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTransmitter);\n    subghz_view_transmitter_free(subghz->subghz_transmitter);\n\n    // Variable Item List\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);\n    variable_item_list_free(subghz->variable_item_list);\n\n    // Frequency Analyzer\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer);\n    subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer);\n\n    // Read RAW\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);\n    subghz_read_raw_free(subghz->subghz_read_raw);\n\n    // Submenu\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdMenu);\n    submenu_free(subghz->submenu);\n\n    // Popup\n    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdPopup);\n    popup_free(subghz->popup);\n\n    // Scene manager\n    scene_manager_free(subghz->scene_manager);\n\n    // View Dispatcher\n    view_dispatcher_free(subghz->view_dispatcher);\n\n    // GUI\n    furi_record_close(RECORD_GUI);\n    subghz->gui = NULL;\n\n    // threshold rssi\n    subghz_threshold_rssi_free(subghz->threshold_rssi);\n\n    //Worker & Protocol & History\n    subghz_history_free(subghz->history);\n\n    //TxRx\n    subghz_txrx_free(subghz->txrx);\n\n    //Error string\n    furi_string_free(subghz->error_str);\n\n    // Notifications\n    furi_record_close(RECORD_NOTIFICATION);\n    subghz->notifications = NULL;\n\n    // Path strings\n    furi_string_free(subghz->file_path);\n    furi_string_free(subghz->file_path_tmp);\n\n    // The rest\n    free(subghz);\n}\n\nint32_t subghz_app(void* p) {\n    SubGhz* subghz = subghz_alloc();\n\n    if(!furi_hal_region_is_provisioned()) {\n        subghz_dialog_message_show_only_rx(subghz);\n        subghz_free(subghz);\n        return 1;\n    }\n\n    // Check argument and run corresponding scene\n    if(p && strlen(p)) {\n        uint32_t rpc_ctx = 0;\n        if(sscanf(p, \"RPC %lX\", &rpc_ctx) == 1) {\n            subghz->rpc_ctx = (void*)rpc_ctx;\n            rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz);\n            rpc_system_app_send_started(subghz->rpc_ctx);\n            view_dispatcher_attach_to_gui(\n                subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeDesktop);\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc);\n        } else {\n            view_dispatcher_attach_to_gui(\n                subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);\n            if(subghz_key_load(subghz, p, true)) {\n                furi_string_set(subghz->file_path, (const char*)p);\n\n                if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {\n                    //Load Raw TX\n                    subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);\n                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);\n                } else {\n                    //Load transmitter TX\n                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);\n                }\n            } else {\n                //exit app\n                scene_manager_stop(subghz->scene_manager);\n                view_dispatcher_stop(subghz->view_dispatcher);\n            }\n        }\n    } else {\n        view_dispatcher_attach_to_gui(\n            subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);\n        furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);\n        if(subghz_txrx_is_database_loaded(subghz->txrx)) {\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);\n        } else {\n            scene_manager_set_scene_state(\n                subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerSet);\n            furi_string_set(\n                subghz->error_str,\n                \"No SD card or\\ndatabase found.\\nSome app function\\nmay be reduced.\");\n            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);\n        }\n    }\n\n    furi_hal_power_suppress_charge_enter();\n\n    view_dispatcher_run(subghz->view_dispatcher);\n\n    furi_hal_power_suppress_charge_exit();\n\n    subghz_free(subghz);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/subghz/subghz.h",
    "content": "#pragma once\n\ntypedef struct SubGhz SubGhz;\n"
  },
  {
    "path": "applications/main/subghz/subghz_cli.c",
    "content": "#include \"subghz_cli.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>\n#include <cli/cli_main_commands.h>\n#include <toolbox/cli/cli_ansi.h>\n\n#include <lib/subghz/subghz_keystore.h>\n#include <lib/subghz/receiver.h>\n#include <lib/subghz/transmitter.h>\n#include <lib/subghz/subghz_file_encoder_worker.h>\n#include <lib/subghz/protocols/protocol_items.h>\n#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>\n#include <lib/subghz/devices/devices.h>\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#include <lib/toolbox/args.h>\n#include <lib/toolbox/strint.h>\n#include <toolbox/pipe.h>\n\n#include \"helpers/subghz_chat.h\"\n\n#include <notification/notification_messages.h>\n#include <flipper_format/flipper_format_i.h>\n\n#define SUBGHZ_FREQUENCY_RANGE_STR \\\n    \"299999755...348000000 or 386999938...464000000 or 778999847...928000000\"\n\n#define TAG \"SubGhzCli\"\n\nstatic void subghz_cli_radio_device_power_on(void) {\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, true);\n    furi_record_close(RECORD_POWER);\n}\n\nstatic void subghz_cli_radio_device_power_off(void) {\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, false);\n    furi_record_close(RECORD_POWER);\n}\n\nstatic SubGhzEnvironment* subghz_cli_environment_init(void) {\n    SubGhzEnvironment* environment = subghz_environment_alloc();\n    if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) {\n        printf(\"Load_keystore keeloq_mfcodes \\033[0;32mOK\\033[0m\\r\\n\");\n    } else {\n        printf(\"Load_keystore keeloq_mfcodes \\033[0;31mERROR\\033[0m\\r\\n\");\n    }\n    if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) {\n        printf(\"Load_keystore keeloq_mfcodes_user \\033[0;32mOK\\033[0m\\r\\n\");\n    } else {\n        printf(\"Load_keystore keeloq_mfcodes_user \\033[0;33mAbsent\\033[0m\\r\\n\");\n    }\n    subghz_environment_set_came_atomo_rainbow_table_file_name(\n        environment, SUBGHZ_CAME_ATOMO_DIR_NAME);\n    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(\n        environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);\n    subghz_environment_set_nice_flor_s_rainbow_table_file_name(\n        environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);\n    subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);\n    return environment;\n}\n\nvoid subghz_cli_command_tx_carrier(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    uint32_t frequency = 433920000;\n\n    if(furi_string_size(args)) {\n        if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) !=\n           StrintParseNoError) {\n            cli_print_usage(\"subghz tx_carrier\", \"<Frequency: in Hz>\", furi_string_get_cstr(args));\n            return;\n        }\n        if(!furi_hal_subghz_is_frequency_valid(frequency)) {\n            printf(\n                \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\",\n                frequency);\n            return;\n        }\n    }\n\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n    frequency = furi_hal_subghz_set_frequency_and_path(frequency);\n\n    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);\n    furi_hal_gpio_write(&gpio_cc1101_g0, true);\n\n    furi_hal_power_suppress_charge_enter();\n\n    if(furi_hal_subghz_tx()) {\n        printf(\"Transmitting at frequency %lu Hz\\r\\n\", frequency);\n        printf(\"Press CTRL+C to stop\\r\\n\");\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(250);\n        }\n    } else {\n        printf(\"This frequency can only be used for RX in your region\\r\\n\");\n    }\n\n    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);\n    furi_hal_subghz_sleep();\n\n    furi_hal_power_suppress_charge_exit();\n}\n\nvoid subghz_cli_command_rx_carrier(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    uint32_t frequency = 433920000;\n\n    if(furi_string_size(args)) {\n        if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) !=\n           StrintParseNoError) {\n            cli_print_usage(\"subghz rx_carrier\", \"<Frequency: in Hz>\", furi_string_get_cstr(args));\n            return;\n        }\n        if(!furi_hal_subghz_is_frequency_valid(frequency)) {\n            printf(\n                \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\",\n                frequency);\n            return;\n        }\n    }\n\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n    frequency = furi_hal_subghz_set_frequency_and_path(frequency);\n    printf(\"Receiving at frequency %lu Hz\\r\\n\", frequency);\n    printf(\"Press CTRL+C to stop\\r\\n\");\n\n    furi_hal_power_suppress_charge_enter();\n\n    furi_hal_subghz_rx();\n\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        furi_delay_ms(250);\n        printf(\"RSSI: %03.1fdbm\\r\", (double)furi_hal_subghz_get_rssi());\n        fflush(stdout);\n    }\n\n    furi_hal_power_suppress_charge_exit();\n\n    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);\n    furi_hal_subghz_sleep();\n}\n\nstatic const SubGhzDevice* subghz_cli_command_get_device(uint32_t* device_ind) {\n    const SubGhzDevice* device = NULL;\n    switch(*device_ind) {\n    case 1:\n        subghz_cli_radio_device_power_on();\n        device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);\n        break;\n\n    default:\n        device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);\n        break;\n    }\n    //check if the device is connected\n    if(!subghz_devices_is_connect(device)) {\n        subghz_cli_radio_device_power_off();\n        device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);\n        *device_ind = 0;\n    }\n    return device;\n}\n\nvoid subghz_cli_command_tx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    uint32_t frequency = 433920000;\n    uint32_t key = 0x0074BADE;\n    uint32_t repeat = 10;\n    uint32_t te = 403;\n    uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT\n\n    if(furi_string_size(args)) {\n        char* args_cstr = (char*)furi_string_get_cstr(args);\n        StrintParseError parse_err = StrintParseNoError;\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &key, 16);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &te, 10);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &repeat, 10);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10);\n        if(parse_err) {\n            cli_print_usage(\n                \"subghz tx\",\n                \"<3 Byte Key: in hex> <Frequency: in Hz> <Te us> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>\",\n                furi_string_get_cstr(args));\n            return;\n        }\n    }\n    subghz_devices_init();\n    const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind);\n    if(!subghz_devices_is_frequency_valid(device, frequency)) {\n        printf(\n            \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\", frequency);\n        subghz_devices_deinit();\n        subghz_cli_radio_device_power_off();\n        return;\n    }\n    printf(\n        \"Transmitting at %lu, key %lx, te %lu, repeat %lu device %lu. Press CTRL+C to stop\\r\\n\",\n        frequency,\n        key,\n        te,\n        repeat,\n        device_ind);\n\n    FuriString* flipper_format_string = furi_string_alloc_printf(\n        \"Protocol: Princeton\\n\"\n        \"Bit: 24\\n\"\n        \"Key: 00 00 00 00 00 %02X %02X %02X\\n\"\n        \"TE: %lu\\n\"\n        \"Repeat: %lu\\n\",\n        (uint8_t)((key >> 16) & 0xFFU),\n        (uint8_t)((key >> 8) & 0xFFU),\n        (uint8_t)(key & 0xFFU),\n        te,\n        repeat);\n    FlipperFormat* flipper_format = flipper_format_string_alloc();\n    Stream* stream = flipper_format_get_raw_stream(flipper_format);\n    stream_clean(stream);\n    stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));\n\n    SubGhzEnvironment* environment = subghz_environment_alloc();\n    subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);\n\n    SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, \"Princeton\");\n    subghz_transmitter_deserialize(transmitter, flipper_format);\n\n    subghz_devices_begin(device);\n    subghz_devices_reset(device);\n    subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL);\n    frequency = subghz_devices_set_frequency(device, frequency);\n\n    furi_hal_power_suppress_charge_enter();\n    if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {\n        while(\n            !(subghz_devices_is_async_complete_tx(device) ||\n              cli_is_pipe_broken_or_is_etx_next_char(pipe))) {\n            printf(\".\");\n            fflush(stdout);\n            furi_delay_ms(333);\n        }\n        subghz_devices_stop_async_tx(device);\n\n    } else {\n        printf(\"Transmission on this frequency is restricted in your region\\r\\n\");\n    }\n\n    subghz_devices_sleep(device);\n    subghz_devices_end(device);\n    subghz_devices_deinit();\n    subghz_cli_radio_device_power_off();\n\n    furi_hal_power_suppress_charge_exit();\n\n    flipper_format_free(flipper_format);\n    subghz_transmitter_free(transmitter);\n    subghz_environment_free(environment);\n}\n\ntypedef struct {\n    volatile bool overrun;\n    FuriStreamBuffer* stream;\n    size_t packet_count;\n} SubGhzCliCommandRx;\n\nstatic void subghz_cli_command_rx_capture_callback(bool level, uint32_t duration, void* context) {\n    SubGhzCliCommandRx* instance = context;\n\n    LevelDuration level_duration = level_duration_make(level, duration);\n    if(instance->overrun) {\n        instance->overrun = false;\n        level_duration = level_duration_reset();\n    }\n    size_t ret =\n        furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);\n    if(sizeof(LevelDuration) != ret) instance->overrun = true;\n}\n\nstatic void subghz_cli_command_rx_callback(\n    SubGhzReceiver* receiver,\n    SubGhzProtocolDecoderBase* decoder_base,\n    void* context) {\n    SubGhzCliCommandRx* instance = context;\n    instance->packet_count++;\n\n    FuriString* text;\n    text = furi_string_alloc();\n    subghz_protocol_decoder_base_get_string(decoder_base, text);\n    subghz_receiver_reset(receiver);\n    printf(\"%s\", furi_string_get_cstr(text));\n    furi_string_free(text);\n}\n\nvoid subghz_cli_command_rx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    uint32_t frequency = 433920000;\n    uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT\n\n    if(furi_string_size(args)) {\n        char* args_cstr = (char*)furi_string_get_cstr(args);\n        StrintParseError parse_err = StrintParseNoError;\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10);\n        if(parse_err) {\n            cli_print_usage(\n                \"subghz rx\",\n                \"<Frequency: in Hz> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>\",\n                furi_string_get_cstr(args));\n            return;\n        }\n    }\n    subghz_devices_init();\n    const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind);\n    if(!subghz_devices_is_frequency_valid(device, frequency)) {\n        printf(\n            \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\", frequency);\n        subghz_devices_deinit();\n        subghz_cli_radio_device_power_off();\n        return;\n    }\n\n    // Allocate context and buffers\n    SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));\n    instance->stream =\n        furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));\n\n    SubGhzEnvironment* environment = subghz_cli_environment_init();\n\n    SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);\n    subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);\n    subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);\n\n    // Configure radio\n    subghz_devices_begin(device);\n    subghz_devices_reset(device);\n    subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL);\n    frequency = subghz_devices_set_frequency(device, frequency);\n\n    furi_hal_power_suppress_charge_enter();\n\n    // Prepare and start RX\n    subghz_devices_start_async_rx(device, subghz_cli_command_rx_capture_callback, instance);\n\n    // Wait for packets to arrive\n    printf(\n        \"Listening at frequency: %lu device: %lu. Press CTRL+C to stop\\r\\n\",\n        frequency,\n        device_ind);\n    LevelDuration level_duration;\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        int ret = furi_stream_buffer_receive(\n            instance->stream, &level_duration, sizeof(LevelDuration), 10);\n        if(ret == sizeof(LevelDuration)) {\n            if(level_duration_is_reset(level_duration)) {\n                printf(\".\");\n                subghz_receiver_reset(receiver);\n            } else {\n                bool level = level_duration_get_level(level_duration);\n                uint32_t duration = level_duration_get_duration(level_duration);\n                subghz_receiver_decode(receiver, level, duration);\n            }\n        }\n    }\n\n    // Shutdown radio\n    subghz_devices_stop_async_rx(device);\n    subghz_devices_sleep(device);\n    subghz_devices_end(device);\n    subghz_devices_deinit();\n    subghz_cli_radio_device_power_off();\n\n    furi_hal_power_suppress_charge_exit();\n\n    printf(\"\\r\\nPackets received %zu\\r\\n\", instance->packet_count);\n\n    // Cleanup\n    subghz_receiver_free(receiver);\n    subghz_environment_free(environment);\n    furi_stream_buffer_free(instance->stream);\n    free(instance);\n}\n\nvoid subghz_cli_command_rx_raw(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    uint32_t frequency = 433920000;\n\n    if(furi_string_size(args)) {\n        if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) !=\n           StrintParseNoError) {\n            cli_print_usage(\"subghz rx\", \"<Frequency: in Hz>\", furi_string_get_cstr(args));\n            return;\n        }\n        if(!furi_hal_subghz_is_frequency_valid(frequency)) {\n            printf(\n                \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\",\n                frequency);\n            return;\n        }\n    }\n\n    // Allocate context and buffers\n    SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));\n    instance->stream =\n        furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));\n\n    // Configure radio\n    furi_hal_subghz_reset();\n    furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs);\n    frequency = furi_hal_subghz_set_frequency_and_path(frequency);\n    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);\n\n    furi_hal_power_suppress_charge_enter();\n\n    // Prepare and start RX\n    furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);\n\n    // Wait for packets to arrive\n    printf(\"Listening at %lu. Press CTRL+C to stop\\r\\n\", frequency);\n    LevelDuration level_duration;\n    size_t counter = 0;\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        int ret = furi_stream_buffer_receive(\n            instance->stream, &level_duration, sizeof(LevelDuration), 10);\n        if(ret == 0) {\n            continue;\n        }\n        if(ret != sizeof(LevelDuration)) {\n            puts(\"stream corrupt\");\n            break;\n        }\n        if(level_duration_is_reset(level_duration)) {\n            puts(\". \");\n        } else {\n            bool level = level_duration_get_level(level_duration);\n            uint32_t duration = level_duration_get_duration(level_duration);\n            printf(\"%c%lu \", level ? '+' : '-', duration);\n        }\n        furi_thread_stdout_flush();\n        counter++;\n        if(counter > 255) {\n            puts(\"\\r\\n\");\n            counter = 0;\n        }\n    }\n\n    // Shutdown radio\n    furi_hal_subghz_stop_async_rx();\n    furi_hal_subghz_sleep();\n\n    furi_hal_power_suppress_charge_exit();\n\n    // Cleanup\n    furi_stream_buffer_free(instance->stream);\n    free(instance);\n}\n\nvoid subghz_cli_command_decode_raw(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* file_name;\n    file_name = furi_string_alloc();\n    furi_string_set(file_name, EXT_PATH(\"subghz/test.sub\"));\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    uint32_t temp_data32;\n    bool check_file = false;\n\n    do {\n        if(furi_string_size(args)) {\n            if(!args_read_string_and_trim(args, file_name)) {\n                cli_print_usage(\n                    \"subghz decode_raw\", \"<file_name: path_RAW_file>\", furi_string_get_cstr(args));\n                break;\n            }\n        }\n\n        if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {\n            printf(\n                \"subghz decode_raw \\033[0;31mError open file\\033[0m %s\\r\\n\",\n                furi_string_get_cstr(file_name));\n            break;\n        }\n\n        if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {\n            printf(\"subghz decode_raw \\033[0;31mMissing or incorrect header\\033[0m\\r\\n\");\n            break;\n        }\n\n        if(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE) &&\n           temp_data32 == SUBGHZ_KEY_FILE_VERSION) {\n        } else {\n            printf(\"subghz decode_raw \\033[0;31mType or version mismatch\\033[0m\\r\\n\");\n            break;\n        }\n\n        check_file = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    flipper_format_free(fff_data_file);\n    furi_record_close(RECORD_STORAGE);\n\n    if(check_file) {\n        // Allocate context\n        SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));\n\n        SubGhzEnvironment* environment = subghz_cli_environment_init();\n\n        SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);\n        subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);\n        subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);\n\n        SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc();\n        if(subghz_file_encoder_worker_start(\n               file_worker_encoder, furi_string_get_cstr(file_name), NULL)) {\n            //the worker needs a file in order to open and read part of the file\n            furi_delay_ms(100);\n        }\n\n        printf(\n            \"Listening at \\033[0;33m%s\\033[0m.\\r\\n\\r\\nPress CTRL+C to stop\\r\\n\\r\\n\",\n            furi_string_get_cstr(file_name));\n\n        LevelDuration level_duration;\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_us(500); //you need to have time to read from the file from the SD card\n            level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder);\n            if(!level_duration_is_reset(level_duration)) {\n                bool level = level_duration_get_level(level_duration);\n                uint32_t duration = level_duration_get_duration(level_duration);\n                subghz_receiver_decode(receiver, level, duration);\n            } else {\n                break;\n            }\n        }\n\n        printf(\"\\r\\nPackets received \\033[0;32m%zu\\033[0m\\r\\n\", instance->packet_count);\n\n        // Cleanup\n        subghz_receiver_free(receiver);\n        subghz_environment_free(environment);\n\n        if(subghz_file_encoder_worker_is_running(file_worker_encoder)) {\n            subghz_file_encoder_worker_stop(file_worker_encoder);\n        }\n        subghz_file_encoder_worker_free(file_worker_encoder);\n        free(instance);\n    }\n    furi_string_free(file_name);\n}\n\nstatic FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) {\n    FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE;\n    if(!strcmp(preset_name, \"FuriHalSubGhzPresetOok270Async\")) {\n        preset = FuriHalSubGhzPresetOok270Async;\n    } else if(!strcmp(preset_name, \"FuriHalSubGhzPresetOok650Async\")) {\n        preset = FuriHalSubGhzPresetOok650Async;\n    } else if(!strcmp(preset_name, \"FuriHalSubGhzPreset2FSKDev238Async\")) {\n        preset = FuriHalSubGhzPreset2FSKDev238Async;\n    } else if(!strcmp(preset_name, \"FuriHalSubGhzPreset2FSKDev476Async\")) {\n        preset = FuriHalSubGhzPreset2FSKDev476Async;\n    } else if(!strcmp(preset_name, \"FuriHalSubGhzPresetCustom\")) {\n        preset = FuriHalSubGhzPresetCustom;\n    } else {\n        printf(\"subghz tx_from_file: unknown preset\");\n    }\n    return preset;\n}\n\nvoid subghz_cli_command_tx_from_file(PipeSide* pipe, FuriString* args, void* context) { // -V524\n    UNUSED(context);\n    FuriString* file_name;\n    file_name = furi_string_alloc();\n    furi_string_set(file_name, EXT_PATH(\"subghz/test.sub\"));\n    uint32_t repeat = 10;\n    uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);\n    FlipperFormat* fff_data_raw = flipper_format_string_alloc();\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    uint32_t temp_data32;\n    bool check_file = false;\n    const SubGhzDevice* device = NULL;\n\n    uint32_t frequency = 0;\n    SubGhzTransmitter* transmitter = NULL;\n\n    subghz_devices_init();\n\n    SubGhzEnvironment* environment = subghz_cli_environment_init();\n\n    do {\n        if(furi_string_size(args)) {\n            if(!args_read_string_and_trim(args, file_name)) {\n                cli_print_usage(\n                    \"subghz tx_from_file: \",\n                    \"<file_name: path_file> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>\",\n                    furi_string_get_cstr(args));\n                break;\n            }\n        }\n\n        if(furi_string_size(args)) {\n            char* args_cstr = (char*)furi_string_get_cstr(args);\n            StrintParseError parse_err = StrintParseNoError;\n            parse_err |= strint_to_uint32(args_cstr, &args_cstr, &repeat, 10);\n            parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10);\n            if(parse_err) {\n                cli_print_usage(\n                    \"subghz tx_from_file:\",\n                    \"<file_name: path_file> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>\",\n                    furi_string_get_cstr(args));\n                break;\n            }\n        }\n\n        device = subghz_cli_command_get_device(&device_ind);\n        if(device == NULL) {\n            printf(\"subghz tx_from_file: \\033[0;31mError device not found\\033[0m\\r\\n\");\n            break;\n        }\n\n        if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {\n            printf(\n                \"subghz tx_from_file: \\033[0;31mError open file\\033[0m %s\\r\\n\",\n                furi_string_get_cstr(file_name));\n            break;\n        }\n\n        if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {\n            printf(\"subghz tx_from_file: \\033[0;31mMissing or incorrect header\\033[0m\\r\\n\");\n            break;\n        }\n\n        if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||\n            (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&\n           temp_data32 == SUBGHZ_KEY_FILE_VERSION) {\n        } else {\n            printf(\"subghz tx_from_file: \\033[0;31mType or version mismatch\\033[0m\\r\\n\");\n            break;\n        }\n\n        //Load frequency\n        if(!flipper_format_read_uint32(fff_data_file, \"Frequency\", &frequency, 1)) {\n            printf(\"subghz tx_from_file: \\033[0;31mMissing Frequency\\033[0m\\r\\n\");\n            break;\n        }\n\n        if(!subghz_devices_is_frequency_valid(device, frequency)) {\n            printf(\"subghz tx_from_file: \\033[0;31mFrequency not supported\\033[0m\\r\\n\");\n            break;\n        }\n\n        //Load preset\n        if(!flipper_format_read_string(fff_data_file, \"Preset\", temp_str)) {\n            printf(\"subghz tx_from_file: \\033[0;31mMissing Preset\\033[0m\\r\\n\");\n            break;\n        }\n\n        subghz_devices_begin(device);\n        subghz_devices_reset(device);\n\n        if(!strcmp(furi_string_get_cstr(temp_str), \"FuriHalSubGhzPresetCustom\")) {\n            uint8_t* custom_preset_data;\n            uint32_t custom_preset_data_size;\n            if(!flipper_format_get_value_count(fff_data_file, \"Custom_preset_data\", &temp_data32))\n                break;\n            if(!temp_data32 || (temp_data32 % 2)) {\n                printf(\"subghz tx_from_file: \\033[0;31mCustom_preset_data size error\\033[0m\\r\\n\");\n                break;\n            }\n            custom_preset_data_size = sizeof(uint8_t) * temp_data32;\n            custom_preset_data = malloc(custom_preset_data_size);\n            if(!flipper_format_read_hex(\n                   fff_data_file,\n                   \"Custom_preset_data\",\n                   custom_preset_data,\n                   custom_preset_data_size)) {\n                printf(\"subghz tx_from_file: \\033[0;31mCustom_preset_data read error\\033[0m\\r\\n\");\n                break;\n            }\n            subghz_devices_load_preset(\n                device,\n                subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)),\n                custom_preset_data);\n            free(custom_preset_data);\n        } else {\n            subghz_devices_load_preset(\n                device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL);\n        }\n\n        subghz_devices_set_frequency(device, frequency);\n\n        //Load protocol\n        if(!flipper_format_read_string(fff_data_file, \"Protocol\", temp_str)) {\n            printf(\"subghz tx_from_file: \\033[0;31mMissing protocol\\033[0m\\r\\n\");\n            break;\n        }\n\n        SubGhzProtocolStatus status;\n        bool is_init_protocol = true;\n        if(!strcmp(furi_string_get_cstr(temp_str), \"RAW\")) { // if RAW protocol\n            subghz_protocol_raw_gen_fff_data(\n                fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device));\n\n            transmitter =\n                subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str));\n            if(transmitter == NULL) {\n                printf(\"subghz tx_from_file: \\033[0;31mError transmitter\\033[0m\\r\\n\");\n                is_init_protocol = false;\n            }\n\n            if(is_init_protocol) {\n                status = subghz_transmitter_deserialize(transmitter, fff_data_raw);\n                if(status != SubGhzProtocolStatusOk) {\n                    printf(\n                        \"subghz tx_from_file: \\033[0;31mError deserialize protocol\\033[0m %d\\r\\n\",\n                        status);\n                    is_init_protocol = false;\n                }\n            }\n\n        } else { //if not RAW protocol\n            flipper_format_insert_or_update_uint32(fff_data_file, \"Repeat\", &repeat, 1);\n\n            transmitter =\n                subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str));\n            if(transmitter == NULL) {\n                printf(\"subghz tx_from_file: \\033[0;31mError transmitter\\033[0m\\r\\n\");\n                is_init_protocol = false;\n            }\n            if(is_init_protocol) {\n                status = subghz_transmitter_deserialize(transmitter, fff_data_file);\n                if(status != SubGhzProtocolStatusOk) {\n                    printf(\n                        \"subghz tx_from_file: \\033[0;31mError deserialize protocol\\033[0m %d\\r\\n\",\n                        status);\n                    is_init_protocol = false;\n                }\n            }\n\n            flipper_format_delete_key(fff_data_file, \"Repeat\");\n        }\n\n        if(is_init_protocol) {\n            check_file = true;\n        } else {\n            subghz_devices_sleep(device);\n            subghz_devices_end(device);\n            subghz_transmitter_free(transmitter);\n        }\n\n    } while(false);\n\n    flipper_format_free(fff_data_file);\n    furi_record_close(RECORD_STORAGE);\n\n    if(check_file) {\n        furi_hal_power_suppress_charge_enter();\n\n        printf(\n            \"Listening at \\033[0;33m%s\\033[0m. Frequency=%lu, Protocol=%s\\r\\n\\r\\nPress CTRL+C to stop\\r\\n\\r\\n\",\n            furi_string_get_cstr(file_name),\n            frequency,\n            furi_string_get_cstr(temp_str));\n        do {\n            //delay in downloading files and other preparatory processes\n            furi_delay_ms(200);\n            if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {\n                while(\n                    !(subghz_devices_is_async_complete_tx(device) ||\n                      cli_is_pipe_broken_or_is_etx_next_char(pipe))) {\n                    printf(\".\");\n                    fflush(stdout);\n                    furi_delay_ms(333);\n                }\n                subghz_devices_stop_async_tx(device);\n\n            } else {\n                printf(\"Transmission on this frequency is restricted in your region\\r\\n\");\n            }\n\n            if(!strcmp(furi_string_get_cstr(temp_str), \"RAW\")) {\n                subghz_transmitter_stop(transmitter);\n                repeat--;\n                if(!cli_is_pipe_broken_or_is_etx_next_char(pipe) && repeat)\n                    subghz_transmitter_deserialize(transmitter, fff_data_raw);\n            }\n\n        } while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) &&\n                (repeat && !strcmp(furi_string_get_cstr(temp_str), \"RAW\")));\n\n        subghz_devices_sleep(device);\n        subghz_devices_end(device);\n        subghz_cli_radio_device_power_off();\n\n        furi_hal_power_suppress_charge_exit();\n\n        subghz_transmitter_free(transmitter);\n    }\n    flipper_format_free(fff_data_raw);\n    furi_string_free(file_name);\n    furi_string_free(temp_str);\n    subghz_devices_deinit();\n    subghz_environment_free(environment);\n}\n\nstatic void subghz_cli_command_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"subghz <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n\n    printf(\n        \"\\tchat <frequency:in Hz> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\\t - Chat with other Flippers\\r\\n\");\n    printf(\n        \"\\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\\t - Transmitting key\\r\\n\");\n    printf(\"\\trx <frequency:in Hz> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\\t - Receive\\r\\n\");\n    printf(\"\\trx_raw <frequency:in Hz>\\t - Receive RAW\\r\\n\");\n    printf(\"\\tdecode_raw <file_name: path_RAW_file>\\t - Testing\\r\\n\");\n    printf(\n        \"\\ttx_from_file <file_name: path_file> <repeat: count> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\\t - Transmitting from file\\r\\n\");\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        printf(\"\\r\\n\");\n        printf(\"  debug cmd:\\r\\n\");\n        printf(\"\\ttx_carrier <frequency:in Hz>\\t - Transmitting carrier\\r\\n\");\n        printf(\"\\trx_carrier <frequency:in Hz>\\t - Receive carrier\\r\\n\");\n        printf(\n            \"\\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\\t - Encrypt keeloq manufacture keys\\r\\n\");\n        printf(\n            \"\\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\\t - Encrypt RAW data\\r\\n\");\n    }\n}\n\nstatic void subghz_cli_command_encrypt_keeloq(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    uint8_t iv[16];\n\n    FuriString* source;\n    FuriString* destination;\n    source = furi_string_alloc();\n    destination = furi_string_alloc();\n\n    SubGhzKeystore* keystore = subghz_keystore_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, source)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!args_read_string_and_trim(args, destination)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!args_read_hex_bytes(args, iv, 16)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!subghz_keystore_load(keystore, furi_string_get_cstr(source))) {\n            printf(\"Failed to load Keystore\");\n            break;\n        }\n\n        if(!subghz_keystore_save(keystore, furi_string_get_cstr(destination), iv)) {\n            printf(\"Failed to save Keystore\");\n            break;\n        }\n    } while(false);\n\n    subghz_keystore_free(keystore);\n    furi_string_free(destination);\n    furi_string_free(source);\n}\n\nstatic void subghz_cli_command_encrypt_raw(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    uint8_t iv[16];\n\n    FuriString* source;\n    FuriString* destination;\n    source = furi_string_alloc();\n    destination = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, source)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!args_read_string_and_trim(args, destination)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!args_read_hex_bytes(args, iv, 16)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(!subghz_keystore_raw_encrypted_save(\n               furi_string_get_cstr(source), furi_string_get_cstr(destination), iv)) {\n            printf(\"Failed to save Keystore\");\n            break;\n        }\n\n    } while(false);\n\n    furi_string_free(destination);\n    furi_string_free(source);\n}\n\nstatic void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {\n    uint32_t frequency = 433920000;\n    uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT\n\n    if(furi_string_size(args)) {\n        char* args_cstr = (char*)furi_string_get_cstr(args);\n        StrintParseError parse_err = StrintParseNoError;\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10);\n        parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10);\n        if(parse_err) {\n            cli_print_usage(\n                \"subghz chat\",\n                \"<Frequency: in Hz> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>\",\n                furi_string_get_cstr(args));\n            return;\n        }\n    }\n    subghz_devices_init();\n    const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind);\n    if(!subghz_devices_is_frequency_valid(device, frequency)) {\n        printf(\n            \"Frequency must be in \" SUBGHZ_FREQUENCY_RANGE_STR \" range, not %lu\\r\\n\", frequency);\n        subghz_devices_deinit();\n        subghz_cli_radio_device_power_off();\n        return;\n    }\n    if(!furi_hal_region_is_frequency_allowed(frequency)) {\n        printf(\n            \"In your region, only reception on this frequency (%lu) is allowed,\\r\\n\"\n            \"the actual operation of the application is not possible\\r\\n \",\n            frequency);\n        return;\n    }\n\n    SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(pipe);\n\n    if(!subghz_chat_worker_start(subghz_chat, device, frequency)) {\n        printf(\"Startup error SubGhzChatWorker\\r\\n\");\n\n        if(subghz_chat_worker_is_running(subghz_chat)) {\n            subghz_chat_worker_stop(subghz_chat);\n            subghz_chat_worker_free(subghz_chat);\n        }\n        return;\n    }\n\n    printf(\"Receiving at frequency %lu Hz\\r\\n\", frequency);\n    printf(\"Press CTRL+C to stop\\r\\n\");\n\n    furi_hal_power_suppress_charge_enter();\n\n    size_t message_max_len = 64;\n    uint8_t message[64] = {0};\n    FuriString* input;\n    input = furi_string_alloc();\n    FuriString* name;\n    name = furi_string_alloc();\n    FuriString* output;\n    output = furi_string_alloc();\n    FuriString* sysmsg;\n    sysmsg = furi_string_alloc();\n    bool exit = false;\n    SubGhzChatEvent chat_event;\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n\n    furi_string_printf(name, \"\\033[0;33m%s\\033[0m: \", furi_hal_version_get_name_ptr());\n    furi_string_set(input, name);\n    printf(\"%s\", furi_string_get_cstr(input));\n    fflush(stdout);\n\n    while(!exit) {\n        chat_event = subghz_chat_worker_get_event_chat(subghz_chat);\n        switch(chat_event.event) {\n        case SubGhzChatEventInputData:\n            if(chat_event.c == CliKeyETX) {\n                printf(\"\\r\\n\");\n                chat_event.event = SubGhzChatEventUserExit;\n                subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);\n                break;\n            } else if((chat_event.c == CliKeyBackspace) || (chat_event.c == CliKeyDEL)) {\n                size_t len = furi_string_utf8_length(input);\n                if(len > furi_string_utf8_length(name)) {\n                    printf(\"%s\", \"\\e[D\\e[1P\");\n                    fflush(stdout);\n                    //delete 1 char UTF\n                    const char* str = furi_string_get_cstr(input);\n                    size_t size = 0;\n                    FuriStringUTF8State s = FuriStringUTF8StateStarting;\n                    FuriStringUnicodeValue u = 0;\n                    furi_string_reset(sysmsg);\n                    while(*str) {\n                        furi_string_utf8_decode(*str, &s, &u);\n                        if((s == FuriStringUTF8StateError) || s == FuriStringUTF8StateStarting) {\n                            furi_string_utf8_push(sysmsg, u);\n                            if(++size >= len - 1) break;\n                            s = FuriStringUTF8StateStarting;\n                        }\n                        str++;\n                    }\n                    furi_string_set(input, sysmsg);\n                }\n            } else if(chat_event.c == CliKeyCR) {\n                printf(\"\\r\\n\");\n                furi_string_push_back(input, '\\r');\n                furi_string_push_back(input, '\\n');\n                while(!subghz_chat_worker_write(\n                    subghz_chat,\n                    (uint8_t*)furi_string_get_cstr(input),\n                    strlen(furi_string_get_cstr(input)))) {\n                    furi_delay_ms(10);\n                }\n\n                furi_string_printf(input, \"%s\", furi_string_get_cstr(name));\n                printf(\"%s\", furi_string_get_cstr(input));\n                fflush(stdout);\n            } else if(chat_event.c == CliKeyLF) {\n                //cut out the symbol \\n\n            } else {\n                putc(chat_event.c, stdout);\n                fflush(stdout);\n                furi_string_push_back(input, chat_event.c);\n                break;\n            case SubGhzChatEventRXData:\n                do {\n                    memset(message, 0x00, message_max_len);\n                    size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len);\n                    for(size_t i = 0; i < len; i++) {\n                        furi_string_push_back(output, message[i]);\n                        if(message[i] == '\\n') {\n                            printf(\"\\r\");\n                            for(uint8_t i = 0; i < 80; i++) {\n                                printf(\" \");\n                            }\n                            printf(\"\\r %s\", furi_string_get_cstr(output));\n                            printf(\"%s\", furi_string_get_cstr(input));\n                            fflush(stdout);\n                            furi_string_reset(output);\n                        }\n                    }\n                } while(subghz_chat_worker_available(subghz_chat));\n                break;\n            case SubGhzChatEventNewMessage:\n                notification_message(notification, &sequence_single_vibro);\n                break;\n            case SubGhzChatEventUserEntrance:\n                furi_string_printf(\n                    sysmsg,\n                    \"\\033[0;34m%s joined chat.\\033[0m\\r\\n\",\n                    furi_hal_version_get_name_ptr());\n                subghz_chat_worker_write(\n                    subghz_chat,\n                    (uint8_t*)furi_string_get_cstr(sysmsg),\n                    strlen(furi_string_get_cstr(sysmsg)));\n                break;\n            case SubGhzChatEventUserExit:\n                furi_string_printf(\n                    sysmsg, \"\\033[0;31m%s left chat.\\033[0m\\r\\n\", furi_hal_version_get_name_ptr());\n                subghz_chat_worker_write(\n                    subghz_chat,\n                    (uint8_t*)furi_string_get_cstr(sysmsg),\n                    strlen(furi_string_get_cstr(sysmsg)));\n                furi_delay_ms(10);\n                exit = true;\n                break;\n            default:\n                FURI_LOG_W(\"SubGhzChat\", \"Error event\");\n                break;\n            }\n        }\n        if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            printf(\"\\r\\n\");\n            chat_event.event = SubGhzChatEventUserExit;\n            subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);\n        }\n    }\n\n    furi_string_free(input);\n    furi_string_free(name);\n    furi_string_free(output);\n    furi_string_free(sysmsg);\n\n    subghz_devices_deinit();\n    subghz_cli_radio_device_power_off();\n\n    furi_hal_power_suppress_charge_exit();\n    furi_record_close(RECORD_NOTIFICATION);\n\n    if(subghz_chat_worker_is_running(subghz_chat)) {\n        subghz_chat_worker_stop(subghz_chat);\n        subghz_chat_worker_free(subghz_chat);\n    }\n    printf(\"\\r\\nExit chat\\r\\n\");\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            subghz_cli_command_print_usage();\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"chat\") == 0) {\n            subghz_cli_command_chat(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"tx\") == 0) {\n            subghz_cli_command_tx(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"rx\") == 0) {\n            subghz_cli_command_rx(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"rx_raw\") == 0) {\n            subghz_cli_command_rx_raw(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"decode_raw\") == 0) {\n            subghz_cli_command_decode_raw(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"tx_from_file\") == 0) {\n            subghz_cli_command_tx_from_file(pipe, args, context);\n            break;\n        }\n\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n            if(furi_string_cmp_str(cmd, \"encrypt_keeloq\") == 0) {\n                subghz_cli_command_encrypt_keeloq(pipe, args);\n                break;\n            }\n\n            if(furi_string_cmp_str(cmd, \"encrypt_raw\") == 0) {\n                subghz_cli_command_encrypt_raw(pipe, args);\n                break;\n            }\n\n            if(furi_string_cmp_str(cmd, \"tx_carrier\") == 0) {\n                subghz_cli_command_tx_carrier(pipe, args, context);\n                break;\n            }\n\n            if(furi_string_cmp_str(cmd, \"rx_carrier\") == 0) {\n                subghz_cli_command_rx_carrier(pipe, args, context);\n                break;\n            }\n        }\n\n        subghz_cli_command_print_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n\nCLI_COMMAND_INTERFACE(subghz, execute, CliCommandFlagDefault, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/main/subghz/subghz_cli.h",
    "content": "#pragma once\n\nvoid subghz_on_system_start(void);\n"
  },
  {
    "path": "applications/main/subghz/subghz_history.c",
    "content": "#include \"subghz_history.h\"\n#include <lib/subghz/receiver.h>\n#include <lib/subghz/protocols/came.h>\n\n#include <furi.h>\n\n#define SUBGHZ_HISTORY_MAX       50\n#define SUBGHZ_HISTORY_FREE_HEAP 20480\n\n#define TAG \"SubGhzHistory\"\n\ntypedef struct {\n    FuriString* item_str;\n    FlipperFormat* flipper_string;\n    uint8_t type;\n    SubGhzRadioPreset* preset;\n} SubGhzHistoryItem;\n\nARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) //-V658\n\n#define M_OPL_SubGhzHistoryItemArray_t() ARRAY_OPLIST(SubGhzHistoryItemArray, M_POD_OPLIST)\n\ntypedef struct {\n    SubGhzHistoryItemArray_t data;\n} SubGhzHistoryStruct;\n\nstruct SubGhzHistory {\n    uint32_t last_update_timestamp;\n    uint16_t last_index_write;\n    uint8_t code_last_hash_data;\n    FuriString* tmp_string;\n    SubGhzHistoryStruct* history;\n};\n\nSubGhzHistory* subghz_history_alloc(void) {\n    SubGhzHistory* instance = malloc(sizeof(SubGhzHistory));\n    instance->tmp_string = furi_string_alloc();\n    instance->history = malloc(sizeof(SubGhzHistoryStruct));\n    SubGhzHistoryItemArray_init(instance->history->data);\n    return instance;\n}\n\nvoid subghz_history_free(SubGhzHistory* instance) {\n    furi_assert(instance);\n    furi_string_free(instance->tmp_string);\n    for\n        M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {\n            furi_string_free(item->item_str);\n            furi_string_free(item->preset->name);\n            free(item->preset);\n            flipper_format_free(item->flipper_string);\n            item->type = 0;\n        }\n    SubGhzHistoryItemArray_clear(instance->history->data);\n    free(instance->history);\n    free(instance);\n}\n\nuint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    return item->preset->frequency;\n}\n\nSubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    return item->preset;\n}\n\nconst char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    return furi_string_get_cstr(item->preset->name);\n}\n\nvoid subghz_history_reset(SubGhzHistory* instance) {\n    furi_assert(instance);\n    furi_string_reset(instance->tmp_string);\n    for\n        M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {\n            furi_string_free(item->item_str);\n            furi_string_free(item->preset->name);\n            free(item->preset);\n            flipper_format_free(item->flipper_string);\n            item->type = 0;\n        }\n    SubGhzHistoryItemArray_reset(instance->history->data);\n    instance->last_index_write = 0;\n    instance->code_last_hash_data = 0;\n}\n\nuint16_t subghz_history_get_item(SubGhzHistory* instance) {\n    furi_assert(instance);\n    return instance->last_index_write;\n}\n\nuint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    return item->type;\n}\n\nconst char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    flipper_format_rewind(item->flipper_string);\n    if(!flipper_format_read_string(item->flipper_string, \"Protocol\", instance->tmp_string)) {\n        FURI_LOG_E(TAG, \"Missing Protocol\");\n        furi_string_reset(instance->tmp_string);\n    }\n    return furi_string_get_cstr(instance->tmp_string);\n}\n\nFlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) {\n    furi_assert(instance);\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    if(item->flipper_string) {\n        return item->flipper_string;\n    } else {\n        return NULL;\n    }\n}\nbool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) {\n    furi_assert(instance);\n    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {\n        if(output != NULL) furi_string_printf(output, \"    Free heap LOW\");\n        return true;\n    }\n    if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {\n        if(output != NULL) furi_string_printf(output, \"   Memory is FULL\");\n        return true;\n    }\n    if(output != NULL)\n        furi_string_printf(output, \"%02u/%02u\", instance->last_index_write, SUBGHZ_HISTORY_MAX);\n    return false;\n}\n\nvoid subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) {\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);\n    furi_string_set(output, item->item_str);\n}\n\nbool subghz_history_add_to_history(\n    SubGhzHistory* instance,\n    void* context,\n    SubGhzRadioPreset* preset) {\n    furi_assert(instance);\n    furi_assert(context);\n\n    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false;\n    if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;\n\n    SubGhzProtocolDecoderBase* decoder_base = context;\n    if((instance->code_last_hash_data ==\n        subghz_protocol_decoder_base_get_hash_data(decoder_base)) &&\n       ((furi_get_tick() - instance->last_update_timestamp) < 500)) {\n        instance->last_update_timestamp = furi_get_tick();\n        return false;\n    }\n\n    instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);\n    instance->last_update_timestamp = furi_get_tick();\n\n    FuriString* text;\n    text = furi_string_alloc();\n    SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);\n    item->preset = malloc(sizeof(SubGhzRadioPreset));\n    item->type = decoder_base->protocol->type;\n    item->preset->frequency = preset->frequency;\n    item->preset->name = furi_string_alloc();\n    furi_string_set(item->preset->name, preset->name);\n    item->preset->data = preset->data;\n    item->preset->data_size = preset->data_size;\n\n    item->item_str = furi_string_alloc();\n    item->flipper_string = flipper_format_string_alloc();\n    subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);\n\n    do {\n        if(!flipper_format_rewind(item->flipper_string)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n        if(!flipper_format_read_string(item->flipper_string, \"Protocol\", instance->tmp_string)) {\n            FURI_LOG_E(TAG, \"Missing Protocol\");\n            break;\n        }\n        if(!strcmp(furi_string_get_cstr(instance->tmp_string), \"KeeLoq\")) {\n            furi_string_set(instance->tmp_string, \"KL \");\n            if(!flipper_format_read_string(item->flipper_string, \"Manufacture\", text)) {\n                FURI_LOG_E(TAG, \"Missing Protocol\");\n                break;\n            }\n            furi_string_cat(instance->tmp_string, text);\n        } else if(!strcmp(furi_string_get_cstr(instance->tmp_string), \"Star Line\")) {\n            furi_string_set(instance->tmp_string, \"SL \");\n            if(!flipper_format_read_string(item->flipper_string, \"Manufacture\", text)) {\n                FURI_LOG_E(TAG, \"Missing Protocol\");\n                break;\n            }\n            furi_string_cat(instance->tmp_string, text);\n        }\n        if(!flipper_format_rewind(item->flipper_string)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        if(!flipper_format_read_hex(item->flipper_string, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_D(TAG, \"No Key\");\n        }\n        uint64_t data = 0;\n        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {\n            data = (data << 8) | key_data[i];\n        }\n        if(data != 0) {\n            if(!(uint32_t)(data >> 32)) {\n                furi_string_printf(\n                    item->item_str,\n                    \"%s %lX\",\n                    furi_string_get_cstr(instance->tmp_string),\n                    (uint32_t)(data & 0xFFFFFFFF));\n            } else {\n                furi_string_printf(\n                    item->item_str,\n                    \"%s %lX%08lX\",\n                    furi_string_get_cstr(instance->tmp_string),\n                    (uint32_t)(data >> 32),\n                    (uint32_t)(data & 0xFFFFFFFF));\n            }\n        } else {\n            furi_string_printf(item->item_str, \"%s\", furi_string_get_cstr(instance->tmp_string));\n        }\n\n    } while(false);\n\n    furi_string_free(text);\n    instance->last_index_write++;\n    return true;\n}\n"
  },
  {
    "path": "applications/main/subghz/subghz_history.h",
    "content": "\n#pragma once\n\n#include <math.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <lib/flipper_format/flipper_format.h>\n#include <lib/subghz/types.h>\n\ntypedef struct SubGhzHistory SubGhzHistory;\n\n/** Allocate SubGhzHistory\n * \n * @return SubGhzHistory* \n */\nSubGhzHistory* subghz_history_alloc(void);\n\n/** Free SubGhzHistory\n * \n * @param instance - SubGhzHistory instance\n */\nvoid subghz_history_free(SubGhzHistory* instance);\n\n/** Clear history\n * \n * @param instance - SubGhzHistory instance\n */\nvoid subghz_history_reset(SubGhzHistory* instance);\n\n/** Get frequency to history[idx]\n * \n * @param instance  - SubGhzHistory instance\n * @param idx       - record index  \n * @return frequency - frequency Hz\n */\nuint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx);\n\nSubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx);\n\n/** Get preset to history[idx]\n * \n * @param instance  - SubGhzHistory instance\n * @param idx       - record index  \n * @return preset   - preset name\n */\nconst char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx);\n\n/** Get history index write \n * \n * @param instance  - SubGhzHistory instance\n * @return idx      - current record index  \n */\nuint16_t subghz_history_get_item(SubGhzHistory* instance);\n\n/** Get type protocol to history[idx]\n * \n * @param instance  - SubGhzHistory instance\n * @param idx       - record index  \n * @return type      - type protocol  \n */\nuint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx);\n\n/** Get name protocol to history[idx]\n * \n * @param instance  - SubGhzHistory instance\n * @param idx       - record index  \n * @return name      - const char* name protocol  \n */\nconst char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx);\n\n/** Get string item menu to history[idx]\n * \n * @param instance  - SubGhzHistory instance\n * @param output    - FuriString* output\n * @param idx       - record index\n */\nvoid subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx);\n\n/** Get string the remaining number of records to history\n * \n * @param instance  - SubGhzHistory instance\n * @param output    - FuriString* output\n * @return bool - is FUUL\n */\nbool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output);\n\n/** Add protocol to history\n * \n * @param instance  - SubGhzHistory instance\n * @param context    - SubGhzProtocolCommon context\n * @param preset    - SubGhzRadioPreset preset\n * @return bool;\n */\nbool subghz_history_add_to_history(\n    SubGhzHistory* instance,\n    void* context,\n    SubGhzRadioPreset* preset);\n\n/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data\n * \n * @param instance  - SubGhzHistory instance\n * @param idx       - record index\n * @return SubGhzProtocolCommonLoad*\n */\nFlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx);\n"
  },
  {
    "path": "applications/main/subghz/subghz_i.c",
    "content": "#include \"subghz_i.h\"\n\n#include \"assets_icons.h\"\n#include \"subghz/types.h\"\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n#include <gui/elements.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n#include <flipper_format/flipper_format.h>\n\n#include <flipper_format/flipper_format_i.h>\n#include <lib/toolbox/stream/stream.h>\n#include <lib/subghz/protocols/raw.h>\n\n#define TAG \"SubGhz\"\n\nvoid subghz_set_default_preset(SubGhz* subghz) {\n    furi_assert(subghz);\n    subghz_txrx_set_preset(\n        subghz->txrx,\n        \"AM650\",\n        subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)),\n        NULL,\n        0);\n}\n\nvoid subghz_blink_start(SubGhz* subghz) {\n    furi_assert(subghz);\n    notification_message(subghz->notifications, &sequence_blink_stop);\n    notification_message(subghz->notifications, &sequence_blink_start_magenta);\n}\n\nvoid subghz_blink_stop(SubGhz* subghz) {\n    furi_assert(subghz);\n    notification_message(subghz->notifications, &sequence_blink_stop);\n}\n\nbool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {\n    switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) {\n    case SubGhzTxRxStartTxStateErrorParserOthers:\n        dialog_message_show_storage_error(\n            subghz->dialogs, \"Error in protocol\\nparameters\\ndescription\");\n        break;\n    case SubGhzTxRxStartTxStateErrorOnlyRx:\n        subghz_dialog_message_show_only_rx(subghz);\n        break;\n\n    default:\n        return true;\n        break;\n    }\n    return false;\n}\n\nvoid subghz_dialog_message_show_only_rx(SubGhz* subghz) {\n    DialogsApp* dialogs = subghz->dialogs;\n    DialogMessage* message = dialog_message_alloc();\n\n    const char* header_text = \"Transmission is Blocked!\";\n    const char* message_text = \"Transmission on\\nthis frequency is\\nrestricted in your\\nregion\";\n    if(!furi_hal_region_is_provisioned()) {\n        header_text = \"Firmware update needed\";\n        message_text = \"Please update\\nfirmware before\\nusing this feature\\nflipp.dev/upd\";\n    }\n\n    dialog_message_set_header(message, header_text, 63, 0, AlignCenter, AlignTop);\n    dialog_message_set_text(message, message_text, 1, 13, AlignLeft, AlignTop);\n\n    dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22);\n\n    dialog_message_show(dialogs, message);\n    dialog_message_free(message);\n}\n\nbool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {\n    furi_assert(subghz);\n    furi_assert(file_path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);\n    Stream* fff_data_stream =\n        flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx));\n\n    SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr;\n    FuriString* temp_str = furi_string_alloc();\n    uint32_t temp_data32;\n\n    do {\n        stream_clean(fff_data_stream);\n        if(!flipper_format_file_open_existing(fff_data_file, file_path)) {\n            FURI_LOG_E(TAG, \"Error open file %s\", file_path);\n            break;\n        }\n\n        if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {\n            FURI_LOG_E(TAG, \"Missing or incorrect header\");\n            break;\n        }\n\n        if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||\n            (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&\n           temp_data32 == SUBGHZ_KEY_FILE_VERSION) {\n        } else {\n            FURI_LOG_E(TAG, \"Type or version mismatch\");\n            break;\n        }\n\n        //Load frequency\n        if(!flipper_format_read_uint32(fff_data_file, \"Frequency\", &temp_data32, 1)) {\n            FURI_LOG_E(TAG, \"Missing Frequency\");\n            break;\n        }\n\n        if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) {\n            FURI_LOG_E(TAG, \"Frequency not supported\");\n            break;\n        }\n\n        //Load preset\n        if(!flipper_format_read_string(fff_data_file, \"Preset\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Preset\");\n            break;\n        }\n\n        furi_string_set_str(\n            temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str)));\n        if(!strcmp(furi_string_get_cstr(temp_str), \"\")) {\n            break;\n        }\n        SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);\n\n        if(!strcmp(furi_string_get_cstr(temp_str), \"CUSTOM\")) {\n            //TODO FL-3551: add Custom_preset_module\n            //delete preset if it already exists\n            subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str));\n            //load custom preset from file\n            if(!subghz_setting_load_custom_preset(\n                   setting, furi_string_get_cstr(temp_str), fff_data_file)) {\n                FURI_LOG_E(TAG, \"Missing Custom preset\");\n                break;\n            }\n        }\n        size_t preset_index =\n            subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));\n        subghz_txrx_set_preset(\n            subghz->txrx,\n            furi_string_get_cstr(temp_str),\n            temp_data32,\n            subghz_setting_get_preset_data(setting, preset_index),\n            subghz_setting_get_preset_data_size(setting, preset_index));\n\n        //Load protocol\n        if(!flipper_format_read_string(fff_data_file, \"Protocol\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Protocol\");\n            break;\n        }\n\n        FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);\n        if(!strcmp(furi_string_get_cstr(temp_str), \"RAW\")) {\n            //if RAW\n            subghz->load_type_file = SubGhzLoadTypeFileRaw;\n            subghz_protocol_raw_gen_fff_data(\n                fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx));\n        } else {\n            subghz->load_type_file = SubGhzLoadTypeFileKey;\n            stream_copy_full(\n                flipper_format_get_raw_stream(fff_data_file),\n                flipper_format_get_raw_stream(fff_data));\n        }\n\n        if(subghz_txrx_load_decoder_by_name_protocol(\n               subghz->txrx, furi_string_get_cstr(temp_str))) {\n            SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(\n                subghz_txrx_get_decoder(subghz->txrx), fff_data);\n            if(status != SubGhzProtocolStatusOk) {\n                load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;\n                break;\n            }\n        } else {\n            FURI_LOG_E(TAG, \"Protocol not found\");\n            break;\n        }\n\n        load_key_state = SubGhzLoadKeyStateOK;\n    } while(0);\n\n    furi_string_free(temp_str);\n    flipper_format_free(fff_data_file);\n    furi_record_close(RECORD_STORAGE);\n\n    switch(load_key_state) {\n    case SubGhzLoadKeyStateParseErr:\n        if(show_dialog) {\n            dialog_message_show_storage_error(subghz->dialogs, \"Cannot parse\\nfile\");\n        }\n        return false;\n    case SubGhzLoadKeyStateProtocolDescriptionErr:\n        if(show_dialog) {\n            dialog_message_show_storage_error(\n                subghz->dialogs, \"Error in protocol\\nparameters\\ndescription\");\n        }\n        return false;\n\n    case SubGhzLoadKeyStateOK:\n        return true;\n\n    default:\n        furi_crash(\"SubGhz: Unknown load_key_state.\");\n        return false;\n    }\n}\n\nSubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) {\n    furi_assert(subghz);\n    return subghz->load_type_file;\n}\n\nbool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {\n    furi_assert(subghz);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriString* temp_str = furi_string_alloc();\n    FuriString* file_name = furi_string_alloc();\n    FuriString* file_path = furi_string_alloc();\n\n    bool res = false;\n\n    if(subghz_path_is_file(subghz->file_path)) {\n        //get the name of the next free file\n        path_extract_filename(subghz->file_path, file_name, true);\n        path_extract_dirname(furi_string_get_cstr(subghz->file_path), file_path);\n\n        storage_get_next_filename(\n            storage,\n            furi_string_get_cstr(file_path),\n            furi_string_get_cstr(file_name),\n            SUBGHZ_APP_FILENAME_EXTENSION,\n            file_name,\n            max_len);\n\n        furi_string_printf(\n            temp_str,\n            \"%s/%s%s\",\n            furi_string_get_cstr(file_path),\n            furi_string_get_cstr(file_name),\n            SUBGHZ_APP_FILENAME_EXTENSION);\n        furi_string_set(subghz->file_path, temp_str);\n        res = true;\n    }\n\n    furi_string_free(temp_str);\n    furi_string_free(file_path);\n    furi_string_free(file_name);\n    furi_record_close(RECORD_STORAGE);\n\n    return res;\n}\n\nbool subghz_save_protocol_to_file(\n    SubGhz* subghz,\n    FlipperFormat* flipper_format,\n    const char* dev_file_name) {\n    furi_assert(subghz);\n    furi_assert(flipper_format);\n    furi_assert(dev_file_name);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);\n\n    bool saved = false;\n    FuriString* file_dir = furi_string_alloc();\n\n    path_extract_dirname(dev_file_name, file_dir);\n    do {\n        //removing additional fields\n        flipper_format_delete_key(flipper_format, \"Repeat\");\n        flipper_format_delete_key(flipper_format, \"Manufacture\");\n\n        // Create subghz folder directory if necessary\n        if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) {\n            dialog_message_show_storage_error(subghz->dialogs, \"Cannot create\\nfolder\");\n            break;\n        }\n\n        if(!storage_simply_remove(storage, dev_file_name)) {\n            break;\n        }\n        stream_seek(flipper_format_stream, 0, StreamOffsetFromStart);\n        stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS);\n\n        if(storage_common_stat(storage, dev_file_name, NULL) != FSE_OK) {\n            break;\n        }\n\n        saved = true;\n    } while(0);\n    furi_string_free(file_dir);\n    furi_record_close(RECORD_STORAGE);\n    return saved;\n}\n\nvoid subghz_save_to_file(void* context) {\n    furi_assert(context);\n    SubGhz* subghz = context;\n    if(subghz_path_is_file(subghz->file_path)) {\n        subghz_save_protocol_to_file(\n            subghz,\n            subghz_txrx_get_fff_data(subghz->txrx),\n            furi_string_get_cstr(subghz->file_path));\n    }\n}\n\nbool subghz_load_protocol_from_file(SubGhz* subghz) {\n    furi_assert(subghz);\n\n    FuriString* file_path = furi_string_alloc();\n\n    DialogsFileBrowserOptions browser_options;\n    dialog_file_browser_set_basic_options(\n        &browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px);\n    browser_options.base_path = SUBGHZ_APP_FOLDER;\n\n    // Input events and views are managed by file_select\n    bool res = dialog_file_browser_show(\n        subghz->dialogs, subghz->file_path, subghz->file_path, &browser_options);\n\n    if(res) {\n        res = subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), true);\n    }\n\n    furi_string_free(file_path);\n\n    return res;\n}\n\nbool subghz_rename_file(SubGhz* subghz) {\n    furi_assert(subghz);\n    bool ret = true;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    if(furi_string_cmp(subghz->file_path_tmp, subghz->file_path)) {\n        FS_Error fs_result = storage_common_rename(\n            storage,\n            furi_string_get_cstr(subghz->file_path_tmp),\n            furi_string_get_cstr(subghz->file_path));\n\n        if(fs_result != FSE_OK) {\n            dialog_message_show_storage_error(subghz->dialogs, \"Cannot rename\\n file/directory\");\n            ret = false;\n        }\n    }\n    furi_record_close(RECORD_STORAGE);\n\n    return ret;\n}\n\nbool subghz_file_available(SubGhz* subghz) {\n    furi_assert(subghz);\n    bool ret = true;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FS_Error fs_result =\n        storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL);\n\n    if(fs_result != FSE_OK) {\n        dialog_message_show_storage_error(subghz->dialogs, \"File not available\\n file/directory\");\n        ret = false;\n    }\n\n    furi_record_close(RECORD_STORAGE);\n    return ret;\n}\n\nbool subghz_delete_file(SubGhz* subghz) {\n    furi_assert(subghz);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool result = storage_simply_remove(storage, furi_string_get_cstr(subghz->file_path_tmp));\n    furi_record_close(RECORD_STORAGE);\n\n    subghz_file_name_clear(subghz);\n\n    return result;\n}\n\nvoid subghz_file_name_clear(SubGhz* subghz) {\n    furi_assert(subghz);\n    furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);\n    furi_string_reset(subghz->file_path_tmp);\n}\n\nbool subghz_path_is_file(FuriString* path) {\n    return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION);\n}\n\nvoid subghz_lock(SubGhz* subghz) {\n    furi_assert(subghz);\n    subghz->lock = SubGhzLockOn;\n}\n\nvoid subghz_unlock(SubGhz* subghz) {\n    furi_assert(subghz);\n    subghz->lock = SubGhzLockOff;\n}\n\nbool subghz_is_locked(SubGhz* subghz) {\n    furi_assert(subghz);\n    return subghz->lock == SubGhzLockOn;\n}\n\nvoid subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) {\n    furi_assert(subghz);\n    subghz->rx_key_state = state;\n}\n\nSubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) {\n    furi_assert(subghz);\n    return subghz->rx_key_state;\n}\n"
  },
  {
    "path": "applications/main/subghz/subghz_i.h",
    "content": "#pragma once\n\n#include \"helpers/subghz_types.h\"\n#include <lib/subghz/types.h>\n#include \"subghz.h\"\n#include \"views/receiver.h\"\n#include \"views/transmitter.h\"\n#include \"views/subghz_frequency_analyzer.h\"\n#include \"views/subghz_read_raw.h\"\n\n#include <gui/gui.h>\n#include <assets_icons.h>\n#include <dialogs/dialogs.h>\n#include <gui/scene_manager.h>\n#include <notification/notification_messages.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/popup.h>\n#include <gui/modules/text_input.h>\n#include <gui/modules/widget.h>\n\n#include <subghz/scenes/subghz_scene.h>\n\n#include \"subghz_history.h\"\n\n#include <gui/modules/variable_item_list.h>\n#include <lib/toolbox/path.h>\n\n#include \"rpc/rpc_app.h\"\n\n#include <power/power_service/power.h>\n\n#include \"helpers/subghz_threshold_rssi.h\"\n\n#include \"helpers/subghz_txrx.h\"\n\n#define SUBGHZ_MAX_LEN_NAME 64\n\nstruct SubGhz {\n    Gui* gui;\n    NotificationApp* notifications;\n\n    SubGhzTxRx* txrx;\n\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    Submenu* submenu;\n    Popup* popup;\n    TextInput* text_input;\n    Widget* widget;\n    DialogsApp* dialogs;\n    FuriString* file_path;\n    FuriString* file_path_tmp;\n    char file_name_tmp[SUBGHZ_MAX_LEN_NAME];\n    SubGhzNotificationState state_notifications;\n\n    SubGhzViewReceiver* subghz_receiver;\n    SubGhzViewTransmitter* subghz_transmitter;\n    VariableItemList* variable_item_list;\n\n    SubGhzFrequencyAnalyzer* subghz_frequency_analyzer;\n    SubGhzReadRAW* subghz_read_raw;\n\n    SubGhzProtocolFlag filter;\n    FuriString* error_str;\n    SubGhzLock lock;\n    SubGhzThresholdRssi* threshold_rssi;\n    SubGhzRxKeyState rx_key_state;\n    SubGhzHistory* history;\n    uint16_t idx_menu_chosen;\n    SubGhzLoadTypeFile load_type_file;\n    void* rpc_ctx;\n};\n\nvoid subghz_set_default_preset(SubGhz* subghz);\nvoid subghz_blink_start(SubGhz* subghz);\nvoid subghz_blink_stop(SubGhz* subghz);\n\nbool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format);\nvoid subghz_dialog_message_show_only_rx(SubGhz* subghz);\n\nbool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog);\nbool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len);\nbool subghz_save_protocol_to_file(\n    SubGhz* subghz,\n    FlipperFormat* flipper_format,\n    const char* dev_file_name);\nvoid subghz_save_to_file(void* context);\nbool subghz_load_protocol_from_file(SubGhz* subghz);\nbool subghz_rename_file(SubGhz* subghz);\nbool subghz_file_available(SubGhz* subghz);\nbool subghz_delete_file(SubGhz* subghz);\nvoid subghz_file_name_clear(SubGhz* subghz);\nbool subghz_path_is_file(FuriString* path);\nSubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz);\n\nvoid subghz_lock(SubGhz* subghz);\nvoid subghz_unlock(SubGhz* subghz);\nbool subghz_is_locked(SubGhz* subghz);\n\nvoid subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state);\nSubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz);\n"
  },
  {
    "path": "applications/main/subghz/views/receiver.c",
    "content": "#include \"receiver.h\"\n\n#include \"types.h\"\n#include <input/input.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <m-array.h>\n\n#define FRAME_HEIGHT 12\n#define MAX_LEN_PX   111\n#define MENU_ITEMS   4u\n#define UNLOCK_CNT   3\n\n#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f\n\ntypedef struct {\n    FuriString* item_str;\n    uint8_t type;\n} SubGhzReceiverMenuItem;\n\nARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) //-V658\n\n#define M_OPL_SubGhzReceiverMenuItemArray_t() \\\n    ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)\n\nstruct SubGhzReceiverHistory {\n    SubGhzReceiverMenuItemArray_t data;\n};\n\ntypedef struct SubGhzReceiverHistory SubGhzReceiverHistory;\n\nstatic const Icon* ReceiverItemIcons[] = {\n    [SubGhzProtocolTypeUnknown] = &I_Quest_7x8,\n    [SubGhzProtocolTypeStatic] = &I_Unlock_7x8,\n    [SubGhzProtocolTypeDynamic] = &I_Lock_7x8,\n};\n\ntypedef enum {\n    SubGhzViewReceiverBarShowDefault,\n    SubGhzViewReceiverBarShowLock,\n    SubGhzViewReceiverBarShowToUnlockPress,\n    SubGhzViewReceiverBarShowUnlock,\n} SubGhzViewReceiverBarShow;\n\nstruct SubGhzViewReceiver {\n    bool lock;\n    uint8_t lock_count;\n    FuriTimer* timer;\n    View* view;\n    SubGhzViewReceiverCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    FuriString* frequency_str;\n    FuriString* preset_str;\n    FuriString* history_stat_str;\n    SubGhzReceiverHistory* history;\n    uint16_t idx;\n    uint16_t list_offset;\n    uint16_t history_item;\n    SubGhzViewReceiverBarShow bar_show;\n    uint8_t u_rssi;\n    SubGhzRadioDeviceType device_type;\n} SubGhzViewReceiverModel;\n\nvoid subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {\n    furi_assert(instance);\n    with_view_model(\n        instance->view,\n        SubGhzViewReceiverModel * model,\n        {\n            if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {\n                model->u_rssi = 0;\n            } else {\n                model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);\n            }\n        },\n        true);\n}\n\nvoid subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) {\n    furi_assert(subghz_receiver);\n    subghz_receiver->lock_count = 0;\n\n    if(lock == true) {\n        subghz_receiver->lock = true;\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            { model->bar_show = SubGhzViewReceiverBarShowLock; },\n            true);\n        furi_timer_start(subghz_receiver->timer, 1000);\n    } else {\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            { model->bar_show = SubGhzViewReceiverBarShowDefault; },\n            true);\n    }\n}\n\nvoid subghz_view_receiver_set_callback(\n    SubGhzViewReceiver* subghz_receiver,\n    SubGhzViewReceiverCallback callback,\n    void* context) {\n    furi_assert(subghz_receiver);\n    furi_assert(callback);\n    subghz_receiver->callback = callback;\n    subghz_receiver->context = context;\n}\n\nstatic void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiver) {\n    furi_assert(subghz_receiver);\n\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            size_t history_item = model->history_item;\n            uint16_t bounds = history_item > 3 ? 2 : history_item;\n\n            if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {\n                model->list_offset = model->idx - 3;\n            } else if(model->list_offset < model->idx - bounds) {\n                model->list_offset =\n                    CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);\n            } else if(model->list_offset > model->idx - bounds) {\n                model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);\n            }\n        },\n        true);\n}\n\nvoid subghz_view_receiver_add_item_to_menu(\n    SubGhzViewReceiver* subghz_receiver,\n    const char* name,\n    uint8_t type) {\n    furi_assert(subghz_receiver);\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            SubGhzReceiverMenuItem* item_menu =\n                SubGhzReceiverMenuItemArray_push_raw(model->history->data);\n            item_menu->item_str = furi_string_alloc_set(name);\n            item_menu->type = type;\n            if(model->idx == model->history_item - 1) {\n                model->history_item++;\n                model->idx++;\n            } else {\n                model->history_item++;\n            }\n        },\n        true);\n    subghz_view_receiver_update_offset(subghz_receiver);\n}\n\nvoid subghz_view_receiver_add_data_statusbar(\n    SubGhzViewReceiver* subghz_receiver,\n    const char* frequency_str,\n    const char* preset_str,\n    const char* history_stat_str) {\n    furi_assert(subghz_receiver);\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            furi_string_set(model->frequency_str, frequency_str);\n            furi_string_set(model->preset_str, preset_str);\n            furi_string_set(model->history_stat_str, history_stat_str);\n        },\n        true);\n}\n\nvoid subghz_view_receiver_set_radio_device_type(\n    SubGhzViewReceiver* subghz_receiver,\n    SubGhzRadioDeviceType device_type) {\n    furi_assert(subghz_receiver);\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        { model->device_type = device_type; },\n        true);\n}\n\nstatic void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);\n\n    canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);\n    canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);\n}\n\nstatic void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {\n    for(uint8_t i = 1; i < model->u_rssi; i++) {\n        if(i % 5) {\n            canvas_draw_dot(canvas, 46 + i, 52);\n            canvas_draw_dot(canvas, 47 + i, 53);\n            canvas_draw_dot(canvas, 46 + i, 54);\n        }\n    }\n}\n\nvoid subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n\n    elements_button_left(canvas, \"Config\");\n\n    bool scrollbar = model->history_item > 4;\n    FuriString* str_buff;\n    str_buff = furi_string_alloc();\n\n    SubGhzReceiverMenuItem* item_menu;\n\n    for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {\n        size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);\n        item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);\n        furi_string_set(str_buff, item_menu->item_str);\n        elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX);\n        if(model->idx == idx) {\n            subghz_view_receiver_draw_frame(canvas, i, scrollbar);\n        } else {\n            canvas_set_color(canvas, ColorBlack);\n        }\n        canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);\n        canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));\n        furi_string_reset(str_buff);\n    }\n    if(scrollbar) {\n        elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);\n    }\n    furi_string_free(str_buff);\n\n    canvas_set_color(canvas, ColorBlack);\n\n    if(model->history_item == 0) {\n        canvas_draw_icon(canvas, 0, 0, &I_Scanning_short_96x52);\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str(canvas, 63, 44, \"Scanning...\");\n        canvas_set_font(canvas, FontSecondary);\n    }\n\n    if(model->device_type == SubGhzRadioDeviceTypeInternal) {\n        canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11);\n    } else {\n        canvas_draw_icon(canvas, 109, 0, &I_External_ant_1_9x11);\n    }\n\n    subghz_view_rssi_draw(canvas, model);\n    switch(model->bar_show) {\n    case SubGhzViewReceiverBarShowLock:\n        canvas_draw_icon(canvas, 64, 56, &I_Lock_7x8);\n        canvas_draw_str(canvas, 74, 64, \"Locked\");\n        break;\n    case SubGhzViewReceiverBarShowToUnlockPress:\n        canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str));\n        canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str));\n        canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str));\n        canvas_set_font(canvas, FontSecondary);\n        elements_bold_rounded_frame(canvas, 14, 8, 99, 48);\n        elements_multiline_text(canvas, 65, 26, \"To unlock\\npress:\");\n        canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);\n        canvas_draw_dot(canvas, 17, 61);\n        break;\n    case SubGhzViewReceiverBarShowUnlock:\n        canvas_draw_icon(canvas, 64, 56, &I_Unlock_7x8);\n        canvas_draw_str(canvas, 74, 64, \"Unlocked\");\n        break;\n    default:\n        canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str));\n        canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str));\n        canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str));\n        break;\n    }\n}\n\nstatic void subghz_view_receiver_timer_callback(void* context) {\n    furi_assert(context);\n    SubGhzViewReceiver* subghz_receiver = context;\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        { model->bar_show = SubGhzViewReceiverBarShowDefault; },\n        true);\n    if(subghz_receiver->lock_count < UNLOCK_CNT) {\n        subghz_receiver->callback(\n            SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);\n    } else {\n        subghz_receiver->lock = false;\n        subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);\n    }\n    subghz_receiver->lock_count = 0;\n}\n\nbool subghz_view_receiver_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzViewReceiver* subghz_receiver = context;\n\n    if(subghz_receiver->lock == true) {\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            { model->bar_show = SubGhzViewReceiverBarShowToUnlockPress; },\n            true);\n        if(subghz_receiver->lock_count == 0) {\n            furi_timer_start(subghz_receiver->timer, 1000);\n        }\n        if(event->key == InputKeyBack && event->type == InputTypeShort) {\n            subghz_receiver->lock_count++;\n        }\n        if(subghz_receiver->lock_count >= UNLOCK_CNT) {\n            // subghz_receiver->callback(\n            //     SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);\n            with_view_model(\n                subghz_receiver->view,\n                SubGhzViewReceiverModel * model,\n                { model->bar_show = SubGhzViewReceiverBarShowUnlock; },\n                true);\n            //subghz_receiver->lock = false;\n            furi_timer_start(subghz_receiver->timer, 650);\n        }\n\n        return true;\n    }\n\n    if(event->key == InputKeyBack && event->type == InputTypeShort) {\n        subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context);\n    } else if(\n        event->key == InputKeyUp &&\n        (event->type == InputTypeShort || event->type == InputTypeRepeat)) {\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            {\n                if(model->idx != 0) model->idx--;\n            },\n            true);\n    } else if(\n        event->key == InputKeyDown &&\n        (event->type == InputTypeShort || event->type == InputTypeRepeat)) {\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            {\n                if((model->history_item != 0) && (model->idx != model->history_item - 1))\n                    model->idx++;\n            },\n            true);\n    } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {\n        subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);\n    } else if(event->key == InputKeyOk && event->type == InputTypeShort) {\n        with_view_model(\n            subghz_receiver->view,\n            SubGhzViewReceiverModel * model,\n            {\n                if(model->history_item != 0) {\n                    subghz_receiver->callback(\n                        SubGhzCustomEventViewReceiverOK, subghz_receiver->context);\n                }\n            },\n            false);\n    }\n\n    subghz_view_receiver_update_offset(subghz_receiver);\n\n    return true;\n}\n\nvoid subghz_view_receiver_enter(void* context) {\n    furi_assert(context);\n}\n\nvoid subghz_view_receiver_exit(void* context) {\n    furi_assert(context);\n    SubGhzViewReceiver* subghz_receiver = context;\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            furi_string_reset(model->frequency_str);\n            furi_string_reset(model->preset_str);\n            furi_string_reset(model->history_stat_str);\n                for\n                    M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {\n                        furi_string_free(item_menu->item_str);\n                        item_menu->type = 0;\n                    }\n                SubGhzReceiverMenuItemArray_reset(model->history->data);\n                model->idx = 0;\n                model->list_offset = 0;\n                model->history_item = 0;\n        },\n        false);\n    furi_timer_stop(subghz_receiver->timer);\n}\n\nSubGhzViewReceiver* subghz_view_receiver_alloc(void) {\n    SubGhzViewReceiver* subghz_receiver = malloc(sizeof(SubGhzViewReceiver));\n\n    // View allocation and configuration\n    subghz_receiver->view = view_alloc();\n\n    subghz_receiver->lock = false;\n    subghz_receiver->lock_count = 0;\n    view_allocate_model(\n        subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel));\n    view_set_context(subghz_receiver->view, subghz_receiver);\n    view_set_draw_callback(subghz_receiver->view, (ViewDrawCallback)subghz_view_receiver_draw);\n    view_set_input_callback(subghz_receiver->view, subghz_view_receiver_input);\n    view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter);\n    view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit);\n\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            model->frequency_str = furi_string_alloc();\n            model->preset_str = furi_string_alloc();\n            model->history_stat_str = furi_string_alloc();\n            model->bar_show = SubGhzViewReceiverBarShowDefault;\n            model->history = malloc(sizeof(SubGhzReceiverHistory));\n            SubGhzReceiverMenuItemArray_init(model->history->data);\n        },\n        true);\n    subghz_receiver->timer =\n        furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver);\n    return subghz_receiver;\n}\n\nvoid subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) {\n    furi_assert(subghz_receiver);\n\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            furi_string_free(model->frequency_str);\n            furi_string_free(model->preset_str);\n            furi_string_free(model->history_stat_str);\n                for\n                    M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {\n                        furi_string_free(item_menu->item_str);\n                        item_menu->type = 0;\n                    }\n                SubGhzReceiverMenuItemArray_clear(model->history->data);\n                free(model->history);\n        },\n        false);\n    furi_timer_free(subghz_receiver->timer);\n    view_free(subghz_receiver->view);\n    free(subghz_receiver);\n}\n\nView* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {\n    furi_assert(subghz_receiver);\n    return subghz_receiver->view;\n}\n\nuint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {\n    furi_assert(subghz_receiver);\n    uint32_t idx = 0;\n    with_view_model(\n        subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);\n    return idx;\n}\n\nvoid subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {\n    furi_assert(subghz_receiver);\n    with_view_model(\n        subghz_receiver->view,\n        SubGhzViewReceiverModel * model,\n        {\n            model->idx = idx;\n            if(model->idx > 2) model->list_offset = idx - 2;\n        },\n        true);\n    subghz_view_receiver_update_offset(subghz_receiver);\n}\n"
  },
  {
    "path": "applications/main/subghz/views/receiver.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../helpers/subghz_types.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\ntypedef struct SubGhzViewReceiver SubGhzViewReceiver;\n\ntypedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context);\n\nvoid subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);\n\nvoid subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool keyboard);\n\nvoid subghz_view_receiver_set_callback(\n    SubGhzViewReceiver* subghz_receiver,\n    SubGhzViewReceiverCallback callback,\n    void* context);\n\nSubGhzViewReceiver* subghz_view_receiver_alloc(void);\n\nvoid subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver);\n\nView* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver);\n\nvoid subghz_view_receiver_add_data_statusbar(\n    SubGhzViewReceiver* subghz_receiver,\n    const char* frequency_str,\n    const char* preset_str,\n    const char* history_stat_str);\n\nvoid subghz_view_receiver_set_radio_device_type(\n    SubGhzViewReceiver* subghz_receiver,\n    SubGhzRadioDeviceType device_type);\n\nvoid subghz_view_receiver_add_item_to_menu(\n    SubGhzViewReceiver* subghz_receiver,\n    const char* name,\n    uint8_t type);\n\nuint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);\n\nvoid subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);\n\nvoid subghz_view_receiver_exit(void* context);\n"
  },
  {
    "path": "applications/main/subghz/views/subghz_frequency_analyzer.c",
    "content": "#include \"subghz_frequency_analyzer.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n#include <gui/elements.h>\n#include <notification/notification_messages.h>\n#include \"../helpers/subghz_frequency_analyzer_worker.h\"\n#include \"../helpers/subghz_frequency_analyzer_log_item_array.h\"\n\n#include <assets_icons.h>\n#include <float_tools.h>\n\n#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem)\n\n#define SNPRINTF_FREQUENCY(buff, freq) \\\n    snprintf(buff, sizeof(buff), \"%03ld.%03ld\", freq / 1000000 % 1000, freq / 1000 % 1000);\n\ntypedef enum {\n    SubGhzFrequencyAnalyzerStatusIDLE,\n} SubGhzFrequencyAnalyzerStatus;\n\ntypedef enum {\n    SubGhzFrequencyAnalyzerFragmentBottomTypeMain,\n    SubGhzFrequencyAnalyzerFragmentBottomTypeLog,\n} SubGhzFrequencyAnalyzerFragmentBottomType;\n\nstruct SubGhzFrequencyAnalyzer {\n    View* view;\n    SubGhzFrequencyAnalyzerWorker* worker;\n    SubGhzFrequencyAnalyzerCallback callback;\n    void* context;\n    bool locked;\n    uint32_t last_frequency;\n};\n\ntypedef struct {\n    uint32_t frequency;\n    uint8_t rssi;\n    uint32_t history_frequency[3];\n    bool signal;\n    SubGhzFrequencyAnalyzerLogItemArray_t log_frequency;\n    SubGhzFrequencyAnalyzerFragmentBottomType fragment_bottom_type;\n    SubGhzFrequencyAnalyzerLogOrderBy log_frequency_order_by;\n    uint8_t log_frequency_scroll_offset;\n} SubGhzFrequencyAnalyzerModel;\n\nstatic inline uint8_t rssi_sanitize(float rssi) {\n    return !float_is_equal(rssi, 0.f) ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0;\n}\n\nvoid subghz_frequency_analyzer_set_callback(\n    SubGhzFrequencyAnalyzer* subghz_frequency_analyzer,\n    SubGhzFrequencyAnalyzerCallback callback,\n    void* context) {\n    furi_assert(subghz_frequency_analyzer);\n    furi_assert(callback);\n    subghz_frequency_analyzer->callback = callback;\n    subghz_frequency_analyzer->context = context;\n}\n\nvoid subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {\n    uint8_t column_number = 0;\n    if(rssi) {\n        rssi = rssi / 3 + 2;\n        if(rssi > 20) rssi = 20;\n        for(uint8_t i = 1; i < rssi; i++) {\n            if(i % 4) {\n                column_number++;\n                canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, column_number);\n            }\n        }\n    }\n}\n\nvoid subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {\n    uint8_t column_height = 6;\n    if(rssi) {\n        if(rssi > 54) rssi = 54;\n        for(uint8_t i = 1; i < rssi; i++) {\n            if(i % 5) {\n                canvas_draw_box(canvas, x + i, y - column_height, 1, column_height);\n            }\n        }\n    }\n}\n\nstatic void subghz_frequency_analyzer_log_frequency_draw(\n    Canvas* canvas,\n    SubGhzFrequencyAnalyzerModel* model) {\n    char buffer[64];\n    const uint8_t offset_x = 0;\n    const uint8_t offset_y = 43;\n    canvas_set_font(canvas, FontKeyboard);\n\n    const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);\n    if(items_count == 0) {\n        canvas_draw_rframe(canvas, offset_x + 27, offset_y - 3, 73, 16, 5);\n        canvas_draw_str_aligned(\n            canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, \"No records\");\n        return;\n    } else if(items_count > 3) {\n        elements_scrollbar_pos(\n            canvas,\n            offset_x + 127,\n            offset_y - 8,\n            29,\n            model->log_frequency_scroll_offset,\n            items_count - 2);\n    }\n\n    SubGhzFrequencyAnalyzerLogItem_t* log_frequency_item;\n    for(uint8_t i = 0; i < 3; ++i) {\n        const uint8_t item_pos = model->log_frequency_scroll_offset + i;\n        if(item_pos >= items_count) {\n            break;\n        }\n        log_frequency_item =\n            SubGhzFrequencyAnalyzerLogItemArray_get(model->log_frequency, item_pos);\n        // Frequency\n        SNPRINTF_FREQUENCY(buffer, (*log_frequency_item)->frequency)\n        canvas_draw_str(canvas, offset_x, offset_y + i * 10, buffer);\n\n        // Count\n        snprintf(buffer, sizeof(buffer), \"%3d\", (*log_frequency_item)->count);\n        canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer);\n\n        // Max RSSI\n        subghz_frequency_analyzer_draw_log_rssi(\n            canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10));\n    }\n\n    canvas_set_font(canvas, FontSecondary);\n}\n\nstatic void subghz_frequency_analyzer_history_frequency_draw(\n    Canvas* canvas,\n    SubGhzFrequencyAnalyzerModel* model) {\n    char buffer[64];\n    uint8_t x = 66;\n    uint8_t y = 43;\n\n    canvas_set_font(canvas, FontKeyboard);\n    for(uint8_t i = 0; i < 3; i++) {\n        if(model->history_frequency[i]) {\n            SNPRINTF_FREQUENCY(buffer, model->history_frequency[i])\n            canvas_draw_str(canvas, x, y + i * 10, buffer);\n        } else {\n            canvas_draw_str(canvas, x, y + i * 10, \"---.---\");\n        }\n        canvas_draw_str(canvas, x + 44, y + i * 10, \"MHz\");\n    }\n    canvas_set_font(canvas, FontSecondary);\n}\n\nvoid subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) {\n    furi_assert(canvas);\n    furi_assert(model);\n    char buffer[64];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n\n    if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) {\n        const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);\n        const char* log_order_by_name =\n            subghz_frequency_analyzer_log_get_order_name(model->log_frequency_order_by);\n        if(items_count < LOG_FREQUENCY_MAX_ITEMS) {\n            snprintf(buffer, sizeof(buffer), \"Frequency Analyzer [%s]\", log_order_by_name);\n            canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, buffer);\n        } else {\n            snprintf(buffer, sizeof(buffer), \"The log is full! [%s]\", log_order_by_name);\n            canvas_draw_str(canvas, 2, 8, buffer);\n        }\n        subghz_frequency_analyzer_log_frequency_draw(canvas, model);\n    } else {\n        canvas_draw_str(canvas, 0, 8, \"Frequency Analyzer\");\n        canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11);\n        canvas_draw_str(canvas, 0, 64, \"RSSI\");\n        subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64);\n\n        subghz_frequency_analyzer_history_frequency_draw(canvas, model);\n    }\n\n    // Frequency\n    canvas_set_font(canvas, FontBigNumbers);\n    SNPRINTF_FREQUENCY(buffer, model->frequency);\n    if(model->signal) {\n        canvas_draw_box(canvas, 4, 11, 121, 22);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_str(canvas, 8, 29, buffer);\n    canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11);\n}\n\nstatic void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) {\n    furi_assert(model);\n    M_LET((cmp, model->log_frequency_order_by), SubGhzFrequencyAnalyzerLogItemArray_compare_by_t)\n    SubGhzFrequencyAnalyzerLogItemArray_sort_fo(\n        model->log_frequency, SubGhzFrequencyAnalyzerLogItemArray_compare_by_as_interface(cmp));\n}\n\nbool subghz_frequency_analyzer_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzFrequencyAnalyzer* instance = context;\n\n    if(event->key == InputKeyBack) {\n        return false;\n    }\n\n    if((event->type == InputTypeShort) &&\n       ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) {\n        with_view_model(\n            instance->view,\n            SubGhzFrequencyAnalyzerModel * model,\n            {\n                if(event->key == InputKeyLeft) {\n                    if(model->fragment_bottom_type == 0) {\n                        model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeLog;\n                    } else {\n                        --model->fragment_bottom_type;\n                    }\n                } else if(event->key == InputKeyRight) {\n                    if(model->fragment_bottom_type ==\n                       SubGhzFrequencyAnalyzerFragmentBottomTypeLog) {\n                        model->fragment_bottom_type = 0;\n                    } else {\n                        ++model->fragment_bottom_type;\n                    }\n                }\n            },\n            true);\n    } else if((event->type == InputTypeShort) && (event->key == InputKeyOk)) {\n        with_view_model(\n            instance->view,\n            SubGhzFrequencyAnalyzerModel * model,\n            {\n                if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) {\n                    ++model->log_frequency_order_by;\n                    if(model->log_frequency_order_by >\n                       SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) {\n                        model->log_frequency_order_by = 0;\n                    }\n                    subghz_frequency_analyzer_log_frequency_sort(model);\n                }\n            },\n            true);\n    } else if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {\n        with_view_model(\n            instance->view,\n            SubGhzFrequencyAnalyzerModel * model,\n            {\n                if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) {\n                    if(event->key == InputKeyUp) {\n                        if(model->log_frequency_scroll_offset > 0) {\n                            --model->log_frequency_scroll_offset;\n                        }\n                    } else if(event->key == InputKeyDown) {\n                        const size_t items_count =\n                            SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);\n                        if((model->log_frequency_scroll_offset + 3u) < items_count) {\n                            ++model->log_frequency_scroll_offset;\n                        }\n                    }\n                }\n            },\n            true);\n    }\n\n    return true;\n}\n\nstatic void subghz_frequency_analyzer_log_frequency_search_it(\n    SubGhzFrequencyAnalyzerLogItemArray_it_t* itref,\n    SubGhzFrequencyAnalyzerLogItemArray_t* log_frequency,\n    uint32_t frequency) {\n    furi_assert(log_frequency);\n\n    SubGhzFrequencyAnalyzerLogItemArray_it(*itref, *log_frequency);\n    SubGhzFrequencyAnalyzerLogItem_t* item;\n    while(!SubGhzFrequencyAnalyzerLogItemArray_end_p(*itref)) {\n        item = SubGhzFrequencyAnalyzerLogItemArray_ref(*itref);\n        if((*item)->frequency == frequency) {\n            break;\n        }\n        SubGhzFrequencyAnalyzerLogItemArray_next(*itref);\n    }\n}\n\nstatic bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyzerModel* model) {\n    furi_assert(model);\n    const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);\n    if(items_count < LOG_FREQUENCY_MAX_ITEMS) {\n        SubGhzFrequencyAnalyzerLogItem_t* item =\n            SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency);\n        (*item)->frequency = model->frequency;\n        (*item)->count = 1;\n        (*item)->rssi_max = model->rssi;\n        (*item)->seq = items_count;\n        return true;\n    }\n    return false;\n}\n\nstatic void subghz_frequency_analyzer_log_frequency_update(\n    SubGhzFrequencyAnalyzerModel* model,\n    bool need_insert) {\n    furi_assert(model);\n    if(!model->frequency) {\n        return;\n    }\n\n    SubGhzFrequencyAnalyzerLogItemArray_it_t it;\n    subghz_frequency_analyzer_log_frequency_search_it(\n        &it, &model->log_frequency, model->frequency);\n    if(!SubGhzFrequencyAnalyzerLogItemArray_end_p(it)) {\n        SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_ref(it);\n        if((*item)->rssi_max < model->rssi) {\n            (*item)->rssi_max = model->rssi;\n        }\n\n        if(need_insert && (*item)->count < UINT8_MAX) {\n            ++(*item)->count;\n            subghz_frequency_analyzer_log_frequency_sort(model);\n        }\n    } else if(need_insert) {\n        if(subghz_frequency_analyzer_log_frequency_insert(model)) {\n            subghz_frequency_analyzer_log_frequency_sort(model);\n        }\n    }\n}\n\nvoid subghz_frequency_analyzer_pair_callback(\n    void* context,\n    uint32_t frequency,\n    float rssi,\n    bool signal) {\n    SubGhzFrequencyAnalyzer* instance = context;\n    if(float_is_equal(rssi, 0.f) && instance->locked) {\n        if(instance->callback) {\n            instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context);\n        }\n        instance->last_frequency = 0;\n        //update history\n        with_view_model(\n            instance->view,\n            SubGhzFrequencyAnalyzerModel * model,\n            {\n                model->history_frequency[2] = model->history_frequency[1];\n                model->history_frequency[1] = model->history_frequency[0];\n                model->history_frequency[0] = model->frequency;\n            },\n            false);\n    } else if(!float_is_equal(rssi, 0.f) && !instance->locked) {\n        if(instance->callback) {\n            instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);\n        }\n    }\n\n    instance->locked = !float_is_equal(rssi, 0.f);\n    with_view_model(\n        instance->view,\n        SubGhzFrequencyAnalyzerModel * model,\n        {\n            model->rssi = rssi_sanitize(rssi);\n            model->frequency = frequency;\n            model->signal = signal;\n            if(frequency) {\n                subghz_frequency_analyzer_log_frequency_update(\n                    model, frequency != instance->last_frequency);\n                instance->last_frequency = frequency;\n            }\n        },\n        true);\n}\n\nvoid subghz_frequency_analyzer_enter(void* context) {\n    furi_assert(context);\n    SubGhzFrequencyAnalyzer* instance = context;\n\n    //Start worker\n    instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context);\n\n    subghz_frequency_analyzer_worker_set_pair_callback(\n        instance->worker,\n        (SubGhzFrequencyAnalyzerWorkerPairCallback)subghz_frequency_analyzer_pair_callback,\n        instance);\n\n    subghz_frequency_analyzer_worker_start(instance->worker);\n\n    with_view_model(\n        instance->view,\n        SubGhzFrequencyAnalyzerModel * model,\n        {\n            model->rssi = 0u;\n            model->frequency = 0;\n            model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;\n            model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;\n            model->log_frequency_scroll_offset = 0;\n            model->history_frequency[0] = model->history_frequency[1] =\n                model->history_frequency[2] = 0;\n            SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency);\n        },\n        true);\n}\n\nvoid subghz_frequency_analyzer_exit(void* context) {\n    furi_assert(context);\n    SubGhzFrequencyAnalyzer* instance = context;\n\n    //Stop worker\n    if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {\n        subghz_frequency_analyzer_worker_stop(instance->worker);\n    }\n    subghz_frequency_analyzer_worker_free(instance->worker);\n\n    with_view_model(\n        instance->view,\n        SubGhzFrequencyAnalyzerModel * model,\n        {\n            model->rssi = 0;\n            model->frequency = 0;\n            model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;\n            model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;\n            model->log_frequency_scroll_offset = 0;\n            model->history_frequency[0] = model->history_frequency[1] =\n                model->history_frequency[2] = 0;\n            SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency);\n        },\n        true);\n}\n\nSubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc(void) {\n    SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer));\n\n    // View allocation and configuration\n    instance->last_frequency = 0;\n    instance->view = view_alloc();\n    view_allocate_model(\n        instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel));\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_frequency_analyzer_draw);\n    view_set_input_callback(instance->view, subghz_frequency_analyzer_input);\n    view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter);\n    view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit);\n\n    with_view_model(\n        instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true);\n\n    return instance;\n}\n\nvoid subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* instance) {\n    furi_assert(instance);\n\n    view_free(instance->view);\n    free(instance);\n}\n\nView* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/main/subghz/views/subghz_frequency_analyzer.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../helpers/subghz_custom_event.h\"\n\ntypedef struct SubGhzFrequencyAnalyzer SubGhzFrequencyAnalyzer;\n\ntypedef void (*SubGhzFrequencyAnalyzerCallback)(SubGhzCustomEvent event, void* context);\n\nvoid subghz_frequency_analyzer_set_callback(\n    SubGhzFrequencyAnalyzer* subghz_frequency_analyzer,\n    SubGhzFrequencyAnalyzerCallback callback,\n    void* context);\n\nSubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc(void);\n\nvoid subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* subghz_static);\n\nView* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* subghz_static);\n"
  },
  {
    "path": "applications/main/subghz/views/subghz_read_raw.c",
    "content": "#include \"subghz_read_raw.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <input/input.h>\n#include <gui/elements.h>\n\n#include <assets_icons.h>\n\n#define TAG \"SubGhzReadRaw\"\n\n#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100\n\nstruct SubGhzReadRAW {\n    View* view;\n    SubGhzReadRAWCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    FuriString* frequency_str;\n    FuriString* preset_str;\n    FuriString* sample_write;\n    FuriString* file_name;\n    uint8_t* rssi_history;\n    uint8_t rssi_curret;\n    bool rssi_history_end;\n    uint8_t ind_write;\n    uint8_t ind_sin;\n    SubGhzReadRAWStatus status;\n    float raw_threshold_rssi;\n    SubGhzRadioDeviceType device_type;\n} SubGhzReadRAWModel;\n\nvoid subghz_read_raw_set_callback(\n    SubGhzReadRAW* subghz_read_raw,\n    SubGhzReadRAWCallback callback,\n    void* context) {\n    furi_assert(subghz_read_raw);\n    furi_assert(callback);\n    subghz_read_raw->callback = callback;\n    subghz_read_raw->context = context;\n}\n\nvoid subghz_read_raw_add_data_statusbar(\n    SubGhzReadRAW* instance,\n    const char* frequency_str,\n    const char* preset_str) {\n    furi_assert(instance);\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            furi_string_set(model->frequency_str, frequency_str);\n            furi_string_set(model->preset_str, preset_str);\n        },\n        true);\n}\n\nvoid subghz_read_raw_set_radio_device_type(\n    SubGhzReadRAW* instance,\n    SubGhzRadioDeviceType device_type) {\n    furi_assert(instance);\n    with_view_model(\n        instance->view, SubGhzReadRAWModel * model, { model->device_type = device_type; }, true);\n}\n\nvoid subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) {\n    furi_assert(instance);\n    uint8_t u_rssi = 0;\n\n    if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {\n        u_rssi = 0;\n    } else {\n        u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7f);\n    }\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            model->rssi_curret = u_rssi;\n            if(trace) {\n                model->rssi_history[model->ind_write++] = u_rssi;\n            } else {\n                model->rssi_history[model->ind_write] = u_rssi;\n            }\n\n            if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) {\n                model->rssi_history_end = true;\n                model->ind_write = 0;\n            }\n        },\n        true);\n}\n\nvoid subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        { furi_string_printf(model->sample_write, \"%zu spl.\", sample); },\n        false);\n}\n\nvoid subghz_read_raw_stop_send(SubGhzReadRAW* instance) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            switch(model->status) {\n            case SubGhzReadRAWStatusTXRepeat:\n            case SubGhzReadRAWStatusLoadKeyTXRepeat:\n                instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);\n                break;\n            case SubGhzReadRAWStatusTX:\n                model->status = SubGhzReadRAWStatusIDLE;\n                break;\n            case SubGhzReadRAWStatusLoadKeyTX:\n                model->status = SubGhzReadRAWStatusLoadKeyIDLE;\n                break;\n\n            default:\n                FURI_LOG_W(TAG, \"unknown status\");\n                model->status = SubGhzReadRAWStatusIDLE;\n                break;\n            }\n        },\n        true);\n}\n\nvoid subghz_read_raw_update_sin(SubGhzReadRAW* instance) {\n    furi_assert(instance);\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            if(model->ind_sin++ > 62) {\n                model->ind_sin = 0;\n            }\n        },\n        true);\n}\n\nstatic int8_t subghz_read_raw_tab_sin(uint8_t x) {\n    const uint8_t tab_sin[64] = {0,   3,   6,   9,   12,  16,  19,  22,  25,  28,  31,  34,  37,\n                                 40,  43,  46,  49,  51,  54,  57,  60,  63,  65,  68,  71,  73,\n                                 76,  78,  81,  83,  85,  88,  90,  92,  94,  96,  98,  100, 102,\n                                 104, 106, 107, 109, 111, 112, 113, 115, 116, 117, 118, 120, 121,\n                                 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127};\n\n    int8_t r = tab_sin[((x & 0x40) ? -x - 1 : x) & 0x3f];\n    if(x & 0x80) return -r;\n    return r;\n}\n\nvoid subghz_read_raw_draw_sin(Canvas* canvas, SubGhzReadRAWModel* model) {\n#define SUBGHZ_RAW_SIN_AMPLITUDE 11\n    for(int i = 113; i > 0; i--) {\n        canvas_draw_line(\n            canvas,\n            i,\n            32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE,\n            i + 1,\n            32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /\n                     SUBGHZ_RAW_SIN_AMPLITUDE);\n        canvas_draw_line(\n            canvas,\n            i + 1,\n            32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE,\n            i + 2,\n            32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /\n                     SUBGHZ_RAW_SIN_AMPLITUDE);\n    }\n}\n\nvoid subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) {\n#define SUBGHZ_RAW_TOP_SCALE 14\n#define SUBGHZ_RAW_END_SCALE 115\n\n    if(model->rssi_history_end == false) {\n        for(int i = SUBGHZ_RAW_END_SCALE; i > 0; i -= 15) {\n            canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);\n            canvas_draw_line(canvas, i - 5, SUBGHZ_RAW_TOP_SCALE, i - 5, SUBGHZ_RAW_TOP_SCALE + 2);\n            canvas_draw_line(\n                canvas, i - 10, SUBGHZ_RAW_TOP_SCALE, i - 10, SUBGHZ_RAW_TOP_SCALE + 2);\n        }\n    } else {\n        for(int i = SUBGHZ_RAW_END_SCALE - model->ind_write % 15; i > -15; i -= 15) {\n            canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);\n            if(SUBGHZ_RAW_END_SCALE > i + 5)\n                canvas_draw_line(\n                    canvas, i + 5, SUBGHZ_RAW_TOP_SCALE, i + 5, SUBGHZ_RAW_TOP_SCALE + 2);\n            if(SUBGHZ_RAW_END_SCALE > i + 10)\n                canvas_draw_line(\n                    canvas, i + 10, SUBGHZ_RAW_TOP_SCALE, i + 10, SUBGHZ_RAW_TOP_SCALE + 2);\n        }\n    }\n}\n\nvoid subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {\n    int ind = 0;\n    int base = 0;\n    uint8_t width = 2;\n    if(model->rssi_history_end == false) {\n        for(int i = model->ind_write; i >= 0; i--) {\n            canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);\n        }\n        canvas_draw_line(\n            canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret);\n        if(model->ind_write > 3) {\n            canvas_draw_line(\n                canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret);\n\n            for(uint8_t i = 13; i < 47; i += width * 2) {\n                canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width);\n            }\n            canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12);\n            canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13);\n        }\n    } else {\n        int i = 0;\n        base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write;\n        for(i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i > 0; i--) {\n            ind = i - base;\n            if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE;\n            canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]);\n        }\n\n        canvas_draw_line(\n            canvas,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,\n            47,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,\n            47 - model->rssi_curret);\n        canvas_draw_line(\n            canvas,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,\n            47,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,\n            47 - model->rssi_curret);\n\n        for(uint8_t i = 13; i < 47; i += width * 2) {\n            canvas_draw_line(\n                canvas,\n                SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE,\n                i,\n                SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE,\n                i + width);\n        }\n        canvas_draw_line(\n            canvas,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2,\n            12,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2,\n            12);\n        canvas_draw_line(\n            canvas,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,\n            13,\n            SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,\n            13);\n    }\n}\n\nvoid subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {\n    uint8_t x = 118;\n    uint8_t y = 48;\n\n    if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {\n        uint8_t x = 118;\n        y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7f);\n\n        uint8_t width = 3;\n        for(uint8_t i = 0; i < x; i += width * 2) {\n            canvas_draw_line(canvas, i, y, i + width, y);\n        }\n    }\n    canvas_draw_line(canvas, x, y - 2, x, y + 2);\n    canvas_draw_line(canvas, x - 1, y - 1, x - 1, y + 1);\n    canvas_draw_dot(canvas, x - 2, y);\n}\n\nvoid subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {\n    uint8_t graphics_mode = 1;\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str(canvas, 0, 9, furi_string_get_cstr(model->frequency_str));\n    canvas_draw_str(canvas, 35, 9, furi_string_get_cstr(model->preset_str));\n    canvas_draw_str_aligned(\n        canvas, 106, 2, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write));\n\n    if(model->device_type == SubGhzRadioDeviceTypeInternal) {\n        canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11);\n    } else {\n        canvas_draw_icon(canvas, 109, 0, &I_External_ant_1_9x11);\n    }\n    canvas_draw_line(canvas, 0, 14, 115, 14);\n    canvas_draw_line(canvas, 0, 48, 115, 48);\n    canvas_draw_line(canvas, 115, 14, 115, 48);\n\n    switch(model->status) {\n    case SubGhzReadRAWStatusIDLE:\n        elements_button_left(canvas, \"Erase\");\n        elements_button_center(canvas, \"Send\");\n        elements_button_right(canvas, \"Save\");\n        break;\n    case SubGhzReadRAWStatusLoadKeyIDLE:\n        elements_button_left(canvas, \"New\");\n        elements_button_center(canvas, \"Send\");\n        elements_button_right(canvas, \"More\");\n        elements_text_box(\n            canvas,\n            4,\n            20,\n            110,\n            30,\n            AlignCenter,\n            AlignCenter,\n            furi_string_get_cstr(model->file_name),\n            true);\n        break;\n\n    case SubGhzReadRAWStatusTX:\n    case SubGhzReadRAWStatusTXRepeat:\n    case SubGhzReadRAWStatusLoadKeyTX:\n    case SubGhzReadRAWStatusLoadKeyTXRepeat:\n        graphics_mode = 0;\n        elements_button_center(canvas, \"Hold to repeat\");\n        break;\n\n    case SubGhzReadRAWStatusStart:\n        elements_button_left(canvas, \"Config\");\n        elements_button_center(canvas, \"REC\");\n        break;\n\n    default:\n        elements_button_center(canvas, \"Stop\");\n        break;\n    }\n\n    if(graphics_mode == 0) {\n        subghz_read_raw_draw_sin(canvas, model);\n    } else {\n        subghz_read_raw_draw_rssi(canvas, model);\n        subghz_read_raw_draw_scale(canvas, model);\n        subghz_read_raw_draw_threshold_rssi(canvas, model);\n        canvas_set_font_direction(canvas, CanvasDirectionBottomToTop);\n        canvas_draw_str(canvas, 128, 40, \"RSSI\");\n        canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);\n    }\n}\n\nbool subghz_read_raw_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzReadRAW* instance = context;\n\n    if((event->key == InputKeyOk) &&\n       (event->type == InputTypeLong || event->type == InputTypeRepeat)) {\n        //we check that if we hold the transfer button,\n        //further check of events is not needed, we exit\n        return false;\n    } else if(event->key == InputKeyOk && event->type == InputTypePress) {\n        uint8_t ret = false;\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                switch(model->status) {\n                case SubGhzReadRAWStatusIDLE:\n                    // Start TX\n                    instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);\n                    model->status = SubGhzReadRAWStatusTXRepeat;\n                    ret = true;\n                    break;\n                case SubGhzReadRAWStatusTX:\n                    // Start TXRepeat\n                    model->status = SubGhzReadRAWStatusTXRepeat;\n                    break;\n                case SubGhzReadRAWStatusLoadKeyIDLE:\n                    // Start Load Key TX\n                    instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);\n                    model->status = SubGhzReadRAWStatusLoadKeyTXRepeat;\n                    ret = true;\n                    break;\n                case SubGhzReadRAWStatusLoadKeyTX:\n                    // Start Load Key TXRepeat\n                    model->status = SubGhzReadRAWStatusLoadKeyTXRepeat;\n                    break;\n\n                default:\n                    break;\n                }\n            },\n            ret);\n    } else if(event->key == InputKeyOk && event->type == InputTypeRelease) {\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                if(model->status == SubGhzReadRAWStatusTXRepeat) {\n                    // Stop repeat TX\n                    model->status = SubGhzReadRAWStatusTX;\n                } else if(model->status == SubGhzReadRAWStatusLoadKeyTXRepeat) {\n                    // Stop repeat TX\n                    model->status = SubGhzReadRAWStatusLoadKeyTX;\n                }\n            },\n            false);\n    } else if(event->key == InputKeyBack && event->type == InputTypeShort) {\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                switch(model->status) {\n                case SubGhzReadRAWStatusREC:\n                    //Stop REC\n                    instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);\n                    model->status = SubGhzReadRAWStatusIDLE;\n                    break;\n                case SubGhzReadRAWStatusLoadKeyTX:\n                    //Stop TxRx\n                    instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context);\n                    model->status = SubGhzReadRAWStatusLoadKeyIDLE;\n                    break;\n                case SubGhzReadRAWStatusTX:\n                    //Stop TxRx\n                    instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context);\n                    model->status = SubGhzReadRAWStatusIDLE;\n                    break;\n                case SubGhzReadRAWStatusLoadKeyIDLE:\n                    //Exit\n                    instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context);\n                    break;\n\n                default:\n                    //Exit\n                    instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context);\n                    break;\n                }\n            },\n            true);\n    } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                if(model->status == SubGhzReadRAWStatusStart) {\n                    //Config\n                    instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context);\n                } else if(\n                    (model->status == SubGhzReadRAWStatusIDLE) ||\n                    (model->status == SubGhzReadRAWStatusLoadKeyIDLE)) {\n                    //Erase\n                    model->status = SubGhzReadRAWStatusStart;\n                    model->rssi_history_end = false;\n                    model->ind_write = 0;\n                    furi_string_set(model->sample_write, \"0 spl.\");\n                    furi_string_reset(model->file_name);\n                    instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context);\n                }\n            },\n            true);\n    } else if(event->key == InputKeyRight && event->type == InputTypeShort) {\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                if(model->status == SubGhzReadRAWStatusIDLE) {\n                    //Save\n                    instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context);\n                } else if(model->status == SubGhzReadRAWStatusLoadKeyIDLE) {\n                    //More\n                    instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context);\n                }\n            },\n            true);\n    } else if(event->key == InputKeyOk && event->type == InputTypeShort) {\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                if(model->status == SubGhzReadRAWStatusStart) {\n                    //Record\n                    instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context);\n                    model->status = SubGhzReadRAWStatusREC;\n                    model->ind_write = 0;\n                    model->rssi_history_end = false;\n                } else if(model->status == SubGhzReadRAWStatusREC) {\n                    //Stop\n                    instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);\n                    model->status = SubGhzReadRAWStatusIDLE;\n                }\n            },\n            true);\n    }\n    return true;\n}\n\nvoid subghz_read_raw_set_status(\n    SubGhzReadRAW* instance,\n    SubGhzReadRAWStatus status,\n    const char* file_name,\n    float raw_threshold_rssi) {\n    furi_assert(instance);\n\n    switch(status) {\n    case SubGhzReadRAWStatusStart:\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                model->status = SubGhzReadRAWStatusStart;\n                model->rssi_history_end = false;\n                model->ind_write = 0;\n                furi_string_reset(model->file_name);\n                furi_string_set(model->sample_write, \"0 spl.\");\n                model->raw_threshold_rssi = raw_threshold_rssi;\n            },\n            true);\n        break;\n    case SubGhzReadRAWStatusIDLE:\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            { model->status = SubGhzReadRAWStatusIDLE; },\n            true);\n        break;\n    case SubGhzReadRAWStatusLoadKeyTX:\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                model->status = SubGhzReadRAWStatusLoadKeyIDLE;\n                model->rssi_history_end = false;\n                model->ind_write = 0;\n                furi_string_set(model->file_name, file_name);\n                furi_string_set(model->sample_write, \"RAW\");\n            },\n            true);\n        break;\n    case SubGhzReadRAWStatusSaveKey:\n        with_view_model(\n            instance->view,\n            SubGhzReadRAWModel * model,\n            {\n                model->status = SubGhzReadRAWStatusLoadKeyIDLE;\n                if(!model->ind_write) {\n                    furi_string_set(model->file_name, file_name);\n                    furi_string_set(model->sample_write, \"RAW\");\n                } else {\n                    furi_string_reset(model->file_name);\n                }\n            },\n            true);\n        break;\n\n    default:\n        FURI_LOG_W(TAG, \"unknown status\");\n        break;\n    }\n}\n\nvoid subghz_read_raw_enter(void* context) {\n    furi_assert(context);\n    //SubGhzReadRAW* instance = context;\n}\n\nvoid subghz_read_raw_exit(void* context) {\n    furi_assert(context);\n    SubGhzReadRAW* instance = context;\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            if(model->status != SubGhzReadRAWStatusIDLE &&\n               model->status != SubGhzReadRAWStatusStart &&\n               model->status != SubGhzReadRAWStatusLoadKeyIDLE) {\n                instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);\n                model->status = SubGhzReadRAWStatusStart;\n            }\n        },\n        true);\n}\n\nSubGhzReadRAW* subghz_read_raw_alloc(void) {\n    SubGhzReadRAW* instance = malloc(sizeof(SubGhzReadRAW));\n\n    // View allocation and configuration\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzReadRAWModel));\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw);\n    view_set_input_callback(instance->view, subghz_read_raw_input);\n    view_set_enter_callback(instance->view, subghz_read_raw_enter);\n    view_set_exit_callback(instance->view, subghz_read_raw_exit);\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            model->frequency_str = furi_string_alloc();\n            model->preset_str = furi_string_alloc();\n            model->sample_write = furi_string_alloc();\n            model->file_name = furi_string_alloc();\n            model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));\n            model->raw_threshold_rssi = -127.0f;\n        },\n        true);\n\n    return instance;\n}\n\nvoid subghz_read_raw_free(SubGhzReadRAW* instance) {\n    furi_assert(instance);\n\n    with_view_model(\n        instance->view,\n        SubGhzReadRAWModel * model,\n        {\n            furi_string_free(model->frequency_str);\n            furi_string_free(model->preset_str);\n            furi_string_free(model->sample_write);\n            furi_string_free(model->file_name);\n            free(model->rssi_history);\n        },\n        true);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* subghz_read_raw_get_view(SubGhzReadRAW* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/main/subghz/views/subghz_read_raw.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../helpers/subghz_types.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\n#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f\n\ntypedef struct SubGhzReadRAW SubGhzReadRAW;\n\ntypedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context);\n\ntypedef enum {\n    SubGhzReadRAWStatusStart,\n    SubGhzReadRAWStatusIDLE,\n    SubGhzReadRAWStatusREC,\n    SubGhzReadRAWStatusTX,\n    SubGhzReadRAWStatusTXRepeat,\n\n    SubGhzReadRAWStatusLoadKeyIDLE,\n    SubGhzReadRAWStatusLoadKeyTX,\n    SubGhzReadRAWStatusLoadKeyTXRepeat,\n    SubGhzReadRAWStatusSaveKey,\n} SubGhzReadRAWStatus;\n\nvoid subghz_read_raw_set_callback(\n    SubGhzReadRAW* subghz_read_raw,\n    SubGhzReadRAWCallback callback,\n    void* context);\n\nSubGhzReadRAW* subghz_read_raw_alloc(void);\n\nvoid subghz_read_raw_free(SubGhzReadRAW* subghz_static);\n\nvoid subghz_read_raw_add_data_statusbar(\n    SubGhzReadRAW* instance,\n    const char* frequency_str,\n    const char* preset_str);\n\nvoid subghz_read_raw_set_radio_device_type(\n    SubGhzReadRAW* instance,\n    SubGhzRadioDeviceType device_type);\n\nvoid subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample);\n\nvoid subghz_read_raw_stop_send(SubGhzReadRAW* instance);\n\nvoid subghz_read_raw_update_sin(SubGhzReadRAW* instance);\n\nvoid subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace);\n\nvoid subghz_read_raw_set_status(\n    SubGhzReadRAW* instance,\n    SubGhzReadRAWStatus status,\n    const char* file_name,\n    float raw_threshold_rssi);\n\nView* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static);\n"
  },
  {
    "path": "applications/main/subghz/views/transmitter.c",
    "content": "#include \"transmitter.h\"\n\n#include <assets_icons.h>\n#include <input/input.h>\n#include <gui/elements.h>\n\nstruct SubGhzViewTransmitter {\n    View* view;\n    SubGhzViewTransmitterCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    FuriString* frequency_str;\n    FuriString* preset_str;\n    FuriString* key_str;\n    bool show_button;\n    SubGhzRadioDeviceType device_type;\n    SubGhzViewTransmitterModelType model_type;\n    IconAnimation* icon_int_ant;\n    IconAnimation* icon_ext_ant;\n} SubGhzViewTransmitterModel;\n\nvoid subghz_view_transmitter_set_callback(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzViewTransmitterCallback callback,\n    void* context) {\n    furi_assert(subghz_transmitter);\n\n    subghz_transmitter->callback = callback;\n    subghz_transmitter->context = context;\n}\n\nvoid subghz_view_transmitter_add_data_to_show(\n    SubGhzViewTransmitter* subghz_transmitter,\n    const char* key_str,\n    const char* frequency_str,\n    const char* preset_str,\n    bool show_button) {\n    furi_assert(subghz_transmitter);\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        {\n            furi_string_set(model->key_str, key_str);\n            furi_string_set(model->frequency_str, frequency_str);\n            furi_string_set(model->preset_str, preset_str);\n            model->show_button = show_button;\n        },\n        true);\n}\n\nvoid subghz_view_transmitter_set_radio_device_type(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzRadioDeviceType device_type) {\n    furi_assert(subghz_transmitter);\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        { model->device_type = device_type; },\n        true);\n}\n\nvoid subghz_view_transmitter_set_model_type(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzViewTransmitterModelType model_type) {\n    furi_assert(subghz_transmitter);\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        { model->model_type = model_type; },\n        true);\n}\n\nstatic void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) {\n    const uint8_t button_height = 12;\n    const uint8_t vertical_offset = 3;\n    const uint8_t horizontal_offset = 1;\n    const uint8_t string_width = canvas_string_width(canvas, str);\n    const Icon* icon = &I_ButtonCenter_7x7;\n    const uint8_t icon_offset = 3;\n    const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset;\n    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 44;\n    const uint8_t y = canvas_height(canvas);\n\n    canvas_draw_box(canvas, x, y - button_height, button_width, button_height);\n\n    canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0);\n    canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1);\n    canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2);\n\n    canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);\n    canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);\n    canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);\n\n    canvas_invert_color(canvas);\n    canvas_draw_icon(\n        canvas,\n        x + horizontal_offset,\n        y - button_height + vertical_offset - 1,\n        &I_ButtonCenter_7x7);\n    canvas_draw_str(\n        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);\n    canvas_invert_color(canvas);\n}\n\nvoid subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* model) {\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(\n        canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str));\n    canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));\n    canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));\n\n    if(model->show_button) {\n        if(model->model_type == SubGhzViewTransmitterModelTypeInfo) {\n            elements_button_center(canvas, \"Send\");\n            elements_button_right(canvas, \"Save\");\n        } else {\n            //default type SubGhzViewTransmitterModelTypeTx\n            subghz_view_transmitter_button_right(canvas, \"Send\");\n        }\n\n        if(model->device_type == SubGhzRadioDeviceTypeInternal) {\n            canvas_draw_icon_animation(canvas, 109, 40, model->icon_int_ant);\n        } else {\n            canvas_draw_icon_animation(canvas, 109, 40, model->icon_ext_ant);\n        }\n    }\n}\n\nbool subghz_view_transmitter_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    SubGhzViewTransmitter* subghz_transmitter = context;\n    bool can_be_sent = false;\n\n    if(event->key == InputKeyBack && event->type == InputTypeShort) {\n        with_view_model(\n            subghz_transmitter->view,\n            SubGhzViewTransmitterModel * model,\n            {\n                furi_string_reset(model->frequency_str);\n                furi_string_reset(model->preset_str);\n                furi_string_reset(model->key_str);\n                model->show_button = false;\n            },\n            false);\n        return false;\n    }\n\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        {\n            if(model->show_button) {\n                can_be_sent = true;\n            }\n        },\n        true);\n\n    if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {\n        with_view_model(\n            subghz_transmitter->view,\n            SubGhzViewTransmitterModel * model,\n            {\n                icon_animation_start(model->icon_int_ant);\n                icon_animation_start(model->icon_ext_ant);\n            },\n            false);\n        subghz_transmitter->callback(\n            SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);\n        return true;\n    } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {\n        with_view_model(\n            subghz_transmitter->view,\n            SubGhzViewTransmitterModel * model,\n            {\n                icon_animation_stop(model->icon_int_ant);\n                icon_animation_stop(model->icon_ext_ant);\n            },\n            false);\n        subghz_transmitter->callback(\n            SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);\n        return true;\n    } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeShort) {\n        subghz_transmitter->callback(\n            SubGhzCustomEventViewTransmitterSendSave, subghz_transmitter->context);\n    }\n\n    return true;\n}\n\nvoid subghz_view_transmitter_enter(void* context) {\n    furi_assert(context);\n}\n\nvoid subghz_view_transmitter_exit(void* context) {\n    furi_assert(context);\n}\n\nSubGhzViewTransmitter* subghz_view_transmitter_alloc(void) {\n    SubGhzViewTransmitter* subghz_transmitter = malloc(sizeof(SubGhzViewTransmitter));\n\n    // View allocation and configuration\n    subghz_transmitter->view = view_alloc();\n    view_allocate_model(\n        subghz_transmitter->view, ViewModelTypeLocking, sizeof(SubGhzViewTransmitterModel));\n    view_set_context(subghz_transmitter->view, subghz_transmitter);\n    view_set_draw_callback(\n        subghz_transmitter->view, (ViewDrawCallback)subghz_view_transmitter_draw);\n    view_set_input_callback(subghz_transmitter->view, subghz_view_transmitter_input);\n    view_set_enter_callback(subghz_transmitter->view, subghz_view_transmitter_enter);\n    view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit);\n\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        {\n            model->frequency_str = furi_string_alloc();\n            model->preset_str = furi_string_alloc();\n            model->key_str = furi_string_alloc();\n            model->model_type = SubGhzViewTransmitterModelTypeTx;\n            model->icon_int_ant = icon_animation_alloc(&A_SubGhz_Internal_ant);\n            view_tie_icon_animation(subghz_transmitter->view, model->icon_int_ant);\n            model->icon_ext_ant = icon_animation_alloc(&A_SubGhz_External_ant);\n            view_tie_icon_animation(subghz_transmitter->view, model->icon_ext_ant);\n        },\n        true);\n    return subghz_transmitter;\n}\n\nvoid subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {\n    furi_assert(subghz_transmitter);\n\n    with_view_model(\n        subghz_transmitter->view,\n        SubGhzViewTransmitterModel * model,\n        {\n            furi_string_free(model->frequency_str);\n            furi_string_free(model->preset_str);\n            furi_string_free(model->key_str);\n            icon_animation_free(model->icon_int_ant);\n            icon_animation_free(model->icon_ext_ant);\n        },\n        true);\n    view_free(subghz_transmitter->view);\n    free(subghz_transmitter);\n}\n\nView* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter) {\n    furi_assert(subghz_transmitter);\n    return subghz_transmitter->view;\n}\n"
  },
  {
    "path": "applications/main/subghz/views/transmitter.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../helpers/subghz_types.h\"\n#include \"../helpers/subghz_custom_event.h\"\n\ntypedef struct SubGhzViewTransmitter SubGhzViewTransmitter;\n\ntypedef enum {\n    SubGhzViewTransmitterModelTypeTx,\n    SubGhzViewTransmitterModelTypeInfo,\n} SubGhzViewTransmitterModelType;\n\ntypedef void (*SubGhzViewTransmitterCallback)(SubGhzCustomEvent event, void* context);\n\nvoid subghz_view_transmitter_set_callback(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzViewTransmitterCallback callback,\n    void* context);\n\nvoid subghz_view_transmitter_set_radio_device_type(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzRadioDeviceType device_type);\n\nvoid subghz_view_transmitter_set_model_type(\n    SubGhzViewTransmitter* subghz_transmitter,\n    SubGhzViewTransmitterModelType model_type);\n\nSubGhzViewTransmitter* subghz_view_transmitter_alloc(void);\n\nvoid subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter);\n\nView* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter);\n\nvoid subghz_view_transmitter_add_data_to_show(\n    SubGhzViewTransmitter* subghz_transmitter,\n    const char* key_str,\n    const char* frequency_str,\n    const char* preset_str,\n    bool show_button);\n"
  },
  {
    "path": "applications/main/u2f/application.fam",
    "content": "App(\n    appid=\"u2f\",\n    name=\"U2F\",\n    apptype=FlipperAppType.MENUEXTERNAL,\n    entry_point=\"u2f_app\",\n    stack_size=2 * 1024,\n    icon=\"A_U2F_14\",\n    order=80,\n    resources=\"resources\",\n    fap_libs=[\"assets\", \"mbedtls\"],\n    fap_category=\"USB\",\n    fap_icon=\"icon.png\",\n)\n"
  },
  {
    "path": "applications/main/u2f/resources/u2f/assets/cert_key.u2f",
    "content": "Filetype: Flipper U2F Certificate Key File\nVersion: 1\nType: 0\nIV: E1 56 CE 83 98 FA 59 0D 45 EC 1C EB 34 FC 08 C9\nData: E1 8C C9 9A 98 F7 B9 50 1E 85 71 8F A4 CE 76 95 87 4F AC 8B 5E D0 1F 13 BA 3B 2E E7 98 73 54 64 58 0A 00 20 55 B8 00 08 58 0A 00 20 50 4E 01 20\n"
  },
  {
    "path": "applications/main/u2f/scenes/u2f_scene.c",
    "content": "#include \"u2f_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const u2f_scene_on_enter_handlers[])(void*) = {\n#include \"u2f_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const u2f_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"u2f_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const u2f_scene_on_exit_handlers[])(void* context) = {\n#include \"u2f_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers u2f_scene_handlers = {\n    .on_enter_handlers = u2f_scene_on_enter_handlers,\n    .on_event_handlers = u2f_scene_on_event_handlers,\n    .on_exit_handlers = u2f_scene_on_exit_handlers,\n    .scene_num = U2fSceneNum,\n};\n"
  },
  {
    "path": "applications/main/u2f/scenes/u2f_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) U2fScene##id,\ntypedef enum {\n#include \"u2f_scene_config.h\"\n    U2fSceneNum,\n} U2fScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers u2f_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"u2f_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"u2f_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"u2f_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/main/u2f/scenes/u2f_scene_config.h",
    "content": "ADD_SCENE(u2f, main, Main)\nADD_SCENE(u2f, error, Error)\n"
  },
  {
    "path": "applications/main/u2f/scenes/u2f_scene_error.c",
    "content": "#include \"../u2f_app_i.h\"\n\nstatic void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    U2fApp* app = context;\n\n    if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventErrorBack);\n    }\n}\n\nvoid u2f_scene_error_on_enter(void* context) {\n    U2fApp* app = context;\n\n    if(app->error == U2fAppErrorNoFiles) {\n        widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);\n        widget_add_string_multiline_element(\n            app->widget,\n            81,\n            4,\n            AlignCenter,\n            AlignTop,\n            FontSecondary,\n            \"No SD card or\\napp data found.\\nThis app will not\\nwork without\\nrequired files.\");\n        widget_add_button_element(\n            app->widget, GuiButtonTypeLeft, \"Back\", u2f_scene_error_event_callback, app);\n    } else if(app->error == U2fAppErrorCloseRpc) {\n        widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);\n        widget_add_string_multiline_element(\n            app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, \"Connection\\nIs Active!\");\n        widget_add_string_multiline_element(\n            app->widget,\n            3,\n            30,\n            AlignLeft,\n            AlignTop,\n            FontSecondary,\n            \"Disconnect from\\nPC or phone to\\nuse this function.\");\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError);\n}\n\nbool u2f_scene_error_on_event(void* context, SceneManagerEvent event) {\n    U2fApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == U2fCustomEventErrorBack) {\n            view_dispatcher_stop(app->view_dispatcher);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid u2f_scene_error_on_exit(void* context) {\n    U2fApp* app = context;\n    widget_reset(app->widget);\n}\n"
  },
  {
    "path": "applications/main/u2f/scenes/u2f_scene_main.c",
    "content": "#include \"../u2f_app_i.h\"\n#include \"../views/u2f_view.h\"\n#include <dolphin/dolphin.h>\n#include <furi_hal.h>\n#include \"../u2f.h\"\n\n#define U2F_REQUEST_TIMEOUT 500\n#define U2F_SUCCESS_TIMEOUT 3000\n\nstatic void u2f_scene_main_ok_callback(InputType type, void* context) {\n    UNUSED(type);\n    furi_assert(context);\n    U2fApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConfirm);\n}\n\nstatic void u2f_scene_main_event_callback(U2fNotifyEvent evt, void* context) {\n    furi_assert(context);\n    U2fApp* app = context;\n    if(evt == U2fNotifyRegister)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventRegister);\n    else if(evt == U2fNotifyAuth)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventAuth);\n    else if(evt == U2fNotifyAuthSuccess)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventAuthSuccess);\n    else if(evt == U2fNotifyWink)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventWink);\n    else if(evt == U2fNotifyConnect)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConnect);\n    else if(evt == U2fNotifyDisconnect)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDisconnect);\n    else if(evt == U2fNotifyError)\n        view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDataError);\n}\n\nstatic void u2f_scene_main_timer_callback(void* context) {\n    furi_assert(context);\n    U2fApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventTimeout);\n}\n\nbool u2f_scene_main_on_event(void* context, SceneManagerEvent event) {\n    furi_assert(context);\n    U2fApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == U2fCustomEventConnect) {\n            furi_timer_stop(app->timer);\n            u2f_view_set_state(app->u2f_view, U2fMsgIdle);\n        } else if(event.event == U2fCustomEventDisconnect) {\n            furi_timer_stop(app->timer);\n            app->event_cur = U2fCustomEventNone;\n            u2f_view_set_state(app->u2f_view, U2fMsgNotConnected);\n        } else if((event.event == U2fCustomEventRegister) || (event.event == U2fCustomEventAuth)) {\n            furi_timer_start(app->timer, U2F_REQUEST_TIMEOUT);\n            if(app->event_cur == U2fCustomEventNone) {\n                app->event_cur = event.event;\n                if(event.event == U2fCustomEventRegister)\n                    u2f_view_set_state(app->u2f_view, U2fMsgRegister);\n                else if(event.event == U2fCustomEventAuth) //-V547\n                    u2f_view_set_state(app->u2f_view, U2fMsgAuth);\n                notification_message(app->notifications, &sequence_display_backlight_on);\n                notification_message(app->notifications, &sequence_single_vibro);\n            }\n            notification_message(app->notifications, &sequence_blink_magenta_10);\n        } else if(event.event == U2fCustomEventWink) {\n            notification_message(app->notifications, &sequence_blink_magenta_10);\n        } else if(event.event == U2fCustomEventAuthSuccess) {\n            notification_message_block(app->notifications, &sequence_set_green_255);\n            dolphin_deed(DolphinDeedU2fAuthorized);\n            furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT);\n            app->event_cur = U2fCustomEventNone;\n            u2f_view_set_state(app->u2f_view, U2fMsgSuccess);\n        } else if(event.event == U2fCustomEventTimeout) {\n            notification_message_block(app->notifications, &sequence_reset_rgb);\n            app->event_cur = U2fCustomEventNone;\n            u2f_view_set_state(app->u2f_view, U2fMsgIdle);\n        } else if(event.event == U2fCustomEventConfirm) {\n            if(app->event_cur != U2fCustomEventNone) {\n                u2f_confirm_user_present(app->u2f_instance);\n            }\n        } else if(event.event == U2fCustomEventDataError) {\n            notification_message(app->notifications, &sequence_set_red_255);\n            furi_timer_stop(app->timer);\n            u2f_view_set_state(app->u2f_view, U2fMsgError);\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid u2f_scene_main_on_enter(void* context) {\n    U2fApp* app = context;\n\n    app->timer = furi_timer_alloc(u2f_scene_main_timer_callback, FuriTimerTypeOnce, app);\n\n    app->u2f_instance = u2f_alloc();\n    app->u2f_ready = u2f_init(app->u2f_instance);\n    if(app->u2f_ready == true) {\n        u2f_set_event_callback(app->u2f_instance, u2f_scene_main_event_callback, app);\n        app->u2f_hid = u2f_hid_start(app->u2f_instance);\n        u2f_view_set_ok_callback(app->u2f_view, u2f_scene_main_ok_callback, app);\n    } else {\n        u2f_free(app->u2f_instance);\n        u2f_view_set_state(app->u2f_view, U2fMsgError);\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewMain);\n}\n\nvoid u2f_scene_main_on_exit(void* context) {\n    U2fApp* app = context;\n    notification_message_block(app->notifications, &sequence_reset_rgb);\n    furi_timer_stop(app->timer);\n    furi_timer_free(app->timer);\n    if(app->u2f_ready == true) {\n        u2f_hid_stop(app->u2f_hid);\n        u2f_free(app->u2f_instance);\n    }\n}\n"
  },
  {
    "path": "applications/main/u2f/u2f.c",
    "content": "#include \"u2f.h\"\n#include \"u2f_data.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_random.h>\n\n#include <mbedtls/sha256.h>\n#include <mbedtls/md.h>\n#include <mbedtls/ecdsa.h>\n#include <mbedtls/error.h>\n\n#define TAG \"U2f\"\n\n#define WORKER_TAG TAG \"Worker\"\n\n#define MCHECK(expr) furi_check((expr) == 0)\n\n#define U2F_CMD_REGISTER     0x01\n#define U2F_CMD_AUTHENTICATE 0x02\n#define U2F_CMD_VERSION      0x03\n\ntypedef enum {\n    U2fCheckOnly = 0x07, // \"check-only\" - only check key handle, don't send auth response\n    U2fEnforce =\n        0x03, // \"enforce-user-presence-and-sign\" - send auth response only if user is present\n    U2fDontEnforce =\n        0x08, // \"dont-enforce-user-presence-and-sign\" - send auth response even if user is missing\n} U2fAuthMode;\n\n#define U2F_HASH_SIZE      32\n#define U2F_NONCE_SIZE     32\n#define U2F_CHALLENGE_SIZE 32\n#define U2F_APP_ID_SIZE    32\n\n#define U2F_EC_KEY_SIZE    32\n#define U2F_EC_BIGNUM_SIZE 32\n#define U2F_EC_POINT_SIZE  65\n\ntypedef struct {\n    uint8_t format;\n    uint8_t xy[64];\n} FURI_PACKED U2fPubKey;\n_Static_assert(sizeof(U2fPubKey) == U2F_EC_POINT_SIZE, \"U2fPubKey size mismatch\");\n\ntypedef struct {\n    uint8_t len;\n    uint8_t hash[U2F_HASH_SIZE];\n    uint8_t nonce[U2F_NONCE_SIZE];\n} FURI_PACKED U2fKeyHandle;\n\ntypedef struct {\n    uint8_t cla;\n    uint8_t ins;\n    uint8_t p1;\n    uint8_t p2;\n    uint8_t len[3];\n    uint8_t challenge[U2F_CHALLENGE_SIZE];\n    uint8_t app_id[U2F_APP_ID_SIZE];\n} FURI_PACKED U2fRegisterReq;\n\ntypedef struct {\n    uint8_t reserved;\n    U2fPubKey pub_key;\n    U2fKeyHandle key_handle;\n    uint8_t cert[];\n} FURI_PACKED U2fRegisterResp;\n\ntypedef struct {\n    uint8_t cla;\n    uint8_t ins;\n    uint8_t p1;\n    uint8_t p2;\n    uint8_t len[3];\n    uint8_t challenge[U2F_CHALLENGE_SIZE];\n    uint8_t app_id[U2F_APP_ID_SIZE];\n    U2fKeyHandle key_handle;\n} FURI_PACKED U2fAuthReq;\n\ntypedef struct {\n    uint8_t user_present;\n    uint32_t counter;\n    uint8_t signature[];\n} FURI_PACKED U2fAuthResp;\n\nstatic const uint8_t ver_str[] = {\"U2F_V2\"};\n\nstatic const uint8_t state_no_error[] = {0x90, 0x00};\nstatic const uint8_t state_not_supported[] = {0x6D, 0x00};\nstatic const uint8_t state_user_missing[] = {0x69, 0x85};\nstatic const uint8_t state_wrong_data[] = {0x6A, 0x80};\n\nstruct U2fData {\n    uint8_t device_key[U2F_EC_KEY_SIZE];\n    uint8_t cert_key[U2F_EC_KEY_SIZE];\n    uint32_t counter;\n    bool ready;\n    bool user_present;\n    U2fEvtCallback callback;\n    void* context;\n    mbedtls_ecp_group group;\n};\n\nstatic int u2f_uecc_random_cb(void* context, uint8_t* dest, unsigned size) {\n    UNUSED(context);\n    furi_hal_random_fill_buf(dest, size);\n    return 0;\n}\n\nU2fData* u2f_alloc(void) {\n    return malloc(sizeof(U2fData));\n}\n\nvoid u2f_free(U2fData* U2F) {\n    furi_assert(U2F);\n    mbedtls_ecp_group_free(&U2F->group);\n    free(U2F);\n}\n\nbool u2f_init(U2fData* U2F) {\n    furi_assert(U2F);\n\n    if(u2f_data_cert_check() == false) {\n        FURI_LOG_E(TAG, \"Certificate load error\");\n        return false;\n    }\n    if(u2f_data_cert_key_load(U2F->cert_key) == false) {\n        FURI_LOG_E(TAG, \"Certificate key load error\");\n        return false;\n    }\n    if(u2f_data_key_load(U2F->device_key) == false) {\n        FURI_LOG_W(TAG, \"Key loading error, generating new\");\n        if(u2f_data_key_generate(U2F->device_key) == false) {\n            FURI_LOG_E(TAG, \"Key write failed\");\n            return false;\n        }\n    }\n    if(u2f_data_cnt_read(&U2F->counter) == false) {\n        FURI_LOG_W(TAG, \"Counter loading error, resetting counter\");\n        U2F->counter = 0;\n        if(u2f_data_cnt_write(0) == false) {\n            FURI_LOG_E(TAG, \"Counter write failed\");\n            return false;\n        }\n    }\n\n    mbedtls_ecp_group_init(&U2F->group);\n    mbedtls_ecp_group_load(&U2F->group, MBEDTLS_ECP_DP_SECP256R1);\n\n    U2F->ready = true;\n    return true;\n}\n\nvoid u2f_set_event_callback(U2fData* U2F, U2fEvtCallback callback, void* context) {\n    furi_assert(U2F);\n    furi_assert(callback);\n    U2F->callback = callback;\n    U2F->context = context;\n}\n\nvoid u2f_confirm_user_present(U2fData* U2F) {\n    U2F->user_present = true;\n}\n\nstatic uint8_t u2f_der_encode_int(uint8_t* der, uint8_t* val, uint8_t val_len) {\n    der[0] = 0x02; // Integer\n\n    uint8_t len = 2;\n    // Omit leading zeros\n    while(val[0] == 0 && val_len > 0) {\n        ++val;\n        --val_len;\n    }\n\n    // Check if integer is negative\n    if(val[0] > 0x7f) der[len++] = 0;\n\n    memcpy(der + len, val, val_len);\n    len += val_len;\n\n    der[1] = len - 2;\n    return len;\n}\n\nstatic uint8_t u2f_der_encode_signature(uint8_t* der, uint8_t* sig) {\n    der[0] = 0x30;\n\n    uint8_t len = 2;\n    len += u2f_der_encode_int(der + len, sig, U2F_HASH_SIZE);\n    len += u2f_der_encode_int(der + len, sig + U2F_HASH_SIZE, U2F_HASH_SIZE);\n\n    der[1] = len - 2;\n    return len;\n}\n\nstatic void\n    u2f_ecc_sign(mbedtls_ecp_group* grp, const uint8_t* key, uint8_t* hash, uint8_t* signature) {\n    mbedtls_mpi r, s, d;\n\n    mbedtls_mpi_init(&r);\n    mbedtls_mpi_init(&s);\n    mbedtls_mpi_init(&d);\n\n    MCHECK(mbedtls_mpi_read_binary(&d, key, U2F_EC_KEY_SIZE));\n    MCHECK(mbedtls_ecdsa_sign(grp, &r, &s, &d, hash, U2F_HASH_SIZE, u2f_uecc_random_cb, NULL));\n    MCHECK(mbedtls_mpi_write_binary(&r, signature, U2F_EC_BIGNUM_SIZE));\n    MCHECK(mbedtls_mpi_write_binary(&s, signature + U2F_EC_BIGNUM_SIZE, U2F_EC_BIGNUM_SIZE));\n\n    mbedtls_mpi_free(&r);\n    mbedtls_mpi_free(&s);\n    mbedtls_mpi_free(&d);\n}\n\nstatic void u2f_ecc_compute_public_key(\n    mbedtls_ecp_group* grp,\n    const uint8_t* private_key,\n    U2fPubKey* public_key) {\n    mbedtls_ecp_point Q;\n    mbedtls_mpi d;\n    size_t olen;\n\n    mbedtls_ecp_point_init(&Q);\n    mbedtls_mpi_init(&d);\n\n    MCHECK(mbedtls_mpi_read_binary(&d, private_key, U2F_EC_KEY_SIZE));\n    MCHECK(mbedtls_ecp_mul(grp, &Q, &d, &grp->G, u2f_uecc_random_cb, NULL));\n    MCHECK(mbedtls_ecp_check_privkey(grp, &d));\n\n    MCHECK(mbedtls_ecp_point_write_binary(\n        grp, &Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (unsigned char*)public_key, sizeof(U2fPubKey)));\n\n    mbedtls_ecp_point_free(&Q);\n    mbedtls_mpi_free(&d);\n}\n\n///////////////////////////////////////////\n\nstatic uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {\n    U2fRegisterReq* req = (U2fRegisterReq*)buf;\n    U2fRegisterResp* resp = (U2fRegisterResp*)buf;\n    U2fKeyHandle handle;\n    uint8_t private[U2F_EC_KEY_SIZE];\n    U2fPubKey pub_key;\n    uint8_t hash[U2F_HASH_SIZE];\n    uint8_t signature[U2F_EC_BIGNUM_SIZE * 2];\n\n    if(u2f_data_check(false) == false) {\n        U2F->ready = false;\n        if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context);\n        memcpy(&buf[0], state_not_supported, 2);\n        return 2;\n    }\n\n    if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context);\n    if(U2F->user_present == false) {\n        memcpy(&buf[0], state_user_missing, 2);\n        return 2;\n    }\n    U2F->user_present = false;\n\n    handle.len = U2F_HASH_SIZE * 2;\n\n    // Generate random nonce\n    furi_hal_random_fill_buf(handle.nonce, 32);\n\n    {\n        mbedtls_md_context_t hmac_ctx;\n        mbedtls_md_init(&hmac_ctx);\n        MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1));\n        MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key)));\n\n        // Generate private key\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, handle.nonce, sizeof(handle.nonce)));\n        MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, private));\n\n        MCHECK(mbedtls_md_hmac_reset(&hmac_ctx));\n\n        // Generate private key handle\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, private, sizeof(private)));\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));\n        MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, handle.hash));\n\n        mbedtls_md_free(&hmac_ctx);\n    }\n\n    // Generate public key\n    u2f_ecc_compute_public_key(&U2F->group, private, &pub_key);\n\n    // Generate signature\n    {\n        uint8_t reserved_byte = 0;\n\n        mbedtls_sha256_context sha_ctx;\n\n        mbedtls_sha256_init(&sha_ctx);\n        mbedtls_sha256_starts(&sha_ctx, 0);\n\n        mbedtls_sha256_update(&sha_ctx, &reserved_byte, 1);\n        mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id));\n        mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge));\n        mbedtls_sha256_update(&sha_ctx, handle.hash, handle.len);\n        mbedtls_sha256_update(&sha_ctx, (uint8_t*)&pub_key, sizeof(U2fPubKey));\n\n        mbedtls_sha256_finish(&sha_ctx, hash);\n        mbedtls_sha256_free(&sha_ctx);\n    }\n\n    // Sign hash\n    u2f_ecc_sign(&U2F->group, U2F->cert_key, hash, signature);\n\n    // Encode response message\n    resp->reserved = 0x05;\n    memcpy(&(resp->pub_key), &pub_key, sizeof(U2fPubKey));\n    memcpy(&(resp->key_handle), &handle, sizeof(U2fKeyHandle));\n    uint32_t cert_len = u2f_data_cert_load(resp->cert);\n    uint8_t signature_len = u2f_der_encode_signature(resp->cert + cert_len, signature);\n    memcpy(resp->cert + cert_len + signature_len, state_no_error, 2);\n\n    return sizeof(U2fRegisterResp) + cert_len + signature_len + 2;\n}\n\nstatic inline uint32_t u2f_to_big_endian(uint32_t a) {\n    return __builtin_bswap32(a);\n}\n\nstatic uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {\n    U2fAuthReq* req = (U2fAuthReq*)buf;\n    U2fAuthResp* resp = (U2fAuthResp*)buf;\n    uint8_t priv_key[U2F_EC_KEY_SIZE];\n    uint8_t mac_control[32];\n    uint8_t flags = 0;\n    uint8_t hash[U2F_HASH_SIZE];\n    uint8_t signature[U2F_HASH_SIZE * 2];\n    uint32_t be_u2f_counter;\n\n    if(u2f_data_check(false) == false) {\n        U2F->ready = false;\n        if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context);\n        memcpy(&buf[0], state_not_supported, 2);\n        return 2;\n    }\n\n    if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context);\n    if(U2F->user_present == true) {\n        flags |= 1;\n    } else {\n        if(req->p1 == U2fEnforce) {\n            memcpy(&buf[0], state_user_missing, 2);\n            return 2;\n        }\n    }\n    U2F->user_present = false;\n\n    // The 4 byte counter is represented in big endian. Increment it before use\n    be_u2f_counter = u2f_to_big_endian(U2F->counter + 1);\n\n    // Generate hash\n    {\n        mbedtls_sha256_context sha_ctx;\n\n        mbedtls_sha256_init(&sha_ctx);\n        mbedtls_sha256_starts(&sha_ctx, 0);\n\n        mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id));\n        mbedtls_sha256_update(&sha_ctx, &flags, 1);\n        mbedtls_sha256_update(&sha_ctx, (uint8_t*)&(be_u2f_counter), sizeof(be_u2f_counter));\n        mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge));\n\n        mbedtls_sha256_finish(&sha_ctx, hash);\n        mbedtls_sha256_free(&sha_ctx);\n    }\n\n    {\n        mbedtls_md_context_t hmac_ctx;\n        mbedtls_md_init(&hmac_ctx);\n        MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1));\n        MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key)));\n\n        // Recover private key\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));\n        MCHECK(mbedtls_md_hmac_update(\n            &hmac_ctx, req->key_handle.nonce, sizeof(req->key_handle.nonce)));\n        MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, priv_key));\n\n        MCHECK(mbedtls_md_hmac_reset(&hmac_ctx));\n\n        // Generate and verify private key handle\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, priv_key, sizeof(priv_key)));\n        MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));\n        MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, mac_control));\n\n        mbedtls_md_free(&hmac_ctx);\n    }\n\n    if(memcmp(req->key_handle.hash, mac_control, sizeof(mac_control)) != 0) {\n        FURI_LOG_W(TAG, \"Wrong handle!\");\n        memcpy(&buf[0], state_wrong_data, 2);\n        return 2;\n    }\n\n    if(req->p1 == U2fCheckOnly) { // Check-only: don't need to send full response\n        memcpy(&buf[0], state_user_missing, 2);\n        return 2;\n    }\n\n    // Sign hash\n    u2f_ecc_sign(&U2F->group, priv_key, hash, signature);\n\n    resp->user_present = flags;\n    resp->counter = be_u2f_counter;\n    uint8_t signature_len = u2f_der_encode_signature(resp->signature, signature);\n    memcpy(resp->signature + signature_len, state_no_error, 2);\n\n    U2F->counter++;\n    FURI_LOG_D(TAG, \"Counter: %lu\", U2F->counter);\n    u2f_data_cnt_write(U2F->counter);\n\n    if(U2F->callback != NULL) U2F->callback(U2fNotifyAuthSuccess, U2F->context);\n\n    return sizeof(U2fAuthResp) + signature_len + 2;\n}\n\nuint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) {\n    furi_assert(U2F);\n    if(!U2F->ready) return 0;\n    if((buf[0] != 0x00) && (len < 5)) return 0;\n    if(buf[1] == U2F_CMD_REGISTER) { // Register request\n        return u2f_register(U2F, buf);\n\n    } else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request\n        return u2f_authenticate(U2F, buf);\n\n    } else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string\n        memcpy(&buf[0], ver_str, 6);\n        memcpy(&buf[6], state_no_error, 2);\n        return 8;\n    } else {\n        memcpy(&buf[0], state_not_supported, 2);\n        return 2;\n    }\n    return 0;\n}\n\nvoid u2f_wink(U2fData* U2F) {\n    if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);\n}\n\nvoid u2f_set_state(U2fData* U2F, uint8_t state) {\n    if(state == 0) {\n        if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context);\n    } else {\n        if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context);\n    }\n    U2F->user_present = false;\n}\n"
  },
  {
    "path": "applications/main/u2f/u2f.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n\ntypedef enum {\n    U2fNotifyRegister,\n    U2fNotifyAuth,\n    U2fNotifyAuthSuccess,\n    U2fNotifyWink,\n    U2fNotifyConnect,\n    U2fNotifyDisconnect,\n    U2fNotifyError,\n} U2fNotifyEvent;\n\ntypedef struct U2fData U2fData;\n\ntypedef void (*U2fEvtCallback)(U2fNotifyEvent evt, void* context);\n\nU2fData* u2f_alloc(void);\n\nbool u2f_init(U2fData* instance);\n\nvoid u2f_free(U2fData* instance);\n\nvoid u2f_set_event_callback(U2fData* instance, U2fEvtCallback callback, void* context);\n\nvoid u2f_confirm_user_present(U2fData* instance);\n\nuint16_t u2f_msg_parse(U2fData* instance, uint8_t* buf, uint16_t len);\n\nvoid u2f_wink(U2fData* instance);\n\nvoid u2f_set_state(U2fData* instance, uint8_t state);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/u2f/u2f_app.c",
    "content": "#include \"u2f_app_i.h\"\n#include \"u2f_data.h\"\n#include <furi.h>\n#include <furi_hal.h>\n\nstatic bool u2f_app_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    U2fApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool u2f_app_back_event_callback(void* context) {\n    furi_assert(context);\n    U2fApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void u2f_app_tick_event_callback(void* context) {\n    furi_assert(context);\n    U2fApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nU2fApp* u2f_app_alloc(void) {\n    U2fApp* app = malloc(sizeof(U2fApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&u2f_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, u2f_app_tick_event_callback, 500);\n\n    view_dispatcher_set_custom_event_callback(app->view_dispatcher, u2f_app_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, u2f_app_back_event_callback);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Custom Widget\n    app->widget = widget_alloc();\n    view_dispatcher_add_view(app->view_dispatcher, U2fAppViewError, widget_get_view(app->widget));\n\n    app->u2f_view = u2f_view_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view));\n\n    if(furi_hal_usb_is_locked()) {\n        app->error = U2fAppErrorCloseRpc;\n        scene_manager_next_scene(app->scene_manager, U2fSceneError);\n    } else {\n        if(u2f_data_check(true)) {\n            scene_manager_next_scene(app->scene_manager, U2fSceneMain);\n        } else {\n            app->error = U2fAppErrorNoFiles;\n            scene_manager_next_scene(app->scene_manager, U2fSceneError);\n        }\n    }\n\n    return app;\n}\n\nvoid u2f_app_free(U2fApp* app) {\n    furi_assert(app);\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, U2fAppViewMain);\n    u2f_view_free(app->u2f_view);\n\n    // Custom Widget\n    view_dispatcher_remove_view(app->view_dispatcher, U2fAppViewError);\n    widget_free(app->widget);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    free(app);\n}\n\nint32_t u2f_app(void* p) {\n    UNUSED(p);\n    U2fApp* u2f_app = u2f_app_alloc();\n\n    view_dispatcher_run(u2f_app->view_dispatcher);\n\n    u2f_app_free(u2f_app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/main/u2f/u2f_app.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct U2fApp U2fApp;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/u2f/u2f_app_i.h",
    "content": "#pragma once\n\n#include \"u2f_app.h\"\n#include \"scenes/u2f_scene.h\"\n\n#include <gui/gui.h>\n#include <assets_icons.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <dialogs/dialogs.h>\n#include <notification/notification_messages.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/widget.h>\n#include \"views/u2f_view.h\"\n#include \"u2f_hid.h\"\n#include \"u2f.h\"\n\ntypedef enum {\n    U2fAppErrorNoFiles,\n    U2fAppErrorCloseRpc,\n} U2fAppError;\n\ntypedef enum {\n    U2fCustomEventNone,\n\n    U2fCustomEventConnect,\n    U2fCustomEventDisconnect,\n    U2fCustomEventDataError,\n\n    U2fCustomEventRegister,\n    U2fCustomEventAuth,\n    U2fCustomEventAuthSuccess,\n    U2fCustomEventWink,\n\n    U2fCustomEventTimeout,\n\n    U2fCustomEventConfirm,\n\n    U2fCustomEventErrorBack,\n\n} GpioCustomEvent;\n\ntypedef enum {\n    U2fAppViewError,\n    U2fAppViewMain,\n} U2fAppView;\n\nstruct U2fApp {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    NotificationApp* notifications;\n    Widget* widget;\n    FuriTimer* timer;\n    U2fHid* u2f_hid;\n    U2fView* u2f_view;\n    U2fData* u2f_instance;\n    GpioCustomEvent event_cur;\n    bool u2f_ready;\n    U2fAppError error;\n};\n"
  },
  {
    "path": "applications/main/u2f/u2f_data.c",
    "content": "#include <furi.h>\n#include \"u2f_data.h\"\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <furi_hal_random.h>\n#include <flipper_format/flipper_format.h>\n\n#define TAG \"U2f\"\n\n#define U2F_DATA_FOLDER   EXT_PATH(\"u2f/\")\n#define U2F_CERT_FILE     U2F_DATA_FOLDER \"assets/cert.der\"\n#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER \"assets/cert_key.u2f\"\n#define U2F_KEY_FILE      U2F_DATA_FOLDER \"key.u2f\"\n#define U2F_CNT_FILE      U2F_DATA_FOLDER \"cnt.u2f\"\n\n#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2\n#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE  FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT\n\n#define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key\n#define U2F_CERT_USER  1 // User certificate, private key is encrypted with unique key\n#define U2F_CERT_USER_UNENCRYPTED \\\n    2 // Unencrypted user certificate, will be encrypted after first load\n\n#define U2F_CERT_KEY_FILE_TYPE \"Flipper U2F Certificate Key File\"\n#define U2F_CERT_KEY_VERSION   1\n\n#define U2F_DEVICE_KEY_FILE_TYPE \"Flipper U2F Device Key File\"\n#define U2F_DEVICE_KEY_VERSION   1\n\n#define U2F_COUNTER_FILE_TYPE   \"Flipper U2F Counter File\"\n#define U2F_COUNTER_VERSION     2\n#define U2F_COUNTER_VERSION_OLD 1\n\n#define U2F_COUNTER_CONTROL_VAL 0xAA5500FF\n\ntypedef struct {\n    uint32_t counter;\n    uint8_t random_salt[24];\n    uint32_t control;\n} FURI_PACKED U2fCounterData;\n\nbool u2f_data_check(bool cert_only) {\n    bool state = false;\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n\n    do {\n        if(!storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        storage_file_close(file);\n        if(!storage_file_open(file, U2F_CERT_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        if(cert_only) {\n            state = true;\n            break;\n        }\n        storage_file_close(file);\n        if(!storage_file_open(file, U2F_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        storage_file_close(file);\n        if(!storage_file_open(file, U2F_CNT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        state = true;\n    } while(0);\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n\nbool u2f_data_cert_check(void) {\n    bool state = false;\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n    uint8_t file_buf[8];\n\n    if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        do {\n            // Read header to check certificate size\n            size_t file_size = storage_file_size(file);\n            size_t len_cur = storage_file_read(file, file_buf, 4);\n            if(len_cur != 4) break;\n\n            if(file_buf[0] != 0x30) {\n                FURI_LOG_E(TAG, \"Wrong certificate header\");\n                break;\n            }\n\n            size_t temp_len = ((file_buf[2] << 8) | (file_buf[3])) + 4;\n            if(temp_len != file_size) {\n                FURI_LOG_E(TAG, \"Wrong certificate length\");\n                break;\n            }\n            state = true;\n        } while(0);\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n\nuint32_t u2f_data_cert_load(uint8_t* cert) {\n    furi_assert(cert);\n\n    Storage* fs_api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(fs_api);\n    uint32_t file_size = 0;\n    uint32_t len_cur = 0;\n\n    if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        file_size = storage_file_size(file);\n        len_cur = storage_file_read(file, cert, file_size);\n        if(len_cur != file_size) len_cur = 0;\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return len_cur;\n}\n\nstatic bool u2f_data_cert_key_encrypt(uint8_t* cert_key) {\n    furi_assert(cert_key);\n\n    bool state = false;\n    uint8_t iv[16];\n    uint8_t key[48];\n    uint32_t cert_type = U2F_CERT_USER;\n\n    FURI_LOG_I(TAG, \"Encrypting user cert key\");\n\n    // Generate random IV\n    furi_hal_random_fill_buf(iv, 16);\n\n    if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {\n        FURI_LOG_E(TAG, \"Unable to load encryption key\");\n        return false;\n    }\n\n    if(!furi_hal_crypto_encrypt(cert_key, key, 32)) {\n        FURI_LOG_E(TAG, \"Encryption failed\");\n        return false;\n    }\n    furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_always(flipper_format, U2F_CERT_KEY_FILE)) {\n        do {\n            if(!flipper_format_write_header_cstr(\n                   flipper_format, U2F_CERT_KEY_FILE_TYPE, U2F_CERT_KEY_VERSION))\n                break;\n            if(!flipper_format_write_uint32(flipper_format, \"Type\", &cert_type, 1)) break;\n            if(!flipper_format_write_hex(flipper_format, \"IV\", iv, 16)) break;\n            if(!flipper_format_write_hex(flipper_format, \"Data\", key, 48)) break;\n            state = true;\n        } while(0);\n    }\n\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n\nbool u2f_data_cert_key_load(uint8_t* cert_key) {\n    furi_assert(cert_key);\n\n    bool state = false;\n    uint8_t iv[16];\n    uint8_t key[48];\n    uint32_t cert_type = 0;\n    uint8_t key_slot = 0;\n    uint32_t version = 0;\n\n    // Check if unique key exists in secure eclave and generate it if missing\n    if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;\n\n    FuriString* filetype;\n    filetype = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_existing(flipper_format, U2F_CERT_KEY_FILE)) {\n        do {\n            if(!flipper_format_read_header(flipper_format, filetype, &version)) {\n                FURI_LOG_E(TAG, \"Missing or incorrect header\");\n                break;\n            }\n\n            if(strcmp(furi_string_get_cstr(filetype), U2F_CERT_KEY_FILE_TYPE) != 0 ||\n               version != U2F_CERT_KEY_VERSION) {\n                FURI_LOG_E(TAG, \"Type or version mismatch\");\n                break;\n            }\n\n            if(!flipper_format_read_uint32(flipper_format, \"Type\", &cert_type, 1)) {\n                FURI_LOG_E(TAG, \"Missing cert type\");\n                break;\n            }\n\n            if(cert_type == U2F_CERT_STOCK) {\n                key_slot = U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY;\n            } else if(cert_type == U2F_CERT_USER) {\n                key_slot = U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE;\n            } else if(cert_type == U2F_CERT_USER_UNENCRYPTED) {\n                key_slot = 0;\n            } else {\n                FURI_LOG_E(TAG, \"Unknown cert type\");\n                break;\n            }\n            if(key_slot != 0) {\n                if(!flipper_format_read_hex(flipper_format, \"IV\", iv, 16)) {\n                    FURI_LOG_E(TAG, \"Missing IV\");\n                    break;\n                }\n\n                if(!flipper_format_read_hex(flipper_format, \"Data\", key, 48)) {\n                    FURI_LOG_E(TAG, \"Missing data\");\n                    break;\n                }\n\n                if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {\n                    FURI_LOG_E(TAG, \"Unable to load encryption key\");\n                    break;\n                }\n                memset(cert_key, 0, 32);\n\n                if(!furi_hal_crypto_decrypt(key, cert_key, 32)) {\n                    memset(cert_key, 0, 32);\n                    FURI_LOG_E(TAG, \"Decryption failed\");\n                    break;\n                }\n                furi_hal_crypto_enclave_unload_key(key_slot);\n            } else {\n                if(!flipper_format_read_hex(flipper_format, \"Data\", cert_key, 32)) {\n                    FURI_LOG_E(TAG, \"Missing data\");\n                    break;\n                }\n            }\n            state = true;\n        } while(0);\n    }\n\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(filetype);\n\n    if(cert_type == U2F_CERT_USER_UNENCRYPTED) {\n        return u2f_data_cert_key_encrypt(cert_key);\n    }\n\n    return state;\n}\n\nbool u2f_data_key_load(uint8_t* device_key) {\n    furi_assert(device_key);\n\n    bool state = false;\n    uint8_t iv[16];\n    uint8_t key[48];\n    uint32_t version = 0;\n\n    FuriString* filetype;\n    filetype = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_existing(flipper_format, U2F_KEY_FILE)) {\n        do {\n            if(!flipper_format_read_header(flipper_format, filetype, &version)) {\n                FURI_LOG_E(TAG, \"Missing or incorrect header\");\n                break;\n            }\n            if(strcmp(furi_string_get_cstr(filetype), U2F_DEVICE_KEY_FILE_TYPE) != 0 ||\n               version != U2F_DEVICE_KEY_VERSION) {\n                FURI_LOG_E(TAG, \"Type or version mismatch\");\n                break;\n            }\n            if(!flipper_format_read_hex(flipper_format, \"IV\", iv, 16)) {\n                FURI_LOG_E(TAG, \"Missing IV\");\n                break;\n            }\n            if(!flipper_format_read_hex(flipper_format, \"Data\", key, 48)) {\n                FURI_LOG_E(TAG, \"Missing data\");\n                break;\n            }\n            if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {\n                FURI_LOG_E(TAG, \"Unable to load encryption key\");\n                break;\n            }\n            memset(device_key, 0, 32);\n            if(!furi_hal_crypto_decrypt(key, device_key, 32)) {\n                memset(device_key, 0, 32);\n                FURI_LOG_E(TAG, \"Decryption failed\");\n                break;\n            }\n            furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);\n            state = true;\n        } while(0);\n    }\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(filetype);\n    return state;\n}\n\nbool u2f_data_key_generate(uint8_t* device_key) {\n    furi_assert(device_key);\n\n    bool state = false;\n    uint8_t iv[16];\n    uint8_t key[32];\n    uint8_t key_encrypted[48];\n\n    // Generate random IV and key\n    furi_hal_random_fill_buf(iv, 16);\n    furi_hal_random_fill_buf(key, 32);\n\n    if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {\n        FURI_LOG_E(TAG, \"Unable to load encryption key\");\n        return false;\n    }\n\n    if(!furi_hal_crypto_encrypt(key, key_encrypted, 32)) {\n        FURI_LOG_E(TAG, \"Encryption failed\");\n        return false;\n    }\n    furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_always(flipper_format, U2F_KEY_FILE)) {\n        do {\n            if(!flipper_format_write_header_cstr(\n                   flipper_format, U2F_DEVICE_KEY_FILE_TYPE, U2F_DEVICE_KEY_VERSION))\n                break;\n            if(!flipper_format_write_hex(flipper_format, \"IV\", iv, 16)) break;\n            if(!flipper_format_write_hex(flipper_format, \"Data\", key_encrypted, 48)) break;\n            state = true;\n            memcpy(device_key, key, 32);\n        } while(0);\n    }\n\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n\nbool u2f_data_cnt_read(uint32_t* cnt_val) {\n    furi_assert(cnt_val);\n\n    bool state = false;\n    bool old_counter = false;\n    uint8_t iv[16];\n    U2fCounterData cnt;\n    uint8_t cnt_encr[48];\n    uint32_t version = 0;\n\n    FuriString* filetype;\n    filetype = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_existing(flipper_format, U2F_CNT_FILE)) {\n        do {\n            if(!flipper_format_read_header(flipper_format, filetype, &version)) {\n                FURI_LOG_E(TAG, \"Missing or incorrect header\");\n                break;\n            }\n            if(strcmp(furi_string_get_cstr(filetype), U2F_COUNTER_FILE_TYPE) != 0) {\n                FURI_LOG_E(TAG, \"Type mismatch\");\n                break;\n            }\n            if(version == U2F_COUNTER_VERSION_OLD) {\n                // Counter is from previous U2F app version with endianness bug\n                FURI_LOG_W(TAG, \"Counter from old version\");\n                old_counter = true;\n            } else if(version != U2F_COUNTER_VERSION) {\n                FURI_LOG_E(TAG, \"Version mismatch\");\n                break;\n            }\n            if(!flipper_format_read_hex(flipper_format, \"IV\", iv, 16)) {\n                FURI_LOG_E(TAG, \"Missing IV\");\n                break;\n            }\n            if(!flipper_format_read_hex(flipper_format, \"Data\", cnt_encr, 48)) {\n                FURI_LOG_E(TAG, \"Missing data\");\n                break;\n            }\n            if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {\n                FURI_LOG_E(TAG, \"Unable to load encryption key\");\n                break;\n            }\n            memset(&cnt, 0, sizeof(U2fCounterData));\n            if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, sizeof(U2fCounterData))) {\n                memset(&cnt, 0, sizeof(U2fCounterData));\n                FURI_LOG_E(TAG, \"Decryption failed\");\n                break;\n            }\n            furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);\n            if(cnt.control == U2F_COUNTER_CONTROL_VAL) {\n                *cnt_val = cnt.counter;\n                state = true;\n            }\n        } while(0);\n    }\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(filetype);\n\n    if(old_counter && state) {\n        // Change counter endianness and rewrite counter file\n        *cnt_val = __REV(cnt.counter);\n        state = u2f_data_cnt_write(*cnt_val);\n    }\n\n    return state;\n}\n\nbool u2f_data_cnt_write(uint32_t cnt_val) {\n    bool state = false;\n    uint8_t iv[16];\n    U2fCounterData cnt;\n    uint8_t cnt_encr[48];\n\n    // Generate random IV and key\n    furi_hal_random_fill_buf(iv, 16);\n    furi_hal_random_fill_buf(cnt.random_salt, 24);\n    cnt.control = U2F_COUNTER_CONTROL_VAL;\n    cnt.counter = cnt_val;\n\n    if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {\n        FURI_LOG_E(TAG, \"Unable to load encryption key\");\n        return false;\n    }\n\n    if(!furi_hal_crypto_encrypt((uint8_t*)&cnt, cnt_encr, 32)) {\n        FURI_LOG_E(TAG, \"Encryption failed\");\n        return false;\n    }\n    furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n\n    if(flipper_format_file_open_always(flipper_format, U2F_CNT_FILE)) {\n        do {\n            if(!flipper_format_write_header_cstr(\n                   flipper_format, U2F_COUNTER_FILE_TYPE, U2F_COUNTER_VERSION))\n                break;\n            if(!flipper_format_write_hex(flipper_format, \"IV\", iv, 16)) break;\n            if(!flipper_format_write_hex(flipper_format, \"Data\", cnt_encr, 48)) break;\n            state = true;\n        } while(0);\n    }\n\n    flipper_format_free(flipper_format);\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n"
  },
  {
    "path": "applications/main/u2f/u2f_data.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n\nbool u2f_data_check(bool cert_only);\n\nbool u2f_data_cert_check(void);\n\nuint32_t u2f_data_cert_load(uint8_t* cert);\n\nbool u2f_data_cert_key_load(uint8_t* cert_key);\n\nbool u2f_data_key_load(uint8_t* device_key);\n\nbool u2f_data_key_generate(uint8_t* device_key);\n\nbool u2f_data_cnt_read(uint32_t* cnt);\n\nbool u2f_data_cnt_write(uint32_t cnt);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/u2f/u2f_hid.c",
    "content": "#include <furi.h>\n#include \"u2f_hid.h\"\n#include \"u2f.h\"\n#include <furi_hal.h>\n#include <gui/gui.h>\n#include <input/input.h>\n#include <lib/toolbox/args.h>\n#include <furi_hal_usb_hid_u2f.h>\n#include <storage/storage.h>\n\n#define TAG \"U2fHid\"\n\n#define WORKER_TAG TAG \"Worker\"\n\n#define U2F_HID_MAX_PAYLOAD_LEN ((HID_U2F_PACKET_LEN - 7) + 128 * (HID_U2F_PACKET_LEN - 5))\n\n#define U2F_HID_TYPE_MASK 0x80 // Frame type mask\n#define U2F_HID_TYPE_INIT 0x80 // Initial frame identifier\n#define U2F_HID_TYPE_CONT 0x00 // Continuation frame identifier\n\n#define U2F_HID_PING  (U2F_HID_TYPE_INIT | 0x01) // Echo data through local processor only\n#define U2F_HID_MSG   (U2F_HID_TYPE_INIT | 0x03) // Send U2F message frame\n#define U2F_HID_LOCK  (U2F_HID_TYPE_INIT | 0x04) // Send lock channel command\n#define U2F_HID_INIT  (U2F_HID_TYPE_INIT | 0x06) // Channel initialization\n#define U2F_HID_WINK  (U2F_HID_TYPE_INIT | 0x08) // Send device identification wink\n#define U2F_HID_ERROR (U2F_HID_TYPE_INIT | 0x3f) // Error response\n\n#define U2F_HID_ERR_NONE          0x00 // No error\n#define U2F_HID_ERR_INVALID_CMD   0x01 // Invalid command\n#define U2F_HID_ERR_INVALID_PAR   0x02 // Invalid parameter\n#define U2F_HID_ERR_INVALID_LEN   0x03 // Invalid message length\n#define U2F_HID_ERR_INVALID_SEQ   0x04 // Invalid message sequencing\n#define U2F_HID_ERR_MSG_TIMEOUT   0x05 // Message has timed out\n#define U2F_HID_ERR_CHANNEL_BUSY  0x06 // Channel busy\n#define U2F_HID_ERR_LOCK_REQUIRED 0x0a // Command requires channel lock\n#define U2F_HID_ERR_SYNC_FAIL     0x0b // SYNC command failed\n#define U2F_HID_ERR_OTHER         0x7f // Other unspecified error\n\n#define U2F_HID_BROADCAST_CID 0xFFFFFFFF\n\ntypedef enum {\n    WorkerEvtReserved = (1 << 0),\n    WorkerEvtStop = (1 << 1),\n    WorkerEvtConnect = (1 << 2),\n    WorkerEvtDisconnect = (1 << 3),\n    WorkerEvtRequest = (1 << 4),\n    WorkerEvtUnlock = (1 << 5),\n} WorkerEvtFlags;\n\nstruct U2fHid_packet {\n    uint32_t cid;\n    uint16_t len;\n    uint8_t cmd;\n    uint8_t payload[U2F_HID_MAX_PAYLOAD_LEN];\n};\n\nstruct U2fHid {\n    FuriThread* thread;\n    FuriTimer* lock_timer;\n    uint8_t seq_id_last;\n    uint16_t req_buf_ptr;\n    uint32_t req_len_left;\n    uint32_t lock_cid;\n    bool lock;\n    U2fData* u2f_instance;\n    struct U2fHid_packet packet;\n};\n\nstatic void u2f_hid_event_callback(HidU2fEvent ev, void* context) {\n    furi_assert(context);\n    U2fHid* u2f_hid = context;\n\n    if(ev == HidU2fDisconnected)\n        furi_thread_flags_set(furi_thread_get_id(u2f_hid->thread), WorkerEvtDisconnect);\n    else if(ev == HidU2fConnected)\n        furi_thread_flags_set(furi_thread_get_id(u2f_hid->thread), WorkerEvtConnect);\n    else if(ev == HidU2fRequest)\n        furi_thread_flags_set(furi_thread_get_id(u2f_hid->thread), WorkerEvtRequest);\n}\n\nstatic void u2f_hid_lock_timeout_callback(void* context) {\n    furi_assert(context);\n    U2fHid* u2f_hid = context;\n\n    furi_thread_flags_set(furi_thread_get_id(u2f_hid->thread), WorkerEvtUnlock);\n}\n\nstatic void u2f_hid_send_response(U2fHid* u2f_hid) {\n    uint8_t packet_buf[HID_U2F_PACKET_LEN];\n    uint16_t len_remain = u2f_hid->packet.len;\n    uint8_t len_cur = 0;\n    uint8_t seq_cnt = 0;\n    uint16_t data_ptr = 0;\n\n    memset(packet_buf, 0, HID_U2F_PACKET_LEN);\n    memcpy(packet_buf, &(u2f_hid->packet.cid), sizeof(uint32_t)); //-V1086\n\n    // Init packet\n    packet_buf[4] = u2f_hid->packet.cmd;\n    packet_buf[5] = u2f_hid->packet.len >> 8;\n    packet_buf[6] = (u2f_hid->packet.len & 0xFF);\n    len_cur = (len_remain < (HID_U2F_PACKET_LEN - 7)) ? (len_remain) : (HID_U2F_PACKET_LEN - 7);\n    if(len_cur > 0) memcpy(&packet_buf[7], u2f_hid->packet.payload, len_cur);\n    furi_hal_hid_u2f_send_response(packet_buf, HID_U2F_PACKET_LEN);\n    data_ptr = len_cur;\n    len_remain -= len_cur;\n\n    // Continuation packets\n    while(len_remain > 0) {\n        memset(&packet_buf[4], 0, HID_U2F_PACKET_LEN - 4);\n        packet_buf[4] = seq_cnt;\n        len_cur = (len_remain < (HID_U2F_PACKET_LEN - 5)) ? (len_remain) :\n                                                            (HID_U2F_PACKET_LEN - 5);\n        memcpy(&packet_buf[5], &(u2f_hid->packet.payload[data_ptr]), len_cur);\n        furi_hal_hid_u2f_send_response(packet_buf, HID_U2F_PACKET_LEN);\n        seq_cnt++;\n        len_remain -= len_cur;\n        data_ptr += len_cur;\n    }\n}\n\nstatic void u2f_hid_send_error(U2fHid* u2f_hid, uint8_t error) {\n    u2f_hid->packet.len = 1;\n    u2f_hid->packet.cmd = U2F_HID_ERROR;\n    u2f_hid->packet.payload[0] = error;\n    u2f_hid_send_response(u2f_hid);\n}\n\nstatic bool u2f_hid_parse_request(U2fHid* u2f_hid) {\n    FURI_LOG_D(\n        WORKER_TAG,\n        \"Req cid=%lX cmd=%x len=%u\",\n        u2f_hid->packet.cid,\n        u2f_hid->packet.cmd,\n        u2f_hid->packet.len);\n\n    if(u2f_hid->packet.cmd == U2F_HID_PING) { // PING - echo request back\n        u2f_hid_send_response(u2f_hid);\n\n    } else if(u2f_hid->packet.cmd == U2F_HID_MSG) { // MSG - U2F message\n        if((u2f_hid->lock == true) && (u2f_hid->packet.cid != u2f_hid->lock_cid)) return false;\n        uint16_t resp_len =\n            u2f_msg_parse(u2f_hid->u2f_instance, u2f_hid->packet.payload, u2f_hid->packet.len);\n        if(resp_len > 0) {\n            u2f_hid->packet.len = resp_len;\n            u2f_hid_send_response(u2f_hid);\n        } else\n            return false;\n\n    } else if(u2f_hid->packet.cmd == U2F_HID_LOCK) { // LOCK - lock all channels except current\n        if(u2f_hid->packet.len != 1) return false;\n        uint8_t lock_timeout = u2f_hid->packet.payload[0];\n        if(lock_timeout == 0) { // Lock off\n            u2f_hid->lock = false;\n            u2f_hid->lock_cid = 0;\n        } else { // Lock on\n            u2f_hid->lock = true;\n            u2f_hid->lock_cid = u2f_hid->packet.cid;\n            furi_timer_start(u2f_hid->lock_timer, lock_timeout * 1000);\n        }\n\n    } else if(u2f_hid->packet.cmd == U2F_HID_INIT) { // INIT - channel initialization request\n        if((u2f_hid->packet.len != 8) || (u2f_hid->packet.cid != U2F_HID_BROADCAST_CID) ||\n           (u2f_hid->lock == true))\n            return false;\n        u2f_hid->packet.len = 17;\n        uint32_t random_cid = furi_hal_random_get();\n        memcpy(&(u2f_hid->packet.payload[8]), &random_cid, sizeof(uint32_t)); //-V1086\n        u2f_hid->packet.payload[12] = 2; // Protocol version\n        u2f_hid->packet.payload[13] = 1; // Device version major\n        u2f_hid->packet.payload[14] = 0; // Device version minor\n        u2f_hid->packet.payload[15] = 1; // Device build version\n        u2f_hid->packet.payload[16] = 1; // Capabilities: wink\n        u2f_hid_send_response(u2f_hid);\n\n    } else if(u2f_hid->packet.cmd == U2F_HID_WINK) { // WINK - notify user\n        if(u2f_hid->packet.len != 0) return false;\n        u2f_wink(u2f_hid->u2f_instance);\n        u2f_hid->packet.len = 0; //-V1048\n        u2f_hid_send_response(u2f_hid);\n    } else\n        return false;\n    return true;\n}\n\nstatic int32_t u2f_hid_worker(void* context) {\n    U2fHid* u2f_hid = context;\n    uint8_t packet_buf[HID_U2F_PACKET_LEN];\n\n    FURI_LOG_D(WORKER_TAG, \"Init\");\n\n    FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();\n    furi_check(furi_hal_usb_set_config(&usb_hid_u2f, NULL) == true);\n\n    u2f_hid->lock_timer =\n        furi_timer_alloc(u2f_hid_lock_timeout_callback, FuriTimerTypeOnce, u2f_hid);\n\n    furi_hal_hid_u2f_set_callback(u2f_hid_event_callback, u2f_hid);\n\n    while(1) {\n        uint32_t flags = furi_thread_flags_wait(\n            WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,\n            FuriFlagWaitAny,\n            FuriWaitForever);\n        furi_check(!(flags & FuriFlagError));\n        if(flags & WorkerEvtStop) break;\n        if(flags & WorkerEvtConnect) {\n            u2f_set_state(u2f_hid->u2f_instance, 1);\n            FURI_LOG_D(WORKER_TAG, \"Connect\");\n        }\n        if(flags & WorkerEvtDisconnect) {\n            u2f_set_state(u2f_hid->u2f_instance, 0);\n            FURI_LOG_D(WORKER_TAG, \"Disconnect\");\n        }\n        if(flags & WorkerEvtRequest) {\n            uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);\n            do {\n                if(len_cur == 0) {\n                    break;\n                }\n                if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {\n                    if(len_cur < 7) {\n                        u2f_hid->req_len_left = 0;\n                        break; // Wrong chunk len\n                    }\n                    // Init packet\n                    u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);\n                    if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) {\n                        u2f_hid->req_len_left = 0;\n                        break; // Wrong packet len\n                    }\n                    if(u2f_hid->packet.len > (len_cur - 7)) {\n                        u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);\n                        len_cur = len_cur - 7;\n                    } else {\n                        u2f_hid->req_len_left = 0;\n                        len_cur = u2f_hid->packet.len;\n                    }\n                    memcpy(&(u2f_hid->packet.cid), packet_buf, 4);\n                    u2f_hid->packet.cmd = packet_buf[4];\n                    u2f_hid->seq_id_last = 0;\n                    u2f_hid->req_buf_ptr = len_cur;\n                    if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);\n                } else {\n                    if(len_cur < 5) {\n                        u2f_hid->req_len_left = 0;\n                        break; // Wrong chunk len\n                    }\n                    // Continuation packet\n                    if(u2f_hid->req_len_left > 0) {\n                        uint32_t cid_temp = 0;\n                        memcpy(&cid_temp, packet_buf, 4);\n                        uint8_t seq_temp = packet_buf[4];\n                        if((cid_temp == u2f_hid->packet.cid) &&\n                           (seq_temp == u2f_hid->seq_id_last)) {\n                            if(u2f_hid->req_len_left > (len_cur - 5)) {\n                                len_cur = len_cur - 5;\n                                u2f_hid->req_len_left -= len_cur;\n                            } else {\n                                len_cur = u2f_hid->req_len_left;\n                                u2f_hid->req_len_left = 0;\n                            }\n                            memcpy(\n                                &(u2f_hid->packet.payload[u2f_hid->req_buf_ptr]),\n                                &packet_buf[5],\n                                len_cur);\n                            u2f_hid->req_buf_ptr += len_cur;\n                            u2f_hid->seq_id_last++;\n                        }\n                    }\n                }\n                if(u2f_hid->req_len_left == 0) {\n                    if(u2f_hid_parse_request(u2f_hid) == false) {\n                        u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);\n                    }\n                }\n            } while(0);\n        }\n        if(flags & WorkerEvtUnlock) {\n            u2f_hid->lock = false;\n            u2f_hid->lock_cid = 0;\n        }\n    }\n    furi_timer_stop(u2f_hid->lock_timer);\n    furi_timer_free(u2f_hid->lock_timer);\n\n    furi_hal_hid_u2f_set_callback(NULL, NULL);\n    furi_hal_usb_set_config(usb_mode_prev, NULL);\n    FURI_LOG_D(WORKER_TAG, \"End\");\n\n    return 0;\n}\n\nU2fHid* u2f_hid_start(U2fData* u2f_inst) {\n    U2fHid* u2f_hid = malloc(sizeof(U2fHid));\n\n    u2f_hid->u2f_instance = u2f_inst;\n\n    u2f_hid->thread = furi_thread_alloc_ex(\"U2fHidWorker\", 2048, u2f_hid_worker, u2f_hid);\n    furi_thread_start(u2f_hid->thread);\n    return u2f_hid;\n}\n\nvoid u2f_hid_stop(U2fHid* u2f_hid) {\n    furi_assert(u2f_hid);\n    furi_thread_flags_set(furi_thread_get_id(u2f_hid->thread), WorkerEvtStop);\n    furi_thread_join(u2f_hid->thread);\n    furi_thread_free(u2f_hid->thread);\n    free(u2f_hid);\n}\n"
  },
  {
    "path": "applications/main/u2f/u2f_hid.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n#include \"u2f.h\"\n\ntypedef struct U2fHid U2fHid;\n\nU2fHid* u2f_hid_start(U2fData* u2f_inst);\n\nvoid u2f_hid_stop(U2fHid* u2f_hid);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/main/u2f/views/u2f_view.c",
    "content": "#include \"u2f_view.h\"\n#include <gui/elements.h>\n#include <assets_icons.h>\n\nstruct U2fView {\n    View* view;\n    U2fOkCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    U2fViewMsg display_msg;\n} U2fModel;\n\nstatic void u2f_view_draw_callback(Canvas* canvas, void* _model) {\n    U2fModel* model = _model;\n\n    canvas_draw_icon(canvas, 8, 14, &I_Drive_112x35);\n    canvas_set_font(canvas, FontSecondary);\n\n    if(model->display_msg == U2fMsgNotConnected) {\n        canvas_draw_icon(canvas, 22, 15, &I_Connect_me_62x31);\n        canvas_draw_str_aligned(\n            canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Connect me to computer\");\n    } else if(model->display_msg == U2fMsgIdle) {\n        canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);\n        canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Connected!\");\n    } else if(model->display_msg == U2fMsgRegister) {\n        elements_button_center(canvas, \"OK\");\n        canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31);\n        canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Press OK to register\");\n    } else if(model->display_msg == U2fMsgAuth) {\n        elements_button_center(canvas, \"OK\");\n        canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31);\n        canvas_draw_str_aligned(\n            canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Press OK to authenticate\");\n    } else if(model->display_msg == U2fMsgSuccess) {\n        canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);\n        canvas_draw_str_aligned(\n            canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Authentication successful!\");\n    } else if(model->display_msg == U2fMsgError) {\n        canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);\n        canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, \"Certificate error\");\n    }\n}\n\nstatic bool u2f_view_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    U2fView* u2f = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyOk) {\n            consumed = true;\n            if(u2f->callback != NULL) u2f->callback(InputTypeShort, u2f->context);\n        }\n    }\n\n    return consumed;\n}\n\nU2fView* u2f_view_alloc(void) {\n    U2fView* u2f = malloc(sizeof(U2fView));\n\n    u2f->view = view_alloc();\n    view_allocate_model(u2f->view, ViewModelTypeLocking, sizeof(U2fModel));\n    view_set_context(u2f->view, u2f);\n    view_set_draw_callback(u2f->view, u2f_view_draw_callback);\n    view_set_input_callback(u2f->view, u2f_view_input_callback);\n\n    return u2f;\n}\n\nvoid u2f_view_free(U2fView* u2f) {\n    furi_assert(u2f);\n    view_free(u2f->view);\n    free(u2f);\n}\n\nView* u2f_view_get_view(U2fView* u2f) {\n    furi_assert(u2f);\n    return u2f->view;\n}\n\nvoid u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* context) {\n    furi_assert(u2f);\n    furi_assert(callback);\n    with_view_model(\n        u2f->view,\n        U2fModel * model,\n        {\n            UNUSED(model);\n            u2f->callback = callback;\n            u2f->context = context;\n        },\n        false);\n}\n\nvoid u2f_view_set_state(U2fView* u2f, U2fViewMsg msg) {\n    with_view_model(u2f->view, U2fModel * model, { model->display_msg = msg; }, true);\n}\n"
  },
  {
    "path": "applications/main/u2f/views/u2f_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct U2fView U2fView;\ntypedef void (*U2fOkCallback)(InputType type, void* context);\n\ntypedef enum {\n    U2fMsgNotConnected,\n    U2fMsgIdle,\n    U2fMsgRegister,\n    U2fMsgAuth,\n    U2fMsgSuccess,\n    U2fMsgError,\n} U2fViewMsg;\n\nU2fView* u2f_view_alloc(void);\n\nvoid u2f_view_free(U2fView* u2f);\n\nView* u2f_view_get_view(U2fView* u2f);\n\nvoid u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* context);\n\nvoid u2f_view_set_state(U2fView* u2f, U2fViewMsg msg);\n"
  },
  {
    "path": "applications/services/application.fam",
    "content": "App(\n    appid=\"basic_services\",\n    name=\"Basic services\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"cli_vcp\",\n        \"crypto_start\",\n        \"rpc_start\",\n        \"expansion_start\",\n        \"bt\",\n        \"desktop\",\n        \"loader\",\n        \"power\",\n    ],\n)\n"
  },
  {
    "path": "applications/services/applications.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <gui/icon.h>\n\ntypedef enum {\n    FlipperInternalApplicationFlagDefault = 0,\n    FlipperInternalApplicationFlagInsomniaSafe = (1 << 0),\n} FlipperInternalApplicationFlag;\n\ntypedef struct {\n    const FuriThreadCallback app;\n    const char* name;\n    const char* appid;\n    const size_t stack_size;\n    const Icon* icon;\n    const FlipperInternalApplicationFlag flags;\n} FlipperInternalApplication;\n\ntypedef struct {\n    const char* name;\n    const Icon* icon;\n    const char* path;\n} FlipperExternalApplication;\n\ntypedef void (*FlipperInternalOnStartHook)(void);\n\nextern const char* FLIPPER_AUTORUN_APP_NAME;\n\n/* Services list\n * Spawned on startup\n */\nextern const FlipperInternalApplication FLIPPER_SERVICES[];\nextern const size_t FLIPPER_SERVICES_COUNT;\n\n/* Apps list\n * Spawned by loader\n */\nextern const FlipperInternalApplication FLIPPER_APPS[];\nextern const size_t FLIPPER_APPS_COUNT;\n\n/* On system start hooks\n * Called by loader, after OS initialization complete\n */\nextern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[];\nextern const size_t FLIPPER_ON_SYSTEM_START_COUNT;\n\n/* System apps\n * Can only be spawned by loader by name\n */\nextern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[];\nextern const size_t FLIPPER_SYSTEM_APPS_COUNT;\n\n/* Debug apps \n * Can only be spawned by loader by name\n */\nextern const FlipperInternalApplication FLIPPER_DEBUG_APPS[];\nextern const size_t FLIPPER_DEBUG_APPS_COUNT;\n\nextern const FlipperInternalApplication FLIPPER_ARCHIVE;\n\n/* Settings list\n * Spawned by loader\n */\nextern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[];\nextern const size_t FLIPPER_SETTINGS_APPS_COUNT;\n\n/* External Menu Apps list\n * Spawned by loader\n */\nextern const FlipperExternalApplication FLIPPER_EXTERNAL_APPS[];\nextern const size_t FLIPPER_EXTERNAL_APPS_COUNT;\n"
  },
  {
    "path": "applications/services/bt/application.fam",
    "content": "App(\n    appid=\"bt\",\n    name=\"BtSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"bt_srv\",\n    cdefines=[\"SRV_BT\"],\n    requires=[\n        \"cli\",\n        \"dialogs\",\n    ],\n    provides=[\n        \"bt_start\",\n        \"bt_settings\",\n    ],\n    stack_size=1 * 1024,\n    order=20,\n    sdk_headers=[\"bt_service/bt.h\", \"bt_service/bt_keys_storage.h\"],\n)\n\nApp(\n    appid=\"bt_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"bt_on_system_start\",\n    order=40,\n)\n"
  },
  {
    "path": "applications/services/bt/bt_cli.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <lib/toolbox/args.h>\n#include <toolbox/pipe.h>\n#include <cli/cli_main_commands.h>\n#include <toolbox/cli/cli_registry.h>\n\n#include <ble/ble.h>\n#include \"bt_settings.h\"\n#include \"bt_service/bt.h\"\n#include <profiles/serial_profile.h>\n\nstatic void bt_cli_command_hci_info(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n    FuriString* buffer;\n    buffer = furi_string_alloc();\n    furi_hal_bt_dump_state(buffer);\n    printf(\"%s\", furi_string_get_cstr(buffer));\n    furi_string_free(buffer);\n}\n\nstatic void bt_cli_command_carrier_tx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    int channel = 0;\n    int power = 0;\n\n    do {\n        if(!args_read_int_and_trim(args, &channel) && (channel < 0 || channel > 39)) {\n            printf(\"Incorrect or missing channel, expected int 0-39\");\n            break;\n        }\n        if(!args_read_int_and_trim(args, &power) && (power < 0 || power > 6)) {\n            printf(\"Incorrect or missing power, expected int 0-6\");\n            break;\n        }\n\n        Bt* bt = furi_record_open(RECORD_BT);\n        bt_disconnect(bt);\n        furi_hal_bt_reinit();\n        printf(\"Transmitting carrier at %d channel at %d dB power\\r\\n\", channel, power);\n        printf(\"Press CTRL+C to stop\\r\\n\");\n        furi_hal_bt_start_tone_tx(channel, 0x19 + power);\n\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(250);\n        }\n        furi_hal_bt_stop_tone_tx();\n\n        bt_profile_restore_default(bt);\n        furi_record_close(RECORD_BT);\n    } while(false);\n}\n\nstatic void bt_cli_command_carrier_rx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    int channel = 0;\n\n    do {\n        if(!args_read_int_and_trim(args, &channel) && (channel < 0 || channel > 39)) {\n            printf(\"Incorrect or missing channel, expected int 0-39\");\n            break;\n        }\n\n        Bt* bt = furi_record_open(RECORD_BT);\n        bt_disconnect(bt);\n        furi_hal_bt_reinit();\n        printf(\"Receiving carrier at %d channel\\r\\n\", channel);\n        printf(\"Press CTRL+C to stop\\r\\n\");\n\n        furi_hal_bt_start_packet_rx(channel, 1);\n\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(250);\n            printf(\"RSSI: %6.1f dB\\r\", (double)furi_hal_bt_get_rssi());\n            fflush(stdout);\n        }\n\n        furi_hal_bt_stop_packet_test();\n\n        bt_profile_restore_default(bt);\n        furi_record_close(RECORD_BT);\n    } while(false);\n}\n\nstatic void bt_cli_command_packet_tx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    int channel = 0;\n    int pattern = 0;\n    int datarate = 1;\n\n    do {\n        if(!args_read_int_and_trim(args, &channel) && (channel < 0 || channel > 39)) {\n            printf(\"Incorrect or missing channel, expected int 0-39\");\n            break;\n        }\n        if(!args_read_int_and_trim(args, &pattern) && (pattern < 0 || pattern > 5)) {\n            printf(\"Incorrect or missing pattern, expected int 0-5 \\r\\n\");\n            printf(\"0 - Pseudo-Random bit sequence 9\\r\\n\");\n            printf(\"1 - Pattern of alternating bits '11110000'\\r\\n\");\n            printf(\"2 - Pattern of alternating bits '10101010'\\r\\n\");\n            printf(\"3 - Pseudo-Random bit sequence 15\\r\\n\");\n            printf(\"4 - Pattern of All '1' bits\\r\\n\");\n            printf(\"5 - Pattern of All '0' bits\\r\\n\");\n            break;\n        }\n        if(!args_read_int_and_trim(args, &datarate) && (datarate < 1 || datarate > 2)) {\n            printf(\"Incorrect or missing datarate, expected int 1-2\");\n            break;\n        }\n\n        Bt* bt = furi_record_open(RECORD_BT);\n        bt_disconnect(bt);\n        furi_hal_bt_reinit();\n        printf(\n            \"Transmitting %d pattern packet at %d channel at %d M datarate\\r\\n\",\n            pattern,\n            channel,\n            datarate);\n        printf(\"Press CTRL+C to stop\\r\\n\");\n        furi_hal_bt_start_packet_tx(channel, pattern, datarate);\n\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(250);\n        }\n        furi_hal_bt_stop_packet_test();\n        printf(\"Transmitted %lu packets\", furi_hal_bt_get_transmitted_packets());\n\n        bt_profile_restore_default(bt);\n        furi_record_close(RECORD_BT);\n    } while(false);\n}\n\nstatic void bt_cli_command_packet_rx(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    int channel = 0;\n    int datarate = 1;\n\n    do {\n        if(!args_read_int_and_trim(args, &channel) && (channel < 0 || channel > 39)) {\n            printf(\"Incorrect or missing channel, expected int 0-39\");\n            break;\n        }\n        if(!args_read_int_and_trim(args, &datarate) && (datarate < 1 || datarate > 2)) {\n            printf(\"Incorrect or missing datarate, expected int 1-2\");\n            break;\n        }\n\n        Bt* bt = furi_record_open(RECORD_BT);\n        bt_disconnect(bt);\n        furi_hal_bt_reinit();\n        printf(\"Receiving packets at %d channel at %d M datarate\\r\\n\", channel, datarate);\n        printf(\"Press CTRL+C to stop\\r\\n\");\n        furi_hal_bt_start_packet_rx(channel, datarate);\n\n        while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n            furi_delay_ms(250);\n            printf(\"RSSI: %03.1f dB\\r\", (double)furi_hal_bt_get_rssi());\n            fflush(stdout);\n        }\n        uint16_t packets_received = furi_hal_bt_stop_packet_test();\n        printf(\"Received %hu packets\", packets_received);\n\n        bt_profile_restore_default(bt);\n        furi_record_close(RECORD_BT);\n    } while(false);\n}\n\nstatic void bt_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"bt <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n    printf(\"\\thci_info\\t - HCI info\\r\\n\");\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) {\n        printf(\"\\ttx_carrier <channel:0-39> <power:0-6>\\t - start tx carrier test\\r\\n\");\n        printf(\"\\trx_carrier <channel:0-39>\\t - start rx carrier test\\r\\n\");\n        printf(\n            \"\\ttx_packet <channel:0-39> <pattern:0-5> <datarate:1-2>\\t - start tx packet test\\r\\n\");\n        printf(\"\\trx_packet <channel:0-39> <datarate:1-2>\\t - start rx packer test\\r\\n\");\n    }\n}\n\nstatic void bt_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    furi_record_open(RECORD_BT);\n\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n    BtSettings bt_settings;\n    bt_settings_load(&bt_settings);\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            bt_cli_print_usage();\n            break;\n        }\n        if(furi_string_cmp_str(cmd, \"hci_info\") == 0) {\n            bt_cli_command_hci_info(pipe, args, NULL);\n            break;\n        }\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) {\n            if(furi_string_cmp_str(cmd, \"tx_carrier\") == 0) {\n                bt_cli_command_carrier_tx(pipe, args, NULL);\n                break;\n            }\n            if(furi_string_cmp_str(cmd, \"rx_carrier\") == 0) {\n                bt_cli_command_carrier_rx(pipe, args, NULL);\n                break;\n            }\n            if(furi_string_cmp_str(cmd, \"tx_packet\") == 0) {\n                bt_cli_command_packet_tx(pipe, args, NULL);\n                break;\n            }\n            if(furi_string_cmp_str(cmd, \"rx_packet\") == 0) {\n                bt_cli_command_packet_rx(pipe, args, NULL);\n                break;\n            }\n        }\n\n        bt_cli_print_usage();\n    } while(false);\n\n    if(bt_settings.enabled) {\n        furi_hal_bt_start_advertising();\n    }\n\n    furi_string_free(cmd);\n    furi_record_close(RECORD_BT);\n}\n\nvoid bt_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(registry, \"bt\", CliCommandFlagDefault, bt_cli, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(bt_cli);\n#endif\n}\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt.c",
    "content": "#include \"bt_i.h\"\n#include \"bt_keys_storage.h\"\n\n#include <core/check.h>\n#include <furi_hal_bt.h>\n#include <services/battery_service.h>\n#include <notification/notification_messages.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <profiles/serial_profile.h>\n\n#define TAG \"BtSrv\"\n\n#define BT_RPC_EVENT_BUFF_SENT    (1UL << 0)\n#define BT_RPC_EVENT_DISCONNECTED (1UL << 1)\n#define BT_RPC_EVENT_ALL          (BT_RPC_EVENT_BUFF_SENT | BT_RPC_EVENT_DISCONNECTED)\n\n#define ICON_SPACER 2\n\nstatic void bt_draw_statusbar_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n\n    Bt* bt = context;\n    uint8_t draw_offset = 0;\n    if(bt->beacon_active) {\n        canvas_draw_icon(canvas, 0, 0, &I_BLE_beacon_7x8);\n        draw_offset += icon_get_width(&I_BLE_beacon_7x8) + ICON_SPACER;\n    }\n    if(bt->status == BtStatusAdvertising) {\n        canvas_draw_icon(canvas, draw_offset, 0, &I_Bluetooth_Idle_5x8);\n    } else if(bt->status == BtStatusConnected) {\n        canvas_draw_icon(canvas, draw_offset, 0, &I_Bluetooth_Connected_16x8);\n    }\n}\n\nstatic ViewPort* bt_statusbar_view_port_alloc(Bt* bt) {\n    ViewPort* statusbar_view_port = view_port_alloc();\n    view_port_set_width(statusbar_view_port, 5);\n    view_port_draw_callback_set(statusbar_view_port, bt_draw_statusbar_callback, bt);\n    view_port_enabled_set(statusbar_view_port, false);\n    return statusbar_view_port;\n}\n\nstatic void bt_pin_code_view_port_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    char pin_code_info[24];\n    canvas_draw_icon(canvas, 0, 0, &I_BLE_Pairing_128x64);\n    snprintf(pin_code_info, sizeof(pin_code_info), \"Pairing code\\n%06lu\", bt->pin_code);\n    elements_multiline_text_aligned(canvas, 64, 4, AlignCenter, AlignTop, pin_code_info);\n    elements_button_left(canvas, \"Quit\");\n}\n\nstatic void bt_pin_code_view_port_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyLeft || event->key == InputKeyBack) {\n            view_port_enabled_set(bt->pin_code_view_port, false);\n        }\n    }\n}\n\nstatic void bt_storage_callback(const void* message, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        const BtMessage msg = {\n            .type = BtMessageTypeReloadKeysSettings,\n        };\n\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n    }\n}\n\nstatic ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, bt_pin_code_view_port_draw_callback, bt);\n    view_port_input_callback_set(view_port, bt_pin_code_view_port_input_callback, bt);\n    view_port_enabled_set(view_port, false);\n    return view_port;\n}\n\nstatic void bt_pin_code_show(Bt* bt, uint32_t pin_code) {\n    bt->pin_code = pin_code;\n    if(!bt->pin_code_view_port) {\n        // Pin code view port\n        bt->pin_code_view_port = bt_pin_code_view_port_alloc(bt);\n        gui_add_view_port(bt->gui, bt->pin_code_view_port, GuiLayerFullscreen);\n    }\n    notification_message(bt->notification, &sequence_display_backlight_on);\n    gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);\n    view_port_enabled_set(bt->pin_code_view_port, true);\n}\n\nstatic void bt_pin_code_hide(Bt* bt) {\n    bt->pin_code = 0;\n    if(bt->pin_code_view_port && view_port_is_enabled(bt->pin_code_view_port)) {\n        view_port_enabled_set(bt->pin_code_view_port, false);\n    }\n}\n\nstatic bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {\n    furi_assert(bt);\n    notification_message(bt->notification, &sequence_display_backlight_on);\n    FuriString* pin_str;\n    if(!bt->dialog_message) {\n        bt->dialog_message = dialog_message_alloc();\n    }\n    dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);\n    pin_str = furi_string_alloc_printf(\"Verify code\\n%06lu\", pin);\n    dialog_message_set_text(\n        bt->dialog_message, furi_string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop);\n    dialog_message_set_buttons(bt->dialog_message, \"Cancel\", \"OK\", NULL);\n    DialogMessageButton button = dialog_message_show(bt->dialogs, bt->dialog_message);\n    furi_string_free(pin_str);\n    return button == DialogMessageButtonCenter;\n}\n\nstatic void bt_battery_level_changed_callback(const void* _event, void* context) {\n    furi_assert(_event);\n    furi_assert(context);\n\n    Bt* bt = context;\n    BtMessage message = {};\n    const PowerEvent* event = _event;\n    bool is_charging = false;\n    switch(event->type) {\n    case PowerEventTypeBatteryLevelChanged:\n        message.type = BtMessageTypeUpdateBatteryLevel;\n        message.data.battery_level = event->data.battery_level;\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n        break;\n    case PowerEventTypeStartCharging:\n        is_charging = true;\n        /* fallthrough */\n    case PowerEventTypeFullyCharged:\n    case PowerEventTypeStopCharging:\n        message.type = BtMessageTypeUpdatePowerState;\n        message.data.power_state_charging = is_charging;\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n        break;\n    }\n}\n\nBt* bt_alloc(void) {\n    Bt* bt = malloc(sizeof(Bt));\n    // Init default maximum packet size\n    bt->max_packet_size = BLE_PROFILE_SERIAL_PACKET_SIZE_MAX;\n    bt->current_profile = NULL;\n    // Keys storage\n    bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH);\n    // Alloc queue\n    bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage));\n\n    // Setup statusbar view port\n    bt->statusbar_view_port = bt_statusbar_view_port_alloc(bt);\n    // Notification\n    bt->notification = furi_record_open(RECORD_NOTIFICATION);\n    // Gui\n    bt->gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);\n\n    // Dialogs\n    bt->dialogs = furi_record_open(RECORD_DIALOGS);\n\n    // Power\n    bt->power = furi_record_open(RECORD_POWER);\n    FuriPubSub* power_pubsub = power_get_pubsub(bt->power);\n    furi_pubsub_subscribe(power_pubsub, bt_battery_level_changed_callback, bt);\n\n    // RPC\n    bt->rpc = furi_record_open(RECORD_RPC);\n    bt->rpc_event = furi_event_flag_alloc();\n\n    // API evnent\n    bt->api_event = furi_event_flag_alloc();\n\n    return bt;\n}\n\n// Called from GAP thread from Serial service\nstatic uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    uint16_t ret = 0;\n\n    if(event.event == SerialServiceEventTypeDataReceived) {\n        size_t bytes_processed =\n            rpc_session_feed(bt->rpc_session, event.data.buffer, event.data.size, 1000);\n        if(bytes_processed != event.data.size) {\n            FURI_LOG_E(\n                TAG, \"Only %zu of %u bytes processed by RPC\", bytes_processed, event.data.size);\n        }\n        ret = rpc_session_get_available_size(bt->rpc_session);\n    } else if(event.event == SerialServiceEventTypeDataSent) {\n        furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT);\n    } else if(event.event == SerialServiceEventTypesBleResetRequest) {\n        FURI_LOG_I(TAG, \"BLE restart request received\");\n        BtMessage message = {\n            .type = BtMessageTypeSetProfile,\n            .data.profile.params = NULL,\n            .data.profile.template = ble_profile_serial,\n        };\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    }\n    return ret;\n}\n\n// Called from RPC thread\nstatic void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) {\n    furi_assert(context);\n    Bt* bt = context;\n\n    if(furi_event_flag_get(bt->rpc_event) & BT_RPC_EVENT_DISCONNECTED) {\n        // Early stop from sending if we're already disconnected\n        return;\n    }\n    furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_ALL & (~BT_RPC_EVENT_DISCONNECTED));\n    size_t bytes_sent = 0;\n    while(bytes_sent < bytes_len) {\n        size_t bytes_remain = bytes_len - bytes_sent;\n        if(bytes_remain > bt->max_packet_size) {\n            ble_profile_serial_tx(bt->current_profile, &bytes[bytes_sent], bt->max_packet_size);\n            bytes_sent += bt->max_packet_size;\n        } else {\n            ble_profile_serial_tx(bt->current_profile, &bytes[bytes_sent], bytes_remain);\n            bytes_sent += bytes_remain;\n        }\n        // We want BT_RPC_EVENT_DISCONNECTED to stick, so don't clear\n        uint32_t event_flag = furi_event_flag_wait(\n            bt->rpc_event, BT_RPC_EVENT_ALL, FuriFlagWaitAny | FuriFlagNoClear, FuriWaitForever);\n        if(event_flag & BT_RPC_EVENT_DISCONNECTED) {\n            break;\n        } else {\n            // If we didn't get BT_RPC_EVENT_DISCONNECTED, then clear everything else\n            furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_ALL & (~BT_RPC_EVENT_DISCONNECTED));\n        }\n    }\n}\n\nstatic void bt_serial_buffer_is_empty_callback(void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    furi_check(furi_hal_bt_check_profile_type(bt->current_profile, ble_profile_serial));\n    ble_profile_serial_notify_buffer_is_empty(bt->current_profile);\n}\n\n// Called from GAP thread\nstatic bool bt_on_gap_event_callback(GapEvent event, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    bool ret = false;\n    bool do_update_status = false;\n    bool current_profile_is_serial =\n        furi_hal_bt_check_profile_type(bt->current_profile, ble_profile_serial);\n\n    if(event.type == GapEventTypeConnected) {\n        // Update status bar\n        bt->status = BtStatusConnected;\n        do_update_status = true;\n        // Clear BT_RPC_EVENT_DISCONNECTED because it might be set from previous session\n        furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED);\n\n        if(current_profile_is_serial) {\n            // Open RPC session\n            bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle);\n            if(bt->rpc_session) {\n                FURI_LOG_I(TAG, \"Open RPC connection\");\n                rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback);\n                rpc_session_set_buffer_is_empty_callback(\n                    bt->rpc_session, bt_serial_buffer_is_empty_callback);\n                rpc_session_set_context(bt->rpc_session, bt);\n                ble_profile_serial_set_event_callback(\n                    bt->current_profile, RPC_BUFFER_SIZE, bt_serial_event_callback, bt);\n                ble_profile_serial_set_rpc_active(\n                    bt->current_profile, FuriHalBtSerialRpcStatusActive);\n            } else {\n                FURI_LOG_W(TAG, \"RPC is busy, failed to open new session\");\n            }\n        }\n        // Update battery level\n        PowerInfo info;\n        power_get_info(bt->power, &info);\n        BtMessage message = {.type = BtMessageTypeUpdateStatus};\n        message.type = BtMessageTypeUpdateBatteryLevel;\n        message.data.battery_level = info.charge;\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n        ret = true;\n    } else if(event.type == GapEventTypeDisconnected) {\n        if(current_profile_is_serial && bt->rpc_session) {\n            FURI_LOG_I(TAG, \"Close RPC connection\");\n            ble_profile_serial_set_rpc_active(\n                bt->current_profile, FuriHalBtSerialRpcStatusNotActive);\n            furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED);\n            rpc_session_close(bt->rpc_session);\n            ble_profile_serial_set_event_callback(bt->current_profile, 0, NULL, NULL);\n            bt->rpc_session = NULL;\n        }\n        ret = true;\n    } else if(event.type == GapEventTypeStartAdvertising) {\n        bt->status = BtStatusAdvertising;\n        do_update_status = true;\n        ret = true;\n    } else if(event.type == GapEventTypeStopAdvertising) {\n        bt->status = BtStatusOff;\n        do_update_status = true;\n        ret = true;\n    } else if(event.type == GapEventTypePinCodeShow) {\n        BtMessage message = {\n            .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code};\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n        ret = true;\n    } else if(event.type == GapEventTypePinCodeVerify) {\n        ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code);\n    } else if(event.type == GapEventTypeUpdateMTU) {\n        bt->max_packet_size = event.data.max_packet_size;\n        ret = true;\n    } else if(event.type == GapEventTypeBeaconStart) {\n        bt->beacon_active = true;\n        do_update_status = true;\n        ret = true;\n    } else if(event.type == GapEventTypeBeaconStop) {\n        bt->beacon_active = false;\n        do_update_status = true;\n        ret = true;\n    }\n\n    if(do_update_status) {\n        BtMessage message = {.type = BtMessageTypeUpdateStatus};\n        furi_check(\n            furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    }\n    return ret;\n}\n\nstatic void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) {\n    furi_assert(context);\n    Bt* bt = context;\n    BtMessage message = {\n        .type = BtMessageTypeKeysStorageUpdated,\n        .data.key_storage_data.start_address = addr,\n        .data.key_storage_data.size = size};\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nstatic void bt_statusbar_update(Bt* bt) {\n    uint8_t active_icon_width = 0;\n    if(bt->beacon_active) {\n        active_icon_width = icon_get_width(&I_BLE_beacon_7x8) + ICON_SPACER;\n    }\n    if(bt->status == BtStatusAdvertising) {\n        active_icon_width += icon_get_width(&I_Bluetooth_Idle_5x8);\n    } else if(bt->status == BtStatusConnected) {\n        active_icon_width += icon_get_width(&I_Bluetooth_Connected_16x8);\n    }\n\n    if(active_icon_width > 0) {\n        view_port_set_width(bt->statusbar_view_port, active_icon_width);\n        view_port_enabled_set(bt->statusbar_view_port, true);\n    } else {\n        view_port_enabled_set(bt->statusbar_view_port, false);\n    }\n}\n\nstatic void bt_show_warning(Bt* bt, const char* text) {\n    if(!bt->dialog_message) {\n        bt->dialog_message = dialog_message_alloc();\n    }\n    dialog_message_set_text(bt->dialog_message, text, 64, 28, AlignCenter, AlignCenter);\n    dialog_message_set_buttons(bt->dialog_message, \"Quit\", NULL, NULL);\n    dialog_message_show(bt->dialogs, bt->dialog_message);\n}\n\nstatic void bt_close_rpc_connection(Bt* bt) {\n    if(furi_hal_bt_check_profile_type(bt->current_profile, ble_profile_serial) &&\n       bt->rpc_session) {\n        FURI_LOG_I(TAG, \"Close RPC connection\");\n        furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED);\n        rpc_session_close(bt->rpc_session);\n        ble_profile_serial_set_event_callback(bt->current_profile, 0, NULL, NULL);\n        bt->rpc_session = NULL;\n    }\n}\n\nstatic void bt_change_profile(Bt* bt, BtMessage* message) {\n    if(furi_hal_bt_is_gatt_gap_supported()) {\n        bt_settings_load(&bt->bt_settings);\n\n        bt_close_rpc_connection(bt);\n\n        bt_keys_storage_load(bt->keys_storage);\n\n        bt->current_profile = furi_hal_bt_change_app(\n            message->data.profile.template,\n            message->data.profile.params,\n            bt_keys_storage_get_root_keys(bt->keys_storage),\n            bt_on_gap_event_callback,\n            bt);\n        if(bt->current_profile) {\n            FURI_LOG_I(TAG, \"Bt App started\");\n            if(bt->bt_settings.enabled) {\n                furi_hal_bt_start_advertising();\n            }\n            furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);\n        } else {\n            FURI_LOG_E(TAG, \"Failed to start Bt App\");\n        }\n        if(message->profile_instance) {\n            *message->profile_instance = bt->current_profile;\n        }\n        if(message->result) {\n            *message->result = bt->current_profile != NULL;\n        }\n\n    } else {\n        bt_show_warning(bt, \"Radio stack doesn't support this app\");\n        if(message->result) {\n            *message->result = false;\n        }\n        if(message->profile_instance) {\n            *message->profile_instance = NULL;\n        }\n    }\n}\n\nstatic void bt_close_connection(Bt* bt) {\n    bt_close_rpc_connection(bt);\n    furi_hal_bt_stop_advertising();\n}\n\nstatic void bt_apply_settings(Bt* bt) {\n    if(bt->bt_settings.enabled) {\n        furi_hal_bt_start_advertising();\n    } else {\n        furi_hal_bt_stop_advertising();\n    }\n}\n\nstatic void bt_load_keys(Bt* bt) {\n    if(!furi_hal_bt_is_gatt_gap_supported()) {\n        bt_show_warning(bt, \"Unsupported radio stack\");\n        bt->status = BtStatusUnavailable;\n        return;\n\n    } else if(bt_keys_storage_is_changed(bt->keys_storage)) {\n        FURI_LOG_I(TAG, \"Loading new keys\");\n\n        bt_close_rpc_connection(bt);\n        bt_keys_storage_load(bt->keys_storage);\n\n        bt->current_profile = NULL;\n    } else {\n        FURI_LOG_I(TAG, \"Keys unchanged\");\n    }\n}\n\nstatic void bt_start_application(Bt* bt) {\n    if(!bt->current_profile) {\n        bt->current_profile = furi_hal_bt_change_app(\n            ble_profile_serial,\n            NULL,\n            bt_keys_storage_get_root_keys(bt->keys_storage),\n            bt_on_gap_event_callback,\n            bt);\n\n        if(!bt->current_profile) {\n            FURI_LOG_E(TAG, \"BLE App start failed\");\n            bt->status = BtStatusUnavailable;\n        }\n    }\n}\n\nstatic void bt_load_settings(Bt* bt) {\n    bt_settings_load(&bt->bt_settings);\n    bt_apply_settings(bt);\n}\n\nstatic void bt_handle_get_settings(Bt* bt, BtMessage* message) {\n    *message->data.settings = bt->bt_settings;\n}\n\nstatic void bt_handle_set_settings(Bt* bt, BtMessage* message) {\n    bt->bt_settings = *message->data.csettings;\n    bt_apply_settings(bt);\n    bt_settings_save(&bt->bt_settings);\n}\n\nstatic void bt_handle_reload_keys_settings(Bt* bt) {\n    bt_load_keys(bt);\n    bt_start_application(bt);\n    bt_load_settings(bt);\n}\n\nstatic void bt_init_keys_settings(Bt* bt) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_subscribe(storage_get_pubsub(storage), bt_storage_callback, bt);\n\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping settings\");\n\n        // Just start the BLE serial application without loading the keys or settings\n        bt_start_application(bt);\n        return;\n    }\n\n    bt_handle_reload_keys_settings(bt);\n}\n\nint32_t bt_srv(void* p) {\n    UNUSED(p);\n    Bt* bt = bt_alloc();\n\n    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {\n        FURI_LOG_W(TAG, \"Skipping start in special boot mode\");\n        ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT);\n        furi_record_create(RECORD_BT, bt);\n\n        furi_thread_suspend(furi_thread_get_current_id());\n        return 0;\n    }\n\n    if(furi_hal_bt_start_radio_stack()) {\n        bt_init_keys_settings(bt);\n        furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);\n\n    } else {\n        FURI_LOG_E(TAG, \"Radio stack start failed\");\n    }\n\n    furi_record_create(RECORD_BT, bt);\n\n    BtMessage message;\n\n    while(1) {\n        furi_check(\n            furi_message_queue_get(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n        FURI_LOG_D(\n            TAG,\n            \"call %d, lock 0x%p, result 0x%p\",\n            message.type,\n            (void*)message.lock,\n            (void*)message.result);\n        if(message.type == BtMessageTypeUpdateStatus) {\n            // Update view ports\n            bt_statusbar_update(bt);\n            bt_pin_code_hide(bt);\n            if(bt->status_changed_cb) {\n                bt->status_changed_cb(bt->status, bt->status_changed_ctx);\n            }\n        } else if(message.type == BtMessageTypeUpdateBatteryLevel) {\n            // Update battery level\n            furi_hal_bt_update_battery_level(message.data.battery_level);\n        } else if(message.type == BtMessageTypeUpdatePowerState) {\n            furi_hal_bt_update_power_state(message.data.power_state_charging);\n        } else if(message.type == BtMessageTypePinCodeShow) {\n            // Display PIN code\n            bt_pin_code_show(bt, message.data.pin_code);\n        } else if(message.type == BtMessageTypeKeysStorageUpdated) {\n            bt_keys_storage_update(\n                bt->keys_storage,\n                message.data.key_storage_data.start_address,\n                message.data.key_storage_data.size);\n        } else if(message.type == BtMessageTypeSetProfile) {\n            bt_change_profile(bt, &message);\n        } else if(message.type == BtMessageTypeDisconnect) {\n            bt_close_connection(bt);\n        } else if(message.type == BtMessageTypeForgetBondedDevices) {\n            bt_keys_storage_delete(bt->keys_storage);\n        } else if(message.type == BtMessageTypeGetSettings) {\n            bt_handle_get_settings(bt, &message);\n        } else if(message.type == BtMessageTypeSetSettings) {\n            bt_handle_set_settings(bt, &message);\n        } else if(message.type == BtMessageTypeReloadKeysSettings) {\n            bt_handle_reload_keys_settings(bt);\n        }\n\n        if(message.lock) api_lock_unlock(message.lock);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi_ble/profile_interface.h>\n#include <core/common_defines.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_BT \"bt\"\n\ntypedef struct Bt Bt;\n\ntypedef enum {\n    BtStatusUnavailable,\n    BtStatusOff,\n    BtStatusAdvertising,\n    BtStatusConnected,\n} BtStatus;\n\ntypedef void (*BtStatusChangedCallback)(BtStatus status, void* context);\n\n/** Change BLE Profile\n * @note Call of this function leads to 2nd core restart\n *\n * @param bt                 Bt instance\n * @param profile_template   Profile template to change to\n * @param params             Profile parameters. Can be NULL\n *\n * @return          true on success\n */\nFURI_WARN_UNUSED FuriHalBleProfileBase* bt_profile_start(\n    Bt* bt,\n    const FuriHalBleProfileTemplate* profile_template,\n    FuriHalBleProfileParams params);\n\n/** Stop current BLE Profile and restore default profile\n * @note Call of this function leads to 2nd core restart\n *\n * @param bt        Bt instance\n *\n * @return          true on success\n */\nbool bt_profile_restore_default(Bt* bt);\n\n/** Disconnect from Central\n *\n * @param bt        Bt instance\n */\nvoid bt_disconnect(Bt* bt);\n\n/** Set callback for Bluetooth status change notification\n *\n * @param bt        Bt instance\n * @param callback  BtStatusChangedCallback instance\n * @param context   pointer to context\n */\nvoid bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context);\n\n/** Forget bonded devices\n * @note Leads to wipe ble key storage and deleting bt.keys\n *\n * @param bt        Bt instance\n */\nvoid bt_forget_bonded_devices(Bt* bt);\n\n/** Set keys storage file path\n *\n * @param bt                    Bt instance\n * @param keys_storage_path     Path to file with saved keys\n */\nvoid bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path);\n\n/** Set default keys storage file path\n *\n * @param bt                    Bt instance\n */\nvoid bt_keys_storage_set_default_path(Bt* bt);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_api.c",
    "content": "#include \"bt_i.h\"\n#include <profiles/serial_profile.h>\n\nFuriHalBleProfileBase* bt_profile_start(\n    Bt* bt,\n    const FuriHalBleProfileTemplate* profile_template,\n    FuriHalBleProfileParams params) {\n    furi_check(bt);\n\n    // Send message\n    FuriHalBleProfileBase* profile_instance = NULL;\n\n    BtMessage message = {\n        .lock = api_lock_alloc_locked(),\n        .type = BtMessageTypeSetProfile,\n        .profile_instance = &profile_instance,\n        .data.profile.params = params,\n        .data.profile.template = profile_template,\n    };\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    // Wait for unlock\n    api_lock_wait_unlock_and_free(message.lock);\n\n    bt->current_profile = profile_instance;\n    return profile_instance;\n}\n\nbool bt_profile_restore_default(Bt* bt) {\n    bt->current_profile = bt_profile_start(bt, ble_profile_serial, NULL);\n    return bt->current_profile != NULL;\n}\n\nvoid bt_disconnect(Bt* bt) {\n    furi_check(bt);\n\n    // Send message\n    BtMessage message = {.lock = api_lock_alloc_locked(), .type = BtMessageTypeDisconnect};\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    // Wait for unlock\n    api_lock_wait_unlock_and_free(message.lock);\n}\n\nvoid bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) {\n    furi_check(bt);\n\n    bt->status_changed_cb = callback;\n    bt->status_changed_ctx = context;\n}\n\nvoid bt_forget_bonded_devices(Bt* bt) {\n    furi_check(bt);\n    BtMessage message = {.type = BtMessageTypeForgetBondedDevices};\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path) {\n    furi_check(bt);\n    furi_check(bt->keys_storage);\n    furi_check(keys_storage_path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriString* path = furi_string_alloc_set(keys_storage_path);\n    storage_common_resolve_path_and_ensure_app_directory(storage, path);\n\n    bt_keys_storage_set_file_path(bt->keys_storage, furi_string_get_cstr(path));\n\n    furi_string_free(path);\n    furi_record_close(RECORD_STORAGE);\n}\n\nvoid bt_keys_storage_set_default_path(Bt* bt) {\n    furi_check(bt);\n    furi_check(bt->keys_storage);\n\n    bt_keys_storage_set_file_path(bt->keys_storage, BT_KEYS_STORAGE_PATH);\n}\n\n/*\n * Private API for the Settings app\n */\n\nvoid bt_get_settings(Bt* bt, BtSettings* settings) {\n    furi_assert(bt);\n    furi_assert(settings);\n\n    BtMessage message = {\n        .lock = api_lock_alloc_locked(),\n        .type = BtMessageTypeGetSettings,\n        .data.settings = settings,\n    };\n\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n\n    api_lock_wait_unlock_and_free(message.lock);\n}\n\nvoid bt_set_settings(Bt* bt, const BtSettings* settings) {\n    furi_assert(bt);\n    furi_assert(settings);\n\n    BtMessage message = {\n        .lock = api_lock_alloc_locked(),\n        .type = BtMessageTypeSetSettings,\n        .data.csettings = settings,\n    };\n\n    furi_check(\n        furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n\n    api_lock_wait_unlock_and_free(message.lock);\n}\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_i.h",
    "content": "#pragma once\n\n#include \"bt.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <api_lock.h>\n\n#include <gui/gui.h>\n#include <gui/view_port.h>\n#include <gui/view.h>\n\n#include <dialogs/dialogs.h>\n#include <power/power_service/power.h>\n#include <rpc/rpc.h>\n#include <notification/notification.h>\n#include <storage/storage.h>\n\n#include <bt/bt_settings.h>\n#include <bt/bt_service/bt_keys_storage.h>\n\n#include \"bt_keys_filename.h\"\n\n#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME)\n\ntypedef enum {\n    BtMessageTypeUpdateStatus,\n    BtMessageTypeUpdateBatteryLevel,\n    BtMessageTypeUpdatePowerState,\n    BtMessageTypePinCodeShow,\n    BtMessageTypeKeysStorageUpdated,\n    BtMessageTypeSetProfile,\n    BtMessageTypeDisconnect,\n    BtMessageTypeForgetBondedDevices,\n    BtMessageTypeGetSettings,\n    BtMessageTypeSetSettings,\n    BtMessageTypeReloadKeysSettings,\n} BtMessageType;\n\ntypedef struct {\n    uint8_t* start_address;\n    uint16_t size;\n} BtKeyStorageUpdateData;\n\ntypedef union {\n    uint32_t pin_code;\n    uint8_t battery_level;\n    bool power_state_charging;\n    struct {\n        const FuriHalBleProfileTemplate* template;\n        FuriHalBleProfileParams params;\n    } profile;\n    FuriHalBleProfileParams profile_params;\n    BtKeyStorageUpdateData key_storage_data;\n    BtSettings* settings;\n    const BtSettings* csettings;\n} BtMessageData;\n\ntypedef struct {\n    FuriApiLock lock;\n    BtMessageType type;\n    BtMessageData data;\n    bool* result;\n    FuriHalBleProfileBase** profile_instance;\n} BtMessage;\n\nstruct Bt {\n    uint8_t* bt_keys_addr_start;\n    uint16_t bt_keys_size;\n    uint16_t max_packet_size;\n    BtSettings bt_settings;\n    BtKeysStorage* keys_storage;\n    BtStatus status;\n    bool beacon_active;\n    FuriHalBleProfileBase* current_profile;\n    FuriMessageQueue* message_queue;\n    NotificationApp* notification;\n    Gui* gui;\n    ViewPort* statusbar_view_port;\n    ViewPort* pin_code_view_port;\n    uint32_t pin_code;\n    DialogsApp* dialogs;\n    DialogMessage* dialog_message;\n    Power* power;\n    Rpc* rpc;\n    RpcSession* rpc_session;\n    FuriEventFlag* rpc_event;\n    FuriEventFlag* api_event;\n    BtStatusChangedCallback status_changed_cb;\n    void* status_changed_ctx;\n};\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_keys_filename.h",
    "content": "#pragma once\n\n#define BT_KEYS_STORAGE_FILE_NAME \".bt.keys\"\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_keys_storage.c",
    "content": "#include \"bt_keys_storage.h\"\n\n#include <furi.h>\n#include <furi_hal_bt.h>\n#include <furi_hal_random.h>\n\n#include <gap.h>\n\n#include <storage/storage.h>\n#include <toolbox/saved_struct.h>\n\n#define BT_KEYS_STORAGE_MAGIC          (0x18)\n#define BT_KEYS_STORAGE_VERSION        (1)\n#define BT_KEYS_STORAGE_LEGACY_VERSION (0) // Legacy version with no root keys\n\n#define TAG \"BtKeyStorage\"\n\n// Identity root key\nstatic const uint8_t gap_legacy_irk[16] =\n    {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};\n// Encryption root key\nstatic const uint8_t gap_legacy_erk[16] =\n    {0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21};\n\nstruct BtKeysStorage {\n    uint8_t* nvm_sram_buff;\n    uint16_t nvm_sram_buff_size;\n    uint16_t current_size;\n    FuriString* file_path;\n    GapRootSecurityKeys root_keys;\n};\n\ntypedef struct {\n    GapRootSecurityKeys root_keys;\n    uint8_t pairing_data[];\n} BtKeysStorageFile;\n\nstatic bool bt_keys_storage_save(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    size_t total_size = sizeof(BtKeysStorageFile) + instance->current_size;\n    BtKeysStorageFile* save_data = malloc(total_size);\n\n    memcpy(&save_data->root_keys, &instance->root_keys, sizeof(GapRootSecurityKeys));\n\n    furi_hal_bt_nvm_sram_sem_acquire();\n    memcpy(save_data->pairing_data, instance->nvm_sram_buff, instance->current_size);\n    furi_hal_bt_nvm_sram_sem_release();\n\n    bool saved = saved_struct_save(\n        furi_string_get_cstr(instance->file_path),\n        save_data,\n        sizeof(GapRootSecurityKeys) + instance->current_size,\n        BT_KEYS_STORAGE_MAGIC,\n        BT_KEYS_STORAGE_VERSION);\n\n    free(save_data);\n    return saved;\n}\n\nstatic bool bt_keys_storage_load_keys_and_pairings(\n    BtKeysStorage* instance,\n    const uint8_t* file_data,\n    size_t data_size) {\n    if(data_size < sizeof(GapRootSecurityKeys)) {\n        return false;\n    }\n\n    const BtKeysStorageFile* loaded = (const BtKeysStorageFile*)file_data;\n    memcpy(&instance->root_keys, &loaded->root_keys, sizeof(GapRootSecurityKeys));\n\n    size_t ble_data_size = data_size - sizeof(GapRootSecurityKeys);\n    if(ble_data_size > instance->nvm_sram_buff_size) {\n        FURI_LOG_E(TAG, \"BLE data too large for SRAM buffer\");\n        return false;\n    }\n\n    furi_hal_bt_nvm_sram_sem_acquire();\n    memcpy(instance->nvm_sram_buff, loaded->pairing_data, ble_data_size);\n    instance->current_size = ble_data_size;\n    furi_hal_bt_nvm_sram_sem_release();\n\n    return true;\n}\n\nstatic void bt_keys_storage_regenerate_root_keys(BtKeysStorage* instance) {\n    furi_hal_random_fill_buf(instance->root_keys.erk, sizeof(instance->root_keys.erk));\n    furi_hal_random_fill_buf(instance->root_keys.irk, sizeof(instance->root_keys.irk));\n}\n\nbool bt_keys_storage_delete(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    bool delete_succeed = false;\n    bool bt_is_active = furi_hal_bt_is_active();\n\n    furi_hal_bt_stop_advertising();\n    delete_succeed = furi_hal_bt_clear_white_list();\n\n    FURI_LOG_I(TAG, \"Root keys regen\");\n    bt_keys_storage_regenerate_root_keys(instance);\n\n    instance->current_size = 0;\n    if(!bt_keys_storage_save(instance)) {\n        FURI_LOG_E(TAG, \"Save after delete failed\");\n    }\n\n    if(bt_is_active) {\n        furi_hal_bt_start_advertising();\n    }\n\n    return delete_succeed;\n}\n\nBtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path) {\n    furi_assert(keys_storage_path);\n\n    BtKeysStorage* instance = malloc(sizeof(BtKeysStorage));\n    // Set default nvm ram parameters\n    furi_hal_bt_get_key_storage_buff(&instance->nvm_sram_buff, &instance->nvm_sram_buff_size);\n    // Set key storage file\n    instance->file_path = furi_string_alloc();\n    furi_string_set_str(instance->file_path, keys_storage_path);\n\n    bt_keys_storage_regenerate_root_keys(instance);\n    return instance;\n}\n\nvoid bt_keys_storage_free(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    furi_string_free(instance->file_path);\n    free(instance);\n}\n\nvoid bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path) {\n    furi_assert(instance);\n    furi_assert(path);\n\n    furi_string_set_str(instance->file_path, path);\n}\n\nvoid bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size) {\n    furi_assert(instance);\n    furi_assert(buff);\n\n    instance->nvm_sram_buff = buff;\n    instance->nvm_sram_buff_size = size;\n}\n\nstatic bool bt_keys_storage_file_exists(const char* file_path) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FileInfo file_info;\n    const bool ret = storage_common_stat(storage, file_path, &file_info) == FSE_OK &&\n                     file_info.size != 0;\n    furi_record_close(RECORD_STORAGE);\n    return ret;\n}\n\nstatic bool bt_keys_storage_validate_file(\n    const char* file_path,\n    size_t* payload_size,\n    uint8_t* file_version) {\n    uint8_t magic, version;\n    size_t size;\n\n    if(!saved_struct_get_metadata(file_path, &magic, &version, &size)) {\n        FURI_LOG_W(TAG, \"Failed to get metadata\");\n        return false;\n\n    } else if(magic != BT_KEYS_STORAGE_MAGIC) {\n        FURI_LOG_W(TAG, \"File magic mismatch\");\n        return false;\n    } else if(version > BT_KEYS_STORAGE_VERSION) {\n        FURI_LOG_E(\n            TAG,\n            \"File version %d is newer than supported version %d\",\n            version,\n            BT_KEYS_STORAGE_VERSION);\n        return false;\n    }\n\n    *payload_size = size;\n    *file_version = version;\n    return true;\n}\n\nbool bt_keys_storage_is_changed(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    bool is_changed = false;\n    uint8_t* data_buffer = NULL;\n\n    do {\n        const char* file_path = furi_string_get_cstr(instance->file_path);\n        size_t payload_size;\n        uint8_t file_version;\n\n        if(!bt_keys_storage_file_exists(file_path)) {\n            FURI_LOG_W(TAG, \"Missing or empty file\");\n            is_changed = true;\n            break;\n        }\n\n        if(!bt_keys_storage_validate_file(file_path, &payload_size, &file_version)) {\n            FURI_LOG_W(TAG, \"Invalid or corrupted file\");\n            is_changed = true;\n            break;\n        }\n\n        // Early check for legacy version: always considered changed, no need to load\n        if(file_version == BT_KEYS_STORAGE_LEGACY_VERSION) {\n            is_changed = true;\n            break;\n        }\n\n        data_buffer = malloc(payload_size);\n        const bool data_loaded = saved_struct_load(\n            file_path, data_buffer, payload_size, BT_KEYS_STORAGE_MAGIC, file_version);\n\n        if(!data_loaded) {\n            FURI_LOG_E(TAG, \"Failed to load file\");\n            break;\n        }\n\n        // At this point, it's version 1 file we have\n        const BtKeysStorageFile* loaded = (const BtKeysStorageFile*)data_buffer;\n        size_t expected_file_size = sizeof(GapRootSecurityKeys) + instance->current_size;\n        if(payload_size == expected_file_size) {\n            furi_hal_bt_nvm_sram_sem_acquire();\n            is_changed =\n                memcmp(loaded->pairing_data, instance->nvm_sram_buff, instance->current_size);\n            furi_hal_bt_nvm_sram_sem_release();\n        } else {\n            FURI_LOG_D(TAG, \"NVRAM sz mismatch (v1)\");\n            is_changed = true;\n        }\n    } while(false);\n\n    if(data_buffer) {\n        free(data_buffer);\n    }\n\n    return is_changed;\n}\n\nstatic bool bt_keys_storage_load_legacy_pairings(\n    BtKeysStorage* instance,\n    const uint8_t* file_data,\n    size_t payload_size) {\n    FURI_LOG_I(TAG, \"Loaded v0, upgrading to v1\");\n    memcpy(instance->root_keys.irk, gap_legacy_irk, sizeof(instance->root_keys.irk));\n    memcpy(instance->root_keys.erk, gap_legacy_erk, sizeof(instance->root_keys.erk));\n    if(payload_size > instance->nvm_sram_buff_size) {\n        FURI_LOG_E(TAG, \"Pairing too large\");\n        return false;\n    }\n    furi_hal_bt_nvm_sram_sem_acquire();\n    memcpy(instance->nvm_sram_buff, file_data, payload_size);\n    instance->current_size = payload_size;\n    furi_hal_bt_nvm_sram_sem_release();\n    if(!bt_keys_storage_save(instance)) {\n        FURI_LOG_W(TAG, \"Upgrade to v1 failed\");\n    } else {\n        FURI_LOG_I(TAG, \"Upgraded to v1\");\n    }\n    return true;\n}\n\nbool bt_keys_storage_load(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    const char* file_path = furi_string_get_cstr(instance->file_path);\n    size_t payload_size;\n    uint8_t file_version;\n    if(!bt_keys_storage_validate_file(file_path, &payload_size, &file_version)) {\n        FURI_LOG_E(TAG, \"Invalid or corrupted file\");\n        return false;\n    }\n\n    bool loaded = false;\n    uint8_t* file_data = malloc(payload_size);\n\n    do {\n        if(!saved_struct_load(\n               file_path, file_data, payload_size, BT_KEYS_STORAGE_MAGIC, file_version)) {\n            FURI_LOG_E(TAG, \"Failed to load\");\n            break;\n        }\n\n        if(file_version == BT_KEYS_STORAGE_LEGACY_VERSION) {\n            loaded = bt_keys_storage_load_legacy_pairings(instance, file_data, payload_size);\n            break;\n        }\n        // Only v1 left\n        loaded = bt_keys_storage_load_keys_and_pairings(instance, file_data, payload_size);\n    } while(false);\n\n    free(file_data);\n    return loaded;\n}\n\nbool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size) {\n    furi_assert(instance);\n    furi_assert(start_addr);\n\n    bool updated = false;\n\n    FURI_LOG_I(\n        TAG,\n        \"Base address: %p. Start update address: %p. Size changed: %lu\",\n        (void*)instance->nvm_sram_buff,\n        start_addr,\n        size);\n\n    do {\n        size_t new_size = start_addr - instance->nvm_sram_buff + size;\n        if(new_size > instance->nvm_sram_buff_size) {\n            FURI_LOG_E(TAG, \"NVM RAM buffer overflow\");\n            break;\n        }\n\n        instance->current_size = new_size;\n\n        // Save using version 1 format with embedded root keys\n        bool data_updated = bt_keys_storage_save(instance);\n\n        if(!data_updated) {\n            FURI_LOG_E(TAG, \"Failed to update key storage\");\n            break;\n        }\n\n        updated = true;\n    } while(false);\n\n    return updated;\n}\n\nconst GapRootSecurityKeys* bt_keys_storage_get_root_keys(BtKeysStorage* instance) {\n    furi_assert(instance);\n\n    return &instance->root_keys;\n}\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_keys_storage.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include <gap.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct BtKeysStorage BtKeysStorage;\n\nBtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path);\n\nvoid bt_keys_storage_free(BtKeysStorage* instance);\n\nvoid bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path);\n\nvoid bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size);\n\nbool bt_keys_storage_is_changed(BtKeysStorage* instance);\n\nconst GapRootSecurityKeys* bt_keys_storage_get_root_keys(BtKeysStorage* instance);\n\nbool bt_keys_storage_load(BtKeysStorage* instance);\n\nbool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size);\n\nbool bt_keys_storage_delete(BtKeysStorage* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/bt/bt_service/bt_settings_api_i.h",
    "content": "#pragma once\n\n#include \"bt.h\"\n#include \"../bt_settings.h\"\n\nvoid bt_get_settings(Bt* bt, BtSettings* settings);\n\nvoid bt_set_settings(Bt* bt, const BtSettings* settings);\n"
  },
  {
    "path": "applications/services/bt/bt_settings.c",
    "content": "#include \"bt_settings.h\"\n#include \"bt_settings_filename.h\"\n\n#include <furi.h>\n\n#include <storage/storage.h>\n#include <toolbox/saved_struct.h>\n\n#define TAG \"BtSettings\"\n\n#define BT_SETTINGS_PATH    INT_PATH(BT_SETTINGS_FILE_NAME)\n#define BT_SETTINGS_VERSION (0)\n#define BT_SETTINGS_MAGIC   (0x19)\n\nvoid bt_settings_load(BtSettings* bt_settings) {\n    furi_assert(bt_settings);\n\n    const bool load_success = saved_struct_load(\n        BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION);\n\n    if(!load_success) {\n        FURI_LOG_W(TAG, \"Failed to load settings, using defaults\");\n\n        bt_settings->enabled = false;\n        bt_settings_save(bt_settings);\n    }\n}\n\nvoid bt_settings_save(const BtSettings* bt_settings) {\n    furi_assert(bt_settings);\n\n    const bool success = saved_struct_save(\n        BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION);\n\n    if(!success) {\n        FURI_LOG_E(TAG, \"Failed to save settings\");\n    }\n}\n"
  },
  {
    "path": "applications/services/bt/bt_settings.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    bool enabled;\n} BtSettings;\n\nvoid bt_settings_load(BtSettings* bt_settings);\n\nvoid bt_settings_save(const BtSettings* bt_settings);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/bt/bt_settings_filename.h",
    "content": "#pragma once\n\n#define BT_SETTINGS_FILE_NAME \".bt.settings\"\n"
  },
  {
    "path": "applications/services/cli/application.fam",
    "content": "App(\n    appid=\"cli\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"cli_on_system_start\",\n    cdefines=[\"SRV_CLI\"],\n    sources=[\n        \"cli_command_gpio.c\",\n        \"cli_main_commands.c\",\n        \"cli_main_shell.c\",\n    ],\n    # This STARTUP has to be processed before those that depend on the \"cli\" record.\n    # \"cli\" used to be a SERVICE, but it's been converted into a STARTUP in order to\n    # reduce RAM usage. The \"block until record has been created\" mechanism\n    # unfortunately leads to a deadlock if the STARTUPs are processed sequentially.\n    order=0,\n)\n\nApp(\n    appid=\"cli_vcp\",\n    name=\"CliVcpSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"cli_vcp_srv\",\n    stack_size=1024,\n    order=10,\n    sdk_headers=[\"cli_vcp.h\", \"cli.h\"],\n    sources=[\"cli_vcp.c\"],\n)\n\nApp(\n    appid=\"cli_hello_world\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_hello_world_ep\",\n    requires=[\"cli\"],\n    sources=[\"commands/hello_world.c\"],\n)\n\nApp(\n    appid=\"cli_neofetch\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_neofetch_ep\",\n    requires=[\"cli\"],\n    sources=[\"commands/neofetch.c\"],\n)\n\nApp(\n    appid=\"cli_subshell_demo\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_subshell_demo_ep\",\n    requires=[\"cli\"],\n    sources=[\"commands/subshell_demo.c\"],\n)\n\nApp(\n    appid=\"cli_buzzer\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"cli_buzzer_ep\",\n    requires=[\"cli\"],\n    sources=[\"commands/buzzer.c\"],\n)\n"
  },
  {
    "path": "applications/services/cli/cli.h",
    "content": "#pragma once\n\n/* \n * Compatibility header for ease of porting existing apps.\n *  In short: \n *   Cli* is replaced with with CliRegistry*\n *   cli_* functions are replaced with cli_registry_* functions\n *   (i.e., cli_add_command() is now cli_registry_add_command())\n*/\n\n#include <toolbox/cli/cli_registry.h>\n\n#define RECORD_CLI \"cli\"\n"
  },
  {
    "path": "applications/services/cli/cli_command_gpio.c",
    "content": "#include \"cli_command_gpio.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <lib/toolbox/args.h>\n#include <toolbox/pipe.h>\n#include <toolbox/cli/cli_command.h>\n\nvoid cli_command_gpio_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"gpio <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n    printf(\"\\tmode <pin_name> <0|1>\\t - Set gpio mode: 0 - input, 1 - output\\r\\n\");\n    printf(\"\\tset <pin_name> <0|1>\\t - Set gpio value\\r\\n\");\n    printf(\"\\tread <pin_name>\\t - Read gpio value\\r\\n\");\n}\n\nstatic bool pin_name_to_int(FuriString* pin_name, size_t* result) {\n    bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(furi_string_equal(pin_name, gpio_pins[i].name)) {\n            if(!gpio_pins[i].debug || is_debug_mode) {\n                *result = i;\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nstatic void gpio_print_pins(void) {\n    printf(\"Wrong pin name. Available pins: \");\n    bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);\n    for(size_t i = 0; i < gpio_pins_count; i++) {\n        if(!gpio_pins[i].debug || is_debug_mode) {\n            printf(\"%s \", gpio_pins[i].name);\n        }\n    }\n}\n\ntypedef enum {\n    GpioParseReturnOk,\n    GpioParseReturnCmdSyntaxError,\n    GpioParseReturnPinError,\n    GpioParseReturnValueError\n} GpioParseReturn;\n\nstatic GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) {\n    GpioParseReturn ret = GpioParseReturnOk;\n    FuriString* pin_name = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, pin_name)) {\n            ret = GpioParseReturnCmdSyntaxError;\n            break;\n        } else if(!pin_name_to_int(pin_name, pin_num)) {\n            ret = GpioParseReturnPinError;\n            break;\n        }\n\n        int pin_mode; //-V779\n        if(!args_read_int_and_trim(args, &pin_mode) || pin_mode < 0 || pin_mode > 1) {\n            ret = GpioParseReturnValueError;\n            break;\n        }\n\n        *value = pin_mode;\n    } while(false);\n\n    furi_string_free(pin_name);\n    return ret;\n}\n\nvoid cli_command_gpio_mode(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n\n    size_t num = 0;\n    uint8_t value = 255;\n\n    GpioParseReturn err = gpio_command_parse(args, &num, &value);\n\n    if(err == GpioParseReturnCmdSyntaxError) {\n        cli_print_usage(\"gpio mode\", \"<pin_name> <0|1>\", furi_string_get_cstr(args));\n        return;\n    } else if(err == GpioParseReturnPinError) { //-V547\n        gpio_print_pins();\n        return;\n    } else if(err == GpioParseReturnValueError) {\n        printf(\"Value is invalid. Enter 1 for input or 0 for output\");\n        return;\n    }\n\n    if(gpio_pins[num].debug) { //-V779\n        printf(\n            \"Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\\r\\n\");\n        char c = getchar();\n        if(c != 'y' && c != 'Y') {\n            printf(\"Cancelled.\\r\\n\");\n            return;\n        }\n    }\n\n    if(value == 1) { // output\n        furi_hal_gpio_write(gpio_pins[num].pin, false);\n        furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeOutputPushPull);\n        printf(\"Pin %s is now an output (low)\", gpio_pins[num].name);\n    } else { // input\n        furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeInput);\n        printf(\"Pin %s is now an input\", gpio_pins[num].name);\n    }\n}\n\nvoid cli_command_gpio_read(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n\n    size_t num = 0;\n    if(!pin_name_to_int(args, &num)) {\n        gpio_print_pins();\n        return;\n    }\n\n    if(LL_GPIO_MODE_INPUT != //-V779\n       LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) {\n        printf(\"Err: pin %s is not set as an input.\", gpio_pins[num].name);\n        return;\n    }\n\n    uint8_t val = !!furi_hal_gpio_read(gpio_pins[num].pin);\n\n    printf(\"Pin %s <= %u\", gpio_pins[num].name, val);\n}\n\nvoid cli_command_gpio_set(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n\n    size_t num = 0;\n    uint8_t value = 0;\n    GpioParseReturn err = gpio_command_parse(args, &num, &value);\n\n    if(err == GpioParseReturnCmdSyntaxError) {\n        cli_print_usage(\"gpio set\", \"<pin_name> <0|1>\", furi_string_get_cstr(args));\n        return;\n    } else if(err == GpioParseReturnPinError) { //-V547\n        gpio_print_pins();\n        return;\n    } else if(err == GpioParseReturnValueError) {\n        printf(\"Value is invalid. Enter 1 for high or 0 for low\");\n        return;\n    }\n\n    if(LL_GPIO_MODE_OUTPUT != //-V779\n       LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) {\n        printf(\"Err: pin %s is not set as an output.\", gpio_pins[num].name);\n        return;\n    }\n\n    // Extra check if debug pins used\n    if(gpio_pins[num].debug) {\n        printf(\n            \"Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\\r\\n\");\n        char c = getchar();\n        if(c != 'y' && c != 'Y') {\n            printf(\"Cancelled.\\r\\n\");\n            return;\n        }\n    }\n\n    furi_hal_gpio_write(gpio_pins[num].pin, !!value);\n    printf(\"Pin %s => %u\", gpio_pins[num].name, !!value);\n}\n\nvoid cli_command_gpio(PipeSide* pipe, FuriString* args, void* context) {\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            cli_command_gpio_print_usage();\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"mode\") == 0) {\n            cli_command_gpio_mode(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"set\") == 0) {\n            cli_command_gpio_set(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"read\") == 0) {\n            cli_command_gpio_read(pipe, args, context);\n            break;\n        }\n\n        cli_command_gpio_print_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n"
  },
  {
    "path": "applications/services/cli/cli_command_gpio.h",
    "content": "#pragma once\n\n#include <toolbox/pipe.h>\n\nvoid cli_command_gpio(PipeSide* pipe, FuriString* args, void* context);\n"
  },
  {
    "path": "applications/services/cli/cli_main_commands.c",
    "content": "#include \"cli_main_commands.h\"\n#include \"cli_command_gpio.h\"\n#include <toolbox/cli/cli_ansi.h>\n\n#include <core/thread.h>\n#include <furi_hal.h>\n#include <furi_hal_info.h>\n#include <task_control_block.h>\n#include <time.h>\n#include <notification/notification_messages.h>\n#include <notification/notification_app.h>\n#include <loader/loader.h>\n#include <lib/toolbox/args.h>\n#include <lib/toolbox/strint.h>\n#include <toolbox/pipe.h>\n\n// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`\n#define CLI_DATE_FORMAT \"%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d\"\n\nvoid cli_command_info_callback(const char* key, const char* value, bool last, void* context) {\n    UNUSED(last);\n    UNUSED(context);\n    printf(\"%-30s: %s\\r\\n\", key, value);\n}\n\n/** Info Command\n *\n * This command is intended to be used by humans\n *\n * Arguments:\n * - device - print device info\n * - power - print power info\n * - power_debug - print power debug info\n *\n * @param      cli      The cli instance\n * @param      args     The arguments\n * @param      context  The context\n */\nvoid cli_command_info(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n\n    if(context) {\n        furi_hal_info_get(cli_command_info_callback, '_', NULL);\n        return;\n    }\n\n    if(!furi_string_cmp(args, \"device\")) {\n        furi_hal_info_get(cli_command_info_callback, '.', NULL);\n    } else if(!furi_string_cmp(args, \"power\")) {\n        furi_hal_power_info_get(cli_command_info_callback, '.', NULL);\n    } else if(!furi_string_cmp(args, \"power_debug\")) {\n        furi_hal_power_debug_get(cli_command_info_callback, NULL);\n    } else {\n        cli_print_usage(\"info\", \"<device|power|power_debug>\", furi_string_get_cstr(args));\n    }\n}\n\nvoid cli_command_uptime(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n    uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();\n    printf(\"Uptime: %luh%lum%lus\", uptime / 60 / 60, uptime / 60 % 60, uptime % 60);\n}\n\nvoid cli_command_date(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n\n    DateTime datetime = {0};\n\n    if(furi_string_size(args) > 0) {\n        uint16_t hours, minutes, seconds, month, day, year, weekday;\n        int ret = sscanf(\n            furi_string_get_cstr(args),\n            \"%hu-%hu-%hu %hu:%hu:%hu %hu\",\n            &year,\n            &month,\n            &day,\n            &hours,\n            &minutes,\n            &seconds,\n            &weekday);\n\n        // Some variables are going to discard upper byte\n        // There will be some funky behaviour which is not breaking anything\n        datetime.hour = hours;\n        datetime.minute = minutes;\n        datetime.second = seconds;\n        datetime.weekday = weekday;\n        datetime.month = month;\n        datetime.day = day;\n        datetime.year = year;\n\n        if(ret != 7) {\n            printf(\n                \"Invalid datetime format, use `%s`. sscanf %d %s\",\n                \"%Y-%m-%d %H:%M:%S %u\",\n                ret,\n                furi_string_get_cstr(args));\n            return;\n        }\n\n        if(!datetime_validate_datetime(&datetime)) {\n            printf(\"Invalid datetime data\");\n            return;\n        }\n\n        furi_hal_rtc_set_datetime(&datetime);\n        // Verification\n        furi_hal_rtc_get_datetime(&datetime);\n        printf(\n            \"New datetime is: \" CLI_DATE_FORMAT,\n            datetime.year,\n            datetime.month,\n            datetime.day,\n            datetime.hour,\n            datetime.minute,\n            datetime.second,\n            datetime.weekday);\n    } else {\n        furi_hal_rtc_get_datetime(&datetime);\n        printf(\n            CLI_DATE_FORMAT,\n            datetime.year,\n            datetime.month,\n            datetime.day,\n            datetime.hour,\n            datetime.minute,\n            datetime.second,\n            datetime.weekday);\n    }\n}\n\n#define CLI_COMMAND_LOG_RING_SIZE   2048\n#define CLI_COMMAND_LOG_BUFFER_SIZE 64\n\nvoid cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) {\n    PipeSide* pipe = context;\n    pipe_send(pipe, buffer, size);\n}\n\nbool cli_command_log_level_set_from_string(FuriString* level) {\n    FuriLogLevel log_level;\n    if(furi_log_level_from_string(furi_string_get_cstr(level), &log_level)) {\n        furi_log_set_level(log_level);\n        return true;\n    } else {\n        printf(\"<log> — start logging using the current level from the system settings\\r\\n\");\n        printf(\"<log error> — only critical errors and other important messages\\r\\n\");\n        printf(\"<log warn> — non-critical errors and warnings including <log error>\\r\\n\");\n        printf(\"<log info> — non-critical information including <log warn>\\r\\n\");\n        printf(\"<log default> — the default system log level (equivalent to <log info>)\\r\\n\");\n        printf(\n            \"<log debug> — debug information including <log info> (may impact system performance)\\r\\n\");\n        printf(\n            \"<log trace> — system traces including <log debug> (may impact system performance)\\r\\n\");\n    }\n    return false;\n}\n\nvoid cli_command_log(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriLogLevel previous_level = furi_log_get_level();\n    bool restore_log_level = false;\n\n    if(furi_string_size(args) > 0) {\n        if(!cli_command_log_level_set_from_string(args)) {\n            return;\n        }\n        restore_log_level = true;\n    }\n\n    const char* current_level;\n    furi_log_level_to_string(furi_log_get_level(), &current_level);\n    printf(\"Current log level: %s\\r\\n\", current_level);\n\n    FuriLogHandler log_handler = {\n        .callback = cli_command_log_tx_callback,\n        .context = pipe,\n    };\n\n    furi_log_add_handler(log_handler);\n\n    printf(\"Use <log ?> to list available log levels\\r\\n\");\n    printf(\"Press CTRL+C to stop...\\r\\n\");\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        furi_delay_ms(100);\n    }\n\n    furi_log_remove_handler(log_handler);\n\n    if(restore_log_level) {\n        // There will be strange behaviour if log level is set from settings while log command is running\n        furi_log_set_level(previous_level);\n    }\n}\n\nvoid cli_command_sysctl_debug(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n    if(!furi_string_cmp(args, \"0\")) {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);\n        printf(\"Debug disabled.\");\n    } else if(!furi_string_cmp(args, \"1\")) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagDebug);\n        printf(\"Debug enabled.\");\n    } else {\n        cli_print_usage(\"sysctl debug\", \"<1|0>\", furi_string_get_cstr(args));\n    }\n}\n\nvoid cli_command_sysctl_heap_track(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n    if(!furi_string_cmp(args, \"none\")) {\n        furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone);\n        printf(\"Heap tracking disabled\");\n    } else if(!furi_string_cmp(args, \"main\")) {\n        furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeMain);\n        printf(\"Heap tracking enabled for application main thread\");\n#ifdef FURI_DEBUG\n    } else if(!furi_string_cmp(args, \"tree\")) {\n        furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeTree);\n        printf(\"Heap tracking enabled for application main and child threads\");\n    } else if(!furi_string_cmp(args, \"all\")) {\n        furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeAll);\n        printf(\"Heap tracking enabled for all threads\");\n#endif\n    } else {\n        cli_print_usage(\"sysctl heap_track\", \"<none|main|tree|all>\", furi_string_get_cstr(args));\n    }\n}\n\nvoid cli_command_sysctl_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"sysctl <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n\n    printf(\"\\tdebug <0|1>\\t - Enable or disable system debug\\r\\n\");\n#ifdef FURI_DEBUG\n    printf(\"\\theap_track <none|main|tree|all>\\t - Set heap allocation tracking mode\\r\\n\");\n#else\n    printf(\"\\theap_track <none|main>\\t - Set heap allocation tracking mode\\r\\n\");\n#endif\n}\n\nvoid cli_command_sysctl(PipeSide* pipe, FuriString* args, void* context) {\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            cli_command_sysctl_print_usage();\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"debug\") == 0) {\n            cli_command_sysctl_debug(pipe, args, context);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"heap_track\") == 0) {\n            cli_command_sysctl_heap_track(pipe, args, context);\n            break;\n        }\n\n        cli_command_sysctl_print_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n\nvoid cli_command_vibro(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n\n    if(!furi_string_cmp(args, \"0\")) {\n        NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n        notification_message_block(notification, &sequence_reset_vibro);\n        furi_record_close(RECORD_NOTIFICATION);\n    } else if(!furi_string_cmp(args, \"1\")) {\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {\n            printf(\"Flipper is in stealth mode. Unmute the device to control vibration.\");\n            return;\n        }\n\n        NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n        if(notification->settings.vibro_on) {\n            notification_message_block(notification, &sequence_set_vibro_on);\n        } else {\n            printf(\"Vibro is disabled in settings. Enable it to control vibration.\");\n        }\n\n        furi_record_close(RECORD_NOTIFICATION);\n    } else {\n        cli_print_usage(\"vibro\", \"<1|0>\", furi_string_get_cstr(args));\n    }\n}\n\nvoid cli_command_led(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n    // Get first word as light name\n    NotificationMessage notification_led_message;\n    FuriString* light_name;\n    light_name = furi_string_alloc();\n    size_t ws = furi_string_search_char(args, ' ');\n    if(ws == FURI_STRING_FAILURE) {\n        cli_print_usage(\"led\", \"<r|g|b|bl> <0-255>\", furi_string_get_cstr(args));\n        furi_string_free(light_name);\n        return;\n    } else {\n        furi_string_set_n(light_name, args, 0, ws);\n        furi_string_right(args, ws);\n        furi_string_trim(args);\n    }\n    // Check light name\n    if(!furi_string_cmp(light_name, \"r\")) {\n        notification_led_message.type = NotificationMessageTypeLedRed;\n    } else if(!furi_string_cmp(light_name, \"g\")) {\n        notification_led_message.type = NotificationMessageTypeLedGreen;\n    } else if(!furi_string_cmp(light_name, \"b\")) {\n        notification_led_message.type = NotificationMessageTypeLedBlue;\n    } else if(!furi_string_cmp(light_name, \"bl\")) {\n        notification_led_message.type = NotificationMessageTypeLedDisplayBacklight;\n    } else {\n        cli_print_usage(\"led\", \"<r|g|b|bl> <0-255>\", furi_string_get_cstr(args));\n        furi_string_free(light_name);\n        return;\n    }\n    furi_string_free(light_name);\n    // Read light value from the rest of the string\n    uint32_t value;\n    if(strint_to_uint32(furi_string_get_cstr(args), NULL, &value, 0) != StrintParseNoError ||\n       value >= 256) {\n        cli_print_usage(\"led\", \"<r|g|b|bl> <0-255>\", furi_string_get_cstr(args));\n        return;\n    }\n\n    // Set led value\n    notification_led_message.data.led.value = value;\n\n    // Form notification sequence\n    const NotificationSequence notification_sequence = {\n        &notification_led_message,\n        NULL,\n    };\n\n    // Send notification\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_internal_message_block(notification, &notification_sequence);\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nstatic void cli_command_top(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n\n    uint32_t interval;\n    if(!args_read_duration(args, &interval, NULL)) {\n        interval = 1000;\n    }\n\n    FuriThreadList* thread_list = furi_thread_list_alloc();\n    do {\n        uint32_t tick = furi_get_tick();\n        furi_thread_enumerate(thread_list);\n\n        if(interval) printf(ANSI_CURSOR_POS(\"1\", \"1\"));\n\n        uint32_t uptime = tick / furi_kernel_get_tick_frequency();\n        printf(\n            \"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\" ANSI_ERASE_LINE(\n                ANSI_ERASE_FROM_CURSOR_TO_END) \"\\r\\n\",\n            furi_thread_list_size(thread_list),\n            (double)furi_thread_list_get_isr_time(thread_list),\n            uptime / 60 / 60,\n            uptime / 60 % 60,\n            uptime % 60);\n\n        printf(\n            \"Heap: total %zu, free %zu, minimum %zu, max block %zu\" ANSI_ERASE_LINE(\n                ANSI_ERASE_FROM_CURSOR_TO_END) \"\\r\\n\" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END) \"\\r\\n\",\n            memmgr_get_total_heap(),\n            memmgr_get_free_heap(),\n            memmgr_get_minimum_free_heap(),\n            memmgr_heap_get_max_free_block());\n\n        printf(\n            \"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\" ANSI_ERASE_LINE(\n                ANSI_ERASE_FROM_CURSOR_TO_END) \"\\r\\n\",\n            \"AppID\",\n            \"Name\",\n            \"State\",\n            \"Prio\",\n            \"Stack start\",\n            \"Stack\",\n            \"Stack Min\",\n            \"Heap\",\n            \"%CPU\");\n\n        for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) {\n            const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i);\n            printf(\n                \"%-17s %-20s %-10s %5d   0x%08lx %6lu %10lu %7zu %5.1f\" ANSI_ERASE_LINE(\n                    ANSI_ERASE_FROM_CURSOR_TO_END) \"\\r\\n\",\n                item->app_id,\n                item->name,\n                item->state,\n                item->priority,\n                item->stack_address,\n                item->stack_size,\n                item->stack_min_free,\n                item->heap,\n                (double)item->cpu);\n        }\n\n        printf(ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END));\n        fflush(stdout);\n\n    } while(interval > 0 && cli_sleep(pipe, interval));\n\n    furi_thread_list_free(thread_list);\n}\n\nvoid cli_command_free(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n\n    printf(\"Free heap size: %zu\\r\\n\", memmgr_get_free_heap());\n    printf(\"Total heap size: %zu\\r\\n\", memmgr_get_total_heap());\n    printf(\"Minimum heap size: %zu\\r\\n\", memmgr_get_minimum_free_heap());\n    printf(\"Maximum heap block: %zu\\r\\n\", memmgr_heap_get_max_free_block());\n\n    printf(\"Pool free: %zu\\r\\n\", memmgr_pool_get_free());\n    printf(\"Maximum pool block: %zu\\r\\n\", memmgr_pool_get_max_block());\n}\n\nvoid cli_command_free_blocks(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n\n    memmgr_heap_printf_free_blocks();\n}\n\nvoid cli_command_i2c(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n\n    furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);\n    printf(\"Scanning external i2c on PC0(SCL)/PC1(SDA)\\r\\n\"\n           \"Clock: 100khz, 7bit address\\r\\n\"\n           \"\\r\\n\");\n    printf(\"  | 0 1 2 3 4 5 6 7 8 9 A B C D E F\\r\\n\");\n    printf(\"--+--------------------------------\\r\\n\");\n    for(uint8_t row = 0; row < 0x8; row++) {\n        printf(\"%x | \", row);\n        for(uint8_t column = 0; column <= 0xF; column++) {\n            bool ret = furi_hal_i2c_is_device_ready(\n                &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2);\n            printf(\"%c \", ret ? '#' : '-');\n        }\n        printf(\"\\r\\n\");\n    }\n    furi_hal_i2c_release(&furi_hal_i2c_handle_external);\n}\n\n/**\n * Echoes any bytes it receives except ASCII ETX (0x03, Ctrl+C)\n */\nvoid cli_command_echo(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(args);\n    UNUSED(context);\n\n    uint8_t buffer[256];\n\n    while(true) {\n        size_t to_read = CLAMP(pipe_bytes_available(pipe), sizeof(buffer), 1UL);\n        size_t read = pipe_receive(pipe, buffer, to_read);\n        if(read < to_read) break;\n\n        if(memchr(buffer, CliKeyETX, read)) break;\n\n        size_t written = pipe_send(pipe, buffer, read);\n        if(written < read) break;\n    }\n}\n\n/**\n * @brief Pause for a specified duration or until Ctrl+C is pressed or the\n * session is terminated.\n *\n * The duration can be specified in various units such as milliseconds (ms),\n * seconds (s), minutes (m), or hours (h). If the unit is not specified, the\n * second is used by default.\n *\n * Example:\n *   sleep 5s\n */\nvoid cli_command_sleep(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* duration_string;\n    duration_string = furi_string_alloc();\n\n    do {\n        uint32_t duration_in_ms = 0;\n        if(!args_read_string_and_trim(args, duration_string) ||\n           !args_read_duration(duration_string, &duration_in_ms, \"s\")) {\n            cli_print_usage(\"sleep\", \"[<0-...>[<ms|s|m|h>]]\", furi_string_get_cstr(args));\n            break;\n        }\n\n        cli_sleep(pipe, duration_in_ms);\n\n    } while(false);\n\n    furi_string_free(duration_string);\n}\n\nvoid cli_main_commands_init(CliRegistry* registry) {\n    cli_registry_add_command(\n        registry, \"!\", CliCommandFlagParallelSafe, cli_command_info, (void*)true);\n    cli_registry_add_command(registry, \"info\", CliCommandFlagParallelSafe, cli_command_info, NULL);\n    cli_registry_add_command(\n        registry, \"device_info\", CliCommandFlagParallelSafe, cli_command_info, (void*)true);\n\n    cli_registry_add_command(\n        registry, \"uptime\", CliCommandFlagParallelSafe, cli_command_uptime, NULL);\n    cli_registry_add_command(registry, \"date\", CliCommandFlagParallelSafe, cli_command_date, NULL);\n    cli_registry_add_command(registry, \"log\", CliCommandFlagParallelSafe, cli_command_log, NULL);\n    cli_registry_add_command(registry, \"sysctl\", CliCommandFlagDefault, cli_command_sysctl, NULL);\n    cli_registry_add_command(registry, \"top\", CliCommandFlagParallelSafe, cli_command_top, NULL);\n    cli_registry_add_command(registry, \"free\", CliCommandFlagParallelSafe, cli_command_free, NULL);\n    cli_registry_add_command(\n        registry, \"free_blocks\", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL);\n    cli_registry_add_command(registry, \"echo\", CliCommandFlagParallelSafe, cli_command_echo, NULL);\n    cli_registry_add_command(\n        registry, \"sleep\", CliCommandFlagParallelSafe, cli_command_sleep, NULL);\n\n    cli_registry_add_command(registry, \"vibro\", CliCommandFlagDefault, cli_command_vibro, NULL);\n    cli_registry_add_command(registry, \"led\", CliCommandFlagDefault, cli_command_led, NULL);\n    cli_registry_add_command(registry, \"gpio\", CliCommandFlagDefault, cli_command_gpio, NULL);\n    cli_registry_add_command(registry, \"i2c\", CliCommandFlagDefault, cli_command_i2c, NULL);\n}\n\nvoid cli_on_system_start(void) {\n    CliRegistry* registry = cli_registry_alloc();\n    cli_main_commands_init(registry);\n    furi_record_create(RECORD_CLI, registry);\n}\n"
  },
  {
    "path": "applications/services/cli/cli_main_commands.h",
    "content": "#pragma once\n\n#include \"cli.h\"\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/cli/cli_registry.h>\n\n#define CLI_APPID \"cli\"\n\nvoid cli_main_commands_init(CliRegistry* registry);\n"
  },
  {
    "path": "applications/services/cli/cli_main_shell.c",
    "content": "#include \"cli_main_shell.h\"\n#include \"cli_main_commands.h\"\n#include <toolbox/cli/cli_ansi.h>\n#include <toolbox/cli/shell/cli_shell.h>\n#include <furi_hal_version.h>\n\nvoid cli_main_motd(void* context) {\n    UNUSED(context);\n    printf(ANSI_FLIPPER_BRAND_ORANGE\n           \"\\r\\n\"\n           \"              _.-------.._                    -,\\r\\n\"\n           \"          .-\\\"```\\\"--..,,_/ /`-,               -,  \\\\ \\r\\n\"\n           \"       .:\\\"          /:/  /'\\\\  \\\\     ,_...,  `. |  |\\r\\n\"\n           \"      /       ,----/:/  /`\\\\ _\\\\~`_-\\\"`     _;\\r\\n\"\n           \"     '      / /`\\\"\\\"\\\"'\\\\ \\\\ \\\\.~`_-'      ,-\\\"'/ \\r\\n\"\n           \"    |      | |  0    | | .-'      ,/`  /\\r\\n\"\n           \"   |    ,..\\\\ \\\\     ,.-\\\"`       ,/`    /\\r\\n\"\n           \"  ;    :    `/`\\\"\\\"\\\\`           ,/--==,/-----,\\r\\n\"\n           \"  |    `-...|        -.___-Z:_______J...---;\\r\\n\"\n           \"  :         `                           _-'\\r\\n\"\n           \" _L_  _     ___  ___  ___  ___  ____--\\\"`___  _     ___\\r\\n\"\n           \"| __|| |   |_ _|| _ \\\\| _ \\\\| __|| _ \\\\   / __|| |   |_ _|\\r\\n\"\n           \"| _| | |__  | | |  _/|  _/| _| |   /  | (__ | |__  | |\\r\\n\"\n           \"|_|  |____||___||_|  |_|  |___||_|_\\\\   \\\\___||____||___|\\r\\n\"\n           \"\\r\\n\" ANSI_FG_BR_WHITE \"Welcome to Flipper Zero Command Line Interface!\\r\\n\"\n           \"Read the manual: https://docs.flipper.net/development/cli\\r\\n\"\n           \"Run `help` or `?` to list available commands\\r\\n\"\n           \"\\r\\n\" ANSI_RESET);\n\n    const Version* firmware_version = furi_hal_version_get_firmware_version();\n    if(firmware_version) {\n        printf(\n            \"Firmware version: %s %s (%s%s built on %s)\\r\\n\",\n            version_get_gitbranch(firmware_version),\n            version_get_version(firmware_version),\n            version_get_githash(firmware_version),\n            version_get_dirty_flag(firmware_version) ? \"-dirty\" : \"\",\n            version_get_builddate(firmware_version));\n    }\n}\n\nconst CliCommandExternalConfig cli_main_ext_config = {\n    .search_directory = \"/ext/apps_data/cli/plugins\",\n    .fal_prefix = \"cli_\",\n    .appid = CLI_APPID,\n};\n"
  },
  {
    "path": "applications/services/cli/cli_main_shell.h",
    "content": "#pragma once\n\n#include <toolbox/cli/cli_command.h>\n\nvoid cli_main_motd(void* context);\n\nextern const CliCommandExternalConfig cli_main_ext_config;\n"
  },
  {
    "path": "applications/services/cli/cli_vcp.c",
    "content": "#include \"cli_vcp.h\"\n#include <furi_hal_usb_cdc.h>\n#include <furi_hal.h>\n#include <furi.h>\n#include <stdint.h>\n#include <toolbox/pipe.h>\n#include <toolbox/cli/shell/cli_shell.h>\n#include <toolbox/api_lock.h>\n#include \"cli_main_shell.h\"\n#include \"cli_main_commands.h\"\n\n#define TAG \"CliVcp\"\n\n#define USB_CDC_PKT_LEN   CDC_DATA_SZ\n#define VCP_BUF_SIZE      (USB_CDC_PKT_LEN * 3)\n#define VCP_IF_NUM        0\n#define VCP_MESSAGE_Q_LEN 8\n\n#ifdef CLI_VCP_TRACE\n#define VCP_TRACE(...) FURI_LOG_T(__VA_ARGS__)\n#else\n#define VCP_TRACE(...)\n#endif\n\ntypedef struct {\n    enum {\n        CliVcpMessageTypeEnable,\n        CliVcpMessageTypeDisable,\n    } type;\n    FuriApiLock api_lock;\n    union {};\n} CliVcpMessage;\n\ntypedef enum {\n    CliVcpInternalEventConnected,\n    CliVcpInternalEventDisconnected,\n    CliVcpInternalEventTxDone,\n    CliVcpInternalEventRx,\n} CliVcpInternalEvent;\n\nstruct CliVcp {\n    FuriEventLoop* event_loop;\n    FuriMessageQueue* message_queue; // <! external messages\n    FuriMessageQueue* internal_evt_queue;\n\n    bool is_enabled, is_connected;\n    FuriHalUsbInterface* previous_interface;\n\n    PipeSide* own_pipe;\n    PipeSide* shell_pipe;\n    volatile bool is_currently_transmitting;\n    size_t previous_tx_length;\n\n    CliRegistry* main_registry;\n    CliShell* shell;\n};\n\n// ============\n// Data copying\n// ============\n\n/**\n * Called in the following cases:\n *   - previous transfer has finished;\n *   - new data became available to send.\n */\nstatic void cli_vcp_maybe_send_data(CliVcp* cli_vcp) {\n    if(cli_vcp->is_currently_transmitting) return;\n    if(!cli_vcp->own_pipe) return;\n\n    uint8_t buf[USB_CDC_PKT_LEN];\n    size_t to_receive_from_pipe = MIN(sizeof(buf), pipe_bytes_available(cli_vcp->own_pipe));\n    size_t length = pipe_receive(cli_vcp->own_pipe, buf, to_receive_from_pipe);\n    if(length > 0 || cli_vcp->previous_tx_length == USB_CDC_PKT_LEN) {\n        VCP_TRACE(TAG, \"cdc_send length=%zu\", length);\n        cli_vcp->is_currently_transmitting = true;\n        furi_hal_cdc_send(VCP_IF_NUM, buf, length);\n    }\n    cli_vcp->previous_tx_length = length;\n}\n\n/**\n * Called in the following cases:\n *   - new data arrived at the endpoint;\n *   - data was read out of the pipe.\n */\nstatic void cli_vcp_maybe_receive_data(CliVcp* cli_vcp) {\n    if(!cli_vcp->own_pipe) return;\n    if(pipe_spaces_available(cli_vcp->own_pipe) < USB_CDC_PKT_LEN) return;\n\n    uint8_t buf[USB_CDC_PKT_LEN];\n    size_t length = furi_hal_cdc_receive(VCP_IF_NUM, buf, sizeof(buf));\n    VCP_TRACE(TAG, \"cdc_receive length=%zu\", length);\n    furi_check(pipe_send(cli_vcp->own_pipe, buf, length) == length);\n}\n\n// =============\n// CDC callbacks\n// =============\n\nstatic void cli_vcp_signal_internal_event(CliVcp* cli_vcp, CliVcpInternalEvent event) {\n    furi_check(furi_message_queue_put(cli_vcp->internal_evt_queue, &event, 0) == FuriStatusOk);\n}\n\nstatic void cli_vcp_cdc_tx_done(void* context) {\n    CliVcp* cli_vcp = context;\n    cli_vcp->is_currently_transmitting = false;\n    cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventTxDone);\n}\n\nstatic void cli_vcp_cdc_rx(void* context) {\n    CliVcp* cli_vcp = context;\n    cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventRx);\n}\n\nstatic void cli_vcp_cdc_state_callback(void* context, CdcState state) {\n    CliVcp* cli_vcp = context;\n    if(state == CdcStateDisconnected) {\n        cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected);\n    }\n    // `Connected` events are generated by DTR going active\n}\n\nstatic void cli_vcp_cdc_ctrl_line_callback(void* context, CdcCtrlLine ctrl_lines) {\n    CliVcp* cli_vcp = context;\n    if(ctrl_lines & CdcCtrlLineDTR) {\n        cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventConnected);\n    } else {\n        cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected);\n    }\n}\n\nstatic CdcCallbacks cdc_callbacks = {\n    .tx_ep_callback = cli_vcp_cdc_tx_done,\n    .rx_ep_callback = cli_vcp_cdc_rx,\n    .state_callback = cli_vcp_cdc_state_callback,\n    .ctrl_line_callback = cli_vcp_cdc_ctrl_line_callback,\n    .config_callback = NULL,\n};\n\n// ======================\n// Pipe callback handlers\n// ======================\n\nstatic void cli_vcp_data_from_shell(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    CliVcp* cli_vcp = context;\n    cli_vcp_maybe_send_data(cli_vcp);\n}\n\nstatic void cli_vcp_shell_ready(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    CliVcp* cli_vcp = context;\n    cli_vcp_maybe_receive_data(cli_vcp);\n}\n\n/**\n * Processes messages arriving from other threads\n */\nstatic void cli_vcp_message_received(FuriEventLoopObject* object, void* context) {\n    CliVcp* cli_vcp = context;\n    CliVcpMessage message;\n    furi_check(furi_message_queue_get(object, &message, 0) == FuriStatusOk);\n\n    switch(message.type) {\n    case CliVcpMessageTypeEnable:\n        if(cli_vcp->is_enabled) break;\n        FURI_LOG_D(TAG, \"Enabling\");\n        cli_vcp->is_enabled = true;\n\n        // switch usb mode\n        cli_vcp->previous_interface = furi_hal_usb_get_config();\n        furi_hal_usb_set_config(&usb_cdc_single, NULL);\n        furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_callbacks, cli_vcp);\n        break;\n\n    case CliVcpMessageTypeDisable:\n        if(!cli_vcp->is_enabled) break;\n        FURI_LOG_D(TAG, \"Disabling\");\n        cli_vcp->is_enabled = false;\n\n        // restore usb mode\n        furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL);\n        furi_hal_usb_set_config(cli_vcp->previous_interface, NULL);\n        break;\n    }\n\n    api_lock_unlock(message.api_lock);\n}\n\n/**\n * Processes messages arriving from CDC event callbacks\n */\nstatic void cli_vcp_internal_event_happened(FuriEventLoopObject* object, void* context) {\n    CliVcp* cli_vcp = context;\n    CliVcpInternalEvent event;\n    furi_check(furi_message_queue_get(object, &event, 0) == FuriStatusOk);\n\n    switch(event) {\n    case CliVcpInternalEventRx: {\n        VCP_TRACE(TAG, \"Rx\");\n        cli_vcp_maybe_receive_data(cli_vcp);\n        break;\n    }\n\n    case CliVcpInternalEventTxDone: {\n        VCP_TRACE(TAG, \"TxDone\");\n        cli_vcp_maybe_send_data(cli_vcp);\n        break;\n    }\n\n    case CliVcpInternalEventDisconnected: {\n        if(!cli_vcp->is_connected) return;\n        FURI_LOG_D(TAG, \"Disconnected\");\n        cli_vcp->is_connected = false;\n\n        // disconnect our side of the pipe\n        pipe_detach_from_event_loop(cli_vcp->own_pipe);\n        pipe_free(cli_vcp->own_pipe);\n        cli_vcp->own_pipe = NULL;\n\n        // wait for shell to stop\n        cli_shell_join(cli_vcp->shell);\n        cli_shell_free(cli_vcp->shell);\n        pipe_free(cli_vcp->shell_pipe);\n        break;\n    }\n\n    case CliVcpInternalEventConnected: {\n        if(cli_vcp->is_connected) return;\n        FURI_LOG_D(TAG, \"Connected\");\n        cli_vcp->is_connected = true;\n\n        // start shell thread\n        PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1);\n        cli_vcp->own_pipe = bundle.alices_side;\n        cli_vcp->shell_pipe = bundle.bobs_side;\n        pipe_attach_to_event_loop(cli_vcp->own_pipe, cli_vcp->event_loop);\n        pipe_set_callback_context(cli_vcp->own_pipe, cli_vcp);\n        pipe_set_data_arrived_callback(\n            cli_vcp->own_pipe, cli_vcp_data_from_shell, FuriEventLoopEventFlagEdge);\n        pipe_set_space_freed_callback(\n            cli_vcp->own_pipe, cli_vcp_shell_ready, FuriEventLoopEventFlagEdge);\n        furi_delay_ms(33); // we are too fast, minicom isn't ready yet\n        cli_vcp->shell = cli_shell_alloc(\n            cli_main_motd, NULL, cli_vcp->shell_pipe, cli_vcp->main_registry, &cli_main_ext_config);\n        cli_shell_start(cli_vcp->shell);\n        break;\n    }\n    }\n}\n\n// ============\n// Thread stuff\n// ============\n\nstatic CliVcp* cli_vcp_alloc(void) {\n    CliVcp* cli_vcp = malloc(sizeof(CliVcp));\n\n    cli_vcp->event_loop = furi_event_loop_alloc();\n\n    cli_vcp->message_queue = furi_message_queue_alloc(VCP_MESSAGE_Q_LEN, sizeof(CliVcpMessage));\n    furi_event_loop_subscribe_message_queue(\n        cli_vcp->event_loop,\n        cli_vcp->message_queue,\n        FuriEventLoopEventIn,\n        cli_vcp_message_received,\n        cli_vcp);\n\n    cli_vcp->internal_evt_queue =\n        furi_message_queue_alloc(VCP_MESSAGE_Q_LEN, sizeof(CliVcpInternalEvent));\n    furi_event_loop_subscribe_message_queue(\n        cli_vcp->event_loop,\n        cli_vcp->internal_evt_queue,\n        FuriEventLoopEventIn,\n        cli_vcp_internal_event_happened,\n        cli_vcp);\n\n    cli_vcp->main_registry = furi_record_open(RECORD_CLI);\n\n    return cli_vcp;\n}\n\nint32_t cli_vcp_srv(void* p) {\n    UNUSED(p);\n\n    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {\n        FURI_LOG_W(TAG, \"Skipping start in special boot mode\");\n        furi_thread_suspend(furi_thread_get_current_id());\n        return 0;\n    }\n\n    CliVcp* cli_vcp = cli_vcp_alloc();\n    furi_record_create(RECORD_CLI_VCP, cli_vcp);\n    furi_event_loop_run(cli_vcp->event_loop);\n\n    return 0;\n}\n\n// ==========\n// Public API\n// ==========\n\nstatic void cli_vcp_synchronous_request(CliVcp* cli_vcp, CliVcpMessage* message) {\n    message->api_lock = api_lock_alloc_locked();\n    furi_message_queue_put(cli_vcp->message_queue, message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message->api_lock);\n}\n\nvoid cli_vcp_enable(CliVcp* cli_vcp) {\n    furi_check(cli_vcp);\n    CliVcpMessage message = {\n        .type = CliVcpMessageTypeEnable,\n    };\n    cli_vcp_synchronous_request(cli_vcp, &message);\n}\n\nvoid cli_vcp_disable(CliVcp* cli_vcp) {\n    furi_check(cli_vcp);\n    CliVcpMessage message = {\n        .type = CliVcpMessageTypeDisable,\n    };\n    cli_vcp_synchronous_request(cli_vcp, &message);\n}\n"
  },
  {
    "path": "applications/services/cli/cli_vcp.h",
    "content": "/**\n * @file cli_vcp.h\n * VCP HAL API\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_CLI_VCP \"cli_vcp\"\n\ntypedef struct CliVcp CliVcp;\n\nvoid cli_vcp_enable(CliVcp* cli_vcp);\nvoid cli_vcp_disable(CliVcp* cli_vcp);\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/cli/commands/buzzer.c",
    "content": "#include \"../cli_main_commands.h\"\n#include <notification/notification_app.h>\n#include <math.h>\n#include <toolbox/args.h>\n#include <toolbox/cli/cli_command.h>\n\nvoid cli_command_buzzer_print_usage(bool is_freq_subcommand, FuriString* args) {\n    if(is_freq_subcommand) {\n        cli_print_usage(\n            \"buzzer freq\", \"<freq_hz> [<0-...>[<ms|s|m|h>]]\", furi_string_get_cstr(args));\n\n    } else {\n        cli_print_usage(\"buzzer note\", \"<note> [<0-...>[<ms|s|m|h>]]\", furi_string_get_cstr(args));\n    }\n}\n\n// Consider volume effectively zero if below this threshold\n#define BUZZER_VOLUME_EPSILON (1e-3f)\n\nfloat cli_command_buzzer_read_frequency(bool is_freq_subcommand, FuriString* args) {\n    float frequency = 0.0f;\n\n    if(is_freq_subcommand) {\n        args_read_float_and_trim(args, &frequency);\n        return frequency;\n    }\n\n    // Extract note frequency from name\n\n    FuriString* note_name_string;\n    note_name_string = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, note_name_string)) {\n            break;\n        }\n        const char* note_name = furi_string_get_cstr(note_name_string);\n        frequency = notification_messages_notes_frequency_from_name(note_name);\n    } while(false);\n\n    furi_string_free(note_name_string);\n\n    return frequency;\n}\n\nvoid cli_command_buzzer_play(\n    PipeSide* pipe,\n    NotificationApp* notification,\n    bool is_freq_subcommand,\n    FuriString* args) {\n    FuriString* duration_string;\n    duration_string = furi_string_alloc();\n\n    do {\n        float frequency = cli_command_buzzer_read_frequency(is_freq_subcommand, args);\n        if(frequency <= 0.0f) {\n            cli_command_buzzer_print_usage(is_freq_subcommand, args);\n            break;\n        }\n\n        const NotificationMessage notification_buzzer_message = {\n            .type = NotificationMessageTypeSoundOn,\n            .data.sound.frequency = frequency,\n            .data.sound.volume = 1.0,\n        };\n\n        // Optional duration\n        uint32_t duration_ms = 100;\n        if(args_read_string_and_trim(args, duration_string)) {\n            if(!args_read_duration(duration_string, &duration_ms, NULL)) {\n                cli_command_buzzer_print_usage(is_freq_subcommand, args);\n                break;\n            }\n        }\n\n        const NotificationSequence sound_on_sequence = {\n            &notification_buzzer_message,\n            &message_do_not_reset,\n            NULL,\n        };\n\n        // Play sound\n        notification_message_block(notification, &sound_on_sequence);\n\n        cli_sleep(pipe, duration_ms);\n\n        // Stop sound\n        const NotificationSequence sound_off_sequence = {\n            &message_sound_off,\n            NULL,\n        };\n        notification_message_block(notification, &sound_off_sequence);\n\n    } while(false);\n\n    furi_string_free(duration_string);\n}\n\nvoid execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n\n    FuriString* command_string;\n    command_string = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, command_string)) {\n            cli_print_usage(\"buzzer\", \"<freq|note>\", furi_string_get_cstr(args));\n            break;\n        }\n\n        // Check volume\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {\n            printf(\"Flipper is in stealth mode. Unmute the device to control buzzer.\");\n            break;\n        }\n        if(fabsf(notification->settings.speaker_volume) < BUZZER_VOLUME_EPSILON) {\n            printf(\"Sound is disabled in settings. Increase volume to control buzzer.\");\n            break;\n        }\n\n        if(furi_string_cmp(command_string, \"freq\") == 0) {\n            cli_command_buzzer_play(pipe, notification, true, args);\n            break;\n        } else if(furi_string_cmp(command_string, \"note\") == 0) {\n            cli_command_buzzer_play(pipe, notification, false, args);\n            break;\n        }\n\n        cli_print_usage(\"buzzer\", \"<freq|note>\", furi_string_get_cstr(args));\n\n    } while(false);\n\n    furi_string_free(command_string);\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nCLI_COMMAND_INTERFACE(buzzer, execute, CliCommandFlagDefault, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/services/cli/commands/hello_world.c",
    "content": "#include \"../cli_main_commands.h\"\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n    puts(\"Hello, World!\");\n}\n\nCLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagParallelSafe, 768, CLI_APPID);\n"
  },
  {
    "path": "applications/services/cli/commands/neofetch.c",
    "content": "#include \"../cli_main_commands.h\"\n#include <toolbox/cli/cli_ansi.h>\n#include <toolbox/version.h>\n#include <furi_hal.h>\n#include <furi_hal_info.h>\n#include <FreeRTOS.h>\n#include <FreeRTOS-Kernel/include/task.h>\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n\n    static const char* const neofetch_logo[] = {\n        \"            _.-------.._                    -,\",\n        \"        .-\\\"```\\\"--..,,_/ /`-,               -,  \\\\ \",\n        \"     .:\\\"          /:/  /'\\\\  \\\\     ,_...,  `. |  |\",\n        \"    /       ,----/:/  /`\\\\ _\\\\~`_-\\\"`     _;\",\n        \"   '      / /`\\\"\\\"\\\"'\\\\ \\\\ \\\\.~`_-'      ,-\\\"'/ \",\n        \"  |      | |  0    | | .-'      ,/`  /\",\n        \" |    ,..\\\\ \\\\     ,.-\\\"`       ,/`    /\",\n        \";    :    `/`\\\"\\\"\\\\`           ,/--==,/-----,\",\n        \"|    `-...|        -.___-Z:_______J...---;\",\n        \":         `                           _-'\",\n    };\n#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE\n\n    // Determine logo parameters\n    size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0;\n    for(size_t i = 0; i < logo_height; i++)\n        logo_width = MAX(logo_width, strlen(neofetch_logo[i]));\n    logo_width += 4; // space between logo and info\n\n    // Format hostname delimiter\n    const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr());\n    char delimiter[64];\n    memset(delimiter, '-', size_of_hostname);\n    delimiter[size_of_hostname] = '\\0';\n\n    // Get heap info\n    size_t heap_total = memmgr_get_total_heap();\n    size_t heap_used = heap_total - memmgr_get_free_heap();\n    uint16_t heap_percent = (100 * heap_used) / heap_total;\n\n    // Get storage info\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    uint64_t ext_total, ext_free, ext_used, ext_percent;\n    storage_common_fs_info(storage, \"/ext\", &ext_total, &ext_free);\n    ext_used = ext_total - ext_free;\n    ext_percent = (100 * ext_used) / ext_total;\n    ext_used /= 1024 * 1024;\n    ext_total /= 1024 * 1024;\n    furi_record_close(RECORD_STORAGE);\n\n    // Get battery info\n    uint16_t charge_percent = furi_hal_power_get_pct();\n    const char* charge_state;\n    if(furi_hal_power_is_charging()) {\n        if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) {\n            charge_state = \"charging\";\n        } else {\n            charge_state = \"charged\";\n        }\n    } else {\n        charge_state = \"discharging\";\n    }\n\n    // Get misc info\n    uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();\n    const Version* version = version_get();\n    uint16_t major, minor;\n    furi_hal_info_get_api_version(&major, &minor);\n\n    // Print ASCII art with info\n    const size_t info_height = 16;\n    for(size_t i = 0; i < MAX(logo_height, info_height); i++) {\n        printf(NEOFETCH_COLOR \"%-*s\", logo_width, (i < logo_height) ? neofetch_logo[i] : \"\");\n        switch(i) {\n        case 0: // you@<hostname>\n            printf(\"you\" ANSI_RESET \"@\" NEOFETCH_COLOR \"%s\", furi_hal_version_get_name_ptr());\n            break;\n        case 1: // delimiter\n            printf(ANSI_RESET \"%s\", delimiter);\n            break;\n        case 2: // OS: FURI <edition> <branch> <version> <commit> (SDK <maj>.<min>)\n            printf(\n                \"OS\" ANSI_RESET \": FURI %s %s %s %s (SDK %hu.%hu)\",\n                version_get_version(version),\n                version_get_gitbranch(version),\n                version_get_version(version),\n                version_get_githash(version),\n                major,\n                minor);\n            break;\n        case 3: // Host: <model> <hostname>\n            printf(\n                \"Host\" ANSI_RESET \": %s %s\",\n                furi_hal_version_get_model_code(),\n                furi_hal_version_get_device_name_ptr());\n            break;\n        case 4: // Kernel: FreeRTOS <maj>.<min>.<build>\n            printf(\n                \"Kernel\" ANSI_RESET \": FreeRTOS %d.%d.%d\",\n                tskKERNEL_VERSION_MAJOR,\n                tskKERNEL_VERSION_MINOR,\n                tskKERNEL_VERSION_BUILD);\n            break;\n        case 5: // Uptime: ?h?m?s\n            printf(\n                \"Uptime\" ANSI_RESET \": %luh%lum%lus\",\n                uptime / 60 / 60,\n                uptime / 60 % 60,\n                uptime % 60);\n            break;\n        case 6: // ST7567 128x64 @ 1 bpp in 1.4\"\n            printf(\"Display\" ANSI_RESET \": ST7567 128x64 @ 1 bpp in 1.4\\\"\");\n            break;\n        case 7: // DE: GuiSrv\n            printf(\"DE\" ANSI_RESET \": GuiSrv\");\n            break;\n        case 8: // Shell: CliSrv\n            printf(\"Shell\" ANSI_RESET \": CliShell\");\n            break;\n        case 9: // CPU: STM32WB55RG @ 64 MHz\n            printf(\"CPU\" ANSI_RESET \": STM32WB55RG @ 64 MHz\");\n            break;\n        case 10: // Memory: <used> / <total> B (??%)\n            printf(\n                \"Memory\" ANSI_RESET \": %zu / %zu B (%hu%%)\", heap_used, heap_total, heap_percent);\n            break;\n        case 11: // Disk (/ext): <used> / <total> MiB (??%)\n            printf(\n                \"Disk (/ext)\" ANSI_RESET \": %llu / %llu MiB (%llu%%)\",\n                ext_used,\n                ext_total,\n                ext_percent);\n            break;\n        case 12: // Battery: ??% (<state>)\n            printf(\"Battery\" ANSI_RESET \": %hu%% (%s)\" ANSI_RESET, charge_percent, charge_state);\n            break;\n        case 13: // empty space\n            break;\n        case 14: // Colors (line 1)\n            for(size_t j = 30; j <= 37; j++)\n                printf(\"\\e[%dm███\", j);\n            break;\n        case 15: // Colors (line 2)\n            for(size_t j = 90; j <= 97; j++)\n                printf(\"\\e[%dm███\", j);\n            break;\n        default:\n            break;\n        }\n        printf(\"\\r\\n\");\n    }\n    printf(ANSI_RESET);\n#undef NEOFETCH_COLOR\n}\n\nCLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/services/cli/commands/subshell_demo.c",
    "content": "#include \"../cli_main_commands.h\"\n#include <toolbox/cli/cli_registry.h>\n#include <toolbox/cli/shell/cli_shell.h>\n#include <toolbox/cli/cli_ansi.h>\n\n#define RAINBOW_SUBCOMMAND                                                                \\\n    ANSI_FG_RED \"s\" ANSI_FG_YELLOW \"u\" ANSI_FG_BLUE \"b\" ANSI_FG_GREEN \"c\" ANSI_FG_MAGENTA \\\n                \"o\" ANSI_FG_RED \"m\" ANSI_FG_YELLOW \"m\" ANSI_FG_BLUE \"a\" ANSI_FG_GREEN     \\\n                \"n\" ANSI_FG_MAGENTA \"d\"\n\nstatic void subcommand(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n    printf(\"This is a ✨\" RAINBOW_SUBCOMMAND ANSI_RESET \"✨!\");\n}\n\nstatic void motd(void* context) {\n    UNUSED(context);\n    printf(\"\\r\\n\");\n    printf(\"+------------------------------------+\\r\\n\");\n    printf(\"|            Hello world!            |\\r\\n\");\n    printf(\"| This is the \" ANSI_FG_GREEN \"MOTD\" ANSI_RESET \" for our \" ANSI_FG_BLUE\n           \"subshell\" ANSI_RESET \"! |\\r\\n\");\n    printf(\"+------------------------------------+\\r\\n\");\n}\n\nstatic void execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(args);\n    UNUSED(context);\n    CliRegistry* registry = cli_registry_alloc();\n    cli_registry_add_command(registry, \"subcommand\", CliCommandFlagParallelSafe, subcommand, NULL);\n\n    CliShell* shell = cli_shell_alloc(motd, NULL, pipe, registry, NULL);\n    cli_shell_set_prompt(shell, \"subshell\");\n    cli_shell_start(shell);\n    cli_shell_join(shell);\n\n    cli_shell_free(shell);\n    cli_registry_free(registry);\n}\n\nCLI_COMMAND_INTERFACE(subshell_demo, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);\n"
  },
  {
    "path": "applications/services/crypto/application.fam",
    "content": "App(\n    appid=\"crypto_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"crypto_on_system_start\",\n    order=20,\n)\n"
  },
  {
    "path": "applications/services/crypto/crypto_cli.c",
    "content": "#include <furi_hal.h>\n#include <furi.h>\n\n#include <lib/toolbox/args.h>\n#include <toolbox/pipe.h>\n#include <cli/cli_main_commands.h>\n#include <toolbox/cli/cli_registry.h>\n#include <toolbox/cli/cli_ansi.h>\n\nvoid crypto_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"crypto <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n    printf(\n        \"\\tencrypt <key_slot:int> <iv:hex>\\t - Using key from secure enclave and IV encrypt plain text with AES256CBC and encode to hex\\r\\n\");\n    printf(\n        \"\\tdecrypt <key_slot:int> <iv:hex>\\t - Using key from secure enclave and IV decrypt hex encoded encrypted with AES256CBC data to plain text\\r\\n\");\n    printf(\"\\thas_key <key_slot:int>\\t - Check if secure enclave has key in slot\\r\\n\");\n    printf(\n        \"\\tstore_key <key_slot:int> <key_type:str> <key_size:int> <key_data:hex>\\t - Store key in secure enclave. !!! NON-REVERSABLE OPERATION - READ MANUAL FIRST !!!\\r\\n\");\n}\n\nvoid crypto_cli_encrypt(PipeSide* pipe, FuriString* args) {\n    int key_slot = 0;\n    bool key_loaded = false;\n    uint8_t iv[16];\n\n    do {\n        if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) {\n            printf(\"Incorrect or missing slot, expected int 1-100\");\n            break;\n        }\n\n        if(!args_read_hex_bytes(args, iv, 16)) {\n            printf(\"Incorrect or missing IV, expected 16 bytes in hex\");\n            break;\n        }\n\n        if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {\n            printf(\"Unable to load key from slot %d\", key_slot);\n            break;\n        }\n        key_loaded = true;\n\n        printf(\"Enter plain text and press Ctrl+C to complete encryption:\\r\\n\");\n\n        FuriString* input;\n        input = furi_string_alloc();\n        char c;\n        while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) {\n            if(c == CliKeyETX) {\n                printf(\"\\r\\n\");\n                break;\n            } else if(c >= 0x20 && c < 0x7F) {\n                putc(c, stdout);\n                fflush(stdout);\n                furi_string_push_back(input, c);\n            } else if(c == CliKeyCR) {\n                printf(\"\\r\\n\");\n                furi_string_cat(input, \"\\r\\n\");\n            }\n        }\n\n        size_t size = furi_string_size(input);\n        if(size > 0) {\n            // C-string null termination and block alignments\n            size++;\n            size_t remain = size % 16;\n            if(remain) {\n                size = size - remain + 16;\n            }\n            furi_string_reserve(input, size);\n            uint8_t* output = malloc(size);\n            if(!furi_hal_crypto_encrypt(\n                   (const uint8_t*)furi_string_get_cstr(input), output, size)) {\n                printf(\"Failed to encrypt input\");\n            } else {\n                printf(\"Hex-encoded encrypted data:\\r\\n\");\n                for(size_t i = 0; i < size; i++) {\n                    if(i % 80 == 0) printf(\"\\r\\n\");\n                    printf(\"%02x\", output[i]);\n                }\n                printf(\"\\r\\n\");\n            }\n            free(output);\n        } else {\n            printf(\"No input\");\n        }\n\n        furi_string_free(input);\n    } while(0);\n\n    if(key_loaded) {\n        furi_hal_crypto_enclave_unload_key(key_slot);\n    }\n}\n\nvoid crypto_cli_decrypt(PipeSide* pipe, FuriString* args) {\n    int key_slot = 0;\n    bool key_loaded = false;\n    uint8_t iv[16];\n\n    do {\n        if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) {\n            printf(\"Incorrect or missing slot, expected int 1-100\");\n            break;\n        }\n\n        if(!args_read_hex_bytes(args, iv, 16)) {\n            printf(\"Incorrect or missing IV, expected 16 bytes in hex\");\n            break;\n        }\n\n        if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {\n            printf(\"Unable to load key from slot %d\", key_slot);\n            break;\n        }\n        key_loaded = true;\n\n        printf(\"Enter Hex-encoded data and press Ctrl+C to complete decryption:\\r\\n\");\n\n        FuriString* hex_input;\n        hex_input = furi_string_alloc();\n        char c;\n        while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) {\n            if(c == CliKeyETX) {\n                printf(\"\\r\\n\");\n                break;\n            } else if(c >= 0x20 && c < 0x7F) {\n                putc(c, stdout);\n                fflush(stdout);\n                furi_string_push_back(hex_input, c);\n            } else if(c == CliKeyCR) {\n                printf(\"\\r\\n\");\n            }\n        }\n\n        furi_string_trim(hex_input);\n        size_t hex_size = furi_string_size(hex_input);\n        if(hex_size > 0 && hex_size % 2 == 0) {\n            size_t size = hex_size / 2;\n            uint8_t* input = malloc(size);\n            uint8_t* output = malloc(size);\n\n            if(args_read_hex_bytes(hex_input, input, size)) {\n                if(furi_hal_crypto_decrypt(input, output, size)) {\n                    printf(\"Decrypted data:\\r\\n\");\n                    printf(\"%s\\r\\n\", output); //-V576\n                } else {\n                    printf(\"Failed to decrypt\\r\\n\");\n                }\n            } else {\n                printf(\"Failed to parse input\");\n            }\n\n            free(input);\n            free(output);\n        } else {\n            printf(\"Invalid or empty input\");\n        }\n\n        furi_string_free(hex_input);\n    } while(0);\n\n    if(key_loaded) {\n        furi_hal_crypto_enclave_unload_key(key_slot);\n    }\n}\n\nvoid crypto_cli_has_key(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    int key_slot = 0;\n    uint8_t iv[16] = {0};\n\n    do {\n        if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) {\n            printf(\"Incorrect or missing slot, expected int 1-100\");\n            break;\n        }\n\n        if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {\n            printf(\"Unable to load key from slot %d\", key_slot);\n            break;\n        }\n\n        printf(\"Successfully loaded key from slot %d\", key_slot);\n\n        furi_hal_crypto_enclave_unload_key(key_slot);\n    } while(0);\n}\n\nvoid crypto_cli_store_key(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    int key_slot = 0;\n    int key_size = 0;\n    FuriString* key_type;\n    key_type = furi_string_alloc();\n\n    uint8_t data[32 + 12] = {};\n    FuriHalCryptoKey key;\n    key.data = data;\n    size_t data_size = 0;\n\n    do {\n        if(!args_read_int_and_trim(args, &key_slot)) {\n            printf(\"Incorrect or missing key type, expected master, simple or encrypted\");\n            break;\n        }\n        if(!args_read_string_and_trim(args, key_type)) {\n            printf(\"Incorrect or missing key type, expected master, simple or encrypted\");\n            break;\n        }\n\n        if(furi_string_cmp_str(key_type, \"master\") == 0) {\n            if(key_slot != 0) {\n                printf(\"Master keyslot must be is 0\");\n                break;\n            }\n            key.type = FuriHalCryptoKeyTypeMaster;\n        } else if(furi_string_cmp_str(key_type, \"simple\") == 0) {\n            if(key_slot < 1 || key_slot > 99) {\n                printf(\"Simple keyslot must be in range\");\n                break;\n            }\n            key.type = FuriHalCryptoKeyTypeSimple;\n        } else if(furi_string_cmp_str(key_type, \"encrypted\") == 0) {\n            key.type = FuriHalCryptoKeyTypeEncrypted;\n            data_size += 12;\n        } else {\n            printf(\"Incorrect or missing key type, expected master, simple or encrypted\");\n            break;\n        }\n\n        if(!args_read_int_and_trim(args, &key_size)) {\n            printf(\"Incorrect or missing key size, expected 128 or 256\");\n            break;\n        }\n\n        if(key_size == 128) {\n            key.size = FuriHalCryptoKeySize128;\n            data_size += 16;\n        } else if(key_size == 256) {\n            key.size = FuriHalCryptoKeySize256;\n            data_size += 32;\n        } else {\n            printf(\"Incorrect or missing key size, expected 128 or 256\");\n        }\n\n        if(!args_read_hex_bytes(args, data, data_size)) {\n            printf(\"Incorrect or missing key data, expected hex encoded key with or without IV.\");\n            break;\n        }\n\n        if(key_slot > 0) {\n            uint8_t iv[16] = {0};\n            if(key_slot > 1) {\n                if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) {\n                    printf(\n                        \"Slot %d before %d is empty, which is not allowed\",\n                        key_slot - 1,\n                        key_slot);\n                    break;\n                }\n                furi_hal_crypto_enclave_unload_key(key_slot - 1);\n            }\n\n            if(furi_hal_crypto_enclave_load_key(key_slot, iv)) {\n                furi_hal_crypto_enclave_unload_key(key_slot);\n                printf(\"Key slot %d is already used\", key_slot);\n                break;\n            }\n        }\n\n        uint8_t slot;\n        if(furi_hal_crypto_enclave_store_key(&key, &slot)) {\n            printf(\"Success. Stored to slot: %d\", slot);\n        } else {\n            printf(\"Failure\");\n        }\n    } while(0);\n\n    furi_string_free(key_type);\n}\n\nstatic void crypto_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            crypto_cli_print_usage();\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"encrypt\") == 0) {\n            crypto_cli_encrypt(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"decrypt\") == 0) {\n            crypto_cli_decrypt(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"has_key\") == 0) {\n            crypto_cli_has_key(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"store_key\") == 0) {\n            crypto_cli_store_key(pipe, args);\n            break;\n        }\n\n        crypto_cli_print_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n\nvoid crypto_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(registry, \"crypto\", CliCommandFlagDefault, crypto_cli, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(crypto_cli);\n#endif\n}\n"
  },
  {
    "path": "applications/services/desktop/animations/animation_manager.c",
    "content": "#include <gui/view_stack.h>\n#include <stdint.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <dolphin/dolphin.h>\n#include <power/power_service/power.h>\n#include <storage/storage.h>\n#include <assets_icons.h>\n\n#include \"views/bubble_animation_view.h\"\n#include \"views/one_shot_animation_view.h\"\n#include \"animation_storage.h\"\n#include \"animation_manager.h\"\n\n#define TAG \"AnimationManager\"\n\n#define HARDCODED_ANIMATION_NAME   \"L1_Tv_128x47\"\n#define NO_SD_ANIMATION_NAME       \"L1_NoSd_128x49\"\n#define BAD_BATTERY_ANIMATION_NAME \"L1_BadBattery_128x47\"\n\n#define NO_DB_ANIMATION_NAME    \"L0_NoDb_128x51\"\n#define BAD_SD_ANIMATION_NAME   \"L0_SdBad_128x51\"\n#define SD_OK_ANIMATION_NAME    \"L0_SdOk_128x51\"\n#define URL_ANIMATION_NAME      \"L0_Url_128x51\"\n#define NEW_MAIL_ANIMATION_NAME \"L0_NewMail_128x51\"\n\ntypedef enum {\n    AnimationManagerStateIdle,\n    AnimationManagerStateBlocked,\n    AnimationManagerStateFreezedIdle,\n    AnimationManagerStateFreezedBlocked,\n} AnimationManagerState;\n\nstruct AnimationManager {\n    AnimationManagerState state;\n    FuriPubSubSubscription* pubsub_subscription_storage;\n    FuriPubSubSubscription* pubsub_subscription_dolphin;\n    BubbleAnimationView* animation_view;\n    OneShotView* one_shot_view;\n    FuriTimer* idle_animation_timer;\n    StorageAnimation* current_animation;\n    AnimationManagerInteractCallback interact_callback;\n    AnimationManagerSetNewIdleAnimationCallback new_idle_callback;\n    AnimationManagerSetNewIdleAnimationCallback check_blocking_callback;\n    void* context;\n    FuriString* freezed_animation_name;\n    int32_t freezed_animation_time_left;\n    ViewStack* view_stack;\n\n    bool dummy_mode            : 1;\n    bool blocking_shown_url    : 1;\n    bool blocking_shown_sd_bad : 1;\n    bool blocking_shown_no_db  : 1;\n    bool blocking_shown_sd_ok  : 1;\n    bool levelup_pending       : 1;\n    bool levelup_active        : 1;\n};\n\nstatic StorageAnimation*\n    animation_manager_select_idle_animation(AnimationManager* animation_manager);\nstatic void animation_manager_replace_current_animation(\n    AnimationManager* animation_manager,\n    StorageAnimation* storage_animation);\nstatic void animation_manager_start_new_idle(AnimationManager* animation_manager);\nstatic bool animation_manager_check_blocking(AnimationManager* animation_manager);\nstatic bool animation_manager_is_valid_idle_animation(\n    const StorageAnimationManifestInfo* info,\n    const DolphinStats* stats);\nstatic void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager);\nstatic void animation_manager_switch_to_animation_view(AnimationManager* animation_manager);\n\nvoid animation_manager_set_context(AnimationManager* animation_manager, void* context) {\n    furi_assert(animation_manager);\n    animation_manager->context = context;\n}\n\nvoid animation_manager_set_new_idle_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerSetNewIdleAnimationCallback callback) {\n    furi_assert(animation_manager);\n    animation_manager->new_idle_callback = callback;\n}\n\nvoid animation_manager_set_check_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerCheckBlockingCallback callback) {\n    furi_assert(animation_manager);\n    animation_manager->check_blocking_callback = callback;\n}\n\nvoid animation_manager_set_interact_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerInteractCallback callback) {\n    furi_assert(animation_manager);\n    animation_manager->interact_callback = callback;\n}\n\nvoid animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) {\n    furi_assert(animation_manager);\n    // Prevent change of animations if mode is the same\n    if(animation_manager->dummy_mode != enabled) {\n        animation_manager->dummy_mode = enabled;\n        animation_manager_start_new_idle(animation_manager);\n    }\n}\n\nstatic void animation_manager_storage_callback(const void* message, void* context) {\n    const StorageEvent* storage_event = message;\n\n    switch(storage_event->type) {\n    case StorageEventTypeCardMount:\n    case StorageEventTypeCardUnmount:\n    case StorageEventTypeCardMountError:\n        furi_assert(context);\n        AnimationManager* animation_manager = context;\n        if(animation_manager->check_blocking_callback) {\n            animation_manager->check_blocking_callback(animation_manager->context);\n        }\n        break;\n\n    default:\n        break;\n    }\n}\n\nstatic void animation_manager_dolphin_callback(const void* message, void* context) {\n    const DolphinPubsubEvent* dolphin_event = message;\n\n    switch(*dolphin_event) {\n    case DolphinPubsubEventUpdate:\n        furi_assert(context);\n        AnimationManager* animation_manager = context;\n        if(animation_manager->check_blocking_callback) {\n            animation_manager->check_blocking_callback(animation_manager->context);\n        }\n        break;\n    default:\n        break;\n    }\n}\n\nstatic void animation_manager_timer_callback(void* context) {\n    furi_assert(context);\n    AnimationManager* animation_manager = context;\n    if(animation_manager->new_idle_callback) {\n        animation_manager->new_idle_callback(animation_manager->context);\n    }\n}\n\nstatic void animation_manager_interact_callback(void* context) {\n    furi_assert(context);\n    AnimationManager* animation_manager = context;\n    if(animation_manager->interact_callback) {\n        animation_manager->interact_callback(animation_manager->context);\n    }\n}\n\n/* reaction to animation_manager->check_blocking_callback() */\nvoid animation_manager_check_blocking_process(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    if(animation_manager->state == AnimationManagerStateIdle) {\n        bool blocked = animation_manager_check_blocking(animation_manager);\n\n        if(!blocked) {\n            Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n            DolphinStats stats = dolphin_stats(dolphin);\n            furi_record_close(RECORD_DOLPHIN);\n\n            const StorageAnimationManifestInfo* manifest_info =\n                animation_storage_get_meta(animation_manager->current_animation);\n            bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats);\n\n            if(!valid) {\n                animation_manager_start_new_idle(animation_manager);\n            }\n        }\n    }\n}\n\n/* reaction to animation_manager->new_idle_callback() */\nvoid animation_manager_new_idle_process(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    if(animation_manager->state == AnimationManagerStateIdle) {\n        animation_manager_start_new_idle(animation_manager);\n    }\n}\n\n/* reaction to animation_manager->interact_callback() */\nbool animation_manager_interact_process(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    bool consumed = true;\n\n    if(animation_manager->levelup_pending) {\n        animation_manager->levelup_pending = false;\n        animation_manager->levelup_active = true;\n        animation_manager_switch_to_one_shot_view(animation_manager);\n        Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n        dolphin_upgrade_level(dolphin);\n        furi_record_close(RECORD_DOLPHIN);\n    } else if(animation_manager->levelup_active) {\n        animation_manager->levelup_active = false;\n        animation_manager_start_new_idle(animation_manager);\n        animation_manager_switch_to_animation_view(animation_manager);\n    } else if(animation_manager->state == AnimationManagerStateBlocked) {\n        bool blocked = animation_manager_check_blocking(animation_manager);\n\n        if(!blocked) {\n            animation_manager_start_new_idle(animation_manager);\n        }\n    } else {\n        consumed = false;\n    }\n\n    return consumed;\n}\n\nstatic void animation_manager_start_new_idle(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    StorageAnimation* new_animation = animation_manager_select_idle_animation(animation_manager);\n    animation_manager_replace_current_animation(animation_manager, new_animation);\n    const BubbleAnimation* bubble_animation =\n        animation_storage_get_bubble_animation(animation_manager->current_animation);\n    animation_manager->state = AnimationManagerStateIdle;\n    furi_timer_start(animation_manager->idle_animation_timer, bubble_animation->duration * 1000);\n}\n\nstatic bool animation_manager_check_blocking(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    StorageAnimation* blocking_animation = NULL;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FS_Error sd_status = storage_sd_status(storage);\n\n    if(sd_status == FSE_INTERNAL) {\n        if(!animation_manager->blocking_shown_sd_bad) {\n            blocking_animation = animation_storage_find_animation(BAD_SD_ANIMATION_NAME);\n            furi_assert(blocking_animation);\n            animation_manager->blocking_shown_sd_bad = true;\n        }\n    } else if(sd_status == FSE_NOT_READY) {\n        animation_manager->blocking_shown_sd_bad = false;\n        animation_manager->blocking_shown_sd_ok = false;\n        animation_manager->blocking_shown_no_db = false;\n    } else if(sd_status == FSE_OK) {\n        if(!animation_manager->blocking_shown_sd_ok) {\n            blocking_animation = animation_storage_find_animation(SD_OK_ANIMATION_NAME);\n            furi_assert(blocking_animation);\n            animation_manager->blocking_shown_sd_ok = true;\n        } else if(!animation_manager->blocking_shown_no_db) {\n            if(!storage_file_exists(storage, EXT_PATH(\"Manifest\"))) {\n                blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME);\n                furi_assert(blocking_animation);\n                animation_manager->blocking_shown_no_db = true;\n                animation_manager->blocking_shown_url = true;\n            }\n        } else if(animation_manager->blocking_shown_url) {\n            blocking_animation = animation_storage_find_animation(URL_ANIMATION_NAME);\n            furi_assert(blocking_animation);\n            animation_manager->blocking_shown_url = false;\n        }\n    }\n\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinStats stats = dolphin_stats(dolphin);\n    furi_record_close(RECORD_DOLPHIN);\n    if(!blocking_animation && stats.level_up_is_pending) {\n        blocking_animation = animation_storage_find_animation(NEW_MAIL_ANIMATION_NAME);\n        furi_check(blocking_animation);\n        animation_manager->levelup_pending = true;\n    }\n\n    if(blocking_animation) {\n        furi_timer_stop(animation_manager->idle_animation_timer);\n        animation_manager_replace_current_animation(animation_manager, blocking_animation);\n        /* no timer starting because this is blocking animation */\n        animation_manager->state = AnimationManagerStateBlocked;\n    }\n\n    furi_record_close(RECORD_STORAGE);\n\n    return !!blocking_animation;\n}\n\nstatic void animation_manager_replace_current_animation(\n    AnimationManager* animation_manager,\n    StorageAnimation* storage_animation) {\n    furi_assert(storage_animation);\n    StorageAnimation* previous_animation = animation_manager->current_animation;\n\n    const BubbleAnimation* animation = animation_storage_get_bubble_animation(storage_animation);\n    bubble_animation_view_set_animation(animation_manager->animation_view, animation);\n    const char* new_name = animation_storage_get_meta(storage_animation)->name;\n    FURI_LOG_I(TAG, \"Select \\'%s\\' animation\", new_name);\n    animation_manager->current_animation = storage_animation;\n\n    if(previous_animation) {\n        animation_storage_free_storage_animation(&previous_animation);\n    }\n}\n\nAnimationManager* animation_manager_alloc(void) {\n    AnimationManager* animation_manager = malloc(sizeof(AnimationManager));\n    animation_manager->animation_view = bubble_animation_view_alloc();\n    animation_manager->view_stack = view_stack_alloc();\n    View* animation_view = bubble_animation_get_view(animation_manager->animation_view);\n    view_stack_add_view(animation_manager->view_stack, animation_view);\n    animation_manager->freezed_animation_name = furi_string_alloc();\n\n    animation_manager->idle_animation_timer =\n        furi_timer_alloc(animation_manager_timer_callback, FuriTimerTypeOnce, animation_manager);\n    bubble_animation_view_set_interact_callback(\n        animation_manager->animation_view, animation_manager_interact_callback, animation_manager);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    animation_manager->pubsub_subscription_storage = furi_pubsub_subscribe(\n        storage_get_pubsub(storage), animation_manager_storage_callback, animation_manager);\n    furi_record_close(RECORD_STORAGE);\n\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    animation_manager->pubsub_subscription_dolphin = furi_pubsub_subscribe(\n        dolphin_get_pubsub(dolphin), animation_manager_dolphin_callback, animation_manager);\n    furi_record_close(RECORD_DOLPHIN);\n\n    animation_manager->blocking_shown_sd_ok = true;\n    if(!animation_manager_check_blocking(animation_manager)) {\n        animation_manager_start_new_idle(animation_manager);\n    }\n\n    return animation_manager;\n}\n\nvoid animation_manager_free(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    furi_pubsub_unsubscribe(\n        dolphin_get_pubsub(dolphin), animation_manager->pubsub_subscription_dolphin);\n    furi_record_close(RECORD_DOLPHIN);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_unsubscribe(\n        storage_get_pubsub(storage), animation_manager->pubsub_subscription_storage);\n    furi_record_close(RECORD_STORAGE);\n\n    furi_string_free(animation_manager->freezed_animation_name);\n    View* animation_view = bubble_animation_get_view(animation_manager->animation_view);\n    view_stack_remove_view(animation_manager->view_stack, animation_view);\n    bubble_animation_view_free(animation_manager->animation_view);\n    furi_timer_free(animation_manager->idle_animation_timer);\n}\n\nView* animation_manager_get_animation_view(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n\n    return view_stack_get_view(animation_manager->view_stack);\n}\n\nstatic bool animation_manager_is_valid_idle_animation(\n    const StorageAnimationManifestInfo* info,\n    const DolphinStats* stats) {\n    furi_assert(info);\n    furi_assert(info->name);\n\n    bool result = true;\n\n    if(!strcmp(info->name, BAD_BATTERY_ANIMATION_NAME)) {\n        Power* power = furi_record_open(RECORD_POWER);\n        bool battery_is_well = power_is_battery_healthy(power);\n        furi_record_close(RECORD_POWER);\n\n        result = !battery_is_well;\n    }\n    if(!strcmp(info->name, NO_SD_ANIMATION_NAME)) {\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n        FS_Error sd_status = storage_sd_status(storage);\n        furi_record_close(RECORD_STORAGE);\n\n        result = (sd_status == FSE_NOT_READY);\n    }\n    if((stats->butthurt < info->min_butthurt) || (stats->butthurt > info->max_butthurt)) {\n        result = false;\n    }\n    if((stats->level < info->min_level) || (stats->level > info->max_level)) {\n        result = false;\n    }\n\n    return result;\n}\n\nstatic StorageAnimation*\n    animation_manager_select_idle_animation(AnimationManager* animation_manager) {\n    if(animation_manager->dummy_mode) {\n        return animation_storage_find_animation(HARDCODED_ANIMATION_NAME);\n    }\n    StorageAnimationList_t animation_list;\n    StorageAnimationList_init(animation_list);\n    animation_storage_fill_animation_list(&animation_list);\n\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinStats stats = dolphin_stats(dolphin);\n    furi_record_close(RECORD_DOLPHIN);\n    uint32_t whole_weight = 0;\n\n    StorageAnimationList_it_t it;\n    for(StorageAnimationList_it(it, animation_list); !StorageAnimationList_end_p(it);) {\n        StorageAnimation* storage_animation = *StorageAnimationList_ref(it);\n        const StorageAnimationManifestInfo* manifest_info =\n            animation_storage_get_meta(storage_animation);\n        bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats);\n\n        if(valid) {\n            whole_weight += manifest_info->weight;\n            StorageAnimationList_next(it);\n        } else {\n            animation_storage_free_storage_animation(&storage_animation);\n            /* remove and increase iterator */\n            StorageAnimationList_remove(animation_list, it);\n        }\n    }\n\n    uint32_t lucky_number = furi_hal_random_get() % whole_weight;\n    uint32_t weight = 0;\n\n    StorageAnimation* selected = NULL;\n    for\n        M_EACH(item, animation_list, StorageAnimationList_t) {\n            if(lucky_number < weight) {\n                break;\n            }\n            weight += animation_storage_get_meta(*item)->weight;\n            selected = *item;\n        }\n\n    for\n        M_EACH(item, animation_list, StorageAnimationList_t) {\n            if(*item != selected) {\n                animation_storage_free_storage_animation(item);\n            }\n        }\n\n    StorageAnimationList_clear(animation_list);\n\n    /* cache animation, if failed - choose reliable animation */\n    if(!animation_storage_get_bubble_animation(selected)) {\n        const char* name = animation_storage_get_meta(selected)->name;\n        FURI_LOG_E(TAG, \"Can't upload animation described in manifest: \\'%s\\'\", name);\n        animation_storage_free_storage_animation(&selected);\n        selected = animation_storage_find_animation(HARDCODED_ANIMATION_NAME);\n    }\n\n    furi_assert(selected);\n    return selected;\n}\n\nbool animation_manager_is_animation_loaded(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    return animation_manager->current_animation;\n}\n\nvoid animation_manager_unload_and_stall_animation(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    furi_assert(animation_manager->current_animation);\n    furi_assert(!furi_string_size(animation_manager->freezed_animation_name));\n    furi_assert(\n        (animation_manager->state == AnimationManagerStateIdle) ||\n        (animation_manager->state == AnimationManagerStateBlocked));\n\n    if(animation_manager->state == AnimationManagerStateBlocked) {\n        animation_manager->state = AnimationManagerStateFreezedBlocked;\n    } else if(animation_manager->state == AnimationManagerStateIdle) { //-V547\n        animation_manager->state = AnimationManagerStateFreezedIdle;\n\n        animation_manager->freezed_animation_time_left =\n            furi_timer_get_expire_time(animation_manager->idle_animation_timer) - furi_get_tick();\n        if(animation_manager->freezed_animation_time_left < 0) {\n            animation_manager->freezed_animation_time_left = 0;\n        }\n        furi_timer_stop(animation_manager->idle_animation_timer);\n    } else {\n        furi_crash();\n    }\n\n    FURI_LOG_I(\n        TAG,\n        \"Unload animation \\'%s\\'\",\n        animation_storage_get_meta(animation_manager->current_animation)->name);\n\n    StorageAnimationManifestInfo* meta =\n        animation_storage_get_meta(animation_manager->current_animation);\n    /* copy str, not move, because it can be internal animation */\n    furi_string_set(animation_manager->freezed_animation_name, meta->name);\n\n    bubble_animation_freeze(animation_manager->animation_view);\n    animation_storage_free_storage_animation(&animation_manager->current_animation);\n}\n\nvoid animation_manager_load_and_continue_animation(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    furi_assert(!animation_manager->current_animation);\n    furi_assert(furi_string_size(animation_manager->freezed_animation_name));\n    furi_assert(\n        (animation_manager->state == AnimationManagerStateFreezedIdle) ||\n        (animation_manager->state == AnimationManagerStateFreezedBlocked));\n\n    if(animation_manager->state == AnimationManagerStateFreezedBlocked) {\n        StorageAnimation* restore_animation = animation_storage_find_animation(\n            furi_string_get_cstr(animation_manager->freezed_animation_name));\n        /* all blocked animations must be in flipper -> we can\n         * always find blocking animation */\n        furi_assert(restore_animation);\n        animation_manager_replace_current_animation(animation_manager, restore_animation);\n        animation_manager->state = AnimationManagerStateBlocked;\n    } else if(animation_manager->state == AnimationManagerStateFreezedIdle) { //-V547\n        /* check if we missed some system notifications, and set current_animation */\n        bool blocked = animation_manager_check_blocking(animation_manager);\n        if(!blocked) {\n            /* if no blocking - try restore last one idle */\n            StorageAnimation* restore_animation = animation_storage_find_animation(\n                furi_string_get_cstr(animation_manager->freezed_animation_name));\n            if(restore_animation) {\n                Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n                DolphinStats stats = dolphin_stats(dolphin);\n                furi_record_close(RECORD_DOLPHIN);\n                const StorageAnimationManifestInfo* manifest_info =\n                    animation_storage_get_meta(restore_animation);\n                bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats);\n                if(valid) {\n                    animation_manager_replace_current_animation(\n                        animation_manager, restore_animation);\n                    animation_manager->state = AnimationManagerStateIdle;\n\n                    if(animation_manager->freezed_animation_time_left) {\n                        furi_timer_start(\n                            animation_manager->idle_animation_timer,\n                            animation_manager->freezed_animation_time_left);\n                    } else {\n                        const BubbleAnimation* animation = animation_storage_get_bubble_animation(\n                            animation_manager->current_animation);\n                        furi_timer_start(\n                            animation_manager->idle_animation_timer, animation->duration * 1000);\n                    }\n                }\n            } else {\n                FURI_LOG_E(\n                    TAG,\n                    \"Failed to restore \\'%s\\'\",\n                    furi_string_get_cstr(animation_manager->freezed_animation_name));\n            }\n        }\n    } else {\n        /* Unknown state is an error. But not in release version.*/\n        furi_crash();\n    }\n\n    /* if can't restore previous animation - select new */\n    if(!animation_manager->current_animation) {\n        animation_manager_start_new_idle(animation_manager);\n    }\n    FURI_LOG_I(\n        TAG,\n        \"Load animation \\'%s\\'\",\n        animation_storage_get_meta(animation_manager->current_animation)->name);\n\n    bubble_animation_unfreeze(animation_manager->animation_view);\n    furi_string_reset(animation_manager->freezed_animation_name);\n    furi_assert(animation_manager->current_animation);\n}\n\nstatic void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    furi_assert(!animation_manager->one_shot_view);\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinStats stats = dolphin_stats(dolphin);\n    furi_record_close(RECORD_DOLPHIN);\n\n    animation_manager->one_shot_view = one_shot_view_alloc();\n    one_shot_view_set_interact_callback(\n        animation_manager->one_shot_view, animation_manager_interact_callback, animation_manager);\n    View* prev_view = bubble_animation_get_view(animation_manager->animation_view);\n    View* next_view = one_shot_view_get_view(animation_manager->one_shot_view);\n    view_stack_remove_view(animation_manager->view_stack, prev_view);\n    view_stack_add_view(animation_manager->view_stack, next_view);\n    if(stats.level == 1) {\n        one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64);\n    } else if(stats.level == 2) {\n        one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup2_128x64);\n    } else {\n        furi_crash();\n    }\n}\n\nstatic void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) {\n    furi_assert(animation_manager);\n    furi_assert(animation_manager->one_shot_view);\n\n    View* prev_view = one_shot_view_get_view(animation_manager->one_shot_view);\n    View* next_view = bubble_animation_get_view(animation_manager->animation_view);\n    view_stack_remove_view(animation_manager->view_stack, prev_view);\n    view_stack_add_view(animation_manager->view_stack, next_view);\n    one_shot_view_free(animation_manager->one_shot_view);\n    animation_manager->one_shot_view = NULL;\n}\n"
  },
  {
    "path": "applications/services/desktop/animations/animation_manager.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include <gui/icon_i.h>\n#include <stdint.h>\n#include <dolphin/dolphin.h>\n\ntypedef struct AnimationManager AnimationManager;\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    const char* text;\n    Align align_h;\n    Align align_v;\n} Bubble;\n\ntypedef struct FrameBubble {\n    Bubble bubble;\n    uint8_t start_frame;\n    uint8_t end_frame;\n    const struct FrameBubble* next_bubble;\n} FrameBubble;\n\ntypedef struct {\n    const FrameBubble* const* frame_bubble_sequences;\n    uint8_t frame_bubble_sequences_count;\n    const Icon icon_animation;\n    const uint8_t* frame_order;\n    uint8_t passive_frames;\n    uint8_t active_frames;\n    uint8_t active_cycles;\n    uint16_t duration;\n    uint16_t active_cooldown;\n} BubbleAnimation;\n\ntypedef void (*AnimationManagerSetNewIdleAnimationCallback)(void* context);\ntypedef void (*AnimationManagerCheckBlockingCallback)(void* context);\ntypedef void (*AnimationManagerInteractCallback)(void*);\n\n/**\n * Allocate Animation Manager\n *\n * @return animation manager instance\n */\nAnimationManager* animation_manager_alloc(void);\n\n/**\n * Free Animation Manager\n *\n * @animation_manager   instance\n */\nvoid animation_manager_free(AnimationManager* animation_manager);\n\n/**\n * Get View of Animation Manager\n *\n * @animation_manager   instance\n * @return      view\n */\nView* animation_manager_get_animation_view(AnimationManager* animation_manager);\n\n/**\n * Set context for all callbacks for Animation Manager\n *\n * @animation_manager   instance\n * @context             context\n */\nvoid animation_manager_set_context(AnimationManager* animation_manager, void* context);\n\n/**\n * Set callback for Animation Manager for deferred calls\n * for animation_manager_new_idle_process().\n * Animation Manager doesn't have it's own thread, so main thread gives\n * callbacks to A.M. to call when it should perform some inner manipulations.\n * This callback is called from other threads and should notify main thread\n * when to call animation_manager_new_idle_process().\n * So scheme is this:\n * A.M. sets callbacks,\n * callbacks notifies main thread\n * main thread in its own context calls appropriate *_process() function.\n *\n * @animation_manager   instance\n * @callback            callback\n */\nvoid animation_manager_set_new_idle_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerSetNewIdleAnimationCallback callback);\n\n/**\n * Function to call in main thread as a response to\n * set_new_idle_callback's call.\n *\n * @animation_manager   instance\n */\nvoid animation_manager_new_idle_process(AnimationManager* animation_manager);\n\n/**\n * Set callback for Animation Manager for deferred calls\n * for animation_manager_check_blocking_process().\n *\n * @animation_manager   instance\n * @callback            callback\n */\nvoid animation_manager_set_check_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerCheckBlockingCallback callback);\n\n/**\n * Function to call in main thread as a response to\n * set_new_idle_callback's call.\n *\n * @animation_manager   instance\n */\nvoid animation_manager_check_blocking_process(AnimationManager* animation_manager);\n\n/**\n * Set callback for Animation Manager for deferred calls\n * for animation_manager_interact_process().\n *\n * @animation_manager   instance\n * @callback            callback\n */\nvoid animation_manager_set_interact_callback(\n    AnimationManager* animation_manager,\n    AnimationManagerInteractCallback callback);\n\n/**\n * Function to call in main thread as a response to\n * set_new_idle_callback's call.\n *\n * @animation_manager   instance\n * @return              true if event was consumed\n */\nbool animation_manager_interact_process(AnimationManager* animation_manager);\n\n/** Check if animation loaded\n *\n * @animation_manager   instance\n */\nbool animation_manager_is_animation_loaded(AnimationManager* animation_manager);\n\n/**\n * Unload and Stall animation actions. Draw callback in view\n * paints first frame of current animation until\n * animation_manager_load_and_continue_animation() is called.\n * Can't be called multiple times. Every Stall has to be finished\n * with Continue.\n *\n * @animation_manager   instance\n */\nvoid animation_manager_unload_and_stall_animation(AnimationManager* animation_manager);\n\n/**\n * Load and Contunue execution of animation manager.\n *\n * @animation_manager   instance\n */\nvoid animation_manager_load_and_continue_animation(AnimationManager* animation_manager);\n\n/**\n * Enable or disable dummy mode backgrounds of animation manager.\n *\n * @animation_manager    instance\n * @enabled              bool\n */\nvoid animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled);\n"
  },
  {
    "path": "applications/services/desktop/animations/animation_storage.c",
    "content": "\n#include <stdint.h>\n#include <flipper_format/flipper_format.h>\n#include <furi.h>\n#include <core/dangerous_defines.h>\n#include <storage/storage.h>\n#include <gui/icon_i.h>\n\n#include \"animation_manager.h\"\n#include \"animation_storage.h\"\n#include <assets_dolphin_internal.h>\n#include <assets_dolphin_blocking.h>\n\n#define TAG \"AnimationStorage\"\n\n#define ANIMATION_META_FILE     \"meta.txt\"\n#define ANIMATION_DIR           EXT_PATH(\"dolphin\")\n#define ANIMATION_MANIFEST_FILE ANIMATION_DIR \"/manifest.txt\"\n\nstatic void animation_storage_free_bubbles(BubbleAnimation* animation);\nstatic void animation_storage_free_frames(BubbleAnimation* animation);\nstatic void animation_storage_free_animation(BubbleAnimation** storage_animation);\nstatic BubbleAnimation* animation_storage_load_animation(const char* name);\n\nstatic bool animation_storage_load_single_manifest_info(\n    StorageAnimationManifestInfo* manifest_info,\n    const char* name) {\n    furi_assert(manifest_info);\n\n    bool result = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    flipper_format_set_strict_mode(file, true);\n    FuriString* read_string;\n    read_string = furi_string_alloc();\n\n    do {\n        uint32_t u32value;\n        if(FSE_OK != storage_sd_status(storage)) break;\n        if(!flipper_format_file_open_existing(file, ANIMATION_MANIFEST_FILE)) break;\n\n        if(!flipper_format_read_header(file, read_string, &u32value)) break;\n        if(furi_string_cmp_str(read_string, \"Flipper Animation Manifest\")) break;\n\n        manifest_info->name = NULL;\n\n        /* skip other animation names */\n        flipper_format_set_strict_mode(file, false);\n        while(flipper_format_read_string(file, \"Name\", read_string) &&\n              furi_string_cmp_str(read_string, name))\n            ;\n        if(furi_string_cmp_str(read_string, name)) break;\n        flipper_format_set_strict_mode(file, true);\n\n        manifest_info->name = strdup(furi_string_get_cstr(read_string));\n\n        if(!flipper_format_read_uint32(file, \"Min butthurt\", &u32value, 1)) break;\n        manifest_info->min_butthurt = u32value;\n        if(!flipper_format_read_uint32(file, \"Max butthurt\", &u32value, 1)) break;\n        manifest_info->max_butthurt = u32value;\n        if(!flipper_format_read_uint32(file, \"Min level\", &u32value, 1)) break;\n        manifest_info->min_level = u32value;\n        if(!flipper_format_read_uint32(file, \"Max level\", &u32value, 1)) break;\n        manifest_info->max_level = u32value;\n        if(!flipper_format_read_uint32(file, \"Weight\", &u32value, 1)) break;\n        manifest_info->weight = u32value;\n        result = true;\n    } while(0);\n\n    if(!result && manifest_info->name) {\n        free((void*)manifest_info->name);\n    }\n    furi_string_free(read_string);\n    flipper_format_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nvoid animation_storage_fill_animation_list(StorageAnimationList_t* animation_list) {\n    furi_assert(sizeof(StorageAnimationList_t) == sizeof(void*));\n    furi_assert(!StorageAnimationList_size(*animation_list));\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    /* Forbid skipping fields */\n    flipper_format_set_strict_mode(file, true);\n    FuriString* read_string;\n    read_string = furi_string_alloc();\n\n    do {\n        uint32_t u32value;\n        StorageAnimation* storage_animation = NULL;\n\n        if(FSE_OK != storage_sd_status(storage)) break;\n        if(!flipper_format_file_open_existing(file, ANIMATION_MANIFEST_FILE)) break;\n        if(!flipper_format_read_header(file, read_string, &u32value)) break;\n        if(furi_string_cmp_str(read_string, \"Flipper Animation Manifest\")) break;\n        do {\n            storage_animation = malloc(sizeof(StorageAnimation));\n            storage_animation->external = true;\n            storage_animation->animation = NULL;\n            storage_animation->manifest_info.name = NULL;\n\n            if(!flipper_format_read_string(file, \"Name\", read_string)) break;\n            storage_animation->manifest_info.name = strdup(furi_string_get_cstr(read_string));\n\n            if(!flipper_format_read_uint32(file, \"Min butthurt\", &u32value, 1)) break;\n            storage_animation->manifest_info.min_butthurt = u32value;\n            if(!flipper_format_read_uint32(file, \"Max butthurt\", &u32value, 1)) break;\n            storage_animation->manifest_info.max_butthurt = u32value;\n            if(!flipper_format_read_uint32(file, \"Min level\", &u32value, 1)) break;\n            storage_animation->manifest_info.min_level = u32value;\n            if(!flipper_format_read_uint32(file, \"Max level\", &u32value, 1)) break;\n            storage_animation->manifest_info.max_level = u32value;\n            if(!flipper_format_read_uint32(file, \"Weight\", &u32value, 1)) break;\n            storage_animation->manifest_info.weight = u32value;\n\n            StorageAnimationList_push_back(*animation_list, storage_animation);\n        } while(1);\n\n        animation_storage_free_storage_animation(&storage_animation);\n    } while(0);\n\n    furi_string_free(read_string);\n    flipper_format_free(file);\n\n    // add hard-coded animations\n    for(size_t i = 0; i < dolphin_internal_size; ++i) {\n        StorageAnimationList_push_back(*animation_list, (StorageAnimation*)&dolphin_internal[i]);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nStorageAnimation* animation_storage_find_animation(const char* name) {\n    furi_assert(name);\n    furi_assert(strlen(name));\n    StorageAnimation* storage_animation = NULL;\n\n    for(size_t i = 0; i < dolphin_blocking_size; ++i) {\n        if(!strcmp(dolphin_blocking[i].manifest_info.name, name)) {\n            storage_animation = (StorageAnimation*)&dolphin_blocking[i];\n            break;\n        }\n    }\n\n    if(!storage_animation) {\n        for(size_t i = 0; i < dolphin_internal_size; ++i) {\n            if(!strcmp(dolphin_internal[i].manifest_info.name, name)) {\n                storage_animation = (StorageAnimation*)&dolphin_internal[i];\n                break;\n            }\n        }\n    }\n\n    /* look through external animations */\n    if(!storage_animation) {\n        storage_animation = malloc(sizeof(StorageAnimation));\n        storage_animation->external = true;\n\n        bool result = false;\n        result =\n            animation_storage_load_single_manifest_info(&storage_animation->manifest_info, name);\n        if(result) {\n            storage_animation->animation = animation_storage_load_animation(name);\n            result = !!storage_animation->animation;\n        }\n        if(!result) {\n            animation_storage_free_storage_animation(&storage_animation);\n        }\n    }\n\n    return storage_animation;\n}\n\nStorageAnimationManifestInfo* animation_storage_get_meta(StorageAnimation* storage_animation) {\n    furi_assert(storage_animation);\n    return &storage_animation->manifest_info;\n}\n\nconst BubbleAnimation*\n    animation_storage_get_bubble_animation(StorageAnimation* storage_animation) {\n    furi_assert(storage_animation);\n    animation_storage_cache_animation(storage_animation);\n    return storage_animation->animation;\n}\n\nvoid animation_storage_cache_animation(StorageAnimation* storage_animation) {\n    furi_assert(storage_animation);\n\n    if(storage_animation->external) {\n        if(!storage_animation->animation) {\n            storage_animation->animation =\n                animation_storage_load_animation(storage_animation->manifest_info.name);\n        }\n    }\n}\n\nstatic void animation_storage_free_animation(BubbleAnimation** animation) {\n    furi_assert(animation);\n\n    if(*animation) {\n        animation_storage_free_bubbles(*animation);\n        animation_storage_free_frames(*animation);\n        if((*animation)->frame_order) {\n            free((void*)(*animation)->frame_order);\n        }\n        free(*animation);\n        *animation = NULL;\n    }\n}\n\nvoid animation_storage_free_storage_animation(StorageAnimation** storage_animation) {\n    furi_assert(storage_animation);\n    furi_assert(*storage_animation);\n\n    if((*storage_animation)->external) {\n        animation_storage_free_animation((BubbleAnimation**)&(*storage_animation)->animation);\n\n        if((*storage_animation)->manifest_info.name) {\n            free((void*)(*storage_animation)->manifest_info.name);\n        }\n        free(*storage_animation);\n    }\n\n    *storage_animation = NULL;\n}\n\nstatic bool animation_storage_cast_align(FuriString* align_str, Align* align) {\n    if(!furi_string_cmp(align_str, \"Bottom\")) {\n        *align = AlignBottom;\n    } else if(!furi_string_cmp(align_str, \"Top\")) {\n        *align = AlignTop;\n    } else if(!furi_string_cmp(align_str, \"Left\")) {\n        *align = AlignLeft;\n    } else if(!furi_string_cmp(align_str, \"Right\")) {\n        *align = AlignRight;\n    } else if(!furi_string_cmp(align_str, \"Center\")) {\n        *align = AlignCenter;\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\nstatic void animation_storage_free_frames(BubbleAnimation* animation) {\n    furi_assert(animation);\n\n    const Icon* icon = &animation->icon_animation;\n    for(int i = 0; i < icon->frame_count; ++i) {\n        if(icon->frames[i]) {\n            free((void*)icon->frames[i]);\n        }\n    }\n\n    free((void*)icon->frames);\n}\n\nstatic bool animation_storage_load_frames(\n    Storage* storage,\n    const char* name,\n    BubbleAnimation* animation,\n    uint32_t* frame_order,\n    uint8_t width,\n    uint8_t height) {\n    uint16_t frame_order_count = animation->passive_frames + animation->active_frames;\n\n    /* The frames should go in order (0...N), without omissions */\n    size_t max_frame_count = 0;\n    for(int i = 0; i < frame_order_count; ++i) {\n        max_frame_count = MAX(max_frame_count, frame_order[i]);\n    }\n\n    if((max_frame_count >= frame_order_count) || (max_frame_count >= 256 /* max uint8_t */)) {\n        return false;\n    }\n\n    Icon* icon = (Icon*)&animation->icon_animation;\n    FURI_CONST_ASSIGN(icon->frame_count, max_frame_count + 1);\n    FURI_CONST_ASSIGN(icon->frame_rate, 0);\n    FURI_CONST_ASSIGN(icon->height, height);\n    FURI_CONST_ASSIGN(icon->width, width);\n    icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count);\n\n    bool frames_ok = false;\n    File* file = storage_file_alloc(storage);\n    FileInfo file_info;\n    FuriString* filename;\n    filename = furi_string_alloc();\n    size_t max_filesize = ROUND_UP_TO(width, 8) * height + 1;\n\n    for(int i = 0; i < icon->frame_count; ++i) {\n        frames_ok = false;\n        furi_string_printf(filename, ANIMATION_DIR \"/%s/frame_%d.bm\", name, i);\n\n        if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK)\n            break;\n        if(file_info.size > max_filesize) {\n            FURI_LOG_E(\n                TAG,\n                \"Filesize %llu, max: %zu (width %u, height %u)\",\n                file_info.size,\n                max_filesize,\n                width,\n                height);\n            break;\n        }\n        if(!storage_file_open(\n               file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) {\n            FURI_LOG_E(TAG, \"Can't open file \\'%s\\'\", furi_string_get_cstr(filename));\n            break;\n        }\n\n        FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size));\n        if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) {\n            FURI_LOG_E(TAG, \"Read failed: \\'%s\\'\", furi_string_get_cstr(filename));\n            break;\n        }\n        storage_file_close(file);\n        frames_ok = true;\n    }\n\n    if(!frames_ok) {\n        FURI_LOG_E(\n            TAG,\n            \"Load \\'%s\\' failed, %ux%u, size: %llu\",\n            furi_string_get_cstr(filename),\n            width,\n            height,\n            file_info.size);\n        animation_storage_free_frames(animation);\n    } else {\n        furi_check(animation->icon_animation.frames);\n        for(int i = 0; i < animation->icon_animation.frame_count; ++i) {\n            furi_check(animation->icon_animation.frames[i]);\n        }\n    }\n\n    storage_file_free(file);\n    furi_string_free(filename);\n\n    return frames_ok;\n}\n\nstatic bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFormat* ff) {\n    uint32_t u32value;\n    FuriString* str;\n    str = furi_string_alloc();\n    bool success = false;\n    furi_assert(!animation->frame_bubble_sequences);\n\n    do {\n        if(!flipper_format_read_uint32(ff, \"Bubble slots\", &u32value, 1)) break;\n        if(u32value > 20) break;\n        animation->frame_bubble_sequences_count = u32value;\n        if(animation->frame_bubble_sequences_count == 0) {\n            success = true;\n            break;\n        }\n        animation->frame_bubble_sequences =\n            malloc(sizeof(FrameBubble*) * animation->frame_bubble_sequences_count);\n\n        int32_t current_slot = 0;\n        for(int i = 0; i < animation->frame_bubble_sequences_count; ++i) {\n            FURI_CONST_ASSIGN_PTR(\n                animation->frame_bubble_sequences[i], malloc(sizeof(FrameBubble)));\n        }\n\n        const FrameBubble* bubble = animation->frame_bubble_sequences[0];\n        int8_t index = -1;\n        for(;;) {\n            if(!flipper_format_read_int32(ff, \"Slot\", &current_slot, 1)) break;\n            if((current_slot != 0) && (index == -1)) break;\n\n            if(current_slot == index) {\n                FURI_CONST_ASSIGN_PTR(bubble->next_bubble, malloc(sizeof(FrameBubble)));\n                bubble = bubble->next_bubble;\n            } else if(current_slot == index + 1) {\n                ++index;\n                bubble = animation->frame_bubble_sequences[index];\n            } else {\n                /* slots have to start from 0, be ascending sorted, and\n                 * have exact number of slots as specified in \"Bubble slots\" */\n                break;\n            }\n            if(index >= animation->frame_bubble_sequences_count) break;\n\n            if(!flipper_format_read_uint32(ff, \"X\", &u32value, 1)) break;\n            FURI_CONST_ASSIGN(bubble->bubble.x, u32value);\n            if(!flipper_format_read_uint32(ff, \"Y\", &u32value, 1)) break;\n            FURI_CONST_ASSIGN(bubble->bubble.y, u32value);\n\n            if(!flipper_format_read_string(ff, \"Text\", str)) break;\n            if(furi_string_size(str) > 100) break;\n\n            furi_string_replace_all(str, \"\\\\n\", \"\\n\");\n\n            FURI_CONST_ASSIGN_PTR(bubble->bubble.text, strdup(furi_string_get_cstr(str)));\n\n            if(!flipper_format_read_string(ff, \"AlignH\", str)) break;\n            if(!animation_storage_cast_align(str, (Align*)&bubble->bubble.align_h)) break;\n            if(!flipper_format_read_string(ff, \"AlignV\", str)) break;\n            if(!animation_storage_cast_align(str, (Align*)&bubble->bubble.align_v)) break;\n\n            if(!flipper_format_read_uint32(ff, \"StartFrame\", &u32value, 1)) break;\n            FURI_CONST_ASSIGN(bubble->start_frame, u32value);\n            if(!flipper_format_read_uint32(ff, \"EndFrame\", &u32value, 1)) break;\n            FURI_CONST_ASSIGN(bubble->end_frame, u32value);\n        }\n        success = (index + 1) == animation->frame_bubble_sequences_count;\n    } while(0);\n\n    if(!success) {\n        if(animation->frame_bubble_sequences) {\n            FURI_LOG_E(TAG, \"Failed to load animation bubbles\");\n            animation_storage_free_bubbles(animation);\n        }\n    }\n\n    furi_string_free(str);\n    return success;\n}\n\nstatic BubbleAnimation* animation_storage_load_animation(const char* name) {\n    furi_assert(name);\n    BubbleAnimation* animation = malloc(sizeof(BubbleAnimation));\n\n    uint32_t height = 0;\n    uint32_t width = 0;\n    uint32_t* u32array = NULL;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_file_alloc(storage);\n    /* Forbid skipping fields */\n    flipper_format_set_strict_mode(ff, true);\n    FuriString* str;\n    str = furi_string_alloc();\n    animation->frame_bubble_sequences = NULL;\n\n    bool success = false;\n    do {\n        uint32_t u32value;\n\n        if(FSE_OK != storage_sd_status(storage)) break;\n\n        furi_string_printf(str, ANIMATION_DIR \"/%s/\" ANIMATION_META_FILE, name);\n        if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(str))) break;\n        if(!flipper_format_read_header(ff, str, &u32value)) break;\n        if(furi_string_cmp_str(str, \"Flipper Animation\")) break;\n\n        if(!flipper_format_read_uint32(ff, \"Width\", &width, 1)) break;\n        if(!flipper_format_read_uint32(ff, \"Height\", &height, 1)) break;\n\n        if(!flipper_format_read_uint32(ff, \"Passive frames\", &u32value, 1)) break;\n        animation->passive_frames = u32value;\n        if(!flipper_format_read_uint32(ff, \"Active frames\", &u32value, 1)) break;\n        animation->active_frames = u32value;\n\n        uint8_t frames = animation->passive_frames + animation->active_frames;\n        uint32_t count = 0;\n        if(!flipper_format_get_value_count(ff, \"Frames order\", &count)) break;\n        if(count != frames) {\n            FURI_LOG_E(TAG, \"Error loading animation: frames order\");\n            break;\n        }\n        u32array = malloc(sizeof(uint32_t) * frames);\n        if(!flipper_format_read_uint32(ff, \"Frames order\", u32array, frames)) break;\n        animation->frame_order = malloc(sizeof(uint8_t) * frames);\n        for(int i = 0; i < frames; ++i) {\n            FURI_CONST_ASSIGN(animation->frame_order[i], u32array[i]);\n        }\n\n        /* passive and active frames must be loaded up to this point */\n        if(!animation_storage_load_frames(storage, name, animation, u32array, width, height))\n            break;\n\n        if(!flipper_format_read_uint32(ff, \"Active cycles\", &u32value, 1)) break; //-V779\n        animation->active_cycles = u32value;\n        if(!flipper_format_read_uint32(ff, \"Frame rate\", &u32value, 1)) break;\n        FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value);\n        if(!flipper_format_read_uint32(ff, \"Duration\", &u32value, 1)) break;\n        animation->duration = u32value;\n        if(!flipper_format_read_uint32(ff, \"Active cooldown\", &u32value, 1)) break;\n        animation->active_cooldown = u32value;\n\n        if(!animation_storage_load_bubbles(animation, ff)) break;\n        success = true;\n    } while(0);\n\n    furi_string_free(str);\n    flipper_format_free(ff);\n    if(u32array) {\n        free(u32array);\n    }\n\n    if(!success) { //-V547\n        if(animation->frame_order) {\n            free((void*)animation->frame_order);\n        }\n        free(animation);\n        animation = NULL;\n    }\n\n    return animation;\n}\n\nstatic void animation_storage_free_bubbles(BubbleAnimation* animation) {\n    if(!animation->frame_bubble_sequences) return;\n\n    for(int i = 0; i < animation->frame_bubble_sequences_count;) {\n        const FrameBubble* const* bubble = &animation->frame_bubble_sequences[i];\n\n        if((*bubble) == NULL) break;\n\n        while((*bubble)->next_bubble != NULL) {\n            bubble = &(*bubble)->next_bubble;\n        }\n\n        if((*bubble)->bubble.text) {\n            free((void*)(*bubble)->bubble.text);\n        }\n        if((*bubble) == animation->frame_bubble_sequences[i]) {\n            ++i;\n        }\n        free((void*)*bubble);\n        FURI_CONST_ASSIGN_PTR(*bubble, NULL);\n    }\n    free((void*)animation->frame_bubble_sequences);\n    animation->frame_bubble_sequences = NULL;\n}\n"
  },
  {
    "path": "applications/services/desktop/animations/animation_storage.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <m-list.h>\n#include \"views/bubble_animation_view.h\"\n\n/** Main structure to handle animation data.\n * Contains all, including animation playing data (BubbleAnimation),\n * data for random animation selection (StorageAnimationMeta) and\n * flag of location internal/external */\ntypedef struct StorageAnimation StorageAnimation;\n\ntypedef struct {\n    const char* name;\n    uint8_t min_butthurt;\n    uint8_t max_butthurt;\n    uint8_t min_level;\n    uint8_t max_level;\n    uint8_t weight;\n} StorageAnimationManifestInfo;\n\n/** Container to return available animations list */\nLIST_DEF(StorageAnimationList, StorageAnimation*, M_PTR_OPLIST)\n#define M_OPL_StorageAnimationList_t() LIST_OPLIST(StorageAnimationList)\n\n/**\n * Fill list of available animations.\n * List will contain all idle animations on inner flash\n * and all available on SD-card, mentioned in manifest.txt.\n * Performs caching of animation. If fail - falls back to\n * inner animation.\n * List has to be initialized.\n *\n * @list        list to fill with animations data\n */\nvoid animation_storage_fill_animation_list(StorageAnimationList_t* list);\n\n/**\n * Get bubble animation of storage animation.\n * Bubble Animation is a structure which describes animation\n * independent of it's place of storage and meta data.\n * It contain all what is need to be played.\n * If storage_animation is not cached - caches it.\n *\n * @storage_animation       animation from which extract bubble animation\n * @return                  bubble_animation, NULL if failed to cache data.\n */\nconst BubbleAnimation* animation_storage_get_bubble_animation(StorageAnimation* storage_animation);\n\n/**\n * Performs caching animation data (Bubble Animation)\n * if this is not done yet.\n *\n * @storage_animation       animation to cache\n */\nvoid animation_storage_cache_animation(StorageAnimation* storage_animation);\n\n/**\n * Find animation by name.\n * Search through the inner flash, and SD-card if has.\n *\n * @name        name of animation\n * @return      found animation. NULL if nothing found.\n */\nStorageAnimation* animation_storage_find_animation(const char* name);\n\n/**\n * Get meta information of storage animation.\n * This information allows to randomly select animation.\n * Also it contains name. Never returns NULL.\n *\n * @storage_animation       item of whom we have to extract meta.\n * @return                  meta itself\n */\nStorageAnimationManifestInfo* animation_storage_get_meta(StorageAnimation* storage_animation);\n\n/**\n * Free storage_animation, which previously acquired\n * by Animation Storage.\n *\n * @storage_animation   item to free. NULL-ed after all.\n */\nvoid animation_storage_free_storage_animation(StorageAnimation** storage_animation);\n\n/**\n * Has to be called at least 1 time to initialize runtime structures\n * of animations in inner flash.\n */\nvoid animation_storage_initialize_internal_animations(void);\n"
  },
  {
    "path": "applications/services/desktop/animations/animation_storage_i.h",
    "content": "#pragma once\n#include \"animation_storage.h\"\n#include \"animation_manager.h\"\n\nstruct StorageAnimation {\n    const BubbleAnimation* animation;\n    bool external;\n    StorageAnimationManifestInfo manifest_info;\n};\n"
  },
  {
    "path": "applications/services/desktop/animations/views/bubble_animation_view.c",
    "content": "\n#include \"../animation_manager.h\"\n#include \"bubble_animation_view.h\"\n\n#include <furi_hal.h>\n#include <furi.h>\n#include <gui/canvas.h>\n#include <gui/elements.h>\n#include <gui/view.h>\n#include <gui/icon_i.h>\n#include <input/input.h>\n#include <stdint.h>\n#include <core/dangerous_defines.h>\n\n#define ACTIVE_SHIFT 2\n\ntypedef struct {\n    const BubbleAnimation* current;\n    const FrameBubble* current_bubble;\n    uint8_t current_frame;\n    uint8_t active_cycle;\n    uint8_t active_bubbles;\n    uint8_t passive_bubbles;\n    uint8_t active_shift;\n    uint32_t active_ended_at;\n    Icon* freeze_frame;\n} BubbleAnimationViewModel;\n\nstruct BubbleAnimationView {\n    View* view;\n    FuriTimer* timer;\n    BubbleAnimationInteractCallback interact_callback;\n    void* interact_callback_context;\n};\n\nstatic void bubble_animation_activate(BubbleAnimationView* view, bool force);\nstatic void bubble_animation_activate_right_now(BubbleAnimationView* view);\n\nstatic uint8_t bubble_animation_get_frame_index(BubbleAnimationViewModel* model) {\n    furi_assert(model);\n    uint8_t icon_index = 0;\n    const BubbleAnimation* animation = model->current;\n\n    if(model->current_frame < animation->passive_frames) {\n        icon_index = model->current_frame;\n    } else {\n        icon_index =\n            (model->current_frame - animation->passive_frames) % animation->active_frames +\n            animation->passive_frames;\n    }\n    furi_assert(icon_index < (animation->passive_frames + animation->active_frames));\n\n    return animation->frame_order[icon_index];\n}\n\nstatic void bubble_animation_draw_callback(Canvas* canvas, void* model_) {\n    furi_assert(model_);\n    furi_assert(canvas);\n\n    BubbleAnimationViewModel* model = model_;\n    const BubbleAnimation* animation = model->current;\n\n    if(model->freeze_frame) {\n        uint8_t y_offset = canvas_height(canvas) - icon_get_height(model->freeze_frame);\n        canvas_draw_icon(canvas, 0, y_offset, model->freeze_frame);\n        return;\n    }\n\n    if(!animation) {\n        return;\n    }\n\n    furi_assert(model->current_frame < 255);\n\n    uint8_t index = bubble_animation_get_frame_index(model);\n    uint8_t width = icon_get_width(&animation->icon_animation);\n    uint8_t height = icon_get_height(&animation->icon_animation);\n    uint8_t y_offset = canvas_height(canvas) - height;\n    canvas_draw_bitmap(\n        canvas, 0, y_offset, width, height, animation->icon_animation.frames[index]);\n\n    const FrameBubble* bubble = model->current_bubble;\n    if(bubble) {\n        if((model->current_frame >= bubble->start_frame) &&\n           (model->current_frame <= bubble->end_frame)) {\n            const Bubble* b = &bubble->bubble;\n            elements_bubble_str(canvas, b->x, b->y, b->text, b->align_h, b->align_v);\n        }\n    }\n}\n\nstatic const FrameBubble*\n    bubble_animation_pick_bubble(BubbleAnimationViewModel* model, bool active) {\n    const FrameBubble* bubble = NULL;\n\n    // Check for division by zero based on the active parameter\n    if((active && model->active_bubbles == 0) || (!active && model->passive_bubbles == 0)) {\n        return NULL;\n    }\n\n    uint8_t random_value = furi_hal_random_get();\n    // In case random generator return zero lets set it to 3\n    if(random_value == 0) {\n        random_value = 3;\n    }\n    uint8_t index = random_value % (active ? model->active_bubbles : model->passive_bubbles);\n    const BubbleAnimation* animation = model->current;\n\n    for(int i = 0; i < animation->frame_bubble_sequences_count; ++i) {\n        if((animation->frame_bubble_sequences[i]->start_frame < animation->passive_frames) ^\n           active) {\n            if(!index) {\n                bubble = animation->frame_bubble_sequences[i];\n            }\n            --index;\n        }\n    }\n\n    return bubble;\n}\n\nstatic bool bubble_animation_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    furi_assert(event);\n\n    BubbleAnimationView* animation_view = context;\n    bool consumed = false;\n\n    if(event->type == InputTypePress) {\n        bubble_animation_activate(animation_view, false);\n    }\n\n    if(event->key == InputKeyRight) {\n        /* Right button reserved for animation activation, so consume */\n        if(event->type == InputTypeShort) {\n            consumed = true;\n            if(animation_view->interact_callback) {\n                animation_view->interact_callback(animation_view->interact_callback_context);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nstatic void bubble_animation_activate(BubbleAnimationView* view, bool force) {\n    furi_assert(view);\n    bool activate = true;\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    if(model->current == NULL) {\n        activate = false;\n    } else if(model->freeze_frame) {\n        activate = false;\n    } else if(model->current->active_frames == 0) {\n        activate = false;\n    }\n\n    if(model->current != NULL) {\n        if(!force) {\n            if((model->active_ended_at + model->current->active_cooldown * 1000) >\n               furi_get_tick()) {\n                activate = false;\n            } else if(model->active_shift) {\n                activate = false;\n            } else if(model->current_frame >= model->current->passive_frames) {\n                activate = false;\n            }\n        }\n    }\n    view_commit_model(view->view, false);\n\n    if(!activate && !force) {\n        return;\n    }\n\n    if(ACTIVE_SHIFT > 0) {\n        BubbleAnimationViewModel* model = view_get_model(view->view);\n        model->active_shift = ACTIVE_SHIFT;\n        view_commit_model(view->view, false);\n    } else {\n        bubble_animation_activate_right_now(view);\n    }\n}\n\nstatic void bubble_animation_activate_right_now(BubbleAnimationView* view) {\n    furi_assert(view);\n\n    uint8_t frame_rate = 0;\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    if(model->current && (model->current->active_frames > 0) && (!model->freeze_frame)) {\n        model->current_frame = model->current->passive_frames;\n        model->current_bubble = bubble_animation_pick_bubble(model, true);\n        frame_rate = model->current->icon_animation.frame_rate;\n    }\n    view_commit_model(view->view, true);\n\n    if(frame_rate) {\n        furi_timer_start(view->timer, 1000 / frame_rate);\n    }\n}\n\nstatic void bubble_animation_next_frame(BubbleAnimationViewModel* model) {\n    furi_assert(model);\n\n    if(!model->current) {\n        return;\n    }\n\n    if(model->current_frame < model->current->passive_frames) {\n        model->current_frame = (model->current_frame + 1) % model->current->passive_frames;\n    } else {\n        ++model->current_frame;\n        model->active_cycle +=\n            !((model->current_frame - model->current->passive_frames) %\n              model->current->active_frames);\n        if(model->active_cycle >= model->current->active_cycles) {\n            // switch to passive\n            model->active_cycle = 0;\n            model->current_frame = 0;\n            model->current_bubble = bubble_animation_pick_bubble(model, false);\n            model->active_ended_at = furi_get_tick();\n        }\n\n        if(model->current_bubble) {\n            if(model->current_frame > model->current_bubble->end_frame) {\n                model->current_bubble = model->current_bubble->next_bubble;\n            }\n        }\n    }\n}\n\nstatic void bubble_animation_timer_callback(void* context) {\n    furi_assert(context);\n    BubbleAnimationView* view = context;\n    bool activate = false;\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n\n    if(model->active_shift > 0) {\n        activate = (--model->active_shift == 0);\n    }\n\n    if(!model->freeze_frame && !activate) {\n        bubble_animation_next_frame(model);\n    }\n\n    view_commit_model(view->view, !activate);\n\n    if(activate) {\n        bubble_animation_activate_right_now(view);\n    }\n}\n\n/* always freeze first passive frame, because\n * animation is always activated at unfreezing and played\n * passive frame first, and 2 frames after - active\n */\nstatic Icon* bubble_animation_clone_first_frame(const Icon* icon_orig) {\n    furi_assert(icon_orig);\n    furi_assert(icon_orig->frames);\n    furi_assert(icon_orig->frames[0]);\n\n    Icon* icon_clone = malloc(sizeof(Icon));\n    memcpy(icon_clone, icon_orig, sizeof(Icon));\n\n    icon_clone->frames = malloc(sizeof(uint8_t*));\n    /* icon bitmap can be either compressed or not. It is compressed if\n     * compressed size is less than original, so max size for bitmap is\n     * uncompressed (width * height) + 1 byte (in uncompressed case)\n     * for compressed header\n     */\n    size_t max_bitmap_size = ROUND_UP_TO(icon_orig->width, 8) * icon_orig->height + 1;\n    FURI_CONST_ASSIGN_PTR(icon_clone->frames[0], malloc(max_bitmap_size));\n    memcpy((void*)icon_clone->frames[0], icon_orig->frames[0], max_bitmap_size);\n    FURI_CONST_ASSIGN(icon_clone->frame_count, 1);\n\n    return icon_clone;\n}\n\nstatic void bubble_animation_release_frame(Icon** icon) {\n    furi_assert(icon);\n    furi_assert(*icon);\n\n    free((void*)(*icon)->frames[0]);\n    free((void*)(*icon)->frames);\n    free(*icon);\n    *icon = NULL;\n}\n\nstatic void bubble_animation_enter(void* context) {\n    furi_assert(context);\n    BubbleAnimationView* view = context;\n    bubble_animation_activate(view, false);\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    uint8_t frame_rate = 0;\n    if(model->current != NULL) {\n        frame_rate = model->current->icon_animation.frame_rate;\n    }\n    view_commit_model(view->view, false);\n\n    if(frame_rate) {\n        furi_timer_start(view->timer, 1000 / frame_rate);\n    }\n}\n\nstatic void bubble_animation_exit(void* context) {\n    furi_assert(context);\n    BubbleAnimationView* view = context;\n    furi_timer_stop(view->timer);\n}\n\nBubbleAnimationView* bubble_animation_view_alloc(void) {\n    BubbleAnimationView* view = malloc(sizeof(BubbleAnimationView));\n    view->view = view_alloc();\n    view->interact_callback = NULL;\n    view->timer = furi_timer_alloc(bubble_animation_timer_callback, FuriTimerTypePeriodic, view);\n\n    view_allocate_model(view->view, ViewModelTypeLocking, sizeof(BubbleAnimationViewModel));\n    view_set_context(view->view, view);\n    view_set_draw_callback(view->view, bubble_animation_draw_callback);\n    view_set_input_callback(view->view, bubble_animation_input_callback);\n    view_set_enter_callback(view->view, bubble_animation_enter);\n    view_set_exit_callback(view->view, bubble_animation_exit);\n\n    return view;\n}\n\nvoid bubble_animation_view_free(BubbleAnimationView* view) {\n    furi_assert(view);\n\n    view_set_draw_callback(view->view, NULL);\n    view_set_input_callback(view->view, NULL);\n    view_set_context(view->view, NULL);\n\n    view_free(view->view);\n    view->view = NULL;\n    free(view);\n}\n\nvoid bubble_animation_view_set_interact_callback(\n    BubbleAnimationView* view,\n    BubbleAnimationInteractCallback callback,\n    void* context) {\n    furi_assert(view);\n\n    view->interact_callback_context = context;\n    view->interact_callback = callback;\n}\n\nvoid bubble_animation_view_set_animation(\n    BubbleAnimationView* view,\n    const BubbleAnimation* new_animation) {\n    furi_assert(view);\n    furi_assert(new_animation);\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    furi_assert(model);\n    model->current = new_animation;\n\n    model->active_ended_at = furi_get_tick() - (model->current->active_cooldown * 1000);\n    model->active_bubbles = 0;\n    model->passive_bubbles = 0;\n    for(int i = 0; i < new_animation->frame_bubble_sequences_count; ++i) {\n        if(new_animation->frame_bubble_sequences[i]->start_frame < new_animation->passive_frames) {\n            ++model->passive_bubbles;\n        } else {\n            ++model->active_bubbles;\n        }\n    }\n\n    /* select bubble sequence */\n    model->current_bubble = bubble_animation_pick_bubble(model, false);\n    model->current_frame = 0;\n    model->active_cycle = 0;\n    view_commit_model(view->view, true);\n\n    furi_timer_start(view->timer, 1000 / new_animation->icon_animation.frame_rate);\n}\n\nvoid bubble_animation_freeze(BubbleAnimationView* view) {\n    furi_assert(view);\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    furi_assert(model->current);\n    furi_assert(!model->freeze_frame);\n    model->freeze_frame = bubble_animation_clone_first_frame(&model->current->icon_animation);\n    model->current = NULL;\n    view_commit_model(view->view, false);\n    furi_timer_stop(view->timer);\n}\n\nvoid bubble_animation_unfreeze(BubbleAnimationView* view) {\n    furi_assert(view);\n    uint8_t frame_rate;\n\n    BubbleAnimationViewModel* model = view_get_model(view->view);\n    furi_assert(model->freeze_frame);\n    bubble_animation_release_frame(&model->freeze_frame);\n    furi_assert(model->current);\n    frame_rate = model->current->icon_animation.frame_rate;\n    view_commit_model(view->view, true);\n\n    furi_timer_start(view->timer, 1000 / frame_rate);\n    bubble_animation_activate(view, false);\n}\n\nView* bubble_animation_get_view(BubbleAnimationView* view) {\n    furi_assert(view);\n\n    return view->view;\n}\n"
  },
  {
    "path": "applications/services/desktop/animations/views/bubble_animation_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"../animation_manager.h\"\n\n/** Bubble Animation instance */\ntypedef struct BubbleAnimationView BubbleAnimationView;\n\n/** Callback type to be called when interact button pressed */\ntypedef void (*BubbleAnimationInteractCallback)(void*);\n\n/**\n * Allocate bubble animation view.\n * This is animation with bubbles, and 2 phases:\n * active and passive.\n *\n * @return instance of new bubble animation\n */\nBubbleAnimationView* bubble_animation_view_alloc(void);\n\n/**\n * Free bubble animation view.\n *\n * @view        bubble animation view instance\n */\nvoid bubble_animation_view_free(BubbleAnimationView* view);\n\n/**\n * Set callback for interact action for animation.\n * Currently this is right button.\n *\n * @view        bubble animation view instance\n * @callback    callback to call when button pressed\n * @context     context\n */\nvoid bubble_animation_view_set_interact_callback(\n    BubbleAnimationView* view,\n    BubbleAnimationInteractCallback callback,\n    void* context);\n\n/**\n * Set new animation.\n * BubbleAnimation doesn't posses Bubble Animation object\n * so it doesn't handle any memory manipulation on Bubble Animations.\n *\n * @view                    bubble animation view instance\n * @new_bubble_animation    new animation to set\n */\nvoid bubble_animation_view_set_animation(\n    BubbleAnimationView* view,\n    const BubbleAnimation* new_bubble_animation);\n\n/**\n * Get view of bubble animation.\n *\n * @view        bubble animation view instance\n * @return      view\n */\nView* bubble_animation_get_view(BubbleAnimationView* view);\n\n/**\n * Freeze current playing animation. Saves a frame to be shown\n * during next unfreeze called.\n * bubble_animation_freeze() stops any reference to 'current' animation\n * so it can be freed. Therefore lock unfreeze should be preceeded with\n * new animation set.\n *\n * Freeze/Unfreeze usage example:\n *\n *  animation_view_alloc()\n *  set_animation()\n *  ...\n *  freeze_animation()\n *   // release animation\n *  ...\n *   // allocate animation\n *  set_animation()\n *  unfreeze()\n *\n * @view        bubble animation view instance\n */\nvoid bubble_animation_freeze(BubbleAnimationView* view);\n\n/**\n * Starts bubble animation after freezing.\n *\n * @view        bubble animation view instance\n */\nvoid bubble_animation_unfreeze(BubbleAnimationView* view);\n"
  },
  {
    "path": "applications/services/desktop/animations/views/one_shot_animation_view.c",
    "content": "\n#include \"one_shot_animation_view.h\"\n#include <furi.h>\n#include <gui/canvas.h>\n#include <gui/view.h>\n#include <gui/icon_i.h>\n#include <stdint.h>\n\ntypedef void (*OneShotInteractCallback)(void*);\n\nstruct OneShotView {\n    View* view;\n    FuriTimer* update_timer;\n    OneShotInteractCallback interact_callback;\n    void* interact_callback_context;\n};\n\ntypedef struct {\n    const Icon* icon;\n    uint32_t index;\n    bool block_input;\n} OneShotViewModel;\n\nstatic void one_shot_view_update_timer_callback(void* context) {\n    OneShotView* view = context;\n\n    OneShotViewModel* model = view_get_model(view->view);\n    if((model->index + 1) < model->icon->frame_count) {\n        ++model->index;\n    } else {\n        model->block_input = false;\n        model->index = model->icon->frame_count - 2;\n    }\n    view_commit_model(view->view, true);\n}\n\nstatic void one_shot_view_draw(Canvas* canvas, void* model_) {\n    furi_assert(canvas);\n    furi_assert(model_);\n\n    OneShotViewModel* model = model_;\n    furi_check(model->index < model->icon->frame_count);\n    uint8_t y_offset = canvas_height(canvas) - model->icon->height;\n    canvas_draw_bitmap(\n        canvas,\n        0,\n        y_offset,\n        model->icon->width,\n        model->icon->height,\n        model->icon->frames[model->index]);\n}\n\nstatic bool one_shot_view_input(InputEvent* event, void* context) {\n    furi_assert(context);\n    furi_assert(event);\n\n    OneShotView* view = context;\n    bool consumed = false;\n\n    OneShotViewModel* model = view_get_model(view->view);\n    consumed = model->block_input;\n    view_commit_model(view->view, false);\n\n    if(!consumed) {\n        if(event->key == InputKeyRight) {\n            /* Right button reserved for animation activation, so consume */\n            if(event->type == InputTypeShort) {\n                consumed = true;\n                if(view->interact_callback) {\n                    view->interact_callback(view->interact_callback_context);\n                }\n            }\n        }\n    }\n\n    return consumed;\n}\n\nOneShotView* one_shot_view_alloc(void) {\n    OneShotView* view = malloc(sizeof(OneShotView));\n    view->view = view_alloc();\n    view->update_timer =\n        furi_timer_alloc(one_shot_view_update_timer_callback, FuriTimerTypePeriodic, view);\n\n    view_allocate_model(view->view, ViewModelTypeLocking, sizeof(OneShotViewModel));\n    view_set_context(view->view, view);\n    view_set_draw_callback(view->view, one_shot_view_draw);\n    view_set_input_callback(view->view, one_shot_view_input);\n\n    return view;\n}\n\nvoid one_shot_view_free(OneShotView* view) {\n    furi_assert(view);\n\n    furi_timer_free(view->update_timer);\n    view_free(view->view);\n    view->view = NULL;\n    free(view);\n}\n\nvoid one_shot_view_set_interact_callback(\n    OneShotView* view,\n    OneShotInteractCallback callback,\n    void* context) {\n    furi_assert(view);\n\n    view->interact_callback_context = context;\n    view->interact_callback = callback;\n}\n\nvoid one_shot_view_start_animation(OneShotView* view, const Icon* icon) {\n    furi_assert(view);\n    furi_assert(icon);\n    furi_check(icon->frame_count >= 2);\n\n    OneShotViewModel* model = view_get_model(view->view);\n    model->index = 0;\n    model->icon = icon;\n    model->block_input = true;\n    view_commit_model(view->view, true);\n    furi_timer_start(view->update_timer, 1000 / model->icon->frame_rate);\n}\n\nView* one_shot_view_get_view(OneShotView* view) {\n    furi_assert(view);\n\n    return view->view;\n}\n"
  },
  {
    "path": "applications/services/desktop/animations/views/one_shot_animation_view.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <gui/view.h>\n#include <stdint.h>\n\ntypedef void (*OneShotInteractCallback)(void*);\ntypedef struct OneShotView OneShotView;\n\nOneShotView* one_shot_view_alloc(void);\nvoid one_shot_view_free(OneShotView* view);\nvoid one_shot_view_set_interact_callback(\n    OneShotView* view,\n    OneShotInteractCallback callback,\n    void* context);\nvoid one_shot_view_start_animation(OneShotView* view, const Icon* icon);\nView* one_shot_view_get_view(OneShotView* view);\n"
  },
  {
    "path": "applications/services/desktop/application.fam",
    "content": "App(\n    appid=\"desktop\",\n    name=\"DesktopSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"desktop_srv\",\n    cdefines=[\"SRV_DESKTOP\"],\n    requires=[\n        \"gui\",\n        \"dolphin\",\n        \"storage\",\n        \"input\",\n    ],\n    provides=[\"desktop_settings\"],\n    conflicts=[\"updater\"],\n    stack_size=2 * 1024,\n    order=60,\n)\n"
  },
  {
    "path": "applications/services/desktop/desktop.c",
    "content": "#include \"desktop_i.h\"\n\n#include <cli/cli_vcp.h>\n\n#include <gui/gui_i.h>\n\n#include <locale/locale.h>\n#include <storage/storage.h>\n\n#include <assets_icons.h>\n\n#include \"scenes/desktop_scene.h\"\n#include \"scenes/desktop_scene_locked.h\"\n\n#define TAG \"Desktop\"\n\nstatic void desktop_auto_lock_arm(Desktop*);\nstatic void desktop_auto_lock_inhibit(Desktop*);\nstatic void desktop_start_auto_lock_timer(Desktop*);\nstatic void desktop_apply_settings(Desktop*);\n\nstatic void desktop_loader_callback(const void* message, void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    const LoaderEvent* event = message;\n\n    if(event->type == LoaderEventTypeApplicationBeforeLoad) {\n        view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);\n        furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);\n    } else if(event->type == LoaderEventTypeNoMoreAppsInQueue) {\n        view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);\n    }\n}\n\nstatic void desktop_storage_callback(const void* message, void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalReloadSettings);\n    }\n}\n\nstatic void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {\n    UNUSED(context);\n    furi_assert(canvas);\n    canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8);\n}\n\nstatic void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) {\n    UNUSED(context);\n    furi_assert(canvas);\n    canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8);\n}\n\nstatic void desktop_clock_update(Desktop* desktop) {\n    furi_assert(desktop);\n\n    DateTime curr_dt;\n    furi_hal_rtc_get_datetime(&curr_dt);\n    bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h;\n\n    if(desktop->clock.hour != curr_dt.hour || desktop->clock.minute != curr_dt.minute ||\n       desktop->clock.format_12 != time_format_12) {\n        desktop->clock.format_12 = time_format_12;\n        desktop->clock.hour = curr_dt.hour;\n        desktop->clock.minute = curr_dt.minute;\n        view_port_update(desktop->clock_viewport);\n    }\n}\n\nstatic void desktop_clock_reconfigure(Desktop* desktop) {\n    furi_assert(desktop);\n\n    desktop_clock_update(desktop);\n\n    if(desktop->settings.display_clock) {\n        furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000));\n    } else {\n        furi_timer_stop(desktop->update_clock_timer);\n    }\n\n    view_port_enabled_set(desktop->clock_viewport, desktop->settings.display_clock);\n}\n\nstatic void desktop_clock_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    furi_assert(canvas);\n\n    Desktop* desktop = context;\n\n    canvas_set_font(canvas, FontPrimary);\n\n    uint8_t hour = desktop->clock.hour;\n    if(desktop->clock.format_12) {\n        if(hour > 12) {\n            hour -= 12;\n        }\n        if(hour == 0) {\n            hour = 12;\n        }\n    }\n\n    char buffer[20];\n    snprintf(buffer, sizeof(buffer), \"%02u:%02u\", hour, desktop->clock.minute);\n\n    view_port_set_width(\n        desktop->clock_viewport,\n        canvas_string_width(canvas, buffer) - 1 + (desktop->clock.minute % 10 == 1));\n\n    canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer);\n}\n\nstatic void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) {\n    UNUSED(context);\n    furi_assert(canvas);\n    canvas_draw_icon(canvas, 0, 0, &I_Muted_8x8);\n}\n\nstatic bool desktop_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    Desktop* desktop = (Desktop*)context;\n\n    if(event == DesktopGlobalBeforeAppStarted) {\n        if(animation_manager_is_animation_loaded(desktop->animation_manager)) {\n            animation_manager_unload_and_stall_animation(desktop->animation_manager);\n        }\n\n        desktop_auto_lock_inhibit(desktop);\n        desktop->app_running = true;\n\n        furi_semaphore_release(desktop->animation_semaphore);\n\n    } else if(event == DesktopGlobalAfterAppFinished) {\n        animation_manager_load_and_continue_animation(desktop->animation_manager);\n        desktop_auto_lock_arm(desktop);\n        desktop->app_running = false;\n\n    } else if(event == DesktopGlobalAutoLock) {\n        if(!desktop->app_running && !desktop->locked) {\n            desktop_lock(desktop);\n        }\n\n    } else if(event == DesktopGlobalSaveSettings) {\n        desktop_settings_save(&desktop->settings);\n        desktop_apply_settings(desktop);\n\n    } else if(event == DesktopGlobalReloadSettings) {\n        desktop_settings_load(&desktop->settings);\n        desktop_apply_settings(desktop);\n\n    } else {\n        return scene_manager_handle_custom_event(desktop->scene_manager, event);\n    }\n\n    return true;\n}\n\nstatic bool desktop_back_event_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = (Desktop*)context;\n    return scene_manager_handle_back_event(desktop->scene_manager);\n}\n\nstatic void desktop_tick_event_callback(void* context) {\n    furi_assert(context);\n    Desktop* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nstatic void desktop_input_event_callback(const void* value, void* context) {\n    furi_assert(value);\n    furi_assert(context);\n    const InputEvent* event = value;\n    Desktop* desktop = context;\n    if(event->type == InputTypePress) {\n        desktop_start_auto_lock_timer(desktop);\n    }\n}\n\nstatic void desktop_auto_lock_timer_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAutoLock);\n}\n\nstatic void desktop_start_auto_lock_timer(Desktop* desktop) {\n    furi_timer_start(\n        desktop->auto_lock_timer, furi_ms_to_ticks(desktop->settings.auto_lock_delay_ms));\n}\n\nstatic void desktop_stop_auto_lock_timer(Desktop* desktop) {\n    furi_timer_stop(desktop->auto_lock_timer);\n}\n\nstatic void desktop_auto_lock_arm(Desktop* desktop) {\n    if(desktop->settings.auto_lock_delay_ms) {\n        desktop->input_events_subscription = furi_pubsub_subscribe(\n            desktop->input_events_pubsub, desktop_input_event_callback, desktop);\n        desktop_start_auto_lock_timer(desktop);\n    }\n}\n\nstatic void desktop_auto_lock_inhibit(Desktop* desktop) {\n    desktop_stop_auto_lock_timer(desktop);\n    if(desktop->input_events_subscription) {\n        furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription);\n        desktop->input_events_subscription = NULL;\n    }\n}\n\nstatic void desktop_clock_timer_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n\n    const bool clock_enabled = gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6;\n\n    if(clock_enabled) {\n        desktop_clock_update(desktop);\n    }\n\n    view_port_enabled_set(desktop->clock_viewport, clock_enabled);\n}\n\nstatic void desktop_apply_settings(Desktop* desktop) {\n    desktop->in_transition = true;\n\n    desktop_clock_reconfigure(desktop);\n\n    view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode);\n    desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode);\n    animation_manager_set_dummy_mode_state(\n        desktop->animation_manager, desktop->settings.dummy_mode);\n\n    if(!desktop->app_running && !desktop->locked) {\n        desktop_auto_lock_arm(desktop);\n    }\n\n    desktop->in_transition = false;\n}\n\nstatic void desktop_init_settings(Desktop* desktop) {\n    furi_pubsub_subscribe(storage_get_pubsub(desktop->storage), desktop_storage_callback, desktop);\n\n    if(storage_sd_status(desktop->storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping settings\");\n        return;\n    }\n\n    desktop_settings_load(&desktop->settings);\n    desktop_apply_settings(desktop);\n}\n\nstatic Desktop* desktop_alloc(void) {\n    Desktop* desktop = malloc(sizeof(Desktop));\n\n    desktop->animation_semaphore = furi_semaphore_alloc(1, 0);\n    desktop->animation_manager = animation_manager_alloc();\n    desktop->gui = furi_record_open(RECORD_GUI);\n    desktop->scene_thread = furi_thread_alloc();\n    desktop->view_dispatcher = view_dispatcher_alloc();\n    desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop);\n\n    view_dispatcher_attach_to_gui(\n        desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop);\n    view_dispatcher_set_tick_event_callback(\n        desktop->view_dispatcher, desktop_tick_event_callback, 500);\n\n    view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop);\n    view_dispatcher_set_custom_event_callback(\n        desktop->view_dispatcher, desktop_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        desktop->view_dispatcher, desktop_back_event_callback);\n\n    desktop->lock_menu = desktop_lock_menu_alloc();\n    desktop->debug_view = desktop_debug_alloc();\n    desktop->popup = popup_alloc();\n    desktop->locked_view = desktop_view_locked_alloc();\n    desktop->pin_input_view = desktop_view_pin_input_alloc();\n    desktop->pin_timeout_view = desktop_view_pin_timeout_alloc();\n    desktop->slideshow_view = desktop_view_slideshow_alloc();\n\n    desktop->main_view_stack = view_stack_alloc();\n    desktop->main_view = desktop_main_alloc();\n    View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager);\n    view_stack_add_view(desktop->main_view_stack, desktop_main_get_view(desktop->main_view));\n    view_stack_add_view(desktop->main_view_stack, dolphin_view);\n    view_stack_add_view(\n        desktop->main_view_stack, desktop_view_locked_get_view(desktop->locked_view));\n\n    /* locked view (as animation view) attends in 2 scenes: main & locked,\n     * because it has to draw \"Unlocked\" label on main scene */\n    desktop->locked_view_stack = view_stack_alloc();\n    view_stack_add_view(desktop->locked_view_stack, dolphin_view);\n    view_stack_add_view(\n        desktop->locked_view_stack, desktop_view_locked_get_view(desktop->locked_view));\n\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdMain,\n        view_stack_get_view(desktop->main_view_stack));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdLocked,\n        view_stack_get_view(desktop->locked_view_stack));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdLockMenu,\n        desktop_lock_menu_get_view(desktop->lock_menu));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher, DesktopViewIdPopup, popup_get_view(desktop->popup));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdPinTimeout,\n        desktop_view_pin_timeout_get_view(desktop->pin_timeout_view));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdPinInput,\n        desktop_view_pin_input_get_view(desktop->pin_input_view));\n    view_dispatcher_add_view(\n        desktop->view_dispatcher,\n        DesktopViewIdSlideshow,\n        desktop_view_slideshow_get_view(desktop->slideshow_view));\n\n    // Lock icon\n    desktop->lock_icon_viewport = view_port_alloc();\n    view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8));\n    view_port_draw_callback_set(\n        desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);\n    view_port_enabled_set(desktop->lock_icon_viewport, false);\n    gui_add_view_port(desktop->gui, desktop->lock_icon_viewport, GuiLayerStatusBarLeft);\n\n    // Dummy mode icon\n    desktop->dummy_mode_icon_viewport = view_port_alloc();\n    view_port_set_width(desktop->dummy_mode_icon_viewport, icon_get_width(&I_GameMode_11x8));\n    view_port_draw_callback_set(\n        desktop->dummy_mode_icon_viewport, desktop_dummy_mode_icon_draw_callback, desktop);\n    view_port_enabled_set(desktop->dummy_mode_icon_viewport, false);\n    gui_add_view_port(desktop->gui, desktop->dummy_mode_icon_viewport, GuiLayerStatusBarLeft);\n\n    // Clock\n    desktop->clock_viewport = view_port_alloc();\n    view_port_set_width(desktop->clock_viewport, 25);\n    view_port_draw_callback_set(desktop->clock_viewport, desktop_clock_draw_callback, desktop);\n    view_port_enabled_set(desktop->clock_viewport, false);\n    gui_add_view_port(desktop->gui, desktop->clock_viewport, GuiLayerStatusBarRight);\n\n    // Stealth mode icon\n    desktop->stealth_mode_icon_viewport = view_port_alloc();\n    view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8));\n    view_port_draw_callback_set(\n        desktop->stealth_mode_icon_viewport, desktop_stealth_mode_icon_draw_callback, desktop);\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {\n        view_port_enabled_set(desktop->stealth_mode_icon_viewport, true);\n    } else {\n        view_port_enabled_set(desktop->stealth_mode_icon_viewport, false);\n    }\n    gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft);\n\n    // Unload animations before starting an application\n    desktop->loader = furi_record_open(RECORD_LOADER);\n    furi_pubsub_subscribe(loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop);\n\n    desktop->storage = furi_record_open(RECORD_STORAGE);\n    desktop->notification = furi_record_open(RECORD_NOTIFICATION);\n    desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS);\n\n    desktop->auto_lock_timer =\n        furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop);\n\n    desktop->status_pubsub = furi_pubsub_alloc();\n\n    desktop->update_clock_timer =\n        furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop);\n\n    desktop->app_running = loader_is_locked(desktop->loader);\n\n    furi_record_create(RECORD_DESKTOP, desktop);\n\n    return desktop;\n}\n\n/*\n * Private API\n */\n\nvoid desktop_lock(Desktop* desktop) {\n    furi_assert(!desktop->locked);\n\n    furi_hal_rtc_set_flag(FuriHalRtcFlagLock);\n\n    if(desktop_pin_code_is_set()) {\n        CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);\n        cli_vcp_disable(cli_vcp);\n        furi_record_close(RECORD_CLI_VCP);\n    }\n\n    desktop_auto_lock_inhibit(desktop);\n    scene_manager_set_scene_state(\n        desktop->scene_manager, DesktopSceneLocked, DesktopSceneLockedStateFirstEnter);\n    scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);\n\n    DesktopStatus status = {.locked = true};\n    furi_pubsub_publish(desktop->status_pubsub, &status);\n\n    desktop->locked = true;\n}\n\nvoid desktop_unlock(Desktop* desktop) {\n    furi_assert(desktop->locked);\n\n    view_port_enabled_set(desktop->lock_icon_viewport, false);\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_set_lockdown(gui, false);\n    furi_record_close(RECORD_GUI);\n    desktop_view_locked_unlock(desktop->locked_view);\n    scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);\n    desktop_auto_lock_arm(desktop);\n    furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);\n    furi_hal_rtc_set_pin_fails(0);\n\n    if(desktop_pin_code_is_set()) {\n        CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);\n        cli_vcp_enable(cli_vcp);\n        furi_record_close(RECORD_CLI_VCP);\n    }\n\n    DesktopStatus status = {.locked = false};\n    furi_pubsub_publish(desktop->status_pubsub, &status);\n\n    desktop->locked = false;\n}\n\nvoid desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {\n    desktop->in_transition = true;\n\n    view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled);\n    desktop_main_set_dummy_mode_state(desktop->main_view, enabled);\n    animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled);\n    desktop->settings.dummy_mode = enabled;\n\n    desktop->in_transition = false;\n\n    desktop_settings_save(&desktop->settings);\n}\n\nvoid desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {\n    desktop->in_transition = true;\n\n    if(enabled) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode);\n    } else {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode);\n    }\n\n    view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled);\n\n    desktop->in_transition = false;\n}\n\n/*\n *  Public API\n */\n\nbool desktop_api_is_locked(Desktop* instance) {\n    furi_assert(instance);\n    return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);\n}\n\nvoid desktop_api_unlock(Desktop* instance) {\n    furi_assert(instance);\n    view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalApiUnlock);\n}\n\nFuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) {\n    furi_assert(instance);\n    return instance->status_pubsub;\n}\n\nvoid desktop_api_reload_settings(Desktop* instance) {\n    furi_assert(instance);\n    view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalReloadSettings);\n}\n\nvoid desktop_api_get_settings(Desktop* instance, DesktopSettings* settings) {\n    furi_assert(instance);\n    furi_assert(settings);\n\n    *settings = instance->settings;\n}\n\nvoid desktop_api_set_settings(Desktop* instance, const DesktopSettings* settings) {\n    furi_assert(instance);\n    furi_assert(settings);\n\n    instance->settings = *settings;\n    view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalSaveSettings);\n}\n\n/*\n * Application thread\n */\n\nint32_t desktop_srv(void* p) {\n    UNUSED(p);\n\n    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {\n        FURI_LOG_W(TAG, \"Skipping start in special boot mode\");\n\n        furi_thread_suspend(furi_thread_get_current_id());\n        return 0;\n    }\n\n    Desktop* desktop = desktop_alloc();\n\n    desktop_init_settings(desktop);\n\n    scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);\n\n    if(desktop_pin_code_is_set()) {\n        desktop_lock(desktop);\n    } else {\n        CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);\n        cli_vcp_enable(cli_vcp);\n        furi_record_close(RECORD_CLI_VCP);\n    }\n\n    if(storage_file_exists(desktop->storage, SLIDESHOW_FS_PATH)) {\n        scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow);\n    }\n\n    if(!furi_hal_version_do_i_belong_here()) {\n        scene_manager_next_scene(desktop->scene_manager, DesktopSceneHwMismatch);\n    }\n\n    if(furi_hal_rtc_get_fault_data()) {\n        scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault);\n    }\n\n    uint8_t keys_total, keys_valid;\n    if(!furi_hal_crypto_enclave_verify(&keys_total, &keys_valid)) {\n        FURI_LOG_E(\n            TAG,\n            \"Secure Enclave verification failed: total %hhu, valid %hhu\",\n            keys_total,\n            keys_valid);\n\n        scene_manager_next_scene(desktop->scene_manager, DesktopSceneSecureEnclave);\n    }\n\n    // Special case: autostart application is already running\n    if(desktop->app_running && animation_manager_is_animation_loaded(desktop->animation_manager)) {\n        animation_manager_unload_and_stall_animation(desktop->animation_manager);\n    }\n\n    view_dispatcher_run(desktop->view_dispatcher);\n\n    // Should never get here (a service thread will crash automatically if it returns)\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/desktop/desktop.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include \"desktop_settings.h\"\n\n#define RECORD_DESKTOP \"desktop\"\n\ntypedef struct Desktop Desktop;\n\ntypedef struct {\n    bool locked;\n} DesktopStatus;\n\nbool desktop_api_is_locked(Desktop* instance);\n\nvoid desktop_api_unlock(Desktop* instance);\n\nFuriPubSub* desktop_api_get_status_pubsub(Desktop* instance);\n\nvoid desktop_api_get_settings(Desktop* instance, DesktopSettings* settings);\n\nvoid desktop_api_set_settings(Desktop* instance, const DesktopSettings* settings);\n"
  },
  {
    "path": "applications/services/desktop/desktop_i.h",
    "content": "#pragma once\n\n#include \"desktop.h\"\n#include \"desktop_settings.h\"\n\n#include \"animations/animation_manager.h\"\n#include \"views/desktop_view_pin_timeout.h\"\n#include \"views/desktop_view_pin_input.h\"\n#include \"views/desktop_view_locked.h\"\n#include \"views/desktop_view_main.h\"\n#include \"views/desktop_view_lock_menu.h\"\n#include \"views/desktop_view_debug.h\"\n#include \"views/desktop_view_slideshow.h\"\n\n#include <gui/gui.h>\n#include <gui/view_stack.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/popup.h>\n#include <gui/scene_manager.h>\n\n#include <loader/loader.h>\n#include <notification/notification_app.h>\n\n#define STATUS_BAR_Y_SHIFT 13\n\ntypedef enum {\n    DesktopViewIdMain,\n    DesktopViewIdLockMenu,\n    DesktopViewIdLocked,\n    DesktopViewIdDebug,\n    DesktopViewIdPopup,\n    DesktopViewIdPinInput,\n    DesktopViewIdPinTimeout,\n    DesktopViewIdSlideshow,\n    DesktopViewIdTotal,\n} DesktopViewId;\n\ntypedef struct {\n    uint8_t hour;\n    uint8_t minute;\n    bool format_12; // 1 - 12 hour, 0 - 24H\n} DesktopClock;\n\nstruct Desktop {\n    FuriThread* scene_thread;\n\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n\n    Popup* popup;\n    DesktopLockMenuView* lock_menu;\n    DesktopDebugView* debug_view;\n    DesktopViewLocked* locked_view;\n    DesktopMainView* main_view;\n    DesktopViewPinTimeout* pin_timeout_view;\n    DesktopSlideshowView* slideshow_view;\n    DesktopViewPinInput* pin_input_view;\n\n    ViewStack* main_view_stack;\n    ViewStack* locked_view_stack;\n\n    ViewPort* lock_icon_viewport;\n    ViewPort* dummy_mode_icon_viewport;\n    ViewPort* clock_viewport;\n    ViewPort* stealth_mode_icon_viewport;\n\n    Loader* loader;\n    Storage* storage;\n    NotificationApp* notification;\n\n    FuriPubSub* status_pubsub;\n    FuriPubSub* input_events_pubsub;\n    FuriPubSubSubscription* input_events_subscription;\n\n    FuriTimer* auto_lock_timer;\n    FuriTimer* update_clock_timer;\n\n    AnimationManager* animation_manager;\n    FuriSemaphore* animation_semaphore;\n\n    DesktopClock clock;\n    DesktopSettings settings;\n\n    bool in_transition;\n    bool app_running;\n    bool locked;\n};\n\nvoid desktop_lock(Desktop* desktop);\nvoid desktop_unlock(Desktop* desktop);\nvoid desktop_set_dummy_mode_state(Desktop* desktop, bool enabled);\nvoid desktop_set_stealth_mode_state(Desktop* desktop, bool enabled);\n"
  },
  {
    "path": "applications/services/desktop/desktop_settings.c",
    "content": "#include \"desktop_settings.h\"\n#include \"desktop_settings_filename.h\"\n\n#include <saved_struct.h>\n#include <storage/storage.h>\n\n#define TAG \"DesktopSettings\"\n\n#define DESKTOP_SETTINGS_VER_10 (10)\n#define DESKTOP_SETTINGS_VER    (11)\n\n#define DESKTOP_SETTINGS_PATH  INT_PATH(DESKTOP_SETTINGS_FILE_NAME)\n#define DESKTOP_SETTINGS_MAGIC (0x17)\n\ntypedef struct {\n    uint8_t reserved[11];\n    DesktopSettings settings;\n} DesktopSettingsV10;\n\n// Actual size of DesktopSettings v10\nstatic_assert(sizeof(DesktopSettingsV10) == 1044);\n\nvoid desktop_settings_load(DesktopSettings* settings) {\n    furi_assert(settings);\n\n    bool success = false;\n\n    do {\n        uint8_t version;\n        if(!saved_struct_get_metadata(DESKTOP_SETTINGS_PATH, NULL, &version, NULL)) break;\n\n        if(version == DESKTOP_SETTINGS_VER) {\n            success = saved_struct_load(\n                DESKTOP_SETTINGS_PATH,\n                settings,\n                sizeof(DesktopSettings),\n                DESKTOP_SETTINGS_MAGIC,\n                DESKTOP_SETTINGS_VER);\n\n        } else if(version == DESKTOP_SETTINGS_VER_10) {\n            DesktopSettingsV10* settings_v10 = malloc(sizeof(DesktopSettingsV10));\n\n            success = saved_struct_load(\n                DESKTOP_SETTINGS_PATH,\n                settings_v10,\n                sizeof(DesktopSettingsV10),\n                DESKTOP_SETTINGS_MAGIC,\n                DESKTOP_SETTINGS_VER_10);\n\n            if(success) {\n                *settings = settings_v10->settings;\n            }\n\n            free(settings_v10);\n        }\n\n    } while(false);\n\n    if(!success) {\n        FURI_LOG_W(TAG, \"Failed to load file, using defaults\");\n        memset(settings, 0, sizeof(DesktopSettings));\n        desktop_settings_save(settings);\n    }\n}\n\nvoid desktop_settings_save(const DesktopSettings* settings) {\n    furi_assert(settings);\n\n    const bool success = saved_struct_save(\n        DESKTOP_SETTINGS_PATH,\n        settings,\n        sizeof(DesktopSettings),\n        DESKTOP_SETTINGS_MAGIC,\n        DESKTOP_SETTINGS_VER);\n\n    if(!success) {\n        FURI_LOG_E(TAG, \"Failed to save file\");\n    }\n}\n"
  },
  {
    "path": "applications/services/desktop/desktop_settings.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\ntypedef enum {\n    FavoriteAppLeftShort,\n    FavoriteAppLeftLong,\n    FavoriteAppRightShort,\n    FavoriteAppRightLong,\n    FavoriteAppNumber,\n} FavoriteAppShortcut;\n\ntypedef enum {\n    DummyAppLeft,\n    DummyAppRight,\n    DummyAppDown,\n    DummyAppOk,\n    DummyAppNumber,\n} DummyAppShortcut;\n\ntypedef struct {\n    char name_or_path[128];\n} FavoriteApp;\n\ntypedef struct {\n    uint32_t auto_lock_delay_ms;\n    uint8_t dummy_mode;\n    uint8_t display_clock;\n    FavoriteApp favorite_apps[FavoriteAppNumber];\n    FavoriteApp dummy_apps[DummyAppNumber];\n} DesktopSettings;\n\nvoid desktop_settings_load(DesktopSettings* settings);\nvoid desktop_settings_save(const DesktopSettings* settings);\n"
  },
  {
    "path": "applications/services/desktop/desktop_settings_filename.h",
    "content": "#pragma once\n\n#define DESKTOP_SETTINGS_FILE_NAME \".desktop.settings\"\n"
  },
  {
    "path": "applications/services/desktop/helpers/pin_code.c",
    "content": "#include \"pin_code.h\"\n\n#include <furi_hal_rtc.h>\n\n#include <furi.h>\n#include <notification/notification_messages.h>\n\n#define DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH (2)\n#define DESKTOP_PIN_CODE_LENGTH_OFFSET   (28)\n\nstatic const NotificationSequence sequence_pin_fail = {\n    &message_display_backlight_on,\n\n    &message_red_255,\n    &message_vibro_on,\n    &message_delay_100,\n    &message_vibro_off,\n    &message_red_0,\n\n    &message_delay_250,\n\n    &message_red_255,\n    &message_vibro_on,\n    &message_delay_100,\n    &message_vibro_off,\n    &message_red_0,\n    NULL,\n};\n\nstatic const uint8_t desktop_helpers_fails_timeout[] = {\n    0,\n    0,\n    0,\n    0,\n    30,\n    60,\n    90,\n    120,\n    150,\n    180,\n    /* +60 for every next fail */\n};\n\nstatic uint32_t desktop_pin_code_pack(const DesktopPinCode* pin_code) {\n    furi_check(pin_code);\n    furi_check(pin_code->length <= sizeof(pin_code->data));\n\n    uint32_t reg_value = 0;\n\n    for(uint8_t i = 0; i < pin_code->length; ++i) {\n        furi_check(pin_code->data[i] < (1 << DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH));\n        reg_value |= (uint32_t)pin_code->data[i] << (i * DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH);\n    }\n\n    reg_value |= (uint32_t)pin_code->length << DESKTOP_PIN_CODE_LENGTH_OFFSET;\n\n    return reg_value;\n}\n\nbool desktop_pin_code_is_set(void) {\n    uint8_t length = furi_hal_rtc_get_pin_value() >> DESKTOP_PIN_CODE_LENGTH_OFFSET;\n    return length >= DESKTOP_PIN_CODE_MIN_LEN && length <= DESKTOP_PIN_CODE_MAX_LEN;\n}\n\nvoid desktop_pin_code_set(const DesktopPinCode* pin_code) {\n    furi_hal_rtc_set_pin_value(desktop_pin_code_pack(pin_code));\n}\n\nvoid desktop_pin_code_reset(void) {\n    furi_hal_rtc_set_pin_value(0);\n}\n\nbool desktop_pin_code_check(const DesktopPinCode* pin_code) {\n    return furi_hal_rtc_get_pin_value() == desktop_pin_code_pack(pin_code);\n}\n\nbool desktop_pin_code_is_equal(const DesktopPinCode* pin_code1, const DesktopPinCode* pin_code2) {\n    furi_check(pin_code1);\n    furi_check(pin_code1->length <= sizeof(pin_code1->data));\n    furi_check(pin_code2);\n    furi_check(pin_code2->length <= sizeof(pin_code2->data));\n\n    return pin_code1->length == pin_code2->length &&\n           memcmp(pin_code1->data, pin_code2->data, pin_code1->length) == 0;\n}\n\nvoid desktop_pin_lock_error_notify(void) {\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_pin_fail);\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nuint32_t desktop_pin_lock_get_fail_timeout(void) {\n    uint32_t pin_fails = furi_hal_rtc_get_pin_fails();\n    uint32_t pin_timeout = 0;\n    uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;\n    if(pin_fails <= max_index) {\n        pin_timeout = desktop_helpers_fails_timeout[pin_fails];\n    } else {\n        pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;\n    }\n\n    return pin_timeout;\n}\n"
  },
  {
    "path": "applications/services/desktop/helpers/pin_code.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#define DESKTOP_PIN_CODE_MIN_LEN (4)\n#define DESKTOP_PIN_CODE_MAX_LEN (10)\n\ntypedef struct {\n    uint8_t data[DESKTOP_PIN_CODE_MAX_LEN];\n    uint8_t length;\n} DesktopPinCode;\n\nbool desktop_pin_code_is_set(void);\n\nvoid desktop_pin_code_set(const DesktopPinCode* pin_code);\n\nvoid desktop_pin_code_reset(void);\n\nbool desktop_pin_code_check(const DesktopPinCode* pin_code);\n\nbool desktop_pin_code_is_equal(const DesktopPinCode* pin_code1, const DesktopPinCode* pin_code2);\n\nvoid desktop_pin_lock_error_notify(void);\n\nuint32_t desktop_pin_lock_get_fail_timeout(void);\n"
  },
  {
    "path": "applications/services/desktop/helpers/slideshow.c",
    "content": "#include \"slideshow.h\"\n\n#include <storage/storage.h>\n#include <gui/icon.h>\n#include <gui/icon_i.h>\n#include <core/dangerous_defines.h>\n\n#define SLIDESHOW_MAGIC                 0x72676468\n#define SLIDESHOW_MAX_SUPPORTED_VERSION 1\n\nstruct Slideshow {\n    Icon icon;\n    uint32_t current_frame;\n    bool loaded;\n};\n\n#pragma pack(push, 1)\n\ntypedef struct {\n    uint32_t magic;\n    uint8_t version;\n    uint8_t width;\n    uint8_t height;\n    uint8_t frame_count;\n} SlideshowFileHeader;\n_Static_assert(sizeof(SlideshowFileHeader) == 8, \"Incorrect SlideshowFileHeader size\");\n\ntypedef struct {\n    uint16_t size;\n} SlideshowFrameHeader;\n_Static_assert(sizeof(SlideshowFrameHeader) == 2, \"Incorrect SlideshowFrameHeader size\");\n\n#pragma pack(pop)\n\nSlideshow* slideshow_alloc(void) {\n    Slideshow* ret = malloc(sizeof(Slideshow));\n    ret->loaded = false;\n    return ret;\n}\n\nvoid slideshow_free(Slideshow* slideshow) {\n    Icon* icon = &slideshow->icon;\n    if(icon) { //-V547\n        for(int frame_idx = 0; frame_idx < icon->frame_count; ++frame_idx) {\n            uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx];\n            free(frame_data);\n        }\n        free((uint8_t**)icon->frames);\n    }\n    free(slideshow);\n}\n\nbool slideshow_load(Slideshow* slideshow, const char* fspath) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* slideshow_file = storage_file_alloc(storage);\n    slideshow->loaded = false;\n    do {\n        if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) {\n            break;\n        }\n        SlideshowFileHeader header;\n        if((storage_file_read(slideshow_file, &header, sizeof(header)) != sizeof(header)) ||\n           (header.magic != SLIDESHOW_MAGIC) ||\n           (header.version > SLIDESHOW_MAX_SUPPORTED_VERSION)) {\n            break;\n        }\n        Icon* icon = &slideshow->icon;\n        FURI_CONST_ASSIGN(icon->frame_count, header.frame_count);\n        FURI_CONST_ASSIGN(icon->width, header.width);\n        FURI_CONST_ASSIGN(icon->height, header.height);\n        icon->frames = malloc(header.frame_count * sizeof(uint8_t*));\n        for(int frame_idx = 0; frame_idx < header.frame_count; ++frame_idx) {\n            SlideshowFrameHeader frame_header;\n            if(storage_file_read(slideshow_file, &frame_header, sizeof(frame_header)) !=\n               sizeof(frame_header)) {\n                break;\n            }\n            FURI_CONST_ASSIGN_PTR(icon->frames[frame_idx], malloc(frame_header.size));\n            uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx];\n            if(storage_file_read(slideshow_file, frame_data, frame_header.size) !=\n               frame_header.size) {\n                break;\n            }\n            slideshow->loaded = (frame_idx + 1) == header.frame_count;\n        }\n    } while(false);\n    storage_file_free(slideshow_file);\n    furi_record_close(RECORD_STORAGE);\n    return slideshow->loaded;\n}\n\nbool slideshow_is_loaded(Slideshow* slideshow) {\n    return slideshow->loaded;\n}\n\nbool slideshow_is_one_page(Slideshow* slideshow) {\n    return slideshow->loaded && (slideshow->icon.frame_count == 1);\n}\n\nbool slideshow_advance(Slideshow* slideshow) {\n    uint8_t next_frame = slideshow->current_frame + 1;\n    if(next_frame < slideshow->icon.frame_count) {\n        slideshow->current_frame = next_frame;\n        return true;\n    }\n    return false;\n}\n\nvoid slideshow_goback(Slideshow* slideshow) {\n    if(slideshow->current_frame > 0) {\n        slideshow->current_frame--;\n    }\n}\n\nvoid slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y) {\n    furi_assert(slideshow->current_frame < slideshow->icon.frame_count);\n    canvas_draw_bitmap(\n        canvas,\n        x,\n        y,\n        slideshow->icon.width,\n        slideshow->icon.height,\n        slideshow->icon.frames[slideshow->current_frame]);\n}\n"
  },
  {
    "path": "applications/services/desktop/helpers/slideshow.h",
    "content": "#pragma once\n\n#include <gui/canvas.h>\n\ntypedef struct Slideshow Slideshow;\n\nSlideshow* slideshow_alloc(void);\n\nvoid slideshow_free(Slideshow* slideshow);\nbool slideshow_load(Slideshow* slideshow, const char* fspath);\nbool slideshow_is_loaded(Slideshow* slideshow);\nbool slideshow_is_one_page(Slideshow* slideshow);\nvoid slideshow_goback(Slideshow* slideshow);\nbool slideshow_advance(Slideshow* slideshow);\nvoid slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y);\n"
  },
  {
    "path": "applications/services/desktop/helpers/slideshow_filename.h",
    "content": "#pragma once\n\n#define SLIDESHOW_FILE_NAME \".slideshow\"\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene.c",
    "content": "#include \"desktop_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const desktop_on_enter_handlers[])(void*) = {\n#include \"desktop_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const desktop_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"desktop_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const desktop_on_exit_handlers[])(void* context) = {\n#include \"desktop_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers desktop_scene_handlers = {\n    .on_enter_handlers = desktop_on_enter_handlers,\n    .on_event_handlers = desktop_on_event_handlers,\n    .on_exit_handlers = desktop_on_exit_handlers,\n    .scene_num = DesktopSceneNum,\n};\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) DesktopScene##id,\ntypedef enum {\n#include \"desktop_scene_config.h\"\n    DesktopSceneNum,\n} DesktopScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers desktop_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"desktop_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"desktop_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"desktop_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_config.h",
    "content": "ADD_SCENE(desktop, main, Main)\nADD_SCENE(desktop, lock_menu, LockMenu)\nADD_SCENE(desktop, debug, Debug)\nADD_SCENE(desktop, hw_mismatch, HwMismatch)\nADD_SCENE(desktop, fault, Fault)\nADD_SCENE(desktop, locked, Locked)\nADD_SCENE(desktop, pin_input, PinInput)\nADD_SCENE(desktop, pin_timeout, PinTimeout)\nADD_SCENE(desktop, slideshow, Slideshow)\nADD_SCENE(desktop, secure_enclave, SecureEnclave)\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_debug.c",
    "content": "\n#include <dolphin/dolphin.h>\n#include <dolphin/helpers/dolphin_deed.h>\n\n#include \"../desktop_i.h\"\n#include \"../views/desktop_view_debug.h\"\n#include \"desktop_scene.h\"\n\nvoid desktop_scene_debug_callback(DesktopEvent event, void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, event);\n}\n\nvoid desktop_scene_debug_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop);\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug);\n}\n\nbool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopDebugEventExit:\n            scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);\n            dolphin_flush(dolphin);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    furi_record_close(RECORD_DOLPHIN);\n    return consumed;\n}\n\nvoid desktop_scene_debug_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_fault.c",
    "content": "#include <furi_hal.h>\n\n#include \"../desktop_i.h\"\n\n#define DesktopFaultEventExit 0x00FF00FF\n\nvoid desktop_scene_fault_callback(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopFaultEventExit);\n}\n\nvoid desktop_scene_fault_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    Popup* popup = desktop->popup;\n    popup_set_context(popup, desktop);\n    popup_set_header(\n        popup,\n        \"Flipper crashed\\n and was rebooted\",\n        64,\n        14 + STATUS_BAR_Y_SHIFT,\n        AlignCenter,\n        AlignCenter);\n\n    char* message = (char*)furi_hal_rtc_get_fault_data();\n    popup_set_text(popup, message, 64, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);\n    popup_set_callback(popup, desktop_scene_fault_callback);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);\n}\n\nbool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopFaultEventExit:\n            scene_manager_previous_scene(desktop->scene_manager);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_scene_fault_on_exit(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    furi_assert(desktop);\n\n    Popup* popup = desktop->popup;\n    popup_reset(popup);\n\n    furi_hal_rtc_set_fault_data(0);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_hw_mismatch.c",
    "content": "#include <gui/scene_manager.h>\n#include <furi_hal.h>\n\n#include \"desktop_scene.h\"\n#include \"../desktop_i.h\"\n\nvoid desktop_scene_hw_mismatch_callback(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopHwMismatchExit);\n}\n\nvoid desktop_scene_hw_mismatch_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    furi_assert(desktop);\n    Popup* popup = desktop->popup;\n\n    char* text_buffer = malloc(256);\n    scene_manager_set_scene_state(\n        desktop->scene_manager, DesktopSceneHwMismatch, (uint32_t)text_buffer);\n\n    snprintf(\n        text_buffer,\n        256,\n        \"HW target: %d\\nFW target: %d\",\n        furi_hal_version_get_hw_target(),\n        version_get_target(NULL));\n    popup_set_context(popup, desktop);\n    popup_set_header(\n        popup, \"!!!! HW Mismatch !!!!\", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);\n    popup_set_text(popup, text_buffer, 64, 33 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);\n    popup_set_callback(popup, desktop_scene_hw_mismatch_callback);\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);\n}\n\nbool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopHwMismatchExit:\n            scene_manager_previous_scene(desktop->scene_manager);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_scene_hw_mismatch_on_exit(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    furi_assert(desktop);\n\n    Popup* popup = desktop->popup;\n    popup_reset(popup);\n\n    char* text_buffer =\n        (char*)scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneHwMismatch);\n    free(text_buffer);\n    scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneHwMismatch, 0);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_lock_menu.c",
    "content": "#include <gui/scene_manager.h>\n#include <applications.h>\n#include <furi_hal.h>\n#include <toolbox/saved_struct.h>\n#include <stdbool.h>\n#include <loader/loader.h>\n\n#include \"../desktop_i.h\"\n#include <desktop/desktop_settings.h>\n#include \"../views/desktop_view_lock_menu.h\"\n#include \"desktop_scene.h\"\n\n#define TAG \"DesktopSceneLock\"\n\nvoid desktop_scene_lock_menu_callback(DesktopEvent event, void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, event);\n}\n\nvoid desktop_scene_lock_menu_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);\n    desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);\n    desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode);\n    desktop_lock_menu_set_stealth_mode_state(\n        desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode));\n    desktop_lock_menu_set_idx(desktop->lock_menu, 0);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu);\n}\n\nbool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        bool check_pin_changed =\n            scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);\n        if(check_pin_changed && desktop_pin_code_is_set()) {\n            scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);\n        }\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopLockMenuEventLock:\n            scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);\n            desktop_lock(desktop);\n            consumed = true;\n            break;\n        case DesktopLockMenuEventDummyModeOn:\n            desktop_set_dummy_mode_state(desktop, true);\n            scene_manager_search_and_switch_to_previous_scene(\n                desktop->scene_manager, DesktopSceneMain);\n            break;\n        case DesktopLockMenuEventDummyModeOff:\n            desktop_set_dummy_mode_state(desktop, false);\n            scene_manager_search_and_switch_to_previous_scene(\n                desktop->scene_manager, DesktopSceneMain);\n            break;\n        case DesktopLockMenuEventStealthModeOn:\n            desktop_set_stealth_mode_state(desktop, true);\n            scene_manager_search_and_switch_to_previous_scene(\n                desktop->scene_manager, DesktopSceneMain);\n            break;\n        case DesktopLockMenuEventStealthModeOff:\n            desktop_set_stealth_mode_state(desktop, false);\n            scene_manager_search_and_switch_to_previous_scene(\n                desktop->scene_manager, DesktopSceneMain);\n            break;\n        default:\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);\n    }\n    return consumed;\n}\n\nvoid desktop_scene_lock_menu_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_locked.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/scene_manager.h>\n#include <gui/view_stack.h>\n#include <stdint.h>\n\n#include \"../desktop.h\"\n#include \"../desktop_i.h\"\n#include \"../helpers/pin_code.h\"\n#include \"../animations/animation_manager.h\"\n#include \"../views/desktop_events.h\"\n#include \"../views/desktop_view_locked.h\"\n#include \"desktop_scene.h\"\n#include \"desktop_scene_locked.h\"\n\n#define WRONG_PIN_HEADER_TIMEOUT 3000\n#define INPUT_PIN_VIEW_TIMEOUT   15000\n\nstatic void desktop_scene_locked_callback(DesktopEvent event, void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, event);\n}\n\nstatic void desktop_scene_locked_new_idle_animation_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    view_dispatcher_send_custom_event(\n        desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation);\n}\n\nvoid desktop_scene_locked_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    // callbacks for 1-st layer\n    animation_manager_set_new_idle_callback(\n        desktop->animation_manager, desktop_scene_locked_new_idle_animation_callback);\n    animation_manager_set_check_callback(desktop->animation_manager, NULL);\n    animation_manager_set_interact_callback(desktop->animation_manager, NULL);\n\n    // callbacks for 2-nd layer\n    desktop_view_locked_set_callback(desktop->locked_view, desktop_scene_locked_callback, desktop);\n\n    bool switch_to_timeout_scene = false;\n    uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);\n    if(state == DesktopSceneLockedStateFirstEnter) {\n        view_port_enabled_set(desktop->lock_icon_viewport, true);\n        Gui* gui = furi_record_open(RECORD_GUI);\n        gui_set_lockdown(gui, true);\n        furi_record_close(RECORD_GUI);\n\n        if(desktop_pin_code_is_set()) {\n            desktop_view_locked_lock(desktop->locked_view, true);\n            uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout();\n            if(pin_timeout > 0) {\n                scene_manager_set_scene_state(\n                    desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);\n                switch_to_timeout_scene = true;\n            } else {\n                desktop_view_locked_close_doors(desktop->locked_view);\n            }\n        } else {\n            desktop_view_locked_lock(desktop->locked_view, false);\n            desktop_view_locked_close_doors(desktop->locked_view);\n        }\n        scene_manager_set_scene_state(\n            desktop->scene_manager, DesktopSceneLocked, DesktopSceneLockedStateRepeatEnter);\n    }\n\n    if(switch_to_timeout_scene) {\n        scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);\n    } else {\n        view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLocked);\n    }\n}\n\nbool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopLockedEventUnlocked:\n        case DesktopGlobalApiUnlock:\n            desktop_unlock(desktop);\n            consumed = true;\n            break;\n        case DesktopLockedEventDoorsClosed:\n            notification_message(desktop->notification, &sequence_display_backlight_off);\n            consumed = true;\n            break;\n        case DesktopLockedEventUpdate:\n            if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) {\n                notification_message(desktop->notification, &sequence_display_backlight_off);\n            }\n            desktop_view_locked_update(desktop->locked_view);\n            consumed = true;\n            break;\n        case DesktopLockedEventShowPinInput:\n            scene_manager_next_scene(desktop->scene_manager, DesktopScenePinInput);\n            consumed = true;\n            break;\n        case DesktopAnimationEventNewIdleAnimation:\n            animation_manager_new_idle_process(desktop->animation_manager);\n            consumed = true;\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_scene_locked_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_locked.h",
    "content": "#pragma once\n\ntypedef enum {\n    DesktopSceneLockedStateFirstEnter,\n    DesktopSceneLockedStateRepeatEnter,\n} DesktopSceneLockedState;\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_main.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <applications.h>\n#include <assets_icons.h>\n#include <loader/loader.h>\n\n#include \"../desktop_i.h\"\n#include \"../views/desktop_events.h\"\n#include \"../views/desktop_view_main.h\"\n#include \"desktop_scene.h\"\n\n#define TAG \"DesktopSrv\"\n\nstatic void desktop_scene_main_new_idle_animation_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    view_dispatcher_send_custom_event(\n        desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation);\n}\n\nstatic void desktop_scene_main_check_animation_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    view_dispatcher_send_custom_event(\n        desktop->view_dispatcher, DesktopAnimationEventCheckAnimation);\n}\n\nstatic void desktop_scene_main_interact_animation_callback(void* context) {\n    furi_assert(context);\n    Desktop* desktop = context;\n    view_dispatcher_send_custom_event(\n        desktop->view_dispatcher, DesktopAnimationEventInteractAnimation);\n}\n\n#ifdef APP_ARCHIVE\nstatic void\n    desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) {\n    furi_assert(desktop);\n    furi_assert(flipper_app);\n    furi_assert(flipper_app->app);\n    furi_assert(flipper_app->name);\n\n    if(furi_thread_get_state(desktop->scene_thread) != FuriThreadStateStopped) {\n        FURI_LOG_E(\"Desktop\", \"Thread is already running\");\n        return;\n    }\n\n    FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();\n    if(mode > FuriHalRtcHeapTrackModeNone) {\n        furi_thread_enable_heap_trace(desktop->scene_thread);\n    } else {\n        furi_thread_disable_heap_trace(desktop->scene_thread);\n    }\n\n    furi_thread_set_name(desktop->scene_thread, flipper_app->name);\n    furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size);\n    furi_thread_set_callback(desktop->scene_thread, flipper_app->app);\n\n    furi_thread_start(desktop->scene_thread);\n}\n#endif\n\nstatic void desktop_scene_main_open_app_or_profile(Desktop* desktop, FavoriteApp* application) {\n    if(strlen(application->name_or_path) > 0) {\n        loader_start_detached_with_gui_error(desktop->loader, application->name_or_path, NULL);\n    } else {\n        loader_start_detached_with_gui_error(desktop->loader, \"Passport\", NULL);\n    }\n}\n\nstatic void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) {\n    if(strlen(application->name_or_path) > 0) {\n        loader_start_detached_with_gui_error(desktop->loader, application->name_or_path, NULL);\n    } else {\n        loader_start_detached_with_gui_error(desktop->loader, LOADER_APPLICATIONS_NAME, NULL);\n    }\n}\n\nvoid desktop_scene_main_callback(DesktopEvent event, void* context) {\n    Desktop* desktop = (Desktop*)context;\n    if(desktop->in_transition) return;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, event);\n}\n\nvoid desktop_scene_main_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    DesktopMainView* main_view = desktop->main_view;\n\n    animation_manager_set_context(desktop->animation_manager, desktop);\n    animation_manager_set_new_idle_callback(\n        desktop->animation_manager, desktop_scene_main_new_idle_animation_callback);\n    animation_manager_set_check_callback(\n        desktop->animation_manager, desktop_scene_main_check_animation_callback);\n    animation_manager_set_interact_callback(\n        desktop->animation_manager, desktop_scene_main_interact_animation_callback);\n\n    desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain);\n}\n\nbool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopMainEventOpenMenu: {\n            Loader* loader = furi_record_open(RECORD_LOADER);\n            loader_show_menu(loader);\n            furi_record_close(RECORD_LOADER);\n            consumed = true;\n        } break;\n\n        case DesktopMainEventLock:\n            desktop_lock(desktop);\n            consumed = true;\n            break;\n\n        case DesktopMainEventOpenLockMenu:\n            scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);\n            consumed = true;\n            break;\n\n        case DesktopMainEventOpenDebug:\n            scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug);\n            consumed = true;\n            break;\n\n        case DesktopMainEventOpenArchive:\n#ifdef APP_ARCHIVE\n            desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE);\n#endif\n            consumed = true;\n            break;\n\n        case DesktopMainEventOpenPowerOff: {\n            loader_start_detached_with_gui_error(desktop->loader, \"Power\", \"off\");\n            consumed = true;\n            break;\n        }\n\n        case DesktopMainEventOpenFavoriteLeftShort:\n            desktop_scene_main_start_favorite(\n                desktop, &desktop->settings.favorite_apps[FavoriteAppLeftShort]);\n            consumed = true;\n            break;\n        case DesktopMainEventOpenFavoriteLeftLong:\n            desktop_scene_main_start_favorite(\n                desktop, &desktop->settings.favorite_apps[FavoriteAppLeftLong]);\n            consumed = true;\n            break;\n        case DesktopMainEventOpenFavoriteRightShort:\n            desktop_scene_main_start_favorite(\n                desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);\n            consumed = true;\n            break;\n        case DesktopMainEventOpenFavoriteRightLong:\n            desktop_scene_main_start_favorite(\n                desktop, &desktop->settings.favorite_apps[FavoriteAppRightLong]);\n            consumed = true;\n            break;\n\n        case DesktopAnimationEventCheckAnimation:\n            animation_manager_check_blocking_process(desktop->animation_manager);\n            consumed = true;\n            break;\n        case DesktopAnimationEventNewIdleAnimation:\n            animation_manager_new_idle_process(desktop->animation_manager);\n            consumed = true;\n            break;\n        case DesktopAnimationEventInteractAnimation:\n            if(!animation_manager_interact_process(desktop->animation_manager)) {\n                if(!desktop->settings.dummy_mode) {\n                    desktop_scene_main_open_app_or_profile(\n                        desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);\n                } else {\n                    desktop_scene_main_open_app_or_profile(\n                        desktop, &desktop->settings.dummy_apps[DummyAppRight]);\n                }\n            }\n            consumed = true;\n            break;\n\n        case DesktopDummyEventOpenLeft:\n            desktop_scene_main_open_app_or_profile(\n                desktop, &desktop->settings.dummy_apps[DummyAppLeft]);\n            break;\n        case DesktopDummyEventOpenDown:\n            desktop_scene_main_open_app_or_profile(\n                desktop, &desktop->settings.dummy_apps[DummyAppDown]);\n            break;\n        case DesktopDummyEventOpenOk:\n            desktop_scene_main_open_app_or_profile(\n                desktop, &desktop->settings.dummy_apps[DummyAppOk]);\n            break;\n\n        case DesktopLockedEventUpdate:\n            desktop_view_locked_update(desktop->locked_view);\n            consumed = true;\n            break;\n\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_scene_main_on_exit(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    animation_manager_set_new_idle_callback(desktop->animation_manager, NULL);\n    animation_manager_set_check_callback(desktop->animation_manager, NULL);\n    animation_manager_set_interact_callback(desktop->animation_manager, NULL);\n    animation_manager_set_context(desktop->animation_manager, desktop);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_pin_input.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/scene_manager.h>\n#include <gui/view_stack.h>\n#include <stdint.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\n#include \"../desktop.h\"\n#include \"../desktop_i.h\"\n#include \"../views/desktop_events.h\"\n#include \"../views/desktop_view_pin_input.h\"\n#include \"../helpers/pin_code.h\"\n#include \"desktop_scene.h\"\n\n#define WRONG_PIN_HEADER_TIMEOUT 3000\n#define INPUT_PIN_VIEW_TIMEOUT   15000\n\ntypedef struct {\n    FuriTimer* timer;\n    FuriString* enter_pin_string;\n} DesktopScenePinInputState;\n\nstatic void desktop_scene_locked_light_red(bool value) {\n    NotificationApp* app = furi_record_open(RECORD_NOTIFICATION);\n    if(value) {\n        notification_message(app, &sequence_set_only_red_255);\n    } else {\n        notification_message(app, &sequence_reset_red);\n    }\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nstatic void desktop_scene_pin_input_set_timer(Desktop* desktop, bool enable, uint32_t new_period) {\n    furi_assert(desktop);\n\n    DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(\n        desktop->scene_manager, DesktopScenePinInput);\n    furi_assert(state);\n    if(enable) {\n        furi_timer_start(state->timer, new_period);\n    } else {\n        furi_timer_stop(state->timer);\n    }\n}\n\nstatic void desktop_scene_pin_input_back_callback(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventBack);\n}\n\nstatic void desktop_scene_pin_input_done_callback(const DesktopPinCode* pin_code, void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    if(desktop_pin_code_check(pin_code)) {\n        view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);\n\n    } else {\n        uint32_t pin_fails = furi_hal_rtc_get_pin_fails();\n        furi_hal_rtc_set_pin_fails(pin_fails + 1);\n        view_dispatcher_send_custom_event(\n            desktop->view_dispatcher, DesktopPinInputEventUnlockFailed);\n    }\n}\n\nstatic void desktop_scene_pin_input_timer_callback(void* context) {\n    Desktop* desktop = context;\n\n    view_dispatcher_send_custom_event(\n        desktop->view_dispatcher, DesktopPinInputEventResetWrongPinLabel);\n}\n\nstatic void\n    desktop_scene_pin_input_update_wrong_count(DesktopScenePinInputState* state, Desktop* desktop) {\n    uint32_t attempts = furi_hal_rtc_get_pin_fails();\n    if(attempts > 0) {\n        furi_string_printf(state->enter_pin_string, \"Wrong Attempts: %lu\", attempts);\n        desktop_view_pin_input_set_label_tertiary(\n            desktop->pin_input_view, 64, 60, furi_string_get_cstr(state->enter_pin_string));\n    } else {\n        desktop_view_pin_input_set_label_tertiary(desktop->pin_input_view, 64, 60, NULL);\n    }\n}\n\nvoid desktop_scene_pin_input_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    desktop_view_pin_input_set_context(desktop->pin_input_view, desktop);\n    desktop_view_pin_input_set_back_callback(\n        desktop->pin_input_view, desktop_scene_pin_input_back_callback);\n    desktop_view_pin_input_set_timeout_callback(\n        desktop->pin_input_view, desktop_scene_pin_input_back_callback);\n    desktop_view_pin_input_set_done_callback(\n        desktop->pin_input_view, desktop_scene_pin_input_done_callback);\n\n    DesktopScenePinInputState* state = malloc(sizeof(DesktopScenePinInputState));\n    state->enter_pin_string = furi_string_alloc();\n    state->timer =\n        furi_timer_alloc(desktop_scene_pin_input_timer_callback, FuriTimerTypeOnce, desktop);\n    scene_manager_set_scene_state(desktop->scene_manager, DesktopScenePinInput, (uint32_t)state);\n\n    desktop_view_pin_input_hide_pin(desktop->pin_input_view, true);\n    desktop_view_pin_input_set_label_button(desktop->pin_input_view, \"OK\");\n    desktop_view_pin_input_set_label_secondary(desktop->pin_input_view, 44, 25, \"Enter PIN:\");\n    desktop_scene_pin_input_update_wrong_count(state, desktop);\n    desktop_view_pin_input_set_pin_position(desktop->pin_input_view, 64, 37);\n    desktop_view_pin_input_reset_pin(desktop->pin_input_view);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinInput);\n}\n\nbool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n    uint32_t pin_timeout = 0;\n    DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(\n        desktop->scene_manager, DesktopScenePinInput);\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopPinInputEventUnlockFailed:\n            pin_timeout = desktop_pin_lock_get_fail_timeout();\n            if(pin_timeout > 0) {\n                desktop_pin_lock_error_notify();\n                scene_manager_set_scene_state(\n                    desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);\n                scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);\n            } else {\n                desktop_scene_locked_light_red(true);\n                desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL);\n                desktop_view_pin_input_set_label_secondary(\n                    desktop->pin_input_view, 25, 25, \"Wrong PIN try again:\");\n                desktop_scene_pin_input_set_timer(desktop, true, WRONG_PIN_HEADER_TIMEOUT);\n                desktop_scene_pin_input_update_wrong_count(state, desktop);\n                desktop_view_pin_input_reset_pin(desktop->pin_input_view);\n            }\n            consumed = true;\n            break;\n        case DesktopPinInputEventResetWrongPinLabel:\n            desktop_scene_locked_light_red(false);\n            desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL);\n            desktop_view_pin_input_set_label_secondary(\n                desktop->pin_input_view, 44, 25, \"Enter PIN:\");\n            desktop_scene_pin_input_update_wrong_count(state, desktop);\n            consumed = true;\n            break;\n        case DesktopPinInputEventUnlocked:\n        case DesktopGlobalApiUnlock:\n            desktop_unlock(desktop);\n            consumed = true;\n            break;\n        case DesktopPinInputEventBack:\n            scene_manager_search_and_switch_to_previous_scene(\n                desktop->scene_manager, DesktopSceneLocked);\n            notification_message(desktop->notification, &sequence_display_backlight_off);\n            consumed = true;\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_scene_pin_input_on_exit(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    desktop_scene_locked_light_red(false);\n\n    DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(\n        desktop->scene_manager, DesktopScenePinInput);\n\n    furi_timer_free(state->timer);\n    furi_string_free(state->enter_pin_string);\n    free(state);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_pin_timeout.c",
    "content": "#include <furi.h>\n#include <gui/scene_manager.h>\n\n#include \"../desktop_i.h\"\n#include \"../views/desktop_view_pin_timeout.h\"\n#include \"desktop_scene.h\"\n\nstatic void desktop_scene_pin_timeout_callback(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinTimeoutExit);\n}\n\nvoid desktop_scene_pin_timeout_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n\n    uint32_t timeout =\n        scene_manager_get_scene_state(desktop->scene_manager, DesktopScenePinTimeout);\n    desktop_view_pin_timeout_start(desktop->pin_timeout_view, timeout);\n    desktop_view_pin_timeout_set_callback(\n        desktop->pin_timeout_view, desktop_scene_pin_timeout_callback, desktop);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinTimeout);\n}\n\nbool desktop_scene_pin_timeout_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopPinTimeoutExit:\n            scene_manager_previous_scene(desktop->scene_manager);\n            consumed = true;\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_scene_pin_timeout_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_secure_enclave.c",
    "content": "#include <gui/scene_manager.h>\n#include <furi_hal.h>\n\n#include \"desktop_scene.h\"\n#include \"../desktop_i.h\"\n\nvoid desktop_scene_secure_enclave_callback(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopEnclaveExit);\n}\n\nvoid desktop_scene_secure_enclave_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    furi_assert(desktop);\n\n    Popup* popup = desktop->popup;\n    popup_set_context(popup, desktop);\n    popup_set_header(\n        popup, \"No Factory Keys Found\", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);\n    popup_set_text(\n        popup,\n        \"Secure Enclave is damaged.\\n\"\n        \"Some apps will not work.\",\n        64,\n        33 + STATUS_BAR_Y_SHIFT,\n        AlignCenter,\n        AlignCenter);\n    popup_set_callback(popup, desktop_scene_secure_enclave_callback);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);\n}\n\nbool desktop_scene_secure_enclave_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopEnclaveExit:\n            scene_manager_previous_scene(desktop->scene_manager);\n            consumed = true;\n            break;\n\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_scene_secure_enclave_on_exit(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    furi_assert(desktop);\n\n    Popup* popup = desktop->popup;\n    popup_reset(popup);\n}\n"
  },
  {
    "path": "applications/services/desktop/scenes/desktop_scene_slideshow.c",
    "content": "#include <storage/storage.h>\n\n#include \"../desktop_i.h\"\n#include \"../views/desktop_view_slideshow.h\"\n#include \"../views/desktop_events.h\"\n#include <power/power_service/power.h>\n\nvoid desktop_scene_slideshow_callback(DesktopEvent event, void* context) {\n    Desktop* desktop = (Desktop*)context;\n    view_dispatcher_send_custom_event(desktop->view_dispatcher, event);\n}\n\nvoid desktop_scene_slideshow_on_enter(void* context) {\n    Desktop* desktop = (Desktop*)context;\n    DesktopSlideshowView* slideshow_view = desktop->slideshow_view;\n\n    desktop_view_slideshow_set_callback(slideshow_view, desktop_scene_slideshow_callback, desktop);\n\n    view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdSlideshow);\n}\n\nbool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {\n    Desktop* desktop = (Desktop*)context;\n    bool consumed = false;\n    Power* power = NULL;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSlideshowCompleted:\n            scene_manager_previous_scene(desktop->scene_manager);\n            consumed = true;\n            break;\n        case DesktopSlideshowPoweroff:\n            power = furi_record_open(RECORD_POWER);\n            power_off(power);\n            furi_record_close(RECORD_POWER);\n            consumed = true;\n            break;\n\n        default:\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_scene_slideshow_on_exit(void* context) {\n    Desktop* desktop = context;\n    storage_common_remove(desktop->storage, SLIDESHOW_FS_PATH);\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_events.h",
    "content": "#pragma once\n\ntypedef enum {\n    DesktopMainEventLock,\n    DesktopMainEventOpenLockMenu,\n    DesktopMainEventOpenArchive,\n    DesktopMainEventOpenFavoriteLeftShort,\n    DesktopMainEventOpenFavoriteLeftLong,\n    DesktopMainEventOpenFavoriteRightShort,\n    DesktopMainEventOpenFavoriteRightLong,\n    DesktopMainEventOpenMenu,\n    DesktopMainEventOpenDebug,\n    DesktopMainEventOpenPowerOff,\n\n    DesktopDummyEventOpenLeft,\n    DesktopDummyEventOpenDown,\n    DesktopDummyEventOpenOk,\n\n    DesktopLockedEventUnlocked,\n    DesktopLockedEventUpdate,\n    DesktopLockedEventShowPinInput,\n    DesktopLockedEventDoorsClosed,\n\n    DesktopPinInputEventResetWrongPinLabel,\n    DesktopPinInputEventUnlocked,\n    DesktopPinInputEventUnlockFailed,\n    DesktopPinInputEventBack,\n\n    DesktopPinTimeoutExit,\n\n    DesktopDebugEventDeed,\n    DesktopDebugEventWrongDeed,\n    DesktopDebugEventSaveState,\n    DesktopDebugEventExit,\n\n    DesktopLockMenuEventLock,\n    DesktopLockMenuEventDummyModeOn,\n    DesktopLockMenuEventDummyModeOff,\n    DesktopLockMenuEventStealthModeOn,\n    DesktopLockMenuEventStealthModeOff,\n\n    DesktopAnimationEventCheckAnimation,\n    DesktopAnimationEventNewIdleAnimation,\n    DesktopAnimationEventInteractAnimation,\n\n    DesktopSlideshowCompleted,\n    DesktopSlideshowPoweroff,\n\n    DesktopHwMismatchExit,\n\n    DesktopEnclaveExit,\n\n    // Global events\n    DesktopGlobalBeforeAppStarted,\n    DesktopGlobalAfterAppFinished,\n    DesktopGlobalAutoLock,\n    DesktopGlobalApiUnlock,\n    DesktopGlobalSaveSettings,\n    DesktopGlobalReloadSettings,\n} DesktopEvent;\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_debug.c",
    "content": "#include <toolbox/version.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <dolphin/helpers/dolphin_state.h>\n#include <dolphin/dolphin.h>\n\n#include \"../desktop_i.h\"\n#include \"desktop_view_debug.h\"\n\nvoid desktop_debug_set_callback(\n    DesktopDebugView* debug_view,\n    DesktopDebugViewCallback callback,\n    void* context) {\n    furi_assert(debug_view);\n    furi_assert(callback);\n    debug_view->callback = callback;\n    debug_view->context = context;\n}\n\nvoid desktop_debug_render(Canvas* canvas, void* model) {\n    UNUSED(model);\n    canvas_clear(canvas);\n    const Version* ver;\n    char buffer[64];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n\n    uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"Uptime: %luh%lum%lus\",\n        uptime / 60 / 60,\n        uptime / 60 % 60,\n        uptime % 60);\n    canvas_draw_str_aligned(canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, buffer);\n\n    canvas_set_font(canvas, FontSecondary);\n\n    // Hardware version\n    const char* my_name = furi_hal_version_get_name_ptr();\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"%d.F%dB%dC%d %s:%s %s\",\n        furi_hal_version_get_hw_version(),\n        furi_hal_version_get_hw_target(),\n        furi_hal_version_get_hw_body(),\n        furi_hal_version_get_hw_connect(),\n        furi_hal_version_get_hw_region_name(),\n        furi_hal_region_get_name(),\n        my_name ? my_name : \"Unknown\");\n    canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer);\n\n    ver = furi_hal_version_get_firmware_version();\n    const BleGlueC2Info* c2_ver = NULL;\n#ifdef SRV_BT\n    c2_ver = ble_glue_get_c2_info();\n#endif\n    if(!ver) { //-V1051\n        canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, \"No info\");\n        return;\n    }\n\n    snprintf(\n        buffer, sizeof(buffer), \"%s [%s]\", version_get_version(ver), version_get_builddate(ver));\n    canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);\n\n    uint16_t api_major, api_minor;\n    furi_hal_info_get_api_version(&api_major, &api_minor);\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"%s%s [%d.%d] %s\",\n        version_get_dirty_flag(ver) ? \"[!] \" : \"\",\n        version_get_githash(ver),\n        api_major,\n        api_minor,\n        c2_ver ? c2_ver->StackTypeString : \"<none>\");\n    canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);\n\n    snprintf(\n        buffer, sizeof(buffer), \"[%d] %s\", version_get_target(ver), version_get_gitbranch(ver));\n    canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);\n}\n\nView* desktop_debug_get_view(DesktopDebugView* debug_view) {\n    furi_assert(debug_view);\n    return debug_view->view;\n}\n\nstatic bool desktop_debug_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopDebugView* debug_view = context;\n\n    if(event->key == InputKeyBack && event->type == InputTypeShort) {\n        debug_view->callback(DesktopDebugEventExit, debug_view->context);\n    }\n\n    return true;\n}\n\nstatic void desktop_debug_enter(void* context) {\n    DesktopDebugView* debug_view = context;\n    furi_timer_start(debug_view->timer, furi_ms_to_ticks(1000));\n}\n\nstatic void desktop_debug_exit(void* context) {\n    DesktopDebugView* debug_view = context;\n    furi_timer_stop(debug_view->timer);\n}\nvoid desktop_debug_timer(void* context) {\n    DesktopDebugView* debug_view = context;\n    view_get_model(debug_view->view);\n    view_commit_model(debug_view->view, true);\n}\n\nDesktopDebugView* desktop_debug_alloc(void) {\n    DesktopDebugView* debug_view = malloc(sizeof(DesktopDebugView));\n    debug_view->view = view_alloc();\n    debug_view->timer = furi_timer_alloc(desktop_debug_timer, FuriTimerTypePeriodic, debug_view);\n    view_set_context(debug_view->view, debug_view);\n    view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render);\n    view_set_input_callback(debug_view->view, desktop_debug_input);\n    view_set_enter_callback(debug_view->view, desktop_debug_enter);\n    view_set_exit_callback(debug_view->view, desktop_debug_exit);\n\n    return debug_view;\n}\n\nvoid desktop_debug_free(DesktopDebugView* debug_view) {\n    furi_assert(debug_view);\n\n    furi_timer_free(debug_view->timer);\n    view_free(debug_view->view);\n    free(debug_view);\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_debug.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <gui/view.h>\n#include \"desktop_events.h\"\n\ntypedef struct DesktopDebugView DesktopDebugView;\n\ntypedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context);\n\nstruct DesktopDebugView {\n    View* view;\n    FuriTimer* timer;\n    DesktopDebugViewCallback callback;\n    void* context;\n};\n\nvoid desktop_debug_set_callback(\n    DesktopDebugView* debug_view,\n    DesktopDebugViewCallback callback,\n    void* context);\n\nView* desktop_debug_get_view(DesktopDebugView* debug_view);\n\nDesktopDebugView* desktop_debug_alloc(void);\n\nvoid desktop_debug_free(DesktopDebugView* debug_view);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_lock_menu.c",
    "content": "#include <furi.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\n#include \"../desktop_i.h\"\n#include \"desktop_view_lock_menu.h\"\n\ntypedef enum {\n    DesktopLockMenuIndexLock,\n    DesktopLockMenuIndexStealth,\n    DesktopLockMenuIndexDummy,\n\n    DesktopLockMenuIndexTotalCount\n} DesktopLockMenuIndex;\n\nvoid desktop_lock_menu_set_callback(\n    DesktopLockMenuView* lock_menu,\n    DesktopLockMenuViewCallback callback,\n    void* context) {\n    furi_assert(lock_menu);\n    furi_assert(callback);\n    lock_menu->callback = callback;\n    lock_menu->context = context;\n}\n\nvoid desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) {\n    with_view_model(\n        lock_menu->view,\n        DesktopLockMenuViewModel * model,\n        { model->dummy_mode = dummy_mode; },\n        true);\n}\n\nvoid desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode) {\n    with_view_model(\n        lock_menu->view,\n        DesktopLockMenuViewModel * model,\n        { model->stealth_mode = stealth_mode; },\n        true);\n}\n\nvoid desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) {\n    furi_assert(idx < DesktopLockMenuIndexTotalCount);\n    with_view_model(\n        lock_menu->view, DesktopLockMenuViewModel * model, { model->idx = idx; }, true);\n}\n\nvoid desktop_lock_menu_draw_callback(Canvas* canvas, void* model) {\n    DesktopLockMenuViewModel* m = model;\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_icon(canvas, -57, 0 + STATUS_BAR_Y_SHIFT, &I_DoorLeft_70x55);\n    canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55);\n    canvas_set_font(canvas, FontSecondary);\n\n    for(size_t i = 0; i < DesktopLockMenuIndexTotalCount; ++i) {\n        const char* str = NULL;\n\n        if(i == DesktopLockMenuIndexLock) {\n            str = \"Lock\";\n        } else if(i == DesktopLockMenuIndexStealth) {\n            if(m->stealth_mode) {\n                str = \"Unmute\";\n            } else {\n                str = \"Mute\";\n            }\n        } else if(i == DesktopLockMenuIndexDummy) { //-V547\n            if(m->dummy_mode) {\n                str = \"Default Mode\";\n            } else {\n                str = \"Dummy Mode\";\n            }\n        }\n\n        if(str) //-V547\n            canvas_draw_str_aligned(\n                canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str);\n\n        if(m->idx == i) elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15);\n    }\n}\n\nView* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu) {\n    furi_assert(lock_menu);\n    return lock_menu->view;\n}\n\nbool desktop_lock_menu_input_callback(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopLockMenuView* lock_menu = context;\n    uint8_t idx = 0;\n    bool consumed = false;\n    bool dummy_mode = false;\n    bool stealth_mode = false;\n    bool update = false;\n\n    with_view_model(\n        lock_menu->view,\n        DesktopLockMenuViewModel * model,\n        {\n            if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {\n                if(event->key == InputKeyUp) {\n                    if(model->idx == 0) {\n                        model->idx = DesktopLockMenuIndexTotalCount - 1;\n                    } else {\n                        model->idx = CLAMP(model->idx - 1, DesktopLockMenuIndexTotalCount - 1, 0);\n                    }\n                    update = true;\n                    consumed = true;\n                } else if(event->key == InputKeyDown) {\n                    if(model->idx == DesktopLockMenuIndexTotalCount - 1) {\n                        model->idx = 0;\n                    } else {\n                        model->idx = CLAMP(model->idx + 1, DesktopLockMenuIndexTotalCount - 1, 0);\n                    }\n                    update = true;\n                    consumed = true;\n                }\n            }\n            idx = model->idx;\n            dummy_mode = model->dummy_mode;\n            stealth_mode = model->stealth_mode;\n        },\n        update);\n\n    if(event->key == InputKeyOk) {\n        if(idx == DesktopLockMenuIndexLock) {\n            if(event->type == InputTypeShort) {\n                lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);\n            }\n        } else if(idx == DesktopLockMenuIndexStealth) {\n            if((stealth_mode == false) && (event->type == InputTypeShort)) {\n                lock_menu->callback(DesktopLockMenuEventStealthModeOn, lock_menu->context);\n            } else if((stealth_mode == true) && (event->type == InputTypeShort)) {\n                lock_menu->callback(DesktopLockMenuEventStealthModeOff, lock_menu->context);\n            }\n        } else if(idx == DesktopLockMenuIndexDummy) {\n            if((dummy_mode == false) && (event->type == InputTypeShort)) {\n                lock_menu->callback(DesktopLockMenuEventDummyModeOn, lock_menu->context);\n            } else if((dummy_mode == true) && (event->type == InputTypeShort)) {\n                lock_menu->callback(DesktopLockMenuEventDummyModeOff, lock_menu->context);\n            }\n        }\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nDesktopLockMenuView* desktop_lock_menu_alloc(void) {\n    DesktopLockMenuView* lock_menu = malloc(sizeof(DesktopLockMenuView));\n    lock_menu->view = view_alloc();\n    view_allocate_model(lock_menu->view, ViewModelTypeLocking, sizeof(DesktopLockMenuViewModel));\n    view_set_context(lock_menu->view, lock_menu);\n    view_set_draw_callback(lock_menu->view, (ViewDrawCallback)desktop_lock_menu_draw_callback);\n    view_set_input_callback(lock_menu->view, desktop_lock_menu_input_callback);\n\n    return lock_menu;\n}\n\nvoid desktop_lock_menu_free(DesktopLockMenuView* lock_menu_view) {\n    furi_assert(lock_menu_view);\n\n    view_free(lock_menu_view->view);\n    free(lock_menu_view);\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_lock_menu.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"desktop_events.h\"\n\n#define HINT_TIMEOUT 2\n\ntypedef struct DesktopLockMenuView DesktopLockMenuView;\n\ntypedef void (*DesktopLockMenuViewCallback)(DesktopEvent event, void* context);\n\nstruct DesktopLockMenuView {\n    View* view;\n    DesktopLockMenuViewCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint8_t idx;\n    bool dummy_mode;\n    bool stealth_mode;\n} DesktopLockMenuViewModel;\n\nvoid desktop_lock_menu_set_callback(\n    DesktopLockMenuView* lock_menu,\n    DesktopLockMenuViewCallback callback,\n    void* context);\n\nView* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);\nvoid desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode);\nvoid desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode);\nvoid desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx);\nDesktopLockMenuView* desktop_lock_menu_alloc(void);\nvoid desktop_lock_menu_free(DesktopLockMenuView* lock_menu);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_locked.c",
    "content": "#include <furi.h>\n\n#include <gui/elements.h>\n#include <gui/icon.h>\n#include <gui/view.h>\n\n#include <assets_icons.h>\n\n#include \"../desktop_i.h\"\n#include \"desktop_view_locked.h\"\n\n#define DOOR_MOVING_INTERVAL_MS  (1000 / 16)\n#define LOCKED_HINT_TIMEOUT_MS   (1000)\n#define UNLOCKED_HINT_TIMEOUT_MS (2000)\n\n#define DOOR_OFFSET_START (-55)\n#define DOOR_OFFSET_END   (0)\n\n#define DOOR_L_FINAL_POS (0)\n#define DOOR_R_FINAL_POS (60)\n\n#define UNLOCK_CNT         (3)\n#define UNLOCK_RST_TIMEOUT (600)\n\nstruct DesktopViewLocked {\n    View* view;\n    DesktopViewLockedCallback callback;\n    void* context;\n\n    FuriTimer* timer;\n    uint8_t lock_count;\n    uint32_t lock_lastpress;\n};\n\ntypedef enum {\n    DesktopViewLockedStateUnlocked,\n    DesktopViewLockedStateLocked,\n    DesktopViewLockedStateDoorsClosing,\n    DesktopViewLockedStateLockedHintShown,\n    DesktopViewLockedStateUnlockedHintShown\n} DesktopViewLockedState;\n\ntypedef struct {\n    bool pin_locked;\n    int8_t door_offset;\n    DesktopViewLockedState view_state;\n} DesktopViewLockedModel;\n\nvoid desktop_view_locked_set_callback(\n    DesktopViewLocked* locked_view,\n    DesktopViewLockedCallback callback,\n    void* context) {\n    furi_assert(locked_view);\n    furi_assert(callback);\n    locked_view->callback = callback;\n    locked_view->context = context;\n}\n\nstatic void locked_view_timer_callback(void* context) {\n    DesktopViewLocked* locked_view = context;\n    locked_view->callback(DesktopLockedEventUpdate, locked_view->context);\n}\n\nstatic void desktop_view_locked_doors_draw(Canvas* canvas, DesktopViewLockedModel* model) {\n    int32_t offset = model->door_offset;\n    int32_t door_left_x = DOOR_L_FINAL_POS + offset;\n    int32_t door_right_x = DOOR_R_FINAL_POS - offset;\n    size_t height = icon_get_height(&I_DoorLeft_70x55);\n    canvas_draw_icon(canvas, door_left_x, canvas_height(canvas) - height, &I_DoorLeft_70x55);\n    canvas_draw_icon(canvas, door_right_x, canvas_height(canvas) - height, &I_DoorRight_70x55);\n}\n\nstatic bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) {\n    bool stop = false;\n    if(model->door_offset < DOOR_OFFSET_END) {\n        model->door_offset = CLAMP(model->door_offset + 5, DOOR_OFFSET_END, DOOR_OFFSET_START);\n        stop = true;\n    }\n\n    return stop;\n}\n\nstatic void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) {\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    const bool change_state = (model->view_state == DesktopViewLockedStateLocked) &&\n                              !model->pin_locked;\n    if(change_state) {\n        model->view_state = DesktopViewLockedStateLockedHintShown;\n    }\n    view_commit_model(locked_view->view, change_state);\n    furi_timer_start(locked_view->timer, LOCKED_HINT_TIMEOUT_MS);\n}\n\nvoid desktop_view_locked_update(DesktopViewLocked* locked_view) {\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    DesktopViewLockedState view_state = model->view_state;\n\n    if(view_state == DesktopViewLockedStateDoorsClosing &&\n       !desktop_view_locked_doors_move(model)) {\n        locked_view->callback(DesktopLockedEventDoorsClosed, locked_view->context);\n        model->view_state = DesktopViewLockedStateLocked;\n    } else if(view_state == DesktopViewLockedStateLockedHintShown) {\n        model->view_state = DesktopViewLockedStateLocked;\n    } else if(view_state == DesktopViewLockedStateUnlockedHintShown) {\n        model->view_state = DesktopViewLockedStateUnlocked;\n    }\n\n    view_commit_model(locked_view->view, true);\n\n    if(view_state != DesktopViewLockedStateDoorsClosing) {\n        furi_timer_stop(locked_view->timer);\n    }\n}\n\nstatic void desktop_view_locked_draw(Canvas* canvas, void* model) {\n    DesktopViewLockedModel* m = model;\n    DesktopViewLockedState view_state = m->view_state;\n    canvas_set_color(canvas, ColorBlack);\n\n    if(view_state == DesktopViewLockedStateDoorsClosing) {\n        desktop_view_locked_doors_draw(canvas, m);\n        canvas_set_font(canvas, FontPrimary);\n        elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, \"Locked\");\n    } else if(view_state == DesktopViewLockedStateLockedHintShown) {\n        canvas_set_font(canvas, FontSecondary);\n        elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48);\n        elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, \"To unlock\\npress:\");\n        canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 80, 36 + STATUS_BAR_Y_SHIFT, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 95, 36 + STATUS_BAR_Y_SHIFT, &I_Pin_back_arrow_10x8);\n        canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42);\n        canvas_draw_dot(canvas, 17, 61);\n    } else if(view_state == DesktopViewLockedStateUnlockedHintShown) {\n        canvas_set_font(canvas, FontPrimary);\n        elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, \"Unlocked\");\n    }\n}\n\nView* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {\n    furi_assert(locked_view);\n    return locked_view->view;\n}\n\nstatic bool desktop_view_locked_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    bool is_changed = false;\n    const uint32_t press_time = furi_get_tick();\n    DesktopViewLocked* locked_view = context;\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    if(model->view_state == DesktopViewLockedStateUnlockedHintShown &&\n       event->type == InputTypePress) {\n        model->view_state = DesktopViewLockedStateUnlocked;\n        is_changed = true;\n    }\n    const DesktopViewLockedState view_state = model->view_state;\n    const bool pin_locked = model->pin_locked;\n    view_commit_model(locked_view->view, is_changed);\n\n    if(view_state == DesktopViewLockedStateUnlocked) {\n        return false;\n    } else if(view_state == DesktopViewLockedStateLocked && pin_locked) {\n        locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context);\n    } else if(\n        view_state == DesktopViewLockedStateLocked ||\n        view_state == DesktopViewLockedStateLockedHintShown) {\n        if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {\n            locked_view->lock_lastpress = press_time;\n            locked_view->lock_count = 0;\n        }\n\n        desktop_view_locked_update_hint_icon_timeout(locked_view);\n\n        if(event->key == InputKeyBack) {\n            if(event->type == InputTypeShort) {\n                locked_view->lock_lastpress = press_time;\n                locked_view->lock_count++;\n                if(locked_view->lock_count == UNLOCK_CNT) {\n                    locked_view->callback(DesktopLockedEventUnlocked, locked_view->context);\n                }\n            }\n        } else {\n            locked_view->lock_count = 0;\n        }\n\n        locked_view->lock_lastpress = press_time;\n    }\n\n    return true;\n}\n\nDesktopViewLocked* desktop_view_locked_alloc(void) {\n    DesktopViewLocked* locked_view = malloc(sizeof(DesktopViewLocked));\n    locked_view->view = view_alloc();\n    locked_view->timer =\n        furi_timer_alloc(locked_view_timer_callback, FuriTimerTypePeriodic, locked_view);\n\n    view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel));\n    view_set_context(locked_view->view, locked_view);\n    view_set_draw_callback(locked_view->view, desktop_view_locked_draw);\n    view_set_input_callback(locked_view->view, desktop_view_locked_input);\n\n    return locked_view;\n}\n\nvoid desktop_view_locked_free(DesktopViewLocked* locked_view) {\n    furi_assert(locked_view);\n    furi_timer_free(locked_view->timer);\n    view_free(locked_view->view);\n    free(locked_view);\n}\n\nvoid desktop_view_locked_close_doors(DesktopViewLocked* locked_view) {\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    furi_assert(model->view_state == DesktopViewLockedStateLocked);\n    model->view_state = DesktopViewLockedStateDoorsClosing;\n    model->door_offset = DOOR_OFFSET_START;\n    view_commit_model(locked_view->view, true);\n    furi_timer_start(locked_view->timer, DOOR_MOVING_INTERVAL_MS);\n}\n\nvoid desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) {\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    furi_assert(model->view_state == DesktopViewLockedStateUnlocked);\n    model->view_state = DesktopViewLockedStateLocked;\n    model->pin_locked = pin_locked;\n    view_commit_model(locked_view->view, true);\n}\n\nvoid desktop_view_locked_unlock(DesktopViewLocked* locked_view) {\n    locked_view->lock_count = 0;\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    model->view_state = DesktopViewLockedStateUnlockedHintShown;\n    model->pin_locked = false;\n    view_commit_model(locked_view->view, true);\n    furi_timer_start(locked_view->timer, UNLOCKED_HINT_TIMEOUT_MS);\n}\n\nbool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) {\n    DesktopViewLockedModel* model = view_get_model(locked_view->view);\n    const DesktopViewLockedState view_state = model->view_state;\n    view_commit_model(locked_view->view, false);\n    return view_state == DesktopViewLockedStateLockedHintShown;\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_locked.h",
    "content": "#pragma once\n\n#include <desktop/desktop_settings.h>\n#include \"../views/desktop_events.h\"\n#include <gui/view.h>\n\ntypedef struct DesktopViewLocked DesktopViewLocked;\n\ntypedef void (*DesktopViewLockedCallback)(DesktopEvent event, void* context);\n\nvoid desktop_view_locked_set_callback(\n    DesktopViewLocked* locked_view,\n    DesktopViewLockedCallback callback,\n    void* context);\nvoid desktop_view_locked_update(DesktopViewLocked* locked_view);\nView* desktop_view_locked_get_view(DesktopViewLocked* locked_view);\nDesktopViewLocked* desktop_view_locked_alloc(void);\nvoid desktop_view_locked_free(DesktopViewLocked* locked_view);\nvoid desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked);\nvoid desktop_view_locked_unlock(DesktopViewLocked* locked_view);\nvoid desktop_view_locked_close_doors(DesktopViewLocked* locked_view);\nbool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_main.c",
    "content": "#include <gui/gui_i.h>\n#include <gui/view.h>\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <furi.h>\n#include <input/input.h>\n#include <dolphin/dolphin.h>\n\n#include \"desktop_view_main.h\"\n\nstruct DesktopMainView {\n    View* view;\n    DesktopMainViewCallback callback;\n    void* context;\n    FuriTimer* poweroff_timer;\n    bool dummy_mode;\n};\n\n#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 5000\n\nstatic void desktop_main_poweroff_timer_callback(void* context) {\n    DesktopMainView* main_view = context;\n    main_view->callback(DesktopMainEventOpenPowerOff, main_view->context);\n}\n\nvoid desktop_main_set_callback(\n    DesktopMainView* main_view,\n    DesktopMainViewCallback callback,\n    void* context) {\n    furi_assert(main_view);\n    furi_assert(callback);\n    main_view->callback = callback;\n    main_view->context = context;\n}\n\nView* desktop_main_get_view(DesktopMainView* main_view) {\n    furi_assert(main_view);\n    return main_view->view;\n}\n\nvoid desktop_main_set_dummy_mode_state(DesktopMainView* main_view, bool dummy_mode) {\n    furi_assert(main_view);\n    main_view->dummy_mode = dummy_mode;\n}\n\nbool desktop_main_input_callback(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopMainView* main_view = context;\n\n    if(main_view->dummy_mode == false) {\n        if(event->type == InputTypeShort) {\n            if(event->key == InputKeyOk) {\n                main_view->callback(DesktopMainEventOpenMenu, main_view->context);\n            } else if(event->key == InputKeyUp) {\n                main_view->callback(DesktopMainEventOpenLockMenu, main_view->context);\n            } else if(event->key == InputKeyDown) {\n                main_view->callback(DesktopMainEventOpenArchive, main_view->context);\n            } else if(event->key == InputKeyLeft) {\n                main_view->callback(DesktopMainEventOpenFavoriteLeftShort, main_view->context);\n            }\n            // Right key short is handled by animation manager\n        } else if(event->type == InputTypeLong) {\n            if(event->key == InputKeyUp) {\n                main_view->callback(DesktopMainEventLock, main_view->context);\n            } else if(event->key == InputKeyDown) {\n                main_view->callback(DesktopMainEventOpenDebug, main_view->context);\n            } else if(event->key == InputKeyLeft) {\n                main_view->callback(DesktopMainEventOpenFavoriteLeftLong, main_view->context);\n            } else if(event->key == InputKeyRight) {\n                main_view->callback(DesktopMainEventOpenFavoriteRightLong, main_view->context);\n            }\n        }\n    } else {\n        if(event->type == InputTypeShort) {\n            if(event->key == InputKeyOk) {\n                main_view->callback(DesktopDummyEventOpenOk, main_view->context);\n            } else if(event->key == InputKeyUp) {\n                main_view->callback(DesktopMainEventOpenLockMenu, main_view->context);\n            } else if(event->key == InputKeyDown) {\n                main_view->callback(DesktopDummyEventOpenDown, main_view->context);\n            } else if(event->key == InputKeyLeft) {\n                main_view->callback(DesktopDummyEventOpenLeft, main_view->context);\n            }\n            // Right key short is handled by animation manager\n        }\n    }\n\n    if(event->key == InputKeyBack) {\n        if(event->type == InputTypePress) {\n            furi_timer_start(main_view->poweroff_timer, DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT);\n        } else if(event->type == InputTypeRelease) {\n            furi_timer_stop(main_view->poweroff_timer);\n        }\n    }\n\n    return true;\n}\n\nDesktopMainView* desktop_main_alloc(void) {\n    DesktopMainView* main_view = malloc(sizeof(DesktopMainView));\n\n    main_view->view = view_alloc();\n    view_set_context(main_view->view, main_view);\n    view_set_input_callback(main_view->view, desktop_main_input_callback);\n\n    main_view->poweroff_timer =\n        furi_timer_alloc(desktop_main_poweroff_timer_callback, FuriTimerTypeOnce, main_view);\n\n    return main_view;\n}\n\nvoid desktop_main_free(DesktopMainView* main_view) {\n    furi_assert(main_view);\n    view_free(main_view->view);\n    furi_timer_free(main_view->poweroff_timer);\n    free(main_view);\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_main.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include \"desktop_events.h\"\n\ntypedef struct DesktopMainView DesktopMainView;\n\ntypedef void (*DesktopMainViewCallback)(DesktopEvent event, void* context);\n\nvoid desktop_main_set_callback(\n    DesktopMainView* main_view,\n    DesktopMainViewCallback callback,\n    void* context);\n\nView* desktop_main_get_view(DesktopMainView* main_view);\nvoid desktop_main_set_dummy_mode_state(DesktopMainView* main_view, bool dummy_mode);\nDesktopMainView* desktop_main_alloc(void);\nvoid desktop_main_free(DesktopMainView* main_view);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_pin_input.c",
    "content": "#include <gui/canvas.h>\n#include <furi.h>\n#include <gui/view.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <stdint.h>\n\n#include \"desktop_view_pin_input.h\"\n\n#define NO_ACTIVITY_TIMEOUT 15000\n\n#define PIN_CELL_WIDTH 13\n#define DEFAULT_PIN_X  64\n#define DEFAULT_PIN_Y  32\n\n#define MIN_PIN_LENGTH DESKTOP_PIN_CODE_MIN_LEN\n#define MAX_PIN_LENGTH DESKTOP_PIN_CODE_MAX_LEN\n\nstruct DesktopViewPinInput {\n    View* view;\n    DesktopViewPinInputCallback back_callback;\n    DesktopViewPinInputCallback timeout_callback;\n    DesktopViewPinInputDoneCallback done_callback;\n    void* context;\n    FuriTimer* timer;\n};\n\ntypedef struct {\n    DesktopPinCode pin;\n    bool pin_hidden;\n    bool locked_input;\n    uint8_t pin_x;\n    uint8_t pin_y;\n    const char* primary_str;\n    uint8_t primary_str_x;\n    uint8_t primary_str_y;\n    const char* secondary_str;\n    uint8_t secondary_str_x;\n    uint8_t secondary_str_y;\n    const char* tertiary_str;\n    uint8_t tertiary_str_x;\n    uint8_t tertiary_str_y;\n    const char* button_label;\n} DesktopViewPinInputModel;\n\nstatic bool desktop_view_pin_input_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopViewPinInput* pin_input = context;\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n\n    bool call_back_callback = false;\n    bool call_done_callback = false;\n    DesktopPinCode pin_code = {0};\n\n    if(event->type == InputTypeShort) {\n        switch(event->key) {\n        case InputKeyRight:\n        case InputKeyLeft:\n        case InputKeyDown:\n        case InputKeyUp:\n            if(!model->locked_input) {\n                if(model->pin.length < MAX_PIN_LENGTH) {\n                    model->pin.data[model->pin.length++] = event->key;\n                }\n            }\n            break;\n        case InputKeyOk:\n            if(model->pin.length >= MIN_PIN_LENGTH) {\n                call_done_callback = true;\n                pin_code = model->pin;\n            }\n            break;\n        case InputKeyBack:\n            if(!model->locked_input) {\n                if(model->pin.length > 0) {\n                    model->pin.length = 0;\n                } else {\n                    call_back_callback = true;\n                }\n            }\n            break;\n        default:\n            furi_crash();\n            break;\n        }\n    }\n    view_commit_model(pin_input->view, true);\n\n    if(call_done_callback && pin_input->done_callback) {\n        pin_input->done_callback(&pin_code, pin_input->context);\n    } else if(call_back_callback && pin_input->back_callback) {\n        pin_input->back_callback(pin_input->context);\n    }\n\n    furi_timer_start(pin_input->timer, NO_ACTIVITY_TIMEOUT);\n\n    return true;\n}\n\nstatic void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInputModel* model) {\n    furi_assert(canvas);\n    furi_assert(model);\n\n    uint8_t draw_pin_size = MAX(MIN_PIN_LENGTH, model->pin.length + 1);\n    if(model->locked_input || (model->pin.length == MAX_PIN_LENGTH)) {\n        draw_pin_size = model->pin.length;\n    }\n\n    uint8_t x = model->pin_x - (draw_pin_size * (PIN_CELL_WIDTH - 1)) / 2;\n    uint8_t y = model->pin_y - (PIN_CELL_WIDTH / 2);\n\n    for(int i = 0; i < draw_pin_size; ++i) {\n        canvas_draw_frame(canvas, x, y, PIN_CELL_WIDTH, PIN_CELL_WIDTH);\n        if(i < model->pin.length) {\n            if(model->pin_hidden) {\n                canvas_draw_icon(canvas, x + 3, y + 3, &I_Pin_star_7x7);\n            } else {\n                switch(model->pin.data[i]) {\n                case InputKeyDown:\n                    canvas_draw_icon_ex(\n                        canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180);\n                    break;\n                case InputKeyUp:\n                    canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0);\n                    break;\n                case InputKeyLeft:\n                    canvas_draw_icon_ex(\n                        canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270);\n                    break;\n                case InputKeyRight:\n                    canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90);\n                    break;\n                default:\n                    furi_crash();\n                    break;\n                }\n            }\n        } else if(i == model->pin.length) {\n            canvas_draw_icon(canvas, x + 4, y + PIN_CELL_WIDTH + 1, &I_Pin_pointer_5x3);\n        }\n        x += PIN_CELL_WIDTH - 1;\n    }\n}\n\nstatic void desktop_view_pin_input_draw(Canvas* canvas, void* context) {\n    furi_assert(canvas);\n    furi_assert(context);\n\n    canvas_set_font(canvas, FontSecondary);\n    DesktopViewPinInputModel* model = context;\n    desktop_view_pin_input_draw_cells(canvas, model);\n\n    if((model->pin.length > 0) && !model->locked_input) {\n        canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8);\n        canvas_draw_str(canvas, 16, 60, \"= clear\");\n    }\n\n    if(model->button_label && ((model->pin.length >= MIN_PIN_LENGTH) || model->locked_input)) {\n        elements_button_center(canvas, model->button_label);\n    }\n\n    if(model->primary_str) {\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str(canvas, model->primary_str_x, model->primary_str_y, model->primary_str);\n        canvas_set_font(canvas, FontSecondary);\n    }\n\n    if(model->secondary_str) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str(\n            canvas, model->secondary_str_x, model->secondary_str_y, model->secondary_str);\n    }\n\n    if(model->tertiary_str && model->pin.length == 0) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(\n            canvas,\n            model->tertiary_str_x,\n            model->tertiary_str_y,\n            AlignCenter,\n            AlignBottom,\n            model->tertiary_str);\n    }\n}\n\nvoid desktop_view_pin_input_timer_callback(void* context) {\n    DesktopViewPinInput* pin_input = context;\n\n    if(pin_input->timeout_callback) {\n        pin_input->timeout_callback(pin_input->context);\n    }\n}\n\nstatic void desktop_view_pin_input_enter(void* context) {\n    DesktopViewPinInput* pin_input = context;\n    furi_timer_start(pin_input->timer, NO_ACTIVITY_TIMEOUT);\n}\n\nstatic void desktop_view_pin_input_exit(void* context) {\n    DesktopViewPinInput* pin_input = context;\n    furi_timer_stop(pin_input->timer);\n}\n\nDesktopViewPinInput* desktop_view_pin_input_alloc(void) {\n    DesktopViewPinInput* pin_input = malloc(sizeof(DesktopViewPinInput));\n    pin_input->view = view_alloc();\n    view_allocate_model(pin_input->view, ViewModelTypeLocking, sizeof(DesktopViewPinInputModel));\n    view_set_context(pin_input->view, pin_input);\n    view_set_draw_callback(pin_input->view, desktop_view_pin_input_draw);\n    view_set_input_callback(pin_input->view, desktop_view_pin_input_input);\n    pin_input->timer =\n        furi_timer_alloc(desktop_view_pin_input_timer_callback, FuriTimerTypeOnce, pin_input);\n    view_set_enter_callback(pin_input->view, desktop_view_pin_input_enter);\n    view_set_exit_callback(pin_input->view, desktop_view_pin_input_exit);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->pin_x = DEFAULT_PIN_X;\n    model->pin_y = DEFAULT_PIN_Y;\n    model->pin.length = 0;\n    view_commit_model(pin_input->view, false);\n\n    return pin_input;\n}\n\nvoid desktop_view_pin_input_free(DesktopViewPinInput* pin_input) {\n    furi_assert(pin_input);\n\n    furi_timer_free(pin_input->timer);\n\n    view_free(pin_input->view);\n    free(pin_input);\n}\n\nvoid desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->locked_input = true;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->locked_input = false;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const DesktopPinCode* pin) {\n    furi_assert(pin_input);\n    furi_assert(pin);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->pin = *pin;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->pin.length = 0;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->pin_hidden = pin_hidden;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->button_label = label;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_label_primary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->primary_str = label;\n    model->primary_str_x = x;\n    model->primary_str_y = y;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_label_secondary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->secondary_str = label;\n    model->secondary_str_x = x;\n    model->secondary_str_y = y;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_label_tertiary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->tertiary_str = label;\n    model->tertiary_str_x = x;\n    model->tertiary_str_y = y;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y) {\n    furi_assert(pin_input);\n\n    DesktopViewPinInputModel* model = view_get_model(pin_input->view);\n    model->pin_x = x;\n    model->pin_y = y;\n    view_commit_model(pin_input->view, true);\n}\n\nvoid desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context) {\n    furi_assert(pin_input);\n    pin_input->context = context;\n}\n\nvoid desktop_view_pin_input_set_timeout_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputCallback callback) {\n    furi_assert(pin_input);\n    pin_input->timeout_callback = callback;\n}\n\nvoid desktop_view_pin_input_set_back_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputCallback callback) {\n    furi_assert(pin_input);\n    pin_input->back_callback = callback;\n}\n\nvoid desktop_view_pin_input_set_done_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputDoneCallback callback) {\n    furi_assert(pin_input);\n    pin_input->done_callback = callback;\n}\n\nView* desktop_view_pin_input_get_view(DesktopViewPinInput* pin_input) {\n    furi_assert(pin_input);\n    return pin_input->view;\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_pin_input.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\n#include \"../helpers/pin_code.h\"\n\ntypedef void (*DesktopViewPinInputCallback)(void*);\ntypedef void (*DesktopViewPinInputDoneCallback)(const DesktopPinCode* pin_code, void*);\ntypedef struct DesktopViewPinInput DesktopViewPinInput;\n\nDesktopViewPinInput* desktop_view_pin_input_alloc(void);\nvoid desktop_view_pin_input_free(DesktopViewPinInput*);\n\nvoid desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const DesktopPinCode* pin_code);\nvoid desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input);\nvoid desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden);\nvoid desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label);\nvoid desktop_view_pin_input_set_label_primary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label);\nvoid desktop_view_pin_input_set_label_secondary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label);\nvoid desktop_view_pin_input_set_label_tertiary(\n    DesktopViewPinInput* pin_input,\n    uint8_t x,\n    uint8_t y,\n    const char* label);\nvoid desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y);\nView* desktop_view_pin_input_get_view(DesktopViewPinInput*);\nvoid desktop_view_pin_input_set_done_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputDoneCallback callback);\nvoid desktop_view_pin_input_set_back_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputCallback callback);\nvoid desktop_view_pin_input_set_timeout_callback(\n    DesktopViewPinInput* pin_input,\n    DesktopViewPinInputCallback callback);\nvoid desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context);\nvoid desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input);\nvoid desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_pin_timeout.c",
    "content": "#include <furi.h>\n\n#include <gui/canvas.h>\n#include <gui/view.h>\n\n#include \"desktop_view_pin_timeout.h\"\n\nstruct DesktopViewPinTimeout {\n    View* view;\n    FuriTimer* timer;\n    DesktopViewPinTimeoutDoneCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint32_t time_left;\n} DesktopViewPinTimeoutModel;\n\nvoid desktop_view_pin_timeout_set_callback(\n    DesktopViewPinTimeout* instance,\n    DesktopViewPinTimeoutDoneCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic void desktop_view_pin_timeout_timer_callback(void* context) {\n    DesktopViewPinTimeout* instance = context;\n    bool stop = false;\n\n    DesktopViewPinTimeoutModel* model = view_get_model(instance->view);\n    if(model->time_left > 0) {\n        --model->time_left;\n    } else {\n        stop = true;\n    }\n    view_commit_model(instance->view, true);\n\n    if(stop) {\n        furi_timer_stop(instance->timer);\n        instance->callback(instance->context);\n    }\n}\n\nstatic bool desktop_view_pin_timeout_input(InputEvent* event, void* context) {\n    UNUSED(event);\n    UNUSED(context);\n    return true;\n}\n\nstatic void desktop_view_pin_timeout_draw(Canvas* canvas, void* _model) {\n    furi_assert(canvas);\n    furi_assert(_model);\n\n    DesktopViewPinTimeoutModel* model = _model;\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 36, 31, \"Wrong PIN!\");\n\n    canvas_set_font(canvas, FontSecondary);\n    char str[30] = {0};\n    snprintf(str, sizeof(str), \"Timeout: %lus\", model->time_left);\n    canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, str);\n}\n\nvoid desktop_view_pin_timeout_free(DesktopViewPinTimeout* instance) {\n    view_free(instance->view);\n    furi_timer_free(instance->timer);\n\n    free(instance);\n}\n\nDesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void) {\n    DesktopViewPinTimeout* instance = malloc(sizeof(DesktopViewPinTimeout));\n    instance->timer =\n        furi_timer_alloc(desktop_view_pin_timeout_timer_callback, FuriTimerTypePeriodic, instance);\n\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(DesktopViewPinTimeoutModel));\n\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, desktop_view_pin_timeout_draw);\n    view_set_input_callback(instance->view, desktop_view_pin_timeout_input);\n\n    return instance;\n}\n\nvoid desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left) {\n    furi_assert(instance);\n\n    DesktopViewPinTimeoutModel* model = view_get_model(instance->view);\n    // no race - always called when timer is stopped\n    model->time_left = time_left;\n    view_commit_model(instance->view, true);\n\n    furi_timer_start(instance->timer, 1000);\n}\n\nView* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance) {\n    furi_assert(instance);\n\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_pin_timeout.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <gui/view.h>\n\ntypedef void (*DesktopViewPinTimeoutDoneCallback)(void*);\ntypedef struct DesktopViewPinTimeout DesktopViewPinTimeout;\n\nvoid desktop_view_pin_timeout_set_callback(\n    DesktopViewPinTimeout* instance,\n    DesktopViewPinTimeoutDoneCallback callback,\n    void* context);\nDesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void);\nvoid desktop_view_pin_timeout_free(DesktopViewPinTimeout*);\nvoid desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left);\nView* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance);\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_slideshow.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/elements.h>\n\n#include \"desktop_view_slideshow.h\"\n#include \"../helpers/slideshow.h\"\n\n#define DESKTOP_SLIDESHOW_POWEROFF_SHORT 5000\n#define DESKTOP_SLIDESHOW_POWEROFF_LONG  (60 * 60 * 1000)\n\nstruct DesktopSlideshowView {\n    View* view;\n    DesktopSlideshowViewCallback callback;\n    void* context;\n    FuriTimer* timer;\n};\n\ntypedef struct {\n    uint8_t page;\n    Slideshow* slideshow;\n} DesktopSlideshowViewModel;\n\nstatic void desktop_view_slideshow_draw(Canvas* canvas, void* model) {\n    DesktopSlideshowViewModel* m = model;\n\n    canvas_clear(canvas);\n    if(slideshow_is_loaded(m->slideshow)) {\n        slideshow_draw(m->slideshow, canvas, 0, 0);\n    }\n}\n\nstatic bool desktop_view_slideshow_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    DesktopSlideshowView* instance = context;\n\n    DesktopSlideshowViewModel* model = view_get_model(instance->view);\n    bool update_view = false;\n    if(event->type == InputTypeShort) {\n        bool end_slideshow = false;\n        switch(event->key) {\n        case InputKeyLeft:\n            slideshow_goback(model->slideshow);\n            break;\n        case InputKeyRight:\n        case InputKeyOk:\n            end_slideshow = !slideshow_advance(model->slideshow);\n            break;\n        case InputKeyBack:\n            end_slideshow = true;\n        default:\n            break;\n        }\n        if(end_slideshow) {\n            instance->callback(DesktopSlideshowCompleted, instance->context);\n        }\n        update_view = true;\n    } else if(event->key == InputKeyOk && instance->timer) {\n        if(event->type == InputTypePress) {\n            furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT);\n        } else if(event->type == InputTypeRelease) {\n            furi_timer_stop(instance->timer);\n            if(!slideshow_is_one_page(model->slideshow)) {\n                furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);\n            }\n        }\n    }\n    view_commit_model(instance->view, update_view);\n\n    return true;\n}\n\nstatic void desktop_first_start_timer_callback(void* context) {\n    DesktopSlideshowView* instance = context;\n    instance->callback(DesktopSlideshowPoweroff, instance->context);\n}\n\nstatic void desktop_view_slideshow_enter(void* context) {\n    DesktopSlideshowView* instance = context;\n\n    furi_assert(instance->timer == NULL);\n    instance->timer =\n        furi_timer_alloc(desktop_first_start_timer_callback, FuriTimerTypeOnce, instance);\n\n    DesktopSlideshowViewModel* model = view_get_model(instance->view);\n    model->slideshow = slideshow_alloc();\n    if(!slideshow_load(model->slideshow, SLIDESHOW_FS_PATH)) {\n        instance->callback(DesktopSlideshowCompleted, instance->context);\n    } else if(!slideshow_is_one_page(model->slideshow)) {\n        furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);\n    }\n    view_commit_model(instance->view, false);\n}\n\nstatic void desktop_view_slideshow_exit(void* context) {\n    DesktopSlideshowView* instance = context;\n\n    furi_timer_stop(instance->timer);\n    furi_timer_free(instance->timer);\n    instance->timer = NULL;\n\n    DesktopSlideshowViewModel* model = view_get_model(instance->view);\n    slideshow_free(model->slideshow);\n    view_commit_model(instance->view, false);\n}\n\nDesktopSlideshowView* desktop_view_slideshow_alloc(void) {\n    DesktopSlideshowView* instance = malloc(sizeof(DesktopSlideshowView));\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DesktopSlideshowViewModel));\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, (ViewDrawCallback)desktop_view_slideshow_draw);\n    view_set_input_callback(instance->view, desktop_view_slideshow_input);\n    view_set_enter_callback(instance->view, desktop_view_slideshow_enter);\n    view_set_exit_callback(instance->view, desktop_view_slideshow_exit);\n\n    return instance;\n}\n\nvoid desktop_view_slideshow_free(DesktopSlideshowView* instance) {\n    furi_assert(instance);\n\n    view_free(instance->view);\n    free(instance);\n}\n\nView* desktop_view_slideshow_get_view(DesktopSlideshowView* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n\nvoid desktop_view_slideshow_set_callback(\n    DesktopSlideshowView* instance,\n    DesktopSlideshowViewCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n    instance->callback = callback;\n    instance->context = context;\n}\n"
  },
  {
    "path": "applications/services/desktop/views/desktop_view_slideshow.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\n#include \"desktop_events.h\"\n#include \"../helpers/slideshow_filename.h\"\n#include <storage/storage.h>\n\n#define SLIDESHOW_FS_PATH INT_PATH(SLIDESHOW_FILE_NAME)\n\ntypedef struct DesktopSlideshowView DesktopSlideshowView;\n\ntypedef void (*DesktopSlideshowViewCallback)(DesktopEvent event, void* context);\n\nDesktopSlideshowView* desktop_view_slideshow_alloc(void);\n\nvoid desktop_view_slideshow_free(DesktopSlideshowView* main_view);\n\nView* desktop_view_slideshow_get_view(DesktopSlideshowView* main_view);\n\nvoid desktop_view_slideshow_set_callback(\n    DesktopSlideshowView* main_view,\n    DesktopSlideshowViewCallback callback,\n    void* context);\n"
  },
  {
    "path": "applications/services/dialogs/application.fam",
    "content": "App(\n    appid=\"dialogs\",\n    name=\"DialogsSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"dialogs_srv\",\n    cdefines=[\"SRV_DIALOGS\"],\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    order=40,\n    sdk_headers=[\"dialogs.h\"],\n)\n"
  },
  {
    "path": "applications/services/dialogs/dialogs.c",
    "content": "#include \"dialogs/dialogs_message.h\"\n#include <toolbox/api_lock.h>\n#include \"dialogs_module_file_browser.h\"\n#include \"dialogs_module_message.h\"\n\nvoid dialog_file_browser_set_basic_options(\n    DialogsFileBrowserOptions* options,\n    const char* extension,\n    const Icon* icon) {\n    furi_check(options);\n    options->extension = extension;\n    options->base_path = NULL;\n    options->skip_assets = true;\n    options->hide_dot_files = true;\n    options->icon = icon;\n    options->hide_ext = true;\n    options->item_loader_callback = NULL;\n    options->item_loader_context = NULL;\n}\n\nstatic DialogsApp* dialogs_app_alloc(void) {\n    DialogsApp* app = malloc(sizeof(DialogsApp));\n    app->message_queue = furi_message_queue_alloc(8, sizeof(DialogsAppMessage));\n\n    return app;\n}\n\nstatic void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) {\n    UNUSED(app);\n    switch(message->command) {\n    case DialogsAppCommandFileBrowser:\n        message->return_data->bool_value =\n            dialogs_app_process_module_file_browser(&message->data->file_browser);\n        break;\n    case DialogsAppCommandDialog:\n        message->return_data->dialog_value =\n            dialogs_app_process_module_message(&message->data->dialog);\n        break;\n    }\n    api_lock_unlock(message->lock);\n}\n\nint32_t dialogs_srv(void* p) {\n    UNUSED(p);\n    DialogsApp* app = dialogs_app_alloc();\n    furi_record_create(RECORD_DIALOGS, app);\n\n    DialogsAppMessage message;\n    while(1) {\n        if(furi_message_queue_get(app->message_queue, &message, FuriWaitForever) == FuriStatusOk) {\n            dialogs_app_process_message(app, &message);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/dialogs/dialogs.h",
    "content": "#pragma once\n#include <furi.h>\n#include <gui/canvas.h>\n#include <gui/modules/file_browser.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/****************** COMMON ******************/\n\n#define RECORD_DIALOGS \"dialogs\"\n\ntypedef struct DialogsApp DialogsApp;\n\n/****************** FILE BROWSER ******************/\n\n/**\n * File browser dialog extra options.\n * This can be default-initialized using {@link dialog_file_browser_set_basic_options}.\n * @param extension file extension to be offered for selection\n * @param base_path root folder path for navigation with back key\n * @param skip_assets true - do not show assets folders\n * @param hide_dot_files true - hide dot files\n * @param icon file icon pointer, NULL for default icon\n * @param hide_ext true - hide extensions for files\n * @param item_loader_callback callback function for providing custom icon & entry name\n * @param hide_ext callback context\n */\ntypedef struct {\n    const char* extension;\n    const char* base_path;\n    bool skip_assets;\n    bool hide_dot_files;\n    const Icon* icon;\n    bool hide_ext;\n    FileBrowserLoadItemCallback item_loader_callback;\n    void* item_loader_context;\n} DialogsFileBrowserOptions;\n\n/**\n * Initialize file browser dialog options and set default values.\n * This is guaranteed to initialize all fields\n * so it is safe to pass pointer to uninitialized {@code options}\n * and assume that the data behind it becomes fully initialized after the call.\n * @param options pointer to options structure\n * @param extension file extension to filter\n * @param icon file icon pointer, NULL for default icon\n */\nvoid dialog_file_browser_set_basic_options(\n    DialogsFileBrowserOptions* options,\n    const char* extension,\n    const Icon* icon);\n\n/**\n * Shows and processes the file browser dialog\n * @param context api pointer\n * @param result_path selected file path string pointer\n * @param path preselected file path string pointer\n * @param options file browser dialog extra options, may be null\n * @return bool whether a file was selected\n */\nbool dialog_file_browser_show(\n    DialogsApp* context,\n    FuriString* result_path,\n    FuriString* path,\n    const DialogsFileBrowserOptions* options);\n\n/****************** MESSAGE ******************/\n\n/**\n * Message result type\n */\ntypedef enum {\n    DialogMessageButtonBack,\n    DialogMessageButtonLeft,\n    DialogMessageButtonCenter,\n    DialogMessageButtonRight,\n} DialogMessageButton;\n\n/**\n * Message struct\n */\ntypedef struct DialogMessage DialogMessage;\n\n/**\n * Allocate and fill message\n * @return DialogMessage* \n */\nDialogMessage* dialog_message_alloc(void);\n\n/**\n * Free message struct\n * @param message message pointer\n */\nvoid dialog_message_free(DialogMessage* message);\n\n/**\n * Set message text\n * @param message message pointer\n * @param text text, can be NULL if you don't want to display the text\n * @param x x position\n * @param y y position\n * @param horizontal horizontal alignment\n * @param vertical vertical alignment\n */\nvoid dialog_message_set_text(\n    DialogMessage* message,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/**\n * Set message header\n * @param message message pointer\n * @param text text, can be NULL if you don't want to display the header\n * @param x x position\n * @param y y position\n * @param horizontal horizontal alignment\n * @param vertical vertical alignment\n */\nvoid dialog_message_set_header(\n    DialogMessage* message,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/**\n * Set message icon\n * @param message message pointer\n * @param icon icon pointer, can be NULL if you don't want to display the icon\n * @param x x position\n * @param y y position\n */\nvoid dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y);\n\n/**\n * Set message buttons text, button text can be NULL if you don't want to display and process some buttons\n * @param message message pointer\n * @param left left button text, can be NULL if you don't want to display the left button\n * @param center center button text, can be NULL if you don't want to display the center button\n * @param right right button text, can be NULL if you don't want to display the right button\n */\nvoid dialog_message_set_buttons(\n    DialogMessage* message,\n    const char* left,\n    const char* center,\n    const char* right);\n\n/**\n * Show message from filled struct\n * @param context api pointer\n * @param message message struct pointer to be shown\n * @return DialogMessageButton type\n */\nDialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message);\n\n/**\n * Show SD error message (with question sign)\n * @param context \n * @param error_text \n */\nvoid dialog_message_show_storage_error(DialogsApp* context, const char* error_text);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_api.c",
    "content": "#include \"dialogs_message.h\"\n#include <toolbox/api_lock.h>\n#include <assets_icons.h>\n#include <storage/storage.h>\n\n/****************** File browser ******************/\n\nbool dialog_file_browser_show(\n    DialogsApp* context,\n    FuriString* result_path,\n    FuriString* path,\n    const DialogsFileBrowserOptions* options) {\n    FuriApiLock lock = api_lock_alloc_locked();\n    furi_check(lock != NULL);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriString* base_path = furi_string_alloc();\n\n    if(options && options->base_path) {\n        furi_string_set(base_path, options->base_path);\n        storage_common_resolve_path_and_ensure_app_directory(storage, base_path);\n    }\n\n    if(result_path) {\n        storage_common_resolve_path_and_ensure_app_directory(storage, result_path);\n    }\n\n    if(path) {\n        storage_common_resolve_path_and_ensure_app_directory(storage, path);\n    }\n\n    DialogsAppData data = {\n        .file_browser = {\n            .extension = options ? options->extension : \"\",\n            .result_path = result_path,\n            .file_icon = options ? options->icon : NULL,\n            .hide_ext = options ? options->hide_ext : true,\n            .skip_assets = options ? options->skip_assets : true,\n            .hide_dot_files = options ? options->hide_dot_files : true,\n            .preselected_filename = path,\n            .item_callback = options ? options->item_loader_callback : NULL,\n            .item_callback_context = options ? options->item_loader_context : NULL,\n            .base_path = furi_string_get_cstr(base_path),\n        }};\n\n    DialogsAppReturn return_data;\n    DialogsAppMessage message = {\n        .lock = lock,\n        .command = DialogsAppCommandFileBrowser,\n        .data = &data,\n        .return_data = &return_data,\n    };\n\n    furi_check(\n        furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    api_lock_wait_unlock_and_free(lock);\n\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(base_path);\n\n    return return_data.bool_value;\n}\n\n/****************** Message ******************/\n\nDialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {\n    furi_check(context);\n\n    FuriApiLock lock = api_lock_alloc_locked();\n    furi_check(lock != NULL);\n\n    DialogsAppData data = {\n        .dialog = {\n            .message = dialog_message,\n        }};\n\n    DialogsAppReturn return_data;\n    DialogsAppMessage message = {\n        .lock = lock,\n        .command = DialogsAppCommandDialog,\n        .data = &data,\n        .return_data = &return_data,\n    };\n\n    furi_check(\n        furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);\n    api_lock_wait_unlock_and_free(lock);\n\n    return return_data.dialog_value;\n}\n\n/****************** Storage error ******************/\n\nvoid dialog_message_show_storage_error(DialogsApp* context, const char* error_text) {\n    furi_check(context);\n\n    DialogMessage* message = dialog_message_alloc();\n    dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter);\n    dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6);\n    dialog_message_set_buttons(message, \"Back\", NULL, NULL);\n    dialog_message_show(context, message);\n    dialog_message_free(message);\n}\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_i.h",
    "content": "#pragma once\n#include \"dialogs.h\"\n#include \"dialogs_message.h\"\n#include <gui/view_holder.h>\n#include <gui/modules/file_browser.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nstruct DialogsApp {\n    FuriMessageQueue* message_queue;\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_message.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"dialogs_i.h\"\n#include <toolbox/api_lock.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    const char* extension;\n    bool skip_assets;\n    bool hide_ext;\n    bool hide_dot_files;\n    const Icon* file_icon;\n    FuriString* result_path;\n    FuriString* preselected_filename;\n    FileBrowserLoadItemCallback item_callback;\n    void* item_callback_context;\n    const char* base_path;\n} DialogsAppMessageDataFileBrowser;\n\ntypedef struct {\n    const DialogMessage* message;\n} DialogsAppMessageDataDialog;\n\ntypedef union {\n    DialogsAppMessageDataFileBrowser file_browser;\n    DialogsAppMessageDataDialog dialog;\n} DialogsAppData;\n\ntypedef union {\n    bool bool_value;\n    DialogMessageButton dialog_value;\n} DialogsAppReturn;\n\ntypedef enum {\n    DialogsAppCommandFileBrowser,\n    DialogsAppCommandDialog,\n} DialogsAppCommand;\n\ntypedef struct {\n    FuriApiLock lock;\n    DialogsAppCommand command;\n    DialogsAppData* data;\n    DialogsAppReturn* return_data;\n} DialogsAppMessage;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_module_file_browser.c",
    "content": "#include \"dialogs_message.h\"\n#include <gui/modules/file_browser.h>\n#include <toolbox/api_lock.h>\n\ntypedef struct {\n    FuriApiLock lock;\n    bool result;\n} DialogsAppFileBrowserContext;\n\nstatic void dialogs_app_file_browser_back_callback(void* context) {\n    furi_assert(context);\n    DialogsAppFileBrowserContext* file_browser_context = context;\n    file_browser_context->result = false;\n    api_lock_unlock(file_browser_context->lock);\n}\n\nstatic void dialogs_app_file_browser_callback(void* context) {\n    furi_assert(context);\n    DialogsAppFileBrowserContext* file_browser_context = context;\n    file_browser_context->result = true;\n    api_lock_unlock(file_browser_context->lock);\n}\n\nbool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) {\n    bool ret = false;\n    Gui* gui = furi_record_open(RECORD_GUI);\n\n    DialogsAppFileBrowserContext* file_browser_context =\n        malloc(sizeof(DialogsAppFileBrowserContext));\n    file_browser_context->lock = api_lock_alloc_locked();\n\n    ViewHolder* view_holder = view_holder_alloc();\n    view_holder_attach_to_gui(view_holder, gui);\n    view_holder_set_back_callback(\n        view_holder, dialogs_app_file_browser_back_callback, file_browser_context);\n\n    FileBrowser* file_browser = file_browser_alloc(data->result_path);\n    file_browser_set_callback(\n        file_browser, dialogs_app_file_browser_callback, file_browser_context);\n    file_browser_configure(\n        file_browser,\n        data->extension,\n        data->base_path,\n        data->skip_assets,\n        data->hide_dot_files,\n        data->file_icon,\n        data->hide_ext);\n    file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context);\n    file_browser_start(file_browser, data->preselected_filename);\n\n    view_holder_set_view(view_holder, file_browser_get_view(file_browser));\n    api_lock_wait_unlock(file_browser_context->lock);\n\n    ret = file_browser_context->result;\n\n    view_holder_set_view(view_holder, NULL);\n    file_browser_stop(file_browser);\n\n    file_browser_free(file_browser);\n    view_holder_free(view_holder);\n\n    api_lock_free(file_browser_context->lock);\n    free(file_browser_context);\n\n    furi_record_close(RECORD_GUI);\n\n    return ret;\n}\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_module_file_browser.h",
    "content": "#pragma once\n#include \"dialogs_message.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_module_message.c",
    "content": "#include \"dialogs.h\"\n#include \"dialogs_message.h\"\n#include <gui/view_holder.h>\n#include <toolbox/api_lock.h>\n#include <gui/modules/dialog_ex.h>\n\ntypedef struct {\n    FuriApiLock lock;\n    DialogMessageButton result;\n} DialogsAppMessageContext;\n\nstruct DialogMessage {\n    const char* header_text;\n    uint8_t header_text_x;\n    uint8_t header_text_y;\n    Align header_horizontal;\n    Align header_vertical;\n    const char* dialog_text;\n    uint8_t dialog_text_x;\n    uint8_t dialog_text_y;\n    Align dialog_text_horizontal;\n    Align dialog_text_vertical;\n    const Icon* icon;\n    uint8_t icon_x;\n    uint8_t icon_y;\n    const char* left_button_text;\n    const char* center_button_text;\n    const char* right_button_text;\n};\n\nstatic void dialogs_app_message_back_callback(void* context) {\n    furi_assert(context);\n    DialogsAppMessageContext* message_context = context;\n    message_context->result = DialogMessageButtonBack;\n    api_lock_unlock(message_context->lock);\n}\n\nstatic void dialogs_app_message_callback(DialogExResult result, void* context) {\n    furi_assert(context);\n    DialogsAppMessageContext* message_context = context;\n    switch(result) {\n    case DialogExResultLeft:\n        message_context->result = DialogMessageButtonLeft;\n        break;\n    case DialogExResultRight:\n        message_context->result = DialogMessageButtonRight;\n        break;\n    case DialogExResultCenter:\n        message_context->result = DialogMessageButtonCenter;\n        break;\n    default:\n        break;\n    }\n    api_lock_unlock(message_context->lock);\n}\n\nDialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {\n    DialogMessageButton ret = DialogMessageButtonBack;\n    Gui* gui = furi_record_open(RECORD_GUI);\n    const DialogMessage* message = data->message;\n    DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext));\n    message_context->lock = api_lock_alloc_locked();\n\n    ViewHolder* view_holder = view_holder_alloc();\n    view_holder_attach_to_gui(view_holder, gui);\n    view_holder_set_back_callback(view_holder, dialogs_app_message_back_callback, message_context);\n\n    DialogEx* dialog_ex = dialog_ex_alloc();\n    dialog_ex_set_result_callback(dialog_ex, dialogs_app_message_callback);\n    dialog_ex_set_context(dialog_ex, message_context);\n    dialog_ex_set_header(\n        dialog_ex,\n        message->header_text,\n        message->header_text_x,\n        message->header_text_y,\n        message->header_horizontal,\n        message->header_vertical);\n    dialog_ex_set_text(\n        dialog_ex,\n        message->dialog_text,\n        message->dialog_text_x,\n        message->dialog_text_y,\n        message->dialog_text_horizontal,\n        message->dialog_text_vertical);\n    dialog_ex_set_icon(dialog_ex, message->icon_x, message->icon_y, message->icon);\n    dialog_ex_set_left_button_text(dialog_ex, message->left_button_text);\n    dialog_ex_set_center_button_text(dialog_ex, message->center_button_text);\n    dialog_ex_set_right_button_text(dialog_ex, message->right_button_text);\n\n    view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));\n    api_lock_wait_unlock(message_context->lock);\n\n    ret = message_context->result;\n\n    view_holder_set_view(view_holder, NULL);\n    view_holder_free(view_holder);\n    dialog_ex_free(dialog_ex);\n    api_lock_free(message_context->lock);\n    free(message_context);\n    furi_record_close(RECORD_GUI);\n\n    return ret;\n}\n\nDialogMessage* dialog_message_alloc(void) {\n    DialogMessage* message = malloc(sizeof(DialogMessage));\n    return message;\n}\n\nvoid dialog_message_free(DialogMessage* message) {\n    furi_check(message);\n    free(message);\n}\n\nvoid dialog_message_set_text(\n    DialogMessage* message,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(message);\n\n    message->dialog_text = text;\n    message->dialog_text_x = x;\n    message->dialog_text_y = y;\n    message->dialog_text_horizontal = horizontal;\n    message->dialog_text_vertical = vertical;\n}\n\nvoid dialog_message_set_header(\n    DialogMessage* message,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(message);\n\n    message->header_text = text;\n    message->header_text_x = x;\n    message->header_text_y = y;\n    message->header_horizontal = horizontal;\n    message->header_vertical = vertical;\n}\n\nvoid dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y) {\n    furi_check(message);\n\n    message->icon = icon;\n    message->icon_x = x;\n    message->icon_y = y;\n}\n\nvoid dialog_message_set_buttons(\n    DialogMessage* message,\n    const char* left,\n    const char* center,\n    const char* right) {\n    furi_check(message);\n\n    message->left_button_text = left;\n    message->center_button_text = center;\n    message->right_button_text = right;\n}\n"
  },
  {
    "path": "applications/services/dialogs/dialogs_module_message.h",
    "content": "#pragma once\n#include \"dialogs_message.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nDialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dolphin/application.fam",
    "content": "App(\n    appid=\"dolphin\",\n    name=\"DolphinSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"dolphin_srv\",\n    cdefines=[\"SRV_DOLPHIN\"],\n    stack_size=1 * 1024,\n    order=50,\n    sdk_headers=[\"dolphin.h\"],\n)\n"
  },
  {
    "path": "applications/services/dolphin/dolphin.c",
    "content": "#include \"dolphin_i.h\"\n\n#include <furi_hal.h>\n#include <storage/storage.h>\n\n#define TAG \"Dolphin\"\n\n#define DOLPHIN_LOCK_EVENT_FLAG (0x1)\n#define EVENT_QUEUE_SIZE        (8)\n\n#define SECONDS_IN_TICKS(x)    ((x) * 1000UL)\n#define MINUTES_IN_TICKS(x)    (SECONDS_IN_TICKS(x) * 60UL)\n#define HOURS_IN_TICKS(x)      (MINUTES_IN_TICKS(x) * 60UL)\n#define DATE_IN_TICKS(h, m, s) (HOURS_IN_TICKS(h) + MINUTES_IN_TICKS(m) + SECONDS_IN_TICKS(s))\n\n#define FLUSH_TIMEOUT_TICKS (SECONDS_IN_TICKS(30UL))\n\n#ifndef DOLPHIN_DEBUG\n#define BUTTHURT_INCREASE_PERIOD_TICKS   (HOURS_IN_TICKS(48UL))\n#define CLEAR_LIMITS_PERIOD_TICKS        (HOURS_IN_TICKS(24UL))\n#define CLEAR_LIMITS_UPDATE_PERIOD_TICKS (HOURS_IN_TICKS(1UL))\n#else\n#define BUTTHURT_INCREASE_PERIOD_TICKS   (SECONDS_IN_TICKS(30UL))\n#define CLEAR_LIMITS_PERIOD_TICKS        (MINUTES_IN_TICKS(1))\n#define CLEAR_LIMITS_UPDATE_PERIOD_TICKS (SECONDS_IN_TICKS(5UL))\n#endif\n\n#define CLEAR_LIMITS_UPDATE_THRESHOLD_TICKS (MINUTES_IN_TICKS(5UL))\n\n#define CLEAR_LIMITS_TIME_HOURS (5UL)\n#define CLEAR_LIMITS_TIME_TICKS (HOURS_IN_TICKS(CLEAR_LIMITS_TIME_HOURS))\n\nstatic void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event);\nstatic void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event);\n\n// Public API\n\nvoid dolphin_deed(DolphinDeed deed) {\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n\n    DolphinEvent event;\n    event.type = DolphinEventTypeDeed;\n    event.deed = deed;\n\n    dolphin_event_send_async(dolphin, &event);\n\n    furi_record_close(RECORD_DOLPHIN);\n}\n\nvoid dolphin_get_settings(Dolphin* dolphin, DolphinSettings* settings) {\n    furi_check(dolphin);\n    furi_check(settings);\n\n    DolphinEvent event;\n    event.type = DolphinEventTypeSettingsGet;\n    event.settings = settings;\n    dolphin_event_send_wait(dolphin, &event);\n}\n\nvoid dolphin_set_settings(Dolphin* dolphin, DolphinSettings* settings) {\n    furi_check(dolphin);\n    furi_check(settings);\n\n    DolphinEvent event;\n    event.type = DolphinEventTypeSettingsSet;\n    event.settings = settings;\n    dolphin_event_send_wait(dolphin, &event);\n}\n\nDolphinStats dolphin_stats(Dolphin* dolphin) {\n    furi_check(dolphin);\n\n    DolphinStats stats;\n    DolphinEvent event;\n\n    event.type = DolphinEventTypeStats;\n    event.stats = &stats;\n\n    dolphin_event_send_wait(dolphin, &event);\n\n    return stats;\n}\n\nvoid dolphin_flush(Dolphin* dolphin) {\n    furi_check(dolphin);\n\n    DolphinEvent event;\n    event.type = DolphinEventTypeFlush;\n\n    dolphin_event_send_wait(dolphin, &event);\n}\n\nvoid dolphin_upgrade_level(Dolphin* dolphin) {\n    furi_check(dolphin);\n\n    DolphinEvent event;\n    event.type = DolphinEventTypeLevel;\n\n    dolphin_event_send_async(dolphin, &event);\n}\n\nFuriPubSub* dolphin_get_pubsub(Dolphin* dolphin) {\n    furi_check(dolphin);\n    return dolphin->pubsub;\n}\n\n// Private functions\n\nstatic void dolphin_butthurt_timer_callback(void* context) {\n    Dolphin* dolphin = context;\n    furi_assert(dolphin);\n\n    FURI_LOG_I(TAG, \"Increase butthurt\");\n    dolphin_state_butthurted(dolphin->state);\n    dolphin_state_save(dolphin->state);\n}\n\nstatic void dolphin_flush_timer_callback(void* context) {\n    Dolphin* dolphin = context;\n    furi_assert(dolphin);\n\n    FURI_LOG_I(TAG, \"Flush stats\");\n    dolphin_state_save(dolphin->state);\n}\n\nstatic void dolphin_clear_limits_timer_callback(void* context) {\n    Dolphin* dolphin = context;\n    furi_assert(dolphin);\n\n    FURI_LOG_I(TAG, \"Clear limits\");\n    dolphin_state_clear_limits(dolphin->state);\n    dolphin_state_save(dolphin->state);\n}\n\nstatic Dolphin* dolphin_alloc(void) {\n    Dolphin* dolphin = malloc(sizeof(Dolphin));\n\n    dolphin->state = dolphin_state_alloc();\n    dolphin->pubsub = furi_pubsub_alloc();\n    dolphin->event_queue = furi_message_queue_alloc(EVENT_QUEUE_SIZE, sizeof(DolphinEvent));\n    dolphin->event_loop = furi_event_loop_alloc();\n\n    dolphin->butthurt_timer = furi_event_loop_timer_alloc(\n        dolphin->event_loop,\n        dolphin_butthurt_timer_callback,\n        FuriEventLoopTimerTypePeriodic,\n        dolphin);\n\n    dolphin->flush_timer = furi_event_loop_timer_alloc(\n        dolphin->event_loop, dolphin_flush_timer_callback, FuriEventLoopTimerTypeOnce, dolphin);\n\n    dolphin->clear_limits_timer = furi_event_loop_timer_alloc(\n        dolphin->event_loop,\n        dolphin_clear_limits_timer_callback,\n        FuriEventLoopTimerTypePeriodic,\n        dolphin);\n\n    return dolphin;\n}\n\nstatic void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) {\n    furi_assert(dolphin);\n    furi_assert(event);\n    event->flag = NULL;\n    furi_check(\n        furi_message_queue_put(dolphin->event_queue, event, FuriWaitForever) == FuriStatusOk);\n}\n\nstatic void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) {\n    furi_assert(dolphin);\n    furi_assert(event);\n\n    event->flag = furi_event_flag_alloc();\n    furi_check(\n        furi_message_queue_put(dolphin->event_queue, event, FuriWaitForever) == FuriStatusOk);\n    furi_check(\n        furi_event_flag_wait(\n            event->flag, DOLPHIN_LOCK_EVENT_FLAG, FuriFlagWaitAny, FuriWaitForever) ==\n        DOLPHIN_LOCK_EVENT_FLAG);\n    furi_event_flag_free(event->flag);\n}\n\nstatic void dolphin_event_release(DolphinEvent* event) {\n    if(event->flag) {\n        furi_event_flag_set(event->flag, DOLPHIN_LOCK_EVENT_FLAG);\n    }\n}\n\nstatic void dolphin_update_clear_limits_timer_period(void* context) {\n    furi_assert(context);\n    Dolphin* dolphin = context;\n\n    uint32_t time_to_clear_limits =\n        furi_event_loop_timer_get_remaining_time(dolphin->clear_limits_timer);\n\n    if(time_to_clear_limits > CLEAR_LIMITS_UPDATE_THRESHOLD_TICKS) {\n        DateTime date;\n        furi_hal_rtc_get_datetime(&date);\n\n        const uint32_t now_time_ticks = DATE_IN_TICKS(date.hour, date.minute, date.second);\n\n        if(date.hour < CLEAR_LIMITS_TIME_HOURS) {\n            time_to_clear_limits = CLEAR_LIMITS_TIME_TICKS - now_time_ticks;\n        } else {\n            time_to_clear_limits =\n                CLEAR_LIMITS_PERIOD_TICKS + CLEAR_LIMITS_TIME_TICKS - now_time_ticks;\n        }\n\n        furi_event_loop_timer_start(dolphin->clear_limits_timer, time_to_clear_limits);\n    }\n\n    FURI_LOG_D(TAG, \"Daily limits reset in %lu ms\", time_to_clear_limits);\n}\n\nstatic void dolphin_process_event(FuriEventLoopObject* object, void* context) {\n    UNUSED(object);\n\n    Dolphin* dolphin = context;\n    DolphinEvent event;\n\n    FuriStatus status = furi_message_queue_get(dolphin->event_queue, &event, 0);\n    furi_check(status == FuriStatusOk);\n\n    if(event.type == DolphinEventTypeDeed) {\n        dolphin_state_on_deed(dolphin->state, event.deed);\n\n        DolphinPubsubEvent pubsub_event = DolphinPubsubEventUpdate;\n        furi_pubsub_publish(dolphin->pubsub, &pubsub_event);\n        furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);\n        furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);\n\n    } else if(event.type == DolphinEventTypeStats) {\n        event.stats->icounter = dolphin->state->data.icounter;\n        event.stats->butthurt = (dolphin->state->data.flags & DolphinFlagHappyMode) ?\n                                    0 :\n                                    dolphin->state->data.butthurt;\n        event.stats->timestamp = dolphin->state->data.timestamp;\n        event.stats->level = dolphin_get_level(dolphin->state->data.icounter);\n        event.stats->level_up_is_pending =\n            !dolphin_state_xp_to_levelup(dolphin->state->data.icounter);\n\n    } else if(event.type == DolphinEventTypeFlush) {\n        furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);\n\n    } else if(event.type == DolphinEventTypeLevel) {\n        dolphin_state_increase_level(dolphin->state);\n        furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);\n\n    } else if(event.type == DolphinEventTypeReloadState) {\n        dolphin_state_load(dolphin->state);\n        furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);\n\n    } else if(event.type == DolphinEventTypeSettingsGet) {\n        event.settings->happy_mode = dolphin->state->data.flags & DolphinFlagHappyMode;\n\n    } else if(event.type == DolphinEventTypeSettingsSet) {\n        dolphin->state->data.flags &= ~DolphinFlagHappyMode;\n        if(event.settings->happy_mode) dolphin->state->data.flags |= DolphinFlagHappyMode;\n        dolphin->state->dirty = true;\n        dolphin_state_save(dolphin->state);\n\n    } else {\n        furi_crash();\n    }\n\n    dolphin_event_release(&event);\n}\n\nstatic void dolphin_storage_callback(const void* message, void* context) {\n    furi_assert(context);\n    Dolphin* dolphin = context;\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        DolphinEvent event = {\n            .type = DolphinEventTypeReloadState,\n        };\n\n        dolphin_event_send_async(dolphin, &event);\n    }\n}\n\nstatic void dolphin_init_state(Dolphin* dolphin) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_subscribe(storage_get_pubsub(storage), dolphin_storage_callback, dolphin);\n\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping state\");\n        return;\n    }\n\n    dolphin_state_load(dolphin->state);\n}\n\n// Application thread\n\nint32_t dolphin_srv(void* p) {\n    UNUSED(p);\n\n    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {\n        FURI_LOG_W(TAG, \"Skipping start in special boot mode\");\n\n        furi_thread_suspend(furi_thread_get_current_id());\n        return 0;\n    }\n\n    Dolphin* dolphin = dolphin_alloc();\n    furi_record_create(RECORD_DOLPHIN, dolphin);\n\n    dolphin_init_state(dolphin);\n\n    furi_event_loop_subscribe_message_queue(\n        dolphin->event_loop,\n        dolphin->event_queue,\n        FuriEventLoopEventIn,\n        dolphin_process_event,\n        dolphin);\n\n    furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);\n    furi_event_loop_timer_start(dolphin->clear_limits_timer, CLEAR_LIMITS_PERIOD_TICKS);\n\n    furi_event_loop_tick_set(\n        dolphin->event_loop,\n        CLEAR_LIMITS_UPDATE_PERIOD_TICKS,\n        dolphin_update_clear_limits_timer_period,\n        dolphin);\n\n    furi_event_loop_pend_callback(\n        dolphin->event_loop, dolphin_update_clear_limits_timer_period, dolphin);\n\n    furi_event_loop_run(dolphin->event_loop);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/dolphin/dolphin.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <core/pubsub.h>\n\n#include \"helpers/dolphin_deed.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_DOLPHIN \"dolphin\"\n\ntypedef struct Dolphin Dolphin;\n\ntypedef struct {\n    uint32_t icounter;\n    uint32_t butthurt;\n    uint64_t timestamp;\n    uint8_t level;\n    bool level_up_is_pending;\n} DolphinStats;\n\ntypedef struct {\n    bool happy_mode;\n} DolphinSettings;\n\ntypedef enum {\n    DolphinPubsubEventUpdate,\n} DolphinPubsubEvent;\n\n/** Deed complete notification. Call it on deed completion.\n * See dolphin_deed.h for available deeds. In futures it will become part of assets.\n * Thread safe, async\n */\nvoid dolphin_deed(DolphinDeed deed);\n\nvoid dolphin_get_settings(Dolphin* dolphin, DolphinSettings* settings);\n\nvoid dolphin_set_settings(Dolphin* dolphin, DolphinSettings* settings);\n\n/** Retrieve dolphin stats\n * Thread safe, blocking\n */\nDolphinStats dolphin_stats(Dolphin* dolphin);\n\n/** Flush dolphin queue and save state\n * Thread safe, blocking\n */\nvoid dolphin_flush(Dolphin* dolphin);\n\nvoid dolphin_upgrade_level(Dolphin* dolphin);\n\nFuriPubSub* dolphin_get_pubsub(Dolphin* dolphin);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dolphin/dolphin_i.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include <core/pubsub.h>\n\n#include \"dolphin.h\"\n#include \"helpers/dolphin_state.h\"\n\ntypedef enum {\n    DolphinEventTypeDeed,\n    DolphinEventTypeStats,\n    DolphinEventTypeFlush,\n    DolphinEventTypeLevel,\n    DolphinEventTypeReloadState,\n    DolphinEventTypeSettingsGet,\n    DolphinEventTypeSettingsSet,\n} DolphinEventType;\n\ntypedef struct {\n    DolphinEventType type;\n    FuriEventFlag* flag;\n    union {\n        DolphinDeed deed;\n        DolphinStats* stats;\n        DolphinSettings* settings;\n    };\n} DolphinEvent;\n\nstruct Dolphin {\n    DolphinState* state;\n    FuriPubSub* pubsub;\n    FuriMessageQueue* event_queue;\n    FuriEventLoop* event_loop;\n    FuriEventLoopTimer* butthurt_timer;\n    FuriEventLoopTimer* flush_timer;\n    FuriEventLoopTimer* clear_limits_timer;\n};\n"
  },
  {
    "path": "applications/services/dolphin/helpers/dolphin_deed.c",
    "content": "#include \"dolphin_deed.h\"\n#include <furi.h>\n\nstatic const DolphinDeedWeight dolphin_deed_weights[] = {\n    {1, DolphinAppSubGhz}, // DolphinDeedSubGhzReceiverInfo\n    {3, DolphinAppSubGhz}, // DolphinDeedSubGhzSave\n    {1, DolphinAppSubGhz}, // DolphinDeedSubGhzRawRec\n    {2, DolphinAppSubGhz}, // DolphinDeedSubGhzAddManually\n    {2, DolphinAppSubGhz}, // DolphinDeedSubGhzSend\n    {1, DolphinAppSubGhz}, // DolphinDeedSubGhzFrequencyAnalyzer\n\n    {1, DolphinAppRfid}, // DolphinDeedRfidRead\n    {3, DolphinAppRfid}, // DolphinDeedRfidReadSuccess\n    {3, DolphinAppRfid}, // DolphinDeedRfidSave\n    {2, DolphinAppRfid}, // DolphinDeedRfidEmulate\n    {2, DolphinAppRfid}, // DolphinDeedRfidAdd\n\n    {1, DolphinAppNfc}, // DolphinDeedNfcRead\n    {3, DolphinAppNfc}, // DolphinDeedNfcReadSuccess\n    {3, DolphinAppNfc}, // DolphinDeedNfcSave\n    {1, DolphinAppNfc}, // DolphinDeedNfcDetectReader\n    {2, DolphinAppNfc}, // DolphinDeedNfcEmulate\n    {2, DolphinAppNfc}, // DolphinDeedNfcKeyAdd\n    {1, DolphinAppNfc}, // DolphinDeedNfcAddSave\n    {1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate\n\n    {1, DolphinAppIr}, // DolphinDeedIrSend\n    {3, DolphinAppIr}, // DolphinDeedIrLearnSuccess\n    {3, DolphinAppIr}, // DolphinDeedIrSave\n\n    {1, DolphinAppIbutton}, // DolphinDeedIbuttonRead\n    {3, DolphinAppIbutton}, // DolphinDeedIbuttonReadSuccess\n    {3, DolphinAppIbutton}, // DolphinDeedIbuttonSave\n    {2, DolphinAppIbutton}, // DolphinDeedIbuttonEmulate\n    {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd\n\n    {3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript\n    {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized\n\n    {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge\n\n    {2, DolphinAppPlugin}, // DolphinDeedPluginStart\n    {1, DolphinAppPlugin}, // DolphinDeedPluginGameStart\n    {10, DolphinAppPlugin}, // DolphinDeedPluginGameWin\n};\n\nstatic uint8_t dolphin_deed_limits[] = {\n    20, // DolphinAppSubGhz\n    20, // DolphinAppRfid\n    20, // DolphinAppNfc\n    20, // DolphinAppIr\n    20, // DolphinAppIbutton\n    20, // DolphinAppBadusb\n    20, // DolphinAppPlugin\n};\n\n_Static_assert(COUNT_OF(dolphin_deed_weights) == DolphinDeedMAX, \"dolphin_deed_weights size error\");\n_Static_assert(COUNT_OF(dolphin_deed_limits) == DolphinAppMAX, \"dolphin_deed_limits size error\");\n\nuint8_t dolphin_deed_get_weight(DolphinDeed deed) {\n    furi_check(deed < DolphinDeedMAX);\n    return dolphin_deed_weights[deed].icounter;\n}\n\nDolphinApp dolphin_deed_get_app(DolphinDeed deed) {\n    furi_check(deed < DolphinDeedMAX);\n    return dolphin_deed_weights[deed].app;\n}\n\nuint8_t dolphin_deed_get_app_limit(DolphinApp app) {\n    furi_check(app < DolphinAppMAX);\n    return dolphin_deed_limits[app];\n}\n"
  },
  {
    "path": "applications/services/dolphin/helpers/dolphin_deed.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    DolphinAppSubGhz,\n    DolphinAppRfid,\n    DolphinAppNfc,\n    DolphinAppIr,\n    DolphinAppIbutton,\n    DolphinAppBadusb,\n    DolphinAppPlugin,\n    DolphinAppMAX,\n} DolphinApp;\n\ntypedef enum {\n    DolphinDeedSubGhzReceiverInfo,\n    DolphinDeedSubGhzSave,\n    DolphinDeedSubGhzRawRec,\n    DolphinDeedSubGhzAddManually,\n    DolphinDeedSubGhzSend,\n    DolphinDeedSubGhzFrequencyAnalyzer,\n\n    DolphinDeedRfidRead,\n    DolphinDeedRfidReadSuccess,\n    DolphinDeedRfidSave,\n    DolphinDeedRfidEmulate,\n    DolphinDeedRfidAdd,\n\n    DolphinDeedNfcRead,\n    DolphinDeedNfcReadSuccess,\n    DolphinDeedNfcSave,\n    DolphinDeedNfcDetectReader,\n    DolphinDeedNfcEmulate,\n    DolphinDeedNfcKeyAdd,\n    DolphinDeedNfcAddSave,\n    DolphinDeedNfcAddEmulate,\n\n    DolphinDeedIrSend,\n    DolphinDeedIrLearnSuccess,\n    DolphinDeedIrSave,\n\n    DolphinDeedIbuttonRead,\n    DolphinDeedIbuttonReadSuccess,\n    DolphinDeedIbuttonSave,\n    DolphinDeedIbuttonEmulate,\n    DolphinDeedIbuttonAdd,\n\n    DolphinDeedBadUsbPlayScript,\n\n    DolphinDeedU2fAuthorized,\n    DolphinDeedGpioUartBridge,\n\n    DolphinDeedPluginStart,\n    DolphinDeedPluginGameStart,\n    DolphinDeedPluginGameWin,\n\n    DolphinDeedMAX,\n\n    DolphinDeedTestLeft,\n    DolphinDeedTestRight,\n} DolphinDeed;\n\ntypedef struct {\n    uint8_t icounter;\n    DolphinApp app;\n} DolphinDeedWeight;\n\ntypedef struct {\n    DolphinApp app;\n    uint8_t icounter_limit;\n} DolphinDeedLimits;\n\nDolphinApp dolphin_deed_get_app(DolphinDeed deed);\nuint8_t dolphin_deed_get_app_limit(DolphinApp app);\nuint8_t dolphin_deed_get_weight(DolphinDeed deed);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/dolphin/helpers/dolphin_state.c",
    "content": "#include \"dolphin_state.h\"\n#include \"dolphin_state_filename.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <storage/storage.h>\n#include <toolbox/saved_struct.h>\n\n#define TAG \"DolphinState\"\n\n#define DOLPHIN_STATE_PATH           INT_PATH(DOLPHIN_STATE_FILE_NAME)\n#define DOLPHIN_STATE_HEADER_MAGIC   0xD0\n#define DOLPHIN_STATE_HEADER_VERSION 0x01\n#define LEVEL2_THRESHOLD             300\n#define LEVEL3_THRESHOLD             1800\n#define BUTTHURT_MAX                 14\n#define BUTTHURT_MIN                 0\n\nDolphinState* dolphin_state_alloc(void) {\n    return malloc(sizeof(DolphinState));\n}\n\nvoid dolphin_state_free(DolphinState* dolphin_state) {\n    free(dolphin_state);\n}\n\nvoid dolphin_state_save(DolphinState* dolphin_state) {\n    if(!dolphin_state->dirty) {\n        return;\n    }\n\n    bool success = saved_struct_save(\n        DOLPHIN_STATE_PATH,\n        &dolphin_state->data,\n        sizeof(DolphinStoreData),\n        DOLPHIN_STATE_HEADER_MAGIC,\n        DOLPHIN_STATE_HEADER_VERSION);\n\n    if(success) {\n        FURI_LOG_I(TAG, \"State saved\");\n        dolphin_state->dirty = false;\n\n    } else {\n        FURI_LOG_E(TAG, \"Failed to save state\");\n    }\n}\n\nvoid dolphin_state_load(DolphinState* dolphin_state) {\n    bool success = saved_struct_load(\n        DOLPHIN_STATE_PATH,\n        &dolphin_state->data,\n        sizeof(DolphinStoreData),\n        DOLPHIN_STATE_HEADER_MAGIC,\n        DOLPHIN_STATE_HEADER_VERSION);\n\n    if(success) {\n        if((dolphin_state->data.butthurt > BUTTHURT_MAX) ||\n           (dolphin_state->data.butthurt < BUTTHURT_MIN)) {\n            success = false;\n        }\n    }\n\n    if(!success) {\n        FURI_LOG_W(TAG, \"Reset Dolphin state\");\n        memset(dolphin_state, 0, sizeof(DolphinState));\n\n        dolphin_state->dirty = true;\n        dolphin_state_save(dolphin_state);\n    }\n}\n\nuint64_t dolphin_state_timestamp(void) {\n    DateTime datetime;\n    furi_hal_rtc_get_datetime(&datetime);\n    return datetime_datetime_to_timestamp(&datetime);\n}\n\nbool dolphin_state_is_levelup(uint32_t icounter) {\n    return (icounter == LEVEL2_THRESHOLD) || (icounter == LEVEL3_THRESHOLD);\n}\n\nuint8_t dolphin_get_level(uint32_t icounter) {\n    if(icounter <= LEVEL2_THRESHOLD) {\n        return 1;\n    } else if(icounter <= LEVEL3_THRESHOLD) {\n        return 2;\n    } else {\n        return 3;\n    }\n}\n\nuint32_t dolphin_state_xp_above_last_levelup(uint32_t icounter) {\n    uint32_t threshold = 0;\n    if(icounter <= LEVEL2_THRESHOLD) {\n        threshold = 0;\n    } else if(icounter <= LEVEL3_THRESHOLD) {\n        threshold = LEVEL2_THRESHOLD + 1;\n    } else {\n        threshold = LEVEL3_THRESHOLD + 1;\n    }\n    return icounter - threshold;\n}\n\nuint32_t dolphin_state_xp_to_levelup(uint32_t icounter) {\n    uint32_t threshold = 0;\n    if(icounter <= LEVEL2_THRESHOLD) {\n        threshold = LEVEL2_THRESHOLD;\n    } else if(icounter <= LEVEL3_THRESHOLD) {\n        threshold = LEVEL3_THRESHOLD;\n    } else {\n        threshold = (uint32_t)-1;\n    }\n    return threshold - icounter;\n}\n\nvoid dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {\n    // Special case for testing\n    if(deed > DolphinDeedMAX) {\n        if(deed == DolphinDeedTestLeft) {\n            dolphin_state->data.butthurt =\n                CLAMP(dolphin_state->data.butthurt + 1, BUTTHURT_MAX, BUTTHURT_MIN);\n            if(dolphin_state->data.icounter > 0) dolphin_state->data.icounter--;\n            dolphin_state->data.timestamp = dolphin_state_timestamp();\n            dolphin_state->dirty = true;\n        } else if(deed == DolphinDeedTestRight) {\n            dolphin_state->data.butthurt = BUTTHURT_MIN;\n            if(dolphin_state->data.icounter < UINT32_MAX) dolphin_state->data.icounter++;\n            dolphin_state->data.timestamp = dolphin_state_timestamp();\n            dolphin_state->dirty = true;\n        }\n        return;\n    }\n\n    DolphinApp app = dolphin_deed_get_app(deed);\n    int8_t weight_limit =\n        dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app];\n    uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0);\n\n    uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter);\n    if(xp_to_levelup) {\n        deed_weight = MIN(xp_to_levelup, deed_weight);\n        dolphin_state->data.icounter += deed_weight;\n        dolphin_state->data.icounter_daily_limit[app] += deed_weight;\n    }\n\n    /* decrease butthurt:\n     * 0 deeds accumulating --> 0 butthurt\n     * +1....+15 deeds accumulating --> -1 butthurt\n     * +16...+30 deeds accumulating --> -1 butthurt\n     * +31...+45 deeds accumulating --> -1 butthurt\n     * +46...... deeds accumulating --> -1 butthurt\n     * -4 butthurt per day is maximum\n     * */\n    uint8_t butthurt_icounter_level_old = dolphin_state->data.butthurt_daily_limit / 15 +\n                                          !!(dolphin_state->data.butthurt_daily_limit % 15);\n    dolphin_state->data.butthurt_daily_limit =\n        CLAMP(dolphin_state->data.butthurt_daily_limit + deed_weight, 46, 0);\n    uint8_t butthurt_icounter_level_new = dolphin_state->data.butthurt_daily_limit / 15 +\n                                          !!(dolphin_state->data.butthurt_daily_limit % 15);\n    int32_t new_butthurt = ((int32_t)dolphin_state->data.butthurt) -\n                           (butthurt_icounter_level_old != butthurt_icounter_level_new);\n    new_butthurt = CLAMP(new_butthurt, BUTTHURT_MAX, BUTTHURT_MIN);\n\n    dolphin_state->data.butthurt = new_butthurt;\n    dolphin_state->data.timestamp = dolphin_state_timestamp();\n    dolphin_state->dirty = true;\n\n    FURI_LOG_D(\n        TAG,\n        \"icounter %lu, butthurt %ld\",\n        dolphin_state->data.icounter,\n        dolphin_state->data.butthurt);\n}\n\nvoid dolphin_state_butthurted(DolphinState* dolphin_state) {\n    if(dolphin_state->data.butthurt < BUTTHURT_MAX) {\n        dolphin_state->data.butthurt++;\n        dolphin_state->data.timestamp = dolphin_state_timestamp();\n        dolphin_state->dirty = true;\n    }\n}\n\nvoid dolphin_state_increase_level(DolphinState* dolphin_state) {\n    furi_assert(dolphin_state_is_levelup(dolphin_state->data.icounter));\n    ++dolphin_state->data.icounter;\n    dolphin_state->dirty = true;\n}\n\nvoid dolphin_state_clear_limits(DolphinState* dolphin_state) {\n    furi_assert(dolphin_state);\n\n    for(int i = 0; i < DolphinAppMAX; ++i) {\n        dolphin_state->data.icounter_daily_limit[i] = 0;\n    }\n    dolphin_state->data.butthurt_daily_limit = 0;\n    dolphin_state->dirty = true;\n}\n"
  },
  {
    "path": "applications/services/dolphin/helpers/dolphin_state.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"dolphin_deed.h\"\n\ntypedef enum {\n    DolphinFlagHappyMode = 1,\n} DolphinFlags;\n\ntypedef struct DolphinState DolphinState;\ntypedef struct {\n    uint8_t icounter_daily_limit[DolphinAppMAX];\n    uint8_t butthurt_daily_limit;\n\n    uint32_t flags;\n    uint32_t icounter;\n    int32_t butthurt;\n    uint64_t timestamp;\n} DolphinStoreData;\n\nstruct DolphinState {\n    DolphinStoreData data;\n    bool dirty;\n};\n\nDolphinState* dolphin_state_alloc(void);\n\nvoid dolphin_state_free(DolphinState* dolphin_state);\n\nvoid dolphin_state_save(DolphinState* dolphin_state);\n\nvoid dolphin_state_load(DolphinState* dolphin_state);\n\nvoid dolphin_state_clear_limits(DolphinState* dolphin_state);\n\nuint64_t dolphin_state_timestamp(void);\n\nvoid dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed);\n\nvoid dolphin_state_butthurted(DolphinState* dolphin_state);\n\nuint32_t dolphin_state_xp_to_levelup(uint32_t icounter);\n\nuint32_t dolphin_state_xp_above_last_levelup(uint32_t icounter);\n\nbool dolphin_state_is_levelup(uint32_t icounter);\n\nvoid dolphin_state_increase_level(DolphinState* dolphin_state);\n\nuint8_t dolphin_get_level(uint32_t icounter);\n"
  },
  {
    "path": "applications/services/dolphin/helpers/dolphin_state_filename.h",
    "content": "#pragma once\n\n#define DOLPHIN_STATE_FILE_NAME \".dolphin.state\"\n"
  },
  {
    "path": "applications/services/expansion/application.fam",
    "content": "App(\n    appid=\"expansion_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"expansion_on_system_start\",\n    cdefines=[\"SRV_EXPANSION\"],\n    sdk_headers=[\n        \"expansion.h\",\n    ],\n    requires=[\"rpc_start\"],\n    provides=[\"expansion_settings\"],\n    order=100,\n)\n"
  },
  {
    "path": "applications/services/expansion/expansion.c",
    "content": "#include \"expansion.h\"\n\n#include <furi_hal_serial_control.h>\n\n#include <furi.h>\n#include <storage/storage.h>\n#include <toolbox/api_lock.h>\n\n#include \"expansion_worker.h\"\n#include \"expansion_settings.h\"\n\n#define TAG \"ExpansionSrv\"\n\n#define EXPANSION_CONTROL_QUEUE_SIZE (8UL)\n#define EXPANSION_CONTROL_STACK_SIZE (768UL)\n\ntypedef enum {\n    ExpansionStateDisabled,\n    ExpansionStateEnabled,\n    ExpansionStateRunning,\n} ExpansionState;\n\ntypedef enum {\n    ExpansionMessageTypeEnable,\n    ExpansionMessageTypeDisable,\n    ExpansionMessageTypeSetListenSerial,\n    ExpansionMessageTypeReloadSettings,\n    ExpansionMessageTypeModuleConnected,\n    ExpansionMessageTypeModuleDisconnected,\n} ExpansionMessageType;\n\ntypedef union {\n    FuriHalSerialId serial_id;\n} ExpansionMessageData;\n\ntypedef struct {\n    ExpansionMessageType type;\n    ExpansionMessageData data;\n    FuriApiLock api_lock;\n} ExpansionMessage;\n\nstruct Expansion {\n    FuriThread* thread;\n    FuriMessageQueue* queue;\n    FuriHalSerialId serial_id;\n    ExpansionWorker* worker;\n    ExpansionState state;\n};\n\nstatic const char* const expansion_uart_names[] = {\n    \"USART\",\n    \"LPUART\",\n};\n\n// Called from the serial control thread\nstatic void expansion_detect_callback(void* context) {\n    furi_assert(context);\n    Expansion* instance = context;\n\n    ExpansionMessage message = {\n        .type = ExpansionMessageTypeModuleConnected,\n        .api_lock = NULL, // Not locking the API here to avoid a deadlock\n    };\n\n    // Not waiting for available queue space, discarding message if there is none\n    const FuriStatus status = furi_message_queue_put(instance->queue, &message, 0);\n    UNUSED(status);\n}\n\nstatic void expansion_worker_callback(void* context) {\n    furi_assert(context);\n    Expansion* instance = context;\n\n    ExpansionMessage message = {\n        .type = ExpansionMessageTypeModuleDisconnected,\n        .api_lock = NULL, // Not locking the API here to avoid a deadlock\n    };\n\n    const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever);\n    furi_check(status == FuriStatusOk);\n}\n\nstatic void\n    expansion_control_handler_enable(Expansion* instance, const ExpansionMessageData* data) {\n    UNUSED(data);\n\n    if(instance->state != ExpansionStateDisabled) {\n        return;\n    }\n\n    ExpansionSettings settings;\n    expansion_settings_load(&settings);\n\n    if(settings.uart_index < FuriHalSerialIdMax) {\n        instance->state = ExpansionStateEnabled;\n        instance->serial_id = settings.uart_index;\n        furi_hal_serial_control_set_expansion_callback(\n            instance->serial_id, expansion_detect_callback, instance);\n\n        FURI_LOG_D(TAG, \"Detection enabled on %s\", expansion_uart_names[instance->serial_id]);\n    }\n}\n\nstatic void\n    expansion_control_handler_disable(Expansion* instance, const ExpansionMessageData* data) {\n    UNUSED(data);\n    if(instance->state == ExpansionStateDisabled) {\n        return;\n    } else if(instance->state == ExpansionStateRunning) {\n        expansion_worker_stop(instance->worker);\n        expansion_worker_free(instance->worker);\n    } else {\n        furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL);\n    }\n\n    instance->state = ExpansionStateDisabled;\n\n    FURI_LOG_D(TAG, \"Detection disabled\");\n}\n\nstatic void expansion_control_handler_set_listen_serial(\n    Expansion* instance,\n    const ExpansionMessageData* data) {\n    if(instance->state != ExpansionStateDisabled && instance->serial_id == data->serial_id) {\n        return;\n\n    } else if(instance->state == ExpansionStateRunning) {\n        expansion_worker_stop(instance->worker);\n        expansion_worker_free(instance->worker);\n\n    } else if(instance->state == ExpansionStateEnabled) {\n        furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL);\n    }\n\n    instance->state = ExpansionStateEnabled;\n    instance->serial_id = data->serial_id;\n\n    furi_hal_serial_control_set_expansion_callback(\n        instance->serial_id, expansion_detect_callback, instance);\n\n    FURI_LOG_D(TAG, \"Listen serial changed to %s\", expansion_uart_names[instance->serial_id]);\n}\n\nstatic void expansion_control_handler_reload_settings(\n    Expansion* instance,\n    const ExpansionMessageData* data) {\n    UNUSED(data);\n\n    ExpansionSettings settings;\n    expansion_settings_load(&settings);\n\n    if(settings.uart_index < FuriHalSerialIdMax) {\n        const ExpansionMessageData data = {\n            .serial_id = settings.uart_index,\n        };\n\n        expansion_control_handler_set_listen_serial(instance, &data);\n\n    } else {\n        expansion_control_handler_disable(instance, NULL);\n    }\n}\n\nstatic void expansion_control_handler_module_connected(\n    Expansion* instance,\n    const ExpansionMessageData* data) {\n    UNUSED(data);\n    if(instance->state != ExpansionStateEnabled) {\n        return;\n    }\n\n    furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL);\n\n    instance->state = ExpansionStateRunning;\n    instance->worker = expansion_worker_alloc(instance->serial_id);\n\n    expansion_worker_set_callback(instance->worker, expansion_worker_callback, instance);\n    expansion_worker_start(instance->worker);\n}\n\nstatic void expansion_control_handler_module_disconnected(\n    Expansion* instance,\n    const ExpansionMessageData* data) {\n    UNUSED(data);\n    if(instance->state != ExpansionStateRunning) {\n        return;\n    }\n\n    instance->state = ExpansionStateEnabled;\n    expansion_worker_free(instance->worker);\n    furi_hal_serial_control_set_expansion_callback(\n        instance->serial_id, expansion_detect_callback, instance);\n}\n\ntypedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*);\n\nstatic const ExpansionControlHandler expansion_control_handlers[] = {\n    [ExpansionMessageTypeEnable] = expansion_control_handler_enable,\n    [ExpansionMessageTypeDisable] = expansion_control_handler_disable,\n    [ExpansionMessageTypeSetListenSerial] = expansion_control_handler_set_listen_serial,\n    [ExpansionMessageTypeReloadSettings] = expansion_control_handler_reload_settings,\n    [ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected,\n    [ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected,\n};\n\nstatic int32_t expansion_control(void* context) {\n    furi_assert(context);\n    Expansion* instance = context;\n\n    for(;;) {\n        ExpansionMessage message;\n\n        FuriStatus status = furi_message_queue_get(instance->queue, &message, FuriWaitForever);\n        furi_check(status == FuriStatusOk);\n\n        furi_check(message.type < COUNT_OF(expansion_control_handlers));\n        expansion_control_handlers[message.type](instance, &message.data);\n\n        if(message.api_lock != NULL) {\n            api_lock_unlock(message.api_lock);\n        }\n    }\n\n    return 0;\n}\n\nstatic Expansion* expansion_alloc(void) {\n    Expansion* instance = malloc(sizeof(Expansion));\n\n    instance->queue =\n        furi_message_queue_alloc(EXPANSION_CONTROL_QUEUE_SIZE, sizeof(ExpansionMessage));\n    instance->thread =\n        furi_thread_alloc_ex(TAG, EXPANSION_CONTROL_STACK_SIZE, expansion_control, instance);\n\n    return instance;\n}\n\nstatic void expansion_storage_callback(const void* message, void* context) {\n    furi_assert(context);\n\n    const StorageEvent* event = message;\n    Expansion* instance = context;\n\n    if(event->type == StorageEventTypeCardMount) {\n        ExpansionMessage em = {\n            .type = ExpansionMessageTypeReloadSettings,\n            .api_lock = NULL,\n        };\n\n        furi_check(furi_message_queue_put(instance->queue, &em, FuriWaitForever) == FuriStatusOk);\n    }\n}\n\nvoid expansion_on_system_start(void* arg) {\n    UNUSED(arg);\n\n    Expansion* instance = expansion_alloc();\n    furi_record_create(RECORD_EXPANSION, instance);\n    furi_thread_start(instance->thread);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_subscribe(storage_get_pubsub(storage), expansion_storage_callback, instance);\n\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping settings\");\n        return;\n    }\n\n    expansion_enable(instance);\n}\n\n// Public API functions\n\nvoid expansion_enable(Expansion* instance) {\n    furi_check(instance);\n\n    ExpansionMessage message = {\n        .type = ExpansionMessageTypeEnable,\n        .api_lock = api_lock_alloc_locked(),\n    };\n\n    furi_message_queue_put(instance->queue, &message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message.api_lock);\n}\n\nvoid expansion_disable(Expansion* instance) {\n    furi_check(instance);\n\n    ExpansionMessage message = {\n        .type = ExpansionMessageTypeDisable,\n        .api_lock = api_lock_alloc_locked(),\n    };\n\n    furi_message_queue_put(instance->queue, &message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message.api_lock);\n}\n\nvoid expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) {\n    furi_check(instance);\n    furi_check(serial_id < FuriHalSerialIdMax);\n\n    ExpansionMessage message = {\n        .type = ExpansionMessageTypeSetListenSerial,\n        .data.serial_id = serial_id,\n        .api_lock = api_lock_alloc_locked(),\n    };\n\n    furi_message_queue_put(instance->queue, &message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message.api_lock);\n}\n"
  },
  {
    "path": "applications/services/expansion/expansion.h",
    "content": "/**\n * @file expansion.h\n * @brief Expansion module support library.\n */\n#pragma once\n\n#include <furi_hal_serial_types.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief FURI record key to access the expansion object.\n */\n#define RECORD_EXPANSION \"expansion\"\n\n/**\n * @brief Expansion opaque type declaration.\n */\ntypedef struct Expansion Expansion;\n\n/**\n * @brief Enable support for expansion modules.\n *\n * Calling this function will load user settings and enable\n * expansion module support on the serial port specified in said settings.\n *\n * If expansion module support was disabled in settings, this function\n * does nothing.\n *\n * @param[in,out] instance pointer to the Expansion instance.\n */\nvoid expansion_enable(Expansion* instance);\n\n/**\n * @brief Disable support for expansion modules.\n *\n * Calling this function will cease all communications with the\n * expansion module (if any), release the serial handle and\n * reset the respective pins to the default state.\n *\n * @note Applications requiring serial port access MUST call\n * this function BEFORE calling furi_hal_serial_control_acquire().\n * Similarly, an expansion_enable() call MUST be made right AFTER\n * a call to furi_hal_serial_control_release() to ensure that\n * the user settings are properly restored.\n *\n * @param[in,out] instance pointer to the Expansion instance.\n */\nvoid expansion_disable(Expansion* instance);\n\n/**\n * @brief Enable support for expansion modules on designated serial port.\n *\n * Only one serial port can be used to communicate with an expansion\n * module at a time.\n *\n * Calling this function when expansion module support is already enabled\n * will first disable the previous setting, then enable the current one.\n *\n * @warning This function does not respect user settings for expansion modules,\n * so calling it might leave the system in inconsistent state. Avoid using it\n * unless absolutely necessary.\n *\n * @param[in,out] instance pointer to the Expansion instance.\n * @param[in] serial_id numerical identifier of the serial.\n */\nvoid expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/expansion/expansion_protocol.h",
    "content": "/**\n * @file expansion_protocol.h\n * @brief Flipper Expansion Protocol parser reference implementation.\n *\n * This file is licensed separately under The Unlicense.\n * See https://unlicense.org/ for more details.\n *\n * This parser is written with low-spec hardware in mind. It does not use\n * dynamic memory allocation or Flipper-specific libraries and can be\n * included directly into any module's firmware's sources.\n */\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Default baud rate to start all communications at.\n */\n#define EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE (9600UL)\n\n/**\n * @brief Maximum data size per frame, in bytes.\n */\n#define EXPANSION_PROTOCOL_MAX_DATA_SIZE (64U)\n\n/**\n * @brief Maximum allowed inactivity period, in milliseconds.\n */\n#define EXPANSION_PROTOCOL_TIMEOUT_MS (250U)\n\n/**\n * @brief Dead time after changing connection baud rate.\n */\n#define EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS (25U)\n\n/**\n * @brief Enumeration of supported frame types.\n */\ntypedef enum {\n    ExpansionFrameTypeHeartbeat = 1, /**< Heartbeat frame. */\n    ExpansionFrameTypeStatus = 2, /**< Status report frame. */\n    ExpansionFrameTypeBaudRate = 3, /**< Baud rate negotiation frame. */\n    ExpansionFrameTypeControl = 4, /**< Control frame. */\n    ExpansionFrameTypeData = 5, /**< Data frame. */\n    ExpansionFrameTypeReserved, /**< Special value. */\n} ExpansionFrameType;\n\n/**\n * @brief Enumeration of possible error types.\n */\ntypedef enum {\n    ExpansionFrameErrorNone = 0x00, /**< No error occurred. */\n    ExpansionFrameErrorUnknown = 0x01, /**< An unknown error has occurred (generic response). */\n    ExpansionFrameErrorBaudRate = 0x02, /**< Requested baud rate is not supported. */\n} ExpansionFrameError;\n\n/**\n * @brief Enumeration of suported control commands.\n */\ntypedef enum {\n    /** @brief Start an RPC session.\n     *\n     * Must only be used while the RPC session is NOT active.\n     */\n    ExpansionFrameControlCommandStartRpc = 0x00,\n    /** @brief Stop an open RPC session.\n      *\n      * Must only be used while the RPC session IS active.\n      */\n    ExpansionFrameControlCommandStopRpc = 0x01,\n    /** @brief Enable OTG (5V) on external GPIO.\n      *\n      * Must only be used while the RPC session is NOT active,\n      * otherwise OTG is to be controlled via RPC messages.\n      */\n    ExpansionFrameControlCommandEnableOtg = 0x02,\n    /** @brief Disable OTG (5V) on external GPIO.\n      *\n      * Must only be used while the RPC session is NOT active,\n      * otherwise OTG is to be controlled via RPC messages.\n      */\n    ExpansionFrameControlCommandDisableOtg = 0x03,\n} ExpansionFrameControlCommand;\n\n#pragma pack(push, 1)\n\n/**\n * @brief Frame header structure.\n */\ntypedef struct {\n    uint8_t type; /**< Type of the frame. @see ExpansionFrameType. */\n} ExpansionFrameHeader;\n\n/**\n * @brief Heartbeat frame contents.\n */\ntypedef struct {\n    /** Empty. */\n} ExpansionFrameHeartbeat;\n\n/**\n * @brief Status frame contents.\n */\ntypedef struct {\n    uint8_t error; /**< Reported error code. @see ExpansionFrameError. */\n} ExpansionFrameStatus;\n\n/**\n * @brief Baud rate frame contents.\n */\ntypedef struct {\n    uint32_t baud; /**< Requested baud rate. */\n} ExpansionFrameBaudRate;\n\n/**\n * @brief Control frame contents.\n */\ntypedef struct {\n    uint8_t command; /**< Control command number. @see ExpansionFrameControlCommand. */\n} ExpansionFrameControl;\n\n/**\n * @brief Data frame contents.\n */\ntypedef struct {\n    /** Size of the data. Must be less than EXPANSION_PROTOCOL_MAX_DATA_SIZE. */\n    uint8_t size;\n    /** Data bytes. Valid only up to ExpansionFrameData::size bytes. */\n    uint8_t bytes[EXPANSION_PROTOCOL_MAX_DATA_SIZE];\n} ExpansionFrameData;\n\n/**\n * @brief Expansion protocol frame structure.\n */\ntypedef struct {\n    ExpansionFrameHeader header; /**< Header of the frame. Required. */\n    union {\n        ExpansionFrameHeartbeat heartbeat; /**< Heartbeat frame contents. */\n        ExpansionFrameStatus status; /**< Status frame contents. */\n        ExpansionFrameBaudRate baud_rate; /**< Baud rate frame contents. */\n        ExpansionFrameControl control; /**< Control frame contents. */\n        ExpansionFrameData data; /**< Data frame contents. */\n    } content; /**< Contents of the frame. */\n} ExpansionFrame;\n\n#pragma pack(pop)\n\n/**\n * @brief Expansion checksum type.\n */\ntypedef uint8_t ExpansionFrameChecksum;\n\n/**\n * @brief Receive function type declaration.\n *\n * @see expansion_frame_decode().\n *\n * @param[out] data pointer to the buffer to reveive the data into.\n * @param[in] data_size maximum output buffer capacity, in bytes.\n * @param[in,out] context pointer to a user-defined context object.\n * @returns number of bytes written into the output buffer.\n */\ntypedef size_t (*ExpansionFrameReceiveCallback)(uint8_t* data, size_t data_size, void* context);\n\n/**\n * @brief Send function type declaration.\n *\n * @see expansion_frame_encode().\n *\n * @param[in] data pointer to the buffer containing the data to be sent.\n * @param[in] data_size size of the data to send, in bytes.\n * @param[in,out] context pointer to a user-defined context object.\n * @returns number of bytes actually sent.\n */\ntypedef size_t (*ExpansionFrameSendCallback)(const uint8_t* data, size_t data_size, void* context);\n\n/**\n * @brief Get encoded frame size.\n *\n * The frame MUST be complete and properly formed.\n *\n * @param[in] frame pointer to the frame to be evaluated.\n * @returns encoded frame size, in bytes.\n */\nstatic inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* frame) {\n    switch(frame->header.type) {\n    case ExpansionFrameTypeHeartbeat:\n        return sizeof(frame->header);\n    case ExpansionFrameTypeStatus:\n        return sizeof(frame->header) + sizeof(frame->content.status);\n    case ExpansionFrameTypeBaudRate:\n        return sizeof(frame->header) + sizeof(frame->content.baud_rate);\n    case ExpansionFrameTypeControl:\n        return sizeof(frame->header) + sizeof(frame->content.control);\n    case ExpansionFrameTypeData:\n        return sizeof(frame->header) + sizeof(frame->content.data.size) + frame->content.data.size;\n    default:\n        return 0;\n    }\n}\n\n/**\n * @brief Get remaining number of bytes needed to properly decode a frame.\n *\n * The return value will vary depending on the received_size parameter value.\n * The frame is considered complete when the function returns 0.\n *\n * @param[in] frame pointer to the frame to be evaluated.\n * @param[in] received_size number of bytes currently availabe for evaluation.\n * @param[out] remaining_size pointer to the variable to contain the number of bytes needed for a complete frame.\n * @returns true if the remaining size could be calculated, false on error.\n */\nstatic inline bool expansion_frame_get_remaining_size(\n    const ExpansionFrame* frame,\n    size_t received_size,\n    size_t* remaining_size) {\n    if(received_size < sizeof(ExpansionFrameHeader)) {\n        // Frame type is unknown as of now\n        *remaining_size = sizeof(ExpansionFrameHeader);\n        return true;\n    }\n\n    const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader);\n    size_t content_size;\n\n    switch(frame->header.type) {\n    case ExpansionFrameTypeHeartbeat:\n        content_size = 0;\n        break;\n    case ExpansionFrameTypeStatus:\n        content_size = sizeof(frame->content.status);\n        break;\n    case ExpansionFrameTypeBaudRate:\n        content_size = sizeof(frame->content.baud_rate);\n        break;\n    case ExpansionFrameTypeControl:\n        content_size = sizeof(frame->content.control);\n        break;\n    case ExpansionFrameTypeData:\n        if(received_content_size < sizeof(frame->content.data.size)) {\n            // Data size is unknown as of now\n            content_size = sizeof(frame->content.data.size);\n        } else if(frame->content.data.size > sizeof(frame->content.data.bytes)) {\n            // Malformed frame or garbage input\n            return false;\n        } else {\n            content_size = sizeof(frame->content.data.size) + frame->content.data.size;\n        }\n        break;\n    default:\n        return false;\n    }\n\n    if(content_size > received_content_size) {\n        *remaining_size = content_size - received_content_size;\n    } else {\n        *remaining_size = 0;\n    }\n\n    return true;\n}\n\n/**\n * @brief Enumeration of protocol parser statuses.\n */\ntypedef enum {\n    ExpansionProtocolStatusOk, /**< No error has occurred. */\n    ExpansionProtocolStatusErrorFormat, /**< Invalid frame type. */\n    ExpansionProtocolStatusErrorChecksum, /**< Checksum mismatch. */\n    ExpansionProtocolStatusErrorCommunication, /**< Input/output error. */\n} ExpansionProtocolStatus;\n\n/**\n * @brief Get the checksum byte corresponding to the frame\n *\n * Lightweight XOR checksum algorithm for basic error detection.\n *\n * @param[in] data pointer to a byte buffer containing the data.\n * @param[in] data_size size of the data buffer.\n * @returns checksum byte of the frame.\n */\nstatic inline ExpansionFrameChecksum\n    expansion_protocol_get_checksum(const uint8_t* data, size_t data_size) {\n    ExpansionFrameChecksum checksum = 0;\n    for(size_t i = 0; i < data_size; ++i) {\n        checksum ^= data[i];\n    }\n    return checksum;\n}\n\n/**\n * @brief Receive and decode a frame.\n *\n * Will repeatedly call the receive callback function until enough data is received.\n *\n * @param[out] frame pointer to the frame to contain decoded data.\n * @param[in] receive pointer to the function used to receive data.\n * @param[in,out] context pointer to a user-defined context object. Will be passed to the receive callback function.\n * @returns ExpansionProtocolStatusOk on success, any other error code on failure.\n */\nstatic inline ExpansionProtocolStatus expansion_protocol_decode(\n    ExpansionFrame* frame,\n    ExpansionFrameReceiveCallback receive,\n    void* context) {\n    size_t total_size = 0;\n    size_t remaining_size;\n\n    while(true) {\n        if(!expansion_frame_get_remaining_size(frame, total_size, &remaining_size)) {\n            return ExpansionProtocolStatusErrorFormat;\n        } else if(remaining_size == 0) {\n            break;\n        }\n\n        const size_t received_size =\n            receive((uint8_t*)frame + total_size, remaining_size, context);\n\n        if(received_size == 0) {\n            return ExpansionProtocolStatusErrorCommunication;\n        }\n\n        total_size += received_size;\n    }\n\n    ExpansionFrameChecksum checksum;\n    const size_t received_size = receive(&checksum, sizeof(checksum), context);\n\n    if(received_size != sizeof(checksum)) {\n        return ExpansionProtocolStatusErrorCommunication;\n    } else if(checksum != expansion_protocol_get_checksum((const uint8_t*)frame, total_size)) {\n        return ExpansionProtocolStatusErrorChecksum;\n    } else {\n        return ExpansionProtocolStatusOk;\n    }\n}\n\n/**\n * @brief Encode and send a frame.\n *\n * @param[in] frame pointer to the frame to be encoded and sent.\n * @param[in] send pointer to the function used to send data.\n * @param[in,out] context pointer to a user-defined context object. Will be passed to the send callback function.\n * @returns ExpansionProtocolStatusOk on success, any other error code on failure.\n */\nstatic inline ExpansionProtocolStatus expansion_protocol_encode(\n    const ExpansionFrame* frame,\n    ExpansionFrameSendCallback send,\n    void* context) {\n    const size_t encoded_size = expansion_frame_get_encoded_size(frame);\n    if(encoded_size == 0) {\n        return ExpansionProtocolStatusErrorFormat;\n    }\n\n    const ExpansionFrameChecksum checksum =\n        expansion_protocol_get_checksum((const uint8_t*)frame, encoded_size);\n\n    if((send((const uint8_t*)frame, encoded_size, context) != encoded_size) ||\n       (send(&checksum, sizeof(checksum), context) != sizeof(checksum))) {\n        return ExpansionProtocolStatusErrorCommunication;\n    } else {\n        return ExpansionProtocolStatusOk;\n    }\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/expansion/expansion_settings.c",
    "content": "#include \"expansion_settings.h\"\n\n#include <storage/storage.h>\n#include <toolbox/saved_struct.h>\n\n#include \"expansion_settings_filename.h\"\n\n#define TAG \"ExpansionSettings\"\n\n#define EXPANSION_SETTINGS_PATH    INT_PATH(EXPANSION_SETTINGS_FILE_NAME)\n#define EXPANSION_SETTINGS_VERSION (0)\n#define EXPANSION_SETTINGS_MAGIC   (0xEA)\n\nvoid expansion_settings_load(ExpansionSettings* settings) {\n    furi_assert(settings);\n\n    const bool success = saved_struct_load(\n        EXPANSION_SETTINGS_PATH,\n        settings,\n        sizeof(ExpansionSettings),\n        EXPANSION_SETTINGS_MAGIC,\n        EXPANSION_SETTINGS_VERSION);\n\n    if(!success) {\n        FURI_LOG_W(TAG, \"Failed to load file, using defaults\");\n        memset(settings, 0, sizeof(ExpansionSettings));\n        expansion_settings_save(settings);\n    }\n}\n\nvoid expansion_settings_save(const ExpansionSettings* settings) {\n    furi_assert(settings);\n\n    const bool success = saved_struct_save(\n        EXPANSION_SETTINGS_PATH,\n        settings,\n        sizeof(ExpansionSettings),\n        EXPANSION_SETTINGS_MAGIC,\n        EXPANSION_SETTINGS_VERSION);\n\n    if(!success) {\n        FURI_LOG_E(TAG, \"Failed to save file\");\n    }\n}\n"
  },
  {
    "path": "applications/services/expansion/expansion_settings.h",
    "content": "/**\n * @file expansion_settings.h\n * @brief Expansion module support settings.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Expansion module support settings storage type.\n */\ntypedef struct {\n    /**\n     * Numerical index of serial port used to communicate\n     * with expansion modules.\n     */\n    uint8_t uart_index;\n} ExpansionSettings;\n\n/**\n * @brief Load expansion module support settings from file.\n *\n * @param[in,out] settings pointer to an ExpansionSettings instance to load settings into.\n */\nvoid expansion_settings_load(ExpansionSettings* settings);\n\n/**\n * @brief Save expansion module support settings to file.\n *\n * @param[in] settings pointer to an ExpansionSettings instance to save settings from.\n */\nvoid expansion_settings_save(const ExpansionSettings* settings);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/expansion/expansion_settings_filename.h",
    "content": "/**\n * @file expansion_settings_filename.h\n */\n#pragma once\n\n/**\n * @brief File name used for expansion settings.\n */\n#define EXPANSION_SETTINGS_FILE_NAME \".expansion.settings\"\n"
  },
  {
    "path": "applications/services/expansion/expansion_worker.c",
    "content": "#include \"expansion_worker.h\"\n\n#include <power/power_service/power.h>\n#include <furi_hal_power.h>\n\n#include <furi_hal_serial.h>\n#include <furi_hal_serial_control.h>\n\n#include <furi.h>\n#include <rpc/rpc.h>\n\n#include \"expansion_protocol.h\"\n\n#define TAG \"ExpansionSrv\"\n\n#define EXPANSION_WORKER_STACK_SZIE  (768UL)\n#define EXPANSION_WORKER_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum))\n\ntypedef enum {\n    ExpansionWorkerStateHandShake,\n    ExpansionWorkerStateConnected,\n    ExpansionWorkerStateRpcActive,\n} ExpansionWorkerState;\n\ntypedef enum {\n    ExpansionWorkerExitReasonUnknown,\n    ExpansionWorkerExitReasonUser,\n    ExpansionWorkerExitReasonError,\n    ExpansionWorkerExitReasonTimeout,\n} ExpansionWorkerExitReason;\n\ntypedef enum {\n    ExpansionWorkerFlagStop = 1 << 0,\n    ExpansionWorkerFlagData = 1 << 1,\n    ExpansionWorkerFlagError = 1 << 2,\n} ExpansionWorkerFlag;\n\n#define EXPANSION_ALL_FLAGS \\\n    (ExpansionWorkerFlagData | ExpansionWorkerFlagStop | ExpansionWorkerFlagError)\n\nstruct ExpansionWorker {\n    FuriThread* thread;\n    FuriStreamBuffer* rx_buf;\n    FuriSemaphore* tx_semaphore;\n\n    FuriHalSerialId serial_id;\n    FuriHalSerialHandle* serial_handle;\n\n    RpcSession* rpc_session;\n\n    ExpansionWorkerState state;\n    ExpansionWorkerExitReason exit_reason;\n    ExpansionWorkerCallback callback;\n    void* cb_context;\n};\n\n// Called in UART IRQ context\nstatic void expansion_worker_serial_rx_callback(\n    FuriHalSerialHandle* handle,\n    FuriHalSerialRxEvent event,\n    void* context) {\n    furi_assert(handle);\n    furi_assert(context);\n\n    ExpansionWorker* instance = context;\n\n    if(event & (FuriHalSerialRxEventNoiseError | FuriHalSerialRxEventFrameError |\n                FuriHalSerialRxEventOverrunError)) {\n        furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagError);\n    } else if(event & FuriHalSerialRxEventData) {\n        while(furi_hal_serial_async_rx_available(handle)) {\n            const uint8_t data = furi_hal_serial_async_rx(handle);\n            furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0);\n        }\n        furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagData);\n    }\n}\n\nstatic size_t expansion_worker_receive_callback(uint8_t* data, size_t data_size, void* context) {\n    ExpansionWorker* instance = context;\n\n    size_t received_size = 0;\n\n    while(true) {\n        received_size += furi_stream_buffer_receive(\n            instance->rx_buf, data + received_size, data_size - received_size, 0);\n\n        if(received_size == data_size) break;\n\n        const uint32_t flags = furi_thread_flags_wait(\n            EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS));\n\n        if(flags & FuriFlagError) {\n            if(flags == (unsigned)FuriFlagErrorTimeout) {\n                // Exiting due to timeout\n                instance->exit_reason = ExpansionWorkerExitReasonTimeout;\n            } else {\n                // Exiting due to an unspecified error\n                instance->exit_reason = ExpansionWorkerExitReasonError;\n            }\n            break;\n        } else if(flags & ExpansionWorkerFlagStop) {\n            // Exiting due to explicit request\n            instance->exit_reason = ExpansionWorkerExitReasonUser;\n            break;\n        } else if(flags & ExpansionWorkerFlagError) {\n            // Exiting due to RPC error\n            instance->exit_reason = ExpansionWorkerExitReasonError;\n            break;\n        } else if(flags & ExpansionWorkerFlagData) {\n            // Go to buffer reading\n            continue;\n        }\n    }\n\n    return received_size;\n}\n\nstatic inline bool\n    expansion_worker_receive_frame(ExpansionWorker* instance, ExpansionFrame* frame) {\n    return expansion_protocol_decode(frame, expansion_worker_receive_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic size_t\n    expansion_worker_send_callback(const uint8_t* data, size_t data_size, void* context) {\n    ExpansionWorker* instance = context;\n    furi_hal_serial_tx(instance->serial_handle, data, data_size);\n    furi_hal_serial_tx_wait_complete(instance->serial_handle);\n    return data_size;\n}\n\nstatic inline bool\n    expansion_worker_send_frame(ExpansionWorker* instance, const ExpansionFrame* frame) {\n    return expansion_protocol_encode(frame, expansion_worker_send_callback, instance) ==\n           ExpansionProtocolStatusOk;\n}\n\nstatic bool expansion_worker_send_heartbeat(ExpansionWorker* instance) {\n    const ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeHeartbeat,\n        .content.heartbeat = {},\n    };\n\n    return expansion_worker_send_frame(instance, &frame);\n}\n\nstatic bool\n    expansion_worker_send_status_response(ExpansionWorker* instance, ExpansionFrameError error) {\n    const ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeStatus,\n        .content.status.error = error,\n    };\n\n    return expansion_worker_send_frame(instance, &frame);\n}\n\nstatic bool expansion_worker_send_data_response(\n    ExpansionWorker* instance,\n    const uint8_t* data,\n    size_t data_size) {\n    furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE);\n\n    ExpansionFrame frame = {\n        .header.type = ExpansionFrameTypeData,\n        .content.data.size = data_size,\n    };\n\n    memcpy(frame.content.data.bytes, data, data_size);\n    return expansion_worker_send_frame(instance, &frame);\n}\n\n// Called in Rpc session thread context\nstatic void expansion_worker_rpc_send_callback(void* context, uint8_t* data, size_t data_size) {\n    ExpansionWorker* instance = context;\n\n    for(size_t sent_data_size = 0; sent_data_size < data_size;) {\n        if(furi_semaphore_acquire(\n               instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) !=\n           FuriStatusOk) {\n            furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagError);\n            break;\n        }\n\n        const size_t current_data_size =\n            MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE);\n        if(!expansion_worker_send_data_response(instance, data + sent_data_size, current_data_size))\n            break;\n        sent_data_size += current_data_size;\n    }\n}\n\nstatic bool expansion_worker_rpc_session_open(ExpansionWorker* instance) {\n    Rpc* rpc = furi_record_open(RECORD_RPC);\n    instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart);\n\n    if(instance->rpc_session) {\n        instance->tx_semaphore = furi_semaphore_alloc(1, 1);\n        rpc_session_set_context(instance->rpc_session, instance);\n        rpc_session_set_send_bytes_callback(\n            instance->rpc_session, expansion_worker_rpc_send_callback);\n    }\n\n    return instance->rpc_session != NULL;\n}\n\nstatic void expansion_worker_rpc_session_close(ExpansionWorker* instance) {\n    if(instance->rpc_session) {\n        rpc_session_close(instance->rpc_session);\n        furi_semaphore_free(instance->tx_semaphore);\n    }\n\n    furi_record_close(RECORD_RPC);\n}\n\nstatic bool expansion_worker_handle_state_handshake(\n    ExpansionWorker* instance,\n    const ExpansionFrame* rx_frame) {\n    bool success = false;\n\n    do {\n        if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break;\n        const uint32_t baud_rate = rx_frame->content.baud_rate.baud;\n\n        FURI_LOG_D(TAG, \"Proposed baud rate: %lu\", baud_rate);\n\n        if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) {\n            instance->state = ExpansionWorkerStateConnected;\n            // Send response at previous baud rate\n            if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;\n            furi_hal_serial_set_br(instance->serial_handle, baud_rate);\n\n        } else {\n            if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorBaudRate))\n                break;\n            FURI_LOG_E(TAG, \"Bad baud rate\");\n        }\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_worker_handle_state_connected(\n    ExpansionWorker* instance,\n    const ExpansionFrame* rx_frame) {\n    bool success = false;\n\n    do {\n        if(rx_frame->header.type == ExpansionFrameTypeControl) {\n            const uint8_t command = rx_frame->content.control.command;\n            if(command == ExpansionFrameControlCommandStartRpc) {\n                if(!expansion_worker_rpc_session_open(instance)) break;\n                instance->state = ExpansionWorkerStateRpcActive;\n            } else if(command == ExpansionFrameControlCommandEnableOtg) {\n                Power* power = furi_record_open(RECORD_POWER);\n                power_enable_otg(power, true);\n                furi_record_close(RECORD_POWER);\n            } else if(command == ExpansionFrameControlCommandDisableOtg) {\n                Power* power = furi_record_open(RECORD_POWER);\n                power_enable_otg(power, false);\n                furi_record_close(RECORD_POWER);\n            } else {\n                break;\n            }\n\n            if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;\n\n        } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) {\n            if(!expansion_worker_send_heartbeat(instance)) break;\n\n        } else {\n            break;\n        }\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool expansion_worker_handle_state_rpc_active(\n    ExpansionWorker* instance,\n    const ExpansionFrame* rx_frame) {\n    bool success = false;\n\n    do {\n        if(rx_frame->header.type == ExpansionFrameTypeData) {\n            if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;\n\n            const size_t size_consumed = rpc_session_feed(\n                instance->rpc_session,\n                rx_frame->content.data.bytes,\n                rx_frame->content.data.size,\n                EXPANSION_PROTOCOL_TIMEOUT_MS);\n            if(size_consumed != rx_frame->content.data.size) break;\n\n        } else if(rx_frame->header.type == ExpansionFrameTypeControl) {\n            const uint8_t command = rx_frame->content.control.command;\n            if(command == ExpansionFrameControlCommandStopRpc) {\n                instance->state = ExpansionWorkerStateConnected;\n                expansion_worker_rpc_session_close(instance);\n            } else {\n                break;\n            }\n\n            if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;\n\n        } else if(rx_frame->header.type == ExpansionFrameTypeStatus) {\n            if(rx_frame->content.status.error != ExpansionFrameErrorNone) break;\n            furi_semaphore_release(instance->tx_semaphore);\n\n        } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) {\n            if(!expansion_worker_send_heartbeat(instance)) break;\n\n        } else {\n            break;\n        }\n        success = true;\n    } while(false);\n\n    return success;\n}\n\ntypedef bool (*ExpansionWorkerStateHandler)(ExpansionWorker*, const ExpansionFrame*);\n\nstatic const ExpansionWorkerStateHandler expansion_handlers[] = {\n    [ExpansionWorkerStateHandShake] = expansion_worker_handle_state_handshake,\n    [ExpansionWorkerStateConnected] = expansion_worker_handle_state_connected,\n    [ExpansionWorkerStateRpcActive] = expansion_worker_handle_state_rpc_active,\n};\n\nstatic inline void expansion_worker_state_machine(ExpansionWorker* instance) {\n    ExpansionFrame rx_frame;\n\n    while(true) {\n        if(!expansion_worker_receive_frame(instance, &rx_frame)) break;\n        if(!expansion_handlers[instance->state](instance, &rx_frame)) break;\n    }\n}\n\nstatic int32_t expansion_worker(void* context) {\n    furi_assert(context);\n    ExpansionWorker* instance = context;\n\n    furi_hal_power_insomnia_enter();\n\n    instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id);\n    furi_check(instance->serial_handle);\n\n    FURI_LOG_D(TAG, \"Worker started\");\n\n    instance->state = ExpansionWorkerStateHandShake;\n    instance->exit_reason = ExpansionWorkerExitReasonUnknown;\n\n    furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE);\n\n    furi_hal_serial_async_rx_start(\n        instance->serial_handle, expansion_worker_serial_rx_callback, instance, true);\n\n    if(expansion_worker_send_heartbeat(instance)) {\n        expansion_worker_state_machine(instance);\n    }\n\n    furi_hal_serial_async_rx_stop(instance->serial_handle);\n\n    if(instance->state == ExpansionWorkerStateRpcActive) {\n        expansion_worker_rpc_session_close(instance);\n    }\n\n    FURI_LOG_D(TAG, \"Worker stopped\");\n\n    furi_hal_serial_control_release(instance->serial_handle);\n    furi_hal_power_insomnia_exit();\n\n    // Do not invoke worker callback on user-requested exit\n    if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) {\n        instance->callback(instance->cb_context);\n    }\n\n    return 0;\n}\n\nExpansionWorker* expansion_worker_alloc(FuriHalSerialId serial_id) {\n    ExpansionWorker* instance = malloc(sizeof(ExpansionWorker));\n\n    instance->thread = furi_thread_alloc_ex(\n        TAG \"Worker\", EXPANSION_WORKER_STACK_SZIE, expansion_worker, instance);\n    instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_WORKER_BUFFER_SIZE, 1);\n    instance->serial_id = serial_id;\n\n    // Improves responsiveness in heavy games at the expense of dropped frames\n    furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);\n\n    return instance;\n}\n\nvoid expansion_worker_free(ExpansionWorker* instance) {\n    furi_stream_buffer_free(instance->rx_buf);\n    furi_thread_join(instance->thread);\n    furi_thread_free(instance->thread);\n    free(instance);\n}\n\nvoid expansion_worker_set_callback(\n    ExpansionWorker* instance,\n    ExpansionWorkerCallback callback,\n    void* context) {\n    instance->callback = callback;\n    instance->cb_context = context;\n}\n\nvoid expansion_worker_start(ExpansionWorker* instance) {\n    furi_thread_start(instance->thread);\n}\n\nvoid expansion_worker_stop(ExpansionWorker* instance) {\n    furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagStop);\n    furi_thread_join(instance->thread);\n}\n"
  },
  {
    "path": "applications/services/expansion/expansion_worker.h",
    "content": "/**\n * @file expansion_worker.h\n * @brief Expansion module handling thread wrapper.\n *\n * The worker is started each time an expansion module is detected\n * and handles all of the communication protocols. Likewise, it is stopped\n * upon module disconnection or communication error.\n *\n * @warning This file is a private implementation detail. Please do not attempt to use it in applications.\n */\n#pragma once\n\n#include <furi_hal_serial_types.h>\n\n/**\n * @brief Expansion worker opaque type declaration.\n */\ntypedef struct ExpansionWorker ExpansionWorker;\n\n/**\n * @brief Worker callback type.\n *\n * @see expansion_worker_set_callback()\n *\n * @param[in,out] context pointer to a user-defined object.\n */\ntypedef void (*ExpansionWorkerCallback)(void* context);\n\n/**\n * @brief Create an expansion worker instance.\n *\n * @param[in] serial_id numerical identifier of the serial to be used by the worker.\n * @returns pointer to the created instance.\n */\nExpansionWorker* expansion_worker_alloc(FuriHalSerialId serial_id);\n\n/**\n * @brief Delete an expansion worker instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid expansion_worker_free(ExpansionWorker* instance);\n\n/**\n * @brief Set the module disconnect callback.\n *\n * The callback will be triggered upon worker stop EXCEPT\n * when it was stopped via an expansion_worker_stop() call.\n *\n * In other words, the callback will ONLY be triggered if the worker was\n * stopped due to the user disconnecting/resetting/powering down the module,\n * or due to some communication error.\n *\n * @param[in,out] instance pointer to the worker instance to be modified.\n * @param[in] callback pointer to the callback function to be called under the above conditions.\n * @param[in] context pointer to a user-defined object, will be passed as a parameter to the callback.\n */\nvoid expansion_worker_set_callback(\n    ExpansionWorker* instance,\n    ExpansionWorkerCallback callback,\n    void* context);\n\n/**\n * @brief Start the expansion module worker.\n *\n * @param[in,out] instance pointer to the worker instance to be started.\n */\nvoid expansion_worker_start(ExpansionWorker* instance);\n\n/**\n * @brief Stop the expansion module worker.\n *\n * If the worker was stopped via this call (and not because of module disconnect/\n * protocol error), the callback will not be triggered.\n *\n * @param[in,out] instance pointer to the worker instance to be stopped.\n */\nvoid expansion_worker_stop(ExpansionWorker* instance);\n"
  },
  {
    "path": "applications/services/gui/application.fam",
    "content": "App(\n    appid=\"gui\",\n    name=\"GuiSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"gui_srv\",\n    cdefines=[\"SRV_GUI\"],\n    requires=[\n        \"input\",\n        \"notification\",\n    ],\n    stack_size=2 * 1024,\n    order=70,\n    sdk_headers=[\n        \"gui.h\",\n        \"icon_i.h\",\n        \"elements.h\",\n        \"view_dispatcher.h\",\n        \"view_stack.h\",\n        \"view_holder.h\",\n        \"modules/button_menu.h\",\n        \"modules/byte_input.h\",\n        \"modules/number_input.h\",\n        \"modules/popup.h\",\n        \"modules/text_input.h\",\n        \"modules/widget.h\",\n        \"modules/validators.h\",\n        \"modules/file_browser.h\",\n        \"modules/button_panel.h\",\n        \"modules/variable_item_list.h\",\n        \"modules/file_browser_worker.h\",\n        \"modules/menu.h\",\n        \"modules/dialog_ex.h\",\n        \"modules/loading.h\",\n        \"modules/text_box.h\",\n        \"modules/submenu.h\",\n        \"modules/widget_elements/widget_element.h\",\n        \"modules/empty_screen.h\",\n    ],\n)\n"
  },
  {
    "path": "applications/services/gui/canvas.c",
    "content": "#include \"canvas_i.h\"\n#include \"icon_animation_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <stdint.h>\n#include <u8g2_glue.h>\n\nconst CanvasFontParameters canvas_font_params[FontTotalNumber] = {\n    [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2},\n    [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},\n    [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},\n    [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0},\n};\n\nCanvas* canvas_init(void) {\n    Canvas* canvas = malloc(sizeof(Canvas));\n    canvas->compress_icon = compress_icon_alloc(ICON_DECOMPRESSOR_BUFFER_SIZE);\n\n    // Initialize mutex\n    canvas->mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    // Initialize callback array\n    CanvasCallbackPairArray_init(canvas->canvas_callback_pair);\n\n    // Setup u8g2\n    u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);\n    canvas->orientation = CanvasOrientationHorizontal;\n    // Initialize display\n    u8g2_InitDisplay(&canvas->fb);\n    // Wake up display\n    u8g2_SetPowerSave(&canvas->fb, 0);\n\n    // Clear buffer and send to device\n    canvas_clear(canvas);\n    canvas_commit(canvas);\n\n    return canvas;\n}\n\nvoid canvas_free(Canvas* canvas) {\n    furi_check(canvas);\n    compress_icon_free(canvas->compress_icon);\n    CanvasCallbackPairArray_clear(canvas->canvas_callback_pair);\n    furi_mutex_free(canvas->mutex);\n    free(canvas);\n}\n\nstatic void canvas_lock(Canvas* canvas) {\n    furi_assert(canvas);\n    furi_check(furi_mutex_acquire(canvas->mutex, FuriWaitForever) == FuriStatusOk);\n}\n\nstatic void canvas_unlock(Canvas* canvas) {\n    furi_assert(canvas);\n    furi_check(furi_mutex_release(canvas->mutex) == FuriStatusOk);\n}\n\nvoid canvas_reset(Canvas* canvas) {\n    furi_check(canvas);\n\n    canvas_clear(canvas);\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n    canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);\n}\n\nvoid canvas_commit(Canvas* canvas) {\n    furi_check(canvas);\n    u8g2_SendBuffer(&canvas->fb);\n\n    // Iterate over callbacks\n    canvas_lock(canvas);\n    for\n        M_EACH(p, canvas->canvas_callback_pair, CanvasCallbackPairArray_t) {\n            p->callback(\n                canvas_get_buffer(canvas),\n                canvas_get_buffer_size(canvas),\n                canvas_get_orientation(canvas),\n                p->context);\n        }\n    canvas_unlock(canvas);\n}\n\nuint8_t* canvas_get_buffer(Canvas* canvas) {\n    furi_check(canvas);\n    return u8g2_GetBufferPtr(&canvas->fb);\n}\n\nsize_t canvas_get_buffer_size(const Canvas* canvas) {\n    furi_check(canvas);\n    return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8;\n}\n\nvoid canvas_frame_set(\n    Canvas* canvas,\n    int32_t offset_x,\n    int32_t offset_y,\n    size_t width,\n    size_t height) {\n    furi_check(canvas);\n    canvas->offset_x = offset_x;\n    canvas->offset_y = offset_y;\n    canvas->width = width;\n    canvas->height = height;\n}\n\nsize_t canvas_width(const Canvas* canvas) {\n    furi_check(canvas);\n    return canvas->width;\n}\n\nsize_t canvas_height(const Canvas* canvas) {\n    furi_check(canvas);\n    return canvas->height;\n}\n\nsize_t canvas_current_font_height(const Canvas* canvas) {\n    furi_check(canvas);\n    size_t font_height = u8g2_GetMaxCharHeight(&canvas->fb);\n\n    if(canvas->fb.font == u8g2_font_haxrcorp4089_tr) {\n        font_height += 1;\n    }\n\n    return font_height;\n}\n\nconst CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) {\n    furi_check(canvas);\n    furi_check(font < FontTotalNumber);\n    return &canvas_font_params[font];\n}\n\nvoid canvas_clear(Canvas* canvas) {\n    furi_check(canvas);\n    u8g2_ClearBuffer(&canvas->fb);\n}\n\nvoid canvas_set_color(Canvas* canvas, Color color) {\n    furi_check(canvas);\n    u8g2_SetDrawColor(&canvas->fb, color);\n}\n\nvoid canvas_set_font_direction(Canvas* canvas, CanvasDirection dir) {\n    furi_check(canvas);\n    u8g2_SetFontDirection(&canvas->fb, dir);\n}\n\nvoid canvas_invert_color(Canvas* canvas) {\n    canvas->fb.draw_color = !canvas->fb.draw_color;\n}\n\nvoid canvas_set_font(Canvas* canvas, Font font) {\n    furi_check(canvas);\n    u8g2_SetFontMode(&canvas->fb, 1);\n    if(font == FontPrimary) {\n        u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr);\n    } else if(font == FontSecondary) {\n        u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr);\n    } else if(font == FontKeyboard) {\n        u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr);\n    } else if(font == FontBigNumbers) {\n        u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn);\n    } else {\n        furi_crash();\n    }\n}\n\nvoid canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) {\n    furi_check(canvas);\n    u8g2_SetFontMode(&canvas->fb, 1);\n    u8g2_SetFont(&canvas->fb, font);\n}\n\nvoid canvas_draw_str(Canvas* canvas, int32_t x, int32_t y, const char* str) {\n    furi_check(canvas);\n    if(!str) return;\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawUTF8(&canvas->fb, x, y, str);\n}\n\nvoid canvas_draw_str_aligned(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    Align horizontal,\n    Align vertical,\n    const char* str) {\n    furi_check(canvas);\n    if(!str) return;\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n\n    switch(horizontal) {\n    case AlignLeft:\n        break;\n    case AlignRight:\n        x -= u8g2_GetUTF8Width(&canvas->fb, str);\n        break;\n    case AlignCenter:\n        x -= (u8g2_GetUTF8Width(&canvas->fb, str) / 2);\n        break;\n    default:\n        furi_crash();\n        break;\n    }\n\n    switch(vertical) {\n    case AlignTop:\n        y += u8g2_GetAscent(&canvas->fb);\n        break;\n    case AlignBottom:\n        break;\n    case AlignCenter:\n        y += (u8g2_GetAscent(&canvas->fb) / 2);\n        break;\n    default:\n        furi_crash();\n        break;\n    }\n\n    u8g2_DrawUTF8(&canvas->fb, x, y, str);\n}\n\nuint16_t canvas_string_width(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n    if(!str) return 0;\n    return u8g2_GetUTF8Width(&canvas->fb, str);\n}\n\nsize_t canvas_glyph_width(Canvas* canvas, uint16_t symbol) {\n    furi_check(canvas);\n    return u8g2_GetGlyphWidth(&canvas->fb, symbol);\n}\n\nvoid canvas_draw_bitmap(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* compressed_bitmap_data) {\n    furi_check(canvas);\n\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    uint8_t* bitmap_data = NULL;\n    compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);\n    canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);\n}\n\nvoid canvas_draw_icon_animation(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    IconAnimation* icon_animation) {\n    furi_check(canvas);\n    furi_check(icon_animation);\n\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    uint8_t* icon_data = NULL;\n    compress_icon_decode(\n        canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);\n    canvas_draw_u8g2_bitmap(\n        &canvas->fb,\n        x,\n        y,\n        icon_animation_get_width(icon_animation),\n        icon_animation_get_height(icon_animation),\n        icon_data,\n        IconRotation0);\n}\n\nstatic void canvas_draw_u8g2_bitmap_int(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    bool mirror,\n    bool rotation,\n    const uint8_t* bitmap) {\n    u8g2_uint_t blen;\n    blen = w;\n    blen += 7;\n    blen >>= 3;\n\n    if(rotation && !mirror) {\n        x += w + 1;\n    } else if(mirror && !rotation) {\n        y += h - 1;\n    }\n\n    while(h > 0) {\n        const uint8_t* b = bitmap;\n        uint16_t len = w;\n        uint16_t x0 = x;\n        uint16_t y0 = y;\n        uint8_t mask;\n        uint8_t color = u8g2->draw_color;\n        uint8_t ncolor = (color == 0 ? 1 : 0);\n        mask = 1;\n\n        while(len > 0) {\n            if(u8x8_pgm_read(b) & mask) {\n                u8g2->draw_color = color;\n                u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);\n            } else if(u8g2->bitmap_transparency == 0) {\n                u8g2->draw_color = ncolor;\n                u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);\n            }\n\n            if(rotation) {\n                y0++;\n            } else {\n                x0++;\n            }\n\n            mask <<= 1;\n            if(mask == 0) {\n                mask = 1;\n                b++;\n            }\n            len--;\n        }\n\n        u8g2->draw_color = color;\n        bitmap += blen;\n\n        if(mirror) {\n            if(rotation) {\n                x++;\n            } else {\n                y--;\n            }\n        } else {\n            if(rotation) {\n                x--;\n            } else {\n                y++;\n            }\n        }\n        h--;\n    }\n}\n\nvoid canvas_draw_u8g2_bitmap(\n    u8g2_t* u8g2,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* bitmap,\n    IconRotation rotation) {\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + width, y + height) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    switch(rotation) {\n    case IconRotation0:\n        canvas_draw_u8g2_bitmap_int(u8g2, x, y, width, height, 0, 0, bitmap);\n        break;\n    case IconRotation90:\n        canvas_draw_u8g2_bitmap_int(u8g2, x, y, width, height, 0, 1, bitmap);\n        break;\n    case IconRotation180:\n        canvas_draw_u8g2_bitmap_int(u8g2, x, y, width, height, 1, 0, bitmap);\n        break;\n    case IconRotation270:\n        canvas_draw_u8g2_bitmap_int(u8g2, x, y, width, height, 1, 1, bitmap);\n        break;\n    default:\n        break;\n    }\n}\n\nvoid canvas_draw_icon_ex(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    const Icon* icon,\n    IconRotation rotation) {\n    furi_check(canvas);\n    furi_check(icon);\n\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    uint8_t* icon_data = NULL;\n    compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data);\n    canvas_draw_u8g2_bitmap(\n        &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);\n}\n\nvoid canvas_draw_icon(Canvas* canvas, int32_t x, int32_t y, const Icon* icon) {\n    furi_check(canvas);\n    furi_check(icon);\n\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    uint8_t* icon_data = NULL;\n    compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data);\n    canvas_draw_u8g2_bitmap(\n        &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);\n}\n\nvoid canvas_draw_dot(Canvas* canvas, int32_t x, int32_t y) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawPixel(&canvas->fb, x, y);\n}\n\nvoid canvas_draw_box(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawBox(&canvas->fb, x, y, width, height);\n}\n\nvoid canvas_draw_rbox(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    size_t radius) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius);\n}\n\nvoid canvas_draw_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawFrame(&canvas->fb, x, y, width, height);\n}\n\nvoid canvas_draw_rframe(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    size_t radius) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius);\n}\n\nvoid canvas_draw_line(Canvas* canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2) {\n    furi_check(canvas);\n    x1 += canvas->offset_x;\n    y1 += canvas->offset_y;\n    x2 += canvas->offset_x;\n    y2 += canvas->offset_y;\n    u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2);\n}\n\nvoid canvas_draw_circle(Canvas* canvas, int32_t x, int32_t y, size_t radius) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawCircle(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);\n}\n\nvoid canvas_draw_disc(Canvas* canvas, int32_t x, int32_t y, size_t radius) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawDisc(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);\n}\n\nvoid canvas_draw_triangle(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t base,\n    size_t height,\n    CanvasDirection dir) {\n    furi_check(canvas);\n    if(dir == CanvasDirectionBottomToTop) {\n        canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);\n        canvas_draw_line(canvas, x - base / 2, y, x, y - height + 1);\n        canvas_draw_line(canvas, x, y - height + 1, x + base / 2, y);\n    } else if(dir == CanvasDirectionTopToBottom) {\n        canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);\n        canvas_draw_line(canvas, x - base / 2, y, x, y + height - 1);\n        canvas_draw_line(canvas, x, y + height - 1, x + base / 2, y);\n    } else if(dir == CanvasDirectionRightToLeft) {\n        canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);\n        canvas_draw_line(canvas, x, y - base / 2, x - height + 1, y);\n        canvas_draw_line(canvas, x - height + 1, y, x, y + base / 2);\n    } else if(dir == CanvasDirectionLeftToRight) {\n        canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);\n        canvas_draw_line(canvas, x, y - base / 2, x + height - 1, y);\n        canvas_draw_line(canvas, x + height - 1, y, x, y + base / 2);\n    }\n}\n\nvoid canvas_draw_xbm(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* bitmap) {\n    furi_check(canvas);\n    canvas_draw_xbm_ex(canvas, x, y, width, height, IconRotation0, bitmap);\n}\n\nvoid canvas_draw_xbm_ex(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    IconRotation rotation,\n    const uint8_t* bitmap_data) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, rotation);\n}\n\nvoid canvas_draw_glyph(Canvas* canvas, int32_t x, int32_t y, uint16_t ch) {\n    furi_check(canvas);\n    x += canvas->offset_x;\n    y += canvas->offset_y;\n    u8g2_DrawGlyph(&canvas->fb, x, y, ch);\n}\n\nvoid canvas_set_bitmap_mode(Canvas* canvas, bool alpha) {\n    u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0);\n}\n\nvoid canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) {\n    furi_check(canvas);\n    const u8g2_cb_t* rotate_cb = NULL;\n    bool need_swap = false;\n    if(canvas->orientation != orientation) {\n        switch(orientation) {\n        case CanvasOrientationHorizontal:\n            need_swap = canvas->orientation == CanvasOrientationVertical ||\n                        canvas->orientation == CanvasOrientationVerticalFlip;\n            rotate_cb = U8G2_R0;\n            break;\n        case CanvasOrientationHorizontalFlip:\n            need_swap = canvas->orientation == CanvasOrientationVertical ||\n                        canvas->orientation == CanvasOrientationVerticalFlip;\n            rotate_cb = U8G2_R2;\n            break;\n        case CanvasOrientationVertical:\n            need_swap = canvas->orientation == CanvasOrientationHorizontal ||\n                        canvas->orientation == CanvasOrientationHorizontalFlip;\n            rotate_cb = U8G2_R3;\n            break;\n        case CanvasOrientationVerticalFlip:\n            need_swap = canvas->orientation == CanvasOrientationHorizontal ||\n                        canvas->orientation == CanvasOrientationHorizontalFlip;\n            rotate_cb = U8G2_R1;\n            break;\n        default:\n            furi_crash();\n        }\n\n        if(need_swap) FURI_SWAP(canvas->width, canvas->height);\n        u8g2_SetDisplayRotation(&canvas->fb, rotate_cb);\n        canvas->orientation = orientation;\n    }\n}\n\nCanvasOrientation canvas_get_orientation(const Canvas* canvas) {\n    return canvas->orientation;\n}\n\nvoid canvas_add_framebuffer_callback(Canvas* canvas, CanvasCommitCallback callback, void* context) {\n    furi_check(canvas);\n\n    const CanvasCallbackPair p = {callback, context};\n\n    canvas_lock(canvas);\n    furi_check(!CanvasCallbackPairArray_count(canvas->canvas_callback_pair, p));\n    CanvasCallbackPairArray_push_back(canvas->canvas_callback_pair, p);\n    canvas_unlock(canvas);\n}\n\nvoid canvas_remove_framebuffer_callback(\n    Canvas* canvas,\n    CanvasCommitCallback callback,\n    void* context) {\n    furi_check(canvas);\n\n    const CanvasCallbackPair p = {callback, context};\n\n    canvas_lock(canvas);\n    furi_check(CanvasCallbackPairArray_count(canvas->canvas_callback_pair, p) == 1);\n    CanvasCallbackPairArray_remove_val(canvas->canvas_callback_pair, p);\n    canvas_unlock(canvas);\n}\n"
  },
  {
    "path": "applications/services/gui/canvas.h",
    "content": "/**\n * @file canvas.h\n * GUI: Canvas API\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n#include <gui/icon_animation.h>\n#include <gui/icon.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Color enumeration */\ntypedef enum {\n    ColorWhite = 0x00,\n    ColorBlack = 0x01,\n    ColorXOR = 0x02,\n} Color;\n\n/** Fonts enumeration */\ntypedef enum {\n    FontPrimary,\n    FontSecondary,\n    FontKeyboard,\n    FontBigNumbers,\n\n    // Keep last for fonts number calculation\n    FontTotalNumber,\n} Font;\n\n/** Alignment enumeration */\ntypedef enum {\n    AlignLeft,\n    AlignRight,\n    AlignTop,\n    AlignBottom,\n    AlignCenter,\n} Align;\n\n/** Canvas Orientation */\ntypedef enum {\n    CanvasOrientationHorizontal,\n    CanvasOrientationHorizontalFlip,\n    CanvasOrientationVertical,\n    CanvasOrientationVerticalFlip,\n} CanvasOrientation;\n\n/** Font Direction */\ntypedef enum {\n    CanvasDirectionLeftToRight,\n    CanvasDirectionTopToBottom,\n    CanvasDirectionRightToLeft,\n    CanvasDirectionBottomToTop,\n} CanvasDirection;\n\n/** Font parameters */\ntypedef struct {\n    uint8_t leading_default;\n    uint8_t leading_min;\n    uint8_t height;\n    uint8_t descender;\n} CanvasFontParameters;\n\n/** Icon flip */\ntypedef enum {\n    IconFlipNone,\n    IconFlipHorizontal,\n    IconFlipVertical,\n    IconFlipBoth,\n} IconFlip;\n\n/** Icon rotation */\ntypedef enum {\n    IconRotation0,\n    IconRotation90,\n    IconRotation180,\n    IconRotation270,\n} IconRotation;\n\n/** Canvas anonymous structure */\ntypedef struct Canvas Canvas;\n\n/** Reset canvas drawing tools configuration\n *\n * @param      canvas  Canvas instance\n */\nvoid canvas_reset(Canvas* canvas);\n\n/** Commit canvas. Send buffer to display\n *\n * @param      canvas  Canvas instance\n */\nvoid canvas_commit(Canvas* canvas);\n\n/** Get Canvas width\n *\n * @param      canvas  Canvas instance\n *\n * @return     width in pixels.\n */\nsize_t canvas_width(const Canvas* canvas);\n\n/** Get Canvas height\n *\n * @param      canvas  Canvas instance\n *\n * @return     height in pixels.\n */\nsize_t canvas_height(const Canvas* canvas);\n\n/** Get current font height\n *\n * @param      canvas  Canvas instance\n *\n * @return     height in pixels.\n */\nsize_t canvas_current_font_height(const Canvas* canvas);\n\n/** Get font parameters\n *\n * @param      canvas  Canvas instance\n * @param      font    Font\n *\n * @return     pointer to CanvasFontParameters structure\n */\nconst CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font);\n\n/** Clear canvas\n *\n * @param      canvas  Canvas instance\n */\nvoid canvas_clear(Canvas* canvas);\n\n/** Set drawing color\n *\n * @param      canvas  Canvas instance\n * @param      color   Color\n */\nvoid canvas_set_color(Canvas* canvas, Color color);\n\n/** Set font swap Argument String Rotation Description\n *\n * @param      canvas  Canvas instance\n * @param      dir     Direction font\n */\nvoid canvas_set_font_direction(Canvas* canvas, CanvasDirection dir);\n\n/** Invert drawing color\n *\n * @param      canvas  Canvas instance\n */\nvoid canvas_invert_color(Canvas* canvas);\n\n/** Set drawing font\n *\n * @param      canvas  Canvas instance\n * @param      font    Font\n */\nvoid canvas_set_font(Canvas* canvas, Font font);\n\n/** Set custom drawing font\n *\n * @param      canvas  Canvas instance\n * @param      font    Pointer to u8g2 const uint8_t* font array\n */\nvoid canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font);\n\n/** Draw string at position of baseline defined by x, y.\n *\n * @param      canvas  Canvas instance\n * @param      x       anchor point x coordinate\n * @param      y       anchor point y coordinate\n * @param      str     C-string\n */\nvoid canvas_draw_str(Canvas* canvas, int32_t x, int32_t y, const char* str);\n\n/** Draw aligned string defined by x, y.\n *\n * Align calculated from position of baseline, string width and ascent (height\n * of the glyphs above the baseline)\n *\n * @param      canvas      Canvas instance\n * @param      x           anchor point x coordinate\n * @param      y           anchor point y coordinate\n * @param      horizontal  horizontal alignment\n * @param      vertical    vertical alignment\n * @param      str         C-string\n */\nvoid canvas_draw_str_aligned(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    Align horizontal,\n    Align vertical,\n    const char* str);\n\n/** Get string width\n *\n * @param      canvas  Canvas instance\n * @param      str     C-string\n *\n * @return     width in pixels.\n */\nuint16_t canvas_string_width(Canvas* canvas, const char* str);\n\n/** Get glyph width\n *\n * @param      canvas  Canvas instance\n * @param[in]  symbol  character\n *\n * @return     width in pixels\n */\nsize_t canvas_glyph_width(Canvas* canvas, uint16_t symbol);\n\n/** Draw bitmap picture at position defined by x,y.\n *\n * @param      canvas                  Canvas instance\n * @param      x                       x coordinate\n * @param      y                       y coordinate\n * @param      width                   width of bitmap\n * @param      height                  height of bitmap\n * @param      compressed_bitmap_data  compressed bitmap data\n */\nvoid canvas_draw_bitmap(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* compressed_bitmap_data);\n\n/** Draw icon at position defined by x,y with rotation and flip.\n *\n * @param      canvas    Canvas instance\n * @param      x         x coordinate\n * @param      y         y coordinate\n * @param      icon      Icon instance\n * @param      rotation  IconRotation\n */\nvoid canvas_draw_icon_ex(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    const Icon* icon,\n    IconRotation rotation);\n\n/** Draw animation at position defined by x,y.\n *\n * @param      canvas          Canvas instance\n * @param      x               x coordinate\n * @param      y               y coordinate\n * @param      icon_animation  IconAnimation instance\n */\nvoid canvas_draw_icon_animation(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    IconAnimation* icon_animation);\n\n/** Draw icon at position defined by x,y.\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      icon    Icon instance\n */\nvoid canvas_draw_icon(Canvas* canvas, int32_t x, int32_t y, const Icon* icon);\n\n/** Draw XBM bitmap\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param[in]  width   bitmap width\n * @param[in]  height  bitmap height\n * @param      bitmap  pointer to XBM bitmap data\n */\nvoid canvas_draw_xbm(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* bitmap);\n\n/** Draw rotated XBM bitmap\n *\n * @param      canvas       Canvas instance\n * @param      x            x coordinate\n * @param      y            y coordinate\n * @param[in]  width        bitmap width\n * @param[in]  height       bitmap height\n * @param[in]  rotation     bitmap rotation\n * @param      bitmap_data  pointer to XBM bitmap data\n */\nvoid canvas_draw_xbm_ex(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    IconRotation rotation,\n    const uint8_t* bitmap_data);\n\n/** Draw dot at x,y\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n */\nvoid canvas_draw_dot(Canvas* canvas, int32_t x, int32_t y);\n\n/** Draw box of width, height at x,y\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      width   box width\n * @param      height  box height\n */\nvoid canvas_draw_box(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height);\n\n/** Draw frame of width, height at x,y\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      width   frame width\n * @param      height  frame height\n */\nvoid canvas_draw_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height);\n\n/** Draw line from x1,y1 to x2,y2\n *\n * @param      canvas  Canvas instance\n * @param      x1      x1 coordinate\n * @param      y1      y1 coordinate\n * @param      x2      x2 coordinate\n * @param      y2      y2 coordinate\n */\nvoid canvas_draw_line(Canvas* canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2);\n\n/** Draw circle at x,y with radius r\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      radius  radius\n */\nvoid canvas_draw_circle(Canvas* canvas, int32_t x, int32_t y, size_t radius);\n\n/** Draw disc at x,y with radius r\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      radius  radius\n */\nvoid canvas_draw_disc(Canvas* canvas, int32_t x, int32_t y, size_t radius);\n\n/** Draw triangle with given base and height lengths and their intersection\n * coordinate\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate of base and height intersection\n * @param      y       y coordinate of base and height intersection\n * @param      base    length of triangle side\n * @param      height  length of triangle height\n * @param      dir     CanvasDirection triangle orientation\n */\nvoid canvas_draw_triangle(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t base,\n    size_t height,\n    CanvasDirection dir);\n\n/** Draw glyph\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      ch      character\n */\nvoid canvas_draw_glyph(Canvas* canvas, int32_t x, int32_t y, uint16_t ch);\n\n/** Set transparency mode\n *\n * @param      canvas  Canvas instance\n * @param      alpha   transparency mode\n */\nvoid canvas_set_bitmap_mode(Canvas* canvas, bool alpha);\n\n/** Draw rounded-corner frame of width, height at x,y, with round value radius\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      width   frame width\n * @param      height  frame height\n * @param      radius  frame corner radius\n */\nvoid canvas_draw_rframe(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    size_t radius);\n\n/** Draw rounded-corner box of width, height at x,y, with round value raduis\n *\n * @param      canvas  Canvas instance\n * @param      x       x coordinate\n * @param      y       y coordinate\n * @param      width   box width\n * @param      height  box height\n * @param      radius  box corner radius\n */\nvoid canvas_draw_rbox(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    size_t radius);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/canvas_i.h",
    "content": "/**\n * @file canvas_i.h\n * GUI: internal Canvas API\n */\n\n#pragma once\n\n#include \"canvas.h\"\n#include <u8g2.h>\n#include <toolbox/compress.h>\n#include <m-array.h>\n#include <m-algo.h>\n#include <furi.h>\n\n#define ICON_DECOMPRESSOR_BUFFER_SIZE (128u * 64 / 8)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*CanvasCommitCallback)(\n    uint8_t* data,\n    size_t size,\n    CanvasOrientation orientation,\n    void* context);\n\ntypedef struct {\n    CanvasCommitCallback callback;\n    void* context;\n} CanvasCallbackPair;\n\nARRAY_DEF(CanvasCallbackPairArray, CanvasCallbackPair, M_POD_OPLIST); //-V658\n\n#define M_OPL_CanvasCallbackPairArray_t() ARRAY_OPLIST(CanvasCallbackPairArray, M_POD_OPLIST)\n\nALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t);\n\n/** Canvas structure\n */\nstruct Canvas {\n    u8g2_t fb;\n    CanvasOrientation orientation;\n    size_t offset_x;\n    size_t offset_y;\n    size_t width;\n    size_t height;\n    CompressIcon* compress_icon;\n    CanvasCallbackPairArray_t canvas_callback_pair;\n    FuriMutex* mutex;\n};\n\n/** Allocate memory and initialize canvas\n *\n * @return     Canvas instance\n */\nCanvas* canvas_init(void);\n\n/** Free canvas memory\n *\n * @param      canvas  Canvas instance\n */\nvoid canvas_free(Canvas* canvas);\n\n/** Get canvas buffer.\n *\n * @param      canvas  Canvas instance\n *\n * @return     pointer to buffer\n */\nuint8_t* canvas_get_buffer(Canvas* canvas);\n\n/** Get canvas buffer size.\n *\n * @param      canvas  Canvas instance\n *\n * @return     size of canvas in bytes\n */\nsize_t canvas_get_buffer_size(const Canvas* canvas);\n\n/** Set drawing region relative to real screen buffer\n *\n * @param      canvas    Canvas instance\n * @param      offset_x  x coordinate offset\n * @param      offset_y  y coordinate offset\n * @param      width     width\n * @param      height    height\n */\nvoid canvas_frame_set(\n    Canvas* canvas,\n    int32_t offset_x,\n    int32_t offset_y,\n    size_t width,\n    size_t height);\n\n/** Set canvas orientation\n *\n * @param      canvas       Canvas instance\n * @param      orientation  CanvasOrientation\n */\nvoid canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation);\n\n/** Get canvas orientation\n *\n * @param      canvas  Canvas instance\n *\n * @return     CanvasOrientation\n */\nCanvasOrientation canvas_get_orientation(const Canvas* canvas);\n\n/** Draw a u8g2 bitmap\n *\n * @param      u8g2     u8g2 instance\n * @param      x        x coordinate\n * @param      y        y coordinate\n * @param      width    width\n * @param      height   height\n * @param      bitmap   bitmap\n * @param      rotation rotation\n */\nvoid canvas_draw_u8g2_bitmap(\n    u8g2_t* u8g2,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    const uint8_t* bitmap,\n    IconRotation rotation);\n\n/** Add canvas commit callback.\n *\n * This callback will be called upon Canvas commit.\n * \n * @param      canvas    Canvas instance\n * @param      callback  CanvasCommitCallback\n * @param      context   CanvasCommitCallback context\n */\nvoid canvas_add_framebuffer_callback(Canvas* canvas, CanvasCommitCallback callback, void* context);\n\n/** Remove canvas commit callback.\n *\n * @param      canvas    Canvas instance\n * @param      callback  CanvasCommitCallback\n * @param      context   CanvasCommitCallback context\n */\nvoid canvas_remove_framebuffer_callback(\n    Canvas* canvas,\n    CanvasCommitCallback callback,\n    void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/elements.c",
    "content": "#include \"elements.h\"\n#include <m-core.h>\n#include <assets_icons.h>\n#include <furi_hal_resources.h>\n#include <furi_hal.h>\n\n#include <gui/canvas.h>\n#include <gui/icon_i.h>\n#include <gui/icon_animation_i.h>\n\n#include <furi.h>\n\n#include <math.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdbool.h>\n\ntypedef struct {\n    int32_t x;\n    int32_t y;\n    int32_t leading_min;\n    int32_t leading_default;\n    size_t height;\n    size_t descender;\n    size_t len;\n    const char* text;\n} ElementTextBoxLine;\n\nvoid elements_progress_bar(Canvas* canvas, int32_t x, int32_t y, size_t width, float progress) {\n    furi_check(canvas);\n    furi_check((progress >= 0.0f) && (progress <= 1.0f));\n    size_t height = 9;\n\n    float progress_width = roundf(progress * (width - 2));\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_rframe(canvas, x, y, width, height, 3);\n\n    canvas_draw_box(canvas, x + 1, y + 1, progress_width, height - 2);\n}\n\nvoid elements_progress_bar_with_text(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    float progress,\n    const char* text) {\n    furi_check(canvas);\n    furi_check((progress >= 0.0f) && (progress <= 1.0f));\n    size_t height = 11;\n\n    float progress_width = roundf(progress * (width - 2));\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_rframe(canvas, x, y, width, height, 3);\n\n    canvas_draw_box(canvas, x + 1, y + 1, progress_width, height - 2);\n\n    canvas_set_color(canvas, ColorXOR);\n    canvas_set_font(canvas, FontSecondary);\n    canvas_draw_str_aligned(canvas, x + width / 2, y + 2, AlignCenter, AlignTop, text);\n}\n\nvoid elements_scrollbar_pos(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t height,\n    size_t pos,\n    size_t total) {\n    furi_check(canvas);\n\n    // prevent overflows\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x - 3, y, 3, height);\n\n    // dot line\n    canvas_set_color(canvas, ColorBlack);\n    for(int32_t i = y; i < (int32_t)height + y; i += 2) {\n        canvas_draw_dot(canvas, x - 2, i);\n    }\n\n    // Position block\n    if(total) {\n        float block_h = ((float)height) / total;\n        canvas_draw_box(canvas, x - 3, y + (block_h * pos), 3, MAX(block_h, 1));\n    }\n}\n\nvoid elements_scrollbar(Canvas* canvas, size_t pos, size_t total) {\n    furi_check(canvas);\n\n    size_t width = canvas_width(canvas);\n    size_t height = canvas_height(canvas);\n\n    // prevent overflows\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, width - 3, 0, 3, height);\n\n    // dot line\n    canvas_set_color(canvas, ColorBlack);\n    for(size_t i = 0; i < height; i += 2) {\n        canvas_draw_dot(canvas, width - 2, i);\n    }\n\n    // Position block\n    if(total) {\n        float block_h = ((float)height) / total;\n        canvas_draw_box(canvas, width - 3, block_h * pos, 3, MAX(block_h, 1));\n    }\n}\n\nvoid elements_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height) {\n    furi_check(canvas);\n\n    canvas_draw_line(canvas, x + 2, y, x + width - 2, y);\n    canvas_draw_line(canvas, x + 1, y + height - 1, x + width, y + height - 1);\n    canvas_draw_line(canvas, x + 2, y + height, x + width - 1, y + height);\n\n    canvas_draw_line(canvas, x, y + 2, x, y + height - 2);\n    canvas_draw_line(canvas, x + width - 1, y + 1, x + width - 1, y + height - 2);\n    canvas_draw_line(canvas, x + width, y + 2, x + width, y + height - 2);\n\n    canvas_draw_dot(canvas, x + 1, y + 1);\n}\n\nvoid elements_button_left(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n\n    const size_t button_height = 12;\n    const size_t vertical_offset = 3;\n    const size_t horizontal_offset = 3;\n    const size_t string_width = canvas_string_width(canvas, str);\n    const Icon* icon = &I_ButtonLeft_4x7;\n    const int32_t icon_h_offset = 3;\n    const int32_t icon_width_with_offset = icon->width + icon_h_offset;\n    const int32_t icon_v_offset = icon->height + vertical_offset;\n    const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const int32_t x = 0;\n    const int32_t y = canvas_height(canvas);\n\n    canvas_draw_box(canvas, x, y - button_height, button_width, button_height);\n    canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);\n    canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);\n    canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);\n\n    canvas_invert_color(canvas);\n    canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, &I_ButtonLeft_4x7);\n    canvas_draw_str(\n        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);\n    canvas_invert_color(canvas);\n}\n\nvoid elements_button_right(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n\n    const size_t button_height = 12;\n    const size_t vertical_offset = 3;\n    const size_t horizontal_offset = 3;\n    const size_t string_width = canvas_string_width(canvas, str);\n    const Icon* icon = &I_ButtonRight_4x7;\n    const int32_t icon_h_offset = 3;\n    const int32_t icon_width_with_offset = icon->width + icon_h_offset;\n    const int32_t icon_v_offset = icon->height + vertical_offset;\n    const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const int32_t x = canvas_width(canvas);\n    const int32_t y = canvas_height(canvas);\n\n    canvas_draw_box(canvas, x - button_width, y - button_height, button_width, button_height);\n    canvas_draw_line(canvas, x - button_width - 1, y, x - button_width - 1, y - button_height + 0);\n    canvas_draw_line(canvas, x - button_width - 2, y, x - button_width - 2, y - button_height + 1);\n    canvas_draw_line(canvas, x - button_width - 3, y, x - button_width - 3, y - button_height + 2);\n\n    canvas_invert_color(canvas);\n    canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);\n    canvas_draw_icon(\n        canvas, x - horizontal_offset - icon->width, y - icon_v_offset, &I_ButtonRight_4x7);\n    canvas_invert_color(canvas);\n}\n\nvoid elements_button_up(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n\n    const Icon* icon = &I_ButtonUp_7x4;\n\n    const size_t button_height = 12;\n    const size_t vertical_offset = 3;\n    const size_t horizontal_offset = 3;\n    const size_t string_width = canvas_string_width(canvas, str);\n    const int32_t icon_h_offset = 3;\n    const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;\n    const int32_t icon_v_offset = icon_get_height(icon) + (int32_t)vertical_offset;\n    const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const int32_t x = 0;\n    const int32_t y = 0 + button_height;\n\n    int32_t line_x = x + button_width;\n    int32_t line_y = y - button_height;\n\n    canvas_draw_box(canvas, x, line_y, button_width, button_height);\n    canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1);\n    canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2);\n    canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3);\n\n    canvas_invert_color(canvas);\n    canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon);\n    canvas_draw_str(\n        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);\n    canvas_invert_color(canvas);\n}\n\nvoid elements_button_down(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n\n    const Icon* icon = &I_ButtonDown_7x4;\n\n    const size_t button_height = 12;\n    const size_t vertical_offset = 3;\n    const size_t horizontal_offset = 3;\n    const size_t string_width = canvas_string_width(canvas, str);\n    const int32_t icon_h_offset = 3;\n    const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;\n    const int32_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;\n    const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const int32_t x = canvas_width(canvas);\n    const int32_t y = button_height;\n\n    int32_t line_x = x - button_width;\n    int32_t line_y = y - button_height;\n\n    canvas_draw_box(canvas, line_x, line_y, button_width, button_height);\n    canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1);\n    canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2);\n    canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3);\n\n    canvas_invert_color(canvas);\n    canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);\n    canvas_draw_icon(\n        canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);\n    canvas_invert_color(canvas);\n}\n\nvoid elements_button_center(Canvas* canvas, const char* str) {\n    furi_check(canvas);\n\n    const size_t button_height = 12;\n    const size_t vertical_offset = 3;\n    const size_t horizontal_offset = 1;\n    const size_t string_width = canvas_string_width(canvas, str);\n    const Icon* icon = &I_ButtonCenter_7x7;\n    const int32_t icon_h_offset = 3;\n    const int32_t icon_width_with_offset = icon->width + icon_h_offset;\n    const int32_t icon_v_offset = icon->height + vertical_offset;\n    const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;\n\n    const int32_t x = (canvas_width(canvas) - button_width) / 2;\n    const int32_t y = canvas_height(canvas);\n\n    canvas_draw_box(canvas, x, y - button_height, button_width, button_height);\n\n    canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0);\n    canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1);\n    canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2);\n\n    canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);\n    canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);\n    canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);\n\n    canvas_invert_color(canvas);\n    canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, &I_ButtonCenter_7x7);\n    canvas_draw_str(\n        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);\n    canvas_invert_color(canvas);\n}\n\nstatic size_t\n    elements_get_max_chars_to_fit(Canvas* canvas, Align horizontal, const char* text, int32_t x) {\n    const char* end = strchr(text, '\\n');\n    if(end == NULL) {\n        end = text + strlen(text);\n    }\n    size_t text_size = end - text;\n    FuriString* str;\n    str = furi_string_alloc_set(text);\n    furi_string_left(str, text_size);\n    size_t result = 0;\n\n    size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(str));\n    size_t px_left = 0;\n    if(horizontal == AlignCenter) {\n        if(x > (int32_t)(canvas_width(canvas) / 2)) {\n            px_left = (canvas_width(canvas) - x) * 2;\n        } else {\n            px_left = x * 2;\n        }\n    } else if(horizontal == AlignLeft) {\n        px_left = canvas_width(canvas) - x;\n    } else if(horizontal == AlignRight) {\n        px_left = x;\n    } else {\n        furi_crash();\n    }\n\n    if(len_px > px_left) {\n        size_t excess_symbols_approximately =\n            ceilf((float)(len_px - px_left) / ((float)len_px / (float)text_size));\n        // reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long\n        if(excess_symbols_approximately > 0) {\n            excess_symbols_approximately = MAX(excess_symbols_approximately, 5u);\n            result = text_size - excess_symbols_approximately - 1;\n        } else {\n            result = text_size;\n        }\n    } else {\n        result = text_size;\n    }\n\n    furi_string_free(str);\n    return result;\n}\n\nvoid elements_multiline_text_aligned(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    Align horizontal,\n    Align vertical,\n    const char* text) {\n    furi_check(canvas);\n    furi_check(text);\n\n    size_t lines_count = 0;\n    size_t font_height = canvas_current_font_height(canvas);\n    FuriString* line;\n\n    /* go through text line by line and count lines */\n    for(const char* start = text; start[0];) {\n        size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x);\n        ++lines_count;\n        start += chars_fit;\n        start += start[0] == '\\n' ? 1 : 0;\n    }\n\n    if(vertical == AlignBottom) {\n        y -= font_height * (lines_count - 1);\n    } else if(vertical == AlignCenter) {\n        y -= (font_height * (lines_count - 1)) / 2;\n    }\n\n    /* go through text line by line and print them */\n    for(const char* start = text; start[0];) {\n        size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x);\n\n        if((start[chars_fit] == '\\n') || (start[chars_fit] == 0)) {\n            line = furi_string_alloc_printf(\"%.*s\", chars_fit, start);\n        } else if((y + font_height) > canvas_height(canvas)) {\n            line = furi_string_alloc_printf(\"%.*s...\\n\", chars_fit, start);\n        } else {\n            chars_fit -= 1; // account for the dash\n            line = furi_string_alloc_printf(\"%.*s-\\n\", chars_fit, start);\n        }\n        canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));\n        furi_string_free(line);\n        y += font_height;\n        if(y > (int32_t)canvas_height(canvas)) {\n            break;\n        }\n\n        start += chars_fit;\n        start += start[0] == '\\n' ? 1 : 0;\n    }\n}\n\nvoid elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* text) {\n    furi_check(canvas);\n    furi_check(text);\n\n    size_t font_height = canvas_current_font_height(canvas);\n    FuriString* str;\n    str = furi_string_alloc();\n    const char* start = text;\n    char* end;\n    do {\n        end = strchr(start, '\\n');\n        if(end) {\n            furi_string_set_strn(str, start, end - start);\n            start = end + 1;\n        } else {\n            furi_string_set(str, start);\n        }\n        canvas_draw_str(canvas, x, y, furi_string_get_cstr(str));\n        y += font_height;\n    } while(end && y < 64);\n    furi_string_free(str);\n}\n\nvoid elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const char* text) {\n    furi_check(canvas);\n    furi_check(text);\n\n    size_t font_height = canvas_current_font_height(canvas);\n    size_t str_width = canvas_string_width(canvas, text);\n\n    // count \\n's\n    size_t lines = 1;\n    const char* t = text;\n    while(*t != '\\0') {\n        if(*t == '\\n') {\n            lines++;\n            size_t temp_width = canvas_string_width(canvas, t + 1);\n            str_width = temp_width > str_width ? temp_width : str_width;\n        }\n        t++;\n    }\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x, y - font_height, str_width + 8, font_height * lines + 4);\n    canvas_set_color(canvas, ColorBlack);\n    elements_multiline_text(canvas, x + 4, y - 1, text);\n    elements_frame(canvas, x, y - font_height, str_width + 8, font_height * lines + 4);\n}\n\nvoid elements_slightly_rounded_frame(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height) {\n    furi_check(canvas);\n    canvas_draw_rframe(canvas, x, y, width, height, 1);\n}\n\nvoid elements_slightly_rounded_box(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height) {\n    furi_check(canvas);\n    canvas_draw_rbox(canvas, x, y, width, height, 1);\n}\n\nvoid elements_bold_rounded_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height) {\n    furi_check(canvas);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x + 2, y + 2, width - 3, height - 3);\n    canvas_set_color(canvas, ColorBlack);\n\n    canvas_draw_line(canvas, x + 3, y, x + width - 3, y);\n    canvas_draw_line(canvas, x + 2, y + 1, x + width - 2, y + 1);\n\n    canvas_draw_line(canvas, x, y + 3, x, y + height - 3);\n    canvas_draw_line(canvas, x + 1, y + 2, x + 1, y + height - 2);\n\n    canvas_draw_line(canvas, x + width, y + 3, x + width, y + height - 3);\n    canvas_draw_line(canvas, x + width - 1, y + 2, x + width - 1, y + height - 2);\n\n    canvas_draw_line(canvas, x + 3, y + height, x + width - 3, y + height);\n    canvas_draw_line(canvas, x + 2, y + height - 1, x + width - 2, y + height - 1);\n\n    canvas_draw_dot(canvas, x + 2, y + 2);\n    canvas_draw_dot(canvas, x + 3, y + 2);\n    canvas_draw_dot(canvas, x + 2, y + 3);\n\n    canvas_draw_dot(canvas, x + width - 2, y + 2);\n    canvas_draw_dot(canvas, x + width - 3, y + 2);\n    canvas_draw_dot(canvas, x + width - 2, y + 3);\n\n    canvas_draw_dot(canvas, x + 2, y + height - 2);\n    canvas_draw_dot(canvas, x + 3, y + height - 2);\n    canvas_draw_dot(canvas, x + 2, y + height - 3);\n\n    canvas_draw_dot(canvas, x + width - 2, y + height - 2);\n    canvas_draw_dot(canvas, x + width - 3, y + height - 2);\n    canvas_draw_dot(canvas, x + width - 2, y + height - 3);\n}\n\nvoid elements_bubble(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height) {\n    furi_check(canvas);\n    canvas_draw_rframe(canvas, x + 4, y, width, height, 3);\n    int32_t y_corner = y + height * 2 / 3;\n    canvas_draw_line(canvas, x, y_corner, x + 4, y_corner - 4);\n    canvas_draw_line(canvas, x, y_corner, x + 4, y_corner + 4);\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_line(canvas, x + 4, y_corner - 3, x + 4, y_corner + 3);\n    canvas_set_color(canvas, ColorBlack);\n}\n\nvoid elements_bubble_str(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    const char* text,\n    Align horizontal,\n    Align vertical) {\n    furi_check(canvas);\n    furi_check(text);\n\n    size_t font_height = canvas_current_font_height(canvas);\n    size_t str_width = canvas_string_width(canvas, text);\n\n    // count \\n's\n    size_t lines = 1;\n    const char* t = text;\n    while(*t != '\\0') {\n        if(*t == '\\n') {\n            lines++;\n            size_t temp_width = canvas_string_width(canvas, t + 1);\n            str_width = temp_width > str_width ? temp_width : str_width;\n        }\n        t++;\n    }\n\n    int32_t frame_x = x;\n    int32_t frame_y = y;\n    size_t frame_width = str_width + 8;\n    size_t frame_height = font_height * lines + 4;\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 1);\n    elements_multiline_text(canvas, x + 4, y - 1 + font_height, text);\n\n    int32_t x1 = 0;\n    int32_t x2 = 0;\n    int32_t x3 = 0;\n    int32_t y1 = 0;\n    int32_t y2 = 0;\n    int32_t y3 = 0;\n    if((horizontal == AlignLeft) && (vertical == AlignTop)) {\n        x1 = frame_x;\n        y1 = frame_y;\n        x2 = frame_x - 4;\n        y2 = frame_y;\n        x3 = frame_x;\n        y3 = frame_y + 4;\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 + 2, y2 + 1, 2, 2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignLeft) && (vertical == AlignCenter)) {\n        x1 = frame_x;\n        y1 = frame_y + (frame_height - 1) / 2 - 4;\n        x2 = frame_x - 4;\n        y2 = frame_y + (frame_height - 1) / 2;\n        x3 = frame_x;\n        y3 = frame_y + (frame_height - 1) / 2 + 4;\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 + 2, y2 - 2, 2, 5);\n        canvas_draw_dot(canvas, x2 + 1, y2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignLeft) && (vertical == AlignBottom)) {\n        x1 = frame_x;\n        y1 = frame_y + (frame_height - 1) - 4;\n        x2 = frame_x - 4;\n        y2 = frame_y + (frame_height - 1);\n        x3 = frame_x;\n        y3 = frame_y + (frame_height - 1);\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 + 2, y2 - 2, 2, 2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignRight) && (vertical == AlignTop)) {\n        x1 = frame_x + (frame_width - 1);\n        y1 = frame_y;\n        x2 = frame_x + (frame_width - 1) + 4;\n        y2 = frame_y;\n        x3 = frame_x + (frame_width - 1);\n        y3 = frame_y + 4;\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 - 3, y2 + 1, 2, 2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignRight) && (vertical == AlignCenter)) {\n        x1 = frame_x + (frame_width - 1);\n        y1 = frame_y + (frame_height - 1) / 2 - 4;\n        x2 = frame_x + (frame_width - 1) + 4;\n        y2 = frame_y + (frame_height - 1) / 2;\n        x3 = frame_x + (frame_width - 1);\n        y3 = frame_y + (frame_height - 1) / 2 + 4;\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 - 3, y2 - 2, 2, 5);\n        canvas_draw_dot(canvas, x2 - 1, y2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignRight) && (vertical == AlignBottom)) {\n        x1 = frame_x + (frame_width - 1);\n        y1 = frame_y + (frame_height - 1) - 4;\n        x2 = frame_x + (frame_width - 1) + 4;\n        y2 = frame_y + (frame_height - 1);\n        x3 = frame_x + (frame_width - 1);\n        y3 = frame_y + (frame_height - 1);\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 - 3, y2 - 2, 2, 2);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignCenter) && (vertical == AlignTop)) {\n        x1 = frame_x + (frame_width - 1) / 2 - 4;\n        y1 = frame_y;\n        x2 = frame_x + (frame_width - 1) / 2;\n        y2 = frame_y - 4;\n        x3 = frame_x + (frame_width - 1) / 2 + 4;\n        y3 = frame_y;\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 - 2, y2 + 2, 5, 2);\n        canvas_draw_dot(canvas, x2, y2 + 1);\n        canvas_set_color(canvas, ColorBlack);\n    } else if((horizontal == AlignCenter) && (vertical == AlignBottom)) {\n        x1 = frame_x + (frame_width - 1) / 2 - 4;\n        y1 = frame_y + (frame_height - 1);\n        x2 = frame_x + (frame_width - 1) / 2;\n        y2 = frame_y + (frame_height - 1) + 4;\n        x3 = frame_x + (frame_width - 1) / 2 + 4;\n        y3 = frame_y + (frame_height - 1);\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, x2 - 2, y2 - 3, 5, 2);\n        canvas_draw_dot(canvas, x2, y2 - 1);\n        canvas_set_color(canvas, ColorBlack);\n    }\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_line(canvas, x3, y3, x1, y1);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_line(canvas, x1, y1, x2, y2);\n    canvas_draw_line(canvas, x2, y2, x3, y3);\n}\n\nvoid elements_string_fit_width(Canvas* canvas, FuriString* string, size_t width) {\n    furi_check(canvas);\n    furi_check(string);\n\n    size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(string));\n    if(len_px > width) {\n        width -= canvas_string_width(canvas, \"...\");\n        do {\n            furi_string_left(string, furi_string_size(string) - 1);\n            len_px = canvas_string_width(canvas, furi_string_get_cstr(string));\n        } while(len_px > width);\n        furi_string_cat(string, \"...\");\n    }\n}\n\nvoid elements_scrollable_text_line(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    FuriString* string,\n    size_t scroll,\n    bool ellipsis) {\n    furi_check(canvas);\n    furi_check(string);\n\n    FuriString* line = furi_string_alloc_set(string);\n\n    size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line));\n    if(len_px > width) {\n        if(ellipsis) {\n            width -= canvas_string_width(canvas, \"...\");\n        }\n\n        // Calculate scroll size\n        size_t scroll_size = furi_string_size(line);\n        size_t right_width = 0;\n        for(size_t i = scroll_size; i > 0; i--) {\n            right_width += canvas_glyph_width(canvas, furi_string_get_char(line, i));\n            if(right_width > width) break;\n            scroll_size--;\n            if(!scroll_size) break;\n        }\n        // Ensure that we have something to scroll\n        if(scroll_size) {\n            scroll_size += 3;\n            scroll = scroll % scroll_size;\n            furi_string_right(line, scroll);\n        }\n\n        len_px = canvas_string_width(canvas, furi_string_get_cstr(line));\n        while(len_px > width) {\n            furi_string_left(line, furi_string_size(line) - 1);\n            len_px = canvas_string_width(canvas, furi_string_get_cstr(line));\n        }\n\n        if(ellipsis) {\n            furi_string_cat(line, \"...\");\n        }\n    }\n\n    canvas_draw_str(canvas, x, y, furi_string_get_cstr(line));\n    furi_string_free(line);\n}\n\nvoid elements_text_box(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots) {\n    furi_check(canvas);\n\n    ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM];\n    bool bold = false;\n    bool mono = false;\n    bool inverse = false;\n    bool inverse_present = false;\n    Font current_font = FontSecondary;\n    Font prev_font = FontSecondary;\n    const CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font);\n\n    // Fill line parameters\n    size_t line_leading_min = font_params->leading_min;\n    size_t line_leading_default = font_params->leading_default;\n    size_t line_height = font_params->height;\n    size_t line_descender = font_params->descender;\n    size_t line_num = 0;\n    size_t line_width = 0;\n    size_t line_len = 0;\n    size_t total_height_min = 0;\n    size_t total_height_default = 0;\n    size_t i = 0;\n    bool full_text_processed = false;\n    size_t dots_width = canvas_string_width(canvas, \"...\");\n\n    canvas_set_font(canvas, FontSecondary);\n\n    // Fill all lines\n    line[0].text = text;\n    for(i = 0; !full_text_processed; i++) {\n        line_len++;\n        // Identify line height\n        if(prev_font != current_font) {\n            font_params = canvas_get_font_params(canvas, current_font);\n            line_leading_min = MAX(line_leading_min, font_params->leading_min);\n            line_leading_default = MAX(line_leading_default, font_params->leading_default);\n            line_height = MAX(line_height, font_params->height);\n            line_descender = MAX(line_descender, font_params->descender);\n            prev_font = current_font;\n        }\n        // Set the font\n        if(text[i] == '\\e' && text[i + 1]) {\n            i++;\n            line_len++;\n            if(text[i] == ELEMENTS_BOLD_MARKER) {\n                if(bold) {\n                    current_font = FontSecondary;\n                } else {\n                    current_font = FontPrimary;\n                }\n                canvas_set_font(canvas, current_font);\n                bold = !bold;\n            }\n            if(text[i] == ELEMENTS_MONO_MARKER) {\n                if(mono) {\n                    current_font = FontSecondary;\n                } else {\n                    current_font = FontKeyboard;\n                }\n                canvas_set_font(canvas, FontKeyboard);\n                mono = !mono;\n            }\n            if(text[i] == ELEMENTS_INVERSE_MARKER) {\n                inverse_present = true;\n            }\n            continue;\n        }\n        if(text[i] != '\\n') {\n            line_width += canvas_glyph_width(canvas, text[i]);\n        }\n        // Process new line\n        if(text[i] == '\\n' || text[i] == '\\0' || line_width > width) {\n            if(line_width > width) {\n                line_width -= canvas_glyph_width(canvas, text[i--]);\n                line_len--;\n            }\n            if(text[i] == '\\0') {\n                full_text_processed = true;\n            }\n            if(inverse_present) {\n                line_leading_min += 1;\n                line_leading_default += 1;\n                inverse_present = false;\n            }\n            line[line_num].leading_min = line_leading_min;\n            line[line_num].leading_default = line_leading_default;\n            line[line_num].height = line_height;\n            line[line_num].descender = line_descender;\n            if(total_height_min + line_leading_min > height) {\n                break;\n            }\n            total_height_min += line_leading_min;\n            total_height_default += line_leading_default;\n            line[line_num].len = line_len;\n            if(horizontal == AlignCenter) {\n                line[line_num].x = x + (width - line_width) / 2;\n            } else if(horizontal == AlignRight) {\n                line[line_num].x = x + (width - line_width);\n            } else {\n                line[line_num].x = x;\n            }\n            line[line_num].y = total_height_min;\n            line_num++;\n            if(!full_text_processed) {\n                line[line_num].text = &text[i + 1];\n            }\n            line_leading_min = font_params->leading_min;\n            line_height = font_params->height;\n            line_descender = font_params->descender;\n            line_width = 0;\n            line_len = 0;\n        }\n    }\n\n    // Set vertical alignment for all lines\n    if(total_height_default < height) {\n        if(vertical == AlignTop) {\n            line[0].y = y + line[0].height;\n        } else if(vertical == AlignCenter) {\n            line[0].y = y + line[0].height + (height - total_height_default) / 2;\n        } else if(vertical == AlignBottom) {\n            line[0].y = y + line[0].height + (height - total_height_default);\n        }\n        if(line_num > 1) {\n            for(size_t i = 1; i < line_num; i++) {\n                line[i].y = line[i - 1].y + line[i - 1].leading_default;\n            }\n        }\n    } else if(line_num > 1) {\n        size_t free_pixel_num = height - total_height_min;\n        size_t fill_pixel = 0;\n        size_t j = 1;\n        line[0].y = y + line[0].height;\n        while(fill_pixel < free_pixel_num) {\n            line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;\n            fill_pixel++;\n            j = j % (line_num - 1) + 1;\n        }\n    }\n\n    // Draw line by line\n    canvas_set_font(canvas, FontSecondary);\n    bold = false;\n    mono = false;\n    inverse = false;\n    for(size_t i = 0; i < line_num; i++) {\n        for(size_t j = 0; j < line[i].len; j++) {\n            // Process format symbols\n            if(line[i].text[j] == '\\e' && j < line[i].len - 1) { //-V781\n                ++j;\n                if(line[i].text[j] == ELEMENTS_BOLD_MARKER) {\n                    if(bold) {\n                        current_font = FontSecondary;\n                    } else {\n                        current_font = FontPrimary;\n                    }\n                    canvas_set_font(canvas, current_font);\n                    bold = !bold;\n                    continue;\n                }\n                if(line[i].text[j] == ELEMENTS_MONO_MARKER) {\n                    if(mono) {\n                        current_font = FontSecondary;\n                    } else {\n                        current_font = FontKeyboard;\n                    }\n                    canvas_set_font(canvas, current_font);\n                    mono = !mono;\n                    continue;\n                }\n                if(line[i].text[j] == ELEMENTS_INVERSE_MARKER) {\n                    inverse = !inverse;\n                    continue;\n                }\n            }\n            if(inverse) {\n                canvas_draw_box(\n                    canvas,\n                    line[i].x - 1,\n                    line[i].y - line[i].height - 1,\n                    canvas_glyph_width(canvas, line[i].text[j]) + 1,\n                    line[i].height + line[i].descender + 2);\n                canvas_invert_color(canvas);\n                canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);\n                canvas_invert_color(canvas);\n            } else {\n                if((i == line_num - 1) && strip_to_dots) {\n                    size_t next_symbol_width = canvas_glyph_width(canvas, line[i].text[j]);\n                    if((line[i].x + (int32_t)next_symbol_width + (int32_t)dots_width) >\n                       (x + (int32_t)width)) {\n                        canvas_draw_str(canvas, line[i].x, line[i].y, \"...\");\n                        break;\n                    }\n                }\n                canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);\n            }\n            line[i].x += canvas_glyph_width(canvas, line[i].text[j]);\n        }\n    }\n    canvas_set_font(canvas, FontSecondary);\n}\n"
  },
  {
    "path": "applications/services/gui/elements.h",
    "content": "/**\n * @file elements.h\n * GUI: Elements API\n * \n * Canvas helpers and UI building blocks.\n * \n */\n\n#pragma once\n\n#include <stdint.h>\n#include <furi.h>\n#include \"canvas.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ELEMENTS_MAX_LINES_NUM  (7)\n#define ELEMENTS_BOLD_MARKER    '#'\n#define ELEMENTS_MONO_MARKER    '*'\n#define ELEMENTS_INVERSE_MARKER '!'\n\n/** Draw progress bar.\n *\n * @param   canvas      Canvas instance\n * @param   x           progress bar position on X axis\n * @param   y           progress bar position on Y axis\n * @param   width       progress bar width\n * @param   progress    progress (0.0 - 1.0)\n */\nvoid elements_progress_bar(Canvas* canvas, int32_t x, int32_t y, size_t width, float progress);\n\n/** Draw progress bar with text.\n *\n * @param   canvas      Canvas instance\n * @param   x           progress bar position on X axis\n * @param   y           progress bar position on Y axis\n * @param   width       progress bar width\n * @param   progress    progress (0.0 - 1.0)\n * @param   text        text to draw\n */\nvoid elements_progress_bar_with_text(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    float progress,\n    const char* text);\n\n/** Draw scrollbar on canvas at specific position.\n *\n * @param   canvas  Canvas instance\n * @param   x       scrollbar position on X axis\n * @param   y       scrollbar position on Y axis\n * @param   height  scrollbar height\n * @param   pos     current element\n * @param   total   total elements\n */\nvoid elements_scrollbar_pos(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t height,\n    size_t pos,\n    size_t total);\n\n/** Draw scrollbar on canvas.\n * @note    width 3px, height equal to canvas height\n *\n * @param   canvas  Canvas instance\n * @param   pos     current element of total elements\n * @param   total   total elements\n */\nvoid elements_scrollbar(Canvas* canvas, size_t pos, size_t total);\n\n/** Draw rounded frame\n *\n * @param   canvas          Canvas instance\n * @param   x, y            top left corner coordinates\n * @param   width, height   frame width and height\n */\nvoid elements_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height);\n\n/** Draw button in left corner\n *\n * @param   canvas  Canvas instance\n * @param   str     button text\n */\nvoid elements_button_left(Canvas* canvas, const char* str);\n\n/** Draw button in right corner\n *\n * @param   canvas  Canvas instance\n * @param   str     button text\n */\nvoid elements_button_right(Canvas* canvas, const char* str);\n\n/**\n * @brief This function draws a button in the top left corner of the canvas with icon and string.\n *\n * The design and layout of the button is defined within this function.\n *\n * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn.\n * @param[in] str This is a pointer to the character string that will be drawn within the button.\n *\n */\nvoid elements_button_up(Canvas* canvas, const char* str);\n\n/**\n * @brief This function draws a button in the top right corner of the canvas with icon and string.\n *\n * The design and layout of the button is defined within this function.\n *\n * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn.\n * @param[in] str This is a pointer to the character string that will be drawn within the button.\n *\n */\nvoid elements_button_down(Canvas* canvas, const char* str);\n\n/** Draw button in center\n *\n * @param   canvas  Canvas instance\n * @param   str     button text\n */\nvoid elements_button_center(Canvas* canvas, const char* str);\n\n/** Draw aligned multiline text\n *\n * @param   canvas                  Canvas instance\n * @param   x, y                    coordinates based on align param\n * @param   horizontal, vertical    alignment of multiline text\n * @param   text                    string (possible multiline)\n */\nvoid elements_multiline_text_aligned(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    Align horizontal,\n    Align vertical,\n    const char* text);\n\n/** Draw multiline text\n *\n * @param   canvas  Canvas instance\n * @param   x       top left corner coordinates\n * @param   y       top left corner coordinates\n * @param   text    string (possible multiline)\n */\nvoid elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* text);\n\n/** Draw framed multiline text\n *\n * @param   canvas  Canvas instance\n * @param   x       top left corner coordinates\n * @param   y       top left corner coordinates\n * @param   text    string (possible multiline)\n */\nvoid elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const char* text);\n\n/** Draw slightly rounded frame\n *\n * @param   canvas          Canvas instance\n * @param   x               top left corner coordinates\n * @param   y               top left corner coordinates\n * @param   width           width of frame\n * @param   height          height of frame\n */\nvoid elements_slightly_rounded_frame(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height);\n\n/** Draw slightly rounded box\n *\n * @param   canvas          Canvas instance\n * @param   x               top left corner coordinates\n * @param   y               top left corner coordinates\n * @param   width           height of box\n * @param   height          height of box\n */\nvoid elements_slightly_rounded_box(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height);\n\n/** Draw bold rounded frame\n *\n * @param   canvas          Canvas instance\n * @param   x               top left corner coordinates\n * @param   y               top left corner coordinates\n * @param   width           width of frame\n * @param   height          height of frame\n */\nvoid elements_bold_rounded_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height);\n\n/** Draw bubble frame for text\n *\n * @param   canvas  Canvas instance\n * @param   x       left x coordinates\n * @param   y       top y coordinate\n * @param   width   bubble width\n * @param   height  bubble height\n */\nvoid elements_bubble(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height);\n\n/** Draw bubble frame for text with corner\n *\n * @param   canvas      Canvas instance\n * @param   x           left x coordinates\n * @param   y           top y coordinate\n * @param   text        text to display\n * @param   horizontal  horizontal aligning\n * @param   vertical    aligning\n */\nvoid elements_bubble_str(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    const char* text,\n    Align horizontal,\n    Align vertical);\n\n/** Trim string buffer to fit width in pixels\n *\n * @param   canvas  Canvas instance\n * @param   string  string to trim\n * @param   width   max width\n */\nvoid elements_string_fit_width(Canvas* canvas, FuriString* string, size_t width);\n\n/** Draw scrollable text line\n *\n * @param      canvas    The canvas\n * @param[in]  x         X coordinate\n * @param[in]  y         Y coordinate\n * @param[in]  width     The width\n * @param      string    The string\n * @param[in]  scroll    The scroll counter: 0 - no scroll, any other number - scroll. Just count up, everything else will be calculated on the inside.\n * @param[in]  ellipsis  The ellipsis flag: true to add ellipse\n */\nvoid elements_scrollable_text_line(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    FuriString* string,\n    size_t scroll,\n    bool ellipsis);\n\n/** Draw text box element\n *\n * @param       canvas          Canvas instance\n * @param       x               x coordinate\n * @param       y               y coordinate\n * @param       width           width to fit text\n * @param       height          height to fit text\n * @param       horizontal      Align instance\n * @param       vertical        Align instance\n * @param[in]   text            Formatted text. The following formats are available:\n *                              \"\\e#Bold text\\e#\" - bold font is used\n *                              \"\\e*Monospaced text\\e*\" - monospaced font is used\n *                              \"\\e!Inverted text\\e!\" - white text on black background\n * @param      strip_to_dots    Strip text to ... if does not fit to width\n */\nvoid elements_text_box(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t width,\n    size_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/gui.c",
    "content": "#include \"gui_i.h\"\n#include <assets_icons.h>\n\n#define TAG \"GuiSrv\"\n\nViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {\n    // Iterating backward\n    ViewPortArray_it_t it;\n    ViewPortArray_it_last(it, array);\n    while(!ViewPortArray_end_p(it)) {\n        ViewPort* view_port = *ViewPortArray_ref(it);\n        if(view_port_is_enabled(view_port)) {\n            return view_port;\n        }\n        ViewPortArray_previous(it);\n    }\n    return NULL;\n}\n\nsize_t gui_active_view_port_count(Gui* gui, GuiLayer layer) {\n    furi_assert(gui);\n    furi_check(layer < GuiLayerMAX);\n    size_t ret = 0;\n\n    gui_lock(gui);\n    ViewPortArray_it_t it;\n    ViewPortArray_it_last(it, gui->layers[layer]);\n    while(!ViewPortArray_end_p(it)) {\n        ViewPort* view_port = *ViewPortArray_ref(it);\n        if(view_port_is_enabled(view_port)) {\n            ret++;\n        }\n        ViewPortArray_previous(it);\n    }\n    gui_unlock(gui);\n\n    return ret;\n}\n\nvoid gui_update(Gui* gui) {\n    furi_assert(gui);\n    if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW);\n}\n\nvoid gui_input_events_callback(const void* value, void* ctx) {\n    furi_assert(value);\n    furi_assert(ctx);\n\n    Gui* gui = ctx;\n\n    furi_message_queue_put(gui->input_queue, value, FuriWaitForever);\n    furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT);\n}\n\n// Only Fullscreen supports vertical display for now\nstatic bool gui_redraw_fs(Gui* gui) {\n    canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);\n    canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);\n    ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);\n    if(view_port) {\n        view_port_draw(view_port, gui->canvas);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstatic void gui_redraw_status_bar(Gui* gui, bool need_attention) {\n    ViewPortArray_it_t it;\n    uint8_t left_used = 0;\n    uint8_t right_used = 0;\n    uint8_t width;\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) {\n        canvas_set_orientation(gui->canvas, CanvasOrientationHorizontalFlip);\n    } else {\n        canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);\n    }\n\n    canvas_frame_set(\n        gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT);\n\n    /* for support black theme - paint white area and\n     * draw icon with transparent white color\n     */\n    canvas_set_color(gui->canvas, ColorWhite);\n    canvas_draw_box(gui->canvas, 1, 1, 9, 7);\n    canvas_draw_box(gui->canvas, 7, 3, 58, 6);\n    canvas_draw_box(gui->canvas, 61, 1, 32, 7);\n    canvas_draw_box(gui->canvas, 89, 3, 38, 6);\n    canvas_set_color(gui->canvas, ColorBlack);\n    canvas_set_bitmap_mode(gui->canvas, 1);\n    canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11);\n    canvas_set_bitmap_mode(gui->canvas, 0);\n\n    // Right side\n    uint8_t x = GUI_DISPLAY_WIDTH - 1;\n    ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]);\n    while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) {\n        ViewPort* view_port = *ViewPortArray_ref(it);\n        if(view_port_is_enabled(view_port)) {\n            width = view_port_get_width(view_port);\n            if(!width) width = 8;\n            // Recalculate next position\n            right_used += (width + 2);\n            x -= (width + 2);\n            // Prepare work area background\n            canvas_frame_set(\n                gui->canvas,\n                x - 1,\n                GUI_STATUS_BAR_Y + 1,\n                width + 2,\n                GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);\n            canvas_set_color(gui->canvas, ColorWhite);\n            canvas_draw_box(\n                gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));\n            canvas_set_color(gui->canvas, ColorBlack);\n            // ViewPort draw\n            canvas_frame_set(\n                gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);\n            view_port_draw(view_port, gui->canvas);\n        }\n        ViewPortArray_next(it);\n    }\n    // Draw frame around icons on the right\n    if(right_used) {\n        canvas_frame_set(\n            gui->canvas,\n            GUI_DISPLAY_WIDTH - 3 - right_used,\n            GUI_STATUS_BAR_Y,\n            right_used + 3,\n            GUI_STATUS_BAR_HEIGHT);\n        canvas_set_color(gui->canvas, ColorBlack);\n        canvas_draw_rframe(\n            gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1);\n        canvas_draw_line(\n            gui->canvas,\n            canvas_width(gui->canvas) - 2,\n            1,\n            canvas_width(gui->canvas) - 2,\n            canvas_height(gui->canvas) - 2);\n        canvas_draw_line(\n            gui->canvas,\n            1,\n            canvas_height(gui->canvas) - 2,\n            canvas_width(gui->canvas) - 2,\n            canvas_height(gui->canvas) - 2);\n    }\n\n    // Left side\n    x = 2;\n    ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]);\n    while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) {\n        ViewPort* view_port = *ViewPortArray_ref(it);\n        if(view_port_is_enabled(view_port)) {\n            width = view_port_get_width(view_port);\n            if(!width) width = 8;\n            // Prepare work area background\n            canvas_frame_set(\n                gui->canvas,\n                x - 1,\n                GUI_STATUS_BAR_Y + 1,\n                width + 2,\n                GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);\n            canvas_set_color(gui->canvas, ColorWhite);\n            canvas_draw_box(\n                gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));\n            canvas_set_color(gui->canvas, ColorBlack);\n            // ViewPort draw\n            canvas_frame_set(\n                gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);\n            view_port_draw(view_port, gui->canvas);\n            // Recalculate next position\n            left_used += (width + 2);\n            x += (width + 2);\n        }\n        ViewPortArray_next(it);\n    }\n    // Extra notification\n    if(need_attention) {\n        width = icon_get_width(&I_Hidden_window_9x8);\n        // Prepare work area background\n        canvas_frame_set(\n            gui->canvas,\n            x - 1,\n            GUI_STATUS_BAR_Y + 1,\n            width + 2,\n            GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);\n        canvas_set_color(gui->canvas, ColorWhite);\n        canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));\n        canvas_set_color(gui->canvas, ColorBlack);\n        // Draw Icon\n        canvas_frame_set(\n            gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);\n        canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8);\n        // Recalculate next position\n        left_used += (width + 2);\n        x += (width + 2);\n    }\n    // Draw frame around icons on the left\n    if(left_used) {\n        canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT);\n        canvas_draw_rframe(\n            gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1);\n        canvas_draw_line(\n            gui->canvas,\n            canvas_width(gui->canvas) - 2,\n            1,\n            canvas_width(gui->canvas) - 2,\n            canvas_height(gui->canvas) - 2);\n        canvas_draw_line(\n            gui->canvas,\n            1,\n            canvas_height(gui->canvas) - 2,\n            canvas_width(gui->canvas) - 2,\n            canvas_height(gui->canvas) - 2);\n    }\n}\n\nstatic bool gui_redraw_window(Gui* gui) {\n    canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);\n    canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);\n    ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);\n    if(view_port) {\n        view_port_draw(view_port, gui->canvas);\n        return true;\n    }\n    return false;\n}\n\nstatic bool gui_redraw_desktop(Gui* gui) {\n    canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);\n    canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);\n    ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);\n    if(view_port) {\n        view_port_draw(view_port, gui->canvas);\n        return true;\n    }\n\n    return false;\n}\n\nstatic void gui_redraw(Gui* gui) {\n    furi_assert(gui);\n    gui_lock(gui);\n\n    do {\n        if(gui->direct_draw) break;\n\n        canvas_reset(gui->canvas);\n\n        if(gui->lockdown) {\n            gui_redraw_desktop(gui);\n            bool need_attention =\n                (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 ||\n                 gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0);\n            gui_redraw_status_bar(gui, need_attention);\n        } else {\n            if(!gui_redraw_fs(gui)) {\n                if(!gui_redraw_window(gui)) {\n                    gui_redraw_desktop(gui);\n                }\n                gui_redraw_status_bar(gui, false);\n            }\n        }\n\n        canvas_commit(gui->canvas);\n    } while(false);\n\n    gui_unlock(gui);\n}\n\nstatic void gui_input(Gui* gui, InputEvent* input_event) {\n    furi_assert(gui);\n    furi_assert(input_event);\n\n    // Check input complementarity\n    uint8_t key_bit = (1 << input_event->key);\n    if(input_event->type == InputTypeRelease) {\n        gui->ongoing_input &= ~key_bit;\n    } else if(input_event->type == InputTypePress) {\n        gui->ongoing_input |= key_bit;\n    } else if(!(gui->ongoing_input & key_bit)) {\n        FURI_LOG_D(\n            TAG,\n            \"non-complementary input, discarding key: %s type: %s, sequence: %p\",\n            input_get_key_name(input_event->key),\n            input_get_type_name(input_event->type),\n            (void*)input_event->sequence);\n        return;\n    }\n\n    gui_lock(gui);\n\n    do {\n        if(gui->direct_draw && !gui->ongoing_input_view_port) {\n            break;\n        }\n\n        ViewPort* view_port = NULL;\n\n        if(gui->lockdown) {\n            view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);\n        } else {\n            view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);\n            if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);\n            if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);\n        }\n\n        if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {\n            gui->ongoing_input_view_port = view_port;\n        }\n\n        if(view_port && view_port == gui->ongoing_input_view_port) {\n            view_port_input(view_port, input_event);\n        } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {\n            FURI_LOG_D(\n                TAG,\n                \"ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port\",\n                gui->ongoing_input_view_port,\n                view_port,\n                input_get_key_name(input_event->key),\n                input_get_type_name(input_event->type),\n                (void*)input_event->sequence);\n            view_port_input(gui->ongoing_input_view_port, input_event);\n        } else {\n            FURI_LOG_D(\n                TAG,\n                \"ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p\",\n                gui->ongoing_input_view_port,\n                view_port,\n                input_get_key_name(input_event->key),\n                input_get_type_name(input_event->type),\n                (void*)input_event->sequence);\n        }\n    } while(false);\n\n    gui_unlock(gui);\n}\n\nvoid gui_lock(Gui* gui) {\n    furi_assert(gui);\n    furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid gui_unlock(Gui* gui) {\n    furi_assert(gui);\n    furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);\n}\n\nvoid gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {\n    furi_check(gui);\n    furi_check(view_port);\n    furi_check(layer < GuiLayerMAX);\n\n    // Only fullscreen supports Vertical orientation for now\n    ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port);\n    furi_check(\n        (layer == GuiLayerFullscreen) ||\n        ((view_port_orientation != ViewPortOrientationVertical) &&\n         (view_port_orientation != ViewPortOrientationVerticalFlip)));\n\n    gui_lock(gui);\n    // Verify that view port is not yet added\n    ViewPortArray_it_t it;\n    for(size_t i = 0; i < GuiLayerMAX; i++) {\n        ViewPortArray_it(it, gui->layers[i]);\n        while(!ViewPortArray_end_p(it)) {\n            furi_assert(*ViewPortArray_ref(it) != view_port);\n            ViewPortArray_next(it);\n        }\n    }\n    // Add view port and link with gui\n    ViewPortArray_push_back(gui->layers[layer], view_port);\n    view_port_gui_set(view_port, gui);\n    gui_unlock(gui);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nvoid gui_remove_view_port(Gui* gui, ViewPort* view_port) {\n    furi_check(gui);\n    furi_check(view_port);\n\n    gui_lock(gui);\n    view_port_gui_set(view_port, NULL);\n    ViewPortArray_it_t it;\n    for(size_t i = 0; i < GuiLayerMAX; i++) {\n        ViewPortArray_it(it, gui->layers[i]);\n        while(!ViewPortArray_end_p(it)) {\n            if(*ViewPortArray_ref(it) == view_port) {\n                ViewPortArray_remove(gui->layers[i], it);\n            } else {\n                ViewPortArray_next(it);\n            }\n        }\n    }\n    if(gui->ongoing_input_view_port == view_port) {\n        gui->ongoing_input_view_port = NULL;\n    }\n    gui_unlock(gui);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nvoid gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) {\n    furi_check(gui);\n    furi_check(view_port);\n\n    gui_lock(gui);\n    // Remove\n    GuiLayer layer = GuiLayerMAX;\n    ViewPortArray_it_t it;\n    for(size_t i = 0; i < GuiLayerMAX; i++) {\n        ViewPortArray_it(it, gui->layers[i]);\n        while(!ViewPortArray_end_p(it)) {\n            if(*ViewPortArray_ref(it) == view_port) {\n                ViewPortArray_remove(gui->layers[i], it);\n                furi_check(layer == GuiLayerMAX);\n                layer = i;\n            } else {\n                ViewPortArray_next(it);\n            }\n        }\n    }\n    furi_check(layer != GuiLayerMAX);\n    // Return to the top\n    ViewPortArray_push_back(gui->layers[layer], view_port);\n    gui_unlock(gui);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nvoid gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) {\n    furi_assert(gui);\n    furi_assert(view_port);\n\n    gui_lock(gui);\n    // Remove\n    GuiLayer layer = GuiLayerMAX;\n    ViewPortArray_it_t it;\n    for(size_t i = 0; i < GuiLayerMAX; i++) {\n        ViewPortArray_it(it, gui->layers[i]);\n        while(!ViewPortArray_end_p(it)) {\n            if(*ViewPortArray_ref(it) == view_port) {\n                ViewPortArray_remove(gui->layers[i], it);\n                furi_assert(layer == GuiLayerMAX);\n                layer = i;\n            } else {\n                ViewPortArray_next(it);\n            }\n        }\n    }\n    furi_assert(layer != GuiLayerMAX);\n    // Return to the top\n    ViewPortArray_push_at(gui->layers[layer], 0, view_port);\n    gui_unlock(gui);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nvoid gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) {\n    furi_check(gui);\n\n    canvas_add_framebuffer_callback(gui->canvas, callback, context);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nvoid gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) {\n    furi_check(gui);\n\n    canvas_remove_framebuffer_callback(gui->canvas, callback, context);\n}\n\nsize_t gui_get_framebuffer_size(const Gui* gui) {\n    furi_check(gui);\n\n    return canvas_get_buffer_size(gui->canvas);\n}\n\nvoid gui_set_lockdown(Gui* gui, bool lockdown) {\n    furi_check(gui);\n\n    gui_lock(gui);\n    gui->lockdown = lockdown;\n    gui_unlock(gui);\n\n    // Request redraw\n    gui_update(gui);\n}\n\nCanvas* gui_direct_draw_acquire(Gui* gui) {\n    furi_check(gui);\n\n    gui_lock(gui);\n    gui->direct_draw = true;\n    gui_unlock(gui);\n\n    canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);\n    canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);\n    canvas_reset(gui->canvas);\n    canvas_commit(gui->canvas);\n\n    return gui->canvas;\n}\n\nvoid gui_direct_draw_release(Gui* gui) {\n    furi_check(gui);\n\n    canvas_reset(gui->canvas);\n    canvas_commit(gui->canvas);\n\n    gui_lock(gui);\n    gui->direct_draw = false;\n    gui_unlock(gui);\n\n    gui_update(gui);\n}\n\nGui* gui_alloc(void) {\n    Gui* gui = malloc(sizeof(Gui));\n    // Thread ID\n    gui->thread_id = furi_thread_get_current_id();\n    // Allocate mutex\n    gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    // Layers\n    for(size_t i = 0; i < GuiLayerMAX; i++) {\n        ViewPortArray_init(gui->layers[i]);\n    }\n\n    // Drawing canvas\n    gui->canvas = canvas_init();\n\n    // Input\n    gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n    gui->input_events = furi_record_open(RECORD_INPUT_EVENTS);\n\n    furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);\n\n    return gui;\n}\n\nint32_t gui_srv(void* p) {\n    UNUSED(p);\n    Gui* gui = gui_alloc();\n\n    furi_record_create(RECORD_GUI, gui);\n\n    while(1) {\n        uint32_t flags =\n            furi_thread_flags_wait(GUI_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever);\n        // Process and dispatch input\n        if(flags & GUI_THREAD_FLAG_INPUT) {\n            // Process till queue become empty\n            InputEvent input_event;\n            while(furi_message_queue_get(gui->input_queue, &input_event, 0) == FuriStatusOk) {\n                gui_input(gui, &input_event);\n            }\n        }\n        // Process and dispatch draw call\n        if(flags & GUI_THREAD_FLAG_DRAW) {\n            // Clear flags that arrived on input step\n            furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);\n            gui_redraw(gui);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/gui/gui.h",
    "content": "/**\n * @file gui.h\n * GUI: main API\n */\n\n#pragma once\n\n#include \"view_port.h\"\n#include \"canvas.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Gui layers */\ntypedef enum {\n    GuiLayerDesktop, /**< Desktop layer for internal use. Like fullscreen but with status bar */\n\n    GuiLayerWindow, /**< Window layer, status bar is shown */\n\n    GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */\n    GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */\n\n    GuiLayerFullscreen, /**< Fullscreen layer, no status bar */\n\n    GuiLayerMAX /**< Don't use or move, special value */\n} GuiLayer;\n\n/** Gui Canvas Commit Callback */\ntypedef void (*GuiCanvasCommitCallback)(\n    uint8_t* data,\n    size_t size,\n    CanvasOrientation orientation,\n    void* context);\n\n#define RECORD_GUI \"gui\"\n\ntypedef struct Gui Gui;\n\n/** Add view_port to view_port tree\n *\n * @remark     thread safe\n *\n * @param      gui        Gui instance\n * @param      view_port  ViewPort instance\n * @param[in]  layer      GuiLayer where to place view_port\n */\nvoid gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer);\n\n/** Remove view_port from rendering tree\n *\n * @remark     thread safe\n *\n * @param      gui        Gui instance\n * @param      view_port  ViewPort instance\n */\nvoid gui_remove_view_port(Gui* gui, ViewPort* view_port);\n\n/** Send ViewPort to the front\n *\n * Places selected ViewPort to the top of the drawing stack\n *\n * @param      gui        Gui instance\n * @param      view_port  ViewPort instance\n */\nvoid gui_view_port_send_to_front(Gui* gui, ViewPort* view_port);\n\n/** Send ViewPort to the back\n *\n * Places selected ViewPort to the bottom of the drawing stack\n *\n * @param      gui        Gui instance\n * @param      view_port  ViewPort instance\n */\nvoid gui_view_port_send_to_back(Gui* gui, ViewPort* view_port);\n\n/** Add gui canvas commit callback\n *\n * This callback will be called upon Canvas commit Callback dispatched from GUI\n * thread and is time critical\n *\n * @param      gui       Gui instance\n * @param      callback  GuiCanvasCommitCallback\n * @param      context   GuiCanvasCommitCallback context\n */\nvoid gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context);\n\n/** Remove gui canvas commit callback\n *\n * @param      gui       Gui instance\n * @param      callback  GuiCanvasCommitCallback\n * @param      context   GuiCanvasCommitCallback context\n */\nvoid gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context);\n\n/** Get gui canvas frame buffer size\n * *\n * @param      gui       Gui instance\n * @return     size_t    size of frame buffer in bytes\n */\nsize_t gui_get_framebuffer_size(const Gui* gui);\n\n/** Set lockdown mode\n *\n * When lockdown mode is enabled, only GuiLayerDesktop is shown.\n * This feature prevents services from showing sensitive information when flipper is locked.\n *\n * @param      gui       Gui instance\n * @param      lockdown  bool, true if enabled\n */\nvoid gui_set_lockdown(Gui* gui, bool lockdown);\n\n/** Acquire Direct Draw lock and get Canvas instance\n *\n * This method return Canvas instance for use in monopoly mode. Direct draw lock\n * disables input and draw call dispatch functions in GUI service. No other\n * applications or services will be able to draw until gui_direct_draw_release\n * call.\n *\n * @param      gui   The graphical user interface\n *\n * @return     Canvas instance\n */\nCanvas* gui_direct_draw_acquire(Gui* gui);\n\n/** Release Direct Draw Lock\n *\n * Release Direct Draw Lock, enables Input and Draw call processing. Canvas\n * acquired in gui_direct_draw_acquire will become invalid after this call.\n *\n * @param      gui   Gui instance\n */\nvoid gui_direct_draw_release(Gui* gui);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/gui_i.h",
    "content": "/**\n * @file gui_i.h\n * GUI: main API internals\n */\n\n#pragma once\n\n#include \"gui.h\"\n\n#include <furi.h>\n#include <furi_hal_rtc.h>\n#include <m-array.h>\n#include <stdio.h>\n\n#include \"canvas.h\"\n#include \"canvas_i.h\"\n#include \"view_port.h\"\n#include \"view_port_i.h\"\n\n#define GUI_DISPLAY_WIDTH  128\n#define GUI_DISPLAY_HEIGHT 64\n\n#define GUI_STATUS_BAR_X               0\n#define GUI_STATUS_BAR_Y               0\n#define GUI_STATUS_BAR_WIDTH           GUI_DISPLAY_WIDTH\n/* 0-1 pixels for upper thin frame\n * 2-9 pixels for icons (battery, sd card, etc)\n * 10-12 pixels for lower bold line */\n#define GUI_STATUS_BAR_HEIGHT          13\n/* icon itself area (battery, sd card, etc) excluding frame.\n * painted 2 pixels below GUI_STATUS_BAR_X.\n */\n#define GUI_STATUS_BAR_WORKAREA_HEIGHT 8\n\n#define GUI_WINDOW_X      0\n#define GUI_WINDOW_Y      GUI_STATUS_BAR_HEIGHT\n#define GUI_WINDOW_WIDTH  GUI_DISPLAY_WIDTH\n#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y)\n\n#define GUI_THREAD_FLAG_DRAW  (1 << 0)\n#define GUI_THREAD_FLAG_INPUT (1 << 1)\n#define GUI_THREAD_FLAG_ALL   (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT)\n\nARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);\n\n/** Gui structure */\nstruct Gui {\n    // Thread and lock\n    FuriThreadId thread_id;\n    FuriMutex* mutex;\n\n    // Layers and Canvas\n    bool lockdown;\n    bool direct_draw;\n    ViewPortArray_t layers[GuiLayerMAX];\n    Canvas* canvas;\n\n    // Input\n    FuriMessageQueue* input_queue;\n    FuriPubSub* input_events;\n    uint8_t ongoing_input;\n    ViewPort* ongoing_input_view_port;\n};\n\n/** Find enabled ViewPort in ViewPortArray\n *\n * @param[in]  array  The ViewPortArray instance\n *\n * @return     ViewPort instance or NULL\n */\nViewPort* gui_view_port_find_enabled(ViewPortArray_t array);\n\n/** Update GUI, request redraw\n *\n * @param      gui   Gui instance\n */\nvoid gui_update(Gui* gui);\n\n/** Input event callback\n * \n * Used to receive input from input service or to inject new input events\n *\n * @param[in]  value  The value pointer (InputEvent*)\n * @param      ctx    The context (Gui instance)\n */\nvoid gui_input_events_callback(const void* value, void* ctx);\n\n/** Get count of view ports in layer\n *\n * @param      gui        The Gui instance\n * @param[in]  layer      GuiLayer that we want to get count of view ports\n */\nsize_t gui_active_view_port_count(Gui* gui, GuiLayer layer);\n\n/** Lock GUI\n *\n * @param      gui   The Gui instance\n */\nvoid gui_lock(Gui* gui);\n\n/** Unlock GUI\n *\n * @param      gui   The Gui instance\n */\nvoid gui_unlock(Gui* gui);\n"
  },
  {
    "path": "applications/services/gui/icon.c",
    "content": "#include \"icon.h\"\n#include \"icon_i.h\" // IWYU pragma: keep\n#include <furi.h>\n\n#include <furi.h>\n\nuint16_t icon_get_width(const Icon* instance) {\n    furi_check(instance);\n\n    return instance->width;\n}\n\nuint16_t icon_get_height(const Icon* instance) {\n    furi_check(instance);\n\n    return instance->height;\n}\n\nconst uint8_t* icon_get_data(const Icon* instance) {\n    furi_check(instance);\n\n    return icon_get_frame_data(instance, 0);\n}\n\nuint32_t icon_get_frame_count(const Icon* instance) {\n    return instance->frame_count;\n}\n\nconst uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame) {\n    furi_check(frame < instance->frame_count);\n    return instance->frames[frame];\n}\n"
  },
  {
    "path": "applications/services/gui/icon.h",
    "content": "/**\n * @file icon.h\n * GUI: Icon API\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <core/common_defines.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Icon Icon;\n\n/** Get icon width \n *\n * @param[in]  instance  pointer to Icon data\n *\n * @return     width in pixels\n */\nuint16_t icon_get_width(const Icon* instance);\n\n/** Get icon height\n *\n * @param[in]  instance  pointer to Icon data\n *\n * @return     height in pixels\n */\nuint16_t icon_get_height(const Icon* instance);\n\n/** Get Icon XBM bitmap data for the first frame\n *\n * @param[in]  instance  pointer to Icon data\n *\n * @return     pointer to compressed XBM bitmap data\n */\nFURI_DEPRECATED const uint8_t* icon_get_data(const Icon* instance);\n\n/** Get Icon frame count\n *\n * @param[in]  instance  pointer to Icon data\n *\n * @return     frame count\n */\nuint32_t icon_get_frame_count(const Icon* instance);\n\n/** Get Icon XBM bitmap data for a particular frame\n *\n * @param[in]  instance  pointer to Icon data\n * @param[in]  frame     frame index\n *\n * @return     pointer to compressed XBM bitmap data\n */\nconst uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/icon_animation.c",
    "content": "#include \"icon_animation_i.h\"\n#include \"icon_i.h\" // IWYU pragma: keep\n\n#include <furi.h>\n\nIconAnimation* icon_animation_alloc(const Icon* icon) {\n    furi_check(icon);\n\n    IconAnimation* instance = malloc(sizeof(IconAnimation));\n    instance->icon = icon;\n    instance->timer =\n        furi_timer_alloc(icon_animation_timer_callback, FuriTimerTypePeriodic, instance);\n\n    return instance;\n}\n\nvoid icon_animation_free(IconAnimation* instance) {\n    furi_check(instance);\n\n    icon_animation_stop(instance);\n    furi_timer_free(instance->timer);\n\n    free(instance);\n}\n\nvoid icon_animation_set_update_callback(\n    IconAnimation* instance,\n    IconAnimationCallback callback,\n    void* context) {\n    furi_check(instance);\n\n    instance->callback = callback;\n    instance->callback_context = context;\n}\n\nconst uint8_t* icon_animation_get_data(const IconAnimation* instance) {\n    return instance->icon->frames[instance->frame];\n}\n\nvoid icon_animation_next_frame(IconAnimation* instance) {\n    furi_assert(instance);\n    instance->frame = (instance->frame + 1) % instance->icon->frame_count;\n}\n\nvoid icon_animation_timer_callback(void* context) {\n    furi_assert(context);\n\n    IconAnimation* instance = context;\n\n    if(!instance->animating) return;\n\n    icon_animation_next_frame(instance);\n    if(instance->callback) {\n        instance->callback(instance, instance->callback_context);\n    }\n}\n\nuint8_t icon_animation_get_width(const IconAnimation* instance) {\n    furi_check(instance);\n\n    return instance->icon->width;\n}\n\nuint8_t icon_animation_get_height(const IconAnimation* instance) {\n    furi_check(instance);\n\n    return instance->icon->height;\n}\n\nvoid icon_animation_start(IconAnimation* instance) {\n    furi_check(instance);\n\n    if(!instance->animating) {\n        instance->animating = true;\n        furi_assert(instance->icon->frame_rate);\n        furi_check(\n            furi_timer_start(\n                instance->timer,\n                (furi_kernel_get_tick_frequency() / instance->icon->frame_rate)) == FuriStatusOk);\n    }\n}\n\nvoid icon_animation_stop(IconAnimation* instance) {\n    furi_check(instance);\n\n    if(instance->animating) {\n        instance->animating = false;\n        furi_timer_stop(instance->timer);\n        instance->frame = 0;\n    }\n}\n\nbool icon_animation_is_last_frame(const IconAnimation* instance) {\n    furi_check(instance);\n\n    return instance->icon->frame_count - instance->frame <= 1;\n}\n"
  },
  {
    "path": "applications/services/gui/icon_animation.h",
    "content": "/**\n * @file icon_animation.h\n * GUI: IconAnimation API\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <gui/icon.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Icon Animation */\ntypedef struct IconAnimation IconAnimation;\n\n/** Icon Animation Callback. Used for update notification */\ntypedef void (*IconAnimationCallback)(IconAnimation* instance, void* context);\n\n/** Allocate icon animation instance with const icon data.\n * \n * always returns Icon or stops system if not enough memory\n *\n * @param[in]  icon  pointer to Icon data\n *\n * @return     IconAnimation instance\n */\nIconAnimation* icon_animation_alloc(const Icon* icon);\n\n/** Release icon animation instance\n *\n * @param      instance  IconAnimation instance\n */\nvoid icon_animation_free(IconAnimation* instance);\n\n/** Set IconAnimation update callback\n *\n * Normally you do not need to use this function, use view_tie_icon_animation\n * instead.\n *\n * @param      instance  IconAnimation instance\n * @param[in]  callback  IconAnimationCallback\n * @param      context   callback context\n */\nvoid icon_animation_set_update_callback(\n    IconAnimation* instance,\n    IconAnimationCallback callback,\n    void* context);\n\n/** Get icon animation width\n *\n * @param      instance  IconAnimation instance\n *\n * @return     width in pixels\n */\nuint8_t icon_animation_get_width(const IconAnimation* instance);\n\n/** Get icon animation height\n *\n * @param      instance  IconAnimation instance\n *\n * @return     height in pixels\n */\nuint8_t icon_animation_get_height(const IconAnimation* instance);\n\n/** Start icon animation\n *\n * @param      instance  IconAnimation instance\n */\nvoid icon_animation_start(IconAnimation* instance);\n\n/** Stop icon animation\n *\n * @param      instance  IconAnimation instance\n */\nvoid icon_animation_stop(IconAnimation* instance);\n\n/** Returns true if current frame is a last one\n *\n * @param      instance  IconAnimation instance\n *\n * @return     true if last frame\n */\nbool icon_animation_is_last_frame(const IconAnimation* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/icon_animation_i.h",
    "content": "/**\n * @file icon_animation_i.h\n * GUI: internal IconAnimation API\n */\n\n#pragma once\n\n#include \"icon_animation.h\"\n\n#include <furi.h>\n\nstruct IconAnimation {\n    const Icon* icon;\n    uint8_t frame;\n    bool animating;\n    FuriTimer* timer;\n    IconAnimationCallback callback;\n    void* callback_context;\n};\n\n/** Get pointer to current frame data\n *\n * @param      instance  IconAnimation instance\n *\n * @return     pointer to current frame XBM bitmap data\n */\nconst uint8_t* icon_animation_get_data(const IconAnimation* instance);\n\n/** Advance to next frame\n *\n * @param      instance  IconAnimation instance\n */\nvoid icon_animation_next_frame(IconAnimation* instance);\n\n/** IconAnimation timer callback\n *\n * @param      context  pointer to IconAnimation\n */\nvoid icon_animation_timer_callback(void* context);\n"
  },
  {
    "path": "applications/services/gui/icon_i.h",
    "content": "/**\n * @file icon_i.h\n * GUI: internal Icon API\n */\n\n#pragma once\n#include <stdint.h>\n\nstruct Icon {\n    const uint16_t width;\n    const uint16_t height;\n    const uint8_t frame_count;\n    const uint8_t frame_rate;\n    const uint8_t* const* frames;\n};\n"
  },
  {
    "path": "applications/services/gui/modules/button_menu.c",
    "content": "#include \"button_menu.h\"\n\n#include <gui/canvas.h>\n#include <gui/elements.h>\n#include <input/input.h>\n\n#include <furi.h>\n#include <assets_icons.h>\n\n#include <stdint.h>\n#include <m-array.h>\n\n#define SCROLL_INTERVAL    (333)\n#define ITEM_FIRST_OFFSET  17\n#define ITEM_NEXT_OFFSET   4\n#define ITEM_HEIGHT        14\n#define ITEM_WIDTH         64\n#define BUTTONS_PER_SCREEN 6\n\nstruct ButtonMenuItem {\n    const char* label;\n    int32_t index;\n    ButtonMenuItemCallback callback;\n    ButtonMenuItemType type;\n    void* callback_context;\n};\n\nARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST); //-V658\n\nstruct ButtonMenu {\n    View* view;\n    bool freeze_input;\n};\n\ntypedef struct {\n    ButtonMenuItemArray_t items;\n    size_t position;\n    const char* header;\n    size_t scroll_counter;\n    FuriTimer* scroll_timer;\n} ButtonMenuModel;\n\nstatic void button_menu_draw_text(\n    Canvas* canvas,\n    uint8_t item_x,\n    uint8_t item_y,\n    const char* text,\n    bool selected,\n    ButtonMenuModel* model) {\n    FuriString* disp_str;\n    disp_str = furi_string_alloc_set(text);\n    bool draw_static = true;\n\n    if(selected) {\n        size_t text_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));\n        if(text_width >= ITEM_WIDTH - 8) {\n            elements_scrollable_text_line(\n                canvas,\n                item_x + 4,\n                item_y + ITEM_HEIGHT - 4,\n                ITEM_WIDTH - 8,\n                disp_str,\n                model->scroll_counter,\n                false);\n            draw_static = false;\n        }\n    }\n\n    if(draw_static) {\n        elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);\n        canvas_draw_str_aligned(\n            canvas,\n            item_x + (ITEM_WIDTH / 2),\n            item_y + (ITEM_HEIGHT / 2),\n            AlignCenter,\n            AlignCenter,\n            furi_string_get_cstr(disp_str));\n    }\n\n    furi_string_free(disp_str);\n}\n\nstatic void button_menu_draw_control_button(\n    Canvas* canvas,\n    uint8_t item_position,\n    const char* text,\n    bool selected,\n    ButtonMenuModel* model) {\n    furi_assert(canvas);\n    furi_assert(text);\n\n    uint8_t item_x = 0;\n    uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET));\n\n    canvas_set_color(canvas, ColorBlack);\n\n    if(selected) {\n        elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT);\n        canvas_set_color(canvas, ColorWhite);\n    }\n\n    button_menu_draw_text(canvas, item_x, item_y, text, selected, model);\n}\n\nstatic void button_menu_draw_common_button(\n    Canvas* canvas,\n    uint8_t item_position,\n    const char* text,\n    bool selected,\n    ButtonMenuModel* model) {\n    furi_assert(canvas);\n    furi_assert(text);\n\n    uint8_t item_x = 0;\n    uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET));\n\n    canvas_set_color(canvas, ColorBlack);\n\n    if(selected) {\n        canvas_draw_rbox(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5);\n        canvas_set_color(canvas, ColorWhite);\n    } else {\n        canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5);\n    }\n\n    button_menu_draw_text(canvas, item_x, item_y, text, selected, model);\n}\n\nstatic void button_menu_view_draw_callback(Canvas* canvas, void* _model) {\n    furi_assert(canvas);\n    furi_assert(_model);\n\n    ButtonMenuModel* model = (ButtonMenuModel*)_model;\n    canvas_set_font(canvas, FontSecondary);\n\n    const size_t active_screen = model->position / BUTTONS_PER_SCREEN;\n    const size_t items_size = ButtonMenuItemArray_size(model->items);\n    const size_t max_screen = items_size ? (items_size - 1) / BUTTONS_PER_SCREEN : 0;\n\n    if(active_screen > 0) {\n        canvas_draw_icon(canvas, 28, 1, &I_InfraredArrowUp_4x8);\n    }\n\n    if(max_screen > active_screen) {\n        canvas_draw_icon(canvas, 28, 123, &I_InfraredArrowDown_4x8);\n    }\n\n    if(model->header) {\n        FuriString* disp_str;\n        disp_str = furi_string_alloc_set(model->header);\n        size_t header_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));\n\n        if(header_width >= ITEM_WIDTH - 8) {\n            elements_scrollable_text_line(\n                canvas, 3, 13, ITEM_WIDTH - 8, disp_str, model->scroll_counter, false);\n        } else {\n            elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 8);\n            canvas_draw_str_aligned(\n                canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str));\n        }\n\n        furi_string_free(disp_str);\n    }\n\n    size_t item_position = 0;\n    ButtonMenuItemArray_it_t it;\n\n    for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it);\n        ButtonMenuItemArray_next(it), ++item_position) {\n        if(active_screen == (item_position / BUTTONS_PER_SCREEN)) {\n            if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeControl) {\n                button_menu_draw_control_button(\n                    canvas,\n                    item_position % BUTTONS_PER_SCREEN,\n                    ButtonMenuItemArray_cref(it)->label,\n                    (item_position == model->position),\n                    model);\n            } else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) {\n                button_menu_draw_common_button(\n                    canvas,\n                    item_position % BUTTONS_PER_SCREEN,\n                    ButtonMenuItemArray_cref(it)->label,\n                    (item_position == model->position),\n                    model);\n            }\n        }\n    }\n}\n\nstatic void button_menu_process_up(ButtonMenu* button_menu) {\n    furi_assert(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            if(model->position > 0) {\n                model->position--;\n                model->scroll_counter = 0;\n            } else {\n                model->position = ButtonMenuItemArray_size(model->items) - 1;\n                model->scroll_counter = 0;\n            }\n        },\n        true);\n}\n\nstatic void button_menu_process_down(ButtonMenu* button_menu) {\n    furi_assert(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) {\n                model->position++;\n                model->scroll_counter = 0;\n            } else {\n                model->position = 0;\n                model->scroll_counter = 0;\n            }\n        },\n        true);\n}\n\nstatic void button_menu_process_right(ButtonMenu* button_menu) {\n    furi_assert(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) {\n                size_t position_candidate = model->position + BUTTONS_PER_SCREEN;\n                position_candidate -= position_candidate % BUTTONS_PER_SCREEN;\n                if(position_candidate < (ButtonMenuItemArray_size(model->items))) {\n                    model->position = position_candidate;\n                    model->scroll_counter = 0;\n                } else {\n                    model->position = 0;\n                    model->scroll_counter = 0;\n                }\n            }\n        },\n        true);\n}\n\nstatic void button_menu_process_left(ButtonMenu* button_menu) {\n    furi_assert(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) {\n                size_t position_candidate;\n                if(model->position < BUTTONS_PER_SCREEN) {\n                    position_candidate = (ButtonMenuItemArray_size(model->items) - 1);\n                } else {\n                    position_candidate = model->position - BUTTONS_PER_SCREEN;\n                };\n                position_candidate -= position_candidate % BUTTONS_PER_SCREEN;\n                model->position = position_candidate;\n                model->scroll_counter = 0;\n            }\n        },\n        true);\n}\n\nstatic void button_menu_process_ok(ButtonMenu* button_menu, InputType type) {\n    furi_assert(button_menu);\n\n    ButtonMenuItem* item = NULL;\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            if(model->position < (ButtonMenuItemArray_size(model->items))) {\n                item = ButtonMenuItemArray_get(model->items, model->position);\n            }\n        },\n        false);\n\n    if(item) {\n        if(item->type == ButtonMenuItemTypeControl) {\n            if(type == InputTypeShort) {\n                if(item->callback) {\n                    item->callback(item->callback_context, item->index, type);\n                }\n            }\n        }\n        if(item->type == ButtonMenuItemTypeCommon) {\n            if((type == InputTypePress) || (type == InputTypeRelease)) {\n                if(item->callback) {\n                    item->callback(item->callback_context, item->index, type);\n                }\n            }\n        }\n    }\n}\n\nstatic bool button_menu_view_input_callback(InputEvent* event, void* context) {\n    furi_assert(event);\n\n    ButtonMenu* button_menu = context;\n    bool consumed = false;\n\n    if(event->key == InputKeyOk) {\n        if((event->type == InputTypeRelease) || (event->type == InputTypePress)) {\n            consumed = true;\n            button_menu->freeze_input = (event->type == InputTypePress);\n            button_menu_process_ok(button_menu, event->type);\n        } else if(event->type == InputTypeShort) {\n            consumed = true;\n            button_menu_process_ok(button_menu, event->type);\n        }\n    }\n\n    if(!button_menu->freeze_input &&\n       ((event->type == InputTypeRepeat) || (event->type == InputTypeShort))) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            button_menu_process_up(button_menu);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            button_menu_process_down(button_menu);\n            break;\n        case InputKeyRight:\n            consumed = true;\n            button_menu_process_right(button_menu);\n            break;\n        case InputKeyLeft:\n            consumed = true;\n            button_menu_process_left(button_menu);\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nView* button_menu_get_view(ButtonMenu* button_menu) {\n    furi_check(button_menu);\n    return button_menu->view;\n}\n\nvoid button_menu_reset(ButtonMenu* button_menu) {\n    furi_check(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            ButtonMenuItemArray_reset(model->items);\n            model->position = 0;\n            model->header = NULL;\n            model->scroll_counter = 0;\n        },\n        true);\n}\n\nvoid button_menu_set_header(ButtonMenu* button_menu, const char* header) {\n    furi_check(button_menu);\n\n    with_view_model(button_menu->view, ButtonMenuModel * model, { model->header = header; }, true);\n}\n\nButtonMenuItem* button_menu_add_item(\n    ButtonMenu* button_menu,\n    const char* label,\n    int32_t index,\n    ButtonMenuItemCallback callback,\n    ButtonMenuItemType type,\n    void* callback_context) {\n    ButtonMenuItem* item = NULL;\n    furi_check(label);\n    furi_check(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            item = ButtonMenuItemArray_push_new(model->items);\n            item->label = label;\n            item->index = index;\n            item->type = type;\n            item->callback = callback;\n            item->callback_context = callback_context;\n        },\n        true);\n\n    return item;\n}\n\nstatic void button_menu_process_timer_callback(void* context) {\n    ButtonMenu* button_menu = context;\n    with_view_model(\n        button_menu->view, ButtonMenuModel * model, { model->scroll_counter++; }, true);\n}\n\nButtonMenu* button_menu_alloc(void) {\n    ButtonMenu* button_menu = malloc(sizeof(ButtonMenu));\n    button_menu->view = view_alloc();\n    view_set_orientation(button_menu->view, ViewOrientationVertical);\n    view_set_context(button_menu->view, button_menu);\n    view_allocate_model(button_menu->view, ViewModelTypeLocking, sizeof(ButtonMenuModel));\n    view_set_draw_callback(button_menu->view, button_menu_view_draw_callback);\n    view_set_input_callback(button_menu->view, button_menu_view_input_callback);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            ButtonMenuItemArray_init(model->items);\n            model->position = 0;\n            model->header = NULL;\n            model->scroll_counter = 0;\n            model->scroll_timer = furi_timer_alloc(\n                button_menu_process_timer_callback, FuriTimerTypePeriodic, button_menu);\n            furi_timer_start(model->scroll_timer, SCROLL_INTERVAL);\n        },\n        true);\n\n    button_menu->freeze_input = false;\n    return button_menu;\n}\n\nvoid button_menu_free(ButtonMenu* button_menu) {\n    furi_check(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            ButtonMenuItemArray_clear(model->items);\n            furi_timer_stop(model->scroll_timer);\n            furi_timer_free(model->scroll_timer);\n        },\n        true);\n    view_free(button_menu->view);\n    free(button_menu);\n}\n\nvoid button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) {\n    furi_check(button_menu);\n\n    with_view_model(\n        button_menu->view,\n        ButtonMenuModel * model,\n        {\n            size_t item_position = 0;\n            ButtonMenuItemArray_it_t it;\n            for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it);\n                ButtonMenuItemArray_next(it), ++item_position) {\n                if((uint32_t)ButtonMenuItemArray_cref(it)->index == index) {\n                    model->position = item_position;\n                    break;\n                }\n            }\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/button_menu.h",
    "content": "/**\n * @file button_menu.h\n * GUI: ButtonMenu view module API\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** ButtonMenu anonymous structure */\ntypedef struct ButtonMenu ButtonMenu;\n\n/** ButtonMenuItem anonymous structure */\ntypedef struct ButtonMenuItem ButtonMenuItem;\n\n/** Callback for any button menu actions */\ntypedef void (*ButtonMenuItemCallback)(void* context, int32_t index, InputType type);\n\n/** Type of button. Difference in drawing buttons. */\ntypedef enum {\n    ButtonMenuItemTypeCommon,\n    ButtonMenuItemTypeControl,\n} ButtonMenuItemType;\n\n/** Get button menu view\n *\n * @param      button_menu  ButtonMenu instance\n *\n * @return     View instance that can be used for embedding\n */\nView* button_menu_get_view(ButtonMenu* button_menu);\n\n/** Clean button menu\n *\n * @param      button_menu  ButtonMenu instance\n */\nvoid button_menu_reset(ButtonMenu* button_menu);\n\n/** Add item to button menu instance\n *\n * @param      button_menu       ButtonMenu instance\n * @param      label             text inside new button\n * @param      index             value to distinct between buttons inside\n *                               ButtonMenuItemCallback\n * @param      callback          The callback\n * @param      type              type of button to create. Differ by button\n *                               drawing. Control buttons have no frames, and\n *                               have more squared borders.\n * @param      callback_context  The callback context\n *\n * @return     pointer to just-created item\n */\nButtonMenuItem* button_menu_add_item(\n    ButtonMenu* button_menu,\n    const char* label,\n    int32_t index,\n    ButtonMenuItemCallback callback,\n    ButtonMenuItemType type,\n    void* callback_context);\n\n/** Allocate and initialize new instance of ButtonMenu model\n *\n * @return     just-created ButtonMenu model\n */\nButtonMenu* button_menu_alloc(void);\n\n/** Free ButtonMenu element\n *\n * @param      button_menu  ButtonMenu instance\n */\nvoid button_menu_free(ButtonMenu* button_menu);\n\n/** Set ButtonMenu header on top of canvas\n *\n * @param      button_menu  ButtonMenu instance\n * @param      header       header on the top of button menu\n */\nvoid button_menu_set_header(ButtonMenu* button_menu, const char* header);\n\n/** Set selected item\n *\n * @param      button_menu  ButtonMenu instance\n * @param      index        index of ButtonMenu to be selected\n */\nvoid button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/button_panel.c",
    "content": "#include \"button_panel.h\"\n\n#include <gui/canvas.h>\n#include <gui/elements.h>\n#include <input/input.h>\n\n#include <furi.h>\n#include <furi_hal_resources.h>\n#include <stdint.h>\n\n#include <m-array.h>\n#include <m-i-list.h>\n#include <m-list.h>\n\ntypedef struct {\n    // uint16_t to support multi-screen, wide button panel\n    uint16_t x;\n    uint16_t y;\n    Font font;\n    const char* str;\n} LabelElement;\n\nLIST_DEF(LabelList, LabelElement, M_POD_OPLIST)\n#define M_OPL_LabelList_t() LIST_OPLIST(LabelList)\n\ntypedef struct {\n    uint16_t x;\n    uint16_t y;\n    const Icon* name;\n    const Icon* name_selected;\n} IconElement;\n\nLIST_DEF(IconList, IconElement, M_POD_OPLIST)\n#define M_OPL_IconList_t() LIST_OPLIST(IconList)\n\ntypedef struct ButtonItem {\n    uint32_t index;\n    ButtonItemCallback callback;\n    IconElement icon;\n    void* callback_context;\n} ButtonItem;\n\nARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST); // NOLINT\n#define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST)\nARRAY_DEF(ButtonMatrix, ButtonArray_t);\n#define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t())\n\nstruct ButtonPanel {\n    View* view;\n    bool freeze_input;\n};\n\ntypedef struct {\n    ButtonMatrix_t button_matrix;\n    IconList_t icons;\n    LabelList_t labels;\n    uint16_t reserve_x;\n    uint16_t reserve_y;\n    uint16_t selected_item_x;\n    uint16_t selected_item_y;\n} ButtonPanelModel;\n\nstatic ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y);\nstatic void button_panel_process_up(ButtonPanel* button_panel);\nstatic void button_panel_process_down(ButtonPanel* button_panel);\nstatic void button_panel_process_left(ButtonPanel* button_panel);\nstatic void button_panel_process_right(ButtonPanel* button_panel);\nstatic void button_panel_process_ok(ButtonPanel* button_panel, InputType input);\nstatic void button_panel_view_draw_callback(Canvas* canvas, void* _model);\nstatic bool button_panel_view_input_callback(InputEvent* event, void* context);\n\nButtonPanel* button_panel_alloc(void) {\n    ButtonPanel* button_panel = malloc(sizeof(ButtonPanel));\n    button_panel->view = view_alloc();\n    view_set_orientation(button_panel->view, ViewOrientationVertical);\n    view_set_context(button_panel->view, button_panel);\n    view_allocate_model(button_panel->view, ViewModelTypeLocking, sizeof(ButtonPanelModel));\n    view_set_draw_callback(button_panel->view, button_panel_view_draw_callback);\n    view_set_input_callback(button_panel->view, button_panel_view_input_callback);\n\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            model->reserve_x = 0;\n            model->reserve_y = 0;\n            model->selected_item_x = 0;\n            model->selected_item_y = 0;\n            ButtonMatrix_init(model->button_matrix);\n            LabelList_init(model->labels);\n        },\n        true);\n\n    return button_panel;\n}\n\nvoid button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) {\n    furi_check(reserve_x > 0);\n    furi_check(reserve_y > 0);\n\n    with_view_model( //-V621\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            model->reserve_x = reserve_x;\n            model->reserve_y = reserve_y;\n            ButtonMatrix_reserve(model->button_matrix, model->reserve_y);\n            for(size_t i = 0; i > model->reserve_y; ++i) {\n                ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i);\n                ButtonArray_init(*array);\n                ButtonArray_reserve(*array, reserve_x);\n            }\n            LabelList_init(model->labels);\n        },\n        true);\n}\n\nvoid button_panel_free(ButtonPanel* button_panel) {\n    furi_check(button_panel);\n\n    button_panel_reset(button_panel);\n\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            LabelList_clear(model->labels);\n            ButtonMatrix_clear(model->button_matrix);\n        },\n        true);\n\n    view_free(button_panel->view);\n    free(button_panel);\n}\n\nvoid button_panel_reset(ButtonPanel* button_panel) {\n    furi_check(button_panel);\n\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            for(size_t x = 0; x < model->reserve_x; ++x) {\n                for(size_t y = 0; y < model->reserve_y; ++y) {\n                    ButtonItem** button_item = button_panel_get_item(model, x, y);\n                    free(*button_item);\n                    *button_item = NULL;\n                }\n            }\n            model->reserve_x = 0;\n            model->reserve_y = 0;\n            model->selected_item_x = 0;\n            model->selected_item_y = 0;\n            LabelList_reset(model->labels);\n            IconList_reset(model->icons);\n            ButtonMatrix_reset(model->button_matrix);\n        },\n        true);\n}\n\nstatic ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) {\n    furi_assert(model);\n\n    furi_check(x < model->reserve_x);\n    furi_check(y < model->reserve_y);\n    ButtonArray_t* button_array = ButtonMatrix_safe_get(model->button_matrix, x);\n    ButtonItem** button_item = ButtonArray_safe_get(*button_array, y);\n    return button_item;\n}\n\nvoid button_panel_add_item(\n    ButtonPanel* button_panel,\n    uint32_t index,\n    uint16_t matrix_place_x,\n    uint16_t matrix_place_y,\n    uint16_t x,\n    uint16_t y,\n    const Icon* icon_name,\n    const Icon* icon_name_selected,\n    ButtonItemCallback callback,\n    void* callback_context) {\n    furi_check(button_panel);\n\n    with_view_model( //-V773\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            ButtonItem** button_item_ptr =\n                button_panel_get_item(model, matrix_place_x, matrix_place_y);\n            furi_check(*button_item_ptr == NULL);\n            *button_item_ptr = malloc(sizeof(ButtonItem));\n            ButtonItem* button_item = *button_item_ptr;\n            button_item->callback = callback;\n            button_item->callback_context = callback_context;\n            button_item->icon.x = x;\n            button_item->icon.y = y;\n            button_item->icon.name = icon_name;\n            button_item->icon.name_selected = icon_name_selected;\n            button_item->index = index;\n        },\n        true);\n}\n\nView* button_panel_get_view(ButtonPanel* button_panel) {\n    furi_check(button_panel);\n    return button_panel->view;\n}\n\nstatic void button_panel_view_draw_callback(Canvas* canvas, void* _model) {\n    furi_assert(canvas);\n    furi_assert(_model);\n\n    ButtonPanelModel* model = _model;\n\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n\n    for\n        M_EACH(icon, model->icons, IconList_t) {\n            canvas_draw_icon(canvas, icon->x, icon->y, icon->name);\n        }\n\n    for(size_t x = 0; x < model->reserve_x; ++x) {\n        for(size_t y = 0; y < model->reserve_y; ++y) {\n            ButtonItem* button_item = *button_panel_get_item(model, x, y);\n            if(!button_item) {\n                continue;\n            }\n            const Icon* icon_name = button_item->icon.name;\n            if((model->selected_item_x == x) && (model->selected_item_y == y)) {\n                icon_name = button_item->icon.name_selected;\n            }\n            canvas_draw_icon(canvas, button_item->icon.x, button_item->icon.y, icon_name);\n        }\n    }\n\n    for\n        M_EACH(label, model->labels, LabelList_t) {\n            canvas_set_font(canvas, label->font);\n            canvas_draw_str(canvas, label->x, label->y, label->str);\n        }\n}\n\nstatic void button_panel_process_down(ButtonPanel* button_panel) {\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            uint16_t new_selected_item_x = model->selected_item_x;\n            uint16_t new_selected_item_y = model->selected_item_y;\n            size_t i;\n\n            if(new_selected_item_y < (model->reserve_y - 1)) {\n                ++new_selected_item_y;\n\n                for(i = 0; i < model->reserve_x; ++i) {\n                    new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;\n                    if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {\n                        break;\n                    }\n                }\n                if(i != model->reserve_x) {\n                    model->selected_item_x = new_selected_item_x;\n                    model->selected_item_y = new_selected_item_y;\n                }\n            }\n        },\n        true);\n}\n\nstatic void button_panel_process_up(ButtonPanel* button_panel) {\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            size_t new_selected_item_x = model->selected_item_x;\n            size_t new_selected_item_y = model->selected_item_y;\n            size_t i;\n\n            if(new_selected_item_y > 0) {\n                --new_selected_item_y;\n\n                for(i = 0; i < model->reserve_x; ++i) {\n                    new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;\n                    if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {\n                        break;\n                    }\n                }\n                if(i != model->reserve_x) {\n                    model->selected_item_x = new_selected_item_x;\n                    model->selected_item_y = new_selected_item_y;\n                }\n            }\n        },\n        true);\n}\n\nstatic void button_panel_process_left(ButtonPanel* button_panel) {\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            size_t new_selected_item_x = model->selected_item_x;\n            size_t new_selected_item_y = model->selected_item_y;\n            size_t i;\n\n            if(new_selected_item_x > 0) {\n                --new_selected_item_x;\n\n                for(i = 0; i < model->reserve_y; ++i) {\n                    new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;\n                    if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {\n                        break;\n                    }\n                }\n                if(i != model->reserve_y) {\n                    model->selected_item_x = new_selected_item_x;\n                    model->selected_item_y = new_selected_item_y;\n                }\n            }\n        },\n        true);\n}\n\nstatic void button_panel_process_right(ButtonPanel* button_panel) {\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            uint16_t new_selected_item_x = model->selected_item_x;\n            uint16_t new_selected_item_y = model->selected_item_y;\n            size_t i;\n\n            if(new_selected_item_x < (model->reserve_x - 1)) {\n                ++new_selected_item_x;\n\n                for(i = 0; i < model->reserve_y; ++i) {\n                    new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;\n                    if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {\n                        break;\n                    }\n                }\n                if(i != model->reserve_y) {\n                    model->selected_item_x = new_selected_item_x;\n                    model->selected_item_y = new_selected_item_y;\n                }\n            }\n        },\n        true);\n}\n\nvoid button_panel_process_ok(ButtonPanel* button_panel, InputType type) {\n    ButtonItem* button_item = NULL;\n\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            button_item =\n                *button_panel_get_item(model, model->selected_item_x, model->selected_item_y);\n        },\n        true);\n\n    if(button_item && button_item->callback) {\n        button_item->callback(button_item->callback_context, button_item->index, type);\n    }\n}\n\nstatic bool button_panel_view_input_callback(InputEvent* event, void* context) {\n    ButtonPanel* button_panel = context;\n    furi_assert(button_panel);\n    bool consumed = false;\n    if(event->key == InputKeyOk) {\n        if((event->type == InputTypePress) || (event->type == InputTypeRelease)) {\n            button_panel->freeze_input = (event->type == InputTypePress);\n        }\n        consumed = true;\n        button_panel_process_ok(button_panel, event->type);\n    }\n    if(!button_panel->freeze_input &&\n       (!(event->type == InputTypePress) && !(event->type == InputTypeRelease))) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            button_panel_process_up(button_panel);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            button_panel_process_down(button_panel);\n            break;\n        case InputKeyLeft:\n            consumed = true;\n            button_panel_process_left(button_panel);\n            break;\n        case InputKeyRight:\n            consumed = true;\n            button_panel_process_right(button_panel);\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid button_panel_add_label(\n    ButtonPanel* button_panel,\n    uint16_t x,\n    uint16_t y,\n    Font font,\n    const char* label_str) {\n    furi_check(button_panel);\n\n    with_view_model(\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            LabelElement* label = LabelList_push_raw(model->labels);\n            label->x = x;\n            label->y = y;\n            label->font = font;\n            label->str = label_str;\n        },\n        true);\n}\n\n// Draw an icon but don't make it a button.\nvoid button_panel_add_icon(\n    ButtonPanel* button_panel,\n    uint16_t x,\n    uint16_t y,\n    const Icon* icon_name) {\n    furi_check(button_panel);\n\n    with_view_model( //-V773\n        button_panel->view,\n        ButtonPanelModel * model,\n        {\n            IconElement* icon = IconList_push_raw(model->icons);\n            icon->x = x;\n            icon->y = y;\n            icon->name = icon_name;\n            icon->name_selected = icon_name;\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/button_panel.h",
    "content": "/**\n * @file button_panel.h\n * GUI: ButtonPanel view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Button panel module descriptor */\ntypedef struct ButtonPanel ButtonPanel;\n\n/** Callback type to call for handling selecting button_panel items */\ntypedef void (*ButtonItemCallback)(void* context, uint32_t index, InputType type);\n\n/** Allocate new button_panel module.\n *\n * @return     ButtonPanel instance\n */\nButtonPanel* button_panel_alloc(void);\n\n/** Free button_panel module.\n *\n * @param      button_panel  ButtonPanel instance\n */\nvoid button_panel_free(ButtonPanel* button_panel);\n\n/** Free items from button_panel module. Preallocated matrix stays unchanged.\n *\n * @param      button_panel  ButtonPanel instance\n */\nvoid button_panel_reset(ButtonPanel* button_panel);\n\n/** Reserve space for adding items.\n *\n * One does not simply use button_panel_add_item() without this function. It\n * should be allocated space for it first.\n *\n * @param      button_panel  ButtonPanel instance\n * @param      reserve_x     number of columns in button_panel\n * @param      reserve_y     number of rows in button_panel\n */\nvoid button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y);\n\n/** Add item to button_panel module.\n *\n * Have to set element in bounds of allocated size by X and by Y.\n *\n * @param      button_panel        ButtonPanel instance\n * @param      index               value to pass to callback\n * @param      matrix_place_x      coordinates by x-axis on virtual grid, it\n *                                 is only used for navigation\n * @param      matrix_place_y      coordinates by y-axis on virtual grid, it\n *                                 is only used for naviagation\n * @param      x                   x-coordinate to draw icon on\n * @param      y                   y-coordinate to draw icon on\n * @param      icon_name           name of the icon to draw\n * @param      icon_name_selected  name of the icon to draw when current\n *                                 element is selected\n * @param      callback            function to call when specific element is\n *                                 selected (pressed Ok on selected item)\n * @param      callback_context    context to pass to callback\n */\nvoid button_panel_add_item(\n    ButtonPanel* button_panel,\n    uint32_t index,\n    uint16_t matrix_place_x,\n    uint16_t matrix_place_y,\n    uint16_t x,\n    uint16_t y,\n    const Icon* icon_name,\n    const Icon* icon_name_selected,\n    ButtonItemCallback callback,\n    void* callback_context);\n\n/** Get button_panel view.\n *\n * @param      button_panel  ButtonPanel instance\n *\n * @return     acquired view\n */\nView* button_panel_get_view(ButtonPanel* button_panel);\n\n/** Add label to button_panel module.\n *\n * @param      button_panel  ButtonPanel instance\n * @param      x             x-coordinate to place label\n * @param      y             y-coordinate to place label\n * @param      font          font to write label with\n * @param      label_str     string label to write\n */\nvoid button_panel_add_label(\n    ButtonPanel* button_panel,\n    uint16_t x,\n    uint16_t y,\n    Font font,\n    const char* label_str);\n\n/** Add a non-button icon to button_panel module.\n *\n * @param      button_panel  ButtonPanel instance\n * @param      x             x-coordinate to place icon\n * @param      y             y-coordinate to place icon\n * @param      icon_name     name of the icon to draw\n */\nvoid button_panel_add_icon(\n    ButtonPanel* button_panel,\n    uint16_t x,\n    uint16_t y,\n    const Icon* icon_name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/byte_input.c",
    "content": "#include \"byte_input.h\"\n\n#include <gui/elements.h>\n#include <furi.h>\n#include <assets_icons.h>\n\n/** ByteInput type */\nstruct ByteInput {\n    View* view;\n};\n\ntypedef struct {\n    const uint8_t value;\n    const uint8_t x;\n    const uint8_t y;\n} ByteInputKey;\n\ntypedef struct {\n    const char* header;\n    uint8_t* bytes;\n    uint8_t bytes_count;\n\n    ByteInputCallback input_callback;\n    ByteChangedCallback changed_callback;\n    void* callback_context;\n\n    bool selected_high_nibble;\n    uint8_t selected_byte;\n    int8_t selected_row; // row -2 - mini_editor, -1 - input, row 0 & 1 - keyboard\n    uint8_t selected_column;\n    uint8_t first_visible_byte;\n} ByteInputModel;\n\nstatic const uint8_t keyboard_origin_x = 7;\nstatic const uint8_t keyboard_origin_y = 31;\nstatic const int8_t keyboard_row_count = 2;\nstatic const uint8_t enter_symbol = '\\r';\nstatic const uint8_t backspace_symbol = '\\b';\nstatic const uint8_t max_drawable_bytes = 8;\n\nstatic const ByteInputKey keyboard_keys_row_1[] = {\n    {'0', 0, 12},\n    {'1', 11, 12},\n    {'2', 22, 12},\n    {'3', 33, 12},\n    {'4', 44, 12},\n    {'5', 55, 12},\n    {'6', 66, 12},\n    {'7', 77, 12},\n    {backspace_symbol, 103, 4},\n};\n\nstatic const ByteInputKey keyboard_keys_row_2[] = {\n    {'8', 0, 26},\n    {'9', 11, 26},\n    {'A', 22, 26},\n    {'B', 33, 26},\n    {'C', 44, 26},\n    {'D', 55, 26},\n    {'E', 66, 26},\n    {'F', 77, 26},\n    {enter_symbol, 95, 17},\n};\n\n/** Get row size\n *\n * @param      row_index  Index of row\n *\n * @return     uint8_t Row size\n */\nstatic uint8_t byte_input_get_row_size(uint8_t row_index) {\n    uint8_t row_size = 0;\n\n    switch(row_index + 1) {\n    case 1:\n        row_size = COUNT_OF(keyboard_keys_row_1);\n        break;\n    case 2:\n        row_size = COUNT_OF(keyboard_keys_row_2);\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row_size;\n}\n\n/** Get row pointer\n *\n * @param      row_index  Index of row\n *\n * @return     const ByteInputKey* Row pointer\n */\nstatic const ByteInputKey* byte_input_get_row(uint8_t row_index) {\n    const ByteInputKey* row = NULL;\n\n    switch(row_index + 1) {\n    case 1:\n        row = keyboard_keys_row_1;\n        break;\n    case 2:\n        row = keyboard_keys_row_2;\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row;\n}\n\n/** Get text from nibble\n *\n * @param      byte         byte value\n * @param      high_nibble  Get from high nibble, otherwise low nibble\n *\n * @return     char nibble text\n */\nstatic char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) {\n    if(high_nibble) {\n        byte = byte >> 4;\n    }\n    byte = byte & 0x0F;\n\n    switch(byte & 0x0F) {\n    case 0x0:\n    case 0x1:\n    case 0x2:\n    case 0x3:\n    case 0x4:\n    case 0x5:\n    case 0x6:\n    case 0x7:\n    case 0x8:\n    case 0x9:\n        byte = byte + '0';\n        break;\n    case 0xA:\n    case 0xB:\n    case 0xC:\n    case 0xD:\n    case 0xE:\n    case 0xF:\n        byte = byte - 0xA + 'A';\n        break;\n    default:\n        byte = '!';\n        break;\n    }\n\n    return byte;\n}\n\nconst char num_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};\n\n/** Draw input box (common view)\n *\n * @param      canvas  The canvas\n * @param      model   The model\n */\nstatic void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) {\n    const uint8_t text_x = 8;\n    const uint8_t text_y = 25;\n    const uint8_t text_y2 = 40;\n    const bool draw_index_line =\n        (model->selected_row == -2) &&\n        (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) <= 100);\n\n    elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);\n\n    canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5);\n    canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5);\n\n    for(uint8_t i = model->first_visible_byte;\n        i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes);\n        i++) {\n        uint8_t byte_position = i - model->first_visible_byte;\n\n        if(i == model->selected_byte) {\n            canvas_draw_frame(canvas, text_x + byte_position * 14, text_y - 9, 15, 11);\n            if(model->selected_row == -2) {\n                canvas_draw_icon(\n                    canvas, text_x + 6 + byte_position * 14, text_y - 14, &I_arrow_nano_up);\n                canvas_draw_icon(\n                    canvas, text_x + 6 + byte_position * 14, text_y + 5, &I_arrow_nano_down);\n            }\n\n            if(model->selected_high_nibble) {\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], false));\n                canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 8, 7, 9);\n                canvas_invert_color(canvas);\n                canvas_draw_line(\n                    canvas,\n                    text_x + 14 + byte_position * 14,\n                    text_y - 6,\n                    text_x + 14 + byte_position * 14,\n                    text_y - 2);\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], true));\n                canvas_invert_color(canvas);\n            } else {\n                canvas_draw_box(canvas, text_x + 7 + byte_position * 14, text_y - 8, 7, 9);\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], true));\n                canvas_invert_color(canvas);\n                canvas_draw_line(\n                    canvas,\n                    text_x + byte_position * 14,\n                    text_y - 6,\n                    text_x + byte_position * 14,\n                    text_y - 2);\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], false));\n                canvas_invert_color(canvas);\n            }\n        } else {\n            if(model->first_visible_byte > 0 && i == model->first_visible_byte) {\n                canvas_draw_icon(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y - 7,\n                    &I_More_data_placeholder_5x7);\n            } else {\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], true));\n            }\n            if(model->bytes_count - model->first_visible_byte > max_drawable_bytes &&\n               i == model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes) - 1) {\n                canvas_draw_icon(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y - 7,\n                    &I_More_data_placeholder_5x7);\n            } else {\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], false));\n            }\n        }\n\n        if(draw_index_line) {\n            canvas_draw_icon(canvas, 1, text_y + 8, &I_Hashmark_7x7);\n            canvas_draw_glyph(\n                canvas, text_x + 2 + byte_position * 14, text_y2, num_to_char[(i + 1) / 10]);\n\n            canvas_draw_glyph(\n                canvas, text_x + 8 + byte_position * 14, text_y2, num_to_char[(i + 1) % 10]);\n        }\n    }\n\n    if((model->selected_row == -2) &&\n       (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) > 100)) {\n        char str[20];\n\n        canvas_set_font(canvas, FontSecondary);\n        snprintf(str, 20, \"Selected index\");\n        canvas_draw_str(canvas, text_x, text_y2, str);\n\n        canvas_set_font(canvas, FontPrimary);\n        snprintf(str, 20, \"%u\", (model->selected_byte + 1));\n        canvas_draw_str(canvas, text_x + 75, text_y2, str);\n    }\n}\n\n/** Draw input box (selected view)\n *\n * @param      canvas  The canvas\n * @param      model   The model\n */\nstatic void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) {\n    const uint8_t text_x = 7;\n    const uint8_t text_y = 25;\n\n    canvas_draw_box(canvas, 0, 12, 127, 19);\n    canvas_invert_color(canvas);\n\n    elements_slightly_rounded_frame(canvas, 6, 14, 115, 15);\n    canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5);\n    canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5);\n\n    for(uint8_t i = model->first_visible_byte;\n        i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes);\n        i++) {\n        uint8_t byte_position = i - model->first_visible_byte;\n\n        if(i == model->selected_byte) {\n            canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 9, 13, 11);\n            canvas_invert_color(canvas);\n            canvas_draw_glyph(\n                canvas,\n                text_x + 2 + byte_position * 14,\n                text_y,\n                byte_input_get_nibble_text(model->bytes[i], true));\n            canvas_draw_glyph(\n                canvas,\n                text_x + 8 + byte_position * 14,\n                text_y,\n                byte_input_get_nibble_text(model->bytes[i], false));\n            canvas_invert_color(canvas);\n        } else {\n            if(model->first_visible_byte > 0 && i == model->first_visible_byte) {\n                canvas_draw_icon(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y - 7,\n                    &I_More_data_placeholder_5x7);\n            } else {\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 2 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], true));\n            }\n            if(model->bytes_count - model->first_visible_byte > max_drawable_bytes &&\n               i == model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes) - 1) {\n                canvas_draw_icon(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y - 7,\n                    &I_More_data_placeholder_5x7);\n            } else {\n                canvas_draw_glyph(\n                    canvas,\n                    text_x + 8 + byte_position * 14,\n                    text_y,\n                    byte_input_get_nibble_text(model->bytes[i], false));\n            }\n        }\n    }\n\n    canvas_invert_color(canvas);\n}\n\n/** Set nibble at position\n *\n * @param      data         where to set nibble\n * @param      position     byte position\n * @param      value        char value\n * @param      high_nibble  set high nibble\n */\nstatic void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) {\n    switch(value) {\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n        value = value - '0';\n        break;\n    case 'A':\n    case 'B':\n    case 'C':\n    case 'D':\n    case 'E':\n    case 'F':\n        value = value - 'A' + 10;\n        break;\n    default:\n        value = 0;\n        break;\n    }\n\n    if(high_nibble) {\n        data[position] &= 0x0F;\n        data[position] |= value << 4;\n    } else {\n        data[position] &= 0xF0;\n        data[position] |= value;\n    }\n}\n\n/** What currently selected\n *\n * @param      model  The model\n *\n * @return     true - keyboard selected, false - input selected\n */\nstatic bool byte_input_keyboard_selected(ByteInputModel* model) {\n    return model->selected_row >= 0;\n}\n\n/** Do transition from keyboard\n *\n * @param      model  The model\n */\nstatic void byte_input_transition_from_keyboard(ByteInputModel* model) {\n    model->selected_row += 1;\n    model->selected_high_nibble = true;\n}\n\n/** Increase selected byte position\n *\n * @param      model  The model\n */\nstatic void byte_input_inc_selected_byte(ByteInputModel* model) {\n    if(model->selected_byte < model->bytes_count - 1) {\n        model->selected_byte += 1;\n\n        if(model->bytes_count > max_drawable_bytes) {\n            if(model->selected_byte - model->first_visible_byte > (max_drawable_bytes - 2)) {\n                if(model->first_visible_byte < model->bytes_count - max_drawable_bytes) {\n                    model->first_visible_byte++;\n                }\n            }\n        }\n    }\n}\n\nstatic void byte_input_inc_selected_byte_mini(ByteInputModel* model) {\n    if((model->selected_byte < model->bytes_count - 1) || model->selected_high_nibble) {\n        if(!model->selected_high_nibble) {\n            model->selected_high_nibble = !model->selected_high_nibble; //-V547\n            byte_input_inc_selected_byte(model);\n        } else {\n            model->selected_high_nibble = !model->selected_high_nibble; //-V547\n        }\n    }\n}\n\n/** Decrease selected byte position\n *\n * @param      model  The model\n */\nstatic void byte_input_dec_selected_byte(ByteInputModel* model) {\n    if(model->selected_byte > 0) {\n        model->selected_byte -= 1;\n\n        furi_assert(model->selected_byte >= model->first_visible_byte);\n        if(model->selected_byte - model->first_visible_byte < 1) {\n            if(model->first_visible_byte > 0) {\n                model->first_visible_byte--;\n            }\n        }\n    }\n}\n\nstatic void byte_input_dec_selected_byte_mini(ByteInputModel* model) {\n    if(model->selected_byte > 0 || !model->selected_high_nibble) {\n        if(model->selected_high_nibble) {\n            model->selected_high_nibble = !model->selected_high_nibble; //-V547\n            byte_input_dec_selected_byte(model);\n        } else {\n            model->selected_high_nibble = !model->selected_high_nibble; //-V547\n        }\n    }\n}\n\n/** Call input callback\n *\n * @param      model  The model\n */\nstatic void byte_input_call_input_callback(ByteInputModel* model) {\n    if(model->input_callback != NULL) {\n        model->input_callback(model->callback_context);\n    }\n}\n\n/** Call changed callback\n *\n * @param      model  The model\n */\nstatic void byte_input_call_changed_callback(ByteInputModel* model) {\n    if(model->changed_callback != NULL) {\n        model->changed_callback(model->callback_context);\n    }\n}\n\n/** Clear selected byte\n *\n * @param      model  The model\n */\n\nstatic void byte_input_clear_selected_byte(ByteInputModel* model) {\n    model->bytes[model->selected_byte] = 0;\n    model->selected_high_nibble = true;\n    byte_input_dec_selected_byte(model);\n    byte_input_call_changed_callback(model);\n}\n\n/** Handle up button\n *\n * @param      model  The model\n */\nstatic void byte_input_handle_up(ByteInputModel* model) {\n    if(model->selected_row > -2) {\n        model->selected_row -= 1;\n    } else if(model->selected_row == -2) {\n        if(!model->selected_high_nibble) {\n            model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) |\n                                                 ((model->bytes[model->selected_byte] + 1) & 0x0F);\n        } else {\n            model->bytes[model->selected_byte] =\n                ((model->bytes[model->selected_byte] + 0x10) & 0xF0) |\n                (model->bytes[model->selected_byte] & 0x0F);\n        }\n        byte_input_call_changed_callback(model);\n    }\n}\n\n/** Handle down button\n *\n * @param      model  The model\n */\nstatic void byte_input_handle_down(ByteInputModel* model) {\n    if(model->selected_row != -2) {\n        if(byte_input_keyboard_selected(model)) {\n            if(model->selected_row < keyboard_row_count - 1) {\n                model->selected_row += 1;\n            }\n        } else {\n            byte_input_transition_from_keyboard(model);\n        }\n    } else {\n        if(!model->selected_high_nibble) {\n            model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) |\n                                                 ((model->bytes[model->selected_byte] - 1) & 0x0F);\n        } else {\n            model->bytes[model->selected_byte] =\n                ((model->bytes[model->selected_byte] - 0x10) & 0xF0) |\n                (model->bytes[model->selected_byte] & 0x0F);\n        }\n        byte_input_call_changed_callback(model);\n    }\n}\n\n/** Handle left button\n *\n * @param      model  The model\n */\nstatic void byte_input_handle_left(ByteInputModel* model) {\n    if(byte_input_keyboard_selected(model)) {\n        if(model->selected_column > 0) {\n            model->selected_column -= 1;\n        } else {\n            model->selected_column = byte_input_get_row_size(model->selected_row) - 1;\n        }\n    } else {\n        if(model->selected_row != -2) {\n            byte_input_dec_selected_byte(model);\n        } else {\n            byte_input_dec_selected_byte_mini(model);\n        }\n    }\n}\n\n/** Handle right button\n *\n * @param      model  The model\n */\nstatic void byte_input_handle_right(ByteInputModel* model) {\n    if(byte_input_keyboard_selected(model)) {\n        if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) {\n            model->selected_column += 1;\n        } else {\n            model->selected_column = 0;\n        }\n    } else {\n        if(model->selected_row != -2) {\n            byte_input_inc_selected_byte(model);\n        } else {\n            byte_input_inc_selected_byte_mini(model);\n        }\n    }\n}\n\n/** Handle OK button\n *\n * @param      model  The model\n */\nstatic void byte_input_handle_ok(ByteInputModel* model) {\n    if(byte_input_keyboard_selected(model)) {\n        uint8_t value = byte_input_get_row(model->selected_row)[model->selected_column].value;\n\n        if(value == enter_symbol) {\n            byte_input_call_input_callback(model);\n        } else if(value == backspace_symbol) {\n            byte_input_clear_selected_byte(model);\n        } else {\n            byte_input_set_nibble(\n                model->bytes, model->selected_byte, value, model->selected_high_nibble);\n            if(model->selected_high_nibble == true) {\n                model->selected_high_nibble = false;\n            } else {\n                byte_input_inc_selected_byte(model);\n                model->selected_high_nibble = true;\n            }\n            byte_input_call_changed_callback(model);\n        }\n    } else if(model->selected_row == -2) {\n        byte_input_call_input_callback(model);\n    } else {\n        byte_input_transition_from_keyboard(model);\n    }\n}\n\n/** Draw callback\n *\n * @param      canvas  The canvas\n * @param      _model  The model\n */\nstatic void byte_input_view_draw_callback(Canvas* canvas, void* _model) {\n    ByteInputModel* model = _model;\n\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontKeyboard);\n\n    if(model->selected_row == -1) {\n        byte_input_draw_input_selected(canvas, model);\n    } else {\n        byte_input_draw_input(canvas, model);\n    }\n\n    if(model->selected_row == -2) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_icon(canvas, 3, 1, &I_Pin_back_arrow_10x8);\n        canvas_draw_str_aligned(canvas, 16, 9, AlignLeft, AlignBottom, \"back to keyboard\");\n        elements_button_center(canvas, \"Save\");\n    } else {\n        // Draw the header\n        canvas_set_font(canvas, FontSecondary);\n        if(model->selected_row == -1) {\n            canvas_draw_str(canvas, 10, 9, \"Move up for alternate input\");\n            canvas_draw_icon(canvas, 3, 4, &I_SmallArrowUp_3x5);\n        } else {\n            canvas_draw_str(canvas, 2, 9, model->header);\n        }\n        canvas_set_font(canvas, FontKeyboard);\n        // Draw keyboard\n        for(int8_t row = 0; row < keyboard_row_count; row++) {\n            const uint8_t column_count = byte_input_get_row_size(row);\n            const ByteInputKey* keys = byte_input_get_row(row);\n\n            for(uint8_t column = 0; column < column_count; column++) {\n                if(keys[column].value == enter_symbol) {\n                    canvas_set_color(canvas, ColorBlack);\n                    if(model->selected_row == row && model->selected_column == column) {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySaveSelected_24x11);\n                    } else {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySave_24x11);\n                    }\n                } else if(keys[column].value == backspace_symbol) {\n                    canvas_set_color(canvas, ColorBlack);\n                    if(model->selected_row == row && model->selected_column == column) {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeyBackspaceSelected_16x9);\n                    } else {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeyBackspace_16x9);\n                    }\n                } else {\n                    if(model->selected_row == row && model->selected_column == column) {\n                        canvas_set_color(canvas, ColorBlack);\n                        canvas_draw_box(\n                            canvas,\n                            keyboard_origin_x + keys[column].x - 3,\n                            keyboard_origin_y + keys[column].y - 10,\n                            11,\n                            13);\n                        canvas_set_color(canvas, ColorWhite);\n                    } else if(\n                        model->selected_row == -1 && row == 0 &&\n                        model->selected_column == column) {\n                        canvas_set_color(canvas, ColorBlack);\n                        canvas_draw_frame(\n                            canvas,\n                            keyboard_origin_x + keys[column].x - 3,\n                            keyboard_origin_y + keys[column].y - 10,\n                            11,\n                            13);\n                    } else {\n                        canvas_set_color(canvas, ColorBlack);\n                    }\n\n                    canvas_draw_glyph(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        keys[column].value);\n                }\n            }\n        }\n    }\n}\n\n/** Input callback\n *\n * @param      event    The event\n * @param      context  The context\n *\n * @return     true\n * @return     false\n */\nstatic bool byte_input_view_input_callback(InputEvent* event, void* context) {\n    ByteInput* byte_input = context;\n    furi_assert(byte_input);\n    bool consumed = false;\n\n    if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n        switch(event->key) {\n        case InputKeyLeft:\n            with_view_model(\n                byte_input->view, ByteInputModel * model, { byte_input_handle_left(model); }, true);\n            consumed = true;\n            break;\n        case InputKeyRight:\n            with_view_model(\n                byte_input->view,\n                ByteInputModel * model,\n                { byte_input_handle_right(model); },\n                true);\n            consumed = true;\n            break;\n        case InputKeyUp:\n            with_view_model(\n                byte_input->view, ByteInputModel * model, { byte_input_handle_up(model); }, true);\n            consumed = true;\n            break;\n        case InputKeyDown:\n            with_view_model(\n                byte_input->view, ByteInputModel * model, { byte_input_handle_down(model); }, true);\n            consumed = true;\n            break;\n        case InputKeyOk:\n            with_view_model(\n                byte_input->view, ByteInputModel * model, { byte_input_handle_ok(model); }, true);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    if(event->type == InputTypeShort && event->key == InputKeyBack) {\n        // Back to keyboard\n        with_view_model(\n            byte_input->view,\n            ByteInputModel * model,\n            {\n                if(model->selected_row == -2) {\n                    model->selected_row += 1;\n                    consumed = true;\n                };\n            },\n            true);\n    }\n\n    if((event->type == InputTypeLong || event->type == InputTypeRepeat) &&\n       event->key == InputKeyBack) {\n        with_view_model(\n            byte_input->view,\n            ByteInputModel * model,\n            { byte_input_clear_selected_byte(model); },\n            true);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\n/** Reset all input-related data in model\n *\n * @param      model  The model\n */\nstatic void byte_input_reset_model_input_data(ByteInputModel* model) {\n    model->bytes = NULL;\n    model->bytes_count = 0;\n    model->selected_high_nibble = true;\n    model->selected_byte = 0;\n    model->selected_row = 0;\n    model->selected_column = 0;\n    model->first_visible_byte = 0;\n}\n\nByteInput* byte_input_alloc(void) {\n    ByteInput* byte_input = malloc(sizeof(ByteInput));\n    byte_input->view = view_alloc();\n    view_set_context(byte_input->view, byte_input);\n    view_allocate_model(byte_input->view, ViewModelTypeLocking, sizeof(ByteInputModel));\n    view_set_draw_callback(byte_input->view, byte_input_view_draw_callback);\n    view_set_input_callback(byte_input->view, byte_input_view_input_callback);\n\n    with_view_model(\n        byte_input->view,\n        ByteInputModel * model,\n        {\n            model->header = \"\";\n            model->input_callback = NULL;\n            model->changed_callback = NULL;\n            model->callback_context = NULL;\n            byte_input_reset_model_input_data(model);\n        },\n        true);\n\n    return byte_input;\n}\n\nvoid byte_input_free(ByteInput* byte_input) {\n    furi_check(byte_input);\n    view_free(byte_input->view);\n    free(byte_input);\n}\n\nView* byte_input_get_view(ByteInput* byte_input) {\n    furi_check(byte_input);\n    return byte_input->view;\n}\n\nvoid byte_input_set_result_callback(\n    ByteInput* byte_input,\n    ByteInputCallback input_callback,\n    ByteChangedCallback changed_callback,\n    void* callback_context,\n    uint8_t* bytes,\n    uint8_t bytes_count) {\n    furi_check(byte_input);\n\n    with_view_model(\n        byte_input->view,\n        ByteInputModel * model,\n        {\n            byte_input_reset_model_input_data(model);\n            model->input_callback = input_callback;\n            model->changed_callback = changed_callback;\n            model->callback_context = callback_context;\n            model->bytes = bytes;\n            model->bytes_count = bytes_count;\n        },\n        true);\n}\n\nvoid byte_input_set_header_text(ByteInput* byte_input, const char* text) {\n    furi_check(byte_input);\n\n    with_view_model(byte_input->view, ByteInputModel * model, { model->header = text; }, true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/byte_input.h",
    "content": "/**\n * @file byte_input.h\n * GUI: ByteInput keyboard view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Byte input anonymous structure  */\ntypedef struct ByteInput ByteInput;\n\n/** callback that is executed on save button press */\ntypedef void (*ByteInputCallback)(void* context);\n\n/** callback that is executed when byte buffer is changed */\ntypedef void (*ByteChangedCallback)(void* context);\n\n/** Allocate and initialize byte input. This byte input is used to enter bytes.\n *\n * @return     ByteInput instance pointer\n */\nByteInput* byte_input_alloc(void);\n\n/** Deinitialize and free byte input\n *\n * @param      byte_input  Byte input instance\n */\nvoid byte_input_free(ByteInput* byte_input);\n\n/** Get byte input view\n *\n * @param      byte_input  byte input instance\n *\n * @return     View instance that can be used for embedding\n */\nView* byte_input_get_view(ByteInput* byte_input);\n\n/** Set byte input result callback\n *\n * @param      byte_input        byte input instance\n * @param      input_callback    input callback fn\n * @param      changed_callback  changed callback fn\n * @param      callback_context  callback context\n * @param      bytes             buffer to use\n * @param      bytes_count       buffer length\n */\nvoid byte_input_set_result_callback(\n    ByteInput* byte_input,\n    ByteInputCallback input_callback,\n    ByteChangedCallback changed_callback,\n    void* callback_context,\n    uint8_t* bytes,\n    uint8_t bytes_count);\n\n/** Set byte input header text\n *\n * @param      byte_input  byte input instance\n * @param      text        text to be shown\n */\nvoid byte_input_set_header_text(ByteInput* byte_input, const char* text);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/dialog_ex.c",
    "content": "#include \"dialog_ex.h\"\n#include <gui/elements.h>\n#include <furi.h>\n\nstruct DialogEx {\n    View* view;\n    void* context;\n    DialogExResultCallback callback;\n    bool enable_extended_events;\n};\n\ntypedef struct {\n    FuriString* text;\n    uint8_t x;\n    uint8_t y;\n    Align horizontal;\n    Align vertical;\n} TextElement;\n\ntypedef struct {\n    int8_t x;\n    int8_t y;\n    const Icon* icon;\n} IconElement;\n\ntypedef struct {\n    TextElement header;\n    TextElement text;\n    IconElement icon;\n\n    FuriString* left_text;\n    FuriString* center_text;\n    FuriString* right_text;\n} DialogExModel;\n\nstatic void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) {\n    DialogExModel* model = _model;\n\n    // Prepare canvas\n    canvas_set_color(canvas, ColorBlack);\n\n    if(model->icon.icon != NULL) {\n        canvas_draw_icon(canvas, model->icon.x, model->icon.y, model->icon.icon);\n    }\n\n    // Draw header\n    canvas_set_font(canvas, FontPrimary);\n    if(furi_string_size(model->header.text)) {\n        elements_multiline_text_aligned(\n            canvas,\n            model->header.x,\n            model->header.y,\n            model->header.horizontal,\n            model->header.vertical,\n            furi_string_get_cstr(model->header.text));\n    }\n\n    // Draw text\n    canvas_set_font(canvas, FontSecondary);\n    if(furi_string_size(model->text.text)) {\n        elements_multiline_text_aligned(\n            canvas,\n            model->text.x,\n            model->text.y,\n            model->text.horizontal,\n            model->text.vertical,\n            furi_string_get_cstr(model->text.text));\n    }\n\n    // Draw buttons\n    if(furi_string_size(model->left_text)) {\n        elements_button_left(canvas, furi_string_get_cstr(model->left_text));\n    }\n\n    if(furi_string_size(model->center_text)) {\n        elements_button_center(canvas, furi_string_get_cstr(model->center_text));\n    }\n\n    if(furi_string_size(model->right_text)) {\n        elements_button_right(canvas, furi_string_get_cstr(model->right_text));\n    }\n}\n\nstatic bool dialog_ex_view_input_callback(InputEvent* event, void* context) {\n    DialogEx* dialog_ex = context;\n    bool consumed = false;\n    bool left_text_present = false;\n    bool center_text_present = false;\n    bool right_text_present = false;\n\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            left_text_present = furi_string_size(model->left_text);\n            center_text_present = furi_string_size(model->center_text);\n            right_text_present = furi_string_size(model->right_text);\n        },\n        false);\n\n    if(dialog_ex->callback) {\n        if(event->type == InputTypeShort) {\n            if(event->key == InputKeyLeft && left_text_present) {\n                dialog_ex->callback(DialogExResultLeft, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyOk && center_text_present) {\n                dialog_ex->callback(DialogExResultCenter, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyRight && right_text_present) {\n                dialog_ex->callback(DialogExResultRight, dialog_ex->context);\n                consumed = true;\n            }\n        }\n\n        if(event->type == InputTypePress && dialog_ex->enable_extended_events) {\n            if(event->key == InputKeyLeft && left_text_present) {\n                dialog_ex->callback(DialogExPressLeft, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyOk && center_text_present) {\n                dialog_ex->callback(DialogExPressCenter, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyRight && right_text_present) {\n                dialog_ex->callback(DialogExPressRight, dialog_ex->context);\n                consumed = true;\n            }\n        }\n\n        if(event->type == InputTypeRelease && dialog_ex->enable_extended_events) {\n            if(event->key == InputKeyLeft && left_text_present) {\n                dialog_ex->callback(DialogExReleaseLeft, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyOk && center_text_present) {\n                dialog_ex->callback(DialogExReleaseCenter, dialog_ex->context);\n                consumed = true;\n            } else if(event->key == InputKeyRight && right_text_present) {\n                dialog_ex->callback(DialogExReleaseRight, dialog_ex->context);\n                consumed = true;\n            }\n        }\n    }\n\n    return consumed;\n}\n\nDialogEx* dialog_ex_alloc(void) {\n    DialogEx* dialog_ex = malloc(sizeof(DialogEx));\n    dialog_ex->view = view_alloc();\n    view_set_context(dialog_ex->view, dialog_ex);\n    view_allocate_model(dialog_ex->view, ViewModelTypeLocking, sizeof(DialogExModel));\n    view_set_draw_callback(dialog_ex->view, dialog_ex_view_draw_callback);\n    view_set_input_callback(dialog_ex->view, dialog_ex_view_input_callback);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            model->header.text = furi_string_alloc();\n            model->header.x = 0;\n            model->header.y = 0;\n            model->header.horizontal = AlignLeft;\n            model->header.vertical = AlignBottom;\n\n            model->text.text = furi_string_alloc();\n            model->text.x = 0;\n            model->text.y = 0;\n            model->text.horizontal = AlignLeft;\n            model->text.vertical = AlignBottom;\n\n            model->icon.x = 0;\n            model->icon.y = 0;\n            model->icon.icon = NULL;\n\n            model->left_text = furi_string_alloc();\n            model->center_text = furi_string_alloc();\n            model->right_text = furi_string_alloc();\n        },\n        false);\n    dialog_ex->enable_extended_events = false;\n    return dialog_ex;\n}\n\nvoid dialog_ex_free(DialogEx* dialog_ex) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            furi_string_free(model->header.text);\n            furi_string_free(model->text.text);\n            furi_string_free(model->left_text);\n            furi_string_free(model->center_text);\n            furi_string_free(model->right_text);\n        },\n        false);\n    view_free(dialog_ex->view);\n    free(dialog_ex);\n}\n\nView* dialog_ex_get_view(DialogEx* dialog_ex) {\n    furi_check(dialog_ex);\n    return dialog_ex->view;\n}\n\nvoid dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback) {\n    furi_check(dialog_ex);\n    dialog_ex->callback = callback;\n}\n\nvoid dialog_ex_set_context(DialogEx* dialog_ex, void* context) {\n    furi_check(dialog_ex);\n    dialog_ex->context = context;\n}\n\nvoid dialog_ex_set_header(\n    DialogEx* dialog_ex,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            furi_string_set(model->header.text, text ? text : \"\");\n            model->header.x = x;\n            model->header.y = y;\n            model->header.horizontal = horizontal;\n            model->header.vertical = vertical;\n        },\n        true);\n}\n\nvoid dialog_ex_set_text(\n    DialogEx* dialog_ex,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            furi_string_set(model->text.text, text ? text : \"\");\n            model->text.x = x;\n            model->text.y = y;\n            model->text.horizontal = horizontal;\n            model->text.vertical = vertical;\n        },\n        true);\n}\n\nvoid dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            model->icon.x = x;\n            model->icon.y = y;\n            model->icon.icon = icon;\n        },\n        true);\n}\n\nvoid dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        { furi_string_set(model->left_text, text ? text : \"\"); },\n        true);\n}\n\nvoid dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        { furi_string_set(model->center_text, text ? text : \"\"); },\n        true);\n}\n\nvoid dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        { furi_string_set(model->right_text, text ? text : \"\"); },\n        true);\n}\n\nvoid dialog_ex_reset(DialogEx* dialog_ex) {\n    furi_check(dialog_ex);\n    with_view_model(\n        dialog_ex->view,\n        DialogExModel * model,\n        {\n            model->icon.icon = NULL;\n            furi_string_reset(model->header.text);\n            furi_string_reset(model->text.text);\n\n            furi_string_reset(model->left_text);\n            furi_string_reset(model->center_text);\n            furi_string_reset(model->right_text);\n        },\n        true);\n    dialog_ex->context = NULL;\n    dialog_ex->callback = NULL;\n}\n\nvoid dialog_ex_enable_extended_events(DialogEx* dialog_ex) {\n    furi_check(dialog_ex);\n    dialog_ex->enable_extended_events = true;\n}\n\nvoid dialog_ex_disable_extended_events(DialogEx* dialog_ex) {\n    furi_check(dialog_ex);\n    dialog_ex->enable_extended_events = false;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/dialog_ex.h",
    "content": "/**\n * @file dialog_ex.h\n * GUI: DialogEx view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Dialog anonymous structure */\ntypedef struct DialogEx DialogEx;\n\n/** DialogEx result */\ntypedef enum {\n    DialogExResultLeft,\n    DialogExResultCenter,\n    DialogExResultRight,\n    DialogExPressLeft,\n    DialogExPressCenter,\n    DialogExPressRight,\n    DialogExReleaseLeft,\n    DialogExReleaseCenter,\n    DialogExReleaseRight,\n} DialogExResult;\n\n/** DialogEx result callback type\n * @warning comes from GUI thread\n */\ntypedef void (*DialogExResultCallback)(DialogExResult result, void* context);\n\n/** Allocate and initialize dialog\n *\n * This dialog used to ask simple questions\n *\n * @return     DialogEx instance\n */\nDialogEx* dialog_ex_alloc(void);\n\n/** Deinitialize and free dialog\n *\n * @param      dialog_ex  DialogEx instance\n */\nvoid dialog_ex_free(DialogEx* dialog_ex);\n\n/** Get dialog view\n *\n * @param      dialog_ex  DialogEx instance\n *\n * @return     View instance that can be used for embedding\n */\nView* dialog_ex_get_view(DialogEx* dialog_ex);\n\n/** Set dialog result callback\n *\n * @param      dialog_ex  DialogEx instance\n * @param      callback   result callback function\n */\nvoid dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback);\n\n/** Set dialog context\n *\n * @param      dialog_ex  DialogEx instance\n * @param      context    context pointer, will be passed to result callback\n */\nvoid dialog_ex_set_context(DialogEx* dialog_ex, void* context);\n\n/** Set dialog header text\n *\n * If text is null, dialog header will not be rendered\n *\n * @param      dialog_ex   DialogEx instance\n * @param      text        text to be shown, can be multiline\n * @param      x           x position\n * @param      y           y position\n * @param      horizontal  horizontal text alignment\n * @param      vertical    vertical text alignment\n */\nvoid dialog_ex_set_header(\n    DialogEx* dialog_ex,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/** Set dialog text\n *\n * If text is null, dialog text will not be rendered\n *\n * @param      dialog_ex   DialogEx instance\n * @param      text        text to be shown, can be multiline\n * @param      x           x position\n * @param      y           y position\n * @param      horizontal  horizontal text alignment\n * @param      vertical    vertical text alignment\n */\nvoid dialog_ex_set_text(\n    DialogEx* dialog_ex,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/** Set dialog icon\n *\n * If x or y is negative, dialog icon will not be rendered\n *\n * @param      dialog_ex  DialogEx instance\n * @param      x          x position\n * @param      y          y position\n * @param      icon       The icon\n */\nvoid dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon);\n\n/** Set left button text\n *\n * If text is null, left button will not be rendered and processed\n *\n * @param      dialog_ex  DialogEx instance\n * @param      text       text to be shown\n */\nvoid dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text);\n\n/** Set center button text\n *\n * If text is null, center button will not be rendered and processed\n *\n * @param      dialog_ex  DialogEx instance\n * @param      text       text to be shown\n */\nvoid dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text);\n\n/** Set right button text\n *\n * If text is null, right button will not be rendered and processed\n *\n * @param      dialog_ex  DialogEx instance\n * @param      text       text to be shown\n */\nvoid dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text);\n\n/** Clean dialog\n *\n * @param      dialog_ex  DialogEx instance\n */\nvoid dialog_ex_reset(DialogEx* dialog_ex);\n\n/** Enable press/release events\n *\n * @param      dialog_ex  DialogEx instance\n */\nvoid dialog_ex_enable_extended_events(DialogEx* dialog_ex);\n\n/** Disable press/release events\n *\n * @param      dialog_ex  DialogEx instance\n */\nvoid dialog_ex_disable_extended_events(DialogEx* dialog_ex);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/empty_screen.c",
    "content": "#include \"empty_screen.h\"\n#include <furi.h>\n\nstruct EmptyScreen {\n    View* view;\n};\n\nstatic void empty_screen_view_draw_callback(Canvas* canvas, void* _model) {\n    UNUSED(_model);\n    canvas_clear(canvas);\n}\n\nstatic bool empty_screen_view_input_callback(InputEvent* event, void* context) {\n    UNUSED(event);\n    UNUSED(context);\n    return false;\n}\n\nEmptyScreen* empty_screen_alloc(void) {\n    EmptyScreen* empty_screen = malloc(sizeof(EmptyScreen));\n    empty_screen->view = view_alloc();\n    view_set_context(empty_screen->view, empty_screen);\n    view_set_draw_callback(empty_screen->view, empty_screen_view_draw_callback);\n    view_set_input_callback(empty_screen->view, empty_screen_view_input_callback);\n    return empty_screen;\n}\n\nvoid empty_screen_free(EmptyScreen* empty_screen) {\n    furi_check(empty_screen);\n    view_free(empty_screen->view);\n    free(empty_screen);\n}\n\nView* empty_screen_get_view(EmptyScreen* empty_screen) {\n    furi_check(empty_screen);\n    return empty_screen->view;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/empty_screen.h",
    "content": "/**\n * @file empty_screen.h\n * GUI: EmptyScreen view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Empty screen anonymous structure */\ntypedef struct EmptyScreen EmptyScreen;\n\n/** Allocate and initialize empty screen\n *\n * This empty screen used to ask simple questions like Yes/\n *\n * @return     EmptyScreen instance\n */\nEmptyScreen* empty_screen_alloc(void);\n\n/** Deinitialize and free empty screen\n *\n * @param      empty_screen  Empty screen instance\n */\nvoid empty_screen_free(EmptyScreen* empty_screen);\n\n/** Get empty screen view\n *\n * @param      empty_screen  Empty screen instance\n *\n * @return     View instance that can be used for embedding\n */\nView* empty_screen_get_view(EmptyScreen* empty_screen);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/file_browser.c",
    "content": "#include \"file_browser.h\"\n#include \"file_browser_worker.h\"\n\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <toolbox/path.h>\n\n#include <furi.h>\n#include <furi_hal_resources.h>\n\n#include <core/check.h>\n#include <core/common_defines.h>\n#include <core/log.h>\n#include <m-array.h>\n\n#define LIST_ITEMS   5u\n#define MAX_LEN_PX   110\n#define FRAME_HEIGHT 12\n#define Y_OFFSET     3\n\n#define ITEM_LIST_LEN_MAX 50\n\n#define CUSTOM_ICON_MAX_SIZE 32\n\n#define SCROLL_INTERVAL (333)\n#define SCROLL_DELAY    (2)\n\ntypedef enum {\n    BrowserItemTypeLoading,\n    BrowserItemTypeBack,\n    BrowserItemTypeFolder,\n    BrowserItemTypeFile,\n} BrowserItemType;\n\ntypedef struct {\n    FuriString* path;\n    BrowserItemType type;\n    uint8_t* custom_icon_data;\n    FuriString* display_name;\n} BrowserItem_t;\n\nstatic void BrowserItem_t_init(BrowserItem_t* obj) {\n    obj->type = BrowserItemTypeLoading;\n    obj->path = furi_string_alloc();\n    obj->display_name = furi_string_alloc();\n    obj->custom_icon_data = NULL;\n}\n\nstatic void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) {\n    obj->type = src->type;\n    obj->path = furi_string_alloc_set(src->path);\n    obj->display_name = furi_string_alloc_set(src->display_name);\n    if(src->custom_icon_data) {\n        obj->custom_icon_data = malloc(CUSTOM_ICON_MAX_SIZE);\n        memcpy(obj->custom_icon_data, src->custom_icon_data, CUSTOM_ICON_MAX_SIZE);\n    } else {\n        obj->custom_icon_data = NULL;\n    }\n}\n\nstatic void BrowserItem_t_set(BrowserItem_t* obj, const BrowserItem_t* src) {\n    obj->type = src->type;\n    furi_string_set(obj->path, src->path);\n    furi_string_set(obj->display_name, src->display_name);\n    if(src->custom_icon_data) {\n        memcpy(obj->custom_icon_data, src->custom_icon_data, CUSTOM_ICON_MAX_SIZE);\n    } else {\n        obj->custom_icon_data = NULL;\n    }\n}\n\nstatic void BrowserItem_t_clear(BrowserItem_t* obj) {\n    furi_string_free(obj->path);\n    furi_string_free(obj->display_name);\n    if(obj->custom_icon_data) {\n        free(obj->custom_icon_data);\n    }\n}\n\nARRAY_DEF(\n    items_array,\n    BrowserItem_t,\n    (INIT(API_2(BrowserItem_t_init)),\n     SET(API_6(BrowserItem_t_set)),\n     INIT_SET(API_6(BrowserItem_t_init_set)),\n     CLEAR(API_2(BrowserItem_t_clear))))\n\nstruct FileBrowser {\n    View* view;\n    BrowserWorker* worker;\n    const char* ext_filter;\n    const char* base_path;\n    bool skip_assets;\n    bool hide_dot_files;\n    bool hide_ext;\n\n    FileBrowserCallback callback;\n    void* context;\n\n    FileBrowserLoadItemCallback item_callback;\n    void* item_context;\n\n    FuriString* result_path;\n    FuriTimer* scroll_timer;\n};\n\ntypedef struct {\n    items_array_t items;\n\n    bool is_root;\n    bool folder_loading;\n    bool list_loading;\n    uint32_t item_cnt;\n    int32_t item_idx;\n    int32_t array_offset;\n    int32_t list_offset;\n\n    const Icon* file_icon;\n    bool hide_ext;\n    size_t scroll_counter;\n\n    uint32_t button_held_for_ticks;\n} FileBrowserModel;\n\nstatic const Icon* BrowserItemIcons[] = {\n    [BrowserItemTypeLoading] = &I_loading_10px,\n    [BrowserItemTypeBack] = &I_back_10px,\n    [BrowserItemTypeFolder] = &I_dir_10px,\n    [BrowserItemTypeFile] = &I_unknown_10px,\n};\n\nstatic void file_browser_view_draw_callback(Canvas* canvas, void* _model);\nstatic bool file_browser_view_input_callback(InputEvent* event, void* context);\n\nstatic void\n    browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root);\nstatic void browser_list_load_cb(void* context, uint32_t list_load_offset);\nstatic void\n    browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last);\nstatic void browser_long_load_cb(void* context);\n\nstatic void file_browser_scroll_timer_callback(void* context) {\n    furi_check(context);\n    FileBrowser* browser = context;\n    with_view_model(browser->view, FileBrowserModel * model, { model->scroll_counter++; }, true);\n}\n\nstatic void file_browser_view_enter_callback(void* context) {\n    furi_check(context);\n    FileBrowser* browser = context;\n    with_view_model(browser->view, FileBrowserModel * model, { model->scroll_counter = 0; }, true);\n    furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL);\n}\n\nstatic void file_browser_view_exit_callback(void* context) {\n    furi_check(context);\n    FileBrowser* browser = context;\n    furi_timer_stop(browser->scroll_timer);\n}\n\nFileBrowser* file_browser_alloc(FuriString* result_path) {\n    furi_check(result_path);\n\n    FileBrowser* browser = malloc(sizeof(FileBrowser));\n    browser->view = view_alloc();\n    view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(FileBrowserModel));\n    view_set_context(browser->view, browser);\n    view_set_draw_callback(browser->view, file_browser_view_draw_callback);\n    view_set_input_callback(browser->view, file_browser_view_input_callback);\n    view_set_enter_callback(browser->view, file_browser_view_enter_callback);\n    view_set_exit_callback(browser->view, file_browser_view_exit_callback);\n\n    browser->scroll_timer =\n        furi_timer_alloc(file_browser_scroll_timer_callback, FuriTimerTypePeriodic, browser);\n\n    browser->result_path = result_path;\n\n    with_view_model(\n        browser->view, FileBrowserModel * model, { items_array_init(model->items); }, false);\n\n    return browser;\n}\n\nvoid file_browser_free(FileBrowser* browser) {\n    furi_check(browser);\n\n    furi_timer_free(browser->scroll_timer);\n\n    with_view_model(\n        browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false);\n\n    view_free(browser->view);\n    free(browser);\n}\n\nView* file_browser_get_view(FileBrowser* browser) {\n    furi_check(browser);\n    return browser->view;\n}\n\nvoid file_browser_configure(\n    FileBrowser* browser,\n    const char* extension,\n    const char* base_path,\n    bool skip_assets,\n    bool hide_dot_files,\n    const Icon* file_icon,\n    bool hide_ext) {\n    furi_check(browser);\n\n    browser->ext_filter = extension;\n    browser->skip_assets = skip_assets;\n    browser->hide_ext = hide_ext;\n    browser->base_path = base_path;\n    browser->hide_dot_files = hide_dot_files;\n\n    with_view_model(\n        browser->view,\n        FileBrowserModel * model,\n        {\n            model->file_icon = file_icon;\n            model->hide_ext = hide_ext;\n        },\n        false);\n}\n\nvoid file_browser_start(FileBrowser* browser, FuriString* path) {\n    furi_check(browser);\n    browser->worker = file_browser_worker_alloc(\n        path,\n        browser->base_path,\n        browser->ext_filter,\n        browser->skip_assets,\n        browser->hide_dot_files);\n    file_browser_worker_set_callback_context(browser->worker, browser);\n    file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb);\n    file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb);\n    file_browser_worker_set_item_callback(browser->worker, browser_list_item_cb);\n    file_browser_worker_set_long_load_callback(browser->worker, browser_long_load_cb);\n}\n\nvoid file_browser_stop(FileBrowser* browser) {\n    furi_check(browser);\n    file_browser_worker_free(browser->worker);\n    with_view_model(\n        browser->view,\n        FileBrowserModel * model,\n        {\n            items_array_reset(model->items);\n            model->item_cnt = 0;\n            model->item_idx = 0;\n            model->array_offset = 0;\n            model->list_offset = 0;\n        },\n        false);\n}\n\nvoid file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) {\n    furi_check(browser);\n    browser->context = context;\n    browser->callback = callback;\n}\n\nvoid file_browser_set_item_callback(\n    FileBrowser* browser,\n    FileBrowserLoadItemCallback callback,\n    void* context) {\n    furi_check(browser);\n\n    browser->item_context = context;\n    browser->item_callback = callback;\n}\n\nstatic bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) {\n    size_t array_size = items_array_size(model->items);\n\n    if((idx >= (uint32_t)model->array_offset + array_size) ||\n       (idx < (uint32_t)model->array_offset)) {\n        return false;\n    }\n    return true;\n}\n\nstatic bool browser_is_list_load_required(FileBrowserModel* model) {\n    size_t array_size = items_array_size(model->items);\n    if((array_size > 0) && (!model->is_root) && (model->array_offset == 0)) {\n        array_size--;\n    }\n    uint32_t item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1);\n\n    if((model->list_loading) || (array_size >= item_cnt)) {\n        return false;\n    }\n\n    if((model->array_offset > 0) &&\n       (model->item_idx < (model->array_offset + ITEM_LIST_LEN_MAX / 4))) {\n        return true;\n    }\n\n    if(((model->array_offset + array_size) < item_cnt) &&\n       (model->item_idx > (int32_t)(model->array_offset + array_size - ITEM_LIST_LEN_MAX / 4))) {\n        return true;\n    }\n\n    return false;\n}\n\nstatic void browser_list_rollover(FileBrowserModel* model) {\n    if(!model->list_loading && items_array_size(model->items) < model->item_cnt) {\n        items_array_reset(model->items);\n    }\n}\n\nstatic void browser_update_offset(FileBrowser* browser) {\n    furi_check(browser);\n\n    with_view_model(\n        browser->view,\n        FileBrowserModel * model,\n        {\n            uint16_t bounds = model->item_cnt > (LIST_ITEMS - 1) ? 2 : model->item_cnt;\n\n            if((model->item_cnt > (LIST_ITEMS - 1)) &&\n               (model->item_idx >= ((int32_t)model->item_cnt - 1))) {\n                model->list_offset = model->item_idx - (LIST_ITEMS - 1);\n            } else if(model->list_offset < model->item_idx - bounds) {\n                model->list_offset = CLAMP(\n                    model->item_idx - (int32_t)(LIST_ITEMS - 2),\n                    (int32_t)model->item_cnt - bounds,\n                    0);\n            } else if(model->list_offset > model->item_idx - bounds) {\n                model->list_offset =\n                    CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0);\n            }\n        },\n        false);\n}\n\nstatic void\n    browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {\n    furi_check(context);\n    FileBrowser* browser = (FileBrowser*)context;\n\n    int32_t load_offset = 0;\n\n    with_view_model(\n        browser->view,\n        FileBrowserModel * model,\n        {\n            items_array_reset(model->items);\n            if(is_root) {\n                model->item_cnt = item_cnt;\n                model->item_idx = (file_idx > 0) ? file_idx : 0;\n                load_offset =\n                    CLAMP(model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0);\n            } else {\n                model->item_cnt = item_cnt + 1;\n                model->item_idx = file_idx + 1;\n                load_offset = CLAMP(\n                    model->item_idx - ITEM_LIST_LEN_MAX / 2 - 1, (int32_t)model->item_cnt - 1, 0);\n            }\n            model->array_offset = 0;\n            model->list_offset = 0;\n            model->is_root = is_root;\n            model->list_loading = true;\n            model->folder_loading = false;\n        },\n        true);\n    browser_update_offset(browser);\n\n    file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX);\n}\n\nstatic void browser_list_load_cb(void* context, uint32_t list_load_offset) {\n    furi_check(context);\n    FileBrowser* browser = (FileBrowser*)context;\n\n    BrowserItem_t back_item;\n    BrowserItem_t_init(&back_item);\n    back_item.type = BrowserItemTypeBack;\n\n    with_view_model(\n        browser->view,\n        FileBrowserModel * model,\n        {\n            items_array_reset(model->items);\n            model->array_offset = list_load_offset;\n            if(!model->is_root) {\n                if(list_load_offset == 0) {\n                    items_array_push_back(model->items, back_item);\n                } else {\n                    model->array_offset += 1;\n                }\n            }\n        },\n        false);\n\n    BrowserItem_t_clear(&back_item);\n}\n\nstatic void\n    browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) {\n    furi_check(context);\n    FileBrowser* browser = (FileBrowser*)context;\n\n    BrowserItem_t item;\n    item.custom_icon_data = NULL;\n\n    if(!is_last) {\n        item.path = furi_string_alloc_set(item_path);\n        item.display_name = furi_string_alloc();\n        if(is_folder) {\n            item.type = BrowserItemTypeFolder;\n        } else {\n            item.type = BrowserItemTypeFile;\n            if(browser->item_callback) {\n                item.custom_icon_data = malloc(CUSTOM_ICON_MAX_SIZE);\n                if(!browser->item_callback(\n                       item_path,\n                       browser->item_context,\n                       &item.custom_icon_data,\n                       item.display_name)) {\n                    free(item.custom_icon_data);\n                    item.custom_icon_data = NULL;\n                }\n            }\n        }\n\n        if(furi_string_empty(item.display_name)) {\n            path_extract_filename(\n                item_path,\n                item.display_name,\n                (browser->hide_ext) && (item.type == BrowserItemTypeFile));\n        }\n\n        // We shouldn't update screen on each item if custom callback is not set\n        // Otherwise it will cause screen flickering\n        bool instant_update = (browser->item_callback != NULL);\n        with_view_model(\n            browser->view,\n            FileBrowserModel * model,\n            { items_array_push_back(model->items, item); },\n            instant_update);\n\n        furi_string_free(item.display_name);\n        furi_string_free(item.path);\n        if(item.custom_icon_data) {\n            free(item.custom_icon_data);\n        }\n    } else {\n        with_view_model(\n            browser->view,\n            FileBrowserModel * model,\n            {\n                model->list_loading = false;\n                if(browser_is_list_load_required(model)) {\n                    model->list_loading = true;\n                    int32_t load_offset = CLAMP(\n                        model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0);\n                    file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX);\n                }\n            },\n            true);\n    }\n}\n\nstatic void browser_long_load_cb(void* context) {\n    furi_check(context);\n    FileBrowser* browser = (FileBrowser*)context;\n\n    with_view_model(\n        browser->view, FileBrowserModel * model, { model->folder_loading = true; }, true);\n}\n\nstatic void browser_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_box(\n        canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT, (scrollbar ? 122 : 127), FRAME_HEIGHT);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_dot(canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 1, Y_OFFSET + idx * FRAME_HEIGHT);\n    canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + 1);\n\n    canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1));\n    canvas_draw_dot(canvas, scrollbar ? 121 : 126, Y_OFFSET + idx * FRAME_HEIGHT);\n    canvas_draw_dot(\n        canvas, scrollbar ? 121 : 126, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1));\n}\n\nstatic void browser_draw_loading(Canvas* canvas, FileBrowserModel* model) {\n    UNUSED(model);\n\n    uint8_t x = 128 / 2 - 24 / 2;\n    uint8_t y = 64 / 2 - 24 / 2;\n\n    canvas_draw_icon(canvas, x, y, &A_Loading_24);\n}\n\nstatic void browser_draw_list(Canvas* canvas, FileBrowserModel* model) {\n    uint32_t array_size = items_array_size(model->items);\n    bool show_scrollbar = model->item_cnt > LIST_ITEMS;\n\n    FuriString* filename;\n    filename = furi_string_alloc();\n\n    for(uint32_t i = 0; i < MIN(model->item_cnt, LIST_ITEMS); i++) {\n        int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u);\n\n        BrowserItemType item_type = BrowserItemTypeLoading;\n        uint8_t* custom_icon_data = NULL;\n\n        if(browser_is_item_in_array(model, idx)) {\n            BrowserItem_t* item = items_array_get(\n                model->items, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0));\n            item_type = item->type;\n            furi_string_set(filename, item->display_name);\n            if(item_type == BrowserItemTypeFile) {\n                custom_icon_data = item->custom_icon_data;\n            }\n        } else {\n            furi_string_set(filename, \"---\");\n        }\n\n        if(item_type == BrowserItemTypeBack) {\n            furi_string_set(filename, \". .\");\n        }\n\n        size_t scroll_counter = model->scroll_counter;\n        if(model->item_idx == idx) {\n            browser_draw_frame(canvas, i, show_scrollbar);\n            if(scroll_counter < SCROLL_DELAY) {\n                scroll_counter = 0;\n            } else {\n                scroll_counter -= SCROLL_DELAY;\n            }\n        } else {\n            canvas_set_color(canvas, ColorBlack);\n            scroll_counter = 0;\n        }\n\n        if(custom_icon_data) { //-V547\n            // Currently only 10*10 icons are supported\n            canvas_draw_bitmap(\n                canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, 10, 10, custom_icon_data);\n        } else if((item_type == BrowserItemTypeFile) && (model->file_icon)) {\n            canvas_draw_icon(canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, model->file_icon);\n        } else if(BrowserItemIcons[item_type] != NULL) {\n            canvas_draw_icon(\n                canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]);\n        }\n        elements_scrollable_text_line(\n            canvas,\n            15,\n            Y_OFFSET + 9 + i * FRAME_HEIGHT,\n            (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX),\n            filename,\n            scroll_counter,\n            (model->item_idx != idx));\n    }\n\n    if(show_scrollbar) {\n        elements_scrollbar_pos(\n            canvas,\n            126,\n            Y_OFFSET,\n            canvas_height(canvas) - Y_OFFSET,\n            model->item_idx,\n            model->item_cnt);\n    }\n\n    uint32_t folder_item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1);\n    if(folder_item_cnt == 0) {\n        canvas_set_color(canvas, ColorBlack);\n        canvas_draw_str_aligned(\n            canvas,\n            canvas_width(canvas) / 2,\n            canvas_height(canvas) / 2,\n            AlignCenter,\n            AlignCenter,\n            \"<Empty>\");\n    }\n\n    furi_string_free(filename);\n}\n\nstatic void file_browser_view_draw_callback(Canvas* canvas, void* _model) {\n    FileBrowserModel* model = _model;\n\n    if(model->folder_loading) {\n        browser_draw_loading(canvas, model);\n    } else {\n        browser_draw_list(canvas, model);\n    }\n}\n\nstatic bool file_browser_view_input_callback(InputEvent* event, void* context) {\n    FileBrowser* browser = context;\n    furi_check(browser);\n    bool consumed = false;\n    bool is_loading = false;\n\n    with_view_model(\n        browser->view, FileBrowserModel * model, { is_loading = model->folder_loading; }, false);\n\n    if(is_loading) {\n        return false;\n    } else if(event->key == InputKeyUp || event->key == InputKeyDown) {\n        if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n            with_view_model(\n                browser->view,\n                FileBrowserModel * model,\n                {\n                    int32_t scroll_speed = 1;\n                    if(model->button_held_for_ticks > 5) {\n                        if(model->button_held_for_ticks % 2) {\n                            scroll_speed = 0;\n                        } else {\n                            scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3;\n                        }\n                    }\n\n                    if(event->key == InputKeyUp) {\n                        if(model->item_idx < scroll_speed) {\n                            model->button_held_for_ticks = 0;\n                            model->item_idx = model->item_cnt - 1;\n                            browser_list_rollover(model);\n                        } else {\n                            model->item_idx =\n                                ((model->item_idx - scroll_speed) + model->item_cnt) %\n                                model->item_cnt;\n                        }\n\n                        if(browser_is_list_load_required(model)) {\n                            model->list_loading = true;\n                            int32_t load_offset = CLAMP(\n                                model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3,\n                                (int32_t)model->item_cnt,\n                                0);\n                            file_browser_worker_load(\n                                browser->worker, load_offset, ITEM_LIST_LEN_MAX);\n                        }\n                        model->scroll_counter = 0;\n\n                        model->button_held_for_ticks += 1;\n                    } else if(event->key == InputKeyDown) {\n                        if(model->item_idx + scroll_speed >= (int32_t)model->item_cnt) {\n                            model->button_held_for_ticks = 0;\n                            model->item_idx = 0;\n                            browser_list_rollover(model);\n                        } else {\n                            model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt;\n                        }\n\n                        if(browser_is_list_load_required(model)) {\n                            model->list_loading = true;\n                            int32_t load_offset = CLAMP(\n                                model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1,\n                                (int32_t)model->item_cnt,\n                                0);\n                            file_browser_worker_load(\n                                browser->worker, load_offset, ITEM_LIST_LEN_MAX);\n                        }\n                        model->scroll_counter = 0;\n\n                        model->button_held_for_ticks += 1;\n                    }\n                },\n                true);\n            browser_update_offset(browser);\n            consumed = true;\n        } else if(event->type == InputTypeRelease) {\n            with_view_model(\n                browser->view,\n                FileBrowserModel * model,\n                { model->button_held_for_ticks = 0; },\n                true);\n        }\n    } else if(event->key == InputKeyOk) {\n        if(event->type == InputTypeShort) {\n            BrowserItem_t* selected_item = NULL;\n            int32_t select_index = 0;\n            with_view_model(\n                browser->view,\n                FileBrowserModel * model,\n                {\n                    if(browser_is_item_in_array(model, model->item_idx)) {\n                        selected_item =\n                            items_array_get(model->items, model->item_idx - model->array_offset);\n                        select_index = model->item_idx;\n                        if((!model->is_root) && (select_index > 0)) {\n                            select_index -= 1;\n                        }\n                    }\n                },\n                false);\n\n            if(selected_item) {\n                if(selected_item->type == BrowserItemTypeBack) {\n                    file_browser_worker_folder_exit(browser->worker);\n                } else if(selected_item->type == BrowserItemTypeFolder) {\n                    file_browser_worker_folder_enter(\n                        browser->worker, selected_item->path, select_index);\n                } else if(selected_item->type == BrowserItemTypeFile) {\n                    furi_string_set(browser->result_path, selected_item->path);\n                    if(browser->callback) {\n                        browser->callback(browser->context);\n                    }\n                }\n            }\n            consumed = true;\n        }\n    } else if(event->key == InputKeyLeft) {\n        if(event->type == InputTypeShort) {\n            bool is_root = false;\n            with_view_model(\n                browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false);\n            if(!is_root) {\n                file_browser_worker_folder_exit(browser->worker);\n            }\n            consumed = true;\n        }\n    } else if(event->key == InputKeyBack) {\n        if(event->type == InputTypeShort) {\n            bool is_root = false;\n            with_view_model(\n                browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false);\n\n            if(!is_root && !file_browser_worker_is_in_start_folder(browser->worker)) {\n                consumed = true;\n                file_browser_worker_folder_exit(browser->worker);\n            }\n        }\n    }\n\n    return consumed;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/file_browser.h",
    "content": "/**\n * @file file_browser.h\n * GUI: FileBrowser view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FileBrowser FileBrowser;\ntypedef void (*FileBrowserCallback)(void* context);\n\ntypedef bool (*FileBrowserLoadItemCallback)(\n    FuriString* path,\n    void* context,\n    uint8_t** icon,\n    FuriString* item_name);\n\nFileBrowser* file_browser_alloc(FuriString* result_path);\n\nvoid file_browser_free(FileBrowser* browser);\n\nView* file_browser_get_view(FileBrowser* browser);\n\nvoid file_browser_configure(\n    FileBrowser* browser,\n    const char* extension,\n    const char* base_path,\n    bool skip_assets,\n    bool hide_dot_files,\n    const Icon* file_icon,\n    bool hide_ext);\n\nvoid file_browser_start(FileBrowser* browser, FuriString* path);\n\nvoid file_browser_stop(FileBrowser* browser);\n\nvoid file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context);\n\nvoid file_browser_set_item_callback(\n    FileBrowser* browser,\n    FileBrowserLoadItemCallback callback,\n    void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/file_browser_worker.c",
    "content": "#include \"file_browser_worker.h\"\n\n#include <storage/filesystem_api_defines.h>\n#include <storage/storage.h>\n\n#include <toolbox/path.h>\n#include <core/check.h>\n#include <core/common_defines.h>\n#include <furi.h>\n\n#include <m-array.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n#define TAG \"BrowserWorker\"\n\n#define ASSETS_DIR          \"assets\"\n#define BROWSER_ROOT        STORAGE_EXT_PATH_PREFIX\n#define FILE_NAME_LEN_MAX   256\n#define LONG_LOAD_THRESHOLD 100\n\ntypedef enum {\n    WorkerEvtStop = (1 << 0),\n    WorkerEvtLoad = (1 << 1),\n    WorkerEvtFolderEnter = (1 << 2),\n    WorkerEvtFolderExit = (1 << 3),\n    WorkerEvtFolderRefresh = (1 << 4),\n    WorkerEvtConfigChange = (1 << 5),\n} WorkerEvtFlags;\n\n#define WORKER_FLAGS_ALL                                                          \\\n    (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \\\n     WorkerEvtFolderRefresh | WorkerEvtConfigChange)\n\nARRAY_DEF(IdxLastArray, int32_t) //-V658\nARRAY_DEF(ExtFilterArray, FuriString*, FURI_STRING_OPLIST) //-V658\n\nstruct BrowserWorker {\n    FuriThread* thread;\n\n    FuriString* path_start;\n    FuriString* path_current;\n    FuriString* path_next;\n    int32_t item_sel_idx;\n    uint32_t load_offset;\n    uint32_t load_count;\n    bool skip_assets;\n    bool hide_dot_files;\n    IdxLastArray_t idx_last;\n    ExtFilterArray_t ext_filter;\n\n    void* cb_ctx;\n    BrowserWorkerFolderOpenCallback folder_cb;\n    BrowserWorkerListLoadCallback list_load_cb;\n    BrowserWorkerListItemCallback list_item_cb;\n    BrowserWorkerLongLoadCallback long_load_cb;\n};\n\nstatic bool browser_path_is_file(FuriString* path) {\n    bool state = false;\n    FileInfo file_info;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {\n        if(!file_info_is_dir(&file_info)) {\n            state = true;\n        }\n    }\n    furi_record_close(RECORD_STORAGE);\n    return state;\n}\n\nstatic bool browser_path_trim(FuriString* path) {\n    bool is_root = false;\n    size_t filename_start = furi_string_search_rchar(path, '/');\n    furi_string_left(path, filename_start);\n    if((furi_string_empty(path)) || (filename_start == FURI_STRING_FAILURE)) {\n        furi_string_set(path, BROWSER_ROOT);\n        is_root = true;\n    }\n    return is_root;\n}\nstatic void browser_parse_ext_filter(ExtFilterArray_t ext_filter, const char* filter_str) {\n    ExtFilterArray_reset(ext_filter);\n    if(!filter_str) {\n        return;\n    }\n\n    size_t len = strlen(filter_str);\n    if(len == 0) {\n        return;\n    }\n\n    size_t str_offset = 0;\n    FuriString* ext_temp = furi_string_alloc();\n    while(1) {\n        size_t ext_len = strcspn(&filter_str[str_offset], \"|\");\n\n        furi_string_set_strn(ext_temp, &filter_str[str_offset], ext_len);\n        ExtFilterArray_push_back(ext_filter, ext_temp);\n\n        str_offset += ext_len + 1;\n        if(str_offset >= len) {\n            break;\n        }\n    }\n    furi_string_free(ext_temp);\n}\n\nstatic bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, bool is_folder) {\n    // Skip dot files if enabled\n    if(browser->hide_dot_files) {\n        if(furi_string_start_with_str(name, \".\")) {\n            return false;\n        }\n    }\n\n    if(is_folder) {\n        // Skip assets folders (if enabled)\n        if(browser->skip_assets) {\n            return (furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true);\n        } else {\n            return true;\n        }\n    } else {\n        // Filter files by extension\n        if(ExtFilterArray_size(browser->ext_filter) == 0) {\n            return true;\n        }\n\n        ExtFilterArray_it_t it;\n        for(ExtFilterArray_it(it, browser->ext_filter); !ExtFilterArray_end_p(it);\n            ExtFilterArray_next(it)) {\n            FuriString* ext = *ExtFilterArray_cref(it);\n            if((furi_string_empty(ext)) || (furi_string_cmp_str(ext, \"*\") == 0)) {\n                return true;\n            }\n            if(furi_string_end_withi(name, ext)) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\nstatic bool browser_folder_check_and_switch(FuriString* path) {\n    FileInfo file_info;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool is_root = false;\n\n    if(furi_string_search_rchar(path, '/') == 0) {\n        is_root = true;\n    }\n\n    while(1) {\n        // Check if folder is existing and navigate back if not\n        if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {\n            if(file_info_is_dir(&file_info)) {\n                break;\n            }\n        }\n        if(is_root) {\n            break;\n        }\n        is_root = browser_path_trim(path);\n    }\n    furi_record_close(RECORD_STORAGE);\n    return is_root;\n}\n\nstatic bool browser_folder_init(\n    BrowserWorker* browser,\n    FuriString* path,\n    FuriString* filename,\n    uint32_t* item_cnt,\n    int32_t* file_idx) {\n    bool state = false;\n    FileInfo file_info;\n    uint32_t total_files_cnt = 0;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* directory = storage_file_alloc(storage);\n\n    char name_temp[FILE_NAME_LEN_MAX];\n    FuriString* name_str;\n    name_str = furi_string_alloc();\n\n    *item_cnt = 0;\n    *file_idx = -1;\n\n    if(storage_dir_open(directory, furi_string_get_cstr(path))) {\n        state = true;\n        while(1) {\n            if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) {\n                break;\n            }\n            if((storage_file_get_error(directory) == FSE_OK) && (name_temp[0] != '\\0')) {\n                total_files_cnt++;\n                furi_string_set(name_str, name_temp);\n                if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) {\n                    if(!furi_string_empty(filename)) {\n                        if(furi_string_cmp(name_str, filename) == 0) {\n                            *file_idx = *item_cnt;\n                        }\n                    }\n                    (*item_cnt)++;\n                }\n                if(total_files_cnt == LONG_LOAD_THRESHOLD) {\n                    // There are too many files in folder and counting them will take some time - send callback to app\n                    if(browser->long_load_cb) {\n                        browser->long_load_cb(browser->cb_ctx);\n                    }\n                }\n            }\n        }\n    }\n\n    furi_string_free(name_str);\n\n    storage_dir_close(directory);\n    storage_file_free(directory);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return state;\n}\n\nstatic bool\n    browser_folder_load(BrowserWorker* browser, FuriString* path, uint32_t offset, uint32_t count) {\n    FileInfo file_info;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* directory = storage_file_alloc(storage);\n\n    char name_temp[FILE_NAME_LEN_MAX];\n    FuriString* name_str;\n    name_str = furi_string_alloc();\n\n    uint32_t items_cnt = 0;\n\n    do {\n        if(!storage_dir_open(directory, furi_string_get_cstr(path))) {\n            break;\n        }\n\n        items_cnt = 0;\n        while(items_cnt < offset) {\n            if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) {\n                break;\n            }\n            if(storage_file_get_error(directory) == FSE_OK) {\n                furi_string_set(name_str, name_temp);\n                if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) {\n                    items_cnt++;\n                }\n            } else {\n                break;\n            }\n        }\n        if(items_cnt != offset) {\n            break;\n        }\n\n        if(browser->list_load_cb) {\n            browser->list_load_cb(browser->cb_ctx, offset);\n        }\n\n        items_cnt = 0;\n        while(items_cnt < count) {\n            if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) {\n                break;\n            }\n            if(storage_file_get_error(directory) == FSE_OK) {\n                furi_string_set(name_str, name_temp);\n                if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) {\n                    furi_string_printf(name_str, \"%s/%s\", furi_string_get_cstr(path), name_temp);\n                    if(browser->list_item_cb) {\n                        browser->list_item_cb(\n                            browser->cb_ctx, name_str, file_info_is_dir(&file_info), false);\n                    }\n                    items_cnt++;\n                }\n            } else {\n                break;\n            }\n        }\n        if(browser->list_item_cb) {\n            browser->list_item_cb(browser->cb_ctx, NULL, false, true);\n        }\n    } while(0);\n\n    furi_string_free(name_str);\n\n    storage_dir_close(directory);\n    storage_file_free(directory);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return items_cnt == count;\n}\n\nstatic int32_t browser_worker(void* context) {\n    BrowserWorker* browser = (BrowserWorker*)context;\n    furi_check(browser);\n    FURI_LOG_D(TAG, \"Start\");\n\n    uint32_t items_cnt = 0;\n    FuriString* path;\n    path = furi_string_alloc_set(BROWSER_ROOT);\n    browser->item_sel_idx = -1;\n\n    FuriString* filename;\n    filename = furi_string_alloc();\n\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange);\n\n    while(1) {\n        uint32_t flags =\n            furi_thread_flags_wait(WORKER_FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever);\n        furi_check((flags & FuriFlagError) == 0);\n\n        if(flags & WorkerEvtConfigChange) {\n            // If start path is a path to the file - try finding index of this file in a folder\n            if(browser_path_is_file(browser->path_next)) {\n                path_extract_filename(browser->path_next, filename, false);\n            }\n            IdxLastArray_reset(browser->idx_last);\n\n            furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter);\n        }\n\n        if(flags & WorkerEvtFolderEnter) {\n            furi_string_set(path, browser->path_next);\n            bool is_root = browser_folder_check_and_switch(path);\n\n            // Push previous selected item index to history array\n            IdxLastArray_push_back(browser->idx_last, browser->item_sel_idx);\n\n            int32_t file_idx = 0;\n            browser_folder_init(browser, path, filename, &items_cnt, &file_idx);\n            furi_string_set(browser->path_current, path);\n            FURI_LOG_D(\n                TAG,\n                \"Enter folder: %s items: %lu idx: %ld\",\n                furi_string_get_cstr(path),\n                items_cnt,\n                file_idx);\n            if(browser->folder_cb) {\n                browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root);\n            }\n            furi_string_reset(filename);\n        }\n\n        if(flags & WorkerEvtFolderExit) {\n            browser_path_trim(path);\n            bool is_root = browser_folder_check_and_switch(path);\n\n            int32_t file_idx = 0;\n            browser_folder_init(browser, path, filename, &items_cnt, &file_idx);\n            if(IdxLastArray_size(browser->idx_last) > 0) {\n                // Pop previous selected item index from history array\n                IdxLastArray_pop_back(&file_idx, browser->idx_last);\n            }\n            furi_string_set(browser->path_current, path);\n            FURI_LOG_D(\n                TAG,\n                \"Exit to: %s items: %lu idx: %ld\",\n                furi_string_get_cstr(path),\n                items_cnt,\n                file_idx);\n            if(browser->folder_cb) {\n                browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root);\n            }\n        }\n\n        if(flags & WorkerEvtFolderRefresh) {\n            bool is_root = browser_folder_check_and_switch(path);\n\n            int32_t file_idx = 0;\n            furi_string_reset(filename);\n            browser_folder_init(browser, path, filename, &items_cnt, &file_idx);\n            FURI_LOG_D(\n                TAG,\n                \"Refresh folder: %s items: %lu idx: %ld\",\n                furi_string_get_cstr(path),\n                items_cnt,\n                browser->item_sel_idx);\n            if(browser->folder_cb) {\n                browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root);\n            }\n        }\n\n        if(flags & WorkerEvtLoad) {\n            FURI_LOG_D(\n                TAG, \"Load offset: %lu cnt: %lu\", browser->load_offset, browser->load_count);\n            browser_folder_load(browser, path, browser->load_offset, browser->load_count);\n        }\n\n        if(flags & WorkerEvtStop) {\n            break;\n        }\n    }\n\n    furi_string_free(filename);\n    furi_string_free(path);\n\n    FURI_LOG_D(TAG, \"End\");\n    return 0;\n}\n\nBrowserWorker* file_browser_worker_alloc(\n    FuriString* path,\n    const char* base_path,\n    const char* ext_filter,\n    bool skip_assets,\n    bool hide_dot_files) {\n    BrowserWorker* browser = malloc(sizeof(BrowserWorker));\n\n    IdxLastArray_init(browser->idx_last);\n    ExtFilterArray_init(browser->ext_filter);\n\n    browser_parse_ext_filter(browser->ext_filter, ext_filter);\n    browser->skip_assets = skip_assets;\n    browser->hide_dot_files = hide_dot_files;\n\n    browser->path_current = furi_string_alloc_set(path);\n    browser->path_next = furi_string_alloc_set(path);\n\n    browser->path_start = furi_string_alloc();\n    if(base_path) {\n        furi_string_set_str(browser->path_start, base_path);\n    }\n\n    browser->thread = furi_thread_alloc_ex(\"BrowserWorker\", 2048, browser_worker, browser);\n    furi_thread_start(browser->thread);\n\n    return browser;\n} //-V773\n\nvoid file_browser_worker_free(BrowserWorker* browser) {\n    furi_check(browser);\n\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtStop);\n    furi_thread_join(browser->thread);\n    furi_thread_free(browser->thread);\n\n    furi_string_free(browser->path_next);\n    furi_string_free(browser->path_current);\n    furi_string_free(browser->path_start);\n\n    IdxLastArray_clear(browser->idx_last);\n    ExtFilterArray_clear(browser->ext_filter);\n\n    free(browser);\n}\n\nvoid file_browser_worker_set_callback_context(BrowserWorker* browser, void* context) {\n    furi_check(browser);\n    browser->cb_ctx = context;\n}\n\nvoid file_browser_worker_set_folder_callback(\n    BrowserWorker* browser,\n    BrowserWorkerFolderOpenCallback cb) {\n    furi_check(browser);\n    browser->folder_cb = cb;\n}\n\nvoid file_browser_worker_set_list_callback(\n    BrowserWorker* browser,\n    BrowserWorkerListLoadCallback cb) {\n    furi_check(browser);\n    browser->list_load_cb = cb;\n}\n\nvoid file_browser_worker_set_item_callback(\n    BrowserWorker* browser,\n    BrowserWorkerListItemCallback cb) {\n    furi_check(browser);\n    browser->list_item_cb = cb;\n}\n\nvoid file_browser_worker_set_long_load_callback(\n    BrowserWorker* browser,\n    BrowserWorkerLongLoadCallback cb) {\n    furi_check(browser);\n    browser->long_load_cb = cb;\n}\n\nvoid file_browser_worker_set_config(\n    BrowserWorker* browser,\n    FuriString* path,\n    const char* ext_filter,\n    bool skip_assets,\n    bool hide_dot_files) {\n    furi_check(browser);\n    furi_string_set(browser->path_next, path);\n    browser_parse_ext_filter(browser->ext_filter, ext_filter);\n    browser->skip_assets = skip_assets;\n    browser->hide_dot_files = hide_dot_files;\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange);\n}\n\nvoid file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx) {\n    furi_check(browser);\n    furi_string_set(browser->path_next, path);\n    browser->item_sel_idx = item_idx;\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter);\n}\n\nbool file_browser_worker_is_in_start_folder(BrowserWorker* browser) {\n    furi_check(browser);\n    return furi_string_cmp(browser->path_start, browser->path_current) == 0;\n}\n\nvoid file_browser_worker_folder_exit(BrowserWorker* browser) {\n    furi_check(browser);\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit);\n}\n\nvoid file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) {\n    furi_check(browser);\n    browser->item_sel_idx = item_idx;\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh);\n}\n\nvoid file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) {\n    furi_check(browser);\n    browser->load_offset = offset;\n    browser->load_count = count;\n    furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtLoad);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/file_browser_worker.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct BrowserWorker BrowserWorker;\ntypedef void (*BrowserWorkerFolderOpenCallback)(\n    void* context,\n    uint32_t item_cnt,\n    int32_t file_idx,\n    bool is_root);\ntypedef void (*BrowserWorkerListLoadCallback)(void* context, uint32_t list_load_offset);\ntypedef void (*BrowserWorkerListItemCallback)(\n    void* context,\n    FuriString* item_path,\n    bool is_folder,\n    bool is_last);\ntypedef void (*BrowserWorkerLongLoadCallback)(void* context);\n\nBrowserWorker* file_browser_worker_alloc(\n    FuriString* path,\n    const char* base_path,\n    const char* ext_filter,\n    bool skip_assets,\n    bool hide_dot_files);\n\nvoid file_browser_worker_free(BrowserWorker* browser);\n\nvoid file_browser_worker_set_callback_context(BrowserWorker* browser, void* context);\n\nvoid file_browser_worker_set_folder_callback(\n    BrowserWorker* browser,\n    BrowserWorkerFolderOpenCallback cb);\n\nvoid file_browser_worker_set_list_callback(\n    BrowserWorker* browser,\n    BrowserWorkerListLoadCallback cb);\n\nvoid file_browser_worker_set_item_callback(\n    BrowserWorker* browser,\n    BrowserWorkerListItemCallback cb);\n\nvoid file_browser_worker_set_long_load_callback(\n    BrowserWorker* browser,\n    BrowserWorkerLongLoadCallback cb);\n\nvoid file_browser_worker_set_config(\n    BrowserWorker* browser,\n    FuriString* path,\n    const char* ext_filter,\n    bool skip_assets,\n    bool hide_dot_files);\n\nvoid file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx);\n\nbool file_browser_worker_is_in_start_folder(BrowserWorker* browser);\n\nvoid file_browser_worker_folder_exit(BrowserWorker* browser);\n\nvoid file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx);\n\nvoid file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/loading.c",
    "content": "#include \"loading.h\"\n\n#include <gui/icon_animation.h>\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <gui/view.h>\n#include <input/input.h>\n\n#include <furi.h>\n#include <assets_icons.h>\n#include <stdint.h>\n\nstruct Loading {\n    View* view;\n};\n\ntypedef struct {\n    IconAnimation* icon;\n} LoadingModel;\n\nstatic void loading_draw_callback(Canvas* canvas, void* _model) {\n    LoadingModel* model = (LoadingModel*)_model;\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, 0, 0, canvas_width(canvas), canvas_height(canvas));\n    canvas_set_color(canvas, ColorBlack);\n\n    uint8_t x = canvas_width(canvas) / 2 - 24 / 2;\n    uint8_t y = canvas_height(canvas) / 2 - 24 / 2;\n\n    canvas_draw_icon(canvas, x, y, &A_Loading_24);\n\n    canvas_draw_icon_animation(canvas, x, y, model->icon);\n}\n\nstatic bool loading_input_callback(InputEvent* event, void* context) {\n    UNUSED(event);\n    furi_assert(context);\n    return true;\n}\n\nstatic void loading_enter_callback(void* context) {\n    furi_assert(context);\n    Loading* instance = context;\n    LoadingModel* model = view_get_model(instance->view);\n    /* using Loading View in conjunction with several\n     * Stack View obligates to reassign\n     * Update callback, as it can be rewritten\n     */\n    view_tie_icon_animation(instance->view, model->icon);\n    icon_animation_start(model->icon);\n    view_commit_model(instance->view, false);\n}\n\nstatic void loading_exit_callback(void* context) {\n    furi_assert(context);\n    Loading* instance = context;\n    LoadingModel* model = view_get_model(instance->view);\n    icon_animation_stop(model->icon);\n    view_commit_model(instance->view, false);\n}\n\nLoading* loading_alloc(void) {\n    Loading* instance = malloc(sizeof(Loading));\n    instance->view = view_alloc();\n    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(LoadingModel));\n    LoadingModel* model = view_get_model(instance->view);\n    model->icon = icon_animation_alloc(&A_Loading_24);\n    view_tie_icon_animation(instance->view, model->icon);\n    view_commit_model(instance->view, false);\n\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, loading_draw_callback);\n    view_set_input_callback(instance->view, loading_input_callback);\n    view_set_enter_callback(instance->view, loading_enter_callback);\n    view_set_exit_callback(instance->view, loading_exit_callback);\n\n    return instance;\n}\n\nvoid loading_free(Loading* instance) {\n    furi_check(instance);\n\n    LoadingModel* model = view_get_model(instance->view);\n    icon_animation_free(model->icon);\n    view_commit_model(instance->view, false);\n\n    furi_assert(instance);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* loading_get_view(Loading* instance) {\n    furi_check(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/loading.h",
    "content": "#pragma once\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Loading anonymous structure */\ntypedef struct Loading Loading;\n\n/** Allocate and initialize\n *\n * This View used to show system is doing some processing\n *\n * @return     Loading View instance\n */\nLoading* loading_alloc(void);\n\n/** Deinitialize and free Loading View\n *\n * @param      instance  Loading instance\n */\nvoid loading_free(Loading* instance);\n\n/** Get Loading view\n *\n * @param      instance  Loading instance\n *\n * @return     View instance that can be used for embedding\n */\nView* loading_get_view(Loading* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/menu.c",
    "content": "#include \"menu.h\"\n\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <furi.h>\n#include <m-array.h>\n\nstruct Menu {\n    View* view;\n};\n\ntypedef struct {\n    const char* label;\n    IconAnimation* icon;\n    uint32_t index;\n    MenuItemCallback callback;\n    void* callback_context;\n} MenuItem;\n\nARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST); //-V658\n\n#define M_OPL_MenuItemArray_t() ARRAY_OPLIST(MenuItemArray, M_POD_OPLIST)\n\ntypedef struct {\n    MenuItemArray_t items;\n    size_t position;\n} MenuModel;\n\nstatic void menu_process_up(Menu* menu);\nstatic void menu_process_down(Menu* menu);\nstatic void menu_process_ok(Menu* menu);\n\nstatic void menu_draw_callback(Canvas* canvas, void* _model) {\n    MenuModel* model = _model;\n\n    canvas_clear(canvas);\n\n    size_t position = model->position;\n    size_t items_count = MenuItemArray_size(model->items);\n    if(items_count) {\n        MenuItem* item;\n        size_t shift_position;\n        // First line\n        canvas_set_font(canvas, FontSecondary);\n        shift_position = (0 + position + items_count - 1) % items_count;\n        item = MenuItemArray_get(model->items, shift_position);\n        canvas_draw_icon_animation(canvas, 4, 3, item->icon);\n        canvas_draw_str(canvas, 22, 14, item->label);\n        // Second line main\n        canvas_set_font(canvas, FontPrimary);\n        shift_position = (1 + position + items_count - 1) % items_count;\n        item = MenuItemArray_get(model->items, shift_position);\n        canvas_draw_icon_animation(canvas, 4, 25, item->icon);\n        canvas_draw_str(canvas, 22, 36, item->label);\n        // Third line\n        canvas_set_font(canvas, FontSecondary);\n        shift_position = (2 + position + items_count - 1) % items_count;\n        item = MenuItemArray_get(model->items, shift_position);\n        canvas_draw_icon_animation(canvas, 4, 47, item->icon);\n        canvas_draw_str(canvas, 22, 58, item->label);\n        // Frame and scrollbar\n        elements_frame(canvas, 0, 21, 128 - 5, 21);\n        elements_scrollbar(canvas, position, items_count);\n    } else {\n        canvas_draw_str(canvas, 2, 32, \"Empty\");\n        elements_scrollbar(canvas, 0, 0);\n    }\n}\n\nstatic bool menu_input_callback(InputEvent* event, void* context) {\n    Menu* menu = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyUp) {\n            consumed = true;\n            menu_process_up(menu);\n        } else if(event->key == InputKeyDown) {\n            consumed = true;\n            menu_process_down(menu);\n        } else if(event->key == InputKeyOk) {\n            consumed = true;\n            menu_process_ok(menu);\n        }\n    } else if(event->type == InputTypeRepeat) {\n        if(event->key == InputKeyUp) {\n            consumed = true;\n            menu_process_up(menu);\n        } else if(event->key == InputKeyDown) {\n            consumed = true;\n            menu_process_down(menu);\n        }\n    }\n\n    return consumed;\n}\n\nstatic void menu_enter(void* context) {\n    Menu* menu = context;\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(MenuItemArray_size(model->items)) {\n                MenuItem* item = MenuItemArray_get(model->items, model->position);\n                icon_animation_start(item->icon);\n            }\n        },\n        false);\n}\n\nstatic void menu_exit(void* context) {\n    Menu* menu = context;\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(MenuItemArray_size(model->items)) {\n                MenuItem* item = MenuItemArray_get(model->items, model->position);\n                icon_animation_stop(item->icon);\n            }\n        },\n        false);\n}\n\nMenu* menu_alloc(void) {\n    Menu* menu = malloc(sizeof(Menu));\n    menu->view = view_alloc();\n    view_set_context(menu->view, menu);\n    view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel));\n    view_set_draw_callback(menu->view, menu_draw_callback);\n    view_set_input_callback(menu->view, menu_input_callback);\n    view_set_enter_callback(menu->view, menu_enter);\n    view_set_exit_callback(menu->view, menu_exit);\n\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            MenuItemArray_init(model->items);\n            model->position = 0;\n        },\n        true);\n\n    return menu;\n}\n\nvoid menu_free(Menu* menu) {\n    furi_check(menu);\n\n    menu_reset(menu);\n    with_view_model(menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false);\n    view_free(menu->view);\n\n    free(menu);\n}\n\nView* menu_get_view(Menu* menu) {\n    furi_check(menu);\n    return menu->view;\n}\n\nvoid menu_add_item(\n    Menu* menu,\n    const char* label,\n    const Icon* icon,\n    uint32_t index,\n    MenuItemCallback callback,\n    void* context) {\n    furi_check(menu);\n    furi_check(label);\n\n    MenuItem* item = NULL;\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            item = MenuItemArray_push_new(model->items);\n            item->label = label;\n            item->icon = icon ? icon_animation_alloc(icon) : icon_animation_alloc(&A_Plugins_14);\n            view_tie_icon_animation(menu->view, item->icon);\n            item->index = index;\n            item->callback = callback;\n            item->callback_context = context;\n        },\n        true);\n}\n\nvoid menu_reset(Menu* menu) {\n    furi_check(menu);\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            for\n                M_EACH(item, model->items, MenuItemArray_t) {\n                    icon_animation_stop(item->icon);\n                    icon_animation_free(item->icon);\n                }\n\n            MenuItemArray_reset(model->items);\n            model->position = 0;\n        },\n        true);\n}\n\nvoid menu_set_selected_item(Menu* menu, uint32_t index) {\n    furi_check(menu);\n\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(index < MenuItemArray_size(model->items)) {\n                model->position = index;\n            }\n        },\n        true);\n}\n\nstatic void menu_process_up(Menu* menu) {\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(MenuItemArray_size(model->items)) {\n                MenuItem* item = MenuItemArray_get(model->items, model->position);\n                icon_animation_stop(item->icon);\n\n                if(model->position > 0) {\n                    model->position--;\n                } else {\n                    model->position = MenuItemArray_size(model->items) - 1;\n                }\n\n                item = MenuItemArray_get(model->items, model->position);\n                icon_animation_start(item->icon);\n            }\n        },\n        true);\n}\n\nstatic void menu_process_down(Menu* menu) {\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(MenuItemArray_size(model->items)) {\n                MenuItem* item = MenuItemArray_get(model->items, model->position);\n                icon_animation_stop(item->icon);\n\n                if(model->position < MenuItemArray_size(model->items) - 1) {\n                    model->position++;\n                } else {\n                    model->position = 0;\n                }\n\n                item = MenuItemArray_get(model->items, model->position);\n                icon_animation_start(item->icon);\n            }\n        },\n        true);\n}\n\nstatic void menu_process_ok(Menu* menu) {\n    MenuItem* item = NULL;\n    with_view_model(\n        menu->view,\n        MenuModel * model,\n        {\n            if(MenuItemArray_size(model->items)) {\n                item = MenuItemArray_get(model->items, model->position);\n            }\n        },\n        true);\n    if(item && item->callback) {\n        item->callback(item->callback_context, item->index);\n    }\n}\n"
  },
  {
    "path": "applications/services/gui/modules/menu.h",
    "content": "/**\n * @file menu.h\n * GUI: Menu view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Menu anonymous structure */\ntypedef struct Menu Menu;\n\n/** Menu Item Callback */\ntypedef void (*MenuItemCallback)(void* context, uint32_t index);\n\n/** Menu allocation and initialization\n *\n * @return     Menu instance\n */\nMenu* menu_alloc(void);\n\n/** Free menu\n *\n * @param      menu  Menu instance\n */\nvoid menu_free(Menu* menu);\n\n/** Get Menu view\n *\n * @param      menu  Menu instance\n *\n * @return     View instance\n */\nView* menu_get_view(Menu* menu);\n\n/** Add item to menu\n *\n * @param      menu      Menu instance\n * @param      label     menu item string label\n * @param      icon      IconAnimation instance\n * @param      index     menu item index\n * @param      callback  MenuItemCallback instance\n * @param      context   pointer to context\n */\nvoid menu_add_item(\n    Menu* menu,\n    const char* label,\n    const Icon* icon,\n    uint32_t index,\n    MenuItemCallback callback,\n    void* context);\n\n/** Clean menu\n * @note       this function does not free menu instance\n *\n * @param      menu  Menu instance\n */\nvoid menu_reset(Menu* menu);\n\n/** Set current menu item\n *\n * @param      menu   Menu instance\n * @param      index  The index\n */\nvoid menu_set_selected_item(Menu* menu, uint32_t index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/number_input.c",
    "content": "#include \"number_input.h\"\n\n#include <gui/elements.h>\n#include <furi.h>\n#include <assets_icons.h>\n#include <lib/toolbox/strint.h>\n\nstruct NumberInput {\n    View* view;\n};\n\ntypedef struct {\n    const char text;\n    const size_t x;\n    const size_t y;\n} NumberInputKey;\n\ntypedef struct {\n    FuriString* header;\n    FuriString* text_buffer;\n\n    int32_t current_number;\n    int32_t max_value;\n    int32_t min_value;\n\n    NumberInputCallback callback;\n    void* callback_context;\n\n    size_t selected_row;\n    size_t selected_column;\n} NumberInputModel;\n\nstatic const size_t keyboard_origin_x = 7;\nstatic const size_t keyboard_origin_y = 31;\nstatic const size_t keyboard_row_count = 2;\nstatic const char enter_symbol = '\\r';\nstatic const char backspace_symbol = '\\b';\nstatic const char sign_symbol = '-';\n\nstatic const NumberInputKey keyboard_keys_row_1[] = {\n    {'0', 0, 12},\n    {'1', 11, 12},\n    {'2', 22, 12},\n    {'3', 33, 12},\n    {'4', 44, 12},\n    {backspace_symbol, 103, 4},\n};\n\nstatic const NumberInputKey keyboard_keys_row_2[] = {\n    {'5', 0, 26},\n    {'6', 11, 26},\n    {'7', 22, 26},\n    {'8', 33, 26},\n    {'9', 44, 26},\n    {sign_symbol, 55, 17},\n    {enter_symbol, 95, 17},\n};\n\nstatic size_t number_input_get_row_size(size_t row_index) {\n    size_t row_size = 0;\n\n    switch(row_index + 1) {\n    case 1:\n        row_size = COUNT_OF(keyboard_keys_row_1);\n        break;\n    case 2:\n        row_size = COUNT_OF(keyboard_keys_row_2);\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row_size;\n}\n\nstatic const NumberInputKey* number_input_get_row(size_t row_index) {\n    const NumberInputKey* row = NULL;\n\n    switch(row_index + 1) {\n    case 1:\n        row = keyboard_keys_row_1;\n        break;\n    case 2:\n        row = keyboard_keys_row_2;\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row;\n}\n\nstatic void number_input_draw_input(Canvas* canvas, NumberInputModel* model) {\n    const size_t text_x = 8;\n    const size_t text_y = 25;\n\n    elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);\n\n    canvas_draw_str(canvas, text_x, text_y, furi_string_get_cstr(model->text_buffer));\n}\n\nstatic bool number_input_use_sign(NumberInputModel* model) {\n    //only show sign button if allowed number range needs it\n    if(model->min_value < 0 && model->max_value >= 0) {\n        return true;\n    }\n    return false;\n}\n\nstatic void number_input_backspace_cb(NumberInputModel* model) {\n    size_t text_length = furi_string_utf8_length(model->text_buffer);\n    if(text_length < 1 || (text_length < 2 && model->current_number <= 0)) {\n        return;\n    }\n    furi_string_set_strn(\n        model->text_buffer, furi_string_get_cstr(model->text_buffer), text_length - 1);\n    model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);\n}\n\nstatic void number_input_handle_up(NumberInputModel* model) {\n    if(model->selected_row > 0) {\n        model->selected_row--;\n        if(model->selected_column > number_input_get_row_size(model->selected_row) - 1) {\n            model->selected_column = number_input_get_row_size(model->selected_row) - 1;\n        }\n    }\n}\n\nstatic void number_input_handle_down(NumberInputModel* model) {\n    if(model->selected_row < keyboard_row_count - 1) {\n        if(model->selected_column >= number_input_get_row_size(model->selected_row) - 1) {\n            model->selected_column = number_input_get_row_size(model->selected_row + 1) - 1;\n        }\n        model->selected_row += 1;\n    }\n    const NumberInputKey* keys = number_input_get_row(model->selected_row);\n    if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {\n        model->selected_column--;\n    }\n}\n\nstatic void number_input_handle_left(NumberInputModel* model) {\n    if(model->selected_column > 0) {\n        model->selected_column--;\n    } else {\n        model->selected_column = number_input_get_row_size(model->selected_row) - 1;\n    }\n    const NumberInputKey* keys = number_input_get_row(model->selected_row);\n    if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {\n        model->selected_column--;\n    }\n}\n\nstatic void number_input_handle_right(NumberInputModel* model) {\n    if(model->selected_column < number_input_get_row_size(model->selected_row) - 1) {\n        model->selected_column++;\n    } else {\n        model->selected_column = 0;\n    }\n    const NumberInputKey* keys = number_input_get_row(model->selected_row);\n    if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {\n        model->selected_column++;\n    }\n}\n\nstatic bool is_number_too_large(NumberInputModel* model) {\n    int64_t value;\n    if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) !=\n       StrintParseNoError) {\n        return true;\n    }\n    if(value > (int64_t)model->max_value) {\n        return true;\n    }\n    return false;\n}\n\nstatic bool is_number_too_small(NumberInputModel* model) {\n    int64_t value;\n    if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) !=\n       StrintParseNoError) {\n        return true;\n    }\n    if(value < (int64_t)model->min_value) {\n        return true;\n    }\n    return false;\n}\n\nstatic void number_input_sign(NumberInputModel* model) {\n    int32_t number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);\n    if(number == 0 && furi_string_cmp_str(model->text_buffer, \"-\") != 0) {\n        furi_string_set_str(model->text_buffer, \"-\");\n        return;\n    }\n    number = number * -1;\n    furi_string_printf(model->text_buffer, \"%ld\", number);\n    if(is_number_too_large(model) || is_number_too_small(model)) {\n        furi_string_printf(model->text_buffer, \"%ld\", model->current_number);\n        return;\n    }\n    model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);\n    if(model->current_number == 0) {\n        furi_string_set_str(model->text_buffer, \"\"); //show empty if 0, better for usability\n    }\n}\n\nstatic void number_input_add_digit(NumberInputModel* model, char* newChar) {\n    furi_string_cat_str(model->text_buffer, newChar);\n    if((model->max_value >= 0 && is_number_too_large(model)) ||\n       (model->min_value < 0 && is_number_too_small(model))) {\n        //you still need to be able to type invalid numbers in some cases to reach valid numbers on later keypress\n        furi_string_printf(model->text_buffer, \"%ld\", model->current_number);\n        return;\n    }\n    model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);\n    if(model->current_number == 0) {\n        furi_string_reset(model->text_buffer);\n    }\n}\n\nstatic void number_input_handle_ok(NumberInputModel* model) {\n    char selected = number_input_get_row(model->selected_row)[model->selected_column].text;\n    char temp_str[2] = {selected, '\\0'};\n    if(selected == enter_symbol) {\n        if(is_number_too_large(model) || is_number_too_small(model)) {\n            return; //Do nothing if number outside allowed range\n        }\n        model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);\n        model->callback(model->callback_context, model->current_number);\n    } else if(selected == backspace_symbol) {\n        number_input_backspace_cb(model);\n    } else if(selected == sign_symbol) {\n        number_input_sign(model);\n    } else {\n        number_input_add_digit(model, temp_str);\n    }\n}\n\nstatic void number_input_view_draw_callback(Canvas* canvas, void* _model) {\n    NumberInputModel* model = _model;\n\n    number_input_draw_input(canvas, model);\n\n    if(!furi_string_empty(model->header)) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str(canvas, 2, 9, furi_string_get_cstr(model->header));\n    }\n    canvas_set_font(canvas, FontKeyboard);\n    // Draw keyboard\n    for(size_t row = 0; row < keyboard_row_count; row++) {\n        const size_t column_count = number_input_get_row_size(row);\n        const NumberInputKey* keys = number_input_get_row(row);\n\n        for(size_t column = 0; column < column_count; column++) {\n            if(keys[column].text == sign_symbol && !number_input_use_sign(model)) {\n                continue;\n            }\n\n            if(keys[column].text == enter_symbol) {\n                if(is_number_too_small(model) || is_number_too_large(model)) {\n                    //in some cases you need to be able to type a number smaller/larger than the limits (expl. min = 50, clear all and editor must allow to type 9 and later 0 for 90)\n                    if(model->selected_row == row && model->selected_column == column) {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySaveBlockedSelected_24x11);\n                    } else {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySaveBlocked_24x11);\n                    }\n                } else {\n                    if(model->selected_row == row && model->selected_column == column) {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySaveSelected_24x11);\n                    } else {\n                        canvas_draw_icon(\n                            canvas,\n                            keyboard_origin_x + keys[column].x,\n                            keyboard_origin_y + keys[column].y,\n                            &I_KeySave_24x11);\n                    }\n                }\n            } else if(keys[column].text == backspace_symbol) {\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeyBackspaceSelected_16x9);\n                } else {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeyBackspace_16x9);\n                }\n            } else if(keys[column].text == sign_symbol) {\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeySignSelected_21x11);\n                } else {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeySign_21x11);\n                }\n            } else {\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_draw_box(\n                        canvas,\n                        keyboard_origin_x + keys[column].x - 3,\n                        keyboard_origin_y + keys[column].y - 10,\n                        11,\n                        13);\n                    canvas_set_color(canvas, ColorWhite);\n                }\n\n                canvas_draw_glyph(\n                    canvas,\n                    keyboard_origin_x + keys[column].x,\n                    keyboard_origin_y + keys[column].y,\n                    keys[column].text);\n                canvas_set_color(canvas, ColorBlack);\n            }\n        }\n    }\n}\n\nstatic bool number_input_view_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    NumberInput* number_input = context;\n\n    bool consumed = false;\n\n    // Fetch the model\n    NumberInputModel* model = view_get_model(number_input->view);\n\n    if(event->type == InputTypeShort || event->type == InputTypeLong ||\n       event->type == InputTypeRepeat) {\n        consumed = true;\n        switch(event->key) {\n        case InputKeyLeft:\n            number_input_handle_left(model);\n            break;\n        case InputKeyRight:\n            number_input_handle_right(model);\n            break;\n        case InputKeyUp:\n            number_input_handle_up(model);\n            break;\n        case InputKeyDown:\n            number_input_handle_down(model);\n            break;\n        case InputKeyOk:\n            number_input_handle_ok(model);\n            break;\n        default:\n            consumed = false;\n            break;\n        }\n    }\n\n    // commit view\n    view_commit_model(number_input->view, consumed);\n\n    return consumed;\n}\n\nNumberInput* number_input_alloc(void) {\n    NumberInput* number_input = malloc(sizeof(NumberInput));\n    number_input->view = view_alloc();\n    view_set_context(number_input->view, number_input);\n    view_allocate_model(number_input->view, ViewModelTypeLocking, sizeof(NumberInputModel));\n    view_set_draw_callback(number_input->view, number_input_view_draw_callback);\n    view_set_input_callback(number_input->view, number_input_view_input_callback);\n\n    with_view_model(\n        number_input->view,\n        NumberInputModel * model,\n        {\n            model->header = furi_string_alloc();\n            model->text_buffer = furi_string_alloc();\n        },\n        true);\n\n    return number_input;\n}\n\nvoid number_input_free(NumberInput* number_input) {\n    furi_check(number_input);\n    with_view_model(\n        number_input->view,\n        NumberInputModel * model,\n        {\n            furi_string_free(model->header);\n            furi_string_free(model->text_buffer);\n        },\n        true);\n    view_free(number_input->view);\n    free(number_input);\n}\n\nView* number_input_get_view(NumberInput* number_input) {\n    furi_check(number_input);\n    return number_input->view;\n}\n\nvoid number_input_set_result_callback(\n    NumberInput* number_input,\n    NumberInputCallback callback,\n    void* callback_context,\n    int32_t current_number,\n    int32_t min_value,\n    int32_t max_value) {\n    furi_check(number_input);\n\n    current_number = CLAMP(current_number, max_value, min_value);\n\n    with_view_model(\n        number_input->view,\n        NumberInputModel * model,\n        {\n            model->callback = callback;\n            model->callback_context = callback_context;\n            model->current_number = current_number;\n            furi_string_printf(model->text_buffer, \"%ld\", current_number);\n            model->min_value = min_value;\n            model->max_value = max_value;\n        },\n        true);\n}\n\nvoid number_input_set_header_text(NumberInput* number_input, const char* text) {\n    furi_check(number_input);\n    with_view_model(\n        number_input->view,\n        NumberInputModel * model,\n        { furi_string_set(model->header, text); },\n        true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/number_input.h",
    "content": "/**\n * @file number_input.h\n * GUI: Integer string keyboard view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Number input anonymous structure  */\ntypedef struct NumberInput NumberInput;\n\n/** Callback to be called on save button press */\ntypedef void (*NumberInputCallback)(void* context, int32_t number);\n\n/** Allocate and initialize Number input.\n *\n * This Number input is used to enter Numbers (Integers).\n *\n * @return     NumberInput instance pointer\n */\nNumberInput* number_input_alloc(void);\n\n/** Deinitialize and free byte input\n *\n * @param      number_input  Number input instance\n */\nvoid number_input_free(NumberInput* number_input);\n\n/** Get byte input view\n *\n * @param      number_input  byte input instance\n *\n * @return     View instance that can be used for embedding\n */\nView* number_input_get_view(NumberInput* number_input);\n\n/** Set byte input result callback\n *\n * @param      number_input      byte input instance\n * @param      input_callback    input callback fn\n * @param      callback_context  callback context\n * @param[in]  current_number    The current number\n * @param      min_value         Min number value\n * @param      max_value         Max number value\n */\n\nvoid number_input_set_result_callback(\n    NumberInput* number_input,\n    NumberInputCallback input_callback,\n    void* callback_context,\n    int32_t current_number,\n    int32_t min_value,\n    int32_t max_value);\n\n/** Set byte input header text\n *\n * @param      number_input  byte input instance\n * @param      text          text to be shown\n */\nvoid number_input_set_header_text(NumberInput* number_input, const char* text);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/popup.c",
    "content": "#include \"popup.h\"\n#include <gui/elements.h>\n#include <furi.h>\n\nstruct Popup {\n    View* view;\n    void* context;\n    PopupCallback callback;\n\n    FuriTimer* timer;\n    uint32_t timer_period_in_ms;\n    bool timer_enabled;\n};\n\ntypedef struct {\n    const char* text;\n    uint8_t x;\n    uint8_t y;\n    Align horizontal;\n    Align vertical;\n} TextElement;\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    const Icon* icon;\n} IconElement;\n\ntypedef struct {\n    TextElement header;\n    TextElement text;\n    IconElement icon;\n} PopupModel;\n\nstatic void popup_view_draw_callback(Canvas* canvas, void* _model) {\n    PopupModel* model = _model;\n\n    // Prepare canvas\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n\n    if(model->icon.icon != NULL) {\n        canvas_draw_icon(canvas, model->icon.x, model->icon.y, model->icon.icon);\n    }\n\n    // Draw header\n    if(model->header.text != NULL) {\n        canvas_set_font(canvas, FontPrimary);\n        elements_multiline_text_aligned(\n            canvas,\n            model->header.x,\n            model->header.y,\n            model->header.horizontal,\n            model->header.vertical,\n            model->header.text);\n    }\n\n    // Draw text\n    if(model->text.text != NULL) {\n        canvas_set_font(canvas, FontSecondary);\n        elements_multiline_text_aligned(\n            canvas,\n            model->text.x,\n            model->text.y,\n            model->text.horizontal,\n            model->text.vertical,\n            model->text.text);\n    }\n}\n\nstatic void popup_timer_callback(void* context) {\n    furi_assert(context);\n    Popup* popup = context;\n\n    if(popup->callback) {\n        popup->callback(popup->context);\n    }\n}\n\nstatic bool popup_view_input_callback(InputEvent* event, void* context) {\n    Popup* popup = context;\n    bool consumed = false;\n\n    // Process key presses only\n    if(event->type == InputTypeShort && popup->callback) {\n        popup->callback(popup->context);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid popup_start_timer(void* context) {\n    Popup* popup = context;\n    if(popup->timer_enabled) {\n        uint32_t timer_period = furi_ms_to_ticks(popup->timer_period_in_ms);\n        if(timer_period == 0) timer_period = 1;\n\n        if(furi_timer_start(popup->timer, timer_period) != FuriStatusOk) {\n            furi_crash();\n        }\n    }\n}\n\nvoid popup_stop_timer(void* context) {\n    Popup* popup = context;\n    furi_timer_stop(popup->timer);\n}\n\nPopup* popup_alloc(void) {\n    Popup* popup = malloc(sizeof(Popup));\n    popup->view = view_alloc();\n    popup->timer = furi_timer_alloc(popup_timer_callback, FuriTimerTypeOnce, popup);\n    popup->timer_period_in_ms = 1000;\n    popup->timer_enabled = false;\n\n    view_set_context(popup->view, popup);\n    view_allocate_model(popup->view, ViewModelTypeLocking, sizeof(PopupModel));\n    view_set_draw_callback(popup->view, popup_view_draw_callback);\n    view_set_input_callback(popup->view, popup_view_input_callback);\n    view_set_enter_callback(popup->view, popup_start_timer);\n    view_set_exit_callback(popup->view, popup_stop_timer);\n\n    with_view_model(\n        popup->view,\n        PopupModel * model,\n        {\n            model->header.text = NULL;\n            model->header.x = 0;\n            model->header.y = 0;\n            model->header.horizontal = AlignLeft;\n            model->header.vertical = AlignBottom;\n\n            model->text.text = NULL;\n            model->text.x = 0;\n            model->text.y = 0;\n            model->text.horizontal = AlignLeft;\n            model->text.vertical = AlignBottom;\n\n            model->icon.x = 0;\n            model->icon.y = 0;\n            model->icon.icon = NULL;\n        },\n        true);\n    return popup;\n}\n\nvoid popup_free(Popup* popup) {\n    furi_check(popup);\n    furi_timer_free(popup->timer);\n    view_free(popup->view);\n    free(popup);\n}\n\nView* popup_get_view(Popup* popup) {\n    furi_check(popup);\n\n    return popup->view;\n}\n\nvoid popup_set_callback(Popup* popup, PopupCallback callback) {\n    furi_check(popup);\n    popup->callback = callback;\n}\n\nvoid popup_set_context(Popup* popup, void* context) {\n    furi_check(popup);\n    popup->context = context;\n}\n\nvoid popup_set_header(\n    Popup* popup,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(popup);\n\n    with_view_model(\n        popup->view,\n        PopupModel * model,\n        {\n            model->header.text = text;\n            model->header.x = x;\n            model->header.y = y;\n            model->header.horizontal = horizontal;\n            model->header.vertical = vertical;\n        },\n        true);\n}\n\nvoid popup_set_text(\n    Popup* popup,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical) {\n    furi_check(popup);\n\n    with_view_model(\n        popup->view,\n        PopupModel * model,\n        {\n            model->text.text = text;\n            model->text.x = x;\n            model->text.y = y;\n            model->text.horizontal = horizontal;\n            model->text.vertical = vertical;\n        },\n        true);\n}\n\nvoid popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon) {\n    furi_check(popup);\n\n    with_view_model(\n        popup->view,\n        PopupModel * model,\n        {\n            model->icon.x = x;\n            model->icon.y = y;\n            model->icon.icon = icon;\n        },\n        true);\n}\n\nvoid popup_set_timeout(Popup* popup, uint32_t timeout_in_ms) {\n    furi_check(popup);\n    popup->timer_period_in_ms = timeout_in_ms;\n}\n\nvoid popup_enable_timeout(Popup* popup) {\n    furi_check(popup);\n\n    popup->timer_enabled = true;\n}\n\nvoid popup_disable_timeout(Popup* popup) {\n    furi_check(popup);\n\n    popup->timer_enabled = false;\n}\n\nvoid popup_reset(Popup* popup) {\n    furi_check(popup);\n\n    with_view_model(\n        popup->view,\n        PopupModel * model,\n        {\n            memset(&model->header, 0, sizeof(model->header));\n            memset(&model->text, 0, sizeof(model->text));\n            memset(&model->icon, 0, sizeof(model->icon));\n        },\n        false);\n    popup->callback = NULL;\n    popup->context = NULL;\n    popup->timer_enabled = false;\n    popup->timer_period_in_ms = 0;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/popup.h",
    "content": "/**\n * @file popup.h\n * GUI: Popup view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Popup anonymous structure */\ntypedef struct Popup Popup;\n\n/** Popup result callback type\n * @warning    comes from GUI thread\n */\ntypedef void (*PopupCallback)(void* context);\n\n/** Allocate and initialize popup\n *\n * This popup used to ask simple questions like Yes/\n *\n * @return     Popup instance\n */\nPopup* popup_alloc(void);\n\n/** Deinitialize and free popup\n *\n * @param      popup  Popup instance\n */\nvoid popup_free(Popup* popup);\n\n/** Get popup view\n *\n * @param      popup  Popup instance\n *\n * @return     View instance that can be used for embedding\n */\nView* popup_get_view(Popup* popup);\n\n/** Set popup timeout callback\n *\n * @param      popup     Popup instance\n * @param      callback  PopupCallback\n */\nvoid popup_set_callback(Popup* popup, PopupCallback callback);\n\n/** Set popup context\n *\n * @param      popup    Popup instance\n * @param      context  context pointer, will be passed to result callback\n */\nvoid popup_set_context(Popup* popup, void* context);\n\n/** Set popup header text\n *\n * If text is null, popup header will not be rendered\n *\n * @param      popup       Popup instance\n * @param      text        text to be shown, can be multiline\n * @param      x           x position\n * @param      y           y position\n * @param      horizontal  horizontal alignment\n * @param      vertical    vertical alignment\n */\nvoid popup_set_header(\n    Popup* popup,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/** Set popup text\n *\n * If text is null, popup text will not be rendered\n *\n * @param      popup       Popup instance\n * @param      text        text to be shown, can be multiline\n * @param      x           x position\n * @param      y           y position\n * @param      horizontal  horizontal alignment\n * @param      vertical    vertical alignment\n */\nvoid popup_set_text(\n    Popup* popup,\n    const char* text,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical);\n\n/** Set popup icon\n *\n * If icon position is negative, popup icon will not be rendered\n *\n * @param      popup  Popup instance\n * @param      x      x position\n * @param      y      y position\n * @param      icon   pointer to Icon data\n */\nvoid popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon);\n\n/** Set popup timeout\n *\n * @param      popup          Popup instance\n * @param      timeout_in_ms  popup timeout value in milliseconds\n */\nvoid popup_set_timeout(Popup* popup, uint32_t timeout_in_ms);\n\n/** Enable popup timeout\n *\n * @param      popup  Popup instance\n */\nvoid popup_enable_timeout(Popup* popup);\n\n/** Disable popup timeout\n *\n * @param      popup  Popup instance\n */\nvoid popup_disable_timeout(Popup* popup);\n\n/** Reset popup instance state\n *\n * @param       popup Popup instance\n */\nvoid popup_reset(Popup* popup);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/submenu.c",
    "content": "#include \"submenu.h\"\n\n#include <gui/elements.h>\n#include <furi.h>\n#include <m-array.h>\n\nstruct Submenu {\n    View* view;\n};\n\ntypedef struct {\n    FuriString* label;\n    uint32_t index;\n    union {\n        SubmenuItemCallback callback;\n        SubmenuItemCallbackEx callback_ex;\n    };\n    void* callback_context;\n    bool has_extended_events;\n} SubmenuItem;\n\nstatic void SubmenuItem_init(SubmenuItem* item) {\n    item->label = furi_string_alloc();\n    item->index = 0;\n    item->callback = NULL;\n    item->callback_context = NULL;\n}\n\nstatic void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) {\n    item->label = furi_string_alloc_set(src->label);\n    item->index = src->index;\n    item->callback = src->callback;\n    item->callback_context = src->callback_context;\n}\n\nstatic void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) {\n    furi_string_set(item->label, src->label);\n    item->index = src->index;\n    item->callback = src->callback;\n    item->callback_context = src->callback_context;\n}\n\nstatic void SubmenuItem_clear(SubmenuItem* item) {\n    furi_string_free(item->label);\n}\n\nARRAY_DEF(\n    SubmenuItemArray,\n    SubmenuItem,\n    (INIT(API_2(SubmenuItem_init)),\n     SET(API_6(SubmenuItem_set)),\n     INIT_SET(API_6(SubmenuItem_init_set)),\n     CLEAR(API_2(SubmenuItem_clear))))\n\ntypedef struct {\n    SubmenuItemArray_t items;\n    FuriString* header;\n    size_t position;\n    size_t window_position;\n} SubmenuModel;\n\nstatic void submenu_process_up(Submenu* submenu);\nstatic void submenu_process_down(Submenu* submenu);\nstatic void submenu_process_ok(Submenu* submenu, InputType input_type);\n\nstatic void submenu_view_draw_callback(Canvas* canvas, void* _model) {\n    SubmenuModel* model = _model;\n\n    const uint8_t item_height = 16;\n    uint8_t item_width = canvas_width(canvas) - 5;\n\n    canvas_clear(canvas);\n\n    if(!furi_string_empty(model->header)) {\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header));\n    }\n\n    canvas_set_font(canvas, FontSecondary);\n\n    size_t position = 0;\n    SubmenuItemArray_it_t it;\n    for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);\n        SubmenuItemArray_next(it)) {\n        const size_t item_position = position - model->window_position;\n        const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;\n        uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16;\n\n        if(item_position < items_on_screen) {\n            if(position == model->position) {\n                canvas_set_color(canvas, ColorBlack);\n                elements_slightly_rounded_box(\n                    canvas,\n                    0,\n                    y_offset + (item_position * item_height) + 1,\n                    item_width,\n                    item_height - 2);\n                canvas_set_color(canvas, ColorWhite);\n            } else {\n                canvas_set_color(canvas, ColorBlack);\n            }\n\n            FuriString* disp_str;\n            disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);\n            elements_string_fit_width(canvas, disp_str, item_width - (6 * 2));\n\n            canvas_draw_str(\n                canvas,\n                6,\n                y_offset + (item_position * item_height) + item_height - 4,\n                furi_string_get_cstr(disp_str));\n\n            furi_string_free(disp_str);\n        }\n\n        position++;\n    }\n\n    elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items));\n}\n\nstatic bool submenu_view_input_callback(InputEvent* event, void* context) {\n    Submenu* submenu = context;\n    furi_assert(submenu);\n    bool consumed = false;\n\n    if(event->key == InputKeyOk) {\n        consumed = true;\n        submenu_process_ok(submenu, event->type);\n    } else if(event->type == InputTypeShort) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            submenu_process_up(submenu);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            submenu_process_down(submenu);\n            break;\n        default:\n            break;\n        }\n    } else if(event->type == InputTypeRepeat) {\n        if(event->key == InputKeyUp) {\n            consumed = true;\n            submenu_process_up(submenu);\n        } else if(event->key == InputKeyDown) {\n            consumed = true;\n            submenu_process_down(submenu);\n        }\n    }\n\n    return consumed;\n}\n\nSubmenu* submenu_alloc(void) {\n    Submenu* submenu = malloc(sizeof(Submenu));\n    submenu->view = view_alloc();\n    view_set_context(submenu->view, submenu);\n    view_allocate_model(submenu->view, ViewModelTypeLocking, sizeof(SubmenuModel));\n    view_set_draw_callback(submenu->view, submenu_view_draw_callback);\n    view_set_input_callback(submenu->view, submenu_view_input_callback);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            SubmenuItemArray_init(model->items);\n            model->position = 0;\n            model->window_position = 0;\n            model->header = furi_string_alloc();\n        },\n        true);\n\n    return submenu;\n}\n\nvoid submenu_free(Submenu* submenu) {\n    furi_check(submenu);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            furi_string_free(model->header);\n            SubmenuItemArray_clear(model->items);\n        },\n        true);\n    view_free(submenu->view);\n    free(submenu);\n}\n\nView* submenu_get_view(Submenu* submenu) {\n    furi_check(submenu);\n    return submenu->view;\n}\n\nvoid submenu_add_item(\n    Submenu* submenu,\n    const char* label,\n    uint32_t index,\n    SubmenuItemCallback callback,\n    void* callback_context) {\n    SubmenuItem* item = NULL;\n    furi_check(label);\n    furi_check(submenu);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            item = SubmenuItemArray_push_new(model->items);\n            furi_string_set_str(item->label, label);\n            item->index = index;\n            item->callback = callback;\n            item->callback_context = callback_context;\n            item->has_extended_events = false;\n        },\n        true);\n}\n\nvoid submenu_add_item_ex(\n    Submenu* submenu,\n    const char* label,\n    uint32_t index,\n    SubmenuItemCallbackEx callback,\n    void* callback_context) {\n    SubmenuItem* item = NULL;\n    furi_check(label);\n    furi_check(submenu);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            item = SubmenuItemArray_push_new(model->items);\n            furi_string_set_str(item->label, label);\n            item->index = index;\n            item->callback_ex = callback;\n            item->callback_context = callback_context;\n            item->has_extended_events = true;\n        },\n        true);\n}\n\nvoid submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label) {\n    furi_check(submenu);\n    furi_check(label);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            SubmenuItemArray_it_t it;\n            for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);\n                SubmenuItemArray_next(it)) {\n                if(index == SubmenuItemArray_cref(it)->index) {\n                    furi_string_set_str(SubmenuItemArray_cref(it)->label, label);\n                    break;\n                }\n            }\n        },\n        true);\n}\n\nvoid submenu_reset(Submenu* submenu) {\n    furi_check(submenu);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            SubmenuItemArray_reset(model->items);\n            model->position = 0;\n            model->window_position = 0;\n            furi_string_reset(model->header);\n        },\n        true);\n}\n\nuint32_t submenu_get_selected_item(Submenu* submenu) {\n    furi_check(submenu);\n\n    uint32_t selected_item_index = 0;\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            if(model->position < SubmenuItemArray_size(model->items)) {\n                const SubmenuItem* item = SubmenuItemArray_cget(model->items, model->position);\n                selected_item_index = item->index;\n            }\n        },\n        false);\n\n    return selected_item_index;\n}\n\nvoid submenu_set_selected_item(Submenu* submenu, uint32_t index) {\n    furi_check(submenu);\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            size_t position = 0;\n            SubmenuItemArray_it_t it;\n            for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);\n                SubmenuItemArray_next(it)) {\n                if(index == SubmenuItemArray_cref(it)->index) {\n                    break;\n                }\n                position++;\n            }\n\n            const size_t items_size = SubmenuItemArray_size(model->items);\n\n            if(position >= items_size) {\n                position = 0;\n            }\n\n            model->position = position;\n            model->window_position = position;\n\n            if(model->window_position > 0) {\n                model->window_position -= 1;\n            }\n\n            const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;\n\n            if(items_size <= items_on_screen) {\n                model->window_position = 0;\n            } else {\n                const size_t pos = items_size - items_on_screen;\n                if(model->window_position > pos) {\n                    model->window_position = pos;\n                }\n            }\n        },\n        true);\n}\n\nvoid submenu_process_up(Submenu* submenu) {\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;\n            const size_t items_size = SubmenuItemArray_size(model->items);\n\n            if(model->position > 0) {\n                model->position--;\n                if((model->position == model->window_position) && (model->window_position > 0)) {\n                    model->window_position--;\n                }\n            } else {\n                model->position = items_size - 1;\n                if(model->position > items_on_screen - 1) {\n                    model->window_position = model->position - (items_on_screen - 1);\n                }\n            }\n        },\n        true);\n}\n\nvoid submenu_process_down(Submenu* submenu) {\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;\n            const size_t items_size = SubmenuItemArray_size(model->items);\n\n            if(model->position < items_size - 1) {\n                model->position++;\n                if((model->position - model->window_position > items_on_screen - 2) &&\n                   (model->window_position < items_size - items_on_screen)) {\n                    model->window_position++;\n                }\n            } else {\n                model->position = 0;\n                model->window_position = 0;\n            }\n        },\n        true);\n}\n\nvoid submenu_process_ok(Submenu* submenu, InputType input_type) {\n    SubmenuItem* item = NULL;\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            const size_t items_size = SubmenuItemArray_size(model->items);\n            if(model->position < items_size) {\n                item = SubmenuItemArray_get(model->items, model->position);\n            }\n        },\n        true);\n\n    if(!item) return;\n\n    if(!item->has_extended_events && input_type == InputTypeShort && item->callback) {\n        item->callback(item->callback_context, item->index);\n    } else if(item->has_extended_events && item->callback_ex) {\n        item->callback_ex(item->callback_context, input_type, item->index);\n    }\n}\n\nvoid submenu_set_header(Submenu* submenu, const char* header) {\n    furi_check(submenu);\n\n    with_view_model(\n        submenu->view,\n        SubmenuModel * model,\n        {\n            if(header == NULL) {\n                furi_string_reset(model->header);\n            } else {\n                furi_string_set_str(model->header, header);\n            }\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/submenu.h",
    "content": "/**\n * @file submenu.h\n * GUI: SubMenu view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Submenu anonymous structure */\ntypedef struct Submenu Submenu;\ntypedef void (*SubmenuItemCallback)(void* context, uint32_t index);\ntypedef void (*SubmenuItemCallbackEx)(void* context, InputType input_type, uint32_t index);\n\n/** Allocate and initialize submenu \n * \n * This submenu is used to select one option\n *\n * @return     Submenu instance\n */\nSubmenu* submenu_alloc(void);\n\n/** Deinitialize and free submenu\n *\n * @param      submenu  Submenu instance\n */\nvoid submenu_free(Submenu* submenu);\n\n/** Get submenu view\n *\n * @param      submenu  Submenu instance\n *\n * @return     View instance that can be used for embedding\n */\nView* submenu_get_view(Submenu* submenu);\n\n/** Add item to submenu\n *\n * @param      submenu           Submenu instance\n * @param      label             menu item label\n * @param      index             menu item index, used for callback, may be\n *                               the same with other items\n * @param      callback          menu item callback\n * @param      callback_context  menu item callback context\n */\nvoid submenu_add_item(\n    Submenu* submenu,\n    const char* label,\n    uint32_t index,\n    SubmenuItemCallback callback,\n    void* callback_context);\n\n/** Add item to submenu with extended press events\n *\n * @param      submenu           Submenu instance\n * @param      label             menu item label\n * @param      index             menu item index, used for callback, may be\n *                               the same with other items\n * @param      callback          menu item extended callback\n * @param      callback_context  menu item callback context\n */\nvoid submenu_add_item_ex(\n    Submenu* submenu,\n    const char* label,\n    uint32_t index,\n    SubmenuItemCallbackEx callback,\n    void* callback_context);\n\n/** Change label of an existing item\n * \n * @param      submenu  Submenu instance\n * @param      index    The index of the item\n * @param      label    The new label\n */\nvoid submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label);\n\n/** Remove all items from submenu\n *\n * @param      submenu  Submenu instance\n */\nvoid submenu_reset(Submenu* submenu);\n\n/** Get submenu selected item index\n *\n * @param      submenu  Submenu instance\n *\n * @return     Index of the selected item\n */\nuint32_t submenu_get_selected_item(Submenu* submenu);\n\n/** Set submenu selected item by index\n *\n * @param      submenu  Submenu instance\n * @param      index    The index of the selected item\n */\nvoid submenu_set_selected_item(Submenu* submenu, uint32_t index);\n\n/** Set optional header for submenu\n *\n * @param      submenu  Submenu instance\n * @param      header   header to set\n */\nvoid submenu_set_header(Submenu* submenu, const char* header);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/text_box.c",
    "content": "#include \"text_box.h\"\n#include <gui/canvas.h>\n#include <gui/elements.h>\n#include <furi.h>\n#include <stdint.h>\n\n#define TEXT_BOX_TEXT_WIDTH           (120)\n#define TEXT_BOX_TEXT_HEIGHT          (56)\n#define TEXT_BOX_MAX_LINES_PER_SCREEN (10)\n\n#define TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM     (3)\n#define TEXT_BOX_LINES_SCROLL_SPEED_FAST       (5)\n#define TEXT_BOX_LINES_SCROLL_SPEED_SATURATION (9)\n\nstruct TextBox {\n    View* view;\n\n    uint16_t button_held_for_ticks;\n};\n\ntypedef struct {\n    TextBoxFont font;\n    TextBoxFocus focus;\n    const char* text;\n\n    int32_t scroll_pos;\n    int32_t scroll_num;\n    int32_t lines_on_screen;\n\n    int32_t line_offset;\n    int32_t text_offset;\n    FuriString* text_on_screen;\n    FuriString* text_line;\n\n    bool formatted;\n} TextBoxModel;\n\nstatic void text_box_process_down(TextBox* text_box, uint8_t lines) {\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            if(model->scroll_pos + lines < model->scroll_num) {\n                model->scroll_pos += lines;\n            } else {\n                if(model->scroll_num > 0) {\n                    model->scroll_pos = model->scroll_num - 1;\n                }\n            }\n        },\n        true);\n}\n\nstatic void text_box_process_up(TextBox* text_box, uint8_t lines) {\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            if(model->scroll_pos - lines > 0) {\n                model->scroll_pos -= lines;\n            } else {\n                model->scroll_pos = 0;\n            }\n        },\n        true);\n}\n\nstatic bool text_box_view_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n\n    TextBox* text_box = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n        int32_t scroll_speed = 1;\n        if(text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_FAST) {\n            if(text_box->button_held_for_ticks % 2) {\n                scroll_speed = 0;\n            } else {\n                scroll_speed =\n                    (text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_SATURATION) ?\n                        TEXT_BOX_LINES_SCROLL_SPEED_FAST :\n                        TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM;\n            }\n        }\n\n        if(event->key == InputKeyDown) {\n            text_box_process_down(text_box, scroll_speed);\n            consumed = true;\n        } else if(event->key == InputKeyUp) {\n            text_box_process_up(text_box, scroll_speed);\n            consumed = true;\n        }\n\n        text_box->button_held_for_ticks++;\n    } else if(event->type == InputTypeRelease) {\n        text_box->button_held_for_ticks = 0;\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nstatic bool text_box_end_of_text_reached(TextBoxModel* model) {\n    return model->text[model->text_offset] == '\\0';\n}\n\nstatic bool text_box_start_of_text_reached(TextBoxModel* model) {\n    return model->text_offset == 0;\n}\n\nstatic void text_box_seek_next_line(Canvas* canvas, TextBoxModel* model) {\n    size_t line_width = 0;\n\n    while(!text_box_end_of_text_reached(model)) {\n        char symb = model->text[model->text_offset];\n        if(symb == '\\n') {\n            model->text_offset++;\n            break;\n        } else {\n            size_t glyph_width = canvas_glyph_width(canvas, symb);\n            if(line_width + glyph_width > TEXT_BOX_TEXT_WIDTH) {\n                break;\n            }\n            line_width += glyph_width;\n            model->text_offset++;\n        }\n    }\n}\n\nstatic void text_box_seek_end_of_prev_line(TextBoxModel* model) {\n    do {\n        if(text_box_start_of_text_reached(model)) break;\n        model->text_offset--;\n        if(text_box_start_of_text_reached(model)) break;\n        if(model->text[model->text_offset] == '\\n') {\n            model->text_offset--;\n        }\n    } while(false);\n}\n\nstatic void text_box_seek_prev_paragraph(TextBoxModel* model) {\n    while(!text_box_start_of_text_reached(model)) {\n        if(model->text[model->text_offset] == '\\n') {\n            model->text_offset++;\n            break;\n        }\n        model->text_offset--;\n    }\n}\n\nstatic void text_box_seek_prev_line(Canvas* canvas, TextBoxModel* model) {\n    int32_t start_text_offset = model->text_offset;\n\n    text_box_seek_end_of_prev_line(model);\n    text_box_seek_prev_paragraph(model);\n\n    int32_t current_text_offset = model->text_offset;\n    while(true) {\n        text_box_seek_next_line(canvas, model);\n        if(model->text_offset == start_text_offset) {\n            break;\n        }\n        current_text_offset = model->text_offset;\n    }\n    model->text_offset = current_text_offset;\n}\n\nstatic void text_box_move_line_offset(Canvas* canvas, TextBoxModel* model, int32_t line_offset) {\n    if(line_offset >= 0) {\n        for(int32_t i = 0; i < line_offset; i++) {\n            text_box_seek_next_line(canvas, model);\n        }\n    } else {\n        for(int32_t i = 0; i < (-line_offset); i++) {\n            text_box_seek_prev_line(canvas, model);\n        }\n    }\n}\n\nstatic void text_box_update_screen_text(Canvas* canvas, TextBoxModel* model) {\n    furi_string_reset(model->text_on_screen);\n    furi_string_reset(model->text_line);\n\n    int32_t start_text_offset = model->text_offset;\n\n    for(int32_t i = 0; i < model->lines_on_screen; i++) {\n        int32_t current_line_text_offset = model->text_offset;\n        text_box_seek_next_line(canvas, model);\n        int32_t next_line_text_offset = model->text_offset;\n        furi_string_set_strn(\n            model->text_line,\n            &model->text[current_line_text_offset],\n            next_line_text_offset - current_line_text_offset);\n        size_t str_len = furi_string_size(model->text_line);\n        if(furi_string_get_char(model->text_line, str_len - 1) != '\\n') {\n            furi_string_push_back(model->text_line, '\\n');\n        }\n        furi_string_cat(model->text_on_screen, model->text_line);\n\n        if(text_box_end_of_text_reached(model)) break;\n        current_line_text_offset = next_line_text_offset;\n    }\n\n    model->text_offset = start_text_offset;\n}\n\nstatic void text_box_update_text_on_screen(Canvas* canvas, TextBoxModel* model) {\n    int32_t line_offset = model->scroll_pos - model->line_offset;\n    text_box_move_line_offset(canvas, model, line_offset);\n    text_box_update_screen_text(canvas, model);\n    model->line_offset = model->scroll_pos;\n}\n\nstatic void text_box_prepare_model(Canvas* canvas, TextBoxModel* model) {\n    int32_t lines_num = 0;\n    model->text_offset = 0;\n    model->scroll_num = 0;\n    model->scroll_pos = 0;\n    model->line_offset = 0;\n    model->lines_on_screen = TEXT_BOX_TEXT_HEIGHT / canvas_current_font_height(canvas);\n\n    // Cache text offset to quick final text offset update if TextBoxFocusEnd is set\n    int32_t window_offset[TEXT_BOX_MAX_LINES_PER_SCREEN] = {};\n    do {\n        window_offset[lines_num % model->lines_on_screen] = model->text_offset;\n        text_box_seek_next_line(canvas, model);\n        lines_num++;\n    } while(!text_box_end_of_text_reached(model));\n    model->text_offset = 0;\n    lines_num++;\n\n    if(model->focus == TextBoxFocusEnd) {\n        if(lines_num > model->lines_on_screen) {\n            model->text_offset = window_offset[(lines_num - 1) % model->lines_on_screen];\n        }\n    }\n\n    if(lines_num > model->lines_on_screen) {\n        model->scroll_num = lines_num - model->lines_on_screen;\n        model->scroll_pos = (model->focus == TextBoxFocusEnd) ? model->scroll_num - 1 : 0;\n    }\n\n    text_box_update_screen_text(canvas, model);\n    model->line_offset = model->scroll_pos;\n}\n\nstatic void text_box_view_draw_callback(Canvas* canvas, void* _model) {\n    TextBoxModel* model = _model;\n\n    if(!model->text) {\n        return;\n    }\n\n    canvas_clear(canvas);\n    if(model->font == TextBoxFontText) {\n        canvas_set_font(canvas, FontSecondary);\n    } else if(model->font == TextBoxFontHex) {\n        canvas_set_font(canvas, FontKeyboard);\n    }\n\n    if(!model->formatted) {\n        text_box_prepare_model(canvas, model);\n        model->formatted = true;\n    }\n\n    elements_slightly_rounded_frame(canvas, 0, 0, 124, 64);\n    elements_scrollbar(canvas, model->scroll_pos, model->scroll_num);\n\n    if(model->line_offset != model->scroll_pos) {\n        text_box_update_text_on_screen(canvas, model);\n    }\n    elements_multiline_text(canvas, 3, 11, furi_string_get_cstr(model->text_on_screen));\n}\n\nTextBox* text_box_alloc(void) {\n    TextBox* text_box = malloc(sizeof(TextBox));\n    text_box->view = view_alloc();\n    view_set_context(text_box->view, text_box);\n    view_allocate_model(text_box->view, ViewModelTypeLocking, sizeof(TextBoxModel));\n    view_set_draw_callback(text_box->view, text_box_view_draw_callback);\n    view_set_input_callback(text_box->view, text_box_view_input_callback);\n\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            model->text = NULL;\n            model->text_on_screen = furi_string_alloc();\n            model->text_line = furi_string_alloc();\n            model->formatted = false;\n            model->font = TextBoxFontText;\n        },\n        true);\n\n    return text_box;\n}\n\nvoid text_box_free(TextBox* text_box) {\n    furi_check(text_box);\n\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            furi_string_free(model->text_on_screen);\n            furi_string_free(model->text_line);\n        },\n        true);\n    view_free(text_box->view);\n    free(text_box);\n}\n\nView* text_box_get_view(TextBox* text_box) {\n    furi_check(text_box);\n    return text_box->view;\n}\n\nvoid text_box_reset(TextBox* text_box) {\n    furi_check(text_box);\n\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            model->text = NULL;\n            model->font = TextBoxFontText;\n            model->focus = TextBoxFocusStart;\n            furi_string_reset(model->text_line);\n            furi_string_reset(model->text_on_screen);\n            model->line_offset = 0;\n            model->text_offset = 0;\n            model->lines_on_screen = 0;\n            model->scroll_num = 0;\n            model->scroll_pos = 0;\n            model->formatted = false;\n        },\n        true);\n}\n\nvoid text_box_set_text(TextBox* text_box, const char* text) {\n    furi_check(text_box);\n    furi_check(text);\n\n    with_view_model(\n        text_box->view,\n        TextBoxModel * model,\n        {\n            model->text = text;\n            model->formatted = false;\n        },\n        true);\n}\n\nvoid text_box_set_font(TextBox* text_box, TextBoxFont font) {\n    furi_check(text_box);\n\n    with_view_model(text_box->view, TextBoxModel * model, { model->font = font; }, true);\n}\n\nvoid text_box_set_focus(TextBox* text_box, TextBoxFocus focus) {\n    furi_check(text_box);\n\n    with_view_model(text_box->view, TextBoxModel * model, { model->focus = focus; }, true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/text_box.h",
    "content": "/**\n * @file text_box.h\n * GUI: TextBox view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** TextBox anonymous structure */\ntypedef struct TextBox TextBox;\n\ntypedef enum {\n    TextBoxFontText,\n    TextBoxFontHex,\n} TextBoxFont;\n\ntypedef enum {\n    TextBoxFocusStart,\n    TextBoxFocusEnd,\n} TextBoxFocus;\n\n/** Allocate and initialize text_box\n *\n * @return     TextBox instance\n */\nTextBox* text_box_alloc(void);\n\n/** Deinitialize and free text_box\n *\n * @param      text_box  text_box instance\n */\nvoid text_box_free(TextBox* text_box);\n\n/** Get text_box view\n *\n * @param      text_box  TextBox instance\n *\n * @return     View instance that can be used for embedding\n */\nView* text_box_get_view(TextBox* text_box);\n\n/** Clean text_box\n *\n * @param      text_box  TextBox instance\n */\nvoid text_box_reset(TextBox* text_box);\n\n/** Set text for text_box\n *\n * @param      text_box  TextBox instance\n * @param      text      text to set\n */\nvoid text_box_set_text(TextBox* text_box, const char* text);\n\n/** Set TextBox font\n *\n * @param      text_box  TextBox instance\n * @param      font      TextBoxFont instance\n */\nvoid text_box_set_font(TextBox* text_box, TextBoxFont font);\n\n/** Set TextBox focus\n * @note Use to display from start or from end\n *\n * @param      text_box  TextBox instance\n * @param      focus     TextBoxFocus instance\n */\nvoid text_box_set_focus(TextBox* text_box, TextBoxFocus focus);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/text_input.c",
    "content": "#include \"text_input.h\"\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <furi.h>\n\nstruct TextInput {\n    View* view;\n    FuriTimer* timer;\n};\n\ntypedef struct {\n    const char text;\n    const uint8_t x;\n    const uint8_t y;\n} TextInputKey;\n\ntypedef struct {\n    const char* header;\n    char* text_buffer;\n    size_t text_buffer_size;\n    size_t minimum_length;\n    bool clear_default_text;\n\n    TextInputCallback callback;\n    void* callback_context;\n\n    uint8_t selected_row;\n    uint8_t selected_column;\n\n    TextInputValidatorCallback validator_callback;\n    void* validator_callback_context;\n    FuriString* validator_text;\n    bool validator_message_visible;\n} TextInputModel;\n\nstatic const uint8_t keyboard_origin_x = 1;\nstatic const uint8_t keyboard_origin_y = 29;\nstatic const uint8_t keyboard_row_count = 3;\n\n#define ENTER_KEY     '\\r'\n#define BACKSPACE_KEY '\\b'\n\nstatic const TextInputKey keyboard_keys_row_1[] = {\n    {'q', 1, 8},\n    {'w', 10, 8},\n    {'e', 19, 8},\n    {'r', 28, 8},\n    {'t', 37, 8},\n    {'y', 46, 8},\n    {'u', 55, 8},\n    {'i', 64, 8},\n    {'o', 73, 8},\n    {'p', 82, 8},\n    {'0', 91, 8},\n    {'1', 100, 8},\n    {'2', 110, 8},\n    {'3', 120, 8},\n};\n\nstatic const TextInputKey keyboard_keys_row_2[] = {\n    {'a', 1, 20},\n    {'s', 10, 20},\n    {'d', 19, 20},\n    {'f', 28, 20},\n    {'g', 37, 20},\n    {'h', 46, 20},\n    {'j', 55, 20},\n    {'k', 64, 20},\n    {'l', 73, 20},\n    {BACKSPACE_KEY, 82, 12},\n    {'4', 100, 20},\n    {'5', 110, 20},\n    {'6', 120, 20},\n};\n\nstatic const TextInputKey keyboard_keys_row_3[] = {\n    {'z', 1, 32},\n    {'x', 10, 32},\n    {'c', 19, 32},\n    {'v', 28, 32},\n    {'b', 37, 32},\n    {'n', 46, 32},\n    {'m', 55, 32},\n    {'_', 64, 32},\n    {ENTER_KEY, 74, 23},\n    {'7', 100, 32},\n    {'8', 110, 32},\n    {'9', 120, 32},\n};\n\nstatic uint8_t get_row_size(uint8_t row_index) {\n    uint8_t row_size = 0;\n\n    switch(row_index + 1) {\n    case 1:\n        row_size = COUNT_OF(keyboard_keys_row_1);\n        break;\n    case 2:\n        row_size = COUNT_OF(keyboard_keys_row_2);\n        break;\n    case 3:\n        row_size = COUNT_OF(keyboard_keys_row_3);\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row_size;\n}\n\nstatic const TextInputKey* get_row(uint8_t row_index) {\n    const TextInputKey* row = NULL;\n\n    switch(row_index + 1) {\n    case 1:\n        row = keyboard_keys_row_1;\n        break;\n    case 2:\n        row = keyboard_keys_row_2;\n        break;\n    case 3:\n        row = keyboard_keys_row_3;\n        break;\n    default:\n        furi_crash();\n    }\n\n    return row;\n}\n\nstatic char get_selected_char(TextInputModel* model) {\n    return get_row(model->selected_row)[model->selected_column].text;\n}\n\nstatic bool char_is_lowercase(char letter) {\n    return letter >= 0x61 && letter <= 0x7A;\n}\n\nstatic char char_to_uppercase(const char letter) {\n    if(letter == '_') {\n        return 0x20;\n    } else if(islower(letter)) {\n        return letter - 0x20;\n    } else {\n        return letter;\n    }\n}\n\nstatic void text_input_backspace_cb(TextInputModel* model) {\n    uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);\n    if(text_length > 0) {\n        model->text_buffer[text_length - 1] = 0;\n    }\n}\n\nstatic void text_input_view_draw_callback(Canvas* canvas, void* _model) {\n    TextInputModel* model = _model;\n    uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;\n    uint8_t needed_string_width = canvas_width(canvas) - 8;\n    uint8_t start_pos = 4;\n\n    const char* text = model->text_buffer;\n\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n\n    canvas_draw_str(canvas, 2, 8, model->header);\n    elements_slightly_rounded_frame(canvas, 1, 12, 126, 15);\n\n    if(canvas_string_width(canvas, text) > needed_string_width) {\n        canvas_draw_str(canvas, start_pos, 22, \"...\");\n        start_pos += 6;\n        needed_string_width -= 8;\n    }\n\n    while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {\n        text++;\n    }\n\n    if(model->clear_default_text) {\n        elements_slightly_rounded_box(\n            canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);\n        canvas_set_color(canvas, ColorWhite);\n    } else {\n        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, \"|\");\n        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, \"|\");\n    }\n    canvas_draw_str(canvas, start_pos, 22, text);\n\n    canvas_set_font(canvas, FontKeyboard);\n\n    for(uint8_t row = 0; row < keyboard_row_count; row++) {\n        const uint8_t column_count = get_row_size(row);\n        const TextInputKey* keys = get_row(row);\n\n        for(size_t column = 0; column < column_count; column++) {\n            if(keys[column].text == ENTER_KEY) {\n                canvas_set_color(canvas, ColorBlack);\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeySaveSelected_24x11);\n                } else {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeySave_24x11);\n                }\n            } else if(keys[column].text == BACKSPACE_KEY) {\n                canvas_set_color(canvas, ColorBlack);\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeyBackspaceSelected_16x9);\n                } else {\n                    canvas_draw_icon(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        &I_KeyBackspace_16x9);\n                }\n            } else {\n                if(model->selected_row == row && model->selected_column == column) {\n                    canvas_set_color(canvas, ColorBlack);\n                    canvas_draw_box(\n                        canvas,\n                        keyboard_origin_x + keys[column].x - 1,\n                        keyboard_origin_y + keys[column].y - 8,\n                        7,\n                        10);\n                    canvas_set_color(canvas, ColorWhite);\n                } else {\n                    canvas_set_color(canvas, ColorBlack);\n                }\n\n                if(model->clear_default_text ||\n                   (text_length == 0 && char_is_lowercase(keys[column].text))) {\n                    canvas_draw_glyph(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        char_to_uppercase(keys[column].text));\n                } else {\n                    canvas_draw_glyph(\n                        canvas,\n                        keyboard_origin_x + keys[column].x,\n                        keyboard_origin_y + keys[column].y,\n                        keys[column].text);\n                }\n            }\n        }\n    }\n    if(model->validator_message_visible) {\n        canvas_set_font(canvas, FontSecondary);\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, 8, 10, 110, 48);\n        canvas_set_color(canvas, ColorBlack);\n        canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);\n        canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);\n        canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);\n        elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));\n        canvas_set_font(canvas, FontKeyboard);\n    }\n}\n\nstatic void text_input_handle_up(TextInput* text_input, TextInputModel* model) {\n    UNUSED(text_input);\n    if(model->selected_row > 0) {\n        model->selected_row--;\n        if(model->selected_column > get_row_size(model->selected_row) - 6) {\n            model->selected_column = model->selected_column + 1;\n        }\n    }\n}\n\nstatic void text_input_handle_down(TextInput* text_input, TextInputModel* model) {\n    UNUSED(text_input);\n    if(model->selected_row < keyboard_row_count - 1) {\n        model->selected_row++;\n        if(model->selected_column > get_row_size(model->selected_row) - 4) {\n            model->selected_column = model->selected_column - 1;\n        }\n    }\n}\n\nstatic void text_input_handle_left(TextInput* text_input, TextInputModel* model) {\n    UNUSED(text_input);\n    if(model->selected_column > 0) {\n        model->selected_column--;\n    } else {\n        model->selected_column = get_row_size(model->selected_row) - 1;\n    }\n}\n\nstatic void text_input_handle_right(TextInput* text_input, TextInputModel* model) {\n    UNUSED(text_input);\n    if(model->selected_column < get_row_size(model->selected_row) - 1) {\n        model->selected_column++;\n    } else {\n        model->selected_column = 0;\n    }\n}\n\nstatic void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) {\n    char selected = get_selected_char(model);\n    size_t text_length = strlen(model->text_buffer);\n\n    bool toggle_case = text_length == 0 || model->clear_default_text;\n    if(shift) toggle_case = !toggle_case;\n    if(toggle_case) {\n        selected = char_to_uppercase(selected);\n    }\n\n    if(selected == ENTER_KEY) {\n        if(model->validator_callback &&\n           (!model->validator_callback(\n               model->text_buffer, model->validator_text, model->validator_callback_context))) {\n            model->validator_message_visible = true;\n            furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4);\n        } else if(model->callback != 0 && text_length >= model->minimum_length) {\n            model->callback(model->callback_context);\n        }\n    } else if(selected == BACKSPACE_KEY) {\n        text_input_backspace_cb(model);\n    } else {\n        if(model->clear_default_text) {\n            text_length = 0;\n        }\n        if(text_length < (model->text_buffer_size - 1)) {\n            model->text_buffer[text_length] = selected;\n            model->text_buffer[text_length + 1] = 0;\n        }\n    }\n    model->clear_default_text = false;\n}\n\nstatic bool text_input_view_input_callback(InputEvent* event, void* context) {\n    TextInput* text_input = context;\n    furi_assert(text_input);\n\n    bool consumed = false;\n\n    // Acquire model\n    TextInputModel* model = view_get_model(text_input->view);\n\n    if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&\n       model->validator_message_visible) {\n        model->validator_message_visible = false;\n        consumed = true;\n    } else if(event->type == InputTypeShort) {\n        consumed = true;\n        switch(event->key) {\n        case InputKeyUp:\n            text_input_handle_up(text_input, model);\n            break;\n        case InputKeyDown:\n            text_input_handle_down(text_input, model);\n            break;\n        case InputKeyLeft:\n            text_input_handle_left(text_input, model);\n            break;\n        case InputKeyRight:\n            text_input_handle_right(text_input, model);\n            break;\n        case InputKeyOk:\n            text_input_handle_ok(text_input, model, false);\n            break;\n        default:\n            consumed = false;\n            break;\n        }\n    } else if(event->type == InputTypeLong) {\n        consumed = true;\n        switch(event->key) {\n        case InputKeyUp:\n            text_input_handle_up(text_input, model);\n            break;\n        case InputKeyDown:\n            text_input_handle_down(text_input, model);\n            break;\n        case InputKeyLeft:\n            text_input_handle_left(text_input, model);\n            break;\n        case InputKeyRight:\n            text_input_handle_right(text_input, model);\n            break;\n        case InputKeyOk:\n            text_input_handle_ok(text_input, model, true);\n            break;\n        case InputKeyBack:\n            text_input_backspace_cb(model);\n            break;\n        default:\n            consumed = false;\n            break;\n        }\n    } else if(event->type == InputTypeRepeat) {\n        consumed = true;\n        switch(event->key) {\n        case InputKeyUp:\n            text_input_handle_up(text_input, model);\n            break;\n        case InputKeyDown:\n            text_input_handle_down(text_input, model);\n            break;\n        case InputKeyLeft:\n            text_input_handle_left(text_input, model);\n            break;\n        case InputKeyRight:\n            text_input_handle_right(text_input, model);\n            break;\n        case InputKeyBack:\n            text_input_backspace_cb(model);\n            break;\n        default:\n            consumed = false;\n            break;\n        }\n    }\n\n    // Commit model\n    view_commit_model(text_input->view, consumed);\n\n    return consumed;\n}\n\nvoid text_input_timer_callback(void* context) {\n    furi_assert(context);\n    TextInput* text_input = context;\n\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { model->validator_message_visible = false; },\n        true);\n}\n\nTextInput* text_input_alloc(void) {\n    TextInput* text_input = malloc(sizeof(TextInput));\n    text_input->view = view_alloc();\n    view_set_context(text_input->view, text_input);\n    view_allocate_model(text_input->view, ViewModelTypeLocking, sizeof(TextInputModel));\n    view_set_draw_callback(text_input->view, text_input_view_draw_callback);\n    view_set_input_callback(text_input->view, text_input_view_input_callback);\n\n    text_input->timer = furi_timer_alloc(text_input_timer_callback, FuriTimerTypeOnce, text_input);\n\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { model->validator_text = furi_string_alloc(); },\n        false);\n\n    text_input_reset(text_input);\n\n    return text_input;\n}\n\nvoid text_input_free(TextInput* text_input) {\n    furi_check(text_input);\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { furi_string_free(model->validator_text); },\n        false);\n\n    // Send stop command\n    furi_timer_stop(text_input->timer);\n    // Release allocated memory\n    furi_timer_free(text_input->timer);\n\n    view_free(text_input->view);\n\n    free(text_input);\n}\n\nvoid text_input_reset(TextInput* text_input) {\n    furi_check(text_input);\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        {\n            model->header = \"\";\n            model->selected_row = 0;\n            model->selected_column = 0;\n            model->minimum_length = 1;\n            model->clear_default_text = false;\n            model->text_buffer = NULL;\n            model->text_buffer_size = 0;\n            model->callback = NULL;\n            model->callback_context = NULL;\n            model->validator_callback = NULL;\n            model->validator_callback_context = NULL;\n            furi_string_reset(model->validator_text);\n            model->validator_message_visible = false;\n        },\n        true);\n}\n\nView* text_input_get_view(TextInput* text_input) {\n    furi_check(text_input);\n    return text_input->view;\n}\n\nvoid text_input_set_result_callback(\n    TextInput* text_input,\n    TextInputCallback callback,\n    void* callback_context,\n    char* text_buffer,\n    size_t text_buffer_size,\n    bool clear_default_text) {\n    furi_check(text_input);\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        {\n            model->callback = callback;\n            model->callback_context = callback_context;\n            model->text_buffer = text_buffer;\n            model->text_buffer_size = text_buffer_size;\n            model->clear_default_text = clear_default_text;\n            if(text_buffer && text_buffer[0] != '\\0') {\n                // Set focus on Save\n                model->selected_row = 2;\n                model->selected_column = 8;\n            }\n        },\n        true);\n}\n\nvoid text_input_set_minimum_length(TextInput* text_input, size_t minimum_length) {\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { model->minimum_length = minimum_length; },\n        true);\n}\n\nvoid text_input_set_validator(\n    TextInput* text_input,\n    TextInputValidatorCallback callback,\n    void* callback_context) {\n    furi_check(text_input);\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        {\n            model->validator_callback = callback;\n            model->validator_callback_context = callback_context;\n        },\n        true);\n}\n\nTextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input) {\n    furi_check(text_input);\n    TextInputValidatorCallback validator_callback = NULL;\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { validator_callback = model->validator_callback; },\n        false);\n    return validator_callback;\n}\n\nvoid* text_input_get_validator_callback_context(TextInput* text_input) {\n    furi_check(text_input);\n    void* validator_callback_context = NULL;\n    with_view_model(\n        text_input->view,\n        TextInputModel * model,\n        { validator_callback_context = model->validator_callback_context; },\n        false);\n    return validator_callback_context;\n}\n\nvoid text_input_set_header_text(TextInput* text_input, const char* text) {\n    furi_check(text_input);\n    with_view_model(text_input->view, TextInputModel * model, { model->header = text; }, true);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/text_input.h",
    "content": "/**\n * @file text_input.h\n * GUI: TextInput keyboard view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n#include \"validators.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Text input anonymous structure */\ntypedef struct TextInput TextInput;\ntypedef void (*TextInputCallback)(void* context);\ntypedef bool (*TextInputValidatorCallback)(const char* text, FuriString* error, void* context);\n\n/** Allocate and initialize text input \n * \n * This text input is used to enter string\n *\n * @return     TextInput instance\n */\nTextInput* text_input_alloc(void);\n\n/** Deinitialize and free text input\n *\n * @param      text_input  TextInput instance\n */\nvoid text_input_free(TextInput* text_input);\n\n/** Clean text input view Note: this function does not free memory\n *\n * @param      text_input  Text input instance\n */\nvoid text_input_reset(TextInput* text_input);\n\n/** Get text input view\n *\n * @param      text_input  TextInput instance\n *\n * @return     View instance that can be used for embedding\n */\nView* text_input_get_view(TextInput* text_input);\n\n/** Set text input result callback\n *\n * @param      text_input          TextInput instance\n * @param      callback            callback fn\n * @param      callback_context    callback context\n * @param      text_buffer         pointer to YOUR text buffer, that we going\n *                                 to modify\n * @param      text_buffer_size    YOUR text buffer size in bytes. Max string\n *                                 length will be text_buffer_size-1.\n * @param      clear_default_text  clear text from text_buffer on first OK\n *                                 event\n */\nvoid text_input_set_result_callback(\n    TextInput* text_input,\n    TextInputCallback callback,\n    void* callback_context,\n    char* text_buffer,\n    size_t text_buffer_size,\n    bool clear_default_text);\n\n/**\n * @brief Sets the minimum length of a TextInput\n * @param [in] text_input TextInput\n * @param [in] minimum_length Minimum input length\n */\nvoid text_input_set_minimum_length(TextInput* text_input, size_t minimum_length);\n\nvoid text_input_set_validator(\n    TextInput* text_input,\n    TextInputValidatorCallback callback,\n    void* callback_context);\n\nTextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input);\n\nvoid* text_input_get_validator_callback_context(TextInput* text_input);\n\n/** Set text input header text\n *\n * @param      text_input  TextInput instance\n * @param      text        text to be shown\n */\nvoid text_input_set_header_text(TextInput* text_input, const char* text);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/validators.c",
    "content": "#include \"validators.h\"\n#include <storage/storage.h>\n#include <furi.h>\n\nstruct ValidatorIsFile {\n    char* app_path_folder;\n    const char* app_extension;\n    char* current_name;\n};\n\nbool validator_is_file_callback(const char* text, FuriString* error, void* context) {\n    furi_check(context);\n    ValidatorIsFile* instance = context;\n\n    if(instance->current_name != NULL) {\n        if(strcmp(instance->current_name, text) == 0) {\n            return true;\n        }\n    }\n\n    FuriString* path = furi_string_alloc_printf(\n        \"%s/%s%s\", instance->app_path_folder, text, instance->app_extension);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    const bool ret = storage_common_stat(storage, furi_string_get_cstr(path), NULL) != FSE_OK;\n    if(!ret) {\n        furi_string_printf(error, \"This name\\nexists!\\nChoose\\nanother one.\");\n    }\n    furi_string_free(path);\n    furi_record_close(RECORD_STORAGE);\n\n    return ret;\n}\n\nValidatorIsFile* validator_is_file_alloc_init(\n    const char* app_path_folder,\n    const char* app_extension,\n    const char* current_name) {\n    ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile));\n\n    instance->app_path_folder = strdup(app_path_folder);\n    instance->app_extension = app_extension;\n    if(current_name != NULL) {\n        instance->current_name = strdup(current_name);\n    }\n\n    return instance;\n}\n\nvoid validator_is_file_free(ValidatorIsFile* instance) {\n    furi_check(instance);\n    free(instance->app_path_folder);\n    free(instance->current_name);\n    free(instance);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/validators.h",
    "content": "#pragma once\n\n#include <core/common_defines.h>\n#include <core/string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\ntypedef struct ValidatorIsFile ValidatorIsFile;\n\nValidatorIsFile* validator_is_file_alloc_init(\n    const char* app_path_folder,\n    const char* app_extension,\n    const char* current_name);\n\nvoid validator_is_file_free(ValidatorIsFile* instance);\n\nbool validator_is_file_callback(const char* text, FuriString* error, void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/variable_item_list.c",
    "content": "#include \"variable_item_list.h\"\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <furi.h>\n#include <m-array.h>\n#include <stdint.h>\n\nstruct VariableItem {\n    const char* label;\n    uint8_t current_value_index;\n    FuriString* current_value_text;\n    uint8_t values_count;\n    VariableItemChangeCallback change_callback;\n    void* context;\n};\n\nARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST); //-V658\n\nstruct VariableItemList {\n    View* view;\n    VariableItemListEnterCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    VariableItemArray_t items;\n    uint8_t position;\n    uint8_t window_position;\n} VariableItemListModel;\n\nstatic void variable_item_list_process_up(VariableItemList* variable_item_list);\nstatic void variable_item_list_process_down(VariableItemList* variable_item_list);\nstatic void variable_item_list_process_left(VariableItemList* variable_item_list);\nstatic void variable_item_list_process_right(VariableItemList* variable_item_list);\nstatic void variable_item_list_process_ok(VariableItemList* variable_item_list);\n\nstatic void variable_item_list_draw_callback(Canvas* canvas, void* _model) {\n    VariableItemListModel* model = _model;\n\n    const uint8_t item_height = 16;\n    const uint8_t item_width = 123;\n\n    canvas_clear(canvas);\n\n    uint8_t position = 0;\n    VariableItemArray_it_t it;\n\n    canvas_set_font(canvas, FontSecondary);\n    for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);\n        VariableItemArray_next(it)) {\n        uint8_t item_position = position - model->window_position;\n        uint8_t items_on_screen = 4;\n        uint8_t y_offset = 0;\n\n        if(item_position < items_on_screen) {\n            const VariableItem* item = VariableItemArray_cref(it);\n            uint8_t item_y = y_offset + (item_position * item_height);\n            uint8_t item_text_y = item_y + item_height - 4;\n\n            if(position == model->position) {\n                canvas_set_color(canvas, ColorBlack);\n                elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2);\n                canvas_set_color(canvas, ColorWhite);\n            } else {\n                canvas_set_color(canvas, ColorBlack);\n            }\n\n            canvas_draw_str(canvas, 6, item_text_y, item->label);\n\n            if(item->current_value_index > 0) {\n                canvas_draw_str(canvas, 73, item_text_y, \"<\");\n            }\n\n            canvas_draw_str_aligned(\n                canvas,\n                (115 + 73) / 2 + 1,\n                item_text_y,\n                AlignCenter,\n                AlignBottom,\n                furi_string_get_cstr(item->current_value_text));\n\n            if(item->current_value_index < (item->values_count - 1)) {\n                canvas_draw_str(canvas, 115, item_text_y, \">\");\n            }\n        }\n\n        position++;\n    }\n\n    elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));\n}\n\nvoid variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) {\n    furi_check(variable_item_list);\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            uint8_t position = index;\n            if(position >= VariableItemArray_size(model->items)) {\n                position = 0;\n            }\n\n            model->position = position;\n            model->window_position = position;\n\n            if(model->window_position > 0) {\n                model->window_position -= 1;\n            }\n\n            if(VariableItemArray_size(model->items) <= 4) {\n                model->window_position = 0;\n            } else {\n                if(model->window_position >= (VariableItemArray_size(model->items) - 4)) {\n                    model->window_position = (VariableItemArray_size(model->items) - 4);\n                }\n            }\n        },\n        true);\n}\n\nuint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) {\n    furi_check(variable_item_list);\n    VariableItemListModel* model = view_get_model(variable_item_list->view);\n    uint8_t idx = model->position;\n    view_commit_model(variable_item_list->view, false);\n    return idx;\n}\n\nstatic bool variable_item_list_input_callback(InputEvent* event, void* context) {\n    VariableItemList* variable_item_list = context;\n    furi_assert(variable_item_list);\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            variable_item_list_process_up(variable_item_list);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            variable_item_list_process_down(variable_item_list);\n            break;\n        case InputKeyLeft:\n            consumed = true;\n            variable_item_list_process_left(variable_item_list);\n            break;\n        case InputKeyRight:\n            consumed = true;\n            variable_item_list_process_right(variable_item_list);\n            break;\n        case InputKeyOk:\n            variable_item_list_process_ok(variable_item_list);\n            break;\n        default:\n            break;\n        }\n    } else if(event->type == InputTypeRepeat) {\n        switch(event->key) {\n        case InputKeyUp:\n            consumed = true;\n            variable_item_list_process_up(variable_item_list);\n            break;\n        case InputKeyDown:\n            consumed = true;\n            variable_item_list_process_down(variable_item_list);\n            break;\n        case InputKeyLeft:\n            consumed = true;\n            variable_item_list_process_left(variable_item_list);\n            break;\n        case InputKeyRight:\n            consumed = true;\n            variable_item_list_process_right(variable_item_list);\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid variable_item_list_process_up(VariableItemList* variable_item_list) {\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            uint8_t items_on_screen = 4;\n            if(model->position > 0) {\n                model->position--;\n\n                if((model->position == model->window_position) && (model->window_position > 0)) {\n                    model->window_position--;\n                }\n            } else {\n                model->position = VariableItemArray_size(model->items) - 1;\n                if(model->position > (items_on_screen - 1)) {\n                    model->window_position = model->position - (items_on_screen - 1);\n                }\n            }\n        },\n        true);\n}\n\nvoid variable_item_list_process_down(VariableItemList* variable_item_list) {\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            uint8_t items_on_screen = 4;\n            if(model->position < (VariableItemArray_size(model->items) - 1)) {\n                model->position++;\n                if((model->position - model->window_position) > (items_on_screen - 2) &&\n                   model->window_position <\n                       (VariableItemArray_size(model->items) - items_on_screen)) {\n                    model->window_position++;\n                }\n            } else {\n                model->position = 0;\n                model->window_position = 0;\n            }\n        },\n        true);\n}\n\nVariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) {\n    VariableItem* item = NULL;\n\n    VariableItemArray_it_t it;\n    uint8_t position = 0;\n    for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);\n        VariableItemArray_next(it)) {\n        if(position == model->position) {\n            break;\n        }\n        position++;\n    }\n\n    item = VariableItemArray_ref(it);\n\n    furi_assert(item);\n    return item;\n}\n\nvoid variable_item_list_process_left(VariableItemList* variable_item_list) {\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            VariableItem* item = variable_item_list_get_selected_item(model);\n            if(item->current_value_index > 0) {\n                item->current_value_index--;\n                if(item->change_callback) {\n                    item->change_callback(item);\n                }\n            }\n        },\n        true);\n}\n\nvoid variable_item_list_process_right(VariableItemList* variable_item_list) {\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            VariableItem* item = variable_item_list_get_selected_item(model);\n            if(item->current_value_index < (item->values_count - 1)) {\n                item->current_value_index++;\n                if(item->change_callback) {\n                    item->change_callback(item);\n                }\n            }\n        },\n        true);\n}\n\nvoid variable_item_list_process_ok(VariableItemList* variable_item_list) {\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            if(variable_item_list->callback) {\n                variable_item_list->callback(variable_item_list->context, model->position);\n            }\n        },\n        false);\n}\n\nVariableItemList* variable_item_list_alloc(void) {\n    VariableItemList* variable_item_list = malloc(sizeof(VariableItemList));\n    variable_item_list->view = view_alloc();\n    view_set_context(variable_item_list->view, variable_item_list);\n    view_allocate_model(\n        variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListModel));\n    view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);\n    view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);\n\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            VariableItemArray_init(model->items);\n            model->position = 0;\n            model->window_position = 0;\n        },\n        true);\n\n    return variable_item_list;\n}\n\nvoid variable_item_list_free(VariableItemList* variable_item_list) {\n    furi_check(variable_item_list);\n\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            VariableItemArray_it_t it;\n            for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);\n                VariableItemArray_next(it)) {\n                furi_string_free(VariableItemArray_ref(it)->current_value_text);\n            }\n            VariableItemArray_clear(model->items);\n        },\n        false);\n    view_free(variable_item_list->view);\n    free(variable_item_list);\n}\n\nvoid variable_item_list_reset(VariableItemList* variable_item_list) {\n    furi_check(variable_item_list);\n\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            VariableItemArray_it_t it;\n            for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);\n                VariableItemArray_next(it)) {\n                furi_string_free(VariableItemArray_ref(it)->current_value_text);\n            }\n            VariableItemArray_reset(model->items);\n        },\n        false);\n}\n\nView* variable_item_list_get_view(VariableItemList* variable_item_list) {\n    furi_check(variable_item_list);\n    return variable_item_list->view;\n}\n\nVariableItem* variable_item_list_add(\n    VariableItemList* variable_item_list,\n    const char* label,\n    uint8_t values_count,\n    VariableItemChangeCallback change_callback,\n    void* context) {\n    VariableItem* item = NULL;\n    furi_check(label);\n    furi_check(variable_item_list);\n\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            item = VariableItemArray_push_new(model->items);\n            item->label = label;\n            item->values_count = values_count;\n            item->change_callback = change_callback;\n            item->context = context;\n            item->current_value_index = 0;\n            item->current_value_text = furi_string_alloc();\n        },\n        true);\n\n    return item;\n}\n\nvoid variable_item_list_set_enter_callback(\n    VariableItemList* variable_item_list,\n    VariableItemListEnterCallback callback,\n    void* context) {\n    furi_check(callback);\n    with_view_model(\n        variable_item_list->view,\n        VariableItemListModel * model,\n        {\n            UNUSED(model);\n            variable_item_list->callback = callback;\n            variable_item_list->context = context;\n        },\n        false);\n}\n\nvoid variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {\n    furi_check(item);\n    item->current_value_index = current_value_index;\n}\n\nvoid variable_item_set_values_count(VariableItem* item, uint8_t values_count) {\n    furi_check(item);\n    item->values_count = values_count;\n}\n\nvoid variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {\n    furi_check(item);\n    furi_string_set(item->current_value_text, current_value_text);\n}\n\nuint8_t variable_item_get_current_value_index(VariableItem* item) {\n    furi_check(item);\n    return item->current_value_index;\n}\n\nvoid* variable_item_get_context(VariableItem* item) {\n    furi_check(item);\n    return item->context;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/variable_item_list.h",
    "content": "/**\n * @file variable_item_list.h\n * GUI: VariableItemList view module API\n */\n\n#pragma once\n\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct VariableItemList VariableItemList;\ntypedef struct VariableItem VariableItem;\ntypedef void (*VariableItemChangeCallback)(VariableItem* item);\ntypedef void (*VariableItemListEnterCallback)(void* context, uint32_t index);\n\n/** Allocate and initialize VariableItemList\n *\n * @return     VariableItemList*\n */\nVariableItemList* variable_item_list_alloc(void);\n\n/** Deinitialize and free VariableItemList\n *\n * @param      variable_item_list  VariableItemList instance\n */\nvoid variable_item_list_free(VariableItemList* variable_item_list);\n\n/** Clear all elements from list\n *\n * @param      variable_item_list  VariableItemList instance\n */\nvoid variable_item_list_reset(VariableItemList* variable_item_list);\n\n/** Get VariableItemList View instance\n *\n * @param      variable_item_list  VariableItemList instance\n *\n * @return     View instance\n */\nView* variable_item_list_get_view(VariableItemList* variable_item_list);\n\n/** Add item to VariableItemList\n *\n * @param      variable_item_list  VariableItemList instance\n * @param      label               item name\n * @param      values_count        item values count\n * @param      change_callback     called on value change in gui\n * @param      context             item context\n *\n * @return     VariableItem* item instance\n */\nVariableItem* variable_item_list_add(\n    VariableItemList* variable_item_list,\n    const char* label,\n    uint8_t values_count,\n    VariableItemChangeCallback change_callback,\n    void* context);\n\n/** Set enter callback\n *\n * @param      variable_item_list  VariableItemList instance\n * @param      callback            VariableItemListEnterCallback instance\n * @param      context             pointer to context\n */\nvoid variable_item_list_set_enter_callback(\n    VariableItemList* variable_item_list,\n    VariableItemListEnterCallback callback,\n    void* context);\n\nvoid variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index);\n\nuint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list);\n\n/** Set item current selected index\n *\n * @param      item                 VariableItem* instance\n * @param      current_value_index  The current value index\n */\nvoid variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index);\n\n/** Set number of values for item\n *\n * @param      item                 VariableItem* instance\n * @param      values_count         The new values count\n */\nvoid variable_item_set_values_count(VariableItem* item, uint8_t values_count);\n\n/** Set item current selected text\n *\n * @param      item                VariableItem* instance\n * @param      current_value_text  The current value text\n */\nvoid variable_item_set_current_value_text(VariableItem* item, const char* current_value_text);\n\n/** Get item current selected index\n *\n * @param      item  VariableItem* instance\n *\n * @return     uint8_t current selected index\n */\nuint8_t variable_item_get_current_value_index(VariableItem* item);\n\n/** Get item context\n *\n * @param      item  VariableItem* instance\n *\n * @return     void* item context\n */\nvoid* variable_item_get_context(VariableItem* item);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/widget.c",
    "content": "#include \"widget.h\"\n#include \"widget_elements/widget_element_i.h\"\n#include <furi.h>\n#include <m-array.h>\n\nARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); // NOLINT\n\nstruct Widget {\n    View* view;\n    void* context;\n};\n\ntypedef struct {\n    ElementArray_t element;\n} GuiWidgetModel;\n\nstatic void gui_widget_view_draw_callback(Canvas* canvas, void* _model) {\n    GuiWidgetModel* model = _model;\n    canvas_clear(canvas);\n\n    // Draw all elements\n    ElementArray_it_t it;\n    ElementArray_it(it, model->element);\n    while(!ElementArray_end_p(it)) {\n        WidgetElement* element = *ElementArray_ref(it);\n        if(element->draw != NULL) {\n            element->draw(canvas, element);\n        }\n        ElementArray_next(it);\n    }\n}\n\nstatic bool gui_widget_view_input_callback(InputEvent* event, void* context) {\n    Widget* widget = context;\n    bool consumed = false;\n\n    // Call all Widget Elements input handlers\n    with_view_model(\n        widget->view,\n        GuiWidgetModel * model,\n        {\n            ElementArray_it_t it;\n            ElementArray_it(it, model->element);\n            while(!ElementArray_end_p(it)) {\n                WidgetElement* element = *ElementArray_ref(it);\n                if(element->input != NULL) {\n                    consumed |= element->input(event, element);\n                }\n                ElementArray_next(it);\n            }\n        },\n        true);\n\n    return consumed;\n}\n\nWidget* widget_alloc(void) {\n    Widget* widget = malloc(sizeof(Widget));\n    widget->view = view_alloc();\n    view_set_context(widget->view, widget);\n    view_allocate_model(widget->view, ViewModelTypeLocking, sizeof(GuiWidgetModel));\n    view_set_draw_callback(widget->view, gui_widget_view_draw_callback);\n    view_set_input_callback(widget->view, gui_widget_view_input_callback);\n\n    with_view_model(\n        widget->view, GuiWidgetModel * model, { ElementArray_init(model->element); }, true);\n\n    return widget;\n}\n\nvoid widget_reset(Widget* widget) {\n    furi_check(widget);\n\n    with_view_model(\n        widget->view,\n        GuiWidgetModel * model,\n        {\n            ElementArray_it_t it;\n            ElementArray_it(it, model->element);\n            while(!ElementArray_end_p(it)) {\n                WidgetElement* element = *ElementArray_ref(it);\n                furi_assert(element->free);\n                element->free(element);\n                ElementArray_next(it);\n            }\n            ElementArray_reset(model->element);\n        },\n        true);\n}\n\nvoid widget_free(Widget* widget) {\n    furi_check(widget);\n    // Free all elements\n    widget_reset(widget);\n    // Free elements container\n    with_view_model(\n        widget->view, GuiWidgetModel * model, { ElementArray_clear(model->element); }, true);\n\n    view_free(widget->view);\n    free(widget);\n}\n\nView* widget_get_view(Widget* widget) {\n    furi_check(widget);\n    return widget->view;\n}\n\nstatic void widget_add_element(Widget* widget, WidgetElement* element) {\n    furi_assert(widget);\n    furi_assert(element);\n\n    with_view_model(\n        widget->view,\n        GuiWidgetModel * model,\n        {\n            element->parent = widget;\n            ElementArray_push_back(model->element, element);\n        },\n        true);\n}\n\nvoid widget_add_string_multiline_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text) {\n    furi_check(widget);\n    WidgetElement* string_multiline_element =\n        widget_element_string_multiline_create(x, y, horizontal, vertical, font, text);\n    widget_add_element(widget, string_multiline_element);\n}\n\nvoid widget_add_string_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text) {\n    furi_check(widget);\n    WidgetElement* string_element =\n        widget_element_string_create(x, y, horizontal, vertical, font, text);\n    widget_add_element(widget, string_element);\n}\n\nvoid widget_add_text_box_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots) {\n    furi_check(widget);\n    WidgetElement* text_box_element = widget_element_text_box_create(\n        x, y, width, height, horizontal, vertical, text, strip_to_dots);\n    widget_add_element(widget, text_box_element);\n}\n\nvoid widget_add_text_scroll_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    const char* text) {\n    furi_check(widget);\n    WidgetElement* text_scroll_element =\n        widget_element_text_scroll_create(x, y, width, height, text);\n    widget_add_element(widget, text_scroll_element);\n}\n\nvoid widget_add_button_element(\n    Widget* widget,\n    GuiButtonType button_type,\n    const char* text,\n    ButtonCallback callback,\n    void* context) {\n    furi_check(widget);\n    WidgetElement* button_element =\n        widget_element_button_create(button_type, text, callback, context);\n    widget_add_element(widget, button_element);\n}\n\nvoid widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) {\n    furi_check(widget);\n    furi_check(icon);\n    WidgetElement* icon_element = widget_element_icon_create(x, y, icon);\n    widget_add_element(widget, icon_element);\n}\n\nvoid widget_add_rect_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    uint8_t radius,\n    bool fill) {\n    furi_check(widget);\n    WidgetElement* rect_element = widget_element_rect_create(x, y, width, height, radius, fill);\n    widget_add_element(widget, rect_element);\n}\n\nvoid widget_add_circle_element(Widget* widget, uint8_t x, uint8_t y, uint8_t radius, bool fill) {\n    furi_check(widget);\n    WidgetElement* circle_element = widget_element_circle_create(x, y, radius, fill);\n    widget_add_element(widget, circle_element);\n}\n\nvoid widget_add_line_element(Widget* widget, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {\n    furi_check(widget);\n    WidgetElement* line_element = widget_element_line_create(x1, y1, x2, y2);\n    widget_add_element(widget, line_element);\n}\n"
  },
  {
    "path": "applications/services/gui/modules/widget.h",
    "content": "/**\n * @file widget.h\n * GUI: Widget view module API\n */\n\n#pragma once\n#include <gui/view.h>\n#include \"widget_elements/widget_element.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Widget Widget;\ntypedef struct WidgetElement WidgetElement;\n\n/** Allocate Widget that holds Widget Elements\n *\n * @return     Widget instance\n */\nWidget* widget_alloc(void);\n\n/** Free Widget\n * @note       this function free allocated Widget Elements\n *\n * @param      widget  Widget instance\n */\nvoid widget_free(Widget* widget);\n\n/** Reset Widget\n *\n * @param      widget  Widget instance\n */\nvoid widget_reset(Widget* widget);\n\n/** Get Widget view\n *\n * @param      widget  Widget instance\n *\n * @return     View instance\n */\nView* widget_get_view(Widget* widget);\n\n/** Add Multi String Element\n *\n * @param      widget      Widget instance\n * @param      x           x coordinate\n * @param      y           y coordinate\n * @param      horizontal  Align instance\n * @param      vertical    Align instance\n * @param      font        Font instance\n * @param[in]  text        The text\n */\nvoid widget_add_string_multiline_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text);\n\n/** Add String Element\n *\n * @param      widget      Widget instance\n * @param      x           x coordinate\n * @param      y           y coordinate\n * @param      horizontal  Align instance\n * @param      vertical    Align instance\n * @param      font        Font instance\n * @param[in]  text        The text\n */\nvoid widget_add_string_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text);\n\n/** Add Text Box Element\n *\n * @param      widget           Widget instance\n * @param      x                x coordinate\n * @param      y                y coordinate\n * @param      width            width to fit text\n * @param      height           height to fit text\n * @param      horizontal       Align instance\n * @param      vertical         Align instance\n * @param[in]  text             Formatted text. The following formats are available:\n *                               \"\\e#Bold text\\e#\" - bold font is used\n *                               \"\\e*Monospaced text\\e*\" - monospaced font is used\n *                               \"\\e!Inversed text\\e!\" - white text on black background\n * @param      strip_to_dots    Strip text to ... if does not fit to width\n */\nvoid widget_add_text_box_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots);\n\n/** Add Text Scroll Element\n *\n * @param      widget           Widget instance\n * @param      x                x coordinate\n * @param      y                y coordinate\n * @param      width            width to fit text\n * @param      height           height to fit text\n * @param[in]  text             Formatted text. Default format: align left, Secondary font.\n *                              The following formats are available:\n *                               \"\\e#Bold text\" - sets bold font before until next '\\n' symbol\n *                               \"\\e*Monospaced text\\e*\" - sets monospaced font before until next '\\n' symbol\n *                               \"\\ecCenter-aligned text\" - sets center horizontal align until the next '\\n' symbol\n *                               \"\\erRight-aligned text\" - sets right horizontal align until the next '\\n' symbol\n */\nvoid widget_add_text_scroll_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    const char* text);\n\n/** Add Button Element\n *\n * @param      widget       Widget instance\n * @param      button_type  GuiButtonType instance\n * @param      text         text on allocated button\n * @param      callback     ButtonCallback instance\n * @param      context      pointer to context\n */\nvoid widget_add_button_element(\n    Widget* widget,\n    GuiButtonType button_type,\n    const char* text,\n    ButtonCallback callback,\n    void* context);\n\n/** Add Icon Element\n *\n * @param      widget  Widget instance\n * @param      x       top left x coordinate\n * @param      y       top left y coordinate\n * @param      icon    Icon instance\n */\nvoid widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon);\n\n/** Add Frame Element\n  *\n  * @param      widget  Widget instance\n  * @param      x       top left x coordinate\n  * @param      y       top left y coordinate\n  * @param      width   frame width\n  * @param      height  frame height\n  * @param      radius  frame radius\n  * \n  * @warning deprecated, use widget_add_rect_element instead\n  */\n#define widget_add_frame_element(widget, x, y, width, height, radius) \\\n    widget_add_rect_element((widget), (x), (y), (width), (height), (radius), false)\n\n/** Add Rect Element\n *\n * @param      widget  Widget instance\n * @param      x       top left x coordinate\n * @param      y       top left y coordinate\n * @param      width   rect width\n * @param      height  rect height\n * @param      radius  corner radius\n * @param      fill    whether to fill the box or not\n */\nvoid widget_add_rect_element(\n    Widget* widget,\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    uint8_t radius,\n    bool fill);\n\n/** Add Circle Element\n *\n * @param      widget  Widget instance\n * @param      x       center x coordinate\n * @param      y       center y coordinate\n * @param      radius  circle radius\n * @param      fill    whether to fill the circle or not\n */\nvoid widget_add_circle_element(Widget* widget, uint8_t x, uint8_t y, uint8_t radius, bool fill);\n\n/** Add Line Element\n *\n * @param      widget  Widget instance\n * @param      x1      first x coordinate\n * @param      y1      first y coordinate\n * @param      x2      second x coordinate\n * @param      y2      second y coordinate\n */\nvoid widget_add_line_element(Widget* widget, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element.h",
    "content": "/**\n * @file widget_element_i.h\n * GUI: internal Widget Element API\n */\n\n#pragma once\n\n#include <input/input.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    GuiButtonTypeLeft,\n    GuiButtonTypeCenter,\n    GuiButtonTypeRight,\n} GuiButtonType;\n\ntypedef void (*ButtonCallback)(GuiButtonType result, InputType type, void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_button.c",
    "content": "#include \"widget_element_i.h\"\n#include <gui/elements.h>\n\ntypedef struct {\n    GuiButtonType button_type;\n    FuriString* text;\n    ButtonCallback callback;\n    void* context;\n} GuiButtonModel;\n\nstatic void gui_button_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiButtonModel* model = element->model;\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontSecondary);\n\n    if(model->button_type == GuiButtonTypeLeft) {\n        elements_button_left(canvas, furi_string_get_cstr(model->text));\n    } else if(model->button_type == GuiButtonTypeRight) {\n        elements_button_right(canvas, furi_string_get_cstr(model->text));\n    } else if(model->button_type == GuiButtonTypeCenter) {\n        elements_button_center(canvas, furi_string_get_cstr(model->text));\n    }\n}\n\nstatic bool gui_button_input(InputEvent* event, WidgetElement* element) {\n    GuiButtonModel* model = element->model;\n    bool consumed = false;\n\n    if(model->callback == NULL) return consumed;\n\n    if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) {\n        model->callback(model->button_type, event->type, model->context);\n        consumed = true;\n    } else if((model->button_type == GuiButtonTypeRight) && (event->key == InputKeyRight)) {\n        model->callback(model->button_type, event->type, model->context);\n        consumed = true;\n    } else if((model->button_type == GuiButtonTypeCenter) && (event->key == InputKeyOk)) {\n        model->callback(model->button_type, event->type, model->context);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nstatic void gui_button_free(WidgetElement* gui_button) {\n    furi_assert(gui_button);\n\n    GuiButtonModel* model = gui_button->model;\n    furi_string_free(model->text);\n    free(gui_button->model);\n    free(gui_button);\n}\n\nWidgetElement* widget_element_button_create(\n    GuiButtonType button_type,\n    const char* text,\n    ButtonCallback callback,\n    void* context) {\n    // Allocate and init model\n    GuiButtonModel* model = malloc(sizeof(GuiButtonModel));\n    model->button_type = button_type;\n    model->callback = callback;\n    model->context = context;\n    model->text = furi_string_alloc_set(text);\n\n    // Allocate and init Element\n    WidgetElement* gui_button = malloc(sizeof(WidgetElement));\n    gui_button->parent = NULL;\n    gui_button->input = gui_button_input;\n    gui_button->draw = gui_button_draw;\n    gui_button->free = gui_button_free;\n    gui_button->model = model;\n\n    return gui_button;\n} //-V773\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_circle.c",
    "content": "#include \"widget_element_i.h\"\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    uint8_t radius;\n    bool fill;\n} GuiCircleModel;\n\nstatic void gui_circle_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiCircleModel* model = element->model;\n    if(model->fill) {\n        canvas_draw_disc(canvas, model->x, model->y, model->radius);\n    } else {\n        canvas_draw_circle(canvas, model->x, model->y, model->radius);\n    }\n}\n\nstatic void gui_circle_free(WidgetElement* gui_circle) {\n    furi_assert(gui_circle);\n\n    free(gui_circle->model);\n    free(gui_circle);\n}\n\nWidgetElement* widget_element_circle_create(uint8_t x, uint8_t y, uint8_t radius, bool fill) {\n    // Allocate and init model\n    GuiCircleModel* model = malloc(sizeof(GuiCircleModel));\n    model->x = x;\n    model->y = y;\n    model->radius = radius;\n    model->fill = fill;\n\n    // Allocate and init Element\n    WidgetElement* gui_circle = malloc(sizeof(WidgetElement));\n    gui_circle->parent = NULL;\n    gui_circle->input = NULL;\n    gui_circle->draw = gui_circle_draw;\n    gui_circle->free = gui_circle_free;\n    gui_circle->model = model;\n\n    return gui_circle;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_i.h",
    "content": "/**\n * @file widget_element_i.h\n * GUI: internal Widget Element API\n */\n\n#pragma once\n\n#include \"../widget.h\"\n#include \"widget_element.h\"\n#include <furi.h>\n#include <gui/view.h>\n#include <input/input.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct WidgetElement WidgetElement;\ntypedef struct Widget Widget;\n\nstruct WidgetElement {\n    // generic draw and input callbacks\n    void (*draw)(Canvas* canvas, WidgetElement* element);\n    bool (*input)(InputEvent* event, WidgetElement* element);\n\n    // free callback\n    void (*free)(WidgetElement* element);\n\n    // generic model holder\n    void* model;\n    FuriMutex* model_mutex;\n\n    // pointer to widget that hold our element\n    Widget* parent;\n};\n\n/** Create multi string element */\nWidgetElement* widget_element_string_multiline_create(\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text);\n\n/** Create string element */\nWidgetElement* widget_element_string_create(\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text);\n\n/** Create text box element */\nWidgetElement* widget_element_text_box_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots);\n\n/** Create button element */\nWidgetElement* widget_element_button_create(\n    GuiButtonType button_type,\n    const char* text,\n    ButtonCallback callback,\n    void* context);\n\n/** Create icon element */\nWidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon);\n\n/** Create rect element */\nWidgetElement* widget_element_rect_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    uint8_t radius,\n    bool fill);\n\n/** Create text scroll element */\nWidgetElement* widget_element_text_scroll_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    const char* text);\n\n/** Create circle element */\nWidgetElement* widget_element_circle_create(uint8_t x, uint8_t y, uint8_t radius, bool fill);\n\n/** Create line element */\nWidgetElement* widget_element_line_create(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_icon.c",
    "content": "#include \"widget_element_i.h\"\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    const Icon* icon;\n} GuiIconModel;\n\nstatic void gui_icon_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiIconModel* model = element->model;\n\n    if(model->icon) {\n        canvas_draw_icon(canvas, model->x, model->y, model->icon);\n    }\n}\n\nstatic void gui_icon_free(WidgetElement* gui_icon) {\n    furi_assert(gui_icon);\n\n    free(gui_icon->model);\n    free(gui_icon);\n}\n\nWidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon) {\n    furi_assert(icon);\n\n    // Allocate and init model\n    GuiIconModel* model = malloc(sizeof(GuiIconModel));\n    model->x = x;\n    model->y = y;\n    model->icon = icon;\n\n    // Allocate and init Element\n    WidgetElement* gui_icon = malloc(sizeof(WidgetElement));\n    gui_icon->parent = NULL;\n    gui_icon->input = NULL;\n    gui_icon->draw = gui_icon_draw;\n    gui_icon->free = gui_icon_free;\n    gui_icon->model = model;\n\n    return gui_icon;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_line.c",
    "content": "#include \"widget_element_i.h\"\n\ntypedef struct {\n    uint8_t x1;\n    uint8_t y1;\n    uint8_t x2;\n    uint8_t y2;\n} GuiLineModel;\n\nstatic void gui_line_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiLineModel* model = element->model;\n    canvas_draw_line(canvas, model->x1, model->y1, model->x2, model->y2);\n}\n\nstatic void gui_line_free(WidgetElement* gui_line) {\n    furi_assert(gui_line);\n\n    free(gui_line->model);\n    free(gui_line);\n}\n\nWidgetElement* widget_element_line_create(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {\n    // Allocate and init model\n    GuiLineModel* model = malloc(sizeof(GuiLineModel));\n    model->x1 = x1;\n    model->y1 = y1;\n    model->x2 = x2;\n    model->y2 = y2;\n\n    // Allocate and init Element\n    WidgetElement* gui_line = malloc(sizeof(WidgetElement));\n    gui_line->parent = NULL;\n    gui_line->input = NULL;\n    gui_line->draw = gui_line_draw;\n    gui_line->free = gui_line_free;\n    gui_line->model = model;\n\n    return gui_line;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_rect.c",
    "content": "#include \"widget_element_i.h\"\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    uint8_t width;\n    uint8_t height;\n    uint8_t radius;\n    bool fill;\n} GuiRectModel;\n\nstatic void gui_rect_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiRectModel* model = element->model;\n    if(model->fill) {\n        canvas_draw_rbox(canvas, model->x, model->y, model->width, model->height, model->radius);\n    } else {\n        canvas_draw_rframe(canvas, model->x, model->y, model->width, model->height, model->radius);\n    }\n}\n\nstatic void gui_rect_free(WidgetElement* gui_rect) {\n    furi_assert(gui_rect);\n\n    free(gui_rect->model);\n    free(gui_rect);\n}\n\nWidgetElement* widget_element_rect_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    uint8_t radius,\n    bool fill) {\n    // Allocate and init model\n    GuiRectModel* model = malloc(sizeof(GuiRectModel));\n    model->x = x;\n    model->y = y;\n    model->width = width;\n    model->height = height;\n    model->radius = radius;\n    model->fill = fill;\n\n    // Allocate and init Element\n    WidgetElement* gui_rect = malloc(sizeof(WidgetElement));\n    gui_rect->parent = NULL;\n    gui_rect->input = NULL;\n    gui_rect->draw = gui_rect_draw;\n    gui_rect->free = gui_rect_free;\n    gui_rect->model = model;\n\n    return gui_rect;\n}\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_string.c",
    "content": "#include \"widget_element_i.h\"\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    Align horizontal;\n    Align vertical;\n    Font font;\n    FuriString* text;\n} GuiStringModel;\n\nstatic void gui_string_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiStringModel* model = element->model;\n\n    if(furi_string_size(model->text)) {\n        canvas_set_font(canvas, model->font);\n        canvas_draw_str_aligned(\n            canvas,\n            model->x,\n            model->y,\n            model->horizontal,\n            model->vertical,\n            furi_string_get_cstr(model->text));\n    }\n}\n\nstatic void gui_string_free(WidgetElement* gui_string) {\n    furi_assert(gui_string);\n\n    GuiStringModel* model = gui_string->model;\n    furi_string_free(model->text);\n    free(gui_string->model);\n    free(gui_string);\n}\n\nWidgetElement* widget_element_string_create(\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text) {\n    furi_assert(text);\n\n    // Allocate and init model\n    GuiStringModel* model = malloc(sizeof(GuiStringModel));\n    model->x = x;\n    model->y = y;\n    model->horizontal = horizontal;\n    model->vertical = vertical;\n    model->font = font;\n    model->text = furi_string_alloc_set(text);\n\n    // Allocate and init Element\n    WidgetElement* gui_string = malloc(sizeof(WidgetElement));\n    gui_string->parent = NULL;\n    gui_string->input = NULL;\n    gui_string->draw = gui_string_draw;\n    gui_string->free = gui_string_free;\n    gui_string->model = model;\n\n    return gui_string;\n} //-V773\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_string_multiline.c",
    "content": "#include \"widget_element_i.h\"\n#include <gui/elements.h>\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    Align horizontal;\n    Align vertical;\n    Font font;\n    FuriString* text;\n} GuiStringMultiLineModel;\n\nstatic void gui_string_multiline_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiStringMultiLineModel* model = element->model;\n\n    if(furi_string_size(model->text)) {\n        canvas_set_font(canvas, model->font);\n        elements_multiline_text_aligned(\n            canvas,\n            model->x,\n            model->y,\n            model->horizontal,\n            model->vertical,\n            furi_string_get_cstr(model->text));\n    }\n}\n\nstatic void gui_string_multiline_free(WidgetElement* gui_string) {\n    furi_assert(gui_string);\n\n    GuiStringMultiLineModel* model = gui_string->model;\n    furi_string_free(model->text);\n    free(gui_string->model);\n    free(gui_string);\n}\n\nWidgetElement* widget_element_string_multiline_create(\n    uint8_t x,\n    uint8_t y,\n    Align horizontal,\n    Align vertical,\n    Font font,\n    const char* text) {\n    furi_assert(text);\n\n    // Allocate and init model\n    GuiStringMultiLineModel* model = malloc(sizeof(GuiStringMultiLineModel));\n    model->x = x;\n    model->y = y;\n    model->horizontal = horizontal;\n    model->vertical = vertical;\n    model->font = font;\n    model->text = furi_string_alloc_set(text);\n\n    // Allocate and init Element\n    WidgetElement* gui_string = malloc(sizeof(WidgetElement));\n    gui_string->parent = NULL;\n    gui_string->input = NULL;\n    gui_string->draw = gui_string_multiline_draw;\n    gui_string->free = gui_string_multiline_free;\n    gui_string->model = model;\n\n    return gui_string;\n} //-V773\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_text_box.c",
    "content": "#include \"widget_element_i.h\"\n#include <gui/elements.h>\n\ntypedef struct {\n    uint8_t x;\n    uint8_t y;\n    uint8_t width;\n    uint8_t height;\n    Align horizontal;\n    Align vertical;\n    FuriString* text;\n    bool strip_to_dots;\n} GuiTextBoxModel;\n\nstatic void gui_text_box_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n    GuiTextBoxModel* model = element->model;\n\n    if(furi_string_size(model->text)) {\n        elements_text_box(\n            canvas,\n            model->x,\n            model->y,\n            model->width,\n            model->height,\n            model->horizontal,\n            model->vertical,\n            furi_string_get_cstr(model->text),\n            model->strip_to_dots);\n    }\n}\n\nstatic void gui_text_box_free(WidgetElement* gui_string) {\n    furi_assert(gui_string);\n\n    GuiTextBoxModel* model = gui_string->model;\n    furi_string_free(model->text);\n    free(gui_string->model);\n    free(gui_string);\n}\n\nWidgetElement* widget_element_text_box_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    Align horizontal,\n    Align vertical,\n    const char* text,\n    bool strip_to_dots) {\n    furi_assert(text);\n\n    // Allocate and init model\n    GuiTextBoxModel* model = malloc(sizeof(GuiTextBoxModel));\n    model->x = x;\n    model->y = y;\n    model->width = width;\n    model->height = height;\n    model->horizontal = horizontal;\n    model->vertical = vertical;\n    model->text = furi_string_alloc_set(text);\n    model->strip_to_dots = strip_to_dots;\n\n    // Allocate and init Element\n    WidgetElement* gui_string = malloc(sizeof(WidgetElement));\n    gui_string->parent = NULL;\n    gui_string->input = NULL;\n    gui_string->draw = gui_text_box_draw;\n    gui_string->free = gui_text_box_free;\n    gui_string->model = model;\n\n    return gui_string;\n} //-V773\n"
  },
  {
    "path": "applications/services/gui/modules/widget_elements/widget_element_text_scroll.c",
    "content": "#include \"widget_element_i.h\"\n#include <gui/elements.h>\n#include <m-array.h>\n\n#define WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET (4)\n\ntypedef struct {\n    Font font;\n    Align horizontal;\n    FuriString* text;\n} TextScrollLineArray;\n\nARRAY_DEF(TextScrollLineArray, TextScrollLineArray, M_POD_OPLIST) //-V658\n\ntypedef struct {\n    TextScrollLineArray_t line_array;\n    uint8_t x;\n    uint8_t y;\n    uint8_t width;\n    uint8_t height;\n    FuriString* text;\n    uint16_t scroll_pos_total;\n    uint16_t scroll_pos_current;\n    bool text_formatted;\n} WidgetElementTextScrollModel;\n\nstatic bool\n    widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, FuriString* text) {\n    bool processed = false;\n\n    do {\n        if(furi_string_get_char(text, 0) != '\\e') break;\n        char ctrl_symbol = furi_string_get_char(text, 1);\n        if(ctrl_symbol == 'c') {\n            line->horizontal = AlignCenter;\n        } else if(ctrl_symbol == 'r') {\n            line->horizontal = AlignRight;\n        } else if(ctrl_symbol == '#') {\n            line->font = FontPrimary;\n        } else if(ctrl_symbol == '*') {\n            line->font = FontKeyboard;\n        }\n        furi_string_right(text, 2);\n        processed = true;\n    } while(false);\n\n    return processed;\n}\n\nvoid widget_element_text_scroll_add_line(WidgetElement* element, TextScrollLineArray* line) {\n    WidgetElementTextScrollModel* model = element->model;\n    TextScrollLineArray new_line;\n    new_line.font = line->font;\n    new_line.horizontal = line->horizontal;\n    new_line.text = furi_string_alloc_set(line->text);\n    TextScrollLineArray_push_back(model->line_array, new_line);\n}\n\nstatic void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* element) {\n    WidgetElementTextScrollModel* model = element->model;\n    TextScrollLineArray line_tmp;\n    bool all_text_processed = false;\n    line_tmp.text = furi_string_alloc();\n    bool reached_new_line = true;\n    uint16_t total_height = 0;\n\n    while(!all_text_processed) {\n        if(reached_new_line) {\n            // Set default line properties\n            line_tmp.font = FontSecondary;\n            line_tmp.horizontal = AlignLeft;\n            furi_string_reset(line_tmp.text);\n            // Process control symbols\n            while(widget_element_text_scroll_process_ctrl_symbols(&line_tmp, model->text))\n                ;\n        }\n        // Set canvas font\n        canvas_set_font(canvas, line_tmp.font);\n        const CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font);\n        total_height += params->height;\n        if(total_height > model->height) {\n            model->scroll_pos_total++;\n        }\n\n        uint8_t line_width = 0;\n        uint16_t char_i = 0;\n        while(true) {\n            char next_char = furi_string_get_char(model->text, char_i++);\n            if(next_char == '\\0') {\n                furi_string_push_back(line_tmp.text, '\\0');\n                widget_element_text_scroll_add_line(element, &line_tmp);\n                total_height += params->leading_default - params->height;\n                all_text_processed = true;\n                break;\n            } else if(next_char == '\\n') {\n                furi_string_push_back(line_tmp.text, '\\0');\n                widget_element_text_scroll_add_line(element, &line_tmp);\n                furi_string_right(model->text, char_i);\n                total_height += params->leading_default - params->height;\n                reached_new_line = true;\n                break;\n            } else {\n                line_width += canvas_glyph_width(canvas, next_char);\n                if(line_width > model->width) {\n                    furi_string_push_back(line_tmp.text, '\\0');\n                    widget_element_text_scroll_add_line(element, &line_tmp);\n                    furi_string_right(model->text, char_i - 1);\n                    furi_string_reset(line_tmp.text);\n                    total_height += params->leading_default - params->height;\n                    reached_new_line = false;\n                    break;\n                } else {\n                    furi_string_push_back(line_tmp.text, next_char);\n                }\n            }\n        }\n    }\n\n    furi_string_free(line_tmp.text);\n}\n\nstatic void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* element) {\n    furi_assert(canvas);\n    furi_assert(element);\n\n    furi_mutex_acquire(element->model_mutex, FuriWaitForever);\n\n    WidgetElementTextScrollModel* model = element->model;\n    if(!model->text_formatted) {\n        widget_element_text_scroll_fill_lines(canvas, element);\n        model->text_formatted = true;\n    }\n\n    uint8_t y = model->y;\n    uint8_t x = model->x;\n    uint16_t curr_line = 0;\n    if(TextScrollLineArray_size(model->line_array)) {\n        TextScrollLineArray_it_t it;\n        for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);\n            TextScrollLineArray_next(it), curr_line++) {\n            if(curr_line < model->scroll_pos_current) continue;\n            TextScrollLineArray* line = TextScrollLineArray_ref(it);\n            const CanvasFontParameters* params = canvas_get_font_params(canvas, line->font);\n            if(y + params->descender > model->y + model->height) break;\n            canvas_set_font(canvas, line->font);\n            if(line->horizontal == AlignLeft) {\n                x = model->x;\n            } else if(line->horizontal == AlignCenter) {\n                x = (model->x + model->width) / 2;\n            } else if(line->horizontal == AlignRight) {\n                x = model->x + model->width;\n            }\n            canvas_draw_str_aligned(\n                canvas, x, y, line->horizontal, AlignTop, furi_string_get_cstr(line->text));\n            y += params->leading_default;\n        }\n        // Draw scroll bar\n        if(model->scroll_pos_total > 1) {\n            elements_scrollbar_pos(\n                canvas,\n                model->x + model->width + WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET,\n                model->y,\n                model->height,\n                model->scroll_pos_current,\n                model->scroll_pos_total);\n        }\n    }\n\n    furi_mutex_release(element->model_mutex);\n}\n\nstatic bool widget_element_text_scroll_input(InputEvent* event, WidgetElement* element) {\n    furi_assert(event);\n    furi_assert(element);\n\n    furi_mutex_acquire(element->model_mutex, FuriWaitForever);\n\n    WidgetElementTextScrollModel* model = element->model;\n    bool consumed = false;\n\n    if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {\n        if(event->key == InputKeyUp) {\n            if(model->scroll_pos_current > 0) {\n                model->scroll_pos_current--;\n            }\n            consumed = true;\n        } else if(event->key == InputKeyDown) {\n            if((model->scroll_pos_total > 1) &&\n               (model->scroll_pos_current < model->scroll_pos_total - 1)) {\n                model->scroll_pos_current++;\n            }\n            consumed = true;\n        }\n    }\n\n    furi_mutex_release(element->model_mutex);\n\n    return consumed;\n}\n\nstatic void widget_element_text_scroll_free(WidgetElement* text_scroll) {\n    furi_assert(text_scroll);\n\n    WidgetElementTextScrollModel* model = text_scroll->model;\n    TextScrollLineArray_it_t it;\n    for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);\n        TextScrollLineArray_next(it)) {\n        TextScrollLineArray* line = TextScrollLineArray_ref(it);\n        furi_string_free(line->text);\n    }\n    TextScrollLineArray_clear(model->line_array);\n    furi_string_free(model->text);\n    free(text_scroll->model);\n    furi_mutex_free(text_scroll->model_mutex);\n    free(text_scroll);\n}\n\nWidgetElement* widget_element_text_scroll_create(\n    uint8_t x,\n    uint8_t y,\n    uint8_t width,\n    uint8_t height,\n    const char* text) {\n    furi_assert(text);\n\n    // Allocate and init model\n    WidgetElementTextScrollModel* model = malloc(sizeof(WidgetElementTextScrollModel));\n    model->x = x;\n    model->y = y;\n    model->width = width - WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET;\n    model->height = height;\n    model->scroll_pos_current = 0;\n    model->scroll_pos_total = 1;\n    TextScrollLineArray_init(model->line_array);\n    model->text = furi_string_alloc_set(text);\n\n    WidgetElement* text_scroll = malloc(sizeof(WidgetElement));\n    text_scroll->parent = NULL;\n    text_scroll->draw = widget_element_text_scroll_draw;\n    text_scroll->input = widget_element_text_scroll_input;\n    text_scroll->free = widget_element_text_scroll_free;\n    text_scroll->model = model;\n    text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    return text_scroll;\n} //-V773\n"
  },
  {
    "path": "applications/services/gui/scene_manager.c",
    "content": "#include \"scene_manager_i.h\"\n#include <furi.h>\n\nSceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers, void* context) {\n    furi_check(app_scene_handlers);\n\n    SceneManager* scene_manager =\n        malloc(sizeof(SceneManager) + (sizeof(AppScene) * app_scene_handlers->scene_num));\n    // Set SceneManager context and scene handlers\n    scene_manager->context = context;\n    scene_manager->scene_handlers = app_scene_handlers;\n    // Initialize ScaneManager array for navigation\n    SceneManagerIdStack_init(scene_manager->scene_id_stack);\n\n    return scene_manager;\n}\n\nvoid scene_manager_free(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n\n    // Clear ScaneManager array\n    SceneManagerIdStack_clear(scene_manager->scene_id_stack);\n    // Free SceneManager structure\n    free(scene_manager);\n}\n\nvoid scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_id, uint32_t state) {\n    furi_check(scene_manager);\n    furi_check(scene_id < scene_manager->scene_handlers->scene_num);\n\n    scene_manager->scene[scene_id].state = state;\n}\n\nuint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id) {\n    furi_check(scene_manager);\n    furi_check(scene_id < scene_manager->scene_handlers->scene_num);\n\n    return scene_manager->scene[scene_id].state;\n}\n\nbool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event) {\n    furi_check(scene_manager);\n\n    SceneManagerEvent event = {\n        .type = SceneManagerEventTypeCustom,\n        .event = custom_event,\n    };\n\n    bool result = false;\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t* scene_id_p = SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        uint32_t scene_id = *scene_id_p;\n        result = scene_manager->scene_handlers->on_event_handlers[scene_id](\n            scene_manager->context, event);\n    }\n\n    return result;\n}\n\nbool scene_manager_handle_back_event(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n\n    SceneManagerEvent event = {\n        .type = SceneManagerEventTypeBack,\n    };\n\n    bool consumed = false;\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t* scene_id_p = SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        uint32_t scene_id = *scene_id_p;\n        consumed = scene_manager->scene_handlers->on_event_handlers[scene_id](\n            scene_manager->context, event);\n    }\n\n    if(!consumed) {\n        consumed = scene_manager_previous_scene(scene_manager);\n    }\n    return consumed;\n}\n\nvoid scene_manager_handle_tick_event(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n\n    SceneManagerEvent event = {\n        .type = SceneManagerEventTypeTick,\n    };\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t* scene_id_p = SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        uint32_t scene_id = *scene_id_p;\n        scene_manager->scene_handlers->on_event_handlers[scene_id](scene_manager->context, event);\n    }\n}\n\nvoid scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_id) {\n    furi_check(scene_manager);\n    furi_check(next_scene_id < scene_manager->scene_handlers->scene_num);\n\n    // Check if it is not the first scene\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n    }\n    // Add next scene and run on_enter\n    SceneManagerIdStack_push_back(scene_manager->scene_id_stack, next_scene_id);\n    scene_manager->scene_handlers->on_enter_handlers[next_scene_id](scene_manager->context);\n}\n\nbool scene_manager_previous_scene(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t cur_scene_id = 0;\n        SceneManagerIdStack_pop_back(&cur_scene_id, scene_manager->scene_id_stack);\n\n        // Handle exit from start scene separately\n        if(SceneManagerIdStack_size(scene_manager->scene_id_stack) == 0) {\n            scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n            return false;\n        }\n        uint32_t prev_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n        scene_manager->scene_handlers->on_enter_handlers[prev_scene_id](scene_manager->context);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool scene_manager_search_and_switch_to_previous_scene(\n    SceneManager* scene_manager,\n    uint32_t scene_id) {\n    furi_check(scene_manager);\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t prev_scene_id = 0;\n        uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        SceneManagerIdStack_it_t scene_it;\n        SceneManagerIdStack_it_last(scene_it, scene_manager->scene_id_stack);\n        // Search scene with given id in navigation stack\n        bool scene_found = false;\n        while(!scene_found) {\n            SceneManagerIdStack_previous(scene_it);\n            if(SceneManagerIdStack_end_p(scene_it)) {\n                return false;\n            }\n            prev_scene_id = *SceneManagerIdStack_ref(scene_it);\n            if(prev_scene_id == scene_id) {\n                scene_found = true;\n            }\n        }\n        // Remove all scene id from navigation stack\n        SceneManagerIdStack_next(scene_it);\n        SceneManagerIdStack_pop_until(scene_manager->scene_id_stack, scene_it);\n\n        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n        scene_manager->scene_handlers->on_enter_handlers[prev_scene_id](scene_manager->context);\n\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool scene_manager_search_and_switch_to_previous_scene_one_of(\n    SceneManager* scene_manager,\n    const uint32_t* scene_ids,\n    size_t scene_ids_size) {\n    furi_check(scene_manager);\n    furi_check(scene_ids);\n    bool scene_found = false;\n\n    for(size_t i = 0; i < scene_ids_size; ++i) {\n        const uint32_t scene_id = scene_ids[i];\n        if(scene_manager_has_previous_scene(scene_manager, scene_id)) {\n            scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id);\n            scene_found = true;\n            break;\n        }\n    }\n    return scene_found;\n}\n\nbool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id) {\n    furi_check(scene_manager);\n    bool scene_found = false;\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t prev_scene_id;\n        SceneManagerIdStack_it_t scene_it;\n        SceneManagerIdStack_it_last(scene_it, scene_manager->scene_id_stack);\n\n        // Perform search in scene stack\n        while(!scene_found) {\n            SceneManagerIdStack_previous(scene_it);\n            if(SceneManagerIdStack_end_p(scene_it)) {\n                break;\n            }\n            prev_scene_id = *SceneManagerIdStack_ref(scene_it);\n            if(prev_scene_id == scene_id) {\n                scene_found = true;\n            }\n        }\n    }\n    return scene_found;\n}\n\nbool scene_manager_search_and_switch_to_another_scene(\n    SceneManager* scene_manager,\n    uint32_t scene_id) {\n    furi_check(scene_manager);\n    furi_check(scene_id < scene_manager->scene_handlers->scene_num);\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        SceneManagerIdStack_it_t scene_it;\n        SceneManagerIdStack_it(scene_it, scene_manager->scene_id_stack);\n        SceneManagerIdStack_next(scene_it);\n        // Remove all scene id from navigation stack until first scene\n        SceneManagerIdStack_pop_until(scene_manager->scene_id_stack, scene_it);\n        // Add next scene\n        SceneManagerIdStack_push_back(scene_manager->scene_id_stack, scene_id);\n\n        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n        scene_manager->scene_handlers->on_enter_handlers[scene_id](scene_manager->context);\n\n        return true;\n    } else {\n        return false;\n    }\n}\n\nuint32_t scene_manager_get_current_scene(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n    return *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n}\n\nvoid scene_manager_stop(SceneManager* scene_manager) {\n    furi_check(scene_manager);\n\n    if(SceneManagerIdStack_size(scene_manager->scene_id_stack) > 0) {\n        uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);\n        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);\n    }\n}\n"
  },
  {
    "path": "applications/services/gui/scene_manager.h",
    "content": "/**\n * @file scene_manager.h\n * GUI: SceneManager API\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Scene Manager events type */\ntypedef enum {\n    SceneManagerEventTypeCustom,\n    SceneManagerEventTypeBack,\n    SceneManagerEventTypeTick,\n} SceneManagerEventType;\n\n/** Scene Manager event\n */\ntypedef struct {\n    SceneManagerEventType type;\n    uint32_t event;\n} SceneManagerEvent;\n\n/** Prototype for Scene on_enter handler */\ntypedef void (*AppSceneOnEnterCallback)(void* context);\n\n/** Prototype for Scene on_event handler */\ntypedef bool (*AppSceneOnEventCallback)(void* context, SceneManagerEvent event);\n\n/** Prototype for Scene on_exit handler */\ntypedef void (*AppSceneOnExitCallback)(void* context);\n\n/** Scene Manager configuration structure\n * Contains array of Scene handlers\n */\ntypedef struct {\n    const AppSceneOnEnterCallback* on_enter_handlers;\n    const AppSceneOnEventCallback* on_event_handlers;\n    const AppSceneOnExitCallback* on_exit_handlers;\n    const uint32_t scene_num;\n} SceneManagerHandlers;\n\ntypedef struct SceneManager SceneManager;\n\n/** Set Scene state\n *\n * @param      scene_manager  SceneManager instance\n * @param      scene_id       Scene ID\n * @param      state          Scene new state\n */\nvoid scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_id, uint32_t state);\n\n/** Get Scene state\n *\n * @param      scene_manager  SceneManager instance\n * @param      scene_id       Scene ID\n *\n * @return     Scene state\n */\nuint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id);\n\n/** Scene Manager allocation and configuration\n *\n * Scene Manager allocates all scenes internally\n *\n * @param      app_scene_handlers  SceneManagerHandlers instance\n * @param      context             context to be set on Scene handlers calls\n *\n * @return     SceneManager instance\n */\nSceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers, void* context);\n\n/** Free Scene Manager with allocated Scenes\n *\n * @param      scene_manager  SceneManager instance\n */\nvoid scene_manager_free(SceneManager* scene_manager);\n\n/** Custom event handler\n *\n * Calls Scene event handler with Custom event parameter\n *\n * @param      scene_manager  SceneManager instance\n * @param      custom_event   Custom event code\n *\n * @return     true if event was consumed, false otherwise\n */\nbool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event);\n\n/** Back event handler\n *\n * Calls Scene event handler with Back event parameter\n *\n * @param      scene_manager  SceneManager instance\n *\n * @return     true if event was consumed, false otherwise\n */\nbool scene_manager_handle_back_event(SceneManager* scene_manager);\n\n/** Tick event handler\n *\n * Calls Scene event handler with Tick event parameter\n *\n * @param      scene_manager  SceneManager instance\n */\nvoid scene_manager_handle_tick_event(SceneManager* scene_manager);\n\n/** Add and run next Scene\n *\n * @param      scene_manager  SceneManager instance\n * @param      next_scene_id  next Scene ID\n */\nvoid scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_id);\n\n/** Run previous Scene\n *\n * @param      scene_manager  SceneManager instance\n *\n * @return     true if previous scene was found, false otherwise\n */\nbool scene_manager_previous_scene(SceneManager* scene_manager);\n\n/** Search previous Scene\n *\n * @param      scene_manager  SceneManager instance\n * @param      scene_id       Scene ID\n *\n * @return     true if previous scene was found, false otherwise\n */\nbool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id);\n\n/** Search and switch to previous Scene\n *\n * @param      scene_manager  SceneManager instance\n * @param      scene_id       Scene ID\n *\n * @return     true if previous scene was found, false otherwise\n */\nbool scene_manager_search_and_switch_to_previous_scene(\n    SceneManager* scene_manager,\n    uint32_t scene_id);\n\n/** Search and switch to previous Scene, multiple choice\n *\n * @param      scene_manager    SceneManager instance\n * @param      scene_ids        Array of scene IDs\n * @param      scene_ids_size   Array of scene IDs size\n *\n * @return     true if one of previous scenes was found, false otherwise\n */\nbool scene_manager_search_and_switch_to_previous_scene_one_of(\n    SceneManager* scene_manager,\n    const uint32_t* scene_ids,\n    size_t scene_ids_size);\n\n/** Clear Scene stack and switch to another Scene\n *\n * @param      scene_manager  SceneManager instance\n * @param      scene_id       Scene ID\n *\n * @return     true if previous scene was found, false otherwise\n */\nbool scene_manager_search_and_switch_to_another_scene(\n    SceneManager* scene_manager,\n    uint32_t scene_id);\n\n/** Get id of current scene\n * \n * @param      scene_manager  SceneManager instance\n * \n * @return                    Scene ID\n */\nuint32_t scene_manager_get_current_scene(SceneManager* scene_manager);\n\n/** Exit from current scene\n *\n * @param      scene_manager  SceneManager instance\n */\nvoid scene_manager_stop(SceneManager* scene_manager);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/scene_manager_i.h",
    "content": "/**\n * @file scene_manager_i.h\n * GUI: internal SceneManager API\n */\n\n#pragma once\n\n#include \"scene_manager.h\"\n#include <m-array.h>\n\nARRAY_DEF(SceneManagerIdStack, uint32_t, M_DEFAULT_OPLIST); //-V658\n\ntypedef struct {\n    uint32_t state;\n} AppScene;\n\nstruct SceneManager {\n    SceneManagerIdStack_t scene_id_stack;\n    const SceneManagerHandlers* scene_handlers;\n    void* context;\n    AppScene scene[];\n};\n"
  },
  {
    "path": "applications/services/gui/view.c",
    "content": "#include \"view_i.h\"\n\nView* view_alloc(void) {\n    View* view = malloc(sizeof(View));\n    view->orientation = ViewOrientationHorizontal;\n    return view;\n}\n\nvoid view_free(View* view) {\n    furi_check(view);\n    view_free_model(view);\n    free(view);\n}\n\nvoid view_tie_icon_animation(View* view, IconAnimation* icon_animation) {\n    furi_check(view);\n    icon_animation_set_update_callback(icon_animation, view_icon_animation_callback, view);\n}\n\nvoid view_set_draw_callback(View* view, ViewDrawCallback callback) {\n    furi_check(view);\n    view->draw_callback = callback;\n}\n\nvoid view_set_input_callback(View* view, ViewInputCallback callback) {\n    furi_check(view);\n    view->input_callback = callback;\n}\n\nvoid view_set_custom_callback(View* view, ViewCustomCallback callback) {\n    furi_check(view);\n    view->custom_callback = callback;\n}\n\nvoid view_set_previous_callback(View* view, ViewNavigationCallback callback) {\n    furi_check(view);\n    view->previous_callback = callback;\n}\n\nvoid view_set_enter_callback(View* view, ViewCallback callback) {\n    furi_check(view);\n    view->enter_callback = callback;\n}\n\nvoid view_set_exit_callback(View* view, ViewCallback callback) {\n    furi_check(view);\n    view->exit_callback = callback;\n}\n\nvoid view_set_update_callback(View* view, ViewUpdateCallback callback) {\n    furi_check(view);\n    view->update_callback = callback;\n}\n\nvoid view_set_update_callback_context(View* view, void* context) {\n    furi_check(view);\n    view->update_callback_context = context;\n}\n\nvoid view_set_context(View* view, void* context) {\n    furi_check(view);\n    view->context = context;\n}\n\nvoid view_set_orientation(View* view, ViewOrientation orientation) {\n    furi_check(view);\n    view->orientation = orientation;\n}\n\nvoid view_allocate_model(View* view, ViewModelType type, size_t size) {\n    furi_check(view);\n    furi_check(size > 0);\n    furi_check(view->model_type == ViewModelTypeNone);\n    furi_check(view->model == NULL);\n    view->model_type = type;\n    if(view->model_type == ViewModelTypeLockFree) {\n        view->model = malloc(size);\n    } else if(view->model_type == ViewModelTypeLocking) {\n        ViewModelLocking* model = malloc(sizeof(ViewModelLocking) + size);\n        model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);\n        view->model = model;\n    } else {\n        furi_crash();\n    }\n}\n\nvoid view_free_model(View* view) {\n    furi_check(view);\n    if(view->model_type == ViewModelTypeNone) {\n        return;\n    } else if(view->model_type == ViewModelTypeLocking) {\n        ViewModelLocking* model = view->model;\n        furi_mutex_free(model->mutex);\n    }\n    free(view->model);\n    view->model = NULL;\n    view->model_type = ViewModelTypeNone;\n}\n\nvoid* view_get_model(View* view) {\n    furi_check(view);\n    if(view->model_type == ViewModelTypeLocking) {\n        ViewModelLocking* model = (ViewModelLocking*)(view->model);\n        furi_check(furi_mutex_acquire(model->mutex, FuriWaitForever) == FuriStatusOk);\n        return model->data;\n    }\n    return view->model;\n}\n\nvoid view_commit_model(View* view, bool update) {\n    furi_check(view);\n    view_unlock_model(view);\n    if(update && view->update_callback) {\n        view->update_callback(view, view->update_callback_context);\n    }\n}\n\nvoid view_icon_animation_callback(IconAnimation* instance, void* context) {\n    UNUSED(instance);\n    furi_check(context);\n    View* view = context;\n    if(view->update_callback) {\n        view->update_callback(view, view->update_callback_context);\n    }\n}\n\nvoid view_unlock_model(View* view) {\n    furi_check(view);\n    if(view->model_type == ViewModelTypeLocking) {\n        ViewModelLocking* model = (ViewModelLocking*)(view->model);\n        furi_check(furi_mutex_release(model->mutex) == FuriStatusOk);\n    }\n}\n\nvoid view_draw(View* view, Canvas* canvas) {\n    furi_check(view);\n    if(view->draw_callback) {\n        void* data = view_get_model(view);\n        view->draw_callback(canvas, data);\n        view_unlock_model(view);\n    }\n}\n\nbool view_input(View* view, InputEvent* event) {\n    furi_check(view);\n    if(view->input_callback) {\n        return view->input_callback(event, view->context);\n    } else {\n        return false;\n    }\n}\n\nbool view_custom(View* view, uint32_t event) {\n    furi_check(view);\n    if(view->custom_callback) {\n        return view->custom_callback(event, view->context);\n    } else {\n        return false;\n    }\n}\n\nuint32_t view_previous(View* view) {\n    furi_check(view);\n    if(view->previous_callback) {\n        return view->previous_callback(view->context);\n    } else {\n        return VIEW_IGNORE;\n    }\n}\n\nvoid view_enter(View* view) {\n    furi_check(view);\n    if(view->enter_callback) view->enter_callback(view->context);\n}\n\nvoid view_exit(View* view) {\n    furi_check(view);\n    if(view->exit_callback) view->exit_callback(view->context);\n}\n"
  },
  {
    "path": "applications/services/gui/view.h",
    "content": "/**\n * @file view.h\n * GUI: View API\n */\n\n#pragma once\n\n#include <input/input.h>\n\n#include \"icon_animation.h\"\n#include \"canvas.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Hides drawing view_port */\n#define VIEW_NONE 0xFFFFFFFF\n\n/** Ignore navigation event */\n#define VIEW_IGNORE 0xFFFFFFFE\n\ntypedef enum {\n    ViewOrientationHorizontal,\n    ViewOrientationHorizontalFlip,\n    ViewOrientationVertical,\n    ViewOrientationVerticalFlip,\n} ViewOrientation;\n\n/** View, anonymous type */\ntypedef struct View View;\n\n/** View Draw callback\n * @param      canvas      pointer to canvas\n * @param      model       pointer to model\n * @warning    called from GUI thread\n */\ntypedef void (*ViewDrawCallback)(Canvas* canvas, void* model);\n\n/** View Input callback\n * @param      event    pointer to input event data\n * @param      context  pointer to context\n * @return     true if event handled, false if event ignored\n * @warning    called from GUI thread\n */\ntypedef bool (*ViewInputCallback)(InputEvent* event, void* context);\n\n/** View Custom callback\n * @param      event    number of custom event\n * @param      context  pointer to context\n * @return     true if event handled, false if event ignored\n */\ntypedef bool (*ViewCustomCallback)(uint32_t event, void* context);\n\n/** View navigation callback\n * @param      context  pointer to context\n * @return     next view id\n * @warning    called from GUI thread\n */\ntypedef uint32_t (*ViewNavigationCallback)(void* context);\n\n/** View callback\n * @param      context  pointer to context\n * @warning    called from GUI thread\n */\ntypedef void (*ViewCallback)(void* context);\n\n/** View Update Callback Called upon model change, need to be propagated to GUI\n * throw ViewPort update\n * @param      view     pointer to view\n * @param      context  pointer to context\n * @warning    called from GUI thread\n */\ntypedef void (*ViewUpdateCallback)(View* view, void* context);\n\n/** View model types */\ntypedef enum {\n    /** Model is not allocated */\n    ViewModelTypeNone,\n    /** Model consist of atomic types and/or partial update is not critical for rendering.\n     * Lock free.\n     */\n    ViewModelTypeLockFree,\n    /** Model access is guarded with mutex.\n     * Locking gui thread.\n     */\n    ViewModelTypeLocking,\n} ViewModelType;\n\n/** Allocate and init View\n * @return View instance\n */\nView* view_alloc(void);\n\n/** Free View\n *\n * @param      view  instance\n */\nvoid view_free(View* view);\n\n/** Tie IconAnimation with View\n *\n * @param      view            View instance\n * @param      icon_animation  IconAnimation instance\n */\nvoid view_tie_icon_animation(View* view, IconAnimation* icon_animation);\n\n/** Set View Draw callback\n *\n * @param      view      View instance\n * @param      callback  draw callback\n */\nvoid view_set_draw_callback(View* view, ViewDrawCallback callback);\n\n/** Set View Input callback\n *\n * @param      view      View instance\n * @param      callback  input callback\n */\nvoid view_set_input_callback(View* view, ViewInputCallback callback);\n\n/** Set View Custom callback\n *\n * @param      view      View instance\n * @param      callback  input callback\n */\nvoid view_set_custom_callback(View* view, ViewCustomCallback callback);\n\n/** Set Navigation Previous callback\n *\n * @param      view      View instance\n * @param      callback  input callback\n */\nvoid view_set_previous_callback(View* view, ViewNavigationCallback callback);\n\n/** Set Enter callback\n *\n * @param      view      View instance\n * @param      callback  callback\n */\nvoid view_set_enter_callback(View* view, ViewCallback callback);\n\n/** Set Exit callback\n *\n * @param      view      View instance\n * @param      callback  callback\n */\nvoid view_set_exit_callback(View* view, ViewCallback callback);\n\n/** Set Update callback\n *\n * @param      view      View instance\n * @param      callback  callback\n */\nvoid view_set_update_callback(View* view, ViewUpdateCallback callback);\n\n/** Set View Draw callback\n *\n * @param      view     View instance\n * @param      context  context for callbacks\n */\nvoid view_set_update_callback_context(View* view, void* context);\n\n/** Set View Draw callback\n *\n * @param      view     View instance\n * @param      context  context for callbacks\n */\nvoid view_set_context(View* view, void* context);\n\n/** Set View Orientation\n *\n * @param      view         View instance\n * @param      orientation  either vertical or horizontal\n */\nvoid view_set_orientation(View* view, ViewOrientation orientation);\n\n/** Allocate view model.\n *\n * @param      view  View instance\n * @param      type  View Model Type\n * @param      size  size\n */\nvoid view_allocate_model(View* view, ViewModelType type, size_t size);\n\n/** Free view model data memory.\n *\n * @param      view  View instance\n */\nvoid view_free_model(View* view);\n\n/** Get view model data\n *\n * @param      view  View instance\n *\n * @return     pointer to model data\n * @warning    Don't forget to commit model changes\n */\nvoid* view_get_model(View* view);\n\n/** Commit view model\n *\n * @param      view    View instance\n * @param      update  true if you want to emit view update, false otherwise\n */\nvoid view_commit_model(View* view, bool update);\n\n#ifdef __cplusplus\n}\n#endif\n\n#ifdef __cplusplus\n#define with_view_model_cpp(view, type, var, code, update)  \\\n    {                                                       \\\n        type var = static_cast<type>(view_get_model(view)); \\\n        {code};                                             \\\n        view_commit_model(view, update);                    \\\n    }\n#else\n/** With clause for view model\n *\n * @param      view           View instance pointer\n * @param      type           View model type\n * @param      code           Code block that will be executed between model lock and unlock\n * @param      update         Bool flag, if true, view will be updated after code block. Can be variable, so code block can decide if update is needed.\n *\n */\n#define with_view_model(view, type, code, update) \\\n    {                                             \\\n        type = view_get_model(view);              \\\n        {code};                                   \\\n        view_commit_model(view, update);          \\\n    }\n#endif\n"
  },
  {
    "path": "applications/services/gui/view_dispatcher.c",
    "content": "#include \"view_dispatcher_i.h\"\n\n#define TAG \"ViewDispatcher\"\n\n#define VIEW_DISPATCHER_QUEUE_LEN (16U)\n\nViewDispatcher* view_dispatcher_alloc(void) {\n    ViewDispatcher* dispatcher = view_dispatcher_alloc_ex(furi_event_loop_alloc());\n    dispatcher->is_event_loop_owned = true;\n    return dispatcher;\n}\n\nViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop) {\n    ViewDispatcher* view_dispatcher = malloc(sizeof(ViewDispatcher));\n\n    view_dispatcher->view_port = view_port_alloc();\n    view_port_draw_callback_set(\n        view_dispatcher->view_port, view_dispatcher_draw_callback, view_dispatcher);\n    view_port_input_callback_set(\n        view_dispatcher->view_port, view_dispatcher_input_callback, view_dispatcher);\n    view_port_enabled_set(view_dispatcher->view_port, false);\n\n    ViewDict_init(view_dispatcher->views);\n\n    view_dispatcher->event_loop = loop;\n\n    view_dispatcher->input_queue =\n        furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(InputEvent));\n    furi_event_loop_subscribe_message_queue(\n        view_dispatcher->event_loop,\n        view_dispatcher->input_queue,\n        FuriEventLoopEventIn,\n        view_dispatcher_run_input_callback,\n        view_dispatcher);\n\n    view_dispatcher->event_queue =\n        furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(uint32_t));\n    furi_event_loop_subscribe_message_queue(\n        view_dispatcher->event_loop,\n        view_dispatcher->event_queue,\n        FuriEventLoopEventIn,\n        view_dispatcher_run_event_callback,\n        view_dispatcher);\n\n    return view_dispatcher;\n}\n\nvoid view_dispatcher_free(ViewDispatcher* view_dispatcher) {\n    // Detach from gui\n    if(view_dispatcher->gui) {\n        gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port);\n    }\n    // Crash if not all views were freed\n    furi_check(!ViewDict_size(view_dispatcher->views));\n\n    ViewDict_clear(view_dispatcher->views);\n    // Free ViewPort\n    view_port_free(view_dispatcher->view_port);\n    // Free internal queue\n    furi_event_loop_unsubscribe(view_dispatcher->event_loop, view_dispatcher->input_queue);\n    furi_event_loop_unsubscribe(view_dispatcher->event_loop, view_dispatcher->event_queue);\n\n    furi_message_queue_free(view_dispatcher->input_queue);\n    furi_message_queue_free(view_dispatcher->event_queue);\n\n    if(view_dispatcher->is_event_loop_owned) furi_event_loop_free(view_dispatcher->event_loop);\n    // Free dispatcher\n    free(view_dispatcher);\n}\n\nvoid view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {\n    UNUSED(view_dispatcher);\n}\n\nvoid view_dispatcher_set_navigation_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherNavigationEventCallback callback) {\n    furi_check(view_dispatcher);\n    view_dispatcher->navigation_event_callback = callback;\n}\n\nvoid view_dispatcher_set_custom_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherCustomEventCallback callback) {\n    furi_check(view_dispatcher);\n    view_dispatcher->custom_event_callback = callback;\n}\n\nvoid view_dispatcher_set_tick_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherTickEventCallback callback,\n    uint32_t tick_period) {\n    furi_check(view_dispatcher);\n    furi_check(view_dispatcher->is_event_loop_owned);\n    view_dispatcher->tick_event_callback = callback;\n    view_dispatcher->tick_period = tick_period;\n}\n\nvoid view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) {\n    furi_check(view_dispatcher);\n    view_dispatcher->event_context = context;\n}\n\nFuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) {\n    furi_check(view_dispatcher);\n\n    return view_dispatcher->event_loop;\n}\n\nvoid view_dispatcher_run(ViewDispatcher* view_dispatcher) {\n    furi_check(view_dispatcher);\n\n    uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever :\n                                                               view_dispatcher->tick_period;\n\n    if(view_dispatcher->is_event_loop_owned)\n        furi_event_loop_tick_set(\n            view_dispatcher->event_loop,\n            tick_period,\n            view_dispatcher_handle_tick_event,\n            view_dispatcher);\n\n    furi_event_loop_run(view_dispatcher->event_loop);\n\n    // Wait till all input events delivered\n    InputEvent input;\n    while(view_dispatcher->ongoing_input) {\n        furi_message_queue_get(view_dispatcher->input_queue, &input, FuriWaitForever);\n        uint8_t key_bit = (1 << input.key);\n        if(input.type == InputTypePress) {\n            view_dispatcher->ongoing_input |= key_bit;\n        } else if(input.type == InputTypeRelease) {\n            view_dispatcher->ongoing_input &= ~key_bit;\n        }\n    }\n}\n\nvoid view_dispatcher_stop(ViewDispatcher* view_dispatcher) {\n    furi_check(view_dispatcher);\n    furi_event_loop_stop(view_dispatcher->event_loop);\n}\n\nvoid view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) {\n    furi_check(view_dispatcher);\n    furi_check(view);\n    // Check if view id is not used and register view\n    furi_check(ViewDict_get(view_dispatcher->views, view_id) == NULL);\n\n    // Lock gui\n    if(view_dispatcher->gui) {\n        gui_lock(view_dispatcher->gui);\n    }\n\n    ViewDict_set_at(view_dispatcher->views, view_id, view);\n    view_set_update_callback(view, view_dispatcher_update);\n    view_set_update_callback_context(view, view_dispatcher);\n\n    // Unlock gui\n    if(view_dispatcher->gui) {\n        gui_unlock(view_dispatcher->gui);\n    }\n}\n\nvoid view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id) {\n    furi_check(view_dispatcher);\n\n    // Lock gui\n    if(view_dispatcher->gui) {\n        gui_lock(view_dispatcher->gui);\n    }\n    // Get View by ID\n    View* view = *ViewDict_get(view_dispatcher->views, view_id);\n\n    // Disable the view if it is active\n    if(view_dispatcher->current_view == view) {\n        view_dispatcher_set_current_view(view_dispatcher, NULL);\n    }\n    // Check if view is receiving input\n    if(view_dispatcher->ongoing_input_view == view) {\n        view_dispatcher->ongoing_input_view = NULL;\n    }\n    // Remove view\n    furi_check(ViewDict_erase(view_dispatcher->views, view_id));\n\n    view_set_update_callback(view, NULL);\n    view_set_update_callback_context(view, NULL);\n\n    // Unlock gui\n    if(view_dispatcher->gui) {\n        gui_unlock(view_dispatcher->gui);\n    }\n}\n\nvoid view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id) {\n    furi_check(view_dispatcher);\n    if(view_id == VIEW_NONE) {\n        view_dispatcher_set_current_view(view_dispatcher, NULL);\n    } else if(view_id == VIEW_IGNORE) {\n    } else {\n        View** view_pp = ViewDict_get(view_dispatcher->views, view_id);\n        furi_check(view_pp != NULL);\n        view_dispatcher_set_current_view(view_dispatcher, *view_pp);\n    }\n}\n\nvoid view_dispatcher_send_to_front(ViewDispatcher* view_dispatcher) {\n    furi_check(view_dispatcher);\n    furi_check(view_dispatcher->gui);\n    gui_view_port_send_to_front(view_dispatcher->gui, view_dispatcher->view_port);\n}\n\nvoid view_dispatcher_send_to_back(ViewDispatcher* view_dispatcher) {\n    furi_check(view_dispatcher);\n    furi_check(view_dispatcher->gui);\n    gui_view_port_send_to_back(view_dispatcher->gui, view_dispatcher->view_port);\n}\n\nvoid view_dispatcher_attach_to_gui(\n    ViewDispatcher* view_dispatcher,\n    Gui* gui,\n    ViewDispatcherType type) {\n    furi_check(view_dispatcher);\n    furi_check(view_dispatcher->gui == NULL);\n    furi_check(gui);\n\n    if(type == ViewDispatcherTypeDesktop) {\n        gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerDesktop);\n    } else if(type == ViewDispatcherTypeWindow) {\n        gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerWindow);\n    } else if(type == ViewDispatcherTypeFullscreen) {\n        gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerFullscreen);\n    } else {\n        furi_crash();\n    }\n    view_dispatcher->gui = gui;\n}\n\nvoid view_dispatcher_draw_callback(Canvas* canvas, void* context) {\n    ViewDispatcher* view_dispatcher = context;\n    if(view_dispatcher->current_view) {\n        view_draw(view_dispatcher->current_view, canvas);\n    }\n}\n\nvoid view_dispatcher_input_callback(InputEvent* event, void* context) {\n    ViewDispatcher* view_dispatcher = context;\n    furi_check(\n        furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) ==\n        FuriStatusOk);\n}\n\nvoid view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) {\n    // Check input complementarity\n    uint8_t key_bit = (1 << event->key);\n    if(event->type == InputTypePress) {\n        view_dispatcher->ongoing_input |= key_bit;\n    } else if(event->type == InputTypeRelease) {\n        view_dispatcher->ongoing_input &= ~key_bit;\n    } else if(!(view_dispatcher->ongoing_input & key_bit)) {\n        FURI_LOG_D(\n            TAG,\n            \"non-complementary input, discarding key: %s, type: %s, sequence: %p\",\n            input_get_key_name(event->key),\n            input_get_type_name(event->type),\n            (void*)event->sequence);\n        return;\n    }\n\n    // Set ongoing input view if this is event is first press event\n    if(!(view_dispatcher->ongoing_input & ~key_bit) && event->type == InputTypePress) {\n        view_dispatcher->ongoing_input_view = view_dispatcher->current_view;\n    }\n\n    // Deliver event\n    if(view_dispatcher->current_view &&\n       view_dispatcher->ongoing_input_view == view_dispatcher->current_view) {\n        // Dispatch input to current view\n        bool is_consumed = view_input(view_dispatcher->current_view, event);\n\n        // Navigate if input is not consumed\n        if(!is_consumed && (event->key == InputKeyBack) &&\n           (event->type == InputTypeShort || event->type == InputTypeLong)) {\n            // Navigate to previous\n            uint32_t view_id = view_previous(view_dispatcher->current_view);\n            if(view_id != VIEW_IGNORE) {\n                // Switch to returned view\n                view_dispatcher_switch_to_view(view_dispatcher, view_id);\n            } else if(view_dispatcher->navigation_event_callback) {\n                // Dispatch navigation event\n                if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) {\n                    view_dispatcher_stop(view_dispatcher);\n                    return;\n                }\n            }\n        }\n    } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {\n        FURI_LOG_D(\n            TAG,\n            \"View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view\",\n            view_dispatcher->ongoing_input_view,\n            view_dispatcher->current_view,\n            input_get_key_name(event->key),\n            input_get_type_name(event->type),\n            (void*)event->sequence);\n        view_input(view_dispatcher->ongoing_input_view, event);\n    }\n}\n\nvoid view_dispatcher_handle_tick_event(void* context) {\n    ViewDispatcher* view_dispatcher = context;\n    if(view_dispatcher->tick_event_callback) {\n        view_dispatcher->tick_event_callback(view_dispatcher->event_context);\n    }\n}\n\nvoid view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {\n    bool is_consumed = false;\n    if(view_dispatcher->current_view) {\n        is_consumed = view_custom(view_dispatcher->current_view, event);\n    }\n    // If custom event is not consumed in View, call callback\n    if(!is_consumed && view_dispatcher->custom_event_callback) {\n        view_dispatcher->custom_event_callback(view_dispatcher->event_context, event);\n    }\n}\n\nvoid view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {\n    furi_check(view_dispatcher);\n\n    furi_check(\n        furi_message_queue_put(view_dispatcher->event_queue, &event, FuriWaitForever) ==\n        FuriStatusOk);\n}\n\nstatic const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = {\n    [ViewOrientationVertical] = ViewPortOrientationVertical,\n    [ViewOrientationVerticalFlip] = ViewPortOrientationVerticalFlip,\n    [ViewOrientationHorizontal] = ViewPortOrientationHorizontal,\n    [ViewOrientationHorizontalFlip] = ViewPortOrientationHorizontalFlip,\n};\n\nvoid view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view) {\n    furi_check(view_dispatcher);\n    // Dispatch view exit event\n    if(view_dispatcher->current_view) {\n        view_exit(view_dispatcher->current_view);\n    }\n    // Set current view\n    view_dispatcher->current_view = view;\n    // Dispatch view enter event\n    if(view_dispatcher->current_view) {\n        ViewPortOrientation orientation =\n            view_dispatcher_view_port_orientation_table[view->orientation];\n        if(view_port_get_orientation(view_dispatcher->view_port) != orientation) {\n            view_port_set_orientation(view_dispatcher->view_port, orientation);\n            // we just rotated input keys, now it's time to sacrifice some input\n            view_dispatcher->ongoing_input = 0;\n        }\n        view_enter(view_dispatcher->current_view);\n        view_port_enabled_set(view_dispatcher->view_port, true);\n        view_port_update(view_dispatcher->view_port);\n    } else {\n        view_port_enabled_set(view_dispatcher->view_port, false);\n        view_dispatcher_stop(view_dispatcher);\n    }\n}\n\nvoid view_dispatcher_update(View* view, void* context) {\n    furi_check(view);\n    furi_check(context);\n\n    ViewDispatcher* view_dispatcher = context;\n\n    if(view_dispatcher->current_view == view) {\n        view_port_update(view_dispatcher->view_port);\n    }\n}\n\nvoid view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    ViewDispatcher* instance = context;\n    furi_assert(instance->event_queue == object);\n\n    uint32_t event;\n    furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);\n    view_dispatcher_handle_custom_event(instance, event);\n}\n\nvoid view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    ViewDispatcher* instance = context;\n    furi_assert(instance->input_queue == object);\n\n    InputEvent input;\n    furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);\n    view_dispatcher_handle_input(instance, &input);\n}\n"
  },
  {
    "path": "applications/services/gui/view_dispatcher.h",
    "content": "/**\n * @file view_dispatcher.h\n * @brief GUI: ViewDispatcher API\n *\n * ViewDispatcher is used to connect several Views to a Gui instance, switch between them and handle various events.\n * This is useful in applications featuring an advanced graphical user interface.\n *\n * Internally, ViewDispatcher employs a FuriEventLoop instance together with two separate\n * message queues for input and custom event handling. See FuriEventLoop for more information.\n *\n * If no multi-view or complex event handling capabilities are required, consider using ViewHolder instead.\n *\n * @warning Views added to a ViewDispatcher MUST NOT be in a ViewStack at the same time.\n */\n\n#pragma once\n\n#include \"view.h\"\n#include \"gui.h\"\n#include \"scene_manager.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** ViewDispatcher view_port placement */\ntypedef enum {\n    ViewDispatcherTypeDesktop, /**< Desktop layer: fullscreen with status bar on top of it. For internal usage. */\n    ViewDispatcherTypeWindow, /**< Window layer: with status bar  */\n    ViewDispatcherTypeFullscreen /**< Fullscreen layer: without status bar */\n} ViewDispatcherType;\n\ntypedef struct ViewDispatcher ViewDispatcher;\n\n/** Prototype for custom event callback */\ntypedef bool (*ViewDispatcherCustomEventCallback)(void* context, uint32_t event);\n\n/** Prototype for navigation event callback */\ntypedef bool (*ViewDispatcherNavigationEventCallback)(void* context);\n\n/** Prototype for tick event callback */\ntypedef void (*ViewDispatcherTickEventCallback)(void* context);\n\n/** Allocate ViewDispatcher instance\n *\n * @return     pointer to ViewDispatcher instance\n */\nViewDispatcher* view_dispatcher_alloc(void);\n\n/** Allocate ViewDispatcher instance with an externally owned event loop. If\n * this constructor is used instead of `view_dispatcher_alloc`, the burden of\n * freeing the event loop is placed on the caller.\n *\n * @param loop pointer to FuriEventLoop instance\n * @return     pointer to ViewDispatcher instance\n */\nViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop);\n\n/** Free ViewDispatcher instance\n *\n * @warning All added views MUST be removed using view_dispatcher_remove_view()\n *          before calling this function.\n *\n * @param      view_dispatcher  pointer to ViewDispatcher\n */\nvoid view_dispatcher_free(ViewDispatcher* view_dispatcher);\n\n/** Enable queue support\n *\n * @deprecated Do NOT use in new code and remove all calls to it from existing code.\n *             The queue support is now always enabled during construction. If no queue support\n *             is required, consider using ViewHolder instead.\n *\n * @param      view_dispatcher  ViewDispatcher instance\n */\nFURI_DEPRECATED void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher);\n\n/** Send custom event\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param[in]  event            The event\n */\nvoid view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event);\n\n/** Set custom event handler\n *\n * Called on Custom Event, if it is not consumed by view\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      callback         ViewDispatcherCustomEventCallback instance\n */\nvoid view_dispatcher_set_custom_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherCustomEventCallback callback);\n\n/** Set navigation event handler\n *\n * Called on Input Short Back Event, if it is not consumed by view\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      callback         ViewDispatcherNavigationEventCallback instance\n */\nvoid view_dispatcher_set_navigation_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherNavigationEventCallback callback);\n\n/** Set tick event handler\n *\n * @warning Requires the event loop to be owned by the view dispatcher, i.e.\n * it should have been instantiated with `view_dispatcher_alloc`, not\n * `view_dispatcher_alloc_ex`.\n * \n * @param      view_dispatcher  ViewDispatcher instance\n * @param      callback         ViewDispatcherTickEventCallback\n * @param      tick_period      callback call period\n */\nvoid view_dispatcher_set_tick_event_callback(\n    ViewDispatcher* view_dispatcher,\n    ViewDispatcherTickEventCallback callback,\n    uint32_t tick_period);\n\n/** Set event callback context\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      context          pointer to context\n */\nvoid view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context);\n\n/** Get event_loop instance\n *\n * Use the return value to connect additional supported primitives (message queues, timers, etc)\n * to this ViewDispatcher instance's event loop.\n *\n * @warning Do NOT call furi_event_loop_run() on the returned instance, it is done internally\n *          in the view_dispatcher_run() call.\n *\n * @param      view_dispatcher  ViewDispatcher instance\n *\n * @return     The event_loop instance.\n */\nFuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher);\n\n/** Run ViewDispatcher\n *\n * This function will start the event loop and block until view_dispatcher_stop() is called\n * or the current thread receives a FuriSignalExit signal.\n *\n * @param      view_dispatcher  ViewDispatcher instance\n */\nvoid view_dispatcher_run(ViewDispatcher* view_dispatcher);\n\n/** Stop ViewDispatcher\n *\n * @param      view_dispatcher  ViewDispatcher instance\n */\nvoid view_dispatcher_stop(ViewDispatcher* view_dispatcher);\n\n/** Add view to ViewDispatcher\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      view_id          View id to register\n * @param      view             View instance\n */\nvoid view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view);\n\n/** Remove view from ViewDispatcher\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      view_id          View id to remove\n */\nvoid view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id);\n\n/** Switch to View\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      view_id          View id to register\n * @warning    switching may be delayed till input events complementarity\n *             reached\n */\nvoid view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id);\n\n/** Send ViewPort of this ViewDispatcher instance to front\n *\n * @param      view_dispatcher  ViewDispatcher instance\n */\nvoid view_dispatcher_send_to_front(ViewDispatcher* view_dispatcher);\n\n/** Send ViewPort of this ViewDispatcher instance to back\n *\n * @param      view_dispatcher  ViewDispatcher instance\n */\nvoid view_dispatcher_send_to_back(ViewDispatcher* view_dispatcher);\n\n/** Attach ViewDispatcher to GUI\n *\n * @param      view_dispatcher  ViewDispatcher instance\n * @param      gui              GUI instance to attach to\n * @param[in]  type             The type\n */\nvoid view_dispatcher_attach_to_gui(\n    ViewDispatcher* view_dispatcher,\n    Gui* gui,\n    ViewDispatcherType type);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/view_dispatcher_i.h",
    "content": "/**\n * @file view_dispatcher_i.h\n * GUI: ViewDispatcher API\n */\n\n#pragma once\n\n#include <m-dict.h>\n\n#include \"view_dispatcher.h\"\n#include \"view_i.h\"\n#include \"gui_i.h\"\n\nDICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) // NOLINT\n\nstruct ViewDispatcher {\n    bool is_event_loop_owned;\n    FuriEventLoop* event_loop;\n    FuriMessageQueue* input_queue;\n    FuriMessageQueue* event_queue;\n\n    Gui* gui;\n    ViewPort* view_port;\n    ViewDict_t views;\n\n    View* current_view;\n\n    View* ongoing_input_view;\n    uint8_t ongoing_input;\n\n    ViewDispatcherCustomEventCallback custom_event_callback;\n    ViewDispatcherNavigationEventCallback navigation_event_callback;\n    ViewDispatcherTickEventCallback tick_event_callback;\n    uint32_t tick_period;\n    void* event_context;\n};\n\n/** ViewPort Draw Callback */\nvoid view_dispatcher_draw_callback(Canvas* canvas, void* context);\n\n/** ViewPort Input Callback */\nvoid view_dispatcher_input_callback(InputEvent* event, void* context);\n\n/** Input handler */\nvoid view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event);\n\n/** Tick handler */\nvoid view_dispatcher_handle_tick_event(void* context);\n\n/** Custom event handler */\nvoid view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event);\n\n/** Set current view, dispatches view enter and exit */\nvoid view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view);\n\n/** ViewDispatcher update event */\nvoid view_dispatcher_update(View* view, void* context);\n\n/** ViewDispatcher run event loop event callback */\nvoid view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);\n\n/** ViewDispatcher run event loop input callback */\nvoid view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);\n"
  },
  {
    "path": "applications/services/gui/view_holder.c",
    "content": "#include \"view_holder.h\"\n#include <gui/view_i.h>\n\n#define TAG \"ViewHolder\"\n\nstruct ViewHolder {\n    View* view;\n    ViewPort* view_port;\n    Gui* gui;\n\n    FreeCallback free_callback;\n    void* free_context;\n\n    BackCallback back_callback;\n    void* back_context;\n\n    uint8_t ongoing_input;\n};\n\nstatic void view_holder_draw_callback(Canvas* canvas, void* context);\nstatic void view_holder_input_callback(InputEvent* event, void* context);\n\nViewHolder* view_holder_alloc(void) {\n    ViewHolder* view_holder = malloc(sizeof(ViewHolder));\n\n    view_holder->view_port = view_port_alloc();\n    view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder);\n    view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder);\n    view_port_enabled_set(view_holder->view_port, false);\n\n    return view_holder;\n}\n\nvoid view_holder_free(ViewHolder* view_holder) {\n    furi_check(view_holder);\n    furi_check(view_holder->view == NULL);\n\n    if(view_holder->gui) {\n        gui_remove_view_port(view_holder->gui, view_holder->view_port);\n    }\n\n    view_port_free(view_holder->view_port);\n\n    if(view_holder->free_callback) {\n        view_holder->free_callback(view_holder->free_context);\n    }\n\n    free(view_holder);\n}\n\nvoid view_holder_set_view(ViewHolder* view_holder, View* view) {\n    furi_check(view_holder);\n\n    if(view_holder->view) {\n        while(view_holder->ongoing_input) {\n            furi_delay_tick(1);\n        }\n\n        view_exit(view_holder->view);\n        view_set_update_callback(view_holder->view, NULL);\n        view_set_update_callback_context(view_holder->view, NULL);\n    }\n\n    view_holder->view = view;\n\n    if(view_holder->view) {\n        const ViewPortOrientation orientation = (ViewPortOrientation)view->orientation;\n        furi_assert(orientation < ViewPortOrientationMAX);\n        if(view_port_get_orientation(view_holder->view_port) != orientation) {\n            view_port_set_orientation(view_holder->view_port, orientation);\n            // we just rotated input keys, now it's time to sacrifice some input\n            view_holder->ongoing_input = 0;\n        }\n\n        view_set_update_callback(view_holder->view, view_holder_update);\n        view_set_update_callback_context(view_holder->view, view_holder);\n\n        view_enter(view_holder->view);\n        view_port_enabled_set(view_holder->view_port, true);\n        view_port_update(view_holder->view_port);\n\n    } else {\n        view_port_enabled_set(view_holder->view_port, false);\n    }\n}\n\nvoid view_holder_set_free_callback(\n    ViewHolder* view_holder,\n    FreeCallback free_callback,\n    void* free_context) {\n    furi_check(view_holder);\n    view_holder->free_callback = free_callback;\n    view_holder->free_context = free_context;\n}\n\nvoid* view_holder_get_free_context(ViewHolder* view_holder) {\n    return view_holder->free_context;\n}\n\nvoid view_holder_set_back_callback(\n    ViewHolder* view_holder,\n    BackCallback back_callback,\n    void* back_context) {\n    furi_check(view_holder);\n    view_holder->back_callback = back_callback;\n    view_holder->back_context = back_context;\n}\n\nvoid view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) {\n    furi_check(view_holder);\n    furi_check(view_holder->gui == NULL);\n    furi_check(gui);\n    gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen);\n    view_holder->gui = gui;\n}\n\nvoid view_holder_update(View* view, void* context) {\n    furi_check(view);\n    furi_check(context);\n\n    ViewHolder* view_holder = context;\n    if(view == view_holder->view) {\n        view_port_update(view_holder->view_port);\n    }\n}\n\nvoid view_holder_send_to_front(ViewHolder* view_holder) {\n    furi_check(view_holder);\n    furi_check(view_holder->gui);\n    gui_view_port_send_to_front(view_holder->gui, view_holder->view_port);\n}\n\nvoid view_holder_send_to_back(ViewHolder* view_holder) {\n    furi_check(view_holder);\n    furi_check(view_holder->gui);\n    gui_view_port_send_to_back(view_holder->gui, view_holder->view_port);\n}\n\nstatic void view_holder_draw_callback(Canvas* canvas, void* context) {\n    ViewHolder* view_holder = context;\n    if(view_holder->view) {\n        view_draw(view_holder->view, canvas);\n    }\n}\n\nstatic void view_holder_input_callback(InputEvent* event, void* context) {\n    ViewHolder* view_holder = context;\n\n    uint8_t key_bit = (1 << event->key);\n    if(event->type == InputTypePress) {\n        view_holder->ongoing_input |= key_bit;\n    } else if(event->type == InputTypeRelease) {\n        view_holder->ongoing_input &= ~key_bit;\n    } else if(!(view_holder->ongoing_input & key_bit)) {\n        FURI_LOG_W(\n            TAG,\n            \"non-complementary input, discarding key: %s, type: %s\",\n            input_get_key_name(event->key),\n            input_get_type_name(event->type));\n        return;\n    }\n\n    bool is_consumed = false;\n\n    if(view_holder->view) {\n        is_consumed = view_input(view_holder->view, event);\n    }\n\n    if(!is_consumed && event->type == InputTypeShort) {\n        if(event->key == InputKeyBack) {\n            if(view_holder->back_callback) {\n                view_holder->back_callback(view_holder->back_context);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "applications/services/gui/view_holder.h",
    "content": "/**\n * @file view_holder.h\n * @brief GUI: ViewHolder API\n *\n * ViewHolder is used to connect a single View to a Gui instance. This is useful in smaller applications\n * with a simple user interface. If advanced view switching capabilites are required, consider using ViewDispatcher instead.\n *\n * @warning Views added to a ViewHolder MUST NOT be in a ViewStack at the same time.\n */\n#pragma once\n\n#include <gui/view.h>\n#include <gui/gui.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct ViewHolder ViewHolder;\n\n/** \n * @brief Free callback type\n */\ntypedef void (*FreeCallback)(void* free_context);\n\n/** \n * @brief Back callback type\n *\n * @warning Will be called from the GUI thread\n */\ntypedef void (*BackCallback)(void* back_context);\n\n/**\n * @brief Allocate ViewHolder\n * @return pointer to ViewHolder instance\n */\nViewHolder* view_holder_alloc(void);\n\n/**\n * @brief Free ViewHolder and call Free callback\n *\n * @warning The current view must be unset prior to freeing a ViewHolder instance.\n *\n * @param view_holder pointer to ViewHolder\n */\nvoid view_holder_free(ViewHolder* view_holder);\n\n/**\n * @brief Set view for ViewHolder\n *\n * Pass NULL as the view parameter to unset the current view.\n * \n * @param view_holder ViewHolder instance\n * @param view View instance\n */\nvoid view_holder_set_view(ViewHolder* view_holder, View* view);\n\n/**\n * @brief Set Free callback\n * \n * @param view_holder ViewHolder instance\n * @param free_callback callback pointer\n * @param free_context callback context\n */\nvoid view_holder_set_free_callback(\n    ViewHolder* view_holder,\n    FreeCallback free_callback,\n    void* free_context);\n\n/**\n * @brief Free callback context getter.\n *\n * Useful if your Free callback is a module destructor, so you can get an instance of the module using this method.\n * \n * @param view_holder ViewHolder instance\n * @return void* free callback context\n */\nvoid* view_holder_get_free_context(ViewHolder* view_holder);\n\n/**\n * @brief Set the back key callback.\n *\n * The callback function will be called if the user has pressed the Back key\n * and the current view did not handle this event.\n *\n * @param view_holder ViewHolder instance\n * @param back_callback pointer to the callback function\n * @param back_context pointer to a user-specific object, can be NULL\n */\nvoid view_holder_set_back_callback(\n    ViewHolder* view_holder,\n    BackCallback back_callback,\n    void* back_context);\n\n/**\n * @brief Attach ViewHolder to GUI\n * \n * @param view_holder ViewHolder instance\n * @param gui GUI instance to attach to\n */\nvoid view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui);\n\n/**\n * @brief View Update Handler\n *\n * @param view View Instance\n * @param context ViewHolder instance\n */\nvoid view_holder_update(View* view, void* context);\n\n/**\n * @brief Send ViewPort of this ViewHolder instance to front\n *\n * @param view_holder ViewHolder instance\n */\nvoid view_holder_send_to_front(ViewHolder* view_holder);\n\n/**\n * @brief Send ViewPort of this ViewHolder instance to back\n *\n * @param view_holder ViewHolder instance\n */\nvoid view_holder_send_to_back(ViewHolder* view_holder);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/view_i.h",
    "content": "/**\n * @file view_i.h\n * GUI: internal View API\n */\n\n#pragma once\n\n#include \"view.h\"\n#include <furi.h>\n\ntypedef struct {\n    FuriMutex* mutex;\n    uint8_t data[];\n} ViewModelLocking;\n\nstruct View {\n    ViewDrawCallback draw_callback;\n    ViewInputCallback input_callback;\n    ViewCustomCallback custom_callback;\n\n    ViewModelType model_type;\n    ViewNavigationCallback previous_callback;\n    ViewCallback enter_callback;\n    ViewCallback exit_callback;\n    ViewOrientation orientation;\n\n    ViewUpdateCallback update_callback;\n    void* update_callback_context;\n\n    void* model;\n    void* context;\n};\n\n/** IconAnimation tie callback */\nvoid view_icon_animation_callback(IconAnimation* instance, void* context);\n\n/** Unlock model */\nvoid view_unlock_model(View* view);\n\n/** Draw Callback for View dispatcher */\nvoid view_draw(View* view, Canvas* canvas);\n\n/** Input Callback for View dispatcher */\nbool view_input(View* view, InputEvent* event);\n\n/** Custom Callback for View dispatcher */\nbool view_custom(View* view, uint32_t event);\n\n/** Previous Callback for View dispatcher */\nuint32_t view_previous(View* view);\n\n/** Enter Callback for View dispatcher */\nvoid view_enter(View* view);\n\n/** Exit Callback for View dispatcher */\nvoid view_exit(View* view);\n"
  },
  {
    "path": "applications/services/gui/view_port.c",
    "content": "#include \"view_port_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include \"gui.h\"\n#include \"gui_i.h\"\n\n#define TAG \"ViewPort\"\n\n_Static_assert(ViewPortOrientationMAX == 4, \"Incorrect ViewPortOrientation count\");\n_Static_assert(\n    (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&\n     ViewPortOrientationVertical == 2 && ViewPortOrientationVerticalFlip == 3),\n    \"Incorrect ViewPortOrientation order\");\n_Static_assert(InputKeyMAX == 6, \"Incorrect InputKey count\");\n_Static_assert(\n    (InputKeyUp == 0 && InputKeyDown == 1 && InputKeyRight == 2 && InputKeyLeft == 3 &&\n     InputKeyOk == 4 && InputKeyBack == 5),\n    \"Incorrect InputKey order\");\n\n/** InputKey directional keys mappings for different screen orientations\n* \n*/\nstatic const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMAX] = {\n    {InputKeyUp,\n     InputKeyDown,\n     InputKeyRight,\n     InputKeyLeft,\n     InputKeyOk,\n     InputKeyBack}, //ViewPortOrientationHorizontal\n    {InputKeyDown,\n     InputKeyUp,\n     InputKeyLeft,\n     InputKeyRight,\n     InputKeyOk,\n     InputKeyBack}, //ViewPortOrientationHorizontalFlip\n    {InputKeyRight,\n     InputKeyLeft,\n     InputKeyDown,\n     InputKeyUp,\n     InputKeyOk,\n     InputKeyBack}, //ViewPortOrientationVertical\n    {InputKeyLeft,\n     InputKeyRight,\n     InputKeyUp,\n     InputKeyDown,\n     InputKeyOk,\n     InputKeyBack}, //ViewPortOrientationVerticalFlip\n};\n\nstatic const InputKey view_port_left_hand_input_mapping[InputKeyMAX] =\n    {InputKeyDown, InputKeyUp, InputKeyLeft, InputKeyRight, InputKeyOk, InputKeyBack};\n\nstatic const CanvasOrientation view_port_orientation_mapping[ViewPortOrientationMAX] = {\n    [ViewPortOrientationHorizontal] = CanvasOrientationHorizontal,\n    [ViewPortOrientationHorizontalFlip] = CanvasOrientationHorizontalFlip,\n    [ViewPortOrientationVertical] = CanvasOrientationVertical,\n    [ViewPortOrientationVerticalFlip] = CanvasOrientationVerticalFlip,\n};\n\n// Remaps directional pad buttons on Flipper based on ViewPort orientation\nstatic void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) {\n    furi_check(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX);\n\n    if(event->sequence_source != INPUT_SEQUENCE_SOURCE_HARDWARE) {\n        return;\n    }\n\n    if(orientation == ViewPortOrientationHorizontal ||\n       orientation == ViewPortOrientationHorizontalFlip) {\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) {\n            event->key = view_port_left_hand_input_mapping[event->key];\n        }\n    }\n    event->key = view_port_input_mapping[orientation][event->key];\n}\n\nstatic void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) {\n    CanvasOrientation orientation = view_port_orientation_mapping[view_port->orientation];\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) {\n        if(orientation == CanvasOrientationHorizontal) {\n            orientation = CanvasOrientationHorizontalFlip;\n        } else if(orientation == CanvasOrientationHorizontalFlip) {\n            orientation = CanvasOrientationHorizontal;\n        }\n    }\n\n    canvas_set_orientation(canvas, orientation);\n}\n\nViewPort* view_port_alloc(void) {\n    ViewPort* view_port = malloc(sizeof(ViewPort));\n    view_port->orientation = ViewPortOrientationHorizontal;\n    view_port->is_enabled = true;\n    view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);\n    return view_port;\n}\n\nvoid view_port_free(ViewPort* view_port) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    furi_check(view_port->gui == NULL);\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n    furi_mutex_free(view_port->mutex);\n    free(view_port);\n}\n\nvoid view_port_set_width(ViewPort* view_port, uint8_t width) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->width = width;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nuint8_t view_port_get_width(const ViewPort* view_port) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    uint8_t width = view_port->width;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n    return width;\n}\n\nvoid view_port_set_height(ViewPort* view_port, uint8_t height) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->height = height;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nuint8_t view_port_get_height(const ViewPort* view_port) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    uint8_t height = view_port->height;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n    return height;\n}\n\nvoid view_port_enabled_set(ViewPort* view_port, bool enabled) {\n    furi_check(view_port);\n\n    // We are not going to lockup system, but will notify you instead\n    // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call\n    if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {\n        FURI_LOG_W(TAG, \"ViewPort lockup: see %s:%d\", __FILE__, __LINE__ - 3);\n    }\n    if(view_port->is_enabled != enabled) {\n        view_port->is_enabled = enabled;\n        if(view_port->gui) gui_update(view_port->gui);\n    }\n    furi_mutex_release(view_port->mutex);\n}\n\nbool view_port_is_enabled(const ViewPort* view_port) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    bool is_enabled = view_port->is_enabled;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n    return is_enabled;\n}\n\nvoid view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->draw_callback = callback;\n    view_port->draw_callback_context = context;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nvoid view_port_input_callback_set(\n    ViewPort* view_port,\n    ViewPortInputCallback callback,\n    void* context) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->input_callback = callback;\n    view_port->input_callback_context = context;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nvoid view_port_update(ViewPort* view_port) {\n    furi_check(view_port);\n\n    // We are not going to lockup system, but will notify you instead\n    // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call\n    if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {\n        FURI_LOG_W(TAG, \"ViewPort lockup: see %s:%d\", __FILE__, __LINE__ - 3);\n    }\n\n    if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);\n    furi_mutex_release(view_port->mutex);\n}\n\nvoid view_port_gui_set(ViewPort* view_port, Gui* gui) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->gui = gui;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nvoid view_port_draw(ViewPort* view_port, Canvas* canvas) {\n    furi_check(view_port);\n    furi_check(canvas);\n\n    // We are not going to lockup system, but will notify you instead\n    // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call\n    if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {\n        FURI_LOG_W(TAG, \"ViewPort lockup: see %s:%d\", __FILE__, __LINE__ - 3);\n    }\n\n    furi_check(view_port->gui);\n\n    if(view_port->draw_callback) {\n        view_port_setup_canvas_orientation(view_port, canvas);\n        view_port->draw_callback(canvas, view_port->draw_callback_context);\n    }\n\n    furi_mutex_release(view_port->mutex);\n}\n\nvoid view_port_input(ViewPort* view_port, InputEvent* event) {\n    furi_check(view_port);\n    furi_check(event);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    furi_check(view_port->gui);\n\n    if(view_port->input_callback) {\n        ViewPortOrientation orientation = view_port_get_orientation(view_port);\n        view_port_map_input(event, orientation);\n        view_port->input_callback(event, view_port->input_callback_context);\n    }\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nvoid view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) {\n    furi_check(view_port);\n    furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);\n    view_port->orientation = orientation;\n    furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);\n}\n\nViewPortOrientation view_port_get_orientation(const ViewPort* view_port) {\n    furi_check(view_port);\n    // We are not going to lockup system, but will notify you instead\n    // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call\n    if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {\n        FURI_LOG_W(TAG, \"ViewPort lockup: see %s:%d\", __FILE__, __LINE__ - 3);\n    }\n    ViewPortOrientation orientation = view_port->orientation;\n    furi_mutex_release(view_port->mutex);\n    return orientation;\n}\n"
  },
  {
    "path": "applications/services/gui/view_port.h",
    "content": "/**\n * @file view_port.h\n * GUI: ViewPort API\n */\n\n#pragma once\n\n#include <input/input.h>\n#include \"canvas.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct ViewPort ViewPort;\n\ntypedef enum {\n    ViewPortOrientationHorizontal,\n    ViewPortOrientationHorizontalFlip,\n    ViewPortOrientationVertical,\n    ViewPortOrientationVerticalFlip,\n    ViewPortOrientationMAX, /**< Special value, don't use it */\n} ViewPortOrientation;\n\n/** ViewPort Draw callback\n * @warning    called from GUI thread\n */\ntypedef void (*ViewPortDrawCallback)(Canvas* canvas, void* context);\n\n/** ViewPort Input callback\n * @warning    called from GUI thread\n */\ntypedef void (*ViewPortInputCallback)(InputEvent* event, void* context);\n\n/** ViewPort allocator\n *\n * always returns view_port or stops system if not enough memory.\n *\n * @return     ViewPort instance\n */\nViewPort* view_port_alloc(void);\n\n/** ViewPort deallocator\n *\n * Ensure that view_port was unregistered in GUI system before use.\n *\n * @param      view_port  ViewPort instance\n */\nvoid view_port_free(ViewPort* view_port);\n\n/** Set view_port width.\n *\n * Will be used to limit canvas drawing area and autolayout feature.\n *\n * @param      view_port  ViewPort instance\n * @param      width      wanted width, 0 - auto.\n */\nvoid view_port_set_width(ViewPort* view_port, uint8_t width);\nuint8_t view_port_get_width(const ViewPort* view_port);\n\n/** Set view_port height.\n *\n * Will be used to limit canvas drawing area and autolayout feature.\n *\n * @param      view_port  ViewPort instance\n * @param      height     wanted height, 0 - auto.\n */\nvoid view_port_set_height(ViewPort* view_port, uint8_t height);\nuint8_t view_port_get_height(const ViewPort* view_port);\n\n/** Enable or disable view_port rendering.\n *\n * @param      view_port  ViewPort instance\n * @param      enabled    Indicates if enabled\n * @warning    automatically dispatches update event\n */\nvoid view_port_enabled_set(ViewPort* view_port, bool enabled);\nbool view_port_is_enabled(const ViewPort* view_port);\n\n/** ViewPort event callbacks\n *\n * @param      view_port  ViewPort instance\n * @param      callback   appropriate callback function\n * @param      context    context to pass to callback\n */\nvoid view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context);\nvoid view_port_input_callback_set(\n    ViewPort* view_port,\n    ViewPortInputCallback callback,\n    void* context);\n\n/** Emit update signal to GUI system.\n *\n * Rendering will happen later after GUI system process signal.\n *\n * @param      view_port  ViewPort instance\n */\nvoid view_port_update(ViewPort* view_port);\n\n/** Set ViewPort orientation.\n *\n * @param      view_port    ViewPort instance\n * @param      orientation  display orientation, horizontal or vertical.\n */\nvoid view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation);\nViewPortOrientation view_port_get_orientation(const ViewPort* view_port);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/gui/view_port_i.h",
    "content": "/**\n * @file view_port_i.h\n * GUI: internal ViewPort API\n */\n\n#pragma once\n\n#include \"gui_i.h\"\n#include \"view_port.h\"\n\nstruct ViewPort {\n    Gui* gui;\n    FuriMutex* mutex;\n    bool is_enabled;\n    ViewPortOrientation orientation;\n\n    uint8_t width;\n    uint8_t height;\n\n    ViewPortDrawCallback draw_callback;\n    void* draw_callback_context;\n\n    ViewPortInputCallback input_callback;\n    void* input_callback_context;\n};\n\n/** Set GUI reference.\n *\n * To be used by GUI, called upon view_port tree insert\n *\n * @param      view_port  ViewPort instance\n * @param      gui        gui instance pointer\n */\nvoid view_port_gui_set(ViewPort* view_port, Gui* gui);\n\n/** Process draw call. Calls draw callback.\n *\n * To be used by GUI, called on tree redraw.\n *\n * @param      view_port  ViewPort instance\n * @param      canvas     canvas to draw at\n */\nvoid view_port_draw(ViewPort* view_port, Canvas* canvas);\n\n/** Process input. Calls input callback.\n *\n * To be used by GUI, called on input dispatch.\n *\n * @param      view_port  ViewPort instance\n * @param      event      pointer to input event\n */\nvoid view_port_input(ViewPort* view_port, InputEvent* event);\n"
  },
  {
    "path": "applications/services/gui/view_stack.c",
    "content": "#include \"view_stack.h\"\n#include \"view_i.h\"\n\n#include <gui/view.h>\n#include <core/memmgr.h>\n\n#define MAX_VIEWS 3\n\ntypedef struct {\n    View* views[MAX_VIEWS];\n} ViewStackModel;\n\nstruct ViewStack {\n    View* view;\n};\n\nstatic void view_stack_draw(Canvas* canvas, void* model);\nstatic bool view_stack_input(InputEvent* event, void* context);\n\nstatic void view_stack_update_callback(View* view_top_or_bottom, void* context) {\n    furi_assert(view_top_or_bottom);\n    furi_assert(context);\n\n    View* view_stack_view = context;\n    if(view_stack_view->update_callback) {\n        view_stack_view->update_callback(\n            view_stack_view, view_stack_view->update_callback_context);\n    }\n}\n\nstatic void view_stack_enter(void* context) {\n    furi_assert(context);\n\n    ViewStack* view_stack = context;\n    ViewStackModel* model = view_get_model(view_stack->view);\n\n    /* if more than 1 Stack View hold same view they have to reassign update_callback_context */\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(model->views[i]) {\n            view_set_update_callback_context(model->views[i], view_stack->view);\n            if(model->views[i]->enter_callback) {\n                model->views[i]->enter_callback(model->views[i]->context);\n            }\n        }\n    }\n\n    view_commit_model(view_stack->view, false);\n}\n\nstatic void view_stack_exit(void* context) {\n    furi_assert(context);\n\n    ViewStack* view_stack = context;\n    ViewStackModel* model = view_get_model(view_stack->view);\n\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(model->views[i] && model->views[i]->exit_callback) {\n            model->views[i]->exit_callback(model->views[i]->context);\n        }\n    }\n\n    view_commit_model(view_stack->view, false);\n}\n\nViewStack* view_stack_alloc(void) {\n    ViewStack* view_stack = malloc(sizeof(ViewStack));\n    view_stack->view = view_alloc();\n\n    view_allocate_model(view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel));\n    view_set_draw_callback(view_stack->view, view_stack_draw);\n    view_set_input_callback(view_stack->view, view_stack_input);\n    view_set_context(view_stack->view, view_stack);\n    view_set_enter_callback(view_stack->view, view_stack_enter);\n    view_set_exit_callback(view_stack->view, view_stack_exit);\n    return view_stack;\n}\n\nvoid view_stack_free(ViewStack* view_stack) {\n    furi_assert(view_stack);\n\n    ViewStackModel* model = view_get_model(view_stack->view);\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(model->views[i]) {\n            view_set_update_callback(model->views[i], NULL);\n            view_set_update_callback_context(model->views[i], NULL);\n        }\n    }\n    view_commit_model(view_stack->view, false);\n\n    view_free(view_stack->view);\n    free(view_stack);\n}\n\nstatic void view_stack_draw(Canvas* canvas, void* _model) {\n    furi_assert(_model);\n\n    ViewStackModel* model = _model;\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(model->views[i]) {\n            view_draw(model->views[i], canvas);\n        }\n    }\n}\n\nstatic bool view_stack_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    ViewStack* view_stack = context;\n\n    bool consumed = false;\n    ViewStackModel* model = view_get_model(view_stack->view);\n    for(int i = MAX_VIEWS - 1; i >= 0; i--) {\n        if(model->views[i] && view_input(model->views[i], event)) {\n            consumed = true;\n            break;\n        }\n    }\n    view_commit_model(view_stack->view, false);\n\n    return consumed;\n}\n\nvoid view_stack_add_view(ViewStack* view_stack, View* view) {\n    furi_assert(view_stack);\n    furi_assert(view);\n\n    bool result = false;\n    ViewStackModel* model = view_get_model(view_stack->view);\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(!model->views[i]) {\n            model->views[i] = view;\n            view_set_update_callback(model->views[i], view_stack_update_callback);\n            view_set_update_callback_context(model->views[i], view_stack->view);\n            if(view->enter_callback) {\n                view->enter_callback(view->context);\n            }\n            result = true;\n            break;\n        }\n    }\n    view_commit_model(view_stack->view, result);\n    furi_assert(result);\n}\n\nvoid view_stack_remove_view(ViewStack* view_stack, View* view) {\n    furi_assert(view_stack);\n    furi_assert(view);\n\n    /* Removing view on-the-go is dangerous, but it is protected with\n     * Locking model, so system is consistent at any time. */\n    bool result = false;\n    ViewStackModel* model = view_get_model(view_stack->view);\n    for(int i = 0; i < MAX_VIEWS; ++i) {\n        if(model->views[i] == view) {\n            if(view->exit_callback) {\n                view->exit_callback(view->context);\n            }\n            view_set_update_callback(model->views[i], NULL);\n            view_set_update_callback_context(model->views[i], NULL);\n            model->views[i] = NULL;\n            result = true;\n            break;\n        }\n    }\n    view_commit_model(view_stack->view, result);\n    furi_assert(result);\n}\n\nView* view_stack_get_view(ViewStack* view_stack) {\n    furi_assert(view_stack);\n    return view_stack->view;\n}\n"
  },
  {
    "path": "applications/services/gui/view_stack.h",
    "content": "/**\n * @file view_stack.h\n * @brief GUI: ViewStack API\n *\n * ViewStack accumulates several Views in one stack.\n * Draw callbacks are called sequenctially starting from\n * first added. Input callbacks are called in reverse order.\n * Consumed input is not passed on underlying layers.\n *\n * @warning Views added to a ViewStack MUST NOT be in a ViewDispatcher or a ViewHolder at the same time.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include \"view.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** ViewStack, anonymous type. */\ntypedef struct ViewStack ViewStack;\n\n/** Allocate and init ViewStack\n *\n * @return      ViewStack instance\n */\nViewStack* view_stack_alloc(void);\n\n/** Free ViewStack instance\n *\n * @param       view_stack  instance\n */\nvoid view_stack_free(ViewStack* view_stack);\n\n/** Get View of ViewStack.\n * Should this View to any view manager such as\n * ViewDispatcher or ViewHolder.\n *\n * @param       view_stack  instance\n */\nView* view_stack_get_view(ViewStack* view_stack);\n\n/** Add View to ViewStack.\n * Adds View on top of ViewStack.\n *\n * @param       view_stack  instance\n * @param       view        view to add\n */\nvoid view_stack_add_view(ViewStack* view_stack, View* view);\n\n/** Remove any View in ViewStack.\n * If no View to remove found - ignore.\n *\n * @param       view_stack  instance\n * @param       view        view to remove\n */\nvoid view_stack_remove_view(ViewStack* view_stack, View* view);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/input/application.fam",
    "content": "App(\n    appid=\"input\",\n    name=\"InputSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"input_srv\",\n    cdefines=[\"SRV_INPUT\"],\n    stack_size=1 * 1024,\n    order=80,\n    sdk_headers=[\"input.h\"],\n)\n"
  },
  {
    "path": "applications/services/input/input.c",
    "content": "#include \"input.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <furi.h>\n#include <furi_hal_gpio.h>\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <toolbox/pipe.h>\n\n#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)\n#define INPUT_PRESS_TICKS         150\n#define INPUT_LONG_PRESS_COUNTS   2\n#define INPUT_THREAD_FLAG_ISR     0x00000001\n\n/** Input pin state */\ntypedef struct {\n    const InputPin* pin;\n    // State\n    volatile bool state;\n    volatile uint8_t debounce;\n    FuriTimer* press_timer;\n    FuriPubSub* event_pubsub;\n    volatile uint8_t press_counter;\n    volatile uint32_t counter;\n} InputPinState;\n\n/** Input CLI command handler */\nvoid input_cli(PipeSide* pipe, FuriString* args, void* context);\n\n// #define INPUT_DEBUG\n\n#define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted))\n\nvoid input_press_timer_callback(void* arg) {\n    InputPinState* input_pin = arg;\n    InputEvent event;\n    event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE;\n    event.sequence_counter = input_pin->counter;\n    event.key = input_pin->pin->key;\n    input_pin->press_counter++;\n    if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) {\n        event.type = InputTypeLong;\n        furi_pubsub_publish(input_pin->event_pubsub, &event);\n    } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) {\n        input_pin->press_counter--;\n        event.type = InputTypeRepeat;\n        furi_pubsub_publish(input_pin->event_pubsub, &event);\n    }\n}\n\nvoid input_isr(void* _ctx) {\n    FuriThreadId thread_id = (FuriThreadId)_ctx;\n    furi_thread_flags_set(thread_id, INPUT_THREAD_FLAG_ISR);\n}\n\nconst char* input_get_key_name(InputKey key) {\n    for(size_t i = 0; i < input_pins_count; i++) {\n        if(input_pins[i].key == key) {\n            return input_pins[i].name;\n        }\n    }\n    return \"Unknown\";\n}\n\nconst char* input_get_type_name(InputType type) {\n    switch(type) {\n    case InputTypePress:\n        return \"Press\";\n    case InputTypeRelease:\n        return \"Release\";\n    case InputTypeShort:\n        return \"Short\";\n    case InputTypeLong:\n        return \"Long\";\n    case InputTypeRepeat:\n        return \"Repeat\";\n    default:\n        return \"Unknown\";\n    }\n}\n\nint32_t input_srv(void* p) {\n    UNUSED(p);\n\n    const FuriThreadId thread_id = furi_thread_get_current_id();\n    FuriPubSub* event_pubsub = furi_pubsub_alloc();\n    uint32_t counter = 1;\n    furi_record_create(RECORD_INPUT_EVENTS, event_pubsub);\n\n#ifdef INPUT_DEBUG\n    furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull);\n#endif\n\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(\n        registry, \"input\", CliCommandFlagParallelSafe, input_cli, event_pubsub);\n    furi_record_close(RECORD_CLI);\n#endif\n\n    InputPinState pin_states[input_pins_count];\n\n    for(size_t i = 0; i < input_pins_count; i++) {\n        furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, thread_id);\n        pin_states[i].pin = &input_pins[i];\n        pin_states[i].state = GPIO_Read(pin_states[i]);\n        pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF;\n        pin_states[i].press_timer =\n            furi_timer_alloc(input_press_timer_callback, FuriTimerTypePeriodic, &pin_states[i]);\n        pin_states[i].event_pubsub = event_pubsub;\n        pin_states[i].press_counter = 0;\n    }\n\n    while(1) {\n        bool is_changing = false;\n        for(size_t i = 0; i < input_pins_count; i++) {\n            bool state = GPIO_Read(pin_states[i]);\n            if(state) {\n                if(pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) pin_states[i].debounce += 1;\n            } else {\n                if(pin_states[i].debounce > 0) pin_states[i].debounce -= 1;\n            }\n\n            if(pin_states[i].debounce > 0 && pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) {\n                is_changing = true;\n            } else if(pin_states[i].state != state) {\n                pin_states[i].state = state;\n\n                // Common state info\n                InputEvent event;\n                event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE;\n                event.key = pin_states[i].pin->key;\n\n                // Short / Long / Repeat timer routine\n                if(state) {\n                    pin_states[i].counter = counter++;\n                    event.sequence_counter = pin_states[i].counter;\n                    furi_timer_start(pin_states[i].press_timer, INPUT_PRESS_TICKS);\n                } else {\n                    event.sequence_counter = pin_states[i].counter;\n                    furi_timer_stop(pin_states[i].press_timer);\n                    while(furi_timer_is_running(pin_states[i].press_timer))\n                        furi_delay_tick(1);\n                    if(pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {\n                        event.type = InputTypeShort;\n                        furi_pubsub_publish(event_pubsub, &event);\n                    }\n                    pin_states[i].press_counter = 0;\n                }\n\n                // Send Press/Release event\n                event.type = pin_states[i].state ? InputTypePress : InputTypeRelease;\n                furi_pubsub_publish(event_pubsub, &event);\n            }\n        }\n\n        if(is_changing) {\n#ifdef INPUT_DEBUG\n            furi_hal_gpio_write(&gpio_ext_pa4, 1);\n#endif\n            furi_delay_tick(1);\n        } else {\n#ifdef INPUT_DEBUG\n            furi_hal_gpio_write(&gpio_ext_pa4, 0);\n#endif\n            furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/input/input.h",
    "content": "/**\n * @file input.h\n * Input: main API\n */\n\n#pragma once\n\n#include <furi_hal_resources.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_INPUT_EVENTS            \"input_events\"\n#define INPUT_SEQUENCE_SOURCE_HARDWARE (0u)\n#define INPUT_SEQUENCE_SOURCE_SOFTWARE (1u)\n\n/** Input Types\n * Some of them are physical events and some logical\n */\ntypedef enum {\n    InputTypePress, /**< Press event, emitted after debounce */\n    InputTypeRelease, /**< Release event, emitted after debounce */\n    InputTypeShort, /**< Short event, emitted after InputTypeRelease done within INPUT_LONG_PRESS interval */\n    InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease  */\n    InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */\n    InputTypeMAX, /**< Special value for exceptional */\n} InputType;\n\n/** Input Event, dispatches with FuriPubSub */\ntypedef struct {\n    union {\n        uint32_t sequence;\n        struct {\n            uint8_t sequence_source   : 2;\n            uint32_t sequence_counter : 30;\n        };\n    };\n    InputKey key;\n    InputType type;\n} InputEvent;\n\n/** Get human readable input key name\n * @param key - InputKey\n * @return string\n */\nconst char* input_get_key_name(InputKey key);\n\n/** Get human readable input type name\n * @param type - InputType\n * @return string\n */\nconst char* input_get_type_name(InputType type);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/input/input_cli.c",
    "content": "#include \"input.h\"\n\n#include <furi.h>\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/args.h>\n#include <toolbox/pipe.h>\n\nstatic void input_cli_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"input <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n    printf(\"\\tdump\\t\\t\\t - dump input events\\r\\n\");\n    printf(\"\\tsend <key> <type>\\t - send input event\\r\\n\");\n}\n\nstatic void input_cli_dump_events_callback(const void* value, void* ctx) {\n    furi_assert(value);\n    furi_assert(ctx);\n    FuriMessageQueue* input_queue = ctx;\n    furi_message_queue_put(input_queue, value, FuriWaitForever);\n}\n\nstatic void input_cli_dump(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) {\n    UNUSED(args);\n    FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n    FuriPubSubSubscription* input_subscription =\n        furi_pubsub_subscribe(event_pubsub, input_cli_dump_events_callback, input_queue);\n\n    InputEvent input_event;\n    printf(\"Press CTRL+C to stop\\r\\n\");\n    while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {\n        if(furi_message_queue_get(input_queue, &input_event, 100) == FuriStatusOk) {\n            printf(\n                \"key: %s type: %s\\r\\n\",\n                input_get_key_name(input_event.key),\n                input_get_type_name(input_event.type));\n        }\n    }\n\n    furi_pubsub_unsubscribe(event_pubsub, input_subscription);\n    furi_message_queue_free(input_queue);\n}\n\nstatic void input_cli_send_print_usage(void) {\n    printf(\"Invalid arguments. Usage:\\r\\n\");\n    printf(\"\\tinput send <key> <type>\\r\\n\");\n    printf(\"\\t\\t <key>\\t - one of 'up', 'down', 'left', 'right', 'back', 'ok'\\r\\n\");\n    printf(\"\\t\\t <type>\\t - one of 'press', 'release', 'short', 'long'\\r\\n\");\n}\n\nstatic void input_cli_send(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) {\n    UNUSED(pipe);\n    InputEvent event;\n    FuriString* key_str;\n    key_str = furi_string_alloc();\n    bool parsed = false;\n\n    do {\n        // Parse Key\n        if(!args_read_string_and_trim(args, key_str)) {\n            break;\n        }\n        if(!furi_string_cmp(key_str, \"up\")) {\n            event.key = InputKeyUp;\n        } else if(!furi_string_cmp(key_str, \"down\")) {\n            event.key = InputKeyDown;\n        } else if(!furi_string_cmp(key_str, \"left\")) {\n            event.key = InputKeyLeft;\n        } else if(!furi_string_cmp(key_str, \"right\")) {\n            event.key = InputKeyRight;\n        } else if(!furi_string_cmp(key_str, \"ok\")) {\n            event.key = InputKeyOk;\n        } else if(!furi_string_cmp(key_str, \"back\")) {\n            event.key = InputKeyBack;\n        } else {\n            break;\n        }\n        // Parse Type\n        if(!furi_string_cmp(args, \"press\")) {\n            event.type = InputTypePress;\n        } else if(!furi_string_cmp(args, \"release\")) {\n            event.type = InputTypeRelease;\n        } else if(!furi_string_cmp(args, \"short\")) {\n            event.type = InputTypeShort;\n        } else if(!furi_string_cmp(args, \"long\")) {\n            event.type = InputTypeLong;\n        } else {\n            break;\n        }\n        parsed = true;\n    } while(false);\n\n    if(parsed) { //-V547\n        furi_pubsub_publish(event_pubsub, &event);\n    } else {\n        input_cli_send_print_usage();\n    }\n    furi_string_free(key_str);\n}\n\nvoid input_cli(PipeSide* pipe, FuriString* args, void* context) {\n    furi_assert(context);\n    FuriPubSub* event_pubsub = context;\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            input_cli_usage();\n            break;\n        }\n        if(furi_string_cmp_str(cmd, \"dump\") == 0) {\n            input_cli_dump(pipe, args, event_pubsub);\n            break;\n        }\n        if(furi_string_cmp_str(cmd, \"send\") == 0) {\n            input_cli_send(pipe, args, event_pubsub);\n            break;\n        }\n\n        input_cli_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n"
  },
  {
    "path": "applications/services/loader/application.fam",
    "content": "App(\n    appid=\"loader\",\n    name=\"LoaderSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"loader_srv\",\n    cdefines=[\"SRV_LOADER\"],\n    requires=[\"gui\"],\n    provides=[\"loader_start\"],\n    stack_size=2 * 1024,\n    order=90,\n    sdk_headers=[\n        \"loader.h\",\n        \"firmware_api/firmware_api.h\",\n    ],\n)\n\nApp(\n    appid=\"loader_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"loader_on_system_start\",\n    requires=[\"loader\"],\n    order=80,\n)\n"
  },
  {
    "path": "applications/services/loader/firmware_api/firmware_api.cpp",
    "content": "#include \"firmware_api.h\"\n\n#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n/* Generated table */\n#include <firmware_api_table.h>\n\n#include <furi_hal_info.h>\n\nstatic_assert(!has_hash_collisions(elf_api_table), \"Detected API method hash collision!\");\n\nconstexpr HashtableApiInterface elf_api_interface{\n    {\n        .api_version_major = (elf_api_version >> 16),\n        .api_version_minor = (elf_api_version & 0xFFFF),\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    elf_api_table.cbegin(),\n    elf_api_table.cend(),\n};\nconst ElfApiInterface* const firmware_api_interface = &elf_api_interface;\n\nextern \"C\" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {\n    *major = firmware_api_interface->api_version_major;\n    *minor = firmware_api_interface->api_version_minor;\n}\n"
  },
  {
    "path": "applications/services/loader/firmware_api/firmware_api.h",
    "content": "#pragma once\n\n#include <flipper_application/elf/elf_api_interface.h>\n\nextern const ElfApiInterface* const firmware_api_interface;\n"
  },
  {
    "path": "applications/services/loader/loader.c",
    "content": "#include \"loader.h\"\n#include \"loader_i.h\"\n#include <applications.h>\n#include <storage/storage.h>\n#include <furi_hal.h>\n#include <assets_icons.h>\n\n#include <dialogs/dialogs.h>\n#include <toolbox/path.h>\n#include <flipper_application/flipper_application.h>\n#include <loader/firmware_api/firmware_api.h>\n\n#define TAG \"Loader\"\n\n#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF\n\n// helpers\n\nstatic const char* loader_find_external_application_by_name(const char* app_name) {\n    for(size_t i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {\n        if(strcmp(FLIPPER_EXTERNAL_APPS[i].name, app_name) == 0) {\n            return FLIPPER_EXTERNAL_APPS[i].path;\n        }\n    }\n\n    return NULL;\n}\n\n// API\n\nstatic LoaderMessageLoaderStatusResult loader_start_internal(\n    Loader* loader,\n    const char* name,\n    const char* args,\n    FuriString* error_message) {\n    LoaderMessage message;\n    LoaderMessageLoaderStatusResult result;\n\n    message.type = LoaderMessageTypeStartByName;\n    message.start.name = name;\n    message.start.args = args;\n    message.start.error_message = error_message;\n    message.api_lock = api_lock_alloc_locked();\n    message.status_value = &result;\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message.api_lock);\n\n    return result;\n}\n\ntypedef struct {\n    const char* error;\n    const char* description;\n    const char* url;\n    const Icon* icon;\n} LoaderError;\n\nstatic const LoaderError err_app_not_found =\n    {\"App Not Found\", \"Update firmware or app\", \"err_01\", &I_err_01};\nstatic const LoaderError err_invalid_flie = {\"Invalid File\", \"Update the app\", \"err_02\", &I_err_02};\nstatic const LoaderError err_invalid_manifest =\n    {\"Invalid Manifest\", \"Update firmware or app\", \"err_03\", &I_err_03};\nstatic const LoaderError err_missing_imports =\n    {\"Missing Imports\", \"Update firmware\", \"err_04\", &I_err_04};\nstatic const LoaderError err_hw_target_mismatch =\n    {\"HW Target\\nMismatch\", \"App not supported\", \"err_05\", &I_err_05};\nstatic const LoaderError err_outdated_app = {\"Outdated App\", \"Update the app\", \"err_06\", &I_err_06};\nstatic const LoaderError err_outdated_firmware =\n    {\"Outdated\\nFirmware\", \"Update firmware\", \"err_07\", &I_err_07};\n\nstatic void loader_dialog_prepare_and_show(DialogsApp* dialogs, const LoaderError* err) {\n    FuriString* header = furi_string_alloc_printf(\"Error: %s\", err->error);\n    FuriString* text =\n        furi_string_alloc_printf(\"%s\\nLearn more:\\nr.flipper.net/%s\", err->description, err->url);\n    DialogMessage* message = dialog_message_alloc();\n\n    dialog_message_set_header(message, furi_string_get_cstr(header), 64, 0, AlignCenter, AlignTop);\n    dialog_message_set_text(message, furi_string_get_cstr(text), 0, 63, AlignLeft, AlignBottom);\n    dialog_message_set_icon(message, err->icon, 128 - 25, 64 - 25);\n    dialog_message_show(dialogs, message);\n\n    dialog_message_free(message);\n    furi_string_free(header);\n    furi_string_free(text);\n}\n\nstatic void loader_show_gui_error(\n    LoaderMessageLoaderStatusResult status,\n    const char* name,\n    FuriString* error_message) {\n    furi_check(name);\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n    DialogMessage* message = dialog_message_alloc();\n\n    if(status.value == LoaderStatusErrorUnknownApp &&\n       loader_find_external_application_by_name(name) != NULL) {\n        // Special case for external apps\n        const char* header = NULL;\n        const char* text = NULL;\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n        if(storage_sd_status(storage) == FSE_OK) {\n            header = \"Update needed\";\n            text = \"Update firmware\\nto run this app\";\n        } else {\n            header = \"SD card needed\";\n            text = \"Install SD card\\nto run this app\";\n        }\n        furi_record_close(RECORD_STORAGE);\n        dialog_message_set_header(message, header, 64, 3, AlignCenter, AlignTop);\n        dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22);\n        dialog_message_set_text(message, text, 3, 26, AlignLeft, AlignTop);\n        dialog_message_show(dialogs, message);\n    } else if(status.value == LoaderStatusErrorUnknownApp) {\n        loader_dialog_prepare_and_show(dialogs, &err_app_not_found);\n    } else if(status.value == LoaderStatusErrorInternal) {\n        // TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu\n        // so i prefer to not show LoaderStatusErrorAppStarted error message for now\n        switch(status.error) {\n        case LoaderStatusErrorInvalidFile:\n            loader_dialog_prepare_and_show(dialogs, &err_invalid_flie);\n            break;\n        case LoaderStatusErrorInvalidManifest:\n            loader_dialog_prepare_and_show(dialogs, &err_invalid_manifest);\n            break;\n        case LoaderStatusErrorMissingImports:\n            loader_dialog_prepare_and_show(dialogs, &err_missing_imports);\n            break;\n        case LoaderStatusErrorHWMismatch:\n            loader_dialog_prepare_and_show(dialogs, &err_hw_target_mismatch);\n            break;\n        case LoaderStatusErrorOutdatedApp:\n            loader_dialog_prepare_and_show(dialogs, &err_outdated_app);\n            break;\n        case LoaderStatusErrorOutdatedFirmware:\n            loader_dialog_prepare_and_show(dialogs, &err_outdated_firmware);\n            break;\n        case LoaderStatusErrorOutOfMemory:\n            dialog_message_set_header(\n                message, \"Error: Out of Memory\", 64, 0, AlignCenter, AlignTop);\n            dialog_message_set_text(\n                message,\n                \"Not enough RAM to run the\\napp. Please reboot the device\",\n                64,\n                13,\n                AlignCenter,\n                AlignTop);\n            dialog_message_set_buttons(message, NULL, NULL, \"Reboot\");\n            if(dialog_message_show(dialogs, message) == DialogMessageButtonRight) {\n                furi_hal_power_reset();\n            }\n            break;\n        default:\n            // Generic error\n            dialog_message_set_header(message, \"Error\", 64, 0, AlignCenter, AlignTop);\n\n            furi_string_replace(error_message, \"/ext/apps/\", \"\");\n            furi_string_replace(error_message, \", \", \"\\n\");\n            furi_string_replace(error_message, \": \", \"\\n\");\n\n            dialog_message_set_text(\n                message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter);\n            dialog_message_show(dialogs, message);\n            break;\n        }\n    }\n\n    dialog_message_free(message);\n    furi_record_close(RECORD_DIALOGS);\n}\n\nstatic void loader_generic_synchronous_request(Loader* loader, LoaderMessage* message) {\n    furi_check(loader);\n    message->api_lock = api_lock_alloc_locked();\n    furi_message_queue_put(loader->queue, message, FuriWaitForever);\n    api_lock_wait_unlock_and_free(message->api_lock);\n}\n\nLoaderStatus\n    loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) {\n    furi_check(loader);\n    furi_check(name);\n\n    LoaderMessageLoaderStatusResult result =\n        loader_start_internal(loader, name, args, error_message);\n    return result.value;\n}\n\nLoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) {\n    furi_check(loader);\n    furi_check(name);\n\n    FuriString* error_message = furi_string_alloc();\n    LoaderMessageLoaderStatusResult result =\n        loader_start_internal(loader, name, args, error_message);\n    loader_show_gui_error(result, name, error_message);\n    furi_string_free(error_message);\n    return result.value;\n}\n\nvoid loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args) {\n    furi_check(loader);\n    furi_check(name);\n\n    LoaderMessage message = {\n        .type = LoaderMessageTypeStartByNameDetachedWithGuiError,\n        .start.name = strdup(name),\n        .start.args = args ? strdup(args) : NULL,\n    };\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n}\n\nbool loader_lock(Loader* loader) {\n    LoaderMessageBoolResult result;\n    LoaderMessage message = {\n        .type = LoaderMessageTypeLock,\n        .bool_value = &result,\n    };\n    loader_generic_synchronous_request(loader, &message);\n    return result.value;\n}\n\nvoid loader_unlock(Loader* loader) {\n    furi_check(loader);\n\n    LoaderMessage message;\n    message.type = LoaderMessageTypeUnlock;\n\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n}\n\nbool loader_is_locked(Loader* loader) {\n    LoaderMessageBoolResult result;\n    LoaderMessage message = {\n        .type = LoaderMessageTypeIsLocked,\n        .bool_value = &result,\n    };\n    loader_generic_synchronous_request(loader, &message);\n    return result.value;\n}\n\nvoid loader_show_menu(Loader* loader) {\n    furi_check(loader);\n\n    LoaderMessage message;\n    message.type = LoaderMessageTypeShowMenu;\n\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n}\n\nFuriPubSub* loader_get_pubsub(Loader* loader) {\n    furi_check(loader);\n    // it's safe to return pubsub without locking\n    // because it's never freed and loader is never exited\n    // also the loader instance cannot be obtained until the pubsub is created\n    return loader->pubsub;\n}\n\nbool loader_signal(Loader* loader, uint32_t signal, void* arg) {\n    LoaderMessageBoolResult result;\n    LoaderMessage message = {\n        .type = LoaderMessageTypeSignal,\n        .signal.signal = signal,\n        .signal.arg = arg,\n        .bool_value = &result,\n    };\n    loader_generic_synchronous_request(loader, &message);\n    return result.value;\n}\n\nbool loader_get_application_name(Loader* loader, FuriString* name) {\n    LoaderMessageBoolResult result;\n    LoaderMessage message = {\n        .type = LoaderMessageTypeGetApplicationName,\n        .application_name = name,\n        .bool_value = &result,\n    };\n    loader_generic_synchronous_request(loader, &message);\n    return result.value;\n}\n\nbool loader_get_application_launch_path(Loader* loader, FuriString* name) {\n    LoaderMessageBoolResult result;\n    LoaderMessage message = {\n        .type = LoaderMessageTypeGetApplicationLaunchPath,\n        .application_name = name,\n        .bool_value = &result,\n    };\n    loader_generic_synchronous_request(loader, &message);\n    return result.value;\n}\n\nvoid loader_enqueue_launch(\n    Loader* loader,\n    const char* name,\n    const char* args,\n    LoaderDeferredLaunchFlag flags) {\n    LoaderMessage message = {\n        .type = LoaderMessageTypeEnqueueLaunch,\n        .defer_start =\n            {\n                .name_or_path = strdup(name),\n                .args = args ? strdup(args) : NULL,\n                .flags = flags,\n            },\n    };\n    loader_generic_synchronous_request(loader, &message);\n}\n\nvoid loader_clear_launch_queue(Loader* loader) {\n    LoaderMessage message = {\n        .type = LoaderMessageTypeClearLaunchQueue,\n    };\n    loader_generic_synchronous_request(loader, &message);\n}\n\n// callbacks\n\nstatic void loader_menu_closed_callback(void* context) {\n    Loader* loader = context;\n    LoaderMessage message;\n    message.type = LoaderMessageTypeMenuClosed;\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n}\n\nstatic void loader_applications_closed_callback(void* context) {\n    Loader* loader = context;\n    LoaderMessage message;\n    message.type = LoaderMessageTypeApplicationsClosed;\n    furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n}\n\nstatic void\n    loader_thread_state_callback(FuriThread* thread, FuriThreadState thread_state, void* context) {\n    UNUSED(thread);\n    furi_assert(context);\n\n    if(thread_state == FuriThreadStateStopped) {\n        Loader* loader = context;\n\n        LoaderMessage message;\n        message.type = LoaderMessageTypeAppClosed;\n        furi_message_queue_put(loader->queue, &message, FuriWaitForever);\n    }\n}\n\n// implementation\n\nstatic Loader* loader_alloc(void) {\n    Loader* loader = malloc(sizeof(Loader));\n    loader->pubsub = furi_pubsub_alloc();\n    loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));\n    loader->gui = furi_record_open(RECORD_GUI);\n    loader->view_holder = view_holder_alloc();\n    loader->loading = loading_alloc();\n    view_holder_attach_to_gui(loader->view_holder, loader->gui);\n    return loader;\n}\n\nstatic FlipperInternalApplication const* loader_find_application_by_name_in_list(\n    const char* name,\n    const FlipperInternalApplication* list,\n    const uint32_t n_apps) {\n    for(size_t i = 0; i < n_apps; i++) {\n        if((strcmp(name, list[i].name) == 0) || (strcmp(name, list[i].appid) == 0)) {\n            return &list[i];\n        }\n    }\n    return NULL;\n}\n\nstatic const FlipperInternalApplication* loader_find_application_by_name(const char* name) {\n    const struct {\n        const FlipperInternalApplication* list;\n        const uint32_t count;\n    } lists[] = {\n        {FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT},\n        {FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT},\n        {FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT},\n    };\n\n    for(size_t i = 0; i < COUNT_OF(lists); i++) {\n        const FlipperInternalApplication* application =\n            loader_find_application_by_name_in_list(name, lists[i].list, lists[i].count);\n        if(application) {\n            return application;\n        }\n    }\n\n    return NULL;\n}\n\nstatic void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) {\n    // setup heap trace\n    FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();\n    if(mode > FuriHalRtcHeapTrackModeNone) {\n        furi_thread_enable_heap_trace(loader->app.thread);\n    } else {\n        furi_thread_disable_heap_trace(loader->app.thread);\n    }\n\n    // setup insomnia\n    if(!(flags & FlipperInternalApplicationFlagInsomniaSafe)) {\n        furi_hal_power_insomnia_enter();\n        loader->app.insomniac = true;\n    } else {\n        loader->app.insomniac = false;\n    }\n\n    // setup thread state callbacks\n    furi_thread_set_state_context(loader->app.thread, loader);\n    furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback);\n\n    // start app thread\n    furi_thread_start(loader->app.thread);\n}\n\nstatic void loader_start_internal_app(\n    Loader* loader,\n    const FlipperInternalApplication* app,\n    const char* args) {\n    FURI_LOG_I(TAG, \"Starting %s\", app->name);\n\n    // store args\n    furi_assert(loader->app.args == NULL);\n    if(args && strlen(args) > 0) {\n        loader->app.args = strdup(args);\n    }\n\n    loader->app.thread =\n        furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args);\n    furi_thread_set_appid(loader->app.thread, app->appid);\n\n    loader_start_app_thread(loader, app->flags);\n}\n\nstatic void loader_log_status_error(\n    LoaderStatus status,\n    FuriString* error_message,\n    const char* format,\n    va_list args) {\n    if(error_message) {\n        furi_string_vprintf(error_message, format, args);\n        FURI_LOG_E(TAG, \"Status [%d]: %s\", status, furi_string_get_cstr(error_message));\n    } else {\n        FURI_LOG_E(TAG, \"Status [%d]\", status);\n    }\n}\n\nstatic LoaderStatus loader_make_status_error(\n    LoaderStatus status,\n    FuriString* error_message,\n    const char* format,\n    ...) {\n    va_list args;\n    va_start(args, format);\n    loader_log_status_error(status, error_message, format, args);\n    va_end(args);\n    return status;\n}\n\nstatic LoaderStatus loader_make_success_status(FuriString* error_message) {\n    if(error_message) {\n        furi_string_set(error_message, \"App started\");\n    }\n\n    return LoaderStatusOk;\n}\n\nstatic LoaderStatusError\n    loader_status_error_from_preload_status(FlipperApplicationPreloadStatus status) {\n    switch(status) {\n    case FlipperApplicationPreloadStatusInvalidFile:\n        return LoaderStatusErrorInvalidFile;\n    case FlipperApplicationPreloadStatusNotEnoughMemory:\n        return LoaderStatusErrorOutOfMemory;\n    case FlipperApplicationPreloadStatusInvalidManifest:\n        return LoaderStatusErrorInvalidManifest;\n    case FlipperApplicationPreloadStatusApiTooOld:\n        return LoaderStatusErrorOutdatedApp;\n    case FlipperApplicationPreloadStatusApiTooNew:\n        return LoaderStatusErrorOutdatedFirmware;\n    case FlipperApplicationPreloadStatusTargetMismatch:\n        return LoaderStatusErrorHWMismatch;\n    default:\n        return LoaderStatusErrorUnknown;\n    }\n}\n\nstatic LoaderStatusError\n    loader_status_error_from_load_status(FlipperApplicationLoadStatus status) {\n    switch(status) {\n    case FlipperApplicationLoadStatusMissingImports:\n        return LoaderStatusErrorMissingImports;\n    default:\n        return LoaderStatusErrorUnknown;\n    }\n}\n\nstatic LoaderMessageLoaderStatusResult loader_start_external_app(\n    Loader* loader,\n    Storage* storage,\n    const char* path,\n    const char* args,\n    FuriString* error_message) {\n    LoaderMessageLoaderStatusResult result;\n    result.value = loader_make_success_status(error_message);\n    result.error = LoaderStatusErrorUnknown;\n\n    do {\n        loader->app.fap = flipper_application_alloc(storage, firmware_api_interface);\n        size_t start = furi_get_tick();\n\n        FURI_LOG_I(TAG, \"Loading %s\", path);\n\n        FlipperApplicationPreloadStatus preload_res =\n            flipper_application_preload(loader->app.fap, path);\n        if(preload_res != FlipperApplicationPreloadStatusSuccess) {\n            const char* err_msg = flipper_application_preload_status_to_string(preload_res);\n            result.value = loader_make_status_error(\n                LoaderStatusErrorInternal, error_message, \"Preload failed, %s: %s\", path, err_msg);\n            result.error = loader_status_error_from_preload_status(preload_res);\n            break;\n        }\n\n        FlipperApplicationLoadStatus load_status =\n            flipper_application_map_to_memory(loader->app.fap);\n        if(load_status != FlipperApplicationLoadStatusSuccess) {\n            const char* err_msg = flipper_application_load_status_to_string(load_status);\n            result.value = loader_make_status_error(\n                LoaderStatusErrorInternal, error_message, \"Load failed, %s: %s\", path, err_msg);\n            result.error = loader_status_error_from_load_status(load_status);\n            break;\n        }\n\n        FURI_LOG_I(TAG, \"Loaded in %zums\", (size_t)(furi_get_tick() - start));\n\n        if(flipper_application_is_plugin(loader->app.fap)) {\n            result.value = loader_make_status_error(\n                LoaderStatusErrorInternal, error_message, \"Plugin %s is not runnable\", path);\n            break;\n        }\n\n        loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args);\n        FuriString* app_name = furi_string_alloc();\n        path_extract_filename_no_ext(path, app_name);\n        furi_thread_set_appid(loader->app.thread, furi_string_get_cstr(app_name));\n        furi_string_free(app_name);\n\n        /* This flag is set by the debugger - to break on app start */\n        if(furi_hal_debug_is_gdb_session_active()) {\n            FURI_LOG_W(TAG, \"Triggering BP for debugger\");\n            /* After hitting this, you can set breakpoints in your .fap's code\n             * Note that you have to toggle breakpoints that were set before */\n            __asm volatile(\"bkpt 0\");\n        }\n\n        loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault);\n    } while(0);\n\n    if(result.value != LoaderStatusOk) {\n        flipper_application_free(loader->app.fap);\n        loader->app.fap = NULL;\n        LoaderEvent event;\n        event.type = LoaderEventTypeApplicationLoadFailed;\n        furi_pubsub_publish(loader->pubsub, &event);\n    }\n\n    return result;\n}\n\n// process messages\n\nstatic void loader_do_menu_show(Loader* loader) {\n    if(!loader->loader_menu) {\n        loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader);\n    }\n}\n\nstatic void loader_do_menu_closed(Loader* loader) {\n    if(loader->loader_menu) {\n        loader_menu_free(loader->loader_menu);\n        loader->loader_menu = NULL;\n    }\n}\n\nstatic void loader_do_applications_show(Loader* loader) {\n    if(!loader->loader_applications) {\n        loader->loader_applications =\n            loader_applications_alloc(loader_applications_closed_callback, loader);\n    }\n}\n\nstatic void loader_do_applications_closed(Loader* loader) {\n    if(loader->loader_applications) {\n        loader_applications_free(loader->loader_applications);\n        loader->loader_applications = NULL;\n    }\n}\n\nstatic bool loader_do_is_locked(Loader* loader) {\n    return loader->app.thread != NULL;\n}\n\nstatic LoaderMessageLoaderStatusResult loader_do_start_by_name(\n    Loader* loader,\n    const char* name,\n    const char* args,\n    FuriString* error_message) {\n    LoaderMessageLoaderStatusResult status;\n    status.value = loader_make_success_status(error_message);\n    status.error = LoaderStatusErrorUnknown;\n\n    if(name == NULL) return status;\n\n    LoaderEvent event;\n    event.type = LoaderEventTypeApplicationBeforeLoad;\n    furi_pubsub_publish(loader->pubsub, &event);\n\n    do {\n        // check lock\n        if(loader_do_is_locked(loader)) {\n            if(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE) {\n                status.value = loader_make_status_error(\n                    LoaderStatusErrorAppStarted, error_message, \"Loader is locked\");\n            } else {\n                const char* current_thread_name =\n                    furi_thread_get_name(furi_thread_get_id(loader->app.thread));\n\n                status.value = loader_make_status_error(\n                    LoaderStatusErrorAppStarted,\n                    error_message,\n                    \"Loader is locked, please close the \\\"%s\\\" first\",\n                    current_thread_name);\n            }\n            break;\n        }\n\n        // check internal apps\n        {\n            const FlipperInternalApplication* app = loader_find_application_by_name(name);\n            if(app) {\n                loader_start_internal_app(loader, app, args);\n                status.value = loader_make_success_status(error_message);\n                break;\n            }\n        }\n\n        // check Applications\n        if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) {\n            loader_do_applications_show(loader);\n            status.value = loader_make_success_status(error_message);\n            break;\n        }\n\n        // check External Applications\n        {\n            const char* path = loader_find_external_application_by_name(name);\n            if(path) {\n                name = path;\n            }\n        }\n\n        // check Faps\n        {\n            Storage* storage = furi_record_open(RECORD_STORAGE);\n            if(storage_file_exists(storage, name)) {\n                status = loader_start_external_app(loader, storage, name, args, error_message);\n                furi_record_close(RECORD_STORAGE);\n                break;\n            }\n            furi_record_close(RECORD_STORAGE);\n        }\n\n        status.value = loader_make_status_error(\n            LoaderStatusErrorUnknownApp, error_message, \"Application \\\"%s\\\" not found\", name);\n    } while(false);\n\n    if(status.value == LoaderStatusOk) {\n        loader->app.launch_path = furi_string_alloc_set_str(name);\n    }\n\n    return status;\n}\n\nstatic bool loader_do_lock(Loader* loader) {\n    if(loader->app.thread) {\n        return false;\n    }\n\n    loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;\n    return true;\n}\n\nstatic void loader_do_unlock(Loader* loader) {\n    furi_check(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);\n    loader->app.thread = NULL;\n}\n\nstatic void loader_do_emit_queue_empty_event(Loader* loader) {\n    if(loader_do_is_locked(loader)) return;\n    FURI_LOG_I(TAG, \"Launch queue empty\");\n    LoaderEvent event;\n    event.type = LoaderEventTypeNoMoreAppsInQueue;\n    furi_pubsub_publish(loader->pubsub, &event);\n}\n\nstatic bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record);\n\nstatic void loader_do_next_deferred_launch_if_available(Loader* loader) {\n    LoaderDeferredLaunchRecord record;\n    if(loader_queue_pop(&loader->launch_queue, &record)) {\n        loader_do_deferred_launch(loader, &record);\n        loader_queue_item_clear(&record);\n    } else {\n        loader_do_emit_queue_empty_event(loader);\n    }\n}\n\nstatic bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record) {\n    furi_assert(loader);\n    furi_assert(record);\n\n    bool is_successful = false;\n    FuriString* error_message = furi_string_alloc();\n    view_holder_set_view(loader->view_holder, loading_get_view(loader->loading));\n    view_holder_send_to_front(loader->view_holder);\n\n    do {\n        const char* app_name_str = record->name_or_path;\n        const char* app_args = record->args;\n        FURI_LOG_I(TAG, \"Deferred launch: %s\", app_name_str);\n\n        LoaderMessageLoaderStatusResult result =\n            loader_do_start_by_name(loader, app_name_str, app_args, error_message);\n        if(result.value == LoaderStatusOk) {\n            is_successful = true;\n            break;\n        }\n\n        if(record->flags & LoaderDeferredLaunchFlagGui)\n            loader_show_gui_error(result, app_name_str, error_message);\n\n        loader_do_next_deferred_launch_if_available(loader);\n    } while(false);\n\n    view_holder_set_view(loader->view_holder, NULL);\n    furi_string_free(error_message);\n    return is_successful;\n}\n\nstatic void loader_do_app_closed(Loader* loader) {\n    furi_assert(loader->app.thread);\n\n    furi_thread_join(loader->app.thread);\n    FURI_LOG_I(TAG, \"App returned: %li\", furi_thread_get_return_code(loader->app.thread));\n\n    if(loader->app.args) {\n        free(loader->app.args);\n        loader->app.args = NULL;\n    }\n\n    if(loader->app.insomniac) {\n        furi_hal_power_insomnia_exit();\n    }\n\n    if(loader->app.fap) {\n        flipper_application_free(loader->app.fap);\n        loader->app.fap = NULL;\n        loader->app.thread = NULL;\n    } else {\n        furi_thread_free(loader->app.thread);\n        loader->app.thread = NULL;\n    }\n\n    furi_string_free(loader->app.launch_path);\n\n    FURI_LOG_I(TAG, \"Application stopped. Free heap: %zu\", memmgr_get_free_heap());\n\n    LoaderEvent event;\n    event.type = LoaderEventTypeApplicationStopped;\n    furi_pubsub_publish(loader->pubsub, &event);\n\n    loader_do_next_deferred_launch_if_available(loader);\n}\n\nstatic bool loader_is_application_running(Loader* loader) {\n    FuriThread* app_thread = loader->app.thread;\n    return app_thread && (app_thread != (FuriThread*)LOADER_MAGIC_THREAD_VALUE);\n}\n\nstatic bool loader_do_signal(Loader* loader, uint32_t signal, void* arg) {\n    if(loader_is_application_running(loader)) {\n        return furi_thread_signal(loader->app.thread, signal, arg);\n    }\n\n    return false;\n}\n\nstatic bool loader_do_get_application_name(Loader* loader, FuriString* name) {\n    if(loader_is_application_running(loader)) {\n        furi_string_set(name, furi_thread_get_name(furi_thread_get_id(loader->app.thread)));\n        return true;\n    }\n\n    return false;\n}\n\nstatic bool loader_do_get_application_launch_path(Loader* loader, FuriString* path) {\n    if(loader_is_application_running(loader)) {\n        furi_string_set(path, loader->app.launch_path);\n        return true;\n    }\n\n    return false;\n}\n\n// app\n\nint32_t loader_srv(void* p) {\n    UNUSED(p);\n    Loader* loader = loader_alloc();\n    furi_record_create(RECORD_LOADER, loader);\n\n    FURI_LOG_I(TAG, \"Executing system start hooks\");\n    for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {\n        FLIPPER_ON_SYSTEM_START[i]();\n    }\n\n    if((furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) && FLIPPER_AUTORUN_APP_NAME &&\n       strlen(FLIPPER_AUTORUN_APP_NAME)) {\n        FURI_LOG_I(TAG, \"Starting autorun app: %s\", FLIPPER_AUTORUN_APP_NAME);\n        loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL);\n    }\n\n    LoaderMessage message;\n    while(true) {\n        if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {\n            switch(message.type) {\n            case LoaderMessageTypeStartByName: {\n                LoaderMessageLoaderStatusResult status = loader_do_start_by_name(\n                    loader,\n                    message.start.name,\n                    message.start.args,\n                    message.start.error_message); //-V595\n                *(message.status_value) = status;\n                if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader);\n                api_lock_unlock(message.api_lock);\n                break;\n            }\n            case LoaderMessageTypeStartByNameDetachedWithGuiError: {\n                FuriString* error_message = furi_string_alloc();\n                LoaderMessageLoaderStatusResult status = loader_do_start_by_name(\n                    loader, message.start.name, message.start.args, error_message); //-V595\n                loader_show_gui_error(status, message.start.name, error_message);\n                if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader);\n                if(message.start.name) free((void*)message.start.name);\n                if(message.start.args) free((void*)message.start.args);\n                furi_string_free(error_message);\n                break;\n            }\n            case LoaderMessageTypeShowMenu:\n                loader_do_menu_show(loader);\n                break;\n            case LoaderMessageTypeMenuClosed:\n                loader_do_menu_closed(loader);\n                break;\n            case LoaderMessageTypeIsLocked:\n                message.bool_value->value = loader_do_is_locked(loader);\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeAppClosed:\n                loader_do_app_closed(loader);\n                break;\n            case LoaderMessageTypeLock:\n                message.bool_value->value = loader_do_lock(loader);\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeUnlock:\n                loader_do_unlock(loader);\n                break;\n            case LoaderMessageTypeApplicationsClosed:\n                loader_do_applications_closed(loader);\n                break;\n            case LoaderMessageTypeSignal:\n                message.bool_value->value =\n                    loader_do_signal(loader, message.signal.signal, message.signal.arg);\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeGetApplicationName:\n                message.bool_value->value =\n                    loader_do_get_application_name(loader, message.application_name);\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeGetApplicationLaunchPath:\n                message.bool_value->value =\n                    loader_do_get_application_launch_path(loader, message.application_name);\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeEnqueueLaunch:\n                furi_check(loader_queue_push(&loader->launch_queue, &message.defer_start));\n                api_lock_unlock(message.api_lock);\n                break;\n            case LoaderMessageTypeClearLaunchQueue:\n                loader_queue_clear(&loader->launch_queue);\n                api_lock_unlock(message.api_lock);\n                break;\n            }\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/loader/loader.h",
    "content": "#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_LOADER            \"loader\"\n#define LOADER_APPLICATIONS_NAME \"Apps\"\n\ntypedef struct Loader Loader;\n\ntypedef enum {\n    LoaderStatusOk,\n    LoaderStatusErrorAppStarted,\n    LoaderStatusErrorUnknownApp,\n    LoaderStatusErrorInternal,\n} LoaderStatus;\n\ntypedef enum {\n    LoaderEventTypeApplicationBeforeLoad,\n    LoaderEventTypeApplicationLoadFailed,\n    LoaderEventTypeApplicationStopped,\n    LoaderEventTypeNoMoreAppsInQueue, //<! The normal `Stopped` event still fires before this one\n} LoaderEventType;\n\ntypedef struct {\n    LoaderEventType type;\n} LoaderEvent;\n\ntypedef enum {\n    LoaderDeferredLaunchFlagNone = 0,\n    LoaderDeferredLaunchFlagGui = (1 << 1), //<! Report launch to the user via a dialog\n} LoaderDeferredLaunchFlag;\n\n/**\n * @brief Start application\n * @param[in] instance loader instance\n * @param[in] name application name or id\n * @param[in] args application arguments\n * @param[out] error_message detailed error message, can be NULL\n * @return LoaderStatus\n */\nLoaderStatus\n    loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message);\n\n/**\n * @brief Start application with GUI error message\n * @param[in] instance loader instance\n * @param[in] name application name or id\n * @param[in] args application arguments\n * @return LoaderStatus\n */\nLoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);\n\n/**\n * @brief Start application detached with GUI error message\n * @param[in] instance loader instance\n * @param[in] name application name or id\n * @param[in] args application arguments\n */\nvoid loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args);\n\n/**\n * @brief Lock application start\n * @param[in] instance loader instance\n * @return true on success\n */\nbool loader_lock(Loader* instance);\n\n/**\n * @brief Unlock application start\n * @param[in] instance loader instance\n */\nvoid loader_unlock(Loader* instance);\n\n/**\n * @brief Check if loader is locked\n * @param[in] instance loader instance\n * @return true if locked\n */\nbool loader_is_locked(Loader* instance);\n\n/**\n * @brief Show loader menu\n * @param[in] instance loader instance\n */\nvoid loader_show_menu(Loader* instance);\n\n/**\n * @brief Get loader pubsub\n * @param[in] instance loader instance\n * @return FuriPubSub* \n */\nFuriPubSub* loader_get_pubsub(Loader* instance);\n\n/**\n * @brief Send a signal to the currently running application\n *\n * @param[in] instance pointer to the loader instance\n * @param[in] signal signal value to be sent\n * @param[inout] arg optional argument (can be of any value, including NULL)\n *\n * @return true if the signal was handled by the application, false otherwise\n */\nbool loader_signal(Loader* instance, uint32_t signal, void* arg);\n\n/**\n * @brief Get the name of the currently running application\n *\n * @param[in] instance pointer to the loader instance\n * @param[inout] name pointer to the string to contain the name (must be allocated)\n * @return true if it was possible to get an application name, false otherwise\n */\nbool loader_get_application_name(Loader* instance, FuriString* name);\n\n/**\n * @brief Get the launch path or name of the currently running application\n * \n * This is the string that was supplied to `loader_start` such that the current\n * app is running now. It might be a name (in the case of internal apps) or a\n * path (in the case of external apps). This value can be used to launch the\n * same app again.\n *\n * @param[in] instance pointer to the loader instance\n * @param[inout] name pointer to the string to contain the path or name\n *                    (must be allocated)\n * @return true if it was possible to get an application path, false otherwise\n */\nbool loader_get_application_launch_path(Loader* instance, FuriString* name);\n\n/**\n * @brief Enqueues a request to launch an application after the current one\n * \n * @param[in] instance pointer to the loader instance\n * @param[in] name pointer to the name or path of the application, or NULL to\n *                 cancel a previous request\n * @param[in] args pointer to argument to provide to the next app\n * @param[in] flags additional flags. see enum documentation for more info\n */\nvoid loader_enqueue_launch(\n    Loader* instance,\n    const char* name,\n    const char* args,\n    LoaderDeferredLaunchFlag flags);\n\n/**\n * @brief Removes all requests to launch applications after the current one\n * exits\n * \n * @param[in] instance pointer to the loader instance\n */\nvoid loader_clear_launch_queue(Loader* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/loader/loader_applications.c",
    "content": "#include \"loader.h\"\n#include \"loader_applications.h\"\n#include <dialogs/dialogs.h>\n#include <flipper_application/flipper_application.h>\n#include <assets_icons.h>\n#include <gui/gui.h>\n#include <gui/view_holder.h>\n#include <gui/modules/loading.h>\n#include <dolphin/dolphin.h>\n#include <lib/toolbox/path.h>\n\n#define TAG \"LoaderApplications\"\n\n#define JS_RUNNER_APP \"JS Runner\"\n\nstruct LoaderApplications {\n    FuriThread* thread;\n    void (*closed_cb)(void*);\n    void* context;\n};\n\nstatic int32_t loader_applications_thread(void* p);\n\nLoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) {\n    LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications));\n    loader_applications->thread =\n        furi_thread_alloc_ex(TAG, 768, loader_applications_thread, (void*)loader_applications);\n    loader_applications->closed_cb = closed_cb;\n    loader_applications->context = context;\n    furi_thread_start(loader_applications->thread);\n    return loader_applications;\n}\n\nvoid loader_applications_free(LoaderApplications* loader_applications) {\n    furi_assert(loader_applications);\n    furi_thread_join(loader_applications->thread);\n    furi_thread_free(loader_applications->thread);\n    free(loader_applications);\n}\n\ntypedef struct {\n    FuriString* file_path;\n    DialogsApp* dialogs;\n    Storage* storage;\n    Loader* loader;\n\n    Gui* gui;\n    ViewHolder* view_holder;\n    Loading* loading;\n} LoaderApplicationsApp;\n\nstatic LoaderApplicationsApp* loader_applications_app_alloc(void) {\n    LoaderApplicationsApp* app = malloc(sizeof(LoaderApplicationsApp)); //-V799\n    app->file_path = furi_string_alloc_set(EXT_PATH(\"apps\"));\n    app->dialogs = furi_record_open(RECORD_DIALOGS);\n    app->storage = furi_record_open(RECORD_STORAGE);\n    app->loader = furi_record_open(RECORD_LOADER);\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->view_holder = view_holder_alloc();\n    app->loading = loading_alloc();\n\n    view_holder_attach_to_gui(app->view_holder, app->gui);\n\n    return app;\n} //-V773\n\nstatic void loader_applications_app_free(LoaderApplicationsApp* app) {\n    furi_assert(app);\n\n    view_holder_free(app->view_holder);\n    loading_free(app->loading);\n    furi_record_close(RECORD_GUI);\n\n    furi_record_close(RECORD_LOADER);\n    furi_record_close(RECORD_DIALOGS);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(app->file_path);\n    free(app);\n}\n\nstatic bool loader_applications_item_callback(\n    FuriString* path,\n    void* context,\n    uint8_t** icon_ptr,\n    FuriString* item_name) {\n    LoaderApplicationsApp* loader_applications_app = context;\n    furi_assert(loader_applications_app);\n    if(furi_string_end_with(path, \".fap\")) {\n        return flipper_application_load_name_and_icon(\n            path, loader_applications_app->storage, icon_ptr, item_name);\n    } else {\n        path_extract_filename(path, item_name, false);\n        memcpy(*icon_ptr, icon_get_frame_data(&I_js_script_10px, 0), FAP_MANIFEST_MAX_ICON_SIZE);\n        return true;\n    }\n}\n\nstatic bool loader_applications_select_app(LoaderApplicationsApp* loader_applications_app) {\n    const DialogsFileBrowserOptions browser_options = {\n        .extension = \".fap|.js\",\n        .skip_assets = true,\n        .icon = &I_unknown_10px,\n        .hide_ext = true,\n        .item_loader_callback = loader_applications_item_callback,\n        .item_loader_context = loader_applications_app,\n        .base_path = EXT_PATH(\"apps\"),\n    };\n\n    return dialog_file_browser_show(\n        loader_applications_app->dialogs,\n        loader_applications_app->file_path,\n        loader_applications_app->file_path,\n        &browser_options);\n}\n\n#define APPLICATION_STOP_EVENT 1\n\nstatic void loader_pubsub_callback(const void* message, void* context) {\n    const LoaderEvent* event = message;\n    const FuriThreadId thread_id = (FuriThreadId)context;\n\n    if(event->type == LoaderEventTypeNoMoreAppsInQueue) {\n        furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT);\n    }\n}\n\nstatic void\n    loader_applications_start_app(LoaderApplicationsApp* app, const char* name, const char* args) {\n    dolphin_deed(DolphinDeedPluginStart);\n\n    // load app\n    FuriThreadId thread_id = furi_thread_get_current_id();\n    FuriPubSubSubscription* subscription =\n        furi_pubsub_subscribe(loader_get_pubsub(app->loader), loader_pubsub_callback, thread_id);\n\n    LoaderStatus status = loader_start_with_gui_error(app->loader, name, args);\n\n    if(status == LoaderStatusOk) {\n        furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever);\n    }\n\n    furi_pubsub_unsubscribe(loader_get_pubsub(app->loader), subscription);\n}\n\nstatic int32_t loader_applications_thread(void* p) {\n    LoaderApplications* loader_applications = p;\n    LoaderApplicationsApp* app = loader_applications_app_alloc();\n\n    // start loading animation\n    view_holder_set_view(app->view_holder, loading_get_view(app->loading));\n\n    while(loader_applications_select_app(app)) {\n        if(!furi_string_end_with(app->file_path, \".js\")) {\n            loader_applications_start_app(app, furi_string_get_cstr(app->file_path), NULL);\n        } else {\n            loader_applications_start_app(\n                app, JS_RUNNER_APP, furi_string_get_cstr(app->file_path));\n        }\n    }\n\n    // stop loading animation\n    view_holder_set_view(app->view_holder, NULL);\n\n    loader_applications_app_free(app);\n\n    if(loader_applications->closed_cb) {\n        loader_applications->closed_cb(loader_applications->context);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/loader/loader_applications.h",
    "content": "#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct LoaderApplications LoaderApplications;\n\nLoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context);\n\nvoid loader_applications_free(LoaderApplications* loader_applications);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/loader/loader_cli.c",
    "content": "#include \"loader.h\"\n\n#include <furi.h>\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <applications.h>\n#include <lib/toolbox/args.h>\n#include <lib/toolbox/strint.h>\n#include <notification/notification_messages.h>\n#include <toolbox/pipe.h>\n\nstatic void loader_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"loader <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n    printf(\"\\tlist\\t - List available applications\\r\\n\");\n    printf(\"\\topen <Application Name:string>\\t - Open application by name\\r\\n\");\n    printf(\"\\tinfo\\t - Show loader state\\r\\n\");\n    printf(\"\\tclose\\t - Close the current application\\r\\n\");\n    printf(\"\\tsignal <signal:number> [arg:hex]\\t - Send a signal with an optional argument\\r\\n\");\n}\n\nstatic void loader_cli_list(void) {\n    printf(\"Apps:\\r\\n\");\n    for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {\n        printf(\"\\t%s\\r\\n\", FLIPPER_APPS[i].name);\n    }\n    printf(\"Settings:\\r\\n\");\n    for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {\n        printf(\"\\t%s\\r\\n\", FLIPPER_SETTINGS_APPS[i].name);\n    }\n}\n\nstatic void loader_cli_info(Loader* loader) {\n    FuriString* app_name = furi_string_alloc();\n\n    if(!loader_get_application_name(loader, app_name)) {\n        printf(\"No application is running\\r\\n\");\n    } else {\n        printf(\"Application \\\"%s\\\" is running\\r\\n\", furi_string_get_cstr(app_name));\n    }\n\n    furi_string_free(app_name);\n}\n\nstatic void loader_cli_open(FuriString* args, Loader* loader) {\n    FuriString* app_name = furi_string_alloc();\n\n    do {\n        if(!args_read_probably_quoted_string_and_trim(args, app_name)) {\n            printf(\"No application provided\\r\\n\");\n            break;\n        }\n        furi_string_trim(args);\n\n        const char* args_str = furi_string_get_cstr(args);\n        if(strlen(args_str) == 0) {\n            args_str = NULL;\n        }\n\n        const char* app_name_str = furi_string_get_cstr(app_name);\n\n        FuriString* error_message = furi_string_alloc();\n        if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) {\n            printf(\"%s\\r\\n\", furi_string_get_cstr(error_message));\n        } else {\n#ifdef SRV_NOTIFICATION\n            NotificationApp* notification_srv = furi_record_open(RECORD_NOTIFICATION);\n            notification_message(notification_srv, &sequence_display_backlight_on);\n            furi_record_close(RECORD_NOTIFICATION);\n#endif\n        }\n        furi_string_free(error_message);\n    } while(false);\n\n    furi_string_free(app_name);\n}\n\nstatic void loader_cli_close(Loader* loader) {\n    FuriString* app_name = furi_string_alloc();\n\n    if(!loader_get_application_name(loader, app_name)) {\n        printf(\"No application is running\\r\\n\");\n    } else if(!loader_signal(loader, FuriSignalExit, NULL)) {\n        printf(\"Application \\\"%s\\\" has to be closed manually\\r\\n\", furi_string_get_cstr(app_name));\n    } else {\n        printf(\"Application \\\"%s\\\" was closed\\r\\n\", furi_string_get_cstr(app_name));\n    }\n\n    furi_string_free(app_name);\n}\n\nstatic void loader_cli_signal(FuriString* args, Loader* loader) {\n    uint32_t signal;\n    uint32_t arg = 0;\n    StrintParseError parse_err = 0;\n    char* args_cstr = (char*)furi_string_get_cstr(args);\n    parse_err |= strint_to_uint32(args_cstr, &args_cstr, &signal, 10);\n    parse_err |= strint_to_uint32(args_cstr, &args_cstr, &arg, 16);\n\n    if(parse_err) {\n        printf(\"Signal must be a decimal number\\r\\n\");\n    } else if(!loader_is_locked(loader)) {\n        printf(\"No application is running\\r\\n\");\n    } else {\n        const bool is_handled = loader_signal(loader, signal, (void*)arg);\n        printf(\n            \"Signal %lu with argument 0x%p was %s\\r\\n\",\n            signal,\n            (void*)arg,\n            is_handled ? \"handled\" : \"ignored\");\n    }\n}\n\nstatic void loader_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n    Loader* loader = furi_record_open(RECORD_LOADER);\n\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    if(!args_read_string_and_trim(args, cmd)) {\n        loader_cli_print_usage();\n    } else if(furi_string_equal(cmd, \"list\")) {\n        loader_cli_list();\n    } else if(furi_string_equal(cmd, \"open\")) {\n        loader_cli_open(args, loader);\n    } else if(furi_string_equal(cmd, \"info\")) {\n        loader_cli_info(loader);\n    } else if(furi_string_equal(cmd, \"close\")) {\n        loader_cli_close(loader);\n    } else if(furi_string_equal(cmd, \"signal\")) {\n        loader_cli_signal(args, loader);\n    } else {\n        loader_cli_print_usage();\n    }\n\n    furi_string_free(cmd);\n    furi_record_close(RECORD_LOADER);\n}\n\nvoid loader_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(\n        registry, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(loader_cli);\n#endif\n}\n"
  },
  {
    "path": "applications/services/loader/loader_i.h",
    "content": "#pragma once\n#include <furi.h>\n#include <toolbox/api_lock.h>\n#include <flipper_application/flipper_application.h>\n\n#include <gui/gui.h>\n#include <gui/view_holder.h>\n#include <gui/modules/loading.h>\n\n#include <m-array.h>\n\n#include \"loader.h\"\n#include \"loader_menu.h\"\n#include \"loader_applications.h\"\n#include \"loader_queue.h\"\n\ntypedef struct {\n    FuriString* launch_path;\n    char* args;\n    FuriThread* thread;\n    bool insomniac;\n    FlipperApplication* fap;\n} LoaderAppData;\n\nstruct Loader {\n    FuriPubSub* pubsub;\n    FuriMessageQueue* queue;\n    LoaderMenu* loader_menu;\n    LoaderApplications* loader_applications;\n    LoaderAppData app;\n\n    LoaderLaunchQueue launch_queue;\n\n    Gui* gui;\n    ViewHolder* view_holder;\n    Loading* loading;\n};\n\ntypedef enum {\n    LoaderMessageTypeStartByName,\n    LoaderMessageTypeAppClosed,\n    LoaderMessageTypeShowMenu,\n    LoaderMessageTypeMenuClosed,\n    LoaderMessageTypeApplicationsClosed,\n    LoaderMessageTypeLock,\n    LoaderMessageTypeUnlock,\n    LoaderMessageTypeIsLocked,\n    LoaderMessageTypeStartByNameDetachedWithGuiError,\n    LoaderMessageTypeSignal,\n    LoaderMessageTypeGetApplicationName,\n    LoaderMessageTypeGetApplicationLaunchPath,\n    LoaderMessageTypeEnqueueLaunch,\n    LoaderMessageTypeClearLaunchQueue,\n} LoaderMessageType;\n\ntypedef struct {\n    const char* name;\n    const char* args;\n    FuriString* error_message;\n} LoaderMessageStartByName;\n\ntypedef struct {\n    uint32_t signal;\n    void* arg;\n} LoaderMessageSignal;\n\ntypedef enum {\n    LoaderStatusErrorUnknown,\n    LoaderStatusErrorInvalidFile,\n    LoaderStatusErrorInvalidManifest,\n    LoaderStatusErrorMissingImports,\n    LoaderStatusErrorHWMismatch,\n    LoaderStatusErrorOutdatedApp,\n    LoaderStatusErrorOutOfMemory,\n    LoaderStatusErrorOutdatedFirmware,\n} LoaderStatusError;\n\ntypedef struct {\n    LoaderStatus value;\n    LoaderStatusError error;\n} LoaderMessageLoaderStatusResult;\n\ntypedef struct {\n    bool value;\n} LoaderMessageBoolResult;\n\ntypedef struct {\n    FuriApiLock api_lock;\n    LoaderMessageType type;\n\n    union {\n        LoaderMessageStartByName start;\n        LoaderDeferredLaunchRecord defer_start;\n        LoaderMessageSignal signal;\n        FuriString* application_name;\n    };\n\n    union {\n        LoaderMessageLoaderStatusResult* status_value;\n        LoaderMessageBoolResult* bool_value;\n    };\n} LoaderMessage;\n"
  },
  {
    "path": "applications/services/loader/loader_menu.c",
    "content": "#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/menu.h>\n#include <gui/modules/submenu.h>\n#include <assets_icons.h>\n#include <applications.h>\n#include <archive/helpers/archive_favorites.h>\n\n#include \"loader.h\"\n#include \"loader_menu.h\"\n\n#define TAG \"LoaderMenu\"\n\nstruct LoaderMenu {\n    FuriThread* thread;\n    void (*closed_cb)(void*);\n    void* context;\n};\n\nstatic int32_t loader_menu_thread(void* p);\n\nLoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context) {\n    LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu));\n    loader_menu->closed_cb = closed_cb;\n    loader_menu->context = context;\n    loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu);\n    furi_thread_start(loader_menu->thread);\n    return loader_menu;\n}\n\nvoid loader_menu_free(LoaderMenu* loader_menu) {\n    furi_assert(loader_menu);\n    furi_thread_join(loader_menu->thread);\n    furi_thread_free(loader_menu->thread);\n    free(loader_menu);\n}\n\ntypedef enum {\n    LoaderMenuViewPrimary,\n    LoaderMenuViewSettings,\n} LoaderMenuView;\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Menu* primary_menu;\n    Submenu* settings_menu;\n} LoaderMenuApp;\n\nstatic void loader_menu_start(const char* name) {\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    loader_start_with_gui_error(loader, name, NULL);\n    furi_record_close(RECORD_LOADER);\n}\n\nstatic void loader_menu_apps_callback(void* context, uint32_t index) {\n    UNUSED(context);\n    const char* name = FLIPPER_APPS[index].name;\n    loader_menu_start(name);\n}\n\nstatic void loader_menu_external_apps_callback(void* context, uint32_t index) {\n    UNUSED(context);\n    const char* path = FLIPPER_EXTERNAL_APPS[index].name;\n    loader_menu_start(path);\n}\n\nstatic void loader_menu_applications_callback(void* context, uint32_t index) {\n    UNUSED(index);\n    UNUSED(context);\n    const char* name = LOADER_APPLICATIONS_NAME;\n    loader_menu_start(name);\n}\n\nstatic void\n    loader_menu_settings_menu_callback(void* context, InputType input_type, uint32_t index) {\n    UNUSED(context);\n    if(input_type == InputTypeShort) {\n        const char* name = FLIPPER_SETTINGS_APPS[index].name;\n        loader_menu_start(name);\n    } else if(input_type == InputTypeLong) {\n        const char* name = FLIPPER_SETTINGS_APPS[index].name;\n        archive_favorites_handle_setting_pin_unpin(name, NULL);\n    }\n}\n\nstatic void loader_menu_switch_to_settings(void* context, uint32_t index) {\n    UNUSED(index);\n    LoaderMenuApp* app = context;\n    view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings);\n}\n\nstatic uint32_t loader_menu_switch_to_primary(void* context) {\n    UNUSED(context);\n    return LoaderMenuViewPrimary;\n}\n\nstatic uint32_t loader_menu_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) {\n    size_t i;\n\n    for(i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {\n        menu_add_item(\n            app->primary_menu,\n            FLIPPER_EXTERNAL_APPS[i].name,\n            FLIPPER_EXTERNAL_APPS[i].icon,\n            i,\n            loader_menu_external_apps_callback,\n            (void*)menu);\n    }\n\n    for(i = 0; i < FLIPPER_APPS_COUNT; i++) {\n        menu_add_item(\n            app->primary_menu,\n            FLIPPER_APPS[i].name,\n            FLIPPER_APPS[i].icon,\n            i,\n            loader_menu_apps_callback,\n            (void*)menu);\n    }\n    menu_add_item(\n        app->primary_menu, \"Settings\", &A_Settings_14, i++, loader_menu_switch_to_settings, app);\n    menu_add_item(\n        app->primary_menu,\n        LOADER_APPLICATIONS_NAME,\n        &A_Plugins_14,\n        i++,\n        loader_menu_applications_callback,\n        (void*)menu);\n}\n\nstatic void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) {\n    for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {\n        submenu_add_item_ex(\n            app->settings_menu,\n            FLIPPER_SETTINGS_APPS[i].name,\n            i,\n            loader_menu_settings_menu_callback,\n            loader_menu);\n    }\n}\n\nstatic LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) {\n    LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp));\n    app->gui = furi_record_open(RECORD_GUI);\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->primary_menu = menu_alloc();\n    app->settings_menu = submenu_alloc();\n\n    loader_menu_build_menu(app, loader_menu);\n    loader_menu_build_submenu(app, loader_menu);\n\n    // Primary menu\n    View* primary_view = menu_get_view(app->primary_menu);\n    view_set_context(primary_view, app->primary_menu);\n    view_set_previous_callback(primary_view, loader_menu_exit);\n    view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view);\n\n    // Settings menu\n    View* settings_view = submenu_get_view(app->settings_menu);\n    view_set_context(settings_view, app->settings_menu);\n    view_set_previous_callback(settings_view, loader_menu_switch_to_primary);\n    view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewPrimary);\n\n    return app;\n}\n\nstatic void loader_menu_app_free(LoaderMenuApp* app) {\n    view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary);\n    view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings);\n    view_dispatcher_free(app->view_dispatcher);\n\n    menu_free(app->primary_menu);\n    submenu_free(app->settings_menu);\n    furi_record_close(RECORD_GUI);\n    free(app);\n}\n\nstatic int32_t loader_menu_thread(void* p) {\n    LoaderMenu* loader_menu = p;\n    furi_assert(loader_menu);\n\n    LoaderMenuApp* app = loader_menu_app_alloc(loader_menu);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n    view_dispatcher_run(app->view_dispatcher);\n\n    if(loader_menu->closed_cb) {\n        loader_menu->closed_cb(loader_menu->context);\n    }\n\n    loader_menu_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/loader/loader_menu.h",
    "content": "#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct LoaderMenu LoaderMenu;\n\nLoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context);\n\nvoid loader_menu_free(LoaderMenu* loader_menu);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/loader/loader_queue.c",
    "content": "#include \"loader_queue.h\"\n\nvoid loader_queue_item_clear(LoaderDeferredLaunchRecord* item) {\n    free(item->args);\n    free(item->name_or_path);\n}\n\nbool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) {\n    if(!queue->item_cnt) return false;\n\n    *item = queue->items[0];\n    queue->item_cnt--;\n    memmove(\n        &queue->items[0], &queue->items[1], queue->item_cnt * sizeof(LoaderDeferredLaunchRecord));\n\n    return true;\n}\n\nbool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) {\n    if(queue->item_cnt == LOADER_QUEUE_MAX_SIZE) return false;\n\n    queue->items[queue->item_cnt] = *item;\n    queue->item_cnt++;\n\n    return true;\n}\n\nvoid loader_queue_clear(LoaderLaunchQueue* queue) {\n    for(size_t i = 0; i < queue->item_cnt; i++)\n        loader_queue_item_clear(&queue->items[i]);\n    queue->item_cnt = 0;\n}\n"
  },
  {
    "path": "applications/services/loader/loader_queue.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include \"loader.h\"\n\n#define LOADER_QUEUE_MAX_SIZE 4\n\ntypedef struct {\n    char* name_or_path;\n    char* args;\n    LoaderDeferredLaunchFlag flags;\n} LoaderDeferredLaunchRecord;\n\ntypedef struct {\n    LoaderDeferredLaunchRecord items[LOADER_QUEUE_MAX_SIZE];\n    size_t item_cnt;\n} LoaderLaunchQueue;\n\n/**\n * @brief Frees internal data in a `DeferredLaunchRecord`\n * \n * @param[out] item Record to clear\n */\nvoid loader_queue_item_clear(LoaderDeferredLaunchRecord* item);\n\n/**\n * @brief Fetches the next item from the launch queue\n * \n * @param[inout] queue Queue instance\n * @param[out]   item  Item output\n * \n * @return `true` if `item` was populated, `false` if queue is empty\n */\nbool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item);\n\n/**\n * @brief Puts an item into the launch queue\n * \n * @param[inout] queue Queue instance\n * @param[in]    item  Item to put in the queue\n * \n * @return `true` if the item was put into the queue, `false` if there's no more\n * space left\n */\nbool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item);\n\n/**\n * @brief Clears the launch queue\n * \n * @param[inout] queue Queue instance\n */\nvoid loader_queue_clear(LoaderLaunchQueue* queue);\n"
  },
  {
    "path": "applications/services/locale/application.fam",
    "content": "App(\n    appid=\"locale\",\n    name=\"LocaleSrv\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"locale_on_system_start\",\n    cdefines=[\"SRV_LOCALE\"],\n    order=70,\n    sdk_headers=[\"locale.h\"],\n)\n"
  },
  {
    "path": "applications/services/locale/locale.c",
    "content": "#include \"locale.h\"\n\n#define TAG \"LocaleSrv\"\n\nLocaleMeasurementUnits locale_get_measurement_unit(void) {\n    return (LocaleMeasurementUnits)furi_hal_rtc_get_locale_units();\n}\n\nvoid locale_set_measurement_unit(LocaleMeasurementUnits format) {\n    furi_hal_rtc_set_locale_units((FuriHalRtcLocaleUnits)format);\n}\n\nLocaleTimeFormat locale_get_time_format(void) {\n    return (LocaleTimeFormat)furi_hal_rtc_get_locale_timeformat();\n}\n\nvoid locale_set_time_format(LocaleTimeFormat format) {\n    furi_hal_rtc_set_locale_timeformat((FuriHalRtcLocaleTimeFormat)format);\n}\n\nLocaleDateFormat locale_get_date_format(void) {\n    return (LocaleDateFormat)furi_hal_rtc_get_locale_dateformat();\n}\n\nvoid locale_set_date_format(LocaleDateFormat format) {\n    furi_hal_rtc_set_locale_dateformat((FuriHalRtcLocaleDateFormat)format);\n}\n\nfloat locale_fahrenheit_to_celsius(float temp_f) {\n    return (temp_f - 32.f) / 1.8f;\n}\n\nfloat locale_celsius_to_fahrenheit(float temp_c) {\n    return temp_c * 1.8f + 32.f;\n}\n\nvoid locale_format_time(\n    FuriString* out_str,\n    const DateTime* datetime,\n    const LocaleTimeFormat format,\n    const bool show_seconds) {\n    furi_check(out_str);\n    furi_check(datetime);\n\n    uint8_t hours = datetime->hour;\n    uint8_t am_pm = 0;\n    if(format == LocaleTimeFormat12h) {\n        if(hours > 12) {\n            hours -= 12;\n            am_pm = 2;\n        } else {\n            am_pm = 1;\n        }\n        if(hours == 0) {\n            hours = 12;\n        }\n    }\n\n    if(show_seconds) {\n        furi_string_printf(out_str, \"%02u:%02u:%02u\", hours, datetime->minute, datetime->second);\n    } else {\n        furi_string_printf(out_str, \"%02u:%02u\", hours, datetime->minute);\n    }\n\n    if(am_pm > 0) {\n        furi_string_cat_printf(out_str, \" %s\", (am_pm == 1) ? (\"AM\") : (\"PM\"));\n    }\n}\n\nvoid locale_format_date(\n    FuriString* out_str,\n    const DateTime* datetime,\n    const LocaleDateFormat format,\n    const char* separator) {\n    furi_check(out_str);\n    furi_check(datetime);\n    furi_check(separator);\n\n    if(format == LocaleDateFormatDMY) {\n        furi_string_printf(\n            out_str,\n            \"%02u%s%02u%s%04u\",\n            datetime->day,\n            separator,\n            datetime->month,\n            separator,\n            datetime->year);\n    } else if(format == LocaleDateFormatMDY) {\n        furi_string_printf(\n            out_str,\n            \"%02u%s%02u%s%04u\",\n            datetime->month,\n            separator,\n            datetime->day,\n            separator,\n            datetime->year);\n    } else {\n        furi_string_printf(\n            out_str,\n            \"%04u%s%02u%s%02u\",\n            datetime->year,\n            separator,\n            datetime->month,\n            separator,\n            datetime->day);\n    }\n}\n\nint32_t locale_on_system_start(void* p) {\n    UNUSED(p);\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/locale/locale.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <furi.h>\n#include <furi_hal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    LocaleMeasurementUnitsMetric = 0, /**< Metric measurement units */\n    LocaleMeasurementUnitsImperial = 1, /**< Imperial measurement units */\n} LocaleMeasurementUnits;\n\ntypedef enum {\n    LocaleTimeFormat24h = 0, /**< 24-hour format */\n    LocaleTimeFormat12h = 1, /**< 12-hour format */\n} LocaleTimeFormat;\n\ntypedef enum {\n    LocaleDateFormatDMY = 0, /**< Day/Month/Year */\n    LocaleDateFormatMDY = 1, /**< Month/Day/Year */\n    LocaleDateFormatYMD = 2, /**< Year/Month/Day */\n} LocaleDateFormat;\n\n/** Get Locale measurement units\n *\n * @return     The locale measurement units.\n */\nLocaleMeasurementUnits locale_get_measurement_unit(void);\n\n/** Set locale measurement units\n *\n * @param[in]  format  The locale measurements units\n */\nvoid locale_set_measurement_unit(LocaleMeasurementUnits format);\n\n/** Convert Fahrenheit to Celsius\n *\n * @param[in]  temp_f  The Temperature in Fahrenheit\n *\n * @return     The Temperature in Celsius \n */\nfloat locale_fahrenheit_to_celsius(float temp_f);\n\n/** Convert Celsius to Fahrenheit\n *\n * @param[in]  temp_c  The Temperature in Celsius \n *\n * @return     The Temperature in Fahrenheit\n */\nfloat locale_celsius_to_fahrenheit(float temp_c);\n\n/** Get Locale time format\n *\n * @return     The locale time format.\n */\nLocaleTimeFormat locale_get_time_format(void);\n\n/** Set Locale Time Format\n *\n * @param[in]  format  The Locale Time Format\n */\nvoid locale_set_time_format(LocaleTimeFormat format);\n\n/** Format time to furi string\n *\n * @param[out] out_str       The FuriString to store formatted time\n * @param[in]  datetime      Pointer to the datetime\n * @param[in]  format        The Locale Time Format\n * @param[in]  show_seconds  The show seconds flag\n */\nvoid locale_format_time(\n    FuriString* out_str,\n    const DateTime* datetime,\n    const LocaleTimeFormat format,\n    const bool show_seconds);\n\n/** Get Locale DateFormat\n *\n * @return     The Locale DateFormat.\n */\nLocaleDateFormat locale_get_date_format(void);\n\n/** Set Locale DateFormat\n *\n * @param[in]  format  The Locale DateFormat\n */\nvoid locale_set_date_format(LocaleDateFormat format);\n\n/** Format date to furi string\n *\n * @param[out] out_str    The FuriString to store formatted date\n * @param[in]  datetime   Pointer to the datetime\n * @param[in]  format     The format\n * @param[in]  separator  The separator\n */\nvoid locale_format_date(\n    FuriString* out_str,\n    const DateTime* datetime,\n    const LocaleDateFormat format,\n    const char* separator);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/notification/application.fam",
    "content": "App(\n    appid=\"notification\",\n    name=\"NotificationSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"notification_srv\",\n    cdefines=[\"SRV_NOTIFICATION\"],\n    requires=[\"input\"],\n    provides=[\"notification_settings\"],\n    stack_size=int(1.5 * 1024),\n    order=100,\n    sdk_headers=[\"notification.h\", \"notification_messages.h\"],\n)\n"
  },
  {
    "path": "applications/services/notification/notification.h",
    "content": "#pragma once\n#include \"stdint.h\"\n#include \"stdbool.h\"\n#include <furi_hal_resources.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_NOTIFICATION \"notification\"\n\ntypedef struct NotificationApp NotificationApp;\ntypedef struct {\n    float frequency;\n    float volume;\n} NotificationMessageDataSound;\n\ntypedef struct {\n    uint8_t value;\n} NotificationMessageDataLed;\n\ntypedef struct {\n    bool on;\n} NotificationMessageDataVibro;\n\ntypedef struct {\n    uint32_t length;\n} NotificationMessageDataDelay;\n\ntypedef struct {\n    float speaker_volume;\n    bool vibro;\n    float display_brightness;\n} NotificationMessageDataForcedSettings;\n\ntypedef struct {\n    uint16_t on_time;\n    uint16_t period;\n    Light color;\n} NotificationMessageDataLedBlink;\n\ntypedef union {\n    NotificationMessageDataSound sound;\n    NotificationMessageDataLed led;\n    NotificationMessageDataLedBlink led_blink;\n    NotificationMessageDataVibro vibro;\n    NotificationMessageDataDelay delay;\n    NotificationMessageDataForcedSettings forced_settings;\n} NotificationMessageData;\n\ntypedef enum {\n    NotificationMessageTypeVibro,\n\n    NotificationMessageTypeSoundOn,\n    NotificationMessageTypeSoundOff,\n\n    NotificationMessageTypeLedRed,\n    NotificationMessageTypeLedGreen,\n    NotificationMessageTypeLedBlue,\n\n    NotificationMessageTypeLedBlinkStart,\n    NotificationMessageTypeLedBlinkStop,\n    NotificationMessageTypeLedBlinkColor,\n\n    NotificationMessageTypeDelay,\n\n    NotificationMessageTypeLedDisplayBacklight,\n    NotificationMessageTypeLedDisplayBacklightEnforceOn,\n    NotificationMessageTypeLedDisplayBacklightEnforceAuto,\n\n    NotificationMessageTypeDoNotReset,\n\n    NotificationMessageTypeForceSpeakerVolumeSetting,\n    NotificationMessageTypeForceVibroSetting,\n    NotificationMessageTypeForceDisplayBrightnessSetting,\n\n    NotificationMessageTypeLedBrightnessSettingApply,\n\n    NotificationMessageTypeLcdContrastUpdate,\n} NotificationMessageType;\n\ntypedef struct {\n    NotificationMessageType type;\n    NotificationMessageData data;\n} NotificationMessage;\n\ntypedef const NotificationMessage* NotificationSequence[];\n\nvoid notification_message(NotificationApp* app, const NotificationSequence* sequence);\nvoid notification_message_block(NotificationApp* app, const NotificationSequence* sequence);\n\n/**\n * @brief Send internal (apply to permanent layer) notification message. Think twice before use.\n * \n * @param app notification record content\n * @param sequence notification sequence\n */\nvoid notification_internal_message(NotificationApp* app, const NotificationSequence* sequence);\n\n/**\n * @brief Send internal (apply to permanent layer) notification message and wait for notification end. Think twice before use.\n * \n * @param app notification record content\n * @param sequence notification sequence\n */\nvoid notification_internal_message_block(\n    NotificationApp* app,\n    const NotificationSequence* sequence);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/notification/notification_app.c",
    "content": "#include <furi_hal_light.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <input/input.h>\n#include <gui/gui_i.h>\n#include <u8g2_glue.h>\n#include <lib/toolbox/float_tools.h>\n#include \"notification.h\"\n#include \"notification_messages.h\"\n#include \"notification_app.h\"\n\n#define TAG \"NotificationSrv\"\n\nstatic const uint8_t minimal_delay = 100;\nstatic const uint8_t led_off_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};\n\nstatic const uint8_t reset_red_mask = 1 << 0;\nstatic const uint8_t reset_green_mask = 1 << 1;\nstatic const uint8_t reset_blue_mask = 1 << 2;\nstatic const uint8_t reset_vibro_mask = 1 << 3;\nstatic const uint8_t reset_sound_mask = 1 << 4;\nstatic const uint8_t reset_display_mask = 1 << 5;\nstatic const uint8_t reset_blink_mask = 1 << 6;\n\nstatic void notification_vibro_on(bool force);\nstatic void notification_vibro_off(void);\nstatic void notification_sound_on(float freq, float volume, bool force);\nstatic void notification_sound_off(void);\n\nstatic uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value);\nstatic uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value);\nstatic uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app);\n\nvoid notification_message_save_settings(NotificationApp* app) {\n    NotificationAppMessage m = {\n        .type = SaveSettingsMessage, .back_event = furi_event_flag_alloc()};\n    furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n    furi_event_flag_wait(\n        m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_event_flag_free(m.back_event);\n}\n\n// internal layer\nstatic void\n    notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) {\n    furi_assert(layer);\n    furi_assert(layer->index < LayerMAX);\n\n    // set value\n    layer->value[LayerInternal] = layer_value;\n\n    // apply if current layer is internal\n    if(layer->index == LayerInternal) {\n        furi_hal_light_set(layer->light, layer->value[LayerInternal]);\n    }\n}\n\nstatic void notification_apply_lcd_contrast(NotificationApp* app) {\n    Gui* gui = furi_record_open(RECORD_GUI);\n    u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast);\n    furi_record_close(RECORD_GUI);\n}\n\nstatic bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) {\n    bool result = false;\n    if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) ||\n       (app->led[2].index == LayerInternal)) {\n        if((app->led[0].value[LayerInternal] != 0x00) ||\n           (app->led[1].value[LayerInternal] != 0x00) ||\n           (app->led[2].value[LayerInternal] != 0x00)) {\n            result = true;\n        }\n    }\n\n    return result;\n}\n\n// notification layer\nstatic void notification_apply_notification_led_layer(\n    NotificationLedLayer* layer,\n    const uint8_t layer_value) {\n    furi_assert(layer);\n    furi_assert(layer->index < LayerMAX);\n\n    // set value\n    layer->index = LayerNotification;\n    // set layer\n    layer->value[LayerNotification] = layer_value;\n    // apply\n    furi_hal_light_set(layer->light, layer->value[LayerNotification]);\n}\n\nstatic void notification_reset_notification_led_layer(NotificationLedLayer* layer) {\n    furi_assert(layer);\n    furi_assert(layer->index < LayerMAX);\n\n    // set value\n    layer->value[LayerNotification] = 0;\n    // set layer\n    layer->index = LayerInternal;\n\n    // apply\n    furi_hal_light_set(layer->light, layer->value[LayerInternal]);\n}\n\nstatic void notification_reset_notification_layer(\n    NotificationApp* app,\n    uint8_t reset_mask,\n    float display_brightness_set) {\n    if(reset_mask & reset_blink_mask) {\n        furi_hal_light_blink_stop();\n    }\n    if(reset_mask & reset_red_mask) {\n        notification_reset_notification_led_layer(&app->led[0]);\n    }\n    if(reset_mask & reset_green_mask) {\n        notification_reset_notification_led_layer(&app->led[1]);\n    }\n    if(reset_mask & reset_blue_mask) {\n        notification_reset_notification_led_layer(&app->led[2]);\n    }\n    if(reset_mask & reset_vibro_mask) {\n        notification_vibro_off();\n    }\n    if(reset_mask & reset_sound_mask) {\n        notification_sound_off();\n    }\n    if(reset_mask & reset_display_mask) {\n        if(!float_is_equal(display_brightness_set, app->settings.display_brightness)) {\n            furi_hal_light_set(LightBacklight, app->settings.display_brightness * 0xFF);\n        }\n        furi_timer_start(app->display_timer, notification_settings_display_off_delay_ticks(app));\n    }\n}\n\nstatic void notification_apply_notification_leds(NotificationApp* app, const uint8_t* values) {\n    for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {\n        notification_apply_notification_led_layer(\n            &app->led[i], notification_settings_get_rgb_led_brightness(app, values[i]));\n    }\n}\n\n// settings\nuint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value) {\n    return value * app->settings.display_brightness;\n}\n\nstatic uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) {\n    return value * app->settings.led_brightness;\n}\n\nstatic uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) {\n    return (float)(app->settings.display_off_delay_ms) /\n           (1000.0f / furi_kernel_get_tick_frequency());\n}\n\n// generics\nstatic void notification_vibro_on(bool force) {\n    if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {\n        furi_hal_vibro_on(true);\n    }\n}\n\nstatic void notification_vibro_off(void) {\n    furi_hal_vibro_on(false);\n}\n\nstatic void notification_sound_on(float freq, float volume, bool force) {\n    if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {\n        if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {\n            furi_hal_speaker_start(freq, volume);\n        }\n    }\n}\n\nstatic void notification_sound_off(void) {\n    if(furi_hal_speaker_is_mine()) {\n        furi_hal_speaker_stop();\n        furi_hal_speaker_release();\n    }\n}\n\n// display timer\nstatic void notification_display_timer(void* ctx) {\n    furi_assert(ctx);\n    NotificationApp* app = ctx;\n    notification_message(app, &sequence_display_backlight_off);\n}\n\n// message processing\nstatic void notification_process_notification_message(\n    NotificationApp* app,\n    NotificationAppMessage* message) {\n    uint32_t notification_message_index = 0;\n    bool force_volume = false;\n    bool force_vibro = false;\n    const NotificationMessage* notification_message;\n    notification_message = (*message->sequence)[notification_message_index];\n\n    bool led_active = false;\n    uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};\n    bool reset_notifications = true;\n    float speaker_volume_setting = app->settings.speaker_volume;\n    bool vibro_setting = app->settings.vibro_on;\n    float display_brightness_setting = app->settings.display_brightness;\n\n    uint8_t reset_mask = 0;\n\n    while(notification_message != NULL) {\n        switch(notification_message->type) {\n        case NotificationMessageTypeLedDisplayBacklight:\n            // if on - switch on and start timer\n            // if off - switch off and stop timer\n            // on timer - switch off\n            if(notification_message->data.led.value > 0x00) {\n                notification_apply_notification_led_layer(\n                    &app->display,\n                    notification_message->data.led.value * display_brightness_setting);\n                reset_mask |= reset_display_mask;\n            } else {\n                reset_mask &= ~reset_display_mask;\n                notification_reset_notification_led_layer(&app->display);\n                if(furi_timer_is_running(app->display_timer)) {\n                    furi_timer_stop(app->display_timer);\n                }\n            }\n            break;\n        case NotificationMessageTypeLedDisplayBacklightEnforceOn:\n            furi_check(app->display_led_lock < UINT8_MAX);\n            app->display_led_lock++;\n            if(app->display_led_lock == 1) {\n                notification_apply_internal_led_layer(\n                    &app->display,\n                    notification_message->data.led.value * display_brightness_setting);\n            }\n            break;\n        case NotificationMessageTypeLedDisplayBacklightEnforceAuto:\n            if(app->display_led_lock > 0) {\n                app->display_led_lock--;\n                if(app->display_led_lock == 0) {\n                    notification_apply_internal_led_layer(\n                        &app->display,\n                        notification_message->data.led.value * display_brightness_setting);\n                }\n            } else {\n                FURI_LOG_E(TAG, \"Incorrect BacklightEnforce use\");\n            }\n            break;\n        case NotificationMessageTypeLedRed:\n            // store and send on delay or after seq\n            led_active = true;\n            led_values[0] = notification_message->data.led.value;\n            app->led[0].value_last[LayerNotification] = led_values[0];\n            reset_mask |= reset_red_mask;\n            break;\n        case NotificationMessageTypeLedGreen:\n            // store and send on delay or after seq\n            led_active = true;\n            led_values[1] = notification_message->data.led.value;\n            app->led[1].value_last[LayerNotification] = led_values[1];\n            reset_mask |= reset_green_mask;\n            break;\n        case NotificationMessageTypeLedBlue:\n            // store and send on delay or after seq\n            led_active = true;\n            led_values[2] = notification_message->data.led.value;\n            app->led[2].value_last[LayerNotification] = led_values[2];\n            reset_mask |= reset_blue_mask;\n            break;\n        case NotificationMessageTypeLedBlinkStart:\n            // store and send on delay or after seq\n            led_active = true;\n            furi_hal_light_blink_start(\n                notification_message->data.led_blink.color,\n                app->settings.led_brightness * 255,\n                notification_message->data.led_blink.on_time,\n                notification_message->data.led_blink.period);\n            reset_mask |= reset_blink_mask;\n            reset_mask |= reset_red_mask;\n            reset_mask |= reset_green_mask;\n            reset_mask |= reset_blue_mask;\n            break;\n        case NotificationMessageTypeLedBlinkColor:\n            led_active = true;\n            furi_hal_light_blink_set_color(notification_message->data.led_blink.color);\n            break;\n        case NotificationMessageTypeLedBlinkStop:\n            furi_hal_light_blink_stop();\n            reset_mask &= ~reset_blink_mask;\n            reset_mask |= reset_red_mask;\n            reset_mask |= reset_green_mask;\n            reset_mask |= reset_blue_mask;\n            break;\n        case NotificationMessageTypeVibro:\n            if(notification_message->data.vibro.on) {\n                if(vibro_setting) notification_vibro_on(force_vibro);\n            } else {\n                notification_vibro_off();\n            }\n            reset_mask |= reset_vibro_mask;\n            break;\n        case NotificationMessageTypeSoundOn:\n            notification_sound_on(\n                notification_message->data.sound.frequency,\n                notification_message->data.sound.volume * speaker_volume_setting,\n                force_volume);\n            reset_mask |= reset_sound_mask;\n            break;\n        case NotificationMessageTypeSoundOff:\n            notification_sound_off();\n            reset_mask |= reset_sound_mask;\n            break;\n        case NotificationMessageTypeDelay:\n            if(led_active) {\n                if(notification_is_any_led_layer_internal_and_not_empty(app)) {\n                    notification_apply_notification_leds(app, led_off_values);\n                    furi_delay_ms(minimal_delay);\n                }\n\n                led_active = false;\n\n                notification_apply_notification_leds(app, led_values);\n                reset_mask |= reset_red_mask;\n                reset_mask |= reset_green_mask;\n                reset_mask |= reset_blue_mask;\n            }\n\n            furi_delay_ms(notification_message->data.delay.length);\n            break;\n        case NotificationMessageTypeDoNotReset:\n            reset_notifications = false;\n            break;\n        case NotificationMessageTypeForceSpeakerVolumeSetting:\n            speaker_volume_setting = notification_message->data.forced_settings.speaker_volume;\n            force_volume = true;\n            break;\n        case NotificationMessageTypeForceVibroSetting:\n            vibro_setting = notification_message->data.forced_settings.vibro;\n            force_vibro = true;\n            break;\n        case NotificationMessageTypeForceDisplayBrightnessSetting:\n            display_brightness_setting =\n                notification_message->data.forced_settings.display_brightness;\n            break;\n        case NotificationMessageTypeLedBrightnessSettingApply:\n            led_active = true;\n            for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {\n                led_values[i] = app->led[i].value_last[LayerNotification];\n            }\n            reset_mask |= reset_red_mask;\n            reset_mask |= reset_green_mask;\n            reset_mask |= reset_blue_mask;\n            break;\n        case NotificationMessageTypeLcdContrastUpdate:\n            notification_apply_lcd_contrast(app);\n            break;\n        }\n        notification_message_index++;\n        notification_message = (*message->sequence)[notification_message_index];\n    };\n\n    // send and do minimal delay\n    if(led_active) {\n        bool need_minimal_delay = false;\n        if(notification_is_any_led_layer_internal_and_not_empty(app)) {\n            need_minimal_delay = true;\n        }\n\n        notification_apply_notification_leds(app, led_values);\n        reset_mask |= reset_red_mask;\n        reset_mask |= reset_green_mask;\n        reset_mask |= reset_blue_mask;\n\n        if((need_minimal_delay) && (reset_notifications)) {\n            notification_apply_notification_leds(app, led_off_values);\n            furi_delay_ms(minimal_delay);\n        }\n    }\n\n    if(reset_notifications) {\n        notification_reset_notification_layer(app, reset_mask, display_brightness_setting);\n    }\n}\n\nstatic void\n    notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) {\n    uint32_t notification_message_index = 0;\n    const NotificationMessage* notification_message;\n    notification_message = (*message->sequence)[notification_message_index];\n\n    while(notification_message != NULL) {\n        switch(notification_message->type) {\n        case NotificationMessageTypeLedDisplayBacklight:\n            notification_apply_internal_led_layer(\n                &app->display,\n                notification_settings_get_display_brightness(\n                    app, notification_message->data.led.value));\n            break;\n        case NotificationMessageTypeLedRed:\n            app->led[0].value_last[LayerInternal] = notification_message->data.led.value;\n            notification_apply_internal_led_layer(\n                &app->led[0],\n                notification_settings_get_rgb_led_brightness(\n                    app, notification_message->data.led.value));\n            break;\n        case NotificationMessageTypeLedGreen:\n            app->led[1].value_last[LayerInternal] = notification_message->data.led.value;\n            notification_apply_internal_led_layer(\n                &app->led[1],\n                notification_settings_get_rgb_led_brightness(\n                    app, notification_message->data.led.value));\n            break;\n        case NotificationMessageTypeLedBlue:\n            app->led[2].value_last[LayerInternal] = notification_message->data.led.value;\n            notification_apply_internal_led_layer(\n                &app->led[2],\n                notification_settings_get_rgb_led_brightness(\n                    app, notification_message->data.led.value));\n            break;\n        case NotificationMessageTypeLedBrightnessSettingApply:\n            for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {\n                uint8_t new_val = notification_settings_get_rgb_led_brightness(\n                    app, app->led[i].value_last[LayerInternal]);\n                notification_apply_internal_led_layer(&app->led[i], new_val);\n            }\n            break;\n        default:\n            break;\n        }\n        notification_message_index++;\n        notification_message = (*message->sequence)[notification_message_index];\n    }\n}\n\nstatic bool notification_load_settings(NotificationApp* app) {\n    NotificationSettings settings;\n    File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));\n    const size_t settings_size = sizeof(NotificationSettings);\n\n    FURI_LOG_I(TAG, \"Loading \\\"%s\\\"\", NOTIFICATION_SETTINGS_PATH);\n    bool fs_result =\n        storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(fs_result) {\n        size_t bytes_count = storage_file_read(file, &settings, settings_size);\n\n        if(bytes_count != settings_size) {\n            fs_result = false;\n        }\n    }\n\n    if(fs_result) {\n        if(settings.version != NOTIFICATION_SETTINGS_VERSION) {\n            FURI_LOG_E(\n                TAG, \"version(%d != %d) mismatch\", settings.version, NOTIFICATION_SETTINGS_VERSION);\n        } else {\n            furi_kernel_lock();\n            memcpy(&app->settings, &settings, settings_size);\n            furi_kernel_unlock();\n        }\n    } else {\n        FURI_LOG_E(TAG, \"Load failed, %s\", storage_file_get_error_desc(file));\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return fs_result;\n}\n\nstatic bool notification_save_settings(NotificationApp* app) {\n    NotificationSettings settings;\n    File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));\n    const size_t settings_size = sizeof(NotificationSettings);\n\n    FURI_LOG_I(TAG, \"Saving \\\"%s\\\"\", NOTIFICATION_SETTINGS_PATH);\n\n    furi_kernel_lock();\n    memcpy(&settings, &app->settings, settings_size);\n    furi_kernel_unlock();\n\n    bool fs_result =\n        storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS);\n\n    if(fs_result) {\n        size_t bytes_count = storage_file_write(file, &settings, settings_size);\n\n        if(bytes_count != settings_size) {\n            fs_result = false;\n        }\n    }\n\n    if(fs_result) {\n    } else {\n        FURI_LOG_E(TAG, \"Save failed, %s\", storage_file_get_error_desc(file));\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return fs_result;\n}\n\nstatic void input_event_callback(const void* value, void* context) {\n    furi_assert(value);\n    furi_assert(context);\n    NotificationApp* app = context;\n    notification_message(app, &sequence_display_backlight_on);\n}\n\n// App alloc\nstatic NotificationApp* notification_app_alloc(void) {\n    NotificationApp* app = malloc(sizeof(NotificationApp));\n    app->queue = furi_message_queue_alloc(8, sizeof(NotificationAppMessage));\n    app->display_timer = furi_timer_alloc(notification_display_timer, FuriTimerTypeOnce, app);\n\n    app->settings.speaker_volume = 1.0f;\n    app->settings.display_brightness = 1.0f;\n    app->settings.led_brightness = 1.0f;\n    app->settings.display_off_delay_ms = 30000;\n    app->settings.vibro_on = true;\n\n    app->display.value[LayerInternal] = 0x00;\n    app->display.value[LayerNotification] = 0x00;\n    app->display.index = LayerInternal;\n    app->display.light = LightBacklight;\n\n    app->led[0].value[LayerInternal] = 0x00;\n    app->led[0].value[LayerNotification] = 0x00;\n    app->led[0].index = LayerInternal;\n    app->led[0].light = LightRed;\n\n    app->led[1].value[LayerInternal] = 0x00;\n    app->led[1].value[LayerNotification] = 0x00;\n    app->led[1].index = LayerInternal;\n    app->led[1].light = LightGreen;\n\n    app->led[2].value[LayerInternal] = 0x00;\n    app->led[2].value[LayerNotification] = 0x00;\n    app->led[2].index = LayerInternal;\n    app->led[2].light = LightBlue;\n\n    app->settings.version = NOTIFICATION_SETTINGS_VERSION;\n\n    // display backlight control\n    app->event_record = furi_record_open(RECORD_INPUT_EVENTS);\n    furi_pubsub_subscribe(app->event_record, input_event_callback, app);\n    notification_message(app, &sequence_display_backlight_on);\n\n    return app;\n}\n\nstatic void notification_storage_callback(const void* message, void* context) {\n    furi_assert(context);\n    NotificationApp* app = context;\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        NotificationAppMessage m = {\n            .type = LoadSettingsMessage,\n        };\n\n        furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n    }\n}\n\nstatic void notification_apply_settings(NotificationApp* app) {\n    if(!notification_load_settings(app)) {\n        notification_save_settings(app);\n    }\n\n    notification_apply_lcd_contrast(app);\n}\n\nstatic void notification_init_settings(NotificationApp* app) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_subscribe(storage_get_pubsub(storage), notification_storage_callback, app);\n\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping settings\");\n        return;\n    }\n\n    notification_apply_settings(app);\n}\n\n// App\nint32_t notification_srv(void* p) {\n    UNUSED(p);\n    NotificationApp* app = notification_app_alloc();\n\n    notification_init_settings(app);\n\n    notification_vibro_off();\n    notification_sound_off();\n    notification_apply_internal_led_layer(&app->display, 0x00);\n    notification_apply_internal_led_layer(&app->led[0], 0x00);\n    notification_apply_internal_led_layer(&app->led[1], 0x00);\n    notification_apply_internal_led_layer(&app->led[2], 0x00);\n\n    furi_record_create(RECORD_NOTIFICATION, app);\n\n    NotificationAppMessage message;\n    while(1) {\n        furi_check(furi_message_queue_get(app->queue, &message, FuriWaitForever) == FuriStatusOk);\n\n        switch(message.type) {\n        case NotificationLayerMessage:\n            notification_process_notification_message(app, &message);\n            break;\n        case InternalLayerMessage:\n            notification_process_internal_message(app, &message);\n            break;\n        case SaveSettingsMessage:\n            notification_save_settings(app);\n            break;\n        case LoadSettingsMessage:\n            notification_load_settings(app);\n            break;\n        }\n\n        if(message.back_event != NULL) {\n            furi_event_flag_set(message.back_event, NOTIFICATION_EVENT_COMPLETE);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/notification/notification_app.h",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"notification.h\"\n#include \"notification_messages.h\"\n#include \"notification_settings_filename.h\"\n\n#define NOTIFICATION_LED_COUNT      3\n#define NOTIFICATION_EVENT_COMPLETE 0x00000001U\n\ntypedef enum {\n    NotificationLayerMessage,\n    InternalLayerMessage,\n    SaveSettingsMessage,\n    LoadSettingsMessage,\n} NotificationAppMessageType;\n\ntypedef struct {\n    const NotificationSequence* sequence;\n    NotificationAppMessageType type;\n    FuriEventFlag* back_event;\n} NotificationAppMessage;\n\ntypedef enum {\n    LayerInternal = 0,\n    LayerNotification = 1,\n    LayerMAX = 2,\n} NotificationLedLayerIndex;\n\ntypedef struct {\n    uint8_t value_last[LayerMAX];\n    uint8_t value[LayerMAX];\n    NotificationLedLayerIndex index;\n    Light light;\n} NotificationLedLayer;\n\n#define NOTIFICATION_SETTINGS_VERSION 0x02\n#define NOTIFICATION_SETTINGS_PATH    INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME)\n\ntypedef struct {\n    uint8_t version;\n    float display_brightness;\n    float led_brightness;\n    float speaker_volume;\n    uint32_t display_off_delay_ms;\n    int8_t contrast;\n    bool vibro_on;\n} NotificationSettings;\n\nstruct NotificationApp {\n    FuriMessageQueue* queue;\n    FuriPubSub* event_record;\n    FuriTimer* display_timer;\n\n    NotificationLedLayer display;\n    NotificationLedLayer led[NOTIFICATION_LED_COUNT];\n    uint8_t display_led_lock;\n\n    NotificationSettings settings;\n};\n\nvoid notification_message_save_settings(NotificationApp* app);\n"
  },
  {
    "path": "applications/services/notification/notification_app_api.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"notification.h\"\n#include \"notification_app.h\"\n\nvoid notification_message(NotificationApp* app, const NotificationSequence* sequence) {\n    furi_check(app);\n    furi_check(sequence);\n\n    NotificationAppMessage m = {\n        .type = NotificationLayerMessage, .sequence = sequence, .back_event = NULL};\n    furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid notification_internal_message(NotificationApp* app, const NotificationSequence* sequence) {\n    furi_check(app);\n    furi_check(sequence);\n\n    NotificationAppMessage m = {\n        .type = InternalLayerMessage, .sequence = sequence, .back_event = NULL};\n    furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid notification_message_block(NotificationApp* app, const NotificationSequence* sequence) {\n    furi_check(app);\n    furi_check(sequence);\n\n    NotificationAppMessage m = {\n        .type = NotificationLayerMessage,\n        .sequence = sequence,\n        .back_event = furi_event_flag_alloc()};\n    furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n    furi_event_flag_wait(\n        m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_event_flag_free(m.back_event);\n}\n\nvoid notification_internal_message_block(\n    NotificationApp* app,\n    const NotificationSequence* sequence) {\n    furi_check(app);\n    furi_check(sequence);\n\n    NotificationAppMessage m = {\n        .type = InternalLayerMessage, .sequence = sequence, .back_event = furi_event_flag_alloc()};\n    furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);\n    furi_event_flag_wait(\n        m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_event_flag_free(m.back_event);\n}\n"
  },
  {
    "path": "applications/services/notification/notification_messages.c",
    "content": "#include <furi_hal_resources.h>\n#include \"notification.h\"\n#include \"notification_messages_notes.h\"\n#include <stddef.h>\n\n/*********************************** Messages **********************************/\n\n/** Display: backlight wakeup */\nconst NotificationMessage message_display_backlight_on = {\n    .type = NotificationMessageTypeLedDisplayBacklight,\n    .data.led.value = 0xFF,\n};\n\n/** Display: backlight force off */\nconst NotificationMessage message_display_backlight_off = {\n    .type = NotificationMessageTypeLedDisplayBacklight,\n    .data.led.value = 0x00,\n};\n\n/** Display: backlight always on */\nconst NotificationMessage message_display_backlight_enforce_on = {\n    .type = NotificationMessageTypeLedDisplayBacklightEnforceOn,\n    .data.led.value = 0xFF,\n};\n\n/** Display: automatic backlight management, with configured timeout */\nconst NotificationMessage message_display_backlight_enforce_auto = {\n    .type = NotificationMessageTypeLedDisplayBacklightEnforceAuto,\n    .data.led.value = 0x00,\n};\n\n// Led ON\nconst NotificationMessage message_red_255 = {\n    .type = NotificationMessageTypeLedRed,\n    .data.led.value = 0xFF,\n};\n\nconst NotificationMessage message_green_255 = {\n    .type = NotificationMessageTypeLedGreen,\n    .data.led.value = 0xFF,\n};\n\nconst NotificationMessage message_blue_255 = {\n    .type = NotificationMessageTypeLedBlue,\n    .data.led.value = 0xFF,\n};\n\n// Led OFF\nconst NotificationMessage message_red_0 = {\n    .type = NotificationMessageTypeLedRed,\n    .data.led.value = 0x00,\n};\n\nconst NotificationMessage message_green_0 = {\n    .type = NotificationMessageTypeLedGreen,\n    .data.led.value = 0x00,\n};\n\nconst NotificationMessage message_blue_0 = {\n    .type = NotificationMessageTypeLedBlue,\n    .data.led.value = 0x00,\n};\n\nconst NotificationMessage message_blink_start_10 = {\n    .type = NotificationMessageTypeLedBlinkStart,\n    .data.led_blink.color = 0,\n    .data.led_blink.on_time = 10,\n    .data.led_blink.period = 100,\n};\n\nconst NotificationMessage message_blink_start_100 = {\n    .type = NotificationMessageTypeLedBlinkStart,\n    .data.led_blink.color = 0,\n    .data.led_blink.on_time = 100,\n    .data.led_blink.period = 1000,\n};\n\nconst NotificationMessage message_blink_stop = {\n    .type = NotificationMessageTypeLedBlinkStop,\n};\n\nconst NotificationMessage message_blink_set_color_red = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightRed,\n};\n\nconst NotificationMessage message_blink_set_color_green = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightGreen,\n};\n\nconst NotificationMessage message_blink_set_color_blue = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightBlue,\n};\n\nconst NotificationMessage message_blink_set_color_cyan = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightBlue | LightGreen,\n};\n\nconst NotificationMessage message_blink_set_color_magenta = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightBlue | LightRed,\n};\n\nconst NotificationMessage message_blink_set_color_yellow = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightGreen | LightRed,\n};\n\nconst NotificationMessage message_blink_set_color_white = {\n    .type = NotificationMessageTypeLedBlinkColor,\n    .data.led_blink.color = LightRed | LightGreen | LightBlue,\n};\n\n// Delay\nconst NotificationMessage message_delay_1 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 1,\n};\n\nconst NotificationMessage message_delay_10 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 10,\n};\n\nconst NotificationMessage message_delay_25 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 25,\n};\n\nconst NotificationMessage message_delay_50 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 50,\n};\n\nconst NotificationMessage message_delay_100 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 100,\n};\n\nconst NotificationMessage message_delay_250 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 250,\n};\n\nconst NotificationMessage message_delay_500 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 500,\n};\n\nconst NotificationMessage message_delay_1000 = {\n    .type = NotificationMessageTypeDelay,\n    .data.delay.length = 1000,\n};\n\n// Sound\nconst NotificationMessage message_sound_off = {\n    .type = NotificationMessageTypeSoundOff,\n};\n\n// Vibro\nconst NotificationMessage message_vibro_on = {\n    .type = NotificationMessageTypeVibro,\n    .data.vibro.on = true,\n};\n\nconst NotificationMessage message_vibro_off = {\n    .type = NotificationMessageTypeVibro,\n    .data.vibro.on = false,\n};\n\n// Reset\nconst NotificationMessage message_do_not_reset = {\n    .type = NotificationMessageTypeDoNotReset,\n};\n\n// Override user settings\nconst NotificationMessage message_force_speaker_volume_setting_1f = {\n    .type = NotificationMessageTypeForceSpeakerVolumeSetting,\n    .data.forced_settings.speaker_volume = 1.0f,\n};\n\nconst NotificationMessage message_force_vibro_setting_on = {\n    .type = NotificationMessageTypeForceVibroSetting,\n    .data.forced_settings.vibro = true,\n};\n\nconst NotificationMessage message_force_vibro_setting_off = {\n    .type = NotificationMessageTypeForceVibroSetting,\n    .data.forced_settings.vibro = false,\n};\n\nconst NotificationMessage message_force_display_brightness_setting_1f = {\n    .type = NotificationMessageTypeForceDisplayBrightnessSetting,\n    .data.forced_settings.display_brightness = 1.0f,\n};\n\nconst NotificationMessage message_lcd_contrast_update = {\n    .type = NotificationMessageTypeLcdContrastUpdate,\n};\n\n/****************************** Message sequences ******************************/\n\n// Reset\nconst NotificationSequence sequence_reset_red = {\n    &message_red_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_green = {\n    &message_green_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_blue = {\n    &message_blue_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_rgb = {\n    &message_red_0,\n    &message_blue_0,\n    &message_green_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_display = {\n    &message_display_backlight_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_sound = {\n    &message_sound_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_reset_vibro = {\n    &message_vibro_off,\n    NULL,\n};\n\n// Vibro\nconst NotificationSequence sequence_set_vibro_on = {\n    &message_vibro_on,\n    &message_do_not_reset,\n    NULL,\n};\n\n// Display\nconst NotificationSequence sequence_display_backlight_on = {\n    &message_display_backlight_on,\n    NULL,\n};\n\nconst NotificationSequence sequence_display_backlight_off = {\n    &message_display_backlight_off,\n    NULL,\n};\n\n/** Display: backlight always on lock */\nconst NotificationSequence sequence_display_backlight_enforce_on = {\n    &message_display_backlight_enforce_on,\n    NULL,\n};\n\n/** Display: backlight always on unlock */\nconst NotificationSequence sequence_display_backlight_enforce_auto = {\n    &message_display_backlight_enforce_auto,\n    NULL,\n};\n\nconst NotificationSequence sequence_display_backlight_off_delay_1000 = {\n    &message_delay_1000,\n    &message_display_backlight_off,\n    NULL,\n};\n\n// Charging\nconst NotificationSequence sequence_charging = {\n    &message_red_255,\n    &message_green_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_charged = {\n    &message_green_255,\n    &message_red_0,\n    NULL,\n};\n\nconst NotificationSequence sequence_not_charging = {\n    &message_red_0,\n    &message_green_0,\n    NULL,\n};\n\n// Light up\nconst NotificationSequence sequence_set_only_red_255 = {\n    &message_red_255,\n    &message_green_0,\n    &message_blue_0,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_set_only_green_255 = {\n    &message_red_0,\n    &message_green_255,\n    &message_blue_0,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_set_only_blue_255 = {\n    &message_red_0,\n    &message_green_0,\n    &message_blue_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_set_red_255 = {\n    &message_red_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_set_green_255 = {\n    &message_green_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_set_blue_255 = {\n    &message_blue_255,\n    &message_do_not_reset,\n    NULL,\n};\n\n// Solid colors\nconst NotificationSequence sequence_solid_yellow = {\n    &message_red_255,\n    &message_green_255,\n    &message_blue_0,\n    &message_do_not_reset,\n    NULL,\n};\n\n// Blink\nconst NotificationSequence sequence_blink_blue_10 = {\n    &message_blue_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_red_10 = {\n    &message_red_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_green_10 = {\n    &message_green_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_yellow_10 = {\n    &message_red_255,\n    &message_green_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_cyan_10 = {\n    &message_green_255,\n    &message_blue_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_magenta_10 = {\n    &message_red_255,\n    &message_blue_255,\n    &message_delay_10,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_red_100 = {\n    &message_red_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_green_100 = {\n    &message_green_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_blue_100 = {\n    &message_blue_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_yellow_100 = {\n    &message_red_255,\n    &message_green_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_cyan_100 = {\n    &message_green_255,\n    &message_blue_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_magenta_100 = {\n    &message_red_255,\n    &message_blue_255,\n    &message_delay_100,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_white_100 = {\n    &message_red_255,\n    &message_green_255,\n    &message_blue_255,\n    &message_delay_100,\n    NULL,\n};\n\n// Hardware blink\nconst NotificationSequence sequence_blink_start_blue = {\n    &message_blink_start_10,\n    &message_blink_set_color_blue,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_start_red = {\n    &message_blink_start_10,\n    &message_blink_set_color_red,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_start_green = {\n    &message_blink_start_10,\n    &message_blink_set_color_green,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_start_yellow = {\n    &message_blink_start_10,\n    &message_blink_set_color_yellow,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_start_cyan = {\n    &message_blink_start_10,\n    &message_blink_set_color_cyan,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_start_magenta = {\n    &message_blink_start_10,\n    &message_blink_set_color_magenta,\n    &message_do_not_reset,\n    NULL,\n};\n\nconst NotificationSequence sequence_blink_stop = {\n    &message_blink_stop,\n    NULL,\n};\n\n//General\nconst NotificationSequence sequence_single_vibro = {\n    &message_vibro_on,\n    &message_delay_100,\n    &message_vibro_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_double_vibro = {\n    &message_vibro_on,\n    &message_delay_100,\n    &message_vibro_off,\n    &message_delay_100,\n    &message_vibro_on,\n    &message_delay_100,\n    &message_vibro_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_success = {\n    &message_display_backlight_on,\n    &message_green_255,\n    &message_vibro_on,\n    &message_note_c5,\n    &message_delay_50,\n    &message_vibro_off,\n    &message_note_e5,\n    &message_delay_50,\n    &message_note_g5,\n    &message_delay_50,\n    &message_note_c6,\n    &message_delay_50,\n    &message_sound_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_semi_success = {\n    &message_display_backlight_on,\n    &message_green_255,\n    &message_vibro_on,\n    &message_note_c4,\n    &message_delay_50,\n    &message_note_e4,\n    &message_delay_50,\n    &message_note_g4,\n    &message_delay_50,\n    &message_sound_off,\n    &message_delay_50,\n    &message_note_c5,\n    &message_delay_50,\n    &message_sound_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_error = {\n    &message_display_backlight_on,\n    &message_red_255,\n    &message_vibro_on,\n    &message_note_c5,\n    &message_delay_100,\n    &message_vibro_off,\n    &message_sound_off,\n    &message_delay_100,\n    &message_vibro_on,\n    &message_note_c5,\n    &message_delay_100,\n    &message_vibro_off,\n    &message_sound_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_audiovisual_alert = {\n    &message_force_speaker_volume_setting_1f,\n    &message_force_vibro_setting_on,\n    &message_force_display_brightness_setting_1f,\n    &message_vibro_on,\n\n    &message_display_backlight_on,\n    &message_note_c7,\n    &message_delay_250,\n\n    &message_display_backlight_off,\n    &message_note_c4,\n    &message_delay_250,\n\n    &message_display_backlight_on,\n    &message_note_c7,\n    &message_delay_250,\n\n    &message_display_backlight_off,\n    &message_note_c4,\n    &message_delay_250,\n\n    &message_display_backlight_on,\n    &message_note_c7,\n    &message_delay_250,\n\n    &message_display_backlight_off,\n    &message_note_c4,\n    &message_delay_250,\n\n    &message_sound_off,\n    &message_vibro_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_lcd_contrast_update = {\n    &message_lcd_contrast_update,\n    NULL,\n};\n\nconst NotificationSequence sequence_empty = {\n    NULL,\n};\n"
  },
  {
    "path": "applications/services/notification/notification_messages.h",
    "content": "#pragma once\n#include \"notification.h\"\n#include \"notification_messages_notes.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*********************************** Messages **********************************/\n\n// Display\nextern const NotificationMessage message_display_backlight_on;\nextern const NotificationMessage message_display_backlight_off;\nextern const NotificationMessage message_display_backlight_enforce_on;\nextern const NotificationMessage message_display_backlight_enforce_auto;\n\n// Led ON\nextern const NotificationMessage message_red_255;\nextern const NotificationMessage message_green_255;\nextern const NotificationMessage message_blue_255;\n\n// Led OFF\nextern const NotificationMessage message_red_0;\nextern const NotificationMessage message_green_0;\nextern const NotificationMessage message_blue_0;\n\n// Led hardware blink control\nextern const NotificationMessage message_blink_start_10;\nextern const NotificationMessage message_blink_start_100;\nextern const NotificationMessage message_blink_stop;\n\nextern const NotificationMessage message_blink_set_color_red;\nextern const NotificationMessage message_blink_set_color_green;\nextern const NotificationMessage message_blink_set_color_blue;\nextern const NotificationMessage message_blink_set_color_cyan;\nextern const NotificationMessage message_blink_set_color_magenta;\nextern const NotificationMessage message_blink_set_color_yellow;\nextern const NotificationMessage message_blink_set_color_white;\n\n// Delay\nextern const NotificationMessage message_delay_1;\nextern const NotificationMessage message_delay_10;\nextern const NotificationMessage message_delay_25;\nextern const NotificationMessage message_delay_50;\nextern const NotificationMessage message_delay_100;\nextern const NotificationMessage message_delay_250;\nextern const NotificationMessage message_delay_500;\nextern const NotificationMessage message_delay_1000;\n\n// Sound\nextern const NotificationMessage message_sound_off;\n\n// Vibro\nextern const NotificationMessage message_vibro_on;\nextern const NotificationMessage message_vibro_off;\n\n// Reset\nextern const NotificationMessage message_do_not_reset;\n\n// Override user settings\nextern const NotificationMessage message_force_speaker_volume_setting_1f;\nextern const NotificationMessage message_force_vibro_setting_on;\nextern const NotificationMessage message_force_vibro_setting_off;\nextern const NotificationMessage message_force_display_brightness_setting_1f;\n\n// LCD Messages\nextern const NotificationMessage message_lcd_contrast_update;\n\n/****************************** Message sequences ******************************/\n\n// Reset\nextern const NotificationSequence sequence_reset_red;\nextern const NotificationSequence sequence_reset_green;\nextern const NotificationSequence sequence_reset_blue;\nextern const NotificationSequence sequence_reset_rgb;\nextern const NotificationSequence sequence_reset_display;\nextern const NotificationSequence sequence_reset_sound;\nextern const NotificationSequence sequence_reset_vibro;\n\n// Vibro\nextern const NotificationSequence sequence_set_vibro_on;\n\n// Display\n/** Display: backlight wakeup */\nextern const NotificationSequence sequence_display_backlight_on;\n/** Display: backlight force off */\nextern const NotificationSequence sequence_display_backlight_off;\n/** Display: backlight force off after a delay of 1000ms */\nextern const NotificationSequence sequence_display_backlight_off_delay_1000;\n\n/** Display: backlight always on lock */\nextern const NotificationSequence sequence_display_backlight_enforce_on;\n/** Display: backlight always on unlock */\nextern const NotificationSequence sequence_display_backlight_enforce_auto;\n\n// Charging\nextern const NotificationSequence sequence_charging;\nextern const NotificationSequence sequence_charged;\nextern const NotificationSequence sequence_not_charging;\n\n// Light up\nextern const NotificationSequence sequence_set_only_red_255;\nextern const NotificationSequence sequence_set_only_green_255;\nextern const NotificationSequence sequence_set_only_blue_255;\nextern const NotificationSequence sequence_set_red_255;\nextern const NotificationSequence sequence_set_green_255;\nextern const NotificationSequence sequence_set_blue_255;\n\n// Solid colors\nextern const NotificationSequence sequence_solid_yellow;\n\n// Blink\nextern const NotificationSequence sequence_blink_blue_10;\nextern const NotificationSequence sequence_blink_red_10;\nextern const NotificationSequence sequence_blink_green_10;\nextern const NotificationSequence sequence_blink_yellow_10;\nextern const NotificationSequence sequence_blink_cyan_10;\nextern const NotificationSequence sequence_blink_magenta_10;\n\nextern const NotificationSequence sequence_blink_red_100;\nextern const NotificationSequence sequence_blink_green_100;\nextern const NotificationSequence sequence_blink_blue_100;\nextern const NotificationSequence sequence_blink_yellow_100;\nextern const NotificationSequence sequence_blink_cyan_100;\nextern const NotificationSequence sequence_blink_magenta_100;\nextern const NotificationSequence sequence_blink_white_100;\n\n// Hardware blink\nextern const NotificationSequence sequence_blink_start_blue;\nextern const NotificationSequence sequence_blink_start_red;\nextern const NotificationSequence sequence_blink_start_green;\nextern const NotificationSequence sequence_blink_start_yellow;\nextern const NotificationSequence sequence_blink_start_cyan;\nextern const NotificationSequence sequence_blink_start_magenta;\nextern const NotificationSequence sequence_blink_stop;\n\n// General\nextern const NotificationSequence sequence_single_vibro;\nextern const NotificationSequence sequence_double_vibro;\nextern const NotificationSequence sequence_success;\nextern const NotificationSequence sequence_semi_success;\nextern const NotificationSequence sequence_error;\nextern const NotificationSequence sequence_audiovisual_alert;\n\n// LCD\nextern const NotificationSequence sequence_lcd_contrast_update;\n\n// Wait for notification queue become empty\nextern const NotificationSequence sequence_empty;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/notification/notification_messages_notes.c",
    "content": "#include \"notification.h\"\n#include <toolbox/strint.h>\n\n/*\nPython script for note messages generation\n\n# coding: utf-8\n# Python script for note messages generation\nfrom typing import List\n\nnote_names: List = ['c', 'cs', 'd', 'ds', 'e', 'f', 'fs', 'g', 'gs', 'a', 'as', 'b']\nbase_note: float = 16.3515979\ncf: float = 2 ** (1.0 / 12)\n\nnote: float = base_note\nfor octave in range(9):\n    for name in note_names:\n        print(f\"const NotificationMessage message_note_{name}{octave}\" + \" = {\\n\"\n              \"\\t.type = NotificationMessageTypeSoundOn,\\n\"\n              f\"\\t.data.sound.frequency = {round(note, 2)}f,\\n\"\n              \"\\t.data.sound.volume = 1.0f,\\n\"\n              \"};\")\n        note = note * cf\n\nfor octave in range(9):\n    for name in note_names:\n        print(f\"extern const NotificationMessage message_note_{name}{octave};\")\n*/\n\nconst NotificationMessage message_click = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 16.35f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 17.32f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 18.35f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 19.45f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 20.6f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 21.83f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 23.12f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 24.5f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 25.96f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 27.5f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 29.14f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b0 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 30.87f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 32.7f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 34.65f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 36.71f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 38.89f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 41.2f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 43.65f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 46.25f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 49.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 51.91f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 55.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 58.27f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b1 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 61.74f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 65.41f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 69.3f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 73.42f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 77.78f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 82.41f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 87.31f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 92.5f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 98.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 103.83f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 110.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 116.54f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b2 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 123.47f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 130.81f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 138.59f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 146.83f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 155.56f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 164.81f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 174.61f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 185.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 196.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 207.65f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 220.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 233.08f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b3 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 246.94f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 261.63f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 277.18f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 293.66f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 311.13f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 329.63f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 349.23f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 369.99f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 392.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 415.3f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 440.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 466.16f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b4 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 493.88f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 523.25f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 554.37f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 587.33f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 622.25f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 659.26f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 698.46f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 739.99f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 783.99f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 830.61f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 880.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 932.33f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b5 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 987.77f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1046.5f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1108.73f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1174.66f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1244.51f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1318.51f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1396.91f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1479.98f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1567.98f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1661.22f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1760.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1864.66f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b6 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 1975.53f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2093.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2217.46f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2349.32f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2489.02f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2637.02f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2793.83f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 2959.96f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 3135.96f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 3322.44f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 3520.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 3729.31f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b7 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 3951.07f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_c8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 4186.01f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_cs8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 4434.92f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_d8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 4698.64f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_ds8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 4978.03f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_e8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 5274.04f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_f8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 5587.65f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_fs8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 5919.91f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_g8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 6271.93f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_gs8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 6644.88f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_a8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 7040.0f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_as8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 7458.62f,\n    .data.sound.volume = 1.0f,\n};\nconst NotificationMessage message_note_b8 = {\n    .type = NotificationMessageTypeSoundOn,\n    .data.sound.frequency = 7902.13f,\n    .data.sound.volume = 1.0f,\n};\n\nfloat notification_messages_notes_frequency_from_name(const char* note_name) {\n    const float base_note = 16.3515979f; // C0\n\n    const char* note_names[] = {\"c\", \"cs\", \"d\", \"ds\", \"e\", \"f\", \"fs\", \"g\", \"gs\", \"a\", \"as\", \"b\"};\n    const size_t notes_count = COUNT_OF(note_names);\n\n    char note_wo_octave[3] = {0};\n    for(size_t i = 0; i < sizeof(note_wo_octave) - 1; i++) {\n        char in = *note_name;\n        if(!in) break;\n        if(!isalpha(in)) break;\n        note_wo_octave[i] = in;\n        note_name++;\n    }\n\n    int note_index = -1;\n    for(size_t i = 0; i < notes_count; i++) {\n        if(strcasecmp(note_wo_octave, note_names[i]) == 0) note_index = i;\n    }\n    if(note_index < 0) return 0.0;\n\n    uint16_t octave;\n    StrintParseError error = strint_to_uint16(note_name, NULL, &octave, 10);\n    if(error != StrintParseNoError) return 0.0;\n    if(octave > 8) return 0.0;\n\n    int semitone_index = octave * notes_count + note_index;\n    float frequency = base_note * powf(2.0f, semitone_index / 12.0f);\n\n    return roundf(frequency * 100) / 100.0f;\n}\n"
  },
  {
    "path": "applications/services/notification/notification_messages_notes.h",
    "content": "#pragma once\n#include \"notification.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NotificationMessage message_click;\nextern const NotificationMessage message_note_c0;\nextern const NotificationMessage message_note_cs0;\nextern const NotificationMessage message_note_d0;\nextern const NotificationMessage message_note_ds0;\nextern const NotificationMessage message_note_e0;\nextern const NotificationMessage message_note_f0;\nextern const NotificationMessage message_note_fs0;\nextern const NotificationMessage message_note_g0;\nextern const NotificationMessage message_note_gs0;\nextern const NotificationMessage message_note_a0;\nextern const NotificationMessage message_note_as0;\nextern const NotificationMessage message_note_b0;\nextern const NotificationMessage message_note_c1;\nextern const NotificationMessage message_note_cs1;\nextern const NotificationMessage message_note_d1;\nextern const NotificationMessage message_note_ds1;\nextern const NotificationMessage message_note_e1;\nextern const NotificationMessage message_note_f1;\nextern const NotificationMessage message_note_fs1;\nextern const NotificationMessage message_note_g1;\nextern const NotificationMessage message_note_gs1;\nextern const NotificationMessage message_note_a1;\nextern const NotificationMessage message_note_as1;\nextern const NotificationMessage message_note_b1;\nextern const NotificationMessage message_note_c2;\nextern const NotificationMessage message_note_cs2;\nextern const NotificationMessage message_note_d2;\nextern const NotificationMessage message_note_ds2;\nextern const NotificationMessage message_note_e2;\nextern const NotificationMessage message_note_f2;\nextern const NotificationMessage message_note_fs2;\nextern const NotificationMessage message_note_g2;\nextern const NotificationMessage message_note_gs2;\nextern const NotificationMessage message_note_a2;\nextern const NotificationMessage message_note_as2;\nextern const NotificationMessage message_note_b2;\nextern const NotificationMessage message_note_c3;\nextern const NotificationMessage message_note_cs3;\nextern const NotificationMessage message_note_d3;\nextern const NotificationMessage message_note_ds3;\nextern const NotificationMessage message_note_e3;\nextern const NotificationMessage message_note_f3;\nextern const NotificationMessage message_note_fs3;\nextern const NotificationMessage message_note_g3;\nextern const NotificationMessage message_note_gs3;\nextern const NotificationMessage message_note_a3;\nextern const NotificationMessage message_note_as3;\nextern const NotificationMessage message_note_b3;\nextern const NotificationMessage message_note_c4;\nextern const NotificationMessage message_note_cs4;\nextern const NotificationMessage message_note_d4;\nextern const NotificationMessage message_note_ds4;\nextern const NotificationMessage message_note_e4;\nextern const NotificationMessage message_note_f4;\nextern const NotificationMessage message_note_fs4;\nextern const NotificationMessage message_note_g4;\nextern const NotificationMessage message_note_gs4;\nextern const NotificationMessage message_note_a4;\nextern const NotificationMessage message_note_as4;\nextern const NotificationMessage message_note_b4;\nextern const NotificationMessage message_note_c5;\nextern const NotificationMessage message_note_cs5;\nextern const NotificationMessage message_note_d5;\nextern const NotificationMessage message_note_ds5;\nextern const NotificationMessage message_note_e5;\nextern const NotificationMessage message_note_f5;\nextern const NotificationMessage message_note_fs5;\nextern const NotificationMessage message_note_g5;\nextern const NotificationMessage message_note_gs5;\nextern const NotificationMessage message_note_a5;\nextern const NotificationMessage message_note_as5;\nextern const NotificationMessage message_note_b5;\nextern const NotificationMessage message_note_c6;\nextern const NotificationMessage message_note_cs6;\nextern const NotificationMessage message_note_d6;\nextern const NotificationMessage message_note_ds6;\nextern const NotificationMessage message_note_e6;\nextern const NotificationMessage message_note_f6;\nextern const NotificationMessage message_note_fs6;\nextern const NotificationMessage message_note_g6;\nextern const NotificationMessage message_note_gs6;\nextern const NotificationMessage message_note_a6;\nextern const NotificationMessage message_note_as6;\nextern const NotificationMessage message_note_b6;\nextern const NotificationMessage message_note_c7;\nextern const NotificationMessage message_note_cs7;\nextern const NotificationMessage message_note_d7;\nextern const NotificationMessage message_note_ds7;\nextern const NotificationMessage message_note_e7;\nextern const NotificationMessage message_note_f7;\nextern const NotificationMessage message_note_fs7;\nextern const NotificationMessage message_note_g7;\nextern const NotificationMessage message_note_gs7;\nextern const NotificationMessage message_note_a7;\nextern const NotificationMessage message_note_as7;\nextern const NotificationMessage message_note_b7;\nextern const NotificationMessage message_note_c8;\nextern const NotificationMessage message_note_cs8;\nextern const NotificationMessage message_note_d8;\nextern const NotificationMessage message_note_ds8;\nextern const NotificationMessage message_note_e8;\nextern const NotificationMessage message_note_f8;\nextern const NotificationMessage message_note_fs8;\nextern const NotificationMessage message_note_g8;\nextern const NotificationMessage message_note_gs8;\nextern const NotificationMessage message_note_a8;\nextern const NotificationMessage message_note_as8;\nextern const NotificationMessage message_note_b8;\n\n/**\n * @brief Returns the frequency of the given note\n *\n * This function calculates and returns the frequency (in Hz) of the specified note.\n * If the input note name is invalid, the function returns 0.0.\n *\n * @param [in] note_name The name of the note (e.g., \"A4\", cs5\")\n * @return The frequency of the note in Hz, or 0.0 if the note name is invalid\n */\nextern float notification_messages_notes_frequency_from_name(const char* note_name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/notification/notification_settings_filename.h",
    "content": "#pragma once\n\n#define NOTIFICATION_SETTINGS_FILE_NAME \".notification.settings\"\n"
  },
  {
    "path": "applications/services/power/application.fam",
    "content": "App(\n    appid=\"power\",\n    name=\"PowerSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"power_srv\",\n    cdefines=[\"SRV_POWER\"],\n    requires=[\n        \"gui\",\n        \"cli\",\n    ],\n    provides=[\n        \"power_settings\",\n        \"power_start\",\n    ],\n    stack_size=1 * 1024,\n    order=110,\n    sdk_headers=[\"power_service/power.h\"],\n)\n\nApp(\n    appid=\"power_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"power_on_system_start\",\n    requires=[\"power\"],\n    order=50,\n)\n"
  },
  {
    "path": "applications/services/power/power_cli.c",
    "content": "#include \"power_cli.h\"\n\n#include <furi_hal.h>\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <lib/toolbox/args.h>\n#include <power/power_service/power.h>\n#include <toolbox/pipe.h>\n\nvoid power_cli_off(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Power* power = furi_record_open(RECORD_POWER);\n    printf(\"It's now safe to disconnect USB from your flipper\\r\\n\");\n    furi_delay_ms(666);\n    power_off(power);\n}\n\nvoid power_cli_reboot(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_reboot(power, PowerBootModeNormal);\n}\n\nvoid power_cli_reboot2dfu(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_reboot(power, PowerBootModeDfu);\n}\n\nvoid power_cli_5v(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    Power* power = furi_record_open(RECORD_POWER);\n    if(!furi_string_cmp(args, \"0\")) {\n        power_enable_otg(power, false);\n    } else if(!furi_string_cmp(args, \"1\")) {\n        power_enable_otg(power, true);\n    } else {\n        cli_print_usage(\"power_otg\", \"<1|0>\", furi_string_get_cstr(args));\n    }\n\n    furi_record_close(RECORD_POWER);\n}\n\nvoid power_cli_3v3(PipeSide* pipe, FuriString* args) {\n    UNUSED(pipe);\n    if(!furi_string_cmp(args, \"0\")) {\n        furi_hal_power_disable_external_3_3v();\n    } else if(!furi_string_cmp(args, \"1\")) {\n        furi_hal_power_enable_external_3_3v();\n    } else {\n        cli_print_usage(\"power_ext\", \"<1|0>\", furi_string_get_cstr(args));\n    }\n}\n\nstatic void power_cli_command_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"power <cmd> <args>\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n\n    printf(\"\\toff\\t - shutdown power\\r\\n\");\n    printf(\"\\treboot\\t - reboot\\r\\n\");\n    printf(\"\\treboot2dfu\\t - reboot to dfu bootloader\\r\\n\");\n    printf(\"\\t5v <0 or 1>\\t - enable or disable 5v ext\\r\\n\");\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        printf(\"\\t3v3 <0 or 1>\\t - enable or disable 3v3 ext\\r\\n\");\n    }\n}\n\nvoid power_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    cmd = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            power_cli_command_print_usage();\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"off\") == 0) {\n            power_cli_off(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"reboot\") == 0) {\n            power_cli_reboot(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"reboot2dfu\") == 0) {\n            power_cli_reboot2dfu(pipe, args);\n            break;\n        }\n\n        if(furi_string_cmp_str(cmd, \"5v\") == 0) {\n            power_cli_5v(pipe, args);\n            break;\n        }\n\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n            if(furi_string_cmp_str(cmd, \"3v3\") == 0) {\n                power_cli_3v3(pipe, args);\n                break;\n            }\n        }\n\n        power_cli_command_print_usage();\n    } while(false);\n\n    furi_string_free(cmd);\n}\n\nvoid power_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(registry, \"power\", CliCommandFlagParallelSafe, power_cli, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(power_cli);\n#endif\n}\n"
  },
  {
    "path": "applications/services/power/power_cli.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid power_on_system_start(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/power/power_service/power.c",
    "content": "#include \"power_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <update_util/update_operation.h>\n#include <notification/notification_messages.h>\n\n#define TAG \"Power\"\n\n#define POWER_OFF_TIMEOUT_S  (90U)\n#define POWER_POLL_PERIOD_MS (1000UL)\n\n#define POWER_VBUS_LOW_THRESHOLD   (4.0f)\n#define POWER_HEALTH_LOW_THRESHOLD (70U)\n\nstatic void power_draw_battery_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    Power* power = context;\n    canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8);\n\n    if(power->info.gauge_is_ok) {\n        canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4);\n\n        if(power->info.voltage_battery_charge_limit < 4.2f) {\n            // Battery charge voltage limit is modified, indicate with cross pattern\n            canvas_invert_color(canvas);\n            uint8_t battery_bar_width = (power->info.charge + 4) / 5;\n            bool cross_odd = false;\n            // Start 1 further in from the battery bar's x position\n            for(uint8_t x = 3; x <= battery_bar_width; x++) {\n                // Cross pattern is from the center of the battery bar\n                // y = 2 + 1 (inset) + 1 (for every other)\n                canvas_draw_dot(canvas, x, 3 + (uint8_t)cross_odd);\n                cross_odd = !cross_odd;\n            }\n            canvas_invert_color(canvas);\n        }\n\n        if(power->state == PowerStateCharging) {\n            canvas_set_bitmap_mode(canvas, 1);\n            canvas_set_color(canvas, ColorWhite);\n            // -1 used here to overflow u8 number and render is outside of the area\n            canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10);\n            canvas_set_color(canvas, ColorBlack);\n            canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10);\n            canvas_set_bitmap_mode(canvas, 0);\n        }\n\n    } else {\n        canvas_draw_box(canvas, 8, 3, 8, 2);\n    }\n}\n\nstatic ViewPort* power_battery_view_port_alloc(Power* power) {\n    ViewPort* battery_view_port = view_port_alloc();\n    view_port_set_width(battery_view_port, icon_get_width(&I_Battery_26x8));\n    view_port_draw_callback_set(battery_view_port, power_draw_battery_callback, power);\n    return battery_view_port;\n}\n\nstatic bool power_update_info(Power* power) {\n    const PowerInfo info = {\n        .is_charging = furi_hal_power_is_charging(),\n        .gauge_is_ok = furi_hal_power_gauge_is_ok(),\n        .is_shutdown_requested = furi_hal_power_is_shutdown_requested(),\n        .is_otg_enabled = furi_hal_power_is_otg_enabled(),\n        .charge = furi_hal_power_get_pct(),\n        .health = furi_hal_power_get_bat_health_pct(),\n        .capacity_remaining = furi_hal_power_get_battery_remaining_capacity(),\n        .capacity_full = furi_hal_power_get_battery_full_capacity(),\n        .current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger),\n        .current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge),\n        .voltage_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(),\n        .voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger),\n        .voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge),\n        .voltage_vbus = furi_hal_power_get_usb_voltage(),\n        .temperature_charger = furi_hal_power_get_battery_temperature(FuriHalPowerICCharger),\n        .temperature_gauge = furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge),\n    };\n\n    const bool need_refresh = (power->info.charge != info.charge) ||\n                              (power->info.is_charging != info.is_charging);\n    power->info = info;\n    return need_refresh;\n}\n\nstatic void power_check_charging_state(Power* power) {\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n\n    if(furi_hal_power_is_charging()) {\n        if((power->info.charge == 100) || (furi_hal_power_is_charging_done())) {\n            if(power->state != PowerStateCharged) {\n                notification_internal_message(notification, &sequence_charged);\n                power->state = PowerStateCharged;\n                power->event.type = PowerEventTypeFullyCharged;\n                furi_pubsub_publish(power->event_pubsub, &power->event);\n            }\n\n        } else if(power->state != PowerStateCharging) {\n            notification_internal_message(notification, &sequence_charging);\n            power->state = PowerStateCharging;\n            power->event.type = PowerEventTypeStartCharging;\n            furi_pubsub_publish(power->event_pubsub, &power->event);\n        }\n\n    } else if(power->state != PowerStateNotCharging) {\n        notification_internal_message(notification, &sequence_not_charging);\n        power->state = PowerStateNotCharging;\n        power->event.type = PowerEventTypeStopCharging;\n        furi_pubsub_publish(power->event_pubsub, &power->event);\n    }\n\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nstatic void power_check_low_battery(Power* power) {\n    if(!power->info.gauge_is_ok) {\n        return;\n    }\n\n    // Check battery charge and vbus voltage\n    if((power->info.is_shutdown_requested) &&\n       (power->info.voltage_vbus < POWER_VBUS_LOW_THRESHOLD) && power->show_battery_low_warning) {\n        if(!power->battery_low) {\n            view_holder_send_to_front(power->view_holder);\n            view_holder_set_view(power->view_holder, power_off_get_view(power->view_power_off));\n        }\n        power->battery_low = true;\n    } else {\n        if(power->battery_low) {\n            // view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE);\n            view_holder_set_view(power->view_holder, NULL);\n            power->power_off_timeout = POWER_OFF_TIMEOUT_S;\n        }\n        power->battery_low = false;\n    }\n    // If battery low, update view and switch off power after timeout\n    if(power->battery_low) {\n        PowerOffResponse response = power_off_get_response(power->view_power_off);\n        if(response == PowerOffResponseDefault) {\n            if(power->power_off_timeout) {\n                power_off_set_time_left(power->view_power_off, power->power_off_timeout--);\n            } else {\n                power_off(power);\n            }\n        } else if(response == PowerOffResponseOk) {\n            power_off(power);\n        } else if(response == PowerOffResponseHide) {\n            view_holder_set_view(power->view_holder, NULL);\n            if(power->power_off_timeout) {\n                power_off_set_time_left(power->view_power_off, power->power_off_timeout--);\n            } else {\n                power_off(power);\n            }\n        } else if(response == PowerOffResponseCancel) {\n            view_holder_set_view(power->view_holder, NULL);\n        }\n    }\n}\n\nstatic void power_check_battery_level_change(Power* power) {\n    if(power->battery_level != power->info.charge) {\n        power->battery_level = power->info.charge;\n        power->event.type = PowerEventTypeBatteryLevelChanged;\n        power->event.data.battery_level = power->battery_level;\n        furi_pubsub_publish(power->event_pubsub, &power->event);\n    }\n}\n\nstatic void power_handle_shutdown(Power* power) {\n    furi_hal_power_off();\n    // Notify user if USB is plugged\n    view_holder_send_to_front(power->view_holder);\n    view_holder_set_view(\n        power->view_holder, power_unplug_usb_get_view(power->view_power_unplug_usb));\n    furi_delay_ms(100);\n    furi_halt(\"Disconnect USB for safe shutdown\");\n}\n\nstatic void power_handle_reboot(PowerBootMode mode) {\n    if(mode == PowerBootModeNormal) {\n        update_operation_disarm();\n    } else if(mode == PowerBootModeDfu) {\n        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeDfu);\n    } else if(mode == PowerBootModeUpdateStart) {\n        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate);\n    } else {\n        furi_crash();\n    }\n\n    furi_hal_power_reset();\n}\n\nstatic void power_message_callback(FuriEventLoopObject* object, void* context) {\n    furi_assert(context);\n    Power* power = context;\n\n    furi_assert(object == power->message_queue);\n\n    PowerMessage msg;\n    furi_check(furi_message_queue_get(power->message_queue, &msg, 0) == FuriStatusOk);\n\n    switch(msg.type) {\n    case PowerMessageTypeShutdown:\n        power_handle_shutdown(power);\n        break;\n    case PowerMessageTypeReboot:\n        power_handle_reboot(msg.boot_mode);\n        break;\n    case PowerMessageTypeGetInfo:\n        *msg.power_info = power->info;\n        break;\n    case PowerMessageTypeIsBatteryHealthy:\n        *msg.bool_param = power->info.health > POWER_HEALTH_LOW_THRESHOLD;\n        break;\n    case PowerMessageTypeShowBatteryLowWarning:\n        power->show_battery_low_warning = *msg.bool_param;\n        break;\n    case PowerMessageTypeSwitchOTG:\n        power->is_otg_requested = *msg.bool_param;\n        if(power->is_otg_requested) {\n            // Only try to enable if VBUS voltage is low, otherwise charger will refuse\n            if(power->info.voltage_vbus < 4.5f) {\n                size_t retries = 5;\n                while(retries-- > 0) {\n                    if(furi_hal_power_enable_otg()) {\n                        break;\n                    }\n                }\n                if(!retries) {\n                    FURI_LOG_W(TAG, \"Failed to enable OTG, will try later\");\n                }\n            } else {\n                FURI_LOG_W(\n                    TAG,\n                    \"Postponing OTG enable: VBUS(%0.1f) >= 4.5v\",\n                    (double)power->info.voltage_vbus);\n            }\n        } else {\n            furi_hal_power_disable_otg();\n        }\n        break;\n    default:\n        furi_crash();\n    }\n\n    if(msg.lock) {\n        api_lock_unlock(msg.lock);\n    }\n}\n\nstatic void power_tick_callback(void* context) {\n    furi_assert(context);\n    Power* power = context;\n\n    // Update data from gauge and charger\n    const bool need_refresh = power_update_info(power);\n    // Check low battery level\n    power_check_low_battery(power);\n    // Check and notify about charging state\n    power_check_charging_state(power);\n    // Check and notify about battery level change\n    power_check_battery_level_change(power);\n    // Update battery view port\n    if(need_refresh) {\n        view_port_update(power->battery_view_port);\n    }\n    // Check OTG status, disable in case of a fault\n    if(furi_hal_power_check_otg_fault()) {\n        FURI_LOG_E(TAG, \"OTG fault detected, disabling OTG\");\n        furi_hal_power_disable_otg();\n        power->is_otg_requested = false;\n    }\n\n    // Change OTG state if needed (i.e. after disconnecting USB power)\n    if(power->is_otg_requested &&\n       (!power->info.is_otg_enabled && power->info.voltage_vbus < 4.5f)) {\n        FURI_LOG_D(TAG, \"OTG requested but not enabled, enabling OTG\");\n        furi_hal_power_enable_otg();\n    }\n}\n\nstatic Power* power_alloc(void) {\n    Power* power = malloc(sizeof(Power));\n    // Pubsub\n    power->event_pubsub = furi_pubsub_alloc();\n    // State initialization\n    power->power_off_timeout = POWER_OFF_TIMEOUT_S;\n    power->show_battery_low_warning = true;\n    // Gui\n    Gui* gui = furi_record_open(RECORD_GUI);\n\n    power->view_holder = view_holder_alloc();\n    power->view_power_off = power_off_alloc();\n    power->view_power_unplug_usb = power_unplug_usb_alloc();\n\n    view_holder_attach_to_gui(power->view_holder, gui);\n    // Battery view port\n    power->battery_view_port = power_battery_view_port_alloc(power);\n    gui_add_view_port(gui, power->battery_view_port, GuiLayerStatusBarRight);\n    // Event loop\n    power->event_loop = furi_event_loop_alloc();\n    power->message_queue = furi_message_queue_alloc(4, sizeof(PowerMessage));\n\n    furi_event_loop_subscribe_message_queue(\n        power->event_loop,\n        power->message_queue,\n        FuriEventLoopEventIn,\n        power_message_callback,\n        power);\n    furi_event_loop_tick_set(power->event_loop, 1000, power_tick_callback, power);\n\n    return power;\n}\n\nint32_t power_srv(void* p) {\n    UNUSED(p);\n\n    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {\n        FURI_LOG_W(TAG, \"Skipping start in special boot mode\");\n\n        furi_thread_suspend(furi_thread_get_current_id());\n        return 0;\n    }\n\n    Power* power = power_alloc();\n    power_update_info(power);\n\n    furi_record_create(RECORD_POWER, power);\n    furi_event_loop_run(power->event_loop);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/power/power_service/power.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include <core/pubsub.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RECORD_POWER \"power\"\n\ntypedef struct Power Power;\n\ntypedef enum {\n    PowerBootModeNormal,\n    PowerBootModeDfu,\n    PowerBootModeUpdateStart,\n} PowerBootMode;\n\ntypedef enum {\n    PowerEventTypeStopCharging,\n    PowerEventTypeStartCharging,\n    PowerEventTypeFullyCharged,\n    PowerEventTypeBatteryLevelChanged,\n} PowerEventType;\n\ntypedef union {\n    uint8_t battery_level;\n} PowerEventData;\n\ntypedef struct {\n    PowerEventType type;\n    PowerEventData data;\n} PowerEvent;\n\ntypedef struct {\n    bool gauge_is_ok;\n    bool is_charging;\n    bool is_shutdown_requested;\n    bool is_otg_enabled;\n\n    float current_charger;\n    float current_gauge;\n\n    float voltage_battery_charge_limit;\n    float voltage_charger;\n    float voltage_gauge;\n    float voltage_vbus;\n\n    uint32_t capacity_remaining;\n    uint32_t capacity_full;\n\n    float temperature_charger;\n    float temperature_gauge;\n\n    uint8_t charge;\n    uint8_t health;\n} PowerInfo;\n\n/** Power off device\n */\nvoid power_off(Power* power);\n\n/** Reboot device\n *\n * @param mode      PowerBootMode\n */\nvoid power_reboot(Power* power, PowerBootMode mode);\n\n/** Get power info\n *\n * @param power     Power instance\n * @param info      PowerInfo instance\n */\nvoid power_get_info(Power* power, PowerInfo* info);\n\n/** Get power event pubsub handler\n *\n * @param power     Power instance\n *\n * @return          FuriPubSub instance\n */\nFuriPubSub* power_get_pubsub(Power* power);\n\n/** Check battery health\n *\n * @return          true if battery is healthy\n */\nbool power_is_battery_healthy(Power* power);\n\n/** Enable or disable battery low level notification message\n *\n * @param power     Power instance\n * @param enable    true - enable, false - disable\n */\nvoid power_enable_low_battery_level_notification(Power* power, bool enable);\n\n/** Enable or disable OTG\n *\n * @param power     Power instance\n * @param enable    true - enable, false - disable\n */\nvoid power_enable_otg(Power* power, bool enable);\n\n/** Check OTG status\n * \n * @return          true if OTG  is requested\n */\nbool power_is_otg_enabled(Power* power);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/power/power_service/power_api.c",
    "content": "#include \"power_i.h\"\n\nvoid power_off(Power* power) {\n    furi_check(power);\n\n    PowerMessage msg = {\n        .type = PowerMessageTypeShutdown,\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid power_reboot(Power* power, PowerBootMode mode) {\n    PowerMessage msg = {\n        .type = PowerMessageTypeReboot,\n        .boot_mode = mode,\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid power_get_info(Power* power, PowerInfo* info) {\n    furi_check(power);\n    furi_check(info);\n\n    PowerMessage msg = {\n        .type = PowerMessageTypeGetInfo,\n        .power_info = info,\n        .lock = api_lock_alloc_locked(),\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n    api_lock_wait_unlock_and_free(msg.lock);\n}\n\nFuriPubSub* power_get_pubsub(Power* power) {\n    furi_check(power);\n    return power->event_pubsub;\n}\n\nbool power_is_battery_healthy(Power* power) {\n    furi_check(power);\n\n    bool ret = false;\n\n    PowerMessage msg = {\n        .type = PowerMessageTypeIsBatteryHealthy,\n        .lock = api_lock_alloc_locked(),\n        .bool_param = &ret,\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n    api_lock_wait_unlock_and_free(msg.lock);\n\n    return ret;\n}\n\nvoid power_enable_low_battery_level_notification(Power* power, bool enable) {\n    furi_check(power);\n\n    PowerMessage msg = {\n        .type = PowerMessageTypeShowBatteryLowWarning,\n        .bool_param = &enable,\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid power_enable_otg(Power* power, bool enable) {\n    furi_check(power);\n\n    PowerMessage msg = {\n        .type = PowerMessageTypeSwitchOTG,\n        .bool_param = &enable,\n        .lock = api_lock_alloc_locked(),\n    };\n\n    furi_check(\n        furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);\n    api_lock_wait_unlock_and_free(msg.lock);\n}\n\nbool power_is_otg_enabled(Power* power) {\n    furi_check(power);\n    return power->is_otg_requested;\n}\n"
  },
  {
    "path": "applications/services/power/power_service/power_i.h",
    "content": "#pragma once\n\n#include \"power.h\"\n\n#include <gui/gui.h>\n#include <gui/view_holder.h>\n\n#include <toolbox/api_lock.h>\n#include <assets_icons.h>\n\n#include \"views/power_off.h\"\n#include \"views/power_unplug_usb.h\"\n\ntypedef enum {\n    PowerStateNotCharging,\n    PowerStateCharging,\n    PowerStateCharged,\n} PowerState;\n\nstruct Power {\n    ViewHolder* view_holder;\n    FuriPubSub* event_pubsub;\n    FuriEventLoop* event_loop;\n    FuriMessageQueue* message_queue;\n\n    ViewPort* battery_view_port;\n    PowerOff* view_power_off;\n    PowerUnplugUsb* view_power_unplug_usb;\n\n    PowerEvent event;\n    PowerState state;\n    PowerInfo info;\n\n    bool battery_low;\n    bool show_battery_low_warning;\n    bool is_otg_requested;\n    uint8_t battery_level;\n    uint8_t power_off_timeout;\n};\n\ntypedef enum {\n    PowerViewOff,\n    PowerViewUnplugUsb,\n} PowerView;\n\ntypedef enum {\n    PowerMessageTypeShutdown,\n    PowerMessageTypeReboot,\n    PowerMessageTypeGetInfo,\n    PowerMessageTypeIsBatteryHealthy,\n    PowerMessageTypeShowBatteryLowWarning,\n    PowerMessageTypeSwitchOTG,\n} PowerMessageType;\n\ntypedef struct {\n    PowerMessageType type;\n    union {\n        PowerBootMode boot_mode;\n        PowerInfo* power_info;\n        bool* bool_param;\n    };\n    FuriApiLock lock;\n} PowerMessage;\n"
  },
  {
    "path": "applications/services/power/power_service/views/power_off.c",
    "content": "#include \"power_off.h\"\n#include <furi.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\nstruct PowerOff {\n    View* view;\n};\n\ntypedef struct {\n    PowerOffResponse response;\n    uint32_t time_left_sec;\n} PowerOffModel;\n\nstatic void power_off_draw_callback(Canvas* canvas, void* _model) {\n    furi_assert(_model);\n    PowerOffModel* model = _model;\n    char buff[32];\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str_aligned(canvas, 64, 1, AlignCenter, AlignTop, \"Battery low!\");\n    canvas_draw_icon(canvas, 0, 18, &I_BatteryBody_52x28);\n    canvas_draw_icon(canvas, 16, 25, &I_FaceNopower_29x14);\n    elements_bubble(canvas, 54, 17, 70, 30);\n\n    canvas_set_font(canvas, FontSecondary);\n    if(model->response == PowerOffResponseDefault) {\n        snprintf(buff, sizeof(buff), \"Charge me!\\nOff in %lus!\", model->time_left_sec);\n        elements_multiline_text_aligned(canvas, 70, 23, AlignLeft, AlignTop, buff);\n\n        elements_button_left(canvas, \"Cancel\");\n        elements_button_center(canvas, \"OK\");\n        elements_button_right(canvas, \"Hide\");\n    } else {\n        snprintf(buff, sizeof(buff), \"Charge me!\\nDon't forget!\");\n        elements_multiline_text_aligned(canvas, 70, 23, AlignLeft, AlignTop, buff);\n\n        canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignBottom, \"Hold a second...\");\n    }\n}\n\nstatic bool power_off_input_callback(InputEvent* event, void* context) {\n    PowerOff* power_off = context;\n\n    bool consumed = false;\n    PowerOffModel* model = view_get_model(power_off->view);\n    if(model->response == PowerOffResponseDefault && event->type == InputTypeShort) {\n        if(event->key == InputKeyOk) {\n            model->response = PowerOffResponseOk;\n            consumed = true;\n        } else if(event->key == InputKeyLeft) {\n            model->response = PowerOffResponseCancel;\n            consumed = true;\n        } else if(event->key == InputKeyRight) {\n            model->response = PowerOffResponseHide;\n            consumed = true;\n        }\n    }\n    view_commit_model(power_off->view, consumed);\n\n    return true;\n}\n\nPowerOff* power_off_alloc(void) {\n    PowerOff* power_off = malloc(sizeof(PowerOff));\n\n    power_off->view = view_alloc();\n    view_allocate_model(power_off->view, ViewModelTypeLocking, sizeof(PowerOffModel));\n    view_set_context(power_off->view, power_off);\n    view_set_draw_callback(power_off->view, power_off_draw_callback);\n    view_set_input_callback(power_off->view, power_off_input_callback);\n\n    return power_off;\n}\n\nvoid power_off_free(PowerOff* power_off) {\n    furi_assert(power_off);\n    view_free(power_off->view);\n    free(power_off);\n}\n\nView* power_off_get_view(PowerOff* power_off) {\n    furi_assert(power_off);\n    return power_off->view;\n}\n\nvoid power_off_set_time_left(PowerOff* power_off, uint8_t time_left) {\n    furi_assert(power_off);\n    with_view_model(\n        power_off->view, PowerOffModel * model, { model->time_left_sec = time_left; }, true);\n}\n\nPowerOffResponse power_off_get_response(PowerOff* power_off) {\n    furi_assert(power_off);\n    PowerOffResponse response;\n    with_view_model(\n        power_off->view, PowerOffModel * model, { response = model->response; }, false);\n    return response;\n}\n"
  },
  {
    "path": "applications/services/power/power_service/views/power_off.h",
    "content": "#pragma once\n\ntypedef struct PowerOff PowerOff;\n\ntypedef enum {\n    PowerOffResponseDefault,\n    PowerOffResponseOk,\n    PowerOffResponseCancel,\n    PowerOffResponseHide,\n} PowerOffResponse;\n\n#include <gui/view.h>\n\nPowerOff* power_off_alloc(void);\n\nvoid power_off_free(PowerOff* power_off);\n\nView* power_off_get_view(PowerOff* power_off);\n\nvoid power_off_set_time_left(PowerOff* power_off, uint8_t time_left);\n\nPowerOffResponse power_off_get_response(PowerOff* power_off);\n"
  },
  {
    "path": "applications/services/power/power_service/views/power_unplug_usb.c",
    "content": "#include \"power_unplug_usb.h\"\n#include <furi.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n\nstruct PowerUnplugUsb {\n    View* view;\n};\n\nstatic void power_unplug_usb_draw_callback(Canvas* canvas, void* _model) {\n    UNUSED(_model);\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_icon(canvas, 0, 0, &I_Unplug_bg_top_128x14);\n    canvas_draw_box(canvas, 0, 14, 128, (64 - 10 - 14));\n    canvas_draw_icon(canvas, 0, (64 - 10), &I_Unplug_bg_bottom_128x10);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(\n        canvas, 64, 32, AlignCenter, AlignCenter, \"It's now safe to unplug\\nthe USB cable\");\n}\n\nPowerUnplugUsb* power_unplug_usb_alloc(void) {\n    PowerUnplugUsb* power_unplug_usb = malloc(sizeof(PowerUnplugUsb));\n\n    power_unplug_usb->view = view_alloc();\n    view_set_context(power_unplug_usb->view, power_unplug_usb);\n    view_set_draw_callback(power_unplug_usb->view, power_unplug_usb_draw_callback);\n    view_set_input_callback(power_unplug_usb->view, NULL);\n\n    return power_unplug_usb;\n}\n\nvoid power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb) {\n    furi_assert(power_unplug_usb);\n    view_free(power_unplug_usb->view);\n    free(power_unplug_usb);\n}\n\nView* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb) {\n    furi_assert(power_unplug_usb);\n    return power_unplug_usb->view;\n}\n"
  },
  {
    "path": "applications/services/power/power_service/views/power_unplug_usb.h",
    "content": "#pragma once\n\ntypedef struct PowerUnplugUsb PowerUnplugUsb;\n\n#include <gui/view.h>\n\nPowerUnplugUsb* power_unplug_usb_alloc(void);\n\nvoid power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb);\n\nView* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb);\n"
  },
  {
    "path": "applications/services/region/application.fam",
    "content": "App(\n    appid=\"region\",\n    name=\"RegionSrv\",\n    apptype=FlipperAppType.STARTUP,\n    targets=[\"f7\"],\n    entry_point=\"region_on_system_start\",\n    cdefines=[\"SRV_REGION\"],\n    requires=[\"storage\"],\n    order=120,\n)\n"
  },
  {
    "path": "applications/services/region/region.c",
    "content": "#include <furi_hal_region.h>\n\n#include <furi.h>\n#include <storage/storage.h>\n\n#include <flipper.pb.h>\n#include <pb_decode.h>\n\n#define TAG \"RegionSrv\"\n\n#define SUBGHZ_REGION_FILENAME INT_PATH(\".region_data\")\n\nstatic bool region_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {\n    File* file = istream->state;\n    size_t ret = storage_file_read(file, buf, count);\n    return count == ret;\n}\n\nstatic bool region_istream_decode_band(pb_istream_t* stream, const pb_field_t* field, void** arg) {\n    UNUSED(field);\n\n    FuriHalRegion* region = *arg;\n\n    PB_Region_Band band = {0};\n    if(!pb_decode(stream, PB_Region_Band_fields, &band)) {\n        FURI_LOG_E(TAG, \"PB Region band decode error: %s\", PB_GET_ERROR(stream));\n        return false;\n    }\n\n    region->bands_count += 1;\n    region = realloc( //-V701\n        region,\n        sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count);\n    size_t pos = region->bands_count - 1;\n    region->bands[pos].start = band.start;\n    region->bands[pos].end = band.end;\n    region->bands[pos].power_limit = band.power_limit;\n    region->bands[pos].duty_cycle = band.duty_cycle;\n    *arg = region;\n\n    FURI_LOG_I(\n        TAG,\n        \"Add allowed band: start %luHz, stop %luHz, power_limit %ddBm, duty_cycle %u%%\",\n        band.start,\n        band.end,\n        band.power_limit,\n        band.duty_cycle);\n    return true;\n}\n\nstatic int32_t region_load_file(void* context) {\n    UNUSED(context);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    PB_Region pb_region = {0};\n    pb_region.bands.funcs.decode = region_istream_decode_band;\n\n    do {\n        FileInfo fileinfo = {0};\n\n        if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK ||\n           fileinfo.size == 0) {\n            FURI_LOG_W(TAG, \"Region file missing or empty\");\n            break;\n\n        } else if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {\n            FURI_LOG_E(TAG, \"Failed to open region file\");\n            break;\n        }\n\n        pb_istream_t istream = {\n            .callback = region_istream_read,\n            .state = file,\n            .errmsg = NULL,\n            .bytes_left = fileinfo.size,\n        };\n\n        pb_region.bands.arg = malloc(sizeof(FuriHalRegion));\n\n        if(!pb_decode(&istream, PB_Region_fields, &pb_region)) {\n            FURI_LOG_E(TAG, \"Failed to decode region file\");\n            free(pb_region.bands.arg);\n            break;\n        }\n\n        FuriHalRegion* region = pb_region.bands.arg;\n\n        memcpy(\n            region->country_code,\n            pb_region.country_code->bytes,\n            MIN(pb_region.country_code->size, sizeof(region->country_code) - 1));\n\n        furi_hal_region_set(region);\n\n        FURI_LOG_I(TAG, \"Dynamic region set: %s\", region->country_code);\n    } while(0);\n\n    pb_release(PB_Region_fields, &pb_region);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return 0;\n}\n\nstatic void\n    region_loader_release_callback(FuriThread* thread, FuriThreadState state, void* context) {\n    UNUSED(context);\n\n    if(state == FuriThreadStateStopped) {\n        furi_thread_free(thread);\n    }\n}\n\nstatic void region_storage_callback(const void* message, void* context) {\n    UNUSED(context);\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        FuriThread* loader = furi_thread_alloc_ex(NULL, 2048, region_load_file, NULL);\n        furi_thread_set_state_callback(loader, region_loader_release_callback);\n        furi_thread_start(loader);\n    }\n}\n\nint32_t region_on_system_start(void* p) {\n    UNUSED(p);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    furi_pubsub_subscribe(storage_get_pubsub(storage), region_storage_callback, NULL);\n\n    if(storage_sd_status(storage) != FSE_OK) {\n        FURI_LOG_D(TAG, \"SD Card not ready, skipping dynamic region\");\n        return 0;\n    }\n\n    region_load_file(NULL);\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/rpc/application.fam",
    "content": "App(\n    appid=\"rpc_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"rpc_on_system_start\",\n    cdefines=[\"SRV_RPC\"],\n    requires=[\"cli\"],\n    order=10,\n    sdk_headers=[\"rpc_app.h\"],\n)\n"
  },
  {
    "path": "applications/services/rpc/rpc.c",
    "content": "#include \"rpc_i.h\"\n\n#include <pb.h>\n#include <pb_decode.h>\n#include <pb_encode.h>\n\n#include <storage.pb.h>\n#include <flipper.pb.h>\n\n#include <furi.h>\n\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <m-dict.h>\n\n#include <bt/bt_service/bt.h>\n\n#define TAG \"RpcSrv\"\n\ntypedef enum {\n    RpcEvtNewData = (1 << 0),\n    RpcEvtDisconnect = (1 << 1),\n} RpcEvtFlags;\n\n#define RPC_ALL_EVENTS (RpcEvtNewData | RpcEvtDisconnect)\n\nDICT_DEF2(RpcHandlerDict, pb_size_t, M_DEFAULT_OPLIST, RpcHandler, M_POD_OPLIST)\n\ntypedef struct {\n    RpcSystemAlloc alloc;\n    RpcSystemFree free;\n    void* context;\n} RpcSystemCallbacks;\n\nstatic RpcSystemCallbacks rpc_systems[] = {\n    {\n        .alloc = rpc_system_system_alloc,\n        .free = NULL,\n    },\n    {\n        .alloc = rpc_system_storage_alloc,\n        .free = rpc_system_storage_free,\n    },\n    {\n        .alloc = rpc_system_app_alloc,\n        .free = rpc_system_app_free,\n    },\n    {\n        .alloc = rpc_system_gui_alloc,\n        .free = rpc_system_gui_free,\n    },\n    {\n        .alloc = rpc_system_gpio_alloc,\n        .free = NULL,\n    },\n    {\n        .alloc = rpc_system_property_alloc,\n        .free = NULL,\n    },\n    {\n        .alloc = rpc_desktop_alloc,\n        .free = rpc_desktop_free,\n    },\n};\n\nstruct RpcSession {\n    Rpc* rpc;\n\n    FuriThread* thread;\n\n    RpcHandlerDict_t handlers;\n    FuriStreamBuffer* stream;\n    PB_Main* decoded_message;\n    bool terminate;\n    void** system_contexts;\n    bool decode_error;\n\n    FuriMutex* callbacks_mutex;\n    RpcSendBytesCallback send_bytes_callback;\n    RpcBufferIsEmptyCallback buffer_is_empty_callback;\n    RpcSessionClosedCallback closed_callback;\n    RpcSessionTerminatedCallback terminated_callback;\n    RpcOwner owner;\n    void* context;\n};\n\nstruct Rpc {\n    FuriMutex* busy_mutex;\n};\n\nRpcOwner rpc_session_get_owner(RpcSession* session) {\n    furi_check(session);\n    return session->owner;\n}\n\nstatic void rpc_close_session_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    RpcSession* session = (RpcSession*)context;\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    if(session->closed_callback) {\n        session->closed_callback(session->context);\n    } else {\n        FURI_LOG_W(TAG, \"Session stop isn't processed by transport layer\");\n    }\n    furi_mutex_release(session->callbacks_mutex);\n}\n\nvoid rpc_session_set_context(RpcSession* session, void* context) {\n    furi_check(session);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    session->context = context;\n    furi_mutex_release(session->callbacks_mutex);\n}\n\nvoid rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) {\n    furi_check(session);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    session->closed_callback = callback;\n    furi_mutex_release(session->callbacks_mutex);\n}\n\nvoid rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) {\n    furi_check(session);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    session->send_bytes_callback = callback;\n    furi_mutex_release(session->callbacks_mutex);\n}\n\nvoid rpc_session_set_buffer_is_empty_callback(\n    RpcSession* session,\n    RpcBufferIsEmptyCallback callback) {\n    furi_check(session);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    session->buffer_is_empty_callback = callback;\n    furi_mutex_release(session->callbacks_mutex);\n}\n\nvoid rpc_session_set_terminated_callback(\n    RpcSession* session,\n    RpcSessionTerminatedCallback callback) {\n    furi_check(session);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    session->terminated_callback = callback;\n    furi_mutex_release(session->callbacks_mutex);\n}\n\n/* Doesn't forbid using rpc_feed_bytes() after session close - it's safe.\n * Because any bytes received in buffer will be flushed before next session.\n * If bytes get into stream buffer before it's get epmtified and this\n * command is gets processed - it's safe either. But case of it is quite\n * odd: client sends close request and sends command after.\n */\nsize_t rpc_session_feed(\n    RpcSession* session,\n    const uint8_t* encoded_bytes,\n    size_t size,\n    uint32_t timeout) {\n    furi_check(session);\n    furi_check(encoded_bytes);\n\n    if(!size) return 0;\n\n    size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout);\n\n    furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData);\n\n    return bytes_sent;\n}\n\nsize_t rpc_session_get_available_size(RpcSession* session) {\n    furi_check(session);\n    return furi_stream_buffer_spaces_available(session->stream);\n}\n\nbool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {\n    furi_assert(istream);\n    furi_assert(buf);\n    RpcSession* session = istream->state;\n    furi_assert(session);\n    furi_assert(istream->bytes_left);\n\n    if(session->terminate) {\n        return false;\n    }\n\n    uint32_t flags = 0;\n    size_t bytes_received = 0;\n\n    while(1) {\n        bytes_received += furi_stream_buffer_receive(\n            session->stream, buf + bytes_received, count - bytes_received, 0);\n        if(furi_stream_buffer_is_empty(session->stream)) {\n            if(session->buffer_is_empty_callback) {\n                session->buffer_is_empty_callback(session->context);\n            }\n        }\n        if(session->decode_error) {\n            /* never go out till RPC_EVENT_DISCONNECT come */\n            bytes_received = 0;\n        }\n        if(count == bytes_received) {\n            break;\n        } else {\n            flags = furi_thread_flags_wait(RPC_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever);\n            if(flags & RpcEvtDisconnect) {\n                if(furi_stream_buffer_is_empty(session->stream)) {\n                    session->terminate = true;\n                    istream->bytes_left = 0;\n                    bytes_received = 0;\n                    break;\n                } else {\n                    /* Save disconnect flag and continue reading buffer */\n                    furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);\n                }\n            } else if(flags & RpcEvtNewData) {\n                // Just wake thread up\n            }\n        }\n    }\n\n#ifdef SRV_RPC_DEBUG\n    rpc_debug_print_data(\"INPUT\", buf, bytes_received);\n#endif\n\n    return count == bytes_received;\n}\n\nstatic bool rpc_pb_content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) {\n    furi_assert(stream);\n    RpcSession* session = stream->state;\n    furi_assert(session);\n    furi_assert(field);\n\n    RpcHandler* handler = RpcHandlerDict_get(session->handlers, field->tag);\n\n    if(handler && handler->decode_submessage) {\n        handler->decode_submessage(stream, field, arg);\n    }\n\n    return true;\n}\n\nstatic int32_t rpc_session_worker(void* context) {\n    furi_assert(context);\n    RpcSession* session = (RpcSession*)context;\n    Rpc* rpc = session->rpc;\n\n    FURI_LOG_D(TAG, \"Session started\");\n\n    while(1) {\n        pb_istream_t istream = {\n            .callback = rpc_pb_stream_read,\n            .state = session,\n            .errmsg = NULL,\n            .bytes_left = SIZE_MAX,\n        };\n\n        bool message_decode_failed = false;\n\n        if(pb_decode_ex(&istream, &PB_Main_msg, session->decoded_message, PB_DECODE_DELIMITED)) {\n#ifdef SRV_RPC_DEBUG\n            FURI_LOG_I(TAG, \"INPUT:\");\n            rpc_debug_print_message(session->decoded_message);\n#endif\n            RpcHandler* handler =\n                RpcHandlerDict_get(session->handlers, session->decoded_message->which_content);\n\n            if(handler && handler->message_handler) {\n                furi_check(furi_mutex_acquire(rpc->busy_mutex, FuriWaitForever) == FuriStatusOk);\n                handler->message_handler(session->decoded_message, handler->context);\n                furi_check(furi_mutex_release(rpc->busy_mutex) == FuriStatusOk);\n            } else if(session->decoded_message->which_content == 0) {\n                /* Receiving zeroes means message is 0-length, which\n                 * is valid for proto3: all fields are filled with default values.\n                 * 0 - is default value for which_content field.\n                 * Mark it as decode error, because there is no content message\n                 * in Main message with tag 0.\n                 */\n                message_decode_failed = true;\n            } else if(!handler && !session->terminate) {\n                FURI_LOG_E(\n                    TAG,\n                    \"Message(%d) decoded, but not implemented\",\n                    session->decoded_message->which_content);\n                rpc_send_and_release_empty(\n                    session,\n                    session->decoded_message->command_id,\n                    PB_CommandStatus_ERROR_NOT_IMPLEMENTED);\n            }\n        } else {\n            message_decode_failed = true;\n        }\n\n        if(message_decode_failed) {\n            furi_stream_buffer_reset(session->stream);\n            if(!session->terminate) {\n                /* Protobuf can't determine start and end of message.\n                 * Handle this by adding varint at beginning\n                 * of a message (PB_ENCODE_DELIMITED). But decoding fail\n                 * means we can't be sure next bytes are varint for next\n                 * message, so the only way to close session.\n                 * RPC itself can't make decision to close session. It has\n                 * to notify:\n                 * 1) down layer (transport)\n                 * 2) other side (companion app)\n                 * Who are responsible to handle RPC session lifecycle.\n                 * Companion receives 2 messages: ERROR_DECODE and session_closed.\n                 */\n                FURI_LOG_E(TAG, \"Decode failed, error: \\'%.128s\\'\", PB_GET_ERROR(&istream));\n                session->decode_error = true;\n                rpc_send_and_release_empty(session, 0, PB_CommandStatus_ERROR_DECODE);\n                furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n                if(session->closed_callback) {\n                    session->closed_callback(session->context);\n                }\n                furi_mutex_release(session->callbacks_mutex);\n\n                if(session->owner == RpcOwnerBle) {\n                    // Disconnect BLE session\n                    FURI_LOG_E(\"RPC\", \"BLE session closed due to a decode error\");\n                    Bt* bt = furi_record_open(RECORD_BT);\n                    bt_profile_restore_default(bt);\n                    furi_record_close(RECORD_BT);\n                    FURI_LOG_E(\"RPC\", \"Finished disconnecting the BLE session\");\n                }\n            }\n        }\n\n        pb_release(&PB_Main_msg, session->decoded_message);\n\n        if(session->terminate) {\n            FURI_LOG_D(TAG, \"Session terminated\");\n            break;\n        }\n    }\n\n    return 0;\n}\n\nstatic void rpc_session_thread_pending_callback(void* context, uint32_t arg) {\n    UNUSED(arg);\n    RpcSession* session = (RpcSession*)context;\n\n    for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {\n        if(rpc_systems[i].free) {\n            (rpc_systems[i].free)(session->system_contexts[i]);\n        }\n    }\n    free(session->system_contexts);\n    free(session->decoded_message);\n    RpcHandlerDict_clear(session->handlers);\n    furi_stream_buffer_free(session->stream);\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    if(session->terminated_callback) {\n        session->terminated_callback(session->context);\n    }\n    furi_mutex_release(session->callbacks_mutex);\n\n    furi_mutex_free(session->callbacks_mutex);\n    furi_thread_join(session->thread);\n    furi_thread_free(session->thread);\n    free(session);\n}\n\nstatic void\n    rpc_session_thread_state_callback(FuriThread* thread, FuriThreadState state, void* context) {\n    UNUSED(thread);\n    if(state == FuriThreadStateStopped) {\n        furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0);\n    }\n}\n\nRpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) {\n    furi_check(rpc);\n\n    RpcSession* session = malloc(sizeof(RpcSession));\n    session->callbacks_mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n    session->stream = furi_stream_buffer_alloc(RPC_BUFFER_SIZE, 1);\n    session->rpc = rpc;\n    session->terminate = false;\n    session->decode_error = false;\n    session->owner = owner;\n    RpcHandlerDict_init(session->handlers);\n\n    session->decoded_message = malloc(sizeof(PB_Main));\n    session->decoded_message->cb_content.funcs.decode = rpc_pb_content_callback;\n    session->decoded_message->cb_content.arg = session;\n\n    session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*));\n    for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {\n        session->system_contexts[i] = rpc_systems[i].alloc(session);\n    }\n\n    RpcHandler rpc_handler = {\n        .message_handler = rpc_close_session_process,\n        .decode_submessage = NULL,\n        .context = session,\n    };\n    rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler);\n\n    session->thread = furi_thread_alloc_ex(\"RpcSessionWorker\", 3072, rpc_session_worker, session);\n\n    furi_thread_set_state_context(session->thread, session);\n    furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback);\n\n    furi_thread_start(session->thread);\n\n    return session;\n}\n\nvoid rpc_session_close(RpcSession* session) {\n    furi_check(session);\n    furi_check(session->rpc);\n\n    rpc_session_set_send_bytes_callback(session, NULL);\n    rpc_session_set_close_callback(session, NULL);\n    rpc_session_set_buffer_is_empty_callback(session, NULL);\n    furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);\n}\n\nvoid rpc_on_system_start(void* p) {\n    UNUSED(p);\n    Rpc* rpc = malloc(sizeof(Rpc));\n\n    rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(\n        registry,\n        \"start_rpc_session\",\n        CliCommandFlagParallelSafe,\n        rpc_cli_command_start_session,\n        rpc);\n    furi_record_close(RECORD_CLI);\n\n    furi_record_create(RECORD_RPC, rpc);\n}\n\nvoid rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler) {\n    furi_assert(RpcHandlerDict_get(session->handlers, message_tag) == NULL);\n\n    RpcHandlerDict_set_at(session->handlers, message_tag, *handler);\n}\n\nvoid rpc_send(RpcSession* session, PB_Main* message) {\n    furi_assert(session);\n    furi_assert(message);\n\n    pb_ostream_t ostream = PB_OSTREAM_SIZING;\n\n#ifdef SRV_RPC_DEBUG\n    FURI_LOG_I(TAG, \"OUTPUT:\");\n    rpc_debug_print_message(message);\n#endif\n\n    bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED);\n    furi_check(result && ostream.bytes_written);\n\n    uint8_t* buffer = malloc(ostream.bytes_written);\n    ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);\n\n    pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED);\n\n#ifdef SRV_RPC_DEBUG\n    rpc_debug_print_data(\"OUTPUT\", buffer, ostream.bytes_written);\n#endif\n\n    furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);\n    if(session->send_bytes_callback) {\n        session->send_bytes_callback(session->context, buffer, ostream.bytes_written);\n    }\n    furi_mutex_release(session->callbacks_mutex);\n\n    free(buffer);\n}\n\nvoid rpc_send_and_release(RpcSession* session, PB_Main* message) {\n    rpc_send(session, message);\n    pb_release(&PB_Main_msg, message);\n}\n\nvoid rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) {\n    furi_assert(session);\n\n    PB_Main message = {\n        .command_id = command_id,\n        .command_status = status,\n        .has_next = false,\n        .which_content = PB_Main_empty_tag,\n    };\n\n    rpc_send_and_release(session, &message);\n    pb_release(&PB_Main_msg, &message);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc.h",
    "content": "#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RPC_BUFFER_SIZE (1024)\n\n#define RECORD_RPC \"rpc\"\n\n/** Rpc interface. Used for opening session only. */\ntypedef struct Rpc Rpc;\n/** Rpc session interface */\ntypedef struct RpcSession RpcSession;\n\n/** Callback to send to client any data (e.g. response to command) */\ntypedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len);\n/** Callback to notify client that buffer is empty */\ntypedef void (*RpcBufferIsEmptyCallback)(void* context);\n/** Callback to notify transport layer that close_session command\n * is received. Any other actions lays on transport layer.\n * No destruction or session close performed. */\ntypedef void (*RpcSessionClosedCallback)(void* context);\n/** Callback to notify transport layer that session was closed\n * and all operations were finished */\ntypedef void (*RpcSessionTerminatedCallback)(void* context);\n\n/** RPC owner */\ntypedef enum {\n    RpcOwnerUnknown = 0,\n    RpcOwnerBle,\n    RpcOwnerUsb,\n    RpcOwnerUart,\n    RpcOwnerCount,\n} RpcOwner;\n\n/** Get RPC session owner\n *\n * @param   session     pointer to RpcSession descriptor\n * @return              session owner\n */\nRpcOwner rpc_session_get_owner(RpcSession* session);\n\n/** Open RPC session\n *\n * USAGE:\n * 1) rpc_session_open();\n * 2) rpc_session_set_context();\n * 3) rpc_session_set_send_bytes_callback();\n * 4) rpc_session_set_close_callback();\n * 5) while(1) {\n *      rpc_session_feed();\n *    }\n * 6) rpc_session_close();\n *\n *\n * @param   rpc     instance\n * @param   owner   owner of session\n * @return          pointer to RpcSession descriptor, or\n *                  NULL if RPC is busy and can't open session now\n */\nRpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner);\n\n/** Close RPC session\n * It is guaranteed that no callbacks will be called\n * as soon as session is closed. So no need in setting\n * callbacks to NULL after session close.\n *\n * @param   session     pointer to RpcSession descriptor\n */\nvoid rpc_session_close(RpcSession* session);\n\n/** Set session context for callbacks to pass\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   context     context to pass to callbacks\n */\nvoid rpc_session_set_context(RpcSession* session, void* context);\n\n/** Set callback to send bytes to client\n *  WARN: It's forbidden to call RPC API within RpcSendBytesCallback\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   callback    callback to send bytes to client (can be NULL)\n */\nvoid rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback);\n\n/** Set callback to notify that buffer is empty\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   callback    callback to notify client that buffer is empty (can be NULL)\n * @param   context     context to pass to callback\n */\nvoid rpc_session_set_buffer_is_empty_callback(\n    RpcSession* session,\n    RpcBufferIsEmptyCallback callback);\n\n/** Set callback to be called when RPC command to close session is received\n *  WARN: It's forbidden to call RPC API within RpcSessionClosedCallback\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   callback    callback to inform about RPC close session command (can be NULL)\n */\nvoid rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback);\n\n/** Set callback to be called when RPC session is closed\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   callback    callback to inform about RPC session state\n */\nvoid rpc_session_set_terminated_callback(\n    RpcSession* session,\n    RpcSessionTerminatedCallback callback);\n\n/** Give bytes to RPC service to decode them and perform command\n *\n * @param   session     pointer to RpcSession descriptor\n * @param   buffer      buffer to provide to RPC service\n * @param   size        size of buffer\n * @param   timeout     max timeout to wait till all buffer will be consumed\n *\n * @return              actually consumed bytes\n */\nsize_t rpc_session_feed(RpcSession* session, const uint8_t* buffer, size_t size, uint32_t timeout);\n\n/** Get available size of RPC buffer\n *\n * @param   session     pointer to RpcSession descriptor\n *\n * @return              bytes available in buffer\n */\nsize_t rpc_session_get_available_size(RpcSession* session);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/rpc/rpc_app.c",
    "content": "#include \"flipper.pb.h\"\n#include <core/record.h>\n#include \"rpc_i.h\"\n#include <furi.h>\n#include <loader/loader.h>\n#include \"rpc_app.h\"\n\n#define TAG \"RpcSystemApp\"\n\nstruct RpcAppSystem {\n    RpcSession* session;\n\n    RpcAppSystemCallback callback;\n    void* callback_context;\n\n    uint32_t error_code;\n    char* error_text;\n\n    uint32_t last_command_id;\n    RpcAppSystemEventType last_event_type;\n};\n\n#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16\n\nstatic void rpc_system_app_send_state_response(\n    RpcAppSystem* rpc_app,\n    PB_App_AppState state,\n    const char* name) {\n    PB_Main* response = malloc(sizeof(PB_Main));\n\n    response->which_content = PB_Main_app_state_response_tag;\n    response->content.app_state_response.state = state;\n\n    FURI_LOG_D(TAG, \"%s\", name);\n    rpc_send(rpc_app->session, response);\n\n    free(response);\n}\n\nstatic void rpc_system_app_send_error_response(\n    RpcAppSystem* rpc_app,\n    uint32_t command_id,\n    PB_CommandStatus status,\n    const char* name) {\n    // Not describing all possible errors as only APP_NOT_RUNNING is used\n    const char* status_str = status == PB_CommandStatus_ERROR_APP_NOT_RUNNING ? \"APP_NOT_RUNNING\" :\n                                                                                \"UNKNOWN\";\n    FURI_LOG_E(TAG, \"%s: %s, id %lu, status: %d\", name, status_str, command_id, status);\n    rpc_send_and_release_empty(rpc_app->session, command_id, status);\n}\n\nstatic void rpc_system_app_set_last_command(\n    RpcAppSystem* rpc_app,\n    uint32_t command_id,\n    const RpcAppSystemEvent* event) {\n    furi_assert(rpc_app->last_command_id == 0);\n    furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);\n\n    rpc_app->last_command_id = command_id;\n    rpc_app->last_event_type = event->type;\n}\n\nstatic void rpc_system_app_start_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_start_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n    furi_assert(rpc_app->last_command_id == 0);\n    furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);\n\n    FURI_LOG_D(TAG, \"StartProcess: id %lu\", request->command_id);\n\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    const char* app_name = request->content.app_start_request.name;\n\n    PB_CommandStatus result;\n\n    if(app_name) {\n        rpc_system_app_error_reset(rpc_app);\n\n        char app_args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];\n        const char* app_args = request->content.app_start_request.args;\n\n        if(app_args && strcmp(app_args, \"RPC\") == 0) {\n            // If app is being started in RPC mode - pass RPC context via args string\n            snprintf(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, \"RPC %08lX\", (uint32_t)rpc_app);\n            app_args = app_args_temp;\n        }\n\n        const LoaderStatus status = loader_start(loader, app_name, app_args, NULL);\n        if(status == LoaderStatusErrorAppStarted) {\n            result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED;\n        } else if(status == LoaderStatusErrorInternal) {\n            result = PB_CommandStatus_ERROR_APP_CANT_START;\n        } else if(status == LoaderStatusErrorUnknownApp) {\n            result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;\n        } else if(status == LoaderStatusOk) {\n            result = PB_CommandStatus_OK;\n        } else {\n            furi_crash();\n        }\n    } else {\n        result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;\n    }\n\n    furi_record_close(RECORD_LOADER);\n\n    FURI_LOG_D(TAG, \"StartProcess: response id %lu, result %d\", request->command_id, result);\n    rpc_send_and_release_empty(rpc_app->session, request->command_id, result);\n}\n\nstatic void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_lock_status_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    rpc_system_app_error_reset(rpc_app);\n\n    FURI_LOG_D(TAG, \"LockStatus\");\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_app_lock_status_response_tag;\n\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    response->content.app_lock_status_response.locked = loader_is_locked(loader);\n    furi_record_close(RECORD_LOADER);\n\n    FURI_LOG_D(TAG, \"LockStatus: response\");\n    rpc_send_and_release(rpc_app->session, response);\n\n    free(response);\n}\n\nstatic void rpc_system_app_exit_request(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_exit_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"ExitRequest: id %lu\", request->command_id);\n\n        const RpcAppSystemEvent event = {\n            .type = RpcAppEventTypeAppExit,\n            .data =\n                {\n                    .type = RpcAppSystemEventDataTypeNone,\n                    {0},\n                },\n        };\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, \"ExitRequest\");\n    }\n}\n\nstatic void rpc_system_app_load_file(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_load_file_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"LoadFile: id %lu\", request->command_id);\n\n        const RpcAppSystemEvent event = {\n            .type = RpcAppEventTypeLoadFile,\n            .data =\n                {\n                    .type = RpcAppSystemEventDataTypeString,\n                    .string = request->content.app_load_file_request.path,\n                },\n        };\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, \"LoadFile\");\n    }\n}\n\nstatic void rpc_system_app_button_press(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_button_press_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"ButtonPress\");\n\n        RpcAppSystemEvent event;\n        event.type = RpcAppEventTypeButtonPress;\n\n        if(strlen(request->content.app_button_press_request.args) != 0) {\n            event.data.type = RpcAppSystemEventDataTypeString;\n            event.data.string = request->content.app_button_press_request.args;\n        } else {\n            event.data.type = RpcAppSystemEventDataTypeInt32;\n            event.data.i32 = request->content.app_button_press_request.index;\n        }\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, \"ButtonPress\");\n    }\n}\n\nstatic void rpc_system_app_button_release(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_button_release_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"ButtonRelease\");\n\n        const RpcAppSystemEvent event = {\n            .type = RpcAppEventTypeButtonRelease,\n            .data =\n                {\n                    .type = RpcAppSystemEventDataTypeNone,\n                    {0},\n                },\n        };\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, \"ButtonRelease\");\n    }\n}\n\nstatic void rpc_system_app_button_press_release(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_button_press_release_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"ButtonPressRelease\");\n\n        RpcAppSystemEvent event;\n        event.type = RpcAppEventTypeButtonPressRelease;\n\n        if(strlen(request->content.app_button_press_release_request.args) != 0) {\n            event.data.type = RpcAppSystemEventDataTypeString;\n            event.data.string = request->content.app_button_press_release_request.args;\n        } else {\n            event.data.type = RpcAppSystemEventDataTypeInt32;\n            event.data.i32 = request->content.app_button_press_release_request.index;\n        }\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app,\n            request->command_id,\n            PB_CommandStatus_ERROR_APP_NOT_RUNNING,\n            \"ButtonPressRelease\");\n    }\n}\n\nstatic void rpc_system_app_get_error_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_get_error_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_app_get_error_response_tag;\n    response->content.app_get_error_response.code = rpc_app->error_code;\n    response->content.app_get_error_response.text = rpc_app->error_text;\n\n    FURI_LOG_D(TAG, \"GetError\");\n    rpc_send(rpc_app->session, response);\n\n    free(response);\n}\n\nstatic void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag);\n\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n\n    if(rpc_app->callback) {\n        FURI_LOG_D(TAG, \"DataExchange\");\n\n        const pb_bytes_array_t* data = request->content.app_data_exchange_request.data;\n\n        const RpcAppSystemEvent event = {\n            .type = RpcAppEventTypeDataExchange,\n            .data =\n                {\n                    .type = RpcAppSystemEventDataTypeBytes,\n                    .bytes =\n                        {\n                            .ptr = data ? data->bytes : NULL,\n                            .size = data ? data->size : 0,\n                        },\n                },\n        };\n\n        rpc_system_app_error_reset(rpc_app);\n        rpc_system_app_set_last_command(rpc_app, request->command_id, &event);\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n    } else {\n        rpc_system_app_send_error_response(\n            rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, \"DataExchange\");\n    }\n}\n\nvoid rpc_system_app_send_started(RpcAppSystem* rpc_app) {\n    furi_check(rpc_app);\n    rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_STARTED, \"SendStarted\");\n}\n\nvoid rpc_system_app_send_exited(RpcAppSystem* rpc_app) {\n    furi_check(rpc_app);\n    rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, \"SendExit\");\n}\n\nvoid rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {\n    furi_check(rpc_app);\n    furi_check(rpc_app->last_command_id != 0);\n    /* Ensure that only commands of these types can be confirmed */\n    furi_check(\n        rpc_app->last_event_type == RpcAppEventTypeAppExit ||\n        rpc_app->last_event_type == RpcAppEventTypeLoadFile ||\n        rpc_app->last_event_type == RpcAppEventTypeButtonPress ||\n        rpc_app->last_event_type == RpcAppEventTypeButtonRelease ||\n        rpc_app->last_event_type == RpcAppEventTypeButtonPressRelease ||\n        rpc_app->last_event_type == RpcAppEventTypeDataExchange);\n\n    const uint32_t last_command_id = rpc_app->last_command_id;\n    const RpcAppSystemEventType last_event_type = rpc_app->last_event_type;\n\n    rpc_app->last_command_id = 0;\n    rpc_app->last_event_type = RpcAppEventTypeInvalid;\n\n    const PB_CommandStatus status = result ? PB_CommandStatus_OK :\n                                             PB_CommandStatus_ERROR_APP_CMD_ERROR;\n    FURI_LOG_D(\n        TAG,\n        \"AppConfirm: event %d last_id %lu status %d\",\n        last_event_type,\n        last_command_id,\n        status);\n\n    rpc_send_and_release_empty(rpc_app->session, last_command_id, status);\n}\n\nvoid rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {\n    furi_check(rpc_app);\n\n    rpc_app->callback = callback;\n    rpc_app->callback_context = ctx;\n}\n\nvoid rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) {\n    furi_check(rpc_app);\n    rpc_app->error_code = error_code;\n}\n\nvoid rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) {\n    furi_check(rpc_app);\n\n    if(rpc_app->error_text) {\n        free(rpc_app->error_text);\n    }\n\n    rpc_app->error_text = error_text ? strdup(error_text) : NULL;\n}\n\nvoid rpc_system_app_error_reset(RpcAppSystem* rpc_app) {\n    furi_check(rpc_app);\n\n    rpc_system_app_set_error_code(rpc_app, 0);\n    rpc_system_app_set_error_text(rpc_app, NULL);\n}\n\nvoid rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) {\n    furi_check(rpc_app);\n\n    PB_Main* request = malloc(sizeof(PB_Main));\n\n    request->which_content = PB_Main_app_data_exchange_request_tag;\n    PB_App_DataExchangeRequest* content = &request->content.app_data_exchange_request;\n\n    if(data && data_size) {\n        content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size));\n        content->data->size = data_size;\n        memcpy(content->data->bytes, data, data_size);\n    } else {\n        content->data = NULL;\n    }\n\n    rpc_send_and_release(rpc_app->session, request);\n\n    free(request);\n}\n\nvoid* rpc_system_app_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));\n    rpc_app->session = session;\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = rpc_app,\n    };\n\n    rpc_handler.message_handler = rpc_system_app_start_process;\n    rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_lock_status_process;\n    rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_exit_request;\n    rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_load_file;\n    rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_button_press;\n    rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_button_release;\n    rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_button_press_release;\n    rpc_add_handler(session, PB_Main_app_button_press_release_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_get_error_process;\n    rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_app_data_exchange_process;\n    rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler);\n\n    return rpc_app;\n}\n\nvoid rpc_system_app_free(void* context) {\n    RpcAppSystem* rpc_app = context;\n    furi_assert(rpc_app);\n    furi_assert(rpc_app->session);\n\n    if(rpc_app->callback) {\n        const RpcAppSystemEvent event = {\n            .type = RpcAppEventTypeSessionClose,\n            .data =\n                {\n                    .type = RpcAppSystemEventDataTypeNone,\n                    {0},\n                },\n        };\n\n        rpc_app->callback(&event, rpc_app->callback_context);\n    }\n\n    while(rpc_app->callback) {\n        furi_delay_tick(1);\n    }\n\n    free(rpc_app);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_app.h",
    "content": "/**\n * @file rpc_app.h\n * @brief Application RPC subsystem interface.\n *\n * The application RPC subsystem provides facilities for interacting with applications,\n * such as starting/stopping, passing parameters, sending commands and exchanging arbitrary data.\n *\n * All commands are handled asynchronously via a user-settable callback.\n *\n * For a complete description of message types handled in this subsystem,\n * see https://github.com/flipperdevices/flipperzero-protobuf/blob/dev/application.proto\n */\n#pragma once\n\n#include \"rpc.h\"\n#include \"rpc_app_error_codes.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Enumeration of possible event data types.\n */\ntypedef enum {\n    RpcAppSystemEventDataTypeNone, /**< No data is provided by the event. */\n    RpcAppSystemEventDataTypeString, /**< Event data contains a zero-terminated string. */\n    RpcAppSystemEventDataTypeInt32, /**< Event data contains a signed 32-bit integer. */\n    RpcAppSystemEventDataTypeBytes, /**< Event data contains zero or more bytes. */\n} RpcAppSystemEventDataType;\n\n/**\n * @brief Event data structure, containing the type and associated data.\n *\n * All below fields except for type are valid only if the respective type is set.\n */\ntypedef struct {\n    RpcAppSystemEventDataType\n        type; /**< Type of the data. The meaning of other fields depends on this one. */\n    union {\n        const char* string; /**< Pointer to a zero-terminated character string. */\n        int32_t i32; /**< Signed 32-bit integer value. */\n        struct {\n            const uint8_t* ptr; /**< Pointer to the byte array data. */\n            size_t size; /**< Size of the byte array, in bytes. */\n        } bytes; /**< Byte array of arbitrary length. */\n    };\n} RpcAppSystemEventData;\n\n/**\n * @brief Enumeration of possible event types.\n */\ntypedef enum {\n    /**\n     * @brief Denotes an invalid state.\n     *\n     * An event of this type shall never be passed into the callback.\n     */\n    RpcAppEventTypeInvalid,\n    /**\n     * @brief The client side has closed the session.\n     *\n     * After receiving this event, the RPC context is no more valid.\n     */\n    RpcAppEventTypeSessionClose,\n    /**\n     * @brief The client has requested the application to exit.\n     *\n     * The application must exit after receiving this command.\n     */\n    RpcAppEventTypeAppExit,\n    /**\n     * @brief The client has requested the application to load a file.\n     *\n     * This command's meaning is application-specific, i.e. the application might or\n     * might not require additional commands after loading a file to do anything useful.\n     */\n    RpcAppEventTypeLoadFile,\n    /**\n     * @brief The client has informed the application that a button has been pressed.\n     *\n     * This command's meaning is application-specific, e.g. to select a part of the\n     * previously loaded file or to invoke a particular function within the application.\n     */\n    RpcAppEventTypeButtonPress,\n    /**\n     * @brief The client has informed the application that a button has been released.\n     *\n     * This command's meaning is application-specific, e.g. to cease\n     * all activities to be conducted while a button is being pressed.\n     */\n    RpcAppEventTypeButtonRelease,\n    /**\n     * @brief The client has informed the application that a button has been pressed and released.\n     * \n     * This command's meaning is application-specific, e.g. to perform an action\n     * once without repeating it.\n     */\n    RpcAppEventTypeButtonPressRelease,\n    /**\n     * @brief The client has sent a byte array of arbitrary size.\n     *\n     * This command's purpose is bi-directional exchange of arbitrary raw data.\n     * Useful for implementing higher-level protocols while using the RPC as a transport layer.\n     */\n    RpcAppEventTypeDataExchange,\n} RpcAppSystemEventType;\n\n/**\n * @brief RPC application subsystem event structure.\n */\ntypedef struct {\n    RpcAppSystemEventType type; /**< Type of the event. */\n    RpcAppSystemEventData data; /**< Data associated with the event. */\n} RpcAppSystemEvent;\n\n/**\n * @brief Callback function type.\n *\n * A function of this type must be passed to rpc_system_app_set_callback() by the user code.\n *\n * @warning The event pointer is valid ONLY inside the callback function.\n *\n * @param[in] event pointer to the event object. Valid only inside the callback function.\n * @param[in,out] context pointer to the user-defined context object.\n */\ntypedef void (*RpcAppSystemCallback)(const RpcAppSystemEvent* event, void* context);\n\n/**\n * @brief RPC application subsystem opaque type declaration.\n */\ntypedef struct RpcAppSystem RpcAppSystem;\n\n/**\n * @brief Set the callback function for use by an RpcAppSystem instance.\n *\n * @param[in,out] rpc_app pointer to the instance to be configured.\n * @param[in] callback pointer to the function to be called upon message reception.\n * @param[in,out] context pointer to the user-defined context object. Will be passed to the callback.\n */\nvoid rpc_system_app_set_callback(\n    RpcAppSystem* rpc_app,\n    RpcAppSystemCallback callback,\n    void* context);\n\n/**\n * @brief Send a notification that an RpcAppSystem instance has been started and is ready.\n *\n * Call this function once right after acquiring an RPC context and setting the callback.\n *\n * @param[in,out] rpc_app pointer to the instance to be used.\n */\nvoid rpc_system_app_send_started(RpcAppSystem* rpc_app);\n\n/**\n * @brief Send a notification that the application using an RpcAppSystem instance is about to exit.\n *\n * Call this function when the application is about to exit (usually in the *_free() function).\n *\n * @param[in,out] rpc_app pointer to the instance to be used.\n */\nvoid rpc_system_app_send_exited(RpcAppSystem* rpc_app);\n\n/**\n * @brief Send a confirmation that the application using an RpcAppSystem instance has handled the event.\n *\n * An explicit confirmation is required for the following event types:\n * - RpcAppEventTypeAppExit\n * - RpcAppEventTypeLoadFile\n * - RpcAppEventTypeButtonPress\n * - RpcAppEventTypeButtonRelease\n * - RpcAppEventTypeButtonPressRelease\n * - RpcAppEventTypeDataExchange\n *\n * Not confirming these events will result in a client-side timeout.\n *\n * @param[in,out] rpc_app pointer to the instance to be used.\n * @param[in] result whether the command was successfully handled or not (true for success).\n */\nvoid rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result);\n\n/**\n * @brief Set the error code stored in an RpcAppSystem instance.\n *\n * The error code can be retrieved by the client at any time by using the GetError request.\n * The error code value has no meaning within the subsystem, i.e. it is only passed through to the client.\n *\n * @param[in,out] rpc_app pointer to the instance to be modified.\n * @param[in] error_code arbitrary error code to be set.\n */\nvoid rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code);\n\n/**\n * @brief Set the error text stored in an RpcAppSystem instance.\n *\n * The error text can be retrieved by the client at any time by using the GetError request.\n * The text has no meaning within the subsystem, i.e. it is only passed through to the client.\n *\n * @param[in,out] rpc_app pointer to the instance to be modified.\n * @param[in] error_text Pointer to a zero-terminated string containing the error text.\n */\nvoid rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text);\n\n/**\n * @brief Reset the error code and text stored in an RpcAppSystem instance.\n *\n * Resets the error code to 0 and error text to \"\" (empty string).\n *\n * @param[in,out] rpc_app pointer to the instance to be reset.\n */\nvoid rpc_system_app_error_reset(RpcAppSystem* rpc_app);\n\n/**\n * @brief Send a byte array of arbitrary data to the client using an RpcAppSystem instance.\n *\n * @param[in,out] rpc_app pointer to the instance to be used.\n * @param[in] data pointer to the data buffer to be sent.\n * @param[in] data_size size of the data buffer, in bytes.\n */\nvoid rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/rpc/rpc_app_error_codes.h",
    "content": "#pragma once\n\n/**\n * @brief Enumeration of possible error codes for application which can be started through rpc\n */\ntypedef enum {\n    RpcAppSystemErrorCodeNone, /** There are no errors */\n    RpcAppSystemErrorCodeParseFile, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */\n    RpcAppSystemErrorCodeRegionLock, /** Requested function is blocked by regional settings */\n    RpcAppSystemErrorCodeInternalParse, /** Error in protocol parameters description, or some data in opened file are unsupported */\n} RpcAppSystemErrorCode;\n"
  },
  {
    "path": "applications/services/rpc/rpc_cli.c",
    "content": "#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <furi.h>\n#include <rpc/rpc.h>\n#include <furi_hal.h>\n#include <toolbox/pipe.h>\n\n#define TAG \"RpcCli\"\n\ntypedef struct {\n    PipeSide* pipe;\n    bool session_close_request;\n    FuriSemaphore* terminate_semaphore;\n} CliRpc;\n\n#define CLI_READ_BUFFER_SIZE 64UL\n\nstatic void rpc_cli_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) {\n    furi_assert(context);\n    furi_assert(bytes);\n    furi_assert(bytes_len > 0);\n    CliRpc* cli_rpc = context;\n    pipe_send(cli_rpc->pipe, bytes, bytes_len);\n}\n\nstatic void rpc_cli_session_close_callback(void* context) {\n    furi_assert(context);\n    CliRpc* cli_rpc = context;\n\n    cli_rpc->session_close_request = true;\n}\n\nstatic void rpc_cli_session_terminated_callback(void* context) {\n    furi_check(context);\n    CliRpc* cli_rpc = context;\n\n    furi_semaphore_release(cli_rpc->terminate_semaphore);\n}\n\nvoid rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(args);\n    furi_assert(pipe);\n    furi_assert(context);\n    Rpc* rpc = context;\n\n    uint32_t mem_before = memmgr_get_free_heap();\n    FURI_LOG_D(TAG, \"Free memory %lu\", mem_before);\n\n    furi_hal_usb_lock();\n    RpcSession* rpc_session = rpc_session_open(rpc, RpcOwnerUsb);\n    if(rpc_session == NULL) {\n        printf(\"Session start error\\r\\n\");\n        furi_hal_usb_unlock();\n        return;\n    }\n\n    CliRpc cli_rpc = {.pipe = pipe, .session_close_request = false};\n    cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0);\n    rpc_session_set_context(rpc_session, &cli_rpc);\n    rpc_session_set_send_bytes_callback(rpc_session, rpc_cli_send_bytes_callback);\n    rpc_session_set_close_callback(rpc_session, rpc_cli_session_close_callback);\n    rpc_session_set_terminated_callback(rpc_session, rpc_cli_session_terminated_callback);\n\n    uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE);\n    size_t size_received = 0;\n\n    while(1) {\n        size_t to_receive = CLAMP(pipe_bytes_available(cli_rpc.pipe), CLI_READ_BUFFER_SIZE, 1UL);\n        size_received = pipe_receive(cli_rpc.pipe, buffer, to_receive);\n        if(size_received < to_receive || cli_rpc.session_close_request) {\n            break;\n        }\n\n        if(size_received) {\n            size_t fed_bytes = rpc_session_feed(rpc_session, buffer, size_received, 3000);\n            (void)fed_bytes;\n            furi_assert(fed_bytes == size_received);\n        }\n    }\n\n    rpc_session_close(rpc_session);\n\n    furi_check(\n        furi_semaphore_acquire(cli_rpc.terminate_semaphore, FuriWaitForever) == FuriStatusOk);\n\n    furi_semaphore_free(cli_rpc.terminate_semaphore);\n\n    free(buffer);\n    furi_hal_usb_unlock();\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_debug.c",
    "content": "#include \"rpc_i.h\"\n\nstatic size_t rpc_debug_print_file_msg(\n    FuriString* str,\n    const char* prefix,\n    const PB_Storage_File* msg_file,\n    size_t msg_files_size) {\n    size_t cnt = 0;\n\n    for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) {\n        furi_string_cat_printf(\n            str,\n            \"%s[%c] size: %5lu\",\n            prefix,\n            msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f',\n            msg_file->size);\n\n        if(msg_file->name) {\n            furi_string_cat_printf(str, \" \\'%s\\'\", msg_file->name);\n        }\n\n        if(msg_file->data && msg_file->data->size) {\n            furi_string_cat_printf(\n                str,\n                \" (%d):\\'%.*s%s\\'\",\n                msg_file->data->size,\n                MIN(msg_file->data->size, 30),\n                msg_file->data->bytes,\n                msg_file->data->size > 30 ? \"...\" : \"\");\n        }\n\n        furi_string_cat_printf(str, \"\\r\\n\");\n    }\n\n    return cnt;\n}\n\nvoid rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size) {\n    FuriString* str;\n    str = furi_string_alloc();\n    furi_string_reserve(str, 100 + size * 5);\n\n    furi_string_cat_printf(str, \"\\r\\n%s DEC(%zu): {\", prefix, size);\n    for(size_t i = 0; i < size; ++i) {\n        furi_string_cat_printf(str, \"%d, \", buffer[i]);\n    }\n    furi_string_cat_printf(str, \"}\\r\\n\");\n\n    printf(\"%s\", furi_string_get_cstr(str));\n    furi_string_reset(str);\n    furi_string_reserve(str, 100 + size * 3);\n\n    furi_string_cat_printf(str, \"%s HEX(%zu): {\", prefix, size);\n    for(size_t i = 0; i < size; ++i) {\n        furi_string_cat_printf(str, \"%02X\", buffer[i]);\n    }\n    furi_string_cat_printf(str, \"}\\r\\n\\r\\n\");\n\n    printf(\"%s\", furi_string_get_cstr(str));\n    furi_string_free(str);\n}\n\nvoid rpc_debug_print_message(const PB_Main* message) {\n    FuriString* str;\n    str = furi_string_alloc();\n\n    furi_string_cat_printf(\n        str,\n        \"PB_Main: {\\r\\n\\tresult: %d cmd_id: %lu (%s)\\r\\n\",\n        message->command_status,\n        message->command_id,\n        message->has_next ? \"has_next\" : \"last\");\n    switch(message->which_content) {\n    default:\n        /* not implemented yet */\n        furi_string_cat_printf(str, \"\\tNOT_IMPLEMENTED (%d) {\\r\\n\", message->which_content);\n        break;\n    case PB_Main_stop_session_tag:\n        furi_string_cat_printf(str, \"\\tstop_session {\\r\\n\");\n        break;\n    case PB_Main_app_start_request_tag: {\n        furi_string_cat_printf(str, \"\\tapp_start {\\r\\n\");\n        const char* name = message->content.app_start_request.name;\n        const char* args = message->content.app_start_request.args;\n        if(name) {\n            furi_string_cat_printf(str, \"\\t\\tname: %s\\r\\n\", name);\n        }\n        if(args) {\n            furi_string_cat_printf(str, \"\\t\\targs: %s\\r\\n\", args);\n        }\n        break;\n    }\n    case PB_Main_app_lock_status_request_tag: {\n        furi_string_cat_printf(str, \"\\tapp_lock_status_request {\\r\\n\");\n        break;\n    }\n    case PB_Main_app_lock_status_response_tag: {\n        furi_string_cat_printf(str, \"\\tapp_lock_status_response {\\r\\n\");\n        bool lock_status = message->content.app_lock_status_response.locked;\n        furi_string_cat_printf(str, \"\\t\\tlocked: %s\\r\\n\", lock_status ? \"true\" : \"false\");\n        break;\n    }\n    case PB_Main_storage_md5sum_request_tag: {\n        furi_string_cat_printf(str, \"\\tmd5sum_request {\\r\\n\");\n        const char* path = message->content.storage_md5sum_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_storage_md5sum_response_tag: {\n        furi_string_cat_printf(str, \"\\tmd5sum_response {\\r\\n\");\n        const char* md5sum = message->content.storage_md5sum_response.md5sum;\n        if(md5sum) { //-V547\n            furi_string_cat_printf(str, \"\\t\\tmd5sum: %s\\r\\n\", md5sum);\n        }\n        break;\n    }\n    case PB_Main_system_ping_request_tag:\n        furi_string_cat_printf(str, \"\\tping_request {\\r\\n\");\n        break;\n    case PB_Main_system_ping_response_tag:\n        furi_string_cat_printf(str, \"\\tping_response {\\r\\n\");\n        break;\n    case PB_Main_system_device_info_request_tag:\n        furi_string_cat_printf(str, \"\\tdevice_info_request {\\r\\n\");\n        break;\n    case PB_Main_system_device_info_response_tag:\n        furi_string_cat_printf(str, \"\\tdevice_info_response {\\r\\n\");\n        furi_string_cat_printf(\n            str,\n            \"\\t\\t%s: %s\\r\\n\",\n            message->content.system_device_info_response.key,\n            message->content.system_device_info_response.value);\n        break;\n    case PB_Main_storage_mkdir_request_tag:\n        furi_string_cat_printf(str, \"\\tmkdir {\\r\\n\");\n        break;\n    case PB_Main_storage_delete_request_tag: {\n        furi_string_cat_printf(str, \"\\tdelete {\\r\\n\");\n        const char* path = message->content.storage_delete_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_empty_tag:\n        furi_string_cat_printf(str, \"\\tempty {\\r\\n\");\n        break;\n    case PB_Main_storage_info_request_tag: {\n        furi_string_cat_printf(str, \"\\tinfo_request {\\r\\n\");\n        const char* path = message->content.storage_info_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_storage_info_response_tag: {\n        furi_string_cat_printf(str, \"\\tinfo_response {\\r\\n\");\n        furi_string_cat_printf(\n            str, \"\\t\\ttotal_space: %llu\\r\\n\", message->content.storage_info_response.total_space);\n        furi_string_cat_printf(\n            str, \"\\t\\tfree_space: %llu\\r\\n\", message->content.storage_info_response.free_space);\n        break;\n    }\n    case PB_Main_storage_stat_request_tag: {\n        furi_string_cat_printf(str, \"\\tstat_request {\\r\\n\");\n        const char* path = message->content.storage_stat_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_storage_stat_response_tag: {\n        furi_string_cat_printf(str, \"\\tstat_response {\\r\\n\");\n        if(message->content.storage_stat_response.has_file) {\n            const PB_Storage_File* msg_file = &message->content.storage_stat_response.file;\n            rpc_debug_print_file_msg(str, \"\\t\\t\\t\", msg_file, 1);\n        }\n        break;\n    }\n    case PB_Main_storage_list_request_tag: {\n        furi_string_cat_printf(str, \"\\tlist_request {\\r\\n\");\n        const char* path = message->content.storage_list_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_storage_read_request_tag: {\n        furi_string_cat_printf(str, \"\\tread_request {\\r\\n\");\n        const char* path = message->content.storage_read_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        break;\n    }\n    case PB_Main_storage_write_request_tag: {\n        furi_string_cat_printf(str, \"\\twrite_request {\\r\\n\");\n        const char* path = message->content.storage_write_request.path;\n        if(path) {\n            furi_string_cat_printf(str, \"\\t\\tpath: %s\\r\\n\", path);\n        }\n        if(message->content.storage_write_request.has_file) {\n            const PB_Storage_File* msg_file = &message->content.storage_write_request.file;\n            rpc_debug_print_file_msg(str, \"\\t\\t\\t\", msg_file, 1);\n        }\n        break;\n    }\n    case PB_Main_storage_read_response_tag:\n        furi_string_cat_printf(str, \"\\tread_response {\\r\\n\");\n        if(message->content.storage_read_response.has_file) {\n            const PB_Storage_File* msg_file = &message->content.storage_read_response.file;\n            rpc_debug_print_file_msg(str, \"\\t\\t\\t\", msg_file, 1);\n        }\n        break;\n    case PB_Main_storage_list_response_tag: {\n        const PB_Storage_File* msg_file = message->content.storage_list_response.file;\n        size_t msg_file_count = message->content.storage_list_response.file_count;\n        furi_string_cat_printf(str, \"\\tlist_response {\\r\\n\");\n        rpc_debug_print_file_msg(str, \"\\t\\t\", msg_file, msg_file_count);\n        break;\n    }\n    case PB_Main_storage_rename_request_tag: {\n        furi_string_cat_printf(str, \"\\trename_request {\\r\\n\");\n        furi_string_cat_printf(\n            str, \"\\t\\told_path: %s\\r\\n\", message->content.storage_rename_request.old_path);\n        furi_string_cat_printf(\n            str, \"\\t\\tnew_path: %s\\r\\n\", message->content.storage_rename_request.new_path);\n        break;\n    }\n    case PB_Main_gui_start_screen_stream_request_tag:\n        furi_string_cat_printf(str, \"\\tstart_screen_stream {\\r\\n\");\n        break;\n    case PB_Main_gui_stop_screen_stream_request_tag:\n        furi_string_cat_printf(str, \"\\tstop_screen_stream {\\r\\n\");\n        break;\n    case PB_Main_gui_screen_frame_tag:\n        furi_string_cat_printf(str, \"\\tscreen_frame {\\r\\n\");\n        break;\n    case PB_Main_gui_send_input_event_request_tag:\n        furi_string_cat_printf(str, \"\\tsend_input_event {\\r\\n\");\n        furi_string_cat_printf(\n            str, \"\\t\\tkey: %d\\r\\n\", message->content.gui_send_input_event_request.key);\n        furi_string_cat_printf(\n            str, \"\\t\\type: %d\\r\\n\", message->content.gui_send_input_event_request.type);\n        break;\n    case PB_Main_gui_start_virtual_display_request_tag:\n        furi_string_cat_printf(str, \"\\tstart_virtual_display {\\r\\n\");\n        break;\n    case PB_Main_gui_stop_virtual_display_request_tag:\n        furi_string_cat_printf(str, \"\\tstop_virtual_display {\\r\\n\");\n        break;\n    }\n    furi_string_cat_printf(str, \"\\t}\\r\\n}\\r\\n\");\n    printf(\"%s\", furi_string_get_cstr(str));\n\n    furi_string_free(str);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_desktop.c",
    "content": "#include \"flipper.pb.h\"\n#include \"rpc_i.h\"\n#include <desktop/desktop.h>\n\n#define TAG \"RpcDesktop\"\n\ntypedef struct {\n    RpcSession* session;\n    Desktop* desktop;\n    FuriPubSub* status_pubsub;\n    FuriPubSubSubscription* status_subscription;\n} RpcDesktop;\n\nstatic void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_desktop_is_locked_request_tag);\n\n    FURI_LOG_D(TAG, \"IsLockedRequest\");\n    RpcDesktop* rpc_desktop = context;\n    RpcSession* session = rpc_desktop->session;\n\n    PB_CommandStatus ret = desktop_api_is_locked(rpc_desktop->desktop) ? PB_CommandStatus_OK :\n                                                                         PB_CommandStatus_ERROR;\n\n    rpc_send_and_release_empty(session, request->command_id, ret);\n}\n\nstatic void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_desktop_unlock_request_tag);\n\n    FURI_LOG_D(TAG, \"UnlockRequest\");\n    RpcDesktop* rpc_desktop = context;\n    RpcSession* session = rpc_desktop->session;\n\n    desktop_api_unlock(rpc_desktop->desktop);\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_desktop_on_desktop_pubsub(const void* message, void* context) {\n    RpcDesktop* rpc_desktop = context;\n    RpcSession* session = rpc_desktop->session;\n    const DesktopStatus* status = message;\n\n    PB_Main rpc_message = {\n        .command_id = 0,\n        .command_status = PB_CommandStatus_OK,\n        .has_next = false,\n        .which_content = PB_Main_desktop_status_tag,\n        .content.desktop_status.locked = status->locked,\n    };\n    rpc_send_and_release(session, &rpc_message);\n}\n\nstatic void rpc_desktop_on_status_subscribe_request(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_desktop_status_subscribe_request_tag);\n\n    FURI_LOG_D(TAG, \"StatusSubscribeRequest\");\n    RpcDesktop* rpc_desktop = context;\n    RpcSession* session = rpc_desktop->session;\n\n    if(rpc_desktop->status_subscription) {\n        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR);\n    } else {\n        rpc_desktop->status_subscription = furi_pubsub_subscribe(\n            rpc_desktop->status_pubsub, rpc_desktop_on_desktop_pubsub, rpc_desktop);\n        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n    }\n}\n\nstatic void rpc_desktop_on_status_unsubscribe_request(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_desktop_status_unsubscribe_request_tag);\n\n    FURI_LOG_D(TAG, \"StatusUnsubscribeRequest\");\n    RpcDesktop* rpc_desktop = context;\n    RpcSession* session = rpc_desktop->session;\n\n    if(rpc_desktop->status_subscription) {\n        furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription);\n        rpc_desktop->status_subscription = NULL;\n        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n    } else {\n        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR);\n    }\n}\n\nvoid* rpc_desktop_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop));\n    rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP);\n    rpc_desktop->status_pubsub = desktop_api_get_status_pubsub(rpc_desktop->desktop);\n    rpc_desktop->session = session;\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = rpc_desktop,\n    };\n\n    rpc_handler.message_handler = rpc_desktop_on_is_locked_request;\n    rpc_add_handler(session, PB_Main_desktop_is_locked_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_desktop_on_unlock_request;\n    rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_desktop_on_status_subscribe_request;\n    rpc_add_handler(session, PB_Main_desktop_status_subscribe_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_desktop_on_status_unsubscribe_request;\n    rpc_add_handler(session, PB_Main_desktop_status_unsubscribe_request_tag, &rpc_handler);\n\n    return rpc_desktop;\n}\n\nvoid rpc_desktop_free(void* context) {\n    furi_assert(context);\n    RpcDesktop* rpc_desktop = context;\n\n    if(rpc_desktop->status_subscription) {\n        furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription);\n    }\n\n    furi_assert(rpc_desktop->desktop);\n    furi_record_close(RECORD_DESKTOP);\n\n    rpc_desktop->session = NULL;\n    free(rpc_desktop);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_gpio.c",
    "content": "#include \"flipper.pb.h\"\n#include \"rpc_i.h\"\n#include \"gpio.pb.h\"\n#include <furi_hal_gpio.h>\n#include <furi_hal_power.h>\n#include <furi_hal_resources.h>\n#include <power/power_service/power.h>\n\nstatic const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) {\n    switch(rpc_pin) {\n    case PB_Gpio_GpioPin_PC0:\n        return &gpio_ext_pc0;\n    case PB_Gpio_GpioPin_PC1:\n        return &gpio_ext_pc1;\n    case PB_Gpio_GpioPin_PC3:\n        return &gpio_ext_pc3;\n    case PB_Gpio_GpioPin_PB2:\n        return &gpio_ext_pb2;\n    case PB_Gpio_GpioPin_PB3:\n        return &gpio_ext_pb3;\n    case PB_Gpio_GpioPin_PA4:\n        return &gpio_ext_pa4;\n    case PB_Gpio_GpioPin_PA6:\n        return &gpio_ext_pa6;\n    case PB_Gpio_GpioPin_PA7:\n        return &gpio_ext_pa7;\n    }\n\n    __builtin_unreachable();\n}\n\nstatic GpioMode rpc_mode_to_hal_mode(PB_Gpio_GpioPinMode rpc_mode) {\n    switch(rpc_mode) {\n    case PB_Gpio_GpioPinMode_OUTPUT:\n        return GpioModeOutputPushPull;\n    case PB_Gpio_GpioPinMode_INPUT:\n        return GpioModeInput;\n    }\n\n    __builtin_unreachable();\n}\n\nstatic GpioPull rpc_pull_mode_to_hall_pull_mode(PB_Gpio_GpioInputPull pull_mode) {\n    switch(pull_mode) {\n    case PB_Gpio_GpioInputPull_UP:\n        return GpioPullUp;\n    case PB_Gpio_GpioInputPull_DOWN:\n        return GpioPullDown;\n    case PB_Gpio_GpioInputPull_NO:\n        return GpioPullNo;\n    }\n\n    __builtin_unreachable();\n}\n\nstatic void rpc_system_gpio_set_pin_mode(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_set_pin_mode_tag);\n\n    RpcSession* session = context;\n\n    PB_Gpio_SetPinMode cmd = request->content.gpio_set_pin_mode;\n    const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin);\n    GpioMode mode = rpc_mode_to_hal_mode(cmd.mode);\n\n    furi_hal_gpio_init_simple(pin, mode);\n    if(mode == GpioModeOutputPushPull) {\n        furi_hal_gpio_write(pin, false);\n    }\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_gpio_write_pin(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_write_pin_tag);\n\n    RpcSession* session = context;\n\n    PB_Gpio_WritePin cmd = request->content.gpio_write_pin;\n    const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin);\n    uint8_t value = !!(cmd.value);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n\n    if(LL_GPIO_MODE_OUTPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) {\n        response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT;\n    } else {\n        response->command_status = PB_CommandStatus_OK;\n        furi_hal_gpio_write(pin, value);\n    }\n\n    rpc_send_and_release(session, response);\n\n    free(response);\n}\n\nstatic void rpc_system_gpio_read_pin(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_read_pin_tag);\n\n    RpcSession* session = context;\n\n    PB_Gpio_ReadPin cmd = request->content.gpio_read_pin;\n    const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n\n    if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) {\n        response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT;\n    } else {\n        response->command_status = PB_CommandStatus_OK;\n        response->which_content = PB_Main_gpio_read_pin_response_tag;\n        response->content.gpio_read_pin_response.value = !!furi_hal_gpio_read(pin);\n    }\n\n    rpc_send_and_release(session, response);\n\n    free(response);\n}\n\nvoid rpc_system_gpio_get_pin_mode(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_get_pin_mode_tag);\n\n    RpcSession* session = context;\n\n    PB_Gpio_GetPinMode cmd = request->content.gpio_get_pin_mode;\n    const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n\n    uint32_t raw_pin_mode = LL_GPIO_GetPinMode(pin->port, pin->pin);\n\n    PB_Gpio_GpioPinMode pin_mode;\n    if(LL_GPIO_MODE_INPUT == raw_pin_mode) {\n        pin_mode = PB_Gpio_GpioPinMode_INPUT;\n        response->command_status = PB_CommandStatus_OK;\n    } else if(LL_GPIO_MODE_OUTPUT == raw_pin_mode) {\n        pin_mode = PB_Gpio_GpioPinMode_OUTPUT;\n        response->command_status = PB_CommandStatus_OK;\n    } else {\n        pin_mode = PB_Gpio_GpioPinMode_INPUT;\n        response->command_status = PB_CommandStatus_ERROR_GPIO_UNKNOWN_PIN_MODE;\n    }\n\n    response->which_content = PB_Main_gpio_get_pin_mode_response_tag;\n    response->content.gpio_get_pin_mode_response.mode = pin_mode;\n\n    rpc_send_and_release(session, response);\n\n    free(response);\n}\n\nvoid rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_set_input_pull_tag);\n\n    RpcSession* session = context;\n\n    PB_Gpio_SetInputPull cmd = request->content.gpio_set_input_pull;\n    const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin);\n    const GpioPull pull_mode = rpc_pull_mode_to_hall_pull_mode(cmd.pull_mode);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n\n    PB_CommandStatus status;\n    if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) {\n        status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT;\n    } else {\n        status = PB_CommandStatus_OK;\n        furi_hal_gpio_init(pin, GpioModeInput, pull_mode, GpioSpeedLow);\n    }\n\n    rpc_send_and_release_empty(session, request->command_id, status);\n\n    free(response);\n}\n\nvoid rpc_system_gpio_get_otg_mode(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_get_otg_mode_tag);\n\n    RpcSession* session = context;\n\n    const bool otg_enabled = furi_hal_power_is_otg_enabled();\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_gpio_get_otg_mode_response_tag;\n    response->content.gpio_get_otg_mode_response.mode = otg_enabled ? PB_Gpio_GpioOtgMode_ON :\n                                                                      PB_Gpio_GpioOtgMode_OFF;\n\n    rpc_send_and_release(session, response);\n\n    free(response);\n}\n\nvoid rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_gpio_set_otg_mode_tag);\n\n    RpcSession* session = context;\n\n    const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode;\n\n    Power* power = furi_record_open(RECORD_POWER);\n\n    if(mode == PB_Gpio_GpioOtgMode_OFF) {\n        power_enable_otg(power, false);\n    } else {\n        power_enable_otg(power, true);\n    }\n\n    furi_record_close(RECORD_POWER);\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nvoid* rpc_system_gpio_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = session,\n    };\n\n    rpc_handler.message_handler = rpc_system_gpio_set_pin_mode;\n    rpc_add_handler(session, PB_Main_gpio_set_pin_mode_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_write_pin;\n    rpc_add_handler(session, PB_Main_gpio_write_pin_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_read_pin;\n    rpc_add_handler(session, PB_Main_gpio_read_pin_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_get_pin_mode;\n    rpc_add_handler(session, PB_Main_gpio_get_pin_mode_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_set_input_pull;\n    rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_get_otg_mode;\n    rpc_add_handler(session, PB_Main_gpio_get_otg_mode_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gpio_set_otg_mode;\n    rpc_add_handler(session, PB_Main_gpio_set_otg_mode_tag, &rpc_handler);\n\n    return NULL;\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_gui.c",
    "content": "#include \"rpc_i.h\"\n#include <gui/gui_i.h>\n#include <assets_icons.h>\n\n#include <flipper.pb.h>\n#include <gui.pb.h>\n\n// Contract assertion\n_Static_assert(InputKeyMAX == 6, \"InputKeyMAX\");\n_Static_assert(InputTypeMAX == 5, \"InputTypeMAX\");\n\n_Static_assert(InputKeyUp == (int32_t)PB_Gui_InputKey_UP, \"InputKeyUp != PB_Gui_InputKey_UP\");\n_Static_assert(\n    InputKeyDown == (int32_t)PB_Gui_InputKey_DOWN,\n    \"InputKeyDown != PB_Gui_InputKey_DOWN\");\n_Static_assert(\n    InputKeyRight == (int32_t)PB_Gui_InputKey_RIGHT,\n    \"InputKeyRight != PB_Gui_InputKey_RIGHT\");\n_Static_assert(\n    InputKeyLeft == (int32_t)PB_Gui_InputKey_LEFT,\n    \"InputKeyLeft != PB_Gui_InputKey_LEFT\");\n_Static_assert(InputKeyOk == (int32_t)PB_Gui_InputKey_OK, \"InputKeyOk != PB_Gui_InputKey_OK\");\n_Static_assert(\n    InputKeyBack == (int32_t)PB_Gui_InputKey_BACK,\n    \"InputKeyBack != PB_Gui_InputKey_BACK\");\n\n_Static_assert(\n    InputTypePress == (int32_t)PB_Gui_InputType_PRESS,\n    \"InputTypePress != PB_Gui_InputType_PRESS\");\n_Static_assert(\n    InputTypeRelease == (int32_t)PB_Gui_InputType_RELEASE,\n    \"InputTypeRelease != PB_Gui_InputType_RELEASE\");\n_Static_assert(\n    InputTypeShort == (int32_t)PB_Gui_InputType_SHORT,\n    \"InputTypeShort != PB_Gui_InputType_SHORT\");\n_Static_assert(\n    InputTypeLong == (int32_t)PB_Gui_InputType_LONG,\n    \"InputTypeLong != PB_Gui_InputType_LONG\");\n_Static_assert(\n    InputTypeRepeat == (int32_t)PB_Gui_InputType_REPEAT,\n    \"InputTypeRepeat != PB_Gui_InputType_REPEAT\");\n\n#define TAG \"RpcGui\"\n\ntypedef enum {\n    RpcGuiWorkerFlagTransmit = (1 << 0),\n    RpcGuiWorkerFlagExit = (1 << 1),\n} RpcGuiWorkerFlag;\n\n#define RpcGuiWorkerFlagAny (RpcGuiWorkerFlagTransmit | RpcGuiWorkerFlagExit)\n\n#define RPC_GUI_INPUT_RESET (0u)\n\ntypedef struct {\n    RpcSession* session;\n    Gui* gui;\n    const Icon* icon;\n    FuriPubSub* input_events;\n\n    // Receive part\n    ViewPort* virtual_display_view_port;\n    uint8_t* virtual_display_buffer;\n\n    // Transmit\n    PB_Main* transmit_frame;\n    FuriThread* transmit_thread;\n\n    bool virtual_display_not_empty;\n    bool is_streaming;\n\n    uint32_t input_key_counter[InputKeyMAX];\n    uint32_t input_counter;\n\n    ViewPort* rpc_session_active_viewport;\n} RpcGuiSystem;\n\nstatic const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = {\n    [CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL,\n    [CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP,\n    [CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL,\n    [CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP,\n};\n\nstatic void rpc_system_gui_screen_stream_frame_callback(\n    uint8_t* data,\n    size_t size,\n    CanvasOrientation orientation,\n    void* context) {\n    furi_assert(data);\n    furi_assert(context);\n\n    RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;\n    uint8_t* buffer = rpc_gui->transmit_frame->content.gui_screen_frame.data->bytes;\n\n    furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);\n\n    memcpy(buffer, data, size);\n    rpc_gui->transmit_frame->content.gui_screen_frame.orientation =\n        rpc_system_gui_screen_orientation_map[orientation];\n\n    furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit);\n}\n\nstatic int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) {\n    furi_assert(context);\n\n    RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;\n\n    uint32_t transmit_time = 0;\n    while(true) {\n        uint32_t flags =\n            furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);\n\n        if(flags & RpcGuiWorkerFlagTransmit) {\n            transmit_time = furi_get_tick();\n            rpc_send(rpc_gui->session, rpc_gui->transmit_frame);\n            transmit_time = furi_get_tick() - transmit_time;\n\n            // Guaranteed bandwidth reserve\n            uint32_t extra_delay = transmit_time / 20;\n            if(extra_delay > 500) extra_delay = 500;\n            if(extra_delay) furi_delay_tick(extra_delay);\n        }\n\n        if(flags & RpcGuiWorkerFlagExit) {\n            break;\n        }\n    }\n\n    return 0;\n}\n\nstatic void rpc_system_gui_start_screen_stream_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"StartScreenStream\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    if(rpc_gui->is_streaming) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED);\n    } else {\n        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n\n        rpc_gui->is_streaming = true;\n        size_t framebuffer_size = gui_get_framebuffer_size(rpc_gui->gui);\n        // Reusable Frame\n        rpc_gui->transmit_frame = malloc(sizeof(PB_Main));\n        rpc_gui->transmit_frame->which_content = PB_Main_gui_screen_frame_tag;\n        rpc_gui->transmit_frame->command_status = PB_CommandStatus_OK;\n        rpc_gui->transmit_frame->content.gui_screen_frame.data =\n            malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size));\n        rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size;\n        // Transmission thread for async TX\n        rpc_gui->transmit_thread = furi_thread_alloc_ex(\n            \"GuiRpcWorker\", 1024, rpc_system_gui_screen_stream_frame_transmit_thread, rpc_gui);\n        furi_thread_start(rpc_gui->transmit_thread);\n        // GUI framebuffer callback\n        gui_add_framebuffer_callback(\n            rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);\n    }\n}\n\nstatic void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"StopScreenStream\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    if(rpc_gui->is_streaming) {\n        rpc_gui->is_streaming = false;\n        // Remove GUI framebuffer callback\n        gui_remove_framebuffer_callback(\n            rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);\n        // Stop and release worker thread\n        furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit);\n        furi_thread_join(rpc_gui->transmit_thread);\n        furi_thread_free(rpc_gui->transmit_thread);\n        // Release frame\n        pb_release(&PB_Main_msg, rpc_gui->transmit_frame);\n        free(rpc_gui->transmit_frame);\n        rpc_gui->transmit_frame = NULL;\n    }\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void\n    rpc_system_gui_send_input_event_request_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_gui_send_input_event_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"SendInputEvent\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    bool is_valid = (request->content.gui_send_input_event_request.key < (int32_t)InputKeyMAX) &&\n                    (request->content.gui_send_input_event_request.type < (int32_t)InputTypeMAX);\n\n    if(!is_valid) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n        return;\n    }\n\n    InputEvent event = {\n        .key = (int32_t)request->content.gui_send_input_event_request.key,\n        .type = (int32_t)request->content.gui_send_input_event_request.type,\n    };\n\n    // Event sequence shenanigans\n    event.sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE;\n    if(event.type == InputTypePress) {\n        rpc_gui->input_counter++;\n        if(rpc_gui->input_counter == RPC_GUI_INPUT_RESET) rpc_gui->input_counter++;\n        rpc_gui->input_key_counter[event.key] = rpc_gui->input_counter;\n    }\n    if(rpc_gui->input_key_counter[event.key] == RPC_GUI_INPUT_RESET) {\n        FURI_LOG_W(TAG, \"Out of sequence input event: key %d, type %d,\", event.key, event.type);\n    }\n    event.sequence_counter = rpc_gui->input_key_counter[event.key];\n    if(event.type == InputTypeRelease) {\n        rpc_gui->input_key_counter[event.key] = RPC_GUI_INPUT_RESET;\n    }\n\n    // Submit event\n    furi_pubsub_publish(rpc_gui->input_events, &event);\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_gui_virtual_display_render_callback(Canvas* canvas, void* context) {\n    furi_assert(canvas);\n    furi_assert(context);\n\n    RpcGuiSystem* rpc_gui = context;\n\n    if(!rpc_gui->virtual_display_not_empty) {\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, \"Virtual Display\");\n        canvas_draw_str_aligned(canvas, 64, 36, AlignCenter, AlignCenter, \"Waiting for frames...\");\n        return;\n    }\n\n    canvas_draw_xbm(canvas, 0, 0, canvas->width, canvas->height, rpc_gui->virtual_display_buffer);\n}\n\nstatic void rpc_system_gui_virtual_display_input_callback(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(event->key < InputKeyMAX);\n    furi_assert(event->type < InputTypeMAX);\n    furi_assert(context);\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n\n    FURI_LOG_D(TAG, \"VirtualDisplay: SendInputEvent\");\n\n    PB_Main rpc_message = {\n        .command_id = 0,\n        .command_status = PB_CommandStatus_OK,\n        .has_next = false,\n        .which_content = PB_Main_gui_send_input_event_request_tag,\n        .content.gui_send_input_event_request.key = (int32_t)event->key,\n        .content.gui_send_input_event_request.type = (int32_t)event->type,\n    };\n\n    rpc_send_and_release(session, &rpc_message);\n}\n\nstatic void rpc_system_gui_start_virtual_display_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"StartVirtualDisplay\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    if(rpc_gui->virtual_display_view_port) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED);\n        return;\n    }\n\n    // TODO FL-3511: consider refactoring\n    // Using display framebuffer size as an XBM buffer size is like comparing apples and oranges\n    // Glad they both are 1024 for now\n    size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas);\n    rpc_gui->virtual_display_buffer = malloc(buffer_size);\n\n    if(request->content.gui_start_virtual_display_request.has_first_frame) {\n        size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas);\n        memcpy(\n            rpc_gui->virtual_display_buffer,\n            request->content.gui_start_virtual_display_request.first_frame.data->bytes,\n            buffer_size);\n        rpc_gui->virtual_display_not_empty = true;\n    }\n\n    rpc_gui->virtual_display_view_port = view_port_alloc();\n    view_port_draw_callback_set(\n        rpc_gui->virtual_display_view_port,\n        rpc_system_gui_virtual_display_render_callback,\n        rpc_gui);\n\n    if(request->content.gui_start_virtual_display_request.send_input) {\n        FURI_LOG_D(TAG, \"VirtualDisplay: input forwarding requested\");\n        view_port_input_callback_set(\n            rpc_gui->virtual_display_view_port,\n            rpc_system_gui_virtual_display_input_callback,\n            rpc_gui);\n    }\n\n    gui_add_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port, GuiLayerFullscreen);\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_gui_stop_virtual_display_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"StopVirtualDisplay\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    if(!rpc_gui->virtual_display_view_port) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_NOT_STARTED);\n        return;\n    }\n\n    gui_remove_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port);\n    view_port_free(rpc_gui->virtual_display_view_port);\n    free(rpc_gui->virtual_display_buffer);\n    rpc_gui->virtual_display_view_port = NULL;\n    rpc_gui->virtual_display_not_empty = false;\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"VirtualDisplayFrame\");\n\n    RpcGuiSystem* rpc_gui = context;\n    RpcSession* session = rpc_gui->session;\n    furi_assert(session);\n\n    if(!rpc_gui->virtual_display_view_port) {\n        FURI_LOG_W(TAG, \"Virtual display is not started, ignoring incoming frame packet\");\n        return;\n    }\n\n    size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas);\n    memcpy(\n        rpc_gui->virtual_display_buffer,\n        request->content.gui_screen_frame.data->bytes,\n        buffer_size);\n    rpc_gui->virtual_display_not_empty = true;\n    view_port_update(rpc_gui->virtual_display_view_port);\n\n    (void)session;\n}\n\nstatic const Icon* rpc_system_gui_get_owner_icon(RpcOwner owner) {\n    switch(owner) {\n    case RpcOwnerUart:\n        return &I_Exp_module_connected_12x8;\n    default:\n        return &I_Rpc_active_7x8;\n    }\n}\n\nstatic void rpc_active_session_icon_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(canvas);\n    RpcGuiSystem* rpc_gui = context;\n    canvas_draw_icon(canvas, 0, 0, rpc_gui->icon);\n}\n\nvoid* rpc_system_gui_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcGuiSystem* rpc_gui = malloc(sizeof(RpcGuiSystem));\n    rpc_gui->gui = furi_record_open(RECORD_GUI);\n    rpc_gui->input_events = furi_record_open(RECORD_INPUT_EVENTS);\n    rpc_gui->session = session;\n\n    // Active session icon\n    const RpcOwner owner = rpc_session_get_owner(rpc_gui->session);\n    if(owner != RpcOwnerBle) {\n        rpc_gui->icon = rpc_system_gui_get_owner_icon(owner);\n        rpc_gui->rpc_session_active_viewport = view_port_alloc();\n        view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(rpc_gui->icon));\n        view_port_draw_callback_set(\n            rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, rpc_gui);\n        gui_add_view_port(\n            rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft);\n    }\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = rpc_gui,\n    };\n\n    rpc_handler.message_handler = rpc_system_gui_start_screen_stream_process;\n    rpc_add_handler(session, PB_Main_gui_start_screen_stream_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gui_stop_screen_stream_process;\n    rpc_add_handler(session, PB_Main_gui_stop_screen_stream_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gui_send_input_event_request_process;\n    rpc_add_handler(session, PB_Main_gui_send_input_event_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gui_start_virtual_display_process;\n    rpc_add_handler(session, PB_Main_gui_start_virtual_display_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gui_stop_virtual_display_process;\n    rpc_add_handler(session, PB_Main_gui_stop_virtual_display_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_gui_virtual_display_frame_process;\n    rpc_add_handler(session, PB_Main_gui_screen_frame_tag, &rpc_handler);\n\n    return rpc_gui;\n}\n\nvoid rpc_system_gui_free(void* context) {\n    furi_assert(context);\n    RpcGuiSystem* rpc_gui = context;\n    furi_assert(rpc_gui->gui);\n\n    // Release ongoing inputs to avoid lockup\n    for(InputKey key = 0; key < InputKeyMAX; key++) {\n        if(rpc_gui->input_key_counter[key] != RPC_GUI_INPUT_RESET) {\n            InputEvent event = {\n                .key = key,\n                .type = InputTypeRelease,\n                .sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE,\n                .sequence_counter = rpc_gui->input_key_counter[key],\n            };\n            furi_pubsub_publish(rpc_gui->input_events, &event);\n        }\n    }\n\n    if(rpc_gui->virtual_display_view_port) {\n        gui_remove_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port);\n        view_port_free(rpc_gui->virtual_display_view_port);\n        free(rpc_gui->virtual_display_buffer);\n        rpc_gui->virtual_display_view_port = NULL;\n        rpc_gui->virtual_display_not_empty = false;\n    }\n\n    if(rpc_gui->rpc_session_active_viewport) {\n        gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport);\n        view_port_free(rpc_gui->rpc_session_active_viewport);\n    }\n\n    if(rpc_gui->is_streaming) {\n        rpc_gui->is_streaming = false;\n        // Remove GUI framebuffer callback\n        gui_remove_framebuffer_callback(\n            rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);\n        // Stop and release worker thread\n        furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit);\n        furi_thread_join(rpc_gui->transmit_thread);\n        furi_thread_free(rpc_gui->transmit_thread);\n        // Release frame\n        pb_release(&PB_Main_msg, rpc_gui->transmit_frame);\n        free(rpc_gui->transmit_frame);\n        rpc_gui->transmit_frame = NULL;\n    }\n    furi_record_close(RECORD_INPUT_EVENTS);\n    furi_record_close(RECORD_GUI);\n    free(rpc_gui);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_i.h",
    "content": "#pragma once\n#include \"rpc.h\"\n#include <storage/filesystem_api_defines.h>\n#include <pb.h>\n#include <pb_decode.h>\n#include <pb_encode.h>\n#include <flipper.pb.h>\n#include <toolbox/pipe.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void* (*RpcSystemAlloc)(RpcSession* session);\ntypedef void (*RpcSystemFree)(void* context);\ntypedef void (*PBMessageHandler)(const PB_Main* msg_request, void* context);\n\ntypedef struct {\n    bool (*decode_submessage)(pb_istream_t* stream, const pb_field_t* field, void** arg);\n    PBMessageHandler message_handler;\n    void* context;\n} RpcHandler;\n\nvoid rpc_send(RpcSession* session, PB_Main* main_message);\n\nvoid rpc_send_and_release(RpcSession* session, PB_Main* main_message);\n\nvoid rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status);\n\nvoid rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler);\n\nvoid* rpc_system_system_alloc(RpcSession* session);\nvoid* rpc_system_storage_alloc(RpcSession* session);\nvoid rpc_system_storage_free(void* ctx);\nvoid* rpc_system_app_alloc(RpcSession* session);\nvoid rpc_system_app_free(void* ctx);\nvoid* rpc_system_gui_alloc(RpcSession* session);\nvoid rpc_system_gui_free(void* ctx);\nvoid* rpc_system_gpio_alloc(RpcSession* session);\nvoid rpc_system_gpio_free(void* ctx);\nvoid* rpc_system_property_alloc(RpcSession* session);\n\nvoid* rpc_desktop_alloc(RpcSession* session);\nvoid rpc_desktop_free(void* ctx);\n\nvoid rpc_debug_print_message(const PB_Main* message);\nvoid rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size);\n\nvoid rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context);\n\nPB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/rpc/rpc_property.c",
    "content": "#include <flipper.pb.h>\n#include <furi_hal.h>\n#include <furi_hal_info.h>\n#include <furi_hal_power.h>\n#include <core/core_defines.h>\n\n#include \"rpc_i.h\"\n\n#define TAG \"RpcProperty\"\n\n#define PROPERTY_CATEGORY_DEVICE_INFO \"devinfo\"\n#define PROPERTY_CATEGORY_POWER_INFO  \"pwrinfo\"\n#define PROPERTY_CATEGORY_POWER_DEBUG \"pwrdebug\"\n\ntypedef struct {\n    RpcSession* session;\n    PB_Main* response;\n    FuriString* subkey;\n} RpcPropertyContext;\n\nstatic void\n    rpc_system_property_get_callback(const char* key, const char* value, bool last, void* context) {\n    furi_assert(key);\n    furi_assert(value);\n    furi_assert(context);\n    furi_assert(key);\n    furi_assert(value);\n\n    RpcPropertyContext* ctx = context;\n    RpcSession* session = ctx->session;\n    PB_Main* response = ctx->response;\n\n    if(!strncmp(key, furi_string_get_cstr(ctx->subkey), furi_string_size(ctx->subkey))) {\n        response->content.system_device_info_response.key = strdup(key);\n        response->content.system_device_info_response.value = strdup(value);\n        rpc_send_and_release(session, response);\n    }\n\n    if(last) {\n        rpc_send_and_release_empty(session, response->command_id, PB_CommandStatus_OK);\n    }\n}\n\nstatic void rpc_system_property_get_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_property_get_request_tag);\n\n    FURI_LOG_D(TAG, \"GetProperty\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    FuriString* topkey = furi_string_alloc();\n    FuriString* subkey = furi_string_alloc_set_str(request->content.property_get_request.key);\n\n    const size_t sep_idx = furi_string_search_char(subkey, '.');\n\n    if(sep_idx == FURI_STRING_FAILURE) {\n        furi_string_swap(topkey, subkey);\n    } else {\n        furi_string_set_n(topkey, subkey, 0, sep_idx);\n        furi_string_right(subkey, sep_idx + 1);\n    }\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n\n    response->command_id = request->command_id;\n    response->command_status = PB_CommandStatus_OK;\n    response->has_next = true;\n    response->which_content = PB_Main_property_get_response_tag;\n\n    RpcPropertyContext property_context = {\n        .session = session,\n        .response = response,\n        .subkey = subkey,\n    };\n\n    if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_DEVICE_INFO)) {\n        furi_hal_info_get(rpc_system_property_get_callback, '.', &property_context);\n    } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_INFO)) {\n        furi_hal_power_info_get(rpc_system_property_get_callback, '.', &property_context);\n    } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_DEBUG)) {\n        furi_hal_power_debug_get(rpc_system_property_get_callback, &property_context);\n    } else {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n    }\n\n    furi_string_free(subkey);\n    furi_string_free(topkey);\n\n    free(response);\n}\n\nvoid* rpc_system_property_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = session,\n    };\n\n    rpc_handler.message_handler = rpc_system_property_get_process;\n    rpc_add_handler(session, PB_Main_property_get_request_tag, &rpc_handler);\n    return NULL;\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_storage.c",
    "content": "#include <core/common_defines.h>\n#include <core/memmgr.h>\n#include <core/record.h>\n#include <rpc/rpc.h>\n#include <rpc/rpc_i.h>\n#include <storage/filesystem_api_defines.h>\n#include <storage/storage.h>\n#include <lib/toolbox/md5_calc.h>\n#include <lib/toolbox/path.h>\n#include <update_util/int_backup.h>\n#include <toolbox/tar/tar_archive.h>\n\n#include <pb_decode.h>\n#include <storage.pb.h>\n#include <flipper.pb.h>\n\n#define TAG \"RpcStorage\"\n\n#define MAX_NAME_LENGTH 255\n\nstatic const size_t MAX_DATA_SIZE = 512;\n\ntypedef enum {\n    RpcStorageStateIdle = 0,\n    RpcStorageStateWriting,\n} RpcStorageState;\n\ntypedef struct {\n    RpcSession* session;\n    Storage* api;\n    File* file;\n    RpcStorageState state;\n    uint32_t current_command_id;\n} RpcStorageSystem;\n\nstatic void rpc_system_storage_reset_state(\n    RpcStorageSystem* rpc_storage,\n    RpcSession* session,\n    bool send_error) {\n    furi_assert(rpc_storage);\n    furi_assert(session);\n\n    if(rpc_storage->state != RpcStorageStateIdle) {\n        if(send_error) {\n            rpc_send_and_release_empty(\n                session,\n                rpc_storage->current_command_id,\n                PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED);\n        }\n\n        if(rpc_storage->state == RpcStorageStateWriting) {\n            storage_file_close(rpc_storage->file);\n            storage_file_free(rpc_storage->file);\n        }\n\n        rpc_storage->state = RpcStorageStateIdle;\n    }\n}\n\nPB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) {\n    PB_CommandStatus pb_error;\n    switch(fs_error) {\n    case FSE_OK:\n        pb_error = PB_CommandStatus_OK;\n        break;\n    case FSE_INVALID_NAME:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;\n        break;\n    case FSE_INVALID_PARAMETER:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;\n        break;\n    case FSE_INTERNAL:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_INTERNAL;\n        break;\n    case FSE_ALREADY_OPEN:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN;\n        break;\n    case FSE_DENIED:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_DENIED;\n        break;\n    case FSE_EXIST:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_EXIST;\n        break;\n    case FSE_NOT_EXIST:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_EXIST;\n        break;\n    case FSE_NOT_READY:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_READY;\n        break;\n    case FSE_NOT_IMPLEMENTED:\n        pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED;\n        break;\n    default:\n        pb_error = PB_CommandStatus_ERROR;\n        break;\n    }\n\n    return pb_error;\n}\n\nstatic PB_CommandStatus rpc_system_storage_get_file_error(File* file) {\n    return rpc_system_storage_get_error(storage_file_get_error(file));\n}\n\nstatic void rpc_system_storage_info_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_info_request_tag);\n\n    FURI_LOG_D(TAG, \"Info\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n\n    FS_Error error = storage_common_fs_info(\n        rpc_storage->api,\n        request->content.storage_info_request.path,\n        &response->content.storage_info_response.total_space,\n        &response->content.storage_info_response.free_space);\n\n    response->command_status = rpc_system_storage_get_error(error);\n    if(error == FSE_OK) {\n        response->which_content = PB_Main_storage_info_response_tag;\n    } else {\n        response->which_content = PB_Main_empty_tag;\n    }\n\n    rpc_send_and_release(session, response);\n    free(response);\n}\n\nstatic void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_timestamp_request_tag);\n\n    FURI_LOG_D(TAG, \"Timestamp\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n\n    const char* path = request->content.storage_timestamp_request.path;\n    uint32_t timestamp = 0;\n    FS_Error error = storage_common_timestamp(rpc_storage->api, path, &timestamp);\n\n    response->command_status = rpc_system_storage_get_error(error);\n    response->which_content = PB_Main_empty_tag;\n\n    if(error == FSE_OK) {\n        response->which_content = PB_Main_storage_timestamp_response_tag;\n        response->content.storage_timestamp_response.timestamp = timestamp;\n    }\n\n    rpc_send_and_release(session, response);\n    free(response);\n}\n\nstatic void rpc_system_storage_stat_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_stat_request_tag);\n\n    FURI_LOG_D(TAG, \"Stat\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n\n    const char* path = request->content.storage_stat_request.path;\n    FileInfo fileinfo;\n    FS_Error error = storage_common_stat(rpc_storage->api, path, &fileinfo);\n\n    response->command_status = rpc_system_storage_get_error(error);\n    response->which_content = PB_Main_empty_tag;\n\n    if(error == FSE_OK) {\n        response->which_content = PB_Main_storage_stat_response_tag;\n        response->content.storage_stat_response.has_file = true;\n        response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ?\n                                                                PB_Storage_File_FileType_DIR :\n                                                                PB_Storage_File_FileType_FILE;\n        response->content.storage_stat_response.file.size = fileinfo.size;\n    }\n\n    rpc_send_and_release(session, response);\n    free(response);\n}\n\nstatic void rpc_system_storage_list_root(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    const char* hard_coded_dirs[] = {\"any\", \"int\", \"ext\"};\n\n    PB_Main response = {\n        .has_next = false,\n        .command_id = request->command_id,\n        .command_status = PB_CommandStatus_OK,\n        .which_content = PB_Main_storage_list_response_tag,\n    };\n    furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file));\n\n    for(uint32_t i = 0; i < COUNT_OF(hard_coded_dirs); ++i) {\n        ++response.content.storage_list_response.file_count;\n        response.content.storage_list_response.file[i].data = NULL;\n        response.content.storage_list_response.file[i].size = 0;\n        response.content.storage_list_response.file[i].type = PB_Storage_File_FileType_DIR;\n        response.content.storage_list_response.file[i].name = strdup(hard_coded_dirs[i]);\n    }\n\n    rpc_send_and_release(session, &response);\n}\n\nstatic bool rpc_system_storage_list_filter(\n    const PB_Storage_ListRequest* request,\n    const FileInfo* fileinfo,\n    const char* name) {\n    bool result = false;\n\n    do {\n        if(!path_contains_only_ascii(name)) break;\n        if(request->filter_max_size) {\n            if(fileinfo->size > request->filter_max_size) break;\n        }\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nstatic void rpc_system_storage_list_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_list_request_tag);\n\n    FURI_LOG_D(TAG, \"List\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    const PB_Storage_ListRequest* list_request = &request->content.storage_list_request;\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    if(!strcmp(list_request->path, \"/\")) {\n        rpc_system_storage_list_root(request, context);\n        return;\n    }\n\n    File* dir = storage_file_alloc(rpc_storage->api);\n\n    PB_Main response = {\n        .command_id = request->command_id,\n        .has_next = false,\n        .which_content = PB_Main_storage_list_response_tag,\n        .command_status = PB_CommandStatus_OK,\n    };\n    PB_Storage_ListResponse* list = &response.content.storage_list_response;\n\n    bool include_md5 = list_request->include_md5;\n    FuriString* md5 = furi_string_alloc();\n    FuriString* md5_path = furi_string_alloc();\n    File* file = storage_file_alloc(rpc_storage->api);\n\n    bool finish = false;\n    int i = 0;\n\n    if(!storage_dir_open(dir, list_request->path)) {\n        response.command_status = rpc_system_storage_get_file_error(dir);\n        response.which_content = PB_Main_empty_tag;\n        finish = true;\n    }\n\n    while(!finish) { //-V1044\n        FileInfo fileinfo;\n        char* name = malloc(MAX_NAME_LENGTH + 1);\n        if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {\n            if(rpc_system_storage_list_filter(list_request, &fileinfo, name)) {\n                if(i == COUNT_OF(list->file)) {\n                    list->file_count = i;\n                    response.has_next = true;\n                    rpc_send_and_release(session, &response);\n                    i = 0;\n                }\n                list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :\n                                                                   PB_Storage_File_FileType_FILE;\n                list->file[i].size = fileinfo.size;\n                list->file[i].data = NULL;\n                list->file[i].name = name;\n\n                if(include_md5 && !file_info_is_dir(&fileinfo)) {\n                    furi_string_printf(md5_path, \"%s/%s\", list_request->path, name); //-V576\n\n                    if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) {\n                        char* md5sum = list->file[i].md5sum;\n                        size_t md5sum_size = sizeof(list->file[i].md5sum);\n                        snprintf(md5sum, md5sum_size, \"%s\", furi_string_get_cstr(md5));\n                    }\n                }\n\n                ++i;\n            } else {\n                free(name);\n            }\n        } else {\n            list->file_count = i;\n            finish = true;\n            free(name);\n        }\n    }\n\n    response.has_next = false;\n    rpc_send_and_release(session, &response);\n\n    furi_string_free(md5);\n    furi_string_free(md5_path);\n    storage_dir_close(dir);\n    storage_file_free(dir);\n    storage_file_free(file);\n}\n\nstatic void rpc_system_storage_read_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_read_request_tag);\n\n    FURI_LOG_D(TAG, \"Read\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    /* use same message memory to send response */\n    PB_Main* response = malloc(sizeof(PB_Main));\n    const char* path = request->content.storage_read_request.path;\n    File* file = storage_file_alloc(rpc_storage->api);\n    bool fs_operation_success = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING);\n\n    if(fs_operation_success) {\n        size_t size_left = storage_file_size(file);\n        do {\n            response->command_id = request->command_id;\n            response->which_content = PB_Main_storage_read_response_tag;\n            response->command_status = PB_CommandStatus_OK;\n\n            size_t read_size = MIN(size_left, MAX_DATA_SIZE);\n            if(read_size) {\n                response->content.storage_read_response.has_file = true;\n                response->content.storage_read_response.file.data =\n                    malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(read_size));\n                uint8_t* buffer = &response->content.storage_read_response.file.data->bytes[0];\n                uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;\n\n                *read_size_msg = storage_file_read(file, buffer, read_size);\n                size_left -= *read_size_msg;\n                fs_operation_success = (*read_size_msg == read_size);\n\n                response->has_next = fs_operation_success && (size_left > 0);\n            } else {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Warray-bounds\"\n                response->content.storage_read_response.file.data =\n                    malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(0));\n                response->content.storage_read_response.file.data->size = 0;\n#pragma GCC diagnostic pop\n                response->content.storage_read_response.has_file = true;\n                response->has_next = false;\n                fs_operation_success = true;\n            }\n\n            if(fs_operation_success) {\n                rpc_send_and_release(session, response);\n            }\n        } while((size_left != 0) && fs_operation_success);\n    }\n\n    if(!fs_operation_success) {\n        rpc_send_and_release_empty(\n            session, request->command_id, rpc_system_storage_get_file_error(file));\n    }\n\n    free(response);\n    storage_file_close(file);\n    storage_file_free(file);\n}\n\nstatic void rpc_system_storage_write_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(context);\n    furi_assert(request->which_content == PB_Main_storage_write_request_tag);\n\n    FURI_LOG_D(TAG, \"Write\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    bool fs_operation_success = true;\n\n    if(!path_contains_only_ascii(request->content.storage_write_request.path)) {\n        rpc_storage->current_command_id = request->command_id;\n        rpc_send_and_release_empty(\n            session, rpc_storage->current_command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME);\n        rpc_system_storage_reset_state(rpc_storage, session, false);\n        return;\n    }\n\n    if((request->command_id != rpc_storage->current_command_id) &&\n       (rpc_storage->state == RpcStorageStateWriting)) {\n        rpc_system_storage_reset_state(rpc_storage, session, true);\n    }\n\n    if(rpc_storage->state != RpcStorageStateWriting) {\n        rpc_storage->file = storage_file_alloc(rpc_storage->api);\n        rpc_storage->current_command_id = request->command_id;\n        rpc_storage->state = RpcStorageStateWriting;\n        const char* path = request->content.storage_write_request.path;\n        fs_operation_success =\n            storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS);\n    }\n\n    File* file = rpc_storage->file;\n    bool send_response = false;\n\n    if(fs_operation_success) {\n        if(request->content.storage_write_request.has_file &&\n           request->content.storage_write_request.file.data &&\n           request->content.storage_write_request.file.data->size) {\n            uint8_t* buffer = request->content.storage_write_request.file.data->bytes;\n            size_t buffer_size = request->content.storage_write_request.file.data->size;\n            size_t written_size = storage_file_write(file, buffer, buffer_size);\n            fs_operation_success = (written_size == buffer_size);\n        }\n\n        send_response = !request->has_next;\n    }\n\n    PB_CommandStatus command_status = PB_CommandStatus_OK;\n    if(!fs_operation_success) {\n        send_response = true;\n        command_status = rpc_system_storage_get_file_error(file);\n        if(command_status == PB_CommandStatus_OK) {\n            // Report errors not handled by underlying APIs\n            command_status = PB_CommandStatus_ERROR_STORAGE_INTERNAL;\n        }\n    }\n\n    if(send_response) {\n        rpc_send_and_release_empty(session, rpc_storage->current_command_id, command_status);\n        rpc_system_storage_reset_state(rpc_storage, session, false);\n    }\n}\n\nstatic bool rpc_system_storage_is_dir_is_empty(Storage* storage, const char* path) {\n    furi_assert(storage);\n    furi_assert(path);\n\n    FileInfo fileinfo;\n    bool is_dir_is_empty = true;\n    FS_Error error = storage_common_stat(storage, path, &fileinfo);\n    if((error == FSE_OK) && file_info_is_dir(&fileinfo)) {\n        File* dir = storage_file_alloc(storage);\n        if(storage_dir_open(dir, path)) {\n            char* name = malloc(MAX_NAME_LENGTH);\n            while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {\n                if(path_contains_only_ascii(name)) {\n                    is_dir_is_empty = false;\n                    break;\n                }\n            }\n            free(name);\n        }\n        storage_dir_close(dir);\n        storage_file_free(dir);\n    }\n\n    return is_dir_is_empty;\n}\n\nstatic void rpc_system_storage_delete_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_delete_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"Delete\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    PB_CommandStatus status = PB_CommandStatus_ERROR;\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    char* path = request->content.storage_delete_request.path;\n    if(!path) {\n        status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;\n    } else {\n        FS_Error error_remove = storage_common_remove(rpc_storage->api, path);\n        // FSE_DENIED is for empty directory, but not only for this\n        // that's why we have to check it\n        if((error_remove == FSE_DENIED) &&\n           !rpc_system_storage_is_dir_is_empty(rpc_storage->api, path)) {\n            if(request->content.storage_delete_request.recursive) {\n                bool deleted = storage_simply_remove_recursive(rpc_storage->api, path);\n                status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;\n            } else {\n                status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY;\n            }\n        } else if(error_remove == FSE_NOT_EXIST) {\n            status = PB_CommandStatus_OK;\n        } else {\n            status = rpc_system_storage_get_error(error_remove);\n        }\n    }\n\n    rpc_send_and_release_empty(session, request->command_id, status);\n}\n\nstatic void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_mkdir_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"Mkdir\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    PB_CommandStatus status;\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    char* path = request->content.storage_mkdir_request.path;\n    if(path) {\n        if(path_contains_only_ascii(path)) {\n            FS_Error error = storage_common_mkdir(rpc_storage->api, path);\n            status = rpc_system_storage_get_error(error);\n        } else {\n            status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;\n        }\n    } else {\n        status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;\n    }\n    rpc_send_and_release_empty(session, request->command_id, status);\n}\n\nstatic void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_md5sum_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"Md5sum\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    const char* filename = request->content.storage_md5sum_request.path;\n    if(!filename) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n        return;\n    }\n\n    File* file = storage_file_alloc(rpc_storage->api);\n    FuriString* md5 = furi_string_alloc();\n    FS_Error file_error;\n\n    if(md5_string_calc_file(file, filename, md5, &file_error)) {\n        PB_Main response = {\n            .command_id = request->command_id,\n            .command_status = PB_CommandStatus_OK,\n            .which_content = PB_Main_storage_md5sum_response_tag,\n            .has_next = false,\n        };\n\n        char* md5sum = response.content.storage_md5sum_response.md5sum;\n        size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum);\n        snprintf(md5sum, md5sum_size, \"%s\", furi_string_get_cstr(md5));\n\n        rpc_send_and_release(session, &response);\n    } else {\n        rpc_send_and_release_empty(\n            session, request->command_id, rpc_system_storage_get_error(file_error));\n    }\n\n    furi_string_free(md5);\n    storage_file_free(file);\n}\n\nstatic void rpc_system_storage_rename_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_rename_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"Rename\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    PB_CommandStatus status;\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    if(path_contains_only_ascii(request->content.storage_rename_request.new_path)) {\n        FS_Error error = storage_common_rename(\n            rpc_storage->api,\n            request->content.storage_rename_request.old_path,\n            request->content.storage_rename_request.new_path);\n        status = rpc_system_storage_get_error(error);\n    } else {\n        status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;\n    }\n\n    rpc_send_and_release_empty(session, request->command_id, status);\n}\n\nstatic void rpc_system_storage_backup_create_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"BackupCreate\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    bool backup_ok = int_backup_create(\n        rpc_storage->api, request->content.storage_backup_create_request.archive_path);\n\n    rpc_send_and_release_empty(\n        session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR);\n}\n\nstatic void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"BackupRestore\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    bool backup_ok = int_backup_unpack(\n        rpc_storage->api, request->content.storage_backup_restore_request.archive_path);\n\n    rpc_send_and_release_empty(\n        session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR);\n}\n\nstatic void rpc_system_storage_tar_extract_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_storage_tar_extract_request_tag);\n    furi_assert(context);\n\n    FURI_LOG_D(TAG, \"TarExtract\");\n\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    PB_CommandStatus status;\n    rpc_system_storage_reset_state(rpc_storage, session, true);\n\n    TarArchive* archive = tar_archive_alloc(rpc_storage->api);\n\n    do {\n        const char *tar_path = request->content.storage_tar_extract_request.tar_path,\n                   *out_path = request->content.storage_tar_extract_request.out_path;\n        if(!path_contains_only_ascii(out_path)) {\n            status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;\n            break;\n        }\n\n        TarOpenMode tar_mode = tar_archive_get_mode_for_path(tar_path);\n\n        if(!tar_archive_open(archive, tar_path, tar_mode)) {\n            status = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;\n            break;\n        }\n\n        if(!tar_archive_unpack_to(archive, out_path, NULL)) {\n            status = PB_CommandStatus_ERROR_STORAGE_INTERNAL;\n            break;\n        }\n\n        status = PB_CommandStatus_OK;\n    } while(0);\n\n    tar_archive_free(archive);\n    rpc_send_and_release_empty(session, request->command_id, status);\n}\n\nvoid* rpc_system_storage_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcStorageSystem* rpc_storage = malloc(sizeof(RpcStorageSystem));\n    rpc_storage->api = furi_record_open(RECORD_STORAGE);\n    rpc_storage->session = session;\n    rpc_storage->state = RpcStorageStateIdle;\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = rpc_storage,\n    };\n\n    rpc_handler.message_handler = rpc_system_storage_info_process;\n    rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_timestamp_process;\n    rpc_add_handler(session, PB_Main_storage_timestamp_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_stat_process;\n    rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_list_process;\n    rpc_add_handler(session, PB_Main_storage_list_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_read_process;\n    rpc_add_handler(session, PB_Main_storage_read_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_write_process;\n    rpc_add_handler(session, PB_Main_storage_write_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_delete_process;\n    rpc_add_handler(session, PB_Main_storage_delete_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_mkdir_process;\n    rpc_add_handler(session, PB_Main_storage_mkdir_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_md5sum_process;\n    rpc_add_handler(session, PB_Main_storage_md5sum_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_rename_process;\n    rpc_add_handler(session, PB_Main_storage_rename_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_backup_create_process;\n    rpc_add_handler(session, PB_Main_storage_backup_create_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_backup_restore_process;\n    rpc_add_handler(session, PB_Main_storage_backup_restore_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_storage_tar_extract_process;\n    rpc_add_handler(session, PB_Main_storage_tar_extract_request_tag, &rpc_handler);\n\n    return rpc_storage;\n}\n\nvoid rpc_system_storage_free(void* context) {\n    furi_assert(context);\n    RpcStorageSystem* rpc_storage = context;\n    RpcSession* session = rpc_storage->session;\n    furi_assert(session);\n\n    rpc_system_storage_reset_state(rpc_storage, session, false);\n\n    furi_record_close(RECORD_STORAGE);\n    rpc_storage->api = NULL;\n    free(rpc_storage);\n}\n"
  },
  {
    "path": "applications/services/rpc/rpc_system.c",
    "content": "#include <flipper.pb.h>\n#include <furi_hal.h>\n#include <power/power_service/power.h>\n#include <notification/notification_messages.h>\n#include <protobuf_version.h>\n#include <update_util/update_operation.h>\n\n#include \"rpc_i.h\"\n\n#define TAG \"RpcSystem\"\n\ntypedef struct {\n    RpcSession* session;\n    PB_Main* response;\n} RpcSystemContext;\n\nstatic void rpc_system_system_ping_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_ping_request_tag);\n\n    FURI_LOG_D(TAG, \"Ping\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    if(request->has_next) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n        return;\n    }\n\n    PB_Main response = PB_Main_init_default;\n    // PB_CommandStatus_OK is 0 in current case, and var by default is 0 too, commenting PVS warn\n    response.command_status = PB_CommandStatus_OK; //-V1048\n    response.command_id = request->command_id;\n    response.which_content = PB_Main_system_ping_response_tag;\n\n    const PB_System_PingRequest* ping_request = &request->content.system_ping_request;\n    PB_System_PingResponse* ping_response = &response.content.system_ping_response;\n    if(ping_request->data && (ping_request->data->size > 0)) {\n        ping_response->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(ping_request->data->size));\n        memcpy(ping_response->data->bytes, ping_request->data->bytes, ping_request->data->size);\n        ping_response->data->size = ping_request->data->size;\n    }\n\n    rpc_send_and_release(session, &response);\n}\n\nstatic void rpc_system_system_reboot_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_reboot_request_tag);\n\n    FURI_LOG_D(TAG, \"Reboot\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    Power* power = furi_record_open(RECORD_POWER);\n    const int mode = request->content.system_reboot_request.mode;\n\n    if(mode == PB_System_RebootRequest_RebootMode_OS) {\n        power_reboot(power, PowerBootModeNormal);\n    } else if(mode == PB_System_RebootRequest_RebootMode_DFU) {\n        power_reboot(power, PowerBootModeDfu);\n    } else if(mode == PB_System_RebootRequest_RebootMode_UPDATE) {\n        power_reboot(power, PowerBootModeUpdateStart);\n    } else {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n    }\n\n    furi_record_close(RECORD_POWER);\n}\n\nstatic void rpc_system_system_device_info_callback(\n    const char* key,\n    const char* value,\n    bool last,\n    void* context) {\n    furi_assert(key);\n    furi_assert(value);\n    RpcSystemContext* ctx = context;\n    furi_assert(ctx);\n\n    furi_assert(key);\n    furi_assert(value);\n    char* str_key = strdup(key);\n    char* str_value = strdup(value);\n\n    ctx->response->has_next = !last;\n    ctx->response->content.system_device_info_response.key = str_key;\n    ctx->response->content.system_device_info_response.value = str_value;\n\n    rpc_send_and_release(ctx->session, ctx->response);\n}\n\nstatic void rpc_system_system_device_info_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_device_info_request_tag);\n\n    FURI_LOG_D(TAG, \"DeviceInfo\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_system_device_info_response_tag;\n    response->command_status = PB_CommandStatus_OK;\n\n    RpcSystemContext device_info_context = {\n        .session = session,\n        .response = response,\n    };\n    furi_hal_info_get(rpc_system_system_device_info_callback, '_', &device_info_context);\n\n    free(response);\n}\n\nstatic void rpc_system_system_get_datetime_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_get_datetime_request_tag);\n\n    FURI_LOG_D(TAG, \"GetDatetime\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    DateTime datetime;\n    furi_hal_rtc_get_datetime(&datetime);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_system_get_datetime_response_tag;\n    response->command_status = PB_CommandStatus_OK;\n    response->content.system_get_datetime_response.has_datetime = true;\n    response->content.system_get_datetime_response.datetime.hour = datetime.hour;\n    response->content.system_get_datetime_response.datetime.minute = datetime.minute;\n    response->content.system_get_datetime_response.datetime.second = datetime.second;\n    response->content.system_get_datetime_response.datetime.day = datetime.day;\n    response->content.system_get_datetime_response.datetime.month = datetime.month;\n    response->content.system_get_datetime_response.datetime.year = datetime.year;\n    response->content.system_get_datetime_response.datetime.weekday = datetime.weekday;\n\n    rpc_send_and_release(session, response);\n    free(response);\n}\n\nstatic void rpc_system_system_set_datetime_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_set_datetime_request_tag);\n\n    FURI_LOG_D(TAG, \"SetDatetime\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    if(!request->content.system_set_datetime_request.has_datetime) {\n        rpc_send_and_release_empty(\n            session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);\n        return;\n    }\n\n    DateTime datetime;\n    datetime.hour = request->content.system_set_datetime_request.datetime.hour;\n    datetime.minute = request->content.system_set_datetime_request.datetime.minute;\n    datetime.second = request->content.system_set_datetime_request.datetime.second;\n    datetime.day = request->content.system_set_datetime_request.datetime.day;\n    datetime.month = request->content.system_set_datetime_request.datetime.month;\n    datetime.year = request->content.system_set_datetime_request.datetime.year;\n    datetime.weekday = request->content.system_set_datetime_request.datetime.weekday;\n    furi_hal_rtc_set_datetime(&datetime);\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_system_factory_reset_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_factory_reset_request_tag);\n\n    FURI_LOG_D(TAG, \"Reset\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    furi_hal_rtc_reset_registers();\n    furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);\n\n    Power* power = furi_record_open(RECORD_POWER);\n    power_reboot(power, PowerBootModeNormal);\n}\n\nstatic void\n    rpc_system_system_play_audiovisual_alert_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_play_audiovisual_alert_request_tag);\n\n    FURI_LOG_D(TAG, \"Alert\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_audiovisual_alert);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);\n}\n\nstatic void rpc_system_system_protobuf_version_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_protobuf_version_request_tag);\n\n    FURI_LOG_D(TAG, \"ProtobufVersion\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n    response->command_status = PB_CommandStatus_OK;\n    response->which_content = PB_Main_system_protobuf_version_response_tag;\n    /* build error here means something wrong with tags in\n     * local repo https://github.com/flipperdevices/flipperzero-protobuf */\n    response->content.system_protobuf_version_response.major = PROTOBUF_MAJOR_VERSION;\n    response->content.system_protobuf_version_response.minor = PROTOBUF_MINOR_VERSION;\n\n    rpc_send_and_release(session, response);\n    free(response);\n}\n\nstatic void rpc_system_system_power_info_callback(\n    const char* key,\n    const char* value,\n    bool last,\n    void* context) {\n    furi_assert(key);\n    furi_assert(value);\n    RpcSystemContext* ctx = context;\n    furi_assert(ctx);\n\n    furi_assert(key);\n    furi_assert(value);\n    char* str_key = strdup(key);\n    char* str_value = strdup(value);\n\n    ctx->response->has_next = !last;\n    ctx->response->content.system_device_info_response.key = str_key;\n    ctx->response->content.system_device_info_response.value = str_value;\n\n    rpc_send_and_release(ctx->session, ctx->response);\n}\n\nstatic void rpc_system_system_get_power_info_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_power_info_request_tag);\n\n    FURI_LOG_D(TAG, \"GetPowerInfo\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->which_content = PB_Main_system_power_info_response_tag;\n    response->command_status = PB_CommandStatus_OK;\n\n    RpcSystemContext power_info_context = {\n        .session = session,\n        .response = response,\n    };\n    furi_hal_power_info_get(rpc_system_system_power_info_callback, '_', &power_info_context);\n\n    free(response);\n}\n\n#ifdef APP_UPDATER\nstatic void rpc_system_system_update_request_process(const PB_Main* request, void* context) {\n    furi_assert(request);\n    furi_assert(request->which_content == PB_Main_system_update_request_tag);\n\n    FURI_LOG_D(TAG, \"SystemUpdate\");\n\n    RpcSession* session = (RpcSession*)context;\n    furi_assert(session);\n\n    UpdatePrepareResult update_prepare_result =\n        update_operation_prepare(request->content.system_update_request.update_manifest);\n\n    PB_Main* response = malloc(sizeof(PB_Main));\n    response->command_id = request->command_id;\n    response->has_next = false;\n    response->command_status = (update_prepare_result == UpdatePrepareResultOK) ?\n                                   PB_CommandStatus_OK :\n                                   PB_CommandStatus_ERROR_INVALID_PARAMETERS;\n    response->which_content = PB_Main_system_update_response_tag;\n    response->content.system_update_response.code =\n        (PB_System_UpdateResponse_UpdateResultCode)update_prepare_result;\n    rpc_send_and_release(session, response);\n    free(response);\n}\n#endif\n\nvoid* rpc_system_system_alloc(RpcSession* session) {\n    furi_assert(session);\n\n    RpcHandler rpc_handler = {\n        .message_handler = NULL,\n        .decode_submessage = NULL,\n        .context = session,\n    };\n\n    rpc_handler.message_handler = rpc_system_system_ping_process;\n    rpc_add_handler(session, PB_Main_system_ping_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_reboot_process;\n    rpc_add_handler(session, PB_Main_system_reboot_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_device_info_process;\n    rpc_add_handler(session, PB_Main_system_device_info_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_factory_reset_process;\n    rpc_add_handler(session, PB_Main_system_factory_reset_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_get_datetime_process;\n    rpc_add_handler(session, PB_Main_system_get_datetime_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_set_datetime_process;\n    rpc_add_handler(session, PB_Main_system_set_datetime_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_play_audiovisual_alert_process;\n    rpc_add_handler(session, PB_Main_system_play_audiovisual_alert_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_protobuf_version_process;\n    rpc_add_handler(session, PB_Main_system_protobuf_version_request_tag, &rpc_handler);\n\n    rpc_handler.message_handler = rpc_system_system_get_power_info_process;\n    rpc_add_handler(session, PB_Main_system_power_info_request_tag, &rpc_handler);\n\n#ifdef APP_UPDATER\n    rpc_handler.message_handler = rpc_system_system_update_request_process;\n    rpc_add_handler(session, PB_Main_system_update_request_tag, &rpc_handler);\n#endif\n\n    return NULL;\n}\n"
  },
  {
    "path": "applications/services/storage/application.fam",
    "content": "App(\n    appid=\"storage\",\n    name=\"StorageSrv\",\n    apptype=FlipperAppType.SERVICE,\n    entry_point=\"storage_srv\",\n    cdefines=[\"SRV_STORAGE\"],\n    requires=[\"storage_settings\"],\n    provides=[\"storage_start\"],\n    stack_size=3 * 1024,\n    order=120,\n    sdk_headers=[\"storage.h\"],\n)\n\nApp(\n    appid=\"storage_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"storage_on_system_start\",\n    requires=[\"storage\"],\n    order=60,\n)\n"
  },
  {
    "path": "applications/services/storage/filesystem_api.c",
    "content": "#include \"filesystem_api_defines.h\"\n#include <furi.h>\n\nconst char* filesystem_api_error_get_desc(FS_Error error_id) {\n    const char* result = \"unknown error\";\n    switch(error_id) {\n    case(FSE_OK):\n        result = \"OK\";\n        break;\n    case(FSE_NOT_READY):\n        result = \"filesystem not ready\";\n        break;\n    case(FSE_EXIST):\n        result = \"file/dir already exist\";\n        break;\n    case(FSE_NOT_EXIST):\n        result = \"file/dir not exist\";\n        break;\n    case(FSE_INVALID_PARAMETER):\n        result = \"invalid parameter\";\n        break;\n    case(FSE_DENIED):\n        result = \"access denied\";\n        break;\n    case(FSE_INVALID_NAME):\n        result = \"invalid name/path\";\n        break;\n    case(FSE_INTERNAL):\n        result = \"internal error\";\n        break;\n    case(FSE_NOT_IMPLEMENTED):\n        result = \"function not implemented\";\n        break;\n    case(FSE_ALREADY_OPEN):\n        result = \"file is already open\";\n        break;\n    }\n    return result;\n}\n\nbool file_info_is_dir(const FileInfo* file_info) {\n    furi_check(file_info);\n    return file_info->flags & FSF_DIRECTORY;\n}\n"
  },
  {
    "path": "applications/services/storage/filesystem_api_defines.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Access mode flags */\ntypedef enum {\n    FSAM_READ = (1 << 0), /**< Read access */\n    FSAM_WRITE = (1 << 1), /**< Write access */\n    FSAM_READ_WRITE = FSAM_READ | FSAM_WRITE, /**< Read and write access */\n} FS_AccessMode;\n\n/** Open mode flags */\ntypedef enum {\n    FSOM_OPEN_EXISTING = 1, /**< Open file, fail if file doesn't exist */\n    FSOM_OPEN_ALWAYS = 2, /**< Open file. Create new file if not exist */\n    FSOM_OPEN_APPEND = 4, /**< Open file. Create new file if not exist. Set R/W pointer to EOF */\n    FSOM_CREATE_NEW = 8, /**< Creates a new file. Fails if the file is exist */\n    FSOM_CREATE_ALWAYS = 16, /**< Creates a new file. If file exist, truncate to zero size */\n} FS_OpenMode;\n\n/** API errors enumeration */\ntypedef enum {\n    FSE_OK, /**< No error */\n    FSE_NOT_READY, /**< FS not ready */\n    FSE_EXIST, /**< File/Dir already exist */\n    FSE_NOT_EXIST, /**< File/Dir does not exist */\n    FSE_INVALID_PARAMETER, /**< Invalid API parameter */\n    FSE_DENIED, /**< Access denied */\n    FSE_INVALID_NAME, /**< Invalid name/path */\n    FSE_INTERNAL, /**< Internal error */\n    FSE_NOT_IMPLEMENTED, /**< Function not implemented */\n    FSE_ALREADY_OPEN, /**< File/Dir already opened */\n} FS_Error;\n\n/** FileInfo flags */\ntypedef enum {\n    FSF_DIRECTORY = (1 << 0), /**< Directory */\n} FS_Flags;\n\n/** Structure that hold file index and returned api errors  */\ntypedef struct File File;\n\n/** Structure that hold file info */\ntypedef struct {\n    uint8_t flags; /**< flags from FS_Flags enum */\n    uint64_t size; /**< file size */\n} FileInfo;\n\n/** Gets the error text from FS_Error\n * @param error_id error id\n * @return const char* error text\n */\nconst char* filesystem_api_error_get_desc(FS_Error error_id);\n\n/** Checks if file info is directory\n * @param file_info file info pointer\n * @return bool is directory\n */\nbool file_info_is_dir(const FileInfo* file_info);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/filesystem_api_internal.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"filesystem_api_defines.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** File type */\ntypedef enum {\n    FileTypeClosed, /**< Closed file */\n    FileTypeOpenDir, /**< Open dir */\n    FileTypeOpenFile, /**< Open file */\n} FileType;\n\n/** Structure that hold file index and returned api errors */\nstruct File {\n    uint32_t file_id; /**< File ID for internal references */\n    FileType type;\n    FS_Error error_id; /**< Standard API error from FS_Error enum */\n    int32_t internal_error_id; /**< Internal API error value */\n    void* storage;\n};\n\n/** File api structure\n *  @var FS_File_Api::open\n *      @brief Open file\n *      @param file pointer to file object, filled by api\n *      @param path path to file \n *      @param access_mode access mode from FS_AccessMode \n *      @param open_mode open mode from FS_OpenMode \n *      @return success flag\n * \n *  @var FS_File_Api::close \n *      @brief Close file\n *      @param file pointer to file object\n *      @return success flag\n * \n *  @var FS_File_Api::read\n *      @brief Read bytes from file to buffer\n *      @param file pointer to file object\n *      @param buff pointer to buffer for reading\n *      @param bytes_to_read how many bytes to read, must be smaller or equal to buffer size \n *      @return how many bytes actually has been read\n * \n *  @var FS_File_Api::write\n *      @brief Write bytes from buffer to file\n *      @param file pointer to file object\n *      @param buff pointer to buffer for writing\n *      @param bytes_to_read how many bytes to write, must be smaller or equal to buffer size \n *      @return how many bytes actually has been written\n * \n *  @var FS_File_Api::seek\n *      @brief Move r/w pointer \n *      @param file pointer to file object\n *      @param offset offset to move r/w pointer\n *      @param from_start set offset from start, or from current position\n *      @return success flag\n * \n *  @var FS_File_Api::tell\n *      @brief Get r/w pointer position\n *      @param file pointer to file object\n *      @return current r/w pointer position\n * \n *  @var FS_File_Api::truncate\n *      @brief Truncate file size to current r/w pointer position\n *      @param file pointer to file object\n *      @return success flag\n * \n *  @var FS_File_Api::size\n *      @brief Fet file size\n *      @param file pointer to file object\n *      @return file size\n * \n *  @var FS_File_Api::sync\n *      @brief Write file cache to storage\n *      @param file pointer to file object\n *      @return success flag\n * \n *  @var FS_File_Api::eof\n *      @brief Checks that the r/w pointer is at the end of the file\n *      @param file pointer to file object\n *      @return end of file flag\n */\ntypedef struct {\n    bool (*const open)(\n        void* context,\n        File* file,\n        const char* path,\n        FS_AccessMode access_mode,\n        FS_OpenMode open_mode);\n    bool (*const close)(void* context, File* file);\n    uint16_t (*read)(void* context, File* file, void* buff, uint16_t bytes_to_read);\n    uint16_t (*write)(void* context, File* file, const void* buff, uint16_t bytes_to_write);\n    bool (*const seek)(void* context, File* file, uint32_t offset, bool from_start);\n    uint64_t (*tell)(void* context, File* file);\n    bool (*const truncate)(void* context, File* file);\n    uint64_t (*size)(void* context, File* file);\n    bool (*const sync)(void* context, File* file);\n    bool (*const eof)(void* context, File* file);\n} FS_File_Api;\n\n/** Dir api structure\n *  @var FS_Dir_Api::open\n *      @brief Open directory to get objects from\n *      @param file pointer to file object, filled by api\n *      @param path path to directory \n *      @return success flag\n * \n *  @var FS_Dir_Api::close \n *      @brief Close directory\n *      @param file pointer to file object\n *      @return success flag\n * \n *  @var FS_Dir_Api::read\n *      @brief Read next object info in directory\n *      @param file pointer to file object\n *      @param fileinfo pointer to read FileInfo, can be NULL\n *      @param name pointer to name buffer, can be NULL\n *      @param name_length name buffer length\n *      @return success flag (if next object not exist also returns false and set error_id to FSE_NOT_EXIST)\n * \n *  @var FS_Dir_Api::rewind\n *      @brief Rewind to first object info in directory\n *      @param file pointer to file object\n *      @return success flag\n */\ntypedef struct {\n    bool (*const open)(void* context, File* file, const char* path);\n    bool (*const close)(void* context, File* file);\n    bool (*const read)(\n        void* context,\n        File* file,\n        FileInfo* fileinfo,\n        char* name,\n        uint16_t name_length);\n    bool (*const rewind)(void* context, File* file);\n} FS_Dir_Api;\n\n/** Common api structure\n *  @var FS_Common_Api::stat\n *      @brief Open directory to get objects from\n *      @param path path to file/directory\n *      @param fileinfo pointer to read FileInfo, can be NULL\n *      @param name pointer to name buffer, can be NULL\n *      @param name_length name buffer length\n *      @return FS_Error error info\n * \n *  @var FS_Common_Api::remove\n *      @brief Remove file/directory from storage, \n *          directory must be empty,\n *          file/directory must not be opened,\n *          file/directory must not have FSF_READ_ONLY flag\n *      @param path path to file/directory\n *      @return FS_Error error info\n * \n *  @var FS_Common_Api::mkdir\n *      @brief Create new directory\n *      @param path path to new directory\n *      @return FS_Error error info\n * \n *  @var FS_Common_Api::fs_info\n *      @brief Get total and free space storage values\n *      @param fs_path path of fs\n *      @param total_space pointer to total space value\n *      @param free_space pointer to free space value\n *      @return FS_Error error info\n *\n *  @var FS_Common_Api::equivalent_path\n *      @brief Test whether two paths are equivalent (e.g differing case on a case-insensitive fs)\n *      @param path1 first path to be compared\n *      @param path2 second path to be compared\n *      @param truncate if set to true, compare only up to the path1's length\n *      @return true if path1 and path2 are considered equivalent\n */\ntypedef struct {\n    FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo);\n    FS_Error (*const remove)(void* context, const char* path);\n    FS_Error (*const mkdir)(void* context, const char* path);\n    FS_Error (*const fs_info)(\n        void* context,\n        const char* fs_path,\n        uint64_t* total_space,\n        uint64_t* free_space);\n    bool (*const equivalent_path)(const char* path1, const char* path2);\n} FS_Common_Api;\n\n/** Full filesystem api structure */\ntypedef struct {\n    const FS_File_Api file;\n    const FS_Dir_Api dir;\n    const FS_Common_Api common;\n} FS_Api;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage.c",
    "content": "#include \"storage.h\"\n#include \"storage_i.h\"\n#include \"storage_message.h\"\n#include \"storage_processing.h\"\n#include \"storage/storage_glue.h\"\n#include \"storages/storage_ext.h\"\n#include <assets_icons.h>\n\n#define STORAGE_TICK 1000\n\n#define ICON_SD_MOUNTED &I_SDcardMounted_11x8\n#define ICON_SD_ERROR   &I_SDcardFail_11x8\n\n#define TAG \"Storage\"\n\nstatic void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(canvas);\n    furi_assert(context);\n    Storage* app = context;\n\n    // here we don't care about thread race when reading / writing status\n    switch(app->storage[ST_EXT].status) {\n    case StorageStatusNotReady:\n        break;\n    case StorageStatusOK:\n        canvas_draw_icon(canvas, 0, 0, ICON_SD_MOUNTED);\n        break;\n    default:\n        canvas_draw_icon(canvas, 0, 0, ICON_SD_ERROR);\n        break;\n    }\n}\n\nStorage* storage_app_alloc(void) {\n    Storage* app = malloc(sizeof(Storage));\n    app->message_queue = furi_message_queue_alloc(8, sizeof(StorageMessage));\n    app->pubsub = furi_pubsub_alloc();\n\n    for(uint8_t i = 0; i < STORAGE_COUNT; i++) {\n        storage_data_init(&app->storage[i]);\n        storage_data_timestamp(&app->storage[i]);\n    }\n\n    storage_ext_init(&app->storage[ST_EXT]);\n\n    // sd icon gui\n    app->sd_gui.enabled = false;\n    app->sd_gui.view_port = view_port_alloc();\n    view_port_set_width(app->sd_gui.view_port, icon_get_width(ICON_SD_MOUNTED));\n    view_port_draw_callback_set(app->sd_gui.view_port, storage_app_sd_icon_draw_callback, app);\n    view_port_enabled_set(app->sd_gui.view_port, false);\n\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, app->sd_gui.view_port, GuiLayerStatusBarLeft);\n    furi_record_close(RECORD_GUI);\n\n    return app;\n}\n\nvoid storage_tick(Storage* app) {\n    for(uint8_t i = 0; i < STORAGE_COUNT; i++) {\n        StorageApi api = app->storage[i].api;\n        if(api.tick != NULL) {\n            api.tick(&app->storage[i]);\n        }\n    }\n\n    // storage not enabled but was enabled (sd card unmount)\n    if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) {\n        app->sd_gui.enabled = false;\n        view_port_enabled_set(app->sd_gui.view_port, false);\n\n        FURI_LOG_I(TAG, \"SD card unmount\");\n        StorageEvent event = {.type = StorageEventTypeCardUnmount};\n        furi_pubsub_publish(app->pubsub, &event);\n    }\n\n    // storage enabled (or in error state) but was not enabled (sd card mount)\n    if((app->storage[ST_EXT].status == StorageStatusOK ||\n        app->storage[ST_EXT].status == StorageStatusNotMounted ||\n        app->storage[ST_EXT].status == StorageStatusNoFS ||\n        app->storage[ST_EXT].status == StorageStatusNotAccessible ||\n        app->storage[ST_EXT].status == StorageStatusErrorInternal) &&\n       app->sd_gui.enabled == false) {\n        app->sd_gui.enabled = true;\n        view_port_enabled_set(app->sd_gui.view_port, true);\n\n        if(app->storage[ST_EXT].status == StorageStatusOK) {\n            FURI_LOG_I(TAG, \"SD card mount\");\n            StorageEvent event = {.type = StorageEventTypeCardMount};\n            furi_pubsub_publish(app->pubsub, &event);\n        } else {\n            FURI_LOG_I(TAG, \"SD card mount error\");\n            StorageEvent event = {.type = StorageEventTypeCardMountError};\n            furi_pubsub_publish(app->pubsub, &event);\n        }\n    }\n}\n\nint32_t storage_srv(void* p) {\n    UNUSED(p);\n    Storage* app = storage_app_alloc();\n    furi_record_create(RECORD_STORAGE, app);\n\n    StorageMessage message;\n    while(1) {\n        if(furi_message_queue_get(app->message_queue, &message, STORAGE_TICK) == FuriStatusOk) {\n            storage_process_message(app, &message);\n        } else {\n            storage_tick(app);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/services/storage/storage.h",
    "content": "/**\n * @file storage.h\n * @brief APIs for working with storages, directories and files.\n */\n#pragma once\n\n#include <stdint.h>\n#include \"filesystem_api_defines.h\"\n#include \"storage_sd_api.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define STORAGE_INT_PATH_PREFIX        \"/int\"\n#define STORAGE_EXT_PATH_PREFIX        \"/ext\"\n#define STORAGE_ANY_PATH_PREFIX        \"/any\"\n#define STORAGE_APP_DATA_PATH_PREFIX   \"/data\"\n#define STORAGE_APP_ASSETS_PATH_PREFIX \"/assets\"\n\n#define INT_PATH(path)        STORAGE_INT_PATH_PREFIX \"/\" path\n#define EXT_PATH(path)        STORAGE_EXT_PATH_PREFIX \"/\" path\n#define ANY_PATH(path)        STORAGE_ANY_PATH_PREFIX \"/\" path\n#define APP_DATA_PATH(path)   STORAGE_APP_DATA_PATH_PREFIX \"/\" path\n#define APP_ASSETS_PATH(path) STORAGE_APP_ASSETS_PATH_PREFIX \"/\" path\n\n#define RECORD_STORAGE \"storage\"\n\ntypedef struct Storage Storage;\n\n/**\n * @brief Allocate and initialize a file instance.\n *\n * @param storage pointer to a storage API instance.\n * @return pointer to the created instance.\n */\nFile* storage_file_alloc(Storage* storage);\n\n/**\n * @brief Free the file instance.\n *\n * If the file was open, calling this function will close it automatically.\n * @param file pointer to the file instance to be freed.\n */\nvoid storage_file_free(File* file);\n\n/**\n * @brief Enumeration of events emitted by the storage through the PubSub system.\n */\ntypedef enum {\n    StorageEventTypeCardMount, /**< SD card was mounted. */\n    StorageEventTypeCardUnmount, /**< SD card was unmounted. */\n    StorageEventTypeCardMountError, /**< An error occurred during mounting of an SD card. */\n    StorageEventTypeFileClose, /**< A file was closed. */\n    StorageEventTypeDirClose, /**< A directory was closed. */\n} StorageEventType;\n\n/**\n * @brief Storage event (passed to the PubSub callback).\n */\ntypedef struct {\n    StorageEventType type; /**< Type of the event. */\n} StorageEvent;\n\n/**\n * @brief Get the storage pubsub instance.\n *\n * Storage will send StorageEvent messages.\n *\n * @param storage pointer to a storage API instance.\n * @return pointer to the pubsub instance.\n */\nFuriPubSub* storage_get_pubsub(Storage* storage);\n\n/******************* File Functions *******************/\n\n/**\n * @brief Open an existing file or create a new one.\n *\n * @warning The calling code MUST call storage_file_close() even if the open operation had failed.\n *\n * @param file pointer to the file instance to be opened.\n * @param path pointer to a zero-terminated string containing the path to the file to be opened.\n * @param access_mode access mode from FS_AccessMode.\n * @param open_mode open mode from FS_OpenMode \n * @return true if the file was successfully opened, false otherwise.\n */\nbool storage_file_open(\n    File* file,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode);\n\n/**\n * @brief Close the file.\n *\n * @param file pointer to the file instance to be closed.\n * @return true if the file was successfully closed, false otherwise.\n */\nbool storage_file_close(File* file);\n\n/**\n * @brief Check whether the file is open.\n *\n * @param file pointer to the file instance in question.\n * @return true if the file is open, false otherwise.\n */\nbool storage_file_is_open(File* file);\n\n/**\n * @brief Check whether a file instance represents a directory.\n *\n * @param file pointer to the file instance in question.\n * @return true if the file instance represents a directory, false otherwise.\n */\nbool storage_file_is_dir(File* file);\n\n/**\n * @brief Read bytes from a file into a buffer.\n *\n * @param file pointer to the file instance to read from.\n * @param buff pointer to the buffer to be filled with read data.\n * @param bytes_to_read number of bytes to read. Must be less than or equal to the size of the buffer.\n * @return actual number of bytes read (may be fewer than requested).\n */\nsize_t storage_file_read(File* file, void* buff, size_t bytes_to_read);\n\n/**\n * @brief Write bytes from a buffer to a file.\n *\n * @param file pointer to the file instance to write into.\n * @param buff pointer to the buffer containing the data to be written.\n * @param bytes_to_write number of bytes to write. Must be less than or equal to the size of the buffer.\n * @return actual number of bytes written (may be fewer than requested).\n */\nsize_t storage_file_write(File* file, const void* buff, size_t bytes_to_write);\n\n/**\n * @brief Change the current access position in a file.\n *\n * @param file pointer to the file instance in question.\n * @param offset access position offset (meaning depends on from_start parameter).\n * @param from_start if true, set the access position relative to the file start, otherwise relative to the current position.\n * @return success flag\n */\nbool storage_file_seek(File* file, uint32_t offset, bool from_start);\n\n/**\n * @brief Get the current access position.\n *\n * @param file pointer to the file instance in question.\n * @return current access position.\n */\nuint64_t storage_file_tell(File* file);\n\n/**\n * @brief Truncate the file size to the current access position.\n *\n * @param file pointer to the file instance to be truncated.\n * @return true if the file was successfully truncated, false otherwise.\n */\nbool storage_file_truncate(File* file);\n\n/**\n * @brief Get the file size.\n *\n * @param file pointer to the file instance in question.\n * @return size of the file, in bytes.\n */\nuint64_t storage_file_size(File* file);\n\n/**\n * @brief Synchronise the file cache with the actual storage.\n *\n * @param file pointer to the file instance in question.\n * @return true if the file was successfully synchronised, false otherwise.\n */\nbool storage_file_sync(File* file);\n\n/**\n * @brief Check whether the current access position is at the end of the file.\n *\n * @param file pointer to a file instance in question.\n * @return bool true if the current access position is at the end of the file, false otherwise.\n */\nbool storage_file_eof(File* file);\n\n/**\n * @brief Check whether a file exists.\n * \n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path to the file in question.\n * @return true if the file exists, false otherwise.\n */\nbool storage_file_exists(Storage* storage, const char* path);\n\n/**\n * @brief Copy data from a source file to the destination file.\n *\n * Both files must be opened prior to calling this function.\n *\n * The requested amount of bytes will be copied from the current access position\n * in the source file to the current access position in the destination file.\n * \n * @param source pointer to a source file instance.\n * @param destination pointer to a destination file instance.\n * @param size data size to be copied, in bytes.\n * @return true if the data was successfully copied, false otherwise.\n */\nbool storage_file_copy_to_file(File* source, File* destination, size_t size);\n\n/******************* Directory Functions *******************/\n\n/**\n * @brief Open a directory.\n *\n * Opening a directory is necessary to be able to read its contents with storage_dir_read().\n *\n * @warning The calling code MUST call storage_dir_close() even if the open operation had failed.\n *\n * @param file pointer to a file instance representing the directory in question.\n * @param path pointer to a zero-terminated string containing the path of the directory in question.\n * @return true if the directory was successfully opened, false otherwise.\n */\nbool storage_dir_open(File* file, const char* path);\n\n/**\n * @brief Close the directory.\n *\n * @param file pointer to a file instance representing the directory in question.\n * @return true if the directory was successfully closed, false otherwise.\n */\nbool storage_dir_close(File* file);\n\n/**\n * @brief Get the next item in the directory.\n *\n * If the next object does not exist, this function returns false as well\n * and sets the file error id to FSE_NOT_EXIST.\n *\n * @param file pointer to a file instance representing the directory in question.\n * @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL).\n * @param name pointer to the buffer to contain the name (may be NULL).\n * @param name_length maximum capacity of the name buffer, in bytes.\n * @return true if the next item was successfully read, false otherwise.\n */\nbool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length);\n\n/**\n * @brief Change the access position to first item in the directory.\n *\n * @param file pointer to a file instance representing the directory in question.\n * @return true if the access position was successfully changed, false otherwise.\n */\nbool storage_dir_rewind(File* file);\n\n/**\n * @brief Check whether a directory exists.\n * \n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path of the directory in question.\n * @return true if the directory exists, false otherwise.\n */\nbool storage_dir_exists(Storage* storage, const char* path);\n\n/******************* Common Functions *******************/\n\n/**\n * @brief Get the last access time in UNIX format.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path of the item in question.\n * @param timestamp pointer to a value to contain the timestamp.\n * @return FSE_OK if the timestamp has been successfully received, any other error code on failure.\n */\nFS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp);\n\n/**\n * @brief Get information about a file or a directory.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path of the item in question.\n * @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL).\n * @return FSE_OK if the info has been successfully received, any other error code on failure.\n */\nFS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo);\n\n/**\n * @brief Remove a file or a directory.\n *\n * The directory must be empty.\n * The file or the directory must NOT be open.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path of the item to be removed.\n * @return FSE_OK if the file or directory has been successfully removed, any other error code on failure.\n */\nFS_Error storage_common_remove(Storage* storage, const char* path);\n\n/**\n * @brief Rename a file or a directory.\n *\n * The file or the directory must NOT be open.\n * Will overwrite the destination file if it already exists.\n *\n * Renaming a regular file to itself does nothing and always succeeds.\n * Renaming a directory to itself or to a subdirectory of itself always fails.\n *\n * @param storage pointer to a storage API instance.\n * @param old_path pointer to a zero-terminated string containing the source path.\n * @param new_path pointer to a zero-terminated string containing the destination path.\n * @return FSE_OK if the file or directory has been successfully renamed, any other error code on failure.\n */\nFS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path);\n\n/**\n * @brief Copy the file to a new location.\n *\n * The file must NOT be open at the time of calling this function.\n *\n * @param storage pointer to a storage API instance.\n * @param old_path pointer to a zero-terminated string containing the source path.\n * @param new_path pointer to a zero-terminated string containing the destination path.\n * @return FSE_OK if the file has been successfully copied, any other error code on failure.\n */\nFS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path);\n\n/**\n * @brief Copy the contents of one directory into another and rename all conflicting files.\n *\n * @param storage pointer to a storage API instance.\n * @param old_path pointer to a zero-terminated string containing the source path.\n * @param new_path pointer to a zero-terminated string containing the destination path.\n * @return FSE_OK if the directories have been successfully merged, any other error code on failure.\n */\nFS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path);\n\n/**\n * @brief Create a directory.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the directory path.\n * @return FSE_OK if the directory has been successfully created, any other error code on failure.\n */\nFS_Error storage_common_mkdir(Storage* storage, const char* path);\n\n/**\n * @brief Get the general information about the storage.\n *\n * @param storage pointer to a storage API instance.\n * @param fs_path pointer to a zero-terminated string containing the path to the storage question.\n * @param total_space pointer to the value to contain the total capacity, in bytes.\n * @param free_space pointer to the value to contain the available space, in bytes.\n * @return FSE_OK if the information has been successfully received, any other error code on failure.\n */\nFS_Error storage_common_fs_info(\n    Storage* storage,\n    const char* fs_path,\n    uint64_t* total_space,\n    uint64_t* free_space);\n\n/**\n * @brief Parse aliases in a path and replace them with the real path.\n *\n * Necessary special directories will be created automatically if they did not exist.\n * \n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path in question.\n */\nvoid storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path);\n\n/**\n * @brief Move the contents of source folder to destination one and rename all conflicting files.\n *\n * Source folder will be deleted if the migration was successful.\n * \n * @param storage pointer to a storage API instance.\n * @param source pointer to a zero-terminated string containing the source path.\n * @param dest pointer to a zero-terminated string containing the destination path.\n * @return FSE_OK if the migration was successfully completed, any other error code on failure.\n */\nFS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest);\n\n/**\n * @brief Check whether a file or a directory exists.\n * \n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the path in question.\n * @return true if a file or a directory exists, false otherwise.\n */\nbool storage_common_exists(Storage* storage, const char* path);\n\n/**\n * @brief Check whether two paths are equivalent.\n *\n * This function will resolve aliases and apply filesystem-specific\n * rules to determine whether the two given paths are equivalent.\n *\n * Examples:\n * - /int/text and /ext/test -> false (Different storages),\n * - /int/Test and /int/test -> false (Case-sensitive storage),\n * - /ext/Test and /ext/test -> true (Case-insensitive storage).\n *\n * @param storage pointer to a storage API instance.\n * @param path1 pointer to a zero-terminated string containing the first path.\n * @param path2 pointer to a zero-terminated string containing the second path.\n * @return true if paths are equivalent, false otherwise.\n */\nbool storage_common_equivalent_path(Storage* storage, const char* path1, const char* path2);\n\n/**\n * @brief Check whether a path is a subpath of another path.\n * \n * This function respects storage-defined equivalence rules\n * (see `storage_common_equivalent_path`).\n * \n * @param storage pointer to a storage API instance.\n * @param parent pointer to a zero-terminated string containing the parent path.\n * @param child pointer to a zero-terminated string containing the child path.\n * @return true if `child` is a subpath of `parent`, or if `child` is equivalent\n *         to `parent`; false otherwise.\n */\nbool storage_common_is_subdir(Storage* storage, const char* parent, const char* child);\n\n/******************* Error Functions *******************/\n\n/**\n * @brief Get the textual description of a numeric error identifier.\n *\n * @param error_id numeric identifier of the error in question.\n * @return pointer to a statically allocated zero-terminated string containing the respective error text.\n */\nconst char* storage_error_get_desc(FS_Error error_id);\n\n/**\n * @brief Get the numeric error identifier from a file instance.\n *\n * @warning It is not possible to get the error identifier after the file has been closed.\n *\n * @param file pointer to the file instance in question (must NOT be NULL).\n * @return numeric identifier of the last error associated with the file instance.\n */\nFS_Error storage_file_get_error(File* file);\n\n/**\n * @brief Get the internal (storage-specific) numeric error identifier from a file instance.\n *\n * @warning It is not possible to get the internal error identifier after the file has been closed.\n *\n * @param file pointer to the file instance in question (must NOT be NULL).\n * @return numeric identifier of the last internal error associated with the file instance.\n */\nint32_t storage_file_get_internal_error(File* file);\n\n/**\n * @brief Get the textual description of a the last error associated with a file instance.\n *\n * @warning It is not possible to get the error text after the file has been closed.\n *\n * @param file pointer to the file instance in question (must NOT be NULL).\n * @return pointer to a statically allocated zero-terminated string containing the respective error text.\n */\nconst char* storage_file_get_error_desc(File* file);\n\n/******************* SD Card Functions *******************/\n\n/**\n * @brief Format the SD Card.\n *\n * @param storage pointer to a storage API instance.\n * @return FSE_OK if the card was successfully formatted, any other error code on failure.\n */\nFS_Error storage_sd_format(Storage* storage);\n\n/**\n * @brief Unmount the SD card.\n *\n * These return values have special meaning:\n * - FSE_NOT_READY if the SD card is not mounted.\n * - FSE_DENIED if there are open files on the SD card.\n *\n * @param storage pointer to a storage API instance.\n * @return FSE_OK if the card was successfully formatted, any other error code on failure.\n */\nFS_Error storage_sd_unmount(Storage* storage);\n\n/**\n * @brief Mount the SD card.\n *\n * @param storage pointer to a storage API instance.\n * @return FSE_OK if the card was successfully mounted, any other error code on failure.\n */\nFS_Error storage_sd_mount(Storage* storage);\n\n/**\n * @brief Get SD card information.\n *\n * @param storage pointer to a storage API instance.\n * @param info pointer to the info object to contain the requested information.\n * @return FSE_OK if the info was successfully received, any other error code on failure.\n */\nFS_Error storage_sd_info(Storage* storage, SDInfo* info);\n\n/**\n * @brief Get SD card status.\n *\n * @param storage pointer to a storage API instance.\n * @return storage status in the form of a numeric error identifier.\n */\nFS_Error storage_sd_status(Storage* storage);\n\n/************ Internal Storage Backup/Restore ************/\n\ntypedef void (*StorageNameConverter)(FuriString*);\n\n/**\n * @brief Back up the internal storage contents to a *.tar archive.\n *\n * @param storage pointer to a storage API instance.\n * @param dstname pointer to a zero-terminated string containing the archive file path.\n * @return FSE_OK if the storage was successfully backed up, any other error code on failure.\n */\nFS_Error storage_int_backup(Storage* storage, const char* dstname);\n\n/**\n * @brief Restore the internal storage contents from a *.tar archive.\n *\n * @param storage pointer to a storage API instance.\n * @param dstname pointer to a zero-terminated string containing the archive file path.\n * @param converter pointer to a filename conversion function (may be NULL).\n * @return FSE_OK if the storage was successfully restored, any other error code on failure.\n */\nFS_Error\n    storage_int_restore(Storage* storage, const char* dstname, StorageNameConverter converter);\n\n/***************** Simplified Functions ******************/\n\n/**\n * @brief Remove a file or a directory.\n *\n * The following conditions must be met:\n * - the directory must be empty.\n * - the file or the directory must NOT be open.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the item path.\n * @return true on success or if the item does not exist, false otherwise.\n */\nbool storage_simply_remove(Storage* storage, const char* path);\n\n/**\n * @brief Recursively remove a file or a directory.\n *\n * Unlike storage_simply_remove(), the directory does not need to be empty.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the item path.\n * @return true on success or if the item does not exist, false otherwise.\n */\nbool storage_simply_remove_recursive(Storage* storage, const char* path);\n\n/**\n * @brief Create a directory.\n *\n * @param storage pointer to a storage API instance.\n * @param path pointer to a zero-terminated string containing the directory path.\n * @return true on success or if directory does already exist, false otherwise.\n */\nbool storage_simply_mkdir(Storage* storage, const char* path);\n\n/**\n * @brief Get the next free filename in a directory.\n *\n * Usage example:\n * ```c\n * FuriString* file_name = furi_string_alloc();\n * Storage* storage = furi_record_open(RECORD_STORAGE);\n *\n * storage_get_next_filename(storage,\n *     \"/ext/test\",\n *     \"cookies\",\n *     \".yum\",\n *     20);\n *\n * furi_record_close(RECORD_STORAGE);\n *\n * use_file_name(file_name);\n *\n * furi_string_free(file_name);\n * ```\n * Possible file_name values after calling storage_get_next_filename():\n * \"cookies\", \"cookies1\", \"cookies2\", ... etc depending on whether any of\n * these files have already existed in the directory.\n *\n * @note If the resulting next file name length is greater than set by the max_len\n * parameter, the original filename will be returned instead.\n * \n * @param storage pointer to a storage API instance.\n * @param dirname pointer to a zero-terminated string containing the directory path.\n * @param filename pointer to a zero-terminated string containing the file name.\n * @param fileextension pointer to a zero-terminated string containing the file extension.\n * @param nextfilename pointer to a dynamic string containing the resulting file name.\n * @param max_len maximum length of the new name.\n */\nvoid storage_get_next_filename(\n    Storage* storage,\n    const char* dirname,\n    const char* filename,\n    const char* fileextension,\n    FuriString* nextfilename,\n    uint8_t max_len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage_cli.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <toolbox/cli/cli_command.h>\n#include <toolbox/cli/cli_ansi.h>\n#include <cli/cli_main_commands.h>\n#include <lib/toolbox/args.h>\n#include <lib/toolbox/dir_walk.h>\n#include <lib/toolbox/md5_calc.h>\n#include <lib/toolbox/strint.h>\n#include <lib/toolbox/tar/tar_archive.h>\n#include <storage/storage.h>\n#include <storage/storage_sd_api.h>\n#include <power/power_service/power.h>\n#include <toolbox/pipe.h>\n\n#define MAX_NAME_LENGTH 255\n\nstatic void storage_cli_print_usage(void);\n\nstatic void storage_cli_print_error(FS_Error error) {\n    printf(\"Storage error: %s\\r\\n\", storage_error_get_desc(error));\n}\n\nstatic void storage_cli_info(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n\n    if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) {\n        uint64_t total_space;\n        uint64_t free_space;\n        FS_Error error =\n            storage_common_fs_info(api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);\n\n        if(error != FSE_OK) {\n            storage_cli_print_error(error);\n        } else {\n            printf(\n                \"Label: %s\\r\\nType: Virtual\\r\\n%luKiB total\\r\\n%luKiB free\\r\\n\",\n                furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : \"Unknown\",\n                (uint32_t)(total_space / 1024),\n                (uint32_t)(free_space / 1024));\n        }\n    } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {\n        SDInfo sd_info;\n        FS_Error error = storage_sd_info(api, &sd_info);\n\n        if(error != FSE_OK) {\n            storage_cli_print_error(error);\n        } else {\n            printf(\n                \"Label: %s\\r\\nType: %s\\r\\n%luKiB total\\r\\n%luKiB free\\r\\n\"\n                \"%02x%s %s v%i.%i\\r\\nSN:%04lx %02i/%i\\r\\n\",\n                sd_info.label,\n                sd_api_get_fs_type_text(sd_info.fs_type),\n                sd_info.kb_total,\n                sd_info.kb_free,\n                sd_info.manufacturer_id,\n                sd_info.oem_id,\n                sd_info.product_name,\n                sd_info.product_revision_major,\n                sd_info.product_revision_minor,\n                sd_info.product_serial_number,\n                sd_info.manufacturing_month,\n                sd_info.manufacturing_year);\n        }\n    } else {\n        storage_cli_print_usage();\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_format(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) {\n        storage_cli_print_error(FSE_NOT_IMPLEMENTED);\n    } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {\n        printf(\"Formatting SD card, All data will be lost! Are you sure (y/n)?\\r\\n\");\n        char answer = getchar();\n        if(answer == 'y' || answer == 'Y') {\n            Storage* api = furi_record_open(RECORD_STORAGE);\n            printf(\"Formatting, please wait...\\r\\n\");\n\n            FS_Error error = storage_sd_format(api);\n\n            if(error != FSE_OK) {\n                storage_cli_print_error(error);\n            } else {\n                printf(\"SD card was successfully formatted.\\r\\n\");\n            }\n            furi_record_close(RECORD_STORAGE);\n        } else {\n            printf(\"Cancelled.\\r\\n\");\n        }\n    } else {\n        storage_cli_print_usage();\n    }\n}\n\nstatic void storage_cli_list(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    if(furi_string_cmp_str(path, \"/\") == 0) {\n        printf(\"\\t[D] int\\r\\n\");\n        printf(\"\\t[D] ext\\r\\n\");\n        printf(\"\\t[D] any\\r\\n\");\n    } else {\n        Storage* api = furi_record_open(RECORD_STORAGE);\n        File* file = storage_file_alloc(api);\n\n        if(storage_dir_open(file, furi_string_get_cstr(path))) {\n            FileInfo fileinfo;\n            char name[MAX_NAME_LENGTH];\n            bool read_done = false;\n\n            while(storage_dir_read(file, &fileinfo, name, MAX_NAME_LENGTH)) {\n                read_done = true;\n                if(file_info_is_dir(&fileinfo)) {\n                    printf(\"\\t[D] %s\\r\\n\", name);\n                } else {\n                    printf(\"\\t[F] %s %lub\\r\\n\", name, (uint32_t)(fileinfo.size));\n                }\n            }\n\n            if(!read_done) {\n                printf(\"\\tEmpty\\r\\n\");\n            }\n        } else {\n            storage_cli_print_error(storage_file_get_error(file));\n        }\n\n        storage_dir_close(file);\n        storage_file_free(file);\n        furi_record_close(RECORD_STORAGE);\n    }\n}\n\nstatic void storage_cli_tree(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(args);\n    if(furi_string_cmp_str(path, \"/\") == 0) {\n        furi_string_set(path, STORAGE_INT_PATH_PREFIX);\n        storage_cli_tree(pipe, path, NULL);\n        furi_string_set(path, STORAGE_EXT_PATH_PREFIX);\n        storage_cli_tree(pipe, path, NULL);\n    } else {\n        Storage* api = furi_record_open(RECORD_STORAGE);\n        DirWalk* dir_walk = dir_walk_alloc(api);\n        FuriString* name;\n        name = furi_string_alloc();\n\n        if(dir_walk_open(dir_walk, furi_string_get_cstr(path))) {\n            FileInfo fileinfo;\n            bool read_done = false;\n\n            while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) {\n                read_done = true;\n                if(file_info_is_dir(&fileinfo)) {\n                    printf(\"\\t[D] %s\\r\\n\", furi_string_get_cstr(name));\n                } else {\n                    printf(\n                        \"\\t[F] %s %lub\\r\\n\",\n                        furi_string_get_cstr(name),\n                        (uint32_t)(fileinfo.size));\n                }\n            }\n\n            if(!read_done) {\n                printf(\"\\tEmpty\\r\\n\");\n            }\n        } else {\n            storage_cli_print_error(dir_walk_get_error(dir_walk));\n        }\n\n        furi_string_free(name);\n        dir_walk_free(dir_walk);\n        furi_record_close(RECORD_STORAGE);\n    }\n}\n\nstatic void storage_cli_read(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n\n    if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {\n        const size_t buffer_size = 128;\n        size_t read_size = 0;\n        uint8_t* data = malloc(buffer_size);\n\n        printf(\"Size: %lu\\r\\n\", (uint32_t)storage_file_size(file));\n\n        do {\n            read_size = storage_file_read(file, data, buffer_size);\n            for(size_t i = 0; i < read_size; i++) {\n                printf(\"%c\", data[i]);\n            }\n        } while(read_size > 0);\n        printf(\"\\r\\n\");\n\n        free(data);\n    } else {\n        storage_cli_print_error(storage_file_get_error(file));\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_write(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n\n    const size_t buffer_size = 512;\n    uint8_t* buffer = malloc(buffer_size);\n\n    if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) {\n        printf(\"Just write your text data. New line by Ctrl+Enter, exit by Ctrl+C.\\r\\n\");\n\n        uint32_t read_index = 0;\n\n        while(true) {\n            uint8_t symbol = getchar();\n\n            if(symbol == CliKeyETX) {\n                size_t write_size = read_index % buffer_size;\n\n                if(write_size > 0) {\n                    size_t written_size = storage_file_write(file, buffer, write_size);\n\n                    if(written_size != write_size) {\n                        storage_cli_print_error(storage_file_get_error(file));\n                    }\n                    break;\n                }\n            }\n\n            buffer[read_index % buffer_size] = symbol;\n            printf(\"%c\", buffer[read_index % buffer_size]);\n            fflush(stdout);\n            read_index++;\n\n            if((read_index % buffer_size) == 0) {\n                size_t written_size = storage_file_write(file, buffer, buffer_size);\n\n                if(written_size != buffer_size) {\n                    storage_cli_print_error(storage_file_get_error(file));\n                    break;\n                }\n            }\n        }\n        printf(\"\\r\\n\");\n\n    } else {\n        storage_cli_print_error(storage_file_get_error(file));\n    }\n    storage_file_close(file);\n\n    free(buffer);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_read_chunks(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n\n    uint32_t buffer_size;\n    if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) !=\n       StrintParseNoError) {\n        storage_cli_print_usage();\n    } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {\n        uint64_t file_size = storage_file_size(file);\n\n        printf(\"Size: %llu\\r\\n\", file_size);\n\n        if(buffer_size) {\n            uint8_t* data = malloc(buffer_size);\n            while(file_size > 0) {\n                printf(\"\\r\\nReady?\\r\\n\");\n                getchar();\n\n                size_t read_size = storage_file_read(file, data, buffer_size);\n                for(size_t i = 0; i < read_size; i++) {\n                    putchar(data[i]);\n                }\n                file_size -= read_size;\n            }\n            free(data);\n        }\n        printf(\"\\r\\n\");\n\n    } else {\n        storage_cli_print_error(storage_file_get_error(file));\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_write_chunk(PipeSide* pipe, FuriString* path, FuriString* args) {\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n\n    uint32_t need_to_read;\n    if(strint_to_uint32(furi_string_get_cstr(args), NULL, &need_to_read, 10) !=\n       StrintParseNoError) {\n        storage_cli_print_usage();\n    } else {\n        if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) {\n            printf(\"Ready\\r\\n\");\n            const size_t buffer_size = 1024;\n            uint8_t* buffer = malloc(buffer_size);\n\n            while(need_to_read) {\n                size_t to_read_this_time = MIN(buffer_size, need_to_read);\n                size_t read_this_time = pipe_receive(pipe, buffer, to_read_this_time);\n                if(read_this_time != to_read_this_time) break;\n\n                size_t wrote_this_time = storage_file_write(file, buffer, read_this_time);\n                if(wrote_this_time != read_this_time) {\n                    storage_cli_print_error(storage_file_get_error(file));\n                    break;\n                }\n                need_to_read -= read_this_time;\n            }\n\n            free(buffer);\n        } else {\n            storage_cli_print_error(storage_file_get_error(file));\n        }\n        storage_file_close(file);\n    }\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_stat(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n\n    if(furi_string_cmp_str(path, \"/\") == 0) {\n        printf(\"Storage\\r\\n\");\n    } else if(\n        furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0 ||\n        furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0 ||\n        furi_string_cmp_str(path, STORAGE_ANY_PATH_PREFIX) == 0) {\n        uint64_t total_space;\n        uint64_t free_space;\n        FS_Error error =\n            storage_common_fs_info(api, furi_string_get_cstr(path), &total_space, &free_space);\n\n        if(error != FSE_OK) {\n            storage_cli_print_error(error);\n        } else {\n            printf(\n                \"Storage, %luKiB total, %luKiB free\\r\\n\",\n                (uint32_t)(total_space / 1024),\n                (uint32_t)(free_space / 1024));\n        }\n    } else {\n        FileInfo fileinfo;\n        FS_Error error = storage_common_stat(api, furi_string_get_cstr(path), &fileinfo);\n\n        if(error == FSE_OK) {\n            if(file_info_is_dir(&fileinfo)) {\n                printf(\"Directory\\r\\n\");\n            } else {\n                printf(\"File, size: %lub\\r\\n\", (uint32_t)(fileinfo.size));\n            }\n        } else {\n            storage_cli_print_error(error);\n        }\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_timestamp(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n\n    uint32_t timestamp = 0;\n    FS_Error error = storage_common_timestamp(api, furi_string_get_cstr(path), &timestamp);\n\n    if(error != FSE_OK) {\n        printf(\"Invalid arguments\\r\\n\");\n    } else {\n        printf(\"Timestamp %lu\\r\\n\", timestamp);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_copy(PipeSide* pipe, FuriString* old_path, FuriString* args) {\n    UNUSED(pipe);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    FuriString* new_path;\n    new_path = furi_string_alloc();\n\n    if(!args_read_probably_quoted_string_and_trim(args, new_path)) {\n        storage_cli_print_usage();\n    } else {\n        FS_Error error = storage_common_copy(\n            api, furi_string_get_cstr(old_path), furi_string_get_cstr(new_path));\n\n        if(error != FSE_OK) {\n            storage_cli_print_error(error);\n        }\n    }\n\n    furi_string_free(new_path);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_remove(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    FS_Error error = storage_common_remove(api, furi_string_get_cstr(path));\n\n    if(error != FSE_OK) {\n        storage_cli_print_error(error);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_rename(PipeSide* pipe, FuriString* old_path, FuriString* args) {\n    UNUSED(pipe);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    FuriString* new_path;\n    new_path = furi_string_alloc();\n\n    if(!args_read_probably_quoted_string_and_trim(args, new_path)) {\n        storage_cli_print_usage();\n    } else {\n        FS_Error error = storage_common_rename(\n            api, furi_string_get_cstr(old_path), furi_string_get_cstr(new_path));\n\n        if(error != FSE_OK) {\n            storage_cli_print_error(error);\n        }\n    }\n\n    furi_string_free(new_path);\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_mkdir(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    FS_Error error = storage_common_mkdir(api, furi_string_get_cstr(path));\n\n    if(error != FSE_OK) {\n        storage_cli_print_error(error);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void storage_cli_md5(PipeSide* pipe, FuriString* path, FuriString* args) {\n    UNUSED(pipe);\n    UNUSED(args);\n    Storage* api = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(api);\n    FuriString* md5 = furi_string_alloc();\n    FS_Error file_error;\n\n    if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) {\n        printf(\"%s\\r\\n\", furi_string_get_cstr(md5));\n    } else {\n        storage_cli_print_error(file_error);\n    }\n\n    furi_string_free(md5);\n    storage_file_close(file);\n    storage_file_free(file);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic bool tar_extract_file_callback(const char* name, bool is_directory, void* context) {\n    UNUSED(context);\n    printf(\"\\t%s %s\\r\\n\", is_directory ? \"D\" : \"F\", name);\n    return true;\n}\n\nstatic void storage_cli_extract(PipeSide* pipe, FuriString* old_path, FuriString* args) {\n    UNUSED(pipe);\n    FuriString* new_path = furi_string_alloc();\n\n    if(!args_read_probably_quoted_string_and_trim(args, new_path)) {\n        storage_cli_print_usage();\n        furi_string_free(new_path);\n        return;\n    }\n\n    Storage* api = furi_record_open(RECORD_STORAGE);\n\n    TarArchive* archive = tar_archive_alloc(api);\n    TarOpenMode tar_mode = tar_archive_get_mode_for_path(furi_string_get_cstr(old_path));\n    do {\n        if(!tar_archive_open(archive, furi_string_get_cstr(old_path), tar_mode)) {\n            printf(\"Failed to open archive\\r\\n\");\n            break;\n        }\n        uint32_t start_tick = furi_get_tick();\n        tar_archive_set_file_callback(archive, tar_extract_file_callback, NULL);\n        printf(\"Unpacking to %s\\r\\n\", furi_string_get_cstr(new_path));\n        bool success = tar_archive_unpack_to(archive, furi_string_get_cstr(new_path), NULL);\n        uint32_t end_tick = furi_get_tick();\n        printf(\n            \"Decompression %s in %lu ticks \\r\\n\",\n            success ? \"success\" : \"failed\",\n            end_tick - start_tick);\n    } while(false);\n\n    tar_archive_free(archive);\n    furi_string_free(new_path);\n    furi_record_close(RECORD_STORAGE);\n}\n\ntypedef void (*StorageCliCommandCallback)(PipeSide* pipe, FuriString* path, FuriString* args);\n\ntypedef struct {\n    const char* command;\n    const char* help;\n    const StorageCliCommandCallback impl;\n} StorageCliCommand;\n\nstatic const StorageCliCommand storage_cli_commands[] = {\n    {\n        \"write_chunk\",\n        \"read data from cli and append it to file, <args> should contain how many bytes you want to write\",\n        &storage_cli_write_chunk,\n    },\n    {\n        \"read_chunks\",\n        \"read data from file and print file size and content to cli, <args> should contain how many bytes you want to read in block\",\n        &storage_cli_read_chunks,\n    },\n    {\n        \"list\",\n        \"list files and dirs\",\n        &storage_cli_list,\n    },\n    {\n        \"md5\",\n        \"md5 hash of the file\",\n        &storage_cli_md5,\n    },\n    {\n        \"stat\",\n        \"info about file or dir\",\n        &storage_cli_stat,\n    },\n    {\n        \"info\",\n        \"get FS info\",\n        &storage_cli_info,\n    },\n    {\n        \"tree\",\n        \"list files and dirs, recursive\",\n        &storage_cli_tree,\n    },\n    {\n        \"read\",\n        \"read text from file and print file size and content to cli\",\n        &storage_cli_read,\n    },\n    {\n        \"write\",\n        \"read text from cli and append it to file, stops by ctrl+c\",\n        &storage_cli_write,\n    },\n    {\n        \"copy\",\n        \"copy file to new file, <args> must contain new path\",\n        &storage_cli_copy,\n    },\n    {\n        \"remove\",\n        \"delete the file or directory\",\n        &storage_cli_remove,\n    },\n    {\n        \"rename\",\n        \"move file to new file, <args> must contain new path\",\n        &storage_cli_rename,\n    },\n    {\n        \"mkdir\",\n        \"creates a new directory\",\n        &storage_cli_mkdir,\n    },\n    {\n        \"timestamp\",\n        \"last modification timestamp\",\n        &storage_cli_timestamp,\n    },\n    {\n        \"extract\",\n        \"extract tar archive to destination\",\n        &storage_cli_extract,\n    },\n    {\n        \"format\",\n        \"format filesystem\",\n        &storage_cli_format,\n    },\n};\n\nstatic void storage_cli_print_usage(void) {\n    printf(\"Usage:\\r\\n\");\n    printf(\"storage <cmd> <path> <args>\\r\\n\");\n    printf(\"The path must start with /int or /ext\\r\\n\");\n    printf(\"Cmd list:\\r\\n\");\n\n    for(size_t i = 0; i < COUNT_OF(storage_cli_commands); ++i) {\n        const StorageCliCommand* command_descr = &storage_cli_commands[i];\n        const char* cli_cmd = command_descr->command;\n        printf(\n            \"\\t%s%s - %s\\r\\n\", cli_cmd, strlen(cli_cmd) > 8 ? \"\\t\" : \"\\t\\t\", command_descr->help);\n    }\n}\n\nvoid storage_cli(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n    FuriString* cmd;\n    FuriString* path;\n    cmd = furi_string_alloc();\n    path = furi_string_alloc();\n\n    do {\n        if(!args_read_string_and_trim(args, cmd)) {\n            storage_cli_print_usage();\n            break;\n        }\n\n        if(!args_read_probably_quoted_string_and_trim(args, path)) {\n            storage_cli_print_usage();\n            break;\n        }\n\n        size_t i = 0;\n        for(; i < COUNT_OF(storage_cli_commands); ++i) {\n            const StorageCliCommand* command_descr = &storage_cli_commands[i];\n            if(furi_string_cmp_str(cmd, command_descr->command) == 0) {\n                command_descr->impl(pipe, path, args);\n                break;\n            }\n        }\n\n        if(i == COUNT_OF(storage_cli_commands)) {\n            storage_cli_print_usage();\n        }\n    } while(false);\n\n    furi_string_free(path);\n    furi_string_free(cmd);\n}\n\nstatic void storage_cli_factory_reset(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    UNUSED(context);\n    printf(\"All data will be lost! Are you sure (y/n)?\\r\\n\");\n    char c = getchar();\n    if(c == 'y' || c == 'Y') {\n        printf(\"Data will be wiped after reboot.\\r\\n\");\n\n        furi_hal_rtc_reset_registers();\n        furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);\n\n        Power* power = furi_record_open(RECORD_POWER);\n        power_reboot(power, PowerBootModeNormal);\n    } else {\n        printf(\"Safe choice.\\r\\n\");\n    }\n}\n\nvoid storage_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(\n        registry,\n        \"storage\",\n        CliCommandFlagParallelSafe | CliCommandFlagUseShellThread,\n        storage_cli,\n        NULL);\n    cli_registry_add_command(\n        registry, \"factory_reset\", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(storage_cli_factory_reset);\n#endif\n}\n"
  },
  {
    "path": "applications/services/storage/storage_external_api.c",
    "content": "#include <core/log.h>\n#include <core/record.h>\n#include \"storage.h\"\n#include \"storage_i.h\" // IWYU pragma: keep\n#include \"storage_message.h\"\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/dir_walk.h>\n#include \"toolbox/path.h\"\n\n#define MAX_NAME_LENGTH  256\n#define MAX_EXT_LEN      16\n#define FILE_BUFFER_SIZE 512\n\n#define TAG \"StorageApi\"\n\n#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();\n\n#define S_FILE_API_PROLOGUE           \\\n    furi_check(file);                 \\\n    Storage* storage = file->storage; \\\n    furi_check(storage);\n\n#define S_API_EPILOGUE                                                               \\\n    furi_check(                                                                      \\\n        furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \\\n        FuriStatusOk);                                                               \\\n    api_lock_wait_unlock_and_free(lock)\n\n#define S_API_MESSAGE(_command)      \\\n    SAReturn return_data;            \\\n    StorageMessage message = {       \\\n        .lock = lock,                \\\n        .command = _command,         \\\n        .data = &data,               \\\n        .return_data = &return_data, \\\n    };\n\n#define S_API_DATA_FILE   \\\n    SAData data = {       \\\n        .file = {         \\\n            .file = file, \\\n        }};\n\n#define S_RETURN_BOOL    (return_data.bool_value);\n#define S_RETURN_UINT16  (return_data.uint16_value);\n#define S_RETURN_UINT64  (return_data.uint64_value);\n#define S_RETURN_ERROR   (return_data.error_value);\n#define S_RETURN_CSTRING (return_data.cstring_value);\n\ntypedef enum {\n    StorageEventFlagFileClose = (1 << 0),\n} StorageEventFlag;\n/****************** FILE ******************/\n\nstatic bool storage_file_open_internal(\n    File* file,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .fopen = {\n            .file = file,\n            .path = path,\n            .access_mode = access_mode,\n            .open_mode = open_mode,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    file->type = FileTypeOpenFile;\n\n    S_API_MESSAGE(StorageCommandFileOpen);\n    S_API_EPILOGUE;\n\n    return S_RETURN_BOOL;\n}\n\nstatic void storage_file_close_callback(const void* message, void* context) {\n    const StorageEvent* storage_event = message;\n\n    if(storage_event->type == StorageEventTypeFileClose ||\n       storage_event->type == StorageEventTypeDirClose) {\n        furi_assert(context);\n        FuriEventFlag* event = context;\n        furi_event_flag_set(event, StorageEventFlagFileClose);\n    }\n}\n\nbool storage_file_open(\n    File* file,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    furi_check(file);\n\n    bool result;\n    FuriEventFlag* event = furi_event_flag_alloc();\n    FuriPubSubSubscription* subscription = furi_pubsub_subscribe(\n        storage_get_pubsub(file->storage), storage_file_close_callback, event);\n\n    do {\n        result = storage_file_open_internal(file, path, access_mode, open_mode);\n\n        if(!result && file->error_id == FSE_ALREADY_OPEN) {\n            furi_event_flag_wait(\n                event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever);\n        } else {\n            break;\n        }\n    } while(true);\n\n    furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);\n    furi_event_flag_free(event);\n\n    FURI_LOG_T(\n        TAG,\n        \"File %p - %p open (%s)\",\n        (void*)((uint32_t)file - SRAM_BASE),\n        (void*)(file->file_id - SRAM_BASE),\n        path);\n\n    return result;\n}\n\nbool storage_file_close(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileClose);\n    S_API_EPILOGUE;\n\n    FURI_LOG_T(\n        TAG,\n        \"File %p - %p closed\",\n        (void*)((uint32_t)file - SRAM_BASE),\n        (void*)(file->file_id - SRAM_BASE));\n    file->type = FileTypeClosed;\n\n    return S_RETURN_BOOL;\n}\n\nstatic uint16_t storage_file_read_underlying(File* file, void* buff, uint16_t bytes_to_read) {\n    if(bytes_to_read == 0) {\n        return 0;\n    }\n\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .fread = {\n            .file = file,\n            .buff = buff,\n            .bytes_to_read = bytes_to_read,\n        }};\n\n    S_API_MESSAGE(StorageCommandFileRead);\n    S_API_EPILOGUE;\n    return S_RETURN_UINT16;\n}\n\nstatic uint16_t\n    storage_file_write_underlying(File* file, const void* buff, uint16_t bytes_to_write) {\n    if(bytes_to_write == 0) {\n        return 0;\n    }\n\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .fwrite = {\n            .file = file,\n            .buff = buff,\n            .bytes_to_write = bytes_to_write,\n        }};\n\n    S_API_MESSAGE(StorageCommandFileWrite);\n    S_API_EPILOGUE;\n    return S_RETURN_UINT16;\n}\n\nsize_t storage_file_read(File* file, void* buff, size_t to_read) {\n    size_t total = 0;\n\n    const size_t max_chunk = UINT16_MAX;\n    do {\n        const size_t chunk = MIN((to_read - total), max_chunk);\n        size_t read = storage_file_read_underlying(file, buff + total, chunk);\n        total += read;\n\n        if(storage_file_get_error(file) != FSE_OK || read != chunk) {\n            break;\n        }\n    } while(total != to_read);\n\n    return total;\n}\n\nsize_t storage_file_write(File* file, const void* buff, size_t to_write) {\n    furi_check(file);\n\n    size_t total = 0;\n\n    const size_t max_chunk = UINT16_MAX;\n    do {\n        const size_t chunk = MIN((to_write - total), max_chunk);\n        size_t written = storage_file_write_underlying(file, buff + total, chunk);\n        total += written;\n\n        if(storage_file_get_error(file) != FSE_OK || written != chunk) {\n            break;\n        }\n    } while(total != to_write);\n\n    return total;\n}\n\nbool storage_file_seek(File* file, uint32_t offset, bool from_start) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .fseek = {\n            .file = file,\n            .offset = offset,\n            .from_start = from_start,\n        }};\n\n    S_API_MESSAGE(StorageCommandFileSeek);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nuint64_t storage_file_tell(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileTell);\n    S_API_EPILOGUE;\n    return S_RETURN_UINT64;\n}\n\nbool storage_file_truncate(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileTruncate);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nuint64_t storage_file_size(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileSize);\n    S_API_EPILOGUE;\n    return S_RETURN_UINT64;\n}\n\nbool storage_file_sync(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileSync);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nbool storage_file_eof(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandFileEof);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nbool storage_file_exists(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    bool exist = false;\n    FileInfo fileinfo;\n    FS_Error error = storage_common_stat(storage, path, &fileinfo);\n\n    if(error == FSE_OK && !file_info_is_dir(&fileinfo)) {\n        exist = true;\n    }\n\n    return exist;\n}\n\nbool storage_file_copy_to_file(File* source, File* destination, size_t size) {\n    furi_check(source);\n    furi_check(destination);\n\n    uint8_t* buffer = malloc(FILE_BUFFER_SIZE);\n\n    while(size) {\n        uint32_t read_size = size > FILE_BUFFER_SIZE ? FILE_BUFFER_SIZE : size;\n        if(storage_file_read(source, buffer, read_size) != read_size) {\n            break;\n        }\n\n        if(storage_file_write(destination, buffer, read_size) != read_size) {\n            break;\n        }\n\n        size -= read_size;\n    }\n\n    free(buffer);\n    return size == 0;\n}\n\n/****************** DIR ******************/\n\nstatic bool storage_dir_open_internal(File* file, const char* path) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .dopen = {\n            .file = file,\n            .path = path,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    file->type = FileTypeOpenDir;\n\n    S_API_MESSAGE(StorageCommandDirOpen);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nbool storage_dir_open(File* file, const char* path) {\n    furi_check(file);\n\n    bool result;\n    FuriEventFlag* event = furi_event_flag_alloc();\n    FuriPubSubSubscription* subscription = furi_pubsub_subscribe(\n        storage_get_pubsub(file->storage), storage_file_close_callback, event);\n\n    do {\n        result = storage_dir_open_internal(file, path);\n\n        if(!result && file->error_id == FSE_ALREADY_OPEN) {\n            furi_event_flag_wait(\n                event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever);\n        } else {\n            break;\n        }\n    } while(true);\n\n    furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);\n    furi_event_flag_free(event);\n\n    FURI_LOG_T(\n        TAG,\n        \"Dir %p - %p open (%s)\",\n        (void*)((uint32_t)file - SRAM_BASE),\n        (void*)(file->file_id - SRAM_BASE),\n        path);\n\n    return result;\n}\n\nbool storage_dir_close(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandDirClose);\n    S_API_EPILOGUE;\n\n    FURI_LOG_T(\n        TAG,\n        \"Dir %p - %p closed\",\n        (void*)((uint32_t)file - SRAM_BASE),\n        (void*)(file->file_id - SRAM_BASE));\n\n    file->type = FileTypeClosed;\n\n    return S_RETURN_BOOL;\n}\n\nbool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .dread = {\n            .file = file,\n            .fileinfo = fileinfo,\n            .name = name,\n            .name_length = name_length,\n        }};\n\n    S_API_MESSAGE(StorageCommandDirRead);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nbool storage_dir_rewind(File* file) {\n    S_FILE_API_PROLOGUE;\n    S_API_PROLOGUE;\n    S_API_DATA_FILE;\n    S_API_MESSAGE(StorageCommandDirRewind);\n    S_API_EPILOGUE;\n    return S_RETURN_BOOL;\n}\n\nbool storage_dir_exists(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    bool exist = false;\n    FileInfo fileinfo;\n    FS_Error error = storage_common_stat(storage, path, &fileinfo);\n\n    if(error == FSE_OK && file_info_is_dir(&fileinfo)) {\n        exist = true;\n    }\n\n    return exist;\n}\n/****************** COMMON ******************/\n\nFS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) {\n    furi_check(storage);\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .ctimestamp = {\n            .path = path,\n            .timestamp = timestamp,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonTimestamp);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {\n        .cstat = {\n            .path = path,\n            .fileinfo = fileinfo,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonStat);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_common_remove(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {\n        .path = {\n            .path = path,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonRemove);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) {\n    furi_check(storage);\n    FS_Error error;\n\n    do {\n        if(!storage_common_exists(storage, old_path)) {\n            error = FSE_INVALID_NAME;\n            break;\n        }\n\n        if(storage_dir_exists(storage, old_path)) {\n            // Cannot overwrite a file with a directory\n            if(storage_file_exists(storage, new_path)) {\n                error = FSE_INVALID_NAME;\n                break;\n            }\n\n            // Cannot rename a directory to itself or to a nested directory\n            if(storage_common_is_subdir(storage, old_path, new_path)) {\n                error = FSE_INVALID_NAME;\n                break;\n            }\n\n            // Renaming a regular file to itself does nothing and always succeeds\n        } else if(storage_common_equivalent_path(storage, old_path, new_path)) {\n            error = FSE_OK;\n            break;\n        }\n\n        if(storage_file_exists(storage, new_path)) {\n            storage_common_remove(storage, new_path);\n        }\n\n        error = storage_common_copy(storage, old_path, new_path);\n        if(error != FSE_OK) {\n            break;\n        }\n\n        if(!storage_simply_remove_recursive(storage, old_path)) {\n            error = FSE_INTERNAL;\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic FS_Error\n    storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) {\n    FS_Error error = storage_common_mkdir(storage, new_path);\n    DirWalk* dir_walk = dir_walk_alloc(storage);\n    FuriString* path;\n    FuriString* tmp_new_path;\n    FuriString* tmp_old_path;\n    FileInfo fileinfo;\n    path = furi_string_alloc();\n    tmp_new_path = furi_string_alloc();\n    tmp_old_path = furi_string_alloc();\n\n    do {\n        if(error != FSE_OK) break;\n\n        if(!dir_walk_open(dir_walk, old_path)) {\n            error = dir_walk_get_error(dir_walk);\n            break;\n        }\n\n        while(1) {\n            DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo);\n\n            if(res == DirWalkError) {\n                error = dir_walk_get_error(dir_walk);\n                break;\n            } else if(res == DirWalkLast) {\n                break;\n            } else {\n                furi_string_set(tmp_old_path, path);\n                furi_string_right(path, strlen(old_path));\n                furi_string_printf(tmp_new_path, \"%s%s\", new_path, furi_string_get_cstr(path));\n\n                if(file_info_is_dir(&fileinfo)) {\n                    error = storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path));\n                } else {\n                    error = storage_common_copy(\n                        storage,\n                        furi_string_get_cstr(tmp_old_path),\n                        furi_string_get_cstr(tmp_new_path));\n                }\n\n                if(error != FSE_OK) break;\n            }\n        }\n\n    } while(false);\n\n    furi_string_free(tmp_new_path);\n    furi_string_free(tmp_old_path);\n    furi_string_free(path);\n    dir_walk_free(dir_walk);\n    return error;\n}\n\nFS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) {\n    furi_check(storage);\n\n    FS_Error error;\n\n    FileInfo fileinfo;\n    error = storage_common_stat(storage, old_path, &fileinfo);\n\n    if(error == FSE_OK) {\n        if(file_info_is_dir(&fileinfo)) {\n            error = storage_copy_recursive(storage, old_path, new_path);\n        } else {\n            Stream* stream_from = file_stream_alloc(storage);\n            Stream* stream_to = file_stream_alloc(storage);\n\n            do {\n                if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n                if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break;\n                stream_copy_full(stream_from, stream_to);\n            } while(false);\n\n            error = file_stream_get_error(stream_from);\n            if(error == FSE_OK) {\n                error = file_stream_get_error(stream_to);\n            }\n\n            stream_free(stream_from);\n            stream_free(stream_to);\n        }\n    }\n\n    return error;\n}\n\nstatic FS_Error\n    storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) {\n    FS_Error error = FSE_OK;\n    DirWalk* dir_walk = dir_walk_alloc(storage);\n    FuriString *path, *file_basename, *tmp_new_path;\n    FileInfo fileinfo;\n    path = furi_string_alloc();\n    file_basename = furi_string_alloc();\n    tmp_new_path = furi_string_alloc();\n\n    do {\n        if(!storage_simply_mkdir(storage, new_path)) break;\n\n        dir_walk_set_recursive(dir_walk, false);\n        if(!dir_walk_open(dir_walk, old_path)) {\n            error = dir_walk_get_error(dir_walk);\n            break;\n        }\n\n        while(1) {\n            DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo);\n\n            if(res == DirWalkError) {\n                error = dir_walk_get_error(dir_walk);\n                break;\n            } else if(res == DirWalkLast) {\n                break;\n            } else {\n                path_extract_basename(furi_string_get_cstr(path), file_basename);\n                path_concat(new_path, furi_string_get_cstr(file_basename), tmp_new_path);\n\n                if(file_info_is_dir(&fileinfo)) {\n                    if(storage_common_stat(\n                           storage, furi_string_get_cstr(tmp_new_path), &fileinfo) == FSE_OK) {\n                        if(file_info_is_dir(&fileinfo)) {\n                            error =\n                                storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path));\n                            if(error != FSE_OK && error != FSE_EXIST) {\n                                break;\n                            }\n                        }\n                    }\n                }\n                error = storage_common_merge(\n                    storage, furi_string_get_cstr(path), furi_string_get_cstr(tmp_new_path));\n\n                if(error != FSE_OK) {\n                    break;\n                }\n            }\n        }\n\n    } while(false);\n\n    furi_string_free(tmp_new_path);\n    furi_string_free(file_basename);\n    furi_string_free(path);\n    dir_walk_free(dir_walk);\n    return error;\n}\n\nFS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) {\n    furi_check(storage);\n\n    FS_Error error;\n    const char* new_path_tmp = NULL;\n    FuriString* new_path_next = NULL;\n    new_path_next = furi_string_alloc();\n\n    FileInfo fileinfo;\n    error = storage_common_stat(storage, old_path, &fileinfo);\n\n    if(error == FSE_OK) {\n        if(file_info_is_dir(&fileinfo)) {\n            error = storage_merge_recursive(storage, old_path, new_path);\n        } else {\n            error = storage_common_stat(storage, new_path, &fileinfo);\n            if(error == FSE_OK) {\n                furi_string_set(new_path_next, new_path);\n                FuriString* dir_path;\n                FuriString* filename;\n                char extension[MAX_EXT_LEN] = {0};\n\n                dir_path = furi_string_alloc();\n                filename = furi_string_alloc();\n\n                path_extract_filename(new_path_next, filename, true);\n                path_extract_dirname(new_path, dir_path);\n                path_extract_extension(new_path_next, extension, MAX_EXT_LEN);\n\n                storage_get_next_filename(\n                    storage,\n                    furi_string_get_cstr(dir_path),\n                    furi_string_get_cstr(filename),\n                    extension,\n                    new_path_next,\n                    255);\n                furi_string_cat_printf(\n                    dir_path, \"/%s%s\", furi_string_get_cstr(new_path_next), extension);\n                furi_string_set(new_path_next, dir_path);\n\n                furi_string_free(dir_path);\n                furi_string_free(filename);\n                new_path_tmp = furi_string_get_cstr(new_path_next);\n            } else {\n                new_path_tmp = new_path;\n            }\n            Stream* stream_from = file_stream_alloc(storage);\n            Stream* stream_to = file_stream_alloc(storage);\n\n            do {\n                if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n                if(!file_stream_open(stream_to, new_path_tmp, FSAM_WRITE, FSOM_CREATE_NEW)) break;\n                stream_copy_full(stream_from, stream_to);\n            } while(false);\n\n            error = file_stream_get_error(stream_from);\n            if(error == FSE_OK) {\n                error = file_stream_get_error(stream_to);\n            }\n\n            stream_free(stream_from);\n            stream_free(stream_to);\n        }\n    }\n\n    furi_string_free(new_path_next);\n\n    return error;\n}\n\nFS_Error storage_common_mkdir(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {\n        .path = {\n            .path = path,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonMkDir);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_common_fs_info(\n    Storage* storage,\n    const char* fs_path,\n    uint64_t* total_space,\n    uint64_t* free_space) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .cfsinfo = {\n            .fs_path = fs_path,\n            .total_space = total_space,\n            .free_space = free_space,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonFSInfo);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nvoid storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .cresolvepath = {\n            .path = path,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonResolvePath);\n    S_API_EPILOGUE;\n}\n\nFS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest) {\n    furi_check(storage);\n\n    if(!storage_common_exists(storage, source)) {\n        return FSE_OK;\n    }\n\n    FS_Error error = storage_common_merge(storage, source, dest);\n\n    if(error == FSE_OK) {\n        storage_simply_remove_recursive(storage, source);\n    }\n\n    return error;\n}\n\nbool storage_common_exists(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    FileInfo file_info;\n    return storage_common_stat(storage, path, &file_info) == FSE_OK;\n}\n\nstatic bool storage_internal_equivalent_path(\n    Storage* storage,\n    const char* path1,\n    const char* path2,\n    bool check_subdir) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n\n    SAData data = {\n        .cequivpath = {\n            .path1 = path1,\n            .path2 = path2,\n            .check_subdir = check_subdir,\n            .thread_id = furi_thread_get_current_id(),\n        }};\n\n    S_API_MESSAGE(StorageCommandCommonEquivalentPath);\n    S_API_EPILOGUE;\n\n    return S_RETURN_BOOL;\n}\n\nbool storage_common_equivalent_path(Storage* storage, const char* path1, const char* path2) {\n    return storage_internal_equivalent_path(storage, path1, path2, false);\n}\n\nbool storage_common_is_subdir(Storage* storage, const char* parent, const char* child) {\n    return storage_internal_equivalent_path(storage, parent, child, true);\n}\n\n/****************** ERROR ******************/\n\nconst char* storage_error_get_desc(FS_Error error_id) {\n    return filesystem_api_error_get_desc(error_id);\n}\n\nFS_Error storage_file_get_error(File* file) {\n    furi_check(file);\n    return file->error_id;\n}\n\nint32_t storage_file_get_internal_error(File* file) {\n    furi_check(file);\n    return file->internal_error_id;\n}\n\nconst char* storage_file_get_error_desc(File* file) {\n    furi_check(file);\n    return filesystem_api_error_get_desc(file->error_id);\n}\n\n/****************** Raw SD API ******************/\n\nFS_Error storage_sd_format(Storage* storage) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {};\n    S_API_MESSAGE(StorageCommandSDFormat);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_sd_unmount(Storage* storage) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {};\n    S_API_MESSAGE(StorageCommandSDUnmount);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_sd_mount(Storage* storage) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {};\n    S_API_MESSAGE(StorageCommandSDMount);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_sd_info(Storage* storage, SDInfo* info) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {\n        .sdinfo = {\n            .info = info,\n        }};\n    S_API_MESSAGE(StorageCommandSDInfo);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFS_Error storage_sd_status(Storage* storage) {\n    furi_check(storage);\n\n    S_API_PROLOGUE;\n    SAData data = {};\n    S_API_MESSAGE(StorageCommandSDStatus);\n    S_API_EPILOGUE;\n    return S_RETURN_ERROR;\n}\n\nFile* storage_file_alloc(Storage* storage) {\n    furi_check(storage);\n\n    File* file = malloc(sizeof(File));\n    file->type = FileTypeClosed;\n    file->storage = storage;\n\n    FURI_LOG_T(TAG, \"File/Dir %p alloc\", (void*)((uint32_t)file - SRAM_BASE));\n\n    return file;\n}\n\nbool storage_file_is_open(File* file) {\n    furi_check(file);\n    return file->type != FileTypeClosed;\n}\n\nbool storage_file_is_dir(File* file) {\n    furi_check(file);\n    return file->type == FileTypeOpenDir;\n}\n\nvoid storage_file_free(File* file) {\n    furi_check(file);\n\n    if(storage_file_is_open(file)) {\n        if(storage_file_is_dir(file)) {\n            storage_dir_close(file);\n        } else {\n            storage_file_close(file);\n        }\n    }\n\n    FURI_LOG_T(TAG, \"File/Dir %p free\", (void*)((uint32_t)file - SRAM_BASE));\n    free(file);\n}\n\nFuriPubSub* storage_get_pubsub(Storage* storage) {\n    furi_check(storage);\n    return storage->pubsub;\n}\n\nbool storage_simply_remove_recursive(Storage* storage, const char* path) {\n    furi_check(storage);\n    furi_check(path);\n    FileInfo fileinfo;\n    bool result = false;\n    FuriString* fullname;\n    FuriString* cur_dir;\n\n    if(storage_simply_remove(storage, path)) {\n        return true;\n    }\n\n    char* name = malloc(MAX_NAME_LENGTH + 1); //-V799\n    File* dir = storage_file_alloc(storage);\n    cur_dir = furi_string_alloc_set(path);\n    bool go_deeper = false;\n\n    while(1) {\n        if(!storage_dir_open(dir, furi_string_get_cstr(cur_dir))) {\n            storage_dir_close(dir);\n            break;\n        }\n\n        while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {\n            if(file_info_is_dir(&fileinfo)) {\n                furi_string_cat_printf(cur_dir, \"/%s\", name); //-V576\n                go_deeper = true;\n                break;\n            }\n\n            fullname = furi_string_alloc_printf(\"%s/%s\", furi_string_get_cstr(cur_dir), name);\n            FS_Error error = storage_common_remove(storage, furi_string_get_cstr(fullname));\n            furi_check(error == FSE_OK);\n            furi_string_free(fullname);\n        }\n        storage_dir_close(dir);\n\n        if(go_deeper) {\n            go_deeper = false;\n            continue;\n        }\n\n        FS_Error error = storage_common_remove(storage, furi_string_get_cstr(cur_dir));\n        furi_check(error == FSE_OK);\n\n        if(furi_string_cmp(cur_dir, path)) {\n            size_t last_char = furi_string_search_rchar(cur_dir, '/');\n            furi_assert(last_char != FURI_STRING_FAILURE);\n            furi_string_left(cur_dir, last_char);\n        } else {\n            result = true;\n            break;\n        }\n    }\n\n    storage_file_free(dir);\n    furi_string_free(cur_dir);\n    free(name);\n    return result;\n} //-V773\n\nbool storage_simply_remove(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    FS_Error result;\n    result = storage_common_remove(storage, path);\n    return result == FSE_OK || result == FSE_NOT_EXIST;\n}\n\nbool storage_simply_mkdir(Storage* storage, const char* path) {\n    furi_check(storage);\n\n    FS_Error result;\n    result = storage_common_mkdir(storage, path);\n    return result == FSE_OK || result == FSE_EXIST;\n}\n\nvoid storage_get_next_filename(\n    Storage* storage,\n    const char* dirname,\n    const char* filename,\n    const char* fileextension,\n    FuriString* nextfilename,\n    uint8_t max_len) {\n    furi_check(storage);\n\n    FuriString* temp_str;\n    uint16_t num = 0;\n\n    temp_str = furi_string_alloc_printf(\"%s/%s%s\", dirname, filename, fileextension);\n\n    while(storage_common_stat(storage, furi_string_get_cstr(temp_str), NULL) == FSE_OK) {\n        num++;\n        furi_string_printf(temp_str, \"%s/%s%d%s\", dirname, filename, num, fileextension);\n    }\n    if(num && (max_len > strlen(filename))) {\n        furi_string_printf(nextfilename, \"%s%d\", filename, num);\n    } else {\n        furi_string_printf(nextfilename, \"%s\", filename);\n    }\n\n    furi_string_free(temp_str);\n}\n"
  },
  {
    "path": "applications/services/storage/storage_glue.c",
    "content": "#include \"storage_glue.h\"\n#include <furi_hal.h>\n\n#define TAG \"StorageGlue\"\n\n/****************** storage file ******************/\n\nvoid storage_file_init(StorageFile* obj) {\n    obj->file = NULL;\n    obj->file_data = NULL;\n    obj->path = furi_string_alloc();\n}\n\nvoid storage_file_init_set(StorageFile* obj, const StorageFile* src) {\n    obj->file = src->file;\n    obj->file_data = src->file_data;\n    obj->path = furi_string_alloc_set(src->path);\n}\n\nvoid storage_file_set(StorageFile* obj, const StorageFile* src) { //-V524\n    obj->file = src->file;\n    obj->file_data = src->file_data;\n    furi_string_set(obj->path, src->path);\n}\n\nvoid storage_file_clear(StorageFile* obj) {\n    furi_string_free(obj->path);\n}\n\n/****************** storage data ******************/\n\nvoid storage_data_init(StorageData* storage) {\n    storage->data = NULL;\n    storage->status = StorageStatusNotReady;\n    StorageFileList_init(storage->files);\n}\n\nStorageStatus storage_data_status(StorageData* storage) {\n    return storage->status;\n}\n\nconst char* storage_data_status_text(StorageData* storage) {\n    const char* result = \"unknown\";\n    switch(storage->status) {\n    case StorageStatusOK:\n        result = \"ok\";\n        break;\n    case StorageStatusNotReady:\n        result = \"not ready\";\n        break;\n    case StorageStatusNotMounted:\n        result = \"not mounted\";\n        break;\n    case StorageStatusNoFS:\n        result = \"no filesystem\";\n        break;\n    case StorageStatusNotAccessible:\n        result = \"not accessible\";\n        break;\n    case StorageStatusErrorInternal:\n        result = \"internal\";\n        break;\n    }\n\n    return result;\n}\n\nvoid storage_data_timestamp(StorageData* storage) {\n    storage->timestamp = furi_hal_rtc_get_timestamp();\n}\n\nuint32_t storage_data_get_timestamp(StorageData* storage) {\n    return storage->timestamp;\n}\n\n/****************** storage glue ******************/\n\nstatic StorageFile* storage_get_file(const File* file, StorageData* storage) {\n    StorageFile* storage_file_ref = NULL;\n\n    StorageFileList_it_t it;\n    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);\n        StorageFileList_next(it)) {\n        StorageFile* storage_file = StorageFileList_ref(it);\n\n        if(storage_file->file->file_id == file->file_id) {\n            storage_file_ref = storage_file;\n            break;\n        }\n    }\n\n    return storage_file_ref;\n}\n\nbool storage_has_file(const File* file, StorageData* storage) {\n    return storage_get_file(file, storage) != NULL;\n}\n\nbool storage_path_already_open(FuriString* path, StorageData* storage) {\n    bool open = false;\n\n    StorageFileList_it_t it;\n\n    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);\n        StorageFileList_next(it)) {\n        const StorageFile* storage_file = StorageFileList_cref(it);\n\n        if(furi_string_cmp(storage_file->path, path) == 0) {\n            open = true;\n            break;\n        }\n    }\n\n    return open;\n}\n\nvoid storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage) {\n    StorageFile* storage_file_ref = storage_get_file(file, storage);\n    furi_check(storage_file_ref != NULL);\n    storage_file_ref->file_data = file_data;\n}\n\nvoid* storage_get_storage_file_data(const File* file, StorageData* storage) {\n    StorageFile* storage_file_ref = storage_get_file(file, storage);\n    furi_check(storage_file_ref != NULL);\n    return storage_file_ref->file_data;\n}\n\nvoid storage_push_storage_file(File* file, FuriString* path, StorageData* storage) {\n    StorageFile* storage_file = StorageFileList_push_new(storage->files);\n    file->file_id = (uint32_t)storage_file;\n    storage_file->file = file;\n    furi_string_set(storage_file->path, path);\n}\n\nbool storage_pop_storage_file(File* file, StorageData* storage) {\n    StorageFileList_it_t it;\n    bool result = false;\n\n    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);\n        StorageFileList_next(it)) {\n        if(StorageFileList_cref(it)->file->file_id == file->file_id) {\n            result = true;\n            break;\n        }\n    }\n\n    if(result) {\n        StorageFileList_remove(storage->files, it);\n    }\n\n    return result;\n}\n\nsize_t storage_open_files_count(StorageData* storage) {\n    size_t count = StorageFileList_size(storage->files);\n    return count;\n}\n"
  },
  {
    "path": "applications/services/storage/storage_glue.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include \"filesystem_api_internal.h\"\n#include <m-list.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    ST_EXT = 0,\n    ST_INT = 1,\n    ST_ANY,\n    ST_ERROR\n} StorageType;\n\ntypedef struct StorageData StorageData;\n\ntypedef struct {\n    void (*tick)(StorageData* storage);\n} StorageApi;\n\ntypedef struct {\n    File* file;\n    void* file_data;\n    FuriString* path;\n} StorageFile;\n\ntypedef enum {\n    StorageStatusOK, /**< storage ok */\n    StorageStatusNotReady, /**< storage not ready (not initialized or waiting for data storage to appear) */\n    StorageStatusNotMounted, /**< datastore appeared, but we cannot mount it */\n    StorageStatusNoFS, /**< datastore appeared and mounted, but does not have a file system */\n    StorageStatusNotAccessible, /**< datastore appeared and mounted, but not available */\n    StorageStatusErrorInternal, /**< any other internal error */\n} StorageStatus;\n\nvoid storage_file_init(StorageFile* obj);\nvoid storage_file_init_set(StorageFile* obj, const StorageFile* src);\nvoid storage_file_set(StorageFile* obj, const StorageFile* src);\nvoid storage_file_clear(StorageFile* obj);\n\nvoid storage_data_init(StorageData* storage);\nStorageStatus storage_data_status(StorageData* storage);\nconst char* storage_data_status_text(StorageData* storage);\nvoid storage_data_timestamp(StorageData* storage);\nuint32_t storage_data_get_timestamp(StorageData* storage);\n\nLIST_DEF(\n    StorageFileList,\n    StorageFile,\n    (INIT(API_2(storage_file_init)),\n     SET(API_6(storage_file_init_set)),\n     INIT_SET(API_6(storage_file_set)),\n     CLEAR(API_2(storage_file_clear))))\n\nstruct StorageData {\n    const FS_Api* fs_api;\n    StorageApi api;\n    void* data;\n    StorageStatus status;\n    StorageFileList_t files;\n    uint32_t timestamp;\n};\n\nbool storage_has_file(const File* file, StorageData* storage_data);\nbool storage_path_already_open(FuriString* path, StorageData* storage_data);\n\nvoid storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage);\nvoid* storage_get_storage_file_data(const File* file, StorageData* storage);\n\nvoid storage_push_storage_file(File* file, FuriString* path, StorageData* storage);\nbool storage_pop_storage_file(File* file, StorageData* storage);\n\nsize_t storage_open_files_count(StorageData* storage);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage_i.h",
    "content": "#pragma once\n#include <furi.h>\n#include <furi_hal.h>\n#include <gui/gui.h>\n#include \"storage_glue.h\"\n#include \"storage_sd_api.h\"\n#include \"filesystem_api_internal.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define STORAGE_COUNT (ST_INT + 1)\n\n#define APPS_DATA_PATH   EXT_PATH(\"apps_data\")\n#define APPS_ASSETS_PATH EXT_PATH(\"apps_assets\")\n\ntypedef struct {\n    ViewPort* view_port;\n    bool enabled;\n} StorageSDGui;\n\nstruct Storage {\n    FuriMessageQueue* message_queue;\n    StorageData storage[STORAGE_COUNT];\n    StorageSDGui sd_gui;\n    FuriPubSub* pubsub;\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage_internal_api.c",
    "content": "#include <core/record.h>\n#include \"storage.h\"\n#include <toolbox/tar/tar_archive.h>\n\nFS_Error storage_int_backup(Storage* storage, const char* dstname) {\n    furi_check(storage);\n\n    TarArchive* archive = tar_archive_alloc(storage);\n    bool success = tar_archive_open(archive, dstname, TarOpenModeWrite) &&\n                   tar_archive_add_dir(archive, STORAGE_INT_PATH_PREFIX, \"\") &&\n                   tar_archive_finalize(archive);\n    tar_archive_free(archive);\n    return success ? FSE_OK : FSE_INTERNAL;\n}\n\nFS_Error\n    storage_int_restore(Storage* storage, const char* srcname, StorageNameConverter converter) {\n    furi_check(storage);\n\n    TarArchive* archive = tar_archive_alloc(storage);\n    bool success = tar_archive_open(archive, srcname, TarOpenModeRead) &&\n                   tar_archive_unpack_to(archive, STORAGE_INT_PATH_PREFIX, converter);\n    tar_archive_free(archive);\n    return success ? FSE_OK : FSE_INTERNAL;\n}\n"
  },
  {
    "path": "applications/services/storage/storage_internal_dirname_i.h",
    "content": "#pragma once\n\n#define STORAGE_INTERNAL_DIR_NAME \".int\"\n"
  },
  {
    "path": "applications/services/storage/storage_message.h",
    "content": "#pragma once\n#include <furi.h>\n#include <toolbox/api_lock.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    File* file;\n    const char* path;\n    FS_AccessMode access_mode;\n    FS_OpenMode open_mode;\n    FuriThreadId thread_id;\n} SADataFOpen;\n\ntypedef struct {\n    File* file;\n    void* buff;\n    uint16_t bytes_to_read;\n} SADataFRead;\n\ntypedef struct {\n    File* file;\n    const void* buff;\n    uint16_t bytes_to_write;\n} SADataFWrite;\n\ntypedef struct {\n    File* file;\n    uint32_t offset;\n    bool from_start;\n} SADataFSeek;\n\ntypedef struct {\n    File* file;\n    const char* path;\n    FuriThreadId thread_id;\n} SADataDOpen;\n\ntypedef struct {\n    File* file;\n    FileInfo* fileinfo;\n    char* name;\n    uint16_t name_length;\n} SADataDRead;\n\ntypedef struct {\n    const char* path;\n    uint32_t* timestamp;\n    FuriThreadId thread_id;\n} SADataCTimestamp;\n\ntypedef struct {\n    const char* path;\n    FileInfo* fileinfo;\n    FuriThreadId thread_id;\n} SADataCStat;\n\ntypedef struct {\n    const char* fs_path;\n    uint64_t* total_space;\n    uint64_t* free_space;\n    FuriThreadId thread_id;\n} SADataCFSInfo;\n\ntypedef struct {\n    FuriString* path;\n    FuriThreadId thread_id;\n} SADataCResolvePath;\n\ntypedef struct {\n    const char* path1;\n    const char* path2;\n    bool check_subdir;\n    FuriThreadId thread_id;\n} SADataCEquivPath;\n\ntypedef struct {\n    uint32_t id;\n} SADataError;\n\ntypedef struct {\n    const char* path;\n    FuriThreadId thread_id;\n} SADataPath;\n\ntypedef struct {\n    File* file;\n} SADataFile;\n\ntypedef struct {\n    SDInfo* info;\n} SAInfo;\n\ntypedef union {\n    SADataFOpen fopen;\n    SADataFRead fread;\n    SADataFWrite fwrite;\n    SADataFSeek fseek;\n\n    SADataDOpen dopen;\n    SADataDRead dread;\n\n    SADataCTimestamp ctimestamp;\n    SADataCStat cstat;\n    SADataCFSInfo cfsinfo;\n    SADataCResolvePath cresolvepath;\n    SADataCEquivPath cequivpath;\n\n    SADataError error;\n\n    SADataFile file;\n    SADataPath path;\n\n    SAInfo sdinfo;\n} SAData;\n\ntypedef union {\n    bool bool_value;\n    uint16_t uint16_value;\n    uint64_t uint64_value;\n    FS_Error error_value;\n    const char* cstring_value;\n} SAReturn;\n\ntypedef enum {\n    StorageCommandFileOpen,\n    StorageCommandFileClose,\n    StorageCommandFileRead,\n    StorageCommandFileWrite,\n    StorageCommandFileSeek,\n    StorageCommandFileTell,\n    StorageCommandFileTruncate,\n    StorageCommandFileSize,\n    StorageCommandFileSync,\n    StorageCommandFileEof,\n    StorageCommandDirOpen,\n    StorageCommandDirClose,\n    StorageCommandDirRead,\n    StorageCommandDirRewind,\n    StorageCommandCommonTimestamp,\n    StorageCommandCommonStat,\n    StorageCommandCommonRemove,\n    StorageCommandCommonMkDir,\n    StorageCommandCommonFSInfo,\n    StorageCommandSDFormat,\n    StorageCommandSDUnmount,\n    StorageCommandSDInfo,\n    StorageCommandSDStatus,\n    StorageCommandCommonResolvePath,\n    StorageCommandSDMount,\n    StorageCommandCommonEquivalentPath,\n} StorageCommand;\n\ntypedef struct {\n    FuriApiLock lock;\n    StorageCommand command;\n    SAData* data;\n    SAReturn* return_data;\n} StorageMessage;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage_processing.c",
    "content": "#include <m-list.h>\n#include <m-dict.h>\n\n#include \"storage_processing.h\"\n#include \"storage_internal_dirname_i.h\"\n\n#define TAG \"Storage\"\n\n#define STORAGE_PATH_PREFIX_LEN 4u\n_Static_assert(\n    sizeof(STORAGE_ANY_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,\n    \"Any path prefix len mismatch\");\n_Static_assert(\n    sizeof(STORAGE_EXT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,\n    \"Ext path prefix len mismatch\");\n_Static_assert(\n    sizeof(STORAGE_INT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,\n    \"Int path prefix len mismatch\");\n\n#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;\n\nstatic bool storage_type_is_valid(StorageType type) {\n#ifdef FURI_RAM_EXEC\n    return type == ST_EXT;\n#else\n    return type < ST_ERROR;\n#endif\n}\n\nstatic StorageData* get_storage_by_file(File* file, StorageData* storages) {\n    StorageData* storage_data = NULL;\n\n    for(uint8_t i = 0; i < STORAGE_COUNT; i++) {\n        if(storage_has_file(file, &storages[i])) {\n            storage_data = &storages[i];\n        }\n    }\n\n    return storage_data;\n}\n\nstatic const char* cstr_path_without_vfs_prefix(FuriString* path) {\n    const char* path_cstr = furi_string_get_cstr(path);\n    return path_cstr + MIN(STORAGE_PATH_PREFIX_LEN, strlen(path_cstr));\n}\n\nstatic StorageType storage_get_type_by_path(FuriString* path) {\n    StorageType type = ST_ERROR;\n    const char* path_cstr = furi_string_get_cstr(path);\n\n    if(furi_string_size(path) > STORAGE_PATH_PREFIX_LEN) {\n        if(path_cstr[STORAGE_PATH_PREFIX_LEN] != '/') {\n            return ST_ERROR;\n        }\n    }\n\n    if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {\n        type = ST_EXT;\n    } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {\n        type = ST_INT;\n    } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {\n        type = ST_ANY;\n    }\n\n    return type;\n}\n\nstatic FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {\n    StorageType type = storage_get_type_by_path(path);\n\n    if(storage_type_is_valid(type)) {\n        // Any storage phase-out: redirect \"/any\" to \"/ext\"\n        if(type == ST_ANY) {\n            FURI_LOG_W(\n                TAG,\n                STORAGE_ANY_PATH_PREFIX \" is deprecated, use \" STORAGE_EXT_PATH_PREFIX \" instead\");\n            furi_string_replace_at(\n                path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);\n            type = ST_EXT;\n        }\n\n        furi_assert(type == ST_EXT);\n\n        if(storage_data_status(&app->storage[type]) != StorageStatusOK) {\n            return FSE_NOT_READY;\n        }\n\n        *storage = &app->storage[type];\n\n        return FSE_OK;\n    } else {\n        return FSE_INVALID_NAME;\n    }\n}\n\nstatic void storage_path_trim_trailing_slashes(FuriString* path) {\n    while(furi_string_end_with(path, \"/\")) {\n        furi_string_left(path, furi_string_size(path) - 1);\n    }\n}\n\n/******************* File Functions *******************/\n\nbool storage_process_file_open(\n    Storage* app,\n    File* file,\n    FuriString* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    bool ret = false;\n    StorageData* storage;\n    file->error_id = storage_get_data(app, path, &storage);\n\n    if(file->error_id == FSE_OK) {\n        if(storage_path_already_open(path, storage)) {\n            file->error_id = FSE_ALREADY_OPEN;\n        } else {\n            if(access_mode & FSAM_WRITE) {\n                storage_data_timestamp(storage);\n            }\n            storage_push_storage_file(file, path, storage);\n\n            const char* path_cstr_no_vfs = cstr_path_without_vfs_prefix(path);\n            FS_CALL(storage, file.open(storage, file, path_cstr_no_vfs, access_mode, open_mode));\n        }\n    }\n\n    return ret;\n}\n\nbool storage_process_file_close(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.close(storage, file));\n        storage_pop_storage_file(file, storage);\n\n        StorageEvent event = {.type = StorageEventTypeFileClose};\n        furi_pubsub_publish(app->pubsub, &event);\n    }\n\n    return ret;\n}\n\nstatic uint16_t\n    storage_process_file_read(Storage* app, File* file, void* buff, uint16_t const bytes_to_read) {\n    uint16_t ret = 0;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.read(storage, file, buff, bytes_to_read));\n    }\n\n    return ret;\n}\n\nstatic uint16_t storage_process_file_write(\n    Storage* app,\n    File* file,\n    const void* buff,\n    uint16_t const bytes_to_write) {\n    uint16_t ret = 0;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        storage_data_timestamp(storage);\n        FS_CALL(storage, file.write(storage, file, buff, bytes_to_write));\n    }\n\n    return ret;\n}\n\nstatic bool storage_process_file_seek(\n    Storage* app,\n    File* file,\n    const uint32_t offset,\n    const bool from_start) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.seek(storage, file, offset, from_start));\n    }\n\n    return ret;\n}\n\nstatic uint64_t storage_process_file_tell(Storage* app, File* file) {\n    uint64_t ret = 0;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.tell(storage, file));\n    }\n\n    return ret;\n}\n\nstatic bool storage_process_file_truncate(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        storage_data_timestamp(storage);\n        FS_CALL(storage, file.truncate(storage, file));\n    }\n\n    return ret;\n}\n\nstatic bool storage_process_file_sync(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        storage_data_timestamp(storage);\n        FS_CALL(storage, file.sync(storage, file));\n    }\n\n    return ret;\n}\n\nstatic uint64_t storage_process_file_size(Storage* app, File* file) {\n    uint64_t ret = 0;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.size(storage, file));\n    }\n\n    return ret;\n}\n\nstatic bool storage_process_file_eof(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, file.eof(storage, file));\n    }\n\n    return ret;\n}\n\n/******************* Dir Functions *******************/\n\nbool storage_process_dir_open(Storage* app, File* file, FuriString* path) {\n    bool ret = false;\n    StorageData* storage;\n    file->error_id = storage_get_data(app, path, &storage);\n\n    if(file->error_id == FSE_OK) {\n        if(storage_path_already_open(path, storage)) {\n            file->error_id = FSE_ALREADY_OPEN;\n        } else {\n            storage_push_storage_file(file, path, storage);\n            FS_CALL(storage, dir.open(storage, file, cstr_path_without_vfs_prefix(path)));\n        }\n    }\n\n    return ret;\n}\n\nbool storage_process_dir_close(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, dir.close(storage, file));\n        storage_pop_storage_file(file, storage);\n\n        StorageEvent event = {.type = StorageEventTypeDirClose};\n        furi_pubsub_publish(app->pubsub, &event);\n    }\n\n    return ret;\n}\n\nbool storage_process_dir_read(\n    Storage* app,\n    File* file,\n    FileInfo* fileinfo,\n    char* name,\n    const uint16_t name_length) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, dir.read(storage, file, fileinfo, name, name_length));\n    }\n\n    return ret;\n}\n\nbool storage_process_dir_rewind(Storage* app, File* file) {\n    bool ret = false;\n    StorageData* storage = get_storage_by_file(file, app->storage);\n\n    if(storage == NULL) {\n        file->error_id = FSE_INVALID_PARAMETER;\n    } else {\n        FS_CALL(storage, dir.rewind(storage, file));\n    }\n\n    return ret;\n}\n\n/******************* Common FS Functions *******************/\n\nstatic FS_Error\n    storage_process_common_timestamp(Storage* app, FuriString* path, uint32_t* timestamp) {\n    StorageData* storage;\n    FS_Error ret = storage_get_data(app, path, &storage);\n\n    if(ret == FSE_OK) {\n        *timestamp = storage_data_get_timestamp(storage);\n    }\n\n    return ret;\n}\n\nstatic FS_Error storage_process_common_stat(Storage* app, FuriString* path, FileInfo* fileinfo) {\n    StorageData* storage;\n    FS_Error ret = storage_get_data(app, path, &storage);\n\n    if(ret == FSE_OK) {\n        FS_CALL(storage, common.stat(storage, cstr_path_without_vfs_prefix(path), fileinfo));\n    }\n\n    return ret;\n}\n\nstatic FS_Error storage_process_common_remove(Storage* app, FuriString* path) {\n    StorageData* storage;\n    FS_Error ret = storage_get_data(app, path, &storage);\n\n    do {\n        if(ret != FSE_OK) break;\n\n        if(storage_path_already_open(path, storage)) {\n            ret = FSE_ALREADY_OPEN;\n            break;\n        }\n\n        storage_data_timestamp(storage);\n        FS_CALL(storage, common.remove(storage, cstr_path_without_vfs_prefix(path)));\n    } while(false);\n\n    return ret;\n}\n\nstatic FS_Error storage_process_common_mkdir(Storage* app, FuriString* path) {\n    StorageData* storage;\n    FS_Error ret = storage_get_data(app, path, &storage);\n\n    if(ret == FSE_OK) {\n        storage_data_timestamp(storage);\n        FS_CALL(storage, common.mkdir(storage, cstr_path_without_vfs_prefix(path)));\n    }\n\n    return ret;\n}\n\nstatic FS_Error storage_process_common_fs_info(\n    Storage* app,\n    FuriString* path,\n    uint64_t* total_space,\n    uint64_t* free_space) {\n    StorageData* storage;\n    FS_Error ret = storage_get_data(app, path, &storage);\n\n    if(ret == FSE_OK) {\n        FS_CALL(\n            storage,\n            common.fs_info(storage, cstr_path_without_vfs_prefix(path), total_space, free_space));\n    }\n\n    return ret;\n}\n\nstatic bool\n    storage_process_common_equivalent_path(Storage* app, FuriString* path1, FuriString* path2) {\n    bool ret = false;\n\n    do {\n        const StorageType storage_type1 = storage_get_type_by_path(path1);\n        const StorageType storage_type2 = storage_get_type_by_path(path2);\n\n        // Paths on different storages are of course not equal\n        if(storage_type1 != storage_type2) break;\n\n        StorageData* storage;\n        const FS_Error status = storage_get_data(app, path1, &storage);\n\n        if(status != FSE_OK) break;\n\n        FS_CALL(\n            storage,\n            common.equivalent_path(furi_string_get_cstr(path1), furi_string_get_cstr(path2)));\n\n    } while(false);\n\n    return ret;\n}\n\n/****************** Raw SD API ******************/\n// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage\n#include \"storages/storage_ext.h\"\n\nstatic FS_Error storage_process_sd_format(Storage* app) {\n    FS_Error ret = FSE_OK;\n\n    if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {\n        ret = FSE_NOT_READY;\n    } else {\n        ret = sd_format_card(&app->storage[ST_EXT]);\n        storage_data_timestamp(&app->storage[ST_EXT]);\n    }\n\n    return ret;\n}\n\nstatic FS_Error storage_process_sd_unmount(Storage* app) {\n    FS_Error ret = FSE_OK;\n\n    do {\n        StorageData* storage = &app->storage[ST_EXT];\n        if(storage_data_status(storage) == StorageStatusNotReady) {\n            ret = FSE_NOT_READY;\n            break;\n        }\n\n        if(storage_open_files_count(storage)) {\n            ret = FSE_DENIED;\n            break;\n        }\n\n        sd_unmount_card(storage);\n        storage_data_timestamp(storage);\n    } while(false);\n\n    return ret;\n}\n\nstatic FS_Error storage_process_sd_mount(Storage* app) {\n    FS_Error ret = FSE_OK;\n\n    do {\n        StorageData* storage = &app->storage[ST_EXT];\n        if(storage_data_status(storage) != StorageStatusNotReady) {\n            ret = FSE_NOT_READY;\n            break;\n        }\n\n        ret = sd_mount_card(storage, true);\n        storage_data_timestamp(storage);\n    } while(false);\n\n    return ret;\n}\n\nstatic FS_Error storage_process_sd_info(Storage* app, SDInfo* info) {\n    FS_Error ret = FSE_OK;\n\n    if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {\n        ret = FSE_NOT_READY;\n    } else {\n        ret = sd_card_info(&app->storage[ST_EXT], info);\n    }\n\n    return ret;\n}\n\nstatic FS_Error storage_process_sd_status(Storage* app) {\n    FS_Error ret;\n    StorageStatus status = storage_data_status(&app->storage[ST_EXT]);\n\n    switch(status) {\n    case StorageStatusOK:\n        ret = FSE_OK;\n        break;\n    case StorageStatusNotReady:\n        ret = FSE_NOT_READY;\n        break;\n    default:\n        ret = FSE_INTERNAL;\n        break;\n    }\n\n    return ret;\n}\n\n/******************** Aliases processing *******************/\n\nvoid storage_process_alias(\n    Storage* app,\n    FuriString* path,\n    FuriThreadId thread_id,\n    bool create_folders) {\n    if(furi_string_start_with(path, STORAGE_APP_DATA_PATH_PREFIX)) {\n        FuriString* apps_data_path_with_appsid = furi_string_alloc_set(APPS_DATA_PATH \"/\");\n        furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));\n\n        // \"/data\" -> \"/ext/apps_data/appsid\"\n        furi_string_replace_at(\n            path,\n            0,\n            strlen(STORAGE_APP_DATA_PATH_PREFIX),\n            furi_string_get_cstr(apps_data_path_with_appsid));\n\n        // Create app data folder if not exists\n        if(create_folders &&\n           storage_process_common_stat(app, apps_data_path_with_appsid, NULL) != FSE_OK) {\n            furi_string_set(apps_data_path_with_appsid, APPS_DATA_PATH);\n            storage_process_common_mkdir(app, apps_data_path_with_appsid);\n            furi_string_cat(apps_data_path_with_appsid, \"/\");\n            furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));\n            storage_process_common_mkdir(app, apps_data_path_with_appsid);\n        }\n\n        furi_string_free(apps_data_path_with_appsid);\n    } else if(furi_string_start_with(path, STORAGE_APP_ASSETS_PATH_PREFIX)) {\n        FuriString* apps_assets_path_with_appsid = furi_string_alloc_set(APPS_ASSETS_PATH \"/\");\n        furi_string_cat(apps_assets_path_with_appsid, furi_thread_get_appid(thread_id));\n\n        // \"/assets\" -> \"/ext/apps_assets/appsid\"\n        furi_string_replace_at(\n            path,\n            0,\n            strlen(STORAGE_APP_ASSETS_PATH_PREFIX),\n            furi_string_get_cstr(apps_assets_path_with_appsid));\n\n        furi_string_free(apps_assets_path_with_appsid);\n\n    } else if(furi_string_start_with(path, STORAGE_INT_PATH_PREFIX)) {\n        furi_string_replace_at(\n            path, 0, strlen(STORAGE_INT_PATH_PREFIX), EXT_PATH(STORAGE_INTERNAL_DIR_NAME));\n\n        FuriString* int_on_ext_path = furi_string_alloc_set(EXT_PATH(STORAGE_INTERNAL_DIR_NAME));\n        if(storage_process_common_stat(app, int_on_ext_path, NULL) != FSE_OK) {\n            storage_process_common_mkdir(app, int_on_ext_path);\n        }\n        furi_string_free(int_on_ext_path);\n    }\n}\n\n/****************** API calls processing ******************/\n\nvoid storage_process_message_internal(Storage* app, StorageMessage* message) {\n    FuriString* path = NULL;\n\n    switch(message->command) {\n    // File operations\n    case StorageCommandFileOpen:\n        path = furi_string_alloc_set(message->data->fopen.path);\n        storage_process_alias(app, path, message->data->fopen.thread_id, true);\n        message->return_data->bool_value = storage_process_file_open(\n            app,\n            message->data->fopen.file,\n            path,\n            message->data->fopen.access_mode,\n            message->data->fopen.open_mode);\n        break;\n    case StorageCommandFileClose:\n        message->return_data->bool_value =\n            storage_process_file_close(app, message->data->fopen.file);\n        break;\n    case StorageCommandFileRead:\n        message->return_data->uint16_value = storage_process_file_read(\n            app,\n            message->data->fread.file,\n            message->data->fread.buff,\n            message->data->fread.bytes_to_read);\n        break;\n    case StorageCommandFileWrite:\n        message->return_data->uint16_value = storage_process_file_write(\n            app,\n            message->data->fwrite.file,\n            message->data->fwrite.buff,\n            message->data->fwrite.bytes_to_write);\n        break;\n    case StorageCommandFileSeek:\n        message->return_data->bool_value = storage_process_file_seek(\n            app,\n            message->data->fseek.file,\n            message->data->fseek.offset,\n            message->data->fseek.from_start);\n        break;\n    case StorageCommandFileTell:\n        message->return_data->uint64_value =\n            storage_process_file_tell(app, message->data->file.file);\n        break;\n    case StorageCommandFileTruncate:\n        message->return_data->bool_value =\n            storage_process_file_truncate(app, message->data->file.file);\n        break;\n    case StorageCommandFileSync:\n        message->return_data->bool_value =\n            storage_process_file_sync(app, message->data->file.file);\n        break;\n    case StorageCommandFileSize:\n        message->return_data->uint64_value =\n            storage_process_file_size(app, message->data->file.file);\n        break;\n    case StorageCommandFileEof:\n        message->return_data->bool_value = storage_process_file_eof(app, message->data->file.file);\n        break;\n\n    // Dir operations\n    case StorageCommandDirOpen:\n        path = furi_string_alloc_set(message->data->dopen.path);\n        storage_process_alias(app, path, message->data->dopen.thread_id, true);\n        message->return_data->bool_value =\n            storage_process_dir_open(app, message->data->dopen.file, path);\n        break;\n    case StorageCommandDirClose:\n        message->return_data->bool_value =\n            storage_process_dir_close(app, message->data->file.file);\n        break;\n    case StorageCommandDirRead:\n        message->return_data->bool_value = storage_process_dir_read(\n            app,\n            message->data->dread.file,\n            message->data->dread.fileinfo,\n            message->data->dread.name,\n            message->data->dread.name_length);\n        break;\n    case StorageCommandDirRewind:\n        message->return_data->bool_value =\n            storage_process_dir_rewind(app, message->data->file.file);\n        break;\n\n    // Common operations\n    case StorageCommandCommonTimestamp:\n        path = furi_string_alloc_set(message->data->ctimestamp.path);\n        storage_process_alias(app, path, message->data->ctimestamp.thread_id, false);\n        message->return_data->error_value =\n            storage_process_common_timestamp(app, path, message->data->ctimestamp.timestamp);\n        break;\n    case StorageCommandCommonStat:\n        path = furi_string_alloc_set(message->data->cstat.path);\n        storage_process_alias(app, path, message->data->cstat.thread_id, false);\n        message->return_data->error_value =\n            storage_process_common_stat(app, path, message->data->cstat.fileinfo);\n        break;\n    case StorageCommandCommonRemove:\n        path = furi_string_alloc_set(message->data->path.path);\n        storage_process_alias(app, path, message->data->path.thread_id, false);\n        message->return_data->error_value = storage_process_common_remove(app, path);\n        break;\n    case StorageCommandCommonMkDir:\n        path = furi_string_alloc_set(message->data->path.path);\n        storage_process_alias(app, path, message->data->path.thread_id, true);\n        message->return_data->error_value = storage_process_common_mkdir(app, path);\n        break;\n    case StorageCommandCommonFSInfo:\n        path = furi_string_alloc_set(message->data->cfsinfo.fs_path);\n        storage_process_alias(app, path, message->data->cfsinfo.thread_id, false);\n        message->return_data->error_value = storage_process_common_fs_info(\n            app, path, message->data->cfsinfo.total_space, message->data->cfsinfo.free_space);\n        break;\n    case StorageCommandCommonResolvePath:\n        storage_process_alias(\n            app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true);\n        break;\n\n    case StorageCommandCommonEquivalentPath: {\n        FuriString* path1 = furi_string_alloc_set(message->data->cequivpath.path1);\n        FuriString* path2 = furi_string_alloc_set(message->data->cequivpath.path2);\n        storage_path_trim_trailing_slashes(path1);\n        storage_path_trim_trailing_slashes(path2);\n        storage_process_alias(app, path1, message->data->cequivpath.thread_id, false);\n        storage_process_alias(app, path2, message->data->cequivpath.thread_id, false);\n        if(message->data->cequivpath.check_subdir) {\n            // by appending slashes at the end and then truncating the second path, we can\n            // effectively check for shared path components:\n            // example 1:\n            //   path1: \"/ext/blah\"      -> \"/ext/blah/\"      -> \"/ext/blah/\"\n            //   path2: \"/ext/blah-blah\" -> \"/ect/blah-blah/\" -> \"/ext/blah-\"\n            //   results unequal, conclusion: path2 is not a subpath of path1\n            // example 2:\n            //   path1: \"/ext/blah\"      -> \"/ext/blah/\"      -> \"/ext/blah/\"\n            //   path2: \"/ext/blah/blah\" -> \"/ect/blah/blah/\" -> \"/ext/blah/\"\n            //   results equal, conclusion: path2 is a subpath of path1\n            // example 3:\n            //   path1: \"/ext/blah/blah\" -> \"/ect/blah/blah/\" -> \"/ext/blah/blah/\"\n            //   path2: \"/ext/blah\"      -> \"/ext/blah/\"      -> \"/ext/blah/\"\n            //   results unequal, conclusion: path2 is not a subpath of path1\n            furi_string_push_back(path1, '/');\n            furi_string_push_back(path2, '/');\n            furi_string_left(path2, furi_string_size(path1));\n        }\n        message->return_data->bool_value =\n            storage_process_common_equivalent_path(app, path1, path2);\n        furi_string_free(path1);\n        furi_string_free(path2);\n        break;\n    }\n\n    // SD operations\n    case StorageCommandSDFormat:\n        message->return_data->error_value = storage_process_sd_format(app);\n        break;\n    case StorageCommandSDUnmount:\n        message->return_data->error_value = storage_process_sd_unmount(app);\n        break;\n    case StorageCommandSDMount:\n        message->return_data->error_value = storage_process_sd_mount(app);\n        break;\n    case StorageCommandSDInfo:\n        message->return_data->error_value =\n            storage_process_sd_info(app, message->data->sdinfo.info);\n        break;\n    case StorageCommandSDStatus:\n        message->return_data->error_value = storage_process_sd_status(app);\n        break;\n    }\n\n    if(path != NULL) { //-V547\n        furi_string_free(path);\n    }\n\n    api_lock_unlock(message->lock);\n}\n\nvoid storage_process_message(Storage* app, StorageMessage* message) {\n    storage_process_message_internal(app, message);\n}\n"
  },
  {
    "path": "applications/services/storage/storage_processing.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"storage.h\"\n#include \"storage_i.h\"\n#include \"storage_message.h\"\n#include \"storage_glue.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid storage_process_message(Storage* app, StorageMessage* message);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storage_sd_api.c",
    "content": "#include \"storage_sd_api.h\"\n\nconst char* sd_api_get_fs_type_text(SDFsType fs_type) {\n    switch(fs_type) {\n    case(FST_FAT12):\n        return \"FAT12\";\n        break;\n    case(FST_FAT16):\n        return \"FAT16\";\n        break;\n    case(FST_FAT32):\n        return \"FAT32\";\n        break;\n    case(FST_EXFAT):\n        return \"EXFAT\";\n        break;\n    default:\n        return \"UNKNOWN\";\n        break;\n    }\n}\n"
  },
  {
    "path": "applications/services/storage/storage_sd_api.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"filesystem_api_defines.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define SD_LABEL_LENGTH 34\n\ntypedef enum {\n    FST_UNKNOWN,\n    FST_FAT12,\n    FST_FAT16,\n    FST_FAT32,\n    FST_EXFAT,\n} SDFsType;\n\ntypedef struct {\n    SDFsType fs_type;\n    uint32_t kb_total;\n    uint32_t kb_free;\n    uint16_t cluster_size;\n    uint16_t sector_size;\n    char label[SD_LABEL_LENGTH];\n\n    uint8_t manufacturer_id;\n    char oem_id[3];\n    char product_name[6];\n    uint8_t product_revision_major;\n    uint8_t product_revision_minor;\n    uint32_t product_serial_number;\n    uint8_t manufacturing_month;\n    uint16_t manufacturing_year;\n} SDInfo;\n\nconst char* sd_api_get_fs_type_text(SDFsType fs_type);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storages/sd_notify.c",
    "content": "#include \"sd_notify.h\"\n\nstatic const NotificationSequence sd_sequence_success = {\n    &message_green_255,\n    &message_delay_50,\n    &message_green_0,\n    &message_delay_50,\n    &message_green_255,\n    &message_delay_50,\n    &message_green_0,\n    &message_delay_50,\n    &message_green_255,\n    &message_delay_50,\n    &message_green_0,\n    &message_delay_50,\n    NULL,\n};\n\nstatic const NotificationSequence sd_sequence_error = {\n    &message_red_255,\n    &message_delay_50,\n    &message_red_0,\n    &message_delay_50,\n    &message_red_255,\n    &message_delay_50,\n    &message_red_0,\n    &message_delay_50,\n    &message_red_255,\n    &message_delay_50,\n    &message_red_0,\n    &message_delay_50,\n    NULL,\n};\n\nstatic const NotificationSequence sd_sequence_eject = {\n    &message_blue_255,\n    &message_delay_50,\n    &message_blue_0,\n    &message_delay_50,\n    &message_blue_255,\n    &message_delay_50,\n    &message_blue_0,\n    &message_delay_50,\n    &message_blue_255,\n    &message_delay_50,\n    &message_blue_0,\n    &message_delay_50,\n    NULL,\n};\n\nstatic const NotificationSequence sd_sequence_wait = {\n    &message_red_255,\n    &message_blue_255,\n    &message_do_not_reset,\n    NULL,\n};\n\nstatic const NotificationSequence sd_sequence_wait_off = {\n    &message_red_0,\n    &message_blue_0,\n    NULL,\n};\n\nvoid sd_notify_wait(NotificationApp* notifications) {\n    notification_message(notifications, &sd_sequence_wait);\n}\n\nvoid sd_notify_wait_off(NotificationApp* notifications) {\n    notification_message(notifications, &sd_sequence_wait_off);\n}\n\nvoid sd_notify_success(NotificationApp* notifications) {\n    notification_message(notifications, &sd_sequence_success);\n}\n\nvoid sd_notify_eject(NotificationApp* notifications) {\n    notification_message(notifications, &sd_sequence_eject);\n}\n\nvoid sd_notify_error(NotificationApp* notifications) {\n    notification_message(notifications, &sd_sequence_error);\n}\n"
  },
  {
    "path": "applications/services/storage/storages/sd_notify.h",
    "content": "#pragma once\n#include <furi.h>\n#include <notification/notification_messages.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid sd_notify_wait(NotificationApp* notifications);\nvoid sd_notify_wait_off(NotificationApp* notifications);\nvoid sd_notify_success(NotificationApp* notifications);\nvoid sd_notify_eject(NotificationApp* notifications);\nvoid sd_notify_error(NotificationApp* notifications);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/services/storage/storages/storage_ext.c",
    "content": "#include <fatfs.h>\n#include <furi_hal.h>\n#include <furi_hal_sd.h>\n\n#include \"sd_notify.h\"\n#include \"storage_ext.h\"\n\n#include \"../filesystem_api_internal.h\"\n#include \"../storage_internal_dirname_i.h\"\n\ntypedef FIL SDFile;\ntypedef DIR SDDir;\ntypedef FILINFO SDFileInfo;\ntypedef FRESULT SDError;\n\n#define TAG \"StorageExt\"\n\n/********************* Definitions ********************/\n\ntypedef struct {\n    FATFS* fs;\n    const char* path;\n    bool sd_was_present;\n} SDData;\n\nstatic FS_Error storage_ext_parse_error(SDError error);\n\n/******************* Core Functions *******************/\n\nstatic bool sd_mount_card_internal(StorageData* storage, bool notify) {\n    bool result = false;\n    uint8_t counter = furi_hal_sd_max_mount_retry_count();\n    uint8_t bsp_result;\n    SDData* sd_data = storage->data;\n\n    while(result == false && counter > 0 && furi_hal_sd_is_present()) {\n        if(notify) {\n            NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n            sd_notify_wait(notification);\n            furi_record_close(RECORD_NOTIFICATION);\n        }\n\n        if((counter % 2) == 0) {\n            // power reset sd card\n            bsp_result = furi_hal_sd_init(true);\n        } else {\n            bsp_result = furi_hal_sd_init(false);\n        }\n\n        if(bsp_result) {\n            // bsp error\n            storage->status = StorageStatusErrorInternal;\n        } else {\n            SDError status = f_mount(sd_data->fs, sd_data->path, 1);\n\n            if(status == FR_OK || status == FR_NO_FILESYSTEM) {\n#ifndef FURI_RAM_EXEC\n                FATFS* fs;\n                uint32_t free_clusters;\n\n                status = f_getfree(sd_data->path, &free_clusters, &fs);\n#endif\n\n                if(status == FR_OK || status == FR_NO_FILESYSTEM) {\n                    result = true;\n                }\n\n                if(status == FR_OK) {\n                    storage->status = StorageStatusOK;\n                } else if(status == FR_NO_FILESYSTEM) {\n                    storage->status = StorageStatusNoFS;\n                } else {\n                    storage->status = StorageStatusNotAccessible;\n                }\n            } else {\n                storage->status = StorageStatusNotMounted;\n            }\n        }\n\n        if(notify) {\n            NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n            sd_notify_wait_off(notification);\n            furi_record_close(RECORD_NOTIFICATION);\n        }\n\n        if(!result) {\n            furi_delay_ms(1000);\n            FURI_LOG_E(\n                TAG, \"init cycle %d, error: %s\", counter, storage_data_status_text(storage));\n            counter--;\n        }\n    }\n\n    storage_data_timestamp(storage);\n\n    return result;\n}\n\nstatic bool sd_remove_recursive(const char* path) {\n    SDDir* current_dir = malloc(sizeof(DIR));\n    SDFileInfo* file_info = malloc(sizeof(FILINFO));\n    FuriString* current_path = furi_string_alloc_set(path);\n\n    bool go_deeper = false;\n    SDError status;\n\n    while(true) {\n        status = f_opendir(current_dir, furi_string_get_cstr(current_path));\n        if(status != FR_OK) break;\n\n        while(true) {\n            status = f_readdir(current_dir, file_info);\n            if(status != FR_OK || !strlen(file_info->fname)) break;\n\n            if(file_info->fattrib & AM_DIR) {\n                furi_string_cat_printf(current_path, \"/%s\", file_info->fname);\n                go_deeper = true;\n                break;\n\n            } else {\n                FuriString* file_path = furi_string_alloc_printf(\n                    \"%s/%s\", furi_string_get_cstr(current_path), file_info->fname);\n                status = f_unlink(furi_string_get_cstr(file_path));\n                furi_string_free(file_path);\n\n                if(status != FR_OK) break;\n            }\n        }\n\n        status = f_closedir(current_dir);\n        if(status != FR_OK) break;\n\n        if(go_deeper) {\n            go_deeper = false;\n            continue;\n        }\n\n        status = f_unlink(furi_string_get_cstr(current_path));\n        if(status != FR_OK) break;\n\n        if(!furi_string_equal(current_path, path)) {\n            size_t last_char_pos = furi_string_search_rchar(current_path, '/');\n            furi_assert(last_char_pos != FURI_STRING_FAILURE);\n            furi_string_left(current_path, last_char_pos);\n        } else {\n            break;\n        }\n    }\n\n    free(current_dir);\n    free(file_info);\n    furi_string_free(current_path);\n\n    return status == FR_OK;\n}\n\nFS_Error sd_unmount_card(StorageData* storage) {\n    SDData* sd_data = storage->data;\n    SDError error;\n\n    storage->status = StorageStatusNotReady;\n    error = FR_DISK_ERR;\n\n    // TODO FL-3522: do i need to close the files?\n    f_mount(0, sd_data->path, 0);\n\n    return storage_ext_parse_error(error);\n}\n\nFS_Error sd_mount_card(StorageData* storage, bool notify) {\n    sd_mount_card_internal(storage, notify);\n    FS_Error error;\n\n    if(storage->status != StorageStatusOK) {\n        FURI_LOG_E(TAG, \"sd init error: %s\", storage_data_status_text(storage));\n        error = FSE_INTERNAL;\n\n    } else {\n        FURI_LOG_I(TAG, \"card mounted\");\n\n#ifndef FURI_RAM_EXEC\n        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal)) {\n            FURI_LOG_I(TAG, \"deleting internal storage directory\");\n            error = sd_remove_recursive(STORAGE_INTERNAL_DIR_NAME) ? FSE_OK : FSE_INTERNAL;\n        } else {\n            error = FSE_OK;\n        }\n#else\n        UNUSED(sd_remove_recursive);\n        error = FSE_OK;\n#endif\n    }\n\n    if(notify) {\n        NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n        if(error != FSE_OK) {\n            sd_notify_error(notification);\n        } else {\n            sd_notify_success(notification);\n        }\n        furi_record_close(RECORD_NOTIFICATION);\n    }\n\n    return error;\n}\n\nFS_Error sd_format_card(StorageData* storage) {\n#ifdef FURI_RAM_EXEC\n    UNUSED(storage);\n    return FSE_NOT_READY;\n#else\n    uint8_t* work_area;\n    SDData* sd_data = storage->data;\n    SDError error;\n\n    work_area = malloc(_MAX_SS);\n    error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS);\n    free(work_area);\n\n    do {\n        storage->status = StorageStatusNotAccessible;\n        if(error != FR_OK) break;\n        storage->status = StorageStatusNoFS;\n        error = f_setlabel(\"Flipper SD\");\n        if(error != FR_OK) break;\n        storage->status = StorageStatusNotMounted;\n        error = f_mount(sd_data->fs, sd_data->path, 1);\n        if(error != FR_OK) break;\n        storage->status = StorageStatusOK;\n    } while(false);\n\n    return storage_ext_parse_error(error);\n#endif\n}\n\nFS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {\n#ifndef FURI_RAM_EXEC\n    uint32_t free_clusters, free_sectors, total_sectors;\n    FATFS* fs;\n#endif\n    SDData* sd_data = storage->data;\n    SDError error;\n\n    // clean data\n    memset(sd_info, 0, sizeof(SDInfo));\n\n    // get fs info\n    error = f_getlabel(sd_data->path, sd_info->label, NULL);\n    if(error == FR_OK) {\n#ifndef FURI_RAM_EXEC\n        error = f_getfree(sd_data->path, &free_clusters, &fs);\n#endif\n    }\n\n    if(error == FR_OK) {\n        // calculate size\n#ifndef FURI_RAM_EXEC\n        total_sectors = (fs->n_fatent - 2) * fs->csize;\n        free_sectors = free_clusters * fs->csize;\n#endif\n\n        uint16_t sector_size = _MAX_SS;\n#if _MAX_SS != _MIN_SS\n        sector_size = fs->ssize;\n#endif\n\n#ifdef FURI_RAM_EXEC\n        sd_info->fs_type = 0;\n        sd_info->kb_total = 0;\n        sd_info->kb_free = 0;\n        sd_info->cluster_size = 512;\n        sd_info->sector_size = sector_size;\n#else\n        sd_info->fs_type = fs->fs_type;\n        switch(fs->fs_type) {\n        case FS_FAT12:\n            sd_info->fs_type = FST_FAT12;\n            break;\n        case FS_FAT16:\n            sd_info->fs_type = FST_FAT16;\n            break;\n        case FS_FAT32:\n            sd_info->fs_type = FST_FAT32;\n            break;\n        case FS_EXFAT:\n            sd_info->fs_type = FST_EXFAT;\n            break;\n        default:\n            sd_info->fs_type = FST_UNKNOWN;\n            break;\n        }\n\n        sd_info->kb_total = total_sectors / 1024 * sector_size;\n        sd_info->kb_free = free_sectors / 1024 * sector_size;\n        sd_info->cluster_size = fs->csize;\n        sd_info->sector_size = sector_size;\n#endif\n    }\n\n    FuriHalSdInfo info;\n    FuriStatus status = furi_hal_sd_info(&info);\n\n    if(status == FuriStatusOk) {\n        sd_info->manufacturer_id = info.manufacturer_id;\n        memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id));\n        memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name));\n        sd_info->product_revision_major = info.product_revision_major;\n        sd_info->product_revision_minor = info.product_revision_minor;\n        sd_info->product_serial_number = info.product_serial_number;\n        sd_info->manufacturing_year = info.manufacturing_year;\n        sd_info->manufacturing_month = info.manufacturing_month;\n    }\n\n    return storage_ext_parse_error(error);\n}\n\nstatic void storage_ext_tick_internal(StorageData* storage, bool notify) {\n    SDData* sd_data = storage->data;\n\n    if(sd_data->sd_was_present) {\n        if(furi_hal_sd_is_present()) {\n            FURI_LOG_I(TAG, \"card detected\");\n            sd_data->sd_was_present = false;\n            sd_mount_card(storage, notify);\n\n            if(!furi_hal_sd_is_present()) {\n                FURI_LOG_I(TAG, \"card removed while mounting\");\n                sd_unmount_card(storage);\n                sd_data->sd_was_present = true;\n            }\n        }\n    } else {\n        if(!furi_hal_sd_is_present()) {\n            FURI_LOG_I(TAG, \"card removed\");\n            sd_data->sd_was_present = true;\n\n            sd_unmount_card(storage);\n            if(notify) {\n                NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n                sd_notify_eject(notification);\n                furi_record_close(RECORD_NOTIFICATION);\n            }\n        }\n    }\n}\n\nstatic void storage_ext_tick(StorageData* storage) {\n    storage_ext_tick_internal(storage, true);\n}\n\n/****************** Common Functions ******************/\n\nstatic FS_Error storage_ext_parse_error(SDError error) {\n    FS_Error result;\n    switch(error) {\n    case FR_OK:\n        result = FSE_OK;\n        break;\n    case FR_NOT_READY:\n        result = FSE_NOT_READY;\n        break;\n    case FR_NO_FILE:\n    case FR_NO_PATH:\n    case FR_NO_FILESYSTEM:\n        result = FSE_NOT_EXIST;\n        break;\n    case FR_EXIST:\n        result = FSE_EXIST;\n        break;\n    case FR_INVALID_NAME:\n        result = FSE_INVALID_NAME;\n        break;\n    case FR_INVALID_OBJECT:\n    case FR_INVALID_PARAMETER:\n        result = FSE_INVALID_PARAMETER;\n        break;\n    case FR_DENIED:\n        result = FSE_DENIED;\n        break;\n    default:\n        result = FSE_INTERNAL;\n        break;\n    }\n\n    return result;\n}\n\n/******************* File Functions *******************/\n\nstatic bool storage_ext_file_open(\n    void* ctx,\n    File* file,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    StorageData* storage = ctx;\n    uint8_t _mode = 0;\n\n    if(access_mode & FSAM_READ) _mode |= FA_READ;\n    if(access_mode & FSAM_WRITE) _mode |= FA_WRITE;\n    if(open_mode & FSOM_OPEN_EXISTING) _mode |= FA_OPEN_EXISTING;\n    if(open_mode & FSOM_OPEN_ALWAYS) _mode |= FA_OPEN_ALWAYS;\n    if(open_mode & FSOM_OPEN_APPEND) _mode |= FA_OPEN_APPEND;\n    if(open_mode & FSOM_CREATE_NEW) _mode |= FA_CREATE_NEW;\n    if(open_mode & FSOM_CREATE_ALWAYS) _mode |= FA_CREATE_ALWAYS;\n\n    SDFile* file_data = malloc(sizeof(SDFile));\n    storage_set_storage_file_data(file, file_data, storage);\n\n    file->internal_error_id = f_open(file_data, path, _mode);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n}\n\nstatic bool storage_ext_file_close(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n    file->internal_error_id = f_close(file_data);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    free(file_data);\n    storage_set_storage_file_data(file, NULL, storage);\n    return file->error_id == FSE_OK;\n}\n\nstatic uint16_t\n    storage_ext_file_read(void* ctx, File* file, void* buff, uint16_t const bytes_to_read) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n    uint16_t bytes_read = 0;\n    file->internal_error_id = f_read(file_data, buff, bytes_to_read, &bytes_read);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return bytes_read;\n}\n\nstatic uint16_t\n    storage_ext_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) {\n#ifdef FURI_RAM_EXEC\n    UNUSED(ctx);\n    UNUSED(file);\n    UNUSED(buff);\n    UNUSED(bytes_to_write);\n    return FSE_NOT_READY;\n#else\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n    uint16_t bytes_written = 0;\n    file->internal_error_id = f_write(file_data, buff, bytes_to_write, &bytes_written);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return bytes_written;\n#endif\n}\n\nstatic bool\n    storage_ext_file_seek(void* ctx, File* file, const uint32_t offset, const bool from_start) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    if(from_start) {\n        file->internal_error_id = f_lseek(file_data, offset);\n    } else {\n        uint64_t position = f_tell(file_data);\n        position += offset;\n        file->internal_error_id = f_lseek(file_data, position);\n    }\n\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n}\n\nstatic uint64_t storage_ext_file_tell(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    uint64_t position = 0;\n    position = f_tell(file_data);\n    file->error_id = FSE_OK;\n    return position;\n}\n\nstatic bool storage_ext_file_truncate(void* ctx, File* file) {\n#ifdef FURI_RAM_EXEC\n    UNUSED(ctx);\n    UNUSED(file);\n    return FSE_NOT_READY;\n#else\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    file->internal_error_id = f_truncate(file_data);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n#endif\n}\n\nstatic bool storage_ext_file_sync(void* ctx, File* file) {\n#ifdef FURI_RAM_EXEC\n    UNUSED(ctx);\n    UNUSED(file);\n    return FSE_NOT_READY;\n#else\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    file->internal_error_id = f_sync(file_data);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n#endif\n}\n\nstatic uint64_t storage_ext_file_size(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    uint64_t size = 0;\n    size = f_size(file_data);\n    file->error_id = FSE_OK;\n    return size;\n}\n\nstatic bool storage_ext_file_eof(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDFile* file_data = storage_get_storage_file_data(file, storage);\n\n    bool eof = f_eof(file_data);\n    file->internal_error_id = 0;\n    file->error_id = FSE_OK;\n    return eof;\n}\n\n/******************* Dir Functions *******************/\n\nstatic bool storage_ext_dir_open(void* ctx, File* file, const char* path) {\n    StorageData* storage = ctx;\n\n    SDDir* file_data = malloc(sizeof(SDDir));\n    storage_set_storage_file_data(file, file_data, storage);\n    file->internal_error_id = f_opendir(file_data, path);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n}\n\nstatic bool storage_ext_dir_close(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDDir* file_data = storage_get_storage_file_data(file, storage);\n\n    file->internal_error_id = f_closedir(file_data);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    free(file_data);\n    return file->error_id == FSE_OK;\n}\n\nstatic bool storage_ext_dir_read(\n    void* ctx,\n    File* file,\n    FileInfo* fileinfo,\n    char* name,\n    const uint16_t name_length) {\n    StorageData* storage = ctx;\n    SDDir* file_data = storage_get_storage_file_data(file, storage);\n\n    SDFileInfo _fileinfo;\n    file->internal_error_id = f_readdir(file_data, &_fileinfo);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n\n    if(fileinfo != NULL) {\n        fileinfo->size = _fileinfo.fsize;\n        fileinfo->flags = 0;\n\n        if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;\n    }\n\n    if(name != NULL) {\n        snprintf(name, name_length, \"%s\", _fileinfo.fname);\n    }\n\n    if(_fileinfo.fname[0] == 0) {\n        file->error_id = FSE_NOT_EXIST;\n    }\n\n    return file->error_id == FSE_OK;\n}\n\nstatic bool storage_ext_dir_rewind(void* ctx, File* file) {\n    StorageData* storage = ctx;\n    SDDir* file_data = storage_get_storage_file_data(file, storage);\n\n    file->internal_error_id = f_readdir(file_data, NULL);\n    file->error_id = storage_ext_parse_error(file->internal_error_id);\n    return file->error_id == FSE_OK;\n}\n/******************* Common FS Functions *******************/\n\nstatic FS_Error storage_ext_common_stat(void* ctx, const char* path, FileInfo* fileinfo) {\n    UNUSED(ctx);\n    SDFileInfo _fileinfo;\n    SDError result = f_stat(path, &_fileinfo);\n\n    if(fileinfo != NULL) {\n        fileinfo->size = _fileinfo.fsize;\n        fileinfo->flags = 0;\n\n        if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;\n    }\n\n    return storage_ext_parse_error(result);\n}\n\nstatic FS_Error storage_ext_common_remove(void* ctx, const char* path) {\n    UNUSED(ctx);\n#ifdef FURI_RAM_EXEC\n    UNUSED(path);\n    return FSE_NOT_READY;\n#else\n    SDError result = f_unlink(path);\n    return storage_ext_parse_error(result);\n#endif\n}\n\nstatic FS_Error storage_ext_common_mkdir(void* ctx, const char* path) {\n    UNUSED(ctx);\n#ifdef FURI_RAM_EXEC\n    UNUSED(path);\n    return FSE_NOT_READY;\n#else\n    SDError result = f_mkdir(path);\n    return storage_ext_parse_error(result);\n#endif\n}\n\nstatic FS_Error storage_ext_common_fs_info(\n    void* ctx,\n    const char* fs_path,\n    uint64_t* total_space,\n    uint64_t* free_space) {\n    UNUSED(fs_path);\n#ifdef FURI_RAM_EXEC\n    UNUSED(ctx);\n    UNUSED(total_space);\n    UNUSED(free_space);\n    return FSE_NOT_READY;\n#else\n    StorageData* storage = ctx;\n    SDData* sd_data = storage->data;\n\n    DWORD free_clusters;\n    FATFS* fs;\n\n    SDError fresult = f_getfree(sd_data->path, &free_clusters, &fs);\n    if((FRESULT)fresult == FR_OK) {\n        uint32_t total_sectors = (fs->n_fatent - 2) * fs->csize;\n        uint32_t free_sectors = free_clusters * fs->csize;\n\n        uint16_t sector_size = _MAX_SS;\n#if _MAX_SS != _MIN_SS\n        sector_size = fs->ssize;\n#endif\n\n        if(total_space != NULL) {\n            *total_space = (uint64_t)total_sectors * (uint64_t)sector_size;\n        }\n\n        if(free_space != NULL) {\n            *free_space = (uint64_t)free_sectors * (uint64_t)sector_size;\n        }\n    }\n\n    return storage_ext_parse_error(fresult);\n#endif\n}\n\nstatic bool storage_ext_common_equivalent_path(const char* path1, const char* path2) {\n#ifdef FURI_RAM_EXEC\n    UNUSED(path1);\n    UNUSED(path2);\n    return false;\n#else\n    return strcasecmp(path1, path2) == 0;\n#endif\n}\n\n/******************* Init Storage *******************/\nstatic const FS_Api fs_api = {\n    .file =\n        {\n            .open = storage_ext_file_open,\n            .close = storage_ext_file_close,\n            .read = storage_ext_file_read,\n            .write = storage_ext_file_write,\n            .seek = storage_ext_file_seek,\n            .tell = storage_ext_file_tell,\n            .truncate = storage_ext_file_truncate,\n            .size = storage_ext_file_size,\n            .sync = storage_ext_file_sync,\n            .eof = storage_ext_file_eof,\n        },\n    .dir =\n        {\n            .open = storage_ext_dir_open,\n            .close = storage_ext_dir_close,\n            .read = storage_ext_dir_read,\n            .rewind = storage_ext_dir_rewind,\n        },\n    .common =\n        {\n            .stat = storage_ext_common_stat,\n            .mkdir = storage_ext_common_mkdir,\n            .remove = storage_ext_common_remove,\n            .fs_info = storage_ext_common_fs_info,\n            .equivalent_path = storage_ext_common_equivalent_path,\n        },\n};\n\nvoid storage_ext_init(StorageData* storage) {\n    fatfs_init();\n\n    SDData* sd_data = malloc(sizeof(SDData));\n    sd_data->fs = &fatfs_object;\n    sd_data->path = \"0:/\";\n    sd_data->sd_was_present = true;\n\n    storage->data = sd_data;\n    storage->api.tick = storage_ext_tick;\n    storage->fs_api = &fs_api;\n\n    furi_hal_sd_presence_init();\n\n    // do not notify on first launch, notifications app is waiting for our thread to read settings\n    storage_ext_tick_internal(storage, false);\n#ifndef FURI_RAM_EXEC\n    // always reset the flag to prevent accidental wipe on SD card insertion\n    furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);\n#endif\n}\n"
  },
  {
    "path": "applications/services/storage/storages/storage_ext.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"../storage_glue.h\"\n#include \"../storage_sd_api.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid storage_ext_init(StorageData* storage);\nFS_Error sd_mount_card(StorageData* storage, bool notify);\nFS_Error sd_unmount_card(StorageData* storage);\nFS_Error sd_format_card(StorageData* storage);\nFS_Error sd_card_info(StorageData* storage, SDInfo* sd_info);\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/settings/about/about.c",
    "content": "#include <furi.h>\n\n#include <gui/gui.h>\n#include <gui/view_holder.h>\n#include <gui/modules/empty_screen.h>\n\n#include <dialogs/dialogs.h>\n#include <assets_icons.h>\n\n#include <furi_hal_version.h>\n#include <furi_hal_region.h>\n#include <furi_hal_bt.h>\n#include <furi_hal_info.h>\n\ntypedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message);\n\nstatic DialogMessageButton about_screen_product(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    FuriString* screen_header = furi_string_alloc_printf(\n        \"Product: %s\\n\"\n        \"Model: %s\",\n        furi_hal_version_get_model_name(),\n        furi_hal_version_get_model_code());\n\n    FuriString* screen_text = furi_string_alloc_printf(\n        \"FCC ID: %s\\n\"\n        \"IC: %s\",\n        furi_hal_version_get_fcc_id(),\n        furi_hal_version_get_ic_id());\n\n    dialog_message_set_header(\n        message, furi_string_get_cstr(screen_header), 0, 0, AlignLeft, AlignTop);\n    dialog_message_set_text(\n        message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop);\n    result = dialog_message_show(dialogs, message);\n\n    furi_string_free(screen_header);\n    furi_string_free(screen_text);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_address(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    const char* screen_text = \"Flipper Devices Inc.\\n\"\n                              \"Suite B #551, 2803\\n\"\n                              \"Philadelphia Pike, Claymont\\n\"\n                              \"DE, USA 19703\\n\";\n\n    dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_compliance(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    const char* screen_text = \"For all compliance\\n\"\n                              \"certificates, please visit:\\n\"\n                              \"www.flipp.dev/compliance\";\n\n    dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_icon1(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_Certification1_103x56, 13, 0);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_icon2(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10);\n    dialog_message_set_text(\n        message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_cert_china_0(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_CertificationChina0_121x41, 3, 3);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_cert_china_1(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_CertificationChina1_124x47, 3, 3);\n    dialog_message_set_text(\n        message, furi_hal_version_get_srrc_id(), 55, 11, AlignLeft, AlignBottom);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_cert_taiwan(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_CertificationTaiwan_33x32, 3, 10);\n    dialog_message_set_text(\n        message, furi_hal_version_get_ncc_id(), 39, 30, AlignLeft, AlignBottom);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_cert_mexico(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n\n    dialog_message_set_icon(message, &I_CertificationMexico_98x41, 17, 4);\n    result = dialog_message_show(dialogs, message);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_hw_version(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n    FuriString* buffer;\n    buffer = furi_string_alloc();\n    const char* my_name = furi_hal_version_get_name_ptr();\n\n    furi_string_cat_printf(\n        buffer,\n        \"%d.F%dB%dC%d %s:%s %s\\n\",\n        furi_hal_version_get_hw_version(),\n        furi_hal_version_get_hw_target(),\n        furi_hal_version_get_hw_body(),\n        furi_hal_version_get_hw_connect(),\n        furi_hal_version_get_hw_region_name(),\n        furi_hal_region_get_name(),\n        my_name ? my_name : \"Unknown\");\n\n    furi_string_cat_printf(buffer, \"Serial Number:\\n\");\n    const uint8_t* uid = furi_hal_version_uid();\n    for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {\n        furi_string_cat_printf(buffer, \"%02X\", uid[i]);\n    }\n\n    dialog_message_set_header(message, \"HW Version Info:\", 0, 0, AlignLeft, AlignTop);\n    dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);\n    result = dialog_message_show(dialogs, message);\n    furi_string_free(buffer);\n\n    return result;\n}\n\nstatic DialogMessageButton about_screen_fw_version(DialogsApp* dialogs, DialogMessage* message) {\n    DialogMessageButton result;\n    FuriString* buffer;\n    buffer = furi_string_alloc();\n    const Version* ver = furi_hal_version_get_firmware_version();\n    const BleGlueC2Info* c2_ver = NULL;\n#ifdef SRV_BT\n    c2_ver = ble_glue_get_c2_info();\n#endif\n\n    if(!ver) { //-V1051\n        furi_string_cat_printf(buffer, \"No info\\n\");\n    } else {\n        uint16_t api_major, api_minor;\n        furi_hal_info_get_api_version(&api_major, &api_minor);\n        furi_string_cat_printf(\n            buffer,\n            \"%s [%s]\\n%s%s [%d.%d] %s\\n[%d] %s\",\n            version_get_version(ver),\n            version_get_builddate(ver),\n            version_get_dirty_flag(ver) ? \"[!] \" : \"\",\n            version_get_githash(ver),\n            api_major,\n            api_minor,\n            c2_ver ? c2_ver->StackTypeString : \"<none>\",\n            version_get_target(ver),\n            version_get_gitbranch(ver));\n    }\n\n    dialog_message_set_header(message, \"FW Version Info:\", 0, 0, AlignLeft, AlignTop);\n    dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);\n    result = dialog_message_show(dialogs, message);\n    furi_string_free(buffer);\n\n    return result;\n}\n\nconst AboutDialogScreen about_screens[] = {\n    about_screen_product,\n    about_screen_compliance,\n    about_screen_address,\n    about_screen_icon1,\n    about_screen_icon2,\n    about_screen_cert_china_0,\n    about_screen_cert_china_1,\n    about_screen_cert_taiwan,\n    about_screen_cert_mexico,\n    about_screen_hw_version,\n    about_screen_fw_version,\n};\n\nint32_t about_settings_app(void* p) {\n    UNUSED(p);\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n    DialogMessage* message = dialog_message_alloc();\n\n    Gui* gui = furi_record_open(RECORD_GUI);\n    ViewHolder* view_holder = view_holder_alloc();\n    EmptyScreen* empty_screen = empty_screen_alloc();\n\n    size_t screen_index = 0;\n    DialogMessageButton screen_result;\n\n    // draw empty screen to prevent menu flickering\n    view_holder_attach_to_gui(view_holder, gui);\n    view_holder_set_view(view_holder, empty_screen_get_view(empty_screen));\n\n    while(1) {\n        if(screen_index >= COUNT_OF(about_screens) - 1) {\n            dialog_message_set_buttons(message, \"Prev.\", NULL, NULL);\n        } else if(screen_index == 0) {\n            dialog_message_set_buttons(message, NULL, NULL, \"Next\");\n        } else {\n            dialog_message_set_buttons(message, \"Prev.\", NULL, \"Next\");\n        }\n\n        screen_result = about_screens[screen_index](dialogs, message);\n\n        dialog_message_set_icon(message, NULL, 0, 0);\n        dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);\n        dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);\n\n        if(screen_result == DialogMessageButtonLeft) {\n            if(screen_index <= 0) {\n                break;\n            } else {\n                screen_index--;\n            }\n        } else if(screen_result == DialogMessageButtonRight) {\n            if(screen_index < COUNT_OF(about_screens) - 1) {\n                screen_index++;\n            }\n        } else if(screen_result == DialogMessageButtonBack) {\n            break;\n        }\n    }\n\n    dialog_message_free(message);\n    furi_record_close(RECORD_DIALOGS);\n\n    view_holder_set_view(view_holder, NULL);\n    view_holder_free(view_holder);\n    empty_screen_free(empty_screen);\n    furi_record_close(RECORD_GUI);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/about/application.fam",
    "content": "App(\n    appid=\"about\",\n    name=\"About\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"about_settings_app\",\n    cdefines=[\"APP_ABOUT\"],\n    requires=[\n        \"gui\",\n        \"dialogs\",\n    ],\n    stack_size=1 * 1024,\n    order=1000,\n)\n"
  },
  {
    "path": "applications/settings/application.fam",
    "content": "App(\n    appid=\"settings_apps\",\n    name=\"Basic settings apps bundle\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"passport\",\n        \"system_settings\",\n        \"clock_settings\",\n        \"about\",\n    ],\n)\n"
  },
  {
    "path": "applications/settings/bt_settings_app/application.fam",
    "content": "App(\n    appid=\"bt_settings\",\n    name=\"Bluetooth\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"bt_settings_app\",\n    stack_size=1 * 1024,\n    requires=[\n        \"bt\",\n        \"gui\",\n    ],\n    order=10,\n)\n"
  },
  {
    "path": "applications/settings/bt_settings_app/bt_settings_app.c",
    "content": "#include \"bt_settings_app.h\"\n\nstatic bool bt_settings_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    BtSettingsApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool bt_settings_back_event_callback(void* context) {\n    furi_assert(context);\n    BtSettingsApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nBtSettingsApp* bt_settings_app_alloc(void) {\n    BtSettingsApp* app = malloc(sizeof(BtSettingsApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->bt = furi_record_open(RECORD_BT);\n\n    // View Dispatcher and Scene Manager\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&bt_settings_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, bt_settings_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, bt_settings_back_event_callback);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Gui Modules\n    app->var_item_list = variable_item_list_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        BtSettingsAppViewVarItemList,\n        variable_item_list_get_view(app->var_item_list));\n\n    app->dialog = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BtSettingsAppViewDialog, dialog_ex_get_view(app->dialog));\n\n    app->popup = popup_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, BtSettingsAppViewPopup, popup_get_view(app->popup));\n\n    bt_get_settings(app->bt, &app->settings);\n\n    // Set first scene\n    scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneStart);\n    return app;\n}\n\nvoid bt_settings_app_free(BtSettingsApp* app) {\n    furi_assert(app);\n    bt_set_settings(app->bt, &app->settings);\n    // Gui modules\n    view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewVarItemList);\n    variable_item_list_free(app->var_item_list);\n\n    view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewDialog);\n    dialog_ex_free(app->dialog);\n\n    view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewPopup);\n    popup_free(app->popup);\n\n    // View Dispatcher and Scene Manager\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Records\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_BT);\n    free(app);\n}\n\nextern int32_t bt_settings_app(void* p) {\n    UNUSED(p);\n    BtSettingsApp* app = bt_settings_app_alloc();\n    view_dispatcher_run(app->view_dispatcher);\n    bt_settings_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/bt_settings_app/bt_settings_app.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/popup.h>\n\n#include <bt/bt_service/bt.h>\n#include <bt/bt_service/bt_settings_api_i.h>\n\n#include <assets_icons.h>\n\n#include \"scenes/bt_settings_scene.h\"\n\nenum BtSettingsCustomEvent {\n    // Keep first 10 events reserved for button types and indexes\n    BtSettingsCustomEventReserved = 10,\n\n    BtSettingsCustomEventForgetDevices,\n    BtSettingsCustomEventExitView,\n};\n\ntypedef struct {\n    BtSettings settings;\n    Bt* bt;\n    Gui* gui;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    VariableItemList* var_item_list;\n    DialogEx* dialog;\n    Popup* popup;\n} BtSettingsApp;\n\ntypedef enum {\n    BtSettingsAppViewVarItemList,\n    BtSettingsAppViewDialog,\n    BtSettingsAppViewPopup,\n} BtSettingsAppView;\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene.c",
    "content": "#include \"bt_settings_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const bt_settings_on_enter_handlers[])(void*) = {\n#include \"bt_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const bt_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"bt_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const bt_settings_on_exit_handlers[])(void* context) = {\n#include \"bt_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers bt_settings_scene_handlers = {\n    .on_enter_handlers = bt_settings_on_enter_handlers,\n    .on_event_handlers = bt_settings_on_event_handlers,\n    .on_exit_handlers = bt_settings_on_exit_handlers,\n    .scene_num = BtSettingsAppSceneNum,\n};\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) BtSettingsAppScene##id,\ntypedef enum {\n#include \"bt_settings_scene_config.h\"\n    BtSettingsAppSceneNum,\n} BtSettingsAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers bt_settings_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"bt_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"bt_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"bt_settings_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene_config.h",
    "content": "ADD_SCENE(bt_settings, start, Start)\nADD_SCENE(bt_settings, forget_dev_confirm, ForgetDevConfirm)\nADD_SCENE(bt_settings, forget_dev_success, ForgetDevSuccess)\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c",
    "content": "#include \"../bt_settings_app.h\"\n#include <furi_hal_bt.h>\n\nvoid bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) {\n    furi_assert(context);\n    BtSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid bt_settings_scene_forget_dev_confirm_on_enter(void* context) {\n    BtSettingsApp* app = context;\n    DialogEx* dialog = app->dialog;\n    dialog_ex_set_header(dialog, \"Unpair All Devices?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(\n        dialog, \"All previous pairings\\nwill be lost!\", 64, 14, AlignCenter, AlignTop);\n    dialog_ex_set_left_button_text(dialog, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog, \"Unpair\");\n    dialog_ex_set_context(dialog, app);\n    dialog_ex_set_result_callback(dialog, bt_settings_scene_forget_dev_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewDialog);\n}\n\nbool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEvent event) {\n    BtSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            consumed = scene_manager_previous_scene(app->scene_manager);\n        } else if(event.event == DialogExResultRight) {\n            bt_forget_bonded_devices(app->bt);\n            scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid bt_settings_scene_forget_dev_confirm_on_exit(void* context) {\n    BtSettingsApp* app = context;\n    dialog_ex_reset(app->dialog);\n}\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c",
    "content": "#include \"../bt_settings_app.h\"\n#include <furi_hal_bt.h>\n\nvoid bt_settings_app_scene_forget_dev_success_popup_callback(void* context) {\n    BtSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, BtSettingsCustomEventExitView);\n}\n\nvoid bt_settings_scene_forget_dev_success_on_enter(void* context) {\n    BtSettingsApp* app = context;\n    Popup* popup = app->popup;\n\n    popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);\n    popup_set_header(popup, \"Done\", 14, 15, AlignLeft, AlignTop);\n    popup_set_timeout(popup, 1500);\n    popup_set_context(popup, app);\n    popup_set_callback(popup, bt_settings_app_scene_forget_dev_success_popup_callback);\n    popup_enable_timeout(popup);\n    view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewPopup);\n}\n\nbool bt_settings_scene_forget_dev_success_on_event(void* context, SceneManagerEvent event) {\n    BtSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == BtSettingsCustomEventExitView) {\n            if(scene_manager_has_previous_scene(app->scene_manager, BtSettingsAppSceneStart)) {\n                consumed = scene_manager_search_and_switch_to_previous_scene(\n                    app->scene_manager, BtSettingsAppSceneStart);\n            }\n        }\n    }\n\n    return consumed;\n}\n\nvoid bt_settings_scene_forget_dev_success_on_exit(void* context) {\n    BtSettingsApp* app = context;\n    popup_reset(app->popup);\n}\n"
  },
  {
    "path": "applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c",
    "content": "#include \"../bt_settings_app.h\"\n#include <furi_hal_bt.h>\n\nenum BtSetting {\n    BtSettingOff,\n    BtSettingOn,\n    BtSettingNum,\n};\n\nenum BtSettingIndex {\n    BtSettingIndexSwitchBt,\n    BtSettingIndexForgetDev,\n};\n\nconst char* const bt_settings_text[BtSettingNum] = {\n    \"OFF\",\n    \"ON\",\n};\n\nstatic void bt_settings_scene_start_var_list_change_callback(VariableItem* item) {\n    BtSettingsApp* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, bt_settings_text[index]);\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nstatic void bt_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    BtSettingsApp* app = context;\n    if(index == BtSettingIndexForgetDev) {\n        view_dispatcher_send_custom_event(\n            app->view_dispatcher, BtSettingsCustomEventForgetDevices);\n    }\n}\n\nvoid bt_settings_scene_start_on_enter(void* context) {\n    BtSettingsApp* app = context;\n    VariableItemList* var_item_list = app->var_item_list;\n    VariableItem* item;\n\n    if(furi_hal_bt_is_gatt_gap_supported()) {\n        item = variable_item_list_add(\n            var_item_list,\n            \"Bluetooth\",\n            BtSettingNum,\n            bt_settings_scene_start_var_list_change_callback,\n            app);\n        if(app->settings.enabled) {\n            variable_item_set_current_value_index(item, BtSettingOn);\n            variable_item_set_current_value_text(item, bt_settings_text[BtSettingOn]);\n        } else {\n            variable_item_set_current_value_index(item, BtSettingOff);\n            variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]);\n        }\n        variable_item_list_add(var_item_list, \"Unpair All Devices\", 1, NULL, NULL);\n        variable_item_list_set_enter_callback(\n            var_item_list, bt_settings_scene_start_var_list_enter_callback, app);\n    } else {\n        item = variable_item_list_add(var_item_list, \"Bluetooth\", 1, NULL, NULL);\n        variable_item_set_current_value_text(item, \"Broken\");\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewVarItemList);\n}\n\nbool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) {\n    BtSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == BtSettingOn) {\n            app->settings.enabled = true;\n            consumed = true;\n        } else if(event.event == BtSettingOff) {\n            app->settings.enabled = false;\n            consumed = true;\n        } else if(event.event == BtSettingsCustomEventForgetDevices) {\n            scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevConfirm);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid bt_settings_scene_start_on_exit(void* context) {\n    BtSettingsApp* app = context;\n    variable_item_list_reset(app->var_item_list);\n}\n"
  },
  {
    "path": "applications/settings/clock_settings/application.fam",
    "content": "App(\n    appid=\"clock_settings\",\n    name=\"Clock & Alarm\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"clock_settings\",\n    requires=[\"gui\"],\n    provides=[\"clock_settings_start\"],\n    stack_size=1 * 1024,\n    order=90,\n)\n\nApp(\n    appid=\"clock_settings_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"clock_settings_start\",\n    order=1000,\n)\n"
  },
  {
    "path": "applications/settings/clock_settings/clock_settings.c",
    "content": "#include \"clock_settings.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\nstatic bool clock_settings_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    ClockSettings* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool clock_settings_back_event_callback(void* context) {\n    furi_assert(context);\n    ClockSettings* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nClockSettings* clock_settings_alloc() {\n    ClockSettings* app = malloc(sizeof(ClockSettings));\n\n    app->gui = furi_record_open(RECORD_GUI);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&clock_settings_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, clock_settings_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, clock_settings_back_event_callback);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->pwm_view =\n        clock_settings_module_alloc(view_dispatcher_get_event_loop(app->view_dispatcher));\n    view_dispatcher_add_view(\n        app->view_dispatcher, ClockSettingsViewPwm, clock_settings_module_get_view(app->pwm_view));\n\n    scene_manager_next_scene(app->scene_manager, ClockSettingsSceneStart);\n\n    return app;\n}\n\nvoid clock_settings_free(ClockSettings* app) {\n    furi_assert(app);\n\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, ClockSettingsViewPwm);\n\n    clock_settings_module_free(app->pwm_view);\n\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n\n    free(app);\n}\n\nint32_t clock_settings(void* p) {\n    UNUSED(p);\n    ClockSettings* clock_settings = clock_settings_alloc();\n\n    view_dispatcher_run(clock_settings->view_dispatcher);\n\n    clock_settings_free(clock_settings);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/clock_settings/clock_settings.h",
    "content": "#pragma once\n\n#include \"scenes/clock_settings_scene.h\"\n\n#include <furi_hal_clock.h>\n#include <furi_hal_pwm.h>\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/submenu.h>\n#include \"views/clock_settings_module.h\"\n\ntypedef struct ClockSettings ClockSettings;\n\nstruct ClockSettings {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    ClockSettingsModule* pwm_view;\n};\n\ntypedef enum {\n    ClockSettingsViewPwm,\n} ClockSettingsView;\n\ntypedef enum {\n    ClockSettingsCustomEventNone,\n} ClockSettingsCustomEvent;\n"
  },
  {
    "path": "applications/settings/clock_settings/clock_settings_alarm.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view_port.h>\n\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\n#include <assets_icons.h>\n\n#define TAG \"ClockSettingsAlarm\"\n\ntypedef struct {\n    DateTime now;\n    IconAnimation* icon;\n} ClockSettingsAlramModel;\n\nconst NotificationSequence sequence_alarm = {\n    &message_force_speaker_volume_setting_1f,\n    &message_force_vibro_setting_on,\n    &message_force_display_brightness_setting_1f,\n    &message_vibro_on,\n\n    &message_display_backlight_on,\n    &message_note_c7,\n    &message_delay_250,\n\n    &message_display_backlight_off,\n    &message_note_c4,\n    &message_delay_250,\n\n    &message_display_backlight_on,\n    &message_note_c7,\n    &message_delay_250,\n\n    &message_display_backlight_off,\n    &message_note_c4,\n    &message_delay_250,\n\n    &message_sound_off,\n    &message_vibro_off,\n    NULL,\n};\n\nstatic void clock_settings_alarm_draw_callback(Canvas* canvas, void* ctx) {\n    ClockSettingsAlramModel* model = ctx;\n    char buffer[64] = {};\n\n    canvas_draw_icon_animation(canvas, 5, 6, model->icon);\n\n    canvas_set_font(canvas, FontBigNumbers);\n    snprintf(buffer, sizeof(buffer), \"%02u:%02u\", model->now.hour, model->now.minute);\n    canvas_draw_str(canvas, 58, 32, buffer);\n\n    canvas_set_font(canvas, FontPrimary);\n    snprintf(\n        buffer,\n        sizeof(buffer),\n        \"%02u.%02u.%04u\",\n        model->now.day,\n        model->now.month,\n        model->now.year);\n    canvas_draw_str(canvas, 60, 44, buffer);\n}\n\nstatic void clock_settings_alarm_input_callback(InputEvent* input_event, void* ctx) {\n    furi_assert(ctx);\n    FuriMessageQueue* event_queue = ctx;\n    furi_message_queue_put(event_queue, input_event, FuriWaitForever);\n}\n\nvoid clock_settings_alarm_animation_callback(IconAnimation* instance, void* context) {\n    UNUSED(instance);\n    ViewPort* view_port = context;\n    view_port_update(view_port);\n}\n\nint32_t clock_settings_alarm(void* p) {\n    UNUSED(p);\n\n    // View Model\n    ClockSettingsAlramModel model;\n\n    furi_hal_rtc_get_datetime(&model.now);\n    model.icon = icon_animation_alloc(&A_Alarm_47x39);\n\n    // Alloc message queue\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));\n\n    // Configure view port\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, clock_settings_alarm_draw_callback, &model);\n    view_port_input_callback_set(view_port, clock_settings_alarm_input_callback, event_queue);\n\n    // Register view port in GUI\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_alarm);\n\n    icon_animation_set_update_callback(\n        model.icon, clock_settings_alarm_animation_callback, view_port);\n    icon_animation_start(model.icon);\n\n    // Process events\n    InputEvent event;\n    bool running = true;\n    while(running) {\n        if(furi_message_queue_get(event_queue, &event, 2000) == FuriStatusOk) {\n            if(event.type == InputTypePress) {\n                running = false;\n            }\n        } else {\n            notification_message(notification, &sequence_alarm);\n            furi_hal_rtc_get_datetime(&model.now);\n            view_port_update(view_port);\n        }\n    }\n\n    icon_animation_stop(model.icon);\n\n    notification_message_block(notification, &sequence_empty);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    view_port_enabled_set(view_port, false);\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n    furi_record_close(RECORD_GUI);\n\n    icon_animation_free(model.icon);\n\n    return 0;\n}\n\nFuriThread* clock_settings_alarm_thread = NULL;\n\nstatic void clock_settings_alarm_thread_state_callback(\n    FuriThread* thread,\n    FuriThreadState state,\n    void* context) {\n    furi_assert(clock_settings_alarm_thread == thread);\n    UNUSED(context);\n\n    if(state == FuriThreadStateStopped) {\n        furi_thread_free(thread);\n        clock_settings_alarm_thread = NULL;\n    }\n}\n\nstatic void clock_settings_alarm_start(void* context, uint32_t arg) {\n    UNUSED(context);\n    UNUSED(arg);\n\n    FURI_LOG_I(TAG, \"spawning alarm thread\");\n\n    if(clock_settings_alarm_thread) return;\n\n    clock_settings_alarm_thread =\n        furi_thread_alloc_ex(\"ClockAlarm\", 1024, clock_settings_alarm, NULL);\n    furi_thread_set_state_callback(\n        clock_settings_alarm_thread, clock_settings_alarm_thread_state_callback);\n    furi_thread_start(clock_settings_alarm_thread);\n}\n\nstatic void clock_settings_alarm_isr(void* context) {\n    UNUSED(context);\n    furi_timer_pending_callback(clock_settings_alarm_start, NULL, 0);\n}\n\nvoid clock_settings_start(void) {\n#ifndef FURI_RAM_EXEC\n    furi_hal_rtc_set_alarm_callback(clock_settings_alarm_isr, NULL);\n#endif\n}\n"
  },
  {
    "path": "applications/settings/clock_settings/scenes/clock_settings_scene.c",
    "content": "#include \"../clock_settings.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const clock_settings_scene_on_enter_handlers[])(void*) = {\n#include \"clock_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const clock_settings_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"clock_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const clock_settings_scene_on_exit_handlers[])(void* context) = {\n#include \"clock_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers clock_settings_scene_handlers = {\n    .on_enter_handlers = clock_settings_scene_on_enter_handlers,\n    .on_event_handlers = clock_settings_scene_on_event_handlers,\n    .on_exit_handlers = clock_settings_scene_on_exit_handlers,\n    .scene_num = ClockSettingsSceneNum,\n};\n"
  },
  {
    "path": "applications/settings/clock_settings/scenes/clock_settings_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) ClockSettingsScene##id,\ntypedef enum {\n#include \"clock_settings_scene_config.h\"\n    ClockSettingsSceneNum,\n} ClockSettingsScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers clock_settings_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"clock_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"clock_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"clock_settings_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/settings/clock_settings/scenes/clock_settings_scene_config.h",
    "content": "ADD_SCENE(clock_settings, start, Start)\n"
  },
  {
    "path": "applications/settings/clock_settings/scenes/clock_settings_scene_start.c",
    "content": "#include \"../clock_settings.h\"\n#include <furi_hal.h>\n\n#define TAG \"SceneStart\"\n\ntypedef enum {\n    SubmenuIndexPwm,\n    SubmenuIndexClockOutput,\n} SubmenuIndex;\n\nvoid clock_settings_scene_start_submenu_callback(void* context, uint32_t index) {\n    ClockSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid clock_settings_scene_start_on_enter(void* context) {\n    ClockSettings* app = context;\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ClockSettingsViewPwm);\n}\n\nbool clock_settings_scene_start_on_event(void* context, SceneManagerEvent event) {\n    UNUSED(context);\n    UNUSED(event);\n\n    return false;\n}\n\nvoid clock_settings_scene_start_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/settings/clock_settings/views/clock_settings_module.c",
    "content": "#include \"clock_settings_module.h\"\n\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <locale/locale.h>\n\n#define TAG \"ClockSettingsModule\"\n\nstruct ClockSettingsModule {\n    FuriEventLoopTimer* timer;\n    View* view;\n};\n\ntypedef struct {\n    DateTime current;\n    DateTime alarm;\n    bool alarm_enabled;\n    bool editing;\n\n    uint8_t row;\n    uint8_t column;\n} ClockSettingsModuleViewModel;\n\ntypedef enum {\n    EditStateNone,\n    EditStateActive,\n    EditStateActiveEditing,\n} EditState;\n\n#define get_state(m, r, c)                                           \\\n    ((m)->row == (r) && (m)->column == (c) ?                         \\\n         ((m)->editing ? EditStateActiveEditing : EditStateActive) : \\\n         EditStateNone)\n\n#define ROW_0_Y (4)\n#define ROW_0_H (20)\n\n#define ROW_1_Y (30)\n#define ROW_1_H (12)\n\n#define ROW_2_Y (48)\n#define ROW_2_H (12)\n\n#define ROW_COUNT    3\n#define COLUMN_COUNT 3\n\nstatic inline void clock_settings_module_cleanup_date(DateTime* dt) {\n    uint8_t day_per_month =\n        datetime_get_days_per_month(datetime_is_leap_year(dt->year), dt->month);\n    if(dt->day > day_per_month) {\n        dt->day = day_per_month;\n    }\n}\n\nstatic inline void clock_settings_module_draw_block(\n    Canvas* canvas,\n    int32_t x,\n    int32_t y,\n    size_t w,\n    size_t h,\n    Font font,\n    EditState state,\n    const char* text) {\n    canvas_set_color(canvas, ColorBlack);\n    if(state != EditStateNone) {\n        if(state == EditStateActiveEditing) {\n            canvas_draw_icon(canvas, x + w / 2 - 2, y - 1 - 3, &I_SmallArrowUp_3x5);\n            canvas_draw_icon(canvas, x + w / 2 - 2, y + h + 1, &I_SmallArrowDown_3x5);\n        }\n        canvas_draw_rbox(canvas, x, y, w, h, 1);\n        canvas_set_color(canvas, ColorWhite);\n    } else {\n        canvas_draw_rframe(canvas, x, y, w, h, 1);\n    }\n\n    canvas_set_font(canvas, font);\n    canvas_draw_str_aligned(canvas, x + w / 2, y + h / 2, AlignCenter, AlignCenter, text);\n    if(state != EditStateNone) {\n        canvas_set_color(canvas, ColorBlack);\n    }\n}\n\nstatic void\n    clock_settings_module_draw_time_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {\n    char buffer[64];\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, ROW_0_Y + 15, \"Time\");\n\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->current.hour);\n    clock_settings_module_draw_block(\n        canvas, 32, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 0), buffer);\n    canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7, 2, 2);\n    canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);\n\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->current.minute);\n    clock_settings_module_draw_block(\n        canvas, 66, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 1), buffer);\n    canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7, 2, 2);\n    canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);\n\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->current.second);\n    clock_settings_module_draw_block(\n        canvas, 100, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 2), buffer);\n}\n\nstatic void\n    clock_settings_module_draw_date_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {\n    char buffer[64];\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, ROW_1_Y + 9, \"Date\");\n    // Day\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->current.day);\n    clock_settings_module_draw_block(\n        canvas, 44, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 0), buffer);\n    canvas_draw_box(canvas, 71 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);\n    // Month\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->current.month);\n    clock_settings_module_draw_block(\n        canvas, 71, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 1), buffer);\n    canvas_draw_box(canvas, 98 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);\n    // Year\n    snprintf(buffer, sizeof(buffer), \"%04u\", model->current.year);\n    clock_settings_module_draw_block(\n        canvas, 98, ROW_1_Y, 30, ROW_1_H, FontPrimary, get_state(model, 1, 2), buffer);\n}\n\nstatic void\n    clock_settings_module_draw_alarm_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {\n    char buffer[64];\n\n    canvas_set_font(canvas, FontPrimary);\n    canvas_draw_str(canvas, 0, ROW_2_Y + 9, \"Alarm\");\n\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->alarm.hour);\n    clock_settings_module_draw_block(\n        canvas, 58, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 0), buffer);\n    canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4, 2, 2);\n    canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4 - 4, 2, 2);\n\n    snprintf(buffer, sizeof(buffer), \"%02u\", model->alarm.minute);\n    clock_settings_module_draw_block(\n        canvas, 81, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 1), buffer);\n\n    clock_settings_module_draw_block(\n        canvas,\n        106,\n        ROW_2_Y,\n        22,\n        ROW_2_H,\n        FontPrimary,\n        get_state(model, 2, 2),\n        model->alarm_enabled ? \"On\" : \"Off\");\n}\n\nstatic void clock_settings_module_draw_callback(Canvas* canvas, void* _model) {\n    ClockSettingsModuleViewModel* model = _model;\n    clock_settings_module_draw_time_callback(canvas, model);\n    clock_settings_module_draw_date_callback(canvas, model);\n    clock_settings_module_draw_alarm_callback(canvas, model);\n}\n\nstatic bool clock_settings_module_input_navigation_callback(\n    InputEvent* event,\n    ClockSettingsModuleViewModel* model) {\n    if(event->key == InputKeyUp) {\n        if(model->row > 0) model->row--;\n    } else if(event->key == InputKeyDown) {\n        if(model->row < ROW_COUNT - 1) model->row++;\n    } else if(event->key == InputKeyOk) {\n        model->editing = !model->editing;\n    } else if(event->key == InputKeyRight) {\n        if(model->column < COLUMN_COUNT - 1) model->column++;\n    } else if(event->key == InputKeyLeft) {\n        if(model->column > 0) model->column--;\n    } else if(event->key == InputKeyBack && model->editing) {\n        model->editing = false;\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool clock_settings_module_input_time_callback(\n    InputEvent* event,\n    ClockSettingsModuleViewModel* model) {\n    if(event->key == InputKeyUp) {\n        if(model->column == 0) {\n            model->current.hour++;\n            model->current.hour = model->current.hour % 24;\n        } else if(model->column == 1) {\n            model->current.minute++;\n            model->current.minute = model->current.minute % 60;\n        } else if(model->column == 2) {\n            model->current.second++;\n            model->current.second = model->current.second % 60;\n        } else {\n            furi_crash();\n        }\n    } else if(event->key == InputKeyDown) {\n        if(model->column == 0) {\n            if(model->current.hour > 0) {\n                model->current.hour--;\n            } else {\n                model->current.hour = 23;\n            }\n            model->current.hour = model->current.hour % 24;\n        } else if(model->column == 1) {\n            if(model->current.minute > 0) {\n                model->current.minute--;\n            } else {\n                model->current.minute = 59;\n            }\n            model->current.minute = model->current.minute % 60;\n        } else if(model->column == 2) {\n            if(model->current.second > 0) {\n                model->current.second--;\n            } else {\n                model->current.second = 59;\n            }\n            model->current.second = model->current.second % 60;\n        } else {\n            furi_crash();\n        }\n    } else {\n        return clock_settings_module_input_navigation_callback(event, model);\n    }\n\n    return true;\n}\n\nstatic bool clock_settings_module_input_date_callback(\n    InputEvent* event,\n    ClockSettingsModuleViewModel* model) {\n    if(event->key == InputKeyUp) {\n        if(model->column == 0) {\n            if(model->current.day < 31) model->current.day++;\n        } else if(model->column == 1) {\n            if(model->current.month < 12) {\n                model->current.month++;\n            }\n        } else if(model->column == 2) {\n            if(model->current.year < 2099) {\n                model->current.year++;\n            }\n        } else {\n            furi_crash();\n        }\n    } else if(event->key == InputKeyDown) {\n        if(model->column == 0) {\n            if(model->current.day > 1) {\n                model->current.day--;\n            }\n        } else if(model->column == 1) {\n            if(model->current.month > 1) {\n                model->current.month--;\n            }\n        } else if(model->column == 2) {\n            if(model->current.year > 2000) {\n                model->current.year--;\n            }\n        } else {\n            furi_crash();\n        }\n    } else {\n        return clock_settings_module_input_navigation_callback(event, model);\n    }\n\n    clock_settings_module_cleanup_date(&model->current);\n\n    return true;\n}\n\nstatic bool clock_settings_module_input_alarm_callback(\n    InputEvent* event,\n    ClockSettingsModuleViewModel* model) {\n    if(event->key == InputKeyUp) {\n        if(model->column == 0) {\n            model->alarm.hour++;\n            model->alarm.hour = model->alarm.hour % 24;\n        } else if(model->column == 1) {\n            model->alarm.minute++;\n            model->alarm.minute = model->alarm.minute % 60;\n        } else if(model->column == 2) {\n            model->alarm_enabled = !model->alarm_enabled;\n        } else {\n            furi_crash();\n        }\n    } else if(event->key == InputKeyDown) {\n        if(model->column == 0) {\n            if(model->alarm.hour > 0) {\n                model->alarm.hour--;\n            } else {\n                model->alarm.hour = 23;\n            }\n            model->alarm.hour = model->alarm.hour % 24;\n        } else if(model->column == 1) {\n            if(model->alarm.minute > 0) {\n                model->alarm.minute--;\n            } else {\n                model->alarm.minute = 59;\n            }\n            model->alarm.minute = model->alarm.minute % 60;\n        } else if(model->column == 2) {\n            model->alarm_enabled = !model->alarm_enabled;\n        } else {\n            furi_crash();\n        }\n    } else {\n        return clock_settings_module_input_navigation_callback(event, model);\n    }\n\n    return true;\n}\n\nstatic bool clock_settings_module_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n\n    ClockSettingsModule* instance = context;\n    bool consumed = false;\n\n    with_view_model(\n        instance->view,\n        ClockSettingsModuleViewModel * model,\n        {\n            if(event->type == InputTypeShort || event->type == InputTypeRepeat) {\n                bool previous_editing = model->editing;\n                if(model->editing) {\n                    if(model->row == 0) {\n                        consumed = clock_settings_module_input_time_callback(event, model);\n                    } else if(model->row == 1) {\n                        consumed = clock_settings_module_input_date_callback(event, model);\n                    } else if(model->row == 2) {\n                        consumed = clock_settings_module_input_alarm_callback(event, model);\n                    } else {\n                        furi_crash();\n                    }\n                } else {\n                    consumed = clock_settings_module_input_navigation_callback(event, model);\n                }\n\n                // Switching between navigate/edit\n                if(model->editing != previous_editing) {\n                    if(model->row == 2) {\n                        if(!model->editing) {\n                            // Disable alarm\n                            furi_hal_rtc_set_alarm(NULL, false);\n                            // Set new alarm\n                            furi_hal_rtc_set_alarm(&model->alarm, model->alarm_enabled);\n                            // Confirm\n                            model->alarm_enabled = furi_hal_rtc_get_alarm(&model->alarm);\n                        }\n                    } else {\n                        if(model->editing) {\n                            // stop timer to prevent mess with current date time\n                            furi_event_loop_timer_stop(instance->timer);\n                        } else {\n                            // save date time and restart timer\n                            furi_hal_rtc_set_datetime(&model->current);\n                            furi_event_loop_timer_start(instance->timer, 1000);\n                        }\n                    }\n                }\n            }\n        },\n        true);\n\n    return consumed;\n}\n\nstatic void clock_settings_module_timer_callback(void* context) {\n    furi_assert(context);\n    ClockSettingsModule* instance = context;\n\n    DateTime dt;\n    furi_hal_rtc_get_datetime(&dt);\n    with_view_model(\n        instance->view, ClockSettingsModuleViewModel * model, { model->current = dt; }, true);\n}\n\nstatic void clock_settings_module_view_enter_callback(void* context) {\n    furi_assert(context);\n    ClockSettingsModule* instance = context;\n\n    clock_settings_module_timer_callback(context);\n\n    DateTime alarm;\n    bool enabled = furi_hal_rtc_get_alarm(&alarm);\n\n    with_view_model(\n        instance->view,\n        ClockSettingsModuleViewModel * model,\n        {\n            model->alarm = alarm;\n            model->alarm_enabled = enabled;\n        },\n        true);\n\n    furi_event_loop_timer_start(instance->timer, 1000);\n}\n\nstatic void clock_settings_module_view_exit_callback(void* context) {\n    furi_assert(context);\n    ClockSettingsModule* instance = context;\n    furi_event_loop_timer_stop(instance->timer);\n}\n\nClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop) {\n    ClockSettingsModule* instance = malloc(sizeof(ClockSettingsModule));\n\n    instance->timer = furi_event_loop_timer_alloc(\n        event_loop, clock_settings_module_timer_callback, FuriEventLoopTimerTypePeriodic, instance);\n    instance->view = view_alloc();\n    view_set_enter_callback(instance->view, clock_settings_module_view_enter_callback);\n    view_set_exit_callback(instance->view, clock_settings_module_view_exit_callback);\n    view_allocate_model(\n        instance->view, ViewModelTypeLocking, sizeof(ClockSettingsModuleViewModel));\n    with_view_model(\n        instance->view, ClockSettingsModuleViewModel * model, { model->row = 0; }, false);\n    view_set_context(instance->view, instance);\n    view_set_draw_callback(instance->view, clock_settings_module_draw_callback);\n    view_set_input_callback(instance->view, clock_settings_module_input_callback);\n\n    return instance;\n}\n\nvoid clock_settings_module_free(ClockSettingsModule* instance) {\n    furi_assert(instance);\n    view_free(instance->view);\n    free(instance);\n}\n\nView* clock_settings_module_get_view(ClockSettingsModule* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/settings/clock_settings/views/clock_settings_module.h",
    "content": "#pragma once\n\n#include <furi_hal.h>\n#include <gui/view.h>\n\ntypedef struct ClockSettingsModule ClockSettingsModule;\ntypedef void (*ClockSettingsModuleViewCallback)(\n    uint8_t channel_id,\n    uint32_t freq,\n    uint8_t duty,\n    void* context);\n\nClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop);\n\nvoid clock_settings_module_free(ClockSettingsModule* instance);\n\nView* clock_settings_module_get_view(ClockSettingsModule* instance);\n\nvoid clock_settings_module_set(\n    ClockSettingsModule* instance,\n    const DateTime* datetime,\n    bool enabled);\n\nbool clock_settings_module_get(ClockSettingsModule* instance, DateTime* datetime);\n"
  },
  {
    "path": "applications/settings/desktop_settings/application.fam",
    "content": "App(\n    appid=\"desktop_settings\",\n    name=\"Desktop\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"desktop_settings_app\",\n    requires=[\n        \"desktop\",\n        \"gui\",\n    ],\n    stack_size=1 * 1024,\n    order=50,\n)\n"
  },
  {
    "path": "applications/settings/desktop_settings/desktop_settings_app.c",
    "content": "#include <furi.h>\n#include <gui/modules/popup.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/scene_manager.h>\n\n#include <desktop/desktop.h>\n#include <desktop/views/desktop_view_pin_input.h>\n\n#include \"desktop_settings_app.h\"\n#include \"scenes/desktop_settings_scene.h\"\n\nstatic bool desktop_settings_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool desktop_settings_back_event_callback(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nDesktopSettingsApp* desktop_settings_app_alloc(void) {\n    DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->dialogs = furi_record_open(RECORD_DIALOGS);\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, desktop_settings_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, desktop_settings_back_event_callback);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->popup = popup_alloc();\n    app->submenu = submenu_alloc();\n    app->variable_item_list = variable_item_list_alloc();\n    app->pin_input_view = desktop_view_pin_input_alloc();\n    app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc();\n    app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc();\n    app->dialog_ex = dialog_ex_alloc();\n\n    view_dispatcher_add_view(\n        app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        DesktopSettingsAppViewVarItemList,\n        variable_item_list_get_view(app->variable_item_list));\n    view_dispatcher_add_view(\n        app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup));\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        DesktopSettingsAppViewIdPinInput,\n        desktop_view_pin_input_get_view(app->pin_input_view));\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        DesktopSettingsAppViewIdPinSetupHowto,\n        desktop_settings_view_pin_setup_howto_get_view(app->pin_setup_howto_view));\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        DesktopSettingsAppViewIdPinSetupHowto2,\n        desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view));\n    view_dispatcher_add_view(\n        app->view_dispatcher, DesktopSettingsAppViewDialogEx, dialog_ex_get_view(app->dialog_ex));\n    return app;\n}\n\nvoid desktop_settings_app_free(DesktopSettingsApp* app) {\n    furi_assert(app);\n    // Variable item list\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);\n    view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewDialogEx);\n    variable_item_list_free(app->variable_item_list);\n    submenu_free(app->submenu);\n    popup_free(app->popup);\n    desktop_view_pin_input_free(app->pin_input_view);\n    desktop_settings_view_pin_setup_howto_free(app->pin_setup_howto_view);\n    desktop_settings_view_pin_setup_howto2_free(app->pin_setup_howto2_view);\n    dialog_ex_free(app->dialog_ex);\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n    // Records\n    furi_record_close(RECORD_DIALOGS);\n    furi_record_close(RECORD_GUI);\n    free(app);\n}\n\nextern int32_t desktop_settings_app(void* p) {\n    UNUSED(p);\n\n    DesktopSettingsApp* app = desktop_settings_app_alloc();\n    Desktop* desktop = furi_record_open(RECORD_DESKTOP);\n\n    desktop_api_get_settings(desktop, &app->settings);\n\n    scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    desktop_api_set_settings(desktop, &app->settings);\n    furi_record_close(RECORD_DESKTOP);\n\n    desktop_settings_app_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/desktop_settings_app.h",
    "content": "#pragma once\n\n#include <gui/gui.h>\n#include <gui/modules/popup.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/submenu.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/modules/dialog_ex.h>\n#include <dialogs/dialogs.h>\n#include <assets_icons.h>\n\n#include <desktop/desktop_settings.h>\n#include <desktop/views/desktop_view_pin_input.h>\n#include \"views/desktop_settings_view_pin_setup_howto.h\"\n#include \"views/desktop_settings_view_pin_setup_howto2.h\"\n\ntypedef enum {\n    DesktopSettingsAppViewMenu,\n    DesktopSettingsAppViewVarItemList,\n    DesktopSettingsAppViewIdPopup,\n    DesktopSettingsAppViewIdPinInput,\n    DesktopSettingsAppViewIdPinSetupHowto,\n    DesktopSettingsAppViewIdPinSetupHowto2,\n    DesktopSettingsAppViewDialogEx,\n} DesktopSettingsAppView;\n\ntypedef struct {\n    DesktopSettings settings;\n\n    Gui* gui;\n    DialogsApp* dialogs;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n    VariableItemList* variable_item_list;\n    Submenu* submenu;\n    Popup* popup;\n    DesktopViewPinInput* pin_input_view;\n    DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;\n    DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;\n    DialogEx* dialog_ex;\n\n    DesktopPinCode pincode_buffer;\n    bool pincode_buffer_filled;\n\n    uint32_t pin_menu_idx;\n    uint32_t quick_apps_menu_idx;\n    uint32_t quick_apps_direction_menu_idx;\n} DesktopSettingsApp;\n"
  },
  {
    "path": "applications/settings/desktop_settings/desktop_settings_custom_event.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    // reserve 100 for button presses, submenu selections, etc.\n    DesktopSettingsCustomEventExit = 100,\n    DesktopSettingsCustomEventDone,\n\n    DesktopSettingsCustomEvent1stPinEntered,\n    DesktopSettingsCustomEventPinsEqual,\n    DesktopSettingsCustomEventPinsDifferent,\n\n    DesktopSettingsCustomEventSetPin,\n    DesktopSettingsCustomEventChangePin,\n    DesktopSettingsCustomEventDisablePin,\n\n    DesktopSettingsCustomEventSetDefault,\n    DesktopSettingsCustomEventSetDummy,\n} DesktopSettingsCustomEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene.c",
    "content": "#include \"desktop_settings_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const desktop_settings_on_enter_handlers[])(void*) = {\n#include \"desktop_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const desktop_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"desktop_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const desktop_settings_on_exit_handlers[])(void* context) = {\n#include \"desktop_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers desktop_settings_scene_handlers = {\n    .on_enter_handlers = desktop_settings_on_enter_handlers,\n    .on_event_handlers = desktop_settings_on_event_handlers,\n    .on_exit_handlers = desktop_settings_on_exit_handlers,\n    .scene_num = DesktopSettingsAppSceneNum,\n};\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) DesktopSettingsAppScene##id,\ntypedef enum {\n#include \"desktop_settings_scene_config.h\"\n    DesktopSettingsAppSceneNum,\n} DesktopSettingsAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers desktop_settings_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"desktop_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"desktop_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"desktop_settings_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h",
    "content": "ADD_SCENE(desktop_settings, start, Start)\nADD_SCENE(desktop_settings, favorite, Favorite)\nADD_SCENE(desktop_settings, pin_menu, PinMenu)\n\nADD_SCENE(desktop_settings, pin_auth, PinAuth)\nADD_SCENE(desktop_settings, pin_error, PinError)\nADD_SCENE(desktop_settings, pin_disable, PinDisable)\nADD_SCENE(desktop_settings, pin_setup, PinSetup)\nADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto)\nADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2)\nADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone)\n\nADD_SCENE(desktop_settings, quick_apps_menu, QuickAppsMenu)\nADD_SCENE(desktop_settings, quick_apps_direction_menu, QuickAppsDirectionMenu)\n\nADD_SCENE(desktop_settings, happy_mode, HappyMode)\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c",
    "content": "#include \"../desktop_settings_app.h\"\n#include \"applications.h\"\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n#include <flipper_application/flipper_application.h>\n#include <storage/storage.h>\n#include <dialogs/dialogs.h>\n\n#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT)\n\n#define DEFAULT_INDEX                  (0)\n#define EXTERNAL_BROWSER_NAME          (\"(   ) Apps Menu (Default)\")\n#define EXTERNAL_BROWSER_NAME_SELECTED (\"(*) Apps Menu (Default)\")\n#define PASSPORT_NAME                  (\"(   ) Passport (Default)\")\n#define PASSPORT_NAME_SELECTED         (\"(*) Passport (Default)\")\n\n#define SELECTED_PREFIX     (\"(*) \")\n#define NOT_SELECTED_PREFIX (\"(   ) \")\n\n#define EXTERNAL_APPLICATION_INDEX         (1)\n#define EXTERNAL_APPLICATION_NAME          (\"(   ) [Select App]\")\n#define EXTERNAL_APPLICATION_NAME_SELECTED (\"(*) [Select App]\")\n\n#define PRESELECTED_SPECIAL 0xffffffff\n\nstatic const char* favorite_fap_get_app_name(size_t i) {\n    const char* name;\n    if(i < FLIPPER_APPS_COUNT) {\n        name = FLIPPER_APPS[i].name;\n    } else {\n        name = FLIPPER_EXTERNAL_APPS[i - FLIPPER_APPS_COUNT].name;\n    }\n\n    return name;\n}\n\nstatic bool favorite_fap_selector_item_callback(\n    FuriString* file_path,\n    void* context,\n    uint8_t** icon_ptr,\n    FuriString* item_name) {\n    UNUSED(context);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name);\n    furi_record_close(RECORD_STORAGE);\n    return success;\n}\n\nstatic bool favorite_fap_selector_file_exists(char* file_path) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool exists = storage_file_exists(storage, file_path);\n    furi_record_close(RECORD_STORAGE);\n    return exists;\n}\n\nstatic void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid desktop_settings_scene_favorite_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    Submenu* submenu = app->submenu;\n    submenu_reset(submenu);\n\n    uint32_t favorite_id =\n        scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);\n    uint32_t pre_select_item = PRESELECTED_SPECIAL;\n    FavoriteApp* curr_favorite_app = NULL;\n    bool default_passport = false;\n\n    if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {\n        furi_assert(favorite_id < FavoriteAppNumber);\n        curr_favorite_app = &app->settings.favorite_apps[favorite_id];\n        if(favorite_id == FavoriteAppRightShort) {\n            default_passport = true;\n        }\n    } else {\n        favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); //-V784\n        furi_assert(favorite_id < DummyAppNumber);\n        curr_favorite_app = &app->settings.dummy_apps[favorite_id];\n        default_passport = true;\n        favorite_id |= SCENE_STATE_SET_DUMMY_APP;\n    }\n\n    // Special case: Application browser\n    submenu_add_item(\n        submenu,\n        default_passport ? (PASSPORT_NAME) : (EXTERNAL_BROWSER_NAME),\n        DEFAULT_INDEX,\n        desktop_settings_scene_favorite_submenu_callback,\n        app);\n\n    // Special case: Specific application\n    submenu_add_item(\n        submenu,\n        EXTERNAL_APPLICATION_NAME,\n        EXTERNAL_APPLICATION_INDEX,\n        desktop_settings_scene_favorite_submenu_callback,\n        app);\n\n    FuriString* full_name = furi_string_alloc();\n\n    for(size_t i = 0; i < APPS_COUNT; i++) {\n        const char* name = favorite_fap_get_app_name(i);\n\n        // Add the prefix\n        furi_string_reset(full_name);\n        if(!strcmp(name, curr_favorite_app->name_or_path)) {\n            furi_string_set_str(full_name, SELECTED_PREFIX);\n        } else {\n            furi_string_set_str(full_name, NOT_SELECTED_PREFIX);\n        }\n        furi_string_cat_str(full_name, name);\n\n        submenu_add_item(\n            submenu,\n            furi_string_get_cstr(full_name),\n            i + 2,\n            desktop_settings_scene_favorite_submenu_callback,\n            app);\n\n        // Select favorite item in submenu\n        if(!strcmp(name, curr_favorite_app->name_or_path)) {\n            pre_select_item = i + 2;\n        }\n    }\n\n    if(pre_select_item == PRESELECTED_SPECIAL) {\n        if(curr_favorite_app->name_or_path[0] == '\\0') {\n            pre_select_item = DEFAULT_INDEX;\n            submenu_change_item_label(\n                submenu,\n                DEFAULT_INDEX,\n                default_passport ? (PASSPORT_NAME_SELECTED) : (EXTERNAL_BROWSER_NAME_SELECTED));\n        } else {\n            pre_select_item = EXTERNAL_APPLICATION_INDEX;\n            submenu_change_item_label(\n                submenu, EXTERNAL_APPLICATION_INDEX, EXTERNAL_APPLICATION_NAME_SELECTED);\n        }\n    }\n\n    switch(favorite_id) {\n    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort:\n    case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft:\n        submenu_set_header(submenu, \"Left - Press\");\n        break;\n    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong:\n        submenu_set_header(submenu, \"Left - Hold\");\n        break;\n    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort:\n    case SCENE_STATE_SET_DUMMY_APP | DummyAppRight:\n        submenu_set_header(submenu, \"Right - Press\");\n        break;\n    case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong:\n        submenu_set_header(submenu, \"Right - Hold\");\n        break;\n    case SCENE_STATE_SET_DUMMY_APP | DummyAppDown:\n        submenu_set_header(submenu, \"Down - Press\");\n        break;\n    case SCENE_STATE_SET_DUMMY_APP | DummyAppOk:\n        submenu_set_header(submenu, \"Middle - Press\");\n        break;\n    default:\n        break;\n    }\n\n    submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch.\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);\n}\n\nbool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n    FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH(\"apps\"));\n\n    uint32_t favorite_id =\n        scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);\n    FavoriteApp* curr_favorite_app = NULL;\n    if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) {\n        furi_assert(favorite_id < FavoriteAppNumber);\n        curr_favorite_app = &app->settings.favorite_apps[favorite_id];\n    } else {\n        favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); //-V784\n        furi_assert(favorite_id < DummyAppNumber);\n        curr_favorite_app = &app->settings.dummy_apps[favorite_id];\n    }\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DEFAULT_INDEX) {\n            curr_favorite_app->name_or_path[0] = '\\0';\n            consumed = true;\n        } else if(event.event == EXTERNAL_APPLICATION_INDEX) {\n            const DialogsFileBrowserOptions browser_options = {\n                .extension = \".fap\",\n                .icon = &I_unknown_10px,\n                .skip_assets = true,\n                .hide_ext = true,\n                .item_loader_callback = favorite_fap_selector_item_callback,\n                .item_loader_context = app,\n                .base_path = EXT_PATH(\"apps\"),\n            };\n\n            // Select favorite fap in file browser\n            if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) {\n                furi_string_set_str(temp_path, curr_favorite_app->name_or_path);\n            }\n\n            if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {\n                submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene\n                strlcpy(\n                    curr_favorite_app->name_or_path,\n                    furi_string_get_cstr(temp_path),\n                    sizeof(curr_favorite_app->name_or_path));\n                consumed = true;\n            }\n        } else {\n            size_t app_index = event.event - 2;\n            const char* name = favorite_fap_get_app_name(app_index);\n            if(name)\n                strlcpy(\n                    curr_favorite_app->name_or_path,\n                    name,\n                    sizeof(curr_favorite_app->name_or_path));\n            consumed = true;\n        }\n        if(consumed) {\n            scene_manager_previous_scene(app->scene_manager);\n        };\n        consumed = true;\n\n        desktop_settings_save(&app->settings);\n    }\n\n    furi_string_free(temp_path);\n    return consumed;\n}\n\nvoid desktop_settings_scene_favorite_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_happy_mode.c",
    "content": "#include <furi.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/dialog_ex.h>\n#include <dolphin/dolphin.h>\n\n#include \"desktop_settings_scene.h\"\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void desktop_settings_scene_happy_mode_done_callback(DialogExResult result, void* context) {\n    DesktopSettingsApp* app = context;\n    DolphinSettings settings;\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    dolphin_get_settings(dolphin, &settings);\n    settings.happy_mode = (result == DialogExResultRight);\n    dolphin_set_settings(dolphin, &settings);\n    furi_record_close(RECORD_DOLPHIN);\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_happy_mode_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinSettings settings;\n    dolphin_get_settings(dolphin, &settings);\n    furi_record_close(RECORD_DOLPHIN);\n\n    dialog_ex_set_header(app->dialog_ex, \"Happy Mode\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(\n        app->dialog_ex,\n        \"I will never get angry at you\\nfor not spending time with me\\nas long as this mode is enabled\",\n        64,\n        30,\n        AlignCenter,\n        AlignCenter);\n    dialog_ex_set_left_button_text(app->dialog_ex, settings.happy_mode ? \"Disable\" : \"Go back\");\n    dialog_ex_set_right_button_text(\n        app->dialog_ex, settings.happy_mode ? \"Keep enabled\" : \"Enable\");\n    dialog_ex_set_result_callback(app->dialog_ex, desktop_settings_scene_happy_mode_done_callback);\n    dialog_ex_set_context(app->dialog_ex, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewDialogEx);\n}\n\nbool desktop_settings_scene_happy_mode_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventExit:\n            scene_manager_previous_scene(app->scene_manager);\n            consumed = true;\n            break;\n        default:\n            furi_crash();\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_happy_mode_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_i.h",
    "content": "#pragma once\n\n#define SCENE_STATE_PIN_AUTH_DISABLE    (0)\n#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1)\n\n#define SCENE_STATE_PIN_ERROR_MISMATCH (0)\n#define SCENE_STATE_PIN_ERROR_WRONG    (1)\n\n#define SCENE_STATE_SET_FAVORITE_APP (0)\n#define SCENE_STATE_SET_DUMMY_APP    (1 << 8)\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c",
    "content": "#include <stdint.h>\n#include <core/check.h>\n#include <gui/scene_manager.h>\n#include <desktop/helpers/pin_code.h>\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n#include <desktop/desktop_settings.h>\n#include <desktop/views/desktop_view_pin_input.h>\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n\nstatic void pin_auth_done_callback(const DesktopPinCode* pin_code, void* context) {\n    furi_assert(pin_code);\n    furi_assert(context);\n\n    DesktopSettingsApp* app = context;\n    app->pincode_buffer = *pin_code;\n\n    if(desktop_pin_code_check(pin_code)) {\n        view_dispatcher_send_custom_event(\n            app->view_dispatcher, DesktopSettingsCustomEventPinsEqual);\n    } else {\n        view_dispatcher_send_custom_event(\n            app->view_dispatcher, DesktopSettingsCustomEventPinsDifferent);\n    }\n}\n\nstatic void pin_auth_back_callback(void* context) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_auth_on_enter(void* context) {\n    furi_assert(desktop_pin_code_is_set());\n\n    DesktopSettingsApp* app = context;\n\n    desktop_view_pin_input_set_context(app->pin_input_view, app);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_auth_done_callback);\n    desktop_view_pin_input_set_label_button(app->pin_input_view, \"OK\");\n    desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);\n    desktop_view_pin_input_set_label_secondary(\n        app->pin_input_view, 0, 8, \"Enter your current PIN:\");\n    desktop_view_pin_input_reset_pin(app->pin_input_view);\n    desktop_view_pin_input_unlock_input(app->pin_input_view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);\n}\n\nbool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventPinsDifferent:\n            scene_manager_set_scene_state(\n                app->scene_manager, DesktopSettingsAppScenePinError, SCENE_STATE_PIN_ERROR_WRONG);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventPinsEqual: {\n            uint32_t state =\n                scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinAuth);\n            if(state == SCENE_STATE_PIN_AUTH_CHANGE_PIN) {\n                scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);\n            } else if(state == SCENE_STATE_PIN_AUTH_DISABLE) {\n                scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinDisable);\n            } else {\n                furi_crash();\n            }\n            consumed = true;\n            break;\n        }\n        case DesktopSettingsCustomEventExit:\n            scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, DesktopSettingsAppScenePinMenu);\n            consumed = true;\n            break;\n\n        default:\n            consumed = true;\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_auth_on_exit(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c",
    "content": "#include <core/check.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/popup.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n#include <desktop/desktop_settings.h>\n#include \"desktop_settings_scene.h\"\n\nstatic void pin_disable_back_callback(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_disable_on_enter(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n\n    desktop_pin_code_reset();\n\n    popup_set_context(app->popup, app);\n    popup_set_callback(app->popup, pin_disable_back_callback);\n    popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62);\n    popup_set_header(app->popup, \"Removed\", 100, 10, AlignCenter, AlignTop);\n    popup_set_timeout(app->popup, 1500);\n    popup_enable_timeout(app->popup);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);\n}\n\nbool desktop_settings_scene_pin_disable_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventExit:\n            scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, DesktopSettingsAppScenePinMenu);\n            consumed = true;\n            break;\n\n        default:\n            consumed = true;\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_disable_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c",
    "content": "#include <stdint.h>\n#include <core/check.h>\n#include <gui/scene_manager.h>\n\n#include <desktop/desktop_settings.h>\n#include <desktop/views/desktop_view_pin_input.h>\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n#include <desktop/helpers/pin_code.h>\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void pin_error_back_callback(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nstatic void pin_error_done_callback(const DesktopPinCode* pin_code, void* context) {\n    UNUSED(pin_code);\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_error_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    desktop_pin_lock_error_notify();\n\n    desktop_view_pin_input_set_context(app->pin_input_view, app);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_error_done_callback);\n\n    uint32_t state =\n        scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinError);\n    if(state == SCENE_STATE_PIN_ERROR_MISMATCH) {\n        desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, \"PIN mismatch!\");\n    } else if(state == SCENE_STATE_PIN_ERROR_WRONG) {\n        desktop_view_pin_input_set_label_primary(app->pin_input_view, 35, 8, \"Wrong PIN!\");\n    } else {\n        furi_crash();\n    }\n    desktop_view_pin_input_set_label_secondary(app->pin_input_view, 0, 8, NULL);\n    desktop_view_pin_input_set_label_button(app->pin_input_view, \"Retry\");\n    desktop_view_pin_input_lock_input(app->pin_input_view);\n    desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);\n}\n\nbool desktop_settings_scene_pin_error_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventExit:\n            scene_manager_previous_scene(app->scene_manager);\n            consumed = true;\n            break;\n\n        default:\n            consumed = true;\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_error_on_exit(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    desktop_view_pin_input_unlock_input(app->pin_input_view);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c",
    "content": "#include <gui/scene_manager.h>\n#include <applications.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void desktop_settings_scene_pin_menu_submenu_callback(void* context, uint32_t index) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid desktop_settings_scene_pin_menu_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    Submenu* submenu = app->submenu;\n    submenu_reset(submenu);\n\n    if(!desktop_pin_code_is_set()) {\n        submenu_add_item(\n            submenu,\n            \"Set PIN\",\n            DesktopSettingsCustomEventSetPin,\n            desktop_settings_scene_pin_menu_submenu_callback,\n            app);\n\n    } else {\n        submenu_add_item(\n            submenu,\n            \"Change PIN\",\n            DesktopSettingsCustomEventChangePin,\n            desktop_settings_scene_pin_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Remove PIN\",\n            DesktopSettingsCustomEventDisablePin,\n            desktop_settings_scene_pin_menu_submenu_callback,\n            app);\n    }\n\n    submenu_set_header(app->submenu, \"PIN Code Settings\");\n    submenu_set_selected_item(app->submenu, app->pin_menu_idx);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);\n}\n\nbool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventSetPin:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventChangePin:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppScenePinAuth,\n                SCENE_STATE_PIN_AUTH_CHANGE_PIN);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventDisablePin:\n            scene_manager_set_scene_state(\n                app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);\n            consumed = true;\n            break;\n        default:\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        submenu_set_selected_item(app->submenu, 0);\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_menu_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n\n    app->pin_menu_idx = submenu_get_selected_item(app->submenu);\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c",
    "content": "#include <stdint.h>\n#include <core/check.h>\n#include <gui/scene_manager.h>\n\n#include \"../desktop_settings_app.h\"\n#include <desktop/desktop_settings.h>\n#include <desktop/views/desktop_view_pin_input.h>\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n#include <desktop/helpers/pin_code.h>\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) {\n    furi_assert(pin_code);\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n\n    if(!app->pincode_buffer_filled) {\n        app->pincode_buffer = *pin_code;\n        app->pincode_buffer_filled = true;\n        view_dispatcher_send_custom_event(\n            app->view_dispatcher, DesktopSettingsCustomEvent1stPinEntered);\n    } else {\n        app->pincode_buffer_filled = false;\n        if(desktop_pin_code_is_equal(&app->pincode_buffer, pin_code)) {\n            view_dispatcher_send_custom_event(\n                app->view_dispatcher, DesktopSettingsCustomEventPinsEqual);\n        } else {\n            view_dispatcher_send_custom_event(\n                app->view_dispatcher, DesktopSettingsCustomEventPinsDifferent);\n        }\n    }\n}\n\nstatic void pin_setup_back_callback(void* context) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_setup_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n\n    app->pincode_buffer_filled = false;\n    desktop_view_pin_input_set_context(app->pin_input_view, app);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_setup_back_callback);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);\n    desktop_view_pin_input_set_label_button(app->pin_input_view, \"OK\");\n    desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);\n    desktop_view_pin_input_set_label_secondary(\n        app->pin_input_view, 0, 8, \"Enter from 4 to 10 arrows:\");\n    desktop_view_pin_input_reset_pin(app->pin_input_view);\n    desktop_view_pin_input_unlock_input(app->pin_input_view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);\n}\n\nbool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEvent1stPinEntered:\n            desktop_view_pin_input_set_label_button(app->pin_input_view, \"OK\");\n            desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);\n            desktop_view_pin_input_set_label_secondary(\n                app->pin_input_view, 0, 8, \"Confirm your PIN:\");\n            desktop_view_pin_input_reset_pin(app->pin_input_view);\n            desktop_view_pin_input_unlock_input(app->pin_input_view);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventPinsDifferent:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppScenePinError,\n                SCENE_STATE_PIN_ERROR_MISMATCH);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventPinsEqual:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto2);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventExit: {\n            uint32_t scene_found;\n            scene_found = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, DesktopSettingsAppScenePinMenu);\n            if(!scene_found) {\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n            consumed = true;\n            break;\n        }\n\n        default:\n            consumed = true;\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_setup_on_exit(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c",
    "content": "#include <furi.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n#include <desktop/desktop_settings.h>\n#include <desktop/views/desktop_view_pin_input.h>\n#include \"desktop_settings_scene.h\"\n\nstatic void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) {\n    furi_assert(pin_code);\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventDone);\n}\n\nvoid desktop_settings_scene_pin_setup_done_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n\n    desktop_pin_code_set(&app->pincode_buffer);\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_single_vibro);\n    notification_message(notification, &sequence_blink_green_10);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    desktop_view_pin_input_set_context(app->pin_input_view, app);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);\n    desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer);\n    desktop_view_pin_input_set_label_button(app->pin_input_view, \"Done\");\n    desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, \"PIN Activated!\");\n    desktop_view_pin_input_set_label_secondary(\n        app->pin_input_view, 7, 45, \"Remember or write it down\");\n    desktop_view_pin_input_lock_input(app->pin_input_view);\n    desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 24);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);\n}\n\nbool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventDone: {\n            bool scene_found = false;\n            scene_found = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, DesktopSettingsAppScenePinMenu);\n            if(!scene_found) {\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n            consumed = true;\n            break;\n        }\n        default:\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_setup_done_on_exit(void* context) {\n    furi_assert(context);\n    DesktopSettingsApp* app = context;\n    desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 32);\n    desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);\n    desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c",
    "content": "#include <furi.h>\n#include <gui/scene_manager.h>\n#include <gui/view_dispatcher.h>\n\n#include \"desktop_settings_scene.h\"\n#include \"../desktop_settings_app.h\"\n#include \"../views/desktop_settings_view_pin_setup_howto.h\"\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void desktop_settings_scene_pin_lock_done_callback(void* context) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_setup_howto_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n\n    desktop_settings_view_pin_setup_howto_set_callback(\n        app->pin_setup_howto_view, desktop_settings_scene_pin_lock_done_callback, app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);\n}\n\nbool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventExit:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetup);\n            consumed = true;\n            break;\n        default:\n            furi_crash();\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_setup_howto_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c",
    "content": "#include <furi.h>\n#include <gui/scene_manager.h>\n\n#include \"desktop_settings_scene.h\"\n#include \"../desktop_settings_app.h\"\n#include \"../views/desktop_settings_view_pin_setup_howto2.h\"\n#include \"../desktop_settings_custom_event.h\"\n\nstatic void desktop_settings_scene_pin_setup_howto2_done_callback(void* context) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventDone);\n}\n\nstatic void desktop_settings_scene_pin_setup_howto2_exit_callback(void* context) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit);\n}\n\nvoid desktop_settings_scene_pin_setup_howto2_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n\n    desktop_settings_view_pin_setup_howto2_set_context(app->pin_setup_howto2_view, app);\n    desktop_settings_view_pin_setup_howto2_set_ok_callback(\n        app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_done_callback);\n    desktop_settings_view_pin_setup_howto2_set_cancel_callback(\n        app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_exit_callback);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);\n}\n\nbool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventDone: {\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupDone);\n            consumed = true;\n            break;\n        }\n        case DesktopSettingsCustomEventExit: {\n            bool scene_found = false;\n            scene_found = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, DesktopSettingsAppScenePinMenu);\n            if(!scene_found) {\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n            consumed = true;\n            break;\n        }\n        default:\n            furi_crash();\n        }\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_pin_setup_howto2_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n    desktop_settings_view_pin_setup_howto2_set_ok_callback(app->pin_setup_howto2_view, NULL);\n    desktop_settings_view_pin_setup_howto2_set_cancel_callback(app->pin_setup_howto2_view, NULL);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c",
    "content": "#include <gui/scene_manager.h>\n#include <applications.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n\nenum QuickAppsSubmenuIndex {\n    QuickAppsSubmenuIndexFavoriteLeftClick,\n    QuickAppsSubmenuIndexFavoriteRightClick,\n    QuickAppsSubmenuIndexFavoriteLeftHold,\n    QuickAppsSubmenuIndexFavoriteRightHold,\n    QuickAppsSubmenuIndexDummyLeftClick,\n    QuickAppsSubmenuIndexDummyRightClick,\n    QuickAppsSubmenuIndexDummyDownClick,\n    QuickAppsSubmenuIndexDummyMiddleClick,\n};\n\nstatic void desktop_settings_scene_quick_apps_direction_menu_submenu_callback(\n    void* context,\n    uint32_t index) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid desktop_settings_scene_quick_apps_direction_menu_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    Submenu* submenu = app->submenu;\n    submenu_reset(submenu);\n\n    uint32_t favorite_id = scene_manager_get_scene_state(\n        app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu);\n\n    if(favorite_id == SCENE_STATE_SET_FAVORITE_APP) {\n        submenu_add_item(\n            submenu,\n            \"Left - Press\",\n            QuickAppsSubmenuIndexFavoriteLeftClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Right - Press\",\n            QuickAppsSubmenuIndexFavoriteRightClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Left - Hold\",\n            QuickAppsSubmenuIndexFavoriteLeftHold,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Right - Hold\",\n            QuickAppsSubmenuIndexFavoriteRightHold,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_set_header(app->submenu, \"Default Mode\");\n    } else {\n        submenu_add_item(\n            submenu,\n            \"Left - Press\",\n            QuickAppsSubmenuIndexDummyLeftClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Right - Press\",\n            QuickAppsSubmenuIndexDummyRightClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Down - Press\",\n            QuickAppsSubmenuIndexDummyDownClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_add_item(\n            submenu,\n            \"Middle - Press\",\n            QuickAppsSubmenuIndexDummyMiddleClick,\n            desktop_settings_scene_quick_apps_direction_menu_submenu_callback,\n            app);\n\n        submenu_set_header(app->submenu, \"Dummy Mode\");\n    }\n\n    submenu_set_selected_item(app->submenu, app->quick_apps_direction_menu_idx);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);\n}\n\nbool desktop_settings_scene_quick_apps_direction_menu_on_event(\n    void* context,\n    SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case QuickAppsSubmenuIndexFavoriteLeftClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexFavoriteRightClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexFavoriteLeftHold:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexFavoriteRightHold:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexDummyLeftClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_DUMMY_APP | DummyAppLeft);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexDummyRightClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_DUMMY_APP | DummyAppRight);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexDummyDownClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_DUMMY_APP | DummyAppDown);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        case QuickAppsSubmenuIndexDummyMiddleClick:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneFavorite,\n                SCENE_STATE_SET_DUMMY_APP | DummyAppOk);\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);\n            consumed = true;\n            break;\n        default:\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        submenu_set_selected_item(app->submenu, 0);\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_scene_quick_apps_direction_menu_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n    app->quick_apps_direction_menu_idx = submenu_get_selected_item(app->submenu);\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c",
    "content": "#include <gui/scene_manager.h>\n#include <applications.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"../desktop_settings_custom_event.h\"\n#include \"desktop_settings_scene.h\"\n#include \"desktop_settings_scene_i.h\"\n\nstatic void\n    desktop_settings_scene_quick_apps_menu_submenu_callback(void* context, uint32_t index) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid desktop_settings_scene_quick_apps_menu_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    Submenu* submenu = app->submenu;\n    submenu_reset(submenu);\n\n    submenu_add_item(\n        submenu,\n        \"Default Mode\",\n        DesktopSettingsCustomEventSetDefault,\n        desktop_settings_scene_quick_apps_menu_submenu_callback,\n        app);\n\n    submenu_add_item(\n        submenu,\n        \"Dummy Mode\",\n        DesktopSettingsCustomEventSetDummy,\n        desktop_settings_scene_quick_apps_menu_submenu_callback,\n        app);\n\n    submenu_set_header(app->submenu, \"Set Quick Access Apps\");\n    submenu_set_selected_item(app->submenu, app->quick_apps_menu_idx);\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);\n}\n\nbool desktop_settings_scene_quick_apps_menu_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsCustomEventSetDefault:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneQuickAppsDirectionMenu,\n                SCENE_STATE_SET_FAVORITE_APP);\n            scene_manager_next_scene(\n                app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu);\n            consumed = true;\n            break;\n        case DesktopSettingsCustomEventSetDummy:\n            scene_manager_set_scene_state(\n                app->scene_manager,\n                DesktopSettingsAppSceneQuickAppsDirectionMenu,\n                SCENE_STATE_SET_DUMMY_APP);\n            scene_manager_next_scene(\n                app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu);\n            consumed = true;\n            break;\n        default:\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        submenu_set_selected_item(app->submenu, 0);\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_scene_quick_apps_menu_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n    app->quick_apps_menu_idx = submenu_get_selected_item(app->submenu);\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c",
    "content": "#include <applications.h>\n#include <lib/toolbox/value_index.h>\n\n#include \"../desktop_settings_app.h\"\n#include \"desktop_settings_scene.h\"\n\ntypedef enum {\n    DesktopSettingsPinSetup = 0,\n    DesktopSettingsAutoLockDelay,\n    DesktopSettingsClockDisplay,\n    DesktopSettingsFavoriteApps,\n    DesktopSettingsHappyMode,\n} DesktopSettingsEntry;\n\n#define AUTO_LOCK_DELAY_COUNT 6\nstatic const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {\n    \"OFF\",\n    \"30s\",\n    \"60s\",\n    \"2min\",\n    \"5min\",\n    \"10min\",\n};\nstatic const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] =\n    {0, 30000, 60000, 120000, 300000, 600000};\n\n#define CLOCK_ENABLE_COUNT 2\nconst char* const clock_enable_text[CLOCK_ENABLE_COUNT] = {\n    \"OFF\",\n    \"ON\",\n};\n\nconst uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1};\n\nstatic void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {\n    DesktopSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nstatic void desktop_settings_scene_start_clock_enable_changed(VariableItem* item) {\n    DesktopSettingsApp* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, clock_enable_text[index]);\n    app->settings.display_clock = index;\n}\n\nstatic void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) {\n    DesktopSettingsApp* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, auto_lock_delay_text[index]);\n    app->settings.auto_lock_delay_ms = auto_lock_delay_value[index];\n}\n\nvoid desktop_settings_scene_start_on_enter(void* context) {\n    DesktopSettingsApp* app = context;\n    VariableItemList* variable_item_list = app->variable_item_list;\n\n    VariableItem* item;\n    uint8_t value_index;\n\n    variable_item_list_add(variable_item_list, \"PIN Setup\", 1, NULL, NULL);\n\n    item = variable_item_list_add(\n        variable_item_list,\n        \"Auto Lock Time\",\n        AUTO_LOCK_DELAY_COUNT,\n        desktop_settings_scene_start_auto_lock_delay_changed,\n        app);\n\n    value_index = value_index_uint32(\n        app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]);\n\n    item = variable_item_list_add(\n        variable_item_list,\n        \"Show Clock\",\n        CLOCK_ENABLE_COUNT,\n        desktop_settings_scene_start_clock_enable_changed,\n        app);\n\n    value_index =\n        value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, clock_enable_text[value_index]);\n\n    variable_item_list_add(variable_item_list, \"Set Quick Access Apps\", 1, NULL, NULL);\n\n    variable_item_list_add(variable_item_list, \"Happy Mode\", 1, NULL, NULL);\n\n    variable_item_list_set_enter_callback(\n        variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);\n}\n\nbool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {\n    DesktopSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DesktopSettingsPinSetup:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu);\n            break;\n\n        case DesktopSettingsFavoriteApps:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneQuickAppsMenu);\n            break;\n\n        case DesktopSettingsHappyMode:\n            scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneHappyMode);\n            break;\n\n        default:\n            break;\n        }\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid desktop_settings_scene_start_on_exit(void* context) {\n    DesktopSettingsApp* app = context;\n    variable_item_list_reset(app->variable_item_list);\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <toolbox/version.h>\n#include <assets_icons.h>\n#include <dolphin/helpers/dolphin_state.h>\n#include <dolphin/dolphin.h>\n\n#include \"desktop_settings_view_pin_setup_howto.h\"\n\nstruct DesktopSettingsViewPinSetupHowto {\n    View* view;\n    DesktopSettingsViewPinSetupHowtoDoneCallback callback;\n    void* context;\n};\n\nstatic void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) {\n    furi_assert(canvas);\n    UNUSED(model);\n\n    canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29);\n    elements_button_right(canvas, \"Next\");\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, \"Setting Up PIN\");\n\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text(canvas, 58, 24, \"Prepare to use\\narrows as\\nPIN symbols\");\n}\n\nstatic bool desktop_settings_view_pin_setup_howto_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopSettingsViewPinSetupHowto* instance = context;\n    bool consumed = false;\n\n    if((event->key == InputKeyRight) && (event->type == InputTypeShort)) {\n        instance->callback(instance->context);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_view_pin_setup_howto_set_callback(\n    DesktopSettingsViewPinSetupHowto* instance,\n    DesktopSettingsViewPinSetupHowtoDoneCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n    instance->callback = callback;\n    instance->context = context;\n}\n\nDesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc(void) {\n    DesktopSettingsViewPinSetupHowto* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto));\n    view->view = view_alloc();\n    view_set_context(view->view, view);\n    view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw);\n    view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input);\n\n    return view;\n}\n\nvoid desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance) {\n    furi_assert(instance);\n\n    view_free(instance->view);\n    free(instance);\n}\n\nView* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct DesktopSettingsViewPinSetupHowto DesktopSettingsViewPinSetupHowto;\n\ntypedef void (*DesktopSettingsViewPinSetupHowtoDoneCallback)(void*);\n\nvoid desktop_settings_view_pin_setup_howto_set_callback(\n    DesktopSettingsViewPinSetupHowto* instance,\n    DesktopSettingsViewPinSetupHowtoDoneCallback callback,\n    void* context);\nDesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc(void);\nvoid desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance);\nView* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance);\n"
  },
  {
    "path": "applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <toolbox/version.h>\n#include <assets_icons.h>\n#include <dolphin/helpers/dolphin_state.h>\n#include <dolphin/dolphin.h>\n\n#include \"desktop_settings_view_pin_setup_howto2.h\"\n\nstruct DesktopSettingsViewPinSetupHowto2 {\n    View* view;\n    DesktopSettingsViewPinSetupHowto2Callback cancel_callback;\n    DesktopSettingsViewPinSetupHowto2Callback ok_callback;\n    void* context;\n};\n\nstatic void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) {\n    furi_assert(canvas);\n    UNUSED(model);\n\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(\n        canvas,\n        64,\n        0,\n        AlignCenter,\n        AlignTop,\n        \"Forgotten PIN can only be\\n\"\n        \"reset with entire device.\\n\"\n        \"Read docs How to reset PIN.\");\n\n    elements_button_right(canvas, \"OK\");\n    elements_button_left(canvas, \"Cancel\");\n}\n\nstatic bool desktop_settings_view_pin_setup_howto2_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    DesktopSettingsViewPinSetupHowto2* instance = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        if(event->key == InputKeyRight) {\n            instance->ok_callback(instance->context);\n            consumed = true;\n        } else if(event->key == InputKeyLeft) {\n            instance->cancel_callback(instance->context);\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid desktop_settings_view_pin_setup_howto2_set_context(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    void* context) {\n    furi_assert(instance);\n    instance->context = context;\n}\n\nvoid desktop_settings_view_pin_setup_howto2_set_cancel_callback(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    DesktopSettingsViewPinSetupHowto2Callback callback) {\n    furi_assert(instance);\n    instance->cancel_callback = callback;\n}\n\nvoid desktop_settings_view_pin_setup_howto2_set_ok_callback(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    DesktopSettingsViewPinSetupHowto2Callback callback) {\n    furi_assert(instance);\n    instance->ok_callback = callback;\n}\n\nDesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc(void) {\n    DesktopSettingsViewPinSetupHowto2* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto2));\n    view->view = view_alloc();\n    view_set_context(view->view, view);\n    view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw);\n    view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input);\n\n    return view;\n}\n\nvoid desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance) {\n    furi_assert(instance);\n\n    view_free(instance->view);\n    free(instance);\n}\n\nView* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance) {\n    furi_assert(instance);\n    return instance->view;\n}\n"
  },
  {
    "path": "applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct DesktopSettingsViewPinSetupHowto2 DesktopSettingsViewPinSetupHowto2;\n\ntypedef void (*DesktopSettingsViewPinSetupHowto2Callback)(void*);\n\nDesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc(void);\nvoid desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance);\nView* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance);\nvoid desktop_settings_view_pin_setup_howto2_set_context(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    void* context);\nvoid desktop_settings_view_pin_setup_howto2_set_cancel_callback(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    DesktopSettingsViewPinSetupHowto2Callback callback);\nvoid desktop_settings_view_pin_setup_howto2_set_ok_callback(\n    DesktopSettingsViewPinSetupHowto2* instance,\n    DesktopSettingsViewPinSetupHowto2Callback callback);\n"
  },
  {
    "path": "applications/settings/dolphin_passport/application.fam",
    "content": "App(\n    appid=\"passport\",\n    name=\"Passport\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"passport_app\",\n    cdefines=[\"APP_PASSPORT\"],\n    requires=[\n        \"gui\",\n        \"dolphin\",\n    ],\n    stack_size=1 * 1024,\n    order=60,\n)\n"
  },
  {
    "path": "applications/settings/dolphin_passport/passport.c",
    "content": "#include <furi.h>\n#include <furi_hal_version.h>\n\n#include <gui/gui.h>\n#include <dolphin/dolphin.h>\n#include <dolphin/helpers/dolphin_state.h>\n\n#include <assets_icons.h>\n\n#define MOODS_TOTAL  3\n#define BUTTHURT_MAX 3\n\nstatic const Icon* const portrait_happy[BUTTHURT_MAX] = {\n    &I_passport_happy1_46x49,\n    &I_passport_happy2_46x49,\n    &I_passport_happy3_46x49};\nstatic const Icon* const portrait_ok[BUTTHURT_MAX] = {\n    &I_passport_okay1_46x49,\n    &I_passport_okay2_46x49,\n    &I_passport_okay3_46x49};\nstatic const Icon* const portrait_bad[BUTTHURT_MAX] = {\n    &I_passport_bad1_46x49,\n    &I_passport_bad2_46x49,\n    &I_passport_bad3_46x49};\n\nstatic const Icon* const* portraits[MOODS_TOTAL] = {portrait_happy, portrait_ok, portrait_bad};\n\nstatic void input_callback(InputEvent* input, void* ctx) {\n    FuriSemaphore* semaphore = ctx;\n\n    if((input->type == InputTypeShort) && (input->key == InputKeyBack)) {\n        furi_semaphore_release(semaphore);\n    }\n}\n\nstatic void render_callback(Canvas* canvas, void* ctx) {\n    DolphinStats* stats = ctx;\n\n    char level_str[20];\n    char mood_str[32];\n    uint8_t mood = 0;\n\n    if(stats->butthurt <= 4) {\n        mood = 0;\n        snprintf(mood_str, 20, \"Mood: Happy\");\n    } else if(stats->butthurt <= 9) {\n        mood = 1;\n        snprintf(mood_str, 20, \"Mood: Ok\");\n    } else {\n        mood = 2;\n        snprintf(mood_str, 20, \"Mood: Angry\");\n    }\n\n    uint32_t xp_progress = 0;\n    uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(stats->icounter);\n    uint32_t xp_for_current_level =\n        xp_to_levelup + dolphin_state_xp_above_last_levelup(stats->icounter);\n    if(stats->level == 3) {\n        xp_progress = 0;\n    } else {\n        xp_progress = xp_to_levelup * 64 / xp_for_current_level;\n    }\n\n    // multipass\n    canvas_draw_icon(canvas, 0, 0, &I_passport_left_6x46);\n    canvas_draw_icon(canvas, 0, 46, &I_passport_bottom_128x18);\n    canvas_draw_line(canvas, 6, 0, 125, 0);\n    canvas_draw_line(canvas, 127, 2, 127, 47);\n    canvas_draw_dot(canvas, 126, 1);\n\n    // portrait\n    furi_assert((stats->level > 0) && (stats->level <= 3));\n    canvas_draw_icon(canvas, 9, 5, portraits[mood][stats->level - 1]);\n    canvas_draw_line(canvas, 58, 16, 123, 16);\n    canvas_draw_line(canvas, 58, 30, 123, 30);\n    canvas_draw_line(canvas, 58, 44, 123, 44);\n\n    const char* my_name = furi_hal_version_get_name_ptr();\n    snprintf(level_str, 20, \"Level: %hu\", stats->level);\n    canvas_draw_str(canvas, 58, 12, my_name ? my_name : \"Unknown\");\n    canvas_draw_str(canvas, 58, 26, mood_str);\n    canvas_draw_str(canvas, 58, 40, level_str);\n\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, 123 - xp_progress, 47, xp_progress + 1, 6);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_line(canvas, 123, 47, 123, 52);\n}\n\nint32_t passport_app(void* p) {\n    UNUSED(p);\n    FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);\n    ViewPort* view_port = view_port_alloc();\n\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinStats stats = dolphin_stats(dolphin);\n    furi_record_close(RECORD_DOLPHIN);\n    view_port_draw_callback_set(view_port, render_callback, &stats);\n    view_port_input_callback_set(view_port, input_callback, semaphore);\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n    view_port_update(view_port);\n\n    furi_check(furi_semaphore_acquire(semaphore, FuriWaitForever) == FuriStatusOk);\n\n    gui_remove_view_port(gui, view_port);\n    view_port_free(view_port);\n    furi_record_close(RECORD_GUI);\n    furi_semaphore_free(semaphore);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/expansion_settings_app/application.fam",
    "content": "App(\n    appid=\"expansion_settings\",\n    name=\"Expansion Modules\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"expansion_settings_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    order=80,\n)\n"
  },
  {
    "path": "applications/settings/expansion_settings_app/expansion_settings_app.c",
    "content": "#include \"expansion_settings_app.h\"\n\nstatic const char* const expansion_uart_text[] = {\n    \"USART\",\n    \"LPUART\",\n    \"None\",\n};\n\nstatic void expansion_settings_app_uart_changed(VariableItem* item) {\n    ExpansionSettingsApp* app = variable_item_get_context(item);\n    const uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, expansion_uart_text[index]);\n    app->settings.uart_index = index;\n\n    if(index < FuriHalSerialIdMax) {\n        expansion_set_listen_serial(app->expansion, index);\n    } else {\n        expansion_disable(app->expansion);\n    }\n}\n\nstatic uint32_t expansion_settings_app_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic ExpansionSettingsApp* expansion_settings_app_alloc(void) {\n    ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp));\n\n    expansion_settings_load(&app->settings);\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->expansion = furi_record_open(RECORD_EXPANSION);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->var_item_list = variable_item_list_alloc();\n\n    VariableItem* item;\n    uint8_t value_index;\n\n    item = variable_item_list_add(\n        app->var_item_list,\n        \"Listen UART\",\n        COUNT_OF(expansion_uart_text),\n        expansion_settings_app_uart_changed,\n        app);\n    value_index = app->settings.uart_index;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, expansion_uart_text[value_index]);\n\n    view_set_previous_callback(\n        variable_item_list_get_view(app->var_item_list), expansion_settings_app_exit);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        ExpansionSettingsViewVarItemList,\n        variable_item_list_get_view(app->var_item_list));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, ExpansionSettingsViewVarItemList);\n\n    return app;\n}\n\nstatic void expansion_settings_app_free(ExpansionSettingsApp* app) {\n    furi_assert(app);\n\n    expansion_settings_save(&app->settings);\n\n    view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList);\n    variable_item_list_free(app->var_item_list);\n    view_dispatcher_free(app->view_dispatcher);\n\n    furi_record_close(RECORD_EXPANSION);\n    furi_record_close(RECORD_GUI);\n\n    free(app);\n}\n\nint32_t expansion_settings_app(void* p) {\n    UNUSED(p);\n    ExpansionSettingsApp* app = expansion_settings_app_alloc();\n    view_dispatcher_run(app->view_dispatcher);\n    expansion_settings_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/expansion_settings_app/expansion_settings_app.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/variable_item_list.h>\n\n#include <expansion/expansion.h>\n#include <expansion/expansion_settings.h>\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    VariableItemList* var_item_list;\n    Expansion* expansion;\n    ExpansionSettings settings;\n} ExpansionSettingsApp;\n\ntypedef enum {\n    ExpansionSettingsViewVarItemList,\n} ExpansionSettingsView;\n"
  },
  {
    "path": "applications/settings/notification_settings/application.fam",
    "content": "App(\n    appid=\"notification_settings\",\n    name=\"LCD and Notifications\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"notification_settings_app\",\n    requires=[\"notification\"],\n    stack_size=1 * 1024,\n    order=20,\n)\n"
  },
  {
    "path": "applications/settings/notification_settings/notification_settings_app.c",
    "content": "#include <furi.h>\n#include <notification/notification_app.h>\n#include <gui/modules/variable_item_list.h>\n#include <gui/view_dispatcher.h>\n#include <lib/toolbox/value_index.h>\n\n#define MAX_NOTIFICATION_SETTINGS 4\n\ntypedef struct {\n    NotificationApp* notification;\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    VariableItemList* variable_item_list;\n} NotificationAppSettings;\n\nstatic const NotificationSequence sequence_note_c = {\n    &message_note_c5,\n    &message_delay_100,\n    &message_sound_off,\n    NULL,\n};\n\n#define CONTRAST_COUNT 17\nconst char* const contrast_text[CONTRAST_COUNT] = {\n    \"-8\",\n    \"-7\",\n    \"-6\",\n    \"-5\",\n    \"-4\",\n    \"-3\",\n    \"-2\",\n    \"-1\",\n    \"0\",\n    \"+1\",\n    \"+2\",\n    \"+3\",\n    \"+4\",\n    \"+5\",\n    \"+6\",\n    \"+7\",\n    \"+8\",\n};\nconst int32_t contrast_value[CONTRAST_COUNT] = {\n    -8,\n    -7,\n    -6,\n    -5,\n    -4,\n    -3,\n    -2,\n    -1,\n    0,\n    1,\n    2,\n    3,\n    4,\n    5,\n    6,\n    7,\n    8,\n};\n\n#define BACKLIGHT_COUNT 21\nconst char* const backlight_text[BACKLIGHT_COUNT] = {\n    \"0%\",  \"5%\",  \"10%\", \"15%\", \"20%\", \"25%\", \"30%\", \"35%\", \"40%\", \"45%\",  \"50%\",\n    \"55%\", \"60%\", \"65%\", \"70%\", \"75%\", \"80%\", \"85%\", \"90%\", \"95%\", \"100%\",\n};\nconst float backlight_value[BACKLIGHT_COUNT] = {\n    0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f,\n    0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.00f,\n};\n\n#define VOLUME_COUNT 21\nconst char* const volume_text[VOLUME_COUNT] = {\n    \"0%\",  \"5%\",  \"10%\", \"15%\", \"20%\", \"25%\", \"30%\", \"35%\", \"40%\", \"45%\",  \"50%\",\n    \"55%\", \"60%\", \"65%\", \"70%\", \"75%\", \"80%\", \"85%\", \"90%\", \"95%\", \"100%\",\n};\nconst float volume_value[VOLUME_COUNT] = {\n    0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f,\n    0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.00f,\n};\n\n#define DELAY_COUNT 11\nconst char* const delay_text[DELAY_COUNT] = {\n    \"1s\",\n    \"5s\",\n    \"10s\",\n    \"15s\",\n    \"30s\",\n    \"60s\",\n    \"90s\",\n    \"120s\",\n    \"5min\",\n    \"10min\",\n    \"30min\",\n};\nconst uint32_t delay_value[DELAY_COUNT] =\n    {1000, 5000, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000, 1800000};\n\n#define VIBRO_COUNT 2\nconst char* const vibro_text[VIBRO_COUNT] = {\n    \"OFF\",\n    \"ON\",\n};\nconst bool vibro_value[VIBRO_COUNT] = {false, true};\n\nstatic void contrast_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, contrast_text[index]);\n    app->notification->settings.contrast = contrast_value[index];\n    notification_message(app->notification, &sequence_lcd_contrast_update);\n}\n\nstatic void backlight_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, backlight_text[index]);\n    app->notification->settings.display_brightness = backlight_value[index];\n    notification_message(app->notification, &sequence_display_backlight_on);\n}\n\nstatic void screen_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, delay_text[index]);\n    app->notification->settings.display_off_delay_ms = delay_value[index];\n    notification_message(app->notification, &sequence_display_backlight_on);\n}\n\nconst NotificationMessage apply_message = {\n    .type = NotificationMessageTypeLedBrightnessSettingApply,\n};\nconst NotificationSequence apply_sequence = {\n    &apply_message,\n    NULL,\n};\n\nstatic void led_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, backlight_text[index]);\n    app->notification->settings.led_brightness = backlight_value[index];\n    notification_message(app->notification, &apply_sequence);\n    notification_internal_message(app->notification, &apply_sequence);\n    notification_message(app->notification, &sequence_blink_white_100);\n}\n\nstatic void volume_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, volume_text[index]);\n    app->notification->settings.speaker_volume = volume_value[index];\n    notification_message(app->notification, &sequence_note_c);\n}\n\nstatic void vibro_changed(VariableItem* item) {\n    NotificationAppSettings* app = variable_item_get_context(item);\n    uint8_t index = variable_item_get_current_value_index(item);\n\n    variable_item_set_current_value_text(item, vibro_text[index]);\n    app->notification->settings.vibro_on = vibro_value[index];\n    notification_message(app->notification, &sequence_single_vibro);\n}\n\nstatic uint32_t notification_app_settings_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic NotificationAppSettings* alloc_settings(void) {\n    NotificationAppSettings* app = malloc(sizeof(NotificationAppSettings));\n    app->notification = furi_record_open(RECORD_NOTIFICATION);\n    app->gui = furi_record_open(RECORD_GUI);\n\n    app->variable_item_list = variable_item_list_alloc();\n    View* view = variable_item_list_get_view(app->variable_item_list);\n    view_set_previous_callback(view, notification_app_settings_exit);\n\n    VariableItem* item;\n    uint8_t value_index;\n\n    item = variable_item_list_add(\n        app->variable_item_list, \"LCD Contrast\", CONTRAST_COUNT, contrast_changed, app);\n    value_index =\n        value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, contrast_text[value_index]);\n\n    item = variable_item_list_add(\n        app->variable_item_list, \"LCD Backlight\", BACKLIGHT_COUNT, backlight_changed, app);\n    value_index = value_index_float(\n        app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, backlight_text[value_index]);\n\n    item = variable_item_list_add(\n        app->variable_item_list, \"Backlight Time\", DELAY_COUNT, screen_changed, app);\n    value_index = value_index_uint32(\n        app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, delay_text[value_index]);\n\n    item = variable_item_list_add(\n        app->variable_item_list, \"LED Brightness\", BACKLIGHT_COUNT, led_changed, app);\n    value_index = value_index_float(\n        app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, backlight_text[value_index]);\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {\n        item = variable_item_list_add(app->variable_item_list, \"Volume\", 1, NULL, app);\n        value_index = 0;\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, \"Stealth\");\n    } else {\n        item = variable_item_list_add(\n            app->variable_item_list, \"Volume\", VOLUME_COUNT, volume_changed, app);\n        value_index = value_index_float(\n            app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT);\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, volume_text[value_index]);\n    }\n\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {\n        item = variable_item_list_add(app->variable_item_list, \"Vibro\", 1, NULL, app);\n        value_index = 0;\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, \"Stealth\");\n    } else {\n        item = variable_item_list_add(\n            app->variable_item_list, \"Vibro\", VIBRO_COUNT, vibro_changed, app);\n        value_index =\n            value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT);\n        variable_item_set_current_value_index(item, value_index);\n        variable_item_set_current_value_text(item, vibro_text[value_index]);\n    }\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n    view_dispatcher_add_view(app->view_dispatcher, 0, view);\n    view_dispatcher_switch_to_view(app->view_dispatcher, 0);\n\n    return app;\n}\n\nstatic void free_settings(NotificationAppSettings* app) {\n    view_dispatcher_remove_view(app->view_dispatcher, 0);\n    variable_item_list_free(app->variable_item_list);\n    view_dispatcher_free(app->view_dispatcher);\n\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    free(app);\n}\n\nint32_t notification_settings_app(void* p) {\n    UNUSED(p);\n    NotificationAppSettings* app = alloc_settings();\n    view_dispatcher_run(app->view_dispatcher);\n    notification_message_save_settings(app->notification);\n    free_settings(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/application.fam",
    "content": "App(\n    appid=\"power_settings\",\n    name=\"Power\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"power_settings_app\",\n    requires=[\n        \"gui\",\n        \"power\",\n        \"locale\",\n    ],\n    flags=[\"InsomniaSafe\"],\n    stack_size=1 * 1024,\n    order=40,\n)\n"
  },
  {
    "path": "applications/settings/power_settings_app/power_settings_app.c",
    "content": "#include \"power_settings_app.h\"\n\nconst SubmenuSettingsHelperDescriptor settings_helper_descriptor = {\n    .app_name = \"Power\",\n    .options_cnt = 3,\n    .options =\n        {\n            {.name = \"Battery Info\", .scene_id = PowerSettingsAppSceneBatteryInfo},\n            {.name = \"Reboot\", .scene_id = PowerSettingsAppSceneReboot},\n            {.name = \"Power OFF\", .scene_id = PowerSettingsAppScenePowerOff},\n        },\n};\n\nstatic bool power_settings_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool power_settings_back_event_callback(void* context) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic void power_settings_tick_event_callback(void* context) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nPowerSettingsApp* power_settings_app_alloc(void) {\n    PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp));\n\n    // Records\n    app->gui = furi_record_open(RECORD_GUI);\n    app->power = furi_record_open(RECORD_POWER);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app);\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, power_settings_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, power_settings_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        app->view_dispatcher, power_settings_tick_event_callback, 2000);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Views\n    app->batery_info = battery_info_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        PowerSettingsAppViewBatteryInfo,\n        battery_info_get_view(app->batery_info));\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, PowerSettingsAppViewSubmenu, submenu_get_view(app->submenu));\n    app->dialog = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, PowerSettingsAppViewDialog, dialog_ex_get_view(app->dialog));\n\n    // Helper\n    app->settings_helper = submenu_settings_helpers_alloc(&settings_helper_descriptor);\n    submenu_settings_helpers_assign_objects(\n        app->settings_helper,\n        app->view_dispatcher,\n        app->scene_manager,\n        app->submenu,\n        PowerSettingsAppViewSubmenu,\n        PowerSettingsAppSceneStart);\n\n    return app;\n}\n\nvoid power_settings_app_free(PowerSettingsApp* app) {\n    furi_assert(app);\n    // Helper\n    submenu_settings_helpers_free(app->settings_helper);\n    // Views\n    view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo);\n    battery_info_free(app->batery_info);\n    view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu);\n    submenu_free(app->submenu);\n    view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewDialog);\n    dialog_ex_free(app->dialog);\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n    // Records\n    furi_record_close(RECORD_POWER);\n    furi_record_close(RECORD_GUI);\n    free(app);\n}\n\nint32_t power_settings_app(void* p) {\n    PowerSettingsApp* app = power_settings_app_alloc();\n    if(!submenu_settings_helpers_app_start(app->settings_helper, p)) {\n        uint32_t first_scene = PowerSettingsAppSceneStart;\n        if(p && strlen(p) && !strcmp(p, \"off\")) {\n            first_scene = PowerSettingsAppScenePowerOff;\n        }\n        scene_manager_next_scene(app->scene_manager, first_scene);\n    }\n    view_dispatcher_run(app->view_dispatcher);\n    power_settings_app_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/power_settings_app.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <power/power_service/power.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <assets_icons.h>\n\n#include \"views/battery_info.h\"\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n\n#include \"scenes/power_settings_scene.h\"\n\n#include <settings_helpers/submenu_based.h>\n\ntypedef struct {\n    Power* power;\n    Gui* gui;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n    BatteryInfo* batery_info;\n    Submenu* submenu;\n    DialogEx* dialog;\n    PowerInfo info;\n    SubmenuSettingsHelper* settings_helper;\n} PowerSettingsApp;\n\ntypedef enum {\n    PowerSettingsAppViewBatteryInfo,\n    PowerSettingsAppViewSubmenu,\n    PowerSettingsAppViewDialog,\n} PowerSettingsAppView;\n\ntypedef enum {\n    RebootTypeDFU,\n    RebootTypeNormal\n} RebootType;\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene.c",
    "content": "#include \"power_settings_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const power_settings_on_enter_handlers[])(void*) = {\n#include \"power_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const power_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"power_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const power_settings_on_exit_handlers[])(void* context) = {\n#include \"power_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers power_settings_scene_handlers = {\n    .on_enter_handlers = power_settings_on_enter_handlers,\n    .on_event_handlers = power_settings_on_event_handlers,\n    .on_exit_handlers = power_settings_on_exit_handlers,\n    .scene_num = PowerSettingsAppSceneNum,\n};\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) PowerSettingsAppScene##id,\ntypedef enum {\n#include \"power_settings_scene_config.h\"\n    PowerSettingsAppSceneNum,\n} PowerSettingsAppScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers power_settings_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"power_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"power_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"power_settings_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c",
    "content": "#include \"../power_settings_app.h\"\n\nstatic void power_settings_scene_battery_info_update_model(PowerSettingsApp* app) {\n    power_get_info(app->power, &app->info);\n    BatteryInfoModel battery_info_data = {\n        .vbus_voltage = app->info.voltage_vbus,\n        .gauge_voltage = app->info.voltage_gauge,\n        .gauge_current = app->info.current_gauge,\n        .gauge_temperature = app->info.temperature_gauge,\n        .charge_voltage_limit = app->info.voltage_battery_charge_limit,\n        .charge = app->info.charge,\n        .health = app->info.health,\n    };\n    battery_info_set_data(app->batery_info, &battery_info_data);\n}\n\nvoid power_settings_scene_battery_info_on_enter(void* context) {\n    PowerSettingsApp* app = context;\n    power_settings_scene_battery_info_update_model(app);\n    view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo);\n}\n\nbool power_settings_scene_battery_info_on_event(void* context, SceneManagerEvent event) {\n    PowerSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        power_settings_scene_battery_info_update_model(app);\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid power_settings_scene_battery_info_on_exit(void* context) {\n    UNUSED(context);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_config.h",
    "content": "ADD_SCENE(power_settings, start, Start)\nADD_SCENE(power_settings, battery_info, BatteryInfo)\nADD_SCENE(power_settings, reboot, Reboot)\nADD_SCENE(power_settings, reboot_confirm, RebootConfirm)\nADD_SCENE(power_settings, power_off, PowerOff)\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c",
    "content": "#include \"../power_settings_app.h\"\n#include <dolphin/dolphin.h>\n\nvoid power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid power_settings_scene_power_off_on_enter(void* context) {\n    PowerSettingsApp* app = context;\n    DialogEx* dialog = app->dialog;\n    Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);\n    DolphinSettings settings;\n    dolphin_get_settings(dolphin, &settings);\n    furi_record_close(RECORD_DOLPHIN);\n\n    dialog_ex_set_header(\n        dialog,\n        \"Turn Off Device?\",\n        64,\n        settings.happy_mode ? 32 : 0,\n        AlignCenter,\n        settings.happy_mode ? AlignCenter : AlignTop);\n    if(!settings.happy_mode) {\n        dialog_ex_set_text(\n            dialog, \"   I will be\\nwaiting for\\n you here...\", 78, 14, AlignLeft, AlignTop);\n        dialog_ex_set_icon(dialog, 14, 10, &I_dolph_cry_49x54);\n    }\n    dialog_ex_set_left_button_text(dialog, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog, \"Power Off\");\n    dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback);\n    dialog_ex_set_context(dialog, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog);\n}\n\nbool power_settings_scene_power_off_on_event(void* context, SceneManagerEvent event) {\n    PowerSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            if(!scene_manager_previous_scene(app->scene_manager)) {\n                scene_manager_stop(app->scene_manager);\n                view_dispatcher_stop(app->view_dispatcher);\n            }\n        } else if(event.event == DialogExResultRight) {\n            power_off(app->power);\n        }\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid power_settings_scene_power_off_on_exit(void* context) {\n    PowerSettingsApp* app = context;\n    dialog_ex_reset(app->dialog);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c",
    "content": "#include \"../power_settings_app.h\"\n\nenum PowerSettingsRebootSubmenuIndex {\n    PowerSettingsRebootSubmenuIndexDfu,\n    PowerSettingsRebootSubmenuIndexOs,\n};\n\nvoid power_settings_scene_reboot_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid power_settings_scene_reboot_on_enter(void* context) {\n    PowerSettingsApp* app = context;\n    Submenu* submenu = app->submenu;\n\n    submenu_set_header(submenu, \"Reboot Type\");\n    submenu_add_item(\n        submenu,\n        \"Firmware Upgrade\",\n        PowerSettingsRebootSubmenuIndexDfu,\n        power_settings_scene_reboot_submenu_callback,\n        app);\n    submenu_add_item(\n        submenu,\n        \"Reboot Flipper\",\n        PowerSettingsRebootSubmenuIndexOs,\n        power_settings_scene_reboot_submenu_callback,\n        app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu);\n}\n\nbool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) {\n    PowerSettingsApp* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == PowerSettingsRebootSubmenuIndexDfu) {\n            scene_manager_set_scene_state(\n                app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeDFU);\n            scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm);\n        } else if(event.event == PowerSettingsRebootSubmenuIndexOs) {\n            scene_manager_set_scene_state(\n                app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeNormal);\n            scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm);\n        }\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid power_settings_scene_reboot_on_exit(void* context) {\n    PowerSettingsApp* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c",
    "content": "#include \"../power_settings_app.h\"\n\nvoid power_settings_scene_reboot_confirm_dialog_callback(DialogExResult result, void* context) {\n    furi_assert(context);\n    PowerSettingsApp* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid power_settings_scene_reboot_confirm_on_enter(void* context) {\n    PowerSettingsApp* app = context;\n    DialogEx* dialog = app->dialog;\n\n    RebootType reboot_type =\n        scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm);\n\n    if(reboot_type == RebootTypeDFU) {\n        dialog_ex_set_header(dialog, \"Reboot to DFU Mode?\", 64, 0, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog,\n            \"Needed for device maintenance\\nor firmware upgrades\",\n            64,\n            14,\n            AlignCenter,\n            AlignTop);\n    } else if(reboot_type == RebootTypeNormal) {\n        dialog_ex_set_header(dialog, \"Reboot Flipper?\", 64, 0, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog, \"May help with some firmware\\n issues\", 64, 14, AlignCenter, AlignTop);\n    } else {\n        furi_crash(\"Invalid reboot type\");\n    }\n\n    dialog_ex_set_left_button_text(dialog, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog, \"Reboot\");\n\n    dialog_ex_set_result_callback(dialog, power_settings_scene_reboot_confirm_dialog_callback);\n    dialog_ex_set_context(dialog, app);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog);\n}\n\nbool power_settings_scene_reboot_confirm_on_event(void* context, SceneManagerEvent event) {\n    PowerSettingsApp* app = context;\n    bool consumed = false;\n    RebootType reboot_type =\n        scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == DialogExResultLeft) {\n            scene_manager_previous_scene(app->scene_manager);\n        } else if(event.event == DialogExResultRight) {\n            Power* power = furi_record_open(RECORD_POWER);\n\n            if(reboot_type == RebootTypeDFU) {\n                power_reboot(power, PowerBootModeDfu);\n            } else {\n                power_reboot(power, PowerBootModeNormal);\n            }\n        }\n        consumed = true;\n    }\n    return consumed;\n}\n\nvoid power_settings_scene_reboot_confirm_on_exit(void* context) {\n    PowerSettingsApp* app = context;\n    dialog_ex_reset(app->dialog);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/scenes/power_settings_scene_start.c",
    "content": "#include \"../power_settings_app.h\"\n\nvoid power_settings_scene_start_on_enter(void* context) {\n    PowerSettingsApp* app = context;\n    submenu_settings_helpers_scene_enter(app->settings_helper);\n}\n\nbool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) {\n    PowerSettingsApp* app = context;\n    return submenu_settings_helpers_scene_event(app->settings_helper, event);\n}\n\nvoid power_settings_scene_start_on_exit(void* context) {\n    PowerSettingsApp* app = context;\n    submenu_settings_helpers_scene_exit(app->settings_helper);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/views/battery_info.c",
    "content": "#include \"battery_info.h\"\n#include <furi.h>\n#include <gui/elements.h>\n#include <assets_icons.h>\n#include <locale/locale.h>\n\n#define LOW_CHARGE_THRESHOLD         (10)\n#define HIGH_DRAIN_CURRENT_THRESHOLD (-100)\n\nstruct BatteryInfo {\n    View* view;\n};\n\nstatic void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) {\n    canvas_draw_frame(canvas, x - 7, y + 7, 30, 13);\n    canvas_draw_icon(canvas, x, y, icon);\n    canvas_set_color(canvas, ColorWhite);\n    canvas_draw_box(canvas, x - 4, y + 16, 24, 6);\n    canvas_set_color(canvas, ColorBlack);\n    canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);\n}\n\nstatic void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {\n    char emote[20] = {};\n    char header[20] = {};\n    char value[20] = {};\n\n    int32_t current = 1000.0f * data->gauge_current;\n\n    // Draw battery\n    canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28);\n    if(current > 0) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14);\n    } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14);\n    } else if(data->charge < LOW_CHARGE_THRESHOLD) {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14);\n    } else {\n        canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14);\n    }\n\n    // Draw bubble\n    elements_bubble(canvas, 53, 0, 71, 39);\n\n    // Set text\n    if(current > 0) {\n        snprintf(emote, sizeof(emote), \"%s\", \"Yummy!\");\n        snprintf(header, sizeof(header), \"%s\", \"Charging at\");\n        snprintf(\n            value,\n            sizeof(value),\n            \"%lu.%luV   %lumA\",\n            (uint32_t)(data->vbus_voltage),\n            (uint32_t)(data->vbus_voltage * 10) % 10,\n            current);\n    } else if(current < -5) {\n        // 0-5ma deadband\n        snprintf(\n            emote,\n            sizeof(emote),\n            \"%s\",\n            current < HIGH_DRAIN_CURRENT_THRESHOLD ? \"Oh no!\" : \"Om-nom-nom!\");\n        snprintf(header, sizeof(header), \"%s\", \"Consumption is\");\n        snprintf(\n            value,\n            sizeof(value),\n            \"%ld %s\",\n            ABS(current),\n            current < HIGH_DRAIN_CURRENT_THRESHOLD ? \"mA!\" : \"mA\");\n    } else if(data->vbus_voltage > 0) {\n        if(data->charge_voltage_limit < 4.2f) {\n            // Non-default battery charging limit, mention it\n            snprintf(emote, sizeof(emote), \"Charged!\");\n            snprintf(header, sizeof(header), \"Limited to\");\n            snprintf(\n                value,\n                sizeof(value),\n                \"%lu.%luV\",\n                (uint32_t)(data->charge_voltage_limit),\n                (uint32_t)(data->charge_voltage_limit * 10) % 10);\n        } else {\n            snprintf(header, sizeof(header), \"Charged!\");\n        }\n    } else {\n        snprintf(header, sizeof(header), \"Napping...\");\n    }\n\n    canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);\n    canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);\n    canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);\n}\n\nstatic void battery_info_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    BatteryInfoModel* model = context;\n\n    canvas_clear(canvas);\n    canvas_set_color(canvas, ColorBlack);\n    draw_battery(canvas, model, 0, 5);\n\n    char batt_level[10];\n    char temperature[10];\n    char voltage[10];\n    char health[10];\n\n    snprintf(batt_level, sizeof(batt_level), \"%lu%%\", (uint32_t)model->charge);\n    if(locale_get_measurement_unit() == LocaleMeasurementUnitsMetric) {\n        snprintf(temperature, sizeof(temperature), \"%lu C\", (uint32_t)model->gauge_temperature);\n    } else {\n        snprintf(\n            temperature,\n            sizeof(temperature),\n            \"%lu F\",\n            (uint32_t)locale_celsius_to_fahrenheit(model->gauge_temperature));\n    }\n    snprintf(\n        voltage,\n        sizeof(voltage),\n        \"%lu.%01lu V\",\n        (uint32_t)model->gauge_voltage,\n        (uint32_t)(model->gauge_voltage * 10) % 10UL);\n    snprintf(health, sizeof(health), \"%d%%\", model->health);\n\n    draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level);\n    draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature);\n    draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage);\n    draw_stat(canvas, 104, 42, &I_Health_16x16, health);\n}\n\nBatteryInfo* battery_info_alloc(void) {\n    BatteryInfo* battery_info = malloc(sizeof(BatteryInfo));\n    battery_info->view = view_alloc();\n    view_set_context(battery_info->view, battery_info);\n    view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel));\n    view_set_draw_callback(battery_info->view, battery_info_draw_callback);\n\n    return battery_info;\n}\n\nvoid battery_info_free(BatteryInfo* battery_info) {\n    furi_assert(battery_info);\n    view_free(battery_info->view);\n    free(battery_info);\n}\n\nView* battery_info_get_view(BatteryInfo* battery_info) {\n    furi_assert(battery_info);\n    return battery_info->view;\n}\n\nvoid battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) {\n    furi_assert(battery_info);\n    furi_assert(data);\n    with_view_model(\n        battery_info->view,\n        BatteryInfoModel * model,\n        { memcpy(model, data, sizeof(BatteryInfoModel)); },\n        true);\n}\n"
  },
  {
    "path": "applications/settings/power_settings_app/views/battery_info.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct BatteryInfo BatteryInfo;\n\ntypedef struct {\n    float vbus_voltage;\n    float gauge_voltage;\n    float gauge_current;\n    float gauge_temperature;\n    float charge_voltage_limit;\n    uint8_t charge;\n    uint8_t health;\n} BatteryInfoModel;\n\nBatteryInfo* battery_info_alloc(void);\n\nvoid battery_info_free(BatteryInfo* battery_info);\n\nView* battery_info_get_view(BatteryInfo* battery_info);\n\nvoid battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data);\n"
  },
  {
    "path": "applications/settings/storage_settings/application.fam",
    "content": "App(\n    appid=\"storage_settings\",\n    name=\"Storage\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"storage_settings_app\",\n    requires=[\"storage\"],\n    stack_size=2 * 1024,\n    order=30,\n)\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene.c",
    "content": "#include \"storage_settings_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const storage_settings_on_enter_handlers[])(void*) = {\n#include \"storage_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const storage_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"storage_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const storage_settings_on_exit_handlers[])(void* context) = {\n#include \"storage_settings_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers storage_settings_scene_handlers = {\n    .on_enter_handlers = storage_settings_on_enter_handlers,\n    .on_event_handlers = storage_settings_on_event_handlers,\n    .on_exit_handlers = storage_settings_on_exit_handlers,\n    .scene_num = StorageSettingsSceneNum,\n};\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) StorageSettings##id,\ntypedef enum {\n#include \"storage_settings_scene_config.h\"\n    StorageSettingsSceneNum,\n} StorageSettingsScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers storage_settings_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"storage_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"storage_settings_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"storage_settings_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c",
    "content": "#include \"../storage_settings.h\"\n#include <furi_hal.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\n#define BENCH_DATA_SIZE 4096\n#define BENCH_COUNT     6\n#define BENCH_REPEATS   4\n#define BENCH_FILE      EXT_PATH(\"rwfiletest.bin\")\n\nstatic void\n    storage_settings_scene_benchmark_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nstatic bool storage_settings_scene_bench_write(\n    Storage* api,\n    uint16_t size,\n    const uint8_t* data,\n    uint32_t* speed) {\n    File* file = storage_file_alloc(api);\n    bool result = true;\n    if(storage_file_open(file, BENCH_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n        uint32_t ticks;\n        ticks = furi_get_tick();\n\n        for(size_t repeat = 0; repeat < BENCH_REPEATS; repeat++) {\n            for(size_t i = 0; i < BENCH_DATA_SIZE / size; i++) {\n                if(storage_file_write(file, (data + i * size), size) != size) {\n                    result = false;\n                    break;\n                }\n            }\n        }\n\n        ticks = furi_get_tick() - ticks;\n        *speed = BENCH_DATA_SIZE * furi_kernel_get_tick_frequency() * BENCH_REPEATS;\n        *speed /= ticks;\n        *speed /= 1024;\n    }\n    storage_file_close(file);\n    storage_file_free(file);\n    return result;\n}\n\nstatic bool\n    storage_settings_scene_bench_read(Storage* api, size_t size, uint8_t* data, uint32_t* speed) {\n    File* file = storage_file_alloc(api);\n    bool result = true;\n    *speed = -1;\n\n    if(storage_file_open(file, BENCH_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        uint32_t ticks;\n        ticks = furi_get_tick();\n\n        for(size_t repeat = 0; repeat < BENCH_REPEATS; repeat++) {\n            for(size_t i = 0; i < BENCH_DATA_SIZE / size; i++) {\n                if(storage_file_read(file, (data + i * size), size) != size) {\n                    result = false;\n                    break;\n                }\n            }\n        }\n\n        ticks = furi_get_tick() - ticks;\n        *speed = BENCH_DATA_SIZE * furi_kernel_get_tick_frequency() * BENCH_REPEATS;\n        *speed /= ticks;\n        *speed /= 1024;\n    }\n    storage_file_close(file);\n    storage_file_free(file);\n    return result;\n}\n\nstatic void storage_settings_scene_benchmark(StorageSettings* app) {\n    DialogEx* dialog_ex = app->dialog_ex;\n    uint8_t* bench_data;\n    dialog_ex_set_header(dialog_ex, \"Preparing Data...\", 64, 32, AlignCenter, AlignCenter);\n\n    bench_data = malloc(BENCH_DATA_SIZE);\n    for(size_t i = 0; i < BENCH_DATA_SIZE; i++) {\n        bench_data[i] = (uint8_t)i;\n    }\n\n    size_t bench_size[BENCH_COUNT] = {1, 8, 32, 256, 512, 1024};\n    uint32_t bench_w_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0};\n    uint32_t bench_r_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0};\n\n    dialog_ex_set_header(dialog_ex, \"Benchmarking...\", 74, 32, AlignCenter, AlignCenter);\n    dialog_ex_set_icon(dialog_ex, 12, 20, &I_LoadingHourglass_24x24);\n    for(size_t i = 0; i < BENCH_COUNT; i++) {\n        if(!storage_settings_scene_bench_write(\n               app->fs_api, bench_size[i], bench_data, &bench_w_speed[i]))\n            break;\n\n        if(i > 0) furi_string_cat_printf(app->text_string, \"\\n\");\n        furi_string_cat_printf(app->text_string, \"%ub : W %luK \", bench_size[i], bench_w_speed[i]);\n        dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);\n        dialog_ex_set_icon(dialog_ex, 0, 0, NULL);\n        dialog_ex_set_text(\n            dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter);\n\n        if(!storage_settings_scene_bench_read(\n               app->fs_api, bench_size[i], bench_data, &bench_r_speed[i]))\n            break;\n\n        furi_string_cat_printf(app->text_string, \"R %luK\", bench_r_speed[i]);\n\n        storage_common_remove(app->fs_api, BENCH_FILE);\n\n        dialog_ex_set_text(\n            dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter);\n    }\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_single_vibro);\n    notification_message(notification, &sequence_set_green_255);\n    notification_message(notification, &sequence_success);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    free(bench_data);\n}\n\nvoid storage_settings_scene_benchmark_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n    scene_manager_set_scene_state(app->scene_manager, StorageSettingsBenchmark, sd_status);\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_benchmark_dialog_callback);\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n\n    if(sd_status != FSE_OK) {\n        dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n        dialog_ex_set_header(dialog_ex, \"SD Card Not Mounted\", 64, 3, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex, \"Try to reinsert\\nor format SD\\ncard.\", 3, 19, AlignLeft, AlignTop);\n        dialog_ex_set_center_button_text(dialog_ex, \"Ok\");\n    } else {\n        storage_settings_scene_benchmark(app);\n        notification_message(app->notification, &sequence_blink_green_100);\n    }\n}\n\nbool storage_settings_scene_benchmark_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    FS_Error sd_status =\n        scene_manager_get_scene_state(app->scene_manager, StorageSettingsBenchmark);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultCenter:\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, StorageSettingsStart);\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        if(sd_status == FSE_OK) {\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, StorageSettingsStart);\n        } else {\n            consumed = true;\n        }\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_benchmark_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_reset_green);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    dialog_ex_reset(dialog_ex);\n\n    furi_string_reset(app->text_string);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c",
    "content": "#include \"../storage_settings.h\"\n\nstatic void\n    storage_settings_scene_benchmark_confirm_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_benchmark_confirm_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n\n    if(sd_status == FSE_NOT_READY) {\n        dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n        dialog_ex_set_header(dialog_ex, \"SD Card Not Mounted\", 64, 3, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex, \"Try to reinsert\\nor format SD\\ncard.\", 3, 19, AlignLeft, AlignTop);\n        dialog_ex_set_center_button_text(dialog_ex, \"Ok\");\n    } else {\n        dialog_ex_set_header(dialog_ex, \"Benchmark SD Card?\", 64, 0, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex,\n            \"SD will be tested in SPI\\nmode. Learn more:\\nr.flipper.net/sd_test\",\n            0,\n            12,\n            AlignLeft,\n            AlignTop);\n        dialog_ex_set_icon(dialog_ex, 103, 12, &I_qr_benchmark_25x25);\n        dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n        dialog_ex_set_right_button_text(dialog_ex, \"Benchmark\");\n    }\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(\n        dialog_ex, storage_settings_scene_benchmark_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_benchmark_confirm_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n        case DialogExResultCenter:\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        case DialogExResultRight:\n            scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark);\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_benchmark_confirm_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_config.h",
    "content": "ADD_SCENE(storage_settings, start, Start)\nADD_SCENE(storage_settings, unmount_confirm, UnmountConfirm)\nADD_SCENE(storage_settings, unmounted, Unmounted)\nADD_SCENE(storage_settings, format_confirm, FormatConfirm)\nADD_SCENE(storage_settings, formatting, Formatting)\nADD_SCENE(storage_settings, sd_info, SDInfo)\nADD_SCENE(storage_settings, internal_info, InternalInfo)\nADD_SCENE(storage_settings, benchmark_confirm, BenchmarkConfirm)\nADD_SCENE(storage_settings, benchmark, Benchmark)\nADD_SCENE(storage_settings, factory_reset, FactoryReset)\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c",
    "content": "#include \"../storage_settings.h\"\n#include <power/power_service/power.h>\n#include <furi_hal.h>\n\n#define STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT 5\n\nstatic void\n    storage_settings_scene_factory_reset_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_factory_reset_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_factory_reset_dialog_callback);\n\n    dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n    dialog_ex_set_right_button_text(dialog_ex, \"Erase\");\n\n    dialog_ex_set_header(dialog_ex, \"Confirm Factory Reset?\", 64, 0, AlignCenter, AlignTop);\n    dialog_ex_set_text(\n        dialog_ex,\n        \"Internal storage will be erased\\ndata and settings will be lost!\",\n        64,\n        14,\n        AlignCenter,\n        AlignTop);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_factory_reset_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    uint32_t counter =\n        scene_manager_get_scene_state(app->scene_manager, StorageSettingsFactoryReset);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n            scene_manager_set_scene_state(app->scene_manager, StorageSettingsFactoryReset, 0);\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        case DialogExResultRight:\n            counter++;\n            if(counter < STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT) {\n                furi_string_printf(\n                    app->text_string,\n                    \"%ld presses left\",\n                    STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT - counter);\n                dialog_ex_set_text(\n                    app->dialog_ex,\n                    furi_string_get_cstr(app->text_string),\n                    64,\n                    32,\n                    AlignCenter,\n                    AlignCenter);\n                scene_manager_set_scene_state(\n                    app->scene_manager, StorageSettingsFactoryReset, counter);\n            } else {\n                furi_hal_rtc_reset_registers();\n                furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);\n\n                Power* power = furi_record_open(RECORD_POWER);\n                power_reboot(power, PowerBootModeNormal);\n            }\n\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_factory_reset_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n\n    furi_string_reset(app->text_string);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c",
    "content": "#include \"../storage_settings.h\"\n\nstatic void\n    storage_settings_scene_format_confirm_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_format_confirm_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n\n    if(sd_status == FSE_NOT_READY) {\n        dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n        dialog_ex_set_header(dialog_ex, \"SD Card Not Mounted\", 64, 3, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex, \"Try to reinsert\\nor format SD\\ncard.\", 3, 19, AlignLeft, AlignTop);\n        dialog_ex_set_center_button_text(dialog_ex, \"Ok\");\n    } else {\n        dialog_ex_set_header(dialog_ex, \"Format SD Card?\", 64, 0, AlignCenter, AlignTop);\n        dialog_ex_set_text(dialog_ex, \"All data will be lost!\", 64, 12, AlignCenter, AlignTop);\n        dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n        dialog_ex_set_right_button_text(dialog_ex, \"Format\");\n    }\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(\n        dialog_ex, storage_settings_scene_format_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_format_confirm_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n        case DialogExResultCenter:\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        case DialogExResultRight:\n            scene_manager_next_scene(app->scene_manager, StorageSettingsFormatting);\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_format_confirm_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c",
    "content": "#include \"../storage_settings.h\"\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\nstatic const NotificationMessage message_green_165 = {\n    .type = NotificationMessageTypeLedGreen,\n    .data.led.value = 165,\n};\n\nstatic const NotificationSequence sequence_set_formatting_leds = {\n    &message_red_255,\n    &message_green_165,\n    &message_blue_0,\n    &message_do_not_reset,\n    NULL,\n};\n\nstatic const NotificationSequence sequence_reset_formatting_leds = {\n    &message_red_0,\n    &message_green_0,\n    NULL,\n};\n\nstatic void\n    storage_settings_scene_formatting_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_formatting_on_enter(void* context) {\n    StorageSettings* app = context;\n    FS_Error error;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_set_header(dialog_ex, \"Formatting...\", 70, 32, AlignCenter, AlignCenter);\n    dialog_ex_set_icon(dialog_ex, 15, 20, &I_LoadingHourglass_24x24);\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n\n    notification_message_block(app->notification, &sequence_set_formatting_leds);\n    error = storage_sd_format(app->fs_api);\n    notification_message(app->notification, &sequence_reset_formatting_leds);\n    notification_message(app->notification, &sequence_blink_green_100);\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_formatting_dialog_callback);\n\n    if(error != FSE_OK) {\n        dialog_ex_set_header(dialog_ex, \"Cannot Format SD Card\", 64, 10, AlignCenter, AlignCenter);\n        dialog_ex_set_icon(dialog_ex, 0, 0, NULL);\n        dialog_ex_set_text(\n            dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);\n    } else {\n        dialog_ex_set_icon(dialog_ex, 48, 6, &I_DolphinDone_80x58);\n        dialog_ex_set_header(dialog_ex, \"Formatted\", 5, 10, AlignLeft, AlignTop);\n        NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n        notification_message(notification, &sequence_single_vibro);\n        notification_message(notification, &sequence_set_green_255);\n        notification_message(notification, &sequence_success);\n        furi_record_close(RECORD_NOTIFICATION);\n    }\n    dialog_ex_set_left_button_text(dialog_ex, \"Finish\");\n}\n\nbool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, StorageSettingsStart);\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_formatting_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    notification_message(notification, &sequence_reset_green);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    dialog_ex_reset(dialog_ex);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c",
    "content": "#include \"../storage_settings.h\"\n#include <furi_hal_version.h>\n\nstatic void\n    storage_settings_scene_internal_info_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_internal_info_on_enter(void* context) {\n    StorageSettings* app = context;\n    uint64_t total_space;\n    uint64_t free_space;\n    FS_Error error =\n        storage_common_fs_info(app->fs_api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_internal_info_dialog_callback);\n\n    if(error != FSE_OK) {\n        dialog_ex_set_header(\n            dialog_ex, \"Internal Storage Error\", 64, 10, AlignCenter, AlignCenter);\n        dialog_ex_set_text(\n            dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);\n    } else {\n        furi_string_printf(\n            app->text_string,\n            \"Name: %s\\nType: Virtual\\nTotal: %lu KiB\\nFree: %lu KiB\",\n            furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : \"Unknown\",\n            (uint32_t)(total_space / 1024),\n            (uint32_t)(free_space / 1024));\n        dialog_ex_set_text(\n            dialog_ex, furi_string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop);\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_internal_info_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        }\n    }\n    return consumed;\n}\n\nvoid storage_settings_scene_internal_info_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n\n    furi_string_reset(app->text_string);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c",
    "content": "#include \"../storage_settings.h\"\n\nstatic void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_sd_info_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    SDInfo sd_info;\n    FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info);\n\n    scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status);\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback);\n\n    if(sd_status != FSE_OK) {\n        dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n        dialog_ex_set_header(dialog_ex, \"SD Card Not Mounted\", 64, 3, AlignCenter, AlignTop);\n        dialog_ex_set_text(\n            dialog_ex, \"Try to reinsert\\nor format SD\\ncard.\", 3, 19, AlignLeft, AlignTop);\n        dialog_ex_set_center_button_text(dialog_ex, \"Ok\");\n    } else {\n        furi_string_printf(\n            app->text_string,\n            \"Label: %s\\nType: %s\\n\",\n            sd_info.label,\n            sd_api_get_fs_type_text(sd_info.fs_type));\n\n        if(sd_info.kb_total < 1024) {\n            furi_string_cat_printf(app->text_string, \"Total: %lu KiB\\n\", sd_info.kb_total);\n        } else if(sd_info.kb_total < 1024 * 1024) {\n            furi_string_cat_printf(app->text_string, \"Total: %lu MiB\\n\", sd_info.kb_total / 1024);\n        } else {\n            furi_string_cat_printf(\n                app->text_string, \"Total: %lu GiB\\n\", sd_info.kb_total / (1024 * 1024));\n        }\n\n        if(sd_info.kb_free < 1024) {\n            furi_string_cat_printf(app->text_string, \"Free: %lu KiB\\n\", sd_info.kb_free);\n        } else if(sd_info.kb_free < 1024 * 1024) {\n            furi_string_cat_printf(app->text_string, \"Free: %lu MiB\\n\", sd_info.kb_free / 1024);\n        } else {\n            furi_string_cat_printf(\n                app->text_string, \"Free: %lu GiB\\n\", sd_info.kb_free / (1024 * 1024));\n        }\n\n        furi_string_cat_printf(\n            app->text_string,\n            \"%02X%s %s v%i.%i\\nSN:%04lX %02i/%i\",\n            sd_info.manufacturer_id,\n            sd_info.oem_id,\n            sd_info.product_name,\n            sd_info.product_revision_major,\n            sd_info.product_revision_minor,\n            sd_info.product_serial_number,\n            sd_info.manufacturing_month,\n            sd_info.manufacturing_year);\n\n        dialog_ex_set_text(\n            dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop);\n    }\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_sd_info_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    FS_Error sd_status = scene_manager_get_scene_state(app->scene_manager, StorageSettingsSDInfo);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultLeft:\n        case DialogExResultCenter:\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        case DialogExResultRight:\n            scene_manager_next_scene(app->scene_manager, StorageSettingsUnmounted);\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack && sd_status != FSE_OK) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_sd_info_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n\n    furi_string_reset(app->text_string);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_start.c",
    "content": "#include \"../storage_settings.h\"\n\nvoid storage_settings_scene_start_on_enter(void* context) {\n    StorageSettings* app = context;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n    app->helper_descriptor->options[STORAGE_SETTINGS_MOUNT_INDEX].name =\n        (sd_status != FSE_OK) ? \"Mount SD Card\" : \"Unmount SD Card\";\n    submenu_settings_helpers_scene_enter(app->settings_helper);\n}\n\nbool storage_settings_scene_start_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    return submenu_settings_helpers_scene_event(app->settings_helper, event);\n}\n\nvoid storage_settings_scene_start_on_exit(void* context) {\n    StorageSettings* app = context;\n    submenu_settings_helpers_scene_exit(app->settings_helper);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c",
    "content": "#include \"../storage_settings.h\"\n\nstatic void\n    storage_settings_scene_unmount_confirm_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_unmount_confirm_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n    if(sd_status == FSE_NOT_READY) {\n        dialog_ex_set_header(dialog_ex, \"Mount SD Card?\", 64, 10, AlignCenter, AlignCenter);\n        dialog_ex_set_text(\n            dialog_ex,\n            \"This may turn off power\\nfor external modules\",\n            64,\n            32,\n            AlignCenter,\n            AlignCenter);\n        dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n        dialog_ex_set_right_button_text(dialog_ex, \"Mount\");\n    } else {\n        dialog_ex_set_header(dialog_ex, \"Unmount SD Card?\", 64, 10, AlignCenter, AlignCenter);\n        dialog_ex_set_text(\n            dialog_ex, \"SD card will be\\nunavailable\", 64, 32, AlignCenter, AlignCenter);\n        dialog_ex_set_left_button_text(dialog_ex, \"Cancel\");\n        dialog_ex_set_right_button_text(dialog_ex, \"Unmount\");\n    }\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(\n        dialog_ex, storage_settings_scene_unmount_confirm_dialog_callback);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_unmount_confirm_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultCenter:\n        case DialogExResultLeft:\n            consumed = scene_manager_previous_scene(app->scene_manager);\n            break;\n        case DialogExResultRight:\n            scene_manager_next_scene(app->scene_manager, StorageSettingsUnmounted);\n            consumed = true;\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_unmount_confirm_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c",
    "content": "#include \"../storage_settings.h\"\n\nstatic void\n    storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) {\n    StorageSettings* app = context;\n\n    view_dispatcher_send_custom_event(app->view_dispatcher, result);\n}\n\nvoid storage_settings_scene_unmounted_on_enter(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    FS_Error sd_status = storage_sd_status(app->fs_api);\n    if(sd_status == FSE_NOT_READY) {\n        FS_Error error = storage_sd_mount(app->fs_api);\n        if(error == FSE_OK) {\n            dialog_ex_set_header(dialog_ex, \"SD Card Mounted\", 64, 3, AlignCenter, AlignTop);\n            dialog_ex_set_text(\n                dialog_ex, \"Flipper can use\\nSD card now.\", 3, 22, AlignLeft, AlignTop);\n            notification_message(app->notification, &sequence_blink_green_100);\n        } else {\n            dialog_ex_set_header(dialog_ex, \"Cannot Mount SD Card\", 64, 3, AlignCenter, AlignTop);\n            dialog_ex_set_text(\n                dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);\n            notification_message(app->notification, &sequence_blink_red_100);\n        }\n    } else {\n        FS_Error error = storage_sd_unmount(app->fs_api);\n        if(error == FSE_OK) {\n            dialog_ex_set_header(dialog_ex, \"SD Card Unmounted\", 64, 3, AlignCenter, AlignTop);\n            dialog_ex_set_text(\n                dialog_ex, \"You can remove\\nSD card now.\", 3, 22, AlignLeft, AlignTop);\n            notification_message(app->notification, &sequence_blink_green_100);\n        } else {\n            dialog_ex_set_header(\n                dialog_ex, \"Cannot Unmount SD Card\", 64, 3, AlignCenter, AlignTop);\n            dialog_ex_set_text(\n                dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);\n            notification_message(app->notification, &sequence_blink_red_100);\n        }\n    }\n\n    dialog_ex_set_center_button_text(dialog_ex, \"OK\");\n    dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);\n\n    dialog_ex_set_context(dialog_ex, app);\n    dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n}\n\nbool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent event) {\n    StorageSettings* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case DialogExResultCenter:\n            consumed = scene_manager_search_and_switch_to_previous_scene(\n                app->scene_manager, StorageSettingsStart);\n            break;\n        }\n    } else if(event.type == SceneManagerEventTypeBack) {\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid storage_settings_scene_unmounted_on_exit(void* context) {\n    StorageSettings* app = context;\n    DialogEx* dialog_ex = app->dialog_ex;\n\n    dialog_ex_reset(dialog_ex);\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/storage_settings.c",
    "content": "#include \"storage_settings.h\"\n\nconst SubmenuSettingsHelperDescriptor descriptor_template = {\n    .app_name = \"Storage\",\n    .options_cnt = 6,\n    .options =\n        {\n            {.name = \"About Internal Storage\", .scene_id = StorageSettingsInternalInfo},\n            {.name = \"About SD Card\", .scene_id = StorageSettingsSDInfo},\n            {.name = \"Unmount SD Card\", .scene_id = StorageSettingsUnmountConfirm},\n            {.name = \"Format SD Card\", .scene_id = StorageSettingsFormatConfirm},\n            {.name = \"Benchmark SD Card\", .scene_id = StorageSettingsBenchmarkConfirm},\n            {.name = \"Factory Reset\", .scene_id = StorageSettingsFactoryReset},\n        },\n};\n\nstatic bool storage_settings_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    StorageSettings* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nstatic bool storage_settings_back_event_callback(void* context) {\n    furi_assert(context);\n    StorageSettings* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nstatic StorageSettings* storage_settings_alloc(void) {\n    StorageSettings* app = malloc(sizeof(StorageSettings));\n\n    app->gui = furi_record_open(RECORD_GUI);\n    app->fs_api = furi_record_open(RECORD_STORAGE);\n    app->notification = furi_record_open(RECORD_NOTIFICATION);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->scene_manager = scene_manager_alloc(&storage_settings_scene_handlers, app);\n    app->text_string = furi_string_alloc();\n\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_set_custom_event_callback(\n        app->view_dispatcher, storage_settings_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        app->view_dispatcher, storage_settings_back_event_callback);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    app->submenu = submenu_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, StorageSettingsViewSubmenu, submenu_get_view(app->submenu));\n\n    app->dialog_ex = dialog_ex_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, StorageSettingsViewDialogEx, dialog_ex_get_view(app->dialog_ex));\n\n    size_t descriptor_size =\n        sizeof(SubmenuSettingsHelperDescriptor) +\n        (descriptor_template.options_cnt * sizeof(SubmenuSettingsHelperOption));\n    app->helper_descriptor = malloc(descriptor_size);\n    memcpy(app->helper_descriptor, &descriptor_template, descriptor_size);\n    app->settings_helper = submenu_settings_helpers_alloc(app->helper_descriptor);\n    submenu_settings_helpers_assign_objects(\n        app->settings_helper,\n        app->view_dispatcher,\n        app->scene_manager,\n        app->submenu,\n        StorageSettingsViewSubmenu,\n        StorageSettingsStart);\n\n    return app;\n}\n\nstatic void storage_settings_free(StorageSettings* app) {\n    submenu_settings_helpers_free(app->settings_helper);\n    free(app->helper_descriptor);\n\n    view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewSubmenu);\n    submenu_free(app->submenu);\n\n    view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewDialogEx);\n    dialog_ex_free(app->dialog_ex);\n\n    view_dispatcher_free(app->view_dispatcher);\n    scene_manager_free(app->scene_manager);\n\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_STORAGE);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    furi_string_free(app->text_string);\n\n    free(app);\n}\n\nint32_t storage_settings_app(void* p) {\n    UNUSED(p);\n    StorageSettings* app = storage_settings_alloc();\n\n    if(!submenu_settings_helpers_app_start(app->settings_helper, p)) {\n        scene_manager_next_scene(app->scene_manager, StorageSettingsStart);\n    }\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    storage_settings_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/storage_settings/storage_settings.h",
    "content": "#pragma once\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <assets_icons.h>\n#include <notification/notification_messages.h>\n\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/popup.h>\n\n#include <storage/storage.h>\n#include <storage/storage_sd_api.h>\n\n#include \"scenes/storage_settings_scene.h\"\n\n#include <settings_helpers/submenu_based.h>\n\n#define STORAGE_SETTINGS_MOUNT_INDEX 2\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    // records\n    Gui* gui;\n    NotificationApp* notification;\n    Storage* fs_api;\n\n    // view management\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n\n    // view modules\n    Submenu* submenu;\n    DialogEx* dialog_ex;\n\n    // text\n    FuriString* text_string;\n\n    // helpers\n    SubmenuSettingsHelperDescriptor* helper_descriptor;\n    SubmenuSettingsHelper* settings_helper;\n} StorageSettings;\n\ntypedef enum {\n    StorageSettingsViewSubmenu,\n    StorageSettingsViewDialogEx,\n} StorageSettingsView;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/settings/system/application.fam",
    "content": "App(\n    appid=\"system_settings\",\n    name=\"System\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"system_settings_app\",\n    requires=[\"gui\", \"locale\"],\n    stack_size=1 * 1024,\n    order=70,\n)\n"
  },
  {
    "path": "applications/settings/system/system_settings.c",
    "content": "#include \"system_settings.h\"\n#include <loader/loader.h>\n#include <lib/toolbox/value_index.h>\n#include <locale/locale.h>\n\nconst char* const log_level_text[] = {\n    \"Default\",\n    \"None\",\n    \"Error\",\n    \"Warning\",\n    \"Info\",\n    \"Debug\",\n    \"Trace\",\n};\n\nconst uint32_t log_level_value[] = {\n    FuriLogLevelDefault,\n    FuriLogLevelNone,\n    FuriLogLevelError,\n    FuriLogLevelWarn,\n    FuriLogLevelInfo,\n    FuriLogLevelDebug,\n    FuriLogLevelTrace,\n};\n\nstatic void log_level_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, log_level_text[index]);\n    furi_hal_rtc_set_log_level(log_level_value[index]);\n}\n\nconst char* const log_device_text[] = {\n    \"USART\",\n    \"LPUART\",\n    \"None\",\n};\n\nconst uint32_t log_device_value[] = {\n    FuriHalRtcLogDeviceUsart,\n    FuriHalRtcLogDeviceLpuart,\n    FuriHalRtcLogDeviceNone};\n\nstatic void log_device_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, log_device_text[index]);\n    furi_hal_rtc_set_log_device(log_device_value[index]);\n}\n\nconst char* const log_baud_rate_text[] = {\n    \"9600\",\n    \"38400\",\n    \"57600\",\n    \"115200\",\n    \"230400\",\n    \"460800\",\n    \"921600\",\n    \"1843200\",\n};\n\nconst uint32_t log_baud_rate_value[] = {\n    FuriHalRtcLogBaudRate9600,\n    FuriHalRtcLogBaudRate38400,\n    FuriHalRtcLogBaudRate57600,\n    FuriHalRtcLogBaudRate115200,\n    FuriHalRtcLogBaudRate230400,\n    FuriHalRtcLogBaudRate460800,\n    FuriHalRtcLogBaudRate921600,\n    FuriHalRtcLogBaudRate1843200,\n};\n\nstatic void log_baud_rate_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, log_baud_rate_text[index]);\n    furi_hal_rtc_set_log_baud_rate(log_baud_rate_value[index]);\n}\n\nconst char* const debug_text[] = {\n    \"OFF\",\n    \"ON\",\n};\n\nstatic void debug_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, debug_text[index]);\n    if(index) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagDebug);\n    } else {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);\n    }\n}\n\nconst char* const heap_trace_mode_text[] = {\n    \"None\",\n    \"Main\",\n#ifdef FURI_DEBUG\n    \"Tree\",\n    \"All\",\n#endif\n};\n\nconst uint32_t heap_trace_mode_value[] = {\n    FuriHalRtcHeapTrackModeNone,\n    FuriHalRtcHeapTrackModeMain,\n#ifdef FURI_DEBUG\n    FuriHalRtcHeapTrackModeTree,\n    FuriHalRtcHeapTrackModeAll,\n#endif\n};\n\nstatic void heap_trace_mode_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, heap_trace_mode_text[index]);\n    furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]);\n}\n\nconst char* const mesurement_units_text[] = {\n    \"Metric\",\n    \"Imperial\",\n};\n\nconst uint32_t mesurement_units_value[] = {\n    LocaleMeasurementUnitsMetric,\n    LocaleMeasurementUnitsImperial,\n};\n\nstatic void mesurement_units_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, mesurement_units_text[index]);\n    locale_set_measurement_unit(mesurement_units_value[index]);\n}\n\nconst char* const time_format_text[] = {\n    \"24h\",\n    \"12h\",\n};\n\nconst uint32_t time_format_value[] = {\n    LocaleTimeFormat24h,\n    LocaleTimeFormat12h,\n};\n\nstatic void time_format_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, time_format_text[index]);\n    locale_set_time_format(time_format_value[index]);\n}\n\nconst char* const date_format_text[] = {\n    \"D/M/Y\",\n    \"M/D/Y\",\n    \"Y/M/D\",\n};\n\nconst uint32_t date_format_value[] = {\n    LocaleDateFormatDMY,\n    LocaleDateFormatMDY,\n    LocaleDateFormatYMD,\n};\n\nstatic void date_format_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, date_format_text[index]);\n    locale_set_date_format(date_format_value[index]);\n}\n\nconst char* const hand_mode[] = {\n    \"Righty\",\n    \"Lefty\",\n};\n\nstatic void hand_orient_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, hand_mode[index]);\n    if(index) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient);\n    } else {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient);\n    }\n}\n\nconst char* const sleep_method[] = {\n    \"Default\",\n    \"Legacy\",\n};\n\nstatic void sleep_method_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, sleep_method[index]);\n    if(index) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep);\n    } else {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep);\n    }\n}\n\nconst char* const filename_scheme[] = {\n    \"Default\",\n    \"Detailed\",\n};\n\nstatic void filename_scheme_changed(VariableItem* item) {\n    uint8_t index = variable_item_get_current_value_index(item);\n    variable_item_set_current_value_text(item, filename_scheme[index]);\n    if(index) {\n        furi_hal_rtc_set_flag(FuriHalRtcFlagDetailedFilename);\n    } else {\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagDetailedFilename);\n    }\n}\n\nstatic uint32_t system_settings_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nSystemSettings* system_settings_alloc(void) {\n    SystemSettings* app = malloc(sizeof(SystemSettings));\n\n    // Load settings\n    app->gui = furi_record_open(RECORD_GUI);\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    VariableItem* item;\n    uint8_t value_index;\n    app->var_item_list = variable_item_list_alloc();\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Hand Orient\", COUNT_OF(hand_mode), hand_orient_changed, app);\n    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient) ? 1 : 0;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, hand_mode[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list,\n        \"Units\",\n        COUNT_OF(mesurement_units_text),\n        mesurement_units_changed,\n        app);\n    value_index = value_index_uint32(\n        locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, mesurement_units_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Time Format\", COUNT_OF(time_format_text), time_format_changed, app);\n    value_index = value_index_uint32(\n        locale_get_time_format(), time_format_value, COUNT_OF(time_format_value));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, time_format_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Date Format\", COUNT_OF(date_format_text), date_format_changed, app);\n    value_index = value_index_uint32(\n        locale_get_date_format(), date_format_value, COUNT_OF(date_format_value));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, date_format_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Log Level\", COUNT_OF(log_level_text), log_level_changed, app);\n    value_index = value_index_uint32(\n        furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, log_level_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Log Device\", COUNT_OF(log_device_text), log_device_changed, app);\n    value_index = value_index_uint32(\n        furi_hal_rtc_get_log_device(), log_device_value, COUNT_OF(log_device_text));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, log_device_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list,\n        \"Log Baud Rate\",\n        COUNT_OF(log_baud_rate_text),\n        log_baud_rate_changed,\n        app);\n    value_index = value_index_uint32(\n        furi_hal_rtc_get_log_baud_rate(), log_baud_rate_value, COUNT_OF(log_baud_rate_text));\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, log_baud_rate_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Debug\", COUNT_OF(debug_text), debug_changed, app);\n    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, debug_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list,\n        \"Heap Trace\",\n        COUNT_OF(heap_trace_mode_text),\n        heap_trace_mode_changed,\n        app);\n    value_index = value_index_uint32(\n        furi_hal_rtc_get_heap_track_mode(), heap_trace_mode_value, COUNT_OF(heap_trace_mode_text));\n    furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[value_index]);\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"Sleep Method\", COUNT_OF(sleep_method), sleep_method_changed, app);\n    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, sleep_method[value_index]);\n\n    item = variable_item_list_add(\n        app->var_item_list, \"File Naming\", COUNT_OF(filename_scheme), filename_scheme_changed, app);\n    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDetailedFilename) ? 1 : 0;\n    variable_item_set_current_value_index(item, value_index);\n    variable_item_set_current_value_text(item, filename_scheme[value_index]);\n\n    view_set_previous_callback(\n        variable_item_list_get_view(app->var_item_list), system_settings_exit);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        SystemSettingsViewVarItemList,\n        variable_item_list_get_view(app->var_item_list));\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, SystemSettingsViewVarItemList);\n\n    return app;\n}\n\nvoid system_settings_free(SystemSettings* app) {\n    furi_assert(app);\n    // Variable item list\n    view_dispatcher_remove_view(app->view_dispatcher, SystemSettingsViewVarItemList);\n    variable_item_list_free(app->var_item_list);\n    // View dispatcher\n    view_dispatcher_free(app->view_dispatcher);\n    // Records\n    furi_record_close(RECORD_GUI);\n    free(app);\n}\n\nint32_t system_settings_app(void* p) {\n    UNUSED(p);\n    SystemSettings* app = system_settings_alloc();\n    view_dispatcher_run(app->view_dispatcher);\n    system_settings_free(app);\n    return 0;\n}\n"
  },
  {
    "path": "applications/settings/system/system_settings.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/variable_item_list.h>\n\ntypedef struct {\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    VariableItemList* var_item_list;\n} SystemSettings;\n\ntypedef enum {\n    SystemSettingsViewVarItemList,\n} SystemSettingsView;\n"
  },
  {
    "path": "applications/system/application.fam",
    "content": "App(\n    appid=\"system_apps\",\n    name=\"Applications not shown in menus\",\n    apptype=FlipperAppType.METAPACKAGE,\n    provides=[\n        \"updater_app\",\n        \"js_app\",\n        # \"archive\",\n    ],\n)\n"
  },
  {
    "path": "applications/system/hid_app/application.fam",
    "content": "App(\n    appid=\"hid_usb\",\n    name=\"Remote\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"hid_usb_app\",\n    stack_size=2 * 1024,\n    sources=[\"*.c\", \"!transport_ble.c\"],\n    cdefines=[\"HID_TRANSPORT_USB\"],\n    fap_description=\"Use Flipper as a HID remote control over USB\",\n    fap_version=\"1.1\",\n    fap_category=\"USB\",\n    fap_icon=\"hid_usb_10px.png\",\n    fap_icon_assets=\"assets\",\n    fap_icon_assets_symbol=\"hid\",\n)\n\n\nApp(\n    appid=\"hid_ble\",\n    name=\"Remote\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"hid_ble_app\",\n    stack_size=2 * 1024,\n    sources=[\"*.c\", \"!transport_usb.c\"],\n    cdefines=[\"HID_TRANSPORT_BLE\"],\n    fap_libs=[\"ble_profile\"],\n    fap_description=\"Use Flipper as a HID remote control over Bluetooth\",\n    fap_version=\"1.1\",\n    fap_category=\"Bluetooth\",\n    fap_icon=\"hid_ble_10px.png\",\n    fap_icon_assets=\"assets\",\n    fap_icon_assets_symbol=\"hid\",\n)\n"
  },
  {
    "path": "applications/system/hid_app/hid.c",
    "content": "#include \"hid.h\"\n#include <extra_profiles/hid_profile.h>\n#include <profiles/serial_profile.h>\n#include \"views.h\"\n#include <notification/notification_messages.h>\n#include <dolphin/dolphin.h>\n\n#define TAG \"HidApp\"\n\nbool hid_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    Hid* app = context;\n    return scene_manager_handle_custom_event(app->scene_manager, event);\n}\n\nbool hid_back_event_callback(void* context) {\n    furi_assert(context);\n    Hid* app = context;\n    return scene_manager_handle_back_event(app->scene_manager);\n}\n\nvoid bt_hid_remove_pairing(Hid* app) {\n    Bt* bt = app->bt;\n    bt_disconnect(bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n\n    furi_hal_bt_stop_advertising();\n\n    bt_forget_bonded_devices(bt);\n\n    furi_hal_bt_start_advertising();\n}\n\nstatic void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {\n    furi_assert(context);\n    Hid* hid = context;\n    const bool connected = (status == BtStatusConnected);\n    notification_internal_message(\n        hid->notifications, connected ? &sequence_set_blue_255 : &sequence_reset_blue);\n    hid_keynote_set_connected_status(hid->hid_keynote, connected);\n    hid_keyboard_set_connected_status(hid->hid_keyboard, connected);\n    hid_media_set_connected_status(hid->hid_media, connected);\n    hid_mouse_set_connected_status(hid->hid_mouse, connected);\n    hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);\n    hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);\n    hid_tiktok_set_connected_status(hid->hid_tiktok, connected);\n}\n\nHid* hid_alloc() {\n    Hid* app = malloc(sizeof(Hid));\n\n    // Gui\n    app->gui = furi_record_open(RECORD_GUI);\n\n    // Bt\n    app->bt = furi_record_open(RECORD_BT);\n\n    // Notifications\n    app->notifications = furi_record_open(RECORD_NOTIFICATION);\n\n    // View dispatcher\n    app->view_dispatcher = view_dispatcher_alloc();\n    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);\n    view_dispatcher_set_custom_event_callback(app->view_dispatcher, hid_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(app->view_dispatcher, hid_back_event_callback);\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n\n    // Scene Manager\n    app->scene_manager = scene_manager_alloc(&hid_scene_handlers, app);\n\n    // Device Type Submenu view\n    app->submenu = submenu_alloc();\n\n    view_dispatcher_add_view(app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->submenu));\n\n    // Dialog view\n    app->dialog = dialog_ex_alloc();\n    view_dispatcher_add_view(app->view_dispatcher, HidViewDialog, dialog_ex_get_view(app->dialog));\n\n    // Popup view\n    app->popup = popup_alloc();\n    view_dispatcher_add_view(app->view_dispatcher, HidViewPopup, popup_get_view(app->popup));\n\n    // Keynote view\n    app->hid_keynote = hid_keynote_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));\n\n    // Keyboard view\n    app->hid_keyboard = hid_keyboard_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));\n\n    // Media view\n    app->hid_media = hid_media_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media));\n\n    // TikTok view\n    app->hid_tiktok = hid_tiktok_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok));\n\n    // Mouse view\n    app->hid_mouse = hid_mouse_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));\n\n    // Mouse clicker view\n    app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        HidViewMouseClicker,\n        hid_mouse_clicker_get_view(app->hid_mouse_clicker));\n\n    // Mouse jiggler view\n    app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);\n    view_dispatcher_add_view(\n        app->view_dispatcher,\n        HidViewMouseJiggler,\n        hid_mouse_jiggler_get_view(app->hid_mouse_jiggler));\n\n    return app;\n}\n\nvoid hid_free(Hid* app) {\n    furi_assert(app);\n\n    // Reset notification\n#ifdef HID_TRANSPORT_BLE\n    notification_internal_message(app->notifications, &sequence_reset_blue);\n#endif\n    // Free views\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);\n    submenu_free(app->submenu);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewDialog);\n    dialog_ex_free(app->dialog);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewPopup);\n    popup_free(app->popup);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);\n    hid_keynote_free(app->hid_keynote);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);\n    hid_keyboard_free(app->hid_keyboard);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);\n    hid_media_free(app->hid_media);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);\n    hid_mouse_free(app->hid_mouse);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);\n    hid_mouse_clicker_free(app->hid_mouse_clicker);\n    view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);\n    hid_mouse_jiggler_free(app->hid_mouse_jiggler);\n    view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);\n    hid_tiktok_free(app->hid_tiktok);\n    scene_manager_free(app->scene_manager);\n    view_dispatcher_free(app->view_dispatcher);\n\n    // Close records\n    furi_record_close(RECORD_GUI);\n    app->gui = NULL;\n    furi_record_close(RECORD_NOTIFICATION);\n    app->notifications = NULL;\n    furi_record_close(RECORD_BT);\n    app->bt = NULL;\n\n    // Free rest\n    free(app);\n}\n\nint32_t hid_usb_app(void* p) {\n    UNUSED(p);\n    Hid* app = hid_alloc();\n\n    FURI_LOG_D(\"HID\", \"Starting as USB app\");\n\n    FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();\n    furi_hal_usb_unlock();\n    furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);\n\n    dolphin_deed(DolphinDeedPluginStart);\n\n    scene_manager_next_scene(app->scene_manager, HidSceneStart);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    furi_hal_usb_set_config(usb_mode_prev, NULL);\n\n    hid_free(app);\n\n    return 0;\n}\n\nint32_t hid_ble_app(void* p) {\n    UNUSED(p);\n    Hid* app = hid_alloc();\n\n    FURI_LOG_D(\"HID\", \"Starting as BLE app\");\n\n    bt_disconnect(app->bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n\n    // Migrate data from old sd-card folder\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    storage_common_migrate(\n        storage,\n        EXT_PATH(\"apps/Tools/\" HID_BT_KEYS_STORAGE_NAME),\n        APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));\n\n    bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));\n\n    furi_record_close(RECORD_STORAGE);\n\n    app->ble_hid_profile = bt_profile_start(app->bt, ble_profile_hid, NULL);\n\n    furi_check(app->ble_hid_profile);\n\n    furi_hal_bt_start_advertising();\n    bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);\n\n    dolphin_deed(DolphinDeedPluginStart);\n\n    scene_manager_next_scene(app->scene_manager, HidSceneStart);\n\n    view_dispatcher_run(app->view_dispatcher);\n\n    bt_set_status_changed_callback(app->bt, NULL, NULL);\n\n    bt_disconnect(app->bt);\n\n    // Wait 2nd core to update nvm storage\n    furi_delay_ms(200);\n\n    bt_keys_storage_set_default_path(app->bt);\n\n    furi_check(bt_profile_restore_default(app->bt));\n\n    hid_free(app);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/system/hid_app/hid.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <furi_hal_bt.h>\n#include <furi_hal_usb.h>\n#include <furi_hal_usb_hid.h>\n\n#include <extra_profiles/hid_profile.h>\n\n#include <bt/bt_service/bt.h>\n#include <gui/gui.h>\n#include <gui/view.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n#include <notification/notification.h>\n#include <storage/storage.h>\n\n#include <gui/modules/submenu.h>\n#include <gui/modules/dialog_ex.h>\n#include <gui/modules/popup.h>\n#include \"views/hid_keynote.h\"\n#include \"views/hid_keyboard.h\"\n#include \"views/hid_media.h\"\n#include \"views/hid_mouse.h\"\n#include \"views/hid_mouse_clicker.h\"\n#include \"views/hid_mouse_jiggler.h\"\n#include \"views/hid_tiktok.h\"\n\n#include \"scenes/hid_scene.h\"\n\n#define HID_BT_KEYS_STORAGE_NAME \".bt_hid.keys\"\n\ntypedef struct Hid Hid;\n\nstruct Hid {\n    FuriHalBleProfileBase* ble_hid_profile;\n    Bt* bt;\n    Gui* gui;\n    NotificationApp* notifications;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    Submenu* submenu;\n    DialogEx* dialog;\n    Popup* popup;\n    HidKeynote* hid_keynote;\n    HidKeyboard* hid_keyboard;\n    HidMedia* hid_media;\n    HidMouse* hid_mouse;\n    HidMouseClicker* hid_mouse_clicker;\n    HidMouseJiggler* hid_mouse_jiggler;\n    HidTikTok* hid_tiktok;\n};\n\nvoid bt_hid_remove_pairing(Hid* app);\n\nvoid hid_hal_keyboard_press(Hid* instance, uint16_t event);\nvoid hid_hal_keyboard_release(Hid* instance, uint16_t event);\nvoid hid_hal_keyboard_release_all(Hid* instance);\n\nvoid hid_hal_consumer_key_press(Hid* instance, uint16_t event);\nvoid hid_hal_consumer_key_release(Hid* instance, uint16_t event);\nvoid hid_hal_consumer_key_release_all(Hid* instance);\n\nvoid hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy);\nvoid hid_hal_mouse_scroll(Hid* instance, int8_t delta);\nvoid hid_hal_mouse_press(Hid* instance, uint16_t event);\nvoid hid_hal_mouse_release(Hid* instance, uint16_t event);\nvoid hid_hal_mouse_release_all(Hid* instance);\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene.c",
    "content": "#include \"hid_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const hid_on_enter_handlers[])(void*) = {\n#include \"hid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const hid_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"hid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const hid_on_exit_handlers[])(void* context) = {\n#include \"hid_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers hid_scene_handlers = {\n    .on_enter_handlers = hid_on_enter_handlers,\n    .on_event_handlers = hid_on_event_handlers,\n    .on_exit_handlers = hid_on_exit_handlers,\n    .scene_num = HidSceneNum,\n};\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) HidScene##id,\ntypedef enum {\n#include \"hid_scene_config.h\"\n    HidSceneNum,\n} HidScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers hid_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"hid_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"hid_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"hid_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene_config.h",
    "content": "ADD_SCENE(hid, start, Start)\nADD_SCENE(hid, main, Main)\nADD_SCENE(hid, unpair, Unpair)\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene_main.c",
    "content": "#include \"../hid.h\"\n#include \"../views.h\"\n\nvoid hid_scene_main_on_enter(void* context) {\n    Hid* app = context;\n    view_dispatcher_switch_to_view(\n        app->view_dispatcher, scene_manager_get_scene_state(app->scene_manager, HidSceneMain));\n}\n\nbool hid_scene_main_on_event(void* context, SceneManagerEvent event) {\n    Hid* app = context;\n    bool consumed = false;\n    UNUSED(app);\n    UNUSED(event);\n\n    return consumed;\n}\n\nvoid hid_scene_main_on_exit(void* context) {\n    Hid* app = context;\n    UNUSED(app);\n}\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene_start.c",
    "content": "#include \"../hid.h\"\n#include \"../views.h\"\n\nenum HidSubmenuIndex {\n    HidSubmenuIndexKeynote,\n    HidSubmenuIndexKeynoteVertical,\n    HidSubmenuIndexKeyboard,\n    HidSubmenuIndexMedia,\n    HidSubmenuIndexTikTok,\n    HidSubmenuIndexMouse,\n    HidSubmenuIndexMouseClicker,\n    HidSubmenuIndexMouseJiggler,\n    HidSubmenuIndexRemovePairing,\n};\n\nstatic void hid_scene_start_submenu_callback(void* context, uint32_t index) {\n    furi_assert(context);\n    Hid* app = context;\n    view_dispatcher_send_custom_event(app->view_dispatcher, index);\n}\n\nvoid hid_scene_start_on_enter(void* context) {\n    Hid* app = context;\n    submenu_add_item(\n        app->submenu, \"Keynote\", HidSubmenuIndexKeynote, hid_scene_start_submenu_callback, app);\n    submenu_add_item(\n        app->submenu,\n        \"Keynote Vertical\",\n        HidSubmenuIndexKeynoteVertical,\n        hid_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu, \"Keyboard\", HidSubmenuIndexKeyboard, hid_scene_start_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Media\", HidSubmenuIndexMedia, hid_scene_start_submenu_callback, app);\n    submenu_add_item(\n        app->submenu, \"Mouse\", HidSubmenuIndexMouse, hid_scene_start_submenu_callback, app);\n#ifdef HID_TRANSPORT_BLE\n    submenu_add_item(\n        app->submenu,\n        \"TikTok Controller\",\n        HidSubmenuIndexTikTok,\n        hid_scene_start_submenu_callback,\n        app);\n#endif\n    submenu_add_item(\n        app->submenu,\n        \"Mouse Clicker\",\n        HidSubmenuIndexMouseClicker,\n        hid_scene_start_submenu_callback,\n        app);\n    submenu_add_item(\n        app->submenu,\n        \"Mouse Jiggler\",\n        HidSubmenuIndexMouseJiggler,\n        hid_scene_start_submenu_callback,\n        app);\n#ifdef HID_TRANSPORT_BLE\n    submenu_add_item(\n        app->submenu,\n        \"Bluetooth Unpairing\",\n        HidSubmenuIndexRemovePairing,\n        hid_scene_start_submenu_callback,\n        app);\n#endif\n\n    submenu_set_selected_item(\n        app->submenu, scene_manager_get_scene_state(app->scene_manager, HidSceneStart));\n    view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);\n}\n\nbool hid_scene_start_on_event(void* context, SceneManagerEvent event) {\n    Hid* app = context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        if(event.event == HidSubmenuIndexRemovePairing) {\n            scene_manager_next_scene(app->scene_manager, HidSceneUnpair);\n        } else {\n            HidView view_id;\n\n            switch(event.event) {\n            case HidSubmenuIndexKeynote:\n                view_id = HidViewKeynote;\n                hid_keynote_set_orientation(app->hid_keynote, false);\n                break;\n            case HidSubmenuIndexKeynoteVertical:\n                view_id = HidViewKeynote;\n                hid_keynote_set_orientation(app->hid_keynote, true);\n                break;\n            case HidSubmenuIndexKeyboard:\n                view_id = HidViewKeyboard;\n                break;\n            case HidSubmenuIndexMedia:\n                view_id = HidViewMedia;\n                break;\n            case HidSubmenuIndexTikTok:\n                view_id = BtHidViewTikTok;\n                break;\n            case HidSubmenuIndexMouse:\n                view_id = HidViewMouse;\n                break;\n            case HidSubmenuIndexMouseClicker:\n                view_id = HidViewMouseClicker;\n                break;\n            case HidSubmenuIndexMouseJiggler:\n                view_id = HidViewMouseJiggler;\n                break;\n            default:\n                furi_crash();\n            }\n\n            scene_manager_set_scene_state(app->scene_manager, HidSceneMain, view_id);\n            scene_manager_next_scene(app->scene_manager, HidSceneMain);\n        }\n\n        scene_manager_set_scene_state(app->scene_manager, HidSceneStart, event.event);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nvoid hid_scene_start_on_exit(void* context) {\n    Hid* app = context;\n    submenu_reset(app->submenu);\n}\n"
  },
  {
    "path": "applications/system/hid_app/scenes/hid_scene_unpair.c",
    "content": "#include \"../hid.h\"\n#include \"../views.h\"\n#include \"hid_icons.h\"\n\nstatic void hid_scene_unpair_dialog_callback(DialogExResult result, void* context) {\n    Hid* app = context;\n\n    if(result == DialogExResultRight) {\n        // Unpair all devices\n        bt_hid_remove_pairing(app);\n\n        // Show popup\n        view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPopup);\n    } else if(result == DialogExResultLeft) {\n        scene_manager_previous_scene(app->scene_manager);\n    }\n}\n\nvoid hid_scene_unpair_popup_callback(void* context) {\n    Hid* app = context;\n\n    scene_manager_previous_scene(app->scene_manager);\n}\n\nvoid hid_scene_unpair_on_enter(void* context) {\n    Hid* app = context;\n\n    // Un-pair dialog view\n    dialog_ex_reset(app->dialog);\n    dialog_ex_set_result_callback(app->dialog, hid_scene_unpair_dialog_callback);\n    dialog_ex_set_context(app->dialog, app);\n    dialog_ex_set_header(app->dialog, \"Unpair the Device?\", 64, 3, AlignCenter, AlignTop);\n    dialog_ex_set_left_button_text(app->dialog, \"Back\");\n    dialog_ex_set_right_button_text(app->dialog, \"Unpair\");\n\n    // Un-pair success popup view\n    popup_set_icon(app->popup, 48, 6, &I_DolphinDone_80x58);\n    popup_set_header(app->popup, \"Done\", 14, 15, AlignLeft, AlignTop);\n    popup_set_timeout(app->popup, 1500);\n    popup_set_context(app->popup, app);\n    popup_set_callback(app->popup, hid_scene_unpair_popup_callback);\n    popup_enable_timeout(app->popup);\n\n    view_dispatcher_switch_to_view(app->view_dispatcher, HidViewDialog);\n}\n\nbool hid_scene_unpair_on_event(void* context, SceneManagerEvent event) {\n    Hid* app = context;\n    bool consumed = false;\n    UNUSED(app);\n    UNUSED(event);\n\n    return consumed;\n}\n\nvoid hid_scene_unpair_on_exit(void* context) {\n    Hid* app = context;\n\n    dialog_ex_reset(app->dialog);\n    popup_reset(app->popup);\n}\n"
  },
  {
    "path": "applications/system/hid_app/transport_ble.c",
    "content": "#include \"hid.h\"\n\n#ifndef HID_TRANSPORT_BLE\n#error \"HID_TRANSPORT_BLE must be defined\"\n#endif\n\nvoid hid_hal_keyboard_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_kb_press(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_keyboard_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_kb_release(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_keyboard_release_all(Hid* instance) {\n    furi_assert(instance);\n    ble_profile_hid_kb_release_all(instance->ble_hid_profile);\n}\n\nvoid hid_hal_consumer_key_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_consumer_key_press(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_consumer_key_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_consumer_key_release(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_consumer_key_release_all(Hid* instance) {\n    furi_assert(instance);\n    ble_profile_hid_consumer_key_release_all(instance->ble_hid_profile);\n}\n\nvoid hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) {\n    furi_assert(instance);\n    ble_profile_hid_mouse_move(instance->ble_hid_profile, dx, dy);\n}\n\nvoid hid_hal_mouse_scroll(Hid* instance, int8_t delta) {\n    furi_assert(instance);\n    ble_profile_hid_mouse_scroll(instance->ble_hid_profile, delta);\n}\n\nvoid hid_hal_mouse_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_mouse_press(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_mouse_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    ble_profile_hid_mouse_release(instance->ble_hid_profile, event);\n}\n\nvoid hid_hal_mouse_release_all(Hid* instance) {\n    furi_assert(instance);\n    ble_profile_hid_mouse_release_all(instance->ble_hid_profile);\n}\n"
  },
  {
    "path": "applications/system/hid_app/transport_usb.c",
    "content": "#include \"hid.h\"\n\n#ifndef HID_TRANSPORT_USB\n#error \"HID_TRANSPORT_USB must be defined\"\n#endif\n\nvoid hid_hal_keyboard_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_kb_press(event);\n}\n\nvoid hid_hal_keyboard_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_kb_release(event);\n}\n\nvoid hid_hal_keyboard_release_all(Hid* instance) {\n    furi_assert(instance);\n    furi_hal_hid_kb_release_all();\n}\n\nvoid hid_hal_consumer_key_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_consumer_key_press(event);\n}\n\nvoid hid_hal_consumer_key_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_consumer_key_release(event);\n}\n\nvoid hid_hal_consumer_key_release_all(Hid* instance) {\n    furi_assert(instance);\n    furi_hal_hid_kb_release_all();\n}\n\nvoid hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) {\n    furi_assert(instance);\n    furi_hal_hid_mouse_move(dx, dy);\n}\n\nvoid hid_hal_mouse_scroll(Hid* instance, int8_t delta) {\n    furi_assert(instance);\n    furi_hal_hid_mouse_scroll(delta);\n}\n\nvoid hid_hal_mouse_press(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_mouse_press(event);\n}\n\nvoid hid_hal_mouse_release(Hid* instance, uint16_t event) {\n    furi_assert(instance);\n    furi_hal_hid_mouse_release(event);\n}\n\nvoid hid_hal_mouse_release_all(Hid* instance) {\n    furi_assert(instance);\n    furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);\n    furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_keyboard.c",
    "content": "#include \"hid_keyboard.h\"\n#include <furi.h>\n#include <gui/elements.h>\n#include <gui/icon_i.h>\n#include \"../hid.h\"\n#include \"hid_icons.h\"\n\n#define TAG \"HidKeyboard\"\n\nstruct HidKeyboard {\n    View* view;\n    Hid* hid;\n};\n\ntypedef struct {\n    bool shift;\n    bool alt;\n    bool ctrl;\n    bool gui;\n    uint8_t x;\n    uint8_t y;\n    uint8_t last_key_code;\n    bool ok_pressed;\n    bool back_pressed;\n    bool connected;\n} HidKeyboardModel;\n\ntypedef struct {\n    uint8_t width;\n    char key;\n    char shift_key;\n    const Icon* icon;\n    const Icon* icon_shift;\n    const Icon* icon_toggled;\n    uint8_t value;\n} HidKeyboardKey;\n\ntypedef struct {\n    int8_t x;\n    int8_t y;\n} HidKeyboardPoint;\n// 4 BY 12\n#define MARGIN_TOP   0\n#define MARGIN_LEFT  3\n#define KEY_WIDTH    11\n#define KEY_HEIGHT   13\n#define KEY_PADDING  -1\n#define ROW_COUNT    7\n#define COLUMN_COUNT 12\n\n// 0 width items are not drawn, but their value is used\nconst HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {\n    {\n        {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1},\n        {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2},\n        {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3},\n        {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4},\n        {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5},\n        {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6},\n        {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7},\n        {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8},\n        {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9},\n        {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10},\n        {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11},\n        {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12},\n    },\n    {\n        {.width = 1, .icon = NULL, .key = '1', .shift_key = '!', .value = HID_KEYBOARD_1},\n        {.width = 1, .icon = NULL, .key = '2', .shift_key = '@', .value = HID_KEYBOARD_2},\n        {.width = 1,\n         .icon = NULL,\n         .icon_shift = &I_hash_button_9x11,\n         .key = '3',\n         .value = HID_KEYBOARD_3},\n        {.width = 1, .icon = NULL, .key = '4', .shift_key = '$', .value = HID_KEYBOARD_4},\n        {.width = 1,\n         .icon = NULL,\n         .icon_shift = &I_percent_button_9x11,\n         .key = '5',\n         .value = HID_KEYBOARD_5},\n        {.width = 1, .icon = NULL, .key = '6', .shift_key = '^', .value = HID_KEYBOARD_6},\n        {.width = 1, .icon = NULL, .key = '7', .shift_key = '&', .value = HID_KEYBOARD_7},\n        {.width = 1, .icon = NULL, .key = '8', .shift_key = '*', .value = HID_KEYBOARD_8},\n        {.width = 1, .icon = NULL, .key = '9', .shift_key = '(', .value = HID_KEYBOARD_9},\n        {.width = 1, .icon = NULL, .key = '0', .shift_key = ')', .value = HID_KEYBOARD_0},\n        {.width = 2, .icon = &I_backspace_19x11, .value = HID_KEYBOARD_DELETE},\n        {.width = 0, .value = HID_KEYBOARD_DELETE},\n    },\n    {\n        {.width = 1, .icon = NULL, .key = 'q', .shift_key = 'Q', .value = HID_KEYBOARD_Q},\n        {.width = 1, .icon = NULL, .key = 'w', .shift_key = 'W', .value = HID_KEYBOARD_W},\n        {.width = 1, .icon = NULL, .key = 'e', .shift_key = 'E', .value = HID_KEYBOARD_E},\n        {.width = 1, .icon = NULL, .key = 'r', .shift_key = 'R', .value = HID_KEYBOARD_R},\n        {.width = 1, .icon = NULL, .key = 't', .shift_key = 'T', .value = HID_KEYBOARD_T},\n        {.width = 1, .icon = NULL, .key = 'y', .shift_key = 'Y', .value = HID_KEYBOARD_Y},\n        {.width = 1, .icon = NULL, .key = 'u', .shift_key = 'U', .value = HID_KEYBOARD_U},\n        {.width = 1, .icon = NULL, .key = 'i', .shift_key = 'I', .value = HID_KEYBOARD_I},\n        {.width = 1, .icon = NULL, .key = 'o', .shift_key = 'O', .value = HID_KEYBOARD_O},\n        {.width = 1, .icon = NULL, .key = 'p', .shift_key = 'P', .value = HID_KEYBOARD_P},\n        {.width = 1,\n         .icon = &I_sq_bracket_left_button_9x11,\n         .icon_shift = &I_brace_left_button_9x11,\n         .value = HID_KEYBOARD_OPEN_BRACKET},\n        {.width = 1,\n         .icon = &I_sq_bracket_right_button_9x11,\n         .icon_shift = &I_brace_right_button_9x11,\n         .value = HID_KEYBOARD_CLOSE_BRACKET},\n    },\n    {\n        {.width = 1, .icon = NULL, .key = 'a', .shift_key = 'A', .value = HID_KEYBOARD_A},\n        {.width = 1, .icon = NULL, .key = 's', .shift_key = 'S', .value = HID_KEYBOARD_S},\n        {.width = 1, .icon = NULL, .key = 'd', .shift_key = 'D', .value = HID_KEYBOARD_D},\n        {.width = 1, .icon = NULL, .key = 'f', .shift_key = 'F', .value = HID_KEYBOARD_F},\n        {.width = 1, .icon = NULL, .key = 'g', .shift_key = 'G', .value = HID_KEYBOARD_G},\n        {.width = 1, .icon = NULL, .key = 'h', .shift_key = 'H', .value = HID_KEYBOARD_H},\n        {.width = 1, .icon = NULL, .key = 'j', .shift_key = 'J', .value = HID_KEYBOARD_J},\n        {.width = 1, .icon = NULL, .key = 'k', .shift_key = 'K', .value = HID_KEYBOARD_K},\n        {.width = 1, .icon = NULL, .key = 'l', .shift_key = 'L', .value = HID_KEYBOARD_L},\n        {.width = 1, .icon = NULL, .key = ';', .shift_key = ':', .value = HID_KEYBOARD_SEMICOLON},\n        {.width = 2, .icon = &I_Return_10x7, .value = HID_KEYBOARD_RETURN},\n        {.width = 0, .value = HID_KEYBOARD_RETURN},\n    },\n    {\n        {.width = 1, .icon = NULL, .key = 'z', .shift_key = 'Z', .value = HID_KEYBOARD_Z},\n        {.width = 1, .icon = NULL, .key = 'x', .shift_key = 'X', .value = HID_KEYBOARD_X},\n        {.width = 1, .icon = NULL, .key = 'c', .shift_key = 'C', .value = HID_KEYBOARD_C},\n        {.width = 1, .icon = NULL, .key = 'v', .shift_key = 'V', .value = HID_KEYBOARD_V},\n        {.width = 1, .icon = NULL, .key = 'b', .shift_key = 'B', .value = HID_KEYBOARD_B},\n        {.width = 1, .icon = NULL, .key = 'n', .shift_key = 'N', .value = HID_KEYBOARD_N},\n        {.width = 1, .icon = NULL, .key = 'm', .shift_key = 'M', .value = HID_KEYBOARD_M},\n        {.width = 1, .icon = &I_slash_button_9x11, .shift_key = '?', .value = HID_KEYBOARD_SLASH},\n        {.width = 1,\n         .icon = &I_backslash_button_9x11,\n         .shift_key = '|',\n         .value = HID_KEYBOARD_BACKSLASH},\n        {.width = 1,\n         .icon = &I_backtick_button_9x11,\n         .shift_key = '~',\n         .value = HID_KEYBOARD_GRAVE_ACCENT},\n        {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW},\n        {.width = 1,\n         .icon = NULL,\n         .icon_shift = &I_underscore_button_9x11,\n         .key = '-',\n         .value = HID_KEYBOARD_MINUS},\n    },\n    {\n        {.width = 1,\n         .icon = &I_Shift_inactive_7x9,\n         .icon_toggled = &I_Shift_active_7x9,\n         .value = HID_KEYBOARD_L_SHIFT},\n        {.width = 1, .icon = NULL, .key = ',', .shift_key = '<', .value = HID_KEYBOARD_COMMA},\n        {.width = 1, .icon = NULL, .key = '.', .shift_key = '>', .value = HID_KEYBOARD_DOT},\n        {.width = 4, .icon = NULL, .key = ' ', .value = HID_KEYBOARD_SPACEBAR},\n        {.width = 0, .value = HID_KEYBOARD_SPACEBAR},\n        {.width = 0, .value = HID_KEYBOARD_SPACEBAR},\n        {.width = 0, .value = HID_KEYBOARD_SPACEBAR},\n        {.width = 1,\n         .icon = &I_apostrophe_button_9x11,\n         .icon_shift = &I_quote_button_9x11,\n         .value = HID_KEYBOARD_APOSTROPHE},\n        {.width = 1,\n         .icon = &I_equals_button_9x11,\n         .shift_key = '+',\n         .value = HID_KEYBOARD_EQUAL_SIGN},\n        {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW},\n        {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW},\n        {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW},\n    },\n    {\n        {.width = 2,\n         .icon = &I_Ctrl_17x10,\n         .icon_toggled = &I_Ctrl_active_17x9,\n         .value = HID_KEYBOARD_L_CTRL},\n        {.width = 0, .value = HID_KEYBOARD_L_CTRL},\n        {.width = 2,\n         .icon = &I_Alt_17x10,\n         .icon_toggled = &I_Alt_active_17x9,\n         .value = HID_KEYBOARD_L_ALT},\n        {.width = 0, .value = HID_KEYBOARD_L_ALT},\n        {.width = 2,\n         .icon = &I_Cmd_17x10,\n         .icon_toggled = &I_Cmd_active_17x9,\n         .value = HID_KEYBOARD_L_GUI},\n        {.width = 0, .value = HID_KEYBOARD_L_GUI},\n        {.width = 2, .icon = &I_Tab_17x10, .value = HID_KEYBOARD_TAB},\n        {.width = 0, .value = HID_KEYBOARD_TAB},\n        {.width = 2, .icon = &I_Esc_17x10, .value = HID_KEYBOARD_ESCAPE},\n        {.width = 0, .value = HID_KEYBOARD_ESCAPE},\n        {.width = 2, .icon = &I_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD},\n        {.width = 0, .value = HID_KEYBOARD_DELETE_FORWARD},\n    },\n};\n\nstatic void hid_keyboard_draw_key(\n    Canvas* canvas,\n    HidKeyboardModel* model,\n    uint8_t x,\n    uint8_t y,\n    HidKeyboardKey key,\n    bool selected) {\n    if(!key.width) return;\n\n    canvas_set_color(canvas, ColorBlack);\n    uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1);\n    if(selected) {\n        // Draw a filled box\n        elements_slightly_rounded_box(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),\n            keyWidth,\n            KEY_HEIGHT);\n        canvas_set_color(canvas, ColorWhite);\n    } else {\n        // Draw a framed box\n        elements_slightly_rounded_frame(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),\n            keyWidth,\n            KEY_HEIGHT);\n    }\n\n    if(model->shift && key.icon_shift != NULL) {\n        // Icon and shift\n        const Icon* key_icon = key.icon_shift;\n\n        if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||\n           (model->alt && key.value == HID_KEYBOARD_L_ALT) ||\n           (key.value == HID_KEYBOARD_L_SHIFT) ||\n           (model->gui && key.value == HID_KEYBOARD_L_GUI)) {\n            if(key.icon_toggled) {\n                key_icon = key.icon_toggled;\n            }\n        }\n        // Draw the icon centered on the button\n        canvas_draw_icon(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key_icon->width / 2,\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key_icon->height / 2,\n            key_icon);\n\n        return;\n    }\n    if(model->shift && key.shift_key != 0) {\n        // Text and shift\n        char key_str[2] = {key.shift_key, '\\0'};\n\n        canvas_draw_str_aligned(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1,\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 + 1,\n            AlignCenter,\n            AlignCenter,\n            key_str);\n\n        return;\n    }\n\n    if(key.icon != NULL) {\n        // Icon with no shift\n        const Icon* key_icon = key.icon;\n\n        if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||\n           (model->alt && key.value == HID_KEYBOARD_L_ALT) ||\n           (model->shift && key.value == HID_KEYBOARD_L_SHIFT) ||\n           (model->gui && key.value == HID_KEYBOARD_L_GUI)) {\n            if(key.icon_toggled) {\n                key_icon = key.icon_toggled;\n            }\n        }\n        // Draw the icon centered on the button\n        canvas_draw_icon(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key_icon->width / 2,\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key_icon->height / 2,\n            key_icon);\n\n        return;\n    }\n\n    if(key.key != 0) {\n        // Text with no shift\n        char key_str[2] = {key.key, '\\0'};\n        uint8_t key_offset = 0;\n\n        // Special case for numbers, draw them one pixel lower\n        if(key.value >= HID_KEYBOARD_1 && key.value <= HID_KEYBOARD_0) {\n            key_offset = 1;\n        }\n\n        canvas_draw_str_aligned(\n            canvas,\n            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1,\n            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 + key_offset,\n            AlignCenter,\n            AlignCenter,\n            key_str);\n\n        return;\n    }\n}\n\nstatic void hid_keyboard_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidKeyboardModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(!model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n        canvas_set_font(canvas, FontPrimary);\n        elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Keyboard\");\n\n        canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8);\n        canvas_set_font(canvas, FontSecondary);\n        elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, \"Hold to exit\");\n\n        elements_multiline_text_aligned(\n            canvas, 4, 60, AlignLeft, AlignBottom, \"Waiting for Connection...\");\n        return; // Dont render the keyboard if we are not yet connected\n    }\n#endif\n\n    canvas_set_font(canvas, FontKeyboard);\n    // Start shifting the all keys up if on the next row (Scrolling)\n    uint8_t initY = model->y == 0 ? 0 : 1;\n\n    if(model->y > 5) {\n        initY = model->y - 4;\n    }\n\n    elements_scrollbar(canvas, initY, 3);\n\n    for(uint8_t y = initY; y < ROW_COUNT; y++) {\n        const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];\n        uint8_t x = 0;\n        for(uint8_t i = 0; i < COLUMN_COUNT; i++) {\n            HidKeyboardKey key = keyboardKeyRow[i];\n            // Select when the button is hovered\n            // Select if the button is hovered within its width\n            // Select if back is clicked and its the backspace key\n            // Deselect when the button clicked or not hovered\n            bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;\n            bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;\n            hid_keyboard_draw_key(\n                canvas,\n                model,\n                x,\n                y - initY,\n                key,\n                (!model->ok_pressed && keySelected) || backSelected);\n            x += key.width;\n        }\n    }\n}\n\nstatic uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) {\n    HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x];\n    return key.value;\n}\n\nstatic void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) {\n    // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map\n    do {\n        const int delta_sum = model->y + delta.y;\n        model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT;\n    } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0);\n\n    do {\n        const int delta_sum = model->x + delta.x;\n        model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT;\n    } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width ==\n                                0); // Skip zero width keys, pretend they are one key\n}\n\nstatic void hid_keyboard_modifier_set(Hid* hid, uint16_t keycode, bool is_pressed) {\n    if(is_pressed) {\n        hid_hal_keyboard_press(hid, keycode);\n    } else {\n        hid_hal_keyboard_release(hid, keycode);\n    }\n}\n\nstatic void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) {\n    with_view_model(\n        hid_keyboard->view,\n        HidKeyboardModel * model,\n        {\n            if(event->key == InputKeyOk) {\n                if(event->type == InputTypePress) {\n                    model->ok_pressed = true;\n                } else if(event->type == InputTypeLong || event->type == InputTypeShort) {\n                    model->last_key_code = hid_keyboard_get_selected_key(model);\n\n                    // Toggle the modifier key when clicked, and click the key\n                    if(model->last_key_code == HID_KEYBOARD_L_SHIFT) {\n                        model->shift = !model->shift;\n                        hid_keyboard_modifier_set(\n                            hid_keyboard->hid, KEY_MOD_LEFT_SHIFT, model->shift);\n                    } else if(model->last_key_code == HID_KEYBOARD_L_ALT) {\n                        model->alt = !model->alt;\n                        hid_keyboard_modifier_set(hid_keyboard->hid, KEY_MOD_LEFT_ALT, model->alt);\n                    } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) {\n                        model->ctrl = !model->ctrl;\n                        hid_keyboard_modifier_set(\n                            hid_keyboard->hid, KEY_MOD_LEFT_CTRL, model->ctrl);\n                    } else if(model->last_key_code == HID_KEYBOARD_L_GUI) {\n                        model->gui = !model->gui;\n                        hid_keyboard_modifier_set(hid_keyboard->hid, KEY_MOD_LEFT_GUI, model->gui);\n                    } else {\n                        hid_hal_keyboard_press(hid_keyboard->hid, model->last_key_code);\n                    }\n\n                } else if(event->type == InputTypeRelease) {\n                    // Release happens after short and long presses\n                    hid_hal_keyboard_release(hid_keyboard->hid, model->last_key_code);\n                    model->ok_pressed = false;\n                }\n            } else if(event->key == InputKeyBack) {\n                // If back is pressed for a short time, backspace\n                if(event->type == InputTypePress) {\n                    model->back_pressed = true;\n                } else if(event->type == InputTypeShort) {\n                    hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE);\n                    hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE);\n                } else if(event->type == InputTypeRelease) {\n                    model->back_pressed = false;\n                }\n            } else if(event->type == InputTypePress || event->type == InputTypeRepeat) {\n                // Cycle the selected keys\n                if(event->key == InputKeyUp) {\n                    hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1});\n                } else if(event->key == InputKeyDown) {\n                    hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1});\n                } else if(event->key == InputKeyLeft) {\n                    hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0});\n                } else if(event->key == InputKeyRight) {\n                    hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0});\n                }\n            }\n        },\n        true);\n}\n\nstatic bool hid_keyboard_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidKeyboard* hid_keyboard = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeLong && event->key == InputKeyBack) {\n        hid_hal_keyboard_release_all(hid_keyboard->hid);\n        with_view_model(\n            hid_keyboard->view,\n            HidKeyboardModel * model,\n            {\n                model->shift = false;\n                model->alt = false;\n                model->ctrl = false;\n                model->gui = false;\n            },\n            true);\n    } else {\n        hid_keyboard_process(hid_keyboard, event);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nHidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {\n    HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard));\n    hid_keyboard->view = view_alloc();\n    hid_keyboard->hid = bt_hid;\n    view_set_context(hid_keyboard->view, hid_keyboard);\n    view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel));\n    view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback);\n    view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);\n\n    with_view_model(hid_keyboard->view, HidKeyboardModel * model, { model->y = 1; }, true);\n\n    return hid_keyboard;\n}\n\nvoid hid_keyboard_free(HidKeyboard* hid_keyboard) {\n    furi_assert(hid_keyboard);\n    view_free(hid_keyboard->view);\n    free(hid_keyboard);\n}\n\nView* hid_keyboard_get_view(HidKeyboard* hid_keyboard) {\n    furi_assert(hid_keyboard);\n    return hid_keyboard->view;\n}\n\nvoid hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) {\n    furi_assert(hid_keyboard);\n    with_view_model(\n        hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_keyboard.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct Hid Hid;\ntypedef struct HidKeyboard HidKeyboard;\n\nHidKeyboard* hid_keyboard_alloc(Hid* bt_hid);\n\nvoid hid_keyboard_free(HidKeyboard* hid_keyboard);\n\nView* hid_keyboard_get_view(HidKeyboard* hid_keyboard);\n\nvoid hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_keynote.c",
    "content": "#include \"hid_keynote.h\"\n#include <gui/elements.h>\n#include \"../hid.h\"\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidKeynote\"\n\nstruct HidKeynote {\n    View* view;\n    Hid* hid;\n};\n\ntypedef struct {\n    bool left_pressed;\n    bool up_pressed;\n    bool right_pressed;\n    bool down_pressed;\n    bool ok_pressed;\n    bool back_pressed;\n    bool connected;\n} HidKeynoteModel;\n\nstatic void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {\n    canvas_draw_triangle(canvas, x, y, 5, 3, dir);\n    if(dir == CanvasDirectionBottomToTop) {\n        canvas_draw_line(canvas, x, y + 6, x, y - 1);\n    } else if(dir == CanvasDirectionTopToBottom) {\n        canvas_draw_line(canvas, x, y - 6, x, y + 1);\n    } else if(dir == CanvasDirectionRightToLeft) {\n        canvas_draw_line(canvas, x + 6, y, x - 1, y);\n    } else if(dir == CanvasDirectionLeftToRight) {\n        canvas_draw_line(canvas, x - 6, y, x + 1, y);\n    }\n}\n\nstatic void hid_keynote_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidKeynoteModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Keynote\");\n\n    canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, \"Hold to exit\");\n\n    // Up\n    canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);\n    if(model->up_pressed) {\n        elements_slightly_rounded_box(canvas, 24, 26, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Down\n    canvas_draw_icon(canvas, 21, 45, &I_Button_18x18);\n    if(model->down_pressed) {\n        elements_slightly_rounded_box(canvas, 24, 47, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Left\n    canvas_draw_icon(canvas, 0, 45, &I_Button_18x18);\n    if(model->left_pressed) {\n        elements_slightly_rounded_box(canvas, 3, 47, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Right\n    canvas_draw_icon(canvas, 42, 45, &I_Button_18x18);\n    if(model->right_pressed) {\n        elements_slightly_rounded_box(canvas, 45, 47, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Ok\n    canvas_draw_icon(canvas, 63, 24, &I_Space_65x18);\n    if(model->ok_pressed) {\n        elements_slightly_rounded_box(canvas, 66, 26, 60, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 74, 28, &I_Ok_btn_9x9);\n    elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, \"Space\");\n    canvas_set_color(canvas, ColorBlack);\n\n    // Back\n    canvas_draw_icon(canvas, 63, 45, &I_Space_65x18);\n    if(model->back_pressed) {\n        elements_slightly_rounded_box(canvas, 66, 47, 60, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);\n    elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, \"Back\");\n}\n\nstatic void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidKeynoteModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, \"Keynote\");\n#else\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, \"Keynote\");\n#endif\n\n    canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8);\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, \"Hold to exit\");\n\n    const uint8_t x_2 = 23;\n    const uint8_t x_1 = 2;\n    const uint8_t x_3 = 44;\n\n    const uint8_t y_1 = 44;\n    const uint8_t y_2 = 65;\n\n    // Up\n    canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18);\n    if(model->up_pressed) {\n        elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Down\n    canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18);\n    if(model->down_pressed) {\n        elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Left\n    canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18);\n    if(model->left_pressed) {\n        elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Right\n    canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18);\n    if(model->right_pressed) {\n        elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Ok\n    canvas_draw_icon(canvas, 2, 86, &I_Space_60x18);\n    if(model->ok_pressed) {\n        elements_slightly_rounded_box(canvas, 5, 88, 55, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9);\n    elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, \"Space\");\n    canvas_set_color(canvas, ColorBlack);\n\n    // Back\n    canvas_draw_icon(canvas, 2, 107, &I_Space_60x18);\n    if(model->back_pressed) {\n        elements_slightly_rounded_box(canvas, 5, 109, 55, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8);\n    elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, \"Back\");\n}\n\nstatic void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {\n    with_view_model(\n        hid_keynote->view,\n        HidKeynoteModel * model,\n        {\n            if(event->type == InputTypePress) {\n                if(event->key == InputKeyUp) {\n                    model->up_pressed = true;\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);\n                } else if(event->key == InputKeyDown) {\n                    model->down_pressed = true;\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);\n                } else if(event->key == InputKeyLeft) {\n                    model->left_pressed = true;\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);\n                } else if(event->key == InputKeyRight) {\n                    model->right_pressed = true;\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);\n                } else if(event->key == InputKeyOk) {\n                    model->ok_pressed = true;\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);\n                } else if(event->key == InputKeyBack) {\n                    model->back_pressed = true;\n                }\n            } else if(event->type == InputTypeRelease) {\n                if(event->key == InputKeyUp) {\n                    model->up_pressed = false;\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);\n                } else if(event->key == InputKeyDown) {\n                    model->down_pressed = false;\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);\n                } else if(event->key == InputKeyLeft) {\n                    model->left_pressed = false;\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);\n                } else if(event->key == InputKeyRight) {\n                    model->right_pressed = false;\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);\n                } else if(event->key == InputKeyOk) {\n                    model->ok_pressed = false;\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);\n                } else if(event->key == InputKeyBack) {\n                    model->back_pressed = false;\n                }\n            } else if(event->type == InputTypeShort) {\n                if(event->key == InputKeyBack) {\n                    hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE);\n                    hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE);\n                    hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK);\n                    hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK);\n                }\n            }\n        },\n        true);\n}\n\nstatic bool hid_keynote_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidKeynote* hid_keynote = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeLong && event->key == InputKeyBack) {\n        hid_hal_keyboard_release_all(hid_keynote->hid);\n    } else {\n        hid_keynote_process(hid_keynote, event);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nHidKeynote* hid_keynote_alloc(Hid* hid) {\n    HidKeynote* hid_keynote = malloc(sizeof(HidKeynote));\n    hid_keynote->view = view_alloc();\n    hid_keynote->hid = hid;\n    view_set_context(hid_keynote->view, hid_keynote);\n    view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel));\n    view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);\n    view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);\n    return hid_keynote;\n}\n\nvoid hid_keynote_free(HidKeynote* hid_keynote) {\n    furi_assert(hid_keynote);\n    view_free(hid_keynote->view);\n    free(hid_keynote);\n}\n\nView* hid_keynote_get_view(HidKeynote* hid_keynote) {\n    furi_assert(hid_keynote);\n    return hid_keynote->view;\n}\n\nvoid hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {\n    furi_assert(hid_keynote);\n    with_view_model(\n        hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);\n}\n\nvoid hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) {\n    furi_assert(hid_keynote);\n\n    if(vertical) {\n        view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback);\n        view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip);\n\n    } else {\n        view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);\n        view_set_orientation(hid_keynote->view, ViewOrientationHorizontal);\n    }\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_keynote.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct Hid Hid;\ntypedef struct HidKeynote HidKeynote;\n\nHidKeynote* hid_keynote_alloc(Hid* bt_hid);\n\nvoid hid_keynote_free(HidKeynote* hid_keynote);\n\nView* hid_keynote_get_view(HidKeynote* hid_keynote);\n\nvoid hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected);\n\nvoid hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_media.c",
    "content": "#include \"hid_media.h\"\n#include <furi.h>\n#include <furi_hal_usb_hid.h>\n#include <extra_profiles/hid_profile.h>\n#include <gui/elements.h>\n#include \"../hid.h\"\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidMedia\"\n\nstruct HidMedia {\n    View* view;\n    Hid* hid;\n};\n\ntypedef struct {\n    bool left_pressed;\n    bool up_pressed;\n    bool right_pressed;\n    bool down_pressed;\n    bool ok_pressed;\n    bool connected;\n} HidMediaModel;\n\nstatic void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {\n    canvas_draw_triangle(canvas, x, y, 5, 3, dir);\n    if(dir == CanvasDirectionBottomToTop) {\n        canvas_draw_dot(canvas, x, y - 1);\n    } else if(dir == CanvasDirectionTopToBottom) {\n        canvas_draw_dot(canvas, x, y + 1);\n    } else if(dir == CanvasDirectionRightToLeft) {\n        canvas_draw_dot(canvas, x - 1, y);\n    } else if(dir == CanvasDirectionLeftToRight) {\n        canvas_draw_dot(canvas, x + 1, y);\n    }\n}\n\nstatic void hid_media_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidMediaModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Media\");\n    canvas_set_font(canvas, FontSecondary);\n\n    // Keypad circles\n    canvas_draw_icon(canvas, 75, 9, &I_Dpad_49x46);\n\n    // Up\n    if(model->up_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 93, 10, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 96, 13, &I_Volup_8x6);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Down\n    if(model->down_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 96, 44, &I_Voldwn_6x6);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Left\n    if(model->left_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);\n    hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Right\n    if(model->right_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);\n    hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Ok\n    if(model->ok_pressed) {\n        canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);\n    canvas_draw_line(canvas, 100, 29, 100, 33);\n    canvas_draw_line(canvas, 102, 29, 102, 33);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Exit\n    canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, \"Hold to exit\");\n}\n\nstatic void hid_media_process_press(HidMedia* hid_media, InputEvent* event) {\n    with_view_model(\n        hid_media->view,\n        HidMediaModel * model,\n        {\n            if(event->key == InputKeyUp) {\n                model->up_pressed = true;\n                hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);\n            } else if(event->key == InputKeyDown) {\n                model->down_pressed = true;\n                hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);\n            } else if(event->key == InputKeyLeft) {\n                model->left_pressed = true;\n                hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);\n            } else if(event->key == InputKeyRight) {\n                model->right_pressed = true;\n                hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);\n            } else if(event->key == InputKeyOk) {\n                model->ok_pressed = true;\n                hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);\n            }\n        },\n        true);\n}\n\nstatic void hid_media_process_release(HidMedia* hid_media, InputEvent* event) {\n    with_view_model(\n        hid_media->view,\n        HidMediaModel * model,\n        {\n            if(event->key == InputKeyUp) {\n                model->up_pressed = false;\n                hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);\n            } else if(event->key == InputKeyDown) {\n                model->down_pressed = false;\n                hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);\n            } else if(event->key == InputKeyLeft) {\n                model->left_pressed = false;\n                hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);\n            } else if(event->key == InputKeyRight) {\n                model->right_pressed = false;\n                hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);\n            } else if(event->key == InputKeyOk) {\n                model->ok_pressed = false;\n                hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);\n            }\n        },\n        true);\n}\n\nstatic bool hid_media_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidMedia* hid_media = context;\n    bool consumed = false;\n\n    if(event->type == InputTypePress) {\n        hid_media_process_press(hid_media, event);\n        consumed = true;\n    } else if(event->type == InputTypeRelease) {\n        hid_media_process_release(hid_media, event);\n        consumed = true;\n    } else if(event->type == InputTypeShort) {\n        consumed = true;\n    } else if(event->type == InputTypeLong) {\n        if(event->key == InputKeyBack) {\n            hid_hal_consumer_key_release_all(hid_media->hid);\n        }\n    }\n\n    return consumed;\n}\n\nHidMedia* hid_media_alloc(Hid* hid) {\n    HidMedia* hid_media = malloc(sizeof(HidMedia));\n    hid_media->view = view_alloc();\n    hid_media->hid = hid;\n    view_set_context(hid_media->view, hid_media);\n    view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel));\n    view_set_draw_callback(hid_media->view, hid_media_draw_callback);\n    view_set_input_callback(hid_media->view, hid_media_input_callback);\n    return hid_media;\n}\n\nvoid hid_media_free(HidMedia* hid_media) {\n    furi_assert(hid_media);\n    view_free(hid_media->view);\n    free(hid_media);\n}\n\nView* hid_media_get_view(HidMedia* hid_media) {\n    furi_assert(hid_media);\n    return hid_media->view;\n}\n\nvoid hid_media_set_connected_status(HidMedia* hid_media, bool connected) {\n    furi_assert(hid_media);\n    with_view_model(\n        hid_media->view, HidMediaModel * model, { model->connected = connected; }, true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_media.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct Hid Hid;\ntypedef struct HidMedia HidMedia;\n\nHidMedia* hid_media_alloc(Hid* hid);\n\nvoid hid_media_free(HidMedia* hid_media);\n\nView* hid_media_get_view(HidMedia* hid_media);\n\nvoid hid_media_set_connected_status(HidMedia* hid_media, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse.c",
    "content": "#include \"hid_mouse.h\"\n#include <gui/elements.h>\n#include \"../hid.h\"\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidMouse\"\n\nstruct HidMouse {\n    View* view;\n    Hid* hid;\n};\n\ntypedef struct {\n    bool left_pressed;\n    bool up_pressed;\n    bool right_pressed;\n    bool down_pressed;\n    bool left_mouse_pressed;\n    bool left_mouse_held;\n    bool right_mouse_pressed;\n    bool connected;\n} HidMouseModel;\n\nstatic void hid_mouse_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidMouseModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Mouse\");\n    canvas_set_font(canvas, FontSecondary);\n\n    if(model->left_mouse_held == true) {\n        elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, \"Selecting...\");\n    } else {\n        canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);\n        canvas_set_font(canvas, FontSecondary);\n        elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, \"Hold to exit\");\n    }\n\n    // Keypad circles\n    canvas_draw_icon(canvas, 63, 9, &I_Dpad_49x46);\n\n    // Up\n    if(model->up_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 81, 10, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 84, 12, &I_Pin_arrow_up_7x9);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Down\n    if(model->down_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Left\n    if(model->left_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Right\n    if(model->right_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Ok\n    if(model->left_mouse_pressed) {\n        canvas_draw_icon(canvas, 81, 26, &I_Ok_btn_pressed_13x12);\n    } else {\n        canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x10);\n    }\n\n    // Back\n    if(model->right_mouse_pressed) {\n        canvas_draw_icon(canvas, 108, 49, &I_Ok_btn_pressed_13x12);\n    } else {\n        canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x10);\n    }\n}\n\nstatic void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) {\n    with_view_model(\n        hid_mouse->view,\n        HidMouseModel * model,\n        {\n            if(event->key == InputKeyBack) {\n                if(event->type == InputTypeShort) {\n                    hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT);\n                    hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT);\n                } else if(event->type == InputTypePress) {\n                    model->right_mouse_pressed = true;\n                } else if(event->type == InputTypeRelease) {\n                    model->right_mouse_pressed = false;\n                }\n            } else if(event->key == InputKeyOk) {\n                if(event->type == InputTypeShort) {\n                    // Just release if it was being held before\n                    if(!model->left_mouse_held)\n                        hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT);\n                    hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT);\n                    model->left_mouse_held = false;\n                } else if(event->type == InputTypeLong) {\n                    hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT);\n                    model->left_mouse_held = true;\n                    model->left_mouse_pressed = true;\n                } else if(event->type == InputTypePress) {\n                    model->left_mouse_pressed = true;\n                } else if(event->type == InputTypeRelease) {\n                    // Only release if it wasn't a long press\n                    if(!model->left_mouse_held) model->left_mouse_pressed = false;\n                }\n            } else if(event->key == InputKeyRight) {\n                if(event->type == InputTypePress) {\n                    model->right_pressed = true;\n                    hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0);\n                } else if(event->type == InputTypeRepeat) {\n                    hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0);\n                } else if(event->type == InputTypeRelease) {\n                    model->right_pressed = false;\n                }\n            } else if(event->key == InputKeyLeft) {\n                if(event->type == InputTypePress) {\n                    model->left_pressed = true;\n                    hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0);\n                } else if(event->type == InputTypeRepeat) {\n                    hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0);\n                } else if(event->type == InputTypeRelease) {\n                    model->left_pressed = false;\n                }\n            } else if(event->key == InputKeyDown) {\n                if(event->type == InputTypePress) {\n                    model->down_pressed = true;\n                    hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT);\n                } else if(event->type == InputTypeRepeat) {\n                    hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG);\n                } else if(event->type == InputTypeRelease) {\n                    model->down_pressed = false;\n                }\n            } else if(event->key == InputKeyUp) {\n                if(event->type == InputTypePress) {\n                    model->up_pressed = true;\n                    hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT);\n                } else if(event->type == InputTypeRepeat) {\n                    hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG);\n                } else if(event->type == InputTypeRelease) {\n                    model->up_pressed = false;\n                }\n            }\n        },\n        true);\n}\n\nstatic bool hid_mouse_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidMouse* hid_mouse = context;\n    bool consumed = false;\n\n    if(event->type == InputTypeLong && event->key == InputKeyBack) {\n        hid_hal_mouse_release_all(hid_mouse->hid);\n\n        with_view_model(\n            hid_mouse->view,\n            HidMouseModel * model,\n            {\n                model->left_mouse_held = false;\n                model->left_mouse_pressed = false;\n            },\n            false);\n    } else {\n        hid_mouse_process(hid_mouse, event);\n        consumed = true;\n    }\n\n    return consumed;\n}\n\nHidMouse* hid_mouse_alloc(Hid* hid) {\n    HidMouse* hid_mouse = malloc(sizeof(HidMouse));\n    hid_mouse->view = view_alloc();\n    hid_mouse->hid = hid;\n    view_set_context(hid_mouse->view, hid_mouse);\n    view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel));\n    view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback);\n    view_set_input_callback(hid_mouse->view, hid_mouse_input_callback);\n    return hid_mouse;\n}\n\nvoid hid_mouse_free(HidMouse* hid_mouse) {\n    furi_assert(hid_mouse);\n    view_free(hid_mouse->view);\n    free(hid_mouse);\n}\n\nView* hid_mouse_get_view(HidMouse* hid_mouse) {\n    furi_assert(hid_mouse);\n    return hid_mouse->view;\n}\n\nvoid hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) {\n    furi_assert(hid_mouse);\n    with_view_model(\n        hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\n#define MOUSE_MOVE_SHORT 5\n#define MOUSE_MOVE_LONG  20\n\ntypedef struct Hid Hid;\ntypedef struct HidMouse HidMouse;\n\nHidMouse* hid_mouse_alloc(Hid* bt_hid);\n\nvoid hid_mouse_free(HidMouse* hid_mouse);\n\nView* hid_mouse_get_view(HidMouse* hid_mouse);\n\nvoid hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse_clicker.c",
    "content": "#include \"hid_mouse_clicker.h\"\n#include <gui/elements.h>\n#include \"../hid.h\"\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidMouseClicker\"\n\n#define DEFAULT_CLICK_RATE 1\n#define MAXIMUM_CLICK_RATE 100\n\nstruct HidMouseClicker {\n    View* view;\n    Hid* hid;\n    FuriTimer* timer;\n};\n\ntypedef struct {\n    bool connected;\n    bool running;\n    int rate;\n    enum HidMouseButtons btn;\n} HidMouseClickerModel;\n\nstatic void hid_mouse_clicker_start_or_restart_timer(void* context) {\n    furi_assert(context);\n    HidMouseClicker* hid_mouse_clicker = context;\n\n    if(furi_timer_is_running(hid_mouse_clicker->timer)) {\n        furi_timer_stop(hid_mouse_clicker->timer);\n    }\n\n    with_view_model(\n        hid_mouse_clicker->view,\n        HidMouseClickerModel * model,\n        {\n            furi_timer_start(\n                hid_mouse_clicker->timer,\n                furi_kernel_get_tick_frequency() /\n                    ((model->rate) ? model->rate : MAXIMUM_CLICK_RATE));\n        },\n        true);\n}\n\nstatic void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidMouseClickerModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Mouse Clicker\");\n    canvas_set_font(canvas, FontSecondary);\n\n    // Ok\n    canvas_draw_icon(canvas, 58, 25, &I_Space_65x18);\n\n    canvas_draw_icon(canvas, 61, 50, &I_ButtonLeft_4x7);\n    canvas_draw_icon(canvas, 117, 50, &I_ButtonRight_4x7);\n\n    const char* btn_label;\n    switch(model->btn) {\n    case HID_MOUSE_BTN_LEFT:\n        btn_label = \"Left\";\n        break;\n    case HID_MOUSE_BTN_WHEEL:\n        btn_label = \"Middle\";\n        break;\n    case HID_MOUSE_BTN_RIGHT:\n        btn_label = \"Right\";\n        break;\n    default:\n        furi_crash();\n    }\n\n    elements_multiline_text_aligned(canvas, 89, 57, AlignCenter, AlignBottom, btn_label);\n\n    if(model->running) {\n        elements_slightly_rounded_box(canvas, 61, 27, 60, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n\n    canvas_draw_icon(canvas, 69, 29, &I_Ok_btn_9x9);\n\n    if(model->running) {\n        elements_multiline_text_aligned(canvas, 86, 37, AlignLeft, AlignBottom, \"Stop\");\n    } else {\n        elements_multiline_text_aligned(canvas, 86, 37, AlignLeft, AlignBottom, \"Start\");\n    }\n    canvas_set_color(canvas, ColorBlack);\n\n    // Clicks/s\n    char label[20];\n    if(model->rate) {\n        snprintf(label, sizeof(label), \"%d clicks/s\", model->rate);\n    } else {\n        snprintf(label, sizeof(label), \"max clicks/s\");\n    }\n    elements_multiline_text_aligned(canvas, 28, 37, AlignCenter, AlignBottom, label);\n\n    canvas_draw_icon(canvas, 25, 20, &I_ButtonUp_7x4);\n    canvas_draw_icon(canvas, 25, 44, &I_ButtonDown_7x4);\n\n    // Back\n    canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);\n    elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, \"Exit\");\n}\n\nstatic void hid_mouse_clicker_timer_callback(void* context) {\n    furi_assert(context);\n    HidMouseClicker* hid_mouse_clicker = context;\n    with_view_model(\n        hid_mouse_clicker->view,\n        HidMouseClickerModel * model,\n        {\n            if(model->running) {\n                hid_hal_mouse_press(hid_mouse_clicker->hid, model->btn);\n                hid_hal_mouse_release(hid_mouse_clicker->hid, model->btn);\n            }\n        },\n        false);\n}\n\nstatic void hid_mouse_clicker_enter_callback(void* context) {\n    hid_mouse_clicker_start_or_restart_timer(context);\n}\n\nstatic void hid_mouse_clicker_exit_callback(void* context) {\n    furi_assert(context);\n    HidMouseClicker* hid_mouse_clicker = context;\n    furi_timer_stop(hid_mouse_clicker->timer);\n}\n\nstatic bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidMouseClicker* hid_mouse_clicker = context;\n\n    bool consumed = false;\n    bool rate_changed = false;\n\n    if(event->type == InputTypePress || event->type == InputTypeRelease) {\n        return false;\n    }\n\n    with_view_model(\n        hid_mouse_clicker->view,\n        HidMouseClickerModel * model,\n        {\n            switch(event->key) {\n            case InputKeyOk:\n                model->running = !model->running;\n                consumed = true;\n                break;\n            case InputKeyUp:\n                if(model->rate < MAXIMUM_CLICK_RATE) {\n                    model->rate++;\n                }\n                rate_changed = true;\n                consumed = true;\n                break;\n            case InputKeyDown:\n                if(model->rate > 0) {\n                    model->rate--;\n                }\n                rate_changed = true;\n                consumed = true;\n                break;\n            case InputKeyBack:\n                model->running = false;\n                break;\n            case InputKeyLeft:\n                switch(model->btn) {\n                case HID_MOUSE_BTN_LEFT:\n                    model->btn = HID_MOUSE_BTN_RIGHT;\n                    break;\n                case HID_MOUSE_BTN_WHEEL:\n                    model->btn = HID_MOUSE_BTN_LEFT;\n                    break;\n                case HID_MOUSE_BTN_RIGHT:\n                    model->btn = HID_MOUSE_BTN_WHEEL;\n                    break;\n                }\n                consumed = true;\n                break;\n            case InputKeyRight:\n                switch(model->btn) {\n                case HID_MOUSE_BTN_LEFT:\n                    model->btn = HID_MOUSE_BTN_WHEEL;\n                    break;\n                case HID_MOUSE_BTN_WHEEL:\n                    model->btn = HID_MOUSE_BTN_RIGHT;\n                    break;\n                case HID_MOUSE_BTN_RIGHT:\n                    model->btn = HID_MOUSE_BTN_LEFT;\n                    break;\n                }\n                consumed = true;\n                break;\n            default:\n                consumed = true;\n                break;\n            }\n        },\n        true);\n\n    if(rate_changed) {\n        hid_mouse_clicker_start_or_restart_timer(context);\n    }\n\n    return consumed;\n}\n\nHidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {\n    HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker));\n\n    hid_mouse_clicker->view = view_alloc();\n    view_set_context(hid_mouse_clicker->view, hid_mouse_clicker);\n    view_allocate_model(\n        hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel));\n    view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback);\n    view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback);\n    view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback);\n    view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback);\n\n    hid_mouse_clicker->hid = hid;\n\n    hid_mouse_clicker->timer = furi_timer_alloc(\n        hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker);\n\n    with_view_model(\n        hid_mouse_clicker->view,\n        HidMouseClickerModel * model,\n        {\n            model->rate = DEFAULT_CLICK_RATE;\n            model->btn = HID_MOUSE_BTN_LEFT;\n        },\n        true);\n\n    return hid_mouse_clicker;\n}\n\nvoid hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) {\n    furi_assert(hid_mouse_clicker);\n\n    furi_timer_stop(hid_mouse_clicker->timer);\n    furi_timer_free(hid_mouse_clicker->timer);\n\n    view_free(hid_mouse_clicker->view);\n\n    free(hid_mouse_clicker);\n}\n\nView* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) {\n    furi_assert(hid_mouse_clicker);\n    return hid_mouse_clicker->view;\n}\n\nvoid hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) {\n    furi_assert(hid_mouse_clicker);\n    with_view_model(\n        hid_mouse_clicker->view,\n        HidMouseClickerModel * model,\n        { model->connected = connected; },\n        true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse_clicker.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct Hid Hid;\ntypedef struct HidMouseClicker HidMouseClicker;\n\nHidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid);\n\nvoid hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker);\n\nView* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker);\n\nvoid hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse_jiggler.c",
    "content": "#include \"hid_mouse_jiggler.h\"\n#include <gui/elements.h>\n#include \"../hid.h\"\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidMouseJiggler\"\n\nstruct HidMouseJiggler {\n    View* view;\n    Hid* hid;\n    FuriTimer* timer;\n};\n\ntypedef struct {\n    bool connected;\n    bool running;\n    uint8_t counter;\n} HidMouseJigglerModel;\n\nstatic void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidMouseJigglerModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"Mouse Jiggler\");\n    canvas_set_font(canvas, FontSecondary);\n\n    // Ok\n    canvas_draw_icon(canvas, 32, 25, &I_Space_65x18);\n\n    if(model->running) {\n        elements_slightly_rounded_box(canvas, 35, 27, 60, 13);\n        canvas_set_color(canvas, ColorWhite);\n    }\n\n    canvas_draw_icon(canvas, 43, 29, &I_Ok_btn_9x9);\n\n    if(model->running) {\n        elements_multiline_text_aligned(canvas, 60, 37, AlignLeft, AlignBottom, \"Stop\");\n    } else {\n        elements_multiline_text_aligned(canvas, 60, 37, AlignLeft, AlignBottom, \"Start\");\n    }\n    canvas_set_color(canvas, ColorBlack);\n\n    // Back\n    canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);\n    elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, \"Exit\");\n}\n\nstatic void hid_mouse_jiggler_timer_callback(void* context) {\n    furi_assert(context);\n    HidMouseJiggler* hid_mouse_jiggler = context;\n    with_view_model(\n        hid_mouse_jiggler->view,\n        HidMouseJigglerModel * model,\n        {\n            if(model->running) {\n                model->counter++;\n                hid_hal_mouse_move(\n                    hid_mouse_jiggler->hid,\n                    (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT,\n                    0);\n            }\n        },\n        false);\n}\n\nstatic void hid_mouse_jiggler_enter_callback(void* context) {\n    furi_assert(context);\n    HidMouseJiggler* hid_mouse_jiggler = context;\n\n    furi_timer_start(hid_mouse_jiggler->timer, 500);\n}\n\nstatic void hid_mouse_jiggler_exit_callback(void* context) {\n    furi_assert(context);\n    HidMouseJiggler* hid_mouse_jiggler = context;\n    furi_timer_stop(hid_mouse_jiggler->timer);\n}\n\nstatic bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidMouseJiggler* hid_mouse_jiggler = context;\n\n    bool consumed = false;\n\n    if(event->type == InputTypeShort) {\n        with_view_model(\n            hid_mouse_jiggler->view,\n            HidMouseJigglerModel * model,\n            {\n                switch(event->key) {\n                case InputKeyOk:\n                    model->running = !model->running;\n                    consumed = true;\n                    break;\n                case InputKeyBack:\n                    model->running = false;\n                    break;\n                default:\n                    consumed = true;\n                    break;\n                }\n            },\n            true);\n    }\n\n    return consumed;\n}\n\nHidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {\n    HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler));\n\n    hid_mouse_jiggler->view = view_alloc();\n    view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler);\n    view_allocate_model(\n        hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel));\n    view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback);\n    view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback);\n    view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback);\n    view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback);\n\n    hid_mouse_jiggler->hid = hid;\n\n    hid_mouse_jiggler->timer = furi_timer_alloc(\n        hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);\n\n    return hid_mouse_jiggler;\n}\n\nvoid hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) {\n    furi_assert(hid_mouse_jiggler);\n\n    furi_timer_stop(hid_mouse_jiggler->timer);\n    furi_timer_free(hid_mouse_jiggler->timer);\n\n    view_free(hid_mouse_jiggler->view);\n\n    free(hid_mouse_jiggler);\n}\n\nView* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) {\n    furi_assert(hid_mouse_jiggler);\n    return hid_mouse_jiggler->view;\n}\n\nvoid hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) {\n    furi_assert(hid_mouse_jiggler);\n    with_view_model(\n        hid_mouse_jiggler->view,\n        HidMouseJigglerModel * model,\n        { model->connected = connected; },\n        true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_mouse_jiggler.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\n#define MOUSE_MOVE_SHORT 5\n#define MOUSE_MOVE_LONG  20\n\ntypedef struct Hid Hid;\ntypedef struct HidMouseJiggler HidMouseJiggler;\n\nHidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid);\n\nvoid hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler);\n\nView* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler);\n\nvoid hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_tiktok.c",
    "content": "#include \"hid_tiktok.h\"\n#include \"../hid.h\"\n#include <gui/elements.h>\n\n#include \"hid_icons.h\"\n\n#define TAG \"HidTikTok\"\n\nstruct HidTikTok {\n    View* view;\n    Hid* hid;\n};\n\ntypedef struct {\n    bool left_pressed;\n    bool up_pressed;\n    bool right_pressed;\n    bool down_pressed;\n    bool ok_pressed;\n    bool connected;\n    bool is_cursor_set;\n} HidTikTokModel;\n\nstatic void hid_tiktok_draw_callback(Canvas* canvas, void* context) {\n    furi_assert(context);\n    HidTikTokModel* model = context;\n\n    // Header\n#ifdef HID_TRANSPORT_BLE\n    if(model->connected) {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);\n    } else {\n        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);\n    }\n#endif\n\n    canvas_set_font(canvas, FontPrimary);\n    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, \"TikTok\");\n    canvas_set_font(canvas, FontSecondary);\n\n    // Keypad circles\n    canvas_draw_icon(canvas, 75, 9, &I_Dpad_49x46);\n\n    // Up\n    if(model->up_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 93, 10, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 96, 12, &I_Arr_up_7x9);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Down\n    if(model->down_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 96, 43, &I_Arr_dwn_7x9);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Left\n    if(model->left_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Right\n    if(model->right_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 110, 25, &I_Pressed_Button_13x13);\n        canvas_set_bitmap_mode(canvas, false);\n        canvas_set_color(canvas, ColorWhite);\n    }\n    canvas_draw_icon(canvas, 112, 29, &I_Volup_8x6);\n    canvas_set_color(canvas, ColorBlack);\n\n    // Ok\n    if(model->ok_pressed) {\n        canvas_set_bitmap_mode(canvas, true);\n        canvas_draw_icon(canvas, 91, 24, &I_Like_pressed_17x16);\n        canvas_set_bitmap_mode(canvas, false);\n    } else {\n        canvas_draw_icon(canvas, 93, 27, &I_Like_def_13x11);\n    }\n    // Exit\n    canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);\n    canvas_set_font(canvas, FontSecondary);\n    elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, \"Hold to exit\");\n}\n\nstatic void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) {\n    // Set cursor to the phone's left up corner\n    // Delays to guarantee one packet per connection interval\n    for(size_t i = 0; i < 8; i++) {\n        hid_hal_mouse_move(hid_tiktok->hid, -127, -127);\n        furi_delay_ms(50);\n    }\n    // Move cursor from the corner\n    // Actions split for some mobiles to properly process mouse movements\n    hid_hal_mouse_move(hid_tiktok->hid, 10, 60);\n    furi_delay_ms(3);\n    hid_hal_mouse_move(hid_tiktok->hid, 0, 60);\n    furi_delay_ms(50);\n}\n\nstatic void\n    hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) {\n    if(event->key == InputKeyUp) {\n        model->up_pressed = true;\n    } else if(event->key == InputKeyDown) {\n        model->down_pressed = true;\n    } else if(event->key == InputKeyLeft) {\n        model->left_pressed = true;\n        hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT);\n    } else if(event->key == InputKeyRight) {\n        model->right_pressed = true;\n        hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);\n    } else if(event->key == InputKeyOk) {\n        model->ok_pressed = true;\n    }\n}\n\nstatic void\n    hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) {\n    if(event->key == InputKeyUp) {\n        model->up_pressed = false;\n    } else if(event->key == InputKeyDown) {\n        model->down_pressed = false;\n    } else if(event->key == InputKeyLeft) {\n        model->left_pressed = false;\n        hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT);\n    } else if(event->key == InputKeyRight) {\n        model->right_pressed = false;\n        hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);\n    } else if(event->key == InputKeyOk) {\n        model->ok_pressed = false;\n    }\n}\n\nstatic bool hid_tiktok_input_callback(InputEvent* event, void* context) {\n    furi_assert(context);\n    HidTikTok* hid_tiktok = context;\n    bool consumed = false;\n\n    with_view_model(\n        hid_tiktok->view,\n        HidTikTokModel * model,\n        {\n            if(event->type == InputTypePress) {\n                hid_tiktok_process_press(hid_tiktok, model, event);\n                if(model->connected && !model->is_cursor_set) {\n                    hid_tiktok_reset_cursor(hid_tiktok);\n                    model->is_cursor_set = true;\n                }\n                consumed = true;\n            } else if(event->type == InputTypeRelease) {\n                hid_tiktok_process_release(hid_tiktok, model, event);\n                consumed = true;\n            } else if(event->type == InputTypeShort) {\n                if(event->key == InputKeyOk) {\n                    // delays adjusted for emulation of a finger tap\n                    hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);\n                    furi_delay_ms(25);\n                    hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);\n                    furi_delay_ms(75);\n                    hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);\n                    furi_delay_ms(25);\n                    hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);\n                    consumed = true;\n                } else if(event->key == InputKeyUp) {\n                    // Emulate up swipe\n                    hid_hal_mouse_scroll(hid_tiktok->hid, -12);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, -24);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, -38);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, -24);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, -12);\n                    consumed = true;\n                } else if(event->key == InputKeyDown) {\n                    // Emulate down swipe\n                    hid_hal_mouse_scroll(hid_tiktok->hid, 12);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, 24);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, 38);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, 24);\n                    hid_hal_mouse_scroll(hid_tiktok->hid, 12);\n                    consumed = true;\n                } else if(event->key == InputKeyBack) {\n                    hid_hal_consumer_key_release_all(hid_tiktok->hid);\n                    consumed = true;\n                }\n            } else if(event->type == InputTypeLong) {\n                if(event->key == InputKeyBack) {\n                    hid_hal_consumer_key_release_all(hid_tiktok->hid);\n                    model->is_cursor_set = false;\n                    consumed = false;\n                }\n            }\n        },\n        true);\n\n    return consumed;\n}\n\nHidTikTok* hid_tiktok_alloc(Hid* bt_hid) {\n    HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok));\n    hid_tiktok->hid = bt_hid;\n    hid_tiktok->view = view_alloc();\n    view_set_context(hid_tiktok->view, hid_tiktok);\n    view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel));\n    view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback);\n    view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback);\n\n    return hid_tiktok;\n}\n\nvoid hid_tiktok_free(HidTikTok* hid_tiktok) {\n    furi_assert(hid_tiktok);\n    view_free(hid_tiktok->view);\n    free(hid_tiktok);\n}\n\nView* hid_tiktok_get_view(HidTikTok* hid_tiktok) {\n    furi_assert(hid_tiktok);\n    return hid_tiktok->view;\n}\n\nvoid hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) {\n    furi_assert(hid_tiktok);\n    with_view_model(\n        hid_tiktok->view,\n        HidTikTokModel * model,\n        {\n            model->connected = connected;\n            model->is_cursor_set = false;\n        },\n        true);\n}\n"
  },
  {
    "path": "applications/system/hid_app/views/hid_tiktok.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct Hid Hid;\ntypedef struct HidTikTok HidTikTok;\n\nHidTikTok* hid_tiktok_alloc(Hid* bt_hid);\n\nvoid hid_tiktok_free(HidTikTok* hid_tiktok);\n\nView* hid_tiktok_get_view(HidTikTok* hid_tiktok);\n\nvoid hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected);\n"
  },
  {
    "path": "applications/system/hid_app/views.h",
    "content": "typedef enum {\n    HidViewSubmenu,\n    HidViewKeynote,\n    HidViewKeyboard,\n    HidViewMedia,\n    HidViewMouse,\n    HidViewMouseClicker,\n    HidViewMouseJiggler,\n    BtHidViewTikTok,\n    HidViewDialog,\n    HidViewPopup,\n} HidView;\n"
  },
  {
    "path": "applications/system/js_app/application.fam",
    "content": "App(\n    appid=\"js_app\",\n    name=\"JS Runner\",\n    apptype=FlipperAppType.SYSTEM,\n    entry_point=\"js_app\",\n    stack_size=2 * 1024,\n    resources=\"examples\",\n    order=10,\n    provides=[\"js_app_start\"],\n    sources=[\n        \"js_app.c\",\n        \"js_modules.c\",\n        \"js_thread.c\",\n        \"js_value.c\",\n        \"plugin_api/app_api_table.cpp\",\n        \"views/console_view.c\",\n        \"modules/js_flipper.c\",\n        \"modules/js_tests.c\",\n    ],\n)\n\nApp(\n    appid=\"js_app_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"js_app_on_system_start\",\n    order=110,\n    sources=[\"js_app.c\"],\n)\n\nApp(\n    appid=\"js_event_loop\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_event_loop_ep\",\n    requires=[\"js_app\"],\n    sources=[\n        \"modules/js_event_loop/js_event_loop.c\",\n        \"modules/js_event_loop/js_event_loop_api_table.cpp\",\n    ],\n)\n\nApp(\n    appid=\"js_gui\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_gui_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/js_gui.c\", \"modules/js_gui/js_gui_api_table.cpp\"],\n)\n\nApp(\n    appid=\"js_gui__loading\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_loading_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/loading.c\"],\n)\n\nApp(\n    appid=\"js_gui__empty_screen\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_empty_screen_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/empty_screen.c\"],\n)\n\nApp(\n    appid=\"js_gui__submenu\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_submenu_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/submenu.c\"],\n)\n\nApp(\n    appid=\"js_gui__text_input\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_text_input_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/text_input.c\"],\n)\n\nApp(\n    appid=\"js_gui__number_input\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_number_input_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/number_input.c\"],\n)\n\nApp(\n    appid=\"js_gui__button_panel\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_button_panel_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/button_panel.c\"],\n)\n\nApp(\n    appid=\"js_gui__popup\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_popup_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/popup.c\"],\n)\n\nApp(\n    appid=\"js_gui__button_menu\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_button_menu_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/button_menu.c\"],\n)\n\nApp(\n    appid=\"js_gui__menu\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_menu_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/menu.c\"],\n)\n\nApp(\n    appid=\"js_gui__vi_list\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_vi_list_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/vi_list.c\"],\n)\n\nApp(\n    appid=\"js_gui__byte_input\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_byte_input_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/byte_input.c\"],\n)\n\nApp(\n    appid=\"js_gui__text_box\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_text_box_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/text_box.c\"],\n)\n\nApp(\n    appid=\"js_gui__dialog\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_dialog_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/dialog.c\"],\n)\n\nApp(\n    appid=\"js_gui__file_picker\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_gui_file_picker_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/file_picker.c\"],\n    fap_libs=[\"assets\"],\n)\n\nApp(\n    appid=\"js_gui__widget\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_view_widget_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/widget.c\"],\n)\n\nApp(\n    appid=\"js_gui__icon\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_gui_icon_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gui/icon.c\"],\n    fap_libs=[\"assets\"],\n)\n\nApp(\n    appid=\"js_notification\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_notification_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_notification.c\"],\n)\n\nApp(\n    appid=\"js_badusb\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_badusb_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_badusb.c\"],\n)\n\nApp(\n    appid=\"js_serial\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_serial_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_serial.c\"],\n)\n\nApp(\n    appid=\"js_gpio\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_gpio_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_gpio.c\"],\n)\n\nApp(\n    appid=\"js_math\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_math_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_math.c\"],\n)\n\nApp(\n    appid=\"js_storage\",\n    apptype=FlipperAppType.PLUGIN,\n    entry_point=\"js_storage_ep\",\n    requires=[\"js_app\"],\n    sources=[\"modules/js_storage.c\"],\n)\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/array_buf_test.js",
    "content": "let arr_1 = Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);\nprint(\"len =\", arr_1.buffer.byteLength);\n\nlet arr_2 = Uint8Array(arr_1.buffer.slice(2, 6));\nprint(\"slice len =\", arr_2.buffer.byteLength);\nfor (let i = 0; i < arr_2.buffer.byteLength; i++) {\n    print(arr_2[i]);\n}\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/bad_uart.js",
    "content": "let serial = require(\"serial\");\nserial.setup(\"lpuart\", 115200);\n\n// serial.write(\"\\n\");\nserial.write([0x0a]);\nlet console_resp = serial.expect(\"# \", 1000);\nif (console_resp === undefined) {\n    print(\"No CLI response\");\n} else {\n    serial.write(\"uci\\n\");\n    let uci_state = serial.expect([\": not found\", \"Usage: \"]);\n    if (uci_state === 1) {\n        serial.expect(\"# \");\n        serial.write(\"uci show wireless\\n\");\n        serial.expect(\".key=\");\n        print(\"key:\", serial.readln());\n    } else {\n        print(\"uci cmd not found\");\n    }\n}\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/badusb_demo.js",
    "content": "let badusb = require(\"badusb\");\nlet notify = require(\"notification\");\nlet flipper = require(\"flipper\");\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet dialog = require(\"gui/dialog\");\n\nlet views = {\n    dialog: dialog.makeWith({\n        header: \"BadUSB demo\",\n        text: \"Press OK to start\",\n        center: \"Start\",\n    }),\n};\n\nbadusb.setup({\n    vid: 0xAAAA,\n    pid: 0xBBBB,\n    mfrName: \"Flipper\",\n    prodName: \"Zero\",\n    layoutPath: \"/ext/badusb/assets/layouts/en-US.kl\"\n});\n\neventLoop.subscribe(views.dialog.input, function (_sub, button, eventLoop, gui) {\n    if (button !== \"center\")\n        return;\n\n    gui.viewDispatcher.sendTo(\"back\");\n\n    if (badusb.isConnected()) {\n        notify.blink(\"green\", \"short\");\n        print(\"USB is connected\");\n\n        badusb.println(\"Hello, world!\");\n\n        badusb.press(\"CTRL\", \"a\");\n        badusb.press(\"CTRL\", \"c\");\n        badusb.press(\"DOWN\");\n        delay(1000);\n        badusb.press(\"CTRL\", \"v\");\n        delay(1000);\n        badusb.press(\"CTRL\", \"v\");\n\n        badusb.println(\"1234\", 200);\n\n        badusb.println(\"Flipper Model: \" + flipper.getModel());\n        badusb.println(\"Flipper Name: \" + flipper.getName());\n        badusb.println(\"Battery level: \" + flipper.getBatteryCharge().toString() + \"%\");\n\n        // Alt+Numpad method works only on Windows!!!\n        badusb.altPrintln(\"This was printed with Alt+Numpad method!\");\n\n        // There's also badusb.print() and badusb.altPrint()\n        // which don't add the return at the end\n\n        notify.success();\n    } else {\n        print(\"USB not connected\");\n        notify.error();\n    }\n\n    // Optional, but allows to unlock usb interface to switch profile\n    badusb.quit();\n\n    eventLoop.stop();\n}, eventLoop, gui);\n\neventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _item, eventLoop) {\n    eventLoop.stop();\n}, eventLoop);\n\ngui.viewDispatcher.switchTo(views.dialog);\neventLoop.run();\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/console.js",
    "content": "print(\"print\", 1);\nconsole.log(\"log\", 2);\nconsole.warn(\"warn\", 3);\nconsole.error(\"error\", 4);\nconsole.debug(\"debug\", 5);\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/delay.js",
    "content": "print(\"start\");\ndelay(1000)\nprint(\"1\");\ndelay(1000)\nprint(\"2\");\ndelay(1000)\nprint(\"3\");\ndelay(1000)\nprint(\"end\");\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/event_loop.js",
    "content": "let eventLoop = require(\"event_loop\");\n\n// print a string after 1337 milliseconds\neventLoop.subscribe(eventLoop.timer(\"oneshot\", 1337), function (_subscription, _item) {\n    print(\"Hi after 1337 ms\");\n});\n\n// count up to 5 with a delay of 100ms between increments\neventLoop.subscribe(eventLoop.timer(\"periodic\", 100), function (subscription, _item, counter) {\n    print(\"Counter two:\", counter);\n    if (counter === 5)\n        subscription.cancel();\n    return [counter + 1];\n}, 0);\n\n// count up to 15 with a delay of 100ms between increments\n// and stop the program when the count reaches 15\neventLoop.subscribe(eventLoop.timer(\"periodic\", 100), function (subscription, _item, event_loop, counter) {\n    print(\"Counter one:\", counter);\n    if (counter === 15)\n        event_loop.stop();\n    return [event_loop, counter + 1];\n}, eventLoop, 0);\n\neventLoop.run();\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/gpio.js",
    "content": "let eventLoop = require(\"event_loop\");\nlet gpio = require(\"gpio\");\n\n// initialize pins\nlet led = gpio.get(\"pc3\"); // same as `gpio.get(7)`\nlet led2 = gpio.get(\"pa7\"); // same as `gpio.get(2)`\nlet pot = gpio.get(\"pc0\"); // same as `gpio.get(16)`\nlet button = gpio.get(\"pc1\"); // same as `gpio.get(15)`\nled.init({ direction: \"out\", outMode: \"push_pull\" });\npot.init({ direction: \"in\", inMode: \"analog\" });\nbutton.init({ direction: \"in\", pull: \"up\", inMode: \"interrupt\", edge: \"falling\" });\n\n// blink led\nprint(\"Commencing blinking (PC3)\");\neventLoop.subscribe(eventLoop.timer(\"periodic\", 1000), function (_, _item, led, state) {\n    led.write(state);\n    return [led, !state];\n}, led, true);\n\n// cycle led pwm\nprint(\"Commencing PWM (PA7)\");\neventLoop.subscribe(eventLoop.timer(\"periodic\", 10), function (_, _item, led2, state) {\n    led2.pwmWrite(10000, state);\n    return [led2, (state + 1) % 101];\n}, led2, 0);\n\n// read potentiometer when button is pressed\nprint(\"Press the button (PC1)\");\neventLoop.subscribe(button.interrupt(), function (_, _item, pot) {\n    print(\"PC0 is at\", pot.readAnalog(), \"mV\");\n}, pot);\n\n// the program will just exit unless this is here\neventLoop.run();\n\n// possible pins https://docs.flipper.net/gpio-and-modules#miFsS\n// \"PA7\" aka 2\n// \"PA6\" aka 3\n// \"PA4\" aka 4\n// \"PB3\" aka 5\n// \"PB2\" aka 6\n// \"PC3\" aka 7\n// \"PA14\" aka 10\n// \"PA13\" aka 12\n// \"PB6\" aka 13\n// \"PB7\" aka 14\n// \"PC1\" aka 15\n// \"PC0\" aka 16\n// \"PB14\" aka 17\n\n// possible modes\n// { direction: \"out\", outMode: \"push_pull\" }\n// { direction: \"out\", outMode: \"open_drain\" }\n// { direction: \"out\", outMode: \"push_pull\", altFn: true }\n// { direction: \"out\", outMode: \"open_drain\", altFn: true }\n// { direction: \"in\", inMode: \"analog\" }\n// { direction: \"in\", inMode: \"plain_digital\" }\n// { direction: \"in\", inMode: \"interrupt\", edge: \"rising\" }\n// { direction: \"in\", inMode: \"interrupt\", edge: \"falling\" }\n// { direction: \"in\", inMode: \"interrupt\", edge: \"both\" }\n// { direction: \"in\", inMode: \"event\", edge: \"rising\" }\n// { direction: \"in\", inMode: \"event\", edge: \"falling\" }\n// { direction: \"in\", inMode: \"event\", edge: \"both\" }\n// all variants support an optional `pull` field which can either be undefined,\n// \"up\" or \"down\"\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/gui.js",
    "content": "// import modules\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet loadingView = require(\"gui/loading\");\nlet submenuView = require(\"gui/submenu\");\nlet emptyView = require(\"gui/empty_screen\");\nlet textInputView = require(\"gui/text_input\");\nlet byteInputView = require(\"gui/byte_input\");\nlet textBoxView = require(\"gui/text_box\");\nlet dialogView = require(\"gui/dialog\");\nlet filePicker = require(\"gui/file_picker\");\nlet buttonMenuView = require(\"gui/button_menu\");\nlet buttonPanelView = require(\"gui/button_panel\");\nlet menuView = require(\"gui/menu\");\nlet numberInputView = require(\"gui/number_input\");\nlet popupView = require(\"gui/popup\");\nlet viListView = require(\"gui/vi_list\");\nlet widget = require(\"gui/widget\");\nlet icon = require(\"gui/icon\");\nlet flipper = require(\"flipper\");\nlet math = require(\"math\");\n\n// declare clock widget children\nlet cuteDolphinWithWatch = icon.getBuiltin(\"DolphinWait_59x54\");\nlet jsLogo = icon.getBuiltin(\"js_script_10px\");\nlet stopwatchWidgetElements = [\n    { element: \"string\", x: 67, y: 44, align: \"bl\", font: \"big_numbers\", text: \"00 00\" },\n    { element: \"string\", x: 77, y: 22, align: \"bl\", font: \"primary\", text: \"Stopwatch\" },\n    { element: \"rect\", x: 64, y: 27, w: 28, h: 20, radius: 3, fill: false },\n    { element: \"rect\", x: 100, y: 27, w: 28, h: 20, radius: 3, fill: false },\n    { element: \"icon\", x: 0, y: 5, iconData: cuteDolphinWithWatch },\n    { element: \"icon\", x: 64, y: 13, iconData: jsLogo },\n    { element: \"button\", button: \"right\", text: \"Back\" },\n];\n\n// icons for the button panel\nlet offIcons = [icon.getBuiltin(\"off_19x20\"), icon.getBuiltin(\"off_hover_19x20\")];\nlet powerIcons = [icon.getBuiltin(\"power_19x20\"), icon.getBuiltin(\"power_hover_19x20\")];\nlet settingsIcon = icon.getBuiltin(\"Settings_14\");\n\n// declare view instances\nlet views = {\n    loading: loadingView.make(),\n    empty: emptyView.make(),\n    keyboard: textInputView.makeWith({\n        header: \"Enter your name\",\n        minLength: 0,\n        maxLength: 32,\n        defaultText: flipper.getName(),\n        defaultTextClear: true,\n    }),\n    helloDialog: dialogView.make(),\n    bytekb: byteInputView.makeWith({\n        header: \"Look ma, I'm a header text!\",\n        length: 8,\n        defaultData: Uint8Array([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]),\n    }),\n    longText: textBoxView.makeWith({\n        text: \"This is a very long string that demonstrates the TextBox view. Use the D-Pad to scroll backwards and forwards.\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse rhoncus est malesuada quam egestas ultrices. Maecenas non eros a nulla eleifend vulputate et ut risus. Quisque in mauris mattis, venenatis risus eget, aliquam diam. Fusce pretium feugiat mauris, ut faucibus ex volutpat in. Phasellus volutpat ex sed gravida consectetur. Aliquam sed lectus feugiat, tristique lectus et, bibendum lacus. Ut sit amet augue eu sapien elementum aliquam quis vitae tortor. Vestibulum quis commodo odio. In elementum fermentum massa, eu pellentesque nibh cursus at. Integer eleifend lacus nec purus elementum sodales. Nulla elementum neque urna, non vulputate massa semper sed. Fusce ut nisi vitae dui blandit congue pretium vitae turpis.\",\n    }),\n    stopwatchWidget: widget.makeWith({}, stopwatchWidgetElements),\n    buttonMenu: buttonMenuView.makeWith({\n        header: \"Header\"\n    }, [\n        { type: \"common\", label: \"Test\" },\n        { type: \"control\", label: \"Test2\" },\n    ]),\n    buttonPanel: buttonPanelView.makeWith({\n        matrixSizeX: 2,\n        matrixSizeY: 2,\n    }, [\n        { type: \"button\", x: 0, y: 0, matrixX: 0, matrixY: 0, icon: offIcons[0], iconSelected: offIcons[1] },\n        { type: \"button\", x: 30, y: 30, matrixX: 1, matrixY: 1, icon: powerIcons[0], iconSelected: powerIcons[1] },\n        { type: \"label\", x: 0, y: 50, text: \"Label\", font: \"primary\" },\n    ]),\n    menu: menuView.makeWith({}, [\n        { label: \"One\", icon: settingsIcon },\n        { label: \"Two\", icon: settingsIcon },\n        { label: \"three\", icon: settingsIcon },\n    ]),\n    numberKbd: numberInputView.makeWith({\n        header: \"Number input\",\n        defaultValue: 100,\n        minValue: 0,\n        maxValue: 200,\n    }),\n    popup: popupView.makeWith({\n        header: \"Hello\",\n        text: \"I'm going to be gone\\nin 2 seconds\",\n    }),\n    viList: viListView.makeWith({}, [\n        { label: \"One\", variants: [\"1\", \"1.0\"] },\n        { label: \"Two\", variants: [\"2\", \"2.0\"] },\n    ]),\n    demos: submenuView.makeWith({\n        header: \"Choose a demo\",\n    }, [\n        \"Hourglass screen\",\n        \"Empty screen\",\n        \"Text input & Dialog\",\n        \"Byte input\",\n        \"Text box\",\n        \"File picker\",\n        \"Widget\",\n        \"Button menu\",\n        \"Button panel\",\n        \"Menu\",\n        \"Number input\",\n        \"Popup\",\n        \"Var. item list\",\n        \"Exit app\",\n    ]),\n};\n\n// demo selector\neventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {\n    if (index === 0) {\n        gui.viewDispatcher.switchTo(views.loading);\n        // the loading view captures all back events, preventing our navigation callback from firing\n        // switch to the demo chooser after a second\n        eventLoop.subscribe(eventLoop.timer(\"oneshot\", 1000), function (_sub, _, gui, views) {\n            gui.viewDispatcher.switchTo(views.demos);\n        }, gui, views);\n    } else if (index === 1) {\n        gui.viewDispatcher.switchTo(views.empty);\n    } else if (index === 2) {\n        gui.viewDispatcher.switchTo(views.keyboard);\n    } else if (index === 3) {\n        gui.viewDispatcher.switchTo(views.bytekb);\n    } else if (index === 4) {\n        gui.viewDispatcher.switchTo(views.longText);\n    } else if (index === 5) {\n        let path = filePicker.pickFile(\"/ext\", \"*\");\n        if (path) {\n            views.helloDialog.set(\"text\", \"You selected:\\n\" + path);\n        } else {\n            views.helloDialog.set(\"text\", \"You didn't select a file\");\n        }\n        views.helloDialog.set(\"center\", \"Nice!\");\n        gui.viewDispatcher.switchTo(views.helloDialog);\n    } else if (index === 6) {\n        gui.viewDispatcher.switchTo(views.stopwatchWidget);\n    } else if (index === 7) {\n        gui.viewDispatcher.switchTo(views.buttonMenu);\n    } else if (index === 8) {\n        gui.viewDispatcher.switchTo(views.buttonPanel);\n    } else if (index === 9) {\n        gui.viewDispatcher.switchTo(views.menu);\n    } else if (index === 10) {\n        gui.viewDispatcher.switchTo(views.numberKbd);\n    } else if (index === 11) {\n        views.popup.set(\"timeout\", 2000);\n        gui.viewDispatcher.switchTo(views.popup);\n    } else if (index === 12) {\n        gui.viewDispatcher.switchTo(views.viList);\n    } else if (index === 13) {\n        eventLoop.stop();\n    }\n}, gui, eventLoop, views);\n\n// say hi after keyboard input\neventLoop.subscribe(views.keyboard.input, function (_sub, name, gui, views) {\n    views.keyboard.set(\"defaultText\", name); // Remember for next usage\n    views.helloDialog.set(\"text\", \"Hi \" + name + \"! :)\");\n    views.helloDialog.set(\"center\", \"Hi Flipper! :)\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// go back after the greeting dialog\neventLoop.subscribe(views.helloDialog.input, function (_sub, button, gui, views) {\n    if (button === \"center\")\n        gui.viewDispatcher.switchTo(views.demos);\n}, gui, views);\n\n// show data after byte input\neventLoop.subscribe(views.bytekb.input, function (_sub, data, gui, views) {\n    let data_view = Uint8Array(data);\n    let text = \"0x\";\n    for (let i = 0; i < data_view.length; i++) {\n        text += data_view[i].toString(16);\n    }\n    views.helloDialog.set(\"text\", \"You typed:\\n\" + text);\n    views.helloDialog.set(\"center\", \"Cool!\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// go to the demo chooser screen when the back key is pressed\neventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views, eventLoop) {\n    if (gui.viewDispatcher.currentView === views.demos) {\n        eventLoop.stop();\n        return;\n    }\n    gui.viewDispatcher.switchTo(views.demos);\n}, gui, views, eventLoop);\n\n// go to the demo chooser screen when the right key is pressed on the widget screen\neventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonEvent, gui, views) {\n    if (buttonEvent.key === \"right\" && buttonEvent.type === \"short\")\n        gui.viewDispatcher.switchTo(views.demos);\n}, gui, views);\n\n// count time\neventLoop.subscribe(eventLoop.timer(\"periodic\", 500), function (_sub, _item, views, stopwatchWidgetElements, halfSeconds) {\n    let text = math.floor(halfSeconds / 2 / 60).toString();\n    if (halfSeconds < 10 * 60 * 2)\n        text = \"0\" + text;\n\n    text += (halfSeconds % 2 === 0) ? \":\" : \" \";\n\n    if (((halfSeconds / 2) % 60) < 10)\n        text += \"0\";\n    text += (math.floor(halfSeconds / 2) % 60).toString();\n\n    stopwatchWidgetElements[0].text = text;\n    views.stopwatchWidget.setChildren(stopwatchWidgetElements);\n\n    halfSeconds++;\n    return [views, stopwatchWidgetElements, halfSeconds];\n}, views, stopwatchWidgetElements, 0);\n\n// go back after popup times out\neventLoop.subscribe(views.popup.timeout, function (_sub, _item, gui, views) {\n    gui.viewDispatcher.switchTo(views.demos);\n}, gui, views);\n\n// button menu callback\neventLoop.subscribe(views.buttonMenu.input, function (_sub, input, gui, views) {\n    views.helloDialog.set(\"text\", \"You selected #\" + input.index.toString());\n    views.helloDialog.set(\"center\", \"Cool!\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// button panel callback\neventLoop.subscribe(views.buttonPanel.input, function (_sub, input, gui, views) {\n    views.helloDialog.set(\"text\", \"You selected #\" + input.index.toString());\n    views.helloDialog.set(\"center\", \"Cool!\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// menu callback\neventLoop.subscribe(views.menu.chosen, function (_sub, index, gui, views) {\n    views.helloDialog.set(\"text\", \"You selected #\" + index.toString());\n    views.helloDialog.set(\"center\", \"Cool!\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// menu callback\neventLoop.subscribe(views.numberKbd.input, function (_sub, number, gui, views) {\n    views.helloDialog.set(\"text\", \"You typed \" + number.toString());\n    views.helloDialog.set(\"center\", \"Cool!\");\n    gui.viewDispatcher.switchTo(views.helloDialog);\n}, gui, views);\n\n// ignore VI list\neventLoop.subscribe(views.viList.valueUpdate, function (_sub, _item) {});\n\n// run UI\ngui.viewDispatcher.switchTo(views.demos);\neventLoop.run();\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/interactive.js",
    "content": "let eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet dialog = require(\"gui/dialog\");\nlet textInput = require(\"gui/text_input\");\nlet loading = require(\"gui/loading\");\nlet storage = require(\"storage\");\n\n// No eval() or exec() so need to run code from file, and filename must be unique\nstorage.makeDirectory(\"/ext/.tmp\");\nstorage.makeDirectory(\"/ext/.tmp/js\");\nstorage.rmrf(\"/ext/.tmp/js/repl\")\nstorage.makeDirectory(\"/ext/.tmp/js/repl\")\nlet ctx = {\n    tmpTemplate: \"/ext/.tmp/js/repl/\",\n    tmpNumber: 0,\n    persistentScope: {},\n};\n\nlet views = {\n    dialog: dialog.makeWith({\n        header: \"Interactive Console\",\n        text: \"Press OK to Start\",\n        center: \"Run Some JS\"\n    }),\n    textInput: textInput.makeWith({\n        header: \"Type JavaScript Code:\",\n        minLength: 0,\n        maxLength: 256,\n        defaultText: \"2+2\",\n        defaultTextClear: true,\n    }),\n    loading: loading.make(),\n};\n\neventLoop.subscribe(views.dialog.input, function (_sub, button, gui, views) {\n    if (button === \"center\") {\n        gui.viewDispatcher.switchTo(views.textInput);\n    }\n}, gui, views);\n\neventLoop.subscribe(views.textInput.input, function (_sub, text, gui, views, ctx) {\n    gui.viewDispatcher.switchTo(views.loading);\n\n    let path = ctx.tmpTemplate + (ctx.tmpNumber++).toString();\n    let file = storage.openFile(path, \"w\", \"create_always\");\n    file.write(text);\n    file.close();\n\n    // Hide GUI before running, we want to see console and avoid deadlock if code fails\n    gui.viewDispatcher.sendTo(\"back\");\n    let result = load(path, ctx.persistentScope); // Load runs JS and returns last value on stack\n    storage.remove(path);\n\n    // Must convert to string explicitly\n    if (result === null) { // mJS: typeof null === \"null\", ECMAScript: typeof null === \"object\", IDE complains when checking \"null\" type\n        result = \"null\";\n    } else if (typeof result === \"string\") {\n        result = \"'\" + result + \"'\";\n    } else if (typeof result === \"number\") {\n        result = result.toString();\n    } else if (typeof result === \"bigint\") { // mJS doesn't support BigInt() but might aswell check\n        result = \"bigint\";\n    } else if (typeof result === \"boolean\") {\n        result = result ? \"true\" : \"false\";\n    } else if (typeof result === \"symbol\") { // mJS doesn't support Symbol() but might aswell check\n        result = \"symbol\";\n    } else if (typeof result === \"undefined\") {\n        result = \"undefined\";\n    } else if (typeof result === \"object\") {\n        result = \"object\"; // JSON.stringify() is not implemented\n    } else if (typeof result === \"function\") {\n        result = \"function\";\n    } else {\n        result = \"unknown type: \" + typeof result;\n    }\n\n    gui.viewDispatcher.sendTo(\"front\");\n    views.dialog.set(\"header\", \"JS Returned:\");\n    views.dialog.set(\"text\", result);\n    gui.viewDispatcher.switchTo(views.dialog);\n    views.textInput.set(\"defaultText\", text);\n}, gui, views, ctx);\n\neventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, eventLoop) {\n    eventLoop.stop();\n}, eventLoop);\n\ngui.viewDispatcher.switchTo(views.dialog);\n\n// Message behind GUI if something breaks\nprint(\"If you're stuck here, something went wrong, re-run the script\")\neventLoop.run();\nprint(\"\\n\\nFinished correctly :)\")\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/load.js",
    "content": "let math = load(__dirname + \"/load_api.js\");\nlet result = math.add(5, 10);\nprint(result);\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/load_api.js",
    "content": "({\n    add: function (a, b) { return a + b; },\n})\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/math.js",
    "content": "let math = require(\"math\");\n\nprint(\"math.abs(-5):\", math.abs(-5));\nprint(\"math.acos(0.5):\", math.acos(0.5));\nprint(\"math.acosh(2):\", math.acosh(2));\nprint(\"math.asin(0.5):\", math.asin(0.5));\nprint(\"math.asinh(2):\", math.asinh(2));\nprint(\"math.atan(1):\", math.atan(1));\nprint(\"math.atan2(1, 1):\", math.atan2(1, 1));\nprint(\"math.atanh(0.5):\", math.atanh(0.5));\nprint(\"math.cbrt(27):\", math.cbrt(27));\nprint(\"math.ceil(5.3):\", math.ceil(5.3));\nprint(\"math.clz32(1):\", math.clz32(1));\nprint(\"math.cos(math.PI):\", math.cos(math.PI));\nprint(\"math.exp(1):\", math.exp(1));\nprint(\"math.floor(5.7):\", math.floor(5.7));\nprint(\"math.max(3, 5):\", math.max(3, 5));\nprint(\"math.min(3, 5):\", math.min(3, 5));\nprint(\"math.pow(2, 3):\", math.pow(2, 3));\nprint(\"math.random():\", math.random());\nprint(\"math.sign(-5):\", math.sign(-5));\nprint(\"math.sin(math.PI/2):\", math.sin(math.PI / 2));\nprint(\"math.sqrt(25):\", math.sqrt(25));\nprint(\"math.trunc(5.7):\", math.trunc(5.7));\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/notify.js",
    "content": "let notify = require(\"notification\");\nnotify.error();\ndelay(1000);\nnotify.success();\ndelay(1000);\nfor (let i = 0; i < 10; i++) {\n    notify.blink(\"red\", \"short\");\n    delay(500);\n}\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/path.js",
    "content": "let storage = require(\"storage\");\n\nprint(\"script has __dirname of\" + __dirname);\nprint(\"script has __filename of\" + __filename);\nif (storage.fileExists(__dirname + \"/math.js\")) {\n    print(\"math.js exist here.\");\n} else {\n    print(\"math.js does not exist here.\");\n}\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/storage.js",
    "content": "let storage = require(\"storage\");\nlet path = \"/ext/storage.test\";\n\nprint(\"File exists:\", storage.fileExists(path));\n\nprint(\"Writing...\");\nlet file = storage.openFile(path, \"w\", \"create_always\");\nfile.write(\"Hello \");\nfile.close();\n\nprint(\"File exists:\", storage.fileExists(path));\n\nfile = storage.openFile(path, \"w\", \"open_append\");\nfile.write(\"World!\");\nfile.close();\n\nprint(\"Reading...\");\nfile = storage.openFile(path, \"r\", \"open_existing\");\nlet text = file.read(\"ascii\", 128);\nfile.close();\nprint(text);\n\nprint(\"Removing...\")\nstorage.remove(path);\n\nprint(\"Done\")\n\n// You don't need to close the file after each operation, this is just to show some different ways to use the API\n// There's also many more functions and options, check type definitions in firmware repo"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/stringutils.js",
    "content": "let sampleText = \"Hello, World!\";\n\nlet lengthOfText = \"Length of text: \" + sampleText.length.toString();\nprint(lengthOfText);\n\nlet start = 7;\nlet end = 12;\nlet substringResult = sampleText.slice(start, end);\nprint(substringResult);\n\nlet searchStr = \"World\";\nlet result2 = sampleText.indexOf(searchStr).toString();\nprint(result2);\n\nlet upperCaseText = \"Text in upper case: \" + sampleText.toUpperCase();\nprint(upperCaseText);\n\nlet lowerCaseText = \"Text in lower case: \" + sampleText.toLowerCase();\nprint(lowerCaseText);\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/uart_echo.js",
    "content": "let serial = require(\"serial\");\nserial.setup(\"usart\", 230400);\n\nwhile (1) {\n    let rx_data = serial.readBytes(1, 1000);\n    if (rx_data !== undefined) {\n        serial.write(rx_data);\n        let data_view = Uint8Array(rx_data);\n        print(\"0x\" + data_view[0].toString(16));\n    }\n}\n\n// There's also serial.end(), so you can serial.setup() again in same script\n// You can also use serial.readAny(timeout), will avoid starving your loop with single byte reads\n"
  },
  {
    "path": "applications/system/js_app/examples/apps/Scripts/uart_echo_8e1.js",
    "content": "// This script is like uart_echo, except it uses 8E1 framing (8 data bits, even\n// parity, 1 stop bit) as opposed to the default 8N1 (8 data bits, no parity,\n// 1 stop bit)\n\nlet serial = require(\"serial\");\nserial.setup(\"usart\", 230400, { dataBits: \"8\", parity: \"even\", stopBits: \"1\" });\n\nwhile (1) {\n    let rx_data = serial.readBytes(1, 1000);\n    if (rx_data !== undefined) {\n        serial.write(rx_data);\n        let data_view = Uint8Array(rx_data);\n        print(\"0x\" + data_view[0].toString(16));\n    }\n}\n"
  },
  {
    "path": "applications/system/js_app/js_app.c",
    "content": "#include <dialogs/dialogs.h>\n#include \"js_thread.h\"\n#include <storage/storage.h>\n#include \"js_app_i.h\"\n#include <toolbox/path.h>\n#include <assets_icons.h>\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <toolbox/pipe.h>\n\n#define TAG \"JS app\"\n\ntypedef struct {\n    JsThread* js_thread;\n    Gui* gui;\n    ViewDispatcher* view_dispatcher;\n    Loading* loading;\n    JsConsoleView* console_view;\n} JsApp;\n\nstatic uint32_t js_view_exit(void* context) {\n    UNUSED(context);\n    return VIEW_NONE;\n}\n\nstatic void js_app_compact_trace(FuriString* trace_str) {\n    // Keep only first line\n    size_t line_end = furi_string_search_char(trace_str, '\\n');\n    if(line_end > 0) {\n        furi_string_left(trace_str, line_end);\n    }\n\n    // Remove full path\n    FuriString* file_name = furi_string_alloc();\n    size_t filename_start = furi_string_search_rchar(trace_str, '/');\n    if(filename_start > 0) {\n        filename_start++;\n        furi_string_set_n(\n            file_name, trace_str, filename_start, furi_string_size(trace_str) - filename_start);\n        furi_string_printf(trace_str, \"at %s\", furi_string_get_cstr(file_name));\n    }\n\n    furi_string_free(file_name);\n}\n\nstatic void js_callback(JsThreadEvent event, const char* msg, void* context) {\n    JsApp* app = context;\n    furi_assert(app);\n\n    if(event == JsThreadEventDone) {\n        FURI_LOG_I(TAG, \"Script done\");\n        console_view_print(app->console_view, \"--- DONE ---\");\n    } else if(event == JsThreadEventPrint) {\n        console_view_print(app->console_view, msg);\n    } else if(event == JsThreadEventError) {\n        console_view_print(app->console_view, \"--- ERROR ---\");\n        console_view_print(app->console_view, msg);\n    } else if(event == JsThreadEventErrorTrace) {\n        FuriString* compact_trace = furi_string_alloc_set_str(msg);\n        js_app_compact_trace(compact_trace);\n        console_view_print(app->console_view, furi_string_get_cstr(compact_trace));\n        furi_string_free(compact_trace);\n        console_view_print(app->console_view, \"See logs for full trace\");\n    }\n}\n\nstatic JsApp* js_app_alloc(void) {\n    JsApp* app = malloc(sizeof(JsApp));\n\n    app->view_dispatcher = view_dispatcher_alloc();\n    app->loading = loading_alloc();\n\n    app->gui = furi_record_open(\"gui\");\n    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);\n    view_dispatcher_add_view(\n        app->view_dispatcher, JsAppViewLoading, loading_get_view(app->loading));\n\n    app->console_view = console_view_alloc();\n    view_dispatcher_add_view(\n        app->view_dispatcher, JsAppViewConsole, console_view_get_view(app->console_view));\n    view_set_previous_callback(console_view_get_view(app->console_view), js_view_exit);\n    view_dispatcher_switch_to_view(app->view_dispatcher, JsAppViewConsole);\n\n    return app;\n}\n\nstatic void js_app_free(JsApp* app) {\n    console_view_free(app->console_view);\n    view_dispatcher_remove_view(app->view_dispatcher, JsAppViewConsole);\n    loading_free(app->loading);\n    view_dispatcher_remove_view(app->view_dispatcher, JsAppViewLoading);\n\n    view_dispatcher_free(app->view_dispatcher);\n    furi_record_close(\"gui\");\n\n    free(app);\n}\n\nint32_t js_app(void* arg) {\n    JsApp* app = js_app_alloc();\n\n    FuriString* script_path = furi_string_alloc_set(EXT_PATH(\"apps/Scripts\"));\n    do {\n        if(arg != NULL && strlen(arg) > 0) {\n            furi_string_set(script_path, (const char*)arg);\n        } else {\n            DialogsFileBrowserOptions browser_options;\n            dialog_file_browser_set_basic_options(&browser_options, \".js\", &I_js_script_10px);\n            DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n            if(!dialog_file_browser_show(dialogs, script_path, script_path, &browser_options))\n                break;\n            furi_record_close(RECORD_DIALOGS);\n        }\n        FuriString* name = furi_string_alloc();\n        path_extract_filename(script_path, name, false);\n        FuriString* start_text =\n            furi_string_alloc_printf(\"Running %s\", furi_string_get_cstr(name));\n        console_view_print(app->console_view, furi_string_get_cstr(start_text));\n        console_view_print(app->console_view, \"-------------\");\n        furi_string_free(name);\n        furi_string_free(start_text);\n\n        app->js_thread = js_thread_run(furi_string_get_cstr(script_path), js_callback, app);\n        view_dispatcher_run(app->view_dispatcher);\n\n        js_thread_stop(app->js_thread);\n    } while(0);\n\n    furi_string_free(script_path);\n\n    js_app_free(app);\n    return 0;\n} //-V773\n\ntypedef struct {\n    PipeSide* pipe;\n    FuriSemaphore* exit_sem;\n} JsCliContext;\n\nstatic void js_cli_print(JsCliContext* ctx, const char* msg) {\n    UNUSED(ctx);\n    UNUSED(msg);\n    pipe_send(ctx->pipe, msg, strlen(msg));\n}\n\nstatic void js_cli_exit(JsCliContext* ctx) {\n    furi_check(furi_semaphore_release(ctx->exit_sem) == FuriStatusOk);\n}\n\nstatic void js_cli_callback(JsThreadEvent event, const char* msg, void* context) {\n    JsCliContext* ctx = context;\n    switch(event) {\n    case JsThreadEventError:\n        js_cli_print(ctx, \"---- ERROR ----\\r\\n\");\n        js_cli_print(ctx, msg);\n        js_cli_print(ctx, \"\\r\\n\");\n        break;\n    case JsThreadEventErrorTrace:\n        js_cli_print(ctx, \"Trace:\\r\\n\");\n        js_cli_print(ctx, msg);\n        js_cli_print(ctx, \"\\r\\n\");\n\n        js_cli_exit(ctx); // Exit when an error occurs\n        break;\n    case JsThreadEventPrint:\n        js_cli_print(ctx, msg);\n        js_cli_print(ctx, \"\\r\\n\");\n        break;\n    case JsThreadEventDone:\n        js_cli_print(ctx, \"Script done!\\r\\n\");\n\n        js_cli_exit(ctx);\n        break;\n    }\n}\n\nvoid js_cli_execute(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(context);\n\n    const char* path = furi_string_get_cstr(args);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    do {\n        if(furi_string_size(args) == 0) {\n            printf(\"Usage:\\r\\njs <path>\\r\\n\");\n            break;\n        }\n\n        if(!storage_file_exists(storage, path)) {\n            printf(\"Can not open file %s\\r\\n\", path);\n            break;\n        }\n\n        JsCliContext ctx = {.pipe = pipe};\n        ctx.exit_sem = furi_semaphore_alloc(1, 0);\n\n        printf(\"Running script %s, press CTRL+C to stop\\r\\n\", path);\n        JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx);\n\n        while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) {\n            if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;\n        }\n\n        js_thread_stop(js_thread);\n        furi_semaphore_free(ctx.exit_sem);\n    } while(false);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nvoid js_app_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(registry, \"js\", CliCommandFlagDefault, js_cli_execute, NULL);\n    furi_record_close(RECORD_CLI);\n#endif\n}\n"
  },
  {
    "path": "applications/system/js_app/js_app_i.h",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/loading.h>\n#include \"views/console_view.h\"\n\ntypedef enum {\n    JsAppViewConsole,\n    JsAppViewLoading,\n} JsAppView;\n"
  },
  {
    "path": "applications/system/js_app/js_modules.c",
    "content": "#include <core/common_defines.h>\n#include \"js_modules.h\"\n#include <m-array.h>\n#include <dialogs/dialogs.h>\n#include <assets_icons.h>\n\n#include \"modules/js_flipper.h\"\n#ifdef FW_CFG_unit_tests\n#include \"modules/js_tests.h\"\n#endif\n\n#define TAG \"JS modules\"\n\n// Absolute path is used to make possible plugin load from CLI\n#define MODULES_PATH \"/ext/apps_data/js_app/plugins\"\n\ntypedef struct {\n    FuriString* name;\n    const JsModuleConstructor create;\n    const JsModuleDestructor destroy;\n    void* context;\n} JsModuleData;\n\n// not using:\n//   - a dict because ordering is required\n//   - a bptree because it forces a sorted ordering\n//   - an rbtree because i deemed it more tedious to implement, and with the\n//     amount of modules in use (under 10 in the overwhelming majority of cases)\n//     i bet it's going to be slower than a plain array\nARRAY_DEF(JsModuleArray, JsModuleData, M_POD_OPLIST); //-V658\n#define M_OPL_JsModuleArray_t() ARRAY_OPLIST(JsModuleArray)\n\nstatic const JsModuleDescriptor modules_builtin[] = {\n    {\"flipper\", js_flipper_create, NULL, NULL},\n#ifdef FW_CFG_unit_tests\n    {\"tests\", js_tests_create, NULL, NULL},\n#endif\n};\n\nstruct JsModules {\n    struct mjs* mjs;\n    JsModuleArray_t modules;\n    PluginManager* plugin_manager;\n    CompositeApiResolver* resolver;\n};\n\nJsModules* js_modules_create(struct mjs* mjs, CompositeApiResolver* resolver) {\n    JsModules* modules = malloc(sizeof(JsModules));\n    modules->mjs = mjs;\n    JsModuleArray_init(modules->modules);\n\n    modules->plugin_manager = plugin_manager_alloc(\n        PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver));\n\n    modules->resolver = resolver;\n\n    return modules;\n}\n\nvoid js_modules_destroy(JsModules* instance) {\n    for\n        M_EACH(module, instance->modules, JsModuleArray_t) {\n            FURI_LOG_T(TAG, \"Tearing down %s\", furi_string_get_cstr(module->name));\n            if(module->destroy) module->destroy(module->context);\n            furi_string_free(module->name);\n        }\n    plugin_manager_free(instance->plugin_manager);\n    JsModuleArray_clear(instance->modules);\n    free(instance);\n}\n\nJsModuleData* js_find_loaded_module(JsModules* instance, const char* name) {\n    for\n        M_EACH(module, instance->modules, JsModuleArray_t) {\n            if(furi_string_cmp_str(module->name, name) == 0) return module;\n        }\n    return NULL;\n}\n\nmjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_len) {\n    // Ignore the initial part of the module name\n    const char* optional_module_prefix = \"@\" JS_SDK_VENDOR \"/fz-sdk/\";\n    if(strncmp(name, optional_module_prefix, strlen(optional_module_prefix)) == 0) {\n        name += strlen(optional_module_prefix);\n    }\n\n    // Check if module is already installed\n    JsModuleData* module_inst = js_find_loaded_module(modules, name);\n    if(module_inst) { //-V547\n        mjs_prepend_errorf(\n            modules->mjs, MJS_BAD_ARGS_ERROR, \"\\\"%s\\\" module is already installed\", name);\n        return MJS_UNDEFINED;\n    }\n\n    bool module_found = false;\n    // Check built-in modules\n    for(size_t i = 0; i < COUNT_OF(modules_builtin); i++) { //-V1008\n        size_t name_compare_len = strlen(modules_builtin[i].name);\n\n        if(name_compare_len != name_len) {\n            continue;\n        }\n\n        if(strncmp(name, modules_builtin[i].name, name_compare_len) == 0) {\n            JsModuleData module = {\n                .create = modules_builtin[i].create,\n                .destroy = modules_builtin[i].destroy,\n                .name = furi_string_alloc_set_str(name),\n            };\n            JsModuleArray_push_at(modules->modules, 0, module);\n            module_found = true;\n            FURI_LOG_I(TAG, \"Using built-in module %s\", name);\n            break;\n        }\n    }\n\n    // External module load\n    if(!module_found) {\n        FuriString* deslashed_name = furi_string_alloc_set_str(name);\n        furi_string_replace_all_str(deslashed_name, \"/\", \"__\");\n        FuriString* module_path = furi_string_alloc();\n        furi_string_printf(\n            module_path, \"%s/js_%s.fal\", MODULES_PATH, furi_string_get_cstr(deslashed_name));\n        FURI_LOG_I(\n            TAG, \"Loading external module %s from %s\", name, furi_string_get_cstr(module_path));\n        do {\n            uint32_t plugin_cnt_last = plugin_manager_get_count(modules->plugin_manager);\n            PluginManagerError load_error = plugin_manager_load_single(\n                modules->plugin_manager, furi_string_get_cstr(module_path));\n            if(load_error != PluginManagerErrorNone) {\n                FURI_LOG_E(\n                    TAG,\n                    \"Module %s load error. It may depend on other modules that are not yet loaded.\",\n                    name);\n                break;\n            }\n            const JsModuleDescriptor* plugin =\n                plugin_manager_get_ep(modules->plugin_manager, plugin_cnt_last);\n            furi_assert(plugin);\n\n            if(furi_string_cmp_str(deslashed_name, plugin->name) != 0) {\n                FURI_LOG_E(TAG, \"Module name mismatch %s\", plugin->name);\n                break;\n            }\n            JsModuleData module = {\n                .create = plugin->create,\n                .destroy = plugin->destroy,\n                .name = furi_string_alloc_set_str(name),\n            };\n            JsModuleArray_push_at(modules->modules, 0, module);\n\n            if(plugin->api_interface) {\n                FURI_LOG_I(TAG, \"Added module API to composite resolver: %s\", plugin->name);\n                composite_api_resolver_add(modules->resolver, plugin->api_interface);\n            }\n\n            module_found = true;\n        } while(0);\n        furi_string_free(module_path);\n        furi_string_free(deslashed_name);\n    }\n\n    // Run module constructor\n    mjs_val_t module_object = MJS_UNDEFINED;\n    if(module_found) {\n        module_inst = js_find_loaded_module(modules, name);\n        furi_assert(module_inst);\n        if(module_inst->create) { //-V779\n            module_inst->context = module_inst->create(modules->mjs, &module_object, modules);\n        }\n    }\n\n    if(module_object == MJS_UNDEFINED) { //-V547\n        mjs_prepend_errorf(modules->mjs, MJS_BAD_ARGS_ERROR, \"\\\"%s\\\" module load fail\", name);\n    }\n\n    return module_object;\n}\n\nvoid* js_module_get(JsModules* modules, const char* name) {\n    FuriString* module_name = furi_string_alloc_set_str(name);\n    JsModuleData* module_inst = js_find_loaded_module(modules, name);\n    furi_string_free(module_name);\n    return module_inst ? module_inst->context : NULL;\n}\n\ntypedef enum {\n    JsSdkCompatStatusCompatible,\n    JsSdkCompatStatusFirmwareTooOld,\n    JsSdkCompatStatusFirmwareTooNew,\n} JsSdkCompatStatus;\n\n/**\n * @brief Checks compatibility between the firmware and the JS SDK version\n *        expected by the script\n */\nstatic JsSdkCompatStatus\n    js_internal_sdk_compatibility_status(int32_t exp_major, int32_t exp_minor) {\n    if(exp_major < JS_SDK_MAJOR) return JsSdkCompatStatusFirmwareTooNew;\n    if(exp_major > JS_SDK_MAJOR || exp_minor > JS_SDK_MINOR)\n        return JsSdkCompatStatusFirmwareTooOld;\n    return JsSdkCompatStatusCompatible;\n}\n\nstatic const JsValueDeclaration js_sdk_version_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeInt32),\n    JS_VALUE_SIMPLE(JsValueTypeInt32),\n};\nstatic const JsValueArguments js_sdk_version_args = JS_VALUE_ARGS(js_sdk_version_arg_list);\n\nvoid js_sdk_compatibility_status(struct mjs* mjs) {\n    int32_t major, minor;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);\n    JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);\n    switch(status) {\n    case JsSdkCompatStatusCompatible:\n        mjs_return(mjs, mjs_mk_string(mjs, \"compatible\", ~0, 0));\n        return;\n    case JsSdkCompatStatusFirmwareTooOld:\n        mjs_return(mjs, mjs_mk_string(mjs, \"firmwareTooOld\", ~0, 0));\n        return;\n    case JsSdkCompatStatusFirmwareTooNew:\n        mjs_return(mjs, mjs_mk_string(mjs, \"firmwareTooNew\", ~0, 0));\n        return;\n    }\n}\n\nvoid js_is_sdk_compatible(struct mjs* mjs) {\n    int32_t major, minor;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);\n    JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);\n    mjs_return(mjs, mjs_mk_boolean(mjs, status == JsSdkCompatStatusCompatible));\n}\n\n/**\n * @brief Asks the user whether to continue executing an incompatible script\n */\nstatic bool js_internal_compat_ask_user(const char* message) {\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n    DialogMessage* dialog = dialog_message_alloc();\n    dialog_message_set_header(dialog, message, 64, 0, AlignCenter, AlignTop);\n    dialog_message_set_text(\n        dialog, \"This script may not\\nwork as expected\", 79, 32, AlignCenter, AlignCenter);\n    dialog_message_set_icon(dialog, &I_Warning_30x23, 0, 18);\n    dialog_message_set_buttons(dialog, \"Go back\", NULL, \"Run anyway\");\n    DialogMessageButton choice = dialog_message_show(dialogs, dialog);\n    dialog_message_free(dialog);\n    furi_record_close(RECORD_DIALOGS);\n    return choice == DialogMessageButtonRight;\n}\n\nvoid js_check_sdk_compatibility(struct mjs* mjs) {\n    int32_t major, minor;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);\n    JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);\n    if(status != JsSdkCompatStatusCompatible) {\n        FURI_LOG_E(\n            TAG,\n            \"Script requests JS SDK %ld.%ld, firmware provides JS SDK %d.%d\",\n            major,\n            minor,\n            JS_SDK_MAJOR,\n            JS_SDK_MINOR);\n\n        const char* message = (status == JsSdkCompatStatusFirmwareTooOld) ? \"Outdated Firmware\" :\n                                                                            \"Outdated Script\";\n        if(!js_internal_compat_ask_user(message)) {\n            JS_ERROR_AND_RETURN(mjs, MJS_NOT_IMPLEMENTED_ERROR, \"Incompatible script\");\n        }\n    }\n}\n\nstatic const char* extra_features[] = {\n    \"baseline\", // dummy \"feature\"\n};\n\n/**\n * @brief Determines whether a feature is supported\n */\nstatic bool js_internal_supports(const char* feature) {\n    for(size_t i = 0; i < COUNT_OF(extra_features); i++) { // -V1008\n        if(strcmp(feature, extra_features[i]) == 0) return true;\n    }\n    return false;\n}\n\n/**\n * @brief Determines whether all of the requested features are supported\n */\nstatic bool js_internal_supports_all_of(struct mjs* mjs, mjs_val_t feature_arr) {\n    furi_assert(mjs_is_array(feature_arr));\n\n    for(size_t i = 0; i < mjs_array_length(mjs, feature_arr); i++) {\n        mjs_val_t feature = mjs_array_get(mjs, feature_arr, i);\n        const char* feature_str = mjs_get_string(mjs, &feature, NULL);\n        if(!feature_str) return false;\n\n        if(!js_internal_supports(feature_str)) return false;\n    }\n\n    return true;\n}\n\nstatic const JsValueDeclaration js_sdk_features_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeAnyArray),\n};\nstatic const JsValueArguments js_sdk_features_args = JS_VALUE_ARGS(js_sdk_features_arg_list);\n\nvoid js_does_sdk_support(struct mjs* mjs) {\n    mjs_val_t features;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);\n    mjs_return(mjs, mjs_mk_boolean(mjs, js_internal_supports_all_of(mjs, features)));\n}\n\nvoid js_check_sdk_features(struct mjs* mjs) {\n    mjs_val_t features;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);\n    if(!js_internal_supports_all_of(mjs, features)) {\n        FURI_LOG_E(TAG, \"Script requests unsupported features\");\n\n        if(!js_internal_compat_ask_user(\"Unsupported Feature\")) {\n            JS_ERROR_AND_RETURN(mjs, MJS_NOT_IMPLEMENTED_ERROR, \"Incompatible script\");\n        }\n    }\n}\n"
  },
  {
    "path": "applications/system/js_app/js_modules.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include \"js_thread_i.h\"\n#include \"js_value.h\"\n#include <flipper_application/flipper_application.h>\n#include <flipper_application/plugins/plugin_manager.h>\n#include <flipper_application/plugins/composite_resolver.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PLUGIN_APP_ID      \"js\"\n#define PLUGIN_API_VERSION 1\n\n#define JS_SDK_VENDOR \"flipperdevices\"\n#define JS_SDK_MAJOR  1\n#define JS_SDK_MINOR  0\n\n/**\n * @brief Returns the foreign pointer in `obj[\"_\"]`\n */\n#define JS_GET_INST(mjs, obj) mjs_get_ptr(mjs, mjs_get(mjs, obj, INST_PROP_NAME, ~0))\n/**\n * @brief Returns the foreign pointer in `this[\"_\"]`\n */\n#define JS_GET_CONTEXT(mjs)   JS_GET_INST(mjs, mjs_get_this(mjs))\n\n/**\n * @brief Syntax sugar for constructing an object\n * \n * Example:\n *\n *  mjs_val_t my_obj = mjs_mk_object(mjs);\n *  JS_ASSIGN_MULTI(mjs, my_obj) {\n *      JS_FIELD(\"method1\", MJS_MK_FN(js_storage_file_is_open));\n *      JS_FIELD(\"method2\", MJS_MK_FN(js_storage_file_is_open));\n *  }\n */\n#define JS_ASSIGN_MULTI(mjs, object)     \\\n    for(struct {                         \\\n            struct mjs* mjs;             \\\n            mjs_val_t val;               \\\n            int i;                       \\\n        } _ass_multi = {mjs, object, 0}; \\\n        _ass_multi.i == 0;               \\\n        _ass_multi.i++)\n#define JS_FIELD(name, value) mjs_set(_ass_multi.mjs, _ass_multi.val, name, ~0, value)\n\n/**\n * @brief The first word of structures that foreign pointer JS values point to\n * \n * This is used to detect situations where JS code mistakenly passes an opaque\n * foreign pointer of one type as an argument to a native function which expects\n * a struct of another type.\n * \n * It is recommended to use this functionality in conjunction with the following\n * convenience verification macros:\n *   - `JS_ARG_STRUCT()`\n *   - `JS_ARG_OBJ_WITH_STRUCT()`\n * \n * @warning In order for the mechanism to work properly, your struct must store\n * the magic value in the first word.\n */\ntypedef enum {\n    JsForeignMagicStart = 0x15BAD000,\n    JsForeignMagic_JsEventLoopContract,\n} JsForeignMagic;\n\n/**\n * @brief Prepends an error, sets the JS return value to `undefined` and returns\n * from the C function\n * @warning This macro executes `return;` by design\n */\n#define JS_ERROR_AND_RETURN(mjs, error_code, ...)         \\\n    do {                                                  \\\n        mjs_prepend_errorf(mjs, error_code, __VA_ARGS__); \\\n        mjs_return(mjs, MJS_UNDEFINED);                   \\\n        return;                                           \\\n    } while(0)\n\n/**\n * @brief Prepends an error, sets the JS return value to `undefined` and returns\n * a value C function\n * @warning This macro executes `return;` by design\n */\n#define JS_ERROR_AND_RETURN_VAL(mjs, error_code, ret_val, ...) \\\n    do {                                                       \\\n        mjs_prepend_errorf(mjs, error_code, __VA_ARGS__);      \\\n        mjs_return(mjs, MJS_UNDEFINED);                        \\\n        return ret_val;                                        \\\n    } while(0)\n\ntypedef struct JsModules JsModules;\n\ntypedef void* (*JsModuleConstructor)(struct mjs* mjs, mjs_val_t* object, JsModules* modules);\ntypedef void (*JsModuleDestructor)(void* inst);\n\ntypedef struct {\n    char* name;\n    JsModuleConstructor create;\n    JsModuleDestructor destroy;\n    const ElfApiInterface* api_interface;\n} JsModuleDescriptor;\n\nJsModules* js_modules_create(struct mjs* mjs, CompositeApiResolver* resolver);\n\nvoid js_modules_destroy(JsModules* modules);\n\nmjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_len);\n\n/**\n * @brief Gets a module instance by its name\n * This is useful when a module wants to access a stateful API of another\n * module.\n * @returns Pointer to module context, NULL if the module is not instantiated\n */\nvoid* js_module_get(JsModules* modules, const char* name);\n\n/**\n * @brief `sdkCompatibilityStatus` function\n */\nvoid js_sdk_compatibility_status(struct mjs* mjs);\n\n/**\n * @brief `isSdkCompatible` function\n */\nvoid js_is_sdk_compatible(struct mjs* mjs);\n\n/**\n * @brief `checkSdkCompatibility` function\n */\nvoid js_check_sdk_compatibility(struct mjs* mjs);\n\n/**\n * @brief `doesSdkSupport` function\n */\nvoid js_does_sdk_support(struct mjs* mjs);\n\n/**\n * @brief `checkSdkFeatures` function\n */\nvoid js_check_sdk_features(struct mjs* mjs);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/js_thread.c",
    "content": "#include <common/cs_dbg.h>\n#include <toolbox/path.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/strint.h>\n#include <loader/firmware_api/firmware_api.h>\n#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/plugins/composite_resolver.h>\n#include <furi_hal.h>\n#include \"plugin_api/app_api_interface.h\"\n#include \"js_thread.h\"\n#include \"js_thread_i.h\"\n#include \"js_modules.h\"\n\n#define TAG \"JS\"\n\nstruct JsThread {\n    FuriThread* thread;\n    FuriString* path;\n    CompositeApiResolver* resolver;\n    JsThreadCallback app_callback;\n    void* context;\n    JsModules* modules;\n};\n\nstatic void js_str_print(FuriString* msg_str, struct mjs* mjs) {\n    size_t num_args = mjs_nargs(mjs);\n    for(size_t i = 0; i < num_args; i++) {\n        char* name = NULL;\n        size_t name_len = 0;\n        int need_free = 0;\n        mjs_val_t arg = mjs_arg(mjs, i);\n        mjs_err_t err = mjs_to_string(mjs, &arg, &name, &name_len, &need_free);\n        if(err != MJS_OK) {\n            furi_string_cat_printf(msg_str, \"err %s \", mjs_strerror(mjs, err));\n        } else {\n            furi_string_cat_printf(msg_str, \"%s \", name);\n        }\n        if(need_free) {\n            free(name);\n            name = NULL;\n        }\n    }\n}\n\nstatic void js_print(struct mjs* mjs) {\n    FuriString* msg_str = furi_string_alloc();\n    js_str_print(msg_str, mjs);\n\n    JsThread* worker = mjs_get_context(mjs);\n    furi_assert(worker);\n    if(worker->app_callback) {\n        worker->app_callback(JsThreadEventPrint, furi_string_get_cstr(msg_str), worker->context);\n    } else {\n        FURI_LOG_D(TAG, \"%s\\r\\n\", furi_string_get_cstr(msg_str));\n    }\n\n    furi_string_free(msg_str);\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_console_log(struct mjs* mjs) {\n    FuriString* msg_str = furi_string_alloc();\n    js_str_print(msg_str, mjs);\n    FURI_LOG_I(TAG, \"%s\", furi_string_get_cstr(msg_str));\n    furi_string_free(msg_str);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_console_warn(struct mjs* mjs) {\n    FuriString* msg_str = furi_string_alloc();\n    js_str_print(msg_str, mjs);\n    FURI_LOG_W(TAG, \"%s\", furi_string_get_cstr(msg_str));\n    furi_string_free(msg_str);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_console_error(struct mjs* mjs) {\n    FuriString* msg_str = furi_string_alloc();\n    js_str_print(msg_str, mjs);\n    FURI_LOG_E(TAG, \"%s\", furi_string_get_cstr(msg_str));\n    furi_string_free(msg_str);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_console_debug(struct mjs* mjs) {\n    FuriString* msg_str = furi_string_alloc();\n    js_str_print(msg_str, mjs);\n    FURI_LOG_D(TAG, \"%s\", furi_string_get_cstr(msg_str));\n    furi_string_free(msg_str);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_exit_flag_poll(struct mjs* mjs) {\n    uint32_t flags = furi_thread_flags_wait(ThreadEventStop, FuriFlagWaitAny | FuriFlagNoClear, 0);\n    if(flags & FuriFlagError) {\n        return;\n    }\n    if(flags & ThreadEventStop) {\n        mjs_exit(mjs);\n    }\n}\n\nbool js_delay_with_flags(struct mjs* mjs, uint32_t time) {\n    uint32_t flags =\n        furi_thread_flags_wait(ThreadEventStop, FuriFlagWaitAny | FuriFlagNoClear, time);\n    if(flags & FuriFlagError) {\n        return false;\n    }\n    if(flags & ThreadEventStop) {\n        mjs_exit(mjs);\n        return true;\n    }\n    return false;\n}\n\nvoid js_flags_set(struct mjs* mjs, uint32_t flags) {\n    JsThread* worker = mjs_get_context(mjs);\n    furi_assert(worker);\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), flags);\n}\n\nuint32_t js_flags_wait(struct mjs* mjs, uint32_t flags_mask, uint32_t timeout) {\n    flags_mask |= ThreadEventStop;\n    uint32_t flags = furi_thread_flags_get();\n    furi_check((flags & FuriFlagError) == 0);\n    if(flags == 0) {\n        flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny | FuriFlagNoClear, timeout);\n    } else {\n        uint32_t state = furi_thread_flags_clear(flags & flags_mask);\n        furi_check((state & FuriFlagError) == 0);\n    }\n\n    if(flags & FuriFlagError) {\n        return 0;\n    }\n    if(flags & ThreadEventStop) {\n        mjs_exit(mjs);\n    }\n    return flags;\n}\n\nstatic void js_delay(struct mjs* mjs) {\n    bool args_correct = false;\n    int ms = 0;\n\n    if(mjs_nargs(mjs) == 1) {\n        mjs_val_t arg = mjs_arg(mjs, 0);\n        if(mjs_is_number(arg)) {\n            ms = mjs_get_int(mjs, arg);\n            args_correct = true;\n        }\n    }\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    js_delay_with_flags(mjs, ms);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void* js_dlsym(void* handle, const char* name) {\n    CompositeApiResolver* resolver = handle;\n    Elf32_Addr addr = 0;\n    uint32_t hash = elf_symbolname_hash(name);\n    const ElfApiInterface* api = composite_api_resolver_get(resolver);\n\n    if(!api->resolver_callback(api, hash, &addr)) {\n        FURI_LOG_E(TAG, \"FFI: cannot find \\\"%s\\\"\", name);\n        return NULL;\n    }\n\n    return (void*)addr;\n}\n\nstatic void js_ffi_address(struct mjs* mjs) {\n    mjs_val_t name_v = mjs_arg(mjs, 0);\n    size_t len;\n    const char* name = mjs_get_string(mjs, &name_v, &len);\n    void* addr = mjs_ffi_resolve(mjs, name);\n    mjs_return(mjs, mjs_mk_foreign(mjs, addr));\n}\n\nstatic void js_require(struct mjs* mjs) {\n    mjs_val_t name_v = mjs_arg(mjs, 0);\n    size_t len;\n    const char* name = mjs_get_string(mjs, &name_v, &len);\n    mjs_val_t req_object = MJS_UNDEFINED;\n    if((len == 0) || (name == NULL)) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"String argument is expected\");\n    } else {\n        JsThread* worker = mjs_get_context(mjs);\n        furi_assert(worker);\n        req_object = js_module_require(worker->modules, name, len);\n    }\n    mjs_return(mjs, req_object);\n}\n\nstatic void js_parse_int(struct mjs* mjs) {\n    static const JsValueDeclaration js_parse_int_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 10),\n    };\n    static const JsValueArguments js_parse_int_args = JS_VALUE_ARGS(js_parse_int_arg_list);\n\n    const char* str;\n    int32_t base;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_parse_int_args, &str, &base);\n\n    int32_t num;\n    if(strint_to_int32(str, NULL, &num, base) != StrintParseNoError) {\n        num = 0;\n    }\n    mjs_return(mjs, mjs_mk_number(mjs, num));\n}\n\n#ifdef JS_DEBUG\nstatic void js_dump_write_callback(void* ctx, const char* format, ...) {\n    File* file = ctx;\n    furi_assert(ctx);\n\n    FuriString* str = furi_string_alloc();\n\n    va_list args;\n    va_start(args, format);\n    furi_string_vprintf(str, format, args);\n    furi_string_cat(str, \"\\n\");\n    va_end(args);\n\n    storage_file_write(file, furi_string_get_cstr(str), furi_string_size(str));\n    furi_string_free(str);\n}\n#endif\n\nstatic int32_t js_thread(void* arg) {\n    JsThread* worker = arg;\n    worker->resolver = composite_api_resolver_alloc();\n    composite_api_resolver_add(worker->resolver, firmware_api_interface);\n    composite_api_resolver_add(worker->resolver, application_api_interface);\n\n    struct mjs* mjs = mjs_create(worker);\n    worker->modules = js_modules_create(mjs, worker->resolver);\n    mjs_val_t global = mjs_get_global(mjs);\n    mjs_val_t console_obj = mjs_mk_object(mjs);\n\n    if(worker->path) {\n        FuriString* dirpath = furi_string_alloc();\n        path_extract_dirname(furi_string_get_cstr(worker->path), dirpath);\n        mjs_set(\n            mjs,\n            global,\n            \"__filename\",\n            ~0,\n            mjs_mk_string(\n                mjs, furi_string_get_cstr(worker->path), furi_string_size(worker->path), true));\n        mjs_set(\n            mjs,\n            global,\n            \"__dirname\",\n            ~0,\n            mjs_mk_string(mjs, furi_string_get_cstr(dirpath), furi_string_size(dirpath), true));\n        furi_string_free(dirpath);\n    }\n\n    JS_ASSIGN_MULTI(mjs, global) {\n        JS_FIELD(\"print\", MJS_MK_FN(js_print));\n        JS_FIELD(\"delay\", MJS_MK_FN(js_delay));\n        JS_FIELD(\"parseInt\", MJS_MK_FN(js_parse_int));\n        JS_FIELD(\"ffi_address\", MJS_MK_FN(js_ffi_address));\n        JS_FIELD(\"require\", MJS_MK_FN(js_require));\n        JS_FIELD(\"console\", console_obj);\n\n        JS_FIELD(\"sdkCompatibilityStatus\", MJS_MK_FN(js_sdk_compatibility_status));\n        JS_FIELD(\"isSdkCompatible\", MJS_MK_FN(js_is_sdk_compatible));\n        JS_FIELD(\"checkSdkCompatibility\", MJS_MK_FN(js_check_sdk_compatibility));\n        JS_FIELD(\"doesSdkSupport\", MJS_MK_FN(js_does_sdk_support));\n        JS_FIELD(\"checkSdkFeatures\", MJS_MK_FN(js_check_sdk_features));\n    }\n\n    JS_ASSIGN_MULTI(mjs, console_obj) {\n        JS_FIELD(\"log\", MJS_MK_FN(js_console_log));\n        JS_FIELD(\"warn\", MJS_MK_FN(js_console_warn));\n        JS_FIELD(\"error\", MJS_MK_FN(js_console_error));\n        JS_FIELD(\"debug\", MJS_MK_FN(js_console_debug));\n    }\n\n    mjs_set_ffi_resolver(mjs, js_dlsym, worker->resolver);\n\n    mjs_set_exec_flags_poller(mjs, js_exit_flag_poll);\n\n    mjs_err_t err = mjs_exec_file(mjs, furi_string_get_cstr(worker->path), NULL);\n\n#ifdef JS_DEBUG\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {\n        FuriString* dump_path = furi_string_alloc_set(worker->path);\n        furi_string_cat(dump_path, \".lst\");\n\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n        File* file = storage_file_alloc(storage);\n\n        if(storage_file_open(\n               file, furi_string_get_cstr(dump_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n            mjs_disasm_all(mjs, js_dump_write_callback, file);\n        }\n\n        storage_file_close(file);\n        storage_file_free(file);\n        furi_record_close(RECORD_STORAGE);\n\n        furi_string_free(dump_path);\n    }\n#endif\n\n    if(err != MJS_OK) {\n        FURI_LOG_E(TAG, \"Exec error: %s\", mjs_strerror(mjs, err));\n        if(worker->app_callback) {\n            worker->app_callback(JsThreadEventError, mjs_strerror(mjs, err), worker->context);\n        }\n        const char* stack_trace = mjs_get_stack_trace(mjs);\n        if(stack_trace != NULL) {\n            FURI_LOG_E(TAG, \"Stack trace:\\r\\n%s\", stack_trace);\n            if(worker->app_callback) {\n                worker->app_callback(JsThreadEventErrorTrace, stack_trace, worker->context);\n            }\n        }\n    } else {\n        if(worker->app_callback) {\n            worker->app_callback(JsThreadEventDone, NULL, worker->context);\n        }\n    }\n\n    mjs_destroy(mjs);\n    js_modules_destroy(worker->modules);\n\n    composite_api_resolver_free(worker->resolver);\n\n    return 0;\n}\n\nJsThread* js_thread_run(const char* script_path, JsThreadCallback callback, void* context) {\n    JsThread* worker = malloc(sizeof(JsThread)); //-V799\n    worker->path = furi_string_alloc_set(script_path);\n    worker->thread = furi_thread_alloc_ex(\"JsThread\", 8 * 1024, js_thread, worker);\n    worker->app_callback = callback;\n    worker->context = context;\n    furi_thread_start(worker->thread);\n    return worker;\n}\n\nvoid js_thread_stop(JsThread* worker) {\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), ThreadEventStop);\n    furi_thread_join(worker->thread);\n    furi_thread_free(worker->thread);\n    furi_string_free(worker->path);\n    free(worker);\n}\n"
  },
  {
    "path": "applications/system/js_app/js_thread.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct JsThread JsThread;\n\ntypedef enum {\n    JsThreadEventDone,\n    JsThreadEventError,\n    JsThreadEventPrint,\n    JsThreadEventErrorTrace,\n} JsThreadEvent;\n\ntypedef void (*JsThreadCallback)(JsThreadEvent event, const char* msg, void* context);\n\nJsThread* js_thread_run(const char* script_path, JsThreadCallback callback, void* context);\n\nvoid js_thread_stop(JsThread* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/js_thread_i.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <mjs_core_public.h>\n#include <mjs_ffi_public.h>\n#include <mjs_exec_public.h>\n#include <mjs_object_public.h>\n#include <mjs_string_public.h>\n#include <mjs_array_public.h>\n#include <mjs_util_public.h>\n#include <mjs_primitive_public.h>\n#include <mjs_array_buf_public.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define INST_PROP_NAME \"_\"\n\ntypedef enum {\n    ThreadEventStop = (1 << 0),\n    ThreadEventCustomDataRx = (1 << 1),\n} WorkerEventFlags;\n\nbool js_delay_with_flags(struct mjs* mjs, uint32_t time);\n\nvoid js_flags_set(struct mjs* mjs, uint32_t flags);\n\nuint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/js_value.c",
    "content": "#include \"js_value.h\"\n#include <stdarg.h>\n\n#ifdef APP_UNIT_TESTS\n#define JS_VAL_DEBUG\n#endif\n\nsize_t js_value_buffer_size(const JsValueParseDeclaration declaration) {\n    if(declaration.source == JsValueParseSourceValue) {\n        const JsValueDeclaration* value_decl = declaration.value_decl;\n        JsValueType type = value_decl->type & JsValueTypeMask;\n\n        if(type == JsValueTypeString) return 1;\n\n        if(type == JsValueTypeObject) {\n            size_t total = 0;\n            for(size_t i = 0; i < value_decl->n_children; i++)\n                total += js_value_buffer_size(\n                    JS_VALUE_PARSE_SOURCE_VALUE(value_decl->object_fields[i].value));\n            return total;\n        }\n\n        return 0;\n\n    } else {\n        const JsValueArguments* arg_decl = declaration.argument_decl;\n        size_t total = 0;\n        for(size_t i = 0; i < arg_decl->n_children; i++)\n            total += js_value_buffer_size(JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i]));\n        return total;\n    }\n}\n\nstatic size_t js_value_resulting_c_values_count(const JsValueParseDeclaration declaration) {\n    if(declaration.source == JsValueParseSourceValue) {\n        const JsValueDeclaration* value_decl = declaration.value_decl;\n        JsValueType type = value_decl->type & JsValueTypeMask;\n\n        if(type == JsValueTypeObject) {\n            size_t total = 0;\n            for(size_t i = 0; i < value_decl->n_children; i++)\n                total += js_value_resulting_c_values_count(\n                    JS_VALUE_PARSE_SOURCE_VALUE(value_decl->object_fields[i].value));\n            return total;\n        }\n\n        return 1;\n\n    } else {\n        const JsValueArguments* arg_decl = declaration.argument_decl;\n        size_t total = 0;\n        for(size_t i = 0; i < arg_decl->n_children; i++)\n            total += js_value_resulting_c_values_count(\n                JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i]));\n        return total;\n    }\n}\n\n#define PREPEND_JS_ERROR_AND_RETURN(mjs, flags, ...)                    \\\n    do {                                                                \\\n        if((flags) & JsValueParseFlagReturnOnError)                     \\\n            mjs_prepend_errorf((mjs), MJS_BAD_ARGS_ERROR, __VA_ARGS__); \\\n        return JsValueParseStatusJsError;                               \\\n    } while(0)\n\n#define PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, type) \\\n    PREPEND_JS_ERROR_AND_RETURN(mjs, flags, \"expected %s\", type)\n\nstatic void js_value_assign_enum_val(void* destination, JsValueType type_w_flags, uint32_t value) {\n    if(type_w_flags & JsValueTypeEnumSize1) {\n        *(uint8_t*)destination = value;\n    } else if(type_w_flags & JsValueTypeEnumSize2) {\n        *(uint16_t*)destination = value;\n    } else if(type_w_flags & JsValueTypeEnumSize4) {\n        *(uint32_t*)destination = value;\n    }\n}\n\nstatic bool js_value_is_null_or_undefined(mjs_val_t* val_ptr) {\n    return mjs_is_null(*val_ptr) || mjs_is_undefined(*val_ptr);\n}\n\nstatic bool js_value_maybe_assign_default(\n    const JsValueDeclaration* declaration,\n    mjs_val_t* val_ptr,\n    void* destination,\n    size_t size) {\n    if((declaration->type & JsValueTypePermitNull) && js_value_is_null_or_undefined(val_ptr)) {\n        memcpy(destination, &declaration->default_value, size);\n        return true;\n    }\n    return false;\n}\n\ntypedef int (*MjsTypecheckFn)(mjs_val_t value);\n\nstatic JsValueParseStatus js_value_parse_literal(\n    struct mjs* mjs,\n    JsValueParseFlag flags,\n    mjs_val_t* destination,\n    mjs_val_t* source,\n    MjsTypecheckFn typecheck,\n    const char* type_name) {\n    if(!typecheck(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, type_name);\n    *destination = *source;\n    return JsValueParseStatusOk;\n}\n\nstatic JsValueParseStatus js_value_parse_va(\n    struct mjs* mjs,\n    const JsValueParseDeclaration declaration,\n    JsValueParseFlag flags,\n    mjs_val_t* source,\n    mjs_val_t* buffer,\n    size_t* buffer_index,\n    va_list* out_pointers) {\n    if(declaration.source == JsValueParseSourceArguments) {\n        const JsValueArguments* arg_decl = declaration.argument_decl;\n\n        for(size_t i = 0; i < arg_decl->n_children; i++) {\n            mjs_val_t arg_val = mjs_arg(mjs, i);\n            JsValueParseStatus status = js_value_parse_va(\n                mjs,\n                JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i]),\n                flags,\n                &arg_val,\n                buffer,\n                buffer_index,\n                out_pointers);\n            if(status != JsValueParseStatusOk) return status;\n        }\n\n        return JsValueParseStatusOk;\n    }\n\n    const JsValueDeclaration* value_decl = declaration.value_decl;\n    JsValueType type_w_flags = value_decl->type;\n    JsValueType type_noflags = type_w_flags & JsValueTypeMask;\n    bool is_null_but_allowed = (type_w_flags & JsValueTypePermitNull) &&\n                               js_value_is_null_or_undefined(source);\n\n    void* destination = NULL;\n    if(type_noflags != JsValueTypeObject) destination = va_arg(*out_pointers, void*);\n\n    switch(type_noflags) {\n    // Literal terms\n    case JsValueTypeAny:\n        *(mjs_val_t*)destination = *source;\n        break;\n    case JsValueTypeAnyArray:\n        return js_value_parse_literal(mjs, flags, destination, source, mjs_is_array, \"array\");\n    case JsValueTypeAnyObject:\n        return js_value_parse_literal(mjs, flags, destination, source, mjs_is_object, \"array\");\n    case JsValueTypeFunction:\n        return js_value_parse_literal(\n            mjs, flags, destination, source, mjs_is_function, \"function\");\n\n    // Primitive types\n    case JsValueTypeRawPointer: {\n        if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(void*))) break;\n        if(!mjs_is_foreign(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"pointer\");\n        *(void**)destination = mjs_get_ptr(mjs, *source);\n        break;\n    }\n    case JsValueTypeInt32: {\n        if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(int32_t))) break;\n        if(!mjs_is_number(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"number\");\n        *(int32_t*)destination = mjs_get_int32(mjs, *source);\n        break;\n    }\n    case JsValueTypeDouble: {\n        if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(double))) break;\n        if(!mjs_is_number(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"number\");\n        *(double*)destination = mjs_get_double(mjs, *source);\n        break;\n    }\n    case JsValueTypeBool: {\n        if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(bool))) break;\n        if(!mjs_is_boolean(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"bool\");\n        *(bool*)destination = mjs_get_bool(mjs, *source);\n        break;\n    }\n    case JsValueTypeString: {\n        if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(const char*)))\n            break;\n        if(!mjs_is_string(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"string\");\n        buffer[*buffer_index] = *source;\n        *(const char**)destination = mjs_get_string(mjs, &buffer[*buffer_index], NULL);\n        (*buffer_index)++;\n        break;\n    }\n\n    // Types with children\n    case JsValueTypeEnum: {\n        if(is_null_but_allowed) {\n            js_value_assign_enum_val(\n                destination, type_w_flags, value_decl->default_value.enum_val);\n\n        } else if(mjs_is_string(*source)) {\n            const char* str = mjs_get_string(mjs, source, NULL);\n            furi_check(str);\n\n            bool match_found = false;\n            for(size_t i = 0; i < value_decl->n_children; i++) {\n                const JsValueEnumVariant* variant = &value_decl->enum_variants[i];\n                if(strcmp(str, variant->string_value) == 0) {\n                    js_value_assign_enum_val(destination, type_w_flags, variant->num_value);\n                    match_found = true;\n                    break;\n                }\n            }\n\n            if(!match_found)\n                PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"one of permitted strings\");\n\n        } else {\n            PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"string\");\n        }\n        break;\n    }\n\n    case JsValueTypeObject: {\n        if(!(is_null_but_allowed || mjs_is_object(*source)))\n            PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, \"object\");\n        for(size_t i = 0; i < value_decl->n_children; i++) {\n            const JsValueObjectField* field = &value_decl->object_fields[i];\n            mjs_val_t field_val = mjs_get(mjs, *source, field->field_name, ~0);\n            JsValueParseStatus status = js_value_parse_va(\n                mjs,\n                JS_VALUE_PARSE_SOURCE_VALUE(field->value),\n                flags,\n                &field_val,\n                buffer,\n                buffer_index,\n                out_pointers);\n            if(status != JsValueParseStatusOk)\n                PREPEND_JS_ERROR_AND_RETURN(mjs, flags, \"field %s: \", field->field_name);\n        }\n        break;\n    }\n\n    case JsValueTypeMask:\n    case JsValueTypeEnumSize1:\n    case JsValueTypeEnumSize2:\n    case JsValueTypeEnumSize4:\n    case JsValueTypePermitNull:\n        furi_crash();\n    }\n\n    return JsValueParseStatusOk;\n}\n\nJsValueParseStatus js_value_parse(\n    struct mjs* mjs,\n    const JsValueParseDeclaration declaration,\n    JsValueParseFlag flags,\n    mjs_val_t* buffer,\n    size_t buf_size,\n    mjs_val_t* source,\n    size_t n_c_vals,\n    ...) {\n    furi_check(mjs);\n    furi_check(buffer);\n\n    if(declaration.source == JsValueParseSourceValue) {\n        furi_check(source);\n        furi_check(declaration.value_decl);\n    } else {\n        furi_check(source == NULL);\n        furi_check(declaration.argument_decl);\n    }\n\n#ifdef JS_VAL_DEBUG\n    furi_check(buf_size == js_value_buffer_size(declaration));\n    furi_check(n_c_vals == js_value_resulting_c_values_count(declaration));\n#else\n    UNUSED(js_value_resulting_c_values_count);\n#endif\n\n    va_list out_pointers;\n    va_start(out_pointers, n_c_vals);\n\n    size_t buffer_index = 0;\n    JsValueParseStatus status =\n        js_value_parse_va(mjs, declaration, flags, source, buffer, &buffer_index, &out_pointers);\n    furi_check(buffer_index <= buf_size);\n\n    va_end(out_pointers);\n\n    return status;\n}\n"
  },
  {
    "path": "applications/system/js_app/js_value.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include \"js_modules.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    // literal types\n    JsValueTypeAny, //<! Literal term\n    JsValueTypeAnyArray, //<! Literal term, after ensuring that it's an array\n    JsValueTypeAnyObject, //<! Literal term, after ensuring that it's an object\n    JsValueTypeFunction, //<! Literal term, after ensuring that it's a function\n\n    // primitive types\n    JsValueTypeRawPointer, //<! Unchecked `void*`\n    JsValueTypeInt32, //<! Number cast to `int32_t`\n    JsValueTypeDouble, //<! Number cast to `double`\n    JsValueTypeString, //<! Any string cast to `const char*`\n    JsValueTypeBool, //<! Bool cast to `bool`\n\n    // types with children\n    JsValueTypeEnum, //<! String with predefined possible values cast to a C enum via a mapping\n    JsValueTypeObject, //<! Object with predefined recursive fields cast to several C values\n\n    JsValueTypeMask = 0xff,\n\n    // enum sizes\n    JsValueTypeEnumSize1 = (1 << 8),\n    JsValueTypeEnumSize2 = (2 << 8),\n    JsValueTypeEnumSize4 = (4 << 8),\n\n    // flags\n    JsValueTypePermitNull = (1 << 16), //<! If the value is absent, assign default value\n} JsValueType;\n\n#define JS_VALUE_TYPE_ENUM_SIZE(x) ((x) << 8)\n\ntypedef struct {\n    const char* string_value;\n    size_t num_value;\n} JsValueEnumVariant;\n\ntypedef union {\n    void* ptr_val;\n    int32_t int32_val;\n    double double_val;\n    const char* str_val;\n    size_t enum_val;\n    bool bool_val;\n} JsValueDefaultValue;\n\ntypedef struct JsValueObjectField JsValueObjectField;\n\ntypedef struct {\n    JsValueType type;\n    JsValueDefaultValue default_value;\n\n    size_t n_children;\n    union {\n        const JsValueEnumVariant* enum_variants;\n        const JsValueObjectField* object_fields;\n    };\n} JsValueDeclaration;\n\nstruct JsValueObjectField {\n    const char* field_name;\n    const JsValueDeclaration* value;\n};\n\ntypedef struct {\n    size_t n_children;\n    const JsValueDeclaration* arguments;\n} JsValueArguments;\n\n#define JS_VALUE_ENUM(c_type, variants)                                    \\\n    {                                                                      \\\n        .type = JsValueTypeEnum | JS_VALUE_TYPE_ENUM_SIZE(sizeof(c_type)), \\\n        .n_children = COUNT_OF(variants),                                  \\\n        .enum_variants = variants,                                         \\\n    }\n\n#define JS_VALUE_ENUM_W_DEFAULT(c_type, variants, default) \\\n    {                                                      \\\n        .type = JsValueTypeEnum | JsValueTypePermitNull |  \\\n                JS_VALUE_TYPE_ENUM_SIZE(sizeof(c_type)),   \\\n        .default_value.enum_val = default,                 \\\n        .n_children = COUNT_OF(variants),                  \\\n        .enum_variants = variants,                         \\\n    }\n\n#define JS_VALUE_OBJECT(fields)         \\\n    {                                   \\\n        .type = JsValueTypeObject,      \\\n        .n_children = COUNT_OF(fields), \\\n        .object_fields = fields,        \\\n    }\n\n#define JS_VALUE_OBJECT_W_DEFAULTS(fields)                 \\\n    {                                                      \\\n        .type = JsValueTypeObject | JsValueTypePermitNull, \\\n        .n_children = COUNT_OF(fields),                    \\\n        .object_fields = fields,                           \\\n    }\n\n#define JS_VALUE_SIMPLE(t) {.type = t}\n\n#define JS_VALUE_SIMPLE_W_DEFAULT(t, name, val) \\\n    {.type = (t) | JsValueTypePermitNull, .default_value.name = (val)}\n\n#define JS_VALUE_ARGS(args)           \\\n    {                                 \\\n        .n_children = COUNT_OF(args), \\\n        .arguments = args,            \\\n    }\n\ntypedef enum {\n    JsValueParseFlagNone = 0,\n    JsValueParseFlagReturnOnError =\n        (1\n         << 0), //<! Sets mjs error string to a description of the parsing error and returns from the JS function\n} JsValueParseFlag;\n\ntypedef enum {\n    JsValueParseStatusOk, //<! Parsing completed successfully\n    JsValueParseStatusJsError, //<! Parsing failed due to incorrect JS input\n} JsValueParseStatus;\n\ntypedef enum {\n    JsValueParseSourceValue,\n    JsValueParseSourceArguments,\n} JsValueParseSource;\n\ntypedef struct {\n    JsValueParseSource source;\n    union {\n        const JsValueDeclaration* value_decl;\n        const JsValueArguments* argument_decl;\n    };\n} JsValueParseDeclaration;\n\n#define JS_VALUE_PARSE_SOURCE_VALUE(declaration) \\\n    ((JsValueParseDeclaration){.source = JsValueParseSourceValue, .value_decl = declaration})\n#define JS_VALUE_PARSE_SOURCE_ARGS(declaration) \\\n    ((JsValueParseDeclaration){                 \\\n        .source = JsValueParseSourceArguments, .argument_decl = declaration})\n\n/**\n * @brief Determines the size of the buffer array of `mjs_val_t`s that needs to\n * be passed to `js_value_parse`.\n */\nsize_t js_value_buffer_size(const JsValueParseDeclaration declaration);\n\n/**\n * @brief Converts a JS value into a series of C values.\n * \n * @param[in]    mjs         mJS instance pointer\n * @param[in]    declaration Declaration for the input value. Chooses where the\n *                           values are to be fetched from (an `mjs_val_t` or\n *                           function arguments)\n * @param[in]    flags       See the corresponding enum.\n * @param[out]   buffer      Temporary buffer for values that need to live\n *                           longer than the function call. To determine the\n *                           size of the buffer, use `js_value_buffer_size`.\n *                           Values parsed by this function will become invalid\n *                           when this buffer goes out of scope.\n * @param[in]    buf_size    Number of entries in the temporary buffer (i.e.\n *                           `COUNT_OF`, not `sizeof`).\n * @param[in]    source      Source JS value that needs to be converted. May be\n *                           NULL if `declaration.source` is\n *                           `JsValueParseSourceArguments`.\n * @param[in]    n_c_vals    Number of output C values\n * @param[out]   ...         Pointers to output C values. The order in which\n *                           these values are populated corresponds to the order\n *                           in which the values are defined in the declaration.\n * \n * @returns Parsing status\n */\nJsValueParseStatus js_value_parse(\n    struct mjs* mjs,\n    const JsValueParseDeclaration declaration,\n    JsValueParseFlag flags,\n    mjs_val_t* buffer,\n    size_t buf_size,\n    mjs_val_t* source,\n    size_t n_c_vals,\n    ...);\n\n#define JS_VALUE_PARSE(mjs, declaration, flags, status_ptr, value_ptr, ...) \\\n    void* _args[] = {__VA_ARGS__};                                          \\\n    size_t _n_args = COUNT_OF(_args);                                       \\\n    size_t _temp_buf_len = js_value_buffer_size(declaration);               \\\n    mjs_val_t _temp_buffer[_temp_buf_len];                                  \\\n    *(status_ptr) = js_value_parse(                                         \\\n        mjs, declaration, flags, _temp_buffer, _temp_buf_len, value_ptr, _n_args, __VA_ARGS__);\n\n#define JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, declaration, ...) \\\n    JsValueParseStatus _status;                              \\\n    JS_VALUE_PARSE(                                          \\\n        mjs,                                                 \\\n        JS_VALUE_PARSE_SOURCE_ARGS(declaration),             \\\n        JsValueParseFlagReturnOnError,                       \\\n        &_status,                                            \\\n        NULL,                                                \\\n        __VA_ARGS__);                                        \\\n    if(_status != JsValueParseStatusOk) return;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/modules/js_badusb.c",
    "content": "#include <core/common_defines.h>\n#include \"../js_modules.h\"\n#include <furi_hal.h>\n\n#define ASCII_TO_KEY(layout, x) (((uint8_t)x < 128) ? (layout[(uint8_t)x]) : HID_KEYBOARD_NONE)\n\ntypedef struct {\n    FuriHalUsbHidConfig* hid_cfg;\n    uint16_t layout[128];\n    FuriHalUsbInterface* usb_if_prev;\n    uint8_t key_hold_cnt;\n} JsBadusbInst;\n\nstatic const struct {\n    char* name;\n    uint16_t code;\n} key_codes[] = {\n    {\"CTRL\", KEY_MOD_LEFT_CTRL},\n    {\"SHIFT\", KEY_MOD_LEFT_SHIFT},\n    {\"ALT\", KEY_MOD_LEFT_ALT},\n    {\"GUI\", KEY_MOD_LEFT_GUI},\n\n    {\"DOWN\", HID_KEYBOARD_DOWN_ARROW},\n    {\"LEFT\", HID_KEYBOARD_LEFT_ARROW},\n    {\"RIGHT\", HID_KEYBOARD_RIGHT_ARROW},\n    {\"UP\", HID_KEYBOARD_UP_ARROW},\n\n    {\"ENTER\", HID_KEYBOARD_RETURN},\n    {\"PAUSE\", HID_KEYBOARD_PAUSE},\n    {\"CAPSLOCK\", HID_KEYBOARD_CAPS_LOCK},\n    {\"DELETE\", HID_KEYBOARD_DELETE_FORWARD},\n    {\"BACKSPACE\", HID_KEYBOARD_DELETE},\n    {\"END\", HID_KEYBOARD_END},\n    {\"ESC\", HID_KEYBOARD_ESCAPE},\n    {\"HOME\", HID_KEYBOARD_HOME},\n    {\"INSERT\", HID_KEYBOARD_INSERT},\n    {\"NUMLOCK\", HID_KEYPAD_NUMLOCK},\n    {\"PAGEUP\", HID_KEYBOARD_PAGE_UP},\n    {\"PAGEDOWN\", HID_KEYBOARD_PAGE_DOWN},\n    {\"PRINTSCREEN\", HID_KEYBOARD_PRINT_SCREEN},\n    {\"SCROLLLOCK\", HID_KEYBOARD_SCROLL_LOCK},\n    {\"SPACE\", HID_KEYBOARD_SPACEBAR},\n    {\"TAB\", HID_KEYBOARD_TAB},\n    {\"MENU\", HID_KEYBOARD_APPLICATION},\n\n    {\"F1\", HID_KEYBOARD_F1},\n    {\"F2\", HID_KEYBOARD_F2},\n    {\"F3\", HID_KEYBOARD_F3},\n    {\"F4\", HID_KEYBOARD_F4},\n    {\"F5\", HID_KEYBOARD_F5},\n    {\"F6\", HID_KEYBOARD_F6},\n    {\"F7\", HID_KEYBOARD_F7},\n    {\"F8\", HID_KEYBOARD_F8},\n    {\"F9\", HID_KEYBOARD_F9},\n    {\"F10\", HID_KEYBOARD_F10},\n    {\"F11\", HID_KEYBOARD_F11},\n    {\"F12\", HID_KEYBOARD_F12},\n    {\"F13\", HID_KEYBOARD_F13},\n    {\"F14\", HID_KEYBOARD_F14},\n    {\"F15\", HID_KEYBOARD_F15},\n    {\"F16\", HID_KEYBOARD_F16},\n    {\"F17\", HID_KEYBOARD_F17},\n    {\"F18\", HID_KEYBOARD_F18},\n    {\"F19\", HID_KEYBOARD_F19},\n    {\"F20\", HID_KEYBOARD_F20},\n    {\"F21\", HID_KEYBOARD_F21},\n    {\"F22\", HID_KEYBOARD_F22},\n    {\"F23\", HID_KEYBOARD_F23},\n    {\"F24\", HID_KEYBOARD_F24},\n\n    {\"NUM0\", HID_KEYPAD_0},\n    {\"NUM1\", HID_KEYPAD_1},\n    {\"NUM2\", HID_KEYPAD_2},\n    {\"NUM3\", HID_KEYPAD_3},\n    {\"NUM4\", HID_KEYPAD_4},\n    {\"NUM5\", HID_KEYPAD_5},\n    {\"NUM6\", HID_KEYPAD_6},\n    {\"NUM7\", HID_KEYPAD_7},\n    {\"NUM8\", HID_KEYPAD_8},\n    {\"NUM9\", HID_KEYPAD_9},\n};\n\nstatic void js_badusb_quit_free(JsBadusbInst* badusb) {\n    if(badusb->usb_if_prev) {\n        furi_hal_hid_kb_release_all();\n        furi_check(furi_hal_usb_set_config(badusb->usb_if_prev, NULL));\n        badusb->usb_if_prev = NULL;\n    }\n    if(badusb->hid_cfg) {\n        free(badusb->hid_cfg);\n        badusb->hid_cfg = NULL;\n    }\n}\n\nstatic bool setup_parse_params(\n    JsBadusbInst* badusb,\n    struct mjs* mjs,\n    mjs_val_t arg,\n    FuriHalUsbHidConfig* hid_cfg) {\n    if(!mjs_is_object(arg)) {\n        return false;\n    }\n    mjs_val_t vid_obj = mjs_get(mjs, arg, \"vid\", ~0);\n    mjs_val_t pid_obj = mjs_get(mjs, arg, \"pid\", ~0);\n    mjs_val_t mfr_obj = mjs_get(mjs, arg, \"mfrName\", ~0);\n    mjs_val_t prod_obj = mjs_get(mjs, arg, \"prodName\", ~0);\n    mjs_val_t layout_obj = mjs_get(mjs, arg, \"layoutPath\", ~0);\n\n    if(mjs_is_number(vid_obj) && mjs_is_number(pid_obj)) {\n        hid_cfg->vid = mjs_get_int32(mjs, vid_obj);\n        hid_cfg->pid = mjs_get_int32(mjs, pid_obj);\n    } else {\n        return false;\n    }\n\n    if(mjs_is_string(mfr_obj)) {\n        size_t str_len = 0;\n        const char* str_temp = mjs_get_string(mjs, &mfr_obj, &str_len);\n        if((str_len == 0) || (str_temp == NULL)) {\n            return false;\n        }\n        strlcpy(hid_cfg->manuf, str_temp, sizeof(hid_cfg->manuf));\n    }\n\n    if(mjs_is_string(prod_obj)) {\n        size_t str_len = 0;\n        const char* str_temp = mjs_get_string(mjs, &prod_obj, &str_len);\n        if((str_len == 0) || (str_temp == NULL)) {\n            return false;\n        }\n        strlcpy(hid_cfg->product, str_temp, sizeof(hid_cfg->product));\n    }\n\n    if(mjs_is_string(layout_obj)) {\n        size_t str_len = 0;\n        const char* str_temp = mjs_get_string(mjs, &layout_obj, &str_len);\n        if((str_len == 0) || (str_temp == NULL)) {\n            return false;\n        }\n        File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));\n        bool layout_loaded = storage_file_open(file, str_temp, FSAM_READ, FSOM_OPEN_EXISTING) &&\n                             storage_file_read(file, badusb->layout, sizeof(badusb->layout)) ==\n                                 sizeof(badusb->layout);\n        storage_file_free(file);\n        furi_record_close(RECORD_STORAGE);\n        if(!layout_loaded) {\n            return false;\n        }\n    } else {\n        memcpy(badusb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(badusb->layout)));\n    }\n\n    return true;\n}\n\nstatic void js_badusb_setup(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n\n    if(badusb->usb_if_prev) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is already started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    bool args_correct = false;\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args == 0) {\n        // No arguments: start USB HID with default settings\n        args_correct = true;\n    } else if(num_args == 1) {\n        badusb->hid_cfg = malloc(sizeof(FuriHalUsbHidConfig));\n        // Parse argument object\n        args_correct = setup_parse_params(badusb, mjs, mjs_arg(mjs, 0), badusb->hid_cfg);\n    }\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    badusb->usb_if_prev = furi_hal_usb_get_config();\n\n    if(!furi_hal_usb_set_config(&usb_hid, badusb->hid_cfg)) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"USB is locked, close companion app first\");\n        badusb->usb_if_prev = NULL;\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_badusb_quit(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    js_badusb_quit_free(badusb);\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_badusb_is_connected(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    bool is_connected = furi_hal_hid_is_connected();\n    mjs_return(mjs, mjs_mk_boolean(mjs, is_connected));\n}\n\nuint16_t get_keycode_by_name(JsBadusbInst* badusb, const char* key_name, size_t name_len) {\n    if(name_len == 1) { // Single char\n        return (ASCII_TO_KEY(badusb->layout, key_name[0]));\n    }\n\n    for(size_t i = 0; i < COUNT_OF(key_codes); i++) {\n        size_t key_cmd_len = strlen(key_codes[i].name);\n        if(key_cmd_len != name_len) {\n            continue;\n        }\n\n        if(strncmp(key_name, key_codes[i].name, name_len) == 0) {\n            return key_codes[i].code;\n        }\n    }\n\n    return HID_KEYBOARD_NONE;\n}\n\nstatic bool parse_keycode(JsBadusbInst* badusb, struct mjs* mjs, size_t nargs, uint16_t* keycode) {\n    uint16_t key_tmp = 0;\n    for(size_t i = 0; i < nargs; i++) {\n        mjs_val_t arg = mjs_arg(mjs, i);\n        if(mjs_is_string(arg)) {\n            size_t name_len = 0;\n            const char* key_name = mjs_get_string(mjs, &arg, &name_len);\n            if((key_name == NULL) || (name_len == 0)) {\n                // String error\n                return false;\n            }\n            uint16_t str_key = get_keycode_by_name(badusb, key_name, name_len);\n            if(str_key == HID_KEYBOARD_NONE) {\n                // Unknown key code\n                return false;\n            }\n            if((str_key & 0xFF) && (key_tmp & 0xFF)) {\n                // Main key is already defined\n                return false;\n            }\n            key_tmp |= str_key;\n        } else if(mjs_is_number(arg)) {\n            uint32_t keycode_number = (uint32_t)mjs_get_int32(mjs, arg);\n            if(((key_tmp & 0xFF) != 0) || (keycode_number > 0xFF)) {\n                return false;\n            }\n            key_tmp |= keycode_number & 0xFF;\n        } else {\n            return false;\n        }\n    }\n    *keycode = key_tmp;\n    return true;\n}\n\nstatic void js_badusb_press(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    bool args_correct = false;\n    uint16_t keycode = HID_KEYBOARD_NONE;\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args > 0) {\n        args_correct = parse_keycode(badusb, mjs, num_args, &keycode);\n    }\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    furi_hal_hid_kb_press(keycode);\n    furi_hal_hid_kb_release(keycode);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_badusb_hold(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    bool args_correct = false;\n    uint16_t keycode = HID_KEYBOARD_NONE;\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args > 0) {\n        args_correct = parse_keycode(badusb, mjs, num_args, &keycode);\n    }\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    if(keycode & 0xFF) {\n        badusb->key_hold_cnt++;\n        if(badusb->key_hold_cnt > (HID_KB_MAX_KEYS - 1)) {\n            mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"Too many keys are hold\");\n            furi_hal_hid_kb_release_all();\n            mjs_return(mjs, MJS_UNDEFINED);\n            return;\n        }\n    }\n    furi_hal_hid_kb_press(keycode);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_badusb_release(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    bool args_correct = false;\n    uint16_t keycode = HID_KEYBOARD_NONE;\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args == 0) {\n        furi_hal_hid_kb_release_all();\n        badusb->key_hold_cnt = 0;\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    } else {\n        args_correct = parse_keycode(badusb, mjs, num_args, &keycode);\n    }\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    if((keycode & 0xFF) && (badusb->key_hold_cnt > 0)) {\n        badusb->key_hold_cnt--;\n    }\n    furi_hal_hid_kb_release(keycode);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n// Make sure NUMLOCK is enabled for altchar\nstatic void ducky_numlock_on() {\n    if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {\n        furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);\n        furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);\n    }\n}\n\n// Simulate pressing a character using ALT+Numpad ASCII code\nstatic void ducky_altchar(JsBadusbInst* badusb, const char* ascii_code) {\n    // Hold the ALT key\n    furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);\n\n    // Press the corresponding numpad key for each digit of the ASCII code\n    for(size_t i = 0; ascii_code[i] != '\\0'; i++) {\n        char digitChar[5] = {'N', 'U', 'M', ascii_code[i], '\\0'}; // Construct the numpad key name\n        uint16_t numpad_keycode = get_keycode_by_name(badusb, digitChar, strlen(digitChar));\n        if(numpad_keycode == HID_KEYBOARD_NONE) {\n            continue; // Skip if keycode not found\n        }\n        furi_hal_hid_kb_press(numpad_keycode);\n        furi_hal_hid_kb_release(numpad_keycode);\n    }\n\n    // Release the ALT key\n    furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT);\n}\n\nstatic void badusb_print(struct mjs* mjs, bool ln, bool alt) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsBadusbInst* badusb = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(badusb);\n    if(badusb->usb_if_prev == NULL) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"HID is not started\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    bool args_correct = false;\n    const char* text_str = NULL;\n    size_t text_len = 0;\n    uint32_t delay_val = 0;\n    do {\n        mjs_val_t obj_string = MJS_UNDEFINED;\n        size_t num_args = mjs_nargs(mjs);\n        if(num_args == 1) {\n            obj_string = mjs_arg(mjs, 0);\n        } else if(num_args == 2) {\n            obj_string = mjs_arg(mjs, 0);\n            mjs_val_t obj_delay = mjs_arg(mjs, 1);\n            if(!mjs_is_number(obj_delay)) {\n                break;\n            }\n            delay_val = (uint32_t)mjs_get_int32(mjs, obj_delay);\n            if(delay_val > 60000) {\n                break;\n            }\n        }\n\n        if(!mjs_is_string(obj_string)) {\n            break;\n        }\n        text_str = mjs_get_string(mjs, &obj_string, &text_len);\n        if((text_str == NULL) || (text_len == 0)) {\n            break;\n        }\n        args_correct = true;\n    } while(0);\n\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    if(alt) {\n        ducky_numlock_on();\n    }\n    for(size_t i = 0; i < text_len; i++) {\n        if(alt) {\n            // Convert character to ascii numeric value\n            char ascii_str[4];\n            snprintf(ascii_str, sizeof(ascii_str), \"%u\", (uint8_t)text_str[i]);\n            ducky_altchar(badusb, ascii_str);\n        } else {\n            uint16_t keycode = ASCII_TO_KEY(badusb->layout, text_str[i]);\n            furi_hal_hid_kb_press(keycode);\n            furi_hal_hid_kb_release(keycode);\n        }\n        if(delay_val > 0) {\n            bool need_exit = js_delay_with_flags(mjs, delay_val);\n            if(need_exit) {\n                mjs_return(mjs, MJS_UNDEFINED);\n                return;\n            }\n        }\n    }\n    if(ln) {\n        furi_hal_hid_kb_press(HID_KEYBOARD_RETURN);\n        furi_hal_hid_kb_release(HID_KEYBOARD_RETURN);\n    }\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_badusb_print(struct mjs* mjs) {\n    badusb_print(mjs, false, false);\n}\n\nstatic void js_badusb_println(struct mjs* mjs) {\n    badusb_print(mjs, true, false);\n}\n\nstatic void js_badusb_alt_print(struct mjs* mjs) {\n    badusb_print(mjs, false, true);\n}\n\nstatic void js_badusb_alt_println(struct mjs* mjs) {\n    badusb_print(mjs, true, true);\n}\n\nstatic void* js_badusb_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    JsBadusbInst* badusb = malloc(sizeof(JsBadusbInst));\n    mjs_val_t badusb_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, badusb_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, badusb));\n    mjs_set(mjs, badusb_obj, \"setup\", ~0, MJS_MK_FN(js_badusb_setup));\n    mjs_set(mjs, badusb_obj, \"quit\", ~0, MJS_MK_FN(js_badusb_quit));\n    mjs_set(mjs, badusb_obj, \"isConnected\", ~0, MJS_MK_FN(js_badusb_is_connected));\n    mjs_set(mjs, badusb_obj, \"press\", ~0, MJS_MK_FN(js_badusb_press));\n    mjs_set(mjs, badusb_obj, \"hold\", ~0, MJS_MK_FN(js_badusb_hold));\n    mjs_set(mjs, badusb_obj, \"release\", ~0, MJS_MK_FN(js_badusb_release));\n    mjs_set(mjs, badusb_obj, \"print\", ~0, MJS_MK_FN(js_badusb_print));\n    mjs_set(mjs, badusb_obj, \"println\", ~0, MJS_MK_FN(js_badusb_println));\n    mjs_set(mjs, badusb_obj, \"altPrint\", ~0, MJS_MK_FN(js_badusb_alt_print));\n    mjs_set(mjs, badusb_obj, \"altPrintln\", ~0, MJS_MK_FN(js_badusb_alt_println));\n    *object = badusb_obj;\n    return badusb;\n}\n\nstatic void js_badusb_destroy(void* inst) {\n    JsBadusbInst* badusb = inst;\n    js_badusb_quit_free(badusb);\n    free(badusb);\n}\n\nstatic const JsModuleDescriptor js_badusb_desc = {\n    \"badusb\",\n    js_badusb_create,\n    js_badusb_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_badusb_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_badusb_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_event_loop/js_event_loop.c",
    "content": "#include \"js_event_loop.h\"\n#include \"../../js_modules.h\" // IWYU pragma: keep\n#include <expansion/expansion.h>\n#include <mlib/m-array.h>\n\n/**\n * @brief Number of arguments that callbacks receive from this module that they can't modify\n */\n#define SYSTEM_ARGS 2\n\n/**\n * @brief Context passed to the generic event callback\n */\ntypedef struct {\n    FuriEventLoop* event_loop;\n    JsEventLoopObjectType object_type;\n\n    struct mjs* mjs;\n    mjs_val_t callback;\n    // NOTE: not using an mlib array because resizing is not needed.\n    mjs_val_t* arguments;\n    size_t arity;\n\n    JsEventLoopTransformer transformer;\n    void* transformer_context;\n} JsEventLoopCallbackContext;\n\n/**\n * @brief Contains data needed to cancel a subscription\n */\ntypedef struct {\n    FuriEventLoop* loop;\n    JsEventLoopObjectType object_type;\n    FuriEventLoopObject* object;\n    JsEventLoopCallbackContext* context;\n    JsEventLoopContract* contract;\n    void* subscriptions; // SubscriptionArray_t, which we can't reference in this definition\n} JsEventLoopSubscription;\n\nARRAY_DEF(SubscriptionArray, JsEventLoopSubscription*, M_PTR_OPLIST); //-V575\nARRAY_DEF(ContractArray, JsEventLoopContract*, M_PTR_OPLIST); //-V575\n\n/**\n * @brief Per-module instance control structure\n */\nstruct JsEventLoop {\n    FuriEventLoop* loop;\n    SubscriptionArray_t subscriptions;\n    ContractArray_t owned_contracts; //<! Contracts that were produced by this module\n};\n\n/**\n * @brief Generic event callback, handles all events by calling the JS callbacks\n */\nstatic void js_event_loop_callback_generic(void* param) {\n    JsEventLoopCallbackContext* context = param;\n    mjs_val_t result;\n    mjs_err_t error = mjs_apply(\n        context->mjs,\n        &result,\n        context->callback,\n        MJS_UNDEFINED,\n        context->arity,\n        context->arguments);\n\n    bool is_error = strcmp(mjs_strerror(context->mjs, error), \"NO_ERROR\") != 0;\n    bool asked_to_stop = js_flags_wait(context->mjs, ThreadEventStop, 0) & ThreadEventStop;\n    if(is_error || asked_to_stop) {\n        furi_event_loop_stop(context->event_loop);\n    }\n\n    // save returned args for next call\n    if(mjs_array_length(context->mjs, result) != context->arity - SYSTEM_ARGS) return;\n    for(size_t i = 0; i < context->arity - SYSTEM_ARGS; i++) {\n        mjs_disown(context->mjs, &context->arguments[i + SYSTEM_ARGS]);\n        context->arguments[i + SYSTEM_ARGS] = mjs_array_get(context->mjs, result, i);\n        mjs_own(context->mjs, &context->arguments[i + SYSTEM_ARGS]);\n    }\n}\n\n/**\n * @brief Handles non-timer events\n */\nstatic void js_event_loop_callback(void* object, void* param) {\n    JsEventLoopCallbackContext* context = param;\n\n    if(context->transformer) {\n        mjs_disown(context->mjs, &context->arguments[1]);\n        context->arguments[1] =\n            context->transformer(context->mjs, object, context->transformer_context);\n        mjs_own(context->mjs, &context->arguments[1]);\n    } else {\n        // default behavior: take semaphores and mutexes\n        switch(context->object_type) {\n        case JsEventLoopObjectTypeSemaphore: {\n            FuriSemaphore* semaphore = object;\n            furi_check(furi_semaphore_acquire(semaphore, 0) == FuriStatusOk);\n        } break;\n        default:\n            // the corresponding check has been performed when we were given the contract\n            furi_crash();\n        }\n    }\n\n    js_event_loop_callback_generic(param);\n}\n\n/**\n * @brief Cancels an event subscription\n */\nstatic void js_event_loop_subscription_cancel(struct mjs* mjs) {\n    JsEventLoopSubscription* subscription = JS_GET_CONTEXT(mjs);\n\n    if(subscription->object_type == JsEventLoopObjectTypeTimer) {\n        // timer operations are deferred, which creates lifetime issues\n        // just stop the timer and let the cleanup routine free everything when the script is done\n        furi_event_loop_timer_stop(subscription->object);\n        return;\n    }\n\n    furi_event_loop_unsubscribe(subscription->loop, subscription->object);\n\n    free(subscription->context->arguments);\n    free(subscription->context);\n\n    // find and remove ourselves from the array\n    SubscriptionArray_it_t iterator;\n    for(SubscriptionArray_it(iterator, subscription->subscriptions);\n        !SubscriptionArray_end_p(iterator);\n        SubscriptionArray_next(iterator)) {\n        JsEventLoopSubscription* item = *SubscriptionArray_cref(iterator);\n        if(item == subscription) break;\n    }\n    SubscriptionArray_remove(subscription->subscriptions, iterator);\n    free(subscription);\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief Subscribes a JavaScript function to an event\n */\nstatic void js_event_loop_subscribe(struct mjs* mjs) {\n    JsEventLoop* module = JS_GET_CONTEXT(mjs);\n\n    // get arguments\n    static const JsValueDeclaration js_loop_subscribe_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeRawPointer),\n        JS_VALUE_SIMPLE(JsValueTypeFunction),\n    };\n    static const JsValueArguments js_loop_subscribe_args =\n        JS_VALUE_ARGS(js_loop_subscribe_arg_list);\n\n    JsEventLoopContract* contract;\n    mjs_val_t callback;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_subscribe_args, &contract, &callback);\n\n    // create subscription object\n    JsEventLoopSubscription* subscription = malloc(sizeof(JsEventLoopSubscription));\n    JsEventLoopCallbackContext* context = malloc(sizeof(JsEventLoopCallbackContext));\n    subscription->loop = module->loop;\n    subscription->object_type = contract->object_type;\n    subscription->context = context;\n    subscription->subscriptions = module->subscriptions;\n    if(contract->object_type == JsEventLoopObjectTypeTimer) subscription->contract = contract;\n    mjs_val_t subscription_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, subscription_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, subscription));\n    mjs_set(mjs, subscription_obj, \"cancel\", ~0, MJS_MK_FN(js_event_loop_subscription_cancel));\n\n    // create callback context\n    context->event_loop = module->loop;\n    context->object_type = contract->object_type;\n    context->arity = mjs_nargs(mjs) - SYSTEM_ARGS + 2;\n    context->arguments = calloc(context->arity, sizeof(mjs_val_t));\n    context->arguments[0] = subscription_obj;\n    context->arguments[1] = MJS_UNDEFINED;\n    for(size_t i = SYSTEM_ARGS; i < context->arity; i++) {\n        mjs_val_t arg = mjs_arg(mjs, i - SYSTEM_ARGS + 2);\n        context->arguments[i] = arg;\n        mjs_own(mjs, &context->arguments[i]);\n    }\n    context->mjs = mjs;\n    context->callback = callback;\n    mjs_own(mjs, &context->callback);\n    mjs_own(mjs, &context->arguments[0]);\n    mjs_own(mjs, &context->arguments[1]);\n\n    // queue and stream contracts must have a transform callback, others are allowed to delegate\n    // the obvious default behavior to this module\n    if(contract->object_type == JsEventLoopObjectTypeQueue ||\n       contract->object_type == JsEventLoopObjectTypeStream) {\n        furi_check(contract->non_timer.transformer);\n    }\n    context->transformer = contract->non_timer.transformer;\n    context->transformer_context = contract->non_timer.transformer_context;\n\n    // subscribe\n    switch(contract->object_type) {\n    case JsEventLoopObjectTypeTimer: {\n        FuriEventLoopTimer* timer = furi_event_loop_timer_alloc(\n            module->loop, js_event_loop_callback_generic, contract->timer.type, context);\n        furi_event_loop_timer_start(timer, contract->timer.interval_ticks);\n        contract->object = timer;\n    } break;\n    case JsEventLoopObjectTypeSemaphore:\n        furi_event_loop_subscribe_semaphore(\n            module->loop,\n            contract->object,\n            contract->non_timer.event,\n            js_event_loop_callback,\n            context);\n        break;\n    case JsEventLoopObjectTypeQueue:\n        furi_event_loop_subscribe_message_queue(\n            module->loop,\n            contract->object,\n            contract->non_timer.event,\n            js_event_loop_callback,\n            context);\n        break;\n    default:\n        furi_crash(\"unimplemented\");\n    }\n\n    subscription->object = contract->object;\n    SubscriptionArray_push_back(module->subscriptions, subscription);\n    mjs_return(mjs, subscription_obj);\n}\n\n/**\n * @brief Runs the event loop until it is stopped\n */\nstatic void js_event_loop_run(struct mjs* mjs) {\n    JsEventLoop* module = JS_GET_CONTEXT(mjs);\n    furi_event_loop_run(module->loop);\n}\n\n/**\n * @brief Stops a running event loop\n */\nstatic void js_event_loop_stop(struct mjs* mjs) {\n    JsEventLoop* module = JS_GET_CONTEXT(mjs);\n    furi_event_loop_stop(module->loop);\n}\n\n/**\n * @brief Creates a timer event that can be subscribed to just like any other\n * event\n */\nstatic void js_event_loop_timer(struct mjs* mjs) {\n    static const JsValueEnumVariant js_loop_timer_mode_variants[] = {\n        {\"periodic\", FuriEventLoopTimerTypePeriodic},\n        {\"oneshot\", FuriEventLoopTimerTypeOnce},\n    };\n\n    static const JsValueDeclaration js_loop_timer_arg_list[] = {\n        JS_VALUE_ENUM(FuriEventLoopTimerType, js_loop_timer_mode_variants),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_loop_timer_args = JS_VALUE_ARGS(js_loop_timer_arg_list);\n\n    FuriEventLoopTimerType mode;\n    int32_t interval;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_timer_args, &mode, &interval);\n\n    JsEventLoop* module = JS_GET_CONTEXT(mjs);\n\n    // make timer contract\n    JsEventLoopContract* contract = malloc(sizeof(JsEventLoopContract));\n    *contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeTimer,\n        .object = NULL,\n        .timer =\n            {\n                .interval_ticks = furi_ms_to_ticks((uint32_t)interval),\n                .type = mode,\n            },\n    };\n    ContractArray_push_back(module->owned_contracts, contract);\n    mjs_return(mjs, mjs_mk_foreign(mjs, contract));\n}\n\n/**\n * @brief Queue transformer. Takes `mjs_val_t` pointers out of a queue and\n * returns their dereferenced value\n */\nstatic mjs_val_t\n    js_event_loop_queue_transformer(struct mjs* mjs, FuriEventLoopObject* object, void* context) {\n    UNUSED(context);\n    mjs_val_t* message_ptr;\n    furi_check(furi_message_queue_get(object, &message_ptr, 0) == FuriStatusOk);\n    mjs_val_t message = *message_ptr;\n    mjs_disown(mjs, message_ptr);\n    free(message_ptr);\n    return message;\n}\n\n/**\n * @brief Sends a message to a queue\n */\nstatic void js_event_loop_queue_send(struct mjs* mjs) {\n    // get arguments\n    static const JsValueDeclaration js_loop_q_send_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_loop_q_send_args = JS_VALUE_ARGS(js_loop_q_send_arg_list);\n\n    mjs_val_t message;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_send_args, &message);\n\n    JsEventLoopContract* contract = JS_GET_CONTEXT(mjs);\n\n    // send message\n    mjs_val_t* message_ptr = malloc(sizeof(mjs_val_t));\n    *message_ptr = message;\n    mjs_own(mjs, message_ptr);\n    furi_message_queue_put(contract->object, &message_ptr, 0);\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief Creates a queue\n */\nstatic void js_event_loop_queue(struct mjs* mjs) {\n    // get arguments\n    static const JsValueDeclaration js_loop_q_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_loop_q_args = JS_VALUE_ARGS(js_loop_q_arg_list);\n\n    int32_t length;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_args, &length);\n\n    JsEventLoop* module = JS_GET_CONTEXT(mjs);\n\n    // make queue contract\n    JsEventLoopContract* contract = malloc(sizeof(JsEventLoopContract));\n    *contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        // we could store `mjs_val_t`s in the queue directly if not for mJS' requirement to have consistent pointers to owned values\n        .object = furi_message_queue_alloc((size_t)length, sizeof(mjs_val_t*)),\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = js_event_loop_queue_transformer,\n            },\n    };\n    ContractArray_push_back(module->owned_contracts, contract);\n\n    // return object with control methods\n    mjs_val_t queue = mjs_mk_object(mjs);\n    mjs_set(mjs, queue, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, contract));\n    mjs_set(mjs, queue, \"input\", ~0, mjs_mk_foreign(mjs, contract));\n    mjs_set(mjs, queue, \"send\", ~0, MJS_MK_FN(js_event_loop_queue_send));\n    mjs_return(mjs, queue);\n}\n\nstatic void* js_event_loop_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    mjs_val_t event_loop_obj = mjs_mk_object(mjs);\n    JsEventLoop* module = malloc(sizeof(JsEventLoop));\n    module->loop = furi_event_loop_alloc();\n    SubscriptionArray_init(module->subscriptions);\n    ContractArray_init(module->owned_contracts);\n\n    JS_ASSIGN_MULTI(mjs, event_loop_obj) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, module));\n        JS_FIELD(\"subscribe\", MJS_MK_FN(js_event_loop_subscribe));\n        JS_FIELD(\"run\", MJS_MK_FN(js_event_loop_run));\n        JS_FIELD(\"stop\", MJS_MK_FN(js_event_loop_stop));\n        JS_FIELD(\"timer\", MJS_MK_FN(js_event_loop_timer));\n        JS_FIELD(\"queue\", MJS_MK_FN(js_event_loop_queue));\n    }\n\n    *object = event_loop_obj;\n    return module;\n}\n\nstatic void js_event_loop_destroy(void* inst) {\n    if(inst) {\n        JsEventLoop* module = inst;\n        furi_event_loop_stop(module->loop);\n\n        // free subscriptions\n        SubscriptionArray_it_t sub_iterator;\n        for(SubscriptionArray_it(sub_iterator, module->subscriptions);\n            !SubscriptionArray_end_p(sub_iterator);\n            SubscriptionArray_next(sub_iterator)) {\n            JsEventLoopSubscription* const* sub = SubscriptionArray_cref(sub_iterator);\n            free((*sub)->context->arguments);\n            free((*sub)->context);\n            free(*sub);\n        }\n        SubscriptionArray_clear(module->subscriptions);\n\n        // free owned contracts\n        ContractArray_it_t iterator;\n        for(ContractArray_it(iterator, module->owned_contracts); !ContractArray_end_p(iterator);\n            ContractArray_next(iterator)) {\n            // unsubscribe object\n            JsEventLoopContract* contract = *ContractArray_cref(iterator);\n            if(contract->object_type == JsEventLoopObjectTypeTimer) {\n                furi_event_loop_timer_stop(contract->object);\n            } else {\n                furi_event_loop_unsubscribe(module->loop, contract->object);\n            }\n\n            // free object\n            switch(contract->object_type) {\n            case JsEventLoopObjectTypeTimer:\n                furi_event_loop_timer_free(contract->object);\n                break;\n            case JsEventLoopObjectTypeSemaphore:\n                furi_semaphore_free(contract->object);\n                break;\n            case JsEventLoopObjectTypeQueue:\n                furi_message_queue_free(contract->object);\n                break;\n            default:\n                furi_crash(\"unimplemented\");\n            }\n\n            free(contract);\n        }\n        ContractArray_clear(module->owned_contracts);\n\n        furi_event_loop_free(module->loop);\n        free(module);\n    }\n}\n\nextern const ElfApiInterface js_event_loop_hashtable_api_interface;\n\nstatic const JsModuleDescriptor js_event_loop_desc = {\n    \"event_loop\",\n    js_event_loop_create,\n    js_event_loop_destroy,\n    &js_event_loop_hashtable_api_interface,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_event_loop_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_event_loop_ep(void) {\n    return &plugin_descriptor;\n}\n\nFuriEventLoop* js_event_loop_get_loop(JsEventLoop* loop) {\n    // porta: not the proudest function that i ever wrote\n    furi_check(loop);\n    return loop->loop;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_event_loop/js_event_loop.h",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include <furi/core/event_loop.h>\n#include <furi/core/event_loop_timer.h>\n\n/**\n * @file js_event_loop.h\n * \n * In JS interpreter code, `js_event_loop` always creates and maintains the\n * event loop. There are two ways in which other modules can integrate with this\n * loop:\n *   - Via contracts: The user of your module would have to acquire an opaque\n *     JS value from you and pass it to `js_event_loop`. This is useful for\n *     events that they user may be interested in. For more info, look at\n *     `JsEventLoopContract`. Also look at `js_event_loop_get_loop`, which\n *     you will need to unsubscribe the event loop from your object.\n *   - Directly: When your module is created, you can acquire an instance of\n *     `JsEventLoop` which you can use to acquire an instance of\n *     `FuriEventLoop` that you can manipulate directly, without the JS\n *     programmer having to pass contracts around. This is useful for\n *     \"behind-the-scenes\" events that the user does not need to know about. For\n *     more info, look at `js_event_loop_get_loop`.\n * \n * In both cases, your module is responsible for both instantiating,\n * unsubscribing and freeing the object that the event loop subscribes to.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct JsEventLoop JsEventLoop;\n\ntypedef enum {\n    JsEventLoopObjectTypeTimer,\n    JsEventLoopObjectTypeQueue,\n    JsEventLoopObjectTypeMutex,\n    JsEventLoopObjectTypeSemaphore,\n    JsEventLoopObjectTypeStream,\n} JsEventLoopObjectType;\n\ntypedef mjs_val_t (\n    *JsEventLoopTransformer)(struct mjs* mjs, FuriEventLoopObject* object, void* context);\n\ntypedef struct {\n    FuriEventLoopEvent event;\n    JsEventLoopTransformer transformer;\n    void* transformer_context;\n} JsEventLoopNonTimerContract;\n\ntypedef struct {\n    FuriEventLoopTimerType type;\n    uint32_t interval_ticks;\n} JsEventLoopTimerContract;\n\n/**\n * @brief Adapter for other JS modules that wish to integrate with the event\n * loop JS module\n * \n * If another module wishes to integrate with `js_event_loop`, it needs to\n * implement a function callable from JS that returns an mJS foreign pointer to\n * an instance of this structure. This value is then read by `event_loop`'s\n * `subscribe` function.\n * \n * There are two fundamental variants of this structure:\n *   - `object_type` is `JsEventLoopObjectTypeTimer`: the `timer` field is\n *     valid, and the `non_timer` field is invalid.\n *   - `object_type` is something else: the `timer` field is invalid, and the\n *     `non_timer` field is valid. `non_timer.event` will be passed to\n *     `furi_event_loop_subscribe`. `non_timer.transformer` will be called to\n *     transform an object into a JS value (called an item) that's passed to the\n *     JS callback. This is useful for example to take an item out of a message\n *     queue and pass it to JS code in a convenient format. If\n *     `non_timer.transformer` is NULL, the event loop will take semaphores and\n *     mutexes on its own.\n * \n * The producer of the contract is responsible for freeing both the contract and\n * the object that it points to when the interpreter is torn down.\n */\ntypedef struct {\n    JsForeignMagic magic; // <! `JsForeignMagic_JsEventLoopContract`\n    JsEventLoopObjectType object_type;\n    FuriEventLoopObject* object;\n    union {\n        JsEventLoopNonTimerContract non_timer;\n        JsEventLoopTimerContract timer;\n    };\n} JsEventLoopContract;\n\nstatic_assert(offsetof(JsEventLoopContract, magic) == 0);\n\n/**\n * @brief Gets the FuriEventLoop owned by a JsEventLoop\n * \n * This function is useful in case your JS module wishes to integrate with\n * the event loop without passing contracts through JS code. Your module will be\n * dynamically linked to this one if you use this function, but only if JS code\n * imports `event_loop` _before_ your module. An instance of `JsEventLoop` may\n * be obtained via `js_module_get`.\n */\nFuriEventLoop* js_event_loop_get_loop(JsEventLoop* loop);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/modules/js_event_loop/js_event_loop_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n#include \"js_event_loop_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(js_event_loop_api_table), \"Detected API method hash collision!\");\n\nextern \"C\" constexpr HashtableApiInterface js_event_loop_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    js_event_loop_api_table.cbegin(),\n    js_event_loop_api_table.cend(),\n};\n"
  },
  {
    "path": "applications/system/js_app/modules/js_event_loop/js_event_loop_api_table_i.h",
    "content": "#include \"js_event_loop.h\"\n\nstatic constexpr auto js_event_loop_api_table = sort(\n    create_array_t<sym_entry>(API_METHOD(js_event_loop_get_loop, FuriEventLoop*, (JsEventLoop*))));\n"
  },
  {
    "path": "applications/system/js_app/modules/js_flipper.c",
    "content": "#include \"../js_modules.h\" // IWYU pragma: keep\n#include <core/common_defines.h>\n#include <furi_hal_version.h>\n#include <power/power_service/power.h>\n\nstatic void js_flipper_get_model(struct mjs* mjs) {\n    mjs_val_t ret = mjs_mk_string(mjs, furi_hal_version_get_model_name(), ~0, true);\n    mjs_return(mjs, ret);\n}\n\nstatic void js_flipper_get_name(struct mjs* mjs) {\n    const char* name_str = furi_hal_version_get_name_ptr();\n    if(name_str == NULL) {\n        name_str = \"Unknown\";\n    }\n    mjs_val_t ret = mjs_mk_string(mjs, name_str, ~0, true);\n    mjs_return(mjs, ret);\n}\n\nstatic void js_flipper_get_battery(struct mjs* mjs) {\n    Power* power = furi_record_open(RECORD_POWER);\n    PowerInfo info;\n    power_get_info(power, &info);\n    furi_record_close(RECORD_POWER);\n    mjs_return(mjs, mjs_mk_number(mjs, info.charge));\n}\n\nvoid* js_flipper_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    mjs_val_t sdk_vsn = mjs_mk_array(mjs);\n    mjs_array_push(mjs, sdk_vsn, mjs_mk_number(mjs, JS_SDK_MAJOR));\n    mjs_array_push(mjs, sdk_vsn, mjs_mk_number(mjs, JS_SDK_MINOR));\n\n    mjs_val_t flipper_obj = mjs_mk_object(mjs);\n    *object = flipper_obj;\n    JS_ASSIGN_MULTI(mjs, flipper_obj) {\n        JS_FIELD(\"getModel\", MJS_MK_FN(js_flipper_get_model));\n        JS_FIELD(\"getName\", MJS_MK_FN(js_flipper_get_name));\n        JS_FIELD(\"getBatteryCharge\", MJS_MK_FN(js_flipper_get_battery));\n        JS_FIELD(\"firmwareVendor\", mjs_mk_string(mjs, JS_SDK_VENDOR, ~0, false));\n        JS_FIELD(\"jsSdkVersion\", sdk_vsn);\n    }\n\n    return (void*)1;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_flipper.h",
    "content": "#pragma once\n#include \"../js_thread_i.h\"\n\nvoid* js_flipper_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gpio.c",
    "content": "#include \"../js_modules.h\" // IWYU pragma: keep\n#include \"./js_event_loop/js_event_loop.h\"\n#include <furi_hal_gpio.h>\n#include <furi_hal_pwm.h>\n#include <furi_hal_resources.h>\n#include <expansion/expansion.h>\n#include <limits.h>\n#include <mlib/m-array.h>\n\n#define INTERRUPT_QUEUE_LEN 16\n\n/**\n * Per-pin control structure\n */\ntypedef struct {\n    const GpioPin* pin;\n    bool had_interrupt;\n    FuriSemaphore* interrupt_semaphore;\n    JsEventLoopContract* interrupt_contract;\n    FuriHalAdcChannel adc_channel;\n    FuriHalPwmOutputId pwm_output;\n    FuriHalAdcHandle* adc_handle;\n} JsGpioPinInst;\n\nARRAY_DEF(ManagedPinsArray, JsGpioPinInst*, M_PTR_OPLIST); //-V575\n#define M_OPL_ManagedPinsArray_t() ARRAY_OPLIST(ManagedPinsArray)\n\n/**\n * Per-module instance control structure\n */\ntypedef struct {\n    FuriEventLoop* loop;\n    ManagedPinsArray_t managed_pins;\n    FuriHalAdcHandle* adc_handle;\n} JsGpioInst;\n\n/**\n * @brief Interrupt callback\n */\nstatic void js_gpio_int_cb(void* arg) {\n    furi_assert(arg);\n    FuriSemaphore* semaphore = arg;\n    furi_semaphore_release(semaphore);\n}\n\n/**\n * @brief Initializes a GPIO pin according to the provided mode object\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let led = gpio.get(\"pc3\");\n * led.init({ direction: \"out\", outMode: \"push_pull\" });\n * ```\n */\nstatic void js_gpio_init(struct mjs* mjs) {\n    // direction variants\n    typedef enum {\n        JsGpioDirectionIn,\n        JsGpioDirectionOut,\n    } JsGpioDirection;\n    static const JsValueEnumVariant js_gpio_direction_variants[] = {\n        {\"in\", JsGpioDirectionIn},\n        {\"out\", JsGpioDirectionOut},\n    };\n    static const JsValueDeclaration js_gpio_direction =\n        JS_VALUE_ENUM(JsGpioDirection, js_gpio_direction_variants);\n\n    // inMode variants\n    typedef enum {\n        JsGpioInModeAnalog = (0 << 0),\n        JsGpioInModePlainDigital = (1 << 0),\n        JsGpioInModeInterrupt = (2 << 0),\n        JsGpioInModeEvent = (3 << 0),\n    } JsGpioInMode;\n    static const JsValueEnumVariant js_gpio_in_mode_variants[] = {\n        {\"analog\", JsGpioInModeAnalog},\n        {\"plain_digital\", JsGpioInModePlainDigital},\n        {\"interrupt\", JsGpioInModeInterrupt},\n        {\"event\", JsGpioInModeEvent},\n    };\n    static const JsValueDeclaration js_gpio_in_mode =\n        JS_VALUE_ENUM_W_DEFAULT(JsGpioInMode, js_gpio_in_mode_variants, JsGpioInModePlainDigital);\n\n    // outMode variants\n    typedef enum {\n        JsGpioOutModePushPull,\n        JsGpioOutModeOpenDrain,\n    } JsGpioOutMode;\n    static const JsValueEnumVariant js_gpio_out_mode_variants[] = {\n        {\"push_pull\", JsGpioOutModePushPull},\n        {\"open_drain\", JsGpioOutModeOpenDrain},\n    };\n    static const JsValueDeclaration js_gpio_out_mode =\n        JS_VALUE_ENUM_W_DEFAULT(JsGpioOutMode, js_gpio_out_mode_variants, JsGpioOutModeOpenDrain);\n\n    // edge variants\n    typedef enum {\n        JsGpioEdgeRising = (0 << 2),\n        JsGpioEdgeFalling = (1 << 2),\n        JsGpioEdgeBoth = (2 << 2),\n    } JsGpioEdge;\n    static const JsValueEnumVariant js_gpio_edge_variants[] = {\n        {\"rising\", JsGpioEdgeRising},\n        {\"falling\", JsGpioEdgeFalling},\n        {\"both\", JsGpioEdgeBoth},\n    };\n    static const JsValueDeclaration js_gpio_edge =\n        JS_VALUE_ENUM_W_DEFAULT(JsGpioEdge, js_gpio_edge_variants, JsGpioEdgeRising);\n\n    // pull variants\n    static const JsValueEnumVariant js_gpio_pull_variants[] = {\n        {\"up\", GpioPullUp},\n        {\"down\", GpioPullDown},\n    };\n    static const JsValueDeclaration js_gpio_pull =\n        JS_VALUE_ENUM_W_DEFAULT(GpioPull, js_gpio_pull_variants, GpioPullNo);\n\n    // complete mode object\n    static const JsValueObjectField js_gpio_mode_object_fields[] = {\n        {\"direction\", &js_gpio_direction},\n        {\"inMode\", &js_gpio_in_mode},\n        {\"outMode\", &js_gpio_out_mode},\n        {\"edge\", &js_gpio_edge},\n        {\"pull\", &js_gpio_pull},\n    };\n\n    // function args\n    static const JsValueDeclaration js_gpio_init_arg_list[] = {\n        JS_VALUE_OBJECT_W_DEFAULTS(js_gpio_mode_object_fields),\n    };\n    static const JsValueArguments js_gpio_init_args = JS_VALUE_ARGS(js_gpio_init_arg_list);\n\n    JsGpioDirection direction;\n    JsGpioInMode in_mode;\n    JsGpioOutMode out_mode;\n    JsGpioEdge edge;\n    GpioPull pull;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(\n        mjs, &js_gpio_init_args, &direction, &in_mode, &out_mode, &edge, &pull);\n\n    GpioMode mode;\n    if(direction == JsGpioDirectionOut) {\n        static const GpioMode js_gpio_out_mode_lut[] = {\n            [JsGpioOutModePushPull] = GpioModeOutputPushPull,\n            [JsGpioOutModeOpenDrain] = GpioModeOutputOpenDrain,\n        };\n        mode = js_gpio_out_mode_lut[out_mode];\n    } else {\n        static const GpioMode js_gpio_in_mode_lut[] = {\n            [JsGpioInModeAnalog] = GpioModeAnalog,\n            [JsGpioInModePlainDigital] = GpioModeInput,\n            [JsGpioInModeInterrupt | JsGpioEdgeRising] = GpioModeInterruptRise,\n            [JsGpioInModeInterrupt | JsGpioEdgeFalling] = GpioModeInterruptFall,\n            [JsGpioInModeInterrupt | JsGpioEdgeBoth] = GpioModeInterruptRiseFall,\n            [JsGpioInModeEvent | JsGpioEdgeRising] = GpioModeEventRise,\n            [JsGpioInModeEvent | JsGpioEdgeFalling] = GpioModeEventFall,\n            [JsGpioInModeEvent | JsGpioEdgeBoth] = GpioModeEventRiseFall,\n        };\n        mode = js_gpio_in_mode_lut[in_mode | edge];\n    }\n\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    furi_hal_gpio_init(manager_data->pin, mode, pull, GpioSpeedVeryHigh);\n}\n\n/**\n * @brief Writes a logic value to a GPIO pin \n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let led = gpio.get(\"pc3\");\n * led.init({ direction: \"out\", outMode: \"push_pull\" });\n * led.write(true);\n * ```\n */\nstatic void js_gpio_write(struct mjs* mjs) {\n    static const JsValueDeclaration js_gpio_write_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeBool),\n    };\n    static const JsValueArguments js_gpio_write_args = JS_VALUE_ARGS(js_gpio_write_arg_list);\n    bool level;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_write_args, &level);\n\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    furi_hal_gpio_write(manager_data->pin, level);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief Reads a logic value from a GPIO pin\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let button = gpio.get(\"pc1\");\n * button.init({ direction: \"in\" });\n * if(button.read())\n *     print(\"hi button!!!!!\");\n * ```\n */\nstatic void js_gpio_read(struct mjs* mjs) {\n    // get level\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    bool value = furi_hal_gpio_read(manager_data->pin);\n    mjs_return(mjs, mjs_mk_boolean(mjs, value));\n}\n\n/**\n * @brief Returns a event loop contract that can be used to listen to interrupts\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let button = gpio.get(\"pc1\");\n * let event_loop = require(\"event_loop\");\n * button.init({ direction: \"in\", pull: \"up\", inMode: \"interrupt\", edge: \"falling\" });\n * event_loop.subscribe(button.interrupt(), function (_) { print(\"Hi!\"); });\n * event_loop.run();\n * ```\n */\nstatic void js_gpio_interrupt(struct mjs* mjs) {\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n\n    // interrupt handling\n    if(!manager_data->had_interrupt) {\n        furi_hal_gpio_add_int_callback(\n            manager_data->pin, js_gpio_int_cb, manager_data->interrupt_semaphore);\n        furi_hal_gpio_enable_int_callback(manager_data->pin);\n        manager_data->had_interrupt = true;\n    }\n\n    // make contract\n    JsEventLoopContract* contract = malloc(sizeof(JsEventLoopContract));\n    *contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeSemaphore,\n        .object = manager_data->interrupt_semaphore,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n            },\n    };\n    manager_data->interrupt_contract = contract;\n    mjs_return(mjs, mjs_mk_foreign(mjs, contract));\n}\n\n/**\n * @brief Reads a voltage from a GPIO pin in analog mode\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let pot = gpio.get(\"pc0\");\n * pot.init({ direction: \"in\", inMode: \"analog\" });\n * print(\"voltage:\" pot.readAnalog(), \"mV\");\n * ```\n */\nstatic void js_gpio_read_analog(struct mjs* mjs) {\n    // get mV (ADC is configured for 12 bits and 2048 mV max)\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    uint16_t millivolts =\n        furi_hal_adc_read(manager_data->adc_handle, manager_data->adc_channel) / 2;\n    mjs_return(mjs, mjs_mk_number(mjs, (double)millivolts));\n}\n\n/**\n * @brief Determines whether this pin supports PWM\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * assert_eq(true, gpio.get(\"pa4\").isPwmSupported());\n * assert_eq(false, gpio.get(\"pa5\").isPwmSupported());\n * ```\n */\nstatic void js_gpio_is_pwm_supported(struct mjs* mjs) {\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, manager_data->pwm_output != FuriHalPwmOutputIdNone));\n}\n\n/**\n * @brief Sets PWM parameters and starts the PWM\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let pa4 = gpio.get(\"pa4\");\n * pa4.pwmWrite(10000, 50);\n * ```\n */\nstatic void js_gpio_pwm_write(struct mjs* mjs) {\n    static const JsValueDeclaration js_gpio_pwm_write_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_gpio_pwm_write_args =\n        JS_VALUE_ARGS(js_gpio_pwm_write_arg_list);\n    int32_t frequency, duty;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_pwm_write_args, &frequency, &duty);\n\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"PWM is not supported on this pin\");\n    }\n\n    if(furi_hal_pwm_is_running(manager_data->pwm_output)) {\n        furi_hal_pwm_set_params(manager_data->pwm_output, frequency, duty);\n    } else {\n        furi_hal_pwm_start(manager_data->pwm_output, frequency, duty);\n    }\n}\n\n/**\n * @brief Determines whether PWM is running\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * assert_eq(false, gpio.get(\"pa4\").isPwmRunning());\n * ```\n */\nstatic void js_gpio_is_pwm_running(struct mjs* mjs) {\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"PWM is not supported on this pin\");\n    }\n\n    mjs_return(mjs, mjs_mk_boolean(mjs, furi_hal_pwm_is_running(manager_data->pwm_output)));\n}\n\n/**\n * @brief Stops PWM\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let pa4 = gpio.get(\"pa4\");\n * pa4.pwmWrite(10000, 50);\n * pa4.pwmStop();\n * ```\n */\nstatic void js_gpio_pwm_stop(struct mjs* mjs) {\n    JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);\n    if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"PWM is not supported on this pin\");\n    }\n\n    furi_hal_pwm_stop(manager_data->pwm_output);\n}\n\n/**\n * @brief Returns an object that manages a specified pin.\n * \n * Example usage:\n * \n * ```js\n * let gpio = require(\"gpio\");\n * let led = gpio.get(\"pc3\");\n * ```\n */\nstatic void js_gpio_get(struct mjs* mjs) {\n    static const JsValueDeclaration js_gpio_get_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_gpio_get_args = JS_VALUE_ARGS(js_gpio_get_arg_list);\n    mjs_val_t name_arg;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_get_args, &name_arg);\n\n    const char* name_string = mjs_get_string(mjs, &name_arg, NULL);\n    const GpioPinRecord* pin_record = NULL;\n\n    // parse input argument to a pin pointer\n    if(name_string) {\n        pin_record = furi_hal_resources_pin_by_name(name_string);\n    } else if(mjs_is_number(name_arg)) {\n        int name_int = mjs_get_int(mjs, name_arg);\n        pin_record = furi_hal_resources_pin_by_number(name_int);\n    } else {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"Must be either a string or a number\");\n    }\n\n    if(!pin_record) JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"Pin not found on device\");\n    if(pin_record->debug)\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"Pin is used for debugging\");\n\n    // return pin manager object\n    JsGpioInst* module = JS_GET_CONTEXT(mjs);\n    mjs_val_t manager = mjs_mk_object(mjs);\n    JsGpioPinInst* manager_data = malloc(sizeof(JsGpioPinInst));\n    manager_data->pin = pin_record->pin;\n    manager_data->interrupt_semaphore = furi_semaphore_alloc(UINT32_MAX, 0);\n    manager_data->adc_handle = module->adc_handle;\n    manager_data->adc_channel = pin_record->channel;\n    manager_data->pwm_output = pin_record->pwm_output;\n    JS_ASSIGN_MULTI(mjs, manager) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, manager_data));\n        JS_FIELD(\"init\", MJS_MK_FN(js_gpio_init));\n        JS_FIELD(\"write\", MJS_MK_FN(js_gpio_write));\n        JS_FIELD(\"read\", MJS_MK_FN(js_gpio_read));\n        JS_FIELD(\"readAnalog\", MJS_MK_FN(js_gpio_read_analog));\n        JS_FIELD(\"interrupt\", MJS_MK_FN(js_gpio_interrupt));\n        JS_FIELD(\"isPwmSupported\", MJS_MK_FN(js_gpio_is_pwm_supported));\n        JS_FIELD(\"pwmWrite\", MJS_MK_FN(js_gpio_pwm_write));\n        JS_FIELD(\"isPwmRunning\", MJS_MK_FN(js_gpio_is_pwm_running));\n        JS_FIELD(\"pwmStop\", MJS_MK_FN(js_gpio_pwm_stop));\n    }\n    mjs_return(mjs, manager);\n\n    // remember pin\n    ManagedPinsArray_push_back(module->managed_pins, manager_data);\n}\n\nstatic void* js_gpio_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    JsEventLoop* js_loop = js_module_get(modules, \"event_loop\");\n    if(M_UNLIKELY(!js_loop)) return NULL;\n    FuriEventLoop* loop = js_event_loop_get_loop(js_loop);\n\n    JsGpioInst* module = malloc(sizeof(JsGpioInst));\n    ManagedPinsArray_init(module->managed_pins);\n    module->adc_handle = furi_hal_adc_acquire();\n    module->loop = loop;\n    furi_hal_adc_configure(module->adc_handle);\n\n    mjs_val_t gpio_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, gpio_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, module));\n    mjs_set(mjs, gpio_obj, \"get\", ~0, MJS_MK_FN(js_gpio_get));\n    *object = gpio_obj;\n\n    return (void*)module;\n}\n\nstatic void js_gpio_destroy(void* inst) {\n    furi_assert(inst);\n    JsGpioInst* module = (JsGpioInst*)inst;\n\n    // reset pins\n    for\n        M_EACH(item, module->managed_pins, ManagedPinsArray_t) {\n            JsGpioPinInst* manager_data = *item;\n\n            if(manager_data->had_interrupt) {\n                furi_hal_gpio_disable_int_callback(manager_data->pin);\n                furi_hal_gpio_remove_int_callback(manager_data->pin);\n            }\n            if(manager_data->pwm_output != FuriHalPwmOutputIdNone) {\n                if(furi_hal_pwm_is_running(manager_data->pwm_output))\n                    furi_hal_pwm_stop(manager_data->pwm_output);\n            }\n            furi_hal_gpio_init(manager_data->pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n\n            furi_event_loop_maybe_unsubscribe(module->loop, manager_data->interrupt_semaphore);\n            furi_semaphore_free(manager_data->interrupt_semaphore);\n\n            free(manager_data->interrupt_contract);\n            free(manager_data);\n        }\n\n    // free buffers\n    furi_hal_adc_release(module->adc_handle);\n    ManagedPinsArray_clear(module->managed_pins);\n    free(module);\n}\n\nstatic const JsModuleDescriptor js_gpio_desc = {\n    \"gpio\",\n    js_gpio_create,\n    js_gpio_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_gpio_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_gpio_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/button_menu.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/button_menu.h>\n#include <toolbox/str_buffer.h>\n\ntypedef struct {\n    int32_t next_index;\n    StrBuffer str_buffer;\n\n    FuriMessageQueue* input_queue;\n    JsEventLoopContract contract;\n} JsBtnMenuContext;\n\ntypedef struct {\n    int32_t index;\n    InputType input_type;\n} JsBtnMenuEvent;\n\nstatic const char* js_input_type_to_str(InputType type) {\n    switch(type) {\n    case InputTypePress:\n        return \"press\";\n    case InputTypeRelease:\n        return \"release\";\n    case InputTypeShort:\n        return \"short\";\n    case InputTypeLong:\n        return \"long\";\n    case InputTypeRepeat:\n        return \"repeat\";\n    default:\n        furi_crash();\n    }\n}\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsBtnMenuContext* context) {\n    UNUSED(context);\n    JsBtnMenuEvent event;\n    furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);\n\n    mjs_val_t event_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, event_obj) {\n        JS_FIELD(\"index\", mjs_mk_number(mjs, event.index));\n        JS_FIELD(\"type\", mjs_mk_string(mjs, js_input_type_to_str(event.input_type), ~0, false));\n    }\n\n    return event_obj;\n}\n\nstatic void input_callback(void* ctx, int32_t index, InputType type) {\n    JsBtnMenuContext* context = ctx;\n    JsBtnMenuEvent event = {\n        .index = index,\n        .input_type = type,\n    };\n    furi_check(furi_message_queue_put(context->input_queue, &event, 0) == FuriStatusOk);\n}\n\nstatic bool matrix_header_assign(\n    struct mjs* mjs,\n    ButtonMenu* menu,\n    JsViewPropValue value,\n    JsBtnMenuContext* context) {\n    UNUSED(mjs);\n    button_menu_set_header(menu, str_buffer_make_owned_clone(&context->str_buffer, value.string));\n    return true;\n}\n\nstatic bool js_button_menu_add_child(\n    struct mjs* mjs,\n    ButtonMenu* menu,\n    JsBtnMenuContext* context,\n    mjs_val_t child_obj) {\n    static const JsValueEnumVariant js_button_menu_item_type_variants[] = {\n        {\"common\", ButtonMenuItemTypeCommon},\n        {\"control\", ButtonMenuItemTypeControl},\n    };\n    static const JsValueDeclaration js_button_menu_item_type =\n        JS_VALUE_ENUM(ButtonMenuItemType, js_button_menu_item_type_variants);\n\n    static const JsValueDeclaration js_button_menu_string = JS_VALUE_SIMPLE(JsValueTypeString);\n\n    static const JsValueObjectField js_button_menu_child_fields[] = {\n        {\"type\", &js_button_menu_item_type},\n        {\"label\", &js_button_menu_string},\n    };\n    static const JsValueDeclaration js_button_menu_child =\n        JS_VALUE_OBJECT(js_button_menu_child_fields);\n\n    ButtonMenuItemType item_type;\n    const char* label;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&js_button_menu_child),\n        JsValueParseFlagReturnOnError,\n        &status,\n        &child_obj,\n        &item_type,\n        &label);\n    if(status != JsValueParseStatusOk) return false;\n\n    button_menu_add_item(\n        menu,\n        str_buffer_make_owned_clone(&context->str_buffer, label),\n        context->next_index++,\n        input_callback,\n        item_type,\n        context);\n\n    return true;\n}\n\nstatic void js_button_menu_reset_children(ButtonMenu* menu, JsBtnMenuContext* context) {\n    context->next_index = 0;\n    button_menu_reset(menu);\n    str_buffer_clear_all_clones(&context->str_buffer);\n}\n\nstatic JsBtnMenuContext* ctx_make(struct mjs* mjs, ButtonMenu* menu, mjs_val_t view_obj) {\n    UNUSED(menu);\n    JsBtnMenuContext* context = malloc(sizeof(JsBtnMenuContext));\n    *context = (JsBtnMenuContext){\n        .next_index = 0,\n        .str_buffer = {0},\n        .input_queue = furi_message_queue_alloc(1, sizeof(JsBtnMenuEvent)),\n    };\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->input_queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(ButtonMenu* input, JsBtnMenuContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_queue);\n    furi_message_queue_free(context->input_queue);\n    str_buffer_clear_all_clones(&context->str_buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)button_menu_alloc,\n    .free = (JsViewFree)button_menu_free,\n    .get_view = (JsViewGetView)button_menu_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .add_child = (JsViewAddChild)js_button_menu_add_child,\n    .reset_children = (JsViewResetChildren)js_button_menu_reset_children,\n    .prop_cnt = 1,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)matrix_header_assign},\n    }};\n\nJS_GUI_VIEW_DEF(button_menu, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/button_panel.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/button_panel.h>\n#include <toolbox/str_buffer.h>\n\ntypedef struct {\n    size_t matrix_x, matrix_y;\n    int32_t next_index;\n    StrBuffer str_buffer;\n\n    FuriMessageQueue* input_queue;\n    JsEventLoopContract contract;\n} JsBtnPanelContext;\n\ntypedef struct {\n    int32_t index;\n    InputType input_type;\n} JsBtnPanelEvent;\n\nstatic const char* js_input_type_to_str(InputType type) {\n    switch(type) {\n    case InputTypePress:\n        return \"press\";\n    case InputTypeRelease:\n        return \"release\";\n    case InputTypeShort:\n        return \"short\";\n    case InputTypeLong:\n        return \"long\";\n    case InputTypeRepeat:\n        return \"repeat\";\n    default:\n        furi_crash();\n    }\n}\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsBtnPanelContext* context) {\n    UNUSED(context);\n    JsBtnPanelEvent event;\n    furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);\n\n    mjs_val_t event_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, event_obj) {\n        JS_FIELD(\"index\", mjs_mk_number(mjs, event.index));\n        JS_FIELD(\"type\", mjs_mk_string(mjs, js_input_type_to_str(event.input_type), ~0, false));\n    }\n\n    return event_obj;\n}\n\nstatic void input_callback(void* ctx, int32_t index, InputType type) {\n    JsBtnPanelContext* context = ctx;\n    JsBtnPanelEvent event = {\n        .index = index,\n        .input_type = type,\n    };\n    furi_check(furi_message_queue_put(context->input_queue, &event, 0) == FuriStatusOk);\n}\n\nstatic bool matrix_size_x_assign(\n    struct mjs* mjs,\n    ButtonPanel* panel,\n    JsViewPropValue value,\n    JsBtnPanelContext* context) {\n    UNUSED(mjs);\n    context->matrix_x = value.number;\n    button_panel_reserve(panel, context->matrix_x, context->matrix_y);\n    return true;\n}\n\nstatic bool matrix_size_y_assign(\n    struct mjs* mjs,\n    ButtonPanel* panel,\n    JsViewPropValue value,\n    JsBtnPanelContext* context) {\n    UNUSED(mjs);\n    context->matrix_y = value.number;\n    button_panel_reserve(panel, context->matrix_x, context->matrix_y);\n    return true;\n}\n\nstatic bool js_button_panel_add_child(\n    struct mjs* mjs,\n    ButtonPanel* panel,\n    JsBtnPanelContext* context,\n    mjs_val_t child_obj) {\n    typedef enum {\n        JsButtonPanelChildTypeButton,\n        JsButtonPanelChildTypeLabel,\n        JsButtonPanelChildTypeIcon,\n    } JsButtonPanelChildType;\n    static const JsValueEnumVariant js_button_panel_child_type_variants[] = {\n        {\"button\", JsButtonPanelChildTypeButton},\n        {\"label\", JsButtonPanelChildTypeLabel},\n        {\"icon\", JsButtonPanelChildTypeIcon},\n    };\n    static const JsValueDeclaration js_button_panel_child_type =\n        JS_VALUE_ENUM(JsButtonPanelChildType, js_button_panel_child_type_variants);\n\n    static const JsValueDeclaration js_button_panel_number = JS_VALUE_SIMPLE(JsValueTypeInt32);\n    static const JsValueObjectField js_button_panel_common_fields[] = {\n        {\"type\", &js_button_panel_child_type},\n        {\"x\", &js_button_panel_number},\n        {\"y\", &js_button_panel_number},\n    };\n    static const JsValueDeclaration js_button_panel_common =\n        JS_VALUE_OBJECT(js_button_panel_common_fields);\n\n    static const JsValueDeclaration js_button_panel_pointer =\n        JS_VALUE_SIMPLE(JsValueTypeRawPointer);\n    static const JsValueObjectField js_button_panel_button_fields[] = {\n        {\"matrixX\", &js_button_panel_number},\n        {\"matrixY\", &js_button_panel_number},\n        {\"icon\", &js_button_panel_pointer},\n        {\"iconSelected\", &js_button_panel_pointer},\n    };\n    static const JsValueDeclaration js_button_panel_button =\n        JS_VALUE_OBJECT(js_button_panel_button_fields);\n\n    static const JsValueDeclaration js_button_panel_string = JS_VALUE_SIMPLE(JsValueTypeString);\n    static const JsValueObjectField js_button_panel_label_fields[] = {\n        {\"text\", &js_button_panel_string},\n        {\"font\", &js_gui_font_declaration},\n    };\n    static const JsValueDeclaration js_button_panel_label =\n        JS_VALUE_OBJECT(js_button_panel_label_fields);\n\n    static const JsValueObjectField js_button_panel_icon_fields[] = {\n        {\"icon\", &js_button_panel_pointer},\n    };\n    static const JsValueDeclaration js_button_panel_icon =\n        JS_VALUE_OBJECT(js_button_panel_icon_fields);\n\n    JsButtonPanelChildType child_type;\n    int32_t x, y;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_common),\n        JsValueParseFlagReturnOnError,\n        &status,\n        &child_obj,\n        &child_type,\n        &x,\n        &y);\n    if(status != JsValueParseStatusOk) return false;\n\n    switch(child_type) {\n    case JsButtonPanelChildTypeButton: {\n        int32_t matrix_x, matrix_y;\n        const Icon *icon, *icon_selected;\n        JS_VALUE_PARSE(\n            mjs,\n            JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_button),\n            JsValueParseFlagReturnOnError,\n            &status,\n            &child_obj,\n            &matrix_x,\n            &matrix_y,\n            &icon,\n            &icon_selected);\n        if(status != JsValueParseStatusOk) return false;\n        button_panel_add_item(\n            panel,\n            context->next_index++,\n            matrix_x,\n            matrix_y,\n            x,\n            y,\n            icon,\n            icon_selected,\n            (ButtonItemCallback)input_callback,\n            context);\n        break;\n    }\n\n    case JsButtonPanelChildTypeLabel: {\n        const char* text;\n        Font font;\n        JS_VALUE_PARSE(\n            mjs,\n            JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_label),\n            JsValueParseFlagReturnOnError,\n            &status,\n            &child_obj,\n            &text,\n            &font);\n        if(status != JsValueParseStatusOk) return false;\n        button_panel_add_label(\n            panel, x, y, font, str_buffer_make_owned_clone(&context->str_buffer, text));\n        break;\n    }\n\n    case JsButtonPanelChildTypeIcon: {\n        const Icon* icon;\n        JS_VALUE_PARSE(\n            mjs,\n            JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_icon),\n            JsValueParseFlagReturnOnError,\n            &status,\n            &child_obj,\n            &icon);\n        if(status != JsValueParseStatusOk) return false;\n        button_panel_add_icon(panel, x, y, icon);\n        break;\n    }\n    }\n\n    return true;\n}\n\nstatic void js_button_panel_reset_children(ButtonPanel* panel, JsBtnPanelContext* context) {\n    context->next_index = 0;\n    button_panel_reset(panel);\n    button_panel_reserve(panel, context->matrix_x, context->matrix_y);\n    str_buffer_clear_all_clones(&context->str_buffer);\n}\n\nstatic JsBtnPanelContext* ctx_make(struct mjs* mjs, ButtonPanel* panel, mjs_val_t view_obj) {\n    UNUSED(panel);\n    JsBtnPanelContext* context = malloc(sizeof(JsBtnPanelContext));\n    *context = (JsBtnPanelContext){\n        .matrix_x = 1,\n        .matrix_y = 1,\n        .next_index = 0,\n        .str_buffer = {0},\n        .input_queue = furi_message_queue_alloc(1, sizeof(JsBtnPanelEvent)),\n    };\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->input_queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(ButtonPanel* input, JsBtnPanelContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_queue);\n    furi_message_queue_free(context->input_queue);\n    str_buffer_clear_all_clones(&context->str_buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)button_panel_alloc,\n    .free = (JsViewFree)button_panel_free,\n    .get_view = (JsViewGetView)button_panel_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .add_child = (JsViewAddChild)js_button_panel_add_child,\n    .reset_children = (JsViewResetChildren)js_button_panel_reset_children,\n    .prop_cnt = 2,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"matrixSizeX\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)matrix_size_x_assign},\n        (JsViewPropDescriptor){\n            .name = \"matrixSizeY\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)matrix_size_y_assign},\n    }};\n\nJS_GUI_VIEW_DEF(button_panel, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/byte_input.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/byte_input.h>\n\n#define DEFAULT_BUF_SZ 4\n\ntypedef struct {\n    uint8_t* buffer;\n    size_t buffer_size;\n    size_t default_data_size;\n    FuriString* header;\n    FuriSemaphore* input_semaphore;\n    JsEventLoopContract contract;\n} JsByteKbContext;\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriSemaphore* semaphore, JsByteKbContext* context) {\n    furi_check(furi_semaphore_acquire(semaphore, 0) == FuriStatusOk);\n    return mjs_mk_array_buf(mjs, (char*)context->buffer, context->buffer_size);\n}\n\nstatic void input_callback(JsByteKbContext* context) {\n    furi_semaphore_release(context->input_semaphore);\n}\n\nstatic bool header_assign(\n    struct mjs* mjs,\n    ByteInput* input,\n    JsViewPropValue value,\n    JsByteKbContext* context) {\n    UNUSED(mjs);\n    furi_string_set(context->header, value.string);\n    byte_input_set_header_text(input, furi_string_get_cstr(context->header));\n    return true;\n}\n\nstatic bool\n    len_assign(struct mjs* mjs, ByteInput* input, JsViewPropValue value, JsByteKbContext* context) {\n    UNUSED(mjs);\n    UNUSED(input);\n    size_t new_buffer_size = value.number;\n    if(new_buffer_size < context->default_data_size) {\n        // Avoid confusing parameters from user\n        mjs_prepend_errorf(\n            mjs, MJS_BAD_ARGS_ERROR, \"length must be larger than defaultData length\");\n        return false;\n    }\n    context->buffer_size = new_buffer_size;\n    context->buffer = realloc(context->buffer, context->buffer_size); //-V701\n    byte_input_set_result_callback(\n        input,\n        (ByteInputCallback)input_callback,\n        NULL,\n        context,\n        context->buffer,\n        context->buffer_size);\n    return true;\n}\n\nstatic bool default_data_assign(\n    struct mjs* mjs,\n    ByteInput* input,\n    JsViewPropValue value,\n    JsByteKbContext* context) {\n    UNUSED(mjs);\n\n    mjs_val_t array_buf = value.term;\n    if(mjs_is_data_view(array_buf)) {\n        array_buf = mjs_dataview_get_buf(mjs, array_buf);\n    }\n    char* default_data = mjs_array_buf_get_ptr(mjs, array_buf, &context->default_data_size);\n    if(context->buffer_size < context->default_data_size) {\n        // Ensure buffer is large enough for defaultData\n        context->buffer_size = context->default_data_size;\n        context->buffer = realloc(context->buffer, context->buffer_size); //-V701\n    }\n    memcpy(context->buffer, (uint8_t*)default_data, context->default_data_size);\n    if(context->buffer_size > context->default_data_size) {\n        // Reset previous data after defaultData\n        memset(\n            context->buffer + context->default_data_size,\n            0x00,\n            context->buffer_size - context->default_data_size);\n    }\n\n    byte_input_set_result_callback(\n        input,\n        (ByteInputCallback)input_callback,\n        NULL,\n        context,\n        context->buffer,\n        context->buffer_size);\n    return true;\n}\n\nstatic JsByteKbContext* ctx_make(struct mjs* mjs, ByteInput* input, mjs_val_t view_obj) {\n    JsByteKbContext* context = malloc(sizeof(JsByteKbContext));\n    *context = (JsByteKbContext){\n        .buffer_size = DEFAULT_BUF_SZ,\n        .buffer = malloc(DEFAULT_BUF_SZ),\n        .header = furi_string_alloc(),\n        .input_semaphore = furi_semaphore_alloc(1, 0),\n    };\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeSemaphore,\n        .object = context->input_semaphore,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    byte_input_set_result_callback(\n        input,\n        (ByteInputCallback)input_callback,\n        NULL,\n        context,\n        context->buffer,\n        context->buffer_size);\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(ByteInput* input, JsByteKbContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_semaphore);\n    furi_semaphore_free(context->input_semaphore);\n    furi_string_free(context->header);\n    free(context->buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)byte_input_alloc,\n    .free = (JsViewFree)byte_input_free,\n    .get_view = (JsViewGetView)byte_input_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 3,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n        (JsViewPropDescriptor){\n            .name = \"length\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)len_assign},\n        (JsViewPropDescriptor){\n            .name = \"defaultData\",\n            .type = JsViewPropTypeTypedArr,\n            .assign = (JsViewPropAssign)default_data_assign},\n    }};\n\nJS_GUI_VIEW_DEF(byte_input, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/dialog.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/dialog_ex.h>\n\n#define QUEUE_LEN 2\n\ntypedef struct {\n    FuriMessageQueue* queue;\n    JsEventLoopContract contract;\n} JsDialogCtx;\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsDialogCtx* context) {\n    UNUSED(context);\n    DialogExResult result;\n    furi_check(furi_message_queue_get(queue, &result, 0) == FuriStatusOk);\n    const char* string;\n    if(result == DialogExResultLeft) {\n        string = \"left\";\n    } else if(result == DialogExResultCenter) {\n        string = \"center\";\n    } else if(result == DialogExResultRight) {\n        string = \"right\";\n    } else {\n        furi_crash();\n    }\n    return mjs_mk_string(mjs, string, ~0, false);\n}\n\nstatic void input_callback(DialogExResult result, JsDialogCtx* context) {\n    furi_check(furi_message_queue_put(context->queue, &result, 0) == FuriStatusOk);\n}\n\nstatic bool\n    header_assign(struct mjs* mjs, DialogEx* dialog, JsViewPropValue value, JsDialogCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    dialog_ex_set_header(dialog, value.string, 64, 0, AlignCenter, AlignTop);\n    return true;\n}\n\nstatic bool\n    text_assign(struct mjs* mjs, DialogEx* dialog, JsViewPropValue value, JsDialogCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    dialog_ex_set_text(dialog, value.string, 64, 32, AlignCenter, AlignCenter);\n    return true;\n}\n\nstatic bool\n    left_assign(struct mjs* mjs, DialogEx* dialog, JsViewPropValue value, JsDialogCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    dialog_ex_set_left_button_text(dialog, value.string);\n    return true;\n}\nstatic bool\n    center_assign(struct mjs* mjs, DialogEx* dialog, JsViewPropValue value, JsDialogCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    dialog_ex_set_center_button_text(dialog, value.string);\n    return true;\n}\nstatic bool\n    right_assign(struct mjs* mjs, DialogEx* dialog, JsViewPropValue value, JsDialogCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    dialog_ex_set_right_button_text(dialog, value.string);\n    return true;\n}\n\nstatic JsDialogCtx* ctx_make(struct mjs* mjs, DialogEx* dialog, mjs_val_t view_obj) {\n    JsDialogCtx* context = malloc(sizeof(JsDialogCtx));\n    context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(DialogExResult));\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n            },\n    };\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    dialog_ex_set_result_callback(dialog, (DialogExResultCallback)input_callback);\n    dialog_ex_set_context(dialog, context);\n    return context;\n}\n\nstatic void ctx_destroy(DialogEx* input, JsDialogCtx* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->queue);\n    furi_message_queue_free(context->queue);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)dialog_ex_alloc,\n    .free = (JsViewFree)dialog_ex_free,\n    .get_view = (JsViewGetView)dialog_ex_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 5,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n        (JsViewPropDescriptor){\n            .name = \"text\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)text_assign},\n        (JsViewPropDescriptor){\n            .name = \"left\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)left_assign},\n        (JsViewPropDescriptor){\n            .name = \"center\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)center_assign},\n        (JsViewPropDescriptor){\n            .name = \"right\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)right_assign},\n    }};\n\nJS_GUI_VIEW_DEF(dialog, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/empty_screen.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include <gui/modules/empty_screen.h>\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)empty_screen_alloc,\n    .free = (JsViewFree)empty_screen_free,\n    .get_view = (JsViewGetView)empty_screen_get_view,\n    .prop_cnt = 0,\n    .props = {},\n};\nJS_GUI_VIEW_DEF(empty_screen, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/file_picker.c",
    "content": "#include \"../../js_modules.h\"\n#include <dialogs/dialogs.h>\n#include <assets_icons.h>\n\nstatic void js_gui_file_picker_pick_file(struct mjs* mjs) {\n    static const JsValueDeclaration js_picker_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE(JsValueTypeString),\n    };\n    static const JsValueArguments js_picker_args = JS_VALUE_ARGS(js_picker_arg_list);\n\n    const char *base_path, *extension;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_picker_args, &base_path, &extension);\n\n    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);\n    const DialogsFileBrowserOptions browser_options = {\n        .extension = extension,\n        .icon = &I_file_10px,\n        .base_path = base_path,\n    };\n    FuriString* path = furi_string_alloc_set(base_path);\n    if(dialog_file_browser_show(dialogs, path, path, &browser_options)) {\n        mjs_return(mjs, mjs_mk_string(mjs, furi_string_get_cstr(path), ~0, true));\n    } else {\n        mjs_return(mjs, MJS_UNDEFINED);\n    }\n    furi_string_free(path);\n    furi_record_close(RECORD_DIALOGS);\n}\n\nstatic void* js_gui_file_picker_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    *object = mjs_mk_object(mjs);\n    mjs_set(mjs, *object, \"pickFile\", ~0, MJS_MK_FN(js_gui_file_picker_pick_file));\n    return NULL;\n}\n\nstatic const JsModuleDescriptor js_gui_file_picker_desc = {\n    \"gui__file_picker\",\n    js_gui_file_picker_create,\n    NULL,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_gui_file_picker_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_gui_file_picker_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/icon.c",
    "content": "#include \"../../js_modules.h\"\n#include <assets_icons.h>\n#include <core/dangerous_defines.h>\n#include <gui/icon_i.h>\n#include <m-list.h>\n\ntypedef struct {\n    const char* name;\n    const Icon* data;\n} IconDefinition;\n\n#define ICON_DEF(icon)                   \\\n    (IconDefinition) {                   \\\n        .name = #icon, .data = &I_##icon \\\n    }\n\n#define ANIM_ICON_DEF(icon)              \\\n    (IconDefinition) {                   \\\n        .name = #icon, .data = &A_##icon \\\n    }\n\nstatic const IconDefinition builtin_icons[] = {\n    ICON_DEF(DolphinWait_59x54),\n    ICON_DEF(js_script_10px),\n    ICON_DEF(off_19x20),\n    ICON_DEF(off_hover_19x20),\n    ICON_DEF(power_19x20),\n    ICON_DEF(power_hover_19x20),\n    ANIM_ICON_DEF(Settings_14),\n};\n\n// Firmware's Icon struct needs a frames array, and uses a small CompressHeader\n// Here we use a variable size allocation to add the uncompressed data in same allocation\n// Also use a one-long array pointing to later in the same struct as the frames array\n// CompressHeader includes a first is_compressed byte so we don't need to compress (.fxbm is uncompressed)\ntypedef struct FURI_PACKED {\n    Icon icon;\n    uint8_t* frames[1];\n    struct {\n        uint8_t is_compressed;\n        uint8_t uncompressed_data[];\n    } frame;\n} FxbmIconWrapper;\n\nLIST_DEF(FxbmIconWrapperList, FxbmIconWrapper*, M_PTR_OPLIST); // NOLINT\n#define M_OPL_FxbmIconWrapperList_t() LIST_OPLIST(FxbmIconWrapperList)\n\ntypedef struct {\n    FxbmIconWrapperList_t fxbm_list;\n} JsGuiIconInst;\n\nstatic const JsValueDeclaration js_icon_get_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeString),\n};\nstatic const JsValueArguments js_icon_get_args = JS_VALUE_ARGS(js_icon_get_arg_list);\n\nstatic void js_gui_icon_get_builtin(struct mjs* mjs) {\n    const char* icon_name;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &icon_name);\n\n    for(size_t i = 0; i < COUNT_OF(builtin_icons); i++) {\n        if(strcmp(icon_name, builtin_icons[i].name) == 0) {\n            mjs_return(mjs, mjs_mk_foreign(mjs, (void*)builtin_icons[i].data));\n            return;\n        }\n    }\n\n    JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"no such built-in icon\");\n}\n\nstatic void js_gui_icon_load_fxbm(struct mjs* mjs) {\n    const char* fxbm_path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &fxbm_path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n    FxbmIconWrapper* fxbm = NULL;\n\n    do {\n        if(!storage_file_open(file, fxbm_path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n            break;\n        }\n\n        struct {\n            uint32_t size; // Total following size including width and height values\n            uint32_t width;\n            uint32_t height;\n        } fxbm_header;\n        if(storage_file_read(file, &fxbm_header, sizeof(fxbm_header)) != sizeof(fxbm_header)) {\n            break;\n        }\n\n        size_t frame_size = fxbm_header.size - sizeof(uint32_t) * 2;\n        fxbm = malloc(sizeof(FxbmIconWrapper) + frame_size);\n        if(storage_file_read(file, fxbm->frame.uncompressed_data, frame_size) != frame_size) {\n            free(fxbm);\n            fxbm = NULL;\n            break;\n        }\n\n        FURI_CONST_ASSIGN(fxbm->icon.width, fxbm_header.width);\n        FURI_CONST_ASSIGN(fxbm->icon.height, fxbm_header.height);\n        FURI_CONST_ASSIGN(fxbm->icon.frame_count, 1);\n        FURI_CONST_ASSIGN(fxbm->icon.frame_rate, 1);\n        FURI_CONST_ASSIGN_PTR(fxbm->icon.frames, fxbm->frames);\n        fxbm->frames[0] = (void*)&fxbm->frame;\n        fxbm->frame.is_compressed = false;\n    } while(false);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    if(!fxbm) {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"could not load .fxbm icon\");\n    }\n\n    JsGuiIconInst* js_icon = JS_GET_CONTEXT(mjs);\n    FxbmIconWrapperList_push_back(js_icon->fxbm_list, fxbm);\n    mjs_return(mjs, mjs_mk_foreign(mjs, (void*)&fxbm->icon));\n}\n\nstatic void* js_gui_icon_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    JsGuiIconInst* js_icon = malloc(sizeof(JsGuiIconInst));\n    FxbmIconWrapperList_init(js_icon->fxbm_list);\n    *object = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, *object) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_icon));\n        JS_FIELD(\"getBuiltin\", MJS_MK_FN(js_gui_icon_get_builtin));\n        JS_FIELD(\"loadFxbm\", MJS_MK_FN(js_gui_icon_load_fxbm));\n    }\n    return js_icon;\n}\n\nstatic void js_gui_icon_destroy(void* inst) {\n    JsGuiIconInst* js_icon = inst;\n    for\n        M_EACH(fxbm, js_icon->fxbm_list, FxbmIconWrapperList_t) {\n            free(*fxbm);\n        }\n    FxbmIconWrapperList_clear(js_icon->fxbm_list);\n    free(js_icon);\n}\n\nstatic const JsModuleDescriptor js_gui_icon_desc = {\n    \"gui__icon\",\n    js_gui_icon_create,\n    js_gui_icon_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_gui_icon_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_gui_icon_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/js_gui.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"./js_gui.h\"\n#include <furi.h>\n#include <mlib/m-array.h>\n#include <gui/view_dispatcher.h>\n#include \"../js_event_loop/js_event_loop.h\"\n#include <m-array.h>\n\n#define EVENT_QUEUE_SIZE 16\n\ntypedef struct {\n    uint32_t next_view_id;\n    FuriEventLoop* loop;\n    Gui* gui;\n    ViewDispatcher* dispatcher;\n    // event stuff\n    JsEventLoopContract custom_contract;\n    FuriMessageQueue* custom;\n    JsEventLoopContract navigation_contract;\n    FuriSemaphore*\n        navigation; // FIXME: (-nofl) convert into callback once FuriEventLoop starts supporting this\n} JsGui;\n\n// Useful for factories\nstatic JsGui* js_gui;\n\ntypedef struct {\n    uint32_t id;\n    const JsViewDescriptor* descriptor;\n    void* specific_view;\n    void* custom_data;\n} JsGuiViewData;\n\nstatic const JsValueEnumVariant js_gui_font_variants[] = {\n    {\"primary\", FontPrimary},\n    {\"secondary\", FontSecondary},\n    {\"keyboard\", FontKeyboard},\n    {\"bit_numbers\", FontBigNumbers},\n};\nconst JsValueDeclaration js_gui_font_declaration = JS_VALUE_ENUM(Font, js_gui_font_variants);\n\n/**\n * @brief Transformer for custom events\n */\nstatic mjs_val_t\n    js_gui_vd_custom_transformer(struct mjs* mjs, FuriEventLoopObject* object, void* context) {\n    UNUSED(context);\n    furi_check(object);\n    FuriMessageQueue* queue = object;\n    uint32_t event;\n    furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);\n    return mjs_mk_number(mjs, (double)event);\n}\n\n/**\n * @brief ViewDispatcher custom event callback\n */\nstatic bool js_gui_vd_custom_callback(void* context, uint32_t event) {\n    furi_check(context);\n    JsGui* module = context;\n    furi_check(furi_message_queue_put(module->custom, &event, 0) == FuriStatusOk);\n    return true;\n}\n\n/**\n * @brief ViewDispatcher navigation event callback\n */\nstatic bool js_gui_vd_nav_callback(void* context) {\n    furi_check(context);\n    JsGui* module = context;\n    furi_semaphore_release(module->navigation);\n    return true;\n}\n\n/**\n * @brief `viewDispatcher.sendCustom`\n */\nstatic void js_gui_vd_send_custom(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_vd_send_custom_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_gui_vd_send_custom_args =\n        JS_VALUE_ARGS(js_gui_vd_send_custom_arg_list);\n\n    int32_t event;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_custom_args, &event);\n\n    JsGui* module = JS_GET_CONTEXT(mjs);\n    view_dispatcher_send_custom_event(module->dispatcher, (uint32_t)event);\n}\n\n/**\n * @brief `viewDispatcher.sendTo`\n */\nstatic void js_gui_vd_send_to(struct mjs* mjs) {\n    typedef enum {\n        JsSendDirToFront,\n        JsSendDirToBack,\n    } JsSendDir;\n    static const JsValueEnumVariant js_send_dir_variants[] = {\n        {\"front\", JsSendDirToFront},\n        {\"back\", JsSendDirToBack},\n    };\n    static const JsValueDeclaration js_gui_vd_send_to_arg_list[] = {\n        JS_VALUE_ENUM(JsSendDir, js_send_dir_variants),\n    };\n    static const JsValueArguments js_gui_vd_send_to_args =\n        JS_VALUE_ARGS(js_gui_vd_send_to_arg_list);\n\n    JsSendDir send_direction;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_to_args, &send_direction);\n\n    JsGui* module = JS_GET_CONTEXT(mjs);\n    if(send_direction == JsSendDirToBack) {\n        view_dispatcher_send_to_back(module->dispatcher);\n    } else {\n        view_dispatcher_send_to_front(module->dispatcher);\n    }\n}\n\n/**\n * @brief `viewDispatcher.switchTo`\n */\nstatic void js_gui_vd_switch_to(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_vd_switch_to_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_gui_vd_switch_to_args =\n        JS_VALUE_ARGS(js_gui_vd_switch_to_arg_list);\n\n    mjs_val_t view;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_switch_to_args, &view);\n\n    JsGuiViewData* view_data = JS_GET_INST(mjs, view);\n    mjs_val_t vd_obj = mjs_get_this(mjs);\n    JsGui* module = JS_GET_INST(mjs, vd_obj);\n    view_dispatcher_switch_to_view(module->dispatcher, (uint32_t)view_data->id);\n    mjs_set(mjs, vd_obj, \"currentView\", ~0, view);\n}\n\nstatic void* js_gui_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    // get event loop\n    JsEventLoop* js_loop = js_module_get(modules, \"event_loop\");\n    if(M_UNLIKELY(!js_loop)) return NULL;\n    FuriEventLoop* loop = js_event_loop_get_loop(js_loop);\n\n    // create C object\n    JsGui* module = malloc(sizeof(JsGui));\n    module->loop = loop;\n    module->gui = furi_record_open(RECORD_GUI);\n    module->dispatcher = view_dispatcher_alloc_ex(loop);\n    module->custom = furi_message_queue_alloc(EVENT_QUEUE_SIZE, sizeof(uint32_t));\n    module->navigation = furi_semaphore_alloc(EVENT_QUEUE_SIZE, 0);\n    view_dispatcher_attach_to_gui(module->dispatcher, module->gui, ViewDispatcherTypeFullscreen);\n    view_dispatcher_send_to_front(module->dispatcher);\n\n    // subscribe to events and create contracts\n    view_dispatcher_set_event_callback_context(module->dispatcher, module);\n    view_dispatcher_set_custom_event_callback(module->dispatcher, js_gui_vd_custom_callback);\n    view_dispatcher_set_navigation_event_callback(module->dispatcher, js_gui_vd_nav_callback);\n    module->custom_contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object = module->custom,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = js_gui_vd_custom_transformer,\n            },\n    };\n    module->navigation_contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object = module->navigation,\n        .object_type = JsEventLoopObjectTypeSemaphore,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n            },\n    };\n\n    // create viewDispatcher object\n    mjs_val_t view_dispatcher = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, view_dispatcher) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, module));\n        JS_FIELD(\"sendCustom\", MJS_MK_FN(js_gui_vd_send_custom));\n        JS_FIELD(\"sendTo\", MJS_MK_FN(js_gui_vd_send_to));\n        JS_FIELD(\"switchTo\", MJS_MK_FN(js_gui_vd_switch_to));\n        JS_FIELD(\"custom\", mjs_mk_foreign(mjs, &module->custom_contract));\n        JS_FIELD(\"navigation\", mjs_mk_foreign(mjs, &module->navigation_contract));\n        JS_FIELD(\"currentView\", MJS_NULL);\n    }\n\n    // create API object\n    mjs_val_t api = mjs_mk_object(mjs);\n    mjs_set(mjs, api, \"viewDispatcher\", ~0, view_dispatcher);\n\n    *object = api;\n    js_gui = module;\n    return module;\n}\n\nstatic void js_gui_destroy(void* inst) {\n    furi_assert(inst);\n    JsGui* module = inst;\n\n    view_dispatcher_free(module->dispatcher);\n    furi_event_loop_maybe_unsubscribe(module->loop, module->custom);\n    furi_event_loop_maybe_unsubscribe(module->loop, module->navigation);\n    furi_message_queue_free(module->custom);\n    furi_semaphore_free(module->navigation);\n\n    furi_record_close(RECORD_GUI);\n    free(module);\n    js_gui = NULL;\n}\n\n/**\n * @brief Assigns a `View` property. Not available from JS.\n */\nstatic bool\n    js_gui_view_assign(struct mjs* mjs, const char* name, mjs_val_t value, JsGuiViewData* data) {\n    const JsViewDescriptor* descriptor = data->descriptor;\n    for(size_t i = 0; i < descriptor->prop_cnt; i++) {\n        JsViewPropDescriptor prop = descriptor->props[i];\n        if(strcmp(prop.name, name) != 0) continue;\n\n        // convert JS value to C\n        JsViewPropValue c_value;\n        const char* expected_type = NULL;\n        switch(prop.type) {\n        case JsViewPropTypeNumber: {\n            if(!mjs_is_number(value)) {\n                expected_type = \"number\";\n                break;\n            }\n            c_value = (JsViewPropValue){.number = mjs_get_int32(mjs, value)};\n        } break;\n        case JsViewPropTypeString: {\n            if(!mjs_is_string(value)) {\n                expected_type = \"string\";\n                break;\n            }\n            c_value = (JsViewPropValue){.string = mjs_get_string(mjs, &value, NULL)};\n        } break;\n        case JsViewPropTypeArr: {\n            if(!mjs_is_array(value)) {\n                expected_type = \"array\";\n                break;\n            }\n            c_value = (JsViewPropValue){.term = value};\n        } break;\n        case JsViewPropTypeTypedArr: {\n            if(!mjs_is_typed_array(value)) {\n                expected_type = \"typed_array\";\n                break;\n            }\n            c_value = (JsViewPropValue){.term = value};\n        } break;\n        case JsViewPropTypeBool: {\n            if(!mjs_is_boolean(value)) {\n                expected_type = \"bool\";\n                break;\n            }\n            c_value = (JsViewPropValue){.boolean = mjs_get_bool(mjs, value)};\n        } break;\n        }\n\n        if(expected_type) {\n            mjs_prepend_errorf(\n                mjs, MJS_BAD_ARGS_ERROR, \"view prop \\\"%s\\\" requires %s value\", name, expected_type);\n            return false;\n        } else {\n            return prop.assign(mjs, data->specific_view, c_value, data->custom_data);\n        }\n    }\n\n    mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"view has no prop named \\\"%s\\\"\", name);\n    return false;\n}\n\n/**\n * @brief Sets the list of children. Not available from JS.\n */\nstatic bool js_gui_view_internal_set_children(\n    struct mjs* mjs,\n    mjs_val_t children,\n    JsGuiViewData* data,\n    bool do_reset) {\n    if(do_reset) data->descriptor->reset_children(data->specific_view, data->custom_data);\n\n    for(size_t i = 0; i < mjs_array_length(mjs, children); i++) {\n        mjs_val_t child = mjs_array_get(mjs, children, i);\n        if(!data->descriptor->add_child(mjs, data->specific_view, data->custom_data, child))\n            return false;\n    }\n\n    return true;\n}\n\n/**\n * @brief `View.set`\n */\nstatic void js_gui_view_set(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_view_set_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_gui_view_set_args = JS_VALUE_ARGS(js_gui_view_set_arg_list);\n\n    const char* name;\n    mjs_val_t value;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_args, &name, &value);\n\n    JsGuiViewData* data = JS_GET_CONTEXT(mjs);\n    bool success = js_gui_view_assign(mjs, name, value, data);\n    UNUSED(success);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief `View.addChild`\n */\nstatic void js_gui_view_add_child(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_view_add_child_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_gui_view_add_child_args =\n        JS_VALUE_ARGS(js_gui_view_add_child_arg_list);\n\n    mjs_val_t child;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_add_child_args, &child);\n\n    JsGuiViewData* data = JS_GET_CONTEXT(mjs);\n    if(!data->descriptor->add_child || !data->descriptor->reset_children)\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"this View can't have children\");\n\n    bool success = data->descriptor->add_child(mjs, data->specific_view, data->custom_data, child);\n    UNUSED(success);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief `View.resetChildren`\n */\nstatic void js_gui_view_reset_children(struct mjs* mjs) {\n    JsGuiViewData* data = JS_GET_CONTEXT(mjs);\n    if(!data->descriptor->add_child || !data->descriptor->reset_children)\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"this View can't have children\");\n\n    data->descriptor->reset_children(data->specific_view, data->custom_data);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\n/**\n * @brief `View.setChildren`\n */\nstatic void js_gui_view_set_children(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_view_set_children_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAnyArray),\n    };\n    static const JsValueArguments js_gui_view_set_children_args =\n        JS_VALUE_ARGS(js_gui_view_set_children_arg_list);\n\n    mjs_val_t children;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_children_args, &children);\n\n    JsGuiViewData* data = JS_GET_CONTEXT(mjs);\n    if(!data->descriptor->add_child || !data->descriptor->reset_children)\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"this View can't have children\");\n\n    js_gui_view_internal_set_children(mjs, children, data, true);\n}\n\n/**\n * @brief `View` destructor\n */\nstatic void js_gui_view_destructor(struct mjs* mjs, mjs_val_t obj) {\n    JsGuiViewData* data = JS_GET_INST(mjs, obj);\n    view_dispatcher_remove_view(js_gui->dispatcher, data->id);\n    if(data->descriptor->custom_destroy)\n        data->descriptor->custom_destroy(data->specific_view, data->custom_data, js_gui->loop);\n    data->descriptor->free(data->specific_view);\n    free(data);\n}\n\n/**\n * @brief Creates a `View` object from a descriptor. Not available from JS.\n */\nstatic mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descriptor) {\n    void* specific_view = descriptor->alloc();\n    View* view = descriptor->get_view(specific_view);\n    uint32_t view_id = js_gui->next_view_id++;\n    view_dispatcher_add_view(js_gui->dispatcher, view_id, view);\n\n    // generic view API\n    mjs_val_t view_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, view_obj) {\n        JS_FIELD(\"set\", MJS_MK_FN(js_gui_view_set));\n        JS_FIELD(\"addChild\", MJS_MK_FN(js_gui_view_add_child));\n        JS_FIELD(\"resetChildren\", MJS_MK_FN(js_gui_view_reset_children));\n        JS_FIELD(\"setChildren\", MJS_MK_FN(js_gui_view_set_children));\n    }\n\n    // object data\n    JsGuiViewData* data = malloc(sizeof(JsGuiViewData));\n    *data = (JsGuiViewData){\n        .descriptor = descriptor,\n        .id = view_id,\n        .specific_view = specific_view,\n        .custom_data =\n            descriptor->custom_make ? descriptor->custom_make(mjs, specific_view, view_obj) : NULL,\n    };\n    mjs_set(mjs, view_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, data));\n    mjs_set(mjs, view_obj, MJS_DESTRUCTOR_PROP_NAME, ~0, MJS_MK_FN(js_gui_view_destructor));\n\n    return view_obj;\n}\n\n/**\n * @brief `ViewFactory.make`\n */\nstatic void js_gui_vf_make(struct mjs* mjs) {\n    const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, js_gui_make_view(mjs, descriptor));\n}\n\n/**\n * @brief `ViewFactory.makeWith`\n */\nstatic void js_gui_vf_make_with(struct mjs* mjs) {\n    static const JsValueDeclaration js_gui_vf_make_with_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAnyObject),\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_gui_vf_make_with_args =\n        JS_VALUE_ARGS(js_gui_vf_make_with_arg_list);\n\n    mjs_val_t props, children;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vf_make_with_args, &props, &children);\n    const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);\n\n    // make the object like normal\n    mjs_val_t view_obj = js_gui_make_view(mjs, descriptor);\n    JsGuiViewData* data = JS_GET_INST(mjs, view_obj);\n\n    // assign properties one by one\n    mjs_val_t key, iter = MJS_UNDEFINED;\n    while((key = mjs_next(mjs, props, &iter)) != MJS_UNDEFINED) {\n        furi_check(mjs_is_string(key));\n        const char* name = mjs_get_string(mjs, &key, NULL);\n        mjs_val_t value = mjs_get(mjs, props, name, ~0);\n\n        if(!js_gui_view_assign(mjs, name, value, data)) {\n            mjs_return(mjs, MJS_UNDEFINED);\n            return;\n        }\n    }\n\n    // assign children\n    if(mjs_is_array(children)) {\n        if(!data->descriptor->add_child || !data->descriptor->reset_children)\n            JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"this View can't have children\");\n\n        if(!js_gui_view_internal_set_children(mjs, children, data, false)) return;\n    }\n\n    mjs_return(mjs, view_obj);\n}\n\nmjs_val_t js_gui_make_view_factory(struct mjs* mjs, const JsViewDescriptor* view_descriptor) {\n    mjs_val_t factory = mjs_mk_object(mjs);\n    mjs_set(mjs, factory, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, (void*)view_descriptor));\n    mjs_set(mjs, factory, \"make\", ~0, MJS_MK_FN(js_gui_vf_make));\n    mjs_set(mjs, factory, \"makeWith\", ~0, MJS_MK_FN(js_gui_vf_make_with));\n    return factory;\n}\n\nextern const ElfApiInterface js_gui_hashtable_api_interface;\n\nstatic const JsModuleDescriptor js_gui_desc = {\n    \"gui\",\n    js_gui_create,\n    js_gui_destroy,\n    &js_gui_hashtable_api_interface,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_gui_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_gui_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/js_gui.h",
    "content": "#include \"../../js_modules.h\"\n#include <gui/view.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    JsViewPropTypeString,\n    JsViewPropTypeNumber,\n    JsViewPropTypeArr,\n    JsViewPropTypeTypedArr,\n    JsViewPropTypeBool,\n} JsViewPropType;\n\ntypedef union {\n    const char* string;\n    int32_t number;\n    bool boolean;\n    mjs_val_t term;\n} JsViewPropValue;\n\n/**\n * JS-to-C font enum mapping\n */\nextern const JsValueDeclaration js_gui_font_declaration;\n\n/**\n * @brief Assigns a value to a view property\n * \n * The name and the type are implicit and defined in the property descriptor\n */\ntypedef bool (\n    *JsViewPropAssign)(struct mjs* mjs, void* specific_view, JsViewPropValue value, void* context);\n\n/** @brief Property descriptor */\ntypedef struct {\n    const char* name; //<! Property name, as visible from JS\n    JsViewPropType type; // <! Property type, ensured by the GUI module\n    JsViewPropAssign assign; // <! Property assignment callback\n} JsViewPropDescriptor;\n\n// View method signatures\n\n/** @brief View's `_alloc` method */\ntypedef void* (*JsViewAlloc)(void);\n/** @brief View's `_get_view` method */\ntypedef View* (*JsViewGetView)(void* specific_view);\n/** @brief View's `_free` method */\ntypedef void (*JsViewFree)(void* specific_view);\n\n// Glue code method signatures\n\n/** @brief Context instantiation for glue code */\ntypedef void* (*JsViewCustomMake)(struct mjs* mjs, void* specific_view, mjs_val_t view_obj);\n/** @brief Context destruction for glue code */\ntypedef void (*JsViewCustomDestroy)(void* specific_view, void* custom_state, FuriEventLoop* loop);\n/** @brief `addChild` callback for glue code */\ntypedef bool (\n    *JsViewAddChild)(struct mjs* mjs, void* specific_view, void* custom_state, mjs_val_t child_obj);\n/** @brief `resetChildren` callback for glue code */\ntypedef void (*JsViewResetChildren)(void* specific_view, void* custom_state);\n\n/**\n * @brief Descriptor for a JS view\n * \n * Contains:\n *   - Pointers to generic view methods (`alloc`, `get_view` and `free`)\n *   - Pointers to glue code context ctor/dtor methods (`custom_make`,\n *     `custom_destroy`)\n *   - Descriptors of properties visible from JS (`prop_cnt`, `props`)\n * \n * `js_gui` uses this descriptor to produce view factories and views.\n */\ntypedef struct {\n    JsViewAlloc alloc;\n    JsViewGetView get_view;\n    JsViewFree free;\n\n    JsViewCustomMake custom_make; // <! May be NULL\n    JsViewCustomDestroy custom_destroy; // <! May be NULL\n\n    JsViewAddChild add_child; // <! May be NULL\n    JsViewResetChildren reset_children; // <! May be NULL\n\n    size_t prop_cnt; //<! Number of properties visible from JS\n    JsViewPropDescriptor props[]; // <! Descriptors of properties visible from JS\n} JsViewDescriptor;\n\n// Callback ordering:\n//                                   +-> add_child       -+\n//                                   +-> reset_children  -+\n// alloc -> get_view -> custom_make -+-> props[i].assign -+> custom_destroy -> free\n// \\__________ creation __________/      \\____ use ____/     \\___ destruction ____/\n\n/**\n * @brief Creates a JS `ViewFactory` object\n * \n * This function is intended to be used by individual view adapter modules that\n * wish to create a unified JS API interface in a declarative way. Usually this\n * is done via the `JS_GUI_VIEW_DEF` macro which hides all the boilerplate.\n * \n * The `ViewFactory` object exposes two methods, `make` and `makeWith`, each\n * returning a `View` object. These objects fully comply with the expectations\n * of the `ViewDispatcher`, TS type definitions and the proposed Flipper JS\n * coding style.\n */\nmjs_val_t js_gui_make_view_factory(struct mjs* mjs, const JsViewDescriptor* view_descriptor);\n\n/**\n * @brief Defines a module implementing `View` glue code\n */\n#define JS_GUI_VIEW_DEF(name, descriptor)                                                \\\n    static void* view_mod_ctor(struct mjs* mjs, mjs_val_t* object, JsModules* modules) { \\\n        UNUSED(modules);                                                                 \\\n        *object = js_gui_make_view_factory(mjs, descriptor);                             \\\n        return NULL;                                                                     \\\n    }                                                                                    \\\n    static const JsModuleDescriptor js_mod_desc = {                                      \\\n        \"gui__\" #name,                                                                   \\\n        view_mod_ctor,                                                                   \\\n        NULL,                                                                            \\\n        NULL,                                                                            \\\n    };                                                                                   \\\n    static const FlipperAppPluginDescriptor plugin_descriptor = {                        \\\n        .appid = PLUGIN_APP_ID,                                                          \\\n        .ep_api_version = PLUGIN_API_VERSION,                                            \\\n        .entry_point = &js_mod_desc,                                                     \\\n    };                                                                                   \\\n    const FlipperAppPluginDescriptor* js_view_##name##_ep(void) {                        \\\n        return &plugin_descriptor;                                                       \\\n    }\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/js_gui_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n#include \"js_gui_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(js_gui_api_table), \"Detected API method hash collision!\");\n\nextern \"C\" constexpr HashtableApiInterface js_gui_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    js_gui_api_table.cbegin(),\n    js_gui_api_table.cend(),\n};\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/js_gui_api_table_i.h",
    "content": "#include \"js_gui.h\"\n\nstatic constexpr auto js_gui_api_table = sort(create_array_t<sym_entry>(\n    API_METHOD(js_gui_make_view_factory, mjs_val_t, (struct mjs*, const JsViewDescriptor*)),\n    API_VARIABLE(js_gui_font_declaration, const JsValueDeclaration)));\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/loading.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include <gui/modules/loading.h>\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)loading_alloc,\n    .free = (JsViewFree)loading_free,\n    .get_view = (JsViewGetView)loading_get_view,\n    .prop_cnt = 0,\n    .props = {},\n};\nJS_GUI_VIEW_DEF(loading, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/menu.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/menu.h>\n#include <toolbox/str_buffer.h>\n\ntypedef struct {\n    int32_t next_index;\n    StrBuffer str_buffer;\n\n    FuriMessageQueue* queue;\n    JsEventLoopContract contract;\n} JsMenuCtx;\n\nstatic mjs_val_t choose_transformer(struct mjs* mjs, FuriMessageQueue* queue, void* context) {\n    UNUSED(context);\n    uint32_t index;\n    furi_check(furi_message_queue_get(queue, &index, 0) == FuriStatusOk);\n    return mjs_mk_number(mjs, (double)index);\n}\n\nstatic void choose_callback(void* context, uint32_t index) {\n    JsMenuCtx* ctx = context;\n    furi_check(furi_message_queue_put(ctx->queue, &index, 0) == FuriStatusOk);\n}\n\nstatic bool\n    js_menu_add_child(struct mjs* mjs, Menu* menu, JsMenuCtx* context, mjs_val_t child_obj) {\n    static const JsValueDeclaration js_menu_string = JS_VALUE_SIMPLE(JsValueTypeString);\n    static const JsValueDeclaration js_menu_pointer = JS_VALUE_SIMPLE(JsValueTypeRawPointer);\n\n    static const JsValueObjectField js_menu_child_fields[] = {\n        {\"icon\", &js_menu_pointer},\n        {\"label\", &js_menu_string},\n    };\n    static const JsValueDeclaration js_menu_child = JS_VALUE_OBJECT(js_menu_child_fields);\n\n    const Icon* icon;\n    const char* label;\n    JsValueParseStatus status;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&js_menu_child),\n        JsValueParseFlagReturnOnError,\n        &status,\n        &child_obj,\n        &icon,\n        &label);\n    if(status != JsValueParseStatusOk) return false;\n\n    menu_add_item(\n        menu,\n        str_buffer_make_owned_clone(&context->str_buffer, label),\n        icon,\n        context->next_index++,\n        choose_callback,\n        context);\n\n    return true;\n}\n\nstatic void js_menu_reset_children(Menu* menu, JsMenuCtx* context) {\n    context->next_index = 0;\n    menu_reset(menu);\n    str_buffer_clear_all_clones(&context->str_buffer);\n}\n\nstatic JsMenuCtx* ctx_make(struct mjs* mjs, Menu* input, mjs_val_t view_obj) {\n    UNUSED(input);\n    JsMenuCtx* context = malloc(sizeof(JsMenuCtx));\n    context->queue = furi_message_queue_alloc(1, sizeof(uint32_t));\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)choose_transformer,\n            },\n    };\n    mjs_set(mjs, view_obj, \"chosen\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(Menu* input, JsMenuCtx* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->queue);\n    furi_message_queue_free(context->queue);\n    str_buffer_clear_all_clones(&context->str_buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)menu_alloc,\n    .free = (JsViewFree)menu_free,\n    .get_view = (JsViewGetView)menu_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .add_child = (JsViewAddChild)js_menu_add_child,\n    .reset_children = (JsViewResetChildren)js_menu_reset_children,\n    .prop_cnt = 0,\n    .props = {},\n};\nJS_GUI_VIEW_DEF(menu, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/number_input.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/number_input.h>\n\ntypedef struct {\n    int32_t default_val, min_val, max_val;\n    FuriMessageQueue* input_queue;\n    JsEventLoopContract contract;\n} JsNumKbdContext;\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsNumKbdContext* context) {\n    UNUSED(context);\n    int32_t number;\n    furi_check(furi_message_queue_get(queue, &number, 0) == FuriStatusOk);\n    return mjs_mk_number(mjs, number);\n}\n\nstatic void input_callback(void* ctx, int32_t value) {\n    JsNumKbdContext* context = ctx;\n    furi_check(furi_message_queue_put(context->input_queue, &value, 0) == FuriStatusOk);\n}\n\nstatic bool header_assign(\n    struct mjs* mjs,\n    NumberInput* input,\n    JsViewPropValue value,\n    JsNumKbdContext* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    number_input_set_header_text(input, value.string);\n    return true;\n}\n\nstatic bool min_val_assign(\n    struct mjs* mjs,\n    NumberInput* input,\n    JsViewPropValue value,\n    JsNumKbdContext* context) {\n    UNUSED(mjs);\n    context->min_val = value.number;\n    number_input_set_result_callback(\n        input, input_callback, context, context->default_val, context->min_val, context->max_val);\n    return true;\n}\n\nstatic bool max_val_assign(\n    struct mjs* mjs,\n    NumberInput* input,\n    JsViewPropValue value,\n    JsNumKbdContext* context) {\n    UNUSED(mjs);\n    context->max_val = value.number;\n    number_input_set_result_callback(\n        input, input_callback, context, context->default_val, context->min_val, context->max_val);\n    return true;\n}\n\nstatic bool default_val_assign(\n    struct mjs* mjs,\n    NumberInput* input,\n    JsViewPropValue value,\n    JsNumKbdContext* context) {\n    UNUSED(mjs);\n    context->default_val = value.number;\n    number_input_set_result_callback(\n        input, input_callback, context, context->default_val, context->min_val, context->max_val);\n    return true;\n}\n\nstatic JsNumKbdContext* ctx_make(struct mjs* mjs, NumberInput* input, mjs_val_t view_obj) {\n    JsNumKbdContext* context = malloc(sizeof(JsNumKbdContext));\n    *context = (JsNumKbdContext){\n        .default_val = 0,\n        .max_val = 100,\n        .min_val = 0,\n        .input_queue = furi_message_queue_alloc(1, sizeof(int32_t)),\n    };\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->input_queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    number_input_set_result_callback(\n        input, input_callback, context, context->default_val, context->min_val, context->max_val);\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(NumberInput* input, JsNumKbdContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_queue);\n    furi_message_queue_free(context->input_queue);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)number_input_alloc,\n    .free = (JsViewFree)number_input_free,\n    .get_view = (JsViewGetView)number_input_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 4,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n        (JsViewPropDescriptor){\n            .name = \"minValue\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)min_val_assign},\n        (JsViewPropDescriptor){\n            .name = \"maxValue\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)max_val_assign},\n        (JsViewPropDescriptor){\n            .name = \"defaultValue\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)default_val_assign},\n    }};\n\nJS_GUI_VIEW_DEF(number_input, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/popup.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/popup.h>\n#include <toolbox/str_buffer.h>\n\ntypedef struct {\n    StrBuffer str_buffer;\n    FuriSemaphore* semaphore;\n    JsEventLoopContract contract;\n} JsPopupCtx;\n\nstatic void timeout_callback(JsPopupCtx* context) {\n    furi_check(furi_semaphore_release(context->semaphore) == FuriStatusOk);\n}\n\nstatic bool\n    header_assign(struct mjs* mjs, Popup* popup, JsViewPropValue value, JsPopupCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    popup_set_header(\n        popup,\n        str_buffer_make_owned_clone(&context->str_buffer, value.string),\n        64,\n        0,\n        AlignCenter,\n        AlignTop);\n    return true;\n}\n\nstatic bool\n    text_assign(struct mjs* mjs, Popup* popup, JsViewPropValue value, JsPopupCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    popup_set_text(\n        popup,\n        str_buffer_make_owned_clone(&context->str_buffer, value.string),\n        64,\n        32,\n        AlignCenter,\n        AlignCenter);\n    return true;\n}\n\nstatic bool\n    timeout_assign(struct mjs* mjs, Popup* popup, JsViewPropValue value, JsPopupCtx* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    popup_set_timeout(popup, value.number);\n    popup_enable_timeout(popup);\n    return true;\n}\n\nstatic JsPopupCtx* ctx_make(struct mjs* mjs, Popup* popup, mjs_val_t view_obj) {\n    JsPopupCtx* context = malloc(sizeof(JsPopupCtx));\n    context->semaphore = furi_semaphore_alloc(1, 0);\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeSemaphore,\n        .object = context->semaphore,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n            },\n    };\n    mjs_set(mjs, view_obj, \"timeout\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    popup_set_callback(popup, (PopupCallback)timeout_callback);\n    popup_set_context(popup, context);\n    return context;\n}\n\nstatic void ctx_destroy(Popup* popup, JsPopupCtx* context, FuriEventLoop* loop) {\n    UNUSED(popup);\n    furi_event_loop_maybe_unsubscribe(loop, context->semaphore);\n    furi_semaphore_free(context->semaphore);\n    str_buffer_clear_all_clones(&context->str_buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)popup_alloc,\n    .free = (JsViewFree)popup_free,\n    .get_view = (JsViewGetView)popup_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 3,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n        (JsViewPropDescriptor){\n            .name = \"text\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)text_assign},\n        (JsViewPropDescriptor){\n            .name = \"timeout\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)timeout_assign},\n    }};\n\nJS_GUI_VIEW_DEF(popup, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/submenu.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/submenu.h>\n\n#define QUEUE_LEN 2\n\ntypedef struct {\n    int32_t next_index;\n    FuriMessageQueue* queue;\n    JsEventLoopContract contract;\n} JsSubmenuCtx;\n\nstatic mjs_val_t choose_transformer(struct mjs* mjs, FuriMessageQueue* queue, void* context) {\n    UNUSED(context);\n    uint32_t index;\n    furi_check(furi_message_queue_get(queue, &index, 0) == FuriStatusOk);\n    return mjs_mk_number(mjs, (double)index);\n}\n\nvoid choose_callback(void* context, uint32_t index) {\n    JsSubmenuCtx* ctx = context;\n    furi_check(furi_message_queue_put(ctx->queue, &index, 0) == FuriStatusOk);\n}\n\nstatic bool\n    header_assign(struct mjs* mjs, Submenu* submenu, JsViewPropValue value, void* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    submenu_set_header(submenu, value.string);\n    return true;\n}\n\nstatic bool js_submenu_add_child(\n    struct mjs* mjs,\n    Submenu* submenu,\n    JsSubmenuCtx* context,\n    mjs_val_t child_obj) {\n    const char* str = mjs_get_string(mjs, &child_obj, NULL);\n    if(!str) return false;\n\n    submenu_add_item(submenu, str, context->next_index++, choose_callback, context);\n\n    return true;\n}\n\nstatic void js_submenu_reset_children(Submenu* submenu, JsSubmenuCtx* context) {\n    context->next_index = 0;\n    submenu_reset(submenu);\n}\n\nstatic JsSubmenuCtx* ctx_make(struct mjs* mjs, Submenu* input, mjs_val_t view_obj) {\n    UNUSED(input);\n    JsSubmenuCtx* context = malloc(sizeof(JsSubmenuCtx));\n    context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(uint32_t));\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)choose_transformer,\n            },\n    };\n    mjs_set(mjs, view_obj, \"chosen\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(Submenu* input, JsSubmenuCtx* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->queue);\n    furi_message_queue_free(context->queue);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)submenu_alloc,\n    .free = (JsViewFree)submenu_free,\n    .get_view = (JsViewGetView)submenu_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .add_child = (JsViewAddChild)js_submenu_add_child,\n    .reset_children = (JsViewResetChildren)js_submenu_reset_children,\n    .prop_cnt = 1,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n    }};\nJS_GUI_VIEW_DEF(submenu, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/text_box.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include <gui/modules/text_box.h>\n\nstatic bool\n    text_assign(struct mjs* mjs, TextBox* text_box, JsViewPropValue value, FuriString* context) {\n    UNUSED(mjs);\n    furi_string_set(context, value.string);\n    text_box_set_text(text_box, furi_string_get_cstr(context));\n    return true;\n}\n\nstatic bool font_assign(struct mjs* mjs, TextBox* text_box, JsViewPropValue value, void* context) {\n    UNUSED(context);\n    TextBoxFont font;\n    if(strcasecmp(value.string, \"hex\") == 0) {\n        font = TextBoxFontHex;\n    } else if(strcasecmp(value.string, \"text\") == 0) {\n        font = TextBoxFontText;\n    } else {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"must be one of: \\\"text\\\", \\\"hex\\\"\");\n        return false;\n    }\n    text_box_set_font(text_box, font);\n    return true;\n}\n\nstatic bool\n    focus_assign(struct mjs* mjs, TextBox* text_box, JsViewPropValue value, void* context) {\n    UNUSED(context);\n    TextBoxFocus focus;\n    if(strcasecmp(value.string, \"start\") == 0) {\n        focus = TextBoxFocusStart;\n    } else if(strcasecmp(value.string, \"end\") == 0) {\n        focus = TextBoxFocusEnd;\n    } else {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"must be one of: \\\"start\\\", \\\"end\\\"\");\n        return false;\n    }\n    text_box_set_focus(text_box, focus);\n    return true;\n}\n\nFuriString* ctx_make(struct mjs* mjs, TextBox* specific_view, mjs_val_t view_obj) {\n    UNUSED(mjs);\n    UNUSED(specific_view);\n    UNUSED(view_obj);\n    return furi_string_alloc();\n}\n\nvoid ctx_destroy(TextBox* specific_view, FuriString* context, FuriEventLoop* loop) {\n    UNUSED(specific_view);\n    UNUSED(loop);\n    furi_string_free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)text_box_alloc,\n    .free = (JsViewFree)text_box_free,\n    .get_view = (JsViewGetView)text_box_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 3,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"text\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)text_assign},\n        (JsViewPropDescriptor){\n            .name = \"font\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)font_assign},\n        (JsViewPropDescriptor){\n            .name = \"focus\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)focus_assign},\n    }};\nJS_GUI_VIEW_DEF(text_box, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/text_input.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/text_input.h>\n\n#define DEFAULT_BUF_SZ 33\n\ntypedef struct {\n    char* buffer;\n    size_t buffer_size;\n    size_t default_text_size;\n    FuriString* header;\n    bool default_text_clear;\n    FuriSemaphore* input_semaphore;\n    JsEventLoopContract contract;\n} JsKbdContext;\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriSemaphore* semaphore, JsKbdContext* context) {\n    furi_check(furi_semaphore_acquire(semaphore, 0) == FuriStatusOk);\n    return mjs_mk_string(mjs, context->buffer, ~0, true);\n}\n\nstatic void input_callback(JsKbdContext* context) {\n    furi_semaphore_release(context->input_semaphore);\n}\n\nstatic bool\n    header_assign(struct mjs* mjs, TextInput* input, JsViewPropValue value, JsKbdContext* context) {\n    UNUSED(mjs);\n    furi_string_set(context->header, value.string);\n    text_input_set_header_text(input, furi_string_get_cstr(context->header));\n    return true;\n}\n\nstatic bool min_len_assign(\n    struct mjs* mjs,\n    TextInput* input,\n    JsViewPropValue value,\n    JsKbdContext* context) {\n    UNUSED(mjs);\n    UNUSED(context);\n    text_input_set_minimum_length(input, (size_t)value.number);\n    return true;\n}\n\nstatic bool max_len_assign(\n    struct mjs* mjs,\n    TextInput* input,\n    JsViewPropValue value,\n    JsKbdContext* context) {\n    UNUSED(mjs);\n    size_t new_buffer_size = value.number + 1;\n    if(new_buffer_size < context->default_text_size) {\n        // Avoid confusing parameters from user\n        mjs_prepend_errorf(\n            mjs, MJS_BAD_ARGS_ERROR, \"maxLength must be larger than defaultText length\");\n        return false;\n    }\n    context->buffer_size = new_buffer_size;\n    context->buffer = realloc(context->buffer, context->buffer_size); //-V701\n    text_input_set_result_callback(\n        input,\n        (TextInputCallback)input_callback,\n        context,\n        context->buffer,\n        context->buffer_size,\n        context->default_text_clear);\n    return true;\n}\n\nstatic bool default_text_assign(\n    struct mjs* mjs,\n    TextInput* input,\n    JsViewPropValue value,\n    JsKbdContext* context) {\n    UNUSED(mjs);\n    UNUSED(input);\n\n    if(value.string) {\n        context->default_text_size = strlen(value.string) + 1;\n        if(context->buffer_size < context->default_text_size) {\n            // Ensure buffer is large enough for defaultData\n            context->buffer_size = context->default_text_size;\n            context->buffer = realloc(context->buffer, context->buffer_size); //-V701\n        }\n        // Also trim excess previous data with strlcpy()\n        strlcpy(context->buffer, value.string, context->buffer_size); //-V575\n        text_input_set_result_callback(\n            input,\n            (TextInputCallback)input_callback,\n            context,\n            context->buffer,\n            context->buffer_size,\n            context->default_text_clear);\n    }\n    return true;\n}\n\nstatic bool default_text_clear_assign(\n    struct mjs* mjs,\n    TextInput* input,\n    JsViewPropValue value,\n    JsKbdContext* context) {\n    UNUSED(mjs);\n\n    context->default_text_clear = value.boolean;\n    text_input_set_result_callback(\n        input,\n        (TextInputCallback)input_callback,\n        context,\n        context->buffer,\n        context->buffer_size,\n        context->default_text_clear);\n    return true;\n}\n\nstatic JsKbdContext* ctx_make(struct mjs* mjs, TextInput* input, mjs_val_t view_obj) {\n    JsKbdContext* context = malloc(sizeof(JsKbdContext));\n    *context = (JsKbdContext){\n        .buffer_size = DEFAULT_BUF_SZ,\n        .buffer = malloc(DEFAULT_BUF_SZ),\n        .header = furi_string_alloc(),\n        .default_text_clear = false,\n        .input_semaphore = furi_semaphore_alloc(1, 0),\n    };\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeSemaphore,\n        .object = context->input_semaphore,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    text_input_set_result_callback(\n        input,\n        (TextInputCallback)input_callback,\n        context,\n        context->buffer,\n        context->buffer_size,\n        context->default_text_clear);\n    mjs_set(mjs, view_obj, \"input\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(TextInput* input, JsKbdContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_semaphore);\n    furi_semaphore_free(context->input_semaphore);\n    furi_string_free(context->header);\n    free(context->buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)text_input_alloc,\n    .free = (JsViewFree)text_input_free,\n    .get_view = (JsViewGetView)text_input_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .prop_cnt = 5,\n    .props = {\n        (JsViewPropDescriptor){\n            .name = \"header\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)header_assign},\n        (JsViewPropDescriptor){\n            .name = \"minLength\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)min_len_assign},\n        (JsViewPropDescriptor){\n            .name = \"maxLength\",\n            .type = JsViewPropTypeNumber,\n            .assign = (JsViewPropAssign)max_len_assign},\n        (JsViewPropDescriptor){\n            .name = \"defaultText\",\n            .type = JsViewPropTypeString,\n            .assign = (JsViewPropAssign)default_text_assign},\n        (JsViewPropDescriptor){\n            .name = \"defaultTextClear\",\n            .type = JsViewPropTypeBool,\n            .assign = (JsViewPropAssign)default_text_clear_assign},\n    }};\n\nJS_GUI_VIEW_DEF(text_input, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/vi_list.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/variable_item_list.h>\n#include <toolbox/str_buffer.h>\n\ntypedef struct {\n    StrBuffer str_buffer;\n\n    // let mjs do the memory management heavy lifting, store children in a js array\n    struct mjs* mjs;\n    mjs_val_t children;\n    VariableItemList* list;\n\n    FuriMessageQueue* input_queue;\n    JsEventLoopContract contract;\n} JsViListContext;\n\ntypedef struct {\n    int32_t item_index;\n    int32_t value_index;\n} JsViListEvent;\n\nstatic mjs_val_t\n    input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsViListContext* context) {\n    UNUSED(context);\n    JsViListEvent event;\n    furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);\n\n    mjs_val_t event_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, event_obj) {\n        JS_FIELD(\"itemIndex\", mjs_mk_number(mjs, event.item_index));\n        JS_FIELD(\"valueIndex\", mjs_mk_number(mjs, event.value_index));\n    }\n\n    return event_obj;\n}\n\nstatic void js_vi_list_change_callback(VariableItem* item) {\n    JsViListContext* context = variable_item_get_context(item);\n    struct mjs* mjs = context->mjs;\n    uint8_t item_index = variable_item_list_get_selected_item_index(context->list);\n    uint8_t value_index = variable_item_get_current_value_index(item);\n\n    // type safety ensured in add_child\n    mjs_val_t variants = mjs_array_get(mjs, context->children, item_index);\n    mjs_val_t variant = mjs_array_get(mjs, variants, value_index);\n    variable_item_set_current_value_text(item, mjs_get_string(mjs, &variant, NULL));\n\n    JsViListEvent event = {\n        .item_index = item_index,\n        .value_index = value_index,\n    };\n    furi_check(furi_message_queue_put(context->input_queue, &event, 0) == FuriStatusOk);\n}\n\nstatic bool js_vi_list_add_child(\n    struct mjs* mjs,\n    VariableItemList* list,\n    JsViListContext* context,\n    mjs_val_t child_obj) {\n    static const JsValueDeclaration js_vi_list_string = JS_VALUE_SIMPLE(JsValueTypeString);\n    static const JsValueDeclaration js_vi_list_arr = JS_VALUE_SIMPLE(JsValueTypeAnyArray);\n    static const JsValueDeclaration js_vi_list_int_default_0 =\n        JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 0);\n\n    static const JsValueObjectField js_vi_list_child_fields[] = {\n        {\"label\", &js_vi_list_string},\n        {\"variants\", &js_vi_list_arr},\n        {\"defaultSelected\", &js_vi_list_int_default_0},\n    };\n    static const JsValueDeclaration js_vi_list_child =\n        JS_VALUE_OBJECT_W_DEFAULTS(js_vi_list_child_fields);\n\n    JsValueParseStatus status;\n    const char* label;\n    mjs_val_t variants;\n    int32_t default_selected;\n    JS_VALUE_PARSE(\n        mjs,\n        JS_VALUE_PARSE_SOURCE_VALUE(&js_vi_list_child),\n        JsValueParseFlagReturnOnError,\n        &status,\n        &child_obj,\n        &label,\n        &variants,\n        &default_selected);\n    if(status != JsValueParseStatusOk) return false;\n\n    size_t variants_cnt = mjs_array_length(mjs, variants);\n    for(size_t i = 0; i < variants_cnt; i++)\n        if(!mjs_is_string(mjs_array_get(mjs, variants, i))) return false;\n\n    VariableItem* item = variable_item_list_add(\n        list,\n        str_buffer_make_owned_clone(&context->str_buffer, label),\n        variants_cnt,\n        js_vi_list_change_callback,\n        context);\n    variable_item_set_current_value_index(item, default_selected);\n    mjs_val_t default_variant = mjs_array_get(mjs, variants, default_selected);\n    variable_item_set_current_value_text(item, mjs_get_string(mjs, &default_variant, NULL));\n\n    mjs_array_push(context->mjs, context->children, variants);\n\n    return true;\n}\n\nstatic void js_vi_list_reset_children(VariableItemList* list, JsViListContext* context) {\n    mjs_disown(context->mjs, &context->children);\n    context->children = mjs_mk_array(context->mjs);\n    mjs_own(context->mjs, &context->children);\n\n    variable_item_list_reset(list);\n    str_buffer_clear_all_clones(&context->str_buffer);\n}\n\nstatic JsViListContext* ctx_make(struct mjs* mjs, VariableItemList* list, mjs_val_t view_obj) {\n    JsViListContext* context = malloc(sizeof(JsViListContext));\n    *context = (JsViListContext){\n        .str_buffer = {0},\n        .mjs = mjs,\n        .children = mjs_mk_array(mjs),\n        .list = list,\n        .input_queue = furi_message_queue_alloc(1, sizeof(JsViListEvent)),\n    };\n    mjs_own(context->mjs, &context->children);\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->input_queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)input_transformer,\n                .transformer_context = context,\n            },\n    };\n    mjs_set(mjs, view_obj, \"valueUpdate\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void ctx_destroy(VariableItemList* input, JsViListContext* context, FuriEventLoop* loop) {\n    UNUSED(input);\n    furi_event_loop_maybe_unsubscribe(loop, context->input_queue);\n    furi_message_queue_free(context->input_queue);\n    str_buffer_clear_all_clones(&context->str_buffer);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)variable_item_list_alloc,\n    .free = (JsViewFree)variable_item_list_free,\n    .get_view = (JsViewGetView)variable_item_list_get_view,\n    .custom_make = (JsViewCustomMake)ctx_make,\n    .custom_destroy = (JsViewCustomDestroy)ctx_destroy,\n    .add_child = (JsViewAddChild)js_vi_list_add_child,\n    .reset_children = (JsViewResetChildren)js_vi_list_reset_children,\n    .prop_cnt = 0,\n    .props = {},\n};\n\nJS_GUI_VIEW_DEF(vi_list, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_gui/widget.c",
    "content": "#include \"../../js_modules.h\" // IWYU pragma: keep\n#include \"js_gui.h\"\n#include \"../js_event_loop/js_event_loop.h\"\n#include <gui/modules/widget.h>\n\ntypedef struct {\n    FuriMessageQueue* queue;\n    JsEventLoopContract contract;\n} JsWidgetCtx;\n\n#define QUEUE_LEN 2\n\ntypedef struct {\n    GuiButtonType key;\n    InputType type;\n} JsWidgetButtonEvent;\n\n/**\n * @brief Parses position (X and Y) from an element declaration object\n */\nstatic bool element_get_position(struct mjs* mjs, mjs_val_t element, int32_t* x, int32_t* y) {\n    mjs_val_t x_in = mjs_get(mjs, element, \"x\", ~0);\n    mjs_val_t y_in = mjs_get(mjs, element, \"y\", ~0);\n    if(!mjs_is_number(x_in) || !mjs_is_number(y_in)) return false;\n    *x = mjs_get_int32(mjs, x_in);\n    *y = mjs_get_int32(mjs, y_in);\n    return true;\n}\n\n/**\n * @brief Parses size (W and h) from an element declaration object\n */\nstatic bool element_get_size(struct mjs* mjs, mjs_val_t element, int32_t* w, int32_t* h) {\n    mjs_val_t w_in = mjs_get(mjs, element, \"w\", ~0);\n    mjs_val_t h_in = mjs_get(mjs, element, \"h\", ~0);\n    if(!mjs_is_number(w_in) || !mjs_is_number(h_in)) return false;\n    *w = mjs_get_int32(mjs, w_in);\n    *h = mjs_get_int32(mjs, h_in);\n    return true;\n}\n\n/**\n * @brief Parses alignment (V and H) from an element declaration object\n */\nstatic bool\n    element_get_alignment(struct mjs* mjs, mjs_val_t element, Align* align_v, Align* align_h) {\n    mjs_val_t align_in = mjs_get(mjs, element, \"align\", ~0);\n    const char* align = mjs_get_string(mjs, &align_in, NULL);\n    if(!align) return false;\n    if(strlen(align) != 2) return false;\n\n    if(align[0] == 't') {\n        *align_v = AlignTop;\n    } else if(align[0] == 'c') {\n        *align_v = AlignCenter;\n    } else if(align[0] == 'b') {\n        *align_v = AlignBottom;\n    } else {\n        return false;\n    }\n\n    if(align[1] == 'l') {\n        *align_h = AlignLeft;\n    } else if(align[1] == 'm') { // m = middle\n        *align_h = AlignCenter;\n    } else if(align[1] == 'r') {\n        *align_h = AlignRight;\n    } else {\n        return false;\n    }\n\n    return true;\n}\n\n/**\n * @brief Parses font from an element declaration object\n */\nstatic bool element_get_font(struct mjs* mjs, mjs_val_t element, Font* font) {\n    mjs_val_t font_in = mjs_get(mjs, element, \"font\", ~0);\n    const char* font_str = mjs_get_string(mjs, &font_in, NULL);\n    if(!font_str) return false;\n\n    if(strcmp(font_str, \"primary\") == 0) {\n        *font = FontPrimary;\n    } else if(strcmp(font_str, \"secondary\") == 0) {\n        *font = FontSecondary;\n    } else if(strcmp(font_str, \"keyboard\") == 0) {\n        *font = FontKeyboard;\n    } else if(strcmp(font_str, \"big_numbers\") == 0) {\n        *font = FontBigNumbers;\n    } else {\n        return false;\n    }\n    return true;\n}\n\n/**\n * @brief Parses text from an element declaration object\n */\nstatic bool element_get_text(struct mjs* mjs, mjs_val_t element, mjs_val_t* text) {\n    *text = mjs_get(mjs, element, \"text\", ~0);\n    return mjs_is_string(*text);\n}\n\n/**\n * @brief Widget button element callback\n */\nstatic void js_widget_button_callback(GuiButtonType result, InputType type, JsWidgetCtx* context) {\n    JsWidgetButtonEvent event = {\n        .key = result,\n        .type = type,\n    };\n    furi_check(furi_message_queue_put(context->queue, &event, 0) == FuriStatusOk);\n}\n\n#define DESTRUCTURE_OR_RETURN(mjs, child_obj, part, ...) \\\n    if(!element_get_##part(mjs, child_obj, __VA_ARGS__)) \\\n        JS_ERROR_AND_RETURN_VAL(mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element \" #part);\n\nstatic bool js_widget_add_child(\n    struct mjs* mjs,\n    Widget* widget,\n    JsWidgetCtx* context,\n    mjs_val_t child_obj) {\n    UNUSED(context);\n    if(!mjs_is_object(child_obj))\n        JS_ERROR_AND_RETURN_VAL(mjs, MJS_BAD_ARGS_ERROR, false, \"child must be an object\");\n\n    mjs_val_t element_type_term = mjs_get(mjs, child_obj, \"element\", ~0);\n    const char* element_type = mjs_get_string(mjs, &element_type_term, NULL);\n    if(!element_type)\n        JS_ERROR_AND_RETURN_VAL(\n            mjs, MJS_BAD_ARGS_ERROR, false, \"child object must have `element` property\");\n\n    if((strcmp(element_type, \"string\") == 0) || (strcmp(element_type, \"string_multiline\") == 0)) {\n        int32_t x, y;\n        Align align_v, align_h;\n        Font font;\n        mjs_val_t text;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, alignment, &align_v, &align_h);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, font, &font);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, text, &text);\n        if(strcmp(element_type, \"string\") == 0) {\n            widget_add_string_element(\n                widget, x, y, align_h, align_v, font, mjs_get_string(mjs, &text, NULL));\n        } else {\n            widget_add_string_multiline_element(\n                widget, x, y, align_h, align_v, font, mjs_get_string(mjs, &text, NULL));\n        }\n\n    } else if(strcmp(element_type, \"text_box\") == 0) {\n        int32_t x, y, w, h;\n        Align align_v, align_h;\n        Font font;\n        mjs_val_t text;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, size, &w, &h);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, alignment, &align_v, &align_h);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, font, &font);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, text, &text);\n        mjs_val_t strip_to_dots_in = mjs_get(mjs, child_obj, \"stripToDots\", ~0);\n        if(!mjs_is_boolean(strip_to_dots_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element stripToDots\");\n        bool strip_to_dots = mjs_get_bool(mjs, strip_to_dots_in);\n        widget_add_text_box_element(\n            widget, x, y, w, h, align_h, align_v, mjs_get_string(mjs, &text, NULL), strip_to_dots);\n\n    } else if(strcmp(element_type, \"text_scroll\") == 0) {\n        int32_t x, y, w, h;\n        mjs_val_t text;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, size, &w, &h);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, text, &text);\n        widget_add_text_scroll_element(widget, x, y, w, h, mjs_get_string(mjs, &text, NULL));\n\n    } else if(strcmp(element_type, \"button\") == 0) {\n        mjs_val_t btn_in = mjs_get(mjs, child_obj, \"button\", ~0);\n        const char* btn_name = mjs_get_string(mjs, &btn_in, NULL);\n        if(!btn_name)\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element button\");\n        GuiButtonType btn_type;\n        if(strcmp(btn_name, \"left\") == 0) {\n            btn_type = GuiButtonTypeLeft;\n        } else if(strcmp(btn_name, \"center\") == 0) {\n            btn_type = GuiButtonTypeCenter;\n        } else if(strcmp(btn_name, \"right\") == 0) {\n            btn_type = GuiButtonTypeRight;\n        } else {\n            JS_ERROR_AND_RETURN_VAL(mjs, MJS_BAD_ARGS_ERROR, false, \"incorrect button type\");\n        }\n        mjs_val_t text;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, text, &text);\n        widget_add_button_element(\n            widget,\n            btn_type,\n            mjs_get_string(mjs, &text, NULL),\n            (ButtonCallback)js_widget_button_callback,\n            context);\n\n    } else if(strcmp(element_type, \"icon\") == 0) {\n        int32_t x, y;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        mjs_val_t icon_data_in = mjs_get(mjs, child_obj, \"iconData\", ~0);\n        if(!mjs_is_foreign(icon_data_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element iconData\");\n        const Icon* icon = mjs_get_ptr(mjs, icon_data_in);\n        widget_add_icon_element(widget, x, y, icon);\n\n    } else if(strcmp(element_type, \"rect\") == 0) {\n        int32_t x, y, w, h;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, size, &w, &h);\n        mjs_val_t radius_in = mjs_get(mjs, child_obj, \"radius\", ~0);\n        if(!mjs_is_number(radius_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element radius\");\n        int32_t radius = mjs_get_int32(mjs, radius_in);\n        mjs_val_t fill_in = mjs_get(mjs, child_obj, \"fill\", ~0);\n        if(!mjs_is_boolean(fill_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element fill\");\n        int32_t fill = mjs_get_bool(mjs, fill_in);\n        widget_add_rect_element(widget, x, y, w, h, radius, fill);\n\n    } else if(strcmp(element_type, \"circle\") == 0) {\n        int32_t x, y;\n        DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);\n        mjs_val_t radius_in = mjs_get(mjs, child_obj, \"radius\", ~0);\n        if(!mjs_is_number(radius_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element radius\");\n        int32_t radius = mjs_get_int32(mjs, radius_in);\n        mjs_val_t fill_in = mjs_get(mjs, child_obj, \"fill\", ~0);\n        if(!mjs_is_boolean(fill_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element fill\");\n        int32_t fill = mjs_get_bool(mjs, fill_in);\n        widget_add_circle_element(widget, x, y, radius, fill);\n\n    } else if(strcmp(element_type, \"line\") == 0) {\n        int32_t x1, y1, x2, y2;\n        mjs_val_t x1_in = mjs_get(mjs, child_obj, \"x1\", ~0);\n        mjs_val_t y1_in = mjs_get(mjs, child_obj, \"y1\", ~0);\n        mjs_val_t x2_in = mjs_get(mjs, child_obj, \"x2\", ~0);\n        mjs_val_t y2_in = mjs_get(mjs, child_obj, \"y2\", ~0);\n        if(!mjs_is_number(x1_in) || !mjs_is_number(y1_in) || !mjs_is_number(x2_in) ||\n           !mjs_is_number(y2_in))\n            JS_ERROR_AND_RETURN_VAL(\n                mjs, MJS_BAD_ARGS_ERROR, false, \"failed to fetch element positions\");\n        x1 = mjs_get_int32(mjs, x1_in);\n        y1 = mjs_get_int32(mjs, y1_in);\n        x2 = mjs_get_int32(mjs, x2_in);\n        y2 = mjs_get_int32(mjs, y2_in);\n        widget_add_line_element(widget, x1, y1, x2, y2);\n    }\n\n    return true;\n}\n\nstatic void js_widget_reset_children(Widget* widget, void* state) {\n    UNUSED(state);\n    widget_reset(widget);\n}\n\nstatic mjs_val_t js_widget_button_event_transformer(\n    struct mjs* mjs,\n    FuriMessageQueue* queue,\n    JsWidgetCtx* context) {\n    UNUSED(context);\n    JsWidgetButtonEvent event;\n    furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);\n    const char* event_key;\n    if(event.key == GuiButtonTypeLeft) {\n        event_key = \"left\";\n    } else if(event.key == GuiButtonTypeCenter) {\n        event_key = \"center\";\n    } else if(event.key == GuiButtonTypeRight) {\n        event_key = \"right\";\n    } else {\n        furi_crash();\n    }\n    const char* event_type;\n    if(event.type == InputTypePress) {\n        event_type = \"press\";\n    } else if(event.type == InputTypeRelease) {\n        event_type = \"release\";\n    } else if(event.type == InputTypeShort) {\n        event_type = \"short\";\n    } else if(event.type == InputTypeLong) {\n        event_type = \"long\";\n    } else if(event.type == InputTypeRepeat) {\n        event_type = \"repeat\";\n    } else {\n        furi_crash();\n    }\n    mjs_val_t obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, obj) {\n        JS_FIELD(\"key\", mjs_mk_string(mjs, event_key, ~0, true));\n        JS_FIELD(\"type\", mjs_mk_string(mjs, event_type, ~0, true));\n    }\n    return obj;\n}\n\nstatic void* js_widget_custom_make(struct mjs* mjs, Widget* widget, mjs_val_t view_obj) {\n    UNUSED(widget);\n    JsWidgetCtx* context = malloc(sizeof(JsWidgetCtx));\n    context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(JsWidgetButtonEvent));\n    context->contract = (JsEventLoopContract){\n        .magic = JsForeignMagic_JsEventLoopContract,\n        .object_type = JsEventLoopObjectTypeQueue,\n        .object = context->queue,\n        .non_timer =\n            {\n                .event = FuriEventLoopEventIn,\n                .transformer = (JsEventLoopTransformer)js_widget_button_event_transformer,\n            },\n    };\n    mjs_set(mjs, view_obj, \"button\", ~0, mjs_mk_foreign(mjs, &context->contract));\n    return context;\n}\n\nstatic void js_widget_custom_destroy(Widget* widget, JsWidgetCtx* context, FuriEventLoop* loop) {\n    UNUSED(widget);\n    furi_event_loop_maybe_unsubscribe(loop, context->queue);\n    furi_message_queue_free(context->queue);\n    free(context);\n}\n\nstatic const JsViewDescriptor view_descriptor = {\n    .alloc = (JsViewAlloc)widget_alloc,\n    .free = (JsViewFree)widget_free,\n    .get_view = (JsViewGetView)widget_get_view,\n    .custom_make = (JsViewCustomMake)js_widget_custom_make,\n    .custom_destroy = (JsViewCustomDestroy)js_widget_custom_destroy,\n    .add_child = (JsViewAddChild)js_widget_add_child,\n    .reset_children = (JsViewResetChildren)js_widget_reset_children,\n    .prop_cnt = 0,\n    .props = {},\n};\nJS_GUI_VIEW_DEF(widget, &view_descriptor);\n"
  },
  {
    "path": "applications/system/js_app/modules/js_math.c",
    "content": "#include \"../js_modules.h\"\n#include \"furi_hal_random.h\"\n#include <float.h>\n\n#define JS_MATH_PI      ((double)M_PI)\n#define JS_MATH_E       ((double)M_E)\n#define JS_MATH_EPSILON ((double)DBL_EPSILON)\n\n#define TAG \"JsMath\"\n\nstatic void ret_bad_args(struct mjs* mjs, const char* error) {\n    mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"%s\", error);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic bool check_args(struct mjs* mjs, size_t count) {\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args != count) {\n        ret_bad_args(mjs, \"Wrong argument count\");\n        return false;\n    }\n    for(size_t i = 0; i < count; i++) {\n        if(!mjs_is_number(mjs_arg(mjs, i))) {\n            ret_bad_args(mjs, \"Wrong argument type\");\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid js_math_is_equal(struct mjs* mjs) {\n    if(!check_args(mjs, 3)) {\n        return;\n    }\n\n    double a = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    double b = mjs_get_double(mjs, mjs_arg(mjs, 1));\n    double e = mjs_get_double(mjs, mjs_arg(mjs, 2));\n    double f = fabs(a - b);\n\n    mjs_return(mjs, mjs_mk_boolean(mjs, (f <= e)));\n}\n\nvoid js_math_abs(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, fabs(x)));\n}\n\nvoid js_math_acos(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    if(x < (double)-1. || x > (double)1.) {\n        ret_bad_args(mjs, \"Invalid input value for math.acos\");\n        return;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, acos(x)));\n}\n\nvoid js_math_acosh(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    if(x < (double)1.) {\n        ret_bad_args(mjs, \"Invalid input value for math.acosh\");\n        return;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - (double)1.))));\n}\n\nvoid js_math_asin(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, asin(x)));\n}\n\nvoid js_math_asinh(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + (double)1.))));\n}\n\nvoid js_math_atan(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, atan(x)));\n}\n\nvoid js_math_atan2(struct mjs* mjs) {\n    if(!check_args(mjs, 2)) {\n        return;\n    }\n\n    double y = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 1));\n\n    mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x)));\n}\n\nvoid js_math_atanh(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    if(x < (double)-1. || x > (double)1.) {\n        ret_bad_args(mjs, \"Invalid input value for math.atanh\");\n        return;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log(((double)1. + x) / ((double)1. - x))));\n}\n\nvoid js_math_cbrt(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, cbrt(x)));\n}\n\nvoid js_math_ceil(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    mjs_return(mjs, mjs_mk_number(mjs, ceil(x)));\n}\n\nvoid js_math_clz32(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0));\n    int count = 0;\n    while(x) {\n        x >>= 1;\n        count++;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, 32 - count));\n}\n\nvoid js_math_cos(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, cos(x)));\n}\n\nvoid js_math_exp(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, exp(x)));\n}\n\nvoid js_math_floor(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, floor(x)));\n}\n\nvoid js_math_log(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    if(x <= 0) {\n        ret_bad_args(mjs, \"Invalid input value for math.log\");\n        return;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, log(x)));\n}\n\nvoid js_math_max(struct mjs* mjs) {\n    if(!check_args(mjs, 2)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    double y = mjs_get_double(mjs, mjs_arg(mjs, 1));\n\n    mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y));\n}\n\nvoid js_math_min(struct mjs* mjs) {\n    if(!check_args(mjs, 2)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    double y = mjs_get_double(mjs, mjs_arg(mjs, 1));\n\n    mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y));\n}\n\nvoid js_math_pow(struct mjs* mjs) {\n    if(!check_args(mjs, 2)) {\n        return;\n    }\n\n    double base = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1));\n\n    mjs_return(mjs, mjs_mk_number(mjs, pow(base, exponent)));\n}\n\nvoid js_math_random(struct mjs* mjs) {\n    if(!check_args(mjs, 0)) {\n        return;\n    }\n\n    // double clearly provides more bits for entropy then we pack\n    // 32bit should be enough for now, but fix it maybe\n    const uint32_t random_val = furi_hal_random_get();\n    double rnd = (double)random_val / (double)FURI_HAL_RANDOM_MAX;\n\n    mjs_return(mjs, mjs_mk_number(mjs, rnd));\n}\n\nvoid js_math_sign(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(\n        mjs,\n        mjs_mk_number(\n            mjs, fabs(x) <= JS_MATH_EPSILON ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0)));\n}\n\nvoid js_math_sin(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, sin(x)));\n}\n\nvoid js_math_sqrt(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n    if(x < (double)0.) {\n        ret_bad_args(mjs, \"Invalid input value for math.sqrt\");\n        return;\n    }\n\n    mjs_return(mjs, mjs_mk_number(mjs, sqrt(x)));\n}\n\nvoid js_math_trunc(struct mjs* mjs) {\n    if(!check_args(mjs, 1)) {\n        return;\n    }\n\n    double x = mjs_get_double(mjs, mjs_arg(mjs, 0));\n\n    mjs_return(mjs, mjs_mk_number(mjs, x < (double)0. ? ceil(x) : floor(x)));\n}\n\nstatic void* js_math_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    mjs_val_t math_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, math_obj, \"isEqual\", ~0, MJS_MK_FN(js_math_is_equal));\n    mjs_set(mjs, math_obj, \"abs\", ~0, MJS_MK_FN(js_math_abs));\n    mjs_set(mjs, math_obj, \"acos\", ~0, MJS_MK_FN(js_math_acos));\n    mjs_set(mjs, math_obj, \"acosh\", ~0, MJS_MK_FN(js_math_acosh));\n    mjs_set(mjs, math_obj, \"asin\", ~0, MJS_MK_FN(js_math_asin));\n    mjs_set(mjs, math_obj, \"asinh\", ~0, MJS_MK_FN(js_math_asinh));\n    mjs_set(mjs, math_obj, \"atan\", ~0, MJS_MK_FN(js_math_atan));\n    mjs_set(mjs, math_obj, \"atan2\", ~0, MJS_MK_FN(js_math_atan2));\n    mjs_set(mjs, math_obj, \"atanh\", ~0, MJS_MK_FN(js_math_atanh));\n    mjs_set(mjs, math_obj, \"cbrt\", ~0, MJS_MK_FN(js_math_cbrt));\n    mjs_set(mjs, math_obj, \"ceil\", ~0, MJS_MK_FN(js_math_ceil));\n    mjs_set(mjs, math_obj, \"clz32\", ~0, MJS_MK_FN(js_math_clz32));\n    mjs_set(mjs, math_obj, \"cos\", ~0, MJS_MK_FN(js_math_cos));\n    mjs_set(mjs, math_obj, \"exp\", ~0, MJS_MK_FN(js_math_exp));\n    mjs_set(mjs, math_obj, \"floor\", ~0, MJS_MK_FN(js_math_floor));\n    mjs_set(mjs, math_obj, \"log\", ~0, MJS_MK_FN(js_math_log));\n    mjs_set(mjs, math_obj, \"max\", ~0, MJS_MK_FN(js_math_max));\n    mjs_set(mjs, math_obj, \"min\", ~0, MJS_MK_FN(js_math_min));\n    mjs_set(mjs, math_obj, \"pow\", ~0, MJS_MK_FN(js_math_pow));\n    mjs_set(mjs, math_obj, \"random\", ~0, MJS_MK_FN(js_math_random));\n    mjs_set(mjs, math_obj, \"sign\", ~0, MJS_MK_FN(js_math_sign));\n    mjs_set(mjs, math_obj, \"sin\", ~0, MJS_MK_FN(js_math_sin));\n    mjs_set(mjs, math_obj, \"sqrt\", ~0, MJS_MK_FN(js_math_sqrt));\n    mjs_set(mjs, math_obj, \"trunc\", ~0, MJS_MK_FN(js_math_trunc));\n    mjs_set(mjs, math_obj, \"PI\", ~0, mjs_mk_number(mjs, JS_MATH_PI));\n    mjs_set(mjs, math_obj, \"E\", ~0, mjs_mk_number(mjs, JS_MATH_E));\n    mjs_set(mjs, math_obj, \"EPSILON\", ~0, mjs_mk_number(mjs, JS_MATH_EPSILON));\n    *object = math_obj;\n    return (void*)1;\n}\n\nstatic const JsModuleDescriptor js_math_desc = {\n    \"math\",\n    js_math_create,\n    NULL,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_math_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_math_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_notification.c",
    "content": "#include <core/common_defines.h>\n#include \"../js_modules.h\"\n#include <notification/notification_messages.h>\n\nstatic void js_notify(struct mjs* mjs, const NotificationSequence* sequence) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    NotificationApp* notification = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(notification);\n    notification_message(notification, sequence);\n}\n\nstatic void js_notify_success(struct mjs* mjs) {\n    js_notify(mjs, &sequence_success);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_notify_error(struct mjs* mjs) {\n    js_notify(mjs, &sequence_error);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic const struct {\n    const char* color_name;\n    const NotificationSequence* sequence_short;\n    const NotificationSequence* sequence_long;\n} led_sequences[] = {\n    {\"blue\", &sequence_blink_blue_10, &sequence_blink_blue_100},\n    {\"red\", &sequence_blink_red_10, &sequence_blink_red_100},\n    {\"green\", &sequence_blink_green_10, &sequence_blink_green_100},\n    {\"yellow\", &sequence_blink_yellow_10, &sequence_blink_yellow_100},\n    {\"cyan\", &sequence_blink_cyan_10, &sequence_blink_cyan_100},\n    {\"magenta\", &sequence_blink_magenta_10, &sequence_blink_magenta_100},\n};\n\nstatic void js_notify_blink(struct mjs* mjs) {\n    const NotificationSequence* sequence = NULL;\n    do {\n        size_t num_args = mjs_nargs(mjs);\n        if(num_args != 2) {\n            break;\n        }\n        mjs_val_t color_obj = mjs_arg(mjs, 0);\n        mjs_val_t type_obj = mjs_arg(mjs, 1);\n        if((!mjs_is_string(color_obj)) || (!mjs_is_string(type_obj))) break;\n\n        size_t arg_len = 0;\n        const char* arg_str = mjs_get_string(mjs, &color_obj, &arg_len);\n        if((arg_len == 0) || (arg_str == NULL)) break;\n\n        int32_t color_id = -1;\n        for(size_t i = 0; i < COUNT_OF(led_sequences); i++) {\n            size_t name_len = strlen(led_sequences[i].color_name);\n            if(arg_len != name_len) continue;\n            if(strncmp(arg_str, led_sequences[i].color_name, arg_len) == 0) {\n                color_id = i;\n                break;\n            }\n        }\n        if(color_id == -1) break;\n\n        arg_str = mjs_get_string(mjs, &type_obj, &arg_len);\n        if((arg_len == 0) || (arg_str == NULL)) break;\n        if(strncmp(arg_str, \"short\", arg_len) == 0) {\n            sequence = led_sequences[color_id].sequence_short;\n        } else if(strncmp(arg_str, \"long\", arg_len) == 0) {\n            sequence = led_sequences[color_id].sequence_long;\n        }\n    } while(0);\n\n    if(sequence == NULL) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n    } else {\n        js_notify(mjs, sequence);\n    }\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void* js_notification_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n    mjs_val_t notify_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, notify_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, notification));\n    mjs_set(mjs, notify_obj, \"success\", ~0, MJS_MK_FN(js_notify_success));\n    mjs_set(mjs, notify_obj, \"error\", ~0, MJS_MK_FN(js_notify_error));\n    mjs_set(mjs, notify_obj, \"blink\", ~0, MJS_MK_FN(js_notify_blink));\n    *object = notify_obj;\n\n    return notification;\n}\n\nstatic void js_notification_destroy(void* inst) {\n    UNUSED(inst);\n    furi_record_close(RECORD_NOTIFICATION);\n}\n\nstatic const JsModuleDescriptor js_notification_desc = {\n    \"notification\",\n    js_notification_create,\n    js_notification_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_notification_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_notification_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_serial.c",
    "content": "#include <core/common_defines.h>\n#include <expansion/expansion.h>\n#include <furi_hal.h>\n#include \"../js_modules.h\"\n#include <m-array.h>\n\n#define TAG \"JsSerial\"\n\n#define RX_BUF_LEN 2048\n\ntypedef struct {\n    bool setup_done;\n    FuriStreamBuffer* rx_stream;\n    FuriHalSerialHandle* serial_handle;\n    struct mjs* mjs;\n} JsSerialInst;\n\ntypedef struct {\n    size_t len;\n    char* data;\n} PatternArrayItem;\n\nARRAY_DEF(PatternArray, PatternArrayItem, M_POD_OPLIST); //-V658\n\nstatic void\n    js_serial_on_async_rx(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {\n    JsSerialInst* serial = context;\n    furi_assert(serial);\n\n    if(event & FuriHalSerialRxEventData) {\n        uint8_t data = furi_hal_serial_async_rx(handle);\n        furi_stream_buffer_send(serial->rx_stream, &data, 1, 0);\n        js_flags_set(serial->mjs, ThreadEventCustomDataRx);\n    }\n}\n\nstatic void js_serial_setup(struct mjs* mjs) {\n    static const JsValueEnumVariant js_serial_id_variants[] = {\n        {\"lpuart\", FuriHalSerialIdLpuart},\n        {\"usart\", FuriHalSerialIdUsart},\n    };\n\n    static const JsValueEnumVariant js_serial_data_bit_variants[] = {\n        {\"6\", FuriHalSerialDataBits6},\n        {\"7\", FuriHalSerialDataBits7},\n        {\"8\", FuriHalSerialDataBits8},\n        {\"9\", FuriHalSerialDataBits9},\n    };\n    static const JsValueDeclaration js_serial_data_bits = JS_VALUE_ENUM_W_DEFAULT(\n        FuriHalSerialDataBits, js_serial_data_bit_variants, FuriHalSerialDataBits8);\n\n    static const JsValueEnumVariant js_serial_parity_variants[] = {\n        {\"none\", FuriHalSerialParityNone},\n        {\"even\", FuriHalSerialParityEven},\n        {\"odd\", FuriHalSerialParityOdd},\n    };\n    static const JsValueDeclaration js_serial_parity = JS_VALUE_ENUM_W_DEFAULT(\n        FuriHalSerialParity, js_serial_parity_variants, FuriHalSerialParityNone);\n\n    static const JsValueEnumVariant js_serial_stop_bit_variants[] = {\n        {\"0.5\", FuriHalSerialStopBits0_5},\n        {\"1\", FuriHalSerialStopBits1},\n        {\"1.5\", FuriHalSerialStopBits1_5},\n        {\"2\", FuriHalSerialStopBits2},\n    };\n    static const JsValueDeclaration js_serial_stop_bits = JS_VALUE_ENUM_W_DEFAULT(\n        FuriHalSerialStopBits, js_serial_stop_bit_variants, FuriHalSerialStopBits1);\n\n    static const JsValueObjectField js_serial_framing_fields[] = {\n        {\"dataBits\", &js_serial_data_bits},\n        {\"parity\", &js_serial_parity},\n        {\"stopBits\", &js_serial_stop_bits},\n    };\n\n    static const JsValueDeclaration js_serial_setup_arg_list[] = {\n        JS_VALUE_ENUM(FuriHalSerialId, js_serial_id_variants),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n        JS_VALUE_OBJECT_W_DEFAULTS(js_serial_framing_fields),\n    };\n    static const JsValueArguments js_serial_setup_args = JS_VALUE_ARGS(js_serial_setup_arg_list);\n\n    FuriHalSerialId serial_id;\n    int32_t baudrate;\n    FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;\n    FuriHalSerialParity parity = FuriHalSerialParityNone;\n    FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(\n        mjs, &js_serial_setup_args, &serial_id, &baudrate, &data_bits, &parity, &stop_bits);\n\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n\n    if(serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is already configured\");\n\n    expansion_disable(furi_record_open(RECORD_EXPANSION));\n    furi_record_close(RECORD_EXPANSION);\n\n    serial->serial_handle = furi_hal_serial_control_acquire(serial_id);\n    if(serial->serial_handle) {\n        serial->rx_stream = furi_stream_buffer_alloc(RX_BUF_LEN, 1);\n        furi_hal_serial_init(serial->serial_handle, baudrate);\n        furi_hal_serial_configure_framing(serial->serial_handle, data_bits, parity, stop_bits);\n        furi_hal_serial_async_rx_start(\n            serial->serial_handle, js_serial_on_async_rx, serial, false);\n        serial->setup_done = true;\n    } else {\n        expansion_enable(furi_record_open(RECORD_EXPANSION));\n        furi_record_close(RECORD_EXPANSION);\n    }\n}\n\nstatic void js_serial_deinit(JsSerialInst* js_serial) {\n    if(js_serial->setup_done) {\n        furi_hal_serial_async_rx_stop(js_serial->serial_handle);\n        furi_hal_serial_deinit(js_serial->serial_handle);\n        furi_hal_serial_control_release(js_serial->serial_handle);\n        js_serial->serial_handle = NULL;\n        furi_stream_buffer_free(js_serial->rx_stream);\n\n        expansion_enable(furi_record_open(RECORD_EXPANSION));\n        furi_record_close(RECORD_EXPANSION);\n\n        js_serial->setup_done = false;\n    }\n}\n\nstatic void js_serial_end(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    js_serial_deinit(serial);\n}\n\nstatic void js_serial_write(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    bool args_correct = true;\n\n    size_t num_args = mjs_nargs(mjs);\n    for(size_t i = 0; i < num_args; i++) {\n        mjs_val_t arg = mjs_arg(mjs, i);\n        if(mjs_is_string(arg)) {\n            size_t str_len = 0;\n            const char* arg_str = mjs_get_string(mjs, &arg, &str_len);\n            if((str_len == 0) || (arg_str == NULL)) {\n                args_correct = false;\n                break;\n            }\n            furi_hal_serial_tx(serial->serial_handle, (uint8_t*)arg_str, str_len);\n        } else if(mjs_is_number(arg)) {\n            uint32_t byte_val = mjs_get_int32(mjs, arg);\n            if(byte_val > 0xFF) {\n                args_correct = false;\n                break;\n            }\n            furi_hal_serial_tx(serial->serial_handle, (uint8_t*)&byte_val, 1);\n        } else if(mjs_is_array(arg)) {\n            size_t array_len = mjs_array_length(mjs, arg);\n            for(size_t i = 0; i < array_len; i++) {\n                mjs_val_t array_arg = mjs_array_get(mjs, arg, i);\n                if(!mjs_is_number(array_arg)) {\n                    args_correct = false;\n                    break;\n                }\n                uint32_t byte_val = mjs_get_int32(mjs, array_arg);\n                if(byte_val > 0xFF) {\n                    args_correct = false;\n                    break;\n                }\n                furi_hal_serial_tx(serial->serial_handle, (uint8_t*)&byte_val, 1);\n            }\n            if(!args_correct) {\n                break;\n            }\n        } else if(mjs_is_typed_array(arg)) {\n            mjs_val_t array_buf = arg;\n            if(mjs_is_data_view(arg)) {\n                array_buf = mjs_dataview_get_buf(mjs, arg);\n            }\n            size_t len = 0;\n            char* buf = mjs_array_buf_get_ptr(mjs, array_buf, &len);\n            furi_hal_serial_tx(serial->serial_handle, (uint8_t*)buf, len);\n        } else {\n            args_correct = false;\n            break;\n        }\n    }\n\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n    }\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic size_t js_serial_receive(JsSerialInst* serial, char* buf, size_t len, uint32_t timeout) {\n    size_t bytes_read = 0;\n    while(1) {\n        uint32_t flags = ThreadEventCustomDataRx;\n        if(furi_stream_buffer_is_empty(serial->rx_stream)) {\n            flags = js_flags_wait(serial->mjs, ThreadEventCustomDataRx, timeout);\n        }\n        if(flags == 0) { // Timeout\n            break;\n        } else if(flags & ThreadEventStop) { // Exit flag\n            bytes_read = 0;\n            break;\n        } else if(flags & ThreadEventCustomDataRx) { // New data received\n            size_t rx_len = furi_stream_buffer_receive(\n                serial->rx_stream, &buf[bytes_read], len - bytes_read, 0);\n            bytes_read += rx_len;\n            if(bytes_read == len) {\n                break;\n            }\n        }\n    }\n    return bytes_read;\n}\n\nstatic const JsValueDeclaration js_serial_read_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeInt32),\n    JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),\n};\nstatic const JsValueArguments js_serial_read_args = JS_VALUE_ARGS(js_serial_read_arg_list);\n\nstatic void js_serial_read(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    int32_t read_len, timeout;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);\n\n    char* read_buf = malloc(read_len);\n    size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);\n\n    mjs_val_t return_obj = MJS_UNDEFINED;\n    if(bytes_read > 0) {\n        return_obj = mjs_mk_string(mjs, read_buf, bytes_read, true);\n    }\n    mjs_return(mjs, return_obj);\n    free(read_buf);\n}\n\nstatic void js_serial_readln(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    static const JsValueDeclaration js_serial_readln_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_serial_readln_args = JS_VALUE_ARGS(js_serial_readln_arg_list);\n\n    int32_t timeout;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_readln_args, &timeout);\n\n    FuriString* rx_buf = furi_string_alloc();\n    size_t bytes_read = 0;\n    char read_char = 0;\n\n    while(1) {\n        size_t read_len = js_serial_receive(serial, &read_char, 1, timeout);\n        if(read_len != 1) {\n            break;\n        }\n        if((read_char == '\\r') || (read_char == '\\n')) {\n            break;\n        } else {\n            furi_string_push_back(rx_buf, read_char);\n            bytes_read++;\n        }\n    }\n\n    mjs_val_t return_obj = MJS_UNDEFINED;\n    if(bytes_read > 0) {\n        return_obj = mjs_mk_string(mjs, furi_string_get_cstr(rx_buf), bytes_read, true);\n    }\n    mjs_return(mjs, return_obj);\n    furi_string_free(rx_buf);\n}\n\nstatic void js_serial_read_bytes(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    int32_t read_len, timeout;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);\n\n    char* read_buf = malloc(read_len);\n    size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);\n\n    mjs_val_t return_obj = MJS_UNDEFINED;\n    if(bytes_read > 0) {\n        return_obj = mjs_mk_array_buf(mjs, read_buf, bytes_read);\n    }\n    mjs_return(mjs, return_obj);\n    free(read_buf);\n}\n\nstatic char* js_serial_receive_any(JsSerialInst* serial, size_t* len, uint32_t timeout) {\n    uint32_t flags = ThreadEventCustomDataRx;\n    if(furi_stream_buffer_is_empty(serial->rx_stream)) {\n        flags = js_flags_wait(serial->mjs, ThreadEventCustomDataRx, timeout);\n    }\n    if(flags & ThreadEventCustomDataRx) { // New data received\n        *len = furi_stream_buffer_bytes_available(serial->rx_stream);\n        if(!*len) return NULL;\n        char* buf = malloc(*len);\n        furi_stream_buffer_receive(serial->rx_stream, buf, *len, 0);\n        return buf;\n    }\n    return NULL;\n}\n\nstatic void js_serial_read_any(struct mjs* mjs) {\n    JsSerialInst* serial = JS_GET_CONTEXT(mjs);\n    furi_assert(serial);\n    if(!serial->setup_done)\n        JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n\n    static const JsValueDeclaration js_serial_read_any_arg_list[] = {\n        JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),\n    };\n    static const JsValueArguments js_serial_read_any_args =\n        JS_VALUE_ARGS(js_serial_read_any_arg_list);\n\n    int32_t timeout;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_any_args, &timeout);\n\n    size_t bytes_read = 0;\n    char* read_buf = js_serial_receive_any(serial, &bytes_read, timeout);\n\n    mjs_val_t return_obj = MJS_UNDEFINED;\n    if(bytes_read > 0 && read_buf) {\n        return_obj = mjs_mk_string(mjs, read_buf, bytes_read, true);\n    }\n    mjs_return(mjs, return_obj);\n    free(read_buf);\n}\n\nstatic bool\n    js_serial_expect_parse_string(struct mjs* mjs, mjs_val_t arg, PatternArray_t patterns) {\n    size_t str_len = 0;\n    const char* arg_str = mjs_get_string(mjs, &arg, &str_len);\n    if((str_len == 0) || (arg_str == NULL)) {\n        return false;\n    }\n    PatternArrayItem* item = PatternArray_push_new(patterns);\n    item->data = malloc(str_len + 1);\n    memcpy(item->data, arg_str, str_len);\n    item->len = str_len;\n    return true;\n}\n\nstatic bool js_serial_expect_parse_array(struct mjs* mjs, mjs_val_t arg, PatternArray_t patterns) {\n    size_t array_len = mjs_array_length(mjs, arg);\n    if(array_len == 0) {\n        return false;\n    }\n    char* array_data = malloc(array_len + 1);\n\n    for(size_t i = 0; i < array_len; i++) {\n        mjs_val_t array_arg = mjs_array_get(mjs, arg, i);\n        if(!mjs_is_number(array_arg)) {\n            free(array_data);\n            return false;\n        }\n\n        uint32_t byte_val = mjs_get_int32(mjs, array_arg);\n        if(byte_val > 0xFF) {\n            free(array_data);\n            return false;\n        }\n        array_data[i] = byte_val;\n    }\n\n    PatternArrayItem* item = PatternArray_push_new(patterns);\n    item->data = array_data;\n    item->len = array_len;\n    return true;\n}\n\nstatic bool\n    js_serial_expect_parse_args(struct mjs* mjs, PatternArray_t patterns, uint32_t* timeout) {\n    size_t num_args = mjs_nargs(mjs);\n    if(num_args == 2) {\n        mjs_val_t timeout_arg = mjs_arg(mjs, 1);\n        if(!mjs_is_number(timeout_arg)) {\n            return false;\n        }\n        *timeout = mjs_get_int32(mjs, timeout_arg);\n    } else if(num_args != 1) {\n        return false;\n    }\n    mjs_val_t patterns_arg = mjs_arg(mjs, 0);\n    if(mjs_is_string(patterns_arg)) { // Single string pattern\n        if(!js_serial_expect_parse_string(mjs, patterns_arg, patterns)) {\n            return false;\n        }\n    } else if(mjs_is_array(patterns_arg)) {\n        size_t array_len = mjs_array_length(mjs, patterns_arg);\n        if(array_len == 0) {\n            return false;\n        }\n        mjs_val_t array_arg = mjs_array_get(mjs, patterns_arg, 0);\n\n        if(mjs_is_number(array_arg)) { // Binary array pattern\n            if(!js_serial_expect_parse_array(mjs, patterns_arg, patterns)) {\n                return false;\n            }\n        } else if((mjs_is_string(array_arg)) || (mjs_is_array(array_arg))) { // Multiple patterns\n            for(size_t i = 0; i < array_len; i++) {\n                mjs_val_t arg = mjs_array_get(mjs, patterns_arg, i);\n\n                if(mjs_is_string(arg)) {\n                    if(!js_serial_expect_parse_string(mjs, arg, patterns)) {\n                        return false;\n                    }\n                } else if(mjs_is_array(arg)) {\n                    if(!js_serial_expect_parse_array(mjs, arg, patterns)) {\n                        return false;\n                    }\n                }\n            }\n        } else {\n            return false;\n        }\n    } else {\n        return false;\n    }\n    return true;\n}\n\nstatic int32_t js_serial_expect_check_pattern_start(\n    PatternArray_t patterns,\n    char value,\n    int32_t pattern_last) {\n    size_t array_len = PatternArray_size(patterns);\n    if((pattern_last + 1) >= (int32_t)array_len) {\n        return -1;\n    }\n    for(size_t i = pattern_last + 1; i < array_len; i++) {\n        if(PatternArray_get(patterns, i)->data[0] == value) {\n            return i;\n        }\n    }\n    return -1;\n}\n\nstatic void js_serial_expect(struct mjs* mjs) {\n    mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);\n    JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);\n    furi_assert(serial);\n    if(!serial->setup_done) {\n        mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"Serial is not configured\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    uint32_t timeout = FuriWaitForever;\n    PatternArray_t patterns;\n    PatternArray_it_t it;\n    PatternArray_init(patterns);\n\n    if(!js_serial_expect_parse_args(mjs, patterns, &timeout)) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        for(PatternArray_it(it, patterns); !PatternArray_end_p(it); PatternArray_next(it)) {\n            const PatternArrayItem* item = PatternArray_cref(it);\n            free(item->data);\n        }\n        PatternArray_clear(patterns);\n        return;\n    }\n\n    size_t pattern_len_max = 0;\n    for(PatternArray_it(it, patterns); !PatternArray_end_p(it); PatternArray_next(it)) {\n        const PatternArrayItem* item = PatternArray_cref(it);\n        if(item->len > pattern_len_max) {\n            pattern_len_max = item->len;\n        }\n    }\n\n    char* compare_buf = malloc(pattern_len_max);\n    int32_t pattern_found = -1;\n    int32_t pattern_candidate = -1;\n    size_t buf_len = 0;\n    bool is_timeout = false;\n\n    while(1) {\n        if(buf_len == 0) {\n            // Empty buffer - read by 1 byte to find pattern start\n            size_t bytes_read = js_serial_receive(serial, &compare_buf[0], 1, timeout);\n            if(bytes_read != 1) {\n                is_timeout = true;\n                break;\n            }\n            pattern_candidate = js_serial_expect_check_pattern_start(patterns, compare_buf[0], -1);\n            if(pattern_candidate == -1) {\n                continue;\n            }\n            buf_len = 1;\n        }\n        assert(pattern_candidate >= 0);\n\n        // Read next and try to find pattern match\n        PatternArrayItem* pattern_cur = PatternArray_get(patterns, pattern_candidate);\n        pattern_found = pattern_candidate;\n        for(size_t i = 0; i < pattern_cur->len; i++) {\n            if(i >= buf_len) {\n                size_t bytes_read = js_serial_receive(serial, &compare_buf[i], 1, timeout);\n                if(bytes_read != 1) {\n                    is_timeout = true;\n                    break;\n                }\n                buf_len++;\n            }\n            if(compare_buf[i] != pattern_cur->data[i]) {\n                pattern_found = -1;\n                break;\n            }\n        }\n        if((is_timeout) || (pattern_found >= 0)) {\n            break;\n        }\n\n        // Search other patterns with the same start char\n        pattern_candidate =\n            js_serial_expect_check_pattern_start(patterns, compare_buf[0], pattern_candidate);\n        if(pattern_candidate >= 0) {\n            continue;\n        }\n\n        // Look for another pattern start\n        for(size_t i = 1; i < buf_len; i++) {\n            pattern_candidate = js_serial_expect_check_pattern_start(patterns, compare_buf[i], -1);\n            if(pattern_candidate >= 0) {\n                memmove(&compare_buf[0], &compare_buf[i], buf_len - i);\n                buf_len -= i;\n                break;\n            }\n        }\n        if(pattern_candidate >= 0) {\n            continue;\n        }\n        // Nothing found - reset buffer\n        buf_len = 0;\n    }\n\n    if(is_timeout) {\n        FURI_LOG_W(TAG, \"Expect: timeout\");\n    }\n\n    for(PatternArray_it(it, patterns); !PatternArray_end_p(it); PatternArray_next(it)) {\n        const PatternArrayItem* item = PatternArray_cref(it);\n        free(item->data);\n    }\n    PatternArray_clear(patterns);\n    free(compare_buf);\n\n    if(pattern_found >= 0) {\n        mjs_return(mjs, mjs_mk_number(mjs, pattern_found));\n    } else {\n        mjs_return(mjs, MJS_UNDEFINED);\n    }\n}\n\nstatic void* js_serial_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    JsSerialInst* js_serial = malloc(sizeof(JsSerialInst));\n    js_serial->mjs = mjs;\n\n    mjs_val_t serial_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, serial_obj) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_serial));\n        JS_FIELD(\"setup\", MJS_MK_FN(js_serial_setup));\n        JS_FIELD(\"end\", MJS_MK_FN(js_serial_end));\n        JS_FIELD(\"write\", MJS_MK_FN(js_serial_write));\n        JS_FIELD(\"read\", MJS_MK_FN(js_serial_read));\n        JS_FIELD(\"readln\", MJS_MK_FN(js_serial_readln));\n        JS_FIELD(\"readBytes\", MJS_MK_FN(js_serial_read_bytes));\n        JS_FIELD(\"readAny\", MJS_MK_FN(js_serial_read_any));\n        JS_FIELD(\"expect\", MJS_MK_FN(js_serial_expect));\n    }\n    *object = serial_obj;\n\n    return js_serial;\n}\n\nstatic void js_serial_destroy(void* inst) {\n    JsSerialInst* js_serial = inst;\n    js_serial_deinit(js_serial);\n    free(js_serial);\n}\n\nstatic const JsModuleDescriptor js_serial_desc = {\n    \"serial\",\n    js_serial_create,\n    js_serial_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_serial_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_serial_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_storage.c",
    "content": "#include \"../js_modules.h\" // IWYU pragma: keep\n#include <path.h>\n\n// ==========================\n// Common argument signatures\n// ==========================\n\nstatic const JsValueDeclaration js_storage_1_int_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeInt32),\n};\nstatic const JsValueArguments js_storage_1_int_args = JS_VALUE_ARGS(js_storage_1_int_arg_list);\n\nstatic const JsValueDeclaration js_storage_1_str_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeString),\n};\nstatic const JsValueArguments js_storage_1_str_args = JS_VALUE_ARGS(js_storage_1_str_arg_list);\n\nstatic const JsValueDeclaration js_storage_2_str_arg_list[] = {\n    JS_VALUE_SIMPLE(JsValueTypeString),\n    JS_VALUE_SIMPLE(JsValueTypeString),\n};\nstatic const JsValueArguments js_storage_2_str_args = JS_VALUE_ARGS(js_storage_2_str_arg_list);\n\n// ======================\n// File object operations\n// ======================\n\nstatic void js_storage_file_close(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_close(file)));\n}\n\nstatic void js_storage_file_is_open(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_is_open(file)));\n}\n\nstatic void js_storage_file_read(struct mjs* mjs) {\n    typedef enum {\n        JsStorageReadModeAscii,\n        JsStorageReadModeBinary,\n    } JsStorageReadMode;\n    static const JsValueEnumVariant js_storage_read_mode_variants[] = {\n        {\"ascii\", JsStorageReadModeAscii},\n        {\"binary\", JsStorageReadModeBinary},\n    };\n    static const JsValueDeclaration js_storage_read_arg_list[] = {\n        JS_VALUE_ENUM(JsStorageReadMode, js_storage_read_mode_variants),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_storage_read_args = JS_VALUE_ARGS(js_storage_read_arg_list);\n\n    JsStorageReadMode read_mode;\n    int32_t length;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_read_args, &read_mode, &length);\n\n    File* file = JS_GET_CONTEXT(mjs);\n    char buffer[length];\n    size_t actually_read = storage_file_read(file, buffer, length);\n    if(read_mode == JsStorageReadModeAscii) {\n        mjs_return(mjs, mjs_mk_string(mjs, buffer, actually_read, true));\n    } else if(read_mode == JsStorageReadModeBinary) {\n        mjs_return(mjs, mjs_mk_array_buf(mjs, buffer, actually_read));\n    }\n}\n\nstatic void js_storage_file_write(struct mjs* mjs) {\n    static const JsValueDeclaration js_storage_file_write_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n    };\n    static const JsValueArguments js_storage_file_write_args =\n        JS_VALUE_ARGS(js_storage_file_write_arg_list);\n\n    mjs_val_t data;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &data);\n\n    const void* buf;\n    size_t len;\n    if(mjs_is_string(data)) {\n        buf = mjs_get_string(mjs, &data, &len);\n    } else if(mjs_is_array_buf(data)) {\n        buf = mjs_array_buf_get_ptr(mjs, data, &len);\n    } else {\n        JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, \"argument 0: expected string or ArrayBuffer\");\n    }\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_number(mjs, storage_file_write(file, buf, len)));\n}\n\nstatic void js_storage_file_seek_relative(struct mjs* mjs) {\n    int32_t offset;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset);\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, false)));\n}\n\nstatic void js_storage_file_seek_absolute(struct mjs* mjs) {\n    int32_t offset;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset);\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, true)));\n}\n\nstatic void js_storage_file_tell(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_number(mjs, storage_file_tell(file)));\n}\n\nstatic void js_storage_file_truncate(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_truncate(file)));\n}\n\nstatic void js_storage_file_size(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_number(mjs, storage_file_size(file)));\n}\n\nstatic void js_storage_file_eof(struct mjs* mjs) {\n    File* file = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_eof(file)));\n}\n\nstatic void js_storage_file_copy_to(struct mjs* mjs) {\n    static const JsValueDeclaration js_storage_file_write_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeAny),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_storage_file_write_args =\n        JS_VALUE_ARGS(js_storage_file_write_arg_list);\n\n    mjs_val_t dest_obj;\n    int32_t bytes;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &dest_obj, &bytes);\n\n    File* source = JS_GET_CONTEXT(mjs);\n    File* destination = JS_GET_INST(mjs, dest_obj);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_copy_to_file(source, destination, bytes)));\n}\n\n// =========================\n// Top-level file operations\n// =========================\n\n// common destructor for file and dir objects\nstatic void js_storage_file_destructor(struct mjs* mjs, mjs_val_t obj) {\n    File* file = JS_GET_INST(mjs, obj);\n    storage_file_free(file);\n}\n\nstatic void js_storage_open_file(struct mjs* mjs) {\n    static const JsValueEnumVariant js_storage_fsam_variants[] = {\n        {\"r\", FSAM_READ},\n        {\"w\", FSAM_WRITE},\n        {\"rw\", FSAM_READ_WRITE},\n    };\n\n    static const JsValueEnumVariant js_storage_fsom_variants[] = {\n        {\"open_existing\", FSOM_OPEN_EXISTING},\n        {\"open_always\", FSOM_OPEN_ALWAYS},\n        {\"open_append\", FSOM_OPEN_APPEND},\n        {\"create_new\", FSOM_CREATE_NEW},\n        {\"create_always\", FSOM_CREATE_ALWAYS},\n    };\n\n    static const JsValueDeclaration js_storage_open_file_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_ENUM(FS_AccessMode, js_storage_fsam_variants),\n        JS_VALUE_ENUM(FS_OpenMode, js_storage_fsom_variants),\n    };\n    static const JsValueArguments js_storage_open_file_args =\n        JS_VALUE_ARGS(js_storage_open_file_arg_list);\n\n    const char* path;\n    FS_AccessMode access_mode;\n    FS_OpenMode open_mode;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(\n        mjs, &js_storage_open_file_args, &path, &access_mode, &open_mode);\n\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    File* file = storage_file_alloc(storage);\n    if(!storage_file_open(file, path, access_mode, open_mode)) {\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    mjs_val_t file_obj = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, file_obj) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, file));\n        JS_FIELD(MJS_DESTRUCTOR_PROP_NAME, MJS_MK_FN(js_storage_file_destructor));\n        JS_FIELD(\"close\", MJS_MK_FN(js_storage_file_close));\n        JS_FIELD(\"isOpen\", MJS_MK_FN(js_storage_file_is_open));\n        JS_FIELD(\"read\", MJS_MK_FN(js_storage_file_read));\n        JS_FIELD(\"write\", MJS_MK_FN(js_storage_file_write));\n        JS_FIELD(\"seekRelative\", MJS_MK_FN(js_storage_file_seek_relative));\n        JS_FIELD(\"seekAbsolute\", MJS_MK_FN(js_storage_file_seek_absolute));\n        JS_FIELD(\"tell\", MJS_MK_FN(js_storage_file_tell));\n        JS_FIELD(\"truncate\", MJS_MK_FN(js_storage_file_truncate));\n        JS_FIELD(\"size\", MJS_MK_FN(js_storage_file_size));\n        JS_FIELD(\"eof\", MJS_MK_FN(js_storage_file_eof));\n        JS_FIELD(\"copyTo\", MJS_MK_FN(js_storage_file_copy_to));\n    }\n    mjs_return(mjs, file_obj);\n}\n\nstatic void js_storage_file_exists(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_exists(storage, path)));\n}\n\n// ====================\n// Directory operations\n// ====================\n\nstatic void js_storage_read_directory(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    File* dir = storage_file_alloc(storage);\n    if(!storage_dir_open(dir, path)) {\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    FileInfo file_info;\n    char name[128];\n    FuriString* file_path = furi_string_alloc_set_str(path);\n    size_t path_size = furi_string_size(file_path);\n    uint32_t timestamp;\n\n    mjs_val_t ret = mjs_mk_array(mjs);\n    while(storage_dir_read(dir, &file_info, name, sizeof(name))) {\n        furi_string_left(file_path, path_size);\n        path_append(file_path, name);\n        furi_check(\n            storage_common_timestamp(storage, furi_string_get_cstr(file_path), &timestamp) ==\n            FSE_OK);\n        mjs_val_t obj = mjs_mk_object(mjs);\n        JS_ASSIGN_MULTI(mjs, obj) {\n            JS_FIELD(\"path\", mjs_mk_string(mjs, name, ~0, true));\n            JS_FIELD(\"isDirectory\", mjs_mk_boolean(mjs, file_info_is_dir(&file_info)));\n            JS_FIELD(\"size\", mjs_mk_number(mjs, file_info.size));\n            JS_FIELD(\"timestamp\", mjs_mk_number(mjs, timestamp));\n        }\n        mjs_array_push(mjs, ret, obj);\n    }\n\n    storage_file_free(dir);\n    furi_string_free(file_path);\n    mjs_return(mjs, ret);\n}\n\nstatic void js_storage_directory_exists(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_dir_exists(storage, path)));\n}\n\nstatic void js_storage_make_directory(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_mkdir(storage, path)));\n}\n\n// =================\n// Common operations\n// =================\n\nstatic void js_storage_file_or_dir_exists(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_exists(storage, path)));\n}\n\nstatic void js_storage_stat(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    FileInfo file_info;\n    uint32_t timestamp;\n    if((storage_common_stat(storage, path, &file_info) |\n        storage_common_timestamp(storage, path, &timestamp)) != FSE_OK) {\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    mjs_val_t ret = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, ret) {\n        JS_FIELD(\"path\", mjs_mk_string(mjs, path, ~0, 1));\n        JS_FIELD(\"isDirectory\", mjs_mk_boolean(mjs, file_info_is_dir(&file_info)));\n        JS_FIELD(\"size\", mjs_mk_number(mjs, file_info.size));\n        JS_FIELD(\"accessTime\", mjs_mk_number(mjs, timestamp));\n    }\n    mjs_return(mjs, ret);\n}\n\nstatic void js_storage_remove(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove(storage, path)));\n}\n\nstatic void js_storage_rmrf(struct mjs* mjs) {\n    const char* path;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove_recursive(storage, path)));\n}\n\nstatic void js_storage_rename(struct mjs* mjs) {\n    const char *old, *new;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &old, &new);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    FS_Error status = storage_common_rename(storage, old, new);\n    mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK));\n}\n\nstatic void js_storage_copy(struct mjs* mjs) {\n    const char *source, *dest;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &source, &dest);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    FS_Error status = storage_common_copy(storage, source, dest);\n    mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK || status == FSE_EXIST));\n}\n\nstatic void js_storage_fs_info(struct mjs* mjs) {\n    const char* fs;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &fs);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    uint64_t total_space, free_space;\n    if(storage_common_fs_info(storage, fs, &total_space, &free_space) != FSE_OK) {\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n    mjs_val_t ret = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, ret) {\n        JS_FIELD(\"totalSpace\", mjs_mk_number(mjs, total_space));\n        JS_FIELD(\"freeSpace\", mjs_mk_number(mjs, free_space));\n    }\n    mjs_return(mjs, ret);\n}\n\nstatic void js_storage_next_available_filename(struct mjs* mjs) {\n    static const JsValueDeclaration js_storage_naf_arg_list[] = {\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE(JsValueTypeString),\n        JS_VALUE_SIMPLE(JsValueTypeInt32),\n    };\n    static const JsValueArguments js_storage_naf_args = JS_VALUE_ARGS(js_storage_naf_arg_list);\n\n    const char *dir_path, *file_name, *file_ext;\n    int32_t max_len;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(\n        mjs, &js_storage_naf_args, &dir_path, &file_name, &file_ext, &max_len);\n\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    FuriString* next_name = furi_string_alloc();\n    storage_get_next_filename(storage, dir_path, file_name, file_ext, next_name, max_len);\n    mjs_return(mjs, mjs_mk_string(mjs, furi_string_get_cstr(next_name), ~0, true));\n    furi_string_free(next_name);\n}\n\n// ===============\n// Path operations\n// ===============\n\nstatic void js_storage_are_paths_equal(struct mjs* mjs) {\n    const char *path1, *path2;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &path1, &path2);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_equivalent_path(storage, path1, path2)));\n}\n\nstatic void js_storage_is_subpath_of(struct mjs* mjs) {\n    const char *parent, *child;\n    JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &parent, &child);\n    Storage* storage = JS_GET_CONTEXT(mjs);\n    mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_is_subdir(storage, parent, child)));\n}\n\n// ==================\n// Module ctor & dtor\n// ==================\n\nstatic void* js_storage_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    UNUSED(storage);\n    *object = mjs_mk_object(mjs);\n    JS_ASSIGN_MULTI(mjs, *object) {\n        JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, storage));\n\n        // top-level file ops\n        JS_FIELD(\"openFile\", MJS_MK_FN(js_storage_open_file));\n        JS_FIELD(\"fileExists\", MJS_MK_FN(js_storage_file_exists));\n\n        // dir ops\n        JS_FIELD(\"readDirectory\", MJS_MK_FN(js_storage_read_directory));\n        JS_FIELD(\"directoryExists\", MJS_MK_FN(js_storage_directory_exists));\n        JS_FIELD(\"makeDirectory\", MJS_MK_FN(js_storage_make_directory));\n\n        // common ops\n        JS_FIELD(\"fileOrDirExists\", MJS_MK_FN(js_storage_file_or_dir_exists));\n        JS_FIELD(\"stat\", MJS_MK_FN(js_storage_stat));\n        JS_FIELD(\"remove\", MJS_MK_FN(js_storage_remove));\n        JS_FIELD(\"rmrf\", MJS_MK_FN(js_storage_rmrf));\n        JS_FIELD(\"rename\", MJS_MK_FN(js_storage_rename));\n        JS_FIELD(\"copy\", MJS_MK_FN(js_storage_copy));\n        JS_FIELD(\"fsInfo\", MJS_MK_FN(js_storage_fs_info));\n        JS_FIELD(\"nextAvailableFilename\", MJS_MK_FN(js_storage_next_available_filename));\n\n        // path ops\n        JS_FIELD(\"arePathsEqual\", MJS_MK_FN(js_storage_are_paths_equal));\n        JS_FIELD(\"isSubpathOf\", MJS_MK_FN(js_storage_is_subpath_of));\n    }\n    return NULL;\n}\n\nstatic void js_storage_destroy(void* data) {\n    UNUSED(data);\n    furi_record_close(RECORD_STORAGE);\n}\n\n// ===========\n// Boilerplate\n// ===========\n\nstatic const JsModuleDescriptor js_storage_desc = {\n    \"storage\",\n    js_storage_create,\n    js_storage_destroy,\n    NULL,\n};\n\nstatic const FlipperAppPluginDescriptor plugin_descriptor = {\n    .appid = PLUGIN_APP_ID,\n    .ep_api_version = PLUGIN_API_VERSION,\n    .entry_point = &js_storage_desc,\n};\n\nconst FlipperAppPluginDescriptor* js_storage_ep(void) {\n    return &plugin_descriptor;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_tests.c",
    "content": "#include \"../js_modules.h\" // IWYU pragma: keep\n#include <core/common_defines.h>\n#include <furi_hal_version.h>\n#include <power/power_service/power.h>\n\n#define TAG \"JsTests\"\n\nstatic void js_tests_fail(struct mjs* mjs) {\n    furi_check(mjs_nargs(mjs) == 1);\n    mjs_val_t message_arg = mjs_arg(mjs, 0);\n    const char* message = mjs_get_string(mjs, &message_arg, NULL);\n    furi_check(message);\n    mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"%s\", message);\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_tests_assert_eq(struct mjs* mjs) {\n    furi_check(mjs_nargs(mjs) == 2);\n\n    mjs_val_t expected_arg = mjs_arg(mjs, 0);\n    mjs_val_t result_arg = mjs_arg(mjs, 1);\n\n    if(mjs_is_number(expected_arg) && mjs_is_number(result_arg)) {\n        int32_t expected = mjs_get_int32(mjs, expected_arg);\n        int32_t result = mjs_get_int32(mjs, result_arg);\n        if(expected == result) {\n            FURI_LOG_T(TAG, \"eq passed (exp=%ld res=%ld)\", expected, result);\n        } else {\n            mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"expected %d, found %d\", expected, result);\n        }\n    } else if(mjs_is_string(expected_arg) && mjs_is_string(result_arg)) {\n        const char* expected = mjs_get_string(mjs, &expected_arg, NULL);\n        const char* result = mjs_get_string(mjs, &result_arg, NULL);\n        if(strcmp(expected, result) == 0) {\n            FURI_LOG_T(TAG, \"eq passed (exp=\\\"%s\\\" res=\\\"%s\\\")\", expected, result);\n        } else {\n            mjs_prepend_errorf(\n                mjs, MJS_INTERNAL_ERROR, \"expected \\\"%s\\\", found \\\"%s\\\"\", expected, result);\n        }\n    } else if(mjs_is_boolean(expected_arg) && mjs_is_boolean(result_arg)) {\n        bool expected = mjs_get_bool(mjs, expected_arg);\n        bool result = mjs_get_bool(mjs, result_arg);\n        if(expected == result) {\n            FURI_LOG_T(\n                TAG,\n                \"eq passed (exp=%s res=%s)\",\n                expected ? \"true\" : \"false\",\n                result ? \"true\" : \"false\");\n        } else {\n            mjs_prepend_errorf(\n                mjs,\n                MJS_INTERNAL_ERROR,\n                \"expected %s, found %s\",\n                expected ? \"true\" : \"false\",\n                result ? \"true\" : \"false\");\n        }\n    } else {\n        JS_ERROR_AND_RETURN(\n            mjs,\n            MJS_INTERNAL_ERROR,\n            \"type mismatch (expected %s, result %s)\",\n            mjs_typeof(expected_arg),\n            mjs_typeof(result_arg));\n    }\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nstatic void js_tests_assert_float_close(struct mjs* mjs) {\n    furi_check(mjs_nargs(mjs) == 3);\n\n    mjs_val_t expected_arg = mjs_arg(mjs, 0);\n    mjs_val_t result_arg = mjs_arg(mjs, 1);\n    mjs_val_t epsilon_arg = mjs_arg(mjs, 2);\n    furi_check(mjs_is_number(expected_arg));\n    furi_check(mjs_is_number(result_arg));\n    furi_check(mjs_is_number(epsilon_arg));\n    double expected = mjs_get_double(mjs, expected_arg);\n    double result = mjs_get_double(mjs, result_arg);\n    double epsilon = mjs_get_double(mjs, epsilon_arg);\n\n    if(ABS(expected - result) > epsilon) {\n        mjs_prepend_errorf(\n            mjs,\n            MJS_INTERNAL_ERROR,\n            \"expected %f found %f (tolerance=%f)\",\n            expected,\n            result,\n            epsilon);\n    }\n\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nvoid* js_tests_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {\n    UNUSED(modules);\n    mjs_val_t tests_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, tests_obj, \"fail\", ~0, MJS_MK_FN(js_tests_fail));\n    mjs_set(mjs, tests_obj, \"assert_eq\", ~0, MJS_MK_FN(js_tests_assert_eq));\n    mjs_set(mjs, tests_obj, \"assert_float_close\", ~0, MJS_MK_FN(js_tests_assert_float_close));\n    *object = tests_obj;\n\n    return (void*)1;\n}\n"
  },
  {
    "path": "applications/system/js_app/modules/js_tests.h",
    "content": "#pragma once\n#include \"../js_thread_i.h\"\n#include \"../js_modules.h\"\n\nvoid* js_tests_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules);\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/README.md",
    "content": "# Flipper Zero JavaScript SDK Wizard\nThis package contains an interactive wizard that lets you scaffold a JavaScript\napplication for Flipper Zero.\n\n## Getting started\nCreate your application using the interactive wizard:\n```shell\nnpx @flipperdevices/create-fz-app@latest\n```\n\nThen, enter the directory with your application and launch it:\n```shell\ncd my-flip-app\nnpm start\n```\n\nYou are free to use `pnpm` or `yarn` instead of `npm`.\n\n## Documentation\nCheck out the [JavaScript section in the Developer Documentation](https://developer.flipper.net/flipperzero/doxygen/js.html)\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/index.js",
    "content": "#!/usr/bin/env node\nimport prompts from \"prompts\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"url\";\nimport { spawnSync } from \"node:child_process\";\nimport { replaceInFileSync } from \"replace-in-file\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n(async () => {\n    const { name, pkgManager, confirm } = await prompts([\n        {\n            type: \"text\",\n            name: \"name\",\n            message: \"What is the name of your project?\",\n            initial: \"my-flip-app\"\n        },\n        {\n            type: \"select\",\n            name: \"pkgManager\",\n            message: \"What package manager should your project use?\",\n            choices: [\n                { title: \"npm\", value: \"npm\" },\n                { title: \"pnpm\", value: \"pnpm\" },\n                { title: \"yarn\", value: \"yarn\" },\n            ],\n        },\n        {\n            type: \"confirm\",\n            name: \"confirm\",\n            message: \"Create project?\",\n            initial: true,\n        },\n    ]);\n\n    if (!confirm)\n        return;\n\n    if (fs.existsSync(name)) {\n        const { replace } = await prompts([\n            {\n                type: \"confirm\",\n                name: \"replace\",\n                message: `File or directory \\`${name}\\` already exists. Continue anyway?`,\n                initial: false,\n            },\n        ]);\n        if (!replace)\n            return;\n    }\n\n    fs.rmSync(name, { recursive: true, force: true });\n\n    console.log(\"Copying files...\");\n    fs.cpSync(path.resolve(__dirname, \"template\"), name, { recursive: true });\n    replaceInFileSync({ files: `${name}/**/*`, from: /<app_name>/g, to: name });\n\n    console.log(\"Installing packages...\");\n    spawnSync(\"bash\", [\"-c\", `cd ${name} && ${pkgManager} install`], {\n        cwd: process.cwd(),\n        detached: true,\n        stdio: \"inherit\",\n    });\n\n    console.log(`Done! Created ${name}. Run \\`cd ${name} && ${pkgManager} start\\` to run it on your Flipper.`);\n})();\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/package.json",
    "content": "{\n  \"name\": \"@flipperdevices/create-fz-app\",\n  \"version\": \"0.1.2\",\n  \"description\": \"Template package for JS apps Flipper Zero\",\n  \"bin\": \"index.js\",\n  \"type\": \"module\",\n  \"keywords\": [\n    \"flipper\",\n    \"flipper zero\"\n  ],\n  \"author\": \"Flipper Devices\",\n  \"license\": \"GPL-3.0-only\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/flipperdevices/flipperzero-firmware.git\",\n    \"directory\": \"applications/system/js_app/packages/create-fz-app\"\n  },\n  \"dependencies\": {\n    \"prompts\": \"^2.4.2\",\n    \"replace-in-file\": \"^8.2.0\"\n  }\n}"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/template/.gitignore",
    "content": "/dist\nnode_modules/\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/template/fz-sdk.config.json5",
    "content": "{\n    build: {\n        // Where to put the compiled file\n        output: \"dist/<app_name>.js\",\n\n        // Whether to reduce the final file size at the cost of readability and\n        // clarity of error messages\n        minify: false,\n\n        // Set this to `false` if you've thoroughly read the documentation and\n        // are sure that you can use manual version checks to your advantage\n        enforceSdkVersion: true,\n    },\n\n    upload: {\n        // Where to grab the file from. If you're not doing any extra processing\n        // after the SDK, this should match `build.output`\n        input: \"dist/<app_name>.js\",\n\n        // Where to put the file on the device\n        output: \"/ext/apps/Scripts/<app_name>.js\",\n    },\n}\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/template/index.ts",
    "content": "// import modules\n// caution: `eventLoop` HAS to be imported before `gui`, and `gui` HAS to be\n// imported before any `gui` submodules.\nimport * as eventLoop from \"@flipperdevices/fz-sdk/event_loop\";\nimport * as gui from \"@flipperdevices/fz-sdk/gui\";\nimport * as dialog from \"@flipperdevices/fz-sdk/gui/dialog\";\n\n// a common pattern is to declare all the views that your app uses on one object\nconst views = {\n    dialog: dialog.makeWith({\n        header: \"Hello from <app_name>\",\n        text: \"Check out index.ts and\\nchange something :)\",\n        center: \"Gonna do that!\",\n    }),\n};\n\n// stop app on center button press\neventLoop.subscribe(views.dialog.input, (_sub, button, eventLoop) => {\n    if (button === \"center\")\n        eventLoop.stop();\n}, eventLoop);\n\n// stop app on back button press\neventLoop.subscribe(gui.viewDispatcher.navigation, (_sub, _item, eventLoop) => {\n    eventLoop.stop();\n}, eventLoop);\n\n// run app\ngui.viewDispatcher.switchTo(views.dialog);\neventLoop.run();\n"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/template/package.json",
    "content": "{\n  \"name\": \"<app_name>\",\n  \"version\": \"1.0.0\",\n  \"scripts\": {\n    \"build\": \"tsc && node node_modules/@flipperdevices/fz-sdk/sdk.js build\",\n    \"start\": \"npm run build && node node_modules/@flipperdevices/fz-sdk/sdk.js upload\"\n  },\n  \"devDependencies\": {\n    \"@flipperdevices/fz-sdk\": \"^1.0\",\n    \"typescript\": \"^5.6.3\"\n  }\n}"
  },
  {
    "path": "applications/system/js_app/packages/create-fz-app/template/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"outDir\": \"dist\",\n        \"checkJs\": true,\n        \"module\": \"CommonJS\",\n        \"noLib\": true,\n        \"target\": \"ES2015\",\n        \"types\": [],\n    },\n    \"files\": [\n        \"./node_modules/@flipperdevices/fz-sdk/global.d.ts\",\n    ],\n    \"include\": [\n        \"./**/*.ts\",\n        \"./**/*.js\",\n    ],\n    \"exclude\": [\n        \"./node_modules/**/*\",\n        \"dist/**/*\",\n    ],\n}"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/.gitignore",
    "content": "docs/\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/README.md",
    "content": "# Flipper Zero JavaScript SDK\nThis package contains official tooling and typings for developing Flipper Zero\napplications in JavaScript.\n\n## Getting started\nCreate your application using the interactive wizard:\n```shell\nnpx @flipperdevices/create-fz-app@latest\n```\n\nThen, enter the directory with your application and launch it:\n```shell\ncd my-flip-app\nnpm start\n```\n\nYou are free to use `pnpm` or `yarn` instead of `npm`.\n\n## Versioning\nFor each version of this package, the major and minor components match those of\nthe Flipper Zero JS SDK version that that package version targets. This version\nfollows semver. For example, apps compiled with SDK version `0.1.0` will be\ncompatible with SDK versions `0.1`...`1.0` (not including `1.0`).\n\nEvery API has a version history reflected in its JSDoc comment. It is heavily\nrecommended to check SDK compatibility using a combination of\n`sdkCompatibilityStatus`, `isSdkCompatible`, `assertSdkCompatibility` depending\non your use case.\n\n## Documentation\nCheck out the [JavaScript section in the Developer Documentation](https://developer.flipper.net/flipperzero/doxygen/js.html)\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/badusb/index.d.ts",
    "content": "/**\n * @brief Special key codes that this module recognizes\n * @version Added in JS SDK 0.1\n */\nexport type ModifierKey = \"CTRL\" | \"SHIFT\" | \"ALT\" | \"GUI\";\n\n/** @version Added in JS SDK 0.1 */\nexport type MainKey =\n    \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"UP\" |\n\n    \"ENTER\" | \"PAUSE\" | \"CAPSLOCK\" | \"DELETE\" | \"BACKSPACE\" | \"END\" | \"ESC\" |\n    \"HOME\" | \"INSERT\" | \"NUMLOCK\" | \"PAGEUP\" | \"PAGEDOWN\" | \"PRINTSCREEN\" |\n    \"SCROLLLOCK\" | \"SPACE\" | \"TAB\" | \"MENU\" |\n\n    \"F1\" | \"F2\" | \"F3\" | \"F4\" | \"F5\" | \"F6\" | \"F7\" | \"F8\" | \"F9\" | \"F10\" |\n    \"F11\" | \"F12\" | \"F13\" | \"F14\" | \"F15\" | \"F16\" | \"F17\" | \"F18\" | \"F19\" |\n    \"F20\" | \"F21\" | \"F22\" | \"F23\" | \"F24\" |\n\n    \"NUM0\" | \"NUM1\" | \"NUM2\" | \"NUM3\" | \"NUM4\" | \"NUM5\" | \"NUM6\" | \"NUM7\" |\n    \"NUM8\" | \"NUM9\" |\n\n    \"\\n\" | \" \" | \"!\" | \"\\\"\" | \"#\" | \"$\" | \"%\" | \"&\" | \"'\" | \"(\" | \")\" | \"*\" |\n    \"+\" | \",\" | \"-\" | \".\" | \"/\" | \":\" | \";\" | \"<\" | \">\" | \"=\" | \"?\" | \"@\" | \"[\" |\n    \"]\" | \"\\\\\" | \"^\" | \"_\" | \"`\" | \"{\" | \"}\" | \"|\" | \"~\" |\n\n    \"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" |\n\n    \"A\" | \"B\" | \"C\" | \"D\" | \"E\" | \"F\" | \"G\" | \"H\" | \"I\" | \"J\" | \"K\" | \"L\" |\n    \"M\" | \"N\" | \"O\" | \"P\" | \"Q\" | \"R\" | \"S\" | \"T\" | \"U\" | \"V\" | \"W\" | \"X\" |\n    \"Y\" | \"Z\" |\n\n    \"a\" | \"b\" | \"c\" | \"d\" | \"e\" | \"f\" | \"g\" | \"h\" | \"i\" | \"j\" | \"k\" | \"l\" |\n    \"m\" | \"n\" | \"o\" | \"p\" | \"q\" | \"r\" | \"s\" | \"t\" | \"u\" | \"v\" | \"w\" | \"x\" |\n    \"y\" | \"z\";\n\n/** @version Added in JS SDK 0.1 */\nexport type KeyCode = MainKey | ModifierKey | number;\n\n/**\n * @brief Initializes the module\n * @param settings USB device settings. Omit to select default parameters\n * @version Added in JS SDK 0.1\n */\nexport declare function setup(settings?: { vid: number, pid: number, mfrName?: string, prodName?: string, layoutPath?: string }): void;\n\n/**\n * @brief Tells whether the virtual USB HID device has successfully connected\n * @version Added in JS SDK 0.1\n */\nexport declare function isConnected(): boolean;\n\n/**\n * @brief Presses one or multiple keys at once, then releases them\n * @param keys The arguments represent a set of keys to. Out of that set, only\n *             one of the keys may represent a \"main key\" (see `MainKey`), with\n *             the rest being modifier keys (see `ModifierKey`).\n * @version Added in JS SDK 0.1\n */\nexport declare function press(...keys: KeyCode[]): void;\n\n/**\n * @brief Presses one or multiple keys at once without releasing them\n * @param keys The arguments represent a set of keys to. Out of that set, only\n *             one of the keys may represent a \"main key\" (see `MainKey`), with\n *             the rest being modifier keys (see `ModifierKey`).\n * @version Added in JS SDK 0.1\n */\nexport declare function hold(...keys: KeyCode[]): void;\n\n/**\n * @brief Releases one or multiple keys at once\n * @param keys The arguments represent a set of keys to. Out of that set, only\n *             one of the keys may represent a \"main key\" (see `MainKey`), with\n *             the rest being modifier keys (see `ModifierKey`).\n * @version Added in JS SDK 0.1\n */\nexport declare function release(...keys: KeyCode[]): void;\n\n/**\n * @brief Prints a string by repeatedly pressing and releasing keys\n * @param string The string to print\n * @param delay How many milliseconds to wait between key presses\n * @version Added in JS SDK 0.1\n */\nexport declare function print(string: string, delay?: number): void;\n\n/**\n * @brief Prints a string by repeatedly pressing and releasing keys. Presses\n *        \"Enter\" after printing the string\n * @param string The string to print\n * @param delay How many milliseconds to wait between key presses\n * @version Added in JS SDK 0.1\n */\nexport declare function println(string: string, delay?: number): void;\n\n/**\n * @brief Prints a string by Alt+Numpad method - works only on Windows!\n * @param string The string to print\n * @param delay How many milliseconds to wait between key presses\n * @version Added in JS SDK 0.1\n */\nexport declare function altPrint(string: string, delay?: number): void;\n\n/**\n * @brief Prints a string by Alt+Numpad method - works only on Windows!\n *        Presses \"Enter\" after printing the string\n * @param string The string to print\n * @param delay How many milliseconds to wait between key presses\n * @version Added in JS SDK 0.1\n */\nexport declare function altPrintln(string: string, delay?: number): void;\n\n/**\n * @brief Releases usb, optional, but allows to switch usb profile\n * @version Added in JS SDK 0.1\n */\nexport declare function quit(): void;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/docs_readme.md",
    "content": "# Welcome\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/event_loop/index.d.ts",
    "content": "/**\n * Module for dealing with events\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * ```\n * \n * The event loop is central to event-based programming in many frameworks, and\n * our JS subsystem is no exception. It is a good idea to familiarize yourself\n * with the event loop first before using any of the advanced modules (e.g. GPIO\n * and GUI).\n * \n * # Conceptualizing the event loop\n * If you ever wrote JavaScript before, you have definitely seen callbacks. It's\n * when a function accepts another function (usually an anonymous one) as one of\n * the arguments, which it will call later on, e.g. when an event happens or\n * when data becomes ready:\n * ```js\n * setTimeout(function() { console.log(\"Hello, World!\") }, 1000);\n * ```\n * \n * Many JavaScript engines employ a queue that the runtime fetches events from\n * as they occur, subsequently calling the corresponding callbacks. This is done\n * in a long-running loop, hence the name \"event loop\". Here's the pseudocode\n * for a typical event loop:\n * ```js\n * while(loop_is_running()) {\n *     if(event_available_in_queue()) {\n *         let event = fetch_event_from_queue();\n *         let callback = get_callback_associated_with(event);\n *         if(callback)\n *             callback(get_extra_data_for(event));\n *     } else {\n *         // avoid wasting CPU time\n *         sleep_until_any_event_becomes_available();\n *     }\n * }\n * ```\n * \n * Most JS runtimes enclose the event loop within themselves, so that most JS\n * programmers does not even need to be aware of its existence. This is not the\n * case with our JS subsystem.\n * \n * # Example\n * This is how one would write something similar to the `setTimeout` example\n * above:\n * ```js\n * // import module\n * let eventLoop = require(\"event_loop\");\n * \n * // create an event source that will fire once 1 second after it has been created\n * let timer = eventLoop.timer(\"oneshot\", 1000);\n * \n * // subscribe a callback to the event source\n * eventLoop.subscribe(timer, function(_subscription, _item, eventLoop) {\n *     print(\"Hello, World!\");\n *     eventLoop.stop();\n * }, eventLoop); // notice this extra argument. we'll come back to this later\n * \n * // run the loop until it is stopped\n * eventLoop.run();\n * \n * // the previous line will only finish executing once `.stop()` is called, hence\n * // the following line will execute only after \"Hello, World!\" is printed\n * print(\"Stopped\");\n * ```\n * \n * I promised you that we'll come back to the extra argument after the callback\n * function. Our JavaScript engine does not support closures (anonymous\n * functions that access values outside of their arguments), so we ask\n * `subscribe` to pass an outside value (namely, `eventLoop`) as an argument to\n * the callback so that we can access it. We can modify this extra state:\n * ```js\n * // this timer will fire every second\n * let timer = eventLoop.timer(\"periodic\", 1000);\n * eventLoop.subscribe(timer, function(_subscription, _item, counter, eventLoop) {\n *     print(\"Counter is at:\", counter);\n *     if(counter === 10)\n *         eventLoop.stop();\n *     // modify the extra arguments that will be passed to us the next time\n *     return [counter + 1, eventLoop];\n * }, 0, eventLoop);\n * ```\n * \n * Because we have two extra arguments, if we return anything other than an\n * array of length 2, the arguments will be kept as-is for the next call.\n * \n * The first two arguments that get passed to our callback are:\n *   - The subscription manager that lets us `.cancel()` our subscription\n *   - The event item, used for events that have extra data. Timer events do\n *     not, they just produce `undefined`.\n *\n * @version Added in JS SDK 0.1\n * @module\n */\n\n/**\n * @ignore\n */\ntype Lit = undefined | null | {};\n\n/**\n * Subscription control interface\n * @version Added in JS SDK 0.1\n */\nexport interface Subscription {\n    /**\n     * Cancels the subscription, preventing any future events managed by the\n     * subscription from firing\n     * @version Added in JS SDK 0.1\n     */\n    cancel(): void;\n}\n\n/**\n * Opaque event source identifier\n * @version Added in JS SDK 0.1\n */\nexport type Contract<Item = undefined> = symbol & { \"__tag__\": \"contract\" };\n// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist.\n\n/**\n * A callback can be assigned to an event loop to listen to an event. It may\n * return an array with values that will be passed to it as arguments the next\n * time that it is called. The first argument is always the subscription\n * manager, and the second argument is always the item that trigged the event.\n * The type of the item is defined by the event source.\n * @version Added in JS SDK 0.1\n */\nexport type Callback<Item, Args extends Lit[]> = (subscription: Subscription, item: Item, ...args: Args) => Args | undefined | void;\n\n/**\n * Subscribes a callback to an event\n * @param contract Event identifier\n * @param callback Function to call when the event is triggered\n * @param args Initial arguments passed to the callback\n * @version Added in JS SDK 0.1\n */\nexport function subscribe<Item, Args extends Lit[]>(contract: Contract<Item>, callback: Callback<Item, Args>, ...args: Args): Subscription;\n/**\n * Runs the event loop until it is stopped (potentially never)\n * @version Added in JS SDK 0.1\n */\nexport function run(): void | never;\n/**\n * Stops the event loop\n * @version Added in JS SDK 0.1\n */\nexport function stop(): void;\n\n/**\n * Creates a timer event that can be subscribed to just like any other event\n * @param mode Either `\"oneshot\"` or `\"periodic\"`\n * @param interval Timer interval in milliseconds\n * @version Added in JS SDK 0.1\n */\nexport function timer(mode: \"oneshot\" | \"periodic\", interval: number): Contract;\n\n/**\n * Message queue\n * @version Added in JS SDK 0.1\n */\nexport declare class Queue<T> {\n    /**\n     * Message event\n     * @version Added in JS SDK 0.1\n     */\n    input: Contract<T>;\n    /**\n     * Sends a message to the queue\n     * @param message message to send\n     * @version Added in JS SDK 0.1\n     */\n    send(message: T): void;\n}\n\n/**\n * Creates a message queue\n * @param length maximum queue capacity\n * @version Added in JS SDK 0.1\n */\nexport function queue<T>(length: number): Queue<T>;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/flipper/index.d.ts",
    "content": "/**\n * Module for querying device properties\n * @version Added in JS SDK 0.1\n * @module\n */\n\n/**\n * @brief Returns the device model\n * @version Added in JS SDK 0.1\n */\nexport declare function getModel(): string;\n\n/**\n * @brief Returns the name of the virtual dolphin\n * @version Added in JS SDK 0.1\n */\nexport declare function getName(): string;\n\n/**\n * @brief Returns the battery charge percentage\n * @version Added in JS SDK 0.1\n */\nexport declare function getBatteryCharge(): number;\n\n/**\n * @warning Do **NOT** use this to check the presence or absence of features. If\n *          you do, I'm gonna be sad :( Instead, refer to `checkSdkFeatures` and\n *          other similar mechanisms.\n * @note Original firmware reports `\"flipperdevices\"`.\n * @version Added in JS SDK 0.1\n */\nexport declare const firmwareVendor: string;\n\n/**\n * @warning Do **NOT** use this to check the presence or absence of features. If\n *          you do, I'm gonna be sad :( Instead, refer to\n *          `checkSdkCompatibility` and other similar mechanisms.\n * @note You're looking at JS SDK 0.1\n * @version Added in JS SDK 0.1\n */\nexport declare const jsSdkVersion: [number, number];\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/global.d.ts",
    "content": "/**\n * Things from this module are automatically available to you without having to\n * explicitly import anything.\n * \n * # SDK versioning and features\n * \n * ## Motivation\n * It is very important that you check that features are implemented before you\n * use them. By adding the necessary checks, you ensure that your users get a\n * clear warning instead of a cryptic error message when running the script.\n * \n * This system has been designed in collaboration with our community in order to\n * make things better for everybody involved. You can find out more in this\n * discussion: https://github.com/flipperdevices/flipperzero-firmware/pull/3961\n * \n * ## Community agreement\n * Each interpreter implementation (aka \"JS SDK\", aka \"JS API\"), including\n * those found in third-party firmware distributions, defines two markers for\n * signaling what it supports: the **SDK version** and the\n * **extra feature set**.\n * \n * The **SDK version** consists of two semver-like integer components: the major\n * version and the minor version. Like semver, the major version is bumped when\n * a breaking change is introduced (i.e. one that would require correction of\n * apps by their developers), and the minor version is bumped when a new\n * non-breaking feature is introduced. Because we have adopted TypeScript,\n * the https://www.semver-ts.org/ standard is used to determine whether a change\n * is breaking or not. The basis of `semver-ts` is the \"no new red squiggles\"\n * rule.\n * \n * Every major version is associated with a set of **extra features** that are\n * present in some firmware distributions but not others. Distributions may\n * cross-port features between each other, until at some point they get ported\n * into the upstream firmware distribution. With the next major release of the\n * JS SDK, all extra features present in the upstream distribution are now\n * declared **baseline features**, and thus no longer recognized as \"extra\n * features\".\n * \n * Before using a feature, you must check that the interpreter that you're\n * running on actually supports it. If you don't, the portability of your\n * application will suffer.\n * \n * ## Implementation\n * Use the following functions to check version compatibility:\n *   - `checkSdkCompatibility` when your script absolutely cannot function on an\n *     incompatible interpreter\n *   - `isSdkCompatible` when your script can leverage multiple interpreter\n *     editions to its advantage\n *   - `sdkCompatibilityStatus` when you need a detailed status on compatibility\n * \n * Use the following functions to check feature compatibility:\n *   - `checkSdkFeatures` when your script absolutely cannot function on an\n *     incompatible interpreter\n *   - `doesSdkSupport` when your script can leverage multiple interpreter\n *     editions to its advantage\n * \n * ## Automatic version enforcement\n * The SDK will automatically insert a call to `checkSdkCompatibility` in the\n * beginning of the resulting script. If you would like to disable this check\n * and instead use other manual compatibility checking facilities, edit your\n * `fz-sdk.config.json5`.\n * \n * # Standard library\n * Standard library features are mostly unimplemented. This module defines,\n * among other things, the features that _are_ implemented.\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\n/**\n * @brief Checks compatibility between the script and the JS SDK that the\n *        firmware provides\n * \n * @note You're looking at JS SDK v1.0\n * \n * @param expectedMajor JS SDK major version expected by the script\n * @param expectedMinor JS SDK minor version expected by the script\n * @returns Compatibility status:\n *   - `\"compatible\"` if the script and the JS SDK are compatible\n *   - `\"firmwareTooOld\"` if the expected major version is larger than the\n *     version of the firmware, or if the expected minor version is larger than\n *     the version of the firmware\n *   - `\"firmwareTooNew\"` if the expected major version is lower than the\n *     version of the firmware\n * @version Added in JS SDK 0.1\n */\ndeclare function sdkCompatibilityStatus(expectedMajor: number, expectedMinor: number):\n    \"compatible\" | \"firmwareTooOld\" | \"firmwareTooNew\";\n\n/**\n * @brief Checks compatibility between the script and the JS SDK that the\n *        firmware provides in a boolean fashion\n * \n * @note You're looking at JS SDK v1.0\n * \n * @param expectedMajor JS SDK major version expected by the script\n * @param expectedMinor JS SDK minor version expected by the script\n * @returns `true` if the two are compatible, `false` otherwise\n * @version Added in JS SDK 0.1\n */\ndeclare function isSdkCompatible(expectedMajor: number, expectedMinor: number): boolean;\n\n/**\n * @brief Asks the user whether to continue executing the script if the versions\n *        are not compatible. Does nothing if they are.\n * \n * @note You're looking at JS SDK v1.0\n * \n * @param expectedMajor JS SDK major version expected by the script\n * @param expectedMinor JS SDK minor version expected by the script\n * @version Added in JS SDK 0.1\n */\ndeclare function checkSdkCompatibility(expectedMajor: number, expectedMinor: number): void | never;\n\n/**\n * @brief Checks whether all of the specified extra features are supported by\n *        the interpreter.\n * @warning This function will return `false` if a queried feature is now\n *          recognized as a baseline feature. For more info, consult the module\n *          documentation.\n * @param features Array of named features to query\n * @version Added in JS SDK 0.1\n */\ndeclare function doesSdkSupport(features: string[]): boolean;\n\n/**\n * @brief Checks whether all of the specified extra features are supported by\n *        the interpreter, asking the user if they want to continue running the\n *        script if they're not.\n * @warning This function will act as if the feature is not implemented for\n *          features that are now recognized as baseline features. For more\n *          info, consult the module documentation.\n * @param features Array of named features to query\n * @version Added in JS SDK 0.1\n */\ndeclare function checkSdkFeatures(features: string[]): void | never;\n\n/**\n * @brief Pauses JavaScript execution for a while\n * @param ms How many milliseconds to pause the execution for\n * @version Added in JS SDK 0.1\n */\ndeclare function delay(ms: number): void;\n\n/**\n * @brief Prints to the GUI console view\n * @param args The arguments are converted to strings, concatenated without any\n *             spaces in between and printed to the console view\n * @version Added in JS SDK 0.1\n */\ndeclare function print(...args: any[]): void;\n\n/**\n * @brief Converts a string to a number\n * @param text The string to convert to a number\n * @param base Integer base (`2`...`16`), default: 10\n * @version Added in JS SDK 0.1\n */\ndeclare function parseInt(text: string, base?: number): number;\n\n/**\n * @brief Path to the directory containing the current script\n * @version Added in JS SDK 0.1\n */\ndeclare const __dirname: string;\n\n/**\n * @brief Path to the current script file\n * @version Added in JS SDK 0.1\n */\ndeclare const __filename: string;\n\n/**\n * @brief Runs a JS file and returns value from it\n * \n * Reads a file at the specified path and runs it as JS, returning the last evaluated value.\n * \n * The result is cached and this filepath will not re-evaluated on future\n * load() calls for this session.\n * \n * @param path The path to the file\n * @param scope An object to use as global scope while running this file\n * @version Added in JS SDK 0.1\n */\ndeclare function load(path: string, scope?: object): any;\n\n/**\n * @brief Return 1-byte string whose ASCII code is the integer `n`\n * \n * If `n` is not numeric or outside of `0-255` range, `null` is returned\n * \n * @param n The ASCII code to convert to string\n * @version Added in JS SDK 0.1\n */\ndeclare function chr(n: number): string | null;\n\n/**\n * @brief Loads a natively implemented module\n * @param module The name of the module to load\n * @version Added in JS SDK 0.1\n */\ndeclare function require(module: string): any;\n\n/**\n * @brief Exit JavaScript with given message\n * @param message The error message to show to user\n * @version Added in JS SDK 0.1\n */\ndeclare function die(message: string): never;\n\n/**\n * @brief mJS Foreign Pointer type\n * \n * JavaScript code cannot do anything with values of `RawPointer` type except\n * acquire them from native code and pass them right back to other parts of\n * native code. These values cannot be turned into something meaningful, nor can\n * be they modified.\n * \n * @version Added in JS SDK 0.1\n */\ndeclare type RawPointer = symbol & { \"__tag__\": \"raw_ptr\" };\n// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist.\n\n/**\n * @brief Holds raw bytes\n * @version Added in JS SDK 0.1\n */\ndeclare class ArrayBuffer {\n    /**\n     * @brief The pointer to the byte buffer\n     * @note Like other `RawPointer` values, this value is essentially useless\n     *       to JS code.\n     * @version Added in JS SDK 0.1\n     */\n    getPtr: RawPointer;\n    /**\n     * @brief The length of the buffer in bytes\n     * @version Added in JS SDK 0.1\n     */\n    byteLength: number;\n    /**\n     * @brief Creates an `ArrayBuffer` that contains a sub-part of the buffer\n     * @param start The index of the byte in the source buffer to be used as the\n     *              start for the new buffer\n     * @param end The index of the byte in the source buffer that follows the\n     *            byte to be used as the last byte for the new buffer\n     * @version Added in JS SDK 0.1\n     */\n    slice(start: number, end?: number): ArrayBuffer;\n}\n\ndeclare function ArrayBuffer(): ArrayBuffer;\n\ndeclare type ElementType = \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\";\n\ndeclare class TypedArray<E extends ElementType> {\n    /**\n     * @brief The length of the buffer in bytes\n     * @version Added in JS SDK 0.1\n     */\n    byteLength: number;\n    /**\n     * @brief The length of the buffer in typed elements\n     * @version Added in JS SDK 0.1\n     */\n    length: number;\n    /**\n     * @brief The underlying `ArrayBuffer`\n     * @version Added in JS SDK 0.1\n     */\n    buffer: ArrayBuffer;\n}\n\ndeclare class Uint8Array extends TypedArray<\"u8\"> { }\ndeclare class Int8Array extends TypedArray<\"i8\"> { }\ndeclare class Uint16Array extends TypedArray<\"u16\"> { }\ndeclare class Int16Array extends TypedArray<\"i16\"> { }\ndeclare class Uint32Array extends TypedArray<\"u32\"> { }\ndeclare class Int32Array extends TypedArray<\"i32\"> { }\n\ndeclare function Uint8Array(data: ArrayBuffer | number | number[]): Uint8Array;\ndeclare function Int8Array(data: ArrayBuffer | number | number[]): Int8Array;\ndeclare function Uint16Array(data: ArrayBuffer | number | number[]): Uint16Array;\ndeclare function Int16Array(data: ArrayBuffer | number | number[]): Int16Array;\ndeclare function Uint32Array(data: ArrayBuffer | number | number[]): Uint32Array;\ndeclare function Int32Array(data: ArrayBuffer | number | number[]): Int32Array;\n\ndeclare const console: {\n    /**\n     * @brief Prints to the UART logs at the `[I]` level\n     * @param args The arguments are converted to strings, concatenated without any\n     *             spaces in between and printed to the logs\n     * @version Added in JS SDK 0.1\n     */\n    log(...args: any[]): void;\n    /**\n     * @brief Prints to the UART logs at the `[D]` level\n     * @param args The arguments are converted to strings, concatenated without any\n     *             spaces in between and printed to the logs\n     * @version Added in JS SDK 0.1\n     */\n    debug(...args: any[]): void;\n    /**\n     * @brief Prints to the UART logs at the `[W]` level\n     * @param args The arguments are converted to strings, concatenated without any\n     *             spaces in between and printed to the logs\n     * @version Added in JS SDK 0.1\n     */\n    warn(...args: any[]): void;\n    /**\n     * @brief Prints to the UART logs at the `[E]` level\n     * @param args The arguments are converted to strings, concatenated without any\n     *             spaces in between and printed to the logs\n     * @version Added in JS SDK 0.1\n     */\n    error(...args: any[]): void;\n};\n\ndeclare class Array<T> {\n    /**\n     * @brief Takes items out of the array\n     * \n     * Removes elements from the array and returns them in a new array\n     * \n     * @param start The index to start taking elements from\n     * @param deleteCount How many elements to take\n     * @returns The elements that were taken out of the original array as a new\n     *          array\n     * @version Added in JS SDK 0.1\n     */\n    splice(start: number, deleteCount: number): T[];\n    /**\n     * @brief Adds a value to the end of the array\n     * @param value The value to add\n     * @returns New length of the array\n     * @version Added in JS SDK 0.1\n     */\n    push(value: T): number;\n    /**\n     * @brief How many elements there are in the array\n     * @version Added in JS SDK 0.1\n     */\n    length: number;\n}\n\ndeclare class String {\n    /**\n     * @brief How many characters there are in the string\n     * @version Added in JS SDK 0.1\n     */\n    length: number;\n    /**\n     * @brief Returns the character code at an index in the string\n     * @param index The index to consult\n     * @version Added in JS SDK 0.1\n     */\n    charCodeAt(index: number): number;\n    /**\n     * See `charCodeAt`\n     * @version Added in JS SDK 0.1\n     */\n    at(index: number): number;\n    /**\n     * @brief Return index of first occurrence of substr within the string or `-1` if not found\n     * @param substr The string to search for\n     * @param fromIndex The index to start searching from\n     * @version Added in JS SDK 0.1\n     */\n    indexOf(substr: string, fromIndex?: number): number;\n    /**\n     * @brief Return a substring between two indices\n     * @param start The index to start substring at\n     * @param end The index to end substring at\n     * @version Added in JS SDK 0.1\n     */\n    slice(start: number, end?: number): string;\n    /**\n     * @brief Return this string transformed to upper case\n     * @version Added in JS SDK 0.1\n     */\n    toUpperCase(): string;\n    /**\n     * @brief Return this string transformed to lower case\n     * @version Added in JS SDK 0.1\n     */\n    toLowerCase(): string;\n}\n\ndeclare class Boolean { }\n\ndeclare class Function { }\n\ndeclare class Number {\n    /**\n     * @brief Converts this number to a string\n     * @param base Integer base (`2`...`16`), default: 10\n     * @version Added in JS SDK 0.1\n     */\n    toString(base?: number): string;\n}\n\ndeclare class Object { }\n\ndeclare class RegExp { }\n\ndeclare interface IArguments { }\n\ndeclare type Partial<O extends object> = { [K in keyof O]?: O[K] };\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gpio/index.d.ts",
    "content": "/**\n * Module for accessing the GPIO (General Purpose Input/Output) ports\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gpio = require(\"gpio\");\n * ```\n * \n * This module depends on the `event_loop` module, so it _must_ only be imported\n * after `event_loop` is imported.\n * \n * # Example\n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gpio = require(\"gpio\");\n * \n * let led = gpio.get(\"pc3\");\n * led.init({ direction: \"out\", outMode: \"push_pull\" });\n * \n * led.write(true);\n * delay(1000);\n * led.write(false);\n * delay(1000);\n * ```\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { Contract } from \"../event_loop\";\n\n/**\n * @version Added in JS SDK 0.1\n */\nexport interface Mode {\n    direction: \"in\" | \"out\";\n    outMode?: \"push_pull\" | \"open_drain\";\n    inMode?: \"analog\" | \"plain_digital\" | \"interrupt\" | \"event\";\n    edge?: \"rising\" | \"falling\" | \"both\";\n    pull?: \"up\" | \"down\";\n}\n\n/**\n * @version Added in JS SDK 0.1\n */\nexport interface Pin {\n    /**\n     * Configures a pin. This may be done several times.\n     * @param mode Pin configuration object\n     * @version Added in JS SDK 0.1\n     */\n    init(mode: Mode): void;\n    /**\n     * Sets the output value of a pin if it's been configured with\n     * `direction: \"out\"`.\n     * @param value Logic value to output\n     * @version Added in JS SDK 0.1\n     */\n    write(value: boolean): void;\n    /**\n     * Gets the input value of a pin if it's been configured with\n     * `direction: \"in\"`, but not `inMode: \"analog\"`.\n     * @version Added in JS SDK 0.1\n     */\n    read(): boolean;\n    /**\n     * Gets the input voltage of a pin in millivolts if it's been configured\n     * with `direction: \"in\"` and `inMode: \"analog\"`\n     * @version Added in JS SDK 0.1\n     */\n    readAnalog(): number;\n    /**\n     * Returns an `event_loop` event that can be used to listen to interrupts,\n     * as configured by `init`\n     * @version Added in JS SDK 0.1\n     */\n    interrupt(): Contract;\n    /**\n     * Determines whether this pin supports PWM. If `false`, all other\n     * PWM-related methods on this pin will throw an error when called.\n     * @note On Flipper Zero only pins PA4 and PA7 support PWM\n     * @version Added in JS SDK 0.2, extra feature `\"gpio-pwm\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    isPwmSupported(): boolean;\n    /**\n     * Sets PWM parameters and starts the PWM. Configures the pin with\n     * `{ direction: \"out\", outMode: \"push_pull\" }`. Throws an error if PWM is\n     * not supported on this pin.\n     * @param freq Frequency in Hz\n     * @param duty Duty cycle in %\n     * @version Added in JS SDK 0.2, extra feature `\"gpio-pwm\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    pwmWrite(freq: number, duty: number): void;\n    /**\n     * Determines whether PWM is running. Throws an error if PWM is not\n     * supported on this pin.\n     * @version Added in JS SDK 0.2, extra feature `\"gpio-pwm\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    isPwmRunning(): boolean;\n    /**\n     * Stops PWM. Does not restore previous pin configuration. Throws an error\n     * if PWM is not supported on this pin.\n     * @version Added in JS SDK 0.2, extra feature `\"gpio-pwm\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    pwmStop(): void;\n}\n\n/**\n * Returns an object that can be used to manage a GPIO pin. For the list of\n * available pins, see https://docs.flipper.net/gpio-and-modules#miFsS\n * @param pin Pin name (e.g. `\"PC3\"`) or number (e.g. `7`)\n * @version Added in JS SDK 0.1\n */\nexport function get(pin: string | number): Pin;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/button_menu.d.ts",
    "content": "/**\n * Displays a list of buttons.\n * \n * <img src=\"../images/button_menu.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let buttonMenuView = require(\"gui/button_menu\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Textual header above the buttons\n * \n * @version Added in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory, InputType } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string;\n};\n\ntype Child = { type: \"common\" | \"control\", label: string };\n\ndeclare class ButtonMenu extends View<Props, Child> {\n    input: Contract<{ index: number, type: InputType }>;\n}\ndeclare class ButtonMenuFactory extends ViewFactory<Props, Child, ButtonMenu> { }\ndeclare const factory: ButtonMenuFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/button_panel.d.ts",
    "content": "/**\n * Displays a button matrix.\n * \n * <img src=\"../images/button_panel.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let buttonPanelView = require(\"gui/button_panel\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `matrixSizeX`: Width of imaginary grid used for navigation\n *   - `matrixSizeY`: Height of imaginary grid used for navigation\n * \n * @version Added in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory, Font, InputType } from \".\";\nimport type { Contract } from \"../event_loop\";\nimport { IconData } from \"./icon\";\n\ntype Props = {\n    matrixSizeX: number,\n    matrixSizeY: number,\n};\n\ntype Position = { x: number, y: number };\n\ntype ButtonChild = { type: \"button\", matrixX: number, matrixY: number, icon: IconData, iconSelected: IconData } & Position;\ntype LabelChild = { type: \"label\", font: Font, text: string } & Position;\ntype IconChild = { type: \"icon\", icon: IconData };\n\ntype Child = ButtonChild | LabelChild | IconChild;\n\ndeclare class ButtonPanel extends View<Props, Child> {\n    input: Contract<{ index: number, type: InputType }>;\n}\ndeclare class ButtonPanelFactory extends ViewFactory<Props, Child, ButtonPanel> { }\ndeclare const factory: ButtonPanelFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/byte_input.d.ts",
    "content": "/**\n * Displays a byte input keyboard.\n * \n * <img src=\"../images/byte_input.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let byteInputView = require(\"gui/byte_input\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Text displayed at the top of the screen\n *   - `length`: Length of data to edit\n *   - `defaultData`: Data to show by default\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n    length: number,\n    defaultData: Uint8Array | ArrayBuffer,\n}\ntype Child = never;\ndeclare class ByteInput extends View<Props, Child> {\n    input: Contract<string>;\n}\ndeclare class ByteInputFactory extends ViewFactory<Props, Child, ByteInput> { }\ndeclare const factory: ByteInputFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/dialog.d.ts",
    "content": "/**\n * Displays a dialog with up to three options.\n * \n * <img src=\"../images/dialog.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let dialogView = require(\"gui/dialog\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Text displayed in bold at the top of the screen\n *   - `text`: Text displayed in the middle of the string\n *   - `left`: Text for the left button\n *   - `center`: Text for the center button\n *   - `right`: Text for the right button\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n    text: string,\n    left: string,\n    center: string,\n    right: string,\n}\ntype Child = never;\ndeclare class Dialog extends View<Props, Child> {\n    input: Contract<\"left\" | \"center\" | \"right\">;\n}\ndeclare class DialogFactory extends ViewFactory<Props, Child, Dialog> { }\ndeclare const factory: DialogFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/empty_screen.d.ts",
    "content": "/**\n * Displays nothing.\n * \n * <img src=\"../images/empty.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let emptyView = require(\"gui/empty_screen\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the GUI example.\n * \n * # View props\n * This view does not have any props.\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\n\ntype Props = {};\ntype Child = never;\ndeclare class EmptyScreen extends View<Props, Child> { }\ndeclare class EmptyScreenFactory extends ViewFactory<Props, Child, EmptyScreen> { }\ndeclare const factory: EmptyScreenFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/file_picker.d.ts",
    "content": "/**\n * @brief Displays a file picker and returns the selected file, or undefined if cancelled\n * @param basePath The path to start at\n * @param extension The file extension(s) to show (like `.sub`, `.iso|.img`, `*`)\n * @version Added in JS SDK 0.1\n */\nexport declare function pickFile(basePath: string, extension: string): string | undefined;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/icon.d.ts",
    "content": "export type BuiltinIcon = \"DolphinWait_59x54\" | \"js_script_10px\"\n    | \"off_19x20\" | \"off_hover_19x20\"\n    | \"power_19x20\" | \"power_hover_19x20\"\n    | \"Settings_14\";\n\nexport type IconData = symbol & { \"__tag__\": \"icon\" };\n// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist.\n\n/**\n * Gets a built-in firmware icon for use in GUI\n * @param icon Name of the icon\n * @version Added in JS SDK 0.2, extra feature `\"gui-widget\"`\n * @version Baseline since JS SDK 1.0\n */\nexport declare function getBuiltin(icon: BuiltinIcon): IconData;\n\n/**\n * Loads a .fxbm icon (XBM Flipper sprite, from flipperzero-game-engine) for use in GUI\n * @param path Path to the .fxbm file\n * @version Added in JS SDK 0.3, extra feature `\"gui-widget-extras\"`\n * @version Baseline since JS SDK 1.0\n */\nexport declare function loadFxbm(path: string): IconData;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/index.d.ts",
    "content": "/**\n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * ```\n * \n * This module depends on the `event_loop` module, so it _must_ only be imported\n * after `event_loop` is imported.\n * \n * ## Conceptualizing GUI\n * ### Event loop\n * It is highly recommended to familiarize yourself with the event loop first\n * before doing GUI-related things.\n * \n * ### Canvas\n * The canvas is just a drawing area with no abstractions over it. Drawing on\n * the canvas directly (i.e. not through a viewport) is useful in case you want\n * to implement a custom design element, but this is rather uncommon.\n * \n * ### Viewport\n * A viewport is a window into a rectangular portion of the canvas. Applications\n * always access the canvas through a viewport.\n * \n * ### View\n * In Flipper's terminology, a \"View\" is a fullscreen design element that\n * assumes control over the entire viewport and all input events. Different\n * types of views are available:\n * | View                 | Has JS adapter?       |\n * |----------------------|-----------------------|\n * | `button_menu`        | ✅                    |\n * | `button_panel`       | ✅                    |\n * | `byte_input`         | ✅                    |\n * | `dialog_ex`          | ✅ (as `dialog`)      |\n * | `empty_screen`       | ✅                    |\n * | `file_browser`       | ✅ (as `file_picker`) |\n * | `loading`            | ✅                    |\n * | `menu`               | ✅                    |\n * | `number_input`       | ✅                    |\n * | `popup`              | ✅                    |\n * | `submenu`            | ✅                    |\n * | `text_box`           | ✅                    |\n * | `text_input`         | ✅                    |\n * | `variable_item_list` | ✅ (as `vi_list`)     |\n * | `widget`             | ✅                    |\n * \n * In JS, each view has its own set of properties (or just \"props\"). The\n * programmer can manipulate these properties in two ways:\n *   - Instantiate a `View` using the `makeWith(props)` method, passing an\n *     object with the initial properties\n *   - Call `set(name, value)` to modify a property of an existing `View`\n * \n * ### View Dispatcher\n * The view dispatcher holds references to all the views that an application\n * needs and switches between them as the application makes requests to do so.\n * \n * ### Scene Manager\n * The scene manager is an optional add-on to the view dispatcher that makes\n * managing applications with complex navigation flows easier. It is currently\n * inaccessible from JS.\n * \n * ### Approaches\n * In total, there are three different approaches that you may take when writing\n * a GUI application:\n * | Approach       | Use cases                                                                    | Available from JS |\n * |----------------|------------------------------------------------------------------------------|-------------------|\n * | ViewPort only  | Accessing the graphics API directly, without any of the nice UI abstractions | ❌                |\n * | ViewDispatcher | Common UI elements that fit with the overall look of the system              | ✅                |\n * | SceneManager   | Additional navigation flow management for complex applications               | ❌                |\n * \n * # Example\n * An example with three different views using the ViewDispatcher approach:\n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let loadingView = require(\"gui/loading\");\n * let submenuView = require(\"gui/submenu\");\n * let emptyView = require(\"gui/empty_screen\");\n * \n * // Common pattern: declare all the views in an object. This is absolutely not\n * // required, but adds clarity to the script.\n * let views = {\n *     // the view dispatcher auto-✨magically✨ remembers views as they are created\n *     loading: loadingView.make(),\n *     empty: emptyView.make(),\n *     demos: submenuView.makeWith({\n *         items: [\n *             \"Hourglass screen\",\n *             \"Empty screen\",\n *             \"Exit app\",\n *         ],\n *     }),\n * };\n * \n * // go to different screens depending on what was selected\n * eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {\n *     if (index === 0) {\n *         gui.viewDispatcher.switchTo(views.loading);\n *     } else if (index === 1) {\n *         gui.viewDispatcher.switchTo(views.empty);\n *     } else if (index === 2) {\n *         eventLoop.stop();\n *     }\n * }, gui, eventLoop, views);\n * \n * // go to the demo chooser screen when the back key is pressed\n * eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views) {\n *     gui.viewDispatcher.switchTo(views.demos);\n * }, gui, views);\n * \n * // run UI\n * gui.viewDispatcher.switchTo(views.demos);\n * eventLoop.run();\n * ```\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { Contract } from \"../event_loop\";\n\nexport type Font = \"primary\" | \"secondary\" | \"keyboard\" | \"big_numbers\";\nexport type InputType = \"press\" | \"release\" | \"short\" | \"long\" | \"repeat\";\n\ntype Properties = { [K: string]: any };\n\nexport declare class View<Props extends Properties, Child> {\n    /**\n     * Assign value to property by name\n     * @param property Name of the property\n     * @param value Value to assign\n     * @version Added in JS SDK 0.1\n     */\n    set<P extends keyof Props>(property: P, value: Props[P]): void;\n    /**\n     * Adds a child to the View\n     * @param child Child to add\n     * @version Added in JS SDK 0.2, extra feature `\"gui-widget\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    addChild<C extends Child>(child: C): void;\n    /**\n     * Removes all children from the View\n     * @version Added in JS SDK 0.2, extra feature `\"gui-widget\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    resetChildren(): void;\n    /**\n     * Removes all previous children from the View and assigns new children\n     * @param children The list of children to assign\n     * @version Added in JS SDK 0.2, extra feature `\"gui-widget\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    setChildren(children: Child[]): void;\n}\n\nexport declare class ViewFactory<Props extends Properties, Child, V extends View<Props, Child>> {\n    /**\n     * Create view instance with default values, can be changed later with set()\n     * @version Added in JS SDK 0.1\n     */\n    make(): V;\n    /**\n     * Create view instance with custom values, can be changed later with set()\n     * @param initial Dictionary of property names to values\n     * @param children Optional list of children to add to the view\n     * @version Added in JS SDK 0.1\n     * @version Amended in JS SDK 0.2, extra feature `\"gui-widget\"`\n     * @version Baseline since JS SDK 1.0\n     */\n    makeWith(initial: Partial<Props>, children?: Child[]): V;\n}\n\n/**\n * @version Added in JS SDK 0.1\n */\ndeclare class ViewDispatcher {\n    /**\n     * Event source for `sendCustom` events\n     * @version Added in JS SDK 0.1\n     */\n    custom: Contract<number>;\n    /**\n     * Event source for navigation events (back key presses)\n     * @version Added in JS SDK 0.1\n     */\n    navigation: Contract;\n    /**\n     * View object currently shown\n     * @version Added in JS SDK 0.1\n     */\n    currentView: View<any, any>;\n    /**\n     * Sends a number to the custom event handler\n     * @param event number to send\n     * @version Added in JS SDK 0.1\n     */\n    sendCustom(event: number): void;\n    /**\n     * Switches to a view\n     * @param assoc View-ViewDispatcher association as returned by `add`\n     * @version Added in JS SDK 0.1\n     */\n    switchTo(assoc: View<any, any>): void;\n    /**\n     * Sends this ViewDispatcher to the front or back, above or below all other\n     * GUI viewports\n     * @param direction Either `\"front\"` or `\"back\"`\n     * @version Added in JS SDK 0.1\n     */\n    sendTo(direction: \"front\" | \"back\"): void;\n}\n\n/**\n * @version Added in JS SDK 0.1\n */\nexport const viewDispatcher: ViewDispatcher;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/loading.d.ts",
    "content": "/**\n * Displays an animated hourglass icon. Suppresses all `navigation` events,\n * making it impossible for the user to exit the view by pressing the back key.\n * \n * <img src=\"../images/loading.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let loadingView = require(\"gui/loading\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the GUI example.\n * \n * # View props\n * This view does not have any props.\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\n\ntype Props = {};\ntype Child = never;\ndeclare class Loading extends View<Props, Child> { }\ndeclare class LoadingFactory extends ViewFactory<Props, Child, Loading> { }\ndeclare const factory: LoadingFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/menu.d.ts",
    "content": "/**\n * A list of selectable entries consisting of an icon and a label.\n * \n * <img src=\"../images/menu.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let submenuView = require(\"gui/menu\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the GUI example.\n * \n * # View props\n * This view doesn't have any props.\n * \n * @version Added in JS SDK 0.1\n * @version API changed in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\nimport type { IconData } from \"./icon\";\n\ntype Props = {};\ntype Child = { icon: IconData, label: string };\ndeclare class Submenu extends View<Props, Child> {\n    chosen: Contract<number>;\n}\ndeclare class SubmenuFactory extends ViewFactory<Props, Child, Submenu> { }\ndeclare const factory: SubmenuFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/number_input.d.ts",
    "content": "/**\n * Displays a number input keyboard.\n * \n * <img src=\"../images/number_input.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let numberInputView = require(\"gui/number_input\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Text displayed at the top of the screen\n *   - `minValue`: Minimum allowed numeric value\n *   - `maxValue`: Maximum allowed numeric value\n *   - `defaultValue`: Default numeric value\n * \n * @version Added in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n    minValue: number,\n    maxValue: number,\n    defaultValue: number,\n}\ntype Child = never;\ndeclare class NumberInput extends View<Props, Child> {\n    input: Contract<number>;\n}\ndeclare class NumberInputFactory extends ViewFactory<Props, Child, NumberInput> { }\ndeclare const factory: NumberInputFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/popup.d.ts",
    "content": "/**\n * Like a Dialog, but with a built-in timer.\n * \n * <img src=\"../images/popup.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let popupView = require(\"gui/popup\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Text displayed in bold at the top of the screen\n *   - `text`: Text displayed in the middle of the string\n *   - `timeout`: Timeout, in milliseconds, after which the event will fire. The\n *     timer starts counting down when this property is assigned.\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n    text: string,\n    timeout: number,\n}\ntype Child = never;\ndeclare class Popup extends View<Props, Child> {\n    timeout: Contract;\n}\ndeclare class PopupFactory extends ViewFactory<Props, Child, Popup> { }\ndeclare const factory: PopupFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/submenu.d.ts",
    "content": "/**\n * Displays a scrollable list of clickable textual entries.\n * \n * <img src=\"../images/submenu.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let submenuView = require(\"gui/submenu\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the GUI example.\n * \n * # View props\n *   - `header`: Text displayed at the top of the screen in bold\n * \n * @version Added in JS SDK 0.1\n * @version API changed in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n};\ntype Child = string;\ndeclare class Submenu extends View<Props, Child> {\n    chosen: Contract<number>;\n}\ndeclare class SubmenuFactory extends ViewFactory<Props, Child, Submenu> { }\ndeclare const factory: SubmenuFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/text_box.d.ts",
    "content": "/**\n * Displays a scrollable read-only text field.\n *\n * <img src=\"text_box.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let textBoxView = require(\"gui/text_box\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `text`: Text in the text box\n *   - `font`: The font to display the text in (`\"text\"` or `\"hex\"`)\n *   - `focus`: The initial focus of the text box (`\"start\"` or `\"end\"`)\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    text: string,\n    font: \"text\" | \"hex\",\n    focus: \"start\" | \"end\",\n}\ntype Child = never;\ndeclare class TextBox extends View<Props, Child> {\n    chosen: Contract<number>;\n}\ndeclare class TextBoxFactory extends ViewFactory<Props, Child, TextBox> { }\ndeclare const factory: TextBoxFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/text_input.d.ts",
    "content": "/**\n * Displays a text input keyboard.\n * \n * <img src=\"../images/text_input.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let textInputView = require(\"gui/text_input\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n *   - `header`: Text displayed at the top of the screen\n *   - `minLength`: Minimum allowed text length\n *   - `maxLength`: Maximum allowed text length\n *   - `defaultText`: Text to show by default\n *   - `defaultTextClear`: Whether to clear the default text on next character typed\n * \n * @version Added in JS SDK 0.1\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {\n    header: string,\n    minLength: number,\n    maxLength: number,\n    defaultText: string,\n    defaultTextClear: boolean,\n}\ntype Child = never;\ndeclare class TextInput extends View<Props, Child> {\n    input: Contract<string>;\n}\ndeclare class TextInputFactory extends ViewFactory<Props, Child, TextInput> { }\ndeclare const factory: TextInputFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/vi_list.d.ts",
    "content": "/**\n * Displays a list of settings-like variable items.\n * \n * <img src=\"../images/vi_list.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let viListView = require(\"gui/vi_list\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the `gui.js` example script.\n * \n * # View props\n * This view doesn't have any props\n * \n * @version Added in JS SDK 0.4\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { Contract } from \"../event_loop\";\n\ntype Props = {};\n\ntype Child = { label: string, variants: string[] };\n\ndeclare class ViList extends View<Props, Child> {\n    valueUpdate: Contract<{ itemIndex: number, valueIndex: number }>;\n}\ndeclare class ViListFactory extends ViewFactory<Props, Child, ViList> { }\ndeclare const factory: ViListFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/gui/widget.d.ts",
    "content": "/**\n * Displays a combination of custom elements on one screen.\n * \n * <img src=\"../images/widget.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n * \n * ```js\n * let eventLoop = require(\"event_loop\");\n * let gui = require(\"gui\");\n * let emptyView = require(\"gui/widget\");\n * ```\n * \n * This module depends on the `gui` module, which in turn depends on the\n * `event_loop` module, so they _must_ be imported in this order. It is also\n * recommended to conceptualize these modules first before using this one.\n * \n * # Example\n * For an example refer to the GUI example.\n * \n * # View props\n * This view does not have any props.\n * \n * # Children\n * This view has the elements as its children.\n * \n * @version Added in JS SDK 0.2, extra feature `\"gui-widget\"`\n * @version Baseline since JS SDK 1.0\n * @module\n */\n\nimport type { View, ViewFactory } from \".\";\nimport type { IconData } from \"./icon\";\nimport type { Contract } from \"../event_loop\";\n\ntype Position = { x: number, y: number };\ntype Size = { w: number, h: number };\ntype Alignment = { align: `${\"t\" | \"c\" | \"b\"}${\"l\" | \"m\" | \"r\"}` };\ntype Font = { font: \"primary\" | \"secondary\" | \"keyboard\" | \"big_numbers\" };\ntype Text = { text: string };\n\ntype StringMultilineElement = { element: \"string_multiline\" } & Position & Alignment & Font & Text;\ntype StringElement = { element: \"string\" } & Position & Alignment & Font & Text;\ntype TextBoxElement = { element: \"text_box\", stripToDots: boolean } & Position & Size & Alignment & Text;\ntype TextScrollElement = { element: \"text_scroll\" } & Position & Size & Text;\ntype ButtonElement = { element: \"button\", button: \"left\" | \"center\" | \"right\" } & Text;\ntype IconElement = { element: \"icon\", iconData: IconData } & Position;\n/**\n * @version Amended in JS SDK 0.3, extra feature `\"gui-widget-extras\"`\n * @version Baseline since JS SDK 1.0\n * */\ntype RectElement = { element: \"rect\", radius: number, fill: boolean } & Position & Size;\n/**\n * @version Added in JS SDK 0.3, extra feature `\"gui-widget-extras\"`\n * @version Baseline since JS SDK 1.0\n * */\ntype CircleElement = { element: \"circle\", radius: number, fill: boolean } & Position;\n/**\n * @version Added in JS SDK 0.3, extra feature `\"gui-widget-extras\"`\n * @version Baseline since JS SDK 1.0\n * */\ntype LineElement = { element: \"line\", x1: number, y1: number, x2: number, y2: number };\n\ntype Element = StringMultilineElement\n    | StringElement\n    | TextBoxElement\n    | TextScrollElement\n    | ButtonElement\n    | IconElement\n    | RectElement\n    | CircleElement\n    | LineElement;\n\ntype Props = {};\ntype Child = Element;\ndeclare class ButtonEvent {\n    key: \"left\" | \"center\" | \"right\";\n    type: \"press\" | \"release\" | \"short\" | \"long\" | \"repeat\";\n}\ndeclare class Widget extends View<Props, Child> {\n    /**\n     * Event source for buttons. Only gets fired if there's a corresponding\n     * button element.\n     */\n    button: Contract<ButtonEvent>;\n}\ndeclare class WidgetFactory extends ViewFactory<Props, Child, Widget> { }\ndeclare const factory: WidgetFactory;\nexport = factory;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/math/index.d.ts",
    "content": "/**\n * Math operations\n * @version Added in JS SDK 0.1\n * @module\n */\n\n/** @version Added in JS SDK 0.1 */\nexport function isEqual(a: number, b: number, tolerance: number): boolean;\n/** @version Added in JS SDK 0.1 */\nexport function abs(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function acos(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function acosh(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function asin(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function asinh(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function atan(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function atan2(a: number, b: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function atanh(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function cbrt(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function ceil(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function clz32(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function cos(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function exp(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function floor(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function log(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function max(n: number, m: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function min(n: number, m: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function pow(n: number, m: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function random(): number;\n/** @version Added in JS SDK 0.1 */\nexport function sign(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function sin(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function sqrt(n: number): number;\n/** @version Added in JS SDK 0.1 */\nexport function trunc(n: number): number;\n/** @version Added in JS SDK 0.1 */\ndeclare const PI: number;\n/** @version Added in JS SDK 0.1 */\ndeclare const E: number;\n/** @version Added in JS SDK 0.1 */\ndeclare const EPSILON: number;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/notification/index.d.ts",
    "content": "/**\n * Module for using the color LED and vibration motor\n * @version Added in JS SDK 0.1\n * @module\n */\n\n/**\n * @brief Signals success to the user via the color LED, speaker and vibration\n *        motor\n * @version Added in JS SDK 0.1\n */\nexport declare function success(): void;\n\n/**\n * @brief Signals failure to the user via the color LED, speaker and vibration\n *        motor\n * @version Added in JS SDK 0.1\n */\nexport declare function error(): void;\n\n/**\n * @version Added in JS SDK 0.1\n */\nexport type Color = \"red\" | \"green\" | \"blue\" | \"yellow\" | \"cyan\" | \"magenta\";\n\n/**\n * @brief Displays a basic color on the color LED\n * @param color The color to display, see `Color`\n * @param duration The duration, either `\"short\"` (10ms) or `\"long\"` (100ms)\n * @version Added in JS SDK 0.1\n */\nexport declare function blink(color: Color, duration: \"short\" | \"long\"): void;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/package.json",
    "content": "{\n  \"name\": \"@flipperdevices/fz-sdk\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Type declarations and documentation for native JS modules available on Flipper Zero\",\n  \"keywords\": [\n    \"flipper\",\n    \"flipper zero\",\n    \"framework\"\n  ],\n  \"author\": \"Flipper Devices\",\n  \"license\": \"GPL-3.0-only\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/flipperdevices/flipperzero-firmware.git\",\n    \"directory\": \"applications/system/js_app/packages/fz-sdk\"\n  },\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"esbuild\": \"^0.24.0\",\n    \"esbuild-plugin-tsc\": \"^0.4.0\",\n    \"json5\": \"^2.2.3\",\n    \"typedoc\": \"^0.26.10\",\n    \"typedoc-material-theme\": \"^1.1.0\",\n    \"prompts\": \"^2.4.2\",\n    \"serialport\": \"^12.0.0\"\n  }\n}\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/sdk.js",
    "content": "#!/usr/bin/env node\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { SerialPort } from \"serialport\";\nimport prompts from \"prompts\";\nimport esbuild from \"esbuild\";\nimport json5 from \"json5\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nasync function build(config) {\n    await esbuild.build({\n        entryPoints: [\"./dist/index.js\"],\n        outfile: config.output,\n        tsconfig: \"./tsconfig.json\",\n        format: \"cjs\",\n        bundle: true,\n        minify: config.minify,\n        external: [\n            \"@flipperdevices/fz-sdk/*\"\n        ],\n        supported: {\n            \"array-spread\": false,\n            \"arrow\": false,\n            \"async-await\": false,\n            \"async-generator\": false,\n            \"bigint\": false,\n            \"class\": false,\n            \"const-and-let\": true,\n            \"decorators\": false,\n            \"default-argument\": false,\n            \"destructuring\": false,\n            \"dynamic-import\": false,\n            \"exponent-operator\": false,\n            \"export-star-as\": false,\n            \"for-await\": false,\n            \"for-of\": false,\n            \"function-name-configurable\": false,\n            \"function-or-class-property-access\": false,\n            \"generator\": false,\n            \"hashbang\": false,\n            \"import-assertions\": false,\n            \"import-meta\": false,\n            \"inline-script\": false,\n            \"logical-assignment\": false,\n            \"nested-rest-binding\": false,\n            \"new-target\": false,\n            \"node-colon-prefix-import\": false,\n            \"node-colon-prefix-require\": false,\n            \"nullish-coalescing\": false,\n            \"object-accessors\": false,\n            \"object-extensions\": false,\n            \"object-rest-spread\": false,\n            \"optional-catch-binding\": false,\n            \"optional-chain\": false,\n            \"regexp-dot-all-flag\": false,\n            \"regexp-lookbehind-assertions\": false,\n            \"regexp-match-indices\": false,\n            \"regexp-named-capture-groups\": false,\n            \"regexp-set-notation\": false,\n            \"regexp-sticky-and-unicode-flags\": false,\n            \"regexp-unicode-property-escapes\": false,\n            \"rest-argument\": false,\n            \"template-literal\": false,\n            \"top-level-await\": false,\n            \"typeof-exotic-object-is-object\": false,\n            \"unicode-escapes\": false,\n            \"using\": false,\n        },\n    });\n\n    let outContents = fs.readFileSync(config.output, \"utf8\");\n    outContents = \"let exports = {};\\n\" + outContents;\n\n    if (config.enforceSdkVersion) {\n        const version = json5.parse(fs.readFileSync(path.join(__dirname, \"package.json\"), \"utf8\")).version;\n        let [major, minor, _] = version.split(\".\");\n        outContents = `checkSdkCompatibility(${major}, ${minor});\\n${outContents}`;\n    }\n\n    fs.writeFileSync(config.output, outContents);\n}\n\nasync function upload(config) {\n    const appFile = fs.readFileSync(config.input, \"utf8\");\n    const serialPorts = await SerialPort.list();\n\n    let flippers = serialPorts\n        .filter(x => x.serialNumber?.startsWith(\"flip_\"))\n        .map(x => ({ path: x.path, name: x.serialNumber.replace(\"flip_\", \"\") }));\n\n    if (!flippers.length) {\n        // some Windows installations don't report the serial number correctly;\n        // filter by STM VCP VID:PID instead\n        flippers = serialPorts\n            .filter(x => x?.vendorId === \"0483\" && x?.productId === \"5740\")\n            .map(x => ({ path: x.path, name: x.path }));\n    }\n\n    if (!flippers.length) {\n        console.error(\"No Flippers found\");\n        process.exit(1);\n    }\n\n    let portPath = flippers[0].path;\n    if (flippers.length > 1) {\n        port = (await prompts([{\n            type: \"select\",\n            name: \"port\",\n            message: \"Select Flipper to run the app on\",\n            choices: flippers.map(x => ({ title: x.serialNumber.replace(\"flip_\", \"\"), value: x.path })),\n        }])).port;\n    }\n\n    console.log(`Connecting to Flipper at ${portPath}`);\n    let port = new SerialPort({ path: portPath, baudRate: 230400 });\n    let received = \"\";\n    let lastMatch = 0;\n    async function waitFor(string, timeoutMs) {\n        return new Promise((resolve, _reject) => {\n            let timeout = undefined;\n            if (timeoutMs) {\n                timeout = setTimeout(() => {\n                    console.error(\"Error: timeout\");\n                    process.exit(1);\n                }, timeoutMs);\n            }\n            setInterval(() => {\n                let idx = received.indexOf(string, lastMatch);\n                if (idx !== -1) {\n                    lastMatch = idx;\n                    if (timeoutMs)\n                        clearTimeout(timeout);\n                    resolve();\n                }\n            }, 50);\n        });\n    }\n    port.on(\"data\", (data) => {\n        received += data.toString();\n    });\n\n    await waitFor(\">: \", 1000);\n    console.log(\"Uploading application file\");\n    port.write(`storage remove ${config.output}\\x0d`);\n    port.drain();\n    await waitFor(\">: \", 1000);\n    port.write(`storage write_chunk ${config.output} ${appFile.length}\\x0d`);\n    await waitFor(\"Ready\", 1000);\n    port.write(appFile);\n    port.drain();\n    await waitFor(\">: \", 1000);\n\n    console.log(\"Launching application\");\n    port.write(`js ${config.output}\\x0d`);\n    port.drain();\n\n    await waitFor(\"Running\", 1000);\n    process.stdout.write(received.slice(lastMatch));\n    port.on(\"data\", (data) => {\n        process.stdout.write(data.toString());\n    });\n    process.on(\"exit\", () => {\n        port.write(\"\\x03\");\n    });\n\n    await waitFor(\"Script done!\", 0);\n    process.exit(0);\n}\n\n(async () => {\n    const commands = {\n        \"build\": build,\n        \"upload\": upload,\n    };\n\n    const config = json5.parse(fs.readFileSync(\"./fz-sdk.config.json5\", \"utf8\"));\n    const command = process.argv[2];\n\n    if (!Object.keys(commands).includes(command)) {\n        console.error(`Unknown command ${command}. Supported: ${Object.keys(commands).join(\", \")}`);\n        process.exit(1);\n    }\n\n    await commands[command](config[command]);\n})();\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/serial/index.d.ts",
    "content": "/**\n * Module for accessing the serial port\n * @version Added in JS SDK 0.1\n * @module\n */\n\nexport interface Framing {\n    /**\n     * @note 6 data bits can only be selected when parity is enabled (even or\n     * odd)\n     * @note 9 data bits can only be selected when parity is disabled (none)\n     */\n    dataBits: \"6\" | \"7\" | \"8\" | \"9\";\n    parity: \"none\" | \"even\" | \"odd\";\n    /**\n     * @note LPUART only supports whole stop bit lengths (i.e. 1 and 2 but not\n     * 0.5 and 1.5)\n     */\n    stopBits: \"0.5\" | \"1\" | \"1.5\" | \"2\";\n}\n\n/**\n * @brief Initializes the serial port\n * \n * Automatically disables Expansion module service to prevent interference.\n * \n * @param port The port to initialize (`\"lpuart\"` or `\"usart\"`)\n * @param baudRate Baud rate\n * @param framing See `Framing` type\n * @version Added in JS SDK 0.1\n * @version Added `framing` parameter in JS SDK 0.3, extra feature `\"serial-framing\"`\n * @version Baseline since JS SDK 1.0\n */\nexport declare function setup(port: \"lpuart\" | \"usart\", baudRate: number, framing?: Framing): void;\n\n/**\n * @brief Writes data to the serial port\n * @param value The data to write:\n *                - Strings will get sent as ASCII.\n *                - Numbers will get sent as a single byte.\n *                - Arrays of numbers will get sent as a sequence of bytes.\n *                - `ArrayBuffer`s and `TypedArray`s will be sent as a sequence\n *                  of bytes.\n * @version Added in JS SDK 0.1\n */\nexport declare function write<E extends ElementType>(value: string | number | number[] | ArrayBuffer | TypedArray<E>): void;\n\n/**\n * @brief Reads data from the serial port\n * @param length The number of bytes to read\n * @param timeout The number of time, in milliseconds, after which this function\n *                will give up and return what it read up to that point. If\n *                unset, the function will wait forever.\n * @returns The received data interpreted as ASCII, or `undefined` if 0 bytes\n *          were read.\n * @version Added in JS SDK 0.1\n */\nexport declare function read(length: number, timeout?: number): string | undefined;\n\n/**\n * @brief Reads data from the serial port\n * \n * Data is read one character after another until either a `\\r` or `\\n`\n * character is received, neither of which is included in the result.\n * \n * @param timeout The number of time, in milliseconds, after which this function\n *                will give up and return what it read up to that point. If\n *                unset, the function will wait forever. The timeout only\n *                applies to characters, not entire strings.\n * @returns The received data interpreted as ASCII, or `undefined` if 0 bytes\n *          were read.\n * @version Added in JS SDK 0.1\n */\nexport declare function readln(timeout?: number): string;\n\n/**\n * @brief Read any available amount of data from the serial port\n * \n * Can be useful to avoid starving your loop with small reads.\n * \n * @param timeout The number of time, in milliseconds, after which this function\n *                will give up and return nothing. If unset, the function will\n *                wait forever.\n * @returns The received data interpreted as ASCII, or `undefined` if 0 bytes\n *          were read.\n * @version Added in JS SDK 0.1\n */\nexport declare function readAny(timeout?: number): string | undefined;\n\n/**\n * @brief Reads data from the serial port\n * @param length The number of bytes to read\n * @param timeout The number of time, in milliseconds, after which this function\n *                will give up and return what it read up to that point. If\n *                unset, the function will wait forever.\n * @returns The received data as an ArrayBuffer, or `undefined` if 0 bytes were\n *          read.\n * @version Added in JS SDK 0.1\n */\nexport declare function readBytes(length: number, timeout?: number): ArrayBuffer;\n\n/**\n * @brief Reads data from the serial port, trying to match it to a pattern\n * @param patterns A single pattern or an array of patterns:\n *                   - If the argument is a single `string`, this function will\n *                     match against the given string.\n *                   - If the argument is an array of `number`s, this function\n *                     will match against the given sequence of bytes,\n *                   - If the argument is an array of `string`s, this function\n *                     will match against any string out of the ones that were\n *                     provided.\n *                   - If the argument is an array of arrays of `number`s, this\n *                     function will match against any sequence of bytes out of\n *                     the ones that were provided.\n * @param timeout The number of time, in milliseconds, after which this function\n *                will give up and return what it read up to that point. If\n *                unset, the function will wait forever. The timeout only\n *                applies to characters, not entire strings.\n * @returns The index of the matched pattern if multiple were provided, or 0 if\n *          only one was provided and it matched, or `undefined` if none of the\n *          patterns matched.\n * @version Added in JS SDK 0.1\n */\nexport declare function expect(patterns: string | number[] | string[] | number[][], timeout?: number): number | undefined;\n\n/**\n * @brief Deinitializes the serial port, allowing multiple initializations per script run\n * @version Added in JS SDK 0.1\n */\nexport declare function end(): void;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/storage/index.d.ts",
    "content": "/**\n * Module for accessing the filesystem\n * @version Added in JS SDK 0.1\n * @module\n */\n\n/**\n * File readability mode:\n *   - `\"r\"`: read-only\n *   - `\"w\"`: write-only\n *   - `\"rw\"`: read-write\n * @version Added in JS SDK 0.1\n */\nexport type AccessMode = \"r\" | \"w\" | \"rw\";\n\n/**\n * File creation mode:\n *   - `\"open_existing\"`: open file or fail if it doesn't exist\n *   - `\"open_always\"`: open file or create a new empty one if it doesn't exist\n *   - `\"open_append\"`: open file and set r/w pointer to EOF, or create a new one if it doesn't exist\n *   - `\"create_new\"`: create new file or fail if it exists\n *   - `\"create_always\"`: truncate and open file, or create a new empty one if it doesn't exist\n * @version Added in JS SDK 0.1\n */\nexport type OpenMode = \"open_existing\" | \"open_always\" | \"open_append\" | \"create_new\" | \"create_always\";\n\n/**\n * Standard UNIX timestamp\n * @version Added in JS SDK 0.1\n */\nexport type Timestamp = number;\n\n/**\n * File information structure\n * @version Added in JS SDK 0.1\n */\nexport declare class FileInfo {\n    /**\n     * Full path (e.g. \"/ext/test\", returned by `stat`) or file name\n     * (e.g. \"test\", returned by `readDirectory`)\n     * @version Added in JS SDK 0.1\n     */\n    path: string;\n    /**\n     * Is the file a directory?\n     * @version Added in JS SDK 0.1\n     */\n    isDirectory: boolean;\n    /**\n     * File size in bytes, or 0 in the case of directories\n     * @version Added in JS SDK 0.1\n     */\n    size: number;\n    /**\n     * Time of last access as a UNIX timestamp\n     * @version Added in JS SDK 0.1\n     */\n    accessTime: Timestamp;\n}\n\n/**\n * Filesystem information structure\n * @version Added in JS SDK 0.1\n */\nexport declare class FsInfo {\n    /**\n     * Total size of the filesystem, in bytes\n     * @version Added in JS SDK 0.1\n     */\n    totalSpace: number;\n    /**\n     * Free space in the filesystem, in bytes\n     * @version Added in JS SDK 0.1\n     */\n    freeSpace: number;\n}\n\n// file operations\n\n/**\n * File class\n * @version Added in JS SDK 0.1\n */\nexport declare class File {\n    /**\n     * Closes the file. After this method is called, all other operations\n     * related to this file become unavailable.\n     * @returns `true` on success, `false` on failure\n     * @version Added in JS SDK 0.1\n     */\n    close(): boolean;\n    /**\n     * Is the file currently open?\n     * @version Added in JS SDK 0.1\n     */\n    isOpen(): boolean;\n    /**\n     * Reads bytes from a file opened in read-only or read-write mode\n     * @param mode The data type to interpret the bytes as: a `string` decoded\n     *             from ASCII data (`\"ascii\"`), or an `ArrayBuf` (`\"binary\"`)\n     * @param bytes How many bytes to read from the file\n     * @returns an `ArrayBuf` if the mode is `\"binary\"`, a `string` if the mode\n     *          is `ascii`. The number of bytes that was actually read may be\n     *          fewer than requested.\n     * @version Added in JS SDK 0.1\n     */\n    read<T extends ArrayBuffer | string>(mode: T extends ArrayBuffer ? \"binary\" : \"ascii\", bytes: number): T;\n    /**\n     * Writes bytes to a file opened in write-only or read-write mode\n     * @param data The data to write: a string that will be ASCII-encoded, or an\n     *             ArrayBuf\n     * @returns the amount of bytes that was actually written\n     * @version Added in JS SDK 0.1\n     */\n    write(data: ArrayBuffer | string): number;\n    /**\n     * Moves the R/W pointer forward\n     * @param bytes How many bytes to move the pointer forward by\n     * @returns `true` on success, `false` on failure\n     * @version Added in JS SDK 0.1\n     */\n    seekRelative(bytes: number): boolean;\n    /**\n     * Moves the R/W pointer to an absolute position inside the file\n     * @param bytes The position inside the file\n     * @returns `true` on success, `false` on failure\n     * @version Added in JS SDK 0.1\n     */\n    seekAbsolute(bytes: number): boolean;\n    /**\n     * Gets the absolute position of the R/W pointer in bytes\n     * @version Added in JS SDK 0.1\n     */\n    tell(): number;\n    /**\n     * Discards the data after the current position of the R/W pointer in a file\n     * opened in either write-only or read-write mode.\n     * @returns `true` on success, `false` on failure\n     * @version Added in JS SDK 0.1\n     */\n    truncate(): boolean;\n    /**\n     * Reads the total size of the file in bytes\n     * @version Added in JS SDK 0.1\n     */\n    size(): number;\n    /**\n     * Detects whether the R/W pointer has reached the end of the file\n     * @version Added in JS SDK 0.1\n     */\n    eof(): boolean;\n    /**\n     * Copies bytes from the R/W pointer in the current file to the R/W pointer\n     * in another file\n     * @param dest The file to copy the bytes into\n     * @param bytes The number of bytes to copy\n     * @returns `true` on success, `false` on failure\n     * @version Added in JS SDK 0.1\n     */\n    copyTo(dest: File, bytes: number): boolean;\n}\n\n/**\n * Opens a file\n * @param path The path to the file\n * @param accessMode `\"r\"`, `\"w\"` or `\"rw\"`; see `AccessMode`\n * @param openMode `\"open_existing\"`, `\"open_always\"`, `\"open_append\"`,\n *                 `\"create_new\"` or `\"create_always\"`; see `OpenMode`\n * @returns a `File` on success, or `undefined` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function openFile(path: string, accessMode: AccessMode, openMode: OpenMode): File | undefined;\n/**\n * Detects whether a file exists\n * @param path The path to the file\n * @returns `true` on success, `false` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function fileExists(path: string): boolean;\n\n// directory operations\n\n/**\n * Reads the list of files in a directory\n * @param path The path to the directory\n * @returns Array of `FileInfo` structures with directory entries,\n *          or `undefined` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function readDirectory(path: string): FileInfo[] | undefined;\n/**\n * Detects whether a directory exists\n * @param path The path to the directory\n * @version Added in JS SDK 0.1\n */\nexport declare function directoryExists(path: string): boolean;\n/**\n * Creates an empty directory\n * @param path The path to the new directory\n * @returns `true` on success, `false` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function makeDirectory(path: string): boolean;\n\n// common (file/dir) operations\n\n/**\n * Detects whether a file or a directory exists\n * @param path The path to the file or directory\n * @version Added in JS SDK 0.1\n */\nexport declare function fileOrDirExists(path: string): boolean;\n/**\n * Acquires metadata about a file or directory\n * @param path The path to the file or directory\n * @returns A `FileInfo` structure or `undefined` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function stat(path: string): FileInfo | undefined;\n/**\n * Removes a file or an empty directory\n * @param path The path to the file or directory\n * @returns `true` on success, `false` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function remove(path: string): boolean;\n/**\n * Removes a file or recursively removes a possibly non-empty directory\n * @param path The path to the file or directory\n * @returns `true` on success, `false` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function rmrf(path: string): boolean;\n/**\n * Renames or moves a file or directory\n * @param oldPath The old path to the file or directory\n * @param newPath The new path that the file or directory will become accessible\n *                under\n * @returns `true` on success, `false` on failure\n * @version Added in JS SDK 0.1\n */\nexport declare function rename(oldPath: string, newPath: string): boolean;\n/**\n * Copies a file or recursively copies a possibly non-empty directory\n * @param oldPath The original path to the file or directory\n * @param newPath The new path that the copy of the file or directory will be\n *                accessible under\n * @version Added in JS SDK 0.1\n */\nexport declare function copy(oldPath: string, newPath: string): boolean;\n/**\n * Fetches generic information about a filesystem\n * @param filesystem The path to the filesystem (e.g. `\"/ext\"` or `\"/int\"`)\n * @version Added in JS SDK 0.1\n */\nexport declare function fsInfo(filesystem: string): FsInfo | undefined;\n/**\n * Chooses the next available filename with a numeric suffix in a directory\n * \n * ```\n * \"/ext/example_dir/example_file123.txt\"\n *  \\______________/ \\__________/\\_/\\__/\n *       dirPath       fileName   |  |\n *                                |  +---- fileExt\n *                                +------- selected by this function\n * ```\n * \n * @param dirPath The directory to look in\n * @param fileName The base of the filename (the part before the numeric suffix)\n * @param fileExt The extension of the filename (the part after the numeric suffix)\n * @param maxLen The maximum length of the filename with the numeric suffix\n * @returns The base of the filename with the next available numeric suffix,\n *          without the extension or the base directory.\n * @version Added in JS SDK 0.1\n */\nexport declare function nextAvailableFilename(dirPath: string, fileName: string, fileExt: string, maxLen: number): string;\n\n// path operations that do not access the filesystem\n\n/**\n * Determines whether the two paths are equivalent. Respects filesystem-defined\n * path equivalence rules.\n * @version Added in JS SDK 0.1\n */\nexport declare function arePathsEqual(path1: string, path2: string): boolean;\n/**\n * Determines whether a path is a subpath of another path. Respects\n * filesystem-defined path equivalence rules.\n * @param parentPath The parent path\n * @param childPath The child path\n * @version Added in JS SDK 0.1\n */\nexport declare function isSubpathOf(parentPath: string, childPath: string): boolean;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/tests/index.d.ts",
    "content": "/**\n * Unit test module. Only available if the firmware has been configured with\n * `FIRMWARE_APP_SET=unit_tests`.\n * @version Added in JS SDK 0.1\n * @module\n */\n\nexport function fail(message: string): never;\nexport function assert_eq<T>(expected: T, result: T): void | never;\nexport function assert_float_close(expected: number, result: number, epsilon: number): void | never;\n"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"checkJs\": true,\n        \"module\": \"CommonJS\",\n        \"noLib\": true,\n    },\n    \"include\": [\n        \"./**/*.d.ts\"\n    ],\n    \"exclude\": [\n        \"node_modules\",\n    ]\n}"
  },
  {
    "path": "applications/system/js_app/packages/fz-sdk/typedoc.json",
    "content": "{\n    \"$schema\": \"https://typedoc.org/schema.json\",\n    \"name\": \"Flipper Zero JS API\",\n    \"excludePrivate\": true,\n    \"entryPointStrategy\": \"expand\",\n    \"entryPoints\": [\n        \".\",\n    ],\n    \"exclude\": [\n        \"node_modules\"\n    ],\n    \"cleanOutputDir\": true,\n    \"out\": \"./docs\",\n    \"plugin\": [\n        \"typedoc-material-theme\",\n    ],\n    \"readme\": \"./docs_readme.md\",\n    \"themeColor\": \"#ff8200\",\n}"
  },
  {
    "path": "applications/system/js_app/plugin_api/app_api_interface.h",
    "content": "#pragma once\n\n#include <flipper_application/api_hashtable/api_hashtable.h>\n\n/* \n * Resolver interface with private application's symbols. \n * Implementation is contained in app_api_table.c\n */\nextern const ElfApiInterface* const application_api_interface;\n"
  },
  {
    "path": "applications/system/js_app/plugin_api/app_api_table.cpp",
    "content": "#include <flipper_application/api_hashtable/api_hashtable.h>\n#include <flipper_application/api_hashtable/compilesort.hpp>\n\n/* \n * This file contains an implementation of a symbol table \n * with private app's symbols. It is used by composite API resolver\n * to load plugins that use internal application's APIs.\n */\n#include \"app_api_table_i.h\"\n\nstatic_assert(!has_hash_collisions(app_api_table), \"Detected API method hash collision!\");\n\nconstexpr HashtableApiInterface applicaton_hashtable_api_interface{\n    {\n        .api_version_major = 0,\n        .api_version_minor = 0,\n        /* generic resolver using pre-sorted array */\n        .resolver_callback = &elf_resolve_from_hashtable,\n    },\n    /* pointers to application's API table boundaries */\n    app_api_table.cbegin(),\n    app_api_table.cend(),\n};\n\n/* Casting to generic resolver to use in Composite API resolver */\nextern \"C\" const ElfApiInterface* const application_api_interface =\n    &applicaton_hashtable_api_interface;\n"
  },
  {
    "path": "applications/system/js_app/plugin_api/app_api_table_i.h",
    "content": "#include \"../js_modules.h\"\n\n/* \n * A list of app's private functions and objects to expose for plugins.\n * It is used to generate a table of symbols for import resolver to use.\n * TBD: automatically generate this table from app's header files\n */\nstatic constexpr auto app_api_table = sort(create_array_t<sym_entry>(\n    API_METHOD(js_delay_with_flags, bool, (struct mjs*, uint32_t)),\n    API_METHOD(js_flags_set, void, (struct mjs*, uint32_t)),\n    API_METHOD(js_flags_wait, uint32_t, (struct mjs*, uint32_t, uint32_t)),\n    API_METHOD(js_module_get, void*, (JsModules*, const char*)),\n    API_METHOD(js_value_buffer_size, size_t, (const JsValueParseDeclaration declaration)),\n    API_METHOD(\n        js_value_parse,\n        JsValueParseStatus,\n        (struct mjs * mjs,\n         const JsValueParseDeclaration declaration,\n         JsValueParseFlag flags,\n         mjs_val_t* buffer,\n         size_t buf_size,\n         mjs_val_t* source,\n         size_t n_c_vals,\n         ...))));\n"
  },
  {
    "path": "applications/system/js_app/views/console_font.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n/*\n  Fontname: -misc-spleen-medium-r-normal--8-80-72-72-C-50-ISO10646-1\n  Copyright: Copyright (c) 2018-2022, Frederic Cambus\n  Glyphs: 96/472\n  BBX Build Mode: 2\n*/\nstatic const uint8_t u8g2_font_spleen5x8_mr[] =\n    \"`\\2\\3\\2\\3\\4\\1\\1\\4\\5\\10\\0\\377\\6\\377\\7\\377\\1\\77\\2\\217\\3\\325 \\6\\305\\372\\274\\2!\\10\\305\"\n    \"Zaw(\\7\\42\\12\\305:\\245$JrV\\0#\\15\\305\\332I\\62(\\245$\\31\\224\\62\\0$\\13\\305Z\"\n    \"\\331R\\23\\65e\\214\\0%\\15\\305zI\\224\\24\\263\\60)%!\\0&\\16\\305ZY\\22%\\221\\224$R\\244\"\n    \"\\244\\0'\\7\\305Za\\235\\31(\\10\\305z\\215\\255\\25\\0)\\10\\305:i\\261\\255\\6*\\13\\305\\372X\\24I\"\n    \"C$\\225\\1+\\12\\305\\372h\\30\\15R\\230\\3,\\10\\305\\372\\314a\\226\\1-\\10\\305\\372\\344!'\\1.\\7\"\n    \"\\305\\372\\34s\\0/\\13\\305za\\26fa\\26\\206\\0\\60\\12\\305\\332R%\\261\\224\\42\\35\\61\\10\\305\\372\\231\\330\"\n    \"\\66\\3\\62\\12\\305\\332R\\61\\222\\302!\\6\\63\\12\\305\\332R-M\\242H\\7\\64\\14\\305\\272a\\22%\\321\\220\\205\"\n    \"\\71\\0\\65\\12\\305\\272C\\22\\256a\\262\\3\\66\\12\\305\\332R\\70U\\242H\\7\\67\\13\\305\\272C\\22\\205Y\\61G\"\n    \"\\0\\70\\12\\305\\332RI\\252D\\221\\16\\71\\12\\305\\332R%\\212\\306H\\7:\\10\\305\\372\\264\\34\\317\\1;\\11\\305\"\n    \"\\372\\264\\34\\12\\263\\14<\\11\\305\\372HVL\\313\\0=\\11\\305\\372\\224!\\36r\\20>\\11\\305\\332i\\61\\253#\"\n    \"\\0\\77\\12\\305:R\\61\\253C\\71\\2@\\13\\305\\332R%Q\\22%\\235\\1A\\14\\305\\332R%J\\206$J\"\n    \"\\242\\30B\\12\\305\\272Se\\252D\\311\\16C\\10\\305\\332K\\330:\\3D\\14\\305\\272S%J\\242$Jv\\0\"\n    \"E\\11\\305\\332K\\70\\205\\351\\14F\\12\\305\\332K\\30Na\\16\\1G\\14\\305\\332K\\230(Q\\22E\\63\\0H\"\n    \"\\16\\305\\272Q\\22%C\\22%Q\\22\\305\\0I\\10\\305\\332[\\330\\66\\3J\\11\\305\\332[\\330\\244#\\0K\\14\"\n    \"\\305\\272Q\\22%S%J\\242\\30L\\7\\305\\272a\\327\\31M\\16\\305\\272Q\\62$C\\22%Q\\22\\305\\0N\"\n    \"\\15\\305\\272Q\\242$JEI\\224(\\6O\\14\\305\\332R%J\\242$\\212t\\0P\\13\\305\\272S%J\\246\"\n    \"\\60\\207\\0Q\\14\\305\\332R%J\\242$\\212D\\5R\\13\\305\\272S%J\\246J\\24\\3S\\11\\305\\332K\\252\"\n    \"\\206\\311\\16T\\10\\305\\272\\203\\24v\\7U\\15\\305\\272Q\\22%Q\\22%Q\\64\\3V\\14\\305\\272Q\\22%Q\"\n    \"\\22E\\232\\16W\\16\\305\\272Q\\22%Q\\62$C\\22\\305\\0X\\14\\305\\272Q\\22E\\232T\\211b\\0Y\\14\"\n    \"\\305\\272Q\\22%Q\\64&;\\0Z\\12\\305\\272C\\230\\65\\16\\61\\0[\\10\\305:S\\330\\343\\2\\134\\13\\305\\32\"\n    \"a\\32\\246a\\32&\\0]\\10\\305:c\\237\\26\\0^\\11\\305\\372YR\\313\\311\\0_\\7\\305\\372\\334\\207\\4`\"\n    \"\\7\\305:i\\316\\21a\\12\\305\\372\\240\\32-Q\\64\\3b\\14\\305\\32a\\70U\\242$Jv\\0c\\11\\305\\372\"\n    \"\\340\\22Vg\\0d\\14\\305za\\264DI\\224D\\321\\14e\\13\\305\\372\\340\\22%C\\222\\316\\0f\\12\\305Z\"\n    \"R\\230ma\\35\\1g\\14\\305\\372\\340\\22%Q\\244&\\23\\0h\\14\\305\\32a\\70U\\242$J\\242\\30i\\11\"\n    \"\\305\\372\\71\\42\\26e\\0j\\11\\305\\372\\71\\24\\66i\\0k\\13\\305\\32a))iIT\\6l\\10\\305:a\"\n    \"\\257\\62\\0m\\15\\305\\372X\\224\\14\\311\\220DI\\24\\3n\\14\\305\\372\\330T\\211\\222(\\211b\\0o\\13\\305\\372\"\n    \"\\240T\\211\\222(\\322\\1p\\13\\305\\372\\330T\\211\\222)\\14\\1q\\13\\305\\372\\340\\22%Q\\64V\\0r\\12\\305\"\n    \"\\372\\340\\22%a\\35\\2s\\11\\305\\372\\340\\222\\252\\311\\16t\\11\\305:a\\266\\205U\\31u\\14\\305\\372X\\224D\"\n    \"I\\224D\\321\\14v\\14\\305\\372X\\224DI\\24i:\\0w\\15\\305\\372X\\224D\\311\\220\\14I\\24\\3x\\13\"\n    \"\\305\\372X\\24iR%\\212\\1y\\14\\305\\372X\\224DI\\24\\215\\311\\4z\\12\\305\\372\\330\\20f\\265!\\6{\"\n    \"\\12\\305ZR\\230\\31\\253\\12\\0|\\7\\305Za\\77\\1}\\13\\305\\32j\\30jZ\\30i\\0~\\11\\305\\372\\244\"\n    \"H\\321I\\0\\177\\6\\305\\372\\274\\2\\0\\0\\0\\4\\377\\377\\0\";\n"
  },
  {
    "path": "applications/system/js_app/views/console_view.c",
    "content": "#include \"../js_app_i.h\"\n#include \"console_font.h\"\n\n#define CONSOLE_LINES   8\n#define CONSOLE_CHAR_W  5\n#define LINE_BREAKS_MAX 3\n#define LINE_LEN_MAX    (128 / CONSOLE_CHAR_W)\n\nstruct JsConsoleView {\n    View* view;\n};\n\ntypedef struct {\n    FuriString* text[CONSOLE_LINES];\n} JsConsoleViewModel;\n\nstatic void console_view_draw_callback(Canvas* canvas, void* _model) {\n    JsConsoleViewModel* model = _model;\n\n    canvas_set_color(canvas, ColorBlack);\n    canvas_set_custom_u8g2_font(canvas, u8g2_font_spleen5x8_mr);\n    uint8_t line_h = canvas_current_font_height(canvas);\n\n    for(size_t i = 0; i < CONSOLE_LINES; i++) {\n        canvas_draw_str(canvas, 0, (i + 1) * line_h - 1, furi_string_get_cstr(model->text[i]));\n        if(furi_string_size(model->text[i]) > LINE_LEN_MAX) {\n            canvas_set_font(canvas, FontSecondary);\n            canvas_draw_str(canvas, 128 - 7, (i + 1) * line_h - 1, \"...\");\n            canvas_set_custom_u8g2_font(canvas, u8g2_font_spleen5x8_mr);\n        }\n    }\n}\n\nstatic bool console_view_input_callback(InputEvent* event, void* context) {\n    UNUSED(event);\n    UNUSED(context);\n    return false;\n}\n\nvoid console_view_push_line(JsConsoleView* console_view, const char* text, bool line_trimmed) {\n    with_view_model(\n        console_view->view,\n        JsConsoleViewModel * model,\n        {\n            FuriString* str_temp = model->text[0];\n            for(size_t i = 0; i < CONSOLE_LINES - 1; i++) {\n                model->text[i] = model->text[i + 1];\n            }\n            if(!line_trimmed) {\n                furi_string_printf(str_temp, \"%.*s\", LINE_LEN_MAX, text);\n            } else {\n                // Leave some space for dots\n                furi_string_printf(str_temp, \"%.*s  \", LINE_LEN_MAX - 1, text);\n            }\n            model->text[CONSOLE_LINES - 1] = str_temp;\n        },\n        true);\n}\n\nvoid console_view_print(JsConsoleView* console_view, const char* text) {\n    char line_buf[LINE_LEN_MAX + 1];\n    uint8_t line_buf_cnt = 0;\n    uint8_t utf8_bytes_left = 0;\n    uint8_t line_break_cnt = 0;\n    bool line_trim = false;\n\n    for(size_t i = 0; i < strlen(text); i++) {\n        if(text[i] & 0x80) { // UTF8 or another non-ascii character byte\n            if(utf8_bytes_left > 0) {\n                utf8_bytes_left--;\n                if(utf8_bytes_left == 0) {\n                    line_buf[line_buf_cnt++] = '?';\n                }\n            } else {\n                if((text[i] & 0xE0) == 0xC0) {\n                    utf8_bytes_left = 1;\n                } else if((text[i] & 0xF0) == 0xE0) {\n                    utf8_bytes_left = 2;\n                } else if((text[i] & 0xF8) == 0xF0) {\n                    utf8_bytes_left = 3;\n                } else {\n                    line_buf[line_buf_cnt++] = '?';\n                }\n            }\n        } else {\n            if(utf8_bytes_left > 0) {\n                utf8_bytes_left = 0;\n                line_buf[line_buf_cnt++] = '?';\n                if(line_buf_cnt >= LINE_LEN_MAX) {\n                    line_break_cnt++;\n                    if(line_break_cnt >= LINE_BREAKS_MAX) {\n                        line_trim = true;\n                        break;\n                    }\n                    line_buf[line_buf_cnt] = '\\0';\n                    console_view_push_line(console_view, line_buf, false);\n                    line_buf_cnt = 1;\n                    line_buf[0] = ' ';\n                }\n            }\n\n            if(text[i] == '\\n') {\n                line_buf[line_buf_cnt] = '\\0';\n                line_buf_cnt = 0;\n                console_view_push_line(console_view, line_buf, false);\n            } else {\n                line_buf[line_buf_cnt++] = text[i];\n            }\n\n            if(line_buf_cnt >= LINE_LEN_MAX) {\n                line_break_cnt++;\n                if(line_break_cnt >= LINE_BREAKS_MAX) {\n                    line_trim = true;\n                    break;\n                }\n                line_buf[line_buf_cnt] = '\\0';\n                console_view_push_line(console_view, line_buf, false);\n                line_buf_cnt = 1;\n                line_buf[0] = ' ';\n            }\n        }\n    }\n    if(line_buf_cnt > 0) {\n        line_buf[line_buf_cnt] = '\\0';\n        console_view_push_line(console_view, line_buf, line_trim);\n    }\n}\n\nJsConsoleView* console_view_alloc(void) {\n    JsConsoleView* console_view = malloc(sizeof(JsConsoleView));\n    console_view->view = view_alloc();\n    view_set_draw_callback(console_view->view, console_view_draw_callback);\n    view_set_input_callback(console_view->view, console_view_input_callback);\n    view_allocate_model(console_view->view, ViewModelTypeLocking, sizeof(JsConsoleViewModel));\n\n    with_view_model(\n        console_view->view,\n        JsConsoleViewModel * model,\n        {\n            for(size_t i = 0; i < CONSOLE_LINES; i++) {\n                model->text[i] = furi_string_alloc();\n            }\n        },\n        true);\n    return console_view;\n}\n\nvoid console_view_free(JsConsoleView* console_view) {\n    with_view_model(\n        console_view->view,\n        JsConsoleViewModel * model,\n        {\n            for(size_t i = 0; i < CONSOLE_LINES; i++) {\n                furi_string_free(model->text[i]);\n            }\n        },\n        false);\n    view_free(console_view->view);\n    free(console_view);\n}\n\nView* console_view_get_view(JsConsoleView* console_view) {\n    return console_view->view;\n}\n"
  },
  {
    "path": "applications/system/js_app/views/console_view.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct JsConsoleView JsConsoleView;\n\nJsConsoleView* console_view_alloc(void);\n\nvoid console_view_free(JsConsoleView* console_view);\n\nView* console_view_get_view(JsConsoleView* console_view);\n\nvoid console_view_print(JsConsoleView* console_view, const char* text);\n"
  },
  {
    "path": "applications/system/snake_game/application.fam",
    "content": "App(\n    appid=\"snake_game\",\n    name=\"Snake Game\",\n    apptype=FlipperAppType.EXTERNAL,\n    entry_point=\"snake_game_app\",\n    requires=[\"gui\"],\n    stack_size=1 * 1024,\n    fap_version=\"1.0\",\n    fap_description=\"Classic Snake Game\",\n    fap_icon=\"snake_10px.png\",\n    fap_category=\"Games\",\n)\n"
  },
  {
    "path": "applications/system/snake_game/snake_game.c",
    "content": "#include <furi.h>\n#include <gui/gui.h>\n#include <input/input.h>\n#include <stdlib.h>\n#include <dolphin/dolphin.h>\n#include <notification/notification.h>\n#include <notification/notification_messages.h>\n\ntypedef struct {\n    //    +-----x\n    //    |\n    //    |\n    //    y\n    uint8_t x;\n    uint8_t y;\n} Point;\n\ntypedef enum {\n    GameStateLife,\n\n    // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto\n    // Armanto: While testing the early versions of the game, I noticed it was hard\n    // to control the snake upon getting close to and edge but not crashing — especially\n    // in the highest speed levels. I wanted the highest level to be as fast as I could\n    // possibly make the device \"run,\" but on the other hand, I wanted to be friendly\n    // and help the player manage that level. Otherwise it might not be fun to play. So\n    // I implemented a little delay. A few milliseconds of extra time right before\n    // the player crashes, during which she can still change the directions. And if\n    // she does, the game continues.\n    GameStateLastChance,\n\n    GameStateGameOver,\n} GameState;\n\n// Note: do not change without purpose. Current values are used in smart\n// orthogonality calculation in `snake_game_get_turn_snake`.\ntypedef enum {\n    DirectionUp,\n    DirectionRight,\n    DirectionDown,\n    DirectionLeft,\n} Direction;\n\n#define MAX_SNAKE_LEN 253\n\ntypedef struct {\n    Point points[MAX_SNAKE_LEN];\n    uint16_t len;\n    Direction currentMovement;\n    Direction nextMovement; // if backward of currentMovement, ignore\n    Point fruit;\n    GameState state;\n    FuriMutex* mutex;\n} SnakeState;\n\ntypedef enum {\n    EventTypeTick,\n    EventTypeKey,\n} EventType;\n\ntypedef struct {\n    EventType type;\n    InputEvent input;\n} SnakeEvent;\n\nconst NotificationSequence sequence_fail = {\n    &message_vibro_on,\n\n    &message_note_ds4,\n    &message_delay_10,\n    &message_sound_off,\n    &message_delay_10,\n\n    &message_note_ds4,\n    &message_delay_10,\n    &message_sound_off,\n    &message_delay_10,\n\n    &message_note_ds4,\n    &message_delay_10,\n    &message_sound_off,\n    &message_delay_10,\n\n    &message_vibro_off,\n    NULL,\n};\n\nconst NotificationSequence sequence_eat = {\n    &message_note_c7,\n    &message_delay_50,\n    &message_sound_off,\n    NULL,\n};\n\nstatic void snake_game_render_callback(Canvas* const canvas, void* ctx) {\n    furi_assert(ctx);\n    const SnakeState* snake_state = ctx;\n\n    furi_mutex_acquire(snake_state->mutex, FuriWaitForever);\n\n    // Frame\n    canvas_draw_frame(canvas, 0, 0, 128, 64);\n\n    // Fruit\n    Point f = snake_state->fruit;\n    f.x = f.x * 4 + 1;\n    f.y = f.y * 4 + 1;\n    canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2);\n\n    // Snake\n    for(uint16_t i = 0; i < snake_state->len; i++) {\n        Point p = snake_state->points[i];\n        p.x = p.x * 4 + 2;\n        p.y = p.y * 4 + 2;\n        canvas_draw_box(canvas, p.x, p.y, 4, 4);\n    }\n\n    // Game Over banner\n    if(snake_state->state == GameStateGameOver) {\n        // Screen is 128x64 px\n        canvas_set_color(canvas, ColorWhite);\n        canvas_draw_box(canvas, 34, 20, 62, 24);\n\n        canvas_set_color(canvas, ColorBlack);\n        canvas_draw_frame(canvas, 34, 20, 62, 24);\n\n        canvas_set_font(canvas, FontPrimary);\n        canvas_draw_str(canvas, 37, 31, \"Game Over\");\n\n        canvas_set_font(canvas, FontSecondary);\n        char buffer[12];\n        snprintf(buffer, sizeof(buffer), \"Score: %u\", snake_state->len - 7U);\n        canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);\n    }\n\n    furi_mutex_release(snake_state->mutex);\n}\n\nstatic void snake_game_input_callback(InputEvent* input_event, void* context) {\n    furi_assert(context);\n    FuriMessageQueue* event_queue = context;\n\n    SnakeEvent event = {.type = EventTypeKey, .input = *input_event};\n    furi_message_queue_put(event_queue, &event, FuriWaitForever);\n}\n\nstatic void snake_game_update_timer_callback(void* context) {\n    furi_assert(context);\n    FuriMessageQueue* event_queue = context;\n\n    SnakeEvent event = {.type = EventTypeTick};\n    furi_message_queue_put(event_queue, &event, 0);\n}\n\nstatic void snake_game_init_game(SnakeState* const snake_state) {\n    Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}};\n    memcpy(snake_state->points, p, sizeof(p)); //-V1086\n\n    snake_state->len = 7;\n\n    snake_state->currentMovement = DirectionRight;\n\n    snake_state->nextMovement = DirectionRight;\n\n    Point f = {18, 6};\n    snake_state->fruit = f;\n\n    snake_state->state = GameStateLife;\n}\n\nstatic Point snake_game_get_new_fruit(SnakeState const* const snake_state) {\n    // 1 bit for each point on the playing field where the snake can turn\n    // and where the fruit can appear\n    uint16_t buffer[8];\n    memset(buffer, 0, sizeof(buffer));\n    uint8_t empty = 8 * 16;\n\n    for(uint16_t i = 0; i < snake_state->len; i++) {\n        Point p = snake_state->points[i];\n\n        if(p.x % 2 != 0 || p.y % 2 != 0) {\n            continue;\n        }\n        p.x /= 2;\n        p.y /= 2;\n\n        buffer[p.y] |= 1 << p.x;\n        empty--;\n    }\n    // Bit set if snake use that playing field\n\n    uint16_t newFruit = rand() % empty;\n\n    // Skip random number of _empty_ fields\n    for(uint8_t y = 0; y < 8; y++) {\n        for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) {\n            if((buffer[y] & mask) == 0) {\n                if(newFruit == 0) {\n                    Point p = {\n                        .x = x * 2,\n                        .y = y * 2,\n                    };\n                    return p;\n                }\n                newFruit--;\n            }\n        }\n    }\n    // We will never be here\n    Point p = {0, 0};\n    return p;\n}\n\nstatic bool snake_game_collision_with_frame(Point const next_step) {\n    // if x == 0 && currentMovement == left then x - 1 == 255 ,\n    // so check only x > right border\n    return next_step.x > 30 || next_step.y > 14;\n}\n\nstatic bool\n    snake_game_collision_with_tail(SnakeState const* const snake_state, Point const next_step) {\n    for(uint16_t i = 0; i < snake_state->len; i++) {\n        Point p = snake_state->points[i];\n        if(p.x == next_step.x && p.y == next_step.y) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic Direction snake_game_get_turn_snake(SnakeState const* const snake_state) {\n    // Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality.\n    bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1;\n    return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement;\n}\n\nstatic Point snake_game_get_next_step(SnakeState const* const snake_state) {\n    Point next_step = snake_state->points[0];\n    switch(snake_state->currentMovement) {\n    // +-----x\n    // |\n    // |\n    // y\n    case DirectionUp:\n        next_step.y--;\n        break;\n    case DirectionRight:\n        next_step.x++;\n        break;\n    case DirectionDown:\n        next_step.y++;\n        break;\n    case DirectionLeft:\n        next_step.x--;\n        break;\n    }\n    return next_step;\n}\n\nstatic void snake_game_move_snake(SnakeState* const snake_state, Point const next_step) {\n    memmove(snake_state->points + 1, snake_state->points, snake_state->len * sizeof(Point));\n    snake_state->points[0] = next_step;\n}\n\nstatic void\n    snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) {\n    if(snake_state->state == GameStateGameOver) {\n        return;\n    }\n\n    bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0);\n    if(can_turn) {\n        snake_state->currentMovement = snake_game_get_turn_snake(snake_state);\n    }\n\n    Point next_step = snake_game_get_next_step(snake_state);\n\n    bool crush = snake_game_collision_with_frame(next_step);\n    if(crush) {\n        if(snake_state->state == GameStateLife) {\n            snake_state->state = GameStateLastChance;\n            return;\n        } else if(snake_state->state == GameStateLastChance) {\n            snake_state->state = GameStateGameOver;\n            notification_message_block(notification, &sequence_fail);\n            return;\n        }\n    } else {\n        if(snake_state->state == GameStateLastChance) {\n            snake_state->state = GameStateLife;\n        }\n    }\n\n    crush = snake_game_collision_with_tail(snake_state, next_step);\n    if(crush) {\n        snake_state->state = GameStateGameOver;\n        notification_message_block(notification, &sequence_fail);\n        return;\n    }\n\n    bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y);\n    if(eatFruit) {\n        snake_state->len++;\n        if(snake_state->len >= MAX_SNAKE_LEN) {\n            snake_state->state = GameStateGameOver;\n            notification_message_block(notification, &sequence_fail);\n            return;\n        }\n    }\n\n    snake_game_move_snake(snake_state, next_step);\n\n    if(eatFruit) {\n        snake_state->fruit = snake_game_get_new_fruit(snake_state);\n        notification_message(notification, &sequence_eat);\n    }\n}\n\nint32_t snake_game_app(void* p) {\n    UNUSED(p);\n\n    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));\n\n    SnakeState* snake_state = malloc(sizeof(SnakeState));\n    snake_game_init_game(snake_state);\n\n    snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    ViewPort* view_port = view_port_alloc();\n    view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state);\n    view_port_input_callback_set(view_port, snake_game_input_callback, event_queue);\n\n    FuriTimer* timer =\n        furi_timer_alloc(snake_game_update_timer_callback, FuriTimerTypePeriodic, event_queue);\n    furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);\n\n    // Open GUI and register view_port\n    Gui* gui = furi_record_open(RECORD_GUI);\n    gui_add_view_port(gui, view_port, GuiLayerFullscreen);\n    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);\n\n    notification_message_block(notification, &sequence_display_backlight_enforce_on);\n\n    dolphin_deed(DolphinDeedPluginGameStart);\n\n    SnakeEvent event;\n    for(bool processing = true; processing;) {\n        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);\n\n        furi_mutex_acquire(snake_state->mutex, FuriWaitForever);\n\n        if(event_status == FuriStatusOk) {\n            // press events\n            if(event.type == EventTypeKey) {\n                if(event.input.type == InputTypePress) {\n                    switch(event.input.key) {\n                    case InputKeyUp:\n                        snake_state->nextMovement = DirectionUp;\n                        break;\n                    case InputKeyDown:\n                        snake_state->nextMovement = DirectionDown;\n                        break;\n                    case InputKeyRight:\n                        snake_state->nextMovement = DirectionRight;\n                        break;\n                    case InputKeyLeft:\n                        snake_state->nextMovement = DirectionLeft;\n                        break;\n                    case InputKeyOk:\n                        if(snake_state->state == GameStateGameOver) {\n                            snake_game_init_game(snake_state);\n                        }\n                        break;\n                    case InputKeyBack:\n                        processing = false;\n                        break;\n                    default:\n                        break;\n                    }\n                }\n            } else if(event.type == EventTypeTick) {\n                snake_game_process_game_step(snake_state, notification);\n            }\n        } else {\n            // event timeout\n        }\n\n        furi_mutex_release(snake_state->mutex);\n        view_port_update(view_port);\n    }\n\n    // Return backlight to normal state\n    notification_message(notification, &sequence_display_backlight_enforce_auto);\n\n    furi_timer_free(timer);\n    view_port_enabled_set(view_port, false);\n    gui_remove_view_port(gui, view_port);\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_NOTIFICATION);\n    view_port_free(view_port);\n    furi_message_queue_free(event_queue);\n    furi_mutex_free(snake_state->mutex);\n    free(snake_state);\n\n    return 0;\n}\n\n// Screen is 128x64 px\n// (4 + 4) * 16 - 4 + 2 + 2border == 128\n// (4 + 4) * 8 - 4 + 2 + 2border == 64\n// Game field from point{x:  0, y: 0} to point{x: 30, y: 14}.\n// The snake turns only in even cells - intersections.\n// ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎\n// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎\n// └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘\n"
  },
  {
    "path": "applications/system/updater/application.fam",
    "content": "App(\n    appid=\"updater\",\n    name=\"UpdaterSrv\",\n    apptype=FlipperAppType.SERVICE,\n    cdefines=[\"SRV_UPDATER\"],\n    requires=[\n        \"gui\",\n        \"storage\",\n    ],\n    conflicts=[\"desktop\"],\n    entry_point=\"updater_srv\",\n    stack_size=2 * 1024,\n    order=130,\n)\n\nApp(\n    appid=\"updater_app\",\n    name=\"UpdaterApp\",\n    apptype=FlipperAppType.SYSTEM,\n    cdefines=[\"APP_UPDATER\"],\n    requires=[\n        \"gui\",\n        \"storage\",\n        \"bt\",\n    ],\n    conflicts=[\"updater\"],\n    provides=[\"updater_start\"],\n    entry_point=\"updater_srv\",\n    stack_size=2 * 1024,\n    order=20,\n)\n\nApp(\n    appid=\"updater_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"updater_on_system_start\",\n    requires=[\"updater_app\"],\n    order=90,\n)\n"
  },
  {
    "path": "applications/system/updater/cli/updater_cli.c",
    "content": "\n#include <furi.h>\n#include <furi_hal.h>\n#include <toolbox/cli/cli_command.h>\n#include <cli/cli_main_commands.h>\n#include <storage/storage.h>\n#include <loader/loader.h>\n#include <toolbox/path.h>\n#include <toolbox/tar/tar_archive.h>\n#include <toolbox/args.h>\n#include <toolbox/pipe.h>\n#include <update_util/update_manifest.h>\n#include <update_util/int_backup.h>\n#include <update_util/update_operation.h>\n\ntypedef void (*cmd_handler)(FuriString* args);\ntypedef struct {\n    const char* command;\n    const cmd_handler handler;\n} CliSubcommand;\n\nstatic void updater_cli_install(FuriString* manifest_path) {\n    printf(\"Verifying update package at '%s'\\r\\n\", furi_string_get_cstr(manifest_path));\n\n    UpdatePrepareResult result = update_operation_prepare(furi_string_get_cstr(manifest_path));\n    if(result != UpdatePrepareResultOK) {\n        printf(\n            \"Error: %s. Stopping update.\\r\\n\",\n            update_operation_describe_preparation_result(result));\n        return;\n    }\n    printf(\"OK.\\r\\nRestarting to apply update. BRB\\r\\n\");\n    furi_delay_ms(100);\n    furi_hal_power_reset();\n}\n\nstatic void updater_cli_backup(FuriString* args) {\n    printf(\"Backup /int to '%s'\\r\\n\", furi_string_get_cstr(args));\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool success = int_backup_create(storage, furi_string_get_cstr(args));\n    furi_record_close(RECORD_STORAGE);\n    printf(\"Result: %s\\r\\n\", success ? \"OK\" : \"FAIL\");\n}\n\nstatic void updater_cli_restore(FuriString* args) {\n    printf(\"Restore /int from '%s'\\r\\n\", furi_string_get_cstr(args));\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    bool success = int_backup_unpack(storage, furi_string_get_cstr(args));\n    furi_record_close(RECORD_STORAGE);\n    printf(\"Result: %s\\r\\n\", success ? \"OK\" : \"FAIL\");\n}\n\nstatic void updater_cli_help(FuriString* args) {\n    UNUSED(args);\n    printf(\"Commands:\\r\\n\"\n           \"\\tinstall /ext/path/to/update.fuf - verify & apply update package\\r\\n\"\n           \"\\tbackup /ext/path/to/backup.tar - create internal storage backup\\r\\n\"\n           \"\\trestore /ext/path/to/backup.tar - restore internal storage backup\\r\\n\");\n}\n\nstatic const CliSubcommand update_cli_subcommands[] = {\n    {.command = \"install\", .handler = updater_cli_install},\n    {.command = \"backup\", .handler = updater_cli_backup},\n    {.command = \"restore\", .handler = updater_cli_restore},\n    {.command = \"help\", .handler = updater_cli_help},\n};\n\nstatic void updater_cli_ep(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(context);\n    FuriString* subcommand;\n    subcommand = furi_string_alloc();\n    if(!args_read_string_and_trim(args, subcommand) || furi_string_empty(args)) {\n        updater_cli_help(args);\n        furi_string_free(subcommand);\n        return;\n    }\n    for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) {\n        const CliSubcommand* subcmd_def = &update_cli_subcommands[idx];\n        if(furi_string_cmp_str(subcommand, subcmd_def->command) == 0) {\n            subcmd_def->handler(args);\n            furi_string_free(subcommand);\n            return;\n        }\n    }\n    furi_string_free(subcommand);\n    updater_cli_help(args);\n}\n\nstatic void updater_start_app(void* context, uint32_t arg) {\n    UNUSED(context);\n    UNUSED(arg);\n\n    FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode();\n    if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) {\n        return;\n    }\n\n    /* We need to spawn a separate thread, because these callbacks are executed \n     * inside loader process, at startup. \n     * So, accessing its record would cause a deadlock \n     */\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    loader_start(loader, \"UpdaterApp\", NULL, NULL);\n    furi_record_close(RECORD_LOADER);\n}\n\nvoid updater_on_system_start(void) {\n#ifdef SRV_CLI\n    CliRegistry* registry = furi_record_open(RECORD_CLI);\n    cli_registry_add_command(registry, \"update\", CliCommandFlagDefault, updater_cli_ep, NULL);\n    furi_record_close(RECORD_CLI);\n#else\n    UNUSED(updater_cli_ep);\n#endif\n#ifndef FURI_RAM_EXEC\n    furi_timer_pending_callback(updater_start_app, NULL, 0);\n#else\n    UNUSED(updater_start_app);\n#endif\n}\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene.c",
    "content": "#include \"updater_scene.h\"\n\n// Generate scene on_enter handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,\nvoid (*const updater_on_enter_handlers[])(void*) = {\n#include \"updater_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_event handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,\nbool (*const updater_on_event_handlers[])(void* context, SceneManagerEvent event) = {\n#include \"updater_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers array\n#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,\nvoid (*const updater_on_exit_handlers[])(void* context) = {\n#include \"updater_scene_config.h\"\n};\n#undef ADD_SCENE\n\n// Initialize scene handlers configuration structure\nconst SceneManagerHandlers updater_scene_handlers = {\n    .on_enter_handlers = updater_on_enter_handlers,\n    .on_event_handlers = updater_on_event_handlers,\n    .on_exit_handlers = updater_on_exit_handlers,\n    .scene_num = UpdaterSceneNum,\n};\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene.h",
    "content": "#pragma once\n\n#include <gui/scene_manager.h>\n\n// Generate scene id and total number\n#define ADD_SCENE(prefix, name, id) UpdaterScene##id,\ntypedef enum {\n#include \"updater_scene_config.h\"\n    UpdaterSceneNum,\n} UpdaterScene;\n#undef ADD_SCENE\n\nextern const SceneManagerHandlers updater_scene_handlers;\n\n// Generate scene on_enter handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);\n#include \"updater_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_event handlers declaration\n#define ADD_SCENE(prefix, name, id) \\\n    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);\n#include \"updater_scene_config.h\"\n#undef ADD_SCENE\n\n// Generate scene on_exit handlers declaration\n#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);\n#include \"updater_scene_config.h\"\n#undef ADD_SCENE\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene_config.h",
    "content": "ADD_SCENE(updater, main, Main)\n#ifndef FURI_RAM_EXEC\nADD_SCENE(updater, loadcfg, LoadCfg)\nADD_SCENE(updater, error, Error)\n#endif\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene_error.c",
    "content": "#include \"../updater_i.h\"\n#include \"updater_scene.h\"\n#include <update_util/update_operation.h>\n\nvoid updater_scene_error_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    Updater* updater = context;\n    if(type != InputTypeShort) {\n        return;\n    }\n\n    if(result == GuiButtonTypeLeft) {\n        view_dispatcher_send_custom_event(\n            updater->view_dispatcher, UpdaterCustomEventCancelUpdate);\n    }\n}\n\nvoid updater_scene_error_on_enter(void* context) {\n    Updater* updater = (Updater*)context;\n\n    widget_add_button_element(\n        updater->widget, GuiButtonTypeLeft, \"Exit\", updater_scene_error_callback, updater);\n\n    widget_add_string_multiline_element(\n        updater->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, \"Error\");\n\n    widget_add_string_multiline_element(\n        updater->widget,\n        64,\n        33,\n        AlignCenter,\n        AlignCenter,\n        FontPrimary,\n        update_operation_describe_preparation_result(updater->preparation_result));\n\n    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewWidget);\n}\n\nbool updater_scene_error_on_event(void* context, SceneManagerEvent event) {\n    Updater* updater = (Updater*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        view_dispatcher_stop(updater->view_dispatcher);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case UpdaterCustomEventCancelUpdate:\n            view_dispatcher_stop(updater->view_dispatcher);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid updater_scene_error_on_exit(void* context) {\n    furi_assert(context);\n    Updater* updater = (Updater*)context;\n\n    widget_reset(updater->widget);\n\n    if(updater->loaded_manifest) {\n        update_manifest_free(updater->loaded_manifest);\n    }\n}\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene_loadcfg.c",
    "content": "#include \"../updater_i.h\"\n#include \"updater_scene.h\"\n\n#include <update_util/update_operation.h>\n#include <furi_hal.h>\n\nvoid updater_scene_loadcfg_apply_callback(GuiButtonType result, InputType type, void* context) {\n    furi_assert(context);\n    Updater* updater = context;\n    if(type != InputTypeShort) {\n        return;\n    }\n\n    if(result == GuiButtonTypeRight) {\n        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);\n    } else if(result == GuiButtonTypeLeft) {\n        view_dispatcher_send_custom_event(\n            updater->view_dispatcher, UpdaterCustomEventCancelUpdate);\n    }\n}\n\nvoid updater_scene_loadcfg_on_enter(void* context) {\n    Updater* updater = (Updater*)context;\n    UpdateManifest* loaded_manifest = updater->loaded_manifest = update_manifest_alloc();\n\n    if(update_manifest_init(loaded_manifest, furi_string_get_cstr(updater->startup_arg))) {\n        widget_add_string_element(\n            updater->widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, \"Update\");\n\n        widget_add_text_box_element(\n            updater->widget,\n            5,\n            20,\n            118,\n            32,\n            AlignCenter,\n            AlignCenter,\n            furi_string_get_cstr(loaded_manifest->version),\n            true);\n\n        widget_add_button_element(\n            updater->widget,\n            GuiButtonTypeRight,\n            \"Install\",\n            updater_scene_loadcfg_apply_callback,\n            updater);\n    } else {\n        widget_add_string_element(\n            updater->widget, 64, 24, AlignCenter, AlignCenter, FontPrimary, \"Invalid manifest\");\n    }\n\n    widget_add_button_element(\n        updater->widget,\n        GuiButtonTypeLeft,\n        \"Cancel\",\n        updater_scene_loadcfg_apply_callback,\n        updater);\n\n    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewWidget);\n}\n\nbool updater_scene_loadcfg_on_event(void* context, SceneManagerEvent event) {\n    Updater* updater = (Updater*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeBack) {\n        view_dispatcher_stop(updater->view_dispatcher);\n        consumed = true;\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case UpdaterCustomEventStartUpdate:\n            updater->preparation_result =\n                update_operation_prepare(furi_string_get_cstr(updater->startup_arg));\n            if(updater->preparation_result == UpdatePrepareResultOK) {\n                furi_hal_power_reset();\n            } else {\n#ifndef FURI_RAM_EXEC\n                scene_manager_next_scene(updater->scene_manager, UpdaterSceneError);\n#endif\n            }\n            consumed = true;\n            break;\n        case UpdaterCustomEventCancelUpdate:\n            view_dispatcher_stop(updater->view_dispatcher);\n            consumed = true;\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid updater_scene_loadcfg_on_exit(void* context) {\n    furi_assert(context);\n    Updater* updater = (Updater*)context;\n\n    widget_reset(updater->widget);\n\n    if(updater->loaded_manifest) {\n        update_manifest_free(updater->loaded_manifest);\n    }\n}\n"
  },
  {
    "path": "applications/system/updater/scenes/updater_scene_main.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include <applications.h>\n#include <storage/storage.h>\n\n#include \"../updater_i.h\"\n#include \"../views/updater_main.h\"\n#include \"updater_scene.h\"\n\nstatic void sd_mount_callback(const void* message, void* context) {\n    Updater* updater = context;\n    const StorageEvent* new_event = message;\n\n    switch(new_event->type) {\n    case StorageEventTypeCardMount:\n        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);\n        break;\n    case StorageEventTypeCardUnmount:\n        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventSdUnmounted);\n        break;\n    default:\n        break;\n    }\n}\n\nvoid updater_scene_main_on_enter(void* context) {\n    Updater* updater = (Updater*)context;\n    notification_message(updater->notification, &sequence_display_backlight_enforce_on);\n    UpdaterMainView* main_view = updater->main_view;\n\n    FuriPubSubSubscription* sub =\n        furi_pubsub_subscribe(storage_get_pubsub(updater->storage), &sd_mount_callback, updater);\n    updater_main_set_storage_pubsub(main_view, sub);\n\n    /* FIXME: there's a misbehavior in storage subsystem. If we produce heavy load on it before it\n    * fires an SD card event, it'll never do that until the load is lifted. Meanwhile SD card icon\n    * will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this\n    * should remain commented out. */\n    // If (somehow) we started after SD card is mounted, initiate update immediately\n    if(storage_sd_status(updater->storage) == FSE_OK) {\n        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);\n    }\n\n    updater_main_set_view_dispatcher(main_view, updater->view_dispatcher);\n    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain);\n}\n\nstatic void updater_scene_cancel_update(void) {\n    update_operation_disarm();\n    furi_hal_power_reset();\n}\n\nbool updater_scene_main_on_event(void* context, SceneManagerEvent event) {\n    Updater* updater = (Updater*)context;\n    bool consumed = false;\n\n    if(event.type == SceneManagerEventTypeTick) {\n        if(!update_task_is_running(updater->update_task)) {\n            if(updater->idle_ticks++ >= (UPDATE_DELAY_OPERATION_ERROR / UPDATER_APP_TICK)) {\n                updater_scene_cancel_update();\n            }\n        } else {\n            updater->idle_ticks = 0;\n        }\n    } else if(event.type == SceneManagerEventTypeCustom) {\n        switch(event.event) {\n        case UpdaterCustomEventStartUpdate:\n        case UpdaterCustomEventRetryUpdate:\n            if(!update_task_is_running(updater->update_task) &&\n               (update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted))\n                update_task_start(updater->update_task);\n            consumed = true;\n            break;\n\n        case UpdaterCustomEventCancelUpdate:\n            if(!update_task_is_running(updater->update_task)) {\n                updater_scene_cancel_update();\n            }\n            consumed = true;\n            break;\n\n        case UpdaterCustomEventSdUnmounted:\n            // TODO FL-3499: error out, stop worker (it's probably dead actually)\n            break;\n        default:\n            break;\n        }\n    }\n\n    return consumed;\n}\n\nvoid updater_scene_main_on_exit(void* context) {\n    Updater* updater = (Updater*)context;\n\n    notification_message(updater->notification, &sequence_display_backlight_enforce_auto);\n    furi_pubsub_unsubscribe(\n        storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view));\n\n    scene_manager_set_scene_state(updater->scene_manager, UpdaterSceneMain, 0);\n}\n"
  },
  {
    "path": "applications/system/updater/updater.c",
    "content": "#include \"scenes/updater_scene.h\"\n#include \"updater_i.h\"\n\n#include <storage/storage.h>\n#include <gui/view_dispatcher.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <stdint.h>\n\nstatic bool updater_custom_event_callback(void* context, uint32_t event) {\n    furi_assert(context);\n    Updater* updater = (Updater*)context;\n    return scene_manager_handle_custom_event(updater->scene_manager, event);\n}\n\nstatic void updater_tick_event_callback(void* context) {\n    furi_assert(context);\n    Updater* app = context;\n    scene_manager_handle_tick_event(app->scene_manager);\n}\n\nstatic bool updater_back_event_callback(void* context) {\n    furi_assert(context);\n    Updater* updater = (Updater*)context;\n    return scene_manager_handle_back_event(updater->scene_manager);\n}\n\nstatic void\n    status_update_cb(const char* message, const uint8_t progress, bool failed, void* context) {\n    UpdaterMainView* main_view = context;\n    updater_main_model_set_state(main_view, message, progress, failed);\n}\n\nUpdater* updater_alloc(const char* arg) {\n    Updater* updater = malloc(sizeof(Updater));\n    if(arg && strlen(arg)) {\n        updater->startup_arg = furi_string_alloc_set(arg);\n        furi_string_replace(updater->startup_arg, ANY_PATH(\"\"), EXT_PATH(\"\"));\n    } else {\n        updater->startup_arg = furi_string_alloc();\n    }\n\n    updater->storage = furi_record_open(RECORD_STORAGE);\n    updater->notification = furi_record_open(RECORD_NOTIFICATION);\n\n    updater->gui = furi_record_open(RECORD_GUI);\n    updater->view_dispatcher = view_dispatcher_alloc();\n    updater->scene_manager = scene_manager_alloc(&updater_scene_handlers, updater);\n\n    view_dispatcher_set_event_callback_context(updater->view_dispatcher, updater);\n    view_dispatcher_set_custom_event_callback(\n        updater->view_dispatcher, updater_custom_event_callback);\n    view_dispatcher_set_navigation_event_callback(\n        updater->view_dispatcher, updater_back_event_callback);\n    view_dispatcher_set_tick_event_callback(\n        updater->view_dispatcher, updater_tick_event_callback, UPDATER_APP_TICK);\n\n    view_dispatcher_attach_to_gui(\n        updater->view_dispatcher, updater->gui, ViewDispatcherTypeFullscreen);\n\n    updater->main_view = updater_main_alloc();\n    view_dispatcher_add_view(\n        updater->view_dispatcher, UpdaterViewMain, updater_main_get_view(updater->main_view));\n\n#ifndef FURI_RAM_EXEC\n    updater->widget = widget_alloc();\n    view_dispatcher_add_view(\n        updater->view_dispatcher, UpdaterViewWidget, widget_get_view(updater->widget));\n#endif\n\n#ifdef FURI_RAM_EXEC\n    if(true) {\n#else\n    FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode();\n    if(!arg && ((boot_mode == FuriHalRtcBootModePreUpdate) ||\n                (boot_mode == FuriHalRtcBootModePostUpdate))) {\n#endif\n        updater->update_task = update_task_alloc();\n        update_task_set_progress_cb(updater->update_task, status_update_cb, updater->main_view);\n\n        scene_manager_next_scene(updater->scene_manager, UpdaterSceneMain);\n    } else {\n#ifndef FURI_RAM_EXEC\n        scene_manager_next_scene(updater->scene_manager, UpdaterSceneLoadCfg);\n#endif\n    }\n\n    return updater;\n}\n\nvoid updater_free(Updater* updater) {\n    furi_assert(updater);\n\n    furi_string_free(updater->startup_arg);\n    if(updater->update_task) {\n        update_task_set_progress_cb(updater->update_task, NULL, NULL);\n        update_task_free(updater->update_task);\n    }\n\n    view_dispatcher_remove_view(updater->view_dispatcher, UpdaterViewMain);\n    updater_main_free(updater->main_view);\n\n#ifndef FURI_RAM_EXEC\n    view_dispatcher_remove_view(updater->view_dispatcher, UpdaterViewWidget);\n    widget_free(updater->widget);\n#endif\n\n    view_dispatcher_free(updater->view_dispatcher);\n    scene_manager_free(updater->scene_manager);\n\n    furi_record_close(RECORD_GUI);\n    furi_record_close(RECORD_STORAGE);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    free(updater);\n}\n\nint32_t updater_srv(void* p) {\n    const char* cfgpath = p;\n\n    Updater* updater = updater_alloc(cfgpath);\n    view_dispatcher_run(updater->view_dispatcher);\n    updater_free(updater);\n\n    return 0;\n}\n"
  },
  {
    "path": "applications/system/updater/updater_i.h",
    "content": "#pragma once\n\n#include \"views/updater_main.h\"\n#include \"util/update_task.h\"\n\n#include <furi.h>\n#include <gui/gui.h>\n#include <gui/view_stack.h>\n#include <gui/view_dispatcher.h>\n#include <gui/modules/popup.h>\n#include <gui/scene_manager.h>\n#include <gui/modules/widget.h>\n#include <storage/storage.h>\n#include <notification/notification_app.h>\n#include <update_util/update_operation.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define UPDATER_APP_TICK 500\n\ntypedef enum {\n    UpdaterViewMain,\n    UpdaterViewWidget,\n} UpdaterViewEnum;\n\ntypedef enum {\n    UpdaterCustomEventUnknown,\n    UpdaterCustomEventStartUpdate,\n    UpdaterCustomEventRetryUpdate,\n    UpdaterCustomEventCancelUpdate,\n    UpdaterCustomEventSdUnmounted,\n} UpdaterCustomEvent;\n\ntypedef struct {\n    // GUI\n    Gui* gui;\n    NotificationApp* notification;\n    SceneManager* scene_manager;\n    ViewDispatcher* view_dispatcher;\n    Storage* storage;\n\n    UpdaterMainView* main_view;\n\n    UpdateManifest* loaded_manifest;\n    UpdatePrepareResult preparation_result;\n\n    UpdateTask* update_task;\n    Widget* widget;\n    FuriString* startup_arg;\n    int32_t idle_ticks;\n} Updater;\n\nUpdater* updater_alloc(const char* arg);\n\nvoid updater_free(Updater* updater);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/updater/util/update_task.c",
    "content": "#include \"update_task.h\"\n#include \"update_task_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <toolbox/path.h>\n#include <update_util/dfu_file.h>\n#include <update_util/update_operation.h>\n\n#define TAG \"UpdWorker\"\n\nstatic const char* update_task_stage_descr[] = {\n    [UpdateTaskStageProgress] = \"...\",\n    [UpdateTaskStageReadManifest] = \"Loading update manifest\",\n    [UpdateTaskStageValidateDFUImage] = \"Checking DFU file\",\n    [UpdateTaskStageFlashWrite] = \"Writing flash\",\n    [UpdateTaskStageFlashValidate] = \"Validating flash\",\n    [UpdateTaskStageRadioImageValidate] = \"Checking radio FW\",\n    [UpdateTaskStageRadioErase] = \"Uninstalling radio FW\",\n    [UpdateTaskStageRadioWrite] = \"Writing radio FW\",\n    [UpdateTaskStageRadioInstall] = \"Installing radio FW\",\n    [UpdateTaskStageRadioBusy] = \"Core 2 busy\",\n    [UpdateTaskStageOBValidation] = \"Validating opt. bytes\",\n    [UpdateTaskStageIntBackup] = \"Backing up configuration\",\n    [UpdateTaskStageIntRestore] = \"Restoring configuration\",\n    [UpdateTaskStageResourcesFileCleanup] = \"Cleaning up files\",\n    [UpdateTaskStageResourcesDirCleanup] = \"Cleaning up directories\",\n    [UpdateTaskStageResourcesFileUnpack] = \"Extracting resources\",\n    [UpdateTaskStageSplashscreenInstall] = \"Installing splashscreen\",\n    [UpdateTaskStageCompleted] = \"Restarting...\",\n    [UpdateTaskStageError] = \"Error\",\n    [UpdateTaskStageOBError] = \"OB, report\",\n};\n\nstatic const struct {\n    UpdateTaskStage stage;\n    uint8_t percent_min, percent_max;\n    const char* descr;\n} update_task_error_detail[] = {\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 0,\n        .percent_max = 13,\n        .descr = \"Wrong Updater HW\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 14,\n        .percent_max = 20,\n        .descr = \"Manifest pointer error\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 21,\n        .percent_max = 30,\n        .descr = \"Manifest load error\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 31,\n        .percent_max = 40,\n        .descr = \"Wrong package version\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 41,\n        .percent_max = 50,\n        .descr = \"HW Target mismatch\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 51,\n        .percent_max = 60,\n        .descr = \"No DFU file\",\n    },\n    {\n        .stage = UpdateTaskStageReadManifest,\n        .percent_min = 61,\n        .percent_max = 80,\n        .descr = \"No Radio file\",\n    },\n#ifndef FURI_RAM_EXEC\n    {\n        .stage = UpdateTaskStageIntBackup,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"FS R/W error\",\n    },\n#else\n    {\n        .stage = UpdateTaskStageRadioImageValidate,\n        .percent_min = 0,\n        .percent_max = 98,\n        .descr = \"FS Read error\",\n    },\n    {\n        .stage = UpdateTaskStageRadioImageValidate,\n        .percent_min = 99,\n        .percent_max = 100,\n        .descr = \"CRC mismatch\",\n    },\n    {\n        .stage = UpdateTaskStageRadioErase,\n        .percent_min = 0,\n        .percent_max = 30,\n        .descr = \"Stack remove: cmd error\",\n    },\n    {\n        .stage = UpdateTaskStageRadioErase,\n        .percent_min = 31,\n        .percent_max = 100,\n        .descr = \"Stack remove: wait failed\",\n    },\n    {\n        .stage = UpdateTaskStageRadioWrite,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"Stack write: error\",\n    },\n    {\n        .stage = UpdateTaskStageRadioInstall,\n        .percent_min = 0,\n        .percent_max = 10,\n        .descr = \"Stack install: cmd error\",\n    },\n    {\n        .stage = UpdateTaskStageRadioInstall,\n        .percent_min = 11,\n        .percent_max = 100,\n        .descr = \"Stack install: wait failed\",\n    },\n    {\n        .stage = UpdateTaskStageRadioBusy,\n        .percent_min = 0,\n        .percent_max = 10,\n        .descr = \"Failed to start C2\",\n    },\n    {\n        .stage = UpdateTaskStageRadioBusy,\n        .percent_min = 11,\n        .percent_max = 20,\n        .descr = \"C2 FUS switch failed\",\n    },\n    {\n        .stage = UpdateTaskStageRadioBusy,\n        .percent_min = 21,\n        .percent_max = 30,\n        .descr = \"FUS operation failed\",\n    },\n    {\n        .stage = UpdateTaskStageRadioBusy,\n        .percent_min = 31,\n        .percent_max = 100,\n        .descr = \"C2 Stach switch failed\",\n    },\n    {\n        .stage = UpdateTaskStageOBValidation,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"Uncorr. value mismatch\",\n    },\n    {\n        .stage = UpdateTaskStageValidateDFUImage,\n        .percent_min = 0,\n        .percent_max = 1,\n        .descr = \"Failed to open DFU file\",\n    },\n    {\n        .stage = UpdateTaskStageValidateDFUImage,\n        .percent_min = 1,\n        .percent_max = 97,\n        .descr = \"DFU file read error\",\n    },\n    {\n        .stage = UpdateTaskStageValidateDFUImage,\n        .percent_min = 98,\n        .percent_max = 100,\n        .descr = \"DFU file CRC mismatch\",\n    },\n    {\n        .stage = UpdateTaskStageFlashWrite,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"Flash write error\",\n    },\n    {\n        .stage = UpdateTaskStageFlashValidate,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"Flash compare error\",\n    },\n#endif\n#ifndef FURI_RAM_EXEC\n    {\n        .stage = UpdateTaskStageIntRestore,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"SD card I/O error\",\n    },\n    {\n        .stage = UpdateTaskStageResourcesFileCleanup,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"SD card I/O error\",\n    },\n    {\n        .stage = UpdateTaskStageResourcesDirCleanup,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"SD card I/O error\",\n    },\n    {\n        .stage = UpdateTaskStageResourcesFileUnpack,\n        .percent_min = 0,\n        .percent_max = 100,\n        .descr = \"SD card I/O error\",\n    },\n#endif\n};\n\nstatic const char* update_task_get_error_message(UpdateTaskStage stage, uint8_t percent) {\n    for(size_t i = 0; i < COUNT_OF(update_task_error_detail); i++) {\n        if(update_task_error_detail[i].stage == stage &&\n           percent >= update_task_error_detail[i].percent_min &&\n           percent <= update_task_error_detail[i].percent_max) {\n            return update_task_error_detail[i].descr;\n        }\n    }\n    return \"Unknown error\";\n}\n\ntypedef struct {\n    UpdateTaskStageGroup group;\n    uint8_t weight;\n} UpdateTaskStageGroupMap;\n\n#define STAGE_DEF(GROUP, WEIGHT) \\\n    {                            \\\n        .group = (GROUP),        \\\n        .weight = (WEIGHT),      \\\n    }\n\nstatic const UpdateTaskStageGroupMap update_task_stage_progress[] = {\n    [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0),\n\n    [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 45),\n    [UpdateTaskStageIntBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5),\n\n    [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15),\n    [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 25),\n    [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 40),\n    [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 30),\n    [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 5),\n\n    [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 2),\n\n    [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 33),\n    [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100),\n    [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 20),\n\n    [UpdateTaskStageIntRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5),\n\n    [UpdateTaskStageResourcesFileCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 100),\n    [UpdateTaskStageResourcesDirCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 50),\n    [UpdateTaskStageResourcesFileUnpack] = STAGE_DEF(UpdateTaskStageGroupResources, 255),\n    [UpdateTaskStageSplashscreenInstall] = STAGE_DEF(UpdateTaskStageGroupSplashscreen, 5),\n\n    [UpdateTaskStageCompleted] = STAGE_DEF(UpdateTaskStageGroupMisc, 1),\n    [UpdateTaskStageError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1),\n    [UpdateTaskStageOBError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1),\n};\n\nstatic UpdateTaskStageGroup update_task_get_task_groups(UpdateTask* update_task) {\n    UpdateTaskStageGroup ret = UpdateTaskStageGroupPreUpdate | UpdateTaskStageGroupPostUpdate;\n    UpdateManifest* manifest = update_task->manifest;\n    if(!furi_string_empty(manifest->radio_image)) {\n        ret |= UpdateTaskStageGroupRadio;\n    }\n    if(update_manifest_has_obdata(manifest)) {\n        ret |= UpdateTaskStageGroupOptionBytes;\n    }\n    if(!furi_string_empty(manifest->firmware_dfu_image)) {\n        ret |= UpdateTaskStageGroupFirmware;\n    }\n    if(!furi_string_empty(manifest->resource_bundle)) {\n        ret |= UpdateTaskStageGroupResources;\n    }\n    if(!furi_string_empty(manifest->splash_file)) {\n        ret |= UpdateTaskStageGroupSplashscreen;\n    }\n    return ret;\n}\n\nstatic void update_task_calc_completed_stages(UpdateTask* update_task) {\n    uint32_t completed_stages_points = 0;\n    for(UpdateTaskStage past_stage = UpdateTaskStageProgress;\n        past_stage < update_task->state.stage;\n        ++past_stage) {\n        const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[past_stage];\n        if((grp_descr->group & update_task->state.groups) == 0) {\n            continue;\n        }\n        completed_stages_points += grp_descr->weight;\n    }\n    update_task->state.completed_stages_points = completed_stages_points;\n}\n\nvoid update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) {\n    if(stage != UpdateTaskStageProgress) {\n        FURI_LOG_I(TAG, \"Stage %d, progress %d\", stage, progress);\n        /* do not override more specific error states */\n        if((stage >= UpdateTaskStageError) && (update_task->state.stage >= UpdateTaskStageError)) {\n            return;\n        }\n        /* Build error message with code \"[stage_idx-stage_percent]\" */\n        if(stage >= UpdateTaskStageError) {\n            furi_string_printf(\n                update_task->state.status,\n                \"%s\\n#[%d-%d]\",\n                update_task_get_error_message(\n                    update_task->state.stage, update_task->state.stage_progress),\n                update_task->state.stage,\n                update_task->state.stage_progress);\n        } else {\n            furi_string_set(update_task->state.status, update_task_stage_descr[stage]);\n        }\n        /* Store stage update */\n        update_task->state.stage = stage;\n        /* If we are still alive, sum completed stages weights */\n        if((stage > UpdateTaskStageProgress) && (stage < UpdateTaskStageCompleted)) {\n            update_task_calc_completed_stages(update_task);\n        }\n    }\n\n    /* Store stage progress for all non-error updates - to provide details on error state */\n    if(!update_stage_is_error(stage)) {\n        update_task->state.stage_progress = progress;\n    }\n\n    /* Calculate \"overall\" progress, based on stage weights */\n    uint32_t adapted_progress = 1;\n    if(update_task->state.total_progress_points != 0) {\n        if(stage < UpdateTaskStageCompleted) {\n            adapted_progress = MIN(\n                (update_task->state.completed_stages_points +\n                 (update_task_stage_progress[update_task->state.stage].weight * progress / 100)) *\n                    100 / (update_task->state.total_progress_points),\n                100u);\n\n        } else {\n            adapted_progress = update_task->state.overall_progress;\n        }\n    }\n    update_task->state.overall_progress = adapted_progress;\n\n    if(update_task->status_change_cb) {\n        (update_task->status_change_cb)(\n            furi_string_get_cstr(update_task->state.status),\n            adapted_progress,\n            update_stage_is_error(update_task->state.stage),\n            update_task->status_change_cb_state);\n    }\n}\n\nstatic void update_task_close_file(UpdateTask* update_task) {\n    furi_assert(update_task);\n    if(!storage_file_is_open(update_task->file)) {\n        return;\n    }\n\n    storage_file_close(update_task->file);\n}\n\nstatic bool update_task_check_file_exists(UpdateTask* update_task, FuriString* filename) {\n    furi_assert(update_task);\n    FuriString* tmp_path;\n    tmp_path = furi_string_alloc_set(update_task->update_path);\n    path_append(tmp_path, furi_string_get_cstr(filename));\n    bool exists = storage_file_exists(update_task->storage, furi_string_get_cstr(tmp_path));\n    furi_string_free(tmp_path);\n    return exists;\n}\n\nbool update_task_open_file(UpdateTask* update_task, FuriString* filename) {\n    furi_assert(update_task);\n    update_task_close_file(update_task);\n\n    FuriString* tmp_path;\n    tmp_path = furi_string_alloc_set(update_task->update_path);\n    path_append(tmp_path, furi_string_get_cstr(filename));\n    bool open_success = storage_file_open(\n        update_task->file, furi_string_get_cstr(tmp_path), FSAM_READ, FSOM_OPEN_EXISTING);\n    furi_string_free(tmp_path);\n    return open_success;\n}\n\nstatic void\n    update_task_worker_thread_cb(FuriThread* thread, FuriThreadState state, void* context) {\n    UNUSED(context);\n\n    if(state != FuriThreadStateStopped) {\n        return;\n    }\n\n    if(furi_thread_get_return_code(thread) == UPDATE_TASK_NOERR) {\n        furi_delay_ms(UPDATE_DELAY_OPERATION_OK);\n        furi_hal_power_reset();\n    }\n}\n\nUpdateTask* update_task_alloc(void) {\n    UpdateTask* update_task = malloc(sizeof(UpdateTask));\n\n    update_task->state.stage = UpdateTaskStageProgress;\n    update_task->state.stage_progress = 0;\n    update_task->state.overall_progress = 0;\n    update_task->state.status = furi_string_alloc();\n\n    update_task->manifest = update_manifest_alloc();\n    update_task->storage = furi_record_open(RECORD_STORAGE);\n    update_task->file = storage_file_alloc(update_task->storage);\n    update_task->status_change_cb = NULL;\n    update_task->boot_mode = furi_hal_rtc_get_boot_mode();\n    update_task->update_path = furi_string_alloc();\n\n    FuriThread* thread = update_task->thread =\n        furi_thread_alloc_ex(\"UpdateWorker\", 5120, NULL, update_task);\n\n    furi_thread_set_state_callback(thread, update_task_worker_thread_cb);\n#ifdef FURI_RAM_EXEC\n    UNUSED(update_task_worker_backup_restore);\n    furi_thread_set_callback(thread, update_task_worker_flash_writer);\n#else\n    UNUSED(update_task_worker_flash_writer);\n    furi_thread_set_callback(thread, update_task_worker_backup_restore);\n#endif\n\n    return update_task;\n}\n\nvoid update_task_free(UpdateTask* update_task) {\n    furi_assert(update_task);\n\n    furi_thread_join(update_task->thread);\n\n    furi_thread_free(update_task->thread);\n    update_task_close_file(update_task);\n    storage_file_free(update_task->file);\n    update_manifest_free(update_task->manifest);\n\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(update_task->update_path);\n\n    free(update_task);\n}\n\nbool update_task_parse_manifest(UpdateTask* update_task) {\n    furi_assert(update_task);\n    update_task->state.stage_progress = 0;\n    update_task->state.overall_progress = 0;\n    update_task->state.total_progress_points = 0;\n    update_task->state.completed_stages_points = 0;\n    update_task->state.groups = 0;\n\n    update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0);\n    bool result = false;\n    FuriString* manifest_path;\n    manifest_path = furi_string_alloc();\n\n    do {\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 13);\n        if(!furi_hal_version_do_i_belong_here()) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 20);\n        if(!update_operation_get_current_package_manifest_path(\n               update_task->storage, manifest_path)) {\n            break;\n        }\n\n        path_extract_dirname(furi_string_get_cstr(manifest_path), update_task->update_path);\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 30);\n\n        UpdateManifest* manifest = update_task->manifest;\n        if(!update_manifest_init(manifest, furi_string_get_cstr(manifest_path))) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 40);\n        if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 50);\n        /* Check target only if it's set - skipped for pre-production samples */\n        if(furi_hal_version_get_hw_target() &&\n           (manifest->target != furi_hal_version_get_hw_target())) {\n            break;\n        }\n\n        update_task->state.groups = update_task_get_task_groups(update_task);\n        for(size_t stage_counter = 0; stage_counter < COUNT_OF(update_task_stage_progress);\n            ++stage_counter) {\n            const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[stage_counter];\n            if((grp_descr->group & update_task->state.groups) != 0) {\n                update_task->state.total_progress_points += grp_descr->weight;\n            }\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 60);\n        if((update_task->state.groups & UpdateTaskStageGroupFirmware) &&\n           !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 80);\n        if((update_task->state.groups & UpdateTaskStageGroupRadio) &&\n           (!update_task_check_file_exists(update_task, manifest->radio_image) ||\n            (manifest->radio_version.version.type == 0))) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 100);\n        result = true;\n    } while(false);\n\n    furi_string_free(manifest_path);\n    return result;\n}\n\nvoid update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state) {\n    update_task->status_change_cb = cb;\n    update_task->status_change_cb_state = state;\n}\n\nvoid update_task_start(UpdateTask* update_task) {\n    furi_assert(update_task);\n    furi_thread_start(update_task->thread);\n}\n\nbool update_task_is_running(UpdateTask* update_task) {\n    furi_assert(update_task);\n    return furi_thread_get_state(update_task->thread) == FuriThreadStateRunning;\n}\n\nUpdateTaskState const* update_task_get_state(UpdateTask* update_task) {\n    furi_assert(update_task);\n    return &update_task->state;\n}\n\nUpdateManifest const* update_task_get_manifest(UpdateTask* update_task) {\n    furi_assert(update_task);\n    return update_task->manifest;\n}\n"
  },
  {
    "path": "applications/system/updater/util/update_task.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <update_util/update_manifest.h>\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#define UPDATE_DELAY_OPERATION_OK    10\n#define UPDATE_DELAY_OPERATION_ERROR INT_MAX\n\ntypedef enum {\n    UpdateTaskStageProgress = 0,\n\n    UpdateTaskStageReadManifest,\n    UpdateTaskStageIntBackup,\n\n    UpdateTaskStageRadioImageValidate,\n    UpdateTaskStageRadioErase,\n    UpdateTaskStageRadioWrite,\n    UpdateTaskStageRadioInstall,\n    UpdateTaskStageRadioBusy,\n\n    UpdateTaskStageOBValidation,\n\n    UpdateTaskStageValidateDFUImage,\n    UpdateTaskStageFlashWrite,\n    UpdateTaskStageFlashValidate,\n\n    UpdateTaskStageIntRestore,\n    UpdateTaskStageResourcesFileCleanup,\n    UpdateTaskStageResourcesDirCleanup,\n    UpdateTaskStageResourcesFileUnpack,\n    UpdateTaskStageSplashscreenInstall,\n\n    UpdateTaskStageCompleted,\n    UpdateTaskStageError,\n    UpdateTaskStageOBError,\n    UpdateTaskStageMAX\n} UpdateTaskStage;\n\ninline bool update_stage_is_error(const UpdateTaskStage stage) {\n    return stage >= UpdateTaskStageError;\n}\n\ntypedef enum {\n    UpdateTaskStageGroupMisc = 0,\n    UpdateTaskStageGroupPreUpdate = 1 << 1,\n    UpdateTaskStageGroupFirmware = 1 << 2,\n    UpdateTaskStageGroupOptionBytes = 1 << 3,\n    UpdateTaskStageGroupRadio = 1 << 4,\n    UpdateTaskStageGroupPostUpdate = 1 << 5,\n    UpdateTaskStageGroupResources = 1 << 6,\n    UpdateTaskStageGroupSplashscreen = 1 << 7,\n} UpdateTaskStageGroup;\n\ntypedef struct {\n    UpdateTaskStage stage;\n    uint8_t overall_progress, stage_progress;\n    FuriString* status;\n    UpdateTaskStageGroup groups;\n    uint32_t total_progress_points;\n    uint32_t completed_stages_points;\n} UpdateTaskState;\n\ntypedef struct UpdateTask UpdateTask;\n\ntypedef void (\n    *updateProgressCb)(const char* status, const uint8_t stage_pct, bool failed, void* state);\n\nUpdateTask* update_task_alloc(void);\n\nvoid update_task_free(UpdateTask* update_task);\n\nvoid update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state);\n\nvoid update_task_start(UpdateTask* update_task);\n\nbool update_task_is_running(UpdateTask* update_task);\n\nUpdateTaskState const* update_task_get_state(UpdateTask* update_task);\n\nUpdateManifest const* update_task_get_manifest(UpdateTask* update_task);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "applications/system/updater/util/update_task_i.h",
    "content": "#pragma once\n\n#include <storage/storage.h>\n#include <furi_hal.h>\n\n#define UPDATE_TASK_NOERR  0\n#define UPDATE_TASK_FAILED -1\n\ntypedef struct UpdateTask {\n    UpdateTaskState state;\n    FuriString* update_path;\n    UpdateManifest* manifest;\n    FuriThread* thread;\n    Storage* storage;\n    File* file;\n    updateProgressCb status_change_cb;\n    void* status_change_cb_state;\n    FuriHalRtcBootMode boot_mode;\n} UpdateTask;\n\nvoid update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress);\nbool update_task_parse_manifest(UpdateTask* update_task);\nbool update_task_open_file(UpdateTask* update_task, FuriString* filename);\n\nint32_t update_task_worker_flash_writer(void* context);\nint32_t update_task_worker_backup_restore(void* context);\n\n#define CHECK_RESULT(x) \\\n    if(!(x)) {          \\\n        break;          \\\n    }\n"
  },
  {
    "path": "applications/system/updater/util/update_task_worker_backup.c",
    "content": "#include \"update_task.h\"\n#include \"update_task_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <desktop/helpers/slideshow_filename.h>\n#include <toolbox/path.h>\n#include <update_util/dfu_file.h>\n#include <update_util/int_backup.h>\n#include <update_util/update_operation.h>\n#include <update_util/resources/manifest.h>\n#include <toolbox/tar/tar_archive.h>\n#include <toolbox/crc32_calc.h>\n\n#define TAG \"UpdWorkerBackup\"\n\nstatic bool update_task_pre_update(UpdateTask* update_task) {\n    bool success = false;\n    FuriString* backup_file_path;\n    backup_file_path = furi_string_alloc();\n    path_concat(\n        furi_string_get_cstr(update_task->update_path),\n        INT_BACKUP_DEFAULT_FILENAME,\n        backup_file_path);\n\n    update_task_set_progress(update_task, UpdateTaskStageIntBackup, 0);\n    /* to avoid bootloops */\n    furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);\n    if((success =\n            int_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) {\n        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate);\n    }\n\n    furi_string_free(backup_file_path);\n    return success;\n}\ntypedef struct {\n    UpdateTask* update_task;\n    TarArchive* archive;\n} TarUnpackProgress;\n\nstatic bool update_task_resource_unpack_cb(const char* name, bool is_directory, void* context) {\n    UNUSED(name);\n    UNUSED(is_directory);\n    TarUnpackProgress* unpack_progress = context;\n    int32_t progress = 0, total = 0;\n    tar_archive_get_read_progress(unpack_progress->archive, &progress, &total);\n    update_task_set_progress(\n        unpack_progress->update_task, UpdateTaskStageProgress, (progress * 100) / (total + 1));\n    return true;\n}\n\nstatic void update_task_cleanup_resources(UpdateTask* update_task) {\n    ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(update_task->storage);\n    do {\n        FURI_LOG_D(TAG, \"Cleaning up old manifest\");\n        if(!resource_manifest_reader_open(manifest_reader, EXT_PATH(\"Manifest\"))) {\n            FURI_LOG_W(TAG, \"No existing manifest\");\n            break;\n        }\n\n        ResourceManifestEntry* entry_ptr = NULL;\n        /* Iterate over manifest and calculate entries count */\n        uint32_t n_file_entries = 1, n_dir_entries = 1;\n        while((entry_ptr = resource_manifest_reader_next(manifest_reader))) {\n            if(entry_ptr->type == ResourceManifestEntryTypeFile) {\n                n_file_entries++;\n            } else if(entry_ptr->type == ResourceManifestEntryTypeDirectory) {\n                n_dir_entries++;\n            }\n        }\n        resource_manifest_rewind(manifest_reader);\n\n        update_task_set_progress(update_task, UpdateTaskStageResourcesFileCleanup, 0);\n        uint32_t n_processed_file_entries = 0;\n        while((entry_ptr = resource_manifest_reader_next(manifest_reader))) {\n            if(entry_ptr->type == ResourceManifestEntryTypeFile) {\n                update_task_set_progress(\n                    update_task,\n                    UpdateTaskStageProgress,\n                    (n_processed_file_entries++ * 100) / n_file_entries);\n\n                FuriString* file_path = furi_string_alloc();\n                path_concat(\n                    STORAGE_EXT_PATH_PREFIX, furi_string_get_cstr(entry_ptr->name), file_path);\n                FURI_LOG_D(TAG, \"Removing %s\", furi_string_get_cstr(file_path));\n\n                FS_Error result =\n                    storage_common_remove(update_task->storage, furi_string_get_cstr(file_path));\n                if(result != FSE_OK && result != FSE_EXIST) {\n                    FURI_LOG_E(\n                        TAG,\n                        \"%s remove failed, cause %s\",\n                        furi_string_get_cstr(file_path),\n                        storage_error_get_desc(result));\n                }\n                furi_string_free(file_path);\n            }\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageResourcesDirCleanup, 0);\n        uint32_t n_processed_dir_entries = 0;\n        while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) {\n            if(entry_ptr->type == ResourceManifestEntryTypeDirectory) {\n                update_task_set_progress(\n                    update_task,\n                    UpdateTaskStageProgress,\n                    (n_processed_dir_entries++ * 100) / n_dir_entries);\n\n                FuriString* folder_path = furi_string_alloc();\n\n                do {\n                    path_concat(\n                        STORAGE_EXT_PATH_PREFIX,\n                        furi_string_get_cstr(entry_ptr->name),\n                        folder_path);\n\n                    FURI_LOG_D(TAG, \"Removing folder %s\", furi_string_get_cstr(folder_path));\n                    FS_Error result = storage_common_remove(\n                        update_task->storage, furi_string_get_cstr(folder_path));\n                    if(result != FSE_OK && result != FSE_EXIST) {\n                        FURI_LOG_E(\n                            TAG,\n                            \"%s remove failed, cause %s\",\n                            furi_string_get_cstr(folder_path),\n                            storage_error_get_desc(result));\n                    }\n                } while(false);\n\n                furi_string_free(folder_path);\n            }\n        }\n    } while(false);\n    resource_manifest_reader_free(manifest_reader);\n}\n\nstatic bool update_task_post_update(UpdateTask* update_task) {\n    bool success = false;\n\n    FuriString* file_path;\n    file_path = furi_string_alloc();\n\n    TarArchive* archive = tar_archive_alloc(update_task->storage);\n    do {\n        path_concat(\n            furi_string_get_cstr(update_task->update_path),\n            INT_BACKUP_DEFAULT_FILENAME,\n            file_path);\n\n        update_task_set_progress(update_task, UpdateTaskStageIntRestore, 0);\n\n        CHECK_RESULT(int_backup_unpack(update_task->storage, furi_string_get_cstr(file_path)));\n\n        if(update_task->state.groups & UpdateTaskStageGroupResources) {\n            TarUnpackProgress progress = {\n                .update_task = update_task,\n                .archive = archive,\n            };\n\n            path_concat(\n                furi_string_get_cstr(update_task->update_path),\n                furi_string_get_cstr(update_task->manifest->resource_bundle),\n                file_path);\n\n            CHECK_RESULT(tar_archive_open(\n                archive, furi_string_get_cstr(file_path), TarOpenModeReadHeatshrink));\n\n            update_task_cleanup_resources(update_task);\n\n            update_task_set_progress(update_task, UpdateTaskStageResourcesFileUnpack, 0);\n            tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress);\n            CHECK_RESULT(tar_archive_unpack_to(archive, STORAGE_EXT_PATH_PREFIX, NULL));\n        }\n\n        if(update_task->state.groups & UpdateTaskStageGroupSplashscreen) {\n            update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 0);\n            FuriString* tmp_path;\n            tmp_path = furi_string_alloc_set(update_task->update_path);\n            path_append(tmp_path, furi_string_get_cstr(update_task->manifest->splash_file));\n            if(storage_common_copy(\n                   update_task->storage,\n                   furi_string_get_cstr(tmp_path),\n                   INT_PATH(SLIDESHOW_FILE_NAME)) != FSE_OK) {\n                // actually, not critical\n            }\n            furi_string_free(tmp_path);\n            update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 100);\n        }\n        success = true;\n    } while(false);\n\n    tar_archive_free(archive);\n    furi_string_free(file_path);\n    return success;\n}\n\nint32_t update_task_worker_backup_restore(void* context) {\n    furi_assert(context);\n    UpdateTask* update_task = context;\n\n    FuriHalRtcBootMode boot_mode = update_task->boot_mode;\n    if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) {\n        /* no idea how we got here. Do nothing */\n        return UPDATE_TASK_NOERR;\n    }\n\n    bool success = false;\n    do {\n        if(!update_task_parse_manifest(update_task)) {\n            break;\n        }\n\n        if(boot_mode == FuriHalRtcBootModePreUpdate) {\n            success = update_task_pre_update(update_task);\n        } else if(boot_mode == FuriHalRtcBootModePostUpdate) { //-V547\n            success = update_task_post_update(update_task);\n            if(success) {\n                update_operation_disarm();\n            }\n        }\n    } while(false);\n\n    if(!success) {\n        update_task_set_progress(update_task, UpdateTaskStageError, 0);\n        return UPDATE_TASK_FAILED;\n    }\n\n    update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);\n    return UPDATE_TASK_NOERR;\n}\n"
  },
  {
    "path": "applications/system/updater/util/update_task_worker_flasher.c",
    "content": "#include \"update_task.h\"\n#include \"update_task_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <storage/storage.h>\n#include <toolbox/path.h>\n#include <update_util/dfu_file.h>\n#include <update_util/update_operation.h>\n#include <toolbox/tar/tar_archive.h>\n#include <toolbox/crc32_calc.h>\n\n#define TAG \"UpdWorkerRam\"\n\n#define STM_DFU_VENDOR_ID            0x0483\n#define STM_DFU_PRODUCT_ID           0xDF11\n/* Written into DFU file by build pipeline */\n#define FLIPPER_ZERO_DFU_DEVICE_CODE 0xFFFF\n/* Time, in ms, to wait for system restart by C2 before crashing */\n#define C2_MODE_SWITCH_TIMEOUT       10000\n\nstatic const DfuValidationParams flipper_dfu_params = {\n    .device = FLIPPER_ZERO_DFU_DEVICE_CODE,\n    .product = STM_DFU_PRODUCT_ID,\n    .vendor = STM_DFU_VENDOR_ID,\n};\n\nstatic void update_task_file_progress(const uint8_t progress, void* context) {\n    UpdateTask* update_task = context;\n    update_task_set_progress(update_task, UpdateTaskStageProgress, progress);\n}\n\nstatic bool page_task_compare_flash(\n    const uint8_t i_page,\n    const uint8_t* update_block,\n    uint16_t update_block_len) {\n    const size_t page_addr = furi_hal_flash_get_base() + furi_hal_flash_get_page_size() * i_page;\n    return memcmp(update_block, (void*)page_addr, update_block_len) == 0;\n}\n\n/* Verifies a flash operation address for fitting into writable memory\n */\nstatic bool check_address_boundaries(const size_t address) {\n    const size_t min_allowed_address = furi_hal_flash_get_base();\n    const size_t max_allowed_address = (size_t)furi_hal_flash_get_free_end_address();\n    return (address >= min_allowed_address) && (address < max_allowed_address);\n}\n\nstatic bool update_task_flash_program_page(\n    const uint8_t i_page,\n    const uint8_t* update_block,\n    uint16_t update_block_len) {\n    furi_hal_flash_program_page(i_page, update_block, update_block_len);\n    return true;\n}\n\nstatic bool update_task_write_dfu(UpdateTask* update_task) {\n    DfuUpdateTask page_task = {\n        .address_cb = &check_address_boundaries,\n        .progress_cb = &update_task_file_progress,\n        .task_cb = &update_task_flash_program_page,\n        .context = update_task,\n    };\n\n    bool success = false;\n    do {\n        update_task_set_progress(update_task, UpdateTaskStageValidateDFUImage, 0);\n        CHECK_RESULT(\n            update_task_open_file(update_task, update_task->manifest->firmware_dfu_image));\n        CHECK_RESULT(\n            dfu_file_validate_crc(update_task->file, &update_task_file_progress, update_task));\n\n        const uint8_t valid_targets =\n            dfu_file_validate_headers(update_task->file, &flipper_dfu_params);\n        if(valid_targets == 0) {\n            break;\n        }\n\n        update_task_set_progress(update_task, UpdateTaskStageFlashWrite, 0);\n        CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));\n\n        page_task.task_cb = &page_task_compare_flash;\n\n        update_task_set_progress(update_task, UpdateTaskStageFlashValidate, 0);\n        CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nstatic bool update_task_write_stack_data(UpdateTask* update_task) {\n    furi_check(storage_file_is_open(update_task->file));\n    const size_t FLASH_PAGE_SIZE = furi_hal_flash_get_page_size();\n\n    uint32_t stack_size = storage_file_size(update_task->file);\n    storage_file_seek(update_task->file, 0, true);\n\n    if(!check_address_boundaries(update_task->manifest->radio_address) ||\n       !check_address_boundaries(update_task->manifest->radio_address + stack_size)) {\n        return false;\n    }\n\n    update_task_set_progress(update_task, UpdateTaskStageRadioWrite, 0);\n    uint8_t* fw_block = malloc(FLASH_PAGE_SIZE);\n    size_t bytes_read = 0;\n    uint32_t element_offs = 0;\n\n    while(element_offs < stack_size) {\n        uint32_t n_bytes_to_read = FLASH_PAGE_SIZE;\n        if((element_offs + n_bytes_to_read) > stack_size) {\n            n_bytes_to_read = stack_size - element_offs;\n        }\n\n        bytes_read = storage_file_read(update_task->file, fw_block, n_bytes_to_read);\n        CHECK_RESULT(bytes_read != 0);\n\n        int16_t i_page =\n            furi_hal_flash_get_page_number(update_task->manifest->radio_address + element_offs);\n        CHECK_RESULT(i_page >= 0);\n\n        furi_hal_flash_program_page(i_page, fw_block, bytes_read);\n\n        element_offs += bytes_read;\n        update_task_set_progress(\n            update_task, UpdateTaskStageProgress, element_offs * 100 / stack_size);\n    }\n\n    free(fw_block);\n    return element_offs == stack_size;\n}\n\nstatic void update_task_wait_for_restart(UpdateTask* update_task) {\n    update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 70);\n    furi_delay_ms(C2_MODE_SWITCH_TIMEOUT);\n    furi_crash(\"C2 timeout\");\n}\n\nstatic bool update_task_write_stack(UpdateTask* update_task) {\n    UpdateManifest* manifest = update_task->manifest;\n    do {\n        FURI_LOG_W(TAG, \"Writing stack\");\n        update_task_set_progress(update_task, UpdateTaskStageRadioImageValidate, 0);\n        CHECK_RESULT(update_task_open_file(update_task, manifest->radio_image));\n        CHECK_RESULT(\n            crc32_calc_file(update_task->file, &update_task_file_progress, update_task) ==\n            manifest->radio_crc);\n\n        CHECK_RESULT(update_task_write_stack_data(update_task));\n        update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 10);\n        CHECK_RESULT(\n            ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError);\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 80);\n        CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 100);\n        /* ...system will restart here. */\n        update_task_wait_for_restart(update_task);\n    } while(false);\n    return false; /* will return only in the case of failure */\n}\n\nstatic bool update_task_remove_stack(UpdateTask* update_task) {\n    do {\n        FURI_LOG_W(TAG, \"Removing stack\");\n        update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30);\n        CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError);\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 80);\n        CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);\n        update_task_set_progress(update_task, UpdateTaskStageProgress, 100);\n        /* ...system will restart here. */\n        update_task_wait_for_restart(update_task);\n    } while(false);\n    return false; /* will return only in the case of failure */\n}\n\nstatic bool update_task_manage_radiostack(UpdateTask* update_task) {\n    update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);\n    bool success = false;\n    do {\n        CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT));\n\n        const BleGlueC2Info* c2_state = ble_glue_get_c2_info();\n\n        const UpdateManifestRadioVersion* radio_ver = &update_task->manifest->radio_version;\n        bool stack_version_match = (c2_state->VersionMajor == radio_ver->version.major) &&\n                                   (c2_state->VersionMinor == radio_ver->version.minor) &&\n                                   (c2_state->VersionSub == radio_ver->version.sub) &&\n                                   (c2_state->VersionBranch == radio_ver->version.branch) &&\n                                   (c2_state->VersionReleaseType == radio_ver->version.release);\n        bool stack_missing = (c2_state->VersionMajor == 0) && (c2_state->VersionMinor == 0);\n\n        if(c2_state->mode == BleGlueC2ModeStack) {\n            /* Stack type is not available when we have FUS running. */\n            bool total_stack_match = stack_version_match &&\n                                     (c2_state->StackType == radio_ver->version.type);\n            if(total_stack_match) {\n                /* Nothing to do. */\n                FURI_LOG_W(TAG, \"Stack version is up2date\");\n                furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update);\n                success = true;\n                break;\n            } else {\n                /* Version or type mismatch. Let's boot to FUS and start updating. */\n                FURI_LOG_W(TAG, \"Restarting to FUS\");\n                furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update);\n                update_task_set_progress(update_task, UpdateTaskStageProgress, 20);\n\n                CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS));\n                /* ...system will restart here. */\n                update_task_wait_for_restart(update_task);\n            }\n        } else if(c2_state->mode == BleGlueC2ModeFUS) {\n            /* OK, we're in FUS mode. */\n            FURI_LOG_W(TAG, \"Waiting for FUS to settle\");\n            update_task_set_progress(update_task, UpdateTaskStageProgress, 30);\n            CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);\n            if(stack_version_match) {\n                /* We can't check StackType with FUS, but partial version matches */\n                if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) {\n                    /* This flag was set when full version was checked.\n                     * And something in versions of the stack didn't match.\n                     * So, clear the flag and drop the stack. */\n                    furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update);\n                    FURI_LOG_W(TAG, \"Forcing stack removal (match)\");\n                    CHECK_RESULT(update_task_remove_stack(update_task));\n                } else {\n                    /* We might just had the stack installed.\n                     * Let's start it up to check its version */\n                    FURI_LOG_W(TAG, \"Starting stack to check full version\");\n                    update_task_set_progress(update_task, UpdateTaskStageProgress, 50);\n                    CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack));\n                    /* ...system will restart here. */\n                    update_task_wait_for_restart(update_task);\n                }\n            } else {\n                if(stack_missing) {\n                    /* Install stack. */\n                    CHECK_RESULT(update_task_write_stack(update_task));\n                } else {\n                    CHECK_RESULT(update_task_remove_stack(update_task));\n                }\n            }\n        }\n    } while(false);\n\n    return success;\n}\n\nbool update_task_validate_optionbytes(UpdateTask* update_task) {\n    update_task_set_progress(update_task, UpdateTaskStageOBValidation, 0);\n\n    bool match = true;\n    bool ob_dirty = false;\n    const UpdateManifest* manifest = update_task->manifest;\n    const FuriHalFlashRawOptionByteData* device_data = furi_hal_flash_ob_get_raw_ptr();\n    for(size_t idx = 0; idx < FURI_HAL_FLASH_OB_TOTAL_VALUES; ++idx) {\n        update_task_set_progress(\n            update_task, UpdateTaskStageProgress, idx * 100 / FURI_HAL_FLASH_OB_TOTAL_VALUES);\n        const uint32_t ref_value = manifest->ob_reference.obs[idx].values.base;\n        const uint32_t device_ob_value = device_data->obs[idx].values.base;\n        const uint32_t device_ob_value_masked = device_ob_value &\n                                                manifest->ob_compare_mask.obs[idx].values.base;\n        if(ref_value != device_ob_value_masked) {\n            match = false;\n            FURI_LOG_E(\n                TAG,\n                \"OB MISMATCH: #%d: real %08lX != %08lX (exp.), full %08lX\",\n                idx,\n                device_ob_value_masked,\n                ref_value,\n                device_ob_value);\n\n            /* any bits we are allowed to write?.. */\n            bool can_patch = ((device_ob_value_masked ^ ref_value) &\n                              manifest->ob_write_mask.obs[idx].values.base) != 0;\n\n            if(can_patch) {\n                const uint32_t patched_value =\n                    /* take all non-writable bits from real value */\n                    (device_ob_value & ~(manifest->ob_write_mask.obs[idx].values.base)) |\n                    /* take all writable bits from reference value */\n                    (manifest->ob_reference.obs[idx].values.base &\n                     manifest->ob_write_mask.obs[idx].values.base);\n\n                FURI_LOG_W(TAG, \"Fixing up OB byte #%d to %08lX\", idx, patched_value);\n                ob_dirty = true;\n\n                bool is_fixed = furi_hal_flash_ob_set_word(idx, patched_value) &&\n                                ((device_data->obs[idx].values.base &\n                                  manifest->ob_compare_mask.obs[idx].values.base) == ref_value);\n\n                if(!is_fixed) {\n                    /* Things are so bad that fixing what we are allowed to still doesn't match\n                     * reference value */\n                    FURI_LOG_W(\n                        TAG,\n                        \"OB #%d is FUBAR (fixed&masked %08lX, not %08lX)\",\n                        idx,\n                        patched_value,\n                        ref_value);\n                }\n            }\n        } else {\n            FURI_LOG_D(\n                TAG,\n                \"OB MATCH: #%d: real %08lX == %08lX (exp.)\",\n                idx,\n                device_ob_value_masked,\n                ref_value);\n        }\n    }\n    if(!match) {\n        update_task_set_progress(update_task, UpdateTaskStageOBError, 0);\n    }\n\n    if(ob_dirty) {\n        FURI_LOG_W(TAG, \"OBs were changed, applying\");\n        furi_hal_flash_ob_apply();\n    }\n    return match;\n}\n\nint32_t update_task_worker_flash_writer(void* context) {\n    furi_assert(context);\n    UpdateTask* update_task = context;\n    bool success = false;\n\n    do {\n        CHECK_RESULT(update_task_parse_manifest(update_task));\n\n        if(update_task->state.groups & UpdateTaskStageGroupRadio) {\n            CHECK_RESULT(update_task_manage_radiostack(update_task));\n        }\n\n        if(update_task->state.groups & UpdateTaskStageGroupOptionBytes) {\n            CHECK_RESULT(update_task_validate_optionbytes(update_task));\n        }\n\n        if(update_task->state.groups & UpdateTaskStageGroupFirmware) {\n            CHECK_RESULT(update_task_write_dfu(update_task));\n        }\n\n        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);\n        // Clean up /int before restoring backup on next boot\n        furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);\n#ifdef FURI_NDEBUG\n        // Production\n        furi_hal_rtc_set_log_level(FuriLogLevelDefault);\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);\n        furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep);\n        furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone);\n#endif\n        update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);\n        success = true;\n    } while(false);\n\n    if(!success) {\n        update_task_set_progress(update_task, UpdateTaskStageError, 0);\n        return UPDATE_TASK_FAILED;\n    }\n\n    return UPDATE_TASK_NOERR;\n}\n"
  },
  {
    "path": "applications/system/updater/views/updater_main.c",
    "content": "#include <gui/gui_i.h>\n#include <gui/view.h>\n#include <gui/elements.h>\n#include <gui/canvas.h>\n#include <assets_icons.h>\n#include <furi.h>\n#include <input/input.h>\n\n#include \"../updater_i.h\"\n#include \"updater_main.h\"\n\nstruct UpdaterMainView {\n    View* view;\n    ViewDispatcher* view_dispatcher;\n    FuriPubSubSubscription* subscription;\n    void* context;\n};\n\nstatic const uint8_t PROGRESS_RENDER_STEP = 1; /* percent, to limit rendering rate */\n\ntypedef struct {\n    FuriString* status;\n    uint8_t progress, rendered_progress;\n    bool failed;\n} UpdaterProgressModel;\n\nvoid updater_main_model_set_state(\n    UpdaterMainView* main_view,\n    const char* message,\n    uint8_t progress,\n    bool failed) {\n    bool update = false;\n    with_view_model(\n        main_view->view,\n        UpdaterProgressModel * model,\n        {\n            model->failed = failed;\n            model->progress = progress;\n            if(furi_string_cmp_str(model->status, message)) {\n                furi_string_set(model->status, message);\n                model->rendered_progress = progress;\n                update = true;\n            } else if(\n                (model->rendered_progress > progress) ||\n                ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) {\n                model->rendered_progress = progress;\n                update = true;\n            }\n        },\n        update);\n}\n\nView* updater_main_get_view(UpdaterMainView* main_view) {\n    furi_assert(main_view);\n    return main_view->view;\n}\n\nbool updater_main_input(InputEvent* event, void* context) {\n    furi_assert(event);\n    furi_assert(context);\n\n    UpdaterMainView* main_view = context;\n    if(!main_view->view_dispatcher) {\n        return true;\n    }\n\n    if((event->type == InputTypeShort) && (event->key == InputKeyOk)) {\n        view_dispatcher_send_custom_event(\n            main_view->view_dispatcher, UpdaterCustomEventRetryUpdate);\n    } else if((event->type == InputTypeLong) && (event->key == InputKeyBack)) {\n        view_dispatcher_send_custom_event(\n            main_view->view_dispatcher, UpdaterCustomEventCancelUpdate);\n    }\n\n    return true;\n}\n\nstatic void updater_main_draw_callback(Canvas* canvas, void* _model) {\n    UpdaterProgressModel* model = _model;\n\n    canvas_set_font(canvas, FontPrimary);\n\n    if(model->failed) {\n        canvas_draw_icon(canvas, 2, 22, &I_Warning_30x23);\n        canvas_draw_str_aligned(canvas, 40, 9, AlignLeft, AlignTop, \"Update Failed!\");\n        canvas_set_font(canvas, FontSecondary);\n\n        elements_multiline_text_aligned(\n            canvas, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(model->status));\n\n        canvas_draw_str_aligned(\n            canvas, 18, 55, AlignLeft, AlignTop, \"to retry, hold       to abort\");\n        canvas_draw_icon(canvas, 7, 54, &I_Ok_btn_9x9);\n        canvas_draw_icon(canvas, 75, 55, &I_Pin_back_arrow_10x8);\n    } else {\n        canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, \"UPDATING\");\n        canvas_set_font(canvas, FontSecondary);\n        canvas_draw_str_aligned(\n            canvas, 64, 51, AlignCenter, AlignTop, furi_string_get_cstr(model->status));\n        canvas_draw_icon(canvas, 4, 5, &I_Updating_32x40);\n        elements_progress_bar(canvas, 42, 29, 80, (float)model->progress / 100);\n    }\n}\n\nUpdaterMainView* updater_main_alloc(void) {\n    UpdaterMainView* main_view = malloc(sizeof(UpdaterMainView));\n\n    main_view->view = view_alloc();\n    view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(UpdaterProgressModel));\n\n    with_view_model(\n        main_view->view,\n        UpdaterProgressModel * model,\n        { model->status = furi_string_alloc_set(\"Waiting for SD card\"); },\n        true);\n\n    view_set_context(main_view->view, main_view);\n    view_set_input_callback(main_view->view, updater_main_input);\n    view_set_draw_callback(main_view->view, updater_main_draw_callback);\n\n    return main_view;\n}\n\nvoid updater_main_free(UpdaterMainView* main_view) {\n    furi_assert(main_view);\n    with_view_model(\n        main_view->view, UpdaterProgressModel * model, { furi_string_free(model->status); }, false);\n    view_free(main_view->view);\n    free(main_view);\n}\n\nvoid updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub) {\n    main_view->subscription = sub;\n}\n\nFuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view) {\n    return main_view->subscription;\n}\n\nvoid updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher) {\n    main_view->view_dispatcher = view_dispatcher;\n}\n"
  },
  {
    "path": "applications/system/updater/views/updater_main.h",
    "content": "#pragma once\n\n#include <gui/view.h>\n\ntypedef struct UpdaterMainView UpdaterMainView;\ntypedef struct FuriPubSubSubscription FuriPubSubSubscription;\ntypedef struct ViewDispatcher ViewDispatcher;\ntypedef void (*UpdaterMainInputCallback)(InputType type, void* context);\n\nView* updater_main_get_view(UpdaterMainView* main_view);\n\nUpdaterMainView* updater_main_alloc(void);\n\nvoid updater_main_free(UpdaterMainView* main_view);\n\nvoid updater_main_model_set_state(\n    UpdaterMainView* main_view,\n    const char* message,\n    uint8_t progress,\n    bool failed);\n\nvoid updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub);\n\nFuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view);\n\nvoid updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher);\n"
  },
  {
    "path": "assets/.gitignore",
    "content": "/core2_firmware\n"
  },
  {
    "path": "assets/ReadMe.md",
    "content": "# Firmware Assets {#firmware_assets}\n\n## Compiling\n\n```bash\n./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources\n```\n\n## Asset naming rules\n\n### Images and Animations\n\n`NAME_VARIANT_SIZE`\n\n- `NAME`    - mandatory - Asset name in CamelCase. [A-Za-z0-9], special symbols not allowed\n- `VARIANT` - optional  - icon variant: can relate to state or rendering conditions. Examples: active, inactive, inverted.\n- `SIZE`    - mandatory - size in px. Example square 10, 20, 24, etc. Example rectangular: 10x8, 19x5, etc.\n\nImage names will be automatically prefixed with `I_`, animation names with `A_`.\nIcons and Animations will be gathered into `icon.h` and `icon.c`.\n\n### Dolphin and Games assets\n\nRules are same as for Images and Animations plus assets are grouped by level and level prepends `NAME`.\nGood starting point: https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/AssetNaming/\n\n## Important notes\n\nDon't include assets that you are not using, compiler is not going to strip unused assets.\n\n## Structure\n- `dolphin`             - Dolphin game assets sources. Goes to `compiled` and `resources` folders in `build` directory.\n- `icons`               - Icons sources. Goes to `compiled` folder in `build` directory.\n- `protobuf`            - Protobuf sources. Goes to `compiled` folder in `build` directory.\n- `slideshow`           - One-time slideshows for desktop\n"
  },
  {
    "path": "assets/SConscript",
    "content": "Import(\"env\")\n\nassetsenv = env.Clone(\n    tools=[\"fbt_assets\"],\n    FW_LIB_NAME=\"assets\",\n    ASSETS_WORK_DIR=env.Dir(\"compiled\"),\n    ASSETS_SRC_DIR=env.Dir(\"#/assets\"),\n)\nassetsenv.ApplyLibFlags()\n\nicons = assetsenv.CompileIcons(\n    assetsenv[\"ASSETS_WORK_DIR\"],\n    assetsenv[\"ASSETS_SRC_DIR\"].Dir(\"icons\"),\n)\nassetsenv.Alias(\"icons\", icons)\n\n\n# Protobuf .proto -> .c + .h\nproto_src = assetsenv.Glob(\"protobuf/*.proto\", source=True)\nproto_options = assetsenv.Glob(\"protobuf/*.options\", source=True)\nproto = assetsenv.ProtoBuilder(assetsenv[\"ASSETS_WORK_DIR\"], proto_src)\nassetsenv.Depends(proto, proto_options)\n# Precious(proto)\nassetsenv.Alias(\"proto\", proto)\n\n\n# Internal animations\n\ndolphin_internal = assetsenv.DolphinSymBuilder(\n    assetsenv[\"ASSETS_WORK_DIR\"],\n    assetsenv[\"ASSETS_SRC_DIR\"].Dir(\"dolphin\"),\n    DOLPHIN_RES_TYPE=\"internal\",\n)\nassetsenv.Alias(\"dolphin_internal\", dolphin_internal)\n\n\n# Blocking animations\n\ndolphin_blocking = assetsenv.DolphinSymBuilder(\n    assetsenv[\"ASSETS_WORK_DIR\"],\n    assetsenv[\"ASSETS_SRC_DIR\"].Dir(\"dolphin\"),\n    DOLPHIN_RES_TYPE=\"blocking\",\n)\nassetsenv.Alias(\"dolphin_blocking\", dolphin_blocking)\n\n\n# Protobuf version meta\nproto_ver = assetsenv.ProtoVerBuilder(\n    \"${ASSETS_WORK_DIR}/protobuf_version.h\",\n    assetsenv[\"ASSETS_SRC_DIR\"].File(\"protobuf/Changelog\"),\n)\nassetsenv.Depends(proto_ver, proto)\nassetsenv.Alias(\"proto_ver\", proto_ver)\n\n# Gather everything into a static lib\nassets_parts = (icons, proto, dolphin_blocking, dolphin_internal, proto_ver)\nenv.Replace(FW_ASSETS_HEADERS=assets_parts)\n\nassetslib = assetsenv.Library(\"${FW_LIB_NAME}\", assets_parts)\nassetsenv.Install(\"${LIB_DIST_DIR}\", assetslib)\n\n\n# Resources for SD card\nif assetsenv[\"IS_BASE_FIRMWARE\"]:\n    dolphin_external_out_dir = assetsenv[\"ASSETS_WORK_DIR\"].Dir(\"dolphin\")\n    # External dolphin animations\n    dolphin_external = assetsenv.DolphinExtBuilder(\n        dolphin_external_out_dir,\n        assetsenv[\"ASSETS_SRC_DIR\"].Dir(\"dolphin\"),\n        DOLPHIN_RES_TYPE=\"external\",\n    )\n    if assetsenv[\"FORCE\"]:\n        assetsenv.AlwaysBuild(dolphin_external)\n    assetsenv.Alias(\"dolphin_ext\", dolphin_external)\n    assetsenv.Clean(dolphin_external, dolphin_external_out_dir)\n\n    env.Replace(DOLPHIN_EXTERNAL_OUT_DIR=dolphin_external_out_dir)\n\nReturn(\"assetslib\")\n"
  },
  {
    "path": "assets/dolphin/ReadMe.md",
    "content": "# Dolphin assets {#dolphin_assets}\n\nDolphin assets are split into 3 parts:\n\n- blocking - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_blocking.[h,c]`.\n- internal  - Internal animations that are used for idle dolphin animation. Converted to `assets_dolphin_internal.[h,c]`.\n- external  - External animations that are used for idle dolphin animation. Packed to resource folder and placed on SD card.\n\n# Files\n\n- `manifest.txt` - contains animations enumeration that is used for random animation selection. Starting point for Dolphin.\n- `meta.txt`     - contains data that describes how animation is drawn.\n- `frame_X.png`  - animation frame.\n\n## File manifest.txt\n\nFlipper Format File with ordered keys.\n\nHeader:\n\n```\nFiletype: Flipper Animation Manifest\nVersion: 1\n```\n\n- `Name` - name of animation. Must be exact animation directory name.\n- `Min butthurt`, `Max butthurt` - range of dolphin's butthurt for this animation.\n- `Min level`, `Max level` - range of dolphin's level for this animation. If 0, this animation doesn't participate in random idle animation selection and can only be selected by exact name.\n- `Weight` - chance of this animation to be chosen at random animation selection.\n\nSome animations can be excluded from participation in random animation selection, such as `L1_NoSd_128x49`.\n\n## File meta.txt\n\nFlipper Format File with ordered keys.\n\nHeader:\n\n```\nFiletype: Flipper Animation\nVersion: 1\n```\n\n- `Width` - animation width in px (<= 128)\n- `Height` - animation height in px (<= 64)\n- `Passive frames` - number of bitmap frames for passive animation state\n- `Active frames` - number of bitmap frames for active animation state (can be 0)\n- `Frames order` - order of bitmap frames where first N frames are passive and following M are active. Each X number in order refers to bitmap frame, with name frame\\_X.bm. This file must exist. Any X number can be repeated to refer same frame in animation.\n- `Active cycles` - cycles to repeat of N active frames for full active period. E.g. if frames for active cycles are 6 and 7, and active cycles is 3, so full active period plays 6 7 6 7 6 7. Full period of passive + active period are called *total period*.\n- `Frame rate` - number of frames to play for 1 second.\n- `Duration` - total amount of seconds to play 1 animation.\n- `Active cooldown` - amount of seconds (after passive mode) to pass before entering next active mode.\n\n- `Bubble slots` - amount of bubble sequences.\n- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurrence in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is chosen. There can also be free of bubbles frames between 2 bubbles.\n\n- `Slot` - number to unite bubbles for same sequence.\n- `X`, `Y` - are coordinates of left top corner of bubble.\n- `Text` - text in bubble. New line is `\\n`\n- `AlignH` - horizontal place of bubble corner (Left, Center, Right)\n- `AlignV` - vertical place of bubble corner (Top, Center, Bottom)\n- `StartFrame`, `EndFrame` - frame index range inside whole period to show bubble.\n\n### Understanding of frame indexes\n\nFor example we have\n\n```\nPassive frames: 6\nActive frames: 2\nFrames order: 0 1 2 3 4 5 6 7\nActive cycles: 4\n```\n\nThen we have indexes\n\n```\n                        passive(6)            active (2 * 4)\nReal frames order:   0  1  2  3  4  5     6  7  6  7  6  7  6  7\nFrames indexes:      0  1  2  3  4  5     6  7  8  9  10 11 12 13\n```\n"
  },
  {
    "path": "assets/dolphin/blocking/L0_NewMail_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 6\nActive frames: 0\nFrames order: 0 1 2 3 2 1\nActive cycles: 0\nFrame rate: 2\nDuration: 0\nActive cooldown: 0\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/blocking/L0_NoDb_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 4\nActive frames: 0\nFrames order: 0 1 2 3\nActive cycles: 0\nFrame rate: 2\nDuration: 0\nActive cooldown: 0\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/blocking/L0_SdBad_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 2\nActive frames: 0\nFrames order: 0 1\nActive cycles: 0\nFrame rate: 2\nDuration: 0\nActive cooldown: 0\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/blocking/L0_SdOk_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 4\nActive frames: 0\nFrames order: 0 1 2 3\nActive cycles: 0\nFrame rate: 2\nDuration: 0\nActive cooldown: 0\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/blocking/L0_Url_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 4\nActive frames: 0\nFrames order: 0 1 2 3\nActive cycles: 0\nFrame rate: 2\nDuration: 0\nActive cooldown: 0\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/blocking/manifest.txt",
    "content": "Filetype: Flipper Animation Manifest\nVersion: 1\n\n# Animation 1\nName: L0_NoDb_128x51\nMin butthurt: 0\nMax butthurt: 0\nMin level: 0\nMax level: 0\nWeight: 0\n\n# Animation 2\nName: L0_SdBad_128x51\nMin butthurt: 0\nMax butthurt: 0\nMin level: 0\nMax level: 0\nWeight: 0\n\n# Animation 3\nName: L0_SdOk_128x51\nMin butthurt: 0\nMax butthurt: 0\nMin level: 0\nMax level: 0\nWeight: 0\n\n# Animation 4\nName: L0_Url_128x51\nMin butthurt: 0\nMax butthurt: 0\nMin level: 0\nMax level: 0\nWeight: 0\n\n# Animation 5\nName: L0_NewMail_128x51\nMin butthurt: 0\nMax butthurt: 0\nMin level: 0\nMax level: 0\nWeight: 0\n"
  },
  {
    "path": "assets/dolphin/external/L1_Akira_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 15\nActive frames: 21\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/external/L1_Boxing_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 9\nActive frames: 7\nFrames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 2\n\nSlot: 0\nX: 78\nY: 16\nText: F*&K!!!\nAlignH: Left\nAlignV: Bottom\nStartFrame: 2\nEndFrame: 4\n\nSlot: 1\nX: 78\nY: 16\nText: What ya\\nlookin at?!\nAlignH: Left\nAlignV: Center\nStartFrame: 10\nEndFrame: 15\n"
  },
  {
    "path": "assets/dolphin/external/L1_Cry_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 8\nActive frames: 4\nFrames order: 0 1 2 3 4 2 3 4 5 6 7 6\nActive cycles: 2\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 2\n\nSlot: 0\nX: 22\nY: 40\nText: I miss you\nAlignH: Right\nAlignV: Bottom\nStartFrame: 9\nEndFrame: 11\n\nSlot: 0\nX: 17\nY: 40\nText: my friend...\nAlignH: Right\nAlignV: Bottom\nStartFrame: 12\nEndFrame: 15\n\nSlot: 1\nX: 2\nY: 29\nText: Why are you\\nalways away?\nAlignH: Right\nAlignV: Bottom\nStartFrame: 9\nEndFrame: 15"
  },
  {
    "path": "assets/dolphin/external/L1_Doom_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 15\nActive frames: 24\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/external/L1_Furippa1_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 8\nActive frames: 11\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L1_Kaiju_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 16\nActive frames: 60\nFrames order: 0 1 2 1 0 3 4 5 6 3 7 1 8 1 7 9 0 10 11 12 13 14 15 16 14 12 17 18 19 20 21 22 22 23 22 24 25 26 27 26 25 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 19 18 14 45 14 45 14 45 14 45 14 16 14 14 11 46 47 1\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 83\nY: 42\nText: Sup\nAlignH: Left\nAlignV: Top\nStartFrame: 38\nEndFrame: 40\n\nSlot: 0\nX: 66\nY: 35\nText: What just\nAlignH: Left\nAlignV: Center\nStartFrame: 62\nEndFrame: 65\n\nSlot: 0\nX: 66\nY: 35\nText: happened?\nAlignH: Left\nAlignV: Center\nStartFrame: 66\nEndFrame: 68\n\nSlot: 0\nX: 87\nY: 38\nText: Idk\nAlignH: Left\nAlignV: Top\nStartFrame: 70\nEndFrame: 70\n"
  },
  {
    "path": "assets/dolphin/external/L1_Laptop_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 6\nActive frames: 2\nFrames order: 0 1 2 3 4 5 6 7\nActive cycles: 4\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 60\nY: 23\nText: I have to rest\nAlignH: Left\nAlignV: Bottom\nStartFrame: 7\nEndFrame: 10\n\nSlot: 0\nX: 60\nY: 23\nText: but not today\nAlignH: Left\nAlignV: Bottom\nStartFrame: 11\nEndFrame: 13\n"
  },
  {
    "path": "assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 4\nActive frames: 42\nFrames order: 0 1 2 1 3 4 5 6 7 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 10 11 12 4 3\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 51\nY: 49\nText: Adios.\nAlignH: Center\nAlignV: Top\nStartFrame: 6\nEndFrame: 9\n\nSlot: 0\nX: 1\nY: 49\nText: Forgot something...\nAlignH: Center\nAlignV: Top\nStartFrame: 42\nEndFrame: 45\n"
  },
  {
    "path": "assets/dolphin/external/L1_Mad_fist_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 7\nActive frames: 13\nFrames order: 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 12 13 12 13\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 67\nY: 24\nText: Am I a joke\\nto you?!?!\nAlignH: Left\nAlignV: Center\nStartFrame: 15\nEndFrame: 19\n"
  },
  {
    "path": "assets/dolphin/external/L1_Mods_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 31\nActive frames: 18\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L1_My_dude_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 19\nActive frames: 51\nFrames order: 0 1 2 3 4 5 6 0 1 2 7 8 9 10 11 12 7 8 9 13 14 15 14 13 14 15 7 8 9 16 17 18 13 14 19 20 21 22 23 24 21 25 26 27 28 29 30 31 32 33 32 34 35 36 35 34 37 38 39 40 41 42 43 44 45 46 17 47 48 7\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 41\nY: 43\nText: My dude\nAlignH: Right\nAlignV: Top\nStartFrame: 50\nEndFrame: 50\n\nSlot: 0\nX: 59\nY: 43\nText: My dude\nAlignH: Left\nAlignV: Top\nStartFrame: 54\nEndFrame: 54"
  },
  {
    "path": "assets/dolphin/external/L1_Painting_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 9\nActive frames: 13\nFrames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 57\nY: 24\nText: No mistakes,\nAlignH: Left\nAlignV: Center\nStartFrame: 11\nEndFrame: 14\n\nSlot: 0\nX: 57\nY: 21\nText: only happy\\n accidents\nAlignH: Left\nAlignV: Center\nStartFrame: 15\nEndFrame: 18"
  },
  {
    "path": "assets/dolphin/external/L1_Procrastinating_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 26\nActive frames: 23\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 9 11 12 13 12 14 15 16 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 37 38 37 38 37 39 40\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 2\nY: 36\nText: I'll just get\\nback to work\nAlignH: Right\nAlignV: Center\nStartFrame: 42\nEndFrame: 47"
  },
  {
    "path": "assets/dolphin/external/L1_Read_books_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 13\nActive frames: 2\nFrames order: 0 1 0 2 3 3 4 0 1 5 6 1 1 7 8\nActive cycles: 2\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 1\n\nSlot: 0\nX: 5\nY: 28\nText: Predictable twist\nAlignH: Right\nAlignV: Bottom\nStartFrame: 14\nEndFrame: 16\n"
  },
  {
    "path": "assets/dolphin/external/L1_Recording_128x51/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 51\nPassive frames: 6\nActive frames: 6\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L1_Sad_song_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 31\nActive frames: 55\nFrames order: 0 1 2 3 4 5 0 1 6 7 8 5 9 10 11 12 13 14 15 0 1 2 3 4 5 0 1 6 7 8 5 9 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 53 54 53 55 56 57 58 59 60 61 62 4 5\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 6\n\nBubble slots: 4\n\nSlot: 0\nX: 65\nY: 14\nText: All by myself\nAlignH: Left\nAlignV: Bottom\nStartFrame: 45\nEndFrame: 51\n\nSlot: 0\nX: 5\nY: 16\nText: Don't want\nAlignH: Right\nAlignV: Bottom\nStartFrame: 56\nEndFrame: 58\n\nSlot: 0\nX: 15\nY: 15\nText: to be\nAlignH: Right\nAlignV: Bottom\nStartFrame: 59\nEndFrame: 60\n\nSlot: 0\nX: 14\nY: 14\nText: All by myself\nAlignH: Right\nAlignV: Bottom\nStartFrame: 63\nEndFrame: 69\n\nSlot: 0\nX: 81\nY: 25\nText: anymore\nAlignH: Left\nAlignV: Bottom\nStartFrame: 72\nEndFrame: 74\n\nSlot: 1\nX: 65\nY: 14\nText: Nevermind\nAlignH: Left\nAlignV: Bottom\nStartFrame: 45\nEndFrame: 48\n\nSlot: 1\nX: 65\nY: 14\nText: I'll find\nAlignH: Left\nAlignV: Bottom\nStartFrame: 49\nEndFrame: 52\n\nSlot: 1\nX: 2\nY: 16\nText: Someone like\nAlignH: Right\nAlignV: Bottom\nStartFrame: 56\nEndFrame: 58\n\nSlot: 1\nX: 11\nY: 16\nText: youuuuu\nAlignH: Right\nAlignV: Bottom\nStartFrame: 59\nEndFrame: 60\n\nSlot: 1\nX: 3\nY: 14\nText: I wish nothing\nAlignH: Right\nAlignV: Bottom\nStartFrame: 64\nEndFrame: 66\n\nSlot: 1\nX: 6\nY: 14\nText: but the best\nAlignH: Right\nAlignV: Bottom\nStartFrame: 67\nEndFrame: 70\n\nSlot: 1\nX: 81\nY: 25\nText: for you\nAlignH: Left\nAlignV: Bottom\nStartFrame: 72\nEndFrame: 74\n\nSlot: 2\nX: 65\nY: 14\nText: What have I\nAlignH: Left\nAlignV: Bottom\nStartFrame: 45\nEndFrame: 48\n\nSlot: 2\nX: 65\nY: 14\nText: become\nAlignH: Left\nAlignV: Bottom\nStartFrame: 47\nEndFrame: 51\n\nSlot: 2\nX: 6\nY: 16\nText: My dearest\nAlignH: Right\nAlignV: Bottom\nStartFrame: 56\nEndFrame: 58\n\nSlot: 2\nX: 14\nY: 16\nText: friend\nAlignH: Right\nAlignV: Bottom\nStartFrame: 59\nEndFrame: 60\n\nSlot: 2\nX: 17\nY: 14\nText: Everyone\nAlignH: Right\nAlignV: Bottom\nStartFrame: 63\nEndFrame: 64\n\nSlot: 2\nX: 17\nY: 14\nText: I know\nAlignH: Right\nAlignV: Bottom\nStartFrame: 65\nEndFrame: 67\n\nSlot: 2\nX: 17\nY: 14\nText: goes away\nAlignH: Right\nAlignV: Bottom\nStartFrame: 68\nEndFrame: 70\n\nSlot: 2\nX: 81\nY: 25\nText: in the\\n end\nAlignH: Left\nAlignV: Bottom\nStartFrame: 72\nEndFrame: 74\n\nSlot: 3\nX: 73\nY: 14\nText: We could\\n have been\nAlignH: Left\nAlignV: Bottom\nStartFrame: 45\nEndFrame: 48\n\nSlot: 3\nX: 73\nY: 14\nText: so good\\n together\nAlignH: Left\nAlignV: Bottom\nStartFrame: 49\nEndFrame: 51\n\nSlot: 3\nX: 7\nY: 17\nText: We could\\n have lived\nAlignH: Right\nAlignV: Bottom\nStartFrame: 55\nEndFrame: 57\n\nSlot: 3\nX: 7\nY: 17\nText: this dance\\n forever\nAlignH: Right\nAlignV: Bottom\nStartFrame: 58\nEndFrame: 60\n\nSlot: 3\nX: 12\nY: 14\nText: But now\nAlignH: Right\nAlignV: Bottom\nStartFrame: 64\nEndFrame: 65\n\nSlot: 3\nX: 5\nY: 14\nText: who's gonna\nAlignH: Right\nAlignV: Bottom\nStartFrame: 66\nEndFrame: 67\n\nSlot: 3\nX: 7\nY: 14\nText: dance with\nAlignH: Right\nAlignV: Bottom\nStartFrame: 68\nEndFrame: 69\n\nSlot: 3\nX: 26\nY: 14\nText: me?\nAlignH: Right\nAlignV: Bottom\nStartFrame: 70\nEndFrame: 70\n\nSlot: 3\nX: 81\nY: 25\nText: Please\nAlignH: Left\nAlignV: Bottom\nStartFrame: 72\nEndFrame: 74\n\nSlot: 3\nX: 81\nY: 25\nText: stay\nAlignH: Left\nAlignV: Bottom\nStartFrame: 74\nEndFrame: 75\n"
  },
  {
    "path": "assets/dolphin/external/L1_Senpai_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 16\nActive frames: 22\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 0 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 5\nY: 29\nText: SENPAI !!!\nAlignH: Right\nAlignV: Center\nStartFrame: 28\nEndFrame: 31\n"
  },
  {
    "path": "assets/dolphin/external/L1_Showtime_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 26\nActive frames: 26\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 41 42 43 44 45 46 47 48 49\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 69\nY: 47\nText: SHOWTIME!\nAlignH: Left\nAlignV: Center\nStartFrame: 41\nEndFrame: 44\n"
  },
  {
    "path": "assets/dolphin/external/L1_Sleep_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 2\nActive frames: 4\nFrames order: 0 1 2 3 2 3\nActive cycles: 2\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 2\n\nSlot: 0\nX: 53\nY: 20\nText: In a lucid dream,\\nI could walk...\nAlignH: Left\nAlignV: Bottom\nStartFrame: 3\nEndFrame: 9\n\nSlot: 1\nX: 53\nY: 20\nText: OH MY GOD!\nAlignH: Left\nAlignV: Bottom\nStartFrame: 3\nEndFrame: 5\n\nSlot: 1\nX: 53\nY: 31\nText: Just a dream...\nAlignH: Left\nAlignV: Bottom\nStartFrame: 6\nEndFrame: 9"
  },
  {
    "path": "assets/dolphin/external/L1_Waves_128x50/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 50\nPassive frames: 2\nActive frames: 4\nFrames order: 0 1 2 3 2 3\nActive cycles: 2\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 3\n\nSlot: 0\nX: 1\nY: 17\nText: I am happy,\\nmy friend!\nAlignH: Right\nAlignV: Bottom\nStartFrame: 3\nEndFrame: 9\n\nSlot: 1\nX: 1\nY: 17\nText: So long and\\nthanks for\\nall the fish!\nAlignH: Right\nAlignV: Center\nStartFrame: 3\nEndFrame: 9\n\nSlot: 2\nX: 1\nY: 25\nText: I wish I could\nAlignH: Right\nAlignV: Bottom\nStartFrame: 3\nEndFrame: 5\n\nSlot: 2\nX: 1\nY: 25\nText: swim all day\nAlignH: Right\nAlignV: Bottom\nStartFrame: 6\nEndFrame: 9"
  },
  {
    "path": "assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 21\nActive frames: 44\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 17 19 20 21 22 23 24 24 25 26 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 7\nY: 46\nText: GOOD JOB!\nAlignH: Center\nAlignV: Top\nStartFrame: 54\nEndFrame: 57"
  },
  {
    "path": "assets/dolphin/external/L2_Dj_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 15\nActive frames: 23\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 26 28 29 30 31 32 33 34 35 36\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/external/L2_Furippa2_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 8\nActive frames: 11\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 3\nActive frames: 2\nFrames order: 0 1 2 3 4\nActive cycles: 4\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 22\nY: 25\nText: Mess with\\nthe best,\nAlignH: Right\nAlignV: Center\nStartFrame: 4\nEndFrame: 7\n\nSlot: 0\nX: 31\nY: 25\nText: die like\\nthe rest.\nAlignH: Right\nAlignV: Center\nStartFrame: 8\nEndFrame: 10\n"
  },
  {
    "path": "assets/dolphin/external/L2_Secret_door_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 29\nActive frames: 24\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L2_Soldering_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 9\nActive frames: 5\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 9 10 9\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 71\nY: 28\nText: I am busy rn\nAlignH: Left\nAlignV: Center\nStartFrame: 10\nEndFrame: 13\n"
  },
  {
    "path": "assets/dolphin/external/L2_Wake_up_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 10\nActive frames: 18\nFrames order: 0 1 0 1 0 1 0 2 3 4 0 5 6 7 8 9 10 11 10 12 13 14 15 16 17 18 19 20\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/external/L3_Freedom_2_dolphins_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 20\nActive frames: 38\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 12 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 52\nY: 46\nText: Thanks, man!\nAlignH: Left\nAlignV: Top\nStartFrame: 37\nEndFrame: 40"
  },
  {
    "path": "assets/dolphin/external/L3_Furippa3_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 8\nActive frames: 11\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 8\nActive frames: 8\nFrames order: 0 1 2 3 4 5 4 6 7 8 9 10 11 12 11 13\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0\n"
  },
  {
    "path": "assets/dolphin/external/L3_Intruder_alert_128x64/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 64\nPassive frames: 14\nActive frames: 42\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 0"
  },
  {
    "path": "assets/dolphin/external/L3_Lab_research_128x54/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 54\nPassive frames: 6\nActive frames: 8\nFrames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13\nActive cycles: 1\nFrame rate: 2\nDuration: 3600\nActive cooldown: 7\n\nBubble slots: 1\n\nSlot: 0\nX: 71\nY: 23\nText: 7em!g!7j!\\nVyP5?T-\nAlignH: Left\nAlignV: Center\nStartFrame: 8\nEndFrame: 8\n\nSlot: 0\nX: 71\nY: 23\nText: aUqF7sz!\\n%9.mP5H\nAlignH: Left\nAlignV: Center\nStartFrame: 9\nEndFrame: 9\n\nSlot: 0\nX: 71\nY: 23\nText: 2%Kx2mV\\nL8EyA84\nAlignH: Left\nAlignV: Center\nStartFrame: 10\nEndFrame: 10\n\nSlot: 0\nX: 71\nY: 23\nText: U7%cRXr\\nvbBa!_W1\nAlignH: Left\nAlignV: Center\nStartFrame: 11\nEndFrame: 11\n\nSlot: 0\nX: 71\nY: 23\nText: 5rm_[K%\\n!!(U9d$tE\nAlignH: Left\nAlignV: Center\nStartFrame: 12\nEndFrame: 12\n"
  },
  {
    "path": "assets/dolphin/external/manifest.txt",
    "content": "Filetype: Flipper Animation Manifest\nVersion: 1\n\nName: L1_Waves_128x50\nMin butthurt: 0\nMax butthurt: 5\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Laptop_128x51\nMin butthurt: 0\nMax butthurt: 7\nMin level: 1\nMax level: 1\nWeight: 3\n\nName: L1_Sleep_128x64\nMin butthurt: 0\nMax butthurt: 10\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Recording_128x51\nMin butthurt: 0\nMax butthurt: 8\nMin level: 1\nMax level: 1\nWeight: 3\n\nName: L1_Furippa1_128x64\nMin butthurt: 0\nMax butthurt: 6\nMin level: 1\nMax level: 1\nWeight: 3\n\nName: L1_Read_books_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 1\nMax level: 1\nWeight: 3\n\nName: L1_Cry_128x64\nMin butthurt: 8\nMax butthurt: 13\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Boxing_128x64\nMin butthurt: 10\nMax butthurt: 13\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Mad_fist_128x64\nMin butthurt: 9\nMax butthurt: 13\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Mods_128x64\nMin butthurt: 0\nMax butthurt: 9\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Painting_128x64\nMin butthurt: 0\nMax butthurt: 7\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Leaving_sad_128x64\nMin butthurt: 14\nMax butthurt: 14\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Senpai_128x64\nMin butthurt: 0\nMax butthurt: 5\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Kaiju_128x64\nMin butthurt: 0\nMax butthurt: 10\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_My_dude_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L2_Wake_up_128x64\nMin butthurt: 0\nMax butthurt: 12\nMin level: 2\nMax level: 3\nWeight: 3\n\nName: L2_Furippa2_128x64\nMin butthurt: 0\nMax butthurt: 6\nMin level: 2\nMax level: 2\nWeight: 3\n\nName: L2_Hacking_pc_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 2\nMax level: 2\nWeight: 3\n\nName: L2_Soldering_128x64\nMin butthurt: 0\nMax butthurt: 10\nMin level: 2\nMax level: 2\nWeight: 3\n\nName: L2_Dj_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 2\nMax level: 3\nWeight: 3\n\nName: L3_Furippa3_128x64\nMin butthurt: 0\nMax butthurt: 6\nMin level: 3\nMax level: 3\nWeight: 3\n\nName: L3_Hijack_radio_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 3\nMax level: 3\nWeight: 3\n\nName: L3_Lab_research_128x54\nMin butthurt: 0\nMax butthurt: 10\nMin level: 3\nMax level: 3\nWeight: 3\n\nName: L1_Sad_song_128x64\nMin butthurt: 8\nMax butthurt: 13\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L2_Coding_in_the_shell_128x64\nMin butthurt: 0\nMax butthurt: 12\nMin level: 2\nMax level: 3\nWeight: 3\n\nName: L2_Secret_door_128x64\nMin butthurt: 0\nMax butthurt: 12\nMin level: 2\nMax level: 3\nWeight: 3\n\nName: L3_Freedom_2_dolphins_128x64\nMin butthurt: 0\nMax butthurt: 12\nMin level: 3\nMax level: 3\nWeight: 3\n\nName: L1_Akira_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L3_Intruder_alert_128x64\nMin butthurt: 0\nMax butthurt: 12\nMin level: 3\nMax level: 3\nWeight: 3\n\nName: L1_Procrastinating_128x64\nMin butthurt: 0\nMax butthurt: 8\nMin level: 1\nMax level: 3\nWeight: 3\n\nName: L1_Showtime_128x64\nMin butthurt: 0\nMax butthurt: 10\nMin level: 1\nMax level: 3\nWeight: 4\n\nName: L1_Doom_128x64\nMin butthurt: 0\nMax butthurt: 13\nMin level: 1\nMax level: 3\nWeight: 4\n"
  },
  {
    "path": "assets/dolphin/internal/L1_BadBattery_128x47/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 47\nPassive frames: 2\nActive frames: 0\nFrames order: 0 1\nActive cycles: 0\nFrame rate: 2\nDuration: 3600\nActive cooldown: 0\n\nBubble slots: 1\n\nSlot: 0\nX: 4\nY: 21\nText: I feel so sick!\\nI'm dying...\nAlignH: Right\nAlignV: Center\nStartFrame: 0\nEndFrame: 1\n"
  },
  {
    "path": "assets/dolphin/internal/L1_NoSd_128x49/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 49\nPassive frames: 10\nActive frames: 0\nFrames order: 0 1 0 1 0 2 3 4 3 5\nActive cycles: 0\nFrame rate: 2\nDuration: 3600\nActive cooldown: 0\n\nBubble slots: 1\n\nSlot: 0\nX: 40\nY: 18\nText: Need an\\nSD card\nAlignH: Right\nAlignV: Bottom\nStartFrame: 0\nEndFrame: 9\n"
  },
  {
    "path": "assets/dolphin/internal/L1_Tv_128x47/meta.txt",
    "content": "Filetype: Flipper Animation\nVersion: 1\n\nWidth: 128\nHeight: 47\nPassive frames: 6\nActive frames: 2\nFrames order: 0 1 2 3 4 5 6 7\nActive cycles: 2\nFrame rate: 2\nDuration: 3600\nActive cooldown: 5\n\nBubble slots: 2\n\nSlot: 0\nX: 1\nY: 23\nText: Take the red pill\nAlignH: Right\nAlignV: Bottom\nStartFrame: 7\nEndFrame: 9\n\nSlot: 1\nX: 1\nY: 23\nText: I can joke better\nAlignH: Right\nAlignV: Bottom\nStartFrame: 7\nEndFrame: 9\n"
  },
  {
    "path": "assets/dolphin/internal/manifest.txt",
    "content": "Filetype: Flipper Animation Manifest\nVersion: 1\n\n# Animation 1\nName: L1_Tv_128x47\nMin butthurt: 0\nMax butthurt: 14\nMin level: 1\nMax level: 3\nWeight: 3\n\n# Animation 2\nName: L1_BadBattery_128x47\nMin butthurt: 0\nMax butthurt: 14\nMin level: 1\nMax level: 3\nWeight: 3\n\n# Animation 3\nName: L1_NoSd_128x49\nMin butthurt: 0\nMax butthurt: 14\nMin level: 1\nMax level: 3\nWeight: 6\n\n"
  },
  {
    "path": "assets/icons/Animations/Levelup1_128x64/frame_rate",
    "content": "2\n"
  },
  {
    "path": "assets/icons/Animations/Levelup2_128x64/frame_rate",
    "content": "2\n"
  },
  {
    "path": "assets/icons/Common/Loading_24/frame_rate",
    "content": "5\n"
  },
  {
    "path": "assets/icons/Common/Round_loader_8x8/frame_rate",
    "content": "2"
  },
  {
    "path": "assets/icons/MainMenu/125khz_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/BadUsb_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/Debug_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/FileManager_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/GPIO_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/Infrared_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/NFC_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/Plugins_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/Settings_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/Sub1ghz_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/U2F_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/MainMenu/iButton_14/frame_rate",
    "content": "3"
  },
  {
    "path": "assets/icons/Settings/Alarm_47x39/frame_rate",
    "content": "2\n"
  },
  {
    "path": "assets/icons/SubGhz/SubGhz_External_ant/frame_rate",
    "content": "6"
  },
  {
    "path": "assets/icons/SubGhz/SubGhz_Internal_ant/frame_rate",
    "content": "6"
  },
  {
    "path": "documentation/.gitignore",
    "content": "/doxygen/build"
  },
  {
    "path": "documentation/AppManifests.md",
    "content": "# FAM (Flipper App Manifests) {#app_manifests}\n\nAll components of Flipper Zero firmware — services, user applications, and system settings — are developed independently. Each component has a build system manifest file named `application.fam`, which defines the basic properties of that component and its relations to other parts of the system.\n\nWhen building firmware, `fbt` collects all app manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](fbt.md) for details on build configurations.\n\n## App definition\n\nA firmware component's properties are declared in a Python code snippet, forming a call to the `App()` function with various parameters.\n\nOnly two parameters are mandatory: **appid** and **apptype**. Others are optional and may only be meaningful for certain app types.\n\n### Parameters\n\n- **appid**: string, app ID within the build system. It is used to specify which app to include in the build configuration and resolve dependencies and conflicts.\n\n- **apptype**: member of FlipperAppType.\\* enumeration. Valid values are:\n\n| Enum member | Firmware component type                                                                     |\n| ----------- | ------------------------------------------------------------------------------------------- |\n| SERVICE     | System service, created at early startup                                                    |\n| SYSTEM      | App is not being shown in any menus. It can be started by other apps or from CLI    |\n| APP         | Regular app for the main menu                                                       |\n| PLUGIN      | App to be built as a part of the firmware and to be placed in the Plugins menu      |\n| DEBUG       | App only visible in Debug menu with debug mode enabled                              |\n| ARCHIVE     | One and only Archive app                                                                    |\n| SETTINGS    | App to be placed in the system settings menu                                        |\n| STARTUP     | Callback function to run at system startup. Does not define a separate app                  |\n| EXTERNAL    | App to be built as `.fap` plugin                                                    |\n| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and app bundles |\n\n- **name**: name displayed in menus.\n- **entry_point**: C function to be used as the app's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern \"C\"` to use them as entry points.\n- **flags**: internal flags for system apps. Do not use.\n- **cdefines**: C preprocessor definitions to declare globally for other apps when the current app is included in the active build configuration. **For external apps**: specified definitions are used when building the app itself.\n- **requires**: list of app IDs to include in the build configuration when the current app is referenced in the list of apps to build.\n- **conflicts**: list of app IDs with which the current app conflicts. If any of them is found in the constructed app list, `fbt` will abort the firmware build process.\n- **provides**: functionally identical to **_requires_** field.\n- **stack_size**: stack size in bytes to allocate for an app on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `top` and `free` CLI commands to profile your app's memory usage._\n- **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware.\n- **order**: order of an app within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._\n- **sdk_headers**: list of C header files from this app's code to include in API definitions for external apps.\n- **targets**: list of strings and target names with which this app is compatible. If not specified, the app is built for all targets. The default value is `[\"all\"]`.\n- **resources**: name of a folder within the app's source folder to be used for packacking SD card resources for this app. They will only be used if app is included in build configuration. The default value is `\"\"`, meaning no resources are packaged.\n\n#### Parameters for external apps\n\nThe following parameters are used only for [FAPs](./AppsOnSDCard.md):\n\n- **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `[\"*.c*\"]` includes C and C++ source files. Apps cannot use the `\"lib\"` folder for their own source code, as it is reserved for **fap_private_libs**. Paths starting with `\"!\"` are excluded from the list of sources. They can also include wildcard characters and directory names. For example, a value of `[\"*.c*\", \"!plugins\"]` will include all C and C++ source files in the app folder except those in the `plugins` (and `lib`) folders. Paths with no wildcards (`*, ?`) are treated as full literal paths for both inclusion and exclusion.\n- **fap_version**: string, app version. The default value is \"0.1\". You can also use a tuple of 2 numbers in the form of (x,y) to specify the version. It is also possible to add more dot-separated parts to the version, like patch number, but only major and minor version numbers are stored in the built .fap.\n- **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file.\n- **fap_libs**: list of extra libraries to link the app against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption.\n- **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system.\n- **fap_description**: string, may be empty. Short app description.\n- **fap_author**: string, may be empty. App's author.\n- **fap_weburl**: string, may be empty. App's homepage.\n- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this app. These images will be preprocessed and built alongside the app. See [FAP assets](AppsOnSDCard.md) for details.\n- **fap_extbuild**: provides support for parts of app sources to be built by external tools. Contains a list of `ExtFile(path=\"file name\", command=\"shell command\")` definitions. `fbt` will run the specified command for each file in the list.\n- **fal_embedded**: boolean, default `False`. Applies only to PLUGIN type. If `True`, the plugin will be embedded into host app's .fap file as a resource and extracted to `apps_assets/APPID` folder on its start. This allows plugins to be distributed as a part of the host app.\n\nNote that commands are executed at the firmware root folder, and all intermediate files must be placed in an app's temporary build folder. For that, you can use pattern expansion by `fbt`: `${FAP_WORK_DIR}` will be replaced with the path to the app's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the app's source folder. You can also use other variables defined internally by `fbt`.\n\nExample for building an app from Rust sources:\n\n```python\n    sources=[\"target/thumbv7em-none-eabihf/release/libhello_rust.a\"],\n    fap_extbuild=(\n        ExtFile(\n            path=\"${FAP_WORK_DIR}/target/thumbv7em-none-eabihf/release/libhello_rust.a\",\n            command=\"cargo build --release --verbose --target thumbv7em-none-eabihf --target-dir ${FAP_WORK_DIR}/target --manifest-path ${FAP_SRC_DIR}/Cargo.toml\",\n        ),\n    ),\n```\n\n- **fap_private_libs**: list of additional libraries distributed as sources alongside the app. These libraries will be built as a part of the app build process.\n  Library sources must be placed in a subfolder of the `lib` folder within the app's source folder.\n  Each library is defined as a call to the `Lib()` function, accepting the following parameters:\n\n  - **name**: name of the library's folder. Required.\n  - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `[\".\"]`, meaning the library's source root.\n  - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `[\"*.c*\"]`.\n  - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`.\n  - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`.\n  - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the app's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`.\n\nExample for building an app with a private library:\n\n```python\n    fap_private_libs=[\n            Lib(\n                name=\"mbedtls\",\n                fap_include_paths=[\"include\"],\n                sources=[\n                    \"library/des.c\",\n                    \"library/sha1.c\",\n                    \"library/platform_util.c\",\n                ],\n                cdefines=[\"MBEDTLS_ERROR_C\"],\n            ),\n            Lib(\n                name=\"loclass\",\n                cflags=[\"-Wno-error\"],\n            ),\n        ],\n```\n\nFor that snippet, `fbt` will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, `fbt` will add `lib/mbedtls/include` to the list of include paths for the app and compile only the files specified in the `sources` list. Additionally, `fbt` will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources.\nFor the `loclass` library, `fbt` will add `lib/loclass` to the list of the included paths for the app and build all sources in that folder. Also, `fbt` will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases.\n\nBoth libraries will be linked with the app.\n\n## .fam file contents\n\nThe `.fam` file contains one or more app definitions. For example, here's a part of `applications/service/bt/application.fam`:\n\n```python\nApp(\n    appid=\"bt_start\",\n    apptype=FlipperAppType.STARTUP,\n    entry_point=\"bt_on_system_start\",\n    order=70,\n)\n\nApp(\n    appid=\"bt_settings\",\n    name=\"Bluetooth\",\n    apptype=FlipperAppType.SETTINGS,\n    entry_point=\"bt_settings_app\",\n    stack_size=1 * 1024,\n    requires=[\n        \"bt\",\n        \"gui\",\n    ],\n    order=10,\n)\n```\n\nFor more examples, see `.fam` files from various firmware parts.\n"
  },
  {
    "path": "documentation/AppsOnSDCard.md",
    "content": "# FAP (Flipper App Package) {#apps_on_sd_card}\n\n[fbt](./fbt.md) supports building apps as FAP files. FAPs are essentially `.elf` executables with extra metadata and resources bundled in.\n\nFAPs are built with the `faps` target. They can also be deployed to the `dist` folder with the `fap_dist` target.\n\nFAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).\n\n## How to set up an app to be built as a FAP {#fap-howto}\n\nFAPs are created and developed the same way as internal apps that are part of the firmware.\n\nTo build your app as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in app. Then configure its `application.fam` manifest, and set its `apptype` to `FlipperAppType.EXTERNAL`. See [Flipper App Manifests](AppManifests.md) for more details.\n\n- To build your app, run `./fbt fap_{APPID}`, where APPID is your app's ID in its manifest.\n- To build your app and upload it over USB to run on Flipper, use `./fbt launch APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a \"Launch App on Flipper\" build action (Ctrl+Shift+B menu).\n- To build an app without uploading it to Flipper, use `./fbt build APPSRC=applications_user/path/to/app`. This command is also available in VSCode configuration as \"Build App\".\n- To build all FAPs, run `./fbt faps` or `./fbt fap_dist`.\n\n## FAP assets\n\nFAPs can include static and animated images as private assets. They will be automatically compiled alongside app sources and can be referenced the same way as assets from the main firmware.\n\nTo use that feature, put your images in a subfolder inside your app's folder, then reference that folder in your app's manifest in the `fap_icon_assets` field. See [Flipper App Manifests](AppManifests.md) for more details.\n\nTo use these assets in your app, put `#include \"{APPID}_icons.h\"` in your app's source code, where `{APPID}` is the `appid` value field from your app's manifest. Then you can use all icons from your app's assets the same way as if they were a part of `assets_icons.h` of the main firmware.\n\nImages and animated icons should follow the same [naming convention](../assets/ReadMe.md) as those from the main firmware.\n\n## Debugging FAPs\n\n`fbt` includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by `fbt` and stock VS Code configurations.\n\nWith it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc.\n\nIf debugging session is active, firmware will trigger a breakpoint after loading a FAP into memory, but before running any code from it. This allows you to set breakpoints in the FAP's code. Note that any breakpoints set before the FAP is loaded may need re-setting after the FAP is actually loaded, since the debugger cannot know the exact address of the FAP's code before loading the FAP.\n\n### Setting up debugging environment\n\nThe debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](fbt.md) for details.\n\nTo debug FAPs, do the following:\n\n1. Build firmware with `./fbt`\n2. Flash it with `./fbt flash`\n3. [Build your FAP](#fap-howto) and run it on Flipper\n\nAfter that, you can attach the debugger to the target MCU with `./fbt debug` or VS Code and use all debug features.\n\nIt is **important** that firmware and app build type (debug/release) match and that the matching firmware folder is linked as `build/latest`. Otherwise, debugging will not work.\n\n## How Flipper runs an app from an SD card\n\nFlipper's MCU cannot run code directly from external storage, so it needs to be copied to RAM first. That is done by the App Loader responsible for loading the FAP from the SD card, verifying its integrity and compatibility, copying it to RAM, and adjusting it for its new location.\n\nSince the FAP has to be loaded to RAM to be executed, the amount of RAM available for allocations from heap is reduced compared to running the same app from flash, as a part of the firmware. Note that the amount of occupied RAM is less than the total FAP file size since only code and data sections are allocated, while the FAP file includes extra information only used at app load time.\n\nApps are built for a specific API version. It is a part of the hardware target's definition and contains a major and minor version number. The App Loader checks if the app's major API version matches the firmware's major API version.\n\nThe App Loader allocates memory for the app and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the app.\n\n## API versioning {#api-versioning}\n\nNot all parts of firmware are available for external apps. A subset of available functions and variables is defined in the \"api_symbols.csv\" file, which is a part of the firmware target definition in the `targets/` directory.\n\n`fbt` uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added.\n\nBreaking changes include:\n\n- Removing a function or a global variable\n- Changing the signature of a function\n\nAPI versioning is mostly automated by `fbt`. When rebuilding the firmware, `fbt` checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a \"`?`\" mark, and the user is supposed to change the mark to \"`+`\" for the entry to be exposed for FAPs, or to \"`-`\" for it to be unavailable.\n\n`fbt` will not allow building a firmware until all \"`?`\" entries are changed to \"`+`\" or \"`-`\".\n\n**NB:** `fbt` automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing \"`+`\" entries are to be changed to \"`-`\".\n\n### Symbol table {#symbol-table}\n\nThe symbol table is a list of symbols exported by firmware and available for external apps. It is generated by `fbt` from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` app.\n\n`fbt` also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The app won't be able to run on the device until all required symbols are provided in the symbol table.\n"
  },
  {
    "path": "documentation/ExpansionModules.md",
    "content": "# Expansion Module Protocol {#expansion_protocol}\n\n## Terms and definitions\n\n- Expansion Module: A third-party hardware unit meant for use with Flipper Zero by connecting it to its GPIO header.\n- Expansion Module Protocol: A serial-based, byte-oriented, synchronous communication protocol described in this document.\n- Host: Hardware unit tasked with serving requests. Used interchangeably with Flipper, Server, Host etc. throughout this document.\n- Device: Used interchangeably with Expansion Module, Module, Client, etc.\n- RPC: Remote Procedure Call, a protobuf-based communication protocol widely used by Flipper Zero companion applications.\n- Timeout Interval: Period of inactivity to be treated as a loss of connection, also denoted as Tto. Equals to 250 ms.\n- Baud Rate Switch Dead Time: Period of time after baud rate change during which no communication is allowed, also denoted Tdt. Equals to 25 ms.\n\n## Features\n\n- Automatic expansion module detection\n- Baud rate negotiation\n- Basic error detection\n- Request-response communication flow\n- Integration with Flipper RPC protocol\n\n## Hardware\n\nDepending on the UART selected for communication, the following pins area available for the expansion modules to connect to:\n\n| UART   | Tx pin | Rx pin |\n|--------|--------|--------|\n| USART  | 13     | 14     |\n| LPUART | 15     | 16     |\n\n## Frame structure\n\nEach frame consists of a header (1 byte), contents (size depends on frame type) and checksum (1 byte) fields:\n\n| Header (1 byte) | Contents (0 or more bytes) | Checksum (1 byte) |\n|-----------------|----------------------------|-------------------|\n| Frame type      | Frame payload              | XOR checksum      |\n\n### Heartbeat frame\n\nHEARTBEAT frames are used to maintain an idle connection. In the event of not receiving any frames within Tto, either side must cease all communications and be ready to initiate the connection again.\n\n| Header (1 byte) | Checksum (1 byte) |\n|-----------------|-------------------|\n| 0x01            | XOR checksum      |\n\nNote that the contents field is not present (0 bytes length).\n\n### Status frame\n\nSTATUS frames are used to report the status of a transaction. Every received frame MUST be confirmed by a matching STATUS response.\n\n| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) |\n|-----------------|-------------------|-------------------|\n| 0x02            | Error code        | XOR checksum      |\n\nThe `Error code` field SHALL have one of the following values:\n\n| Error code | Meaning                 |\n|------------|-------------------------|\n| 0x00       | OK (No error)           |\n| 0x01       | Unknown error           |\n| 0x02       | Baud rate not supported |\n\n### Baud rate frame\n\nBAUD RATE frames are used to negotiate communication speed. The initial connection SHALL always happen at 9600 baud. The first message sent by the module MUST be a BAUD RATE frame, even if a different speed is not required.\n\n| Header (1 byte) | Contents (4 bytes) | Checksum (1 byte) |\n|-----------------|--------------------|-------------------|\n| 0x03            | Baud rate          | XOR checksum      |\n\nIf the requested baud rate is supported by the host, it SHALL respond with a STATUS frame with an OK error code, otherwise the error code SHALL be 0x02 (Baud rate not supported). Until the negotiation succeeds, the speed SHALL remain at 9600 baud. The module MAY send additional BAUD RATE frames with alternative speeds in case the initial request was refused. No other frames are allowed until the speed negotiation succeeds.\n\n### Control frame\n\nCONTROL frames are used to control various aspects of the communication and enable/disable various device features.\n\n| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) |\n|-----------------|-------------------|-------------------|\n| 0x04            | Command           | XOR checksum      |\n\nThe `Command` field SHALL have one of the following values:\n\n| Command | Meaning                  | Note |\n|---------|--------------------------|:----:|\n| 0x00    | Start RPC session        | 1    |\n| 0x01    | Stop RPC session         | 2    |\n| 0x02    | Enable OTG (5V) on GPIO  | 3    |\n| 0x03    | Disable OTG (5V) on GPIO | 3    |\n\nNotes:\n\n1. Must only be used while the RPC session NOT active.\n2. Must only be used while the RPC session IS active.\n3. See 1, otherwise OTG is to be controlled via RPC messages.\n\n### Data frame\n\nDATA frames are used to transmit arbitrary data in either direction. Each DATA frame can hold up to 64 bytes. If an RPC session is currently open, all received bytes are forwarded to it.\n\n| Header (1 byte) | Contents (1 to 65 byte(s)) | Checksum (1 byte) |\n|-----------------|----------------------------|-------------------|\n| 0x05            | Data                       | XOR checksum      |\n\nThe `Data` field SHALL have the following structure:\n\n| Data size (1 byte) | Data (0 to 64 bytes) |\n|--------------------|----------------------|\n| 0x00 ... 0x40      | Arbitrary data       |\n\n## Communication flow\n\nIn order for the host to be able to detect the module, the respective feature must be enabled first. This can be done via the GUI by going to `Settings → Expansion Modules` and selecting the required `Listen UART` or programmatically by calling `expansion_enable()`. Likewise, disabling this feature via the same GUI or by calling `expansion_disable()` will result in ceasing all communications and not being able to detect any connected modules.\n\nThe communication is always initiated by the module by the means of shortly pulling the RX pin down. The host SHALL respond with a HEARTBEAT frame indicating that it is ready to receive requests. The module then MUST issue a BAUDRATE request within Tto. Failure to do so will result in the host dropping the connection and returning to its initial state.\n\n```\n        MODULE               |            FLIPPER\n-----------------------------+---------------------------\n                             |        (Start)\nPull down RX                -->\n                            <--       Heartbeat\nBaud Rate                   -->\n                            <--       Status [OK | Error]\n                             |\n(Module changes baud rate    |        (Flipper changes \n and waits for Tdt)          |         baud rate)\n                             |\nControl [Start RPC]         -->\n                            <--       Status [OK | Error]\n-----------------------------+--------------------------- (1)\nData [RPC Request]          -->\n                            <--       Status [OK | Error]\n                            <--       Data [RPC Response]\nStatus [OK | Error]         -->\n-----------------------------+--------------------------- (2)\nData [RPC Request pt.1]     -->\n                            <--       Status [OK | Error]\nData [RPC Request pt.2]     -->\n                            <--       Status [OK | Error]\nData [RPC Request pt.3]     -->\n                            <--       Status [OK | Error]\n                            <--       Data [RPC Response]\nStatus [OK | Error]         -->\n-----------------------------+--------------------------- (3)\nHeartbeat                   -->\n                            <--       Heartbeat\nHeartbeat                   -->\n                            <--       Heartbeat\n-----------------------------+---------------------------\nControl [Stop RPC]          -->\n                            <--       Status [OK | Error]\n(Module disconnected)        |\n                             |        (No activity within Tto\n                             |            return to start)\n\n(1) The module MUST confirm all implicitly requested frames (e.g. DATA frames containing RPC responses) with a STATUS frame.\n(2) RPC requests larger than 64 bytes are split into multiple frames. Every DATA frame MUST be confirmed with a STATUS frame.\n(3) When the module has no data to send, it MUST send HEARTBEAT frames with a period < Tto in order to maintain the connection.\n    The host SHALL respond with a HEARTBEAT frame each time.\n```\n\n## Error detection\n\nError detection is implemented via adding an extra checksum byte to every frame (see above).\n\nThe checksum is calculated by bitwise XOR-ing every byte in the frame (excluding the checksum byte itself), with an initial value of 0.\n\n### Error recovery behaviour\n\nIn the event of a detected error, the concerned side MUST cease all communications and reset to initial state. The other side will then experience\na communication timeout and the connection will be re-established automatically.\n"
  },
  {
    "path": "documentation/FuriCheck.md",
    "content": "# Run time checks and forced system crash {#furi_check}\n\nThe best way to protect system integrity is to reduce amount cases that we must handle and crash the system as early as possible.\nFor that purpose, we have a bunch of helpers located in Furi Core `check.h`.\n\n## Couple notes before start\n\n- Definition of Crash — log event, save crash information in RTC and reboot the system.\n- Definition of Halt — log event, stall the system.\n- Debug and production builds behave differently: debug build will never reset system in order to preserve state for debugging.\n- If you have debugger connected we will stop before reboot automatically.\n- All helpers accept optional MESSAGE_CSTR: it can be in RAM or Flash memory, but only messages from Flash will be shown after system reboot.\n- MESSAGE_CSTR can be NULL, but macros magic already doing it for you, so just don't.\n\n## `furi_assert(CONDITION)` or `furi_assert(CONDITION, MESSAGE_CSTR)`\n\nAssert condition in development environment and crash the system if CONDITION is false.\n\n- Should be used at development stage in apps and services.\n- Keep in mind that release never contains this check.\n- Keep in mind that libraries never contain this check by default, use `LIB_DEBUG=1` if you need it.\n- Avoid putting function calls into CONDITION, since it may be omitted in some builds.\n\n## `furi_check(CONDITION)` or `furi_check(CONDITION, MESSAGE_CSTR)`\n\nAlways assert condition and crash the system if CONDITION is false.\n\n- Use it if you always need to check conditions\n\n## `furi_crash()` or `furi_crash(MESSAGE_CSTR)`\n\nCrash the system.\n\n- Use it to crash the system. For example, if an abnormal condition is detected.\n\n## `furi_halt()` or `furi_halt(MESSAGE_CSTR)`\n\nHalt the system.\n\n- We use it internally to shutdown Flipper if poweroff is not possible.\n"
  },
  {
    "path": "documentation/FuriHalBus.md",
    "content": "# Using FuriHalBus API {#furi_hal_bus}\n\n## Basic info\n\nOn system startup, most of the peripheral devices are under reset and not clocked by default. This is done to reduce power consumption and to guarantee that the device will always be in the same state before use.\nSome crucial peripherals are enabled right away by the system, others must be explicitly enabled by the user code.\n\n**NOTE:** Here and afterwards, the word *\"system\"* refers to any code belonging to the operating system, hardware drivers or built-in apps.\n\nTo **ENABLE** a peripheral, call `furi_hal_bus_enable()`. At the time of the call, the peripheral in question MUST be disabled, otherwise a crash will occur to indicate improper use. This means that any given peripheral cannot be enabled twice or more without disabling it first.\n\nTo **DISABLE** a peripheral, call `furi_hal_bus_disable()`. Likewise, the peripheral in question MUST be enabled, otherwise a crash will occur.\n\nTo **RESET** a peripheral, call `furi_hal_bus_reset()`. The peripheral in question MUST be enabled, otherwise a crash will occur. This method is used whenever it is necessary to reset all the peripheral's registers to their initial states without disabling it.\n\n## Peripherals\n\nBuilt-in peripherals are divided into three categories: \n- Enabled by the system on startup, never disabled;\n- Enabled and disabled by the system on demand;\n- Enabled and disabled by the user code.\n\n### Always-on peripherals\n\nBelow is the list of peripherals that are enabled by the system. The user code must NEVER attempt to disable them. If a corresponding API is provided, the user code must employ it in order to access the peripheral.\n\n*Table 1* — Peripherals enabled by the system\n\n| Peripheral    | Enabled at                |\n| :-----------: | :-----------------------: |\n| DMA1          | `furi_hal_dma.c`          |\n| DMA2          | --                        |\n| DMAMUX        | --                        |\n| GPIOA         | `furi_hal_resources.c`    |\n| GPIOB         | --                        |\n| GPIOC         | --                        |\n| GPIOD         | --                        |\n| GPIOE         | --                        |\n| GPIOH         | --                        |\n| PKA           | `furi_hal_bt.c`           |\n| AES2          | --                        |\n| HSEM          | --                        |\n| IPCC          | --                        |\n| FLASH         | enabled by hardware       |\n\n### On-demand system peripherals\n\nBelow is the list of peripherals that are enabled and disabled by the system. The user code must avoid using them directly, preferring the respective APIs instead.\n\nWhen not using the API, these peripherals MUST be enabled by the user code and then disabled when not needed anymore.\n\n*Table 2* — Peripherals enabled and disabled by the system\n\n| Peripheral    | API header file       |\n| :-----------: | :-------------------: |\n| RNG           | `furi_hal_random.h`   |\n| SPI1          | `furi_hal_spi.h`      |\n| SPI2          | --                    |\n| I2C1          | `furi_hal_i2c.h`      |\n| I2C3          | --                    |\n| USART1        | `furi_hal_serial.h`   |\n| LPUART1       | --                    |\n| USB           | `furi_hal_usb.h`      |\n\n### On-demand shared peripherals\n\nBelow is the list of peripherals that are not enabled by default and MUST be enabled by the user code each time it accesses them. \n\nNote that some of these peripherals may also be used by the system to implement its certain features.\nThe system will take over any given peripheral only when the respective feature is in use.\n\n*Table 3* — Peripherals enabled and disabled by user\n\n| Peripheral    | System    | Purpose                               |\n| :-----------: | :-------: | ------------------------------------- |\n| CRC           |           |                                       |\n| TSC           |           |                                       |\n| ADC           |           |                                       |\n| QUADSPI       |           |                                       |\n| TIM1          | yes       | subghz, lfrfid, nfc, infrared, etc... |\n| TIM2          | yes       | subghz, infrared, etc...              |\n| TIM16         | yes       | speaker                               |\n| TIM17         | yes       | cc1101_ext                            |\n| LPTIM1        | yes       | tickless idle timer                   |\n| LPTIM2        | yes       | pwm                                   |\n| SAI1          |           |                                       |\n| LCD           |           |                                       |\n\n\n## DMA\n\nThe DMA1,2 peripherals are a special case in that they have multiple independent channels. Some of the channels may be in use by the system.\n\nBelow is the list of DMA channels and their usage by the system.\n\n*Table 4* — DMA channels\n\n| DMA   | Channel   | System    | Purpose                   |\n| :---: | :-------: | :-------: | ------------------------- |\n| DMA1  | 1         | yes       | digital signal            |\n|  --   | 2         | yes       | --                        |\n|  --   | 3         |           |                           |\n|  --   | 4         | yes       | pulse reader              |\n|  --   | 5         |           |                           |\n|  --   | 6         | yes       | USART_Rx                  |\n|  --   | 7         | yes       | LPUART_Rx                 |\n| DMA2  | 1         | yes       | infrared, lfrfid, subghz, |\n|  --   | 2         | yes       | --                        |\n|  --   | 3         | yes       | cc1101_ext                |\n|  --   | 4         | yes       | cc1101_ext                |\n|  --   | 5         | yes       | cc1101_ext                |\n|  --   | 6         | yes       | SPI                       |\n|  --   | 7         | yes       | SPI                       |\n"
  },
  {
    "path": "documentation/FuriHalDebugging.md",
    "content": "# Furi HAL Debugging {#furi_hal_debugging}\n\nSome Furi subsystems have additional debugging features that can be enabled by adding additional defines to firmware compilation.\nUsually, they are used for low level tracing and profiling or signal redirection/duplication.\n\n\n## FuriHalOs\n\n`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow.\n\nThere are 3 signals that will be exposed to external GPIO pins:\n\n- `AWAKE`   — `PA7` — High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode.\n- `TICK`    — `PA6` — Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling.\n- `SECOND`  — `PA4` — Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conform Hard RT.\n\n\n\n## FuriHalPower\n\n`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing.\n\nThere are 2 signals that will be exposed to external GPIO pins:\n\n- `WFI`     — `PB2` — Light sleep (wait for interrupt) used. Basically, this is the lightest and most non-breaking things power save mode. All functions and debug should work correctly in this mode.\n- `STOP`    — `PC3` — STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.\n\n## FuriHalSD\n\n`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging.\n"
  },
  {
    "path": "documentation/HardwareTargets.md",
    "content": "## What a Firmware Target is {#hardware_targets}\n\nFlipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_.\n\nTarget-specific files are placed in a single sub-folder in `targets`. It must contain a target definition file, `target.json`, and may contain other files if they are referenced by current target's definition. By default, `fbt` gathers all source files in target folder, unless they are explicitly excluded.\n\nTargets can inherit most code parts from other targets, to reduce common code duplication.\n\n\n## Target Definition File\n\nA target definition file, `target.json`, is a JSON file that can contain the following fields:\n\n* `include_paths`: list of strings, folder paths relative to current target folder to add to global C/C++ header path lookup list.\n* `sdk_header_paths`: list of strings, folder paths relative to current target folder to gather headers from for including in SDK.\n* `startup_script`: filename of a startup script, performing initial hardware initialization.\n* `linker_script_flash`: filename of a linker script for creating the main firmware image.\n* `linker_script_ram`: filename of a linker script to use in \"updater\" build configuration.\n* `linker_script_app`: filename of a linker script to use for linking .fap files.\n* `sdk_symbols`: filename of a .csv file containing current SDK configuration for this target.\n* `linker_dependencies`: list of libraries to link the firmware with. Note that those not in the list won't be built by `fbt`. Also several link passes might be needed, in such case you may need to specify same library name twice.\n* `inherit`: string, specifies hardware target to borrow main configuration from. Current configuration may specify additional values for parameters that are lists of strings, or override values that are not lists.\n* `excluded_sources`: list of filenames from the inherited configuration(s) NOT to be built.\n* `excluded_headers`: list of headers from the inherited configuration(s) NOT to be included in generated SDK.\n* `excluded_modules`: list of strings specifying fbt library (module) names to exclude from being used to configure build environment.\n\n\n## Apps & Hardware\n\nNot all apps are available on different hardware targets. \n\n* For apps built into the firmware, you have to specify a compatible app set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md) for details on build configurations.\n\n* For apps built as external FAPs, you have to explicitly specify compatible targets in the app's manifest, `application.fam`. For example, to limit the app to a single target, add `targets=[\"f7\"],` to the manifest. It won't be built for other targets.\n\nFor details on app manifests, check out [their docs page](./AppManifests.md).\n\n\n## Building Firmware for a Specific Target\n\nYou have to specify TARGET_HW (and, optionally, FIRMWARE_APP_SET) for `fbt` to build firmware for a non-default target. For example, building and flashing debug firmware for f18 can be done with\n\n    ./fbt TARGET_HW=18 flash_usb_full\n\n"
  },
  {
    "path": "documentation/KeyCombo.md",
    "content": "# Key Combos {#key_combos}\n\nThere are times when your Flipper feels blue and doesn't respond to any of your commands due to a software issue. This guide will help you solve this problem.\n\n## Basic combos\n\n### Hardware reset\n\n- Press `LEFT` and `BACK` and hold for a couple of seconds\n- Release `LEFT` and `BACK`\n\nThis combo performs a hardware reset by pulling the MCU reset line down.\nMain components involved: Keys → DD8(NC7SZ32M5X, OR-gate) → DD1(STM32WB55, MCU).\n\nIt won't work only in one case:\n\n- The MCU debug block is active and holding the reset line from inside.\n\n### Hardware Power Reset\n\n- Disconnect the USB cable and any external power supplies\n- Disconnect the USB once again\n- Make sure you've disconnected the USB and any external power supplies\n- Press `BACK` and hold for 30 seconds (this will only work with the USB disconnected)\n- If you haven't disconnected the USB, then disconnect it and repeat the previous step\n- Release the `BACK` key\n\nThis combo performs a reset by switching SYS power line off and then on.\nMain components involved: Keys → DD6(bq25896, charger).\n\nIt won't work only in one case:\n\n- Power supply is connected to USB or 5V_ext\n\n### Software DFU\n\n- Press `LEFT` on boot to enter DFU with Flipper boot-loader\n\nIt won't work only in one case:\n\n- Flipper boot-loader is damaged or absent\n\n### Hardware DFU\n\n- Press `OK` on boot to enter DFU with ST boot-loader\n\nIt won't work only in one case:\n\n- Option Bytes are damaged or set to ignore the `OK` key\n\n## DFU combos\n\n### Hardware Reset + Software DFU\n\n- Press `LEFT` and `BACK` and hold for a couple of seconds\n- Release `BACK`\n- Device will enter DFU with an indication (Blue LED + DFU Screen)\n- Release `LEFT`\n\nThis combo performs a hardware reset by pulling the MCU reset line down. Then, the `LEFT` key indicates to the boot-loader that DFU mode is requested.\n\nIt won't work in two cases:\n\n- The MCU debug block is active and holding the reset line from inside\n- Flipper boot-loader is damaged or absent\n\n### Hardware Reset + Hardware DFU\n\n- Press `LEFT`, `BACK` and `OK` and hold for a couple of seconds\n- Release `BACK` and `LEFT`\n- The device will enter DFU without an indication\n\nThis combo performs a hardware reset by pulling the MCU reset line down. Then, the `OK` key forces MCU to load the internal boot-loader.\n\nIt won't work in two cases:\n\n- The MCU debug block is active and holding the reset line from inside\n- Option Bytes are damaged or set to ignore the `OK` key\n\n### Hardware Power Reset + Software DFU\n\n- Disconnect the USB and any external power supplies\n- Press `BACK` and `LEFT` for 30 seconds\n- Release `BACK`\n- The device will enter DFU with an indication (Blue LED + DFU Screen)\n- Release `LEFT`\n- Plug in the USB\n\nThis combo performs a reset by switching the SYS power line off and then on. Next, the `LEFT` key indicates to the boot-loader that DFU mode is requested.\n\nIt won't work in two cases:\n\n- Power supply is connected to USB or 5V_ext\n- Flipper boot-loader is damaged or absent\n\n### Hardware Power Reset + Hardware DFU\n\n- Disconnect the USB and any external power supplies\n- Press `BACK` and `OK` and hold for 30 seconds\n- Release `BACK` and `OK`\n- The device will enter DFU without indication\n- Plug in the USB\n\nThis combo performs a reset by switching the SYS power line off and then on. Next, the `OK` key forces MCU to load the internal boot-loader.\n\nIt won't work in two cases:\n\n- Power supply is connected to USB or 5V_ext\n- Option Bytes are damaged or set to ignore the `OK` key\n\n# Alternative ways to recover your device\n\nIf none of the described methods helped you:\n\n- Make sure the battery charged\n- Disconnect the battery and connect again (requires disassembly)\n- Try to flash the device with ST-Link or another programmer that supports SWD\n\nIf you're still here and your device is not working: it's not a software issue.\n"
  },
  {
    "path": "documentation/LFRFIDRaw.md",
    "content": "# Reading RAW RFID data {#lfrfid_raw}\n\nFlipper Zero has the option to read RAW data from 125 kHz cards that allows you to record the card's data and save it, similar to how a dictaphone records sound.\n\nTo use this function, you need to activate the Debug mode on your Flipper Zero by doing the following:\n\n1. Go to **Main Menu** → **Settings** → **System**.\n\n2. Set **Debug** to **ON**.\n\nOnce the Debug mode is activated on your Flipper Zero, you can read RAW data from 125 kHz RFID cards:\n\n1. Go to **Main Menu** → **125 kHz RFID** → **Extra Actions**.\n\n2. Select **RAW RFID** data and name the raw file.\n\n3. Read instructions and press **OK**.\n\n4. Apply the card to Flipper Zero's back.\n\n5. Once the reading is finished, press **OK**.\n\nTwo files with data (with ASK and PSK modulations) will be saved in the `lfrfid` folder on the microSD card. Now, you can share it and the card's photo with developers by creating an issue on GitHub.\n"
  },
  {
    "path": "documentation/OTA.md",
    "content": "# Flipper Zero OTA update process {#ota_updates}\n\n## Executing code from RAM\n\nIn Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. The system image executing in RAM has full write access to Flipper's entire flash memory — something that's not possible when running main code from the same flash.\n\nWe leverage that boot mode to perform OTA firmware updates, including operations on a radio stack running on the second MCU core.\n\n## How does Flipper OTA work?\n\nInstallation of OTA updates goes through 3 stages:\n\n### 1. Backing up internal storage (/int)\n\nIt is a special partition of Flipper's flash memory, taking up all available space not used by the firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss.\n\nSo, before taking any action on the firmware, we back up the current configuration from `/int` into a plain tar archive on the SD card.\n\n### 2. Performing device update\n\nThe main firmware loads an updater image — a customized build of the main Flipper firmware — into RAM and runs it. Updater performs operations on system flash as described by an Update manifest file.\n\nFirst, if there's a Radio stack image bundled with the update, updater compares its version with the currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software FUS running on Core2, and leads to a series of system restarts.\n\nThen, updater validates and corrects Option Bytes — a special memory region containing low-level configuration for Flipper's MCU.\n\nAfter that, updater loads a `.dfu` file with firmware to be flashed, checks its integrity using CRC32, writes it to system flash and validates written data.\n\n### 3. Restoring internal storage and updating resources\n\nAfter performing operations on flash memory, the system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents.\n\nIf the update package contains an additional resources archive, it is extracted onto the SD card.\n\n## Update manifest\n\nAn update package comes with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs.\n\n### Mandatory fields\n\nAn update manifest must contain the following keys in the given order:\n\n- **Filetype**: a constant string, \"Flipper firmware upgrade configuration\".\n\n- **Version**: manifest version. The current value is 2.\n\n- **Info**: arbitrary string, describing package contents.\n\n- **Target**: hardware revision for which the package is built.\n\n- **Loader**: file name of stage 2 loader that is executed from RAM.\n\n- **Loader CRC**: CRC32 of loader file. Note that it is represented in little-endian hex.\n\n### Optional fields\n\nOther fields may have empty values. In this case, updater skips all operations related to these values.\n\n- **Radio**: file name of radio stack image, provided by STM.\n\n- **Radio address**: address to install the radio stack at. It is specified in Release Notes by STM.\n\n- **Radio version**: radio major, minor and sub versions followed by branch, release and stack type packed into 6 hex-encoded bytes.\n\n- **Radio CRC**: CRC32 of radio image.\n\n- **Resources**: file name of TAR archive with resources to be extracted onto the SD card.\n\n- **OB reference**, **OB mask**, **OB write mask**: reference values for validating and correcting option bytes.\n\n## OTA update error codes\n\nWe designed the OTA update process to be as fail-safe as possible. We don't start any risky operations before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state.\n\nEven if something goes wrong, updater allows you to retry failed operations and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes the failed operation, and `YY` contains extra details on its progress where the error occurred.\n\n|    Stage description    |   Code | Progress   | Description                                |\n| :---------------------: | -----: | ---------- | ------------------------------------------ |\n| Loading update manifest |  **1** | **13**     | Updater reported hardware version mismatch |\n|                         |        | **20**     | Failed to get saved manifest path          |\n|                         |        | **30**     | Failed to load manifest                    |\n|                         |        | **40**     | Unsupported update package version         |\n|                         |        | **50**     | Package has mismatching HW target          |\n|                         |        | **60**     | Missing DFU file                           |\n|                         |        | **80**     | Missing radio firmware file                |\n| Backing up configuration|  **2** | **0-100**  | FS read/write error                        |\n|    Checking radio FW    |  **3** | **0-99**   | Error reading radio firmware file          |\n|                         |        | **100**    | CRC mismatch                               |\n|  Uninstalling radio FW  |  **4** | **0**      | SHCI Delete command error                  |\n|                         |        | **80**     | Error awaiting command status              |\n|    Writing radio FW     |  **5** | **0-100**  | Block read/write error                     |\n|   Installing radio FW   |  **6** | **10**     | SHCI Install command error                 |\n|                         |        | **80**     | Error awaiting command status              |\n|      Core2 is busy      |  **7** | **10**     | Couldn't start C2                          |\n|                         |        | **20**     | Failed to switch C2 to FUS mode            |\n|                         |        | **30**     | Error in FUS operation                     |\n|                         |        | **50**     | Failed to switch C2 to stack mode          |\n|  Validating opt. bytes  |  **8** | **yy**     | Option byte code                           |\n|    Checking DFU file    |  **9** | **0**      | Error opening DFU file                     |\n|                         |        | **1-98**   | Error reading DFU file                     |\n|                         |        | **99-100** | Corrupted DFU file                         |\n|      Writing flash      | **10** | **0-100**  | Block read/write error                     |\n|    Validating flash     | **11** | **0-100**  | Block read/write error                     |\n| Restoring configuration | **12** | **0-100**  | FS read/write error                        |\n|   Updating resources    | **13-15** | **0-100**  | SD card read/write error                   |\n\n## Building update packages\n\n### Full package\n\nTo build a full update package, including firmware, radio stack and resources for the SD card, run:\n\n`./fbt COMPACT=1 DEBUG=0 updater_package`\n\n### Minimal package\n\nTo build a minimal update package, including only firmware, run:\n\n`./fbt COMPACT=1 DEBUG=0 updater_minpackage`\n\n### Customizing update bundles\n\nDefault update packages are built with Bluetooth Light stack.\nYou can pick a different stack if your firmware version supports it, and build a bundle with it by passing the stack type and binary name to `fbt`:\n\n`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full`\n\nNote that `COPRO_OB_DATA` must point to a valid file in the `scripts` folder containing reference Option Byte data matching your radio stack type.\n\nIn certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line.\n\n### Building partial update packages\n\nYou can customize package contents by calling `scripts/update.py` directly.\nFor example, to build a package only for installing BLE FULL stack:\n\n```shell\nscripts/update.py generate \\\n  -t f7 -d r13.3_full -v \"BLE FULL 13.3\" \\\n  --stage dist/f7/flipper-z-f7-updater-*.bin \\\n  --radio lib/stm32wb_copro/firmware/stm32wb5x_BLE_Stack_full_fw.bin \\\n  --radiotype ble_full\n```\n\nFor the full list of options, check `scripts/update.py generate` help.\n"
  },
  {
    "path": "documentation/UnitTests.md",
    "content": "# Unit tests {#unit_tests}\n\n## Intro\n\nUnit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they are correct.\nThey are crucial for writing robust, bug-free code.\n\nFlipper Zero firmware includes a separate app called [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests).\nIt is run directly on Flipper devices in order to employ their hardware features and rule out any platform-related differences.\n\nWhen contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features.\nRunning existing unit tests is useful to ensure that the new code doesn't introduce any regressions.\n\n## Running unit tests\n\nTo run the unit tests, follow these steps:\n\n1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests updater_package`.\n2. Flash the firmware using your preferred method, including SD card resources (`build/latest/resources`).\n3. Launch the CLI session and run the `unit_tests` command.\n\n**NOTE:** To run a particular test (and skip all others), specify its name as the command argument.\nTest names match application names defined [here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/application.fam).\n\n## Adding unit tests\n\n### General\n\n#### Entry point\n\nThe common entry point for all tests is the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests) app. Test-specific code is packaged as a `PLUGIN` app placed in a subdirectory of `tests` in the `unit_tests` mother-app and referenced in the common `application.fam`. Look at other tests for an example.\n\n#### Test assets\n\nSome unit tests require external data in order to function. These files (commonly called assets) reside in the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.).\n\n### App-specific\n\n#### Infrared\n\nEach infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol.\nTo add unit tests for your protocol, follow these steps:\n\n1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory.\n2. Fill it with the test data (more on it below).\n3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/tests/infrared/infrared_test.c).\n4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass.\n\n##### Test data format\n\nEach unit test has three sections:\n\n1. `decoder` — takes in a raw signal and outputs decoded messages.\n2. `encoder` — takes in decoded messages and outputs a raw signal.\n3. `encoder_decoder` — takes in decoded messages, turns them into a raw signal, and then decodes again.\n\nInfrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions.\nDecoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder, these two are switched.\n\nDecoded data is represented in arrays (since a single raw signal may be decoded into several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples.\n\n##### Getting raw signals\n\nRecording raw IR signals is possible using Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards the Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format.\n"
  },
  {
    "path": "documentation/UniversalRemotes.md",
    "content": "# Universal Remotes {#universal_remotes}\n\n## Televisions\n\nAdding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV.\n\nEach signal is recorded using the following algorithm:\n\n1. Get the remote and point it to Flipper's IR receiver.\n2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise.\n3. Press a remote button and save it under a corresponding name.\n4. Repeat steps 2-3 until all required signals are saved.\n\nThe signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to.\n\nIf everything checks out, append these signals **to the end** of the [TV universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir).\n\n## Audio players\n\nAdding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player.\n\nThe signal names are self-explanatory.\nOn many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`.\nMake sure that every signal does what it's supposed to.\n\nIf everything checks out, append these signals **to the end** of the [audio player universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir).\n\n## Projectors\n\nAdding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector.\nTo save time, please make sure every recording has been named accordingly.\nIn case of omitting, on most projectors with the 4 following buttons, you should not have a problem.\n\n\n## Air conditioners\n\nAir conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.\nThe majority of A/C remotes have a small display that shows the current mode, temperature, and other settings.\nWhen the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.\n\nIn order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`.\nEach signal (except `Off`) is recorded using the following algorithm:\n\n1. Get the remote and press the **POWER** button so that the display shows that A/C is ON.\n2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable).\n3. Press the **POWER** button to switch the A/C off.\n4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.\n5. Point the remote to Flipper's IR receiver as directed and press the **POWER** button once again.\n6. Save the resulting signal under the specified name.\n7. Repeat steps 2-6 for each signal from the table below.\n\n| Signal  |    Mode    | Temperature | Note                                |\n| :-----: | :--------: | :---------: | ----------------------------------- |\n|   Dh    | Dehumidify |     N/A     |                                     |\n| Cool_hi |  Cooling   |  See note   | Lowest temperature in cooling mode  |\n| Cool_lo |  Cooling   |    23°C     |                                     |\n| Heat_hi |  Heating   |  See note   | Highest temperature in heating mode |\n| Heat_lo |  Heating   |    23°C     |                                     |\n\nFinally, record the `Off` signal:\n\n1. Make sure the display shows that the A/C is ON.\n2. Start learning a new signal on Flipper and point the remote towards the IR receiver.\n3. Press the **POWER** button so that the remote shows the OFF state.\n4. Save the resulting signal under the name `Off`.\n\nThe resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality.\nTest the file against the actual device. Make sure that every signal does what it's supposed to.\n\nIf everything checks out, append these signals **to the end** of the [A/C universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir).\n\n## Final steps\n\nThe order of signals is not important, but they should be preceded by the following comment: `# Model: <Your model name>` in order to keep the library organized.\n\nWhen done, open a pull request containing the changed file.\n"
  },
  {
    "path": "documentation/devboard/Debugging via the Devboard.md",
    "content": "# Debugging via the Devboard {#dev_board_debugging_guide}\n\nOn this page, you'll learn about how debugging via the Wi-Fi Developer Board works. To illustrate this process, we'll start a debug session for Flipper Zero's firmware in VS Code using the native Flipper Build Tool.\n\n***\n\n## Overview\n\nThe Developer Board acts as the debug probe, which provides a bridge between the IDE (integrated development environment) with a debugger running on a host computer and the target microcontroller (in your Flipper Zero). The user controls the debugging process on the computer connected to the Developer Board via [Wi-Fi](#dev_board_wifi_connection) or [USB cable](#dev_board_usb_connection).\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_hardware_CDN.jpg width=700\n\nData exchange between the Wi-Fi Developer Board and your Flipper Zero is conducted via the Serial Wire Debug interface. The following GPIO pins serve this purpose:\n\n- **Pin 10:** Serial Wire Clock (SWCLK)\n\n- **Pin 12:** Serial Wire Debug Data I/O (SWDIO)\n\nTo learn more about Flipper Zero pinout, visit [GPIO & modules in Flipper Docs](https://docs.flipper.net/gpio-and-modules).\n\n***\n\n## Prerequisites\n\n### Step 1. Installing Git\n\nYou'll need Git installed on your computer to clone the firmware repository. If you don't have Git, install it following the [official installation guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).\n\n### Step 2. Building the firmware\n\nBefore starting debugging, you need to clone and build Flipper Zero firmware:\n\n1. Open the **Terminal** (on Linux & macOS) or **PowerShell** (on Windows) in the directory where you want to store the firmware source code.\n\n2. Clone the firmware repository:\n\n    ```\n    git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git\n    cd flipperzero-firmware\n    ```\n\n3. Run the **Flipper Build Tool (FBT)** to build the firmware:\n\n    ```\n    ./fbt\n    ```\n\n***\n\n## Debugging the firmware\n\nFrom the **flipperzero-firmware** directory that you cloned earlier, run the following command:\n\n```\n./fbt flash\n```\n\nThis will upload the firmware you've just built to your Flipper Zero via the Developer Board. After that, you can start debugging the firmware. We recommend using **VS Code** with the recommended extensions (as described below), and we have pre-made configurations for it.\n\nTo debug in **VS Code**, do the following:\n\n1. In VS Code, open the **flipperzero-firmware** directory.\n\n2. You should see a notification about recommended extensions. Install them.\n\n    If there were no notifications, open the **Extensions** tab, enter `@recommended` in the search bar, and install the workspace recommendations.\n\n3. Run the `./fbt vscode_dist` command. This will generate the VS Code configuration files needed for debugging.\n\n4. In VS Code, open the **Run and Debug** tab and select a debugger from the dropdown menu:\n\n    - **Attach FW (blackmagic):** Can be used via **Wi-Fi** or **USB**\n    - **Attach FW (DAP):** Can be used via **USB** only\n\n    Note that when debugging via USB, you need to make sure the selected debugger matches the debug mode on your Devboard. To check the debug mode on your Devboard, access the Devboard's web interface as described [here](#dev_board_wifi_connection) and check the **USB mode** field. If you want to use a different debug mode, enable this mode by following the steps in [Devboard debug modes](#dev_board_debug_modes).\n\n5. If needed, flash your Flipper Zero with the `./fbt flash` command, then click the ▷ **Start Debugging** button in the debug sidebar to start the debugging session.\n\n6. Note that starting a debug session halts the execution of the firmware, so you'll need to click the I▷ **Continue** button on the toolbar at the top of your VS Code window to continue execution.\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_VS_Code.jpg width=900\n\n> [!note]\n> If you want to use a different debug mode on your Developer Board, visit [Devboard debug modes](#dev_board_debug_modes).\n>\n> If you want to read logs via the Developer Board, see [Reading logs via the Devboard](#dev_board_reading_logs).\n> \n> To learn about debugging in VS Code, see [VS Code official guide](https://code.visualstudio.com/docs/editor/debugging).\n"
  },
  {
    "path": "documentation/devboard/Devboard debug modes.md",
    "content": "# Devboard debug modes {#dev_board_debug_modes}\n\nThe Wi-Fi Devboard for Flipper Zero supports **Black Magic** and **DAPLink** debug modes, and you can switch between them depending on your needs. Note that available modes depend on connection:\n\n- **Wi-Fi:** Only **Black Magic** mode is available.\n- **USB:** Switch between **Black Magic** (default) and **DAPLink**. Learn more about switching debug modes for USB connection below.\n\n> [!note]\n> Black Magic mode doesn't support RTOS threads, but you can still perform other debugging operations.\n\n***\n\n## Switching debug modes for USB connection\n\nSwitching debug modes for working via USB has to be done wirelessly (yes, you read that correctly). Additionally, depending on how the Devboard wireless connection is configured, you may need to follow different steps for **Wi-Fi access point mode** or **Wi-Fi client mode**:\n\n1. If the Devboard isn't connected to your Flipper Zero, turn off your Flipper Zero and connect the Developer Board, then turn the device back on.\n\n2. Access the Devboard's web interface:\n\n    - [Wi-Fi access point mode](#wifi-access-point)\n\n    - [Wi-Fi client mode](#wifi-client-mode)\n\n3. In the **WiFi** tab, click the **USB mode** option and select **BlackMagicProbe** or **DapLink**.\n\n4. Click **SAVE**, then click **REBOOT** to apply the changes.\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_switching_modes_CDN.jpg width=700\n\n> [!note]\n> After switching debug modes on your Devboard, remember to select the same debugger in **VS Code** in the **Run and Debug** tab, and click the ▷ **Start Debugging** button.\n\n"
  },
  {
    "path": "documentation/devboard/Firmware update on Developer Board.md",
    "content": "# Firmware update on Developer Board {#dev_board_fw_update}\n\nIt's important to regularly update your Developer Board to ensure that you have access to the latest features and bug fixes. This page will guide you through the necessary steps to update the firmware of your Developer Board.\n\n> [!note]\n> This guide assumes that you're familiar with the basics of the command line. If you're new to it, we recommend checking out these [Windows](https://learn.microsoft.com/en-us/powershell/scripting/learn/ps101/01-getting-started?view=powershell-7.4) or [macOS/Linux](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) command line tutorials.\n\n***\n\n## Step 1. Install the micro Flipper Build Tool\n\n[micro Flipper Build Tool (uFBT)](https://pypi.org/project/ufbt/) is a cross-platform tool developed and supported by our team that enables basic development tasks for Flipper Zero, such as building and debugging applications, flashing firmware, creating VS Code development configurations, and flashing firmware to the Wi-Fi Developer Board.\n\n**On Linux & macOS:**\n\n1. Open a terminal.\n2. Install `pipx` by following the instructions on the [official website](https://pipx.pypa.io/stable/installation/).\n3. Restart the terminal.\n4. Install `ufbt`:\n    ```\n    pipx install ufbt\n    ```\n\n**On Windows:**\n\n1. Download the latest version of Python on [the official website](https://www.python.org/downloads/windows/) and install it.\n2. Open PowerShell.\n3. Install `pipx`:\n    ```\n    py -m pip install --user pipx\n    ```\n4. Add `pipx` to PATH:\n    ```\n    py -m pipx ensurepath\n    ```\n5. Restart PowerShell.\n6. Install `ufbt`:\n    ```\n    pipx install ufbt\n    ```\n***\n\n## Step 2. Connect the Devboard to PC\n\nTo update the firmware, you need to switch your Developer Board to Bootloader mode, connect to a PC via a USB cable, and make sure that the PC detects the Developer Board:\n\n1. List all of the serial devices on your computer.\n\n    - **macOS:** Run the `ls /dev/cu.*` command in the Terminal.\n\n    - **Linux:** Run the `ls /dev/tty*` command in the Terminal.\n\n    - **Windows:** Go to **Device Manager** and expand the **Ports (COM & LPT)** section.\n\n2. Connect the Developer Board to your computer using a USB-C cable.\n    \\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_update_wired_connection.jpg width=700\n\n3. Switch your Developer Board to Bootloader mode:\n\n    3.1. Press and hold the **BOOT** button.\n\n    3.2. Press and release the **RESET** button while holding the **BOOT** button.\n    \n    3.3. Release the **BOOT** button.\n    \\image html https://cdn.flipper.net/Flipper_Zero_devboard_bootloader.jpg width=700\n\n4. Repeat the first command above (listing serial devices) and view the name of your Developer Board that appeared in the list.\n\n***\n\n## Step 3. Flash the firmware\n\n**On Linux & macOS:**\n\n```\npython3 -m ufbt devboard_flash\n```\n\n**On Windows:** Run the following command in PowerShell:\n\n```\npy -m ufbt devboard_flash\n```\n\nYou should see the following message: `WiFi board flashed successfully`.\n\n### If flashing fails\n\nOccasionally, you might get an error message during the flashing process, such as:\n\n```\nA fatal error occurred: Serial data stream stopped: Possible serial noise or corruption.\n```\n\n*or*\n\n```\nFileNotFoundError: [Errno 2] No such file or directory: '/dev/cu.usbmodem01'\n```\n\nTo fix it, try doing the following:\n\n- Disconnect the Developer Board from your computer, then reconnect it. After that, switch your Developer Board to Bootloader mode once again, as described in Step 2.\n\n- Use a different USB port on your computer.\n\n- Use a different USB-C cable.\n\n***\n\n## Step 4. Finish the installation\n\n1. Reboot the Developer Board by pressing the **RESET** button.\n    \\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot_after_flashing.jpg width=700\n\n2. Disconnect and reconnect the USB-C cable.\n\n    You've successfully updated the firmware of your Developer Board!\n\nIf you followed the **Get started with the Devboard** guide, you're ready for the next step: [Step 3. Plug the Devboard into Flipper Zero](#dev_board_get_started_step-3).\n\n"
  },
  {
    "path": "documentation/devboard/Get started with the Dev Board.md",
    "content": "# Get started with the Devboard {#dev_board_get_started}\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_developer_board_box_CDN.jpg width=700\n\nBefore you start using your Devboard, you need to prepare your Flipper Zero and Devboard for debugging. In this guide, we'll walk you through all the necessary steps and provide links to explore the Devboard's capabilities further.\n\n***\n\n## Step 1. Enable Debug Mode on your Flipper Zero\n\nSince the main purpose of the Developer board is to debug applications on Flipper Zero, you first need to enable Debug Mode. To do so, go to **Settings → System** and set **Debug** to **ON**.\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_enamble_debug_CDN.jpg width=700\n\n> [!note]\n> Debug Mode needs to be re-enabled after each update of Flipper Zero's firmware.\n\nDebug Mode allows you to debug your apps for Flipper Zero, as well as access debugging options in apps via the user interface and CLI. To learn more about Flipper Zero CLI, visit [Command-line interface in Flipper Docs](https://docs.flipper.net/development/cli).\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Command_Line_Interface_CDN.jpg width=700\n\n***\n\n## Step 2. Update firmware on the Developer Board\n\nThe Developer Board comes with stock firmware that may not include all the latest features and bug fixes. To ensure optimal performance, please update your board's firmware using the instructions in [Firmware update on Devboard](#dev_board_fw_update).\n\n***\n\n## Step 3. Plug the Devboard into Flipper Zero {#dev_board_get_started_step-3}\n\nOnce your Developer Board firmware is up to date, you can proceed to plug it into your Flipper Zero. Two important things to keep in mind:\n\n1. **Power off your Flipper Zero before plugging in the Developer Board.**\n\n    If you skip this step, you may corrupt the data stored on the microSD card. Connecting external modules with a large capacitive load may affect the microSD card's power supply since both the microSD card and external module are powered from the same 3.3 V power source inside Flipper Zero.\n\n2. **Make sure the Developer Board is inserted all the way in.**\n\n    If your Flipper Zero isn't in a silicone case, insert the module all the way in so there is no gap between your Flipper Zero and the Devboard. You may need to apply more force to insert it completely. After that, press and hold the **BACK** button to power on your Flipper Zero.\n\n    \\image html https://cdn.flipperzero.one/Flipper_Zero_external_module_without_case_CDN.jpg width=700\n\n    If your Flipper Zero is in a silicone case, insert the module all the way in so there is no gap in the middle between the silicone case and the module. After that, press and hold the **BACK** button to power on your Flipper Zero.\n\n    \\image html https://cdn.flipperzero.one/Flipper_Zero_external_module_with_case_CDN.jpg width=700\n\n***\n\n## Step 4. Connect to a computer\n\nNow, you can connect the Developer Board to your computer via USB or Wi-Fi, depending on your needs. We described both methods in separate documents:\n\n- **[Via USB cable](#dev_board_usb_connection)** for debugging in DAP Link or Black Magic mode, and reading logs.\n- [Via Wi-Fi](#dev_board_wifi_connection) for debugging in Black Magic mode.\n\n***\n\n## Next steps\n\nYou are ready to debug now! To further explore what you can do with the Devboard, check out these pages:\n\n- [Debugging via the Devboard](#dev_board_debugging_guide)\n- [Devboard debug modes](#dev_board_debug_modes)\n- [Reading logs via the Devboard](#dev_board_reading_logs)\n\nThese guides should help you get started with your Devboard. If you have any questions or you want to share your experience, don't hesitate to join our community on [Reddit](https://www.reddit.com/r/flipperzero/) and [Discord](https://discord.com/invite/flipper), where we have a dedicated #wifi-devboard channel.\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "documentation/devboard/Reading logs via the Dev Board.md",
    "content": "# Reading logs via the Devboard {#dev_board_reading_logs}\n\nThe Developer Board allows you to read Flipper Zero logs via UART. Unlike reading logs via the command-line interface (CLI), the Developer Board enables you to collect logs from the device directly to a serial console independently from the operating system of Flipper Zero. It allows you to see the device's logs when it's loading, updating, or crashing. It's useful for debugging and troubleshooting during software development.\n\n> **NOTE:**  Flipper Zero logs can only be viewed when the developer board is connected via USB. The option to view logs over Wi-Fi will be added in future updates.\n\n***\n\n## Setting the log level\n\nDepending on your needs, you can set the log level by going to **Main Menu → Settings → Log Level**. To learn more about logging levels, visit [Settings](https://docs.flipper.net/basics/settings#d5TAt).\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_log_level.jpg \"You can manually set the preferred log level\" width=700\n\n***\n\n## Viewing Flipper Zero logs\n\nDepending on your operating system, you need to install an additional application on your computer to read logs via the Developer Board:\n\n### macOS\n\nOn macOS, you need to install the **minicom** communication program by doing the following:\n\n1. [Install Homebrew](https://brew.sh/) by running the following command in the Terminal:\n\n   ```text\n   /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n   ```\n\n2. After installation of Homebrew, run the following command to install minicom:\n\n   ```text\n   brew install minicom\n   ```\n\nAfter installation of minicom on your macOS computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following:\n\n1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.\n\n2. On your computer, open the Terminal and run the following command:\n\n   ```text\n   ls /dev/cu.*\n   ```\n\n   Note the list of devices.\n\n3. Connect the developer board to your computer using a USB Type-C cable.\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700\n\n4. Rerun the command. Two new devices have to appear: this is the Developer Board.\n\n   ```text\n   /dev/cu.usbmodemblackmagic1\n   ```\n\n   ```text\n   /dev/cu.usbmodemblackmagic3\n   ```\n\n    Your Developer Board might have different names.\n\n5. Run the following command:\n\n   ```text\n   minicom -D /dev/<port> -b 230400\n   ```\n\n    Where `<port>` is the name of your device with a bigger number.\n\n    Example:\n\n   ```text\n   minicom -D /dev/cu.usbmodemblackmagic3 -b 230400\n   ```\n\n6. View logs of your Flipper Zero in the Terminal.\n\n7. To quit, close the minicom window or quit via the minicom menu.\n\n### Linux\n\nOn Linux, you need to install the **minicom** communication program. For example, on Ubuntu, run in the Terminal the following command:\n\n```text\n   sudo apt install minicom\n   ```\n\nAfter installation of minicom on your Linux computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following:\n\n1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.\n\n2. On your computer, open the Terminal and run the following command:\n\n   ```text\n   ls /dev/tty*\n   ```\n\n    Note the list of devices.\n\n3. Connect the developer board to your computer using a USB Type-C cable.\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700\n\n4. Rerun the command. Two new devices have to appear: this is the Developer Board.\n\n   ```text\n   /dev/ttyACM0\n   ```\n\n   ```text\n   /dev/ttyACM1\n   ```\n\n    Your Developer Board might have different names.\n\n5. Run the following command:\n\n    ```text\n    minicom -D /dev/<port> -b 230400\n    ```\n\n    Where `<port>` is the name of your device with a bigger number.\n\n    Example:\n\n    ```text\n    minicom -D /dev/cu.usbmodemblackmagic3 -b 230400\n    ```\n\n6. View logs of your Flipper Zero in the Terminal.\n\n    **NOTE:**  If no logs are shown in the Terminal, try running the command from Step 5 with another device name.\n\n7. To quit, close the minicom window or quit via the minicom menu.\n\n### Windows\n\nOn Windows, do the following:\n\n1. On your computer, [install the PuTTY application](https://www.chiark.greenend.org.uk/\\~sgtatham/putty/latest.html).\n\n2. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.\n\n3. Connect the developer board to your computer using a USB Type-C cable.\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700\n\n4. Find the serial port that the developer board is connected to by going to **Device Manager → Ports (COM & LPT)** and looking for a new port that appears when you connect the Wi-Fi developer board.\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_Device_Manager.png width=700\n\n5. Run the PuTTY application and select **Serial** as the connection type.\n\n6. Enter the port number you found in the previous step into the **Serial line** field.\n\n7. Set the **Speed** parameter to **230400** and click **Open**.\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_PuTTy.jpg width=700\n\n8. View logs of your Flipper Zero in the PuTTY terminal window.\n\n9. To quit, close the PuTTY window.\n"
  },
  {
    "path": "documentation/devboard/USB connection to the Devboard.md",
    "content": "# USB connection to the Devboard {#dev_board_usb_connection}\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_USB_connection_CDN.jpg width=700\n\nTo connect to the Developer Board via USB, do the following:\n\n1. If the Devboard isn't connected to your Flipper Zero, turn off your Flipper Zero and connect the Developer Board to it. Then, turn your Flipper Zero back on.\n\n2. On your computer, check the list of serial devices.\n\n    - **macOS:** On your computer, run `ls /dev/cu.*` in the Terminal.\n\n    - **Linux:** On your computer, run `ls /dev/tty*` in the Terminal.\n\n    - **Windows:** Go to **Device Manager** and expand the **Ports (COM & LPT)** section.\n\n3. Connect the Devboard to your computer via a USB-C cable.\n\n4. Repeat **Step 2**. Two new devices will appear — this is the Developer Board.\n\n> [!warning]\n> If the Developer Board doesn't appear in the list of devices, try using a different cable, USB port, or computer."
  },
  {
    "path": "documentation/devboard/Wi-Fi connection to the Devboard.md",
    "content": "# Wi-Fi connection to the Devboard {#dev_board_wifi_connection}\n\nYou can connect to the Developer Board wirelessly in two ways:\n\n- **Wi-Fi access point mode (default):** The Devboard creates its own Wi-Fi network, which you can connect to in order to access its web interface and debug via Wi-Fi. The downside is that you will need to disconnect from your current Wi-Fi network, resulting in a loss of internet connection.\n\n- **Wi-Fi client mode:** You can connect to the Devboard through an existing Wi-Fi network, allowing you to access the Devboard web interface and debug via Wi-Fi without losing your internet connection.\n\nLet's go over both of these modes below.\n\n***\n\n## Wi-Fi access point (AP) mode {#wifi-access-point}\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_Access_Point_CDN.jpg width=700\n\nOut of the box, the Developer Board is configured to work as a Wi-Fi access point. To connect the Developer Board in this mode, do the following:\n\n1. Plug the Wi-Fi Devboard into your Flipper Zero by turning off your Flipper Zero and connecting the Developer Board, and then turning it back on.\n\n2. Open Wi-Fi settings on your client device (phone, laptop, or other).\n\n3. Connect to the network:\n\n    Name: `blackmagic`\n    Password: `iamwitcher`\n\n    If your computer fails to find the **blackmagic** network, read the [troubleshooting section](#wifi-access-point_troubleshooting) below.\n\n4. To access the Devboard's web interface, open a browser and go to <http://192.168.4.1> or <http://blackmagic.local>.\n\n### If your computer fails to find the black magic network {#wifi-access-point_troubleshooting}\n\n- Reset Wi-Fi connection on your computer.\n\n- The Developer Board is probably configured to work in Wi-Fi client mode. → Reset your Developer Board settings to default by pressing and holding the **BOOT** button for **10 seconds**, then wait for the Devboard to reboot. After the reset, the Devboard will work in Wi-Fi access point mode.\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot.jpg width=700\n\n***\n\n## Wi-Fi client (STA) mode {#wifi-client-mode}\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_STA_CDN.jpg width=700\n\nTo connect the Developer Board in **Wi-Fi client** mode, you need to configure it to connect to your Wi-Fi network by doing the following:\n\n1. Plug the Wi-Fi Devboard into your Flipper Zero by turning off your Flipper Zero and connecting the Developer Board, and then turning the device back on.\n\n2. Connect to the Developer Board in [Wi-Fi access point](#wifi-access-point) mode.\n\n3. In a browser, go to the Devboard's web interface at <http://192.168.4.1> or <http://blackmagic.local>.\n\n4. Select the **STA** mode and enter your network's **SSID** (name) and **password**. For convenience, you can click the **+** button to see the list of nearby 2.4 GHz networks (5 GHz networks aren't supported).\n\n5. Save the configuration and reboot the Developer Board.\n    \\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_connect_to_WiFi_CDN.jpg width=700\n\n6. Now, you can access the Devboard's web interface at [http://blackmagic.local](https://blackmagic.local) via the existing Wi-Fi network without losing connection to the internet.\n"
  },
  {
    "path": "documentation/doxygen/Doxyfile-awesome.cfg",
    "content": "@INCLUDE               = $(DOXY_CONFIG_DIR)/Doxyfile.cfg\nGENERATE_TREEVIEW      = YES # required!\nDISABLE_INDEX          = NO\nFULL_SIDEBAR           = NO\nHTML_EXTRA_STYLESHEET  = $(DOXY_CONFIG_DIR)/doxygen-awesome-css/doxygen-awesome.css \\\n                         $(DOXY_CONFIG_DIR)/doxygen-awesome-css/doxygen-awesome-sidebar-only.css \\\n                         $(DOXY_CONFIG_DIR)/doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css\nHTML_COLORSTYLE        = LIGHT # required with Doxygen >= 1.9.5\nHTML_HEADER            = $(DOXY_CONFIG_DIR)/header.html\nHTML_EXTRA_FILES       = $(DOXY_CONFIG_DIR)/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js\n"
  },
  {
    "path": "documentation/doxygen/Doxyfile.cfg",
    "content": "# Doxyfile 1.10.0\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"Flipper Developer Docs\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = $(DOXY_CONFIG_DIR)/logo.png\n\n# With the PROJECT_ICON tag one can specify an icon that is included in the tabs\n# when the HTML document is shown. Doxygen will copy the logo to the output\n# directory.\n\nPROJECT_ICON           = $(DOXY_CONFIG_DIR)/favicon.ico\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = $(DOXY_CONFIG_DIR)/build\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = NO\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = YES\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = YES\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = NO\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 4\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= NO\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = NO\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = YES\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = YES\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about\n# undocumented enumeration values. If set to NO, doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = FAIL_ON_WARNINGS\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = $(DOXY_SRC_ROOT)/applications \\\n                         $(DOXY_SRC_ROOT)/documentation \\\n                         $(DOXY_SRC_ROOT)/targets \\\n                         $(DOXY_SRC_ROOT)/assets \\\n                         $(DOXY_SRC_ROOT)/lib \\\n                         $(DOXY_SRC_ROOT)/furi \\\n                         $(DOXY_SRC_ROOT)/.vscode\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding\n# \"INPUT_ENCODING\" for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,\n# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,\n# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to\n# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.md \\\n                         *.dox\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                = $(DOXY_SRC_ROOT)/lib/mlib \\\n                         $(DOXY_SRC_ROOT)/lib/STM32CubeWB \\\n                         $(DOXY_SRC_ROOT)/lib/nanopb \\\n                         $(DOXY_SRC_ROOT)/assets/protobuf \\\n                         $(DOXY_SRC_ROOT)/lib/libusb_stm32 \\\n                         $(DOXY_SRC_ROOT)/lib/FreeRTOS-Kernel \\\n                         $(DOXY_SRC_ROOT)/lib/microtar \\\n                         $(DOXY_SRC_ROOT)/lib/mbedtls \\\n                         $(DOXY_SRC_ROOT)/lib/cxxheaderparser \\\n                         $(DOXY_SRC_ROOT)/lib/ST25RFAL002 \\\n                         $(DOXY_SRC_ROOT)/lib/fatfs \\\n                         $(DOXY_SRC_ROOT)/lib/mlib \\\n                         $(DOXY_SRC_ROOT)/lib/stm32wb_cmsis \\\n                         $(DOXY_SRC_ROOT)/lib/stm32wb_copro \\\n                         $(DOXY_SRC_ROOT)/lib/stm32wb_hal_driver \\\n                         $(DOXY_SRC_ROOT)/lib/stm32wb_hal \\\n                         $(DOXY_SRC_ROOT)/lib/cmsis_core \\\n                         $(DOXY_SRC_ROOT)/targets/f7/fatfs/ \\\n                         $(DOXY_SRC_ROOT)/applications/plugins/dap_link/lib/free-dap \\\n                         $(DOXY_SRC_ROOT)/applications/debug \\\n                         $(DOXY_SRC_ROOT)/applications/main \\\n                         $(DOXY_SRC_ROOT)/applications/system/js_app/packages \\\n                         $(DOXY_SRC_ROOT)/applications/settings \\\n                         $(DOXY_SRC_ROOT)/lib/micro-ecc \\\n                         $(DOXY_SRC_ROOT)/lib/ReadMe.md \\\n                         $(DOXY_SRC_ROOT)/lib/callback-connector \\\n                         $(DOXY_SRC_ROOT)/lib/app-scened-template \\\n                         $(DOXY_SRC_ROOT)/applications/ReadMe.md \\\n                         $(DOXY_SRC_ROOT)/targets/ReadMe.md \\\n                         $(DOXY_SRC_ROOT)/web \\\n                         $(DOXY_SRC_ROOT)/assets/protobuf \\\n                         $(DOXY_SRC_ROOT)/lib/libusb_stm32 \\\n                         $(DOXY_SRC_ROOT)/lib/FreeRTOS-Kernel \\\n                         $(DOXY_SRC_ROOT)/lib/microtar \\\n                         $(DOXY_SRC_ROOT)/lib/mbedtls \\\n                         $(DOXY_SRC_ROOT)/lib/cxxheaderparser \\\n                         $(DOXY_SRC_ROOT)/applications/external/dap_link/lib/free-dap \\\n                         $(DOXY_SRC_ROOT)/lib/heatshrink \\\n                         $(DOXY_CONFIG_DIR)/doxygen-awesome-css\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             = $(DOXY_SRC_ROOT)/documentation/images\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# multi-line macros, enums or list initialized variables directly into the\n# documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generate light mode output, DARK always\n# generate dark mode output, AUTO_LIGHT automatically set the mode according to\n# the user preference, use light mode if no preference is set (the default),\n# AUTO_DARK automatically set the mode according to the user preference, use\n# dark mode if no preference is set and TOGGLE allow to user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in\n# the top right corner of code and text fragments that allows the user to copy\n# its content to the clipboard. Note this only works if supported by the browser\n# and the web page is served via a secure context (see:\n# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:\n# protocol.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COPY_CLIPBOARD    = YES\n\n# Doxygen stores a couple of settings persistently in the browser (via e.g.\n# cookies). By default these settings apply to all HTML pages generated by\n# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store\n# the settings under a project specific key, such that the user preferences will\n# be stored separately.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_PROJECT_COOKIE    =\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        =\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3\n# database with symbols found by doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each doxygen run. If set to NO, doxygen\n# will warn if a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links. Explicit enabling an inheritance\n# graph or choosing a different representation for an inheritance graph of a\n# specific class, can be accomplished by means of the command \\inheritancegraph.\n# Disabling an inheritance graph can be accomplished by means of the command\n# \\hideinheritancegraph.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will be wrapped across multiple lines. Some heuristics are\n# applied to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "documentation/doxygen/app_publishing.dox",
    "content": "/**\n@page app_publishing Publishing to the Apps Catalog\n\nYou can publish your app in the Flipper Apps Catalog. Users will be able to download your app and install it on their Flipper Zero via [mobile apps](https://flpr.app/) and [Flipper Lab](https://lab.flipper.net/apps). Check out the documentation below:\n\n- [Apps Catalog: Contribution Guide](https://github.com/flipperdevices/flipper-application-catalog/blob/main/documentation/Contributing.md) — How to publish and update your app in the Apps Catalog\n*/\n"
  },
  {
    "path": "documentation/doxygen/applications.dox",
    "content": "/**\n@page applications App Development\n\nFlipper Zero features full support for custom apps which (usually) do not require any changes to the firmware.\n\nFor easy app development, a software tool called [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) is available.\n\n- @subpage apps_on_sd_card — Creating apps that can be dynamically loaded from the SD card\n- @subpage app_manifests — How apps announce themselves to the system\n- @subpage app_examples — Various app examples, complete with the source code\n- @subpage app_publishing — Learn how to publish and update your app in the Apps Catalog\n*/\n"
  },
  {
    "path": "documentation/doxygen/dev_board.dox",
    "content": "/**\n@page dev_board Wi-Fi Developer Board\n\n\\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_laptop_CDN.jpg width=700\n\nWi-Fi-enabled Developer Board brings debugging and firmware update capabilities to your Flipper Zero. The Developer Board is based on the ESP32-S2 MCU with custom firmware incorporating Black Magic Debug and CMSIS-DAP, and is built with ESP-IDF. It can flash and debug various microprocessors and microcontrollers (including the one used in your Flipper Zero) via Wi-Fi or USB cable.\n\nThe Developer Board provides a debug interface, allowing developers to halt program execution, set breakpoints, inspect variables and memory, and step through code execution. \n\n<div align=\"center\">\n<a href=\"https://shop.flipperzero.one/products/wifi-devboard\"><img src=\"https://cdn.flipperzero.one/Get_developer_board_button_green_600.png\" width=\"350\" align=\"center\" alt=\"Get your Wi-Fi Developer Board\"/></a>\n</div>\n<br>\n\nCheck out these guides to get started with the Devboard:\n\n- @subpage dev_board_get_started — Quick start for new users\n- @subpage dev_board_fw_update — Keep the Developer Board up to date\n- @subpage dev_board_usb_connection — Instructions for Windows, macOS and Linux\n- @subpage dev_board_wifi_connection — Instructions for Windows, macOS and Linux\n- @subpage dev_board_debugging_guide — Learn how it works\n- @subpage dev_board_debug_modes — Available modes and how to switch between them\n- @subpage dev_board_reading_logs — Find out what is currently happening on the system\n\n## Hardware\n\nThe Developer Board is equipped with an [ESP32-S2-WROVER](https://www.espressif.com/en/products/socs/esp32-s2) module, which includes built-in Wi-Fi capabilities. It also offers GPIO pins for easy connectivity to various targets. Additionally, the Developer Board features a USB Type-C connector for data transfer and power supply. For user interaction, the Developer Board has tactile switches.\n\n\\image html https://cdn.flipper.net/Flipper_Zero_devboard_main_parts.jpg width=700\n\n## Pinout\n\n\\image html https://cdn.flipper.net/Flipper_Zero_wifi_devboard_pinout_updated.png width=700\n\n## Additional resources\n\nTo learn more about the Wi-Fi Developer Board hardware, visit [Schematics in Flipper Docs](https://docs.flipper.net/zero/development/hardware/devboard-schematics).\n\nFor additional information about Flipper Zero GPIO pins, visit [GPIO & modules in Flipper Docs](https://docs.flipperzero.one/gpio-and-modules).\n\n*/\n"
  },
  {
    "path": "documentation/doxygen/dev_tools.dox",
    "content": "/**\n@page dev_tools Developer Tools\n\nHardware and software tools for all kinds of programming.\n\n- @subpage fbt — Official build and deployment tool for Flipper Zero\n- @subpage vscode — Flipper Zero integration for VS Code\n- @subpage dev_board — ESP32-based development board\n- @subpage ota_updates — Standalone firmware self-update mechanism\n*/\n"
  },
  {
    "path": "documentation/doxygen/examples.dox",
    "content": "/**\n@page app_examples App Examples\n\nA collection of examples covering various aspects of app development for Flipper Zero.\n\n- @subpage example_number_input — Using a simple keyboard that limits user inputs to a full number (integer)\n- @subpage example_app_images — Using images and icons in an app\n- @subpage example_app_assets — Using app-specific asset folders\n- @subpage example_app_data — Using app-specific data folders\n- @subpage example_thermo — Reading data from a 1-Wire thermometer\n\nYou can find more app examples in the [repository on GitHub](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples).\n*/\n"
  },
  {
    "path": "documentation/doxygen/expansion_modules.dox",
    "content": "/**\n@page expansion Expansion Modules\n\nExpansion modules are special pieces of hardware designed to interface with Flipper's GPIO connector, such as the [Video Game Module](https://shop.flipperzero.one/collections/flipper-zero-accessories/products/video-game-module-for-flipper-zero).\n\n- @subpage expansion_protocol — Transport protocol for smart expansion modules\n\n*/\n"
  },
  {
    "path": "documentation/doxygen/file_formats.dox",
    "content": "/**\n@page file_formats File Formats\n\nDescriptions of various file formats used in Flipper Zero, grouped by applications that use them.\n\n- @subpage badusb_file_format\n- @subpage ibutton_file_format\n- @subpage infrared_file_format\n- @subpage lfrfid_file_format\n- @subpage nfc_file_format\n- @subpage subghz_file_format\n- @subpage heatshrink_file_format\n\n*/\n"
  },
  {
    "path": "documentation/doxygen/header.html",
    "content": "<!-- HTML header for doxygen 1.10.0-->\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"$langISO\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\"/>\n<meta name=\"generator\" content=\"Doxygen $doxygenversion\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->\n<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->\n<!--BEGIN PROJECT_ICON-->\n<link rel=\"icon\" href=\"$relpath^$projecticon\" type=\"image/x-icon\" />\n<!--END PROJECT_ICON-->\n<link href=\"$relpath^tabs.css\" rel=\"stylesheet\" type=\"text/css\"/>\n<!--BEGIN DISABLE_INDEX-->\n  <!--BEGIN FULL_SIDEBAR-->\n<script type=\"text/javascript\">var page_layout=1;</script>\n  <!--END FULL_SIDEBAR-->\n<!--END DISABLE_INDEX-->\n<script type=\"text/javascript\" src=\"$relpath^jquery.js\"></script>\n<script type=\"text/javascript\" src=\"$relpath^dynsections.js\"></script>\n<!--BEGIN COPY_CLIPBOARD-->\n<script type=\"text/javascript\" src=\"$relpath^clipboard.js\"></script>\n<!--END COPY_CLIPBOARD-->\n$treeview\n$search\n$mathjax\n$darkmode\n<link href=\"$relpath^$stylesheet\" rel=\"stylesheet\" type=\"text/css\" />\n$extrastylesheet\n<script type=\"text/javascript\" src=\"$relpath^doxygen-awesome-darkmode-toggle.js\"></script>\n<script type=\"text/javascript\">\n  DoxygenAwesomeDarkModeToggle.init()\n</script>\n</head>\n<body>\n<!--BEGIN DISABLE_INDEX-->\n  <!--BEGIN FULL_SIDEBAR-->\n<div id=\"side-nav\" class=\"ui-resizable side-nav-resizable\"><!-- do not remove this div, it is closed by doxygen! -->\n  <!--END FULL_SIDEBAR-->\n<!--END DISABLE_INDEX-->\n\n<div id=\"top\"><!-- do not remove this div, it is closed by doxygen! -->\n\n<!--BEGIN TITLEAREA-->\n<div id=\"titlearea\">\n<table cellspacing=\"0\" cellpadding=\"0\">\n <tbody>\n <tr id=\"projectrow\">\n  <!--BEGIN PROJECT_LOGO-->\n  <td id=\"projectlogo\"><a style=\"background: none;\" href=\"index.html\"><img alt=\"Logo\" src=\"$relpath^$projectlogo\"$logosize/></a></td>\n  <!--END PROJECT_LOGO-->\n  <!--BEGIN PROJECT_NAME-->\n  <td id=\"projectalign\">\n   <div id=\"projectname\"><a href=\"index.html\" style=\"background: none; color: var(--foreground_color) !important;\">$projectname</a><!--BEGIN PROJECT_NUMBER--><span id=\"projectnumber\">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->\n   </div>\n   <!--BEGIN PROJECT_BRIEF--><div id=\"projectbrief\">$projectbrief</div><!--END PROJECT_BRIEF-->\n  </td>\n  <!--END PROJECT_NAME-->\n  <!--BEGIN !PROJECT_NAME-->\n   <!--BEGIN PROJECT_BRIEF-->\n    <td>\n    <div id=\"projectbrief\">$projectbrief</div>\n    </td>\n   <!--END PROJECT_BRIEF-->\n  <!--END !PROJECT_NAME-->\n  <!--BEGIN DISABLE_INDEX-->\n   <!--BEGIN SEARCHENGINE-->\n     <!--BEGIN !FULL_SIDEBAR-->\n    <td>$searchbox</td>\n     <!--END !FULL_SIDEBAR-->\n   <!--END SEARCHENGINE-->\n  <!--END DISABLE_INDEX-->\n </tr>\n  <!--BEGIN SEARCHENGINE-->\n   <!--BEGIN FULL_SIDEBAR-->\n   <tr><td colspan=\"2\">$searchbox</td></tr>\n   <!--END FULL_SIDEBAR-->\n  <!--END SEARCHENGINE-->\n </tbody>\n</table>\n</div>\n<!--END TITLEAREA-->\n<!-- end header part -->\n"
  },
  {
    "path": "documentation/doxygen/index.dox",
    "content": "/**\n@mainpage notitle\n## Welcome to Flipper Developer Documentation!\n\n---\n\nThis documentation is intended for developers interested in modifying the Flipper Zero firmware, creating Apps and JavaScript programs, or working on external hardware modules for the device.\n\nIf you are looking for the user manual, please visit the [User Documentation](https://docs.flipper.net/) instead.\n\nThe documentation is divided into several sections. All of them are accessible from the sidebar on the left:\n\n- @ref dev_tools — Hardware and software tools for all kinds of programming\n- @ref system — Understanding the firmware's internals\n- @ref applications — Developing apps for Flipper Zero\n- @ref js — JS-based scripting engine\n- @ref expansion — Additional modules to expand Flipper's consciousness\n- @ref file_formats — Saving and loading data to and from files\n- @ref misc — Various useful pieces of information\n\nThese sections are all manually written. There are also a few automatically generated ones at the bottom of the sidebar:\n\n- [Data Structures](annotated.html) — Every data structure in a list\n- [Files](files.html) — Source file tree with easy navigation\n\nThese are generated from the source code and are useful for quickly finding the source code or API documentation for a particular function or data structure.\n*/\n"
  },
  {
    "path": "documentation/doxygen/js.dox",
    "content": "/**\n@page js JavaScript\n\nFlipper Zero's built-in JavaScript engine enables you to run lightweight scripts, similar to full-fledged C/C++ apps. Scripts can be shared, copied to a microSD card, and launched directly from the Flipper Zero menu — no precompilation needed.\n\n## Get started with JavaScript\n\n- @subpage js_about_js_engine — Learn about the implementation, advantages and limitations of our JavaScript engine\n\n- @subpage js_your_first_js_app — Create a simple app and run it using the Flipper Zero UI or CLI\n\n- @subpage js_developing_apps_using_js_sdk — Learn how to install and use the JavaScript SDK for fast app debugging\n\n- @subpage js_using_js_modules — Learn how you can use JS modules in your apps\n\n## JavaScript modules {#js_modules}\n\n- @subpage js_badusb — This module allows you to emulate a standard USB keyboard\n- @subpage js_event_loop — The module for easy event-based developing\n- @subpage js_flipper — This module allows to query device information\n- @subpage js_gpio — This module allows you to control GPIO pins\n- @subpage js_gui — This module allows you to use GUI (graphical user interface)\n- @subpage js_math — This module contains mathematical methods and constants\n- @subpage js_notification — This module allows you to use LED, speaker and vibro for notifications\n- @subpage js_serial — The module for interaction with external devices via UART\n- @subpage js_storage — The module for accessing the filesystem\n\n## Examples {#js_examples}\n\n- [Our examples (GitHub)](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/system/js_app/examples/apps/Scripts) — Pre-installed with the firmware, so you can run them directly from the Flipper Zero menu (**Apps → Scripts**)\n- [Featured: Derek Jamison's examples (GitHub)](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/js) — Come with detailed video guides for most scripts on [his YouTube channel](https://www.youtube.com/@MrDerekJamison)\n- Just google \"flipper zero javascript examples\"\n\n## Other resources\n\n- @subpage js_data_types — A list of data types you can use in your JS scripts\n- @subpage js_builtin — A list of functions you can use without including any JS modules\n\n*/\n"
  },
  {
    "path": "documentation/doxygen/misc.dox",
    "content": "/**\n@page misc Miscellaneous\n\nVarious pieces of information that do not fall into other categories.\n\n- @subpage lfrfid_raw — Collecting raw data from LF RFID tags\n- @subpage key_combos — Different key combination shortcuts for Flipper Zero\n- @subpage universal_remotes — Creating and improving IR universal remote libraries\n*/\n"
  },
  {
    "path": "documentation/doxygen/system.dox",
    "content": "/**\n@page system System Programming\n\nLower-level aspects of software development for Flipper Zero.\n\n- @subpage unit_tests — Automated testing, a crucial part of the development process\n- @subpage furi_check — Hard checks for exceptional situations\n- @subpage furi_hal_bus — Access the on-chip peripherals in a safe way\n- @subpage furi_hal_debugging — Low-level debugging features\n- @subpage hardware_targets — Support for different hardware platforms\n- @subpage firmware_assets — Various files required for building the firmware\n- @subpage dolphin_assets — Animations for the Dolphin game\n*/\n"
  },
  {
    "path": "documentation/fbt.md",
    "content": "# Flipper Build Tool {#fbt}\n\nFBT is the entry point for firmware-related commands and utilities.\nIt is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system.\n\nIf you don't need all features of `fbt` — like building the whole firmware — and only want to build and debug a single app, you can use [ufbt](https://pypi.org/project/ufbt/).\n\n## Environment\n\nTo use `fbt`, you only need `git` installed in your system.\n\n`fbt` by default downloads and unpacks a pre-built toolchain, and then modifies environment variables for itself to use it. It does not contaminate your global system's path with the toolchain.\n > However, if you wish to use tools supplied with the toolchain outside `fbt`, you can open an *fbt shell*, with properly configured environment.\n >    - On Windows, simply run `scripts/toolchain/fbtenv.cmd`.\n >    - On Linux & MacOS, run `source scripts/toolchain/fbtenv.sh` in a new shell.\n >    - You can also type ```. `./fbt -s env` ``` in your shell. (Keep  the \".\" at the beginning.)\n \n If your system is not supported by pre-built toolchain variants or you want to use custom versions of dependencies, you can `set FBT_NOENV=1`. `fbt` will skip toolchain & environment configuration and will expect all tools to be available on your system's `PATH`. *(this option is not available on Windows)*\n \n If `FBT_TOOLCHAIN_PATH` variable is set, `fbt` will use that directory to unpack toolchain into. By default, it downloads toolchain into `toolchain` subdirectory repo's root.\n\nIf you want to enable extra debug output for `fbt` and toolchain management scripts, you can `set FBT_VERBOSE=1`.\n\n`fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment:\n  - On Windows, it's `set \"FBT_NO_SYNC=1\"` in the shell you're running `fbt` from\n  - On \\*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...`\n\n > There are more variables controlling basic `fbt` behavior. See `fbt` & `fbtenv` scripts' sources for details.\n\n\n## Invoking FBT\n\nTo build with FBT, call it and specify configuration options & targets to build. For example:\n\n`./fbt COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist`\n\nTo run cleanup (think of `make clean`) for specified targets, add the `-c` option.\n\n## Build directories\n\n`fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (it is used for code completion support in IDEs).\n \n`build/latest` symlink & compilation database are only updated upon *firmware build targets* — that is, when you're re-building the firmware itself. Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration.\n\n## VSCode integration\n\n`fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the \"File > Open Folder\" menu.\n\nTo use language servers other than the default VS Code C/C++ language server, use `./fbt vscode_dist LANG_SERVER=<language-server>` instead. Currently `fbt` supports the default language server (`cpptools`) and `clangd`.\n\n- On the first start, you'll be prompted to install recommended plugins. We highly recommend installing them for the best development experience. _You can find a list of them in `.vscode/extensions.json`._\n- Basic build tasks are invoked in the Ctrl+Shift+B menu.\n- Debugging requires a supported probe. That includes:\n  - Wi-Fi Devboard with stock firmware (blackmagic).\n  - ST-Link and compatible devices.\n  - J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put them on your system's PATH._\n- Without a supported probe, you can install firmware on Flipper using the USB installation method.\n\n## FBT targets\n\n`fbt` keeps track of internal dependencies, so you only need to build the highest-level target you need, and `fbt` will make sure everything they depend on is up-to-date.\n\n### High-level (what you most likely need)\n\n- `fw_dist` — build & publish firmware to the `dist` folder. This is a default target when no others are specified.\n- `fap_dist` — build external plugins & publish to the `dist` folder.\n- `updater_package`, `updater_minpackage` — build a self-update package. The minimal version only includes the firmware's DFU file; the full version also includes a radio stack & resources for the SD card.\n- `copro_dist` — bundle Core2 FUS+stack binaries for qFlipper.\n- `flash` — flash the attached device over SWD interface with supported probes. Probe is detected automatically; you can override it with `SWD_TRANSPORT=...` variable. If multiple probes are attached, you can specify the serial number of the probe to use with `SWD_TRANSPORT_SERIAL=...`.\n- `flash_usb`, `flash_usb_full` — build, upload and install the update package to the device over USB. See details on `updater_package` and `updater_minpackage`.\n- `debug` — build and flash firmware, then attach with gdb with firmware's .elf loaded.\n- `debug_other`, `debug_other_blackmagic` — attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB.\n- `updater_debug` — attach GDB with the updater's `.elf` loaded.\n- `devboard_flash` — Update WiFi dev board. Supports `ARGS=\"...\"` to pass extra arguments to the update script, e.g. `ARGS=\"-c dev\"`.\n- `blackmagic` — debug firmware with Blackmagic probe (WiFi dev board).\n- `openocd` — just start OpenOCD. You can pass extra arguments with `ARGS=\"...\"`.\n- `get_blackmagic` — output the blackmagic address in the GDB remote format. Useful for IDE integration.\n- `get_stlink` — output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`.\n- `lint`, `format` — run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. Supports `ARGS=\"...\"` to pass extra arguments to clang-format.\n- `lint_py`, `format_py` — run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & app manifests. Supports `ARGS=\"...\"` to pass extra arguments to black.\n- `lint_img`, `format_img` — check the image assets for errors and format them. Enforces color depth and strips metadata.\n- `lint_all`, `format_all` — run all linters and formatters.\n- `firmware_pvs` — generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`.\n- `doxygen` — generate Doxygen documentation for the firmware. `doxy` target also opens web browser to view the generated documentation.\n- `cli` — start a Flipper CLI session over USB.\n\n### Firmware targets\n\n- `faps` — build all external & plugin apps as [`.faps`](AppsOnSDCard.md).\n- `fbt` also defines per-app targets. For example, for an app with `appid=snake_game` target names are:\n  - `fap_snake_game`, etc. — build single app as `.fap` by its app ID.\n  - Check out [--extra-ext-apps](#command-line-parameters) for force adding extra apps to external build.\n  - `fap_snake_game_list`, etc — generate source + assembler listing for app's `.fap`.\n- `flash`, `firmware_flash` — flash the current version to the attached device over SWD.\n- `jflash` — flash the current version to the attached device with JFlash using a J-Link probe. The JFlash executable must be on your `$PATH`.\n- `firmware_all`, `updater_all` — build a basic set of binaries.\n- `firmware_list`, `updater_list` — generate source + assembler listing.\n- `firmware_cdb`, `updater_cdb` — generate a `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware.\n\n### Assets\n\n- `resources` — build resources and their manifest files\n  - `dolphin_ext` — process dolphin animations for the SD card\n- `icons` — generate `.c+.h` for icons from PNG assets\n- `proto` — generate `.pb.c+.pb.h` for `.proto` sources\n- `proto_ver` — generate `.h` with a protobuf version\n- `dolphin_internal`, `dolphin_blocking` — generate `.c+.h` for corresponding dolphin assets\n\n## Command-line parameters {#command-line-parameters}\n\n- `--options optionfile.py` (default value `fbt_options.py`) — load a file with multiple configuration values\n- `--extra-int-apps=app1,app2,appN` — force listed apps to be built as internal with the `firmware` target\n- `--extra-ext-apps=app1,app2,appN` — force listed apps to be built as external with the `firmware_extapps` target\n- `--extra-define=A --extra-define=B=C ` — extra global defines that will be passed to the C/C++ compiler, can be specified multiple times\n- `--proxy-env=VAR1,VAR2` — additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file.\n\n## Configuration\n\nDefault configuration variables are set in the configuration file: `fbt_options.py`.\nValues set in the command line have higher precedence over the configuration file.\n\nYou can also create a file called `fbt_options_local.py` that will be evaluated when loading default options file, enabling persistent overriding of  default options without modifying default configuration.\n\nYou can find out available options with `./fbt -h`.\n\n### Firmware application set\n\nYou can create customized firmware builds by modifying the list of apps to be included in the build. App presets are configured with the `FIRMWARE_APPS` option, which is a `map(configuration_name:str → application_list:tuple(str))`. To specify an app set to use in the build, set `FIRMWARE_APP_SET` to its name.\nFor example, to build a firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`.\n\nCheck out `fbt_options.py` for details.\n"
  },
  {
    "path": "documentation/file_formats/BadUsbScriptFormat.md",
    "content": "# BadUSB File Format {#badusb_file_format}\n\n## Command syntax\n\nBadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command, and more functional keys.\n\n## Script file format\n\nBadUsb app can execute only text scripts from `.txt` files, no compilation is required. Both `\\n` and `\\r\\n` line endings are supported. Empty lines are allowed. You can use spaces or tabs for line indentation.\n\n## Command set\n\n### Comment line\n\nJust a single comment line. The interpreter will ignore all text after the REM command.\n| Command | Parameters   | Notes |\n| ------- | ------------ | ----- |\n| REM     | Comment text |       |\n\n### Delay\n\nPause script execution by a defined time.\n| Command       | Parameters        | Notes                               |\n| ------------- | ----------------- | ----------------------------------- |\n| DELAY         | Delay value in ms | Single delay                        |\n| DEFAULT_DELAY | Delay value in ms | Add delay before every next command |\n| DEFAULTDELAY  | Delay value in ms | Same as DEFAULT_DELAY               |\n\n### Special keys\n\n| Command            | Notes            |\n| ------------------ | ---------------- |\n| DOWNARROW / DOWN   |                  |\n| LEFTARROW / LEFT   |                  |\n| RIGHTARROW / RIGHT |                  |\n| UPARROW / UP       |                  |\n| ENTER              |                  |\n| DELETE             |                  |\n| BACKSPACE          |                  |\n| END                |                  |\n| HOME               |                  |\n| ESCAPE / ESC       |                  |\n| INSERT             |                  |\n| PAGEUP             |                  |\n| PAGEDOWN           |                  |\n| CAPSLOCK           |                  |\n| NUMLOCK            |                  |\n| SCROLLLOCK         |                  |\n| PRINTSCREEN        |                  |\n| BREAK              | Pause/Break key  |\n| PAUSE              | Pause/Break key  |\n| SPACE              |                  |\n| TAB                |                  |\n| MENU               | Context menu key |\n| APP                | Same as MENU     |\n| Fx                 | F1-F12 keys      |\n\n### Modifier keys\n\nThe following modifier keys are recognized:\n| Command | Notes        |\n| ------- | ------------ |\n| CTRL    |              |\n| CONTROL | Same as CTRL |\n| SHIFT   |              |\n| ALT     |              |\n| GUI     |              |\n| WINDOWS | Same as GUI  |\n\nYou can chain multiple modifier keys together using hyphens (`-`) or spaces.\n\n## Key hold and release\n\nUp to 5 keys can be hold simultaneously.\n| Command | Parameters                      | Notes                                    |\n| ------- | ------------------------------- | ---------------------------------------- |\n| HOLD    | Special key or single character | Press and hold key until RELEASE command |\n| RELEASE | Special key or single character | Release key                              |\n\n## String\n\n| Command  | Parameters  | Notes                                      |\n| -------  | ----------- | -----------------                          |\n| STRING   | Text string | Print text string                          |\n| STRINGLN | Text string | Print text string and press enter after it |\n\n## String delay\n\nDelay between keypresses.\n| Command              | Parameters        | Notes                                         |\n| -------------------- | ----------------- | --------------------------------------------- |\n| STRING_DELAY         | Delay value in ms | Applied once to next appearing STRING command |\n| STRINGDELAY          | Delay value in ms | Same as STRING_DELAY                          |\n| DEFAULT_STRING_DELAY | Delay value in ms | Apply to every appearing STRING command       |\n| DEFAULTSTRINGDELAY   | Delay value in ms | Same as DEFAULT_STRING_DELAY                  |\n\n### Repeat\n\n| Command | Parameters                   | Notes                   |\n| ------- | ---------------------------- | ----------------------- |\n| REPEAT  | Number of additional repeats | Repeat previous command |\n\n### ALT+Numpad input\n\nOn Windows and some Linux systems, you can print characters by holding `ALT` key and entering its code on Numpad.\n| Command   | Parameters     | Notes                                                           |\n| --------- | -------------- | --------------------------------------------------------------- |\n| ALTCHAR   | Character code | Print single character                                          |\n| ALTSTRING | Text string    | Print text string using ALT+Numpad method                       |\n| ALTCODE   | Text string    | Same as ALTSTRING, presents in some Duckyscript implementations |\n\n### SysRq\n\nSend [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key)\n| Command | Parameters       | Notes |\n| ------- | ---------------- | ----- |\n| SYSRQ   | Single character |       |\n\n## Media keys\n\nSome Media/Consumer Control keys can be pressed with \"MEDIA\" command\n\n| Command | Parameters                | Notes |\n| ------- | ------------------------- | ----- |\n| MEDIA   | Media key, see list below |       |\n\n| Key name          | Notes                         |\n| ----------------- | ----------------------------- |\n| POWER             |                               |\n| REBOOT            |                               |\n| SLEEP             |                               |\n| LOGOFF            |                               |\n| EXIT              |                               |\n| HOME              |                               |\n| BACK              |                               |\n| FORWARD           |                               |\n| REFRESH           |                               |\n| SNAPSHOT          | Take photo in a camera app    |\n| PLAY              |                               |\n| PAUSE             |                               |\n| PLAY_PAUSE        |                               |\n| NEXT_TRACK        |                               |\n| PREV_TRACK        |                               |\n| STOP              |                               |\n| EJECT             |                               |\n| MUTE              |                               |\n| VOLUME_UP         |                               |\n| VOLUME_DOWN       |                               |\n| FN                | Fn/Globe key on Mac keyboard  |\n| BRIGHT_UP         | Increase display brightness   |\n| BRIGHT_DOWN       | Decrease display brightness   |\n\n## Fn/Globe key commands (Mac/iPad)\n\n| Command | Parameters                      | Notes |\n| ------- | ------------------------------- | ----- |\n| GLOBE   | Special key or single character |       |\n\n## Wait for button press\n\nWill wait indefinitely for a button to be pressed\n| Command               | Parameters   | Notes                                                                 |\n| --------------------- | ------------ | --------------------------------------------------------------------- |\n| WAIT_FOR_BUTTON_PRESS | None         | Will wait for the user to press a button to continue script execution |\n\n## USB device ID\n\nYou can set the custom ID of the Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run.\n\n| Command | Parameters                   | Notes |\n| ------- | ---------------------------- | ----- |\n| ID      | VID:PID Manufacturer:Product |       |\n\nExample:\n`ID 1234:abcd Flipper Devices:Flipper Zero`\n\nVID and PID are hex codes and are mandatory. Manufacturer and Product are text strings and are optional.\n\n## Mouse Commands\n\nMouse movement and click commands. Mouse click commands support HOLD functionality. \n\n| Command       | Parameters                     | Notes                            |\n| ------------- | -------------------------------| -------------------------------- |\n|  LEFTCLICK    | None                           |                                  |\n|  LEFT_CLICK   | None                           | functionally same as LEFTCLICK   |\n|  RIGHTCLICK   | None                           |                                  |\n|  RIGHT_CLICK  | None                           | functionally same as RIGHTCLICK  |\n|  MOUSEMOVE    | x y: int move mount/direction  |                                  |\n|  MOUSE_MOVE   | x y: int move mount/direction  | functionally same as MOUSEMOVE   |\n|  MOUSESCROLL  | delta: int scroll distance     |                                  |\n|  MOUSE_SCROLL | delta: int scroll distance     | functionally same as MOUSESCROLL |\n"
  },
  {
    "path": "documentation/file_formats/InfraredFileFormats.md",
    "content": "# Infrared Flipper File Formats {#infrared_file_format}\n\n## Supported protocols list for \"type: parsed\"\n\n```\n    NEC\n    NECext\n    NEC42\n    NEC42ext\n    Samsung32\n    RC6\n    RC5\n    RC5X\n    SIRC\n    SIRC15\n    SIRC20\n    Kaseikyo\n    RCA\n```\n\n## Infrared Remote File Format\n\n### Example\n\n    Filetype: IR signals file\n    Version: 1\n    #\n    name: Button_1\n    type: parsed\n    protocol: NECext\n    address: EE 87 00 00\n    command: 5D A0 00 00\n    #\n    name: Button_2\n    type: raw\n    frequency: 38000\n    duty_cycle: 0.330000\n    data: 504 3432 502 483 500 484 510 502 502 482 501 485 509 1452 504 1458 509 1452 504 481 501 474 509 3420 503\n    #\n    name: Button_3\n    type: parsed\n    protocol: SIRC\n    address: 01 00 00 00\n    command: 15 00 00 00\n\n### Description\n\nFilename extension: `.ir`\n\nThis file format is used to store an infrared remote that consists of an arbitrary number of buttons.\nEach button is separated from others by a comment character (`#`) for better readability.\n\nKnown protocols are represented in the `parsed` form, whereas non-recognized signals may be saved and re-transmitted as `raw` data.\n\n#### Version history\n\n1. Initial version.\n\n#### Format fields\n\n| Name       | Use    | Type   | Description                                                                                                                                   |\n| ---------- | ------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |\n| name       | both   | string | Name of the button. Only printable ASCII characters are allowed.                                                                              |\n| type       | both   | string | Type of the signal. Must be `parsed` or `raw`.                                                                                                |\n| protocol   | parsed | string | Name of the infrared protocol. Refer to `ir` console command for the complete list of supported protocols.                                    |\n| address    | parsed | hex    | Payload address. Must be 4 bytes long.                                                                                                        |\n| command    | parsed | hex    | Payload command. Must be 4 bytes long.                                                                                                        |\n| frequency  | raw    | uint32 | Carrier frequency, in Hertz, usually 38000 Hz.                                                                                                |\n| duty_cycle | raw    | float  | Carrier duty cycle, usually 0.33.                                                                                                             |\n| data       | raw    | uint32 | Raw signal timings, in microseconds between logic level changes. Individual elements must be space-separated. Maximum timings amount is 1024. |\n\n## Infrared Library File Format\n\n### Examples\n\n- [TV Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir)\n- [A/C Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir)\n- [Audio Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir)\n\n### Description\n\nFilename extension: `.ir`\n\nThis file format is used to store universal remote libraries. It is identical to the previous format, differing only in the `Filetype` field.\nIt also has predefined button names for each universal library type, so that the universal remote application can understand them.\nSee [Universal Remotes](../UniversalRemotes.md) for more information.\n\n### Version history\n\n1. Initial version.\n\n## Infrared Test File Format\n\n### Examples\n\nSee [Infrared Unit Tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) for various examples.\n\n### Description\n\nFilename extension: `.irtest`\n\nThis file format is used to store technical test data that is too large to keep directly in the firmware.\nIt is mostly similar to the two previous formats, with the main difference being the addition of the parsed signal arrays.\n\nEach infrared protocol must have corresponding unit tests complete with an `.irtest` file.\n\nKnown protocols are represented in the `parsed_array` form, whereas raw data has the `raw` type.\nNote: a single parsed signal must be represented as an array of size 1.\n\n### Version history\n\n1. Initial version.\n\n#### Format fields\n\n| Name       | Use          | Type   | Description                                                      |\n| ---------- | ------------ | ------ | ---------------------------------------------------------------- |\n| name       | both         | string | Name of the signal. Only printable ASCII characters are allowed. |\n| type       | both         | string | Type of the signal. Must be `parsed_array` or `raw`.             |\n| count      | parsed_array | uint32 | The number of parsed signals in an array. Must be at least 1.    |\n| protocol   | parsed_array | string | Same as in previous formats.                                     |\n| address    | parsed_array | hex    | Ditto.                                                           |\n| command    | parsed_array | hex    | Ditto.                                                           |\n| repeat     | parsed_array | bool   | Indicates whether the signal is a repeated button press.         |\n| frequency  | raw          | uint32 | Same as in previous formats.                                     |\n| duty_cycle | raw          | float  | Ditto.                                                           |\n| data       | raw          | uint32 | Ditto.                                                           |\n\n#### Signal names\n\nThe signal names in an `.irtest` file follow a convention `<name><test_number>`, where the name is one of:\n\n- decoder_input\n- decoder_expected\n- encoder_decoder_input,\n\nand the number is a sequential integer: 1, 2, 3, etc., which produces names like `decoder_input1`, `encoder_decoder_input3`, and so on.\n\n| Name                  | Type         | Description                                                                                           |\n| --------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |\n| decoder_input         | raw          | A raw signal containing the decoder input. Also used as the expected encoder output.               |\n| decoder_expected      | parsed_array | An array of parsed signals containing the expected decoder output. Also used as the encoder input. |\n| encoder_decoder_input | parsed_array | An array of parsed signals containing both the encoder-decoder input and expected output.             |\n\nSee [Unit Tests](../UnitTests.md) for more info.\n"
  },
  {
    "path": "documentation/file_formats/LfRfidFileFormat.md",
    "content": "# LF RFID key file format {#lfrfid_file_format}\n\n## Example\n\n```\nFiletype: Flipper RFID key\nVersion: 1\nKey type: EM4100\nData: 01 23 45 67 89\n```\n\n## Description\n\nFilename extension: `.rfid`\n\nThe file stores a single RFID key of the type defined by the `Key type` parameter.\n\n### Version history\n\n1. Initial version.\n\n### Format fields\n\n| Name     | Description           |\n| -------- | --------------------- |\n| Key type | Key protocol type     |\n| Data     | Key data (HEX values) |\n\n### Supported key types\n\n| Type        | Full name         |\n| ----------- | ----------------- |\n| EM4100      | EM-Micro EM4100   |\n| H10301      | HID H10301        |\n| Idteck      | IDTECK            |\n| Indala26    | Motorola Indala26 |\n| IOProxXSF   | Kantech IOProxXSF |\n| AWID        | AWID              |\n| FDX-A       | FECAVA FDX-A      |\n| FDX-B       | ISO FDX-B         |\n| HIDProx     | Generic HIDProx   |\n| HIDExt      | Generic HIDExt    |\n| Pyramid     | Farpointe Pyramid |\n| Viking      | Viking            |\n| Jablotron   | Jablotron         |\n| Paradox     | Paradox           |\n| PAC/Stanley | PAC/Stanley       |\n| Keri        | Keri              |\n| Gallagher   | Gallagher         |\n| GProxII     | Guardall GProx II |\n"
  },
  {
    "path": "documentation/file_formats/NfcFileFormats.md",
    "content": "# NFC Flipper File Formats {#nfc_file_format}\n\n## UID + Header (General format)\n\n### Example\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: ISO14443-4A\n    # UID is common for all formats\n    UID: 04 48 6A 32 33 58 80\n    -------------------------\n    (Device-specific data)\n\n### Description\n\nThis file format is used to store the device type and the UID of an NFC device. It does not store any internal data, so it is only used as a header for other formats.\n\nVersion differences:\n\n1. Initial version, deprecated\n2. LSB ATQA (e.g. 4400 instead of 0044)\n3. MSB ATQA (current version)\n4. Replace UID device type with ISO14443-3A\n\n## ISO14443-3A\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: ISO14443-3A\n    # UID is common for all formats\n    UID: 34 19 6D 41 14 56 E6\n    # ISO14443-3A specific data\n    ATQA: 00 44\n    SAK: 00\n\n### Description\n\nThis file format is used to store the UID, SAK and ATQA of an ISO14443-3A device.\nUID must be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long.\n\nVersion differences:\nNone, there are no versions yet.\n\n## ISO14443-3B\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: ISO14443-3B\n    # UID is common for all formats\n    UID: 30 1D B3 28\n    # ISO14443-3B specific data\n    Application data: 00 12 34 FF\n    Protocol info: 11 81 E1\n\n### Description\n\nThis file format is used to store the UID, Application data and Protocol info of a ISO14443-3B device.\nUID must be 4 bytes long. Application data is 4 bytes long. Protocol info is 3 bytes long.\n\nVersion differences:\nNone, there are no versions yet.\n\n## ISO14443-4A\n\n### Example\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: ISO14443-4A\n    # UID is common for all formats\n    UID: 04 48 6A 32 33 58 80\n    # ISO14443-3A specific data\n    ATQA: 03 44\n    SAK: 20\n    # ISO14443-4A specific data\n    ATS: 06 75 77 81 02 80\n    \n### Description\n\nThis file format is used to store the UID, SAK and ATQA of a ISO14443-4A device. It also stores the Answer to Select (ATS) data of the card.\nATS must be no less than 5 bytes long.\n\nVersion differences:\nNone, there are no versions yet.\n\n## NTAG/Ultralight\n\n### Example\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: NTAG/Ultralight\n    # UID is common for all formats\n    UID: 04 85 90 54 12 98 23\n    # ISO14443-3A specific data\n    ATQA: 00 44\n    SAK: 00\n    # NTAG/Ultralight specific data\n    Data format version: 2\n    NTAG/Ultralight type: NTAG216\n    Signature: 1B 84 EB 70 BD 4C BD 1B 1D E4 98 0B 18 58 BD 7C 72 85 B4 E4 7B 38 8E 96 CF 88 6B EE A3 43 AD 90\n    Mifare version: 00 04 04 02 01 00 13 03\n    Counter 0: 0\n    Tearing 0: 00\n    Counter 1: 0\n    Tearing 1: 00\n    Counter 2: 0\n    Tearing 2: 00\n    Pages total: 231\n    Pages read: 231\n    Page 0: 04 85 92 9B\n    Page 1: 8A A0 61 81\n    Page 2: CA 48 0F 00\n    ...\n    Page 224: 00 00 00 00\n    Page 225: 00 00 00 00\n    Page 226: 00 00 7F BD\n    Page 227: 04 00 00 E2\n    Page 228: 00 05 00 00\n    Page 229: 00 00 00 00\n    Page 230: 00 00 00 00\n    Failed authentication attempts: 0\n\n### Description\n\nThis file format is used to store the UID, SAK and ATQA of a Mifare Ultralight/NTAG device. It also stores the internal data of the card, the signature, the version, and the counters. The data is stored in pages, just like on the card itself.\n\nThe \"NTAG/Ultralight type\" field contains the concrete device type. It must be one of: Mifare Ultralight, Mifare Ultralight 11, Mifare Ultralight 21, NTAG203, NTAG213, NTAG215, NTAG216, NTAG I2C 1K, NTAG I2C 2K, NTAG I2C Plus 1K, NTAG I2C Plus 2K.\n\nThe \"Signature\" field contains the reply of the tag to the READ_SIG command. More on that can be found here: <https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf> (page 31)\n\nThe \"Mifare version\" field is not related to the file format version but to the Mifare Ultralight version. It contains the response of the tag to the GET_VERSION command. More on that can be found here: <https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf> (page 21)\n\nOther fields are the direct representation of the card's internal state. Learn more about them in the same datasheet.\n\nVersion differences:\n\n1. Mifare Ultralight type is stored directly in Device type field\n2. Current version, Mifare Ultralight type is stored in the same-named field\n\n## Mifare Classic\n\n### Example\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: Mifare Classic\n    # UID is common for all formats\n    UID: BA E2 7C 9D\n    # ISO14443-3A specific data\n    ATQA: 00 02\n    SAK: 18\n    # Mifare Classic specific data\n    Mifare Classic type: 4K\n    Data format version: 2\n    # Mifare Classic blocks, '??' means unknown data\n    Block 0: BA E2 7C 9D B9 18 02 00 46 44 53 37 30 56 30 31\n    Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF\n    Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 7: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF\n    ...\n    Block 238: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 239: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF\n    Block 240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 241: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 242: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 243: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 244: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 245: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 246: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 247: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 248: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 249: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 251: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 252: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 253: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 254: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    Block 255: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF\n\n### Description\n\nThis file format is used to store the NFC-A and Mifare Classic specific data of a Mifare Classic card. Aside from the NFC-A data, it stores the card type (1K/4K) and the internal data of the card. The data is stored in blocks, there is no sector grouping. If the block's data is unknown, it is represented by '??'. Otherwise, the data is represented as a hex string.\n\nVersion differences:\n\n1. Initial version, has Key A and Key B masks instead of marking unknown data with '??'.\n\nExample:\n\n    ...\n    Data format version: 1\n    # Key map is the bit mask indicating valid key in each sector\n    Key A map: 000000000000FFFF\n    Key B map: 000000000000FFFF\n    # Mifare Classic blocks\n    ...\n\n2. Current version\n\n## Mifare DESFire\n\n### Example\n\n    Filetype: Flipper NFC device\n    Version: 4\n    # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire\n    Device type: Mifare DESFire\n    # UID is common for all formats\n    UID: 04 2F 19 0A CD 66 80\n    # ISO14443-3A specific data\n    ATQA: 03 44\n    SAK: 20\n    # ISO14443-4A specific data\n    ATS: 06 75 77 81 02 80\n    # Mifare DESFire specific data\n    PICC Version: 04 01 01 12 00 1A 05 04 01 01 02 01 1A 05 04 2F 19 0A CD 66 80 CE ED D4 51 80 31 19\n    PICC Free Memory: 7520\n    PICC Change Key ID: 00\n    PICC Config Changeable: true\n    PICC Free Create Delete: true\n    PICC Free Directory List: true\n    PICC Key Changeable: true\n    PICC Max Keys: 01\n    PICC Key 0 Version: 00\n    Application Count: 1\n    Application IDs: 56 34 12\n    Application 563412 Change Key ID: 00\n    Application 563412 Config Changeable: true\n    Application 563412 Free Create Delete: true\n    Application 563412 Free Directory List: true\n    Application 563412 Key Changeable: true\n    Application 563412 Max Keys: 0E\n    Application 563412 Key 0 Version: 00\n    Application 563412 Key 1 Version: 00\n    Application 563412 Key 2 Version: 00\n    Application 563412 Key 3 Version: 00\n    Application 563412 Key 4 Version: 00\n    Application 563412 Key 5 Version: 00\n    Application 563412 Key 6 Version: 00\n    Application 563412 Key 7 Version: 00\n    Application 563412 Key 8 Version: 00\n    Application 563412 Key 9 Version: 00\n    Application 563412 Key 10 Version: 00\n    Application 563412 Key 11 Version: 00\n    Application 563412 Key 12 Version: 00\n    Application 563412 Key 13 Version: 00\n    Application 563412 File IDs: 01\n    Application 563412 File 1 Type: 00\n    Application 563412 File 1 Communication Settings: 00\n    Application 563412 File 1 Access Rights: EE EE\n    Application 563412 File 1 Size: 256\n    Application 563412 File 1: 13 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n\n### Description\n\nThis file format is used to store the NFC-A and Mifare DESFire specific data of a Mifare DESFire card. Aside from the NFC-A data, it stores the card type (DESFire) and the internal data of the card. The data is stored per-application, and per-file. Here, the card was written using those pm3 commands:\n\n    hf mfdes createapp --aid 123456 --fid 2345 --dfname astra\n    hf mfdes createfile --aid 123456 --fid 01 --isofid 0001 --size 000100\n    hf mfdes write --aid 123456 --fid 01 -d 1337\n\nVersion differences:\nNone, there are no versions yet.\n\n## Mifare Classic Dictionary\n\n### Example\n\n    # Key dictionary from https://github.com/ikarus23/MifareClassicTool.git\n\n    # More well known keys!\n    # Standard keys\n    FFFFFFFFFFFF\n    A0A1A2A3A4A5\n    D3F7D3F7D3F7\n    000000000000\n\n    # Keys from mfoc\n    B0B1B2B3B4B5\n    4D3A99C351DD\n    1A982C7E459A\n    AABBCCDDEEFF\n    714C5C886E97\n    587EE5F9350F\n    A0478CC39091\n    533CB6C723F6\n    8FD0A4F256E9\n    ...\n\n### Description\n\nThis file contains a list of Mifare Classic keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well.\n\n## Mifare Ultralight C Dictionary\n\n### Example\n\n    # Hexadecimal-Reversed Sample Key\n    12E4143455F495649454D4B414542524\n    # Byte-Reversed Sample Key (!NACUOYFIEMKAERB)\n    214E4143554F594649454D4B41455242\n    # Sample Key (BREAKMEIFYOUCAN!)\n    425245414B4D454946594F5543414E21\n    # Semnox Key (IEMKAERB!NACUOY )\n    49454D4B41455242214E4143554F5900\n    # Modified Semnox Key (IEMKAERB!NACUOYF)\n    49454D4B41455242214E4143554F5946\n    ...\n\n### Description\n\nThis file contains a list of Mifare Ultralight C keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well.\n\n## EMV resources\n\n### Example\n\n    Filetype: Flipper EMV resources\n    Version: 1\n    # EMV currency code: currency name\n    0997: USN\n    0994: XSU\n    0990: CLF\n    0986: BRL\n    0985: PLN\n    0984: BOV\n    ...\n\n### Description\n\nThis file stores a list of EMV currency codes, country codes, or AIDs and their names. Each line contains a hex value and a name separated by a colon and a space.\n\nVersion differences:\n\n1. Initial version\n"
  },
  {
    "path": "documentation/file_formats/SubGhzFileFormats.md",
    "content": "# SubGhz Subsystem File Formats {#subghz_file_format}\n\n## .sub File Format\n\nFlipper uses `.sub` files to store SubGhz signals. These files use the Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data.\n\nA `.sub` file consist of 3 parts:\n\n- **header**, contains the file type, version, and frequency\n- **preset information**, preset type and, in case of a custom preset, transceiver configuration data\n- **protocol and its data**, contains protocol name and its specific data, such as key, bit length, etc., or RAW data\n\nFlipper's SubGhz subsystem uses presets to configure the radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) section for more details.\n\n## Header format\n\nHeader is a mandatory part of a `.sub` file. It contains the file type, version, and frequency.\n\n| Field       | Type   | Description                                                       |\n| ----------- | ------ | ----------------------------------------------------------------- |\n| `Filetype`  | string | Filetype of subghz file format, must be `Flipper SubGhz Key File` |\n| `Version`   | uint   | Version of subghz file format, current version is 1               |\n| `Frequency` | uint   | Frequency in Hertz                                                |\n\n## Preset information\n\nPreset information is a mandatory part for `.sub` files. It contains preset type and, in case of custom preset, transceiver configuration data.\n\nWhen using one of the standard presets, only `Preset` field is required. When using a custom preset, `Custom_preset_module` and `Custom_preset_data` fields are required.\n\n| Field                  | Description                                                                                                                          |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `Preset`               | Radio preset name (configures modulation, bandwidth, filters, etc.). When using a custom preset, must be `FuriHalSubGhzPresetCustom` |\n| `Custom_preset_module` | Transceiver identifier, `CC1101` for Flipper Zero                                                                                    |\n| `Custom_preset_data`   | Transceiver configuration data                                                                                                       |\n\nBuilt-in presets:\n\n- `FuriHalSubGhzPresetOok270Async` — On/Off Keying, 270kHz bandwidth, async(IO throw GP0)\n- `FuriHalSubGhzPresetOok650Async` — On/Off Keying, 650kHz bandwidth, async(IO throw GP0)\n- `FuriHalSubGhzPreset2FSKDev238Async` — 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0)\n- `FuriHalSubGhzPreset2FSKDev476Async` — 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0)\n\n### Transceiver Configuration Data {#transceiver-configuration-data}\n\nTransceiver configuration data is a string of bytes, encoded in hex format, separated by spaces. For CC1101 data structure is: `XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ`, where:\n\n- **XX**, holds register address,\n- **YY**, contains register value,\n- **00 00**, marks register block end,\n- **ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ**, 8 byte PA table (Power amplifier ramp table).\n\nYou can find more details in the [CC1101 datasheet](https://www.ti.com/lit/ds/symlink/cc1101.pdf) and `furi_hal_subghz` code.\n\n## File Data\n\n`.sub` file data section can either contain key data, consisting of a protocol name and its specific data, bit length, etc., or RAW data, which consists of an array of signal timings, recorded without any protocol-specific processing.\n\n### Key Files\n\n`.sub` files with key data files contain protocol name and its specific data, such as key value, bit length, etc.\nCheck out the protocol registry for the full list of supported protocol names.\n\nExample of a key data block in Princeton format:\n\n```\n...\nProtocol: Princeton\nBit: 24\nKey: 00 00 00 00 00 95 D5 D4\nTE: 400\n```\n\nProtocol-specific fields in this example:\n\n| Field | Description                       |\n| ----- | --------------------------------- |\n| `Bit` | Princeton payload length, in bits |\n| `Key` | Princeton payload data            |\n| `TE`  | Princeton quantization interval   |\n\nThis file may contain additional fields, more details on available fields can be found in subghz protocols library.\n\n### RAW Files\n\nRAW `.sub` files contain raw signal data that is not processed through protocol-specific decoding. These files are useful for testing or sending data not supported by any known protocol.\n\nFor RAW files, 2 fields are required:\n\n- **Protocol**, must be `RAW`\n- **RAW_Data**, contains an array of timings, specified in microseconds. Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data.\n\nExample of RAW data:\n\n    Protocol: RAW\n    RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ...\n\nA long payload that doesn't fit into the internal memory buffer and consists of short duration timings (< 10us) may not be read fast enough from the SD card. That might cause the signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads.\n\n### BIN_RAW Files\n\nBinRAW `.sub` files and `RAW` files both contain data that has not been decoded by any protocol. However, unlike `RAW`, `BinRAW` files only record a useful repeating sequence of durations with a restored byte transfer rate and without broadcast noise. These files can emulate nearly all static protocols, whether Flipper knows them or not.\n\n- Usually, you have to receive the signal a little longer so that Flipper accumulates sufficient data to analyze it correctly.\n\nFor `BinRAW` files, the following parameters are required and must be aligned to the left:\n\n- **Protocol**, must be `BinRAW`.\n- **Bit**, is the length of the payload of the entire file, in bits (max 4096).\n- **TE**, is the quantization interval, in us.\n- **Bit_RAW**, is the length of the payload in the next Data_RAW parameter, in bits.\n- **Data_RAW**, is an encoded sequence of durations, where each bit in the sequence encodes one TE interval: 1 - high level (there is a carrier), 0 - low (no carrier).\n    For example, TE=100, Bit_RAW=8, Data_RAW=0x37 => 0b00110111, that is, `-200 200 -100 300` will be transmitted.\n    When sending uploads, `Bit_RAW` and `Data_RAW` form a repeating block. Several such blocks are necessary if you want to send different sequences sequentially. However, usually, there will be only one block.\n\nExample data from a `BinRAW` file:\n\n```\n...\nProtocol: BinRAW\nBit: 1572\nTE: 597\nBit_RAW: 260\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 4A B5 55 4C B3 52 AC D5 2D 53 52 AD 4A D5 35 00\nBit_RAW: 263\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 04 D5 32 D2 AB 2B 33 32 CB 2C CC B3 52 D3 00\nBit_RAW: 259\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 4A AB 55 34 D5 2D 4C CD 33 4A CD 55 4C D2 B3 00\nBit_RAW: 263\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 7F 4A AA D5 2A CC B2 B4 CB 34 CC AA AB 4D 53 53 00\nBit_RAW: 264\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 FC 00 00 15 2C CB 34 D3 35 35 4D 4B 32 B2 D3 33 00\nBit_RAW: 263\nData_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 02 D3 54 D5 4C D2 CC AD 4B 2C B2 B5 54 CC AB 00\n```\n\n## File examples\n\n### Key file, standard preset\n\n    Filetype: Flipper SubGhz Key File\n    Version: 1\n    Frequency: 433920000\n    Preset: FuriHalSubGhzPresetOok650Async\n    Protocol: Princeton\n    Bit: 24\n    Key: 00 00 00 00 00 95 D5 D4\n    TE: 400\n\n### Key file, custom preset\n\n    Filetype: Flipper SubGhz Key File\n    Version: 1\n    Frequency: 433920000\n    Preset: FuriHalSubGhzPresetCustom\n    Custom_preset_module: CC1101\n    Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n    Protocol: Princeton\n    Bit: 24\n    Key: 00 00 00 00 00 95 D5 D4\n    TE: 400\n\n### RAW file, standard preset\n\n    Filetype: Flipper SubGhz RAW File\n    Version: 1\n    Frequency: 433920000\n    Preset: FuriHalSubGhzPresetOok650Async\n    Protocol: RAW\n    RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ...\n    RAW_Data: -424 205 -412 159 -412 381 -240 181 ...\n    RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ...\n\n### RAW file, custom preset\n\n    Filetype: Flipper SubGhz RAW File\n    Version: 1\n    Frequency: 433920000\n    Preset: FuriHalSubGhzPresetCustom\n    Custom_preset_module: CC1101\n    Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n    Protocol: RAW\n    RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ...\n    RAW_Data: -424 205 -412 159 -412 381 -240 181 ...\n    RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ...\n\n# SubGhz configuration files\n\nSubGhz application provides support for adding extra radio presets and additional keys for decoding transmissions in certain protocols.\n\n## SubGhz keeloq_mfcodes_user file\n\nThis file contains additional manufacturer keys for Keeloq protocol. It is used to decode Keeloq transmissions.\nThis file is loaded at subghz application start and is located at path `/ext/subghz/assets/keeloq_mfcodes_user`.\n\n### File format\n\nFile contains a header and a list of manufacturer keys.\n\nFile header format:\n\n| Field        | Type   | Description                                                        |\n| ------------ | ------ | ------------------------------------------------------------------ |\n| `Filetype`   | string | SubGhz Keystore file format, always `Flipper SubGhz Keystore File` |\n| `Version`    | uint   | File format version, 0                                             |\n| `Encryption` | uint   | File encryption: for user-provided file, set to 0 (disabled)       |\n\nFollowing the header, file contains a list of user-provided manufacture keys, one key per line.\nFor each key, a name and encryption method must be specified, according to comment in file header. More information can be found in keeloq decoder source code.\n\n### Example\n\n    # to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user\n    # for adding manufacture keys\n    # AABBCCDDEEFFAABB:X:NAME\n    # AABBCCDDEEFFAABB - man 64 bit\n    # X - encryption method:\n    # - 0 - iterates over both previous and man in direct and reverse byte sequence\n    # - 1 - Simple Learning\n    # - 2 - Normal_Learning\n    # - 3 - Secure_Learning\n    # - 4 - Magic_xor_type1 Learning\n    #\n    # NAME - name (string without spaces) max 64 characters long\n    Filetype: Flipper SubGhz Keystore File\n    Version: 0\n    Encryption: 0\n    AABBCCDDEEFFAABB:1:Test1\n    AABBCCDDEEFFAABB:1:Test2\n\n## SubGhz setting_user file\n\nThis file contains additional radio presets and frequencies for SubGhz application. It is used to add new presets and frequencies for existing presets. This file is being loaded on subghz application start and is located at path `/ext/subghz/assets/setting_user`.\n\n### File format\n\nFile contains a header, basic options, and optional lists of presets and frequencies.\n\nHeader must contain the following fields:\n\n- `Filetype`: SubGhz setting file format, must be `Flipper SubGhz Setting File`.\n- `Version`: file format version, current is `1`.\n\n#### Basic settings\n\n- `Add_standard_frequencies`: bool, flag indicating whether to load standard frequencies shipped with firmware. If set to `false`, only frequencies specified in this file will be used.\n- `Default_frequency`: uint, default frequency used in SubGhz application.\n\n#### Adding more frequencies\n\n- `Frequency`: uint — additional frequency for the subghz application frequency list. Used in Read and Read RAW. You can specify multiple frequencies, one per line.\n\n#### Adding more hopper frequencies\n\n- `Hopper_frequency`: uint — additional frequency for subghz application frequency hopping. Used in Frequency Analyzer. You can specify multiple frequencies, one per line.\n\nRepeating the same frequency will cause Flipper to listen to this frequency more often.\n\n#### Adding a Custom Preset {#adding-a-custom-preset}\n\nYou can have as many presets as you want. Presets are embedded into `.sub` files, so another Flipper can load them directly from that file.\nEach preset is defined by the following fields:\n\n| Field                  | Description                                                                                                        |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| `Custom_preset_name`   | string, preset name that will be shown in SubGHz application                                                       |\n| `Custom_preset_module` | string, transceiver identifier. Set to `CC1101` for Flipper Zero                                                   |\n| `Custom_preset_data`   | transceiver configuration data. See [Transceiver Configuration Data](#transceiver-configuration-data) for details. |\n\n### Example\n\n```\n# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user\nFiletype: Flipper SubGhz Setting File\nVersion: 1\n\n# Add Standard frequencies for your region\nAdd_standard_frequencies: true\n\n# Default Frequency: used as default for \"Read\" and \"Read Raw\"\nDefault_frequency: 433920000\n\n# Frequencies used for \"Read\", \"Read Raw\" and \"Frequency Analyzer\"\nFrequency: 300000000\nFrequency: 310000000\nFrequency: 320000000\n\n# Frequencies used for hopping mode (keep this list small or Flipper will miss the signal)\nHopper_frequency: 300000000\nHopper_frequency: 310000000\nHopper_frequency: 310000000\n\n# Custom preset\n# format for CC1101 \"Custom_preset_data:\" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register\n\n#Custom_preset_name: AM_1\nCustom_preset_module: CC1101\nCustom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n\n#Custom_preset_name: AM_2\n#Custom_preset_module: CC1101\n#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00\n```\n"
  },
  {
    "path": "documentation/file_formats/TarHeatshrinkFormat.md",
    "content": "# Heatshrink-compressed Tarball Format {#heatshrink_file_format}\n\nFlipper supports the use of Heatshrink compression library for `.tar` archives. This allows for smaller file sizes and faster OTA updates. \n\nHeatshrink specification does not define a container format for storing compression parameters. This document describes the format used by Flipper to store Heatshrink-compressed data streams.\n\n## Header\n\nHeader begins with a magic value, followed by a version number and compression parameters - window size and lookahead size.\n\nMagic value consists of 4 bytes: `0x48 0x53 0x44 0x53` (ASCII \"HSDS\", HeatShrink DataStream).\n\nVersion number is a single byte, currently set to `0x01`.\n\nWindow size is a single byte, representing the size of the sliding window used by the compressor. It corresponds to `-w` parameter in Heatshrink CLI.\n\nLookahead size is a single byte, representing the size of the lookahead buffer used by the compressor. It corresponds to `-l` parameter in Heatshrink CLI.\n\nTotal header size is 7 bytes. Header is followed by compressed data.\n"
  },
  {
    "path": "documentation/file_formats/iButtonFileFormat.md",
    "content": "# iButton key file format {#ibutton_file_format}\n\n## Example\n\n```\nFiletype: Flipper iButton key\nVersion: 2\nProtocol: DS1992\nRom Data: 08 DE AD BE EF FA CE 4E\nSram Data: 4E 65 76 65 72 47 6F 6E 6E 61 47 69 76 65 59 6F 75 55 70 4E 65 76 65 72 47 6F 6E 6E 61 4C 65 74 59 6F 75 44 6F 77 6E 4E 65 76 65 72 47 6F 6E 6E 61 52 75 6E 41 72 6F 75 6E 64 41 6E 64 44 65 73 65 72 74 59 6F 75 4E 65 76 65 72 47 6F 6E 6E 61 4D 61 6B 65 59 6F 75 43 72 79 4E 65 76 65 72 47 6F 6E 6E 61 53 61 79 47 6F 6F 64 62 79 65 4E 65 76 65 72 47 6F 6E 6E 61 54 65 6C 6C 41 4C 69 65\n```\n\n## Description\n\nFilename extension: `.ibtn`\n\nThe file stores a single iButton key, complete with all data required by the protocol.\n\n## Version history\n### 2. Current version.\nChangelog:\n- Added support for different Dallas protocols\n- Fields after `Protocol` are protocol-dependent for flexibiliy\n\n#### Format fields\n\n| Name        | Type   | Description                                  |\n| ----------- | ------ | -------------------------------------------- |\n| Protocol    | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DSGeneric*, Cyfral, Metakom |\n| Rom Data    | hex    | Read-only memory data (Dallas protocols only) |\n| Sram Data   | hex    | Static RAM data (DS1992 and DS1996 only)\n| Eeprom Data | hex    | EEPROM data (DS1971 only)\n| Data        | hex    | Key data (Cyfral & Metakom only)              |\n\nNOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. \nIt can also be used if a key with a deliberately invalid family code or checksum is required.\n\nNOTE 2: When adding new protocols, it is not necessarily to increase the format version, define the format in the protocol implementation instead.\n\n### 1. Initial version.\nDeprecated, will be converted to current version upon saving.\n\n#### Format fields\n\n| Name     | Type   | Description                                  |\n| -------- | ------ | -------------------------------------------- |\n| Key type | string | Currently supported: Cyfral, Dallas, Metakom |\n| Data     | hex    | Key data                                     |\n\n\n\n"
  },
  {
    "path": "documentation/js/js_about.md",
    "content": "# About the JavaScript engine {#js_about_js_engine}\n\n> Developing applications for Flipper Zero is now much more accessible with the introduction of JavaScript support.\n\nPreviously, building an app for Flipper Zero required C/C++ skills, setting up a development environment, and studying the code of existing applications and documentation. While embedded developers are very familiar with all of this, we wanted to make it easier for people from all backgrounds to create apps for Flipper Zero.\n\nFlipper firmware now includes a built-in scripting engine that runs JavaScript, one of the most widely used programming languages. You can create script files, share them with others, and launch them directly from the **Apps/Scripts** menu on your Flipper Zero — no need for compiling on a PC.\n\nJavaScript support is based on the [mJS scripting engine](https://github.com/cesanta/mjs). Originally designed for microcontrollers, mJS makes efficient use of system resources, requiring less than 50k of flash space and 2k of RAM. We've kept the core features of mJS and also added some useful improvements, such as support for compact binary arrays.\n\n> [!note]\n> mJS has some limitations compared to JavaScript engines built into modern browsers. For details on capabilities and limitations, refer to the [mJS documentation on GitHub](https://github.com/cesanta/mjs).\n\nJavaScript apps can interact with Flipper Zero's resources, including its GUI, buttons, USB-HID device, GPIO, UART interfaces, and more. Let's go through the steps to create your first JavaScript app for Flipper Zero.\n\n**Next step:** [Your first JavaScript app](#js_your_first_js_app)\n"
  },
  {
    "path": "documentation/js/js_badusb.md",
    "content": "# BadUSB module {#js_badusb}\n\n```js\nlet badusb = require(\"badusb\");\n```\n# Methods\n## setup()\nStart USB HID with optional parameters. Should be called before all other methods.\n\n**Parameters**\n\nConfiguration object *(optional)*:\n- vid, pid (number): VID and PID values, both are mandatory\n- mfrName (string): Manufacturer name (32  ASCII characters max), optional\n- prodName (string): Product name (32  ASCII characters max), optional\n- layoutPath (string): Path to keyboard layout file, optional\n\n**Examples**\n```js\n// Start USB HID with default parameters\nbadusb.setup();\n// Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer and product strings not defined\nbadusb.setup({ vid: 0xAAAA, pid: 0xBBBB }); \n// Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer string = \"Flipper Devices\", product string = \"Flipper Zero\"\nbadusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfrName: \"Flipper Devices\", prodName: \"Flipper Zero\" });\n```\n\n<br>\n\n## isConnected()\nReturns USB connection state.\n\n**Example**\n```js\nif (badusb.isConnected()) {\n    // Do something\n} else {\n    // Show an error\n}\n```\n\n<br>\n\n## press()\nPress and release a key.\n\n**Parameters**\n\nKey or modifier name, key code.\n\nSee a [list of key names below](#js_badusb_keynames).\n\n**Examples**\n```js\nbadusb.press(\"a\"); // Press \"a\" key\nbadusb.press(\"A\"); // SHIFT + \"a\"\nbadusb.press(\"CTRL\", \"a\"); // CTRL + \"a\"\nbadusb.press(\"CTRL\", \"SHIFT\", \"ESC\"); // CTRL + SHIFT + ESC combo\nbadusb.press(98); // Press key with HID code (dec) 98 (Numpad 0 / Insert)\nbadusb.press(0x47); // Press key with HID code (hex) 0x47 (Scroll lock)\n```\n\n<br>\n\n## hold()\nHold a key. Up to 5 keys (excluding modifiers) can be held simultaneously.\n\n**Parameters**\n\nSame as `press`.\n\n**Examples**\n```js\nbadusb.hold(\"a\"); // Press and hold \"a\" key\nbadusb.hold(\"CTRL\", \"v\"); // Press and hold CTRL + \"v\" combo\n```\n\n<br>\n\n## release()\nRelease a previously held key.\n\n**Parameters**\n\nSame as `press`.\n\nRelease all keys if called without parameters.\n\n**Examples**\n```js\nbadusb.release(); // Release all keys\nbadusb.release(\"a\"); // Release \"a\" key\n```\n\n<br>\n\n## print()\nPrint a string.\n\n**Parameters**\n\n- A string to print\n- *(optional)* Delay between key presses\n\n**Examples**\n```js\nbadusb.print(\"Hello, world!\"); // print \"Hello, world!\"\nbadusb.print(\"Hello, world!\", 100); // Add 100ms delay between key presses\n```\n<br>\n\n## println()\nSame as `print` but ended with \"ENTER\" press.\n\n**Parameters**\n\n- A string to print\n- *(optional)* Delay between key presses\n\n**Examples**\n```js\nbadusb.println(\"Hello, world!\");  // print \"Hello, world!\" and press \"ENTER\"\n```\n<br>\n\n## altPrint()\nPrints a string by Alt+Numpad method - works only on Windows!\n\n**Parameters**\n\n- A string to print\n- *(optional)* delay between key presses\n\n**Examples**\n```js\nbadusb.altPrint(\"Hello, world!\"); // print \"Hello, world!\"\nbadusb.altPrint(\"Hello, world!\", 100); // Add 100ms delay between key presses\n```\n<br>\n\n## altPrintln()\nSame as `altPrint` but ended with \"ENTER\" press.\n\n**Parameters**\n\n- A string to print\n- *(optional)* delay between key presses\n\n**Examples**\n```js\nbadusb.altPrintln(\"Hello, world!\");  // print \"Hello, world!\" and press \"ENTER\"\n```\n<br>\n\n## quit()\nReleases usb, optional, but allows to interchange with usbdisk.\n\n**Examples**\n```js\nbadusb.quit();\nusbdisk.start(...)\n```\n<br>\n\n# Key names list {#js_badusb_keynames}\n\n## Modifier keys\n\n| Name          |\n| ------------- |\n| CTRL          |\n| SHIFT         |\n| ALT           |\n| GUI           |\n\n## Special keys\n\n| Name               | Notes            |\n| ------------------ | ---------------- |\n| DOWN               | Down arrow       |\n| LEFT               | Left arrow       |\n| RIGHT              | Right arrow      |\n| UP                 | Up arrow         |\n| ENTER              |                  |\n| DELETE             |                  |\n| BACKSPACE          |                  |\n| END                |                  |\n| HOME               |                  |\n| ESC                |                  |\n| INSERT             |                  |\n| PAGEUP             |                  |\n| PAGEDOWN           |                  |\n| CAPSLOCK           |                  |\n| NUMLOCK            |                  |\n| SCROLLLOCK         |                  |\n| PRINTSCREEN        |                  |\n| PAUSE              | Pause/Break key  |\n| SPACE              |                  |\n| TAB                |                  |\n| MENU               | Context menu key |\n| Fx                 | F1-F24 keys      |\n| NUMx               | NUM0-NUM9 keys   |\n"
  },
  {
    "path": "documentation/js/js_builtin.md",
    "content": "# Built-in methods {#js_builtin}\n\n## require()\nLoad a module plugin.\n\n**Parameters**\n- Module name\n\n**Examples**\n```js\nlet serial = require(\"serial\"); // Load \"serial\" module\n```\n\n<br>\n\n## delay()\n**Parameters**\n- Delay value in ms\n\n**Examples**\n```js\ndelay(500); // Delay for 500ms\n```\n<br>\n\n## print()\nPrint a message on a screen console.\n\n**Parameters**\nThe following argument types are supported:\n- String\n- Number\n- Bool\n- undefined\n\n**Examples**\n```js\nprint(\"string1\", \"string2\", 123);\n```\n<br>\n\n## Console object\nSame as `print`, but output to serial console only, with corresponding log level.\n\n### console.log()\n\n<br>\n\n### console.warn()\n\n<br>\n\n### console.error()\n\n<br>\n\n### console.debug()\n\n<br>\n\n## load()\nRuns a JS file and returns value from it.\n\n**Parameters**\n- The path to the file\n- An optional object to use as the global scope while running this file\n\n**Examples**\n```js\nload(\"/ext/apps/Scripts/script.js\");\n```\n<br>\n\n## chr()\nConvert an ASCII character number to string.\n\n**Examples**\n```js\nchr(65); // \"A\"\n```\n<br>\n\n## die()\nExit JavaScript with given message.\n\n**Examples**\n```js\ndie(\"Some error occurred\");\n```\n<br>\n\n## parseInt()\nConvert a string to number with an optional base.\n\n**Examples**\n```js\nparseInt(\"123\"); // 123\nparseInt(\"7b\", 16); // 123\n```\n<br>\n\n## Number object\n\n### Number.toString()\nConvert a number to string with an optional base.\n\n**Examples**\n```js\nlet num = 123;\nnum.toString(); // \"123\"\nnum.toString(16); // \"0x7b\"\n```\n<br>\n\n## ArrayBuffer object\n\n**Fields**\n\n- byteLength: The length of the buffer in bytes\n<br>\n\n### ArrayBuffer.slice()\nCreates an `ArrayBuffer` that contains a sub-part of the buffer.\n\n**Parameters**\n- The index to start the new buffer at\n- An optional non-inclusive index of where to stop the new buffer\n\n**Examples**\n```js\nUint8Array([1, 2, 3]).buffer.slice(0, 1) // ArrayBuffer([1])\n```\n<br>\n\n## DataView objects\nWrappers around `ArrayBuffer` objects, with dedicated types such as:\n- `Uint8Array`\n- `Int8Array`\n- `Uint16Array`\n- `Int16Array`\n- `Uint32Array`\n- `Int32Array`\n\n**Fields**\n\n- byteLength: The length of the buffer in bytes\n- length: The length of the buffer in typed elements\n- buffer: The underlying `ArrayBuffer`\n<br>\n\n## Array object\n\n**Fields**\n\n- length: How many elements there are in the array\n<br>\n\n### Array.splice()\nRemoves elements from the array and returns them in a new array.\n\n**Parameters**\n- The index to start taking elements from\n- An optional count of how many elements to take\n\n**Examples**\n```js\nlet arr = [1, 2, 3];\narr.splice(1); // [2, 3]\narr; // [1]\n```\n<br>\n\n### Array.push()\nAdds a value to the end of the array.\n\n**Examples**\n```js\nlet arr = [1, 2];\narr.push(3);\narr; // [1, 2, 3]\n```\n<br>\n\n## String object\n\n**Fields**\n\n- length: How many characters there are in the string\n<br>\n\n### String.charCodeAt()\nReturns the character code at an index in the string.\n\n**Examples**\n```js\n\"A\".charCodeAt(0) // 65\n```\n<br>\n\n### String.at()\nSame as `String.charCodeAt()`.\n<br>\n\n### String.indexOf()\nReturn index of first occurrence of substr within the string or `-1` if not found.\n\n**Parameters**\n- Substring to search for\n- Optional index to start searching from\n\n**Examples**\n```js\n\"Example\".indexOf(\"amp\") // 2\n```\n<br>\n\n### String.slice()\nReturn a substring between two indices.\n\n**Parameters**\n- The index to start the new string at\n- An optional non-inclusive index of where to stop the new string\n\n**Examples**\n```js\n\"Example\".slice(2) // \"ample\"\n```\n<br>\n\n### String.toUpperCase()\nTransforms the string to upper case.\n\n**Examples**\n```js\n\"Example\".toUpperCase() // \"EXAMPLE\"\n```\n<br>\n\n### String.toLowerCase()\nTransforms the string to lower case.\n\n**Examples**\n```js\n\"Example\".toLowerCase() // \"example\"\n```\n<br>\n\n## __dirname\nPath to the directory containing the current script.\n\n**Examples**\n```js\nprint(__dirname); // /ext/apps/Scripts\n```\n<br>\n\n## __filename\nPath to the current script file.\n\n**Examples**\n```js\nprint(__filename); // /ext/apps/Scripts/path.js\n```\n<br>\n\n# SDK compatibility methods {#js_builtin_sdk_compatibility}\n\n## sdkCompatibilityStatus()\nChecks compatibility between the script and the JS SDK that the firmware provides.\n\n**Returns**\n- `\"compatible\"` if the script and the JS SDK are compatible\n- `\"firmwareTooOld\"` if the expected major version is larger than the version of the firmware, or if the expected minor version is larger than the version of the firmware\n- `\"firmwareTooNew\"` if the expected major version is lower than the version of the firmware\n\n**Examples**\n```js\nsdkCompatibilityStatus(0, 3); // \"compatible\"\n```\n<br>\n\n## isSdkCompatible()\nChecks compatibility between the script and the JS SDK that the firmware provides in a boolean fashion.\n\n**Examples**\n```js\nisSdkCompatible(0, 3); // true\n```\n<br>\n\n## checkSdkCompatibility()\nAsks the user whether to continue executing the script if the versions are not compatible. Does nothing if they are.\n\n**Examples**\n```js\ncheckSdkCompatibility(0, 3);\n```\n<br>\n\n## doesSdkSupport()\nChecks whether all of the specified extra features are supported by the interpreter.\n\n**Examples**\n```js\ndoesSdkSupport([\"gui-widget\"]); // true\n```\n<br>\n\n## checkSdkFeatures()\nChecks whether all of the specified extra features are supported by the interpreter, asking the user if they want to continue running the script if they're not.\n\n**Examples**\n```js\ncheckSdkFeatures([\"gui-widget\"]);\n```\n"
  },
  {
    "path": "documentation/js/js_data_types.md",
    "content": "# Data types {#js_data_types}\n\nHere is a list of common data types used by mJS.\n- string — sequence of single byte characters, no UTF8 support\n- number\n- boolean\n- foreign — C function or data pointer\n- undefined\n- null\n- Object — a data structure with named fields\n- Array — special type of object, all items have indexes and equal types\n- ArrayBuffer — raw data buffer\n- DataView — provides interface for accessing ArrayBuffer contents\n"
  },
  {
    "path": "documentation/js/js_developing_apps_using_js_sdk.md",
    "content": "# Developing apps using JavaScript SDK {#js_developing_apps_using_js_sdk}\n\nIn the [previous guide](#js_your_first_js_app), we learned how to create and run a JavaScript app on Flipper Zero. However, when debugging a script, you often need to repeatedly modify the code and test it on the device. While you can use qFlipper for this, it involves a lot of repetitive steps. Fortunately, there's a more efficient alternative — the Flipper Zero JavaScript SDK, a set of tools that simplify app development in JavaScript.\n\nMain features of the Flipper Zero JavaScript SDK:\n\n* [Loading and running an app with a single command](#js_sdk_run_app)\n* [Code completion](#js_sdk_code_completion)\n* [JS code minifier (compressor)](#js_sdk_js_minifier)\n\nIn this guide, we'll install the JavaScript SDK and learn how to run JavaScript apps on Flipper Zero using it.\n\n## How to get JavaScript SDK\n\nThe JavaScript SDK for Flipper Zero is distributed as an [NPM package](npmjs.com/package/\\@flipperdevices/fz-sdk), so you can install it using a package manager like npm, pnpm, or yarn. You'll also need Node.js, a JavaScript runtime environment required for the NPM package manager to work.\n\n> [!note]\n> In this guide, we'll use **npm**, the default package manager for Node.js.\n\nFollow these steps:\n\n1. Install **Node.js + npm** on your PC. Check out this [official Downloads page](https://nodejs.org/en/download/package-manager), select your OS and preferences, and run the provided commands in your terminal.\n\n2. Open a terminal in the folder where you want to store your project.\n\n3. Run the `npx @flipperdevices/create-fz-app@latest` command to create a JavaScript app template and include the JavaScript SDK into it. This command will launch an interactive wizard. You'll need to specify the project name and choose a package manager (in our case, **npm**).\n\nYou'll now find a JavaScript app template in your project folder, alongside the JavaScript SDK package, all necessary dependencies and configs. The app code will be in the `index.ts` file.\n\nNow, let's take a look at the main features of the Flipper Zero JavaScript SDK.\n\n## Running your app {#js_sdk_run_app}\n\nTo run the application:\n\n1. Connect your Flipper Zero to your PC via USB.\n\n2. Open a terminal in your app's folder.\n\n3. Run the `npm start` command to copy the JS file to Flipper Zero and run it.\n\n\\image html js_sdk_npm_start.jpg width=800\n\nYou'll see output messages from the `print()` function in the terminal.\n\n## Updating your app {#js_sdk_update_app}\n\nAfter making changes to your app's code, simply run `npm start` again. As long as your Flipper Zero is still connected, the updated app will launch, and the old `.js` file on Flipper Zero will be replaced with the new version.\n\n\n## Other JavaScript SDK features\n\nAs you can see, it's quite easy to launch and update your app with a single command. Now let's explore two more important features of the Flipper Zero JavaScript SDK: **code completion** and **JS minifier**.\n\n\n### Code completion {#js_sdk_code_completion}\n\nCode completion helps speed up the development process by automatically suggesting code as you type, reducing the need to refer to documentation.\n\n\\image html js_sdk_code_completion.jpg width=800\n\n> [!note]\n> Code completion works in code editors and IDEs that support Language Server, for example,  [VS Code](https://code.visualstudio.com/).\n\n\n### JS minifier {#js_sdk_js_minifier}\n\nThe JS minifier reduces the size of JavaScript files by removing unnecessary characters (like spaces, tabs and line breaks) and shortening variable names. This can make your scripts run a bit faster without changing their logic.\n\nHowever, it has a drawback — it can make debugging harder, as error messages in minified files are harder to read in larger applications. For this reason, it's recommended to disable the JS minifier during debugging and it's disabled by default. To enable it, set the `minify` parameter to `true` in the `fz-sdk.config.json5` file in your app folder. This will minify your JavaScript app before loading it onto Flipper Zero.\n\n\n## Differences with normal Flipper JavaScript\n\nWith the Flipper JavaScript SDK, you will be developing in **TypeScript**. This means that you get a better development experience, with more accurate code completion and warnings when variable types are incompatible, but it also means your code will be different from basic Flipper JS.\n\nSome things to look out for:\n- Importing modules:\n  - Instead of `let module = require(\"module\");`\n  - You will use `import * as module from \"@flipperdevices/fz-sdk/module\";`\n- Multiple source code files:\n  - The Flipper JavaScript SDK does not yet support having multiple `.ts` files and importing them\n  - You can use `load()`, but this will not benefit from TypeScript type checking\n- Casting values:\n  - Some Flipper JavaScript functions will return generic types\n  - For example `eventLoop.subscribe()` will run your callback with a generic `Item` type\n  - In some cases you might need to cast these values before using them, you can do this by:\n  - Inline casting: `<string>item`\n  - Declare with new type: `let text = item as string;`\n\nWhen you upload the script to Flipper with `npm start`, it gets transpiled to normal JavaScript and optionally minified (see below). If you're looking to share your script with others, this is what you should give them to run.\n\n\n## What's next?\n\nYou've learned how to run and debug simple JavaScript apps. But how can you access Flipper Zero's hardware from your JS code? For that, you'll need to use JS modules — which we'll cover in the next guide.\n\n**Next step:** [Using JavaScript modules](#js_using_js_modules)\n"
  },
  {
    "path": "documentation/js/js_event_loop.md",
    "content": "# Event Loop module {#js_event_loop}\n\nThe event loop is central to event-based programming in many frameworks, and our\nJS subsystem is no exception. It is a good idea to familiarize yourself with the\nevent loop first before using any of the advanced modules (e.g. GPIO and GUI).\n\n```js\nlet eventLoop = require(\"event_loop\");\n```\n\n## Conceptualizing the event loop\nIf you've ever written JavaScript code before, you've definitely seen callbacks. It's\nwhen a function takes another function (usually an anonymous one) as one of\nthe arguments, which it will call later, e.g. when an event happens or when\ndata becomes ready:\n```js\nsetTimeout(function() { console.log(\"Hello, World!\") }, 1000);\n```\n\nMany JavaScript engines employ a queue from which the runtime fetches events as\nthey occur, subsequently calling the corresponding callbacks. This is done in a\nlong-running loop, hence the name \"event loop\". Here's the pseudocode for a\ntypical event loop:\n\n\\code{.js}\nwhile(loop_is_running()) {\n    if(event_available_in_queue()) {\n        let event = fetch_event_from_queue();\n        let callback = get_callback_associated_with(event);\n        if(callback)\n            callback(get_extra_data_for(event));\n    } else {\n        // avoid wasting CPU time\n        sleep_until_any_event_becomes_available();\n    }\n}\n\\endcode\n\nMost JS runtimes enclose the event loop within themselves, so that most JS\nprogrammers don't even need to be aware of its existence. This is not the\ncase with our JS subsystem.\n\n---\n\n# Example\nThis is how one would write something similar to the `setTimeout` example above:\n```js\n// import module\nlet eventLoop = require(\"event_loop\");\n\n// create an event source that will fire once 1 second after it has been created\nlet timer = eventLoop.timer(\"oneshot\", 1000);\n\n// subscribe a callback to the event source\neventLoop.subscribe(timer, function(_subscription, _item, eventLoop) {\n    print(\"Hello, World!\");\n    eventLoop.stop();\n}, eventLoop); // notice this extra argument. we'll come back to this later\n\n// run the loop until it is stopped\neventLoop.run();\n\n// the previous line will only finish executing once `.stop()` is called, hence\n// the following line will execute only after \"Hello, World!\" is printed\nprint(\"Stopped\");\n```\n\nI promised you that we'll come back to the extra argument after the callback\nfunction. Our JavaScript engine does not support closures (anonymous functions\nthat access values outside of their arguments), so we ask `subscribe` to pass an\noutside value (namely, `eventLoop`) as an argument to the callback so that we\ncan access it. We can modify this extra state:\n```js\n// this timer will fire every second\nlet timer = eventLoop.timer(\"periodic\", 1000);\neventLoop.subscribe(timer, function(_subscription, _item, counter, eventLoop) {\n    print(\"Counter is at:\", counter);\n    if(counter === 10)\n        eventLoop.stop();\n    // modify the extra arguments that will be passed to us the next time\n    return [counter + 1, eventLoop];\n}, 0, eventLoop);\n```\n\nBecause we have two extra arguments, if we return anything other than an array\nof length 2, the arguments will be kept as-is for the next call.\n\nThe first two arguments that get passed to our callback are:\n  - The subscription manager that lets us `.cancel()` our subscription.\n  - The event item, used for events that have extra data. Timer events do not,\n    they just produce `undefined`.\n\n---\n\n# API reference\n## run()\nRuns the event loop until it is stopped with `stop`.\n\n<br>\n\n## subscribe()\nSubscribes a function to an event.\n\n**Parameters**\n  - `contract`: an event source identifier\n  - `callback`: the function to call when the event happens\n  - extra arguments: will be passed as extra arguments to the callback\n\nThe callback will be called with at least two arguments, plus however many were\npassed as extra arguments to `subscribe`. The first argument is the subscription\nmanager (the same one that `subscribe` itself returns). The second argument is\nthe event item for events that produce extra data; the ones that don't set this\nto `undefined`. The callback may return an array of the same length as the count\nof the extra arguments to modify them for the next time that the event handler\nis called. Any other returns values are discarded.\n\n**Returns**\n\nA `SubscriptionManager` object:\n  - `SubscriptionManager.cancel()`: unsubscribes the callback from the event\n\n**Warning**\n\nEach event source may only have one callback associated with it.\n\n<br>\n\n## stop()\nStops the event loop.\n\n<br>\n\n## timer()\nProduces an event source that fires with a constant interval either once or\nindefinitely.\n\n**Parameters**\n  - `mode`: either `\"oneshot\"` or `\"periodic\"`\n  - `interval`: the timeout (for `\"oneshot\"`) timers or the period (for\n    `\"periodic\"` timers)\n\n**Returns**\n\nA `Contract` object, as expected by `subscribe`'s first parameter.\n\n<br>\n\n## queue()\nProduces a queue that can be used to exchange messages.\n\n**Parameters**\n  - `length`: the maximum number of items that the queue may contain\n\n**Returns**\n\nA `Queue` object:\n  - `Queue.send(message)`:\n    - `message`: a value of any type that will be placed at the end of the queue\n  - `input`: a `Contract` (event source) that pops items from the front of the\n    queue\n"
  },
  {
    "path": "documentation/js/js_flipper.md",
    "content": "# Flipper module {#js_flipper}\n\nThe module contains methods and values to query device information and properties. Call the `require` function to load the module before first using its methods:\n\n```js\nlet flipper = require(\"flipper\");\n```\n\n# Values\n\n## firmwareVendor\nString representing the firmware installed on the device.\nOriginal firmware reports `\"flipperdevices\"`.\nDo **NOT** use this to check the presence or absence of features, refer to [other ways to check SDK compatibility](#js_builtin_sdk_compatibility).\n\n## jsSdkVersion\nVersion of the JavaScript SDK.\nDo **NOT** use this to check the presence or absence of features, refer to [other ways to check SDK compatibility](#js_builtin_sdk_compatibility).\n\n<br>\n\n---\n\n# Methods\n\n## getModel()\nReturns the device model.\n\n**Example**\n```js\nflipper.getModel(); // \"Flipper Zero\"\n```\n\n<br>\n\n## getName()\nReturns the name of the virtual dolphin.\n\n**Example**\n```js\nflipper.getName(); // \"Fur1pp44\"\n```\n\n<br>\n\n## getBatteryCharge()\nReturns the battery charge percentage.\n\n**Example**\n```js\nflipper.getBatteryCharge(); // 100\n```\n"
  },
  {
    "path": "documentation/js/js_gpio.md",
    "content": "# GPIO module {#js_gpio}\n\nThe module allows you to control GPIO pins of the expansion connector on Flipper Zero. Call the `require` function to load the module before first using its methods. This module depends on the `event_loop` module, so it **must** be imported after `event_loop` is imported:\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gpio = require(\"gpio\");\n```\n\n# Example\n```js\nlet eventLoop = require(\"event_loop\");\nlet gpio = require(\"gpio\");\n\nlet led = gpio.get(\"pc3\");\nled.init({ direction: \"out\", outMode: \"push_pull\" });\n\nled.write(true);\ndelay(1000);\nled.write(false);\ndelay(1000);\n```\n\n---\n\n# API reference\n## get()\nGets a `Pin` object that can be used to manage a pin.\n\n**Parameters**\n  - `pin`: pin identifier (examples: `\"pc3\"`, `7`, `\"pa6\"`, `3`)\n\n**Returns**\n\nA `Pin` object.\n\n<br>\n\n## Pin object\n### Pin.init()\nConfigures a pin.\n\n**Parameters**\n  - `mode`: `Mode` object:\n    - `direction` (required): either `\"in\"` or `\"out\"`\n    - `outMode` (required for `direction: \"out\"`): either `\"open_drain\"` or\n      `\"push_pull\"`\n    - `inMode` (required for `direction: \"in\"`): either `\"analog\"`,\n      `\"plain_digital\"`, `\"interrupt\"` or `\"event\"`\n    - `edge` (required for `inMode: \"interrupt\"` or `\"event\"`): either\n      `\"rising\"`, `\"falling\"` or `\"both\"`\n    - `pull` (optional): either `\"up\"`, `\"down\"` or unset\n\n<br>\n\n### Pin.write()\nWrites a digital value to a pin configured with `direction: \"out\"`.\n\n**Parameters**\n  - `value`: boolean logic level to write\n\n<br>\n\n### Pin.read()\nReads a digital value from a pin configured with `direction: \"in\"` and any\n`inMode` except `\"analog\"`.\n\n**Returns**\n\nBoolean logic level.\n\n<br>\n\n### Pin.readAnalog()\nReads an analog voltage level in millivolts from a pin configured with\n`direction: \"in\"` and `inMode: \"analog\"`.\n\n**Returns**\n\nVoltage on pin in millivolts.\n\n<br>\n\n### Pin.interrupt()\nAttaches an interrupt to a pin configured with `direction: \"in\"` and\n`inMode: \"interrupt\"` or `\"event\"`.\n\n**Returns**\n\nAn event loop `Contract` object that identifies the interrupt event source. The\nevent does not produce any extra data.\n\n### Pin.isPwmSupported()\nDetermines whether this pin supports PWM.\nIf `false`, all other PWM-related methods on this pin will throw an error when called.\n\n**Returns**\n\nBoolean value.\n\n### Pin.pwmWrite()\nSets PWM parameters and starts the PWM.\nConfigures the pin with `{ direction: \"out\", outMode: \"push_pull\" }`.\nThrows an error if PWM is not supported on this pin.\n\n**Parameters**\n  - `freq`: Frequency in Hz\n  - `duty`: Duty cycle in %\n\n### Pin.isPwmRunning()\nDetermines whether PWM is running.\nThrows an error if PWM is not supported on this pin.\n\n**Returns**\n\nBoolean value.\n\n### Pin.pwmStop()\nStops PWM.\nDoes not restore previous pin configuration.\nThrows an error if PWM is not supported on this pin.\n"
  },
  {
    "path": "documentation/js/js_gui.md",
    "content": "# GUI module {#js_gui}\n\nThe module allows you to use GUI (graphical user interface) in concepts off the Flipper Zero firmware. Call the `require` function to load the module before first using its methods. This module depends on the `event_loop` module, so it **must** be imported after the `event_loop` import:\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\n```\n## Submodules\n\nGUI module has several submodules:\n\n- @subpage js_gui__byte_input — Keyboard-like hex input\n- @subpage js_gui__dialog — Dialog with up to 3 options\n- @subpage js_gui__empty_screen — Just empty screen\n- @subpage js_gui__file_picker — Displays a file selection prompt\n- @subpage js_gui__icon — Retrieves and loads icons for use in GUI\n- @subpage js_gui__loading — Displays an animated hourglass icon\n- @subpage js_gui__submenu — Displays a scrollable list of clickable textual entries\n- @subpage js_gui__text_box — Simple multiline text box\n- @subpage js_gui__text_input — Keyboard-like text input\n- @subpage js_gui__widget — Displays a combination of custom elements on one screen\n\n---\n\n## Conceptualizing GUI\n### Event loop\nIt is highly recommended to familiarize yourself with the event loop first\nbefore doing GUI-related things.\n\n### Canvas\nThe canvas is just a drawing area with no abstractions over it. Drawing on the\ncanvas directly (i.e. not through a viewport) is useful in case you want to\nimplement a custom design element, but this is rather uncommon.\n\n### Viewport\nA viewport is a window into a rectangular portion of the canvas. Applications\nalways access the canvas through a viewport.\n\n### View\nIn Flipper's terminology, a \"View\" is a fullscreen design element that assumes\ncontrol over the entire viewport and all input events. Different types of views\nare available (not all of which are unfortunately currently implemented in JS):\n| View                 | Has JS adapter?       |\n|----------------------|-----------------------|\n| `button_menu`        | ❌                    |\n| `button_panel`       | ❌                    |\n| `byte_input`         | ✅                    |\n| `dialog_ex`          | ✅ (as `dialog`)      |\n| `empty_screen`       | ✅                    |\n| `file_browser`       | ✅ (as `file_picker`) |\n| `loading`            | ✅                    |\n| `menu`               | ❌                    |\n| `number_input`       | ❌                    |\n| `popup`              | ❌                    |\n| `submenu`            | ✅                    |\n| `text_box`           | ✅                    |\n| `text_input`         | ✅                    |\n| `variable_item_list` | ❌                    |\n| `widget`             | ✅                    |\n\nIn JS, each view has its own set of properties (or just \"props\"). The programmer\ncan manipulate these properties in two ways:\n  - Instantiate a `View` using the `makeWith(props)` method, passing an object\n    with the initial properties\n  - Call `set(name, value)` to modify a property of an existing `View`\n\n### View Dispatcher\nThe view dispatcher holds references to all the views that an application needs\nand switches between them as the application makes requests to do so.\n\n### Scene Manager\nThe scene manager is an optional add-on to the view dispatcher that makes\nmanaging applications with complex navigation flows easier. It is currently\ninaccessible from JS.\n\n### Approaches\nIn total, there are three different approaches that you may take when writing\na GUI application:\n| Approach       | Use cases                                                                    | Available from JS |\n|----------------|------------------------------------------------------------------------------|-------------------|\n| ViewPort only  | Accessing the graphics API directly, without any of the nice UI abstractions | ❌                |\n| ViewDispatcher | Common UI elements that fit with the overall look of the system              | ✅                |\n| SceneManager   | Additional navigation flow management for complex applications               | ❌                |\n\n---\n\n## Example\nAn example with three different views using the ViewDispatcher approach:\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet loadingView = require(\"gui/loading\");\nlet submenuView = require(\"gui/submenu\");\nlet emptyView = require(\"gui/empty_screen\");\n\n// Common pattern: declare all the views in an object. This is absolutely not\n// required, but adds clarity to the script.\nlet views = {\n    // the view dispatcher auto-✨magically✨ remembers views as they are created\n    loading: loadingView.make(),\n    empty: emptyView.make(),\n    demos: submenuView.makeWith({\n        items: [\n            \"Hourglass screen\",\n            \"Empty screen\",\n            \"Exit app\",\n        ],\n    }),\n};\n\n// go to different screens depending on what was selected\neventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {\n    if (index === 0) {\n        gui.viewDispatcher.switchTo(views.loading);\n    } else if (index === 1) {\n        gui.viewDispatcher.switchTo(views.empty);\n    } else if (index === 2) {\n        eventLoop.stop();\n    }\n}, gui, eventLoop, views);\n\n// go to the demo chooser screen when the back key is pressed\neventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views) {\n    gui.viewDispatcher.switchTo(views.demos);\n}, gui, views);\n\n// run UI\ngui.viewDispatcher.switchTo(views.demos);\neventLoop.run();\n```\n\n---\n\n# API reference\n## viewDispatcher\nThe `viewDispatcher` constant holds the `ViewDispatcher` singleton.\n\n<br>\n\n### viewDispatcher.switchTo(view)\nSwitches to a view, giving it control over the display and input.\n\n**Parameters**\n  - `view`: the `View` to switch to\n\n<br>\n\n### viewDispatcher.sendTo(direction)\nSends the viewport that the dispatcher manages to the front of the stackup\n(effectively making it visible), or to the back (effectively making it\ninvisible).\n\n**Parameters**\n  - `direction`: either `\"front\"` or `\"back\"`\n\n<br>\n\n### viewDispatcher.sendCustom(event)\nSends a custom number to the `custom` event handler.\n\n**Parameters**\n  - `event`: number to send\n\n<br>\n\n### viewDispatcher.custom\nAn event loop `Contract` object that identifies the custom event source,\ntriggered by `ViewDispatcher.sendCustom(event)`.\n\n<br>\n\n### viewDispatcher.navigation\nAn event loop `Contract` object that identifies the navigation event source,\ntriggered when the back key is pressed.\n\n<br>\n\n### viewDispatcher.currentView\nThe `View` object currently being shown.\n\n<br>\n\n## ViewFactory\nWhen you import a module implementing a view, a `ViewFactory` is instantiated. For example, in the example above, `loadingView`, `submenuView` and `emptyView` are view factories.\n\n<br>\n\n### ViewFactory.make()\nCreates an instance of a `View`.\n\n<br>\n\n### ViewFactory.makeWith(props, children)\nCreates an instance of a `View` and assigns initial properties from `props` and optionally a list of children.\n\n**Parameters**\n  - `props`: simple key-value object, e.g. `{ header: \"Header\" }`\n  - `children`: optional array of children, e.g. `[ { element: \"button\", button: \"right\", text: \"Back\" } ]`\n\n## View\nWhen you call `ViewFactory.make()` or `ViewFactory.makeWith()`, a `View` is instantiated. For example, in the example above, `views.loading`, `views.demos` and `views.empty` are views.\n\n<br>\n\n### View.set(property, value)\nAssign value to property by name.\n\n**Parameters**\n  - `property`: name of the property to change\n  - `value`: value to assign to the property\n\n<br>\n\n### View.addChild(child)\nAdds a child to the `View`.\n\n**Parameters**\n  - `child`: the child to add, e.g. `{ element: \"button\", button: \"right\", text: \"Back\" }`\n\nThe format of the `child` parameter depends on the type of View that you're working with. Look in the View documentation.\n\n### View.resetChildren()\nRemoves all children from the `View`.\n\n### View.setChildren(children)\nRemoves all previous children from the `View` and assigns new children.\n\n**Parameters**\n  - `children`: the array of new children, e.g. `[ { element: \"button\", button: \"right\", text: \"Back\" } ]`\n"
  },
  {
    "path": "documentation/js/js_gui__byte_input.md",
    "content": "# Byte input GUI view {#js_gui__byte_input}\n\nDisplays a hexadecimal keyboard.\n\n<img src=\"byte_input.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet byteInputView = require(\"gui/byte_input\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n## Example\nFor an example refer to the `gui.js` example script.\n\n## View props\n\n| Prop        | Type   | Description                                      |\n|-------------|--------|--------------------------------------------------|\n| `length`      | `number` | The length in bytes of the buffer to modify.           |\n| `header`      | `string` | A single line of text that appears above the keyboard. |\n| `defaultData` | `string` | Data to show by default.                               |\n\n## View events\n\n| Item        | Type   | Description                                      |\n|-------------|--------|--------------------------------------------------|\n| `input`     | `ArrayBuffer` | Fires when the user selects the \"Save\" button. |\n"
  },
  {
    "path": "documentation/js/js_gui__dialog.md",
    "content": "# Dialog GUI view {#js_gui__dialog}\n\nDisplays a dialog with up to three options.\n\n<img src=\"dialog.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet dialogView = require(\"gui/dialog\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n# Example\nFor an example, refer to the `gui.js` example script.\n\n# View props\n\n| **Prop**   | **Type**  | **Description**                                                |\n|------------|-----------|----------------------------------------------------------------|\n| `header`   | string    | Text that appears in bold at the top of the screen.            |\n| `text`     | string    | Text that appears in the middle of the screen.                 |\n| `left`     | string    | Text for the left button. If unset, the left button does not show up. |\n| `center`   | string    | Text for the center button. If unset, the center button does not show up. |\n| `right`    | string    | Text for the right button. If unset, the right button does not show up. |\n\n# View events\n\n| Item     | Type   | Description                                                                 |\n|----------|--------|-----------------------------------------------------------------------------|\n| `input`  | `string`| Fires when the user presses on either of the three possible buttons. The item contains one of the strings `\"left\"`, `\"center\"`, or `\"right\"` depending on the button. |\n"
  },
  {
    "path": "documentation/js/js_gui__empty_screen.md",
    "content": "# Empty Screen GUI view {#js_gui__empty_screen}\n\nDisplays an empty screen.\n\n<img src=\"empty.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet emptyView = require(\"gui/empty_screen\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n# Example\nFor an example, refer to the GUI example.\n\n# View props\nThis view does not have any props.\n"
  },
  {
    "path": "documentation/js/js_gui__file_picker.md",
    "content": "# File Picker GUI prompt {#js_gui__file_picker}\n\nAllows asking the user to select a file.\nIt is not GUI view like other JS GUI views, rather just a function that shows a prompt.\n\n# Example\nFor an example, refer to the `gui.js` example script.\n\n# API reference\n\n## pickFile()\nDisplays a file picker and returns the selected file, or undefined if cancelled.\n\n**Parameters**\n  - `basePath`: the path to start at\n  - `extension`: the file extension(s) to show (like `.sub`, `.iso|.img`, `*`)\n\n**Returns**\n\nA `string` path, or `undefined`.\n"
  },
  {
    "path": "documentation/js/js_gui__icon.md",
    "content": "# GUI Icons {#js_gui__icon}\n\nRetrieves and loads icons for use with GUI views such as [Dialog](#js_gui__dialog).\n\n# Example\nFor an example, refer to the `gui.js` example script.\n\n# API reference\n\n## getBuiltin()\nGets a built-in firmware icon by its name.\nNot all icons are supported, currently only `\"DolphinWait_59x54\"` and `\"js_script_10px\"` are available.\n\n**Parameters**\n  - `icon`: name of the icon\n\n**Returns**\n\nAn `IconData` object.\n\n<br>\n\n## loadFxbm()\nLoads a `.fxbm` icon (XBM Flipper sprite, from `flipperzero-game-engine`) from file.\nIt will be automatically unloaded when the script exits.\n\n**Parameters**\n  - `path`: path to the `.fxbm` file\n\n**Returns**\n\nAn `IconData` object.\n"
  },
  {
    "path": "documentation/js/js_gui__loading.md",
    "content": "# Loading GUI view {#js_gui__loading}\n\nDisplays an animated hourglass icon. Suppresses all `navigation` events, making it impossible for the user to exit the view by pressing the BACK key.\n\n<img src=\"loading.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet loadingView = require(\"gui/loading\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n# Example\nFor an example refer to the GUI example.\n\n# View props\nThis view does not have any props.\n"
  },
  {
    "path": "documentation/js/js_gui__submenu.md",
    "content": "# Submenu GUI view {#js_gui__submenu}\n\nDisplays a scrollable list of clickable textual entries.\n\n<img src=\"submenu.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet submenuView = require(\"gui/submenu\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n## View props\n\n| Property | Type      | Description                               |\n|----------|-----------|-------------------------------------------|\n| `header` | `string`  | A single line of text that appears above the list. |\n| `items`  | `string[]`| The list of options.                     |\n\n\n## View events\n\n| Item     | Type    | Description                                                   |\n|----------|---------|---------------------------------------------------------------|\n| `chosen` | `number`| Fires when an entry has been chosen by the user. The item contains the index of the entry. |\n"
  },
  {
    "path": "documentation/js/js_gui__text_box.md",
    "content": "# Text box GUI view {#js_gui__text_box}\n\nDisplays a scrollable read-only text field.\n\n<img src=\"text_box.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet textBoxView = require(\"gui/text_box\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n## Example\nFor an example, refer to the `gui.js` example script.\n\n## View props\n\n| Prop     | Type    | Description                        |\n|----------|---------|------------------------------------|\n| `text`   | `string`| Text to show in the text box.                             |\n| `font`   | `string`| The font to display the text in (`\"text\"` or `\"hex\"`).    |\n| `focus`  | `string`| The initial focus of the text box (`\"start\"` or `\"end\"`). |\n"
  },
  {
    "path": "documentation/js/js_gui__text_input.md",
    "content": "# Text input GUI view {#js_gui__text_input}\n\nDisplays a keyboard.\n\n<img src=\"text_input.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet textInputView = require(\"gui/text_input\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n## Example\nFor an example, refer to the `gui.js` example script.\n\n## View props\n\n| Prop        | Type   | Description                                      |\n|-------------|--------|--------------------------------------------------|\n| `minLength`        | `number`  | The shortest allowed text length.                          |\n| `maxLength`        | `number`  | The longest allowed text length. <br> Default: `32`        |\n| `header`           | `string`  | A single line of text that appears above the keyboard.     |\n| `defaultText`      | `string`  | Text to show by default.                                   |\n| `defaultTextClear` | `boolean` | Whether to clear the default text on next character typed. |\n\n## View events\n\n| Item        | Type   | Description                                      |\n|-------------|--------|--------------------------------------------------|\n| `input`     | `string` | Fires when the user selects the \"Save\" button and the text matches the length constrained by `minLength` and `maxLength`. |\n"
  },
  {
    "path": "documentation/js/js_gui__widget.md",
    "content": "# Widget GUI view {#js_gui__widget}\n\nDisplays a combination of custom elements on one screen.\n\n<img src=\"widget.png\" width=\"200\" alt=\"Sample screenshot of the view\" />\n\n```js\nlet eventLoop = require(\"event_loop\");\nlet gui = require(\"gui\");\nlet widgetView = require(\"gui/widget\");\n```\n\nThis module depends on the `gui` module, which in turn depends on the\n`event_loop` module, so they **must** be imported in this order. It is also\nrecommended to conceptualize these modules first before using this one.\n\n## Example\nFor an example, refer to the `gui.js` example script.\n\n## View props\nThis view does not have any props.\n\n## Children\nThis view has the elements as its children.\nElements are objects with properties to define them, in the form `{ element: \"type\", ...properties }` (e.g. `{ element: \"button\", button: \"right\", text: \"Back\" }`).\n\n| **Element Type** | **Properties** | **Description**                                |\n|------------------|----------------|------------------------------------------------|\n| `string_multiline` | `x` (number), `y` (number) <br> `align` ((`\"t\"`, `\"c\"`, `\"b\"`) + (`\"l\"`, `\"m\"`, `\"r\"`)) <br> `font` (`\"primary\"`, `\"secondary\"`, `\"keyboard\"`, `\"big_numbers\"`) <br> `text` (string) | String of text that can span multiple lines.      |\n| `string`           | `x` (number), `y` (number) <br> `align` ((`\"t\"`, `\"c\"`, `\"b\"`) + (`\"l\"`, `\"m\"`, `\"r\"`)) <br> `font` (`\"primary\"`, `\"secondary\"`, `\"keyboard\"`, `\"big_numbers\"`) <br> `text` (string) | String of text.                                   |\n| `text_box`         | `x` (number), `y` (number) <br> `w` (number), `h` (number) <br> `align` ((`\"t\"`, `\"c\"`, `\"b\"`) + (`\"l\"`, `\"m\"`, `\"r\"`)) <br> `text` (string) <br> `stripToDots` (boolean)            | Box of with text that can be scrolled vertically. |\n| `text_scroll`      | `x` (number), `y` (number) <br> `w` (number), `h` (number) <br> `text` (string)                                                                                                      | Text that can be scrolled vertically.             |\n| `button`           | `text` (string) <br> `button` (`\"left\"`, `\"center\"`, `\"right\"`)                                                                                                                      | Button at the bottom of the screen.               |\n| `icon`             | `x` (number), `y` (number) <br> `iconData` ([IconData](#js_gui__icon))                                                                                                               | Display an icon.                                  |\n| `rect`             | `x` (number), `y` (number) <br> `w` (number), `h` (number) <br> `radius` (number), `fill` (boolean)                                                                                  | Draw a rectangle, optionally rounded and filled.  |\n| `circle`           | `x` (number), `y` (number) <br> `radius` (number), `fill` (boolean)                                                                                                                  | Draw a circle, optionally filled.                 |\n| `line`             | `x1` (number), `y1` (number) <br> `x2` (number), `y2` (number)                                                                                                                       | Draw a line between 2 points.                     |\n\n## Structures\n\n### ButtonEvent \n\nButton event information structure.\n\n**Fields**\n\n- key: The key that was pressed (`\"left\" | \"center\" | \"right\"`)\n- type: The type of the event (`\"press\" | \"release\" | \"short\" | \"long\" | \"repeat\"`)\n\n## View events\n\n| Item     | Type   | Description                                                                 |\n|----------|--------|-----------------------------------------------------------------------------|\n| `button` | `ButtonEvent`| Fires when the user presses on one of the three possible buttons if there's a corresponding button element. Refer to the `ButtonEvent` structure above for possible values. |\n"
  },
  {
    "path": "documentation/js/js_math.md",
    "content": "# Math module {#js_math}\n\nThe module contains mathematical methods and constants. Call the `require` function to load the module before first using its methods:\n\n```js\nlet math = require(\"math\");\n```\n\n# Constants\n\n## PI\nThe number π = 3.14159265358979323846264338327950288.\n\n## E\nThe number e (Euler's number) = 2.71828182845904523536028747135266250.\n\n## EPSILON\nThe smallest number that satisfies the condition: 1.0 + EPSILON != 1.0.\nEPSILON = 2.2204460492503131e-16.\n\n<br>\n\n---\n\n# Methods\n\n## abs()\nReturn the absolute value of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe absolute value of `x`. If `x` is negative (including -0), returns `-x`. Otherwise, returns `x`. The result is therefore always a positive number or 0.\n\n**Example**\n```js\nmath.abs(-5); // 5\n```\n\n<br>\n\n## acos()\nReturn the inverse cosine (in radians) of a number.\n\n**Parameters**\n- x: A number between -1 and 1, inclusive, representing the angle's cosine value\n\n**Returns**\n\nThe inverse cosine (angle in radians between 0 and π, inclusive) of `x`. If `x` is less than -1 or greater than 1, returns `NaN`.\n\n**Example**\n```js\nmath.acos(-1); // 3.141592653589793\n```\n\n<br>\n\n## acosh()\nReturn the inverse hyperbolic cosine of a number.\n\n**Parameters**\n- x: A number greater than or equal to 1\n\n**Returns**\n\nThe inverse hyperbolic cosine of `x`.\n\n**Example**\n```js\nmath.acosh(1); // 0\n```\n\n<br>\n\n## asin()\nReturn the inverse sine (in radians) of a number.\n\n**Parameters**\n- x: A number between -1 and 1, inclusive, representing the angle's sine value\n\n**Returns**\n\nThe inverse sine (angle in radians between -𝜋/2 and 𝜋/2, inclusive) of `x`.\n\n**Example**\n```js\nmath.asin(0.5); // 0.5235987755982989\n```\n\n<br>\n\n## asinh()\nReturn the inverse hyperbolic sine of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe inverse hyperbolic sine of `x`.\n\n**Example**\n```js\nmath.asinh(1); // 0.881373587019543\n```\n\n<br>\n\n## atan()\nReturn the inverse tangent (in radians) of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe inverse tangent (angle in radians between -𝜋/2 and 𝜋/2, inclusive) of `x`.\n\n**Example**\n```js\nmath.atan(1); // 0.7853981633974483\n```\n\n<br>\n\n## atan2()\nReturn the angle in the plane (in radians) between the positive x-axis and the ray from (0, 0) to the point (x, y), for math.atan2(y, x).\n\n**Parameters**\n- y: The y coordinate of the point\n- x: The x coordinate of the point\n\n**Returns**\n\nThe angle in radians (between -π and π, inclusive) between the positive x-axis and the ray from (0, 0) to the point (x, y).\n\n**Example**\n```js\nmath.atan2(90, 15); // 1.4056476493802699\n```\n\n<br>\n\n## atanh()\nThe method returns the inverse hyperbolic tangent of a number.\n\n**Parameters**\n- x: A number between -1 and 1, inclusive\n\n**Returns**\n\nThe inverse hyperbolic tangent of `x`.\n\n**Example**\n```js\nmath.atanh(0.5); // 0.5493061443340548\n```\n\n<br>\n\n## cbrt()\nReturn the cube root of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe cube root of `x`.\n\n**Example**\n```js\nmath.cbrt(2); // 1.2599210498948732\n```\n\n<br>\n\n## ceil()\nRound up and return the smallest integer greater than or equal to a given number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe smallest integer greater than or equal to `x`. It's the same value as `-math.floor(-x)`.\n\n**Example**\n```js\nmath.ceil(-7.004); // -7\nmath.ceil(7.004);  // 8\n```\n\n<br>\n\n## clz32()\nReturn the number of leading zero bits in the 32-bit binary representation of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe number of leading zero bits in the 32-bit binary representation of `x`.\n\n**Example**\n```js\nmath.clz32(1);    // 31\nmath.clz32(1000); // 22\n```\n\n<br>\n\n## cos()\nReturn the cosine of a number in radians.\n\n**Parameters**\n- x: A number representing an angle in radians\n\n**Returns**\n\nThe cosine of `x`, between -1 and 1, inclusive.\n\n**Example**\n```js\nmath.cos(math.PI); // -1\n```\n\n<br>\n\n## exp()\nReturn e raised to the power of a number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nA nonnegative number representing `e^x`, where `e` is the base of the natural logarithm.\n\n**Example**\n```js\nmath.exp(0); // 1\nmath.exp(1); // 2.718281828459045\n```\n\n<br>\n\n## floor()\nRound down and return the largest integer less than or equal to a given number.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe largest integer smaller than or equal to `x`. It's the same value as `-math.ceil(-x)`.\n\n**Example**\n```js\nmath.floor(-45.95); // -46\nmath.floor(-45.05); // -46\nmath.floor(-0); // -0\nmath.floor(0); // 0\nmath.floor(45.05); // 45\nmath.floor(45.95); // 45\n```\n\n<br>\n\n## log()\nReturn the natural logarithm of x.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe natural logarithm of `x`, as in `ln(x)` where `e` is the base of the natural logarithm.\n\n**Example**\n```js\nmath.log(1); // 0\nmath.log(3); // 1.0986122886681098\n```\n\n## isEqual()\nReturn true if the difference between numbers `a` and `b` is less than the specified `tolerance`.\n\n**Parameters**\n- a: A number a\n- b: A number b\n- tolerance: How much difference is allowed between the numbers to be considered equal\n\n**Returns**\n\nTrue if the difference between numbers `a` and `b` is less than the specified parameter `e`. Otherwise, false.\n\n**Example**\n```js\nmath.isEqual(1.4, 1.6, 0.2);      // false\nmath.isEqual(3.556, 3.555, 0.01); // true\n```\n\n<br>\n\n## max()\nReturn the largest of two numbers given as input parameters.\n\n**Parameters**\n- a: A number a\n- b: A number b\n\n**Returns**\n\nThe largest of the given numbers.\n\n**Example**\n```js\nmath.max(10, 20);   // 20\nmath.max(-10, -20); // -10\n```\n\n<br>\n\n## min()\nReturn the smallest of two numbers given as input parameters.\n\n**Parameters**\n- a: A number a\n- b: A number b\n\n**Returns**\n\nThe smallest of the given numbers.\n\n**Example**\n```js\nmath.min(10, 20);   // 10\nmath.min(-10, -20); // -20\n```\n\n<br>\n\n## pow()\nReturn the value of a base raised to a power.\n\n**Parameters**\n- base: The base number\n- exponent: The exponent number\n\n**Returns**\n\nA number representing base taken to the power of exponent.\n\n**Example**\n```js\nmath.pow(7, 2);  // 49\nmath.pow(7, 3);  // 343\nmath.pow(2, 10); // 1024\n```\n\n<br>\n\n## random()\nReturn a floating-point, pseudo-random number that's greater than or equal to 0 and less than 1, with approximately uniform distribution over that range — which you can then scale to your desired range.\n\n**Returns**\n\nA floating-point, pseudo-random number between 0 (inclusive) and 1 (exclusive).\n\n**Example**\n```js\nlet num = math.random();\n```\n\n<br>\n\n## sign()\nReturn 1 or -1, indicating the sign of the number passed as argument.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\n-1 if the number is less than 0, and 1 otherwise.\n\n**Example**\n```js\nmath.sign(3);  // 1\nmath.sign(0);  // 1\nmath.sign(-3); // -1\n```\n\n<br>\n\n## sin()\nReturn the sine of a number in radians.\n\n**Parameters**\n- x: A number representing an angle in radians\n\n**Returns**\n\nThe sine of `x`, between -1 and 1, inclusive.\n\n**Example**\n```js\nmath.sin(math.PI / 2); // 1\n```\n\n<br>\n\n## sqrt()\nReturn the square root of a number.\n\n**Parameters**\n- x: A number greater than or equal to 0\n\n**Returns**\n\n\nThe square root of `x`, a nonnegative number. If `x` < 0, script will fail with an error.\n\n**Example**\n```js\nmath.sqrt(25); // 5\n```\n\n<br>\n\n## trunc()\nReturn the integer part of a number by removing any fractional digits.\n\n**Parameters**\n- x: A number\n\n**Returns**\n\nThe integer part of `x`.\n\n**Example**\n```js\nmath.trunc(-1.123); // -1\nmath.trunc(0.123);  // 0\nmath.trunc(13.37);  // 13\nmath.trunc(42.84);  // 42\n```\n"
  },
  {
    "path": "documentation/js/js_notification.md",
    "content": "# Notification module {#js_notification}\n\n```js\nlet notify = require(\"notification\");\n```\n## Methods\n\n### success()\n\"Success\" flipper notification message.\n\n**Example**\n```js\nnotify.success();\n```\n\n<br>\n\n### error()\n\"Error\" flipper notification message.\n\n**Example**\n```js\nnotify.error();\n```\n\n<br>\n\n### blink()\nBlink notification LED.\n\n**Parameters**\n- Blink color (blue/red/green/yellow/cyan/magenta)\n- Blink type (short/long)\n\n**Examples**\n```js\nnotify.blink(\"red\", \"short\"); // Short blink of red LED\nnotify.blink(\"green\", \"short\"); // Long blink of green LED\n```"
  },
  {
    "path": "documentation/js/js_serial.md",
    "content": "# Serial module {#js_serial}\n\n```js\nlet serial = require(\"serial\");\n```\n# Methods\n\n## setup()\nConfigure serial port. Should be called before all other methods.\n\n**Parameters**\n\n- Serial port name (`\"usart\"`, `\"lpuart\"`)\n- Baudrate\n- Optional framing configuration object (e.g. `{ dataBits: \"8\", parity: \"even\", stopBits: \"1\" }`):\n  - `dataBits`: `\"6\"`, `\"7\"`, `\"8\"`, `\"9\"`\n    - 6 data bits can only be selected when parity is enabled (even or odd)\n    - 9 data bits can only be selected when parity is disabled (none)\n  - `parity`: `\"none\"`, `\"even\"`, `\"odd\"`\n  - `stopBits`: `\"0.5\"`, `\"1\"`, `\"1.5\"`, `\"2\"`\n    - LPUART only supports whole stop bit lengths (i.e. 1 and 2 but not 0.5 and 1.5)\n\n**Example**\n\n```js\n// Configure LPUART port with baudrate = 115200\nserial.setup(\"lpuart\", 115200);\n```\n\n<br>\n\n## write()\nWrite data to serial port.\n\n**Parameters**\n\nOne or more arguments of the following types:\n- A string\n- Single number, each number is interpreted as a byte\n- Array of numbers, each number is interpreted as a byte\n- ArrayBuffer or DataView\n\n**Example**\n\n```js\nserial.write(0x0a); // Write a single byte 0x0A\nserial.write(\"Hello, world!\"); // Write a string\nserial.write(\"Hello, world!\", [0x0d, 0x0a]); // Write a string followed by two bytes\n```\n\n<br>\n\n## read()\nRead a fixed number of characters from serial port.\n\n**Parameters**\n\n- Number of bytes to read\n- *(optional)* Timeout value in ms\n\n**Returns**\n\nA sting of received characters or undefined if nothing was received before timeout.\n\n**Example**\n\n```js\nserial.read(1); // Read a single byte, without timeout\nserial.read(10, 5000); // Read 10 bytes, with 5s timeout\n```\n\n<br>\n\n## readln()\nRead from serial port until line break character.\n\n**Parameters**\n\n- *(optional)* Timeout value in ms.\n\n**Returns**\n\nA sting of received characters or undefined if nothing was received before timeout.\n\n**Example**\n\n```js\nserial.readln(); // Read without timeout\nserial.readln(5000); // Read with 5s timeout\n```\n\n<br>\n\n## readAny()\nRead any available amount of data from serial port, can help avoid starving your loop with small reads.\n\n**Parameters**\n\n- *(optional)* Timeout value in ms\n\n**Returns**\n\nA sting of received characters or undefined if nothing was received before timeout.\n\n**Example**\n\n```js\nserial.readAny(); // Read without timeout\nserial.readAny(5000); // Read with 5s timeout\n```\n\n<br>\n\n## readBytes()\nRead from serial port until line break character.\n\n**Parameters**\n\n- Number of bytes to read\n- *(optional)* Timeout value in ms\n\n**Returns**\n\nArrayBuffer with received data or undefined if nothing was received before timeout.\n\n**Example**\n\n```js\nserial.readBytes(4); // Read 4 bytes, without timeout\n\n// Read one byte from receive buffer with zero timeout, returns UNDEFINED if Rx buffer is empty\nserial.readBytes(1, 0);\n```\n\n<br>\n\n## expect()\nSearch for a string pattern in received data stream.\n\n**Parameters**\n\n- Single argument or array of the following types:\n    - A string\n    - Array of numbers, each number is interpreted as a byte\n- *(optional)* Timeout value in ms\n\n**Returns**\n\nIndex of matched pattern in input patterns list, undefined if nothing was found.\n\n**Example**\n\n```js\n// Wait for root shell prompt with 1s timeout, returns 0 if it was received before timeout, undefined if not\nserial.expect(\"# \", 1000);\n\n// Infinitely wait for one of two strings, should return 0 if the first string got matched, 1 if the second one\nserial.expect([\": not found\", \"Usage: \"]);\n```\n\n<br>\n\n## end()\nDeinitializes serial port, allowing multiple initializations per script run.\n\n**Example**\n\n```js\nserial.end();\n// Configure LPUART port with baudrate = 115200\nserial.setup(\"lpuart\", 115200);\n```"
  },
  {
    "path": "documentation/js/js_storage.md",
    "content": "# Storage module {#js_storage}\n\nThe module allows you to access files and directories on the Flipper Zero filesystems. Call the `require` function to load the module before first using its methods:\n\n```js\nlet storage = require(\"storage\");\n```\n\n## Paths\n\nTo work with files and folders, you'll need to specify paths to them. File paths have the following structure:\n\n```\n/ext/example_subdir_1/example_subdir_2/example_file.txt\n\\____________________________________/ \\__________/ \\_/\n                dirPath                  fileName  fileExt\n```\n\n* **dirPath** — directory path starting with `/int/` (small storage in the MCU's flash memory) or `/ext/` (microSD card storage). Specify the sub-directories containing the file using `/` as a separator between directory names.\n* **fileName** — file name.\n* **fileExt** — file extension (separated from the file name by a period).\n\n---\n\n# Structures\n\n## FsInfo \n\nFilesystem information structure.\n\n**Fields**\n\n- totalSpace: Total size of the filesystem, in bytes\n- freeSpace: Free space in the filesystem, in bytes\n\n<br>\n\n## FileInfo\n\nFile information structure.\n\n**Fields**\n\n- path: Full path (e.g. \"/ext/test\", returned by `stat`) or file name (e.g. \"test\", returned by `readDirectory`)\n- isDirectory: Returns `true` if path leads to a directory (not a file)\n- size: File size in bytes, or 0 in the case of directories\n- accessTime: Time of last access as a UNIX timestamp\n\n---\n\n# Classes\n\n## File\n\nThis class implements methods for working with file. To get an object of the File class, use the `openFile` method.\n\n<br>\n\n### close()\n\nCloses the file. After this method is called, all other operations related to this file become unavailable.\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n<br>\n\n### isOpen()\n\n**Returns**\n\n`true` if file is currently opened, `false` otherwise.\n\n<br>\n\n### read()\n\nReads bytes from a file opened in read-only or read-write mode.\n\n**Parameters**\n\n- mode: The data type to interpret the bytes as: a `string` decoded from ASCII data (`\"ascii\"`), or an `ArrayBuf` (`\"binary\"`)\n- bytes: How many bytes to read from the file\n\n**Returns**\n\nAn `ArrayBuf` if the mode is `\"binary\"`, a `string` if the mode is `ascii`. The number of bytes that was actually read may be fewer than requested.\n\n<br>\n\n### write()\n\nWrites bytes to a file opened in write-only or read-write mode.\n\n**Parameters**\n\n- data: The data to write: a string that will be ASCII-encoded, or an ArrayBuf\n\n**Returns**\n\nThe amount of bytes that was actually written.\n\n<br>\n\n### seekRelative()\n\nMoves the R/W pointer forward.\n\n**Parameters**\n\n- bytes: How many bytes to move the pointer forward by\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n<br>\n\n### seekAbsolute()\n\nMoves the R/W pointer to an absolute position inside the file.\n\n**Parameters**\n\n- bytes: The position inside the file\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n<br>\n\n### tell()\n\nGets the absolute position of the R/W pointer in bytes.\n\n**Returns**\n\nThe absolute current position in the file.\n\n<br>\n\n### truncate()\n\nDiscards the data after the current position of the R/W pointer in a file opened in either write-only or read-write mode.\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n<br>\n\n### size()\n\n**Returns**\n\nThe total size of the file in bytes.\n\n<br>\n\n### eof()\n\nDetects whether the R/W pointer has reached the end of the file.\n\n**Returns**\n\n`true` if end of file reached, `false` otherwise.\n\n<br>\n\n### copyTo()\n\nCopies bytes from the R/W pointer in the current file to the R/W pointer in another file.\n\n**Parameters**\n\n- dest: The file to copy the bytes into\n- bytes: The number of bytes to copy\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n---\n\n# Methods\n\n## openFile()\n\nOpens a file.\n\n**Parameters**\n\n- path: The path to the file\n- accessMode: How to access the file (`\"r\"`, `\"w\"` or `\"rw\"`)\n- openMode: How to open the file (`\"open_existing\"`, `\"open_always\"`, `\"open_append\"`, `\"create_new\"` or `\"create_always\"`)\n\n**Returns**\n\nA `File` object on success, `undefined` otherwise.\n\n<br>\n\n## fileExists()\n\nDetects whether a file exists.\n\n**Parameters**\n\n- path: The path to the file\n\n**Returns**\n\n`true` if file exists, `false` otherwise.\n\n<br>\n\n## arePathsEqual()\n\nDetermines whether the two paths are equivalent. Respects filesystem-defined path equivalence rules.\n\n**Parameters**\n\n- path1: The first path for comparison\n- path2: The second path for comparison\n\n**Returns**\n\n`true` if path1 and path2 are equals, `false` otherwise.\n\n<br>\n\n## isSubpathOf()\n\nDetermines whether a path is a subpath of another path. Respects filesystem-defined path equivalence rules.\n\n**Parameters**\n\n- parentPath: The parent path\n- childPath: The child path\n\n**Returns**\n\n`true` if path1 and path2 are equals, `false` otherwise.\n\n<br>\n\n## fileOrDirExists()\n\nDetects whether a file or a directory exists.\n\n**Parameters**\n\n- path: The path to the file or directory\n\n**Returns**\n\n`true` if file/directory exists, `false` otherwise.\n\n**Example**\n```js\nif (storage.fileOrDirExists(\"/ext/test_dir\")) {\n    print(\"Test directory exists\");\n}\n```\n\n<br>\n\n## fsInfo()\n\nFetches generic information about a filesystem.\n\n**Parameters**\n\n- filesystem: The path to the filesystem (e.g. `\"/ext\"` or `\"/int\"`)\n\n**Returns**\n\nA `fsInfo` structure or `undefined` on failure.\n\n**Example**\n```js\nlet fsinfo = storage.fsInfo(\"/ext\");\nif (fsinfo === undefined) {\n    print(\"Filesystem access error\");\n} else {\n    print(\"Free space on the /ext filesystem:\", fsinfo.freeSpace);\n}\n```\n\n<br>\n\n## stat()\n\nAcquires metadata about a file or directory.\n\n**Parameters**\n\n- path: The path to the file or directory\n\n**Returns**\n\nA `FileInfo` structure or `undefined` on failure.\n\n**Example**\n```js\nlet finfo = storage.stat(\"/ext/test_file.txt\");\nif (finfo === undefined) {\n    print(\"File not found\");\n} else {\n    print(\"File size:\", finfo.size);\n}\n```\n\n<br>\n\n## directoryExists()\n\nDetects whether a directory exists.\n\n**Parameters**\n\n- path: The path to the directory\n\n**Returns**\n\n`true` if directory exists, `false` otherwise.\n\n<br>\n\n## makeDirectory()\n\nCreates an empty directory.\n\n**Parameters**\n\n- path: The path to the new directory\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n<br>\n\n## readDirectory()\n\nReads the list of files in a directory.\n\n**Parameters**\n\n- path: The path to the directory\n\n**Returns**\n\nArray of `FileInfo` structures with directory entries, or `undefined` on failure.\n\n<br>\n\n## nextAvailableFilename()\n\nChooses the next available filename with a numeric suffix in a directory.\n```\n/ext/example_dir/example_file123.txt\n\\______________/ \\__________/\\_/ \\_/\n    dirPath        fileName   |   |\n                              |   +--- fileExt\n                              +------- suffix selected by this function\n```\n\n**Parameters**\n\n- dirPath: The directory to look in\n- fileName: The base of the filename (the part before the numeric suffix)\n- fileExt: The extension of the filename (the part after the numeric suffix)\n- maxLen: The maximum length of the filename with the numeric suffix\n\n**Returns**\n\nThe base of the filename with the next available numeric suffix, without the extension or the base directory.\n\n<br>\n\n## copy()\n\nCopies a file or recursively copies a possibly non-empty directory.\n\n**Parameters**\n\n- oldPath: The original path to the file or directory\n- newPath: The new path that the copy of the file or directory will be accessible under\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n**Example**\n```js\nif (storage.copy(\"/ext/src_file.txt\", \"/ext/dst_file.txt\")) {\n    print(\"File copied\");\n}\n```\n\n<br>\n\n## rename()\n\nRenames or moves a file or directory.\n\n**Parameters**\n\n- oldPath: The old path to the file or directory\n- newPath: The new path that the file or directory will become accessible\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n**Example**\n```js\nif (storage.rename(\"/ext/src_file.txt\", \"/ext/dst_file.txt\")) {\n    print(\"File moved\");\n}\n```\n\n<br>\n\n## remove()\n\nRemoves a file or an empty directory.\n\n**Parameters**\n\n- path: The path to the file or directory\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n**Example**\n```js\nlet path = \"/ext/test_file.txt\";\n\nfile = storage.openFile(path, \"w\", \"create_always\");\nfile.write(\"Test\");\nfile.close();\nprint(\"File created\");\n\nif (storage.remove(path)) {\n    print(\"File removed\");\n}\n```\n\n<br>\n\n## rmrf()\n\nRemoves a file or recursively removes a possibly non-empty directory.\n\n**Parameters**\n\n- path: The path to the file or directory\n\n**Returns**\n\n`true` on success, `false` on failure.\n\n**Example**\n```js\nlet path = \"/ext/test_dir\";\n\nif (storage.rmrf(path)) {\n    print(\"Directory removed\");\n}\n```\n"
  },
  {
    "path": "documentation/js/js_using_js_modules.md",
    "content": "# Using JavaScript modules {#js_using_js_modules}\n\nIn the previous guides, we learned how to write a basic JavaScript app using [built-in functions](#js_builtin). However, the set of built-in functions is limited, so when developing your JS apps, you'll likely want to use external JS modules. These modules offer a wide range of functions (methods) for various tasks.\n\nFor example:\n* The `serial` module enables transmitting and receiving data via a serial interface\n* The `badusb` module enables USB keyboard emulation and sending key press events via USB\n* The `math` module provides mathematical functions\n\nJS modules are written in C/C++, making them fast and efficient. They come with Flipper Zero firmware and are stored on the microSD card in compiled form as **FAL (Flipper Application File)** files.\n\n> [!note]\n> You can find the implementation of all supported JS modules in the [Flipper Zero firmware repository](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/system/js_app/modules). Also, check out the [docs for JS modules](#js_modules) for more details.\n\n## How to use JS modules in your app\n\nBefore using any of the JS module methods, you **must** import the module using the `require()` function. This loads the module into RAM, allowing you to access its methods.\n\nTo save RAM and improve performance, avoid loading modules you don't plan to use. Also, all loaded modules will be automatically unloaded from RAM after the app execution ends.\n\nTo load a module, call the `require()` function with the module name in quotes. For example, to load the `notification` module, write this:\n\n\\code{.js}\nlet notify = require(\"notification\");\n\\endcode\n\nNow you can call methods of the `notification` module using the `notify` variable to access them:\n\n\\code{.js}\nlet notify = require(\"notification\");\n\nnotify.success();\nprint(\"success notification\");\n\\endcode\n\n## What's next?\n\nCongratulations, you've completed the **Getting Started** section of our JS docs. You've learned how to run and debug JS apps, and how to use JS modules. Now, we invite you to check out the [main JavaScript page](#js) where you'll find:\n\n* JavaScript app examples\n* Documentation on JS modules\n* Additional resources related to JavaScript on Flipper Zero\n"
  },
  {
    "path": "documentation/js/js_your_first_js_app.md",
    "content": "# Your first JavaScript app {#js_your_first_js_app}\n\nIn this guide, we'll create a simple script that outputs ordinal numbers with a delay and learn how to run it on Flipper Zero in different ways. All you need is your Flipper Zero, a PC, and a USB cable.\n\n## Step 1. Create the script file\n\nCreate a new text file `first_app.js`. Paste the code below into it and save the file:\n\n\\code{.js}\nprint(\"start\");\ndelay(1000);\nprint(\"1\");\ndelay(500);\nprint(\"2\");\ndelay(500);\nprint(\"3\");\ndelay(500);\nprint(\"end\");\n\\endcode\n\nWhat the code does:\n* Outputs the text **start**, waits 1 second\n* Outputs the numbers **1**, **2** and **3**, with a 0.5-second pause after each number\n* Outputs the text **end**\n\nThe `print()` function is used to output text. The string to be output is in the brackets. This is a built-in function, so you don't need to include additional JS modules. You can use this function anywhere in your application.\n\nAnother built-in function, `delay()`, implements delay. The delay time in milliseconds is given in the brackets. Since 1000 milliseconds equals 1 second, a 1-second delay is written as 1000, and a 0.5-second delay as 500.\n\n> [!note]\n> Find the list of built-in functions in [Built-in functions](#js_builtin).\n\n## Step 2. Copy the file to Flipper Zero\n\nTo copy the JavaScript file to Flipper Zero, follow these steps:\n1. Connect your Flipper Zero to your PC via USB.\n2. Open the **qFlipper** application.\n3. Go to the **File manager** tab and open the path `SD Card/apps/Scripts/`.\n4. Drag and drop the file into the qFlipper window.\n\n> [!note]\n> To learn more about qFlipper, visit the [dedicated section in our user documentation](https://docs.flipper.net/qflipper).\n\nYour script is now ready to run on Flipper Zero.\n\n## Step 3. Run your script\n\nYou can launch your app in two ways:\n\n* From the Flipper Zero menu\n* Remotely from your PC using the CLI (command-line interface)\n\nLet's explore them both.\n\n### How to run a script from Flipper Zero's menu\n\n1. Go to **Apps → Scripts** in your Flipper Zero's menu. Here, you'll see a list of scripts located in the `SD Card/apps/Scripts/` folder.\n2. Select the script you want to run.\n3. Press the **OK** button to run the script.\n\n\\image html js_first_app_on_fz.jpg width=500\n\nThe Flipper Zero screen will display the strings with the specified delay, as defined by the `print()` and `delay()` functions.\n\n### How to run script using CLI\n\nThe command-line interface (CLI) is a text-based interface that lets you control your Flipper Zero from your computer, including running scripts. Running JavaScript apps via CLI is useful for debugging, as it lets you write and test code remotely, without switching between your PC and the device.\n\nTo run the script via CLI:\n\n1. Connect your Flipper Zero to your PC via USB.\n2. Access the CLI using one of the [recommended methods](https://docs.flipper.net/development/cli#HfXTy).\n3. Enter the `js path` command, replacing `path` with the path to the script file on your Flipper Zero:\n\n\\code{.sh}\njs /ext/apps/Scripts/first_app.js\n\\endcode\n\n\\image html js_first_app_on_cli.jpg width=700\n\nAs you can see, unlike running JavaScript apps from the Flipper Zero UI, all output from the `print()` function is sent to the CLI, not the device screen.\n\n**Next step:** [Developing apps using JavaScript SDK](#js_developing_apps_using_js_sdk)\n"
  },
  {
    "path": "fbt",
    "content": "#!/bin/sh\n\n# shellcheck disable=SC2086 source=/dev/null\n# unofficial strict mode\nset -eu;\n\n# private variables\nN_CORES=\"$(getconf _NPROCESSORS_ONLN)\";\nN_GIT_THREADS=\"$(($N_CORES * 2))\";\nSCRIPT_PATH=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\";\nSCONS_DEFAULT_FLAGS=\"--warn=target-not-built\";\nSCONS_EP=\"python3 -m SCons\";\n\n# public variables\nFBT_NO_SYNC=\"${FBT_NO_SYNC:-\"\"}\";\nFBT_TOOLCHAIN_PATH=\"${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}\";\nFBT_VERBOSE=\"${FBT_VERBOSE:-\"\"}\";\nFBT_GIT_SUBMODULE_SHALLOW=\"${FBT_GIT_SUBMODULE_SHALLOW:-\"\"}\";\n\nFBT_VERBOSE=\"$FBT_VERBOSE\" . \"$SCRIPT_PATH/scripts/toolchain/fbtenv.sh\";\n\nif [ -z \"$FBT_VERBOSE\" ]; then\n    SCONS_DEFAULT_FLAGS=\"$SCONS_DEFAULT_FLAGS -Q\";\nfi\n\nif [ -z \"$FBT_NO_SYNC\" ]; then\n    if [ ! -e \"$SCRIPT_PATH/.git\" ]; then\n        echo \"\\\".git\\\" directory not found, please clone repo via \\\"git clone\\\"\";\n        exit 1;\n    fi\n    _FBT_CLONE_FLAGS=\"--jobs $N_GIT_THREADS\";\n    if [ ! -z \"$FBT_GIT_SUBMODULE_SHALLOW\" ]; then\n        _FBT_CLONE_FLAGS=\"$_FBT_CLONE_FLAGS --depth 1\";\n    fi\n\n    git submodule update --init --recursive $_FBT_CLONE_FLAGS;\nfi\n\n$SCONS_EP $SCONS_DEFAULT_FLAGS \"$@\"\n"
  },
  {
    "path": "fbt.cmd",
    "content": "@echo off\r\ncall \"%~dp0scripts\\toolchain\\fbtenv.cmd\" env || exit /b\r\n\r\nset SCONS_EP=python -m SCons\r\n\r\nif [%FBT_NO_SYNC%] == [] (\r\n    set _FBT_CLONE_FLAGS=--jobs %NUMBER_OF_PROCESSORS%\r\n    if not [%FBT_GIT_SUBMODULE_SHALLOW%] == [] (\r\n        set _FBT_CLONE_FLAGS=%_FBT_CLONE_FLAGS% --depth 1\r\n    )\r\n    if exist \".git\" (\r\n        git submodule update --init --recursive %_FBT_CLONE_FLAGS%\r\n        if %ERRORLEVEL% neq 0 (\r\n            echo Failed to update submodules, set FBT_NO_SYNC to skip\r\n            exit /b 1\r\n        )\r\n    ) else (\r\n        echo .git not found, please clone repo with \"git clone\"\r\n        exit /b 1\r\n    )\r\n)\r\n\r\nset \"SCONS_DEFAULT_FLAGS=--warn=target-not-built\"\r\n\r\nif not defined FBT_VERBOSE (\r\n    set \"SCONS_DEFAULT_FLAGS=%SCONS_DEFAULT_FLAGS% -Q\"\r\n)\r\n\r\n%SCONS_EP% %SCONS_DEFAULT_FLAGS% %*\r\n"
  },
  {
    "path": "fbt_options.py",
    "content": "from pathlib import Path\nimport posixpath\n\n# For more details on these options, run 'fbt -h'\n\nFIRMWARE_ORIGIN = \"Official\"\n\n# Default hardware target\nTARGET_HW = 7\n\n# Optimization flags\n## Optimize for size\nCOMPACT = 0\n## Optimize for debugging experience\nDEBUG = 1\n\n# Suffix to add to files when building distribution\n# If OS environment has DIST_SUFFIX set, it will be used instead\nDIST_SUFFIX = \"local\"\n\n# Coprocessor firmware\nCOPRO_OB_DATA = \"scripts/ob.data\"\n\n# Must match lib/stm32wb_copro version\nCOPRO_CUBE_VERSION = \"1.20.0\"\n\nCOPRO_CUBE_DIR = \"lib/stm32wb_copro\"\n\n# Default radio stack\nCOPRO_STACK_BIN = \"stm32wb5x_BLE_Stack_light_fw.bin\"\n# Firmware also supports \"ble_full\", but it might not fit into debug builds\nCOPRO_STACK_TYPE = \"ble_light\"\n\n# Leave 0 to let scripts automatically calculate it\nCOPRO_STACK_ADDR = \"0x0\"\n\n# If you override COPRO_CUBE_DIR on commandline, override this as well\nCOPRO_STACK_BIN_DIR = posixpath.join(COPRO_CUBE_DIR, \"firmware\")\n\n# Supported toolchain versions\n# Also specify in scripts/ufbt/SConstruct\nFBT_TOOLCHAIN_VERSIONS = (\" 12.3.\", \" 13.2.\")\n\nOPENOCD_OPTS = [\n    \"-f\",\n    \"interface/stlink.cfg\",\n    \"-c\",\n    \"transport select hla_swd\",\n    \"-f\",\n    \"${FBT_DEBUG_DIR}/stm32wbx.cfg\",\n    \"-c\",\n    \"stm32wbx.cpu configure -rtos auto\",\n]\n\nSVD_FILE = \"${FBT_DEBUG_DIR}/STM32WB55_CM4.svd\"\n\n# Look for blackmagic probe on serial ports and local network\nBLACKMAGIC = \"auto\"\n\n# Application to start on boot\nLOADER_AUTOSTART = \"\"\n\nFIRMWARE_APPS = {\n    \"default\": [\n        # Svc\n        \"basic_services\",\n        # Apps\n        \"main_apps\",\n        \"system_apps\",\n        # Settings\n        \"settings_apps\",\n    ],\n    \"unit_tests\": [\n        \"basic_services\",\n        \"updater_app\",\n        \"radio_device_cc1101_ext\",\n        \"unit_tests\",\n        \"js_app\",\n        \"infrared\",\n        \"archive\",\n    ],\n}\n\nFIRMWARE_APP_SET = \"default\"\n\ncustom_options_fn = \"fbt_options_local.py\"\n\nif Path(custom_options_fn).exists():\n    exec(compile(Path(custom_options_fn).read_text(), custom_options_fn, \"exec\"))\n"
  },
  {
    "path": "firmware.scons",
    "content": "import itertools\n\nfrom fbt.sdk.cache import LazySdkVersionLoader\nfrom fbt.version import get_git_commit_unix_timestamp\nfrom fbt_extra.util import link_elf_dir_as_latest, should_gen_cdb_and_link_dir\nfrom SCons.Errors import UserError\nfrom SCons.Node import FS\n\nImport(\"ENV\", \"fw_build_meta\")\n\n# Building initial C environment for libs\nenv = ENV.Clone(\n    tools=[\n        (\"compilation_db\", {\"COMPILATIONDB_COMSTR\": \"\\tCDB\\t${TARGET}\"}),\n        \"fwbin\",\n        \"fbt_apps\",\n        \"pvsstudio\",\n        \"fbt_hwtarget\",\n        \"fbt_envhooks\",\n        \"fbt_resources\",\n    ],\n    COMPILATIONDB_USE_ABSPATH=False,\n    COMPILATIONDB_USE_BINARY_ABSPATH=True,\n    BUILD_DIR=fw_build_meta[\"build_dir\"],\n    IS_BASE_FIRMWARE=fw_build_meta[\"type\"] == \"firmware\",\n    FW_FLAVOR=fw_build_meta[\"flavor\"],\n    LIB_DIST_DIR=fw_build_meta[\"build_dir\"].Dir(\"lib\"),\n    RESOURCES_ROOT=fw_build_meta[\"build_dir\"].Dir(\"resources\"),\n    TARGETS_ROOT=Dir(\"#/targets\"),\n    LINT_SOURCES=[\n        Dir(\"applications\"),\n        # Not C code\n        Dir(\"!applications/system/js_app/packages\"),\n    ],\n    LIBPATH=[\n        \"${LIB_DIST_DIR}\",\n    ],\n    CPPPATH=[\n        \"#/furi\",\n        *(f\"#/{app_dir[0]}\" for app_dir in ENV[\"APPDIRS\"] if app_dir[1]),\n        \"#/targets/furi_hal_include\",\n    ],\n    # Specific flags for building libraries - always do optimized builds\n    FW_LIB_OPTS={\n        \"Default\": {\n            \"CCFLAGS\": [\n                \"-Og\" if ENV[\"LIB_DEBUG\"] else \"-Os\",\n            ],\n            \"CPPDEFINES\": [\n                \"NDEBUG\",\n                \"FURI_DEBUG\" if ENV[\"LIB_DEBUG\"] else \"FURI_NDEBUG\",\n            ],\n            # You can add other entries named after libraries\n            # If they are present, they have precedence over Default\n        },\n        # for furi_check to respect build type\n        \"furi\": {\n            \"CCFLAGS\": [\n                \"-Os\",\n            ],\n            \"CPPDEFINES\": [\n                \"NDEBUG\",\n                \"FURI_DEBUG\" if ENV[\"DEBUG\"] else \"FURI_NDEBUG\",\n            ],\n        },\n        \"flipper_application\": {\n            \"CCFLAGS\": [\n                \"-Og\",\n            ],\n            \"CPPDEFINES\": [\n                \"NDEBUG\",\n                \"FURI_DEBUG\" if ENV[\"DEBUG\"] else \"FURI_NDEBUG\",\n            ],\n        },\n    },\n    FW_API_TABLE=None,\n    _APP_ICONS=None,\n    APPS=_.split(\",\") if (_ := GetOption(\"extra_int_apps\")) else [],\n    EXTRA_EXT_APPS=_.split(\",\") if (_ := GetOption(\"extra_ext_apps\")) else [],\n)\n\nenv.PreConfigureFwEnvionment()\n\nif env[\"IS_BASE_FIRMWARE\"]:\n    env.Append(\n        FIRMWARE_BUILD_CFG=\"firmware\",\n        RAM_EXEC=False,\n    )\nelse:\n    env.Append(\n        FIRMWARE_BUILD_CFG=\"updater\",\n        RAM_EXEC=True,\n        CPPDEFINES=[\n            \"FURI_RAM_EXEC\",\n        ],\n    )\nenv.AppendUnique(CPPDEFINES=[\"FW_CFG_${FIRMWARE_APP_SET}\"])\n\nenv.ConfigureForTarget(env.subst(\"${TARGET_HW}\"))\n\nExport(\"env\")\n\n# Invoke child SCopscripts to populate global `env` + build their own part of the code\nlib_targets = env.BuildModules(\n    [\n        \"lib\",\n        \"assets\",\n        \"targets\",\n        \"furi\",\n    ],\n)\n\n# Configure firmware origin definitions\nenv.Append(\n    CPPDEFINES=[\n        env.subst(\"FW_ORIGIN_${FIRMWARE_ORIGIN}\"),\n    ]\n)\n\n\n# Now, env is fully set up with everything to build apps\nfwenv = env.Clone(FW_ARTIFACTS=[])\n\nfw_artifacts = fwenv[\"FW_ARTIFACTS\"]\n\n# Set up additional app-specific build flags\nSConscript(\"site_scons/firmwareopts.scons\", exports={\"ENV\": fwenv})\n\n# Set up app configuration\nif env[\"IS_BASE_FIRMWARE\"]:\n    fwenv.Append(APPS=fwenv[\"FIRMWARE_APPS\"].get(fwenv.subst(\"$FIRMWARE_APP_SET\")))\nelse:\n    fwenv.Append(APPS=[\"updater\"])\n\n\nfor app_dir, _ in fwenv[\"APPDIRS\"]:\n    app_dir_node = env.Dir(\"#\").Dir(app_dir)\n\n    for entry in app_dir_node.glob(\"*\"):\n        if isinstance(entry, FS.Dir) and not str(entry).startswith(\".\"):\n            fwenv.LoadAppManifest(entry)\n\nfwenv.PrepareApplicationsBuild()\n\n\n# Build external apps + configure SDK\nif env[\"IS_BASE_FIRMWARE\"]:\n    # Ensure all libs are built - even if they are not used in firmware\n    fw_artifacts.append(fwenv[\"LIB_DIST_DIR\"].glob(\"*.a\"))\n\n    fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT=fwenv[\"BUILD_DIR\"].Dir(\".extapps\"))\n    fw_extapps = fwenv[\"FW_EXTAPPS\"] = SConscript(\n        \"site_scons/extapps.scons\",\n        exports={\"ENV\": fwenv},\n    )\n    fw_artifacts.append(fw_extapps.sdk_tree)\n\n    # Resources & manifest for SD card\n    manifest = fwenv.ManifestBuilder(\n        \"${RESOURCES_ROOT}/Manifest\",\n        GIT_UNIX_TIMESTAMP=get_git_commit_unix_timestamp(),\n        _EXTRA_DIST=[fwenv[\"DOLPHIN_EXTERNAL_OUT_DIR\"]],\n    )\n    fwenv.Replace(FW_RESOURCES_MANIFEST=manifest)\n    fwenv.Alias(\"resources\", manifest)\n\n    # API getter\n    fwenv.Append(FBT_API_VERSION=LazySdkVersionLoader(fwenv.subst(\"$SDK_DEFINITION\")))\n    fwenv.PhonyTarget(\n        \"get_apiversion\",\n        \"@echo $( ${FBT_API_VERSION} $)\",\n    )\n\n# Add preprocessor definitions for current set of apps\nfwenv.Append(\n    CPPDEFINES=fwenv[\"APPBUILD\"].get_apps_cdefs(),\n)\n\n# Build applications.c for selected services & apps\n# Depends on virtual value-only node, so it only gets rebuilt when set of apps changes\napps_c = fwenv.ApplicationsC(\n    \"applications/applications.c\",\n    [Value(fwenv[\"APPS\"]), Value(fwenv[\"LOADER_AUTOSTART\"])],\n)\n\n# Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed\nfor app_dir, _ in env[\"APPDIRS\"]:\n    app_dir_node = env.Dir(\"#\").Dir(app_dir)\n    fwenv.Depends(apps_c, app_dir_node.glob(\"*/application.fam\"))\n\n# Sanity check - certain external apps are using features that are not available in base firmware\nif advanced_faps := list(\n    filter(\n        lambda app: app.fap_extbuild or app.fap_private_libs or app.fap_icon_assets,\n        fwenv[\"APPBUILD\"].get_builtin_apps(),\n    )\n):\n    raise UserError(\n        \"An Application that is using fap-specific features cannot be built into base firmware.\"\n        f\" Offending app(s): {', '.join(app.appid for app in advanced_faps)}\"\n    )\n\nsources = [apps_c]\n# Gather sources only from app folders in current configuration\nsources.extend(\n    itertools.chain.from_iterable(\n        fwenv.GatherSources([source_type, \"!lib\"], appdir.relpath)\n        for appdir, source_type in fwenv[\"APPBUILD\"].get_builtin_app_folders()\n    )\n)\n\n# Debug\n# print(fwenv.Dump())\n\n# Full firmware definition\nfwelf = fwenv[\"FW_ELF\"] = fwenv.Program(\n    \"${FIRMWARE_BUILD_CFG}\",\n    sources,\n    LIBS=fwenv[\"TARGET_CFG\"].linker_dependencies,\n)\nDepends(fwelf, fwenv[\"LINKER_SCRIPT_PATH\"])\n\n# Firmware depends on everything child builders returned\n# Depends(fwelf, lib_targets)\n# Output extra details after building firmware\nAddPostAction(fwelf, fwenv[\"APPBUILD_DUMP\"])\nAddPostAction(\n    fwelf,\n    Action(\n        [[\"${PYTHON3}\", \"${BIN_SIZE_SCRIPT}\", \"elf\", \"${TARGET}\"]],\n        \"Firmware size\",\n    ),\n)\n\n# Produce extra firmware files\nfwhex = fwenv[\"FW_HEX\"] = fwenv.HEXBuilder(\"${FIRMWARE_BUILD_CFG}\")\nfwbin = fwenv[\"FW_BIN\"] = fwenv.BINBuilder(\"${FIRMWARE_BUILD_CFG}\")\nAddPostAction(\n    fwbin,\n    Action([[\"@${PYTHON3}\", \"${BIN_SIZE_SCRIPT}\", \"bin\", \"${TARGET}\"]]),\n)\n\nfwdfu = fwenv[\"FW_DFU\"] = fwenv.DFUBuilder(\"${FIRMWARE_BUILD_CFG}\")\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_dfu\", fwdfu)\n\nfwdump = fwenv.ObjDump(\"${FIRMWARE_BUILD_CFG}\")\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_list\", fwdump)\n\n\nfw_artifacts.extend(\n    [\n        fwhex,\n        fwbin,\n        fwdfu,\n        fwenv[\"FW_VERSION_JSON\"],\n    ]\n)\n\n\nfwcdb = fwenv[\"FW_CDB\"] = fwenv.CompilationDatabase()\n# without filtering, both updater & firmware commands would be generated in same file\nfwenv.Replace(\n    COMPILATIONDB_PATH_FILTER=fwenv.subst(\"*${FW_FLAVOR}*\"),\n    COMPILATIONDB_SRCPATH_FILTER=\"*.c*\",\n)\nAlwaysBuild(fwcdb)\nPrecious(fwcdb)\nNoClean(fwcdb)\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_cdb\", fwcdb)\n\npvscheck = fwenv.PVSCheck(\"pvsreport.log\", fwcdb)\nDepends(\n    pvscheck,\n    [\n        fwenv[\"FW_VERSION_JSON\"],\n        fwenv[\"FW_ASSETS_HEADERS\"],\n        fwenv[\"FW_API_TABLE\"],\n        fwenv[\"_APP_ICONS\"],\n    ],\n)\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_pvscheck\", pvscheck)\nAlwaysBuild(pvscheck)\nPrecious(pvscheck)\n\npvsreport = fwenv.PVSReport(None, pvscheck, REPORT_DIR=Dir(\"pvsreport\"))\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_pvs\", pvsreport)\nAlwaysBuild(pvsreport)\n\n# If current configuration was explicitly requested, generate compilation database\n# and link its directory as build/latest\nif should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS):\n    fw_artifacts.append(fwcdb)\n\n    # Adding as a phony target, so folder link is updated even if elf didn't change\n    link_dir_command = fwenv.PhonyTarget(\n        fwenv.subst(\"${FIRMWARE_BUILD_CFG}_latest\"),\n        Action(\n            lambda source, target, env: link_elf_dir_as_latest(env, source[0]),\n            None,\n        ),\n        source=fwelf,\n    )\n    fw_artifacts.append(link_dir_command)\n\nAlias(fwenv[\"FIRMWARE_BUILD_CFG\"] + \"_all\", fw_artifacts)\n\nenv.PostConfigureFwEnvionment()\n\nReturn(\"fwenv\")\n"
  },
  {
    "path": "furi/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ]\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"furi\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "furi/core/base.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <furi_config.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    FuriWaitForever = 0xFFFFFFFFU,\n} FuriWait;\n\ntypedef enum {\n    FuriFlagWaitAny = 0x00000000U, ///< Wait for any flag (default).\n    FuriFlagWaitAll = 0x00000001U, ///< Wait for all flags.\n    FuriFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.\n\n    FuriFlagError = 0x80000000U, ///< Error indicator.\n    FuriFlagErrorUnknown = 0xFFFFFFFFU, ///< FuriStatusError (-1).\n    FuriFlagErrorTimeout = 0xFFFFFFFEU, ///< FuriStatusErrorTimeout (-2).\n    FuriFlagErrorResource = 0xFFFFFFFDU, ///< FuriStatusErrorResource (-3).\n    FuriFlagErrorParameter = 0xFFFFFFFCU, ///< FuriStatusErrorParameter (-4).\n    FuriFlagErrorISR = 0xFFFFFFFAU, ///< FuriStatusErrorISR (-6).\n} FuriFlag;\n\ntypedef enum {\n    FuriStatusOk = 0, ///< Operation completed successfully.\n    FuriStatusError =\n        -1, ///< Unspecified RTOS error: run-time error but no other error message fits.\n    FuriStatusErrorTimeout = -2, ///< Operation not completed within the timeout period.\n    FuriStatusErrorResource = -3, ///< Resource not available.\n    FuriStatusErrorParameter = -4, ///< Parameter error.\n    FuriStatusErrorNoMemory =\n        -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.\n    FuriStatusErrorISR =\n        -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.\n    FuriStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.\n} FuriStatus;\n\ntypedef enum {\n    FuriSignalExit, /**< Request (graceful) exit. */\n    // Other standard signals may be added in the future\n    FuriSignalCustom = 100, /**< Custom signal values start from here. */\n} FuriSignal;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/check.c",
    "content": "#include \"check.h\"\n#include \"common_defines.h\"\n\n#include <stm32wbxx.h>\n#include <furi_hal_power.h>\n#include <furi_hal_rtc.h>\n#include <furi_hal_debug.h>\n#include <furi_hal_bt.h>\n#include <furi_hal_interrupt.h>\n#include <stdio.h>\n\n#include <FreeRTOS.h>\n#include <task.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nPLACE_IN_SECTION(\"MB_MEM2\") const char* __furi_check_message = NULL;\nPLACE_IN_SECTION(\"MB_MEM2\") uint32_t __furi_check_registers[13] = {0};\n\n/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */\n#define GET_MESSAGE_AND_STORE_REGISTERS()               \\\n    asm volatile(\"ldr r11, =__furi_check_message    \\n\" \\\n                 \"str r12, [r11]                    \\n\" \\\n                 \"ldr r12, =__furi_check_registers  \\n\" \\\n                 \"stm r12, {r0-r11}                 \\n\" \\\n                 \"str lr, [r12, #48]                \\n\" \\\n                 :                                      \\\n                 :                                      \\\n                 : \"memory\");\n\n/** Restore registers and halt MCU\n * \n * - Always use it with GET_MESSAGE_AND_STORE_REGISTERS\n * - If debugger is(was) connected this routine will raise bkpt\n * - If debugger is not connected then endless loop\n * \n */\n#define RESTORE_REGISTERS_AND_HALT_MCU(debug)           \\\n    register bool r0 asm(\"r0\") = debug;                 \\\n    asm volatile(\"cbnz  r0, with_debugger%=         \\n\" \\\n                 \"ldr   r12, =__furi_check_registers\\n\" \\\n                 \"ldm   r12, {r0-r11}               \\n\" \\\n                 \"loop%=:                           \\n\" \\\n                 \"wfi                               \\n\" \\\n                 \"b     loop%=                      \\n\" \\\n                 \"with_debugger%=:                  \\n\" \\\n                 \"ldr   r12, =__furi_check_registers\\n\" \\\n                 \"ldm   r12, {r0-r11}               \\n\" \\\n                 \"debug_loop%=:                     \\n\" \\\n                 \"bkpt  0x00                        \\n\" \\\n                 \"wfi                               \\n\" \\\n                 \"b     debug_loop%=                \\n\" \\\n                 :                                      \\\n                 : \"r\"(r0)                              \\\n                 : \"memory\");\n\nextern size_t xPortGetTotalHeapSize(void);\n\nstatic void __furi_put_uint32_as_text(uint32_t data) {\n    char tmp_str[] = \"-2147483648\";\n    itoa(data, tmp_str, 10);\n    furi_log_puts(tmp_str);\n}\n\nstatic void __furi_put_uint32_as_hex(uint32_t data) {\n    char tmp_str[] = \"0xFFFFFFFF\";\n    itoa(data, tmp_str, 16);\n    furi_log_puts(tmp_str);\n}\n\nstatic void __furi_print_register_info(void) {\n    // Print registers\n    for(uint8_t i = 0; i < 12; i++) {\n        furi_log_puts(\"\\r\\n\\tr\");\n        __furi_put_uint32_as_text(i);\n        furi_log_puts(\" : \");\n        __furi_put_uint32_as_hex(__furi_check_registers[i]);\n    }\n\n    furi_log_puts(\"\\r\\n\\tlr : \");\n    __furi_put_uint32_as_hex(__furi_check_registers[12]);\n}\n\nstatic void __furi_print_stack_info(void) {\n    furi_log_puts(\"\\r\\n\\tstack watermark: \");\n    __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);\n}\n\nstatic void __furi_print_bt_stack_info(void) {\n    const BleGlueHardfaultInfo* fault_info = ble_glue_get_hardfault_info();\n    if(fault_info == NULL) {\n        furi_log_puts(\"\\r\\n\\tcore2: not faulted\");\n    } else {\n        furi_log_puts(\"\\r\\n\\tcore2: hardfaulted.\\r\\n\\tPC: \");\n        __furi_put_uint32_as_hex(fault_info->source_pc);\n        furi_log_puts(\"\\r\\n\\tLR: \");\n        __furi_put_uint32_as_hex(fault_info->source_lr);\n        furi_log_puts(\"\\r\\n\\tSP: \");\n        __furi_put_uint32_as_hex(fault_info->source_sp);\n    }\n}\n\nstatic void __furi_print_heap_info(void) {\n    furi_log_puts(\"\\r\\n\\t     heap total: \");\n    __furi_put_uint32_as_text(configTOTAL_HEAP_SIZE);\n    furi_log_puts(\"\\r\\n\\t      heap free: \");\n    __furi_put_uint32_as_text(xPortGetFreeHeapSize());\n    HeapStats_t heap_stats;\n    vPortGetHeapStats(&heap_stats);\n    furi_log_puts(\"\\r\\n\\t heap watermark: \");\n    __furi_put_uint32_as_text(heap_stats.xMinimumEverFreeBytesRemaining);\n}\n\nstatic void __furi_print_name(bool isr) {\n    if(isr) {\n        uint8_t exception_number = __get_IPSR();\n        const char* name = furi_hal_interrupt_get_name(exception_number);\n        furi_log_puts(\"[ISR \");\n        if(name) {\n            furi_log_puts(name);\n        } else {\n            __furi_put_uint32_as_text(__get_IPSR());\n        }\n        furi_log_puts(\"] \");\n    } else {\n        const char* name = pcTaskGetName(NULL);\n        if(name == NULL) {\n            furi_log_puts(\"[main] \");\n        } else {\n            furi_log_puts(\"[\");\n            furi_log_puts(name);\n            furi_log_puts(\"] \");\n        }\n    }\n}\n\nFURI_NORETURN void __furi_crash_implementation(void) {\n    __disable_irq();\n    GET_MESSAGE_AND_STORE_REGISTERS();\n\n    bool isr = FURI_IS_IRQ_MODE();\n\n    if(__furi_check_message == NULL) {\n        __furi_check_message = \"Fatal Error\";\n    } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) {\n        __furi_check_message = \"furi_assert failed\";\n    } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) {\n        __furi_check_message = \"furi_check failed\";\n    }\n\n    furi_log_puts(\"\\r\\n\\033[0;31m[CRASH]\");\n    __furi_print_name(isr);\n    furi_log_puts(__furi_check_message);\n\n    __furi_print_register_info();\n    if(!isr) {\n        __furi_print_stack_info();\n    }\n    __furi_print_heap_info();\n    __furi_print_bt_stack_info();\n\n    // Check if debug enabled by DAP\n    // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en\n    bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;\n#ifdef FURI_NDEBUG\n    if(debug) {\n#endif\n        furi_log_puts(\"\\r\\nSystem halted. Connect debugger for more info\\r\\n\");\n        furi_log_puts(\"\\033[0m\\r\\n\");\n        furi_hal_debug_enable();\n\n        RESTORE_REGISTERS_AND_HALT_MCU(debug);\n#ifdef FURI_NDEBUG\n    } else {\n        uint32_t ptr = (uint32_t)__furi_check_message;\n        if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) {\n            ptr = (uint32_t) \"Check serial logs\";\n        }\n        furi_hal_rtc_set_fault_data(ptr);\n        furi_log_puts(\"\\r\\nRebooting system.\\r\\n\");\n        furi_log_puts(\"\\033[0m\\r\\n\");\n        furi_hal_power_reset();\n    }\n#endif\n    __builtin_unreachable();\n}\n\nFURI_NORETURN void __furi_halt_implementation(void) {\n    __disable_irq();\n    GET_MESSAGE_AND_STORE_REGISTERS();\n\n    bool isr = FURI_IS_IRQ_MODE();\n\n    if(__furi_check_message == NULL) {\n        __furi_check_message = \"System halt requested.\";\n    }\n\n    furi_log_puts(\"\\r\\n\\033[0;31m[HALT]\");\n    __furi_print_name(isr);\n    furi_log_puts(__furi_check_message);\n    furi_log_puts(\"\\r\\nSystem halted. Bye-bye!\\r\\n\");\n    furi_log_puts(\"\\033[0m\\r\\n\");\n\n    // Check if debug enabled by DAP\n    // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en\n    bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;\n    RESTORE_REGISTERS_AND_HALT_MCU(debug);\n\n    __builtin_unreachable();\n}\n"
  },
  {
    "path": "furi/core/check.h",
    "content": "/**\n * @file check.h\n * \n * Furi crash and assert functions.\n * \n * The main problem with crashing is that you can't do anything without disturbing registers,\n * and if you disturb registers, you won't be able to see the correct register values in the debugger.\n * \n * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function.\n * r0-r10 are stored in the ram2 on crash routine start and restored at the end.\n * The only register that is going to be lost is r11.\n * \n */\n#pragma once\n\n#include <m-core.h>\n#include \"common_defines.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls.\n#define __FURI_ASSERT_MESSAGE_FLAG (0x01)\n#define __FURI_CHECK_MESSAGE_FLAG  (0x02)\n\n/** Crash system */\nFURI_NORETURN void __furi_crash_implementation(void);\n\n/** Halt system */\nFURI_NORETURN void __furi_halt_implementation(void);\n\n/** Crash system with message. Show message after reboot. */\n#define __furi_crash(message)                                 \\\n    do {                                                      \\\n        register const void* r12 asm(\"r12\") = (void*)message; \\\n        asm volatile(\"sukima%=:\" : : \"r\"(r12));               \\\n        __furi_crash_implementation();                        \\\n    } while(0)\n\n/** Crash system\n *\n * @param      ... optional  message (const char*)\n */\n#define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))\n\n/** Halt system with message. */\n#define __furi_halt(message)                                  \\\n    do {                                                      \\\n        register const void* r12 asm(\"r12\") = (void*)message; \\\n        asm volatile(\"sukima%=:\" : : \"r\"(r12));               \\\n        __furi_halt_implementation();                         \\\n    } while(0)\n\n/** Halt system\n *\n * @param      ... optional  message (const char*)\n */\n#define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))\n\n/** Check condition and crash if check failed */\n#define __furi_check(__e, __m) \\\n    do {                       \\\n        if(!(__e)) {           \\\n            __furi_crash(__m); \\\n        }                      \\\n    } while(0)\n\n/** Check condition and crash if failed\n *\n * @param      ... condition to check and optional  message (const char*)\n */\n#define furi_check(...) \\\n    M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__))\n\n/** Only in debug build: Assert condition and crash if assert failed  */\n#ifdef FURI_DEBUG\n#define __furi_assert(__e, __m) \\\n    do {                        \\\n        if(!(__e)) {            \\\n            __furi_crash(__m);  \\\n        }                       \\\n    } while(0)\n#else\n#define __furi_assert(__e, __m) \\\n    do {                        \\\n        ((void)(__e));          \\\n        ((void)(__m));          \\\n    } while(0)\n#endif\n\n/** Assert condition and crash if failed\n *\n * @warning    only will do check if firmware compiled in debug mode\n *\n * @param      ... condition to check and optional  message (const char*)\n */\n#define furi_assert(...) \\\n    M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__))\n\n#define furi_break(__e)             \\\n    do {                            \\\n        if(!(__e)) {                \\\n            asm volatile(\"bkpt 0\"); \\\n        }                           \\\n    } while(0)\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/common_defines.h",
    "content": "#pragma once\n\n#include \"core_defines.h\"\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#define FURI_NORETURN [[noreturn]]\n#else\n#include <stdnoreturn.h>\n#define FURI_NORETURN noreturn\n#endif\n\n#include <cmsis_compiler.h>\n\n#ifndef FURI_WARN_UNUSED\n#define FURI_WARN_UNUSED __attribute__((warn_unused_result))\n#endif\n\n#ifndef FURI_DEPRECATED\n#define FURI_DEPRECATED __attribute__((deprecated))\n#endif\n\n#ifndef FURI_WEAK\n#define FURI_WEAK __attribute__((weak))\n#endif\n\n#ifndef FURI_PACKED\n#define FURI_PACKED __attribute__((packed))\n#endif\n\n#ifndef FURI_ALWAYS_INLINE\n#define FURI_ALWAYS_INLINE __attribute__((always_inline)) inline\n#endif\n\n#ifndef FURI_IS_IRQ_MASKED\n#define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U)\n#endif\n\n#ifndef FURI_IS_IRQ_MODE\n#define FURI_IS_IRQ_MODE() (__get_IPSR() != 0U)\n#endif\n\n#ifndef FURI_IS_ISR\n#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())\n#endif\n\ntypedef struct {\n    uint32_t isrm;\n    bool from_isr;\n    bool kernel_running;\n} __FuriCriticalInfo;\n\n__FuriCriticalInfo __furi_critical_enter(void);\n\nvoid __furi_critical_exit(__FuriCriticalInfo info);\n\n#ifndef FURI_CRITICAL_ENTER\n#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter();\n#endif\n\n#ifndef FURI_CRITICAL_EXIT\n#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info);\n#endif\n\n#ifndef FURI_CHECK_RETURN\n#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__))\n#endif\n\n#ifndef FURI_NAKED\n#define FURI_NAKED __attribute__((naked))\n#endif\n\n#ifndef FURI_DEFAULT\n#define FURI_DEFAULT(x) __attribute__((weak, alias(x)))\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/core_defines.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull))\n\n#ifndef MAX\n#define MAX(a, b)               \\\n    ({                          \\\n        __typeof__(a) _a = (a); \\\n        __typeof__(b) _b = (b); \\\n        _a > _b ? _a : _b;      \\\n    })\n#endif\n\n#ifndef MIN\n#define MIN(a, b)               \\\n    ({                          \\\n        __typeof__(a) _a = (a); \\\n        __typeof__(b) _b = (b); \\\n        _a < _b ? _a : _b;      \\\n    })\n#endif\n\n#ifndef ABS\n#define ABS(a) ({ (a) < 0 ? -(a) : (a); })\n#endif\n\n#ifndef ROUND_UP_TO\n#define ROUND_UP_TO(a, b)       \\\n    ({                          \\\n        __typeof__(a) _a = (a); \\\n        __typeof__(b) _b = (b); \\\n        _a / _b + !!(_a % _b);  \\\n    })\n#endif\n\n#ifndef CLAMP\n#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))\n#endif\n\n#ifndef CLAMP_WRAPAROUND\n#define CLAMP_WRAPAROUND(x, upper, lower)                       \\\n    ({                                                          \\\n        __typeof__(x) _x = (x);                                 \\\n        __typeof__(upper) _upper = (upper);                     \\\n        __typeof__(lower) _lower = (lower);                     \\\n        (_x > _upper) ? _lower : ((_x < _lower) ? _upper : _x); \\\n    })\n#endif\n\n#ifndef COUNT_OF\n#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))\n#endif\n\n#ifndef FURI_SWAP\n#define FURI_SWAP(x, y)     \\\n    do {                    \\\n        typeof(x) SWAP = x; \\\n        x = y;              \\\n        y = SWAP;           \\\n    } while(0)\n#endif\n\n#ifndef PLACE_IN_SECTION\n#define PLACE_IN_SECTION(x) __attribute__((section(x)))\n#endif\n\n#ifndef ALIGN\n#define ALIGN(n) __attribute__((aligned(n)))\n#endif\n\n#ifndef __weak\n#define __weak __attribute__((weak))\n#endif\n\n#ifndef UNUSED\n#define UNUSED(X) (void)(X)\n#endif\n\n#ifndef STRINGIFY\n#define STRINGIFY(x) #x\n#endif\n\n#ifndef TOSTRING\n#define TOSTRING(x) STRINGIFY(x)\n#endif\n\n#ifndef CONCATENATE\n#define CONCATENATE(a, b)  CONCATENATE_(a, b)\n#define CONCATENATE_(a, b) a##b\n#endif\n\n#ifndef REVERSE_BYTES_U32\n#define REVERSE_BYTES_U32(x)                                                              \\\n    ((((x) & 0x000000FF) << 24) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8) | \\\n     (((x) & 0xFF000000) >> 24))\n#endif\n\n#ifndef FURI_BIT\n#define FURI_BIT(x, n) (((x) >> (n)) & 1)\n#endif\n\n#ifndef FURI_BIT_SET\n#define FURI_BIT_SET(x, n)      \\\n    ({                          \\\n        __typeof__(x) _x = (1); \\\n        (x) |= (_x << (n));     \\\n    })\n#endif\n\n#ifndef FURI_BIT_CLEAR\n#define FURI_BIT_CLEAR(x, n)    \\\n    ({                          \\\n        __typeof__(x) _x = (1); \\\n        (x) &= ~(_x << (n));    \\\n    })\n#endif\n\n#define FURI_SW_MEMBARRIER() asm volatile(\"\" : : : \"memory\")\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/critical.c",
    "content": "#include \"common_defines.h\"\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n__FuriCriticalInfo __furi_critical_enter(void) {\n    __FuriCriticalInfo info;\n\n    info.isrm = 0;\n    info.from_isr = FURI_IS_ISR();\n    info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING);\n\n    if(info.from_isr) {\n        info.isrm = taskENTER_CRITICAL_FROM_ISR();\n    } else if(info.kernel_running) {\n        taskENTER_CRITICAL();\n    } else {\n        __disable_irq();\n    }\n\n    return info;\n}\n\nvoid __furi_critical_exit(__FuriCriticalInfo info) {\n    if(info.from_isr) {\n        taskEXIT_CRITICAL_FROM_ISR(info.isrm);\n    } else if(info.kernel_running) {\n        taskEXIT_CRITICAL();\n    } else {\n        __enable_irq();\n    }\n}\n"
  },
  {
    "path": "furi/core/dangerous_defines.h",
    "content": "#pragma once\n\n/** Assign value to variable with const modifier\n * \n * This macros is equivalent to `const_cast` from C++\n * Literally x = y, but with some magic.\n * It's as dangerous as only can be.\n * We don't advice you to use it unless you REALLY MUST.\n * Like REALLY REALLY.\n * \n * @param x - const variable \n * @param y - variable\n * \n * @return assigned variable value\n */\n#ifndef FURI_CONST_ASSIGN\n#define FURI_CONST_ASSIGN_(T, x, y) \\\n    ({                              \\\n        T* tmp_x = (T*)&x;          \\\n        *tmp_x = y;                 \\\n        *tmp_x;                     \\\n    })\n#define FURI_CONST_ASSIGN_PTR(x, y) \\\n    ({                              \\\n        void** tmp_x = (void**)&x;  \\\n        *tmp_x = y;                 \\\n        *tmp_x;                     \\\n    })\n#define FURI_CONST_ASSIGN(x, y)                                           \\\n    _Generic(                                                             \\\n        (x),                                                              \\\n        signed char: FURI_CONST_ASSIGN_(signed char, x, y),               \\\n        unsigned char: FURI_CONST_ASSIGN_(unsigned char, x, y),           \\\n        short: FURI_CONST_ASSIGN_(short, x, y),                           \\\n        unsigned short: FURI_CONST_ASSIGN_(unsigned short, x, y),         \\\n        int: FURI_CONST_ASSIGN_(int, x, y),                               \\\n        unsigned: FURI_CONST_ASSIGN_(unsigned, x, y),                     \\\n        long: FURI_CONST_ASSIGN_(long, x, y),                             \\\n        unsigned long: FURI_CONST_ASSIGN_(unsigned long, x, y),           \\\n        long long: FURI_CONST_ASSIGN_(long long, x, y),                   \\\n        unsigned long long: FURI_CONST_ASSIGN_(unsigned long long, x, y), \\\n        float: FURI_CONST_ASSIGN_(float, x, y),                           \\\n        double: FURI_CONST_ASSIGN_(double, x, y),                         \\\n        long double: FURI_CONST_ASSIGN_(long double, x, y))\n#endif\n"
  },
  {
    "path": "furi/core/event_flag.c",
    "content": "#include \"event_flag.h\"\n#include \"common_defines.h\"\n#include \"check.h\"\n\n#include <FreeRTOS.h>\n#include <event_groups.h>\n\n#include \"event_loop_link_i.h\"\n\n#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U\n#define FURI_EVENT_FLAG_VALID_BITS            ((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)\n#define FURI_EVENT_FLAG_INVALID_BITS          (~(FURI_EVENT_FLAG_VALID_BITS))\n\nstruct FuriEventFlag {\n    StaticEventGroup_t container;\n    FuriEventLoopLink event_loop_link;\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriEventFlag, container) == 0);\n\nFuriEventFlag* furi_event_flag_alloc(void) {\n    furi_check(!FURI_IS_IRQ_MODE());\n\n    FuriEventFlag* instance = malloc(sizeof(FuriEventFlag));\n\n    furi_check(xEventGroupCreateStatic(&instance->container) == (EventGroupHandle_t)instance);\n\n    return instance;\n}\n\nvoid furi_event_flag_free(FuriEventFlag* instance) {\n    furi_check(!FURI_IS_IRQ_MODE());\n\n    // Event Loop must be disconnected\n    furi_check(!instance->event_loop_link.item_in);\n    furi_check(!instance->event_loop_link.item_out);\n\n    vEventGroupDelete((EventGroupHandle_t)instance);\n    free(instance);\n}\n\nuint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {\n    furi_check(instance);\n    furi_check((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);\n\n    EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;\n    uint32_t rflags;\n    BaseType_t yield;\n\n    FURI_CRITICAL_ENTER();\n\n    if(FURI_IS_IRQ_MODE()) {\n        yield = pdFALSE;\n        if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {\n            rflags = (uint32_t)FuriFlagErrorResource;\n        } else {\n            rflags = flags;\n            portYIELD_FROM_ISR(yield);\n        }\n    } else {\n        rflags = xEventGroupSetBits(hEventGroup, (EventBits_t)flags);\n    }\n\n    if(rflags & flags) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn);\n    }\n\n    FURI_CRITICAL_EXIT();\n\n    /* Return event flags after setting */\n    return rflags;\n}\n\nuint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {\n    furi_check(instance);\n    furi_check((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);\n\n    EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;\n    uint32_t rflags;\n\n    FURI_CRITICAL_ENTER();\n    if(FURI_IS_IRQ_MODE()) {\n        rflags = xEventGroupGetBitsFromISR(hEventGroup);\n\n        if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) {\n            rflags = (uint32_t)FuriStatusErrorResource;\n        } else {\n            /* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */\n            /* Yield is required here otherwise clear operation might not execute in the right order. */\n            /* See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/93 for more info.               */\n            portYIELD_FROM_ISR(pdTRUE);\n        }\n    } else {\n        rflags = xEventGroupClearBits(hEventGroup, (EventBits_t)flags);\n    }\n\n    if(rflags & flags) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n    FURI_CRITICAL_EXIT();\n\n    /* Return event flags before clearing */\n    return rflags;\n}\n\nuint32_t furi_event_flag_get(FuriEventFlag* instance) {\n    furi_check(instance);\n\n    EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;\n    uint32_t rflags;\n\n    if(FURI_IS_IRQ_MODE()) {\n        rflags = xEventGroupGetBitsFromISR(hEventGroup);\n    } else {\n        rflags = xEventGroupGetBits(hEventGroup);\n    }\n\n    /* Return current event flags */\n    return rflags;\n}\n\nuint32_t furi_event_flag_wait(\n    FuriEventFlag* instance,\n    uint32_t flags,\n    uint32_t options,\n    uint32_t timeout) {\n    furi_check(!FURI_IS_IRQ_MODE());\n    furi_check(instance);\n    furi_check((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);\n\n    EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;\n    BaseType_t wait_all;\n    BaseType_t exit_clr;\n    uint32_t rflags;\n\n    if(options & FuriFlagWaitAll) {\n        wait_all = pdTRUE;\n    } else {\n        wait_all = pdFAIL;\n    }\n\n    if(options & FuriFlagNoClear) {\n        exit_clr = pdFAIL;\n    } else {\n        exit_clr = pdTRUE;\n    }\n\n    rflags = xEventGroupWaitBits(\n        hEventGroup, (EventBits_t)flags, exit_clr, wait_all, (TickType_t)timeout);\n\n    if(options & FuriFlagWaitAll) {\n        if((flags & rflags) != flags) {\n            if(timeout > 0U) {\n                rflags = (uint32_t)FuriStatusErrorTimeout;\n            } else {\n                rflags = (uint32_t)FuriStatusErrorResource;\n            }\n        }\n    } else {\n        if((flags & rflags) == 0U) {\n            if(timeout > 0U) {\n                rflags = (uint32_t)FuriStatusErrorTimeout;\n            } else {\n                rflags = (uint32_t)FuriStatusErrorResource;\n            }\n        }\n    }\n\n    if((rflags & FuriFlagError) == 0U) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    /* Return event flags before clearing */\n    return rflags;\n}\n\nstatic FuriEventLoopLink* furi_event_flag_event_loop_get_link(FuriEventLoopObject* object) {\n    FuriEventFlag* instance = object;\n    furi_assert(instance);\n    return &instance->event_loop_link;\n}\n\nstatic bool\n    furi_event_flag_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {\n    FuriEventFlag* instance = object;\n    furi_assert(instance);\n\n    if(event == FuriEventLoopEventIn) {\n        return (furi_event_flag_get(instance) & FURI_EVENT_FLAG_VALID_BITS);\n    } else if(event == FuriEventLoopEventOut) {\n        return (furi_event_flag_get(instance) & FURI_EVENT_FLAG_VALID_BITS) !=\n               FURI_EVENT_FLAG_VALID_BITS;\n    } else {\n        furi_crash();\n    }\n}\n\nconst FuriEventLoopContract furi_event_flag_event_loop_contract = {\n    .get_link = furi_event_flag_event_loop_get_link,\n    .get_level = furi_event_flag_event_loop_get_level,\n};\n"
  },
  {
    "path": "furi/core/event_flag.h",
    "content": "/**\n * @file event_flag.h\n * Furi Event Flag\n */\n#pragma once\n\n#include \"base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FuriEventFlag FuriEventFlag;\n\n/** Allocate FuriEventFlag\n *\n * @return     pointer to FuriEventFlag\n */\nFuriEventFlag* furi_event_flag_alloc(void);\n\n/** Deallocate FuriEventFlag\n *\n * @param      instance  pointer to FuriEventFlag\n */\nvoid furi_event_flag_free(FuriEventFlag* instance);\n\n/** Set flags\n *\n * @warning    result of this function can be flags that you've just asked to\n *             set or not if someone was waiting for them and asked to clear it.\n *             It is highly recommended to read this function and\n *             xEventGroupSetBits source code.\n *\n * @param      instance  pointer to FuriEventFlag\n * @param[in]  flags     The flags to set\n *\n * @return     Resulting flags(see warning) or error (FuriStatus)\n */\nuint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags);\n\n/** Clear flags\n *\n * @param      instance  pointer to FuriEventFlag\n * @param[in]  flags     The flags\n *\n * @return     Resulting flags or error (FuriStatus)\n */\nuint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags);\n\n/** Get flags\n *\n * @param      instance  pointer to FuriEventFlag\n *\n * @return     Resulting flags\n */\nuint32_t furi_event_flag_get(FuriEventFlag* instance);\n\n/** Wait flags\n *\n * @param      instance  pointer to FuriEventFlag\n * @param[in]  flags     The flags\n * @param[in]  options   The option flags\n * @param[in]  timeout   The timeout\n *\n * @return     Resulting flags or error (FuriStatus)\n */\nuint32_t furi_event_flag_wait(\n    FuriEventFlag* instance,\n    uint32_t flags,\n    uint32_t options,\n    uint32_t timeout);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/event_loop.c",
    "content": "#include \"event_loop_i.h\"\n\n#include \"log.h\"\n#include \"check.h\"\n#include \"thread.h\"\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n#define TAG \"FuriEventLoop\"\n\n/*\n * Private functions\n */\n\nstatic FuriEventLoopItem* furi_event_loop_item_alloc(\n    FuriEventLoop* owner,\n    const FuriEventLoopContract* contract,\n    void* object,\n    FuriEventLoopEvent event);\n\nstatic void furi_event_loop_item_free(FuriEventLoopItem* instance);\n\nstatic void furi_event_loop_item_free_later(FuriEventLoopItem* instance);\n\nstatic void furi_event_loop_item_set_callback(\n    FuriEventLoopItem* instance,\n    FuriEventLoopEventCallback callback,\n    void* callback_context);\n\nstatic void furi_event_loop_item_notify(FuriEventLoopItem* instance);\n\nstatic bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance);\n\nstatic bool furi_event_loop_signal_callback(uint32_t signal, void* arg, void* context) {\n    furi_assert(context);\n    FuriEventLoop* instance = context;\n    UNUSED(arg);\n\n    switch(signal) {\n    case FuriSignalExit:\n        furi_event_loop_stop(instance);\n        return true;\n    // Room for possible other standard signal handlers\n    default:\n        return false;\n    }\n}\n\n/*\n * Main public API\n */\n\nFuriEventLoop* furi_event_loop_alloc(void) {\n    FuriEventLoop* instance = malloc(sizeof(FuriEventLoop));\n\n    instance->thread_id = furi_thread_get_current_id();\n\n    FuriEventLoopTree_init(instance->tree);\n    WaitingList_init(instance->waiting_list);\n    TimerList_init(instance->timer_list);\n    TimerQueue_init(instance->timer_queue);\n    PendingQueue_init(instance->pending_queue);\n\n    // Clear notification state and value\n    TaskHandle_t task = (TaskHandle_t)instance->thread_id;\n    xTaskNotifyStateClearIndexed(task, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX);\n    ulTaskNotifyValueClearIndexed(task, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0xFFFFFFFF);\n\n    return instance;\n}\n\nvoid furi_event_loop_free(FuriEventLoop* instance) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    furi_check(instance->state == FuriEventLoopStateStopped);\n\n    furi_event_loop_process_timer_queue(instance);\n    furi_check(TimerList_empty_p(instance->timer_list));\n    furi_check(WaitingList_empty_p(instance->waiting_list));\n    furi_check(!instance->are_thread_flags_subscribed);\n\n    FuriEventLoopTree_clear(instance->tree);\n    PendingQueue_clear(instance->pending_queue);\n\n    uint32_t flags = 0;\n    BaseType_t ret = xTaskNotifyWaitIndexed(\n        FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, 0);\n    if(ret == pdTRUE) {\n        FURI_LOG_D(TAG, \"Some events were not processed: 0x%lx\", flags);\n    }\n\n    free(instance);\n}\n\nstatic inline FuriEventLoopProcessStatus\n    furi_event_loop_process_edge_event(FuriEventLoopItem* item) {\n    FuriEventLoopProcessStatus status = FuriEventLoopProcessStatusComplete;\n    item->callback(item->object, item->callback_context);\n\n    return status;\n}\n\nstatic inline FuriEventLoopProcessStatus\n    furi_event_loop_process_level_event(FuriEventLoopItem* item) {\n    FuriEventLoopProcessStatus status = FuriEventLoopProcessStatusComplete;\n    if(item->contract->get_level(item->object, item->event)) {\n        item->callback(item->object, item->callback_context);\n\n        if(item->contract->get_level(item->object, item->event)) {\n            status = FuriEventLoopProcessStatusIncomplete;\n        }\n    }\n\n    return status;\n}\n\nstatic inline FuriEventLoopProcessStatus\n    furi_event_loop_process_event(FuriEventLoop* instance, FuriEventLoopItem* item) {\n    FuriEventLoopProcessStatus status;\n\n    if(item->event & FuriEventLoopEventFlagOnce) {\n        furi_event_loop_unsubscribe(instance, item->object);\n    }\n\n    instance->current_item = item;\n\n    if(item->event & FuriEventLoopEventFlagEdge) {\n        status = furi_event_loop_process_edge_event(item);\n    } else {\n        status = furi_event_loop_process_level_event(item);\n    }\n\n    instance->current_item = NULL;\n\n    if(item->owner == NULL) {\n        status = FuriEventLoopProcessStatusFreeLater;\n    }\n\n    return status;\n}\n\nstatic inline FuriEventLoopItem* furi_event_loop_get_waiting_item(FuriEventLoop* instance) {\n    FuriEventLoopItem* item = NULL;\n\n    FURI_CRITICAL_ENTER();\n\n    if(!WaitingList_empty_p(instance->waiting_list)) {\n        item = WaitingList_pop_front(instance->waiting_list);\n        WaitingList_init_field(item);\n    }\n\n    FURI_CRITICAL_EXIT();\n\n    return item;\n}\n\nstatic inline void furi_event_loop_sync_flags(FuriEventLoop* instance) {\n    FURI_CRITICAL_ENTER();\n\n    if(!WaitingList_empty_p(instance->waiting_list)) {\n        xTaskNotifyIndexed(\n            (TaskHandle_t)instance->thread_id,\n            FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,\n            FuriEventLoopFlagEvent,\n            eSetBits);\n    }\n\n    FURI_CRITICAL_EXIT();\n}\n\nstatic void furi_event_loop_process_waiting_list(FuriEventLoop* instance) {\n    FuriEventLoopItem* item = furi_event_loop_get_waiting_item(instance);\n    if(!item) return;\n\n    FuriEventLoopProcessStatus status = furi_event_loop_process_event(instance, item);\n\n    if(status == FuriEventLoopProcessStatusComplete) {\n        // Event processing complete, do nothing\n    } else if(status == FuriEventLoopProcessStatusIncomplete) {\n        // Event processing incomplete, put item back in waiting list\n        furi_event_loop_item_notify(item);\n    } else if(status == FuriEventLoopProcessStatusFreeLater) { //-V547\n        // Unsubscribed from inside the callback, delete item\n        furi_event_loop_item_free(item);\n    } else {\n        furi_crash();\n    }\n\n    furi_event_loop_sync_flags(instance);\n}\n\nstatic void furi_event_loop_process_pending_callbacks(FuriEventLoop* instance) {\n    for(; !PendingQueue_empty_p(instance->pending_queue);\n        PendingQueue_pop_back(NULL, instance->pending_queue)) {\n        const FuriEventLoopPendingQueueItem* item = PendingQueue_back(instance->pending_queue);\n        item->callback(item->context);\n    }\n}\n\nstatic void furi_event_loop_restore_flags(FuriEventLoop* instance, uint32_t flags) {\n    if(flags) {\n        xTaskNotifyIndexed(\n            (TaskHandle_t)instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flags, eSetBits);\n    }\n}\n\nvoid furi_event_loop_run(FuriEventLoop* instance) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    FuriThread* thread = furi_thread_get_current();\n\n    // Set the default signal callback if none was previously set\n    if(furi_thread_get_signal_callback(thread) == NULL) {\n        furi_thread_set_signal_callback(thread, furi_event_loop_signal_callback, instance);\n    }\n\n    furi_event_loop_init_tick(instance);\n\n    instance->state = FuriEventLoopStateRunning;\n\n    while(true) {\n        const TickType_t ticks_to_sleep =\n            MIN(furi_event_loop_get_timer_wait_time(instance),\n                furi_event_loop_get_tick_wait_time(instance));\n\n        uint32_t flags = 0;\n        BaseType_t ret = xTaskNotifyWaitIndexed(\n            FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, ticks_to_sleep);\n\n        if(ret == pdTRUE) {\n            if(flags & FuriEventLoopFlagStop) {\n                instance->state = FuriEventLoopStateStopped;\n                break;\n\n            } else if(flags & FuriEventLoopFlagEvent) {\n                furi_event_loop_process_waiting_list(instance);\n                furi_event_loop_restore_flags(instance, flags & ~FuriEventLoopFlagEvent);\n\n            } else if(flags & FuriEventLoopFlagTimer) {\n                furi_event_loop_process_timer_queue(instance);\n                furi_event_loop_restore_flags(instance, flags & ~FuriEventLoopFlagTimer);\n\n            } else if(flags & FuriEventLoopFlagPending) {\n                furi_event_loop_process_pending_callbacks(instance);\n\n            } else if(flags & FuriEventLoopFlagThreadFlag) {\n                if(instance->are_thread_flags_subscribed)\n                    instance->thread_flags_callback(instance->thread_flags_callback_context);\n\n            } else {\n                furi_crash();\n            }\n\n        } else if(!furi_event_loop_process_expired_timers(instance)) {\n            furi_event_loop_process_tick(instance);\n        }\n    }\n\n    // Disable the default signal callback\n    if(furi_thread_get_signal_callback(thread) == furi_event_loop_signal_callback) {\n        furi_thread_set_signal_callback(thread, NULL, NULL);\n    }\n}\n\nstatic void furi_event_loop_notify(FuriEventLoop* instance, FuriEventLoopFlag flag) {\n    if(FURI_IS_IRQ_MODE()) {\n        BaseType_t yield = pdFALSE;\n\n        (void)xTaskNotifyIndexedFromISR(\n            (TaskHandle_t)instance->thread_id,\n            FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,\n            flag,\n            eSetBits,\n            &yield);\n\n        portYIELD_FROM_ISR(yield);\n\n    } else {\n        (void)xTaskNotifyIndexed(\n            (TaskHandle_t)instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flag, eSetBits);\n    }\n}\n\nvoid furi_event_loop_stop(FuriEventLoop* instance) {\n    furi_check(instance);\n    furi_event_loop_notify(instance, FuriEventLoopFlagStop);\n}\n\n/*\n * Public deferred function call API\n */\n\nvoid furi_event_loop_pend_callback(\n    FuriEventLoop* instance,\n    FuriEventLoopPendingCallback callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    furi_check(callback);\n\n    const FuriEventLoopPendingQueueItem item = {\n        .callback = callback,\n        .context = context,\n    };\n\n    PendingQueue_push_front(instance->pending_queue, item);\n\n    furi_event_loop_notify(instance, FuriEventLoopFlagPending);\n}\n\n/*\n * Private generic susbscription API\n */\n\nstatic void furi_event_loop_object_subscribe(\n    FuriEventLoop* instance,\n    FuriEventLoopObject* object,\n    const FuriEventLoopContract* contract,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    furi_check(object);\n    furi_assert(contract);\n    furi_check(callback);\n\n    FURI_CRITICAL_ENTER();\n\n    furi_check(FuriEventLoopTree_get(instance->tree, object) == NULL);\n\n    // Allocate and setup item\n    FuriEventLoopItem* item = furi_event_loop_item_alloc(instance, contract, object, event);\n    furi_event_loop_item_set_callback(item, callback, context);\n\n    FuriEventLoopTree_set_at(instance->tree, object, item);\n\n    FuriEventLoopLink* link = item->contract->get_link(object);\n    FuriEventLoopEvent event_noflags = item->event & FuriEventLoopEventMask;\n\n    if(event_noflags == FuriEventLoopEventIn) {\n        furi_check(link->item_in == NULL);\n        link->item_in = item;\n    } else if(event_noflags == FuriEventLoopEventOut) {\n        furi_check(link->item_out == NULL);\n        link->item_out = item;\n    } else {\n        furi_crash();\n    }\n\n    if(!(item->event & FuriEventLoopEventFlagEdge)) {\n        if(item->contract->get_level(item->object, event_noflags)) {\n            furi_event_loop_item_notify(item);\n        }\n    }\n\n    FURI_CRITICAL_EXIT();\n}\n\n/**\n * Public specialized subscription API\n */\n\nvoid furi_event_loop_subscribe_event_flag(\n    FuriEventLoop* instance,\n    FuriEventFlag* event_flag,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    extern const FuriEventLoopContract furi_event_flag_event_loop_contract;\n    furi_event_loop_object_subscribe(\n        instance, event_flag, &furi_event_flag_event_loop_contract, event, callback, context);\n}\n\nvoid furi_event_loop_subscribe_message_queue(\n    FuriEventLoop* instance,\n    FuriMessageQueue* message_queue,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    extern const FuriEventLoopContract furi_message_queue_event_loop_contract;\n\n    furi_event_loop_object_subscribe(\n        instance, message_queue, &furi_message_queue_event_loop_contract, event, callback, context);\n}\n\nvoid furi_event_loop_subscribe_stream_buffer(\n    FuriEventLoop* instance,\n    FuriStreamBuffer* stream_buffer,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    extern const FuriEventLoopContract furi_stream_buffer_event_loop_contract;\n\n    furi_event_loop_object_subscribe(\n        instance, stream_buffer, &furi_stream_buffer_event_loop_contract, event, callback, context);\n}\n\nvoid furi_event_loop_subscribe_semaphore(\n    FuriEventLoop* instance,\n    FuriSemaphore* semaphore,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    extern const FuriEventLoopContract furi_semaphore_event_loop_contract;\n\n    furi_event_loop_object_subscribe(\n        instance, semaphore, &furi_semaphore_event_loop_contract, event, callback, context);\n}\n\nvoid furi_event_loop_subscribe_mutex(\n    FuriEventLoop* instance,\n    FuriMutex* mutex,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context) {\n    extern const FuriEventLoopContract furi_mutex_event_loop_contract;\n\n    furi_event_loop_object_subscribe(\n        instance, mutex, &furi_mutex_event_loop_contract, event, callback, context);\n}\n\nvoid furi_event_loop_subscribe_thread_flags(\n    FuriEventLoop* instance,\n    FuriEventLoopThreadFlagsCallback callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(callback);\n    furi_check(!instance->are_thread_flags_subscribed);\n    instance->are_thread_flags_subscribed = true;\n    instance->thread_flags_callback = callback;\n    instance->thread_flags_callback_context = context;\n}\n\nvoid furi_event_loop_unsubscribe_thread_flags(FuriEventLoop* instance) {\n    furi_check(instance);\n    furi_check(instance->are_thread_flags_subscribed);\n    instance->are_thread_flags_subscribed = false;\n}\n\n/**\n * Public generic unsubscription API\n */\n\nvoid furi_event_loop_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* object) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n\n    FURI_CRITICAL_ENTER();\n\n    FuriEventLoopItem* item = NULL;\n    furi_check(FuriEventLoopTree_pop_at(&item, instance->tree, object));\n\n    furi_check(item);\n    furi_check(item->owner == instance);\n\n    FuriEventLoopLink* link = item->contract->get_link(object);\n    FuriEventLoopEvent event_noflags = item->event & FuriEventLoopEventMask;\n\n    if(event_noflags == FuriEventLoopEventIn) {\n        furi_check(link->item_in == item);\n        link->item_in = NULL;\n    } else if(event_noflags == FuriEventLoopEventOut) {\n        furi_check(link->item_out == item);\n        link->item_out = NULL;\n    } else {\n        furi_crash();\n    }\n\n    if(furi_event_loop_item_is_waiting(item)) {\n        WaitingList_unlink(item);\n    }\n\n    if(instance->current_item == item) {\n        furi_event_loop_item_free_later(item);\n    } else {\n        furi_event_loop_item_free(item);\n    }\n\n    FURI_CRITICAL_EXIT();\n}\n\nbool furi_event_loop_is_subscribed(FuriEventLoop* instance, FuriEventLoopObject* object) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    FURI_CRITICAL_ENTER();\n\n    FuriEventLoopItem* const* item = FuriEventLoopTree_cget(instance->tree, object);\n    bool result = !!item;\n\n    FURI_CRITICAL_EXIT();\n    return result;\n}\n\n/* \n * Private Event Loop Item functions\n */\n\nstatic FuriEventLoopItem* furi_event_loop_item_alloc(\n    FuriEventLoop* owner,\n    const FuriEventLoopContract* contract,\n    void* object,\n    FuriEventLoopEvent event) {\n    furi_assert(owner);\n    furi_assert(object);\n\n    FuriEventLoopItem* instance = malloc(sizeof(FuriEventLoopItem));\n\n    instance->owner = owner;\n    instance->contract = contract;\n    instance->object = object;\n    instance->event = event;\n\n    WaitingList_init_field(instance);\n\n    return instance;\n}\n\nstatic void furi_event_loop_item_free(FuriEventLoopItem* instance) {\n    furi_assert(instance);\n    furi_assert(!furi_event_loop_item_is_waiting(instance));\n    free(instance);\n}\n\nstatic void furi_event_loop_item_free_later(FuriEventLoopItem* instance) {\n    furi_assert(instance);\n    furi_assert(!furi_event_loop_item_is_waiting(instance));\n    instance->owner = NULL;\n}\n\nstatic void furi_event_loop_item_set_callback(\n    FuriEventLoopItem* instance,\n    FuriEventLoopEventCallback callback,\n    void* callback_context) {\n    furi_assert(instance);\n    furi_assert(!instance->callback);\n\n    instance->callback = callback;\n    instance->callback_context = callback_context;\n}\n\nstatic void furi_event_loop_item_notify(FuriEventLoopItem* instance) {\n    furi_assert(instance);\n\n    FURI_CRITICAL_ENTER();\n\n    FuriEventLoop* owner = instance->owner;\n    furi_assert(owner);\n\n    if(!furi_event_loop_item_is_waiting(instance)) {\n        WaitingList_push_back(owner->waiting_list, instance);\n    }\n\n    FURI_CRITICAL_EXIT();\n\n    furi_event_loop_notify(owner, FuriEventLoopFlagEvent);\n}\n\nstatic bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance) {\n    return instance->WaitingList.prev || instance->WaitingList.next;\n}\n\nvoid furi_event_loop_thread_flag_callback(FuriThreadId thread_id) {\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n    BaseType_t yield;\n\n    if(FURI_IS_IRQ_MODE()) {\n        yield = pdFALSE;\n        (void)xTaskNotifyIndexedFromISR(\n            hTask,\n            FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,\n            FuriEventLoopFlagThreadFlag,\n            eSetBits,\n            &yield);\n        portYIELD_FROM_ISR(yield);\n    } else {\n        (void)xTaskNotifyIndexed(\n            hTask, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagThreadFlag, eSetBits);\n    }\n}\n\n/*\n * Internal event loop link API, used by supported primitives\n */\n\nvoid furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event) {\n    furi_assert(instance);\n\n    FURI_CRITICAL_ENTER();\n\n    if(event & FuriEventLoopEventIn) {\n        if(instance->item_in) furi_event_loop_item_notify(instance->item_in);\n    } else if(event & FuriEventLoopEventOut) {\n        if(instance->item_out) furi_event_loop_item_notify(instance->item_out);\n    } else {\n        furi_crash();\n    }\n\n    FURI_CRITICAL_EXIT();\n}\n"
  },
  {
    "path": "furi/core/event_loop.h",
    "content": "/**\n * @file event_loop.h\n * @brief      Furi Event Loop\n *\n *             This module is designed to handle application event loop in fully\n *             asynchronous, reactive nature. On the low level this modules is\n *             inspired by epoll/kqueue concept, on the high level by asyncio\n *             event loop.\n *\n *             This module is trying to best fit into Furi OS, so we don't\n *             provide any compatibility with other event driven APIs. But\n *             programming concepts are the same, except some runtime\n *             limitations from our side.\n *\n * @warning Only ONE instance of FuriEventLoop per thread is possible. ALL FuriEventLoop\n * funcitons MUST be called from the same thread that the instance was created in.\n */\n#pragma once\n\n#include \"base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Enumeration of event types, flags and masks.\n *\n * Only one event direction (In or Out) can be used per subscription.\n * An object can have no more than one subscription for each direction.\n *\n * Additional flags that modify the behaviour can be\n * set using the bitwise OR operation (see flag description).\n */\ntypedef enum {\n    /**\n     * @brief Subscribe to In events.\n     *\n     * In events occur on the following conditions:\n     * - One or more items were inserted into a FuriMessageQueue,\n     * - Enough data has been written to a FuriStreamBuffer,\n     * - A FuriSemaphore has been released at least once,\n     * - A FuriMutex has been released.\n     */\n    FuriEventLoopEventIn = 0x00000001U,\n    /**\n     * @brief Subscribe to Out events.\n     *\n     * Out events occur on the following conditions:\n     * - One or more items were removed from a FuriMessageQueue,\n     * - Any amount of data has been read out of a FuriStreamBuffer,\n     * - A FuriSemaphore has been acquired at least once,\n     * - A FuriMutex has been acquired.\n     */\n    FuriEventLoopEventOut = 0x00000002U,\n    /**\n     * @brief Special value containing the event direction bits, used internally.\n     */\n    FuriEventLoopEventMask = 0x00000003U,\n    /**\n     * @brief Use edge triggered events.\n     *\n     * By default, level triggered events are used. A level above zero\n     * is reported based on the following conditions:\n     *\n     * In events:\n     * - a FuriMessageQueue contains one or more items,\n     * - a FuriStreamBuffer contains one or more bytes,\n     * - a FuriSemaphore can be acquired at least once,\n     * - a FuriMutex can be acquired.\n     *\n     * Out events:\n     * - a FuriMessageQueue has at least one item of free space,\n     * - a FuriStreamBuffer has at least one byte of free space,\n     * - a FuriSemaphore has been acquired at least once,\n     * - a FuriMutex has been acquired.\n     *\n     * If this flag is NOT set, the event will be generated repeatedly until\n     * the level becomes zero (e.g. all items have been removed from\n     * a FuriMessageQueue in case of the \"In\" event, etc.)\n     *\n     * If this flag IS set, then the above check is skipped and the event\n     * is generated ONLY when a change occurs, with the event direction\n     * (In or Out) taken into account.\n     */\n    FuriEventLoopEventFlagEdge = 0x00000004U,\n    /**\n     * @brief Automatically unsubscribe from events after one time.\n     *\n     * By default, events will be generated each time the specified conditions\n     * have been met. If this flag IS set, the event subscription will be cancelled\n     * upon the first occurred event and no further events will be generated.\n     */\n    FuriEventLoopEventFlagOnce = 0x00000008U,\n    /**\n     * @brief Special value containing the event flag bits, used internally.\n     */\n    FuriEventLoopEventFlagMask = 0xFFFFFFFCU,\n    /**\n     * @brief Special value to force the enum to 32-bit values.\n     */\n    FuriEventLoopEventReserved = UINT32_MAX,\n} FuriEventLoopEvent;\n\n/** Anonymous message queue type */\ntypedef struct FuriEventLoop FuriEventLoop;\n\n/** Allocate Event Loop instance\n *\n * Couple things to keep in mind:\n * - You can have 1 event_loop per 1 thread\n * - You can not use event_loop instance in the other thread\n * - Do not use blocking API to query object delegated to Event Loop\n *\n * @return     The Event Loop instance\n */\nFuriEventLoop* furi_event_loop_alloc(void);\n\n/** Free Event Loop instance\n *\n * @param      instance  The Event Loop instance\n */\nvoid furi_event_loop_free(FuriEventLoop* instance);\n\n/** Continuously poll for events\n *\n * Can be stopped with `furi_event_loop_stop`\n *\n * @param      instance  The Event Loop instance\n */\nvoid furi_event_loop_run(FuriEventLoop* instance);\n\n/** Stop Event Loop instance\n *\n * @param      instance  The Event Loop instance\n */\nvoid furi_event_loop_stop(FuriEventLoop* instance);\n\n/*\n * Tick related API\n */\n\n/** Tick callback type\n *\n * @param      context  The context for callback\n */\ntypedef void (*FuriEventLoopTickCallback)(void* context);\n\n/** Set Event Loop tick callback\n *\n * Tick callback is called periodically after specified inactivity time.\n * It acts like a low-priority timer: it will only fire if there is time\n * left after processing the synchronization primitives and the regular timers.\n * Therefore, it is not monotonic: ticks will be skipped if the event loop is busy.\n *\n * @param      instance  The Event Loop instance\n * @param[in]  interval  The tick interval\n * @param[in]  callback  The callback to call\n * @param      context   The context for callback\n */\nvoid furi_event_loop_tick_set(\n    FuriEventLoop* instance,\n    uint32_t interval,\n    FuriEventLoopTickCallback callback,\n    void* context);\n\n/*\n * Deferred function call API\n */\n\n/**\n * @brief Timer callback type for functions to be called in a deferred manner.\n *\n * @param[in,out] context pointer to a user-specific object that was provided during\n *                        furi_event_loop_pend_callback() call\n */\ntypedef void (*FuriEventLoopPendingCallback)(void* context);\n\n/**\n * @brief Call a function when all preceding timer commands are processed\n *\n * This function may be useful to call another function when the event loop has been started.\n *\n * @param[in,out] instance pointer to the current FuriEventLoop instance\n * @param[in] callback pointer to the callback to be executed when previous commands have been processed\n * @param[in,out] context pointer to a user-specific object (will be passed to the callback)\n */\nvoid furi_event_loop_pend_callback(\n    FuriEventLoop* instance,\n    FuriEventLoopPendingCallback callback,\n    void* context);\n\n/*\n * Event subscription/notification APIs\n */\n\ntypedef void FuriEventLoopObject;\n\n/** Callback type for event loop events\n *\n * @param      object   The object that triggered the event\n * @param      context  The context that was provided upon subscription\n */\ntypedef void (*FuriEventLoopEventCallback)(FuriEventLoopObject* object, void* context);\n\n/** Callback type for event loop thread flag events\n * \n * @param context The context that was provided upon subscription\n */\ntypedef void (*FuriEventLoopThreadFlagsCallback)(void* context);\n\n/** Opaque event flag type */\ntypedef struct FuriEventFlag FuriEventFlag;\n\n/** Subscribe to event flag events\n *\n * @warning you can only have one subscription for one event type.\n *\n * @param      instance       The Event Loop instance\n * @param      event_flag     The event flag to add\n * @param[in]  event          The Event Loop event to trigger on\n * @param[in]  callback       The callback to call on event\n * @param      context        The context for callback\n */\n\nvoid furi_event_loop_subscribe_event_flag(\n    FuriEventLoop* instance,\n    FuriEventFlag* event_flag,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context);\n\n/** Opaque message queue type */\ntypedef struct FuriMessageQueue FuriMessageQueue;\n\n/** Subscribe to message queue events\n * \n * @warning you can only have one subscription for one event type.\n *\n * @param      instance       The Event Loop instance\n * @param      message_queue  The message queue to add\n * @param[in]  event          The Event Loop event to trigger on\n * @param[in]  callback       The callback to call on event\n * @param      context        The context for callback\n */\nvoid furi_event_loop_subscribe_message_queue(\n    FuriEventLoop* instance,\n    FuriMessageQueue* message_queue,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context);\n\n/** Opaque stream buffer type */\ntypedef struct FuriStreamBuffer FuriStreamBuffer;\n\n/** Subscribe to stream buffer events\n *\n * @warning you can only have one subscription for one event type.\n *\n * @param      instance       The Event Loop instance\n * @param      stream_buffer  The stream buffer to add\n * @param[in]  event          The Event Loop event to trigger on\n * @param[in]  callback       The callback to call on event\n * @param      context        The context for callback\n */\nvoid furi_event_loop_subscribe_stream_buffer(\n    FuriEventLoop* instance,\n    FuriStreamBuffer* stream_buffer,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context);\n\n/** Opaque semaphore type */\ntypedef struct FuriSemaphore FuriSemaphore;\n\n/** Subscribe to semaphore events\n *\n * @warning you can only have one subscription for one event type.\n *\n * @param      instance       The Event Loop instance\n * @param      semaphore      The semaphore to add\n * @param[in]  event          The Event Loop event to trigger on\n * @param[in]  callback       The callback to call on event\n * @param      context        The context for callback\n */\nvoid furi_event_loop_subscribe_semaphore(\n    FuriEventLoop* instance,\n    FuriSemaphore* semaphore,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context);\n\n/** Opaque mutex type */\ntypedef struct FuriMutex FuriMutex;\n\n/** Subscribe to mutex events\n *\n * @warning you can only have one subscription for one event type.\n *\n * @param      instance       The Event Loop instance\n * @param      mutex          The mutex to add\n * @param[in]  event          The Event Loop event to trigger on\n * @param[in]  callback       The callback to call on event\n * @param      context        The context for callback\n */\nvoid furi_event_loop_subscribe_mutex(\n    FuriEventLoop* instance,\n    FuriMutex* mutex,\n    FuriEventLoopEvent event,\n    FuriEventLoopEventCallback callback,\n    void* context);\n\n/** Subscribe to thread flag events of the current thread\n * \n * @param instance The Event Loop instance\n * @param callback The callback to call when a flag has been set\n * @param context  The context for callback\n */\nvoid furi_event_loop_subscribe_thread_flags(\n    FuriEventLoop* instance,\n    FuriEventLoopThreadFlagsCallback callback,\n    void* context);\n\n/** Unsubscribe from thread flag events of the current thread\n * \n * @param instance The Event Loop instance\n */\nvoid furi_event_loop_unsubscribe_thread_flags(FuriEventLoop* instance);\n\n/** Unsubscribe from events (common)\n *\n * @param      instance       The Event Loop instance\n * @param      object         The object to unsubscribe from\n */\nvoid furi_event_loop_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* object);\n\n/**\n * @brief Checks if the loop is subscribed to an object of any kind\n * \n * @param      instance       Event Loop instance\n * @param      object         Object to check\n */\nbool furi_event_loop_is_subscribed(FuriEventLoop* instance, FuriEventLoopObject* object);\n\n/**\n * @brief Convenience function for `if(is_subscribed()) unsubscribe()`\n */\nstatic inline void\n    furi_event_loop_maybe_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* object) {\n    if(furi_event_loop_is_subscribed(instance, object))\n        furi_event_loop_unsubscribe(instance, object);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/event_loop_i.h",
    "content": "#pragma once\n\n#include \"event_loop.h\"\n#include \"event_loop_link_i.h\"\n#include \"event_loop_timer_i.h\"\n#include \"event_loop_tick_i.h\"\n#include \"event_loop_thread_flag_interface.h\"\n\n#include <m-list.h>\n#include <m-bptree.h>\n#include <m-i-list.h>\n\n#include \"thread.h\"\n#include \"thread_i.h\"\n\nstruct FuriEventLoopItem {\n    // Source\n    FuriEventLoop* owner;\n\n    // Tracking item\n    FuriEventLoopEvent event;\n    FuriEventLoopObject* object;\n    const FuriEventLoopContract* contract;\n\n    // Callback and context\n    FuriEventLoopEventCallback callback;\n    void* callback_context;\n\n    // Waiting list\n    ILIST_INTERFACE(WaitingList, FuriEventLoopItem);\n};\n\nILIST_DEF(WaitingList, FuriEventLoopItem, M_POD_OPLIST)\n\n/* Event Loop RB tree */\n#define FURI_EVENT_LOOP_TREE_RANK (4)\n\nBPTREE_DEF2( // NOLINT\n    FuriEventLoopTree,\n    FURI_EVENT_LOOP_TREE_RANK,\n    FuriEventLoopObject*, /* pointer to object we track */\n    M_PTR_OPLIST,\n    FuriEventLoopItem*, /* pointer to the FuriEventLoopItem */\n    M_PTR_OPLIST)\n\n#define M_OPL_FuriEventLoopTree_t() BPTREE_OPLIST(FuriEventLoopTree, M_POD_OPLIST)\n\n#define FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX (2)\n\ntypedef enum {\n    FuriEventLoopFlagEvent = (1 << 0),\n    FuriEventLoopFlagStop = (1 << 1),\n    FuriEventLoopFlagTimer = (1 << 2),\n    FuriEventLoopFlagPending = (1 << 3),\n    FuriEventLoopFlagThreadFlag = (1 << 4),\n} FuriEventLoopFlag;\n\n#define FuriEventLoopFlagAll                                                   \\\n    (FuriEventLoopFlagEvent | FuriEventLoopFlagStop | FuriEventLoopFlagTimer | \\\n     FuriEventLoopFlagPending | FuriEventLoopFlagThreadFlag)\n\ntypedef enum {\n    FuriEventLoopProcessStatusComplete,\n    FuriEventLoopProcessStatusIncomplete,\n    FuriEventLoopProcessStatusFreeLater,\n} FuriEventLoopProcessStatus;\n\ntypedef enum {\n    FuriEventLoopStateStopped,\n    FuriEventLoopStateRunning,\n} FuriEventLoopState;\n\ntypedef struct {\n    FuriEventLoopPendingCallback callback;\n    void* context;\n} FuriEventLoopPendingQueueItem;\n\nLIST_DUAL_PUSH_DEF(PendingQueue, FuriEventLoopPendingQueueItem, M_POD_OPLIST)\n\nstruct FuriEventLoop {\n    // Only works if all operations are done from the same thread\n    FuriThreadId thread_id;\n\n    // Poller state\n    volatile FuriEventLoopState state;\n    volatile FuriEventLoopItem* current_item;\n\n    // Event handling\n    FuriEventLoopTree_t tree;\n    WaitingList_t waiting_list;\n\n    // Active timer list\n    TimerList_t timer_list;\n    // Timer request queue\n    TimerQueue_t timer_queue;\n    // Pending callback queue\n    PendingQueue_t pending_queue;\n    // Tick event\n    FuriEventLoopTick tick;\n\n    // Thread flags callback\n    bool are_thread_flags_subscribed;\n    FuriEventLoopThreadFlagsCallback thread_flags_callback;\n    void* thread_flags_callback_context;\n};\n"
  },
  {
    "path": "furi/core/event_loop_link_i.h",
    "content": "#pragma once\n\n#include \"event_loop.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FuriEventLoopItem FuriEventLoopItem;\n\n/* Link between Event Loop  */\n\ntypedef struct {\n    FuriEventLoopItem* item_in;\n    FuriEventLoopItem* item_out;\n} FuriEventLoopLink;\n\nvoid furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event);\n\n/* Contract between event loop and an object */\n\ntypedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(FuriEventLoopObject* object);\n\ntypedef bool (\n    *FuriEventLoopContractGetLevel)(FuriEventLoopObject* object, FuriEventLoopEvent event);\n\ntypedef struct {\n    const FuriEventLoopContractGetLink get_link;\n    const FuriEventLoopContractGetLevel get_level;\n} FuriEventLoopContract;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/event_loop_thread_flag_interface.h",
    "content": "#pragma once\n\n#include \"thread.h\"\n\n/**\n * @brief Notify `FuriEventLoop` that `furi_thread_flags_set` has been called\n * \n * @param thread_id Thread id\n */\nextern void furi_event_loop_thread_flag_callback(FuriThreadId thread_id);\n"
  },
  {
    "path": "furi/core/event_loop_tick.c",
    "content": "#include \"event_loop_i.h\"\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n#include <furi.h>\n\n/**\n * Private functions\n */\n\nstatic inline uint32_t furi_event_loop_tick_get_elapsed_time(const FuriEventLoop* instance) {\n    return xTaskGetTickCount() - instance->tick.prev_time;\n}\n\nstatic inline uint32_t furi_event_loop_tick_get_remaining_time(const FuriEventLoop* instance) {\n    const uint32_t elapsed_time = furi_event_loop_tick_get_elapsed_time(instance);\n    return elapsed_time < instance->tick.interval ? instance->tick.interval - elapsed_time : 0;\n}\n\nstatic inline bool furi_event_loop_tick_is_expired(const FuriEventLoop* instance) {\n    return furi_event_loop_tick_get_elapsed_time(instance) >= instance->tick.interval;\n}\n\n/*\n * Private tick API\n */\n\nvoid furi_event_loop_init_tick(FuriEventLoop* instance) {\n    if(instance->tick.callback) {\n        instance->tick.prev_time = xTaskGetTickCount();\n    }\n}\n\nvoid furi_event_loop_process_tick(FuriEventLoop* instance) {\n    if(instance->tick.callback && furi_event_loop_tick_is_expired(instance)) {\n        instance->tick.prev_time += instance->tick.interval;\n        instance->tick.callback(instance->tick.callback_context);\n    }\n}\n\nuint32_t furi_event_loop_get_tick_wait_time(const FuriEventLoop* instance) {\n    uint32_t wait_time = FuriWaitForever;\n\n    if(instance->tick.callback) {\n        wait_time = furi_event_loop_tick_get_remaining_time(instance);\n    }\n\n    return wait_time;\n}\n\n/*\n * Public tick API\n */\n\nvoid furi_event_loop_tick_set(\n    FuriEventLoop* instance,\n    uint32_t interval,\n    FuriEventLoopTickCallback callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    furi_check(callback ? interval > 0 : true);\n\n    instance->tick.callback = callback;\n    instance->tick.callback_context = context;\n    instance->tick.interval = interval;\n    instance->tick.prev_time = xTaskGetTickCount();\n}\n"
  },
  {
    "path": "furi/core/event_loop_tick_i.h",
    "content": "#pragma once\n\n#include \"event_loop.h\"\n\ntypedef struct {\n    uint32_t interval;\n    uint32_t prev_time;\n    FuriEventLoopTickCallback callback;\n    void* callback_context;\n} FuriEventLoopTick;\n\nvoid furi_event_loop_init_tick(FuriEventLoop* instance);\n\nvoid furi_event_loop_process_tick(FuriEventLoop* instance);\n\nuint32_t furi_event_loop_get_tick_wait_time(const FuriEventLoop* instance);\n"
  },
  {
    "path": "furi/core/event_loop_timer.c",
    "content": "#include \"event_loop_i.h\"\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n#include <furi.h>\n\n/*\n * Private functions\n */\n\nstatic inline uint32_t furi_event_loop_timer_get_elapsed_time(const FuriEventLoopTimer* timer) {\n    return xTaskGetTickCount() - timer->start_time;\n}\n\nstatic inline uint32_t\n    furi_event_loop_timer_get_remaining_time_private(const FuriEventLoopTimer* timer) {\n    const uint32_t elapsed_time = furi_event_loop_timer_get_elapsed_time(timer);\n    return elapsed_time < timer->interval ? timer->interval - elapsed_time : 0;\n}\n\nstatic inline bool furi_event_loop_timer_is_expired(const FuriEventLoopTimer* timer) {\n    return furi_event_loop_timer_get_elapsed_time(timer) >= timer->interval;\n}\n\nstatic void furi_event_loop_schedule_timer(FuriEventLoop* instance, FuriEventLoopTimer* timer) {\n    FuriEventLoopTimer* timer_pos = NULL;\n\n    FURI_CRITICAL_ENTER();\n\n    const uint32_t remaining_time = furi_event_loop_timer_get_remaining_time_private(timer);\n\n    TimerList_it_t it;\n    for(TimerList_it_last(it, instance->timer_list); !TimerList_end_p(it);\n        TimerList_previous(it)) {\n        FuriEventLoopTimer* tmp = TimerList_ref(it);\n        if(remaining_time >= furi_event_loop_timer_get_remaining_time_private(tmp)) {\n            timer_pos = tmp;\n            break;\n        }\n    }\n\n    FURI_CRITICAL_EXIT();\n\n    if(timer_pos) {\n        TimerList_push_after(timer_pos, timer);\n    } else {\n        TimerList_push_front(instance->timer_list, timer);\n    }\n    // At this point, TimerList_front() points to the first timer to expire\n}\n\nstatic void furi_event_loop_timer_enqueue_request(\n    FuriEventLoopTimer* timer,\n    FuriEventLoopTimerRequest request) {\n    if(timer->request != FuriEventLoopTimerRequestNone) {\n        // You cannot change your mind after calling furi_event_loop_timer_free()\n        furi_check(timer->request != FuriEventLoopTimerRequestFree);\n        TimerQueue_unlink(timer);\n    }\n\n    timer->request = request;\n\n    FuriEventLoop* instance = timer->owner;\n    TimerQueue_push_back(instance->timer_queue, timer);\n\n    xTaskNotifyIndexed(\n        (TaskHandle_t)instance->thread_id,\n        FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,\n        FuriEventLoopFlagTimer,\n        eSetBits);\n}\n\n/*\n * Private API\n */\n\nuint32_t furi_event_loop_get_timer_wait_time(const FuriEventLoop* instance) {\n    uint32_t wait_time = FuriWaitForever;\n\n    if(!TimerList_empty_p(instance->timer_list)) {\n        FuriEventLoopTimer* timer = TimerList_front(instance->timer_list);\n        wait_time = furi_event_loop_timer_get_remaining_time_private(timer);\n    }\n\n    return wait_time;\n}\n\nvoid furi_event_loop_process_timer_queue(FuriEventLoop* instance) {\n    while(!TimerQueue_empty_p(instance->timer_queue)) {\n        FuriEventLoopTimer* timer = TimerQueue_pop_front(instance->timer_queue);\n\n        if(timer->active) {\n            TimerList_unlink(timer);\n        }\n\n        if(timer->request == FuriEventLoopTimerRequestStart) {\n            timer->active = true;\n            timer->interval = timer->next_interval;\n            timer->start_time = xTaskGetTickCount();\n            timer->request = FuriEventLoopTimerRequestNone;\n\n            furi_event_loop_schedule_timer(instance, timer);\n\n        } else if(timer->request == FuriEventLoopTimerRequestStop) {\n            timer->active = false;\n            timer->request = FuriEventLoopTimerRequestNone;\n\n        } else if(timer->request == FuriEventLoopTimerRequestFree) {\n            free(timer);\n\n        } else {\n            furi_crash();\n        }\n    }\n}\n\nbool furi_event_loop_process_expired_timers(FuriEventLoop* instance) {\n    if(TimerList_empty_p(instance->timer_list)) {\n        return false;\n    }\n    // The front() element contains the earliest-expiring timer\n    FuriEventLoopTimer* timer = TimerList_front(instance->timer_list);\n\n    if(!furi_event_loop_timer_is_expired(timer)) {\n        return false;\n    }\n\n    TimerList_unlink(timer);\n\n    if(timer->periodic) {\n        const uint32_t num_events =\n            furi_event_loop_timer_get_elapsed_time(timer) / timer->interval;\n\n        timer->start_time += timer->interval * num_events;\n        furi_event_loop_schedule_timer(instance, timer);\n\n    } else {\n        timer->active = false;\n    }\n\n    timer->callback(timer->context);\n    return true;\n}\n\n/*\n * Public timer API\n */\n\nFuriEventLoopTimer* furi_event_loop_timer_alloc(\n    FuriEventLoop* instance,\n    FuriEventLoopTimerCallback callback,\n    FuriEventLoopTimerType type,\n    void* context) {\n    furi_check(instance);\n    furi_check(instance->thread_id == furi_thread_get_current_id());\n    furi_check(callback);\n    furi_check(type <= FuriEventLoopTimerTypePeriodic);\n\n    FuriEventLoopTimer* timer = malloc(sizeof(FuriEventLoopTimer));\n\n    timer->owner = instance;\n    timer->callback = callback;\n    timer->context = context;\n    timer->periodic = (type == FuriEventLoopTimerTypePeriodic);\n\n    TimerList_init_field(timer);\n    TimerQueue_init_field(timer);\n\n    return timer;\n}\n\nvoid furi_event_loop_timer_free(FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    furi_check(timer->owner->thread_id == furi_thread_get_current_id());\n\n    furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestFree);\n}\n\nvoid furi_event_loop_timer_start(FuriEventLoopTimer* timer, uint32_t interval) {\n    furi_check(timer);\n    furi_check(timer->owner->thread_id == furi_thread_get_current_id());\n\n    timer->next_interval = interval;\n\n    furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStart);\n}\n\nvoid furi_event_loop_timer_restart(FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    furi_check(timer->owner->thread_id == furi_thread_get_current_id());\n\n    timer->next_interval = timer->interval;\n\n    furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStart);\n}\n\nvoid furi_event_loop_timer_stop(FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    furi_check(timer->owner->thread_id == furi_thread_get_current_id());\n\n    furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStop);\n}\n\nuint32_t furi_event_loop_timer_get_remaining_time(const FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    return furi_event_loop_timer_get_remaining_time_private(timer);\n}\n\nuint32_t furi_event_loop_timer_get_interval(const FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    return timer->interval;\n}\n\nbool furi_event_loop_timer_is_running(const FuriEventLoopTimer* timer) {\n    furi_check(timer);\n    return timer->active;\n}\n"
  },
  {
    "path": "furi/core/event_loop_timer.h",
    "content": "/**\n * @file event_loop_timer.h\n * @brief Software timer functionality for FuriEventLoop.\n *\n * @warning ALL FuriEventLoopTimer functions MUST be called from the\n * same thread that the owner FuriEventLoop instance was created in.\n */\n\n#pragma once\n\n#include \"event_loop.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Enumeration of possible timer types.\n */\ntypedef enum {\n    FuriEventLoopTimerTypeOnce = 0, /**< One-shot timer. */\n    FuriEventLoopTimerTypePeriodic = 1, /**< Repeating timer. */\n} FuriEventLoopTimerType;\n\n/**\n * @brief Timer callback type for functions to be called when a timer expires.\n *\n * In the timer callback, it is ALLOWED:\n * - To start, stop, or restart an existing timer,\n * - To create new timers using furi_event_loop_timer_alloc(),\n * - To delete timers using furi_event_loop_timer_free().\n *\n * @param[in,out] context pointer to a user-specific object that was provided during timer creation\n */\ntypedef void (*FuriEventLoopTimerCallback)(void* context);\n\n/**\n * @brief Opaque event loop timer type.\n */\ntypedef struct FuriEventLoopTimer FuriEventLoopTimer;\n\n/**\n * @brief Create a new event loop timer instance.\n *\n * @param[in,out] instance pointer to the current FuriEventLoop instance\n * @param[in] callback pointer to the callback function to be executed upon timer timeout\n * @param[in] type timer type value to determine its behavior (single-shot or periodic)\n * @param[in,out] context pointer to a user-specific object (will be passed to the callback)\n * @returns pointer to the created timer instance\n */\nFuriEventLoopTimer* furi_event_loop_timer_alloc(\n    FuriEventLoop* instance,\n    FuriEventLoopTimerCallback callback,\n    FuriEventLoopTimerType type,\n    void* context);\n\n/**\n * @brief Delete an event loop timer instance.\n *\n * @warning The user code MUST call furi_event_loop_timer_free() on ALL instances\n *          associated with the current event loop BEFORE calling furi_event_loop_free().\n *          The event loop may EITHER be running OR stopped when the timers are being deleted.\n *\n * @param[in,out] timer pointer to the timer instance to be deleted\n */\nvoid furi_event_loop_timer_free(FuriEventLoopTimer* timer);\n\n/**\n * @brief Start a timer or restart it with a new interval.\n *\n * @param[in,out] timer pointer to the timer instance to be (re)started\n * @param[in] interval timer interval in ticks\n */\nvoid furi_event_loop_timer_start(FuriEventLoopTimer* timer, uint32_t interval);\n\n/**\n * @brief Restart a timer with the previously set interval.\n *\n * @param[in,out] timer pointer to the timer instance to be restarted\n */\nvoid furi_event_loop_timer_restart(FuriEventLoopTimer* timer);\n\n/**\n * @brief Stop a timer without firing its callback.\n *\n * It is safe to call this function on an already stopped timer (it will do nothing).\n *\n * @param[in,out] timer pointer to the timer instance to be stopped\n */\nvoid furi_event_loop_timer_stop(FuriEventLoopTimer* timer);\n\n/**\n * @brief Get the time remaining before the timer becomes expires.\n *\n * For stopped or expired timers, this function returns 0.\n *\n * @param[in] timer pointer to the timer to be queried\n * @returns remaining time in ticks\n */\nuint32_t furi_event_loop_timer_get_remaining_time(const FuriEventLoopTimer* timer);\n\n/**\n * @brief Get the timer interval.\n *\n * @param[in] timer pointer to the timer to be queried\n * @returns timer interval in ticks\n */\nuint32_t furi_event_loop_timer_get_interval(const FuriEventLoopTimer* timer);\n\n/**\n * @brief Check if the timer is currently running.\n *\n * A timer is considered running if it has not expired yet.\n * @param[in] timer pointer to the timer to be queried\n * @returns true if the timer is running, false otherwise\n */\nbool furi_event_loop_timer_is_running(const FuriEventLoopTimer* timer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/event_loop_timer_i.h",
    "content": "#pragma once\n\n#include \"event_loop_timer.h\"\n\n#include <m-i-list.h>\n\ntypedef enum {\n    FuriEventLoopTimerRequestNone,\n    FuriEventLoopTimerRequestStart,\n    FuriEventLoopTimerRequestStop,\n    FuriEventLoopTimerRequestFree,\n} FuriEventLoopTimerRequest;\n\nstruct FuriEventLoopTimer {\n    FuriEventLoop* owner;\n\n    FuriEventLoopTimerCallback callback;\n    void* context;\n\n    uint32_t interval;\n    uint32_t start_time;\n    uint32_t next_interval;\n\n    // Interface for the active timer list\n    ILIST_INTERFACE(TimerList, FuriEventLoopTimer);\n\n    // Interface for the timer request queue\n    ILIST_INTERFACE(TimerQueue, FuriEventLoopTimer);\n\n    FuriEventLoopTimerRequest request;\n\n    bool active;\n    bool periodic;\n};\n\nILIST_DEF(TimerList, FuriEventLoopTimer, M_POD_OPLIST)\nILIST_DEF(TimerQueue, FuriEventLoopTimer, M_POD_OPLIST)\n\nuint32_t furi_event_loop_get_timer_wait_time(const FuriEventLoop* instance);\n\nvoid furi_event_loop_process_timer_queue(FuriEventLoop* instance);\n\nbool furi_event_loop_process_expired_timers(FuriEventLoop* instance);\n"
  },
  {
    "path": "furi/core/kernel.c",
    "content": "#include \"kernel.h\"\n#include \"base.h\"\n#include \"check.h\"\n#include \"common_defines.h\"\n\n#include <furi_hal.h>\n\n#include <FreeRTOS.h>\n#include <task.h>\n\nbool furi_kernel_is_irq_or_masked(void) {\n    bool irq = false;\n    BaseType_t state;\n\n    if(FURI_IS_IRQ_MODE()) {\n        /* Called from interrupt context */\n        irq = true;\n    } else {\n        /* Get FreeRTOS scheduler state */\n        state = xTaskGetSchedulerState();\n\n        if(state != taskSCHEDULER_NOT_STARTED) {\n            /* Scheduler was started */\n            if(FURI_IS_IRQ_MASKED()) {\n                /* Interrupts are masked */\n                irq = true;\n            }\n        }\n    }\n\n    /* Return context, 0: thread context, 1: IRQ context */\n    return irq;\n}\n\nbool furi_kernel_is_running(void) {\n    return xTaskGetSchedulerState() == taskSCHEDULER_RUNNING;\n}\n\nint32_t furi_kernel_lock(void) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n\n    int32_t lock;\n\n    switch(xTaskGetSchedulerState()) {\n    case taskSCHEDULER_SUSPENDED:\n        lock = 1;\n        break;\n\n    case taskSCHEDULER_RUNNING:\n        vTaskSuspendAll();\n        lock = 0;\n        break;\n\n    case taskSCHEDULER_NOT_STARTED:\n    default:\n        lock = (int32_t)FuriStatusError;\n        break;\n    }\n\n    /* Return previous lock state */\n    return lock;\n}\n\nint32_t furi_kernel_unlock(void) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n\n    int32_t lock;\n\n    switch(xTaskGetSchedulerState()) {\n    case taskSCHEDULER_SUSPENDED:\n        lock = 1;\n\n        if(xTaskResumeAll() != pdTRUE) {\n            if(xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {\n                lock = (int32_t)FuriStatusError;\n            }\n        }\n        break;\n\n    case taskSCHEDULER_RUNNING:\n        lock = 0;\n        break;\n\n    case taskSCHEDULER_NOT_STARTED:\n    default:\n        lock = (int32_t)FuriStatusError;\n        break;\n    }\n\n    /* Return previous lock state */\n    return lock;\n}\n\nint32_t furi_kernel_restore_lock(int32_t lock) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n\n    switch(xTaskGetSchedulerState()) {\n    case taskSCHEDULER_SUSPENDED:\n    case taskSCHEDULER_RUNNING:\n        if(lock == 1) {\n            vTaskSuspendAll();\n        } else {\n            if(lock != 0) {\n                lock = (int32_t)FuriStatusError;\n            } else {\n                if(xTaskResumeAll() != pdTRUE) {\n                    if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {\n                        lock = (int32_t)FuriStatusError;\n                    }\n                }\n            }\n        }\n        break;\n\n    case taskSCHEDULER_NOT_STARTED:\n    default:\n        lock = (int32_t)FuriStatusError;\n        break;\n    }\n\n    /* Return new lock state */\n    return lock;\n}\n\nuint32_t furi_kernel_get_tick_frequency(void) {\n    /* Return frequency in hertz */\n    return configTICK_RATE_HZ_RAW;\n}\n\nvoid furi_delay_tick(uint32_t ticks) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(furi_thread_get_current_id() != xTaskGetIdleTaskHandle());\n\n    if(ticks == 0U) {\n        taskYIELD();\n    } else {\n        vTaskDelay(ticks);\n    }\n}\n\nFuriStatus furi_delay_until_tick(uint32_t tick) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(furi_thread_get_current_id() != xTaskGetIdleTaskHandle());\n\n    TickType_t tcnt, delay;\n    FuriStatus stat;\n\n    stat = FuriStatusOk;\n    tcnt = xTaskGetTickCount();\n\n    /* Determine remaining number of tick to delay */\n    delay = (TickType_t)tick - tcnt;\n\n    /* Check if target tick has not expired */\n    if((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {\n        if(xTaskDelayUntil(&tcnt, delay) == pdFALSE) {\n            /* Did not delay */\n            stat = FuriStatusError;\n        }\n    } else {\n        /* No delay or already expired */\n        stat = FuriStatusErrorParameter;\n    }\n\n    /* Return execution status */\n    return stat;\n}\n\nuint32_t furi_get_tick(void) {\n    TickType_t ticks;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        ticks = xTaskGetTickCountFromISR();\n    } else {\n        ticks = xTaskGetTickCount();\n    }\n\n    return ticks;\n}\n\nuint32_t furi_ms_to_ticks(uint32_t milliseconds) {\n#if configTICK_RATE_HZ_RAW == 1000\n    return milliseconds;\n#else\n    return (uint32_t)((float)configTICK_RATE_HZ_RAW) / 1000.0f * (float)milliseconds;\n#endif\n}\n\nvoid furi_delay_ms(uint32_t milliseconds) {\n    if(!FURI_IS_ISR() && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {\n        if(milliseconds > 0 && milliseconds < portMAX_DELAY - 1) {\n            milliseconds += 1;\n        }\n#if configTICK_RATE_HZ_RAW == 1000\n        furi_delay_tick(milliseconds);\n#else\n        furi_delay_tick(furi_ms_to_ticks(milliseconds));\n#endif\n    } else if(milliseconds > 0) {\n        furi_delay_us(milliseconds * 1000);\n    }\n}\n\nvoid furi_delay_us(uint32_t microseconds) {\n    furi_hal_cortex_delay_us(microseconds);\n}\n"
  },
  {
    "path": "furi/core/kernel.h",
    "content": "/**\n * @file kernel.h\n * Furi Kernel primitives\n */\n#pragma once\n\n#include <core/base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Check if CPU is in IRQ or kernel running and IRQ is masked\n * \n * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK.\n * \n * Meaningful use cases are:\n * \n * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section)\n * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK.\n * \n * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior.\n * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel.\n * It's up to you to figure out if it is safe for your code or not.\n * \n * @return     true if CPU is in IRQ or kernel running and IRQ is masked\n */\nbool furi_kernel_is_irq_or_masked(void);\n\n/** Check if kernel is running\n *\n * @return     true if running, false otherwise\n */\nbool furi_kernel_is_running(void);\n\n/** Lock kernel, pause process scheduling\n *\n * @warning This should never be called in interrupt request context.\n *\n * @return     previous lock state(0 - unlocked, 1 - locked)\n */\nint32_t furi_kernel_lock(void);\n\n/** Unlock kernel, resume process scheduling\n *\n * @warning This should never be called in interrupt request context.\n *\n * @return     previous lock state(0 - unlocked, 1 - locked)\n */\nint32_t furi_kernel_unlock(void);\n\n/** Restore kernel lock state\n *\n * @warning This should never be called in interrupt request context.\n *\n * @param[in]  lock  The lock state\n *\n * @return     new lock state or error\n */\nint32_t furi_kernel_restore_lock(int32_t lock);\n\n/** Get kernel systick frequency\n *\n * @return     systick counts per second\n */\nuint32_t furi_kernel_get_tick_frequency(void);\n\n/** Delay execution\n *\n * @warning This should never be called in interrupt request context.\n *\n * Also keep in mind delay is aliased to scheduler timer intervals.\n *\n * @param[in]  ticks  The ticks count to pause\n */\nvoid furi_delay_tick(uint32_t ticks);\n\n/** Delay until tick\n *\n * @warning This should never be called in interrupt request context.\n *\n * @param[in]  tick  The tick until which kernel should delay task execution\n *\n * @return     The furi status.\n */\nFuriStatus furi_delay_until_tick(uint32_t tick);\n\n/** Get current tick counter\n *\n * System uptime, may overflow.\n *\n * @return     Current ticks in milliseconds\n */\nuint32_t furi_get_tick(void);\n\n/** Convert milliseconds to ticks\n *\n * @param[in]   milliseconds    time in milliseconds\n * @return      time in ticks\n */\nuint32_t furi_ms_to_ticks(uint32_t milliseconds);\n\n/** Delay in milliseconds\n * \n * This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals.\n * Real wait time will be between X+ milliseconds.\n * Special value: 0, will cause task yield.\n * Also if used when kernel is not running will fall back to `furi_delay_us`.\n * \n * @warning    Cannot be used from ISR\n *\n * @param[in]  milliseconds  milliseconds to wait\n */\nvoid furi_delay_ms(uint32_t milliseconds);\n\n/** Delay in microseconds\n * \n * Implemented using Cortex DWT counter. Blocking and non aliased.\n *\n * @param[in]  microseconds  microseconds to wait\n */\nvoid furi_delay_us(uint32_t microseconds);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/log.c",
    "content": "#include \"log.h\"\n#include \"check.h\"\n#include \"mutex.h\"\n#include <furi_hal.h>\n#include <m-list.h>\n\nLIST_DEF(FuriLogHandlersList, FuriLogHandler, M_POD_OPLIST)\n\n#define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo\n\ntypedef struct {\n    FuriLogLevel log_level;\n    FuriMutex* mutex;\n    FuriLogHandlersList_t tx_handlers;\n} FuriLogParams;\n\nstatic FuriLogParams furi_log = {0};\n\ntypedef struct {\n    const char* str;\n    FuriLogLevel level;\n} FuriLogLevelDescription;\n\nstatic const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = {\n    {\"default\", FuriLogLevelDefault},\n    {\"none\", FuriLogLevelNone},\n    {\"error\", FuriLogLevelError},\n    {\"warn\", FuriLogLevelWarn},\n    {\"info\", FuriLogLevelInfo},\n    {\"debug\", FuriLogLevelDebug},\n    {\"trace\", FuriLogLevelTrace},\n};\n\nvoid furi_log_init(void) {\n    // Set default logging parameters\n    furi_log.log_level = FURI_LOG_LEVEL_DEFAULT;\n    furi_log.mutex = furi_mutex_alloc(FuriMutexTypeRecursive);\n    FuriLogHandlersList_init(furi_log.tx_handlers);\n}\n\nbool furi_log_add_handler(FuriLogHandler handler) {\n    furi_check(handler.callback);\n\n    bool ret = true;\n\n    furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);\n\n    FuriLogHandlersList_it_t it;\n    FuriLogHandlersList_it(it, furi_log.tx_handlers);\n    while(!FuriLogHandlersList_end_p(it)) {\n        if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) {\n            ret = false;\n        } else {\n            FuriLogHandlersList_next(it);\n        }\n    }\n\n    if(ret) {\n        FuriLogHandlersList_push_back(furi_log.tx_handlers, handler);\n    }\n\n    furi_mutex_release(furi_log.mutex);\n\n    return ret;\n}\n\nbool furi_log_remove_handler(FuriLogHandler handler) {\n    bool ret = false;\n\n    furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);\n\n    FuriLogHandlersList_it_t it;\n    FuriLogHandlersList_it(it, furi_log.tx_handlers);\n    while(!FuriLogHandlersList_end_p(it)) {\n        if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) {\n            FuriLogHandlersList_remove(furi_log.tx_handlers, it);\n            ret = true;\n        } else {\n            FuriLogHandlersList_next(it);\n        }\n    }\n\n    furi_mutex_release(furi_log.mutex);\n\n    return ret;\n}\n\nvoid furi_log_tx(const uint8_t* data, size_t size) {\n    if(!FURI_IS_ISR()) {\n        furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);\n    } else {\n        if(furi_mutex_get_owner(furi_log.mutex)) return;\n    }\n\n    FuriLogHandlersList_it_t it;\n    FuriLogHandlersList_it(it, furi_log.tx_handlers);\n    while(!FuriLogHandlersList_end_p(it)) {\n        FuriLogHandlersList_ref(it)->callback(data, size, FuriLogHandlersList_ref(it)->context);\n        FuriLogHandlersList_next(it);\n    }\n\n    if(!FURI_IS_ISR()) furi_mutex_release(furi_log.mutex);\n}\n\nvoid furi_log_puts(const char* data) {\n    furi_check(data);\n    furi_log_tx((const uint8_t*)data, strlen(data));\n}\n\nvoid furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) {\n    do {\n        if(level > furi_log.log_level) {\n            break;\n        }\n\n        if(furi_mutex_acquire(furi_log.mutex, furi_kernel_is_running() ? FuriWaitForever : 0) !=\n           FuriStatusOk) {\n            break;\n        }\n\n        FuriString* string = furi_string_alloc();\n\n        const char* color = _FURI_LOG_CLR_RESET;\n        const char* log_letter = \" \";\n        switch(level) {\n        case FuriLogLevelError:\n            color = _FURI_LOG_CLR_E;\n            log_letter = \"E\";\n            break;\n        case FuriLogLevelWarn:\n            color = _FURI_LOG_CLR_W;\n            log_letter = \"W\";\n            break;\n        case FuriLogLevelInfo:\n            color = _FURI_LOG_CLR_I;\n            log_letter = \"I\";\n            break;\n        case FuriLogLevelDebug:\n            color = _FURI_LOG_CLR_D;\n            log_letter = \"D\";\n            break;\n        case FuriLogLevelTrace:\n            color = _FURI_LOG_CLR_T;\n            log_letter = \"T\";\n            break;\n        default:\n            break;\n        }\n\n        // Timestamp\n        furi_string_printf(\n            string, \"%lu %s[%s][%s] \" _FURI_LOG_CLR_RESET, furi_get_tick(), color, log_letter, tag);\n        furi_log_puts(furi_string_get_cstr(string));\n        furi_string_reset(string);\n\n        va_list args;\n        va_start(args, format);\n        furi_string_vprintf(string, format, args);\n        va_end(args);\n\n        furi_log_puts(furi_string_get_cstr(string));\n        furi_string_free(string);\n\n        furi_log_puts(\"\\r\\n\");\n\n        furi_mutex_release(furi_log.mutex);\n    } while(0);\n}\n\nvoid furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) {\n    if(level <= furi_log.log_level &&\n       furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) {\n        FuriString* string;\n        string = furi_string_alloc();\n        va_list args;\n        va_start(args, format);\n        furi_string_vprintf(string, format, args);\n        va_end(args);\n\n        furi_log_puts(furi_string_get_cstr(string));\n        furi_string_free(string);\n\n        furi_mutex_release(furi_log.mutex);\n    }\n}\n\nvoid furi_log_set_level(FuriLogLevel level) {\n    furi_check(level <= FuriLogLevelTrace);\n\n    if(level == FuriLogLevelDefault) {\n        level = FURI_LOG_LEVEL_DEFAULT;\n    }\n    furi_log.log_level = level;\n}\n\nFuriLogLevel furi_log_get_level(void) {\n    return furi_log.log_level;\n}\n\nbool furi_log_level_to_string(FuriLogLevel level, const char** str) {\n    for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) {\n        if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) {\n            *str = FURI_LOG_LEVEL_DESCRIPTIONS[i].str;\n            return true;\n        }\n    }\n    return false;\n}\n\nbool furi_log_level_from_string(const char* str, FuriLogLevel* level) {\n    for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) {\n        if(strcmp(str, FURI_LOG_LEVEL_DESCRIPTIONS[i].str) == 0) {\n            *level = FURI_LOG_LEVEL_DESCRIPTIONS[i].level;\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "furi/core/log.h",
    "content": "/**\n * @file log.h\n * Furi Logging system\n */\n#pragma once\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdarg.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    FuriLogLevelDefault = 0,\n    FuriLogLevelNone = 1,\n    FuriLogLevelError = 2,\n    FuriLogLevelWarn = 3,\n    FuriLogLevelInfo = 4,\n    FuriLogLevelDebug = 5,\n    FuriLogLevelTrace = 6,\n} FuriLogLevel;\n\n#define _FURI_LOG_CLR(clr)  \"\\033[0;\" clr \"m\"\n#define _FURI_LOG_CLR_RESET \"\\033[0m\"\n\n#define _FURI_LOG_CLR_BLACK  \"30\"\n#define _FURI_LOG_CLR_RED    \"31\"\n#define _FURI_LOG_CLR_GREEN  \"32\"\n#define _FURI_LOG_CLR_BROWN  \"33\"\n#define _FURI_LOG_CLR_BLUE   \"34\"\n#define _FURI_LOG_CLR_PURPLE \"35\"\n\n#define _FURI_LOG_CLR_E _FURI_LOG_CLR(_FURI_LOG_CLR_RED)\n#define _FURI_LOG_CLR_W _FURI_LOG_CLR(_FURI_LOG_CLR_BROWN)\n#define _FURI_LOG_CLR_I _FURI_LOG_CLR(_FURI_LOG_CLR_GREEN)\n#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE)\n#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE)\n\ntypedef void (*FuriLogHandlerCallback)(const uint8_t* data, size_t size, void* context);\n\ntypedef struct {\n    FuriLogHandlerCallback callback;\n    void* context;\n} FuriLogHandler;\n\n/** Initialize logging */\nvoid furi_log_init(void);\n\n/** Add log TX callback\n *\n * @param[in]  handler  The callback and its context\n *\n * @return     true on success, false otherwise\n */\nbool furi_log_add_handler(FuriLogHandler handler);\n\n/** Remove log TX callback\n *\n * @param[in]  handler  The callback and its context\n *\n * @return     true on success, false otherwise\n */\nbool furi_log_remove_handler(FuriLogHandler handler);\n\n/** Transmit data through log IO callbacks\n *\n * @param[in]  data  The data\n * @param[in]  size  The size\n */\nvoid furi_log_tx(const uint8_t* data, size_t size);\n\n/** Transmit data through log IO callbacks\n *\n * @param[in]  data  The data, null-terminated C-string\n */\nvoid furi_log_puts(const char* data);\n\n/** Print log record\n * \n * @param level \n * @param tag \n * @param format \n * @param ... \n */\nvoid furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 3, 4)));\n\n/** Print log record\n * \n * @param level \n * @param format \n * @param ... \n */\nvoid furi_log_print_raw_format(FuriLogLevel level, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/** Set log level\n *\n * @param[in]  level  The level\n */\nvoid furi_log_set_level(FuriLogLevel level);\n\n/** Get log level\n *\n * @return     The furi log level.\n */\nFuriLogLevel furi_log_get_level(void);\n\n/** Log level to string\n *\n * @param[in]  level  The level\n * @param[out] str    String representation of the level\n *\n * @return     True if success, False otherwise\n */\nbool furi_log_level_to_string(FuriLogLevel level, const char** str);\n\n/** Log level from string\n *\n * @param[in]  str    The string\n * @param[out] level  The level\n * \n * @return     True if success, False otherwise\n */\nbool furi_log_level_from_string(const char* str, FuriLogLevel* level);\n\n/** Log methods\n *\n * @param      tag     The application tag\n * @param      format  The format\n * @param      ...     VA Args\n */\n#define FURI_LOG_E(tag, format, ...) \\\n    furi_log_print_format(FuriLogLevelError, tag, format, ##__VA_ARGS__)\n#define FURI_LOG_W(tag, format, ...) \\\n    furi_log_print_format(FuriLogLevelWarn, tag, format, ##__VA_ARGS__)\n#define FURI_LOG_I(tag, format, ...) \\\n    furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__)\n#define FURI_LOG_D(tag, format, ...) \\\n    furi_log_print_format(FuriLogLevelDebug, tag, format, ##__VA_ARGS__)\n#define FURI_LOG_T(tag, format, ...) \\\n    furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__)\n\n/** Log methods\n *\n * @param      format  The raw format \n * @param      ...     VA Args\n */\n#define FURI_LOG_RAW_E(format, ...) \\\n    furi_log_print_raw_format(FuriLogLevelError, format, ##__VA_ARGS__)\n#define FURI_LOG_RAW_W(format, ...) \\\n    furi_log_print_raw_format(FuriLogLevelWarn, format, ##__VA_ARGS__)\n#define FURI_LOG_RAW_I(format, ...) \\\n    furi_log_print_raw_format(FuriLogLevelInfo, format, ##__VA_ARGS__)\n#define FURI_LOG_RAW_D(format, ...) \\\n    furi_log_print_raw_format(FuriLogLevelDebug, format, ##__VA_ARGS__)\n#define FURI_LOG_RAW_T(format, ...) \\\n    furi_log_print_raw_format(FuriLogLevelTrace, format, ##__VA_ARGS__)\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/memmgr.c",
    "content": "#include \"memmgr.h\"\n#include <string.h>\n#include <furi_hal_memory.h>\n#include <FreeRTOS.h>\n\nextern void* pvPortMalloc(size_t xSize);\nextern void vPortFree(void* pv);\nextern size_t xPortGetFreeHeapSize(void);\nextern size_t xPortGetTotalHeapSize(void);\nextern size_t xPortGetMinimumEverFreeHeapSize(void);\n\nvoid* malloc(size_t size) {\n    return pvPortMalloc(size);\n}\n\nvoid free(void* ptr) {\n    vPortFree(ptr);\n}\n\nvoid* realloc(void* ptr, size_t size) {\n    if(size == 0) {\n        vPortFree(ptr);\n        return NULL;\n    }\n\n    void* p = pvPortMalloc(size);\n    if(ptr != NULL) {\n        memcpy(p, ptr, size);\n        vPortFree(ptr);\n    }\n\n    return p;\n}\n\nvoid* calloc(size_t count, size_t size) {\n    return pvPortMalloc(count * size);\n}\n\nchar* strdup(const char* s) {\n    // arg s marked as non-null, so we need hack to check for NULL\n    furi_check(((uint32_t)s << 2) != 0);\n\n    size_t siz = strlen(s) + 1;\n    char* y = pvPortMalloc(siz);\n    memcpy(y, s, siz);\n\n    return y;\n}\n\nsize_t memmgr_get_free_heap(void) {\n    return xPortGetFreeHeapSize();\n}\n\nsize_t memmgr_get_total_heap(void) {\n    return configTOTAL_HEAP_SIZE;\n}\n\nsize_t memmgr_get_minimum_free_heap(void) {\n    return xPortGetMinimumEverFreeHeapSize();\n}\n\nvoid* __wrap__malloc_r(struct _reent* r, size_t size) {\n    UNUSED(r);\n    return pvPortMalloc(size);\n}\n\nvoid __wrap__free_r(struct _reent* r, void* ptr) {\n    UNUSED(r);\n    vPortFree(ptr);\n}\n\nvoid* __wrap__calloc_r(struct _reent* r, size_t count, size_t size) {\n    UNUSED(r);\n    return calloc(count, size);\n}\n\nvoid* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) {\n    UNUSED(r);\n    return realloc(ptr, size);\n}\n\nvoid* memmgr_alloc_from_pool(size_t size) {\n    void* p = furi_hal_memory_alloc(size);\n    if(p == NULL) p = malloc(size);\n\n    return p;\n}\n\nsize_t memmgr_pool_get_free(void) {\n    return furi_hal_memory_get_free();\n}\n\nsize_t memmgr_pool_get_max_block(void) {\n    return furi_hal_memory_max_pool_block();\n}\n\nvoid* aligned_malloc(size_t size, size_t alignment) {\n    void* p1; // original block\n    void** p2; // aligned block\n    int offset = alignment - 1 + sizeof(void*);\n    if((p1 = (void*)malloc(size + offset)) == NULL) {\n        return NULL;\n    }\n    p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1));\n    p2[-1] = p1;\n    return p2;\n}\n\nvoid aligned_free(void* p) {\n    if(p) {\n        free(((void**)p)[-1]);\n    }\n}\n"
  },
  {
    "path": "furi/core/memmgr.h",
    "content": "/**\n * @file memmgr.h\n * Furi: memory management API and glue\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"check.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// define for test case \"link against furi memmgr\"\n#define FURI_MEMMGR_GUARD 1\n\n/** Get free heap size\n *\n * @return     free heap size in bytes\n */\nsize_t memmgr_get_free_heap(void);\n\n/** Get total heap size\n *\n * @return     total heap size in bytes\n */\nsize_t memmgr_get_total_heap(void);\n\n/** Get heap watermark\n *\n * @return     minimum heap in bytes\n */\nsize_t memmgr_get_minimum_free_heap(void);\n\n/**\n * An aligned version of malloc, used when you need to get the aligned space on the heap\n * Freeing the received address is performed ONLY through the aligned_free function\n * @param size \n * @param alignment \n * @return void* \n */\nvoid* aligned_malloc(size_t size, size_t alignment);\n\n/**\n * Freed space obtained through the aligned_malloc function\n * @param p pointer to result of aligned_malloc\n */\nvoid aligned_free(void* p);\n\n/**\n * @brief Allocate memory from separate memory pool. That memory can't be freed.\n * \n * @param size \n * @return void* \n */\nvoid* memmgr_alloc_from_pool(size_t size);\n\n/**\n * @brief Get free memory pool size\n * \n * @return size_t \n */\nsize_t memmgr_pool_get_free(void);\n\n/**\n * @brief Get max free block size from memory pool\n * \n * @return size_t \n */\nsize_t memmgr_pool_get_max_block(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/memmgr_heap.c",
    "content": "/*\n * FreeRTOS Kernel V11.1.0\n * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of\n * this software and associated documentation files (the \"Software\"), to deal in\n * the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n * the Software, and to permit persons to whom the Software is furnished to do so,\n * subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n *\n * https://www.FreeRTOS.org\n * https://github.com/FreeRTOS\n *\n */\n\n/*\n * A sample implementation of pvPortMalloc() and vPortFree() that combines\n * (coalescences) adjacent memory blocks as they are freed, and in so doing\n * limits memory fragmentation.\n *\n * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the\n * memory management pages of https://www.FreeRTOS.org for more information.\n */\n\n#include \"memmgr_heap.h\"\n#include \"check.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <stm32wbxx.h>\n#include <stm32wb55_linker.h>\n#include <core/log.h>\n#include <core/common_defines.h>\n\n// -V::562\n// -V::650\n\n/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining\n * all the API functions to use the MPU wrappers.  That should only be done when\n * task.h is included from an application file. */\n#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE\n\n#include <FreeRTOS.h>\n#include <task.h>\n\n#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE\n\n#if(configSUPPORT_DYNAMIC_ALLOCATION == 0)\n#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0\n#endif\n\n#ifndef configHEAP_CLEAR_MEMORY_ON_FREE\n#define configHEAP_CLEAR_MEMORY_ON_FREE 0\n#endif\n\n/* Block sizes must not get too small. */\n#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1))\n\n/* Assumes 8bit bytes! */\n#define heapBITS_PER_BYTE ((size_t)8)\n\n/* Max value that fits in a size_t type. */\n#define heapSIZE_MAX (~((size_t)0))\n\n/* Check if multiplying a and b will result in overflow. */\n#define heapMULTIPLY_WILL_OVERFLOW(a, b) (((a) > 0) && ((b) > (heapSIZE_MAX / (a))))\n\n/* Check if adding a and b will result in overflow. */\n#define heapADD_WILL_OVERFLOW(a, b) ((a) > (heapSIZE_MAX - (b)))\n\n/* Check if the subtraction operation ( a - b ) will result in underflow. */\n#define heapSUBTRACT_WILL_UNDERFLOW(a, b) ((a) < (b))\n\n/* MSB of the xBlockSize member of an BlockLink_t structure is used to track\n * the allocation status of a block.  When MSB of the xBlockSize member of\n * an BlockLink_t structure is set then the block belongs to the application.\n * When the bit is free the block is still part of the free heap space. */\n#define heapBLOCK_ALLOCATED_BITMASK         (((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1))\n#define heapBLOCK_SIZE_IS_VALID(xBlockSize) (((xBlockSize) & heapBLOCK_ALLOCATED_BITMASK) == 0)\n#define heapBLOCK_IS_ALLOCATED(pxBlock) \\\n    (((pxBlock->xBlockSize) & heapBLOCK_ALLOCATED_BITMASK) != 0)\n#define heapALLOCATE_BLOCK(pxBlock) ((pxBlock->xBlockSize) |= heapBLOCK_ALLOCATED_BITMASK)\n#define heapFREE_BLOCK(pxBlock)     ((pxBlock->xBlockSize) &= ~heapBLOCK_ALLOCATED_BITMASK)\n\n/*-----------------------------------------------------------*/\n\n/* Heap start end symbols provided by linker */\nuint8_t* ucHeap = (uint8_t*)&__heap_start__;\n\n/* Define the linked list structure.  This is used to link free blocks in order\n * of their memory address. */\ntypedef struct A_BLOCK_LINK {\n    struct A_BLOCK_LINK* pxNextFreeBlock; /**< The next free block in the list. */\n    size_t xBlockSize; /**< The size of the free block. */\n} BlockLink_t;\n\n/* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers\n * protection using an application supplied canary value to catch heap\n * corruption should a heap buffer overflow occur.\n */\n#if(configENABLE_HEAP_PROTECTOR == 1)\n\n/**\n * @brief Application provided function to get a random value to be used as canary.\n *\n * @param pxHeapCanary [out] Output parameter to return the canary value.\n */\nextern void vApplicationGetRandomHeapCanary(portPOINTER_SIZE_TYPE* pxHeapCanary);\n\n/* Canary value for protecting internal heap pointers. */\nPRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary;\n\n/* Macro to load/store BlockLink_t pointers to memory. By XORing the\n * pointers with a random canary value, heap overflows will result\n * in randomly unpredictable pointer values which will be caught by\n * heapVALIDATE_BLOCK_POINTER assert. */\n#define heapPROTECT_BLOCK_POINTER(pxBlock) \\\n    ((BlockLink_t*)(((portPOINTER_SIZE_TYPE)(pxBlock)) ^ xHeapCanary))\n#else\n\n#define heapPROTECT_BLOCK_POINTER(pxBlock) (pxBlock)\n\n#endif /* configENABLE_HEAP_PROTECTOR */\n\n/* Assert that a heap block pointer is within the heap bounds. */\n#define heapVALIDATE_BLOCK_POINTER(pxBlock)      \\\n    configASSERT(                                \\\n        ((uint8_t*)(pxBlock) >= &(ucHeap[0])) && \\\n        ((uint8_t*)(pxBlock) <= &(ucHeap[configTOTAL_HEAP_SIZE - 1])))\n\n/*-----------------------------------------------------------*/\n\n/*\n * Inserts a block of memory that is being freed into the correct position in\n * the list of free memory blocks.  The block being freed will be merged with\n * the block in front it and/or the block behind it if the memory blocks are\n * adjacent to each other.\n */\nstatic void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) PRIVILEGED_FUNCTION;\n\n/*\n * Called automatically to setup the required heap structures the first time\n * pvPortMalloc() is called.\n */\nstatic void prvHeapInit(void) PRIVILEGED_FUNCTION;\n\n/*-----------------------------------------------------------*/\n\n/* The size of the structure placed at the beginning of each allocated memory\n * block must by correctly byte aligned. */\nstatic const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) &\n                                      ~((size_t)portBYTE_ALIGNMENT_MASK);\n\n/* Create a couple of list links to mark the start and end of the list. */\nPRIVILEGED_DATA static BlockLink_t xStart;\nPRIVILEGED_DATA static BlockLink_t* pxEnd = NULL;\n\n/* Keeps track of the number of calls to allocate and free memory as well as the\n * number of free bytes remaining, but says nothing about fragmentation. */\nPRIVILEGED_DATA static size_t xFreeBytesRemaining = (size_t)0U;\nPRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = (size_t)0U;\nPRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = (size_t)0U;\nPRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = (size_t)0U;\n\n/* Furi heap extension */\n#include <m-dict.h>\n\n/* Allocation tracking types */\nDICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048\n\nDICT_DEF2( //-V1048\n    MemmgrHeapThreadDict,\n    uint32_t,\n    M_DEFAULT_OPLIST,\n    MemmgrHeapAllocDict_t,\n    DICT_OPLIST(MemmgrHeapAllocDict))\n\n/* Thread allocation tracing storage */\nstatic MemmgrHeapThreadDict_t memmgr_heap_thread_dict = {0};\nstatic volatile uint32_t memmgr_heap_thread_trace_depth = 0;\n\n/* Initialize tracing storage on start */\nvoid memmgr_heap_init(void) {\n    MemmgrHeapThreadDict_init(memmgr_heap_thread_dict);\n}\n\nvoid memmgr_heap_enable_thread_trace(FuriThreadId thread_id) {\n    vTaskSuspendAll();\n    {\n        memmgr_heap_thread_trace_depth++;\n        furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) == NULL);\n        MemmgrHeapAllocDict_t alloc_dict;\n        MemmgrHeapAllocDict_init(alloc_dict);\n        MemmgrHeapThreadDict_set_at(memmgr_heap_thread_dict, (uint32_t)thread_id, alloc_dict);\n        MemmgrHeapAllocDict_clear(alloc_dict);\n        memmgr_heap_thread_trace_depth--;\n    }\n    (void)xTaskResumeAll();\n}\n\nvoid memmgr_heap_disable_thread_trace(FuriThreadId thread_id) {\n    vTaskSuspendAll();\n    {\n        memmgr_heap_thread_trace_depth++;\n        furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id));\n        memmgr_heap_thread_trace_depth--;\n    }\n    (void)xTaskResumeAll();\n}\n\nsize_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) {\n    size_t leftovers = MEMMGR_HEAP_UNKNOWN;\n    vTaskSuspendAll();\n    {\n        memmgr_heap_thread_trace_depth++;\n        MemmgrHeapAllocDict_t* alloc_dict =\n            MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);\n        if(alloc_dict) {\n            leftovers = 0;\n            MemmgrHeapAllocDict_it_t alloc_dict_it;\n            for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict);\n                !MemmgrHeapAllocDict_end_p(alloc_dict_it);\n                MemmgrHeapAllocDict_next(alloc_dict_it)) {\n                MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it);\n                if(data->key != 0) {\n                    uint8_t* puc = (uint8_t*)data->key;\n                    puc -= xHeapStructSize;\n                    BlockLink_t* pxLink = (void*)puc;\n\n                    if((pxLink->xBlockSize & heapBLOCK_ALLOCATED_BITMASK) &&\n                       pxLink->pxNextFreeBlock == NULL) {\n                        leftovers += data->value;\n                    }\n                }\n            }\n        }\n        memmgr_heap_thread_trace_depth--;\n    }\n    (void)xTaskResumeAll();\n    return leftovers;\n}\n\n#undef traceMALLOC\nstatic inline void traceMALLOC(void* pointer, size_t size) {\n    FuriThreadId thread_id = furi_thread_get_current_id();\n    if(thread_id && memmgr_heap_thread_trace_depth == 0) {\n        memmgr_heap_thread_trace_depth++;\n        MemmgrHeapAllocDict_t* alloc_dict =\n            MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);\n        if(alloc_dict) {\n            MemmgrHeapAllocDict_set_at(*alloc_dict, (uint32_t)pointer, (uint32_t)size);\n        }\n        memmgr_heap_thread_trace_depth--;\n    }\n}\n\n#undef traceFREE\nstatic inline void traceFREE(void* pointer, size_t size) {\n    UNUSED(size);\n    FuriThreadId thread_id = furi_thread_get_current_id();\n    if(thread_id && memmgr_heap_thread_trace_depth == 0) {\n        memmgr_heap_thread_trace_depth++;\n        MemmgrHeapAllocDict_t* alloc_dict =\n            MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);\n        if(alloc_dict) {\n            // In some cases thread may want to release memory that was not allocated by it\n            const bool res = MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer);\n            UNUSED(res);\n        }\n        memmgr_heap_thread_trace_depth--;\n    }\n}\n\nsize_t memmgr_heap_get_max_free_block(void) {\n    HeapStats_t heap_stats;\n    vPortGetHeapStats(&heap_stats);\n    return heap_stats.xSizeOfLargestFreeBlockInBytes;\n}\n\nvoid memmgr_heap_printf_free_blocks(void) {\n    BlockLink_t* pxBlock;\n    //can be enabled once we can do printf with a locked scheduler\n    //vTaskSuspendAll();\n\n    pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock);\n    heapVALIDATE_BLOCK_POINTER(pxBlock);\n    while(pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER(NULL)) {\n        printf(\"A %p S %lu\\r\\n\", (void*)pxBlock, (uint32_t)pxBlock->xBlockSize);\n        pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock);\n        heapVALIDATE_BLOCK_POINTER(pxBlock);\n    }\n\n    //xTaskResumeAll();\n}\n\n/*-----------------------------------------------------------*/\n\nvoid* pvPortMalloc(size_t xWantedSize) {\n    BlockLink_t* pxBlock;\n    BlockLink_t* pxPreviousBlock;\n    BlockLink_t* pxNewBlockLink;\n    void* pvReturn = NULL;\n    size_t xToWipe = xWantedSize;\n    size_t xAdditionalRequiredSize;\n    size_t xAllocatedBlockSize = 0;\n\n    if(FURI_IS_IRQ_MODE()) {\n        furi_crash(\"memmgt in ISR\");\n    }\n\n    if(xWantedSize > 0) {\n        /* The wanted size must be increased so it can contain a BlockLink_t\n         * structure in addition to the requested amount of bytes. */\n        if(heapADD_WILL_OVERFLOW(xWantedSize, xHeapStructSize) == 0) {\n            xWantedSize += xHeapStructSize;\n\n            /* Ensure that blocks are always aligned to the required number\n             * of bytes. */\n            if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) {\n                /* Byte alignment required. */\n                xAdditionalRequiredSize =\n                    portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK);\n\n                if(heapADD_WILL_OVERFLOW(xWantedSize, xAdditionalRequiredSize) == 0) {\n                    xWantedSize += xAdditionalRequiredSize;\n                } else {\n                    xWantedSize = 0;\n                }\n            } else {\n                mtCOVERAGE_TEST_MARKER();\n            }\n        } else {\n            xWantedSize = 0;\n        }\n    } else {\n        mtCOVERAGE_TEST_MARKER();\n    }\n\n    vTaskSuspendAll();\n    {\n        /* If this is the first call to malloc then the heap will require\n         * initialisation to setup the list of free blocks. */\n        if(pxEnd == NULL) {\n            prvHeapInit();\n            memmgr_heap_init();\n        } else {\n            mtCOVERAGE_TEST_MARKER();\n        }\n\n        /* Check the block size we are trying to allocate is not so large that the\n         * top bit is set.  The top bit of the block size member of the BlockLink_t\n         * structure is used to determine who owns the block - the application or\n         * the kernel, so it must be free. */\n        if(heapBLOCK_SIZE_IS_VALID(xWantedSize) != 0) {\n            if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)) {\n                /* Traverse the list from the start (lowest address) block until\n                 * one of adequate size is found. */\n                pxPreviousBlock = &xStart;\n                pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock);\n                heapVALIDATE_BLOCK_POINTER(pxBlock);\n\n                while((pxBlock->xBlockSize < xWantedSize) &&\n                      (pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER(NULL))) {\n                    pxPreviousBlock = pxBlock;\n                    pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock);\n                    heapVALIDATE_BLOCK_POINTER(pxBlock);\n                }\n\n                /* If the end marker was reached then a block of adequate size\n                 * was not found. */\n                if(pxBlock != pxEnd) {\n                    /* Return the memory space pointed to - jumping over the\n                     * BlockLink_t structure at its start. */\n                    pvReturn = (void*)(((uint8_t*)heapPROTECT_BLOCK_POINTER(\n                                           pxPreviousBlock->pxNextFreeBlock)) +\n                                       xHeapStructSize);\n                    heapVALIDATE_BLOCK_POINTER(pvReturn);\n\n                    /* This block is being returned for use so must be taken out\n                     * of the list of free blocks. */\n                    pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;\n\n                    /* If the block is larger than required it can be split into\n                     * two. */\n                    configASSERT(\n                        heapSUBTRACT_WILL_UNDERFLOW(pxBlock->xBlockSize, xWantedSize) == 0);\n\n                    if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE) {\n                        /* This block is to be split into two.  Create a new\n                         * block following the number of bytes requested. The void\n                         * cast is used to prevent byte alignment warnings from the\n                         * compiler. */\n                        pxNewBlockLink = (void*)(((uint8_t*)pxBlock) + xWantedSize);\n                        configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0);\n\n                        /* Calculate the sizes of two blocks split from the\n                         * single block. */\n                        pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;\n                        pxBlock->xBlockSize = xWantedSize;\n\n                        /* Insert the new block into the list of free blocks. */\n                        pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock;\n                        pxPreviousBlock->pxNextFreeBlock =\n                            heapPROTECT_BLOCK_POINTER(pxNewBlockLink);\n                    } else {\n                        mtCOVERAGE_TEST_MARKER();\n                    }\n\n                    xFreeBytesRemaining -= pxBlock->xBlockSize;\n\n                    if(xFreeBytesRemaining < xMinimumEverFreeBytesRemaining) {\n                        xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;\n                    } else {\n                        mtCOVERAGE_TEST_MARKER();\n                    }\n\n                    xAllocatedBlockSize = pxBlock->xBlockSize;\n\n                    /* The block is being returned - it is allocated and owned\n                     * by the application and has no \"next\" block. */\n                    heapALLOCATE_BLOCK(pxBlock);\n                    pxBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(NULL);\n                    xNumberOfSuccessfulAllocations++;\n                } else {\n                    mtCOVERAGE_TEST_MARKER();\n                }\n            } else {\n                mtCOVERAGE_TEST_MARKER();\n            }\n        } else {\n            mtCOVERAGE_TEST_MARKER();\n        }\n\n        traceMALLOC(pvReturn, xAllocatedBlockSize);\n\n        /* Prevent compiler warnings when trace macros are not used. */\n        (void)xAllocatedBlockSize;\n    }\n    (void)xTaskResumeAll();\n\n#if(configUSE_MALLOC_FAILED_HOOK == 1)\n    {\n        if(pvReturn == NULL) {\n            vApplicationMallocFailedHook();\n        } else {\n            mtCOVERAGE_TEST_MARKER();\n        }\n    }\n#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */\n\n    configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0);\n\n    furi_check(pvReturn, xWantedSize ? \"out of memory\" : \"malloc(0)\");\n    pvReturn = memset(pvReturn, 0, xToWipe);\n    return pvReturn;\n}\n/*-----------------------------------------------------------*/\n\nvoid vPortFree(void* pv) {\n    uint8_t* puc = (uint8_t*)pv;\n    BlockLink_t* pxLink;\n\n    if(FURI_IS_IRQ_MODE()) {\n        furi_crash(\"memmgt in ISR\");\n    }\n\n    if(pv != NULL) {\n        /* The memory being freed will have an BlockLink_t structure immediately\n         * before it. */\n        puc -= xHeapStructSize;\n\n        /* This casting is to keep the compiler from issuing warnings. */\n        pxLink = (void*)puc;\n\n        heapVALIDATE_BLOCK_POINTER(pxLink);\n        configASSERT(heapBLOCK_IS_ALLOCATED(pxLink) != 0);\n        configASSERT(pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER(NULL));\n\n        if(heapBLOCK_IS_ALLOCATED(pxLink) != 0) {\n            if(pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER(NULL)) {\n                /* The block is being returned to the heap - it is no longer\n                 * allocated. */\n                heapFREE_BLOCK(pxLink);\n#if(configHEAP_CLEAR_MEMORY_ON_FREE == 1)\n                {\n                    /* Check for underflow as this can occur if xBlockSize is\n                     * overwritten in a heap block. */\n                    if(heapSUBTRACT_WILL_UNDERFLOW(pxLink->xBlockSize, xHeapStructSize) == 0) {\n                        (void)memset(\n                            puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize);\n                    }\n                }\n#endif\n\n                vTaskSuspendAll();\n                {\n                    furi_assert((size_t)pv >= SRAM_BASE);\n                    furi_assert((size_t)pv < SRAM_BASE + 1024 * 256);\n                    furi_assert(pxLink->xBlockSize >= xHeapStructSize);\n                    furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256);\n\n                    /* Add this block to the list of free blocks. */\n                    xFreeBytesRemaining += pxLink->xBlockSize;\n                    traceFREE(pv, pxLink->xBlockSize);\n                    prvInsertBlockIntoFreeList(((BlockLink_t*)pxLink));\n                    xNumberOfSuccessfulFrees++;\n                }\n                (void)xTaskResumeAll();\n            } else {\n                mtCOVERAGE_TEST_MARKER();\n            }\n        } else {\n            mtCOVERAGE_TEST_MARKER();\n        }\n    }\n}\n/*-----------------------------------------------------------*/\n\nsize_t xPortGetFreeHeapSize(void) {\n    return xFreeBytesRemaining;\n}\n/*-----------------------------------------------------------*/\n\nsize_t xPortGetMinimumEverFreeHeapSize(void) {\n    return xMinimumEverFreeBytesRemaining;\n}\n/*-----------------------------------------------------------*/\n\nvoid xPortResetHeapMinimumEverFreeHeapSize(void) {\n    xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;\n}\n/*-----------------------------------------------------------*/\n\nvoid vPortInitialiseBlocks(void) {\n    /* This just exists to keep the linker quiet. */\n}\n/*-----------------------------------------------------------*/\n\nvoid* pvPortCalloc(size_t xNum, size_t xSize) {\n    void* pv = NULL;\n\n    if(heapMULTIPLY_WILL_OVERFLOW(xNum, xSize) == 0) {\n        pv = pvPortMalloc(xNum * xSize);\n\n        if(pv != NULL) {\n            (void)memset(pv, 0, xNum * xSize);\n        }\n    }\n\n    return pv;\n}\n/*-----------------------------------------------------------*/\n\nstatic void prvHeapInit(void) /* PRIVILEGED_FUNCTION */\n{\n    BlockLink_t* pxFirstFreeBlock;\n    portPOINTER_SIZE_TYPE uxStartAddress, uxEndAddress;\n    size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;\n\n    /* Ensure the heap starts on a correctly aligned boundary. */\n    uxStartAddress = (portPOINTER_SIZE_TYPE)ucHeap;\n\n    if((uxStartAddress & portBYTE_ALIGNMENT_MASK) != 0) {\n        uxStartAddress += (portBYTE_ALIGNMENT - 1);\n        uxStartAddress &= ~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK);\n        xTotalHeapSize -= (size_t)(uxStartAddress - (portPOINTER_SIZE_TYPE)ucHeap);\n    }\n\n#if(configENABLE_HEAP_PROTECTOR == 1)\n    { vApplicationGetRandomHeapCanary(&(xHeapCanary)); }\n#endif\n\n    /* xStart is used to hold a pointer to the first item in the list of free\n     * blocks.  The void cast is used to prevent compiler warnings. */\n    xStart.pxNextFreeBlock = (void*)heapPROTECT_BLOCK_POINTER(uxStartAddress);\n    xStart.xBlockSize = (size_t)0;\n\n    /* pxEnd is used to mark the end of the list of free blocks and is inserted\n     * at the end of the heap space. */\n    uxEndAddress = uxStartAddress + (portPOINTER_SIZE_TYPE)xTotalHeapSize;\n    uxEndAddress -= (portPOINTER_SIZE_TYPE)xHeapStructSize;\n    uxEndAddress &= ~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK);\n    pxEnd = (BlockLink_t*)uxEndAddress;\n    pxEnd->xBlockSize = 0;\n    pxEnd->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(NULL);\n\n    /* To start with there is a single free block that is sized to take up the\n     * entire heap space, minus the space taken by pxEnd. */\n    pxFirstFreeBlock = (BlockLink_t*)uxStartAddress;\n    pxFirstFreeBlock->xBlockSize =\n        (size_t)(uxEndAddress - (portPOINTER_SIZE_TYPE)pxFirstFreeBlock);\n    pxFirstFreeBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxEnd);\n\n    /* Only one block exists - and it covers the entire usable heap space. */\n    xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;\n    xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;\n}\n/*-----------------------------------------------------------*/\n\nstatic void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) /* PRIVILEGED_FUNCTION */\n{\n    BlockLink_t* pxIterator;\n    uint8_t* puc;\n\n    /* Iterate through the list until a block is found that has a higher address\n     * than the block being inserted. */\n    for(pxIterator = &xStart;\n        heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock) < pxBlockToInsert;\n        pxIterator = heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)) {\n        /* Nothing to do here, just iterate to the right position. */\n    }\n\n    if(pxIterator != &xStart) {\n        heapVALIDATE_BLOCK_POINTER(pxIterator);\n    }\n\n    /* Do the block being inserted, and the block it is being inserted after\n     * make a contiguous block of memory? */\n    puc = (uint8_t*)pxIterator;\n\n    if((puc + pxIterator->xBlockSize) == (uint8_t*)pxBlockToInsert) {\n        pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;\n        pxBlockToInsert = pxIterator;\n    } else {\n        mtCOVERAGE_TEST_MARKER();\n    }\n\n    /* Do the block being inserted, and the block it is being inserted before\n     * make a contiguous block of memory? */\n    puc = (uint8_t*)pxBlockToInsert;\n\n    if((puc + pxBlockToInsert->xBlockSize) ==\n       (uint8_t*)heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)) {\n        if(heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock) != pxEnd) {\n            /* Form one big block from the two blocks. */\n            pxBlockToInsert->xBlockSize +=\n                heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)->xBlockSize;\n            pxBlockToInsert->pxNextFreeBlock =\n                heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)->pxNextFreeBlock;\n        } else {\n            pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxEnd);\n        }\n    } else {\n        pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;\n    }\n\n    /* If the block being inserted plugged a gap, so was merged with the block\n     * before and the block after, then it's pxNextFreeBlock pointer will have\n     * already been set, and should not be set here as that would make it point\n     * to itself. */\n    if(pxIterator != pxBlockToInsert) {\n        pxIterator->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxBlockToInsert);\n    } else {\n        mtCOVERAGE_TEST_MARKER();\n    }\n}\n/*-----------------------------------------------------------*/\n\nvoid vPortGetHeapStats(HeapStats_t* pxHeapStats) {\n    BlockLink_t* pxBlock;\n    size_t\n        xBlocks = 0,\n        xMaxSize = 0,\n        xMinSize =\n            portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */\n\n    vTaskSuspendAll();\n    {\n        pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock);\n\n        /* pxBlock will be NULL if the heap has not been initialised.  The heap\n         * is initialised automatically when the first allocation is made. */\n        if(pxBlock != NULL) {\n            while(pxBlock != pxEnd) {\n                /* Increment the number of blocks and record the largest block seen\n                 * so far. */\n                xBlocks++;\n\n                if(pxBlock->xBlockSize > xMaxSize) {\n                    xMaxSize = pxBlock->xBlockSize;\n                }\n\n                if(pxBlock->xBlockSize < xMinSize) {\n                    xMinSize = pxBlock->xBlockSize;\n                }\n\n                /* Move to the next block in the chain until the last block is\n                 * reached. */\n                pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock);\n            }\n        }\n    }\n    (void)xTaskResumeAll();\n\n    pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;\n    pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;\n    pxHeapStats->xNumberOfFreeBlocks = xBlocks;\n\n    taskENTER_CRITICAL();\n    {\n        pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;\n        pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;\n        pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;\n        pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;\n    }\n    taskEXIT_CRITICAL();\n}\n/*-----------------------------------------------------------*/\n\n/*\n * Reset the state in this file. This state is normally initialized at start up.\n * This function must be called by the application before restarting the\n * scheduler.\n */\nvoid vPortHeapResetState(void) {\n    pxEnd = NULL;\n\n    xFreeBytesRemaining = (size_t)0U;\n    xMinimumEverFreeBytesRemaining = (size_t)0U;\n    xNumberOfSuccessfulAllocations = (size_t)0U;\n    xNumberOfSuccessfulFrees = (size_t)0U;\n}\n/*-----------------------------------------------------------*/\n"
  },
  {
    "path": "furi/core/memmgr_heap.h",
    "content": "/**\n * @file memmgr_heap.h\n * Furi: heap memory management API and allocator\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <core/thread.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MEMMGR_HEAP_UNKNOWN 0xFFFFFFFF\n\n/** Memmgr heap enable thread allocation tracking\n *\n * @param      thread_id  - thread id to track\n */\nvoid memmgr_heap_enable_thread_trace(FuriThreadId thread_id);\n\n/** Memmgr heap disable thread allocation tracking\n *\n * @param      thread_id  - thread id to track\n */\nvoid memmgr_heap_disable_thread_trace(FuriThreadId thread_id);\n\n/** Memmgr heap get allocatred thread memory\n *\n * @param      thread_id  - thread id to track\n *\n * @return     bytes allocated right now\n */\nsize_t memmgr_heap_get_thread_memory(FuriThreadId thread_id);\n\n/** Memmgr heap get the max contiguous block size on the heap\n *\n * @return     size_t max contiguous block size\n */\nsize_t memmgr_heap_get_max_free_block(void);\n\n/** Print the address and size of all free blocks to stdout\n */\nvoid memmgr_heap_printf_free_blocks(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/message_queue.c",
    "content": "#include \"message_queue.h\"\n\n#include <FreeRTOS.h>\n#include <queue.h>\n\n#include \"kernel.h\"\n#include \"check.h\"\n\n#include \"event_loop_link_i.h\"\n\n// Internal FreeRTOS member names\n#define uxMessagesWaiting uxDummy4[0]\n#define uxLength          uxDummy4[1]\n#define uxItemSize        uxDummy4[2]\n\nstruct FuriMessageQueue {\n    StaticQueue_t container;\n    FuriEventLoopLink event_loop_link;\n    uint8_t buffer[];\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriMessageQueue, container) == 0);\n// IMPORTANT: buffer MUST be the LAST struct member\nstatic_assert(offsetof(FuriMessageQueue, buffer) == sizeof(FuriMessageQueue));\n\nFuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {\n    furi_check((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U));\n\n    FuriMessageQueue* instance = malloc(sizeof(FuriMessageQueue) + msg_count * msg_size);\n\n    // 3 things happens here:\n    // - create queue\n    // - check results\n    // - ensure that queue container is first in the FuriMessageQueue structure\n    //\n    // As a bonus it guarantees that FuriMessageQueue* can be casted into StaticQueue_t* or QueueHandle_t.\n    furi_check(\n        xQueueCreateStatic(msg_count, msg_size, instance->buffer, &instance->container) ==\n        (void*)instance);\n\n    return instance;\n}\n\nvoid furi_message_queue_free(FuriMessageQueue* instance) {\n    furi_check(furi_kernel_is_irq_or_masked() == 0U);\n    furi_check(instance);\n\n    // Event Loop must be disconnected\n    furi_check(!instance->event_loop_link.item_in);\n    furi_check(!instance->event_loop_link.item_out);\n\n    vQueueDelete((QueueHandle_t)instance);\n    free(instance);\n}\n\nFuriStatus\n    furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout) {\n    furi_check(instance);\n\n    QueueHandle_t hQueue = (QueueHandle_t)instance;\n    FuriStatus stat;\n    BaseType_t yield;\n\n    stat = FuriStatusOk;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        if((msg_ptr == NULL) || (timeout != 0U)) {\n            stat = FuriStatusErrorParameter;\n        } else {\n            yield = pdFALSE;\n\n            if(xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) {\n                stat = FuriStatusErrorResource;\n            } else {\n                portYIELD_FROM_ISR(yield);\n            }\n        }\n    } else {\n        if(msg_ptr == NULL) {\n            stat = FuriStatusErrorParameter;\n        } else {\n            if(xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {\n                if(timeout != 0U) {\n                    stat = FuriStatusErrorTimeout;\n                } else {\n                    stat = FuriStatusErrorResource;\n                }\n            }\n        }\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn);\n    }\n\n    /* Return execution status */\n    return stat;\n}\n\nFuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) {\n    furi_check(instance);\n\n    QueueHandle_t hQueue = (QueueHandle_t)instance;\n    FuriStatus stat;\n    BaseType_t yield;\n\n    stat = FuriStatusOk;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        if((msg_ptr == NULL) || (timeout != 0U)) {\n            stat = FuriStatusErrorParameter;\n        } else {\n            yield = pdFALSE;\n\n            if(xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) {\n                stat = FuriStatusErrorResource;\n            } else {\n                portYIELD_FROM_ISR(yield);\n            }\n        }\n    } else {\n        if(msg_ptr == NULL) {\n            stat = FuriStatusErrorParameter;\n        } else {\n            if(xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {\n                if(timeout != 0U) {\n                    stat = FuriStatusErrorTimeout;\n                } else {\n                    stat = FuriStatusErrorResource;\n                }\n            }\n        }\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    return stat;\n}\n\nuint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {\n    furi_check(instance);\n\n    return instance->container.uxLength;\n}\n\nuint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {\n    furi_check(instance);\n\n    return instance->container.uxItemSize;\n}\n\nuint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {\n    furi_check(instance);\n\n    QueueHandle_t hQueue = (QueueHandle_t)instance;\n    UBaseType_t count;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        count = uxQueueMessagesWaitingFromISR(hQueue);\n    } else {\n        count = uxQueueMessagesWaiting(hQueue);\n    }\n\n    return (uint32_t)count;\n}\n\nuint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {\n    furi_check(instance);\n\n    uint32_t space;\n    uint32_t isrm;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        isrm = taskENTER_CRITICAL_FROM_ISR();\n\n        space = instance->container.uxLength - instance->container.uxMessagesWaiting;\n\n        taskEXIT_CRITICAL_FROM_ISR(isrm);\n    } else {\n        space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance);\n    }\n\n    return space;\n}\n\nFuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {\n    furi_check(instance);\n\n    QueueHandle_t hQueue = (QueueHandle_t)instance;\n    FuriStatus stat;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        stat = FuriStatusErrorISR;\n    } else {\n        stat = FuriStatusOk;\n        (void)xQueueReset(hQueue);\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    /* Return execution status */\n    return stat;\n}\n\nstatic FuriEventLoopLink* furi_message_queue_event_loop_get_link(FuriEventLoopObject* object) {\n    FuriMessageQueue* instance = object;\n    furi_assert(instance);\n    return &instance->event_loop_link;\n}\n\nstatic bool\n    furi_message_queue_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {\n    FuriMessageQueue* instance = object;\n    furi_assert(instance);\n\n    if(event == FuriEventLoopEventIn) {\n        return furi_message_queue_get_count(instance);\n    } else if(event == FuriEventLoopEventOut) {\n        return furi_message_queue_get_space(instance);\n    } else {\n        furi_crash();\n    }\n}\n\nconst FuriEventLoopContract furi_message_queue_event_loop_contract = {\n    .get_link = furi_message_queue_event_loop_get_link,\n    .get_level = furi_message_queue_event_loop_get_level,\n};\n"
  },
  {
    "path": "furi/core/message_queue.h",
    "content": "/**\n * @file message_queue.h\n * FuriMessageQueue\n */\n#pragma once\n\n#include \"base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FuriMessageQueue FuriMessageQueue;\n\n/** Allocate furi message queue\n *\n * @param[in]  msg_count  The message count\n * @param[in]  msg_size   The message size\n *\n * @return     pointer to FuriMessageQueue instance\n */\nFuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size);\n\n/** Free queue\n *\n * @param      instance  pointer to FuriMessageQueue instance\n */\nvoid furi_message_queue_free(FuriMessageQueue* instance);\n\n/** Put message into queue\n *\n * @param      instance  pointer to FuriMessageQueue instance\n * @param[in]  msg_ptr   The message pointer\n * @param[in]  timeout   The timeout\n *\n * @return     The furi status.\n */\nFuriStatus\n    furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout);\n\n/** Get message from queue\n *\n * @param      instance  pointer to FuriMessageQueue instance\n * @param      msg_ptr   The message pointer\n * @param[in]  timeout   The timeout\n *\n * @return     The furi status.\n */\nFuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout);\n\n/** Get queue capacity\n *\n * @param      instance  pointer to FuriMessageQueue instance\n *\n * @return     capacity in object count\n */\nuint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance);\n\n/** Get message size\n *\n * @param      instance  pointer to FuriMessageQueue instance\n *\n * @return     Message size in bytes\n */\nuint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance);\n\n/** Get message count in queue\n *\n * @param      instance  pointer to FuriMessageQueue instance\n *\n * @return     Message count\n */\nuint32_t furi_message_queue_get_count(FuriMessageQueue* instance);\n\n/** Get queue available space\n *\n * @param      instance  pointer to FuriMessageQueue instance\n *\n * @return     Message count\n */\nuint32_t furi_message_queue_get_space(FuriMessageQueue* instance);\n\n/** Reset queue\n *\n * @param      instance  pointer to FuriMessageQueue instance\n *\n * @return     The furi status.\n */\nFuriStatus furi_message_queue_reset(FuriMessageQueue* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/mutex.c",
    "content": "#include \"mutex.h\"\n\n#include <FreeRTOS.h>\n#include <semphr.h>\n\n#include \"check.h\"\n\n#include \"event_loop_link_i.h\"\n\n// Internal FreeRTOS member names\n#define ucQueueType ucDummy9\n\nstruct FuriMutex {\n    StaticSemaphore_t container;\n    FuriEventLoopLink event_loop_link;\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriMutex, container) == 0);\n\nFuriMutex* furi_mutex_alloc(FuriMutexType type) {\n    furi_check(!FURI_IS_IRQ_MODE());\n\n    FuriMutex* instance = malloc(sizeof(FuriMutex));\n\n    SemaphoreHandle_t hMutex;\n\n    if(type == FuriMutexTypeNormal) {\n        hMutex = xSemaphoreCreateMutexStatic(&instance->container);\n    } else if(type == FuriMutexTypeRecursive) {\n        hMutex = xSemaphoreCreateRecursiveMutexStatic(&instance->container);\n    } else {\n        furi_crash();\n    }\n\n    furi_check(hMutex == (SemaphoreHandle_t)instance);\n\n    return instance;\n}\n\nvoid furi_mutex_free(FuriMutex* instance) {\n    furi_check(!FURI_IS_IRQ_MODE());\n    furi_check(instance);\n\n    // Event Loop must be disconnected\n    furi_check(!instance->event_loop_link.item_in);\n    furi_check(!instance->event_loop_link.item_out);\n\n    vSemaphoreDelete((SemaphoreHandle_t)instance);\n    free(instance);\n}\n\nFuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);\n    const uint8_t mutex_type = instance->container.ucQueueType;\n\n    FuriStatus stat = FuriStatusOk;\n\n    if(FURI_IS_IRQ_MODE()) {\n        stat = FuriStatusErrorISR;\n\n    } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {\n        if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {\n            if(timeout != 0U) {\n                stat = FuriStatusErrorTimeout;\n            } else {\n                stat = FuriStatusErrorResource;\n            }\n        }\n\n    } else if(mutex_type == queueQUEUE_TYPE_MUTEX) {\n        if(xSemaphoreTake(hMutex, timeout) != pdPASS) {\n            if(timeout != 0U) {\n                stat = FuriStatusErrorTimeout;\n            } else {\n                stat = FuriStatusErrorResource;\n            }\n        }\n\n    } else {\n        furi_crash();\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    return stat;\n}\n\nFuriStatus furi_mutex_release(FuriMutex* instance) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);\n    const uint8_t mutex_type = instance->container.ucQueueType;\n\n    FuriStatus stat = FuriStatusOk;\n\n    if(FURI_IS_IRQ_MODE()) {\n        stat = FuriStatusErrorISR;\n\n    } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {\n        if(xSemaphoreGiveRecursive(hMutex) != pdPASS) {\n            stat = FuriStatusErrorResource;\n        }\n\n    } else if(mutex_type == queueQUEUE_TYPE_MUTEX) {\n        if(xSemaphoreGive(hMutex) != pdPASS) {\n            stat = FuriStatusErrorResource;\n        }\n\n    } else {\n        furi_crash();\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn);\n    }\n\n    return stat;\n}\n\nFuriThreadId furi_mutex_get_owner(FuriMutex* instance) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance;\n\n    FuriThreadId owner;\n\n    if(FURI_IS_IRQ_MODE()) {\n        owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex);\n    } else {\n        owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);\n    }\n\n    return owner;\n}\n\nstatic FuriEventLoopLink* furi_mutex_event_loop_get_link(FuriEventLoopObject* object) {\n    FuriMutex* instance = object;\n    furi_assert(instance);\n    return &instance->event_loop_link;\n}\n\nstatic bool\n    furi_mutex_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {\n    FuriMutex* instance = object;\n    furi_assert(instance);\n\n    if(event == FuriEventLoopEventIn || event == FuriEventLoopEventOut) {\n        return !furi_mutex_get_owner(instance);\n    } else {\n        furi_crash();\n    }\n}\n\nconst FuriEventLoopContract furi_mutex_event_loop_contract = {\n    .get_link = furi_mutex_event_loop_get_link,\n    .get_level = furi_mutex_event_loop_get_level,\n};\n"
  },
  {
    "path": "furi/core/mutex.h",
    "content": "/**\n * @file mutex.h\n * FuriMutex\n */\n#pragma once\n\n#include \"base.h\"\n#include \"thread.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    FuriMutexTypeNormal,\n    FuriMutexTypeRecursive,\n} FuriMutexType;\n\ntypedef struct FuriMutex FuriMutex;\n\n/** Allocate FuriMutex\n *\n * @param[in]  type  The mutex type\n *\n * @return     pointer to FuriMutex instance\n */\nFuriMutex* furi_mutex_alloc(FuriMutexType type);\n\n/** Free FuriMutex\n *\n * @param      instance  The pointer to FuriMutex instance\n */\nvoid furi_mutex_free(FuriMutex* instance);\n\n/** Acquire mutex\n *\n * @param      instance  The pointer to FuriMutex instance\n * @param[in]  timeout   The timeout\n *\n * @return     The furi status.\n */\nFuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout);\n\n/** Release mutex\n *\n * @param      instance  The pointer to FuriMutex instance\n *\n * @return     The furi status.\n */\nFuriStatus furi_mutex_release(FuriMutex* instance);\n\n/** Get mutex owner thread id\n *\n * @param      instance  The pointer to FuriMutex instance\n *\n * @return     The furi thread identifier.\n */\nFuriThreadId furi_mutex_get_owner(FuriMutex* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/pubsub.c",
    "content": "#include \"pubsub.h\"\n#include \"check.h\"\n#include \"mutex.h\"\n\n#include <m-list.h>\n\nstruct FuriPubSubSubscription {\n    FuriPubSubCallback callback;\n    void* callback_context;\n};\n\nLIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST);\n\nstruct FuriPubSub {\n    FuriPubSubSubscriptionList_t items;\n    FuriMutex* mutex;\n};\n\nFuriPubSub* furi_pubsub_alloc(void) {\n    FuriPubSub* pubsub = malloc(sizeof(FuriPubSub));\n\n    pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n\n    FuriPubSubSubscriptionList_init(pubsub->items);\n\n    return pubsub;\n}\n\nvoid furi_pubsub_free(FuriPubSub* pubsub) {\n    furi_assert(pubsub);\n\n    furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0);\n\n    FuriPubSubSubscriptionList_clear(pubsub->items);\n\n    furi_mutex_free(pubsub->mutex);\n\n    free(pubsub);\n}\n\nFuriPubSubSubscription*\n    furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) {\n    furi_check(pubsub);\n    furi_check(callback);\n\n    furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);\n    // put uninitialized item to the list\n    FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items);\n\n    // initialize item\n    item->callback = callback;\n    item->callback_context = callback_context;\n\n    furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);\n\n    return item;\n}\n\nvoid furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) {\n    furi_assert(pubsub);\n    furi_assert(pubsub_subscription);\n\n    furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);\n    bool result = false;\n\n    // iterate over items\n    FuriPubSubSubscriptionList_it_t it;\n    for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it);\n        FuriPubSubSubscriptionList_next(it)) {\n        const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it);\n\n        // if the iterator is equal to our element\n        if(item == pubsub_subscription) {\n            FuriPubSubSubscriptionList_remove(pubsub->items, it);\n            result = true;\n            break;\n        }\n    }\n\n    furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);\n    furi_check(result);\n}\n\nvoid furi_pubsub_publish(FuriPubSub* pubsub, void* message) {\n    furi_check(pubsub);\n\n    furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);\n\n    // iterate over subscribers\n    FuriPubSubSubscriptionList_it_t it;\n    for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it);\n        FuriPubSubSubscriptionList_next(it)) {\n        const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it);\n        item->callback(message, item->callback_context);\n    }\n\n    furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);\n}\n"
  },
  {
    "path": "furi/core/pubsub.h",
    "content": "/**\n * @file pubsub.h\n * FuriPubSub\n */\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** FuriPubSub Callback type */\ntypedef void (*FuriPubSubCallback)(const void* message, void* context);\n\n/** FuriPubSub type */\ntypedef struct FuriPubSub FuriPubSub;\n\n/** FuriPubSubSubscription type */\ntypedef struct FuriPubSubSubscription FuriPubSubSubscription;\n\n/** Allocate FuriPubSub\n *\n * Reentrable, Not threadsafe, one owner\n *\n * @return     pointer to FuriPubSub instance\n */\nFuriPubSub* furi_pubsub_alloc(void);\n\n/** Free FuriPubSub\n * \n * @param      pubsub  FuriPubSub instance\n */\nvoid furi_pubsub_free(FuriPubSub* pubsub);\n\n/** Subscribe to FuriPubSub\n * \n * Threadsafe, Reentrable\n * \n * @param      pubsub            pointer to FuriPubSub instance\n * @param[in]  callback          The callback\n * @param      callback_context  The callback context\n *\n * @return     pointer to FuriPubSubSubscription instance\n */\nFuriPubSubSubscription*\n    furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context);\n\n/** Unsubscribe from FuriPubSub\n * \n * No use of `pubsub_subscription` allowed after call of this method\n * Threadsafe, Reentrable.\n *\n * @param      pubsub               pointer to FuriPubSub instance\n * @param      pubsub_subscription  pointer to FuriPubSubSubscription instance\n */\nvoid furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription);\n\n/** Publish message to FuriPubSub\n *\n * Threadsafe, Reentrable.\n * \n * @param      pubsub   pointer to FuriPubSub instance\n * @param      message  message pointer to publish\n */\nvoid furi_pubsub_publish(FuriPubSub* pubsub, void* message);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/record.c",
    "content": "#include \"record.h\"\n#include \"check.h\"\n#include \"mutex.h\"\n#include \"event_flag.h\"\n\n#include <m-dict.h>\n#include <toolbox/m_cstr_dup.h>\n\n#define FURI_RECORD_FLAG_READY (0x1)\n\ntypedef struct {\n    FuriEventFlag* flags;\n    void* data;\n    size_t holders_count;\n} FuriRecordData;\n\nDICT_DEF2(FuriRecordDataDict, const char*, M_CSTR_DUP_OPLIST, FuriRecordData, M_POD_OPLIST)\n\ntypedef struct {\n    FuriMutex* mutex;\n    FuriRecordDataDict_t records;\n} FuriRecord;\n\nstatic FuriRecord* furi_record = NULL;\n\nstatic FuriRecordData* furi_record_get(const char* name) {\n    return FuriRecordDataDict_get(furi_record->records, name);\n}\n\nstatic void furi_record_put(const char* name, FuriRecordData* record_data) {\n    FuriRecordDataDict_set_at(furi_record->records, name, *record_data);\n}\n\nstatic void furi_record_erase(const char* name, FuriRecordData* record_data) {\n    furi_event_flag_free(record_data->flags);\n    FuriRecordDataDict_erase(furi_record->records, name);\n}\n\nvoid furi_record_init(void) {\n    furi_record = malloc(sizeof(FuriRecord));\n    furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal);\n    FuriRecordDataDict_init(furi_record->records);\n}\n\nstatic FuriRecordData* furi_record_data_get_or_create(const char* name) {\n    furi_check(furi_record);\n    FuriRecordData* record_data = furi_record_get(name);\n    if(!record_data) {\n        FuriRecordData new_record;\n        new_record.flags = furi_event_flag_alloc();\n        new_record.data = NULL;\n        new_record.holders_count = 0;\n        furi_record_put(name, &new_record);\n        record_data = furi_record_get(name);\n    }\n    return record_data;\n}\n\nstatic void furi_record_lock(void) {\n    furi_check(furi_mutex_acquire(furi_record->mutex, FuriWaitForever) == FuriStatusOk);\n}\n\nstatic void furi_record_unlock(void) {\n    furi_check(furi_mutex_release(furi_record->mutex) == FuriStatusOk);\n}\n\nbool furi_record_exists(const char* name) {\n    furi_check(furi_record);\n    furi_check(name);\n\n    bool ret = false;\n\n    furi_record_lock();\n    ret = (furi_record_get(name) != NULL);\n    furi_record_unlock();\n\n    return ret;\n}\n\nvoid furi_record_create(const char* name, void* data) {\n    furi_check(furi_record);\n    furi_check(name);\n    furi_check(data);\n\n    furi_record_lock();\n\n    // Get record data and fill it\n    FuriRecordData* record_data = furi_record_data_get_or_create(name);\n    furi_check(record_data->data == NULL);\n    record_data->data = data;\n    furi_event_flag_set(record_data->flags, FURI_RECORD_FLAG_READY);\n\n    furi_record_unlock();\n}\n\nbool furi_record_destroy(const char* name) {\n    furi_check(furi_record);\n    furi_check(name);\n\n    bool ret = false;\n\n    furi_record_lock();\n\n    FuriRecordData* record_data = furi_record_get(name);\n    furi_check(record_data);\n    if(record_data->holders_count == 0) {\n        furi_record_erase(name, record_data);\n        ret = true;\n    }\n\n    furi_record_unlock();\n\n    return ret;\n}\n\nvoid* furi_record_open(const char* name) {\n    furi_check(furi_record);\n    furi_check(name);\n\n    furi_record_lock();\n\n    FuriRecordData* record_data = furi_record_data_get_or_create(name);\n    record_data->holders_count++;\n\n    furi_record_unlock();\n\n    // Wait for record to become ready\n    furi_check(\n        furi_event_flag_wait(\n            record_data->flags,\n            FURI_RECORD_FLAG_READY,\n            FuriFlagWaitAny | FuriFlagNoClear,\n            FuriWaitForever) == FURI_RECORD_FLAG_READY);\n\n    return record_data->data;\n}\n\nvoid furi_record_close(const char* name) {\n    furi_check(furi_record);\n    furi_check(name);\n\n    furi_record_lock();\n\n    FuriRecordData* record_data = furi_record_get(name);\n    furi_check(record_data);\n    record_data->holders_count--;\n\n    furi_record_unlock();\n}\n"
  },
  {
    "path": "furi/core/record.h",
    "content": "/**\n * @file record.h\n * Furi: record API\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include \"core_defines.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Initialize record storage For internal use only.\n */\nvoid furi_record_init(void);\n\n/** Check if record exists\n *\n * @param      name  record name\n * @note       Thread safe. Create and destroy must be executed from the same\n *             thread.\n */\nbool furi_record_exists(const char* name);\n\n/** Create record\n *\n * @param      name  record name\n * @param      data  data pointer (not NULL)\n * @note       Thread safe. Create and destroy must be executed from the same\n *             thread.\n */\nvoid furi_record_create(const char* name, void* data);\n\n/** Destroy record\n *\n * @param      name  record name\n *\n * @return     true if successful, false if still have holders or thread is not\n *             owner.\n * @note       Thread safe. Create and destroy must be executed from the same\n *             thread.\n */\nbool furi_record_destroy(const char* name);\n\n/** Open record\n *\n * @param      name  record name\n *\n * @return     pointer to the record\n * @note       Thread safe. Open and close must be executed from the same\n *             thread. Suspends caller thread till record is available\n */\nFURI_RETURNS_NONNULL void* furi_record_open(const char* name);\n\n/** Close record\n *\n * @param      name  record name\n * @note       Thread safe. Open and close must be executed from the same\n *             thread.\n */\nvoid furi_record_close(const char* name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/semaphore.c",
    "content": "#include \"semaphore.h\"\n\n#include <FreeRTOS.h>\n#include <semphr.h>\n\n#include \"check.h\"\n#include \"kernel.h\"\n\n#include \"event_loop_link_i.h\"\n\n// Internal FreeRTOS member names\n#define uxMessagesWaiting uxDummy4[0]\n#define uxLength          uxDummy4[1]\n\nstruct FuriSemaphore {\n    StaticSemaphore_t container;\n    FuriEventLoopLink event_loop_link;\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriSemaphore, container) == 0);\n\nFuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {\n    furi_check(!FURI_IS_IRQ_MODE());\n    furi_check((max_count > 0U) && (initial_count <= max_count));\n\n    FuriSemaphore* instance = malloc(sizeof(FuriSemaphore));\n\n    SemaphoreHandle_t hSemaphore;\n\n    if(max_count == 1U) {\n        hSemaphore = xSemaphoreCreateBinaryStatic(&instance->container);\n    } else {\n        hSemaphore =\n            xSemaphoreCreateCountingStatic(max_count, initial_count, &instance->container);\n    }\n\n    furi_check(hSemaphore == (SemaphoreHandle_t)instance);\n\n    if(max_count == 1U && initial_count != 0U) {\n        furi_check(xSemaphoreGive(hSemaphore) == pdPASS);\n    }\n\n    return instance;\n}\n\nvoid furi_semaphore_free(FuriSemaphore* instance) {\n    furi_check(instance);\n    furi_check(!FURI_IS_IRQ_MODE());\n\n    // Event Loop must be disconnected\n    furi_check(!instance->event_loop_link.item_in);\n    furi_check(!instance->event_loop_link.item_out);\n\n    vSemaphoreDelete((SemaphoreHandle_t)instance);\n    free(instance);\n}\n\nFuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;\n    FuriStatus stat;\n    BaseType_t yield;\n\n    stat = FuriStatusOk;\n\n    if(FURI_IS_IRQ_MODE()) {\n        if(timeout != 0U) {\n            stat = FuriStatusErrorParameter;\n        } else {\n            yield = pdFALSE;\n\n            if(xSemaphoreTakeFromISR(hSemaphore, &yield) != pdPASS) {\n                stat = FuriStatusErrorResource;\n            } else {\n                portYIELD_FROM_ISR(yield);\n            }\n        }\n\n    } else {\n        if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) {\n            if(timeout != 0U) {\n                stat = FuriStatusErrorTimeout;\n            } else {\n                stat = FuriStatusErrorResource;\n            }\n        }\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    return stat;\n}\n\nFuriStatus furi_semaphore_release(FuriSemaphore* instance) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;\n    FuriStatus stat;\n    BaseType_t yield;\n\n    stat = FuriStatusOk;\n\n    if(FURI_IS_IRQ_MODE()) {\n        yield = pdFALSE;\n\n        if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) {\n            stat = FuriStatusErrorResource;\n        } else {\n            portYIELD_FROM_ISR(yield);\n        }\n\n    } else {\n        if(xSemaphoreGive(hSemaphore) != pdPASS) {\n            stat = FuriStatusErrorResource;\n        }\n    }\n\n    if(stat == FuriStatusOk) {\n        furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn);\n    }\n\n    return stat;\n}\n\nuint32_t furi_semaphore_get_count(FuriSemaphore* instance) {\n    furi_check(instance);\n\n    SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;\n    uint32_t count;\n\n    if(FURI_IS_IRQ_MODE()) {\n        count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);\n    } else {\n        count = (uint32_t)uxSemaphoreGetCount(hSemaphore);\n    }\n\n    return count;\n}\n\nuint32_t furi_semaphore_get_space(FuriSemaphore* instance) {\n    furi_assert(instance);\n\n    uint32_t space;\n\n    if(furi_kernel_is_irq_or_masked() != 0U) {\n        uint32_t isrm = taskENTER_CRITICAL_FROM_ISR();\n\n        space = instance->container.uxLength - instance->container.uxMessagesWaiting;\n\n        taskEXIT_CRITICAL_FROM_ISR(isrm);\n    } else {\n        space = uxQueueSpacesAvailable((QueueHandle_t)instance);\n    }\n\n    return space;\n}\n\nstatic FuriEventLoopLink* furi_semaphore_event_loop_get_link(FuriEventLoopObject* object) {\n    FuriSemaphore* instance = object;\n    furi_assert(instance);\n    return &instance->event_loop_link;\n}\n\nstatic bool\n    furi_semaphore_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {\n    FuriSemaphore* instance = object;\n    furi_assert(instance);\n\n    if(event == FuriEventLoopEventIn) {\n        return furi_semaphore_get_count(instance);\n    } else if(event == FuriEventLoopEventOut) {\n        return furi_semaphore_get_space(instance);\n    } else {\n        furi_crash();\n    }\n}\n\nconst FuriEventLoopContract furi_semaphore_event_loop_contract = {\n    .get_link = furi_semaphore_event_loop_get_link,\n    .get_level = furi_semaphore_event_loop_get_level,\n};\n"
  },
  {
    "path": "furi/core/semaphore.h",
    "content": "/**\n * @file semaphore.h\n * FuriSemaphore\n */\n#pragma once\n\n#include \"base.h\"\n#include \"thread.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FuriSemaphore FuriSemaphore;\n\n/** Allocate semaphore\n *\n * @param[in]  max_count      The maximum count\n * @param[in]  initial_count  The initial count\n *\n * @return     pointer to FuriSemaphore instance\n */\nFuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count);\n\n/** Free semaphore\n *\n * @param      instance  The pointer to FuriSemaphore instance\n */\nvoid furi_semaphore_free(FuriSemaphore* instance);\n\n/** Acquire semaphore\n *\n * @param      instance  The pointer to FuriSemaphore instance\n * @param[in]  timeout   The timeout\n *\n * @return     The furi status.\n */\nFuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout);\n\n/** Release semaphore\n *\n * @param      instance  The pointer to FuriSemaphore instance\n *\n * @return     The furi status.\n */\nFuriStatus furi_semaphore_release(FuriSemaphore* instance);\n\n/** Get semaphore count\n *\n * @param      instance  The pointer to FuriSemaphore instance\n *\n * @return     Semaphore count\n */\nuint32_t furi_semaphore_get_count(FuriSemaphore* instance);\n\n/** Get available space\n *\n * @param      instance  The pointer to FuriSemaphore instance\n *\n * @return     Semaphore available space\n */\nuint32_t furi_semaphore_get_space(FuriSemaphore* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/stream_buffer.c",
    "content": "#include \"stream_buffer.h\"\n\n#include <FreeRTOS.h>\n#include <FreeRTOS-Kernel/include/stream_buffer.h>\n\n#include \"check.h\"\n#include \"common_defines.h\"\n\n#include \"event_loop_link_i.h\"\n\n// Internal FreeRTOS member names\n#define xTriggerLevelBytes uxDummy1[3]\n\nstruct FuriStreamBuffer {\n    StaticStreamBuffer_t container;\n    FuriEventLoopLink event_loop_link;\n    uint8_t buffer[];\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriStreamBuffer, container) == 0);\n// IMPORTANT: buffer MUST be the LAST struct member\nstatic_assert(offsetof(FuriStreamBuffer, buffer) == sizeof(FuriStreamBuffer));\n\nFuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) {\n    furi_check(size != 0);\n\n    // Actual FreeRTOS usable buffer size seems to be one less\n    const size_t buffer_size = size + 1;\n\n    FuriStreamBuffer* stream_buffer = malloc(sizeof(FuriStreamBuffer) + buffer_size);\n    StreamBufferHandle_t hStreamBuffer = xStreamBufferCreateStatic(\n        buffer_size, trigger_level, stream_buffer->buffer, &stream_buffer->container);\n\n    furi_check(hStreamBuffer == (StreamBufferHandle_t)stream_buffer);\n\n    return stream_buffer;\n}\n\nvoid furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    // Event Loop must be disconnected\n    furi_check(!stream_buffer->event_loop_link.item_in);\n    furi_check(!stream_buffer->event_loop_link.item_out);\n\n    vStreamBufferDelete((StreamBufferHandle_t)stream_buffer);\n    free(stream_buffer);\n}\n\nbool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) {\n    furi_check(stream_buffer);\n    return xStreamBufferSetTriggerLevel((StreamBufferHandle_t)stream_buffer, trigger_level) ==\n           pdTRUE;\n}\n\nsize_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n    return ((StaticStreamBuffer_t*)stream_buffer)->xTriggerLevelBytes;\n}\n\nsize_t furi_stream_buffer_send(\n    FuriStreamBuffer* stream_buffer,\n    const void* data,\n    size_t length,\n    uint32_t timeout) {\n    furi_check(stream_buffer);\n\n    size_t ret;\n\n    if(FURI_IS_IRQ_MODE()) {\n        BaseType_t yield;\n        ret = xStreamBufferSendFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);\n        portYIELD_FROM_ISR(yield);\n    } else {\n        ret = xStreamBufferSend((StreamBufferHandle_t)stream_buffer, data, length, timeout);\n    }\n\n    if(ret > 0) {\n        const size_t bytes_available =\n            xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer);\n        const size_t trigger_level = ((StaticStreamBuffer_t*)stream_buffer)->xTriggerLevelBytes;\n\n        if(bytes_available >= trigger_level) {\n            furi_event_loop_link_notify(&stream_buffer->event_loop_link, FuriEventLoopEventIn);\n        }\n    }\n\n    return ret;\n}\n\nsize_t furi_stream_buffer_receive(\n    FuriStreamBuffer* stream_buffer,\n    void* data,\n    size_t length,\n    uint32_t timeout) {\n    furi_check(stream_buffer);\n\n    size_t ret;\n\n    if(FURI_IS_IRQ_MODE()) {\n        BaseType_t yield;\n        ret =\n            xStreamBufferReceiveFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);\n        portYIELD_FROM_ISR(yield);\n    } else {\n        ret = xStreamBufferReceive((StreamBufferHandle_t)stream_buffer, data, length, timeout);\n    }\n\n    if(ret > 0) {\n        furi_event_loop_link_notify(&stream_buffer->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    return ret;\n}\n\nsize_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    return xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer);\n}\n\nsize_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    return xStreamBufferSpacesAvailable((StreamBufferHandle_t)stream_buffer);\n}\n\nbool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    return xStreamBufferIsFull((StreamBufferHandle_t)stream_buffer) == pdTRUE;\n}\n\nbool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    return xStreamBufferIsEmpty((StreamBufferHandle_t)stream_buffer) == pdTRUE;\n}\n\nFuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) {\n    furi_check(stream_buffer);\n\n    FuriStatus status;\n\n    if(xStreamBufferReset((StreamBufferHandle_t)stream_buffer) == pdPASS) {\n        status = FuriStatusOk;\n    } else {\n        status = FuriStatusError;\n    }\n\n    if(status == FuriStatusOk) {\n        furi_event_loop_link_notify(&stream_buffer->event_loop_link, FuriEventLoopEventOut);\n    }\n\n    return status;\n}\n\nstatic FuriEventLoopLink* furi_stream_buffer_event_loop_get_link(FuriEventLoopObject* object) {\n    FuriStreamBuffer* stream_buffer = object;\n    furi_assert(stream_buffer);\n    return &stream_buffer->event_loop_link;\n}\n\nstatic bool\n    furi_stream_buffer_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {\n    FuriStreamBuffer* stream_buffer = object;\n    furi_assert(stream_buffer);\n\n    if(event == FuriEventLoopEventIn) {\n        return xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer);\n    } else if(event == FuriEventLoopEventOut) {\n        return xStreamBufferSpacesAvailable((StreamBufferHandle_t)stream_buffer);\n    } else {\n        furi_crash();\n    }\n}\n\nconst FuriEventLoopContract furi_stream_buffer_event_loop_contract = {\n    .get_link = furi_stream_buffer_event_loop_get_link,\n    .get_level = furi_stream_buffer_event_loop_get_level,\n};\n"
  },
  {
    "path": "furi/core/stream_buffer.h",
    "content": "/**\n * @file stream_buffer.h\n * Furi stream buffer primitive.\n * \n * Stream buffers are used to send a continuous stream of data from one task or\n * interrupt to another.  Their implementation is light weight, making them\n * particularly suited for interrupt to task and core to core communication\n * scenarios.\n * \n * ***NOTE***: Stream buffer implementation assumes there is only one task or\n * interrupt that will write to the buffer (the writer), and only one task or\n * interrupt that will read from the buffer (the reader).\n */\n#pragma once\n\n#include \"base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FuriStreamBuffer FuriStreamBuffer;\n\n/**\n * @brief Allocate stream buffer instance.\n * Stream buffer implementation assumes there is only one task or\n * interrupt that will write to the buffer (the writer), and only one task or\n * interrupt that will read from the buffer (the reader).\n * \n * @param size The total number of bytes the stream buffer will be able to hold at any one time.\n * @param trigger_level The number of bytes that must be in the stream buffer \n * before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state.\n * @return The stream buffer instance.\n */\nFuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level);\n\n/**\n * @brief Free stream buffer instance\n * \n * @param stream_buffer The stream buffer instance.\n */\nvoid furi_stream_buffer_free(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Set trigger level for stream buffer.\n * A stream buffer's trigger level is the number of bytes that must be in the\n * stream buffer before a task that is blocked on the stream buffer to\n * wait for data is moved out of the blocked state.\n * \n * @param stream_buffer The stream buffer instance\n * @param trigger_level The new trigger level for the stream buffer.\n * @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length). \n * @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length).\n */\nbool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level);\n\n/**\n * @brief Get trigger level for stream buffer.\n * A stream buffer's trigger level is the number of bytes that must be in the\n * stream buffer before a task that is blocked on the stream buffer to\n * wait for data is moved out of the blocked state.\n * \n * @param stream_buffer The stream buffer instance\n * @return The trigger level for the stream buffer\n */\nsize_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer.\n * Wakes up task waiting for data to become available if called from ISR.\n * \n * @param stream_buffer The stream buffer instance.\n * @param data A pointer to the data that is to be copied into the stream buffer.\n * @param length The maximum number of bytes to copy from data into the stream buffer.\n * @param timeout The maximum amount of time the task should remain in the\n * Blocked state to wait for space to become available if the stream buffer is full. \n * Will return immediately if timeout is zero. \n * Setting timeout to FuriWaitForever will cause the task to wait indefinitely.\n * Ignored if called from ISR.\n * @return The number of bytes actually written to the stream buffer. \n */\nsize_t furi_stream_buffer_send(\n    FuriStreamBuffer* stream_buffer,\n    const void* data,\n    size_t length,\n    uint32_t timeout);\n\n/**\n * @brief Receives bytes from a stream buffer.\n * Wakes up task waiting for space to become available if called from ISR.\n * \n * @param stream_buffer The stream buffer instance.\n * @param data A pointer to the buffer into which the received bytes will be\n * copied.\n * @param length The length of the buffer pointed to by the data parameter.\n * @param timeout The maximum amount of time the task should remain in the\n * Blocked state to wait for data to become available if the stream buffer is empty. \n * Will return immediately if timeout is zero. \n * Setting timeout to FuriWaitForever will cause the task to wait indefinitely.\n * Ignored if called from ISR.\n * @return The number of bytes read from the stream buffer, if any. \n */\nsize_t furi_stream_buffer_receive(\n    FuriStreamBuffer* stream_buffer,\n    void* data,\n    size_t length,\n    uint32_t timeout);\n\n/**\n * @brief Queries a stream buffer to see how much data it contains, which is equal to\n * the number of bytes that can be read from the stream buffer before the stream\n * buffer would be empty.\n * \n * @param stream_buffer The stream buffer instance.\n * @return The number of bytes that can be read from the stream buffer before\n * the stream buffer would be empty.\n */\nsize_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Queries a stream buffer to see how much free space it contains, which is\n * equal to the amount of data that can be sent to the stream buffer before it\n * is full.\n * \n * @param stream_buffer The stream buffer instance.\n * @return The number of bytes that can be written to the stream buffer before\n * the stream buffer would be full. \n */\nsize_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Queries a stream buffer to see if it is full.\n * \n * @param stream_buffer stream buffer instance.\n * @return true if the stream buffer is full.\n * @return false if the stream buffer is not full.\n */\nbool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Queries a stream buffer to see if it is empty.\n * \n * @param stream_buffer The stream buffer instance.\n * @return true if the stream buffer is empty.\n * @return false if the stream buffer is not empty.\n */\nbool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer);\n\n/**\n * @brief Resets a stream buffer to its initial, empty, state. Any data that was \n * in the stream buffer is discarded. A stream buffer can only be reset if there \n * are no tasks blocked waiting to either send to or receive from the stream buffer.\n * \n * @param stream_buffer The stream buffer instance.\n * @return FuriStatusOk if the stream buffer is reset. \n * @return FuriStatusError if there was a task blocked waiting to send to or read \n * from the stream buffer then the stream buffer is not reset.\n */\nFuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/string.c",
    "content": "#include \"string.h\"\n#include <m-string.h>\n\nstruct FuriString {\n    string_t string;\n};\n\n#undef furi_string_alloc_set\n#undef furi_string_set\n#undef furi_string_cmp\n#undef furi_string_cmpi\n#undef furi_string_search\n#undef furi_string_search_str\n#undef furi_string_equal\n#undef furi_string_replace\n#undef furi_string_replace_str\n#undef furi_string_replace_all\n#undef furi_string_start_with\n#undef furi_string_end_with\n#undef furi_string_end_withi\n#undef furi_string_search_char\n#undef furi_string_search_rchar\n#undef furi_string_trim\n#undef furi_string_cat\n\nFuriString* furi_string_alloc(void) {\n    FuriString* string = malloc(sizeof(FuriString));\n    string_init(string->string);\n    return string;\n}\n\nFuriString* furi_string_alloc_set(const FuriString* s) {\n    FuriString* string = malloc(sizeof(FuriString)); //-V799\n    string_init_set(string->string, s->string);\n    return string;\n} //-V773\n\nFuriString* furi_string_alloc_set_str(const char cstr[]) {\n    FuriString* string = malloc(sizeof(FuriString)); //-V799\n    string_init_set(string->string, cstr);\n    return string;\n} //-V773\n\nFuriString* furi_string_alloc_printf(const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    FuriString* string = furi_string_alloc_vprintf(format, args);\n    va_end(args);\n    return string;\n}\n\nFuriString* furi_string_alloc_vprintf(const char format[], va_list args) {\n    FuriString* string = malloc(sizeof(FuriString));\n    string_init_vprintf(string->string, format, args);\n    return string;\n}\n\nFuriString* furi_string_alloc_move(FuriString* s) {\n    FuriString* string = malloc(sizeof(FuriString));\n    string_init_move(string->string, s->string);\n    free(s);\n    return string;\n}\n\nvoid furi_string_free(FuriString* s) {\n    string_clear(s->string);\n    free(s);\n}\n\nvoid furi_string_reserve(FuriString* s, size_t alloc) {\n    string_reserve(s->string, alloc);\n}\n\nvoid furi_string_reset(FuriString* s) {\n    string_clear(s->string);\n    string_init(s->string);\n}\n\nvoid furi_string_swap(FuriString* v1, FuriString* v2) {\n    string_swap(v1->string, v2->string);\n}\n\nvoid furi_string_move(FuriString* v1, FuriString* v2) {\n    string_clear(v1->string);\n    string_init_move(v1->string, v2->string);\n    free(v2);\n}\n\nsize_t furi_string_hash(const FuriString* v) {\n    return string_hash(v->string);\n}\n\nchar furi_string_get_char(const FuriString* v, size_t index) {\n    return string_get_char(v->string, index);\n}\n\nconst char* furi_string_get_cstr(const FuriString* s) {\n    return string_get_cstr(s->string);\n}\n\nvoid furi_string_set(FuriString* s, FuriString* source) {\n    string_set(s->string, source->string);\n}\n\nvoid furi_string_set_str(FuriString* s, const char cstr[]) {\n    string_set(s->string, cstr);\n}\n\nvoid furi_string_set_strn(FuriString* s, const char str[], size_t n) {\n    string_set_strn(s->string, str, n);\n}\n\nvoid furi_string_set_char(FuriString* s, size_t index, const char c) {\n    string_set_char(s->string, index, c);\n}\n\nint furi_string_cmp(const FuriString* s1, const FuriString* s2) {\n    return string_cmp(s1->string, s2->string);\n}\n\nint furi_string_cmp_str(const FuriString* s1, const char str[]) {\n    return string_cmp(s1->string, str);\n}\n\nint furi_string_cmpi(const FuriString* v1, const FuriString* v2) {\n    return string_cmpi(v1->string, v2->string);\n}\n\nint furi_string_cmpi_str(const FuriString* v1, const char p2[]) {\n    return string_cmpi_str(v1->string, p2);\n}\n\nsize_t furi_string_search(const FuriString* v, const FuriString* needle, size_t start) {\n    return string_search(v->string, needle->string, start);\n}\n\nsize_t furi_string_search_str(const FuriString* v, const char needle[], size_t start) {\n    return string_search(v->string, needle, start);\n}\n\nbool furi_string_equal(const FuriString* v1, const FuriString* v2) {\n    return string_equal_p(v1->string, v2->string);\n}\n\nbool furi_string_equal_str(const FuriString* v1, const char v2[]) {\n    return string_equal_p(v1->string, v2);\n}\n\nvoid furi_string_push_back(FuriString* v, char c) {\n    string_push_back(v->string, c);\n}\n\nsize_t furi_string_size(const FuriString* s) {\n    return string_size(s->string);\n}\n\nint furi_string_printf(FuriString* v, const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    int result = furi_string_vprintf(v, format, args);\n    va_end(args);\n    return result;\n}\n\nint furi_string_vprintf(FuriString* v, const char format[], va_list args) {\n    return string_vprintf(v->string, format, args);\n}\n\nint furi_string_cat_printf(FuriString* v, const char format[], ...) {\n    va_list args;\n    va_start(args, format);\n    int result = furi_string_cat_vprintf(v, format, args);\n    va_end(args);\n    return result;\n}\n\nint furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) {\n    FuriString* string = furi_string_alloc();\n    int ret = furi_string_vprintf(string, format, args);\n    furi_string_cat(v, string);\n    furi_string_free(string);\n    return ret;\n}\n\nbool furi_string_empty(const FuriString* v) {\n    return string_empty_p(v->string);\n}\n\nvoid furi_string_replace_at(FuriString* v, size_t pos, size_t len, const char str2[]) {\n    string_replace_at(v->string, pos, len, str2);\n}\n\nsize_t\n    furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start) {\n    return string_replace(string->string, needle->string, replace->string, start);\n}\n\nsize_t furi_string_replace_str(FuriString* v, const char str1[], const char str2[], size_t start) {\n    return string_replace_str(v->string, str1, str2, start);\n}\n\nvoid furi_string_replace_all_str(FuriString* v, const char str1[], const char str2[]) {\n    string_replace_all_str(v->string, str1, str2);\n}\n\nvoid furi_string_replace_all(FuriString* v, const FuriString* str1, const FuriString* str2) {\n    string_replace_all(v->string, str1->string, str2->string);\n}\n\nbool furi_string_start_with(const FuriString* v, const FuriString* v2) {\n    return string_start_with_string_p(v->string, v2->string);\n}\n\nbool furi_string_start_with_str(const FuriString* v, const char str[]) {\n    return string_start_with_str_p(v->string, str);\n}\n\nbool furi_string_end_with(const FuriString* v, const FuriString* v2) {\n    return string_end_with_string_p(v->string, v2->string);\n}\n\nbool furi_string_end_withi(const FuriString* v, const FuriString* v2) {\n    return furi_string_end_withi_str(v, string_get_cstr(v2->string));\n}\n\nbool furi_string_end_with_str(const FuriString* v, const char str[]) {\n    return string_end_with_str_p(v->string, str);\n}\n\nbool furi_string_end_withi_str(const FuriString* v, const char str[]) {\n    M_STR1NG_CONTRACT(v);\n    M_ASSERT(str != NULL);\n\n    const size_t str_len = strlen(str);\n    const size_t v_len = string_size(v->string);\n\n    if(v_len < str_len) {\n        return false;\n    }\n\n    return strcasecmp(&string_get_cstr(v->string)[v_len - str_len], str) == 0;\n}\n\nsize_t furi_string_search_char(const FuriString* v, char c, size_t start) {\n    return string_search_char(v->string, c, start);\n}\n\nsize_t furi_string_search_rchar(const FuriString* v, char c, size_t start) {\n    return string_search_rchar(v->string, c, start);\n}\n\nvoid furi_string_left(FuriString* v, size_t index) {\n    string_left(v->string, index);\n}\n\nvoid furi_string_right(FuriString* v, size_t index) {\n    string_right(v->string, index);\n}\n\nvoid furi_string_mid(FuriString* v, size_t index, size_t size) {\n    string_mid(v->string, index, size);\n}\n\nvoid furi_string_trim(FuriString* v, const char charac[]) {\n    string_strim(v->string, charac);\n}\n\nvoid furi_string_cat(FuriString* v, const FuriString* v2) {\n    string_cat(v->string, v2->string);\n}\n\nvoid furi_string_cat_str(FuriString* v, const char str[]) {\n    string_cat(v->string, str);\n}\n\nvoid furi_string_set_n(FuriString* v, const FuriString* ref, size_t offset, size_t length) {\n    string_set_n(v->string, ref->string, offset, length);\n}\n\nsize_t furi_string_utf8_length(FuriString* str) {\n    return string_length_u(str->string);\n}\n\nvoid furi_string_utf8_push(FuriString* str, FuriStringUnicodeValue u) {\n    string_push_u(str->string, u);\n}\n\nstatic m_str1ng_utf8_state_e furi_state_to_state(FuriStringUTF8State state) {\n    switch(state) {\n    case FuriStringUTF8StateStarting:\n        return M_STRING_UTF8_STARTING;\n    case FuriStringUTF8StateDecoding1:\n        return M_STRING_UTF8_DECODING_1;\n    case FuriStringUTF8StateDecoding2:\n        return M_STRING_UTF8_DECODING_2;\n    case FuriStringUTF8StateDecoding3:\n        return M_STRING_UTF8_DOCODING_3;\n    default:\n        return M_STRING_UTF8_ERROR;\n    }\n}\n\nstatic FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) {\n    switch(state) {\n    case M_STRING_UTF8_STARTING:\n        return FuriStringUTF8StateStarting;\n    case M_STRING_UTF8_DECODING_1:\n        return FuriStringUTF8StateDecoding1;\n    case M_STRING_UTF8_DECODING_2:\n        return FuriStringUTF8StateDecoding2;\n    case M_STRING_UTF8_DOCODING_3:\n        return FuriStringUTF8StateDecoding3;\n    default:\n        return FuriStringUTF8StateError;\n    }\n}\n\nvoid furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) {\n    string_unicode_t m_u = *unicode;\n    m_str1ng_utf8_state_e m_state = furi_state_to_state(*state);\n    m_str1ng_utf8_decode(c, &m_state, &m_u);\n    *state = state_to_furi_state(m_state);\n    *unicode = m_u;\n}\n"
  },
  {
    "path": "furi/core/string.h",
    "content": "/** Furi string container\n * \n * And various method to manipulate strings\n *\n * @file string.h\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <stdarg.h>\n#include <m-core.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Furi string failure constant. */\n#define FURI_STRING_FAILURE ((size_t) - 1)\n\n/** Furi string primitive. */\ntypedef struct FuriString FuriString;\n\n//---------------------------------------------------------------------------\n//                               Constructors\n//---------------------------------------------------------------------------\n\n/** Allocate new FuriString.\n *\n * @return     pointer to the instance of FuriString\n */\nFuriString* furi_string_alloc(void);\n\n/** Allocate new FuriString and set it to string.\n *\n * Allocate & Set the string a to the string.\n *\n * @param      source  The source FuriString instance\n *\n * @return     pointer to the new instance of FuriString\n */\nFuriString* furi_string_alloc_set(const FuriString* source);\n\n/** Allocate new FuriString and set it to C string.\n *\n * Allocate & Set the string a to the C string.\n *\n * @param      cstr_source  The C-string instance\n *\n * @return     pointer to the new instance of FuriString\n */\nFuriString* furi_string_alloc_set_str(const char cstr_source[]);\n\n/** Allocate new FuriString and printf to it.\n *\n * Initialize and set a string to the given formatted value.\n * \n * @param      format     The printf format\n * @param[in]  ...        args to format\n *\n * @return     pointer to the new instance of FuriString\n */\nFuriString* furi_string_alloc_printf(const char format[], ...)\n    _ATTRIBUTE((__format__(__printf__, 1, 2)));\n\n/** Allocate new FuriString and printf to it.\n *\n * Initialize and set a string to the given formatted value.\n *\n * @param      format  The printf format\n * @param      args    The format arguments\n *\n * @return     pointer to the new instance of FuriString\n */\nFuriString* furi_string_alloc_vprintf(const char format[], va_list args);\n\n/** Allocate new FuriString and move source string content to it.\n *\n * Allocate the string, set it to the other one, and destroy the other one.\n *\n * @param      source  The source FuriString instance\n *\n * @return     pointer to the new instance of FuriString\n */\nFuriString* furi_string_alloc_move(FuriString* source);\n\n//---------------------------------------------------------------------------\n//                               Destructors\n//---------------------------------------------------------------------------\n\n/** Free FuriString.\n *\n * @param      string  The FuriString instance to free\n */\nvoid furi_string_free(FuriString* string);\n\n//---------------------------------------------------------------------------\n//                         String memory management\n//---------------------------------------------------------------------------\n\n/** Reserve memory for string.\n *\n * Modify the string capacity to be able to handle at least 'alloc' characters\n * (including final null char).\n *\n * @param      string  The FuriString instance\n * @param      size    The size to reserve\n */\nvoid furi_string_reserve(FuriString* string, size_t size);\n\n/** Reset string.\n *\n * Make the string empty.\n *\n * @param      string  The FuriString instance\n */\nvoid furi_string_reset(FuriString* string);\n\n/** Swap two strings.\n *\n * Swap the two strings string_1 and string_2.\n *\n * @param      string_1  The FuriString instance 1\n * @param      string_2  The FuriString instance 2\n */\nvoid furi_string_swap(FuriString* string_1, FuriString* string_2);\n\n/** Move string_2 content to string_1.\n *\n * Copy data from one string to another and destroy the source.\n *\n * @param      destination  The destination FuriString\n * @param      source  The source FuriString\n */\nvoid furi_string_move(FuriString* destination, FuriString* source);\n\n/** Compute a hash for the string.\n *\n * @param      string  The FuriString instance\n *\n * @return     hash value\n */\nsize_t furi_string_hash(const FuriString* string);\n\n/** Get string size (usually length, but not for UTF-8)\n *\n * @param      string  The FuriString instance\n *\n * @return     size of the string\n */\nsize_t furi_string_size(const FuriString* string);\n\n/** Check that string is empty or not\n *\n * @param      string  The FuriString instance\n *\n * @return     true if empty otherwise false\n */\nbool furi_string_empty(const FuriString* string);\n\n//---------------------------------------------------------------------------\n//                               Getters\n//---------------------------------------------------------------------------\n\n/** Get the character at the given index.\n *\n * Return the selected character of the string.\n *\n * @param      string  The FuriString instance\n * @param      index   The index\n *\n * @return     character at index\n */\nchar furi_string_get_char(const FuriString* string, size_t index);\n\n/** Return the string view a classic C string.\n *\n * @param      string  The FuriString instance\n *\n * @return     const C-string, usable till first container change\n */\nconst char* furi_string_get_cstr(const FuriString* string);\n\n//---------------------------------------------------------------------------\n//                               Setters\n//---------------------------------------------------------------------------\n\n/** Set the string to the other string.\n *\n * Set the string to the source string.\n *\n * @param      string  The FuriString instance\n * @param      source  The source\n */\nvoid furi_string_set(FuriString* string, FuriString* source);\n\n/** Set the string to the other C string.\n *\n * Set the string to the source C string.\n *\n * @param      string  The FuriString instance\n * @param      source  The source\n */\nvoid furi_string_set_str(FuriString* string, const char source[]);\n\n/** Set the string to the n first characters of the C string.\n *\n * @param      string  The FuriString instance\n * @param      source  The source\n * @param      length  The length\n */\nvoid furi_string_set_strn(FuriString* string, const char source[], size_t length);\n\n/** Set the character at the given index.\n *\n * @param      string  The FuriString instance\n * @param      index   The index\n * @param      c       The character\n */\nvoid furi_string_set_char(FuriString* string, size_t index, const char c);\n\n/** Set the string to the n first characters of other one.\n *\n * @param      string  The FuriString instance\n * @param      source  The source\n * @param      offset  The offset\n * @param      length  The length\n */\nvoid furi_string_set_n(FuriString* string, const FuriString* source, size_t offset, size_t length);\n\n/** Format in the string the given printf format\n *\n * @param      string     The string\n * @param      format     The format\n * @param[in]  ...        The args\n *\n * @return     number of characters printed or negative value on error\n */\nint furi_string_printf(FuriString* string, const char format[], ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/** Format in the string the given printf format\n *\n * @param      string  The FuriString instance\n * @param      format  The format\n * @param      args    The arguments\n *\n * @return     number of characters printed or negative value on error\n */\nint furi_string_vprintf(FuriString* string, const char format[], va_list args);\n\n//---------------------------------------------------------------------------\n//                               Appending\n//---------------------------------------------------------------------------\n\n/** Append a character to the string.\n *\n * @param      string  The FuriString instance\n * @param      c       The character\n */\nvoid furi_string_push_back(FuriString* string, char c);\n\n/** Append a string to the string.\n *\n * Concatenate the string with the other string.\n *\n * @param      string_1  The string 1\n * @param      string_2  The string 2\n */\nvoid furi_string_cat(FuriString* string_1, const FuriString* string_2);\n\n/** Append a C string to the string.\n *\n * Concatenate the string with the C string.\n *\n * @param      string_1   The string 1\n * @param      cstring_2  The cstring 2\n */\nvoid furi_string_cat_str(FuriString* string_1, const char cstring_2[]);\n\n/** Append to the string the formatted string of the given printf format.\n *\n * @param      string     The string\n * @param      format     The format\n * @param[in]  ...        The args\n *\n * @return     number of characters printed or negative value on error\n */\nint furi_string_cat_printf(FuriString* string, const char format[], ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/** Append to the string the formatted string of the given printf format.\n *\n * @param      string  The FuriString instance\n * @param      format  The format\n * @param      args    The arguments\n *\n * @return     number of characters printed or negative value on error\n */\nint furi_string_cat_vprintf(FuriString* string, const char format[], va_list args);\n\n//---------------------------------------------------------------------------\n//                               Comparators\n//---------------------------------------------------------------------------\n\n/** Compare two strings and return the sort order.\n *\n * @param      string_1  The string 1\n * @param      string_2  The string 2\n *\n * @return     zero if equal\n */\nint furi_string_cmp(const FuriString* string_1, const FuriString* string_2);\n\n/** Compare string with C string and return the sort order.\n *\n * @param      string_1   The string 1\n * @param      cstring_2  The cstring 2\n *\n * @return     zero if equal\n */\nint furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]);\n\n/** Compare two strings (case insensitive according to the current locale) and\n * return the sort order.\n *\n * Note: doesn't work with UTF-8 strings.\n *\n * @param      string_1  The string 1\n * @param      string_2  The string 2\n *\n * @return     zero if equal\n */\nint furi_string_cmpi(const FuriString* string_1, const FuriString* string_2);\n\n/** Compare string with C string (case insensitive according to the current\n * locale) and return the sort order.\n *\n * Note: doesn't work with UTF-8 strings.\n *\n * @param      string_1   The string 1\n * @param      cstring_2  The cstring 2\n *\n * @return     zero if equal\n */\nint furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]);\n\n//---------------------------------------------------------------------------\n//                                 Search\n//---------------------------------------------------------------------------\n\n/** Search the first occurrence of the needle in the string from the position\n * start.\n *\n * @param      string  The FuriString instance\n * @param      needle  The needle\n * @param      start   The start (By default, start is zero)\n *\n * @return     position or FURI_STRING_FAILURE if not found\n */\nsize_t furi_string_search(const FuriString* string, const FuriString* needle, size_t start);\n\n/** Search the first occurrence of the needle in the string from the position\n * start.\n *\n * @param      string  The FuriString instance\n * @param      needle  The needle\n * @param      start   The start (By default, start is zero)\n *\n * @return     position or FURI_STRING_FAILURE if not found\n */\nsize_t furi_string_search_str(const FuriString* string, const char needle[], size_t start);\n\n/** Search for the position of the character c from the position start (include)\n * in the string.\n *\n * @param      string  The FuriString instance\n * @param      c       The character\n * @param      start   The start (By default, start is zero)\n *\n * @return     position or FURI_STRING_FAILURE if not found\n */\nsize_t furi_string_search_char(const FuriString* string, char c, size_t start);\n\n/** Reverse search for the position of the character c from the position start\n * (include) in the string.\n *\n * @param      string  The FuriString instance\n * @param      c       The character\n * @param      start   The start (By default, start is zero)\n *\n * @return     position or FURI_STRING_FAILURE if not found\n */\nsize_t furi_string_search_rchar(const FuriString* string, char c, size_t start);\n\n//---------------------------------------------------------------------------\n//                                Equality\n//---------------------------------------------------------------------------\n\n/** Test if two strings are equal.\n *\n * @param      string_1  The string 1\n * @param      string_2  The string 2\n *\n * @return     true if equal false otherwise\n */\nbool furi_string_equal(const FuriString* string_1, const FuriString* string_2);\n\n/** Test if the string is equal to the C string.\n *\n * @param      string_1   The string 1\n * @param      cstring_2  The cstring 2\n *\n * @return     true if equal false otherwise\n */\nbool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]);\n\n//---------------------------------------------------------------------------\n//                                Replace\n//---------------------------------------------------------------------------\n\n/** Replace in the string the sub-string at position 'pos' for 'len' bytes into\n * the C string 'replace'.\n *\n * @param      string   The string\n * @param      pos      The position\n * @param      len      The length\n * @param      replace  The replace\n */\nvoid furi_string_replace_at(FuriString* string, size_t pos, size_t len, const char replace[]);\n\n/** Replace a string 'needle' to string 'replace' in a string from 'start'\n * position.\n *\n * @param      string   The string\n * @param      needle   The needle\n * @param      replace  The replace\n * @param      start    The start (By default, start is zero)\n *\n * @return     Return FURI_STRING_FAILURE if 'needle' not found or replace position.\n */\nsize_t\n    furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start);\n\n/** Replace a C string 'needle' to C string 'replace' in a string from 'start'\n * position.\n *\n * @param      string   The string\n * @param      needle   The needle\n * @param      replace  The replace\n * @param      start    The start (By default, start is zero)\n *\n * @return     Return FURI_STRING_FAILURE if 'needle' not found or replace position.\n */\nsize_t furi_string_replace_str(\n    FuriString* string,\n    const char needle[],\n    const char replace[],\n    size_t start);\n\n/** Replace all occurrences of 'needle' string into 'replace' string.\n *\n * @param      string   The string\n * @param      needle   The needle\n * @param      replace  The replace\n */\nvoid furi_string_replace_all(\n    FuriString* string,\n    const FuriString* needle,\n    const FuriString* replace);\n\n/** Replace all occurrences of 'needle' C string into 'replace' C string.\n *\n * @param      string   The string\n * @param      needle   The needle\n * @param      replace  The replace\n */\nvoid furi_string_replace_all_str(FuriString* string, const char needle[], const char replace[]);\n\n//---------------------------------------------------------------------------\n//                            Start / End tests\n//---------------------------------------------------------------------------\n\n/** Test if the string starts with the given string.\n *\n * @param      string  The FuriString instance\n * @param      start   The FuriString instance \n *\n * @return     true if string starts with\n */\nbool furi_string_start_with(const FuriString* string, const FuriString* start);\n\n/** Test if the string starts with the given C string.\n *\n * @param      string  The FuriString instance\n * @param      start   The start\n *\n * @return     true if string starts with\n */\nbool furi_string_start_with_str(const FuriString* string, const char start[]);\n\n/** Test if the string ends with the given string.\n *\n * @param      string  The FuriString instance\n * @param      end     The end\n *\n * @return     true if string ends with\n */\nbool furi_string_end_with(const FuriString* string, const FuriString* end);\n\n/** Test if the string ends with the given string (case insensitive according to the current locale).\n *\n * @param      string  The FuriString instance\n * @param      end     The end\n *\n * @return     true if string ends with\n */\nbool furi_string_end_withi(const FuriString* string, const FuriString* end);\n\n/** Test if the string ends with the given C string.\n *\n * @param      string  The FuriString instance\n * @param      end     The end\n *\n * @return     true if string ends with\n */\nbool furi_string_end_with_str(const FuriString* string, const char end[]);\n\n/** Test if the string ends with the given C string (case insensitive according to the current locale).\n *\n * @param      string  The FuriString instance\n * @param      end     The end\n *\n * @return     true if string ends with\n */\nbool furi_string_end_withi_str(const FuriString* string, const char end[]);\n\n//---------------------------------------------------------------------------\n//                                Trim\n//---------------------------------------------------------------------------\n\n/** Trim the string left to the first 'index' bytes.\n *\n * @param      string  The FuriString instance\n * @param      index   The index\n */\nvoid furi_string_left(FuriString* string, size_t index);\n\n/** Trim the string right from the 'index' position to the last position.\n *\n * @param      string  The FuriString instance\n * @param      index   The index\n */\nvoid furi_string_right(FuriString* string, size_t index);\n\n/** Trim the string from position index to size bytes.\n *\n * See also furi_string_set_n.\n *\n * @param      string  The FuriString instance\n * @param      index   The index\n * @param      size    The size\n */\nvoid furi_string_mid(FuriString* string, size_t index, size_t size);\n\n/** Trim a string from the given set of characters (default is \" \\n\\r\\t\").\n *\n * @param      string  The FuriString instance\n * @param      chars   The characters\n */\nvoid furi_string_trim(FuriString* string, const char chars[]);\n\n//---------------------------------------------------------------------------\n//                                UTF8\n//---------------------------------------------------------------------------\n\n/** An unicode value */\ntypedef unsigned int FuriStringUnicodeValue;\n\n/** Compute the length in UTF8 characters in the string.\n *\n * @param      string  The FuriString instance\n *\n * @return     strings size\n */\nsize_t furi_string_utf8_length(FuriString* string);\n\n/** Push unicode into string, encoding it in UTF8.\n *\n * @param      string   The string\n * @param      unicode  The unicode\n */\nvoid furi_string_utf8_push(FuriString* string, FuriStringUnicodeValue unicode);\n\n/** State of the UTF8 decoding machine state */\ntypedef enum {\n    FuriStringUTF8StateStarting,\n    FuriStringUTF8StateDecoding1,\n    FuriStringUTF8StateDecoding2,\n    FuriStringUTF8StateDecoding3,\n    FuriStringUTF8StateError\n} FuriStringUTF8State;\n\n/** Main generic UTF8 decoder\n *\n * It takes a character, and the previous state and the previous value of the\n * unicode value. It updates the state and the decoded unicode value. A decoded\n * unicode encoded value is valid only when the state is\n * FuriStringUTF8StateStarting.\n *\n * @param      c        The character\n * @param      state    The state\n * @param      unicode  The unicode\n */\nvoid furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode);\n\n//---------------------------------------------------------------------------\n//                Lasciate ogne speranza, voi ch’entrate\n//---------------------------------------------------------------------------\n\n/**\n *\n * Select either the string function or the str function depending on\n * the b operand to the function.\n * func1 is the string function / func2 is the str function.\n */\n\n/** Select for 1 argument */\n#define FURI_STRING_SELECT1(func1, func2, a)                                                       \\\n    _Generic((a), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \\\n        a)\n\n/** Select for 2 arguments */\n#define FURI_STRING_SELECT2(func1, func2, a, b)                                                    \\\n    _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \\\n        a, b)\n\n/** Select for 3 arguments */\n#define FURI_STRING_SELECT3(func1, func2, a, b, c)                                                 \\\n    _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \\\n        a, b, c)\n\n/** Select for 4 arguments */\n#define FURI_STRING_SELECT4(func1, func2, a, b, c, d)                                              \\\n    _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \\\n        a, b, c, d)\n\n/** Allocate new FuriString and set it content to string (or C string).\n *\n * ([c]string)\n */\n#define furi_string_alloc_set(a) \\\n    FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a)\n\n/** Set the string content to string (or C string).\n *\n * (string, [c]string)\n */\n#define furi_string_set(a, b) FURI_STRING_SELECT2(furi_string_set, furi_string_set_str, a, b)\n\n/** Compare string with string (or C string) and return the sort order.\n *\n * Note: doesn't work with UTF-8 strings.\n * (string, [c]string)\n */\n#define furi_string_cmp(a, b) FURI_STRING_SELECT2(furi_string_cmp, furi_string_cmp_str, a, b)\n\n/** Compare string with string (or C string) (case insensitive according to the current locale) and return the sort order.\n *\n * Note: doesn't work with UTF-8 strings.\n * (string, [c]string)\n */\n#define furi_string_cmpi(a, b) FURI_STRING_SELECT2(furi_string_cmpi, furi_string_cmpi_str, a, b)\n\n/** Test if the string is equal to the string (or C string).\n *\n * (string, [c]string)\n */\n#define furi_string_equal(a, b) FURI_STRING_SELECT2(furi_string_equal, furi_string_equal_str, a, b)\n\n/** Replace all occurrences of string into string (or C string to another C string) in a string.\n *\n * (string, [c]string, [c]string)\n */\n#define furi_string_replace_all(a, b, c) \\\n    FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, a, b, c)\n\n/** Search for a string (or C string) in a string\n *\n * (string, [c]string[, start=0])\n */\n#define furi_string_search(...) \\\n    M_APPLY(                    \\\n        FURI_STRING_SELECT3,    \\\n        furi_string_search,     \\\n        furi_string_search_str, \\\n        M_DEFAULT_ARGS(3, (0), __VA_ARGS__))\n/** Search for a C string in a string\n *\n * (string, cstring[, start=0])\n */\n#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))\n\n/** Test if the string starts with the given string (or C string).\n *\n * (string, [c]string)\n */\n#define furi_string_start_with(a, b) \\\n    FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, a, b)\n\n/** Test if the string ends with the given string (or C string).\n *\n * (string, [c]string)\n */\n#define furi_string_end_with(a, b) \\\n    FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, a, b)\n\n/** Test if the string ends with the given string (or C string) (case insensitive according to the current locale).\n *\n * (string, [c]string)\n */\n#define furi_string_end_withi(a, b) \\\n    FURI_STRING_SELECT2(furi_string_end_withi, furi_string_end_withi_str, a, b)\n\n/** Append a string (or C string) to the string.\n *\n * (string, [c]string)\n */\n#define furi_string_cat(a, b) FURI_STRING_SELECT2(furi_string_cat, furi_string_cat_str, a, b)\n\n/** Trim a string from the given set of characters (default is \" \\n\\r\\t\").\n *\n * (string[, set=\" \\n\\r\\t\"])\n */\n#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (\"  \\n\\r\\t\"), __VA_ARGS__))\n\n/** Search for a character in a string.\n *\n * (string, character[, start=0])\n */\n#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))\n\n/** Reverse Search for a character in a string.\n *\n * (string, character[, start=0])\n */\n#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))\n\n/** Replace a string to another string (or C string to another C string) in a string.\n *\n * (string, [c]string, [c]string[, start=0])\n */\n#define furi_string_replace(...) \\\n    M_APPLY(                     \\\n        FURI_STRING_SELECT4,     \\\n        furi_string_replace,     \\\n        furi_string_replace_str, \\\n        M_DEFAULT_ARGS(4, (0), __VA_ARGS__))\n\n/** Replace a C string to another C string in a string.\n *\n * (string, cstring, cstring[, start=0])\n */\n#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__))\n\n/** INIT OPLIST for FuriString */\n#define F_STR_INIT(a) ((a) = furi_string_alloc())\n\n/** INIT SET OPLIST for FuriString */\n#define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b))\n\n/** INIT MOVE OPLIST for FuriString */\n#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b))\n\n/** OPLIST for FuriString */\n#define FURI_STRING_OPLIST       \\\n    (INIT(F_STR_INIT),           \\\n     INIT_SET(F_STR_INIT_SET),   \\\n     SET(furi_string_set),       \\\n     INIT_MOVE(F_STR_INIT_MOVE), \\\n     MOVE(furi_string_move),     \\\n     SWAP(furi_string_swap),     \\\n     RESET(furi_string_reset),   \\\n     EMPTY_P(furi_string_empty), \\\n     CLEAR(furi_string_free),    \\\n     HASH(furi_string_hash),     \\\n     EQUAL(furi_string_equal),   \\\n     CMP(furi_string_cmp),       \\\n     TYPE(FuriString*))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/thread.c",
    "content": "#include \"thread_i.h\"\n#include \"thread_list_i.h\"\n#include \"kernel.h\"\n#include \"message_queue.h\"\n#include \"memmgr.h\"\n#include \"memmgr_heap.h\"\n#include \"check.h\"\n#include \"common_defines.h\"\n#include \"string.h\"\n#include \"event_loop_thread_flag_interface.h\"\n\n#include \"log.h\"\n#include <furi_hal_rtc.h>\n\n#include <FreeRTOS.h>\n#include <stdint.h>\n#include <task.h>\n\n#include <task_control_block.h>\n\n#define TAG \"FuriThread\"\n\n#define THREAD_NOTIFY_INDEX (1) // Index 0 is used for stream buffers\n\n#define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t))\n\n#define THREAD_STACK_WATERMARK_MIN (256u)\n\ntypedef struct {\n    FuriThreadStdoutWriteCallback write_callback;\n    FuriString* buffer;\n    void* context;\n} FuriThreadStdout;\n\ntypedef struct {\n    FuriThreadStdinReadCallback read_callback;\n    FuriString* unread_buffer; // <! stores data from `ungetc` and friends\n    void* context;\n} FuriThreadStdin;\n\nstruct FuriThread {\n    StaticTask_t container;\n    StackType_t* stack_buffer;\n\n    volatile FuriThreadState state;\n    int32_t ret;\n\n    FuriThreadCallback callback;\n    void* context;\n\n    FuriThreadStateCallback state_callback;\n    void* state_context;\n\n    FuriThreadSignalCallback signal_callback;\n    void* signal_context;\n\n    char* name;\n    char* appid;\n\n    FuriThreadPriority priority;\n\n    size_t stack_size;\n    size_t heap_size;\n\n    FuriThreadStdout output;\n    FuriThreadStdin input;\n\n    // Keep all non-alignable byte types in one place,\n    // this ensures that the size of this structure is minimal\n    bool is_service;\n    bool heap_trace_enabled;\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriThread, container) == 0);\n\n// Our idle priority should be equal to the one from FreeRTOS\nstatic_assert(FuriThreadPriorityIdle == tskIDLE_PRIORITY);\n\nstatic FuriMessageQueue* furi_thread_scrub_message_queue = NULL;\n\nstatic size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);\nstatic int32_t __furi_thread_stdout_flush(FuriThread* thread);\n\n/** Catch threads that are trying to exit wrong way */\n__attribute__((__noreturn__)) void furi_thread_catch(void) { //-V1082\n    // If you're here it means you're probably doing something wrong\n    // with critical sections or with scheduler state\n    asm volatile(\"nop\"); // extra magic\n    furi_crash(\"You are doing it wrong\"); //-V779\n    __builtin_unreachable();\n}\n\nstatic void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {\n    furi_assert(thread);\n    thread->state = state;\n    if(thread->state_callback) {\n        thread->state_callback(thread, state, thread->state_context);\n    }\n}\n\nstatic void furi_thread_body(void* context) {\n    furi_check(context);\n    FuriThread* thread = context;\n\n    // store thread instance to thread local storage\n    furi_check(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL);\n    vTaskSetThreadLocalStoragePointer(NULL, 0, thread);\n\n    furi_check(thread->state == FuriThreadStateStarting);\n    furi_thread_set_state(thread, FuriThreadStateRunning);\n\n    if(thread->heap_trace_enabled == true) {\n        memmgr_heap_enable_thread_trace((FuriThreadId)thread);\n    }\n\n    thread->ret = thread->callback(thread->context);\n\n    furi_check(!thread->is_service, \"Service threads MUST NOT return\");\n\n    size_t stack_watermark = furi_thread_get_stack_space(thread);\n    if(stack_watermark < THREAD_STACK_WATERMARK_MIN) {\n#ifdef FURI_DEBUG\n        furi_crash(\"Stack watermark is dangerously low\");\n#endif\n        FURI_LOG_E( //-V779\n            thread->name ? thread->name : \"Thread\",\n            \"Stack watermark is too low %zu < \" STRINGIFY(\n                THREAD_STACK_WATERMARK_MIN) \". Increase stack size.\",\n            stack_watermark);\n    }\n\n    if(thread->heap_trace_enabled == true) {\n        furi_delay_ms(33);\n        thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)thread);\n        furi_log_print_format(\n            thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo,\n            TAG,\n            \"%s allocation balance: %zu\",\n            thread->name ? thread->name : \"Thread\",\n            thread->heap_size);\n        memmgr_heap_disable_thread_trace((FuriThreadId)thread);\n    }\n\n    furi_check(thread->state == FuriThreadStateRunning);\n\n    // flush stdout\n    __furi_thread_stdout_flush(thread);\n\n    furi_thread_set_state(thread, FuriThreadStateStopping);\n\n    furi_message_queue_put(furi_thread_scrub_message_queue, &thread, FuriWaitForever);\n\n    vTaskSuspend(NULL);\n    furi_thread_catch();\n}\n\nstatic void furi_thread_init_common(FuriThread* thread) {\n    thread->output.buffer = furi_string_alloc();\n    thread->input.unread_buffer = furi_string_alloc();\n\n    FuriThread* parent = NULL;\n    if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {\n        // TLS is not available, if we called not from thread context\n        parent = pvTaskGetThreadLocalStoragePointer(NULL, 0);\n\n        if(parent && parent->appid) {\n            furi_thread_set_appid(thread, parent->appid);\n        } else {\n            furi_thread_set_appid(thread, \"unknown\");\n        }\n    } else {\n        // if scheduler is not started, we are starting driver thread\n        furi_thread_set_appid(thread, \"driver\");\n    }\n\n    thread->priority = FuriThreadPriorityNormal;\n\n    FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();\n    if(mode == FuriHalRtcHeapTrackModeAll) {\n        thread->heap_trace_enabled = true;\n    } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) {\n        if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled;\n    } else {\n        thread->heap_trace_enabled = false;\n    }\n}\n\nvoid furi_thread_init(void) {\n    furi_thread_scrub_message_queue = furi_message_queue_alloc(8, sizeof(FuriThread*));\n}\n\nvoid furi_thread_scrub(void) {\n    FuriThread* thread_to_scrub = NULL;\n    while(true) {\n        furi_check(\n            furi_message_queue_get(\n                furi_thread_scrub_message_queue, &thread_to_scrub, FuriWaitForever) ==\n            FuriStatusOk);\n\n        TaskHandle_t task = (TaskHandle_t)thread_to_scrub;\n\n        // Delete task: FreeRTOS will remove task from all lists where it may be\n        vTaskDelete(task);\n        // Sanity check: ensure that local storage is ours and clear it\n        furi_check(pvTaskGetThreadLocalStoragePointer(task, 0) == thread_to_scrub);\n        vTaskSetThreadLocalStoragePointer(task, 0, NULL);\n\n        // Deliver thread stopped callback\n        furi_thread_set_state(thread_to_scrub, FuriThreadStateStopped);\n    }\n}\n\nFuriThread* furi_thread_alloc(void) {\n    FuriThread* thread = malloc(sizeof(FuriThread));\n\n    furi_thread_init_common(thread);\n\n    return thread;\n}\n\nFuriThread* furi_thread_alloc_service(\n    const char* name,\n    uint32_t stack_size,\n    FuriThreadCallback callback,\n    void* context) {\n    FuriThread* thread = memmgr_alloc_from_pool(sizeof(FuriThread));\n\n    furi_thread_init_common(thread);\n\n    thread->stack_buffer = memmgr_alloc_from_pool(stack_size);\n    thread->stack_size = stack_size;\n    thread->is_service = true;\n\n    furi_thread_set_name(thread, name);\n    furi_thread_set_callback(thread, callback);\n    furi_thread_set_context(thread, context);\n\n    return thread;\n}\n\nFuriThread* furi_thread_alloc_ex(\n    const char* name,\n    uint32_t stack_size,\n    FuriThreadCallback callback,\n    void* context) {\n    FuriThread* thread = furi_thread_alloc();\n    furi_thread_set_name(thread, name);\n    furi_thread_set_stack_size(thread, stack_size);\n    furi_thread_set_callback(thread, callback);\n    furi_thread_set_context(thread, context);\n    return thread;\n}\n\nvoid furi_thread_free(FuriThread* thread) {\n    furi_check(thread);\n    // Cannot free a service thread\n    furi_check(thread->is_service == false);\n    // Cannot free a non-joined thread\n    furi_check(thread->state == FuriThreadStateStopped);\n\n    furi_thread_set_name(thread, NULL);\n    furi_thread_set_appid(thread, NULL);\n\n    if(thread->stack_buffer) {\n        free(thread->stack_buffer);\n    }\n\n    furi_string_free(thread->output.buffer);\n    furi_string_free(thread->input.unread_buffer);\n    free(thread);\n}\n\nvoid furi_thread_set_name(FuriThread* thread, const char* name) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n\n    if(thread->name) {\n        free(thread->name);\n    }\n\n    thread->name = name ? strdup(name) : NULL;\n}\n\nvoid furi_thread_set_appid(FuriThread* thread, const char* appid) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n\n    if(thread->appid) {\n        free(thread->appid);\n    }\n\n    thread->appid = appid ? strdup(appid) : NULL;\n}\n\nvoid furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    furi_check(stack_size);\n    furi_check(stack_size <= THREAD_MAX_STACK_SIZE);\n    furi_check(stack_size % sizeof(StackType_t) == 0);\n    // Stack size cannot be configured for a thread that has been marked as a service\n    furi_check(thread->is_service == false);\n\n    if(thread->stack_buffer) {\n        free(thread->stack_buffer);\n    }\n\n    thread->stack_buffer = malloc(stack_size);\n    thread->stack_size = stack_size;\n}\n\nvoid furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->callback = callback;\n}\n\nvoid furi_thread_set_context(FuriThread* thread, void* context) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->context = context;\n}\n\nvoid furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    furi_check(priority <= FuriThreadPriorityIsr);\n    thread->priority = priority;\n}\n\nFuriThreadPriority furi_thread_get_priority(FuriThread* thread) {\n    furi_check(thread);\n    TaskHandle_t hTask = (TaskHandle_t)thread;\n    return (FuriThreadPriority)uxTaskPriorityGet(hTask);\n}\n\nvoid furi_thread_set_current_priority(FuriThreadPriority priority) {\n    furi_check(priority <= FuriThreadPriorityIsr);\n    vTaskPrioritySet(NULL, priority);\n}\n\nFuriThreadPriority furi_thread_get_current_priority(void) {\n    return (FuriThreadPriority)uxTaskPriorityGet(NULL);\n}\n\nvoid furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->state_callback = callback;\n}\n\nvoid furi_thread_set_state_context(FuriThread* thread, void* context) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->state_context = context;\n}\n\nFuriThreadState furi_thread_get_state(FuriThread* thread) {\n    furi_check(thread);\n    return thread->state;\n}\n\nvoid furi_thread_set_signal_callback(\n    FuriThread* thread,\n    FuriThreadSignalCallback callback,\n    void* context) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped || thread == furi_thread_get_current());\n\n    thread->signal_callback = callback;\n    thread->signal_context = context;\n}\n\nFuriThreadSignalCallback furi_thread_get_signal_callback(const FuriThread* thread) {\n    furi_check(thread);\n\n    return thread->signal_callback;\n}\n\nbool furi_thread_signal(const FuriThread* thread, uint32_t signal, void* arg) {\n    furi_check(thread);\n\n    bool is_consumed = false;\n\n    if(thread->signal_callback) {\n        is_consumed = thread->signal_callback(signal, arg, thread->signal_context);\n    }\n\n    return is_consumed;\n}\n\nvoid furi_thread_start(FuriThread* thread) {\n    furi_check(thread);\n    furi_check(thread->callback);\n    furi_check(thread->state == FuriThreadStateStopped);\n    furi_check(thread->stack_size > 0);\n\n    furi_thread_set_state(thread, FuriThreadStateStarting);\n\n    uint32_t stack_depth = thread->stack_size / sizeof(StackType_t);\n\n    furi_check(\n        xTaskCreateStatic(\n            furi_thread_body,\n            thread->name,\n            stack_depth,\n            thread,\n            thread->priority,\n            thread->stack_buffer,\n            &thread->container) == (TaskHandle_t)thread);\n}\n\nbool furi_thread_join(FuriThread* thread) {\n    furi_check(thread);\n    // Cannot join a service thread\n    furi_check(!thread->is_service);\n    // Cannot join a thread to itself\n    furi_check(furi_thread_get_current() != thread);\n\n    // !!! IMPORTANT NOTICE !!!\n    //\n    // If your thread exited, but your app stuck here: some other thread uses\n    // all cpu time, which delays kernel from releasing task handle\n    while(thread->state != FuriThreadStateStopped) {\n        furi_delay_tick(2);\n    }\n\n    return true;\n}\n\nFuriThreadId furi_thread_get_id(FuriThread* thread) {\n    furi_check(thread);\n    return (FuriThreadId)thread;\n}\n\nvoid furi_thread_enable_heap_trace(FuriThread* thread) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->heap_trace_enabled = true;\n}\n\nvoid furi_thread_disable_heap_trace(FuriThread* thread) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    thread->heap_trace_enabled = false;\n}\n\nsize_t furi_thread_get_heap_size(FuriThread* thread) {\n    furi_check(thread);\n    furi_check(thread->heap_trace_enabled == true);\n    return thread->heap_size;\n}\n\nint32_t furi_thread_get_return_code(FuriThread* thread) {\n    furi_check(thread);\n    furi_check(thread->state == FuriThreadStateStopped);\n    return thread->ret;\n}\n\nFuriThreadId furi_thread_get_current_id(void) {\n    return (FuriThreadId)xTaskGetCurrentTaskHandle();\n}\n\nFuriThread* furi_thread_get_current(void) {\n    FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);\n    return thread;\n}\n\nvoid furi_thread_yield(void) {\n    furi_check(!FURI_IS_IRQ_MODE());\n    taskYIELD();\n}\n\n/* Limits */\n#define MAX_BITS_TASK_NOTIFY  31U\n#define MAX_BITS_EVENT_GROUPS 24U\n\n#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))\n#define EVENT_FLAGS_INVALID_BITS  (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))\n\nuint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) {\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n    uint32_t rflags;\n    BaseType_t yield;\n\n    if((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {\n        rflags = (uint32_t)FuriStatusErrorParameter;\n    } else {\n        rflags = (uint32_t)FuriStatusError;\n\n        if(FURI_IS_IRQ_MODE()) {\n            yield = pdFALSE;\n\n            (void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield);\n            (void)xTaskNotifyAndQueryIndexedFromISR(\n                hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL);\n\n            portYIELD_FROM_ISR(yield);\n        } else {\n            (void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits);\n            (void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags);\n        }\n    }\n\n    furi_event_loop_thread_flag_callback(thread_id);\n\n    /* Return flags after setting */\n    return rflags;\n}\n\nuint32_t furi_thread_flags_clear(uint32_t flags) {\n    TaskHandle_t hTask;\n    uint32_t rflags, cflags;\n\n    if(FURI_IS_IRQ_MODE()) {\n        rflags = (uint32_t)FuriStatusErrorISR;\n    } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {\n        rflags = (uint32_t)FuriStatusErrorParameter;\n    } else {\n        hTask = xTaskGetCurrentTaskHandle();\n\n        if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) ==\n           pdPASS) {\n            rflags = cflags;\n            cflags &= ~flags;\n\n            if(xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=\n               pdPASS) {\n                rflags = (uint32_t)FuriStatusError;\n            }\n        } else {\n            rflags = (uint32_t)FuriStatusError;\n        }\n    }\n\n    /* Return flags before clearing */\n    return rflags;\n}\n\nuint32_t furi_thread_flags_get(void) {\n    TaskHandle_t hTask;\n    uint32_t rflags;\n\n    if(FURI_IS_IRQ_MODE()) {\n        rflags = (uint32_t)FuriStatusErrorISR;\n    } else {\n        hTask = xTaskGetCurrentTaskHandle();\n\n        if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=\n           pdPASS) {\n            rflags = (uint32_t)FuriStatusError;\n        }\n    }\n\n    return rflags;\n}\n\nuint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {\n    uint32_t rflags, nval;\n    uint32_t clear;\n    TickType_t t0, td, tout;\n    BaseType_t rval;\n\n    if(FURI_IS_IRQ_MODE()) {\n        rflags = (uint32_t)FuriStatusErrorISR;\n    } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {\n        rflags = (uint32_t)FuriStatusErrorParameter;\n    } else {\n        if((options & FuriFlagNoClear) == FuriFlagNoClear) {\n            clear = 0U;\n        } else {\n            clear = flags;\n        }\n\n        rflags = 0U;\n        tout = timeout;\n\n        t0 = xTaskGetTickCount();\n        do {\n            rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout);\n\n            if(rval == pdPASS) {\n                rflags &= flags;\n                rflags |= nval;\n\n                if((options & FuriFlagWaitAll) == FuriFlagWaitAll) {\n                    if((flags & rflags) == flags) {\n                        break;\n                    } else {\n                        if(timeout == 0U) {\n                            rflags = (uint32_t)FuriStatusErrorResource;\n                            break;\n                        }\n                    }\n                } else {\n                    if((flags & rflags) != 0) {\n                        break;\n                    } else {\n                        if(timeout == 0U) {\n                            rflags = (uint32_t)FuriStatusErrorResource;\n                            break;\n                        }\n                    }\n                }\n\n                /* Update timeout */\n                td = xTaskGetTickCount() - t0;\n\n                if(td > tout) {\n                    tout = 0;\n                } else {\n                    tout -= td;\n                }\n            } else {\n                if(timeout == 0) {\n                    rflags = (uint32_t)FuriStatusErrorResource;\n                } else {\n                    rflags = (uint32_t)FuriStatusErrorTimeout;\n                }\n            }\n        } while(rval != pdFAIL);\n    }\n\n    /* Return flags before clearing */\n    return rflags;\n}\n\nstatic const char* furi_thread_state_name(eTaskState state) {\n    switch(state) {\n    case eRunning:\n        return \"Running\";\n    case eReady:\n        return \"Ready\";\n    case eBlocked:\n        return \"Blocked\";\n    case eSuspended:\n        return \"Suspended\";\n    case eDeleted:\n        return \"Deleted\";\n    case eInvalid:\n        return \"Invalid\";\n    default:\n        return \"?\";\n    }\n}\n\nbool furi_thread_enumerate(FuriThreadList* thread_list) {\n    furi_check(thread_list);\n    furi_check(!FURI_IS_IRQ_MODE());\n\n    bool result = false;\n\n    vTaskSuspendAll();\n    do {\n        uint32_t tick = furi_get_tick();\n        uint32_t count = uxTaskGetNumberOfTasks();\n\n        TaskStatus_t* task = pvPortMalloc(count * sizeof(TaskStatus_t));\n\n        if(!task) break;\n\n        configRUN_TIME_COUNTER_TYPE total_run_time;\n        count = uxTaskGetSystemState(task, count, &total_run_time);\n        for(uint32_t i = 0U; i < count; i++) {\n            TaskControlBlock* tcb = (TaskControlBlock*)task[i].xHandle;\n\n            FuriThreadListItem* item =\n                furi_thread_list_get_or_insert(thread_list, (FuriThread*)task[i].xHandle);\n\n            FuriThreadId thread_id = (FuriThreadId)task[i].xHandle;\n            item->thread = (FuriThread*)thread_id;\n            item->app_id = furi_thread_get_appid(thread_id);\n            item->name = task[i].pcTaskName;\n            item->priority = task[i].uxCurrentPriority;\n            item->stack_address = (uint32_t)tcb->pxStack;\n            size_t thread_heap = memmgr_heap_get_thread_memory(thread_id);\n            item->heap = thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap;\n            item->stack_size = (tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t);\n            item->stack_min_free = furi_thread_get_stack_space(thread_id);\n            item->state = furi_thread_state_name(task[i].eCurrentState);\n            item->counter_previous = item->counter_current;\n            item->counter_current = task[i].ulRunTimeCounter;\n            item->tick = tick;\n        }\n\n        vPortFree(task);\n        furi_thread_list_process(thread_list, total_run_time, tick);\n\n        result = true;\n    } while(false);\n    (void)xTaskResumeAll();\n\n    return result;\n}\n\nconst char* furi_thread_get_name(FuriThreadId thread_id) {\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n    const char* name;\n\n    if(FURI_IS_IRQ_MODE() || (hTask == NULL)) {\n        name = NULL;\n    } else {\n        name = pcTaskGetName(hTask);\n    }\n\n    return name;\n}\n\nconst char* furi_thread_get_appid(FuriThreadId thread_id) {\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n    const char* appid = \"system\";\n\n    if(!FURI_IS_IRQ_MODE() && (hTask != NULL)) {\n        FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);\n        if(thread) {\n            appid = thread->appid;\n        }\n    }\n\n    return appid;\n}\n\nuint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n    uint32_t sz;\n\n    if(FURI_IS_IRQ_MODE() || (hTask == NULL)) {\n        sz = 0U;\n    } else {\n        sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));\n    }\n\n    return sz;\n}\n\nstatic size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {\n    if(thread->output.write_callback != NULL) {\n        thread->output.write_callback(data, size, thread->output.context);\n    } else {\n        furi_log_tx((const uint8_t*)data, size);\n    }\n    return size;\n}\n\nstatic size_t\n    __furi_thread_stdin_read(FuriThread* thread, char* data, size_t size, FuriWait timeout) {\n    if(thread->input.read_callback != NULL) {\n        return thread->input.read_callback(data, size, timeout, thread->input.context);\n    } else {\n        return 0;\n    }\n}\n\nstatic int32_t __furi_thread_stdout_flush(FuriThread* thread) {\n    FuriString* buffer = thread->output.buffer;\n    size_t size = furi_string_size(buffer);\n    if(size > 0) {\n        __furi_thread_stdout_write(thread, furi_string_get_cstr(buffer), size);\n        furi_string_reset(buffer);\n    }\n    return 0;\n}\n\nvoid furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n    furi_check(callback);\n    furi_check(context);\n    *callback = thread->output.write_callback;\n    *context = thread->output.context;\n}\n\nvoid furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n    furi_check(callback);\n    furi_check(context);\n    *callback = thread->input.read_callback;\n    *context = thread->input.context;\n}\n\nvoid furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n    __furi_thread_stdout_flush(thread);\n    thread->output.write_callback = callback;\n    thread->output.context = context;\n}\n\nvoid furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n    thread->input.read_callback = callback;\n    thread->input.context = context;\n}\n\nsize_t furi_thread_stdout_write(const char* data, size_t size) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n\n    if(size == 0 || data == NULL) {\n        return __furi_thread_stdout_flush(thread);\n    } else {\n        if(data[size - 1] == '\\n') {\n            // if the last character is a newline, we can flush buffer and write data as is, wo buffers\n            __furi_thread_stdout_flush(thread);\n            __furi_thread_stdout_write(thread, data, size);\n        } else {\n            // string_cat doesn't work here because we need to write the exact size data\n            for(size_t i = 0; i < size; i++) {\n                furi_string_push_back(thread->output.buffer, data[i]);\n                if(data[i] == '\\n') {\n                    __furi_thread_stdout_flush(thread);\n                }\n            }\n        }\n    }\n\n    return size;\n}\n\nint32_t furi_thread_stdout_flush(void) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n\n    return __furi_thread_stdout_flush(thread);\n}\n\nsize_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n\n    size_t from_buffer = MIN(furi_string_size(thread->input.unread_buffer), size);\n    size_t from_input = size - from_buffer;\n    size_t from_input_actual =\n        __furi_thread_stdin_read(thread, buffer + from_buffer, from_input, timeout);\n    memcpy(buffer, furi_string_get_cstr(thread->input.unread_buffer), from_buffer);\n    furi_string_right(thread->input.unread_buffer, from_buffer);\n\n    return from_buffer + from_input_actual;\n}\n\nvoid furi_thread_stdin_unread(char* buffer, size_t size) {\n    FuriThread* thread = furi_thread_get_current();\n    furi_check(thread);\n\n    FuriString* new_buf = furi_string_alloc(); // there's no furi_string_alloc_set_strn :(\n    furi_string_set_strn(new_buf, buffer, size);\n    furi_string_cat(new_buf, thread->input.unread_buffer);\n    furi_string_free(thread->input.unread_buffer);\n    thread->input.unread_buffer = new_buf;\n}\n\nvoid furi_thread_suspend(FuriThreadId thread_id) {\n    furi_check(thread_id);\n\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n\n    vTaskSuspend(hTask);\n}\n\nvoid furi_thread_resume(FuriThreadId thread_id) {\n    furi_check(thread_id);\n\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n\n    if(FURI_IS_IRQ_MODE()) {\n        xTaskResumeFromISR(hTask);\n    } else {\n        vTaskResume(hTask);\n    }\n}\n\nbool furi_thread_is_suspended(FuriThreadId thread_id) {\n    furi_check(thread_id);\n\n    TaskHandle_t hTask = (TaskHandle_t)thread_id;\n\n    return eTaskGetState(hTask) == eSuspended;\n}\n"
  },
  {
    "path": "furi/core/thread.h",
    "content": "/**\n * @file thread.h\n * @brief Furi: Furi Thread API\n */\n\n#pragma once\n\n#include \"base.h\"\n#include \"common_defines.h\"\n\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Enumeration of possible FuriThread states.\n *\n * Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED.\n */\ntypedef enum {\n    FuriThreadStateStopped, /**< Thread is stopped and is safe to release. Event delivered from system init thread(TCB cleanup routine). It is safe to release thread instance. */\n    FuriThreadStateStopping, /**< Thread is stopping. Event delivered from child thread. */\n    FuriThreadStateStarting, /**< Thread is starting. Event delivered from parent(self) thread. */\n    FuriThreadStateRunning, /**< Thread is running. Event delivered from child thread. */\n} FuriThreadState;\n\n/**\n * @brief Enumeration of possible FuriThread priorities.\n */\ntypedef enum {\n    FuriThreadPriorityIdle = 0, /**< Idle priority */\n    FuriThreadPriorityInit = 4, /**< Init System Thread Priority */\n    FuriThreadPriorityLowest = 14, /**< Lowest */\n    FuriThreadPriorityLow = 15, /**< Low */\n    FuriThreadPriorityNormal = 16, /**< Normal, system default */\n    FuriThreadPriorityHigh = 17, /**< High */\n    FuriThreadPriorityHighest = 18, /**< Highest */\n    FuriThreadPriorityIsr =\n        (FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */\n} FuriThreadPriority;\n\n/**\n * @brief FuriThread opaque type.\n */\ntypedef struct FuriThread FuriThread;\n\n/** FuriThreadList type */\ntypedef struct FuriThreadList FuriThreadList;\n\n/**\n * @brief Unique thread identifier type (used by the OS kernel).\n */\ntypedef void* FuriThreadId;\n\n/**\n * @brief Thread callback function pointer type.\n *\n * The function to be used as a thread callback MUST follow this signature.\n *\n * @param[in,out] context pointer to a user-specified object\n * @return value to be used as the thread return code\n */\ntypedef int32_t (*FuriThreadCallback)(void* context);\n\n/**\n * @brief Standard output callback function pointer type.\n *\n * The function to be used as a standard output callback MUST follow this signature.\n *\n * @warning The handler MUST process ALL of the provided data before returning.\n *\n * @param[in] data pointer to the data to be written to the standard out\n * @param[in] size size of the data in bytes\n * @param[in] context optional context\n */\ntypedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size, void* context);\n\n/**\n * @brief Standard input callback function pointer type\n * \n * The function to be used as a standard input callback MUST follow this signature.\n * \n * @param[out] buffer buffer to read data into\n * @param[in] size maximum number of bytes to read into the buffer\n * @param[in] timeout how long to wait for (in ticks) before giving up\n * @param[in] context optional context\n * @returns number of bytes that was actually read into the buffer\n */\ntypedef size_t (\n    *FuriThreadStdinReadCallback)(char* buffer, size_t size, FuriWait timeout, void* context);\n\n/**\n * @brief         State change callback function pointer type.\n *\n *                The function to be used as a state callback MUST follow this\n *                signature.\n *\n * @param[in]     thread   to the FuriThread instance that changed the state\n * @param[in]     state    identifier of the state the thread has transitioned\n *                         to\n * @param[in,out] context  pointer to a user-specified object\n */\ntypedef void (*FuriThreadStateCallback)(FuriThread* thread, FuriThreadState state, void* context);\n\n/**\n * @brief Signal handler callback function pointer type.\n *\n * The function to be used as a signal handler callback MUS follow this signature.\n *\n * @param[in] signal value of the signal to be handled by the recipient\n * @param[in,out] arg optional argument (can be of any value, including NULL)\n * @param[in,out] context pointer to a user-specified object\n * @returns true if the signal was handled, false otherwise\n */\ntypedef bool (*FuriThreadSignalCallback)(uint32_t signal, void* arg, void* context);\n\n/**\n * @brief Create a FuriThread instance.\n *\n * @return pointer to the created FuriThread instance\n */\nFuriThread* furi_thread_alloc(void);\n\n/**\n * @brief Create a FuriThread instance (service mode).\n *\n * Service threads are more memory efficient, but have\n * the following limitations:\n *\n * - Cannot return from the callback\n * - Cannot be joined or freed\n * - Stack size cannot be altered\n *\n * @param[in] name human-readable thread name (can be NULL)\n * @param[in] stack_size stack size in bytes (cannot be changed later)\n * @param[in] callback pointer to a function to be executed in this thread\n * @param[in] context pointer to a user-specified object (will be passed to the callback)\n * @return pointer to the created FuriThread instance\n */\nFuriThread* furi_thread_alloc_service(\n    const char* name,\n    uint32_t stack_size,\n    FuriThreadCallback callback,\n    void* context);\n\n/**\n * @brief Create a FuriThread instance w/ extra parameters.\n * \n * @param[in] name human-readable thread name (can be NULL)\n * @param[in] stack_size stack size in bytes (can be changed later)\n * @param[in] callback pointer to a function to be executed in this thread\n * @param[in] context pointer to a user-specified object (will be passed to the callback)\n * @return pointer to the created FuriThread instance\n */\nFuriThread* furi_thread_alloc_ex(\n    const char* name,\n    uint32_t stack_size,\n    FuriThreadCallback callback,\n    void* context);\n\n/**\n * @brief Delete a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @warning see furi_thread_join for caveats on stopping a thread.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be deleted\n */\nvoid furi_thread_free(FuriThread* thread);\n\n/**\n * @brief Set the name of a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] name human-readable thread name (can be NULL)\n */\nvoid furi_thread_set_name(FuriThread* thread, const char* name);\n\n/**\n * @brief Set the application ID of a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function.\n *\n * Technically, it is like a \"process id\", but it is not a system-wide unique identifier.\n * All threads spawned by the same app will have the same appid.\n * \n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] appid thread application ID (can be NULL)\n */\nvoid furi_thread_set_appid(FuriThread* thread, const char* appid);\n\n/**\n * @brief Set the stack size of a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function. Additionally, it is NOT possible\n * to change the stack size of a service thread under any circumstances.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] stack_size stack size in bytes\n */\nvoid furi_thread_set_stack_size(FuriThread* thread, size_t stack_size);\n\n/**\n * @brief Set the user callback function to be executed in a FuriThread.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] callback pointer to a user-specified function to be executed in this thread\n */\nvoid furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);\n\n/**\n * @brief Set the callback function context.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL)\n */\nvoid furi_thread_set_context(FuriThread* thread, void* context);\n\n/**\n * @brief Set the priority of a FuriThread.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] priority priority level value\n */\nvoid furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);\n\n/**\n * @brief Get the priority of a FuriThread.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return priority level value\n */\nFuriThreadPriority furi_thread_get_priority(FuriThread* thread);\n\n/**\n * @brief Set the priority of the current FuriThread.\n *\n * @param priority priority level value\n */\nvoid furi_thread_set_current_priority(FuriThreadPriority priority);\n\n/**\n * @brief Get the priority of the current FuriThread.\n *\n * @return priority level value\n */\nFuriThreadPriority furi_thread_get_current_priority(void);\n\n/**\n * Set the callback function to be executed upon a state thransition of a FuriThread.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] callback pointer to a user-specified callback function\n */\nvoid furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback);\n\n/**\n * @brief Set the state change callback context.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL)\n */\nvoid furi_thread_set_state_context(FuriThread* thread, void* context);\n\n/**\n * @brief Get the state of a FuriThread isntance.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return thread state value\n */\nFuriThreadState furi_thread_get_state(FuriThread* thread);\n\n/**\n * @brief Set a signal handler callback for a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function if calling it from another thread.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n * @param[in] callback pointer to a user-specified callback function\n * @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL)\n */\nvoid furi_thread_set_signal_callback(\n    FuriThread* thread,\n    FuriThreadSignalCallback callback,\n    void* context);\n\n/**\n * @brief Get a signal callback for a FuriThread instance.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return pointer to the callback function or NULL if none has been set\n */\nFuriThreadSignalCallback furi_thread_get_signal_callback(const FuriThread* thread);\n\n/**\n * @brief Send a signal to a FuriThread instance.\n *\n * @param[in] thread pointer to the FuriThread instance to be signaled\n * @param[in] signal signal value to be sent\n * @param[in,out] arg optional argument (can be of any value, including NULL)\n */\nbool furi_thread_signal(const FuriThread* thread, uint32_t signal, void* arg);\n\n/**\n * @brief Start a FuriThread instance.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be started\n */\nvoid furi_thread_start(FuriThread* thread);\n\n/**\n * @brief Wait for a FuriThread to exit.\n *\n * The thread callback function must return in order for the FuriThread instance to become joinable.\n *\n * @warning Use this method only when the CPU is not busy (i.e. when the\n *          Idle task receives control), otherwise it will wait forever.\n *\n * @param[in] thread pointer to the FuriThread instance to be joined\n * @return always true\n */\nbool furi_thread_join(FuriThread* thread);\n\n/**\n * @brief Get the unique identifier of a FuriThread instance.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return unique identifier value or NULL if thread is not running\n */\nFuriThreadId furi_thread_get_id(FuriThread* thread);\n\n/**\n * @brief Enable heap usage tracing for a FuriThread.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n */\nvoid furi_thread_enable_heap_trace(FuriThread* thread);\n\n/**\n * @brief Disable heap usage tracing for a FuriThread.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in,out] thread pointer to the FuriThread instance to be modified\n */\nvoid furi_thread_disable_heap_trace(FuriThread* thread);\n\n/**\n * @brief Get heap usage by a FuriThread instance.\n *\n * The heap trace MUST be enabled before callgin this function.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return heap usage in bytes\n */\nsize_t furi_thread_get_heap_size(FuriThread* thread);\n\n/**\n * @brief Get the return code of a FuriThread instance.\n *\n * This value is equal to the return value of the thread callback function.\n *\n * The thread MUST be stopped when calling this function.\n *\n * @param[in] thread pointer to the FuriThread instance to be queried\n * @return return code value\n */\nint32_t furi_thread_get_return_code(FuriThread* thread);\n\n/**\n * @brief Get the unique identifier of the current FuriThread.\n *\n * @return unique identifier value\n */\nFuriThreadId furi_thread_get_current_id(void);\n\n/**\n * @brief Get the FuriThread instance associated with the current thread.\n * \n * @return pointer to a FuriThread instance or NULL if this thread does not belong to Furi\n */\nFuriThread* furi_thread_get_current(void);\n\n/**\n * @brief Return control to the scheduler.\n */\nvoid furi_thread_yield(void);\n\n/**\n * @brief Set the thread flags of a FuriThread.\n *\n * Can be used as a simple inter-thread communication mechanism.\n *\n * @param[in] thread_id unique identifier of the thread to be notified\n * @param[in] flags bitmask of thread flags to set\n * @return bitmask combination of previous and newly set flags\n */\nuint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags);\n\n/**\n * @brief Clear the thread flags of the current FuriThread.\n *\n * @param[in] flags bitmask of thread flags to clear\n * @return bitmask of thread flags before clearing\n */\nuint32_t furi_thread_flags_clear(uint32_t flags);\n\n/**\n * @brief Get the thread flags of the current FuriThread.\n * @return current bitmask of thread flags\n */\nuint32_t furi_thread_flags_get(void);\n\n/**\n * @brief Wait for some thread flags to be set.\n *\n * @see FuriFlag for option and error flags.\n *\n * @param[in] flags bitmask of thread flags to wait for\n * @param[in] options combination of option flags determining the behavior of the function\n * @param[in] timeout maximum time to wait in milliseconds (use FuriWaitForever to wait forever)\n * @return bitmask combination of received thread and error flags\n */\nuint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);\n\n/**\n * @brief      Enumerate all threads.\n *\n * @param[out] thread_list  pointer to the FuriThreadList container\n *\n * @return     true on success, false otherwise\n */\nbool furi_thread_enumerate(FuriThreadList* thread_list);\n\n/**\n * @brief Get the name of a thread based on its unique identifier.\n * \n * @param[in] thread_id unique identifier of the thread to be queried\n * @return pointer to a zero-terminated string or NULL\n */\nconst char* furi_thread_get_name(FuriThreadId thread_id);\n\n/**\n * @brief Get the application id of a thread based on its unique identifier.\n * \n * @param[in] thread_id unique identifier of the thread to be queried\n * @return pointer to a zero-terminated string\n */\nconst char* furi_thread_get_appid(FuriThreadId thread_id);\n\n/**\n * @brief Get thread stack watermark.\n * \n * @param[in] thread_id unique identifier of the thread to be queried\n * @return stack watermark value\n */\nuint32_t furi_thread_get_stack_space(FuriThreadId thread_id);\n\n/**\n * @brief Get the standard output callback for the current thead.\n *\n * @param[out] callback where to store the stdout callback\n * @param[out] context where to store the context\n */\nvoid furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context);\n\n/**\n * @brief Get the standard input callback for the current thead.\n *\n * @param[out] callback where to store the stdin callback\n * @param[out] context where to store the context\n */\nvoid furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context);\n\n/** Set standard output callback for the current thread.\n *\n * @param[in] callback pointer to the callback function or NULL to clear\n * @param[in] context context to be passed to the callback\n */\nvoid furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context);\n\n/** Set standard input callback for the current thread.\n * \n * @param[in] callback pointer to the callback function or NULL to clear\n * @param[in] context context to be passed to the callback\n */\nvoid furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context);\n\n/** Write data to buffered standard output.\n * \n * @note You can also use the standard C `putc`, `puts`, `printf` and friends.\n * \n * @param[in] data pointer to the data to be written\n * @param[in] size data size in bytes\n * @return number of bytes that was actually written\n */\nsize_t furi_thread_stdout_write(const char* data, size_t size);\n\n/**\n * @brief Flush buffered data to standard output.\n * \n * @return error code value\n */\nint32_t furi_thread_stdout_flush(void);\n\n/** Read data from the standard input\n * \n * @note You can also use the standard C `getc`, `gets` and friends.\n * \n * @param[in] buffer pointer to the buffer to read data into\n * @param[in] size how many bytes to read into the buffer\n * @param[in] timeout how long to wait for (in ticks) before giving up\n * @return number of bytes that was actually read\n */\nsize_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout);\n\n/** Puts data back into the standard input buffer\n * \n * `furi_thread_stdin_read` will return the bytes in the same order that they\n * were supplied to this function.\n * \n * @note You can also use the standard C `ungetc`.\n * \n * @param[in] buffer pointer to the buffer to get data from\n * @param[in] size how many bytes to read from the buffer\n */\nvoid furi_thread_stdin_unread(char* buffer, size_t size);\n\n/**\n * @brief Suspend a thread.\n *\n * Suspended threads are no more receiving any of the processor time.\n * \n * @param[in] thread_id unique identifier of the thread to be suspended\n */\nvoid furi_thread_suspend(FuriThreadId thread_id);\n\n/**\n * @brief Resume a thread.\n * \n * @param[in] thread_id unique identifier of the thread to be resumed\n */\nvoid furi_thread_resume(FuriThreadId thread_id);\n\n/**\n * @brief Test if a thread is suspended.\n * \n * @param[in] thread_id unique identifier of the thread to be queried\n * @return true if thread is suspended, false otherwise\n */\nbool furi_thread_is_suspended(FuriThreadId thread_id);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/thread_i.h",
    "content": "#pragma once\n\n#include \"thread.h\"\n\nvoid furi_thread_init(void);\n\nvoid furi_thread_scrub(void);\n"
  },
  {
    "path": "furi/core/thread_list.c",
    "content": "#include \"thread_list.h\"\n#include \"check.h\"\n\n#include <furi_hal_interrupt.h>\n\n#include <m-array.h>\n#include <m-dict.h>\n\nARRAY_DEF(FuriThreadListItemArray, FuriThreadListItem*, M_PTR_OPLIST) // NOLINT\n\n#define M_OPL_FuriThreadListItemArray_t() ARRAY_OPLIST(FuriThreadListItemArray, M_PTR_OPLIST)\n\nDICT_DEF2(\n    FuriThreadListItemDict,\n    uint32_t,\n    M_DEFAULT_OPLIST,\n    FuriThreadListItem*,\n    M_PTR_OPLIST) // NOLINT\n\n#define M_OPL_FuriThreadListItemDict_t() \\\n    DICT_OPLIST(FuriThreadListItemDict, M_DEFAULT_OPLIST, M_PTR_OPLIST)\n\nstruct FuriThreadList {\n    FuriThreadListItemArray_t items;\n    FuriThreadListItemDict_t search;\n    uint32_t runtime_previous;\n    uint32_t runtime_current;\n    uint32_t isr_previous;\n    uint32_t isr_current;\n};\n\nFuriThreadList* furi_thread_list_alloc(void) {\n    FuriThreadList* instance = malloc(sizeof(FuriThreadList));\n\n    FuriThreadListItemArray_init(instance->items);\n    FuriThreadListItemDict_init(instance->search);\n\n    return instance;\n}\n\nvoid furi_thread_list_free(FuriThreadList* instance) {\n    furi_check(instance);\n\n    FuriThreadListItemArray_it_t it;\n    FuriThreadListItemArray_it(it, instance->items);\n    while(!FuriThreadListItemArray_end_p(it)) {\n        FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);\n        free(item);\n        FuriThreadListItemArray_next(it);\n    }\n\n    FuriThreadListItemDict_clear(instance->search);\n    FuriThreadListItemArray_clear(instance->items);\n\n    free(instance);\n}\n\nsize_t furi_thread_list_size(FuriThreadList* instance) {\n    furi_check(instance);\n    return FuriThreadListItemArray_size(instance->items);\n}\n\nFuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position) {\n    furi_check(instance);\n    furi_check(position < furi_thread_list_size(instance));\n\n    return *FuriThreadListItemArray_get(instance->items, position);\n}\n\nFuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread) {\n    furi_check(instance);\n\n    FuriThreadListItem** item_ptr = FuriThreadListItemDict_get(instance->search, (uint32_t)thread);\n    if(item_ptr) {\n        return *item_ptr;\n    }\n\n    FuriThreadListItem* item = malloc(sizeof(FuriThreadListItem));\n\n    FuriThreadListItemArray_push_back(instance->items, item);\n    FuriThreadListItemDict_set_at(instance->search, (uint32_t)thread, item);\n\n    return item;\n}\n\nvoid furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick) {\n    furi_assert(instance);\n\n    instance->runtime_previous = instance->runtime_current;\n    instance->runtime_current = runtime;\n\n    instance->isr_previous = instance->isr_current;\n    instance->isr_current = furi_hal_interrupt_get_time_in_isr_total();\n\n    const uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;\n\n    FuriThreadListItemArray_it_t it;\n    FuriThreadListItemArray_it(it, instance->items);\n    while(!FuriThreadListItemArray_end_p(it)) {\n        FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);\n        if(item->tick != tick) {\n            FuriThreadListItemArray_remove(instance->items, it);\n            (void)FuriThreadListItemDict_erase(instance->search, (uint32_t)item->thread);\n            free(item);\n        } else {\n            uint32_t item_counter = item->counter_current - item->counter_previous;\n            if(item_counter && item->counter_previous && item->counter_current) {\n                item->cpu = (float)item_counter / (float)runtime_counter * 100.0f;\n                if(item->cpu > 200.0f) item->cpu = 0.0f;\n            } else {\n                item->cpu = 0.0f;\n            }\n\n            FuriThreadListItemArray_next(it);\n        }\n    }\n}\n\nfloat furi_thread_list_get_isr_time(FuriThreadList* instance) {\n    const uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;\n    const uint32_t isr_counter = instance->isr_current - instance->isr_previous;\n\n    return (float)isr_counter / (float)runtime_counter;\n}\n"
  },
  {
    "path": "furi/core/thread_list.h",
    "content": "#pragma once\n\n#include \"base.h\"\n#include \"common_defines.h\"\n#include \"thread.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    FuriThread* thread; /**< Pointer to FuriThread, valid while it is running */\n    const char* app_id; /**< Thread application id, valid while it is running */\n    const char* name; /**< Thread name, valid while it is running */\n    FuriThreadPriority priority; /**< Thread priority */\n    uint32_t stack_address; /**< Thread stack address */\n    size_t heap; /**< Thread heap size if tracking enabled, 0 - otherwise */\n    uint32_t stack_size; /**< Thread stack size */\n    uint32_t stack_min_free; /**< Thread minimum of the stack size ever reached */\n    const char*\n        state; /**< Thread state, can be: \"Running\", \"Ready\", \"Blocked\", \"Suspended\", \"Deleted\", \"Invalid\" */\n    float cpu; /**< Thread CPU usage time in percents (including interrupts happened while running) */\n\n    // Service variables\n    uint32_t counter_previous; /**< Thread previous runtime counter */\n    uint32_t counter_current; /**< Thread current runtime counter */\n    uint32_t tick; /**< Thread last seen tick */\n} FuriThreadListItem;\n\n/** Anonymous FuriThreadList type */\ntypedef struct FuriThreadList FuriThreadList;\n\n/** Allocate FuriThreadList instance\n *\n * @return     FuriThreadList instance\n */\nFuriThreadList* furi_thread_list_alloc(void);\n\n/** Free FuriThreadList instance\n *\n * @param      instance  The FuriThreadList instance to free\n */\nvoid furi_thread_list_free(FuriThreadList* instance);\n\n/** Get FuriThreadList instance size\n *\n * @param      instance  The instance\n *\n * @return     Item count\n */\nsize_t furi_thread_list_size(FuriThreadList* instance);\n\n/** Get item at position\n *\n * @param      instance  The FuriThreadList instance\n * @param[in]  position  The position of the item\n *\n * @return     The FuriThreadListItem instance\n */\nFuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position);\n\n/** Get item by thread FuriThread pointer\n *\n * @param      instance  The FuriThreadList instance\n * @param      thread    The FuriThread pointer\n *\n * @return     The FuriThreadListItem instance\n */\nFuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread);\n\n/** Get percent of time spent in ISR\n *\n * @param      instance  The instance\n *\n * @return     percent of time spent in ISR\n */\nfloat furi_thread_list_get_isr_time(FuriThreadList* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/thread_list_i.h",
    "content": "#pragma once\n\n#include \"thread_list.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Process items in the FuriThreadList instance\n *\n * @param      instance  The instance\n * @param[in]  runtime   The runtime of the system since start\n * @param[in]  tick      The tick when processing happened\n */\nvoid furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/core/timer.c",
    "content": "#include \"timer.h\"\n#include \"check.h\"\n#include \"kernel.h\"\n\n#include <FreeRTOS.h>\n#include <event_groups.h>\n#include <timers.h>\n\nstruct FuriTimer {\n    StaticTimer_t container;\n    FuriTimerCallback cb_func;\n    void* cb_context;\n};\n\n// IMPORTANT: container MUST be the FIRST struct member\nstatic_assert(offsetof(FuriTimer, container) == 0);\n\n#define TIMER_DELETED_EVENT (1U << 0)\n\nstatic void furi_timer_callback(TimerHandle_t hTimer) {\n    FuriTimer* instance = pvTimerGetTimerID(hTimer);\n    furi_check(instance);\n    instance->cb_func(instance->cb_context);\n}\n\nstatic void furi_timer_flush_epilogue(void* context, uint32_t arg) {\n    furi_assert(context);\n    UNUSED(arg);\n\n    EventGroupHandle_t hEvent = context;\n\n    // See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/1142\n    vTaskSuspendAll();\n    xEventGroupSetBits(hEvent, TIMER_DELETED_EVENT);\n    (void)xTaskResumeAll();\n}\n\nFuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {\n    furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));\n\n    FuriTimer* instance = malloc(sizeof(FuriTimer));\n\n    instance->cb_func = func;\n    instance->cb_context = context;\n\n    const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE);\n    const TimerHandle_t hTimer = xTimerCreateStatic(\n        NULL, portMAX_DELAY, reload, instance, furi_timer_callback, &instance->container);\n\n    furi_check(hTimer == (TimerHandle_t)instance);\n\n    return instance;\n}\n\nvoid furi_timer_free(FuriTimer* instance) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n    furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);\n\n    furi_timer_flush();\n\n    free(instance);\n}\n\nvoid furi_timer_flush(void) {\n    StaticEventGroup_t event_container = {};\n    EventGroupHandle_t hEvent = xEventGroupCreateStatic(&event_container);\n    furi_check(\n        xTimerPendFunctionCall(furi_timer_flush_epilogue, hEvent, 0, portMAX_DELAY) == pdPASS);\n\n    furi_check(\n        xEventGroupWaitBits(hEvent, TIMER_DELETED_EVENT, pdFALSE, pdTRUE, portMAX_DELAY) ==\n        TIMER_DELETED_EVENT);\n    vEventGroupDelete(hEvent);\n}\n\nFuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n    furi_check(ticks < portMAX_DELAY);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n    FuriStatus stat;\n\n    if(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) {\n        stat = FuriStatusOk;\n    } else {\n        stat = FuriStatusErrorResource;\n    }\n\n    return stat;\n}\n\nFuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n    furi_check(ticks < portMAX_DELAY);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n    FuriStatus stat;\n\n    if(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS &&\n       xTimerReset(hTimer, portMAX_DELAY) == pdPASS) {\n        stat = FuriStatusOk;\n    } else {\n        stat = FuriStatusErrorResource;\n    }\n\n    return stat;\n}\n\nFuriStatus furi_timer_stop(FuriTimer* instance) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n\n    furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);\n\n    return FuriStatusOk;\n}\n\nuint32_t furi_timer_is_running(FuriTimer* instance) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n\n    /* Return 0: not running, 1: running */\n    return (uint32_t)xTimerIsTimerActive(hTimer);\n}\n\nuint32_t furi_timer_get_expire_time(FuriTimer* instance) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(instance);\n\n    TimerHandle_t hTimer = (TimerHandle_t)instance;\n\n    return (uint32_t)xTimerGetExpiryTime(hTimer);\n}\n\nvoid furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) {\n    furi_check(callback);\n\n    BaseType_t ret = pdFAIL;\n    if(furi_kernel_is_irq_or_masked()) {\n        ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);\n    } else {\n        ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever);\n    }\n\n    furi_check(ret == pdPASS);\n}\n\nvoid furi_timer_set_thread_priority(FuriTimerThreadPriority priority) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n\n    TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();\n    furi_check(task_handle); // Don't call this method before timer task start\n\n    if(priority == FuriTimerThreadPriorityNormal) {\n        vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY);\n    } else if(priority == FuriTimerThreadPriorityElevated) {\n        vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1);\n    } else {\n        furi_crash();\n    }\n}\n"
  },
  {
    "path": "furi/core/timer.h",
    "content": "/**\n * @file timer.h\n * @brief Furi software Timer API.\n */\n#pragma once\n\n#include \"base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*FuriTimerCallback)(void* context);\n\ntypedef enum {\n    FuriTimerTypeOnce = 0, ///< One-shot timer.\n    FuriTimerTypePeriodic = 1 ///< Repeating timer.\n} FuriTimerType;\n\ntypedef struct FuriTimer FuriTimer;\n\n/** Allocate timer\n *\n * @param[in]  func     The callback function\n * @param[in]  type     The timer type\n * @param      context  The callback context\n *\n * @return     The pointer to FuriTimer instance\n */\nFuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context);\n\n/** Free timer\n *\n * @param      instance  The pointer to FuriTimer instance\n */\nvoid furi_timer_free(FuriTimer* instance);\n\n/** Flush timer task control message queue\n *\n * Ensures that all commands before this point was processed.\n */\nvoid furi_timer_flush(void);\n\n/** Start timer\n *\n * @warning    This is asynchronous call, real operation will happen as soon as\n *             timer service process this request.\n *\n * @param      instance  The pointer to FuriTimer instance\n * @param[in]  ticks     The interval in ticks\n *\n * @return     The furi status.\n */\nFuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks);\n\n/** Restart timer with previous timeout value\n *\n * @warning    This is asynchronous call, real operation will happen as soon as\n *             timer service process this request.\n *\n * @param      instance  The pointer to FuriTimer instance\n * @param[in]  ticks     The interval in ticks\n *\n * @return     The furi status.\n */\nFuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks);\n\n/** Stop timer\n *\n * @warning    This is synchronous call that will be blocked till timer queue processed.\n *\n * @param      instance  The pointer to FuriTimer instance\n *\n * @return     The furi status.\n */\nFuriStatus furi_timer_stop(FuriTimer* instance);\n\n/** Is timer running\n *\n * @warning    This cal may and will return obsolete timer state if timer\n *             commands are still in the queue. Please read FreeRTOS timer\n *             documentation first.\n *\n * @param      instance  The pointer to FuriTimer instance\n *\n * @return     0: not running, 1: running\n */\nuint32_t furi_timer_is_running(FuriTimer* instance);\n\n/** Get timer expire time\n *\n * @param      instance  The Timer instance\n *\n * @return     expire tick\n */\nuint32_t furi_timer_get_expire_time(FuriTimer* instance);\n\ntypedef void (*FuriTimerPendigCallback)(void* context, uint32_t arg);\n\nvoid furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg);\n\ntypedef enum {\n    FuriTimerThreadPriorityNormal, /**< Lower then other threads */\n    FuriTimerThreadPriorityElevated, /**< Same as other threads */\n} FuriTimerThreadPriority;\n\n/** Set Timer thread priority\n *\n * @param[in]  priority  The priority\n */\nvoid furi_timer_set_thread_priority(FuriTimerThreadPriority priority);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "furi/flipper.c",
    "content": "#include \"flipper.h\"\n#include <applications.h>\n#include <furi.h>\n#include <furi_hal_version.h>\n#include <furi_hal_memory.h>\n#include <furi_hal_rtc.h>\n\n#include <FreeRTOS.h>\n\n#define TAG \"Flipper\"\n\n#define HEAP_CANARY_VALUE 0x8BADF00D\n\nstatic void flipper_print_version(const char* target, const Version* version) {\n    if(version) {\n        FURI_LOG_I(\n            TAG,\n            \"\\r\\n\\t%s version:\\t%s\\r\\n\"\n            \"\\tBuild date:\\t\\t%s\\r\\n\"\n            \"\\tGit Commit:\\t\\t%s (%s)%s\\r\\n\"\n            \"\\tGit Branch:\\t\\t%s\",\n            target,\n            version_get_version(version),\n            version_get_builddate(version),\n            version_get_githash(version),\n            version_get_gitbranchnum(version),\n            version_get_dirty_flag(version) ? \" (dirty)\" : \"\",\n            version_get_gitbranch(version));\n    } else {\n        FURI_LOG_I(TAG, \"No build info for %s\", target);\n    }\n}\n\nvoid flipper_init(void) {\n    flipper_print_version(\"Firmware\", furi_hal_version_get_firmware_version());\n\n    FURI_LOG_I(TAG, \"Boot mode %d, starting services\", furi_hal_rtc_get_boot_mode());\n\n    for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) {\n        FURI_LOG_D(TAG, \"Starting service %s\", FLIPPER_SERVICES[i].name);\n\n        FuriThread* thread = furi_thread_alloc_service(\n            FLIPPER_SERVICES[i].name,\n            FLIPPER_SERVICES[i].stack_size,\n            FLIPPER_SERVICES[i].app,\n            NULL);\n        furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid);\n\n        furi_thread_start(thread);\n    }\n\n    FURI_LOG_I(TAG, \"Startup complete\");\n}\n\nvoid vApplicationGetIdleTaskMemory(\n    StaticTask_t** tcb_ptr,\n    StackType_t** stack_ptr,\n    uint32_t* stack_size) {\n    *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));\n    *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH);\n    *stack_size = configIDLE_TASK_STACK_DEPTH;\n}\n\nvoid vApplicationGetTimerTaskMemory(\n    StaticTask_t** tcb_ptr,\n    StackType_t** stack_ptr,\n    uint32_t* stack_size) {\n    *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));\n    *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH);\n    *stack_size = configTIMER_TASK_STACK_DEPTH;\n}\n\nvoid vApplicationGetRandomHeapCanary(portPOINTER_SIZE_TYPE* pxHeapCanary) {\n    *pxHeapCanary = HEAP_CANARY_VALUE;\n}\n"
  },
  {
    "path": "furi/flipper.h",
    "content": "#pragma once\n\nvoid flipper_init(void);\n"
  },
  {
    "path": "furi/furi.c",
    "content": "#include \"furi.h\"\n\n#include \"core/thread_i.h\"\n\n#include <FreeRTOS.h>\n#include <queue.h>\n\nvoid furi_init(void) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED);\n\n    furi_thread_init();\n    furi_log_init();\n    furi_record_init();\n}\n\nvoid furi_run(void) {\n    furi_check(!furi_kernel_is_irq_or_masked());\n    furi_check(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED);\n\n    /* Start the kernel scheduler */\n    vTaskStartScheduler();\n}\n\nvoid furi_background(void) {\n    furi_thread_scrub();\n}\n"
  },
  {
    "path": "furi/furi.h",
    "content": "#pragma once\n\n#include <stdlib.h>\n\n#include \"core/common_defines.h\"\n#include \"core/check.h\"\n#include \"core/event_loop.h\"\n#include \"core/event_loop_timer.h\"\n#include \"core/event_flag.h\"\n#include \"core/kernel.h\"\n#include \"core/log.h\"\n#include \"core/memmgr.h\"\n#include \"core/memmgr_heap.h\"\n#include \"core/message_queue.h\"\n#include \"core/mutex.h\"\n#include \"core/pubsub.h\"\n#include \"core/record.h\"\n#include \"core/semaphore.h\"\n#include \"core/thread.h\"\n#include \"core/thread_list.h\"\n#include \"core/timer.h\"\n#include \"core/string.h\"\n#include \"core/stream_buffer.h\"\n\n#include <furi_hal_gpio.h>\n\n// Workaround for math.h leaking through HAL in older versions\n#include <math.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid furi_init(void);\n\nvoid furi_run(void);\n\nvoid furi_background(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/FreeRTOS-glue/task_control_block.h",
    "content": "#pragma once\n\n#include <FreeRTOS.h>\n#include <task.h>\n\ntypedef struct {\n    volatile StackType_t * pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */\n\n    #if ( portUSING_MPU_WRAPPERS == 1 )\n        xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */\n    #endif\n\n    ListItem_t xStateListItem;                  /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */\n    ListItem_t xEventListItem;                  /*< Used to reference a task from an event list. */\n    UBaseType_t uxPriority;                     /*< The priority of the task.  0 is the lowest priority. */\n    StackType_t * pxStack;                      /*< Points to the start of the stack. */\n    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */\n\n    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )\n        StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */\n    #endif\n\n    #if ( portCRITICAL_NESTING_IN_TCB == 1 )\n        UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */\n    #endif\n\n    #if ( configUSE_TRACE_FACILITY == 1 )\n        UBaseType_t uxTCBNumber;  /*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */\n        UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */\n    #endif\n\n    #if ( configUSE_MUTEXES == 1 )\n        UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */\n        UBaseType_t uxMutexesHeld;\n    #endif\n\n    #if ( configUSE_APPLICATION_TASK_TAG == 1 )\n        TaskHookFunction_t pxTaskTag;\n    #endif\n\n    #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )\n        void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];\n    #endif\n\n    #if ( configGENERATE_RUN_TIME_STATS == 1 )\n        configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */\n    #endif\n\n    #if ( configUSE_NEWLIB_REENTRANT == 1 )\n\n        /* Allocate a Newlib reent structure that is specific to this task.\n         * Note Newlib support has been included by popular demand, but is not\n         * used by the FreeRTOS maintainers themselves.  FreeRTOS is not\n         * responsible for resulting newlib operation.  User must be familiar with\n         * newlib and must provide system-wide implementations of the necessary\n         * stubs. Be warned that (at the time of writing) the current newlib design\n         * implements a system-wide malloc() that must be provided with locks.\n         *\n         * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html\n         * for additional information. */\n        struct  _reent xNewLib_reent;\n    #endif\n\n    #if ( configUSE_TASK_NOTIFICATIONS == 1 )\n        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];\n        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];\n    #endif\n\n    /* See the comments in FreeRTOS.h with the definition of\n     * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */\n    #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */\n        uint8_t ucStaticallyAllocated;                     /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */\n    #endif\n\n    #if ( INCLUDE_xTaskAbortDelay == 1 )\n        uint8_t ucDelayAborted;\n    #endif\n\n    #if ( configUSE_POSIX_ERRNO == 1 )\n        int iTaskErrno;\n    #endif\n} TaskControlBlock;\n"
  },
  {
    "path": "lib/ReadMe.md",
    "content": "# Structure\n\n- `app-scened-template` - C++ app library\n- `bit_lib`             - library for working with bits/bytes directly\n- `ble_profile`         - BLE Profiles source code\n- `cmsis_core`          - CMSIS Core package, contain cortex-m core headers\n- `cxxheaderparser`     - C++ headers parser, used by SDK bundler\n- `datetime`            - DateTime library\n- `digital_signal`      - Digital signal library: used by NFC for software implemented protocols\n- `drivers`             - Various flipper drivers\n- `fatfs`               - FatFS file system driver\n- `flipper_application` - Flipper application library, used for FAPs\n- `flipper_format`      - Flipper File Format library\n- `FreeRTOS-glue`       - Extra glue to hold together FreeRTOS kernel and flipper firmware\n- `FreeRTOS-Kernel`     - FreeRTOS kernel source code\n- `heatshrink`          - Heatshrink compression library\n- `ibutton`             - ibutton library, used by iButton application\n- `infrared`            - Infrared library, used by Infrared application\n- `lfrfid`              - LF-RFID library, used by LF RFID application\n- `libusb_stm32`        - LibUSB for STM32 series MCU\n- `mbedtls`             - MbedTLS cryptography library\n- `microtar`            - MicroTAR library\n- `mjs`                 - MJs, javascript engine library\n- `mlib`                - M-Lib C containers library\n- `music_worker`        - MusicWorker library for playing midi and RTTTL files\n- `nanopb`              - NanoPB library, protobuf implementation for MCU\n- `nfc`                 - NFC library, used by NFC application\n- `one_wire`            - OneWire library, used by iButton application\n- `print`               - Tiny printf implementation\n- `digital_signal`      - Digital Signal library used by NFC for software implemented protocols\n- `pulse_reader`        - Pulse Reader library used by NFC for software implemented protocols\n- `stm32wb_cmsis`       - STM32WB series CMSIS headers, extends CMSIS Core\n- `stm32wb_copro`       - STM32WB Copro library: contains WPAN and radio co-processor firmware\n- `stm32wb_hal`         - STM32WB HAL library, extends STM32WB CMSIS and provides HAL\n- `subghz`              - Subghz library, used by SubGhz application\n- `toolbox`             - Toolbox library, contains various things that is used by Flipper firmware\n- `u8g2`                - u8g2 graphics library, used by GUI subsystem\n- `update_util`         - update utilities library, used by updater\n"
  },
  {
    "path": "lib/SConscript",
    "content": "Import(\"env\")\n\n\nenv.Append(\n    CPPPATH=[\n        \"#/\",\n        \"#/lib\",\n        # Ugly hack\n        Dir(\"../assets/compiled\"),\n    ],\n)\n\n\nlibs = env.BuildModules(\n    [\n        \"mlib\",\n        \"stm32wb\",\n        \"freertos\",\n        \"print\",\n        \"microtar\",\n        \"mbedtls\",\n        \"toolbox\",\n        \"libusb_stm32\",\n        \"drivers\",\n        \"fatfs\",\n        \"flipper_format\",\n        \"one_wire\",\n        \"ibutton\",\n        \"infrared\",\n        \"subghz\",\n        \"nfc\",\n        \"digital_signal\",\n        \"pulse_reader\",\n        \"signal_reader\",\n        \"u8g2\",\n        \"lfrfid\",\n        \"flipper_application\",\n        \"music_worker\",\n        \"mjs\",\n        \"nanopb\",\n        \"update_util\",\n        \"heatshrink\",\n        \"ble_profile\",\n        \"bit_lib\",\n        \"datetime\",\n        \"ieee754_parse_wrap\",\n    ],\n)\n\nReturn(\"libs\")\n"
  },
  {
    "path": "lib/bit_lib/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    CPPPATH=[\n        \"#/lib/bit_lib\",\n    ],\n    SDK_HEADERS=[\n        File(\"bit_lib.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"bit_lib\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/bit_lib/bit_lib.c",
    "content": "#include \"bit_lib.h\"\n#include <core/check.h>\n#include <stdio.h>\n\nvoid bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit) {\n    size_t last_index = data_size - 1;\n\n    for(size_t i = 0; i < last_index; ++i) {\n        data[i] = (data[i] << 1) | ((data[i + 1] >> 7) & 1);\n    }\n    data[last_index] = (data[last_index] << 1) | bit;\n}\n\nvoid bit_lib_set_bit(uint8_t* data, size_t position, bool bit) {\n    if(bit) {\n        data[position / 8] |= 1UL << (7 - (position % 8));\n    } else {\n        data[position / 8] &= ~(1UL << (7 - (position % 8)));\n    }\n}\n\nvoid bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length) {\n    furi_check(length <= 8);\n    furi_check(length > 0);\n\n    for(uint8_t i = 0; i < length; ++i) {\n        uint8_t shift = (length - 1) - i;\n        bit_lib_set_bit(data, position + i, (byte >> shift) & 1); //-V610\n    }\n}\n\nbool bit_lib_get_bit(const uint8_t* data, size_t position) {\n    return (data[position / 8] >> (7 - (position % 8))) & 1;\n}\n\nuint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) {\n    uint8_t shift = position % 8;\n    if(shift == 0) {\n        return data[position / 8] >> (8 - length);\n    } else {\n        // TODO FL-3534: fix read out of bounds\n        uint8_t value = (data[position / 8] << (shift));\n        value |= data[position / 8 + 1] >> (8 - shift);\n        value = value >> (8 - length);\n        return value;\n    }\n}\n\nuint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length) {\n    uint16_t value = 0;\n    if(length <= 8) {\n        value = bit_lib_get_bits(data, position, length);\n    } else {\n        value = bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= bit_lib_get_bits(data, position + 8, length - 8);\n    }\n    return value;\n}\n\nuint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length) {\n    uint32_t value = 0;\n    if(length <= 8) {\n        value = bit_lib_get_bits(data, position, length);\n    } else if(length <= 16) {\n        value = bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= bit_lib_get_bits(data, position + 8, length - 8);\n    } else if(length <= 24) {\n        value = bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= bit_lib_get_bits(data, position + 16, length - 16);\n    } else {\n        value = (uint32_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint32_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint32_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= bit_lib_get_bits(data, position + 24, length - 24);\n    }\n\n    return value;\n}\n\nuint64_t bit_lib_get_bits_64(const uint8_t* data, size_t position, uint8_t length) {\n    uint64_t value = 0;\n    if(length <= 8) {\n        value = bit_lib_get_bits(data, position, length);\n    } else if(length <= 16) {\n        value = bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= bit_lib_get_bits(data, position + 8, length - 8);\n    } else if(length <= 24) {\n        value = bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= bit_lib_get_bits(data, position + 16, length - 16);\n    } else if(length <= 32) {\n        value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= bit_lib_get_bits(data, position + 24, length - 24);\n    } else if(length <= 40) {\n        value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 24, 8) << (length - 32);\n        value |= bit_lib_get_bits(data, position + 32, length - 32);\n    } else if(length <= 48) {\n        value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 24, 8) << (length - 32);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 32, 8) << (length - 40);\n        value |= bit_lib_get_bits(data, position + 40, length - 40);\n    } else if(length <= 56) {\n        value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 24, 8) << (length - 32);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 32, 8) << (length - 40);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 40, 8) << (length - 48);\n        value |= bit_lib_get_bits(data, position + 48, length - 48);\n    } else {\n        value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 24, 8) << (length - 32);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 32, 8) << (length - 40);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 40, 8) << (length - 48);\n        value |= (uint64_t)bit_lib_get_bits(data, position + 48, 8) << (length - 56);\n        value |= bit_lib_get_bits(data, position + 56, length - 56);\n    }\n\n    return value;\n}\n\nbool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity) {\n#if !defined __GNUC__\n#error Please, implement parity test for non-GCC compilers\n#else\n    switch(parity) {\n    case BitLibParityEven:\n        return __builtin_parity(bits);\n    case BitLibParityOdd:\n        return !__builtin_parity(bits);\n    default:\n        furi_crash(\"Unknown parity\");\n    }\n#endif\n}\n\nbool bit_lib_test_parity(\n    const uint8_t* bits,\n    size_t position,\n    uint8_t length,\n    BitLibParity parity,\n    uint8_t parity_length) {\n    uint32_t parity_block;\n    bool result = true;\n    const size_t parity_blocks_count = length / parity_length;\n\n    for(size_t i = 0; i < parity_blocks_count; ++i) {\n        switch(parity) {\n        case BitLibParityEven:\n        case BitLibParityOdd:\n            parity_block = bit_lib_get_bits_32(bits, position + i * parity_length, parity_length);\n            if(!bit_lib_test_parity_32(parity_block, parity)) {\n                result = false;\n            }\n            break;\n        case BitLibParityAlways0:\n            if(bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {\n                result = false;\n            }\n            break;\n        case BitLibParityAlways1:\n            if(!bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {\n                result = false;\n            }\n            break;\n        }\n\n        if(!result) break;\n    }\n    return result;\n}\n\nsize_t bit_lib_add_parity(\n    const uint8_t* data,\n    size_t position,\n    uint8_t* dest,\n    size_t dest_position,\n    uint8_t source_length,\n    uint8_t parity_length,\n    BitLibParity parity) {\n    uint32_t parity_word = 0;\n    size_t j = 0, bit_count = 0;\n    for(int word = 0; word < source_length; word += parity_length - 1) {\n        for(int bit = 0; bit < parity_length - 1; bit++) {\n            parity_word = (parity_word << 1) | bit_lib_get_bit(data, position + word + bit);\n            bit_lib_set_bit(\n                dest, dest_position + j++, bit_lib_get_bit(data, position + word + bit));\n        }\n        // if parity fails then return 0\n        switch(parity) {\n        case BitLibParityAlways0:\n            bit_lib_set_bit(dest, dest_position + j++, 0);\n            break; // marker bit which should be a 0\n        case BitLibParityAlways1:\n            bit_lib_set_bit(dest, dest_position + j++, 1);\n            break; // marker bit which should be a 1\n        default:\n            bit_lib_set_bit(\n                dest,\n                dest_position + j++,\n                (bit_lib_test_parity_32(parity_word, BitLibParityOdd) ^ parity) ^ 1);\n            break;\n        }\n        bit_count += parity_length;\n        parity_word = 0;\n    }\n    // if we got here then all the parities passed\n    // return bit count\n    return bit_count;\n}\n\nsize_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n) {\n    size_t counter = 0;\n    size_t result_counter = 0;\n    uint8_t bit_buffer = 0;\n    uint8_t bit_counter = 0;\n\n    while(counter < length) {\n        if((counter + 1) % n != 0) {\n            bit_buffer = (bit_buffer << 1) | bit_lib_get_bit(data, position + counter);\n            bit_counter++;\n        }\n\n        if(bit_counter == 8) {\n            bit_lib_set_bits(data, position + result_counter, bit_buffer, 8);\n            bit_counter = 0;\n            bit_buffer = 0;\n            result_counter += 8;\n        }\n        counter++;\n    }\n\n    if(bit_counter != 0) {\n        bit_lib_set_bits(data, position + result_counter, bit_buffer, bit_counter);\n        result_counter += bit_counter;\n    }\n    return result_counter;\n}\n\nvoid bit_lib_copy_bits(\n    uint8_t* data,\n    size_t position,\n    size_t length,\n    const uint8_t* source,\n    size_t source_position) {\n    for(size_t i = 0; i < length; ++i) {\n        bit_lib_set_bit(data, position + i, bit_lib_get_bit(source, source_position + i));\n    }\n}\n\nvoid bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length) {\n    size_t i = 0;\n    size_t j = length - 1;\n\n    while(i < j) {\n        bool tmp = bit_lib_get_bit(data, position + i);\n        bit_lib_set_bit(data, position + i, bit_lib_get_bit(data, position + j));\n        bit_lib_set_bit(data, position + j, tmp);\n        i++;\n        j--;\n    }\n}\n\nuint8_t bit_lib_get_bit_count(uint32_t data) {\n#if defined __GNUC__\n    return __builtin_popcountl(data);\n#else\n#error Please, implement popcount for non-GCC compilers\n#endif\n}\n\nvoid bit_lib_print_bits(const uint8_t* data, size_t length) {\n    for(size_t i = 0; i < length; ++i) {\n        printf(\"%u\", bit_lib_get_bit(data, i));\n    }\n}\n\nvoid bit_lib_print_regions(\n    const BitLibRegion* regions,\n    size_t region_count,\n    const uint8_t* data,\n    size_t length) {\n    // print data\n    bit_lib_print_bits(data, length);\n    printf(\"\\r\\n\");\n\n    // print regions\n    for(size_t c = 0; c < length; ++c) {\n        bool print = false;\n\n        for(size_t i = 0; i < region_count; i++) {\n            if(regions[i].start <= c && c < regions[i].start + regions[i].length) {\n                print = true;\n                printf(\"%c\", regions[i].mark);\n                break;\n            }\n        }\n\n        if(!print) {\n            printf(\" \");\n        }\n    }\n    printf(\"\\r\\n\");\n\n    // print regions data\n    for(size_t c = 0; c < length; ++c) {\n        bool print = false;\n\n        for(size_t i = 0; i < region_count; i++) {\n            if(regions[i].start <= c && c < regions[i].start + regions[i].length) {\n                print = true;\n                printf(\"%u\", bit_lib_get_bit(data, c));\n                break;\n            }\n        }\n\n        if(!print) {\n            printf(\" \");\n        }\n    }\n    printf(\"\\r\\n\");\n}\n\nuint16_t bit_lib_reverse_16_fast(uint16_t data) {\n    uint16_t result = 0;\n    result |= (data & 0x8000) >> 15;\n    result |= (data & 0x4000) >> 13;\n    result |= (data & 0x2000) >> 11;\n    result |= (data & 0x1000) >> 9;\n    result |= (data & 0x0800) >> 7;\n    result |= (data & 0x0400) >> 5;\n    result |= (data & 0x0200) >> 3;\n    result |= (data & 0x0100) >> 1;\n    result |= (data & 0x0080) << 1;\n    result |= (data & 0x0040) << 3;\n    result |= (data & 0x0020) << 5;\n    result |= (data & 0x0010) << 7;\n    result |= (data & 0x0008) << 9;\n    result |= (data & 0x0004) << 11;\n    result |= (data & 0x0002) << 13;\n    result |= (data & 0x0001) << 15;\n    return result;\n}\n\nuint8_t bit_lib_reverse_8_fast(uint8_t byte) {\n    byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;\n    byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;\n    byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;\n    return byte;\n}\n\nuint16_t bit_lib_crc8(\n    uint8_t const* data,\n    size_t data_size,\n    uint8_t polynom,\n    uint8_t init,\n    bool ref_in,\n    bool ref_out,\n    uint8_t xor_out) {\n    uint8_t crc = init;\n\n    for(size_t i = 0; i < data_size; ++i) {\n        uint8_t byte = data[i];\n        if(ref_in) bit_lib_reverse_bits(&byte, 0, 8);\n        crc ^= byte;\n\n        for(size_t j = 8; j > 0; --j) {\n            if(crc & TOPBIT(8)) {\n                crc = (crc << 1) ^ polynom;\n            } else {\n                crc = (crc << 1);\n            }\n        }\n    }\n\n    if(ref_out) bit_lib_reverse_bits(&crc, 0, 8);\n    crc ^= xor_out;\n\n    return crc;\n}\n\nuint16_t bit_lib_crc16(\n    uint8_t const* data,\n    size_t data_size,\n    uint16_t polynom,\n    uint16_t init,\n    bool ref_in,\n    bool ref_out,\n    uint16_t xor_out) {\n    uint16_t crc = init;\n\n    for(size_t i = 0; i < data_size; ++i) {\n        uint8_t byte = data[i];\n        if(ref_in) byte = bit_lib_reverse_16_fast(byte) >> 8;\n\n        for(size_t j = 0; j < 8; ++j) {\n            bool c15 = (crc >> 15 & 1);\n            bool bit = (byte >> (7 - j) & 1);\n            crc <<= 1;\n            if(c15 ^ bit) crc ^= polynom;\n        }\n    }\n\n    if(ref_out) crc = bit_lib_reverse_16_fast(crc);\n    crc ^= xor_out;\n\n    return crc;\n}\n\nvoid bit_lib_num_to_bytes_be(uint64_t src, uint8_t len, uint8_t* dest) {\n    furi_check(dest);\n    furi_check(len <= 8);\n\n    while(len--) {\n        dest[len] = (uint8_t)src;\n        src >>= 8;\n    }\n}\n\nvoid bit_lib_num_to_bytes_le(uint64_t src, uint8_t len, uint8_t* dest) {\n    furi_check(dest);\n    furi_check(len <= 8);\n\n    for(int i = 0; i < len; i++) {\n        dest[i] = (uint8_t)(src >> (8 * i));\n    }\n}\n\nuint64_t bit_lib_bytes_to_num_be(const uint8_t* src, uint8_t len) {\n    furi_check(src);\n    furi_check(len <= 8);\n\n    uint64_t res = 0;\n    while(len--) {\n        res = (res << 8) | (*src);\n        src++;\n    }\n    return res;\n}\n\nuint64_t bit_lib_bytes_to_num_le(const uint8_t* src, uint8_t len) {\n    furi_check(src);\n    furi_check(len <= 8);\n\n    uint64_t res = 0;\n    uint8_t shift = 0;\n    while(len--) {\n        res |= ((uint64_t)*src) << (8 * shift++);\n        src++;\n    }\n    return res;\n}\n\nuint64_t bit_lib_bytes_to_num_bcd(const uint8_t* src, uint8_t len, bool* is_bcd) {\n    furi_check(src);\n    furi_check(len <= 9);\n\n    uint64_t res = 0;\n    uint8_t nibble_1, nibble_2;\n    *is_bcd = true;\n\n    for(uint8_t i = 0; i < len; i++) {\n        nibble_1 = src[i] / 16;\n        nibble_2 = src[i] % 16;\n        if((nibble_1 > 9) || (nibble_2 > 9)) *is_bcd = false;\n\n        res *= 10;\n        res += nibble_1;\n\n        res *= 10;\n        res += nibble_2;\n    }\n\n    return res;\n}\n"
  },
  {
    "path": "lib/bit_lib/bit_lib.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define TOPBIT(X) (1 << ((X) - 1))\n\ntypedef enum {\n    BitLibParityEven,\n    BitLibParityOdd,\n    BitLibParityAlways0,\n    BitLibParityAlways1,\n} BitLibParity;\n\n/** @brief Increment and wrap around a value.\n *  @param index value to increment\n *  @param length wrap-around range\n */\n#define bit_lib_increment_index(index, length) (index = (((index) + 1) % (length)))\n\n/** @brief Test if a bit is set.\n *  @param data value to test\n *  @param index bit index to test\n */\n#define bit_lib_bit_is_set(data, index) (((data) & (1 << (index))) != 0)\n\n/** @brief Test if a bit is not set.\n *  @param data value to test\n *  @param index bit index to test\n */\n#define bit_lib_bit_is_not_set(data, index) (((data) & (1 << (index))) == 0)\n\n/** @brief Push a bit into a byte array.\n *  @param data array to push bit into\n *  @param data_size array size\n *  @param bit bit to push\n */\nvoid bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit);\n\n/** @brief Set a bit in a byte array.\n *  @param data array to set bit in\n *  @param position The position of the bit to set.\n *  @param bit bit value to set\n */\nvoid bit_lib_set_bit(uint8_t* data, size_t position, bool bit);\n\n/** @brief Set the bit at the given position to the given value.\n * @param data The data to set the bit in.\n * @param position The position of the bit to set.\n * @param byte The data to set the bit to.\n * @param length The length of the data.\n */\nvoid bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length);\n\n/** @brief Get the bit of a byte.\n * @param data The byte to get the bits from.\n * @param position The position of the bit.\n * @return The bit.\n */\nbool bit_lib_get_bit(const uint8_t* data, size_t position);\n\n/**\n * @brief Get the bits of a data, as uint8_t.\n * @param data The data to get the bits from.\n * @param position The position of the first bit.\n * @param length The length of the bits.\n * @return The bits.\n */\nuint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length);\n\n/**\n * @brief Get the bits of a data, as uint16_t.\n * @param data The data to get the bits from.\n * @param position The position of the first bit.\n * @param length The length of the bits.\n * @return The bits.\n */\nuint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length);\n\n/**\n * @brief Get the bits of a data, as uint32_t.\n * @param data The data to get the bits from.\n * @param position The position of the first bit.\n * @param length The length of the bits.\n * @return The bits.\n */\nuint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length);\n\n/**\n * @brief Get the bits of a data, as uint64_t.\n * @param data The data to get the bits from.\n * @param position The position of the first bit.\n * @param length The length of the bits.\n * @return The bits.\n */\nuint64_t bit_lib_get_bits_64(const uint8_t* data, size_t position, uint8_t length);\n\n/**\n * @brief Test parity of given bits\n * @param bits Bits to test parity of\n * @param parity Parity to test against\n * @return true if parity is correct, false otherwise\n */\nbool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity);\n\n/**\n * @brief Test parity of bit array, check parity for every parity_length block from start\n * \n * @param data Bit array\n * @param position Start position\n * @param length Bit count\n * @param parity Parity to test against\n * @param parity_length Parity block length\n * @return true \n * @return false \n */\nbool bit_lib_test_parity(\n    const uint8_t* data,\n    size_t position,\n    uint8_t length,\n    BitLibParity parity,\n    uint8_t parity_length);\n\n/**\n * @brief Add parity to bit array\n * \n * @param data Source bit array\n * @param position Start position\n * @param dest Destination bit array\n * @param dest_position Destination position\n * @param source_length Source bit count\n * @param parity_length Parity block length\n * @param parity Parity to test against\n * @return size_t \n */\nsize_t bit_lib_add_parity(\n    const uint8_t* data,\n    size_t position,\n    uint8_t* dest,\n    size_t dest_position,\n    uint8_t source_length,\n    uint8_t parity_length,\n    BitLibParity parity);\n\n/**\n * @brief Remove bit every n in array and shift array left. Useful to remove parity.\n * \n * @param data Bit array\n * @param position Start position\n * @param length Bit count\n * @param n every n bit will be removed\n * @return size_t \n */\nsize_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n);\n\n/**\n * @brief Copy bits from source to destination.\n * \n * @param data destination array\n * @param position position in destination array\n * @param length length of bits to copy\n * @param source source array\n * @param source_position position in source array\n */\nvoid bit_lib_copy_bits(\n    uint8_t* data,\n    size_t position,\n    size_t length,\n    const uint8_t* source,\n    size_t source_position);\n\n/**\n * @brief Reverse bits in bit array\n * \n * @param data Bit array\n * @param position start position\n * @param length length of bits to reverse\n */\nvoid bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length);\n\n/**\n * @brief Count 1 bits in data\n * \n * @param data \n * @return uint8_t set bit count\n */\nuint8_t bit_lib_get_bit_count(uint32_t data);\n\n/**\n * @brief Print data as bit array\n * \n * @param data \n * @param length \n */\nvoid bit_lib_print_bits(const uint8_t* data, size_t length);\n\ntypedef struct {\n    const char mark;\n    const size_t start;\n    const size_t length;\n} BitLibRegion;\n\n/**\n * @brief Print data as bit array and mark regions. Regions needs to be sorted by start position.\n * \n * @param regions \n * @param region_count \n * @param data \n * @param length \n */\nvoid bit_lib_print_regions(\n    const BitLibRegion* regions,\n    size_t region_count,\n    const uint8_t* data,\n    size_t length);\n\n/**\n * @brief Reverse bits in uint16_t, faster than generic bit_lib_reverse_bits.\n * \n * @param data \n * @return uint16_t \n */\nuint16_t bit_lib_reverse_16_fast(uint16_t data);\n\n/**\n * @brief Reverse bits in uint8_t, faster than generic bit_lib_reverse_bits.\n * \n * @param byte Byte\n * @return uint8_t the reversed byte\n */\nuint8_t bit_lib_reverse_8_fast(uint8_t byte);\n\n/**\n * @brief Slow, but generic CRC8 implementation\n * \n * @param data \n * @param data_size \n * @param polynom CRC polynom\n * @param init init value\n * @param ref_in true if the right bit is older\n * @param ref_out true to reverse output\n * @param xor_out xor output with this value\n * @return uint8_t \n */\nuint16_t bit_lib_crc8(\n    uint8_t const* data,\n    size_t data_size,\n    uint8_t polynom,\n    uint8_t init,\n    bool ref_in,\n    bool ref_out,\n    uint8_t xor_out);\n\n/**\n * @brief Slow, but generic CRC16 implementation\n * \n * @param data \n * @param data_size \n * @param polynom CRC polynom\n * @param init init value\n * @param ref_in true if the right bit is older\n * @param ref_out true to reverse output\n * @param xor_out xor output with this value\n * @return uint16_t \n */\nuint16_t bit_lib_crc16(\n    uint8_t const* data,\n    size_t data_size,\n    uint16_t polynom,\n    uint16_t init,\n    bool ref_in,\n    bool ref_out,\n    uint16_t xor_out);\n\n/**\n * @brief Convert number to bytes in big endian order\n * \n * @param src number to convert \n * @param len max used bytes count \n * @param dest destination\n * @return void\n */\nvoid bit_lib_num_to_bytes_be(uint64_t src, uint8_t len, uint8_t* dest);\n\n/**\n * @brief Convert number to bytes in little endian order\n * \n * @param src number to convert \n * @param len max used bytes count \n * @param dest destination\n * @return void\n */\nvoid bit_lib_num_to_bytes_le(uint64_t src, uint8_t len, uint8_t* dest);\n\n/**\n * @brief Convert bytes to number in big endian order\n * \n * @param src byte array \n * @param len max used bytes count \n * @return uint64_t\n */\nuint64_t bit_lib_bytes_to_num_be(const uint8_t* src, uint8_t len);\n\n/**\n * @brief Convert bytes to number in little endian order\n * \n * @param src byte array \n * @param len max used bytes count \n * @return uint64_t\n */\nuint64_t bit_lib_bytes_to_num_le(const uint8_t* src, uint8_t len);\n\n/**\n * @brief Convert bytes in binary-coded decimal encoding to number\n * \n * @param src byte array \n * @param len max used bytes count \n * @param is_bcd will be true if all processed bytes is BCD encoded (no A-F nibbles)\n * @return uint64_t\n */\nuint64_t bit_lib_bytes_to_num_bcd(const uint8_t* src, uint8_t len, bool* is_bcd);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ble_profile/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/ble_profile\",\n    ],\n    SDK_HEADERS=[\n        File(\"extra_profiles/hid_profile.h\"),\n        File(\"extra_services/hid_service.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"ble_profile\")\nlibenv.AppendUnique(\n    CCFLAGS=[\n        # Required for lib to be linkable with .faps\n        \"-mword-relocations\",\n        \"-mlong-calls\",\n    ],\n)\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/ble_profile/extra_profiles/hid_profile.c",
    "content": "#include \"hid_profile.h\"\n\n#include <furi_hal_usb_hid.h>\n#include <services/dev_info_service.h>\n#include <services/battery_service.h>\n#include <extra_services/hid_service.h>\n\n#include <furi.h>\n#include <usb_hid.h>\n#include <ble/ble.h>\n\n#define HID_INFO_BASE_USB_SPECIFICATION (0x0101)\n#define HID_INFO_COUNTRY_CODE (0x00)\n#define BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01)\n#define BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK (0x02)\n\n#define BLE_PROFILE_HID_KB_MAX_KEYS (6)\n#define BLE_PROFILE_CONSUMER_MAX_KEYS (1)\n\n// Report ids cant be 0\nenum HidReportId {\n    ReportIdKeyboard = 1,\n    ReportIdMouse = 2,\n    ReportIdConsumer = 3,\n};\n// Report numbers corresponded to the report id with an offset of 1\nenum HidInputNumber {\n    ReportNumberKeyboard = 0,\n    ReportNumberMouse = 1,\n    ReportNumberConsumer = 2,\n};\n\ntypedef struct {\n    uint8_t mods;\n    uint8_t reserved;\n    uint8_t key[BLE_PROFILE_HID_KB_MAX_KEYS];\n} FURI_PACKED FuriHalBtHidKbReport;\n\ntypedef struct {\n    uint8_t btn;\n    int8_t x;\n    int8_t y;\n    int8_t wheel;\n} FURI_PACKED FuriHalBtHidMouseReport;\n\ntypedef struct {\n    uint16_t key[BLE_PROFILE_CONSUMER_MAX_KEYS];\n} FURI_PACKED FuriHalBtHidConsumerReport;\n\n// keyboard+mouse+consumer hid report\nstatic const uint8_t ble_profile_hid_report_map_data[] = {\n    // Keyboard Report\n    HID_USAGE_PAGE(HID_PAGE_DESKTOP),\n    HID_USAGE(HID_DESKTOP_KEYBOARD),\n    HID_COLLECTION(HID_APPLICATION_COLLECTION),\n    HID_REPORT_ID(ReportIdKeyboard),\n    HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),\n    HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),\n    HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),\n    HID_LOGICAL_MINIMUM(0),\n    HID_LOGICAL_MAXIMUM(1),\n    HID_REPORT_SIZE(1),\n    HID_REPORT_COUNT(8),\n    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_REPORT_COUNT(1),\n    HID_REPORT_SIZE(8),\n    HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_USAGE_PAGE(HID_PAGE_LED),\n    HID_REPORT_COUNT(8),\n    HID_REPORT_SIZE(1),\n    HID_USAGE_MINIMUM(1),\n    HID_USAGE_MAXIMUM(8),\n    HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_REPORT_COUNT(BLE_PROFILE_HID_KB_MAX_KEYS),\n    HID_REPORT_SIZE(8),\n    HID_LOGICAL_MINIMUM(0),\n    HID_LOGICAL_MAXIMUM(101),\n    HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),\n    HID_USAGE_MINIMUM(0),\n    HID_USAGE_MAXIMUM(101),\n    HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),\n    HID_END_COLLECTION,\n    // Mouse Report\n    HID_USAGE_PAGE(HID_PAGE_DESKTOP),\n    HID_USAGE(HID_DESKTOP_MOUSE),\n    HID_COLLECTION(HID_APPLICATION_COLLECTION),\n    HID_USAGE(HID_DESKTOP_POINTER),\n    HID_COLLECTION(HID_PHYSICAL_COLLECTION),\n    HID_REPORT_ID(ReportIdMouse),\n    HID_USAGE_PAGE(HID_PAGE_BUTTON),\n    HID_USAGE_MINIMUM(1),\n    HID_USAGE_MAXIMUM(3),\n    HID_LOGICAL_MINIMUM(0),\n    HID_LOGICAL_MAXIMUM(1),\n    HID_REPORT_COUNT(3),\n    HID_REPORT_SIZE(1),\n    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_REPORT_SIZE(1),\n    HID_REPORT_COUNT(5),\n    HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_USAGE_PAGE(HID_PAGE_DESKTOP),\n    HID_USAGE(HID_DESKTOP_X),\n    HID_USAGE(HID_DESKTOP_Y),\n    HID_USAGE(HID_DESKTOP_WHEEL),\n    HID_LOGICAL_MINIMUM(-127),\n    HID_LOGICAL_MAXIMUM(127),\n    HID_REPORT_SIZE(8),\n    HID_REPORT_COUNT(3),\n    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),\n    HID_END_COLLECTION,\n    HID_END_COLLECTION,\n    // Consumer Report\n    HID_USAGE_PAGE(HID_PAGE_CONSUMER),\n    HID_USAGE(HID_CONSUMER_CONTROL),\n    HID_COLLECTION(HID_APPLICATION_COLLECTION),\n    HID_REPORT_ID(ReportIdConsumer),\n    HID_LOGICAL_MINIMUM(0),\n    HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),\n    HID_USAGE_MINIMUM(0),\n    HID_RI_USAGE_MAXIMUM(16, 0x3FF),\n    HID_REPORT_COUNT(BLE_PROFILE_CONSUMER_MAX_KEYS),\n    HID_REPORT_SIZE(16),\n    HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),\n    HID_END_COLLECTION,\n};\n\ntypedef struct {\n    FuriHalBleProfileBase base;\n\n    FuriHalBtHidKbReport* kb_report;\n    FuriHalBtHidMouseReport* mouse_report;\n    FuriHalBtHidConsumerReport* consumer_report;\n\n    BleServiceBattery* battery_svc;\n    BleServiceDevInfo* dev_info_svc;\n    BleServiceHid* hid_svc;\n} BleProfileHid;\n_Static_assert(offsetof(BleProfileHid, base) == 0, \"Wrong layout\");\n\nstatic FuriHalBleProfileBase* ble_profile_hid_start(FuriHalBleProfileParams profile_params) {\n    UNUSED(profile_params);\n\n    BleProfileHid* profile = malloc(sizeof(BleProfileHid));\n\n    profile->base.config = ble_profile_hid;\n\n    profile->battery_svc = ble_svc_battery_start(true);\n    profile->dev_info_svc = ble_svc_dev_info_start();\n    profile->hid_svc = ble_svc_hid_start();\n\n    // Configure HID Keyboard\n    profile->kb_report = malloc(sizeof(FuriHalBtHidKbReport));\n    profile->mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));\n    profile->consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));\n\n    // Configure Report Map characteristic\n    ble_svc_hid_update_report_map(\n        profile->hid_svc,\n        ble_profile_hid_report_map_data,\n        sizeof(ble_profile_hid_report_map_data));\n    // Configure HID Information characteristic\n    uint8_t hid_info_val[4] = {\n        HID_INFO_BASE_USB_SPECIFICATION & 0x00ff,\n        (HID_INFO_BASE_USB_SPECIFICATION & 0xff00) >> 8,\n        HID_INFO_COUNTRY_CODE,\n        BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK |\n            BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK,\n    };\n    ble_svc_hid_update_info(profile->hid_svc, hid_info_val);\n\n    return &profile->base;\n}\n\nstatic void ble_profile_hid_stop(FuriHalBleProfileBase* profile) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    ble_svc_battery_stop(hid_profile->battery_svc);\n    ble_svc_dev_info_stop(hid_profile->dev_info_svc);\n    ble_svc_hid_stop(hid_profile->hid_svc);\n\n    free(hid_profile->kb_report);\n    free(hid_profile->mouse_report);\n    free(hid_profile->consumer_report);\n}\n\nbool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {\n        if(kb_report->key[i] == 0) {\n            kb_report->key[i] = button & 0xFF;\n            break;\n        }\n    }\n    kb_report->mods |= (button >> 8);\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberKeyboard,\n        (uint8_t*)kb_report,\n        sizeof(FuriHalBtHidKbReport));\n}\n\nbool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n\n    FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {\n        if(kb_report->key[i] == (button & 0xFF)) {\n            kb_report->key[i] = 0;\n            break;\n        }\n    }\n    kb_report->mods &= ~(button >> 8);\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberKeyboard,\n        (uint8_t*)kb_report,\n        sizeof(FuriHalBtHidKbReport));\n}\n\nbool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {\n        kb_report->key[i] = 0;\n    }\n    kb_report->mods = 0;\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberKeyboard,\n        (uint8_t*)kb_report,\n        sizeof(FuriHalBtHidKbReport));\n}\n\nbool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008\n        if(consumer_report->key[i] == 0) {\n            consumer_report->key[i] = button;\n            break;\n        }\n    }\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberConsumer,\n        (uint8_t*)consumer_report,\n        sizeof(FuriHalBtHidConsumerReport));\n}\n\nbool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008\n        if(consumer_report->key[i] == button) {\n            consumer_report->key[i] = 0;\n            break;\n        }\n    }\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberConsumer,\n        (uint8_t*)consumer_report,\n        sizeof(FuriHalBtHidConsumerReport));\n}\n\nbool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;\n    for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008\n        consumer_report->key[i] = 0;\n    }\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberConsumer,\n        (uint8_t*)consumer_report,\n        sizeof(FuriHalBtHidConsumerReport));\n}\n\nbool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;\n    mouse_report->x = dx;\n    mouse_report->y = dy;\n    bool state = ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberMouse,\n        (uint8_t*)mouse_report,\n        sizeof(FuriHalBtHidMouseReport));\n    mouse_report->x = 0;\n    mouse_report->y = 0;\n    return state;\n}\n\nbool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;\n    mouse_report->btn |= button;\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberMouse,\n        (uint8_t*)mouse_report,\n        sizeof(FuriHalBtHidMouseReport));\n}\n\nbool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;\n    mouse_report->btn &= ~button;\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberMouse,\n        (uint8_t*)mouse_report,\n        sizeof(FuriHalBtHidMouseReport));\n}\n\nbool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;\n    mouse_report->btn = 0;\n    return ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberMouse,\n        (uint8_t*)mouse_report,\n        sizeof(FuriHalBtHidMouseReport));\n}\n\nbool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta) {\n    furi_check(profile);\n    furi_check(profile->config == ble_profile_hid);\n\n    BleProfileHid* hid_profile = (BleProfileHid*)profile;\n    FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;\n    mouse_report->wheel = delta;\n    bool state = ble_svc_hid_update_input_report(\n        hid_profile->hid_svc,\n        ReportNumberMouse,\n        (uint8_t*)mouse_report,\n        sizeof(FuriHalBtHidMouseReport));\n    mouse_report->wheel = 0;\n    return state;\n}\n\n// AN5289: 4.7, in order to use flash controller interval must be at least 25ms + advertisement, which is 30 ms\n// Since we don't use flash controller anymore interval can be lowered to 7.5ms\n#define CONNECTION_INTERVAL_MIN (0x0006)\n// Up to 45 ms\n#define CONNECTION_INTERVAL_MAX (0x24)\n\nstatic GapConfig template_config = {\n    .adv_service = {\n        .UUID_Type = UUID_TYPE_16,\n        .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,\n    },\n    .appearance_char = GAP_APPEARANCE_KEYBOARD,\n    .bonding_mode = true,\n    .pairing_method = GapPairingPinCodeVerifyYesNo,\n    .conn_param =\n        {\n            .conn_int_min = CONNECTION_INTERVAL_MIN,\n            .conn_int_max = CONNECTION_INTERVAL_MAX,\n            .slave_latency = 0,\n            .supervisor_timeout = 0,\n        },\n};\n\nstatic void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) {\n    BleProfileHidParams* hid_profile_params = profile_params;\n\n    furi_check(config);\n    memcpy(config, &template_config, sizeof(GapConfig));\n    // Set mac address\n    memcpy(config->mac_address, furi_hal_version_get_ble_mac(), sizeof(config->mac_address));\n\n    // Change MAC address for HID profile\n    config->mac_address[2]++;\n    if(hid_profile_params) {\n        config->mac_address[0] ^= hid_profile_params->mac_xor;\n        config->mac_address[1] ^= hid_profile_params->mac_xor >> 8;\n    }\n\n    // Set advertise name\n    memset(config->adv_name, 0, sizeof(config->adv_name));\n    FuriString* name = furi_string_alloc_set(furi_hal_version_get_ble_local_device_name_ptr());\n\n    const char* clicker_str = \"Control\";\n    if(hid_profile_params && hid_profile_params->device_name_prefix) {\n        clicker_str = hid_profile_params->device_name_prefix;\n    }\n    furi_string_replace_str(name, \"Flipper\", clicker_str);\n    if(furi_string_size(name) >= sizeof(config->adv_name)) {\n        furi_string_left(name, sizeof(config->adv_name) - 1);\n    }\n    memcpy(config->adv_name, furi_string_get_cstr(name), furi_string_size(name));\n    furi_string_free(name);\n}\n\nstatic const FuriHalBleProfileTemplate profile_callbacks = {\n    .start = ble_profile_hid_start,\n    .stop = ble_profile_hid_stop,\n    .get_gap_config = ble_profile_hid_get_config,\n};\n\nconst FuriHalBleProfileTemplate* ble_profile_hid = &profile_callbacks;\n"
  },
  {
    "path": "lib/ble_profile/extra_profiles/hid_profile.h",
    "content": "#pragma once\n\n#include <furi_ble/profile_interface.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** \n * Optional arguments to pass along with profile template as \n * FuriHalBleProfileParams for tuning profile behavior \n **/\ntypedef struct {\n    const char* device_name_prefix; /**< Prefix for device name. Length must be less than 8 */\n    uint16_t mac_xor; /**< XOR mask for device address, for uniqueness */\n} BleProfileHidParams;\n\n/** Hid Keyboard Profile descriptor */\nextern const FuriHalBleProfileTemplate* ble_profile_hid;\n\n/** Press keyboard button\n *\n * @param profile   profile instance\n * @param button    button code from HID specification\n *\n * @return          true on success\n */\nbool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button);\n\n/** Release keyboard button\n *\n * @param profile   profile instance\n * @param button    button code from HID specification\n *\n * @return          true on success\n */\nbool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button);\n\n/** Release all keyboard buttons\n *\n * @param profile   profile instance\n * @return          true on success\n */\nbool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile);\n\n/** Set the following consumer key to pressed state and send HID report\n *\n * @param profile   profile instance\n * @param button    key code\n */\nbool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button);\n\n/** Set the following consumer key to released state and send HID report\n *\n * @param profile   profile instance\n * @param button    key code\n */\nbool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button);\n\n/** Set consumer key to released state and send HID report\n *\n * @param profile   profile instance\n * @param button    key code\n */\nbool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile);\n\n/** Set mouse movement and send HID report\n *\n * @param profile    profile instance\n * @param      dx    x coordinate delta\n * @param      dy    y coordinate delta\n */\nbool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy);\n\n/** Set mouse button to pressed state and send HID report\n *\n * @param profile   profile instance\n * @param   button  key code\n */\nbool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button);\n\n/** Set mouse button to released state and send HID report\n *\n * @param profile   profile instance\n * @param   button  key code\n */\nbool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button);\n\n/** Set mouse button to released state and send HID report\n *\n * @param profile   profile instance\n * @param   button  key code\n */\nbool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile);\n\n/** Set mouse wheel position and send HID report\n *\n * @param profile   profile instance\n * @param    delta  number of scroll steps\n */\nbool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ble_profile/extra_services/hid_service.c",
    "content": "#include \"hid_service.h\"\n#include \"app_common.h\" // IWYU pragma: keep\n#include <ble/ble.h>\n#include <furi_ble/event_dispatcher.h>\n#include <furi_ble/gatt.h>\n\n#include <furi.h>\n#include <stdint.h>\n\n#define TAG \"BleHid\"\n\n#define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255)\n#define BLE_SVC_HID_REPORT_MAX_LEN     (255)\n#define BLE_SVC_HID_REPORT_REF_LEN     (2)\n#define BLE_SVC_HID_INFO_LEN           (4)\n#define BLE_SVC_HID_CONTROL_POINT_LEN  (1)\n\n#define BLE_SVC_HID_INPUT_REPORT_COUNT   (3)\n#define BLE_SVC_HID_OUTPUT_REPORT_COUNT  (0)\n#define BLE_SVC_HID_FEATURE_REPORT_COUNT (0)\n#define BLE_SVC_HID_REPORT_COUNT                                        \\\n    (BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \\\n     BLE_SVC_HID_FEATURE_REPORT_COUNT)\n\ntypedef enum {\n    HidSvcGattCharacteristicProtocolMode = 0,\n    HidSvcGattCharacteristicReportMap,\n    HidSvcGattCharacteristicInfo,\n    HidSvcGattCharacteristicCtrlPoint,\n    HidSvcGattCharacteristicCount,\n} HidSvcGattCharacteristicId;\n\ntypedef struct {\n    uint8_t report_idx;\n    uint8_t report_type;\n} HidSvcReportId;\n\nstatic_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), \"HidSvcReportId must be 2 bytes\");\n\nstatic const Service_UUID_t ble_svc_hid_uuid = {\n    .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,\n};\n\nstatic bool ble_svc_hid_char_desc_data_callback(\n    const void* context,\n    const uint8_t** data,\n    uint16_t* data_len) {\n    const HidSvcReportId* report_id = context;\n    *data_len = sizeof(HidSvcReportId);\n    if(data) {\n        *data = (const uint8_t*)report_id;\n    }\n    return false;\n}\n\ntypedef struct {\n    const void* data_ptr;\n    uint16_t data_len;\n} HidSvcDataWrapper;\n\nstatic bool ble_svc_hid_report_data_callback(\n    const void* context,\n    const uint8_t** data,\n    uint16_t* data_len) {\n    const HidSvcDataWrapper* report_data = context;\n    if(data) {\n        *data = report_data->data_ptr;\n        *data_len = report_data->data_len;\n    } else {\n        *data_len = BLE_SVC_HID_REPORT_MAP_MAX_LEN;\n    }\n    return false;\n}\n\nstatic const BleGattCharacteristicParams ble_svc_hid_chars[HidSvcGattCharacteristicCount] = {\n    [HidSvcGattCharacteristicProtocolMode] =\n        {.name = \"Protocol Mode\",\n         .data_prop_type = FlipperGattCharacteristicDataFixed,\n         .data.fixed.length = 1,\n         .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID,\n         .uuid_type = UUID_TYPE_16,\n         .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,\n         .security_permissions = ATTR_PERMISSION_NONE,\n         .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,\n         .is_variable = CHAR_VALUE_LEN_CONSTANT},\n    [HidSvcGattCharacteristicReportMap] =\n        {.name = \"Report Map\",\n         .data_prop_type = FlipperGattCharacteristicDataCallback,\n         .data.callback.fn = ble_svc_hid_report_data_callback,\n         .data.callback.context = NULL,\n         .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID,\n         .uuid_type = UUID_TYPE_16,\n         .char_properties = CHAR_PROP_READ,\n         .security_permissions = ATTR_PERMISSION_NONE,\n         .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,\n         .is_variable = CHAR_VALUE_LEN_VARIABLE},\n    [HidSvcGattCharacteristicInfo] =\n        {.name = \"HID Information\",\n         .data_prop_type = FlipperGattCharacteristicDataFixed,\n         .data.fixed.length = BLE_SVC_HID_INFO_LEN,\n         .data.fixed.ptr = NULL,\n         .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID,\n         .uuid_type = UUID_TYPE_16,\n         .char_properties = CHAR_PROP_READ,\n         .security_permissions = ATTR_PERMISSION_NONE,\n         .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,\n         .is_variable = CHAR_VALUE_LEN_CONSTANT},\n    [HidSvcGattCharacteristicCtrlPoint] =\n        {.name = \"HID Control Point\",\n         .data_prop_type = FlipperGattCharacteristicDataFixed,\n         .data.fixed.length = BLE_SVC_HID_CONTROL_POINT_LEN,\n         .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID,\n         .uuid_type = UUID_TYPE_16,\n         .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP,\n         .security_permissions = ATTR_PERMISSION_NONE,\n         .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,\n         .is_variable = CHAR_VALUE_LEN_CONSTANT},\n};\n\nstatic const BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr_template = {\n    .uuid_type = UUID_TYPE_16,\n    .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID,\n    .max_length = BLE_SVC_HID_REPORT_REF_LEN,\n    .data_callback.fn = ble_svc_hid_char_desc_data_callback,\n    .security_permissions = ATTR_PERMISSION_NONE,\n    .access_permissions = ATTR_ACCESS_READ_WRITE,\n    .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,\n    .is_variable = CHAR_VALUE_LEN_CONSTANT,\n};\n\nstatic const BleGattCharacteristicParams ble_svc_hid_report_template = {\n    .name = \"Report\",\n    .data_prop_type = FlipperGattCharacteristicDataCallback,\n    .data.callback.fn = ble_svc_hid_report_data_callback,\n    .data.callback.context = NULL,\n    .uuid.Char_UUID_16 = REPORT_CHAR_UUID,\n    .uuid_type = UUID_TYPE_16,\n    .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,\n    .security_permissions = ATTR_PERMISSION_NONE,\n    .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,\n    .is_variable = CHAR_VALUE_LEN_VARIABLE,\n};\n\nstruct BleServiceHid {\n    uint16_t svc_handle;\n    BleGattCharacteristicInstance chars[HidSvcGattCharacteristicCount];\n    BleGattCharacteristicInstance input_report_chars[BLE_SVC_HID_INPUT_REPORT_COUNT];\n    BleGattCharacteristicInstance output_report_chars[BLE_SVC_HID_OUTPUT_REPORT_COUNT];\n    BleGattCharacteristicInstance feature_report_chars[BLE_SVC_HID_FEATURE_REPORT_COUNT];\n    GapSvcEventHandler* event_handler;\n};\n\nstatic BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) {\n    UNUSED(context);\n\n    BleEventAckStatus ret = BleEventNotAck;\n    hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);\n    evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;\n    // aci_gatt_attribute_modified_event_rp0* attribute_modified;\n\n    if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {\n        if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {\n            // Process modification events\n            ret = BleEventAckFlowEnable;\n        } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {\n            // Process notification confirmation\n            ret = BleEventAckFlowEnable;\n        }\n    }\n    return ret;\n}\n\nBleServiceHid* ble_svc_hid_start(void) {\n    BleServiceHid* hid_svc = malloc(sizeof(BleServiceHid));\n\n    // Register event handler\n    hid_svc->event_handler =\n        ble_event_dispatcher_register_svc_handler(ble_svc_hid_event_handler, hid_svc);\n    /**\n     *  Add Human Interface Device Service\n     */\n    if(!ble_gatt_service_add(\n           UUID_TYPE_16,\n           &ble_svc_hid_uuid,\n           PRIMARY_SERVICE,\n           2 + /* protocol mode */\n               (4 * BLE_SVC_HID_INPUT_REPORT_COUNT) + (3 * BLE_SVC_HID_OUTPUT_REPORT_COUNT) +\n               (3 * BLE_SVC_HID_FEATURE_REPORT_COUNT) + 1 + 2 + 2 +\n               2, /* Service + Report Map + HID Information + HID Control Point */\n           &hid_svc->svc_handle)) {\n        free(hid_svc);\n        return NULL;\n    }\n\n    // Maintain previously defined characteristic order\n    ble_gatt_characteristic_init(\n        hid_svc->svc_handle,\n        &ble_svc_hid_chars[HidSvcGattCharacteristicProtocolMode],\n        &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]);\n\n    uint8_t protocol_mode = 1;\n    ble_gatt_characteristic_update(\n        hid_svc->svc_handle,\n        &hid_svc->chars[HidSvcGattCharacteristicProtocolMode],\n        &protocol_mode);\n\n    // reports\n    BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr;\n    BleGattCharacteristicParams report_char;\n    HidSvcReportId report_id;\n\n    memcpy(\n        &ble_svc_hid_char_descr, &ble_svc_hid_char_descr_template, sizeof(ble_svc_hid_char_descr));\n    memcpy(&report_char, &ble_svc_hid_report_template, sizeof(report_char));\n\n    ble_svc_hid_char_descr.data_callback.context = &report_id;\n    report_char.descriptor_params = &ble_svc_hid_char_descr;\n\n    typedef struct {\n        uint8_t report_type;\n        uint8_t report_count;\n        BleGattCharacteristicInstance* chars;\n    } HidSvcReportCharProps;\n\n    HidSvcReportCharProps hid_report_chars[] = {\n        {0x01, BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},\n        {0x02, BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},\n        {0x03, BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},\n    };\n\n    for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);\n        report_type_idx++) {\n        report_id.report_type = hid_report_chars[report_type_idx].report_type;\n        for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;\n            report_idx++) {\n            report_id.report_idx = report_idx + 1;\n            ble_gatt_characteristic_init(\n                hid_svc->svc_handle,\n                &report_char,\n                &hid_report_chars[report_type_idx].chars[report_idx]);\n        }\n    }\n\n    // Setup remaining characteristics\n    for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) {\n        ble_gatt_characteristic_init(\n            hid_svc->svc_handle, &ble_svc_hid_chars[i], &hid_svc->chars[i]);\n    }\n\n    return hid_svc;\n}\n\nbool ble_svc_hid_update_report_map(BleServiceHid* hid_svc, const uint8_t* data, uint16_t len) {\n    furi_assert(data);\n    furi_assert(hid_svc);\n\n    HidSvcDataWrapper report_data = {\n        .data_ptr = data,\n        .data_len = len,\n    };\n    return ble_gatt_characteristic_update(\n        hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data);\n}\n\nbool ble_svc_hid_update_input_report(\n    BleServiceHid* hid_svc,\n    uint8_t input_report_num,\n    uint8_t* data,\n    uint16_t len) {\n    furi_assert(data);\n    furi_assert(hid_svc);\n    furi_assert(input_report_num < BLE_SVC_HID_INPUT_REPORT_COUNT);\n\n    HidSvcDataWrapper report_data = {\n        .data_ptr = data,\n        .data_len = len,\n    };\n\n    return ble_gatt_characteristic_update(\n        hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);\n}\n\nbool ble_svc_hid_update_info(BleServiceHid* hid_svc, uint8_t* data) {\n    furi_assert(data);\n    furi_assert(hid_svc);\n\n    return ble_gatt_characteristic_update(\n        hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data);\n}\n\nvoid ble_svc_hid_stop(BleServiceHid* hid_svc) {\n    furi_assert(hid_svc);\n    ble_event_dispatcher_unregister_svc_handler(hid_svc->event_handler);\n    // Delete characteristics\n    for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) {\n        ble_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]);\n    }\n\n    typedef struct {\n        uint8_t report_count;\n        BleGattCharacteristicInstance* chars;\n    } HidSvcReportCharProps;\n\n    HidSvcReportCharProps hid_report_chars[] = {\n        {BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},\n        {BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},\n        {BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},\n    };\n\n    for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);\n        report_type_idx++) {\n        for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;\n            report_idx++) {\n            ble_gatt_characteristic_delete(\n                hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]);\n        }\n    }\n\n    // Delete service\n    ble_gatt_service_delete(hid_svc->svc_handle);\n    free(hid_svc);\n}\n"
  },
  {
    "path": "lib/ble_profile/extra_services/hid_service.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct BleServiceHid BleServiceHid;\n\nBleServiceHid* ble_svc_hid_start(void);\n\nvoid ble_svc_hid_stop(BleServiceHid* service);\n\nbool ble_svc_hid_update_report_map(BleServiceHid* service, const uint8_t* data, uint16_t len);\n\nbool ble_svc_hid_update_input_report(\n    BleServiceHid* service,\n    uint8_t input_report_num,\n    uint8_t* data,\n    uint16_t len);\n\n// Expects data to be of length BLE_SVC_HID_INFO_LEN (4 bytes)\nbool ble_svc_hid_update_info(BleServiceHid* service, uint8_t* data);\n\n#ifdef __cplusplus\n}\n#endif"
  },
  {
    "path": "lib/cmsis_core/cmsis_armcc.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_armcc.h\n * @brief    CMSIS compiler ARMCC (Arm Compiler 5) header file\n * @version  V5.4.0\n * @date     20. January 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CMSIS_ARMCC_H\n#define __CMSIS_ARMCC_H\n\n\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677)\n  #error \"Please use Arm Compiler Toolchain V4.0.677 or later!\"\n#endif\n\n/* CMSIS compiler control architecture macros */\n#if ((defined (__TARGET_ARCH_6_M  ) && (__TARGET_ARCH_6_M   == 1)) || \\\n     (defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M  == 1))   )\n  #define __ARM_ARCH_6M__           1\n#endif\n\n#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M  == 1))\n  #define __ARM_ARCH_7M__           1\n#endif\n\n#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1))\n  #define __ARM_ARCH_7EM__          1\n#endif\n\n  /* __ARM_ARCH_8M_BASE__  not applicable */\n  /* __ARM_ARCH_8M_MAIN__  not applicable */\n  /* __ARM_ARCH_8_1M_MAIN__  not applicable */\n\n/* CMSIS compiler control DSP macros */\n#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     )\n  #define __ARM_FEATURE_DSP         1\n#endif\n\n/* CMSIS compiler specific defines */\n#ifndef   __ASM\n  #define __ASM                                  __asm\n#endif\n#ifndef   __INLINE\n  #define __INLINE                               __inline\n#endif\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE                        static __inline\n#endif\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE                   static __forceinline\n#endif\n#ifndef   __NO_RETURN\n  #define __NO_RETURN                            __declspec(noreturn)\n#endif\n#ifndef   __USED\n  #define __USED                                 __attribute__((used))\n#endif\n#ifndef   __WEAK\n  #define __WEAK                                 __attribute__((weak))\n#endif\n#ifndef   __PACKED\n  #define __PACKED                               __attribute__((packed))\n#endif\n#ifndef   __PACKED_STRUCT\n  #define __PACKED_STRUCT                        __packed struct\n#endif\n#ifndef   __PACKED_UNION\n  #define __PACKED_UNION                         __packed union\n#endif\n#ifndef   __UNALIGNED_UINT32        /* deprecated */\n  #define __UNALIGNED_UINT32(x)                  (*((__packed uint32_t *)(x)))\n#endif\n#ifndef   __UNALIGNED_UINT16_WRITE\n  #define __UNALIGNED_UINT16_WRITE(addr, val)    ((*((__packed uint16_t *)(addr))) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT16_READ\n  #define __UNALIGNED_UINT16_READ(addr)          (*((const __packed uint16_t *)(addr)))\n#endif\n#ifndef   __UNALIGNED_UINT32_WRITE\n  #define __UNALIGNED_UINT32_WRITE(addr, val)    ((*((__packed uint32_t *)(addr))) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT32_READ\n  #define __UNALIGNED_UINT32_READ(addr)          (*((const __packed uint32_t *)(addr)))\n#endif\n#ifndef   __ALIGNED\n  #define __ALIGNED(x)                           __attribute__((aligned(x)))\n#endif\n#ifndef   __RESTRICT\n  #define __RESTRICT                             __restrict\n#endif\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER()                   __memory_changed()\n#endif\n#ifndef __NO_INIT\n  #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\"), zero_init))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x)                             __attribute__ ((alias(x)))\n#endif\n\n/* #########################  Startup and Lowlevel Init  ######################## */\n\n#ifndef __PROGRAM_START\n#define __PROGRAM_START           __main\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              Image$$ARM_LIB_STACK$$ZI$$Limit\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             Image$$ARM_LIB_STACK$$ZI$$Base\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __Vectors\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  __attribute__((used, section(\"RESET\")))\n#endif\n\n/* ##########################  Core Instruction Access  ######################### */\n/** \\defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface\n  Access to dedicated instructions\n  @{\n*/\n\n/**\n  \\brief   No Operation\n  \\details No Operation does nothing. This instruction can be used for code alignment purposes.\n */\n#define __NOP                             __nop\n\n\n/**\n  \\brief   Wait For Interrupt\n  \\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.\n */\n#define __WFI                             __wfi\n\n\n/**\n  \\brief   Wait For Event\n  \\details Wait For Event is a hint instruction that permits the processor to enter\n           a low-power state until one of a number of events occurs.\n */\n#define __WFE                             __wfe\n\n\n/**\n  \\brief   Send Event\n  \\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.\n */\n#define __SEV                             __sev\n\n\n/**\n  \\brief   Instruction Synchronization Barrier\n  \\details Instruction Synchronization Barrier flushes the pipeline in the processor,\n           so that all instructions following the ISB are fetched from cache or memory,\n           after the instruction has been completed.\n */\n#define __ISB()                           __isb(0xF)\n\n/**\n  \\brief   Data Synchronization Barrier\n  \\details Acts as a special kind of Data Memory Barrier.\n           It completes when all explicit memory accesses before this instruction complete.\n */\n#define __DSB()                           __dsb(0xF)\n\n/**\n  \\brief   Data Memory Barrier\n  \\details Ensures the apparent order of the explicit memory operations before\n           and after the instruction, without ensuring their completion.\n */\n#define __DMB()                           __dmb(0xF)\n\n\n/**\n  \\brief   Reverse byte order (32 bit)\n  \\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV                             __rev\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#ifndef __NO_EMBEDDED_ASM\n__attribute__((section(\".rev16_text\"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value)\n{\n  rev16 r0, r0\n  bx lr\n}\n#endif\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#ifndef __NO_EMBEDDED_ASM\n__attribute__((section(\".revsh_text\"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value)\n{\n  revsh r0, r0\n  bx lr\n}\n#endif\n\n\n/**\n  \\brief   Rotate Right in unsigned value (32 bit)\n  \\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.\n  \\param [in]    op1  Value to rotate\n  \\param [in]    op2  Number of Bits to rotate\n  \\return               Rotated value\n */\n#define __ROR                             __ror\n\n\n/**\n  \\brief   Breakpoint\n  \\details Causes the processor to enter Debug state.\n           Debug tools can use this to investigate system state when the instruction at a particular address is reached.\n  \\param [in]    value  is ignored by the processor.\n                 If required, a debugger can use it to store additional information about the breakpoint.\n */\n#define __BKPT(value)                       __breakpoint(value)\n\n\n/**\n  \\brief   Reverse bit order of value\n  \\details Reverses the bit order of the given value.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n     (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     )\n  #define __RBIT                          __rbit\n#else\n__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value)\n{\n  uint32_t result;\n  uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */\n\n  result = value;                      /* r will be reversed bits of v; first get LSB of v */\n  for (value >>= 1U; value != 0U; value >>= 1U)\n  {\n    result <<= 1U;\n    result |= value & 1U;\n    s--;\n  }\n  result <<= s;                        /* shift when v's highest bits are zero */\n  return result;\n}\n#endif\n\n\n/**\n  \\brief   Count leading zeros\n  \\details Counts the number of leading zeros of a data value.\n  \\param [in]  value  Value to count the leading zeros\n  \\return             number of leading zeros in value\n */\n#define __CLZ                             __clz\n\n\n#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n     (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     )\n\n/**\n  \\brief   LDR Exclusive (8 bit)\n  \\details Executes a exclusive LDR instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __LDREXB(ptr)                                                        ((uint8_t ) __ldrex(ptr))\n#else\n  #define __LDREXB(ptr)          _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") ((uint8_t ) __ldrex(ptr))  _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   LDR Exclusive (16 bit)\n  \\details Executes a exclusive LDR instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __LDREXH(ptr)                                                        ((uint16_t) __ldrex(ptr))\n#else\n  #define __LDREXH(ptr)          _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") ((uint16_t) __ldrex(ptr))  _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   LDR Exclusive (32 bit)\n  \\details Executes a exclusive LDR instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __LDREXW(ptr)                                                        ((uint32_t ) __ldrex(ptr))\n#else\n  #define __LDREXW(ptr)          _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") ((uint32_t ) __ldrex(ptr))  _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   STR Exclusive (8 bit)\n  \\details Executes a exclusive STR instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __STREXB(value, ptr)                                                 __strex(value, ptr)\n#else\n  #define __STREXB(value, ptr)   _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") __strex(value, ptr)        _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   STR Exclusive (16 bit)\n  \\details Executes a exclusive STR instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __STREXH(value, ptr)                                                 __strex(value, ptr)\n#else\n  #define __STREXH(value, ptr)   _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") __strex(value, ptr)        _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   STR Exclusive (32 bit)\n  \\details Executes a exclusive STR instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)\n  #define __STREXW(value, ptr)                                                 __strex(value, ptr)\n#else\n  #define __STREXW(value, ptr)   _Pragma(\"push\") _Pragma(\"diag_suppress 3731\") __strex(value, ptr)        _Pragma(\"pop\")\n#endif\n\n\n/**\n  \\brief   Remove the exclusive lock\n  \\details Removes the exclusive lock which is created by LDREX.\n */\n#define __CLREX                           __clrex\n\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n#define __SSAT                            __ssat\n\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n#define __USAT                            __usat\n\n\n/**\n  \\brief   Rotate Right with Extend (32 bit)\n  \\details Moves each bit of a bitstring right by one bit.\n           The carry input is shifted in at the left end of the bitstring.\n  \\param [in]    value  Value to rotate\n  \\return               Rotated value\n */\n#ifndef __NO_EMBEDDED_ASM\n__attribute__((section(\".rrx_text\"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value)\n{\n  rrx r0, r0\n  bx lr\n}\n#endif\n\n\n/**\n  \\brief   LDRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged LDRT instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define __LDRBT(ptr)                      ((uint8_t )  __ldrt(ptr))\n\n\n/**\n  \\brief   LDRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged LDRT instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define __LDRHT(ptr)                      ((uint16_t)  __ldrt(ptr))\n\n\n/**\n  \\brief   LDRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged LDRT instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define __LDRT(ptr)                       ((uint32_t ) __ldrt(ptr))\n\n\n/**\n  \\brief   STRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged STRT instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n#define __STRBT(value, ptr)               __strt(value, ptr)\n\n\n/**\n  \\brief   STRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged STRT instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n#define __STRHT(value, ptr)               __strt(value, ptr)\n\n\n/**\n  \\brief   STRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged STRT instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n#define __STRT(value, ptr)                __strt(value, ptr)\n\n#else  /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n           (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     ) */\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat)\n{\n  if ((sat >= 1U) && (sat <= 32U))\n  {\n    const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n    const int32_t min = -1 - max ;\n    if (val > max)\n    {\n      return max;\n    }\n    else if (val < min)\n    {\n      return min;\n    }\n  }\n  return val;\n}\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat)\n{\n  if (sat <= 31U)\n  {\n    const uint32_t max = ((1U << sat) - 1U);\n    if (val > (int32_t)max)\n    {\n      return max;\n    }\n    else if (val < 0)\n    {\n      return 0U;\n    }\n  }\n  return (uint32_t)val;\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n           (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     ) */\n\n/*@}*/ /* end of group CMSIS_Core_InstructionInterface */\n\n\n/* ###########################  Core Function Access  ########################### */\n/** \\ingroup  CMSIS_Core_FunctionInterface\n    \\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions\n  @{\n */\n\n/**\n  \\brief   Enable IRQ Interrupts\n  \\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n/* intrinsic void __enable_irq();     */\n\n\n/**\n  \\brief   Disable IRQ Interrupts\n  \\details Disables IRQ interrupts by setting special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n/* intrinsic void __disable_irq();    */\n\n/**\n  \\brief   Get Control Register\n  \\details Returns the content of the Control Register.\n  \\return               Control Register value\n */\n__STATIC_INLINE uint32_t __get_CONTROL(void)\n{\n  register uint32_t __regControl         __ASM(\"control\");\n  return(__regControl);\n}\n\n\n/**\n  \\brief   Set Control Register\n  \\details Writes the given value to the Control Register.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_INLINE void __set_CONTROL(uint32_t control)\n{\n  register uint32_t __regControl         __ASM(\"control\");\n  __regControl = control;\n  __ISB();\n}\n\n\n/**\n  \\brief   Get IPSR Register\n  \\details Returns the content of the IPSR Register.\n  \\return               IPSR Register value\n */\n__STATIC_INLINE uint32_t __get_IPSR(void)\n{\n  register uint32_t __regIPSR          __ASM(\"ipsr\");\n  return(__regIPSR);\n}\n\n\n/**\n  \\brief   Get APSR Register\n  \\details Returns the content of the APSR Register.\n  \\return               APSR Register value\n */\n__STATIC_INLINE uint32_t __get_APSR(void)\n{\n  register uint32_t __regAPSR          __ASM(\"apsr\");\n  return(__regAPSR);\n}\n\n\n/**\n  \\brief   Get xPSR Register\n  \\details Returns the content of the xPSR Register.\n  \\return               xPSR Register value\n */\n__STATIC_INLINE uint32_t __get_xPSR(void)\n{\n  register uint32_t __regXPSR          __ASM(\"xpsr\");\n  return(__regXPSR);\n}\n\n\n/**\n  \\brief   Get Process Stack Pointer\n  \\details Returns the current value of the Process Stack Pointer (PSP).\n  \\return               PSP Register value\n */\n__STATIC_INLINE uint32_t __get_PSP(void)\n{\n  register uint32_t __regProcessStackPointer  __ASM(\"psp\");\n  return(__regProcessStackPointer);\n}\n\n\n/**\n  \\brief   Set Process Stack Pointer\n  \\details Assigns the given value to the Process Stack Pointer (PSP).\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)\n{\n  register uint32_t __regProcessStackPointer  __ASM(\"psp\");\n  __regProcessStackPointer = topOfProcStack;\n}\n\n\n/**\n  \\brief   Get Main Stack Pointer\n  \\details Returns the current value of the Main Stack Pointer (MSP).\n  \\return               MSP Register value\n */\n__STATIC_INLINE uint32_t __get_MSP(void)\n{\n  register uint32_t __regMainStackPointer     __ASM(\"msp\");\n  return(__regMainStackPointer);\n}\n\n\n/**\n  \\brief   Set Main Stack Pointer\n  \\details Assigns the given value to the Main Stack Pointer (MSP).\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)\n{\n  register uint32_t __regMainStackPointer     __ASM(\"msp\");\n  __regMainStackPointer = topOfMainStack;\n}\n\n\n/**\n  \\brief   Get Priority Mask\n  \\details Returns the current state of the priority mask bit from the Priority Mask Register.\n  \\return               Priority Mask value\n */\n__STATIC_INLINE uint32_t __get_PRIMASK(void)\n{\n  register uint32_t __regPriMask         __ASM(\"primask\");\n  return(__regPriMask);\n}\n\n\n/**\n  \\brief   Set Priority Mask\n  \\details Assigns the given value to the Priority Mask Register.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_INLINE void __set_PRIMASK(uint32_t priMask)\n{\n  register uint32_t __regPriMask         __ASM(\"primask\");\n  __regPriMask = (priMask);\n}\n\n\n#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n     (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     )\n\n/**\n  \\brief   Enable FIQ\n  \\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n#define __enable_fault_irq                __enable_fiq\n\n\n/**\n  \\brief   Disable FIQ\n  \\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n#define __disable_fault_irq               __disable_fiq\n\n\n/**\n  \\brief   Get Base Priority\n  \\details Returns the current value of the Base Priority register.\n  \\return               Base Priority register value\n */\n__STATIC_INLINE uint32_t  __get_BASEPRI(void)\n{\n  register uint32_t __regBasePri         __ASM(\"basepri\");\n  return(__regBasePri);\n}\n\n\n/**\n  \\brief   Set Base Priority\n  \\details Assigns the given value to the Base Priority register.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_INLINE void __set_BASEPRI(uint32_t basePri)\n{\n  register uint32_t __regBasePri         __ASM(\"basepri\");\n  __regBasePri = (basePri & 0xFFU);\n}\n\n\n/**\n  \\brief   Set Base Priority with condition\n  \\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,\n           or the new value increases the BASEPRI priority level.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_INLINE void __set_BASEPRI_MAX(uint32_t basePri)\n{\n  register uint32_t __regBasePriMax      __ASM(\"basepri_max\");\n  __regBasePriMax = (basePri & 0xFFU);\n}\n\n\n/**\n  \\brief   Get Fault Mask\n  \\details Returns the current value of the Fault Mask register.\n  \\return               Fault Mask register value\n */\n__STATIC_INLINE uint32_t __get_FAULTMASK(void)\n{\n  register uint32_t __regFaultMask       __ASM(\"faultmask\");\n  return(__regFaultMask);\n}\n\n\n/**\n  \\brief   Set Fault Mask\n  \\details Assigns the given value to the Fault Mask register.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)\n{\n  register uint32_t __regFaultMask       __ASM(\"faultmask\");\n  __regFaultMask = (faultMask & (uint32_t)1U);\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__  == 1)) || \\\n           (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     ) */\n\n\n/**\n  \\brief   Get FPSCR\n  \\details Returns the current value of the Floating Point Status/Control register.\n  \\return               Floating Point Status/Control register value\n */\n__STATIC_INLINE uint32_t __get_FPSCR(void)\n{\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n  register uint32_t __regfpscr         __ASM(\"fpscr\");\n  return(__regfpscr);\n#else\n   return(0U);\n#endif\n}\n\n\n/**\n  \\brief   Set FPSCR\n  \\details Assigns the given value to the Floating Point Status/Control register.\n  \\param [in]    fpscr  Floating Point Status/Control value to set\n */\n__STATIC_INLINE void __set_FPSCR(uint32_t fpscr)\n{\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n  register uint32_t __regfpscr         __ASM(\"fpscr\");\n  __regfpscr = (fpscr);\n#else\n  (void)fpscr;\n#endif\n}\n\n\n/*@} end of CMSIS_Core_RegAccFunctions */\n\n\n/* ###################  Compiler specific Intrinsics  ########################### */\n/** \\defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics\n  Access to dedicated SIMD instructions\n  @{\n*/\n\n#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     )\n\n#define __SADD8                           __sadd8\n#define __QADD8                           __qadd8\n#define __SHADD8                          __shadd8\n#define __UADD8                           __uadd8\n#define __UQADD8                          __uqadd8\n#define __UHADD8                          __uhadd8\n#define __SSUB8                           __ssub8\n#define __QSUB8                           __qsub8\n#define __SHSUB8                          __shsub8\n#define __USUB8                           __usub8\n#define __UQSUB8                          __uqsub8\n#define __UHSUB8                          __uhsub8\n#define __SADD16                          __sadd16\n#define __QADD16                          __qadd16\n#define __SHADD16                         __shadd16\n#define __UADD16                          __uadd16\n#define __UQADD16                         __uqadd16\n#define __UHADD16                         __uhadd16\n#define __SSUB16                          __ssub16\n#define __QSUB16                          __qsub16\n#define __SHSUB16                         __shsub16\n#define __USUB16                          __usub16\n#define __UQSUB16                         __uqsub16\n#define __UHSUB16                         __uhsub16\n#define __SASX                            __sasx\n#define __QASX                            __qasx\n#define __SHASX                           __shasx\n#define __UASX                            __uasx\n#define __UQASX                           __uqasx\n#define __UHASX                           __uhasx\n#define __SSAX                            __ssax\n#define __QSAX                            __qsax\n#define __SHSAX                           __shsax\n#define __USAX                            __usax\n#define __UQSAX                           __uqsax\n#define __UHSAX                           __uhsax\n#define __USAD8                           __usad8\n#define __USADA8                          __usada8\n#define __SSAT16                          __ssat16\n#define __USAT16                          __usat16\n#define __UXTB16                          __uxtb16\n#define __UXTAB16                         __uxtab16\n#define __SXTB16                          __sxtb16\n#define __SXTAB16                         __sxtab16\n#define __SMUAD                           __smuad\n#define __SMUADX                          __smuadx\n#define __SMLAD                           __smlad\n#define __SMLADX                          __smladx\n#define __SMLALD                          __smlald\n#define __SMLALDX                         __smlaldx\n#define __SMUSD                           __smusd\n#define __SMUSDX                          __smusdx\n#define __SMLSD                           __smlsd\n#define __SMLSDX                          __smlsdx\n#define __SMLSLD                          __smlsld\n#define __SMLSLDX                         __smlsldx\n#define __SEL                             __sel\n#define __QADD                            __qadd\n#define __QSUB                            __qsub\n\n#define __PKHBT(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0x0000FFFFUL) |  \\\n                                           ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL)  )\n\n#define __PKHTB(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0xFFFF0000UL) |  \\\n                                           ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL)  )\n\n#define __SMMLA(ARG1,ARG2,ARG3)          ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \\\n                                                      ((int64_t)(ARG3) << 32U)     ) >> 32U))\n\n#define __SXTB16_RORn(ARG1, ARG2)        __SXTB16(__ROR(ARG1, ARG2))\n\n#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))\n\n#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))     ) */\n/*@} end of group CMSIS_SIMD_intrinsics */\n\n\n#endif /* __CMSIS_ARMCC_H */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_armclang.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_armclang.h\n * @brief    CMSIS compiler armclang (Arm Compiler 6) header file\n * @version  V5.5.0\n * @date     20. January 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */\n\n#ifndef __CMSIS_ARMCLANG_H\n#define __CMSIS_ARMCLANG_H\n\n#pragma clang system_header   /* treat file as system include file */\n\n/* CMSIS compiler specific defines */\n#ifndef   __ASM\n  #define __ASM                                  __asm\n#endif\n#ifndef   __INLINE\n  #define __INLINE                               __inline\n#endif\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE                        static __inline\n#endif\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE                   __attribute__((always_inline)) static __inline\n#endif\n#ifndef   __NO_RETURN\n  #define __NO_RETURN                            __attribute__((__noreturn__))\n#endif\n#ifndef   __USED\n  #define __USED                                 __attribute__((used))\n#endif\n#ifndef   __WEAK\n  #define __WEAK                                 __attribute__((weak))\n#endif\n#ifndef   __PACKED\n  #define __PACKED                               __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_STRUCT\n  #define __PACKED_STRUCT                        struct __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_UNION\n  #define __PACKED_UNION                         union __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __UNALIGNED_UINT32        /* deprecated */\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */\n  struct __attribute__((packed)) T_UINT32 { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n#endif\n#ifndef   __UNALIGNED_UINT16_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */\n  __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT16_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */\n  __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __UNALIGNED_UINT32_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */\n  __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT32_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */\n  __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __ALIGNED\n  #define __ALIGNED(x)                           __attribute__((aligned(x)))\n#endif\n#ifndef   __RESTRICT\n  #define __RESTRICT                             __restrict\n#endif\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER()                   __ASM volatile(\"\":::\"memory\")\n#endif\n#ifndef __NO_INIT\n  #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x)                             __attribute__ ((alias(x)))\n#endif\n\n\n/* #########################  Startup and Lowlevel Init  ######################## */\n\n#ifndef __PROGRAM_START\n#define __PROGRAM_START           __main\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              Image$$ARM_LIB_STACK$$ZI$$Limit\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             Image$$ARM_LIB_STACK$$ZI$$Base\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __Vectors\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  __attribute__((used, section(\"RESET\")))\n#endif\n\n#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)\n#ifndef __STACK_SEAL\n#define __STACK_SEAL              Image$$STACKSEAL$$ZI$$Base\n#endif\n\n#ifndef __TZ_STACK_SEAL_SIZE\n#define __TZ_STACK_SEAL_SIZE      8U\n#endif\n\n#ifndef __TZ_STACK_SEAL_VALUE\n#define __TZ_STACK_SEAL_VALUE     0xFEF5EDA5FEF5EDA5ULL\n#endif\n\n\n__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) {\n  *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE;\n}\n#endif\n\n\n/* ##########################  Core Instruction Access  ######################### */\n/** \\defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface\n  Access to dedicated instructions\n  @{\n*/\n\n/* Define macros for porting to both thumb1 and thumb2.\n * For thumb1, use low register (r0-r7), specified by constraint \"l\"\n * Otherwise, use general registers, specified by constraint \"r\" */\n#if defined (__thumb__) && !defined (__thumb2__)\n#define __CMSIS_GCC_OUT_REG(r) \"=l\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+l\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"l\" (r)\n#else\n#define __CMSIS_GCC_OUT_REG(r) \"=r\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+r\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"r\" (r)\n#endif\n\n/**\n  \\brief   No Operation\n  \\details No Operation does nothing. This instruction can be used for code alignment purposes.\n */\n#define __NOP          __builtin_arm_nop\n\n/**\n  \\brief   Wait For Interrupt\n  \\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.\n */\n#define __WFI          __builtin_arm_wfi\n\n\n/**\n  \\brief   Wait For Event\n  \\details Wait For Event is a hint instruction that permits the processor to enter\n           a low-power state until one of a number of events occurs.\n */\n#define __WFE          __builtin_arm_wfe\n\n\n/**\n  \\brief   Send Event\n  \\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.\n */\n#define __SEV          __builtin_arm_sev\n\n\n/**\n  \\brief   Instruction Synchronization Barrier\n  \\details Instruction Synchronization Barrier flushes the pipeline in the processor,\n           so that all instructions following the ISB are fetched from cache or memory,\n           after the instruction has been completed.\n */\n#define __ISB()        __builtin_arm_isb(0xF)\n\n/**\n  \\brief   Data Synchronization Barrier\n  \\details Acts as a special kind of Data Memory Barrier.\n           It completes when all explicit memory accesses before this instruction complete.\n */\n#define __DSB()        __builtin_arm_dsb(0xF)\n\n\n/**\n  \\brief   Data Memory Barrier\n  \\details Ensures the apparent order of the explicit memory operations before\n           and after the instruction, without ensuring their completion.\n */\n#define __DMB()        __builtin_arm_dmb(0xF)\n\n\n/**\n  \\brief   Reverse byte order (32 bit)\n  \\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV(value)   __builtin_bswap32(value)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV16(value) __ROR(__REV(value), 16)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REVSH(value) (int16_t)__builtin_bswap16(value)\n\n\n/**\n  \\brief   Rotate Right in unsigned value (32 bit)\n  \\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.\n  \\param [in]    op1  Value to rotate\n  \\param [in]    op2  Number of Bits to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2)\n{\n  op2 %= 32U;\n  if (op2 == 0U)\n  {\n    return op1;\n  }\n  return (op1 >> op2) | (op1 << (32U - op2));\n}\n\n\n/**\n  \\brief   Breakpoint\n  \\details Causes the processor to enter Debug state.\n           Debug tools can use this to investigate system state when the instruction at a particular address is reached.\n  \\param [in]    value  is ignored by the processor.\n                 If required, a debugger can use it to store additional information about the breakpoint.\n */\n#define __BKPT(value)     __ASM volatile (\"bkpt \"#value)\n\n\n/**\n  \\brief   Reverse bit order of value\n  \\details Reverses the bit order of the given value.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __RBIT            __builtin_arm_rbit\n\n/**\n  \\brief   Count leading zeros\n  \\details Counts the number of leading zeros of a data value.\n  \\param [in]  value  Value to count the leading zeros\n  \\return             number of leading zeros in value\n */\n__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value)\n{\n  /* Even though __builtin_clz produces a CLZ instruction on ARM, formally\n     __builtin_clz(0) is undefined behaviour, so handle this case specially.\n     This guarantees ARM-compatible results if happening to compile on a non-ARM\n     target, and ensures the compiler doesn't decide to activate any\n     optimisations using the logic \"value was passed to __builtin_clz, so it\n     is non-zero\".\n     ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a\n     single CLZ instruction.\n   */\n  if (value == 0U)\n  {\n    return 32U;\n  }\n  return __builtin_clz(value);\n}\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   LDR Exclusive (8 bit)\n  \\details Executes a exclusive LDR instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define __LDREXB        (uint8_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (16 bit)\n  \\details Executes a exclusive LDR instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define __LDREXH        (uint16_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (32 bit)\n  \\details Executes a exclusive LDR instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define __LDREXW        (uint32_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   STR Exclusive (8 bit)\n  \\details Executes a exclusive STR instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXB        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (16 bit)\n  \\details Executes a exclusive STR instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXH        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (32 bit)\n  \\details Executes a exclusive STR instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXW        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   Remove the exclusive lock\n  \\details Removes the exclusive lock which is created by LDREX.\n */\n#define __CLREX             __builtin_arm_clrex\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n#define __SSAT             __builtin_arm_ssat\n\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n#define __USAT             __builtin_arm_usat\n\n\n/**\n  \\brief   Rotate Right with Extend (32 bit)\n  \\details Moves each bit of a bitstring right by one bit.\n           The carry input is shifted in at the left end of the bitstring.\n  \\param [in]    value  Value to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value)\n{\n  uint32_t result;\n\n  __ASM volatile (\"rrx %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return(result);\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged LDRT instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrbt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint8_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged LDRT instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrht %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint16_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged LDRT instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return(result);\n}\n\n\n/**\n  \\brief   STRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged STRT instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"strbt %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged STRT instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"strht %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged STRT instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"strt %1, %0\" : \"=Q\" (*ptr) : \"r\" (value) );\n}\n\n#else /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n          (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n          (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n          (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat)\n{\n  if ((sat >= 1U) && (sat <= 32U))\n  {\n    const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n    const int32_t min = -1 - max ;\n    if (val > max)\n    {\n      return max;\n    }\n    else if (val < min)\n    {\n      return min;\n    }\n  }\n  return val;\n}\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat)\n{\n  if (sat <= 31U)\n  {\n    const uint32_t max = ((1U << sat) - 1U);\n    if (val > (int32_t)max)\n    {\n      return max;\n    }\n    else if (val < 0)\n    {\n      return 0U;\n    }\n  }\n  return (uint32_t)val;\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Load-Acquire (8 bit)\n  \\details Executes a LDAB instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldab %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint8_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (16 bit)\n  \\details Executes a LDAH instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldah %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint16_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (32 bit)\n  \\details Executes a LDA instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"lda %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return(result);\n}\n\n\n/**\n  \\brief   Store-Release (8 bit)\n  \\details Executes a STLB instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"stlb %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (16 bit)\n  \\details Executes a STLH instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"stlh %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (32 bit)\n  \\details Executes a STL instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"stl %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (8 bit)\n  \\details Executes a LDAB exclusive instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define     __LDAEXB                 (uint8_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (16 bit)\n  \\details Executes a LDAH exclusive instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define     __LDAEXH                 (uint16_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (32 bit)\n  \\details Executes a LDA exclusive instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define     __LDAEX                  (uint32_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Store-Release Exclusive (8 bit)\n  \\details Executes a STLB exclusive instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXB                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (16 bit)\n  \\details Executes a STLH exclusive instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXH                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (32 bit)\n  \\details Executes a STL exclusive instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEX                  (uint32_t)__builtin_arm_stlex\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/** @}*/ /* end of group CMSIS_Core_InstructionInterface */\n\n\n/* ###########################  Core Function Access  ########################### */\n/** \\ingroup  CMSIS_Core_FunctionInterface\n    \\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions\n  @{\n */\n\n/**\n  \\brief   Enable IRQ Interrupts\n  \\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __enable_irq(void)\n{\n  __ASM volatile (\"cpsie i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Disable IRQ Interrupts\n  \\details Disables IRQ interrupts by setting special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __disable_irq(void)\n{\n  __ASM volatile (\"cpsid i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Get Control Register\n  \\details Returns the content of the Control Register.\n  \\return               Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_CONTROL(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Control Register (non-secure)\n  \\details Returns the content of the non-secure Control Register when in secure mode.\n  \\return               non-secure Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Control Register\n  \\details Writes the given value to the Control Register.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)\n{\n  __ASM volatile (\"MSR control, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Control Register (non-secure)\n  \\details Writes the given value to the non-secure Control Register when in secure state.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)\n{\n  __ASM volatile (\"MSR control_ns, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n#endif\n\n\n/**\n  \\brief   Get IPSR Register\n  \\details Returns the content of the IPSR Register.\n  \\return               IPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_IPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, ipsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get APSR Register\n  \\details Returns the content of the APSR Register.\n  \\return               APSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_APSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, apsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get xPSR Register\n  \\details Returns the content of the xPSR Register.\n  \\return               xPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_xPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, xpsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get Process Stack Pointer\n  \\details Returns the current value of the Process Stack Pointer (PSP).\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp\"  : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp_ns\"  : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer\n  \\details Assigns the given value to the Process Stack Pointer (PSP).\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp, %0\" : : \"r\" (topOfProcStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp_ns, %0\" : : \"r\" (topOfProcStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer\n  \\details Returns the current value of the Main Stack Pointer (MSP).\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Main Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer\n  \\details Assigns the given value to the Main Stack Pointer (MSP).\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp, %0\" : : \"r\" (topOfMainStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Main Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp_ns, %0\" : : \"r\" (topOfMainStack) : );\n}\n#endif\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Stack Pointer (SP) when in secure state.\n  \\return               SP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, sp_ns\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Set Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state.\n  \\param [in]    topOfStack  Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)\n{\n  __ASM volatile (\"MSR sp_ns, %0\" : : \"r\" (topOfStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Priority Mask\n  \\details Returns the current state of the priority mask bit from the Priority Mask Register.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Priority Mask (non-secure)\n  \\details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Priority Mask\n  \\details Assigns the given value to the Priority Mask Register.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask, %0\" : : \"r\" (priMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Priority Mask (non-secure)\n  \\details Assigns the given value to the non-secure Priority Mask Register when in secure state.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask_ns, %0\" : : \"r\" (priMask) : \"memory\");\n}\n#endif\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n/**\n  \\brief   Enable FIQ\n  \\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __enable_fault_irq(void)\n{\n  __ASM volatile (\"cpsie f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Disable FIQ\n  \\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __disable_fault_irq(void)\n{\n  __ASM volatile (\"cpsid f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Get Base Priority\n  \\details Returns the current value of the Base Priority register.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Base Priority (non-secure)\n  \\details Returns the current value of the non-secure Base Priority register when in secure state.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority\n  \\details Assigns the given value to the Base Priority register.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Base Priority (non-secure)\n  \\details Assigns the given value to the non-secure Base Priority register when in secure state.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_ns, %0\" : : \"r\" (basePri) : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority with condition\n  \\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,\n           or the new value increases the BASEPRI priority level.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_max, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n/**\n  \\brief   Get Fault Mask\n  \\details Returns the current value of the Fault Mask register.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Fault Mask (non-secure)\n  \\details Returns the current value of the non-secure Fault Mask register when in secure state.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Fault Mask\n  \\details Assigns the given value to the Fault Mask register.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Fault Mask (non-secure)\n  \\details Assigns the given value to the non-secure Fault Mask register when in secure state.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask_ns, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Get Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the Process Stack Pointer Limit (PSPLIM).\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n\n#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim_ns\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the Process Stack Pointer Limit (PSPLIM).\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim, %0\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim_ns, %0\\n\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the Main Stack Pointer Limit (MSPLIM).\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim\" : \"=r\" (result) );\n  return result;\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Get Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state.\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim_ns\" : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the Main Stack Pointer Limit (MSPLIM).\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state.\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim_ns, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/**\n  \\brief   Get FPSCR\n  \\details Returns the current value of the Floating Point Status/Control register.\n  \\return               Floating Point Status/Control register value\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __get_FPSCR      (uint32_t)__builtin_arm_get_fpscr\n#else\n#define __get_FPSCR()      ((uint32_t)0U)\n#endif\n\n/**\n  \\brief   Set FPSCR\n  \\details Assigns the given value to the Floating Point Status/Control register.\n  \\param [in]    fpscr  Floating Point Status/Control value to set\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __set_FPSCR      __builtin_arm_set_fpscr\n#else\n#define __set_FPSCR(fpscr)      ((void)(fpscr))\n#endif\n\n\n/** @} end of CMSIS_Core_RegAccFunctions */\n\n\n/* ###################  Compiler specific Intrinsics  ########################### */\n/** \\defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics\n  Access to dedicated SIMD instructions\n  @{\n*/\n\n#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1))\n\n#define     __SADD8                 __builtin_arm_sadd8\n#define     __QADD8                 __builtin_arm_qadd8\n#define     __SHADD8                __builtin_arm_shadd8\n#define     __UADD8                 __builtin_arm_uadd8\n#define     __UQADD8                __builtin_arm_uqadd8\n#define     __UHADD8                __builtin_arm_uhadd8\n#define     __SSUB8                 __builtin_arm_ssub8\n#define     __QSUB8                 __builtin_arm_qsub8\n#define     __SHSUB8                __builtin_arm_shsub8\n#define     __USUB8                 __builtin_arm_usub8\n#define     __UQSUB8                __builtin_arm_uqsub8\n#define     __UHSUB8                __builtin_arm_uhsub8\n#define     __SADD16                __builtin_arm_sadd16\n#define     __QADD16                __builtin_arm_qadd16\n#define     __SHADD16               __builtin_arm_shadd16\n#define     __UADD16                __builtin_arm_uadd16\n#define     __UQADD16               __builtin_arm_uqadd16\n#define     __UHADD16               __builtin_arm_uhadd16\n#define     __SSUB16                __builtin_arm_ssub16\n#define     __QSUB16                __builtin_arm_qsub16\n#define     __SHSUB16               __builtin_arm_shsub16\n#define     __USUB16                __builtin_arm_usub16\n#define     __UQSUB16               __builtin_arm_uqsub16\n#define     __UHSUB16               __builtin_arm_uhsub16\n#define     __SASX                  __builtin_arm_sasx\n#define     __QASX                  __builtin_arm_qasx\n#define     __SHASX                 __builtin_arm_shasx\n#define     __UASX                  __builtin_arm_uasx\n#define     __UQASX                 __builtin_arm_uqasx\n#define     __UHASX                 __builtin_arm_uhasx\n#define     __SSAX                  __builtin_arm_ssax\n#define     __QSAX                  __builtin_arm_qsax\n#define     __SHSAX                 __builtin_arm_shsax\n#define     __USAX                  __builtin_arm_usax\n#define     __UQSAX                 __builtin_arm_uqsax\n#define     __UHSAX                 __builtin_arm_uhsax\n#define     __USAD8                 __builtin_arm_usad8\n#define     __USADA8                __builtin_arm_usada8\n#define     __SSAT16                __builtin_arm_ssat16\n#define     __USAT16                __builtin_arm_usat16\n#define     __UXTB16                __builtin_arm_uxtb16\n#define     __UXTAB16               __builtin_arm_uxtab16\n#define     __SXTB16                __builtin_arm_sxtb16\n#define     __SXTAB16               __builtin_arm_sxtab16\n#define     __SMUAD                 __builtin_arm_smuad\n#define     __SMUADX                __builtin_arm_smuadx\n#define     __SMLAD                 __builtin_arm_smlad\n#define     __SMLADX                __builtin_arm_smladx\n#define     __SMLALD                __builtin_arm_smlald\n#define     __SMLALDX               __builtin_arm_smlaldx\n#define     __SMUSD                 __builtin_arm_smusd\n#define     __SMUSDX                __builtin_arm_smusdx\n#define     __SMLSD                 __builtin_arm_smlsd\n#define     __SMLSDX                __builtin_arm_smlsdx\n#define     __SMLSLD                __builtin_arm_smlsld\n#define     __SMLSLDX               __builtin_arm_smlsldx\n#define     __SEL                   __builtin_arm_sel\n#define     __QADD                  __builtin_arm_qadd\n#define     __QSUB                  __builtin_arm_qsub\n\n#define __PKHBT(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0x0000FFFFUL) |  \\\n                                           ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL)  )\n\n#define __PKHTB(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0xFFFF0000UL) |  \\\n                                           ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL)  )\n\n#define __SXTB16_RORn(ARG1, ARG2)        __SXTB16(__ROR(ARG1, ARG2))\n\n#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))\n\n__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3)\n{\n  int32_t result;\n\n  __ASM volatile (\"smmla %0, %1, %2, %3\" : \"=r\" (result): \"r\"  (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n#endif /* (__ARM_FEATURE_DSP == 1) */\n/** @} end of group CMSIS_SIMD_intrinsics */\n\n\n#endif /* __CMSIS_ARMCLANG_H */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_armclang_ltm.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_armclang_ltm.h\n * @brief    CMSIS compiler armclang (Arm Compiler 6) header file\n * @version  V1.6.0\n * @date     20. January 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2018-2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */\n\n#ifndef __CMSIS_ARMCLANG_H\n#define __CMSIS_ARMCLANG_H\n\n#pragma clang system_header   /* treat file as system include file */\n\n/* CMSIS compiler specific defines */\n#ifndef   __ASM\n  #define __ASM                                  __asm\n#endif\n#ifndef   __INLINE\n  #define __INLINE                               __inline\n#endif\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE                        static __inline\n#endif\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE                   __attribute__((always_inline)) static __inline\n#endif\n#ifndef   __NO_RETURN\n  #define __NO_RETURN                            __attribute__((__noreturn__))\n#endif\n#ifndef   __USED\n  #define __USED                                 __attribute__((used))\n#endif\n#ifndef   __WEAK\n  #define __WEAK                                 __attribute__((weak))\n#endif\n#ifndef   __PACKED\n  #define __PACKED                               __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_STRUCT\n  #define __PACKED_STRUCT                        struct __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_UNION\n  #define __PACKED_UNION                         union __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __UNALIGNED_UINT32        /* deprecated */\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */\n  struct __attribute__((packed)) T_UINT32 { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n#endif\n#ifndef   __UNALIGNED_UINT16_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */\n  __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT16_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */\n  __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __UNALIGNED_UINT32_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */\n  __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT32_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */\n  __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __ALIGNED\n  #define __ALIGNED(x)                           __attribute__((aligned(x)))\n#endif\n#ifndef   __RESTRICT\n  #define __RESTRICT                             __restrict\n#endif\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER()                   __ASM volatile(\"\":::\"memory\")\n#endif\n#ifndef __NO_INIT\n  #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x)                             __attribute__ ((alias(x)))\n#endif\n\n/* #########################  Startup and Lowlevel Init  ######################## */\n\n#ifndef __PROGRAM_START\n#define __PROGRAM_START           __main\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              Image$$ARM_LIB_STACK$$ZI$$Limit\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             Image$$ARM_LIB_STACK$$ZI$$Base\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __Vectors\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  __attribute__((used, section(\"RESET\")))\n#endif\n\n#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)\n#ifndef __STACK_SEAL\n#define __STACK_SEAL              Image$$STACKSEAL$$ZI$$Base\n#endif\n\n#ifndef __TZ_STACK_SEAL_SIZE\n#define __TZ_STACK_SEAL_SIZE      8U\n#endif\n\n#ifndef __TZ_STACK_SEAL_VALUE\n#define __TZ_STACK_SEAL_VALUE     0xFEF5EDA5FEF5EDA5ULL\n#endif\n\n\n__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) {\n  *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE;\n}\n#endif\n\n\n/* ##########################  Core Instruction Access  ######################### */\n/** \\defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface\n  Access to dedicated instructions\n  @{\n*/\n\n/* Define macros for porting to both thumb1 and thumb2.\n * For thumb1, use low register (r0-r7), specified by constraint \"l\"\n * Otherwise, use general registers, specified by constraint \"r\" */\n#if defined (__thumb__) && !defined (__thumb2__)\n#define __CMSIS_GCC_OUT_REG(r) \"=l\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"l\" (r)\n#else\n#define __CMSIS_GCC_OUT_REG(r) \"=r\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"r\" (r)\n#endif\n\n/**\n  \\brief   No Operation\n  \\details No Operation does nothing. This instruction can be used for code alignment purposes.\n */\n#define __NOP          __builtin_arm_nop\n\n/**\n  \\brief   Wait For Interrupt\n  \\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.\n */\n#define __WFI          __builtin_arm_wfi\n\n\n/**\n  \\brief   Wait For Event\n  \\details Wait For Event is a hint instruction that permits the processor to enter\n           a low-power state until one of a number of events occurs.\n */\n#define __WFE          __builtin_arm_wfe\n\n\n/**\n  \\brief   Send Event\n  \\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.\n */\n#define __SEV          __builtin_arm_sev\n\n\n/**\n  \\brief   Instruction Synchronization Barrier\n  \\details Instruction Synchronization Barrier flushes the pipeline in the processor,\n           so that all instructions following the ISB are fetched from cache or memory,\n           after the instruction has been completed.\n */\n#define __ISB()        __builtin_arm_isb(0xF)\n\n/**\n  \\brief   Data Synchronization Barrier\n  \\details Acts as a special kind of Data Memory Barrier.\n           It completes when all explicit memory accesses before this instruction complete.\n */\n#define __DSB()        __builtin_arm_dsb(0xF)\n\n\n/**\n  \\brief   Data Memory Barrier\n  \\details Ensures the apparent order of the explicit memory operations before\n           and after the instruction, without ensuring their completion.\n */\n#define __DMB()        __builtin_arm_dmb(0xF)\n\n\n/**\n  \\brief   Reverse byte order (32 bit)\n  \\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV(value)   __builtin_bswap32(value)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV16(value) __ROR(__REV(value), 16)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REVSH(value) (int16_t)__builtin_bswap16(value)\n\n\n/**\n  \\brief   Rotate Right in unsigned value (32 bit)\n  \\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.\n  \\param [in]    op1  Value to rotate\n  \\param [in]    op2  Number of Bits to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2)\n{\n  op2 %= 32U;\n  if (op2 == 0U)\n  {\n    return op1;\n  }\n  return (op1 >> op2) | (op1 << (32U - op2));\n}\n\n\n/**\n  \\brief   Breakpoint\n  \\details Causes the processor to enter Debug state.\n           Debug tools can use this to investigate system state when the instruction at a particular address is reached.\n  \\param [in]    value  is ignored by the processor.\n                 If required, a debugger can use it to store additional information about the breakpoint.\n */\n#define __BKPT(value)     __ASM volatile (\"bkpt \"#value)\n\n\n/**\n  \\brief   Reverse bit order of value\n  \\details Reverses the bit order of the given value.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __RBIT            __builtin_arm_rbit\n\n/**\n  \\brief   Count leading zeros\n  \\details Counts the number of leading zeros of a data value.\n  \\param [in]  value  Value to count the leading zeros\n  \\return             number of leading zeros in value\n */\n__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value)\n{\n  /* Even though __builtin_clz produces a CLZ instruction on ARM, formally\n     __builtin_clz(0) is undefined behaviour, so handle this case specially.\n     This guarantees ARM-compatible results if happening to compile on a non-ARM\n     target, and ensures the compiler doesn't decide to activate any\n     optimisations using the logic \"value was passed to __builtin_clz, so it\n     is non-zero\".\n     ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a\n     single CLZ instruction.\n   */\n  if (value == 0U)\n  {\n    return 32U;\n  }\n  return __builtin_clz(value);\n}\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n/**\n  \\brief   LDR Exclusive (8 bit)\n  \\details Executes a exclusive LDR instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define __LDREXB        (uint8_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (16 bit)\n  \\details Executes a exclusive LDR instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define __LDREXH        (uint16_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (32 bit)\n  \\details Executes a exclusive LDR instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define __LDREXW        (uint32_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   STR Exclusive (8 bit)\n  \\details Executes a exclusive STR instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXB        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (16 bit)\n  \\details Executes a exclusive STR instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXH        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (32 bit)\n  \\details Executes a exclusive STR instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXW        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   Remove the exclusive lock\n  \\details Removes the exclusive lock which is created by LDREX.\n */\n#define __CLREX             __builtin_arm_clrex\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n#define __SSAT             __builtin_arm_ssat\n\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n#define __USAT             __builtin_arm_usat\n\n\n/**\n  \\brief   Rotate Right with Extend (32 bit)\n  \\details Moves each bit of a bitstring right by one bit.\n           The carry input is shifted in at the left end of the bitstring.\n  \\param [in]    value  Value to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value)\n{\n  uint32_t result;\n\n  __ASM volatile (\"rrx %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return(result);\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged LDRT instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrbt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint8_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged LDRT instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrht %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint16_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged LDRT instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return(result);\n}\n\n\n/**\n  \\brief   STRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged STRT instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"strbt %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged STRT instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"strht %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged STRT instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"strt %1, %0\" : \"=Q\" (*ptr) : \"r\" (value) );\n}\n\n#else  /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat)\n{\n  if ((sat >= 1U) && (sat <= 32U))\n  {\n    const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n    const int32_t min = -1 - max ;\n    if (val > max)\n    {\n      return max;\n    }\n    else if (val < min)\n    {\n      return min;\n    }\n  }\n  return val;\n}\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat)\n{\n  if (sat <= 31U)\n  {\n    const uint32_t max = ((1U << sat) - 1U);\n    if (val > (int32_t)max)\n    {\n      return max;\n    }\n    else if (val < 0)\n    {\n      return 0U;\n    }\n  }\n  return (uint32_t)val;\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n/**\n  \\brief   Load-Acquire (8 bit)\n  \\details Executes a LDAB instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldab %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint8_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (16 bit)\n  \\details Executes a LDAH instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldah %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint16_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (32 bit)\n  \\details Executes a LDA instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"lda %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return(result);\n}\n\n\n/**\n  \\brief   Store-Release (8 bit)\n  \\details Executes a STLB instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"stlb %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (16 bit)\n  \\details Executes a STLH instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"stlh %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (32 bit)\n  \\details Executes a STL instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"stl %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (8 bit)\n  \\details Executes a LDAB exclusive instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define     __LDAEXB                 (uint8_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (16 bit)\n  \\details Executes a LDAH exclusive instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define     __LDAEXH                 (uint16_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (32 bit)\n  \\details Executes a LDA exclusive instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define     __LDAEX                  (uint32_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Store-Release Exclusive (8 bit)\n  \\details Executes a STLB exclusive instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXB                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (16 bit)\n  \\details Executes a STLH exclusive instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXH                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (32 bit)\n  \\details Executes a STL exclusive instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEX                  (uint32_t)__builtin_arm_stlex\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n/*@}*/ /* end of group CMSIS_Core_InstructionInterface */\n\n\n/* ###########################  Core Function Access  ########################### */\n/** \\ingroup  CMSIS_Core_FunctionInterface\n    \\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions\n  @{\n */\n\n/**\n  \\brief   Enable IRQ Interrupts\n  \\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __enable_irq(void)\n{\n  __ASM volatile (\"cpsie i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Disable IRQ Interrupts\n  \\details Disables IRQ interrupts by setting special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __disable_irq(void)\n{\n  __ASM volatile (\"cpsid i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Get Control Register\n  \\details Returns the content of the Control Register.\n  \\return               Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_CONTROL(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Control Register (non-secure)\n  \\details Returns the content of the non-secure Control Register when in secure mode.\n  \\return               non-secure Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Control Register\n  \\details Writes the given value to the Control Register.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)\n{\n  __ASM volatile (\"MSR control, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Control Register (non-secure)\n  \\details Writes the given value to the non-secure Control Register when in secure state.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)\n{\n  __ASM volatile (\"MSR control_ns, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n#endif\n\n\n/**\n  \\brief   Get IPSR Register\n  \\details Returns the content of the IPSR Register.\n  \\return               IPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_IPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, ipsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get APSR Register\n  \\details Returns the content of the APSR Register.\n  \\return               APSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_APSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, apsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get xPSR Register\n  \\details Returns the content of the xPSR Register.\n  \\return               xPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_xPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, xpsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get Process Stack Pointer\n  \\details Returns the current value of the Process Stack Pointer (PSP).\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp\"  : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp_ns\"  : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer\n  \\details Assigns the given value to the Process Stack Pointer (PSP).\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp, %0\" : : \"r\" (topOfProcStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp_ns, %0\" : : \"r\" (topOfProcStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer\n  \\details Returns the current value of the Main Stack Pointer (MSP).\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Main Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer\n  \\details Assigns the given value to the Main Stack Pointer (MSP).\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp, %0\" : : \"r\" (topOfMainStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Main Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp_ns, %0\" : : \"r\" (topOfMainStack) : );\n}\n#endif\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Stack Pointer (SP) when in secure state.\n  \\return               SP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, sp_ns\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Set Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state.\n  \\param [in]    topOfStack  Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)\n{\n  __ASM volatile (\"MSR sp_ns, %0\" : : \"r\" (topOfStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Priority Mask\n  \\details Returns the current state of the priority mask bit from the Priority Mask Register.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Priority Mask (non-secure)\n  \\details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Priority Mask\n  \\details Assigns the given value to the Priority Mask Register.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask, %0\" : : \"r\" (priMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Priority Mask (non-secure)\n  \\details Assigns the given value to the non-secure Priority Mask Register when in secure state.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask_ns, %0\" : : \"r\" (priMask) : \"memory\");\n}\n#endif\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )\n/**\n  \\brief   Enable FIQ\n  \\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __enable_fault_irq(void)\n{\n  __ASM volatile (\"cpsie f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Disable FIQ\n  \\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __disable_fault_irq(void)\n{\n  __ASM volatile (\"cpsid f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Get Base Priority\n  \\details Returns the current value of the Base Priority register.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Base Priority (non-secure)\n  \\details Returns the current value of the non-secure Base Priority register when in secure state.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority\n  \\details Assigns the given value to the Base Priority register.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Base Priority (non-secure)\n  \\details Assigns the given value to the non-secure Base Priority register when in secure state.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_ns, %0\" : : \"r\" (basePri) : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority with condition\n  \\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,\n           or the new value increases the BASEPRI priority level.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_max, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n/**\n  \\brief   Get Fault Mask\n  \\details Returns the current value of the Fault Mask register.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Fault Mask (non-secure)\n  \\details Returns the current value of the non-secure Fault Mask register when in secure state.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Fault Mask\n  \\details Assigns the given value to the Fault Mask register.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Fault Mask (non-secure)\n  \\details Assigns the given value to the non-secure Fault Mask register when in secure state.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask_ns, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n\n/**\n  \\brief   Get Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the Process Stack Pointer Limit (PSPLIM).\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n\n#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim_ns\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the Process Stack Pointer Limit (PSPLIM).\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim, %0\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim_ns, %0\\n\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the Main Stack Pointer Limit (MSPLIM).\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim\" : \"=r\" (result) );\n  return result;\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Get Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state.\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim_ns\" : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the Main Stack Pointer Limit (MSPLIM).\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state.\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim_ns, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n/**\n  \\brief   Get FPSCR\n  \\details Returns the current value of the Floating Point Status/Control register.\n  \\return               Floating Point Status/Control register value\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __get_FPSCR      (uint32_t)__builtin_arm_get_fpscr\n#else\n#define __get_FPSCR()      ((uint32_t)0U)\n#endif\n\n/**\n  \\brief   Set FPSCR\n  \\details Assigns the given value to the Floating Point Status/Control register.\n  \\param [in]    fpscr  Floating Point Status/Control value to set\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __set_FPSCR      __builtin_arm_set_fpscr\n#else\n#define __set_FPSCR(x)      ((void)(x))\n#endif\n\n\n/*@} end of CMSIS_Core_RegAccFunctions */\n\n\n/* ###################  Compiler specific Intrinsics  ########################### */\n/** \\defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics\n  Access to dedicated SIMD instructions\n  @{\n*/\n\n#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1))\n\n__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n\n__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n\n__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"qsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"shsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uqsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uhsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usad8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usada8 %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n#define __SSAT16(ARG1,ARG2) \\\n({                          \\\n  int32_t __RES, __ARG1 = (ARG1); \\\n  __ASM (\"ssat16 %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) ); \\\n  __RES; \\\n })\n\n#define __USAT16(ARG1,ARG2) \\\n({                          \\\n  uint32_t __RES, __ARG1 = (ARG1); \\\n  __ASM (\"usat16 %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) ); \\\n  __RES; \\\n })\n\n__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uxtb16 %0, %1\" : \"=r\" (result) : \"r\" (op1));\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uxtab16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sxtb16 %0, %1\" : \"=r\" (result) : \"r\" (op1));\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sxtab16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUAD  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smuad %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smuadx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlad %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smladx %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlald %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlald %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlaldx %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlaldx %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUSD  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smusd %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smusdx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlsd %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlsdx %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlsld %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlsld %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlsldx %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlsldx %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint32_t __SEL  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sel %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE  int32_t __QADD( int32_t op1,  int32_t op2)\n{\n  int32_t result;\n\n  __ASM volatile (\"qadd %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE  int32_t __QSUB( int32_t op1,  int32_t op2)\n{\n  int32_t result;\n\n  __ASM volatile (\"qsub %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n#define __PKHBT(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0x0000FFFFUL) |  \\\n                                           ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL)  )\n\n#define __PKHTB(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0xFFFF0000UL) |  \\\n                                           ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL)  )\n\n#define __SXTB16_RORn(ARG1, ARG2)        __SXTB16(__ROR(ARG1, ARG2))\n\n#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))\n\n__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3)\n{\n  int32_t result;\n\n  __ASM volatile (\"smmla %0, %1, %2, %3\" : \"=r\" (result): \"r\"  (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n#endif /* (__ARM_FEATURE_DSP == 1) */\n/*@} end of group CMSIS_SIMD_intrinsics */\n\n\n#endif /* __CMSIS_ARMCLANG_H */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_compiler.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_compiler.h\n * @brief    CMSIS compiler generic header file\n * @version  V5.3.0\n * @date     04. April 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CMSIS_COMPILER_H\n#define __CMSIS_COMPILER_H\n\n#include <stdint.h>\n\n/*\n * Arm Compiler 4/5\n */\n#if   defined ( __CC_ARM )\n  #include \"cmsis_armcc.h\"\n\n\n/*\n * Arm Compiler 6.6 LTM (armclang)\n */\n#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100)\n  #include \"cmsis_armclang_ltm.h\"\n\n  /*\n * Arm Compiler above 6.10.1 (armclang)\n */\n#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)\n  #include \"cmsis_armclang.h\"\n\n/*\n * TI Arm Clang Compiler (tiarmclang)\n */\n#elif defined (__ti__)\n  #include \"cmsis_tiarmclang.h\"\n\n/*\n * GNU Compiler\n */\n#elif defined ( __GNUC__ )\n  #include \"cmsis_gcc.h\"\n\n\n/*\n * IAR Compiler\n */\n#elif defined ( __ICCARM__ )\n  #include <cmsis_iccarm.h>\n\n\n/*\n * TI Arm Compiler (armcl)\n */\n#elif defined ( __TI_ARM__ )\n  #include <cmsis_ccs.h>\n\n  #ifndef   __ASM\n    #define __ASM                                  __asm\n  #endif\n  #ifndef   __INLINE\n    #define __INLINE                               inline\n  #endif\n  #ifndef   __STATIC_INLINE\n    #define __STATIC_INLINE                        static inline\n  #endif\n  #ifndef   __STATIC_FORCEINLINE\n    #define __STATIC_FORCEINLINE                   __STATIC_INLINE\n  #endif\n  #ifndef   __NO_RETURN\n    #define __NO_RETURN                            __attribute__((noreturn))\n  #endif\n  #ifndef   __USED\n    #define __USED                                 __attribute__((used))\n  #endif\n  #ifndef   __WEAK\n    #define __WEAK                                 __attribute__((weak))\n  #endif\n  #ifndef   __PACKED\n    #define __PACKED                               __attribute__((packed))\n  #endif\n  #ifndef   __PACKED_STRUCT\n    #define __PACKED_STRUCT                        struct __attribute__((packed))\n  #endif\n  #ifndef   __PACKED_UNION\n    #define __PACKED_UNION                         union __attribute__((packed))\n  #endif\n  #ifndef   __UNALIGNED_UINT32        /* deprecated */\n    struct __attribute__((packed)) T_UINT32 { uint32_t v; };\n    #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT16_WRITE\n    __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n    #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT16_READ\n    __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n    #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT32_WRITE\n    __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n    #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT32_READ\n    __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n    #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __ALIGNED\n    #define __ALIGNED(x)                           __attribute__((aligned(x)))\n  #endif\n  #ifndef   __RESTRICT\n    #define __RESTRICT                             __restrict\n  #endif\n  #ifndef   __COMPILER_BARRIER\n    #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.\n    #define __COMPILER_BARRIER()                   (void)0\n  #endif\n  #ifndef __NO_INIT\n    #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n  #endif\n  #ifndef __ALIAS\n    #define __ALIAS(x)                             __attribute__ ((alias(x)))\n  #endif\n\n/*\n * TASKING Compiler\n */\n#elif defined ( __TASKING__ )\n  /*\n   * The CMSIS functions have been implemented as intrinsics in the compiler.\n   * Please use \"carm -?i\" to get an up to date list of all intrinsics,\n   * Including the CMSIS ones.\n   */\n\n  #ifndef   __ASM\n    #define __ASM                                  __asm\n  #endif\n  #ifndef   __INLINE\n    #define __INLINE                               inline\n  #endif\n  #ifndef   __STATIC_INLINE\n    #define __STATIC_INLINE                        static inline\n  #endif\n  #ifndef   __STATIC_FORCEINLINE\n    #define __STATIC_FORCEINLINE                   __STATIC_INLINE\n  #endif\n  #ifndef   __NO_RETURN\n    #define __NO_RETURN                            __attribute__((noreturn))\n  #endif\n  #ifndef   __USED\n    #define __USED                                 __attribute__((used))\n  #endif\n  #ifndef   __WEAK\n    #define __WEAK                                 __attribute__((weak))\n  #endif\n  #ifndef   __PACKED\n    #define __PACKED                               __packed__\n  #endif\n  #ifndef   __PACKED_STRUCT\n    #define __PACKED_STRUCT                        struct __packed__\n  #endif\n  #ifndef   __PACKED_UNION\n    #define __PACKED_UNION                         union __packed__\n  #endif\n  #ifndef   __UNALIGNED_UINT32        /* deprecated */\n    struct __packed__ T_UINT32 { uint32_t v; };\n    #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT16_WRITE\n    __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n    #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT16_READ\n    __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n    #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT32_WRITE\n    __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n    #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT32_READ\n    __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n    #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __ALIGNED\n    #define __ALIGNED(x)              __align(x)\n  #endif\n  #ifndef   __RESTRICT\n    #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.\n    #define __RESTRICT\n  #endif\n  #ifndef   __COMPILER_BARRIER\n    #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.\n    #define __COMPILER_BARRIER()                   (void)0\n  #endif\n  #ifndef __NO_INIT\n    #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n  #endif\n  #ifndef __ALIAS\n    #define __ALIAS(x)                             __attribute__ ((alias(x)))\n  #endif\n\n/*\n * COSMIC Compiler\n */\n#elif defined ( __CSMC__ )\n   #include <cmsis_csm.h>\n\n #ifndef   __ASM\n    #define __ASM                                  _asm\n  #endif\n  #ifndef   __INLINE\n    #define __INLINE                               inline\n  #endif\n  #ifndef   __STATIC_INLINE\n    #define __STATIC_INLINE                        static inline\n  #endif\n  #ifndef   __STATIC_FORCEINLINE\n    #define __STATIC_FORCEINLINE                   __STATIC_INLINE\n  #endif\n  #ifndef   __NO_RETURN\n    // NO RETURN is automatically detected hence no warning here\n    #define __NO_RETURN\n  #endif\n  #ifndef   __USED\n    #warning No compiler specific solution for __USED. __USED is ignored.\n    #define __USED\n  #endif\n  #ifndef   __WEAK\n    #define __WEAK                                 __weak\n  #endif\n  #ifndef   __PACKED\n    #define __PACKED                               @packed\n  #endif\n  #ifndef   __PACKED_STRUCT\n    #define __PACKED_STRUCT                        @packed struct\n  #endif\n  #ifndef   __PACKED_UNION\n    #define __PACKED_UNION                         @packed union\n  #endif\n  #ifndef   __UNALIGNED_UINT32        /* deprecated */\n    @packed struct T_UINT32 { uint32_t v; };\n    #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT16_WRITE\n    __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n    #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT16_READ\n    __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n    #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __UNALIGNED_UINT32_WRITE\n    __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n    #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n  #endif\n  #ifndef   __UNALIGNED_UINT32_READ\n    __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n    #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n  #endif\n  #ifndef   __ALIGNED\n    #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored.\n    #define __ALIGNED(x)\n  #endif\n  #ifndef   __RESTRICT\n    #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.\n    #define __RESTRICT\n  #endif\n  #ifndef   __COMPILER_BARRIER\n    #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.\n    #define __COMPILER_BARRIER()                   (void)0\n  #endif\n  #ifndef __NO_INIT\n    #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n  #endif\n  #ifndef __ALIAS\n    #define __ALIAS(x)                             __attribute__ ((alias(x)))\n  #endif\n\n#else\n  #error Unknown compiler.\n#endif\n\n\n#endif /* __CMSIS_COMPILER_H */\n\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_gcc.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_gcc.h\n * @brief    CMSIS compiler GCC header file\n * @version  V5.4.2\n * @date     17. December 2022\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2021 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CMSIS_GCC_H\n#define __CMSIS_GCC_H\n\n/* ignore some GCC warnings */\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n#pragma GCC diagnostic ignored \"-Wconversion\"\n#pragma GCC diagnostic ignored \"-Wunused-parameter\"\n\n/* Fallback for __has_builtin */\n#ifndef __has_builtin\n  #define __has_builtin(x) (0)\n#endif\n\n/* CMSIS compiler specific defines */\n#ifndef   __ASM\n  #define __ASM                                  __asm\n#endif\n#ifndef   __INLINE\n  #define __INLINE                               inline\n#endif\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE                        static inline\n#endif\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE                   __attribute__((always_inline)) static inline\n#endif\n#ifndef   __NO_RETURN\n  #define __NO_RETURN                            __attribute__((__noreturn__))\n#endif\n#ifndef   __USED\n  #define __USED                                 __attribute__((used))\n#endif\n#ifndef   __WEAK\n  #define __WEAK                                 __attribute__((weak))\n#endif\n#ifndef   __PACKED\n  #define __PACKED                               __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_STRUCT\n  #define __PACKED_STRUCT                        struct __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_UNION\n  #define __PACKED_UNION                         union __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __UNALIGNED_UINT32        /* deprecated */\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wpacked\"\n  #pragma GCC diagnostic ignored \"-Wattributes\"\n  struct __attribute__((packed)) T_UINT32 { uint32_t v; };\n  #pragma GCC diagnostic pop\n  #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n#endif\n#ifndef   __UNALIGNED_UINT16_WRITE\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wpacked\"\n  #pragma GCC diagnostic ignored \"-Wattributes\"\n  __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n  #pragma GCC diagnostic pop\n  #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT16_READ\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wpacked\"\n  #pragma GCC diagnostic ignored \"-Wattributes\"\n  __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n  #pragma GCC diagnostic pop\n  #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __UNALIGNED_UINT32_WRITE\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wpacked\"\n  #pragma GCC diagnostic ignored \"-Wattributes\"\n  __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n  #pragma GCC diagnostic pop\n  #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT32_READ\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wpacked\"\n  #pragma GCC diagnostic ignored \"-Wattributes\"\n  __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n  #pragma GCC diagnostic pop\n  #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __ALIGNED\n  #define __ALIGNED(x)                           __attribute__((aligned(x)))\n#endif\n#ifndef   __RESTRICT\n  #define __RESTRICT                             __restrict\n#endif\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER()                   __ASM volatile(\"\":::\"memory\")\n#endif\n#ifndef __NO_INIT\n  #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x)                             __attribute__ ((alias(x)))\n#endif\n\n/* #########################  Startup and Lowlevel Init  ######################## */\n\n#ifndef __PROGRAM_START\n\n/**\n  \\brief   Initializes data and bss sections\n  \\details This default implementations initialized all data and additional bss\n           sections relying on .copy.table and .zero.table specified properly\n           in the used linker script.\n\n */\n__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void)\n{\n  extern void _start(void) __NO_RETURN;\n\n  typedef struct __copy_table {\n    uint32_t const* src;\n    uint32_t* dest;\n    uint32_t  wlen;\n  } __copy_table_t;\n\n  typedef struct __zero_table {\n    uint32_t* dest;\n    uint32_t  wlen;\n  } __zero_table_t;\n\n  extern const __copy_table_t __copy_table_start__;\n  extern const __copy_table_t __copy_table_end__;\n  extern const __zero_table_t __zero_table_start__;\n  extern const __zero_table_t __zero_table_end__;\n\n  for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) {\n    for(uint32_t i=0u; i<pTable->wlen; ++i) {\n      pTable->dest[i] = pTable->src[i];\n    }\n  }\n\n  for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) {\n    for(uint32_t i=0u; i<pTable->wlen; ++i) {\n      pTable->dest[i] = 0u;\n    }\n  }\n\n  _start();\n}\n\n#define __PROGRAM_START           __cmsis_start\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              __StackTop\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             __StackLimit\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __Vectors\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  __attribute__((used, section(\".vectors\")))\n#endif\n\n#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)\n#ifndef __STACK_SEAL\n#define __STACK_SEAL              __StackSeal\n#endif\n\n#ifndef __TZ_STACK_SEAL_SIZE\n#define __TZ_STACK_SEAL_SIZE      8U\n#endif\n\n#ifndef __TZ_STACK_SEAL_VALUE\n#define __TZ_STACK_SEAL_VALUE     0xFEF5EDA5FEF5EDA5ULL\n#endif\n\n\n__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) {\n  *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE;\n}\n#endif\n\n\n/* ##########################  Core Instruction Access  ######################### */\n/** \\defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface\n  Access to dedicated instructions\n  @{\n*/\n\n/* Define macros for porting to both thumb1 and thumb2.\n * For thumb1, use low register (r0-r7), specified by constraint \"l\"\n * Otherwise, use general registers, specified by constraint \"r\" */\n#if defined (__thumb__) && !defined (__thumb2__)\n#define __CMSIS_GCC_OUT_REG(r) \"=l\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+l\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"l\" (r)\n#else\n#define __CMSIS_GCC_OUT_REG(r) \"=r\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+r\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"r\" (r)\n#endif\n\n/**\n  \\brief   No Operation\n  \\details No Operation does nothing. This instruction can be used for code alignment purposes.\n */\n#define __NOP()                             __ASM volatile (\"nop\")\n\n/**\n  \\brief   Wait For Interrupt\n  \\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.\n */\n#define __WFI()                             __ASM volatile (\"wfi\":::\"memory\")\n\n\n/**\n  \\brief   Wait For Event\n  \\details Wait For Event is a hint instruction that permits the processor to enter\n           a low-power state until one of a number of events occurs.\n */\n#define __WFE()                             __ASM volatile (\"wfe\":::\"memory\")\n\n\n/**\n  \\brief   Send Event\n  \\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.\n */\n#define __SEV()                             __ASM volatile (\"sev\")\n\n\n/**\n  \\brief   Instruction Synchronization Barrier\n  \\details Instruction Synchronization Barrier flushes the pipeline in the processor,\n           so that all instructions following the ISB are fetched from cache or memory,\n           after the instruction has been completed.\n */\n__STATIC_FORCEINLINE void __ISB(void)\n{\n  __ASM volatile (\"isb 0xF\":::\"memory\");\n}\n\n\n/**\n  \\brief   Data Synchronization Barrier\n  \\details Acts as a special kind of Data Memory Barrier.\n           It completes when all explicit memory accesses before this instruction complete.\n */\n__STATIC_FORCEINLINE void __DSB(void)\n{\n  __ASM volatile (\"dsb 0xF\":::\"memory\");\n}\n\n\n/**\n  \\brief   Data Memory Barrier\n  \\details Ensures the apparent order of the explicit memory operations before\n           and after the instruction, without ensuring their completion.\n */\n__STATIC_FORCEINLINE void __DMB(void)\n{\n  __ASM volatile (\"dmb 0xF\":::\"memory\");\n}\n\n\n/**\n  \\brief   Reverse byte order (32 bit)\n  \\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n__STATIC_FORCEINLINE uint32_t __REV(uint32_t value)\n{\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)\n  return __builtin_bswap32(value);\n#else\n  uint32_t result;\n\n  __ASM (\"rev %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return result;\n#endif\n}\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value)\n{\n  uint32_t result;\n\n  __ASM (\"rev16 %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return result;\n}\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n__STATIC_FORCEINLINE int16_t __REVSH(int16_t value)\n{\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n  return (int16_t)__builtin_bswap16(value);\n#else\n  int16_t result;\n\n  __ASM (\"revsh %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return result;\n#endif\n}\n\n\n/**\n  \\brief   Rotate Right in unsigned value (32 bit)\n  \\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.\n  \\param [in]    op1  Value to rotate\n  \\param [in]    op2  Number of Bits to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2)\n{\n  op2 %= 32U;\n  if (op2 == 0U)\n  {\n    return op1;\n  }\n  return (op1 >> op2) | (op1 << (32U - op2));\n}\n\n\n/**\n  \\brief   Breakpoint\n  \\details Causes the processor to enter Debug state.\n           Debug tools can use this to investigate system state when the instruction at a particular address is reached.\n  \\param [in]    value  is ignored by the processor.\n                 If required, a debugger can use it to store additional information about the breakpoint.\n */\n#define __BKPT(value)                       __ASM volatile (\"bkpt \"#value)\n\n\n/**\n  \\brief   Reverse bit order of value\n  \\details Reverses the bit order of the given value.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value)\n{\n  uint32_t result;\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )\n   __ASM (\"rbit %0, %1\" : \"=r\" (result) : \"r\" (value) );\n#else\n  uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */\n\n  result = value;                      /* r will be reversed bits of v; first get LSB of v */\n  for (value >>= 1U; value != 0U; value >>= 1U)\n  {\n    result <<= 1U;\n    result |= value & 1U;\n    s--;\n  }\n  result <<= s;                        /* shift when v's highest bits are zero */\n#endif\n  return result;\n}\n\n\n/**\n  \\brief   Count leading zeros\n  \\details Counts the number of leading zeros of a data value.\n  \\param [in]  value  Value to count the leading zeros\n  \\return             number of leading zeros in value\n */\n__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value)\n{\n  /* Even though __builtin_clz produces a CLZ instruction on ARM, formally\n     __builtin_clz(0) is undefined behaviour, so handle this case specially.\n     This guarantees ARM-compatible results if happening to compile on a non-ARM\n     target, and ensures the compiler doesn't decide to activate any\n     optimisations using the logic \"value was passed to __builtin_clz, so it\n     is non-zero\".\n     ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a\n     single CLZ instruction.\n   */\n  if (value == 0U)\n  {\n    return 32U;\n  }\n  return __builtin_clz(value);\n}\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n/**\n  \\brief   LDR Exclusive (8 bit)\n  \\details Executes a exclusive LDR instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr)\n{\n    uint32_t result;\n\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n   __ASM volatile (\"ldrexb %0, %1\" : \"=r\" (result) : \"Q\" (*addr) );\n#else\n    /* Prior to GCC 4.8, \"Q\" will be expanded to [rx, #0] which is not\n       accepted by assembler. So has to use following less efficient pattern.\n    */\n   __ASM volatile (\"ldrexb %0, [%1]\" : \"=r\" (result) : \"r\" (addr) : \"memory\" );\n#endif\n   return ((uint8_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDR Exclusive (16 bit)\n  \\details Executes a exclusive LDR instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr)\n{\n    uint32_t result;\n\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n   __ASM volatile (\"ldrexh %0, %1\" : \"=r\" (result) : \"Q\" (*addr) );\n#else\n    /* Prior to GCC 4.8, \"Q\" will be expanded to [rx, #0] which is not\n       accepted by assembler. So has to use following less efficient pattern.\n    */\n   __ASM volatile (\"ldrexh %0, [%1]\" : \"=r\" (result) : \"r\" (addr) : \"memory\" );\n#endif\n   return ((uint16_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDR Exclusive (32 bit)\n  \\details Executes a exclusive LDR instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldrex %0, %1\" : \"=r\" (result) : \"Q\" (*addr) );\n   return(result);\n}\n\n\n/**\n  \\brief   STR Exclusive (8 bit)\n  \\details Executes a exclusive STR instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"strexb %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*addr) : \"r\" ((uint32_t)value) );\n   return(result);\n}\n\n\n/**\n  \\brief   STR Exclusive (16 bit)\n  \\details Executes a exclusive STR instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"strexh %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*addr) : \"r\" ((uint32_t)value) );\n   return(result);\n}\n\n\n/**\n  \\brief   STR Exclusive (32 bit)\n  \\details Executes a exclusive STR instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"strex %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*addr) : \"r\" (value) );\n   return(result);\n}\n\n\n/**\n  \\brief   Remove the exclusive lock\n  \\details Removes the exclusive lock which is created by LDREX.\n */\n__STATIC_FORCEINLINE void __CLREX(void)\n{\n  __ASM volatile (\"clrex\" ::: \"memory\");\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  ARG1  Value to be saturated\n  \\param [in]  ARG2  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n#define __SSAT(ARG1, ARG2) \\\n__extension__ \\\n({                          \\\n  int32_t __RES, __ARG1 = (ARG1); \\\n  __ASM volatile (\"ssat %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) : \"cc\" ); \\\n  __RES; \\\n })\n\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  ARG1  Value to be saturated\n  \\param [in]  ARG2  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n#define __USAT(ARG1, ARG2) \\\n__extension__ \\\n({                          \\\n  uint32_t __RES, __ARG1 = (ARG1); \\\n  __ASM volatile (\"usat %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) : \"cc\" ); \\\n  __RES; \\\n })\n\n\n/**\n  \\brief   Rotate Right with Extend (32 bit)\n  \\details Moves each bit of a bitstring right by one bit.\n           The carry input is shifted in at the left end of the bitstring.\n  \\param [in]    value  Value to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value)\n{\n  uint32_t result;\n\n  __ASM volatile (\"rrx %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return(result);\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged LDRT instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr)\n{\n    uint32_t result;\n\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n   __ASM volatile (\"ldrbt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n#else\n    /* Prior to GCC 4.8, \"Q\" will be expanded to [rx, #0] which is not\n       accepted by assembler. So has to use following less efficient pattern.\n    */\n   __ASM volatile (\"ldrbt %0, [%1]\" : \"=r\" (result) : \"r\" (ptr) : \"memory\" );\n#endif\n   return ((uint8_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged LDRT instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr)\n{\n    uint32_t result;\n\n#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n   __ASM volatile (\"ldrht %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n#else\n    /* Prior to GCC 4.8, \"Q\" will be expanded to [rx, #0] which is not\n       accepted by assembler. So has to use following less efficient pattern.\n    */\n   __ASM volatile (\"ldrht %0, [%1]\" : \"=r\" (result) : \"r\" (ptr) : \"memory\" );\n#endif\n   return ((uint16_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged LDRT instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldrt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n   return(result);\n}\n\n\n/**\n  \\brief   STRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged STRT instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr)\n{\n   __ASM volatile (\"strbt %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged STRT instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr)\n{\n   __ASM volatile (\"strht %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged STRT instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr)\n{\n   __ASM volatile (\"strt %1, %0\" : \"=Q\" (*ptr) : \"r\" (value) );\n}\n\n#else  /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat)\n{\n  if ((sat >= 1U) && (sat <= 32U))\n  {\n    const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n    const int32_t min = -1 - max ;\n    if (val > max)\n    {\n      return max;\n    }\n    else if (val < min)\n    {\n      return min;\n    }\n  }\n  return val;\n}\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat)\n{\n  if (sat <= 31U)\n  {\n    const uint32_t max = ((1U << sat) - 1U);\n    if (val > (int32_t)max)\n    {\n      return max;\n    }\n    else if (val < 0)\n    {\n      return 0U;\n    }\n  }\n  return (uint32_t)val;\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n/**\n  \\brief   Load-Acquire (8 bit)\n  \\details Executes a LDAB instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldab %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return ((uint8_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (16 bit)\n  \\details Executes a LDAH instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldah %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return ((uint16_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (32 bit)\n  \\details Executes a LDA instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"lda %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return(result);\n}\n\n\n/**\n  \\brief   Store-Release (8 bit)\n  \\details Executes a STLB instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr)\n{\n   __ASM volatile (\"stlb %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (16 bit)\n  \\details Executes a STLH instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr)\n{\n   __ASM volatile (\"stlh %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (32 bit)\n  \\details Executes a STL instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr)\n{\n   __ASM volatile (\"stl %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (8 bit)\n  \\details Executes a LDAB exclusive instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldaexb %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return ((uint8_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (16 bit)\n  \\details Executes a LDAH exclusive instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldaexh %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return ((uint16_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (32 bit)\n  \\details Executes a LDA exclusive instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr)\n{\n    uint32_t result;\n\n   __ASM volatile (\"ldaex %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n   return(result);\n}\n\n\n/**\n  \\brief   Store-Release Exclusive (8 bit)\n  \\details Executes a STLB exclusive instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"stlexb %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n   return(result);\n}\n\n\n/**\n  \\brief   Store-Release Exclusive (16 bit)\n  \\details Executes a STLH exclusive instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"stlexh %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n   return(result);\n}\n\n\n/**\n  \\brief   Store-Release Exclusive (32 bit)\n  \\details Executes a STL exclusive instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr)\n{\n   uint32_t result;\n\n   __ASM volatile (\"stlex %0, %2, %1\" : \"=&r\" (result), \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n   return(result);\n}\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n/*@}*/ /* end of group CMSIS_Core_InstructionInterface */\n\n\n/* ###########################  Core Function Access  ########################### */\n/** \\ingroup  CMSIS_Core_FunctionInterface\n    \\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions\n  @{\n */\n\n/**\n  \\brief   Enable IRQ Interrupts\n  \\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __enable_irq(void)\n{\n  __ASM volatile (\"cpsie i\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Disable IRQ Interrupts\n  \\details Disables IRQ interrupts by setting special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __disable_irq(void)\n{\n  __ASM volatile (\"cpsid i\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Get Control Register\n  \\details Returns the content of the Control Register.\n  \\return               Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_CONTROL(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Control Register (non-secure)\n  \\details Returns the content of the non-secure Control Register when in secure mode.\n  \\return               non-secure Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Control Register\n  \\details Writes the given value to the Control Register.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)\n{\n  __ASM volatile (\"MSR control, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Control Register (non-secure)\n  \\details Writes the given value to the non-secure Control Register when in secure state.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)\n{\n  __ASM volatile (\"MSR control_ns, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n#endif\n\n\n/**\n  \\brief   Get IPSR Register\n  \\details Returns the content of the IPSR Register.\n  \\return               IPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_IPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, ipsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get APSR Register\n  \\details Returns the content of the APSR Register.\n  \\return               APSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_APSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, apsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get xPSR Register\n  \\details Returns the content of the xPSR Register.\n  \\return               xPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_xPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, xpsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get Process Stack Pointer\n  \\details Returns the current value of the Process Stack Pointer (PSP).\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp\"  : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp_ns\"  : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer\n  \\details Assigns the given value to the Process Stack Pointer (PSP).\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp, %0\" : : \"r\" (topOfProcStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp_ns, %0\" : : \"r\" (topOfProcStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer\n  \\details Returns the current value of the Main Stack Pointer (MSP).\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Main Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer\n  \\details Assigns the given value to the Main Stack Pointer (MSP).\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp, %0\" : : \"r\" (topOfMainStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Main Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp_ns, %0\" : : \"r\" (topOfMainStack) : );\n}\n#endif\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Stack Pointer (SP) when in secure state.\n  \\return               SP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, sp_ns\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Set Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state.\n  \\param [in]    topOfStack  Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)\n{\n  __ASM volatile (\"MSR sp_ns, %0\" : : \"r\" (topOfStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Priority Mask\n  \\details Returns the current state of the priority mask bit from the Priority Mask Register.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Priority Mask (non-secure)\n  \\details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Priority Mask\n  \\details Assigns the given value to the Priority Mask Register.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask, %0\" : : \"r\" (priMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Priority Mask (non-secure)\n  \\details Assigns the given value to the non-secure Priority Mask Register when in secure state.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask_ns, %0\" : : \"r\" (priMask) : \"memory\");\n}\n#endif\n\n\n#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n     (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )\n/**\n  \\brief   Enable FIQ\n  \\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __enable_fault_irq(void)\n{\n  __ASM volatile (\"cpsie f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Disable FIQ\n  \\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __disable_fault_irq(void)\n{\n  __ASM volatile (\"cpsid f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Get Base Priority\n  \\details Returns the current value of the Base Priority register.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Base Priority (non-secure)\n  \\details Returns the current value of the non-secure Base Priority register when in secure state.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority\n  \\details Assigns the given value to the Base Priority register.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Base Priority (non-secure)\n  \\details Assigns the given value to the non-secure Base Priority register when in secure state.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_ns, %0\" : : \"r\" (basePri) : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority with condition\n  \\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,\n           or the new value increases the BASEPRI priority level.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_max, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n/**\n  \\brief   Get Fault Mask\n  \\details Returns the current value of the Fault Mask register.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Fault Mask (non-secure)\n  \\details Returns the current value of the non-secure Fault Mask register when in secure state.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Fault Mask\n  \\details Assigns the given value to the Fault Mask register.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Fault Mask (non-secure)\n  \\details Assigns the given value to the non-secure Fault Mask register when in secure state.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask_ns, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \\\n           (defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n\n/**\n  \\brief   Get Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the Process Stack Pointer Limit (PSPLIM).\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n\n#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim_ns\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the Process Stack Pointer Limit (PSPLIM).\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim, %0\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim_ns, %0\\n\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the Main Stack Pointer Limit (MSPLIM).\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim\" : \"=r\" (result) );\n  return result;\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Get Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state.\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim_ns\" : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the Main Stack Pointer Limit (MSPLIM).\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state.\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit)\n{\n#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim_ns, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    ) */\n\n\n/**\n  \\brief   Get FPSCR\n  \\details Returns the current value of the Floating Point Status/Control register.\n  \\return               Floating Point Status/Control register value\n */\n__STATIC_FORCEINLINE uint32_t __get_FPSCR(void)\n{\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#if __has_builtin(__builtin_arm_get_fpscr)\n// Re-enable using built-in when GCC has been fixed\n// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)\n  /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */\n  return __builtin_arm_get_fpscr();\n#else\n  uint32_t result;\n\n  __ASM volatile (\"VMRS %0, fpscr\" : \"=r\" (result) );\n  return(result);\n#endif\n#else\n  return(0U);\n#endif\n}\n\n\n/**\n  \\brief   Set FPSCR\n  \\details Assigns the given value to the Floating Point Status/Control register.\n  \\param [in]    fpscr  Floating Point Status/Control value to set\n */\n__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr)\n{\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#if __has_builtin(__builtin_arm_set_fpscr)\n// Re-enable using built-in when GCC has been fixed\n// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)\n  /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */\n  __builtin_arm_set_fpscr(fpscr);\n#else\n  __ASM volatile (\"VMSR fpscr, %0\" : : \"r\" (fpscr) : \"vfpcc\", \"memory\");\n#endif\n#else\n  (void)fpscr;\n#endif\n}\n\n\n/*@} end of CMSIS_Core_RegAccFunctions */\n\n\n/* ###################  Compiler specific Intrinsics  ########################### */\n/** \\defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics\n  Access to dedicated SIMD instructions\n  @{\n*/\n\n#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1))\n\n__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhadd8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n\n__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhsub8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n\n__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhadd16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhsub16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"uasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhasx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ssax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"qsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"shsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"usax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uqsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uhsax %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"usad8 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM (\"usada8 %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n#define __SSAT16(ARG1, ARG2) \\\n__extension__ \\\n({                          \\\n  int32_t __RES, __ARG1 = (ARG1); \\\n  __ASM volatile (\"ssat16 %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) : \"cc\" ); \\\n  __RES; \\\n })\n\n#define __USAT16(ARG1, ARG2) \\\n__extension__ \\\n({                          \\\n  uint32_t __RES, __ARG1 = (ARG1); \\\n  __ASM volatile (\"usat16 %0, %1, %2\" : \"=r\" (__RES) :  \"I\" (ARG2), \"r\" (__ARG1) : \"cc\" ); \\\n  __RES; \\\n })\n\n__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1)\n{\n  uint32_t result;\n\n  __ASM (\"uxtb16 %0, %1\" : \"=r\" (result) : \"r\" (op1));\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"uxtab16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1)\n{\n  uint32_t result;\n\n  __ASM (\"sxtb16 %0, %1\" : \"=r\" (result) : \"r\" (op1));\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTB16_RORn(uint32_t op1, uint32_t rotate)\n{\n  uint32_t result;\n  if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {\n    __ASM volatile (\"sxtb16 %0, %1, ROR %2\" : \"=r\" (result) : \"r\" (op1), \"i\" (rotate) );\n  } else {\n    result = __SXTB16(__ROR(op1, rotate)) ;\n  }\n  return result;\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM (\"sxtab16 %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SXTAB16_RORn(uint32_t op1, uint32_t op2, uint32_t rotate)\n{\n  uint32_t result;\n  if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {\n    __ASM volatile (\"sxtab16 %0, %1, %2, ROR %3\" : \"=r\" (result) : \"r\" (op1) , \"r\" (op2) , \"i\" (rotate));\n  } else {\n    result = __SXTAB16(op1, __ROR(op2, rotate));\n  }\n  return result;\n}\n\n\n__STATIC_FORCEINLINE uint32_t __SMUAD  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smuad %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smuadx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlad %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smladx %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlald %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlald %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlaldx %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlaldx %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUSD  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smusd %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smusdx %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlsd %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3)\n{\n  uint32_t result;\n\n  __ASM volatile (\"smlsdx %0, %1, %2, %3\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlsld %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlsld %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc)\n{\n  union llreg_u{\n    uint32_t w32[2];\n    uint64_t w64;\n  } llr;\n  llr.w64 = acc;\n\n#ifndef __ARMEB__   /* Little endian */\n  __ASM volatile (\"smlsldx %0, %1, %2, %3\" : \"=r\" (llr.w32[0]), \"=r\" (llr.w32[1]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[0]), \"1\" (llr.w32[1]) );\n#else               /* Big endian */\n  __ASM volatile (\"smlsldx %0, %1, %2, %3\" : \"=r\" (llr.w32[1]), \"=r\" (llr.w32[0]): \"r\" (op1), \"r\" (op2) , \"0\" (llr.w32[1]), \"1\" (llr.w32[0]) );\n#endif\n\n  return(llr.w64);\n}\n\n__STATIC_FORCEINLINE uint32_t __SEL  (uint32_t op1, uint32_t op2)\n{\n  uint32_t result;\n\n  __ASM volatile (\"sel %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE  int32_t __QADD( int32_t op1,  int32_t op2)\n{\n  int32_t result;\n\n  __ASM volatile (\"qadd %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n__STATIC_FORCEINLINE  int32_t __QSUB( int32_t op1,  int32_t op2)\n{\n  int32_t result;\n\n  __ASM volatile (\"qsub %0, %1, %2\" : \"=r\" (result) : \"r\" (op1), \"r\" (op2) );\n  return(result);\n}\n\n\n#define __PKHBT(ARG1,ARG2,ARG3) \\\n__extension__ \\\n({                          \\\n  uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \\\n  __ASM (\"pkhbt %0, %1, %2, lsl %3\" : \"=r\" (__RES) :  \"r\" (__ARG1), \"r\" (__ARG2), \"I\" (ARG3)  ); \\\n  __RES; \\\n })\n\n#define __PKHTB(ARG1,ARG2,ARG3) \\\n__extension__ \\\n({                          \\\n  uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \\\n  if (ARG3 == 0) \\\n    __ASM (\"pkhtb %0, %1, %2\" : \"=r\" (__RES) :  \"r\" (__ARG1), \"r\" (__ARG2)  ); \\\n  else \\\n    __ASM (\"pkhtb %0, %1, %2, asr %3\" : \"=r\" (__RES) :  \"r\" (__ARG1), \"r\" (__ARG2), \"I\" (ARG3)  ); \\\n  __RES; \\\n })\n\n\n__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3)\n{\n int32_t result;\n\n __ASM (\"smmla %0, %1, %2, %3\" : \"=r\" (result): \"r\"  (op1), \"r\" (op2), \"r\" (op3) );\n return(result);\n}\n\n#endif /* (__ARM_FEATURE_DSP == 1) */\n/*@} end of group CMSIS_SIMD_intrinsics */\n\n\n#pragma GCC diagnostic pop\n\n#endif /* __CMSIS_GCC_H */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_iccarm.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_iccarm.h\n * @brief    CMSIS compiler ICCARM (IAR Compiler for Arm) header file\n * @version  V5.4.0\n * @date     20. January 2023\n ******************************************************************************/\n\n//------------------------------------------------------------------------------\n//\n// Copyright (c) 2017-2021 IAR Systems\n// Copyright (c) 2017-2023 Arm Limited. All rights reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\")\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n//------------------------------------------------------------------------------\n\n\n#ifndef __CMSIS_ICCARM_H__\n#define __CMSIS_ICCARM_H__\n\n#ifndef __ICCARM__\n  #error This file should only be compiled by ICCARM\n#endif\n\n#pragma system_include\n\n#define __IAR_FT _Pragma(\"inline=forced\") __intrinsic\n\n#if (__VER__ >= 8000000)\n  #define __ICCARM_V8 1\n#else\n  #define __ICCARM_V8 0\n#endif\n\n#ifndef __ALIGNED\n  #if __ICCARM_V8\n    #define __ALIGNED(x) __attribute__((aligned(x)))\n  #elif (__VER__ >= 7080000)\n    /* Needs IAR language extensions */\n    #define __ALIGNED(x) __attribute__((aligned(x)))\n  #else\n    #warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored.\n    #define __ALIGNED(x)\n  #endif\n#endif\n\n\n/* Define compiler macros for CPU architecture, used in CMSIS 5.\n */\n#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ || __ARM_ARCH_8M_BASE__ || __ARM_ARCH_8M_MAIN__\n/* Macros already defined */\n#else\n  #if defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)\n    #define __ARM_ARCH_8M_MAIN__ 1\n  #elif defined(__ARM8M_BASELINE__)\n    #define __ARM_ARCH_8M_BASE__ 1\n  #elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M'\n    #if __ARM_ARCH == 6\n      #define __ARM_ARCH_6M__ 1\n    #elif __ARM_ARCH == 7\n      #if __ARM_FEATURE_DSP\n        #define __ARM_ARCH_7EM__ 1\n      #else\n        #define __ARM_ARCH_7M__ 1\n      #endif\n    #endif /* __ARM_ARCH */\n  #endif /* __ARM_ARCH_PROFILE == 'M' */\n#endif\n\n/* Alternativ core deduction for older ICCARM's */\n#if !defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__) && \\\n    !defined(__ARM_ARCH_8M_BASE__) && !defined(__ARM_ARCH_8M_MAIN__)\n  #if defined(__ARM6M__) && (__CORE__ == __ARM6M__)\n    #define __ARM_ARCH_6M__ 1\n  #elif defined(__ARM7M__) && (__CORE__ == __ARM7M__)\n    #define __ARM_ARCH_7M__ 1\n  #elif defined(__ARM7EM__) && (__CORE__ == __ARM7EM__)\n    #define __ARM_ARCH_7EM__  1\n  #elif defined(__ARM8M_BASELINE__) && (__CORE == __ARM8M_BASELINE__)\n    #define __ARM_ARCH_8M_BASE__ 1\n  #elif defined(__ARM8M_MAINLINE__) && (__CORE == __ARM8M_MAINLINE__)\n    #define __ARM_ARCH_8M_MAIN__ 1\n  #elif defined(__ARM8EM_MAINLINE__) && (__CORE == __ARM8EM_MAINLINE__)\n    #define __ARM_ARCH_8M_MAIN__ 1\n  #else\n    #error \"Unknown target.\"\n  #endif\n#endif\n\n\n\n#if defined(__ARM_ARCH_6M__) && __ARM_ARCH_6M__==1\n  #define __IAR_M0_FAMILY  1\n#elif defined(__ARM_ARCH_8M_BASE__) && __ARM_ARCH_8M_BASE__==1\n  #define __IAR_M0_FAMILY  1\n#else\n  #define __IAR_M0_FAMILY  0\n#endif\n\n#ifndef __NO_INIT\n  #define __NO_INIT __attribute__ ((section (\".noinit\")))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x) __attribute__ ((alias(x)))\n#endif\n\n#ifndef __ASM\n  #define __ASM __asm\n#endif\n\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER() __ASM volatile(\"\":::\"memory\")\n#endif\n\n#ifndef __INLINE\n  #define __INLINE inline\n#endif\n\n#ifndef   __NO_RETURN\n  #if __ICCARM_V8\n    #define __NO_RETURN __attribute__((__noreturn__))\n  #else\n    #define __NO_RETURN _Pragma(\"object_attribute=__noreturn\")\n  #endif\n#endif\n\n#ifndef   __PACKED\n  #if __ICCARM_V8\n    #define __PACKED __attribute__((packed, aligned(1)))\n  #else\n    /* Needs IAR language extensions */\n    #define __PACKED __packed\n  #endif\n#endif\n\n#ifndef   __PACKED_STRUCT\n  #if __ICCARM_V8\n    #define __PACKED_STRUCT struct __attribute__((packed, aligned(1)))\n  #else\n    /* Needs IAR language extensions */\n    #define __PACKED_STRUCT __packed struct\n  #endif\n#endif\n\n#ifndef   __PACKED_UNION\n  #if __ICCARM_V8\n    #define __PACKED_UNION union __attribute__((packed, aligned(1)))\n  #else\n    /* Needs IAR language extensions */\n    #define __PACKED_UNION __packed union\n  #endif\n#endif\n\n#ifndef   __RESTRICT\n  #if __ICCARM_V8\n    #define __RESTRICT            __restrict\n  #else\n    /* Needs IAR language extensions */\n    #define __RESTRICT            restrict\n  #endif\n#endif\n\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE       static inline\n#endif\n\n#ifndef   __FORCEINLINE\n  #define __FORCEINLINE         _Pragma(\"inline=forced\")\n#endif\n\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE  __FORCEINLINE __STATIC_INLINE\n#endif\n\n#ifndef __UNALIGNED_UINT16_READ\n#pragma language=save\n#pragma language=extended\n__IAR_FT uint16_t __iar_uint16_read(void const *ptr)\n{\n  return *(__packed uint16_t*)(ptr);\n}\n#pragma language=restore\n#define __UNALIGNED_UINT16_READ(PTR) __iar_uint16_read(PTR)\n#endif\n\n\n#ifndef __UNALIGNED_UINT16_WRITE\n#pragma language=save\n#pragma language=extended\n__IAR_FT void __iar_uint16_write(void const *ptr, uint16_t val)\n{\n  *(__packed uint16_t*)(ptr) = val;;\n}\n#pragma language=restore\n#define __UNALIGNED_UINT16_WRITE(PTR,VAL) __iar_uint16_write(PTR,VAL)\n#endif\n\n#ifndef __UNALIGNED_UINT32_READ\n#pragma language=save\n#pragma language=extended\n__IAR_FT uint32_t __iar_uint32_read(void const *ptr)\n{\n  return *(__packed uint32_t*)(ptr);\n}\n#pragma language=restore\n#define __UNALIGNED_UINT32_READ(PTR) __iar_uint32_read(PTR)\n#endif\n\n#ifndef __UNALIGNED_UINT32_WRITE\n#pragma language=save\n#pragma language=extended\n__IAR_FT void __iar_uint32_write(void const *ptr, uint32_t val)\n{\n  *(__packed uint32_t*)(ptr) = val;;\n}\n#pragma language=restore\n#define __UNALIGNED_UINT32_WRITE(PTR,VAL) __iar_uint32_write(PTR,VAL)\n#endif\n\n#ifndef __UNALIGNED_UINT32   /* deprecated */\n#pragma language=save\n#pragma language=extended\n__packed struct  __iar_u32 { uint32_t v; };\n#pragma language=restore\n#define __UNALIGNED_UINT32(PTR) (((struct __iar_u32 *)(PTR))->v)\n#endif\n\n#ifndef   __USED\n  #if __ICCARM_V8\n    #define __USED __attribute__((used))\n  #else\n    #define __USED _Pragma(\"__root\")\n  #endif\n#endif\n\n#undef __WEAK                           /* undo the definition from DLib_Defaults.h */\n#ifndef   __WEAK\n  #if __ICCARM_V8\n    #define __WEAK __attribute__((weak))\n  #else\n    #define __WEAK _Pragma(\"__weak\")\n  #endif\n#endif\n\n#ifndef __PROGRAM_START\n#define __PROGRAM_START           __iar_program_start\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              CSTACK$$Limit\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             CSTACK$$Base\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __vector_table\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  @\".intvec\"\n#endif\n\n#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)\n#ifndef __STACK_SEAL\n#define __STACK_SEAL              STACKSEAL$$Base\n#endif\n\n#ifndef __TZ_STACK_SEAL_SIZE\n#define __TZ_STACK_SEAL_SIZE      8U\n#endif\n\n#ifndef __TZ_STACK_SEAL_VALUE\n#define __TZ_STACK_SEAL_VALUE     0xFEF5EDA5FEF5EDA5ULL\n#endif\n\n__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) {\n  *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE;\n}\n#endif\n\n#ifndef __ICCARM_INTRINSICS_VERSION__\n  #define __ICCARM_INTRINSICS_VERSION__  0\n#endif\n\n#if __ICCARM_INTRINSICS_VERSION__ == 2\n\n  #if defined(__CLZ)\n    #undef __CLZ\n  #endif\n  #if defined(__REVSH)\n    #undef __REVSH\n  #endif\n  #if defined(__RBIT)\n    #undef __RBIT\n  #endif\n  #if defined(__SSAT)\n    #undef __SSAT\n  #endif\n  #if defined(__USAT)\n    #undef __USAT\n  #endif\n\n  #include \"iccarm_builtin.h\"\n\n  #define __disable_fault_irq __iar_builtin_disable_fiq\n  #define __disable_irq       __iar_builtin_disable_interrupt\n  #define __enable_fault_irq  __iar_builtin_enable_fiq\n  #define __enable_irq        __iar_builtin_enable_interrupt\n  #define __arm_rsr           __iar_builtin_rsr\n  #define __arm_wsr           __iar_builtin_wsr\n\n\n  #define __get_APSR()                (__arm_rsr(\"APSR\"))\n  #define __get_BASEPRI()             (__arm_rsr(\"BASEPRI\"))\n  #define __get_CONTROL()             (__arm_rsr(\"CONTROL\"))\n  #define __get_FAULTMASK()           (__arm_rsr(\"FAULTMASK\"))\n\n  #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n       (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n    #define __get_FPSCR()             (__arm_rsr(\"FPSCR\"))\n    #define __set_FPSCR(VALUE)        (__arm_wsr(\"FPSCR\", (VALUE)))\n  #else\n    #define __get_FPSCR()             ( 0 )\n    #define __set_FPSCR(VALUE)        ((void)VALUE)\n  #endif\n\n  #define __get_IPSR()                (__arm_rsr(\"IPSR\"))\n  #define __get_MSP()                 (__arm_rsr(\"MSP\"))\n  #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n       (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure MSPLIM is RAZ/WI\n    #define __get_MSPLIM()            (0U)\n  #else\n    #define __get_MSPLIM()            (__arm_rsr(\"MSPLIM\"))\n  #endif\n  #define __get_PRIMASK()             (__arm_rsr(\"PRIMASK\"))\n  #define __get_PSP()                 (__arm_rsr(\"PSP\"))\n\n  #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n       (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n    #define __get_PSPLIM()            (0U)\n  #else\n    #define __get_PSPLIM()            (__arm_rsr(\"PSPLIM\"))\n  #endif\n\n  #define __get_xPSR()                (__arm_rsr(\"xPSR\"))\n\n  #define __set_BASEPRI(VALUE)        (__arm_wsr(\"BASEPRI\", (VALUE)))\n  #define __set_BASEPRI_MAX(VALUE)    (__arm_wsr(\"BASEPRI_MAX\", (VALUE)))\n\n__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)\n{\n  __arm_wsr(\"CONTROL\", control);\n  __iar_builtin_ISB();\n}\n\n  #define __set_FAULTMASK(VALUE)      (__arm_wsr(\"FAULTMASK\", (VALUE)))\n  #define __set_MSP(VALUE)            (__arm_wsr(\"MSP\", (VALUE)))\n\n  #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n       (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure MSPLIM is RAZ/WI\n    #define __set_MSPLIM(VALUE)       ((void)(VALUE))\n  #else\n    #define __set_MSPLIM(VALUE)       (__arm_wsr(\"MSPLIM\", (VALUE)))\n  #endif\n  #define __set_PRIMASK(VALUE)        (__arm_wsr(\"PRIMASK\", (VALUE)))\n  #define __set_PSP(VALUE)            (__arm_wsr(\"PSP\", (VALUE)))\n  #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n       (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n    #define __set_PSPLIM(VALUE)       ((void)(VALUE))\n  #else\n    #define __set_PSPLIM(VALUE)       (__arm_wsr(\"PSPLIM\", (VALUE)))\n  #endif\n\n  #define __TZ_get_CONTROL_NS()       (__arm_rsr(\"CONTROL_NS\"))\n\n__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)\n{\n  __arm_wsr(\"CONTROL_NS\", control);\n  __iar_builtin_ISB();\n}\n\n  #define __TZ_get_PSP_NS()           (__arm_rsr(\"PSP_NS\"))\n  #define __TZ_set_PSP_NS(VALUE)      (__arm_wsr(\"PSP_NS\", (VALUE)))\n  #define __TZ_get_MSP_NS()           (__arm_rsr(\"MSP_NS\"))\n  #define __TZ_set_MSP_NS(VALUE)      (__arm_wsr(\"MSP_NS\", (VALUE)))\n  #define __TZ_get_SP_NS()            (__arm_rsr(\"SP_NS\"))\n  #define __TZ_set_SP_NS(VALUE)       (__arm_wsr(\"SP_NS\", (VALUE)))\n  #define __TZ_get_PRIMASK_NS()       (__arm_rsr(\"PRIMASK_NS\"))\n  #define __TZ_set_PRIMASK_NS(VALUE)  (__arm_wsr(\"PRIMASK_NS\", (VALUE)))\n  #define __TZ_get_BASEPRI_NS()       (__arm_rsr(\"BASEPRI_NS\"))\n  #define __TZ_set_BASEPRI_NS(VALUE)  (__arm_wsr(\"BASEPRI_NS\", (VALUE)))\n  #define __TZ_get_FAULTMASK_NS()     (__arm_rsr(\"FAULTMASK_NS\"))\n  #define __TZ_set_FAULTMASK_NS(VALUE)(__arm_wsr(\"FAULTMASK_NS\", (VALUE)))\n\n  #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n       (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n    #define __TZ_get_PSPLIM_NS()      (0U)\n    #define __TZ_set_PSPLIM_NS(VALUE) ((void)(VALUE))\n  #else\n    #define __TZ_get_PSPLIM_NS()      (__arm_rsr(\"PSPLIM_NS\"))\n    #define __TZ_set_PSPLIM_NS(VALUE) (__arm_wsr(\"PSPLIM_NS\", (VALUE)))\n  #endif\n\n  #define __TZ_get_MSPLIM_NS()        (__arm_rsr(\"MSPLIM_NS\"))\n  #define __TZ_set_MSPLIM_NS(VALUE)   (__arm_wsr(\"MSPLIM_NS\", (VALUE)))\n\n  #define __NOP     __iar_builtin_no_operation\n\n  #define __CLZ     __iar_builtin_CLZ\n  #define __CLREX   __iar_builtin_CLREX\n\n  #define __DMB     __iar_builtin_DMB\n  #define __DSB     __iar_builtin_DSB\n  #define __ISB     __iar_builtin_ISB\n\n  #define __LDREXB  __iar_builtin_LDREXB\n  #define __LDREXH  __iar_builtin_LDREXH\n  #define __LDREXW  __iar_builtin_LDREX\n\n  #define __RBIT    __iar_builtin_RBIT\n  #define __REV     __iar_builtin_REV\n  #define __REV16   __iar_builtin_REV16\n\n  __IAR_FT int16_t __REVSH(int16_t val)\n  {\n    return (int16_t) __iar_builtin_REVSH(val);\n  }\n\n  #define __ROR     __iar_builtin_ROR\n  #define __RRX     __iar_builtin_RRX\n\n  #define __SEV     __iar_builtin_SEV\n\n  #if !__IAR_M0_FAMILY\n    #define __SSAT    __iar_builtin_SSAT\n  #endif\n\n  #define __STREXB  __iar_builtin_STREXB\n  #define __STREXH  __iar_builtin_STREXH\n  #define __STREXW  __iar_builtin_STREX\n\n  #if !__IAR_M0_FAMILY\n    #define __USAT    __iar_builtin_USAT\n  #endif\n\n  #define __WFE     __iar_builtin_WFE\n  #define __WFI     __iar_builtin_WFI\n\n  #if __ARM_MEDIA__\n    #define __SADD8   __iar_builtin_SADD8\n    #define __QADD8   __iar_builtin_QADD8\n    #define __SHADD8  __iar_builtin_SHADD8\n    #define __UADD8   __iar_builtin_UADD8\n    #define __UQADD8  __iar_builtin_UQADD8\n    #define __UHADD8  __iar_builtin_UHADD8\n    #define __SSUB8   __iar_builtin_SSUB8\n    #define __QSUB8   __iar_builtin_QSUB8\n    #define __SHSUB8  __iar_builtin_SHSUB8\n    #define __USUB8   __iar_builtin_USUB8\n    #define __UQSUB8  __iar_builtin_UQSUB8\n    #define __UHSUB8  __iar_builtin_UHSUB8\n    #define __SADD16  __iar_builtin_SADD16\n    #define __QADD16  __iar_builtin_QADD16\n    #define __SHADD16 __iar_builtin_SHADD16\n    #define __UADD16  __iar_builtin_UADD16\n    #define __UQADD16 __iar_builtin_UQADD16\n    #define __UHADD16 __iar_builtin_UHADD16\n    #define __SSUB16  __iar_builtin_SSUB16\n    #define __QSUB16  __iar_builtin_QSUB16\n    #define __SHSUB16 __iar_builtin_SHSUB16\n    #define __USUB16  __iar_builtin_USUB16\n    #define __UQSUB16 __iar_builtin_UQSUB16\n    #define __UHSUB16 __iar_builtin_UHSUB16\n    #define __SASX    __iar_builtin_SASX\n    #define __QASX    __iar_builtin_QASX\n    #define __SHASX   __iar_builtin_SHASX\n    #define __UASX    __iar_builtin_UASX\n    #define __UQASX   __iar_builtin_UQASX\n    #define __UHASX   __iar_builtin_UHASX\n    #define __SSAX    __iar_builtin_SSAX\n    #define __QSAX    __iar_builtin_QSAX\n    #define __SHSAX   __iar_builtin_SHSAX\n    #define __USAX    __iar_builtin_USAX\n    #define __UQSAX   __iar_builtin_UQSAX\n    #define __UHSAX   __iar_builtin_UHSAX\n    #define __USAD8   __iar_builtin_USAD8\n    #define __USADA8  __iar_builtin_USADA8\n    #define __SSAT16  __iar_builtin_SSAT16\n    #define __USAT16  __iar_builtin_USAT16\n    #define __UXTB16  __iar_builtin_UXTB16\n    #define __UXTAB16 __iar_builtin_UXTAB16\n    #define __SXTB16  __iar_builtin_SXTB16\n    #define __SXTAB16 __iar_builtin_SXTAB16\n    #define __SMUAD   __iar_builtin_SMUAD\n    #define __SMUADX  __iar_builtin_SMUADX\n    #define __SMMLA   __iar_builtin_SMMLA\n    #define __SMLAD   __iar_builtin_SMLAD\n    #define __SMLADX  __iar_builtin_SMLADX\n    #define __SMLALD  __iar_builtin_SMLALD\n    #define __SMLALDX __iar_builtin_SMLALDX\n    #define __SMUSD   __iar_builtin_SMUSD\n    #define __SMUSDX  __iar_builtin_SMUSDX\n    #define __SMLSD   __iar_builtin_SMLSD\n    #define __SMLSDX  __iar_builtin_SMLSDX\n    #define __SMLSLD  __iar_builtin_SMLSLD\n    #define __SMLSLDX __iar_builtin_SMLSLDX\n    #define __SEL     __iar_builtin_SEL\n    #define __QADD    __iar_builtin_QADD\n    #define __QSUB    __iar_builtin_QSUB\n    #define __PKHBT   __iar_builtin_PKHBT\n    #define __PKHTB   __iar_builtin_PKHTB\n  #endif\n\n#else /* __ICCARM_INTRINSICS_VERSION__ == 2 */\n\n  #if __IAR_M0_FAMILY\n   /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */\n    #define __CLZ  __cmsis_iar_clz_not_active\n    #define __SSAT __cmsis_iar_ssat_not_active\n    #define __USAT __cmsis_iar_usat_not_active\n    #define __RBIT __cmsis_iar_rbit_not_active\n    #define __get_APSR  __cmsis_iar_get_APSR_not_active\n  #endif\n\n\n  #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n         (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     ))\n    #define __get_FPSCR __cmsis_iar_get_FPSR_not_active\n    #define __set_FPSCR __cmsis_iar_set_FPSR_not_active\n  #endif\n\n  #ifdef __INTRINSICS_INCLUDED\n  #error intrinsics.h is already included previously!\n  #endif\n\n  #include <intrinsics.h>\n\n  #if __IAR_M0_FAMILY\n   /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */\n    #undef __CLZ\n    #undef __SSAT\n    #undef __USAT\n    #undef __RBIT\n    #undef __get_APSR\n\n    __STATIC_INLINE uint8_t __CLZ(uint32_t data)\n    {\n      if (data == 0U) { return 32U; }\n\n      uint32_t count = 0U;\n      uint32_t mask = 0x80000000U;\n\n      while ((data & mask) == 0U)\n      {\n        count += 1U;\n        mask = mask >> 1U;\n      }\n      return count;\n    }\n\n    __STATIC_INLINE uint32_t __RBIT(uint32_t v)\n    {\n      uint8_t sc = 31U;\n      uint32_t r = v;\n      for (v >>= 1U; v; v >>= 1U)\n      {\n        r <<= 1U;\n        r |= v & 1U;\n        sc--;\n      }\n      return (r << sc);\n    }\n\n    __STATIC_INLINE  uint32_t __get_APSR(void)\n    {\n      uint32_t res;\n      __asm(\"MRS      %0,APSR\" : \"=r\" (res));\n      return res;\n    }\n\n  #endif\n\n  #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n         (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     ))\n    #undef __get_FPSCR\n    #undef __set_FPSCR\n    #define __get_FPSCR()       (0)\n    #define __set_FPSCR(VALUE)  ((void)VALUE)\n  #endif\n\n  #pragma diag_suppress=Pe940\n  #pragma diag_suppress=Pe177\n\n  #define __enable_irq    __enable_interrupt\n  #define __disable_irq   __disable_interrupt\n  #define __NOP           __no_operation\n\n  #define __get_xPSR      __get_PSR\n\n  #if (!defined(__ARM_ARCH_6M__) || __ARM_ARCH_6M__==0)\n\n    __IAR_FT uint32_t __LDREXW(uint32_t volatile *ptr)\n    {\n      return __LDREX((unsigned long *)ptr);\n    }\n\n    __IAR_FT uint32_t __STREXW(uint32_t value, uint32_t volatile *ptr)\n    {\n      return __STREX(value, (unsigned long *)ptr);\n    }\n  #endif\n\n\n  /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */\n  #if (__CORTEX_M >= 0x03)\n\n    __IAR_FT uint32_t __RRX(uint32_t value)\n    {\n      uint32_t result;\n      __ASM volatile(\"RRX      %0, %1\" : \"=r\"(result) : \"r\" (value));\n      return(result);\n    }\n\n    __IAR_FT void __set_BASEPRI_MAX(uint32_t value)\n    {\n      __asm volatile(\"MSR      BASEPRI_MAX,%0\"::\"r\" (value));\n    }\n\n\n    #define __enable_fault_irq  __enable_fiq\n    #define __disable_fault_irq __disable_fiq\n\n\n  #endif /* (__CORTEX_M >= 0x03) */\n\n  __IAR_FT uint32_t __ROR(uint32_t op1, uint32_t op2)\n  {\n    return (op1 >> op2) | (op1 << ((sizeof(op1)*8)-op2));\n  }\n\n  #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n       (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n\n   __IAR_FT uint32_t __get_MSPLIM(void)\n    {\n      uint32_t res;\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure MSPLIM is RAZ/WI\n      res = 0U;\n    #else\n      __asm volatile(\"MRS      %0,MSPLIM\" : \"=r\" (res));\n    #endif\n      return res;\n    }\n\n    __IAR_FT void   __set_MSPLIM(uint32_t value)\n    {\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure MSPLIM is RAZ/WI\n      (void)value;\n    #else\n      __asm volatile(\"MSR      MSPLIM,%0\" :: \"r\" (value));\n    #endif\n    }\n\n    __IAR_FT uint32_t __get_PSPLIM(void)\n    {\n      uint32_t res;\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure PSPLIM is RAZ/WI\n      res = 0U;\n    #else\n      __asm volatile(\"MRS      %0,PSPLIM\" : \"=r\" (res));\n    #endif\n      return res;\n    }\n\n    __IAR_FT void   __set_PSPLIM(uint32_t value)\n    {\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure PSPLIM is RAZ/WI\n      (void)value;\n    #else\n      __asm volatile(\"MSR      PSPLIM,%0\" :: \"r\" (value));\n    #endif\n    }\n\n    __IAR_FT uint32_t __TZ_get_CONTROL_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,CONTROL_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_CONTROL_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      CONTROL_NS,%0\" :: \"r\" (value));\n      __iar_builtin_ISB();\n    }\n\n    __IAR_FT uint32_t   __TZ_get_PSP_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,PSP_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_PSP_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      PSP_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_MSP_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,MSP_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_MSP_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      MSP_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_SP_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,SP_NS\" : \"=r\" (res));\n      return res;\n    }\n    __IAR_FT void   __TZ_set_SP_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      SP_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_PRIMASK_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,PRIMASK_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_PRIMASK_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      PRIMASK_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_BASEPRI_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,BASEPRI_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_BASEPRI_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      BASEPRI_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_FAULTMASK_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,FAULTMASK_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_FAULTMASK_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      FAULTMASK_NS,%0\" :: \"r\" (value));\n    }\n\n    __IAR_FT uint32_t   __TZ_get_PSPLIM_NS(void)\n    {\n      uint32_t res;\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure PSPLIM is RAZ/WI\n      res = 0U;\n    #else\n      __asm volatile(\"MRS      %0,PSPLIM_NS\" : \"=r\" (res));\n    #endif\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_PSPLIM_NS(uint32_t value)\n    {\n    #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \\\n         (!defined (__ARM_FEATURE_CMSE  ) || (__ARM_FEATURE_CMSE   < 3)))\n      // without main extensions, the non-secure PSPLIM is RAZ/WI\n      (void)value;\n    #else\n      __asm volatile(\"MSR      PSPLIM_NS,%0\" :: \"r\" (value));\n    #endif\n    }\n\n    __IAR_FT uint32_t   __TZ_get_MSPLIM_NS(void)\n    {\n      uint32_t res;\n      __asm volatile(\"MRS      %0,MSPLIM_NS\" : \"=r\" (res));\n      return res;\n    }\n\n    __IAR_FT void   __TZ_set_MSPLIM_NS(uint32_t value)\n    {\n      __asm volatile(\"MSR      MSPLIM_NS,%0\" :: \"r\" (value));\n    }\n\n  #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */\n\n#endif   /* __ICCARM_INTRINSICS_VERSION__ == 2 */\n\n#define __BKPT(value)    __asm volatile (\"BKPT     %0\" : : \"i\"(value))\n\n#if __IAR_M0_FAMILY\n  __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat)\n  {\n    if ((sat >= 1U) && (sat <= 32U))\n    {\n      const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n      const int32_t min = -1 - max ;\n      if (val > max)\n      {\n        return max;\n      }\n      else if (val < min)\n      {\n        return min;\n      }\n    }\n    return val;\n  }\n\n  __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat)\n  {\n    if (sat <= 31U)\n    {\n      const uint32_t max = ((1U << sat) - 1U);\n      if (val > (int32_t)max)\n      {\n        return max;\n      }\n      else if (val < 0)\n      {\n        return 0U;\n      }\n    }\n    return (uint32_t)val;\n  }\n#endif\n\n#if (__CORTEX_M >= 0x03)   /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */\n\n  __IAR_FT uint8_t __LDRBT(volatile uint8_t *addr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDRBT %0, [%1]\" : \"=r\" (res) : \"r\" (addr) : \"memory\");\n    return ((uint8_t)res);\n  }\n\n  __IAR_FT uint16_t __LDRHT(volatile uint16_t *addr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDRHT %0, [%1]\" : \"=r\" (res) : \"r\" (addr) : \"memory\");\n    return ((uint16_t)res);\n  }\n\n  __IAR_FT uint32_t __LDRT(volatile uint32_t *addr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDRT %0, [%1]\" : \"=r\" (res) : \"r\" (addr) : \"memory\");\n    return res;\n  }\n\n  __IAR_FT void __STRBT(uint8_t value, volatile uint8_t *addr)\n  {\n    __ASM volatile (\"STRBT %1, [%0]\" : : \"r\" (addr), \"r\" ((uint32_t)value) : \"memory\");\n  }\n\n  __IAR_FT void __STRHT(uint16_t value, volatile uint16_t *addr)\n  {\n    __ASM volatile (\"STRHT %1, [%0]\" : : \"r\" (addr), \"r\" ((uint32_t)value) : \"memory\");\n  }\n\n  __IAR_FT void __STRT(uint32_t value, volatile uint32_t *addr)\n  {\n    __ASM volatile (\"STRT %1, [%0]\" : : \"r\" (addr), \"r\" (value) : \"memory\");\n  }\n\n#endif /* (__CORTEX_M >= 0x03) */\n\n#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1))    )\n\n\n  __IAR_FT uint8_t __LDAB(volatile uint8_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDAB %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return ((uint8_t)res);\n  }\n\n  __IAR_FT uint16_t __LDAH(volatile uint16_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDAH %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return ((uint16_t)res);\n  }\n\n  __IAR_FT uint32_t __LDA(volatile uint32_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDA %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return res;\n  }\n\n  __IAR_FT void __STLB(uint8_t value, volatile uint8_t *ptr)\n  {\n    __ASM volatile (\"STLB %1, [%0]\" :: \"r\" (ptr), \"r\" (value) : \"memory\");\n  }\n\n  __IAR_FT void __STLH(uint16_t value, volatile uint16_t *ptr)\n  {\n    __ASM volatile (\"STLH %1, [%0]\" :: \"r\" (ptr), \"r\" (value) : \"memory\");\n  }\n\n  __IAR_FT void __STL(uint32_t value, volatile uint32_t *ptr)\n  {\n    __ASM volatile (\"STL %1, [%0]\" :: \"r\" (ptr), \"r\" (value) : \"memory\");\n  }\n\n  __IAR_FT uint8_t __LDAEXB(volatile uint8_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDAEXB %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return ((uint8_t)res);\n  }\n\n  __IAR_FT uint16_t __LDAEXH(volatile uint16_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDAEXH %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return ((uint16_t)res);\n  }\n\n  __IAR_FT uint32_t __LDAEX(volatile uint32_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"LDAEX %0, [%1]\" : \"=r\" (res) : \"r\" (ptr) : \"memory\");\n    return res;\n  }\n\n  __IAR_FT uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"STLEXB %0, %2, [%1]\" : \"=r\" (res) : \"r\" (ptr), \"r\" (value) : \"memory\");\n    return res;\n  }\n\n  __IAR_FT uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"STLEXH %0, %2, [%1]\" : \"=r\" (res) : \"r\" (ptr), \"r\" (value) : \"memory\");\n    return res;\n  }\n\n  __IAR_FT uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr)\n  {\n    uint32_t res;\n    __ASM volatile (\"STLEX %0, %2, [%1]\" : \"=r\" (res) : \"r\" (ptr), \"r\" (value) : \"memory\");\n    return res;\n  }\n\n#endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */\n\n#undef __IAR_FT\n#undef __IAR_M0_FAMILY\n#undef __ICCARM_V8\n\n#pragma diag_default=Pe940\n#pragma diag_default=Pe177\n\n#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2))\n\n#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))\n\n#endif /* __CMSIS_ICCARM_H__ */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_tiarmclang.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_tiarmclang.h\n * @brief    CMSIS compiler tiarmclang header file\n * @version  V1.0.0\n * @date     04. April 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */\n\n#ifndef __CMSIS_TIARMCLANG_H\n#define __CMSIS_TIARMCLANG_H\n\n#pragma clang system_header   /* treat file as system include file */\n\n/* CMSIS compiler specific defines */\n#ifndef   __ASM\n  #define __ASM                                  __asm\n#endif\n#ifndef   __INLINE\n  #define __INLINE                               __inline\n#endif\n#ifndef   __STATIC_INLINE\n  #define __STATIC_INLINE                        static __inline\n#endif\n#ifndef   __STATIC_FORCEINLINE\n  #define __STATIC_FORCEINLINE                   __attribute__((always_inline)) static __inline\n#endif\n#ifndef   __NO_RETURN\n  #define __NO_RETURN                            __attribute__((__noreturn__))\n#endif\n#ifndef   __USED\n  #define __USED                                 __attribute__((used))\n#endif\n#ifndef   __WEAK\n  #define __WEAK                                 __attribute__((weak))\n#endif\n#ifndef   __PACKED\n  #define __PACKED                               __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_STRUCT\n  #define __PACKED_STRUCT                        struct __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __PACKED_UNION\n  #define __PACKED_UNION                         union __attribute__((packed, aligned(1)))\n#endif\n#ifndef   __UNALIGNED_UINT32        /* deprecated */\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */\n  struct __attribute__((packed)) T_UINT32 { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32(x)                  (((struct T_UINT32 *)(x))->v)\n#endif\n#ifndef   __UNALIGNED_UINT16_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */\n  __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_WRITE(addr, val)    (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT16_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */\n  __PACKED_STRUCT T_UINT16_READ { uint16_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT16_READ(addr)          (((const struct T_UINT16_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __UNALIGNED_UINT32_WRITE\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */\n  __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_WRITE(addr, val)    (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))\n#endif\n#ifndef   __UNALIGNED_UINT32_READ\n  #pragma clang diagnostic push\n  #pragma clang diagnostic ignored \"-Wpacked\"\n/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */\n  __PACKED_STRUCT T_UINT32_READ { uint32_t v; };\n  #pragma clang diagnostic pop\n  #define __UNALIGNED_UINT32_READ(addr)          (((const struct T_UINT32_READ *)(const void *)(addr))->v)\n#endif\n#ifndef   __ALIGNED\n  #define __ALIGNED(x)                           __attribute__((aligned(x)))\n#endif\n#ifndef   __RESTRICT\n  #define __RESTRICT                             __restrict\n#endif\n#ifndef   __COMPILER_BARRIER\n  #define __COMPILER_BARRIER()                   __ASM volatile(\"\":::\"memory\")\n#endif\n#ifndef __NO_INIT\n  #define __NO_INIT                              __attribute__ ((section (\".bss.noinit\")))\n#endif\n#ifndef __ALIAS\n  #define __ALIAS(x)                             __attribute__ ((alias(x)))\n#endif\n\n\n/* #########################  Startup and Lowlevel Init  ######################## */\n\n#ifndef __PROGRAM_START\n#define __PROGRAM_START           _c_int00\n#endif\n\n#ifndef __INITIAL_SP\n#define __INITIAL_SP              __STACK_END\n#endif\n\n#ifndef __STACK_LIMIT\n#define __STACK_LIMIT             __STACK_SIZE\n#endif\n\n#ifndef __VECTOR_TABLE\n#define __VECTOR_TABLE            __Vectors\n#endif\n\n#ifndef __VECTOR_TABLE_ATTRIBUTE\n#define __VECTOR_TABLE_ATTRIBUTE  __attribute__((used, section(\".intvecs\")))\n#endif\n\n#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)\n#ifndef __STACK_SEAL\n#define __STACK_SEAL              Image$$STACKSEAL$$ZI$$Base\n#endif\n\n#ifndef __TZ_STACK_SEAL_SIZE\n#define __TZ_STACK_SEAL_SIZE      8U\n#endif\n\n#ifndef __TZ_STACK_SEAL_VALUE\n#define __TZ_STACK_SEAL_VALUE     0xFEF5EDA5FEF5EDA5ULL\n#endif\n\n\n__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) {\n  *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE;\n}\n#endif\n\n\n/* ##########################  Core Instruction Access  ######################### */\n/** \\defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface\n  Access to dedicated instructions\n  @{\n*/\n\n/* Define macros for porting to both thumb1 and thumb2.\n * For thumb1, use low register (r0-r7), specified by constraint \"l\"\n * Otherwise, use general registers, specified by constraint \"r\" */\n#if defined (__thumb__) && !defined (__thumb2__)\n#define __CMSIS_GCC_OUT_REG(r) \"=l\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+l\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"l\" (r)\n#else\n#define __CMSIS_GCC_OUT_REG(r) \"=r\" (r)\n#define __CMSIS_GCC_RW_REG(r) \"+r\" (r)\n#define __CMSIS_GCC_USE_REG(r) \"r\" (r)\n#endif\n\n/**\n  \\brief   No Operation\n  \\details No Operation does nothing. This instruction can be used for code alignment purposes.\n */\n#define __NOP          __builtin_arm_nop\n\n/**\n  \\brief   Wait For Interrupt\n  \\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.\n */\n#define __WFI          __builtin_arm_wfi\n\n\n/**\n  \\brief   Wait For Event\n  \\details Wait For Event is a hint instruction that permits the processor to enter\n           a low-power state until one of a number of events occurs.\n */\n#define __WFE          __builtin_arm_wfe\n\n\n/**\n  \\brief   Send Event\n  \\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.\n */\n#define __SEV          __builtin_arm_sev\n\n\n/**\n  \\brief   Instruction Synchronization Barrier\n  \\details Instruction Synchronization Barrier flushes the pipeline in the processor,\n           so that all instructions following the ISB are fetched from cache or memory,\n           after the instruction has been completed.\n */\n#define __ISB()        __builtin_arm_isb(0xF)\n\n/**\n  \\brief   Data Synchronization Barrier\n  \\details Acts as a special kind of Data Memory Barrier.\n           It completes when all explicit memory accesses before this instruction complete.\n */\n#define __DSB()        __builtin_arm_dsb(0xF)\n\n\n/**\n  \\brief   Data Memory Barrier\n  \\details Ensures the apparent order of the explicit memory operations before\n           and after the instruction, without ensuring their completion.\n */\n#define __DMB()        __builtin_arm_dmb(0xF)\n\n\n/**\n  \\brief   Reverse byte order (32 bit)\n  \\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV(value)   __builtin_bswap32(value)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REV16(value) __ROR(__REV(value), 16)\n\n\n/**\n  \\brief   Reverse byte order (16 bit)\n  \\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __REVSH(value) (int16_t)__builtin_bswap16(value)\n\n\n/**\n  \\brief   Rotate Right in unsigned value (32 bit)\n  \\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.\n  \\param [in]    op1  Value to rotate\n  \\param [in]    op2  Number of Bits to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2)\n{\n  op2 %= 32U;\n  if (op2 == 0U)\n  {\n    return op1;\n  }\n  return (op1 >> op2) | (op1 << (32U - op2));\n}\n\n\n/**\n  \\brief   Breakpoint\n  \\details Causes the processor to enter Debug state.\n           Debug tools can use this to investigate system state when the instruction at a particular address is reached.\n  \\param [in]    value  is ignored by the processor.\n                 If required, a debugger can use it to store additional information about the breakpoint.\n */\n#define __BKPT(value)     __ASM volatile (\"bkpt \"#value)\n\n\n/**\n  \\brief   Reverse bit order of value\n  \\details Reverses the bit order of the given value.\n  \\param [in]    value  Value to reverse\n  \\return               Reversed value\n */\n#define __RBIT            __builtin_arm_rbit\n\n/**\n  \\brief   Count leading zeros\n  \\details Counts the number of leading zeros of a data value.\n  \\param [in]  value  Value to count the leading zeros\n  \\return             number of leading zeros in value\n */\n__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value)\n{\n  /* Even though __builtin_clz produces a CLZ instruction on ARM, formally\n     __builtin_clz(0) is undefined behaviour, so handle this case specially.\n     This guarantees ARM-compatible results if happening to compile on a non-ARM\n     target, and ensures the compiler doesn't decide to activate any\n     optimisations using the logic \"value was passed to __builtin_clz, so it\n     is non-zero\".\n     ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a\n     single CLZ instruction.\n   */\n  if (value == 0U)\n  {\n    return 32U;\n  }\n  return __builtin_clz(value);\n}\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   LDR Exclusive (8 bit)\n  \\details Executes a exclusive LDR instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define __LDREXB        (uint8_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (16 bit)\n  \\details Executes a exclusive LDR instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define __LDREXH        (uint16_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   LDR Exclusive (32 bit)\n  \\details Executes a exclusive LDR instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define __LDREXW        (uint32_t)__builtin_arm_ldrex\n\n\n/**\n  \\brief   STR Exclusive (8 bit)\n  \\details Executes a exclusive STR instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXB        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (16 bit)\n  \\details Executes a exclusive STR instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXH        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   STR Exclusive (32 bit)\n  \\details Executes a exclusive STR instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define __STREXW        (uint32_t)__builtin_arm_strex\n\n\n/**\n  \\brief   Remove the exclusive lock\n  \\details Removes the exclusive lock which is created by LDREX.\n */\n#define __CLREX             __builtin_arm_clrex\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n#define __SSAT             __builtin_arm_ssat\n\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n#define __USAT             __builtin_arm_usat\n\n\n/**\n  \\brief   Rotate Right with Extend (32 bit)\n  \\details Moves each bit of a bitstring right by one bit.\n           The carry input is shifted in at the left end of the bitstring.\n  \\param [in]    value  Value to rotate\n  \\return               Rotated value\n */\n__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value)\n{\n  uint32_t result;\n\n  __ASM volatile (\"rrx %0, %1\" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );\n  return(result);\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged LDRT instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrbt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint8_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged LDRT instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrht %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return ((uint16_t) result);    /* Add explicit type cast here */\n}\n\n\n/**\n  \\brief   LDRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged LDRT instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldrt %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) );\n  return(result);\n}\n\n\n/**\n  \\brief   STRT Unprivileged (8 bit)\n  \\details Executes a Unprivileged STRT instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"strbt %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (16 bit)\n  \\details Executes a Unprivileged STRT instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"strht %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) );\n}\n\n\n/**\n  \\brief   STRT Unprivileged (32 bit)\n  \\details Executes a Unprivileged STRT instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"strt %1, %0\" : \"=Q\" (*ptr) : \"r\" (value) );\n}\n\n#else /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n          (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n          (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n          (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/**\n  \\brief   Signed Saturate\n  \\details Saturates a signed value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (1..32)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat)\n{\n  if ((sat >= 1U) && (sat <= 32U))\n  {\n    const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);\n    const int32_t min = -1 - max ;\n    if (val > max)\n    {\n      return max;\n    }\n    else if (val < min)\n    {\n      return min;\n    }\n  }\n  return val;\n}\n\n/**\n  \\brief   Unsigned Saturate\n  \\details Saturates an unsigned value.\n  \\param [in]  value  Value to be saturated\n  \\param [in]    sat  Bit position to saturate to (0..31)\n  \\return             Saturated value\n */\n__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat)\n{\n  if (sat <= 31U)\n  {\n    const uint32_t max = ((1U << sat) - 1U);\n    if (val > (int32_t)max)\n    {\n      return max;\n    }\n    else if (val < 0)\n    {\n      return 0U;\n    }\n  }\n  return (uint32_t)val;\n}\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Load-Acquire (8 bit)\n  \\details Executes a LDAB instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldab %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint8_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (16 bit)\n  \\details Executes a LDAH instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"ldah %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return ((uint16_t) result);\n}\n\n\n/**\n  \\brief   Load-Acquire (32 bit)\n  \\details Executes a LDA instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr)\n{\n  uint32_t result;\n\n  __ASM volatile (\"lda %0, %1\" : \"=r\" (result) : \"Q\" (*ptr) : \"memory\" );\n  return(result);\n}\n\n\n/**\n  \\brief   Store-Release (8 bit)\n  \\details Executes a STLB instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr)\n{\n  __ASM volatile (\"stlb %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (16 bit)\n  \\details Executes a STLH instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr)\n{\n  __ASM volatile (\"stlh %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Store-Release (32 bit)\n  \\details Executes a STL instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n */\n__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr)\n{\n  __ASM volatile (\"stl %1, %0\" : \"=Q\" (*ptr) : \"r\" ((uint32_t)value) : \"memory\" );\n}\n\n\n/**\n  \\brief   Load-Acquire Exclusive (8 bit)\n  \\details Executes a LDAB exclusive instruction for 8 bit value.\n  \\param [in]    ptr  Pointer to data\n  \\return             value of type uint8_t at (*ptr)\n */\n#define     __LDAEXB                 (uint8_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (16 bit)\n  \\details Executes a LDAH exclusive instruction for 16 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint16_t at (*ptr)\n */\n#define     __LDAEXH                 (uint16_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Load-Acquire Exclusive (32 bit)\n  \\details Executes a LDA exclusive instruction for 32 bit values.\n  \\param [in]    ptr  Pointer to data\n  \\return        value of type uint32_t at (*ptr)\n */\n#define     __LDAEX                  (uint32_t)__builtin_arm_ldaex\n\n\n/**\n  \\brief   Store-Release Exclusive (8 bit)\n  \\details Executes a STLB exclusive instruction for 8 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXB                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (16 bit)\n  \\details Executes a STLH exclusive instruction for 16 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEXH                 (uint32_t)__builtin_arm_stlex\n\n\n/**\n  \\brief   Store-Release Exclusive (32 bit)\n  \\details Executes a STL exclusive instruction for 32 bit values.\n  \\param [in]  value  Value to store\n  \\param [in]    ptr  Pointer to location\n  \\return          0  Function succeeded\n  \\return          1  Function failed\n */\n#define     __STLEX                  (uint32_t)__builtin_arm_stlex\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/** @}*/ /* end of group CMSIS_Core_InstructionInterface */\n\n\n/* ###########################  Core Function Access  ########################### */\n/** \\ingroup  CMSIS_Core_FunctionInterface\n    \\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions\n  @{\n */\n\n/**\n  \\brief   Enable IRQ Interrupts\n  \\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __enable_irq(void)\n{\n  __ASM volatile (\"cpsie i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Disable IRQ Interrupts\n  \\details Disables IRQ interrupts by setting special-purpose register PRIMASK.\n           Can only be executed in Privileged modes.\n */\n#ifndef __ARM_COMPAT_H\n__STATIC_FORCEINLINE void __disable_irq(void)\n{\n  __ASM volatile (\"cpsid i\" : : : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Get Control Register\n  \\details Returns the content of the Control Register.\n  \\return               Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_CONTROL(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Control Register (non-secure)\n  \\details Returns the content of the non-secure Control Register when in secure mode.\n  \\return               non-secure Control Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, control_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Control Register\n  \\details Writes the given value to the Control Register.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)\n{\n  __ASM volatile (\"MSR control, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Control Register (non-secure)\n  \\details Writes the given value to the non-secure Control Register when in secure state.\n  \\param [in]    control  Control Register value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)\n{\n  __ASM volatile (\"MSR control_ns, %0\" : : \"r\" (control) : \"memory\");\n  __ISB();\n}\n#endif\n\n\n/**\n  \\brief   Get IPSR Register\n  \\details Returns the content of the IPSR Register.\n  \\return               IPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_IPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, ipsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get APSR Register\n  \\details Returns the content of the APSR Register.\n  \\return               APSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_APSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, apsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get xPSR Register\n  \\details Returns the content of the xPSR Register.\n  \\return               xPSR Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_xPSR(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, xpsr\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Get Process Stack Pointer\n  \\details Returns the current value of the Process Stack Pointer (PSP).\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp\"  : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\return               PSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, psp_ns\"  : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer\n  \\details Assigns the given value to the Process Stack Pointer (PSP).\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp, %0\" : : \"r\" (topOfProcStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state.\n  \\param [in]    topOfProcStack  Process Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack)\n{\n  __ASM volatile (\"MSR psp_ns, %0\" : : \"r\" (topOfProcStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer\n  \\details Returns the current value of the Main Stack Pointer (MSP).\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSP(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Main Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\return               MSP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, msp_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer\n  \\details Assigns the given value to the Main Stack Pointer (MSP).\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp, %0\" : : \"r\" (topOfMainStack) : );\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Main Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state.\n  \\param [in]    topOfMainStack  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack)\n{\n  __ASM volatile (\"MSR msp_ns, %0\" : : \"r\" (topOfMainStack) : );\n}\n#endif\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Stack Pointer (non-secure)\n  \\details Returns the current value of the non-secure Stack Pointer (SP) when in secure state.\n  \\return               SP Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, sp_ns\" : \"=r\" (result) );\n  return(result);\n}\n\n\n/**\n  \\brief   Set Stack Pointer (non-secure)\n  \\details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state.\n  \\param [in]    topOfStack  Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)\n{\n  __ASM volatile (\"MSR sp_ns, %0\" : : \"r\" (topOfStack) : );\n}\n#endif\n\n\n/**\n  \\brief   Get Priority Mask\n  \\details Returns the current state of the priority mask bit from the Priority Mask Register.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Priority Mask (non-secure)\n  \\details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state.\n  \\return               Priority Mask value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, primask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Priority Mask\n  \\details Assigns the given value to the Priority Mask Register.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask, %0\" : : \"r\" (priMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Priority Mask (non-secure)\n  \\details Assigns the given value to the non-secure Priority Mask Register when in secure state.\n  \\param [in]    priMask  Priority Mask\n */\n__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask)\n{\n  __ASM volatile (\"MSR primask_ns, %0\" : : \"r\" (priMask) : \"memory\");\n}\n#endif\n\n\n#if ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n     (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n     (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n/**\n  \\brief   Enable FIQ\n  \\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __enable_fault_irq(void)\n{\n  __ASM volatile (\"cpsie f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Disable FIQ\n  \\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.\n           Can only be executed in Privileged modes.\n */\n__STATIC_FORCEINLINE void __disable_fault_irq(void)\n{\n  __ASM volatile (\"cpsid f\" : : : \"memory\");\n}\n\n\n/**\n  \\brief   Get Base Priority\n  \\details Returns the current value of the Base Priority register.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Base Priority (non-secure)\n  \\details Returns the current value of the non-secure Base Priority register when in secure state.\n  \\return               Base Priority register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, basepri_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority\n  \\details Assigns the given value to the Base Priority register.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Base Priority (non-secure)\n  \\details Assigns the given value to the non-secure Base Priority register when in secure state.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_ns, %0\" : : \"r\" (basePri) : \"memory\");\n}\n#endif\n\n\n/**\n  \\brief   Set Base Priority with condition\n  \\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,\n           or the new value increases the BASEPRI priority level.\n  \\param [in]    basePri  Base Priority value to set\n */\n__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri)\n{\n  __ASM volatile (\"MSR basepri_max, %0\" : : \"r\" (basePri) : \"memory\");\n}\n\n\n/**\n  \\brief   Get Fault Mask\n  \\details Returns the current value of the Fault Mask register.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask\" : \"=r\" (result) );\n  return(result);\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Fault Mask (non-secure)\n  \\details Returns the current value of the non-secure Fault Mask register when in secure state.\n  \\return               Fault Mask register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void)\n{\n  uint32_t result;\n\n  __ASM volatile (\"MRS %0, faultmask_ns\" : \"=r\" (result) );\n  return(result);\n}\n#endif\n\n\n/**\n  \\brief   Set Fault Mask\n  \\details Assigns the given value to the Fault Mask register.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Set Fault Mask (non-secure)\n  \\details Assigns the given value to the non-secure Fault Mask register when in secure state.\n  \\param [in]    faultMask  Fault Mask value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask)\n{\n  __ASM volatile (\"MSR faultmask_ns, %0\" : : \"r\" (faultMask) : \"memory\");\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_7M__       ) && (__ARM_ARCH_7M__        == 1)) || \\\n           (defined (__ARM_ARCH_7EM__      ) && (__ARM_ARCH_7EM__       == 1)) || \\\n           (defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n\n#if ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n     (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n     (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     )\n\n/**\n  \\brief   Get Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the Process Stack Pointer Limit (PSPLIM).\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n    // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n\n#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3))\n/**\n  \\brief   Get Process Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always in non-secure\n  mode.\n\n  \\details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\return               PSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, psplim_ns\"  : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Process Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the Process Stack Pointer Limit (PSPLIM).\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim, %0\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Process Stack Pointer (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored in non-secure\n  mode.\n\n  \\details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state.\n  \\param [in]    ProcStackPtrLimit  Process Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure PSPLIM is RAZ/WI\n  (void)ProcStackPtrLimit;\n#else\n  __ASM volatile (\"MSR psplim_ns, %0\\n\" : : \"r\" (ProcStackPtrLimit));\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Get Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the Main Stack Pointer Limit (MSPLIM).\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim\" : \"=r\" (result) );\n  return result;\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Get Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence zero is returned always.\n\n  \\details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state.\n  \\return               MSPLIM Register value\n */\n__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  return 0U;\n#else\n  uint32_t result;\n  __ASM volatile (\"MRS %0, msplim_ns\" : \"=r\" (result) );\n  return result;\n#endif\n}\n#endif\n\n\n/**\n  \\brief   Set Main Stack Pointer Limit\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the Main Stack Pointer Limit (MSPLIM).\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer Limit value to set\n */\n__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) && \\\n    (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3)))\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n\n\n#if (defined (__ARM_FEATURE_CMSE  ) && (__ARM_FEATURE_CMSE   == 3))\n/**\n  \\brief   Set Main Stack Pointer Limit (non-secure)\n  Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure\n  Stack Pointer Limit register hence the write is silently ignored.\n\n  \\details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state.\n  \\param [in]    MainStackPtrLimit  Main Stack Pointer value to set\n */\n__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit)\n{\n#if (!((defined (__ARM_ARCH_8M_MAIN__   ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n       (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1))   ) )\n  // without main extensions, the non-secure MSPLIM is RAZ/WI\n  (void)MainStackPtrLimit;\n#else\n  __ASM volatile (\"MSR msplim_ns, %0\" : : \"r\" (MainStackPtrLimit));\n#endif\n}\n#endif\n\n#endif /* ((defined (__ARM_ARCH_8M_MAIN__  ) && (__ARM_ARCH_8M_MAIN__   == 1)) || \\\n           (defined (__ARM_ARCH_8M_BASE__  ) && (__ARM_ARCH_8M_BASE__   == 1)) || \\\n           (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1))     ) */\n\n/**\n  \\brief   Get FPSCR\n  \\details Returns the current value of the Floating Point Status/Control register.\n  \\return               Floating Point Status/Control register value\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __get_FPSCR      (uint32_t)__builtin_arm_get_fpscr\n#else\n#define __get_FPSCR()      ((uint32_t)0U)\n#endif\n\n/**\n  \\brief   Set FPSCR\n  \\details Assigns the given value to the Floating Point Status/Control register.\n  \\param [in]    fpscr  Floating Point Status/Control value to set\n */\n#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \\\n     (defined (__FPU_USED   ) && (__FPU_USED    == 1U))     )\n#define __set_FPSCR      __builtin_arm_set_fpscr\n#else\n#define __set_FPSCR(fpscr)      ((void)(fpscr))\n#endif\n\n\n/** @} end of CMSIS_Core_RegAccFunctions */\n\n\n/* ###################  Compiler specific Intrinsics  ########################### */\n/** \\defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics\n  Access to dedicated SIMD instructions\n  @{\n*/\n\n#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1))\n\n#define     __SADD8                 __builtin_arm_sadd8\n#define     __QADD8                 __builtin_arm_qadd8\n#define     __SHADD8                __builtin_arm_shadd8\n#define     __UADD8                 __builtin_arm_uadd8\n#define     __UQADD8                __builtin_arm_uqadd8\n#define     __UHADD8                __builtin_arm_uhadd8\n#define     __SSUB8                 __builtin_arm_ssub8\n#define     __QSUB8                 __builtin_arm_qsub8\n#define     __SHSUB8                __builtin_arm_shsub8\n#define     __USUB8                 __builtin_arm_usub8\n#define     __UQSUB8                __builtin_arm_uqsub8\n#define     __UHSUB8                __builtin_arm_uhsub8\n#define     __SADD16                __builtin_arm_sadd16\n#define     __QADD16                __builtin_arm_qadd16\n#define     __SHADD16               __builtin_arm_shadd16\n#define     __UADD16                __builtin_arm_uadd16\n#define     __UQADD16               __builtin_arm_uqadd16\n#define     __UHADD16               __builtin_arm_uhadd16\n#define     __SSUB16                __builtin_arm_ssub16\n#define     __QSUB16                __builtin_arm_qsub16\n#define     __SHSUB16               __builtin_arm_shsub16\n#define     __USUB16                __builtin_arm_usub16\n#define     __UQSUB16               __builtin_arm_uqsub16\n#define     __UHSUB16               __builtin_arm_uhsub16\n#define     __SASX                  __builtin_arm_sasx\n#define     __QASX                  __builtin_arm_qasx\n#define     __SHASX                 __builtin_arm_shasx\n#define     __UASX                  __builtin_arm_uasx\n#define     __UQASX                 __builtin_arm_uqasx\n#define     __UHASX                 __builtin_arm_uhasx\n#define     __SSAX                  __builtin_arm_ssax\n#define     __QSAX                  __builtin_arm_qsax\n#define     __SHSAX                 __builtin_arm_shsax\n#define     __USAX                  __builtin_arm_usax\n#define     __UQSAX                 __builtin_arm_uqsax\n#define     __UHSAX                 __builtin_arm_uhsax\n#define     __USAD8                 __builtin_arm_usad8\n#define     __USADA8                __builtin_arm_usada8\n#define     __SSAT16                __builtin_arm_ssat16\n#define     __USAT16                __builtin_arm_usat16\n#define     __UXTB16                __builtin_arm_uxtb16\n#define     __UXTAB16               __builtin_arm_uxtab16\n#define     __SXTB16                __builtin_arm_sxtb16\n#define     __SXTAB16               __builtin_arm_sxtab16\n#define     __SMUAD                 __builtin_arm_smuad\n#define     __SMUADX                __builtin_arm_smuadx\n#define     __SMLAD                 __builtin_arm_smlad\n#define     __SMLADX                __builtin_arm_smladx\n#define     __SMLALD                __builtin_arm_smlald\n#define     __SMLALDX               __builtin_arm_smlaldx\n#define     __SMUSD                 __builtin_arm_smusd\n#define     __SMUSDX                __builtin_arm_smusdx\n#define     __SMLSD                 __builtin_arm_smlsd\n#define     __SMLSDX                __builtin_arm_smlsdx\n#define     __SMLSLD                __builtin_arm_smlsld\n#define     __SMLSLDX               __builtin_arm_smlsldx\n#define     __SEL                   __builtin_arm_sel\n#define     __QADD                  __builtin_arm_qadd\n#define     __QSUB                  __builtin_arm_qsub\n\n#define __PKHBT(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0x0000FFFFUL) |  \\\n                                           ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL)  )\n\n#define __PKHTB(ARG1,ARG2,ARG3)          ( ((((uint32_t)(ARG1))          ) & 0xFFFF0000UL) |  \\\n                                           ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL)  )\n\n#define __SXTB16_RORn(ARG1, ARG2)        __SXTB16(__ROR(ARG1, ARG2))\n\n#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))\n\n__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3)\n{\n  int32_t result;\n\n  __ASM volatile (\"smmla %0, %1, %2, %3\" : \"=r\" (result): \"r\"  (op1), \"r\" (op2), \"r\" (op3) );\n  return(result);\n}\n\n#endif /* (__ARM_FEATURE_DSP == 1) */\n/** @} end of group CMSIS_SIMD_intrinsics */\n\n\n#endif /* __CMSIS_TIARMCLANG_H */\n"
  },
  {
    "path": "lib/cmsis_core/cmsis_version.h",
    "content": "/**************************************************************************//**\n * @file     cmsis_version.h\n * @brief    CMSIS Core(M) Version definitions\n * @version  V5.0.5\n * @date     02. February 2022\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2022 ARM Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if   defined ( __ICCARM__ )\n  #pragma system_include         /* treat file as system include file for MISRA check */\n#elif defined (__clang__)\n  #pragma clang system_header   /* treat file as system include file */\n#endif\n\n#ifndef __CMSIS_VERSION_H\n#define __CMSIS_VERSION_H\n\n/*  CMSIS Version definitions */\n#define __CM_CMSIS_VERSION_MAIN  ( 5U)                                      /*!< [31:16] CMSIS Core(M) main version */\n#define __CM_CMSIS_VERSION_SUB   ( 6U)                                      /*!< [15:0]  CMSIS Core(M) sub version */\n#define __CM_CMSIS_VERSION       ((__CM_CMSIS_VERSION_MAIN << 16U) | \\\n                                   __CM_CMSIS_VERSION_SUB           )       /*!< CMSIS Core(M) version number */\n#endif\n"
  },
  {
    "path": "lib/cmsis_core/core_cm4.h",
    "content": "/**************************************************************************//**\n * @file     core_cm4.h\n * @brief    CMSIS Cortex-M4 Core Peripheral Access Layer Header File\n * @version  V5.2.0\n * @date     04. April 2023\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2023 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if   defined ( __ICCARM__ )\n  #pragma system_include         /* treat file as system include file for MISRA check */\n#elif defined (__clang__)\n  #pragma clang system_header   /* treat file as system include file */\n#endif\n\n#ifndef __CORE_CM4_H_GENERIC\n#define __CORE_CM4_H_GENERIC\n\n#include <stdint.h>\n\n#ifdef __cplusplus\n extern \"C\" {\n#endif\n\n/**\n  \\page CMSIS_MISRA_Exceptions  MISRA-C:2004 Compliance Exceptions\n  CMSIS violates the following MISRA-C:2004 rules:\n\n   \\li Required Rule 8.5, object/function definition in header file.<br>\n     Function definitions in header files are used to allow 'inlining'.\n\n   \\li Required Rule 18.4, declaration of union type or object of union type: '{...}'.<br>\n     Unions are used for effective representation of core registers.\n\n   \\li Advisory Rule 19.7, Function-like macro defined.<br>\n     Function-like macros are used to allow more efficient code.\n */\n\n\n/*******************************************************************************\n *                 CMSIS definitions\n ******************************************************************************/\n/**\n  \\ingroup Cortex_M4\n  @{\n */\n\n#include \"cmsis_version.h\"\n\n/* CMSIS CM4 definitions */\n#define __CM4_CMSIS_VERSION_MAIN  (__CM_CMSIS_VERSION_MAIN)              /*!< \\deprecated [31:16] CMSIS HAL main version */\n#define __CM4_CMSIS_VERSION_SUB   (__CM_CMSIS_VERSION_SUB)               /*!< \\deprecated [15:0]  CMSIS HAL sub version */\n#define __CM4_CMSIS_VERSION       ((__CM4_CMSIS_VERSION_MAIN << 16U) | \\\n                                    __CM4_CMSIS_VERSION_SUB           )  /*!< \\deprecated CMSIS HAL version number */\n\n#define __CORTEX_M                (4U)                                   /*!< Cortex-M Core */\n\n/** __FPU_USED indicates whether an FPU is used or not.\n    For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions.\n*/\n#if defined ( __CC_ARM )\n  #if defined __TARGET_FPU_VFP\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)\n  #if defined __ARM_FP\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #warning \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined (__ti__)\n  #if defined (__ARM_FP)\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #warning \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined ( __GNUC__ )\n  #if defined (__VFP_FP__) && !defined(__SOFTFP__)\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined ( __ICCARM__ )\n  #if defined __ARMVFP__\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined ( __TI_ARM__ )\n  #if defined __TI_VFP_SUPPORT__\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined ( __TASKING__ )\n  #if defined __FPU_VFP__\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#elif defined ( __CSMC__ )\n  #if ( __CSMC__ & 0x400U)\n    #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)\n      #define __FPU_USED       1U\n    #else\n      #error \"Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)\"\n      #define __FPU_USED       0U\n    #endif\n  #else\n    #define __FPU_USED         0U\n  #endif\n\n#endif\n\n#include \"cmsis_compiler.h\"               /* CMSIS compiler specific defines */\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CORE_CM4_H_GENERIC */\n\n#ifndef __CMSIS_GENERIC\n\n#ifndef __CORE_CM4_H_DEPENDANT\n#define __CORE_CM4_H_DEPENDANT\n\n#ifdef __cplusplus\n extern \"C\" {\n#endif\n\n/* check device defines and use defaults */\n#if defined __CHECK_DEVICE_DEFINES\n  #ifndef __CM4_REV\n    #define __CM4_REV               0x0000U\n    #warning \"__CM4_REV not defined in device header file; using default!\"\n  #endif\n\n  #ifndef __FPU_PRESENT\n    #define __FPU_PRESENT             0U\n    #warning \"__FPU_PRESENT not defined in device header file; using default!\"\n  #endif\n\n  #ifndef __MPU_PRESENT\n    #define __MPU_PRESENT             0U\n    #warning \"__MPU_PRESENT not defined in device header file; using default!\"\n  #endif\n\n  #ifndef __VTOR_PRESENT\n    #define __VTOR_PRESENT             1U\n    #warning \"__VTOR_PRESENT not defined in device header file; using default!\"\n  #endif\n\n  #ifndef __NVIC_PRIO_BITS\n    #define __NVIC_PRIO_BITS          3U\n    #warning \"__NVIC_PRIO_BITS not defined in device header file; using default!\"\n  #endif\n\n  #ifndef __Vendor_SysTickConfig\n    #define __Vendor_SysTickConfig    0U\n    #warning \"__Vendor_SysTickConfig not defined in device header file; using default!\"\n  #endif\n#endif\n\n/* IO definitions (access restrictions to peripheral registers) */\n/**\n    \\defgroup CMSIS_glob_defs CMSIS Global Defines\n\n    <strong>IO Type Qualifiers</strong> are used\n    \\li to specify the access to peripheral variables.\n    \\li for automatic generation of peripheral register debug information.\n*/\n#ifdef __cplusplus\n  #define   __I     volatile             /*!< Defines 'read only' permissions */\n#else\n  #define   __I     volatile const       /*!< Defines 'read only' permissions */\n#endif\n#define     __O     volatile             /*!< Defines 'write only' permissions */\n#define     __IO    volatile             /*!< Defines 'read / write' permissions */\n\n/* following defines should be used for structure members */\n#define     __IM     volatile const      /*! Defines 'read only' structure member permissions */\n#define     __OM     volatile            /*! Defines 'write only' structure member permissions */\n#define     __IOM    volatile            /*! Defines 'read / write' structure member permissions */\n\n/*@} end of group Cortex_M4 */\n\n\n\n/*******************************************************************************\n *                 Register Abstraction\n  Core Register contain:\n  - Core Register\n  - Core NVIC Register\n  - Core SCB Register\n  - Core SysTick Register\n  - Core Debug Register\n  - Core MPU Register\n  - Core FPU Register\n ******************************************************************************/\n/**\n  \\defgroup CMSIS_core_register Defines and Type Definitions\n  \\brief Type definitions and defines for Cortex-M processor based devices.\n*/\n\n/**\n  \\ingroup    CMSIS_core_register\n  \\defgroup   CMSIS_CORE  Status and Control Registers\n  \\brief      Core Register type definitions.\n  @{\n */\n\n/**\n  \\brief  Union type to access the Application Program Status Register (APSR).\n */\ntypedef union\n{\n  struct\n  {\n    uint32_t _reserved0:16;              /*!< bit:  0..15  Reserved */\n    uint32_t GE:4;                       /*!< bit: 16..19  Greater than or Equal flags */\n    uint32_t _reserved1:7;               /*!< bit: 20..26  Reserved */\n    uint32_t Q:1;                        /*!< bit:     27  Saturation condition flag */\n    uint32_t V:1;                        /*!< bit:     28  Overflow condition code flag */\n    uint32_t C:1;                        /*!< bit:     29  Carry condition code flag */\n    uint32_t Z:1;                        /*!< bit:     30  Zero condition code flag */\n    uint32_t N:1;                        /*!< bit:     31  Negative condition code flag */\n  } b;                                   /*!< Structure used for bit  access */\n  uint32_t w;                            /*!< Type      used for word access */\n} APSR_Type;\n\n/* APSR Register Definitions */\n#define APSR_N_Pos                         31U                                            /*!< APSR: N Position */\n#define APSR_N_Msk                         (1UL << APSR_N_Pos)                            /*!< APSR: N Mask */\n\n#define APSR_Z_Pos                         30U                                            /*!< APSR: Z Position */\n#define APSR_Z_Msk                         (1UL << APSR_Z_Pos)                            /*!< APSR: Z Mask */\n\n#define APSR_C_Pos                         29U                                            /*!< APSR: C Position */\n#define APSR_C_Msk                         (1UL << APSR_C_Pos)                            /*!< APSR: C Mask */\n\n#define APSR_V_Pos                         28U                                            /*!< APSR: V Position */\n#define APSR_V_Msk                         (1UL << APSR_V_Pos)                            /*!< APSR: V Mask */\n\n#define APSR_Q_Pos                         27U                                            /*!< APSR: Q Position */\n#define APSR_Q_Msk                         (1UL << APSR_Q_Pos)                            /*!< APSR: Q Mask */\n\n#define APSR_GE_Pos                        16U                                            /*!< APSR: GE Position */\n#define APSR_GE_Msk                        (0xFUL << APSR_GE_Pos)                         /*!< APSR: GE Mask */\n\n\n/**\n  \\brief  Union type to access the Interrupt Program Status Register (IPSR).\n */\ntypedef union\n{\n  struct\n  {\n    uint32_t ISR:9;                      /*!< bit:  0.. 8  Exception number */\n    uint32_t _reserved0:23;              /*!< bit:  9..31  Reserved */\n  } b;                                   /*!< Structure used for bit  access */\n  uint32_t w;                            /*!< Type      used for word access */\n} IPSR_Type;\n\n/* IPSR Register Definitions */\n#define IPSR_ISR_Pos                        0U                                            /*!< IPSR: ISR Position */\n#define IPSR_ISR_Msk                       (0x1FFUL /*<< IPSR_ISR_Pos*/)                  /*!< IPSR: ISR Mask */\n\n\n/**\n  \\brief  Union type to access the Special-Purpose Program Status Registers (xPSR).\n */\ntypedef union\n{\n  struct\n  {\n    uint32_t ISR:9;                      /*!< bit:  0.. 8  Exception number */\n    uint32_t _reserved0:1;               /*!< bit:      9  Reserved */\n    uint32_t ICI_IT_1:6;                 /*!< bit: 10..15  ICI/IT part 1 */\n    uint32_t GE:4;                       /*!< bit: 16..19  Greater than or Equal flags */\n    uint32_t _reserved1:4;               /*!< bit: 20..23  Reserved */\n    uint32_t T:1;                        /*!< bit:     24  Thumb bit */\n    uint32_t ICI_IT_2:2;                 /*!< bit: 25..26  ICI/IT part 2 */\n    uint32_t Q:1;                        /*!< bit:     27  Saturation condition flag */\n    uint32_t V:1;                        /*!< bit:     28  Overflow condition code flag */\n    uint32_t C:1;                        /*!< bit:     29  Carry condition code flag */\n    uint32_t Z:1;                        /*!< bit:     30  Zero condition code flag */\n    uint32_t N:1;                        /*!< bit:     31  Negative condition code flag */\n  } b;                                   /*!< Structure used for bit  access */\n  uint32_t w;                            /*!< Type      used for word access */\n} xPSR_Type;\n\n/* xPSR Register Definitions */\n#define xPSR_N_Pos                         31U                                            /*!< xPSR: N Position */\n#define xPSR_N_Msk                         (1UL << xPSR_N_Pos)                            /*!< xPSR: N Mask */\n\n#define xPSR_Z_Pos                         30U                                            /*!< xPSR: Z Position */\n#define xPSR_Z_Msk                         (1UL << xPSR_Z_Pos)                            /*!< xPSR: Z Mask */\n\n#define xPSR_C_Pos                         29U                                            /*!< xPSR: C Position */\n#define xPSR_C_Msk                         (1UL << xPSR_C_Pos)                            /*!< xPSR: C Mask */\n\n#define xPSR_V_Pos                         28U                                            /*!< xPSR: V Position */\n#define xPSR_V_Msk                         (1UL << xPSR_V_Pos)                            /*!< xPSR: V Mask */\n\n#define xPSR_Q_Pos                         27U                                            /*!< xPSR: Q Position */\n#define xPSR_Q_Msk                         (1UL << xPSR_Q_Pos)                            /*!< xPSR: Q Mask */\n\n#define xPSR_ICI_IT_2_Pos                  25U                                            /*!< xPSR: ICI/IT part 2 Position */\n#define xPSR_ICI_IT_2_Msk                  (3UL << xPSR_ICI_IT_2_Pos)                     /*!< xPSR: ICI/IT part 2 Mask */\n\n#define xPSR_T_Pos                         24U                                            /*!< xPSR: T Position */\n#define xPSR_T_Msk                         (1UL << xPSR_T_Pos)                            /*!< xPSR: T Mask */\n\n#define xPSR_GE_Pos                        16U                                            /*!< xPSR: GE Position */\n#define xPSR_GE_Msk                        (0xFUL << xPSR_GE_Pos)                         /*!< xPSR: GE Mask */\n\n#define xPSR_ICI_IT_1_Pos                  10U                                            /*!< xPSR: ICI/IT part 1 Position */\n#define xPSR_ICI_IT_1_Msk                  (0x3FUL << xPSR_ICI_IT_1_Pos)                  /*!< xPSR: ICI/IT part 1 Mask */\n\n#define xPSR_ISR_Pos                        0U                                            /*!< xPSR: ISR Position */\n#define xPSR_ISR_Msk                       (0x1FFUL /*<< xPSR_ISR_Pos*/)                  /*!< xPSR: ISR Mask */\n\n\n/**\n  \\brief  Union type to access the Control Registers (CONTROL).\n */\ntypedef union\n{\n  struct\n  {\n    uint32_t nPRIV:1;                    /*!< bit:      0  Execution privilege in Thread mode */\n    uint32_t SPSEL:1;                    /*!< bit:      1  Stack to be used */\n    uint32_t FPCA:1;                     /*!< bit:      2  FP extension active flag */\n    uint32_t _reserved0:29;              /*!< bit:  3..31  Reserved */\n  } b;                                   /*!< Structure used for bit  access */\n  uint32_t w;                            /*!< Type      used for word access */\n} CONTROL_Type;\n\n/* CONTROL Register Definitions */\n#define CONTROL_FPCA_Pos                    2U                                            /*!< CONTROL: FPCA Position */\n#define CONTROL_FPCA_Msk                   (1UL << CONTROL_FPCA_Pos)                      /*!< CONTROL: FPCA Mask */\n\n#define CONTROL_SPSEL_Pos                   1U                                            /*!< CONTROL: SPSEL Position */\n#define CONTROL_SPSEL_Msk                  (1UL << CONTROL_SPSEL_Pos)                     /*!< CONTROL: SPSEL Mask */\n\n#define CONTROL_nPRIV_Pos                   0U                                            /*!< CONTROL: nPRIV Position */\n#define CONTROL_nPRIV_Msk                  (1UL /*<< CONTROL_nPRIV_Pos*/)                 /*!< CONTROL: nPRIV Mask */\n\n/*@} end of group CMSIS_CORE */\n\n\n/**\n  \\ingroup    CMSIS_core_register\n  \\defgroup   CMSIS_NVIC  Nested Vectored Interrupt Controller (NVIC)\n  \\brief      Type definitions for the NVIC Registers\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Nested Vectored Interrupt Controller (NVIC).\n */\ntypedef struct\n{\n  __IOM uint32_t ISER[8U];               /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register */\n        uint32_t RESERVED0[24U];\n  __IOM uint32_t ICER[8U];               /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register */\n        uint32_t RESERVED1[24U];\n  __IOM uint32_t ISPR[8U];               /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register */\n        uint32_t RESERVED2[24U];\n  __IOM uint32_t ICPR[8U];               /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register */\n        uint32_t RESERVED3[24U];\n  __IOM uint32_t IABR[8U];               /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register */\n        uint32_t RESERVED4[56U];\n  __IOM uint8_t  IP[240U];               /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */\n        uint32_t RESERVED5[644U];\n  __OM  uint32_t STIR;                   /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register */\n}  NVIC_Type;\n\n/* Software Triggered Interrupt Register Definitions */\n#define NVIC_STIR_INTID_Pos                 0U                                         /*!< STIR: INTLINESNUM Position */\n#define NVIC_STIR_INTID_Msk                (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/)        /*!< STIR: INTLINESNUM Mask */\n\n/*@} end of group CMSIS_NVIC */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_SCB     System Control Block (SCB)\n  \\brief    Type definitions for the System Control Block Registers\n  @{\n */\n\n/**\n  \\brief  Structure type to access the System Control Block (SCB).\n */\ntypedef struct\n{\n  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */\n  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */\n  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */\n  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */\n  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */\n  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */\n  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */\n  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */\n  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */\n  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */\n  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */\n  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */\n  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */\n  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */\n  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */\n  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */\n  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */\n  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */\n  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */\n        uint32_t RESERVED0[5U];\n  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */\n} SCB_Type;\n\n/* SCB CPUID Register Definitions */\n#define SCB_CPUID_IMPLEMENTER_Pos          24U                                            /*!< SCB CPUID: IMPLEMENTER Position */\n#define SCB_CPUID_IMPLEMENTER_Msk          (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos)          /*!< SCB CPUID: IMPLEMENTER Mask */\n\n#define SCB_CPUID_VARIANT_Pos              20U                                            /*!< SCB CPUID: VARIANT Position */\n#define SCB_CPUID_VARIANT_Msk              (0xFUL << SCB_CPUID_VARIANT_Pos)               /*!< SCB CPUID: VARIANT Mask */\n\n#define SCB_CPUID_ARCHITECTURE_Pos         16U                                            /*!< SCB CPUID: ARCHITECTURE Position */\n#define SCB_CPUID_ARCHITECTURE_Msk         (0xFUL << SCB_CPUID_ARCHITECTURE_Pos)          /*!< SCB CPUID: ARCHITECTURE Mask */\n\n#define SCB_CPUID_PARTNO_Pos                4U                                            /*!< SCB CPUID: PARTNO Position */\n#define SCB_CPUID_PARTNO_Msk               (0xFFFUL << SCB_CPUID_PARTNO_Pos)              /*!< SCB CPUID: PARTNO Mask */\n\n#define SCB_CPUID_REVISION_Pos              0U                                            /*!< SCB CPUID: REVISION Position */\n#define SCB_CPUID_REVISION_Msk             (0xFUL /*<< SCB_CPUID_REVISION_Pos*/)          /*!< SCB CPUID: REVISION Mask */\n\n/* SCB Interrupt Control State Register Definitions */\n#define SCB_ICSR_NMIPENDSET_Pos            31U                                            /*!< SCB ICSR: NMIPENDSET Position */\n#define SCB_ICSR_NMIPENDSET_Msk            (1UL << SCB_ICSR_NMIPENDSET_Pos)               /*!< SCB ICSR: NMIPENDSET Mask */\n\n#define SCB_ICSR_PENDSVSET_Pos             28U                                            /*!< SCB ICSR: PENDSVSET Position */\n#define SCB_ICSR_PENDSVSET_Msk             (1UL << SCB_ICSR_PENDSVSET_Pos)                /*!< SCB ICSR: PENDSVSET Mask */\n\n#define SCB_ICSR_PENDSVCLR_Pos             27U                                            /*!< SCB ICSR: PENDSVCLR Position */\n#define SCB_ICSR_PENDSVCLR_Msk             (1UL << SCB_ICSR_PENDSVCLR_Pos)                /*!< SCB ICSR: PENDSVCLR Mask */\n\n#define SCB_ICSR_PENDSTSET_Pos             26U                                            /*!< SCB ICSR: PENDSTSET Position */\n#define SCB_ICSR_PENDSTSET_Msk             (1UL << SCB_ICSR_PENDSTSET_Pos)                /*!< SCB ICSR: PENDSTSET Mask */\n\n#define SCB_ICSR_PENDSTCLR_Pos             25U                                            /*!< SCB ICSR: PENDSTCLR Position */\n#define SCB_ICSR_PENDSTCLR_Msk             (1UL << SCB_ICSR_PENDSTCLR_Pos)                /*!< SCB ICSR: PENDSTCLR Mask */\n\n#define SCB_ICSR_ISRPREEMPT_Pos            23U                                            /*!< SCB ICSR: ISRPREEMPT Position */\n#define SCB_ICSR_ISRPREEMPT_Msk            (1UL << SCB_ICSR_ISRPREEMPT_Pos)               /*!< SCB ICSR: ISRPREEMPT Mask */\n\n#define SCB_ICSR_ISRPENDING_Pos            22U                                            /*!< SCB ICSR: ISRPENDING Position */\n#define SCB_ICSR_ISRPENDING_Msk            (1UL << SCB_ICSR_ISRPENDING_Pos)               /*!< SCB ICSR: ISRPENDING Mask */\n\n#define SCB_ICSR_VECTPENDING_Pos           12U                                            /*!< SCB ICSR: VECTPENDING Position */\n#define SCB_ICSR_VECTPENDING_Msk           (0x1FFUL << SCB_ICSR_VECTPENDING_Pos)          /*!< SCB ICSR: VECTPENDING Mask */\n\n#define SCB_ICSR_RETTOBASE_Pos             11U                                            /*!< SCB ICSR: RETTOBASE Position */\n#define SCB_ICSR_RETTOBASE_Msk             (1UL << SCB_ICSR_RETTOBASE_Pos)                /*!< SCB ICSR: RETTOBASE Mask */\n\n#define SCB_ICSR_VECTACTIVE_Pos             0U                                            /*!< SCB ICSR: VECTACTIVE Position */\n#define SCB_ICSR_VECTACTIVE_Msk            (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/)       /*!< SCB ICSR: VECTACTIVE Mask */\n\n/* SCB Vector Table Offset Register Definitions */\n#define SCB_VTOR_TBLOFF_Pos                 7U                                            /*!< SCB VTOR: TBLOFF Position */\n#define SCB_VTOR_TBLOFF_Msk                (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos)           /*!< SCB VTOR: TBLOFF Mask */\n\n/* SCB Application Interrupt and Reset Control Register Definitions */\n#define SCB_AIRCR_VECTKEY_Pos              16U                                            /*!< SCB AIRCR: VECTKEY Position */\n#define SCB_AIRCR_VECTKEY_Msk              (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos)            /*!< SCB AIRCR: VECTKEY Mask */\n\n#define SCB_AIRCR_VECTKEYSTAT_Pos          16U                                            /*!< SCB AIRCR: VECTKEYSTAT Position */\n#define SCB_AIRCR_VECTKEYSTAT_Msk          (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos)        /*!< SCB AIRCR: VECTKEYSTAT Mask */\n\n#define SCB_AIRCR_ENDIANESS_Pos            15U                                            /*!< SCB AIRCR: ENDIANESS Position */\n#define SCB_AIRCR_ENDIANESS_Msk            (1UL << SCB_AIRCR_ENDIANESS_Pos)               /*!< SCB AIRCR: ENDIANESS Mask */\n\n#define SCB_AIRCR_PRIGROUP_Pos              8U                                            /*!< SCB AIRCR: PRIGROUP Position */\n#define SCB_AIRCR_PRIGROUP_Msk             (7UL << SCB_AIRCR_PRIGROUP_Pos)                /*!< SCB AIRCR: PRIGROUP Mask */\n\n#define SCB_AIRCR_SYSRESETREQ_Pos           2U                                            /*!< SCB AIRCR: SYSRESETREQ Position */\n#define SCB_AIRCR_SYSRESETREQ_Msk          (1UL << SCB_AIRCR_SYSRESETREQ_Pos)             /*!< SCB AIRCR: SYSRESETREQ Mask */\n\n#define SCB_AIRCR_VECTCLRACTIVE_Pos         1U                                            /*!< SCB AIRCR: VECTCLRACTIVE Position */\n#define SCB_AIRCR_VECTCLRACTIVE_Msk        (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos)           /*!< SCB AIRCR: VECTCLRACTIVE Mask */\n\n#define SCB_AIRCR_VECTRESET_Pos             0U                                            /*!< SCB AIRCR: VECTRESET Position */\n#define SCB_AIRCR_VECTRESET_Msk            (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/)           /*!< SCB AIRCR: VECTRESET Mask */\n\n/* SCB System Control Register Definitions */\n#define SCB_SCR_SEVONPEND_Pos               4U                                            /*!< SCB SCR: SEVONPEND Position */\n#define SCB_SCR_SEVONPEND_Msk              (1UL << SCB_SCR_SEVONPEND_Pos)                 /*!< SCB SCR: SEVONPEND Mask */\n\n#define SCB_SCR_SLEEPDEEP_Pos               2U                                            /*!< SCB SCR: SLEEPDEEP Position */\n#define SCB_SCR_SLEEPDEEP_Msk              (1UL << SCB_SCR_SLEEPDEEP_Pos)                 /*!< SCB SCR: SLEEPDEEP Mask */\n\n#define SCB_SCR_SLEEPONEXIT_Pos             1U                                            /*!< SCB SCR: SLEEPONEXIT Position */\n#define SCB_SCR_SLEEPONEXIT_Msk            (1UL << SCB_SCR_SLEEPONEXIT_Pos)               /*!< SCB SCR: SLEEPONEXIT Mask */\n\n/* SCB Configuration Control Register Definitions */\n#define SCB_CCR_STKALIGN_Pos                9U                                            /*!< SCB CCR: STKALIGN Position */\n#define SCB_CCR_STKALIGN_Msk               (1UL << SCB_CCR_STKALIGN_Pos)                  /*!< SCB CCR: STKALIGN Mask */\n\n#define SCB_CCR_BFHFNMIGN_Pos               8U                                            /*!< SCB CCR: BFHFNMIGN Position */\n#define SCB_CCR_BFHFNMIGN_Msk              (1UL << SCB_CCR_BFHFNMIGN_Pos)                 /*!< SCB CCR: BFHFNMIGN Mask */\n\n#define SCB_CCR_DIV_0_TRP_Pos               4U                                            /*!< SCB CCR: DIV_0_TRP Position */\n#define SCB_CCR_DIV_0_TRP_Msk              (1UL << SCB_CCR_DIV_0_TRP_Pos)                 /*!< SCB CCR: DIV_0_TRP Mask */\n\n#define SCB_CCR_UNALIGN_TRP_Pos             3U                                            /*!< SCB CCR: UNALIGN_TRP Position */\n#define SCB_CCR_UNALIGN_TRP_Msk            (1UL << SCB_CCR_UNALIGN_TRP_Pos)               /*!< SCB CCR: UNALIGN_TRP Mask */\n\n#define SCB_CCR_USERSETMPEND_Pos            1U                                            /*!< SCB CCR: USERSETMPEND Position */\n#define SCB_CCR_USERSETMPEND_Msk           (1UL << SCB_CCR_USERSETMPEND_Pos)              /*!< SCB CCR: USERSETMPEND Mask */\n\n#define SCB_CCR_NONBASETHRDENA_Pos          0U                                            /*!< SCB CCR: NONBASETHRDENA Position */\n#define SCB_CCR_NONBASETHRDENA_Msk         (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/)        /*!< SCB CCR: NONBASETHRDENA Mask */\n\n/* SCB System Handler Control and State Register Definitions */\n#define SCB_SHCSR_USGFAULTENA_Pos          18U                                            /*!< SCB SHCSR: USGFAULTENA Position */\n#define SCB_SHCSR_USGFAULTENA_Msk          (1UL << SCB_SHCSR_USGFAULTENA_Pos)             /*!< SCB SHCSR: USGFAULTENA Mask */\n\n#define SCB_SHCSR_BUSFAULTENA_Pos          17U                                            /*!< SCB SHCSR: BUSFAULTENA Position */\n#define SCB_SHCSR_BUSFAULTENA_Msk          (1UL << SCB_SHCSR_BUSFAULTENA_Pos)             /*!< SCB SHCSR: BUSFAULTENA Mask */\n\n#define SCB_SHCSR_MEMFAULTENA_Pos          16U                                            /*!< SCB SHCSR: MEMFAULTENA Position */\n#define SCB_SHCSR_MEMFAULTENA_Msk          (1UL << SCB_SHCSR_MEMFAULTENA_Pos)             /*!< SCB SHCSR: MEMFAULTENA Mask */\n\n#define SCB_SHCSR_SVCALLPENDED_Pos         15U                                            /*!< SCB SHCSR: SVCALLPENDED Position */\n#define SCB_SHCSR_SVCALLPENDED_Msk         (1UL << SCB_SHCSR_SVCALLPENDED_Pos)            /*!< SCB SHCSR: SVCALLPENDED Mask */\n\n#define SCB_SHCSR_BUSFAULTPENDED_Pos       14U                                            /*!< SCB SHCSR: BUSFAULTPENDED Position */\n#define SCB_SHCSR_BUSFAULTPENDED_Msk       (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos)          /*!< SCB SHCSR: BUSFAULTPENDED Mask */\n\n#define SCB_SHCSR_MEMFAULTPENDED_Pos       13U                                            /*!< SCB SHCSR: MEMFAULTPENDED Position */\n#define SCB_SHCSR_MEMFAULTPENDED_Msk       (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos)          /*!< SCB SHCSR: MEMFAULTPENDED Mask */\n\n#define SCB_SHCSR_USGFAULTPENDED_Pos       12U                                            /*!< SCB SHCSR: USGFAULTPENDED Position */\n#define SCB_SHCSR_USGFAULTPENDED_Msk       (1UL << SCB_SHCSR_USGFAULTPENDED_Pos)          /*!< SCB SHCSR: USGFAULTPENDED Mask */\n\n#define SCB_SHCSR_SYSTICKACT_Pos           11U                                            /*!< SCB SHCSR: SYSTICKACT Position */\n#define SCB_SHCSR_SYSTICKACT_Msk           (1UL << SCB_SHCSR_SYSTICKACT_Pos)              /*!< SCB SHCSR: SYSTICKACT Mask */\n\n#define SCB_SHCSR_PENDSVACT_Pos            10U                                            /*!< SCB SHCSR: PENDSVACT Position */\n#define SCB_SHCSR_PENDSVACT_Msk            (1UL << SCB_SHCSR_PENDSVACT_Pos)               /*!< SCB SHCSR: PENDSVACT Mask */\n\n#define SCB_SHCSR_MONITORACT_Pos            8U                                            /*!< SCB SHCSR: MONITORACT Position */\n#define SCB_SHCSR_MONITORACT_Msk           (1UL << SCB_SHCSR_MONITORACT_Pos)              /*!< SCB SHCSR: MONITORACT Mask */\n\n#define SCB_SHCSR_SVCALLACT_Pos             7U                                            /*!< SCB SHCSR: SVCALLACT Position */\n#define SCB_SHCSR_SVCALLACT_Msk            (1UL << SCB_SHCSR_SVCALLACT_Pos)               /*!< SCB SHCSR: SVCALLACT Mask */\n\n#define SCB_SHCSR_USGFAULTACT_Pos           3U                                            /*!< SCB SHCSR: USGFAULTACT Position */\n#define SCB_SHCSR_USGFAULTACT_Msk          (1UL << SCB_SHCSR_USGFAULTACT_Pos)             /*!< SCB SHCSR: USGFAULTACT Mask */\n\n#define SCB_SHCSR_BUSFAULTACT_Pos           1U                                            /*!< SCB SHCSR: BUSFAULTACT Position */\n#define SCB_SHCSR_BUSFAULTACT_Msk          (1UL << SCB_SHCSR_BUSFAULTACT_Pos)             /*!< SCB SHCSR: BUSFAULTACT Mask */\n\n#define SCB_SHCSR_MEMFAULTACT_Pos           0U                                            /*!< SCB SHCSR: MEMFAULTACT Position */\n#define SCB_SHCSR_MEMFAULTACT_Msk          (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/)         /*!< SCB SHCSR: MEMFAULTACT Mask */\n\n/* SCB Configurable Fault Status Register Definitions */\n#define SCB_CFSR_USGFAULTSR_Pos            16U                                            /*!< SCB CFSR: Usage Fault Status Register Position */\n#define SCB_CFSR_USGFAULTSR_Msk            (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos)          /*!< SCB CFSR: Usage Fault Status Register Mask */\n\n#define SCB_CFSR_BUSFAULTSR_Pos             8U                                            /*!< SCB CFSR: Bus Fault Status Register Position */\n#define SCB_CFSR_BUSFAULTSR_Msk            (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos)            /*!< SCB CFSR: Bus Fault Status Register Mask */\n\n#define SCB_CFSR_MEMFAULTSR_Pos             0U                                            /*!< SCB CFSR: Memory Manage Fault Status Register Position */\n#define SCB_CFSR_MEMFAULTSR_Msk            (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/)        /*!< SCB CFSR: Memory Manage Fault Status Register Mask */\n\n/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */\n#define SCB_CFSR_MMARVALID_Pos             (SCB_CFSR_MEMFAULTSR_Pos + 7U)                 /*!< SCB CFSR (MMFSR): MMARVALID Position */\n#define SCB_CFSR_MMARVALID_Msk             (1UL << SCB_CFSR_MMARVALID_Pos)                /*!< SCB CFSR (MMFSR): MMARVALID Mask */\n\n#define SCB_CFSR_MLSPERR_Pos               (SCB_CFSR_MEMFAULTSR_Pos + 5U)                 /*!< SCB CFSR (MMFSR): MLSPERR Position */\n#define SCB_CFSR_MLSPERR_Msk               (1UL << SCB_CFSR_MLSPERR_Pos)                  /*!< SCB CFSR (MMFSR): MLSPERR Mask */\n\n#define SCB_CFSR_MSTKERR_Pos               (SCB_CFSR_MEMFAULTSR_Pos + 4U)                 /*!< SCB CFSR (MMFSR): MSTKERR Position */\n#define SCB_CFSR_MSTKERR_Msk               (1UL << SCB_CFSR_MSTKERR_Pos)                  /*!< SCB CFSR (MMFSR): MSTKERR Mask */\n\n#define SCB_CFSR_MUNSTKERR_Pos             (SCB_CFSR_MEMFAULTSR_Pos + 3U)                 /*!< SCB CFSR (MMFSR): MUNSTKERR Position */\n#define SCB_CFSR_MUNSTKERR_Msk             (1UL << SCB_CFSR_MUNSTKERR_Pos)                /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */\n\n#define SCB_CFSR_DACCVIOL_Pos              (SCB_CFSR_MEMFAULTSR_Pos + 1U)                 /*!< SCB CFSR (MMFSR): DACCVIOL Position */\n#define SCB_CFSR_DACCVIOL_Msk              (1UL << SCB_CFSR_DACCVIOL_Pos)                 /*!< SCB CFSR (MMFSR): DACCVIOL Mask */\n\n#define SCB_CFSR_IACCVIOL_Pos              (SCB_CFSR_MEMFAULTSR_Pos + 0U)                 /*!< SCB CFSR (MMFSR): IACCVIOL Position */\n#define SCB_CFSR_IACCVIOL_Msk              (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/)             /*!< SCB CFSR (MMFSR): IACCVIOL Mask */\n\n/* BusFault Status Register (part of SCB Configurable Fault Status Register) */\n#define SCB_CFSR_BFARVALID_Pos            (SCB_CFSR_BUSFAULTSR_Pos + 7U)                  /*!< SCB CFSR (BFSR): BFARVALID Position */\n#define SCB_CFSR_BFARVALID_Msk            (1UL << SCB_CFSR_BFARVALID_Pos)                 /*!< SCB CFSR (BFSR): BFARVALID Mask */\n\n#define SCB_CFSR_LSPERR_Pos               (SCB_CFSR_BUSFAULTSR_Pos + 5U)                  /*!< SCB CFSR (BFSR): LSPERR Position */\n#define SCB_CFSR_LSPERR_Msk               (1UL << SCB_CFSR_LSPERR_Pos)                    /*!< SCB CFSR (BFSR): LSPERR Mask */\n\n#define SCB_CFSR_STKERR_Pos               (SCB_CFSR_BUSFAULTSR_Pos + 4U)                  /*!< SCB CFSR (BFSR): STKERR Position */\n#define SCB_CFSR_STKERR_Msk               (1UL << SCB_CFSR_STKERR_Pos)                    /*!< SCB CFSR (BFSR): STKERR Mask */\n\n#define SCB_CFSR_UNSTKERR_Pos             (SCB_CFSR_BUSFAULTSR_Pos + 3U)                  /*!< SCB CFSR (BFSR): UNSTKERR Position */\n#define SCB_CFSR_UNSTKERR_Msk             (1UL << SCB_CFSR_UNSTKERR_Pos)                  /*!< SCB CFSR (BFSR): UNSTKERR Mask */\n\n#define SCB_CFSR_IMPRECISERR_Pos          (SCB_CFSR_BUSFAULTSR_Pos + 2U)                  /*!< SCB CFSR (BFSR): IMPRECISERR Position */\n#define SCB_CFSR_IMPRECISERR_Msk          (1UL << SCB_CFSR_IMPRECISERR_Pos)               /*!< SCB CFSR (BFSR): IMPRECISERR Mask */\n\n#define SCB_CFSR_PRECISERR_Pos            (SCB_CFSR_BUSFAULTSR_Pos + 1U)                  /*!< SCB CFSR (BFSR): PRECISERR Position */\n#define SCB_CFSR_PRECISERR_Msk            (1UL << SCB_CFSR_PRECISERR_Pos)                 /*!< SCB CFSR (BFSR): PRECISERR Mask */\n\n#define SCB_CFSR_IBUSERR_Pos              (SCB_CFSR_BUSFAULTSR_Pos + 0U)                  /*!< SCB CFSR (BFSR): IBUSERR Position */\n#define SCB_CFSR_IBUSERR_Msk              (1UL << SCB_CFSR_IBUSERR_Pos)                   /*!< SCB CFSR (BFSR): IBUSERR Mask */\n\n/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */\n#define SCB_CFSR_DIVBYZERO_Pos            (SCB_CFSR_USGFAULTSR_Pos + 9U)                  /*!< SCB CFSR (UFSR): DIVBYZERO Position */\n#define SCB_CFSR_DIVBYZERO_Msk            (1UL << SCB_CFSR_DIVBYZERO_Pos)                 /*!< SCB CFSR (UFSR): DIVBYZERO Mask */\n\n#define SCB_CFSR_UNALIGNED_Pos            (SCB_CFSR_USGFAULTSR_Pos + 8U)                  /*!< SCB CFSR (UFSR): UNALIGNED Position */\n#define SCB_CFSR_UNALIGNED_Msk            (1UL << SCB_CFSR_UNALIGNED_Pos)                 /*!< SCB CFSR (UFSR): UNALIGNED Mask */\n\n#define SCB_CFSR_NOCP_Pos                 (SCB_CFSR_USGFAULTSR_Pos + 3U)                  /*!< SCB CFSR (UFSR): NOCP Position */\n#define SCB_CFSR_NOCP_Msk                 (1UL << SCB_CFSR_NOCP_Pos)                      /*!< SCB CFSR (UFSR): NOCP Mask */\n\n#define SCB_CFSR_INVPC_Pos                (SCB_CFSR_USGFAULTSR_Pos + 2U)                  /*!< SCB CFSR (UFSR): INVPC Position */\n#define SCB_CFSR_INVPC_Msk                (1UL << SCB_CFSR_INVPC_Pos)                     /*!< SCB CFSR (UFSR): INVPC Mask */\n\n#define SCB_CFSR_INVSTATE_Pos             (SCB_CFSR_USGFAULTSR_Pos + 1U)                  /*!< SCB CFSR (UFSR): INVSTATE Position */\n#define SCB_CFSR_INVSTATE_Msk             (1UL << SCB_CFSR_INVSTATE_Pos)                  /*!< SCB CFSR (UFSR): INVSTATE Mask */\n\n#define SCB_CFSR_UNDEFINSTR_Pos           (SCB_CFSR_USGFAULTSR_Pos + 0U)                  /*!< SCB CFSR (UFSR): UNDEFINSTR Position */\n#define SCB_CFSR_UNDEFINSTR_Msk           (1UL << SCB_CFSR_UNDEFINSTR_Pos)                /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */\n\n/* SCB Hard Fault Status Register Definitions */\n#define SCB_HFSR_DEBUGEVT_Pos              31U                                            /*!< SCB HFSR: DEBUGEVT Position */\n#define SCB_HFSR_DEBUGEVT_Msk              (1UL << SCB_HFSR_DEBUGEVT_Pos)                 /*!< SCB HFSR: DEBUGEVT Mask */\n\n#define SCB_HFSR_FORCED_Pos                30U                                            /*!< SCB HFSR: FORCED Position */\n#define SCB_HFSR_FORCED_Msk                (1UL << SCB_HFSR_FORCED_Pos)                   /*!< SCB HFSR: FORCED Mask */\n\n#define SCB_HFSR_VECTTBL_Pos                1U                                            /*!< SCB HFSR: VECTTBL Position */\n#define SCB_HFSR_VECTTBL_Msk               (1UL << SCB_HFSR_VECTTBL_Pos)                  /*!< SCB HFSR: VECTTBL Mask */\n\n/* SCB Debug Fault Status Register Definitions */\n#define SCB_DFSR_EXTERNAL_Pos               4U                                            /*!< SCB DFSR: EXTERNAL Position */\n#define SCB_DFSR_EXTERNAL_Msk              (1UL << SCB_DFSR_EXTERNAL_Pos)                 /*!< SCB DFSR: EXTERNAL Mask */\n\n#define SCB_DFSR_VCATCH_Pos                 3U                                            /*!< SCB DFSR: VCATCH Position */\n#define SCB_DFSR_VCATCH_Msk                (1UL << SCB_DFSR_VCATCH_Pos)                   /*!< SCB DFSR: VCATCH Mask */\n\n#define SCB_DFSR_DWTTRAP_Pos                2U                                            /*!< SCB DFSR: DWTTRAP Position */\n#define SCB_DFSR_DWTTRAP_Msk               (1UL << SCB_DFSR_DWTTRAP_Pos)                  /*!< SCB DFSR: DWTTRAP Mask */\n\n#define SCB_DFSR_BKPT_Pos                   1U                                            /*!< SCB DFSR: BKPT Position */\n#define SCB_DFSR_BKPT_Msk                  (1UL << SCB_DFSR_BKPT_Pos)                     /*!< SCB DFSR: BKPT Mask */\n\n#define SCB_DFSR_HALTED_Pos                 0U                                            /*!< SCB DFSR: HALTED Position */\n#define SCB_DFSR_HALTED_Msk                (1UL /*<< SCB_DFSR_HALTED_Pos*/)               /*!< SCB DFSR: HALTED Mask */\n\n/*@} end of group CMSIS_SCB */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB)\n  \\brief    Type definitions for the System Control and ID Register not in the SCB\n  @{\n */\n\n/**\n  \\brief  Structure type to access the System Control and ID Register not in the SCB.\n */\ntypedef struct\n{\n        uint32_t RESERVED0[1U];\n  __IM  uint32_t ICTR;                   /*!< Offset: 0x004 (R/ )  Interrupt Controller Type Register */\n  __IOM uint32_t ACTLR;                  /*!< Offset: 0x008 (R/W)  Auxiliary Control Register */\n} SCnSCB_Type;\n\n/* Interrupt Controller Type Register Definitions */\n#define SCnSCB_ICTR_INTLINESNUM_Pos         0U                                         /*!< ICTR: INTLINESNUM Position */\n#define SCnSCB_ICTR_INTLINESNUM_Msk        (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/)  /*!< ICTR: INTLINESNUM Mask */\n\n/* Auxiliary Control Register Definitions */\n#define SCnSCB_ACTLR_DISOOFP_Pos            9U                                         /*!< ACTLR: DISOOFP Position */\n#define SCnSCB_ACTLR_DISOOFP_Msk           (1UL << SCnSCB_ACTLR_DISOOFP_Pos)           /*!< ACTLR: DISOOFP Mask */\n\n#define SCnSCB_ACTLR_DISFPCA_Pos            8U                                         /*!< ACTLR: DISFPCA Position */\n#define SCnSCB_ACTLR_DISFPCA_Msk           (1UL << SCnSCB_ACTLR_DISFPCA_Pos)           /*!< ACTLR: DISFPCA Mask */\n\n#define SCnSCB_ACTLR_DISFOLD_Pos            2U                                         /*!< ACTLR: DISFOLD Position */\n#define SCnSCB_ACTLR_DISFOLD_Msk           (1UL << SCnSCB_ACTLR_DISFOLD_Pos)           /*!< ACTLR: DISFOLD Mask */\n\n#define SCnSCB_ACTLR_DISDEFWBUF_Pos         1U                                         /*!< ACTLR: DISDEFWBUF Position */\n#define SCnSCB_ACTLR_DISDEFWBUF_Msk        (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos)        /*!< ACTLR: DISDEFWBUF Mask */\n\n#define SCnSCB_ACTLR_DISMCYCINT_Pos         0U                                         /*!< ACTLR: DISMCYCINT Position */\n#define SCnSCB_ACTLR_DISMCYCINT_Msk        (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/)    /*!< ACTLR: DISMCYCINT Mask */\n\n/*@} end of group CMSIS_SCnotSCB */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_SysTick     System Tick Timer (SysTick)\n  \\brief    Type definitions for the System Timer Registers.\n  @{\n */\n\n/**\n  \\brief  Structure type to access the System Timer (SysTick).\n */\ntypedef struct\n{\n  __IOM uint32_t CTRL;                   /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */\n  __IOM uint32_t LOAD;                   /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register */\n  __IOM uint32_t VAL;                    /*!< Offset: 0x008 (R/W)  SysTick Current Value Register */\n  __IM  uint32_t CALIB;                  /*!< Offset: 0x00C (R/ )  SysTick Calibration Register */\n} SysTick_Type;\n\n/* SysTick Control / Status Register Definitions */\n#define SysTick_CTRL_COUNTFLAG_Pos         16U                                            /*!< SysTick CTRL: COUNTFLAG Position */\n#define SysTick_CTRL_COUNTFLAG_Msk         (1UL << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: COUNTFLAG Mask */\n\n#define SysTick_CTRL_CLKSOURCE_Pos          2U                                            /*!< SysTick CTRL: CLKSOURCE Position */\n#define SysTick_CTRL_CLKSOURCE_Msk         (1UL << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */\n\n#define SysTick_CTRL_TICKINT_Pos            1U                                            /*!< SysTick CTRL: TICKINT Position */\n#define SysTick_CTRL_TICKINT_Msk           (1UL << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */\n\n#define SysTick_CTRL_ENABLE_Pos             0U                                            /*!< SysTick CTRL: ENABLE Position */\n#define SysTick_CTRL_ENABLE_Msk            (1UL /*<< SysTick_CTRL_ENABLE_Pos*/)           /*!< SysTick CTRL: ENABLE Mask */\n\n/* SysTick Reload Register Definitions */\n#define SysTick_LOAD_RELOAD_Pos             0U                                            /*!< SysTick LOAD: RELOAD Position */\n#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/)    /*!< SysTick LOAD: RELOAD Mask */\n\n/* SysTick Current Register Definitions */\n#define SysTick_VAL_CURRENT_Pos             0U                                            /*!< SysTick VAL: CURRENT Position */\n#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/)    /*!< SysTick VAL: CURRENT Mask */\n\n/* SysTick Calibration Register Definitions */\n#define SysTick_CALIB_NOREF_Pos            31U                                            /*!< SysTick CALIB: NOREF Position */\n#define SysTick_CALIB_NOREF_Msk            (1UL << SysTick_CALIB_NOREF_Pos)               /*!< SysTick CALIB: NOREF Mask */\n\n#define SysTick_CALIB_SKEW_Pos             30U                                            /*!< SysTick CALIB: SKEW Position */\n#define SysTick_CALIB_SKEW_Msk             (1UL << SysTick_CALIB_SKEW_Pos)                /*!< SysTick CALIB: SKEW Mask */\n\n#define SysTick_CALIB_TENMS_Pos             0U                                            /*!< SysTick CALIB: TENMS Position */\n#define SysTick_CALIB_TENMS_Msk            (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/)    /*!< SysTick CALIB: TENMS Mask */\n\n/*@} end of group CMSIS_SysTick */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_ITM     Instrumentation Trace Macrocell (ITM)\n  \\brief    Type definitions for the Instrumentation Trace Macrocell (ITM)\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Instrumentation Trace Macrocell Register (ITM).\n */\ntypedef struct\n{\n  __OM  union\n  {\n    __OM  uint8_t    u8;                 /*!< Offset: 0x000 ( /W)  ITM Stimulus Port 8-bit */\n    __OM  uint16_t   u16;                /*!< Offset: 0x000 ( /W)  ITM Stimulus Port 16-bit */\n    __OM  uint32_t   u32;                /*!< Offset: 0x000 ( /W)  ITM Stimulus Port 32-bit */\n  }  PORT [32U];                         /*!< Offset: 0x000 ( /W)  ITM Stimulus Port Registers */\n        uint32_t RESERVED0[864U];\n  __IOM uint32_t TER;                    /*!< Offset: 0xE00 (R/W)  ITM Trace Enable Register */\n        uint32_t RESERVED1[15U];\n  __IOM uint32_t TPR;                    /*!< Offset: 0xE40 (R/W)  ITM Trace Privilege Register */\n        uint32_t RESERVED2[15U];\n  __IOM uint32_t TCR;                    /*!< Offset: 0xE80 (R/W)  ITM Trace Control Register */\n        uint32_t RESERVED3[32U];\n        uint32_t RESERVED4[43U];\n  __OM  uint32_t LAR;                    /*!< Offset: 0xFB0 ( /W)  ITM Lock Access Register */\n  __IM  uint32_t LSR;                    /*!< Offset: 0xFB4 (R/ )  ITM Lock Status Register */\n        uint32_t RESERVED5[6U];\n  __IM  uint32_t PID4;                   /*!< Offset: 0xFD0 (R/ )  ITM Peripheral Identification Register #4 */\n  __IM  uint32_t PID5;                   /*!< Offset: 0xFD4 (R/ )  ITM Peripheral Identification Register #5 */\n  __IM  uint32_t PID6;                   /*!< Offset: 0xFD8 (R/ )  ITM Peripheral Identification Register #6 */\n  __IM  uint32_t PID7;                   /*!< Offset: 0xFDC (R/ )  ITM Peripheral Identification Register #7 */\n  __IM  uint32_t PID0;                   /*!< Offset: 0xFE0 (R/ )  ITM Peripheral Identification Register #0 */\n  __IM  uint32_t PID1;                   /*!< Offset: 0xFE4 (R/ )  ITM Peripheral Identification Register #1 */\n  __IM  uint32_t PID2;                   /*!< Offset: 0xFE8 (R/ )  ITM Peripheral Identification Register #2 */\n  __IM  uint32_t PID3;                   /*!< Offset: 0xFEC (R/ )  ITM Peripheral Identification Register #3 */\n  __IM  uint32_t CID0;                   /*!< Offset: 0xFF0 (R/ )  ITM Component  Identification Register #0 */\n  __IM  uint32_t CID1;                   /*!< Offset: 0xFF4 (R/ )  ITM Component  Identification Register #1 */\n  __IM  uint32_t CID2;                   /*!< Offset: 0xFF8 (R/ )  ITM Component  Identification Register #2 */\n  __IM  uint32_t CID3;                   /*!< Offset: 0xFFC (R/ )  ITM Component  Identification Register #3 */\n} ITM_Type;\n\n/* ITM Trace Privilege Register Definitions */\n#define ITM_TPR_PRIVMASK_Pos                0U                                            /*!< ITM TPR: PRIVMASK Position */\n#define ITM_TPR_PRIVMASK_Msk               (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/)     /*!< ITM TPR: PRIVMASK Mask */\n\n/* ITM Trace Control Register Definitions */\n#define ITM_TCR_BUSY_Pos                   23U                                            /*!< ITM TCR: BUSY Position */\n#define ITM_TCR_BUSY_Msk                   (1UL << ITM_TCR_BUSY_Pos)                      /*!< ITM TCR: BUSY Mask */\n\n#define ITM_TCR_TRACEBUSID_Pos             16U                                            /*!< ITM TCR: ATBID Position */\n#define ITM_TCR_TRACEBUSID_Msk             (0x7FUL << ITM_TCR_TRACEBUSID_Pos)             /*!< ITM TCR: ATBID Mask */\n\n#define ITM_TCR_GTSFREQ_Pos                10U                                            /*!< ITM TCR: Global timestamp frequency Position */\n#define ITM_TCR_GTSFREQ_Msk                (3UL << ITM_TCR_GTSFREQ_Pos)                   /*!< ITM TCR: Global timestamp frequency Mask */\n\n#define ITM_TCR_TSPRESCALE_Pos              8U                                            /*!< ITM TCR: TSPrescale Position */\n#define ITM_TCR_TSPRESCALE_Msk             (3UL << ITM_TCR_TSPRESCALE_Pos)                /*!< ITM TCR: TSPrescale Mask */\n\n#define ITM_TCR_SWOENA_Pos                  4U                                            /*!< ITM TCR: SWOENA Position */\n#define ITM_TCR_SWOENA_Msk                 (1UL << ITM_TCR_SWOENA_Pos)                    /*!< ITM TCR: SWOENA Mask */\n\n#define ITM_TCR_DWTENA_Pos                  3U                                            /*!< ITM TCR: DWTENA Position */\n#define ITM_TCR_DWTENA_Msk                 (1UL << ITM_TCR_DWTENA_Pos)                    /*!< ITM TCR: DWTENA Mask */\n\n#define ITM_TCR_SYNCENA_Pos                 2U                                            /*!< ITM TCR: SYNCENA Position */\n#define ITM_TCR_SYNCENA_Msk                (1UL << ITM_TCR_SYNCENA_Pos)                   /*!< ITM TCR: SYNCENA Mask */\n\n#define ITM_TCR_TSENA_Pos                   1U                                            /*!< ITM TCR: TSENA Position */\n#define ITM_TCR_TSENA_Msk                  (1UL << ITM_TCR_TSENA_Pos)                     /*!< ITM TCR: TSENA Mask */\n\n#define ITM_TCR_ITMENA_Pos                  0U                                            /*!< ITM TCR: ITM Enable bit Position */\n#define ITM_TCR_ITMENA_Msk                 (1UL /*<< ITM_TCR_ITMENA_Pos*/)                /*!< ITM TCR: ITM Enable bit Mask */\n\n/* ITM Lock Status Register Definitions */\n#define ITM_LSR_BYTEACC_Pos                 2U                                            /*!< ITM LSR: ByteAcc Position */\n#define ITM_LSR_BYTEACC_Msk                (1UL << ITM_LSR_BYTEACC_Pos)                   /*!< ITM LSR: ByteAcc Mask */\n\n#define ITM_LSR_ACCESS_Pos                  1U                                            /*!< ITM LSR: Access Position */\n#define ITM_LSR_ACCESS_Msk                 (1UL << ITM_LSR_ACCESS_Pos)                    /*!< ITM LSR: Access Mask */\n\n#define ITM_LSR_PRESENT_Pos                 0U                                            /*!< ITM LSR: Present Position */\n#define ITM_LSR_PRESENT_Msk                (1UL /*<< ITM_LSR_PRESENT_Pos*/)               /*!< ITM LSR: Present Mask */\n\n/*@}*/ /* end of group CMSIS_ITM */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_DWT     Data Watchpoint and Trace (DWT)\n  \\brief    Type definitions for the Data Watchpoint and Trace (DWT)\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Data Watchpoint and Trace Register (DWT).\n */\ntypedef struct\n{\n  __IOM uint32_t CTRL;                   /*!< Offset: 0x000 (R/W)  Control Register */\n  __IOM uint32_t CYCCNT;                 /*!< Offset: 0x004 (R/W)  Cycle Count Register */\n  __IOM uint32_t CPICNT;                 /*!< Offset: 0x008 (R/W)  CPI Count Register */\n  __IOM uint32_t EXCCNT;                 /*!< Offset: 0x00C (R/W)  Exception Overhead Count Register */\n  __IOM uint32_t SLEEPCNT;               /*!< Offset: 0x010 (R/W)  Sleep Count Register */\n  __IOM uint32_t LSUCNT;                 /*!< Offset: 0x014 (R/W)  LSU Count Register */\n  __IOM uint32_t FOLDCNT;                /*!< Offset: 0x018 (R/W)  Folded-instruction Count Register */\n  __IM  uint32_t PCSR;                   /*!< Offset: 0x01C (R/ )  Program Counter Sample Register */\n  __IOM uint32_t COMP0;                  /*!< Offset: 0x020 (R/W)  Comparator Register 0 */\n  __IOM uint32_t MASK0;                  /*!< Offset: 0x024 (R/W)  Mask Register 0 */\n  __IOM uint32_t FUNCTION0;              /*!< Offset: 0x028 (R/W)  Function Register 0 */\n        uint32_t RESERVED0[1U];\n  __IOM uint32_t COMP1;                  /*!< Offset: 0x030 (R/W)  Comparator Register 1 */\n  __IOM uint32_t MASK1;                  /*!< Offset: 0x034 (R/W)  Mask Register 1 */\n  __IOM uint32_t FUNCTION1;              /*!< Offset: 0x038 (R/W)  Function Register 1 */\n        uint32_t RESERVED1[1U];\n  __IOM uint32_t COMP2;                  /*!< Offset: 0x040 (R/W)  Comparator Register 2 */\n  __IOM uint32_t MASK2;                  /*!< Offset: 0x044 (R/W)  Mask Register 2 */\n  __IOM uint32_t FUNCTION2;              /*!< Offset: 0x048 (R/W)  Function Register 2 */\n        uint32_t RESERVED2[1U];\n  __IOM uint32_t COMP3;                  /*!< Offset: 0x050 (R/W)  Comparator Register 3 */\n  __IOM uint32_t MASK3;                  /*!< Offset: 0x054 (R/W)  Mask Register 3 */\n  __IOM uint32_t FUNCTION3;              /*!< Offset: 0x058 (R/W)  Function Register 3 */\n} DWT_Type;\n\n/* DWT Control Register Definitions */\n#define DWT_CTRL_NUMCOMP_Pos               28U                                         /*!< DWT CTRL: NUMCOMP Position */\n#define DWT_CTRL_NUMCOMP_Msk               (0xFUL << DWT_CTRL_NUMCOMP_Pos)             /*!< DWT CTRL: NUMCOMP Mask */\n\n#define DWT_CTRL_NOTRCPKT_Pos              27U                                         /*!< DWT CTRL: NOTRCPKT Position */\n#define DWT_CTRL_NOTRCPKT_Msk              (0x1UL << DWT_CTRL_NOTRCPKT_Pos)            /*!< DWT CTRL: NOTRCPKT Mask */\n\n#define DWT_CTRL_NOEXTTRIG_Pos             26U                                         /*!< DWT CTRL: NOEXTTRIG Position */\n#define DWT_CTRL_NOEXTTRIG_Msk             (0x1UL << DWT_CTRL_NOEXTTRIG_Pos)           /*!< DWT CTRL: NOEXTTRIG Mask */\n\n#define DWT_CTRL_NOCYCCNT_Pos              25U                                         /*!< DWT CTRL: NOCYCCNT Position */\n#define DWT_CTRL_NOCYCCNT_Msk              (0x1UL << DWT_CTRL_NOCYCCNT_Pos)            /*!< DWT CTRL: NOCYCCNT Mask */\n\n#define DWT_CTRL_NOPRFCNT_Pos              24U                                         /*!< DWT CTRL: NOPRFCNT Position */\n#define DWT_CTRL_NOPRFCNT_Msk              (0x1UL << DWT_CTRL_NOPRFCNT_Pos)            /*!< DWT CTRL: NOPRFCNT Mask */\n\n#define DWT_CTRL_CYCEVTENA_Pos             22U                                         /*!< DWT CTRL: CYCEVTENA Position */\n#define DWT_CTRL_CYCEVTENA_Msk             (0x1UL << DWT_CTRL_CYCEVTENA_Pos)           /*!< DWT CTRL: CYCEVTENA Mask */\n\n#define DWT_CTRL_FOLDEVTENA_Pos            21U                                         /*!< DWT CTRL: FOLDEVTENA Position */\n#define DWT_CTRL_FOLDEVTENA_Msk            (0x1UL << DWT_CTRL_FOLDEVTENA_Pos)          /*!< DWT CTRL: FOLDEVTENA Mask */\n\n#define DWT_CTRL_LSUEVTENA_Pos             20U                                         /*!< DWT CTRL: LSUEVTENA Position */\n#define DWT_CTRL_LSUEVTENA_Msk             (0x1UL << DWT_CTRL_LSUEVTENA_Pos)           /*!< DWT CTRL: LSUEVTENA Mask */\n\n#define DWT_CTRL_SLEEPEVTENA_Pos           19U                                         /*!< DWT CTRL: SLEEPEVTENA Position */\n#define DWT_CTRL_SLEEPEVTENA_Msk           (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos)         /*!< DWT CTRL: SLEEPEVTENA Mask */\n\n#define DWT_CTRL_EXCEVTENA_Pos             18U                                         /*!< DWT CTRL: EXCEVTENA Position */\n#define DWT_CTRL_EXCEVTENA_Msk             (0x1UL << DWT_CTRL_EXCEVTENA_Pos)           /*!< DWT CTRL: EXCEVTENA Mask */\n\n#define DWT_CTRL_CPIEVTENA_Pos             17U                                         /*!< DWT CTRL: CPIEVTENA Position */\n#define DWT_CTRL_CPIEVTENA_Msk             (0x1UL << DWT_CTRL_CPIEVTENA_Pos)           /*!< DWT CTRL: CPIEVTENA Mask */\n\n#define DWT_CTRL_EXCTRCENA_Pos             16U                                         /*!< DWT CTRL: EXCTRCENA Position */\n#define DWT_CTRL_EXCTRCENA_Msk             (0x1UL << DWT_CTRL_EXCTRCENA_Pos)           /*!< DWT CTRL: EXCTRCENA Mask */\n\n#define DWT_CTRL_PCSAMPLENA_Pos            12U                                         /*!< DWT CTRL: PCSAMPLENA Position */\n#define DWT_CTRL_PCSAMPLENA_Msk            (0x1UL << DWT_CTRL_PCSAMPLENA_Pos)          /*!< DWT CTRL: PCSAMPLENA Mask */\n\n#define DWT_CTRL_SYNCTAP_Pos               10U                                         /*!< DWT CTRL: SYNCTAP Position */\n#define DWT_CTRL_SYNCTAP_Msk               (0x3UL << DWT_CTRL_SYNCTAP_Pos)             /*!< DWT CTRL: SYNCTAP Mask */\n\n#define DWT_CTRL_CYCTAP_Pos                 9U                                         /*!< DWT CTRL: CYCTAP Position */\n#define DWT_CTRL_CYCTAP_Msk                (0x1UL << DWT_CTRL_CYCTAP_Pos)              /*!< DWT CTRL: CYCTAP Mask */\n\n#define DWT_CTRL_POSTINIT_Pos               5U                                         /*!< DWT CTRL: POSTINIT Position */\n#define DWT_CTRL_POSTINIT_Msk              (0xFUL << DWT_CTRL_POSTINIT_Pos)            /*!< DWT CTRL: POSTINIT Mask */\n\n#define DWT_CTRL_POSTPRESET_Pos             1U                                         /*!< DWT CTRL: POSTPRESET Position */\n#define DWT_CTRL_POSTPRESET_Msk            (0xFUL << DWT_CTRL_POSTPRESET_Pos)          /*!< DWT CTRL: POSTPRESET Mask */\n\n#define DWT_CTRL_CYCCNTENA_Pos              0U                                         /*!< DWT CTRL: CYCCNTENA Position */\n#define DWT_CTRL_CYCCNTENA_Msk             (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/)       /*!< DWT CTRL: CYCCNTENA Mask */\n\n/* DWT CPI Count Register Definitions */\n#define DWT_CPICNT_CPICNT_Pos               0U                                         /*!< DWT CPICNT: CPICNT Position */\n#define DWT_CPICNT_CPICNT_Msk              (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/)       /*!< DWT CPICNT: CPICNT Mask */\n\n/* DWT Exception Overhead Count Register Definitions */\n#define DWT_EXCCNT_EXCCNT_Pos               0U                                         /*!< DWT EXCCNT: EXCCNT Position */\n#define DWT_EXCCNT_EXCCNT_Msk              (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/)       /*!< DWT EXCCNT: EXCCNT Mask */\n\n/* DWT Sleep Count Register Definitions */\n#define DWT_SLEEPCNT_SLEEPCNT_Pos           0U                                         /*!< DWT SLEEPCNT: SLEEPCNT Position */\n#define DWT_SLEEPCNT_SLEEPCNT_Msk          (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/)   /*!< DWT SLEEPCNT: SLEEPCNT Mask */\n\n/* DWT LSU Count Register Definitions */\n#define DWT_LSUCNT_LSUCNT_Pos               0U                                         /*!< DWT LSUCNT: LSUCNT Position */\n#define DWT_LSUCNT_LSUCNT_Msk              (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/)       /*!< DWT LSUCNT: LSUCNT Mask */\n\n/* DWT Folded-instruction Count Register Definitions */\n#define DWT_FOLDCNT_FOLDCNT_Pos             0U                                         /*!< DWT FOLDCNT: FOLDCNT Position */\n#define DWT_FOLDCNT_FOLDCNT_Msk            (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/)     /*!< DWT FOLDCNT: FOLDCNT Mask */\n\n/* DWT Comparator Mask Register Definitions */\n#define DWT_MASK_MASK_Pos                   0U                                         /*!< DWT MASK: MASK Position */\n#define DWT_MASK_MASK_Msk                  (0x1FUL /*<< DWT_MASK_MASK_Pos*/)           /*!< DWT MASK: MASK Mask */\n\n/* DWT Comparator Function Register Definitions */\n#define DWT_FUNCTION_MATCHED_Pos           24U                                         /*!< DWT FUNCTION: MATCHED Position */\n#define DWT_FUNCTION_MATCHED_Msk           (0x1UL << DWT_FUNCTION_MATCHED_Pos)         /*!< DWT FUNCTION: MATCHED Mask */\n\n#define DWT_FUNCTION_DATAVADDR1_Pos        16U                                         /*!< DWT FUNCTION: DATAVADDR1 Position */\n#define DWT_FUNCTION_DATAVADDR1_Msk        (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos)      /*!< DWT FUNCTION: DATAVADDR1 Mask */\n\n#define DWT_FUNCTION_DATAVADDR0_Pos        12U                                         /*!< DWT FUNCTION: DATAVADDR0 Position */\n#define DWT_FUNCTION_DATAVADDR0_Msk        (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos)      /*!< DWT FUNCTION: DATAVADDR0 Mask */\n\n#define DWT_FUNCTION_DATAVSIZE_Pos         10U                                         /*!< DWT FUNCTION: DATAVSIZE Position */\n#define DWT_FUNCTION_DATAVSIZE_Msk         (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos)       /*!< DWT FUNCTION: DATAVSIZE Mask */\n\n#define DWT_FUNCTION_LNK1ENA_Pos            9U                                         /*!< DWT FUNCTION: LNK1ENA Position */\n#define DWT_FUNCTION_LNK1ENA_Msk           (0x1UL << DWT_FUNCTION_LNK1ENA_Pos)         /*!< DWT FUNCTION: LNK1ENA Mask */\n\n#define DWT_FUNCTION_DATAVMATCH_Pos         8U                                         /*!< DWT FUNCTION: DATAVMATCH Position */\n#define DWT_FUNCTION_DATAVMATCH_Msk        (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos)      /*!< DWT FUNCTION: DATAVMATCH Mask */\n\n#define DWT_FUNCTION_CYCMATCH_Pos           7U                                         /*!< DWT FUNCTION: CYCMATCH Position */\n#define DWT_FUNCTION_CYCMATCH_Msk          (0x1UL << DWT_FUNCTION_CYCMATCH_Pos)        /*!< DWT FUNCTION: CYCMATCH Mask */\n\n#define DWT_FUNCTION_EMITRANGE_Pos          5U                                         /*!< DWT FUNCTION: EMITRANGE Position */\n#define DWT_FUNCTION_EMITRANGE_Msk         (0x1UL << DWT_FUNCTION_EMITRANGE_Pos)       /*!< DWT FUNCTION: EMITRANGE Mask */\n\n#define DWT_FUNCTION_FUNCTION_Pos           0U                                         /*!< DWT FUNCTION: FUNCTION Position */\n#define DWT_FUNCTION_FUNCTION_Msk          (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/)    /*!< DWT FUNCTION: FUNCTION Mask */\n\n/*@}*/ /* end of group CMSIS_DWT */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_TPI     Trace Port Interface (TPI)\n  \\brief    Type definitions for the Trace Port Interface (TPI)\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Trace Port Interface Register (TPI).\n */\ntypedef struct\n{\n  __IM  uint32_t SSPSR;                  /*!< Offset: 0x000 (R/ )  Supported Parallel Port Size Register */\n  __IOM uint32_t CSPSR;                  /*!< Offset: 0x004 (R/W)  Current Parallel Port Size Register */\n        uint32_t RESERVED0[2U];\n  __IOM uint32_t ACPR;                   /*!< Offset: 0x010 (R/W)  Asynchronous Clock Prescaler Register */\n        uint32_t RESERVED1[55U];\n  __IOM uint32_t SPPR;                   /*!< Offset: 0x0F0 (R/W)  Selected Pin Protocol Register */\n        uint32_t RESERVED2[131U];\n  __IM  uint32_t FFSR;                   /*!< Offset: 0x300 (R/ )  Formatter and Flush Status Register */\n  __IOM uint32_t FFCR;                   /*!< Offset: 0x304 (R/W)  Formatter and Flush Control Register */\n  __IM  uint32_t FSCR;                   /*!< Offset: 0x308 (R/ )  Formatter Synchronization Counter Register */\n        uint32_t RESERVED3[759U];\n  __IM  uint32_t TRIGGER;                /*!< Offset: 0xEE8 (R/ )  TRIGGER Register */\n  __IM  uint32_t FIFO0;                  /*!< Offset: 0xEEC (R/ )  Integration ETM Data */\n  __IM  uint32_t ITATBCTR2;              /*!< Offset: 0xEF0 (R/ )  ITATBCTR2 */\n        uint32_t RESERVED4[1U];\n  __IM  uint32_t ITATBCTR0;              /*!< Offset: 0xEF8 (R/ )  ITATBCTR0 */\n  __IM  uint32_t FIFO1;                  /*!< Offset: 0xEFC (R/ )  Integration ITM Data */\n  __IOM uint32_t ITCTRL;                 /*!< Offset: 0xF00 (R/W)  Integration Mode Control */\n        uint32_t RESERVED5[39U];\n  __IOM uint32_t CLAIMSET;               /*!< Offset: 0xFA0 (R/W)  Claim tag set */\n  __IOM uint32_t CLAIMCLR;               /*!< Offset: 0xFA4 (R/W)  Claim tag clear */\n        uint32_t RESERVED7[8U];\n  __IM  uint32_t DEVID;                  /*!< Offset: 0xFC8 (R/ )  TPIU_DEVID */\n  __IM  uint32_t DEVTYPE;                /*!< Offset: 0xFCC (R/ )  TPIU_DEVTYPE */\n} TPI_Type;\n\n/* TPI Asynchronous Clock Prescaler Register Definitions */\n#define TPI_ACPR_PRESCALER_Pos              0U                                         /*!< TPI ACPR: PRESCALER Position */\n#define TPI_ACPR_PRESCALER_Msk             (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/)    /*!< TPI ACPR: PRESCALER Mask */\n\n/* TPI Selected Pin Protocol Register Definitions */\n#define TPI_SPPR_TXMODE_Pos                 0U                                         /*!< TPI SPPR: TXMODE Position */\n#define TPI_SPPR_TXMODE_Msk                (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/)          /*!< TPI SPPR: TXMODE Mask */\n\n/* TPI Formatter and Flush Status Register Definitions */\n#define TPI_FFSR_FtNonStop_Pos              3U                                         /*!< TPI FFSR: FtNonStop Position */\n#define TPI_FFSR_FtNonStop_Msk             (0x1UL << TPI_FFSR_FtNonStop_Pos)           /*!< TPI FFSR: FtNonStop Mask */\n\n#define TPI_FFSR_TCPresent_Pos              2U                                         /*!< TPI FFSR: TCPresent Position */\n#define TPI_FFSR_TCPresent_Msk             (0x1UL << TPI_FFSR_TCPresent_Pos)           /*!< TPI FFSR: TCPresent Mask */\n\n#define TPI_FFSR_FtStopped_Pos              1U                                         /*!< TPI FFSR: FtStopped Position */\n#define TPI_FFSR_FtStopped_Msk             (0x1UL << TPI_FFSR_FtStopped_Pos)           /*!< TPI FFSR: FtStopped Mask */\n\n#define TPI_FFSR_FlInProg_Pos               0U                                         /*!< TPI FFSR: FlInProg Position */\n#define TPI_FFSR_FlInProg_Msk              (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/)        /*!< TPI FFSR: FlInProg Mask */\n\n/* TPI Formatter and Flush Control Register Definitions */\n#define TPI_FFCR_TrigIn_Pos                 8U                                         /*!< TPI FFCR: TrigIn Position */\n#define TPI_FFCR_TrigIn_Msk                (0x1UL << TPI_FFCR_TrigIn_Pos)              /*!< TPI FFCR: TrigIn Mask */\n\n#define TPI_FFCR_EnFCont_Pos                1U                                         /*!< TPI FFCR: EnFCont Position */\n#define TPI_FFCR_EnFCont_Msk               (0x1UL << TPI_FFCR_EnFCont_Pos)             /*!< TPI FFCR: EnFCont Mask */\n\n/* TPI TRIGGER Register Definitions */\n#define TPI_TRIGGER_TRIGGER_Pos             0U                                         /*!< TPI TRIGGER: TRIGGER Position */\n#define TPI_TRIGGER_TRIGGER_Msk            (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/)      /*!< TPI TRIGGER: TRIGGER Mask */\n\n/* TPI Integration ETM Data Register Definitions (FIFO0) */\n#define TPI_FIFO0_ITM_ATVALID_Pos          29U                                         /*!< TPI FIFO0: ITM_ATVALID Position */\n#define TPI_FIFO0_ITM_ATVALID_Msk          (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos)        /*!< TPI FIFO0: ITM_ATVALID Mask */\n\n#define TPI_FIFO0_ITM_bytecount_Pos        27U                                         /*!< TPI FIFO0: ITM_bytecount Position */\n#define TPI_FIFO0_ITM_bytecount_Msk        (0x3UL << TPI_FIFO0_ITM_bytecount_Pos)      /*!< TPI FIFO0: ITM_bytecount Mask */\n\n#define TPI_FIFO0_ETM_ATVALID_Pos          26U                                         /*!< TPI FIFO0: ETM_ATVALID Position */\n#define TPI_FIFO0_ETM_ATVALID_Msk          (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos)        /*!< TPI FIFO0: ETM_ATVALID Mask */\n\n#define TPI_FIFO0_ETM_bytecount_Pos        24U                                         /*!< TPI FIFO0: ETM_bytecount Position */\n#define TPI_FIFO0_ETM_bytecount_Msk        (0x3UL << TPI_FIFO0_ETM_bytecount_Pos)      /*!< TPI FIFO0: ETM_bytecount Mask */\n\n#define TPI_FIFO0_ETM2_Pos                 16U                                         /*!< TPI FIFO0: ETM2 Position */\n#define TPI_FIFO0_ETM2_Msk                 (0xFFUL << TPI_FIFO0_ETM2_Pos)              /*!< TPI FIFO0: ETM2 Mask */\n\n#define TPI_FIFO0_ETM1_Pos                  8U                                         /*!< TPI FIFO0: ETM1 Position */\n#define TPI_FIFO0_ETM1_Msk                 (0xFFUL << TPI_FIFO0_ETM1_Pos)              /*!< TPI FIFO0: ETM1 Mask */\n\n#define TPI_FIFO0_ETM0_Pos                  0U                                         /*!< TPI FIFO0: ETM0 Position */\n#define TPI_FIFO0_ETM0_Msk                 (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/)          /*!< TPI FIFO0: ETM0 Mask */\n\n/* TPI ITATBCTR2 Register Definitions */\n#define TPI_ITATBCTR2_ATREADY2_Pos          0U                                         /*!< TPI ITATBCTR2: ATREADY2 Position */\n#define TPI_ITATBCTR2_ATREADY2_Msk         (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/)   /*!< TPI ITATBCTR2: ATREADY2 Mask */\n\n#define TPI_ITATBCTR2_ATREADY1_Pos          0U                                         /*!< TPI ITATBCTR2: ATREADY1 Position */\n#define TPI_ITATBCTR2_ATREADY1_Msk         (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/)   /*!< TPI ITATBCTR2: ATREADY1 Mask */\n\n/* TPI Integration ITM Data Register Definitions (FIFO1) */\n#define TPI_FIFO1_ITM_ATVALID_Pos          29U                                         /*!< TPI FIFO1: ITM_ATVALID Position */\n#define TPI_FIFO1_ITM_ATVALID_Msk          (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos)        /*!< TPI FIFO1: ITM_ATVALID Mask */\n\n#define TPI_FIFO1_ITM_bytecount_Pos        27U                                         /*!< TPI FIFO1: ITM_bytecount Position */\n#define TPI_FIFO1_ITM_bytecount_Msk        (0x3UL << TPI_FIFO1_ITM_bytecount_Pos)      /*!< TPI FIFO1: ITM_bytecount Mask */\n\n#define TPI_FIFO1_ETM_ATVALID_Pos          26U                                         /*!< TPI FIFO1: ETM_ATVALID Position */\n#define TPI_FIFO1_ETM_ATVALID_Msk          (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos)        /*!< TPI FIFO1: ETM_ATVALID Mask */\n\n#define TPI_FIFO1_ETM_bytecount_Pos        24U                                         /*!< TPI FIFO1: ETM_bytecount Position */\n#define TPI_FIFO1_ETM_bytecount_Msk        (0x3UL << TPI_FIFO1_ETM_bytecount_Pos)      /*!< TPI FIFO1: ETM_bytecount Mask */\n\n#define TPI_FIFO1_ITM2_Pos                 16U                                         /*!< TPI FIFO1: ITM2 Position */\n#define TPI_FIFO1_ITM2_Msk                 (0xFFUL << TPI_FIFO1_ITM2_Pos)              /*!< TPI FIFO1: ITM2 Mask */\n\n#define TPI_FIFO1_ITM1_Pos                  8U                                         /*!< TPI FIFO1: ITM1 Position */\n#define TPI_FIFO1_ITM1_Msk                 (0xFFUL << TPI_FIFO1_ITM1_Pos)              /*!< TPI FIFO1: ITM1 Mask */\n\n#define TPI_FIFO1_ITM0_Pos                  0U                                         /*!< TPI FIFO1: ITM0 Position */\n#define TPI_FIFO1_ITM0_Msk                 (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/)          /*!< TPI FIFO1: ITM0 Mask */\n\n/* TPI ITATBCTR0 Register Definitions */\n#define TPI_ITATBCTR0_ATREADY2_Pos          0U                                         /*!< TPI ITATBCTR0: ATREADY2 Position */\n#define TPI_ITATBCTR0_ATREADY2_Msk         (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/)   /*!< TPI ITATBCTR0: ATREADY2 Mask */\n\n#define TPI_ITATBCTR0_ATREADY1_Pos          0U                                         /*!< TPI ITATBCTR0: ATREADY1 Position */\n#define TPI_ITATBCTR0_ATREADY1_Msk         (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/)   /*!< TPI ITATBCTR0: ATREADY1 Mask */\n\n/* TPI Integration Mode Control Register Definitions */\n#define TPI_ITCTRL_Mode_Pos                 0U                                         /*!< TPI ITCTRL: Mode Position */\n#define TPI_ITCTRL_Mode_Msk                (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/)          /*!< TPI ITCTRL: Mode Mask */\n\n/* TPI DEVID Register Definitions */\n#define TPI_DEVID_NRZVALID_Pos             11U                                         /*!< TPI DEVID: NRZVALID Position */\n#define TPI_DEVID_NRZVALID_Msk             (0x1UL << TPI_DEVID_NRZVALID_Pos)           /*!< TPI DEVID: NRZVALID Mask */\n\n#define TPI_DEVID_MANCVALID_Pos            10U                                         /*!< TPI DEVID: MANCVALID Position */\n#define TPI_DEVID_MANCVALID_Msk            (0x1UL << TPI_DEVID_MANCVALID_Pos)          /*!< TPI DEVID: MANCVALID Mask */\n\n#define TPI_DEVID_PTINVALID_Pos             9U                                         /*!< TPI DEVID: PTINVALID Position */\n#define TPI_DEVID_PTINVALID_Msk            (0x1UL << TPI_DEVID_PTINVALID_Pos)          /*!< TPI DEVID: PTINVALID Mask */\n\n#define TPI_DEVID_MinBufSz_Pos              6U                                         /*!< TPI DEVID: MinBufSz Position */\n#define TPI_DEVID_MinBufSz_Msk             (0x7UL << TPI_DEVID_MinBufSz_Pos)           /*!< TPI DEVID: MinBufSz Mask */\n\n#define TPI_DEVID_AsynClkIn_Pos             5U                                         /*!< TPI DEVID: AsynClkIn Position */\n#define TPI_DEVID_AsynClkIn_Msk            (0x1UL << TPI_DEVID_AsynClkIn_Pos)          /*!< TPI DEVID: AsynClkIn Mask */\n\n#define TPI_DEVID_NrTraceInput_Pos          0U                                         /*!< TPI DEVID: NrTraceInput Position */\n#define TPI_DEVID_NrTraceInput_Msk         (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/)  /*!< TPI DEVID: NrTraceInput Mask */\n\n/* TPI DEVTYPE Register Definitions */\n#define TPI_DEVTYPE_SubType_Pos             4U                                         /*!< TPI DEVTYPE: SubType Position */\n#define TPI_DEVTYPE_SubType_Msk            (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/)      /*!< TPI DEVTYPE: SubType Mask */\n\n#define TPI_DEVTYPE_MajorType_Pos           0U                                         /*!< TPI DEVTYPE: MajorType Position */\n#define TPI_DEVTYPE_MajorType_Msk          (0xFUL << TPI_DEVTYPE_MajorType_Pos)        /*!< TPI DEVTYPE: MajorType Mask */\n\n/*@}*/ /* end of group CMSIS_TPI */\n\n\n#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U)\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_MPU     Memory Protection Unit (MPU)\n  \\brief    Type definitions for the Memory Protection Unit (MPU)\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Memory Protection Unit (MPU).\n */\ntypedef struct\n{\n  __IM  uint32_t TYPE;                   /*!< Offset: 0x000 (R/ )  MPU Type Register */\n  __IOM uint32_t CTRL;                   /*!< Offset: 0x004 (R/W)  MPU Control Register */\n  __IOM uint32_t RNR;                    /*!< Offset: 0x008 (R/W)  MPU Region RNRber Register */\n  __IOM uint32_t RBAR;                   /*!< Offset: 0x00C (R/W)  MPU Region Base Address Register */\n  __IOM uint32_t RASR;                   /*!< Offset: 0x010 (R/W)  MPU Region Attribute and Size Register */\n  __IOM uint32_t RBAR_A1;                /*!< Offset: 0x014 (R/W)  MPU Alias 1 Region Base Address Register */\n  __IOM uint32_t RASR_A1;                /*!< Offset: 0x018 (R/W)  MPU Alias 1 Region Attribute and Size Register */\n  __IOM uint32_t RBAR_A2;                /*!< Offset: 0x01C (R/W)  MPU Alias 2 Region Base Address Register */\n  __IOM uint32_t RASR_A2;                /*!< Offset: 0x020 (R/W)  MPU Alias 2 Region Attribute and Size Register */\n  __IOM uint32_t RBAR_A3;                /*!< Offset: 0x024 (R/W)  MPU Alias 3 Region Base Address Register */\n  __IOM uint32_t RASR_A3;                /*!< Offset: 0x028 (R/W)  MPU Alias 3 Region Attribute and Size Register */\n} MPU_Type;\n\n#define MPU_TYPE_RALIASES                  4U\n\n/* MPU Type Register Definitions */\n#define MPU_TYPE_IREGION_Pos               16U                                            /*!< MPU TYPE: IREGION Position */\n#define MPU_TYPE_IREGION_Msk               (0xFFUL << MPU_TYPE_IREGION_Pos)               /*!< MPU TYPE: IREGION Mask */\n\n#define MPU_TYPE_DREGION_Pos                8U                                            /*!< MPU TYPE: DREGION Position */\n#define MPU_TYPE_DREGION_Msk               (0xFFUL << MPU_TYPE_DREGION_Pos)               /*!< MPU TYPE: DREGION Mask */\n\n#define MPU_TYPE_SEPARATE_Pos               0U                                            /*!< MPU TYPE: SEPARATE Position */\n#define MPU_TYPE_SEPARATE_Msk              (1UL /*<< MPU_TYPE_SEPARATE_Pos*/)             /*!< MPU TYPE: SEPARATE Mask */\n\n/* MPU Control Register Definitions */\n#define MPU_CTRL_PRIVDEFENA_Pos             2U                                            /*!< MPU CTRL: PRIVDEFENA Position */\n#define MPU_CTRL_PRIVDEFENA_Msk            (1UL << MPU_CTRL_PRIVDEFENA_Pos)               /*!< MPU CTRL: PRIVDEFENA Mask */\n\n#define MPU_CTRL_HFNMIENA_Pos               1U                                            /*!< MPU CTRL: HFNMIENA Position */\n#define MPU_CTRL_HFNMIENA_Msk              (1UL << MPU_CTRL_HFNMIENA_Pos)                 /*!< MPU CTRL: HFNMIENA Mask */\n\n#define MPU_CTRL_ENABLE_Pos                 0U                                            /*!< MPU CTRL: ENABLE Position */\n#define MPU_CTRL_ENABLE_Msk                (1UL /*<< MPU_CTRL_ENABLE_Pos*/)               /*!< MPU CTRL: ENABLE Mask */\n\n/* MPU Region Number Register Definitions */\n#define MPU_RNR_REGION_Pos                  0U                                            /*!< MPU RNR: REGION Position */\n#define MPU_RNR_REGION_Msk                 (0xFFUL /*<< MPU_RNR_REGION_Pos*/)             /*!< MPU RNR: REGION Mask */\n\n/* MPU Region Base Address Register Definitions */\n#define MPU_RBAR_ADDR_Pos                   5U                                            /*!< MPU RBAR: ADDR Position */\n#define MPU_RBAR_ADDR_Msk                  (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos)             /*!< MPU RBAR: ADDR Mask */\n\n#define MPU_RBAR_VALID_Pos                  4U                                            /*!< MPU RBAR: VALID Position */\n#define MPU_RBAR_VALID_Msk                 (1UL << MPU_RBAR_VALID_Pos)                    /*!< MPU RBAR: VALID Mask */\n\n#define MPU_RBAR_REGION_Pos                 0U                                            /*!< MPU RBAR: REGION Position */\n#define MPU_RBAR_REGION_Msk                (0xFUL /*<< MPU_RBAR_REGION_Pos*/)             /*!< MPU RBAR: REGION Mask */\n\n/* MPU Region Attribute and Size Register Definitions */\n#define MPU_RASR_ATTRS_Pos                 16U                                            /*!< MPU RASR: MPU Region Attribute field Position */\n#define MPU_RASR_ATTRS_Msk                 (0xFFFFUL << MPU_RASR_ATTRS_Pos)               /*!< MPU RASR: MPU Region Attribute field Mask */\n\n#define MPU_RASR_XN_Pos                    28U                                            /*!< MPU RASR: ATTRS.XN Position */\n#define MPU_RASR_XN_Msk                    (1UL << MPU_RASR_XN_Pos)                       /*!< MPU RASR: ATTRS.XN Mask */\n\n#define MPU_RASR_AP_Pos                    24U                                            /*!< MPU RASR: ATTRS.AP Position */\n#define MPU_RASR_AP_Msk                    (0x7UL << MPU_RASR_AP_Pos)                     /*!< MPU RASR: ATTRS.AP Mask */\n\n#define MPU_RASR_TEX_Pos                   19U                                            /*!< MPU RASR: ATTRS.TEX Position */\n#define MPU_RASR_TEX_Msk                   (0x7UL << MPU_RASR_TEX_Pos)                    /*!< MPU RASR: ATTRS.TEX Mask */\n\n#define MPU_RASR_S_Pos                     18U                                            /*!< MPU RASR: ATTRS.S Position */\n#define MPU_RASR_S_Msk                     (1UL << MPU_RASR_S_Pos)                        /*!< MPU RASR: ATTRS.S Mask */\n\n#define MPU_RASR_C_Pos                     17U                                            /*!< MPU RASR: ATTRS.C Position */\n#define MPU_RASR_C_Msk                     (1UL << MPU_RASR_C_Pos)                        /*!< MPU RASR: ATTRS.C Mask */\n\n#define MPU_RASR_B_Pos                     16U                                            /*!< MPU RASR: ATTRS.B Position */\n#define MPU_RASR_B_Msk                     (1UL << MPU_RASR_B_Pos)                        /*!< MPU RASR: ATTRS.B Mask */\n\n#define MPU_RASR_SRD_Pos                    8U                                            /*!< MPU RASR: Sub-Region Disable Position */\n#define MPU_RASR_SRD_Msk                   (0xFFUL << MPU_RASR_SRD_Pos)                   /*!< MPU RASR: Sub-Region Disable Mask */\n\n#define MPU_RASR_SIZE_Pos                   1U                                            /*!< MPU RASR: Region Size Field Position */\n#define MPU_RASR_SIZE_Msk                  (0x1FUL << MPU_RASR_SIZE_Pos)                  /*!< MPU RASR: Region Size Field Mask */\n\n#define MPU_RASR_ENABLE_Pos                 0U                                            /*!< MPU RASR: Region enable bit Position */\n#define MPU_RASR_ENABLE_Msk                (1UL /*<< MPU_RASR_ENABLE_Pos*/)               /*!< MPU RASR: Region enable bit Disable Mask */\n\n/*@} end of group CMSIS_MPU */\n#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_FPU     Floating Point Unit (FPU)\n  \\brief    Type definitions for the Floating Point Unit (FPU)\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Floating Point Unit (FPU).\n */\ntypedef struct\n{\n        uint32_t RESERVED0[1U];\n  __IOM uint32_t FPCCR;                  /*!< Offset: 0x004 (R/W)  Floating-Point Context Control Register */\n  __IOM uint32_t FPCAR;                  /*!< Offset: 0x008 (R/W)  Floating-Point Context Address Register */\n  __IOM uint32_t FPDSCR;                 /*!< Offset: 0x00C (R/W)  Floating-Point Default Status Control Register */\n  __IM  uint32_t MVFR0;                  /*!< Offset: 0x010 (R/ )  Media and FP Feature Register 0 */\n  __IM  uint32_t MVFR1;                  /*!< Offset: 0x014 (R/ )  Media and FP Feature Register 1 */\n  __IM  uint32_t MVFR2;                  /*!< Offset: 0x018 (R/ )  Media and FP Feature Register 2 */\n} FPU_Type;\n\n/* Floating-Point Context Control Register Definitions */\n#define FPU_FPCCR_ASPEN_Pos                31U                                            /*!< FPCCR: ASPEN bit Position */\n#define FPU_FPCCR_ASPEN_Msk                (1UL << FPU_FPCCR_ASPEN_Pos)                   /*!< FPCCR: ASPEN bit Mask */\n\n#define FPU_FPCCR_LSPEN_Pos                30U                                            /*!< FPCCR: LSPEN Position */\n#define FPU_FPCCR_LSPEN_Msk                (1UL << FPU_FPCCR_LSPEN_Pos)                   /*!< FPCCR: LSPEN bit Mask */\n\n#define FPU_FPCCR_MONRDY_Pos                8U                                            /*!< FPCCR: MONRDY Position */\n#define FPU_FPCCR_MONRDY_Msk               (1UL << FPU_FPCCR_MONRDY_Pos)                  /*!< FPCCR: MONRDY bit Mask */\n\n#define FPU_FPCCR_BFRDY_Pos                 6U                                            /*!< FPCCR: BFRDY Position */\n#define FPU_FPCCR_BFRDY_Msk                (1UL << FPU_FPCCR_BFRDY_Pos)                   /*!< FPCCR: BFRDY bit Mask */\n\n#define FPU_FPCCR_MMRDY_Pos                 5U                                            /*!< FPCCR: MMRDY Position */\n#define FPU_FPCCR_MMRDY_Msk                (1UL << FPU_FPCCR_MMRDY_Pos)                   /*!< FPCCR: MMRDY bit Mask */\n\n#define FPU_FPCCR_HFRDY_Pos                 4U                                            /*!< FPCCR: HFRDY Position */\n#define FPU_FPCCR_HFRDY_Msk                (1UL << FPU_FPCCR_HFRDY_Pos)                   /*!< FPCCR: HFRDY bit Mask */\n\n#define FPU_FPCCR_THREAD_Pos                3U                                            /*!< FPCCR: processor mode bit Position */\n#define FPU_FPCCR_THREAD_Msk               (1UL << FPU_FPCCR_THREAD_Pos)                  /*!< FPCCR: processor mode active bit Mask */\n\n#define FPU_FPCCR_USER_Pos                  1U                                            /*!< FPCCR: privilege level bit Position */\n#define FPU_FPCCR_USER_Msk                 (1UL << FPU_FPCCR_USER_Pos)                    /*!< FPCCR: privilege level bit Mask */\n\n#define FPU_FPCCR_LSPACT_Pos                0U                                            /*!< FPCCR: Lazy state preservation active bit Position */\n#define FPU_FPCCR_LSPACT_Msk               (1UL /*<< FPU_FPCCR_LSPACT_Pos*/)              /*!< FPCCR: Lazy state preservation active bit Mask */\n\n/* Floating-Point Context Address Register Definitions */\n#define FPU_FPCAR_ADDRESS_Pos               3U                                            /*!< FPCAR: ADDRESS bit Position */\n#define FPU_FPCAR_ADDRESS_Msk              (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos)        /*!< FPCAR: ADDRESS bit Mask */\n\n/* Floating-Point Default Status Control Register Definitions */\n#define FPU_FPDSCR_AHP_Pos                 26U                                            /*!< FPDSCR: AHP bit Position */\n#define FPU_FPDSCR_AHP_Msk                 (1UL << FPU_FPDSCR_AHP_Pos)                    /*!< FPDSCR: AHP bit Mask */\n\n#define FPU_FPDSCR_DN_Pos                  25U                                            /*!< FPDSCR: DN bit Position */\n#define FPU_FPDSCR_DN_Msk                  (1UL << FPU_FPDSCR_DN_Pos)                     /*!< FPDSCR: DN bit Mask */\n\n#define FPU_FPDSCR_FZ_Pos                  24U                                            /*!< FPDSCR: FZ bit Position */\n#define FPU_FPDSCR_FZ_Msk                  (1UL << FPU_FPDSCR_FZ_Pos)                     /*!< FPDSCR: FZ bit Mask */\n\n#define FPU_FPDSCR_RMode_Pos               22U                                            /*!< FPDSCR: RMode bit Position */\n#define FPU_FPDSCR_RMode_Msk               (3UL << FPU_FPDSCR_RMode_Pos)                  /*!< FPDSCR: RMode bit Mask */\n\n/* Media and FP Feature Register 0 Definitions */\n#define FPU_MVFR0_FP_rounding_modes_Pos    28U                                            /*!< MVFR0: FP rounding modes bits Position */\n#define FPU_MVFR0_FP_rounding_modes_Msk    (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos)     /*!< MVFR0: FP rounding modes bits Mask */\n\n#define FPU_MVFR0_Short_vectors_Pos        24U                                            /*!< MVFR0: Short vectors bits Position */\n#define FPU_MVFR0_Short_vectors_Msk        (0xFUL << FPU_MVFR0_Short_vectors_Pos)         /*!< MVFR0: Short vectors bits Mask */\n\n#define FPU_MVFR0_Square_root_Pos          20U                                            /*!< MVFR0: Square root bits Position */\n#define FPU_MVFR0_Square_root_Msk          (0xFUL << FPU_MVFR0_Square_root_Pos)           /*!< MVFR0: Square root bits Mask */\n\n#define FPU_MVFR0_Divide_Pos               16U                                            /*!< MVFR0: Divide bits Position */\n#define FPU_MVFR0_Divide_Msk               (0xFUL << FPU_MVFR0_Divide_Pos)                /*!< MVFR0: Divide bits Mask */\n\n#define FPU_MVFR0_FP_excep_trapping_Pos    12U                                            /*!< MVFR0: FP exception trapping bits Position */\n#define FPU_MVFR0_FP_excep_trapping_Msk    (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos)     /*!< MVFR0: FP exception trapping bits Mask */\n\n#define FPU_MVFR0_Double_precision_Pos      8U                                            /*!< MVFR0: Double-precision bits Position */\n#define FPU_MVFR0_Double_precision_Msk     (0xFUL << FPU_MVFR0_Double_precision_Pos)      /*!< MVFR0: Double-precision bits Mask */\n\n#define FPU_MVFR0_Single_precision_Pos      4U                                            /*!< MVFR0: Single-precision bits Position */\n#define FPU_MVFR0_Single_precision_Msk     (0xFUL << FPU_MVFR0_Single_precision_Pos)      /*!< MVFR0: Single-precision bits Mask */\n\n#define FPU_MVFR0_A_SIMD_registers_Pos      0U                                            /*!< MVFR0: A_SIMD registers bits Position */\n#define FPU_MVFR0_A_SIMD_registers_Msk     (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/)  /*!< MVFR0: A_SIMD registers bits Mask */\n\n/* Media and FP Feature Register 1 Definitions */\n#define FPU_MVFR1_FP_fused_MAC_Pos         28U                                            /*!< MVFR1: FP fused MAC bits Position */\n#define FPU_MVFR1_FP_fused_MAC_Msk         (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos)          /*!< MVFR1: FP fused MAC bits Mask */\n\n#define FPU_MVFR1_FP_HPFP_Pos              24U                                            /*!< MVFR1: FP HPFP bits Position */\n#define FPU_MVFR1_FP_HPFP_Msk              (0xFUL << FPU_MVFR1_FP_HPFP_Pos)               /*!< MVFR1: FP HPFP bits Mask */\n\n#define FPU_MVFR1_D_NaN_mode_Pos            4U                                            /*!< MVFR1: D_NaN mode bits Position */\n#define FPU_MVFR1_D_NaN_mode_Msk           (0xFUL << FPU_MVFR1_D_NaN_mode_Pos)            /*!< MVFR1: D_NaN mode bits Mask */\n\n#define FPU_MVFR1_FtZ_mode_Pos              0U                                            /*!< MVFR1: FtZ mode bits Position */\n#define FPU_MVFR1_FtZ_mode_Msk             (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/)          /*!< MVFR1: FtZ mode bits Mask */\n\n/* Media and FP Feature Register 2 Definitions */\n\n#define FPU_MVFR2_VFP_Misc_Pos              4U                                            /*!< MVFR2: VFP Misc bits Position */\n#define FPU_MVFR2_VFP_Misc_Msk             (0xFUL << FPU_MVFR2_VFP_Misc_Pos)              /*!< MVFR2: VFP Misc bits Mask */\n\n/*@} end of group CMSIS_FPU */\n\n\n/**\n  \\ingroup  CMSIS_core_register\n  \\defgroup CMSIS_CoreDebug       Core Debug Registers (CoreDebug)\n  \\brief    Type definitions for the Core Debug Registers\n  @{\n */\n\n/**\n  \\brief  Structure type to access the Core Debug Register (CoreDebug).\n */\ntypedef struct\n{\n  __IOM uint32_t DHCSR;                  /*!< Offset: 0x000 (R/W)  Debug Halting Control and Status Register */\n  __OM  uint32_t DCRSR;                  /*!< Offset: 0x004 ( /W)  Debug Core Register Selector Register */\n  __IOM uint32_t DCRDR;                  /*!< Offset: 0x008 (R/W)  Debug Core Register Data Register */\n  __IOM uint32_t DEMCR;                  /*!< Offset: 0x00C (R/W)  Debug Exception and Monitor Control Register */\n} CoreDebug_Type;\n\n/* Debug Halting Control and Status Register Definitions */\n#define CoreDebug_DHCSR_DBGKEY_Pos         16U                                            /*!< CoreDebug DHCSR: DBGKEY Position */\n#define CoreDebug_DHCSR_DBGKEY_Msk         (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos)       /*!< CoreDebug DHCSR: DBGKEY Mask */\n\n#define CoreDebug_DHCSR_S_RESET_ST_Pos     25U                                            /*!< CoreDebug DHCSR: S_RESET_ST Position */\n#define CoreDebug_DHCSR_S_RESET_ST_Msk     (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos)        /*!< CoreDebug DHCSR: S_RESET_ST Mask */\n\n#define CoreDebug_DHCSR_S_RETIRE_ST_Pos    24U                                            /*!< CoreDebug DHCSR: S_RETIRE_ST Position */\n#define CoreDebug_DHCSR_S_RETIRE_ST_Msk    (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos)       /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */\n\n#define CoreDebug_DHCSR_S_LOCKUP_Pos       19U                                            /*!< CoreDebug DHCSR: S_LOCKUP Position */\n#define CoreDebug_DHCSR_S_LOCKUP_Msk       (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos)          /*!< CoreDebug DHCSR: S_LOCKUP Mask */\n\n#define CoreDebug_DHCSR_S_SLEEP_Pos        18U                                            /*!< CoreDebug DHCSR: S_SLEEP Position */\n#define CoreDebug_DHCSR_S_SLEEP_Msk        (1UL << CoreDebug_DHCSR_S_SLEEP_Pos)           /*!< CoreDebug DHCSR: S_SLEEP Mask */\n\n#define CoreDebug_DHCSR_S_HALT_Pos         17U                                            /*!< CoreDebug DHCSR: S_HALT Position */\n#define CoreDebug_DHCSR_S_HALT_Msk         (1UL << CoreDebug_DHCSR_S_HALT_Pos)            /*!< CoreDebug DHCSR: S_HALT Mask */\n\n#define CoreDebug_DHCSR_S_REGRDY_Pos       16U                                            /*!< CoreDebug DHCSR: S_REGRDY Position */\n#define CoreDebug_DHCSR_S_REGRDY_Msk       (1UL << CoreDebug_DHCSR_S_REGRDY_Pos)          /*!< CoreDebug DHCSR: S_REGRDY Mask */\n\n#define CoreDebug_DHCSR_C_SNAPSTALL_Pos     5U                                            /*!< CoreDebug DHCSR: C_SNAPSTALL Position */\n#define CoreDebug_DHCSR_C_SNAPSTALL_Msk    (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos)       /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */\n\n#define CoreDebug_DHCSR_C_MASKINTS_Pos      3U                                            /*!< CoreDebug DHCSR: C_MASKINTS Position */\n#define CoreDebug_DHCSR_C_MASKINTS_Msk     (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos)        /*!< CoreDebug DHCSR: C_MASKINTS Mask */\n\n#define CoreDebug_DHCSR_C_STEP_Pos          2U                                            /*!< CoreDebug DHCSR: C_STEP Position */\n#define CoreDebug_DHCSR_C_STEP_Msk         (1UL << CoreDebug_DHCSR_C_STEP_Pos)            /*!< CoreDebug DHCSR: C_STEP Mask */\n\n#define CoreDebug_DHCSR_C_HALT_Pos          1U                                            /*!< CoreDebug DHCSR: C_HALT Position */\n#define CoreDebug_DHCSR_C_HALT_Msk         (1UL << CoreDebug_DHCSR_C_HALT_Pos)            /*!< CoreDebug DHCSR: C_HALT Mask */\n\n#define CoreDebug_DHCSR_C_DEBUGEN_Pos       0U                                            /*!< CoreDebug DHCSR: C_DEBUGEN Position */\n#define CoreDebug_DHCSR_C_DEBUGEN_Msk      (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/)     /*!< CoreDebug DHCSR: C_DEBUGEN Mask */\n\n/* Debug Core Register Selector Register Definitions */\n#define CoreDebug_DCRSR_REGWnR_Pos         16U                                            /*!< CoreDebug DCRSR: REGWnR Position */\n#define CoreDebug_DCRSR_REGWnR_Msk         (1UL << CoreDebug_DCRSR_REGWnR_Pos)            /*!< CoreDebug DCRSR: REGWnR Mask */\n\n#define CoreDebug_DCRSR_REGSEL_Pos          0U                                            /*!< CoreDebug DCRSR: REGSEL Position */\n#define CoreDebug_DCRSR_REGSEL_Msk         (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/)     /*!< CoreDebug DCRSR: REGSEL Mask */\n\n/* Debug Exception and Monitor Control Register Definitions */\n#define CoreDebug_DEMCR_TRCENA_Pos         24U                                            /*!< CoreDebug DEMCR: TRCENA Position */\n#define CoreDebug_DEMCR_TRCENA_Msk         (1UL << CoreDebug_DEMCR_TRCENA_Pos)            /*!< CoreDebug DEMCR: TRCENA Mask */\n\n#define CoreDebug_DEMCR_MON_REQ_Pos        19U                                            /*!< CoreDebug DEMCR: MON_REQ Position */\n#define CoreDebug_DEMCR_MON_REQ_Msk        (1UL << CoreDebug_DEMCR_MON_REQ_Pos)           /*!< CoreDebug DEMCR: MON_REQ Mask */\n\n#define CoreDebug_DEMCR_MON_STEP_Pos       18U                                            /*!< CoreDebug DEMCR: MON_STEP Position */\n#define CoreDebug_DEMCR_MON_STEP_Msk       (1UL << CoreDebug_DEMCR_MON_STEP_Pos)          /*!< CoreDebug DEMCR: MON_STEP Mask */\n\n#define CoreDebug_DEMCR_MON_PEND_Pos       17U                                            /*!< CoreDebug DEMCR: MON_PEND Position */\n#define CoreDebug_DEMCR_MON_PEND_Msk       (1UL << CoreDebug_DEMCR_MON_PEND_Pos)          /*!< CoreDebug DEMCR: MON_PEND Mask */\n\n#define CoreDebug_DEMCR_MON_EN_Pos         16U                                            /*!< CoreDebug DEMCR: MON_EN Position */\n#define CoreDebug_DEMCR_MON_EN_Msk         (1UL << CoreDebug_DEMCR_MON_EN_Pos)            /*!< CoreDebug DEMCR: MON_EN Mask */\n\n#define CoreDebug_DEMCR_VC_HARDERR_Pos     10U                                            /*!< CoreDebug DEMCR: VC_HARDERR Position */\n#define CoreDebug_DEMCR_VC_HARDERR_Msk     (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos)        /*!< CoreDebug DEMCR: VC_HARDERR Mask */\n\n#define CoreDebug_DEMCR_VC_INTERR_Pos       9U                                            /*!< CoreDebug DEMCR: VC_INTERR Position */\n#define CoreDebug_DEMCR_VC_INTERR_Msk      (1UL << CoreDebug_DEMCR_VC_INTERR_Pos)         /*!< CoreDebug DEMCR: VC_INTERR Mask */\n\n#define CoreDebug_DEMCR_VC_BUSERR_Pos       8U                                            /*!< CoreDebug DEMCR: VC_BUSERR Position */\n#define CoreDebug_DEMCR_VC_BUSERR_Msk      (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos)         /*!< CoreDebug DEMCR: VC_BUSERR Mask */\n\n#define CoreDebug_DEMCR_VC_STATERR_Pos      7U                                            /*!< CoreDebug DEMCR: VC_STATERR Position */\n#define CoreDebug_DEMCR_VC_STATERR_Msk     (1UL << CoreDebug_DEMCR_VC_STATERR_Pos)        /*!< CoreDebug DEMCR: VC_STATERR Mask */\n\n#define CoreDebug_DEMCR_VC_CHKERR_Pos       6U                                            /*!< CoreDebug DEMCR: VC_CHKERR Position */\n#define CoreDebug_DEMCR_VC_CHKERR_Msk      (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos)         /*!< CoreDebug DEMCR: VC_CHKERR Mask */\n\n#define CoreDebug_DEMCR_VC_NOCPERR_Pos      5U                                            /*!< CoreDebug DEMCR: VC_NOCPERR Position */\n#define CoreDebug_DEMCR_VC_NOCPERR_Msk     (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos)        /*!< CoreDebug DEMCR: VC_NOCPERR Mask */\n\n#define CoreDebug_DEMCR_VC_MMERR_Pos        4U                                            /*!< CoreDebug DEMCR: VC_MMERR Position */\n#define CoreDebug_DEMCR_VC_MMERR_Msk       (1UL << CoreDebug_DEMCR_VC_MMERR_Pos)          /*!< CoreDebug DEMCR: VC_MMERR Mask */\n\n#define CoreDebug_DEMCR_VC_CORERESET_Pos    0U                                            /*!< CoreDebug DEMCR: VC_CORERESET Position */\n#define CoreDebug_DEMCR_VC_CORERESET_Msk   (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/)  /*!< CoreDebug DEMCR: VC_CORERESET Mask */\n\n/*@} end of group CMSIS_CoreDebug */\n\n\n/**\n  \\ingroup    CMSIS_core_register\n  \\defgroup   CMSIS_core_bitfield     Core register bit field macros\n  \\brief      Macros for use with bit field definitions (xxx_Pos, xxx_Msk).\n  @{\n */\n\n/**\n  \\brief   Mask and shift a bit field value for use in a register bit range.\n  \\param[in] field  Name of the register bit field.\n  \\param[in] value  Value of the bit field. This parameter is interpreted as an uint32_t type.\n  \\return           Masked and shifted value.\n*/\n#define _VAL2FLD(field, value)    (((uint32_t)(value) << field ## _Pos) & field ## _Msk)\n\n/**\n  \\brief     Mask and shift a register value to extract a bit filed value.\n  \\param[in] field  Name of the register bit field.\n  \\param[in] value  Value of register. This parameter is interpreted as an uint32_t type.\n  \\return           Masked and shifted bit field value.\n*/\n#define _FLD2VAL(field, value)    (((uint32_t)(value) & field ## _Msk) >> field ## _Pos)\n\n/*@} end of group CMSIS_core_bitfield */\n\n\n/**\n  \\ingroup    CMSIS_core_register\n  \\defgroup   CMSIS_core_base     Core Definitions\n  \\brief      Definitions for base addresses, unions, and structures.\n  @{\n */\n\n/* Memory mapping of Core Hardware */\n#define SCS_BASE            (0xE000E000UL)                            /*!< System Control Space Base Address */\n#define ITM_BASE            (0xE0000000UL)                            /*!< ITM Base Address */\n#define DWT_BASE            (0xE0001000UL)                            /*!< DWT Base Address */\n#define TPI_BASE            (0xE0040000UL)                            /*!< TPI Base Address */\n#define CoreDebug_BASE      (0xE000EDF0UL)                            /*!< Core Debug Base Address */\n#define SysTick_BASE        (SCS_BASE +  0x0010UL)                    /*!< SysTick Base Address */\n#define NVIC_BASE           (SCS_BASE +  0x0100UL)                    /*!< NVIC Base Address */\n#define SCB_BASE            (SCS_BASE +  0x0D00UL)                    /*!< System Control Block Base Address */\n\n#define SCnSCB              ((SCnSCB_Type    *)     SCS_BASE      )   /*!< System control Register not in SCB */\n#define SCB                 ((SCB_Type       *)     SCB_BASE      )   /*!< SCB configuration struct */\n#define SysTick             ((SysTick_Type   *)     SysTick_BASE  )   /*!< SysTick configuration struct */\n#define NVIC                ((NVIC_Type      *)     NVIC_BASE     )   /*!< NVIC configuration struct */\n#define ITM                 ((ITM_Type       *)     ITM_BASE      )   /*!< ITM configuration struct */\n#define DWT                 ((DWT_Type       *)     DWT_BASE      )   /*!< DWT configuration struct */\n#define TPI                 ((TPI_Type       *)     TPI_BASE      )   /*!< TPI configuration struct */\n#define CoreDebug           ((CoreDebug_Type *)     CoreDebug_BASE)   /*!< Core Debug configuration struct */\n\n#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U)\n  #define MPU_BASE          (SCS_BASE +  0x0D90UL)                    /*!< Memory Protection Unit */\n  #define MPU               ((MPU_Type       *)     MPU_BASE      )   /*!< Memory Protection Unit */\n#endif\n\n#define FPU_BASE            (SCS_BASE +  0x0F30UL)                    /*!< Floating Point Unit */\n#define FPU                 ((FPU_Type       *)     FPU_BASE      )   /*!< Floating Point Unit */\n\n/*@} */\n\n\n/**\n  \\ingroup    CMSIS_core_register\n  \\defgroup   CMSIS_register_aliases     Backwards Compatibility Aliases\n  \\brief      Register alias definitions for backwards compatibility.\n  @{\n */\n\n/* Capitalize ITM_TCR Register Definitions */\n\n/* ITM Trace Control Register Definitions */\n#define ITM_TCR_TraceBusID_Pos           (ITM_TCR_TRACEBUSID_Pos)     /*!< \\deprecated ITM_TCR_TraceBusID_Pos */\n#define ITM_TCR_TraceBusID_Msk           (ITM_TCR_TRACEBUSID_Msk)     /*!< \\deprecated ITM_TCR_TraceBusID_Msk */\n\n#define ITM_TCR_TSPrescale_Pos           (ITM_TCR_TSPRESCALE_Pos)     /*!< \\deprecated ITM_TCR_TSPrescale_Pos */\n#define ITM_TCR_TSPrescale_Msk           (ITM_TCR_TSPRESCALE_Msk)     /*!< \\deprecated ITM_TCR_TSPrescale_Msk */\n\n/* ITM Lock Status Register Definitions */\n#define ITM_LSR_ByteAcc_Pos              (ITM_LSR_BYTEACC_Pos)        /*!< \\deprecated ITM_LSR_ByteAcc_Pos */\n#define ITM_LSR_ByteAcc_Msk              (ITM_LSR_BYTEACC_Msk)        /*!< \\deprecated ITM_LSR_ByteAcc_Msk */\n\n#define ITM_LSR_Access_Pos               (ITM_LSR_ACCESS_Pos)         /*!< \\deprecated ITM_LSR_Access_Pos */\n#define ITM_LSR_Access_Msk               (ITM_LSR_ACCESS_Msk)         /*!< \\deprecated ITM_LSR_Access_Msk */\n\n#define ITM_LSR_Present_Pos              (ITM_LSR_PRESENT_Pos)        /*!< \\deprecated ITM_LSR_Present_Pos */\n#define ITM_LSR_Present_Msk              (ITM_LSR_PRESENT_Msk)        /*!< \\deprecated ITM_LSR_Present_Msk */\n\n/*@} */\n\n\n\n/*******************************************************************************\n *                Hardware Abstraction Layer\n  Core Function Interface contains:\n  - Core NVIC Functions\n  - Core SysTick Functions\n  - Core Debug Functions\n  - Core Register Access Functions\n ******************************************************************************/\n/**\n  \\defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference\n*/\n\n\n\n/* ##########################   NVIC functions  #################################### */\n/**\n  \\ingroup  CMSIS_Core_FunctionInterface\n  \\defgroup CMSIS_Core_NVICFunctions NVIC Functions\n  \\brief    Functions that manage interrupts and exceptions via the NVIC.\n  @{\n */\n\n#ifdef CMSIS_NVIC_VIRTUAL\n  #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE\n    #define CMSIS_NVIC_VIRTUAL_HEADER_FILE \"cmsis_nvic_virtual.h\"\n  #endif\n  #include CMSIS_NVIC_VIRTUAL_HEADER_FILE\n#else\n  #define NVIC_SetPriorityGrouping    __NVIC_SetPriorityGrouping\n  #define NVIC_GetPriorityGrouping    __NVIC_GetPriorityGrouping\n  #define NVIC_EnableIRQ              __NVIC_EnableIRQ\n  #define NVIC_GetEnableIRQ           __NVIC_GetEnableIRQ\n  #define NVIC_DisableIRQ             __NVIC_DisableIRQ\n  #define NVIC_GetPendingIRQ          __NVIC_GetPendingIRQ\n  #define NVIC_SetPendingIRQ          __NVIC_SetPendingIRQ\n  #define NVIC_ClearPendingIRQ        __NVIC_ClearPendingIRQ\n  #define NVIC_GetActive              __NVIC_GetActive\n  #define NVIC_SetPriority            __NVIC_SetPriority\n  #define NVIC_GetPriority            __NVIC_GetPriority\n  #define NVIC_SystemReset            __NVIC_SystemReset\n#endif /* CMSIS_NVIC_VIRTUAL */\n\n#ifdef CMSIS_VECTAB_VIRTUAL\n  #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE\n    #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE \"cmsis_vectab_virtual.h\"\n  #endif\n  #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE\n#else\n  #define NVIC_SetVector              __NVIC_SetVector\n  #define NVIC_GetVector              __NVIC_GetVector\n#endif  /* (CMSIS_VECTAB_VIRTUAL) */\n\n#define NVIC_USER_IRQ_OFFSET          16\n\n\n/* The following EXC_RETURN values are saved the LR on exception entry */\n#define EXC_RETURN_HANDLER         (0xFFFFFFF1UL)     /* return to Handler mode, uses MSP after return                               */\n#define EXC_RETURN_THREAD_MSP      (0xFFFFFFF9UL)     /* return to Thread mode, uses MSP after return                                */\n#define EXC_RETURN_THREAD_PSP      (0xFFFFFFFDUL)     /* return to Thread mode, uses PSP after return                                */\n#define EXC_RETURN_HANDLER_FPU     (0xFFFFFFE1UL)     /* return to Handler mode, uses MSP after return, restore floating-point state */\n#define EXC_RETURN_THREAD_MSP_FPU  (0xFFFFFFE9UL)     /* return to Thread mode, uses MSP after return, restore floating-point state  */\n#define EXC_RETURN_THREAD_PSP_FPU  (0xFFFFFFEDUL)     /* return to Thread mode, uses PSP after return, restore floating-point state  */\n\n\n/**\n  \\brief   Set Priority Grouping\n  \\details Sets the priority grouping field using the required unlock sequence.\n           The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field.\n           Only values from 0..7 are used.\n           In case of a conflict between priority grouping and available\n           priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set.\n  \\param [in]      PriorityGroup  Priority grouping field.\n */\n__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)\n{\n  uint32_t reg_value;\n  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);             /* only values 0..7 are used          */\n\n  reg_value  =  SCB->AIRCR;                                                   /* read old register configuration    */\n  reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change               */\n  reg_value  =  (reg_value                                   |\n                ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |\n                (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)  );              /* Insert write key and priority group */\n  SCB->AIRCR =  reg_value;\n}\n\n\n/**\n  \\brief   Get Priority Grouping\n  \\details Reads the priority grouping field from the NVIC Interrupt Controller.\n  \\return                Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field).\n */\n__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void)\n{\n  return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos));\n}\n\n\n/**\n  \\brief   Enable Interrupt\n  \\details Enables a device specific interrupt in the NVIC interrupt controller.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    __COMPILER_BARRIER();\n    NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));\n    __COMPILER_BARRIER();\n  }\n}\n\n\n/**\n  \\brief   Get Interrupt Enable status\n  \\details Returns a device specific interrupt enable status from the NVIC interrupt controller.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\return             0  Interrupt is not enabled.\n  \\return             1  Interrupt is enabled.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));\n  }\n  else\n  {\n    return(0U);\n  }\n}\n\n\n/**\n  \\brief   Disable Interrupt\n  \\details Disables a device specific interrupt in the NVIC interrupt controller.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));\n    __DSB();\n    __ISB();\n  }\n}\n\n\n/**\n  \\brief   Get Pending Interrupt\n  \\details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\return             0  Interrupt status is not pending.\n  \\return             1  Interrupt status is pending.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));\n  }\n  else\n  {\n    return(0U);\n  }\n}\n\n\n/**\n  \\brief   Set Pending Interrupt\n  \\details Sets the pending bit of a device specific interrupt in the NVIC pending register.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));\n  }\n}\n\n\n/**\n  \\brief   Clear Pending Interrupt\n  \\details Clears the pending bit of a device specific interrupt in the NVIC pending register.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));\n  }\n}\n\n\n/**\n  \\brief   Get Active Interrupt\n  \\details Reads the active register in the NVIC and returns the active bit for the device specific interrupt.\n  \\param [in]      IRQn  Device specific interrupt number.\n  \\return             0  Interrupt status is not active.\n  \\return             1  Interrupt status is active.\n  \\note    IRQn must not be negative.\n */\n__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));\n  }\n  else\n  {\n    return(0U);\n  }\n}\n\n\n/**\n  \\brief   Set Interrupt Priority\n  \\details Sets the priority of a device specific interrupt or a processor exception.\n           The interrupt number can be positive to specify a device specific interrupt,\n           or negative to specify a processor exception.\n  \\param [in]      IRQn  Interrupt number.\n  \\param [in]  priority  Priority to set.\n  \\note    The priority cannot be set for every processor exception.\n */\n__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)\n{\n  if ((int32_t)(IRQn) >= 0)\n  {\n    NVIC->IP[((uint32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);\n  }\n  else\n  {\n    SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);\n  }\n}\n\n\n/**\n  \\brief   Get Interrupt Priority\n  \\details Reads the priority of a device specific interrupt or a processor exception.\n           The interrupt number can be positive to specify a device specific interrupt,\n           or negative to specify a processor exception.\n  \\param [in]   IRQn  Interrupt number.\n  \\return             Interrupt Priority.\n                      Value is aligned automatically to the implemented priority bits of the microcontroller.\n */\n__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn)\n{\n\n  if ((int32_t)(IRQn) >= 0)\n  {\n    return(((uint32_t)NVIC->IP[((uint32_t)IRQn)]               >> (8U - __NVIC_PRIO_BITS)));\n  }\n  else\n  {\n    return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS)));\n  }\n}\n\n\n/**\n  \\brief   Encode Priority\n  \\details Encodes the priority for an interrupt with the given priority group,\n           preemptive priority value, and subpriority value.\n           In case of a conflict between priority grouping and available\n           priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set.\n  \\param [in]     PriorityGroup  Used priority group.\n  \\param [in]   PreemptPriority  Preemptive priority value (starting from 0).\n  \\param [in]       SubPriority  Subpriority value (starting from 0).\n  \\return                        Encoded priority. Value can be used in the function \\ref NVIC_SetPriority().\n */\n__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)\n{\n  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);   /* only values 0..7 are used          */\n  uint32_t PreemptPriorityBits;\n  uint32_t SubPriorityBits;\n\n  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);\n  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));\n\n  return (\n           ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) |\n           ((SubPriority     & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL)))\n         );\n}\n\n\n/**\n  \\brief   Decode Priority\n  \\details Decodes an interrupt priority value with a given priority group to\n           preemptive priority value and subpriority value.\n           In case of a conflict between priority grouping and available\n           priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set.\n  \\param [in]         Priority   Priority value, which can be retrieved with the function \\ref NVIC_GetPriority().\n  \\param [in]     PriorityGroup  Used priority group.\n  \\param [out] pPreemptPriority  Preemptive priority value (starting from 0).\n  \\param [out]     pSubPriority  Subpriority value (starting from 0).\n */\n__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority)\n{\n  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);   /* only values 0..7 are used          */\n  uint32_t PreemptPriorityBits;\n  uint32_t SubPriorityBits;\n\n  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);\n  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));\n\n  *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL);\n  *pSubPriority     = (Priority                   ) & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL);\n}\n\n\n/**\n  \\brief   Set Interrupt Vector\n  \\details Sets an interrupt vector in SRAM based interrupt vector table.\n           The interrupt number can be positive to specify a device specific interrupt,\n           or negative to specify a processor exception.\n           VTOR must been relocated to SRAM before.\n  \\param [in]   IRQn      Interrupt number\n  \\param [in]   vector    Address of interrupt handler function\n */\n__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)\n{\n  uint32_t *vectors = (uint32_t *)SCB->VTOR;\n  vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;\n  /* ARM Application Note 321 states that the M4 does not require the architectural barrier */\n}\n\n\n/**\n  \\brief   Get Interrupt Vector\n  \\details Reads an interrupt vector from interrupt vector table.\n           The interrupt number can be positive to specify a device specific interrupt,\n           or negative to specify a processor exception.\n  \\param [in]   IRQn      Interrupt number.\n  \\return                 Address of interrupt handler function\n */\n__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn)\n{\n  uint32_t *vectors = (uint32_t *)SCB->VTOR;\n  return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET];\n}\n\n\n/**\n  \\brief   System Reset\n  \\details Initiates a system reset request to reset the MCU.\n */\n__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)\n{\n  __DSB();                                                          /* Ensure all outstanding memory accesses included\n                                                                       buffered write are completed before reset */\n  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |\n                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |\n                            SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */\n  __DSB();                                                          /* Ensure completion of memory access */\n\n  for(;;)                                                           /* wait until reset */\n  {\n    __NOP();\n  }\n}\n\n/*@} end of CMSIS_Core_NVICFunctions */\n\n\n/* ##########################  MPU functions  #################################### */\n\n#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U)\n\n#include \"mpu_armv7.h\"\n\n#endif\n\n\n/* ##########################  FPU functions  #################################### */\n/**\n  \\ingroup  CMSIS_Core_FunctionInterface\n  \\defgroup CMSIS_Core_FpuFunctions FPU Functions\n  \\brief    Function that provides FPU type.\n  @{\n */\n\n/**\n  \\brief   get FPU type\n  \\details returns the FPU type\n  \\returns\n   - \\b  0: No FPU\n   - \\b  1: Single precision FPU\n   - \\b  2: Double + Single precision FPU\n */\n__STATIC_INLINE uint32_t SCB_GetFPUType(void)\n{\n  uint32_t mvfr0;\n\n  mvfr0 = FPU->MVFR0;\n  if      ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U)\n  {\n    return 1U;           /* Single precision FPU */\n  }\n  else\n  {\n    return 0U;           /* No FPU */\n  }\n}\n\n\n/*@} end of CMSIS_Core_FpuFunctions */\n\n\n\n/* ##################################    SysTick function  ############################################ */\n/**\n  \\ingroup  CMSIS_Core_FunctionInterface\n  \\defgroup CMSIS_Core_SysTickFunctions SysTick Functions\n  \\brief    Functions that configure the System.\n  @{\n */\n\n#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)\n\n/**\n  \\brief   System Tick Configuration\n  \\details Initializes the System Timer and its interrupt, and starts the System Tick Timer.\n           Counter is in free running mode to generate periodic interrupts.\n  \\param [in]  ticks  Number of ticks between two interrupts.\n  \\return          0  Function succeeded.\n  \\return          1  Function failed.\n  \\note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the\n           function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>\n           must contain a vendor-specific implementation of this function.\n */\n__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)\n{\n  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)\n  {\n    return (1UL);                                                   /* Reload value impossible */\n  }\n\n  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */\n  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */\n  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */\n  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |\n                   SysTick_CTRL_TICKINT_Msk   |\n                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */\n  return (0UL);                                                     /* Function successful */\n}\n\n#endif\n\n/*@} end of CMSIS_Core_SysTickFunctions */\n\n\n\n/* ##################################### Debug In/Output function ########################################### */\n/**\n  \\ingroup  CMSIS_Core_FunctionInterface\n  \\defgroup CMSIS_core_DebugFunctions ITM Functions\n  \\brief    Functions that access the ITM debug interface.\n  @{\n */\n\nextern volatile int32_t ITM_RxBuffer;                              /*!< External variable to receive characters. */\n#define                 ITM_RXBUFFER_EMPTY  ((int32_t)0x5AA55AA5U) /*!< Value identifying \\ref ITM_RxBuffer is ready for next character. */\n\n\n/**\n  \\brief   ITM Send Character\n  \\details Transmits a character via the ITM channel 0, and\n           \\li Just returns when no debugger is connected that has booked the output.\n           \\li Is blocking when a debugger is connected, but the previous character sent has not been transmitted.\n  \\param [in]     ch  Character to transmit.\n  \\returns            Character to transmit.\n */\n__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch)\n{\n  if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) &&      /* ITM enabled */\n      ((ITM->TER & 1UL               ) != 0UL)   )     /* ITM Port #0 enabled */\n  {\n    while (ITM->PORT[0U].u32 == 0UL)\n    {\n      __NOP();\n    }\n    ITM->PORT[0U].u8 = (uint8_t)ch;\n  }\n  return (ch);\n}\n\n\n/**\n  \\brief   ITM Receive Character\n  \\details Inputs a character via the external variable \\ref ITM_RxBuffer.\n  \\return             Received character.\n  \\return         -1  No character pending.\n */\n__STATIC_INLINE int32_t ITM_ReceiveChar (void)\n{\n  int32_t ch = -1;                           /* no character available */\n\n  if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY)\n  {\n    ch = ITM_RxBuffer;\n    ITM_RxBuffer = ITM_RXBUFFER_EMPTY;       /* ready for next character */\n  }\n\n  return (ch);\n}\n\n\n/**\n  \\brief   ITM Check Character\n  \\details Checks whether a character is pending for reading in the variable \\ref ITM_RxBuffer.\n  \\return          0  No character available.\n  \\return          1  Character available.\n */\n__STATIC_INLINE int32_t ITM_CheckChar (void)\n{\n\n  if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY)\n  {\n    return (0);                              /* no character available */\n  }\n  else\n  {\n    return (1);                              /*    character available */\n  }\n}\n\n/*@} end of CMSIS_core_DebugFunctions */\n\n\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CORE_CM4_H_DEPENDANT */\n\n#endif /* __CMSIS_GENERIC */\n"
  },
  {
    "path": "lib/cmsis_core/mpu_armv7.h",
    "content": "/******************************************************************************\n * @file     mpu_armv7.h\n * @brief    CMSIS MPU API for Armv7-M MPU\n * @version  V5.1.2\n * @date     25. May 2020\n ******************************************************************************/\n/*\n * Copyright (c) 2017-2020 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \n#if   defined ( __ICCARM__ )\n  #pragma system_include         /* treat file as system include file for MISRA check */\n#elif defined (__clang__)\n  #pragma clang system_header    /* treat file as system include file */\n#endif\n \n#ifndef ARM_MPU_ARMV7_H\n#define ARM_MPU_ARMV7_H\n\n#define ARM_MPU_REGION_SIZE_32B      ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes\n#define ARM_MPU_REGION_SIZE_64B      ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes\n#define ARM_MPU_REGION_SIZE_128B     ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes\n#define ARM_MPU_REGION_SIZE_256B     ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes\n#define ARM_MPU_REGION_SIZE_512B     ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes\n#define ARM_MPU_REGION_SIZE_1KB      ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte\n#define ARM_MPU_REGION_SIZE_2KB      ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes\n#define ARM_MPU_REGION_SIZE_4KB      ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes\n#define ARM_MPU_REGION_SIZE_8KB      ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes\n#define ARM_MPU_REGION_SIZE_16KB     ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes\n#define ARM_MPU_REGION_SIZE_32KB     ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes\n#define ARM_MPU_REGION_SIZE_64KB     ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes\n#define ARM_MPU_REGION_SIZE_128KB    ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes\n#define ARM_MPU_REGION_SIZE_256KB    ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes\n#define ARM_MPU_REGION_SIZE_512KB    ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes\n#define ARM_MPU_REGION_SIZE_1MB      ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte\n#define ARM_MPU_REGION_SIZE_2MB      ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes\n#define ARM_MPU_REGION_SIZE_4MB      ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes\n#define ARM_MPU_REGION_SIZE_8MB      ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes\n#define ARM_MPU_REGION_SIZE_16MB     ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes\n#define ARM_MPU_REGION_SIZE_32MB     ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes\n#define ARM_MPU_REGION_SIZE_64MB     ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes\n#define ARM_MPU_REGION_SIZE_128MB    ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes\n#define ARM_MPU_REGION_SIZE_256MB    ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes\n#define ARM_MPU_REGION_SIZE_512MB    ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes\n#define ARM_MPU_REGION_SIZE_1GB      ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte\n#define ARM_MPU_REGION_SIZE_2GB      ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes\n#define ARM_MPU_REGION_SIZE_4GB      ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes\n\n#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access\n#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only\n#define ARM_MPU_AP_URO  2U ///!< MPU Access Permission unprivileged access read-only\n#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access\n#define ARM_MPU_AP_PRO  5U ///!< MPU Access Permission privileged access read-only\n#define ARM_MPU_AP_RO   6U ///!< MPU Access Permission read-only access\n\n/** MPU Region Base Address Register Value\n*\n* \\param Region The region to be configured, number 0 to 15.\n* \\param BaseAddress The base address for the region.\n*/\n#define ARM_MPU_RBAR(Region, BaseAddress) \\\n  (((BaseAddress) & MPU_RBAR_ADDR_Msk) |  \\\n   ((Region) & MPU_RBAR_REGION_Msk)    |  \\\n   (MPU_RBAR_VALID_Msk))\n\n/**\n* MPU Memory Access Attributes\n* \n* \\param TypeExtField      Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.\n* \\param IsShareable       Region is shareable between multiple bus masters.\n* \\param IsCacheable       Region is cacheable, i.e. its value may be kept in cache.\n* \\param IsBufferable      Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.\n*/  \n#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable)   \\\n  ((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk)                  | \\\n   (((IsShareable)  << MPU_RASR_S_Pos)   & MPU_RASR_S_Msk)                    | \\\n   (((IsCacheable)  << MPU_RASR_C_Pos)   & MPU_RASR_C_Msk)                    | \\\n   (((IsBufferable) << MPU_RASR_B_Pos)   & MPU_RASR_B_Msk))\n\n/**\n* MPU Region Attribute and Size Register Value\n* \n* \\param DisableExec       Instruction access disable bit, 1= disable instruction fetches.\n* \\param AccessPermission  Data access permissions, allows you to configure read/write access for User and Privileged mode.\n* \\param AccessAttributes  Memory access attribution, see \\ref ARM_MPU_ACCESS_.\n* \\param SubRegionDisable  Sub-region disable field.\n* \\param Size              Region size of the region to be configured, for example 4K, 8K.\n*/\n#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size)    \\\n  ((((DisableExec)      << MPU_RASR_XN_Pos)   & MPU_RASR_XN_Msk)                                  | \\\n   (((AccessPermission) << MPU_RASR_AP_Pos)   & MPU_RASR_AP_Msk)                                  | \\\n   (((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \\\n   (((SubRegionDisable) << MPU_RASR_SRD_Pos)  & MPU_RASR_SRD_Msk)                                 | \\\n   (((Size)             << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk)                                | \\\n   (((MPU_RASR_ENABLE_Msk))))\n\n/**\n* MPU Region Attribute and Size Register Value\n* \n* \\param DisableExec       Instruction access disable bit, 1= disable instruction fetches.\n* \\param AccessPermission  Data access permissions, allows you to configure read/write access for User and Privileged mode.\n* \\param TypeExtField      Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.\n* \\param IsShareable       Region is shareable between multiple bus masters.\n* \\param IsCacheable       Region is cacheable, i.e. its value may be kept in cache.\n* \\param IsBufferable      Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.\n* \\param SubRegionDisable  Sub-region disable field.\n* \\param Size              Region size of the region to be configured, for example 4K, 8K.\n*/                         \n#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \\\n  ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size)\n\n/**\n* MPU Memory Access Attribute for strongly ordered memory.\n*  - TEX: 000b\n*  - Shareable\n*  - Non-cacheable\n*  - Non-bufferable\n*/ \n#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U)\n\n/**\n* MPU Memory Access Attribute for device memory.\n*  - TEX: 000b (if shareable) or 010b (if non-shareable)\n*  - Shareable or non-shareable\n*  - Non-cacheable\n*  - Bufferable (if shareable) or non-bufferable (if non-shareable)\n*\n* \\param IsShareable Configures the device memory as shareable or non-shareable.\n*/ \n#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U))\n\n/**\n* MPU Memory Access Attribute for normal memory.\n*  - TEX: 1BBb (reflecting outer cacheability rules)\n*  - Shareable or non-shareable\n*  - Cacheable or non-cacheable (reflecting inner cacheability rules)\n*  - Bufferable or non-bufferable (reflecting inner cacheability rules)\n*\n* \\param OuterCp Configures the outer cache policy.\n* \\param InnerCp Configures the inner cache policy.\n* \\param IsShareable Configures the memory as shareable or non-shareable.\n*/ \n#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) >> 1U), ((InnerCp) & 1U))\n\n/**\n* MPU Memory Access Attribute non-cacheable policy.\n*/\n#define ARM_MPU_CACHEP_NOCACHE 0U\n\n/**\n* MPU Memory Access Attribute write-back, write and read allocate policy.\n*/\n#define ARM_MPU_CACHEP_WB_WRA 1U\n\n/**\n* MPU Memory Access Attribute write-through, no write allocate policy.\n*/\n#define ARM_MPU_CACHEP_WT_NWA 2U\n\n/**\n* MPU Memory Access Attribute write-back, no write allocate policy.\n*/\n#define ARM_MPU_CACHEP_WB_NWA 3U\n\n\n/**\n* Struct for a single MPU Region\n*/\ntypedef struct {\n  uint32_t RBAR; //!< The region base address register value (RBAR)\n  uint32_t RASR; //!< The region attribute and size register value (RASR) \\ref MPU_RASR\n} ARM_MPU_Region_t;\n    \n/** Enable the MPU.\n* \\param MPU_Control Default access permissions for unconfigured regions.\n*/\n__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control)\n{\n  __DMB();\n  MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;\n#ifdef SCB_SHCSR_MEMFAULTENA_Msk\n  SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;\n#endif\n  __DSB();\n  __ISB();\n}\n\n/** Disable the MPU.\n*/\n__STATIC_INLINE void ARM_MPU_Disable(void)\n{\n  __DMB();\n#ifdef SCB_SHCSR_MEMFAULTENA_Msk\n  SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;\n#endif\n  MPU->CTRL  &= ~MPU_CTRL_ENABLE_Msk;\n  __DSB();\n  __ISB();\n}\n\n/** Clear and disable the given MPU region.\n* \\param rnr Region number to be cleared.\n*/\n__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr)\n{\n  MPU->RNR = rnr;\n  MPU->RASR = 0U;\n}\n\n/** Configure an MPU region.\n* \\param rbar Value for RBAR register.\n* \\param rasr Value for RASR register.\n*/   \n__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr)\n{\n  MPU->RBAR = rbar;\n  MPU->RASR = rasr;\n}\n\n/** Configure the given MPU region.\n* \\param rnr Region number to be configured.\n* \\param rbar Value for RBAR register.\n* \\param rasr Value for RASR register.\n*/   \n__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr)\n{\n  MPU->RNR = rnr;\n  MPU->RBAR = rbar;\n  MPU->RASR = rasr;\n}\n\n/** Memcpy with strictly ordered memory access, e.g. used by code in ARM_MPU_Load().\n* \\param dst Destination data is copied to.\n* \\param src Source data is copied from.\n* \\param len Amount of data words to be copied.\n*/\n__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len)\n{\n  uint32_t i;\n  for (i = 0U; i < len; ++i) \n  {\n    dst[i] = src[i];\n  }\n}\n\n/** Load the given number of MPU regions from a table.\n* \\param table Pointer to the MPU configuration table.\n* \\param cnt Amount of regions to be configured.\n*/\n__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) \n{\n  const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U;\n  while (cnt > MPU_TYPE_RALIASES) {\n    ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize);\n    table += MPU_TYPE_RALIASES;\n    cnt -= MPU_TYPE_RALIASES;\n  }\n  ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize);\n}\n\n#endif\n"
  },
  {
    "path": "lib/datetime/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    CPPPATH=[\n        \"#/lib/datetime\",\n    ],\n    SDK_HEADERS=[\n        File(\"datetime.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"datetime\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/datetime/datetime.c",
    "content": "#include \"datetime.h\"\n#include <furi.h>\n\n#define TAG \"DateTime\"\n\n#define SECONDS_PER_MINUTE 60\n#define SECONDS_PER_HOUR   (SECONDS_PER_MINUTE * 60)\n#define SECONDS_PER_DAY    (SECONDS_PER_HOUR * 24)\n#define MONTHS_COUNT       12\n#define EPOCH_START_YEAR   1970\n\nstatic const uint8_t datetime_days_per_month[2][MONTHS_COUNT] = {\n    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},\n    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};\n\nstatic const uint16_t datetime_days_per_year[] = {365, 366};\n\nbool datetime_validate_datetime(DateTime* datetime) {\n    bool invalid = false;\n\n    invalid |= (datetime->second > 59);\n    invalid |= (datetime->minute > 59);\n    invalid |= (datetime->hour > 23);\n\n    invalid |= (datetime->year < 2000);\n    invalid |= (datetime->year > 2099);\n\n    invalid |= (datetime->month == 0);\n    invalid |= (datetime->month > 12);\n\n    invalid |= (datetime->day == 0);\n    invalid |= (datetime->day > 31);\n\n    invalid |= (datetime->weekday == 0);\n    invalid |= (datetime->weekday > 7);\n\n    return !invalid;\n}\n\nuint32_t datetime_datetime_to_timestamp(DateTime* datetime) {\n    furi_check(datetime);\n\n    uint32_t timestamp = 0;\n    uint8_t years = 0;\n    uint8_t leap_years = 0;\n\n    for(uint16_t y = EPOCH_START_YEAR; y < datetime->year; y++) {\n        if(datetime_is_leap_year(y)) {\n            leap_years++;\n        } else {\n            years++;\n        }\n    }\n\n    timestamp += ((years * datetime_days_per_year[0]) + (leap_years * datetime_days_per_year[1])) *\n                 SECONDS_PER_DAY;\n\n    bool leap_year = datetime_is_leap_year(datetime->year);\n\n    for(uint8_t m = 1; m < datetime->month; m++) {\n        timestamp += datetime_get_days_per_month(leap_year, m) * SECONDS_PER_DAY;\n    }\n\n    timestamp += (datetime->day - 1) * SECONDS_PER_DAY;\n    timestamp += datetime->hour * SECONDS_PER_HOUR;\n    timestamp += datetime->minute * SECONDS_PER_MINUTE;\n    timestamp += datetime->second;\n\n    return timestamp;\n}\n\nvoid datetime_timestamp_to_datetime(uint32_t timestamp, DateTime* datetime) {\n    furi_check(datetime);\n    uint32_t days = timestamp / SECONDS_PER_DAY;\n    uint32_t seconds_in_day = timestamp % SECONDS_PER_DAY;\n\n    datetime->year = EPOCH_START_YEAR;\n    datetime->weekday = ((days + 3) % 7) + 1;\n\n    while(days >= datetime_get_days_per_year(datetime->year)) {\n        days -= datetime_get_days_per_year(datetime->year);\n        (datetime->year)++;\n    }\n\n    datetime->month = 1;\n    while(days >=\n          datetime_get_days_per_month(datetime_is_leap_year(datetime->year), datetime->month)) {\n        days -=\n            datetime_get_days_per_month(datetime_is_leap_year(datetime->year), datetime->month);\n        (datetime->month)++;\n    }\n\n    datetime->day = days + 1;\n    datetime->hour = seconds_in_day / SECONDS_PER_HOUR;\n    datetime->minute = (seconds_in_day % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;\n    datetime->second = seconds_in_day % SECONDS_PER_MINUTE;\n}\n\nuint16_t datetime_get_days_per_year(uint16_t year) {\n    return datetime_days_per_year[datetime_is_leap_year(year) ? 1 : 0];\n}\n\nbool datetime_is_leap_year(uint16_t year) {\n    return (((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0);\n}\n\nuint8_t datetime_get_days_per_month(bool leap_year, uint8_t month) {\n    return datetime_days_per_month[leap_year ? 1 : 0][month - 1];\n}\n"
  },
  {
    "path": "lib/datetime/datetime.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    // Time\n    uint8_t hour; /**< Hour in 24H format: 0-23 */\n    uint8_t minute; /**< Minute: 0-59 */\n    uint8_t second; /**< Second: 0-59 */\n    // Date\n    uint8_t day; /**< Current day: 1-31 */\n    uint8_t month; /**< Current month: 1-12 */\n    uint16_t year; /**< Current year: 2000-2099 */\n    uint8_t weekday; /**< Current weekday: 1-7 */\n} DateTime;\n\n/** Validate Date Time\n *\n * @param      datetime  The datetime to validate\n *\n * @return     { description_of_the_return_value }\n */\nbool datetime_validate_datetime(DateTime* datetime);\n\n/** Convert DateTime to UNIX timestamp\n * \n * @warning    Mind timezone when perform conversion\n *\n * @param      datetime  The datetime (UTC)\n *\n * @return     UNIX Timestamp in seconds from UNIX epoch start\n */\nuint32_t datetime_datetime_to_timestamp(DateTime* datetime);\n\n/** Convert UNIX timestamp to DateTime\n *\n * @warning    Mind timezone when perform conversion\n *\n * @param[in]  timestamp  UNIX Timestamp in seconds from UNIX epoch start\n * @param[out] datetime   The datetime (UTC)\n */\nvoid datetime_timestamp_to_datetime(uint32_t timestamp, DateTime* datetime);\n\n/** Gets the number of days in the year according to the Gregorian calendar.\n *\n * @param year Input year.\n *\n * @return number of days in `year`.\n */\nuint16_t datetime_get_days_per_year(uint16_t year);\n\n/** Check if a year a leap year in the Gregorian calendar.\n *\n * @param year Input year.\n *\n * @return true if `year` is a leap year.\n */\nbool datetime_is_leap_year(uint16_t year);\n\n/** Get the number of days in the month.\n *\n * @param leap_year true to calculate based on leap years\n * @param month month to check, where 1 = January\n * @return the number of days in the month\n */\nuint8_t datetime_get_days_per_month(bool leap_year, uint8_t month);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/digital_signal/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/digital_signal\",\n    ],\n    SDK_HEADERS=[\n        File(\"digital_signal.h\"),\n        File(\"digital_sequence.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"digital_signal\")\nlibenv.ApplyLibFlags()\nlibenv.Append(CCFLAGS=[\"-O3\", \"-funroll-loops\", \"-Ofast\"])\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/digital_signal/digital_sequence.c",
    "content": "#include \"digital_sequence.h\"\n#include \"digital_signal_i.h\"\n\n#include <furi.h>\n#include <furi_hal_bus.h>\n\n#include <stm32wbxx_ll_dma.h>\n#include <stm32wbxx_ll_tim.h>\n\n/**\n * To enable debug output on an additional pin, set DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN to the required\n * GpioPin variable. It can be passed at compile time via the --extra-define fbt switch.\n * NOTE: This pin must be on the same GPIO port as the main pin.\n *\n * Example:\n * ./fbt --extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3\n */\n#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN\n#include <furi_hal.h>\n#endif\n\n#define TAG \"DigitalSequence\"\n\n/* Special value used to indicate the end of DMA ring buffer. */\n#define DIGITAL_SEQUENCE_TIMER_MAX 0xFFFFFFFFUL\n\n/* Time to wait in loops before returning */\n#define DIGITAL_SEQUENCE_LOCK_WAIT_MS    10UL\n#define DIGITAL_SEQUENCE_LOCK_WAIT_TICKS (DIGITAL_SEQUENCE_LOCK_WAIT_MS * 1000 * 64)\n\n#define DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE 2\n\n/* Maximum capacity of the DMA ring buffer. */\n#define DIGITAL_SEQUENCE_RING_BUFFER_SIZE 128\n\n#define DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE 2\n\n/* Maximum amount of registered signals. */\n#define DIGITAL_SEQUENCE_BANK_SIZE 32\n\ntypedef enum {\n    DigitalSequenceStateIdle,\n    DigitalSequenceStateActive,\n} DigitalSequenceState;\n\ntypedef struct {\n    uint32_t data[DIGITAL_SEQUENCE_RING_BUFFER_SIZE];\n    uint32_t write_pos;\n    uint32_t read_pos;\n} DigitalSequenceRingBuffer;\n\ntypedef uint32_t DigitalSequenceGpioBuffer[DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE];\n\ntypedef const DigitalSignal* DigitalSequenceSignalBank[DIGITAL_SEQUENCE_BANK_SIZE];\n\nstruct DigitalSequence {\n    const GpioPin* gpio;\n\n    uint32_t size;\n    uint32_t max_size;\n\n    LL_DMA_InitTypeDef dma_config_gpio;\n    LL_DMA_InitTypeDef dma_config_timer;\n\n    DigitalSequenceGpioBuffer gpio_buf;\n    DigitalSequenceRingBuffer timer_buf;\n    DigitalSequenceSignalBank signals;\n    DigitalSequenceState state;\n\n    uint8_t data[];\n};\n\nDigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {\n    furi_assert(size);\n    furi_assert(gpio);\n\n    DigitalSequence* sequence = malloc(sizeof(DigitalSequence) + size);\n\n    sequence->gpio = gpio;\n    sequence->max_size = size;\n\n    sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR;\n    sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buf;\n    sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;\n    sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;\n    sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;\n    sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;\n    sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;\n    sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;\n    sequence->dma_config_gpio.NbData = DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE;\n    sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;\n    sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;\n\n    sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR;\n    sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->timer_buf.data;\n    sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;\n    sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;\n    sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;\n    sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;\n    sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;\n    sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;\n    sequence->dma_config_timer.NbData = DIGITAL_SEQUENCE_RING_BUFFER_SIZE;\n    sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;\n    sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;\n\n    return sequence;\n}\n\nvoid digital_sequence_free(DigitalSequence* sequence) {\n    furi_assert(sequence);\n\n    free(sequence);\n}\n\nvoid digital_sequence_register_signal(\n    DigitalSequence* sequence,\n    uint8_t signal_index,\n    const DigitalSignal* signal) {\n    furi_check(sequence);\n    furi_check(signal);\n    furi_check(signal_index < DIGITAL_SEQUENCE_BANK_SIZE);\n\n    sequence->signals[signal_index] = signal;\n}\n\nvoid digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index) {\n    furi_check(sequence);\n    furi_check(signal_index < DIGITAL_SEQUENCE_BANK_SIZE);\n    furi_check(sequence->size < sequence->max_size);\n\n    sequence->data[sequence->size++] = signal_index;\n}\n\nstatic inline void digital_sequence_start_dma(DigitalSequence* sequence) {\n    furi_assert(sequence);\n\n    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio);\n    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer);\n\n    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);\n    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);\n}\n\nstatic inline void digital_sequence_stop_dma(void) {\n    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);\n    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);\n    LL_DMA_ClearFlag_TC1(DMA1);\n    LL_DMA_ClearFlag_TC2(DMA1);\n}\n\nstatic inline void digital_sequence_start_timer(void) {\n    furi_hal_bus_enable(FuriHalBusTIM2);\n\n    LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);\n    LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);\n    LL_TIM_SetPrescaler(TIM2, 0);\n    LL_TIM_SetAutoReload(TIM2, DIGITAL_SEQUENCE_TIMER_MAX);\n    LL_TIM_SetCounter(TIM2, 0);\n\n    LL_TIM_EnableCounter(TIM2);\n    LL_TIM_EnableUpdateEvent(TIM2);\n    LL_TIM_EnableDMAReq_UPDATE(TIM2);\n    LL_TIM_GenerateEvent_UPDATE(TIM2);\n}\n\nstatic void digital_sequence_stop_timer(void) {\n    LL_TIM_DisableCounter(TIM2);\n    LL_TIM_DisableUpdateEvent(TIM2);\n    LL_TIM_DisableDMAReq_UPDATE(TIM2);\n\n    furi_hal_bus_disable(FuriHalBusTIM2);\n}\n\nstatic inline void digital_sequence_init_gpio_buffer(\n    DigitalSequence* sequence,\n    const DigitalSignal* first_signal) {\n    const uint32_t bit_set = sequence->gpio->pin << GPIO_BSRR_BS0_Pos\n#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN\n                             | DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BS0_Pos\n#endif\n        ;\n\n    const uint32_t bit_reset = sequence->gpio->pin << GPIO_BSRR_BR0_Pos\n#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN\n                               | DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BR0_Pos\n#endif\n        ;\n\n    if(first_signal->start_level) {\n        sequence->gpio_buf[0] = bit_set;\n        sequence->gpio_buf[1] = bit_reset;\n    } else {\n        sequence->gpio_buf[0] = bit_reset;\n        sequence->gpio_buf[1] = bit_set;\n    }\n}\n\nstatic inline void digital_sequence_finish(DigitalSequence* sequence) {\n    if(sequence->state == DigitalSequenceStateActive) {\n        const uint32_t prev_timer = DWT->CYCCNT;\n\n        do {\n            /* Special value has been loaded into the timer, signaling the end of transmission. */\n            if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) {\n                break;\n            }\n\n            if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) {\n                DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf;\n                dma_buffer->read_pos = DIGITAL_SEQUENCE_RING_BUFFER_SIZE -\n                                       LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);\n                FURI_LOG_D(\n                    TAG,\n                    \"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)\",\n                    DIGITAL_SEQUENCE_LOCK_WAIT_MS,\n                    TIM2->ARR,\n                    dma_buffer->read_pos,\n                    dma_buffer->write_pos);\n                break;\n            }\n        } while(true);\n    }\n\n    digital_sequence_stop_timer();\n    digital_sequence_stop_dma();\n}\n\nstatic inline void digital_sequence_enqueue_period(DigitalSequence* sequence, uint32_t length) {\n    DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf;\n\n    if(sequence->state == DigitalSequenceStateActive) {\n        const uint32_t prev_timer = DWT->CYCCNT;\n\n        do {\n            dma_buffer->read_pos =\n                DIGITAL_SEQUENCE_RING_BUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);\n\n            const uint32_t size_free = (DIGITAL_SEQUENCE_RING_BUFFER_SIZE + dma_buffer->read_pos -\n                                        dma_buffer->write_pos) %\n                                       DIGITAL_SEQUENCE_RING_BUFFER_SIZE;\n\n            if(size_free > DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE) {\n                break;\n            }\n\n            if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) {\n                FURI_LOG_D(\n                    TAG,\n                    \"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)\",\n                    DIGITAL_SEQUENCE_LOCK_WAIT_MS,\n                    TIM2->ARR,\n                    dma_buffer->read_pos,\n                    dma_buffer->write_pos);\n                break;\n            }\n\n            if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) {\n                FURI_LOG_D(\n                    TAG,\n                    \"[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)\",\n                    TIM2->ARR,\n                    dma_buffer->read_pos,\n                    dma_buffer->write_pos);\n                break;\n            }\n        } while(true);\n    }\n\n    dma_buffer->data[dma_buffer->write_pos] = length;\n\n    dma_buffer->write_pos += 1;\n    dma_buffer->write_pos %= DIGITAL_SEQUENCE_RING_BUFFER_SIZE;\n\n    dma_buffer->data[dma_buffer->write_pos] = DIGITAL_SEQUENCE_TIMER_MAX;\n}\n\nstatic inline void digital_sequence_timer_buffer_reset(DigitalSequence* sequence) {\n    sequence->timer_buf.data[0] = DIGITAL_SEQUENCE_TIMER_MAX;\n    sequence->timer_buf.read_pos = 0;\n    sequence->timer_buf.write_pos = 0;\n}\n\nvoid digital_sequence_transmit(DigitalSequence* sequence) {\n    furi_check(sequence);\n    furi_check(sequence->size);\n    furi_check(sequence->state == DigitalSequenceStateIdle);\n\n    FURI_CRITICAL_ENTER();\n\n    furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);\n#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN\n    furi_hal_gpio_init(\n        &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);\n#endif\n\n    const DigitalSignal* signal_current = sequence->signals[sequence->data[0]];\n\n    digital_sequence_init_gpio_buffer(sequence, signal_current);\n\n    int32_t remainder_ticks = 0;\n    uint32_t reload_value_carry = 0;\n    uint32_t next_signal_index = 1;\n\n    for(;;) {\n        const DigitalSignal* signal_next =\n            (next_signal_index < sequence->size) ?\n                sequence->signals[sequence->data[next_signal_index++]] :\n                NULL;\n\n        for(uint32_t i = 0; i < signal_current->size; i++) {\n            const bool is_last_value = (i == signal_current->size - 1);\n            const uint32_t reload_value = signal_current->data[i] + reload_value_carry;\n\n            reload_value_carry = 0;\n\n            if(is_last_value) {\n                if(signal_next != NULL) {\n                    /* Special case: signal boundary. Depending on whether the adjacent levels are equal or not,\n                     * they will be combined to a single one or handled separately. */\n                    const bool end_level = signal_current->start_level ^\n                                           ((signal_current->size % 2) == 0);\n\n                    /* If the adjacent levels are equal, carry the current period duration over to the next signal. */\n                    if(end_level == signal_next->start_level) {\n                        reload_value_carry = reload_value;\n                    }\n                } else {\n                    /** Special case: during the last period of the last signal, hold the output level indefinitely.\n                     * @see digital_signal.h\n                     *\n                     * Setting reload_value_carry to a non-zero value will prevent the respective period from being\n                     * added to the DMA ring buffer. */\n                    reload_value_carry = 1;\n                }\n            }\n\n            /* A non-zero reload_value_carry means that the level was the same on the both sides of the signal boundary\n             * and the two respective periods were combined to one. */\n            if(reload_value_carry == 0) {\n                digital_sequence_enqueue_period(sequence, reload_value);\n            }\n\n            if(sequence->state == DigitalSequenceStateIdle) {\n                const bool is_buffer_filled = sequence->timer_buf.write_pos >=\n                                              (DIGITAL_SEQUENCE_RING_BUFFER_SIZE -\n                                               DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE);\n                const bool is_end_of_data = (signal_next == NULL) && is_last_value;\n\n                if(is_buffer_filled || is_end_of_data) {\n                    digital_sequence_start_dma(sequence);\n                    digital_sequence_start_timer();\n                    sequence->state = DigitalSequenceStateActive;\n                }\n            }\n        }\n\n        /* Exit the loop here when no further signals are available */\n        if(signal_next == NULL) break;\n\n        /* Prevent the rounding error from accumulating by distributing it across multiple periods. */\n        remainder_ticks += signal_current->remainder;\n        if(remainder_ticks >= DIGITAL_SIGNAL_T_TIM_DIV2) {\n            remainder_ticks -= DIGITAL_SIGNAL_T_TIM;\n            reload_value_carry += 1;\n        }\n\n        signal_current = signal_next;\n    };\n\n    digital_sequence_finish(sequence);\n    digital_sequence_timer_buffer_reset(sequence);\n\n    FURI_CRITICAL_EXIT();\n\n    sequence->state = DigitalSequenceStateIdle;\n}\n\nvoid digital_sequence_clear(DigitalSequence* sequence) {\n    furi_assert(sequence);\n\n    sequence->size = 0;\n}\n"
  },
  {
    "path": "lib/digital_signal/digital_sequence.h",
    "content": "/**\n * @file digital_sequence.h\n * @brief Fast and precise digital signal generation library.\n *\n * Each sequence is represented by one or more (up to 32) registered signals, which are addressed\n * by their indices, and a list of signal indices to be transmitted.\n *\n * The registered signals must be set up prior to actually defining the sequence.\n *\n * Example: A sequence containing 4 registered signals and n indices to transmit.\n *\n * |Signal | Index |\n * |:-----:|:-----:|\n * |  SOF  |   0   |\n * |  EOF  |   1   |\n * | Zero  |   2   |\n * |  One  |   3   |\n *\n * ```\n * Signal index | 0 | 3 | 2 | 2 | ... |   3   |   1   |\n *                0   1   2   3   ...   n - 2   n - 1\n * ```\n *\n * The above sequence starts by transmitting the signal with index 0, which is SOF in this case,\n * then it proceeds with indices 3, 2, 2, which are One, Zero, Zero and after n - 2 signals,\n * it will conclude with indices 3 and 1 which are One and EOF respectively.\n *\n * This way, only the order in which the signals are sent is stored, while the signals themselves\n * are not duplicated.\n */\n#pragma once\n\n#include <furi_hal_gpio.h>\n\n#include <digital_signal/digital_signal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct DigitalSequence DigitalSequence;\n\n/**\n * @brief Allocate a DigitalSequence instance of a given size which will operate on a set GPIO pin.\n *\n * @param[in] size maximum number of signal indices contained in the instance.\n * @param[in] gpio the GPIO pin used to generate the signal.\n * @returns pointer to the allocated DigitalSequence instance.\n */\nDigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio);\n\n/**\n * @brief Delete a previously allocated DigitalSequence instance.\n *\n * @param[in,out] sequence pointer to the instance to be deleted.\n */\nvoid digital_sequence_free(DigitalSequence* sequence);\n\n/**\n * @brief Register a signal within a DigitalSequence instance by its index.\n *\n * This function must be called for each signal to be used in the sequence. The DigitalSequence\n * instance does not own the signals, therefore, their lifetime must be no less than the instance's.\n *\n * The user is responsible for creation and deletion of DigitalSignal instances and\n * also for keeping track of their respective indices.\n *\n * @param[in,out] sequence pointer to the instance to be modified.\n * @param[in] signal_index index to register the signal under (must be less than 32).\n * @param[in] signal pointer to the DigitalSignal instance to be registered.\n */\nvoid digital_sequence_register_signal(\n    DigitalSequence* sequence,\n    uint8_t signal_index,\n    const DigitalSignal* signal);\n\n/**\n * @brief Append a signal index to a DigitalSequence instance.\n *\n * The signal under the index must be registered beforehand by calling digital_sequence_set_signal().\n *\n * @param[in,out] sequence pointer to the instance to be modified.\n * @param[in] signal_index signal index to be appended to the sequence (must be less than 32).\n */\nvoid digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index);\n\n/**\n * @brief Transmit the sequence contained in the DigitalSequence instance.\n *\n * Must contain at least one registered signal and one signal index.\n *\n * NOTE: The current implementation will properly initialise the GPIO provided during construction,\n * but it is the caller's responsibility to reconfigure it back before reusing for other purposes.\n * This is due to performance reasons.\n *\n * @param[in] sequence pointer to the sequence to be transmitted.\n */\nvoid digital_sequence_transmit(DigitalSequence* sequence);\n\n/**\n * @brief Clear the signal sequence in a DigitalSequence instance.\n *\n * Calling this function does not un-register the registered signals, so it is\n * safe to call digital_sequence_add_signal() right afterwards.\n *\n * @param[in,out] sequence pointer to the instance to be cleared.\n */\nvoid digital_sequence_clear(DigitalSequence* sequence);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/digital_signal/digital_signal.c",
    "content": "#include \"digital_signal.h\"\n#include \"digital_signal_i.h\"\n\n#include <furi.h>\n\n#define TAG \"DigitalSignal\"\n\nDigitalSignal* digital_signal_alloc(uint32_t max_size) {\n    DigitalSignal* signal = malloc(sizeof(DigitalSignal) + (max_size * sizeof(uint32_t)));\n\n    signal->max_size = max_size;\n\n    return signal;\n}\n\nvoid digital_signal_free(DigitalSignal* signal) {\n    furi_check(signal);\n\n    free(signal);\n}\n\nbool digital_signal_get_start_level(const DigitalSignal* signal) {\n    furi_check(signal);\n\n    return signal->start_level;\n}\n\nvoid digital_signal_set_start_level(DigitalSignal* signal, bool level) {\n    furi_check(signal);\n\n    signal->start_level = level;\n}\n\nuint32_t digital_signal_get_size(const DigitalSignal* signal) {\n    furi_check(signal);\n\n    return signal->size;\n}\n\nvoid digital_signal_add_period(DigitalSignal* signal, uint32_t ticks) {\n    furi_check(signal);\n    furi_check(signal->size < signal->max_size);\n\n    const uint32_t duration = ticks + signal->remainder;\n\n    uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM;\n    int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM;\n\n    if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) {\n        reload_value += 1;\n        remainder -= DIGITAL_SIGNAL_T_TIM;\n    }\n\n    furi_check(reload_value > 1);\n\n    signal->data[signal->size++] = reload_value - 1;\n    signal->remainder = remainder;\n}\n\nstatic void digital_signal_extend_last_period(DigitalSignal* signal, uint32_t ticks) {\n    furi_assert(signal->size <= signal->max_size);\n\n    const uint32_t reload_value_old = signal->data[signal->size - 1] + 1;\n    const uint32_t duration = ticks + signal->remainder + reload_value_old * DIGITAL_SIGNAL_T_TIM;\n\n    uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM;\n    int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM;\n\n    if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) {\n        reload_value += 1;\n        remainder -= DIGITAL_SIGNAL_T_TIM;\n    }\n\n    furi_check(reload_value > 1);\n\n    signal->data[signal->size - 1] = reload_value - 1;\n    signal->remainder = remainder;\n}\n\nvoid digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level) {\n    furi_check(signal);\n\n    if(signal->size == 0) {\n        signal->start_level = level;\n        digital_signal_add_period(signal, ticks);\n    } else {\n        const bool end_level = signal->start_level ^ !(signal->size % 2);\n\n        if(level != end_level) {\n            digital_signal_add_period(signal, ticks);\n        } else {\n            digital_signal_extend_last_period(signal, ticks);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/digital_signal/digital_signal.h",
    "content": "/**\n * @file digital_signal.h\n * @brief Simple digital signal container for the DigitalSequence library.\n *\n * Each signal is represented by its start level (high or low) and one or more periods.\n * The output will transition to its inverse value on each period boundary.\n *\n * Example: A signal with n periods and HIGH start level.\n *\n * ```\n * ----+        +------+    +- ... -+\n *  t0 |   t1   |  t2  | t3 |       | tn - 1\n *     +--------+      +----+       +--------\n * ```\n *\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// DigitalSignal uses 10 picosecond time units (1 tick = 10 ps).\n// Use the macros below to convert the time from other units.\n\n#define DIGITAL_SIGNAL_MS(x) ((x) * 100000000UL)\n#define DIGITAL_SIGNAL_US(x) ((x) * 100000UL)\n#define DIGITAL_SIGNAL_NS(x) ((x) * 100UL)\n#define DIGITAL_SIGNAL_PS(x) ((x) / 10UL)\n\ntypedef struct DigitalSignal DigitalSignal;\n\n/**\n * @brief Allocate a DigitalSignal instance with a defined maximum size.\n *\n * @param[in] max_size the maximum number of periods the instance will be able to contain.\n * @returns pointer to the allocated instance.\n */\nDigitalSignal* digital_signal_alloc(uint32_t max_size);\n\n/**\n * @brief Delete a previously allocated DigitalSignal instance.\n *\n * @param[in,out] signal pointer to the instance to be deleted.\n */\nvoid digital_signal_free(DigitalSignal* signal);\n\n/**\n * @brief Append one period to the end of the DigitalSignal instance.\n *\n * @param[in,out] signal pointer to a the instance to append to.\n * @param[in] ticks the period length, in 10 picosecond units.\n */\nvoid digital_signal_add_period(DigitalSignal* signal, uint32_t ticks);\n\n/**\n * @brief Append one period to the end of the DigitalSignal instance, with the level specified.\n *\n * If the level is the same as the last level contained in the instance, then it is extened\n * by the given ticks value. Otherwise, the behaviour is identical to digital_signal_add_period().\n *\n * Example 1: add tc with HIGH level\n * ```\n * before:\n * ... ------+\n *       ta  |  tb\n *           +-------\n * after:\n * ... ------+      +-------\n *       ta  |  tb  |  tc\n *           +------+\n * ```\n * Example 2: add tc with LOW level\n * ```\n * before:\n * ... ------+\n *       ta  |  tb\n *           +-------\n * after:\n * ... ------+\n *       ta  |   tb + tc\n *           +--------------\n * ```\n *\n * @param[in,out] signal pointer to the instance to append to.\n * @param[in] ticks the period length, in 10 picosecond units.\n * @param[in] level the level to be set during the period.\n */\nvoid digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level);\n\n/**\n * @brief Get the current start level contained in the DigitalSignal instance.\n *\n * If not explicitly set with digital_signal_set_start_level(), it defaults to false.\n *\n * @param[in] signal pointer to the instance to be queried.\n * @returns the start level value.\n */\nbool digital_signal_get_start_level(const DigitalSignal* signal);\n\n/**\n * @brief Set the start level contained in the DigitalSignal instance.\n *\n * @param[in,out] signal pointer to the instance to be modified.\n * @param[in] level signal level to be set as the start level.\n */\nvoid digital_signal_set_start_level(DigitalSignal* signal, bool level);\n\n/**\n * @brief Get the number of periods currently stored in a DigitalSignal instance.\n *\n * @param[in] signal pointer to the instance to be queried.\n * @return the number of periods stored in the instance.\n */\nuint32_t digital_signal_get_size(const DigitalSignal* signal);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/digital_signal/digital_signal_i.h",
    "content": "/**\n * @file digital_signal_i.h\n * @brief DigitalSignal private definitions.\n *\n * This file is an implementation detail. It must not be included in\n * any public API-related headers.\n */\n#include <stdint.h>\n#include <stdbool.h>\n\n#define DIGITAL_SIGNAL_T_TIM      1562 /**< 15.625 ns *100 */\n#define DIGITAL_SIGNAL_T_TIM_DIV2 (DIGITAL_SIGNAL_T_TIM / 2) /**< 15.625 ns / 2 *100 */\n\n/**\n * @brief DigitalSignal structure type.\n */\nstruct DigitalSignal {\n    bool start_level; /**< The level to begin the signal with. */\n    uint32_t size; /**< Current period count contained in the instance. */\n    uint32_t max_size; /**< Maximum period count this instance can hold. */\n    int32_t remainder; /**< Remainder left after converting all periods into timer ticks. */\n    uint32_t data[]; /**< The array of time periods. */\n};\n"
  },
  {
    "path": "lib/digital_signal/presets/nfc/iso14443_3a_signal.c",
    "content": "#include \"iso14443_3a_signal.h\"\n\n#include <digital_signal/digital_sequence.h>\n\n#define BITS_IN_BYTE (8)\n\n#define ISO14443_3A_SIGNAL_BIT_MAX_EDGES (10)\n#define ISO14443_3A_SIGNAL_MAX_EDGES     (1350)\n\n#define ISO14443_3A_SIGNAL_SEQUENCE_SIZE \\\n    (ISO14443_3A_SIGNAL_MAX_EDGES / (ISO14443_3A_SIGNAL_BIT_MAX_EDGES - 2))\n\n#define ISO14443_3A_SIGNAL_F_SIG       (13560000.0)\n#define ISO14443_3A_SIGNAL_T_SIG       7374 //73.746ns*100\n#define ISO14443_3A_SIGNAL_T_SIG_X8    58992 //T_SIG*8\n#define ISO14443_3A_SIGNAL_T_SIG_X8_X8 471936 //T_SIG*8*8\n#define ISO14443_3A_SIGNAL_T_SIG_X8_X9 530928 //T_SIG*8*9\n\ntypedef enum {\n    Iso14443_3aSignalIndexZero,\n    Iso14443_3aSignalIndexOne,\n    Iso14443_3aSignalIndexCount,\n} Iso14443_3aSignalIndex;\n\ntypedef DigitalSignal* Iso14443_3aSignalBank[Iso14443_3aSignalIndexCount];\n\nstruct Iso14443_3aSignal {\n    DigitalSequence* tx_sequence;\n    Iso14443_3aSignalBank signals;\n};\n\nstatic void iso14443_3a_signal_add_byte(Iso14443_3aSignal* instance, uint8_t byte, bool parity) {\n    for(size_t i = 0; i < BITS_IN_BYTE; i++) {\n        digital_sequence_add_signal(\n            instance->tx_sequence,\n            FURI_BIT(byte, i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);\n    }\n    digital_sequence_add_signal(\n        instance->tx_sequence, parity ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);\n}\n\nstatic void iso14443_3a_signal_encode(\n    Iso14443_3aSignal* instance,\n    const uint8_t* tx_data,\n    const uint8_t* tx_parity,\n    size_t tx_bits) {\n    furi_assert(instance);\n    furi_assert(tx_data);\n    furi_assert(tx_parity);\n\n    // Start of frame\n    digital_sequence_add_signal(instance->tx_sequence, Iso14443_3aSignalIndexOne);\n\n    if(tx_bits < BITS_IN_BYTE) {\n        for(size_t i = 0; i < tx_bits; i++) {\n            digital_sequence_add_signal(\n                instance->tx_sequence,\n                FURI_BIT(tx_data[0], i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);\n        }\n    } else {\n        for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; i++) {\n            bool parity = FURI_BIT(tx_parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE);\n            iso14443_3a_signal_add_byte(instance, tx_data[i], parity);\n        }\n    }\n}\n\nstatic inline void iso14443_3a_signal_set_bit(DigitalSignal* signal, bool bit) {\n    digital_signal_set_start_level(signal, bit);\n\n    if(bit) {\n        for(uint32_t i = 0; i < 7; ++i) {\n            digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8);\n        }\n        digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X9);\n    } else {\n        digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X8);\n        for(uint32_t i = 0; i < 8; ++i) {\n            digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8);\n        }\n    }\n}\n\nstatic inline void iso14443_3a_signal_bank_fill(Iso14443_3aSignalBank bank) {\n    for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {\n        bank[i] = digital_signal_alloc(ISO14443_3A_SIGNAL_BIT_MAX_EDGES);\n        iso14443_3a_signal_set_bit(bank[i], i % Iso14443_3aSignalIndexCount != 0);\n    }\n}\n\nstatic inline void iso14443_3a_signal_bank_clear(Iso14443_3aSignalBank bank) {\n    for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {\n        digital_signal_free(bank[i]);\n    }\n}\n\nstatic inline void\n    iso14443_3a_signal_bank_register(Iso14443_3aSignalBank bank, DigitalSequence* sequence) {\n    for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {\n        digital_sequence_register_signal(sequence, i, bank[i]);\n    }\n}\n\nIso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin) {\n    furi_assert(pin);\n\n    Iso14443_3aSignal* instance = malloc(sizeof(Iso14443_3aSignal));\n    instance->tx_sequence = digital_sequence_alloc(ISO14443_3A_SIGNAL_SEQUENCE_SIZE, pin);\n\n    iso14443_3a_signal_bank_fill(instance->signals);\n    iso14443_3a_signal_bank_register(instance->signals, instance->tx_sequence);\n\n    return instance;\n}\n\nvoid iso14443_3a_signal_free(Iso14443_3aSignal* instance) {\n    furi_assert(instance);\n    furi_assert(instance->tx_sequence);\n\n    iso14443_3a_signal_bank_clear(instance->signals);\n    digital_sequence_free(instance->tx_sequence);\n    free(instance);\n}\n\nvoid iso14443_3a_signal_tx(\n    Iso14443_3aSignal* instance,\n    const uint8_t* tx_data,\n    const uint8_t* tx_parity,\n    size_t tx_bits) {\n    furi_assert(instance);\n    furi_assert(tx_data);\n    furi_assert(tx_parity);\n\n    FURI_CRITICAL_ENTER();\n    digital_sequence_clear(instance->tx_sequence);\n    iso14443_3a_signal_encode(instance, tx_data, tx_parity, tx_bits);\n    digital_sequence_transmit(instance->tx_sequence);\n    FURI_CRITICAL_EXIT();\n}\n"
  },
  {
    "path": "lib/digital_signal/presets/nfc/iso14443_3a_signal.h",
    "content": "/**\n * @file iso14443_3a_signal.h\n * @brief DigitalSequence preset for generating ISO14443-3A compliant signals.\n */\n#pragma once\n\n#include <furi_hal_resources.h>\n\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso14443_3aSignal Iso14443_3aSignal;\n\n/**\n * @brief Allocate an Iso14443_3aSignal instance with a set GPIO pin.\n *\n * @param[in] pin GPIO pin to use during transmission.\n * @returns pointer to the allocated instance.\n */\nIso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin);\n\n/**\n * @brief Delete an Iso14443_3aSignal instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid iso14443_3a_signal_free(Iso14443_3aSignal* instance);\n\n/**\n * @brief Transmit arbitrary bytes using an Iso14443_3aSignal instance.\n *\n * This function will block until the transmisson has been completed.\n *\n * @param[in] instance pointer to the instance used in transmission.\n * @param[in] tx_data pointer to the data to be transmitted.\n * @param[in] tx_parity pointer to the bit-packed parity array.\n * @param[in] tx_bits size of the data to be transmitted in bits.\n */\nvoid iso14443_3a_signal_tx(\n    Iso14443_3aSignal* instance,\n    const uint8_t* tx_data,\n    const uint8_t* tx_parity,\n    size_t tx_bits);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/digital_signal/presets/nfc/iso15693_signal.c",
    "content": "#include \"iso15693_signal.h\"\n\n#include <digital_signal/digital_sequence.h>\n\n#define BITS_IN_BYTE (8U)\n\n#define ISO15693_SIGNAL_COEFF_HI (1U)\n#define ISO15693_SIGNAL_COEFF_LO (4U)\n\n#define ISO15693_SIGNAL_ZERO_EDGES (16U)\n#define ISO15693_SIGNAL_ONE_EDGES  (ISO15693_SIGNAL_ZERO_EDGES + 1U)\n#define ISO15693_SIGNAL_EOF_EDGES  (64U)\n#define ISO15693_SIGNAL_SOF_EDGES  (ISO15693_SIGNAL_EOF_EDGES + 1U)\n#define ISO15693_SIGNAL_EDGES      (1350U)\n\n#define ISO15693_SIGNAL_FC     (13.56e6)\n#define ISO15693_SIGNAL_FC_16  (16.0e11 / ISO15693_SIGNAL_FC)\n#define ISO15693_SIGNAL_FC_256 (256.0e11 / ISO15693_SIGNAL_FC)\n#define ISO15693_SIGNAL_FC_768 (768.0e11 / ISO15693_SIGNAL_FC)\n\ntypedef enum {\n    Iso15693SignalIndexSof,\n    Iso15693SignalIndexEof,\n    Iso15693SignalIndexOne,\n    Iso15693SignalIndexZero,\n    Iso15693SignalIndexNum,\n} Iso15693SignalIndex;\n\ntypedef DigitalSignal* Iso15693SignalBank[Iso15693SignalIndexNum];\n\nstruct Iso15693Signal {\n    DigitalSequence* tx_sequence;\n    Iso15693SignalBank banks[Iso15693SignalDataRateNum];\n};\n\n// Add an unmodulated signal for the length of Fc / 256 * k (where k = 1 or 4)\nstatic void iso15693_add_silence(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {\n    const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :\n                                                               ISO15693_SIGNAL_COEFF_LO;\n    digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_256 * k, false);\n}\n\n// Add 8 * k subcarrier pulses of Fc / 16 (where k = 1 or 4)\nstatic void iso15693_add_subcarrier(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {\n    const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :\n                                                               ISO15693_SIGNAL_COEFF_LO;\n    for(uint32_t i = 0; i < ISO15693_SIGNAL_ZERO_EDGES * k; ++i) {\n        digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_16, !(i % 2));\n    }\n}\n\nstatic void iso15693_add_bit(DigitalSignal* signal, Iso15693SignalDataRate data_rate, bool bit) {\n    if(bit) {\n        iso15693_add_silence(signal, data_rate);\n        iso15693_add_subcarrier(signal, data_rate);\n    } else {\n        iso15693_add_subcarrier(signal, data_rate);\n        iso15693_add_silence(signal, data_rate);\n    }\n}\n\nstatic inline void iso15693_add_sof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {\n    // Not adding silence since it only increases response time\n\n    for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) {\n        iso15693_add_subcarrier(signal, data_rate);\n    }\n\n    iso15693_add_bit(signal, data_rate, true);\n}\n\nstatic inline void iso15693_add_eof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {\n    iso15693_add_bit(signal, data_rate, false);\n\n    for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) {\n        iso15693_add_subcarrier(signal, data_rate);\n    }\n\n    // Not adding silence since it does nothing here\n}\n\nstatic inline uint32_t\n    iso15693_get_sequence_index(Iso15693SignalIndex index, Iso15693SignalDataRate data_rate) {\n    return index + data_rate * Iso15693SignalIndexNum;\n}\n\nstatic inline void\n    iso15693_add_byte(Iso15693Signal* instance, Iso15693SignalDataRate data_rate, uint8_t byte) {\n    for(size_t i = 0; i < BITS_IN_BYTE; i++) {\n        const uint8_t bit = byte & (1U << i);\n        digital_sequence_add_signal(\n            instance->tx_sequence,\n            iso15693_get_sequence_index(\n                bit ? Iso15693SignalIndexOne : Iso15693SignalIndexZero, data_rate));\n    }\n}\n\nstatic inline void iso15693_signal_encode(\n    Iso15693Signal* instance,\n    Iso15693SignalDataRate data_rate,\n    const uint8_t* tx_data,\n    size_t tx_data_size) {\n    digital_sequence_add_signal(\n        instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate));\n\n    for(size_t i = 0; i < tx_data_size; i++) {\n        iso15693_add_byte(instance, data_rate, tx_data[i]);\n    }\n\n    digital_sequence_add_signal(\n        instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexEof, data_rate));\n}\n\nstatic void iso15693_signal_bank_fill(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {\n    const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :\n                                                               ISO15693_SIGNAL_COEFF_LO;\n    DigitalSignal** bank = instance->banks[data_rate];\n\n    bank[Iso15693SignalIndexSof] = digital_signal_alloc(ISO15693_SIGNAL_SOF_EDGES * k);\n    bank[Iso15693SignalIndexEof] = digital_signal_alloc(ISO15693_SIGNAL_EOF_EDGES * k);\n    bank[Iso15693SignalIndexOne] = digital_signal_alloc(ISO15693_SIGNAL_ONE_EDGES * k);\n    bank[Iso15693SignalIndexZero] = digital_signal_alloc(ISO15693_SIGNAL_ZERO_EDGES * k);\n\n    iso15693_add_sof(bank[Iso15693SignalIndexSof], data_rate);\n    iso15693_add_eof(bank[Iso15693SignalIndexEof], data_rate);\n    iso15693_add_bit(bank[Iso15693SignalIndexOne], data_rate, true);\n    iso15693_add_bit(bank[Iso15693SignalIndexZero], data_rate, false);\n}\n\nstatic void\n    iso15693_signal_bank_clear(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {\n    DigitalSignal** bank = instance->banks[data_rate];\n\n    for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) {\n        digital_signal_free(bank[i]);\n    }\n}\n\nstatic void\n    iso15693_signal_bank_register(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {\n    for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) {\n        digital_sequence_register_signal(\n            instance->tx_sequence,\n            iso15693_get_sequence_index(i, data_rate),\n            instance->banks[data_rate][i]);\n    }\n}\n\nIso15693Signal* iso15693_signal_alloc(const GpioPin* pin) {\n    furi_assert(pin);\n\n    Iso15693Signal* instance = malloc(sizeof(Iso15693Signal));\n\n    instance->tx_sequence = digital_sequence_alloc(BITS_IN_BYTE * 255 + 2, pin);\n\n    for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) {\n        iso15693_signal_bank_fill(instance, i);\n        iso15693_signal_bank_register(instance, i);\n    }\n\n    return instance;\n}\n\nvoid iso15693_signal_free(Iso15693Signal* instance) {\n    furi_assert(instance);\n\n    digital_sequence_free(instance->tx_sequence);\n\n    for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) {\n        iso15693_signal_bank_clear(instance, i);\n    }\n\n    free(instance);\n}\n\nvoid iso15693_signal_tx(\n    Iso15693Signal* instance,\n    Iso15693SignalDataRate data_rate,\n    const uint8_t* tx_data,\n    size_t tx_data_size) {\n    furi_assert(instance);\n    furi_assert(data_rate < Iso15693SignalDataRateNum);\n    furi_assert(tx_data);\n\n    FURI_CRITICAL_ENTER();\n    digital_sequence_clear(instance->tx_sequence);\n    iso15693_signal_encode(instance, data_rate, tx_data, tx_data_size);\n    digital_sequence_transmit(instance->tx_sequence);\n\n    FURI_CRITICAL_EXIT();\n}\n\nvoid iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {\n    furi_assert(instance);\n    furi_assert(data_rate < Iso15693SignalDataRateNum);\n\n    FURI_CRITICAL_ENTER();\n    digital_sequence_clear(instance->tx_sequence);\n    digital_sequence_add_signal(\n        instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate));\n    digital_sequence_transmit(instance->tx_sequence);\n\n    FURI_CRITICAL_EXIT();\n}\n"
  },
  {
    "path": "lib/digital_signal/presets/nfc/iso15693_signal.h",
    "content": "/**\n * @file iso15693_signal.h\n * @brief DigitalSequence preset for generating ISO15693-compliant signals.\n *\n */\n#pragma once\n\n#include <furi_hal_resources.h>\n\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso15693Signal Iso15693Signal;\n\n/**\n * @brief Supported data rates.\n */\ntypedef enum {\n    Iso15693SignalDataRateHi, /**< High data rate. */\n    Iso15693SignalDataRateLo, /**< Low data rate. */\n    Iso15693SignalDataRateNum, /**< Data rate mode count. Internal use. */\n} Iso15693SignalDataRate;\n\n/**\n * @brief Allocate an Iso15693Signal instance with a set GPIO pin.\n *\n * @param[in] pin GPIO pin to use during transmission.\n * @returns pointer to the allocated instance.\n */\nIso15693Signal* iso15693_signal_alloc(const GpioPin* pin);\n\n/**\n * @brief Delete an Iso15693Signal instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid iso15693_signal_free(Iso15693Signal* instance);\n\n/**\n * @brief Transmit arbitrary bytes using an Iso15693Signal instance.\n * @see Iso15693SignalDataRate\n *\n * This function will block until the transmisson has been completed.\n *\n * @param[in] instance pointer to the instance used in transmission.\n * @param[in] data_rate data rate to transmit at.\n * @param[in] tx_data pointer to the data to be transmitted.\n * @param[in] tx_data_size size of the data to be transmitted in bytes.\n */\nvoid iso15693_signal_tx(\n    Iso15693Signal* instance,\n    Iso15693SignalDataRate data_rate,\n    const uint8_t* tx_data,\n    size_t tx_data_size);\n\n/**\n * @brief Transmit Start of Frame using an Iso15693Signal instance.\n * @see Iso15693SignalDataRate\n *\n * This function will block until the transmisson has been completed.\n *\n * @param[in] instance pointer to the instance used in transmission.\n * @param[in] data_rate data rate to transmit at.\n */\nvoid iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/drivers/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/drivers\",\n    ],\n    SDK_HEADERS=[\n        File(\"cc1101_regs.h\"),\n        File(\"st25r3916_reg.h\"),\n        File(\"st25r3916.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"hwdrivers\")\nlibenv.ApplyLibFlags()\n\nsources = Glob(\"*.c\", source=True)\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/drivers/bq25896.c",
    "content": "#include \"bq25896.h\"\n\n#include <stddef.h>\n\nuint8_t bit_reverse(uint8_t b) {\n    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;\n    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;\n    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;\n    return b;\n}\n\ntypedef struct {\n    REG00 r00;\n    REG01 r01;\n    REG02 r02;\n    REG03 r03;\n    REG04 r04;\n    REG05 r05;\n    REG06 r06;\n    REG07 r07;\n    REG08 r08;\n    REG09 r09;\n    REG0A r0A;\n    REG0B r0B;\n    REG0C r0C;\n    REG0D r0D;\n    REG0E r0E;\n    REG0F r0F;\n    REG10 r10;\n    REG11 r11;\n    REG12 r12;\n    REG13 r13;\n    REG14 r14;\n} bq25896_regs_t;\n\nstatic bq25896_regs_t bq25896_regs;\n\nbool bq25896_init(const FuriHalI2cBusHandle* handle) {\n    bool result = true;\n\n    bq25896_regs.r14.REG_RST = 1;\n    result &= furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x14, *(uint8_t*)&bq25896_regs.r14, BQ25896_I2C_TIMEOUT);\n\n    // Readout all registers\n    result &= furi_hal_i2c_read_mem(\n        handle,\n        BQ25896_ADDRESS,\n        0x00,\n        (uint8_t*)&bq25896_regs,\n        sizeof(bq25896_regs),\n        BQ25896_I2C_TIMEOUT);\n\n    // Poll ADC forever\n    bq25896_regs.r02.CONV_START = 1;\n    bq25896_regs.r02.CONV_RATE = 1;\n    result &= furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x02, *(uint8_t*)&bq25896_regs.r02, BQ25896_I2C_TIMEOUT);\n\n    bq25896_regs.r07.WATCHDOG = WatchdogDisable;\n    result &= furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x07, *(uint8_t*)&bq25896_regs.r07, BQ25896_I2C_TIMEOUT);\n\n    // OTG power configuration\n    bq25896_regs.r0A.BOOSTV = 0x8; // BOOST Voltage: 5.062V\n    bq25896_regs.r0A.BOOST_LIM = BoostLim_1400; // BOOST Current limit: 1.4A\n    result &= furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT);\n\n    result &= furi_hal_i2c_read_mem(\n        handle,\n        BQ25896_ADDRESS,\n        0x00,\n        (uint8_t*)&bq25896_regs,\n        sizeof(bq25896_regs),\n        BQ25896_I2C_TIMEOUT);\n\n    return result;\n}\n\nvoid bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim) {\n    bq25896_regs.r0A.BOOST_LIM = boost_lim;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT);\n}\n\nvoid bq25896_poweroff(const FuriHalI2cBusHandle* handle) {\n    bq25896_regs.r09.BATFET_DIS = 1;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT);\n}\n\nChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_mem(\n        handle,\n        BQ25896_ADDRESS,\n        0x00,\n        (uint8_t*)&bq25896_regs,\n        sizeof(bq25896_regs),\n        BQ25896_I2C_TIMEOUT);\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x0B, (uint8_t*)&bq25896_regs.r0B, BQ25896_I2C_TIMEOUT);\n    return bq25896_regs.r0B.CHRG_STAT;\n}\n\nbool bq25896_is_charging(const FuriHalI2cBusHandle* handle) {\n    // Include precharge, fast charging, and charging termination done as \"charging\"\n    return bq25896_get_charge_status(handle) != ChrgStatNo;\n}\n\nbool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle) {\n    return bq25896_get_charge_status(handle) == ChrgStatDone;\n}\n\nvoid bq25896_enable_charging(const FuriHalI2cBusHandle* handle) {\n    bq25896_regs.r03.CHG_CONFIG = 1;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);\n}\n\nvoid bq25896_disable_charging(const FuriHalI2cBusHandle* handle) {\n    bq25896_regs.r03.CHG_CONFIG = 0;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);\n}\n\nvoid bq25896_enable_otg(const FuriHalI2cBusHandle* handle) {\n    bq25896_regs.r03.OTG_CONFIG = 1;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);\n}\n\nvoid bq25896_disable_otg(const FuriHalI2cBusHandle* handle) {\n    bq25896_regs.r03.OTG_CONFIG = 0;\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);\n}\n\nbool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x03, (uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);\n    return bq25896_regs.r03.OTG_CONFIG;\n}\n\nuint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);\n    return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840;\n}\n\nvoid bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) {\n    if(vreg_voltage < 3840) {\n        // Minimum valid value is 3840 mV\n        vreg_voltage = 3840;\n    } else if(vreg_voltage > 4208) {\n        // Maximum safe value is 4208 mV\n        vreg_voltage = 4208;\n    }\n\n    // Find the nearest voltage value (subtract offset, divide into sections)\n    // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV)\n    bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16);\n\n    // Apply changes\n    furi_hal_i2c_write_reg_8(\n        handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);\n}\n\nbool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT);\n    return bq25896_regs.r0C.BOOST_FAULT;\n}\n\nuint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x11, (uint8_t*)&bq25896_regs.r11, BQ25896_I2C_TIMEOUT);\n    if(bq25896_regs.r11.VBUS_GD) {\n        return (uint16_t)bq25896_regs.r11.VBUSV * 100 + 2600;\n    } else {\n        return 0;\n    }\n}\n\nuint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x0F, (uint8_t*)&bq25896_regs.r0F, BQ25896_I2C_TIMEOUT);\n    return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304;\n}\n\nuint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x0E, (uint8_t*)&bq25896_regs.r0E, BQ25896_I2C_TIMEOUT);\n    return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304;\n}\n\nuint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x12, (uint8_t*)&bq25896_regs.r12, BQ25896_I2C_TIMEOUT);\n    return (uint16_t)bq25896_regs.r12.ICHGR * 50;\n}\n\nuint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle) {\n    furi_hal_i2c_read_reg_8(\n        handle, BQ25896_ADDRESS, 0x10, (uint8_t*)&bq25896_regs.r10, BQ25896_I2C_TIMEOUT);\n    return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000;\n}\n"
  },
  {
    "path": "lib/drivers/bq25896.h",
    "content": "#pragma once\n\n#include \"bq25896_reg.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <furi_hal_i2c.h>\n\n/** Initialize Driver */\nbool bq25896_init(const FuriHalI2cBusHandle* handle);\n\n/** Set boost lim*/\nvoid bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim);\n\n/** Send device into shipping mode */\nvoid bq25896_poweroff(const FuriHalI2cBusHandle* handle);\n\n/** Get charging status */\nChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle);\n\n/** Is currently charging */\nbool bq25896_is_charging(const FuriHalI2cBusHandle* handle);\n\n/** Is charging completed while connected to charger */\nbool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle);\n\n/** Enable charging */\nvoid bq25896_enable_charging(const FuriHalI2cBusHandle* handle);\n\n/** Disable charging */\nvoid bq25896_disable_charging(const FuriHalI2cBusHandle* handle);\n\n/** Enable otg */\nvoid bq25896_enable_otg(const FuriHalI2cBusHandle* handle);\n\n/** Disable otg */\nvoid bq25896_disable_otg(const FuriHalI2cBusHandle* handle);\n\n/** Is otg enabled */\nbool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle);\n\n/** Get VREG (charging limit) voltage in mV */\nuint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle);\n\n/** Set VREG (charging limit) voltage in mV\n *\n * Valid range: 3840mV - 4208mV, in steps of 16mV\n */\nvoid bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage);\n\n/** Check OTG BOOST Fault status */\nbool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle);\n\n/** Get VBUS Voltage in mV */\nuint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle);\n\n/** Get VSYS Voltage in mV */\nuint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle);\n\n/** Get VBAT Voltage in mV */\nuint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle);\n\n/** Get VBAT current in mA */\nuint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle);\n\n/** Get NTC voltage in mpct of REGN */\nuint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle);\n"
  },
  {
    "path": "lib/drivers/bq25896_reg.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#if defined(BITS_BIG_ENDIAN) && BITS_BIG_ENDIAN == 1\n#error Bit structures defined in this file are not portable to BE\n#endif\n\n#define BQ25896_ADDRESS     0xD6\n#define BQ25896_I2C_TIMEOUT 50\n\n#define IILIM_1600 (1 << 5)\n#define IILIM_800  (1 << 4)\n#define IILIM_400  (1 << 3)\n#define IILIM_200  (1 << 2)\n#define IILIM_100  (1 << 1)\n#define IILIM_50   (1 << 0)\n\ntypedef struct {\n    uint8_t IINLIM : 6; // Input Current Limit, mA, offset: +100mA\n    bool EN_ILIM   : 1; // Enable ILIM Pin\n    bool EN_HIZ    : 1; // Enable HIZ Mode\n} REG00;\n\n#define VINDPM_OS_1600 (1 << 4)\n#define VINDPM_OS_800  (1 << 3)\n#define VINDPM_OS_400  (1 << 2)\n#define VINDPM_OS_200  (1 << 1)\n#define VINDPM_OS_100  (1 << 0)\n\ntypedef enum {\n    Bhot34 = 0b00, // – VBHOT1 Threshold (34.75%) (default)\n    Bhot37 = 0b01, // – VBHOT0 Threshold (Typ. 37.75%)\n    Bhot31 = 0b10, // – VBHOT2 Threshold (Typ. 31.25%)\n    BhotDisable = 0b11, // – Disable boost mode thermal protection\n} Bhot;\n\ntypedef struct {\n    uint8_t VINDPM_OS : 5; // Input Voltage Limit Offset, mV\n    bool BCOLD        : 1; // Boost Mode Cold Temperature Monitor Threshold\n    Bhot BHOT         : 2; // Boost Mode Hot Temperature Monitor Threshold\n} REG01;\n\ntypedef struct {\n    bool AUTO_DPDM_EN : 1; // Automatic Input Detection Enable\n    bool FORCE_DPDM   : 1; // Force Input Detection\n    uint8_t RES       : 2; // Reserved\n    bool ICO_EN       : 1; // Input Current Optimizer (ICO) Enable\n    bool BOOST_FREQ   : 1; // Boost Mode Frequency Selection\n    bool CONV_RATE    : 1; // ADC Conversion Rate Selection\n    bool CONV_START   : 1; // ADC Conversion Start Control\n} REG02;\n\n#define SYS_MIN_400 (1 << 2)\n#define SYS_MIN_200 (1 << 1)\n#define SYS_MIN_100 (1 << 0)\n\ntypedef struct {\n    bool MIN_VBAT_SEL : 1; // Minimum Battery Voltage (falling) to exit boost mode\n    uint8_t SYS_MIN   : 3; // Minimum System Voltage Limit, mV, offset: +3000mV\n    bool CHG_CONFIG   : 1; // Charge Enable Configuration\n    bool OTG_CONFIG   : 1; // Boost (OTG) Mode Configuration\n    bool WD_RST       : 1; // I2C Watchdog Timer Reset\n    bool BAT_LOADEN   : 1; // Battery Load (IBATLOAD) Enable\n} REG03;\n\n#define ICHG_4096 (1 << 6)\n#define ICHG_2048 (1 << 5)\n#define ICHG_1024 (1 << 4)\n#define ICHG_512  (1 << 3)\n#define ICHG_256  (1 << 2)\n#define ICHG_128  (1 << 1)\n#define ICHG_64   (1 << 0)\n\ntypedef struct {\n    uint8_t ICHG  : 7; // Fast Charge Current Limit, mA\n    bool EN_PUMPX : 1; // Current pulse control Enable\n} REG04;\n\n#define IPRETERM_512 (1 << 3)\n#define IPRETERM_256 (1 << 2)\n#define IPRETERM_128 (1 << 1)\n#define IPRETERM_64  (1 << 0)\n\ntypedef struct {\n    uint8_t ITERM   : 4; // Termination Current Limit, offset: +64mA\n    uint8_t IPRECHG : 4; // Precharge Current Limit, offset: +64mA\n} REG05;\n\n#define VREG_512 (1 << 5)\n#define VREG_256 (1 << 4)\n#define VREG_128 (1 << 3)\n#define VREG_64  (1 << 2)\n#define VREG_32  (1 << 1)\n#define VREG_16  (1 << 0)\n\ntypedef struct {\n    bool VRECHG  : 1; // Battery Recharge Threshold Offset\n    bool BATLOWV : 1; // Battery Precharge to Fast Charge Threshold\n    uint8_t VREG : 6; // Charge Voltage Limit, offset: +3840mV\n} REG06;\n\ntypedef enum {\n    WatchdogDisable = 0b00,\n    Watchdog40 = 0b01,\n    Watchdog80 = 0b10,\n    Watchdog160 = 0b11,\n} Watchdog;\n\ntypedef enum {\n    ChgTimer5 = 0b00,\n    ChgTimer8 = 0b01,\n    ChgTimer12 = 0b10,\n    ChgTimer20 = 0b11,\n} ChgTimer;\n\ntypedef struct {\n    bool JEITA_ISET    : 1; // JEITA Low Temperature Current Setting\n    ChgTimer CHG_TIMER : 2; // Fast Charge Timer Setting\n    bool EN_TIMER      : 1; // Charging Safety Timer Enable\n    Watchdog WATCHDOG  : 2; // I2C Watchdog Timer Setting\n    bool STAT_DIS      : 1; // STAT Pin Disable\n    bool EN_TERM       : 1; // Charging Termination Enable\n} REG07;\n\n#define BAT_COMP_80 (1 << 2)\n#define BAT_COMP_40 (1 << 1)\n#define BAT_COMP_20 (1 << 0)\n\n#define VCLAMP_128 (1 << 2)\n#define VCLAMP_64  (1 << 1)\n#define VCLAMP_32  (1 << 0)\n\n#define TREG_60  (0b00)\n#define TREG_80  (0b01)\n#define TREG_100 (0b10)\n#define TREG_120 (0b11)\n\ntypedef struct {\n    uint8_t TREG     : 2; // Thermal Regulation Threshold\n    uint8_t VCLAMP   : 3; // IR Compensation Voltage Clamp\n    uint8_t BAT_COMP : 3; // IR Compensation Resistor Setting\n} REG08;\n\ntypedef struct {\n    bool PUMPX_DN      : 1; // Current pulse control voltage down enable\n    bool PUMPX_UP      : 1; // Current pulse control voltage up enable\n    bool BATFET_RST_EN : 1; // BATFET full system reset enable\n    bool BATFET_DLY    : 1; // BATFET turn off delay control\n    bool JEITA_VSET    : 1; // JEITA High Temperature Voltage Setting\n    bool BATFET_DIS    : 1; // Force BATFET off to enable ship mode\n    bool TMR2X_EN      : 1; // Safety Timer Setting during DPM or Thermal Regulation\n    bool FORCE_ICO     : 1; // Force Start Input Current Optimizer\n} REG09;\n\n#define BOOSTV_512 (1 << 3)\n#define BOOSTV_256 (1 << 2)\n#define BOOSTV_128 (1 << 1)\n#define BOOSTV_64  (1 << 0)\n\ntypedef enum {\n    BoostLim_500 = 0b000,\n    BoostLim_750 = 0b001,\n    BoostLim_1200 = 0b010,\n    BoostLim_1400 = 0b011,\n    BoostLim_1650 = 0b100,\n    BoostLim_1875 = 0b101,\n    BoostLim_2150 = 0b110,\n    BoostLim_Rsvd = 0b111,\n} BoostLim;\n\ntypedef struct {\n    uint8_t BOOST_LIM : 3; // Boost Mode Current Limit\n    bool PFM_OTG_DIS  : 1; // PFM mode allowed in boost mode\n    uint8_t BOOSTV    : 4; // Boost Mode Voltage Regulation, offset: +4550mV\n} REG0A;\n\ntypedef enum {\n    VBusStatNo = 0b000,\n    VBusStatUSB = 0b001,\n    VBusStatExternal = 0b010,\n    VBusStatOTG = 0b111,\n} VBusStat;\n\ntypedef enum {\n    ChrgStatNo = 0b00,\n    ChrgStatPre = 0b01,\n    ChrgStatFast = 0b10,\n    ChrgStatDone = 0b11,\n} ChrgStat;\n\ntypedef struct {\n    bool VSYS_STAT     : 1; // VSYS Regulation Status\n    bool RES           : 1; // Reserved: Always reads 1\n    bool PG_STAT       : 1; // Power Good Status\n    ChrgStat CHRG_STAT : 2; // Charging Status\n    VBusStat VBUS_STAT : 3; // VBUS Status register\n} REG0B;\n\ntypedef enum {\n    ChrgFaultNO = 0b00,\n    ChrgFaultIN = 0b01,\n    ChrgFaultTH = 0b10,\n    ChrgFaultTIM = 0b11,\n} ChrgFault;\n\ntypedef enum {\n    NtcFaultNo = 0b000,\n    NtcFaultWarm = 0b010,\n    NtcFaultCool = 0b011,\n    NtcFaultCold = 0b101,\n    NtcFaultHot = 0b110,\n} NtcFault;\n\ntypedef struct {\n    NtcFault NTC_FAULT   : 3; // NTC Fault Status\n    bool BAT_FAULT       : 1; // Battery Fault Status\n    ChrgFault CHRG_FAULT : 2; // Charge Fault Status\n    bool BOOST_FAULT     : 1; // Boost Mode Fault Status\n    bool WATCHDOG_FAULT  : 1; // Watchdog Fault Status\n} REG0C;\n\n#define VINDPM_6400 (1 << 6)\n#define VINDPM_3200 (1 << 5)\n#define VINDPM_1600 (1 << 4)\n#define VINDPM_800  (1 << 3)\n#define VINDPM_400  (1 << 2)\n#define VINDPM_200  (1 << 1)\n#define VINDPM_100  (1 << 0)\n\ntypedef struct {\n    uint8_t VINDPM    : 7; // Absolute VINDPM Threshold, offset: +2600mV\n    bool FORCE_VINDPM : 1; // VINDPM Threshold Setting Method\n} REG0D;\n\ntypedef struct {\n    uint8_t BATV    : 7; // ADC conversion of Battery Voltage (VBAT), offset: +2304mV\n    bool THERM_STAT : 1; // Thermal Regulation Status\n} REG0E;\n\ntypedef struct {\n    uint8_t SYSV : 7; // ADDC conversion of System Voltage (VSYS), offset: +2304mV\n    uint8_t RES  : 1; // Reserved: Always reads 0\n} REG0F;\n\ntypedef struct {\n    uint8_t TSPCT : 7; // ADC conversion of TS Voltage (TS) as percentage of REGN, offset: +21%\n    uint8_t RES   : 1; // Reserved: Always reads 0\n} REG10;\n\ntypedef struct {\n    uint8_t VBUSV : 7; // ADC conversion of VBUS voltage (VBUS), offset: +2600mV\n    bool VBUS_GD  : 1; // VBUS Good Status\n} REG11;\n\ntypedef struct {\n    uint8_t ICHGR : 7; // ADC conversion of Charge Current (IBAT) when VBAT > VBATSHORT\n    uint8_t RES   : 1; // Reserved: Always reads 0\n} REG12;\n\ntypedef struct {\n    uint8_t\n        IDPM_LIM : 6; // Input Current Limit in effect while Input Current Optimizer (ICO) is enabled, offset: 100mA (default)\n    bool IDPM_STAT : 1; // IINDPM Status\n    bool VDPM_STAT : 1; // VINDPM Status\n} REG13;\n\ntypedef struct {\n    uint8_t DEV_REV    : 2; // Device Revision\n    bool TS_PROFILE    : 1; // Temperature Profile\n    uint8_t PN         : 3; // Device Configuration\n    bool ICO_OPTIMIZED : 1; // Input Current Optimizer (ICO) Status\n    bool REG_RST       : 1; // Register Reset\n} REG14;\n"
  },
  {
    "path": "lib/drivers/bq27220.c",
    "content": "#include \"bq27220.h\"\n#include \"bq27220_reg.h\"\n#include \"bq27220_data_memory.h\"\n\n#include <furi.h>\n#include <stdbool.h>\n\n#define TAG \"Gauge\"\n\n#define BQ27220_ID (0x0220u)\n\n/** Delay between 2 writes into Subclass/MAC area. Fails at ~120us. */\n#define BQ27220_MAC_WRITE_DELAY_US (250u)\n\n/** Delay between we ask chip to load data to MAC and it become valid. Fails at ~500us. */\n#define BQ27220_SELECT_DELAY_US (1000u)\n\n/** Delay between 2 control operations(like unseal or full access). Fails at ~2500us.*/\n#define BQ27220_MAGIC_DELAY_US (5000u)\n\n/** Delay before freshly written configuration can be read. Fails at ? */\n#define BQ27220_CONFIG_DELAY_US (10000u)\n\n/** Config apply delay. Must wait, or DM read returns garbage. */\n#define BQ27220_CONFIG_APPLY_US (2000000u)\n\n/** Timeout for common operations. */\n#define BQ27220_TIMEOUT_COMMON_US (2000000u)\n\n/** Timeout for reset operation. Normally reset takes ~2s. */\n#define BQ27220_TIMEOUT_RESET_US (4000000u)\n\n/** Timeout cycle interval  */\n#define BQ27220_TIMEOUT_CYCLE_INTERVAL_US (1000u)\n\n/** Timeout cycles count helper */\n#define BQ27220_TIMEOUT(timeout_us) ((timeout_us) / (BQ27220_TIMEOUT_CYCLE_INTERVAL_US))\n\n#ifdef BQ27220_DEBUG\n#define BQ27220_DEBUG_LOG(...) FURI_LOG_D(TAG, ##__VA_ARGS__)\n#else\n#define BQ27220_DEBUG_LOG(...)\n#endif\n\nstatic inline bool bq27220_read_reg(\n    const FuriHalI2cBusHandle* handle,\n    uint8_t address,\n    uint8_t* buffer,\n    size_t buffer_size) {\n    return furi_hal_i2c_trx(\n        handle, BQ27220_ADDRESS, &address, 1, buffer, buffer_size, BQ27220_I2C_TIMEOUT);\n}\n\nstatic inline bool bq27220_write(\n    const FuriHalI2cBusHandle* handle,\n    uint8_t address,\n    const uint8_t* buffer,\n    size_t buffer_size) {\n    return furi_hal_i2c_write_mem(\n        handle, BQ27220_ADDRESS, address, buffer, buffer_size, BQ27220_I2C_TIMEOUT);\n}\n\nstatic inline bool bq27220_control(const FuriHalI2cBusHandle* handle, uint16_t control) {\n    return bq27220_write(handle, CommandControl, (uint8_t*)&control, 2);\n}\n\nstatic uint16_t bq27220_read_word(const FuriHalI2cBusHandle* handle, uint8_t address) {\n    uint16_t buf = BQ27220_ERROR;\n\n    if(!bq27220_read_reg(handle, address, (uint8_t*)&buf, 2)) {\n        FURI_LOG_E(TAG, \"bq27220_read_word failed\");\n    }\n\n    return buf;\n}\n\nstatic uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) {\n    uint8_t ret = 0;\n    for(uint16_t i = 0; i < len; i++) {\n        ret += data[i];\n    }\n    return 0xFF - ret;\n}\n\nstatic bool bq27220_parameter_check(\n    const FuriHalI2cBusHandle* handle,\n    uint16_t address,\n    uint32_t value,\n    size_t size,\n    bool update) {\n    furi_assert(size == 1 || size == 2 || size == 4);\n    bool ret = false;\n    uint8_t buffer[6] = {0};\n    uint8_t old_data[4] = {0};\n\n    do {\n        buffer[0] = address & 0xFF;\n        buffer[1] = (address >> 8) & 0xFF;\n\n        for(size_t i = 0; i < size; i++) {\n            buffer[1 + size - i] = (value >> (i * 8)) & 0xFF;\n        }\n\n        if(update) {\n            // Datasheet contains incorrect procedure for memory update, more info:\n            // https://e2e.ti.com/support/power-management-group/power-management/f/power-management-forum/719878/bq27220-technical-reference-manual-sluubd4-is-missing-extended-data-commands-chapter\n            // Also see note in the header\n\n            // Write the address AND the parameter data to 0x3E+ (auto increment)\n            if(!bq27220_write(handle, CommandSelectSubclass, buffer, size + 2)) {\n                FURI_LOG_E(TAG, \"DM write failed\");\n                break;\n            }\n\n            // We must wait, otherwise write will fail\n            furi_delay_us(BQ27220_MAC_WRITE_DELAY_US);\n\n            // Calculate the check sum: 0xFF - (sum of address and data) OR 0xFF\n            uint8_t checksum = bq27220_get_checksum(buffer, size + 2);\n            // Write the check sum to 0x60 and the total length of (address + parameter data + check sum + length) to 0x61\n            buffer[0] = checksum;\n            // 2 bytes address, `size` bytes data, 1 byte check sum, 1 byte length\n            buffer[1] = 2 + size + 1 + 1;\n            if(!bq27220_write(handle, CommandMACDataSum, buffer, 2)) {\n                FURI_LOG_E(TAG, \"CRC write failed\");\n                break;\n            }\n            // Final wait as in gm.fs specification\n            furi_delay_us(BQ27220_CONFIG_DELAY_US);\n            ret = true;\n        } else {\n            if(!bq27220_write(handle, CommandSelectSubclass, buffer, 2)) {\n                FURI_LOG_E(TAG, \"DM SelectSubclass for read failed\");\n                break;\n            }\n\n            // bqstudio uses 15ms wait delay here\n            furi_delay_us(BQ27220_SELECT_DELAY_US);\n\n            if(!bq27220_read_reg(handle, CommandMACData, old_data, size)) {\n                FURI_LOG_E(TAG, \"DM read failed\");\n                break;\n            }\n\n            // bqstudio uses burst reads with continue(CommandSelectSubclass without argument) and ~5ms between burst\n            furi_delay_us(BQ27220_SELECT_DELAY_US);\n\n            if(*(uint32_t*)&(old_data[0]) != *(uint32_t*)&(buffer[2])) {\n                FURI_LOG_E( //-V641\n                    TAG,\n                    \"Data at 0x%04x(%zu): 0x%08lx!=0x%08lx\",\n                    address,\n                    size,\n                    *(uint32_t*)&(old_data[0]),\n                    *(uint32_t*)&(buffer[2]));\n            } else {\n                ret = true;\n            }\n        }\n    } while(0);\n\n    return ret;\n}\n\nstatic bool bq27220_data_memory_check(\n    const FuriHalI2cBusHandle* handle,\n    const BQ27220DMData* data_memory,\n    bool update) {\n    if(update) {\n        const uint16_t cfg_request = Control_ENTER_CFG_UPDATE;\n        if(!bq27220_write(\n               handle, CommandSelectSubclass, (uint8_t*)&cfg_request, sizeof(cfg_request))) {\n            FURI_LOG_E(TAG, \"ENTER_CFG_UPDATE command failed\");\n            return false;\n        };\n\n        // Wait for enter CFG update mode\n        uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_COMMON_US);\n        Bq27220OperationStatus operation_status;\n        while(--timeout > 0) {\n            if(!bq27220_get_operation_status(handle, &operation_status)) {\n                FURI_LOG_W(TAG, \"Failed to get operation status, retries left %lu\", timeout);\n            } else if(operation_status.CFGUPDATE) {\n                break;\n            };\n            furi_delay_us(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);\n        }\n\n        if(timeout == 0) {\n            FURI_LOG_E(\n                TAG,\n                \"Enter CFGUPDATE mode failed, CFG %u, SEC %u\",\n                operation_status.CFGUPDATE,\n                operation_status.SEC);\n            return false;\n        }\n        BQ27220_DEBUG_LOG(\"Cycles left: %lu\", timeout);\n    }\n\n    // Process data memory records\n    bool result = true;\n    while(data_memory->type != BQ27220DMTypeEnd) {\n        if(data_memory->type == BQ27220DMTypeWait) {\n            furi_delay_us(data_memory->value.u32);\n        } else if(data_memory->type == BQ27220DMTypeU8) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.u8, 1, update);\n        } else if(data_memory->type == BQ27220DMTypeU16) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.u16, 2, update);\n        } else if(data_memory->type == BQ27220DMTypeU32) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.u32, 4, update);\n        } else if(data_memory->type == BQ27220DMTypeI8) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.i8, 1, update);\n        } else if(data_memory->type == BQ27220DMTypeI16) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.i16, 2, update);\n        } else if(data_memory->type == BQ27220DMTypeI32) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.i32, 4, update);\n        } else if(data_memory->type == BQ27220DMTypeF32) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, data_memory->value.u32, 4, update);\n        } else if(data_memory->type == BQ27220DMTypePtr8) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, *(uint8_t*)data_memory->value.u32, 1, update);\n        } else if(data_memory->type == BQ27220DMTypePtr16) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, *(uint16_t*)data_memory->value.u32, 2, update);\n        } else if(data_memory->type == BQ27220DMTypePtr32) {\n            result &= bq27220_parameter_check(\n                handle, data_memory->address, *(uint32_t*)data_memory->value.u32, 4, update);\n        } else {\n            furi_crash(\"Invalid DM Type\");\n        }\n        data_memory++;\n    }\n\n    // Finalize configuration update\n    if(update && result) {\n        bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT);\n\n        // Wait for gauge to apply new configuration\n        furi_delay_us(BQ27220_CONFIG_APPLY_US);\n\n        // ensure that we exited config update mode\n        uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_COMMON_US);\n        Bq27220OperationStatus operation_status;\n        while(--timeout > 0) {\n            if(!bq27220_get_operation_status(handle, &operation_status)) {\n                FURI_LOG_W(TAG, \"Failed to get operation status, retries left %lu\", timeout);\n            } else if(operation_status.CFGUPDATE != true) {\n                break;\n            }\n            furi_delay_us(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);\n        }\n\n        // Check timeout\n        if(timeout == 0) {\n            FURI_LOG_E(TAG, \"Exit CFGUPDATE mode failed\");\n            return false;\n        }\n        BQ27220_DEBUG_LOG(\"Cycles left: %lu\", timeout);\n    }\n\n    return result;\n}\n\nbool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) {\n    bool result = false;\n    bool reset_and_provisioning_required = false;\n\n    do {\n        // Request device number(chip PN)\n        BQ27220_DEBUG_LOG(\"Checking device ID\");\n        if(!bq27220_control(handle, Control_DEVICE_NUMBER)) {\n            FURI_LOG_E(TAG, \"ID: Device is not responding\");\n            break;\n        };\n        // Enterprise wait(MAC read fails if less than 500us)\n        // bqstudio uses ~15ms\n        furi_delay_us(BQ27220_SELECT_DELAY_US);\n        // Read id data from MAC scratch space\n        uint16_t data = bq27220_read_word(handle, CommandMACData);\n        if(data != BQ27220_ID) {\n            FURI_LOG_E(TAG, \"Invalid Device Number %04x != 0x0220\", data);\n            break;\n        }\n\n        // Unseal device since we are going to read protected configuration\n        BQ27220_DEBUG_LOG(\"Unsealing\");\n        if(!bq27220_unseal(handle)) {\n            break;\n        }\n\n        // Try to recover gauge from forever init\n        BQ27220_DEBUG_LOG(\"Checking initialization status\");\n        Bq27220OperationStatus operation_status;\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Failed to get operation status\");\n            break;\n        }\n        if(!operation_status.INITCOMP || operation_status.CFGUPDATE) {\n            FURI_LOG_E(TAG, \"Incorrect state, reset needed\");\n            reset_and_provisioning_required = true;\n        }\n\n        // Ensure correct profile is selected\n        BQ27220_DEBUG_LOG(\"Checking chosen profile\");\n        Bq27220ControlStatus control_status;\n        if(!bq27220_get_control_status(handle, &control_status)) {\n            FURI_LOG_E(TAG, \"Failed to get control status\");\n            break;\n        }\n        if(control_status.BATT_ID != 0) {\n            FURI_LOG_E(TAG, \"Incorrect profile, reset needed\");\n            reset_and_provisioning_required = true;\n        }\n\n        // Ensure correct configuration loaded into gauge DataMemory\n        // Only if reset is not required, otherwise we don't\n        if(!reset_and_provisioning_required) {\n            BQ27220_DEBUG_LOG(\"Checking data memory\");\n            if(!bq27220_data_memory_check(handle, data_memory, false)) {\n                FURI_LOG_E(TAG, \"Incorrect configuration data, reset needed\");\n                reset_and_provisioning_required = true;\n            }\n        }\n\n        // Reset needed\n        if(reset_and_provisioning_required) {\n            FURI_LOG_W(TAG, \"Resetting device\");\n            if(!bq27220_reset(handle)) {\n                FURI_LOG_E(TAG, \"Failed to reset device\");\n                break;\n            }\n\n            // Get full access to read and modify parameters\n            // Also it looks like this step is totally unnecessary\n            BQ27220_DEBUG_LOG(\"Acquiring Full Access\");\n            if(!bq27220_full_access(handle)) {\n                break;\n            }\n\n            // Update memory\n            FURI_LOG_W(TAG, \"Updating data memory\");\n            bq27220_data_memory_check(handle, data_memory, true);\n            if(!bq27220_data_memory_check(handle, data_memory, false)) {\n                FURI_LOG_E(TAG, \"Data memory update failed\");\n                break;\n            }\n        }\n\n        BQ27220_DEBUG_LOG(\"Sealing\");\n        if(!bq27220_seal(handle)) {\n            FURI_LOG_E(TAG, \"Seal failed\");\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    return result;\n}\n\nbool bq27220_reset(const FuriHalI2cBusHandle* handle) {\n    bool result = false;\n    do {\n        if(!bq27220_control(handle, Control_RESET)) {\n            FURI_LOG_E(TAG, \"Reset request failed\");\n            break;\n        };\n\n        uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_RESET_US);\n        Bq27220OperationStatus operation_status;\n        while(--timeout > 0) {\n            if(!bq27220_get_operation_status(handle, &operation_status)) {\n                FURI_LOG_W(TAG, \"Failed to get operation status, retries left %lu\", timeout);\n            } else if(operation_status.INITCOMP == true) {\n                break;\n            };\n            furi_delay_us(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);\n        }\n\n        if(timeout == 0) {\n            FURI_LOG_E(TAG, \"INITCOMP timeout after reset\");\n            break;\n        }\n        BQ27220_DEBUG_LOG(\"Cycles left: %lu\", timeout);\n\n        result = true;\n    } while(0);\n\n    return result;\n}\n\nbool bq27220_seal(const FuriHalI2cBusHandle* handle) {\n    Bq27220OperationStatus operation_status = {0};\n    bool result = false;\n    do {\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Status query failed\");\n            break;\n        }\n        if(operation_status.SEC == Bq27220OperationStatusSecSealed) {\n            result = true;\n            break;\n        }\n\n        if(!bq27220_control(handle, Control_SEALED)) {\n            FURI_LOG_E(TAG, \"Seal request failed\");\n            break;\n        }\n\n        furi_delay_us(BQ27220_SELECT_DELAY_US);\n\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Status query failed\");\n            break;\n        }\n        if(operation_status.SEC != Bq27220OperationStatusSecSealed) {\n            FURI_LOG_E(TAG, \"Seal failed\");\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    return result;\n}\n\nbool bq27220_unseal(const FuriHalI2cBusHandle* handle) {\n    Bq27220OperationStatus operation_status = {0};\n    bool result = false;\n    do {\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Status query failed\");\n            break;\n        }\n        if(operation_status.SEC != Bq27220OperationStatusSecSealed) {\n            result = true;\n            break;\n        }\n\n        // Hai, Kazuma desu\n        bq27220_control(handle, UnsealKey1);\n        furi_delay_us(BQ27220_MAGIC_DELAY_US);\n        bq27220_control(handle, UnsealKey2);\n        furi_delay_us(BQ27220_MAGIC_DELAY_US);\n\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Status query failed\");\n            break;\n        }\n        if(operation_status.SEC != Bq27220OperationStatusSecUnsealed) {\n            FURI_LOG_E(TAG, \"Unseal failed %u\", operation_status.SEC);\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    return result;\n}\n\nbool bq27220_full_access(const FuriHalI2cBusHandle* handle) {\n    bool result = false;\n\n    do {\n        uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_COMMON_US);\n        Bq27220OperationStatus operation_status;\n        while(--timeout > 0) {\n            if(!bq27220_get_operation_status(handle, &operation_status)) {\n                FURI_LOG_W(TAG, \"Failed to get operation status, retries left %lu\", timeout);\n            } else {\n                break;\n            };\n            furi_delay_us(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);\n        }\n\n        if(timeout == 0) {\n            FURI_LOG_E(TAG, \"Failed to get operation status\");\n            break;\n        }\n        BQ27220_DEBUG_LOG(\"Cycles left: %lu\", timeout);\n\n        // Already full access\n        if(operation_status.SEC == Bq27220OperationStatusSecFull) {\n            result = true;\n            break;\n        }\n        // Must be unsealed to get full access\n        if(operation_status.SEC != Bq27220OperationStatusSecUnsealed) {\n            FURI_LOG_E(TAG, \"Not in unsealed state\");\n            break;\n        }\n\n        // Explosion!!!\n        bq27220_control(handle, FullAccessKey); //-V760\n        furi_delay_us(BQ27220_MAGIC_DELAY_US);\n        bq27220_control(handle, FullAccessKey);\n        furi_delay_us(BQ27220_MAGIC_DELAY_US);\n\n        if(!bq27220_get_operation_status(handle, &operation_status)) {\n            FURI_LOG_E(TAG, \"Status query failed\");\n            break;\n        }\n        if(operation_status.SEC != Bq27220OperationStatusSecFull) {\n            FURI_LOG_E(TAG, \"Full access failed %u\", operation_status.SEC);\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    return result;\n}\n\nuint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandVoltage);\n}\n\nint16_t bq27220_get_current(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandCurrent);\n}\n\nbool bq27220_get_control_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220ControlStatus* control_status) {\n    return bq27220_read_reg(handle, CommandControl, (uint8_t*)control_status, 2);\n}\n\nbool bq27220_get_battery_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220BatteryStatus* battery_status) {\n    return bq27220_read_reg(handle, CommandBatteryStatus, (uint8_t*)battery_status, 2);\n}\n\nbool bq27220_get_operation_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220OperationStatus* operation_status) {\n    return bq27220_read_reg(handle, CommandOperationStatus, (uint8_t*)operation_status, 2);\n}\n\nbool bq27220_get_gauging_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220GaugingStatus* gauging_status) {\n    // Request gauging data to be loaded to MAC\n    if(!bq27220_control(handle, Control_GAUGING_STATUS)) {\n        FURI_LOG_E(TAG, \"DM SelectSubclass for read failed\");\n        return false;\n    }\n    // Wait for data being loaded to MAC\n    furi_delay_us(BQ27220_SELECT_DELAY_US);\n    // Read id data from MAC scratch space\n    return bq27220_read_reg(handle, CommandMACData, (uint8_t*)gauging_status, 2);\n}\n\nuint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandTemperature);\n}\n\nuint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandFullChargeCapacity);\n}\n\nuint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandDesignCapacity);\n}\n\nuint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandRemainingCapacity);\n}\n\nuint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandStateOfCharge);\n}\n\nuint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle) {\n    return bq27220_read_word(handle, CommandStateOfHealth);\n}\n"
  },
  {
    "path": "lib/drivers/bq27220.h",
    "content": "/** \n * @file bq27220.h\n * \n * Quite problematic chip with quite bad documentation.\n * \n * Couple things to keep in mind:\n * \n * - Datasheet and technical reference manual are full of bullshit\n * - bqstudio is ignoring them\n * - bqstudio i2c exchange tracing gives some ideas on timings that works, but there is a catch\n * - bqstudio timings contradicts to gm.fs file specification\n * - it's impossible to reproduce all situations in bqstudio\n * - experiments with blackbox can not cover all edge cases\n * - final timings are kinda blend between all of those sources\n * - device behavior differs depending on i2c clock speed\n * - The Hero Himmel would not have used this gauge in the first place\n * \n * Couple advises if you'll need to modify this driver:\n * - Reset and wait for INITCOMP if something is not right.\n * - Do not do partial config update, it takes unpredictable amount of time to apply.\n * - Don't forget to reset chip before writing new config.\n * - If something fails at config update stage, wait for 4 seconds before doing next cycle.\n * - If you can program and lock chip at factory stage - do it. It will save you a lot of time.\n * - Keep sealed or strange things may happen.\n * - There is a condition when it may stuck at INITCOMP state, just \"press reset button\".\n * \n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi_hal_i2c.h>\n\n#define BQ27220_ERROR   0x0\n#define BQ27220_SUCCESS 0x1\n\ntypedef struct {\n    // Low byte, Low bit first\n    uint8_t BATT_ID : 3; /**< Battery Identification */\n    bool SNOOZE     : 1; /**< SNOOZE mode is enabled */\n    bool BCA        : 1; /**< fuel gauge board calibration routine is active */\n    bool CCA        : 1; /**< Coulomb Counter Calibration routine is active */\n    uint8_t RSVD0   : 2; /**< Reserved */\n    // High byte, Low bit first\n    uint8_t RSVD1; /**< Reserved */\n} Bq27220ControlStatus;\n\n_Static_assert(sizeof(Bq27220ControlStatus) == 2, \"Incorrect Bq27220ControlStatus structure size\");\n\ntypedef struct {\n    // Low byte, Low bit first\n    bool DSG      : 1; /**< The device is in DISCHARGE */\n    bool SYSDWN   : 1; /**< System down bit indicating the system should shut down */\n    bool TDA      : 1; /**< Terminate Discharge Alarm */\n    bool BATTPRES : 1; /**< Battery Present detected */\n    bool AUTH_GD  : 1; /**< Detect inserted battery */\n    bool OCVGD    : 1; /**< Good OCV measurement taken */\n    bool TCA      : 1; /**< Terminate Charge Alarm */\n    bool RSVD     : 1; /**< Reserved */\n    // High byte, Low bit first\n    bool CHGINH   : 1; /**< Charge inhibit */\n    bool FC       : 1; /**< Full-charged is detected */\n    bool OTD      : 1; /**< Overtemperature in discharge condition is detected */\n    bool OTC      : 1; /**< Overtemperature in charge condition is detected */\n    bool SLEEP    : 1; /**< Device is operating in SLEEP mode when set */\n    bool OCVFAIL  : 1; /**< Status bit indicating that the OCV reading failed due to current */\n    bool OCVCOMP  : 1; /**< An OCV measurement update is complete */\n    bool FD       : 1; /**< Full-discharge is detected */\n} Bq27220BatteryStatus;\n\n_Static_assert(sizeof(Bq27220BatteryStatus) == 2, \"Incorrect Bq27220BatteryStatus structure size\");\n\ntypedef enum {\n    Bq27220OperationStatusSecSealed = 0b11,\n    Bq27220OperationStatusSecUnsealed = 0b10,\n    Bq27220OperationStatusSecFull = 0b01,\n} Bq27220OperationStatusSec;\n\ntypedef struct {\n    // Low byte, Low bit first\n    bool CALMD  : 1; /**< Calibration mode enabled */\n    uint8_t SEC : 2; /**< Current security access */\n    bool EDV2   : 1; /**< EDV2 threshold exceeded */\n    bool VDQ : 1; /**< Indicates if Current discharge cycle is NOT qualified or qualified for an FCC updated */\n    bool INITCOMP  : 1; /**< gauge initialization is complete */\n    bool SMTH      : 1; /**< RemainingCapacity is scaled by smooth engine */\n    bool BTPINT    : 1; /**< BTP threshold has been crossed */\n    // High byte, Low bit first\n    uint8_t RSVD1  : 2; /**< Reserved */\n    bool CFGUPDATE : 1; /**< Gauge is in CONFIG UPDATE mode */\n    uint8_t RSVD0  : 5; /**< Reserved */\n} Bq27220OperationStatus;\n\n_Static_assert(\n    sizeof(Bq27220OperationStatus) == 2,\n    \"Incorrect Bq27220OperationStatus structure size\");\n\ntypedef struct {\n    // Low byte, Low bit first\n    bool FD       : 1; /**< Full Discharge */\n    bool FC       : 1; /**< Full Charge */\n    bool TD       : 1; /**< Terminate Discharge */\n    bool TC       : 1; /**< Terminate Charge */\n    bool RSVD0    : 1; /**< Reserved */\n    bool EDV      : 1; /**< Cell voltage is above or below EDV0 threshold */\n    bool DSG      : 1; /**< DISCHARGE or RELAXATION */\n    bool CF       : 1; /**< Battery conditioning is needed */\n    // High byte, Low bit first\n    uint8_t RSVD1 : 2; /**< Reserved */\n    bool FCCX     : 1; /**< fcc1hz clock going into CC: 0 = 1 Hz, 1 = 16 Hz*/\n    uint8_t RSVD2 : 2; /**< Reserved */\n    bool EDV1     : 1; /**< Cell voltage is above or below EDV1 threshold */\n    bool EDV2     : 1; /**< Cell voltage is above or below EDV2 threshold */\n    bool VDQ      : 1; /**< Charge cycle FCC update qualification */\n} Bq27220GaugingStatus;\n\n_Static_assert(sizeof(Bq27220GaugingStatus) == 2, \"Incorrect Bq27220GaugingStatus structure size\");\n\ntypedef struct BQ27220DMData BQ27220DMData;\n\n/** Initialize Driver\n * \n * This routine performs a lot of things under the hood:\n * - Verifies that gauge is present on i2c bus and got correct ID(0220)\n * - Unseals gauge\n * - Checks various internal statuses\n * - Checks that current profile is 0\n * - Checks configuration again provided data_memory\n * - Reset gauge if something on previous stages was fishy\n * - Updates configuration if needed\n * - Sealing gauge to prevent configuration and state from accidental damage\n *\n * @param      handle       The I2C Bus handle\n * @param[in]  data_memory  The data memory to be uploaded into gauge\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory);\n\n/** Reset gauge\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_reset(const FuriHalI2cBusHandle* handle);\n\n/** Seal gauge access\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_seal(const FuriHalI2cBusHandle* handle);\n\n/** Unseal gauge access\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_unseal(const FuriHalI2cBusHandle* handle);\n\n/** Get full access\n *\n * @warning    must be done in unsealed state\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_full_access(const FuriHalI2cBusHandle* handle);\n\n/** Get battery voltage\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     voltage in mV or BQ27220_ERROR\n */\nuint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle);\n\n/** Get current\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     current in mA or BQ27220_ERROR\n */\nint16_t bq27220_get_current(const FuriHalI2cBusHandle* handle);\n\n/** Get control status\n *\n * @param      handle          The handle\n * @param      control_status  The control status\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_get_control_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220ControlStatus* control_status);\n\n/** Get battery status\n *\n * @param      handle          The handle\n * @param      battery_status  The battery status\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_get_battery_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220BatteryStatus* battery_status);\n\n/** Get operation status\n *\n * @param      handle            The handle\n * @param      operation_status  The operation status\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_get_operation_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220OperationStatus* operation_status);\n\n/** Get gauging status\n *\n * @param      handle          The handle\n * @param      gauging_status  The gauging status\n *\n * @return     true on success, false otherwise\n */\nbool bq27220_get_gauging_status(\n    const FuriHalI2cBusHandle* handle,\n    Bq27220GaugingStatus* gauging_status);\n\n/** Get temperature\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     temperature in units of 0.1°K\n */\nuint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle);\n\n/** Get compensated full charge capacity\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     full charge capacity in mAh or BQ27220_ERROR\n */\nuint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle);\n\n/** Get design capacity\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     design capacity in mAh or BQ27220_ERROR\n */\nuint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle);\n\n/** Get remaining capacity\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     remaining capacity in mAh or BQ27220_ERROR\n */\nuint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle);\n\n/** Get predicted remaining battery capacity\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     state of charge in percents or BQ27220_ERROR\n */\nuint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle);\n\n/** Get ratio of full charge capacity over design capacity\n *\n * @param      handle  The I2C Bus handle\n *\n * @return     state of health in percents or BQ27220_ERROR\n */\nuint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle);\n"
  },
  {
    "path": "lib/drivers/bq27220_data_memory.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\ntypedef enum {\n    BQ27220DMTypeEnd,\n    BQ27220DMTypeWait,\n    BQ27220DMTypeU8,\n    BQ27220DMTypeU16,\n    BQ27220DMTypeU32,\n    BQ27220DMTypeI8,\n    BQ27220DMTypeI16,\n    BQ27220DMTypeI32,\n    BQ27220DMTypeF32,\n    BQ27220DMTypePtr8,\n    BQ27220DMTypePtr16,\n    BQ27220DMTypePtr32,\n} BQ27220DMType;\n\ntypedef enum {\n    BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig = 0x929B,\n    BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity = 0x929D,\n    BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity = 0x929F,\n    BQ27220DMAddressGasGaugingCEDVProfile1EMF = 0x92A3,\n    BQ27220DMAddressGasGaugingCEDVProfile1C0 = 0x92A9,\n    BQ27220DMAddressGasGaugingCEDVProfile1R0 = 0x92AB,\n    BQ27220DMAddressGasGaugingCEDVProfile1T0 = 0x92AD,\n    BQ27220DMAddressGasGaugingCEDVProfile1R1 = 0x92AF,\n    BQ27220DMAddressGasGaugingCEDVProfile1TC = 0x92B1,\n    BQ27220DMAddressGasGaugingCEDVProfile1C1 = 0x92B2,\n    BQ27220DMAddressGasGaugingCEDVProfile1EDV0 = 0x92B4,\n    BQ27220DMAddressGasGaugingCEDVProfile1EDV1 = 0x92B7,\n    BQ27220DMAddressGasGaugingCEDVProfile1EDV2 = 0x92BA,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0 = 0x92BD,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10 = 0x92BF,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20 = 0x92C1,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30 = 0x92C3,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40 = 0x92C5,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50 = 0x92C7,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60 = 0x92C9,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70 = 0x92CB,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80 = 0x92CD,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90 = 0x92CF,\n    BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100 = 0x92D1,\n    BQ27220DMAddressCalibrationCurrentDeadband = 0x91DE,\n    BQ27220DMAddressConfigurationPowerSleepCurrent = 0x9217,\n    BQ27220DMAddressConfigurationCurrentThresholdsDischargeDetectionThreshold = 0x9228,\n    BQ27220DMAddressConfigurationDataInitialStandby = 0x923C,\n} BQ27220DMAddress;\n\ntypedef struct BQ27220DMData BQ27220DMData;\n\nstruct BQ27220DMData {\n    uint16_t type;\n    uint16_t address;\n    union {\n        uint8_t u8;\n        uint16_t u16;\n        uint32_t u32;\n        int8_t i8;\n        int16_t i16;\n        int32_t i32;\n        float f32;\n    } value;\n};\n\ntypedef struct {\n    // Low byte, Low bit first\n    const bool CCT        : 1;\n    const bool CSYNC      : 1;\n    const bool RSVD0      : 1;\n    const bool EDV_CMP    : 1;\n    const bool SC         : 1;\n    const bool FIXED_EDV0 : 1;\n    const uint8_t RSVD1   : 2;\n    // High byte, Low bit first\n    const bool FCC_LIM    : 1;\n    const bool RSVD2      : 1;\n    const bool FC_FOR_VDQ : 1;\n    const bool IGNORE_SD  : 1;\n    const bool SME0       : 1;\n    const uint8_t RSVD3   : 3;\n} BQ27220DMGaugingConfig;\n\n_Static_assert(sizeof(BQ27220DMGaugingConfig) == 2, \"Incorrect structure size\");\n"
  },
  {
    "path": "lib/drivers/bq27220_reg.h",
    "content": "#pragma once\n\n#define BQ27220_ADDRESS     (0xAAu)\n#define BQ27220_I2C_TIMEOUT (50u)\n\n#define CommandControl             (0x00u)\n#define CommandAtRate              (0x02u)\n#define CommandAtRateTimeToEmpty   (0x04u)\n#define CommandTemperature         (0x06u)\n#define CommandVoltage             (0x08u)\n#define CommandBatteryStatus       (0x0Au)\n#define CommandCurrent             (0x0Cu)\n#define CommandRemainingCapacity   (0x10u)\n#define CommandFullChargeCapacity  (0x12u)\n#define CommandAverageCurrent      (0x14u)\n#define CommandTimeToEmpty         (0x16u)\n#define CommandTimeToFull          (0x18u)\n#define CommandStandbyCurrent      (0x1Au)\n#define CommandStandbyTimeToEmpty  (0x1Cu)\n#define CommandMaxLoadCurrent      (0x1Eu)\n#define CommandMaxLoadTimeToEmpty  (0x20u)\n#define CommandRawCoulombCount     (0x22u)\n#define CommandAveragePower        (0x24u)\n#define CommandInternalTemperature (0x28u)\n#define CommandCycleCount          (0x2Au)\n#define CommandStateOfCharge       (0x2Cu)\n#define CommandStateOfHealth       (0x2Eu)\n#define CommandChargeVoltage       (0x30u)\n#define CommandChargeCurrent       (0x32u)\n#define CommandBTPDischargeSet     (0x34u)\n#define CommandBTPChargeSet        (0x36u)\n#define CommandOperationStatus     (0x3Au)\n#define CommandDesignCapacity      (0x3Cu)\n#define CommandSelectSubclass      (0x3Eu)\n#define CommandMACData             (0x40u)\n#define CommandMACDataSum          (0x60u)\n#define CommandMACDataLen          (0x61u)\n#define CommandAnalogCount         (0x79u)\n#define CommandRawCurrent          (0x7Au)\n#define CommandRawVoltage          (0x7Cu)\n#define CommandRawIntTemp          (0x7Eu)\n\n#define Control_CONTROL_STATUS         (0x0000u)\n#define Control_DEVICE_NUMBER          (0x0001u)\n#define Control_FW_VERSION             (0x0002u)\n#define Control_HW_VERSION             (0x0003u)\n#define Control_BOARD_OFFSET           (0x0009u)\n#define Control_CC_OFFSET              (0x000Au)\n#define Control_CC_OFFSET_SAVE         (0x000Bu)\n#define Control_OCV_CMD                (0x000Cu)\n#define Control_BAT_INSERT             (0x000Du)\n#define Control_BAT_REMOVE             (0x000Eu)\n#define Control_SET_SNOOZE             (0x0013u)\n#define Control_CLEAR_SNOOZE           (0x0014u)\n#define Control_SET_PROFILE_1          (0x0015u)\n#define Control_SET_PROFILE_2          (0x0016u)\n#define Control_SET_PROFILE_3          (0x0017u)\n#define Control_SET_PROFILE_4          (0x0018u)\n#define Control_SET_PROFILE_5          (0x0019u)\n#define Control_SET_PROFILE_6          (0x001Au)\n#define Control_CAL_TOGGLE             (0x002Du)\n#define Control_SEALED                 (0x0030u)\n#define Control_RESET                  (0x0041u)\n#define Control_OERATION_STATUS        (0x0054u)\n#define Control_GAUGING_STATUS         (0x0056u)\n#define Control_EXIT_CAL               (0x0080u)\n#define Control_ENTER_CAL              (0x0081u)\n#define Control_ENTER_CFG_UPDATE       (0x0090u)\n#define Control_EXIT_CFG_UPDATE_REINIT (0x0091u)\n#define Control_EXIT_CFG_UPDATE        (0x0092u)\n#define Control_RETURN_TO_ROM          (0x0F00u)\n\n#define UnsealKey1 (0x0414u)\n#define UnsealKey2 (0x3672u)\n\n#define FullAccessKey (0xffffu)\n"
  },
  {
    "path": "lib/drivers/cc1101.c",
    "content": "#include \"cc1101.h\"\n#include <assert.h>\n#include <string.h>\n#include <furi_hal_cortex.h>\n\nstatic bool\n    cc1101_spi_trx(const FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) {\n    FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CC1101_TIMEOUT * 1000);\n\n    while(furi_hal_gpio_read(handle->miso)) {\n        if(furi_hal_cortex_timer_is_expired(timer)) {\n            //timeout\n            return false;\n        }\n    }\n    if(!furi_hal_spi_bus_trx(handle, tx, rx, size, CC1101_TIMEOUT)) return false;\n    return true;\n}\n\nCC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe) {\n    uint8_t tx[1] = {strobe};\n    CC1101Status rx[1] = {0};\n    rx[0].CHIP_RDYn = 1;\n\n    cc1101_spi_trx(handle, tx, (uint8_t*)rx, 1);\n\n    assert(rx[0].CHIP_RDYn == 0);\n    return rx[0];\n}\n\nCC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {\n    uint8_t tx[2] = {reg, data};\n    CC1101Status rx[2] = {0};\n    rx[0].CHIP_RDYn = 1;\n    rx[1].CHIP_RDYn = 1;\n\n    cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2);\n\n    assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0);\n    return rx[1];\n}\n\nCC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) {\n    assert(sizeof(CC1101Status) == 1);\n    uint8_t tx[2] = {reg | CC1101_READ, 0};\n    CC1101Status rx[2] = {0};\n    rx[0].CHIP_RDYn = 1;\n\n    cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2);\n\n    assert((rx[0].CHIP_RDYn) == 0);\n    *data = *(uint8_t*)&rx[1];\n    return rx[0];\n}\n\nuint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle) {\n    uint8_t partnumber = 0;\n    cc1101_read_reg(handle, CC1101_STATUS_PARTNUM | CC1101_BURST, &partnumber);\n    return partnumber;\n}\n\nuint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle) {\n    uint8_t version = 0;\n    cc1101_read_reg(handle, CC1101_STATUS_VERSION | CC1101_BURST, &version);\n    return version;\n}\n\nuint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle) {\n    uint8_t rssi = 0;\n    cc1101_read_reg(handle, CC1101_STATUS_RSSI | CC1101_BURST, &rssi);\n    return rssi;\n}\n\nCC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SRES);\n}\n\nCC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SNOP);\n}\n\nbool cc1101_wait_status_state(\n    const FuriHalSpiBusHandle* handle,\n    CC1101State state,\n    uint32_t timeout_us) {\n    bool result = false;\n    CC1101Status status = {0};\n    FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us);\n    while(!furi_hal_cortex_timer_is_expired(timer)) {\n        status = cc1101_strobe(handle, CC1101_STROBE_SNOP);\n        if(status.STATE == state) {\n            result = true;\n            break;\n        }\n    }\n    return result;\n}\n\nCC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SPWD);\n}\n\nCC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SCAL);\n}\n\nCC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SIDLE);\n}\n\nCC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SRX);\n}\n\nCC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_STX);\n}\n\nCC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SFRX);\n}\n\nCC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle) {\n    return cc1101_strobe(handle, CC1101_STROBE_SFTX);\n}\n\nuint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) {\n    uint64_t real_value = (uint64_t)value * CC1101_FDIV / CC1101_QUARTZ;\n\n    // Sanity check\n    assert((real_value & CC1101_FMASK) == real_value);\n\n    cc1101_write_reg(handle, CC1101_FREQ2, (real_value >> 16) & 0xFF);\n    cc1101_write_reg(handle, CC1101_FREQ1, (real_value >> 8) & 0xFF);\n    cc1101_write_reg(handle, CC1101_FREQ0, (real_value >> 0) & 0xFF);\n\n    uint64_t real_frequency = real_value * CC1101_QUARTZ / CC1101_FDIV;\n\n    return (uint32_t)real_frequency;\n}\n\nuint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) {\n    uint64_t real_value = value * CC1101_IFDIV / CC1101_QUARTZ;\n    assert((real_value & 0xFF) == real_value);\n\n    cc1101_write_reg(handle, CC1101_FSCTRL0, (real_value >> 0) & 0xFF);\n\n    uint64_t real_frequency = real_value * CC1101_QUARTZ / CC1101_IFDIV;\n\n    return (uint32_t)real_frequency;\n}\n\nvoid cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]) {\n    uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009\n    CC1101Status rx[9] = {0};\n    rx[0].CHIP_RDYn = 1;\n    rx[8].CHIP_RDYn = 1;\n\n    memcpy(&tx[1], &value[0], 8);\n\n    cc1101_spi_trx(handle, tx, (uint8_t*)rx, sizeof(rx));\n\n    assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0);\n}\n\nuint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) {\n    uint8_t buff_tx[64];\n    uint8_t buff_rx[64];\n    buff_tx[0] = CC1101_FIFO | CC1101_BURST;\n    memcpy(&buff_tx[1], data, size);\n\n    cc1101_spi_trx(handle, buff_tx, (uint8_t*)buff_rx, size + 1);\n\n    return size;\n}\n\nuint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) {\n    uint8_t buff_trx[2];\n    buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST;\n\n    cc1101_spi_trx(handle, buff_trx, buff_trx, 2);\n\n    // Check that the packet is placed in the receive buffer\n    if(buff_trx[1] > 64) {\n        *size = 64;\n    } else {\n        *size = buff_trx[1];\n    }\n    furi_hal_spi_bus_trx(handle, NULL, data, *size, CC1101_TIMEOUT);\n\n    return *size;\n}\n"
  },
  {
    "path": "lib/drivers/cc1101.h",
    "content": "#pragma once\n\n#include \"cc1101_regs.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <furi_hal_spi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Low level API */\n\n/** Strobe command to the device\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      strobe  - command to execute\n *\n * @return     device status\n */\nCC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe);\n\n/** Write device register\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      reg     - register\n * @param      data    - data to write\n *\n * @return     device status\n */\nCC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data);\n\n/** Read device register\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      reg     - register\n * @param[out] data    - pointer to data\n *\n * @return     device status\n */\nCC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data);\n\n/* High level API */\n\n/** Reset\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle);\n\n/** Get status\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n *\n * @return     CC1101Status structure\n */\nCC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle);\n\n/** Wait specific chip state\n *\n * @param      handle      The SPI bus handle\n * @param[in]  state       The state to wait\n * @param[in]  timeout_us  The timeout in microseconds\n *\n * @return     true on success, false otherwise\n */\nbool cc1101_wait_status_state(\n    const FuriHalSpiBusHandle* handle,\n    CC1101State state,\n    uint32_t timeout_us);\n\n/** Enable shutdown mode\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle);\n\n/** Get Partnumber\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n *\n * @return     part number id\n */\nuint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle);\n\n/** Get Version\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n *\n * @return     version\n */\nuint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle);\n\n/** Get raw RSSI value\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n *\n * @return     rssi value\n */\nuint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle);\n\n/** Calibrate oscillator\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle);\n\n/** Switch to idle\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n */\nCC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle);\n\n/** Switch to RX\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle);\n\n/** Switch to TX\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle);\n\n/** Flush RX FIFO\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * \n * @return     CC1101Status structure\n */\nCC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle);\n\n/** Flush TX FIFO\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n */\nCC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle);\n\n/** Set Frequency\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      value   - frequency in herz\n *\n * @return     real frequency that were synthesized\n */\nuint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value);\n\n/** Set Intermediate Frequency\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      value   - frequency in herz\n *\n * @return     real inermediate frequency that were synthesized\n */\nuint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value);\n\n/** Set Power Amplifier level table, ramp\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      value   - array of power level values\n */\nvoid cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]);\n\n/** Write FIFO\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      data    pointer to byte array\n * @param      size    write bytes count\n *\n * @return     size, written bytes count\n */\nuint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size);\n\n/** Read FIFO\n *\n * @param      handle  - pointer to FuriHalSpiHandle\n * @param      data    pointer to byte array\n * @param      size    bytes to read from fifo\n *\n * @return     size, read bytes count\n */\nuint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/drivers/cc1101_regs.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Frequency Synthesizer constants */\n#define CC1101_QUARTZ 26000000\n#define CC1101_FMASK  0xFFFFFF\n#define CC1101_FDIV   0x10000\n#define CC1101_IFDIV  0x400\n\n/* IO Bus constants */\n#define CC1101_TIMEOUT 250\n\n/* Bits and pieces */\n#define CC1101_READ  (1 << 7) /** Read Bit */\n#define CC1101_BURST (1 << 6) /** Burst Bit */\n\n/* Common registers, CC1101_BURST and CC1101_WRITE behaves as expected  */\n#define CC1101_IOCFG2   0x00 /** GDO2 output pin configuration */\n#define CC1101_IOCFG1   0x01 /** GDO1 output pin configuration */\n#define CC1101_IOCFG0   0x02 /** GDO0 output pin configuration */\n#define CC1101_FIFOTHR  0x03 /** RX FIFO and TX FIFO thresholds */\n#define CC1101_SYNC1    0x04 /** Sync word, high byte */\n#define CC1101_SYNC0    0x05 /** Sync word, low byte */\n#define CC1101_PKTLEN   0x06 /** Packet length */\n#define CC1101_PKTCTRL1 0x07 /** Packet automation control */\n#define CC1101_PKTCTRL0 0x08 /** Packet automation control */\n#define CC1101_ADDR     0x09 /** Device address */\n#define CC1101_CHANNR   0x0A /** Channel number */\n#define CC1101_FSCTRL1  0x0B /** Frequency synthesizer control */\n#define CC1101_FSCTRL0  0x0C /** Frequency synthesizer control */\n#define CC1101_FREQ2    0x0D /** Frequency control word, high byte */\n#define CC1101_FREQ1    0x0E /** Frequency control word, middle byte */\n#define CC1101_FREQ0    0x0F /** Frequency control word, low byte */\n#define CC1101_MDMCFG4  0x10 /** Modem configuration */\n#define CC1101_MDMCFG3  0x11 /** Modem configuration */\n#define CC1101_MDMCFG2  0x12 /** Modem configuration */\n#define CC1101_MDMCFG1  0x13 /** Modem configuration */\n#define CC1101_MDMCFG0  0x14 /** Modem configuration */\n#define CC1101_DEVIATN  0x15 /** Modem deviation setting */\n#define CC1101_MCSM2    0x16 /** Main Radio Control State Machine configuration */\n#define CC1101_MCSM1    0x17 /** Main Radio Control State Machine configuration */\n#define CC1101_MCSM0    0x18 /** Main Radio Control State Machine configuration */\n#define CC1101_FOCCFG   0x19 /** Frequency Offset Compensation configuration */\n#define CC1101_BSCFG    0x1A /** Bit Synchronization configuration */\n#define CC1101_AGCCTRL2 0x1B /** AGC control */\n#define CC1101_AGCCTRL1 0x1C /** AGC control */\n#define CC1101_AGCCTRL0 0x1D /** AGC control */\n#define CC1101_WOREVT1  0x1E /** High byte Event 0 timeout */\n#define CC1101_WOREVT0  0x1F /** Low byte Event 0 timeout */\n#define CC1101_WORCTRL  0x20 /** Wake On Radio control */\n#define CC1101_FREND1   0x21 /** Front end RX configuration */\n#define CC1101_FREND0   0x22 /** Front end TX configuration */\n#define CC1101_FSCAL3   0x23 /** Frequency synthesizer calibration */\n#define CC1101_FSCAL2   0x24 /** Frequency synthesizer calibration */\n#define CC1101_FSCAL1   0x25 /** Frequency synthesizer calibration */\n#define CC1101_FSCAL0   0x26 /** Frequency synthesizer calibration */\n#define CC1101_RCCTRL1  0x27 /** RC oscillator configuration */\n#define CC1101_RCCTRL0  0x28 /** RC oscillator configuration */\n#define CC1101_FSTEST   0x29 /** Frequency synthesizer calibration control */\n#define CC1101_PTEST    0x2A /** Production test */\n#define CC1101_AGCTEST  0x2B /** AGC test */\n#define CC1101_TEST2    0x2C /** Various test settings */\n#define CC1101_TEST1    0x2D /** Various test settings */\n#define CC1101_TEST0    0x2E /** Various test settings */\n\n/* Strobe registers, CC1101_BURST is not available, CC1101_WRITE ignored */\n#define CC1101_STROBE_SRES 0x30 /** Reset chip. */\n#define CC1101_STROBE_SFSTXON \\\n    0x31 /** Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround). */\n#define CC1101_STROBE_SXOFF 0x32 /** Turn off crystal oscillator. */\n#define CC1101_STROBE_SCAL \\\n    0x33 /** Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without setting manual calibration mode (MCSM0.FS_AUTOCAL=0) */\n#define CC1101_STROBE_SRX \\\n    0x34 /** Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1. */\n#define CC1101_STROBE_STX \\\n    0x35 /** In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: Only go to TX if channel is clear. */\n#define CC1101_STROBE_SIDLE \\\n    0x36 /** Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable. */\n#define CC1101_STROBE_SWOR \\\n    0x38 /** Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0. */\n/* 0x37 is unused */\n#define CC1101_STROBE_SPWD 0x39 /** Enter power down mode when CSn goes high. */\n#define CC1101_STROBE_SFRX \\\n    0x3A /** Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states. */\n#define CC1101_STROBE_SFTX \\\n    0x3B /** Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states. */\n#define CC1101_STROBE_SWORRST 0x3C /** Reset real time clock to Event1 value. */\n#define CC1101_STROBE_SNOP \\\n    0x3D /** No operation. May be used to get access to the chip status byte.*/\n\n/* Status registers, must be accessed with CC1101_BURST, but one by one */\n#define CC1101_STATUS_PARTNUM    0x30 /** Chip ID Part Number */\n#define CC1101_STATUS_VERSION    0x31 /** Chip ID Version */\n#define CC1101_STATUS_FREQEST    0x32 /** Frequency Offset Estimate from Demodulator */\n#define CC1101_STATUS_LQI        0x33 /** Demodulator Estimate for Link Quality, 7bit-CRC, 6..0-LQI*/\n#define CC1101_STATUS_RSSI       0x34 /** Received Signal Strength Indication */\n#define CC1101_STATUS_MARCSTATE  0x35 /** Main Radio Control State Machine State */\n#define CC1101_STATUS_WORTIME1   0x36 /** High Byte of WOR Time */\n#define CC1101_STATUS_WORTIME0   0x37 /** Low Byte of WOR Time */\n#define CC1101_STATUS_PKTSTATUS  0x38 /** Current GDOx Status and Packet Status */\n#define CC1101_STATUS_VCO_VC_DAC 0x39 /** Current Setting from PLL Calibration Module */\n#define CC1101_STATUS_TXBYTES \\\n    0x3A /** Underflow and Number of Bytes, 7bit-Underflow, 6..0-Number of Bytes*/\n#define CC1101_STATUS_RXBYTES \\\n    0x3B /** Overflow and Number of Bytes, 7bit-Overflow*, 6..0-Number of Bytes*/\n#define CC1101_STATUS_RCCTRL1_STATUS 0x3C /** Last RC Oscillator Calibration Result */\n#define CC1101_STATUS_RCCTRL0_STATUS 0x3D /** Last RC Oscillator Calibration Result */\n\n/* Some special registers, use CC1101_BURST to read/write data */\n#define CC1101_PATABLE \\\n    0x3E /** PATABLE register number, an 8-byte table that defines the PA control settings */\n#define CC1101_FIFO \\\n    0x3F /** FIFO register nunmber, can be combined with CC1101_WRITE and/or CC1101_BURST */\n#define CC1101_IOCFG_INV (1 << 6) /** IOCFG inversion */\n\ntypedef enum {\n    CC1101IocfgRxFifoThreshold = 0x00,\n    CC1101IocfgRxFifoThresholdOrPacket = 0x01,\n    CC1101IocfgTxFifoThreshold = 0x02,\n    CC1101IocfgTxFifoFull = 0x03,\n    CC1101IocfgRxOverflow = 0x04,\n    CC1101IocfgTxUnderflow = 0x05,\n    CC1101IocfgSyncWord = 0x06,\n    CC1101IocfgPacket = 0x07,\n    CC1101IocfgPreamble = 0x08,\n    CC1101IocfgClearChannel = 0x09,\n    CC1101IocfgLockDetector = 0x0A,\n    CC1101IocfgSerialClock = 0x0B,\n    CC1101IocfgSerialSynchronousDataOutput = 0x0C,\n    CC1101IocfgSerialDataOutput = 0x0D,\n    CC1101IocfgCarrierSense = 0x0E,\n    CC1101IocfgCrcOk = 0x0F,\n    /* Reserved range: 0x10 - 0x15 */\n    CC1101IocfgRxHardData1 = 0x16,\n    CC1101IocfgRxHardData0 = 0x17,\n    /* Reserved range: 0x18 - 0x1A */\n    CC1101IocfgPaPd = 0x1B,\n    CC1101IocfgLnaPd = 0x1C,\n    CC1101IocfgRxSymbolTick = 0x1D,\n    /* Reserved range: 0x1E - 0x23 */\n    CC1101IocfgWorEvnt0 = 0x24,\n    CC1101IocfgWorEvnt1 = 0x25,\n    CC1101IocfgClk256 = 0x26,\n    CC1101IocfgClk32k = 0x27,\n    /* Reserved: 0x28 */\n    CC1101IocfgChpRdyN = 0x29,\n    /* Reserved: 0x2A */\n    CC1101IocfgXoscStable = 0x2B,\n    /* Reserved range: 0x2C - 0x2D */\n    CC1101IocfgHighImpedance = 0x2E,\n    CC1101IocfgHW = 0x2F,\n    /* Only one CC1101IocfgClkXoscN can be selected as an output at any time */\n    CC1101IocfgClkXosc1 = 0x30,\n    CC1101IocfgClkXosc1_5 = 0x31,\n    CC1101IocfgClkXosc2 = 0x32,\n    CC1101IocfgClkXosc3 = 0x33,\n    CC1101IocfgClkXosc4 = 0x34,\n    CC1101IocfgClkXosc6 = 0x35,\n    CC1101IocfgClkXosc8 = 0x36,\n    CC1101IocfgClkXosc12 = 0x37,\n    CC1101IocfgClkXosc16 = 0x38,\n    CC1101IocfgClkXosc24 = 0x39,\n    CC1101IocfgClkXosc32 = 0x3A,\n    CC1101IocfgClkXosc48 = 0x3B,\n    CC1101IocfgClkXosc64 = 0x3C,\n    CC1101IocfgClkXosc96 = 0x3D,\n    CC1101IocfgClkXosc128 = 0x3E,\n    CC1101IocfgClkXosc192 = 0x3F,\n} CC1101Iocfg;\n\ntypedef enum {\n    CC1101StateIDLE = 0b000, /** IDLE state */\n    CC1101StateRX = 0b001, /** Receive mode */\n    CC1101StateTX = 0b010, /** Transmit mode */\n    CC1101StateFSTXON = 0b011, /** Fast TX ready */\n    CC1101StateCALIBRATE = 0b100, /** Frequency synthesizer calibration is running */\n    CC1101StateSETTLING = 0b101, /** PLL is settling */\n    CC1101StateRXFIFO_OVERFLOW =\n        0b110, /** RX FIFO has overflowed. Read out any useful data, then flush the FIFO with SFRX */\n    CC1101StateTXFIFO_UNDERFLOW = 0b111, /** TX FIFO has underflowed. Acknowledge with SFTX */\n} CC1101State;\n\ntypedef struct {\n    uint8_t FIFO_BYTES_AVAILABLE : 4;\n    CC1101State STATE            : 3;\n    bool CHIP_RDYn               : 1;\n} CC1101Status;\n\ntypedef union {\n    CC1101Status status;\n    uint8_t status_raw;\n} CC1101StatusRaw;\n\ntypedef struct {\n    uint8_t NUM_TXBYTES   : 7;\n    bool TXFIFO_UNDERFLOW : 1;\n} CC1101TxBytes;\n\ntypedef struct {\n    uint8_t NUM_RXBYTES  : 7;\n    bool RXFIFO_OVERFLOW : 1;\n} CC1101RxBytes;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/drivers/lp5562.c",
    "content": "#include \"lp5562.h\"\n#include <core/common_defines.h>\n#include \"lp5562_reg.h\"\n#include <furi_hal.h>\n\nvoid lp5562_reset(const FuriHalI2cBusHandle* handle) {\n    Reg0D_Reset reg = {.value = 0xFF};\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);\n}\n\nvoid lp5562_configure(const FuriHalI2cBusHandle* handle) {\n    Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true};\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x08, *(uint8_t*)&config, LP5562_I2C_TIMEOUT);\n\n    Reg70_LedMap map = {\n        .red = EngSelectI2C,\n        .green = EngSelectI2C,\n        .blue = EngSelectI2C,\n        .white = EngSelectI2C,\n    };\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, *(uint8_t*)&map, LP5562_I2C_TIMEOUT);\n}\n\nvoid lp5562_enable(const FuriHalI2cBusHandle* handle) {\n    Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true};\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);\n    //>488μs delay is required after writing to 0x00 register, otherwise program engine will not work\n    furi_delay_us(500);\n}\n\nvoid lp5562_set_channel_current(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    uint8_t value) {\n    uint8_t reg_no;\n    if(channel == LP5562ChannelRed) {\n        reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER;\n    } else if(channel == LP5562ChannelGreen) {\n        reg_no = LP5562_CHANNEL_GREEN_CURRENT_REGISTER;\n    } else if(channel == LP5562ChannelBlue) {\n        reg_no = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;\n    } else if(channel == LP5562ChannelWhite) {\n        reg_no = LP5562_CHANNEL_WHITE_CURRENT_REGISTER;\n    } else {\n        return;\n    }\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);\n}\n\nvoid lp5562_set_channel_value(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    uint8_t value) {\n    uint8_t reg_no;\n    if(channel == LP5562ChannelRed) {\n        reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelGreen) {\n        reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelBlue) {\n        reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelWhite) {\n        reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER;\n    } else {\n        return;\n    }\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);\n}\n\nuint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel) {\n    uint8_t reg_no;\n    uint8_t value;\n    if(channel == LP5562ChannelRed) {\n        reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelGreen) {\n        reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelBlue) {\n        reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER;\n    } else if(channel == LP5562ChannelWhite) {\n        reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER;\n    } else {\n        return 0;\n    }\n    furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, reg_no, &value, LP5562_I2C_TIMEOUT);\n    return value;\n}\n\nvoid lp5562_set_channel_src(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    LP5562Engine src) {\n    uint8_t reg_val = 0;\n    uint8_t bit_offset = 0;\n\n    do {\n        if(channel & LP5562ChannelRed) {\n            bit_offset = 4;\n            channel &= ~LP5562ChannelRed;\n        } else if(channel & LP5562ChannelGreen) {\n            bit_offset = 2;\n            channel &= ~LP5562ChannelGreen;\n        } else if(channel & LP5562ChannelBlue) {\n            bit_offset = 0;\n            channel &= ~LP5562ChannelBlue;\n        } else if(channel & LP5562ChannelWhite) {\n            bit_offset = 6;\n            channel &= ~LP5562ChannelWhite;\n        } else {\n            return;\n        }\n\n        furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x70, &reg_val, LP5562_I2C_TIMEOUT);\n        reg_val &= ~(0x3 << bit_offset);\n        reg_val |= ((src & 0x03) << bit_offset);\n        furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT);\n    } while(channel != 0);\n}\n\nvoid lp5562_execute_program(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint16_t* program) {\n    if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return;\n    uint8_t reg_val = 0;\n    uint8_t bit_offset = 0;\n    uint8_t enable_reg = 0;\n\n    // Read old value of enable register\n    furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x00, &enable_reg, LP5562_I2C_TIMEOUT);\n\n    // Engine configuration\n    bit_offset = (3 - eng) * 2;\n    furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);\n    reg_val &= ~(0x3 << bit_offset);\n    reg_val |= (0x01 << bit_offset); // load\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);\n    furi_delay_us(100);\n\n    // Program load\n    for(uint8_t i = 0; i < 16; i++) {\n        // Program words are big-endian, so reverse byte order before loading\n        program[i] = __REV16(program[i]);\n    }\n    furi_hal_i2c_write_mem(\n        handle,\n        LP5562_ADDRESS,\n        0x10 + (0x20 * (eng - 1)),\n        (uint8_t*)program,\n        16 * 2,\n        LP5562_I2C_TIMEOUT);\n\n    // Program start\n    bit_offset = (3 - eng) * 2;\n    furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);\n    reg_val &= ~(0x3 << bit_offset);\n    reg_val |= (0x02 << bit_offset); // run\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);\n\n    // Switch output to Execution Engine\n    lp5562_set_channel_src(handle, ch, eng);\n\n    enable_reg &= ~(0x3 << bit_offset);\n    enable_reg |= (0x02 << bit_offset); // run\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT);\n}\n\nvoid lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng) {\n    if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return;\n    uint8_t reg_val = 0;\n    uint8_t bit_offset = 0;\n\n    // Engine configuration\n    bit_offset = (3 - eng) * 2;\n    furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);\n    reg_val &= ~(0x3 << bit_offset);\n    // Not setting lowest 2 bits here\n    furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);\n}\n\nvoid lp5562_execute_ramp(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint8_t val_start,\n    uint8_t val_end,\n    uint16_t time) {\n    if(val_start == val_end) return;\n\n    // Temporary switch to constant value from register\n    lp5562_set_channel_src(handle, ch, LP5562Direct);\n\n    // Prepare command sequence\n    uint16_t program[16];\n    uint8_t diff = (val_end > val_start) ? (val_end - val_start) : (val_start - val_end);\n    if(diff == 0) { // Making division below safer\n        diff = 1;\n    }\n    uint16_t time_step = time * 2 / diff;\n    uint8_t prescaller = 0;\n    if(time_step > 0x3F) {\n        time_step /= 32;\n        prescaller = 1;\n    }\n\n    if(time_step == 0) {\n        time_step = 1;\n    } else if(time_step > 0x3F)\n        time_step = 0x3F;\n\n    program[0] = 0x4000 | val_start; // Set PWM\n    if(val_end > val_start) {\n        program[1] = (prescaller << 14) | (time_step << 8) | ((diff / 2) & 0x7F); // Ramp Up\n    } else {\n        program[1] = (prescaller << 14) | (time_step << 8) | 0x80 |\n                     ((diff / 2) & 0x7F); // Ramp Down\n    }\n    program[2] = 0xA001 | ((2 - 1) << 7); // Loop to step 1, repeat twice to get full 8-bit scale\n    program[3] = 0xC000; // End\n\n    // Execute program\n    lp5562_execute_program(handle, eng, LP5562ChannelWhite, program);\n\n    // Write end value to register\n    lp5562_set_channel_value(handle, ch, val_end);\n}\n\nvoid lp5562_execute_blink(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint16_t on_time,\n    uint16_t period,\n    uint8_t brightness) {\n    // Temporary switch to constant value from register\n    lp5562_set_channel_src(handle, ch, LP5562Direct);\n\n    // Prepare command sequence\n    uint16_t program[16];\n    uint16_t time_step = 0;\n    uint8_t prescaller = 0;\n\n    program[0] = 0x4000 | brightness; // Set PWM\n\n    time_step = on_time * 2;\n    if(time_step > 0x3F) {\n        time_step /= 32;\n        prescaller = 1;\n    } else {\n        prescaller = 0;\n    }\n    if(time_step == 0) {\n        time_step = 1;\n    } else if(time_step > 0x3F)\n        time_step = 0x3F;\n    program[1] = (prescaller << 14) | (time_step << 8); // Delay\n\n    program[2] = 0x4000 | 0; // Set PWM\n\n    time_step = (period - on_time) * 2;\n    if(time_step > 0x3F) {\n        time_step /= 32;\n        prescaller = 1;\n    } else {\n        prescaller = 0;\n    }\n    if(time_step == 0) {\n        time_step = 1;\n    } else if(time_step > 0x3F)\n        time_step = 0x3F;\n    program[3] = (prescaller << 14) | (time_step << 8); // Delay\n\n    program[4] = 0x0000; // Go to start\n\n    // Execute program\n    lp5562_execute_program(handle, eng, ch, program);\n}\n"
  },
  {
    "path": "lib/drivers/lp5562.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi_hal_i2c.h>\n\n/** Channel types */\ntypedef enum {\n    LP5562ChannelRed = (1 << 0),\n    LP5562ChannelGreen = (1 << 1),\n    LP5562ChannelBlue = (1 << 2),\n    LP5562ChannelWhite = (1 << 3),\n} LP5562Channel;\n\ntypedef enum {\n    LP5562Direct = 0,\n    LP5562Engine1 = 1,\n    LP5562Engine2 = 2,\n    LP5562Engine3 = 3,\n} LP5562Engine;\n\n/** Initialize Driver */\nvoid lp5562_reset(const FuriHalI2cBusHandle* handle);\n\n/** Configure Driver */\nvoid lp5562_configure(const FuriHalI2cBusHandle* handle);\n\n/** Enable Driver */\nvoid lp5562_enable(const FuriHalI2cBusHandle* handle);\n\n/** Set channel current */\nvoid lp5562_set_channel_current(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    uint8_t value);\n\n/** Set channel PWM value */\nvoid lp5562_set_channel_value(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    uint8_t value);\n\n/** Get channel PWM value */\nuint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel);\n\n/** Set channel source */\nvoid lp5562_set_channel_src(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Channel channel,\n    LP5562Engine src);\n\n/** Execute program sequence */\nvoid lp5562_execute_program(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint16_t* program);\n\n/** Stop program sequence */\nvoid lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng);\n\n/** Execute ramp program sequence */\nvoid lp5562_execute_ramp(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint8_t val_start,\n    uint8_t val_end,\n    uint16_t time);\n\n/** Start blink program sequence */\nvoid lp5562_execute_blink(\n    const FuriHalI2cBusHandle* handle,\n    LP5562Engine eng,\n    LP5562Channel ch,\n    uint16_t on_time,\n    uint16_t period,\n    uint8_t brightness);\n"
  },
  {
    "path": "lib/drivers/lp5562_reg.h",
    "content": "#pragma once\n\n#if defined(BITS_BIG_ENDIAN) && BITS_BIG_ENDIAN == 1\n#error Bit structures defined in this file are not portable to BE\n#endif\n\n#define LP5562_ADDRESS     0x60\n#define LP5562_I2C_TIMEOUT 50\n\n#define LP5562_CHANNEL_RED_CURRENT_REGISTER   0x07\n#define LP5562_CHANNEL_GREEN_CURRENT_REGISTER 0x06\n#define LP5562_CHANNEL_BLUE_CURRENT_REGISTER  0x05\n#define LP5562_CHANNEL_WHITE_CURRENT_REGISTER 0x0F\n\n#define LP5562_CHANNEL_RED_VALUE_REGISTER   0x04\n#define LP5562_CHANNEL_GREEN_VALUE_REGISTER 0x03\n#define LP5562_CHANNEL_BLUE_VALUE_REGISTER  0x02\n#define LP5562_CHANNEL_WHITE_VALUE_REGISTER 0x0E\n\ntypedef enum {\n    EngExecHold = 0b00,\n    EngExecStep = 0b01,\n    EngExecRun = 0b10,\n    EngExecPC = 0b11,\n} EngExec;\n\ntypedef struct {\n    EngExec ENG3_EXEC : 2;\n    EngExec ENG2_EXEC : 2;\n    EngExec ENG1_EXEC : 2;\n    bool CHIP_EN      : 1;\n    bool LOG_EN       : 1;\n} Reg00_Enable;\n\ntypedef enum {\n    EngModeDisable = 0b00,\n    EngModeLoad = 0b01,\n    EngModeRun = 0b10,\n    EngModeDirect = 0b11,\n} EngMode;\n\ntypedef struct {\n    EngMode ENG3_MODE : 2;\n    EngMode ENG2_MODE : 2;\n    EngMode ENG1_MODE : 2;\n    uint8_t reserved  : 2;\n} Reg01_OpMode;\n\ntypedef struct {\n    bool INT_CLK_EN   : 1;\n    bool CLK_DET_EN   : 1;\n    uint8_t reserved0 : 3;\n    bool PS_EN        : 1;\n    bool PWM_HF       : 1;\n    uint8_t reserved1 : 1;\n} Reg08_Config;\n\ntypedef struct {\n    uint8_t pc       : 3;\n    uint8_t reserved : 5;\n} Reg09_Engine1PC;\n\ntypedef struct {\n    uint8_t pc       : 3;\n    uint8_t reserved : 5;\n} Reg0A_Engine2PC;\n\ntypedef struct {\n    uint8_t pc       : 3;\n    uint8_t reserved : 5;\n} Reg0B_Engine3PC;\n\ntypedef struct {\n    bool ENG3_INT     : 1;\n    bool ENG2_INT     : 1;\n    bool ENG1_INT     : 1;\n    bool EXT_CLK_USED : 1;\n    uint8_t reserved  : 5;\n} Reg0C_Status;\n\ntypedef struct {\n    uint8_t value;\n} Reg0D_Reset;\n\ntypedef enum {\n    EngSelectI2C = 0b00,\n    EngSelectEngine1 = 0b01,\n    EngSelectEngine2 = 0b10,\n    EngSelectEngine3 = 0b11,\n} EngSelect;\n\ntypedef struct {\n    EngSelect blue  : 2;\n    EngSelect green : 2;\n    EngSelect red   : 2;\n    EngSelect white : 2;\n} Reg70_LedMap;\n"
  },
  {
    "path": "lib/drivers/st25r3916.c",
    "content": "#include \"st25r3916.h\"\n\n#include <furi.h>\n\nvoid st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask) {\n    furi_assert(handle);\n\n    uint8_t irq_mask_regs[4] = {\n        mask & 0xff,\n        (mask >> 8) & 0xff,\n        (mask >> 16) & 0xff,\n        (mask >> 24) & 0xff,\n    };\n    st25r3916_write_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_mask_regs, 4);\n}\n\nuint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle) {\n    furi_assert(handle);\n\n    uint8_t irq_regs[4] = {};\n    uint32_t irq = 0;\n    st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_regs, 4);\n    // FURI_LOG_I(\n    //     \"Mask Irq\", \"%02X %02X %02X %02X\", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]);\n    st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MAIN, irq_regs, 4);\n    irq = (uint32_t)irq_regs[0];\n    irq |= (uint32_t)irq_regs[1] << 8;\n    irq |= (uint32_t)irq_regs[2] << 16;\n    irq |= (uint32_t)irq_regs[3] << 24;\n    // FURI_LOG_I(\"iRQ\", \"%02X %02X %02X %02X\", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]);\n\n    return irq;\n}\n\nvoid st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) {\n    furi_assert(handle);\n    furi_assert(buff);\n\n    size_t bytes = (bits + 7) / 8;\n\n    st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, (uint8_t)(bits & 0xFFU));\n    st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES1, (uint8_t)((bits >> 8) & 0xFFU));\n\n    st25r3916_reg_write_fifo(handle, buff, bytes);\n}\n\nbool st25r3916_read_fifo(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t* buff,\n    size_t buff_size,\n    size_t* buff_bits) {\n    furi_assert(handle);\n    furi_assert(buff);\n\n    bool read_success = false;\n\n    do {\n        uint8_t fifo_status[2] = {};\n        st25r3916_read_burst_regs(handle, ST25R3916_REG_FIFO_STATUS1, fifo_status, 2);\n\n        uint16_t fifo_status_b9_b8 =\n            ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >>\n             ST25R3916_REG_FIFO_STATUS2_fifo_b_shift);\n        size_t bytes = (fifo_status_b9_b8 << 8) | fifo_status[0];\n\n        uint8_t bits =\n            ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >>\n             ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift);\n\n        if(bytes == 0) break;\n        if(bytes > buff_size) break;\n\n        st25r3916_reg_read_fifo(handle, buff, bytes);\n\n        if(bits) {\n            *buff_bits = (bytes - 1) * 8 + bits;\n        } else {\n            *buff_bits = bytes * 8;\n        }\n        read_success = true;\n    } while(false);\n\n    return read_success;\n}\n"
  },
  {
    "path": "lib/drivers/st25r3916.h",
    "content": "#pragma once\n\n#include \"st25r3916_reg.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ST25R3916_IRQ_MASK_ALL  (uint32_t)(0xFFFFFFFFUL) /** All ST25R3916 interrupt sources */\n#define ST25R3916_IRQ_MASK_NONE (uint32_t)(0x00000000UL) /**No ST25R3916 interrupt source */\n\n/** Main interrupt register */\n#define ST25R3916_IRQ_MASK_OSC (uint32_t)(0x00000080U) /** ST25R3916 oscillator stable interrupt */\n#define ST25R3916_IRQ_MASK_FWL (uint32_t)(0x00000040U) /** ST25R3916 FIFO water level interrupt */\n#define ST25R3916_IRQ_MASK_RXS (uint32_t)(0x00000020U) /** ST25R3916 start of receive interrupt */\n#define ST25R3916_IRQ_MASK_RXE (uint32_t)(0x00000010U) /** ST25R3916 end of receive interrupt */\n#define ST25R3916_IRQ_MASK_TXE \\\n    (uint32_t)(0x00000008U) /** ST25R3916 end of transmission interrupt */\n#define ST25R3916_IRQ_MASK_COL (uint32_t)(0x00000004U) /** ST25R3916 bit collision interrupt */\n#define ST25R3916_IRQ_MASK_RX_REST \\\n    (uint32_t)(0x00000002U) /** ST25R3916 automatic reception restart interrupt */\n#define ST25R3916_IRQ_MASK_RFU (uint32_t)(0x00000001U) /** ST25R3916 RFU interrupt */\n\n/** Timer and NFC interrupt register */\n#define ST25R3916_IRQ_MASK_DCT \\\n    (uint32_t)(0x00008000U) /** ST25R3916 termination of direct command interrupt. */\n#define ST25R3916_IRQ_MASK_NRE \\\n    (uint32_t)(0x00004000U) /** ST25R3916 no-response timer expired interrupt */\n#define ST25R3916_IRQ_MASK_GPE \\\n    (uint32_t)(0x00002000U) /** ST25R3916 general purpose timer expired interrupt */\n#define ST25R3916_IRQ_MASK_EON (uint32_t)(0x00001000U) /** ST25R3916 external field on interrupt */\n#define ST25R3916_IRQ_MASK_EOF \\\n    (uint32_t)(0x00000800U) /** ST25R3916 external field off interrupt */\n#define ST25R3916_IRQ_MASK_CAC \\\n    (uint32_t)(0x00000400U) /** ST25R3916 collision during RF collision avoidance  */\n#define ST25R3916_IRQ_MASK_CAT \\\n    (uint32_t)(0x00000200U) /** ST25R3916 minimum guard time expired interrupt */\n#define ST25R3916_IRQ_MASK_NFCT \\\n    (uint32_t)(0x00000100U) /** ST25R3916 initiator bit rate recognised interrupt */\n\n/** Error and wake-up interrupt register */\n#define ST25R3916_IRQ_MASK_CRC (uint32_t)(0x00800000U) /** ST25R3916 CRC error interrupt */\n#define ST25R3916_IRQ_MASK_PAR (uint32_t)(0x00400000U) /** ST25R3916 parity error interrupt */\n#define ST25R3916_IRQ_MASK_ERR2 \\\n    (uint32_t)(0x00200000U) /** ST25R3916 soft framing error interrupt */\n#define ST25R3916_IRQ_MASK_ERR1 \\\n    (uint32_t)(0x00100000U) /** ST25R3916 hard framing error interrupt */\n#define ST25R3916_IRQ_MASK_WT (uint32_t)(0x00080000U) /** ST25R3916 wake-up interrupt */\n#define ST25R3916_IRQ_MASK_WAM \\\n    (uint32_t)(0x00040000U) /** ST25R3916 wake-up due to amplitude interrupt */\n#define ST25R3916_IRQ_MASK_WPH \\\n    (uint32_t)(0x00020000U) /** ST25R3916 wake-up due to phase interrupt */\n#define ST25R3916_IRQ_MASK_WCAP \\\n    (uint32_t)(0x00010000U) /** ST25R3916 wake-up due to capacitance measurement */\n\n/** Passive Target Interrupt Register */\n#define ST25R3916_IRQ_MASK_PPON2 \\\n    (uint32_t)(0x80000000U) /** ST25R3916 PPON2 Field on waiting Timer interrupt */\n#define ST25R3916_IRQ_MASK_SL_WL \\\n    (uint32_t)(0x40000000U) /** ST25R3916 Passive target slot number water level interrupt */\n#define ST25R3916_IRQ_MASK_APON \\\n    (uint32_t)(0x20000000U) /** ST25R3916 Anticollision done and Field On interrupt */\n#define ST25R3916_IRQ_MASK_RXE_PTA \\\n    (uint32_t)(0x10000000U) /** ST25R3916 RXE with an automatic response interrupt */\n#define ST25R3916_IRQ_MASK_WU_F \\\n    (uint32_t)(0x08000000U) /** ST25R3916 212/424b/s Passive target interrupt: Active */\n#define ST25R3916_IRQ_MASK_RFU2 (uint32_t)(0x04000000U) /** ST25R3916 RFU2 interrupt */\n#define ST25R3916_IRQ_MASK_WU_A_X \\\n    (uint32_t)(0x02000000U) /** ST25R3916 106kb/s Passive target state interrupt: Active* */\n#define ST25R3916_IRQ_MASK_WU_A \\\n    (uint32_t)(0x01000000U) /** ST25R3916 106kb/s Passive target state interrupt: Active */\n\n/** Mask st25r3916 interrupts\n *\n * @param   handle  - pointer to FuriHalSpiBusHandle instance\n * @param   mask    - mask of interrupts to be disabled\n */\nvoid st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask);\n\n/** Get st25r3916 interrupts\n *\n * @param   handle  - pointer to FuriHalSpiBusHandle instance\n *\n * @return received interrupts\n */\nuint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle);\n\n/** Write FIFO\n *\n * @param   handle  - pointer to FuriHalSpiBusHandle instance\n * @param   buff    - buffer to write to FIFO\n * @param   bits    - number of bits to write\n */\nvoid st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits);\n\n/** Read FIFO\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   buff        - buffer to read from FIFO\n * @param   buff_size   - buffer size n bytes\n * @param   buff_bits   - pointer to number of bits read\n *\n * @return  true if read success, false otherwise\n*/\nbool st25r3916_read_fifo(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t* buff,\n    size_t buff_size,\n    size_t* buff_bits);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/drivers/st25r3916_reg.c",
    "content": "#include \"st25r3916_reg.h\"\n\n#include <furi.h>\n\n#define ST25R3916_WRITE_MODE \\\n    (0U << 6) /*!< ST25R3916 Operation Mode: Write                                */\n#define ST25R3916_READ_MODE \\\n    (1U << 6) /*!< ST25R3916 Operation Mode: Read                                 */\n#define ST25R3916_CMD_MODE \\\n    (3U << 6) /*!< ST25R3916 Operation Mode: Direct Command                       */\n#define ST25R3916_FIFO_LOAD \\\n    (0x80U) /*!< ST25R3916 Operation Mode: FIFO Load                            */\n#define ST25R3916_FIFO_READ \\\n    (0x9FU) /*!< ST25R3916 Operation Mode: FIFO Read                            */\n#define ST25R3916_PT_A_CONFIG_LOAD \\\n    (0xA0U) /*!< ST25R3916 Operation Mode: Passive Target Memory A-Config Load  */\n#define ST25R3916_PT_F_CONFIG_LOAD \\\n    (0xA8U) /*!< ST25R3916 Operation Mode: Passive Target Memory F-Config Load  */\n#define ST25R3916_PT_TSN_DATA_LOAD \\\n    (0xACU) /*!< ST25R3916 Operation Mode: Passive Target Memory TSN Load       */\n#define ST25R3916_PT_MEM_READ \\\n    (0xBFU) /*!< ST25R3916 Operation Mode: Passive Target Memory Read           */\n\n#define ST25R3916_CMD_LEN \\\n    (1U) /*!< ST25R3916 CMD length                                           */\n#define ST25R3916_FIFO_DEPTH (512U)\n#define ST25R3916_BUF_LEN \\\n    (ST25R3916_CMD_LEN +  \\\n     ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length    */\n\nstatic void st25r3916_reg_tx_byte(const FuriHalSpiBusHandle* handle, uint8_t byte) {\n    uint8_t val = byte;\n    furi_hal_spi_bus_tx(handle, &val, 1, 5);\n}\n\nvoid st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {\n    furi_check(handle);\n    st25r3916_read_burst_regs(handle, reg, val, 1);\n}\n\nvoid st25r3916_read_burst_regs(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg_start,\n    uint8_t* values,\n    uint8_t length) {\n    furi_check(handle);\n    furi_check(values);\n    furi_check(length);\n\n    furi_hal_gpio_write(handle->cs, false);\n\n    if(reg_start & ST25R3916_SPACE_B) {\n        // Send direct command first\n        st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS);\n    }\n    st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_READ_MODE);\n    furi_hal_spi_bus_rx(handle, values, length, 5);\n\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {\n    furi_check(handle);\n    uint8_t reg_val = val;\n    st25r3916_write_burst_regs(handle, reg, &reg_val, 1);\n}\n\nvoid st25r3916_write_burst_regs(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg_start,\n    const uint8_t* values,\n    uint8_t length) {\n    furi_check(handle);\n    furi_check(values);\n    furi_check(length);\n\n    furi_hal_gpio_write(handle->cs, false);\n\n    if(reg_start & ST25R3916_SPACE_B) {\n        // Send direct command first\n        st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS);\n    }\n    st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_WRITE_MODE);\n    furi_hal_spi_bus_tx(handle, values, length, 5);\n\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_reg_write_fifo(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* buff,\n    size_t length) {\n    furi_check(handle);\n    furi_check(buff);\n    furi_check(length);\n    furi_check(length <= ST25R3916_FIFO_DEPTH);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_LOAD);\n    furi_hal_spi_bus_tx(handle, buff, length, 200);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {\n    furi_check(handle);\n    furi_check(buff);\n    furi_check(length);\n    furi_check(length <= ST25R3916_FIFO_DEPTH);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_READ);\n    furi_hal_spi_bus_rx(handle, buff, length, 200);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_write_pta_mem(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* values,\n    size_t length) {\n    furi_check(handle);\n    furi_check(values);\n    furi_check(length);\n    furi_check(length <= ST25R3916_PTM_LEN);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_PT_A_CONFIG_LOAD);\n    furi_hal_spi_bus_tx(handle, values, length, 200);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {\n    furi_check(handle);\n    furi_check(buff);\n    furi_check(length);\n    furi_check(length <= ST25R3916_PTM_LEN);\n\n    uint8_t tmp_buff[ST25R3916_PTM_LEN + 1];\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_PT_MEM_READ);\n    furi_hal_spi_bus_rx(handle, tmp_buff, length + 1, 200);\n    furi_hal_gpio_write(handle->cs, true);\n    memcpy(buff, tmp_buff + 1, length);\n}\n\nvoid st25r3916_write_ptf_mem(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* values,\n    size_t length) {\n    furi_check(handle);\n    furi_check(values);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_PT_F_CONFIG_LOAD);\n    furi_hal_spi_bus_tx(handle, values, length, 200);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {\n    furi_check(handle);\n    furi_check(buff);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_PT_TSN_DATA_LOAD);\n    furi_hal_spi_bus_tx(handle, buff, length, 200);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd) {\n    furi_check(handle);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, cmd | ST25R3916_CMD_MODE);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {\n    furi_check(handle);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS);\n    st25r3916_reg_tx_byte(handle, reg | ST25R3916_READ_MODE);\n    furi_hal_spi_bus_rx(handle, val, 1, 5);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {\n    furi_check(handle);\n\n    furi_hal_gpio_write(handle->cs, false);\n    st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS);\n    st25r3916_reg_tx_byte(handle, reg | ST25R3916_WRITE_MODE);\n    furi_hal_spi_bus_tx(handle, &val, 1, 5);\n    furi_hal_gpio_write(handle->cs, true);\n}\n\nvoid st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) {\n    furi_check(handle);\n\n    uint8_t reg_val = 0;\n    st25r3916_read_reg(handle, reg, &reg_val);\n    if((reg_val & ~clr_mask) != reg_val) {\n        reg_val &= ~clr_mask;\n        st25r3916_write_reg(handle, reg, reg_val);\n    }\n}\n\nvoid st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) {\n    furi_check(handle);\n\n    uint8_t reg_val = 0;\n    st25r3916_read_reg(handle, reg, &reg_val);\n    if((reg_val | set_mask) != reg_val) {\n        reg_val |= set_mask;\n        st25r3916_write_reg(handle, reg, reg_val);\n    }\n}\n\nvoid st25r3916_change_reg_bits(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t mask,\n    uint8_t value) {\n    furi_check(handle);\n\n    st25r3916_modify_reg(handle, reg, mask, (mask & value));\n}\n\nvoid st25r3916_modify_reg(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t clr_mask,\n    uint8_t set_mask) {\n    furi_check(handle);\n\n    uint8_t reg_val = 0;\n    uint8_t new_val = 0;\n    st25r3916_read_reg(handle, reg, &reg_val);\n    new_val = (reg_val & ~clr_mask) | set_mask;\n    if(new_val != reg_val) {\n        st25r3916_write_reg(handle, reg, new_val);\n    }\n}\n\nvoid st25r3916_change_test_reg_bits(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t mask,\n    uint8_t value) {\n    furi_check(handle);\n\n    uint8_t reg_val = 0;\n    uint8_t new_val = 0;\n    st25r3916_read_test_reg(handle, reg, &reg_val);\n    new_val = (reg_val & ~mask) | (mask & value);\n    if(new_val != reg_val) {\n        st25r3916_write_test_reg(handle, reg, new_val);\n    }\n}\n\nbool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) {\n    furi_check(handle);\n\n    uint8_t reg_val = 0;\n    st25r3916_read_reg(handle, reg, &reg_val);\n    return (reg_val & mask) == val;\n}\n"
  },
  {
    "path": "lib/drivers/st25r3916_reg.h",
    "content": "#pragma once\n\n#include <furi_hal_spi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** ST25R3916 direct commands */\n#define ST25R3916_CMD_SET_DEFAULT \\\n    0xC1U /** Puts the chip in default state (same as after power-up */\n#define ST25R3916_CMD_STOP                 0xC2U /*!< Stops all activities and clears FIFO */\n#define ST25R3916_CMD_TRANSMIT_WITH_CRC    0xC4U /** Transmit with CRC */\n#define ST25R3916_CMD_TRANSMIT_WITHOUT_CRC 0xC5U /** Transmit without CRC */\n#define ST25R3916_CMD_TRANSMIT_REQA        0xC6U /** Transmit REQA */\n#define ST25R3916_CMD_TRANSMIT_WUPA        0xC7U /** Transmit WUPA */\n#define ST25R3916_CMD_INITIAL_RF_COLLISION \\\n    0xC8U /** NFC transmit with Initial RF Collision Avoidance */\n#define ST25R3916_CMD_RESPONSE_RF_COLLISION_N \\\n    0xC9U /** NFC transmit with Response RF Collision Avoidance */\n#define ST25R3916_CMD_GOTO_SENSE          0xCDU /** Passive target logic to Sense/Idle state */\n#define ST25R3916_CMD_GOTO_SLEEP          0xCEU /** Passive target logic to Sleep/Halt state */\n#define ST25R3916_CMD_MASK_RECEIVE_DATA   0xD0U /** Mask receive data */\n#define ST25R3916_CMD_UNMASK_RECEIVE_DATA 0xD1U /** Unmask receive data */\n#define ST25R3916_CMD_AM_MOD_STATE_CHANGE 0xD2U /** AM Modulation state change */\n#define ST25R3916_CMD_MEASURE_AMPLITUDE   0xD3U /** Measure singal amplitude on RFI inputs */\n#define ST25R3916_CMD_RESET_RXGAIN        0xD5U /** Reset RX Gain */\n#define ST25R3916_CMD_ADJUST_REGULATORS   0xD6U /** Adjust regulators */\n#define ST25R3916_CMD_CALIBRATE_DRIVER_TIMING \\\n    0xD8U /** Starts the sequence to adjust the driver timing */\n#define ST25R3916_CMD_MEASURE_PHASE            0xD9U /** Measure phase between RFO and RFI signal */\n#define ST25R3916_CMD_CLEAR_RSSI               0xDAU /** Clear RSSI bits and restart the measurement */\n#define ST25R3916_CMD_CLEAR_FIFO               0xDBU /** Clears FIFO, Collision and IRQ status */\n#define ST25R3916_CMD_TRANSPARENT_MODE         0xDCU /** Transparent mode */\n#define ST25R3916_CMD_CALIBRATE_C_SENSOR       0xDDU /** Calibrate the capacitive sensor */\n#define ST25R3916_CMD_MEASURE_CAPACITANCE      0xDEU /** Measure capacitance */\n#define ST25R3916_CMD_MEASURE_VDD              0xDFU /** Measure power supply voltage */\n#define ST25R3916_CMD_START_GP_TIMER           0xE0U /** Start the general purpose timer */\n#define ST25R3916_CMD_START_WUP_TIMER          0xE1U /** Start the wake-up timer */\n#define ST25R3916_CMD_START_MASK_RECEIVE_TIMER 0xE2U /** Start the mask-receive timer */\n#define ST25R3916_CMD_START_NO_RESPONSE_TIMER  0xE3U /** Start the no-response timer */\n#define ST25R3916_CMD_START_PPON2_TIMER        0xE4U /** Start PPon2 timer */\n#define ST25R3916_CMD_STOP_NRT                 0xE8U /** Stop No Response Timer */\n#define ST25R3916_CMD_SPACE_B_ACCESS           0xFBU /** Enable R/W access to the test registers */\n#define ST25R3916_CMD_TEST_ACCESS              0xFCU /** Enable R/W access to the test registers */\n\n#define ST25R3916_SPACE_B         0x40U /** ST25R3916 Space-B indicator */\n#define ST25R3916_SPACE_B_REG_LEN 16U /** Number of register in the space B */\n\n#define ST25R3916_FIFO_STATUS_LEN 2 /** Number of FIFO Status Register */\n\n#define ST25R3916_PTM_A_LEN   15U /** Passive target memory A config length */\n#define ST25R3916_PTM_B_LEN   0U /** Passive target memory B config length */\n#define ST25R3916_PTM_F_LEN   21U /** Passive target memory F config length */\n#define ST25R3916_PTM_TSN_LEN 12U /** Passive target memory TSN data length */\n\n/** Full Passive target memory length */\n#define ST25R3916_PTM_LEN \\\n    (ST25R3916_PTM_A_LEN + ST25R3916_PTM_B_LEN + ST25R3916_PTM_F_LEN + ST25R3916_PTM_TSN_LEN)\n\n/** IO configuration registers */\n#define ST25R3916_REG_IO_CONF1 0x00U /** RW IO Configuration Register 1 */\n#define ST25R3916_REG_IO_CONF2 0x01U /** RW IO Configuration Register 2 */\n\n/** Operation control and mode definition  */\n#define ST25R3916_REG_OP_CONTROL 0x02U /** RW Operation Control Register */\n#define ST25R3916_REG_MODE       0x03U /** RW Mode Definition Register */\n#define ST25R3916_REG_BIT_RATE   0x04U /** RW Bit Rate Definition Register */\n\n/** Protocol Configuration registers */\n#define ST25R3916_REG_ISO14443A_NFC 0x05U /** RW ISO14443A and NFC 106 kBit/s Settings Register */\n#define ST25R3916_REG_EMD_SUP_CONF \\\n    (ST25R3916_SPACE_B | 0x05U) /*!< RW EMD Suppression Configuration Register */\n#define ST25R3916_REG_ISO14443B_1 0x06U /** RW ISO14443B Settings Register 1 */\n#define ST25R3916_REG_SUBC_START_TIME \\\n    (ST25R3916_SPACE_B | 0x06U) /*!< RW Subcarrier Start Time Register */\n#define ST25R3916_REG_ISO14443B_2    0x07U /** RW ISO14443B Settings Register 2 */\n#define ST25R3916_REG_PASSIVE_TARGET 0x08U /** RW Passive Target Definition Register */\n#define ST25R3916_REG_STREAM_MODE    0x09U /** RW Stream Mode Definition Register */\n#define ST25R3916_REG_AUX            0x0AU /** RW Auxiliary Definition Register */\n\n/** Receiver Configuration registers */\n#define ST25R3916_REG_RX_CONF1 0x0BU /** RW Receiver Configuration Register 1 */\n#define ST25R3916_REG_RX_CONF2 0x0CU /** RW Receiver Configuration Register 2 */\n#define ST25R3916_REG_RX_CONF3 0x0DU /** RW Receiver Configuration Register 3 */\n#define ST25R3916_REG_RX_CONF4 0x0EU /** RW Receiver Configuration Register 4 */\n#define ST25R3916_REG_P2P_RX_CONF \\\n    (ST25R3916_SPACE_B | 0x0BU) /** RW P2P Receiver Configuration Register 1 */\n#define ST25R3916_REG_CORR_CONF1 \\\n    (ST25R3916_SPACE_B | 0x0CU) /** RW Correlator configuration register 1 */\n#define ST25R3916_REG_CORR_CONF2 \\\n    (ST25R3916_SPACE_B | 0x0DU) /** RW Correlator configuration register 2 */\n\n/** Timer definition registers */\n#define ST25R3916_REG_MASK_RX_TIMER      0x0FU /** RW Mask Receive Timer Register */\n#define ST25R3916_REG_NO_RESPONSE_TIMER1 0x10U /** RW No-response Timer Register 1 */\n#define ST25R3916_REG_NO_RESPONSE_TIMER2 0x11U /** RW No-response Timer Register 2 */\n#define ST25R3916_REG_TIMER_EMV_CONTROL  0x12U /** RW Timer and EMV Control */\n#define ST25R3916_REG_GPT1               0x13U /** RW General Purpose Timer Register 1 */\n#define ST25R3916_REG_GPT2               0x14U /** RW General Purpose Timer Register 2 */\n#define ST25R3916_REG_PPON2              0x15U /** RW PPON2 Field waiting Timer Register */\n#define ST25R3916_REG_SQUELCH_TIMER      (ST25R3916_SPACE_B | 0x0FU) /** RW Squelch timeout Register */\n#define ST25R3916_REG_FIELD_ON_GT        (ST25R3916_SPACE_B | 0x15U) /** RW NFC Field on guard time */\n\n/** Interrupt and associated reporting registers */\n#define ST25R3916_REG_IRQ_MASK_MAIN         0x16U /** RW Mask Main Interrupt Register */\n#define ST25R3916_REG_IRQ_MASK_TIMER_NFC    0x17U /** RW Mask Timer and NFC Interrupt Register */\n#define ST25R3916_REG_IRQ_MASK_ERROR_WUP    0x18U /** RW Mask Error and Wake-up Interrupt Register */\n#define ST25R3916_REG_IRQ_MASK_TARGET       0x19U /** RW Mask 3916 Target Interrupt Register */\n#define ST25R3916_REG_IRQ_MAIN              0x1AU /** R  Main Interrupt Register */\n#define ST25R3916_REG_IRQ_TIMER_NFC         0x1BU /** R  Timer and NFC Interrupt Register */\n#define ST25R3916_REG_IRQ_ERROR_WUP         0x1CU /** R  Error and Wake-up Interrupt Register */\n#define ST25R3916_REG_IRQ_TARGET            0x1DU /*!< R  ST25R3916 Target Interrupt Register */\n#define ST25R3916_REG_FIFO_STATUS1          0x1EU /** R  FIFO Status Register 1 */\n#define ST25R3916_REG_FIFO_STATUS2          0x1FU /** R  FIFO Status Register 2 */\n#define ST25R3916_REG_COLLISION_STATUS      0x20U /** R  Collision Display Register */\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS 0x21U /** R  Passive target state status */\n\n/** Definition of number of transmitted bytes */\n#define ST25R3916_REG_NUM_TX_BYTES1 0x22U /** RW Number of Transmitted Bytes Register 1 */\n#define ST25R3916_REG_NUM_TX_BYTES2 0x23U /** RW Number of Transmitted Bytes Register 2 */\n\n/** NFCIP Bit Rate Display Register */\n#define ST25R3916_REG_NFCIP1_BIT_RATE 0x24U /** R  NFCIP Bit Rate Detection Display Register */\n\n/** A/D Converter Output Register */\n#define ST25R3916_REG_AD_RESULT 0x25U /** R  A/D Converter Output Register */\n\n/** Antenna tuning registers */\n#define ST25R3916_REG_ANT_TUNE_A 0x26U /** RW Antenna Tuning Control (AAT-A) Register 1 */\n#define ST25R3916_REG_ANT_TUNE_B 0x27U /** RW Antenna Tuning Control (AAT-B) Register 2 */\n\n/** Antenna Driver and Modulation registers */\n#define ST25R3916_REG_TX_DRIVER 0x28U /** RW TX driver register */\n#define ST25R3916_REG_PT_MOD    0x29U /** RW PT modulation Register */\n#define ST25R3916_REG_AUX_MOD   (ST25R3916_SPACE_B | 0x28U) /** RW Aux Modulation setting Register */\n#define ST25R3916_REG_TX_DRIVER_TIMING \\\n    (ST25R3916_SPACE_B | 0x29U) /** RW TX driver timing Register */\n#define ST25R3916_REG_RES_AM_MOD \\\n    (ST25R3916_SPACE_B | 0x2AU) /** RW Resistive AM modulation register */\n#define ST25R3916_REG_TX_DRIVER_STATUS \\\n    (ST25R3916_SPACE_B | 0x2BU) /** R  TX driver timing readout Register */\n\n/** External Field Detector Threshold Registers */\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV \\\n    0x2AU /** RW External Field Detector Activation Threshold Reg */\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV \\\n    0x2BU /** RW External Field Detector Deactivation Threshold Reg */\n\n/** Regulator registers */\n#define ST25R3916_REG_REGULATOR_CONTROL 0x2CU /** RW Regulated Voltage Control Register */\n#define ST25R3916_REG_REGULATOR_RESULT \\\n    (ST25R3916_SPACE_B | 0x2CU) /** R Regulator Display Register */\n\n/** Receiver State Display Register */\n#define ST25R3916_REG_RSSI_RESULT        0x2DU /** R RSSI Display Register */\n#define ST25R3916_REG_GAIN_RED_STATE     0x2EU /** R Gain Reduction State Register */\n#define ST25R3916_REG_CAP_SENSOR_CONTROL 0x2FU /** RW Capacitive Sensor Control Register */\n#define ST25R3916_REG_CAP_SENSOR_RESULT  0x30U /** R  Capacitive Sensor Display Register */\n#define ST25R3916_REG_AUX_DISPLAY        0x31U /** R Auxiliary Display Register */\n\n/** Over/Undershoot Protection Configuration Registers */\n#define ST25R3916_REG_OVERSHOOT_CONF1 \\\n    (ST25R3916_SPACE_B | 0x30U) /** RW  Overshoot Protection Configuration Register 1 */\n#define ST25R3916_REG_OVERSHOOT_CONF2 \\\n    (ST25R3916_SPACE_B | 0x31U) /** RW  Overshoot Protection Configuration Register 2 */\n#define ST25R3916_REG_UNDERSHOOT_CONF1 \\\n    (ST25R3916_SPACE_B | 0x32U) /** RW  Undershoot Protection Configuration Register 1 */\n#define ST25R3916_REG_UNDERSHOOT_CONF2 \\\n    (ST25R3916_SPACE_B | 0x33U) /** RW  Undershoot Protection Configuration Register 2 */\n\n/** Detection of card presence */\n#define ST25R3916_REG_WUP_TIMER_CONTROL 0x32U /** RW Wake-up Timer Control Register */\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF \\\n    0x33U /** RW Amplitude Measurement Configuration Register */\n#define ST25R3916_REG_AMPLITUDE_MEASURE_REF \\\n    0x34U /** RW Amplitude Measurement Reference Register */\n#define ST25R3916_REG_AMPLITUDE_MEASURE_AA_RESULT \\\n    0x35U /** R  Amplitude Measurement Auto Averaging Display Reg */\n#define ST25R3916_REG_AMPLITUDE_MEASURE_RESULT \\\n    0x36U /** R  Amplitude Measurement Display Register */\n#define ST25R3916_REG_PHASE_MEASURE_CONF 0x37U /** RW Phase Measurement Configuration Register */\n#define ST25R3916_REG_PHASE_MEASURE_REF  0x38U /** RW Phase Measurement Reference Register */\n#define ST25R3916_REG_PHASE_MEASURE_AA_RESULT \\\n    0x39U /** R  Phase Measurement Auto Averaging Display */\n#define ST25R3916_REG_PHASE_MEASURE_RESULT 0x3AU /** R  Phase Measurement Display Register */\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF \\\n    0x3BU /** RW Capacitance Measurement Configuration Register */\n#define ST25R3916_REG_CAPACITANCE_MEASURE_REF \\\n    0x3CU /** RW Capacitance Measurement Reference Register */\n#define ST25R3916_REG_CAPACITANCE_MEASURE_AA_RESULT \\\n    0x3DU /** R  Capacitance Measurement Auto Averaging Display Reg */\n#define ST25R3916_REG_CAPACITANCE_MEASURE_RESULT \\\n    0x3EU /** R  Capacitance Measurement Display Register */\n\n/** IC identity  */\n#define ST25R3916_REG_IC_IDENTITY 0x3FU /** R  Chip Id: 0 for old silicon, v2 silicon: 0x09 */\n\n/** Register bit definitions */\n\n#define ST25R3916_REG_IO_CONF1_single          (1U << 7)\n#define ST25R3916_REG_IO_CONF1_rfo2            (1U << 6)\n#define ST25R3916_REG_IO_CONF1_i2c_thd1        (1U << 5)\n#define ST25R3916_REG_IO_CONF1_i2c_thd0        (1U << 4)\n#define ST25R3916_REG_IO_CONF1_i2c_thd_mask    (3U << 4)\n#define ST25R3916_REG_IO_CONF1_i2c_thd_shift   (4U)\n#define ST25R3916_REG_IO_CONF1_rfu             (1U << 3)\n#define ST25R3916_REG_IO_CONF1_out_cl1         (1U << 2)\n#define ST25R3916_REG_IO_CONF1_out_cl0         (1U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_disabled (3U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_13_56MHZ (2U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_4_78MHZ  (1U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_3_39MHZ  (0U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_mask     (3U << 1)\n#define ST25R3916_REG_IO_CONF1_out_cl_shift    (1U)\n#define ST25R3916_REG_IO_CONF1_lf_clk_off      (1U << 0)\n#define ST25R3916_REG_IO_CONF1_lf_clk_off_on   (1U << 0)\n#define ST25R3916_REG_IO_CONF1_lf_clk_off_off  (0U << 0)\n\n#define ST25R3916_REG_IO_CONF2_sup3V      (1U << 7)\n#define ST25R3916_REG_IO_CONF2_sup3V_3V   (1U << 7)\n#define ST25R3916_REG_IO_CONF2_sup3V_5V   (0U << 7)\n#define ST25R3916_REG_IO_CONF2_vspd_off   (1U << 6)\n#define ST25R3916_REG_IO_CONF2_aat_en     (1U << 5)\n#define ST25R3916_REG_IO_CONF2_miso_pd2   (1U << 4)\n#define ST25R3916_REG_IO_CONF2_miso_pd1   (1U << 3)\n#define ST25R3916_REG_IO_CONF2_io_drv_lvl (1U << 2)\n#define ST25R3916_REG_IO_CONF2_slow_up    (1U << 0)\n\n#define ST25R3916_REG_OP_CONTROL_en                   (1U << 7)\n#define ST25R3916_REG_OP_CONTROL_rx_en                (1U << 6)\n#define ST25R3916_REG_OP_CONTROL_rx_chn               (1U << 5)\n#define ST25R3916_REG_OP_CONTROL_rx_man               (1U << 4)\n#define ST25R3916_REG_OP_CONTROL_tx_en                (1U << 3)\n#define ST25R3916_REG_OP_CONTROL_wu                   (1U << 2)\n#define ST25R3916_REG_OP_CONTROL_en_fd_c1             (1U << 1)\n#define ST25R3916_REG_OP_CONTROL_en_fd_c0             (1U << 0)\n#define ST25R3916_REG_OP_CONTROL_en_fd_efd_off        (0U << 0)\n#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_ca  (1U << 0)\n#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_pdt (2U << 0)\n#define ST25R3916_REG_OP_CONTROL_en_fd_auto_efd       (3U << 0)\n#define ST25R3916_REG_OP_CONTROL_en_fd_shift          (0U)\n#define ST25R3916_REG_OP_CONTROL_en_fd_mask           (3U << 0)\n\n#define ST25R3916_REG_MODE_targ                 (1U << 7)\n#define ST25R3916_REG_MODE_targ_targ            (1U << 7)\n#define ST25R3916_REG_MODE_targ_init            (0U << 7)\n#define ST25R3916_REG_MODE_om3                  (1U << 6)\n#define ST25R3916_REG_MODE_om2                  (1U << 5)\n#define ST25R3916_REG_MODE_om1                  (1U << 4)\n#define ST25R3916_REG_MODE_om0                  (1U << 3)\n#define ST25R3916_REG_MODE_om_bpsk_stream       (0xfU << 3)\n#define ST25R3916_REG_MODE_om_subcarrier_stream (0xeU << 3)\n#define ST25R3916_REG_MODE_om_topaz             (0x4U << 3)\n#define ST25R3916_REG_MODE_om_felica            (0x3U << 3)\n#define ST25R3916_REG_MODE_om_iso14443b         (0x2U << 3)\n#define ST25R3916_REG_MODE_om_iso14443a         (0x1U << 3)\n#define ST25R3916_REG_MODE_om_targ_nfca         (0x1U << 3)\n#define ST25R3916_REG_MODE_om_targ_nfcb         (0x2U << 3)\n#define ST25R3916_REG_MODE_om_targ_nfcf         (0x4U << 3)\n#define ST25R3916_REG_MODE_om_targ_nfcip        (0x7U << 3)\n#define ST25R3916_REG_MODE_om_nfc               (0x0U << 3)\n#define ST25R3916_REG_MODE_om_mask              (0xfU << 3)\n#define ST25R3916_REG_MODE_om_shift             (3U)\n#define ST25R3916_REG_MODE_tr_am                (1U << 2)\n#define ST25R3916_REG_MODE_tr_am_ook            (0U << 2)\n#define ST25R3916_REG_MODE_tr_am_am             (1U << 2)\n#define ST25R3916_REG_MODE_nfc_ar1              (1U << 1)\n#define ST25R3916_REG_MODE_nfc_ar0              (1U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_off           (0U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_auto_rx       (1U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_eof           (2U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_rfu           (3U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_mask          (3U << 0)\n#define ST25R3916_REG_MODE_nfc_ar_shift         (0U)\n\n#define ST25R3916_REG_BIT_RATE_txrate_106   (0x0U << 4)\n#define ST25R3916_REG_BIT_RATE_txrate_212   (0x1U << 4)\n#define ST25R3916_REG_BIT_RATE_txrate_424   (0x2U << 4)\n#define ST25R3916_REG_BIT_RATE_txrate_848   (0x3U << 4)\n#define ST25R3916_REG_BIT_RATE_txrate_mask  (0x3U << 4)\n#define ST25R3916_REG_BIT_RATE_txrate_shift (4U)\n#define ST25R3916_REG_BIT_RATE_rxrate_106   (0x0U << 0)\n#define ST25R3916_REG_BIT_RATE_rxrate_212   (0x1U << 0)\n#define ST25R3916_REG_BIT_RATE_rxrate_424   (0x2U << 0)\n#define ST25R3916_REG_BIT_RATE_rxrate_848   (0x3U << 0)\n#define ST25R3916_REG_BIT_RATE_rxrate_mask  (0x3U << 0)\n#define ST25R3916_REG_BIT_RATE_rxrate_shift (0U)\n\n#define ST25R3916_REG_ISO14443A_NFC_no_tx_par     (1U << 7)\n#define ST25R3916_REG_ISO14443A_NFC_no_tx_par_off (0U << 7)\n#define ST25R3916_REG_ISO14443A_NFC_no_rx_par     (1U << 6)\n#define ST25R3916_REG_ISO14443A_NFC_no_rx_par_off (0U << 6)\n#define ST25R3916_REG_ISO14443A_NFC_nfc_f0        (1U << 5)\n#define ST25R3916_REG_ISO14443A_NFC_nfc_f0_off    (0U << 5)\n#define ST25R3916_REG_ISO14443A_NFC_p_len3        (1U << 4)\n#define ST25R3916_REG_ISO14443A_NFC_p_len2        (1U << 3)\n#define ST25R3916_REG_ISO14443A_NFC_p_len1        (1U << 2)\n#define ST25R3916_REG_ISO14443A_NFC_p_len0        (1U << 1)\n#define ST25R3916_REG_ISO14443A_NFC_p_len_mask    (0xfU << 1)\n#define ST25R3916_REG_ISO14443A_NFC_p_len_shift   (1U)\n#define ST25R3916_REG_ISO14443A_NFC_antcl         (1U << 0)\n\n#define ST25R3916_REG_EMD_SUP_CONF_emd_emv          (1U << 7)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_on       (1U << 7)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_off      (0U << 7)\n#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv     (1U << 6)\n#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on  (1U << 6)\n#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_off (0U << 6)\n#define ST25R3916_REG_EMD_SUP_CONF_rfu1             (1U << 5)\n#define ST25R3916_REG_EMD_SUP_CONF_rfu0             (1U << 4)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld3        (1U << 3)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld2        (1U << 2)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld1        (1U << 1)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld0        (1U << 0)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_mask    (0xfU << 0)\n#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_shift   (0U)\n\n#define ST25R3916_REG_SUBC_START_TIME_rfu2      (1U << 7)\n#define ST25R3916_REG_SUBC_START_TIME_rfu1      (1U << 6)\n#define ST25R3916_REG_SUBC_START_TIME_rfu0      (1U << 5)\n#define ST25R3916_REG_SUBC_START_TIME_sst4      (1U << 4)\n#define ST25R3916_REG_SUBC_START_TIME_sst3      (1U << 3)\n#define ST25R3916_REG_SUBC_START_TIME_sst2      (1U << 2)\n#define ST25R3916_REG_SUBC_START_TIME_sst1      (1U << 1)\n#define ST25R3916_REG_SUBC_START_TIME_sst0      (1U << 0)\n#define ST25R3916_REG_SUBC_START_TIME_sst_mask  (0x1fU << 0)\n#define ST25R3916_REG_SUBC_START_TIME_sst_shift (0U)\n\n#define ST25R3916_REG_ISO14443B_1_egt2        (1U << 7)\n#define ST25R3916_REG_ISO14443B_1_egt1        (1U << 6)\n#define ST25R3916_REG_ISO14443B_1_egt0        (1U << 5)\n#define ST25R3916_REG_ISO14443B_1_egt_shift   (5U)\n#define ST25R3916_REG_ISO14443B_1_egt_mask    (7U << 5)\n#define ST25R3916_REG_ISO14443B_1_sof_1       (1U << 3)\n#define ST25R3916_REG_ISO14443B_1_sof_1_3etu  (1U << 3)\n#define ST25R3916_REG_ISO14443B_1_sof_1_2etu  (0U << 3)\n#define ST25R3916_REG_ISO14443B_1_sof_0       (1U << 4)\n#define ST25R3916_REG_ISO14443B_1_sof_0_11etu (1U << 4)\n#define ST25R3916_REG_ISO14443B_1_sof_0_10etu (0U << 4)\n#define ST25R3916_REG_ISO14443B_1_sof_mask    (3U << 3)\n#define ST25R3916_REG_ISO14443B_1_eof         (1U << 2)\n#define ST25R3916_REG_ISO14443B_1_eof_11etu   (1U << 2)\n#define ST25R3916_REG_ISO14443B_1_eof_10etu   (0U << 2)\n#define ST25R3916_REG_ISO14443B_1_half        (1U << 1)\n#define ST25R3916_REG_ISO14443B_1_rx_st_om    (1U << 0)\n\n#define ST25R3916_REG_ISO14443B_2_tr1_1        (1U << 7)\n#define ST25R3916_REG_ISO14443B_2_tr1_0        (1U << 6)\n#define ST25R3916_REG_ISO14443B_2_tr1_64fs32fs (1U << 6)\n#define ST25R3916_REG_ISO14443B_2_tr1_80fs80fs (0U << 6)\n#define ST25R3916_REG_ISO14443B_2_tr1_mask     (3U << 6)\n#define ST25R3916_REG_ISO14443B_2_tr1_shift    (6U)\n#define ST25R3916_REG_ISO14443B_2_no_sof       (1U << 5)\n#define ST25R3916_REG_ISO14443B_2_no_eof       (1U << 4)\n#define ST25R3916_REG_ISO14443B_rfu1           (1U << 3)\n#define ST25R3916_REG_ISO14443B_rfu0           (1U << 2)\n#define ST25R3916_REG_ISO14443B_2_f_p1         (1U << 1)\n#define ST25R3916_REG_ISO14443B_2_f_p0         (1U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_96       (3U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_80       (2U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_64       (1U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_48       (0U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_mask     (3U << 0)\n#define ST25R3916_REG_ISO14443B_2_f_p_shift    (0U)\n\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_3       (1U << 7)\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_2       (1U << 6)\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_1       (1U << 5)\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_0       (1U << 4)\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_mask    (0xfU << 4)\n#define ST25R3916_REG_PASSIVE_TARGET_fdel_shift   (4U)\n#define ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p    (1U << 3)\n#define ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r (1U << 2)\n#define ST25R3916_REG_PASSIVE_TARGET_rfu          (1U << 1)\n#define ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a   (1U << 0)\n\n#define ST25R3916_REG_STREAM_MODE_rfu          (1U << 7)\n#define ST25R3916_REG_STREAM_MODE_scf1         (1U << 6)\n#define ST25R3916_REG_STREAM_MODE_scf0         (1U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_sc212    (0U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_sc424    (1U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_sc848    (2U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_sc1695   (3U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_bpsk848  (0U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_bpsk1695 (1U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_bpsk3390 (2U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_bpsk106  (3U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_mask     (3U << 5)\n#define ST25R3916_REG_STREAM_MODE_scf_shift    (5U)\n#define ST25R3916_REG_STREAM_MODE_scp1         (1U << 4)\n#define ST25R3916_REG_STREAM_MODE_scp0         (1U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_1pulse   (0U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_2pulses  (1U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_4pulses  (2U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_8pulses  (3U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_mask     (3U << 3)\n#define ST25R3916_REG_STREAM_MODE_scp_shift    (3U)\n#define ST25R3916_REG_STREAM_MODE_stx2         (1U << 2)\n#define ST25R3916_REG_STREAM_MODE_stx1         (1U << 1)\n#define ST25R3916_REG_STREAM_MODE_stx0         (1U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_106      (0U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_212      (1U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_424      (2U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_848      (3U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_mask     (7U << 0)\n#define ST25R3916_REG_STREAM_MODE_stx_shift    (0U)\n\n#define ST25R3916_REG_AUX_no_crc_rx           (1U << 7)\n#define ST25R3916_REG_AUX_rfu                 (1U << 6)\n#define ST25R3916_REG_AUX_nfc_id1             (1U << 5)\n#define ST25R3916_REG_AUX_nfc_id0             (1U << 4)\n#define ST25R3916_REG_AUX_nfc_id_7bytes       (1U << 4)\n#define ST25R3916_REG_AUX_nfc_id_4bytes       (0U << 4)\n#define ST25R3916_REG_AUX_nfc_id_mask         (3U << 4)\n#define ST25R3916_REG_AUX_nfc_id_shift        (4U)\n#define ST25R3916_REG_AUX_mfaz_cl90           (1U << 3)\n#define ST25R3916_REG_AUX_dis_corr            (1U << 2)\n#define ST25R3916_REG_AUX_dis_corr_coherent   (1U << 2)\n#define ST25R3916_REG_AUX_dis_corr_correlator (0U << 2)\n#define ST25R3916_REG_AUX_nfc_n1              (1U << 1)\n#define ST25R3916_REG_AUX_nfc_n0              (1U << 0)\n#define ST25R3916_REG_AUX_nfc_n_mask          (3U << 0)\n#define ST25R3916_REG_AUX_nfc_n_shift         (0U)\n\n#define ST25R3916_REG_RX_CONF1_ch_sel           (1U << 7)\n#define ST25R3916_REG_RX_CONF1_ch_sel_PM        (1U << 7)\n#define ST25R3916_REG_RX_CONF1_ch_sel_AM        (0U << 7)\n#define ST25R3916_REG_RX_CONF1_lp2              (1U << 6)\n#define ST25R3916_REG_RX_CONF1_lp1              (1U << 5)\n#define ST25R3916_REG_RX_CONF1_lp0              (1U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_1200khz       (0U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_600khz        (1U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_300khz        (2U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_2000khz       (4U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_7000khz       (5U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_mask          (7U << 4)\n#define ST25R3916_REG_RX_CONF1_lp_shift         (4U)\n#define ST25R3916_REG_RX_CONF1_z600k            (1U << 3)\n#define ST25R3916_REG_RX_CONF1_h200             (1U << 2)\n#define ST25R3916_REG_RX_CONF1_h80              (1U << 1)\n#define ST25R3916_REG_RX_CONF1_z12k             (1U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_60_400khz     (0U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_60_200khz     (4U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_40_80khz      (2U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_12_200khz     (1U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_12_80khz      (3U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_12_200khz_alt (5U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_600_400khz    (8U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_600_200khz    (12U << 0)\n#define ST25R3916_REG_RX_CONF1_hz_mask          (0xfU << 0)\n#define ST25R3916_REG_RX_CONF1_hz_shift         (0U)\n\n#define ST25R3916_REG_RX_CONF2_demod_mode    (1U << 7)\n#define ST25R3916_REG_RX_CONF2_amd_sel       (1U << 6)\n#define ST25R3916_REG_RX_CONF2_amd_sel_mixer (1U << 6)\n#define ST25R3916_REG_RX_CONF2_amd_sel_peak  (0U << 6)\n#define ST25R3916_REG_RX_CONF2_sqm_dyn       (1U << 5)\n#define ST25R3916_REG_RX_CONF2_pulz_61       (1U << 4)\n#define ST25R3916_REG_RX_CONF2_agc_en        (1U << 3)\n#define ST25R3916_REG_RX_CONF2_agc_m         (1U << 2)\n#define ST25R3916_REG_RX_CONF2_agc_alg       (1U << 1)\n#define ST25R3916_REG_RX_CONF2_agc6_3        (1U << 0)\n\n#define ST25R3916_REG_RX_CONF3_rg1_am2      (1U << 7)\n#define ST25R3916_REG_RX_CONF3_rg1_am1      (1U << 6)\n#define ST25R3916_REG_RX_CONF3_rg1_am0      (1U << 5)\n#define ST25R3916_REG_RX_CONF3_rg1_am_mask  (0x7U << 5)\n#define ST25R3916_REG_RX_CONF3_rg1_am_shift (5U)\n#define ST25R3916_REG_RX_CONF3_rg1_pm2      (1U << 4)\n#define ST25R3916_REG_RX_CONF3_rg1_pm1      (1U << 3)\n#define ST25R3916_REG_RX_CONF3_rg1_pm0      (1U << 2)\n#define ST25R3916_REG_RX_CONF3_rg1_pm_mask  (0x7U << 2)\n#define ST25R3916_REG_RX_CONF3_rg1_pm_shift (2U)\n#define ST25R3916_REG_RX_CONF3_lf_en        (1U << 1)\n#define ST25R3916_REG_RX_CONF3_lf_op        (1U << 0)\n\n#define ST25R3916_REG_RX_CONF4_rg2_am3      (1U << 7)\n#define ST25R3916_REG_RX_CONF4_rg2_am2      (1U << 6)\n#define ST25R3916_REG_RX_CONF4_rg2_am1      (1U << 5)\n#define ST25R3916_REG_RX_CONF4_rg2_am0      (1U << 4)\n#define ST25R3916_REG_RX_CONF4_rg2_am_mask  (0xfU << 4)\n#define ST25R3916_REG_RX_CONF4_rg2_am_shift (4U)\n#define ST25R3916_REG_RX_CONF4_rg2_pm3      (1U << 3)\n#define ST25R3916_REG_RX_CONF4_rg2_pm2      (1U << 2)\n#define ST25R3916_REG_RX_CONF4_rg2_pm1      (1U << 1)\n#define ST25R3916_REG_RX_CONF4_rg2_pm0      (1U << 0)\n#define ST25R3916_REG_RX_CONF4_rg2_pm_mask  (0xfU << 0)\n#define ST25R3916_REG_RX_CONF4_rg2_pm_shift (0U)\n\n#define ST25R3916_REG_P2P_RX_CONF_ook_fd   (1U << 7)\n#define ST25R3916_REG_P2P_RX_CONF_ook_rc1  (1U << 6)\n#define ST25R3916_REG_P2P_RX_CONF_ook_rc0  (1U << 5)\n#define ST25R3916_REG_P2P_RX_CONF_ook_thd1 (1U << 4)\n#define ST25R3916_REG_P2P_RX_CONF_ook_thd0 (1U << 3)\n#define ST25R3916_REG_P2P_RX_CONF_ask_rc1  (1U << 2)\n#define ST25R3916_REG_P2P_RX_CONF_ask_rc0  (1U << 1)\n#define ST25R3916_REG_P2P_RX_CONF_ask_thd  (1U << 0)\n\n#define ST25R3916_REG_CORR_CONF1_corr_s7 (1U << 7)\n#define ST25R3916_REG_CORR_CONF1_corr_s6 (1U << 6)\n#define ST25R3916_REG_CORR_CONF1_corr_s5 (1U << 5)\n#define ST25R3916_REG_CORR_CONF1_corr_s4 (1U << 4)\n#define ST25R3916_REG_CORR_CONF1_corr_s3 (1U << 3)\n#define ST25R3916_REG_CORR_CONF1_corr_s2 (1U << 2)\n#define ST25R3916_REG_CORR_CONF1_corr_s1 (1U << 1)\n#define ST25R3916_REG_CORR_CONF1_corr_s0 (1U << 0)\n\n#define ST25R3916_REG_CORR_CONF2_rfu5    (1U << 7)\n#define ST25R3916_REG_CORR_CONF2_rfu4    (1U << 6)\n#define ST25R3916_REG_CORR_CONF2_rfu3    (1U << 5)\n#define ST25R3916_REG_CORR_CONF2_rfu2    (1U << 4)\n#define ST25R3916_REG_CORR_CONF2_rfu1    (1U << 3)\n#define ST25R3916_REG_CORR_CONF2_rfu0    (1U << 2)\n#define ST25R3916_REG_CORR_CONF2_corr_s9 (1U << 1)\n#define ST25R3916_REG_CORR_CONF2_corr_s8 (1U << 0)\n\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc2            (1U << 7)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc1            (1U << 6)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc0            (1U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger  (0U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx         (1U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_srx         (2U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc     (3U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask        (7U << 5)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_shift       (5U)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_rfu              (1U << 4)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step         (1U << 3)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_512     (1U << 3)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_64      (0U << 3)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc          (1U << 2)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_on       (1U << 2)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off      (0U << 2)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv          (1U << 1)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_on       (1U << 1)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_off      (0U << 1)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step         (1U << 0)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_64fc    (0U << 0)\n#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_4096_fc (1U << 0)\n\n#define ST25R3916_REG_FIFO_STATUS2_fifo_b9       (1U << 7)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_b8       (1U << 6)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_b_mask   (3U << 6)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_b_shift  (6U)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_unf      (1U << 5)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_ovr      (1U << 4)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_lb2      (1U << 3)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_lb1      (1U << 2)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_lb0      (1U << 1)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask  (7U << 1)\n#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift (1U)\n#define ST25R3916_REG_FIFO_STATUS2_np_lb         (1U << 0)\n\n#define ST25R3916_REG_COLLISION_STATUS_c_byte3      (1U << 7)\n#define ST25R3916_REG_COLLISION_STATUS_c_byte2      (1U << 6)\n#define ST25R3916_REG_COLLISION_STATUS_c_byte1      (1U << 5)\n#define ST25R3916_REG_COLLISION_STATUS_c_byte0      (1U << 4)\n#define ST25R3916_REG_COLLISION_STATUS_c_byte_mask  (0xfU << 4)\n#define ST25R3916_REG_COLLISION_STATUS_c_byte_shift (4U)\n#define ST25R3916_REG_COLLISION_STATUS_c_bit2       (1U << 3)\n#define ST25R3916_REG_COLLISION_STATUS_c_bit1       (1U << 2)\n#define ST25R3916_REG_COLLISION_STATUS_c_bit0       (1U << 1)\n#define ST25R3916_REG_COLLISION_STATUS_c_pb         (1U << 0)\n#define ST25R3916_REG_COLLISION_STATUS_c_bit_mask   (3U << 1)\n#define ST25R3916_REG_COLLISION_STATUS_c_bit_shift  (1U)\n\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu               (1U << 7)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu1              (1U << 6)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu2              (1U << 5)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu3              (1U << 4)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state3        (1U << 3)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state2        (1U << 2)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state1        (1U << 1)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state0        (1U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_power_off  (0x0U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_idle       (0x1U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1   (0x2U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2   (0x3U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu4       (0x4U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active     (0x5U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu6       (0x6U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu7       (0x7U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu8       (0x8U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_halt       (0x9U << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1_x (0xaU << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2_x (0xbU << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu12      (0xcU << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active_x   (0xdU << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_mask    (0xfU << 0)\n#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_shift   (0U)\n\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx4       (1U << 7)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx3       (1U << 6)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx2       (1U << 5)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx1       (1U << 4)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx0       (1U << 3)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx_mask   (0x1fU << 3)\n#define ST25R3916_REG_NUM_TX_BYTES2_ntx_shift  (3U)\n#define ST25R3916_REG_NUM_TX_BYTES2_nbtx2      (1U << 2)\n#define ST25R3916_REG_NUM_TX_BYTES2_nbtx1      (1U << 1)\n#define ST25R3916_REG_NUM_TX_BYTES2_nbtx0      (1U << 0)\n#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_mask  (7U << 0)\n#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_shift (0U)\n\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu1       (1U << 7)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu0       (1U << 6)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate1      (1U << 5)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate0      (1U << 4)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_mask  (0x3U << 4)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift (4U)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_ppt2_on        (1U << 3)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on         (1U << 2)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on         (1U << 1)\n#define ST25R3916_REG_NFCIP1_BIT_RATE_mrt_on         (1U << 0)\n\n#define ST25R3916_REG_TX_DRIVER_am_mod3          (1U << 7)\n#define ST25R3916_REG_TX_DRIVER_am_mod2          (1U << 6)\n#define ST25R3916_REG_TX_DRIVER_am_mod1          (1U << 5)\n#define ST25R3916_REG_TX_DRIVER_am_mod0          (1U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_5percent  (0x0U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_6percent  (0x1U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_7percent  (0x2U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_8percent  (0x3U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_9percent  (0x4U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_10percent (0x5U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_11percent (0x6U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_12percent (0x7U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_13percent (0x8U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_14percent (0x9U << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_15percent (0xaU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_17percent (0xbU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_19percent (0xcU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_22percent (0xdU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_26percent (0xeU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_40percent (0xfU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_mask      (0xfU << 4)\n#define ST25R3916_REG_TX_DRIVER_am_mod_shift     (4U)\n#define ST25R3916_REG_TX_DRIVER_d_res3           (1U << 3)\n#define ST25R3916_REG_TX_DRIVER_d_res2           (1U << 2)\n#define ST25R3916_REG_TX_DRIVER_d_res1           (1U << 1)\n#define ST25R3916_REG_TX_DRIVER_d_res0           (1U << 0)\n#define ST25R3916_REG_TX_DRIVER_d_res_mask       (0xfU << 0)\n#define ST25R3916_REG_TX_DRIVER_d_res_shift      (0U)\n\n#define ST25R3916_REG_PT_MOD_ptm_res3      (1U << 7)\n#define ST25R3916_REG_PT_MOD_ptm_res2      (1U << 6)\n#define ST25R3916_REG_PT_MOD_ptm_res1      (1U << 5)\n#define ST25R3916_REG_PT_MOD_ptm_res0      (1U << 4)\n#define ST25R3916_REG_PT_MOD_ptm_res_mask  (0xfU << 4)\n#define ST25R3916_REG_PT_MOD_ptm_res_shift (4U)\n#define ST25R3916_REG_PT_MOD_pt_res3       (1U << 3)\n#define ST25R3916_REG_PT_MOD_pt_res2       (1U << 2)\n#define ST25R3916_REG_PT_MOD_pt_res1       (1U << 1)\n#define ST25R3916_REG_PT_MOD_pt_res0       (1U << 0)\n#define ST25R3916_REG_PT_MOD_pt_res_mask   (0xfU << 0)\n#define ST25R3916_REG_PT_MOD_pt_res_shift  (0U)\n\n#define ST25R3916_REG_AUX_MOD_dis_reg_am (1U << 7)\n#define ST25R3916_REG_AUX_MOD_lm_ext_pol (1U << 6)\n#define ST25R3916_REG_AUX_MOD_lm_ext     (1U << 5)\n#define ST25R3916_REG_AUX_MOD_lm_dri     (1U << 4)\n#define ST25R3916_REG_AUX_MOD_res_am     (1U << 3)\n#define ST25R3916_REG_AUX_MOD_rfu2       (1U << 2)\n#define ST25R3916_REG_AUX_MOD_rfu1       (1U << 1)\n#define ST25R3916_REG_AUX_MOD_rfu0       (1U << 0)\n\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t3      (1U << 7)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t2      (1U << 6)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t1      (1U << 5)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t0      (1U << 4)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_mask    (0xfU << 4)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_shift   (4U)\n#define ST25R3916_REG_TX_DRIVER_TIMING_rfu           (1U << 3)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m2      (1U << 2)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m1      (1U << 1)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m0      (1U << 0)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_mask  (0x7U << 0)\n#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_shift (0U)\n\n#define ST25R3916_REG_RES_AM_MOD_fa3_f        (1U << 7)\n#define ST25R3916_REG_RES_AM_MOD_md_res6      (1U << 6)\n#define ST25R3916_REG_RES_AM_MOD_md_res5      (1U << 5)\n#define ST25R3916_REG_RES_AM_MOD_md_res4      (1U << 4)\n#define ST25R3916_REG_RES_AM_MOD_md_res3      (1U << 3)\n#define ST25R3916_REG_RES_AM_MOD_md_res2      (1U << 2)\n#define ST25R3916_REG_RES_AM_MOD_md_res1      (1U << 1)\n#define ST25R3916_REG_RES_AM_MOD_md_res0      (1U << 0)\n#define ST25R3916_REG_RES_AM_MOD_md_res_mask  (0x7FU << 0)\n#define ST25R3916_REG_RES_AM_MOD_md_res_shift (0U)\n\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r3    (1U << 7)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r2    (1U << 6)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r1    (1U << 5)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r0    (1U << 4)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_mask  (0xfU << 4)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_shift (4U)\n#define ST25R3916_REG_TX_DRIVER_STATUS_rfu         (1U << 3)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r2    (1U << 2)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r1    (1U << 1)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r0    (1U << 0)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_mask  (0x7U << 0)\n#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_shift (0U)\n\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l2a   (1U << 6)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l1a   (1U << 5)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l0a   (1U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_75mV  (0x0U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV (0x1U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_150mV (0x2U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_205mV (0x3U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_290mV (0x4U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_400mV (0x5U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_560mV (0x6U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_800mV (0x7U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask  (7U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_shift (4U)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t3a   (1U << 3)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t2a   (1U << 2)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t1a   (1U << 1)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t0a   (1U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_75mV  (0x0U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV (0x1U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_150mV (0x2U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_205mV (0x3U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_290mV (0x4U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_400mV (0x5U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_560mV (0x6U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_800mV (0x7U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_25mV  (0x8U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_33mV  (0x9U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_47mV  (0xAU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_64mV  (0xBU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_90mV  (0xCU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_125mV (0xDU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_175mV (0xEU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_250mV (0xFU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask  (0xfU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_shift (0U)\n\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l2d   (1U << 6)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l1d   (1U << 5)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l0d   (1U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV  (0x0U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_105mV (0x1U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_150mV (0x2U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_205mV (0x3U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_290mV (0x4U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_400mV (0x5U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_560mV (0x6U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_800mV (0x7U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask  (7U << 4)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_shift (4U)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t3d   (1U << 3)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t2d   (1U << 2)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t1d   (1U << 1)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t0d   (1U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV  (0x0U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_105mV (0x1U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_150mV (0x2U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_205mV (0x3U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_290mV (0x4U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_400mV (0x5U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_560mV (0x6U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_800mV (0x7U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_25mV  (0x8U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_33mV  (0x9U << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_47mV  (0xAU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_64mV  (0xBU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_90mV  (0xCU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_125mV (0xDU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_175mV (0xEU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_250mV (0xFU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask  (0xfU << 0)\n#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_shift (0U)\n\n#define ST25R3916_REG_REGULATOR_CONTROL_reg_s       (1U << 7)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_3      (1U << 6)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_2      (1U << 5)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_1      (1U << 4)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_0      (1U << 3)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_mask   (0xfU << 3)\n#define ST25R3916_REG_REGULATOR_CONTROL_rege_shift  (3U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv2       (2U << 2)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv1       (1U << 1)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv0       (1U << 0)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd    (0U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a  (1U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d  (2U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf (3U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am (4U)\n#define ST25R3916_REG_REGULATOR_CONTROL_rfu         (5U)\n#define ST25R3916_REG_REGULATOR_CONTROL_rfu1        (6U)\n#define ST25R3916_REG_REGULATOR_CONTROL_rfu2        (7U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask   (7U)\n#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_shift  (0U)\n\n#define ST25R3916_REG_REGULATOR_RESULT_reg_3     (1U << 7)\n#define ST25R3916_REG_REGULATOR_RESULT_reg_2     (1U << 6)\n#define ST25R3916_REG_REGULATOR_RESULT_reg_1     (1U << 5)\n#define ST25R3916_REG_REGULATOR_RESULT_reg_0     (1U << 4)\n#define ST25R3916_REG_REGULATOR_RESULT_reg_mask  (0xfU << 4)\n#define ST25R3916_REG_REGULATOR_RESULT_reg_shift (4U)\n#define ST25R3916_REG_REGULATOR_RESULT_i_lim     (1U << 0)\n\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_3     (1U << 7)\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_2     (1U << 6)\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_1     (1U << 5)\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_0     (1U << 4)\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_mask  (0xfU << 4)\n#define ST25R3916_REG_RSSI_RESULT_rssi_am_shift (4U)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm3      (1U << 3)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm2      (1U << 2)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm1      (1U << 1)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm0      (1U << 0)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm_mask  (0xfU << 0)\n#define ST25R3916_REG_RSSI_RESULT_rssi_pm_shift (0U)\n\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_3     (1U << 7)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_2     (1U << 6)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_1     (1U << 5)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_0     (1U << 4)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_mask  (0xfU << 4)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_am_shift (4U)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_3     (1U << 3)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_2     (1U << 2)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_1     (1U << 1)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_0     (1U << 0)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_mask  (0xfU << 0)\n#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_shift (0U)\n\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal4      (1U << 7)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal3      (1U << 6)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal2      (1U << 5)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal1      (1U << 4)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal0      (1U << 3)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_mask  (0x1fU << 3)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_shift (3U)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g2         (1U << 2)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g1         (1U << 1)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g0         (1U << 0)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_mask     (7U << 0)\n#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_shift    (0U)\n\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal4      (1U << 7)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal3      (1U << 6)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal2      (1U << 5)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal1      (1U << 4)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal0      (1U << 3)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_mask  (0x1fU << 3)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_shift (3U)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end   (1U << 2)\n#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err   (1U << 1)\n\n#define ST25R3916_REG_AUX_DISPLAY_a_cha   (1U << 7)\n#define ST25R3916_REG_AUX_DISPLAY_efd_o   (1U << 6)\n#define ST25R3916_REG_AUX_DISPLAY_tx_on   (1U << 5)\n#define ST25R3916_REG_AUX_DISPLAY_osc_ok  (1U << 4)\n#define ST25R3916_REG_AUX_DISPLAY_rx_on   (1U << 3)\n#define ST25R3916_REG_AUX_DISPLAY_rx_act  (1U << 2)\n#define ST25R3916_REG_AUX_DISPLAY_en_peer (1U << 1)\n#define ST25R3916_REG_AUX_DISPLAY_en_ac   (1U << 0)\n\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode1  (1U << 7)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode0  (1U << 6)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern13 (1U << 5)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern12 (1U << 4)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern11 (1U << 3)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern10 (1U << 2)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern9  (1U << 1)\n#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern8  (1U << 0)\n\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern7 (1U << 7)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern6 (1U << 6)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern5 (1U << 5)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern4 (1U << 4)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern3 (1U << 3)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern2 (1U << 2)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern1 (1U << 1)\n#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern0 (1U << 0)\n\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode1  (1U << 7)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode0  (1U << 6)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern13 (1U << 5)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern12 (1U << 4)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern11 (1U << 3)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern10 (1U << 2)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern9  (1U << 1)\n#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern8  (1U << 0)\n\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern7 (1U << 7)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern6 (1U << 6)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern5 (1U << 5)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern4 (1U << 4)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern3 (1U << 3)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern2 (1U << 2)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern1 (1U << 1)\n#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern0 (1U << 0)\n\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wur       (1U << 7)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wut2      (1U << 6)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wut1      (1U << 5)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wut0      (1U << 4)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_mask  (7U << 4)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_shift (4U)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wto       (1U << 3)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wam       (1U << 2)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wph       (1U << 1)\n#define ST25R3916_REG_WUP_TIMER_CONTROL_wcap      (1U << 0)\n\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d3        (1U << 7)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d2        (1U << 6)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d1        (1U << 5)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d0        (1U << 4)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_mask    (0xfU << 4)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_shift   (4U)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aam       (1U << 3)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew1      (1U << 2)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew0      (1U << 1)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_mask  (0x3U << 1)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_shift (1U)\n#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_ae        (1U << 0)\n\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d3        (1U << 7)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d2        (1U << 6)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d1        (1U << 5)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d0        (1U << 4)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_mask    (0xfU << 4)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_shift   (4U)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aam       (1U << 3)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew1      (1U << 2)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew0      (1U << 1)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_mask  (0x3U << 1)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_shift (1U)\n#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_ae        (1U << 0)\n\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d3        (1U << 7)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d2        (1U << 6)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d1        (1U << 5)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d0        (1U << 4)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_mask    (0xfU << 4)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_shift   (4U)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aam       (1U << 3)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew1      (1U << 2)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew0      (1U << 1)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_mask  (0x3U << 1)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_shift (1U)\n#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_ae        (1U << 0)\n\n#define ST25R3916_REG_IC_IDENTITY_ic_type4          (1U << 7)\n#define ST25R3916_REG_IC_IDENTITY_ic_type3          (1U << 6)\n#define ST25R3916_REG_IC_IDENTITY_ic_type2          (1U << 5)\n#define ST25R3916_REG_IC_IDENTITY_ic_type1          (1U << 4)\n#define ST25R3916_REG_IC_IDENTITY_ic_type0          (1U << 3)\n#define ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916 (5U << 3)\n#define ST25R3916_REG_IC_IDENTITY_ic_type_mask      (0x1fU << 3)\n#define ST25R3916_REG_IC_IDENTITY_ic_type_shift     (3U)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev2           (1U << 2)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev1           (1U << 1)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev0           (1U << 0)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev_v0         (0U << 0)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev_mask       (7U << 0)\n#define ST25R3916_REG_IC_IDENTITY_ic_rev_shift      (0U)\n\n/** Read register\n *\n * @param   handle      - pointer t FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   val         - pointer to the variable to store the read value\n */\nvoid st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);\n\n/** Read multiple registers\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg_start   - start register address\n * @param   values      - pointer to the buffer to store the read values\n * @param   length      - number of registers to read\n */\nvoid st25r3916_read_burst_regs(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg_start,\n    uint8_t* values,\n    uint8_t length);\n\n/** Write register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   val         - value to write\n */\nvoid st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);\n\n/** Write multiple registers\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg_start   - start register address\n * @param   values      - pointer to buffer to write\n * @param   length      - number of registers to write\n */\nvoid st25r3916_write_burst_regs(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg_start,\n    const uint8_t* values,\n    uint8_t length);\n\n/** Write fifo register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   buff        - buffer to write to FIFO\n * @param   length      - number of bytes to write\n */\nvoid st25r3916_reg_write_fifo(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* buff,\n    size_t length);\n\n/** Read fifo register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   buff        - buffer to store the read values\n * @param   length      - number of bytes to read\n */\nvoid st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length);\n\n/** Write PTA memory register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   values      - pointer to buffer to write\n * @param   length      - number of bytes to write\n */\nvoid st25r3916_write_pta_mem(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* values,\n    size_t length);\n\n/** Read PTA memory register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   values      - buffer to store the read values\n * @param   length      - number of bytes to read\n */\nvoid st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);\n\n/** Write PTF memory register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   values      - pointer to buffer to write\n * @param   length      - number of bytes to write\n */\nvoid st25r3916_write_ptf_mem(\n    const FuriHalSpiBusHandle* handle,\n    const uint8_t* values,\n    size_t length);\n\n/** Read PTTSN memory register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   values      - pointer to buffer to write\n * @param   length      - number of bytes to write\n */\nvoid st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);\n\n/** Send Direct command\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   cmd         - direct command\n */\nvoid st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd);\n\n/** Read test register\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   val         - pointer to the variable to store the read value\n */\nvoid st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);\n\n/** Write test register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   val         - value to write\n */\nvoid st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);\n\n/** Clear register bits\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   clr_mask    - bit mask to clear\n */\nvoid st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask);\n\n/** Set register bits\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   set_mask    - bit mask to set\n */\nvoid st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask);\n\n/** Change register bits\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   mask        - bit mask to change\n * @param   value       - new register value to write\n */\nvoid st25r3916_change_reg_bits(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t mask,\n    uint8_t value);\n\n/** Modify register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   clr_mask    - bit mask to clear\n * @param   set_mask    - bit mask to set\n */\nvoid st25r3916_modify_reg(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t clr_mask,\n    uint8_t set_mask);\n\n/** Change test register bits\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   mask        - bit mask to change\n * @param   value       - new register value to write\n */\nvoid st25r3916_change_test_reg_bits(\n    const FuriHalSpiBusHandle* handle,\n    uint8_t reg,\n    uint8_t mask,\n    uint8_t value);\n\n/** Check register\n *\n * @param   handle      - pointer to FuriHalSpiBusHandle instance\n * @param   reg         - register address\n * @param   mask        - bit mask to check\n * @param   val         - expected register value\n *\n * @return  true if register value matches the expected value, false otherwise\n */\nbool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/err.h",
    "content": "#pragma once\n#include <furi.h>\n\n#define err(...) FURI_LOG_E(\"Heatshrink\", \"Error: %d-%s\", __VA_ARGS__)"
  },
  {
    "path": "lib/fatfs/SConscript",
    "content": "Import(\"env\")\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"fatfs\")\nlibenv.ApplyLibFlags()\n\n\nsources = [\"option/unicode.c\"]\nsources += Glob(\"*.c\", source=True)\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/fatfs/diskio.c",
    "content": "/*-----------------------------------------------------------------------*/\n/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2017        */\n/*                                                                       */\n/*   Portions COPYRIGHT 2017 STMicroelectronics                          */\n/*   Portions Copyright (C) 2017, ChaN, all right reserved               */\n/*-----------------------------------------------------------------------*/\n/* If a working storage control module is available, it should be        */\n/* attached to the FatFs via a glue function rather than modifying it.   */\n/* This is an example of glue functions to attach various existing      */\n/* storage control modules to the FatFs module with a defined API.       */\n/*-----------------------------------------------------------------------*/\n\n/* Includes ------------------------------------------------------------------*/\n#include \"diskio.h\"\n#include \"ff_gen_drv.h\"\n\n#if defined ( __GNUC__ )\n#ifndef __weak\n#define __weak __attribute__((weak))\n#endif\n#endif\n\n/* Private typedef -----------------------------------------------------------*/\n/* Private define ------------------------------------------------------------*/\n/* Private variables ---------------------------------------------------------*/\nextern Disk_drvTypeDef  disk;\n\n/* Private function prototypes -----------------------------------------------*/\n/* Private functions ---------------------------------------------------------*/\n\n/**\n  * @brief  Gets Disk Status\n  * @param  pdrv: Physical drive number (0..)\n  * @retval DSTATUS: Operation status\n  */\nDSTATUS disk_status (\n\tBYTE pdrv\t\t/* Physical drive number to identify the drive */\n)\n{\n  DSTATUS stat;\n\n  stat = disk.drv[pdrv]->disk_status(disk.lun[pdrv]);\n  return stat;\n}\n\n/**\n  * @brief  Initializes a Drive\n  * @param  pdrv: Physical drive number (0..)\n  * @retval DSTATUS: Operation status\n  */\nDSTATUS disk_initialize (\n\tBYTE pdrv\t\t\t\t/* Physical drive nmuber to identify the drive */\n)\n{\n  DSTATUS stat = RES_OK;\n\n  if(disk.is_initialized[pdrv] == 0)\n  {\n    disk.is_initialized[pdrv] = 1;\n    stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]);\n  }\n  return stat;\n}\n\n/**\n  * @brief  Reads Sector(s)\n  * @param  pdrv: Physical drive number (0..)\n  * @param  *buff: Data buffer to store read data\n  * @param  sector: Sector address (LBA)\n  * @param  count: Number of sectors to read (1..128)\n  * @retval DRESULT: Operation result\n  */\nDRESULT disk_read (\n\tBYTE pdrv,\t\t/* Physical drive nmuber to identify the drive */\n\tBYTE *buff,\t\t/* Data buffer to store read data */\n\tDWORD sector,\t        /* Sector address in LBA */\n\tUINT count\t\t/* Number of sectors to read */\n)\n{\n  DRESULT res;\n\n  res = disk.drv[pdrv]->disk_read(disk.lun[pdrv], buff, sector, count);\n  return res;\n}\n\n/**\n  * @brief  Writes Sector(s)\n  * @param  pdrv: Physical drive number (0..)\n  * @param  *buff: Data to be written\n  * @param  sector: Sector address (LBA)\n  * @param  count: Number of sectors to write (1..128)\n  * @retval DRESULT: Operation result\n  */\n#if _USE_WRITE == 1\nDRESULT disk_write (\n\tBYTE pdrv,\t\t/* Physical drive nmuber to identify the drive */\n\tconst BYTE *buff,\t/* Data to be written */\n\tDWORD sector,\t\t/* Sector address in LBA */\n\tUINT count        \t/* Number of sectors to write */\n)\n{\n  DRESULT res;\n\n  res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count);\n  return res;\n}\n#endif /* _USE_WRITE == 1 */\n\n/**\n  * @brief  I/O control operation\n  * @param  pdrv: Physical drive number (0..)\n  * @param  cmd: Control code\n  * @param  *buff: Buffer to send/receive control data\n  * @retval DRESULT: Operation result\n  */\n#if _USE_IOCTL == 1\nDRESULT disk_ioctl (\n\tBYTE pdrv,\t\t/* Physical drive nmuber (0..) */\n\tBYTE cmd,\t\t/* Control code */\n\tvoid *buff\t\t/* Buffer to send/receive control data */\n)\n{\n  DRESULT res;\n\n  res = disk.drv[pdrv]->disk_ioctl(disk.lun[pdrv], cmd, buff);\n  return res;\n}\n#endif /* _USE_IOCTL == 1 */\n\n/**\n  * @brief  Gets Time from RTC\n  * @param  None\n  * @retval Time in DWORD\n  */\n__weak DWORD get_fattime (void)\n{\n  return 0;\n}\n\n/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/\n\n"
  },
  {
    "path": "lib/fatfs/diskio.h",
    "content": "/*-----------------------------------------------------------------------/\n/  Low level disk interface modlue include file   (C)ChaN, 2014          /\n/-----------------------------------------------------------------------*/\n\n#ifndef _DISKIO_DEFINED\n#define _DISKIO_DEFINED\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define _USE_WRITE\t1\t/* 1: Enable disk_write function */\n#define _USE_IOCTL\t1\t/* 1: Enable disk_ioctl function */\n\n#include \"integer.h\"\n\n\n/* Status of Disk Functions */\ntypedef BYTE\tDSTATUS;\n\n/* Results of Disk Functions */\ntypedef enum {\n\tRES_OK = 0,\t\t/* 0: Successful */\n\tRES_ERROR,\t\t/* 1: R/W Error */\n\tRES_WRPRT,\t\t/* 2: Write Protected */\n\tRES_NOTRDY,\t\t/* 3: Not Ready */\n\tRES_PARERR\t\t/* 4: Invalid Parameter */\n} DRESULT;\n\n\n/*---------------------------------------*/\n/* Prototypes for disk control functions */\n\n\nDSTATUS disk_initialize (BYTE pdrv);\nDSTATUS disk_status (BYTE pdrv);\nDRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);\nDRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);\nDRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);\n\n/* Disk Status Bits (DSTATUS) */\n\n#define STA_NOINIT\t\t0x01\t/* Drive not initialized */\n#define STA_NODISK\t\t0x02\t/* No medium in the drive */\n#define STA_PROTECT\t\t0x04\t/* Write protected */\n\n\n/* Command code for disk_ioctrl fucntion */\n\n/* Generic command (Used by FatFs) */\n#define CTRL_SYNC\t\t0\t/* Complete pending write process (needed at _FS_READONLY == 0) */\n#define GET_SECTOR_COUNT\t1\t/* Get media size (needed at _USE_MKFS == 1) */\n#define GET_SECTOR_SIZE\t\t2\t/* Get sector size (needed at _MAX_SS != _MIN_SS) */\n#define GET_BLOCK_SIZE\t\t3\t/* Get erase block size (needed at _USE_MKFS == 1) */\n#define CTRL_TRIM\t\t4\t/* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */\n\n/* Generic command (Not used by FatFs) */\n#define CTRL_POWER\t\t\t5\t/* Get/Set power status */\n#define CTRL_LOCK\t\t\t6\t/* Lock/Unlock media removal */\n#define CTRL_EJECT\t\t\t7\t/* Eject media */\n#define CTRL_FORMAT\t\t\t8\t/* Create physical format on the media */\n\n/* MMC/SDC specific ioctl command */\n#define MMC_GET_TYPE\t\t10\t/* Get card type */\n#define MMC_GET_CSD\t\t\t11\t/* Get CSD */\n#define MMC_GET_CID\t\t\t12\t/* Get CID */\n#define MMC_GET_OCR\t\t\t13\t/* Get OCR */\n#define MMC_GET_SDSTAT\t\t14\t/* Get SD status */\n\n/* ATA/CF specific ioctl command */\n#define ATA_GET_REV\t\t\t20\t/* Get F/W revision */\n#define ATA_GET_MODEL\t\t21\t/* Get model name */\n#define ATA_GET_SN\t\t\t22\t/* Get serial number */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/fatfs/ff.c",
    "content": "/*----------------------------------------------------------------------------/\n/  FatFs - Generic FAT file system module  R0.12c                             /\n/-----------------------------------------------------------------------------/\n/\n/ Copyright (C) 2017, ChaN, all right reserved.\n/\n/ FatFs module is an open source software. Redistribution and use of FatFs in\n/ source and binary forms, with or without modification, are permitted provided\n/ that the following condition is met:\n/\n/ 1. Redistributions of source code must retain the above copyright notice,\n/    this condition and the following disclaimer.\n/\n/ This software is provided by the copyright holder and contributors \"AS IS\"\n/ and any warranties related to this software are DISCLAIMED.\n/ The copyright owner or contributors be NOT LIABLE for any damages caused\n/ by use of this software.\n/----------------------------------------------------------------------------*/\n\n\n#include \"ff.h\"\t\t\t/* Declarations of FatFs API */\n#include \"diskio.h\"\t\t/* Declarations of device I/O functions */\n\n\n/*--------------------------------------------------------------------------\n\n   Module Private Definitions\n\n---------------------------------------------------------------------------*/\n\n#if _FATFS != 68300\t/* Revision ID */\n#error Wrong include file (ff.h).\n#endif\n\n\n/* DBCS code ranges and SBCS upper conversion tables */\n\n#if _CODE_PAGE == 932\t/* Japanese Shift-JIS */\n#define _DF1S\t0x81\t/* DBC 1st byte range 1 start */\n#define _DF1E\t0x9F\t/* DBC 1st byte range 1 end */\n#define _DF2S\t0xE0\t/* DBC 1st byte range 2 start */\n#define _DF2E\t0xFC\t/* DBC 1st byte range 2 end */\n#define _DS1S\t0x40\t/* DBC 2nd byte range 1 start */\n#define _DS1E\t0x7E\t/* DBC 2nd byte range 1 end */\n#define _DS2S\t0x80\t/* DBC 2nd byte range 2 start */\n#define _DS2E\t0xFC\t/* DBC 2nd byte range 2 end */\n\n#elif _CODE_PAGE == 936\t/* Simplified Chinese GBK */\n#define _DF1S\t0x81\n#define _DF1E\t0xFE\n#define _DS1S\t0x40\n#define _DS1E\t0x7E\n#define _DS2S\t0x80\n#define _DS2E\t0xFE\n\n#elif _CODE_PAGE == 949\t/* Korean */\n#define _DF1S\t0x81\n#define _DF1E\t0xFE\n#define _DS1S\t0x41\n#define _DS1E\t0x5A\n#define _DS2S\t0x61\n#define _DS2E\t0x7A\n#define _DS3S\t0x81\n#define _DS3E\t0xFE\n\n#elif _CODE_PAGE == 950\t/* Traditional Chinese Big5 */\n#define _DF1S\t0x81\n#define _DF1E\t0xFE\n#define _DS1S\t0x40\n#define _DS1E\t0x7E\n#define _DS2S\t0xA1\n#define _DS2E\t0xFE\n\n#elif _CODE_PAGE == 437\t/* U.S. */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 720\t/* Arabic */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 737\t/* Greek */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \\\n\t\t\t\t0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 771\t/* KBL */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}\n\n#elif _CODE_PAGE == 775\t/* Baltic */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 850\t/* Latin 1 */\n#define _DF1S\t0\n#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \\\n\t\t\t\t0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \\\n\t\t\t\t0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \\\n\t\t\t\t0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 852\t/* Latin 2 */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \\\n\t\t\t\t0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}\n\n#elif _CODE_PAGE == 855\t/* Cyrillic */\n#define _DF1S\t0\n#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \\\n\t\t\t\t0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \\\n\t\t\t\t0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \\\n\t\t\t\t0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 857\t/* Turkish */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \\\n\t\t\t\t0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 860\t/* Portuguese */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 861\t/* Icelandic */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 862\t/* Hebrew */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 863\t/* Canadian-French */\n#define _DF1S\t0\n#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \\\n\t\t\t\t0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \\\n\t\t\t\t0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 864\t/* Arabic */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 865\t/* Nordic */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\\n\t\t\t\t0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 866\t/* Russian */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\\n\t\t\t\t0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\n\n#elif _CODE_PAGE == 869\t/* Greek 2 */\n#define _DF1S\t0\n#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\\n\t\t\t\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \\\n\t\t\t\t0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\\n\t\t\t\t0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\\n\t\t\t\t0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\\n\t\t\t\t0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \\\n\t\t\t\t0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \\\n\t\t\t\t0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}\n\n#elif _CODE_PAGE == 1\t/* ASCII (for only non-LFN cfg) */\n#if _USE_LFN != 0\n#error Cannot enable LFN without valid code page.\n#endif\n#define _DF1S\t0\n\n#else\n#error Unknown code page\n\n#endif\n\n\n/* Character code support macros */\n#define IsUpper(c)\t(((c)>='A')&&((c)<='Z'))\n#define IsLower(c)\t(((c)>='a')&&((c)<='z'))\n#define IsDigit(c)\t(((c)>='0')&&((c)<='9'))\n\n#if _DF1S != 0\t/* Code page is DBCS */\n\n#ifdef _DF2S\t/* Two 1st byte areas */\n#define IsDBCS1(c)\t(((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))\n#else\t\t\t/* One 1st byte area */\n#define IsDBCS1(c)\t((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)\n#endif\n\n#ifdef _DS3S\t/* Three 2nd byte areas */\n#define IsDBCS2(c)\t(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))\n#else\t\t\t/* Two 2nd byte areas */\n#define IsDBCS2(c)\t(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))\n#endif\n\n#else\t\t\t/* Code page is SBCS */\n\n#define IsDBCS1(c)\t0\n#define IsDBCS2(c)\t0\n\n#endif /* _DF1S */\n\n\n/* Additional file attribute bits for internal use */\n#define\tAM_VOL\t\t0x08\t/* Volume label */\n#define AM_LFN\t\t0x0F\t/* LFN entry */\n#define AM_MASK\t\t0x3F\t/* Mask of defined bits */\n\n\n/* Additional file access control and file status flags for internal use */\n#define FA_SEEKEND\t0x20\t/* Seek to end of the file on file open */\n#define FA_MODIFIED\t0x40\t/* File has been modified */\n#define FA_DIRTY\t0x80\t/* FIL.buf[] needs to be written-back */\n\n\n/* Name status flags in fn[] */\n#define NSFLAG\t\t11\t\t/* Index of the name status byte */\n#define NS_LOSS\t\t0x01\t/* Out of 8.3 format */\n#define NS_LFN\t\t0x02\t/* Force to create LFN entry */\n#define NS_LAST\t\t0x04\t/* Last segment */\n#define NS_BODY\t\t0x08\t/* Lower case flag (body) */\n#define NS_EXT\t\t0x10\t/* Lower case flag (ext) */\n#define NS_DOT\t\t0x20\t/* Dot entry */\n#define NS_NOLFN\t0x40\t/* Do not find LFN */\n#define NS_NONAME\t0x80\t/* Not followed */\n\n\n/* Limits and boundaries */\n#define MAX_DIR\t\t0x200000\t\t/* Max size of FAT directory */\n#define MAX_DIR_EX\t0x10000000\t\t/* Max size of exFAT directory */\n#define MAX_FAT12\t0xFF5\t\t\t/* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */\n#define\tMAX_FAT16\t0xFFF5\t\t\t/* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */\n#define\tMAX_FAT32\t0x0FFFFFF5\t\t/* Max FAT32 clusters (not specified, practical limit) */\n#define\tMAX_EXFAT\t0x7FFFFFFD\t\t/* Max exFAT clusters (differs from specs, implementation limit) */\n\n\n/* FatFs refers the FAT structure as simple byte array instead of structure member\n/ because the C structure is not binary compatible between different platforms */\n\n#define BS_JmpBoot\t\t\t0\t\t/* x86 jump instruction (3-byte) */\n#define BS_OEMName\t\t\t3\t\t/* OEM name (8-byte) */\n#define BPB_BytsPerSec\t\t11\t\t/* Sector size [byte] (WORD) */\n#define BPB_SecPerClus\t\t13\t\t/* Cluster size [sector] (BYTE) */\n#define BPB_RsvdSecCnt\t\t14\t\t/* Size of reserved area [sector] (WORD) */\n#define BPB_NumFATs\t\t\t16\t\t/* Number of FATs (BYTE) */\n#define BPB_RootEntCnt\t\t17\t\t/* Size of root directory area for FAT12/16 [entry] (WORD) */\n#define BPB_TotSec16\t\t19\t\t/* Volume size (16-bit) [sector] (WORD) */\n#define BPB_Media\t\t\t21\t\t/* Media descriptor byte (BYTE) */\n#define BPB_FATSz16\t\t\t22\t\t/* FAT size (16-bit) [sector] (WORD) */\n#define BPB_SecPerTrk\t\t24\t\t/* Track size for int13h [sector] (WORD) */\n#define BPB_NumHeads\t\t26\t\t/* Number of heads for int13h (WORD) */\n#define BPB_HiddSec\t\t\t28\t\t/* Volume offset from top of the drive (DWORD) */\n#define BPB_TotSec32\t\t32\t\t/* Volume size (32-bit) [sector] (DWORD) */\n#define BS_DrvNum\t\t\t36\t\t/* Physical drive number for int13h (BYTE) */\n#define BS_NTres\t\t\t37\t\t/* Error flag (BYTE) */\n#define BS_BootSig\t\t\t38\t\t/* Extended boot signature (BYTE) */\n#define BS_VolID\t\t\t39\t\t/* Volume serial number (DWORD) */\n#define BS_VolLab\t\t\t43\t\t/* Volume label string (8-byte) */\n#define BS_FilSysType\t\t54\t\t/* File system type string (8-byte) */\n#define BS_BootCode\t\t\t62\t\t/* Boot code (448-byte) */\n#define BS_55AA\t\t\t\t510\t\t/* Signature word (WORD) */\n\n#define BPB_FATSz32\t\t\t36\t\t/* FAT32: FAT size [sector] (DWORD) */\n#define BPB_ExtFlags32\t\t40\t\t/* FAT32: Extended flags (WORD) */\n#define BPB_FSVer32\t\t\t42\t\t/* FAT32: File system version (WORD) */\n#define BPB_RootClus32\t\t44\t\t/* FAT32: Root directory cluster (DWORD) */\n#define BPB_FSInfo32\t\t48\t\t/* FAT32: Offset of FSINFO sector (WORD) */\n#define BPB_BkBootSec32\t\t50\t\t/* FAT32: Offset of backup boot sector (WORD) */\n#define BS_DrvNum32\t\t\t64\t\t/* FAT32: Physical drive number for int13h (BYTE) */\n#define BS_NTres32\t\t\t65\t\t/* FAT32: Error flag (BYTE) */\n#define BS_BootSig32\t\t66\t\t/* FAT32: Extended boot signature (BYTE) */\n#define BS_VolID32\t\t\t67\t\t/* FAT32: Volume serial number (DWORD) */\n#define BS_VolLab32\t\t\t71\t\t/* FAT32: Volume label string (8-byte) */\n#define BS_FilSysType32\t\t82\t\t/* FAT32: File system type string (8-byte) */\n#define BS_BootCode32\t\t90\t\t/* FAT32: Boot code (420-byte) */\n\n#define BPB_ZeroedEx\t\t11\t\t/* exFAT: MBZ field (53-byte) */\n#define BPB_VolOfsEx\t\t64\t\t/* exFAT: Volume offset from top of the drive [sector] (QWORD) */\n#define BPB_TotSecEx\t\t72\t\t/* exFAT: Volume size [sector] (QWORD) */\n#define BPB_FatOfsEx\t\t80\t\t/* exFAT: FAT offset from top of the volume [sector] (DWORD) */\n#define BPB_FatSzEx\t\t\t84\t\t/* exFAT: FAT size [sector] (DWORD) */\n#define BPB_DataOfsEx\t\t88\t\t/* exFAT: Data offset from top of the volume [sector] (DWORD) */\n#define BPB_NumClusEx\t\t92\t\t/* exFAT: Number of clusters (DWORD) */\n#define BPB_RootClusEx\t\t96\t\t/* exFAT: Root directory start cluster (DWORD) */\n#define BPB_VolIDEx\t\t\t100\t\t/* exFAT: Volume serial number (DWORD) */\n#define BPB_FSVerEx\t\t\t104\t\t/* exFAT: File system version (WORD) */\n#define BPB_VolFlagEx\t\t106\t\t/* exFAT: Volume flags (BYTE) */\n#define BPB_ActFatEx\t\t107\t\t/* exFAT: Active FAT flags (BYTE) */\n#define BPB_BytsPerSecEx\t108\t\t/* exFAT: Log2 of sector size in unit of byte (BYTE) */\n#define BPB_SecPerClusEx\t109\t\t/* exFAT: Log2 of cluster size in unit of sector (BYTE) */\n#define BPB_NumFATsEx\t\t110\t\t/* exFAT: Number of FATs (BYTE) */\n#define BPB_DrvNumEx\t\t111\t\t/* exFAT: Physical drive number for int13h (BYTE) */\n#define BPB_PercInUseEx\t\t112\t\t/* exFAT: Percent in use (BYTE) */\n#define\tBPB_RsvdEx\t\t\t113\t\t/* exFAT: Reserved (7-byte) */\n#define BS_BootCodeEx\t\t120\t\t/* exFAT: Boot code (390-byte) */\n\n#define\tDIR_Name\t\t\t0\t\t/* Short file name (11-byte) */\n#define\tDIR_Attr\t\t\t11\t\t/* Attribute (BYTE) */\n#define\tDIR_NTres\t\t\t12\t\t/* Lower case flag (BYTE) */\n#define DIR_CrtTime10\t\t13\t\t/* Created time sub-second (BYTE) */\n#define\tDIR_CrtTime\t\t\t14\t\t/* Created time (DWORD) */\n#define DIR_LstAccDate\t\t18\t\t/* Last accessed date (WORD) */\n#define\tDIR_FstClusHI\t\t20\t\t/* Higher 16-bit of first cluster (WORD) */\n#define\tDIR_ModTime\t\t\t22\t\t/* Modified time (DWORD) */\n#define\tDIR_FstClusLO\t\t26\t\t/* Lower 16-bit of first cluster (WORD) */\n#define\tDIR_FileSize\t\t28\t\t/* File size (DWORD) */\n#define\tLDIR_Ord\t\t\t0\t\t/* LFN: LFN order and LLE flag (BYTE) */\n#define\tLDIR_Attr\t\t\t11\t\t/* LFN: LFN attribute (BYTE) */\n#define\tLDIR_Type\t\t\t12\t\t/* LFN: Entry type (BYTE) */\n#define\tLDIR_Chksum\t\t\t13\t\t/* LFN: Checksum of the SFN (BYTE) */\n#define\tLDIR_FstClusLO\t\t26\t\t/* LFN: MBZ field (WORD) */\n#define\tXDIR_Type\t\t\t0\t\t/* exFAT: Type of exFAT directory entry (BYTE) */\n#define\tXDIR_NumLabel\t\t1\t\t/* exFAT: Number of volume label characters (BYTE) */\n#define\tXDIR_Label\t\t\t2\t\t/* exFAT: Volume label (11-WORD) */\n#define\tXDIR_CaseSum\t\t4\t\t/* exFAT: Sum of case conversion table (DWORD) */\n#define\tXDIR_NumSec\t\t\t1\t\t/* exFAT: Number of secondary entries (BYTE) */\n#define\tXDIR_SetSum\t\t\t2\t\t/* exFAT: Sum of the set of directory entries (WORD) */\n#define\tXDIR_Attr\t\t\t4\t\t/* exFAT: File attribute (WORD) */\n#define\tXDIR_CrtTime\t\t8\t\t/* exFAT: Created time (DWORD) */\n#define\tXDIR_ModTime\t\t12\t\t/* exFAT: Modified time (DWORD) */\n#define\tXDIR_AccTime\t\t16\t\t/* exFAT: Last accessed time (DWORD) */\n#define\tXDIR_CrtTime10\t\t20\t\t/* exFAT: Created time subsecond (BYTE) */\n#define\tXDIR_ModTime10\t\t21\t\t/* exFAT: Modified time subsecond (BYTE) */\n#define\tXDIR_CrtTZ\t\t\t22\t\t/* exFAT: Created timezone (BYTE) */\n#define\tXDIR_ModTZ\t\t\t23\t\t/* exFAT: Modified timezone (BYTE) */\n#define\tXDIR_AccTZ\t\t\t24\t\t/* exFAT: Last accessed timezone (BYTE) */\n#define\tXDIR_GenFlags\t\t33\t\t/* exFAT: General secondary flags (WORD) */\n#define\tXDIR_NumName\t\t35\t\t/* exFAT: Number of file name characters (BYTE) */\n#define\tXDIR_NameHash\t\t36\t\t/* exFAT: Hash of file name (WORD) */\n#define XDIR_ValidFileSize\t40\t\t/* exFAT: Valid file size (QWORD) */\n#define\tXDIR_FstClus\t\t52\t\t/* exFAT: First cluster of the file data (DWORD) */\n#define\tXDIR_FileSize\t\t56\t\t/* exFAT: File/Directory size (QWORD) */\n\n#define\tSZDIRE\t\t\t\t32\t\t/* Size of a directory entry */\n#define\tDDEM\t\t\t\t0xE5\t/* Deleted directory entry mark set to DIR_Name[0] */\n#define\tRDDEM\t\t\t\t0x05\t/* Replacement of the character collides with DDEM */\n#define\tLLEF\t\t\t\t0x40\t/* Last long entry flag in LDIR_Ord */\n\n#define\tFSI_LeadSig\t\t\t0\t\t/* FAT32 FSI: Leading signature (DWORD) */\n#define\tFSI_StrucSig\t\t484\t\t/* FAT32 FSI: Structure signature (DWORD) */\n#define\tFSI_Free_Count\t\t488\t\t/* FAT32 FSI: Number of free clusters (DWORD) */\n#define\tFSI_Nxt_Free\t\t492\t\t/* FAT32 FSI: Last allocated cluster (DWORD) */\n\n#define MBR_Table\t\t\t446\t\t/* MBR: Offset of partition table in the MBR */\n#define\tSZ_PTE\t\t\t\t16\t\t/* MBR: Size of a partition table entry */\n#define PTE_Boot\t\t\t0\t\t/* MBR PTE: Boot indicator */\n#define PTE_StHead\t\t\t1\t\t/* MBR PTE: Start head */\n#define PTE_StSec\t\t\t2\t\t/* MBR PTE: Start sector */\n#define PTE_StCyl\t\t\t3\t\t/* MBR PTE: Start cylinder */\n#define PTE_System\t\t\t4\t\t/* MBR PTE: System ID */\n#define PTE_EdHead\t\t\t5\t\t/* MBR PTE: End head */\n#define PTE_EdSec\t\t\t6\t\t/* MBR PTE: End sector */\n#define PTE_EdCyl\t\t\t7\t\t/* MBR PTE: End cylinder */\n#define PTE_StLba\t\t\t8\t\t/* MBR PTE: Start in LBA */\n#define PTE_SizLba\t\t\t12\t\t/* MBR PTE: Size in LBA */\n\n\n/* Post process after fatal error on file operation */\n#define\tABORT(fs, res)\t\t{ fp->err = (BYTE)(res); LEAVE_FF(fs, res); }\n\n\n/* Reentrancy related */\n#if _FS_REENTRANT\n#if _USE_LFN == 1\n#error Static LFN work area cannot be used at thread-safe configuration\n#endif\n#define\tENTER_FF(fs)\t\t{ if (!lock_fs(fs)) return FR_TIMEOUT; }\n#define\tLEAVE_FF(fs, res)\t{ unlock_fs(fs, res); return res; }\n#else\n#define\tENTER_FF(fs)\n#define LEAVE_FF(fs, res)\treturn res\n#endif\n\n\n/* Definitions of volume - partition conversion */\n#if _MULTI_PARTITION\n#define LD2PD(vol) VolToPart[vol].pd\t/* Get physical drive number */\n#define LD2PT(vol) VolToPart[vol].pt\t/* Get partition index */\n#else\n#define LD2PD(vol) (BYTE)(vol)\t/* Each logical drive is bound to the same physical drive number */\n#define LD2PT(vol) 0\t\t\t/* Find first valid partition or in SFD */\n#endif\n\n\n/* Definitions of sector size */\n#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)\n#error Wrong sector size configuration\n#endif\n#if _MAX_SS == _MIN_SS\n#define\tSS(fs)\t((UINT)_MAX_SS)\t/* Fixed sector size */\n#else\n#define\tSS(fs)\t((fs)->ssize)\t/* Variable sector size */\n#endif\n\n\n/* Timestamp */\n#if _FS_NORTC == 1\n#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31\n#error Invalid _FS_NORTC settings\n#endif\n#define GET_FATTIME()\t((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16)\n#else\n#define GET_FATTIME()\tget_fattime()\n#endif\n\n\n/* File lock controls */\n#if _FS_LOCK != 0\n#if _FS_READONLY\n#error _FS_LOCK must be 0 at read-only configuration\n#endif\ntypedef struct {\n\tFATFS *fs;\t\t/* Object ID 1, volume (NULL:blank entry) */\n\tDWORD clu;\t\t/* Object ID 2, containing directory (0:root) */\n\tDWORD ofs;\t\t/* Object ID 3, offset in the directory */\n\tWORD ctr;\t\t/* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */\n} FILESEM;\n#endif\n\n\n\n\n\n/*--------------------------------------------------------------------------\n\n   Module Private Work Area\n\n---------------------------------------------------------------------------*/\n\n/* Remark: Variables defined here without initial value shall be guaranteed\n/  zero/null at start-up. If not, the linker option or start-up routine is\n/  not compliance with C standard. */\n\n#if _VOLUMES < 1 || _VOLUMES > 10\n#error Wrong _VOLUMES setting\n#endif\nstatic FATFS *FatFs[_VOLUMES];\t/* Pointer to the file system objects (logical drives) */\nstatic WORD Fsid;\t\t\t\t/* File system mount ID */\n\n#if _FS_RPATH != 0 && _VOLUMES >= 2\nstatic BYTE CurrVol;\t\t\t/* Current drive */\n#endif\n\n#if _FS_LOCK != 0\nstatic FILESEM Files[_FS_LOCK];\t/* Open object lock semaphores */\n#endif\n\n#if _USE_LFN == 0\t\t/* Non-LFN configuration */\n#define\tDEF_NAMBUF\n#define INIT_NAMBUF(fs)\n#define\tFREE_NAMBUF()\n\n#else\t\t\t\t\t/* LFN configuration */\n#if _MAX_LFN < 12 || _MAX_LFN > 255\n#error Wrong _MAX_LFN value\n#endif\n#define MAXDIRB(nc)\t((nc + 44U) / 15 * SZDIRE)\n\n#if _USE_LFN == 1\t\t/* LFN enabled with static working buffer */\n#if _FS_EXFAT\nstatic BYTE\tDirBuf[MAXDIRB(_MAX_LFN)];\t/* Directory entry block scratchpad buffer */\n#endif\nstatic WCHAR LfnBuf[_MAX_LFN + 1];\t/* LFN enabled with static working buffer */\n#define\tDEF_NAMBUF\n#define INIT_NAMBUF(fs)\n#define\tFREE_NAMBUF()\n\n#elif _USE_LFN == 2 \t/* LFN enabled with dynamic working buffer on the stack */\n#if _FS_EXFAT\n#define\tDEF_NAMBUF\t\tWCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[MAXDIRB(_MAX_LFN)];\n#define INIT_NAMBUF(fs)\t{ (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }\n#define\tFREE_NAMBUF()\n#else\n#define\tDEF_NAMBUF\t\tWCHAR lbuf[_MAX_LFN+1];\n#define INIT_NAMBUF(fs)\t{ (fs)->lfnbuf = lbuf; }\n#define\tFREE_NAMBUF()\n#endif\n\n#elif _USE_LFN == 3 \t/* LFN enabled with dynamic working buffer on the heap */\n#if _FS_EXFAT\n#define\tDEF_NAMBUF\t\tWCHAR *lfn;\n#define INIT_NAMBUF(fs)\t{ lfn = ff_memalloc((_MAX_LFN+1)*2 + MAXDIRB(_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); }\n#define\tFREE_NAMBUF()\tff_memfree(lfn)\n#else\n#define\tDEF_NAMBUF\t\tWCHAR *lfn;\n#define INIT_NAMBUF(fs)\t{ lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }\n#define\tFREE_NAMBUF()\tff_memfree(lfn)\n#endif\n\n#else\n#error Wrong _USE_LFN setting\n\n#endif\n#endif\t/* else _USE_LFN == 0 */\n\n#ifdef _EXCVT\nstatic const BYTE ExCvt[] = _EXCVT;\t/* Upper conversion table for SBCS extended characters */\n#endif\n\n\n\n\n\n\n/*--------------------------------------------------------------------------\n\n   Module Private Functions\n\n---------------------------------------------------------------------------*/\n\n\n/*-----------------------------------------------------------------------*/\n/* Load/Store multi-byte word in the FAT structure                       */\n/*-----------------------------------------------------------------------*/\n\nstatic\nWORD ld_word (const BYTE* ptr)\t/*\t Load a 2-byte little-endian word */\n{\n\tWORD rv;\n\n\trv = ptr[1];\n\trv = rv << 8 | ptr[0];\n\treturn rv;\n}\n\nstatic\nDWORD ld_dword (const BYTE* ptr)\t/* Load a 4-byte little-endian word */\n{\n\tDWORD rv;\n\n\trv = ptr[3];\n\trv = rv << 8 | ptr[2];\n\trv = rv << 8 | ptr[1];\n\trv = rv << 8 | ptr[0];\n\treturn rv;\n}\n\n#if _FS_EXFAT\nstatic\nQWORD ld_qword (const BYTE* ptr)\t/* Load an 8-byte little-endian word */\n{\n\tQWORD rv;\n\n\trv = ptr[7];\n\trv = rv << 8 | ptr[6];\n\trv = rv << 8 | ptr[5];\n\trv = rv << 8 | ptr[4];\n\trv = rv << 8 | ptr[3];\n\trv = rv << 8 | ptr[2];\n\trv = rv << 8 | ptr[1];\n\trv = rv << 8 | ptr[0];\n\treturn rv;\n}\n#endif\n\n#if !_FS_READONLY\nstatic\nvoid st_word (BYTE* ptr, WORD val)\t/* Store a 2-byte word in little-endian */\n{\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val;\n}\n\nstatic\nvoid st_dword (BYTE* ptr, DWORD val)\t/* Store a 4-byte word in little-endian */\n{\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val;\n}\n\n#if _FS_EXFAT\nstatic\nvoid st_qword (BYTE* ptr, QWORD val)\t/* Store an 8-byte word in little-endian */\n{\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val; val >>= 8;\n\t*ptr++ = (BYTE)val;\n}\n#endif\n#endif\t/* !_FS_READONLY */\n\n\n\n/*-----------------------------------------------------------------------*/\n/* String functions                                                      */\n/*-----------------------------------------------------------------------*/\n\n/* Copy memory to memory */\nstatic\nvoid mem_cpy (void* dst, const void* src, UINT cnt) {\n\tBYTE *d = (BYTE*)dst;\n\tconst BYTE *s = (const BYTE*)src;\n\n\tif (cnt) {\n\t\tdo {\n\t\t\t*d++ = *s++;\n\t\t} while (--cnt);\n\t}\n}\n\n/* Fill memory block */\nstatic\nvoid mem_set (void* dst, int val, UINT cnt) {\n\tBYTE *d = (BYTE*)dst;\n\n\tdo {\n\t\t*d++ = (BYTE)val;\n\t} while (--cnt);\n}\n\n/* Compare memory block */\nstatic\nint mem_cmp (const void* dst, const void* src, UINT cnt) {\t/* ZR:same, NZ:different */\n\tconst BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;\n\tint r = 0;\n\n\tdo {\n\t\tr = *d++ - *s++;\n\t} while (--cnt && r == 0);\n\n\treturn r;\n}\n\n/* Check if chr is contained in the string */\nstatic\nint chk_chr (const char* str, int chr) {\t/* NZ:contained, ZR:not contained */\n\twhile (*str && *str != chr) str++;\n\treturn *str;\n}\n\n\n\n\n#if _FS_REENTRANT\n/*-----------------------------------------------------------------------*/\n/* Request/Release grant to access the volume                            */\n/*-----------------------------------------------------------------------*/\nstatic\nint lock_fs (\n\tFATFS* fs\t\t/* File system object */\n)\n{\n\treturn (fs && ff_req_grant(fs->sobj)) ? 1 : 0;\n}\n\n\nstatic\nvoid unlock_fs (\n\tFATFS* fs,\t\t/* File system object */\n\tFRESULT res\t\t/* Result code to be returned */\n)\n{\n\tif (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {\n\t\tff_rel_grant(fs->sobj);\n\t}\n}\n\n#endif\n\n\n\n#if _FS_LOCK != 0\n/*-----------------------------------------------------------------------*/\n/* File lock control functions                                           */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT chk_lock (\t/* Check if the file can be accessed */\n\tDIR* dp,\t\t/* Directory object pointing the file to be checked */\n\tint acc\t\t\t/* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */\n)\n{\n\tUINT i, be;\n\n\t/* Search file semaphore table */\n\tfor (i = be = 0; i < _FS_LOCK; i++) {\n\t\tif (Files[i].fs) {\t/* Existing entry */\n\t\t\tif (Files[i].fs == dp->obj.fs &&\t \t/* Check if the object matched with an open object */\n\t\t\t\tFiles[i].clu == dp->obj.sclust &&\n\t\t\t\tFiles[i].ofs == dp->dptr) break;\n\t\t} else {\t\t\t/* Blank entry */\n\t\t\tbe = 1;\n\t\t}\n\t}\n\tif (i == _FS_LOCK) {\t/* The object is not opened */\n\t\treturn (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;\t/* Is there a blank entry for new object? */\n\t}\n\n\t/* The object has been opened. Reject any open against writing file and all write mode open */\n\treturn (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;\n}\n\n\nstatic\nint enq_lock (void)\t/* Check if an entry is available for a new object */\n{\n\tUINT i;\n\n\tfor (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\n\treturn (i == _FS_LOCK) ? 0 : 1;\n}\n\n\nstatic\nUINT inc_lock (\t/* Increment object open counter and returns its index (0:Internal error) */\n\tDIR* dp,\t/* Directory object pointing the file to register or increment */\n\tint acc\t\t/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */\n)\n{\n\tUINT i;\n\n\n\tfor (i = 0; i < _FS_LOCK; i++) {\t/* Find the object */\n\t\tif (Files[i].fs == dp->obj.fs &&\n\t\t\tFiles[i].clu == dp->obj.sclust &&\n\t\t\tFiles[i].ofs == dp->dptr) break;\n\t}\n\n\tif (i == _FS_LOCK) {\t\t\t\t/* Not opened. Register it as new. */\n\t\tfor (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\n\t\tif (i == _FS_LOCK) return 0;\t/* No free entry to register (int err) */\n\t\tFiles[i].fs = dp->obj.fs;\n\t\tFiles[i].clu = dp->obj.sclust;\n\t\tFiles[i].ofs = dp->dptr;\n\t\tFiles[i].ctr = 0;\n\t}\n\n\tif (acc && Files[i].ctr) return 0;\t/* Access violation (int err) */\n\n\tFiles[i].ctr = acc ? 0x100 : Files[i].ctr + 1;\t/* Set semaphore value */\n\n\treturn i + 1;\n}\n\n\nstatic\nFRESULT dec_lock (\t/* Decrement object open counter */\n\tUINT i\t\t\t/* Semaphore index (1..) */\n)\n{\n\tWORD n;\n\tFRESULT res;\n\n\n\tif (--i < _FS_LOCK) {\t/* Shift index number origin from 0 */\n\t\tn = Files[i].ctr;\n\t\tif (n == 0x100) n = 0;\t\t/* If write mode open, delete the entry */\n\t\tif (n > 0) n--;\t\t\t\t/* Decrement read mode open count */\n\t\tFiles[i].ctr = n;\n\t\tif (n == 0) Files[i].fs = 0;\t/* Delete the entry if open count gets zero */\n\t\tres = FR_OK;\n\t} else {\n\t\tres = FR_INT_ERR;\t\t\t/* Invalid index nunber */\n\t}\n\treturn res;\n}\n\n\nstatic\nvoid clear_lock (\t/* Clear lock entries of the volume */\n\tFATFS *fs\n)\n{\n\tUINT i;\n\n\tfor (i = 0; i < _FS_LOCK; i++) {\n\t\tif (Files[i].fs == fs) Files[i].fs = 0;\n\t}\n}\n\n#endif\t/* _FS_LOCK != 0 */\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Move/Flush disk access window in the file system object               */\n/*-----------------------------------------------------------------------*/\n#if !_FS_READONLY\nstatic\nFRESULT sync_window (\t/* Returns FR_OK or FR_DISK_ERROR */\n\tFATFS* fs\t\t\t/* File system object */\n)\n{\n\tDWORD wsect;\n\tUINT nf;\n\tFRESULT res = FR_OK;\n\n\n\tif (fs->wflag) {\t/* Write back the sector if it is dirty */\n\t\twsect = fs->winsect;\t/* Current sector number */\n\t\tif (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) {\n\t\t\tres = FR_DISK_ERR;\n\t\t} else {\n\t\t\tfs->wflag = 0;\n\t\t\tif (wsect - fs->fatbase < fs->fsize) {\t\t/* Is it in the FAT area? */\n\t\t\t\tfor (nf = fs->n_fats; nf >= 2; nf--) {\t/* Reflect the change to all FAT copies */\n\t\t\t\t\twsect += fs->fsize;\n\t\t\t\t\tdisk_write(fs->drv, fs->win, wsect, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn res;\n}\n#endif\n\n\nstatic\nFRESULT move_window (\t/* Returns FR_OK or FR_DISK_ERROR */\n\tFATFS* fs,\t\t\t/* File system object */\n\tDWORD sector\t\t/* Sector number to make appearance in the fs->win[] */\n)\n{\n\tFRESULT res = FR_OK;\n\n\n\tif (sector != fs->winsect) {\t/* Window offset changed? */\n#if !_FS_READONLY\n\t\tres = sync_window(fs);\t\t/* Write-back changes */\n#endif\n\t\tif (res == FR_OK) {\t\t\t/* Fill sector window with new data */\n\t\t\tif (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) {\n\t\t\t\tsector = 0xFFFFFFFF;\t/* Invalidate window if data is not reliable */\n\t\t\t\tres = FR_DISK_ERR;\n\t\t\t}\n\t\t\tfs->winsect = sector;\n\t\t}\n\t}\n\treturn res;\n}\n\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Synchronize file system and strage device                             */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT sync_fs (\t/* FR_OK:succeeded, !=0:error */\n\tFATFS* fs\t\t/* File system object */\n)\n{\n\tFRESULT res;\n\n\n\tres = sync_window(fs);\n\tif (res == FR_OK) {\n\t\t/* Update FSInfo sector if needed */\n\t\tif (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {\n\t\t\t/* Create FSInfo structure */\n\t\t\tmem_set(fs->win, 0, SS(fs));\n\t\t\tst_word(fs->win + BS_55AA, 0xAA55);\n\t\t\tst_dword(fs->win + FSI_LeadSig, 0x41615252);\n\t\t\tst_dword(fs->win + FSI_StrucSig, 0x61417272);\n\t\t\tst_dword(fs->win + FSI_Free_Count, fs->free_clst);\n\t\t\tst_dword(fs->win + FSI_Nxt_Free, fs->last_clst);\n\t\t\t/* Write it into the FSInfo sector */\n\t\t\tfs->winsect = fs->volbase + 1;\n\t\t\tdisk_write(fs->drv, fs->win, fs->winsect, 1);\n\t\t\tfs->fsi_flag = 0;\n\t\t}\n\t\t/* Make sure that no pending write process in the physical drive */\n\t\tif (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;\n\t}\n\n\treturn res;\n}\n\n#endif\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Get sector# from cluster#                                             */\n/*-----------------------------------------------------------------------*/\n\nstatic\nDWORD clust2sect (\t/* !=0:Sector number, 0:Failed (invalid cluster#) */\n\tFATFS* fs,\t\t/* File system object */\n\tDWORD clst\t\t/* Cluster# to be converted */\n)\n{\n\tclst -= 2;\n\tif (clst >= fs->n_fatent - 2) return 0;\t\t/* Invalid cluster# */\n\treturn clst * fs->csize + fs->database;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* FAT access - Read value of a FAT entry                                */\n/*-----------------------------------------------------------------------*/\n\nstatic\nDWORD get_fat (\t/* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */\n\t_FDID* obj,\t/* Corresponding object */\n\tDWORD clst\t/* Cluster number to get the value */\n)\n{\n\tUINT wc, bc;\n\tDWORD val;\n\tFATFS *fs = obj->fs;\n\n\n\tif (clst < 2 || clst >= fs->n_fatent) {\t/* Check if in valid range */\n\t\tval = 1;\t/* Internal error */\n\n\t} else {\n\t\tval = 0xFFFFFFFF;\t/* Default value falls on disk error */\n\n\t\tswitch (fs->fs_type) {\n\t\tcase FS_FAT12 :\n\t\t\tbc = (UINT)clst; bc += bc / 2;\n\t\t\tif (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\n\t\t\twc = fs->win[bc++ % SS(fs)];\n\t\t\tif (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\n\t\t\twc |= fs->win[bc % SS(fs)] << 8;\n\t\t\tval = (clst & 1) ? (wc >> 4) : (wc & 0xFFF);\n\t\t\tbreak;\n\n\t\tcase FS_FAT16 :\n\t\t\tif (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;\n\t\t\tval = ld_word(fs->win + clst * 2 % SS(fs));\n\t\t\tbreak;\n\n\t\tcase FS_FAT32 :\n\t\t\tif (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;\n\t\t\tval = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF;\n\t\t\tbreak;\n#if _FS_EXFAT\n\t\tcase FS_EXFAT :\n\t\t\tif (obj->objsize) {\n\t\t\t\tDWORD cofs = clst - obj->sclust;\t/* Offset from start cluster */\n\t\t\t\tDWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize;\t/* Number of clusters - 1 */\n\n\t\t\t\tif (obj->stat == 2) {\t/* Is there no valid chain on the FAT? */\n\t\t\t\t\tif (cofs <= clen) {\n\t\t\t\t\t\tval = (cofs == clen) ? 0x7FFFFFFF : clst + 1;\t/* Generate the value */\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (obj->stat == 3 && cofs < obj->n_cont) {\t/* Is it in the 1st fragment? */\n\t\t\t\t\tval = clst + 1; \t/* Generate the value */\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (obj->stat != 2) {\t/* Get value from FAT if FAT chain is valid */\n\t\t\t\t\tif (obj->n_frag != 0) {\t/* Is it on the growing edge? */\n\t\t\t\t\t\tval = 0x7FFFFFFF;\t/* Generate EOC */\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;\n\t\t\t\t\t\tval = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t__attribute__ ((fallthrough));\n#endif\n\t\tdefault:\n\t\t\tval = 1;\t/* Internal error */\n\t\t}\n\t}\n\n\treturn val;\n}\n\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* FAT access - Change value of a FAT entry                              */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT put_fat (\t/* FR_OK(0):succeeded, !=0:error */\n\tFATFS* fs,\t\t/* Corresponding file system object */\n\tDWORD clst,\t\t/* FAT index number (cluster number) to be changed */\n\tDWORD val\t\t/* New value to be set to the entry */\n)\n{\n\tUINT bc;\n\tBYTE *p;\n\tFRESULT res = FR_INT_ERR;\n\n\tif (clst >= 2 && clst < fs->n_fatent) {\t/* Check if in valid range */\n\t\tswitch (fs->fs_type) {\n\t\tcase FS_FAT12 :\t/* Bitfield items */\n\t\t\tbc = (UINT)clst; bc += bc / 2;\n\t\t\tres = move_window(fs, fs->fatbase + (bc / SS(fs)));\n\t\t\tif (res != FR_OK) break;\n\t\t\tp = fs->win + bc++ % SS(fs);\n\t\t\t*p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;\n\t\t\tfs->wflag = 1;\n\t\t\tres = move_window(fs, fs->fatbase + (bc / SS(fs)));\n\t\t\tif (res != FR_OK) break;\n\t\t\tp = fs->win + bc % SS(fs);\n\t\t\t*p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));\n\t\t\tfs->wflag = 1;\n\t\t\tbreak;\n\n\t\tcase FS_FAT16 :\t/* WORD aligned items */\n\t\t\tres = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));\n\t\t\tif (res != FR_OK) break;\n\t\t\tst_word(fs->win + clst * 2 % SS(fs), (WORD)val);\n\t\t\tfs->wflag = 1;\n\t\t\tbreak;\n\n\t\tcase FS_FAT32 :\t/* DWORD aligned items */\n#if _FS_EXFAT\n\t\tcase FS_EXFAT :\n#endif\n\t\t\tres = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));\n\t\t\tif (res != FR_OK) break;\n\t\t\tif (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\n\t\t\t\tval = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000);\n\t\t\t}\n\t\t\tst_dword(fs->win + clst * 4 % SS(fs), val);\n\t\t\tfs->wflag = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn res;\n}\n\n#endif /* !_FS_READONLY */\n\n\n\n\n#if _FS_EXFAT && !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* exFAT: Accessing FAT and Allocation Bitmap                            */\n/*-----------------------------------------------------------------------*/\n\n/*--------------------------------------*/\n/* Find a contiguous free cluster block */\n/*--------------------------------------*/\n\nstatic\nDWORD find_bitmap (\t/* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */\n\tFATFS* fs,\t/* File system object */\n\tDWORD clst,\t/* Cluster number to scan from */\n\tDWORD ncl\t/* Number of contiguous clusters to find (1..) */\n)\n{\n\tBYTE bm, bv;\n\tUINT i;\n\tDWORD val, scl, ctr;\n\n\n\tclst -= 2;\t/* The first bit in the bitmap corresponds to cluster #2 */\n\tif (clst >= fs->n_fatent - 2) clst = 0;\n\tscl = val = clst; ctr = 0;\n\tfor (;;) {\n\t\tif (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;\t/* (assuming bitmap is located top of the cluster heap) */\n\t\ti = val / 8 % SS(fs); bm = 1 << (val % 8);\n\t\tdo {\n\t\t\tdo {\n\t\t\t\tbv = fs->win[i] & bm; bm <<= 1;\t\t/* Get bit value */\n\t\t\t\tif (++val >= fs->n_fatent - 2) {\t/* Next cluster (with wrap-around) */\n\t\t\t\t\tval = 0; bm = 0; i = SS(fs);\n\t\t\t\t}\n\t\t\t\tif (!bv) {\t/* Is it a free cluster? */\n\t\t\t\t\tif (++ctr == ncl) return scl + 2;\t/* Check if run length is sufficient for required */\n\t\t\t\t} else {\n\t\t\t\t\tscl = val; ctr = 0;\t\t/* Encountered a cluster in-use, restart to scan */\n\t\t\t\t}\n\t\t\t\tif (val == clst) return 0;\t/* All cluster scanned? */\n\t\t\t} while (bm);\n\t\t\tbm = 1;\n\t\t} while (++i < SS(fs));\n\t}\n}\n\n\n/*----------------------------------------*/\n/* Set/Clear a block of allocation bitmap */\n/*----------------------------------------*/\n\nstatic\nFRESULT change_bitmap (\n\tFATFS* fs,\t/* File system object */\n\tDWORD clst,\t/* Cluster number to change from */\n\tDWORD ncl,\t/* Number of clusters to be changed */\n\tint bv\t\t/* bit value to be set (0 or 1) */\n)\n{\n\tBYTE bm;\n\tUINT i;\n\tDWORD sect;\n\n\tclst -= 2;\t/* The first bit corresponds to cluster #2 */\n\tsect = fs->database + clst / 8 / SS(fs);\t/* Sector address (assuming bitmap is located top of the cluster heap) */\n\ti = clst / 8 % SS(fs);\t\t\t\t\t\t/* Byte offset in the sector */\n\tbm = 1 << (clst % 8);\t\t\t\t\t\t/* Bit mask in the byte */\n\tfor (;;) {\n\t\tif (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;\n\t\tdo {\n\t\t\tdo {\n\t\t\t\tif (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR;\t/* Is the bit expected value? */\n\t\t\t\tfs->win[i] ^= bm;\t/* Flip the bit */\n\t\t\t\tfs->wflag = 1;\n\t\t\t\tif (--ncl == 0) return FR_OK;\t/* All bits processed? */\n\t\t\t} while (bm <<= 1);\t\t/* Next bit */\n\t\t\tbm = 1;\n\t\t} while (++i < SS(fs));\t\t/* Next byte */\n\t\ti = 0;\n\t}\n}\n\n\n/*---------------------------------------------*/\n/* Fill the first fragment of the FAT chain    */\n/*---------------------------------------------*/\n\nstatic\nFRESULT fill_first_frag (\n\t_FDID* obj\t/* Pointer to the corresponding object */\n)\n{\n\tFRESULT res;\n\tDWORD cl, n;\n\n\tif (obj->stat == 3) {\t/* Has the object been changed 'fragmented'? */\n\t\tfor (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) {\t/* Create cluster chain on the FAT */\n\t\t\tres = put_fat(obj->fs, cl, cl + 1);\n\t\t\tif (res != FR_OK) return res;\n\t\t}\n\t\tobj->stat = 0;\t/* Change status 'FAT chain is valid' */\n\t}\n\treturn FR_OK;\n}\n\n\n/*---------------------------------------------*/\n/* Fill the last fragment of the FAT chain     */\n/*---------------------------------------------*/\n\nstatic\nFRESULT fill_last_frag (\n\t_FDID* obj,\t/* Pointer to the corresponding object */\n\tDWORD lcl,\t/* Last cluster of the fragment */\n\tDWORD term\t/* Value to set the last FAT entry */\n)\n{\n\tFRESULT res;\n\n\twhile (obj->n_frag > 0) {\t/* Create the last chain on the FAT */\n\t\tres = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);\n\t\tif (res != FR_OK) return res;\n\t\tobj->n_frag--;\n\t}\n\treturn FR_OK;\n}\n\n#endif\t/* _FS_EXFAT && !_FS_READONLY */\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* FAT handling - Remove a cluster chain                                 */\n/*-----------------------------------------------------------------------*/\nstatic\nFRESULT remove_chain (\t/* FR_OK(0):succeeded, !=0:error */\n\t_FDID* obj,\t\t\t/* Corresponding object */\n\tDWORD clst,\t\t\t/* Cluster to remove a chain from */\n\tDWORD pclst\t\t\t/* Previous cluster of clst (0:an entire chain) */\n)\n{\n\tFRESULT res = FR_OK;\n\tDWORD nxt;\n\tFATFS *fs = obj->fs;\n#if _FS_EXFAT || _USE_TRIM\n\tDWORD scl = clst, ecl = clst;\n#endif\n#if _USE_TRIM\n\tDWORD rt[2];\n#endif\n\n\tif (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;\t/* Check if in valid range */\n\n\t/* Mark the previous cluster 'EOC' on the FAT if it exists */\n\tif (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {\n\t\tres = put_fat(fs, pclst, 0xFFFFFFFF);\n\t\tif (res != FR_OK) return res;\n\t}\n\n\t/* Remove the chain */\n\tdo {\n\t\tnxt = get_fat(obj, clst);\t\t\t/* Get cluster status */\n\t\tif (nxt == 0) break;\t\t\t\t/* Empty cluster? */\n\t\tif (nxt == 1) return FR_INT_ERR;\t/* Internal error? */\n\t\tif (nxt == 0xFFFFFFFF) return FR_DISK_ERR;\t/* Disk error? */\n\t\tif (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\n\t\t\tres = put_fat(fs, clst, 0);\t\t/* Mark the cluster 'free' on the FAT */\n\t\t\tif (res != FR_OK) return res;\n\t\t}\n\t\tif (fs->free_clst < fs->n_fatent - 2) {\t/* Update FSINFO */\n\t\t\tfs->free_clst++;\n\t\t\tfs->fsi_flag |= 1;\n\t\t}\n#if _FS_EXFAT || _USE_TRIM\n\t\tif (ecl + 1 == nxt) {\t/* Is next cluster contiguous? */\n\t\t\tecl = nxt;\n\t\t} else {\t\t\t\t/* End of contiguous cluster block */\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\tres = change_bitmap(fs, scl, ecl - scl + 1, 0);\t/* Mark the cluster block 'free' on the bitmap */\n\t\t\t\tif (res != FR_OK) return res;\n\t\t\t}\n#endif\n#if _USE_TRIM\n\t\t\trt[0] = clust2sect(fs, scl);\t\t\t\t\t/* Start sector */\n\t\t\trt[1] = clust2sect(fs, ecl) + fs->csize - 1;\t/* End sector */\n\t\t\tdisk_ioctl(fs->drv, CTRL_TRIM, rt);\t\t\t\t/* Inform device the block can be erased */\n#endif\n\t\t\tscl = ecl = nxt;\n\t\t}\n#endif\n\t\tclst = nxt;\t\t\t\t\t/* Next cluster */\n\t} while (clst < fs->n_fatent);\t/* Repeat while not the last link */\n\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\n\t\tif (pclst == 0) {\t/* Does the object have no chain? */\n\t\t\tobj->stat = 0;\t\t/* Change the object status 'initial' */\n\t\t} else {\n\t\t\tif (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) {\t/* Did the chain get contiguous? */\n\t\t\t\tobj->stat = 2;\t/* Change the object status 'contiguous' */\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn FR_OK;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* FAT handling - Stretch a chain or Create a new chain                  */\n/*-----------------------------------------------------------------------*/\nstatic\nDWORD create_chain (\t/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */\n\t_FDID* obj,\t\t\t/* Corresponding object */\n\tDWORD clst\t\t\t/* Cluster# to stretch, 0:Create a new chain */\n)\n{\n\tDWORD cs, ncl, scl;\n\tFRESULT res;\n\tFATFS *fs = obj->fs;\n\n\n\tif (clst == 0) {\t/* Create a new chain */\n\t\tscl = fs->last_clst;\t\t\t\t/* Get suggested cluster to start from */\n\t\tif (scl == 0 || scl >= fs->n_fatent) scl = 1;\n\t}\n\telse {\t\t\t\t/* Stretch current chain */\n\t\tcs = get_fat(obj, clst);\t\t\t/* Check the cluster status */\n\t\tif (cs < 2) return 1;\t\t\t\t/* Invalid FAT value */\n\t\tif (cs == 0xFFFFFFFF) return cs;\t/* A disk error occurred */\n\t\tif (cs < fs->n_fatent) return cs;\t/* It is already followed by next cluster */\n\t\tscl = clst;\n\t}\n\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\tncl = find_bitmap(fs, scl, 1);\t\t\t\t/* Find a free cluster */\n\t\tif (ncl == 0 || ncl == 0xFFFFFFFF) return ncl;\t/* No free cluster or hard error? */\n\t\tres = change_bitmap(fs, ncl, 1, 1);\t\t\t/* Mark the cluster 'in use' */\n\t\tif (res == FR_INT_ERR) return 1;\n\t\tif (res == FR_DISK_ERR) return 0xFFFFFFFF;\n\t\tif (clst == 0) {\t\t\t\t\t\t\t/* Is it a new chain? */\n\t\t\tobj->stat = 2;\t\t\t\t\t\t\t/* Set status 'contiguous' */\n\t\t} else {\t\t\t\t\t\t\t\t\t/* It is a stretched chain */\n\t\t\tif (obj->stat == 2 && ncl != scl + 1) {\t/* Is the chain got fragmented? */\n\t\t\t\tobj->n_cont = scl - obj->sclust;\t/* Set size of the contiguous part */\n\t\t\t\tobj->stat = 3;\t\t\t\t\t\t/* Change status 'just fragmented' */\n\t\t\t}\n\t\t}\n\t\tif (obj->stat != 2) {\t/* Is the file non-contiguous? */\n\t\t\tif (ncl == clst + 1) {\t/* Is the cluster next to previous one? */\n\t\t\t\tobj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2;\t/* Increment size of last framgent */\n\t\t\t} else {\t\t\t\t/* New fragment */\n\t\t\t\tif (obj->n_frag == 0) obj->n_frag = 1;\n\t\t\t\tres = fill_last_frag(obj, clst, ncl);\t/* Fill last fragment on the FAT and link it to new one */\n\t\t\t\tif (res == FR_OK) obj->n_frag = 1;\n\t\t\t}\n\t\t}\n\t} else\n#endif\n\t{\t/* On the FAT12/16/32 volume */\n\t\tncl = scl;\t/* Start cluster */\n\t\tfor (;;) {\n\t\t\tncl++;\t\t\t\t\t\t\t/* Next cluster */\n\t\t\tif (ncl >= fs->n_fatent) {\t\t/* Check wrap-around */\n\t\t\t\tncl = 2;\n\t\t\t\tif (ncl > scl) return 0;\t/* No free cluster */\n\t\t\t}\n\t\t\tcs = get_fat(obj, ncl);\t\t\t/* Get the cluster status */\n\t\t\tif (cs == 0) break;\t\t\t\t/* Found a free cluster */\n\t\t\tif (cs == 1 || cs == 0xFFFFFFFF) return cs;\t/* An error occurred */\n\t\t\tif (ncl == scl) return 0;\t\t/* No free cluster */\n\t\t}\n\t\tres = put_fat(fs, ncl, 0xFFFFFFFF);\t/* Mark the new cluster 'EOC' */\n\t\tif (res == FR_OK && clst != 0) {\n\t\t\tres = put_fat(fs, clst, ncl);\t/* Link it from the previous one if needed */\n\t\t}\n\t}\n\n\tif (res == FR_OK) {\t\t\t/* Update FSINFO if function succeeded. */\n\t\tfs->last_clst = ncl;\n\t\tif (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;\n\t\tfs->fsi_flag |= 1;\n\t} else {\n\t\tncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;\t/* Failed. Generate error status */\n\t}\n\n\treturn ncl;\t\t/* Return new cluster number or error status */\n}\n\n#endif /* !_FS_READONLY */\n\n\n\n\n#if _USE_FASTSEEK\n/*-----------------------------------------------------------------------*/\n/* FAT handling - Convert offset into cluster with link map table        */\n/*-----------------------------------------------------------------------*/\n\nstatic\nDWORD clmt_clust (\t/* <2:Error, >=2:Cluster number */\n\tFIL* fp,\t\t/* Pointer to the file object */\n\tFSIZE_t ofs\t\t/* File offset to be converted to cluster# */\n)\n{\n\tDWORD cl, ncl, *tbl;\n\tFATFS *fs = fp->obj.fs;\n\n\n\ttbl = fp->cltbl + 1;\t/* Top of CLMT */\n\tcl = (DWORD)(ofs / SS(fs) / fs->csize);\t/* Cluster order from top of the file */\n\tfor (;;) {\n\t\tncl = *tbl++;\t\t\t/* Number of cluters in the fragment */\n\t\tif (ncl == 0) return 0;\t/* End of table? (error) */\n\t\tif (cl < ncl) break;\t/* In this fragment? */\n\t\tcl -= ncl; tbl++;\t\t/* Next fragment */\n\t}\n\treturn cl + *tbl;\t/* Return the cluster number */\n}\n\n#endif\t/* _USE_FASTSEEK */\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Directory handling - Set directory index                              */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_sdi (\t/* FR_OK(0):succeeded, !=0:error */\n\tDIR* dp,\t\t/* Pointer to directory object */\n\tDWORD ofs\t\t/* Offset of directory table */\n)\n{\n\tDWORD csz, clst;\n\tFATFS *fs = dp->obj.fs;\n\n\n\tif (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) {\t/* Check range of offset and alignment */\n\t\treturn FR_INT_ERR;\n\t}\n\tdp->dptr = ofs;\t\t\t\t/* Set current offset */\n\tclst = dp->obj.sclust;\t\t/* Table start cluster (0:root) */\n\tif (clst == 0 && fs->fs_type >= FS_FAT32) {\t/* Replace cluster# 0 with root cluster# */\n\t\tclst = fs->dirbase;\n\t\tif (_FS_EXFAT) dp->obj.stat = 0;\t/* exFAT: Root dir has an FAT chain */\n\t}\n\n\tif (clst == 0) {\t/* Static table (root-directory in FAT12/16) */\n\t\tif (ofs / SZDIRE >= fs->n_rootdir)\treturn FR_INT_ERR;\t/* Is index out of range? */\n\t\tdp->sect = fs->dirbase;\n\n\t} else {\t\t\t/* Dynamic table (sub-directory or root-directory in FAT32+) */\n\t\tcsz = (DWORD)fs->csize * SS(fs);\t/* Bytes per cluster */\n\t\twhile (ofs >= csz) {\t\t\t\t/* Follow cluster chain */\n\t\t\tclst = get_fat(&dp->obj, clst);\t\t\t\t/* Get next cluster */\n\t\t\tif (clst == 0xFFFFFFFF) return FR_DISK_ERR;\t/* Disk error */\n\t\t\tif (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;\t/* Reached to end of table or internal error */\n\t\t\tofs -= csz;\n\t\t}\n\t\tdp->sect = clust2sect(fs, clst);\n\t}\n\tdp->clust = clst;\t\t\t\t\t/* Current cluster# */\n\tif (!dp->sect) return FR_INT_ERR;\n\tdp->sect += ofs / SS(fs);\t\t\t/* Sector# of the directory entry */\n\tdp->dir = fs->win + (ofs % SS(fs));\t/* Pointer to the entry in the win[] */\n\n\treturn FR_OK;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Directory handling - Move directory table index next                  */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_next (\t/* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */\n\tDIR* dp,\t\t/* Pointer to the directory object */\n\tint stretch\t\t/* 0: Do not stretch table, 1: Stretch table if needed */\n)\n{\n\tDWORD ofs, clst;\n\tFATFS *fs = dp->obj.fs;\n#if !_FS_READONLY\n\tUINT n;\n#endif\n\n\tofs = dp->dptr + SZDIRE;\t/* Next entry */\n\tif (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE;\t/* Report EOT when offset has reached max value */\n\n\tif (ofs % SS(fs) == 0) {\t/* Sector changed? */\n\t\tdp->sect++;\t\t\t\t/* Next sector */\n\n\t\tif (!dp->clust) {\t\t/* Static table */\n\t\t\tif (ofs / SZDIRE >= fs->n_rootdir) {\t/* Report EOT if it reached end of static table */\n\t\t\t\tdp->sect = 0; return FR_NO_FILE;\n\t\t\t}\n\t\t}\n\t\telse {\t\t\t\t\t/* Dynamic table */\n\t\t\tif ((ofs / SS(fs) & (fs->csize - 1)) == 0) {\t\t/* Cluster changed? */\n\t\t\t\tclst = get_fat(&dp->obj, dp->clust);\t\t\t/* Get next cluster */\n\t\t\t\tif (clst <= 1) return FR_INT_ERR;\t\t\t\t/* Internal error */\n\t\t\t\tif (clst == 0xFFFFFFFF) return FR_DISK_ERR;\t\t/* Disk error */\n\t\t\t\tif (clst >= fs->n_fatent) {\t\t\t\t\t\t/* Reached end of dynamic table */\n#if !_FS_READONLY\n\t\t\t\t\tif (!stretch) {\t\t\t\t\t\t\t\t/* If no stretch, report EOT */\n\t\t\t\t\t\tdp->sect = 0; return FR_NO_FILE;\n\t\t\t\t\t}\n\t\t\t\t\tclst = create_chain(&dp->obj, dp->clust);\t/* Allocate a cluster */\n\t\t\t\t\tif (clst == 0) return FR_DENIED;\t\t\t/* No free cluster */\n\t\t\t\t\tif (clst == 1) return FR_INT_ERR;\t\t\t/* Internal error */\n\t\t\t\t\tif (clst == 0xFFFFFFFF) return FR_DISK_ERR;\t/* Disk error */\n\t\t\t\t\t/* Clean-up the stretched table */\n\t\t\t\t\tif (_FS_EXFAT) dp->obj.stat |= 4;\t\t\t/* The directory needs to be updated */\n\t\t\t\t\tif (sync_window(fs) != FR_OK) return FR_DISK_ERR;\t/* Flush disk access window */\n\t\t\t\t\tmem_set(fs->win, 0, SS(fs));\t\t\t\t/* Clear window buffer */\n\t\t\t\t\tfor (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) {\t/* Fill the new cluster with 0 */\n\t\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\t\tif (sync_window(fs) != FR_OK) return FR_DISK_ERR;\n\t\t\t\t\t}\n\t\t\t\t\tfs->winsect -= n;\t\t\t\t\t\t\t/* Restore window offset */\n#else\n\t\t\t\t\tif (!stretch) dp->sect = 0;\t\t\t\t\t/* (this line is to suppress compiler warning) */\n\t\t\t\t\tdp->sect = 0; return FR_NO_FILE;\t\t\t/* Report EOT */\n#endif\n\t\t\t\t}\n\t\t\t\tdp->clust = clst;\t\t/* Initialize data for new cluster */\n\t\t\t\tdp->sect = clust2sect(fs, clst);\n\t\t\t}\n\t\t}\n\t}\n\tdp->dptr = ofs;\t\t\t\t\t\t/* Current entry */\n\tdp->dir = fs->win + ofs % SS(fs);\t/* Pointer to the entry in the win[] */\n\n\treturn FR_OK;\n}\n\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Directory handling - Reserve a block of directory entries             */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_alloc (\t/* FR_OK(0):succeeded, !=0:error */\n\tDIR* dp,\t\t/* Pointer to the directory object */\n\tUINT nent\t\t/* Number of contiguous entries to allocate */\n)\n{\n\tFRESULT res;\n\tUINT n;\n\tFATFS *fs = dp->obj.fs;\n\n\n\tres = dir_sdi(dp, 0);\n\tif (res == FR_OK) {\n\t\tn = 0;\n\t\tdo {\n\t\t\tres = move_window(fs, dp->sect);\n\t\t\tif (res != FR_OK) break;\n#if _FS_EXFAT\n\t\t\tif ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {\n#else\n\t\t\tif (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {\n#endif\n\t\t\t\tif (++n == nent) break;\t/* A block of contiguous free entries is found */\n\t\t\t} else {\n\t\t\t\tn = 0;\t\t\t\t\t/* Not a blank entry. Restart to search */\n\t\t\t}\n\t\t\tres = dir_next(dp, 1);\n\t\t} while (res == FR_OK);\t/* Next entry with table stretch enabled */\n\t}\n\n\tif (res == FR_NO_FILE) res = FR_DENIED;\t/* No directory entry to allocate */\n\treturn res;\n}\n\n#endif\t/* !_FS_READONLY */\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* FAT: Directory handling - Load/Store start cluster number             */\n/*-----------------------------------------------------------------------*/\n\nstatic\nDWORD ld_clust (\t/* Returns the top cluster value of the SFN entry */\n\tFATFS* fs,\t\t/* Pointer to the fs object */\n\tconst BYTE* dir\t/* Pointer to the key entry */\n)\n{\n\tDWORD cl;\n\n\tcl = ld_word(dir + DIR_FstClusLO);\n\tif (fs->fs_type == FS_FAT32) {\n\t\tcl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;\n\t}\n\n\treturn cl;\n}\n\n\n#if !_FS_READONLY\nstatic\nvoid st_clust (\n\tFATFS* fs,\t/* Pointer to the fs object */\n\tBYTE* dir,\t/* Pointer to the key entry */\n\tDWORD cl\t/* Value to be set */\n)\n{\n\tst_word(dir + DIR_FstClusLO, (WORD)cl);\n\tif (fs->fs_type == FS_FAT32) {\n\t\tst_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));\n\t}\n}\n#endif\n\n\n\n#if _USE_LFN != 0\n/*------------------------------------------------------------------------*/\n/* FAT-LFN: LFN handling                                                  */\n/*------------------------------------------------------------------------*/\nstatic\nconst BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};\t/* Offset of LFN characters in the directory entry */\n\n\n/*--------------------------------------------------------*/\n/* FAT-LFN: Compare a part of file name with an LFN entry */\n/*--------------------------------------------------------*/\nstatic\nint cmp_lfn (\t\t\t\t/* 1:matched, 0:not matched */\n\tconst WCHAR* lfnbuf,\t/* Pointer to the LFN working buffer to be compared */\n\tBYTE* dir\t\t\t\t/* Pointer to the directory entry containing the part of LFN */\n)\n{\n\tUINT i, s;\n\tWCHAR wc, uc;\n\n\n\tif (ld_word(dir + LDIR_FstClusLO) != 0) return 0;\t/* Check LDIR_FstClusLO */\n\n\ti = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;\t/* Offset in the LFN buffer */\n\n\tfor (wc = 1, s = 0; s < 13; s++) {\t\t/* Process all characters in the entry */\n\t\tuc = ld_word(dir + LfnOfs[s]);\t\t/* Pick an LFN character */\n\t\tif (wc) {\n\t\t\tif (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) {\t/* Compare it */\n\t\t\t\treturn 0;\t\t\t\t\t/* Not matched */\n\t\t\t}\n\t\t\twc = uc;\n\t\t} else {\n\t\t\tif (uc != 0xFFFF) return 0;\t\t/* Check filler */\n\t\t}\n\t}\n\n\tif ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0;\t/* Last segment matched but different length */\n\n\treturn 1;\t\t/* The part of LFN matched */\n}\n\n\n#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT\n/*-----------------------------------------------------*/\n/* FAT-LFN: Pick a part of file name from an LFN entry */\n/*-----------------------------------------------------*/\nstatic\nint pick_lfn (\t\t\t/* 1:succeeded, 0:buffer overflow or invalid LFN entry */\n\tWCHAR* lfnbuf,\t\t/* Pointer to the LFN working buffer */\n\tBYTE* dir\t\t\t/* Pointer to the LFN entry */\n)\n{\n\tUINT i, s;\n\tWCHAR wc, uc;\n\n\n\tif (ld_word(dir + LDIR_FstClusLO) != 0) return 0;\t/* Check LDIR_FstClusLO is 0 */\n\n\ti = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13;\t/* Offset in the LFN buffer */\n\n\tfor (wc = 1, s = 0; s < 13; s++) {\t\t/* Process all characters in the entry */\n\t\tuc = ld_word(dir + LfnOfs[s]);\t\t/* Pick an LFN character */\n\t\tif (wc) {\n\t\t\tif (i >= _MAX_LFN) return 0;\t/* Buffer overflow? */\n\t\t\tlfnbuf[i++] = wc = uc;\t\t\t/* Store it */\n\t\t} else {\n\t\t\tif (uc != 0xFFFF) return 0;\t\t/* Check filler */\n\t\t}\n\t}\n\n\tif (dir[LDIR_Ord] & LLEF) {\t\t\t\t/* Put terminator if it is the last LFN part */\n\t\tif (i >= _MAX_LFN) return 0;\t\t/* Buffer overflow? */\n\t\tlfnbuf[i] = 0;\n\t}\n\n\treturn 1;\t\t/* The part of LFN is valid */\n}\n#endif\n\n\n#if !_FS_READONLY\n/*-----------------------------------------*/\n/* FAT-LFN: Create an entry of LFN entries */\n/*-----------------------------------------*/\nstatic\nvoid put_lfn (\n\tconst WCHAR* lfn,\t/* Pointer to the LFN */\n\tBYTE* dir,\t\t\t/* Pointer to the LFN entry to be created */\n\tBYTE ord,\t\t\t/* LFN order (1-20) */\n\tBYTE sum\t\t\t/* Checksum of the corresponding SFN */\n)\n{\n\tUINT i, s;\n\tWCHAR wc;\n\n\n\tdir[LDIR_Chksum] = sum;\t\t\t/* Set checksum */\n\tdir[LDIR_Attr] = AM_LFN;\t\t/* Set attribute. LFN entry */\n\tdir[LDIR_Type] = 0;\n\tst_word(dir + LDIR_FstClusLO, 0);\n\n\ti = (ord - 1) * 13;\t\t\t\t/* Get offset in the LFN working buffer */\n\ts = wc = 0;\n\tdo {\n\t\tif (wc != 0xFFFF) wc = lfn[i++];\t/* Get an effective character */\n\t\tst_word(dir + LfnOfs[s], wc);\t\t/* Put it */\n\t\tif (wc == 0) wc = 0xFFFF;\t\t/* Padding characters for left locations */\n\t} while (++s < 13);\n\tif (wc == 0xFFFF || !lfn[i]) ord |= LLEF;\t/* Last LFN part is the start of LFN sequence */\n\tdir[LDIR_Ord] = ord;\t\t\t/* Set the LFN order */\n}\n\n#endif\t/* !_FS_READONLY */\n#endif\t/* _USE_LFN != 0 */\n\n\n\n#if _USE_LFN != 0 && !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* FAT-LFN: Create a Numbered SFN                                        */\n/*-----------------------------------------------------------------------*/\n\nstatic\nvoid gen_numname (\n\tBYTE* dst,\t\t\t/* Pointer to the buffer to store numbered SFN */\n\tconst BYTE* src,\t/* Pointer to SFN */\n\tconst WCHAR* lfn,\t/* Pointer to LFN */\n\tUINT seq\t\t\t/* Sequence number */\n)\n{\n\tBYTE ns[8], c;\n\tUINT i, j;\n\tWCHAR wc;\n\tDWORD sr;\n\n\n\tmem_cpy(dst, src, 11);\n\n\tif (seq > 5) {\t/* In case of many collisions, generate a hash number instead of sequential number */\n\t\tsr = seq;\n\t\twhile (*lfn) {\t/* Create a CRC */\n\t\t\twc = *lfn++;\n\t\t\tfor (i = 0; i < 16; i++) {\n\t\t\t\tsr = (sr << 1) + (wc & 1);\n\t\t\t\twc >>= 1;\n\t\t\t\tif (sr & 0x10000) sr ^= 0x11021;\n\t\t\t}\n\t\t}\n\t\tseq = (UINT)sr;\n\t}\n\n\t/* itoa (hexdecimal) */\n\ti = 7;\n\tdo {\n\t\tc = (BYTE)((seq % 16) + '0');\n\t\tif (c > '9') c += 7;\n\t\tns[i--] = c;\n\t\tseq /= 16;\n\t} while (seq);\n\tns[i] = '~';\n\n\t/* Append the number */\n\tfor (j = 0; j < i && dst[j] != ' '; j++) {\n\t\tif (IsDBCS1(dst[j])) {\n\t\t\tif (j == i - 1) break;\n\t\t\tj++;\n\t\t}\n\t}\n\tdo {\n\t\tdst[j++] = (i < 8) ? ns[i++] : ' ';\n\t} while (j < 8);\n}\n#endif\t/* _USE_LFN != 0 && !_FS_READONLY */\n\n\n\n#if _USE_LFN != 0\n/*-----------------------------------------------------------------------*/\n/* FAT-LFN: Calculate checksum of an SFN entry                           */\n/*-----------------------------------------------------------------------*/\n\nstatic\nBYTE sum_sfn (\n\tconst BYTE* dir\t\t/* Pointer to the SFN entry */\n)\n{\n\tBYTE sum = 0;\n\tUINT n = 11;\n\n\tdo {\n\t\tsum = (sum >> 1) + (sum << 7) + *dir++;\n\t} while (--n);\n\treturn sum;\n}\n\n#endif\t/* _USE_LFN != 0 */\n\n\n\n#if _FS_EXFAT\n/*-----------------------------------------------------------------------*/\n/* exFAT: Checksum                                                       */\n/*-----------------------------------------------------------------------*/\n\nstatic\nWORD xdir_sum (\t\t\t/* Get checksum of the directoly block */\n\tconst BYTE* dir\t\t/* Directory entry block to be calculated */\n)\n{\n\tUINT i, szblk;\n\tWORD sum;\n\n\n\tszblk = (dir[XDIR_NumSec] + 1) * SZDIRE;\n\tfor (i = sum = 0; i < szblk; i++) {\n\t\tif (i == XDIR_SetSum) {\t/* Skip sum field */\n\t\t\ti++;\n\t\t} else {\n\t\t\tsum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];\n\t\t}\n\t}\n\treturn sum;\n}\n\n\n\nstatic\nWORD xname_sum (\t\t/* Get check sum (to be used as hash) of the name */\n\tconst WCHAR* name\t/* File name to be calculated */\n)\n{\n\tWCHAR chr;\n\tWORD sum = 0;\n\n\n\twhile ((chr = *name++) != 0) {\n\t\tchr = ff_wtoupper(chr);\t\t/* File name needs to be ignored case */\n\t\tsum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);\n\t\tsum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);\n\t}\n\treturn sum;\n}\n\n\n#if !_FS_READONLY && _USE_MKFS\nstatic\nDWORD xsum32 (\n\tBYTE  dat,\t/* Data to be sumed */\n\tDWORD sum\t/* Previous value */\n)\n{\n\tsum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;\n\treturn sum;\n}\n#endif\n\n\n#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2\n/*------------------------------------------------------*/\n/* exFAT: Get object information from a directory block */\n/*------------------------------------------------------*/\n\nstatic\nvoid get_xdir_info (\n\tBYTE* dirb,\t\t\t/* Pointer to the directory entry block 85+C0+C1s */\n\tFILINFO* fno\t\t/* Buffer to store the extracted file information */\n)\n{\n\tUINT di, si;\n\tWCHAR w;\n#if !_LFN_UNICODE\n\tUINT nc;\n#endif\n\n\t/* Get file name */\n\tdi = 0;\n#if _LFN_UNICODE\n\tfor (si = SZDIRE * 2; di < dirb[XDIR_NumName]; si += 2, di++) {\n\t\tif ((si % SZDIRE) == 0) si += 2;\t\t/* Skip entry type field */\n\t\tw = ld_word(dirb + si);\t\t\t\t\t/* Get a character */\n\t\tif (di >= _MAX_LFN) { di = 0; break; }\t/* Buffer overflow --> inaccessible object name */\n\t\tfno->fname[di] = w;\t\t\t\t\t\t/* Store it */\n\t}\n#else\n\tfor (si = SZDIRE * 2, nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) {\n\t\tif ((si % SZDIRE) == 0) si += 2;\t\t/* Skip entry type field */\n\t\tw = ff_convert(ld_word(dirb + si), 0);\t/* Get a character and Unicode -> OEM */\n\t\tif (_DF1S && w >= 0x100) {\t\t\t\t/* Is it a double byte char? (always false at SBCS cfg) */\n\t\t\tfno->fname[di++] = (char)(w >> 8);\t/* Put 1st byte of the DBC */\n\t\t}\n\t\tif (w == 0 || di >= _MAX_LFN) { di = 0; break; }\t/* Invalid char or buffer overflow --> inaccessible object name */\n\t\tfno->fname[di++] = (char)w;\n\t}\n#endif\n\tif (di == 0) fno->fname[di++] = '?';\t/* Inaccessible object name? */\n\tfno->fname[di] = 0;\t\t\t\t\t\t/* Terminate file name */\n\n\tfno->altname[0] = 0;\t\t\t\t\t\t\t/* No SFN */\n\tfno->fattrib = dirb[XDIR_Attr];\t\t\t\t\t/* Attribute */\n\tfno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize);\t/* Size */\n\tfno->ftime = ld_word(dirb + XDIR_ModTime + 0);\t/* Time */\n\tfno->fdate = ld_word(dirb + XDIR_ModTime + 2);\t/* Date */\n}\n\n#endif\t/* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */\n\n\n/*-----------------------------------*/\n/* exFAT: Get a directory entry block */\n/*-----------------------------------*/\n\nstatic\nFRESULT load_xdir (\t/* FR_INT_ERR: invalid entry block */\n\tDIR* dp\t\t\t/* Pointer to the reading directory object pointing the 85 entry */\n)\n{\n\tFRESULT res;\n\tUINT i, sz_ent;\n\tBYTE* dirb = dp->obj.fs->dirbuf;\t/* Pointer to the on-memory directory entry block 85+C0+C1s */\n\n\n\t/* Load 85 entry */\n\tres = move_window(dp->obj.fs, dp->sect);\n\tif (res != FR_OK) return res;\n\tif (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR;\n\tmem_cpy(dirb + 0, dp->dir, SZDIRE);\n\tsz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE;\n\tif (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR;\n\n\t/* Load C0 entry */\n\tres = dir_next(dp, 0);\n\tif (res != FR_OK) return res;\n\tres = move_window(dp->obj.fs, dp->sect);\n\tif (res != FR_OK) return res;\n\tif (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR;\n\tmem_cpy(dirb + SZDIRE, dp->dir, SZDIRE);\n\tif (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR;\n\n\t/* Load C1 entries */\n\ti = SZDIRE * 2;\t/* C1 offset */\n\tdo {\n\t\tres = dir_next(dp, 0);\n\t\tif (res != FR_OK) return res;\n\t\tres = move_window(dp->obj.fs, dp->sect);\n\t\tif (res != FR_OK) return res;\n\t\tif (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR;\n\t\tif (i < MAXDIRB(_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE);\n\t} while ((i += SZDIRE) < sz_ent);\n\n\t/* Sanity check (do it when accessible object name) */\n\tif (i <= MAXDIRB(_MAX_LFN)) {\n\t\tif (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;\n\t}\n\treturn FR_OK;\n}\n\n\n#if !_FS_READONLY || _FS_RPATH != 0\n/*------------------------------------------------*/\n/* exFAT: Load the object's directory entry block */\n/*------------------------------------------------*/\nstatic\nFRESULT load_obj_dir (\n\tDIR* dp,\t\t\t/* Blank directory object to be used to access containing directory */\n\tconst _FDID* obj\t/* Object with its containing directory information */\n)\n{\n\tFRESULT res;\n\n\t/* Open object containing directory */\n\tdp->obj.fs = obj->fs;\n\tdp->obj.sclust = obj->c_scl;\n\tdp->obj.stat = (BYTE)obj->c_size;\n\tdp->obj.objsize = obj->c_size & 0xFFFFFF00;\n\tdp->blk_ofs = obj->c_ofs;\n\n\tres = dir_sdi(dp, dp->blk_ofs);\t/* Goto object's entry block */\n\tif (res == FR_OK) {\n\t\tres = load_xdir(dp);\t\t/* Load the object's entry block */\n\t}\n\treturn res;\n}\n#endif\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------*/\n/* exFAT: Store the directory block to the media */\n/*-----------------------------------------------*/\nstatic\nFRESULT store_xdir (\n\tDIR* dp\t\t\t\t/* Pointer to the directory object */\n)\n{\n\tFRESULT res;\n\tUINT nent;\n\tBYTE* dirb = dp->obj.fs->dirbuf;\t/* Pointer to the directory entry block 85+C0+C1s */\n\n\t/* Create set sum */\n\tst_word(dirb + XDIR_SetSum, xdir_sum(dirb));\n\tnent = dirb[XDIR_NumSec] + 1;\n\n\t/* Store the set of directory to the volume */\n\tres = dir_sdi(dp, dp->blk_ofs);\n\twhile (res == FR_OK) {\n\t\tres = move_window(dp->obj.fs, dp->sect);\n\t\tif (res != FR_OK) break;\n\t\tmem_cpy(dp->dir, dirb, SZDIRE);\n\t\tdp->obj.fs->wflag = 1;\n\t\tif (--nent == 0) break;\n\t\tdirb += SZDIRE;\n\t\tres = dir_next(dp, 0);\n\t}\n\treturn (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;\n}\n\n\n\n/*-------------------------------------------*/\n/* exFAT: Create a new directory enrty block */\n/*-------------------------------------------*/\n\nstatic\nvoid create_xdir (\n\tBYTE* dirb,\t\t\t/* Pointer to the directory entry block buffer */\n\tconst WCHAR* lfn\t/* Pointer to the nul terminated file name */\n)\n{\n\tUINT i;\n\tBYTE nb, nc;\n\tWCHAR chr;\n\n\n\t/* Create 85+C0 entry */\n\tmem_set(dirb, 0, 2 * SZDIRE);\n\tdirb[XDIR_Type] = 0x85;\n\tdirb[XDIR_Type + SZDIRE] = 0xC0;\n\n\t/* Create C1 entries */\n\tnc = 0; nb = 1; chr = 1; i = SZDIRE * 2;\n\tdo {\n\t\tdirb[i++] = 0xC1; dirb[i++] = 0;\t/* Entry type C1 */\n\t\tdo {\t/* Fill name field */\n\t\t\tif (chr && (chr = lfn[nc]) != 0) nc++;\t/* Get a character if exist */\n\t\t\tst_word(dirb + i, chr); \t\t/* Store it */\n\t\t} while ((i += 2) % SZDIRE != 0);\n\t\tnb++;\n\t} while (lfn[nc]);\t/* Fill next entry if any char follows */\n\n\tdirb[XDIR_NumName] = nc;\t/* Set name length */\n\tdirb[XDIR_NumSec] = nb;\t\t/* Set block length */\n\tst_word(dirb + XDIR_NameHash, xname_sum(lfn));\t/* Set name hash */\n}\n\n#endif\t/* !_FS_READONLY */\n#endif\t/* _FS_EXFAT */\n\n\n\n#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT\n/*-----------------------------------------------------------------------*/\n/* Read an object from the directory                                     */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_read (\n\tDIR* dp,\t\t/* Pointer to the directory object */\n\tint vol\t\t\t/* Filtered by 0:file/directory or 1:volume label */\n)\n{\n\tFRESULT res = FR_NO_FILE;\n\tFATFS *fs = dp->obj.fs;\n\tBYTE a, c;\n#if _USE_LFN != 0\n\tBYTE ord = 0xFF, sum = 0xFF;\n#endif\n\n\twhile (dp->sect) {\n\t\tres = move_window(fs, dp->sect);\n\t\tif (res != FR_OK) break;\n\t\tc = dp->dir[DIR_Name];\t/* Test for the entry type */\n\t\tif (c == 0) {\n\t\t\tres = FR_NO_FILE; break; /* Reached to end of the directory */\n\t\t}\n#if _FS_EXFAT\n\t\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\t\tif (_USE_LABEL && vol) {\n\t\t\t\tif (c == 0x83) break;\t/* Volume label entry? */\n\t\t\t} else {\n\t\t\t\tif (c == 0x85) {\t\t/* Start of the file entry block? */\n\t\t\t\t\tdp->blk_ofs = dp->dptr;\t/* Get location of the block */\n\t\t\t\t\tres = load_xdir(dp);\t/* Load the entry block */\n\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\tdp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK;\t/* Get attribute */\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else\n#endif\n\t\t{\t/* On the FAT12/16/32 volume */\n\t\t\tdp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;\t/* Get attribute */\n#if _USE_LFN != 0\t/* LFN configuration */\n\t\t\tif (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) {\t/* An entry without valid data */\n\t\t\t\tord = 0xFF;\n\t\t\t} else {\n\t\t\t\tif (a == AM_LFN) {\t\t\t/* An LFN entry is found */\n\t\t\t\t\tif (c & LLEF) {\t\t\t/* Is it start of an LFN sequence? */\n\t\t\t\t\t\tsum = dp->dir[LDIR_Chksum];\n\t\t\t\t\t\tc &= (BYTE)~LLEF; ord = c;\n\t\t\t\t\t\tdp->blk_ofs = dp->dptr;\n\t\t\t\t\t}\n\t\t\t\t\t/* Check LFN validity and capture it */\n\t\t\t\t\tord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;\n\t\t\t\t} else {\t\t\t\t\t/* An SFN entry is found */\n\t\t\t\t\tif (ord || sum != sum_sfn(dp->dir)) {\t/* Is there a valid LFN? */\n\t\t\t\t\t\tdp->blk_ofs = 0xFFFFFFFF;\t\t\t/* It has no LFN. */\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#else\t\t/* Non LFN configuration */\n\t\t\tif (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) {\t/* Is it a valid entry? */\n\t\t\t\tbreak;\n\t\t\t}\n#endif\n\t\t}\n\t\tres = dir_next(dp, 0);\t\t/* Next entry */\n\t\tif (res != FR_OK) break;\n\t}\n\n\tif (res != FR_OK) dp->sect = 0;\t\t/* Terminate the read operation on error or EOT */\n\treturn res;\n}\n\n#endif\t/* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Directory handling - Find an object in the directory                  */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_find (\t/* FR_OK(0):succeeded, !=0:error */\n\tDIR* dp\t\t\t/* Pointer to the directory object with the file name */\n)\n{\n\tFRESULT res;\n\tFATFS *fs = dp->obj.fs;\n\tBYTE c;\n#if _USE_LFN != 0\n\tBYTE a, ord, sum;\n#endif\n\n\tres = dir_sdi(dp, 0);\t\t\t/* Rewind directory object */\n\tif (res != FR_OK) return res;\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\tBYTE nc;\n\t\tUINT di, ni;\n\t\tWORD hash = xname_sum(fs->lfnbuf);\t\t/* Hash value of the name to find */\n\n\t\twhile ((res = dir_read(dp, 0)) == FR_OK) {\t/* Read an item */\n#if _MAX_LFN < 255\n\t\t\tif (fs->dirbuf[XDIR_NumName] > _MAX_LFN) continue;\t\t\t/* Skip comparison if inaccessible object name */\n#endif\n\t\t\tif (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue;\t/* Skip comparison if hash mismatched */\n\t\t\tfor (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) {\t/* Compare the name */\n\t\t\t\tif ((di % SZDIRE) == 0) di += 2;\n\t\t\t\tif (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;\n\t\t\t}\n\t\t\tif (nc == 0 && !fs->lfnbuf[ni]) break;\t/* Name matched? */\n\t\t}\n\t\treturn res;\n\t}\n#endif\n\t/* On the FAT12/16/32 volume */\n#if _USE_LFN != 0\n\tord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF;\t/* Reset LFN sequence */\n#endif\n\tdo {\n\t\tres = move_window(fs, dp->sect);\n\t\tif (res != FR_OK) break;\n\t\tc = dp->dir[DIR_Name];\n\t\tif (c == 0) { res = FR_NO_FILE; break; }\t/* Reached to end of table */\n#if _USE_LFN != 0\t/* LFN configuration */\n\t\tdp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;\n\t\tif (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) {\t/* An entry without valid data */\n\t\t\tord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;\t/* Reset LFN sequence */\n\t\t} else {\n\t\t\tif (a == AM_LFN) {\t\t\t/* An LFN entry is found */\n\t\t\t\tif (!(dp->fn[NSFLAG] & NS_NOLFN)) {\n\t\t\t\t\tif (c & LLEF) {\t\t/* Is it start of LFN sequence? */\n\t\t\t\t\t\tsum = dp->dir[LDIR_Chksum];\n\t\t\t\t\t\tc &= (BYTE)~LLEF; ord = c;\t/* LFN start order */\n\t\t\t\t\t\tdp->blk_ofs = dp->dptr;\t/* Start offset of LFN */\n\t\t\t\t\t}\n\t\t\t\t\t/* Check validity of the LFN entry and compare it with given name */\n\t\t\t\t\tord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;\n\t\t\t\t}\n\t\t\t} else {\t\t\t\t\t/* An SFN entry is found */\n\t\t\t\tif (!ord && sum == sum_sfn(dp->dir)) break;\t/* LFN matched? */\n\t\t\t\tif (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break;\t/* SFN matched? */\n\t\t\t\tord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;\t/* Reset LFN sequence */\n\t\t\t}\n\t\t}\n#else\t\t/* Non LFN configuration */\n\t\tdp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;\n\t\tif (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break;\t/* Is it a valid entry? */\n#endif\n\t\tres = dir_next(dp, 0);\t/* Next entry */\n\t} while (res == FR_OK);\n\n\treturn res;\n}\n\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Register an object to the directory                                   */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_register (\t/* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */\n\tDIR* dp\t\t\t\t/* Target directory with object name to be created */\n)\n{\n\tFRESULT res;\n\tFATFS *fs = dp->obj.fs;\n#if _USE_LFN != 0\t/* LFN configuration */\n\tUINT n, nlen, nent;\n\tBYTE sn[12], sum;\n\n\n\tif (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME;\t/* Check name validity */\n\tfor (nlen = 0; fs->lfnbuf[nlen]; nlen++) ;\t/* Get lfn length */\n\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\tDIR dj;\n\n\t\tnent = (nlen + 14) / 15 + 2;\t/* Number of entries to allocate (85+C0+C1s) */\n\t\tres = dir_alloc(dp, nent);\t\t/* Allocate entries */\n\t\tif (res != FR_OK) return res;\n\t\tdp->blk_ofs = dp->dptr - SZDIRE * (nent - 1);\t/* Set the allocated entry block offset */\n\n\t\tif (dp->obj.sclust != 0 && (dp->obj.stat & 4)) {\t/* Has the sub-directory been stretched? */\n\t\t\tdp->obj.objsize += (DWORD)fs->csize * SS(fs);\t/* Increase the directory size by cluster size */\n\t\t\tres = fill_first_frag(&dp->obj);\t\t\t\t/* Fill first fragment on the FAT if needed */\n\t\t\tif (res != FR_OK) return res;\n\t\t\tres = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF);\t/* Fill last fragment on the FAT if needed */\n\t\t\tif (res != FR_OK) return res;\n\t\t\tres = load_obj_dir(&dj, &dp->obj);\t\t\t\t/* Load the object status */\n\t\t\tif (res != FR_OK) return res;\n\t\t\tst_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize);\t\t/* Update the allocation status */\n\t\t\tst_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);\n\t\t\tfs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;\n\t\t\tres = store_xdir(&dj);\t\t\t\t\t\t\t/* Store the object status */\n\t\t\tif (res != FR_OK) return res;\n\t\t}\n\n\t\tcreate_xdir(fs->dirbuf, fs->lfnbuf);\t/* Create on-memory directory block to be written later */\n\t\treturn FR_OK;\n\t}\n#endif\n\t/* On the FAT12/16/32 volume */\n\tmem_cpy(sn, dp->fn, 12);\n\tif (sn[NSFLAG] & NS_LOSS) {\t\t\t/* When LFN is out of 8.3 format, generate a numbered name */\n\t\tdp->fn[NSFLAG] = NS_NOLFN;\t\t/* Find only SFN */\n\t\tfor (n = 1; n < 100; n++) {\n\t\t\tgen_numname(dp->fn, sn, fs->lfnbuf, n);\t/* Generate a numbered name */\n\t\t\tres = dir_find(dp);\t\t\t\t/* Check if the name collides with existing SFN */\n\t\t\tif (res != FR_OK) break;\n\t\t}\n\t\tif (n == 100) return FR_DENIED;\t\t/* Abort if too many collisions */\n\t\tif (res != FR_NO_FILE) return res;\t/* Abort if the result is other than 'not collided' */\n\t\tdp->fn[NSFLAG] = sn[NSFLAG];\n\t}\n\n\t/* Create an SFN with/without LFNs. */\n\tnent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1;\t/* Number of entries to allocate */\n\tres = dir_alloc(dp, nent);\t\t/* Allocate entries */\n\tif (res == FR_OK && --nent) {\t/* Set LFN entry if needed */\n\t\tres = dir_sdi(dp, dp->dptr - nent * SZDIRE);\n\t\tif (res == FR_OK) {\n\t\t\tsum = sum_sfn(dp->fn);\t/* Checksum value of the SFN tied to the LFN */\n\t\t\tdo {\t\t\t\t\t/* Store LFN entries in bottom first */\n\t\t\t\tres = move_window(fs, dp->sect);\n\t\t\t\tif (res != FR_OK) break;\n\t\t\t\tput_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);\n\t\t\t\tfs->wflag = 1;\n\t\t\t\tres = dir_next(dp, 0);\t/* Next entry */\n\t\t\t} while (res == FR_OK && --nent);\n\t\t}\n\t}\n\n#else\t/* Non LFN configuration */\n\tres = dir_alloc(dp, 1);\t\t/* Allocate an entry for SFN */\n\n#endif\n\n\t/* Set SFN entry */\n\tif (res == FR_OK) {\n\t\tres = move_window(fs, dp->sect);\n\t\tif (res == FR_OK) {\n\t\t\tmem_set(dp->dir, 0, SZDIRE);\t/* Clean the entry */\n\t\t\tmem_cpy(dp->dir + DIR_Name, dp->fn, 11);\t/* Put SFN */\n#if _USE_LFN != 0\n\t\t\tdp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT);\t/* Put NT flag */\n#endif\n\t\t\tfs->wflag = 1;\n\t\t}\n\t}\n\n\treturn res;\n}\n\n#endif /* !_FS_READONLY */\n\n\n\n#if !_FS_READONLY && _FS_MINIMIZE == 0\n/*-----------------------------------------------------------------------*/\n/* Remove an object from the directory                                   */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT dir_remove (\t/* FR_OK:Succeeded, FR_DISK_ERR:A disk error */\n\tDIR* dp\t\t\t\t/* Directory object pointing the entry to be removed */\n)\n{\n\tFRESULT res;\n\tFATFS *fs = dp->obj.fs;\n#if _USE_LFN != 0\t/* LFN configuration */\n\tDWORD last = dp->dptr;\n\n\tres = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs);\t/* Goto top of the entry block if LFN is exist */\n\tif (res == FR_OK) {\n\t\tdo {\n\t\t\tres = move_window(fs, dp->sect);\n\t\t\tif (res != FR_OK) break;\n\t\t\t/* Mark an entry 'deleted' */\n\t\t\tif (_FS_EXFAT && fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\t\t\tdp->dir[XDIR_Type] &= 0x7F;\n\t\t\t} else {\t\t\t\t\t\t\t\t\t/* On the FAT12/16/32 volume */\n\t\t\t\tdp->dir[DIR_Name] = DDEM;\n\t\t\t}\n\t\t\tfs->wflag = 1;\n\t\t\tif (dp->dptr >= last) break;\t/* If reached last entry then all entries of the object has been deleted. */\n\t\t\tres = dir_next(dp, 0);\t/* Next entry */\n\t\t} while (res == FR_OK);\n\t\tif (res == FR_NO_FILE) res = FR_INT_ERR;\n\t}\n#else\t\t\t/* Non LFN configuration */\n\n\tres = move_window(fs, dp->sect);\n\tif (res == FR_OK) {\n\t\tdp->dir[DIR_Name] = DDEM;\n\t\tfs->wflag = 1;\n\t}\n#endif\n\n\treturn res;\n}\n\n#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */\n\n\n\n#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2\n/*-----------------------------------------------------------------------*/\n/* Get file information from directory entry                             */\n/*-----------------------------------------------------------------------*/\n\nstatic\nvoid get_fileinfo (\t\t/* No return code */\n\tDIR* dp,\t\t\t/* Pointer to the directory object */\n\tFILINFO* fno\t \t/* Pointer to the file information to be filled */\n)\n{\n\tUINT i, j;\n\tTCHAR c;\n\tDWORD tm;\n#if _USE_LFN != 0\n\tWCHAR w, lfv;\n\tFATFS *fs = dp->obj.fs;\n#endif\n\n\n\tfno->fname[0] = 0;\t\t/* Invaidate file info */\n\tif (!dp->sect) return;\t/* Exit if read pointer has reached end of directory */\n\n#if _USE_LFN != 0\t/* LFN configuration */\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\tget_xdir_info(fs->dirbuf, fno);\n\t\treturn;\n\t} else\n#endif\n\t{\t/* On the FAT12/16/32 volume */\n\t\tif (dp->blk_ofs != 0xFFFFFFFF) {\t/* Get LFN if available */\n\t\t\ti = j = 0;\n\t\t\twhile ((w = fs->lfnbuf[j++]) != 0) {\t/* Get an LFN character */\n#if !_LFN_UNICODE\n\t\t\t\tw = ff_convert(w, 0);\t\t/* Unicode -> OEM */\n\t\t\t\tif (w == 0) { i = 0; break; }\t/* No LFN if it could not be converted */\n\t\t\t\tif (_DF1S && w >= 0x100) {\t/* Put 1st byte if it is a DBC (always false at SBCS cfg) */\n\t\t\t\t\tfno->fname[i++] = (char)(w >> 8);\n\t\t\t\t}\n#endif\n\t\t\t\tif (i >= _MAX_LFN) { i = 0; break; }\t/* No LFN if buffer overflow */\n\t\t\t\tfno->fname[i++] = (TCHAR)w;\n\t\t\t}\n\t\t\tfno->fname[i] = 0;\t/* Terminate the LFN */\n\t\t}\n\t}\n\n\ti = j = 0;\n\tlfv = fno->fname[i];\t/* LFN is exist if non-zero */\n\twhile (i < 11) {\t\t/* Copy name body and extension */\n\t\tc = (TCHAR)dp->dir[i++];\n\t\tif (c == ' ') continue;\t\t\t\t/* Skip padding spaces */\n\t\tif (c == RDDEM) c = (TCHAR)DDEM;\t/* Restore replaced DDEM character */\n\t\tif (i == 9) {\t\t\t\t\t\t/* Insert a . if extension is exist */\n\t\t\tif (!lfv) fno->fname[j] = '.';\n\t\t\tfno->altname[j++] = '.';\n\t\t}\n#if _LFN_UNICODE\n\t\tif (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) {\n\t\t\tc = c << 8 | dp->dir[i++];\n\t\t}\n\t\tc = ff_convert(c, 1);\t/* OEM -> Unicode */\n\t\tif (!c) c = '?';\n#endif\n\t\tfno->altname[j] = c;\n\t\tif (!lfv) {\n\t\t\tif (IsUpper(c) && (dp->dir[DIR_NTres] & ((i >= 9) ? NS_EXT : NS_BODY))) {\n\t\t\t\tc += 0x20;\t\t\t/* To lower */\n\t\t\t}\n\t\t\tfno->fname[j] = c;\n\t\t}\n\t\tj++;\n\t}\n\tif (!lfv) {\n\t\tfno->fname[j] = 0;\n\t\tif (!dp->dir[DIR_NTres]) j = 0;\t/* Altname is no longer needed if neither LFN nor case info is exist. */\n\t}\n\tfno->altname[j] = 0;\t/* Terminate the SFN */\n\n#else\t/* Non-LFN configuration */\n\ti = j = 0;\n\twhile (i < 11) {\t\t/* Copy name body and extension */\n\t\tc = (TCHAR)dp->dir[i++];\n\t\tif (c == ' ') continue;\t\t\t\t/* Skip padding spaces */\n\t\tif (c == RDDEM) c = (TCHAR)DDEM;\t/* Restore replaced DDEM character */\n\t\tif (i == 9) fno->fname[j++] = '.';\t/* Insert a . if extension is exist */\n\t\tfno->fname[j++] = c;\n\t}\n\tfno->fname[j] = 0;\n#endif\n\n\tfno->fattrib = dp->dir[DIR_Attr];\t\t\t\t/* Attribute */\n\tfno->fsize = ld_dword(dp->dir + DIR_FileSize);\t/* Size */\n\ttm = ld_dword(dp->dir + DIR_ModTime);\t\t\t/* Timestamp */\n\tfno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16);\n}\n\n#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */\n\n\n\n#if _USE_FIND && _FS_MINIMIZE <= 1\n/*-----------------------------------------------------------------------*/\n/* Pattern matching                                                      */\n/*-----------------------------------------------------------------------*/\n\nstatic\nWCHAR get_achar (\t\t/* Get a character and advances ptr 1 or 2 */\n\tconst TCHAR** ptr\t/* Pointer to pointer to the SBCS/DBCS/Unicode string */\n)\n{\n#if !_LFN_UNICODE\n\tWCHAR chr;\n\n\tchr = (BYTE)*(*ptr)++;\t\t\t\t\t/* Get a byte */\n\tif (IsLower(chr)) chr -= 0x20;\t\t\t/* To upper ASCII char */\n#ifdef _EXCVT\n\tif (chr >= 0x80) chr = ExCvt[chr - 0x80];\t/* To upper SBCS extended char */\n#else\n\tif (IsDBCS1(chr) && IsDBCS2(**ptr)) {\t\t/* Get DBC 2nd byte if needed */\n\t\tchr = chr << 8 | (BYTE)*(*ptr)++;\n\t}\n#endif\n\treturn chr;\n#else\n\treturn ff_wtoupper(*(*ptr)++);\t\t\t/* Get a word and to upper */\n#endif\n}\n\n\nstatic\nint pattern_matching (\t/* 0:not matched, 1:matched */\n\tconst TCHAR* pat,\t/* Matching pattern */\n\tconst TCHAR* nam,\t/* String to be tested */\n\tint skip,\t\t\t/* Number of pre-skip chars (number of ?s) */\n\tint inf\t\t\t\t/* Infinite search (* specified) */\n)\n{\n\tconst TCHAR *pp, *np;\n\tWCHAR pc, nc;\n\tint nm, nx;\n\n\n\twhile (skip--) {\t\t\t\t/* Pre-skip name chars */\n\t\tif (!get_achar(&nam)) return 0;\t/* Branch mismatched if less name chars */\n\t}\n\tif (!*pat && inf) return 1;\t\t/* (short circuit) */\n\n\tdo {\n\t\tpp = pat; np = nam;\t\t\t/* Top of pattern and name to match */\n\t\tfor (;;) {\n\t\t\tif (*pp == '?' || *pp == '*') {\t/* Wildcard? */\n\t\t\t\tnm = nx = 0;\n\t\t\t\tdo {\t\t\t\t/* Analyze the wildcard chars */\n\t\t\t\t\tif (*pp++ == '?') nm++; else nx = 1;\n\t\t\t\t} while (*pp == '?' || *pp == '*');\n\t\t\t\tif (pattern_matching(pp, np, nm, nx)) return 1;\t/* Test new branch (recurs upto number of wildcard blocks in the pattern) */\n\t\t\t\tnc = *np; break;\t/* Branch mismatched */\n\t\t\t}\n\t\t\tpc = get_achar(&pp);\t/* Get a pattern char */\n\t\t\tnc = get_achar(&np);\t/* Get a name char */\n\t\t\tif (pc != nc) break;\t/* Branch mismatched? */\n\t\t\tif (pc == 0) return 1;\t/* Branch matched? (matched at end of both strings) */\n\t\t}\n\t\tget_achar(&nam);\t\t\t/* nam++ */\n\t} while (inf && nc);\t\t\t/* Retry until end of name if infinite search is specified */\n\n\treturn 0;\n}\n\n#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Pick a top segment and create the object name in directory form       */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT create_name (\t/* FR_OK: successful, FR_INVALID_NAME: could not create */\n\tDIR* dp,\t\t\t/* Pointer to the directory object */\n\tconst TCHAR** path\t/* Pointer to pointer to the segment in the path string */\n)\n{\n#if _USE_LFN != 0\t/* LFN configuration */\n\tBYTE b, cf;\n\tWCHAR w, *lfn;\n\tUINT i, ni, si, di;\n\tconst TCHAR *p;\n\n\t/* Create LFN in Unicode */\n\tp = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0;\n\tfor (;;) {\n\t\tw = p[si++];\t\t\t\t\t/* Get a character */\n\t\tif (w < ' ') break;\t\t\t\t/* Break if end of the path name */\n\t\tif (w == '/' || w == '\\\\') {\t/* Break if a separator is found */\n\t\t\twhile (p[si] == '/' || p[si] == '\\\\') si++;\t/* Skip duplicated separator if exist */\n\t\t\tbreak;\n\t\t}\n\t\tif (di >= _MAX_LFN) return FR_INVALID_NAME;\t/* Reject too long name */\n#if !_LFN_UNICODE\n\t\tw &= 0xFF;\n\t\tif (IsDBCS1(w)) {\t\t\t\t/* Check if it is a DBC 1st byte (always false on SBCS cfg) */\n\t\t\tb = (BYTE)p[si++];\t\t\t/* Get 2nd byte */\n\t\t\tw = (w << 8) + b;\t\t\t/* Create a DBC */\n\t\t\tif (!IsDBCS2(b)) return FR_INVALID_NAME;\t/* Reject invalid sequence */\n\t\t}\n\t\tw = ff_convert(w, 1);\t\t\t/* Convert ANSI/OEM to Unicode */\n\t\tif (!w) return FR_INVALID_NAME;\t/* Reject invalid code */\n#endif\n\t\tif (w < 0x80 && chk_chr(\"\\\"*:<>\\?|\\x7F\", w)) return FR_INVALID_NAME;\t/* Reject illegal characters for LFN */\n\t\tlfn[di++] = w;\t\t\t\t\t/* Store the Unicode character */\n\t}\n\t*path = &p[si];\t\t\t\t\t\t/* Return pointer to the next segment */\n\tcf = (w < ' ') ? NS_LAST : 0;\t\t/* Set last segment flag if end of the path */\n#if _FS_RPATH != 0\n\tif ((di == 1 && lfn[di - 1] == '.') ||\n\t\t(di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {\t/* Is this segment a dot name? */\n\t\tlfn[di] = 0;\n\t\tfor (i = 0; i < 11; i++)\t\t/* Create dot name for SFN entry */\n\t\t\tdp->fn[i] = (i < di) ? '.' : ' ';\n\t\tdp->fn[i] = cf | NS_DOT;\t\t/* This is a dot entry */\n\t\treturn FR_OK;\n\t}\n#endif\n\twhile (di) {\t\t\t\t\t\t/* Snip off trailing spaces and dots if exist */\n\t\tw = lfn[di - 1];\n\t\tif (w != ' ' && w != '.') break;\n\t\tdi--;\n\t}\n\tlfn[di] = 0;\t\t\t\t\t\t/* LFN is created */\n\tif (di == 0) return FR_INVALID_NAME;\t/* Reject nul name */\n\n\t/* Create SFN in directory form */\n\tmem_set(dp->fn, ' ', 11);\n\tfor (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;\t/* Strip leading spaces and dots */\n\tif (si) cf |= NS_LOSS | NS_LFN;\n\twhile (di && lfn[di - 1] != '.') di--;\t/* Find extension (di<=si: no extension) */\n\n\ti = b = 0; ni = 8;\n\tfor (;;) {\n\t\tw = lfn[si++];\t\t\t\t\t/* Get an LFN character */\n\t\tif (!w) break;\t\t\t\t\t/* Break on end of the LFN */\n\t\tif (w == ' ' || (w == '.' && si != di)) {\t/* Remove spaces and dots */\n\t\t\tcf |= NS_LOSS | NS_LFN; continue;\n\t\t}\n\n\t\tif (i >= ni || si == di) {\t\t/* Extension or end of SFN */\n\t\t\tif (ni == 11) {\t\t\t\t/* Long extension */\n\t\t\t\tcf |= NS_LOSS | NS_LFN; break;\n\t\t\t}\n\t\t\tif (si != di) cf |= NS_LOSS | NS_LFN;\t/* Out of 8.3 format */\n\t\t\tif (si > di) break;\t\t\t/* No extension */\n\t\t\tsi = di; i = 8; ni = 11;\t/* Enter extension section */\n\t\t\tb <<= 2; continue;\n\t\t}\n\n\t\tif (w >= 0x80) {\t\t\t\t/* Non ASCII character */\n#ifdef _EXCVT\n\t\t\tw = ff_convert(w, 0);\t\t/* Unicode -> OEM code */\n\t\t\tif (w) w = ExCvt[w - 0x80];\t/* Convert extended character to upper (SBCS) */\n#else\n\t\t\tw = ff_convert(ff_wtoupper(w), 0);\t/* Upper converted Unicode -> OEM code */\n#endif\n\t\t\tcf |= NS_LFN;\t\t\t\t/* Force create LFN entry */\n\t\t}\n\n\t\tif (_DF1S && w >= 0x100) {\t\t/* Is this DBC? (always false at SBCS cfg) */\n\t\t\tif (i >= ni - 1) {\n\t\t\t\tcf |= NS_LOSS | NS_LFN; i = ni; continue;\n\t\t\t}\n\t\t\tdp->fn[i++] = (BYTE)(w >> 8);\n\t\t} else {\t\t\t\t\t\t/* SBC */\n\t\t\tif (!w || chk_chr(\"+,;=[]\", w)) {\t/* Replace illegal characters for SFN */\n\t\t\t\tw = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */\n\t\t\t} else {\n\t\t\t\tif (IsUpper(w)) {\t\t/* ASCII large capital */\n\t\t\t\t\tb |= 2;\n\t\t\t\t} else {\n\t\t\t\t\tif (IsLower(w)) {\t/* ASCII small capital */\n\t\t\t\t\t\tb |= 1; w -= 0x20;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdp->fn[i++] = (BYTE)w;\n\t}\n\n\tif (dp->fn[0] == DDEM) dp->fn[0] = RDDEM;\t/* If the first character collides with DDEM, replace it with RDDEM */\n\n\tif (ni == 8) b <<= 2;\n\tif ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN;\t/* Create LFN entry when there are composite capitals */\n\tif (!(cf & NS_LFN)) {\t\t\t\t\t\t/* When LFN is in 8.3 format without extended character, NT flags are created */\n\t\tif ((b & 0x03) == 0x01) cf |= NS_EXT;\t/* NT flag (Extension has only small capital) */\n\t\tif ((b & 0x0C) == 0x04) cf |= NS_BODY;\t/* NT flag (Filename has only small capital) */\n\t}\n\n\tdp->fn[NSFLAG] = cf;\t/* SFN is created */\n\n\treturn FR_OK;\n\n\n#else\t/* _USE_LFN != 0 : Non-LFN configuration */\n\tBYTE c, d, *sfn;\n\tUINT ni, si, i;\n\tconst char *p;\n\n\t/* Create file name in directory form */\n\tp = *path; sfn = dp->fn;\n\tmem_set(sfn, ' ', 11);\n\tsi = i = 0; ni = 8;\n#if _FS_RPATH != 0\n\tif (p[si] == '.') { /* Is this a dot entry? */\n\t\tfor (;;) {\n\t\t\tc = (BYTE)p[si++];\n\t\t\tif (c != '.' || si >= 3) break;\n\t\t\tsfn[i++] = c;\n\t\t}\n\t\tif (c != '/' && c != '\\\\' && c > ' ') return FR_INVALID_NAME;\n\t\t*path = p + si;\t\t\t\t\t\t\t\t/* Return pointer to the next segment */\n\t\tsfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;\t/* Set last segment flag if end of the path */\n\t\treturn FR_OK;\n\t}\n#endif\n\tfor (;;) {\n\t\tc = (BYTE)p[si++];\n\t\tif (c <= ' ') break; \t\t\t/* Break if end of the path name */\n\t\tif (c == '/' || c == '\\\\') {\t/* Break if a separator is found */\n\t\t\twhile (p[si] == '/' || p[si] == '\\\\') si++;\t/* Skip duplicated separator if exist */\n\t\t\tbreak;\n\t\t}\n\t\tif (c == '.' || i >= ni) {\t\t/* End of body or over size? */\n\t\t\tif (ni == 11 || c != '.') return FR_INVALID_NAME;\t/* Over size or invalid dot */\n\t\t\ti = 8; ni = 11;\t\t\t\t/* Goto extension */\n\t\t\tcontinue;\n\t\t}\n\t\tif (c >= 0x80) {\t\t\t\t/* Extended character? */\n#ifdef _EXCVT\n\t\t\tc = ExCvt[c - 0x80];\t\t/* To upper extended characters (SBCS cfg) */\n#else\n#if !_DF1S\n\t\t\treturn FR_INVALID_NAME;\t\t/* Reject extended characters (ASCII only cfg) */\n#endif\n#endif\n\t\t}\n\t\tif (IsDBCS1(c)) {\t\t\t\t/* Check if it is a DBC 1st byte (always false at SBCS cfg.) */\n\t\t\td = (BYTE)p[si++];\t\t\t/* Get 2nd byte */\n\t\t\tif (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME;\t/* Reject invalid DBC */\n\t\t\tsfn[i++] = c;\n\t\t\tsfn[i++] = d;\n\t\t} else {\t\t\t\t\t\t/* SBC */\n\t\t\tif (chk_chr(\"\\\"*+,:;<=>\\?[]|\\x7F\", c)) return FR_INVALID_NAME;\t/* Reject illegal chrs for SFN */\n\t\t\tif (IsLower(c)) c -= 0x20;\t/* To upper */\n\t\t\tsfn[i++] = c;\n\t\t}\n\t}\n\t*path = p + si;\t\t\t\t\t\t/* Return pointer to the next segment */\n\tif (i == 0) return FR_INVALID_NAME;\t/* Reject nul string */\n\n\tif (sfn[0] == DDEM) sfn[0] = RDDEM;\t/* If the first character collides with DDEM, replace it with RDDEM */\n\tsfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0;\t\t/* Set last segment flag if end of the path */\n\n\treturn FR_OK;\n#endif /* _USE_LFN != 0 */\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Follow a file path                                                    */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT follow_path (\t/* FR_OK(0): successful, !=0: error code */\n\tDIR* dp,\t\t\t/* Directory object to return last directory and found object */\n\tconst TCHAR* path\t/* Full-path string to find a file or directory */\n)\n{\n\tFRESULT res;\n\tBYTE ns;\n\t_FDID *obj = &dp->obj;\n\tFATFS *fs = obj->fs;\n\n\n#if _FS_RPATH != 0\n\tif (*path != '/' && *path != '\\\\') {\t/* Without heading separator */\n\t\tobj->sclust = fs->cdir;\t\t\t\t/* Start from current directory */\n\t} else\n#endif\n\t{\t\t\t\t\t\t\t\t\t\t/* With heading separator */\n\t\twhile (*path == '/' || *path == '\\\\') path++;\t/* Strip heading separator */\n\t\tobj->sclust = 0;\t\t\t\t\t/* Start from root directory */\n\t}\n#if _FS_EXFAT\n\tobj->n_frag = 0;\t/* Invalidate last fragment counter of the object */\n#if _FS_RPATH != 0\n\tif (fs->fs_type == FS_EXFAT && obj->sclust) {\t/* Retrieve the sub-directory status if needed */\n\t\tDIR dj;\n\n\t\tobj->c_scl = fs->cdc_scl;\n\t\tobj->c_size = fs->cdc_size;\n\t\tobj->c_ofs = fs->cdc_ofs;\n\t\tres = load_obj_dir(&dj, obj);\n\t\tif (res != FR_OK) return res;\n\t\tobj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize);\n\t\tobj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t}\n#endif\n#endif\n\n\tif ((UINT)*path < ' ') {\t\t\t\t/* Null path name is the origin directory itself */\n\t\tdp->fn[NSFLAG] = NS_NONAME;\n\t\tres = dir_sdi(dp, 0);\n\n\t} else {\t\t\t\t\t\t\t\t/* Follow path */\n\t\tfor (;;) {\n\t\t\tres = create_name(dp, &path);\t/* Get a segment name of the path */\n\t\t\tif (res != FR_OK) break;\n\t\t\tres = dir_find(dp);\t\t\t\t/* Find an object with the segment name */\n\t\t\tns = dp->fn[NSFLAG];\n\t\t\tif (res != FR_OK) {\t\t\t\t/* Failed to find the object */\n\t\t\t\tif (res == FR_NO_FILE) {\t/* Object is not found */\n\t\t\t\t\tif (_FS_RPATH && (ns & NS_DOT)) {\t/* If dot entry is not exist, stay there */\n\t\t\t\t\t\tif (!(ns & NS_LAST)) continue;\t/* Continue to follow if not last segment */\n\t\t\t\t\t\tdp->fn[NSFLAG] = NS_NONAME;\n\t\t\t\t\t\tres = FR_OK;\n\t\t\t\t\t} else {\t\t\t\t\t\t\t/* Could not find the object */\n\t\t\t\t\t\tif (!(ns & NS_LAST)) res = FR_NO_PATH;\t/* Adjust error code if not last segment */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (ns & NS_LAST) break;\t\t\t/* Last segment matched. Function completed. */\n\t\t\t/* Get into the sub-directory */\n\t\t\tif (!(obj->attr & AM_DIR)) {\t\t/* It is not a sub-directory and cannot follow */\n\t\t\t\tres = FR_NO_PATH; break;\n\t\t\t}\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\t\t/* Save containing directory information for next dir */\n\t\t\t\tobj->c_scl = obj->sclust;\n\t\t\t\tobj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat;\n\t\t\t\tobj->c_ofs = dp->blk_ofs;\n\t\t\t\tobj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus);\t/* Open next directory */\n\t\t\t\tobj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t\t\t\tobj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\tobj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs));\t/* Open next directory */\n\t\t\t}\n\t\t}\n\t}\n\n\treturn res;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Get logical drive number from path name                               */\n/*-----------------------------------------------------------------------*/\n\nstatic\nint get_ldnumber (\t\t/* Returns logical drive number (-1:invalid drive) */\n\tconst TCHAR** path\t/* Pointer to pointer to the path name */\n)\n{\n\tconst TCHAR *tp, *tt;\n\tUINT i;\n\tint vol = -1;\n#if _STR_VOLUME_ID\t\t/* Find string drive id */\n\tstatic const char* const volid[] = {_VOLUME_STRS};\n\tconst char *sp;\n\tchar c;\n\tTCHAR tc;\n#endif\n\n\n\tif (*path) {\t/* If the pointer is not a null */\n\t\tfor (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;\t/* Find ':' in the path */\n\t\tif (*tt == ':') {\t/* If a ':' is exist in the path name */\n\t\t\ttp = *path;\n\t\t\ti = *tp++ - '0';\n\t\t\tif (i < 10 && tp == tt) {\t/* Is there a numeric drive id? */\n\t\t\t\tif (i < _VOLUMES) {\t/* If a drive id is found, get the value and strip it */\n\t\t\t\t\tvol = (int)i;\n\t\t\t\t\t*path = ++tt;\n\t\t\t\t}\n\t\t\t}\n#if _STR_VOLUME_ID\n\t\t\t else {\t/* No numeric drive number, find string drive id */\n\t\t\t\ti = 0; tt++;\n\t\t\t\tdo {\n\t\t\t\t\tsp = volid[i]; tp = *path;\n\t\t\t\t\tdo {\t/* Compare a string drive id with path name */\n\t\t\t\t\t\tc = *sp++; tc = *tp++;\n\t\t\t\t\t\tif (IsLower(tc)) tc -= 0x20;\n\t\t\t\t\t} while (c && (TCHAR)c == tc);\n\t\t\t\t} while ((c || tp != tt) && ++i < _VOLUMES);\t/* Repeat for each id until pattern match */\n\t\t\t\tif (i < _VOLUMES) {\t/* If a drive id is found, get the value and strip it */\n\t\t\t\t\tvol = (int)i;\n\t\t\t\t\t*path = tt;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\treturn vol;\n\t\t}\n#if _FS_RPATH != 0 && _VOLUMES >= 2\n\t\tvol = CurrVol;\t/* Current drive */\n#else\n\t\tvol = 0;\t\t/* Drive 0 */\n#endif\n\t}\n\treturn vol;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Load a sector and check if it is an FAT boot sector                   */\n/*-----------------------------------------------------------------------*/\n\nstatic\nBYTE check_fs (\t/* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */\n\tFATFS* fs,\t/* File system object */\n\tDWORD sect\t/* Sector# (lba) to load and check if it is an FAT-VBR or not */\n)\n{\n\tfs->wflag = 0; fs->winsect = 0xFFFFFFFF;\t\t/* Invaidate window */\n\tif (move_window(fs, sect) != FR_OK) return 4;\t/* Load boot record */\n\n\tif (ld_word(fs->win + BS_55AA) != 0xAA55) return 3;\t/* Check boot record signature (always placed here even if the sector size is >512) */\n\n\tif (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) {\n\t\tif ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0;\t/* Check \"FAT\" string */\n\t\tif (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0;\t\t\t/* Check \"FAT3\" string */\n\t}\n#if _FS_EXFAT\n\tif (!mem_cmp(fs->win + BS_JmpBoot, \"\\xEB\\x76\\x90\" \"EXFAT   \", 11)) return 1;\n#endif\n\treturn 2;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Find logical drive and check if the volume is mounted                 */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT find_volume (\t/* FR_OK(0): successful, !=0: any error occurred */\n\tconst TCHAR** path,\t/* Pointer to pointer to the path name (drive number) */\n\tFATFS** rfs,\t\t/* Pointer to pointer to the found file system object */\n\tBYTE mode\t\t\t/* !=0: Check write protection for write access */\n)\n{\n\tBYTE fmt, *pt;\n\tint vol;\n\tDSTATUS stat;\n\tDWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];\n\tWORD nrsv;\n\tFATFS *fs;\n\tUINT i;\n\n\n\t/* Get logical drive number */\n\t*rfs = 0;\n\tvol = get_ldnumber(path);\n\tif (vol < 0) return FR_INVALID_DRIVE;\n\n\t/* Check if the file system object is valid or not */\n\tfs = FatFs[vol];\t\t\t\t\t/* Get pointer to the file system object */\n\tif (!fs) return FR_NOT_ENABLED;\t\t/* Is the file system object available? */\n\n\tENTER_FF(fs);\t\t\t\t\t\t/* Lock the volume */\n\t*rfs = fs;\t\t\t\t\t\t\t/* Return pointer to the file system object */\n\n\tmode &= (BYTE)~FA_READ;\t\t\t\t/* Desired access mode, write access or not */\n\tif (fs->fs_type) {\t\t\t\t\t/* If the volume has been mounted */\n\t\tstat = disk_status(fs->drv);\n\t\tif (!(stat & STA_NOINIT)) {\t\t/* and the physical drive is kept initialized */\n\t\t\tif (!_FS_READONLY && mode && (stat & STA_PROTECT)) {\t/* Check write protection if needed */\n\t\t\t\treturn FR_WRITE_PROTECTED;\n\t\t\t}\n\t\t\treturn FR_OK;\t\t\t\t/* The file system object is valid */\n\t\t}\n\t}\n\n\t/* The file system object is not valid. */\n\t/* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */\n\n\tfs->fs_type = 0;\t\t\t\t\t/* Clear the file system object */\n\tfs->drv = LD2PD(vol);\t\t\t\t/* Bind the logical drive and a physical drive */\n\tstat = disk_initialize(fs->drv);\t/* Initialize the physical drive */\n\tif (stat & STA_NOINIT) { \t\t\t/* Check if the initialization succeeded */\n\t\treturn FR_NOT_READY;\t\t\t/* Failed to initialize due to no medium or hard error */\n\t}\n\tif (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */\n\t\treturn FR_WRITE_PROTECTED;\n\t}\n#if _MAX_SS != _MIN_SS\t\t\t\t\t/* Get sector size (multiple sector size cfg only) */\n\tif (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;\n\tif (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;\n#endif\n\n\t/* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */\n\tbsect = 0;\n\tfmt = check_fs(fs, bsect);\t\t\t/* Load sector 0 and check if it is an FAT-VBR as SFD */\n\tif (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) {\t/* Not an FAT-VBR or forced partition number */\n\t\tfor (i = 0; i < 4; i++) {\t\t/* Get partition offset */\n\t\t\tpt = fs->win + (MBR_Table + i * SZ_PTE);\n\t\t\tbr[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;\n\t\t}\n\t\ti = LD2PT(vol);\t\t\t\t\t/* Partition number: 0:auto, 1-4:forced */\n\t\tif (i) i--;\n\t\tdo {\t\t\t\t\t\t\t/* Find an FAT volume */\n\t\t\tbsect = br[i];\n\t\t\tfmt = bsect ? check_fs(fs, bsect) : 3;\t/* Check the partition */\n\t\t} while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);\n\t}\n\tif (fmt == 4) return FR_DISK_ERR;\t\t/* An error occured in the disk I/O layer */\n\tif (fmt >= 2) return FR_NO_FILESYSTEM;\t/* No FAT volume is found */\n\n\t/* An FAT volume is found (bsect). Following code initializes the file system object */\n\n#if _FS_EXFAT\n\tif (fmt == 1) {\n\t\tQWORD maxlba;\n\n\t\tfor (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ;\t/* Check zero filler */\n\t\tif (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;\n\n\t\tif (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM;\t/* Check exFAT revision (Must be 1.0) */\n\n\t\tif (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) {\t/* (BPB_BytsPerSecEx must be equal to the physical sector size) */\n\t\t\treturn FR_NO_FILESYSTEM;\n\t\t}\n\n\t\tmaxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect;\t/* Last LBA + 1 of the volume */\n\t\tif (maxlba >= 0x100000000) return FR_NO_FILESYSTEM;\t/* (It cannot be handled in 32-bit LBA) */\n\n\t\tfs->fsize = ld_dword(fs->win + BPB_FatSzEx);\t/* Number of sectors per FAT */\n\n\t\tfs->n_fats = fs->win[BPB_NumFATsEx];\t\t\t/* Number of FATs */\n\t\tif (fs->n_fats != 1) return FR_NO_FILESYSTEM;\t/* (Supports only 1 FAT) */\n\n\t\tfs->csize = 1 << fs->win[BPB_SecPerClusEx];\t\t/* Cluster size */\n\t\tif (fs->csize == 0)\treturn FR_NO_FILESYSTEM;\t/* (Must be 1..32768) */\n\n\t\tnclst = ld_dword(fs->win + BPB_NumClusEx);\t\t/* Number of clusters */\n\t\tif (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM;\t/* (Too many clusters) */\n\t\tfs->n_fatent = nclst + 2;\n\n\t\t/* Boundaries and Limits */\n\t\tfs->volbase = bsect;\n\t\tfs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);\n\t\tfs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);\n\t\tif (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM;\t/* (Volume size must not be smaller than the size requiered) */\n\t\tfs->dirbase = ld_dword(fs->win + BPB_RootClusEx);\n\n\t\t/* Check if bitmap location is in assumption (at the first cluster) */\n\t\tif (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR;\n\t\tfor (i = 0; i < SS(fs); i += SZDIRE) {\n\t\t\tif (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break;\t/* 81 entry with cluster #2? */\n\t\t}\n\t\tif (i == SS(fs)) return FR_NO_FILESYSTEM;\n#if !_FS_READONLY\n\t\tfs->last_clst = fs->free_clst = 0xFFFFFFFF;\t\t/* Initialize cluster allocation information */\n#endif\n\t\tfmt = FS_EXFAT;\t\t\t/* FAT sub-type */\n\t} else\n#endif\t/* _FS_EXFAT */\n\t{\n\t\tif (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM;\t/* (BPB_BytsPerSec must be equal to the physical sector size) */\n\n\t\tfasize = ld_word(fs->win + BPB_FATSz16);\t\t/* Number of sectors per FAT */\n\t\tif (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);\n\t\tfs->fsize = fasize;\n\n\t\tfs->n_fats = fs->win[BPB_NumFATs];\t\t\t\t/* Number of FATs */\n\t\tif (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM;\t/* (Must be 1 or 2) */\n\t\tfasize *= fs->n_fats;\t\t\t\t\t\t\t/* Number of sectors for FAT area */\n\n\t\tfs->csize = fs->win[BPB_SecPerClus];\t\t\t/* Cluster size */\n\t\tif (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM;\t/* (Must be power of 2) */\n\n\t\tfs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt);\t/* Number of root directory entries */\n\t\tif (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM;\t/* (Must be sector aligned) */\n\n\t\ttsect = ld_word(fs->win + BPB_TotSec16);\t\t/* Number of sectors on the volume */\n\t\tif (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);\n\n\t\tnrsv = ld_word(fs->win + BPB_RsvdSecCnt);\t\t/* Number of reserved sectors */\n\t\tif (nrsv == 0) return FR_NO_FILESYSTEM;\t\t\t/* (Must not be 0) */\n\n\t\t/* Determine the FAT sub type */\n\t\tsysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE);\t/* RSV + FAT + DIR */\n\t\tif (tsect < sysect) return FR_NO_FILESYSTEM;\t/* (Invalid volume size) */\n\t\tnclst = (tsect - sysect) / fs->csize;\t\t\t/* Number of clusters */\n\t\tif (nclst == 0) return FR_NO_FILESYSTEM;\t\t/* (Invalid volume size) */\n\t\tfmt = FS_FAT32;\n\t\tif (nclst <= MAX_FAT16) fmt = FS_FAT16;\n\t\tif (nclst <= MAX_FAT12) fmt = FS_FAT12;\n\n\t\t/* Boundaries and Limits */\n\t\tfs->n_fatent = nclst + 2;\t\t\t\t\t\t/* Number of FAT entries */\n\t\tfs->volbase = bsect;\t\t\t\t\t\t\t/* Volume start sector */\n\t\tfs->fatbase = bsect + nrsv; \t\t\t\t\t/* FAT start sector */\n\t\tfs->database = bsect + sysect;\t\t\t\t\t/* Data start sector */\n\t\tif (fmt == FS_FAT32) {\n\t\t\tif (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM;\t/* (Must be FAT32 revision 0.0) */\n\t\t\tif (fs->n_rootdir) return FR_NO_FILESYSTEM;\t/* (BPB_RootEntCnt must be 0) */\n\t\t\tfs->dirbase = ld_dword(fs->win + BPB_RootClus32);\t/* Root directory start cluster */\n\t\t\tszbfat = fs->n_fatent * 4;\t\t\t\t\t/* (Needed FAT size) */\n\t\t} else {\n\t\t\tif (fs->n_rootdir == 0)\treturn FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */\n\t\t\tfs->dirbase = fs->fatbase + fasize;\t\t\t/* Root directory start sector */\n\t\t\tszbfat = (fmt == FS_FAT16) ?\t\t\t\t/* (Needed FAT size) */\n\t\t\t\tfs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);\n\t\t}\n\t\tif (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM;\t/* (BPB_FATSz must not be less than the size needed) */\n\n#if !_FS_READONLY\n\t\t/* Get FSINFO if available */\n\t\tfs->last_clst = fs->free_clst = 0xFFFFFFFF;\t\t/* Initialize cluster allocation information */\n\t\tfs->fsi_flag = 0x80;\n#if (_FS_NOFSINFO & 3) != 3\n\t\tif (fmt == FS_FAT32\t\t\t\t/* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */\n\t\t\t&& ld_word(fs->win + BPB_FSInfo32) == 1\n\t\t\t&& move_window(fs, bsect + 1) == FR_OK)\n\t\t{\n\t\t\tfs->fsi_flag = 0;\n\t\t\tif (ld_word(fs->win + BS_55AA) == 0xAA55\t/* Load FSINFO data if available */\n\t\t\t\t&& ld_dword(fs->win + FSI_LeadSig) == 0x41615252\n\t\t\t\t&& ld_dword(fs->win + FSI_StrucSig) == 0x61417272)\n\t\t\t{\n#if (_FS_NOFSINFO & 1) == 0\n\t\t\t\tfs->free_clst = ld_dword(fs->win + FSI_Free_Count);\n#endif\n#if (_FS_NOFSINFO & 2) == 0\n\t\t\t\tfs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);\n#endif\n\t\t\t}\n\t\t}\n#endif\t/* (_FS_NOFSINFO & 3) != 3 */\n#endif\t/* !_FS_READONLY */\n\t}\n\n\tfs->fs_type = fmt;\t\t/* FAT sub-type */\n\tfs->id = ++Fsid;\t\t/* File system mount ID */\n#if _USE_LFN == 1\n\tfs->lfnbuf = LfnBuf;\t/* Static LFN working buffer */\n#if _FS_EXFAT\n\tfs->dirbuf = DirBuf;\t/* Static directory block scratchpad buuffer */\n#endif\n#endif\n#if _FS_RPATH != 0\n\tfs->cdir = 0;\t\t\t/* Initialize current directory */\n#endif\n#if _FS_LOCK != 0\t\t\t/* Clear file lock semaphores */\n\tclear_lock(fs);\n#endif\n\treturn FR_OK;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Check if the file/directory object is valid or not                    */\n/*-----------------------------------------------------------------------*/\n\nstatic\nFRESULT validate (\t/* Returns FR_OK or FR_INVALID_OBJECT */\n\t_FDID* obj,\t\t/* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */\n\tFATFS** fs\t\t/* Pointer to pointer to the owner file system object to return */\n)\n{\n\tFRESULT res = FR_INVALID_OBJECT;\n\n\n\tif (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) {\t/* Test if the object is valid */\n#if _FS_REENTRANT\n\t\tif (lock_fs(obj->fs)) {\t/* Obtain the filesystem object */\n\t\t\tif (!(disk_status(obj->fs->drv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */\n\t\t\t\tres = FR_OK;\n\t\t\t} else {\n\t\t\t\tunlock_fs(obj->fs, FR_OK);\n\t\t\t}\n\t\t} else {\n\t\t\tres = FR_TIMEOUT;\n\t\t}\n#else\n\t\tif (!(disk_status(obj->fs->drv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */\n\t\t\tres = FR_OK;\n\t\t}\n#endif\n\t}\n\t*fs = (res == FR_OK) ? obj->fs : 0;\t/* Corresponding filesystem object */\n\treturn res;\n}\n\n\n\n\n/*---------------------------------------------------------------------------\n\n   Public Functions (FatFs API)\n\n----------------------------------------------------------------------------*/\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Mount/Unmount a Logical Drive                                         */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_mount (\n\tFATFS* fs,\t\t\t/* Pointer to the file system object (NULL:unmount)*/\n\tconst TCHAR* path,\t/* Logical drive number to be mounted/unmounted */\n\tBYTE opt\t\t\t/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */\n)\n{\n\tFATFS *cfs;\n\tint vol;\n\tFRESULT res;\n\tconst TCHAR *rp = path;\n\n\n\t/* Get logical drive number */\n\tvol = get_ldnumber(&rp);\n\tif (vol < 0) return FR_INVALID_DRIVE;\n\tcfs = FatFs[vol];\t\t\t\t\t/* Pointer to fs object */\n\n\tif (cfs) {\n#if _FS_LOCK != 0\n\t\tclear_lock(cfs);\n#endif\n#if _FS_REENTRANT\t\t\t\t\t\t/* Discard sync object of the current volume */\n\t\tif (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;\n#endif\n\t\tcfs->fs_type = 0;\t\t\t\t/* Clear old fs object */\n\t}\n\n\tif (fs) {\n\t\tfs->fs_type = 0;\t\t\t\t/* Clear new fs object */\n#if _FS_REENTRANT\t\t\t\t\t\t/* Create sync object for the new volume */\n\t\tif (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;\n#endif\n\t}\n\tFatFs[vol] = fs;\t\t\t\t\t/* Register new fs object */\n\n\tif (!fs || opt != 1) return FR_OK;\t/* Do not mount now, it will be mounted later */\n\n\tres = find_volume(&path, &fs, 0);\t/* Force mounted the volume */\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Open or Create a File                                                 */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_open (\n\tFIL* fp,\t\t\t/* Pointer to the blank file object */\n\tconst TCHAR* path,\t/* Pointer to the file name */\n\tBYTE mode\t\t\t/* Access mode and file open mode flags */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n#if !_FS_READONLY\n\tDWORD dw, cl, bcs, clst, sc;\n\tFSIZE_t ofs;\n#endif\n\tDEF_NAMBUF\n\n\n\tif (!fp) return FR_INVALID_OBJECT;\n\n\t/* Get logical drive */\n\tmode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;\n\tres = find_volume(&path, &fs, mode);\n\tif (res == FR_OK) {\n\t\tdj.obj.fs = fs;\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t/* Follow the file path */\n#if !_FS_READONLY\t/* R/W configuration */\n\t\tif (res == FR_OK) {\n\t\t\tif (dj.fn[NSFLAG] & NS_NONAME) {\t/* Origin directory itself? */\n\t\t\t\tres = FR_INVALID_NAME;\n\t\t\t}\n#if _FS_LOCK != 0\n\t\t\telse {\n\t\t\t\tres = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\n\t\t\t}\n#endif\n\t\t}\n\t\t/* Create or Open a file */\n\t\tif (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {\n\t\t\tif (res != FR_OK) {\t\t\t\t\t/* No file, create new */\n\t\t\t\tif (res == FR_NO_FILE) {\t\t/* There is no file to open, create a new entry */\n#if _FS_LOCK != 0\n\t\t\t\t\tres = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;\n#else\n\t\t\t\t\tres = dir_register(&dj);\n#endif\n\t\t\t\t}\n\t\t\t\tmode |= FA_CREATE_ALWAYS;\t\t/* File is created */\n\t\t\t}\n\t\t\telse {\t\t\t\t\t\t\t\t/* Any object is already existing */\n\t\t\t\tif (dj.obj.attr & (AM_RDO | AM_DIR)) {\t/* Cannot overwrite it (R/O or DIR) */\n\t\t\t\t\tres = FR_DENIED;\n\t\t\t\t} else {\n\t\t\t\t\tif (mode & FA_CREATE_NEW) res = FR_EXIST;\t/* Cannot create as new file */\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {\t/* Truncate it if overwrite mode */\n\t\t\t\tdw = GET_FATTIME();\n#if _FS_EXFAT\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\t/* Get current allocation info */\n\t\t\t\t\tfp->obj.fs = fs;\n\t\t\t\t\tfp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);\n\t\t\t\t\tfp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\n\t\t\t\t\tfp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t\t\t\t\tfp->obj.n_frag = 0;\n\t\t\t\t\t/* Initialize directory entry block */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_CrtTime, dw);\t/* Set created time */\n\t\t\t\t\tfs->dirbuf[XDIR_CrtTime10] = 0;\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_ModTime, dw);\t/* Set modified time */\n\t\t\t\t\tfs->dirbuf[XDIR_ModTime10] = 0;\n\t\t\t\t\tfs->dirbuf[XDIR_Attr] = AM_ARC;\t\t\t\t/* Reset attribute */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_FstClus, 0);\t\t/* Reset file allocation info */\n\t\t\t\t\tst_qword(fs->dirbuf + XDIR_FileSize, 0);\n\t\t\t\t\tst_qword(fs->dirbuf + XDIR_ValidFileSize, 0);\n\t\t\t\t\tfs->dirbuf[XDIR_GenFlags] = 1;\n\t\t\t\t\tres = store_xdir(&dj);\n\t\t\t\t\tif (res == FR_OK && fp->obj.sclust) {\t\t/* Remove the cluster chain if exist */\n\t\t\t\t\t\tres = remove_chain(&fp->obj, fp->obj.sclust, 0);\n\t\t\t\t\t\tfs->last_clst = fp->obj.sclust - 1;\t\t/* Reuse the cluster hole */\n\t\t\t\t\t}\n\t\t\t\t} else\n#endif\n\t\t\t\t{\n\t\t\t\t\t/* Clean directory info */\n\t\t\t\t\tst_dword(dj.dir + DIR_CrtTime, dw);\t/* Set created time */\n\t\t\t\t\tst_dword(dj.dir + DIR_ModTime, dw);\t/* Set modified time */\n\t\t\t\t\tdj.dir[DIR_Attr] = AM_ARC;\t\t\t/* Reset attribute */\n\t\t\t\t\tcl = ld_clust(fs, dj.dir);\t\t\t/* Get cluster chain */\n\t\t\t\t\tst_clust(fs, dj.dir, 0);\t\t\t/* Reset file allocation info */\n\t\t\t\t\tst_dword(dj.dir + DIR_FileSize, 0);\n\t\t\t\t\tfs->wflag = 1;\n\n\t\t\t\t\tif (cl) {\t\t\t\t\t\t\t/* Remove the cluster chain if exist */\n\t\t\t\t\t\tdw = fs->winsect;\n\t\t\t\t\t\tres = remove_chain(&dj.obj, cl, 0);\n\t\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\t\tres = move_window(fs, dw);\n\t\t\t\t\t\t\tfs->last_clst = cl - 1;\t\t/* Reuse the cluster hole */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\t/* Open an existing file */\n\t\t\tif (res == FR_OK) {\t\t\t\t\t/* Following succeeded */\n\t\t\t\tif (dj.obj.attr & AM_DIR) {\t\t/* It is a directory */\n\t\t\t\t\tres = FR_NO_FILE;\n\t\t\t\t} else {\n\t\t\t\t\tif ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */\n\t\t\t\t\t\tres = FR_DENIED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (res == FR_OK) {\n\t\t\tif (mode & FA_CREATE_ALWAYS)\t\t/* Set file change flag if created or overwritten */\n\t\t\t\tmode |= FA_MODIFIED;\n\t\t\tfp->dir_sect = fs->winsect;\t\t\t/* Pointer to the directory entry */\n\t\t\tfp->dir_ptr = dj.dir;\n#if _FS_LOCK != 0\n\t\t\tfp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\n\t\t\tif (!fp->obj.lockid) res = FR_INT_ERR;\n#endif\n\t\t}\n#else\t\t/* R/O configuration */\n\t\tif (res == FR_OK) {\n\t\t\tif (dj.fn[NSFLAG] & NS_NONAME) {\t/* Origin directory itself? */\n\t\t\t\tres = FR_INVALID_NAME;\n\t\t\t} else {\n\t\t\t\tif (dj.obj.attr & AM_DIR) {\t\t/* It is a directory */\n\t\t\t\t\tres = FR_NO_FILE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\tif (res == FR_OK) {\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\tfp->obj.c_scl = dj.obj.sclust;\t\t\t\t\t\t\t/* Get containing directory info */\n\t\t\t\tfp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;\n\t\t\t\tfp->obj.c_ofs = dj.blk_ofs;\n\t\t\t\tfp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);\t/* Get object allocation info */\n\t\t\t\tfp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\n\t\t\t\tfp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\tfp->obj.sclust = ld_clust(fs, dj.dir);\t\t\t\t\t/* Get object allocation info */\n\t\t\t\tfp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);\n\t\t\t}\n#if _USE_FASTSEEK\n\t\t\tfp->cltbl = 0;\t\t\t/* Disable fast seek mode */\n#endif\n\t\t\tfp->obj.fs = fs;\t \t/* Validate the file object */\n\t\t\tfp->obj.id = fs->id;\n\t\t\tfp->flag = mode;\t\t/* Set file access mode */\n\t\t\tfp->err = 0;\t\t\t/* Clear error flag */\n\t\t\tfp->sect = 0;\t\t\t/* Invalidate current data sector */\n\t\t\tfp->fptr = 0;\t\t\t/* Set file pointer top of the file */\n#if !_FS_READONLY\n#if !_FS_TINY\n\t\t\tmem_set(fp->buf, 0, _MAX_SS);\t/* Clear sector buffer */\n#endif\n\t\t\tif ((mode & FA_SEEKEND) && fp->obj.objsize > 0) {\t/* Seek to end of file if FA_OPEN_APPEND is specified */\n\t\t\t\tfp->fptr = fp->obj.objsize;\t\t\t/* Offset to seek */\n\t\t\t\tbcs = (DWORD)fs->csize * SS(fs);\t/* Cluster size in byte */\n\t\t\t\tclst = fp->obj.sclust;\t\t\t\t/* Follow the cluster chain */\n\t\t\t\tfor (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {\n\t\t\t\t\tclst = get_fat(&fp->obj, clst);\n\t\t\t\t\tif (clst <= 1) res = FR_INT_ERR;\n\t\t\t\t\tif (clst == 0xFFFFFFFF) res = FR_DISK_ERR;\n\t\t\t\t}\n\t\t\t\tfp->clust = clst;\n\t\t\t\tif (res == FR_OK && ofs % SS(fs)) {\t/* Fill sector buffer if not on the sector boundary */\n\t\t\t\t\tif ((sc = clust2sect(fs, clst)) == 0) {\n\t\t\t\t\t\tres = FR_INT_ERR;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfp->sect = sc + (DWORD)(ofs / SS(fs));\n#if !_FS_TINY\n\t\t\t\t\t\tif (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t}\n\n\t\tFREE_NAMBUF();\n\t}\n\n\tif (res != FR_OK) fp->obj.fs = 0;\t/* Invalidate file object on error */\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Read File                                                             */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_read (\n\tFIL* fp, \t/* Pointer to the file object */\n\tvoid* buff,\t/* Pointer to data buffer */\n\tUINT btr,\t/* Number of bytes to read */\n\tUINT* br\t/* Pointer to number of bytes read */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD clst, sect;\n\tFSIZE_t remain;\n\tUINT rcnt, cc, csect;\n\tBYTE *rbuff = (BYTE*)buff;\n\n\n\t*br = 0;\t/* Clear read byte counter */\n\tres = validate(&fp->obj, &fs);\t\t\t\t/* Check validity of the file object */\n\tif (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);\t/* Check validity */\n\tif (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */\n\tremain = fp->obj.objsize - fp->fptr;\n\tif (btr > remain) btr = (UINT)remain;\t\t/* Truncate btr by remaining bytes */\n\n\tfor ( ;  btr;\t\t\t\t\t\t\t\t/* Repeat until all data read */\n\t\trbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {\n\t\tif (fp->fptr % SS(fs) == 0) {\t\t\t/* On the sector boundary? */\n\t\t\tcsect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));\t/* Sector offset in the cluster */\n\t\t\tif (csect == 0) {\t\t\t\t\t/* On the cluster boundary? */\n\t\t\t\tif (fp->fptr == 0) {\t\t\t/* On the top of the file? */\n\t\t\t\t\tclst = fp->obj.sclust;\t\t/* Follow cluster chain from the origin */\n\t\t\t\t} else {\t\t\t\t\t\t/* Middle or end of the file */\n#if _USE_FASTSEEK\n\t\t\t\t\tif (fp->cltbl) {\n\t\t\t\t\t\tclst = clmt_clust(fp, fp->fptr);\t/* Get cluster# from the CLMT */\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tclst = get_fat(&fp->obj, fp->clust);\t/* Follow cluster chain on the FAT */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (clst < 2) ABORT(fs, FR_INT_ERR);\n\t\t\t\tif (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->clust = clst;\t\t\t\t/* Update current cluster */\n\t\t\t}\n\t\t\tsect = clust2sect(fs, fp->clust);\t/* Get current sector */\n\t\t\tif (!sect) ABORT(fs, FR_INT_ERR);\n\t\t\tsect += csect;\n\t\t\tcc = btr / SS(fs);\t\t\t\t\t/* When remaining bytes >= sector size, */\n\t\t\tif (cc) {\t\t\t\t\t\t\t/* Read maximum contiguous sectors directly */\n\t\t\t\tif (csect + cc > fs->csize) {\t/* Clip at cluster boundary */\n\t\t\t\t\tcc = fs->csize - csect;\n\t\t\t\t}\n\t\t\t\tif (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);\n#if !_FS_READONLY && _FS_MINIMIZE <= 2\t\t\t/* Replace one of the read sectors with cached data if it contains a dirty sector */\n#if _FS_TINY\n\t\t\t\tif (fs->wflag && fs->winsect - sect < cc) {\n\t\t\t\t\tmem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));\n\t\t\t\t}\n#else\n\t\t\t\tif ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {\n\t\t\t\t\tmem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));\n\t\t\t\t}\n#endif\n#endif\n\t\t\t\trcnt = SS(fs) * cc;\t\t\t\t/* Number of bytes transferred */\n\t\t\t\tcontinue;\n\t\t\t}\n#if !_FS_TINY\n\t\t\tif (fp->sect != sect) {\t\t\t/* Load data sector if not in cache */\n#if !_FS_READONLY\n\t\t\t\tif (fp->flag & FA_DIRTY) {\t\t/* Write-back dirty sector cache */\n\t\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t\t}\n#endif\n\t\t\t\tif (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK)\tABORT(fs, FR_DISK_ERR);\t/* Fill sector cache */\n\t\t\t}\n#endif\n\t\t\tfp->sect = sect;\n\t\t}\n\t\trcnt = SS(fs) - (UINT)fp->fptr % SS(fs);\t/* Number of bytes left in the sector */\n\t\tif (rcnt > btr) rcnt = btr;\t\t\t\t\t/* Clip it by btr if needed */\n#if _FS_TINY\n\t\tif (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR);\t/* Move sector window */\n\t\tmem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt);\t/* Extract partial sector */\n#else\n\t\tmem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt);\t/* Extract partial sector */\n#endif\n\t}\n\n\tLEAVE_FF(fs, FR_OK);\n}\n\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Write File                                                            */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_write (\n\tFIL* fp,\t\t\t/* Pointer to the file object */\n\tconst void* buff,\t/* Pointer to the data to be written */\n\tUINT btw,\t\t\t/* Number of bytes to write */\n\tUINT* bw\t\t\t/* Pointer to number of bytes written */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD clst, sect;\n\tUINT wcnt, cc, csect;\n\tconst BYTE *wbuff = (const BYTE*)buff;\n\n\n\t*bw = 0;\t/* Clear write byte counter */\n\tres = validate(&fp->obj, &fs);\t\t\t/* Check validity of the file object */\n\tif (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);\t/* Check validity */\n\tif (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);\t/* Check access mode */\n\n\t/* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */\n\tif ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {\n\t\tbtw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);\n\t}\n\n\tfor ( ;  btw;\t\t\t\t\t\t\t/* Repeat until all data written */\n\t\twbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {\n\t\tif (fp->fptr % SS(fs) == 0) {\t\t/* On the sector boundary? */\n\t\t\tcsect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1);\t/* Sector offset in the cluster */\n\t\t\tif (csect == 0) {\t\t\t\t/* On the cluster boundary? */\n\t\t\t\tif (fp->fptr == 0) {\t\t/* On the top of the file? */\n\t\t\t\t\tclst = fp->obj.sclust;\t/* Follow from the origin */\n\t\t\t\t\tif (clst == 0) {\t\t/* If no cluster is allocated, */\n\t\t\t\t\t\tclst = create_chain(&fp->obj, 0);\t/* create a new cluster chain */\n\t\t\t\t\t}\n\t\t\t\t} else {\t\t\t\t\t/* On the middle or end of the file */\n#if _USE_FASTSEEK\n\t\t\t\t\tif (fp->cltbl) {\n\t\t\t\t\t\tclst = clmt_clust(fp, fp->fptr);\t/* Get cluster# from the CLMT */\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tclst = create_chain(&fp->obj, fp->clust);\t/* Follow or stretch cluster chain on the FAT */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (clst == 0) break;\t\t/* Could not allocate a new cluster (disk full) */\n\t\t\t\tif (clst == 1) ABORT(fs, FR_INT_ERR);\n\t\t\t\tif (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->clust = clst;\t\t\t/* Update current cluster */\n\t\t\t\tif (fp->obj.sclust == 0) fp->obj.sclust = clst;\t/* Set start cluster if the first write */\n\t\t\t}\n#if _FS_TINY\n\t\t\tif (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);\t/* Write-back sector cache */\n#else\n\t\t\tif (fp->flag & FA_DIRTY) {\t\t/* Write-back sector cache */\n\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t}\n#endif\n\t\t\tsect = clust2sect(fs, fp->clust);\t/* Get current sector */\n\t\t\tif (!sect) ABORT(fs, FR_INT_ERR);\n\t\t\tsect += csect;\n\t\t\tcc = btw / SS(fs);\t\t\t\t/* When remaining bytes >= sector size, */\n\t\t\tif (cc) {\t\t\t\t\t\t/* Write maximum contiguous sectors directly */\n\t\t\t\tif (csect + cc > fs->csize) {\t/* Clip at cluster boundary */\n\t\t\t\t\tcc = fs->csize - csect;\n\t\t\t\t}\n\t\t\t\tif (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);\n#if _FS_MINIMIZE <= 2\n#if _FS_TINY\n\t\t\t\tif (fs->winsect - sect < cc) {\t/* Refill sector cache if it gets invalidated by the direct write */\n\t\t\t\t\tmem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));\n\t\t\t\t\tfs->wflag = 0;\n\t\t\t\t}\n#else\n\t\t\t\tif (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */\n\t\t\t\t\tmem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));\n\t\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t\t}\n#endif\n#endif\n\t\t\t\twcnt = SS(fs) * cc;\t\t/* Number of bytes transferred */\n\t\t\t\tcontinue;\n\t\t\t}\n#if _FS_TINY\n\t\t\tif (fp->fptr >= fp->obj.objsize) {\t/* Avoid silly cache filling on the growing edge */\n\t\t\t\tif (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfs->winsect = sect;\n\t\t\t}\n#else\n\t\t\tif (fp->sect != sect && \t\t/* Fill sector cache with file data */\n\t\t\t\tfp->fptr < fp->obj.objsize &&\n\t\t\t\tdisk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {\n\t\t\t\t\tABORT(fs, FR_DISK_ERR);\n\t\t\t}\n#endif\n\t\t\tfp->sect = sect;\n\t\t}\n\t\twcnt = SS(fs) - (UINT)fp->fptr % SS(fs);\t/* Number of bytes left in the sector */\n\t\tif (wcnt > btw) wcnt = btw;\t\t\t\t\t/* Clip it by btw if needed */\n#if _FS_TINY\n\t\tif (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR);\t/* Move sector window */\n\t\tmem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt);\t/* Fit data to the sector */\n\t\tfs->wflag = 1;\n#else\n\t\tmem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt);\t/* Fit data to the sector */\n\t\tfp->flag |= FA_DIRTY;\n#endif\n\t}\n\n\tfp->flag |= FA_MODIFIED;\t\t\t\t/* Set file change flag */\n\n\tLEAVE_FF(fs, FR_OK);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Synchronize the File                                                  */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_sync (\n\tFIL* fp\t\t/* Pointer to the file object */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD tm;\n\tBYTE *dir;\n#if _FS_EXFAT\n\tDIR dj;\n\tDEF_NAMBUF\n#endif\n\n\tres = validate(&fp->obj, &fs);\t/* Check validity of the file object */\n\tif (res == FR_OK) {\n\t\tif (fp->flag & FA_MODIFIED) {\t/* Is there any change to the file? */\n#if !_FS_TINY\n\t\t\tif (fp->flag & FA_DIRTY) {\t/* Write-back cached data if needed */\n\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);\n\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t}\n#endif\n\t\t\t/* Update the directory entry */\n\t\t\ttm = GET_FATTIME();\t\t\t\t/* Modified time */\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\tres = fill_first_frag(&fp->obj);\t/* Fill first fragment on the FAT if needed */\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tres = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);\t/* Fill last fragment on the FAT if needed */\n\t\t\t\t}\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tINIT_NAMBUF(fs);\n\t\t\t\t\tres = load_obj_dir(&dj, &fp->obj);\t/* Load directory entry block */\n\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\tfs->dirbuf[XDIR_Attr] |= AM_ARC;\t\t\t\t/* Set archive bit */\n\t\t\t\t\t\tfs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1;\t/* Update file allocation info */\n\t\t\t\t\t\tst_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);\n\t\t\t\t\t\tst_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);\n\t\t\t\t\t\tst_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);\n\t\t\t\t\t\tst_dword(fs->dirbuf + XDIR_ModTime, tm);\t\t/* Update modified time */\n\t\t\t\t\t\tfs->dirbuf[XDIR_ModTime10] = 0;\n\t\t\t\t\t\tst_dword(fs->dirbuf + XDIR_AccTime, 0);\n\t\t\t\t\t\tres = store_xdir(&dj);\t/* Restore it to the directory */\n\t\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\t\tres = sync_fs(fs);\n\t\t\t\t\t\t\tfp->flag &= (BYTE)~FA_MODIFIED;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tFREE_NAMBUF();\n\t\t\t\t}\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\tres = move_window(fs, fp->dir_sect);\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tdir = fp->dir_ptr;\n\t\t\t\t\tdir[DIR_Attr] |= AM_ARC;\t\t\t\t\t\t/* Set archive bit */\n\t\t\t\t\tst_clust(fp->obj.fs, dir, fp->obj.sclust);\t\t/* Update file allocation info  */\n\t\t\t\t\tst_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize);\t/* Update file size */\n\t\t\t\t\tst_dword(dir + DIR_ModTime, tm);\t\t\t\t/* Update modified time */\n\t\t\t\t\tst_word(dir + DIR_LstAccDate, 0);\n\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\tres = sync_fs(fs);\t\t\t\t\t/* Restore it to the directory */\n\t\t\t\t\tfp->flag &= (BYTE)~FA_MODIFIED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif /* !_FS_READONLY */\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Close File                                                            */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_close (\n\tFIL* fp\t\t/* Pointer to the file object to be closed */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\n#if !_FS_READONLY\n\tres = f_sync(fp);\t\t\t\t\t/* Flush cached data */\n\tif (res == FR_OK)\n#endif\n\t{\n\t\tres = validate(&fp->obj, &fs);\t/* Lock volume */\n\t\tif (res == FR_OK) {\n#if _FS_LOCK != 0\n\t\t\tres = dec_lock(fp->obj.lockid);\t/* Decrement file open counter */\n\t\t\tif (res == FR_OK)\n#endif\n\t\t\t{\n\t\t\t\tfp->obj.fs = 0;\t\t\t/* Invalidate file object */\n\t\t\t}\n#if _FS_REENTRANT\n\t\t\tunlock_fs(fs, FR_OK);\t\t/* Unlock volume */\n#endif\n\t\t}\n\t}\n\treturn res;\n}\n\n\n\n\n#if _FS_RPATH >= 1\n/*-----------------------------------------------------------------------*/\n/* Change Current Directory or Current Drive, Get Current Directory      */\n/*-----------------------------------------------------------------------*/\n\n#if _VOLUMES >= 2\nFRESULT f_chdrive (\n\tconst TCHAR* path\t\t/* Drive number */\n)\n{\n\tint vol;\n\n\n\t/* Get logical drive number */\n\tvol = get_ldnumber(&path);\n\tif (vol < 0) return FR_INVALID_DRIVE;\n\n\tCurrVol = (BYTE)vol;\t/* Set it as current volume */\n\n\treturn FR_OK;\n}\n#endif\n\n\nFRESULT f_chdir (\n\tconst TCHAR* path\t/* Pointer to the directory path */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tDEF_NAMBUF\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &fs, 0);\n\tif (res == FR_OK) {\n\t\tdj.obj.fs = fs;\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t\t/* Follow the path */\n\t\tif (res == FR_OK) {\t\t\t\t\t/* Follow completed */\n\t\t\tif (dj.fn[NSFLAG] & NS_NONAME) {\n\t\t\t\tfs->cdir = dj.obj.sclust;\t/* It is the start directory itself */\n#if _FS_EXFAT\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\tfs->cdc_scl = dj.obj.c_scl;\n\t\t\t\t\tfs->cdc_size = dj.obj.c_size;\n\t\t\t\t\tfs->cdc_ofs = dj.obj.c_ofs;\n\t\t\t\t}\n#endif\n\t\t\t} else {\n\t\t\t\tif (dj.obj.attr & AM_DIR) {\t/* It is a sub-directory */\n#if _FS_EXFAT\n\t\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\t\tfs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus);\t\t/* Sub-directory cluster */\n\t\t\t\t\t\tfs->cdc_scl = dj.obj.sclust;\t\t\t\t\t\t/* Save containing directory information */\n\t\t\t\t\t\tfs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;\n\t\t\t\t\t\tfs->cdc_ofs = dj.blk_ofs;\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tfs->cdir = ld_clust(fs, dj.dir);\t\t\t\t\t/* Sub-directory cluster */\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tres = FR_NO_PATH;\t\t/* Reached but a file */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t\tif (res == FR_NO_FILE) res = FR_NO_PATH;\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n#if _FS_RPATH >= 2\nFRESULT f_getcwd (\n\tTCHAR* buff,\t/* Pointer to the directory path */\n\tUINT len\t\t/* Size of path */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tUINT i, n;\n\tDWORD ccl;\n\tTCHAR *tp;\n\tFILINFO fno;\n\tDEF_NAMBUF\n\n\n\t*buff = 0;\n\t/* Get logical drive */\n\tres = find_volume((const TCHAR**)&buff, &fs, 0);\t/* Get current volume */\n\tif (res == FR_OK) {\n\t\tdj.obj.fs = fs;\n\t\tINIT_NAMBUF(fs);\n\t\ti = len;\t\t\t/* Bottom of buffer (directory stack base) */\n\t\tif (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\t/* (Cannot do getcwd on exFAT and returns root path) */\n\t\t\tdj.obj.sclust = fs->cdir;\t\t\t\t/* Start to follow upper directory from current directory */\n\t\t\twhile ((ccl = dj.obj.sclust) != 0) {\t/* Repeat while current directory is a sub-directory */\n\t\t\t\tres = dir_sdi(&dj, 1 * SZDIRE);\t/* Get parent directory */\n\t\t\t\tif (res != FR_OK) break;\n\t\t\t\tres = move_window(fs, dj.sect);\n\t\t\t\tif (res != FR_OK) break;\n\t\t\t\tdj.obj.sclust = ld_clust(fs, dj.dir);\t/* Goto parent directory */\n\t\t\t\tres = dir_sdi(&dj, 0);\n\t\t\t\tif (res != FR_OK) break;\n\t\t\t\tdo {\t\t\t\t\t\t\t/* Find the entry links to the child directory */\n\t\t\t\t\tres = dir_read(&dj, 0);\n\t\t\t\t\tif (res != FR_OK) break;\n\t\t\t\t\tif (ccl == ld_clust(fs, dj.dir)) break;\t/* Found the entry */\n\t\t\t\t\tres = dir_next(&dj, 0);\n\t\t\t\t} while (res == FR_OK);\n\t\t\t\tif (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */\n\t\t\t\tif (res != FR_OK) break;\n\t\t\t\tget_fileinfo(&dj, &fno);\t\t/* Get the directory name and push it to the buffer */\n\t\t\t\tfor (n = 0; fno.fname[n]; n++) ;\n\t\t\t\tif (i < n + 3) {\n\t\t\t\t\tres = FR_NOT_ENOUGH_CORE; break;\n\t\t\t\t}\n\t\t\t\twhile (n) buff[--i] = fno.fname[--n];\n\t\t\t\tbuff[--i] = '/';\n\t\t\t}\n\t\t}\n\t\ttp = buff;\n\t\tif (res == FR_OK) {\n#if _VOLUMES >= 2\n\t\t\t*tp++ = '0' + CurrVol;\t\t\t/* Put drive number */\n\t\t\t*tp++ = ':';\n#endif\n\t\t\tif (i == len) {\t\t\t\t\t/* Root-directory */\n\t\t\t\t*tp++ = '/';\n\t\t\t} else {\t\t\t\t\t\t/* Sub-directory */\n\t\t\t\tdo\t\t/* Add stacked path str */\n\t\t\t\t\t*tp++ = buff[i++];\n\t\t\t\twhile (i < len);\n\t\t\t}\n\t\t}\n\t\t*tp = 0;\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif /* _FS_RPATH >= 2 */\n#endif /* _FS_RPATH >= 1 */\n\n\n\n#if _FS_MINIMIZE <= 2\n/*-----------------------------------------------------------------------*/\n/* Seek File R/W Pointer                                                 */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_lseek (\n\tFIL* fp,\t\t/* Pointer to the file object */\n\tFSIZE_t ofs\t\t/* File pointer from top of file */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD clst, bcs, nsect;\n\tFSIZE_t ifptr;\n#if _USE_FASTSEEK\n\tDWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;\n#endif\n\n\tres = validate(&fp->obj, &fs);\t\t/* Check validity of the file object */\n\tif (res == FR_OK) res = (FRESULT)fp->err;\n#if _FS_EXFAT && !_FS_READONLY\n\tif (res == FR_OK && fs->fs_type == FS_EXFAT) {\n\t\tres = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);\t/* Fill last fragment on the FAT if needed */\n\t}\n#endif\n\tif (res != FR_OK) LEAVE_FF(fs, res);\n\n#if _USE_FASTSEEK\n\tif (fp->cltbl) {\t/* Fast seek */\n\t\tif (ofs == CREATE_LINKMAP) {\t/* Create CLMT */\n\t\t\ttbl = fp->cltbl;\n\t\t\ttlen = *tbl++; ulen = 2;\t/* Given table size and required table size */\n\t\t\tcl = fp->obj.sclust;\t\t/* Origin of the chain */\n\t\t\tif (cl) {\n\t\t\t\tdo {\n\t\t\t\t\t/* Get a fragment */\n\t\t\t\t\ttcl = cl; ncl = 0; ulen += 2;\t/* Top, length and used items */\n\t\t\t\t\tdo {\n\t\t\t\t\t\tpcl = cl; ncl++;\n\t\t\t\t\t\tcl = get_fat(&fp->obj, cl);\n\t\t\t\t\t\tif (cl <= 1) ABORT(fs, FR_INT_ERR);\n\t\t\t\t\t\tif (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\t\t} while (cl == pcl + 1);\n\t\t\t\t\tif (ulen <= tlen) {\t\t/* Store the length and top of the fragment */\n\t\t\t\t\t\t*tbl++ = ncl; *tbl++ = tcl;\n\t\t\t\t\t}\n\t\t\t\t} while (cl < fs->n_fatent);\t/* Repeat until end of chain */\n\t\t\t}\n\t\t\t*fp->cltbl = ulen;\t/* Number of items used */\n\t\t\tif (ulen <= tlen) {\n\t\t\t\t*tbl = 0;\t\t/* Terminate table */\n\t\t\t} else {\n\t\t\t\tres = FR_NOT_ENOUGH_CORE;\t/* Given table size is smaller than required */\n\t\t\t}\n\t\t} else {\t\t\t\t\t\t/* Fast seek */\n\t\t\tif (ofs > fp->obj.objsize) ofs = fp->obj.objsize;\t/* Clip offset at the file size */\n\t\t\tfp->fptr = ofs;\t\t\t\t/* Set file pointer */\n\t\t\tif (ofs) {\n\t\t\t\tfp->clust = clmt_clust(fp, ofs - 1);\n\t\t\t\tdsc = clust2sect(fs, fp->clust);\n\t\t\t\tif (!dsc) ABORT(fs, FR_INT_ERR);\n\t\t\t\tdsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);\n\t\t\t\tif (fp->fptr % SS(fs) && dsc != fp->sect) {\t/* Refill sector cache if needed */\n#if !_FS_TINY\n#if !_FS_READONLY\n\t\t\t\t\tif (fp->flag & FA_DIRTY) {\t\t/* Write-back dirty sector cache */\n\t\t\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\t/* Load current sector */\n#endif\n\t\t\t\t\tfp->sect = dsc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else\n#endif\n\n\t/* Normal Seek */\n\t{\n#if _FS_EXFAT\n\t\tif (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF;\t/* Clip at 4GiB-1 if at FATxx */\n#endif\n\t\tif (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) {\t/* In read-only mode, clip offset with the file size */\n\t\t\tofs = fp->obj.objsize;\n\t\t}\n\t\tifptr = fp->fptr;\n\t\tfp->fptr = nsect = 0;\n\t\tif (ofs) {\n\t\t\tbcs = (DWORD)fs->csize * SS(fs);\t/* Cluster size (byte) */\n\t\t\tif (ifptr > 0 &&\n\t\t\t\t(ofs - 1) / bcs >= (ifptr - 1) / bcs) {\t/* When seek to same or following cluster, */\n\t\t\t\tfp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1);\t/* start from the current cluster */\n\t\t\t\tofs -= fp->fptr;\n\t\t\t\tclst = fp->clust;\n\t\t\t} else {\t\t\t\t\t\t\t\t\t/* When seek to back cluster, */\n\t\t\t\tclst = fp->obj.sclust;\t\t\t\t\t/* start from the first cluster */\n#if !_FS_READONLY\n\t\t\t\tif (clst == 0) {\t\t\t\t\t\t/* If no cluster chain, create a new chain */\n\t\t\t\t\tclst = create_chain(&fp->obj, 0);\n\t\t\t\t\tif (clst == 1) ABORT(fs, FR_INT_ERR);\n\t\t\t\t\tif (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\t\tfp->obj.sclust = clst;\n\t\t\t\t}\n#endif\n\t\t\t\tfp->clust = clst;\n\t\t\t}\n\t\t\tif (clst != 0) {\n\t\t\t\twhile (ofs > bcs) {\t\t\t\t\t\t/* Cluster following loop */\n\t\t\t\t\tofs -= bcs; fp->fptr += bcs;\n#if !_FS_READONLY\n\t\t\t\t\tif (fp->flag & FA_WRITE) {\t\t\t/* Check if in write mode or not */\n\t\t\t\t\t\tif (_FS_EXFAT && fp->fptr > fp->obj.objsize) {\t/* No FAT chain object needs correct objsize to generate FAT value */\n\t\t\t\t\t\t\tfp->obj.objsize = fp->fptr;\n\t\t\t\t\t\t\tfp->flag |= FA_MODIFIED;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclst = create_chain(&fp->obj, clst);\t/* Follow chain with forceed stretch */\n\t\t\t\t\t\tif (clst == 0) {\t\t\t\t/* Clip file size in case of disk full */\n\t\t\t\t\t\t\tofs = 0; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tclst = get_fat(&fp->obj, clst);\t/* Follow cluster chain if not in write mode */\n\t\t\t\t\t}\n\t\t\t\t\tif (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\t\tif (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);\n\t\t\t\t\tfp->clust = clst;\n\t\t\t\t}\n\t\t\t\tfp->fptr += ofs;\n\t\t\t\tif (ofs % SS(fs)) {\n\t\t\t\t\tnsect = clust2sect(fs, clst);\t/* Current sector */\n\t\t\t\t\tif (!nsect) ABORT(fs, FR_INT_ERR);\n\t\t\t\t\tnsect += (DWORD)(ofs / SS(fs));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!_FS_READONLY && fp->fptr > fp->obj.objsize) {\t\t/* Set file change flag if the file size is extended */\n\t\t\tfp->obj.objsize = fp->fptr;\n\t\t\tfp->flag |= FA_MODIFIED;\n\t\t}\n\t\tif (fp->fptr % SS(fs) && nsect != fp->sect) {\t/* Fill sector cache if needed */\n#if !_FS_TINY\n#if !_FS_READONLY\n\t\t\tif (fp->flag & FA_DIRTY) {\t\t\t/* Write-back dirty sector cache */\n\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t}\n#endif\n\t\t\tif (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\t/* Fill sector cache */\n#endif\n\t\t\tfp->sect = nsect;\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n#if _FS_MINIMIZE <= 1\n/*-----------------------------------------------------------------------*/\n/* Create a Directory Object                                             */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_opendir (\n\tDIR* dp,\t\t\t/* Pointer to directory object to create */\n\tconst TCHAR* path\t/* Pointer to the directory path */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\t_FDID *obj;\n\tDEF_NAMBUF\n\n\n\tif (!dp) return FR_INVALID_OBJECT;\n\n\t/* Get logical drive */\n\tobj = &dp->obj;\n\tres = find_volume(&path, &fs, 0);\n\tif (res == FR_OK) {\n\t\tobj->fs = fs;\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(dp, path);\t\t\t/* Follow the path to the directory */\n\t\tif (res == FR_OK) {\t\t\t\t\t\t/* Follow completed */\n\t\t\tif (!(dp->fn[NSFLAG] & NS_NONAME)) {\t/* It is not the origin directory itself */\n\t\t\t\tif (obj->attr & AM_DIR) {\t\t/* This object is a sub-directory */\n#if _FS_EXFAT\n\t\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\t\tobj->c_scl = obj->sclust;\t\t\t\t\t\t\t/* Get containing directory inforamation */\n\t\t\t\t\t\tobj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat;\n\t\t\t\t\t\tobj->c_ofs = dp->blk_ofs;\n\t\t\t\t\t\tobj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus);\t/* Get object allocation info */\n\t\t\t\t\t\tobj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\n\t\t\t\t\t\tobj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tobj->sclust = ld_clust(fs, dp->dir);\t/* Get object allocation info */\n\t\t\t\t\t}\n\t\t\t\t} else {\t\t\t\t\t\t/* This object is a file */\n\t\t\t\t\tres = FR_NO_PATH;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tobj->id = fs->id;\n\t\t\t\tres = dir_sdi(dp, 0);\t\t\t/* Rewind directory */\n#if _FS_LOCK != 0\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tif (obj->sclust) {\n\t\t\t\t\t\tobj->lockid = inc_lock(dp, 0);\t/* Lock the sub directory */\n\t\t\t\t\t\tif (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tobj->lockid = 0;\t/* Root directory need not to be locked */\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t\tif (res == FR_NO_FILE) res = FR_NO_PATH;\n\t}\n\tif (res != FR_OK) obj->fs = 0;\t\t/* Invalidate the directory object if function faild */\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Close Directory                                                       */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_closedir (\n\tDIR *dp\t\t/* Pointer to the directory object to be closed */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\n\n\tres = validate(&dp->obj, &fs);\t\t\t/* Check validity of the file object */\n\tif (res == FR_OK) {\n#if _FS_LOCK != 0\n\t\tif (dp->obj.lockid) {\t\t\t\t/* Decrement sub-directory open counter */\n\t\t\tres = dec_lock(dp->obj.lockid);\n\t\t}\n\t\tif (res == FR_OK)\n#endif\n\t\t{\n\t\t\tdp->obj.fs = 0;\t\t\t/* Invalidate directory object */\n\t\t}\n#if _FS_REENTRANT\n\t\tunlock_fs(fs, FR_OK);\t\t/* Unlock volume */\n#endif\n\t}\n\treturn res;\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Read Directory Entries in Sequence                                    */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_readdir (\n\tDIR* dp,\t\t\t/* Pointer to the open directory object */\n\tFILINFO* fno\t\t/* Pointer to file information to return */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDEF_NAMBUF\n\n\n\tres = validate(&dp->obj, &fs);\t/* Check validity of the directory object */\n\tif (res == FR_OK) {\n\t\tif (!fno) {\n\t\t\tres = dir_sdi(dp, 0);\t\t\t/* Rewind the directory object */\n\t\t} else {\n\t\t\tINIT_NAMBUF(fs);\n\t\t\tres = dir_read(dp, 0);\t\t\t/* Read an item */\n\t\t\tif (res == FR_NO_FILE) res = FR_OK;\t/* Ignore end of directory */\n\t\t\tif (res == FR_OK) {\t\t\t\t/* A valid entry is found */\n\t\t\t\tget_fileinfo(dp, fno);\t\t/* Get the object information */\n\t\t\t\tres = dir_next(dp, 0);\t\t/* Increment index for next */\n\t\t\t\tif (res == FR_NO_FILE) res = FR_OK;\t/* Ignore end of directory now */\n\t\t\t}\n\t\t\tFREE_NAMBUF();\n\t\t}\n\t}\n\tLEAVE_FF(fs, res);\n}\n\n\n\n#if _USE_FIND\n/*-----------------------------------------------------------------------*/\n/* Find Next File                                                        */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_findnext (\n\tDIR* dp,\t\t/* Pointer to the open directory object */\n\tFILINFO* fno\t/* Pointer to the file information structure */\n)\n{\n\tFRESULT res;\n\n\n\tfor (;;) {\n\t\tres = f_readdir(dp, fno);\t\t/* Get a directory item */\n\t\tif (res != FR_OK || !fno || !fno->fname[0]) break;\t/* Terminate if any error or end of directory */\n\t\tif (pattern_matching(dp->pat, fno->fname, 0, 0)) break;\t\t/* Test for the file name */\n#if _USE_LFN != 0 && _USE_FIND == 2\n\t\tif (pattern_matching(dp->pat, fno->altname, 0, 0)) break;\t/* Test for alternative name if exist */\n#endif\n\t}\n\treturn res;\n}\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Find First File                                                       */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_findfirst (\n\tDIR* dp,\t\t\t\t/* Pointer to the blank directory object */\n\tFILINFO* fno,\t\t\t/* Pointer to the file information structure */\n\tconst TCHAR* path,\t\t/* Pointer to the directory to open */\n\tconst TCHAR* pattern\t/* Pointer to the matching pattern */\n)\n{\n\tFRESULT res;\n\n\n\tdp->pat = pattern;\t\t/* Save pointer to pattern string */\n\tres = f_opendir(dp, path);\t\t/* Open the target directory */\n\tif (res == FR_OK) {\n\t\tres = f_findnext(dp, fno);\t/* Find the first item */\n\t}\n\treturn res;\n}\n\n#endif\t/* _USE_FIND */\n\n\n\n#if _FS_MINIMIZE == 0\n/*-----------------------------------------------------------------------*/\n/* Get File Status                                                       */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_stat (\n\tconst TCHAR* path,\t/* Pointer to the file path */\n\tFILINFO* fno\t\t/* Pointer to file information to return */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tDEF_NAMBUF\n\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &dj.obj.fs, 0);\n\tif (res == FR_OK) {\n\t\tINIT_NAMBUF(dj.obj.fs);\n\t\tres = follow_path(&dj, path);\t/* Follow the file path */\n\t\tif (res == FR_OK) {\t\t\t\t/* Follow completed */\n\t\t\tif (dj.fn[NSFLAG] & NS_NONAME) {\t/* It is origin directory */\n\t\t\t\tres = FR_INVALID_NAME;\n\t\t\t} else {\t\t\t\t\t\t\t/* Found an object */\n\t\t\t\tif (fno) get_fileinfo(&dj, fno);\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(dj.obj.fs, res);\n}\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Get Number of Free Clusters                                           */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_getfree (\n\tconst TCHAR* path,\t/* Path name of the logical drive number */\n\tDWORD* nclst,\t\t/* Pointer to a variable to return number of free clusters */\n\tFATFS** fatfs\t\t/* Pointer to return pointer to corresponding file system object */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD nfree, clst, sect, stat;\n\tUINT i;\n\tBYTE *p;\n\t_FDID obj;\n\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &fs, 0);\n\tif (res == FR_OK) {\n\t\t*fatfs = fs;\t\t\t\t/* Return ptr to the fs object */\n\t\t/* If free_clst is valid, return it without full cluster scan */\n\t\tif (fs->free_clst <= fs->n_fatent - 2) {\n\t\t\t*nclst = fs->free_clst;\n\t\t} else {\n\t\t\t/* Get number of free clusters */\n\t\t\tnfree = 0;\n\t\t\tif (fs->fs_type == FS_FAT12) {\t/* FAT12: Sector unalighed FAT entries */\n\t\t\t\tclst = 2; obj.fs = fs;\n\t\t\t\tdo {\n\t\t\t\t\tstat = get_fat(&obj, clst);\n\t\t\t\t\tif (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }\n\t\t\t\t\tif (stat == 1) { res = FR_INT_ERR; break; }\n\t\t\t\t\tif (stat == 0) nfree++;\n\t\t\t\t} while (++clst < fs->n_fatent);\n\t\t\t} else {\n#if _FS_EXFAT\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\t/* exFAT: Scan bitmap table */\n\t\t\t\t\tBYTE bm;\n\t\t\t\t\tUINT b;\n\n\t\t\t\t\tclst = fs->n_fatent - 2;\n\t\t\t\t\tsect = fs->database;\n\t\t\t\t\ti = 0;\n\t\t\t\t\tdo {\n\t\t\t\t\t\tif (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break;\n\t\t\t\t\t\tfor (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {\n\t\t\t\t\t\t\tif (!(bm & 1)) nfree++;\n\t\t\t\t\t\t\tbm >>= 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti = (i + 1) % SS(fs);\n\t\t\t\t\t} while (clst);\n\t\t\t\t} else\n#endif\n\t\t\t\t{\t/* FAT16/32: Sector alighed FAT entries */\n\t\t\t\t\tclst = fs->n_fatent; sect = fs->fatbase;\n\t\t\t\t\ti = 0; p = 0;\n\t\t\t\t\tdo {\n\t\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t\tres = move_window(fs, sect++);\n\t\t\t\t\t\t\tif (res != FR_OK) break;\n\t\t\t\t\t\t\tp = fs->win;\n\t\t\t\t\t\t\ti = SS(fs);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (fs->fs_type == FS_FAT16) {\n\t\t\t\t\t\t\tif (ld_word(p) == 0) nfree++;\n\t\t\t\t\t\t\tp += 2; i -= 2;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++;\n\t\t\t\t\t\t\tp += 4; i -= 4;\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (--clst);\n\t\t\t\t}\n\t\t\t}\n\t\t\t*nclst = nfree;\t\t\t/* Return the free clusters */\n\t\t\tfs->free_clst = nfree;\t/* Now free_clst is valid */\n\t\t\tfs->fsi_flag |= 1;\t\t/* FSInfo is to be updated */\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Truncate File                                                         */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_truncate (\n\tFIL* fp\t\t/* Pointer to the file object */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD ncl;\n\n\n\tres = validate(&fp->obj, &fs);\t/* Check validity of the file object */\n\tif (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);\n\tif (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);\t/* Check access mode */\n\n\tif (fp->fptr < fp->obj.objsize) {\t/* Process when fptr is not on the eof */\n\t\tif (fp->fptr == 0) {\t/* When set file size to zero, remove entire cluster chain */\n\t\t\tres = remove_chain(&fp->obj, fp->obj.sclust, 0);\n\t\t\tfp->obj.sclust = 0;\n\t\t} else {\t\t\t\t/* When truncate a part of the file, remove remaining clusters */\n\t\t\tncl = get_fat(&fp->obj, fp->clust);\n\t\t\tres = FR_OK;\n\t\t\tif (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;\n\t\t\tif (ncl == 1) res = FR_INT_ERR;\n\t\t\tif (res == FR_OK && ncl < fs->n_fatent) {\n\t\t\t\tres = remove_chain(&fp->obj, ncl, fp->clust);\n\t\t\t}\n\t\t}\n\t\tfp->obj.objsize = fp->fptr;\t/* Set file size to current R/W point */\n\t\tfp->flag |= FA_MODIFIED;\n#if !_FS_TINY\n\t\tif (res == FR_OK && (fp->flag & FA_DIRTY)) {\n\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\n\t\t\t\tres = FR_DISK_ERR;\n\t\t\t} else {\n\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t}\n\t\t}\n#endif\n\t\tif (res != FR_OK) ABORT(fs, res);\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Delete a File/Directory                                               */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_unlink (\n\tconst TCHAR* path\t\t/* Pointer to the file or directory path */\n)\n{\n\tFRESULT res;\n\tDIR dj, sdj;\n\tDWORD dclst = 0;\n\tFATFS *fs;\n#if _FS_EXFAT\n\t_FDID obj;\n#endif\n\tDEF_NAMBUF\n\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &fs, FA_WRITE);\n\tdj.obj.fs = fs;\n\tif (res == FR_OK) {\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t\t/* Follow the file path */\n\t\tif (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {\n\t\t\tres = FR_INVALID_NAME;\t\t\t/* Cannot remove dot entry */\n\t\t}\n#if _FS_LOCK != 0\n\t\tif (res == FR_OK) res = chk_lock(&dj, 2);\t/* Check if it is an open object */\n#endif\n\t\tif (res == FR_OK) {\t\t\t\t\t/* The object is accessible */\n\t\t\tif (dj.fn[NSFLAG] & NS_NONAME) {\n\t\t\t\tres = FR_INVALID_NAME;\t\t/* Cannot remove the origin directory */\n\t\t\t} else {\n\t\t\t\tif (dj.obj.attr & AM_RDO) {\n\t\t\t\t\tres = FR_DENIED;\t\t/* Cannot remove R/O object */\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n#if _FS_EXFAT\n\t\t\t\tobj.fs = fs;\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\tobj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus);\n\t\t\t\t\tobj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\n\t\t\t\t\tobj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\n\t\t\t\t} else\n#endif\n\t\t\t\t{\n\t\t\t\t\tdclst = ld_clust(fs, dj.dir);\n\t\t\t\t}\n\t\t\t\tif (dj.obj.attr & AM_DIR) {\t\t\t/* Is it a sub-directory? */\n#if _FS_RPATH != 0\n\t\t\t\t\tif (dclst == fs->cdir) {\t\t \t\t/* Is it the current directory? */\n\t\t\t\t\t\tres = FR_DENIED;\n\t\t\t\t\t} else\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tsdj.obj.fs = fs;\t\t\t\t\t\t/* Open the sub-directory */\n\t\t\t\t\t\tsdj.obj.sclust = dclst;\n#if _FS_EXFAT\n\t\t\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\t\t\tsdj.obj.objsize = obj.objsize;\n\t\t\t\t\t\t\tsdj.obj.stat = obj.stat;\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tres = dir_sdi(&sdj, 0);\n\t\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\t\tres = dir_read(&sdj, 0);\t\t\t/* Read an item */\n\t\t\t\t\t\t\tif (res == FR_OK) res = FR_DENIED;\t/* Not empty? */\n\t\t\t\t\t\t\tif (res == FR_NO_FILE) res = FR_OK;\t/* Empty? */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tres = dir_remove(&dj);\t\t\t/* Remove the directory entry */\n\t\t\t\tif (res == FR_OK && dclst) {\t/* Remove the cluster chain if exist */\n#if _FS_EXFAT\n\t\t\t\t\tres = remove_chain(&obj, dclst, 0);\n#else\n\t\t\t\t\tres = remove_chain(&dj.obj, dclst, 0);\n#endif\n\t\t\t\t}\n\t\t\t\tif (res == FR_OK) res = sync_fs(fs);\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Create a Directory                                                    */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_mkdir (\n\tconst TCHAR* path\t\t/* Pointer to the directory path */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tBYTE *dir;\n\tUINT n;\n\tDWORD dsc, dcl, pcl, tm;\n\tDEF_NAMBUF\n\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &fs, FA_WRITE);\n\tdj.obj.fs = fs;\n\tif (res == FR_OK) {\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t\t\t/* Follow the file path */\n\t\tif (res == FR_OK) res = FR_EXIST;\t\t/* Any object with same name is already existing */\n\t\tif (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) {\n\t\t\tres = FR_INVALID_NAME;\n\t\t}\n\t\tif (res == FR_NO_FILE) {\t\t\t\t/* Can create a new directory */\n\t\t\tdcl = create_chain(&dj.obj, 0);\t\t/* Allocate a cluster for the new directory table */\n\t\t\tdj.obj.objsize = (DWORD)fs->csize * SS(fs);\n\t\t\tres = FR_OK;\n\t\t\tif (dcl == 0) res = FR_DENIED;\t\t/* No space to allocate a new cluster */\n\t\t\tif (dcl == 1) res = FR_INT_ERR;\n\t\t\tif (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;\n\t\t\tif (res == FR_OK) res = sync_window(fs);\t/* Flush FAT */\n\t\t\ttm = GET_FATTIME();\n\t\t\tif (res == FR_OK) {\t\t\t\t\t/* Initialize the new directory table */\n\t\t\t\tdsc = clust2sect(fs, dcl);\n\t\t\t\tdir = fs->win;\n\t\t\t\tmem_set(dir, 0, SS(fs));\n\t\t\t\tif (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\n\t\t\t\t\tmem_set(dir + DIR_Name, ' ', 11);\t/* Create \".\" entry */\n\t\t\t\t\tdir[DIR_Name] = '.';\n\t\t\t\t\tdir[DIR_Attr] = AM_DIR;\n\t\t\t\t\tst_dword(dir + DIR_ModTime, tm);\n\t\t\t\t\tst_clust(fs, dir, dcl);\n\t\t\t\t\tmem_cpy(dir + SZDIRE, dir, SZDIRE); \t/* Create \"..\" entry */\n\t\t\t\t\tdir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;\n\t\t\t\t\tif (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0;\n\t\t\t\t\tst_clust(fs, dir + SZDIRE, pcl);\n\t\t\t\t}\n\t\t\t\tfor (n = fs->csize; n; n--) {\t/* Write dot entries and clear following sectors */\n\t\t\t\t\tfs->winsect = dsc++;\n\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\tres = sync_window(fs);\n\t\t\t\t\tif (res != FR_OK) break;\n\t\t\t\t\tmem_set(dir, 0, SS(fs));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tres = dir_register(&dj);\t/* Register the object to the directory */\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n#if _FS_EXFAT\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\t/* Initialize directory entry block */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_ModTime, tm);\t/* Created time */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_FstClus, dcl);\t/* Table start cluster */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize);\t/* File size needs to be valid */\n\t\t\t\t\tst_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize);\n\t\t\t\t\tfs->dirbuf[XDIR_GenFlags] = 3;\t\t\t\t/* Initialize the object flag (contiguous) */\n\t\t\t\t\tfs->dirbuf[XDIR_Attr] = AM_DIR;\t\t\t\t/* Attribute */\n\t\t\t\t\tres = store_xdir(&dj);\n\t\t\t\t} else\n#endif\n\t\t\t\t{\n\t\t\t\t\tdir = dj.dir;\n\t\t\t\t\tst_dword(dir + DIR_ModTime, tm);\t/* Created time */\n\t\t\t\t\tst_clust(fs, dir, dcl);\t\t\t\t/* Table start cluster */\n\t\t\t\t\tdir[DIR_Attr] = AM_DIR;\t\t\t\t/* Attribute */\n\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t}\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tres = sync_fs(fs);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tremove_chain(&dj.obj, dcl, 0);\t\t/* Could not register, remove cluster chain */\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Rename a File/Directory                                               */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_rename (\n\tconst TCHAR* path_old,\t/* Pointer to the object name to be renamed */\n\tconst TCHAR* path_new\t/* Pointer to the new name */\n)\n{\n\tFRESULT res;\n\tDIR djo, djn;\n\tFATFS *fs;\n\tBYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir;\n\tDWORD dw;\n\tDEF_NAMBUF\n\n\n\tget_ldnumber(&path_new);\t\t\t\t\t\t/* Snip drive number of new name off */\n\tres = find_volume(&path_old, &fs, FA_WRITE);\t/* Get logical drive of the old object */\n\tif (res == FR_OK) {\n\t\tdjo.obj.fs = fs;\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&djo, path_old);\t\t/* Check old object */\n\t\tif (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;\t/* Check validity of name */\n#if _FS_LOCK != 0\n\t\tif (res == FR_OK) {\n\t\t\tres = chk_lock(&djo, 2);\n\t\t}\n#endif\n\t\tif (res == FR_OK) {\t\t\t\t\t\t/* Object to be renamed is found */\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\t/* At exFAT */\n\t\t\t\tBYTE nf, nn;\n\t\t\t\tWORD nh;\n\n\t\t\t\tmem_cpy(buf, fs->dirbuf, SZDIRE * 2);\t/* Save 85+C0 entry of old object */\n\t\t\t\tmem_cpy(&djn, &djo, sizeof djo);\n\t\t\t\tres = follow_path(&djn, path_new);\t\t/* Make sure if new object name is not in use */\n\t\t\t\tif (res == FR_OK) {\t\t\t\t\t\t/* Is new name already in use by any other object? */\n\t\t\t\t\tres = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;\n\t\t\t\t}\n\t\t\t\tif (res == FR_NO_FILE) { \t\t\t\t/* It is a valid path and no name collision */\n\t\t\t\t\tres = dir_register(&djn);\t\t\t/* Register the new entry */\n\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\tnf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];\n\t\t\t\t\t\tnh = ld_word(fs->dirbuf + XDIR_NameHash);\n\t\t\t\t\t\tmem_cpy(fs->dirbuf, buf, SZDIRE * 2);\n\t\t\t\t\t\tfs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;\n\t\t\t\t\t\tst_word(fs->dirbuf + XDIR_NameHash, nh);\n/* Start of critical section where an interruption can cause a cross-link */\n\t\t\t\t\t\tres = store_xdir(&djn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else\n#endif\n\t\t\t{\t/* At FAT12/FAT16/FAT32 */\n\t\t\t\tmem_cpy(buf, djo.dir + DIR_Attr, 21);\t/* Save information about the object except name */\n\t\t\t\tmem_cpy(&djn, &djo, sizeof (DIR));\t\t/* Duplicate the directory object */\n\t\t\t\tres = follow_path(&djn, path_new);\t\t/* Make sure if new object name is not in use */\n\t\t\t\tif (res == FR_OK) {\t\t\t\t\t\t/* Is new name already in use by any other object? */\n\t\t\t\t\tres = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;\n\t\t\t\t}\n\t\t\t\tif (res == FR_NO_FILE) { \t\t\t\t/* It is a valid path and no name collision */\n\t\t\t\t\tres = dir_register(&djn);\t\t\t/* Register the new entry */\n\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\tdir = djn.dir;\t\t\t\t\t/* Copy information about object except name */\n\t\t\t\t\t\tmem_cpy(dir + 13, buf + 2, 19);\n\t\t\t\t\t\tdir[DIR_Attr] = buf[0] | AM_ARC;\n\t\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\t\tif ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) {\t/* Update .. entry in the sub-directory if needed */\n\t\t\t\t\t\t\tdw = clust2sect(fs, ld_clust(fs, dir));\n\t\t\t\t\t\t\tif (!dw) {\n\t\t\t\t\t\t\t\tres = FR_INT_ERR;\n\t\t\t\t\t\t\t} else {\n/* Start of critical section where an interruption can cause a cross-link */\n\t\t\t\t\t\t\t\tres = move_window(fs, dw);\n\t\t\t\t\t\t\t\tdir = fs->win + SZDIRE * 1;\t/* Ptr to .. entry */\n\t\t\t\t\t\t\t\tif (res == FR_OK && dir[1] == '.') {\n\t\t\t\t\t\t\t\t\tst_clust(fs, dir, djn.obj.sclust);\n\t\t\t\t\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tres = dir_remove(&djo);\t\t/* Remove old entry */\n\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\tres = sync_fs(fs);\n\t\t\t\t}\n\t\t\t}\n/* End of the critical section */\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif /* !_FS_READONLY */\n#endif /* _FS_MINIMIZE == 0 */\n#endif /* _FS_MINIMIZE <= 1 */\n#endif /* _FS_MINIMIZE <= 2 */\n\n\n\n#if _USE_CHMOD && !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Change Attribute                                                      */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_chmod (\n\tconst TCHAR* path,\t/* Pointer to the file path */\n\tBYTE attr,\t\t\t/* Attribute bits */\n\tBYTE mask\t\t\t/* Attribute mask to change */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tDEF_NAMBUF\n\n\n\tres = find_volume(&path, &fs, FA_WRITE);\t/* Get logical drive */\n\tdj.obj.fs = fs;\n\tif (res == FR_OK) {\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t/* Follow the file path */\n\t\tif (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;\t/* Check object validity */\n\t\tif (res == FR_OK) {\n\t\t\tmask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;\t/* Valid attribute mask */\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\tfs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask);\t/* Apply attribute change */\n\t\t\t\tres = store_xdir(&dj);\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\tdj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask);\t/* Apply attribute change */\n\t\t\t\tfs->wflag = 1;\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tres = sync_fs(fs);\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Change Timestamp                                                      */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_utime (\n\tconst TCHAR* path,\t/* Pointer to the file/directory name */\n\tconst FILINFO* fno\t/* Pointer to the time stamp to be set */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tDEF_NAMBUF\n\n\n\tres = find_volume(&path, &fs, FA_WRITE);\t/* Get logical drive */\n\tdj.obj.fs = fs;\n\tif (res == FR_OK) {\n\t\tINIT_NAMBUF(fs);\n\t\tres = follow_path(&dj, path);\t/* Follow the file path */\n\t\tif (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;\t/* Check object validity */\n\t\tif (res == FR_OK) {\n#if _FS_EXFAT\n\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\tst_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);\n\t\t\t\tres = store_xdir(&dj);\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\tst_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);\n\t\t\t\tfs->wflag = 1;\n\t\t\t}\n\t\t\tif (res == FR_OK) {\n\t\t\t\tres = sync_fs(fs);\n\t\t\t}\n\t\t}\n\t\tFREE_NAMBUF();\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif\t/* _USE_CHMOD && !_FS_READONLY */\n\n\n\n#if _USE_LABEL\n/*-----------------------------------------------------------------------*/\n/* Get Volume Label                                                      */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_getlabel (\n\tconst TCHAR* path,\t/* Path name of the logical drive number */\n\tTCHAR* label,\t\t/* Pointer to a buffer to return the volume label */\n\tDWORD* vsn\t\t\t/* Pointer to a variable to return the volume serial number */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tUINT si, di;\n#if _LFN_UNICODE || _FS_EXFAT\n\tWCHAR w;\n#endif\n\n\t/* Get logical drive */\n\tres = find_volume(&path, &fs, 0);\n\n\t/* Get volume label */\n\tif (res == FR_OK && label) {\n\t\tdj.obj.fs = fs; dj.obj.sclust = 0;\t/* Open root directory */\n\t\tres = dir_sdi(&dj, 0);\n\t\tif (res == FR_OK) {\n\t\t \tres = dir_read(&dj, 1);\t\t\t/* Find a volume label entry */\n\t\t \tif (res == FR_OK) {\n#if _FS_EXFAT\n\t\t\t\tif (fs->fs_type == FS_EXFAT) {\n\t\t\t\t\tfor (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) {\t/* Extract volume label from 83 entry */\n\t\t\t\t\t\tw = ld_word(dj.dir + XDIR_Label + si * 2);\n#if _LFN_UNICODE\n\t\t\t\t\t\tlabel[di++] = w;\n#else\n\t\t\t\t\t\tw = ff_convert(w, 0);\t/* Unicode -> OEM */\n\t\t\t\t\t\tif (w == 0) w = '?';\t/* Replace wrong character */\n\t\t\t\t\t\tif (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8);\n\t\t\t\t\t\tlabel[di++] = (char)w;\n#endif\n\t\t\t\t\t}\n\t\t\t\t\tlabel[di] = 0;\n\t\t\t\t} else\n#endif\n\t\t\t\t{\n\t\t\t\t\tsi = di = 0;\t\t/* Extract volume label from AM_VOL entry with code comversion */\n\t\t\t\t\tdo {\n#if _LFN_UNICODE\n\t\t\t\t\t\tw = (si < 11) ? dj.dir[si++] : ' ';\n\t\t\t\t\t\tif (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) {\n\t\t\t\t\t\t\tw = w << 8 | dj.dir[si++];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlabel[di++] = ff_convert(w, 1);\t/* OEM -> Unicode */\n#else\n\t\t\t\t\t\tlabel[di++] = dj.dir[si++];\n#endif\n\t\t\t\t\t} while (di < 11);\n\t\t\t\t\tdo {\t\t\t\t/* Truncate trailing spaces */\n\t\t\t\t\t\tlabel[di] = 0;\n\t\t\t\t\t\tif (di == 0) break;\n\t\t\t\t\t} while (label[--di] == ' ');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (res == FR_NO_FILE) {\t/* No label entry and return nul string */\n\t\t\tlabel[0] = 0;\n\t\t\tres = FR_OK;\n\t\t}\n\t}\n\n\t/* Get volume serial number */\n\tif (res == FR_OK && vsn) {\n\t\tres = move_window(fs, fs->volbase);\n\t\tif (res == FR_OK) {\n\t\t\tswitch (fs->fs_type) {\n\t\t\tcase FS_EXFAT:\n\t\t\t\tdi = BPB_VolIDEx; break;\n\n\t\t\tcase FS_FAT32:\n\t\t\t\tdi = BS_VolID32; break;\n\n\t\t\tdefault:\n\t\t\t\tdi = BS_VolID;\n\t\t\t}\n\t\t\t*vsn = ld_dword(fs->win + di);\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n\n\n#if !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Set Volume Label                                                      */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_setlabel (\n\tconst TCHAR* label\t/* Pointer to the volume label to set */\n)\n{\n\tFRESULT res;\n\tDIR dj;\n\tFATFS *fs;\n\tBYTE dirvn[22];\n\tUINT i, j, slen;\n\tWCHAR w;\n\tstatic const char badchr[] = \"\\\"*+,.:;<=>\\?[]|\\x7F\";\n\n\n\t/* Get logical drive */\n\tres = find_volume(&label, &fs, FA_WRITE);\n\tif (res != FR_OK) LEAVE_FF(fs, res);\n\tdj.obj.fs = fs;\n\n\t/* Get length of given volume label */\n\tfor (slen = 0; (UINT)label[slen] >= ' '; slen++) ;\t/* Get name length */\n\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\t/* On the exFAT volume */\n\t\tfor (i = j = 0; i < slen; ) {\t/* Create volume label in directory form */\n\t\t\tw = label[i++];\n#if !_LFN_UNICODE\n\t\t\tif (IsDBCS1(w)) {\n\t\t\t\tw = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;\n\t\t\t}\n\t\t\tw = ff_convert(w, 1);\n#endif\n\t\t\tif (w == 0 || chk_chr(badchr, w) || j == 22) {\t/* Check validity check validity of the volume label */\n\t\t\t\tLEAVE_FF(fs, FR_INVALID_NAME);\n\t\t\t}\n\t\t\tst_word(dirvn + j, w); j += 2;\n\t\t}\n\t\tslen = j;\n\t} else\n#endif\n\t{\t/* On the FAT12/16/32 volume */\n\t\tfor ( ; slen && label[slen - 1] == ' '; slen--) ;\t/* Remove trailing spaces */\n\t\tif (slen) {\t\t/* Is there a volume label to be set? */\n\t\t\tdirvn[0] = 0; i = j = 0;\t/* Create volume label in directory form */\n\t\t\tdo {\n#if _LFN_UNICODE\n\t\t\t\tw = ff_convert(ff_wtoupper(label[i++]), 0);\n#else\n\t\t\t\tw = (BYTE)label[i++];\n\t\t\t\tif (IsDBCS1(w)) {\n\t\t\t\t\tw = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;\n\t\t\t\t}\n#if _USE_LFN != 0\n\t\t\t\tw = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);\n#else\n\t\t\t\tif (IsLower(w)) w -= 0x20;\t\t\t/* To upper ASCII characters */\n#ifdef _EXCVT\n\t\t\t\tif (w >= 0x80) w = ExCvt[w - 0x80];\t/* To upper extended characters (SBCS cfg) */\n#else\n\t\t\t\tif (!_DF1S && w >= 0x80) w = 0;\t\t/* Reject extended characters (ASCII cfg) */\n#endif\n#endif\n#endif\n\t\t\t\tif (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) {\t/* Reject invalid characters for volume label */\n\t\t\t\t\tLEAVE_FF(fs, FR_INVALID_NAME);\n\t\t\t\t}\n\t\t\t\tif (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8);\n\t\t\t\tdirvn[j++] = (BYTE)w;\n\t\t\t} while (i < slen);\n\t\t\twhile (j < 11) dirvn[j++] = ' ';\t/* Fill remaining name field */\n\t\t\tif (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME);\t/* Reject illegal name (heading DDEM) */\n\t\t}\n\t}\n\n\t/* Set volume label */\n\tdj.obj.sclust = 0;\t\t/* Open root directory */\n\tres = dir_sdi(&dj, 0);\n\tif (res == FR_OK) {\n\t\tres = dir_read(&dj, 1);\t/* Get volume label entry */\n\t\tif (res == FR_OK) {\n\t\t\tif (_FS_EXFAT && fs->fs_type == FS_EXFAT) {\n\t\t\t\tdj.dir[XDIR_NumLabel] = (BYTE)(slen / 2);\t/* Change the volume label */\n\t\t\t\tmem_cpy(dj.dir + XDIR_Label, dirvn, slen);\n\t\t\t} else {\n\t\t\t\tif (slen) {\n\t\t\t\t\tmem_cpy(dj.dir, dirvn, 11);\t/* Change the volume label */\n\t\t\t\t} else {\n\t\t\t\t\tdj.dir[DIR_Name] = DDEM;\t/* Remove the volume label */\n\t\t\t\t}\n\t\t\t}\n\t\t\tfs->wflag = 1;\n\t\t\tres = sync_fs(fs);\n\t\t} else {\t\t\t/* No volume label entry is found or error */\n\t\t\tif (res == FR_NO_FILE) {\n\t\t\t\tres = FR_OK;\n\t\t\t\tif (slen) {\t/* Create a volume label entry */\n\t\t\t\t\tres = dir_alloc(&dj, 1);\t/* Allocate an entry */\n\t\t\t\t\tif (res == FR_OK) {\n\t\t\t\t\t\tmem_set(dj.dir, 0, SZDIRE);\t/* Clear the entry */\n\t\t\t\t\t\tif (_FS_EXFAT && fs->fs_type == FS_EXFAT) {\n\t\t\t\t\t\t\tdj.dir[XDIR_Type] = 0x83;\t\t/* Create 83 entry */\n\t\t\t\t\t\t\tdj.dir[XDIR_NumLabel] = (BYTE)(slen / 2);\n\t\t\t\t\t\t\tmem_cpy(dj.dir + XDIR_Label, dirvn, slen);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdj.dir[DIR_Attr] = AM_VOL;\t\t/* Create volume label entry */\n\t\t\t\t\t\t\tmem_cpy(dj.dir, dirvn, 11);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfs->wflag = 1;\n\t\t\t\t\t\tres = sync_fs(fs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif /* !_FS_READONLY */\n#endif /* _USE_LABEL */\n\n\n\n#if _USE_EXPAND && !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Allocate a Contiguous Blocks to the File                              */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_expand (\n\tFIL* fp,\t\t/* Pointer to the file object */\n\tFSIZE_t fsz,\t/* File size to be expanded to */\n\tBYTE opt\t\t/* Operation mode 0:Find and prepare or 1:Find and allocate */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD n, clst, stcl, scl, ncl, tcl, lclst;\n\n\n\tres = validate(&fp->obj, &fs);\t\t/* Check validity of the file object */\n\tif (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);\n\tif (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);\n#if _FS_EXFAT\n\tif (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED);\t/* Check if in size limit */\n#endif\n\tn = (DWORD)fs->csize * SS(fs);\t/* Cluster size */\n\ttcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0);\t/* Number of clusters required */\n\tstcl = fs->last_clst; lclst = 0;\n\tif (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;\n\n#if _FS_EXFAT\n\tif (fs->fs_type == FS_EXFAT) {\n\t\tscl = find_bitmap(fs, stcl, tcl);\t\t\t/* Find a contiguous cluster block */\n\t\tif (scl == 0) res = FR_DENIED;\t\t\t\t/* No contiguous cluster block was found */\n\t\tif (scl == 0xFFFFFFFF) res = FR_DISK_ERR;\n\t\tif (res == FR_OK) {\t/* A contiguous free area is found */\n\t\t\tif (opt) {\t\t/* Allocate it now */\n\t\t\t\tres = change_bitmap(fs, scl, tcl, 1);\t/* Mark the cluster block 'in use' */\n\t\t\t\tlclst = scl + tcl - 1;\n\t\t\t} else {\t\t/* Set it as suggested point for next allocation */\n\t\t\t\tlclst = scl - 1;\n\t\t\t}\n\t\t}\n\t} else\n#endif\n\t{\n\t\tscl = clst = stcl; ncl = 0;\n\t\tfor (;;) {\t/* Find a contiguous cluster block */\n\t\t\tn = get_fat(&fp->obj, clst);\n\t\t\tif (++clst >= fs->n_fatent) clst = 2;\n\t\t\tif (n == 1) { res = FR_INT_ERR; break; }\n\t\t\tif (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }\n\t\t\tif (n == 0) {\t/* Is it a free cluster? */\n\t\t\t\tif (++ncl == tcl) break;\t/* Break if a contiguous cluster block is found */\n\t\t\t} else {\n\t\t\t\tscl = clst; ncl = 0;\t\t/* Not a free cluster */\n\t\t\t}\n\t\t\tif (clst == stcl) { res = FR_DENIED; break; }\t/* No contiguous cluster? */\n\t\t}\n\t\tif (res == FR_OK) {\t/* A contiguous free area is found */\n\t\t\tif (opt) {\t\t/* Allocate it now */\n\t\t\t\tfor (clst = scl, n = tcl; n; clst++, n--) {\t/* Create a cluster chain on the FAT */\n\t\t\t\t\tres = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);\n\t\t\t\t\tif (res != FR_OK) break;\n\t\t\t\t\tlclst = clst;\n\t\t\t\t}\n\t\t\t} else {\t\t/* Set it as suggested point for next allocation */\n\t\t\t\tlclst = scl - 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (res == FR_OK) {\n\t\tfs->last_clst = lclst;\t\t/* Set suggested start cluster to start next */\n\t\tif (opt) {\t/* Is it allocated now? */\n\t\t\tfp->obj.sclust = scl;\t\t/* Update object allocation information */\n\t\t\tfp->obj.objsize = fsz;\n\t\t\tif (_FS_EXFAT) fp->obj.stat = 2;\t/* Set status 'contiguous chain' */\n\t\t\tfp->flag |= FA_MODIFIED;\n\t\t\tif (fs->free_clst <= fs->n_fatent - 2) {\t/* Update FSINFO */\n\t\t\t\tfs->free_clst -= tcl;\n\t\t\t\tfs->fsi_flag |= 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tLEAVE_FF(fs, res);\n}\n\n#endif /* _USE_EXPAND && !_FS_READONLY */\n\n\n\n#if _USE_FORWARD\n/*-----------------------------------------------------------------------*/\n/* Forward data to the stream directly                                   */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_forward (\n\tFIL* fp, \t\t\t\t\t\t/* Pointer to the file object */\n\tUINT (*func)(const BYTE*,UINT),\t/* Pointer to the streaming function */\n\tUINT btf,\t\t\t\t\t\t/* Number of bytes to forward */\n\tUINT* bf\t\t\t\t\t\t/* Pointer to number of bytes forwarded */\n)\n{\n\tFRESULT res;\n\tFATFS *fs;\n\tDWORD clst, sect;\n\tFSIZE_t remain;\n\tUINT rcnt, csect;\n\tBYTE *dbuf;\n\n\n\t*bf = 0;\t/* Clear transfer byte counter */\n\tres = validate(&fp->obj, &fs);\t\t/* Check validity of the file object */\n\tif (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);\n\tif (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED);\t/* Check access mode */\n\n\tremain = fp->obj.objsize - fp->fptr;\n\tif (btf > remain) btf = (UINT)remain;\t\t\t/* Truncate btf by remaining bytes */\n\n\tfor ( ;  btf && (*func)(0, 0);\t\t\t\t\t/* Repeat until all data transferred or stream goes busy */\n\t\tfp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {\n\t\tcsect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));\t/* Sector offset in the cluster */\n\t\tif (fp->fptr % SS(fs) == 0) {\t\t\t\t/* On the sector boundary? */\n\t\t\tif (csect == 0) {\t\t\t\t\t\t/* On the cluster boundary? */\n\t\t\t\tclst = (fp->fptr == 0) ?\t\t\t/* On the top of the file? */\n\t\t\t\t\tfp->obj.sclust : get_fat(&fp->obj, fp->clust);\n\t\t\t\tif (clst <= 1) ABORT(fs, FR_INT_ERR);\n\t\t\t\tif (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->clust = clst;\t\t\t\t\t/* Update current cluster */\n\t\t\t}\n\t\t}\n\t\tsect = clust2sect(fs, fp->clust);\t\t\t/* Get current data sector */\n\t\tif (!sect) ABORT(fs, FR_INT_ERR);\n\t\tsect += csect;\n#if _FS_TINY\n\t\tif (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR);\t/* Move sector window to the file data */\n\t\tdbuf = fs->win;\n#else\n\t\tif (fp->sect != sect) {\t\t/* Fill sector cache with file data */\n#if !_FS_READONLY\n\t\t\tif (fp->flag & FA_DIRTY) {\t\t/* Write-back dirty sector cache */\n\t\t\t\tif (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t\t\tfp->flag &= (BYTE)~FA_DIRTY;\n\t\t\t}\n#endif\n\t\t\tif (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);\n\t\t}\n\t\tdbuf = fp->buf;\n#endif\n\t\tfp->sect = sect;\n\t\trcnt = SS(fs) - (UINT)fp->fptr % SS(fs);\t/* Number of bytes left in the sector */\n\t\tif (rcnt > btf) rcnt = btf;\t\t\t\t\t/* Clip it by btr if needed */\n\t\trcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt);\t/* Forward the file data */\n\t\tif (!rcnt) ABORT(fs, FR_INT_ERR);\n\t}\n\n\tLEAVE_FF(fs, FR_OK);\n}\n#endif /* _USE_FORWARD */\n\n\n\n#if _USE_MKFS && !_FS_READONLY\n/*-----------------------------------------------------------------------*/\n/* Create an FAT/exFAT volume                                            */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_mkfs (\n\tconst TCHAR* path,\t/* Logical drive number */\n\tBYTE opt,\t\t\t/* Format option */\n\tDWORD au,\t\t\t/* Size of allocation unit (cluster) [byte] */\n\tvoid* work,\t\t\t/* Pointer to working buffer */\n\tUINT len\t\t\t/* Size of working buffer */\n)\n{\n\tconst UINT n_fats = 1;\t\t/* Number of FATs for FAT12/16/32 volume (1 or 2) */\n\tconst UINT n_rootdir = 512;\t/* Number of root directory entries for FAT12/16 volume */\n\tstatic const WORD cst[] = {1, 4, 16, 64, 256, 512, 0};\t/* Cluster size boundary for FAT12/16 volume (4Ks unit) */\n\tstatic const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0};\t/* Cluster size boundary for FAT32 volume (128Ks unit) */\n\tBYTE fmt, sys, *buf, *pte, pdrv, part;\n\tWORD ss;\n\tDWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;\n\tDWORD b_vol, b_fat, b_data;\t\t\t\t/* Base LBA for volume, fat, data */\n\tDWORD sz_vol, sz_rsv, sz_fat, sz_dir;\t/* Size for volume, fat, dir, data */\n\tUINT i;\n\tint vol;\n\tDSTATUS stat;\n#if _USE_TRIM || _FS_EXFAT\n\tDWORD tbl[3];\n#endif\n\n\n\t/* Check mounted drive and clear work area */\n\tvol = get_ldnumber(&path);\t\t\t\t\t/* Get target logical drive */\n\tif (vol < 0) return FR_INVALID_DRIVE;\n\tif (FatFs[vol]) FatFs[vol]->fs_type = 0;\t/* Clear the volume */\n\tpdrv = LD2PD(vol);\t/* Physical drive */\n\tpart = LD2PT(vol);\t/* Partition (0:create as new, 1-4:get from partition table) */\n\n\t/* Check physical drive status */\n\tstat = disk_initialize(pdrv);\n\tif (stat & STA_NOINIT) return FR_NOT_READY;\n\tif (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\n\tif (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1;\t/* Erase block to align data area */\n#if _MAX_SS != _MIN_SS\t\t/* Get sector size of the medium if variable sector size cfg. */\n\tif (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;\n\tif (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;\n#else\n\tss = _MAX_SS;\n#endif\n\tif ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER;\t/* Check if au is valid */\n\tau /= ss;\t/* Cluster size in unit of sector */\n\n\t/* Get working buffer */\n\tbuf = (BYTE*)work;\t\t/* Working buffer */\n\tsz_buf = len / ss;\t\t/* Size of working buffer (sector) */\n\tszb_buf = sz_buf * ss;\t/* Size of working buffer (byte) */\n\tif (!szb_buf) return FR_MKFS_ABORTED;\n\n\t/* Determine where the volume to be located (b_vol, sz_vol) */\n\tif (_MULTI_PARTITION && part != 0) {\n\t\t/* Get partition information from partition table in the MBR */\n\t\tif (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;\t/* Load MBR */\n\t\tif (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;\t/* Check if MBR is valid */\n\t\tpte = buf + (MBR_Table + (part - 1) * SZ_PTE);\n\t\tif (!pte[PTE_System]) return FR_MKFS_ABORTED;\t/* No partition? */\n\t\tb_vol = ld_dword(pte + PTE_StLba);\t\t/* Get volume start sector */\n\t\tsz_vol = ld_dword(pte + PTE_SizLba);\t/* Get volume size */\n\t} else {\n\t\t/* Create a single-partition in this function */\n\t\tif (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR;\n\t\tb_vol = (opt & FM_SFD) ? 0 : 63;\t\t/* Volume start sector */\n\t\tif (sz_vol < b_vol) return FR_MKFS_ABORTED;\n\t\tsz_vol -= b_vol;\t\t\t\t\t\t/* Volume size */\n\t}\n\tif (sz_vol < 128) return FR_MKFS_ABORTED;\t/* Check if volume size is >=128s */\n\n\t/* Pre-determine the FAT type */\n\tdo {\n\t\tif (_FS_EXFAT && (opt & FM_EXFAT)) {\t/* exFAT possible? */\n\t\t\tif ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) {\t/* exFAT only, vol >= 64Ms or au > 128s ? */\n\t\t\t\tfmt = FS_EXFAT; break;\n\t\t\t}\n\t\t}\n\t\tif (au > 128) return FR_INVALID_PARAMETER;\t/* Too large au for FAT/FAT32 */\n\t\tif (opt & FM_FAT32) {\t/* FAT32 possible? */\n\t\t\tif ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) {\t/* FAT32 only or no-FAT? */\n\t\t\t\tfmt = FS_FAT32; break;\n\t\t\t}\n\t\t}\n\t\tif (!(opt & FM_FAT)) return FR_INVALID_PARAMETER;\t/* no-FAT? */\n\t\tfmt = FS_FAT16;\n\t} while (0);\n\n#if _FS_EXFAT\n\tif (fmt == FS_EXFAT) {\t/* Create an exFAT volume */\n\t\tDWORD szb_bit, szb_case, sum, nb, cl;\n\t\tWCHAR ch, si;\n\t\tUINT j, st;\n\t\tBYTE b;\n\n\t\tif (sz_vol < 0x1000) return FR_MKFS_ABORTED;\t/* Too small volume? */\n#if _USE_TRIM\n\t\ttbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1;\t/* Inform the device the volume area may be erased */\n\t\tdisk_ioctl(pdrv, CTRL_TRIM, tbl);\n#endif\n\t\t/* Determine FAT location, data location and number of clusters */\n\t\tif (!au) {\t/* au auto-selection */\n\t\t\tau = 8;\n\t\t\tif (sz_vol >= 0x80000) au = 64;\t\t/* >= 512Ks */\n\t\t\tif (sz_vol >= 0x4000000) au = 256;\t/* >= 64Ms */\n\t\t}\n\t\tb_fat = b_vol + 32;\t\t\t\t\t\t\t\t\t\t/* FAT start at offset 32 */\n\t\tsz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss;\t\t\t/* Number of FAT sectors */\n\t\tb_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1);\t/* Align data area to the erase block boundary */\n\t\tif (b_data >= sz_vol / 2) return FR_MKFS_ABORTED;\t\t/* Too small volume? */\n\t\tn_clst = (sz_vol - (b_data - b_vol)) / au;\t\t\t\t/* Number of clusters */\n\t\tif (n_clst <16) return FR_MKFS_ABORTED;\t\t\t\t\t/* Too few clusters? */\n\t\tif (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED;\t\t\t/* Too many clusters? */\n\n\t\tszb_bit = (n_clst + 7) / 8;\t\t\t\t\t\t/* Size of allocation bitmap */\n\t\ttbl[0] = (szb_bit + au * ss - 1) / (au * ss);\t/* Number of allocation bitmap clusters */\n\n\t\t/* Create a compressed up-case table */\n\t\tsect = b_data + au * tbl[0];\t/* Table start sector */\n\t\tsum = 0;\t\t\t\t\t\t/* Table checksum to be stored in the 82 entry */\n\t\tst = si = i = j = szb_case = 0;\n\t\tdo {\n\t\t\tswitch (st) {\n\t\t\tcase 0:\n\t\t\t\tch = ff_wtoupper(si);\t/* Get an up-case char */\n\t\t\t\tif (ch != si) {\n\t\t\t\t\tsi++; break;\t\t/* Store the up-case char if exist */\n\t\t\t\t}\n\t\t\t\tfor (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ;\t/* Get run length of no-case block */\n\t\t\t\tif (j >= 128) {\n\t\t\t\t\tch = 0xFFFF; st = 2; break;\t/* Compress the no-case block if run is >= 128 */\n\t\t\t\t}\n\t\t\t\tst = 1;\t\t\t/* Do not compress short run */\n\t\t\t\t__attribute__ ((fallthrough));\n\t\t\tcase 1:\n\t\t\t\tch = si++;\t\t/* Fill the short run */\n\t\t\t\tif (--j == 0) st = 0;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tch = (WCHAR)j; si += j;\t/* Number of chars to skip */\n\t\t\t\tst = 0;\n\t\t\t}\n\t\t\tsum = xsum32(buf[i + 0] = (BYTE)ch, sum);\t\t/* Put it into the write buffer */\n\t\t\tsum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);\n\t\t\ti += 2; szb_case += 2;\n\t\t\tif (!si || i == szb_buf) {\t\t/* Write buffered data when buffer full or end of process */\n\t\t\t\tn = (i + ss - 1) / ss;\n\t\t\t\tif (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;\n\t\t\t\tsect += n; i = 0;\n\t\t\t}\n\t\t} while (si);\n\t\ttbl[1] = (szb_case + au * ss - 1) / (au * ss);\t/* Number of up-case table clusters */\n\t\ttbl[2] = 1;\t\t\t\t\t\t\t\t\t\t/* Number of root dir clusters */\n\n\t\t/* Initialize the allocation bitmap */\n\t\tsect = b_data; nsect = (szb_bit + ss - 1) / ss;\t/* Start of bitmap and number of sectors */\n\t\tnb = tbl[0] + tbl[1] + tbl[2];\t\t\t\t\t/* Number of clusters in-use by system */\n\t\tdo {\n\t\t\tmem_set(buf, 0, szb_buf);\n\t\t\tfor (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;\n\t\t\tfor (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;\n\t\t\tn = (nsect > sz_buf) ? sz_buf : nsect;\t\t/* Write the buffered data */\n\t\t\tif (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;\n\t\t\tsect += n; nsect -= n;\n\t\t} while (nsect);\n\n\t\t/* Initialize the FAT */\n\t\tsect = b_fat; nsect = sz_fat;\t/* Start of FAT and number of FAT sectors */\n\t\tj = nb = cl = 0;\n\t\tdo {\n\t\t\tmem_set(buf, 0, szb_buf); i = 0;\t/* Clear work area and reset write index */\n\t\t\tif (cl == 0) {\t/* Set entry 0 and 1 */\n\t\t\t\tst_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;\n\t\t\t\tst_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;\n\t\t\t}\n\t\t\tdo {\t\t\t/* Create chains of bitmap, up-case and root dir */\n\t\t\t\twhile (nb && i < szb_buf) {\t\t\t/* Create a chain */\n\t\t\t\t\tst_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);\n\t\t\t\t\ti += 4; cl++; nb--;\n\t\t\t\t}\n\t\t\t\tif (!nb && j < 3) nb = tbl[j++];\t/* Next chain */\n\t\t\t} while (nb && i < szb_buf);\n\t\t\tn = (nsect > sz_buf) ? sz_buf : nsect;\t/* Write the buffered data */\n\t\t\tif (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;\n\t\t\tsect += n; nsect -= n;\n\t\t} while (nsect);\n\n\t\t/* Initialize the root directory */\n\t\tmem_set(buf, 0, szb_buf);\n\t\tbuf[SZDIRE * 0 + 0] = 0x83;\t\t/* 83 entry (volume label) */\n\t\tbuf[SZDIRE * 1 + 0] = 0x81;\t\t/* 81 entry (allocation bitmap) */\n\t\tst_dword(buf + SZDIRE * 1 + 20, 2);\n\t\tst_dword(buf + SZDIRE * 1 + 24, szb_bit);\n\t\tbuf[SZDIRE * 2 + 0] = 0x82;\t\t/* 82 entry (up-case table) */\n\t\tst_dword(buf + SZDIRE * 2 + 4, sum);\n\t\tst_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]);\n\t\tst_dword(buf + SZDIRE * 2 + 24, szb_case);\n\t\tsect = b_data + au * (tbl[0] + tbl[1]);\tnsect = au;\t/* Start of the root directory and number of sectors */\n\t\tdo {\t/* Fill root directory sectors */\n\t\t\tn = (nsect > sz_buf) ? sz_buf : nsect;\n\t\t\tif (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tsect += n; nsect -= n;\n\t\t} while (nsect);\n\n\t\t/* Create two set of the exFAT VBR blocks */\n\t\tsect = b_vol;\n\t\tfor (n = 0; n < 2; n++) {\n\t\t\t/* Main record (+0) */\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tmem_cpy(buf + BS_JmpBoot, \"\\xEB\\x76\\x90\" \"EXFAT   \", 11);\t/* Boot jump code (x86), OEM name */\n\t\t\tst_dword(buf + BPB_VolOfsEx, b_vol);\t\t\t\t\t/* Volume offset in the physical drive [sector] */\n\t\t\tst_dword(buf + BPB_TotSecEx, sz_vol);\t\t\t\t\t/* Volume size [sector] */\n\t\t\tst_dword(buf + BPB_FatOfsEx, b_fat - b_vol);\t\t\t/* FAT offset [sector] */\n\t\t\tst_dword(buf + BPB_FatSzEx, sz_fat);\t\t\t\t\t/* FAT size [sector] */\n\t\t\tst_dword(buf + BPB_DataOfsEx, b_data - b_vol);\t\t\t/* Data offset [sector] */\n\t\t\tst_dword(buf + BPB_NumClusEx, n_clst);\t\t\t\t\t/* Number of clusters */\n\t\t\tst_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]);\t/* Root dir cluster # */\n\t\t\tst_dword(buf + BPB_VolIDEx, GET_FATTIME());\t\t\t\t/* VSN */\n\t\t\tst_word(buf + BPB_FSVerEx, 0x100);\t\t\t\t\t\t/* File system version (1.00) */\n\t\t\tfor (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ;\t/* Log2 of sector size [byte] */\n\t\t\tfor (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ;\t/* Log2 of cluster size [sector] */\n\t\t\tbuf[BPB_NumFATsEx] = 1;\t\t\t\t\t/* Number of FATs */\n\t\t\tbuf[BPB_DrvNumEx] = 0x80;\t\t\t\t/* Drive number (for int13) */\n\t\t\tst_word(buf + BS_BootCodeEx, 0xFEEB);\t/* Boot code (x86) */\n\t\t\tst_word(buf + BS_55AA, 0xAA55);\t\t\t/* Signature (placed here regardless of sector size) */\n\t\t\tfor (i = sum = 0; i < ss; i++) {\t\t/* VBR checksum */\n\t\t\t\tif (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);\n\t\t\t}\n\t\t\tif (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;\n\t\t\t/* Extended bootstrap record (+1..+8) */\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tst_word(buf + ss - 2, 0xAA55);\t/* Signature (placed at end of sector) */\n\t\t\tfor (j = 1; j < 9; j++) {\n\t\t\t\tfor (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;\t/* VBR checksum */\n\t\t\t\tif (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;\n\t\t\t}\n\t\t\t/* OEM/Reserved record (+9..+10) */\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tfor ( ; j < 11; j++) {\n\t\t\t\tfor (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;\t/* VBR checksum */\n\t\t\t\tif (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;\n\t\t\t}\n\t\t\t/* Sum record (+11) */\n\t\t\tfor (i = 0; i < ss; i += 4) st_dword(buf + i, sum);\t\t/* Fill with checksum value */\n\t\t\tif (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;\n\t\t}\n\n\t} else\n#endif\t/* _FS_EXFAT */\n\t{\t/* Create an FAT12/16/32 volume */\n\t\tdo {\n\t\t\tpau = au;\n\t\t\t/* Pre-determine number of clusters and FAT sub-type */\n\t\t\tif (fmt == FS_FAT32) {\t/* FAT32 volume */\n\t\t\t\tif (!pau) {\t/* au auto-selection */\n\t\t\t\t\tn = sz_vol / 0x20000;\t/* Volume size in unit of 128KS */\n\t\t\t\t\tfor (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ;\t/* Get from table */\n\t\t\t\t}\n\t\t\t\tn_clst = sz_vol / pau;\t/* Number of clusters */\n\t\t\t\tsz_fat = (n_clst * 4 + 8 + ss - 1) / ss;\t/* FAT size [sector] */\n\t\t\t\tsz_rsv = 32;\t/* Number of reserved sectors */\n\t\t\t\tsz_dir = 0;\t\t/* No static directory */\n\t\t\t\tif (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED;\n\t\t\t} else {\t\t\t\t/* FAT12/16 volume */\n\t\t\t\tif (!pau) {\t/* au auto-selection */\n\t\t\t\t\tn = sz_vol / 0x1000;\t/* Volume size in unit of 4KS */\n\t\t\t\t\tfor (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ;\t/* Get from table */\n\t\t\t\t}\n\t\t\t\tn_clst = sz_vol / pau;\n\t\t\t\tif (n_clst > MAX_FAT12) {\n\t\t\t\t\tn = n_clst * 2 + 4;\t\t/* FAT size [byte] */\n\t\t\t\t} else {\n\t\t\t\t\tfmt = FS_FAT12;\n\t\t\t\t\tn = (n_clst * 3 + 1) / 2 + 3;\t/* FAT size [byte] */\n\t\t\t\t}\n\t\t\t\tsz_fat = (n + ss - 1) / ss;\t\t/* FAT size [sector] */\n\t\t\t\tsz_rsv = 1;\t\t\t\t\t\t/* Number of reserved sectors */\n\t\t\t\tsz_dir = (DWORD)n_rootdir * SZDIRE / ss;\t/* Rootdir size [sector] */\n\t\t\t}\n\t\t\tb_fat = b_vol + sz_rsv;\t\t\t\t\t\t/* FAT base */\n\t\t\tb_data = b_fat + sz_fat * n_fats + sz_dir;\t/* Data base */\n\n\t\t\t/* Align data base to erase block boundary (for flash memory media) */\n\t\t\tn = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data;\t/* Next nearest erase block from current data base */\n\t\t\tif (fmt == FS_FAT32) {\t\t/* FAT32: Move FAT base */\n\t\t\t\tsz_rsv += n; b_fat += n;\n\t\t\t} else {\t\t\t\t\t/* FAT12/16: Expand FAT size */\n\t\t\t\tsz_fat += n / n_fats;\n\t\t\t}\n\n\t\t\t/* Determine number of clusters and final check of validity of the FAT sub-type */\n\t\t\tif (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED;\t/* Too small volume */\n\t\t\tn_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;\n\t\t\tif (fmt == FS_FAT32) {\n\t\t\t\tif (n_clst <= MAX_FAT16) {\t/* Too few clusters for FAT32 */\n\t\t\t\t\tif (!au && (au = pau / 2) != 0) continue;\t/* Adjust cluster size and retry */\n\t\t\t\t\treturn FR_MKFS_ABORTED;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (fmt == FS_FAT16) {\n\t\t\t\tif (n_clst > MAX_FAT16) {\t/* Too many clusters for FAT16 */\n\t\t\t\t\tif (!au && (pau * 2) <= 64) {\n\t\t\t\t\t\tau = pau * 2; continue;\t\t/* Adjust cluster size and retry */\n\t\t\t\t\t}\n\t\t\t\t\tif ((opt & FM_FAT32)) {\n\t\t\t\t\t\tfmt = FS_FAT32; continue;\t/* Switch type to FAT32 and retry */\n\t\t\t\t\t}\n\t\t\t\t\tif (!au && (au = pau * 2) <= 128) continue;\t/* Adjust cluster size and retry */\n\t\t\t\t\treturn FR_MKFS_ABORTED;\n\t\t\t\t}\n\t\t\t\tif  (n_clst <= MAX_FAT12) {\t/* Too few clusters for FAT16 */\n\t\t\t\t\tif (!au && (au = pau * 2) <= 128) continue;\t/* Adjust cluster size and retry */\n\t\t\t\t\treturn FR_MKFS_ABORTED;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED;\t/* Too many clusters for FAT12 */\n\n\t\t\t/* Ok, it is the valid cluster configuration */\n\t\t\tbreak;\n\t\t} while (1);\n\n#if _USE_TRIM\n\t\ttbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1;\t/* Inform the device the volume area can be erased */\n\t\tdisk_ioctl(pdrv, CTRL_TRIM, tbl);\n#endif\n\t\t/* Create FAT VBR */\n\t\tmem_set(buf, 0, ss);\n\t\tmem_cpy(buf + BS_JmpBoot, \"\\xEB\\xFE\\x90\" \"MSDOS5.0\", 11);/* Boot jump code (x86), OEM name */\n\t\tst_word(buf + BPB_BytsPerSec, ss);\t\t\t\t/* Sector size [byte] */\n\t\tbuf[BPB_SecPerClus] = (BYTE)pau;\t\t\t\t/* Cluster size [sector] */\n\t\tst_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv);\t/* Size of reserved area */\n\t\tbuf[BPB_NumFATs] = (BYTE)n_fats;\t\t\t\t/* Number of FATs */\n\t\tst_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir));\t/* Number of root directory entries */\n\t\tif (sz_vol < 0x10000) {\n\t\t\tst_word(buf + BPB_TotSec16, (WORD)sz_vol);\t/* Volume size in 16-bit LBA */\n\t\t} else {\n\t\t\tst_dword(buf + BPB_TotSec32, sz_vol);\t\t/* Volume size in 32-bit LBA */\n\t\t}\n\t\tbuf[BPB_Media] = 0xF8;\t\t\t\t\t\t\t/* Media descriptor byte */\n\t\tst_word(buf + BPB_SecPerTrk, 63);\t\t\t\t/* Number of sectors per track (for int13) */\n\t\tst_word(buf + BPB_NumHeads, 255);\t\t\t\t/* Number of heads (for int13) */\n\t\tst_dword(buf + BPB_HiddSec, b_vol);\t\t\t\t/* Volume offset in the physical drive [sector] */\n\t\tif (fmt == FS_FAT32) {\n\t\t\tst_dword(buf + BS_VolID32, GET_FATTIME());\t/* VSN */\n\t\t\tst_dword(buf + BPB_FATSz32, sz_fat);\t\t/* FAT size [sector] */\n\t\t\tst_dword(buf + BPB_RootClus32, 2);\t\t\t/* Root directory cluster # (2) */\n\t\t\tst_word(buf + BPB_FSInfo32, 1);\t\t\t\t/* Offset of FSINFO sector (VBR + 1) */\n\t\t\tst_word(buf + BPB_BkBootSec32, 6);\t\t\t/* Offset of backup VBR (VBR + 6) */\n\t\t\tbuf[BS_DrvNum32] = 0x80;\t\t\t\t\t/* Drive number (for int13) */\n\t\t\tbuf[BS_BootSig32] = 0x29;\t\t\t\t\t/* Extended boot signature */\n\t\t\tmem_cpy(buf + BS_VolLab32, \"NO NAME    \" \"FAT32   \", 19);\t/* Volume label, FAT signature */\n\t\t} else {\n\t\t\tst_dword(buf + BS_VolID, GET_FATTIME());\t/* VSN */\n\t\t\tst_word(buf + BPB_FATSz16, (WORD)sz_fat);\t/* FAT size [sector] */\n\t\t\tbuf[BS_DrvNum] = 0x80;\t\t\t\t\t\t/* Drive number (for int13) */\n\t\t\tbuf[BS_BootSig] = 0x29;\t\t\t\t\t\t/* Extended boot signature */\n\t\t\tmem_cpy(buf + BS_VolLab, \"NO NAME    \" \"FAT     \", 19);\t/* Volume label, FAT signature */\n\t\t}\n\t\tst_word(buf + BS_55AA, 0xAA55);\t\t\t\t\t/* Signature (offset is fixed here regardless of sector size) */\n\t\tif (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR;\t/* Write it to the VBR sector */\n\n\t\t/* Create FSINFO record if needed */\n\t\tif (fmt == FS_FAT32) {\n\t\t\tdisk_write(pdrv, buf, b_vol + 6, 1);\t\t/* Write backup VBR (VBR + 6) */\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tst_dword(buf + FSI_LeadSig, 0x41615252);\n\t\t\tst_dword(buf + FSI_StrucSig, 0x61417272);\n\t\t\tst_dword(buf + FSI_Free_Count, n_clst - 1);\t/* Number of free clusters */\n\t\t\tst_dword(buf + FSI_Nxt_Free, 2);\t\t\t/* Last allocated cluster# */\n\t\t\tst_word(buf + BS_55AA, 0xAA55);\n\t\t\tdisk_write(pdrv, buf, b_vol + 7, 1);\t\t/* Write backup FSINFO (VBR + 7) */\n\t\t\tdisk_write(pdrv, buf, b_vol + 1, 1);\t\t/* Write original FSINFO (VBR + 1) */\n\t\t}\n\n\t\t/* Initialize FAT area */\n\t\tmem_set(buf, 0, (UINT)szb_buf);\n\t\tsect = b_fat;\t\t/* FAT start sector */\n\t\tfor (i = 0; i < n_fats; i++) {\t\t\t/* Initialize FATs each */\n\t\t\tif (fmt == FS_FAT32) {\n\t\t\t\tst_dword(buf + 0, 0xFFFFFFF8);\t/* Entry 0 */\n\t\t\t\tst_dword(buf + 4, 0xFFFFFFFF);\t/* Entry 1 */\n\t\t\t\tst_dword(buf + 8, 0x0FFFFFFF);\t/* Entry 2 (root directory) */\n\t\t\t} else {\n\t\t\t\tst_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8);\t/* Entry 0 and 1 */\n\t\t\t}\n\t\t\tnsect = sz_fat;\t\t/* Number of FAT sectors */\n\t\t\tdo {\t/* Fill FAT sectors */\n\t\t\t\tn = (nsect > sz_buf) ? sz_buf : nsect;\n\t\t\t\tif (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;\n\t\t\t\tmem_set(buf, 0, ss);\n\t\t\t\tsect += n; nsect -= n;\n\t\t\t} while (nsect);\n\t\t}\n\n\t\t/* Initialize root directory (fill with zero) */\n\t\tnsect = (fmt == FS_FAT32) ? pau : sz_dir;\t/* Number of root directory sectors */\n\t\tdo {\n\t\t\tn = (nsect > sz_buf) ? sz_buf : nsect;\n\t\t\tif (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;\n\t\t\tsect += n; nsect -= n;\n\t\t} while (nsect);\n\t}\n\n\t/* Determine system ID in the partition table */\n\tif (_FS_EXFAT && fmt == FS_EXFAT) {\n\t\tsys = 0x07;\t\t\t/* HPFS/NTFS/exFAT */\n\t} else {\n\t\tif (fmt == FS_FAT32) {\n\t\t\tsys = 0x0C;\t\t/* FAT32X */\n\t\t} else {\n\t\t\tif (sz_vol >= 0x10000) {\n\t\t\t\tsys = 0x06;\t/* FAT12/16 (>=64KS) */\n\t\t\t} else {\n\t\t\t\tsys = (fmt == FS_FAT16) ? 0x04 : 0x01;\t/* FAT16 (<64KS) : FAT12 (<64KS) */\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Update partition information */\n\tif (_MULTI_PARTITION && part != 0) {\t/* Created in the existing partition */\n\t\t/* Update system ID in the partition table */\n\t\tif (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;\t/* Read the MBR */\n\t\tbuf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys;\t\t/* Set system ID */\n\t\tif (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;\t/* Write it back to the MBR */\n\t} else {\t\t\t\t\t\t\t\t/* Created as a new single partition */\n\t\tif (!(opt & FM_SFD)) {\t/* Create partition table if in FDISK format */\n\t\t\tmem_set(buf, 0, ss);\n\t\t\tst_word(buf + BS_55AA, 0xAA55);\t\t/* MBR signature */\n\t\t\tpte = buf + MBR_Table;\t\t\t\t/* Create partition table for single partition in the drive */\n\t\t\tpte[PTE_Boot] = 0;\t\t\t\t\t/* Boot indicator */\n\t\t\tpte[PTE_StHead] = 1;\t\t\t\t/* Start head */\n\t\t\tpte[PTE_StSec] = 1;\t\t\t\t\t/* Start sector */\n\t\t\tpte[PTE_StCyl] = 0;\t\t\t\t\t/* Start cylinder */\n\t\t\tpte[PTE_System] = sys;\t\t\t\t/* System type */\n\t\t\tn = (b_vol + sz_vol) / (63 * 255);\t/* (End CHS may be invalid) */\n\t\t\tpte[PTE_EdHead] = 254;\t\t\t\t/* End head */\n\t\t\tpte[PTE_EdSec] = (BYTE)(n >> 2 | 63);\t/* End sector */\n\t\t\tpte[PTE_EdCyl] = (BYTE)n;\t\t\t/* End cylinder */\n\t\t\tst_dword(pte + PTE_StLba, b_vol);\t/* Start offset in LBA */\n\t\t\tst_dword(pte + PTE_SizLba, sz_vol);\t/* Size in sectors */\n\t\t\tif (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;\t/* Write it to the MBR */\n\t\t}\n\t}\n\n\tif (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR;\n\n\treturn FR_OK;\n}\n\n\n\n#if _MULTI_PARTITION\n/*-----------------------------------------------------------------------*/\n/* Create partition table on the physical drive                          */\n/*-----------------------------------------------------------------------*/\n\nFRESULT f_fdisk (\n\tBYTE pdrv,\t\t\t/* Physical drive number */\n\tconst DWORD* szt,\t/* Pointer to the size table for each partitions */\n\tvoid* work\t\t\t/* Pointer to the working buffer */\n)\n{\n\tUINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;\n\tBYTE s_hd, e_hd, *p, *buf = (BYTE*)work;\n\tDSTATUS stat;\n\tDWORD sz_disk, sz_part, s_part;\n\n\n\tstat = disk_initialize(pdrv);\n\tif (stat & STA_NOINIT) return FR_NOT_READY;\n\tif (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\n\tif (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;\n\n\t/* Determine the CHS without any consideration of the drive geometry */\n\tfor (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;\n\tif (n == 256) n--;\n\te_hd = n - 1;\n\tsz_cyl = 63 * n;\n\ttot_cyl = sz_disk / sz_cyl;\n\n\t/* Create partition table */\n\tmem_set(buf, 0, _MAX_SS);\n\tp = buf + MBR_Table; b_cyl = 0;\n\tfor (i = 0; i < 4; i++, p += SZ_PTE) {\n\t\tp_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;\t/* Number of cylinders */\n\t\tif (!p_cyl) continue;\n\t\ts_part = (DWORD)sz_cyl * b_cyl;\n\t\tsz_part = (DWORD)sz_cyl * p_cyl;\n\t\tif (i == 0) {\t/* Exclude first track of cylinder 0 */\n\t\t\ts_hd = 1;\n\t\t\ts_part += 63; sz_part -= 63;\n\t\t} else {\n\t\t\ts_hd = 0;\n\t\t}\n\t\te_cyl = b_cyl + p_cyl - 1;\t/* End cylinder */\n\t\tif (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;\n\n\t\t/* Set partition table */\n\t\tp[1] = s_hd;\t\t\t\t\t\t/* Start head */\n\t\tp[2] = (BYTE)((b_cyl >> 2) + 1);\t/* Start sector */\n\t\tp[3] = (BYTE)b_cyl;\t\t\t\t\t/* Start cylinder */\n\t\tp[4] = 0x07;\t\t\t\t\t\t/* System type (temporary setting) */\n\t\tp[5] = e_hd;\t\t\t\t\t\t/* End head */\n\t\tp[6] = (BYTE)((e_cyl >> 2) + 63);\t/* End sector */\n\t\tp[7] = (BYTE)e_cyl;\t\t\t\t\t/* End cylinder */\n\t\tst_dword(p + 8, s_part);\t\t\t/* Start sector in LBA */\n\t\tst_dword(p + 12, sz_part);\t\t\t/* Number of sectors */\n\n\t\t/* Next partition */\n\t\tb_cyl += p_cyl;\n\t}\n\tst_word(p, 0xAA55);\n\n\t/* Write it to the MBR */\n\treturn (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK;\n}\n\n#endif /* _MULTI_PARTITION */\n#endif /* _USE_MKFS && !_FS_READONLY */\n\n\n\n\n#if _USE_STRFUNC\n/*-----------------------------------------------------------------------*/\n/* Get a string from the file                                            */\n/*-----------------------------------------------------------------------*/\n\nTCHAR* f_gets (\n\tTCHAR* buff,\t/* Pointer to the string buffer to read */\n\tint len,\t\t/* Size of string buffer (characters) */\n\tFIL* fp\t\t\t/* Pointer to the file object */\n)\n{\n\tint n = 0;\n\tTCHAR c, *p = buff;\n\tBYTE s[2];\n\tUINT rc;\n\n\n\twhile (n < len - 1) {\t/* Read characters until buffer gets filled */\n#if _LFN_UNICODE\n#if _STRF_ENCODE == 3\t\t/* Read a character in UTF-8 */\n\t\tf_read(fp, s, 1, &rc);\n\t\tif (rc != 1) break;\n\t\tc = s[0];\n\t\tif (c >= 0x80) {\n\t\t\tif (c < 0xC0) continue;\t/* Skip stray trailer */\n\t\t\tif (c < 0xE0) {\t\t\t/* Two-byte sequence (0x80-0x7FF) */\n\t\t\t\tf_read(fp, s, 1, &rc);\n\t\t\t\tif (rc != 1) break;\n\t\t\t\tc = (c & 0x1F) << 6 | (s[0] & 0x3F);\n\t\t\t\tif (c < 0x80) c = '?';\t/* Reject invalid code range */\n\t\t\t} else {\n\t\t\t\tif (c < 0xF0) {\t\t/* Three-byte sequence (0x800-0xFFFF) */\n\t\t\t\t\tf_read(fp, s, 2, &rc);\n\t\t\t\t\tif (rc != 2) break;\n\t\t\t\t\tc = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);\n\t\t\t\t\tif (c < 0x800) c = '?';\t/* Reject invalid code range */\n\t\t\t\t} else {\t\t\t/* Reject four-byte sequence */\n\t\t\t\t\tc = '?';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#elif _STRF_ENCODE == 2\t\t/* Read a character in UTF-16BE */\n\t\tf_read(fp, s, 2, &rc);\n\t\tif (rc != 2) break;\n\t\tc = s[1] + (s[0] << 8);\n#elif _STRF_ENCODE == 1\t\t/* Read a character in UTF-16LE */\n\t\tf_read(fp, s, 2, &rc);\n\t\tif (rc != 2) break;\n\t\tc = s[0] + (s[1] << 8);\n#else\t\t\t\t\t\t/* Read a character in ANSI/OEM */\n\t\tf_read(fp, s, 1, &rc);\n\t\tif (rc != 1) break;\n\t\tc = s[0];\n\t\tif (IsDBCS1(c)) {\n\t\t\tf_read(fp, s, 1, &rc);\n\t\t\tif (rc != 1) break;\n\t\t\tc = (c << 8) + s[0];\n\t\t}\n\t\tc = ff_convert(c, 1);\t/* OEM -> Unicode */\n\t\tif (!c) c = '?';\n#endif\n#else\t\t\t\t\t\t/* Read a character without conversion */\n\t\tf_read(fp, s, 1, &rc);\n\t\tif (rc != 1) break;\n\t\tc = s[0];\n#endif\n\t\tif (_USE_STRFUNC == 2 && c == '\\r') continue;\t/* Strip '\\r' */\n\t\t*p++ = c;\n\t\tn++;\n\t\tif (c == '\\n') break;\t\t/* Break on EOL */\n\t}\n\t*p = 0;\n\treturn n ? buff : 0;\t\t\t/* When no data read (eof or error), return with error. */\n}\n\n\n\n\n#if !_FS_READONLY\n#include <stdarg.h>\n/*-----------------------------------------------------------------------*/\n/* Put a character to the file                                           */\n/*-----------------------------------------------------------------------*/\n\ntypedef struct {\n\tFIL *fp;\t\t/* Ptr to the writing file */\n\tint idx, nchr;\t/* Write index of buf[] (-1:error), number of chars written */\n\tBYTE buf[64];\t/* Write buffer */\n} putbuff;\n\n\nstatic\nvoid putc_bfd (\t\t/* Buffered write with code conversion */\n\tputbuff* pb,\n\tTCHAR c\n)\n{\n\tUINT bw;\n\tint i;\n\n\n\tif (_USE_STRFUNC == 2 && c == '\\n') {\t /* LF -> CRLF conversion */\n\t\tputc_bfd(pb, '\\r');\n\t}\n\n\ti = pb->idx;\t\t/* Write index of pb->buf[] */\n\tif (i < 0) return;\n\n#if _LFN_UNICODE\n#if _STRF_ENCODE == 3\t\t\t/* Write a character in UTF-8 */\n\tif (c < 0x80) {\t\t\t\t/* 7-bit */\n\t\tpb->buf[i++] = (BYTE)c;\n\t} else {\n\t\tif (c < 0x800) {\t\t/* 11-bit */\n\t\t\tpb->buf[i++] = (BYTE)(0xC0 | c >> 6);\n\t\t} else {\t\t\t\t/* 16-bit */\n\t\t\tpb->buf[i++] = (BYTE)(0xE0 | c >> 12);\n\t\t\tpb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));\n\t\t}\n\t\tpb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));\n\t}\n#elif _STRF_ENCODE == 2\t\t\t/* Write a character in UTF-16BE */\n\tpb->buf[i++] = (BYTE)(c >> 8);\n\tpb->buf[i++] = (BYTE)c;\n#elif _STRF_ENCODE == 1\t\t\t/* Write a character in UTF-16LE */\n\tpb->buf[i++] = (BYTE)c;\n\tpb->buf[i++] = (BYTE)(c >> 8);\n#else\t\t\t\t\t\t\t/* Write a character in ANSI/OEM */\n\tc = ff_convert(c, 0);\t/* Unicode -> OEM */\n\tif (!c) c = '?';\n\tif (c >= 0x100)\n\t\tpb->buf[i++] = (BYTE)(c >> 8);\n\tpb->buf[i++] = (BYTE)c;\n#endif\n#else\t\t\t\t\t\t\t/* Write a character without conversion */\n\tpb->buf[i++] = (BYTE)c;\n#endif\n\n\tif (i >= (int)(sizeof pb->buf) - 3) {\t/* Write buffered characters to the file */\n\t\tf_write(pb->fp, pb->buf, (UINT)i, &bw);\n\t\ti = (bw == (UINT)i) ? 0 : -1;\n\t}\n\tpb->idx = i;\n\tpb->nchr++;\n}\n\n\nstatic\nint putc_flush (\t\t/* Flush left characters in the buffer */\n\tputbuff* pb\n)\n{\n\tUINT nw;\n\n\tif (   pb->idx >= 0\t/* Flush buffered characters to the file */\n\t\t&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK\n\t\t&& (UINT)pb->idx == nw) return pb->nchr;\n\treturn EOF;\n}\n\n\nstatic\nvoid putc_init (\t\t/* Initialize write buffer */\n\tputbuff* pb,\n\tFIL* fp\n)\n{\n\tpb->fp = fp;\n\tpb->nchr = pb->idx = 0;\n}\n\n\n\nint f_putc (\n\tTCHAR c,\t/* A character to be output */\n\tFIL* fp\t\t/* Pointer to the file object */\n)\n{\n\tputbuff pb;\n\n\n\tputc_init(&pb, fp);\n\tputc_bfd(&pb, c);\t/* Put the character */\n\treturn putc_flush(&pb);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Put a string to the file                                              */\n/*-----------------------------------------------------------------------*/\n\nint f_puts (\n\tconst TCHAR* str,\t/* Pointer to the string to be output */\n\tFIL* fp\t\t\t\t/* Pointer to the file object */\n)\n{\n\tputbuff pb;\n\n\n\tputc_init(&pb, fp);\n\twhile (*str) putc_bfd(&pb, *str++);\t\t/* Put the string */\n\treturn putc_flush(&pb);\n}\n\n\n\n\n/*-----------------------------------------------------------------------*/\n/* Put a formatted string to the file                                    */\n/*-----------------------------------------------------------------------*/\n\nint f_printf (\n\tFIL* fp,\t\t\t/* Pointer to the file object */\n\tconst TCHAR* fmt,\t/* Pointer to the format string */\n\t...\t\t\t\t\t/* Optional arguments... */\n)\n{\n\tva_list arp;\n\tputbuff pb;\n\tBYTE f, r;\n\tUINT i, j, w;\n\tDWORD v;\n\tTCHAR c, d, str[32], *p;\n\n\n\tputc_init(&pb, fp);\n\n\tva_start(arp, fmt);\n\n\tfor (;;) {\n\t\tc = *fmt++;\n\t\tif (c == 0) break;\t\t\t/* End of string */\n\t\tif (c != '%') {\t\t\t\t/* Non escape character */\n\t\t\tputc_bfd(&pb, c);\n\t\t\tcontinue;\n\t\t}\n\t\tw = f = 0;\n\t\tc = *fmt++;\n\t\tif (c == '0') {\t\t\t\t/* Flag: '0' padding */\n\t\t\tf = 1; c = *fmt++;\n\t\t} else {\n\t\t\tif (c == '-') {\t\t\t/* Flag: left justified */\n\t\t\t\tf = 2; c = *fmt++;\n\t\t\t}\n\t\t}\n\t\twhile (IsDigit(c)) {\t\t/* Precision */\n\t\t\tw = w * 10 + c - '0';\n\t\t\tc = *fmt++;\n\t\t}\n\t\tif (c == 'l' || c == 'L') {\t/* Prefix: Size is long int */\n\t\t\tf |= 4; c = *fmt++;\n\t\t}\n\t\tif (!c) break;\n\t\td = c;\n\t\tif (IsLower(d)) d -= 0x20;\n\t\tswitch (d) {\t\t\t\t/* Type is... */\n\t\tcase 'S' :\t\t\t\t\t/* String */\n\t\t\tp = va_arg(arp, TCHAR*);\n\t\t\tfor (j = 0; p[j]; j++) ;\n\t\t\tif (!(f & 2)) {\n\t\t\t\twhile (j++ < w) putc_bfd(&pb, ' ');\n\t\t\t}\n\t\t\twhile (*p) putc_bfd(&pb, *p++);\n\t\t\twhile (j++ < w) putc_bfd(&pb, ' ');\n\t\t\tcontinue;\n\n\t\tcase 'C' :\t\t\t\t\t/* Character */\n\t\t\tputc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;\n\n\t\tcase 'B' :\t\t\t\t\t/* Binary */\n\t\t\tr = 2; break;\n\n\t\tcase 'O' :\t\t\t\t\t/* Octal */\n\t\t\tr = 8; break;\n\n\t\tcase 'D' :\t\t\t\t\t/* Signed decimal */\n\t\tcase 'U' :\t\t\t\t\t/* Unsigned decimal */\n\t\t\tr = 10; break;\n\n\t\tcase 'X' :\t\t\t\t\t/* Hexdecimal */\n\t\t\tr = 16; break;\n\n\t\tdefault:\t\t\t\t\t/* Unknown type (pass-through) */\n\t\t\tputc_bfd(&pb, c); continue;\n\t\t}\n\n\t\t/* Get an argument and put it in numeral */\n\t\tv = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));\n\t\tif (d == 'D' && (v & 0x80000000)) {\n\t\t\tv = 0 - v;\n\t\t\tf |= 8;\n\t\t}\n\t\ti = 0;\n\t\tdo {\n\t\t\td = (TCHAR)(v % r); v /= r;\n\t\t\tif (d > 9) d += (c == 'x') ? 0x27 : 0x07;\n\t\t\tstr[i++] = d + '0';\n\t\t} while (v && i < sizeof str / sizeof str[0]);\n\t\tif (f & 8) str[i++] = '-';\n\t\tj = i; d = (f & 1) ? '0' : ' ';\n\t\twhile (!(f & 2) && j++ < w) putc_bfd(&pb, d);\n\t\tdo {\n\t\t\tputc_bfd(&pb, str[--i]);\n\t\t} while (i);\n\t\twhile (j++ < w) putc_bfd(&pb, d);\n\t}\n\n\tva_end(arp);\n\n\treturn putc_flush(&pb);\n}\n\n#endif /* !_FS_READONLY */\n#endif /* _USE_STRFUNC */\n"
  },
  {
    "path": "lib/fatfs/ff.h",
    "content": "/*----------------------------------------------------------------------------/\n/  FatFs - Generic FAT file system module  R0.12c                             /\n/-----------------------------------------------------------------------------/\n/\n/ Copyright (C) 2017, ChaN, all right reserved.\n/\n/ FatFs module is an open source software. Redistribution and use of FatFs in\n/ source and binary forms, with or without modification, are permitted provided\n/ that the following condition is met:\n\n/ 1. Redistributions of source code must retain the above copyright notice,\n/    this condition and the following disclaimer.\n/\n/ This software is provided by the copyright holder and contributors \"AS IS\"\n/ and any warranties related to this software are DISCLAIMED.\n/ The copyright owner or contributors be NOT LIABLE for any damages caused\n/ by use of this software.\n/----------------------------------------------------------------------------*/\n\n\n#ifndef _FATFS\n#define _FATFS\t68300\t/* Revision ID */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"integer.h\"\t/* Basic integer types */\n#include \"ffconf.h\"\t\t/* FatFs configuration options */\n\n#if _FATFS != _FFCONF\n#error Wrong configuration file (ffconf.h).\n#endif\n\n\n\n/* Definitions of volume management */\n\n#if _MULTI_PARTITION\t\t/* Multiple partition configuration */\ntypedef struct {\n\tBYTE pd;\t/* Physical drive number */\n\tBYTE pt;\t/* Partition: 0:Auto detect, 1-4:Forced partition) */\n} PARTITION;\nextern PARTITION VolToPart[];\t/* Volume - Partition resolution table */\n#endif\n\n\n\n/* Type of path name strings on FatFs API */\n\n#if _LFN_UNICODE\t\t\t/* Unicode (UTF-16) string */\n#if _USE_LFN == 0\n#error _LFN_UNICODE must be 0 at non-LFN cfg.\n#endif\n#ifndef _INC_TCHAR\ntypedef WCHAR TCHAR;\n#define _T(x) L ## x\n#define _TEXT(x) L ## x\n#endif\n#else\t\t\t\t\t\t/* ANSI/OEM string */\n#ifndef _INC_TCHAR\ntypedef char TCHAR;\n#define _T(x) x\n#define _TEXT(x) x\n#endif\n#endif\n\n\n\n/* Type of file size variables */\n\n#if _FS_EXFAT\n#if _USE_LFN == 0\n#error LFN must be enabled when enable exFAT\n#endif\ntypedef QWORD FSIZE_t;\n#else\ntypedef DWORD FSIZE_t;\n#endif\n\n\n\n/* File system object structure (FATFS) */\n\ntypedef struct {\n\tBYTE\tfs_type;\t\t/* File system type (0:N/A) */\n\tBYTE\tdrv;\t\t\t/* Physical drive number */\n\tBYTE\tn_fats;\t\t\t/* Number of FATs (1 or 2) */\n\tBYTE\twflag;\t\t\t/* win[] flag (b0:dirty) */\n\tBYTE\tfsi_flag;\t\t/* FSINFO flags (b7:disabled, b0:dirty) */\n\tWORD\tid;\t\t\t\t/* File system mount ID */\n\tWORD\tn_rootdir;\t\t/* Number of root directory entries (FAT12/16) */\n\tWORD\tcsize;\t\t\t/* Cluster size [sectors] */\n#if _MAX_SS != _MIN_SS\n\tWORD\tssize;\t\t\t/* Sector size (512, 1024, 2048 or 4096) */\n#endif\n#if _USE_LFN != 0\n\tWCHAR*\tlfnbuf;\t\t\t/* LFN working buffer */\n#endif\n#if _FS_EXFAT\n\tBYTE*\tdirbuf;\t\t\t/* Directory entry block scratchpad buffer */\n#endif\n#if _FS_REENTRANT\n\t_SYNC_t\tsobj;\t\t\t/* Identifier of sync object */\n#endif\n#if !_FS_READONLY\n\tDWORD\tlast_clst;\t\t/* Last allocated cluster */\n\tDWORD\tfree_clst;\t\t/* Number of free clusters */\n#endif\n#if _FS_RPATH != 0\n\tDWORD\tcdir;\t\t\t/* Current directory start cluster (0:root) */\n#if _FS_EXFAT\n\tDWORD\tcdc_scl;\t\t/* Containing directory start cluster (invalid when cdir is 0) */\n\tDWORD\tcdc_size;\t\t/* b31-b8:Size of containing directory, b7-b0: Chain status */\n\tDWORD\tcdc_ofs;\t\t/* Offset in the containing directory (invalid when cdir is 0) */\n#endif\n#endif\n\tDWORD\tn_fatent;\t\t/* Number of FAT entries (number of clusters + 2) */\n\tDWORD\tfsize;\t\t\t/* Size of an FAT [sectors] */\n\tDWORD\tvolbase;\t\t/* Volume base sector */\n\tDWORD\tfatbase;\t\t/* FAT base sector */\n\tDWORD\tdirbase;\t\t/* Root directory base sector/cluster */\n\tDWORD\tdatabase;\t\t/* Data base sector */\n\tDWORD\twinsect;\t\t/* Current sector appearing in the win[] */\n\tBYTE\twin[_MAX_SS];\t/* Disk access window for Directory, FAT (and file data at tiny cfg) */\n} FATFS;\n\n\n\n/* Object ID and allocation information (_FDID) */\n\ntypedef struct {\n\tFATFS*\tfs;\t\t\t/* Pointer to the owner file system object */\n\tWORD\tid;\t\t\t/* Owner file system mount ID */\n\tBYTE\tattr;\t\t/* Object attribute */\n\tBYTE\tstat;\t\t/* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */\n\tDWORD\tsclust;\t\t/* Object start cluster (0:no cluster or root directory) */\n\tFSIZE_t\tobjsize;\t/* Object size (valid when sclust != 0) */\n#if _FS_EXFAT\n\tDWORD\tn_cont;\t\t/* Size of first fragment, clusters - 1 (valid when stat == 3) */\n\tDWORD\tn_frag;\t\t/* Size of last fragment needs to be written (valid when not zero) */\n\tDWORD\tc_scl;\t\t/* Containing directory start cluster (valid when sclust != 0) */\n\tDWORD\tc_size;\t\t/* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */\n\tDWORD\tc_ofs;\t\t/* Offset in the containing directory (valid when sclust != 0 and non-directory object) */\n#endif\n#if _FS_LOCK != 0\n\tUINT\tlockid;\t\t/* File lock ID origin from 1 (index of file semaphore table Files[]) */\n#endif\n} _FDID;\n\n\n\n/* File object structure (FIL) */\n\ntypedef struct {\n\t_FDID\tobj;\t\t\t/* Object identifier (must be the 1st member to detect invalid object pointer) */\n\tBYTE\tflag;\t\t\t/* File status flags */\n\tBYTE\terr;\t\t\t/* Abort flag (error code) */\n\tFSIZE_t\tfptr;\t\t\t/* File read/write pointer (Zeroed on file open) */\n\tDWORD\tclust;\t\t\t/* Current cluster of fpter (invalid when fptr is 0) */\n\tDWORD\tsect;\t\t\t/* Sector number appearing in buf[] (0:invalid) */\n#if !_FS_READONLY\n\tDWORD\tdir_sect;\t\t/* Sector number containing the directory entry */\n\tBYTE*\tdir_ptr;\t\t/* Pointer to the directory entry in the win[] */\n#endif\n#if _USE_FASTSEEK\n\tDWORD*\tcltbl;\t\t\t/* Pointer to the cluster link map table (nulled on open, set by application) */\n#endif\n#if !_FS_TINY\n\tBYTE\tbuf[_MAX_SS];\t/* File private data read/write window */\n#endif\n} FIL;\n\n\n\n/* Directory object structure (DIR) */\n\ntypedef struct {\n\t_FDID\tobj;\t\t\t/* Object identifier */\n\tDWORD\tdptr;\t\t\t/* Current read/write offset */\n\tDWORD\tclust;\t\t\t/* Current cluster */\n\tDWORD\tsect;\t\t\t/* Current sector (0:Read operation has terminated) */\n\tBYTE*\tdir;\t\t\t/* Pointer to the directory item in the win[] */\n\tBYTE\tfn[12];\t\t\t/* SFN (in/out) {body[8],ext[3],status[1]} */\n#if _USE_LFN != 0\n\tDWORD\tblk_ofs;\t\t/* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */\n#endif\n#if _USE_FIND\n\tconst TCHAR* pat;\t\t/* Pointer to the name matching pattern */\n#endif\n} DIR;\n\n\n\n/* File information structure (FILINFO) */\n\ntypedef struct {\n\tFSIZE_t\tfsize;\t\t\t/* File size */\n\tWORD\tfdate;\t\t\t/* Modified date */\n\tWORD\tftime;\t\t\t/* Modified time */\n\tBYTE\tfattrib;\t\t/* File attribute */\n#if _USE_LFN != 0\n\tTCHAR\taltname[13];\t\t\t/* Alternative file name */\n\tTCHAR\tfname[_MAX_LFN + 1];\t/* Primary file name */\n#else\n\tTCHAR\tfname[13];\t\t/* File name */\n#endif\n} FILINFO;\n\n\n\n/* File function return code (FRESULT) */\n\ntypedef enum {\n\tFR_OK = 0,\t\t\t\t/* (0) Succeeded */\n\tFR_DISK_ERR,\t\t\t/* (1) A hard error occurred in the low level disk I/O layer */\n\tFR_INT_ERR,\t\t\t\t/* (2) Assertion failed */\n\tFR_NOT_READY,\t\t\t/* (3) The physical drive cannot work */\n\tFR_NO_FILE,\t\t\t\t/* (4) Could not find the file */\n\tFR_NO_PATH,\t\t\t\t/* (5) Could not find the path */\n\tFR_INVALID_NAME,\t\t/* (6) The path name format is invalid */\n\tFR_DENIED,\t\t\t\t/* (7) Access denied due to prohibited access or directory full */\n\tFR_EXIST,\t\t\t\t/* (8) Access denied due to prohibited access */\n\tFR_INVALID_OBJECT,\t\t/* (9) The file/directory object is invalid */\n\tFR_WRITE_PROTECTED,\t\t/* (10) The physical drive is write protected */\n\tFR_INVALID_DRIVE,\t\t/* (11) The logical drive number is invalid */\n\tFR_NOT_ENABLED,\t\t\t/* (12) The volume has no work area */\n\tFR_NO_FILESYSTEM,\t\t/* (13) There is no valid FAT volume */\n\tFR_MKFS_ABORTED,\t\t/* (14) The f_mkfs() aborted due to any problem */\n\tFR_TIMEOUT,\t\t\t\t/* (15) Could not get a grant to access the volume within defined period */\n\tFR_LOCKED,\t\t\t\t/* (16) The operation is rejected according to the file sharing policy */\n\tFR_NOT_ENOUGH_CORE,\t\t/* (17) LFN working buffer could not be allocated */\n\tFR_TOO_MANY_OPEN_FILES,\t/* (18) Number of open files > _FS_LOCK */\n\tFR_INVALID_PARAMETER\t/* (19) Given parameter is invalid */\n} FRESULT;\n\n\n\n/*--------------------------------------------------------------*/\n/* FatFs module application interface                           */\n\nFRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);\t\t\t\t/* Open or create a file */\nFRESULT f_close (FIL* fp);\t\t\t\t\t\t\t\t\t\t\t/* Close an open file object */\nFRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);\t\t\t/* Read data from the file */\nFRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);\t/* Write data to the file */\nFRESULT f_lseek (FIL* fp, FSIZE_t ofs);\t\t\t\t\t\t\t\t/* Move file pointer of the file object */\nFRESULT f_truncate (FIL* fp);\t\t\t\t\t\t\t\t\t\t/* Truncate the file */\nFRESULT f_sync (FIL* fp);\t\t\t\t\t\t\t\t\t\t\t/* Flush cached data of the writing file */\nFRESULT f_opendir (DIR* dp, const TCHAR* path);\t\t\t\t\t\t/* Open a directory */\nFRESULT f_closedir (DIR* dp);\t\t\t\t\t\t\t\t\t\t/* Close an open directory */\nFRESULT f_readdir (DIR* dp, FILINFO* fno);\t\t\t\t\t\t\t/* Read a directory item */\nFRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern);\t/* Find first file */\nFRESULT f_findnext (DIR* dp, FILINFO* fno);\t\t\t\t\t\t\t/* Find next file */\nFRESULT f_mkdir (const TCHAR* path);\t\t\t\t\t\t\t\t/* Create a sub directory */\nFRESULT f_unlink (const TCHAR* path);\t\t\t\t\t\t\t\t/* Delete an existing file or directory */\nFRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);\t/* Rename/Move a file or directory */\nFRESULT f_stat (const TCHAR* path, FILINFO* fno);\t\t\t\t\t/* Get file status */\nFRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask);\t\t\t/* Change attribute of a file/dir */\nFRESULT f_utime (const TCHAR* path, const FILINFO* fno);\t\t\t/* Change timestamp of a file/dir */\nFRESULT f_chdir (const TCHAR* path);\t\t\t\t\t\t\t\t/* Change current directory */\nFRESULT f_chdrive (const TCHAR* path);\t\t\t\t\t\t\t\t/* Change current drive */\nFRESULT f_getcwd (TCHAR* buff, UINT len);\t\t\t\t\t\t\t/* Get current directory */\nFRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);\t/* Get number of free clusters on the drive */\nFRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);\t/* Get volume label */\nFRESULT f_setlabel (const TCHAR* label);\t\t\t\t\t\t\t/* Set volume label */\nFRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf);\t/* Forward data to the stream */\nFRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt);\t\t\t\t\t/* Allocate a contiguous block to the file */\nFRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);\t\t\t/* Mount/Unmount a logical drive */\nFRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len);\t/* Create a FAT volume */\nFRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work);\t\t\t/* Divide a physical drive into some partitions */\nint f_putc (TCHAR c, FIL* fp);\t\t\t\t\t\t\t\t\t\t/* Put a character to the file */\nint f_puts (const TCHAR* str, FIL* cp);\t\t\t\t\t\t\t\t/* Put a string to the file */\nint f_printf (FIL* fp, const TCHAR* str, ...);\t\t\t\t\t\t/* Put a formatted string to the file */\nTCHAR* f_gets (TCHAR* buff, int len, FIL* fp);\t\t\t\t\t\t/* Get a string from the file */\n\n#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))\n#define f_error(fp) ((fp)->err)\n#define f_tell(fp) ((fp)->fptr)\n#define f_size(fp) ((fp)->obj.objsize)\n#define f_rewind(fp) f_lseek((fp), 0)\n#define f_rewinddir(dp) f_readdir((dp), 0)\n#define f_rmdir(path) f_unlink(path)\n\n#ifndef EOF\n#define EOF (-1)\n#endif\n\n\n\n\n/*--------------------------------------------------------------*/\n/* Additional user defined functions                            */\n\n/* RTC function */\n#if !_FS_READONLY && !_FS_NORTC\nDWORD get_fattime (void);\n#endif\n\n/* Unicode support functions */\n#if _USE_LFN != 0\t\t\t\t\t\t/* Unicode - OEM code conversion */\nWCHAR ff_convert (WCHAR chr, UINT dir);\t/* OEM-Unicode bidirectional conversion */\nWCHAR ff_wtoupper (WCHAR chr);\t\t\t/* Unicode upper-case conversion */\n#if _USE_LFN == 3\t\t\t\t\t\t/* Memory functions */\nvoid* ff_memalloc (UINT msize);\t\t\t/* Allocate memory block */\nvoid ff_memfree (void* mblock);\t\t\t/* Free memory block */\n#endif\n#endif\n\n/* Sync functions */\n#if _FS_REENTRANT\nint ff_cre_syncobj (BYTE vol, _SYNC_t* sobj);\t/* Create a sync object */\nint ff_req_grant (_SYNC_t sobj);\t\t\t\t/* Lock sync object */\nvoid ff_rel_grant (_SYNC_t sobj);\t\t\t\t/* Unlock sync object */\nint ff_del_syncobj (_SYNC_t sobj);\t\t\t\t/* Delete a sync object */\n#endif\n\n\n\n\n/*--------------------------------------------------------------*/\n/* Flags and offset address                                     */\n\n\n/* File access mode and open method flags (3rd argument of f_open) */\n#define\tFA_READ\t\t\t\t0x01\n#define\tFA_WRITE\t\t\t0x02\n#define\tFA_OPEN_EXISTING\t0x00\n#define\tFA_CREATE_NEW\t\t0x04\n#define\tFA_CREATE_ALWAYS\t0x08\n#define\tFA_OPEN_ALWAYS\t\t0x10\n#define\tFA_OPEN_APPEND\t\t0x30\n\n/* Fast seek controls (2nd argument of f_lseek) */\n#define CREATE_LINKMAP\t((FSIZE_t)0 - 1)\n\n/* Format options (2nd argument of f_mkfs) */\n#define FM_FAT\t\t0x01\n#define FM_FAT32\t0x02\n#define FM_EXFAT\t0x04\n#define FM_ANY\t\t0x07\n#define FM_SFD\t\t0x08\n\n/* Filesystem type (FATFS.fs_type) */\n#define FS_FAT12\t1\n#define FS_FAT16\t2\n#define FS_FAT32\t3\n#define FS_EXFAT\t4\n\n/* File attribute bits for directory entry (FILINFO.fattrib) */\n#define\tAM_RDO\t0x01\t/* Read only */\n#define\tAM_HID\t0x02\t/* Hidden */\n#define\tAM_SYS\t0x04\t/* System */\n#define AM_DIR\t0x10\t/* Directory */\n#define AM_ARC\t0x20\t/* Archive */\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _FATFS */\n"
  },
  {
    "path": "lib/fatfs/ff_gen_drv.c",
    "content": "/**\n  ******************************************************************************\n  * @file    ff_gen_drv.c\n  * @author  MCD Application Team\n  * @brief   FatFs generic low level driver.\n  *****************************************************************************\n  * @attention\n  *\n  * Copyright (c) 2017 STMicroelectronics. All rights reserved.\n  *\n  * This software component is licensed by ST under BSD 3-Clause license,\n  * the \"License\"; You may not use this file except in compliance with the\n  * License. You may obtain a copy of the License at:\n  *                       opensource.org/licenses/BSD-3-Clause\n  *\n  ******************************************************************************\n**/\n/* Includes ------------------------------------------------------------------*/\n#include \"ff_gen_drv.h\"\n\n/* Private typedef -----------------------------------------------------------*/\n/* Private define ------------------------------------------------------------*/\n/* Private variables ---------------------------------------------------------*/\nDisk_drvTypeDef disk = {{0},{0},{0},0};\n\n/* Private function prototypes -----------------------------------------------*/\n/* Private functions ---------------------------------------------------------*/\n\n/**\n  * @brief  Links a compatible diskio driver/lun id and increments the number of active\n  *         linked drivers.\n  * @note   The number of linked drivers (volumes) is up to 10 due to FatFs limits.\n  * @param  drv: pointer to the disk IO Driver structure\n  * @param  path: pointer to the logical drive path\n  * @param  lun : only used for USB Key Disk to add multi-lun management\n            else the parameter must be equal to 0\n  * @retval Returns 0 in case of success, otherwise 1.\n  */\nuint8_t FATFS_LinkDriverEx(const Diskio_drvTypeDef *drv, char *path, uint8_t lun)\n{\n  uint8_t ret = 1;\n  uint8_t DiskNum = 0;\n\n  if(disk.nbr < _VOLUMES)\n  {\n    disk.is_initialized[disk.nbr] = 0;\n    disk.drv[disk.nbr] = drv;\n    disk.lun[disk.nbr] = lun;\n    DiskNum = disk.nbr++;\n    path[0] = DiskNum + '0';\n    path[1] = ':';\n    path[2] = '/';\n    path[3] = 0;\n    ret = 0;\n  }\n\n  return ret;\n}\n\n/**\n  * @brief  Links a compatible diskio driver and increments the number of active\n  *         linked drivers.\n  * @note   The number of linked drivers (volumes) is up to 10 due to FatFs limits\n  * @param  drv: pointer to the disk IO Driver structure\n  * @param  path: pointer to the logical drive path\n  * @retval Returns 0 in case of success, otherwise 1.\n  */\nuint8_t FATFS_LinkDriver(const Diskio_drvTypeDef *drv, char *path)\n{\n  return FATFS_LinkDriverEx(drv, path, 0);\n}\n\n/**\n  * @brief  Unlinks a diskio driver and decrements the number of active linked\n  *         drivers.\n  * @param  path: pointer to the logical drive path\n  * @param  lun : not used\n  * @retval Returns 0 in case of success, otherwise 1.\n  */\nuint8_t FATFS_UnLinkDriverEx(char *path, uint8_t lun)\n{\n  (void)lun;\n  uint8_t DiskNum = 0;\n  uint8_t ret = 1;\n\n  if(disk.nbr >= 1)\n  {\n    DiskNum = path[0] - '0';\n    if(disk.drv[DiskNum] != 0)\n    {\n      disk.drv[DiskNum] = 0;\n      disk.lun[DiskNum] = 0;\n      disk.nbr--;\n      ret = 0;\n    }\n  }\n\n  return ret;\n}\n\n/**\n  * @brief  Unlinks a diskio driver and decrements the number of active linked\n  *         drivers.\n  * @param  path: pointer to the logical drive path\n  * @retval Returns 0 in case of success, otherwise 1.\n  */\nuint8_t FATFS_UnLinkDriver(char *path)\n{\n  return FATFS_UnLinkDriverEx(path, 0);\n}\n\n/**\n  * @brief  Gets number of linked drivers to the FatFs module.\n  * @param  None\n  * @retval Number of attached drivers.\n  */\nuint8_t FATFS_GetAttachedDriversNbr(void)\n{\n  return disk.nbr;\n}\n\n/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/\n\n"
  },
  {
    "path": "lib/fatfs/ff_gen_drv.h",
    "content": "/**\n  ******************************************************************************\n  * @file    ff_gen_drv.h\n  * @author  MCD Application Team\n  * @brief   Header for ff_gen_drv.c module.\n  *****************************************************************************\n  * @attention\n  *\n  * Copyright (c) 2017 STMicroelectronics. All rights reserved.\n  *\n  * This software component is licensed by ST under BSD 3-Clause license,\n  * the \"License\"; You may not use this file except in compliance with the\n  * License. You may obtain a copy of the License at:\n  *                       opensource.org/licenses/BSD-3-Clause\n  *\n  ******************************************************************************\n**/\n\n/* Define to prevent recursive inclusion -------------------------------------*/\n#ifndef __FF_GEN_DRV_H\n#define __FF_GEN_DRV_H\n\n#ifdef __cplusplus\n extern \"C\" {\n#endif\n\n/* Includes ------------------------------------------------------------------*/\n#include \"diskio.h\"\n#include \"ff.h\"\n#include \"stdint.h\"\n\n\n/* Exported types ------------------------------------------------------------*/\n\n/**\n  * @brief  Disk IO Driver structure definition\n  */\ntypedef struct\n{\n  DSTATUS (*disk_initialize) (BYTE);                     /*!< Initialize Disk Drive                     */\n  DSTATUS (*disk_status)     (BYTE);                     /*!< Get Disk Status                           */\n  DRESULT (*disk_read)       (BYTE, BYTE*, DWORD, UINT);       /*!< Read Sector(s)                            */\n#if _USE_WRITE == 1\n  DRESULT (*disk_write)      (BYTE, const BYTE*, DWORD, UINT); /*!< Write Sector(s) when _USE_WRITE = 0       */\n#endif /* _USE_WRITE == 1 */\n#if _USE_IOCTL == 1\n  DRESULT (*disk_ioctl)      (BYTE, BYTE, void*);              /*!< I/O control operation when _USE_IOCTL = 1 */\n#endif /* _USE_IOCTL == 1 */\n\n}Diskio_drvTypeDef;\n\n/**\n  * @brief  Global Disk IO Drivers structure definition\n  */\ntypedef struct\n{\n  uint8_t                 is_initialized[_VOLUMES];\n  const Diskio_drvTypeDef *drv[_VOLUMES];\n  uint8_t                 lun[_VOLUMES];\n  volatile uint8_t        nbr;\n\n}Disk_drvTypeDef;\n\n/* Exported constants --------------------------------------------------------*/\n/* Exported macro ------------------------------------------------------------*/\n/* Exported functions ------------------------------------------------------- */\nuint8_t FATFS_LinkDriver(const Diskio_drvTypeDef *drv, char *path);\nuint8_t FATFS_UnLinkDriver(char *path);\nuint8_t FATFS_LinkDriverEx(const Diskio_drvTypeDef *drv, char *path, BYTE lun);\nuint8_t FATFS_UnLinkDriverEx(char *path, BYTE lun);\nuint8_t FATFS_GetAttachedDriversNbr(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __FF_GEN_DRV_H */\n\n/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/\n\n"
  },
  {
    "path": "lib/fatfs/ffconf_template.h",
    "content": "/*----------------------------------------------------------------------------/\n/  FatFs - Generic FAT file system module  R0.12c                             /\n/-----------------------------------------------------------------------------/\n/\n/ Copyright (C) 2017, ChaN, all right reserved.\n/ Portions Copyright (C) STMicroelectronics, all right reserved.\n/\n/ FatFs module is an open source software. Redistribution and use of FatFs in\n/ source and binary forms, with or without modification, are permitted provided\n/ that the following condition is met:\n\n/ 1. Redistributions of source code must retain the above copyright notice,\n/    this condition and the following disclaimer.\n/\n/ This software is provided by the copyright holder and contributors \"AS IS\"\n/ and any warranties related to this software are DISCLAIMED.\n/ The copyright owner or contributors be NOT LIABLE for any damages caused\n/ by use of this software.\n/----------------------------------------------------------------------------*/\n\n\n/*---------------------------------------------------------------------------/\n/  FatFs - FAT file system module configuration file\n/---------------------------------------------------------------------------*/\n\n#define _FFCONF 68300\t/* Revision ID */\n\n/*---------------------------------------------------------------------------/\n/ Function Configurations\n/---------------------------------------------------------------------------*/\n\n#define _FS_READONLY\t0\n/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)\n/  Read-only configuration removes writing API functions, f_write(), f_sync(),\n/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()\n/  and optional writing functions as well. */\n\n\n#define _FS_MINIMIZE\t0\n/* This option defines minimization level to remove some basic API functions.\n/\n/   0: All basic functions are enabled.\n/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()\n/      are removed.\n/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.\n/   3: f_lseek() function is removed in addition to 2. */\n\n\n#define\t_USE_STRFUNC\t0\n/* This option switches string functions, f_gets(), f_putc(), f_puts() and\n/  f_printf().\n/\n/  0: Disable string functions.\n/  1: Enable without LF-CRLF conversion.\n/  2: Enable with LF-CRLF conversion. */\n\n\n#define _USE_FIND\t\t0\n/* This option switches filtered directory read functions, f_findfirst() and\n/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */\n\n\n#define\t_USE_MKFS\t\t1\n/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */\n\n\n#define\t_USE_FASTSEEK\t1\n/* This option switches fast seek function. (0:Disable or 1:Enable) */\n\n\n#define\t_USE_EXPAND\t\t0\n/* This option switches f_expand function. (0:Disable or 1:Enable) */\n\n\n#define _USE_CHMOD\t\t0\n/* This option switches attribute manipulation functions, f_chmod() and f_utime().\n/  (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */\n\n\n#define _USE_LABEL\t\t0\n/* This option switches volume label functions, f_getlabel() and f_setlabel().\n/  (0:Disable or 1:Enable) */\n\n\n#define\t_USE_FORWARD\t0\n/* This option switches f_forward() function. (0:Disable or 1:Enable) */\n\n\n/*---------------------------------------------------------------------------/\n/ Locale and Namespace Configurations\n/---------------------------------------------------------------------------*/\n\n#define _CODE_PAGE\t850\n/* This option specifies the OEM code page to be used on the target system.\n/  Incorrect setting of the code page can cause a file open failure.\n/\n/   1   - ASCII (No extended character. Non-LFN cfg. only)\n/   437 - U.S.\n/   720 - Arabic\n/   737 - Greek\n/   771 - KBL\n/   775 - Baltic\n/   850 - Latin 1\n/   852 - Latin 2\n/   855 - Cyrillic\n/   857 - Turkish\n/   860 - Portuguese\n/   861 - Icelandic\n/   862 - Hebrew\n/   863 - Canadian French\n/   864 - Arabic\n/   865 - Nordic\n/   866 - Russian\n/   869 - Greek 2\n/   932 - Japanese (DBCS)\n/   936 - Simplified Chinese (DBCS)\n/   949 - Korean (DBCS)\n/   950 - Traditional Chinese (DBCS)\n*/\n\n\n#define\t_USE_LFN\t3\n#define\t_MAX_LFN\t255\n/* The _USE_LFN switches the support of long file name (LFN).\n/\n/   0: Disable support of LFN. _MAX_LFN has no effect.\n/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.\n/   2: Enable LFN with dynamic working buffer on the STACK.\n/   3: Enable LFN with dynamic working buffer on the HEAP.\n/\n/  To enable the LFN, Unicode handling functions (option/unicode.c) must be added\n/  to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and\n/  additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255.\n/  It should be set 255 to support full featured LFN operations.\n/  When use stack for the working buffer, take care on stack overflow. When use heap\n/  memory for the working buffer, memory management functions, ff_memalloc() and\n/  ff_memfree(), must be added to the project. */\n\n\n#define\t_LFN_UNICODE\t0\n/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16)\n/  To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1.\n/  This option also affects behavior of string I/O functions. */\n\n\n#define _STRF_ENCODE\t3\n/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to\n/  be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().\n/\n/  0: ANSI/OEM\n/  1: UTF-16LE\n/  2: UTF-16BE\n/  3: UTF-8\n/\n/  This option has no effect when _LFN_UNICODE == 0. */\n\n\n#define _FS_RPATH\t0\n/* This option configures support of relative path.\n/\n/   0: Disable relative path and remove related functions.\n/   1: Enable relative path. f_chdir() and f_chdrive() are available.\n/   2: f_getcwd() function is available in addition to 1.\n*/\n\n\n/*---------------------------------------------------------------------------/\n/ Drive/Volume Configurations\n/---------------------------------------------------------------------------*/\n\n#define _VOLUMES\t2\n/* Number of volumes (logical drives) to be used. */\n\n\n#define _STR_VOLUME_ID\t0\n#define _VOLUME_STRS\t\"RAM\",\"NAND\",\"CF\",\"SD\",\"SD2\",\"USB\",\"USB2\",\"USB3\"\n/* _STR_VOLUME_ID switches string support of volume ID.\n/  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive\n/  number in the path name. _VOLUME_STRS defines the drive ID strings for each\n/  logical drives. Number of items must be equal to _VOLUMES. Valid characters for\n/  the drive ID strings are: A-Z and 0-9. */\n\n\n#define\t_MULTI_PARTITION\t0\n/* This option switches support of multi-partition on a physical drive.\n/  By default (0), each logical drive number is bound to the same physical drive\n/  number and only an FAT volume found on the physical drive will be mounted.\n/  When multi-partition is enabled (1), each logical drive number can be bound to\n/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()\n/  funciton will be available. */\n\n\n#define\t_MIN_SS\t\t512\n#define\t_MAX_SS\t\t512\n/* These options configure the range of sector size to be supported. (512, 1024,\n/  2048 or 4096) Always set both 512 for most systems, all type of memory cards and\n/  harddisk. But a larger value may be required for on-board flash memory and some\n/  type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured\n/  to variable sector size and GET_SECTOR_SIZE command must be implemented to the\n/  disk_ioctl() function. */\n\n\n#define\t_USE_TRIM\t0\n/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable)\n/  To enable Trim function, also CTRL_TRIM command should be implemented to the\n/  disk_ioctl() function. */\n\n\n#define _FS_NOFSINFO\t0\n/* If you need to know correct free space on the FAT32 volume, set bit 0 of this\n/  option, and f_getfree() function at first time after volume mount will force\n/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.\n/\n/  bit0=0: Use free cluster count in the FSINFO if available.\n/  bit0=1: Do not trust free cluster count in the FSINFO.\n/  bit1=0: Use last allocated cluster number in the FSINFO if available.\n/  bit1=1: Do not trust last allocated cluster number in the FSINFO.\n*/\n\n\n\n/*---------------------------------------------------------------------------/\n/ System Configurations\n/---------------------------------------------------------------------------*/\n\n#define\t_FS_TINY\t0\n/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)\n/  At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes.\n/  Instead of private sector buffer eliminated from the file object, common sector\n/  buffer in the file system object (FATFS) is used for the file data transfer. */\n\n\n#define _FS_EXFAT\t0\n/* This option switches support of exFAT file system. (0:Disable or 1:Enable)\n/  When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)\n/  Note that enabling exFAT discards C89 compatibility. */\n\n\n#define _FS_NORTC\t0\n#define _NORTC_MON\t1\n#define _NORTC_MDAY\t1\n#define _NORTC_YEAR\t2016\n/* The option _FS_NORTC switches timestamp functiton. If the system does not have\n/  any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable\n/  the timestamp function. All objects modified by FatFs will have a fixed timestamp\n/  defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time.\n/  To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be\n/  added to the project to get current time form real-time clock. _NORTC_MON,\n/  _NORTC_MDAY and _NORTC_YEAR have no effect.\n/  These options have no effect at read-only configuration (_FS_READONLY = 1). */\n\n\n#define\t_FS_LOCK\t2\n/* The option _FS_LOCK switches file lock function to control duplicated file open\n/  and illegal operation to open objects. This option must be 0 when _FS_READONLY\n/  is 1.\n/\n/  0:  Disable file lock function. To avoid volume corruption, application program\n/      should avoid illegal open, remove and rename to the open objects.\n/  >0: Enable file lock function. The value defines how many files/sub-directories\n/      can be opened simultaneously under file lock control. Note that the file\n/      lock control is independent of re-entrancy. */\n\n#define _FS_REENTRANT\t1\n\n#if _FS_REENTRANT\n#include \"cmsis_os.h\"\n#define _FS_TIMEOUT\t\t1000\n#define\t_SYNC_t         osSemaphoreId\n#endif\n/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs\n/  module itself. Note that regardless of this option, file access to different\n/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()\n/  and f_fdisk() function, are always not re-entrant. Only file/directory access\n/  to the same volume is under control of this function.\n/\n/   0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.\n/   1: Enable re-entrancy. Also user provided synchronization handlers,\n/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()\n/      function, must be added to the project. Samples are available in\n/      option/syscall.c.\n/\n/  The _FS_TIMEOUT defines timeout period in unit of time tick.\n/  The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,\n/  SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be\n/  included somewhere in the scope of ff.h. */\n\n/* #include <windows.h>\t// O/S definitions  */\n\n#if _USE_LFN == 3\n#if !defined(ff_malloc) || !defined(ff_free)\n#include <stdlib.h>\n#endif\n\n#if !defined(ff_malloc)\n#define ff_malloc malloc\n#endif\n\n#if !defined(ff_free)\n#define ff_free free\n#endif\n#endif\n/*--- End of configuration options ---*/\n"
  },
  {
    "path": "lib/fatfs/integer.h",
    "content": "/*-------------------------------------------*/\n/* Integer type definitions for FatFs module */\n/*-------------------------------------------*/\n\n#ifndef _FF_INTEGER\n#define _FF_INTEGER\n\n#ifdef _WIN32 /* FatFs development platform */\n\n#include <windows.h>\n#include <tchar.h>\ntypedef unsigned __int64 QWORD;\n\n#else /* Embedded platform */\n#include <stdint.h>\n\n/* These types MUST be 16-bit or 32-bit */\ntypedef int16_t INT;\ntypedef uint16_t UINT;\n\n/* This type MUST be 8-bit */\ntypedef uint8_t BYTE;\n\n/* These types MUST be 16-bit */\ntypedef int16_t SHORT;\ntypedef uint16_t WORD;\ntypedef uint16_t WCHAR;\n\n/* These types MUST be 32-bit */\ntypedef int32_t LONG;\ntypedef uint32_t DWORD;\n\n/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */\ntypedef uint64_t QWORD;\n\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/fatfs/option/cc932.c",
    "content": "/*------------------------------------------------------------------------*/\n/* Unicode - OEM code bidirectional converter  (C)ChaN, 2015              */\n/* CP932 (Japanese Shift-JIS)                                             */\n/*------------------------------------------------------------------------*/\n\n#include \"../ff.h\"\n\n#define _TINY_TABLE\t0\n\n#if !_USE_LFN || _CODE_PAGE != 932\n#error This file is not needed in current configuration. Remove from the project.\n#endif\n\n\nstatic\nconst WCHAR uni2sjis[] = {\n/*  Unicode - Sjis, Unicode - Sjis, Unicode - Sjis, Unicode - Sjis, */\n\t0x00A7, 0x8198, 0x00A8, 0x814E, 0x00B0, 0x818B, 0x00B1, 0x817D,\n\t0x00B4, 0x814C, 0x00B6, 0x81F7, 0x00D7, 0x817E, 0x00F7, 0x8180,\n\t0x0391, 0x839F, 0x0392, 0x83A0, 0x0393, 0x83A1, 0x0394, 0x83A2,\n\t0x0395, 0x83A3, 0x0396, 0x83A4, 0x0397, 0x83A5, 0x0398, 0x83A6,\n\t0x0399, 0x83A7, 0x039A, 0x83A8, 0x039B, 0x83A9, 0x039C, 0x83AA,\n\t0x039D, 0x83AB, 0x039E, 0x83AC, 0x039F, 0x83AD, 0x03A0, 0x83AE,\n\t0x03A1, 0x83AF, 0x03A3, 0x83B0, 0x03A4, 0x83B1, 0x03A5, 0x83B2,\n\t0x03A6, 0x83B3, 0x03A7, 0x83B4, 0x03A8, 0x83B5, 0x03A9, 0x83B6,\n\t0x03B1, 0x83BF, 0x03B2, 0x83C0, 0x03B3, 0x83C1, 0x03B4, 0x83C2,\n\t0x03B5, 0x83C3, 0x03B6, 0x83C4, 0x03B7, 0x83C5, 0x03B8, 0x83C6,\n\t0x03B9, 0x83C7, 0x03BA, 0x83C8, 0x03BB, 0x83C9, 0x03BC, 0x83CA,\n\t0x03BD, 0x83CB, 0x03BE, 0x83CC, 0x03BF, 0x83CD, 0x03C0, 0x83CE,\n\t0x03C1, 0x83CF, 0x03C3, 0x83D0, 0x03C4, 0x83D1, 0x03C5, 0x83D2,\n\t0x03C6, 0x83D3, 0x03C7, 0x83D4, 0x03C8, 0x83D5, 0x03C9, 0x83D6,\n\t0x0401, 0x8446, 0x0410, 0x8440, 0x0411, 0x8441, 0x0412, 0x8442,\n\t0x0413, 0x8443, 0x0414, 0x8444, 0x0415, 0x8445, 0x0416, 0x8447,\n\t0x0417, 0x8448, 0x0418, 0x8449, 0x0419, 0x844A, 0x041A, 0x844B,\n\t0x041B, 0x844C, 0x041C, 0x844D, 0x041D, 0x844E, 0x041E, 0x844F,\n\t0x041F, 0x8450, 0x0420, 0x8451, 0x0421, 0x8452, 0x0422, 0x8453,\n\t0x0423, 0x8454, 0x0424, 0x8455, 0x0425, 0x8456, 0x0426, 0x8457,\n\t0x0427, 0x8458, 0x0428, 0x8459, 0x0429, 0x845A, 0x042A, 0x845B,\n\t0x042B, 0x845C, 0x042C, 0x845D, 0x042D, 0x845E, 0x042E, 0x845F,\n\t0x042F, 0x8460, 0x0430, 0x8470, 0x0431, 0x8471, 0x0432, 0x8472,\n\t0x0433, 0x8473, 0x0434, 0x8474, 0x0435, 0x8475, 0x0436, 0x8477,\n\t0x0437, 0x8478, 0x0438, 0x8479, 0x0439, 0x847A, 0x043A, 0x847B,\n\t0x043B, 0x847C, 0x043C, 0x847D, 0x043D, 0x847E, 0x043E, 0x8480,\n\t0x043F, 0x8481, 0x0440, 0x8482, 0x0441, 0x8483, 0x0442, 0x8484,\n\t0x0443, 0x8485, 0x0444, 0x8486, 0x0445, 0x8487, 0x0446, 0x8488,\n\t0x0447, 0x8489, 0x0448, 0x848A, 0x0449, 0x848B, 0x044A, 0x848C,\n\t0x044B, 0x848D, 0x044C, 0x848E, 0x044D, 0x848F, 0x044E, 0x8490,\n\t0x044F, 0x8491, 0x0451, 0x8476, 0x2010, 0x815D, 0x2015, 0x815C,\n\t0x2018, 0x8165, 0x2019, 0x8166, 0x201C, 0x8167, 0x201D, 0x8168,\n\t0x2020, 0x81F5, 0x2021, 0x81F6, 0x2025, 0x8164, 0x2026, 0x8163,\n\t0x2030, 0x81F1, 0x2032, 0x818C, 0x2033, 0x818D, 0x203B, 0x81A6,\n\t0x2103, 0x818E, 0x2116, 0x8782, 0x2121, 0x8784, 0x212B, 0x81F0,\n\t0x2160, 0x8754, 0x2161, 0x8755, 0x2162, 0x8756, 0x2163, 0x8757,\n\t0x2164, 0x8758, 0x2165, 0x8759, 0x2166, 0x875A, 0x2167, 0x875B,\n\t0x2168, 0x875C, 0x2169, 0x875D, 0x2170, 0xFA40, 0x2171, 0xFA41,\n\t0x2172, 0xFA42, 0x2173, 0xFA43, 0x2174, 0xFA44, 0x2175, 0xFA45,\n\t0x2176, 0xFA46, 0x2177, 0xFA47, 0x2178, 0xFA48, 0x2179, 0xFA49,\n\t0x2190, 0x81A9, 0x2191, 0x81AA, 0x2192, 0x81A8, 0x2193, 0x81AB,\n\t0x21D2, 0x81CB, 0x21D4, 0x81CC, 0x2200, 0x81CD, 0x2202, 0x81DD,\n\t0x2203, 0x81CE, 0x2207, 0x81DE, 0x2208, 0x81B8, 0x220B, 0x81B9,\n\t0x2211, 0x8794, 0x221A, 0x81E3, 0x221D, 0x81E5, 0x221E, 0x8187,\n\t0x221F, 0x8798, 0x2220, 0x81DA, 0x2225, 0x8161, 0x2227, 0x81C8,\n\t0x2228, 0x81C9, 0x2229, 0x81BF, 0x222A, 0x81BE, 0x222B, 0x81E7,\n\t0x222C, 0x81E8, 0x222E, 0x8793, 0x2234, 0x8188, 0x2235, 0x81E6,\n\t0x223D, 0x81E4, 0x2252, 0x81E0, 0x2260, 0x8182, 0x2261, 0x81DF,\n\t0x2266, 0x8185, 0x2267, 0x8186, 0x226A, 0x81E1, 0x226B, 0x81E2,\n\t0x2282, 0x81BC, 0x2283, 0x81BD, 0x2286, 0x81BA, 0x2287, 0x81BB,\n\t0x22A5, 0x81DB, 0x22BF, 0x8799, 0x2312, 0x81DC, 0x2460, 0x8740,\n\t0x2461, 0x8741, 0x2462, 0x8742, 0x2463, 0x8743, 0x2464, 0x8744,\n\t0x2465, 0x8745, 0x2466, 0x8746, 0x2467, 0x8747, 0x2468, 0x8748,\n\t0x2469, 0x8749, 0x246A, 0x874A, 0x246B, 0x874B, 0x246C, 0x874C,\n\t0x246D, 0x874D, 0x246E, 0x874E, 0x246F, 0x874F, 0x2470, 0x8750,\n\t0x2471, 0x8751, 0x2472, 0x8752, 0x2473, 0x8753, 0x2500, 0x849F,\n\t0x2501, 0x84AA, 0x2502, 0x84A0, 0x2503, 0x84AB, 0x250C, 0x84A1,\n\t0x250F, 0x84AC, 0x2510, 0x84A2, 0x2513, 0x84AD, 0x2514, 0x84A4,\n\t0x2517, 0x84AF, 0x2518, 0x84A3, 0x251B, 0x84AE, 0x251C, 0x84A5,\n\t0x251D, 0x84BA, 0x2520, 0x84B5, 0x2523, 0x84B0, 0x2524, 0x84A7,\n\t0x2525, 0x84BC, 0x2528, 0x84B7, 0x252B, 0x84B2, 0x252C, 0x84A6,\n\t0x252F, 0x84B6, 0x2530, 0x84BB, 0x2533, 0x84B1, 0x2534, 0x84A8,\n\t0x2537, 0x84B8, 0x2538, 0x84BD, 0x253B, 0x84B3, 0x253C, 0x84A9,\n\t0x253F, 0x84B9, 0x2542, 0x84BE, 0x254B, 0x84B4, 0x25A0, 0x81A1,\n\t0x25A1, 0x81A0, 0x25B2, 0x81A3, 0x25B3, 0x81A2, 0x25BC, 0x81A5,\n\t0x25BD, 0x81A4, 0x25C6, 0x819F, 0x25C7, 0x819E, 0x25CB, 0x819B,\n\t0x25CE, 0x819D, 0x25CF, 0x819C, 0x25EF, 0x81FC, 0x2605, 0x819A,\n\t0x2606, 0x8199, 0x2640, 0x818A, 0x2642, 0x8189, 0x266A, 0x81F4,\n\t0x266D, 0x81F3, 0x266F, 0x81F2, 0x3000, 0x8140, 0x3001, 0x8141,\n\t0x3002, 0x8142, 0x3003, 0x8156, 0x3005, 0x8158, 0x3006, 0x8159,\n\t0x3007, 0x815A, 0x3008, 0x8171, 0x3009, 0x8172, 0x300A, 0x8173,\n\t0x300B, 0x8174, 0x300C, 0x8175, 0x300D, 0x8176, 0x300E, 0x8177,\n\t0x300F, 0x8178, 0x3010, 0x8179, 0x3011, 0x817A, 0x3012, 0x81A7,\n\t0x3013, 0x81AC, 0x3014, 0x816B, 0x3015, 0x816C, 0x301D, 0x8780,\n\t0x301F, 0x8781, 0x3041, 0x829F, 0x3042, 0x82A0, 0x3043, 0x82A1,\n\t0x3044, 0x82A2, 0x3045, 0x82A3, 0x3046, 0x82A4, 0x3047, 0x82A5,\n\t0x3048, 0x82A6, 0x3049, 0x82A7, 0x304A, 0x82A8, 0x304B, 0x82A9,\n\t0x304C, 0x82AA, 0x304D, 0x82AB, 0x304E, 0x82AC, 0x304F, 0x82AD,\n\t0x3050, 0x82AE, 0x3051, 0x82AF, 0x3052, 0x82B0, 0x3053, 0x82B1,\n\t0x3054, 0x82B2, 0x3055, 0x82B3, 0x3056, 0x82B4, 0x3057, 0x82B5,\n\t0x3058, 0x82B6, 0x3059, 0x82B7, 0x305A, 0x82B8, 0x305B, 0x82B9,\n\t0x305C, 0x82BA, 0x305D, 0x82BB, 0x305E, 0x82BC, 0x305F, 0x82BD,\n\t0x3060, 0x82BE, 0x3061, 0x82BF, 0x3062, 0x82C0, 0x3063, 0x82C1,\n\t0x3064, 0x82C2, 0x3065, 0x82C3, 0x3066, 0x82C4, 0x3067, 0x82C5,\n\t0x3068, 0x82C6, 0x3069, 0x82C7, 0x306A, 0x82C8, 0x306B, 0x82C9,\n\t0x306C, 0x82CA, 0x306D, 0x82CB, 0x306E, 0x82CC, 0x306F, 0x82CD,\n\t0x3070, 0x82CE, 0x3071, 0x82CF, 0x3072, 0x82D0, 0x3073, 0x82D1,\n\t0x3074, 0x82D2, 0x3075, 0x82D3, 0x3076, 0x82D4, 0x3077, 0x82D5,\n\t0x3078, 0x82D6, 0x3079, 0x82D7, 0x307A, 0x82D8, 0x307B, 0x82D9,\n\t0x307C, 0x82DA, 0x307D, 0x82DB, 0x307E, 0x82DC, 0x307F, 0x82DD,\n\t0x3080, 0x82DE, 0x3081, 0x82DF, 0x3082, 0x82E0, 0x3083, 0x82E1,\n\t0x3084, 0x82E2, 0x3085, 0x82E3, 0x3086, 0x82E4, 0x3087, 0x82E5,\n\t0x3088, 0x82E6, 0x3089, 0x82E7, 0x308A, 0x82E8, 0x308B, 0x82E9,\n\t0x308C, 0x82EA, 0x308D, 0x82EB, 0x308E, 0x82EC, 0x308F, 0x82ED,\n\t0x3090, 0x82EE, 0x3091, 0x82EF, 0x3092, 0x82F0, 0x3093, 0x82F1,\n\t0x309B, 0x814A, 0x309C, 0x814B, 0x309D, 0x8154, 0x309E, 0x8155,\n\t0x30A1, 0x8340, 0x30A2, 0x8341, 0x30A3, 0x8342, 0x30A4, 0x8343,\n\t0x30A5, 0x8344, 0x30A6, 0x8345, 0x30A7, 0x8346, 0x30A8, 0x8347,\n\t0x30A9, 0x8348, 0x30AA, 0x8349, 0x30AB, 0x834A, 0x30AC, 0x834B,\n\t0x30AD, 0x834C, 0x30AE, 0x834D, 0x30AF, 0x834E, 0x30B0, 0x834F,\n\t0x30B1, 0x8350, 0x30B2, 0x8351, 0x30B3, 0x8352, 0x30B4, 0x8353,\n\t0x30B5, 0x8354, 0x30B6, 0x8355, 0x30B7, 0x8356, 0x30B8, 0x8357,\n\t0x30B9, 0x8358, 0x30BA, 0x8359, 0x30BB, 0x835A, 0x30BC, 0x835B,\n\t0x30BD, 0x835C, 0x30BE, 0x835D, 0x30BF, 0x835E, 0x30C0, 0x835F,\n\t0x30C1, 0x8360, 0x30C2, 0x8361, 0x30C3, 0x8362, 0x30C4, 0x8363,\n\t0x30C5, 0x8364, 0x30C6, 0x8365, 0x30C7, 0x8366, 0x30C8, 0x8367,\n\t0x30C9, 0x8368, 0x30CA, 0x8369, 0x30CB, 0x836A, 0x30CC, 0x836B,\n\t0x30CD, 0x836C, 0x30CE, 0x836D, 0x30CF, 0x836E, 0x30D0, 0x836F,\n\t0x30D1, 0x8370, 0x30D2, 0x8371, 0x30D3, 0x8372, 0x30D4, 0x8373,\n\t0x30D5, 0x8374, 0x30D6, 0x8375, 0x30D7, 0x8376, 0x30D8, 0x8377,\n\t0x30D9, 0x8378, 0x30DA, 0x8379, 0x30DB, 0x837A, 0x30DC, 0x837B,\n\t0x30DD, 0x837C, 0x30DE, 0x837D, 0x30DF, 0x837E, 0x30E0, 0x8380,\n\t0x30E1, 0x8381, 0x30E2, 0x8382, 0x30E3, 0x8383, 0x30E4, 0x8384,\n\t0x30E5, 0x8385, 0x30E6, 0x8386, 0x30E7, 0x8387, 0x30E8, 0x8388,\n\t0x30E9, 0x8389, 0x30EA, 0x838A, 0x30EB, 0x838B, 0x30EC, 0x838C,\n\t0x30ED, 0x838D, 0x30EE, 0x838E, 0x30EF, 0x838F, 0x30F0, 0x8390,\n\t0x30F1, 0x8391, 0x30F2, 0x8392, 0x30F3, 0x8393, 0x30F4, 0x8394,\n\t0x30F5, 0x8395, 0x30F6, 0x8396, 0x30FB, 0x8145, 0x30FC, 0x815B,\n\t0x30FD, 0x8152, 0x30FE, 0x8153, 0x3231, 0x878A, 0x3232, 0x878B,\n\t0x3239, 0x878C, 0x32A4, 0x8785, 0x32A5, 0x8786, 0x32A6, 0x8787,\n\t0x32A7, 0x8788, 0x32A8, 0x8789, 0x3303, 0x8765, 0x330D, 0x8769,\n\t0x3314, 0x8760, 0x3318, 0x8763, 0x3322, 0x8761, 0x3323, 0x876B,\n\t0x3326, 0x876A, 0x3327, 0x8764, 0x332B, 0x876C, 0x3336, 0x8766,\n\t0x333B, 0x876E, 0x3349, 0x875F, 0x334A, 0x876D, 0x334D, 0x8762,\n\t0x3351, 0x8767, 0x3357, 0x8768, 0x337B, 0x877E, 0x337C, 0x878F,\n\t0x337D, 0x878E, 0x337E, 0x878D, 0x338E, 0x8772, 0x338F, 0x8773,\n\t0x339C, 0x876F, 0x339D, 0x8770, 0x339E, 0x8771, 0x33A1, 0x8775,\n\t0x33C4, 0x8774, 0x33CD, 0x8783, 0x4E00, 0x88EA, 0x4E01, 0x929A,\n\t0x4E03, 0x8EB5, 0x4E07, 0x969C, 0x4E08, 0x8FE4, 0x4E09, 0x8E4F,\n\t0x4E0A, 0x8FE3, 0x4E0B, 0x89BA, 0x4E0D, 0x9573, 0x4E0E, 0x975E,\n\t0x4E10, 0x98A0, 0x4E11, 0x894E, 0x4E14, 0x8A8E, 0x4E15, 0x98A1,\n\t0x4E16, 0x90A2, 0x4E17, 0x99C0, 0x4E18, 0x8B75, 0x4E19, 0x95B8,\n\t0x4E1E, 0x8FE5, 0x4E21, 0x97BC, 0x4E26, 0x95C0, 0x4E28, 0xFA68,\n\t0x4E2A, 0x98A2, 0x4E2D, 0x9286, 0x4E31, 0x98A3, 0x4E32, 0x8BF8,\n\t0x4E36, 0x98A4, 0x4E38, 0x8ADB, 0x4E39, 0x924F, 0x4E3B, 0x8EE5,\n\t0x4E3C, 0x98A5, 0x4E3F, 0x98A6, 0x4E42, 0x98A7, 0x4E43, 0x9454,\n\t0x4E45, 0x8B76, 0x4E4B, 0x9456, 0x4E4D, 0x93E1, 0x4E4E, 0x8CC1,\n\t0x4E4F, 0x9652, 0x4E55, 0xE568, 0x4E56, 0x98A8, 0x4E57, 0x8FE6,\n\t0x4E58, 0x98A9, 0x4E59, 0x89B3, 0x4E5D, 0x8BE3, 0x4E5E, 0x8CEE,\n\t0x4E5F, 0x96E7, 0x4E62, 0x9BA4, 0x4E71, 0x9790, 0x4E73, 0x93FB,\n\t0x4E7E, 0x8AA3, 0x4E80, 0x8B54, 0x4E82, 0x98AA, 0x4E85, 0x98AB,\n\t0x4E86, 0x97B9, 0x4E88, 0x975C, 0x4E89, 0x9188, 0x4E8A, 0x98AD,\n\t0x4E8B, 0x8E96, 0x4E8C, 0x93F1, 0x4E8E, 0x98B0, 0x4E91, 0x895D,\n\t0x4E92, 0x8CDD, 0x4E94, 0x8CDC, 0x4E95, 0x88E4, 0x4E98, 0x986A,\n\t0x4E99, 0x9869, 0x4E9B, 0x8DB1, 0x4E9C, 0x889F, 0x4E9E, 0x98B1,\n\t0x4E9F, 0x98B2, 0x4EA0, 0x98B3, 0x4EA1, 0x9653, 0x4EA2, 0x98B4,\n\t0x4EA4, 0x8CF0, 0x4EA5, 0x88E5, 0x4EA6, 0x9692, 0x4EA8, 0x8B9C,\n\t0x4EAB, 0x8B9D, 0x4EAC, 0x8B9E, 0x4EAD, 0x92E0, 0x4EAE, 0x97BA,\n\t0x4EB0, 0x98B5, 0x4EB3, 0x98B6, 0x4EB6, 0x98B7, 0x4EBA, 0x906C,\n\t0x4EC0, 0x8F59, 0x4EC1, 0x906D, 0x4EC2, 0x98BC, 0x4EC4, 0x98BA,\n\t0x4EC6, 0x98BB, 0x4EC7, 0x8B77, 0x4ECA, 0x8DA1, 0x4ECB, 0x89EE,\n\t0x4ECD, 0x98B9, 0x4ECE, 0x98B8, 0x4ECF, 0x95A7, 0x4ED4, 0x8E65,\n\t0x4ED5, 0x8E64, 0x4ED6, 0x91BC, 0x4ED7, 0x98BD, 0x4ED8, 0x9574,\n\t0x4ED9, 0x90E5, 0x4EDD, 0x8157, 0x4EDE, 0x98BE, 0x4EDF, 0x98C0,\n\t0x4EE1, 0xFA69, 0x4EE3, 0x91E3, 0x4EE4, 0x97DF, 0x4EE5, 0x88C8,\n\t0x4EED, 0x98BF, 0x4EEE, 0x89BC, 0x4EF0, 0x8BC2, 0x4EF2, 0x9287,\n\t0x4EF6, 0x8C8F, 0x4EF7, 0x98C1, 0x4EFB, 0x9443, 0x4EFC, 0xFA6A,\n\t0x4F00, 0xFA6B, 0x4F01, 0x8AE9, 0x4F03, 0xFA6C, 0x4F09, 0x98C2,\n\t0x4F0A, 0x88C9, 0x4F0D, 0x8CDE, 0x4F0E, 0x8AEA, 0x4F0F, 0x959A,\n\t0x4F10, 0x94B0, 0x4F11, 0x8B78, 0x4F1A, 0x89EF, 0x4F1C, 0x98E5,\n\t0x4F1D, 0x9360, 0x4F2F, 0x948C, 0x4F30, 0x98C4, 0x4F34, 0x94BA,\n\t0x4F36, 0x97E0, 0x4F38, 0x904C, 0x4F39, 0xFA6D, 0x4F3A, 0x8E66,\n\t0x4F3C, 0x8E97, 0x4F3D, 0x89BE, 0x4F43, 0x92CF, 0x4F46, 0x9241,\n\t0x4F47, 0x98C8, 0x4F4D, 0x88CA, 0x4F4E, 0x92E1, 0x4F4F, 0x8F5A,\n\t0x4F50, 0x8DB2, 0x4F51, 0x9743, 0x4F53, 0x91CC, 0x4F55, 0x89BD,\n\t0x4F56, 0xFA6E, 0x4F57, 0x98C7, 0x4F59, 0x975D, 0x4F5A, 0x98C3,\n\t0x4F5B, 0x98C5, 0x4F5C, 0x8DEC, 0x4F5D, 0x98C6, 0x4F5E, 0x9B43,\n\t0x4F69, 0x98CE, 0x4F6F, 0x98D1, 0x4F70, 0x98CF, 0x4F73, 0x89C0,\n\t0x4F75, 0x95B9, 0x4F76, 0x98C9, 0x4F7B, 0x98CD, 0x4F7C, 0x8CF1,\n\t0x4F7F, 0x8E67, 0x4F83, 0x8AA4, 0x4F86, 0x98D2, 0x4F88, 0x98CA,\n\t0x4F8A, 0xFA70, 0x4F8B, 0x97E1, 0x4F8D, 0x8E98, 0x4F8F, 0x98CB,\n\t0x4F91, 0x98D0, 0x4F92, 0xFA6F, 0x4F94, 0xFA72, 0x4F96, 0x98D3,\n\t0x4F98, 0x98CC, 0x4F9A, 0xFA71, 0x4F9B, 0x8B9F, 0x4F9D, 0x88CB,\n\t0x4FA0, 0x8BA0, 0x4FA1, 0x89BF, 0x4FAB, 0x9B44, 0x4FAD, 0x9699,\n\t0x4FAE, 0x958E, 0x4FAF, 0x8CF2, 0x4FB5, 0x904E, 0x4FB6, 0x97B5,\n\t0x4FBF, 0x95D6, 0x4FC2, 0x8C57, 0x4FC3, 0x91A3, 0x4FC4, 0x89E2,\n\t0x4FC9, 0xFA61, 0x4FCA, 0x8F72, 0x4FCD, 0xFA73, 0x4FCE, 0x98D7,\n\t0x4FD0, 0x98DC, 0x4FD1, 0x98DA, 0x4FD4, 0x98D5, 0x4FD7, 0x91AD,\n\t0x4FD8, 0x98D8, 0x4FDA, 0x98DB, 0x4FDB, 0x98D9, 0x4FDD, 0x95DB,\n\t0x4FDF, 0x98D6, 0x4FE1, 0x904D, 0x4FE3, 0x9693, 0x4FE4, 0x98DD,\n\t0x4FE5, 0x98DE, 0x4FEE, 0x8F43, 0x4FEF, 0x98EB, 0x4FF3, 0x946F,\n\t0x4FF5, 0x9555, 0x4FF6, 0x98E6, 0x4FF8, 0x95EE, 0x4FFA, 0x89B4,\n\t0x4FFE, 0x98EA, 0x4FFF, 0xFA76, 0x5005, 0x98E4, 0x5006, 0x98ED,\n\t0x5009, 0x9171, 0x500B, 0x8CC2, 0x500D, 0x947B, 0x500F, 0xE0C5,\n\t0x5011, 0x98EC, 0x5012, 0x937C, 0x5014, 0x98E1, 0x5016, 0x8CF4,\n\t0x5019, 0x8CF3, 0x501A, 0x98DF, 0x501E, 0xFA77, 0x501F, 0x8ED8,\n\t0x5021, 0x98E7, 0x5022, 0xFA75, 0x5023, 0x95ED, 0x5024, 0x926C,\n\t0x5025, 0x98E3, 0x5026, 0x8C91, 0x5028, 0x98E0, 0x5029, 0x98E8,\n\t0x502A, 0x98E2, 0x502B, 0x97CF, 0x502C, 0x98E9, 0x502D, 0x9860,\n\t0x5036, 0x8BE4, 0x5039, 0x8C90, 0x5040, 0xFA74, 0x5042, 0xFA7A,\n\t0x5043, 0x98EE, 0x5046, 0xFA78, 0x5047, 0x98EF, 0x5048, 0x98F3,\n\t0x5049, 0x88CC, 0x504F, 0x95CE, 0x5050, 0x98F2, 0x5055, 0x98F1,\n\t0x5056, 0x98F5, 0x505A, 0x98F4, 0x505C, 0x92E2, 0x5065, 0x8C92,\n\t0x506C, 0x98F6, 0x5070, 0xFA79, 0x5072, 0x8EC3, 0x5074, 0x91A4,\n\t0x5075, 0x92E3, 0x5076, 0x8BF4, 0x5078, 0x98F7, 0x507D, 0x8B55,\n\t0x5080, 0x98F8, 0x5085, 0x98FA, 0x508D, 0x9654, 0x5091, 0x8C86,\n\t0x5094, 0xFA7B, 0x5098, 0x8E50, 0x5099, 0x94F5, 0x509A, 0x98F9,\n\t0x50AC, 0x8DC3, 0x50AD, 0x9762, 0x50B2, 0x98FC, 0x50B3, 0x9942,\n\t0x50B4, 0x98FB, 0x50B5, 0x8DC2, 0x50B7, 0x8F9D, 0x50BE, 0x8C58,\n\t0x50C2, 0x9943, 0x50C5, 0x8BCD, 0x50C9, 0x9940, 0x50CA, 0x9941,\n\t0x50CD, 0x93AD, 0x50CF, 0x919C, 0x50D1, 0x8BA1, 0x50D5, 0x966C,\n\t0x50D6, 0x9944, 0x50D8, 0xFA7D, 0x50DA, 0x97BB, 0x50DE, 0x9945,\n\t0x50E3, 0x9948, 0x50E5, 0x9946, 0x50E7, 0x916D, 0x50ED, 0x9947,\n\t0x50EE, 0x9949, 0x50F4, 0xFA7C, 0x50F5, 0x994B, 0x50F9, 0x994A,\n\t0x50FB, 0x95C6, 0x5100, 0x8B56, 0x5101, 0x994D, 0x5102, 0x994E,\n\t0x5104, 0x89AD, 0x5109, 0x994C, 0x5112, 0x8EF2, 0x5114, 0x9951,\n\t0x5115, 0x9950, 0x5116, 0x994F, 0x5118, 0x98D4, 0x511A, 0x9952,\n\t0x511F, 0x8F9E, 0x5121, 0x9953, 0x512A, 0x9744, 0x5132, 0x96D7,\n\t0x5137, 0x9955, 0x513A, 0x9954, 0x513B, 0x9957, 0x513C, 0x9956,\n\t0x513F, 0x9958, 0x5140, 0x9959, 0x5141, 0x88F2, 0x5143, 0x8CB3,\n\t0x5144, 0x8C5A, 0x5145, 0x8F5B, 0x5146, 0x929B, 0x5147, 0x8BA2,\n\t0x5148, 0x90E6, 0x5149, 0x8CF5, 0x514A, 0xFA7E, 0x514B, 0x8D8E,\n\t0x514C, 0x995B, 0x514D, 0x96C6, 0x514E, 0x9365, 0x5150, 0x8E99,\n\t0x5152, 0x995A, 0x5154, 0x995C, 0x515A, 0x937D, 0x515C, 0x8A95,\n\t0x5162, 0x995D, 0x5164, 0xFA80, 0x5165, 0x93FC, 0x5168, 0x9153,\n\t0x5169, 0x995F, 0x516A, 0x9960, 0x516B, 0x94AA, 0x516C, 0x8CF6,\n\t0x516D, 0x985A, 0x516E, 0x9961, 0x5171, 0x8BA4, 0x5175, 0x95BA,\n\t0x5176, 0x91B4, 0x5177, 0x8BEF, 0x5178, 0x9354, 0x517C, 0x8C93,\n\t0x5180, 0x9962, 0x5182, 0x9963, 0x5185, 0x93E0, 0x5186, 0x897E,\n\t0x5189, 0x9966, 0x518A, 0x8DFB, 0x518C, 0x9965, 0x518D, 0x8DC4,\n\t0x518F, 0x9967, 0x5190, 0xE3EC, 0x5191, 0x9968, 0x5192, 0x9660,\n\t0x5193, 0x9969, 0x5195, 0x996A, 0x5196, 0x996B, 0x5197, 0x8FE7,\n\t0x5199, 0x8ECA, 0x519D, 0xFA81, 0x51A0, 0x8AA5, 0x51A2, 0x996E,\n\t0x51A4, 0x996C, 0x51A5, 0x96BB, 0x51A6, 0x996D, 0x51A8, 0x9579,\n\t0x51A9, 0x996F, 0x51AA, 0x9970, 0x51AB, 0x9971, 0x51AC, 0x937E,\n\t0x51B0, 0x9975, 0x51B1, 0x9973, 0x51B2, 0x9974, 0x51B3, 0x9972,\n\t0x51B4, 0x8DE1, 0x51B5, 0x9976, 0x51B6, 0x96E8, 0x51B7, 0x97E2,\n\t0x51BD, 0x9977, 0x51BE, 0xFA82, 0x51C4, 0x90A6, 0x51C5, 0x9978,\n\t0x51C6, 0x8F79, 0x51C9, 0x9979, 0x51CB, 0x929C, 0x51CC, 0x97BD,\n\t0x51CD, 0x9380, 0x51D6, 0x99C3, 0x51DB, 0x997A, 0x51DC, 0xEAA3,\n\t0x51DD, 0x8BC3, 0x51E0, 0x997B, 0x51E1, 0x967D, 0x51E6, 0x8F88,\n\t0x51E7, 0x91FA, 0x51E9, 0x997D, 0x51EA, 0x93E2, 0x51EC, 0xFA83,\n\t0x51ED, 0x997E, 0x51F0, 0x9980, 0x51F1, 0x8A4D, 0x51F5, 0x9981,\n\t0x51F6, 0x8BA5, 0x51F8, 0x93CA, 0x51F9, 0x899A, 0x51FA, 0x8F6F,\n\t0x51FD, 0x949F, 0x51FE, 0x9982, 0x5200, 0x9381, 0x5203, 0x906E,\n\t0x5204, 0x9983, 0x5206, 0x95AA, 0x5207, 0x90D8, 0x5208, 0x8AA0,\n\t0x520A, 0x8AA7, 0x520B, 0x9984, 0x520E, 0x9986, 0x5211, 0x8C59,\n\t0x5214, 0x9985, 0x5215, 0xFA84, 0x5217, 0x97F1, 0x521D, 0x8F89,\n\t0x5224, 0x94BB, 0x5225, 0x95CA, 0x5227, 0x9987, 0x5229, 0x9798,\n\t0x522A, 0x9988, 0x522E, 0x9989, 0x5230, 0x939E, 0x5233, 0x998A,\n\t0x5236, 0x90A7, 0x5237, 0x8DFC, 0x5238, 0x8C94, 0x5239, 0x998B,\n\t0x523A, 0x8E68, 0x523B, 0x8D8F, 0x5243, 0x92E4, 0x5244, 0x998D,\n\t0x5247, 0x91A5, 0x524A, 0x8DED, 0x524B, 0x998E, 0x524C, 0x998F,\n\t0x524D, 0x914F, 0x524F, 0x998C, 0x5254, 0x9991, 0x5256, 0x9655,\n\t0x525B, 0x8D84, 0x525E, 0x9990, 0x5263, 0x8C95, 0x5264, 0x8DDC,\n\t0x5265, 0x948D, 0x5269, 0x9994, 0x526A, 0x9992, 0x526F, 0x959B,\n\t0x5270, 0x8FE8, 0x5271, 0x999B, 0x5272, 0x8A84, 0x5273, 0x9995,\n\t0x5274, 0x9993, 0x5275, 0x916E, 0x527D, 0x9997, 0x527F, 0x9996,\n\t0x5283, 0x8A63, 0x5287, 0x8C80, 0x5288, 0x999C, 0x5289, 0x97AB,\n\t0x528D, 0x9998, 0x5291, 0x999D, 0x5292, 0x999A, 0x5294, 0x9999,\n\t0x529B, 0x97CD, 0x529C, 0xFA85, 0x529F, 0x8CF7, 0x52A0, 0x89C1,\n\t0x52A3, 0x97F2, 0x52A6, 0xFA86, 0x52A9, 0x8F95, 0x52AA, 0x9377,\n\t0x52AB, 0x8D85, 0x52AC, 0x99A0, 0x52AD, 0x99A1, 0x52AF, 0xFB77,\n\t0x52B1, 0x97E3, 0x52B4, 0x984A, 0x52B5, 0x99A3, 0x52B9, 0x8CF8,\n\t0x52BC, 0x99A2, 0x52BE, 0x8A4E, 0x52C0, 0xFA87, 0x52C1, 0x99A4,\n\t0x52C3, 0x9675, 0x52C5, 0x92BA, 0x52C7, 0x9745, 0x52C9, 0x95D7,\n\t0x52CD, 0x99A5, 0x52D2, 0xE8D3, 0x52D5, 0x93AE, 0x52D7, 0x99A6,\n\t0x52D8, 0x8AA8, 0x52D9, 0x96B1, 0x52DB, 0xFA88, 0x52DD, 0x8F9F,\n\t0x52DE, 0x99A7, 0x52DF, 0x95E5, 0x52E0, 0x99AB, 0x52E2, 0x90A8,\n\t0x52E3, 0x99A8, 0x52E4, 0x8BCE, 0x52E6, 0x99A9, 0x52E7, 0x8AA9,\n\t0x52F2, 0x8C4D, 0x52F3, 0x99AC, 0x52F5, 0x99AD, 0x52F8, 0x99AE,\n\t0x52F9, 0x99AF, 0x52FA, 0x8ED9, 0x52FE, 0x8CF9, 0x52FF, 0x96DC,\n\t0x5300, 0xFA89, 0x5301, 0x96E6, 0x5302, 0x93F5, 0x5305, 0x95EF,\n\t0x5306, 0x99B0, 0x5307, 0xFA8A, 0x5308, 0x99B1, 0x530D, 0x99B3,\n\t0x530F, 0x99B5, 0x5310, 0x99B4, 0x5315, 0x99B6, 0x5316, 0x89BB,\n\t0x5317, 0x966B, 0x5319, 0x8DFA, 0x531A, 0x99B7, 0x531D, 0x9178,\n\t0x5320, 0x8FA0, 0x5321, 0x8BA7, 0x5323, 0x99B8, 0x5324, 0xFA8B,\n\t0x532A, 0x94D9, 0x532F, 0x99B9, 0x5331, 0x99BA, 0x5333, 0x99BB,\n\t0x5338, 0x99BC, 0x5339, 0x9543, 0x533A, 0x8BE6, 0x533B, 0x88E3,\n\t0x533F, 0x93BD, 0x5340, 0x99BD, 0x5341, 0x8F5C, 0x5343, 0x90E7,\n\t0x5345, 0x99BF, 0x5346, 0x99BE, 0x5347, 0x8FA1, 0x5348, 0x8CDF,\n\t0x5349, 0x99C1, 0x534A, 0x94BC, 0x534D, 0x99C2, 0x5351, 0x94DA,\n\t0x5352, 0x91B2, 0x5353, 0x91EC, 0x5354, 0x8BA6, 0x5357, 0x93EC,\n\t0x5358, 0x9250, 0x535A, 0x948E, 0x535C, 0x966D, 0x535E, 0x99C4,\n\t0x5360, 0x90E8, 0x5366, 0x8C54, 0x5369, 0x99C5, 0x536E, 0x99C6,\n\t0x536F, 0x894B, 0x5370, 0x88F3, 0x5371, 0x8AEB, 0x5372, 0xFA8C,\n\t0x5373, 0x91A6, 0x5374, 0x8B70, 0x5375, 0x9791, 0x5377, 0x99C9,\n\t0x5378, 0x89B5, 0x537B, 0x99C8, 0x537F, 0x8BA8, 0x5382, 0x99CA,\n\t0x5384, 0x96EF, 0x5393, 0xFA8D, 0x5396, 0x99CB, 0x5398, 0x97D0,\n\t0x539A, 0x8CFA, 0x539F, 0x8CB4, 0x53A0, 0x99CC, 0x53A5, 0x99CE,\n\t0x53A6, 0x99CD, 0x53A8, 0x907E, 0x53A9, 0x8958, 0x53AD, 0x897D,\n\t0x53AE, 0x99CF, 0x53B0, 0x99D0, 0x53B2, 0xFA8E, 0x53B3, 0x8CB5,\n\t0x53B6, 0x99D1, 0x53BB, 0x8B8E, 0x53C2, 0x8E51, 0x53C3, 0x99D2,\n\t0x53C8, 0x9694, 0x53C9, 0x8DB3, 0x53CA, 0x8B79, 0x53CB, 0x9746,\n\t0x53CC, 0x916F, 0x53CD, 0x94BD, 0x53CE, 0x8EFB, 0x53D4, 0x8F66,\n\t0x53D6, 0x8EE6, 0x53D7, 0x8EF3, 0x53D9, 0x8F96, 0x53DB, 0x94BE,\n\t0x53DD, 0xFA8F, 0x53DF, 0x99D5, 0x53E1, 0x8962, 0x53E2, 0x9170,\n\t0x53E3, 0x8CFB, 0x53E4, 0x8CC3, 0x53E5, 0x8BE5, 0x53E8, 0x99D9,\n\t0x53E9, 0x9240, 0x53EA, 0x91FC, 0x53EB, 0x8BA9, 0x53EC, 0x8FA2,\n\t0x53ED, 0x99DA, 0x53EE, 0x99D8, 0x53EF, 0x89C2, 0x53F0, 0x91E4,\n\t0x53F1, 0x8EB6, 0x53F2, 0x8E6A, 0x53F3, 0x8945, 0x53F6, 0x8A90,\n\t0x53F7, 0x8D86, 0x53F8, 0x8E69, 0x53FA, 0x99DB, 0x5401, 0x99DC,\n\t0x5403, 0x8B68, 0x5404, 0x8A65, 0x5408, 0x8D87, 0x5409, 0x8B67,\n\t0x540A, 0x92DD, 0x540B, 0x8944, 0x540C, 0x93AF, 0x540D, 0x96BC,\n\t0x540E, 0x8D40, 0x540F, 0x9799, 0x5410, 0x9366, 0x5411, 0x8CFC,\n\t0x541B, 0x8C4E, 0x541D, 0x99E5, 0x541F, 0x8BE1, 0x5420, 0x9669,\n\t0x5426, 0x94DB, 0x5429, 0x99E4, 0x542B, 0x8ADC, 0x542C, 0x99DF,\n\t0x542D, 0x99E0, 0x542E, 0x99E2, 0x5436, 0x99E3, 0x5438, 0x8B7A,\n\t0x5439, 0x9081, 0x543B, 0x95AB, 0x543C, 0x99E1, 0x543D, 0x99DD,\n\t0x543E, 0x8CE1, 0x5440, 0x99DE, 0x5442, 0x9843, 0x5446, 0x95F0,\n\t0x5448, 0x92E6, 0x5449, 0x8CE0, 0x544A, 0x8D90, 0x544E, 0x99E6,\n\t0x5451, 0x93DB, 0x545F, 0x99EA, 0x5468, 0x8EFC, 0x546A, 0x8EF4,\n\t0x5470, 0x99ED, 0x5471, 0x99EB, 0x5473, 0x96A1, 0x5475, 0x99E8,\n\t0x5476, 0x99F1, 0x5477, 0x99EC, 0x547B, 0x99EF, 0x547C, 0x8CC4,\n\t0x547D, 0x96BD, 0x5480, 0x99F0, 0x5484, 0x99F2, 0x5486, 0x99F4,\n\t0x548A, 0xFA92, 0x548B, 0x8DEE, 0x548C, 0x9861, 0x548E, 0x99E9,\n\t0x548F, 0x99E7, 0x5490, 0x99F3, 0x5492, 0x99EE, 0x549C, 0xFA91,\n\t0x54A2, 0x99F6, 0x54A4, 0x9A42, 0x54A5, 0x99F8, 0x54A8, 0x99FC,\n\t0x54A9, 0xFA93, 0x54AB, 0x9A40, 0x54AC, 0x99F9, 0x54AF, 0x9A5D,\n\t0x54B2, 0x8DE7, 0x54B3, 0x8A50, 0x54B8, 0x99F7, 0x54BC, 0x9A44,\n\t0x54BD, 0x88F4, 0x54BE, 0x9A43, 0x54C0, 0x88A3, 0x54C1, 0x9569,\n\t0x54C2, 0x9A41, 0x54C4, 0x99FA, 0x54C7, 0x99F5, 0x54C8, 0x99FB,\n\t0x54C9, 0x8DC6, 0x54D8, 0x9A45, 0x54E1, 0x88F5, 0x54E2, 0x9A4E,\n\t0x54E5, 0x9A46, 0x54E6, 0x9A47, 0x54E8, 0x8FA3, 0x54E9, 0x9689,\n\t0x54ED, 0x9A4C, 0x54EE, 0x9A4B, 0x54F2, 0x934E, 0x54FA, 0x9A4D,\n\t0x54FD, 0x9A4A, 0x54FF, 0xFA94, 0x5504, 0x8953, 0x5506, 0x8DB4,\n\t0x5507, 0x904F, 0x550F, 0x9A48, 0x5510, 0x9382, 0x5514, 0x9A49,\n\t0x5516, 0x88A0, 0x552E, 0x9A53, 0x552F, 0x9742, 0x5531, 0x8FA5,\n\t0x5533, 0x9A59, 0x5538, 0x9A58, 0x5539, 0x9A4F, 0x553E, 0x91C1,\n\t0x5540, 0x9A50, 0x5544, 0x91ED, 0x5545, 0x9A55, 0x5546, 0x8FA4,\n\t0x554C, 0x9A52, 0x554F, 0x96E2, 0x5553, 0x8C5B, 0x5556, 0x9A56,\n\t0x5557, 0x9A57, 0x555C, 0x9A54, 0x555D, 0x9A5A, 0x5563, 0x9A51,\n\t0x557B, 0x9A60, 0x557C, 0x9A65, 0x557E, 0x9A61, 0x5580, 0x9A5C,\n\t0x5583, 0x9A66, 0x5584, 0x9150, 0x5586, 0xFA95, 0x5587, 0x9A68,\n\t0x5589, 0x8D41, 0x558A, 0x9A5E, 0x558B, 0x929D, 0x5598, 0x9A62,\n\t0x5599, 0x9A5B, 0x559A, 0x8AAB, 0x559C, 0x8AEC, 0x559D, 0x8A85,\n\t0x559E, 0x9A63, 0x559F, 0x9A5F, 0x55A7, 0x8C96, 0x55A8, 0x9A69,\n\t0x55A9, 0x9A67, 0x55AA, 0x9172, 0x55AB, 0x8B69, 0x55AC, 0x8BAA,\n\t0x55AE, 0x9A64, 0x55B0, 0x8BF2, 0x55B6, 0x8963, 0x55C4, 0x9A6D,\n\t0x55C5, 0x9A6B, 0x55C7, 0x9AA5, 0x55D4, 0x9A70, 0x55DA, 0x9A6A,\n\t0x55DC, 0x9A6E, 0x55DF, 0x9A6C, 0x55E3, 0x8E6B, 0x55E4, 0x9A6F,\n\t0x55F7, 0x9A72, 0x55F9, 0x9A77, 0x55FD, 0x9A75, 0x55FE, 0x9A74,\n\t0x5606, 0x9251, 0x5609, 0x89C3, 0x5614, 0x9A71, 0x5616, 0x9A73,\n\t0x5617, 0x8FA6, 0x5618, 0x8952, 0x561B, 0x9A76, 0x5629, 0x89DC,\n\t0x562F, 0x9A82, 0x5631, 0x8FFA, 0x5632, 0x9A7D, 0x5634, 0x9A7B,\n\t0x5636, 0x9A7C, 0x5638, 0x9A7E, 0x5642, 0x895C, 0x564C, 0x9158,\n\t0x564E, 0x9A78, 0x5650, 0x9A79, 0x565B, 0x8A9A, 0x5664, 0x9A81,\n\t0x5668, 0x8AED, 0x566A, 0x9A84, 0x566B, 0x9A80, 0x566C, 0x9A83,\n\t0x5674, 0x95AC, 0x5678, 0x93D3, 0x567A, 0x94B6, 0x5680, 0x9A86,\n\t0x5686, 0x9A85, 0x5687, 0x8A64, 0x568A, 0x9A87, 0x568F, 0x9A8A,\n\t0x5694, 0x9A89, 0x56A0, 0x9A88, 0x56A2, 0x9458, 0x56A5, 0x9A8B,\n\t0x56AE, 0x9A8C, 0x56B4, 0x9A8E, 0x56B6, 0x9A8D, 0x56BC, 0x9A90,\n\t0x56C0, 0x9A93, 0x56C1, 0x9A91, 0x56C2, 0x9A8F, 0x56C3, 0x9A92,\n\t0x56C8, 0x9A94, 0x56CE, 0x9A95, 0x56D1, 0x9A96, 0x56D3, 0x9A97,\n\t0x56D7, 0x9A98, 0x56D8, 0x9964, 0x56DA, 0x8EFA, 0x56DB, 0x8E6C,\n\t0x56DE, 0x89F1, 0x56E0, 0x88F6, 0x56E3, 0x9263, 0x56EE, 0x9A99,\n\t0x56F0, 0x8DA2, 0x56F2, 0x88CD, 0x56F3, 0x907D, 0x56F9, 0x9A9A,\n\t0x56FA, 0x8CC5, 0x56FD, 0x8D91, 0x56FF, 0x9A9C, 0x5700, 0x9A9B,\n\t0x5703, 0x95DE, 0x5704, 0x9A9D, 0x5708, 0x9A9F, 0x5709, 0x9A9E,\n\t0x570B, 0x9AA0, 0x570D, 0x9AA1, 0x570F, 0x8C97, 0x5712, 0x8980,\n\t0x5713, 0x9AA2, 0x5716, 0x9AA4, 0x5718, 0x9AA3, 0x571C, 0x9AA6,\n\t0x571F, 0x9379, 0x5726, 0x9AA7, 0x5727, 0x88B3, 0x5728, 0x8DDD,\n\t0x572D, 0x8C5C, 0x5730, 0x926E, 0x5737, 0x9AA8, 0x5738, 0x9AA9,\n\t0x573B, 0x9AAB, 0x5740, 0x9AAC, 0x5742, 0x8DE2, 0x5747, 0x8BCF,\n\t0x574A, 0x9656, 0x574E, 0x9AAA, 0x574F, 0x9AAD, 0x5750, 0x8DBF,\n\t0x5751, 0x8D42, 0x5759, 0xFA96, 0x5761, 0x9AB1, 0x5764, 0x8DA3,\n\t0x5765, 0xFA97, 0x5766, 0x9252, 0x5769, 0x9AAE, 0x576A, 0x92D8,\n\t0x577F, 0x9AB2, 0x5782, 0x9082, 0x5788, 0x9AB0, 0x5789, 0x9AB3,\n\t0x578B, 0x8C5E, 0x5793, 0x9AB4, 0x57A0, 0x9AB5, 0x57A2, 0x8D43,\n\t0x57A3, 0x8A5F, 0x57A4, 0x9AB7, 0x57AA, 0x9AB8, 0x57AC, 0xFA98,\n\t0x57B0, 0x9AB9, 0x57B3, 0x9AB6, 0x57C0, 0x9AAF, 0x57C3, 0x9ABA,\n\t0x57C6, 0x9ABB, 0x57C7, 0xFA9A, 0x57C8, 0xFA99, 0x57CB, 0x9684,\n\t0x57CE, 0x8FE9, 0x57D2, 0x9ABD, 0x57D3, 0x9ABE, 0x57D4, 0x9ABC,\n\t0x57D6, 0x9AC0, 0x57DC, 0x9457, 0x57DF, 0x88E6, 0x57E0, 0x9575,\n\t0x57E3, 0x9AC1, 0x57F4, 0x8FFB, 0x57F7, 0x8EB7, 0x57F9, 0x947C,\n\t0x57FA, 0x8AEE, 0x57FC, 0x8DE9, 0x5800, 0x9678, 0x5802, 0x93B0,\n\t0x5805, 0x8C98, 0x5806, 0x91CD, 0x580A, 0x9ABF, 0x580B, 0x9AC2,\n\t0x5815, 0x91C2, 0x5819, 0x9AC3, 0x581D, 0x9AC4, 0x5821, 0x9AC6,\n\t0x5824, 0x92E7, 0x582A, 0x8AAC, 0x582F, 0xEA9F, 0x5830, 0x8981,\n\t0x5831, 0x95F1, 0x5834, 0x8FEA, 0x5835, 0x9367, 0x583A, 0x8DE4,\n\t0x583D, 0x9ACC, 0x5840, 0x95BB, 0x5841, 0x97DB, 0x584A, 0x89F2,\n\t0x584B, 0x9AC8, 0x5851, 0x9159, 0x5852, 0x9ACB, 0x5854, 0x9383,\n\t0x5857, 0x9368, 0x5858, 0x9384, 0x5859, 0x94B7, 0x585A, 0x92CB,\n\t0x585E, 0x8DC7, 0x5862, 0x9AC7, 0x5869, 0x8996, 0x586B, 0x9355,\n\t0x5870, 0x9AC9, 0x5872, 0x9AC5, 0x5875, 0x906F, 0x5879, 0x9ACD,\n\t0x587E, 0x8F6D, 0x5883, 0x8BAB, 0x5885, 0x9ACE, 0x5893, 0x95E6,\n\t0x5897, 0x919D, 0x589C, 0x92C4, 0x589E, 0xFA9D, 0x589F, 0x9AD0,\n\t0x58A8, 0x966E, 0x58AB, 0x9AD1, 0x58AE, 0x9AD6, 0x58B2, 0xFA9E,\n\t0x58B3, 0x95AD, 0x58B8, 0x9AD5, 0x58B9, 0x9ACF, 0x58BA, 0x9AD2,\n\t0x58BB, 0x9AD4, 0x58BE, 0x8DA4, 0x58C1, 0x95C7, 0x58C5, 0x9AD7,\n\t0x58C7, 0x9264, 0x58CA, 0x89F3, 0x58CC, 0x8FEB, 0x58D1, 0x9AD9,\n\t0x58D3, 0x9AD8, 0x58D5, 0x8D88, 0x58D7, 0x9ADA, 0x58D8, 0x9ADC,\n\t0x58D9, 0x9ADB, 0x58DC, 0x9ADE, 0x58DE, 0x9AD3, 0x58DF, 0x9AE0,\n\t0x58E4, 0x9ADF, 0x58E5, 0x9ADD, 0x58EB, 0x8E6D, 0x58EC, 0x9070,\n\t0x58EE, 0x9173, 0x58EF, 0x9AE1, 0x58F0, 0x90BA, 0x58F1, 0x88EB,\n\t0x58F2, 0x9484, 0x58F7, 0x92D9, 0x58F9, 0x9AE3, 0x58FA, 0x9AE2,\n\t0x58FB, 0x9AE4, 0x58FC, 0x9AE5, 0x58FD, 0x9AE6, 0x5902, 0x9AE7,\n\t0x5909, 0x95CF, 0x590A, 0x9AE8, 0x590B, 0xFA9F, 0x590F, 0x89C4,\n\t0x5910, 0x9AE9, 0x5915, 0x975B, 0x5916, 0x8A4F, 0x5918, 0x99C7,\n\t0x5919, 0x8F67, 0x591A, 0x91BD, 0x591B, 0x9AEA, 0x591C, 0x96E9,\n\t0x5922, 0x96B2, 0x5925, 0x9AEC, 0x5927, 0x91E5, 0x5929, 0x9356,\n\t0x592A, 0x91BE, 0x592B, 0x9576, 0x592C, 0x9AED, 0x592D, 0x9AEE,\n\t0x592E, 0x899B, 0x5931, 0x8EB8, 0x5932, 0x9AEF, 0x5937, 0x88CE,\n\t0x5938, 0x9AF0, 0x593E, 0x9AF1, 0x5944, 0x8982, 0x5947, 0x8AEF,\n\t0x5948, 0x93DE, 0x5949, 0x95F2, 0x594E, 0x9AF5, 0x594F, 0x9174,\n\t0x5950, 0x9AF4, 0x5951, 0x8C5F, 0x5953, 0xFAA0, 0x5954, 0x967A,\n\t0x5955, 0x9AF3, 0x5957, 0x9385, 0x5958, 0x9AF7, 0x595A, 0x9AF6,\n\t0x595B, 0xFAA1, 0x595D, 0xFAA2, 0x5960, 0x9AF9, 0x5962, 0x9AF8,\n\t0x5963, 0xFAA3, 0x5965, 0x899C, 0x5967, 0x9AFA, 0x5968, 0x8FA7,\n\t0x5969, 0x9AFC, 0x596A, 0x9244, 0x596C, 0x9AFB, 0x596E, 0x95B1,\n\t0x5973, 0x8F97, 0x5974, 0x937A, 0x5978, 0x9B40, 0x597D, 0x8D44,\n\t0x5981, 0x9B41, 0x5982, 0x9440, 0x5983, 0x94DC, 0x5984, 0x96CF,\n\t0x598A, 0x9444, 0x598D, 0x9B4A, 0x5993, 0x8B57, 0x5996, 0x9764,\n\t0x5999, 0x96AD, 0x599B, 0x9BAA, 0x599D, 0x9B42, 0x59A3, 0x9B45,\n\t0x59A4, 0xFAA4, 0x59A5, 0x91C3, 0x59A8, 0x9657, 0x59AC, 0x9369,\n\t0x59B2, 0x9B46, 0x59B9, 0x9685, 0x59BA, 0xFAA5, 0x59BB, 0x8DC8,\n\t0x59BE, 0x8FA8, 0x59C6, 0x9B47, 0x59C9, 0x8E6F, 0x59CB, 0x8E6E,\n\t0x59D0, 0x88B7, 0x59D1, 0x8CC6, 0x59D3, 0x90A9, 0x59D4, 0x88CF,\n\t0x59D9, 0x9B4B, 0x59DA, 0x9B4C, 0x59DC, 0x9B49, 0x59E5, 0x8957,\n\t0x59E6, 0x8AAD, 0x59E8, 0x9B48, 0x59EA, 0x96C3, 0x59EB, 0x9550,\n\t0x59F6, 0x88A6, 0x59FB, 0x88F7, 0x59FF, 0x8E70, 0x5A01, 0x88D0,\n\t0x5A03, 0x88A1, 0x5A09, 0x9B51, 0x5A11, 0x9B4F, 0x5A18, 0x96BA,\n\t0x5A1A, 0x9B52, 0x5A1C, 0x9B50, 0x5A1F, 0x9B4E, 0x5A20, 0x9050,\n\t0x5A25, 0x9B4D, 0x5A29, 0x95D8, 0x5A2F, 0x8CE2, 0x5A35, 0x9B56,\n\t0x5A36, 0x9B57, 0x5A3C, 0x8FA9, 0x5A40, 0x9B53, 0x5A41, 0x984B,\n\t0x5A46, 0x946B, 0x5A49, 0x9B55, 0x5A5A, 0x8DA5, 0x5A62, 0x9B58,\n\t0x5A66, 0x9577, 0x5A6A, 0x9B59, 0x5A6C, 0x9B54, 0x5A7F, 0x96B9,\n\t0x5A92, 0x947D, 0x5A9A, 0x9B5A, 0x5A9B, 0x9551, 0x5ABC, 0x9B5B,\n\t0x5ABD, 0x9B5F, 0x5ABE, 0x9B5C, 0x5AC1, 0x89C5, 0x5AC2, 0x9B5E,\n\t0x5AC9, 0x8EB9, 0x5ACB, 0x9B5D, 0x5ACC, 0x8C99, 0x5AD0, 0x9B6B,\n\t0x5AD6, 0x9B64, 0x5AD7, 0x9B61, 0x5AE1, 0x9284, 0x5AE3, 0x9B60,\n\t0x5AE6, 0x9B62, 0x5AE9, 0x9B63, 0x5AFA, 0x9B65, 0x5AFB, 0x9B66,\n\t0x5B09, 0x8AF0, 0x5B0B, 0x9B68, 0x5B0C, 0x9B67, 0x5B16, 0x9B69,\n\t0x5B22, 0x8FEC, 0x5B2A, 0x9B6C, 0x5B2C, 0x92DA, 0x5B30, 0x8964,\n\t0x5B32, 0x9B6A, 0x5B36, 0x9B6D, 0x5B3E, 0x9B6E, 0x5B40, 0x9B71,\n\t0x5B43, 0x9B6F, 0x5B45, 0x9B70, 0x5B50, 0x8E71, 0x5B51, 0x9B72,\n\t0x5B54, 0x8D45, 0x5B55, 0x9B73, 0x5B56, 0xFAA6, 0x5B57, 0x8E9A,\n\t0x5B58, 0x91B6, 0x5B5A, 0x9B74, 0x5B5B, 0x9B75, 0x5B5C, 0x8E79,\n\t0x5B5D, 0x8D46, 0x5B5F, 0x96D0, 0x5B63, 0x8B47, 0x5B64, 0x8CC7,\n\t0x5B65, 0x9B76, 0x5B66, 0x8A77, 0x5B69, 0x9B77, 0x5B6B, 0x91B7,\n\t0x5B70, 0x9B78, 0x5B71, 0x9BA1, 0x5B73, 0x9B79, 0x5B75, 0x9B7A,\n\t0x5B78, 0x9B7B, 0x5B7A, 0x9B7D, 0x5B80, 0x9B7E, 0x5B83, 0x9B80,\n\t0x5B85, 0x91EE, 0x5B87, 0x8946, 0x5B88, 0x8EE7, 0x5B89, 0x88C0,\n\t0x5B8B, 0x9176, 0x5B8C, 0x8AAE, 0x5B8D, 0x8EB3, 0x5B8F, 0x8D47,\n\t0x5B95, 0x9386, 0x5B97, 0x8F40, 0x5B98, 0x8AAF, 0x5B99, 0x9288,\n\t0x5B9A, 0x92E8, 0x5B9B, 0x88B6, 0x5B9C, 0x8B58, 0x5B9D, 0x95F3,\n\t0x5B9F, 0x8EC0, 0x5BA2, 0x8B71, 0x5BA3, 0x90E9, 0x5BA4, 0x8EBA,\n\t0x5BA5, 0x9747, 0x5BA6, 0x9B81, 0x5BAE, 0x8B7B, 0x5BB0, 0x8DC9,\n\t0x5BB3, 0x8A51, 0x5BB4, 0x8983, 0x5BB5, 0x8FAA, 0x5BB6, 0x89C6,\n\t0x5BB8, 0x9B82, 0x5BB9, 0x9765, 0x5BBF, 0x8F68, 0x5BC0, 0xFAA7,\n\t0x5BC2, 0x8EE2, 0x5BC3, 0x9B83, 0x5BC4, 0x8AF1, 0x5BC5, 0x93D0,\n\t0x5BC6, 0x96A7, 0x5BC7, 0x9B84, 0x5BC9, 0x9B85, 0x5BCC, 0x9578,\n\t0x5BD0, 0x9B87, 0x5BD2, 0x8AA6, 0x5BD3, 0x8BF5, 0x5BD4, 0x9B86,\n\t0x5BD8, 0xFAA9, 0x5BDB, 0x8AB0, 0x5BDD, 0x9051, 0x5BDE, 0x9B8B,\n\t0x5BDF, 0x8E40, 0x5BE1, 0x89C7, 0x5BE2, 0x9B8A, 0x5BE4, 0x9B88,\n\t0x5BE5, 0x9B8C, 0x5BE6, 0x9B89, 0x5BE7, 0x944A, 0x5BE8, 0x9ECB,\n\t0x5BE9, 0x9052, 0x5BEB, 0x9B8D, 0x5BEC, 0xFAAA, 0x5BEE, 0x97BE,\n\t0x5BF0, 0x9B8E, 0x5BF3, 0x9B90, 0x5BF5, 0x929E, 0x5BF6, 0x9B8F,\n\t0x5BF8, 0x90A1, 0x5BFA, 0x8E9B, 0x5BFE, 0x91CE, 0x5BFF, 0x8EF5,\n\t0x5C01, 0x9595, 0x5C02, 0x90EA, 0x5C04, 0x8ECB, 0x5C05, 0x9B91,\n\t0x5C06, 0x8FAB, 0x5C07, 0x9B92, 0x5C08, 0x9B93, 0x5C09, 0x88D1,\n\t0x5C0A, 0x91B8, 0x5C0B, 0x9071, 0x5C0D, 0x9B94, 0x5C0E, 0x93B1,\n\t0x5C0F, 0x8FAC, 0x5C11, 0x8FAD, 0x5C13, 0x9B95, 0x5C16, 0x90EB,\n\t0x5C1A, 0x8FAE, 0x5C1E, 0xFAAB, 0x5C20, 0x9B96, 0x5C22, 0x9B97,\n\t0x5C24, 0x96DE, 0x5C28, 0x9B98, 0x5C2D, 0x8BC4, 0x5C31, 0x8F41,\n\t0x5C38, 0x9B99, 0x5C39, 0x9B9A, 0x5C3A, 0x8EDA, 0x5C3B, 0x904B,\n\t0x5C3C, 0x93F2, 0x5C3D, 0x9073, 0x5C3E, 0x94F6, 0x5C3F, 0x9441,\n\t0x5C40, 0x8BC7, 0x5C41, 0x9B9B, 0x5C45, 0x8B8F, 0x5C46, 0x9B9C,\n\t0x5C48, 0x8BFC, 0x5C4A, 0x93CD, 0x5C4B, 0x89AE, 0x5C4D, 0x8E72,\n\t0x5C4E, 0x9B9D, 0x5C4F, 0x9BA0, 0x5C50, 0x9B9F, 0x5C51, 0x8BFB,\n\t0x5C53, 0x9B9E, 0x5C55, 0x9357, 0x5C5E, 0x91AE, 0x5C60, 0x936A,\n\t0x5C61, 0x8EC6, 0x5C64, 0x9177, 0x5C65, 0x979A, 0x5C6C, 0x9BA2,\n\t0x5C6E, 0x9BA3, 0x5C6F, 0x93D4, 0x5C71, 0x8E52, 0x5C76, 0x9BA5,\n\t0x5C79, 0x9BA6, 0x5C8C, 0x9BA7, 0x5C90, 0x8AF2, 0x5C91, 0x9BA8,\n\t0x5C94, 0x9BA9, 0x5CA1, 0x89AA, 0x5CA6, 0xFAAC, 0x5CA8, 0x915A,\n\t0x5CA9, 0x8AE2, 0x5CAB, 0x9BAB, 0x5CAC, 0x96A6, 0x5CB1, 0x91D0,\n\t0x5CB3, 0x8A78, 0x5CB6, 0x9BAD, 0x5CB7, 0x9BAF, 0x5CB8, 0x8ADD,\n\t0x5CBA, 0xFAAD, 0x5CBB, 0x9BAC, 0x5CBC, 0x9BAE, 0x5CBE, 0x9BB1,\n\t0x5CC5, 0x9BB0, 0x5CC7, 0x9BB2, 0x5CD9, 0x9BB3, 0x5CE0, 0x93BB,\n\t0x5CE1, 0x8BAC, 0x5CE8, 0x89E3, 0x5CE9, 0x9BB4, 0x5CEA, 0x9BB9,\n\t0x5CED, 0x9BB7, 0x5CEF, 0x95F5, 0x5CF0, 0x95F4, 0x5CF5, 0xFAAE,\n\t0x5CF6, 0x9387, 0x5CFA, 0x9BB6, 0x5CFB, 0x8F73, 0x5CFD, 0x9BB5,\n\t0x5D07, 0x9092, 0x5D0B, 0x9BBA, 0x5D0E, 0x8DE8, 0x5D11, 0x9BC0,\n\t0x5D14, 0x9BC1, 0x5D15, 0x9BBB, 0x5D16, 0x8A52, 0x5D17, 0x9BBC,\n\t0x5D18, 0x9BC5, 0x5D19, 0x9BC4, 0x5D1A, 0x9BC3, 0x5D1B, 0x9BBF,\n\t0x5D1F, 0x9BBE, 0x5D22, 0x9BC2, 0x5D27, 0xFAAF, 0x5D29, 0x95F6,\n\t0x5D42, 0xFAB2, 0x5D4B, 0x9BC9, 0x5D4C, 0x9BC6, 0x5D4E, 0x9BC8,\n\t0x5D50, 0x9792, 0x5D52, 0x9BC7, 0x5D53, 0xFAB0, 0x5D5C, 0x9BBD,\n\t0x5D69, 0x9093, 0x5D6C, 0x9BCA, 0x5D6D, 0xFAB3, 0x5D6F, 0x8DB5,\n\t0x5D73, 0x9BCB, 0x5D76, 0x9BCC, 0x5D82, 0x9BCF, 0x5D84, 0x9BCE,\n\t0x5D87, 0x9BCD, 0x5D8B, 0x9388, 0x5D8C, 0x9BB8, 0x5D90, 0x9BD5,\n\t0x5D9D, 0x9BD1, 0x5DA2, 0x9BD0, 0x5DAC, 0x9BD2, 0x5DAE, 0x9BD3,\n\t0x5DB7, 0x9BD6, 0x5DB8, 0xFAB4, 0x5DB9, 0xFAB5, 0x5DBA, 0x97E4,\n\t0x5DBC, 0x9BD7, 0x5DBD, 0x9BD4, 0x5DC9, 0x9BD8, 0x5DCC, 0x8ADE,\n\t0x5DCD, 0x9BD9, 0x5DD0, 0xFAB6, 0x5DD2, 0x9BDB, 0x5DD3, 0x9BDA,\n\t0x5DD6, 0x9BDC, 0x5DDB, 0x9BDD, 0x5DDD, 0x90EC, 0x5DDE, 0x8F42,\n\t0x5DE1, 0x8F84, 0x5DE3, 0x9183, 0x5DE5, 0x8D48, 0x5DE6, 0x8DB6,\n\t0x5DE7, 0x8D49, 0x5DE8, 0x8B90, 0x5DEB, 0x9BDE, 0x5DEE, 0x8DB7,\n\t0x5DF1, 0x8CC8, 0x5DF2, 0x9BDF, 0x5DF3, 0x96A4, 0x5DF4, 0x9462,\n\t0x5DF5, 0x9BE0, 0x5DF7, 0x8D4A, 0x5DFB, 0x8AAA, 0x5DFD, 0x9246,\n\t0x5DFE, 0x8BD0, 0x5E02, 0x8E73, 0x5E03, 0x957A, 0x5E06, 0x94BF,\n\t0x5E0B, 0x9BE1, 0x5E0C, 0x8AF3, 0x5E11, 0x9BE4, 0x5E16, 0x929F,\n\t0x5E19, 0x9BE3, 0x5E1A, 0x9BE2, 0x5E1B, 0x9BE5, 0x5E1D, 0x92E9,\n\t0x5E25, 0x9083, 0x5E2B, 0x8E74, 0x5E2D, 0x90C8, 0x5E2F, 0x91D1,\n\t0x5E30, 0x8B41, 0x5E33, 0x92A0, 0x5E36, 0x9BE6, 0x5E37, 0x9BE7,\n\t0x5E38, 0x8FED, 0x5E3D, 0x9658, 0x5E40, 0x9BEA, 0x5E43, 0x9BE9,\n\t0x5E44, 0x9BE8, 0x5E45, 0x959D, 0x5E47, 0x9BF1, 0x5E4C, 0x9679,\n\t0x5E4E, 0x9BEB, 0x5E54, 0x9BED, 0x5E55, 0x968B, 0x5E57, 0x9BEC,\n\t0x5E5F, 0x9BEE, 0x5E61, 0x94A6, 0x5E62, 0x9BEF, 0x5E63, 0x95BC,\n\t0x5E64, 0x9BF0, 0x5E72, 0x8AB1, 0x5E73, 0x95BD, 0x5E74, 0x944E,\n\t0x5E75, 0x9BF2, 0x5E76, 0x9BF3, 0x5E78, 0x8D4B, 0x5E79, 0x8AB2,\n\t0x5E7A, 0x9BF4, 0x5E7B, 0x8CB6, 0x5E7C, 0x9763, 0x5E7D, 0x9748,\n\t0x5E7E, 0x8AF4, 0x5E7F, 0x9BF6, 0x5E81, 0x92A1, 0x5E83, 0x8D4C,\n\t0x5E84, 0x8FAF, 0x5E87, 0x94DD, 0x5E8A, 0x8FB0, 0x5E8F, 0x8F98,\n\t0x5E95, 0x92EA, 0x5E96, 0x95F7, 0x5E97, 0x9358, 0x5E9A, 0x8D4D,\n\t0x5E9C, 0x957B, 0x5EA0, 0x9BF7, 0x5EA6, 0x9378, 0x5EA7, 0x8DC0,\n\t0x5EAB, 0x8CC9, 0x5EAD, 0x92EB, 0x5EB5, 0x88C1, 0x5EB6, 0x8F8E,\n\t0x5EB7, 0x8D4E, 0x5EB8, 0x9766, 0x5EC1, 0x9BF8, 0x5EC2, 0x9BF9,\n\t0x5EC3, 0x9470, 0x5EC8, 0x9BFA, 0x5EC9, 0x97F5, 0x5ECA, 0x984C,\n\t0x5ECF, 0x9BFC, 0x5ED0, 0x9BFB, 0x5ED3, 0x8A66, 0x5ED6, 0x9C40,\n\t0x5EDA, 0x9C43, 0x5EDB, 0x9C44, 0x5EDD, 0x9C42, 0x5EDF, 0x955F,\n\t0x5EE0, 0x8FB1, 0x5EE1, 0x9C46, 0x5EE2, 0x9C45, 0x5EE3, 0x9C41,\n\t0x5EE8, 0x9C47, 0x5EE9, 0x9C48, 0x5EEC, 0x9C49, 0x5EF0, 0x9C4C,\n\t0x5EF1, 0x9C4A, 0x5EF3, 0x9C4B, 0x5EF4, 0x9C4D, 0x5EF6, 0x8984,\n\t0x5EF7, 0x92EC, 0x5EF8, 0x9C4E, 0x5EFA, 0x8C9A, 0x5EFB, 0x89F4,\n\t0x5EFC, 0x9455, 0x5EFE, 0x9C4F, 0x5EFF, 0x93F9, 0x5F01, 0x95D9,\n\t0x5F03, 0x9C50, 0x5F04, 0x984D, 0x5F09, 0x9C51, 0x5F0A, 0x95BE,\n\t0x5F0B, 0x9C54, 0x5F0C, 0x989F, 0x5F0D, 0x98AF, 0x5F0F, 0x8EAE,\n\t0x5F10, 0x93F3, 0x5F11, 0x9C55, 0x5F13, 0x8B7C, 0x5F14, 0x92A2,\n\t0x5F15, 0x88F8, 0x5F16, 0x9C56, 0x5F17, 0x95A4, 0x5F18, 0x8D4F,\n\t0x5F1B, 0x926F, 0x5F1F, 0x92ED, 0x5F21, 0xFAB7, 0x5F25, 0x96ED,\n\t0x5F26, 0x8CB7, 0x5F27, 0x8CCA, 0x5F29, 0x9C57, 0x5F2D, 0x9C58,\n\t0x5F2F, 0x9C5E, 0x5F31, 0x8EE3, 0x5F34, 0xFAB8, 0x5F35, 0x92A3,\n\t0x5F37, 0x8BAD, 0x5F38, 0x9C59, 0x5F3C, 0x954A, 0x5F3E, 0x9265,\n\t0x5F41, 0x9C5A, 0x5F45, 0xFA67, 0x5F48, 0x9C5B, 0x5F4A, 0x8BAE,\n\t0x5F4C, 0x9C5C, 0x5F4E, 0x9C5D, 0x5F51, 0x9C5F, 0x5F53, 0x9396,\n\t0x5F56, 0x9C60, 0x5F57, 0x9C61, 0x5F59, 0x9C62, 0x5F5C, 0x9C53,\n\t0x5F5D, 0x9C52, 0x5F61, 0x9C63, 0x5F62, 0x8C60, 0x5F66, 0x9546,\n\t0x5F67, 0xFAB9, 0x5F69, 0x8DCA, 0x5F6A, 0x9556, 0x5F6B, 0x92A4,\n\t0x5F6C, 0x956A, 0x5F6D, 0x9C64, 0x5F70, 0x8FB2, 0x5F71, 0x8965,\n\t0x5F73, 0x9C65, 0x5F77, 0x9C66, 0x5F79, 0x96F0, 0x5F7C, 0x94DE,\n\t0x5F7F, 0x9C69, 0x5F80, 0x899D, 0x5F81, 0x90AA, 0x5F82, 0x9C68,\n\t0x5F83, 0x9C67, 0x5F84, 0x8C61, 0x5F85, 0x91D2, 0x5F87, 0x9C6D,\n\t0x5F88, 0x9C6B, 0x5F8A, 0x9C6A, 0x5F8B, 0x97A5, 0x5F8C, 0x8CE3,\n\t0x5F90, 0x8F99, 0x5F91, 0x9C6C, 0x5F92, 0x936B, 0x5F93, 0x8F5D,\n\t0x5F97, 0x93BE, 0x5F98, 0x9C70, 0x5F99, 0x9C6F, 0x5F9E, 0x9C6E,\n\t0x5FA0, 0x9C71, 0x5FA1, 0x8CE4, 0x5FA8, 0x9C72, 0x5FA9, 0x959C,\n\t0x5FAA, 0x8F7A, 0x5FAD, 0x9C73, 0x5FAE, 0x94F7, 0x5FB3, 0x93BF,\n\t0x5FB4, 0x92A5, 0x5FB7, 0xFABA, 0x5FB9, 0x934F, 0x5FBC, 0x9C74,\n\t0x5FBD, 0x8B4A, 0x5FC3, 0x9053, 0x5FC5, 0x954B, 0x5FCC, 0x8AF5,\n\t0x5FCD, 0x9445, 0x5FD6, 0x9C75, 0x5FD7, 0x8E75, 0x5FD8, 0x9659,\n\t0x5FD9, 0x965A, 0x5FDC, 0x899E, 0x5FDD, 0x9C7A, 0x5FDE, 0xFABB,\n\t0x5FE0, 0x9289, 0x5FE4, 0x9C77, 0x5FEB, 0x89F5, 0x5FF0, 0x9CAB,\n\t0x5FF1, 0x9C79, 0x5FF5, 0x944F, 0x5FF8, 0x9C78, 0x5FFB, 0x9C76,\n\t0x5FFD, 0x8D9A, 0x5FFF, 0x9C7C, 0x600E, 0x9C83, 0x600F, 0x9C89,\n\t0x6010, 0x9C81, 0x6012, 0x937B, 0x6015, 0x9C86, 0x6016, 0x957C,\n\t0x6019, 0x9C80, 0x601B, 0x9C85, 0x601C, 0x97E5, 0x601D, 0x8E76,\n\t0x6020, 0x91D3, 0x6021, 0x9C7D, 0x6025, 0x8B7D, 0x6026, 0x9C88,\n\t0x6027, 0x90AB, 0x6028, 0x8985, 0x6029, 0x9C82, 0x602A, 0x89F6,\n\t0x602B, 0x9C87, 0x602F, 0x8BAF, 0x6031, 0x9C84, 0x603A, 0x9C8A,\n\t0x6041, 0x9C8C, 0x6042, 0x9C96, 0x6043, 0x9C94, 0x6046, 0x9C91,\n\t0x604A, 0x9C90, 0x604B, 0x97F6, 0x604D, 0x9C92, 0x6050, 0x8BB0,\n\t0x6052, 0x8D50, 0x6055, 0x8F9A, 0x6059, 0x9C99, 0x605A, 0x9C8B,\n\t0x605D, 0xFABC, 0x605F, 0x9C8F, 0x6060, 0x9C7E, 0x6062, 0x89F8,\n\t0x6063, 0x9C93, 0x6064, 0x9C95, 0x6065, 0x9270, 0x6068, 0x8DA6,\n\t0x6069, 0x89B6, 0x606A, 0x9C8D, 0x606B, 0x9C98, 0x606C, 0x9C97,\n\t0x606D, 0x8BB1, 0x606F, 0x91A7, 0x6070, 0x8A86, 0x6075, 0x8C62,\n\t0x6077, 0x9C8E, 0x6081, 0x9C9A, 0x6083, 0x9C9D, 0x6084, 0x9C9F,\n\t0x6085, 0xFABD, 0x6089, 0x8EBB, 0x608A, 0xFABE, 0x608B, 0x9CA5,\n\t0x608C, 0x92EE, 0x608D, 0x9C9B, 0x6092, 0x9CA3, 0x6094, 0x89F7,\n\t0x6096, 0x9CA1, 0x6097, 0x9CA2, 0x609A, 0x9C9E, 0x609B, 0x9CA0,\n\t0x609F, 0x8CE5, 0x60A0, 0x9749, 0x60A3, 0x8AB3, 0x60A6, 0x8978,\n\t0x60A7, 0x9CA4, 0x60A9, 0x9459, 0x60AA, 0x88AB, 0x60B2, 0x94DF,\n\t0x60B3, 0x9C7B, 0x60B4, 0x9CAA, 0x60B5, 0x9CAE, 0x60B6, 0x96E3,\n\t0x60B8, 0x9CA7, 0x60BC, 0x9389, 0x60BD, 0x9CAC, 0x60C5, 0x8FEE,\n\t0x60C6, 0x9CAD, 0x60C7, 0x93D5, 0x60D1, 0x9866, 0x60D3, 0x9CA9,\n\t0x60D5, 0xFAC0, 0x60D8, 0x9CAF, 0x60DA, 0x8D9B, 0x60DC, 0x90C9,\n\t0x60DE, 0xFABF, 0x60DF, 0x88D2, 0x60E0, 0x9CA8, 0x60E1, 0x9CA6,\n\t0x60E3, 0x9179, 0x60E7, 0x9C9C, 0x60E8, 0x8E53, 0x60F0, 0x91C4,\n\t0x60F1, 0x9CBB, 0x60F2, 0xFAC2, 0x60F3, 0x917A, 0x60F4, 0x9CB6,\n\t0x60F6, 0x9CB3, 0x60F7, 0x9CB4, 0x60F9, 0x8EE4, 0x60FA, 0x9CB7,\n\t0x60FB, 0x9CBA, 0x6100, 0x9CB5, 0x6101, 0x8F44, 0x6103, 0x9CB8,\n\t0x6106, 0x9CB2, 0x6108, 0x96FA, 0x6109, 0x96F9, 0x610D, 0x9CBC,\n\t0x610E, 0x9CBD, 0x610F, 0x88D3, 0x6111, 0xFAC3, 0x6115, 0x9CB1,\n\t0x611A, 0x8BF0, 0x611B, 0x88A4, 0x611F, 0x8AB4, 0x6120, 0xFAC1,\n\t0x6121, 0x9CB9, 0x6127, 0x9CC1, 0x6128, 0x9CC0, 0x612C, 0x9CC5,\n\t0x6130, 0xFAC5, 0x6134, 0x9CC6, 0x6137, 0xFAC4, 0x613C, 0x9CC4,\n\t0x613D, 0x9CC7, 0x613E, 0x9CBF, 0x613F, 0x9CC3, 0x6142, 0x9CC8,\n\t0x6144, 0x9CC9, 0x6147, 0x9CBE, 0x6148, 0x8E9C, 0x614A, 0x9CC2,\n\t0x614B, 0x91D4, 0x614C, 0x8D51, 0x614D, 0x9CB0, 0x614E, 0x9054,\n\t0x6153, 0x9CD6, 0x6155, 0x95E7, 0x6158, 0x9CCC, 0x6159, 0x9CCD,\n\t0x615A, 0x9CCE, 0x615D, 0x9CD5, 0x615F, 0x9CD4, 0x6162, 0x969D,\n\t0x6163, 0x8AB5, 0x6165, 0x9CD2, 0x6167, 0x8C64, 0x6168, 0x8A53,\n\t0x616B, 0x9CCF, 0x616E, 0x97B6, 0x616F, 0x9CD1, 0x6170, 0x88D4,\n\t0x6171, 0x9CD3, 0x6173, 0x9CCA, 0x6174, 0x9CD0, 0x6175, 0x9CD7,\n\t0x6176, 0x8C63, 0x6177, 0x9CCB, 0x617E, 0x977C, 0x6182, 0x974A,\n\t0x6187, 0x9CDA, 0x618A, 0x9CDE, 0x618E, 0x919E, 0x6190, 0x97F7,\n\t0x6191, 0x9CDF, 0x6194, 0x9CDC, 0x6196, 0x9CD9, 0x6198, 0xFAC6,\n\t0x6199, 0x9CD8, 0x619A, 0x9CDD, 0x61A4, 0x95AE, 0x61A7, 0x93B2,\n\t0x61A9, 0x8C65, 0x61AB, 0x9CE0, 0x61AC, 0x9CDB, 0x61AE, 0x9CE1,\n\t0x61B2, 0x8C9B, 0x61B6, 0x89AF, 0x61BA, 0x9CE9, 0x61BE, 0x8AB6,\n\t0x61C3, 0x9CE7, 0x61C6, 0x9CE8, 0x61C7, 0x8DA7, 0x61C8, 0x9CE6,\n\t0x61C9, 0x9CE4, 0x61CA, 0x9CE3, 0x61CB, 0x9CEA, 0x61CC, 0x9CE2,\n\t0x61CD, 0x9CEC, 0x61D0, 0x89F9, 0x61E3, 0x9CEE, 0x61E6, 0x9CED,\n\t0x61F2, 0x92A6, 0x61F4, 0x9CF1, 0x61F6, 0x9CEF, 0x61F7, 0x9CE5,\n\t0x61F8, 0x8C9C, 0x61FA, 0x9CF0, 0x61FC, 0x9CF4, 0x61FD, 0x9CF3,\n\t0x61FE, 0x9CF5, 0x61FF, 0x9CF2, 0x6200, 0x9CF6, 0x6208, 0x9CF7,\n\t0x6209, 0x9CF8, 0x620A, 0x95E8, 0x620C, 0x9CFA, 0x620D, 0x9CF9,\n\t0x620E, 0x8F5E, 0x6210, 0x90AC, 0x6211, 0x89E4, 0x6212, 0x89FA,\n\t0x6213, 0xFAC7, 0x6214, 0x9CFB, 0x6216, 0x88BD, 0x621A, 0x90CA,\n\t0x621B, 0x9CFC, 0x621D, 0xE6C1, 0x621E, 0x9D40, 0x621F, 0x8C81,\n\t0x6221, 0x9D41, 0x6226, 0x90ED, 0x622A, 0x9D42, 0x622E, 0x9D43,\n\t0x622F, 0x8B59, 0x6230, 0x9D44, 0x6232, 0x9D45, 0x6233, 0x9D46,\n\t0x6234, 0x91D5, 0x6238, 0x8CCB, 0x623B, 0x96DF, 0x623F, 0x965B,\n\t0x6240, 0x8F8A, 0x6241, 0x9D47, 0x6247, 0x90EE, 0x6248, 0xE7BB,\n\t0x6249, 0x94E0, 0x624B, 0x8EE8, 0x624D, 0x8DCB, 0x624E, 0x9D48,\n\t0x6253, 0x91C5, 0x6255, 0x95A5, 0x6258, 0x91EF, 0x625B, 0x9D4B,\n\t0x625E, 0x9D49, 0x6260, 0x9D4C, 0x6263, 0x9D4A, 0x6268, 0x9D4D,\n\t0x626E, 0x95AF, 0x6271, 0x88B5, 0x6276, 0x957D, 0x6279, 0x94E1,\n\t0x627C, 0x9D4E, 0x627E, 0x9D51, 0x627F, 0x8FB3, 0x6280, 0x8B5A,\n\t0x6282, 0x9D4F, 0x6283, 0x9D56, 0x6284, 0x8FB4, 0x6289, 0x9D50,\n\t0x628A, 0x9463, 0x6291, 0x977D, 0x6292, 0x9D52, 0x6293, 0x9D53,\n\t0x6294, 0x9D57, 0x6295, 0x938A, 0x6296, 0x9D54, 0x6297, 0x8D52,\n\t0x6298, 0x90DC, 0x629B, 0x9D65, 0x629C, 0x94B2, 0x629E, 0x91F0,\n\t0x62A6, 0xFAC8, 0x62AB, 0x94E2, 0x62AC, 0x9DAB, 0x62B1, 0x95F8,\n\t0x62B5, 0x92EF, 0x62B9, 0x9695, 0x62BB, 0x9D5A, 0x62BC, 0x899F,\n\t0x62BD, 0x928A, 0x62C2, 0x9D63, 0x62C5, 0x9253, 0x62C6, 0x9D5D,\n\t0x62C7, 0x9D64, 0x62C8, 0x9D5F, 0x62C9, 0x9D66, 0x62CA, 0x9D62,\n\t0x62CC, 0x9D61, 0x62CD, 0x948F, 0x62CF, 0x9D5B, 0x62D0, 0x89FB,\n\t0x62D1, 0x9D59, 0x62D2, 0x8B91, 0x62D3, 0x91F1, 0x62D4, 0x9D55,\n\t0x62D7, 0x9D58, 0x62D8, 0x8D53, 0x62D9, 0x90D9, 0x62DB, 0x8FB5,\n\t0x62DC, 0x9D60, 0x62DD, 0x9471, 0x62E0, 0x8B92, 0x62E1, 0x8A67,\n\t0x62EC, 0x8A87, 0x62ED, 0x9040, 0x62EE, 0x9D68, 0x62EF, 0x9D6D,\n\t0x62F1, 0x9D69, 0x62F3, 0x8C9D, 0x62F5, 0x9D6E, 0x62F6, 0x8E41,\n\t0x62F7, 0x8D89, 0x62FE, 0x8F45, 0x62FF, 0x9D5C, 0x6301, 0x8E9D,\n\t0x6302, 0x9D6B, 0x6307, 0x8E77, 0x6308, 0x9D6C, 0x6309, 0x88C2,\n\t0x630C, 0x9D67, 0x6311, 0x92A7, 0x6319, 0x8B93, 0x631F, 0x8BB2,\n\t0x6327, 0x9D6A, 0x6328, 0x88A5, 0x632B, 0x8DC1, 0x632F, 0x9055,\n\t0x633A, 0x92F0, 0x633D, 0x94D2, 0x633E, 0x9D70, 0x633F, 0x917D,\n\t0x6349, 0x91A8, 0x634C, 0x8E4A, 0x634D, 0x9D71, 0x634F, 0x9D73,\n\t0x6350, 0x9D6F, 0x6355, 0x95DF, 0x6357, 0x92BB, 0x635C, 0x917B,\n\t0x6367, 0x95F9, 0x6368, 0x8ECC, 0x6369, 0x9D80, 0x636B, 0x9D7E,\n\t0x636E, 0x9098, 0x6372, 0x8C9E, 0x6376, 0x9D78, 0x6377, 0x8FB7,\n\t0x637A, 0x93E6, 0x637B, 0x9450, 0x6380, 0x9D76, 0x6383, 0x917C,\n\t0x6388, 0x8EF6, 0x6389, 0x9D7B, 0x638C, 0x8FB6, 0x638E, 0x9D75,\n\t0x638F, 0x9D7A, 0x6392, 0x9472, 0x6396, 0x9D74, 0x6398, 0x8C40,\n\t0x639B, 0x8A7C, 0x639F, 0x9D7C, 0x63A0, 0x97A9, 0x63A1, 0x8DCC,\n\t0x63A2, 0x9254, 0x63A3, 0x9D79, 0x63A5, 0x90DA, 0x63A7, 0x8D54,\n\t0x63A8, 0x9084, 0x63A9, 0x8986, 0x63AA, 0x915B, 0x63AB, 0x9D77,\n\t0x63AC, 0x8B64, 0x63B2, 0x8C66, 0x63B4, 0x92CD, 0x63B5, 0x9D7D,\n\t0x63BB, 0x917E, 0x63BE, 0x9D81, 0x63C0, 0x9D83, 0x63C3, 0x91B5,\n\t0x63C4, 0x9D89, 0x63C6, 0x9D84, 0x63C9, 0x9D86, 0x63CF, 0x9560,\n\t0x63D0, 0x92F1, 0x63D2, 0x9D87, 0x63D6, 0x974B, 0x63DA, 0x9767,\n\t0x63DB, 0x8AB7, 0x63E1, 0x88AC, 0x63E3, 0x9D85, 0x63E9, 0x9D82,\n\t0x63EE, 0x8AF6, 0x63F4, 0x8987, 0x63F5, 0xFAC9, 0x63F6, 0x9D88,\n\t0x63FA, 0x9768, 0x6406, 0x9D8C, 0x640D, 0x91B9, 0x640F, 0x9D93,\n\t0x6413, 0x9D8D, 0x6416, 0x9D8A, 0x6417, 0x9D91, 0x641C, 0x9D72,\n\t0x6426, 0x9D8E, 0x6428, 0x9D92, 0x642C, 0x94C0, 0x642D, 0x938B,\n\t0x6434, 0x9D8B, 0x6436, 0x9D8F, 0x643A, 0x8C67, 0x643E, 0x8DEF,\n\t0x6442, 0x90DB, 0x644E, 0x9D97, 0x6458, 0x9345, 0x6460, 0xFACA,\n\t0x6467, 0x9D94, 0x6469, 0x9680, 0x646F, 0x9D95, 0x6476, 0x9D96,\n\t0x6478, 0x96CC, 0x647A, 0x90A0, 0x6483, 0x8C82, 0x6488, 0x9D9D,\n\t0x6492, 0x8E54, 0x6493, 0x9D9A, 0x6495, 0x9D99, 0x649A, 0x9451,\n\t0x649D, 0xFACB, 0x649E, 0x93B3, 0x64A4, 0x9350, 0x64A5, 0x9D9B,\n\t0x64A9, 0x9D9C, 0x64AB, 0x958F, 0x64AD, 0x9464, 0x64AE, 0x8E42,\n\t0x64B0, 0x90EF, 0x64B2, 0x966F, 0x64B9, 0x8A68, 0x64BB, 0x9DA3,\n\t0x64BC, 0x9D9E, 0x64C1, 0x9769, 0x64C2, 0x9DA5, 0x64C5, 0x9DA1,\n\t0x64C7, 0x9DA2, 0x64CD, 0x9180, 0x64CE, 0xFACC, 0x64D2, 0x9DA0,\n\t0x64D4, 0x9D5E, 0x64D8, 0x9DA4, 0x64DA, 0x9D9F, 0x64E0, 0x9DA9,\n\t0x64E1, 0x9DAA, 0x64E2, 0x9346, 0x64E3, 0x9DAC, 0x64E6, 0x8E43,\n\t0x64E7, 0x9DA7, 0x64EC, 0x8B5B, 0x64EF, 0x9DAD, 0x64F1, 0x9DA6,\n\t0x64F2, 0x9DB1, 0x64F4, 0x9DB0, 0x64F6, 0x9DAF, 0x64FA, 0x9DB2,\n\t0x64FD, 0x9DB4, 0x64FE, 0x8FEF, 0x6500, 0x9DB3, 0x6505, 0x9DB7,\n\t0x6518, 0x9DB5, 0x651C, 0x9DB6, 0x651D, 0x9D90, 0x6523, 0x9DB9,\n\t0x6524, 0x9DB8, 0x652A, 0x9D98, 0x652B, 0x9DBA, 0x652C, 0x9DAE,\n\t0x652F, 0x8E78, 0x6534, 0x9DBB, 0x6535, 0x9DBC, 0x6536, 0x9DBE,\n\t0x6537, 0x9DBD, 0x6538, 0x9DBF, 0x6539, 0x89FC, 0x653B, 0x8D55,\n\t0x653E, 0x95FA, 0x653F, 0x90AD, 0x6545, 0x8CCC, 0x6548, 0x9DC1,\n\t0x654D, 0x9DC4, 0x654E, 0xFACD, 0x654F, 0x9571, 0x6551, 0x8B7E,\n\t0x6555, 0x9DC3, 0x6556, 0x9DC2, 0x6557, 0x9473, 0x6558, 0x9DC5,\n\t0x6559, 0x8BB3, 0x655D, 0x9DC7, 0x655E, 0x9DC6, 0x6562, 0x8AB8,\n\t0x6563, 0x8E55, 0x6566, 0x93D6, 0x656C, 0x8C68, 0x6570, 0x9094,\n\t0x6572, 0x9DC8, 0x6574, 0x90AE, 0x6575, 0x9347, 0x6577, 0x957E,\n\t0x6578, 0x9DC9, 0x6582, 0x9DCA, 0x6583, 0x9DCB, 0x6587, 0x95B6,\n\t0x6588, 0x9B7C, 0x6589, 0x90C4, 0x658C, 0x956B, 0x658E, 0x8DD6,\n\t0x6590, 0x94E3, 0x6591, 0x94C1, 0x6597, 0x936C, 0x6599, 0x97BF,\n\t0x659B, 0x9DCD, 0x659C, 0x8ECE, 0x659F, 0x9DCE, 0x65A1, 0x88B4,\n\t0x65A4, 0x8BD2, 0x65A5, 0x90CB, 0x65A7, 0x9580, 0x65AB, 0x9DCF,\n\t0x65AC, 0x8E61, 0x65AD, 0x9266, 0x65AF, 0x8E7A, 0x65B0, 0x9056,\n\t0x65B7, 0x9DD0, 0x65B9, 0x95FB, 0x65BC, 0x8997, 0x65BD, 0x8E7B,\n\t0x65C1, 0x9DD3, 0x65C3, 0x9DD1, 0x65C4, 0x9DD4, 0x65C5, 0x97B7,\n\t0x65C6, 0x9DD2, 0x65CB, 0x90F9, 0x65CC, 0x9DD5, 0x65CF, 0x91B0,\n\t0x65D2, 0x9DD6, 0x65D7, 0x8AF8, 0x65D9, 0x9DD8, 0x65DB, 0x9DD7,\n\t0x65E0, 0x9DD9, 0x65E1, 0x9DDA, 0x65E2, 0x8AF9, 0x65E5, 0x93FA,\n\t0x65E6, 0x9255, 0x65E7, 0x8B8C, 0x65E8, 0x8E7C, 0x65E9, 0x9181,\n\t0x65EC, 0x8F7B, 0x65ED, 0x88AE, 0x65F1, 0x9DDB, 0x65FA, 0x89A0,\n\t0x65FB, 0x9DDF, 0x6600, 0xFACE, 0x6602, 0x8D56, 0x6603, 0x9DDE,\n\t0x6606, 0x8DA9, 0x6607, 0x8FB8, 0x6609, 0xFAD1, 0x660A, 0x9DDD,\n\t0x660C, 0x8FB9, 0x660E, 0x96BE, 0x660F, 0x8DA8, 0x6613, 0x88D5,\n\t0x6614, 0x90CC, 0x6615, 0xFACF, 0x661C, 0x9DE4, 0x661E, 0xFAD3,\n\t0x661F, 0x90AF, 0x6620, 0x8966, 0x6624, 0xFAD4, 0x6625, 0x8F74,\n\t0x6627, 0x9686, 0x6628, 0x8DF0, 0x662D, 0x8FBA, 0x662E, 0xFAD2,\n\t0x662F, 0x90A5, 0x6631, 0xFA63, 0x6634, 0x9DE3, 0x6635, 0x9DE1,\n\t0x6636, 0x9DE2, 0x663B, 0xFAD0, 0x663C, 0x928B, 0x663F, 0x9E45,\n\t0x6641, 0x9DE8, 0x6642, 0x8E9E, 0x6643, 0x8D57, 0x6644, 0x9DE6,\n\t0x6649, 0x9DE7, 0x664B, 0x9057, 0x664F, 0x9DE5, 0x6652, 0x8E4E,\n\t0x6657, 0xFAD6, 0x6659, 0xFAD7, 0x665D, 0x9DEA, 0x665E, 0x9DE9,\n\t0x665F, 0x9DEE, 0x6662, 0x9DEF, 0x6664, 0x9DEB, 0x6665, 0xFAD5,\n\t0x6666, 0x8A41, 0x6667, 0x9DEC, 0x6668, 0x9DED, 0x6669, 0x94D3,\n\t0x666E, 0x9581, 0x666F, 0x8C69, 0x6670, 0x9DF0, 0x6673, 0xFAD9,\n\t0x6674, 0x90B0, 0x6676, 0x8FBB, 0x667A, 0x9271, 0x6681, 0x8BC5,\n\t0x6683, 0x9DF1, 0x6684, 0x9DF5, 0x6687, 0x89C9, 0x6688, 0x9DF2,\n\t0x6689, 0x9DF4, 0x668E, 0x9DF3, 0x6691, 0x8F8B, 0x6696, 0x9267,\n\t0x6697, 0x88C3, 0x6698, 0x9DF6, 0x6699, 0xFADA, 0x669D, 0x9DF7,\n\t0x66A0, 0xFADB, 0x66A2, 0x92A8, 0x66A6, 0x97EF, 0x66AB, 0x8E62,\n\t0x66AE, 0x95E9, 0x66B2, 0xFADC, 0x66B4, 0x965C, 0x66B8, 0x9E41,\n\t0x66B9, 0x9DF9, 0x66BC, 0x9DFC, 0x66BE, 0x9DFB, 0x66BF, 0xFADD,\n\t0x66C1, 0x9DF8, 0x66C4, 0x9E40, 0x66C7, 0x93DC, 0x66C9, 0x9DFA,\n\t0x66D6, 0x9E42, 0x66D9, 0x8F8C, 0x66DA, 0x9E43, 0x66DC, 0x976A,\n\t0x66DD, 0x9498, 0x66E0, 0x9E44, 0x66E6, 0x9E46, 0x66E9, 0x9E47,\n\t0x66F0, 0x9E48, 0x66F2, 0x8BC8, 0x66F3, 0x8967, 0x66F4, 0x8D58,\n\t0x66F5, 0x9E49, 0x66F7, 0x9E4A, 0x66F8, 0x8F91, 0x66F9, 0x9182,\n\t0x66FA, 0xFADE, 0x66FB, 0xFA66, 0x66FC, 0x99D6, 0x66FD, 0x915D,\n\t0x66FE, 0x915C, 0x66FF, 0x91D6, 0x6700, 0x8DC5, 0x6703, 0x98F0,\n\t0x6708, 0x8C8E, 0x6709, 0x974C, 0x670B, 0x95FC, 0x670D, 0x959E,\n\t0x670E, 0xFADF, 0x670F, 0x9E4B, 0x6714, 0x8DF1, 0x6715, 0x92BD,\n\t0x6716, 0x9E4C, 0x6717, 0x984E, 0x671B, 0x965D, 0x671D, 0x92A9,\n\t0x671E, 0x9E4D, 0x671F, 0x8AFA, 0x6726, 0x9E4E, 0x6727, 0x9E4F,\n\t0x6728, 0x96D8, 0x672A, 0x96A2, 0x672B, 0x9696, 0x672C, 0x967B,\n\t0x672D, 0x8E44, 0x672E, 0x9E51, 0x6731, 0x8EE9, 0x6734, 0x9670,\n\t0x6736, 0x9E53, 0x6737, 0x9E56, 0x6738, 0x9E55, 0x673A, 0x8AF7,\n\t0x673D, 0x8B80, 0x673F, 0x9E52, 0x6741, 0x9E54, 0x6746, 0x9E57,\n\t0x6749, 0x9099, 0x674E, 0x979B, 0x674F, 0x88C7, 0x6750, 0x8DDE,\n\t0x6751, 0x91BA, 0x6753, 0x8EDB, 0x6756, 0x8FF1, 0x6759, 0x9E5A,\n\t0x675C, 0x936D, 0x675E, 0x9E58, 0x675F, 0x91A9, 0x6760, 0x9E59,\n\t0x6761, 0x8FF0, 0x6762, 0x96DB, 0x6763, 0x9E5B, 0x6764, 0x9E5C,\n\t0x6765, 0x9788, 0x6766, 0xFAE1, 0x676A, 0x9E61, 0x676D, 0x8D59,\n\t0x676F, 0x9474, 0x6770, 0x9E5E, 0x6771, 0x938C, 0x6772, 0x9DDC,\n\t0x6773, 0x9DE0, 0x6775, 0x8B6E, 0x6777, 0x9466, 0x677C, 0x9E60,\n\t0x677E, 0x8FBC, 0x677F, 0x94C2, 0x6785, 0x9E66, 0x6787, 0x94F8,\n\t0x6789, 0x9E5D, 0x678B, 0x9E63, 0x678C, 0x9E62, 0x6790, 0x90CD,\n\t0x6795, 0x968D, 0x6797, 0x97D1, 0x679A, 0x9687, 0x679C, 0x89CA,\n\t0x679D, 0x8E7D, 0x67A0, 0x9867, 0x67A1, 0x9E65, 0x67A2, 0x9095,\n\t0x67A6, 0x9E64, 0x67A9, 0x9E5F, 0x67AF, 0x8CCD, 0x67B3, 0x9E6B,\n\t0x67B4, 0x9E69, 0x67B6, 0x89CB, 0x67B7, 0x9E67, 0x67B8, 0x9E6D,\n\t0x67B9, 0x9E73, 0x67BB, 0xFAE2, 0x67C0, 0xFAE4, 0x67C1, 0x91C6,\n\t0x67C4, 0x95BF, 0x67C6, 0x9E75, 0x67CA, 0x9541, 0x67CE, 0x9E74,\n\t0x67CF, 0x9490, 0x67D0, 0x965E, 0x67D1, 0x8AB9, 0x67D3, 0x90F5,\n\t0x67D4, 0x8F5F, 0x67D8, 0x92D1, 0x67DA, 0x974D, 0x67DD, 0x9E70,\n\t0x67DE, 0x9E6F, 0x67E2, 0x9E71, 0x67E4, 0x9E6E, 0x67E7, 0x9E76,\n\t0x67E9, 0x9E6C, 0x67EC, 0x9E6A, 0x67EE, 0x9E72, 0x67EF, 0x9E68,\n\t0x67F1, 0x928C, 0x67F3, 0x96F6, 0x67F4, 0x8EC4, 0x67F5, 0x8DF2,\n\t0x67FB, 0x8DB8, 0x67FE, 0x968F, 0x67FF, 0x8A60, 0x6801, 0xFAE5,\n\t0x6802, 0x92CC, 0x6803, 0x93C8, 0x6804, 0x8968, 0x6813, 0x90F0,\n\t0x6816, 0x90B2, 0x6817, 0x8C49, 0x681E, 0x9E78, 0x6821, 0x8D5A,\n\t0x6822, 0x8A9C, 0x6829, 0x9E7A, 0x682A, 0x8A94, 0x682B, 0x9E81,\n\t0x6832, 0x9E7D, 0x6834, 0x90F1, 0x6838, 0x8A6A, 0x6839, 0x8DAA,\n\t0x683C, 0x8A69, 0x683D, 0x8DCD, 0x6840, 0x9E7B, 0x6841, 0x8C85,\n\t0x6842, 0x8C6A, 0x6843, 0x938D, 0x6844, 0xFAE6, 0x6846, 0x9E79,\n\t0x6848, 0x88C4, 0x684D, 0x9E7C, 0x684E, 0x9E7E, 0x6850, 0x8BCB,\n\t0x6851, 0x8C4B, 0x6852, 0xFAE3, 0x6853, 0x8ABA, 0x6854, 0x8B6A,\n\t0x6859, 0x9E82, 0x685C, 0x8DF7, 0x685D, 0x9691, 0x685F, 0x8E56,\n\t0x6863, 0x9E83, 0x6867, 0x954F, 0x6874, 0x9E8F, 0x6876, 0x89B1,\n\t0x6877, 0x9E84, 0x687E, 0x9E95, 0x687F, 0x9E85, 0x6881, 0x97C0,\n\t0x6883, 0x9E8C, 0x6885, 0x947E, 0x688D, 0x9E94, 0x688F, 0x9E87,\n\t0x6893, 0x88B2, 0x6894, 0x9E89, 0x6897, 0x8D5B, 0x689B, 0x9E8B,\n\t0x689D, 0x9E8A, 0x689F, 0x9E86, 0x68A0, 0x9E91, 0x68A2, 0x8FBD,\n\t0x68A6, 0x9AEB, 0x68A7, 0x8CE6, 0x68A8, 0x979C, 0x68AD, 0x9E88,\n\t0x68AF, 0x92F2, 0x68B0, 0x8A42, 0x68B1, 0x8DAB, 0x68B3, 0x9E80,\n\t0x68B5, 0x9E90, 0x68B6, 0x8A81, 0x68B9, 0x9E8E, 0x68BA, 0x9E92,\n\t0x68BC, 0x938E, 0x68C4, 0x8AFC, 0x68C6, 0x9EB0, 0x68C8, 0xFA64,\n\t0x68C9, 0x96C7, 0x68CA, 0x9E97, 0x68CB, 0x8AFB, 0x68CD, 0x9E9E,\n\t0x68CF, 0xFAE7, 0x68D2, 0x965F, 0x68D4, 0x9E9F, 0x68D5, 0x9EA1,\n\t0x68D7, 0x9EA5, 0x68D8, 0x9E99, 0x68DA, 0x9249, 0x68DF, 0x938F,\n\t0x68E0, 0x9EA9, 0x68E1, 0x9E9C, 0x68E3, 0x9EA6, 0x68E7, 0x9EA0,\n\t0x68EE, 0x9058, 0x68EF, 0x9EAA, 0x68F2, 0x90B1, 0x68F9, 0x9EA8,\n\t0x68FA, 0x8ABB, 0x6900, 0x986F, 0x6901, 0x9E96, 0x6904, 0x9EA4,\n\t0x6905, 0x88D6, 0x6908, 0x9E98, 0x690B, 0x96B8, 0x690C, 0x9E9D,\n\t0x690D, 0x9041, 0x690E, 0x92C5, 0x690F, 0x9E93, 0x6912, 0x9EA3,\n\t0x6919, 0x909A, 0x691A, 0x9EAD, 0x691B, 0x8A91, 0x691C, 0x8C9F,\n\t0x6921, 0x9EAF, 0x6922, 0x9E9A, 0x6923, 0x9EAE, 0x6925, 0x9EA7,\n\t0x6926, 0x9E9B, 0x6928, 0x9EAB, 0x692A, 0x9EAC, 0x6930, 0x9EBD,\n\t0x6934, 0x93CC, 0x6936, 0x9EA2, 0x6939, 0x9EB9, 0x693D, 0x9EBB,\n\t0x693F, 0x92D6, 0x694A, 0x976B, 0x6953, 0x9596, 0x6954, 0x9EB6,\n\t0x6955, 0x91C8, 0x6959, 0x9EBC, 0x695A, 0x915E, 0x695C, 0x9EB3,\n\t0x695D, 0x9EC0, 0x695E, 0x9EBF, 0x6960, 0x93ED, 0x6961, 0x9EBE,\n\t0x6962, 0x93E8, 0x6968, 0xFAE9, 0x696A, 0x9EC2, 0x696B, 0x9EB5,\n\t0x696D, 0x8BC6, 0x696E, 0x9EB8, 0x696F, 0x8F7C, 0x6973, 0x9480,\n\t0x6974, 0x9EBA, 0x6975, 0x8BC9, 0x6977, 0x9EB2, 0x6978, 0x9EB4,\n\t0x6979, 0x9EB1, 0x697C, 0x984F, 0x697D, 0x8A79, 0x697E, 0x9EB7,\n\t0x6981, 0x9EC1, 0x6982, 0x8A54, 0x698A, 0x8DE5, 0x698E, 0x897C,\n\t0x6991, 0x9ED2, 0x6994, 0x9850, 0x6995, 0x9ED5, 0x6998, 0xFAEB,\n\t0x699B, 0x9059, 0x699C, 0x9ED4, 0x69A0, 0x9ED3, 0x69A7, 0x9ED0,\n\t0x69AE, 0x9EC4, 0x69B1, 0x9EE1, 0x69B2, 0x9EC3, 0x69B4, 0x9ED6,\n\t0x69BB, 0x9ECE, 0x69BE, 0x9EC9, 0x69BF, 0x9EC6, 0x69C1, 0x9EC7,\n\t0x69C3, 0x9ECF, 0x69C7, 0xEAA0, 0x69CA, 0x9ECC, 0x69CB, 0x8D5C,\n\t0x69CC, 0x92C6, 0x69CD, 0x9184, 0x69CE, 0x9ECA, 0x69D0, 0x9EC5,\n\t0x69D3, 0x9EC8, 0x69D8, 0x976C, 0x69D9, 0x968A, 0x69DD, 0x9ECD,\n\t0x69DE, 0x9ED7, 0x69E2, 0xFAEC, 0x69E7, 0x9EDF, 0x69E8, 0x9ED8,\n\t0x69EB, 0x9EE5, 0x69ED, 0x9EE3, 0x69F2, 0x9EDE, 0x69F9, 0x9EDD,\n\t0x69FB, 0x92CE, 0x69FD, 0x9185, 0x69FF, 0x9EDB, 0x6A02, 0x9ED9,\n\t0x6A05, 0x9EE0, 0x6A0A, 0x9EE6, 0x6A0B, 0x94F3, 0x6A0C, 0x9EEC,\n\t0x6A12, 0x9EE7, 0x6A13, 0x9EEA, 0x6A14, 0x9EE4, 0x6A17, 0x9294,\n\t0x6A19, 0x9557, 0x6A1B, 0x9EDA, 0x6A1E, 0x9EE2, 0x6A1F, 0x8FBE,\n\t0x6A21, 0x96CD, 0x6A22, 0x9EF6, 0x6A23, 0x9EE9, 0x6A29, 0x8CA0,\n\t0x6A2A, 0x89A1, 0x6A2B, 0x8A7E, 0x6A2E, 0x9ED1, 0x6A30, 0xFAED,\n\t0x6A35, 0x8FBF, 0x6A36, 0x9EEE, 0x6A38, 0x9EF5, 0x6A39, 0x8EF7,\n\t0x6A3A, 0x8A92, 0x6A3D, 0x924D, 0x6A44, 0x9EEB, 0x6A46, 0xFAEF,\n\t0x6A47, 0x9EF0, 0x6A48, 0x9EF4, 0x6A4B, 0x8BB4, 0x6A58, 0x8B6B,\n\t0x6A59, 0x9EF2, 0x6A5F, 0x8B40, 0x6A61, 0x93C9, 0x6A62, 0x9EF1,\n\t0x6A66, 0x9EF3, 0x6A6B, 0xFAEE, 0x6A72, 0x9EED, 0x6A73, 0xFAF0,\n\t0x6A78, 0x9EEF, 0x6A7E, 0xFAF1, 0x6A7F, 0x8A80, 0x6A80, 0x9268,\n\t0x6A84, 0x9EFA, 0x6A8D, 0x9EF8, 0x6A8E, 0x8CE7, 0x6A90, 0x9EF7,\n\t0x6A97, 0x9F40, 0x6A9C, 0x9E77, 0x6AA0, 0x9EF9, 0x6AA2, 0x9EFB,\n\t0x6AA3, 0x9EFC, 0x6AAA, 0x9F4B, 0x6AAC, 0x9F47, 0x6AAE, 0x9E8D,\n\t0x6AB3, 0x9F46, 0x6AB8, 0x9F45, 0x6ABB, 0x9F42, 0x6AC1, 0x9EE8,\n\t0x6AC2, 0x9F44, 0x6AC3, 0x9F43, 0x6AD1, 0x9F49, 0x6AD3, 0x9845,\n\t0x6ADA, 0x9F4C, 0x6ADB, 0x8BF9, 0x6ADE, 0x9F48, 0x6ADF, 0x9F4A,\n\t0x6AE2, 0xFAF2, 0x6AE4, 0xFAF3, 0x6AE8, 0x94A5, 0x6AEA, 0x9F4D,\n\t0x6AFA, 0x9F51, 0x6AFB, 0x9F4E, 0x6B04, 0x9793, 0x6B05, 0x9F4F,\n\t0x6B0A, 0x9EDC, 0x6B12, 0x9F52, 0x6B16, 0x9F53, 0x6B1D, 0x8954,\n\t0x6B1F, 0x9F55, 0x6B20, 0x8C87, 0x6B21, 0x8E9F, 0x6B23, 0x8BD3,\n\t0x6B27, 0x89A2, 0x6B32, 0x977E, 0x6B37, 0x9F57, 0x6B38, 0x9F56,\n\t0x6B39, 0x9F59, 0x6B3A, 0x8B5C, 0x6B3D, 0x8BD4, 0x6B3E, 0x8ABC,\n\t0x6B43, 0x9F5C, 0x6B47, 0x9F5B, 0x6B49, 0x9F5D, 0x6B4C, 0x89CC,\n\t0x6B4E, 0x9256, 0x6B50, 0x9F5E, 0x6B53, 0x8ABD, 0x6B54, 0x9F60,\n\t0x6B59, 0x9F5F, 0x6B5B, 0x9F61, 0x6B5F, 0x9F62, 0x6B61, 0x9F63,\n\t0x6B62, 0x8E7E, 0x6B63, 0x90B3, 0x6B64, 0x8D9F, 0x6B66, 0x9590,\n\t0x6B69, 0x95E0, 0x6B6A, 0x9863, 0x6B6F, 0x8E95, 0x6B73, 0x8DCE,\n\t0x6B74, 0x97F0, 0x6B78, 0x9F64, 0x6B79, 0x9F65, 0x6B7B, 0x8E80,\n\t0x6B7F, 0x9F66, 0x6B80, 0x9F67, 0x6B83, 0x9F69, 0x6B84, 0x9F68,\n\t0x6B86, 0x9677, 0x6B89, 0x8F7D, 0x6B8A, 0x8EEA, 0x6B8B, 0x8E63,\n\t0x6B8D, 0x9F6A, 0x6B95, 0x9F6C, 0x6B96, 0x9042, 0x6B98, 0x9F6B,\n\t0x6B9E, 0x9F6D, 0x6BA4, 0x9F6E, 0x6BAA, 0x9F6F, 0x6BAB, 0x9F70,\n\t0x6BAF, 0x9F71, 0x6BB1, 0x9F73, 0x6BB2, 0x9F72, 0x6BB3, 0x9F74,\n\t0x6BB4, 0x89A3, 0x6BB5, 0x9269, 0x6BB7, 0x9F75, 0x6BBA, 0x8E45,\n\t0x6BBB, 0x8A6B, 0x6BBC, 0x9F76, 0x6BBF, 0x9361, 0x6BC0, 0x9ACA,\n\t0x6BC5, 0x8B42, 0x6BC6, 0x9F77, 0x6BCB, 0x9F78, 0x6BCD, 0x95EA,\n\t0x6BCE, 0x9688, 0x6BD2, 0x93C5, 0x6BD3, 0x9F79, 0x6BD4, 0x94E4,\n\t0x6BD6, 0xFAF4, 0x6BD8, 0x94F9, 0x6BDB, 0x96D1, 0x6BDF, 0x9F7A,\n\t0x6BEB, 0x9F7C, 0x6BEC, 0x9F7B, 0x6BEF, 0x9F7E, 0x6BF3, 0x9F7D,\n\t0x6C08, 0x9F81, 0x6C0F, 0x8E81, 0x6C11, 0x96AF, 0x6C13, 0x9F82,\n\t0x6C14, 0x9F83, 0x6C17, 0x8B43, 0x6C1B, 0x9F84, 0x6C23, 0x9F86,\n\t0x6C24, 0x9F85, 0x6C34, 0x9085, 0x6C37, 0x9558, 0x6C38, 0x8969,\n\t0x6C3E, 0x94C3, 0x6C3F, 0xFAF5, 0x6C40, 0x92F3, 0x6C41, 0x8F60,\n\t0x6C42, 0x8B81, 0x6C4E, 0x94C4, 0x6C50, 0x8EAC, 0x6C55, 0x9F88,\n\t0x6C57, 0x8ABE, 0x6C5A, 0x8998, 0x6C5C, 0xFAF6, 0x6C5D, 0x93F0,\n\t0x6C5E, 0x9F87, 0x6C5F, 0x8D5D, 0x6C60, 0x9272, 0x6C62, 0x9F89,\n\t0x6C68, 0x9F91, 0x6C6A, 0x9F8A, 0x6C6F, 0xFAF8, 0x6C70, 0x91BF,\n\t0x6C72, 0x8B82, 0x6C73, 0x9F92, 0x6C7A, 0x8C88, 0x6C7D, 0x8B44,\n\t0x6C7E, 0x9F90, 0x6C81, 0x9F8E, 0x6C82, 0x9F8B, 0x6C83, 0x9780,\n\t0x6C86, 0xFAF7, 0x6C88, 0x92BE, 0x6C8C, 0x93D7, 0x6C8D, 0x9F8C,\n\t0x6C90, 0x9F94, 0x6C92, 0x9F93, 0x6C93, 0x8C42, 0x6C96, 0x89AB,\n\t0x6C99, 0x8DB9, 0x6C9A, 0x9F8D, 0x6C9B, 0x9F8F, 0x6CA1, 0x9676,\n\t0x6CA2, 0x91F2, 0x6CAB, 0x9697, 0x6CAE, 0x9F9C, 0x6CB1, 0x9F9D,\n\t0x6CB3, 0x89CD, 0x6CB8, 0x95A6, 0x6CB9, 0x96FB, 0x6CBA, 0x9F9F,\n\t0x6CBB, 0x8EA1, 0x6CBC, 0x8FC0, 0x6CBD, 0x9F98, 0x6CBE, 0x9F9E,\n\t0x6CBF, 0x8988, 0x6CC1, 0x8BB5, 0x6CC4, 0x9F95, 0x6CC5, 0x9F9A,\n\t0x6CC9, 0x90F2, 0x6CCA, 0x9491, 0x6CCC, 0x94E5, 0x6CD3, 0x9F97,\n\t0x6CD5, 0x9640, 0x6CD7, 0x9F99, 0x6CD9, 0x9FA2, 0x6CDA, 0xFAF9,\n\t0x6CDB, 0x9FA0, 0x6CDD, 0x9F9B, 0x6CE1, 0x9641, 0x6CE2, 0x9467,\n\t0x6CE3, 0x8B83, 0x6CE5, 0x9344, 0x6CE8, 0x928D, 0x6CEA, 0x9FA3,\n\t0x6CEF, 0x9FA1, 0x6CF0, 0x91D7, 0x6CF1, 0x9F96, 0x6CF3, 0x896A,\n\t0x6D04, 0xFAFA, 0x6D0B, 0x976D, 0x6D0C, 0x9FAE, 0x6D12, 0x9FAD,\n\t0x6D17, 0x90F4, 0x6D19, 0x9FAA, 0x6D1B, 0x978C, 0x6D1E, 0x93B4,\n\t0x6D1F, 0x9FA4, 0x6D25, 0x92C3, 0x6D29, 0x896B, 0x6D2A, 0x8D5E,\n\t0x6D2B, 0x9FA7, 0x6D32, 0x8F46, 0x6D33, 0x9FAC, 0x6D35, 0x9FAB,\n\t0x6D36, 0x9FA6, 0x6D38, 0x9FA9, 0x6D3B, 0x8A88, 0x6D3D, 0x9FA8,\n\t0x6D3E, 0x9468, 0x6D41, 0x97AC, 0x6D44, 0x8FF2, 0x6D45, 0x90F3,\n\t0x6D59, 0x9FB4, 0x6D5A, 0x9FB2, 0x6D5C, 0x956C, 0x6D63, 0x9FAF,\n\t0x6D64, 0x9FB1, 0x6D66, 0x8959, 0x6D69, 0x8D5F, 0x6D6A, 0x9851,\n\t0x6D6C, 0x8A5C, 0x6D6E, 0x9582, 0x6D6F, 0xFAFC, 0x6D74, 0x9781,\n\t0x6D77, 0x8A43, 0x6D78, 0x905A, 0x6D79, 0x9FB3, 0x6D85, 0x9FB8,\n\t0x6D87, 0xFAFB, 0x6D88, 0x8FC1, 0x6D8C, 0x974F, 0x6D8E, 0x9FB5,\n\t0x6D93, 0x9FB0, 0x6D95, 0x9FB6, 0x6D96, 0xFB40, 0x6D99, 0x97DC,\n\t0x6D9B, 0x9393, 0x6D9C, 0x93C0, 0x6DAC, 0xFB41, 0x6DAF, 0x8A55,\n\t0x6DB2, 0x8974, 0x6DB5, 0x9FBC, 0x6DB8, 0x9FBF, 0x6DBC, 0x97C1,\n\t0x6DC0, 0x9784, 0x6DC5, 0x9FC6, 0x6DC6, 0x9FC0, 0x6DC7, 0x9FBD,\n\t0x6DCB, 0x97D2, 0x6DCC, 0x9FC3, 0x6DCF, 0xFB42, 0x6DD1, 0x8F69,\n\t0x6DD2, 0x9FC5, 0x6DD5, 0x9FCA, 0x6DD8, 0x9391, 0x6DD9, 0x9FC8,\n\t0x6DDE, 0x9FC2, 0x6DE1, 0x9257, 0x6DE4, 0x9FC9, 0x6DE6, 0x9FBE,\n\t0x6DE8, 0x9FC4, 0x6DEA, 0x9FCB, 0x6DEB, 0x88FA, 0x6DEC, 0x9FC1,\n\t0x6DEE, 0x9FCC, 0x6DF1, 0x905B, 0x6DF2, 0xFB44, 0x6DF3, 0x8F7E,\n\t0x6DF5, 0x95A3, 0x6DF7, 0x8DAC, 0x6DF8, 0xFB43, 0x6DF9, 0x9FB9,\n\t0x6DFA, 0x9FC7, 0x6DFB, 0x9359, 0x6DFC, 0xFB45, 0x6E05, 0x90B4,\n\t0x6E07, 0x8A89, 0x6E08, 0x8DCF, 0x6E09, 0x8FC2, 0x6E0A, 0x9FBB,\n\t0x6E0B, 0x8F61, 0x6E13, 0x8C6B, 0x6E15, 0x9FBA, 0x6E19, 0x9FD0,\n\t0x6E1A, 0x8F8D, 0x6E1B, 0x8CB8, 0x6E1D, 0x9FDF, 0x6E1F, 0x9FD9,\n\t0x6E20, 0x8B94, 0x6E21, 0x936E, 0x6E23, 0x9FD4, 0x6E24, 0x9FDD,\n\t0x6E25, 0x88AD, 0x6E26, 0x8951, 0x6E27, 0xFB48, 0x6E29, 0x89B7,\n\t0x6E2B, 0x9FD6, 0x6E2C, 0x91AA, 0x6E2D, 0x9FCD, 0x6E2E, 0x9FCF,\n\t0x6E2F, 0x8D60, 0x6E38, 0x9FE0, 0x6E39, 0xFB46, 0x6E3A, 0x9FDB,\n\t0x6E3C, 0xFB49, 0x6E3E, 0x9FD3, 0x6E43, 0x9FDA, 0x6E4A, 0x96A9,\n\t0x6E4D, 0x9FD8, 0x6E4E, 0x9FDC, 0x6E56, 0x8CCE, 0x6E58, 0x8FC3,\n\t0x6E5B, 0x9258, 0x6E5C, 0xFB47, 0x6E5F, 0x9FD2, 0x6E67, 0x974E,\n\t0x6E6B, 0x9FD5, 0x6E6E, 0x9FCE, 0x6E6F, 0x9392, 0x6E72, 0x9FD1,\n\t0x6E76, 0x9FD7, 0x6E7E, 0x9870, 0x6E7F, 0x8EBC, 0x6E80, 0x969E,\n\t0x6E82, 0x9FE1, 0x6E8C, 0x94AC, 0x6E8F, 0x9FED, 0x6E90, 0x8CB9,\n\t0x6E96, 0x8F80, 0x6E98, 0x9FE3, 0x6E9C, 0x97AD, 0x6E9D, 0x8D61,\n\t0x6E9F, 0x9FF0, 0x6EA2, 0x88EC, 0x6EA5, 0x9FEE, 0x6EAA, 0x9FE2,\n\t0x6EAF, 0x9FE8, 0x6EB2, 0x9FEA, 0x6EB6, 0x976E, 0x6EB7, 0x9FE5,\n\t0x6EBA, 0x934D, 0x6EBD, 0x9FE7, 0x6EBF, 0xFB4A, 0x6EC2, 0x9FEF,\n\t0x6EC4, 0x9FE9, 0x6EC5, 0x96C5, 0x6EC9, 0x9FE4, 0x6ECB, 0x8EA0,\n\t0x6ECC, 0x9FFC, 0x6ED1, 0x8A8A, 0x6ED3, 0x9FE6, 0x6ED4, 0x9FEB,\n\t0x6ED5, 0x9FEC, 0x6EDD, 0x91EA, 0x6EDE, 0x91D8, 0x6EEC, 0x9FF4,\n\t0x6EEF, 0x9FFA, 0x6EF2, 0x9FF8, 0x6EF4, 0x9348, 0x6EF7, 0xE042,\n\t0x6EF8, 0x9FF5, 0x6EFE, 0x9FF6, 0x6EFF, 0x9FDE, 0x6F01, 0x8B99,\n\t0x6F02, 0x9559, 0x6F06, 0x8EBD, 0x6F09, 0x8D97, 0x6F0F, 0x9852,\n\t0x6F11, 0x9FF2, 0x6F13, 0xE041, 0x6F14, 0x8989, 0x6F15, 0x9186,\n\t0x6F20, 0x9499, 0x6F22, 0x8ABF, 0x6F23, 0x97F8, 0x6F2B, 0x969F,\n\t0x6F2C, 0x92D0, 0x6F31, 0x9FF9, 0x6F32, 0x9FFB, 0x6F38, 0x9151,\n\t0x6F3E, 0xE040, 0x6F3F, 0x9FF7, 0x6F41, 0x9FF1, 0x6F45, 0x8AC1,\n\t0x6F54, 0x8C89, 0x6F58, 0xE04E, 0x6F5B, 0xE049, 0x6F5C, 0x90F6,\n\t0x6F5F, 0x8A83, 0x6F64, 0x8F81, 0x6F66, 0xE052, 0x6F6D, 0xE04B,\n\t0x6F6E, 0x92AA, 0x6F6F, 0xE048, 0x6F70, 0x92D7, 0x6F74, 0xE06B,\n\t0x6F78, 0xE045, 0x6F7A, 0xE044, 0x6F7C, 0xE04D, 0x6F80, 0xE047,\n\t0x6F81, 0xE046, 0x6F82, 0xE04C, 0x6F84, 0x909F, 0x6F86, 0xE043,\n\t0x6F88, 0xFB4B, 0x6F8E, 0xE04F, 0x6F91, 0xE050, 0x6F97, 0x8AC0,\n\t0x6FA1, 0xE055, 0x6FA3, 0xE054, 0x6FA4, 0xE056, 0x6FAA, 0xE059,\n\t0x6FB1, 0x9362, 0x6FB3, 0xE053, 0x6FB5, 0xFB4C, 0x6FB9, 0xE057,\n\t0x6FC0, 0x8C83, 0x6FC1, 0x91F7, 0x6FC2, 0xE051, 0x6FC3, 0x945A,\n\t0x6FC6, 0xE058, 0x6FD4, 0xE05D, 0x6FD5, 0xE05B, 0x6FD8, 0xE05E,\n\t0x6FDB, 0xE061, 0x6FDF, 0xE05A, 0x6FE0, 0x8D8A, 0x6FE1, 0x9447,\n\t0x6FE4, 0x9FB7, 0x6FEB, 0x9794, 0x6FEC, 0xE05C, 0x6FEE, 0xE060,\n\t0x6FEF, 0x91F3, 0x6FF1, 0xE05F, 0x6FF3, 0xE04A, 0x6FF5, 0xFB4D,\n\t0x6FF6, 0xE889, 0x6FFA, 0xE064, 0x6FFE, 0xE068, 0x7001, 0xE066,\n\t0x7005, 0xFB4E, 0x7007, 0xFB4F, 0x7009, 0xE062, 0x700B, 0xE063,\n\t0x700F, 0xE067, 0x7011, 0xE065, 0x7015, 0x956D, 0x7018, 0xE06D,\n\t0x701A, 0xE06A, 0x701B, 0xE069, 0x701D, 0xE06C, 0x701E, 0x93D2,\n\t0x701F, 0xE06E, 0x7026, 0x9295, 0x7027, 0x91EB, 0x7028, 0xFB50,\n\t0x702C, 0x90A3, 0x7030, 0xE06F, 0x7032, 0xE071, 0x703E, 0xE070,\n\t0x704C, 0x9FF3, 0x7051, 0xE072, 0x7058, 0x93E5, 0x7063, 0xE073,\n\t0x706B, 0x89CE, 0x706F, 0x9394, 0x7070, 0x8A44, 0x7078, 0x8B84,\n\t0x707C, 0x8EDC, 0x707D, 0x8DD0, 0x7085, 0xFB51, 0x7089, 0x9846,\n\t0x708A, 0x9086, 0x708E, 0x898A, 0x7092, 0xE075, 0x7099, 0xE074,\n\t0x70AB, 0xFB52, 0x70AC, 0xE078, 0x70AD, 0x9259, 0x70AE, 0xE07B,\n\t0x70AF, 0xE076, 0x70B3, 0xE07A, 0x70B8, 0xE079, 0x70B9, 0x935F,\n\t0x70BA, 0x88D7, 0x70BB, 0xFA62, 0x70C8, 0x97F3, 0x70CB, 0xE07D,\n\t0x70CF, 0x8947, 0x70D9, 0xE080, 0x70DD, 0xE07E, 0x70DF, 0xE07C,\n\t0x70F1, 0xE077, 0x70F9, 0x9642, 0x70FD, 0xE082, 0x7104, 0xFB54,\n\t0x7109, 0xE081, 0x710F, 0xFB53, 0x7114, 0x898B, 0x7119, 0xE084,\n\t0x711A, 0x95B0, 0x711C, 0xE083, 0x7121, 0x96B3, 0x7126, 0x8FC5,\n\t0x7136, 0x9152, 0x713C, 0x8FC4, 0x7146, 0xFB56, 0x7147, 0xFB57,\n\t0x7149, 0x97F9, 0x714C, 0xE08A, 0x714E, 0x90F7, 0x7155, 0xE086,\n\t0x7156, 0xE08B, 0x7159, 0x898C, 0x715C, 0xFB55, 0x7162, 0xE089,\n\t0x7164, 0x9481, 0x7165, 0xE085, 0x7166, 0xE088, 0x7167, 0x8FC6,\n\t0x7169, 0x94CF, 0x716C, 0xE08C, 0x716E, 0x8ECF, 0x717D, 0x90F8,\n\t0x7184, 0xE08F, 0x7188, 0xE087, 0x718A, 0x8C46, 0x718F, 0xE08D,\n\t0x7194, 0x976F, 0x7195, 0xE090, 0x7199, 0xEAA4, 0x719F, 0x8F6E,\n\t0x71A8, 0xE091, 0x71AC, 0xE092, 0x71B1, 0x944D, 0x71B9, 0xE094,\n\t0x71BE, 0xE095, 0x71C1, 0xFB59, 0x71C3, 0x9452, 0x71C8, 0x9395,\n\t0x71C9, 0xE097, 0x71CE, 0xE099, 0x71D0, 0x97D3, 0x71D2, 0xE096,\n\t0x71D4, 0xE098, 0x71D5, 0x898D, 0x71D7, 0xE093, 0x71DF, 0x9A7A,\n\t0x71E0, 0xE09A, 0x71E5, 0x9187, 0x71E6, 0x8E57, 0x71E7, 0xE09C,\n\t0x71EC, 0xE09B, 0x71ED, 0x9043, 0x71EE, 0x99D7, 0x71F5, 0xE09D,\n\t0x71F9, 0xE09F, 0x71FB, 0xE08E, 0x71FC, 0xE09E, 0x71FE, 0xFB5A,\n\t0x71FF, 0xE0A0, 0x7206, 0x949A, 0x720D, 0xE0A1, 0x7210, 0xE0A2,\n\t0x721B, 0xE0A3, 0x7228, 0xE0A4, 0x722A, 0x92DC, 0x722C, 0xE0A6,\n\t0x722D, 0xE0A5, 0x7230, 0xE0A7, 0x7232, 0xE0A8, 0x7235, 0x8EDD,\n\t0x7236, 0x9583, 0x723A, 0x96EA, 0x723B, 0xE0A9, 0x723C, 0xE0AA,\n\t0x723D, 0x9175, 0x723E, 0x8EA2, 0x723F, 0xE0AB, 0x7240, 0xE0AC,\n\t0x7246, 0xE0AD, 0x7247, 0x95D0, 0x7248, 0x94C5, 0x724B, 0xE0AE,\n\t0x724C, 0x9476, 0x7252, 0x92AB, 0x7258, 0xE0AF, 0x7259, 0x89E5,\n\t0x725B, 0x8B8D, 0x725D, 0x96C4, 0x725F, 0x96B4, 0x7261, 0x89B2,\n\t0x7262, 0x9853, 0x7267, 0x9671, 0x7269, 0x95A8, 0x7272, 0x90B5,\n\t0x7274, 0xE0B0, 0x7279, 0x93C1, 0x727D, 0x8CA1, 0x727E, 0xE0B1,\n\t0x7280, 0x8DD2, 0x7281, 0xE0B3, 0x7282, 0xE0B2, 0x7287, 0xE0B4,\n\t0x7292, 0xE0B5, 0x7296, 0xE0B6, 0x72A0, 0x8B5D, 0x72A2, 0xE0B7,\n\t0x72A7, 0xE0B8, 0x72AC, 0x8CA2, 0x72AF, 0x94C6, 0x72B1, 0xFB5B,\n\t0x72B2, 0xE0BA, 0x72B6, 0x8FF3, 0x72B9, 0xE0B9, 0x72BE, 0xFB5C,\n\t0x72C2, 0x8BB6, 0x72C3, 0xE0BB, 0x72C4, 0xE0BD, 0x72C6, 0xE0BC,\n\t0x72CE, 0xE0BE, 0x72D0, 0x8CCF, 0x72D2, 0xE0BF, 0x72D7, 0x8BE7,\n\t0x72D9, 0x915F, 0x72DB, 0x8D9D, 0x72E0, 0xE0C1, 0x72E1, 0xE0C2,\n\t0x72E2, 0xE0C0, 0x72E9, 0x8EEB, 0x72EC, 0x93C6, 0x72ED, 0x8BB7,\n\t0x72F7, 0xE0C4, 0x72F8, 0x924B, 0x72F9, 0xE0C3, 0x72FC, 0x9854,\n\t0x72FD, 0x9482, 0x730A, 0xE0C7, 0x7316, 0xE0C9, 0x7317, 0xE0C6,\n\t0x731B, 0x96D2, 0x731C, 0xE0C8, 0x731D, 0xE0CA, 0x731F, 0x97C2,\n\t0x7324, 0xFB5D, 0x7325, 0xE0CE, 0x7329, 0xE0CD, 0x732A, 0x9296,\n\t0x732B, 0x944C, 0x732E, 0x8CA3, 0x732F, 0xE0CC, 0x7334, 0xE0CB,\n\t0x7336, 0x9750, 0x7337, 0x9751, 0x733E, 0xE0CF, 0x733F, 0x898E,\n\t0x7344, 0x8D96, 0x7345, 0x8E82, 0x734E, 0xE0D0, 0x734F, 0xE0D1,\n\t0x7357, 0xE0D3, 0x7363, 0x8F62, 0x7368, 0xE0D5, 0x736A, 0xE0D4,\n\t0x7370, 0xE0D6, 0x7372, 0x8A6C, 0x7375, 0xE0D8, 0x7377, 0xFB5F,\n\t0x7378, 0xE0D7, 0x737A, 0xE0DA, 0x737B, 0xE0D9, 0x7384, 0x8CBA,\n\t0x7387, 0x97A6, 0x7389, 0x8BCA, 0x738B, 0x89A4, 0x7396, 0x8BE8,\n\t0x73A9, 0x8ADF, 0x73B2, 0x97E6, 0x73B3, 0xE0DC, 0x73BB, 0xE0DE,\n\t0x73BD, 0xFB60, 0x73C0, 0xE0DF, 0x73C2, 0x89CF, 0x73C8, 0xE0DB,\n\t0x73C9, 0xFB61, 0x73CA, 0x8E58, 0x73CD, 0x92BF, 0x73CE, 0xE0DD,\n\t0x73D2, 0xFB64, 0x73D6, 0xFB62, 0x73DE, 0xE0E2, 0x73E0, 0x8EEC,\n\t0x73E3, 0xFB63, 0x73E5, 0xE0E0, 0x73EA, 0x8C5D, 0x73ED, 0x94C7,\n\t0x73EE, 0xE0E1, 0x73F1, 0xE0FC, 0x73F5, 0xFB66, 0x73F8, 0xE0E7,\n\t0x73FE, 0x8CBB, 0x7403, 0x8B85, 0x7405, 0xE0E4, 0x7406, 0x979D,\n\t0x7407, 0xFB65, 0x7409, 0x97AE, 0x7422, 0x91F4, 0x7425, 0xE0E6,\n\t0x7426, 0xFB67, 0x7429, 0xFB69, 0x742A, 0xFB68, 0x742E, 0xFB6A,\n\t0x7432, 0xE0E8, 0x7433, 0x97D4, 0x7434, 0x8BD5, 0x7435, 0x94FA,\n\t0x7436, 0x9469, 0x743A, 0xE0E9, 0x743F, 0xE0EB, 0x7441, 0xE0EE,\n\t0x7455, 0xE0EA, 0x7459, 0xE0ED, 0x745A, 0x8CE8, 0x745B, 0x896C,\n\t0x745C, 0xE0EF, 0x745E, 0x9090, 0x745F, 0xE0EC, 0x7460, 0x97DA,\n\t0x7462, 0xFB6B, 0x7463, 0xE0F2, 0x7464, 0xEAA2, 0x7469, 0xE0F0,\n\t0x746A, 0xE0F3, 0x746F, 0xE0E5, 0x7470, 0xE0F1, 0x7473, 0x8DBA,\n\t0x7476, 0xE0F4, 0x747E, 0xE0F5, 0x7483, 0x979E, 0x7489, 0xFB6C,\n\t0x748B, 0xE0F6, 0x749E, 0xE0F7, 0x749F, 0xFB6D, 0x74A2, 0xE0E3,\n\t0x74A7, 0xE0F8, 0x74B0, 0x8AC2, 0x74BD, 0x8EA3, 0x74CA, 0xE0F9,\n\t0x74CF, 0xE0FA, 0x74D4, 0xE0FB, 0x74DC, 0x895A, 0x74E0, 0xE140,\n\t0x74E2, 0x955A, 0x74E3, 0xE141, 0x74E6, 0x8AA2, 0x74E7, 0xE142,\n\t0x74E9, 0xE143, 0x74EE, 0xE144, 0x74F0, 0xE146, 0x74F1, 0xE147,\n\t0x74F2, 0xE145, 0x74F6, 0x9572, 0x74F7, 0xE149, 0x74F8, 0xE148,\n\t0x7501, 0xFB6E, 0x7503, 0xE14B, 0x7504, 0xE14A, 0x7505, 0xE14C,\n\t0x750C, 0xE14D, 0x750D, 0xE14F, 0x750E, 0xE14E, 0x7511, 0x8D99,\n\t0x7513, 0xE151, 0x7515, 0xE150, 0x7518, 0x8AC3, 0x751A, 0x9072,\n\t0x751C, 0x935B, 0x751E, 0xE152, 0x751F, 0x90B6, 0x7523, 0x8E59,\n\t0x7525, 0x8999, 0x7526, 0xE153, 0x7528, 0x9770, 0x752B, 0x95E1,\n\t0x752C, 0xE154, 0x752F, 0xFAA8, 0x7530, 0x9363, 0x7531, 0x9752,\n\t0x7532, 0x8D62, 0x7533, 0x905C, 0x7537, 0x926A, 0x7538, 0x99B2,\n\t0x753A, 0x92AC, 0x753B, 0x89E6, 0x753C, 0xE155, 0x7544, 0xE156,\n\t0x7546, 0xE15B, 0x7549, 0xE159, 0x754A, 0xE158, 0x754B, 0x9DC0,\n\t0x754C, 0x8A45, 0x754D, 0xE157, 0x754F, 0x88D8, 0x7551, 0x94A8,\n\t0x7554, 0x94C8, 0x7559, 0x97AF, 0x755A, 0xE15C, 0x755B, 0xE15A,\n\t0x755C, 0x927B, 0x755D, 0x90A4, 0x7560, 0x94A9, 0x7562, 0x954C,\n\t0x7564, 0xE15E, 0x7565, 0x97AA, 0x7566, 0x8C6C, 0x7567, 0xE15F,\n\t0x7569, 0xE15D, 0x756A, 0x94D4, 0x756B, 0xE160, 0x756D, 0xE161,\n\t0x756F, 0xFB6F, 0x7570, 0x88D9, 0x7573, 0x8FF4, 0x7574, 0xE166,\n\t0x7576, 0xE163, 0x7577, 0x93EB, 0x7578, 0xE162, 0x757F, 0x8B45,\n\t0x7582, 0xE169, 0x7586, 0xE164, 0x7587, 0xE165, 0x7589, 0xE168,\n\t0x758A, 0xE167, 0x758B, 0x9544, 0x758E, 0x9161, 0x758F, 0x9160,\n\t0x7591, 0x8B5E, 0x7594, 0xE16A, 0x759A, 0xE16B, 0x759D, 0xE16C,\n\t0x75A3, 0xE16E, 0x75A5, 0xE16D, 0x75AB, 0x8975, 0x75B1, 0xE176,\n\t0x75B2, 0x94E6, 0x75B3, 0xE170, 0x75B5, 0xE172, 0x75B8, 0xE174,\n\t0x75B9, 0x905D, 0x75BC, 0xE175, 0x75BD, 0xE173, 0x75BE, 0x8EBE,\n\t0x75C2, 0xE16F, 0x75C3, 0xE171, 0x75C5, 0x9561, 0x75C7, 0x8FC7,\n\t0x75CA, 0xE178, 0x75CD, 0xE177, 0x75D2, 0xE179, 0x75D4, 0x8EA4,\n\t0x75D5, 0x8DAD, 0x75D8, 0x9397, 0x75D9, 0xE17A, 0x75DB, 0x92C9,\n\t0x75DE, 0xE17C, 0x75E2, 0x979F, 0x75E3, 0xE17B, 0x75E9, 0x9189,\n\t0x75F0, 0xE182, 0x75F2, 0xE184, 0x75F3, 0xE185, 0x75F4, 0x9273,\n\t0x75FA, 0xE183, 0x75FC, 0xE180, 0x75FE, 0xE17D, 0x75FF, 0xE17E,\n\t0x7601, 0xE181, 0x7609, 0xE188, 0x760B, 0xE186, 0x760D, 0xE187,\n\t0x761F, 0xE189, 0x7620, 0xE18B, 0x7621, 0xE18C, 0x7622, 0xE18D,\n\t0x7624, 0xE18E, 0x7627, 0xE18A, 0x7630, 0xE190, 0x7634, 0xE18F,\n\t0x763B, 0xE191, 0x7642, 0x97C3, 0x7646, 0xE194, 0x7647, 0xE192,\n\t0x7648, 0xE193, 0x764C, 0x8AE0, 0x7652, 0x96FC, 0x7656, 0x95C8,\n\t0x7658, 0xE196, 0x765C, 0xE195, 0x7661, 0xE197, 0x7662, 0xE198,\n\t0x7667, 0xE19C, 0x7668, 0xE199, 0x7669, 0xE19A, 0x766A, 0xE19B,\n\t0x766C, 0xE19D, 0x7670, 0xE19E, 0x7672, 0xE19F, 0x7676, 0xE1A0,\n\t0x7678, 0xE1A1, 0x767A, 0x94AD, 0x767B, 0x936F, 0x767C, 0xE1A2,\n\t0x767D, 0x9492, 0x767E, 0x9553, 0x7680, 0xE1A3, 0x7682, 0xFB70,\n\t0x7683, 0xE1A4, 0x7684, 0x9349, 0x7686, 0x8A46, 0x7687, 0x8D63,\n\t0x7688, 0xE1A5, 0x768B, 0xE1A6, 0x768E, 0xE1A7, 0x7690, 0x8E48,\n\t0x7693, 0xE1A9, 0x7696, 0xE1A8, 0x7699, 0xE1AA, 0x769A, 0xE1AB,\n\t0x769B, 0xFB73, 0x769C, 0xFB71, 0x769E, 0xFB72, 0x76A6, 0xFB74,\n\t0x76AE, 0x94E7, 0x76B0, 0xE1AC, 0x76B4, 0xE1AD, 0x76B7, 0xEA89,\n\t0x76B8, 0xE1AE, 0x76B9, 0xE1AF, 0x76BA, 0xE1B0, 0x76BF, 0x8E4D,\n\t0x76C2, 0xE1B1, 0x76C3, 0x9475, 0x76C6, 0x967E, 0x76C8, 0x896D,\n\t0x76CA, 0x8976, 0x76CD, 0xE1B2, 0x76D2, 0xE1B4, 0x76D6, 0xE1B3,\n\t0x76D7, 0x9390, 0x76DB, 0x90B7, 0x76DC, 0x9F58, 0x76DE, 0xE1B5,\n\t0x76DF, 0x96BF, 0x76E1, 0xE1B6, 0x76E3, 0x8AC4, 0x76E4, 0x94D5,\n\t0x76E5, 0xE1B7, 0x76E7, 0xE1B8, 0x76EA, 0xE1B9, 0x76EE, 0x96DA,\n\t0x76F2, 0x96D3, 0x76F4, 0x92BC, 0x76F8, 0x918A, 0x76FB, 0xE1BB,\n\t0x76FE, 0x8F82, 0x7701, 0x8FC8, 0x7704, 0xE1BE, 0x7707, 0xE1BD,\n\t0x7708, 0xE1BC, 0x7709, 0x94FB, 0x770B, 0x8AC5, 0x770C, 0x8CA7,\n\t0x771B, 0xE1C4, 0x771E, 0xE1C1, 0x771F, 0x905E, 0x7720, 0x96B0,\n\t0x7724, 0xE1C0, 0x7725, 0xE1C2, 0x7726, 0xE1C3, 0x7729, 0xE1BF,\n\t0x7737, 0xE1C5, 0x7738, 0xE1C6, 0x773A, 0x92AD, 0x773C, 0x8AE1,\n\t0x7740, 0x9285, 0x7746, 0xFB76, 0x7747, 0xE1C7, 0x775A, 0xE1C8,\n\t0x775B, 0xE1CB, 0x7761, 0x9087, 0x7763, 0x93C2, 0x7765, 0xE1CC,\n\t0x7766, 0x9672, 0x7768, 0xE1C9, 0x776B, 0xE1CA, 0x7779, 0xE1CF,\n\t0x777E, 0xE1CE, 0x777F, 0xE1CD, 0x778B, 0xE1D1, 0x778E, 0xE1D0,\n\t0x7791, 0xE1D2, 0x779E, 0xE1D4, 0x77A0, 0xE1D3, 0x77A5, 0x95CB,\n\t0x77AC, 0x8F75, 0x77AD, 0x97C4, 0x77B0, 0xE1D5, 0x77B3, 0x93B5,\n\t0x77B6, 0xE1D6, 0x77B9, 0xE1D7, 0x77BB, 0xE1DB, 0x77BC, 0xE1D9,\n\t0x77BD, 0xE1DA, 0x77BF, 0xE1D8, 0x77C7, 0xE1DC, 0x77CD, 0xE1DD,\n\t0x77D7, 0xE1DE, 0x77DA, 0xE1DF, 0x77DB, 0x96B5, 0x77DC, 0xE1E0,\n\t0x77E2, 0x96EE, 0x77E3, 0xE1E1, 0x77E5, 0x926D, 0x77E7, 0x948A,\n\t0x77E9, 0x8BE9, 0x77ED, 0x925A, 0x77EE, 0xE1E2, 0x77EF, 0x8BB8,\n\t0x77F3, 0x90CE, 0x77FC, 0xE1E3, 0x7802, 0x8DBB, 0x780C, 0xE1E4,\n\t0x7812, 0xE1E5, 0x7814, 0x8CA4, 0x7815, 0x8DD3, 0x7820, 0xE1E7,\n\t0x7821, 0xFB78, 0x7825, 0x9375, 0x7826, 0x8DD4, 0x7827, 0x8B6D,\n\t0x7832, 0x9643, 0x7834, 0x946A, 0x783A, 0x9376, 0x783F, 0x8D7B,\n\t0x7845, 0xE1E9, 0x784E, 0xFB79, 0x785D, 0x8FC9, 0x7864, 0xFB7A,\n\t0x786B, 0x97B0, 0x786C, 0x8D64, 0x786F, 0x8CA5, 0x7872, 0x94A1,\n\t0x7874, 0xE1EB, 0x787A, 0xFB7B, 0x787C, 0xE1ED, 0x7881, 0x8CE9,\n\t0x7886, 0xE1EC, 0x7887, 0x92F4, 0x788C, 0xE1EF, 0x788D, 0x8A56,\n\t0x788E, 0xE1EA, 0x7891, 0x94E8, 0x7893, 0x894F, 0x7895, 0x8DEA,\n\t0x7897, 0x9871, 0x789A, 0xE1EE, 0x78A3, 0xE1F0, 0x78A7, 0x95C9,\n\t0x78A9, 0x90D7, 0x78AA, 0xE1F2, 0x78AF, 0xE1F3, 0x78B5, 0xE1F1,\n\t0x78BA, 0x8A6D, 0x78BC, 0xE1F9, 0x78BE, 0xE1F8, 0x78C1, 0x8EA5,\n\t0x78C5, 0xE1FA, 0x78C6, 0xE1F5, 0x78CA, 0xE1FB, 0x78CB, 0xE1F6,\n\t0x78D0, 0x94D6, 0x78D1, 0xE1F4, 0x78D4, 0xE1F7, 0x78DA, 0xE241,\n\t0x78E7, 0xE240, 0x78E8, 0x9681, 0x78EC, 0xE1FC, 0x78EF, 0x88E9,\n\t0x78F4, 0xE243, 0x78FD, 0xE242, 0x7901, 0x8FCA, 0x7907, 0xE244,\n\t0x790E, 0x9162, 0x7911, 0xE246, 0x7912, 0xE245, 0x7919, 0xE247,\n\t0x7926, 0xE1E6, 0x792A, 0xE1E8, 0x792B, 0xE249, 0x792C, 0xE248,\n\t0x7930, 0xFB7C, 0x793A, 0x8EA6, 0x793C, 0x97E7, 0x793E, 0x8ED0,\n\t0x7940, 0xE24A, 0x7941, 0x8C56, 0x7947, 0x8B5F, 0x7948, 0x8B46,\n\t0x7949, 0x8E83, 0x7950, 0x9753, 0x7953, 0xE250, 0x7955, 0xE24F,\n\t0x7956, 0x9163, 0x7957, 0xE24C, 0x795A, 0xE24E, 0x795D, 0x8F6A,\n\t0x795E, 0x905F, 0x795F, 0xE24D, 0x7960, 0xE24B, 0x7962, 0x9449,\n\t0x7965, 0x8FCB, 0x7968, 0x955B, 0x796D, 0x8DD5, 0x7977, 0x9398,\n\t0x797A, 0xE251, 0x797F, 0xE252, 0x7980, 0xE268, 0x7981, 0x8BD6,\n\t0x7984, 0x985C, 0x7985, 0x9154, 0x798A, 0xE253, 0x798D, 0x89D0,\n\t0x798E, 0x92F5, 0x798F, 0x959F, 0x7994, 0xFB81, 0x799B, 0xFB83,\n\t0x799D, 0xE254, 0x79A6, 0x8B9A, 0x79A7, 0xE255, 0x79AA, 0xE257,\n\t0x79AE, 0xE258, 0x79B0, 0x9448, 0x79B3, 0xE259, 0x79B9, 0xE25A,\n\t0x79BA, 0xE25B, 0x79BD, 0x8BD7, 0x79BE, 0x89D1, 0x79BF, 0x93C3,\n\t0x79C0, 0x8F47, 0x79C1, 0x8E84, 0x79C9, 0xE25C, 0x79CB, 0x8F48,\n\t0x79D1, 0x89C8, 0x79D2, 0x9562, 0x79D5, 0xE25D, 0x79D8, 0x94E9,\n\t0x79DF, 0x9164, 0x79E1, 0xE260, 0x79E3, 0xE261, 0x79E4, 0x9489,\n\t0x79E6, 0x9060, 0x79E7, 0xE25E, 0x79E9, 0x9281, 0x79EC, 0xE25F,\n\t0x79F0, 0x8FCC, 0x79FB, 0x88DA, 0x7A00, 0x8B48, 0x7A08, 0xE262,\n\t0x7A0B, 0x92F6, 0x7A0D, 0xE263, 0x7A0E, 0x90C5, 0x7A14, 0x96AB,\n\t0x7A17, 0x9542, 0x7A18, 0xE264, 0x7A19, 0xE265, 0x7A1A, 0x9274,\n\t0x7A1C, 0x97C5, 0x7A1F, 0xE267, 0x7A20, 0xE266, 0x7A2E, 0x8EED,\n\t0x7A31, 0xE269, 0x7A32, 0x88EE, 0x7A37, 0xE26C, 0x7A3B, 0xE26A,\n\t0x7A3C, 0x89D2, 0x7A3D, 0x8C6D, 0x7A3E, 0xE26B, 0x7A3F, 0x8D65,\n\t0x7A40, 0x8D92, 0x7A42, 0x95E4, 0x7A43, 0xE26D, 0x7A46, 0x9673,\n\t0x7A49, 0xE26F, 0x7A4D, 0x90CF, 0x7A4E, 0x896E, 0x7A4F, 0x89B8,\n\t0x7A50, 0x88AA, 0x7A57, 0xE26E, 0x7A61, 0xE270, 0x7A62, 0xE271,\n\t0x7A63, 0x8FF5, 0x7A69, 0xE272, 0x7A6B, 0x8A6E, 0x7A70, 0xE274,\n\t0x7A74, 0x8C8A, 0x7A76, 0x8B86, 0x7A79, 0xE275, 0x7A7A, 0x8BF3,\n\t0x7A7D, 0xE276, 0x7A7F, 0x90FA, 0x7A81, 0x93CB, 0x7A83, 0x90DE,\n\t0x7A84, 0x8DF3, 0x7A88, 0xE277, 0x7A92, 0x9282, 0x7A93, 0x918B,\n\t0x7A95, 0xE279, 0x7A96, 0xE27B, 0x7A97, 0xE278, 0x7A98, 0xE27A,\n\t0x7A9F, 0x8C41, 0x7AA9, 0xE27C, 0x7AAA, 0x8C45, 0x7AAE, 0x8B87,\n\t0x7AAF, 0x9771, 0x7AB0, 0xE27E, 0x7AB6, 0xE280, 0x7ABA, 0x894D,\n\t0x7ABF, 0xE283, 0x7AC3, 0x8A96, 0x7AC4, 0xE282, 0x7AC5, 0xE281,\n\t0x7AC7, 0xE285, 0x7AC8, 0xE27D, 0x7ACA, 0xE286, 0x7ACB, 0x97A7,\n\t0x7ACD, 0xE287, 0x7ACF, 0xE288, 0x7AD1, 0xFB84, 0x7AD2, 0x9AF2,\n\t0x7AD3, 0xE28A, 0x7AD5, 0xE289, 0x7AD9, 0xE28B, 0x7ADA, 0xE28C,\n\t0x7ADC, 0x97B3, 0x7ADD, 0xE28D, 0x7ADF, 0xE8ED, 0x7AE0, 0x8FCD,\n\t0x7AE1, 0xE28E, 0x7AE2, 0xE28F, 0x7AE3, 0x8F76, 0x7AE5, 0x93B6,\n\t0x7AE6, 0xE290, 0x7AE7, 0xFB85, 0x7AEA, 0x9247, 0x7AEB, 0xFB87,\n\t0x7AED, 0xE291, 0x7AEF, 0x925B, 0x7AF0, 0xE292, 0x7AF6, 0x8BA3,\n\t0x7AF8, 0x995E, 0x7AF9, 0x927C, 0x7AFA, 0x8EB1, 0x7AFF, 0x8AC6,\n\t0x7B02, 0xE293, 0x7B04, 0xE2A0, 0x7B06, 0xE296, 0x7B08, 0x8B88,\n\t0x7B0A, 0xE295, 0x7B0B, 0xE2A2, 0x7B0F, 0xE294, 0x7B11, 0x8FCE,\n\t0x7B18, 0xE298, 0x7B19, 0xE299, 0x7B1B, 0x934A, 0x7B1E, 0xE29A,\n\t0x7B20, 0x8A7D, 0x7B25, 0x9079, 0x7B26, 0x9584, 0x7B28, 0xE29C,\n\t0x7B2C, 0x91E6, 0x7B33, 0xE297, 0x7B35, 0xE29B, 0x7B36, 0xE29D,\n\t0x7B39, 0x8DF9, 0x7B45, 0xE2A4, 0x7B46, 0x954D, 0x7B48, 0x94A4,\n\t0x7B49, 0x9399, 0x7B4B, 0x8BD8, 0x7B4C, 0xE2A3, 0x7B4D, 0xE2A1,\n\t0x7B4F, 0x94B3, 0x7B50, 0xE29E, 0x7B51, 0x927D, 0x7B52, 0x939B,\n\t0x7B54, 0x939A, 0x7B56, 0x8DF4, 0x7B5D, 0xE2B6, 0x7B65, 0xE2A6,\n\t0x7B67, 0xE2A8, 0x7B6C, 0xE2AB, 0x7B6E, 0xE2AC, 0x7B70, 0xE2A9,\n\t0x7B71, 0xE2AA, 0x7B74, 0xE2A7, 0x7B75, 0xE2A5, 0x7B7A, 0xE29F,\n\t0x7B86, 0x95CD, 0x7B87, 0x89D3, 0x7B8B, 0xE2B3, 0x7B8D, 0xE2B0,\n\t0x7B8F, 0xE2B5, 0x7B92, 0xE2B4, 0x7B94, 0x9493, 0x7B95, 0x96A5,\n\t0x7B97, 0x8E5A, 0x7B98, 0xE2AE, 0x7B99, 0xE2B7, 0x7B9A, 0xE2B2,\n\t0x7B9C, 0xE2B1, 0x7B9D, 0xE2AD, 0x7B9E, 0xFB88, 0x7B9F, 0xE2AF,\n\t0x7BA1, 0x8AC7, 0x7BAA, 0x925C, 0x7BAD, 0x90FB, 0x7BB1, 0x94A0,\n\t0x7BB4, 0xE2BC, 0x7BB8, 0x94A2, 0x7BC0, 0x90DF, 0x7BC1, 0xE2B9,\n\t0x7BC4, 0x94CD, 0x7BC6, 0xE2BD, 0x7BC7, 0x95D1, 0x7BC9, 0x927A,\n\t0x7BCB, 0xE2B8, 0x7BCC, 0xE2BA, 0x7BCF, 0xE2BB, 0x7BDD, 0xE2BE,\n\t0x7BE0, 0x8EC2, 0x7BE4, 0x93C4, 0x7BE5, 0xE2C3, 0x7BE6, 0xE2C2,\n\t0x7BE9, 0xE2BF, 0x7BED, 0x9855, 0x7BF3, 0xE2C8, 0x7BF6, 0xE2CC,\n\t0x7BF7, 0xE2C9, 0x7C00, 0xE2C5, 0x7C07, 0xE2C6, 0x7C0D, 0xE2CB,\n\t0x7C11, 0xE2C0, 0x7C12, 0x99D3, 0x7C13, 0xE2C7, 0x7C14, 0xE2C1,\n\t0x7C17, 0xE2CA, 0x7C1F, 0xE2D0, 0x7C21, 0x8AC8, 0x7C23, 0xE2CD,\n\t0x7C27, 0xE2CE, 0x7C2A, 0xE2CF, 0x7C2B, 0xE2D2, 0x7C37, 0xE2D1,\n\t0x7C38, 0x94F4, 0x7C3D, 0xE2D3, 0x7C3E, 0x97FA, 0x7C3F, 0x95EB,\n\t0x7C40, 0xE2D8, 0x7C43, 0xE2D5, 0x7C4C, 0xE2D4, 0x7C4D, 0x90D0,\n\t0x7C4F, 0xE2D7, 0x7C50, 0xE2D9, 0x7C54, 0xE2D6, 0x7C56, 0xE2DD,\n\t0x7C58, 0xE2DA, 0x7C5F, 0xE2DB, 0x7C60, 0xE2C4, 0x7C64, 0xE2DC,\n\t0x7C65, 0xE2DE, 0x7C6C, 0xE2DF, 0x7C73, 0x95C4, 0x7C75, 0xE2E0,\n\t0x7C7E, 0x96E0, 0x7C81, 0x8BCC, 0x7C82, 0x8C48, 0x7C83, 0xE2E1,\n\t0x7C89, 0x95B2, 0x7C8B, 0x9088, 0x7C8D, 0x96AE, 0x7C90, 0xE2E2,\n\t0x7C92, 0x97B1, 0x7C95, 0x9494, 0x7C97, 0x9165, 0x7C98, 0x9453,\n\t0x7C9B, 0x8F6C, 0x7C9F, 0x88BE, 0x7CA1, 0xE2E7, 0x7CA2, 0xE2E5,\n\t0x7CA4, 0xE2E3, 0x7CA5, 0x8A9F, 0x7CA7, 0x8FCF, 0x7CA8, 0xE2E8,\n\t0x7CAB, 0xE2E6, 0x7CAD, 0xE2E4, 0x7CAE, 0xE2EC, 0x7CB1, 0xE2EB,\n\t0x7CB2, 0xE2EA, 0x7CB3, 0xE2E9, 0x7CB9, 0xE2ED, 0x7CBD, 0xE2EE,\n\t0x7CBE, 0x90B8, 0x7CC0, 0xE2EF, 0x7CC2, 0xE2F1, 0x7CC5, 0xE2F0,\n\t0x7CCA, 0x8CD0, 0x7CCE, 0x9157, 0x7CD2, 0xE2F3, 0x7CD6, 0x939C,\n\t0x7CD8, 0xE2F2, 0x7CDC, 0xE2F4, 0x7CDE, 0x95B3, 0x7CDF, 0x918C,\n\t0x7CE0, 0x8D66, 0x7CE2, 0xE2F5, 0x7CE7, 0x97C6, 0x7CEF, 0xE2F7,\n\t0x7CF2, 0xE2F8, 0x7CF4, 0xE2F9, 0x7CF6, 0xE2FA, 0x7CF8, 0x8E85,\n\t0x7CFA, 0xE2FB, 0x7CFB, 0x8C6E, 0x7CFE, 0x8B8A, 0x7D00, 0x8B49,\n\t0x7D02, 0xE340, 0x7D04, 0x96F1, 0x7D05, 0x8D67, 0x7D06, 0xE2FC,\n\t0x7D0A, 0xE343, 0x7D0B, 0x96E4, 0x7D0D, 0x945B, 0x7D10, 0x9552,\n\t0x7D14, 0x8F83, 0x7D15, 0xE342, 0x7D17, 0x8ED1, 0x7D18, 0x8D68,\n\t0x7D19, 0x8E86, 0x7D1A, 0x8B89, 0x7D1B, 0x95B4, 0x7D1C, 0xE341,\n\t0x7D20, 0x9166, 0x7D21, 0x9661, 0x7D22, 0x8DF5, 0x7D2B, 0x8E87,\n\t0x7D2C, 0x92DB, 0x7D2E, 0xE346, 0x7D2F, 0x97DD, 0x7D30, 0x8DD7,\n\t0x7D32, 0xE347, 0x7D33, 0x9061, 0x7D35, 0xE349, 0x7D39, 0x8FD0,\n\t0x7D3A, 0x8DAE, 0x7D3F, 0xE348, 0x7D42, 0x8F49, 0x7D43, 0x8CBC,\n\t0x7D44, 0x9167, 0x7D45, 0xE344, 0x7D46, 0xE34A, 0x7D48, 0xFB8A,\n\t0x7D4B, 0xE345, 0x7D4C, 0x8C6F, 0x7D4E, 0xE34D, 0x7D4F, 0xE351,\n\t0x7D50, 0x8C8B, 0x7D56, 0xE34C, 0x7D5B, 0xE355, 0x7D5C, 0xFB8B,\n\t0x7D5E, 0x8D69, 0x7D61, 0x978D, 0x7D62, 0x88BA, 0x7D63, 0xE352,\n\t0x7D66, 0x8B8B, 0x7D68, 0xE34F, 0x7D6E, 0xE350, 0x7D71, 0x939D,\n\t0x7D72, 0xE34E, 0x7D73, 0xE34B, 0x7D75, 0x8A47, 0x7D76, 0x90E2,\n\t0x7D79, 0x8CA6, 0x7D7D, 0xE357, 0x7D89, 0xE354, 0x7D8F, 0xE356,\n\t0x7D93, 0xE353, 0x7D99, 0x8C70, 0x7D9A, 0x91B1, 0x7D9B, 0xE358,\n\t0x7D9C, 0x918E, 0x7D9F, 0xE365, 0x7DA0, 0xFB8D, 0x7DA2, 0xE361,\n\t0x7DA3, 0xE35B, 0x7DAB, 0xE35F, 0x7DAC, 0x8EF8, 0x7DAD, 0x88DB,\n\t0x7DAE, 0xE35A, 0x7DAF, 0xE362, 0x7DB0, 0xE366, 0x7DB1, 0x8D6A,\n\t0x7DB2, 0x96D4, 0x7DB4, 0x92D4, 0x7DB5, 0xE35C, 0x7DB7, 0xFB8C,\n\t0x7DB8, 0xE364, 0x7DBA, 0xE359, 0x7DBB, 0x925D, 0x7DBD, 0xE35E,\n\t0x7DBE, 0x88BB, 0x7DBF, 0x96C8, 0x7DC7, 0xE35D, 0x7DCA, 0x8BD9,\n\t0x7DCB, 0x94EA, 0x7DCF, 0x918D, 0x7DD1, 0x97CE, 0x7DD2, 0x8F8F,\n\t0x7DD5, 0xE38E, 0x7DD6, 0xFB8E, 0x7DD8, 0xE367, 0x7DDA, 0x90FC,\n\t0x7DDC, 0xE363, 0x7DDD, 0xE368, 0x7DDE, 0xE36A, 0x7DE0, 0x92F7,\n\t0x7DE1, 0xE36D, 0x7DE4, 0xE369, 0x7DE8, 0x95D2, 0x7DE9, 0x8AC9,\n\t0x7DEC, 0x96C9, 0x7DEF, 0x88DC, 0x7DF2, 0xE36C, 0x7DF4, 0x97FB,\n\t0x7DFB, 0xE36B, 0x7E01, 0x898F, 0x7E04, 0x93EA, 0x7E05, 0xE36E,\n\t0x7E09, 0xE375, 0x7E0A, 0xE36F, 0x7E0B, 0xE376, 0x7E12, 0xE372,\n\t0x7E1B, 0x949B, 0x7E1E, 0x8EC8, 0x7E1F, 0xE374, 0x7E21, 0xE371,\n\t0x7E22, 0xE377, 0x7E23, 0xE370, 0x7E26, 0x8F63, 0x7E2B, 0x9644,\n\t0x7E2E, 0x8F6B, 0x7E31, 0xE373, 0x7E32, 0xE380, 0x7E35, 0xE37B,\n\t0x7E37, 0xE37E, 0x7E39, 0xE37C, 0x7E3A, 0xE381, 0x7E3B, 0xE37A,\n\t0x7E3D, 0xE360, 0x7E3E, 0x90D1, 0x7E41, 0x94C9, 0x7E43, 0xE37D,\n\t0x7E46, 0xE378, 0x7E4A, 0x9140, 0x7E4B, 0x8C71, 0x7E4D, 0x8F4A,\n\t0x7E52, 0xFB8F, 0x7E54, 0x9044, 0x7E55, 0x9155, 0x7E56, 0xE384,\n\t0x7E59, 0xE386, 0x7E5A, 0xE387, 0x7E5D, 0xE383, 0x7E5E, 0xE385,\n\t0x7E66, 0xE379, 0x7E67, 0xE382, 0x7E69, 0xE38A, 0x7E6A, 0xE389,\n\t0x7E6D, 0x969A, 0x7E70, 0x8C4A, 0x7E79, 0xE388, 0x7E7B, 0xE38C,\n\t0x7E7C, 0xE38B, 0x7E7D, 0xE38F, 0x7E7F, 0xE391, 0x7E82, 0x8E5B,\n\t0x7E83, 0xE38D, 0x7E88, 0xE392, 0x7E89, 0xE393, 0x7E8A, 0xFA5C,\n\t0x7E8C, 0xE394, 0x7E8E, 0xE39A, 0x7E8F, 0x935A, 0x7E90, 0xE396,\n\t0x7E92, 0xE395, 0x7E93, 0xE397, 0x7E94, 0xE398, 0x7E96, 0xE399,\n\t0x7E9B, 0xE39B, 0x7E9C, 0xE39C, 0x7F36, 0x8ACA, 0x7F38, 0xE39D,\n\t0x7F3A, 0xE39E, 0x7F45, 0xE39F, 0x7F47, 0xFB90, 0x7F4C, 0xE3A0,\n\t0x7F4D, 0xE3A1, 0x7F4E, 0xE3A2, 0x7F50, 0xE3A3, 0x7F51, 0xE3A4,\n\t0x7F54, 0xE3A6, 0x7F55, 0xE3A5, 0x7F58, 0xE3A7, 0x7F5F, 0xE3A8,\n\t0x7F60, 0xE3A9, 0x7F67, 0xE3AC, 0x7F68, 0xE3AA, 0x7F69, 0xE3AB,\n\t0x7F6A, 0x8DDF, 0x7F6B, 0x8C72, 0x7F6E, 0x9275, 0x7F70, 0x94B1,\n\t0x7F72, 0x8F90, 0x7F75, 0x946C, 0x7F77, 0x94EB, 0x7F78, 0xE3AD,\n\t0x7F79, 0x9CEB, 0x7F82, 0xE3AE, 0x7F83, 0xE3B0, 0x7F85, 0x9785,\n\t0x7F86, 0xE3AF, 0x7F87, 0xE3B2, 0x7F88, 0xE3B1, 0x7F8A, 0x9772,\n\t0x7F8C, 0xE3B3, 0x7F8E, 0x94FC, 0x7F94, 0xE3B4, 0x7F9A, 0xE3B7,\n\t0x7F9D, 0xE3B6, 0x7F9E, 0xE3B5, 0x7FA1, 0xFB91, 0x7FA3, 0xE3B8,\n\t0x7FA4, 0x8C51, 0x7FA8, 0x9141, 0x7FA9, 0x8B60, 0x7FAE, 0xE3BC,\n\t0x7FAF, 0xE3B9, 0x7FB2, 0xE3BA, 0x7FB6, 0xE3BD, 0x7FB8, 0xE3BE,\n\t0x7FB9, 0xE3BB, 0x7FBD, 0x8948, 0x7FC1, 0x89A5, 0x7FC5, 0xE3C0,\n\t0x7FC6, 0xE3C1, 0x7FCA, 0xE3C2, 0x7FCC, 0x9782, 0x7FD2, 0x8F4B,\n\t0x7FD4, 0xE3C4, 0x7FD5, 0xE3C3, 0x7FE0, 0x9089, 0x7FE1, 0xE3C5,\n\t0x7FE6, 0xE3C6, 0x7FE9, 0xE3C7, 0x7FEB, 0x8AE3, 0x7FF0, 0x8ACB,\n\t0x7FF3, 0xE3C8, 0x7FF9, 0xE3C9, 0x7FFB, 0x967C, 0x7FFC, 0x9783,\n\t0x8000, 0x9773, 0x8001, 0x9856, 0x8003, 0x8D6C, 0x8004, 0xE3CC,\n\t0x8005, 0x8ED2, 0x8006, 0xE3CB, 0x800B, 0xE3CD, 0x800C, 0x8EA7,\n\t0x8010, 0x91CF, 0x8012, 0xE3CE, 0x8015, 0x8D6B, 0x8017, 0x96D5,\n\t0x8018, 0xE3CF, 0x8019, 0xE3D0, 0x801C, 0xE3D1, 0x8021, 0xE3D2,\n\t0x8028, 0xE3D3, 0x8033, 0x8EA8, 0x8036, 0x96EB, 0x803B, 0xE3D5,\n\t0x803D, 0x925E, 0x803F, 0xE3D4, 0x8046, 0xE3D7, 0x804A, 0xE3D6,\n\t0x8052, 0xE3D8, 0x8056, 0x90B9, 0x8058, 0xE3D9, 0x805A, 0xE3DA,\n\t0x805E, 0x95B7, 0x805F, 0xE3DB, 0x8061, 0x918F, 0x8062, 0xE3DC,\n\t0x8068, 0xE3DD, 0x806F, 0x97FC, 0x8070, 0xE3E0, 0x8072, 0xE3DF,\n\t0x8073, 0xE3DE, 0x8074, 0x92AE, 0x8076, 0xE3E1, 0x8077, 0x9045,\n\t0x8079, 0xE3E2, 0x807D, 0xE3E3, 0x807E, 0x9857, 0x807F, 0xE3E4,\n\t0x8084, 0xE3E5, 0x8085, 0xE3E7, 0x8086, 0xE3E6, 0x8087, 0x94A3,\n\t0x8089, 0x93F7, 0x808B, 0x985D, 0x808C, 0x94A7, 0x8093, 0xE3E9,\n\t0x8096, 0x8FD1, 0x8098, 0x9549, 0x809A, 0xE3EA, 0x809B, 0xE3E8,\n\t0x809D, 0x8ACC, 0x80A1, 0x8CD2, 0x80A2, 0x8E88, 0x80A5, 0x94EC,\n\t0x80A9, 0x8CA8, 0x80AA, 0x9662, 0x80AC, 0xE3ED, 0x80AD, 0xE3EB,\n\t0x80AF, 0x8D6D, 0x80B1, 0x8D6E, 0x80B2, 0x88E7, 0x80B4, 0x8DE6,\n\t0x80BA, 0x9478, 0x80C3, 0x88DD, 0x80C4, 0xE3F2, 0x80C6, 0x925F,\n\t0x80CC, 0x9477, 0x80CE, 0x91D9, 0x80D6, 0xE3F4, 0x80D9, 0xE3F0,\n\t0x80DA, 0xE3F3, 0x80DB, 0xE3EE, 0x80DD, 0xE3F1, 0x80DE, 0x9645,\n\t0x80E1, 0x8CD3, 0x80E4, 0x88FB, 0x80E5, 0xE3EF, 0x80EF, 0xE3F6,\n\t0x80F1, 0xE3F7, 0x80F4, 0x93B7, 0x80F8, 0x8BB9, 0x80FC, 0xE445,\n\t0x80FD, 0x945C, 0x8102, 0x8E89, 0x8105, 0x8BBA, 0x8106, 0x90C6,\n\t0x8107, 0x9865, 0x8108, 0x96AC, 0x8109, 0xE3F5, 0x810A, 0x90D2,\n\t0x811A, 0x8B72, 0x811B, 0xE3F8, 0x8123, 0xE3FA, 0x8129, 0xE3F9,\n\t0x812F, 0xE3FB, 0x8131, 0x9245, 0x8133, 0x945D, 0x8139, 0x92AF,\n\t0x813E, 0xE442, 0x8146, 0xE441, 0x814B, 0xE3FC, 0x814E, 0x9074,\n\t0x8150, 0x9585, 0x8151, 0xE444, 0x8153, 0xE443, 0x8154, 0x8D6F,\n\t0x8155, 0x9872, 0x815F, 0xE454, 0x8165, 0xE448, 0x8166, 0xE449,\n\t0x816B, 0x8EEE, 0x816E, 0xE447, 0x8170, 0x8D98, 0x8171, 0xE446,\n\t0x8174, 0xE44A, 0x8178, 0x92B0, 0x8179, 0x95A0, 0x817A, 0x9142,\n\t0x817F, 0x91DA, 0x8180, 0xE44E, 0x8182, 0xE44F, 0x8183, 0xE44B,\n\t0x8188, 0xE44C, 0x818A, 0xE44D, 0x818F, 0x8D70, 0x8193, 0xE455,\n\t0x8195, 0xE451, 0x819A, 0x9586, 0x819C, 0x968C, 0x819D, 0x9547,\n\t0x81A0, 0xE450, 0x81A3, 0xE453, 0x81A4, 0xE452, 0x81A8, 0x9663,\n\t0x81A9, 0xE456, 0x81B0, 0xE457, 0x81B3, 0x9156, 0x81B5, 0xE458,\n\t0x81B8, 0xE45A, 0x81BA, 0xE45E, 0x81BD, 0xE45B, 0x81BE, 0xE459,\n\t0x81BF, 0x945E, 0x81C0, 0xE45C, 0x81C2, 0xE45D, 0x81C6, 0x89B0,\n\t0x81C8, 0xE464, 0x81C9, 0xE45F, 0x81CD, 0xE460, 0x81D1, 0xE461,\n\t0x81D3, 0x919F, 0x81D8, 0xE463, 0x81D9, 0xE462, 0x81DA, 0xE465,\n\t0x81DF, 0xE466, 0x81E0, 0xE467, 0x81E3, 0x9062, 0x81E5, 0x89E7,\n\t0x81E7, 0xE468, 0x81E8, 0x97D5, 0x81EA, 0x8EA9, 0x81ED, 0x8F4C,\n\t0x81F3, 0x8E8A, 0x81F4, 0x9276, 0x81FA, 0xE469, 0x81FB, 0xE46A,\n\t0x81FC, 0x8950, 0x81FE, 0xE46B, 0x8201, 0xE46C, 0x8202, 0xE46D,\n\t0x8205, 0xE46E, 0x8207, 0xE46F, 0x8208, 0x8BBB, 0x8209, 0x9DA8,\n\t0x820A, 0xE470, 0x820C, 0x90E3, 0x820D, 0xE471, 0x820E, 0x8EC9,\n\t0x8210, 0xE472, 0x8212, 0x98AE, 0x8216, 0xE473, 0x8217, 0x95DC,\n\t0x8218, 0x8ADA, 0x821B, 0x9143, 0x821C, 0x8F77, 0x821E, 0x9591,\n\t0x821F, 0x8F4D, 0x8229, 0xE474, 0x822A, 0x8D71, 0x822B, 0xE475,\n\t0x822C, 0x94CA, 0x822E, 0xE484, 0x8233, 0xE477, 0x8235, 0x91C7,\n\t0x8236, 0x9495, 0x8237, 0x8CBD, 0x8238, 0xE476, 0x8239, 0x9144,\n\t0x8240, 0xE478, 0x8247, 0x92F8, 0x8258, 0xE47A, 0x8259, 0xE479,\n\t0x825A, 0xE47C, 0x825D, 0xE47B, 0x825F, 0xE47D, 0x8262, 0xE480,\n\t0x8264, 0xE47E, 0x8266, 0x8ACD, 0x8268, 0xE481, 0x826A, 0xE482,\n\t0x826B, 0xE483, 0x826E, 0x8DAF, 0x826F, 0x97C7, 0x8271, 0xE485,\n\t0x8272, 0x9046, 0x8276, 0x8990, 0x8277, 0xE486, 0x8278, 0xE487,\n\t0x827E, 0xE488, 0x828B, 0x88F0, 0x828D, 0xE489, 0x8292, 0xE48A,\n\t0x8299, 0x9587, 0x829D, 0x8EC5, 0x829F, 0xE48C, 0x82A5, 0x8A48,\n\t0x82A6, 0x88B0, 0x82AB, 0xE48B, 0x82AC, 0xE48E, 0x82AD, 0x946D,\n\t0x82AF, 0x9063, 0x82B1, 0x89D4, 0x82B3, 0x9646, 0x82B8, 0x8C7C,\n\t0x82B9, 0x8BDA, 0x82BB, 0xE48D, 0x82BD, 0x89E8, 0x82C5, 0x8AA1,\n\t0x82D1, 0x8991, 0x82D2, 0xE492, 0x82D3, 0x97E8, 0x82D4, 0x91DB,\n\t0x82D7, 0x9563, 0x82D9, 0xE49E, 0x82DB, 0x89D5, 0x82DC, 0xE49C,\n\t0x82DE, 0xE49A, 0x82DF, 0xE491, 0x82E1, 0xE48F, 0x82E3, 0xE490,\n\t0x82E5, 0x8EE1, 0x82E6, 0x8BEA, 0x82E7, 0x9297, 0x82EB, 0x93CF,\n\t0x82F1, 0x8970, 0x82F3, 0xE494, 0x82F4, 0xE493, 0x82F9, 0xE499,\n\t0x82FA, 0xE495, 0x82FB, 0xE498, 0x8301, 0xFB93, 0x8302, 0x96CE,\n\t0x8303, 0xE497, 0x8304, 0x89D6, 0x8305, 0x8A9D, 0x8306, 0xE49B,\n\t0x8309, 0xE49D, 0x830E, 0x8C73, 0x8316, 0xE4A1, 0x8317, 0xE4AA,\n\t0x8318, 0xE4AB, 0x831C, 0x88A9, 0x8323, 0xE4B2, 0x8328, 0x88EF,\n\t0x832B, 0xE4A9, 0x832F, 0xE4A8, 0x8331, 0xE4A3, 0x8332, 0xE4A2,\n\t0x8334, 0xE4A0, 0x8335, 0xE49F, 0x8336, 0x9283, 0x8338, 0x91F9,\n\t0x8339, 0xE4A5, 0x8340, 0xE4A4, 0x8345, 0xE4A7, 0x8349, 0x9190,\n\t0x834A, 0x8C74, 0x834F, 0x8960, 0x8350, 0xE4A6, 0x8352, 0x8D72,\n\t0x8358, 0x9191, 0x8362, 0xFB94, 0x8373, 0xE4B8, 0x8375, 0xE4B9,\n\t0x8377, 0x89D7, 0x837B, 0x89AC, 0x837C, 0xE4B6, 0x837F, 0xFB95,\n\t0x8385, 0xE4AC, 0x8387, 0xE4B4, 0x8389, 0xE4BB, 0x838A, 0xE4B5,\n\t0x838E, 0xE4B3, 0x8393, 0xE496, 0x8396, 0xE4B1, 0x839A, 0xE4AD,\n\t0x839E, 0x8ACE, 0x839F, 0xE4AF, 0x83A0, 0xE4BA, 0x83A2, 0xE4B0,\n\t0x83A8, 0xE4BC, 0x83AA, 0xE4AE, 0x83AB, 0x949C, 0x83B1, 0x9789,\n\t0x83B5, 0xE4B7, 0x83BD, 0xE4CD, 0x83C1, 0xE4C5, 0x83C5, 0x909B,\n\t0x83C7, 0xFB96, 0x83CA, 0x8B65, 0x83CC, 0x8BDB, 0x83CE, 0xE4C0,\n\t0x83D3, 0x89D9, 0x83D6, 0x8FD2, 0x83D8, 0xE4C3, 0x83DC, 0x8DD8,\n\t0x83DF, 0x9370, 0x83E0, 0xE4C8, 0x83E9, 0x95EC, 0x83EB, 0xE4BF,\n\t0x83EF, 0x89D8, 0x83F0, 0x8CD4, 0x83F1, 0x9548, 0x83F2, 0xE4C9,\n\t0x83F4, 0xE4BD, 0x83F6, 0xFB97, 0x83F7, 0xE4C6, 0x83FB, 0xE4D0,\n\t0x83FD, 0xE4C1, 0x8403, 0xE4C2, 0x8404, 0x93B8, 0x8407, 0xE4C7,\n\t0x840B, 0xE4C4, 0x840C, 0x9647, 0x840D, 0xE4CA, 0x840E, 0x88DE,\n\t0x8413, 0xE4BE, 0x8420, 0xE4CC, 0x8422, 0xE4CB, 0x8429, 0x948B,\n\t0x842A, 0xE4D2, 0x842C, 0xE4DD, 0x8431, 0x8A9E, 0x8435, 0xE4E0,\n\t0x8438, 0xE4CE, 0x843C, 0xE4D3, 0x843D, 0x978E, 0x8446, 0xE4DC,\n\t0x8448, 0xFB98, 0x8449, 0x9774, 0x844E, 0x97A8, 0x8457, 0x9298,\n\t0x845B, 0x8A8B, 0x8461, 0x9592, 0x8462, 0xE4E2, 0x8463, 0x939F,\n\t0x8466, 0x88AF, 0x8469, 0xE4DB, 0x846B, 0xE4D7, 0x846C, 0x9192,\n\t0x846D, 0xE4D1, 0x846E, 0xE4D9, 0x846F, 0xE4DE, 0x8471, 0x944B,\n\t0x8475, 0x88A8, 0x8477, 0xE4D6, 0x8479, 0xE4DF, 0x847A, 0x9598,\n\t0x8482, 0xE4DA, 0x8484, 0xE4D5, 0x848B, 0x8FD3, 0x8490, 0x8F4E,\n\t0x8494, 0x8EAA, 0x8499, 0x96D6, 0x849C, 0x9566, 0x849F, 0xE4E5,\n\t0x84A1, 0xE4EE, 0x84AD, 0xE4D8, 0x84B2, 0x8A97, 0x84B4, 0xFB99,\n\t0x84B8, 0x8FF6, 0x84B9, 0xE4E3, 0x84BB, 0xE4E8, 0x84BC, 0x9193,\n\t0x84BF, 0xE4E4, 0x84C1, 0xE4EB, 0x84C4, 0x927E, 0x84C6, 0xE4EC,\n\t0x84C9, 0x9775, 0x84CA, 0xE4E1, 0x84CB, 0x8A57, 0x84CD, 0xE4E7,\n\t0x84D0, 0xE4EA, 0x84D1, 0x96AA, 0x84D6, 0xE4ED, 0x84D9, 0xE4E6,\n\t0x84DA, 0xE4E9, 0x84DC, 0xFA60, 0x84EC, 0x9648, 0x84EE, 0x9840,\n\t0x84F4, 0xE4F1, 0x84FC, 0xE4F8, 0x84FF, 0xE4F0, 0x8500, 0x8EC1,\n\t0x8506, 0xE4CF, 0x8511, 0x95CC, 0x8513, 0x96A0, 0x8514, 0xE4F7,\n\t0x8515, 0xE4F6, 0x8517, 0xE4F2, 0x8518, 0xE4F3, 0x851A, 0x8955,\n\t0x851F, 0xE4F5, 0x8521, 0xE4EF, 0x8526, 0x92D3, 0x852C, 0xE4F4,\n\t0x852D, 0x88FC, 0x8535, 0x91A0, 0x853D, 0x95C1, 0x8540, 0xE4F9,\n\t0x8541, 0xE540, 0x8543, 0x94D7, 0x8548, 0xE4FC, 0x8549, 0x8FD4,\n\t0x854A, 0x8EC7, 0x854B, 0xE542, 0x854E, 0x8BBC, 0x8553, 0xFB9A,\n\t0x8555, 0xE543, 0x8557, 0x9599, 0x8558, 0xE4FB, 0x8559, 0xFB9B,\n\t0x855A, 0xE4D4, 0x8563, 0xE4FA, 0x8568, 0x986E, 0x8569, 0x93A0,\n\t0x856A, 0x9593, 0x856B, 0xFB9C, 0x856D, 0xE54A, 0x8577, 0xE550,\n\t0x857E, 0xE551, 0x8580, 0xE544, 0x8584, 0x9496, 0x8587, 0xE54E,\n\t0x8588, 0xE546, 0x858A, 0xE548, 0x8590, 0xE552, 0x8591, 0xE547,\n\t0x8594, 0xE54B, 0x8597, 0x8992, 0x8599, 0x93E3, 0x859B, 0xE54C,\n\t0x859C, 0xE54F, 0x85A4, 0xE545, 0x85A6, 0x9145, 0x85A8, 0xE549,\n\t0x85A9, 0x8E46, 0x85AA, 0x9064, 0x85AB, 0x8C4F, 0x85AC, 0x96F2,\n\t0x85AE, 0x96F7, 0x85AF, 0x8F92, 0x85B0, 0xFB9E, 0x85B9, 0xE556,\n\t0x85BA, 0xE554, 0x85C1, 0x986D, 0x85C9, 0xE553, 0x85CD, 0x9795,\n\t0x85CF, 0xE555, 0x85D0, 0xE557, 0x85D5, 0xE558, 0x85DC, 0xE55B,\n\t0x85DD, 0xE559, 0x85E4, 0x93A1, 0x85E5, 0xE55A, 0x85E9, 0x94CB,\n\t0x85EA, 0xE54D, 0x85F7, 0x8F93, 0x85F9, 0xE55C, 0x85FA, 0xE561,\n\t0x85FB, 0x9194, 0x85FE, 0xE560, 0x8602, 0xE541, 0x8606, 0xE562,\n\t0x8607, 0x9168, 0x860A, 0xE55D, 0x860B, 0xE55F, 0x8613, 0xE55E,\n\t0x8616, 0x9F50, 0x8617, 0x9F41, 0x861A, 0xE564, 0x8622, 0xE563,\n\t0x862D, 0x9796, 0x862F, 0xE1BA, 0x8630, 0xE565, 0x863F, 0xE566,\n\t0x864D, 0xE567, 0x864E, 0x8CD5, 0x8650, 0x8B73, 0x8654, 0xE569,\n\t0x8655, 0x997C, 0x865A, 0x8B95, 0x865C, 0x97B8, 0x865E, 0x8BF1,\n\t0x865F, 0xE56A, 0x8667, 0xE56B, 0x866B, 0x928E, 0x8671, 0xE56C,\n\t0x8679, 0x93F8, 0x867B, 0x88B8, 0x868A, 0x89E1, 0x868B, 0xE571,\n\t0x868C, 0xE572, 0x8693, 0xE56D, 0x8695, 0x8E5C, 0x86A3, 0xE56E,\n\t0x86A4, 0x9461, 0x86A9, 0xE56F, 0x86AA, 0xE570, 0x86AB, 0xE57A,\n\t0x86AF, 0xE574, 0x86B0, 0xE577, 0x86B6, 0xE573, 0x86C4, 0xE575,\n\t0x86C6, 0xE576, 0x86C7, 0x8ED6, 0x86C9, 0xE578, 0x86CB, 0x9260,\n\t0x86CD, 0x8C75, 0x86CE, 0x8A61, 0x86D4, 0xE57B, 0x86D9, 0x8A5E,\n\t0x86DB, 0xE581, 0x86DE, 0xE57C, 0x86DF, 0xE580, 0x86E4, 0x94B8,\n\t0x86E9, 0xE57D, 0x86EC, 0xE57E, 0x86ED, 0x9567, 0x86EE, 0x94D8,\n\t0x86EF, 0xE582, 0x86F8, 0x91FB, 0x86F9, 0xE58C, 0x86FB, 0xE588,\n\t0x86FE, 0x89E9, 0x8700, 0xE586, 0x8702, 0x9649, 0x8703, 0xE587,\n\t0x8706, 0xE584, 0x8708, 0xE585, 0x8709, 0xE58A, 0x870A, 0xE58D,\n\t0x870D, 0xE58B, 0x8711, 0xE589, 0x8712, 0xE583, 0x8718, 0x9277,\n\t0x871A, 0xE594, 0x871C, 0x96A8, 0x8725, 0xE592, 0x8729, 0xE593,\n\t0x8734, 0xE58E, 0x8737, 0xE590, 0x873B, 0xE591, 0x873F, 0xE58F,\n\t0x8749, 0x90E4, 0x874B, 0x9858, 0x874C, 0xE598, 0x874E, 0xE599,\n\t0x8753, 0xE59F, 0x8755, 0x9049, 0x8757, 0xE59B, 0x8759, 0xE59E,\n\t0x875F, 0xE596, 0x8760, 0xE595, 0x8763, 0xE5A0, 0x8766, 0x89DA,\n\t0x8768, 0xE59C, 0x876A, 0xE5A1, 0x876E, 0xE59D, 0x8774, 0xE59A,\n\t0x8776, 0x92B1, 0x8778, 0xE597, 0x877F, 0x9488, 0x8782, 0xE5A5,\n\t0x878D, 0x975A, 0x879F, 0xE5A4, 0x87A2, 0xE5A3, 0x87AB, 0xE5AC,\n\t0x87AF, 0xE5A6, 0x87B3, 0xE5AE, 0x87BA, 0x9786, 0x87BB, 0xE5B1,\n\t0x87BD, 0xE5A8, 0x87C0, 0xE5A9, 0x87C4, 0xE5AD, 0x87C6, 0xE5B0,\n\t0x87C7, 0xE5AF, 0x87CB, 0xE5A7, 0x87D0, 0xE5AA, 0x87D2, 0xE5BB,\n\t0x87E0, 0xE5B4, 0x87EF, 0xE5B2, 0x87F2, 0xE5B3, 0x87F6, 0xE5B8,\n\t0x87F7, 0xE5B9, 0x87F9, 0x8A49, 0x87FB, 0x8B61, 0x87FE, 0xE5B7,\n\t0x8805, 0xE5A2, 0x8807, 0xFBA1, 0x880D, 0xE5B6, 0x880E, 0xE5BA,\n\t0x880F, 0xE5B5, 0x8811, 0xE5BC, 0x8815, 0xE5BE, 0x8816, 0xE5BD,\n\t0x8821, 0xE5C0, 0x8822, 0xE5BF, 0x8823, 0xE579, 0x8827, 0xE5C4,\n\t0x8831, 0xE5C1, 0x8836, 0xE5C2, 0x8839, 0xE5C3, 0x883B, 0xE5C5,\n\t0x8840, 0x8C8C, 0x8842, 0xE5C7, 0x8844, 0xE5C6, 0x8846, 0x8F4F,\n\t0x884C, 0x8D73, 0x884D, 0x9FA5, 0x8852, 0xE5C8, 0x8853, 0x8F70,\n\t0x8857, 0x8A58, 0x8859, 0xE5C9, 0x885B, 0x8971, 0x885D, 0x8FD5,\n\t0x885E, 0xE5CA, 0x8861, 0x8D74, 0x8862, 0xE5CB, 0x8863, 0x88DF,\n\t0x8868, 0x955C, 0x886B, 0xE5CC, 0x8870, 0x908A, 0x8872, 0xE5D3,\n\t0x8875, 0xE5D0, 0x8877, 0x928F, 0x887D, 0xE5D1, 0x887E, 0xE5CE,\n\t0x887F, 0x8BDC, 0x8881, 0xE5CD, 0x8882, 0xE5D4, 0x8888, 0x8C55,\n\t0x888B, 0x91DC, 0x888D, 0xE5DA, 0x8892, 0xE5D6, 0x8896, 0x91B3,\n\t0x8897, 0xE5D5, 0x8899, 0xE5D8, 0x889E, 0xE5CF, 0x88A2, 0xE5D9,\n\t0x88A4, 0xE5DB, 0x88AB, 0x94ED, 0x88AE, 0xE5D7, 0x88B0, 0xE5DC,\n\t0x88B1, 0xE5DE, 0x88B4, 0x8CD1, 0x88B5, 0xE5D2, 0x88B7, 0x88BF,\n\t0x88BF, 0xE5DD, 0x88C1, 0x8DD9, 0x88C2, 0x97F4, 0x88C3, 0xE5DF,\n\t0x88C4, 0xE5E0, 0x88C5, 0x9195, 0x88CF, 0x97A0, 0x88D4, 0xE5E1,\n\t0x88D5, 0x9754, 0x88D8, 0xE5E2, 0x88D9, 0xE5E3, 0x88DC, 0x95E2,\n\t0x88DD, 0xE5E4, 0x88DF, 0x8DBE, 0x88E1, 0x97A1, 0x88E8, 0xE5E9,\n\t0x88F2, 0xE5EA, 0x88F3, 0x8FD6, 0x88F4, 0xE5E8, 0x88F5, 0xFBA2,\n\t0x88F8, 0x9787, 0x88F9, 0xE5E5, 0x88FC, 0xE5E7, 0x88FD, 0x90BB,\n\t0x88FE, 0x909E, 0x8902, 0xE5E6, 0x8904, 0xE5EB, 0x8907, 0x95A1,\n\t0x890A, 0xE5ED, 0x890C, 0xE5EC, 0x8910, 0x8A8C, 0x8912, 0x964A,\n\t0x8913, 0xE5EE, 0x891C, 0xFA5D, 0x891D, 0xE5FA, 0x891E, 0xE5F0,\n\t0x8925, 0xE5F1, 0x892A, 0xE5F2, 0x892B, 0xE5F3, 0x8936, 0xE5F7,\n\t0x8938, 0xE5F8, 0x893B, 0xE5F6, 0x8941, 0xE5F4, 0x8943, 0xE5EF,\n\t0x8944, 0xE5F5, 0x894C, 0xE5F9, 0x894D, 0xE8B5, 0x8956, 0x89A6,\n\t0x895E, 0xE5FC, 0x895F, 0x8BDD, 0x8960, 0xE5FB, 0x8964, 0xE641,\n\t0x8966, 0xE640, 0x896A, 0xE643, 0x896D, 0xE642, 0x896F, 0xE644,\n\t0x8972, 0x8F50, 0x8974, 0xE645, 0x8977, 0xE646, 0x897E, 0xE647,\n\t0x897F, 0x90BC, 0x8981, 0x9776, 0x8983, 0xE648, 0x8986, 0x95A2,\n\t0x8987, 0x9465, 0x8988, 0xE649, 0x898A, 0xE64A, 0x898B, 0x8CA9,\n\t0x898F, 0x8B4B, 0x8993, 0xE64B, 0x8996, 0x8E8B, 0x8997, 0x9460,\n\t0x8998, 0xE64C, 0x899A, 0x8A6F, 0x89A1, 0xE64D, 0x89A6, 0xE64F,\n\t0x89A7, 0x9797, 0x89A9, 0xE64E, 0x89AA, 0x9065, 0x89AC, 0xE650,\n\t0x89AF, 0xE651, 0x89B2, 0xE652, 0x89B3, 0x8ACF, 0x89BA, 0xE653,\n\t0x89BD, 0xE654, 0x89BF, 0xE655, 0x89C0, 0xE656, 0x89D2, 0x8A70,\n\t0x89DA, 0xE657, 0x89DC, 0xE658, 0x89DD, 0xE659, 0x89E3, 0x89F0,\n\t0x89E6, 0x9047, 0x89E7, 0xE65A, 0x89F4, 0xE65B, 0x89F8, 0xE65C,\n\t0x8A00, 0x8CBE, 0x8A02, 0x92F9, 0x8A03, 0xE65D, 0x8A08, 0x8C76,\n\t0x8A0A, 0x9075, 0x8A0C, 0xE660, 0x8A0E, 0x93A2, 0x8A10, 0xE65F,\n\t0x8A12, 0xFBA3, 0x8A13, 0x8C50, 0x8A16, 0xE65E, 0x8A17, 0x91F5,\n\t0x8A18, 0x8B4C, 0x8A1B, 0xE661, 0x8A1D, 0xE662, 0x8A1F, 0x8FD7,\n\t0x8A23, 0x8C8D, 0x8A25, 0xE663, 0x8A2A, 0x964B, 0x8A2D, 0x90DD,\n\t0x8A31, 0x8B96, 0x8A33, 0x96F3, 0x8A34, 0x9169, 0x8A36, 0xE664,\n\t0x8A37, 0xFBA4, 0x8A3A, 0x9066, 0x8A3B, 0x9290, 0x8A3C, 0x8FD8,\n\t0x8A41, 0xE665, 0x8A46, 0xE668, 0x8A48, 0xE669, 0x8A50, 0x8DBC,\n\t0x8A51, 0x91C0, 0x8A52, 0xE667, 0x8A54, 0x8FD9, 0x8A55, 0x955D,\n\t0x8A5B, 0xE666, 0x8A5E, 0x8E8C, 0x8A60, 0x8972, 0x8A62, 0xE66D,\n\t0x8A63, 0x8C77, 0x8A66, 0x8E8E, 0x8A69, 0x8E8D, 0x8A6B, 0x986C,\n\t0x8A6C, 0xE66C, 0x8A6D, 0xE66B, 0x8A6E, 0x9146, 0x8A70, 0x8B6C,\n\t0x8A71, 0x9862, 0x8A72, 0x8A59, 0x8A73, 0x8FDA, 0x8A79, 0xFBA5,\n\t0x8A7C, 0xE66A, 0x8A82, 0xE66F, 0x8A84, 0xE670, 0x8A85, 0xE66E,\n\t0x8A87, 0x8CD6, 0x8A89, 0x975F, 0x8A8C, 0x8E8F, 0x8A8D, 0x9446,\n\t0x8A91, 0xE673, 0x8A93, 0x90BE, 0x8A95, 0x9261, 0x8A98, 0x9755,\n\t0x8A9A, 0xE676, 0x8A9E, 0x8CEA, 0x8AA0, 0x90BD, 0x8AA1, 0xE672,\n\t0x8AA3, 0xE677, 0x8AA4, 0x8CEB, 0x8AA5, 0xE674, 0x8AA6, 0xE675,\n\t0x8AA7, 0xFBA6, 0x8AA8, 0xE671, 0x8AAC, 0x90E0, 0x8AAD, 0x93C7,\n\t0x8AB0, 0x924E, 0x8AB2, 0x89DB, 0x8AB9, 0x94EE, 0x8ABC, 0x8B62,\n\t0x8ABE, 0xFBA7, 0x8ABF, 0x92B2, 0x8AC2, 0xE67A, 0x8AC4, 0xE678,\n\t0x8AC7, 0x926B, 0x8ACB, 0x90BF, 0x8ACC, 0x8AD0, 0x8ACD, 0xE679,\n\t0x8ACF, 0x907A, 0x8AD2, 0x97C8, 0x8AD6, 0x985F, 0x8ADA, 0xE67B,\n\t0x8ADB, 0xE687, 0x8ADC, 0x92B3, 0x8ADE, 0xE686, 0x8ADF, 0xFBA8,\n\t0x8AE0, 0xE683, 0x8AE1, 0xE68B, 0x8AE2, 0xE684, 0x8AE4, 0xE680,\n\t0x8AE6, 0x92FA, 0x8AE7, 0xE67E, 0x8AEB, 0xE67C, 0x8AED, 0x9740,\n\t0x8AEE, 0x8E90, 0x8AF1, 0xE681, 0x8AF3, 0xE67D, 0x8AF6, 0xFBAA,\n\t0x8AF7, 0xE685, 0x8AF8, 0x8F94, 0x8AFA, 0x8CBF, 0x8AFE, 0x91F8,\n\t0x8B00, 0x9664, 0x8B01, 0x8979, 0x8B02, 0x88E0, 0x8B04, 0x93A3,\n\t0x8B07, 0xE689, 0x8B0C, 0xE688, 0x8B0E, 0x93E4, 0x8B10, 0xE68D,\n\t0x8B14, 0xE682, 0x8B16, 0xE68C, 0x8B17, 0xE68E, 0x8B19, 0x8CAA,\n\t0x8B1A, 0xE68A, 0x8B1B, 0x8D75, 0x8B1D, 0x8ED3, 0x8B20, 0xE68F,\n\t0x8B21, 0x9777, 0x8B26, 0xE692, 0x8B28, 0xE695, 0x8B2B, 0xE693,\n\t0x8B2C, 0x9554, 0x8B33, 0xE690, 0x8B39, 0x8BDE, 0x8B3E, 0xE694,\n\t0x8B41, 0xE696, 0x8B49, 0xE69A, 0x8B4C, 0xE697, 0x8B4E, 0xE699,\n\t0x8B4F, 0xE698, 0x8B53, 0xFBAB, 0x8B56, 0xE69B, 0x8B58, 0x8EAF,\n\t0x8B5A, 0xE69D, 0x8B5B, 0xE69C, 0x8B5C, 0x9588, 0x8B5F, 0xE69F,\n\t0x8B66, 0x8C78, 0x8B6B, 0xE69E, 0x8B6C, 0xE6A0, 0x8B6F, 0xE6A1,\n\t0x8B70, 0x8B63, 0x8B71, 0xE3BF, 0x8B72, 0x8FF7, 0x8B74, 0xE6A2,\n\t0x8B77, 0x8CEC, 0x8B7D, 0xE6A3, 0x8B7F, 0xFBAC, 0x8B80, 0xE6A4,\n\t0x8B83, 0x8E5D, 0x8B8A, 0x9DCC, 0x8B8C, 0xE6A5, 0x8B8E, 0xE6A6,\n\t0x8B90, 0x8F51, 0x8B92, 0xE6A7, 0x8B93, 0xE6A8, 0x8B96, 0xE6A9,\n\t0x8B99, 0xE6AA, 0x8B9A, 0xE6AB, 0x8C37, 0x924A, 0x8C3A, 0xE6AC,\n\t0x8C3F, 0xE6AE, 0x8C41, 0xE6AD, 0x8C46, 0x93A4, 0x8C48, 0xE6AF,\n\t0x8C4A, 0x964C, 0x8C4C, 0xE6B0, 0x8C4E, 0xE6B1, 0x8C50, 0xE6B2,\n\t0x8C55, 0xE6B3, 0x8C5A, 0x93D8, 0x8C61, 0x8FDB, 0x8C62, 0xE6B4,\n\t0x8C6A, 0x8D8B, 0x8C6B, 0x98AC, 0x8C6C, 0xE6B5, 0x8C78, 0xE6B6,\n\t0x8C79, 0x955E, 0x8C7A, 0xE6B7, 0x8C7C, 0xE6BF, 0x8C82, 0xE6B8,\n\t0x8C85, 0xE6BA, 0x8C89, 0xE6B9, 0x8C8A, 0xE6BB, 0x8C8C, 0x9665,\n\t0x8C8D, 0xE6BC, 0x8C8E, 0xE6BD, 0x8C94, 0xE6BE, 0x8C98, 0xE6C0,\n\t0x8C9D, 0x8A4C, 0x8C9E, 0x92E5, 0x8CA0, 0x9589, 0x8CA1, 0x8DE0,\n\t0x8CA2, 0x8D76, 0x8CA7, 0x956E, 0x8CA8, 0x89DD, 0x8CA9, 0x94CC,\n\t0x8CAA, 0xE6C3, 0x8CAB, 0x8AD1, 0x8CAC, 0x90D3, 0x8CAD, 0xE6C2,\n\t0x8CAE, 0xE6C7, 0x8CAF, 0x9299, 0x8CB0, 0x96E1, 0x8CB2, 0xE6C5,\n\t0x8CB3, 0xE6C6, 0x8CB4, 0x8B4D, 0x8CB6, 0xE6C8, 0x8CB7, 0x9483,\n\t0x8CB8, 0x91DD, 0x8CBB, 0x94EF, 0x8CBC, 0x935C, 0x8CBD, 0xE6C4,\n\t0x8CBF, 0x9666, 0x8CC0, 0x89EA, 0x8CC1, 0xE6CA, 0x8CC2, 0x9847,\n\t0x8CC3, 0x92C0, 0x8CC4, 0x9864, 0x8CC7, 0x8E91, 0x8CC8, 0xE6C9,\n\t0x8CCA, 0x91AF, 0x8CCD, 0xE6DA, 0x8CCE, 0x9147, 0x8CD1, 0x93F6,\n\t0x8CD3, 0x956F, 0x8CDA, 0xE6CD, 0x8CDB, 0x8E5E, 0x8CDC, 0x8E92,\n\t0x8CDE, 0x8FDC, 0x8CE0, 0x9485, 0x8CE2, 0x8CAB, 0x8CE3, 0xE6CC,\n\t0x8CE4, 0xE6CB, 0x8CE6, 0x958A, 0x8CEA, 0x8EBF, 0x8CED, 0x9371,\n\t0x8CF0, 0xFBAD, 0x8CF4, 0xFBAE, 0x8CFA, 0xE6CF, 0x8CFB, 0xE6D0,\n\t0x8CFC, 0x8D77, 0x8CFD, 0xE6CE, 0x8D04, 0xE6D1, 0x8D05, 0xE6D2,\n\t0x8D07, 0xE6D4, 0x8D08, 0x91A1, 0x8D0A, 0xE6D3, 0x8D0B, 0x8AE4,\n\t0x8D0D, 0xE6D6, 0x8D0F, 0xE6D5, 0x8D10, 0xE6D7, 0x8D12, 0xFBAF,\n\t0x8D13, 0xE6D9, 0x8D14, 0xE6DB, 0x8D16, 0xE6DC, 0x8D64, 0x90D4,\n\t0x8D66, 0x8ECD, 0x8D67, 0xE6DD, 0x8D6B, 0x8A71, 0x8D6D, 0xE6DE,\n\t0x8D70, 0x9196, 0x8D71, 0xE6DF, 0x8D73, 0xE6E0, 0x8D74, 0x958B,\n\t0x8D76, 0xFBB0, 0x8D77, 0x8B4E, 0x8D81, 0xE6E1, 0x8D85, 0x92B4,\n\t0x8D8A, 0x897A, 0x8D99, 0xE6E2, 0x8DA3, 0x8EEF, 0x8DA8, 0x9096,\n\t0x8DB3, 0x91AB, 0x8DBA, 0xE6E5, 0x8DBE, 0xE6E4, 0x8DC2, 0xE6E3,\n\t0x8DCB, 0xE6EB, 0x8DCC, 0xE6E9, 0x8DCF, 0xE6E6, 0x8DD6, 0xE6E8,\n\t0x8DDA, 0xE6E7, 0x8DDB, 0xE6EA, 0x8DDD, 0x8B97, 0x8DDF, 0xE6EE,\n\t0x8DE1, 0x90D5, 0x8DE3, 0xE6EF, 0x8DE8, 0x8CD7, 0x8DEA, 0xE6EC,\n\t0x8DEB, 0xE6ED, 0x8DEF, 0x9848, 0x8DF3, 0x92B5, 0x8DF5, 0x9148,\n\t0x8DFC, 0xE6F0, 0x8DFF, 0xE6F3, 0x8E08, 0xE6F1, 0x8E09, 0xE6F2,\n\t0x8E0A, 0x9778, 0x8E0F, 0x93A5, 0x8E10, 0xE6F6, 0x8E1D, 0xE6F4,\n\t0x8E1E, 0xE6F5, 0x8E1F, 0xE6F7, 0x8E2A, 0xE748, 0x8E30, 0xE6FA,\n\t0x8E34, 0xE6FB, 0x8E35, 0xE6F9, 0x8E42, 0xE6F8, 0x8E44, 0x92FB,\n\t0x8E47, 0xE740, 0x8E48, 0xE744, 0x8E49, 0xE741, 0x8E4A, 0xE6FC,\n\t0x8E4C, 0xE742, 0x8E50, 0xE743, 0x8E55, 0xE74A, 0x8E59, 0xE745,\n\t0x8E5F, 0x90D6, 0x8E60, 0xE747, 0x8E63, 0xE749, 0x8E64, 0xE746,\n\t0x8E72, 0xE74C, 0x8E74, 0x8F52, 0x8E76, 0xE74B, 0x8E7C, 0xE74D,\n\t0x8E81, 0xE74E, 0x8E84, 0xE751, 0x8E85, 0xE750, 0x8E87, 0xE74F,\n\t0x8E8A, 0xE753, 0x8E8B, 0xE752, 0x8E8D, 0x96F4, 0x8E91, 0xE755,\n\t0x8E93, 0xE754, 0x8E94, 0xE756, 0x8E99, 0xE757, 0x8EA1, 0xE759,\n\t0x8EAA, 0xE758, 0x8EAB, 0x9067, 0x8EAC, 0xE75A, 0x8EAF, 0x8BEB,\n\t0x8EB0, 0xE75B, 0x8EB1, 0xE75D, 0x8EBE, 0xE75E, 0x8EC5, 0xE75F,\n\t0x8EC6, 0xE75C, 0x8EC8, 0xE760, 0x8ECA, 0x8ED4, 0x8ECB, 0xE761,\n\t0x8ECC, 0x8B4F, 0x8ECD, 0x8C52, 0x8ECF, 0xFBB2, 0x8ED2, 0x8CAC,\n\t0x8EDB, 0xE762, 0x8EDF, 0x93EE, 0x8EE2, 0x935D, 0x8EE3, 0xE763,\n\t0x8EEB, 0xE766, 0x8EF8, 0x8EB2, 0x8EFB, 0xE765, 0x8EFC, 0xE764,\n\t0x8EFD, 0x8C79, 0x8EFE, 0xE767, 0x8F03, 0x8A72, 0x8F05, 0xE769,\n\t0x8F09, 0x8DDA, 0x8F0A, 0xE768, 0x8F0C, 0xE771, 0x8F12, 0xE76B,\n\t0x8F13, 0xE76D, 0x8F14, 0x95E3, 0x8F15, 0xE76A, 0x8F19, 0xE76C,\n\t0x8F1B, 0xE770, 0x8F1C, 0xE76E, 0x8F1D, 0x8B50, 0x8F1F, 0xE76F,\n\t0x8F26, 0xE772, 0x8F29, 0x9479, 0x8F2A, 0x97D6, 0x8F2F, 0x8F53,\n\t0x8F33, 0xE773, 0x8F38, 0x9741, 0x8F39, 0xE775, 0x8F3B, 0xE774,\n\t0x8F3E, 0xE778, 0x8F3F, 0x9760, 0x8F42, 0xE777, 0x8F44, 0x8A8D,\n\t0x8F45, 0xE776, 0x8F46, 0xE77B, 0x8F49, 0xE77A, 0x8F4C, 0xE779,\n\t0x8F4D, 0x9351, 0x8F4E, 0xE77C, 0x8F57, 0xE77D, 0x8F5C, 0xE77E,\n\t0x8F5F, 0x8D8C, 0x8F61, 0x8C44, 0x8F62, 0xE780, 0x8F63, 0xE781,\n\t0x8F64, 0xE782, 0x8F9B, 0x9068, 0x8F9C, 0xE783, 0x8F9E, 0x8EAB,\n\t0x8F9F, 0xE784, 0x8FA3, 0xE785, 0x8FA7, 0x999F, 0x8FA8, 0x999E,\n\t0x8FAD, 0xE786, 0x8FAE, 0xE390, 0x8FAF, 0xE787, 0x8FB0, 0x9243,\n\t0x8FB1, 0x904A, 0x8FB2, 0x945F, 0x8FB7, 0xE788, 0x8FBA, 0x95D3,\n\t0x8FBB, 0x92D2, 0x8FBC, 0x8D9E, 0x8FBF, 0x9248, 0x8FC2, 0x8949,\n\t0x8FC4, 0x9698, 0x8FC5, 0x9076, 0x8FCE, 0x8C7D, 0x8FD1, 0x8BDF,\n\t0x8FD4, 0x95D4, 0x8FDA, 0xE789, 0x8FE2, 0xE78B, 0x8FE5, 0xE78A,\n\t0x8FE6, 0x89DE, 0x8FE9, 0x93F4, 0x8FEA, 0xE78C, 0x8FEB, 0x9497,\n\t0x8FED, 0x9352, 0x8FEF, 0xE78D, 0x8FF0, 0x8F71, 0x8FF4, 0xE78F,\n\t0x8FF7, 0x96C0, 0x8FF8, 0xE79E, 0x8FF9, 0xE791, 0x8FFA, 0xE792,\n\t0x8FFD, 0x92C7, 0x9000, 0x91DE, 0x9001, 0x9197, 0x9003, 0x93A6,\n\t0x9005, 0xE790, 0x9006, 0x8B74, 0x900B, 0xE799, 0x900D, 0xE796,\n\t0x900E, 0xE7A3, 0x900F, 0x93A7, 0x9010, 0x9280, 0x9011, 0xE793,\n\t0x9013, 0x92FC, 0x9014, 0x9372, 0x9015, 0xE794, 0x9016, 0xE798,\n\t0x9017, 0x9080, 0x9019, 0x9487, 0x901A, 0x92CA, 0x901D, 0x90C0,\n\t0x901E, 0xE797, 0x901F, 0x91AC, 0x9020, 0x91A2, 0x9021, 0xE795,\n\t0x9022, 0x88A7, 0x9023, 0x9841, 0x9027, 0xE79A, 0x902E, 0x91DF,\n\t0x9031, 0x8F54, 0x9032, 0x9069, 0x9035, 0xE79C, 0x9036, 0xE79B,\n\t0x9038, 0x88ED, 0x9039, 0xE79D, 0x903C, 0x954E, 0x903E, 0xE7A5,\n\t0x9041, 0x93D9, 0x9042, 0x908B, 0x9045, 0x9278, 0x9047, 0x8BF6,\n\t0x9049, 0xE7A4, 0x904A, 0x9756, 0x904B, 0x895E, 0x904D, 0x95D5,\n\t0x904E, 0x89DF, 0x904F, 0xE79F, 0x9050, 0xE7A0, 0x9051, 0xE7A1,\n\t0x9052, 0xE7A2, 0x9053, 0x93B9, 0x9054, 0x9242, 0x9055, 0x88E1,\n\t0x9056, 0xE7A6, 0x9058, 0xE7A7, 0x9059, 0xEAA1, 0x905C, 0x91BB,\n\t0x905E, 0xE7A8, 0x9060, 0x8993, 0x9061, 0x916B, 0x9063, 0x8CAD,\n\t0x9065, 0x9779, 0x9067, 0xFBB5, 0x9068, 0xE7A9, 0x9069, 0x934B,\n\t0x906D, 0x9198, 0x906E, 0x8ED5, 0x906F, 0xE7AA, 0x9072, 0xE7AD,\n\t0x9075, 0x8F85, 0x9076, 0xE7AB, 0x9077, 0x914A, 0x9078, 0x9149,\n\t0x907A, 0x88E2, 0x907C, 0x97C9, 0x907D, 0xE7AF, 0x907F, 0x94F0,\n\t0x9080, 0xE7B1, 0x9081, 0xE7B0, 0x9082, 0xE7AE, 0x9083, 0xE284,\n\t0x9084, 0x8AD2, 0x9087, 0xE78E, 0x9089, 0xE7B3, 0x908A, 0xE7B2,\n\t0x908F, 0xE7B4, 0x9091, 0x9757, 0x90A3, 0x93DF, 0x90A6, 0x964D,\n\t0x90A8, 0xE7B5, 0x90AA, 0x8ED7, 0x90AF, 0xE7B6, 0x90B1, 0xE7B7,\n\t0x90B5, 0xE7B8, 0x90B8, 0x9340, 0x90C1, 0x88E8, 0x90CA, 0x8D78,\n\t0x90CE, 0x9859, 0x90DB, 0xE7BC, 0x90DE, 0xFBB6, 0x90E1, 0x8C53,\n\t0x90E2, 0xE7B9, 0x90E4, 0xE7BA, 0x90E8, 0x9594, 0x90ED, 0x8A73,\n\t0x90F5, 0x9758, 0x90F7, 0x8BBD, 0x90FD, 0x9373, 0x9102, 0xE7BD,\n\t0x9112, 0xE7BE, 0x9115, 0xFBB8, 0x9119, 0xE7BF, 0x9127, 0xFBB9,\n\t0x912D, 0x9341, 0x9130, 0xE7C1, 0x9132, 0xE7C0, 0x9149, 0x93D1,\n\t0x914A, 0xE7C2, 0x914B, 0x8F55, 0x914C, 0x8EDE, 0x914D, 0x947A,\n\t0x914E, 0x9291, 0x9152, 0x8EF0, 0x9154, 0x908C, 0x9156, 0xE7C3,\n\t0x9158, 0xE7C4, 0x9162, 0x907C, 0x9163, 0xE7C5, 0x9165, 0xE7C6,\n\t0x9169, 0xE7C7, 0x916A, 0x978F, 0x916C, 0x8F56, 0x9172, 0xE7C9,\n\t0x9173, 0xE7C8, 0x9175, 0x8D79, 0x9177, 0x8D93, 0x9178, 0x8E5F,\n\t0x9182, 0xE7CC, 0x9187, 0x8F86, 0x9189, 0xE7CB, 0x918B, 0xE7CA,\n\t0x918D, 0x91E7, 0x9190, 0x8CED, 0x9192, 0x90C1, 0x9197, 0x94AE,\n\t0x919C, 0x8F58, 0x91A2, 0xE7CD, 0x91A4, 0x8FDD, 0x91AA, 0xE7D0,\n\t0x91AB, 0xE7CE, 0x91AF, 0xE7CF, 0x91B4, 0xE7D2, 0x91B5, 0xE7D1,\n\t0x91B8, 0x8FF8, 0x91BA, 0xE7D3, 0x91C0, 0xE7D4, 0x91C1, 0xE7D5,\n\t0x91C6, 0x94CE, 0x91C7, 0x8DD1, 0x91C8, 0x8EDF, 0x91C9, 0xE7D6,\n\t0x91CB, 0xE7D7, 0x91CC, 0x97A2, 0x91CD, 0x8F64, 0x91CE, 0x96EC,\n\t0x91CF, 0x97CA, 0x91D0, 0xE7D8, 0x91D1, 0x8BE0, 0x91D6, 0xE7D9,\n\t0x91D7, 0xFBBB, 0x91D8, 0x9342, 0x91DA, 0xFBBA, 0x91DB, 0xE7DC,\n\t0x91DC, 0x8A98, 0x91DD, 0x906A, 0x91DE, 0xFBBC, 0x91DF, 0xE7DA,\n\t0x91E1, 0xE7DB, 0x91E3, 0x92DE, 0x91E4, 0xFBBF, 0x91E5, 0xFBC0,\n\t0x91E6, 0x9674, 0x91E7, 0x8BFA, 0x91ED, 0xFBBD, 0x91EE, 0xFBBE,\n\t0x91F5, 0xE7DE, 0x91F6, 0xE7DF, 0x91FC, 0xE7DD, 0x91FF, 0xE7E1,\n\t0x9206, 0xFBC1, 0x920A, 0xFBC3, 0x920D, 0x93DD, 0x920E, 0x8A62,\n\t0x9210, 0xFBC2, 0x9211, 0xE7E5, 0x9214, 0xE7E2, 0x9215, 0xE7E4,\n\t0x921E, 0xE7E0, 0x9229, 0xE86E, 0x922C, 0xE7E3, 0x9234, 0x97E9,\n\t0x9237, 0x8CD8, 0x9239, 0xFBCA, 0x923A, 0xFBC4, 0x923C, 0xFBC6,\n\t0x923F, 0xE7ED, 0x9240, 0xFBC5, 0x9244, 0x9353, 0x9245, 0xE7E8,\n\t0x9248, 0xE7EB, 0x9249, 0xE7E9, 0x924B, 0xE7EE, 0x924E, 0xFBC7,\n\t0x9250, 0xE7EF, 0x9251, 0xFBC9, 0x9257, 0xE7E7, 0x9259, 0xFBC8,\n\t0x925A, 0xE7F4, 0x925B, 0x8994, 0x925E, 0xE7E6, 0x9262, 0x94AB,\n\t0x9264, 0xE7EA, 0x9266, 0x8FDE, 0x9267, 0xFBCB, 0x9271, 0x8D7A,\n\t0x9277, 0xFBCD, 0x9278, 0xFBCE, 0x927E, 0x9667, 0x9280, 0x8BE2,\n\t0x9283, 0x8F65, 0x9285, 0x93BA, 0x9288, 0xFA5F, 0x9291, 0x914C,\n\t0x9293, 0xE7F2, 0x9295, 0xE7EC, 0x9296, 0xE7F1, 0x9298, 0x96C1,\n\t0x929A, 0x92B6, 0x929B, 0xE7F3, 0x929C, 0xE7F0, 0x92A7, 0xFBCC,\n\t0x92AD, 0x914B, 0x92B7, 0xE7F7, 0x92B9, 0xE7F6, 0x92CF, 0xE7F5,\n\t0x92D0, 0xFBD2, 0x92D2, 0x964E, 0x92D3, 0xFBD6, 0x92D5, 0xFBD4,\n\t0x92D7, 0xFBD0, 0x92D9, 0xFBD1, 0x92E0, 0xFBD5, 0x92E4, 0x8F9B,\n\t0x92E7, 0xFBCF, 0x92E9, 0xE7F8, 0x92EA, 0x95DD, 0x92ED, 0x8973,\n\t0x92F2, 0x9565, 0x92F3, 0x9292, 0x92F8, 0x8B98, 0x92F9, 0xFA65,\n\t0x92FA, 0xE7FA, 0x92FB, 0xFBD9, 0x92FC, 0x8D7C, 0x92FF, 0xFBDC,\n\t0x9302, 0xFBDE, 0x9306, 0x8E4B, 0x930F, 0xE7F9, 0x9310, 0x908D,\n\t0x9318, 0x908E, 0x9319, 0xE840, 0x931A, 0xE842, 0x931D, 0xFBDD,\n\t0x931E, 0xFBDB, 0x9320, 0x8FF9, 0x9321, 0xFBD8, 0x9322, 0xE841,\n\t0x9323, 0xE843, 0x9325, 0xFBD7, 0x9326, 0x8BD1, 0x9328, 0x9564,\n\t0x932B, 0x8EE0, 0x932C, 0x9842, 0x932E, 0xE7FC, 0x932F, 0x8DF6,\n\t0x9332, 0x985E, 0x9335, 0xE845, 0x933A, 0xE844, 0x933B, 0xE846,\n\t0x9344, 0xE7FB, 0x9348, 0xFA5E, 0x934B, 0x93E7, 0x934D, 0x9374,\n\t0x9354, 0x92D5, 0x9356, 0xE84B, 0x9357, 0xFBE0, 0x935B, 0x9262,\n\t0x935C, 0xE847, 0x9360, 0xE848, 0x936C, 0x8C4C, 0x936E, 0xE84A,\n\t0x9370, 0xFBDF, 0x9375, 0x8CAE, 0x937C, 0xE849, 0x937E, 0x8FDF,\n\t0x938C, 0x8A99, 0x9394, 0xE84F, 0x9396, 0x8DBD, 0x9397, 0x9199,\n\t0x939A, 0x92C8, 0x93A4, 0xFBE1, 0x93A7, 0x8A5A, 0x93AC, 0xE84D,\n\t0x93AD, 0xE84E, 0x93AE, 0x92C1, 0x93B0, 0xE84C, 0x93B9, 0xE850,\n\t0x93C3, 0xE856, 0x93C6, 0xFBE2, 0x93C8, 0xE859, 0x93D0, 0xE858,\n\t0x93D1, 0x934C, 0x93D6, 0xE851, 0x93D7, 0xE852, 0x93D8, 0xE855,\n\t0x93DD, 0xE857, 0x93DE, 0xFBE3, 0x93E1, 0x8BBE, 0x93E4, 0xE85A,\n\t0x93E5, 0xE854, 0x93E8, 0xE853, 0x93F8, 0xFBE4, 0x9403, 0xE85E,\n\t0x9407, 0xE85F, 0x9410, 0xE860, 0x9413, 0xE85D, 0x9414, 0xE85C,\n\t0x9418, 0x8FE0, 0x9419, 0x93A8, 0x941A, 0xE85B, 0x9421, 0xE864,\n\t0x942B, 0xE862, 0x9431, 0xFBE5, 0x9435, 0xE863, 0x9436, 0xE861,\n\t0x9438, 0x91F6, 0x943A, 0xE865, 0x9441, 0xE866, 0x9444, 0xE868,\n\t0x9445, 0xFBE6, 0x9448, 0xFBE7, 0x9451, 0x8AD3, 0x9452, 0xE867,\n\t0x9453, 0x96F8, 0x945A, 0xE873, 0x945B, 0xE869, 0x945E, 0xE86C,\n\t0x9460, 0xE86A, 0x9462, 0xE86B, 0x946A, 0xE86D, 0x9470, 0xE86F,\n\t0x9475, 0xE870, 0x9477, 0xE871, 0x947C, 0xE874, 0x947D, 0xE872,\n\t0x947E, 0xE875, 0x947F, 0xE877, 0x9481, 0xE876, 0x9577, 0x92B7,\n\t0x9580, 0x96E5, 0x9582, 0xE878, 0x9583, 0x914D, 0x9587, 0xE879,\n\t0x9589, 0x95C2, 0x958A, 0xE87A, 0x958B, 0x8A4A, 0x958F, 0x895B,\n\t0x9591, 0x8AD5, 0x9592, 0xFBE8, 0x9593, 0x8AD4, 0x9594, 0xE87B,\n\t0x9596, 0xE87C, 0x9598, 0xE87D, 0x9599, 0xE87E, 0x95A0, 0xE880,\n\t0x95A2, 0x8AD6, 0x95A3, 0x8A74, 0x95A4, 0x8D7D, 0x95A5, 0x94B4,\n\t0x95A7, 0xE882, 0x95A8, 0xE881, 0x95AD, 0xE883, 0x95B2, 0x897B,\n\t0x95B9, 0xE886, 0x95BB, 0xE885, 0x95BC, 0xE884, 0x95BE, 0xE887,\n\t0x95C3, 0xE88A, 0x95C7, 0x88C5, 0x95CA, 0xE888, 0x95CC, 0xE88C,\n\t0x95CD, 0xE88B, 0x95D4, 0xE88E, 0x95D5, 0xE88D, 0x95D6, 0xE88F,\n\t0x95D8, 0x93AC, 0x95DC, 0xE890, 0x95E1, 0xE891, 0x95E2, 0xE893,\n\t0x95E5, 0xE892, 0x961C, 0x958C, 0x9621, 0xE894, 0x9628, 0xE895,\n\t0x962A, 0x8DE3, 0x962E, 0xE896, 0x962F, 0xE897, 0x9632, 0x9668,\n\t0x963B, 0x916A, 0x963F, 0x88A2, 0x9640, 0x91C9, 0x9642, 0xE898,\n\t0x9644, 0x958D, 0x964B, 0xE89B, 0x964C, 0xE899, 0x964D, 0x8D7E,\n\t0x964F, 0xE89A, 0x9650, 0x8CC0, 0x965B, 0x95C3, 0x965C, 0xE89D,\n\t0x965D, 0xE89F, 0x965E, 0xE89E, 0x965F, 0xE8A0, 0x9662, 0x8940,\n\t0x9663, 0x9077, 0x9664, 0x8F9C, 0x9665, 0x8AD7, 0x9666, 0xE8A1,\n\t0x966A, 0x9486, 0x966C, 0xE8A3, 0x9670, 0x8941, 0x9672, 0xE8A2,\n\t0x9673, 0x92C2, 0x9675, 0x97CB, 0x9676, 0x93A9, 0x9677, 0xE89C,\n\t0x9678, 0x97A4, 0x967A, 0x8CAF, 0x967D, 0x977A, 0x9685, 0x8BF7,\n\t0x9686, 0x97B2, 0x9688, 0x8C47, 0x968A, 0x91E0, 0x968B, 0xE440,\n\t0x968D, 0xE8A4, 0x968E, 0x8A4B, 0x968F, 0x908F, 0x9694, 0x8A75,\n\t0x9695, 0xE8A6, 0x9697, 0xE8A7, 0x9698, 0xE8A5, 0x9699, 0x8C84,\n\t0x969B, 0x8DDB, 0x969C, 0x8FE1, 0x969D, 0xFBEB, 0x96A0, 0x8942,\n\t0x96A3, 0x97D7, 0x96A7, 0xE8A9, 0x96A8, 0xE7AC, 0x96AA, 0xE8A8,\n\t0x96AF, 0xFBEC, 0x96B0, 0xE8AC, 0x96B1, 0xE8AA, 0x96B2, 0xE8AB,\n\t0x96B4, 0xE8AD, 0x96B6, 0xE8AE, 0x96B7, 0x97EA, 0x96B8, 0xE8AF,\n\t0x96B9, 0xE8B0, 0x96BB, 0x90C7, 0x96BC, 0x94B9, 0x96C0, 0x909D,\n\t0x96C1, 0x8AE5, 0x96C4, 0x9759, 0x96C5, 0x89EB, 0x96C6, 0x8F57,\n\t0x96C7, 0x8CD9, 0x96C9, 0xE8B3, 0x96CB, 0xE8B2, 0x96CC, 0x8E93,\n\t0x96CD, 0xE8B4, 0x96CE, 0xE8B1, 0x96D1, 0x8E47, 0x96D5, 0xE8B8,\n\t0x96D6, 0xE5AB, 0x96D9, 0x99D4, 0x96DB, 0x9097, 0x96DC, 0xE8B6,\n\t0x96E2, 0x97A3, 0x96E3, 0x93EF, 0x96E8, 0x894A, 0x96EA, 0x90E1,\n\t0x96EB, 0x8EB4, 0x96F0, 0x95B5, 0x96F2, 0x895F, 0x96F6, 0x97EB,\n\t0x96F7, 0x978B, 0x96F9, 0xE8B9, 0x96FB, 0x9364, 0x9700, 0x8EF9,\n\t0x9704, 0xE8BA, 0x9706, 0xE8BB, 0x9707, 0x906B, 0x9708, 0xE8BC,\n\t0x970A, 0x97EC, 0x970D, 0xE8B7, 0x970E, 0xE8BE, 0x970F, 0xE8C0,\n\t0x9711, 0xE8BF, 0x9713, 0xE8BD, 0x9716, 0xE8C1, 0x9719, 0xE8C2,\n\t0x971C, 0x919A, 0x971E, 0x89E0, 0x9724, 0xE8C3, 0x9727, 0x96B6,\n\t0x972A, 0xE8C4, 0x9730, 0xE8C5, 0x9732, 0x9849, 0x9733, 0xFBED,\n\t0x9738, 0x9E50, 0x9739, 0xE8C6, 0x973B, 0xFBEE, 0x973D, 0xE8C7,\n\t0x973E, 0xE8C8, 0x9742, 0xE8CC, 0x9743, 0xFBEF, 0x9744, 0xE8C9,\n\t0x9746, 0xE8CA, 0x9748, 0xE8CB, 0x9749, 0xE8CD, 0x974D, 0xFBF0,\n\t0x974F, 0xFBF1, 0x9751, 0xFBF2, 0x9752, 0x90C2, 0x9755, 0xFBF3,\n\t0x9756, 0x96F5, 0x9759, 0x90C3, 0x975C, 0xE8CE, 0x975E, 0x94F1,\n\t0x9760, 0xE8CF, 0x9761, 0xEA72, 0x9762, 0x96CA, 0x9764, 0xE8D0,\n\t0x9766, 0xE8D1, 0x9768, 0xE8D2, 0x9769, 0x8A76, 0x976B, 0xE8D4,\n\t0x976D, 0x9078, 0x9771, 0xE8D5, 0x9774, 0x8C43, 0x9779, 0xE8D6,\n\t0x977A, 0xE8DA, 0x977C, 0xE8D8, 0x9781, 0xE8D9, 0x9784, 0x8A93,\n\t0x9785, 0xE8D7, 0x9786, 0xE8DB, 0x978B, 0xE8DC, 0x978D, 0x88C6,\n\t0x978F, 0xE8DD, 0x9790, 0xE8DE, 0x9798, 0x8FE2, 0x979C, 0xE8DF,\n\t0x97A0, 0x8B66, 0x97A3, 0xE8E2, 0x97A6, 0xE8E1, 0x97A8, 0xE8E0,\n\t0x97AB, 0xE691, 0x97AD, 0x95DA, 0x97B3, 0xE8E3, 0x97B4, 0xE8E4,\n\t0x97C3, 0xE8E5, 0x97C6, 0xE8E6, 0x97C8, 0xE8E7, 0x97CB, 0xE8E8,\n\t0x97D3, 0x8AD8, 0x97DC, 0xE8E9, 0x97ED, 0xE8EA, 0x97EE, 0x9442,\n\t0x97F2, 0xE8EC, 0x97F3, 0x89B9, 0x97F5, 0xE8EF, 0x97F6, 0xE8EE,\n\t0x97FB, 0x8943, 0x97FF, 0x8BBF, 0x9801, 0x95C5, 0x9802, 0x92B8,\n\t0x9803, 0x8DA0, 0x9805, 0x8D80, 0x9806, 0x8F87, 0x9808, 0x907B,\n\t0x980C, 0xE8F1, 0x980F, 0xE8F0, 0x9810, 0x9761, 0x9811, 0x8AE6,\n\t0x9812, 0x94D0, 0x9813, 0x93DA, 0x9817, 0x909C, 0x9818, 0x97CC,\n\t0x981A, 0x8C7A, 0x9821, 0xE8F4, 0x9824, 0xE8F3, 0x982C, 0x966A,\n\t0x982D, 0x93AA, 0x9834, 0x896F, 0x9837, 0xE8F5, 0x9838, 0xE8F2,\n\t0x983B, 0x9570, 0x983C, 0x978A, 0x983D, 0xE8F6, 0x9846, 0xE8F7,\n\t0x984B, 0xE8F9, 0x984C, 0x91E8, 0x984D, 0x8A7A, 0x984E, 0x8A7B,\n\t0x984F, 0xE8F8, 0x9854, 0x8AE7, 0x9855, 0x8CB0, 0x9857, 0xFBF4,\n\t0x9858, 0x8AE8, 0x985B, 0x935E, 0x985E, 0x97DE, 0x9865, 0xFBF5,\n\t0x9867, 0x8CDA, 0x986B, 0xE8FA, 0x986F, 0xE8FB, 0x9870, 0xE8FC,\n\t0x9871, 0xE940, 0x9873, 0xE942, 0x9874, 0xE941, 0x98A8, 0x9597,\n\t0x98AA, 0xE943, 0x98AF, 0xE944, 0x98B1, 0xE945, 0x98B6, 0xE946,\n\t0x98C3, 0xE948, 0x98C4, 0xE947, 0x98C6, 0xE949, 0x98DB, 0x94F2,\n\t0x98DC, 0xE3CA, 0x98DF, 0x9048, 0x98E2, 0x8B51, 0x98E9, 0xE94A,\n\t0x98EB, 0xE94B, 0x98ED, 0x99AA, 0x98EE, 0x9F5A, 0x98EF, 0x94D1,\n\t0x98F2, 0x88F9, 0x98F4, 0x88B9, 0x98FC, 0x8E94, 0x98FD, 0x964F,\n\t0x98FE, 0x8FFC, 0x9903, 0xE94C, 0x9905, 0x96DD, 0x9909, 0xE94D,\n\t0x990A, 0x977B, 0x990C, 0x8961, 0x9910, 0x8E60, 0x9912, 0xE94E,\n\t0x9913, 0x89EC, 0x9914, 0xE94F, 0x9918, 0xE950, 0x991D, 0xE952,\n\t0x991E, 0xE953, 0x9920, 0xE955, 0x9921, 0xE951, 0x9924, 0xE954,\n\t0x9927, 0xFBF8, 0x9928, 0x8AD9, 0x992C, 0xE956, 0x992E, 0xE957,\n\t0x993D, 0xE958, 0x993E, 0xE959, 0x9942, 0xE95A, 0x9945, 0xE95C,\n\t0x9949, 0xE95B, 0x994B, 0xE95E, 0x994C, 0xE961, 0x9950, 0xE95D,\n\t0x9951, 0xE95F, 0x9952, 0xE960, 0x9955, 0xE962, 0x9957, 0x8BC0,\n\t0x9996, 0x8EF1, 0x9997, 0xE963, 0x9998, 0xE964, 0x9999, 0x8D81,\n\t0x999E, 0xFBFA, 0x99A5, 0xE965, 0x99A8, 0x8A5D, 0x99AC, 0x946E,\n\t0x99AD, 0xE966, 0x99AE, 0xE967, 0x99B3, 0x9279, 0x99B4, 0x93E9,\n\t0x99BC, 0xE968, 0x99C1, 0x949D, 0x99C4, 0x91CA, 0x99C5, 0x8977,\n\t0x99C6, 0x8BEC, 0x99C8, 0x8BED, 0x99D0, 0x9293, 0x99D1, 0xE96D,\n\t0x99D2, 0x8BEE, 0x99D5, 0x89ED, 0x99D8, 0xE96C, 0x99DB, 0xE96A,\n\t0x99DD, 0xE96B, 0x99DF, 0xE969, 0x99E2, 0xE977, 0x99ED, 0xE96E,\n\t0x99EE, 0xE96F, 0x99F1, 0xE970, 0x99F2, 0xE971, 0x99F8, 0xE973,\n\t0x99FB, 0xE972, 0x99FF, 0x8F78, 0x9A01, 0xE974, 0x9A05, 0xE976,\n\t0x9A0E, 0x8B52, 0x9A0F, 0xE975, 0x9A12, 0x919B, 0x9A13, 0x8CB1,\n\t0x9A19, 0xE978, 0x9A28, 0x91CB, 0x9A2B, 0xE979, 0x9A30, 0x93AB,\n\t0x9A37, 0xE97A, 0x9A3E, 0xE980, 0x9A40, 0xE97D, 0x9A42, 0xE97C,\n\t0x9A43, 0xE97E, 0x9A45, 0xE97B, 0x9A4D, 0xE982, 0x9A4E, 0xFBFB,\n\t0x9A55, 0xE981, 0x9A57, 0xE984, 0x9A5A, 0x8BC1, 0x9A5B, 0xE983,\n\t0x9A5F, 0xE985, 0x9A62, 0xE986, 0x9A64, 0xE988, 0x9A65, 0xE987,\n\t0x9A69, 0xE989, 0x9A6A, 0xE98B, 0x9A6B, 0xE98A, 0x9AA8, 0x8D9C,\n\t0x9AAD, 0xE98C, 0x9AB0, 0xE98D, 0x9AB8, 0x8A5B, 0x9ABC, 0xE98E,\n\t0x9AC0, 0xE98F, 0x9AC4, 0x9091, 0x9ACF, 0xE990, 0x9AD1, 0xE991,\n\t0x9AD3, 0xE992, 0x9AD4, 0xE993, 0x9AD8, 0x8D82, 0x9AD9, 0xFBFC,\n\t0x9ADC, 0xFC40, 0x9ADE, 0xE994, 0x9ADF, 0xE995, 0x9AE2, 0xE996,\n\t0x9AE3, 0xE997, 0x9AE6, 0xE998, 0x9AEA, 0x94AF, 0x9AEB, 0xE99A,\n\t0x9AED, 0x9545, 0x9AEE, 0xE99B, 0x9AEF, 0xE999, 0x9AF1, 0xE99D,\n\t0x9AF4, 0xE99C, 0x9AF7, 0xE99E, 0x9AFB, 0xE99F, 0x9B06, 0xE9A0,\n\t0x9B18, 0xE9A1, 0x9B1A, 0xE9A2, 0x9B1F, 0xE9A3, 0x9B22, 0xE9A4,\n\t0x9B23, 0xE9A5, 0x9B25, 0xE9A6, 0x9B27, 0xE9A7, 0x9B28, 0xE9A8,\n\t0x9B29, 0xE9A9, 0x9B2A, 0xE9AA, 0x9B2E, 0xE9AB, 0x9B2F, 0xE9AC,\n\t0x9B31, 0x9F54, 0x9B32, 0xE9AD, 0x9B3B, 0xE2F6, 0x9B3C, 0x8B53,\n\t0x9B41, 0x8A40, 0x9B42, 0x8DB0, 0x9B43, 0xE9AF, 0x9B44, 0xE9AE,\n\t0x9B45, 0x96A3, 0x9B4D, 0xE9B1, 0x9B4E, 0xE9B2, 0x9B4F, 0xE9B0,\n\t0x9B51, 0xE9B3, 0x9B54, 0x9682, 0x9B58, 0xE9B4, 0x9B5A, 0x8B9B,\n\t0x9B6F, 0x9844, 0x9B72, 0xFC42, 0x9B74, 0xE9B5, 0x9B75, 0xFC41,\n\t0x9B83, 0xE9B7, 0x9B8E, 0x88BC, 0x9B8F, 0xFC43, 0x9B91, 0xE9B8,\n\t0x9B92, 0x95A9, 0x9B93, 0xE9B6, 0x9B96, 0xE9B9, 0x9B97, 0xE9BA,\n\t0x9B9F, 0xE9BB, 0x9BA0, 0xE9BC, 0x9BA8, 0xE9BD, 0x9BAA, 0x968E,\n\t0x9BAB, 0x8E4C, 0x9BAD, 0x8DF8, 0x9BAE, 0x914E, 0x9BB1, 0xFC44,\n\t0x9BB4, 0xE9BE, 0x9BB9, 0xE9C1, 0x9BBB, 0xFC45, 0x9BC0, 0xE9BF,\n\t0x9BC6, 0xE9C2, 0x9BC9, 0x8CEF, 0x9BCA, 0xE9C0, 0x9BCF, 0xE9C3,\n\t0x9BD1, 0xE9C4, 0x9BD2, 0xE9C5, 0x9BD4, 0xE9C9, 0x9BD6, 0x8E49,\n\t0x9BDB, 0x91E2, 0x9BE1, 0xE9CA, 0x9BE2, 0xE9C7, 0x9BE3, 0xE9C6,\n\t0x9BE4, 0xE9C8, 0x9BE8, 0x8C7E, 0x9BF0, 0xE9CE, 0x9BF1, 0xE9CD,\n\t0x9BF2, 0xE9CC, 0x9BF5, 0x88B1, 0x9C00, 0xFC46, 0x9C04, 0xE9D8,\n\t0x9C06, 0xE9D4, 0x9C08, 0xE9D5, 0x9C09, 0xE9D1, 0x9C0A, 0xE9D7,\n\t0x9C0C, 0xE9D3, 0x9C0D, 0x8A82, 0x9C10, 0x986B, 0x9C12, 0xE9D6,\n\t0x9C13, 0xE9D2, 0x9C14, 0xE9D0, 0x9C15, 0xE9CF, 0x9C1B, 0xE9DA,\n\t0x9C21, 0xE9DD, 0x9C24, 0xE9DC, 0x9C25, 0xE9DB, 0x9C2D, 0x9568,\n\t0x9C2E, 0xE9D9, 0x9C2F, 0x88F1, 0x9C30, 0xE9DE, 0x9C32, 0xE9E0,\n\t0x9C39, 0x8A8F, 0x9C3A, 0xE9CB, 0x9C3B, 0x8956, 0x9C3E, 0xE9E2,\n\t0x9C46, 0xE9E1, 0x9C47, 0xE9DF, 0x9C48, 0x924C, 0x9C52, 0x9690,\n\t0x9C57, 0x97D8, 0x9C5A, 0xE9E3, 0x9C60, 0xE9E4, 0x9C67, 0xE9E5,\n\t0x9C76, 0xE9E6, 0x9C78, 0xE9E7, 0x9CE5, 0x92B9, 0x9CE7, 0xE9E8,\n\t0x9CE9, 0x94B5, 0x9CEB, 0xE9ED, 0x9CEC, 0xE9E9, 0x9CF0, 0xE9EA,\n\t0x9CF3, 0x9650, 0x9CF4, 0x96C2, 0x9CF6, 0x93CE, 0x9D03, 0xE9EE,\n\t0x9D06, 0xE9EF, 0x9D07, 0x93BC, 0x9D08, 0xE9EC, 0x9D09, 0xE9EB,\n\t0x9D0E, 0x89A8, 0x9D12, 0xE9F7, 0x9D15, 0xE9F6, 0x9D1B, 0x8995,\n\t0x9D1F, 0xE9F4, 0x9D23, 0xE9F3, 0x9D26, 0xE9F1, 0x9D28, 0x8A9B,\n\t0x9D2A, 0xE9F0, 0x9D2B, 0x8EB0, 0x9D2C, 0x89A7, 0x9D3B, 0x8D83,\n\t0x9D3E, 0xE9FA, 0x9D3F, 0xE9F9, 0x9D41, 0xE9F8, 0x9D44, 0xE9F5,\n\t0x9D46, 0xE9FB, 0x9D48, 0xE9FC, 0x9D50, 0xEA44, 0x9D51, 0xEA43,\n\t0x9D59, 0xEA45, 0x9D5C, 0x894C, 0x9D5D, 0xEA40, 0x9D5E, 0xEA41,\n\t0x9D60, 0x8D94, 0x9D61, 0x96B7, 0x9D64, 0xEA42, 0x9D6B, 0xFC48,\n\t0x9D6C, 0x9651, 0x9D6F, 0xEA4A, 0x9D70, 0xFC47, 0x9D72, 0xEA46,\n\t0x9D7A, 0xEA4B, 0x9D87, 0xEA48, 0x9D89, 0xEA47, 0x9D8F, 0x8C7B,\n\t0x9D9A, 0xEA4C, 0x9DA4, 0xEA4D, 0x9DA9, 0xEA4E, 0x9DAB, 0xEA49,\n\t0x9DAF, 0xE9F2, 0x9DB2, 0xEA4F, 0x9DB4, 0x92DF, 0x9DB8, 0xEA53,\n\t0x9DBA, 0xEA54, 0x9DBB, 0xEA52, 0x9DC1, 0xEA51, 0x9DC2, 0xEA57,\n\t0x9DC4, 0xEA50, 0x9DC6, 0xEA55, 0x9DCF, 0xEA56, 0x9DD3, 0xEA59,\n\t0x9DD9, 0xEA58, 0x9DE6, 0xEA5B, 0x9DED, 0xEA5C, 0x9DEF, 0xEA5D,\n\t0x9DF2, 0x9868, 0x9DF8, 0xEA5A, 0x9DF9, 0x91E9, 0x9DFA, 0x8DEB,\n\t0x9DFD, 0xEA5E, 0x9E19, 0xFC4A, 0x9E1A, 0xEA5F, 0x9E1B, 0xEA60,\n\t0x9E1E, 0xEA61, 0x9E75, 0xEA62, 0x9E78, 0x8CB2, 0x9E79, 0xEA63,\n\t0x9E7D, 0xEA64, 0x9E7F, 0x8EAD, 0x9E81, 0xEA65, 0x9E88, 0xEA66,\n\t0x9E8B, 0xEA67, 0x9E8C, 0xEA68, 0x9E91, 0xEA6B, 0x9E92, 0xEA69,\n\t0x9E93, 0x985B, 0x9E95, 0xEA6A, 0x9E97, 0x97ED, 0x9E9D, 0xEA6C,\n\t0x9E9F, 0x97D9, 0x9EA5, 0xEA6D, 0x9EA6, 0x949E, 0x9EA9, 0xEA6E,\n\t0x9EAA, 0xEA70, 0x9EAD, 0xEA71, 0x9EB8, 0xEA6F, 0x9EB9, 0x8D8D,\n\t0x9EBA, 0x96CB, 0x9EBB, 0x9683, 0x9EBC, 0x9BF5, 0x9EBE, 0x9F80,\n\t0x9EBF, 0x969B, 0x9EC4, 0x89A9, 0x9ECC, 0xEA73, 0x9ECD, 0x8B6F,\n\t0x9ECE, 0xEA74, 0x9ECF, 0xEA75, 0x9ED0, 0xEA76, 0x9ED1, 0xFC4B,\n\t0x9ED2, 0x8D95, 0x9ED4, 0xEA77, 0x9ED8, 0xE0D2, 0x9ED9, 0x96D9,\n\t0x9EDB, 0x91E1, 0x9EDC, 0xEA78, 0x9EDD, 0xEA7A, 0x9EDE, 0xEA79,\n\t0x9EE0, 0xEA7B, 0x9EE5, 0xEA7C, 0x9EE8, 0xEA7D, 0x9EEF, 0xEA7E,\n\t0x9EF4, 0xEA80, 0x9EF6, 0xEA81, 0x9EF7, 0xEA82, 0x9EF9, 0xEA83,\n\t0x9EFB, 0xEA84, 0x9EFC, 0xEA85, 0x9EFD, 0xEA86, 0x9F07, 0xEA87,\n\t0x9F08, 0xEA88, 0x9F0E, 0x9343, 0x9F13, 0x8CDB, 0x9F15, 0xEA8A,\n\t0x9F20, 0x916C, 0x9F21, 0xEA8B, 0x9F2C, 0xEA8C, 0x9F3B, 0x9540,\n\t0x9F3E, 0xEA8D, 0x9F4A, 0xEA8E, 0x9F4B, 0xE256, 0x9F4E, 0xE6D8,\n\t0x9F4F, 0xE8EB, 0x9F52, 0xEA8F, 0x9F54, 0xEA90, 0x9F5F, 0xEA92,\n\t0x9F60, 0xEA93, 0x9F61, 0xEA94, 0x9F62, 0x97EE, 0x9F63, 0xEA91,\n\t0x9F66, 0xEA95, 0x9F67, 0xEA96, 0x9F6A, 0xEA98, 0x9F6C, 0xEA97,\n\t0x9F72, 0xEA9A, 0x9F76, 0xEA9B, 0x9F77, 0xEA99, 0x9F8D, 0x97B4,\n\t0x9F95, 0xEA9C, 0x9F9C, 0xEA9D, 0x9F9D, 0xE273, 0x9FA0, 0xEA9E,\n\t0xF929, 0xFAE0, 0xF9DC, 0xFBE9, 0xFA0E, 0xFA90, 0xFA0F, 0xFA9B,\n\t0xFA10, 0xFA9C, 0xFA11, 0xFAB1, 0xFA12, 0xFAD8, 0xFA13, 0xFAE8,\n\t0xFA14, 0xFAEA, 0xFA15, 0xFB58, 0xFA16, 0xFB5E, 0xFA17, 0xFB75,\n\t0xFA18, 0xFB7D, 0xFA19, 0xFB7E, 0xFA1A, 0xFB80, 0xFA1B, 0xFB82,\n\t0xFA1C, 0xFB86, 0xFA1D, 0xFB89, 0xFA1E, 0xFB92, 0xFA1F, 0xFB9D,\n\t0xFA20, 0xFB9F, 0xFA21, 0xFBA0, 0xFA22, 0xFBA9, 0xFA23, 0xFBB1,\n\t0xFA24, 0xFBB3, 0xFA25, 0xFBB4, 0xFA26, 0xFBB7, 0xFA27, 0xFBD3,\n\t0xFA28, 0xFBDA, 0xFA29, 0xFBEA, 0xFA2A, 0xFBF6, 0xFA2B, 0xFBF7,\n\t0xFA2C, 0xFBF9, 0xFA2D, 0xFC49, 0xFF01, 0x8149, 0xFF02, 0xFA57,\n\t0xFF03, 0x8194, 0xFF04, 0x8190, 0xFF05, 0x8193, 0xFF06, 0x8195,\n\t0xFF07, 0xFA56, 0xFF08, 0x8169, 0xFF09, 0x816A, 0xFF0A, 0x8196,\n\t0xFF0B, 0x817B, 0xFF0C, 0x8143, 0xFF0D, 0x817C, 0xFF0E, 0x8144,\n\t0xFF0F, 0x815E, 0xFF10, 0x824F, 0xFF11, 0x8250, 0xFF12, 0x8251,\n\t0xFF13, 0x8252, 0xFF14, 0x8253, 0xFF15, 0x8254, 0xFF16, 0x8255,\n\t0xFF17, 0x8256, 0xFF18, 0x8257, 0xFF19, 0x8258, 0xFF1A, 0x8146,\n\t0xFF1B, 0x8147, 0xFF1C, 0x8183, 0xFF1D, 0x8181, 0xFF1E, 0x8184,\n\t0xFF1F, 0x8148, 0xFF20, 0x8197, 0xFF21, 0x8260, 0xFF22, 0x8261,\n\t0xFF23, 0x8262, 0xFF24, 0x8263, 0xFF25, 0x8264, 0xFF26, 0x8265,\n\t0xFF27, 0x8266, 0xFF28, 0x8267, 0xFF29, 0x8268, 0xFF2A, 0x8269,\n\t0xFF2B, 0x826A, 0xFF2C, 0x826B, 0xFF2D, 0x826C, 0xFF2E, 0x826D,\n\t0xFF2F, 0x826E, 0xFF30, 0x826F, 0xFF31, 0x8270, 0xFF32, 0x8271,\n\t0xFF33, 0x8272, 0xFF34, 0x8273, 0xFF35, 0x8274, 0xFF36, 0x8275,\n\t0xFF37, 0x8276, 0xFF38, 0x8277, 0xFF39, 0x8278, 0xFF3A, 0x8279,\n\t0xFF3B, 0x816D, 0xFF3C, 0x815F, 0xFF3D, 0x816E, 0xFF3E, 0x814F,\n\t0xFF3F, 0x8151, 0xFF40, 0x814D, 0xFF41, 0x8281, 0xFF42, 0x8282,\n\t0xFF43, 0x8283, 0xFF44, 0x8284, 0xFF45, 0x8285, 0xFF46, 0x8286,\n\t0xFF47, 0x8287, 0xFF48, 0x8288, 0xFF49, 0x8289, 0xFF4A, 0x828A,\n\t0xFF4B, 0x828B, 0xFF4C, 0x828C, 0xFF4D, 0x828D, 0xFF4E, 0x828E,\n\t0xFF4F, 0x828F, 0xFF50, 0x8290, 0xFF51, 0x8291, 0xFF52, 0x8292,\n\t0xFF53, 0x8293, 0xFF54, 0x8294, 0xFF55, 0x8295, 0xFF56, 0x8296,\n\t0xFF57, 0x8297, 0xFF58, 0x8298, 0xFF59, 0x8299, 0xFF5A, 0x829A,\n\t0xFF5B, 0x816F, 0xFF5C, 0x8162, 0xFF5D, 0x8170, 0xFF5E, 0x8160,\n\t0xFF61, 0x00A1, 0xFF62, 0x00A2, 0xFF63, 0x00A3, 0xFF64, 0x00A4,\n\t0xFF65, 0x00A5, 0xFF66, 0x00A6, 0xFF67, 0x00A7, 0xFF68, 0x00A8,\n\t0xFF69, 0x00A9, 0xFF6A, 0x00AA, 0xFF6B, 0x00AB, 0xFF6C, 0x00AC,\n\t0xFF6D, 0x00AD, 0xFF6E, 0x00AE, 0xFF6F, 0x00AF, 0xFF70, 0x00B0,\n\t0xFF71, 0x00B1, 0xFF72, 0x00B2, 0xFF73, 0x00B3, 0xFF74, 0x00B4,\n\t0xFF75, 0x00B5, 0xFF76, 0x00B6, 0xFF77, 0x00B7, 0xFF78, 0x00B8,\n\t0xFF79, 0x00B9, 0xFF7A, 0x00BA, 0xFF7B, 0x00BB, 0xFF7C, 0x00BC,\n\t0xFF7D, 0x00BD, 0xFF7E, 0x00BE, 0xFF7F, 0x00BF, 0xFF80, 0x00C0,\n\t0xFF81, 0x00C1, 0xFF82, 0x00C2, 0xFF83, 0x00C3, 0xFF84, 0x00C4,\n\t0xFF85, 0x00C5, 0xFF86, 0x00C6, 0xFF87, 0x00C7, 0xFF88, 0x00C8,\n\t0xFF89, 0x00C9, 0xFF8A, 0x00CA, 0xFF8B, 0x00CB, 0xFF8C, 0x00CC,\n\t0xFF8D, 0x00CD, 0xFF8E, 0x00CE, 0xFF8F, 0x00CF, 0xFF90, 0x00D0,\n\t0xFF91, 0x00D1, 0xFF92, 0x00D2, 0xFF93, 0x00D3, 0xFF94, 0x00D4,\n\t0xFF95, 0x00D5, 0xFF96, 0x00D6, 0xFF97, 0x00D7, 0xFF98, 0x00D8,\n\t0xFF99, 0x00D9, 0xFF9A, 0x00DA, 0xFF9B, 0x00DB, 0xFF9C, 0x00DC,\n\t0xFF9D, 0x00DD, 0xFF9E, 0x00DE, 0xFF9F, 0x00DF, 0xFFE0, 0x8191,\n\t0xFFE1, 0x8192, 0xFFE2, 0x81CA, 0xFFE3, 0x8150, 0xFFE4, 0xFA55,\n\t0xFFE5, 0x818F, 0, 0\n};\n\n#if !_TINY_TABLE\nstatic\nconst WCHAR sjis2uni[] = {\n/*\tSJIS - Unicode, SJIS - Unicode, SJIS - Unicode, SJIS - Unicode, */\n\t0x00A1, 0xFF61, 0x00A2, 0xFF62, 0x00A3, 0xFF63, 0x00A4, 0xFF64,\n\t0x00A5, 0xFF65, 0x00A6, 0xFF66, 0x00A7, 0xFF67, 0x00A8, 0xFF68,\n\t0x00A9, 0xFF69, 0x00AA, 0xFF6A, 0x00AB, 0xFF6B, 0x00AC, 0xFF6C,\n\t0x00AD, 0xFF6D, 0x00AE, 0xFF6E, 0x00AF, 0xFF6F, 0x00B0, 0xFF70,\n\t0x00B1, 0xFF71, 0x00B2, 0xFF72, 0x00B3, 0xFF73, 0x00B4, 0xFF74,\n\t0x00B5, 0xFF75, 0x00B6, 0xFF76, 0x00B7, 0xFF77, 0x00B8, 0xFF78,\n\t0x00B9, 0xFF79, 0x00BA, 0xFF7A, 0x00BB, 0xFF7B, 0x00BC, 0xFF7C,\n\t0x00BD, 0xFF7D, 0x00BE, 0xFF7E, 0x00BF, 0xFF7F, 0x00C0, 0xFF80,\n\t0x00C1, 0xFF81, 0x00C2, 0xFF82, 0x00C3, 0xFF83, 0x00C4, 0xFF84,\n\t0x00C5, 0xFF85, 0x00C6, 0xFF86, 0x00C7, 0xFF87, 0x00C8, 0xFF88,\n\t0x00C9, 0xFF89, 0x00CA, 0xFF8A, 0x00CB, 0xFF8B, 0x00CC, 0xFF8C,\n\t0x00CD, 0xFF8D, 0x00CE, 0xFF8E, 0x00CF, 0xFF8F, 0x00D0, 0xFF90,\n\t0x00D1, 0xFF91, 0x00D2, 0xFF92, 0x00D3, 0xFF93, 0x00D4, 0xFF94,\n\t0x00D5, 0xFF95, 0x00D6, 0xFF96, 0x00D7, 0xFF97, 0x00D8, 0xFF98,\n\t0x00D9, 0xFF99, 0x00DA, 0xFF9A, 0x00DB, 0xFF9B, 0x00DC, 0xFF9C,\n\t0x00DD, 0xFF9D, 0x00DE, 0xFF9E, 0x00DF, 0xFF9F, 0x8140, 0x3000,\n\t0x8141, 0x3001, 0x8142, 0x3002, 0x8143, 0xFF0C, 0x8144, 0xFF0E,\n\t0x8145, 0x30FB, 0x8146, 0xFF1A, 0x8147, 0xFF1B, 0x8148, 0xFF1F,\n\t0x8149, 0xFF01, 0x814A, 0x309B, 0x814B, 0x309C, 0x814C, 0x00B4,\n\t0x814D, 0xFF40, 0x814E, 0x00A8, 0x814F, 0xFF3E, 0x8150, 0xFFE3,\n\t0x8151, 0xFF3F, 0x8152, 0x30FD, 0x8153, 0x30FE, 0x8154, 0x309D,\n\t0x8155, 0x309E, 0x8156, 0x3003, 0x8157, 0x4EDD, 0x8158, 0x3005,\n\t0x8159, 0x3006, 0x815A, 0x3007, 0x815B, 0x30FC, 0x815C, 0x2015,\n\t0x815D, 0x2010, 0x815E, 0xFF0F, 0x815F, 0xFF3C, 0x8160, 0xFF5E,\n\t0x8161, 0x2225, 0x8162, 0xFF5C, 0x8163, 0x2026, 0x8164, 0x2025,\n\t0x8165, 0x2018, 0x8166, 0x2019, 0x8167, 0x201C, 0x8168, 0x201D,\n\t0x8169, 0xFF08, 0x816A, 0xFF09, 0x816B, 0x3014, 0x816C, 0x3015,\n\t0x816D, 0xFF3B, 0x816E, 0xFF3D, 0x816F, 0xFF5B, 0x8170, 0xFF5D,\n\t0x8171, 0x3008, 0x8172, 0x3009, 0x8173, 0x300A, 0x8174, 0x300B,\n\t0x8175, 0x300C, 0x8176, 0x300D, 0x8177, 0x300E, 0x8178, 0x300F,\n\t0x8179, 0x3010, 0x817A, 0x3011, 0x817B, 0xFF0B, 0x817C, 0xFF0D,\n\t0x817D, 0x00B1, 0x817E, 0x00D7, 0x8180, 0x00F7, 0x8181, 0xFF1D,\n\t0x8182, 0x2260, 0x8183, 0xFF1C, 0x8184, 0xFF1E, 0x8185, 0x2266,\n\t0x8186, 0x2267, 0x8187, 0x221E, 0x8188, 0x2234, 0x8189, 0x2642,\n\t0x818A, 0x2640, 0x818B, 0x00B0, 0x818C, 0x2032, 0x818D, 0x2033,\n\t0x818E, 0x2103, 0x818F, 0xFFE5, 0x8190, 0xFF04, 0x8191, 0xFFE0,\n\t0x8192, 0xFFE1, 0x8193, 0xFF05, 0x8194, 0xFF03, 0x8195, 0xFF06,\n\t0x8196, 0xFF0A, 0x8197, 0xFF20, 0x8198, 0x00A7, 0x8199, 0x2606,\n\t0x819A, 0x2605, 0x819B, 0x25CB, 0x819C, 0x25CF, 0x819D, 0x25CE,\n\t0x819E, 0x25C7, 0x819F, 0x25C6, 0x81A0, 0x25A1, 0x81A1, 0x25A0,\n\t0x81A2, 0x25B3, 0x81A3, 0x25B2, 0x81A4, 0x25BD, 0x81A5, 0x25BC,\n\t0x81A6, 0x203B, 0x81A7, 0x3012, 0x81A8, 0x2192, 0x81A9, 0x2190,\n\t0x81AA, 0x2191, 0x81AB, 0x2193, 0x81AC, 0x3013, 0x81B8, 0x2208,\n\t0x81B9, 0x220B, 0x81BA, 0x2286, 0x81BB, 0x2287, 0x81BC, 0x2282,\n\t0x81BD, 0x2283, 0x81BE, 0x222A, 0x81BF, 0x2229, 0x81C8, 0x2227,\n\t0x81C9, 0x2228, 0x81CA, 0xFFE2, 0x81CB, 0x21D2, 0x81CC, 0x21D4,\n\t0x81CD, 0x2200, 0x81CE, 0x2203, 0x81DA, 0x2220, 0x81DB, 0x22A5,\n\t0x81DC, 0x2312, 0x81DD, 0x2202, 0x81DE, 0x2207, 0x81DF, 0x2261,\n\t0x81E0, 0x2252, 0x81E1, 0x226A, 0x81E2, 0x226B, 0x81E3, 0x221A,\n\t0x81E4, 0x223D, 0x81E5, 0x221D, 0x81E6, 0x2235, 0x81E7, 0x222B,\n\t0x81E8, 0x222C, 0x81F0, 0x212B, 0x81F1, 0x2030, 0x81F2, 0x266F,\n\t0x81F3, 0x266D, 0x81F4, 0x266A, 0x81F5, 0x2020, 0x81F6, 0x2021,\n\t0x81F7, 0x00B6, 0x81FC, 0x25EF, 0x824F, 0xFF10, 0x8250, 0xFF11,\n\t0x8251, 0xFF12, 0x8252, 0xFF13, 0x8253, 0xFF14, 0x8254, 0xFF15,\n\t0x8255, 0xFF16, 0x8256, 0xFF17, 0x8257, 0xFF18, 0x8258, 0xFF19,\n\t0x8260, 0xFF21, 0x8261, 0xFF22, 0x8262, 0xFF23, 0x8263, 0xFF24,\n\t0x8264, 0xFF25, 0x8265, 0xFF26, 0x8266, 0xFF27, 0x8267, 0xFF28,\n\t0x8268, 0xFF29, 0x8269, 0xFF2A, 0x826A, 0xFF2B, 0x826B, 0xFF2C,\n\t0x826C, 0xFF2D, 0x826D, 0xFF2E, 0x826E, 0xFF2F, 0x826F, 0xFF30,\n\t0x8270, 0xFF31, 0x8271, 0xFF32, 0x8272, 0xFF33, 0x8273, 0xFF34,\n\t0x8274, 0xFF35, 0x8275, 0xFF36, 0x8276, 0xFF37, 0x8277, 0xFF38,\n\t0x8278, 0xFF39, 0x8279, 0xFF3A, 0x8281, 0xFF41, 0x8282, 0xFF42,\n\t0x8283, 0xFF43, 0x8284, 0xFF44, 0x8285, 0xFF45, 0x8286, 0xFF46,\n\t0x8287, 0xFF47, 0x8288, 0xFF48, 0x8289, 0xFF49, 0x828A, 0xFF4A,\n\t0x828B, 0xFF4B, 0x828C, 0xFF4C, 0x828D, 0xFF4D, 0x828E, 0xFF4E,\n\t0x828F, 0xFF4F, 0x8290, 0xFF50, 0x8291, 0xFF51, 0x8292, 0xFF52,\n\t0x8293, 0xFF53, 0x8294, 0xFF54, 0x8295, 0xFF55, 0x8296, 0xFF56,\n\t0x8297, 0xFF57, 0x8298, 0xFF58, 0x8299, 0xFF59, 0x829A, 0xFF5A,\n\t0x829F, 0x3041, 0x82A0, 0x3042, 0x82A1, 0x3043, 0x82A2, 0x3044,\n\t0x82A3, 0x3045, 0x82A4, 0x3046, 0x82A5, 0x3047, 0x82A6, 0x3048,\n\t0x82A7, 0x3049, 0x82A8, 0x304A, 0x82A9, 0x304B, 0x82AA, 0x304C,\n\t0x82AB, 0x304D, 0x82AC, 0x304E, 0x82AD, 0x304F, 0x82AE, 0x3050,\n\t0x82AF, 0x3051, 0x82B0, 0x3052, 0x82B1, 0x3053, 0x82B2, 0x3054,\n\t0x82B3, 0x3055, 0x82B4, 0x3056, 0x82B5, 0x3057, 0x82B6, 0x3058,\n\t0x82B7, 0x3059, 0x82B8, 0x305A, 0x82B9, 0x305B, 0x82BA, 0x305C,\n\t0x82BB, 0x305D, 0x82BC, 0x305E, 0x82BD, 0x305F, 0x82BE, 0x3060,\n\t0x82BF, 0x3061, 0x82C0, 0x3062, 0x82C1, 0x3063, 0x82C2, 0x3064,\n\t0x82C3, 0x3065, 0x82C4, 0x3066, 0x82C5, 0x3067, 0x82C6, 0x3068,\n\t0x82C7, 0x3069, 0x82C8, 0x306A, 0x82C9, 0x306B, 0x82CA, 0x306C,\n\t0x82CB, 0x306D, 0x82CC, 0x306E, 0x82CD, 0x306F, 0x82CE, 0x3070,\n\t0x82CF, 0x3071, 0x82D0, 0x3072, 0x82D1, 0x3073, 0x82D2, 0x3074,\n\t0x82D3, 0x3075, 0x82D4, 0x3076, 0x82D5, 0x3077, 0x82D6, 0x3078,\n\t0x82D7, 0x3079, 0x82D8, 0x307A, 0x82D9, 0x307B, 0x82DA, 0x307C,\n\t0x82DB, 0x307D, 0x82DC, 0x307E, 0x82DD, 0x307F, 0x82DE, 0x3080,\n\t0x82DF, 0x3081, 0x82E0, 0x3082, 0x82E1, 0x3083, 0x82E2, 0x3084,\n\t0x82E3, 0x3085, 0x82E4, 0x3086, 0x82E5, 0x3087, 0x82E6, 0x3088,\n\t0x82E7, 0x3089, 0x82E8, 0x308A, 0x82E9, 0x308B, 0x82EA, 0x308C,\n\t0x82EB, 0x308D, 0x82EC, 0x308E, 0x82ED, 0x308F, 0x82EE, 0x3090,\n\t0x82EF, 0x3091, 0x82F0, 0x3092, 0x82F1, 0x3093, 0x8340, 0x30A1,\n\t0x8341, 0x30A2, 0x8342, 0x30A3, 0x8343, 0x30A4, 0x8344, 0x30A5,\n\t0x8345, 0x30A6, 0x8346, 0x30A7, 0x8347, 0x30A8, 0x8348, 0x30A9,\n\t0x8349, 0x30AA, 0x834A, 0x30AB, 0x834B, 0x30AC, 0x834C, 0x30AD,\n\t0x834D, 0x30AE, 0x834E, 0x30AF, 0x834F, 0x30B0, 0x8350, 0x30B1,\n\t0x8351, 0x30B2, 0x8352, 0x30B3, 0x8353, 0x30B4, 0x8354, 0x30B5,\n\t0x8355, 0x30B6, 0x8356, 0x30B7, 0x8357, 0x30B8, 0x8358, 0x30B9,\n\t0x8359, 0x30BA, 0x835A, 0x30BB, 0x835B, 0x30BC, 0x835C, 0x30BD,\n\t0x835D, 0x30BE, 0x835E, 0x30BF, 0x835F, 0x30C0, 0x8360, 0x30C1,\n\t0x8361, 0x30C2, 0x8362, 0x30C3, 0x8363, 0x30C4, 0x8364, 0x30C5,\n\t0x8365, 0x30C6, 0x8366, 0x30C7, 0x8367, 0x30C8, 0x8368, 0x30C9,\n\t0x8369, 0x30CA, 0x836A, 0x30CB, 0x836B, 0x30CC, 0x836C, 0x30CD,\n\t0x836D, 0x30CE, 0x836E, 0x30CF, 0x836F, 0x30D0, 0x8370, 0x30D1,\n\t0x8371, 0x30D2, 0x8372, 0x30D3, 0x8373, 0x30D4, 0x8374, 0x30D5,\n\t0x8375, 0x30D6, 0x8376, 0x30D7, 0x8377, 0x30D8, 0x8378, 0x30D9,\n\t0x8379, 0x30DA, 0x837A, 0x30DB, 0x837B, 0x30DC, 0x837C, 0x30DD,\n\t0x837D, 0x30DE, 0x837E, 0x30DF, 0x8380, 0x30E0, 0x8381, 0x30E1,\n\t0x8382, 0x30E2, 0x8383, 0x30E3, 0x8384, 0x30E4, 0x8385, 0x30E5,\n\t0x8386, 0x30E6, 0x8387, 0x30E7, 0x8388, 0x30E8, 0x8389, 0x30E9,\n\t0x838A, 0x30EA, 0x838B, 0x30EB, 0x838C, 0x30EC, 0x838D, 0x30ED,\n\t0x838E, 0x30EE, 0x838F, 0x30EF, 0x8390, 0x30F0, 0x8391, 0x30F1,\n\t0x8392, 0x30F2, 0x8393, 0x30F3, 0x8394, 0x30F4, 0x8395, 0x30F5,\n\t0x8396, 0x30F6, 0x839F, 0x0391, 0x83A0, 0x0392, 0x83A1, 0x0393,\n\t0x83A2, 0x0394, 0x83A3, 0x0395, 0x83A4, 0x0396, 0x83A5, 0x0397,\n\t0x83A6, 0x0398, 0x83A7, 0x0399, 0x83A8, 0x039A, 0x83A9, 0x039B,\n\t0x83AA, 0x039C, 0x83AB, 0x039D, 0x83AC, 0x039E, 0x83AD, 0x039F,\n\t0x83AE, 0x03A0, 0x83AF, 0x03A1, 0x83B0, 0x03A3, 0x83B1, 0x03A4,\n\t0x83B2, 0x03A5, 0x83B3, 0x03A6, 0x83B4, 0x03A7, 0x83B5, 0x03A8,\n\t0x83B6, 0x03A9, 0x83BF, 0x03B1, 0x83C0, 0x03B2, 0x83C1, 0x03B3,\n\t0x83C2, 0x03B4, 0x83C3, 0x03B5, 0x83C4, 0x03B6, 0x83C5, 0x03B7,\n\t0x83C6, 0x03B8, 0x83C7, 0x03B9, 0x83C8, 0x03BA, 0x83C9, 0x03BB,\n\t0x83CA, 0x03BC, 0x83CB, 0x03BD, 0x83CC, 0x03BE, 0x83CD, 0x03BF,\n\t0x83CE, 0x03C0, 0x83CF, 0x03C1, 0x83D0, 0x03C3, 0x83D1, 0x03C4,\n\t0x83D2, 0x03C5, 0x83D3, 0x03C6, 0x83D4, 0x03C7, 0x83D5, 0x03C8,\n\t0x83D6, 0x03C9, 0x8440, 0x0410, 0x8441, 0x0411, 0x8442, 0x0412,\n\t0x8443, 0x0413, 0x8444, 0x0414, 0x8445, 0x0415, 0x8446, 0x0401,\n\t0x8447, 0x0416, 0x8448, 0x0417, 0x8449, 0x0418, 0x844A, 0x0419,\n\t0x844B, 0x041A, 0x844C, 0x041B, 0x844D, 0x041C, 0x844E, 0x041D,\n\t0x844F, 0x041E, 0x8450, 0x041F, 0x8451, 0x0420, 0x8452, 0x0421,\n\t0x8453, 0x0422, 0x8454, 0x0423, 0x8455, 0x0424, 0x8456, 0x0425,\n\t0x8457, 0x0426, 0x8458, 0x0427, 0x8459, 0x0428, 0x845A, 0x0429,\n\t0x845B, 0x042A, 0x845C, 0x042B, 0x845D, 0x042C, 0x845E, 0x042D,\n\t0x845F, 0x042E, 0x8460, 0x042F, 0x8470, 0x0430, 0x8471, 0x0431,\n\t0x8472, 0x0432, 0x8473, 0x0433, 0x8474, 0x0434, 0x8475, 0x0435,\n\t0x8476, 0x0451, 0x8477, 0x0436, 0x8478, 0x0437, 0x8479, 0x0438,\n\t0x847A, 0x0439, 0x847B, 0x043A, 0x847C, 0x043B, 0x847D, 0x043C,\n\t0x847E, 0x043D, 0x8480, 0x043E, 0x8481, 0x043F, 0x8482, 0x0440,\n\t0x8483, 0x0441, 0x8484, 0x0442, 0x8485, 0x0443, 0x8486, 0x0444,\n\t0x8487, 0x0445, 0x8488, 0x0446, 0x8489, 0x0447, 0x848A, 0x0448,\n\t0x848B, 0x0449, 0x848C, 0x044A, 0x848D, 0x044B, 0x848E, 0x044C,\n\t0x848F, 0x044D, 0x8490, 0x044E, 0x8491, 0x044F, 0x849F, 0x2500,\n\t0x84A0, 0x2502, 0x84A1, 0x250C, 0x84A2, 0x2510, 0x84A3, 0x2518,\n\t0x84A4, 0x2514, 0x84A5, 0x251C, 0x84A6, 0x252C, 0x84A7, 0x2524,\n\t0x84A8, 0x2534, 0x84A9, 0x253C, 0x84AA, 0x2501, 0x84AB, 0x2503,\n\t0x84AC, 0x250F, 0x84AD, 0x2513, 0x84AE, 0x251B, 0x84AF, 0x2517,\n\t0x84B0, 0x2523, 0x84B1, 0x2533, 0x84B2, 0x252B, 0x84B3, 0x253B,\n\t0x84B4, 0x254B, 0x84B5, 0x2520, 0x84B6, 0x252F, 0x84B7, 0x2528,\n\t0x84B8, 0x2537, 0x84B9, 0x253F, 0x84BA, 0x251D, 0x84BB, 0x2530,\n\t0x84BC, 0x2525, 0x84BD, 0x2538, 0x84BE, 0x2542, 0x8740, 0x2460,\n\t0x8741, 0x2461, 0x8742, 0x2462, 0x8743, 0x2463, 0x8744, 0x2464,\n\t0x8745, 0x2465, 0x8746, 0x2466, 0x8747, 0x2467, 0x8748, 0x2468,\n\t0x8749, 0x2469, 0x874A, 0x246A, 0x874B, 0x246B, 0x874C, 0x246C,\n\t0x874D, 0x246D, 0x874E, 0x246E, 0x874F, 0x246F, 0x8750, 0x2470,\n\t0x8751, 0x2471, 0x8752, 0x2472, 0x8753, 0x2473, 0x8754, 0x2160,\n\t0x8755, 0x2161, 0x8756, 0x2162, 0x8757, 0x2163, 0x8758, 0x2164,\n\t0x8759, 0x2165, 0x875A, 0x2166, 0x875B, 0x2167, 0x875C, 0x2168,\n\t0x875D, 0x2169, 0x875F, 0x3349, 0x8760, 0x3314, 0x8761, 0x3322,\n\t0x8762, 0x334D, 0x8763, 0x3318, 0x8764, 0x3327, 0x8765, 0x3303,\n\t0x8766, 0x3336, 0x8767, 0x3351, 0x8768, 0x3357, 0x8769, 0x330D,\n\t0x876A, 0x3326, 0x876B, 0x3323, 0x876C, 0x332B, 0x876D, 0x334A,\n\t0x876E, 0x333B, 0x876F, 0x339C, 0x8770, 0x339D, 0x8771, 0x339E,\n\t0x8772, 0x338E, 0x8773, 0x338F, 0x8774, 0x33C4, 0x8775, 0x33A1,\n\t0x877E, 0x337B, 0x8780, 0x301D, 0x8781, 0x301F, 0x8782, 0x2116,\n\t0x8783, 0x33CD, 0x8784, 0x2121, 0x8785, 0x32A4, 0x8786, 0x32A5,\n\t0x8787, 0x32A6, 0x8788, 0x32A7, 0x8789, 0x32A8, 0x878A, 0x3231,\n\t0x878B, 0x3232, 0x878C, 0x3239, 0x878D, 0x337E, 0x878E, 0x337D,\n\t0x878F, 0x337C, 0x8793, 0x222E, 0x8794, 0x2211, 0x8798, 0x221F,\n\t0x8799, 0x22BF, 0x889F, 0x4E9C, 0x88A0, 0x5516, 0x88A1, 0x5A03,\n\t0x88A2, 0x963F, 0x88A3, 0x54C0, 0x88A4, 0x611B, 0x88A5, 0x6328,\n\t0x88A6, 0x59F6, 0x88A7, 0x9022, 0x88A8, 0x8475, 0x88A9, 0x831C,\n\t0x88AA, 0x7A50, 0x88AB, 0x60AA, 0x88AC, 0x63E1, 0x88AD, 0x6E25,\n\t0x88AE, 0x65ED, 0x88AF, 0x8466, 0x88B0, 0x82A6, 0x88B1, 0x9BF5,\n\t0x88B2, 0x6893, 0x88B3, 0x5727, 0x88B4, 0x65A1, 0x88B5, 0x6271,\n\t0x88B6, 0x5B9B, 0x88B7, 0x59D0, 0x88B8, 0x867B, 0x88B9, 0x98F4,\n\t0x88BA, 0x7D62, 0x88BB, 0x7DBE, 0x88BC, 0x9B8E, 0x88BD, 0x6216,\n\t0x88BE, 0x7C9F, 0x88BF, 0x88B7, 0x88C0, 0x5B89, 0x88C1, 0x5EB5,\n\t0x88C2, 0x6309, 0x88C3, 0x6697, 0x88C4, 0x6848, 0x88C5, 0x95C7,\n\t0x88C6, 0x978D, 0x88C7, 0x674F, 0x88C8, 0x4EE5, 0x88C9, 0x4F0A,\n\t0x88CA, 0x4F4D, 0x88CB, 0x4F9D, 0x88CC, 0x5049, 0x88CD, 0x56F2,\n\t0x88CE, 0x5937, 0x88CF, 0x59D4, 0x88D0, 0x5A01, 0x88D1, 0x5C09,\n\t0x88D2, 0x60DF, 0x88D3, 0x610F, 0x88D4, 0x6170, 0x88D5, 0x6613,\n\t0x88D6, 0x6905, 0x88D7, 0x70BA, 0x88D8, 0x754F, 0x88D9, 0x7570,\n\t0x88DA, 0x79FB, 0x88DB, 0x7DAD, 0x88DC, 0x7DEF, 0x88DD, 0x80C3,\n\t0x88DE, 0x840E, 0x88DF, 0x8863, 0x88E0, 0x8B02, 0x88E1, 0x9055,\n\t0x88E2, 0x907A, 0x88E3, 0x533B, 0x88E4, 0x4E95, 0x88E5, 0x4EA5,\n\t0x88E6, 0x57DF, 0x88E7, 0x80B2, 0x88E8, 0x90C1, 0x88E9, 0x78EF,\n\t0x88EA, 0x4E00, 0x88EB, 0x58F1, 0x88EC, 0x6EA2, 0x88ED, 0x9038,\n\t0x88EE, 0x7A32, 0x88EF, 0x8328, 0x88F0, 0x828B, 0x88F1, 0x9C2F,\n\t0x88F2, 0x5141, 0x88F3, 0x5370, 0x88F4, 0x54BD, 0x88F5, 0x54E1,\n\t0x88F6, 0x56E0, 0x88F7, 0x59FB, 0x88F8, 0x5F15, 0x88F9, 0x98F2,\n\t0x88FA, 0x6DEB, 0x88FB, 0x80E4, 0x88FC, 0x852D, 0x8940, 0x9662,\n\t0x8941, 0x9670, 0x8942, 0x96A0, 0x8943, 0x97FB, 0x8944, 0x540B,\n\t0x8945, 0x53F3, 0x8946, 0x5B87, 0x8947, 0x70CF, 0x8948, 0x7FBD,\n\t0x8949, 0x8FC2, 0x894A, 0x96E8, 0x894B, 0x536F, 0x894C, 0x9D5C,\n\t0x894D, 0x7ABA, 0x894E, 0x4E11, 0x894F, 0x7893, 0x8950, 0x81FC,\n\t0x8951, 0x6E26, 0x8952, 0x5618, 0x8953, 0x5504, 0x8954, 0x6B1D,\n\t0x8955, 0x851A, 0x8956, 0x9C3B, 0x8957, 0x59E5, 0x8958, 0x53A9,\n\t0x8959, 0x6D66, 0x895A, 0x74DC, 0x895B, 0x958F, 0x895C, 0x5642,\n\t0x895D, 0x4E91, 0x895E, 0x904B, 0x895F, 0x96F2, 0x8960, 0x834F,\n\t0x8961, 0x990C, 0x8962, 0x53E1, 0x8963, 0x55B6, 0x8964, 0x5B30,\n\t0x8965, 0x5F71, 0x8966, 0x6620, 0x8967, 0x66F3, 0x8968, 0x6804,\n\t0x8969, 0x6C38, 0x896A, 0x6CF3, 0x896B, 0x6D29, 0x896C, 0x745B,\n\t0x896D, 0x76C8, 0x896E, 0x7A4E, 0x896F, 0x9834, 0x8970, 0x82F1,\n\t0x8971, 0x885B, 0x8972, 0x8A60, 0x8973, 0x92ED, 0x8974, 0x6DB2,\n\t0x8975, 0x75AB, 0x8976, 0x76CA, 0x8977, 0x99C5, 0x8978, 0x60A6,\n\t0x8979, 0x8B01, 0x897A, 0x8D8A, 0x897B, 0x95B2, 0x897C, 0x698E,\n\t0x897D, 0x53AD, 0x897E, 0x5186, 0x8980, 0x5712, 0x8981, 0x5830,\n\t0x8982, 0x5944, 0x8983, 0x5BB4, 0x8984, 0x5EF6, 0x8985, 0x6028,\n\t0x8986, 0x63A9, 0x8987, 0x63F4, 0x8988, 0x6CBF, 0x8989, 0x6F14,\n\t0x898A, 0x708E, 0x898B, 0x7114, 0x898C, 0x7159, 0x898D, 0x71D5,\n\t0x898E, 0x733F, 0x898F, 0x7E01, 0x8990, 0x8276, 0x8991, 0x82D1,\n\t0x8992, 0x8597, 0x8993, 0x9060, 0x8994, 0x925B, 0x8995, 0x9D1B,\n\t0x8996, 0x5869, 0x8997, 0x65BC, 0x8998, 0x6C5A, 0x8999, 0x7525,\n\t0x899A, 0x51F9, 0x899B, 0x592E, 0x899C, 0x5965, 0x899D, 0x5F80,\n\t0x899E, 0x5FDC, 0x899F, 0x62BC, 0x89A0, 0x65FA, 0x89A1, 0x6A2A,\n\t0x89A2, 0x6B27, 0x89A3, 0x6BB4, 0x89A4, 0x738B, 0x89A5, 0x7FC1,\n\t0x89A6, 0x8956, 0x89A7, 0x9D2C, 0x89A8, 0x9D0E, 0x89A9, 0x9EC4,\n\t0x89AA, 0x5CA1, 0x89AB, 0x6C96, 0x89AC, 0x837B, 0x89AD, 0x5104,\n\t0x89AE, 0x5C4B, 0x89AF, 0x61B6, 0x89B0, 0x81C6, 0x89B1, 0x6876,\n\t0x89B2, 0x7261, 0x89B3, 0x4E59, 0x89B4, 0x4FFA, 0x89B5, 0x5378,\n\t0x89B6, 0x6069, 0x89B7, 0x6E29, 0x89B8, 0x7A4F, 0x89B9, 0x97F3,\n\t0x89BA, 0x4E0B, 0x89BB, 0x5316, 0x89BC, 0x4EEE, 0x89BD, 0x4F55,\n\t0x89BE, 0x4F3D, 0x89BF, 0x4FA1, 0x89C0, 0x4F73, 0x89C1, 0x52A0,\n\t0x89C2, 0x53EF, 0x89C3, 0x5609, 0x89C4, 0x590F, 0x89C5, 0x5AC1,\n\t0x89C6, 0x5BB6, 0x89C7, 0x5BE1, 0x89C8, 0x79D1, 0x89C9, 0x6687,\n\t0x89CA, 0x679C, 0x89CB, 0x67B6, 0x89CC, 0x6B4C, 0x89CD, 0x6CB3,\n\t0x89CE, 0x706B, 0x89CF, 0x73C2, 0x89D0, 0x798D, 0x89D1, 0x79BE,\n\t0x89D2, 0x7A3C, 0x89D3, 0x7B87, 0x89D4, 0x82B1, 0x89D5, 0x82DB,\n\t0x89D6, 0x8304, 0x89D7, 0x8377, 0x89D8, 0x83EF, 0x89D9, 0x83D3,\n\t0x89DA, 0x8766, 0x89DB, 0x8AB2, 0x89DC, 0x5629, 0x89DD, 0x8CA8,\n\t0x89DE, 0x8FE6, 0x89DF, 0x904E, 0x89E0, 0x971E, 0x89E1, 0x868A,\n\t0x89E2, 0x4FC4, 0x89E3, 0x5CE8, 0x89E4, 0x6211, 0x89E5, 0x7259,\n\t0x89E6, 0x753B, 0x89E7, 0x81E5, 0x89E8, 0x82BD, 0x89E9, 0x86FE,\n\t0x89EA, 0x8CC0, 0x89EB, 0x96C5, 0x89EC, 0x9913, 0x89ED, 0x99D5,\n\t0x89EE, 0x4ECB, 0x89EF, 0x4F1A, 0x89F0, 0x89E3, 0x89F1, 0x56DE,\n\t0x89F2, 0x584A, 0x89F3, 0x58CA, 0x89F4, 0x5EFB, 0x89F5, 0x5FEB,\n\t0x89F6, 0x602A, 0x89F7, 0x6094, 0x89F8, 0x6062, 0x89F9, 0x61D0,\n\t0x89FA, 0x6212, 0x89FB, 0x62D0, 0x89FC, 0x6539, 0x8A40, 0x9B41,\n\t0x8A41, 0x6666, 0x8A42, 0x68B0, 0x8A43, 0x6D77, 0x8A44, 0x7070,\n\t0x8A45, 0x754C, 0x8A46, 0x7686, 0x8A47, 0x7D75, 0x8A48, 0x82A5,\n\t0x8A49, 0x87F9, 0x8A4A, 0x958B, 0x8A4B, 0x968E, 0x8A4C, 0x8C9D,\n\t0x8A4D, 0x51F1, 0x8A4E, 0x52BE, 0x8A4F, 0x5916, 0x8A50, 0x54B3,\n\t0x8A51, 0x5BB3, 0x8A52, 0x5D16, 0x8A53, 0x6168, 0x8A54, 0x6982,\n\t0x8A55, 0x6DAF, 0x8A56, 0x788D, 0x8A57, 0x84CB, 0x8A58, 0x8857,\n\t0x8A59, 0x8A72, 0x8A5A, 0x93A7, 0x8A5B, 0x9AB8, 0x8A5C, 0x6D6C,\n\t0x8A5D, 0x99A8, 0x8A5E, 0x86D9, 0x8A5F, 0x57A3, 0x8A60, 0x67FF,\n\t0x8A61, 0x86CE, 0x8A62, 0x920E, 0x8A63, 0x5283, 0x8A64, 0x5687,\n\t0x8A65, 0x5404, 0x8A66, 0x5ED3, 0x8A67, 0x62E1, 0x8A68, 0x64B9,\n\t0x8A69, 0x683C, 0x8A6A, 0x6838, 0x8A6B, 0x6BBB, 0x8A6C, 0x7372,\n\t0x8A6D, 0x78BA, 0x8A6E, 0x7A6B, 0x8A6F, 0x899A, 0x8A70, 0x89D2,\n\t0x8A71, 0x8D6B, 0x8A72, 0x8F03, 0x8A73, 0x90ED, 0x8A74, 0x95A3,\n\t0x8A75, 0x9694, 0x8A76, 0x9769, 0x8A77, 0x5B66, 0x8A78, 0x5CB3,\n\t0x8A79, 0x697D, 0x8A7A, 0x984D, 0x8A7B, 0x984E, 0x8A7C, 0x639B,\n\t0x8A7D, 0x7B20, 0x8A7E, 0x6A2B, 0x8A80, 0x6A7F, 0x8A81, 0x68B6,\n\t0x8A82, 0x9C0D, 0x8A83, 0x6F5F, 0x8A84, 0x5272, 0x8A85, 0x559D,\n\t0x8A86, 0x6070, 0x8A87, 0x62EC, 0x8A88, 0x6D3B, 0x8A89, 0x6E07,\n\t0x8A8A, 0x6ED1, 0x8A8B, 0x845B, 0x8A8C, 0x8910, 0x8A8D, 0x8F44,\n\t0x8A8E, 0x4E14, 0x8A8F, 0x9C39, 0x8A90, 0x53F6, 0x8A91, 0x691B,\n\t0x8A92, 0x6A3A, 0x8A93, 0x9784, 0x8A94, 0x682A, 0x8A95, 0x515C,\n\t0x8A96, 0x7AC3, 0x8A97, 0x84B2, 0x8A98, 0x91DC, 0x8A99, 0x938C,\n\t0x8A9A, 0x565B, 0x8A9B, 0x9D28, 0x8A9C, 0x6822, 0x8A9D, 0x8305,\n\t0x8A9E, 0x8431, 0x8A9F, 0x7CA5, 0x8AA0, 0x5208, 0x8AA1, 0x82C5,\n\t0x8AA2, 0x74E6, 0x8AA3, 0x4E7E, 0x8AA4, 0x4F83, 0x8AA5, 0x51A0,\n\t0x8AA6, 0x5BD2, 0x8AA7, 0x520A, 0x8AA8, 0x52D8, 0x8AA9, 0x52E7,\n\t0x8AAA, 0x5DFB, 0x8AAB, 0x559A, 0x8AAC, 0x582A, 0x8AAD, 0x59E6,\n\t0x8AAE, 0x5B8C, 0x8AAF, 0x5B98, 0x8AB0, 0x5BDB, 0x8AB1, 0x5E72,\n\t0x8AB2, 0x5E79, 0x8AB3, 0x60A3, 0x8AB4, 0x611F, 0x8AB5, 0x6163,\n\t0x8AB6, 0x61BE, 0x8AB7, 0x63DB, 0x8AB8, 0x6562, 0x8AB9, 0x67D1,\n\t0x8ABA, 0x6853, 0x8ABB, 0x68FA, 0x8ABC, 0x6B3E, 0x8ABD, 0x6B53,\n\t0x8ABE, 0x6C57, 0x8ABF, 0x6F22, 0x8AC0, 0x6F97, 0x8AC1, 0x6F45,\n\t0x8AC2, 0x74B0, 0x8AC3, 0x7518, 0x8AC4, 0x76E3, 0x8AC5, 0x770B,\n\t0x8AC6, 0x7AFF, 0x8AC7, 0x7BA1, 0x8AC8, 0x7C21, 0x8AC9, 0x7DE9,\n\t0x8ACA, 0x7F36, 0x8ACB, 0x7FF0, 0x8ACC, 0x809D, 0x8ACD, 0x8266,\n\t0x8ACE, 0x839E, 0x8ACF, 0x89B3, 0x8AD0, 0x8ACC, 0x8AD1, 0x8CAB,\n\t0x8AD2, 0x9084, 0x8AD3, 0x9451, 0x8AD4, 0x9593, 0x8AD5, 0x9591,\n\t0x8AD6, 0x95A2, 0x8AD7, 0x9665, 0x8AD8, 0x97D3, 0x8AD9, 0x9928,\n\t0x8ADA, 0x8218, 0x8ADB, 0x4E38, 0x8ADC, 0x542B, 0x8ADD, 0x5CB8,\n\t0x8ADE, 0x5DCC, 0x8ADF, 0x73A9, 0x8AE0, 0x764C, 0x8AE1, 0x773C,\n\t0x8AE2, 0x5CA9, 0x8AE3, 0x7FEB, 0x8AE4, 0x8D0B, 0x8AE5, 0x96C1,\n\t0x8AE6, 0x9811, 0x8AE7, 0x9854, 0x8AE8, 0x9858, 0x8AE9, 0x4F01,\n\t0x8AEA, 0x4F0E, 0x8AEB, 0x5371, 0x8AEC, 0x559C, 0x8AED, 0x5668,\n\t0x8AEE, 0x57FA, 0x8AEF, 0x5947, 0x8AF0, 0x5B09, 0x8AF1, 0x5BC4,\n\t0x8AF2, 0x5C90, 0x8AF3, 0x5E0C, 0x8AF4, 0x5E7E, 0x8AF5, 0x5FCC,\n\t0x8AF6, 0x63EE, 0x8AF7, 0x673A, 0x8AF8, 0x65D7, 0x8AF9, 0x65E2,\n\t0x8AFA, 0x671F, 0x8AFB, 0x68CB, 0x8AFC, 0x68C4, 0x8B40, 0x6A5F,\n\t0x8B41, 0x5E30, 0x8B42, 0x6BC5, 0x8B43, 0x6C17, 0x8B44, 0x6C7D,\n\t0x8B45, 0x757F, 0x8B46, 0x7948, 0x8B47, 0x5B63, 0x8B48, 0x7A00,\n\t0x8B49, 0x7D00, 0x8B4A, 0x5FBD, 0x8B4B, 0x898F, 0x8B4C, 0x8A18,\n\t0x8B4D, 0x8CB4, 0x8B4E, 0x8D77, 0x8B4F, 0x8ECC, 0x8B50, 0x8F1D,\n\t0x8B51, 0x98E2, 0x8B52, 0x9A0E, 0x8B53, 0x9B3C, 0x8B54, 0x4E80,\n\t0x8B55, 0x507D, 0x8B56, 0x5100, 0x8B57, 0x5993, 0x8B58, 0x5B9C,\n\t0x8B59, 0x622F, 0x8B5A, 0x6280, 0x8B5B, 0x64EC, 0x8B5C, 0x6B3A,\n\t0x8B5D, 0x72A0, 0x8B5E, 0x7591, 0x8B5F, 0x7947, 0x8B60, 0x7FA9,\n\t0x8B61, 0x87FB, 0x8B62, 0x8ABC, 0x8B63, 0x8B70, 0x8B64, 0x63AC,\n\t0x8B65, 0x83CA, 0x8B66, 0x97A0, 0x8B67, 0x5409, 0x8B68, 0x5403,\n\t0x8B69, 0x55AB, 0x8B6A, 0x6854, 0x8B6B, 0x6A58, 0x8B6C, 0x8A70,\n\t0x8B6D, 0x7827, 0x8B6E, 0x6775, 0x8B6F, 0x9ECD, 0x8B70, 0x5374,\n\t0x8B71, 0x5BA2, 0x8B72, 0x811A, 0x8B73, 0x8650, 0x8B74, 0x9006,\n\t0x8B75, 0x4E18, 0x8B76, 0x4E45, 0x8B77, 0x4EC7, 0x8B78, 0x4F11,\n\t0x8B79, 0x53CA, 0x8B7A, 0x5438, 0x8B7B, 0x5BAE, 0x8B7C, 0x5F13,\n\t0x8B7D, 0x6025, 0x8B7E, 0x6551, 0x8B80, 0x673D, 0x8B81, 0x6C42,\n\t0x8B82, 0x6C72, 0x8B83, 0x6CE3, 0x8B84, 0x7078, 0x8B85, 0x7403,\n\t0x8B86, 0x7A76, 0x8B87, 0x7AAE, 0x8B88, 0x7B08, 0x8B89, 0x7D1A,\n\t0x8B8A, 0x7CFE, 0x8B8B, 0x7D66, 0x8B8C, 0x65E7, 0x8B8D, 0x725B,\n\t0x8B8E, 0x53BB, 0x8B8F, 0x5C45, 0x8B90, 0x5DE8, 0x8B91, 0x62D2,\n\t0x8B92, 0x62E0, 0x8B93, 0x6319, 0x8B94, 0x6E20, 0x8B95, 0x865A,\n\t0x8B96, 0x8A31, 0x8B97, 0x8DDD, 0x8B98, 0x92F8, 0x8B99, 0x6F01,\n\t0x8B9A, 0x79A6, 0x8B9B, 0x9B5A, 0x8B9C, 0x4EA8, 0x8B9D, 0x4EAB,\n\t0x8B9E, 0x4EAC, 0x8B9F, 0x4F9B, 0x8BA0, 0x4FA0, 0x8BA1, 0x50D1,\n\t0x8BA2, 0x5147, 0x8BA3, 0x7AF6, 0x8BA4, 0x5171, 0x8BA5, 0x51F6,\n\t0x8BA6, 0x5354, 0x8BA7, 0x5321, 0x8BA8, 0x537F, 0x8BA9, 0x53EB,\n\t0x8BAA, 0x55AC, 0x8BAB, 0x5883, 0x8BAC, 0x5CE1, 0x8BAD, 0x5F37,\n\t0x8BAE, 0x5F4A, 0x8BAF, 0x602F, 0x8BB0, 0x6050, 0x8BB1, 0x606D,\n\t0x8BB2, 0x631F, 0x8BB3, 0x6559, 0x8BB4, 0x6A4B, 0x8BB5, 0x6CC1,\n\t0x8BB6, 0x72C2, 0x8BB7, 0x72ED, 0x8BB8, 0x77EF, 0x8BB9, 0x80F8,\n\t0x8BBA, 0x8105, 0x8BBB, 0x8208, 0x8BBC, 0x854E, 0x8BBD, 0x90F7,\n\t0x8BBE, 0x93E1, 0x8BBF, 0x97FF, 0x8BC0, 0x9957, 0x8BC1, 0x9A5A,\n\t0x8BC2, 0x4EF0, 0x8BC3, 0x51DD, 0x8BC4, 0x5C2D, 0x8BC5, 0x6681,\n\t0x8BC6, 0x696D, 0x8BC7, 0x5C40, 0x8BC8, 0x66F2, 0x8BC9, 0x6975,\n\t0x8BCA, 0x7389, 0x8BCB, 0x6850, 0x8BCC, 0x7C81, 0x8BCD, 0x50C5,\n\t0x8BCE, 0x52E4, 0x8BCF, 0x5747, 0x8BD0, 0x5DFE, 0x8BD1, 0x9326,\n\t0x8BD2, 0x65A4, 0x8BD3, 0x6B23, 0x8BD4, 0x6B3D, 0x8BD5, 0x7434,\n\t0x8BD6, 0x7981, 0x8BD7, 0x79BD, 0x8BD8, 0x7B4B, 0x8BD9, 0x7DCA,\n\t0x8BDA, 0x82B9, 0x8BDB, 0x83CC, 0x8BDC, 0x887F, 0x8BDD, 0x895F,\n\t0x8BDE, 0x8B39, 0x8BDF, 0x8FD1, 0x8BE0, 0x91D1, 0x8BE1, 0x541F,\n\t0x8BE2, 0x9280, 0x8BE3, 0x4E5D, 0x8BE4, 0x5036, 0x8BE5, 0x53E5,\n\t0x8BE6, 0x533A, 0x8BE7, 0x72D7, 0x8BE8, 0x7396, 0x8BE9, 0x77E9,\n\t0x8BEA, 0x82E6, 0x8BEB, 0x8EAF, 0x8BEC, 0x99C6, 0x8BED, 0x99C8,\n\t0x8BEE, 0x99D2, 0x8BEF, 0x5177, 0x8BF0, 0x611A, 0x8BF1, 0x865E,\n\t0x8BF2, 0x55B0, 0x8BF3, 0x7A7A, 0x8BF4, 0x5076, 0x8BF5, 0x5BD3,\n\t0x8BF6, 0x9047, 0x8BF7, 0x9685, 0x8BF8, 0x4E32, 0x8BF9, 0x6ADB,\n\t0x8BFA, 0x91E7, 0x8BFB, 0x5C51, 0x8BFC, 0x5C48, 0x8C40, 0x6398,\n\t0x8C41, 0x7A9F, 0x8C42, 0x6C93, 0x8C43, 0x9774, 0x8C44, 0x8F61,\n\t0x8C45, 0x7AAA, 0x8C46, 0x718A, 0x8C47, 0x9688, 0x8C48, 0x7C82,\n\t0x8C49, 0x6817, 0x8C4A, 0x7E70, 0x8C4B, 0x6851, 0x8C4C, 0x936C,\n\t0x8C4D, 0x52F2, 0x8C4E, 0x541B, 0x8C4F, 0x85AB, 0x8C50, 0x8A13,\n\t0x8C51, 0x7FA4, 0x8C52, 0x8ECD, 0x8C53, 0x90E1, 0x8C54, 0x5366,\n\t0x8C55, 0x8888, 0x8C56, 0x7941, 0x8C57, 0x4FC2, 0x8C58, 0x50BE,\n\t0x8C59, 0x5211, 0x8C5A, 0x5144, 0x8C5B, 0x5553, 0x8C5C, 0x572D,\n\t0x8C5D, 0x73EA, 0x8C5E, 0x578B, 0x8C5F, 0x5951, 0x8C60, 0x5F62,\n\t0x8C61, 0x5F84, 0x8C62, 0x6075, 0x8C63, 0x6176, 0x8C64, 0x6167,\n\t0x8C65, 0x61A9, 0x8C66, 0x63B2, 0x8C67, 0x643A, 0x8C68, 0x656C,\n\t0x8C69, 0x666F, 0x8C6A, 0x6842, 0x8C6B, 0x6E13, 0x8C6C, 0x7566,\n\t0x8C6D, 0x7A3D, 0x8C6E, 0x7CFB, 0x8C6F, 0x7D4C, 0x8C70, 0x7D99,\n\t0x8C71, 0x7E4B, 0x8C72, 0x7F6B, 0x8C73, 0x830E, 0x8C74, 0x834A,\n\t0x8C75, 0x86CD, 0x8C76, 0x8A08, 0x8C77, 0x8A63, 0x8C78, 0x8B66,\n\t0x8C79, 0x8EFD, 0x8C7A, 0x981A, 0x8C7B, 0x9D8F, 0x8C7C, 0x82B8,\n\t0x8C7D, 0x8FCE, 0x8C7E, 0x9BE8, 0x8C80, 0x5287, 0x8C81, 0x621F,\n\t0x8C82, 0x6483, 0x8C83, 0x6FC0, 0x8C84, 0x9699, 0x8C85, 0x6841,\n\t0x8C86, 0x5091, 0x8C87, 0x6B20, 0x8C88, 0x6C7A, 0x8C89, 0x6F54,\n\t0x8C8A, 0x7A74, 0x8C8B, 0x7D50, 0x8C8C, 0x8840, 0x8C8D, 0x8A23,\n\t0x8C8E, 0x6708, 0x8C8F, 0x4EF6, 0x8C90, 0x5039, 0x8C91, 0x5026,\n\t0x8C92, 0x5065, 0x8C93, 0x517C, 0x8C94, 0x5238, 0x8C95, 0x5263,\n\t0x8C96, 0x55A7, 0x8C97, 0x570F, 0x8C98, 0x5805, 0x8C99, 0x5ACC,\n\t0x8C9A, 0x5EFA, 0x8C9B, 0x61B2, 0x8C9C, 0x61F8, 0x8C9D, 0x62F3,\n\t0x8C9E, 0x6372, 0x8C9F, 0x691C, 0x8CA0, 0x6A29, 0x8CA1, 0x727D,\n\t0x8CA2, 0x72AC, 0x8CA3, 0x732E, 0x8CA4, 0x7814, 0x8CA5, 0x786F,\n\t0x8CA6, 0x7D79, 0x8CA7, 0x770C, 0x8CA8, 0x80A9, 0x8CA9, 0x898B,\n\t0x8CAA, 0x8B19, 0x8CAB, 0x8CE2, 0x8CAC, 0x8ED2, 0x8CAD, 0x9063,\n\t0x8CAE, 0x9375, 0x8CAF, 0x967A, 0x8CB0, 0x9855, 0x8CB1, 0x9A13,\n\t0x8CB2, 0x9E78, 0x8CB3, 0x5143, 0x8CB4, 0x539F, 0x8CB5, 0x53B3,\n\t0x8CB6, 0x5E7B, 0x8CB7, 0x5F26, 0x8CB8, 0x6E1B, 0x8CB9, 0x6E90,\n\t0x8CBA, 0x7384, 0x8CBB, 0x73FE, 0x8CBC, 0x7D43, 0x8CBD, 0x8237,\n\t0x8CBE, 0x8A00, 0x8CBF, 0x8AFA, 0x8CC0, 0x9650, 0x8CC1, 0x4E4E,\n\t0x8CC2, 0x500B, 0x8CC3, 0x53E4, 0x8CC4, 0x547C, 0x8CC5, 0x56FA,\n\t0x8CC6, 0x59D1, 0x8CC7, 0x5B64, 0x8CC8, 0x5DF1, 0x8CC9, 0x5EAB,\n\t0x8CCA, 0x5F27, 0x8CCB, 0x6238, 0x8CCC, 0x6545, 0x8CCD, 0x67AF,\n\t0x8CCE, 0x6E56, 0x8CCF, 0x72D0, 0x8CD0, 0x7CCA, 0x8CD1, 0x88B4,\n\t0x8CD2, 0x80A1, 0x8CD3, 0x80E1, 0x8CD4, 0x83F0, 0x8CD5, 0x864E,\n\t0x8CD6, 0x8A87, 0x8CD7, 0x8DE8, 0x8CD8, 0x9237, 0x8CD9, 0x96C7,\n\t0x8CDA, 0x9867, 0x8CDB, 0x9F13, 0x8CDC, 0x4E94, 0x8CDD, 0x4E92,\n\t0x8CDE, 0x4F0D, 0x8CDF, 0x5348, 0x8CE0, 0x5449, 0x8CE1, 0x543E,\n\t0x8CE2, 0x5A2F, 0x8CE3, 0x5F8C, 0x8CE4, 0x5FA1, 0x8CE5, 0x609F,\n\t0x8CE6, 0x68A7, 0x8CE7, 0x6A8E, 0x8CE8, 0x745A, 0x8CE9, 0x7881,\n\t0x8CEA, 0x8A9E, 0x8CEB, 0x8AA4, 0x8CEC, 0x8B77, 0x8CED, 0x9190,\n\t0x8CEE, 0x4E5E, 0x8CEF, 0x9BC9, 0x8CF0, 0x4EA4, 0x8CF1, 0x4F7C,\n\t0x8CF2, 0x4FAF, 0x8CF3, 0x5019, 0x8CF4, 0x5016, 0x8CF5, 0x5149,\n\t0x8CF6, 0x516C, 0x8CF7, 0x529F, 0x8CF8, 0x52B9, 0x8CF9, 0x52FE,\n\t0x8CFA, 0x539A, 0x8CFB, 0x53E3, 0x8CFC, 0x5411, 0x8D40, 0x540E,\n\t0x8D41, 0x5589, 0x8D42, 0x5751, 0x8D43, 0x57A2, 0x8D44, 0x597D,\n\t0x8D45, 0x5B54, 0x8D46, 0x5B5D, 0x8D47, 0x5B8F, 0x8D48, 0x5DE5,\n\t0x8D49, 0x5DE7, 0x8D4A, 0x5DF7, 0x8D4B, 0x5E78, 0x8D4C, 0x5E83,\n\t0x8D4D, 0x5E9A, 0x8D4E, 0x5EB7, 0x8D4F, 0x5F18, 0x8D50, 0x6052,\n\t0x8D51, 0x614C, 0x8D52, 0x6297, 0x8D53, 0x62D8, 0x8D54, 0x63A7,\n\t0x8D55, 0x653B, 0x8D56, 0x6602, 0x8D57, 0x6643, 0x8D58, 0x66F4,\n\t0x8D59, 0x676D, 0x8D5A, 0x6821, 0x8D5B, 0x6897, 0x8D5C, 0x69CB,\n\t0x8D5D, 0x6C5F, 0x8D5E, 0x6D2A, 0x8D5F, 0x6D69, 0x8D60, 0x6E2F,\n\t0x8D61, 0x6E9D, 0x8D62, 0x7532, 0x8D63, 0x7687, 0x8D64, 0x786C,\n\t0x8D65, 0x7A3F, 0x8D66, 0x7CE0, 0x8D67, 0x7D05, 0x8D68, 0x7D18,\n\t0x8D69, 0x7D5E, 0x8D6A, 0x7DB1, 0x8D6B, 0x8015, 0x8D6C, 0x8003,\n\t0x8D6D, 0x80AF, 0x8D6E, 0x80B1, 0x8D6F, 0x8154, 0x8D70, 0x818F,\n\t0x8D71, 0x822A, 0x8D72, 0x8352, 0x8D73, 0x884C, 0x8D74, 0x8861,\n\t0x8D75, 0x8B1B, 0x8D76, 0x8CA2, 0x8D77, 0x8CFC, 0x8D78, 0x90CA,\n\t0x8D79, 0x9175, 0x8D7A, 0x9271, 0x8D7B, 0x783F, 0x8D7C, 0x92FC,\n\t0x8D7D, 0x95A4, 0x8D7E, 0x964D, 0x8D80, 0x9805, 0x8D81, 0x9999,\n\t0x8D82, 0x9AD8, 0x8D83, 0x9D3B, 0x8D84, 0x525B, 0x8D85, 0x52AB,\n\t0x8D86, 0x53F7, 0x8D87, 0x5408, 0x8D88, 0x58D5, 0x8D89, 0x62F7,\n\t0x8D8A, 0x6FE0, 0x8D8B, 0x8C6A, 0x8D8C, 0x8F5F, 0x8D8D, 0x9EB9,\n\t0x8D8E, 0x514B, 0x8D8F, 0x523B, 0x8D90, 0x544A, 0x8D91, 0x56FD,\n\t0x8D92, 0x7A40, 0x8D93, 0x9177, 0x8D94, 0x9D60, 0x8D95, 0x9ED2,\n\t0x8D96, 0x7344, 0x8D97, 0x6F09, 0x8D98, 0x8170, 0x8D99, 0x7511,\n\t0x8D9A, 0x5FFD, 0x8D9B, 0x60DA, 0x8D9C, 0x9AA8, 0x8D9D, 0x72DB,\n\t0x8D9E, 0x8FBC, 0x8D9F, 0x6B64, 0x8DA0, 0x9803, 0x8DA1, 0x4ECA,\n\t0x8DA2, 0x56F0, 0x8DA3, 0x5764, 0x8DA4, 0x58BE, 0x8DA5, 0x5A5A,\n\t0x8DA6, 0x6068, 0x8DA7, 0x61C7, 0x8DA8, 0x660F, 0x8DA9, 0x6606,\n\t0x8DAA, 0x6839, 0x8DAB, 0x68B1, 0x8DAC, 0x6DF7, 0x8DAD, 0x75D5,\n\t0x8DAE, 0x7D3A, 0x8DAF, 0x826E, 0x8DB0, 0x9B42, 0x8DB1, 0x4E9B,\n\t0x8DB2, 0x4F50, 0x8DB3, 0x53C9, 0x8DB4, 0x5506, 0x8DB5, 0x5D6F,\n\t0x8DB6, 0x5DE6, 0x8DB7, 0x5DEE, 0x8DB8, 0x67FB, 0x8DB9, 0x6C99,\n\t0x8DBA, 0x7473, 0x8DBB, 0x7802, 0x8DBC, 0x8A50, 0x8DBD, 0x9396,\n\t0x8DBE, 0x88DF, 0x8DBF, 0x5750, 0x8DC0, 0x5EA7, 0x8DC1, 0x632B,\n\t0x8DC2, 0x50B5, 0x8DC3, 0x50AC, 0x8DC4, 0x518D, 0x8DC5, 0x6700,\n\t0x8DC6, 0x54C9, 0x8DC7, 0x585E, 0x8DC8, 0x59BB, 0x8DC9, 0x5BB0,\n\t0x8DCA, 0x5F69, 0x8DCB, 0x624D, 0x8DCC, 0x63A1, 0x8DCD, 0x683D,\n\t0x8DCE, 0x6B73, 0x8DCF, 0x6E08, 0x8DD0, 0x707D, 0x8DD1, 0x91C7,\n\t0x8DD2, 0x7280, 0x8DD3, 0x7815, 0x8DD4, 0x7826, 0x8DD5, 0x796D,\n\t0x8DD6, 0x658E, 0x8DD7, 0x7D30, 0x8DD8, 0x83DC, 0x8DD9, 0x88C1,\n\t0x8DDA, 0x8F09, 0x8DDB, 0x969B, 0x8DDC, 0x5264, 0x8DDD, 0x5728,\n\t0x8DDE, 0x6750, 0x8DDF, 0x7F6A, 0x8DE0, 0x8CA1, 0x8DE1, 0x51B4,\n\t0x8DE2, 0x5742, 0x8DE3, 0x962A, 0x8DE4, 0x583A, 0x8DE5, 0x698A,\n\t0x8DE6, 0x80B4, 0x8DE7, 0x54B2, 0x8DE8, 0x5D0E, 0x8DE9, 0x57FC,\n\t0x8DEA, 0x7895, 0x8DEB, 0x9DFA, 0x8DEC, 0x4F5C, 0x8DED, 0x524A,\n\t0x8DEE, 0x548B, 0x8DEF, 0x643E, 0x8DF0, 0x6628, 0x8DF1, 0x6714,\n\t0x8DF2, 0x67F5, 0x8DF3, 0x7A84, 0x8DF4, 0x7B56, 0x8DF5, 0x7D22,\n\t0x8DF6, 0x932F, 0x8DF7, 0x685C, 0x8DF8, 0x9BAD, 0x8DF9, 0x7B39,\n\t0x8DFA, 0x5319, 0x8DFB, 0x518A, 0x8DFC, 0x5237, 0x8E40, 0x5BDF,\n\t0x8E41, 0x62F6, 0x8E42, 0x64AE, 0x8E43, 0x64E6, 0x8E44, 0x672D,\n\t0x8E45, 0x6BBA, 0x8E46, 0x85A9, 0x8E47, 0x96D1, 0x8E48, 0x7690,\n\t0x8E49, 0x9BD6, 0x8E4A, 0x634C, 0x8E4B, 0x9306, 0x8E4C, 0x9BAB,\n\t0x8E4D, 0x76BF, 0x8E4E, 0x6652, 0x8E4F, 0x4E09, 0x8E50, 0x5098,\n\t0x8E51, 0x53C2, 0x8E52, 0x5C71, 0x8E53, 0x60E8, 0x8E54, 0x6492,\n\t0x8E55, 0x6563, 0x8E56, 0x685F, 0x8E57, 0x71E6, 0x8E58, 0x73CA,\n\t0x8E59, 0x7523, 0x8E5A, 0x7B97, 0x8E5B, 0x7E82, 0x8E5C, 0x8695,\n\t0x8E5D, 0x8B83, 0x8E5E, 0x8CDB, 0x8E5F, 0x9178, 0x8E60, 0x9910,\n\t0x8E61, 0x65AC, 0x8E62, 0x66AB, 0x8E63, 0x6B8B, 0x8E64, 0x4ED5,\n\t0x8E65, 0x4ED4, 0x8E66, 0x4F3A, 0x8E67, 0x4F7F, 0x8E68, 0x523A,\n\t0x8E69, 0x53F8, 0x8E6A, 0x53F2, 0x8E6B, 0x55E3, 0x8E6C, 0x56DB,\n\t0x8E6D, 0x58EB, 0x8E6E, 0x59CB, 0x8E6F, 0x59C9, 0x8E70, 0x59FF,\n\t0x8E71, 0x5B50, 0x8E72, 0x5C4D, 0x8E73, 0x5E02, 0x8E74, 0x5E2B,\n\t0x8E75, 0x5FD7, 0x8E76, 0x601D, 0x8E77, 0x6307, 0x8E78, 0x652F,\n\t0x8E79, 0x5B5C, 0x8E7A, 0x65AF, 0x8E7B, 0x65BD, 0x8E7C, 0x65E8,\n\t0x8E7D, 0x679D, 0x8E7E, 0x6B62, 0x8E80, 0x6B7B, 0x8E81, 0x6C0F,\n\t0x8E82, 0x7345, 0x8E83, 0x7949, 0x8E84, 0x79C1, 0x8E85, 0x7CF8,\n\t0x8E86, 0x7D19, 0x8E87, 0x7D2B, 0x8E88, 0x80A2, 0x8E89, 0x8102,\n\t0x8E8A, 0x81F3, 0x8E8B, 0x8996, 0x8E8C, 0x8A5E, 0x8E8D, 0x8A69,\n\t0x8E8E, 0x8A66, 0x8E8F, 0x8A8C, 0x8E90, 0x8AEE, 0x8E91, 0x8CC7,\n\t0x8E92, 0x8CDC, 0x8E93, 0x96CC, 0x8E94, 0x98FC, 0x8E95, 0x6B6F,\n\t0x8E96, 0x4E8B, 0x8E97, 0x4F3C, 0x8E98, 0x4F8D, 0x8E99, 0x5150,\n\t0x8E9A, 0x5B57, 0x8E9B, 0x5BFA, 0x8E9C, 0x6148, 0x8E9D, 0x6301,\n\t0x8E9E, 0x6642, 0x8E9F, 0x6B21, 0x8EA0, 0x6ECB, 0x8EA1, 0x6CBB,\n\t0x8EA2, 0x723E, 0x8EA3, 0x74BD, 0x8EA4, 0x75D4, 0x8EA5, 0x78C1,\n\t0x8EA6, 0x793A, 0x8EA7, 0x800C, 0x8EA8, 0x8033, 0x8EA9, 0x81EA,\n\t0x8EAA, 0x8494, 0x8EAB, 0x8F9E, 0x8EAC, 0x6C50, 0x8EAD, 0x9E7F,\n\t0x8EAE, 0x5F0F, 0x8EAF, 0x8B58, 0x8EB0, 0x9D2B, 0x8EB1, 0x7AFA,\n\t0x8EB2, 0x8EF8, 0x8EB3, 0x5B8D, 0x8EB4, 0x96EB, 0x8EB5, 0x4E03,\n\t0x8EB6, 0x53F1, 0x8EB7, 0x57F7, 0x8EB8, 0x5931, 0x8EB9, 0x5AC9,\n\t0x8EBA, 0x5BA4, 0x8EBB, 0x6089, 0x8EBC, 0x6E7F, 0x8EBD, 0x6F06,\n\t0x8EBE, 0x75BE, 0x8EBF, 0x8CEA, 0x8EC0, 0x5B9F, 0x8EC1, 0x8500,\n\t0x8EC2, 0x7BE0, 0x8EC3, 0x5072, 0x8EC4, 0x67F4, 0x8EC5, 0x829D,\n\t0x8EC6, 0x5C61, 0x8EC7, 0x854A, 0x8EC8, 0x7E1E, 0x8EC9, 0x820E,\n\t0x8ECA, 0x5199, 0x8ECB, 0x5C04, 0x8ECC, 0x6368, 0x8ECD, 0x8D66,\n\t0x8ECE, 0x659C, 0x8ECF, 0x716E, 0x8ED0, 0x793E, 0x8ED1, 0x7D17,\n\t0x8ED2, 0x8005, 0x8ED3, 0x8B1D, 0x8ED4, 0x8ECA, 0x8ED5, 0x906E,\n\t0x8ED6, 0x86C7, 0x8ED7, 0x90AA, 0x8ED8, 0x501F, 0x8ED9, 0x52FA,\n\t0x8EDA, 0x5C3A, 0x8EDB, 0x6753, 0x8EDC, 0x707C, 0x8EDD, 0x7235,\n\t0x8EDE, 0x914C, 0x8EDF, 0x91C8, 0x8EE0, 0x932B, 0x8EE1, 0x82E5,\n\t0x8EE2, 0x5BC2, 0x8EE3, 0x5F31, 0x8EE4, 0x60F9, 0x8EE5, 0x4E3B,\n\t0x8EE6, 0x53D6, 0x8EE7, 0x5B88, 0x8EE8, 0x624B, 0x8EE9, 0x6731,\n\t0x8EEA, 0x6B8A, 0x8EEB, 0x72E9, 0x8EEC, 0x73E0, 0x8EED, 0x7A2E,\n\t0x8EEE, 0x816B, 0x8EEF, 0x8DA3, 0x8EF0, 0x9152, 0x8EF1, 0x9996,\n\t0x8EF2, 0x5112, 0x8EF3, 0x53D7, 0x8EF4, 0x546A, 0x8EF5, 0x5BFF,\n\t0x8EF6, 0x6388, 0x8EF7, 0x6A39, 0x8EF8, 0x7DAC, 0x8EF9, 0x9700,\n\t0x8EFA, 0x56DA, 0x8EFB, 0x53CE, 0x8EFC, 0x5468, 0x8F40, 0x5B97,\n\t0x8F41, 0x5C31, 0x8F42, 0x5DDE, 0x8F43, 0x4FEE, 0x8F44, 0x6101,\n\t0x8F45, 0x62FE, 0x8F46, 0x6D32, 0x8F47, 0x79C0, 0x8F48, 0x79CB,\n\t0x8F49, 0x7D42, 0x8F4A, 0x7E4D, 0x8F4B, 0x7FD2, 0x8F4C, 0x81ED,\n\t0x8F4D, 0x821F, 0x8F4E, 0x8490, 0x8F4F, 0x8846, 0x8F50, 0x8972,\n\t0x8F51, 0x8B90, 0x8F52, 0x8E74, 0x8F53, 0x8F2F, 0x8F54, 0x9031,\n\t0x8F55, 0x914B, 0x8F56, 0x916C, 0x8F57, 0x96C6, 0x8F58, 0x919C,\n\t0x8F59, 0x4EC0, 0x8F5A, 0x4F4F, 0x8F5B, 0x5145, 0x8F5C, 0x5341,\n\t0x8F5D, 0x5F93, 0x8F5E, 0x620E, 0x8F5F, 0x67D4, 0x8F60, 0x6C41,\n\t0x8F61, 0x6E0B, 0x8F62, 0x7363, 0x8F63, 0x7E26, 0x8F64, 0x91CD,\n\t0x8F65, 0x9283, 0x8F66, 0x53D4, 0x8F67, 0x5919, 0x8F68, 0x5BBF,\n\t0x8F69, 0x6DD1, 0x8F6A, 0x795D, 0x8F6B, 0x7E2E, 0x8F6C, 0x7C9B,\n\t0x8F6D, 0x587E, 0x8F6E, 0x719F, 0x8F6F, 0x51FA, 0x8F70, 0x8853,\n\t0x8F71, 0x8FF0, 0x8F72, 0x4FCA, 0x8F73, 0x5CFB, 0x8F74, 0x6625,\n\t0x8F75, 0x77AC, 0x8F76, 0x7AE3, 0x8F77, 0x821C, 0x8F78, 0x99FF,\n\t0x8F79, 0x51C6, 0x8F7A, 0x5FAA, 0x8F7B, 0x65EC, 0x8F7C, 0x696F,\n\t0x8F7D, 0x6B89, 0x8F7E, 0x6DF3, 0x8F80, 0x6E96, 0x8F81, 0x6F64,\n\t0x8F82, 0x76FE, 0x8F83, 0x7D14, 0x8F84, 0x5DE1, 0x8F85, 0x9075,\n\t0x8F86, 0x9187, 0x8F87, 0x9806, 0x8F88, 0x51E6, 0x8F89, 0x521D,\n\t0x8F8A, 0x6240, 0x8F8B, 0x6691, 0x8F8C, 0x66D9, 0x8F8D, 0x6E1A,\n\t0x8F8E, 0x5EB6, 0x8F8F, 0x7DD2, 0x8F90, 0x7F72, 0x8F91, 0x66F8,\n\t0x8F92, 0x85AF, 0x8F93, 0x85F7, 0x8F94, 0x8AF8, 0x8F95, 0x52A9,\n\t0x8F96, 0x53D9, 0x8F97, 0x5973, 0x8F98, 0x5E8F, 0x8F99, 0x5F90,\n\t0x8F9A, 0x6055, 0x8F9B, 0x92E4, 0x8F9C, 0x9664, 0x8F9D, 0x50B7,\n\t0x8F9E, 0x511F, 0x8F9F, 0x52DD, 0x8FA0, 0x5320, 0x8FA1, 0x5347,\n\t0x8FA2, 0x53EC, 0x8FA3, 0x54E8, 0x8FA4, 0x5546, 0x8FA5, 0x5531,\n\t0x8FA6, 0x5617, 0x8FA7, 0x5968, 0x8FA8, 0x59BE, 0x8FA9, 0x5A3C,\n\t0x8FAA, 0x5BB5, 0x8FAB, 0x5C06, 0x8FAC, 0x5C0F, 0x8FAD, 0x5C11,\n\t0x8FAE, 0x5C1A, 0x8FAF, 0x5E84, 0x8FB0, 0x5E8A, 0x8FB1, 0x5EE0,\n\t0x8FB2, 0x5F70, 0x8FB3, 0x627F, 0x8FB4, 0x6284, 0x8FB5, 0x62DB,\n\t0x8FB6, 0x638C, 0x8FB7, 0x6377, 0x8FB8, 0x6607, 0x8FB9, 0x660C,\n\t0x8FBA, 0x662D, 0x8FBB, 0x6676, 0x8FBC, 0x677E, 0x8FBD, 0x68A2,\n\t0x8FBE, 0x6A1F, 0x8FBF, 0x6A35, 0x8FC0, 0x6CBC, 0x8FC1, 0x6D88,\n\t0x8FC2, 0x6E09, 0x8FC3, 0x6E58, 0x8FC4, 0x713C, 0x8FC5, 0x7126,\n\t0x8FC6, 0x7167, 0x8FC7, 0x75C7, 0x8FC8, 0x7701, 0x8FC9, 0x785D,\n\t0x8FCA, 0x7901, 0x8FCB, 0x7965, 0x8FCC, 0x79F0, 0x8FCD, 0x7AE0,\n\t0x8FCE, 0x7B11, 0x8FCF, 0x7CA7, 0x8FD0, 0x7D39, 0x8FD1, 0x8096,\n\t0x8FD2, 0x83D6, 0x8FD3, 0x848B, 0x8FD4, 0x8549, 0x8FD5, 0x885D,\n\t0x8FD6, 0x88F3, 0x8FD7, 0x8A1F, 0x8FD8, 0x8A3C, 0x8FD9, 0x8A54,\n\t0x8FDA, 0x8A73, 0x8FDB, 0x8C61, 0x8FDC, 0x8CDE, 0x8FDD, 0x91A4,\n\t0x8FDE, 0x9266, 0x8FDF, 0x937E, 0x8FE0, 0x9418, 0x8FE1, 0x969C,\n\t0x8FE2, 0x9798, 0x8FE3, 0x4E0A, 0x8FE4, 0x4E08, 0x8FE5, 0x4E1E,\n\t0x8FE6, 0x4E57, 0x8FE7, 0x5197, 0x8FE8, 0x5270, 0x8FE9, 0x57CE,\n\t0x8FEA, 0x5834, 0x8FEB, 0x58CC, 0x8FEC, 0x5B22, 0x8FED, 0x5E38,\n\t0x8FEE, 0x60C5, 0x8FEF, 0x64FE, 0x8FF0, 0x6761, 0x8FF1, 0x6756,\n\t0x8FF2, 0x6D44, 0x8FF3, 0x72B6, 0x8FF4, 0x7573, 0x8FF5, 0x7A63,\n\t0x8FF6, 0x84B8, 0x8FF7, 0x8B72, 0x8FF8, 0x91B8, 0x8FF9, 0x9320,\n\t0x8FFA, 0x5631, 0x8FFB, 0x57F4, 0x8FFC, 0x98FE, 0x9040, 0x62ED,\n\t0x9041, 0x690D, 0x9042, 0x6B96, 0x9043, 0x71ED, 0x9044, 0x7E54,\n\t0x9045, 0x8077, 0x9046, 0x8272, 0x9047, 0x89E6, 0x9048, 0x98DF,\n\t0x9049, 0x8755, 0x904A, 0x8FB1, 0x904B, 0x5C3B, 0x904C, 0x4F38,\n\t0x904D, 0x4FE1, 0x904E, 0x4FB5, 0x904F, 0x5507, 0x9050, 0x5A20,\n\t0x9051, 0x5BDD, 0x9052, 0x5BE9, 0x9053, 0x5FC3, 0x9054, 0x614E,\n\t0x9055, 0x632F, 0x9056, 0x65B0, 0x9057, 0x664B, 0x9058, 0x68EE,\n\t0x9059, 0x699B, 0x905A, 0x6D78, 0x905B, 0x6DF1, 0x905C, 0x7533,\n\t0x905D, 0x75B9, 0x905E, 0x771F, 0x905F, 0x795E, 0x9060, 0x79E6,\n\t0x9061, 0x7D33, 0x9062, 0x81E3, 0x9063, 0x82AF, 0x9064, 0x85AA,\n\t0x9065, 0x89AA, 0x9066, 0x8A3A, 0x9067, 0x8EAB, 0x9068, 0x8F9B,\n\t0x9069, 0x9032, 0x906A, 0x91DD, 0x906B, 0x9707, 0x906C, 0x4EBA,\n\t0x906D, 0x4EC1, 0x906E, 0x5203, 0x906F, 0x5875, 0x9070, 0x58EC,\n\t0x9071, 0x5C0B, 0x9072, 0x751A, 0x9073, 0x5C3D, 0x9074, 0x814E,\n\t0x9075, 0x8A0A, 0x9076, 0x8FC5, 0x9077, 0x9663, 0x9078, 0x976D,\n\t0x9079, 0x7B25, 0x907A, 0x8ACF, 0x907B, 0x9808, 0x907C, 0x9162,\n\t0x907D, 0x56F3, 0x907E, 0x53A8, 0x9080, 0x9017, 0x9081, 0x5439,\n\t0x9082, 0x5782, 0x9083, 0x5E25, 0x9084, 0x63A8, 0x9085, 0x6C34,\n\t0x9086, 0x708A, 0x9087, 0x7761, 0x9088, 0x7C8B, 0x9089, 0x7FE0,\n\t0x908A, 0x8870, 0x908B, 0x9042, 0x908C, 0x9154, 0x908D, 0x9310,\n\t0x908E, 0x9318, 0x908F, 0x968F, 0x9090, 0x745E, 0x9091, 0x9AC4,\n\t0x9092, 0x5D07, 0x9093, 0x5D69, 0x9094, 0x6570, 0x9095, 0x67A2,\n\t0x9096, 0x8DA8, 0x9097, 0x96DB, 0x9098, 0x636E, 0x9099, 0x6749,\n\t0x909A, 0x6919, 0x909B, 0x83C5, 0x909C, 0x9817, 0x909D, 0x96C0,\n\t0x909E, 0x88FE, 0x909F, 0x6F84, 0x90A0, 0x647A, 0x90A1, 0x5BF8,\n\t0x90A2, 0x4E16, 0x90A3, 0x702C, 0x90A4, 0x755D, 0x90A5, 0x662F,\n\t0x90A6, 0x51C4, 0x90A7, 0x5236, 0x90A8, 0x52E2, 0x90A9, 0x59D3,\n\t0x90AA, 0x5F81, 0x90AB, 0x6027, 0x90AC, 0x6210, 0x90AD, 0x653F,\n\t0x90AE, 0x6574, 0x90AF, 0x661F, 0x90B0, 0x6674, 0x90B1, 0x68F2,\n\t0x90B2, 0x6816, 0x90B3, 0x6B63, 0x90B4, 0x6E05, 0x90B5, 0x7272,\n\t0x90B6, 0x751F, 0x90B7, 0x76DB, 0x90B8, 0x7CBE, 0x90B9, 0x8056,\n\t0x90BA, 0x58F0, 0x90BB, 0x88FD, 0x90BC, 0x897F, 0x90BD, 0x8AA0,\n\t0x90BE, 0x8A93, 0x90BF, 0x8ACB, 0x90C0, 0x901D, 0x90C1, 0x9192,\n\t0x90C2, 0x9752, 0x90C3, 0x9759, 0x90C4, 0x6589, 0x90C5, 0x7A0E,\n\t0x90C6, 0x8106, 0x90C7, 0x96BB, 0x90C8, 0x5E2D, 0x90C9, 0x60DC,\n\t0x90CA, 0x621A, 0x90CB, 0x65A5, 0x90CC, 0x6614, 0x90CD, 0x6790,\n\t0x90CE, 0x77F3, 0x90CF, 0x7A4D, 0x90D0, 0x7C4D, 0x90D1, 0x7E3E,\n\t0x90D2, 0x810A, 0x90D3, 0x8CAC, 0x90D4, 0x8D64, 0x90D5, 0x8DE1,\n\t0x90D6, 0x8E5F, 0x90D7, 0x78A9, 0x90D8, 0x5207, 0x90D9, 0x62D9,\n\t0x90DA, 0x63A5, 0x90DB, 0x6442, 0x90DC, 0x6298, 0x90DD, 0x8A2D,\n\t0x90DE, 0x7A83, 0x90DF, 0x7BC0, 0x90E0, 0x8AAC, 0x90E1, 0x96EA,\n\t0x90E2, 0x7D76, 0x90E3, 0x820C, 0x90E4, 0x8749, 0x90E5, 0x4ED9,\n\t0x90E6, 0x5148, 0x90E7, 0x5343, 0x90E8, 0x5360, 0x90E9, 0x5BA3,\n\t0x90EA, 0x5C02, 0x90EB, 0x5C16, 0x90EC, 0x5DDD, 0x90ED, 0x6226,\n\t0x90EE, 0x6247, 0x90EF, 0x64B0, 0x90F0, 0x6813, 0x90F1, 0x6834,\n\t0x90F2, 0x6CC9, 0x90F3, 0x6D45, 0x90F4, 0x6D17, 0x90F5, 0x67D3,\n\t0x90F6, 0x6F5C, 0x90F7, 0x714E, 0x90F8, 0x717D, 0x90F9, 0x65CB,\n\t0x90FA, 0x7A7F, 0x90FB, 0x7BAD, 0x90FC, 0x7DDA, 0x9140, 0x7E4A,\n\t0x9141, 0x7FA8, 0x9142, 0x817A, 0x9143, 0x821B, 0x9144, 0x8239,\n\t0x9145, 0x85A6, 0x9146, 0x8A6E, 0x9147, 0x8CCE, 0x9148, 0x8DF5,\n\t0x9149, 0x9078, 0x914A, 0x9077, 0x914B, 0x92AD, 0x914C, 0x9291,\n\t0x914D, 0x9583, 0x914E, 0x9BAE, 0x914F, 0x524D, 0x9150, 0x5584,\n\t0x9151, 0x6F38, 0x9152, 0x7136, 0x9153, 0x5168, 0x9154, 0x7985,\n\t0x9155, 0x7E55, 0x9156, 0x81B3, 0x9157, 0x7CCE, 0x9158, 0x564C,\n\t0x9159, 0x5851, 0x915A, 0x5CA8, 0x915B, 0x63AA, 0x915C, 0x66FE,\n\t0x915D, 0x66FD, 0x915E, 0x695A, 0x915F, 0x72D9, 0x9160, 0x758F,\n\t0x9161, 0x758E, 0x9162, 0x790E, 0x9163, 0x7956, 0x9164, 0x79DF,\n\t0x9165, 0x7C97, 0x9166, 0x7D20, 0x9167, 0x7D44, 0x9168, 0x8607,\n\t0x9169, 0x8A34, 0x916A, 0x963B, 0x916B, 0x9061, 0x916C, 0x9F20,\n\t0x916D, 0x50E7, 0x916E, 0x5275, 0x916F, 0x53CC, 0x9170, 0x53E2,\n\t0x9171, 0x5009, 0x9172, 0x55AA, 0x9173, 0x58EE, 0x9174, 0x594F,\n\t0x9175, 0x723D, 0x9176, 0x5B8B, 0x9177, 0x5C64, 0x9178, 0x531D,\n\t0x9179, 0x60E3, 0x917A, 0x60F3, 0x917B, 0x635C, 0x917C, 0x6383,\n\t0x917D, 0x633F, 0x917E, 0x63BB, 0x9180, 0x64CD, 0x9181, 0x65E9,\n\t0x9182, 0x66F9, 0x9183, 0x5DE3, 0x9184, 0x69CD, 0x9185, 0x69FD,\n\t0x9186, 0x6F15, 0x9187, 0x71E5, 0x9188, 0x4E89, 0x9189, 0x75E9,\n\t0x918A, 0x76F8, 0x918B, 0x7A93, 0x918C, 0x7CDF, 0x918D, 0x7DCF,\n\t0x918E, 0x7D9C, 0x918F, 0x8061, 0x9190, 0x8349, 0x9191, 0x8358,\n\t0x9192, 0x846C, 0x9193, 0x84BC, 0x9194, 0x85FB, 0x9195, 0x88C5,\n\t0x9196, 0x8D70, 0x9197, 0x9001, 0x9198, 0x906D, 0x9199, 0x9397,\n\t0x919A, 0x971C, 0x919B, 0x9A12, 0x919C, 0x50CF, 0x919D, 0x5897,\n\t0x919E, 0x618E, 0x919F, 0x81D3, 0x91A0, 0x8535, 0x91A1, 0x8D08,\n\t0x91A2, 0x9020, 0x91A3, 0x4FC3, 0x91A4, 0x5074, 0x91A5, 0x5247,\n\t0x91A6, 0x5373, 0x91A7, 0x606F, 0x91A8, 0x6349, 0x91A9, 0x675F,\n\t0x91AA, 0x6E2C, 0x91AB, 0x8DB3, 0x91AC, 0x901F, 0x91AD, 0x4FD7,\n\t0x91AE, 0x5C5E, 0x91AF, 0x8CCA, 0x91B0, 0x65CF, 0x91B1, 0x7D9A,\n\t0x91B2, 0x5352, 0x91B3, 0x8896, 0x91B4, 0x5176, 0x91B5, 0x63C3,\n\t0x91B6, 0x5B58, 0x91B7, 0x5B6B, 0x91B8, 0x5C0A, 0x91B9, 0x640D,\n\t0x91BA, 0x6751, 0x91BB, 0x905C, 0x91BC, 0x4ED6, 0x91BD, 0x591A,\n\t0x91BE, 0x592A, 0x91BF, 0x6C70, 0x91C0, 0x8A51, 0x91C1, 0x553E,\n\t0x91C2, 0x5815, 0x91C3, 0x59A5, 0x91C4, 0x60F0, 0x91C5, 0x6253,\n\t0x91C6, 0x67C1, 0x91C7, 0x8235, 0x91C8, 0x6955, 0x91C9, 0x9640,\n\t0x91CA, 0x99C4, 0x91CB, 0x9A28, 0x91CC, 0x4F53, 0x91CD, 0x5806,\n\t0x91CE, 0x5BFE, 0x91CF, 0x8010, 0x91D0, 0x5CB1, 0x91D1, 0x5E2F,\n\t0x91D2, 0x5F85, 0x91D3, 0x6020, 0x91D4, 0x614B, 0x91D5, 0x6234,\n\t0x91D6, 0x66FF, 0x91D7, 0x6CF0, 0x91D8, 0x6EDE, 0x91D9, 0x80CE,\n\t0x91DA, 0x817F, 0x91DB, 0x82D4, 0x91DC, 0x888B, 0x91DD, 0x8CB8,\n\t0x91DE, 0x9000, 0x91DF, 0x902E, 0x91E0, 0x968A, 0x91E1, 0x9EDB,\n\t0x91E2, 0x9BDB, 0x91E3, 0x4EE3, 0x91E4, 0x53F0, 0x91E5, 0x5927,\n\t0x91E6, 0x7B2C, 0x91E7, 0x918D, 0x91E8, 0x984C, 0x91E9, 0x9DF9,\n\t0x91EA, 0x6EDD, 0x91EB, 0x7027, 0x91EC, 0x5353, 0x91ED, 0x5544,\n\t0x91EE, 0x5B85, 0x91EF, 0x6258, 0x91F0, 0x629E, 0x91F1, 0x62D3,\n\t0x91F2, 0x6CA2, 0x91F3, 0x6FEF, 0x91F4, 0x7422, 0x91F5, 0x8A17,\n\t0x91F6, 0x9438, 0x91F7, 0x6FC1, 0x91F8, 0x8AFE, 0x91F9, 0x8338,\n\t0x91FA, 0x51E7, 0x91FB, 0x86F8, 0x91FC, 0x53EA, 0x9240, 0x53E9,\n\t0x9241, 0x4F46, 0x9242, 0x9054, 0x9243, 0x8FB0, 0x9244, 0x596A,\n\t0x9245, 0x8131, 0x9246, 0x5DFD, 0x9247, 0x7AEA, 0x9248, 0x8FBF,\n\t0x9249, 0x68DA, 0x924A, 0x8C37, 0x924B, 0x72F8, 0x924C, 0x9C48,\n\t0x924D, 0x6A3D, 0x924E, 0x8AB0, 0x924F, 0x4E39, 0x9250, 0x5358,\n\t0x9251, 0x5606, 0x9252, 0x5766, 0x9253, 0x62C5, 0x9254, 0x63A2,\n\t0x9255, 0x65E6, 0x9256, 0x6B4E, 0x9257, 0x6DE1, 0x9258, 0x6E5B,\n\t0x9259, 0x70AD, 0x925A, 0x77ED, 0x925B, 0x7AEF, 0x925C, 0x7BAA,\n\t0x925D, 0x7DBB, 0x925E, 0x803D, 0x925F, 0x80C6, 0x9260, 0x86CB,\n\t0x9261, 0x8A95, 0x9262, 0x935B, 0x9263, 0x56E3, 0x9264, 0x58C7,\n\t0x9265, 0x5F3E, 0x9266, 0x65AD, 0x9267, 0x6696, 0x9268, 0x6A80,\n\t0x9269, 0x6BB5, 0x926A, 0x7537, 0x926B, 0x8AC7, 0x926C, 0x5024,\n\t0x926D, 0x77E5, 0x926E, 0x5730, 0x926F, 0x5F1B, 0x9270, 0x6065,\n\t0x9271, 0x667A, 0x9272, 0x6C60, 0x9273, 0x75F4, 0x9274, 0x7A1A,\n\t0x9275, 0x7F6E, 0x9276, 0x81F4, 0x9277, 0x8718, 0x9278, 0x9045,\n\t0x9279, 0x99B3, 0x927A, 0x7BC9, 0x927B, 0x755C, 0x927C, 0x7AF9,\n\t0x927D, 0x7B51, 0x927E, 0x84C4, 0x9280, 0x9010, 0x9281, 0x79E9,\n\t0x9282, 0x7A92, 0x9283, 0x8336, 0x9284, 0x5AE1, 0x9285, 0x7740,\n\t0x9286, 0x4E2D, 0x9287, 0x4EF2, 0x9288, 0x5B99, 0x9289, 0x5FE0,\n\t0x928A, 0x62BD, 0x928B, 0x663C, 0x928C, 0x67F1, 0x928D, 0x6CE8,\n\t0x928E, 0x866B, 0x928F, 0x8877, 0x9290, 0x8A3B, 0x9291, 0x914E,\n\t0x9292, 0x92F3, 0x9293, 0x99D0, 0x9294, 0x6A17, 0x9295, 0x7026,\n\t0x9296, 0x732A, 0x9297, 0x82E7, 0x9298, 0x8457, 0x9299, 0x8CAF,\n\t0x929A, 0x4E01, 0x929B, 0x5146, 0x929C, 0x51CB, 0x929D, 0x558B,\n\t0x929E, 0x5BF5, 0x929F, 0x5E16, 0x92A0, 0x5E33, 0x92A1, 0x5E81,\n\t0x92A2, 0x5F14, 0x92A3, 0x5F35, 0x92A4, 0x5F6B, 0x92A5, 0x5FB4,\n\t0x92A6, 0x61F2, 0x92A7, 0x6311, 0x92A8, 0x66A2, 0x92A9, 0x671D,\n\t0x92AA, 0x6F6E, 0x92AB, 0x7252, 0x92AC, 0x753A, 0x92AD, 0x773A,\n\t0x92AE, 0x8074, 0x92AF, 0x8139, 0x92B0, 0x8178, 0x92B1, 0x8776,\n\t0x92B2, 0x8ABF, 0x92B3, 0x8ADC, 0x92B4, 0x8D85, 0x92B5, 0x8DF3,\n\t0x92B6, 0x929A, 0x92B7, 0x9577, 0x92B8, 0x9802, 0x92B9, 0x9CE5,\n\t0x92BA, 0x52C5, 0x92BB, 0x6357, 0x92BC, 0x76F4, 0x92BD, 0x6715,\n\t0x92BE, 0x6C88, 0x92BF, 0x73CD, 0x92C0, 0x8CC3, 0x92C1, 0x93AE,\n\t0x92C2, 0x9673, 0x92C3, 0x6D25, 0x92C4, 0x589C, 0x92C5, 0x690E,\n\t0x92C6, 0x69CC, 0x92C7, 0x8FFD, 0x92C8, 0x939A, 0x92C9, 0x75DB,\n\t0x92CA, 0x901A, 0x92CB, 0x585A, 0x92CC, 0x6802, 0x92CD, 0x63B4,\n\t0x92CE, 0x69FB, 0x92CF, 0x4F43, 0x92D0, 0x6F2C, 0x92D1, 0x67D8,\n\t0x92D2, 0x8FBB, 0x92D3, 0x8526, 0x92D4, 0x7DB4, 0x92D5, 0x9354,\n\t0x92D6, 0x693F, 0x92D7, 0x6F70, 0x92D8, 0x576A, 0x92D9, 0x58F7,\n\t0x92DA, 0x5B2C, 0x92DB, 0x7D2C, 0x92DC, 0x722A, 0x92DD, 0x540A,\n\t0x92DE, 0x91E3, 0x92DF, 0x9DB4, 0x92E0, 0x4EAD, 0x92E1, 0x4F4E,\n\t0x92E2, 0x505C, 0x92E3, 0x5075, 0x92E4, 0x5243, 0x92E5, 0x8C9E,\n\t0x92E6, 0x5448, 0x92E7, 0x5824, 0x92E8, 0x5B9A, 0x92E9, 0x5E1D,\n\t0x92EA, 0x5E95, 0x92EB, 0x5EAD, 0x92EC, 0x5EF7, 0x92ED, 0x5F1F,\n\t0x92EE, 0x608C, 0x92EF, 0x62B5, 0x92F0, 0x633A, 0x92F1, 0x63D0,\n\t0x92F2, 0x68AF, 0x92F3, 0x6C40, 0x92F4, 0x7887, 0x92F5, 0x798E,\n\t0x92F6, 0x7A0B, 0x92F7, 0x7DE0, 0x92F8, 0x8247, 0x92F9, 0x8A02,\n\t0x92FA, 0x8AE6, 0x92FB, 0x8E44, 0x92FC, 0x9013, 0x9340, 0x90B8,\n\t0x9341, 0x912D, 0x9342, 0x91D8, 0x9343, 0x9F0E, 0x9344, 0x6CE5,\n\t0x9345, 0x6458, 0x9346, 0x64E2, 0x9347, 0x6575, 0x9348, 0x6EF4,\n\t0x9349, 0x7684, 0x934A, 0x7B1B, 0x934B, 0x9069, 0x934C, 0x93D1,\n\t0x934D, 0x6EBA, 0x934E, 0x54F2, 0x934F, 0x5FB9, 0x9350, 0x64A4,\n\t0x9351, 0x8F4D, 0x9352, 0x8FED, 0x9353, 0x9244, 0x9354, 0x5178,\n\t0x9355, 0x586B, 0x9356, 0x5929, 0x9357, 0x5C55, 0x9358, 0x5E97,\n\t0x9359, 0x6DFB, 0x935A, 0x7E8F, 0x935B, 0x751C, 0x935C, 0x8CBC,\n\t0x935D, 0x8EE2, 0x935E, 0x985B, 0x935F, 0x70B9, 0x9360, 0x4F1D,\n\t0x9361, 0x6BBF, 0x9362, 0x6FB1, 0x9363, 0x7530, 0x9364, 0x96FB,\n\t0x9365, 0x514E, 0x9366, 0x5410, 0x9367, 0x5835, 0x9368, 0x5857,\n\t0x9369, 0x59AC, 0x936A, 0x5C60, 0x936B, 0x5F92, 0x936C, 0x6597,\n\t0x936D, 0x675C, 0x936E, 0x6E21, 0x936F, 0x767B, 0x9370, 0x83DF,\n\t0x9371, 0x8CED, 0x9372, 0x9014, 0x9373, 0x90FD, 0x9374, 0x934D,\n\t0x9375, 0x7825, 0x9376, 0x783A, 0x9377, 0x52AA, 0x9378, 0x5EA6,\n\t0x9379, 0x571F, 0x937A, 0x5974, 0x937B, 0x6012, 0x937C, 0x5012,\n\t0x937D, 0x515A, 0x937E, 0x51AC, 0x9380, 0x51CD, 0x9381, 0x5200,\n\t0x9382, 0x5510, 0x9383, 0x5854, 0x9384, 0x5858, 0x9385, 0x5957,\n\t0x9386, 0x5B95, 0x9387, 0x5CF6, 0x9388, 0x5D8B, 0x9389, 0x60BC,\n\t0x938A, 0x6295, 0x938B, 0x642D, 0x938C, 0x6771, 0x938D, 0x6843,\n\t0x938E, 0x68BC, 0x938F, 0x68DF, 0x9390, 0x76D7, 0x9391, 0x6DD8,\n\t0x9392, 0x6E6F, 0x9393, 0x6D9B, 0x9394, 0x706F, 0x9395, 0x71C8,\n\t0x9396, 0x5F53, 0x9397, 0x75D8, 0x9398, 0x7977, 0x9399, 0x7B49,\n\t0x939A, 0x7B54, 0x939B, 0x7B52, 0x939C, 0x7CD6, 0x939D, 0x7D71,\n\t0x939E, 0x5230, 0x939F, 0x8463, 0x93A0, 0x8569, 0x93A1, 0x85E4,\n\t0x93A2, 0x8A0E, 0x93A3, 0x8B04, 0x93A4, 0x8C46, 0x93A5, 0x8E0F,\n\t0x93A6, 0x9003, 0x93A7, 0x900F, 0x93A8, 0x9419, 0x93A9, 0x9676,\n\t0x93AA, 0x982D, 0x93AB, 0x9A30, 0x93AC, 0x95D8, 0x93AD, 0x50CD,\n\t0x93AE, 0x52D5, 0x93AF, 0x540C, 0x93B0, 0x5802, 0x93B1, 0x5C0E,\n\t0x93B2, 0x61A7, 0x93B3, 0x649E, 0x93B4, 0x6D1E, 0x93B5, 0x77B3,\n\t0x93B6, 0x7AE5, 0x93B7, 0x80F4, 0x93B8, 0x8404, 0x93B9, 0x9053,\n\t0x93BA, 0x9285, 0x93BB, 0x5CE0, 0x93BC, 0x9D07, 0x93BD, 0x533F,\n\t0x93BE, 0x5F97, 0x93BF, 0x5FB3, 0x93C0, 0x6D9C, 0x93C1, 0x7279,\n\t0x93C2, 0x7763, 0x93C3, 0x79BF, 0x93C4, 0x7BE4, 0x93C5, 0x6BD2,\n\t0x93C6, 0x72EC, 0x93C7, 0x8AAD, 0x93C8, 0x6803, 0x93C9, 0x6A61,\n\t0x93CA, 0x51F8, 0x93CB, 0x7A81, 0x93CC, 0x6934, 0x93CD, 0x5C4A,\n\t0x93CE, 0x9CF6, 0x93CF, 0x82EB, 0x93D0, 0x5BC5, 0x93D1, 0x9149,\n\t0x93D2, 0x701E, 0x93D3, 0x5678, 0x93D4, 0x5C6F, 0x93D5, 0x60C7,\n\t0x93D6, 0x6566, 0x93D7, 0x6C8C, 0x93D8, 0x8C5A, 0x93D9, 0x9041,\n\t0x93DA, 0x9813, 0x93DB, 0x5451, 0x93DC, 0x66C7, 0x93DD, 0x920D,\n\t0x93DE, 0x5948, 0x93DF, 0x90A3, 0x93E0, 0x5185, 0x93E1, 0x4E4D,\n\t0x93E2, 0x51EA, 0x93E3, 0x8599, 0x93E4, 0x8B0E, 0x93E5, 0x7058,\n\t0x93E6, 0x637A, 0x93E7, 0x934B, 0x93E8, 0x6962, 0x93E9, 0x99B4,\n\t0x93EA, 0x7E04, 0x93EB, 0x7577, 0x93EC, 0x5357, 0x93ED, 0x6960,\n\t0x93EE, 0x8EDF, 0x93EF, 0x96E3, 0x93F0, 0x6C5D, 0x93F1, 0x4E8C,\n\t0x93F2, 0x5C3C, 0x93F3, 0x5F10, 0x93F4, 0x8FE9, 0x93F5, 0x5302,\n\t0x93F6, 0x8CD1, 0x93F7, 0x8089, 0x93F8, 0x8679, 0x93F9, 0x5EFF,\n\t0x93FA, 0x65E5, 0x93FB, 0x4E73, 0x93FC, 0x5165, 0x9440, 0x5982,\n\t0x9441, 0x5C3F, 0x9442, 0x97EE, 0x9443, 0x4EFB, 0x9444, 0x598A,\n\t0x9445, 0x5FCD, 0x9446, 0x8A8D, 0x9447, 0x6FE1, 0x9448, 0x79B0,\n\t0x9449, 0x7962, 0x944A, 0x5BE7, 0x944B, 0x8471, 0x944C, 0x732B,\n\t0x944D, 0x71B1, 0x944E, 0x5E74, 0x944F, 0x5FF5, 0x9450, 0x637B,\n\t0x9451, 0x649A, 0x9452, 0x71C3, 0x9453, 0x7C98, 0x9454, 0x4E43,\n\t0x9455, 0x5EFC, 0x9456, 0x4E4B, 0x9457, 0x57DC, 0x9458, 0x56A2,\n\t0x9459, 0x60A9, 0x945A, 0x6FC3, 0x945B, 0x7D0D, 0x945C, 0x80FD,\n\t0x945D, 0x8133, 0x945E, 0x81BF, 0x945F, 0x8FB2, 0x9460, 0x8997,\n\t0x9461, 0x86A4, 0x9462, 0x5DF4, 0x9463, 0x628A, 0x9464, 0x64AD,\n\t0x9465, 0x8987, 0x9466, 0x6777, 0x9467, 0x6CE2, 0x9468, 0x6D3E,\n\t0x9469, 0x7436, 0x946A, 0x7834, 0x946B, 0x5A46, 0x946C, 0x7F75,\n\t0x946D, 0x82AD, 0x946E, 0x99AC, 0x946F, 0x4FF3, 0x9470, 0x5EC3,\n\t0x9471, 0x62DD, 0x9472, 0x6392, 0x9473, 0x6557, 0x9474, 0x676F,\n\t0x9475, 0x76C3, 0x9476, 0x724C, 0x9477, 0x80CC, 0x9478, 0x80BA,\n\t0x9479, 0x8F29, 0x947A, 0x914D, 0x947B, 0x500D, 0x947C, 0x57F9,\n\t0x947D, 0x5A92, 0x947E, 0x6885, 0x9480, 0x6973, 0x9481, 0x7164,\n\t0x9482, 0x72FD, 0x9483, 0x8CB7, 0x9484, 0x58F2, 0x9485, 0x8CE0,\n\t0x9486, 0x966A, 0x9487, 0x9019, 0x9488, 0x877F, 0x9489, 0x79E4,\n\t0x948A, 0x77E7, 0x948B, 0x8429, 0x948C, 0x4F2F, 0x948D, 0x5265,\n\t0x948E, 0x535A, 0x948F, 0x62CD, 0x9490, 0x67CF, 0x9491, 0x6CCA,\n\t0x9492, 0x767D, 0x9493, 0x7B94, 0x9494, 0x7C95, 0x9495, 0x8236,\n\t0x9496, 0x8584, 0x9497, 0x8FEB, 0x9498, 0x66DD, 0x9499, 0x6F20,\n\t0x949A, 0x7206, 0x949B, 0x7E1B, 0x949C, 0x83AB, 0x949D, 0x99C1,\n\t0x949E, 0x9EA6, 0x949F, 0x51FD, 0x94A0, 0x7BB1, 0x94A1, 0x7872,\n\t0x94A2, 0x7BB8, 0x94A3, 0x8087, 0x94A4, 0x7B48, 0x94A5, 0x6AE8,\n\t0x94A6, 0x5E61, 0x94A7, 0x808C, 0x94A8, 0x7551, 0x94A9, 0x7560,\n\t0x94AA, 0x516B, 0x94AB, 0x9262, 0x94AC, 0x6E8C, 0x94AD, 0x767A,\n\t0x94AE, 0x9197, 0x94AF, 0x9AEA, 0x94B0, 0x4F10, 0x94B1, 0x7F70,\n\t0x94B2, 0x629C, 0x94B3, 0x7B4F, 0x94B4, 0x95A5, 0x94B5, 0x9CE9,\n\t0x94B6, 0x567A, 0x94B7, 0x5859, 0x94B8, 0x86E4, 0x94B9, 0x96BC,\n\t0x94BA, 0x4F34, 0x94BB, 0x5224, 0x94BC, 0x534A, 0x94BD, 0x53CD,\n\t0x94BE, 0x53DB, 0x94BF, 0x5E06, 0x94C0, 0x642C, 0x94C1, 0x6591,\n\t0x94C2, 0x677F, 0x94C3, 0x6C3E, 0x94C4, 0x6C4E, 0x94C5, 0x7248,\n\t0x94C6, 0x72AF, 0x94C7, 0x73ED, 0x94C8, 0x7554, 0x94C9, 0x7E41,\n\t0x94CA, 0x822C, 0x94CB, 0x85E9, 0x94CC, 0x8CA9, 0x94CD, 0x7BC4,\n\t0x94CE, 0x91C6, 0x94CF, 0x7169, 0x94D0, 0x9812, 0x94D1, 0x98EF,\n\t0x94D2, 0x633D, 0x94D3, 0x6669, 0x94D4, 0x756A, 0x94D5, 0x76E4,\n\t0x94D6, 0x78D0, 0x94D7, 0x8543, 0x94D8, 0x86EE, 0x94D9, 0x532A,\n\t0x94DA, 0x5351, 0x94DB, 0x5426, 0x94DC, 0x5983, 0x94DD, 0x5E87,\n\t0x94DE, 0x5F7C, 0x94DF, 0x60B2, 0x94E0, 0x6249, 0x94E1, 0x6279,\n\t0x94E2, 0x62AB, 0x94E3, 0x6590, 0x94E4, 0x6BD4, 0x94E5, 0x6CCC,\n\t0x94E6, 0x75B2, 0x94E7, 0x76AE, 0x94E8, 0x7891, 0x94E9, 0x79D8,\n\t0x94EA, 0x7DCB, 0x94EB, 0x7F77, 0x94EC, 0x80A5, 0x94ED, 0x88AB,\n\t0x94EE, 0x8AB9, 0x94EF, 0x8CBB, 0x94F0, 0x907F, 0x94F1, 0x975E,\n\t0x94F2, 0x98DB, 0x94F3, 0x6A0B, 0x94F4, 0x7C38, 0x94F5, 0x5099,\n\t0x94F6, 0x5C3E, 0x94F7, 0x5FAE, 0x94F8, 0x6787, 0x94F9, 0x6BD8,\n\t0x94FA, 0x7435, 0x94FB, 0x7709, 0x94FC, 0x7F8E, 0x9540, 0x9F3B,\n\t0x9541, 0x67CA, 0x9542, 0x7A17, 0x9543, 0x5339, 0x9544, 0x758B,\n\t0x9545, 0x9AED, 0x9546, 0x5F66, 0x9547, 0x819D, 0x9548, 0x83F1,\n\t0x9549, 0x8098, 0x954A, 0x5F3C, 0x954B, 0x5FC5, 0x954C, 0x7562,\n\t0x954D, 0x7B46, 0x954E, 0x903C, 0x954F, 0x6867, 0x9550, 0x59EB,\n\t0x9551, 0x5A9B, 0x9552, 0x7D10, 0x9553, 0x767E, 0x9554, 0x8B2C,\n\t0x9555, 0x4FF5, 0x9556, 0x5F6A, 0x9557, 0x6A19, 0x9558, 0x6C37,\n\t0x9559, 0x6F02, 0x955A, 0x74E2, 0x955B, 0x7968, 0x955C, 0x8868,\n\t0x955D, 0x8A55, 0x955E, 0x8C79, 0x955F, 0x5EDF, 0x9560, 0x63CF,\n\t0x9561, 0x75C5, 0x9562, 0x79D2, 0x9563, 0x82D7, 0x9564, 0x9328,\n\t0x9565, 0x92F2, 0x9566, 0x849C, 0x9567, 0x86ED, 0x9568, 0x9C2D,\n\t0x9569, 0x54C1, 0x956A, 0x5F6C, 0x956B, 0x658C, 0x956C, 0x6D5C,\n\t0x956D, 0x7015, 0x956E, 0x8CA7, 0x956F, 0x8CD3, 0x9570, 0x983B,\n\t0x9571, 0x654F, 0x9572, 0x74F6, 0x9573, 0x4E0D, 0x9574, 0x4ED8,\n\t0x9575, 0x57E0, 0x9576, 0x592B, 0x9577, 0x5A66, 0x9578, 0x5BCC,\n\t0x9579, 0x51A8, 0x957A, 0x5E03, 0x957B, 0x5E9C, 0x957C, 0x6016,\n\t0x957D, 0x6276, 0x957E, 0x6577, 0x9580, 0x65A7, 0x9581, 0x666E,\n\t0x9582, 0x6D6E, 0x9583, 0x7236, 0x9584, 0x7B26, 0x9585, 0x8150,\n\t0x9586, 0x819A, 0x9587, 0x8299, 0x9588, 0x8B5C, 0x9589, 0x8CA0,\n\t0x958A, 0x8CE6, 0x958B, 0x8D74, 0x958C, 0x961C, 0x958D, 0x9644,\n\t0x958E, 0x4FAE, 0x958F, 0x64AB, 0x9590, 0x6B66, 0x9591, 0x821E,\n\t0x9592, 0x8461, 0x9593, 0x856A, 0x9594, 0x90E8, 0x9595, 0x5C01,\n\t0x9596, 0x6953, 0x9597, 0x98A8, 0x9598, 0x847A, 0x9599, 0x8557,\n\t0x959A, 0x4F0F, 0x959B, 0x526F, 0x959C, 0x5FA9, 0x959D, 0x5E45,\n\t0x959E, 0x670D, 0x959F, 0x798F, 0x95A0, 0x8179, 0x95A1, 0x8907,\n\t0x95A2, 0x8986, 0x95A3, 0x6DF5, 0x95A4, 0x5F17, 0x95A5, 0x6255,\n\t0x95A6, 0x6CB8, 0x95A7, 0x4ECF, 0x95A8, 0x7269, 0x95A9, 0x9B92,\n\t0x95AA, 0x5206, 0x95AB, 0x543B, 0x95AC, 0x5674, 0x95AD, 0x58B3,\n\t0x95AE, 0x61A4, 0x95AF, 0x626E, 0x95B0, 0x711A, 0x95B1, 0x596E,\n\t0x95B2, 0x7C89, 0x95B3, 0x7CDE, 0x95B4, 0x7D1B, 0x95B5, 0x96F0,\n\t0x95B6, 0x6587, 0x95B7, 0x805E, 0x95B8, 0x4E19, 0x95B9, 0x4F75,\n\t0x95BA, 0x5175, 0x95BB, 0x5840, 0x95BC, 0x5E63, 0x95BD, 0x5E73,\n\t0x95BE, 0x5F0A, 0x95BF, 0x67C4, 0x95C0, 0x4E26, 0x95C1, 0x853D,\n\t0x95C2, 0x9589, 0x95C3, 0x965B, 0x95C4, 0x7C73, 0x95C5, 0x9801,\n\t0x95C6, 0x50FB, 0x95C7, 0x58C1, 0x95C8, 0x7656, 0x95C9, 0x78A7,\n\t0x95CA, 0x5225, 0x95CB, 0x77A5, 0x95CC, 0x8511, 0x95CD, 0x7B86,\n\t0x95CE, 0x504F, 0x95CF, 0x5909, 0x95D0, 0x7247, 0x95D1, 0x7BC7,\n\t0x95D2, 0x7DE8, 0x95D3, 0x8FBA, 0x95D4, 0x8FD4, 0x95D5, 0x904D,\n\t0x95D6, 0x4FBF, 0x95D7, 0x52C9, 0x95D8, 0x5A29, 0x95D9, 0x5F01,\n\t0x95DA, 0x97AD, 0x95DB, 0x4FDD, 0x95DC, 0x8217, 0x95DD, 0x92EA,\n\t0x95DE, 0x5703, 0x95DF, 0x6355, 0x95E0, 0x6B69, 0x95E1, 0x752B,\n\t0x95E2, 0x88DC, 0x95E3, 0x8F14, 0x95E4, 0x7A42, 0x95E5, 0x52DF,\n\t0x95E6, 0x5893, 0x95E7, 0x6155, 0x95E8, 0x620A, 0x95E9, 0x66AE,\n\t0x95EA, 0x6BCD, 0x95EB, 0x7C3F, 0x95EC, 0x83E9, 0x95ED, 0x5023,\n\t0x95EE, 0x4FF8, 0x95EF, 0x5305, 0x95F0, 0x5446, 0x95F1, 0x5831,\n\t0x95F2, 0x5949, 0x95F3, 0x5B9D, 0x95F4, 0x5CF0, 0x95F5, 0x5CEF,\n\t0x95F6, 0x5D29, 0x95F7, 0x5E96, 0x95F8, 0x62B1, 0x95F9, 0x6367,\n\t0x95FA, 0x653E, 0x95FB, 0x65B9, 0x95FC, 0x670B, 0x9640, 0x6CD5,\n\t0x9641, 0x6CE1, 0x9642, 0x70F9, 0x9643, 0x7832, 0x9644, 0x7E2B,\n\t0x9645, 0x80DE, 0x9646, 0x82B3, 0x9647, 0x840C, 0x9648, 0x84EC,\n\t0x9649, 0x8702, 0x964A, 0x8912, 0x964B, 0x8A2A, 0x964C, 0x8C4A,\n\t0x964D, 0x90A6, 0x964E, 0x92D2, 0x964F, 0x98FD, 0x9650, 0x9CF3,\n\t0x9651, 0x9D6C, 0x9652, 0x4E4F, 0x9653, 0x4EA1, 0x9654, 0x508D,\n\t0x9655, 0x5256, 0x9656, 0x574A, 0x9657, 0x59A8, 0x9658, 0x5E3D,\n\t0x9659, 0x5FD8, 0x965A, 0x5FD9, 0x965B, 0x623F, 0x965C, 0x66B4,\n\t0x965D, 0x671B, 0x965E, 0x67D0, 0x965F, 0x68D2, 0x9660, 0x5192,\n\t0x9661, 0x7D21, 0x9662, 0x80AA, 0x9663, 0x81A8, 0x9664, 0x8B00,\n\t0x9665, 0x8C8C, 0x9666, 0x8CBF, 0x9667, 0x927E, 0x9668, 0x9632,\n\t0x9669, 0x5420, 0x966A, 0x982C, 0x966B, 0x5317, 0x966C, 0x50D5,\n\t0x966D, 0x535C, 0x966E, 0x58A8, 0x966F, 0x64B2, 0x9670, 0x6734,\n\t0x9671, 0x7267, 0x9672, 0x7766, 0x9673, 0x7A46, 0x9674, 0x91E6,\n\t0x9675, 0x52C3, 0x9676, 0x6CA1, 0x9677, 0x6B86, 0x9678, 0x5800,\n\t0x9679, 0x5E4C, 0x967A, 0x5954, 0x967B, 0x672C, 0x967C, 0x7FFB,\n\t0x967D, 0x51E1, 0x967E, 0x76C6, 0x9680, 0x6469, 0x9681, 0x78E8,\n\t0x9682, 0x9B54, 0x9683, 0x9EBB, 0x9684, 0x57CB, 0x9685, 0x59B9,\n\t0x9686, 0x6627, 0x9687, 0x679A, 0x9688, 0x6BCE, 0x9689, 0x54E9,\n\t0x968A, 0x69D9, 0x968B, 0x5E55, 0x968C, 0x819C, 0x968D, 0x6795,\n\t0x968E, 0x9BAA, 0x968F, 0x67FE, 0x9690, 0x9C52, 0x9691, 0x685D,\n\t0x9692, 0x4EA6, 0x9693, 0x4FE3, 0x9694, 0x53C8, 0x9695, 0x62B9,\n\t0x9696, 0x672B, 0x9697, 0x6CAB, 0x9698, 0x8FC4, 0x9699, 0x4FAD,\n\t0x969A, 0x7E6D, 0x969B, 0x9EBF, 0x969C, 0x4E07, 0x969D, 0x6162,\n\t0x969E, 0x6E80, 0x969F, 0x6F2B, 0x96A0, 0x8513, 0x96A1, 0x5473,\n\t0x96A2, 0x672A, 0x96A3, 0x9B45, 0x96A4, 0x5DF3, 0x96A5, 0x7B95,\n\t0x96A6, 0x5CAC, 0x96A7, 0x5BC6, 0x96A8, 0x871C, 0x96A9, 0x6E4A,\n\t0x96AA, 0x84D1, 0x96AB, 0x7A14, 0x96AC, 0x8108, 0x96AD, 0x5999,\n\t0x96AE, 0x7C8D, 0x96AF, 0x6C11, 0x96B0, 0x7720, 0x96B1, 0x52D9,\n\t0x96B2, 0x5922, 0x96B3, 0x7121, 0x96B4, 0x725F, 0x96B5, 0x77DB,\n\t0x96B6, 0x9727, 0x96B7, 0x9D61, 0x96B8, 0x690B, 0x96B9, 0x5A7F,\n\t0x96BA, 0x5A18, 0x96BB, 0x51A5, 0x96BC, 0x540D, 0x96BD, 0x547D,\n\t0x96BE, 0x660E, 0x96BF, 0x76DF, 0x96C0, 0x8FF7, 0x96C1, 0x9298,\n\t0x96C2, 0x9CF4, 0x96C3, 0x59EA, 0x96C4, 0x725D, 0x96C5, 0x6EC5,\n\t0x96C6, 0x514D, 0x96C7, 0x68C9, 0x96C8, 0x7DBF, 0x96C9, 0x7DEC,\n\t0x96CA, 0x9762, 0x96CB, 0x9EBA, 0x96CC, 0x6478, 0x96CD, 0x6A21,\n\t0x96CE, 0x8302, 0x96CF, 0x5984, 0x96D0, 0x5B5F, 0x96D1, 0x6BDB,\n\t0x96D2, 0x731B, 0x96D3, 0x76F2, 0x96D4, 0x7DB2, 0x96D5, 0x8017,\n\t0x96D6, 0x8499, 0x96D7, 0x5132, 0x96D8, 0x6728, 0x96D9, 0x9ED9,\n\t0x96DA, 0x76EE, 0x96DB, 0x6762, 0x96DC, 0x52FF, 0x96DD, 0x9905,\n\t0x96DE, 0x5C24, 0x96DF, 0x623B, 0x96E0, 0x7C7E, 0x96E1, 0x8CB0,\n\t0x96E2, 0x554F, 0x96E3, 0x60B6, 0x96E4, 0x7D0B, 0x96E5, 0x9580,\n\t0x96E6, 0x5301, 0x96E7, 0x4E5F, 0x96E8, 0x51B6, 0x96E9, 0x591C,\n\t0x96EA, 0x723A, 0x96EB, 0x8036, 0x96EC, 0x91CE, 0x96ED, 0x5F25,\n\t0x96EE, 0x77E2, 0x96EF, 0x5384, 0x96F0, 0x5F79, 0x96F1, 0x7D04,\n\t0x96F2, 0x85AC, 0x96F3, 0x8A33, 0x96F4, 0x8E8D, 0x96F5, 0x9756,\n\t0x96F6, 0x67F3, 0x96F7, 0x85AE, 0x96F8, 0x9453, 0x96F9, 0x6109,\n\t0x96FA, 0x6108, 0x96FB, 0x6CB9, 0x96FC, 0x7652, 0x9740, 0x8AED,\n\t0x9741, 0x8F38, 0x9742, 0x552F, 0x9743, 0x4F51, 0x9744, 0x512A,\n\t0x9745, 0x52C7, 0x9746, 0x53CB, 0x9747, 0x5BA5, 0x9748, 0x5E7D,\n\t0x9749, 0x60A0, 0x974A, 0x6182, 0x974B, 0x63D6, 0x974C, 0x6709,\n\t0x974D, 0x67DA, 0x974E, 0x6E67, 0x974F, 0x6D8C, 0x9750, 0x7336,\n\t0x9751, 0x7337, 0x9752, 0x7531, 0x9753, 0x7950, 0x9754, 0x88D5,\n\t0x9755, 0x8A98, 0x9756, 0x904A, 0x9757, 0x9091, 0x9758, 0x90F5,\n\t0x9759, 0x96C4, 0x975A, 0x878D, 0x975B, 0x5915, 0x975C, 0x4E88,\n\t0x975D, 0x4F59, 0x975E, 0x4E0E, 0x975F, 0x8A89, 0x9760, 0x8F3F,\n\t0x9761, 0x9810, 0x9762, 0x50AD, 0x9763, 0x5E7C, 0x9764, 0x5996,\n\t0x9765, 0x5BB9, 0x9766, 0x5EB8, 0x9767, 0x63DA, 0x9768, 0x63FA,\n\t0x9769, 0x64C1, 0x976A, 0x66DC, 0x976B, 0x694A, 0x976C, 0x69D8,\n\t0x976D, 0x6D0B, 0x976E, 0x6EB6, 0x976F, 0x7194, 0x9770, 0x7528,\n\t0x9771, 0x7AAF, 0x9772, 0x7F8A, 0x9773, 0x8000, 0x9774, 0x8449,\n\t0x9775, 0x84C9, 0x9776, 0x8981, 0x9777, 0x8B21, 0x9778, 0x8E0A,\n\t0x9779, 0x9065, 0x977A, 0x967D, 0x977B, 0x990A, 0x977C, 0x617E,\n\t0x977D, 0x6291, 0x977E, 0x6B32, 0x9780, 0x6C83, 0x9781, 0x6D74,\n\t0x9782, 0x7FCC, 0x9783, 0x7FFC, 0x9784, 0x6DC0, 0x9785, 0x7F85,\n\t0x9786, 0x87BA, 0x9787, 0x88F8, 0x9788, 0x6765, 0x9789, 0x83B1,\n\t0x978A, 0x983C, 0x978B, 0x96F7, 0x978C, 0x6D1B, 0x978D, 0x7D61,\n\t0x978E, 0x843D, 0x978F, 0x916A, 0x9790, 0x4E71, 0x9791, 0x5375,\n\t0x9792, 0x5D50, 0x9793, 0x6B04, 0x9794, 0x6FEB, 0x9795, 0x85CD,\n\t0x9796, 0x862D, 0x9797, 0x89A7, 0x9798, 0x5229, 0x9799, 0x540F,\n\t0x979A, 0x5C65, 0x979B, 0x674E, 0x979C, 0x68A8, 0x979D, 0x7406,\n\t0x979E, 0x7483, 0x979F, 0x75E2, 0x97A0, 0x88CF, 0x97A1, 0x88E1,\n\t0x97A2, 0x91CC, 0x97A3, 0x96E2, 0x97A4, 0x9678, 0x97A5, 0x5F8B,\n\t0x97A6, 0x7387, 0x97A7, 0x7ACB, 0x97A8, 0x844E, 0x97A9, 0x63A0,\n\t0x97AA, 0x7565, 0x97AB, 0x5289, 0x97AC, 0x6D41, 0x97AD, 0x6E9C,\n\t0x97AE, 0x7409, 0x97AF, 0x7559, 0x97B0, 0x786B, 0x97B1, 0x7C92,\n\t0x97B2, 0x9686, 0x97B3, 0x7ADC, 0x97B4, 0x9F8D, 0x97B5, 0x4FB6,\n\t0x97B6, 0x616E, 0x97B7, 0x65C5, 0x97B8, 0x865C, 0x97B9, 0x4E86,\n\t0x97BA, 0x4EAE, 0x97BB, 0x50DA, 0x97BC, 0x4E21, 0x97BD, 0x51CC,\n\t0x97BE, 0x5BEE, 0x97BF, 0x6599, 0x97C0, 0x6881, 0x97C1, 0x6DBC,\n\t0x97C2, 0x731F, 0x97C3, 0x7642, 0x97C4, 0x77AD, 0x97C5, 0x7A1C,\n\t0x97C6, 0x7CE7, 0x97C7, 0x826F, 0x97C8, 0x8AD2, 0x97C9, 0x907C,\n\t0x97CA, 0x91CF, 0x97CB, 0x9675, 0x97CC, 0x9818, 0x97CD, 0x529B,\n\t0x97CE, 0x7DD1, 0x97CF, 0x502B, 0x97D0, 0x5398, 0x97D1, 0x6797,\n\t0x97D2, 0x6DCB, 0x97D3, 0x71D0, 0x97D4, 0x7433, 0x97D5, 0x81E8,\n\t0x97D6, 0x8F2A, 0x97D7, 0x96A3, 0x97D8, 0x9C57, 0x97D9, 0x9E9F,\n\t0x97DA, 0x7460, 0x97DB, 0x5841, 0x97DC, 0x6D99, 0x97DD, 0x7D2F,\n\t0x97DE, 0x985E, 0x97DF, 0x4EE4, 0x97E0, 0x4F36, 0x97E1, 0x4F8B,\n\t0x97E2, 0x51B7, 0x97E3, 0x52B1, 0x97E4, 0x5DBA, 0x97E5, 0x601C,\n\t0x97E6, 0x73B2, 0x97E7, 0x793C, 0x97E8, 0x82D3, 0x97E9, 0x9234,\n\t0x97EA, 0x96B7, 0x97EB, 0x96F6, 0x97EC, 0x970A, 0x97ED, 0x9E97,\n\t0x97EE, 0x9F62, 0x97EF, 0x66A6, 0x97F0, 0x6B74, 0x97F1, 0x5217,\n\t0x97F2, 0x52A3, 0x97F3, 0x70C8, 0x97F4, 0x88C2, 0x97F5, 0x5EC9,\n\t0x97F6, 0x604B, 0x97F7, 0x6190, 0x97F8, 0x6F23, 0x97F9, 0x7149,\n\t0x97FA, 0x7C3E, 0x97FB, 0x7DF4, 0x97FC, 0x806F, 0x9840, 0x84EE,\n\t0x9841, 0x9023, 0x9842, 0x932C, 0x9843, 0x5442, 0x9844, 0x9B6F,\n\t0x9845, 0x6AD3, 0x9846, 0x7089, 0x9847, 0x8CC2, 0x9848, 0x8DEF,\n\t0x9849, 0x9732, 0x984A, 0x52B4, 0x984B, 0x5A41, 0x984C, 0x5ECA,\n\t0x984D, 0x5F04, 0x984E, 0x6717, 0x984F, 0x697C, 0x9850, 0x6994,\n\t0x9851, 0x6D6A, 0x9852, 0x6F0F, 0x9853, 0x7262, 0x9854, 0x72FC,\n\t0x9855, 0x7BED, 0x9856, 0x8001, 0x9857, 0x807E, 0x9858, 0x874B,\n\t0x9859, 0x90CE, 0x985A, 0x516D, 0x985B, 0x9E93, 0x985C, 0x7984,\n\t0x985D, 0x808B, 0x985E, 0x9332, 0x985F, 0x8AD6, 0x9860, 0x502D,\n\t0x9861, 0x548C, 0x9862, 0x8A71, 0x9863, 0x6B6A, 0x9864, 0x8CC4,\n\t0x9865, 0x8107, 0x9866, 0x60D1, 0x9867, 0x67A0, 0x9868, 0x9DF2,\n\t0x9869, 0x4E99, 0x986A, 0x4E98, 0x986B, 0x9C10, 0x986C, 0x8A6B,\n\t0x986D, 0x85C1, 0x986E, 0x8568, 0x986F, 0x6900, 0x9870, 0x6E7E,\n\t0x9871, 0x7897, 0x9872, 0x8155, 0x989F, 0x5F0C, 0x98A0, 0x4E10,\n\t0x98A1, 0x4E15, 0x98A2, 0x4E2A, 0x98A3, 0x4E31, 0x98A4, 0x4E36,\n\t0x98A5, 0x4E3C, 0x98A6, 0x4E3F, 0x98A7, 0x4E42, 0x98A8, 0x4E56,\n\t0x98A9, 0x4E58, 0x98AA, 0x4E82, 0x98AB, 0x4E85, 0x98AC, 0x8C6B,\n\t0x98AD, 0x4E8A, 0x98AE, 0x8212, 0x98AF, 0x5F0D, 0x98B0, 0x4E8E,\n\t0x98B1, 0x4E9E, 0x98B2, 0x4E9F, 0x98B3, 0x4EA0, 0x98B4, 0x4EA2,\n\t0x98B5, 0x4EB0, 0x98B6, 0x4EB3, 0x98B7, 0x4EB6, 0x98B8, 0x4ECE,\n\t0x98B9, 0x4ECD, 0x98BA, 0x4EC4, 0x98BB, 0x4EC6, 0x98BC, 0x4EC2,\n\t0x98BD, 0x4ED7, 0x98BE, 0x4EDE, 0x98BF, 0x4EED, 0x98C0, 0x4EDF,\n\t0x98C1, 0x4EF7, 0x98C2, 0x4F09, 0x98C3, 0x4F5A, 0x98C4, 0x4F30,\n\t0x98C5, 0x4F5B, 0x98C6, 0x4F5D, 0x98C7, 0x4F57, 0x98C8, 0x4F47,\n\t0x98C9, 0x4F76, 0x98CA, 0x4F88, 0x98CB, 0x4F8F, 0x98CC, 0x4F98,\n\t0x98CD, 0x4F7B, 0x98CE, 0x4F69, 0x98CF, 0x4F70, 0x98D0, 0x4F91,\n\t0x98D1, 0x4F6F, 0x98D2, 0x4F86, 0x98D3, 0x4F96, 0x98D4, 0x5118,\n\t0x98D5, 0x4FD4, 0x98D6, 0x4FDF, 0x98D7, 0x4FCE, 0x98D8, 0x4FD8,\n\t0x98D9, 0x4FDB, 0x98DA, 0x4FD1, 0x98DB, 0x4FDA, 0x98DC, 0x4FD0,\n\t0x98DD, 0x4FE4, 0x98DE, 0x4FE5, 0x98DF, 0x501A, 0x98E0, 0x5028,\n\t0x98E1, 0x5014, 0x98E2, 0x502A, 0x98E3, 0x5025, 0x98E4, 0x5005,\n\t0x98E5, 0x4F1C, 0x98E6, 0x4FF6, 0x98E7, 0x5021, 0x98E8, 0x5029,\n\t0x98E9, 0x502C, 0x98EA, 0x4FFE, 0x98EB, 0x4FEF, 0x98EC, 0x5011,\n\t0x98ED, 0x5006, 0x98EE, 0x5043, 0x98EF, 0x5047, 0x98F0, 0x6703,\n\t0x98F1, 0x5055, 0x98F2, 0x5050, 0x98F3, 0x5048, 0x98F4, 0x505A,\n\t0x98F5, 0x5056, 0x98F6, 0x506C, 0x98F7, 0x5078, 0x98F8, 0x5080,\n\t0x98F9, 0x509A, 0x98FA, 0x5085, 0x98FB, 0x50B4, 0x98FC, 0x50B2,\n\t0x9940, 0x50C9, 0x9941, 0x50CA, 0x9942, 0x50B3, 0x9943, 0x50C2,\n\t0x9944, 0x50D6, 0x9945, 0x50DE, 0x9946, 0x50E5, 0x9947, 0x50ED,\n\t0x9948, 0x50E3, 0x9949, 0x50EE, 0x994A, 0x50F9, 0x994B, 0x50F5,\n\t0x994C, 0x5109, 0x994D, 0x5101, 0x994E, 0x5102, 0x994F, 0x5116,\n\t0x9950, 0x5115, 0x9951, 0x5114, 0x9952, 0x511A, 0x9953, 0x5121,\n\t0x9954, 0x513A, 0x9955, 0x5137, 0x9956, 0x513C, 0x9957, 0x513B,\n\t0x9958, 0x513F, 0x9959, 0x5140, 0x995A, 0x5152, 0x995B, 0x514C,\n\t0x995C, 0x5154, 0x995D, 0x5162, 0x995E, 0x7AF8, 0x995F, 0x5169,\n\t0x9960, 0x516A, 0x9961, 0x516E, 0x9962, 0x5180, 0x9963, 0x5182,\n\t0x9964, 0x56D8, 0x9965, 0x518C, 0x9966, 0x5189, 0x9967, 0x518F,\n\t0x9968, 0x5191, 0x9969, 0x5193, 0x996A, 0x5195, 0x996B, 0x5196,\n\t0x996C, 0x51A4, 0x996D, 0x51A6, 0x996E, 0x51A2, 0x996F, 0x51A9,\n\t0x9970, 0x51AA, 0x9971, 0x51AB, 0x9972, 0x51B3, 0x9973, 0x51B1,\n\t0x9974, 0x51B2, 0x9975, 0x51B0, 0x9976, 0x51B5, 0x9977, 0x51BD,\n\t0x9978, 0x51C5, 0x9979, 0x51C9, 0x997A, 0x51DB, 0x997B, 0x51E0,\n\t0x997C, 0x8655, 0x997D, 0x51E9, 0x997E, 0x51ED, 0x9980, 0x51F0,\n\t0x9981, 0x51F5, 0x9982, 0x51FE, 0x9983, 0x5204, 0x9984, 0x520B,\n\t0x9985, 0x5214, 0x9986, 0x520E, 0x9987, 0x5227, 0x9988, 0x522A,\n\t0x9989, 0x522E, 0x998A, 0x5233, 0x998B, 0x5239, 0x998C, 0x524F,\n\t0x998D, 0x5244, 0x998E, 0x524B, 0x998F, 0x524C, 0x9990, 0x525E,\n\t0x9991, 0x5254, 0x9992, 0x526A, 0x9993, 0x5274, 0x9994, 0x5269,\n\t0x9995, 0x5273, 0x9996, 0x527F, 0x9997, 0x527D, 0x9998, 0x528D,\n\t0x9999, 0x5294, 0x999A, 0x5292, 0x999B, 0x5271, 0x999C, 0x5288,\n\t0x999D, 0x5291, 0x999E, 0x8FA8, 0x999F, 0x8FA7, 0x99A0, 0x52AC,\n\t0x99A1, 0x52AD, 0x99A2, 0x52BC, 0x99A3, 0x52B5, 0x99A4, 0x52C1,\n\t0x99A5, 0x52CD, 0x99A6, 0x52D7, 0x99A7, 0x52DE, 0x99A8, 0x52E3,\n\t0x99A9, 0x52E6, 0x99AA, 0x98ED, 0x99AB, 0x52E0, 0x99AC, 0x52F3,\n\t0x99AD, 0x52F5, 0x99AE, 0x52F8, 0x99AF, 0x52F9, 0x99B0, 0x5306,\n\t0x99B1, 0x5308, 0x99B2, 0x7538, 0x99B3, 0x530D, 0x99B4, 0x5310,\n\t0x99B5, 0x530F, 0x99B6, 0x5315, 0x99B7, 0x531A, 0x99B8, 0x5323,\n\t0x99B9, 0x532F, 0x99BA, 0x5331, 0x99BB, 0x5333, 0x99BC, 0x5338,\n\t0x99BD, 0x5340, 0x99BE, 0x5346, 0x99BF, 0x5345, 0x99C0, 0x4E17,\n\t0x99C1, 0x5349, 0x99C2, 0x534D, 0x99C3, 0x51D6, 0x99C4, 0x535E,\n\t0x99C5, 0x5369, 0x99C6, 0x536E, 0x99C7, 0x5918, 0x99C8, 0x537B,\n\t0x99C9, 0x5377, 0x99CA, 0x5382, 0x99CB, 0x5396, 0x99CC, 0x53A0,\n\t0x99CD, 0x53A6, 0x99CE, 0x53A5, 0x99CF, 0x53AE, 0x99D0, 0x53B0,\n\t0x99D1, 0x53B6, 0x99D2, 0x53C3, 0x99D3, 0x7C12, 0x99D4, 0x96D9,\n\t0x99D5, 0x53DF, 0x99D6, 0x66FC, 0x99D7, 0x71EE, 0x99D8, 0x53EE,\n\t0x99D9, 0x53E8, 0x99DA, 0x53ED, 0x99DB, 0x53FA, 0x99DC, 0x5401,\n\t0x99DD, 0x543D, 0x99DE, 0x5440, 0x99DF, 0x542C, 0x99E0, 0x542D,\n\t0x99E1, 0x543C, 0x99E2, 0x542E, 0x99E3, 0x5436, 0x99E4, 0x5429,\n\t0x99E5, 0x541D, 0x99E6, 0x544E, 0x99E7, 0x548F, 0x99E8, 0x5475,\n\t0x99E9, 0x548E, 0x99EA, 0x545F, 0x99EB, 0x5471, 0x99EC, 0x5477,\n\t0x99ED, 0x5470, 0x99EE, 0x5492, 0x99EF, 0x547B, 0x99F0, 0x5480,\n\t0x99F1, 0x5476, 0x99F2, 0x5484, 0x99F3, 0x5490, 0x99F4, 0x5486,\n\t0x99F5, 0x54C7, 0x99F6, 0x54A2, 0x99F7, 0x54B8, 0x99F8, 0x54A5,\n\t0x99F9, 0x54AC, 0x99FA, 0x54C4, 0x99FB, 0x54C8, 0x99FC, 0x54A8,\n\t0x9A40, 0x54AB, 0x9A41, 0x54C2, 0x9A42, 0x54A4, 0x9A43, 0x54BE,\n\t0x9A44, 0x54BC, 0x9A45, 0x54D8, 0x9A46, 0x54E5, 0x9A47, 0x54E6,\n\t0x9A48, 0x550F, 0x9A49, 0x5514, 0x9A4A, 0x54FD, 0x9A4B, 0x54EE,\n\t0x9A4C, 0x54ED, 0x9A4D, 0x54FA, 0x9A4E, 0x54E2, 0x9A4F, 0x5539,\n\t0x9A50, 0x5540, 0x9A51, 0x5563, 0x9A52, 0x554C, 0x9A53, 0x552E,\n\t0x9A54, 0x555C, 0x9A55, 0x5545, 0x9A56, 0x5556, 0x9A57, 0x5557,\n\t0x9A58, 0x5538, 0x9A59, 0x5533, 0x9A5A, 0x555D, 0x9A5B, 0x5599,\n\t0x9A5C, 0x5580, 0x9A5D, 0x54AF, 0x9A5E, 0x558A, 0x9A5F, 0x559F,\n\t0x9A60, 0x557B, 0x9A61, 0x557E, 0x9A62, 0x5598, 0x9A63, 0x559E,\n\t0x9A64, 0x55AE, 0x9A65, 0x557C, 0x9A66, 0x5583, 0x9A67, 0x55A9,\n\t0x9A68, 0x5587, 0x9A69, 0x55A8, 0x9A6A, 0x55DA, 0x9A6B, 0x55C5,\n\t0x9A6C, 0x55DF, 0x9A6D, 0x55C4, 0x9A6E, 0x55DC, 0x9A6F, 0x55E4,\n\t0x9A70, 0x55D4, 0x9A71, 0x5614, 0x9A72, 0x55F7, 0x9A73, 0x5616,\n\t0x9A74, 0x55FE, 0x9A75, 0x55FD, 0x9A76, 0x561B, 0x9A77, 0x55F9,\n\t0x9A78, 0x564E, 0x9A79, 0x5650, 0x9A7A, 0x71DF, 0x9A7B, 0x5634,\n\t0x9A7C, 0x5636, 0x9A7D, 0x5632, 0x9A7E, 0x5638, 0x9A80, 0x566B,\n\t0x9A81, 0x5664, 0x9A82, 0x562F, 0x9A83, 0x566C, 0x9A84, 0x566A,\n\t0x9A85, 0x5686, 0x9A86, 0x5680, 0x9A87, 0x568A, 0x9A88, 0x56A0,\n\t0x9A89, 0x5694, 0x9A8A, 0x568F, 0x9A8B, 0x56A5, 0x9A8C, 0x56AE,\n\t0x9A8D, 0x56B6, 0x9A8E, 0x56B4, 0x9A8F, 0x56C2, 0x9A90, 0x56BC,\n\t0x9A91, 0x56C1, 0x9A92, 0x56C3, 0x9A93, 0x56C0, 0x9A94, 0x56C8,\n\t0x9A95, 0x56CE, 0x9A96, 0x56D1, 0x9A97, 0x56D3, 0x9A98, 0x56D7,\n\t0x9A99, 0x56EE, 0x9A9A, 0x56F9, 0x9A9B, 0x5700, 0x9A9C, 0x56FF,\n\t0x9A9D, 0x5704, 0x9A9E, 0x5709, 0x9A9F, 0x5708, 0x9AA0, 0x570B,\n\t0x9AA1, 0x570D, 0x9AA2, 0x5713, 0x9AA3, 0x5718, 0x9AA4, 0x5716,\n\t0x9AA5, 0x55C7, 0x9AA6, 0x571C, 0x9AA7, 0x5726, 0x9AA8, 0x5737,\n\t0x9AA9, 0x5738, 0x9AAA, 0x574E, 0x9AAB, 0x573B, 0x9AAC, 0x5740,\n\t0x9AAD, 0x574F, 0x9AAE, 0x5769, 0x9AAF, 0x57C0, 0x9AB0, 0x5788,\n\t0x9AB1, 0x5761, 0x9AB2, 0x577F, 0x9AB3, 0x5789, 0x9AB4, 0x5793,\n\t0x9AB5, 0x57A0, 0x9AB6, 0x57B3, 0x9AB7, 0x57A4, 0x9AB8, 0x57AA,\n\t0x9AB9, 0x57B0, 0x9ABA, 0x57C3, 0x9ABB, 0x57C6, 0x9ABC, 0x57D4,\n\t0x9ABD, 0x57D2, 0x9ABE, 0x57D3, 0x9ABF, 0x580A, 0x9AC0, 0x57D6,\n\t0x9AC1, 0x57E3, 0x9AC2, 0x580B, 0x9AC3, 0x5819, 0x9AC4, 0x581D,\n\t0x9AC5, 0x5872, 0x9AC6, 0x5821, 0x9AC7, 0x5862, 0x9AC8, 0x584B,\n\t0x9AC9, 0x5870, 0x9ACA, 0x6BC0, 0x9ACB, 0x5852, 0x9ACC, 0x583D,\n\t0x9ACD, 0x5879, 0x9ACE, 0x5885, 0x9ACF, 0x58B9, 0x9AD0, 0x589F,\n\t0x9AD1, 0x58AB, 0x9AD2, 0x58BA, 0x9AD3, 0x58DE, 0x9AD4, 0x58BB,\n\t0x9AD5, 0x58B8, 0x9AD6, 0x58AE, 0x9AD7, 0x58C5, 0x9AD8, 0x58D3,\n\t0x9AD9, 0x58D1, 0x9ADA, 0x58D7, 0x9ADB, 0x58D9, 0x9ADC, 0x58D8,\n\t0x9ADD, 0x58E5, 0x9ADE, 0x58DC, 0x9ADF, 0x58E4, 0x9AE0, 0x58DF,\n\t0x9AE1, 0x58EF, 0x9AE2, 0x58FA, 0x9AE3, 0x58F9, 0x9AE4, 0x58FB,\n\t0x9AE5, 0x58FC, 0x9AE6, 0x58FD, 0x9AE7, 0x5902, 0x9AE8, 0x590A,\n\t0x9AE9, 0x5910, 0x9AEA, 0x591B, 0x9AEB, 0x68A6, 0x9AEC, 0x5925,\n\t0x9AED, 0x592C, 0x9AEE, 0x592D, 0x9AEF, 0x5932, 0x9AF0, 0x5938,\n\t0x9AF1, 0x593E, 0x9AF2, 0x7AD2, 0x9AF3, 0x5955, 0x9AF4, 0x5950,\n\t0x9AF5, 0x594E, 0x9AF6, 0x595A, 0x9AF7, 0x5958, 0x9AF8, 0x5962,\n\t0x9AF9, 0x5960, 0x9AFA, 0x5967, 0x9AFB, 0x596C, 0x9AFC, 0x5969,\n\t0x9B40, 0x5978, 0x9B41, 0x5981, 0x9B42, 0x599D, 0x9B43, 0x4F5E,\n\t0x9B44, 0x4FAB, 0x9B45, 0x59A3, 0x9B46, 0x59B2, 0x9B47, 0x59C6,\n\t0x9B48, 0x59E8, 0x9B49, 0x59DC, 0x9B4A, 0x598D, 0x9B4B, 0x59D9,\n\t0x9B4C, 0x59DA, 0x9B4D, 0x5A25, 0x9B4E, 0x5A1F, 0x9B4F, 0x5A11,\n\t0x9B50, 0x5A1C, 0x9B51, 0x5A09, 0x9B52, 0x5A1A, 0x9B53, 0x5A40,\n\t0x9B54, 0x5A6C, 0x9B55, 0x5A49, 0x9B56, 0x5A35, 0x9B57, 0x5A36,\n\t0x9B58, 0x5A62, 0x9B59, 0x5A6A, 0x9B5A, 0x5A9A, 0x9B5B, 0x5ABC,\n\t0x9B5C, 0x5ABE, 0x9B5D, 0x5ACB, 0x9B5E, 0x5AC2, 0x9B5F, 0x5ABD,\n\t0x9B60, 0x5AE3, 0x9B61, 0x5AD7, 0x9B62, 0x5AE6, 0x9B63, 0x5AE9,\n\t0x9B64, 0x5AD6, 0x9B65, 0x5AFA, 0x9B66, 0x5AFB, 0x9B67, 0x5B0C,\n\t0x9B68, 0x5B0B, 0x9B69, 0x5B16, 0x9B6A, 0x5B32, 0x9B6B, 0x5AD0,\n\t0x9B6C, 0x5B2A, 0x9B6D, 0x5B36, 0x9B6E, 0x5B3E, 0x9B6F, 0x5B43,\n\t0x9B70, 0x5B45, 0x9B71, 0x5B40, 0x9B72, 0x5B51, 0x9B73, 0x5B55,\n\t0x9B74, 0x5B5A, 0x9B75, 0x5B5B, 0x9B76, 0x5B65, 0x9B77, 0x5B69,\n\t0x9B78, 0x5B70, 0x9B79, 0x5B73, 0x9B7A, 0x5B75, 0x9B7B, 0x5B78,\n\t0x9B7C, 0x6588, 0x9B7D, 0x5B7A, 0x9B7E, 0x5B80, 0x9B80, 0x5B83,\n\t0x9B81, 0x5BA6, 0x9B82, 0x5BB8, 0x9B83, 0x5BC3, 0x9B84, 0x5BC7,\n\t0x9B85, 0x5BC9, 0x9B86, 0x5BD4, 0x9B87, 0x5BD0, 0x9B88, 0x5BE4,\n\t0x9B89, 0x5BE6, 0x9B8A, 0x5BE2, 0x9B8B, 0x5BDE, 0x9B8C, 0x5BE5,\n\t0x9B8D, 0x5BEB, 0x9B8E, 0x5BF0, 0x9B8F, 0x5BF6, 0x9B90, 0x5BF3,\n\t0x9B91, 0x5C05, 0x9B92, 0x5C07, 0x9B93, 0x5C08, 0x9B94, 0x5C0D,\n\t0x9B95, 0x5C13, 0x9B96, 0x5C20, 0x9B97, 0x5C22, 0x9B98, 0x5C28,\n\t0x9B99, 0x5C38, 0x9B9A, 0x5C39, 0x9B9B, 0x5C41, 0x9B9C, 0x5C46,\n\t0x9B9D, 0x5C4E, 0x9B9E, 0x5C53, 0x9B9F, 0x5C50, 0x9BA0, 0x5C4F,\n\t0x9BA1, 0x5B71, 0x9BA2, 0x5C6C, 0x9BA3, 0x5C6E, 0x9BA4, 0x4E62,\n\t0x9BA5, 0x5C76, 0x9BA6, 0x5C79, 0x9BA7, 0x5C8C, 0x9BA8, 0x5C91,\n\t0x9BA9, 0x5C94, 0x9BAA, 0x599B, 0x9BAB, 0x5CAB, 0x9BAC, 0x5CBB,\n\t0x9BAD, 0x5CB6, 0x9BAE, 0x5CBC, 0x9BAF, 0x5CB7, 0x9BB0, 0x5CC5,\n\t0x9BB1, 0x5CBE, 0x9BB2, 0x5CC7, 0x9BB3, 0x5CD9, 0x9BB4, 0x5CE9,\n\t0x9BB5, 0x5CFD, 0x9BB6, 0x5CFA, 0x9BB7, 0x5CED, 0x9BB8, 0x5D8C,\n\t0x9BB9, 0x5CEA, 0x9BBA, 0x5D0B, 0x9BBB, 0x5D15, 0x9BBC, 0x5D17,\n\t0x9BBD, 0x5D5C, 0x9BBE, 0x5D1F, 0x9BBF, 0x5D1B, 0x9BC0, 0x5D11,\n\t0x9BC1, 0x5D14, 0x9BC2, 0x5D22, 0x9BC3, 0x5D1A, 0x9BC4, 0x5D19,\n\t0x9BC5, 0x5D18, 0x9BC6, 0x5D4C, 0x9BC7, 0x5D52, 0x9BC8, 0x5D4E,\n\t0x9BC9, 0x5D4B, 0x9BCA, 0x5D6C, 0x9BCB, 0x5D73, 0x9BCC, 0x5D76,\n\t0x9BCD, 0x5D87, 0x9BCE, 0x5D84, 0x9BCF, 0x5D82, 0x9BD0, 0x5DA2,\n\t0x9BD1, 0x5D9D, 0x9BD2, 0x5DAC, 0x9BD3, 0x5DAE, 0x9BD4, 0x5DBD,\n\t0x9BD5, 0x5D90, 0x9BD6, 0x5DB7, 0x9BD7, 0x5DBC, 0x9BD8, 0x5DC9,\n\t0x9BD9, 0x5DCD, 0x9BDA, 0x5DD3, 0x9BDB, 0x5DD2, 0x9BDC, 0x5DD6,\n\t0x9BDD, 0x5DDB, 0x9BDE, 0x5DEB, 0x9BDF, 0x5DF2, 0x9BE0, 0x5DF5,\n\t0x9BE1, 0x5E0B, 0x9BE2, 0x5E1A, 0x9BE3, 0x5E19, 0x9BE4, 0x5E11,\n\t0x9BE5, 0x5E1B, 0x9BE6, 0x5E36, 0x9BE7, 0x5E37, 0x9BE8, 0x5E44,\n\t0x9BE9, 0x5E43, 0x9BEA, 0x5E40, 0x9BEB, 0x5E4E, 0x9BEC, 0x5E57,\n\t0x9BED, 0x5E54, 0x9BEE, 0x5E5F, 0x9BEF, 0x5E62, 0x9BF0, 0x5E64,\n\t0x9BF1, 0x5E47, 0x9BF2, 0x5E75, 0x9BF3, 0x5E76, 0x9BF4, 0x5E7A,\n\t0x9BF5, 0x9EBC, 0x9BF6, 0x5E7F, 0x9BF7, 0x5EA0, 0x9BF8, 0x5EC1,\n\t0x9BF9, 0x5EC2, 0x9BFA, 0x5EC8, 0x9BFB, 0x5ED0, 0x9BFC, 0x5ECF,\n\t0x9C40, 0x5ED6, 0x9C41, 0x5EE3, 0x9C42, 0x5EDD, 0x9C43, 0x5EDA,\n\t0x9C44, 0x5EDB, 0x9C45, 0x5EE2, 0x9C46, 0x5EE1, 0x9C47, 0x5EE8,\n\t0x9C48, 0x5EE9, 0x9C49, 0x5EEC, 0x9C4A, 0x5EF1, 0x9C4B, 0x5EF3,\n\t0x9C4C, 0x5EF0, 0x9C4D, 0x5EF4, 0x9C4E, 0x5EF8, 0x9C4F, 0x5EFE,\n\t0x9C50, 0x5F03, 0x9C51, 0x5F09, 0x9C52, 0x5F5D, 0x9C53, 0x5F5C,\n\t0x9C54, 0x5F0B, 0x9C55, 0x5F11, 0x9C56, 0x5F16, 0x9C57, 0x5F29,\n\t0x9C58, 0x5F2D, 0x9C59, 0x5F38, 0x9C5A, 0x5F41, 0x9C5B, 0x5F48,\n\t0x9C5C, 0x5F4C, 0x9C5D, 0x5F4E, 0x9C5E, 0x5F2F, 0x9C5F, 0x5F51,\n\t0x9C60, 0x5F56, 0x9C61, 0x5F57, 0x9C62, 0x5F59, 0x9C63, 0x5F61,\n\t0x9C64, 0x5F6D, 0x9C65, 0x5F73, 0x9C66, 0x5F77, 0x9C67, 0x5F83,\n\t0x9C68, 0x5F82, 0x9C69, 0x5F7F, 0x9C6A, 0x5F8A, 0x9C6B, 0x5F88,\n\t0x9C6C, 0x5F91, 0x9C6D, 0x5F87, 0x9C6E, 0x5F9E, 0x9C6F, 0x5F99,\n\t0x9C70, 0x5F98, 0x9C71, 0x5FA0, 0x9C72, 0x5FA8, 0x9C73, 0x5FAD,\n\t0x9C74, 0x5FBC, 0x9C75, 0x5FD6, 0x9C76, 0x5FFB, 0x9C77, 0x5FE4,\n\t0x9C78, 0x5FF8, 0x9C79, 0x5FF1, 0x9C7A, 0x5FDD, 0x9C7B, 0x60B3,\n\t0x9C7C, 0x5FFF, 0x9C7D, 0x6021, 0x9C7E, 0x6060, 0x9C80, 0x6019,\n\t0x9C81, 0x6010, 0x9C82, 0x6029, 0x9C83, 0x600E, 0x9C84, 0x6031,\n\t0x9C85, 0x601B, 0x9C86, 0x6015, 0x9C87, 0x602B, 0x9C88, 0x6026,\n\t0x9C89, 0x600F, 0x9C8A, 0x603A, 0x9C8B, 0x605A, 0x9C8C, 0x6041,\n\t0x9C8D, 0x606A, 0x9C8E, 0x6077, 0x9C8F, 0x605F, 0x9C90, 0x604A,\n\t0x9C91, 0x6046, 0x9C92, 0x604D, 0x9C93, 0x6063, 0x9C94, 0x6043,\n\t0x9C95, 0x6064, 0x9C96, 0x6042, 0x9C97, 0x606C, 0x9C98, 0x606B,\n\t0x9C99, 0x6059, 0x9C9A, 0x6081, 0x9C9B, 0x608D, 0x9C9C, 0x60E7,\n\t0x9C9D, 0x6083, 0x9C9E, 0x609A, 0x9C9F, 0x6084, 0x9CA0, 0x609B,\n\t0x9CA1, 0x6096, 0x9CA2, 0x6097, 0x9CA3, 0x6092, 0x9CA4, 0x60A7,\n\t0x9CA5, 0x608B, 0x9CA6, 0x60E1, 0x9CA7, 0x60B8, 0x9CA8, 0x60E0,\n\t0x9CA9, 0x60D3, 0x9CAA, 0x60B4, 0x9CAB, 0x5FF0, 0x9CAC, 0x60BD,\n\t0x9CAD, 0x60C6, 0x9CAE, 0x60B5, 0x9CAF, 0x60D8, 0x9CB0, 0x614D,\n\t0x9CB1, 0x6115, 0x9CB2, 0x6106, 0x9CB3, 0x60F6, 0x9CB4, 0x60F7,\n\t0x9CB5, 0x6100, 0x9CB6, 0x60F4, 0x9CB7, 0x60FA, 0x9CB8, 0x6103,\n\t0x9CB9, 0x6121, 0x9CBA, 0x60FB, 0x9CBB, 0x60F1, 0x9CBC, 0x610D,\n\t0x9CBD, 0x610E, 0x9CBE, 0x6147, 0x9CBF, 0x613E, 0x9CC0, 0x6128,\n\t0x9CC1, 0x6127, 0x9CC2, 0x614A, 0x9CC3, 0x613F, 0x9CC4, 0x613C,\n\t0x9CC5, 0x612C, 0x9CC6, 0x6134, 0x9CC7, 0x613D, 0x9CC8, 0x6142,\n\t0x9CC9, 0x6144, 0x9CCA, 0x6173, 0x9CCB, 0x6177, 0x9CCC, 0x6158,\n\t0x9CCD, 0x6159, 0x9CCE, 0x615A, 0x9CCF, 0x616B, 0x9CD0, 0x6174,\n\t0x9CD1, 0x616F, 0x9CD2, 0x6165, 0x9CD3, 0x6171, 0x9CD4, 0x615F,\n\t0x9CD5, 0x615D, 0x9CD6, 0x6153, 0x9CD7, 0x6175, 0x9CD8, 0x6199,\n\t0x9CD9, 0x6196, 0x9CDA, 0x6187, 0x9CDB, 0x61AC, 0x9CDC, 0x6194,\n\t0x9CDD, 0x619A, 0x9CDE, 0x618A, 0x9CDF, 0x6191, 0x9CE0, 0x61AB,\n\t0x9CE1, 0x61AE, 0x9CE2, 0x61CC, 0x9CE3, 0x61CA, 0x9CE4, 0x61C9,\n\t0x9CE5, 0x61F7, 0x9CE6, 0x61C8, 0x9CE7, 0x61C3, 0x9CE8, 0x61C6,\n\t0x9CE9, 0x61BA, 0x9CEA, 0x61CB, 0x9CEB, 0x7F79, 0x9CEC, 0x61CD,\n\t0x9CED, 0x61E6, 0x9CEE, 0x61E3, 0x9CEF, 0x61F6, 0x9CF0, 0x61FA,\n\t0x9CF1, 0x61F4, 0x9CF2, 0x61FF, 0x9CF3, 0x61FD, 0x9CF4, 0x61FC,\n\t0x9CF5, 0x61FE, 0x9CF6, 0x6200, 0x9CF7, 0x6208, 0x9CF8, 0x6209,\n\t0x9CF9, 0x620D, 0x9CFA, 0x620C, 0x9CFB, 0x6214, 0x9CFC, 0x621B,\n\t0x9D40, 0x621E, 0x9D41, 0x6221, 0x9D42, 0x622A, 0x9D43, 0x622E,\n\t0x9D44, 0x6230, 0x9D45, 0x6232, 0x9D46, 0x6233, 0x9D47, 0x6241,\n\t0x9D48, 0x624E, 0x9D49, 0x625E, 0x9D4A, 0x6263, 0x9D4B, 0x625B,\n\t0x9D4C, 0x6260, 0x9D4D, 0x6268, 0x9D4E, 0x627C, 0x9D4F, 0x6282,\n\t0x9D50, 0x6289, 0x9D51, 0x627E, 0x9D52, 0x6292, 0x9D53, 0x6293,\n\t0x9D54, 0x6296, 0x9D55, 0x62D4, 0x9D56, 0x6283, 0x9D57, 0x6294,\n\t0x9D58, 0x62D7, 0x9D59, 0x62D1, 0x9D5A, 0x62BB, 0x9D5B, 0x62CF,\n\t0x9D5C, 0x62FF, 0x9D5D, 0x62C6, 0x9D5E, 0x64D4, 0x9D5F, 0x62C8,\n\t0x9D60, 0x62DC, 0x9D61, 0x62CC, 0x9D62, 0x62CA, 0x9D63, 0x62C2,\n\t0x9D64, 0x62C7, 0x9D65, 0x629B, 0x9D66, 0x62C9, 0x9D67, 0x630C,\n\t0x9D68, 0x62EE, 0x9D69, 0x62F1, 0x9D6A, 0x6327, 0x9D6B, 0x6302,\n\t0x9D6C, 0x6308, 0x9D6D, 0x62EF, 0x9D6E, 0x62F5, 0x9D6F, 0x6350,\n\t0x9D70, 0x633E, 0x9D71, 0x634D, 0x9D72, 0x641C, 0x9D73, 0x634F,\n\t0x9D74, 0x6396, 0x9D75, 0x638E, 0x9D76, 0x6380, 0x9D77, 0x63AB,\n\t0x9D78, 0x6376, 0x9D79, 0x63A3, 0x9D7A, 0x638F, 0x9D7B, 0x6389,\n\t0x9D7C, 0x639F, 0x9D7D, 0x63B5, 0x9D7E, 0x636B, 0x9D80, 0x6369,\n\t0x9D81, 0x63BE, 0x9D82, 0x63E9, 0x9D83, 0x63C0, 0x9D84, 0x63C6,\n\t0x9D85, 0x63E3, 0x9D86, 0x63C9, 0x9D87, 0x63D2, 0x9D88, 0x63F6,\n\t0x9D89, 0x63C4, 0x9D8A, 0x6416, 0x9D8B, 0x6434, 0x9D8C, 0x6406,\n\t0x9D8D, 0x6413, 0x9D8E, 0x6426, 0x9D8F, 0x6436, 0x9D90, 0x651D,\n\t0x9D91, 0x6417, 0x9D92, 0x6428, 0x9D93, 0x640F, 0x9D94, 0x6467,\n\t0x9D95, 0x646F, 0x9D96, 0x6476, 0x9D97, 0x644E, 0x9D98, 0x652A,\n\t0x9D99, 0x6495, 0x9D9A, 0x6493, 0x9D9B, 0x64A5, 0x9D9C, 0x64A9,\n\t0x9D9D, 0x6488, 0x9D9E, 0x64BC, 0x9D9F, 0x64DA, 0x9DA0, 0x64D2,\n\t0x9DA1, 0x64C5, 0x9DA2, 0x64C7, 0x9DA3, 0x64BB, 0x9DA4, 0x64D8,\n\t0x9DA5, 0x64C2, 0x9DA6, 0x64F1, 0x9DA7, 0x64E7, 0x9DA8, 0x8209,\n\t0x9DA9, 0x64E0, 0x9DAA, 0x64E1, 0x9DAB, 0x62AC, 0x9DAC, 0x64E3,\n\t0x9DAD, 0x64EF, 0x9DAE, 0x652C, 0x9DAF, 0x64F6, 0x9DB0, 0x64F4,\n\t0x9DB1, 0x64F2, 0x9DB2, 0x64FA, 0x9DB3, 0x6500, 0x9DB4, 0x64FD,\n\t0x9DB5, 0x6518, 0x9DB6, 0x651C, 0x9DB7, 0x6505, 0x9DB8, 0x6524,\n\t0x9DB9, 0x6523, 0x9DBA, 0x652B, 0x9DBB, 0x6534, 0x9DBC, 0x6535,\n\t0x9DBD, 0x6537, 0x9DBE, 0x6536, 0x9DBF, 0x6538, 0x9DC0, 0x754B,\n\t0x9DC1, 0x6548, 0x9DC2, 0x6556, 0x9DC3, 0x6555, 0x9DC4, 0x654D,\n\t0x9DC5, 0x6558, 0x9DC6, 0x655E, 0x9DC7, 0x655D, 0x9DC8, 0x6572,\n\t0x9DC9, 0x6578, 0x9DCA, 0x6582, 0x9DCB, 0x6583, 0x9DCC, 0x8B8A,\n\t0x9DCD, 0x659B, 0x9DCE, 0x659F, 0x9DCF, 0x65AB, 0x9DD0, 0x65B7,\n\t0x9DD1, 0x65C3, 0x9DD2, 0x65C6, 0x9DD3, 0x65C1, 0x9DD4, 0x65C4,\n\t0x9DD5, 0x65CC, 0x9DD6, 0x65D2, 0x9DD7, 0x65DB, 0x9DD8, 0x65D9,\n\t0x9DD9, 0x65E0, 0x9DDA, 0x65E1, 0x9DDB, 0x65F1, 0x9DDC, 0x6772,\n\t0x9DDD, 0x660A, 0x9DDE, 0x6603, 0x9DDF, 0x65FB, 0x9DE0, 0x6773,\n\t0x9DE1, 0x6635, 0x9DE2, 0x6636, 0x9DE3, 0x6634, 0x9DE4, 0x661C,\n\t0x9DE5, 0x664F, 0x9DE6, 0x6644, 0x9DE7, 0x6649, 0x9DE8, 0x6641,\n\t0x9DE9, 0x665E, 0x9DEA, 0x665D, 0x9DEB, 0x6664, 0x9DEC, 0x6667,\n\t0x9DED, 0x6668, 0x9DEE, 0x665F, 0x9DEF, 0x6662, 0x9DF0, 0x6670,\n\t0x9DF1, 0x6683, 0x9DF2, 0x6688, 0x9DF3, 0x668E, 0x9DF4, 0x6689,\n\t0x9DF5, 0x6684, 0x9DF6, 0x6698, 0x9DF7, 0x669D, 0x9DF8, 0x66C1,\n\t0x9DF9, 0x66B9, 0x9DFA, 0x66C9, 0x9DFB, 0x66BE, 0x9DFC, 0x66BC,\n\t0x9E40, 0x66C4, 0x9E41, 0x66B8, 0x9E42, 0x66D6, 0x9E43, 0x66DA,\n\t0x9E44, 0x66E0, 0x9E45, 0x663F, 0x9E46, 0x66E6, 0x9E47, 0x66E9,\n\t0x9E48, 0x66F0, 0x9E49, 0x66F5, 0x9E4A, 0x66F7, 0x9E4B, 0x670F,\n\t0x9E4C, 0x6716, 0x9E4D, 0x671E, 0x9E4E, 0x6726, 0x9E4F, 0x6727,\n\t0x9E50, 0x9738, 0x9E51, 0x672E, 0x9E52, 0x673F, 0x9E53, 0x6736,\n\t0x9E54, 0x6741, 0x9E55, 0x6738, 0x9E56, 0x6737, 0x9E57, 0x6746,\n\t0x9E58, 0x675E, 0x9E59, 0x6760, 0x9E5A, 0x6759, 0x9E5B, 0x6763,\n\t0x9E5C, 0x6764, 0x9E5D, 0x6789, 0x9E5E, 0x6770, 0x9E5F, 0x67A9,\n\t0x9E60, 0x677C, 0x9E61, 0x676A, 0x9E62, 0x678C, 0x9E63, 0x678B,\n\t0x9E64, 0x67A6, 0x9E65, 0x67A1, 0x9E66, 0x6785, 0x9E67, 0x67B7,\n\t0x9E68, 0x67EF, 0x9E69, 0x67B4, 0x9E6A, 0x67EC, 0x9E6B, 0x67B3,\n\t0x9E6C, 0x67E9, 0x9E6D, 0x67B8, 0x9E6E, 0x67E4, 0x9E6F, 0x67DE,\n\t0x9E70, 0x67DD, 0x9E71, 0x67E2, 0x9E72, 0x67EE, 0x9E73, 0x67B9,\n\t0x9E74, 0x67CE, 0x9E75, 0x67C6, 0x9E76, 0x67E7, 0x9E77, 0x6A9C,\n\t0x9E78, 0x681E, 0x9E79, 0x6846, 0x9E7A, 0x6829, 0x9E7B, 0x6840,\n\t0x9E7C, 0x684D, 0x9E7D, 0x6832, 0x9E7E, 0x684E, 0x9E80, 0x68B3,\n\t0x9E81, 0x682B, 0x9E82, 0x6859, 0x9E83, 0x6863, 0x9E84, 0x6877,\n\t0x9E85, 0x687F, 0x9E86, 0x689F, 0x9E87, 0x688F, 0x9E88, 0x68AD,\n\t0x9E89, 0x6894, 0x9E8A, 0x689D, 0x9E8B, 0x689B, 0x9E8C, 0x6883,\n\t0x9E8D, 0x6AAE, 0x9E8E, 0x68B9, 0x9E8F, 0x6874, 0x9E90, 0x68B5,\n\t0x9E91, 0x68A0, 0x9E92, 0x68BA, 0x9E93, 0x690F, 0x9E94, 0x688D,\n\t0x9E95, 0x687E, 0x9E96, 0x6901, 0x9E97, 0x68CA, 0x9E98, 0x6908,\n\t0x9E99, 0x68D8, 0x9E9A, 0x6922, 0x9E9B, 0x6926, 0x9E9C, 0x68E1,\n\t0x9E9D, 0x690C, 0x9E9E, 0x68CD, 0x9E9F, 0x68D4, 0x9EA0, 0x68E7,\n\t0x9EA1, 0x68D5, 0x9EA2, 0x6936, 0x9EA3, 0x6912, 0x9EA4, 0x6904,\n\t0x9EA5, 0x68D7, 0x9EA6, 0x68E3, 0x9EA7, 0x6925, 0x9EA8, 0x68F9,\n\t0x9EA9, 0x68E0, 0x9EAA, 0x68EF, 0x9EAB, 0x6928, 0x9EAC, 0x692A,\n\t0x9EAD, 0x691A, 0x9EAE, 0x6923, 0x9EAF, 0x6921, 0x9EB0, 0x68C6,\n\t0x9EB1, 0x6979, 0x9EB2, 0x6977, 0x9EB3, 0x695C, 0x9EB4, 0x6978,\n\t0x9EB5, 0x696B, 0x9EB6, 0x6954, 0x9EB7, 0x697E, 0x9EB8, 0x696E,\n\t0x9EB9, 0x6939, 0x9EBA, 0x6974, 0x9EBB, 0x693D, 0x9EBC, 0x6959,\n\t0x9EBD, 0x6930, 0x9EBE, 0x6961, 0x9EBF, 0x695E, 0x9EC0, 0x695D,\n\t0x9EC1, 0x6981, 0x9EC2, 0x696A, 0x9EC3, 0x69B2, 0x9EC4, 0x69AE,\n\t0x9EC5, 0x69D0, 0x9EC6, 0x69BF, 0x9EC7, 0x69C1, 0x9EC8, 0x69D3,\n\t0x9EC9, 0x69BE, 0x9ECA, 0x69CE, 0x9ECB, 0x5BE8, 0x9ECC, 0x69CA,\n\t0x9ECD, 0x69DD, 0x9ECE, 0x69BB, 0x9ECF, 0x69C3, 0x9ED0, 0x69A7,\n\t0x9ED1, 0x6A2E, 0x9ED2, 0x6991, 0x9ED3, 0x69A0, 0x9ED4, 0x699C,\n\t0x9ED5, 0x6995, 0x9ED6, 0x69B4, 0x9ED7, 0x69DE, 0x9ED8, 0x69E8,\n\t0x9ED9, 0x6A02, 0x9EDA, 0x6A1B, 0x9EDB, 0x69FF, 0x9EDC, 0x6B0A,\n\t0x9EDD, 0x69F9, 0x9EDE, 0x69F2, 0x9EDF, 0x69E7, 0x9EE0, 0x6A05,\n\t0x9EE1, 0x69B1, 0x9EE2, 0x6A1E, 0x9EE3, 0x69ED, 0x9EE4, 0x6A14,\n\t0x9EE5, 0x69EB, 0x9EE6, 0x6A0A, 0x9EE7, 0x6A12, 0x9EE8, 0x6AC1,\n\t0x9EE9, 0x6A23, 0x9EEA, 0x6A13, 0x9EEB, 0x6A44, 0x9EEC, 0x6A0C,\n\t0x9EED, 0x6A72, 0x9EEE, 0x6A36, 0x9EEF, 0x6A78, 0x9EF0, 0x6A47,\n\t0x9EF1, 0x6A62, 0x9EF2, 0x6A59, 0x9EF3, 0x6A66, 0x9EF4, 0x6A48,\n\t0x9EF5, 0x6A38, 0x9EF6, 0x6A22, 0x9EF7, 0x6A90, 0x9EF8, 0x6A8D,\n\t0x9EF9, 0x6AA0, 0x9EFA, 0x6A84, 0x9EFB, 0x6AA2, 0x9EFC, 0x6AA3,\n\t0x9F40, 0x6A97, 0x9F41, 0x8617, 0x9F42, 0x6ABB, 0x9F43, 0x6AC3,\n\t0x9F44, 0x6AC2, 0x9F45, 0x6AB8, 0x9F46, 0x6AB3, 0x9F47, 0x6AAC,\n\t0x9F48, 0x6ADE, 0x9F49, 0x6AD1, 0x9F4A, 0x6ADF, 0x9F4B, 0x6AAA,\n\t0x9F4C, 0x6ADA, 0x9F4D, 0x6AEA, 0x9F4E, 0x6AFB, 0x9F4F, 0x6B05,\n\t0x9F50, 0x8616, 0x9F51, 0x6AFA, 0x9F52, 0x6B12, 0x9F53, 0x6B16,\n\t0x9F54, 0x9B31, 0x9F55, 0x6B1F, 0x9F56, 0x6B38, 0x9F57, 0x6B37,\n\t0x9F58, 0x76DC, 0x9F59, 0x6B39, 0x9F5A, 0x98EE, 0x9F5B, 0x6B47,\n\t0x9F5C, 0x6B43, 0x9F5D, 0x6B49, 0x9F5E, 0x6B50, 0x9F5F, 0x6B59,\n\t0x9F60, 0x6B54, 0x9F61, 0x6B5B, 0x9F62, 0x6B5F, 0x9F63, 0x6B61,\n\t0x9F64, 0x6B78, 0x9F65, 0x6B79, 0x9F66, 0x6B7F, 0x9F67, 0x6B80,\n\t0x9F68, 0x6B84, 0x9F69, 0x6B83, 0x9F6A, 0x6B8D, 0x9F6B, 0x6B98,\n\t0x9F6C, 0x6B95, 0x9F6D, 0x6B9E, 0x9F6E, 0x6BA4, 0x9F6F, 0x6BAA,\n\t0x9F70, 0x6BAB, 0x9F71, 0x6BAF, 0x9F72, 0x6BB2, 0x9F73, 0x6BB1,\n\t0x9F74, 0x6BB3, 0x9F75, 0x6BB7, 0x9F76, 0x6BBC, 0x9F77, 0x6BC6,\n\t0x9F78, 0x6BCB, 0x9F79, 0x6BD3, 0x9F7A, 0x6BDF, 0x9F7B, 0x6BEC,\n\t0x9F7C, 0x6BEB, 0x9F7D, 0x6BF3, 0x9F7E, 0x6BEF, 0x9F80, 0x9EBE,\n\t0x9F81, 0x6C08, 0x9F82, 0x6C13, 0x9F83, 0x6C14, 0x9F84, 0x6C1B,\n\t0x9F85, 0x6C24, 0x9F86, 0x6C23, 0x9F87, 0x6C5E, 0x9F88, 0x6C55,\n\t0x9F89, 0x6C62, 0x9F8A, 0x6C6A, 0x9F8B, 0x6C82, 0x9F8C, 0x6C8D,\n\t0x9F8D, 0x6C9A, 0x9F8E, 0x6C81, 0x9F8F, 0x6C9B, 0x9F90, 0x6C7E,\n\t0x9F91, 0x6C68, 0x9F92, 0x6C73, 0x9F93, 0x6C92, 0x9F94, 0x6C90,\n\t0x9F95, 0x6CC4, 0x9F96, 0x6CF1, 0x9F97, 0x6CD3, 0x9F98, 0x6CBD,\n\t0x9F99, 0x6CD7, 0x9F9A, 0x6CC5, 0x9F9B, 0x6CDD, 0x9F9C, 0x6CAE,\n\t0x9F9D, 0x6CB1, 0x9F9E, 0x6CBE, 0x9F9F, 0x6CBA, 0x9FA0, 0x6CDB,\n\t0x9FA1, 0x6CEF, 0x9FA2, 0x6CD9, 0x9FA3, 0x6CEA, 0x9FA4, 0x6D1F,\n\t0x9FA5, 0x884D, 0x9FA6, 0x6D36, 0x9FA7, 0x6D2B, 0x9FA8, 0x6D3D,\n\t0x9FA9, 0x6D38, 0x9FAA, 0x6D19, 0x9FAB, 0x6D35, 0x9FAC, 0x6D33,\n\t0x9FAD, 0x6D12, 0x9FAE, 0x6D0C, 0x9FAF, 0x6D63, 0x9FB0, 0x6D93,\n\t0x9FB1, 0x6D64, 0x9FB2, 0x6D5A, 0x9FB3, 0x6D79, 0x9FB4, 0x6D59,\n\t0x9FB5, 0x6D8E, 0x9FB6, 0x6D95, 0x9FB7, 0x6FE4, 0x9FB8, 0x6D85,\n\t0x9FB9, 0x6DF9, 0x9FBA, 0x6E15, 0x9FBB, 0x6E0A, 0x9FBC, 0x6DB5,\n\t0x9FBD, 0x6DC7, 0x9FBE, 0x6DE6, 0x9FBF, 0x6DB8, 0x9FC0, 0x6DC6,\n\t0x9FC1, 0x6DEC, 0x9FC2, 0x6DDE, 0x9FC3, 0x6DCC, 0x9FC4, 0x6DE8,\n\t0x9FC5, 0x6DD2, 0x9FC6, 0x6DC5, 0x9FC7, 0x6DFA, 0x9FC8, 0x6DD9,\n\t0x9FC9, 0x6DE4, 0x9FCA, 0x6DD5, 0x9FCB, 0x6DEA, 0x9FCC, 0x6DEE,\n\t0x9FCD, 0x6E2D, 0x9FCE, 0x6E6E, 0x9FCF, 0x6E2E, 0x9FD0, 0x6E19,\n\t0x9FD1, 0x6E72, 0x9FD2, 0x6E5F, 0x9FD3, 0x6E3E, 0x9FD4, 0x6E23,\n\t0x9FD5, 0x6E6B, 0x9FD6, 0x6E2B, 0x9FD7, 0x6E76, 0x9FD8, 0x6E4D,\n\t0x9FD9, 0x6E1F, 0x9FDA, 0x6E43, 0x9FDB, 0x6E3A, 0x9FDC, 0x6E4E,\n\t0x9FDD, 0x6E24, 0x9FDE, 0x6EFF, 0x9FDF, 0x6E1D, 0x9FE0, 0x6E38,\n\t0x9FE1, 0x6E82, 0x9FE2, 0x6EAA, 0x9FE3, 0x6E98, 0x9FE4, 0x6EC9,\n\t0x9FE5, 0x6EB7, 0x9FE6, 0x6ED3, 0x9FE7, 0x6EBD, 0x9FE8, 0x6EAF,\n\t0x9FE9, 0x6EC4, 0x9FEA, 0x6EB2, 0x9FEB, 0x6ED4, 0x9FEC, 0x6ED5,\n\t0x9FED, 0x6E8F, 0x9FEE, 0x6EA5, 0x9FEF, 0x6EC2, 0x9FF0, 0x6E9F,\n\t0x9FF1, 0x6F41, 0x9FF2, 0x6F11, 0x9FF3, 0x704C, 0x9FF4, 0x6EEC,\n\t0x9FF5, 0x6EF8, 0x9FF6, 0x6EFE, 0x9FF7, 0x6F3F, 0x9FF8, 0x6EF2,\n\t0x9FF9, 0x6F31, 0x9FFA, 0x6EEF, 0x9FFB, 0x6F32, 0x9FFC, 0x6ECC,\n\t0xE040, 0x6F3E, 0xE041, 0x6F13, 0xE042, 0x6EF7, 0xE043, 0x6F86,\n\t0xE044, 0x6F7A, 0xE045, 0x6F78, 0xE046, 0x6F81, 0xE047, 0x6F80,\n\t0xE048, 0x6F6F, 0xE049, 0x6F5B, 0xE04A, 0x6FF3, 0xE04B, 0x6F6D,\n\t0xE04C, 0x6F82, 0xE04D, 0x6F7C, 0xE04E, 0x6F58, 0xE04F, 0x6F8E,\n\t0xE050, 0x6F91, 0xE051, 0x6FC2, 0xE052, 0x6F66, 0xE053, 0x6FB3,\n\t0xE054, 0x6FA3, 0xE055, 0x6FA1, 0xE056, 0x6FA4, 0xE057, 0x6FB9,\n\t0xE058, 0x6FC6, 0xE059, 0x6FAA, 0xE05A, 0x6FDF, 0xE05B, 0x6FD5,\n\t0xE05C, 0x6FEC, 0xE05D, 0x6FD4, 0xE05E, 0x6FD8, 0xE05F, 0x6FF1,\n\t0xE060, 0x6FEE, 0xE061, 0x6FDB, 0xE062, 0x7009, 0xE063, 0x700B,\n\t0xE064, 0x6FFA, 0xE065, 0x7011, 0xE066, 0x7001, 0xE067, 0x700F,\n\t0xE068, 0x6FFE, 0xE069, 0x701B, 0xE06A, 0x701A, 0xE06B, 0x6F74,\n\t0xE06C, 0x701D, 0xE06D, 0x7018, 0xE06E, 0x701F, 0xE06F, 0x7030,\n\t0xE070, 0x703E, 0xE071, 0x7032, 0xE072, 0x7051, 0xE073, 0x7063,\n\t0xE074, 0x7099, 0xE075, 0x7092, 0xE076, 0x70AF, 0xE077, 0x70F1,\n\t0xE078, 0x70AC, 0xE079, 0x70B8, 0xE07A, 0x70B3, 0xE07B, 0x70AE,\n\t0xE07C, 0x70DF, 0xE07D, 0x70CB, 0xE07E, 0x70DD, 0xE080, 0x70D9,\n\t0xE081, 0x7109, 0xE082, 0x70FD, 0xE083, 0x711C, 0xE084, 0x7119,\n\t0xE085, 0x7165, 0xE086, 0x7155, 0xE087, 0x7188, 0xE088, 0x7166,\n\t0xE089, 0x7162, 0xE08A, 0x714C, 0xE08B, 0x7156, 0xE08C, 0x716C,\n\t0xE08D, 0x718F, 0xE08E, 0x71FB, 0xE08F, 0x7184, 0xE090, 0x7195,\n\t0xE091, 0x71A8, 0xE092, 0x71AC, 0xE093, 0x71D7, 0xE094, 0x71B9,\n\t0xE095, 0x71BE, 0xE096, 0x71D2, 0xE097, 0x71C9, 0xE098, 0x71D4,\n\t0xE099, 0x71CE, 0xE09A, 0x71E0, 0xE09B, 0x71EC, 0xE09C, 0x71E7,\n\t0xE09D, 0x71F5, 0xE09E, 0x71FC, 0xE09F, 0x71F9, 0xE0A0, 0x71FF,\n\t0xE0A1, 0x720D, 0xE0A2, 0x7210, 0xE0A3, 0x721B, 0xE0A4, 0x7228,\n\t0xE0A5, 0x722D, 0xE0A6, 0x722C, 0xE0A7, 0x7230, 0xE0A8, 0x7232,\n\t0xE0A9, 0x723B, 0xE0AA, 0x723C, 0xE0AB, 0x723F, 0xE0AC, 0x7240,\n\t0xE0AD, 0x7246, 0xE0AE, 0x724B, 0xE0AF, 0x7258, 0xE0B0, 0x7274,\n\t0xE0B1, 0x727E, 0xE0B2, 0x7282, 0xE0B3, 0x7281, 0xE0B4, 0x7287,\n\t0xE0B5, 0x7292, 0xE0B6, 0x7296, 0xE0B7, 0x72A2, 0xE0B8, 0x72A7,\n\t0xE0B9, 0x72B9, 0xE0BA, 0x72B2, 0xE0BB, 0x72C3, 0xE0BC, 0x72C6,\n\t0xE0BD, 0x72C4, 0xE0BE, 0x72CE, 0xE0BF, 0x72D2, 0xE0C0, 0x72E2,\n\t0xE0C1, 0x72E0, 0xE0C2, 0x72E1, 0xE0C3, 0x72F9, 0xE0C4, 0x72F7,\n\t0xE0C5, 0x500F, 0xE0C6, 0x7317, 0xE0C7, 0x730A, 0xE0C8, 0x731C,\n\t0xE0C9, 0x7316, 0xE0CA, 0x731D, 0xE0CB, 0x7334, 0xE0CC, 0x732F,\n\t0xE0CD, 0x7329, 0xE0CE, 0x7325, 0xE0CF, 0x733E, 0xE0D0, 0x734E,\n\t0xE0D1, 0x734F, 0xE0D2, 0x9ED8, 0xE0D3, 0x7357, 0xE0D4, 0x736A,\n\t0xE0D5, 0x7368, 0xE0D6, 0x7370, 0xE0D7, 0x7378, 0xE0D8, 0x7375,\n\t0xE0D9, 0x737B, 0xE0DA, 0x737A, 0xE0DB, 0x73C8, 0xE0DC, 0x73B3,\n\t0xE0DD, 0x73CE, 0xE0DE, 0x73BB, 0xE0DF, 0x73C0, 0xE0E0, 0x73E5,\n\t0xE0E1, 0x73EE, 0xE0E2, 0x73DE, 0xE0E3, 0x74A2, 0xE0E4, 0x7405,\n\t0xE0E5, 0x746F, 0xE0E6, 0x7425, 0xE0E7, 0x73F8, 0xE0E8, 0x7432,\n\t0xE0E9, 0x743A, 0xE0EA, 0x7455, 0xE0EB, 0x743F, 0xE0EC, 0x745F,\n\t0xE0ED, 0x7459, 0xE0EE, 0x7441, 0xE0EF, 0x745C, 0xE0F0, 0x7469,\n\t0xE0F1, 0x7470, 0xE0F2, 0x7463, 0xE0F3, 0x746A, 0xE0F4, 0x7476,\n\t0xE0F5, 0x747E, 0xE0F6, 0x748B, 0xE0F7, 0x749E, 0xE0F8, 0x74A7,\n\t0xE0F9, 0x74CA, 0xE0FA, 0x74CF, 0xE0FB, 0x74D4, 0xE0FC, 0x73F1,\n\t0xE140, 0x74E0, 0xE141, 0x74E3, 0xE142, 0x74E7, 0xE143, 0x74E9,\n\t0xE144, 0x74EE, 0xE145, 0x74F2, 0xE146, 0x74F0, 0xE147, 0x74F1,\n\t0xE148, 0x74F8, 0xE149, 0x74F7, 0xE14A, 0x7504, 0xE14B, 0x7503,\n\t0xE14C, 0x7505, 0xE14D, 0x750C, 0xE14E, 0x750E, 0xE14F, 0x750D,\n\t0xE150, 0x7515, 0xE151, 0x7513, 0xE152, 0x751E, 0xE153, 0x7526,\n\t0xE154, 0x752C, 0xE155, 0x753C, 0xE156, 0x7544, 0xE157, 0x754D,\n\t0xE158, 0x754A, 0xE159, 0x7549, 0xE15A, 0x755B, 0xE15B, 0x7546,\n\t0xE15C, 0x755A, 0xE15D, 0x7569, 0xE15E, 0x7564, 0xE15F, 0x7567,\n\t0xE160, 0x756B, 0xE161, 0x756D, 0xE162, 0x7578, 0xE163, 0x7576,\n\t0xE164, 0x7586, 0xE165, 0x7587, 0xE166, 0x7574, 0xE167, 0x758A,\n\t0xE168, 0x7589, 0xE169, 0x7582, 0xE16A, 0x7594, 0xE16B, 0x759A,\n\t0xE16C, 0x759D, 0xE16D, 0x75A5, 0xE16E, 0x75A3, 0xE16F, 0x75C2,\n\t0xE170, 0x75B3, 0xE171, 0x75C3, 0xE172, 0x75B5, 0xE173, 0x75BD,\n\t0xE174, 0x75B8, 0xE175, 0x75BC, 0xE176, 0x75B1, 0xE177, 0x75CD,\n\t0xE178, 0x75CA, 0xE179, 0x75D2, 0xE17A, 0x75D9, 0xE17B, 0x75E3,\n\t0xE17C, 0x75DE, 0xE17D, 0x75FE, 0xE17E, 0x75FF, 0xE180, 0x75FC,\n\t0xE181, 0x7601, 0xE182, 0x75F0, 0xE183, 0x75FA, 0xE184, 0x75F2,\n\t0xE185, 0x75F3, 0xE186, 0x760B, 0xE187, 0x760D, 0xE188, 0x7609,\n\t0xE189, 0x761F, 0xE18A, 0x7627, 0xE18B, 0x7620, 0xE18C, 0x7621,\n\t0xE18D, 0x7622, 0xE18E, 0x7624, 0xE18F, 0x7634, 0xE190, 0x7630,\n\t0xE191, 0x763B, 0xE192, 0x7647, 0xE193, 0x7648, 0xE194, 0x7646,\n\t0xE195, 0x765C, 0xE196, 0x7658, 0xE197, 0x7661, 0xE198, 0x7662,\n\t0xE199, 0x7668, 0xE19A, 0x7669, 0xE19B, 0x766A, 0xE19C, 0x7667,\n\t0xE19D, 0x766C, 0xE19E, 0x7670, 0xE19F, 0x7672, 0xE1A0, 0x7676,\n\t0xE1A1, 0x7678, 0xE1A2, 0x767C, 0xE1A3, 0x7680, 0xE1A4, 0x7683,\n\t0xE1A5, 0x7688, 0xE1A6, 0x768B, 0xE1A7, 0x768E, 0xE1A8, 0x7696,\n\t0xE1A9, 0x7693, 0xE1AA, 0x7699, 0xE1AB, 0x769A, 0xE1AC, 0x76B0,\n\t0xE1AD, 0x76B4, 0xE1AE, 0x76B8, 0xE1AF, 0x76B9, 0xE1B0, 0x76BA,\n\t0xE1B1, 0x76C2, 0xE1B2, 0x76CD, 0xE1B3, 0x76D6, 0xE1B4, 0x76D2,\n\t0xE1B5, 0x76DE, 0xE1B6, 0x76E1, 0xE1B7, 0x76E5, 0xE1B8, 0x76E7,\n\t0xE1B9, 0x76EA, 0xE1BA, 0x862F, 0xE1BB, 0x76FB, 0xE1BC, 0x7708,\n\t0xE1BD, 0x7707, 0xE1BE, 0x7704, 0xE1BF, 0x7729, 0xE1C0, 0x7724,\n\t0xE1C1, 0x771E, 0xE1C2, 0x7725, 0xE1C3, 0x7726, 0xE1C4, 0x771B,\n\t0xE1C5, 0x7737, 0xE1C6, 0x7738, 0xE1C7, 0x7747, 0xE1C8, 0x775A,\n\t0xE1C9, 0x7768, 0xE1CA, 0x776B, 0xE1CB, 0x775B, 0xE1CC, 0x7765,\n\t0xE1CD, 0x777F, 0xE1CE, 0x777E, 0xE1CF, 0x7779, 0xE1D0, 0x778E,\n\t0xE1D1, 0x778B, 0xE1D2, 0x7791, 0xE1D3, 0x77A0, 0xE1D4, 0x779E,\n\t0xE1D5, 0x77B0, 0xE1D6, 0x77B6, 0xE1D7, 0x77B9, 0xE1D8, 0x77BF,\n\t0xE1D9, 0x77BC, 0xE1DA, 0x77BD, 0xE1DB, 0x77BB, 0xE1DC, 0x77C7,\n\t0xE1DD, 0x77CD, 0xE1DE, 0x77D7, 0xE1DF, 0x77DA, 0xE1E0, 0x77DC,\n\t0xE1E1, 0x77E3, 0xE1E2, 0x77EE, 0xE1E3, 0x77FC, 0xE1E4, 0x780C,\n\t0xE1E5, 0x7812, 0xE1E6, 0x7926, 0xE1E7, 0x7820, 0xE1E8, 0x792A,\n\t0xE1E9, 0x7845, 0xE1EA, 0x788E, 0xE1EB, 0x7874, 0xE1EC, 0x7886,\n\t0xE1ED, 0x787C, 0xE1EE, 0x789A, 0xE1EF, 0x788C, 0xE1F0, 0x78A3,\n\t0xE1F1, 0x78B5, 0xE1F2, 0x78AA, 0xE1F3, 0x78AF, 0xE1F4, 0x78D1,\n\t0xE1F5, 0x78C6, 0xE1F6, 0x78CB, 0xE1F7, 0x78D4, 0xE1F8, 0x78BE,\n\t0xE1F9, 0x78BC, 0xE1FA, 0x78C5, 0xE1FB, 0x78CA, 0xE1FC, 0x78EC,\n\t0xE240, 0x78E7, 0xE241, 0x78DA, 0xE242, 0x78FD, 0xE243, 0x78F4,\n\t0xE244, 0x7907, 0xE245, 0x7912, 0xE246, 0x7911, 0xE247, 0x7919,\n\t0xE248, 0x792C, 0xE249, 0x792B, 0xE24A, 0x7940, 0xE24B, 0x7960,\n\t0xE24C, 0x7957, 0xE24D, 0x795F, 0xE24E, 0x795A, 0xE24F, 0x7955,\n\t0xE250, 0x7953, 0xE251, 0x797A, 0xE252, 0x797F, 0xE253, 0x798A,\n\t0xE254, 0x799D, 0xE255, 0x79A7, 0xE256, 0x9F4B, 0xE257, 0x79AA,\n\t0xE258, 0x79AE, 0xE259, 0x79B3, 0xE25A, 0x79B9, 0xE25B, 0x79BA,\n\t0xE25C, 0x79C9, 0xE25D, 0x79D5, 0xE25E, 0x79E7, 0xE25F, 0x79EC,\n\t0xE260, 0x79E1, 0xE261, 0x79E3, 0xE262, 0x7A08, 0xE263, 0x7A0D,\n\t0xE264, 0x7A18, 0xE265, 0x7A19, 0xE266, 0x7A20, 0xE267, 0x7A1F,\n\t0xE268, 0x7980, 0xE269, 0x7A31, 0xE26A, 0x7A3B, 0xE26B, 0x7A3E,\n\t0xE26C, 0x7A37, 0xE26D, 0x7A43, 0xE26E, 0x7A57, 0xE26F, 0x7A49,\n\t0xE270, 0x7A61, 0xE271, 0x7A62, 0xE272, 0x7A69, 0xE273, 0x9F9D,\n\t0xE274, 0x7A70, 0xE275, 0x7A79, 0xE276, 0x7A7D, 0xE277, 0x7A88,\n\t0xE278, 0x7A97, 0xE279, 0x7A95, 0xE27A, 0x7A98, 0xE27B, 0x7A96,\n\t0xE27C, 0x7AA9, 0xE27D, 0x7AC8, 0xE27E, 0x7AB0, 0xE280, 0x7AB6,\n\t0xE281, 0x7AC5, 0xE282, 0x7AC4, 0xE283, 0x7ABF, 0xE284, 0x9083,\n\t0xE285, 0x7AC7, 0xE286, 0x7ACA, 0xE287, 0x7ACD, 0xE288, 0x7ACF,\n\t0xE289, 0x7AD5, 0xE28A, 0x7AD3, 0xE28B, 0x7AD9, 0xE28C, 0x7ADA,\n\t0xE28D, 0x7ADD, 0xE28E, 0x7AE1, 0xE28F, 0x7AE2, 0xE290, 0x7AE6,\n\t0xE291, 0x7AED, 0xE292, 0x7AF0, 0xE293, 0x7B02, 0xE294, 0x7B0F,\n\t0xE295, 0x7B0A, 0xE296, 0x7B06, 0xE297, 0x7B33, 0xE298, 0x7B18,\n\t0xE299, 0x7B19, 0xE29A, 0x7B1E, 0xE29B, 0x7B35, 0xE29C, 0x7B28,\n\t0xE29D, 0x7B36, 0xE29E, 0x7B50, 0xE29F, 0x7B7A, 0xE2A0, 0x7B04,\n\t0xE2A1, 0x7B4D, 0xE2A2, 0x7B0B, 0xE2A3, 0x7B4C, 0xE2A4, 0x7B45,\n\t0xE2A5, 0x7B75, 0xE2A6, 0x7B65, 0xE2A7, 0x7B74, 0xE2A8, 0x7B67,\n\t0xE2A9, 0x7B70, 0xE2AA, 0x7B71, 0xE2AB, 0x7B6C, 0xE2AC, 0x7B6E,\n\t0xE2AD, 0x7B9D, 0xE2AE, 0x7B98, 0xE2AF, 0x7B9F, 0xE2B0, 0x7B8D,\n\t0xE2B1, 0x7B9C, 0xE2B2, 0x7B9A, 0xE2B3, 0x7B8B, 0xE2B4, 0x7B92,\n\t0xE2B5, 0x7B8F, 0xE2B6, 0x7B5D, 0xE2B7, 0x7B99, 0xE2B8, 0x7BCB,\n\t0xE2B9, 0x7BC1, 0xE2BA, 0x7BCC, 0xE2BB, 0x7BCF, 0xE2BC, 0x7BB4,\n\t0xE2BD, 0x7BC6, 0xE2BE, 0x7BDD, 0xE2BF, 0x7BE9, 0xE2C0, 0x7C11,\n\t0xE2C1, 0x7C14, 0xE2C2, 0x7BE6, 0xE2C3, 0x7BE5, 0xE2C4, 0x7C60,\n\t0xE2C5, 0x7C00, 0xE2C6, 0x7C07, 0xE2C7, 0x7C13, 0xE2C8, 0x7BF3,\n\t0xE2C9, 0x7BF7, 0xE2CA, 0x7C17, 0xE2CB, 0x7C0D, 0xE2CC, 0x7BF6,\n\t0xE2CD, 0x7C23, 0xE2CE, 0x7C27, 0xE2CF, 0x7C2A, 0xE2D0, 0x7C1F,\n\t0xE2D1, 0x7C37, 0xE2D2, 0x7C2B, 0xE2D3, 0x7C3D, 0xE2D4, 0x7C4C,\n\t0xE2D5, 0x7C43, 0xE2D6, 0x7C54, 0xE2D7, 0x7C4F, 0xE2D8, 0x7C40,\n\t0xE2D9, 0x7C50, 0xE2DA, 0x7C58, 0xE2DB, 0x7C5F, 0xE2DC, 0x7C64,\n\t0xE2DD, 0x7C56, 0xE2DE, 0x7C65, 0xE2DF, 0x7C6C, 0xE2E0, 0x7C75,\n\t0xE2E1, 0x7C83, 0xE2E2, 0x7C90, 0xE2E3, 0x7CA4, 0xE2E4, 0x7CAD,\n\t0xE2E5, 0x7CA2, 0xE2E6, 0x7CAB, 0xE2E7, 0x7CA1, 0xE2E8, 0x7CA8,\n\t0xE2E9, 0x7CB3, 0xE2EA, 0x7CB2, 0xE2EB, 0x7CB1, 0xE2EC, 0x7CAE,\n\t0xE2ED, 0x7CB9, 0xE2EE, 0x7CBD, 0xE2EF, 0x7CC0, 0xE2F0, 0x7CC5,\n\t0xE2F1, 0x7CC2, 0xE2F2, 0x7CD8, 0xE2F3, 0x7CD2, 0xE2F4, 0x7CDC,\n\t0xE2F5, 0x7CE2, 0xE2F6, 0x9B3B, 0xE2F7, 0x7CEF, 0xE2F8, 0x7CF2,\n\t0xE2F9, 0x7CF4, 0xE2FA, 0x7CF6, 0xE2FB, 0x7CFA, 0xE2FC, 0x7D06,\n\t0xE340, 0x7D02, 0xE341, 0x7D1C, 0xE342, 0x7D15, 0xE343, 0x7D0A,\n\t0xE344, 0x7D45, 0xE345, 0x7D4B, 0xE346, 0x7D2E, 0xE347, 0x7D32,\n\t0xE348, 0x7D3F, 0xE349, 0x7D35, 0xE34A, 0x7D46, 0xE34B, 0x7D73,\n\t0xE34C, 0x7D56, 0xE34D, 0x7D4E, 0xE34E, 0x7D72, 0xE34F, 0x7D68,\n\t0xE350, 0x7D6E, 0xE351, 0x7D4F, 0xE352, 0x7D63, 0xE353, 0x7D93,\n\t0xE354, 0x7D89, 0xE355, 0x7D5B, 0xE356, 0x7D8F, 0xE357, 0x7D7D,\n\t0xE358, 0x7D9B, 0xE359, 0x7DBA, 0xE35A, 0x7DAE, 0xE35B, 0x7DA3,\n\t0xE35C, 0x7DB5, 0xE35D, 0x7DC7, 0xE35E, 0x7DBD, 0xE35F, 0x7DAB,\n\t0xE360, 0x7E3D, 0xE361, 0x7DA2, 0xE362, 0x7DAF, 0xE363, 0x7DDC,\n\t0xE364, 0x7DB8, 0xE365, 0x7D9F, 0xE366, 0x7DB0, 0xE367, 0x7DD8,\n\t0xE368, 0x7DDD, 0xE369, 0x7DE4, 0xE36A, 0x7DDE, 0xE36B, 0x7DFB,\n\t0xE36C, 0x7DF2, 0xE36D, 0x7DE1, 0xE36E, 0x7E05, 0xE36F, 0x7E0A,\n\t0xE370, 0x7E23, 0xE371, 0x7E21, 0xE372, 0x7E12, 0xE373, 0x7E31,\n\t0xE374, 0x7E1F, 0xE375, 0x7E09, 0xE376, 0x7E0B, 0xE377, 0x7E22,\n\t0xE378, 0x7E46, 0xE379, 0x7E66, 0xE37A, 0x7E3B, 0xE37B, 0x7E35,\n\t0xE37C, 0x7E39, 0xE37D, 0x7E43, 0xE37E, 0x7E37, 0xE380, 0x7E32,\n\t0xE381, 0x7E3A, 0xE382, 0x7E67, 0xE383, 0x7E5D, 0xE384, 0x7E56,\n\t0xE385, 0x7E5E, 0xE386, 0x7E59, 0xE387, 0x7E5A, 0xE388, 0x7E79,\n\t0xE389, 0x7E6A, 0xE38A, 0x7E69, 0xE38B, 0x7E7C, 0xE38C, 0x7E7B,\n\t0xE38D, 0x7E83, 0xE38E, 0x7DD5, 0xE38F, 0x7E7D, 0xE390, 0x8FAE,\n\t0xE391, 0x7E7F, 0xE392, 0x7E88, 0xE393, 0x7E89, 0xE394, 0x7E8C,\n\t0xE395, 0x7E92, 0xE396, 0x7E90, 0xE397, 0x7E93, 0xE398, 0x7E94,\n\t0xE399, 0x7E96, 0xE39A, 0x7E8E, 0xE39B, 0x7E9B, 0xE39C, 0x7E9C,\n\t0xE39D, 0x7F38, 0xE39E, 0x7F3A, 0xE39F, 0x7F45, 0xE3A0, 0x7F4C,\n\t0xE3A1, 0x7F4D, 0xE3A2, 0x7F4E, 0xE3A3, 0x7F50, 0xE3A4, 0x7F51,\n\t0xE3A5, 0x7F55, 0xE3A6, 0x7F54, 0xE3A7, 0x7F58, 0xE3A8, 0x7F5F,\n\t0xE3A9, 0x7F60, 0xE3AA, 0x7F68, 0xE3AB, 0x7F69, 0xE3AC, 0x7F67,\n\t0xE3AD, 0x7F78, 0xE3AE, 0x7F82, 0xE3AF, 0x7F86, 0xE3B0, 0x7F83,\n\t0xE3B1, 0x7F88, 0xE3B2, 0x7F87, 0xE3B3, 0x7F8C, 0xE3B4, 0x7F94,\n\t0xE3B5, 0x7F9E, 0xE3B6, 0x7F9D, 0xE3B7, 0x7F9A, 0xE3B8, 0x7FA3,\n\t0xE3B9, 0x7FAF, 0xE3BA, 0x7FB2, 0xE3BB, 0x7FB9, 0xE3BC, 0x7FAE,\n\t0xE3BD, 0x7FB6, 0xE3BE, 0x7FB8, 0xE3BF, 0x8B71, 0xE3C0, 0x7FC5,\n\t0xE3C1, 0x7FC6, 0xE3C2, 0x7FCA, 0xE3C3, 0x7FD5, 0xE3C4, 0x7FD4,\n\t0xE3C5, 0x7FE1, 0xE3C6, 0x7FE6, 0xE3C7, 0x7FE9, 0xE3C8, 0x7FF3,\n\t0xE3C9, 0x7FF9, 0xE3CA, 0x98DC, 0xE3CB, 0x8006, 0xE3CC, 0x8004,\n\t0xE3CD, 0x800B, 0xE3CE, 0x8012, 0xE3CF, 0x8018, 0xE3D0, 0x8019,\n\t0xE3D1, 0x801C, 0xE3D2, 0x8021, 0xE3D3, 0x8028, 0xE3D4, 0x803F,\n\t0xE3D5, 0x803B, 0xE3D6, 0x804A, 0xE3D7, 0x8046, 0xE3D8, 0x8052,\n\t0xE3D9, 0x8058, 0xE3DA, 0x805A, 0xE3DB, 0x805F, 0xE3DC, 0x8062,\n\t0xE3DD, 0x8068, 0xE3DE, 0x8073, 0xE3DF, 0x8072, 0xE3E0, 0x8070,\n\t0xE3E1, 0x8076, 0xE3E2, 0x8079, 0xE3E3, 0x807D, 0xE3E4, 0x807F,\n\t0xE3E5, 0x8084, 0xE3E6, 0x8086, 0xE3E7, 0x8085, 0xE3E8, 0x809B,\n\t0xE3E9, 0x8093, 0xE3EA, 0x809A, 0xE3EB, 0x80AD, 0xE3EC, 0x5190,\n\t0xE3ED, 0x80AC, 0xE3EE, 0x80DB, 0xE3EF, 0x80E5, 0xE3F0, 0x80D9,\n\t0xE3F1, 0x80DD, 0xE3F2, 0x80C4, 0xE3F3, 0x80DA, 0xE3F4, 0x80D6,\n\t0xE3F5, 0x8109, 0xE3F6, 0x80EF, 0xE3F7, 0x80F1, 0xE3F8, 0x811B,\n\t0xE3F9, 0x8129, 0xE3FA, 0x8123, 0xE3FB, 0x812F, 0xE3FC, 0x814B,\n\t0xE440, 0x968B, 0xE441, 0x8146, 0xE442, 0x813E, 0xE443, 0x8153,\n\t0xE444, 0x8151, 0xE445, 0x80FC, 0xE446, 0x8171, 0xE447, 0x816E,\n\t0xE448, 0x8165, 0xE449, 0x8166, 0xE44A, 0x8174, 0xE44B, 0x8183,\n\t0xE44C, 0x8188, 0xE44D, 0x818A, 0xE44E, 0x8180, 0xE44F, 0x8182,\n\t0xE450, 0x81A0, 0xE451, 0x8195, 0xE452, 0x81A4, 0xE453, 0x81A3,\n\t0xE454, 0x815F, 0xE455, 0x8193, 0xE456, 0x81A9, 0xE457, 0x81B0,\n\t0xE458, 0x81B5, 0xE459, 0x81BE, 0xE45A, 0x81B8, 0xE45B, 0x81BD,\n\t0xE45C, 0x81C0, 0xE45D, 0x81C2, 0xE45E, 0x81BA, 0xE45F, 0x81C9,\n\t0xE460, 0x81CD, 0xE461, 0x81D1, 0xE462, 0x81D9, 0xE463, 0x81D8,\n\t0xE464, 0x81C8, 0xE465, 0x81DA, 0xE466, 0x81DF, 0xE467, 0x81E0,\n\t0xE468, 0x81E7, 0xE469, 0x81FA, 0xE46A, 0x81FB, 0xE46B, 0x81FE,\n\t0xE46C, 0x8201, 0xE46D, 0x8202, 0xE46E, 0x8205, 0xE46F, 0x8207,\n\t0xE470, 0x820A, 0xE471, 0x820D, 0xE472, 0x8210, 0xE473, 0x8216,\n\t0xE474, 0x8229, 0xE475, 0x822B, 0xE476, 0x8238, 0xE477, 0x8233,\n\t0xE478, 0x8240, 0xE479, 0x8259, 0xE47A, 0x8258, 0xE47B, 0x825D,\n\t0xE47C, 0x825A, 0xE47D, 0x825F, 0xE47E, 0x8264, 0xE480, 0x8262,\n\t0xE481, 0x8268, 0xE482, 0x826A, 0xE483, 0x826B, 0xE484, 0x822E,\n\t0xE485, 0x8271, 0xE486, 0x8277, 0xE487, 0x8278, 0xE488, 0x827E,\n\t0xE489, 0x828D, 0xE48A, 0x8292, 0xE48B, 0x82AB, 0xE48C, 0x829F,\n\t0xE48D, 0x82BB, 0xE48E, 0x82AC, 0xE48F, 0x82E1, 0xE490, 0x82E3,\n\t0xE491, 0x82DF, 0xE492, 0x82D2, 0xE493, 0x82F4, 0xE494, 0x82F3,\n\t0xE495, 0x82FA, 0xE496, 0x8393, 0xE497, 0x8303, 0xE498, 0x82FB,\n\t0xE499, 0x82F9, 0xE49A, 0x82DE, 0xE49B, 0x8306, 0xE49C, 0x82DC,\n\t0xE49D, 0x8309, 0xE49E, 0x82D9, 0xE49F, 0x8335, 0xE4A0, 0x8334,\n\t0xE4A1, 0x8316, 0xE4A2, 0x8332, 0xE4A3, 0x8331, 0xE4A4, 0x8340,\n\t0xE4A5, 0x8339, 0xE4A6, 0x8350, 0xE4A7, 0x8345, 0xE4A8, 0x832F,\n\t0xE4A9, 0x832B, 0xE4AA, 0x8317, 0xE4AB, 0x8318, 0xE4AC, 0x8385,\n\t0xE4AD, 0x839A, 0xE4AE, 0x83AA, 0xE4AF, 0x839F, 0xE4B0, 0x83A2,\n\t0xE4B1, 0x8396, 0xE4B2, 0x8323, 0xE4B3, 0x838E, 0xE4B4, 0x8387,\n\t0xE4B5, 0x838A, 0xE4B6, 0x837C, 0xE4B7, 0x83B5, 0xE4B8, 0x8373,\n\t0xE4B9, 0x8375, 0xE4BA, 0x83A0, 0xE4BB, 0x8389, 0xE4BC, 0x83A8,\n\t0xE4BD, 0x83F4, 0xE4BE, 0x8413, 0xE4BF, 0x83EB, 0xE4C0, 0x83CE,\n\t0xE4C1, 0x83FD, 0xE4C2, 0x8403, 0xE4C3, 0x83D8, 0xE4C4, 0x840B,\n\t0xE4C5, 0x83C1, 0xE4C6, 0x83F7, 0xE4C7, 0x8407, 0xE4C8, 0x83E0,\n\t0xE4C9, 0x83F2, 0xE4CA, 0x840D, 0xE4CB, 0x8422, 0xE4CC, 0x8420,\n\t0xE4CD, 0x83BD, 0xE4CE, 0x8438, 0xE4CF, 0x8506, 0xE4D0, 0x83FB,\n\t0xE4D1, 0x846D, 0xE4D2, 0x842A, 0xE4D3, 0x843C, 0xE4D4, 0x855A,\n\t0xE4D5, 0x8484, 0xE4D6, 0x8477, 0xE4D7, 0x846B, 0xE4D8, 0x84AD,\n\t0xE4D9, 0x846E, 0xE4DA, 0x8482, 0xE4DB, 0x8469, 0xE4DC, 0x8446,\n\t0xE4DD, 0x842C, 0xE4DE, 0x846F, 0xE4DF, 0x8479, 0xE4E0, 0x8435,\n\t0xE4E1, 0x84CA, 0xE4E2, 0x8462, 0xE4E3, 0x84B9, 0xE4E4, 0x84BF,\n\t0xE4E5, 0x849F, 0xE4E6, 0x84D9, 0xE4E7, 0x84CD, 0xE4E8, 0x84BB,\n\t0xE4E9, 0x84DA, 0xE4EA, 0x84D0, 0xE4EB, 0x84C1, 0xE4EC, 0x84C6,\n\t0xE4ED, 0x84D6, 0xE4EE, 0x84A1, 0xE4EF, 0x8521, 0xE4F0, 0x84FF,\n\t0xE4F1, 0x84F4, 0xE4F2, 0x8517, 0xE4F3, 0x8518, 0xE4F4, 0x852C,\n\t0xE4F5, 0x851F, 0xE4F6, 0x8515, 0xE4F7, 0x8514, 0xE4F8, 0x84FC,\n\t0xE4F9, 0x8540, 0xE4FA, 0x8563, 0xE4FB, 0x8558, 0xE4FC, 0x8548,\n\t0xE540, 0x8541, 0xE541, 0x8602, 0xE542, 0x854B, 0xE543, 0x8555,\n\t0xE544, 0x8580, 0xE545, 0x85A4, 0xE546, 0x8588, 0xE547, 0x8591,\n\t0xE548, 0x858A, 0xE549, 0x85A8, 0xE54A, 0x856D, 0xE54B, 0x8594,\n\t0xE54C, 0x859B, 0xE54D, 0x85EA, 0xE54E, 0x8587, 0xE54F, 0x859C,\n\t0xE550, 0x8577, 0xE551, 0x857E, 0xE552, 0x8590, 0xE553, 0x85C9,\n\t0xE554, 0x85BA, 0xE555, 0x85CF, 0xE556, 0x85B9, 0xE557, 0x85D0,\n\t0xE558, 0x85D5, 0xE559, 0x85DD, 0xE55A, 0x85E5, 0xE55B, 0x85DC,\n\t0xE55C, 0x85F9, 0xE55D, 0x860A, 0xE55E, 0x8613, 0xE55F, 0x860B,\n\t0xE560, 0x85FE, 0xE561, 0x85FA, 0xE562, 0x8606, 0xE563, 0x8622,\n\t0xE564, 0x861A, 0xE565, 0x8630, 0xE566, 0x863F, 0xE567, 0x864D,\n\t0xE568, 0x4E55, 0xE569, 0x8654, 0xE56A, 0x865F, 0xE56B, 0x8667,\n\t0xE56C, 0x8671, 0xE56D, 0x8693, 0xE56E, 0x86A3, 0xE56F, 0x86A9,\n\t0xE570, 0x86AA, 0xE571, 0x868B, 0xE572, 0x868C, 0xE573, 0x86B6,\n\t0xE574, 0x86AF, 0xE575, 0x86C4, 0xE576, 0x86C6, 0xE577, 0x86B0,\n\t0xE578, 0x86C9, 0xE579, 0x8823, 0xE57A, 0x86AB, 0xE57B, 0x86D4,\n\t0xE57C, 0x86DE, 0xE57D, 0x86E9, 0xE57E, 0x86EC, 0xE580, 0x86DF,\n\t0xE581, 0x86DB, 0xE582, 0x86EF, 0xE583, 0x8712, 0xE584, 0x8706,\n\t0xE585, 0x8708, 0xE586, 0x8700, 0xE587, 0x8703, 0xE588, 0x86FB,\n\t0xE589, 0x8711, 0xE58A, 0x8709, 0xE58B, 0x870D, 0xE58C, 0x86F9,\n\t0xE58D, 0x870A, 0xE58E, 0x8734, 0xE58F, 0x873F, 0xE590, 0x8737,\n\t0xE591, 0x873B, 0xE592, 0x8725, 0xE593, 0x8729, 0xE594, 0x871A,\n\t0xE595, 0x8760, 0xE596, 0x875F, 0xE597, 0x8778, 0xE598, 0x874C,\n\t0xE599, 0x874E, 0xE59A, 0x8774, 0xE59B, 0x8757, 0xE59C, 0x8768,\n\t0xE59D, 0x876E, 0xE59E, 0x8759, 0xE59F, 0x8753, 0xE5A0, 0x8763,\n\t0xE5A1, 0x876A, 0xE5A2, 0x8805, 0xE5A3, 0x87A2, 0xE5A4, 0x879F,\n\t0xE5A5, 0x8782, 0xE5A6, 0x87AF, 0xE5A7, 0x87CB, 0xE5A8, 0x87BD,\n\t0xE5A9, 0x87C0, 0xE5AA, 0x87D0, 0xE5AB, 0x96D6, 0xE5AC, 0x87AB,\n\t0xE5AD, 0x87C4, 0xE5AE, 0x87B3, 0xE5AF, 0x87C7, 0xE5B0, 0x87C6,\n\t0xE5B1, 0x87BB, 0xE5B2, 0x87EF, 0xE5B3, 0x87F2, 0xE5B4, 0x87E0,\n\t0xE5B5, 0x880F, 0xE5B6, 0x880D, 0xE5B7, 0x87FE, 0xE5B8, 0x87F6,\n\t0xE5B9, 0x87F7, 0xE5BA, 0x880E, 0xE5BB, 0x87D2, 0xE5BC, 0x8811,\n\t0xE5BD, 0x8816, 0xE5BE, 0x8815, 0xE5BF, 0x8822, 0xE5C0, 0x8821,\n\t0xE5C1, 0x8831, 0xE5C2, 0x8836, 0xE5C3, 0x8839, 0xE5C4, 0x8827,\n\t0xE5C5, 0x883B, 0xE5C6, 0x8844, 0xE5C7, 0x8842, 0xE5C8, 0x8852,\n\t0xE5C9, 0x8859, 0xE5CA, 0x885E, 0xE5CB, 0x8862, 0xE5CC, 0x886B,\n\t0xE5CD, 0x8881, 0xE5CE, 0x887E, 0xE5CF, 0x889E, 0xE5D0, 0x8875,\n\t0xE5D1, 0x887D, 0xE5D2, 0x88B5, 0xE5D3, 0x8872, 0xE5D4, 0x8882,\n\t0xE5D5, 0x8897, 0xE5D6, 0x8892, 0xE5D7, 0x88AE, 0xE5D8, 0x8899,\n\t0xE5D9, 0x88A2, 0xE5DA, 0x888D, 0xE5DB, 0x88A4, 0xE5DC, 0x88B0,\n\t0xE5DD, 0x88BF, 0xE5DE, 0x88B1, 0xE5DF, 0x88C3, 0xE5E0, 0x88C4,\n\t0xE5E1, 0x88D4, 0xE5E2, 0x88D8, 0xE5E3, 0x88D9, 0xE5E4, 0x88DD,\n\t0xE5E5, 0x88F9, 0xE5E6, 0x8902, 0xE5E7, 0x88FC, 0xE5E8, 0x88F4,\n\t0xE5E9, 0x88E8, 0xE5EA, 0x88F2, 0xE5EB, 0x8904, 0xE5EC, 0x890C,\n\t0xE5ED, 0x890A, 0xE5EE, 0x8913, 0xE5EF, 0x8943, 0xE5F0, 0x891E,\n\t0xE5F1, 0x8925, 0xE5F2, 0x892A, 0xE5F3, 0x892B, 0xE5F4, 0x8941,\n\t0xE5F5, 0x8944, 0xE5F6, 0x893B, 0xE5F7, 0x8936, 0xE5F8, 0x8938,\n\t0xE5F9, 0x894C, 0xE5FA, 0x891D, 0xE5FB, 0x8960, 0xE5FC, 0x895E,\n\t0xE640, 0x8966, 0xE641, 0x8964, 0xE642, 0x896D, 0xE643, 0x896A,\n\t0xE644, 0x896F, 0xE645, 0x8974, 0xE646, 0x8977, 0xE647, 0x897E,\n\t0xE648, 0x8983, 0xE649, 0x8988, 0xE64A, 0x898A, 0xE64B, 0x8993,\n\t0xE64C, 0x8998, 0xE64D, 0x89A1, 0xE64E, 0x89A9, 0xE64F, 0x89A6,\n\t0xE650, 0x89AC, 0xE651, 0x89AF, 0xE652, 0x89B2, 0xE653, 0x89BA,\n\t0xE654, 0x89BD, 0xE655, 0x89BF, 0xE656, 0x89C0, 0xE657, 0x89DA,\n\t0xE658, 0x89DC, 0xE659, 0x89DD, 0xE65A, 0x89E7, 0xE65B, 0x89F4,\n\t0xE65C, 0x89F8, 0xE65D, 0x8A03, 0xE65E, 0x8A16, 0xE65F, 0x8A10,\n\t0xE660, 0x8A0C, 0xE661, 0x8A1B, 0xE662, 0x8A1D, 0xE663, 0x8A25,\n\t0xE664, 0x8A36, 0xE665, 0x8A41, 0xE666, 0x8A5B, 0xE667, 0x8A52,\n\t0xE668, 0x8A46, 0xE669, 0x8A48, 0xE66A, 0x8A7C, 0xE66B, 0x8A6D,\n\t0xE66C, 0x8A6C, 0xE66D, 0x8A62, 0xE66E, 0x8A85, 0xE66F, 0x8A82,\n\t0xE670, 0x8A84, 0xE671, 0x8AA8, 0xE672, 0x8AA1, 0xE673, 0x8A91,\n\t0xE674, 0x8AA5, 0xE675, 0x8AA6, 0xE676, 0x8A9A, 0xE677, 0x8AA3,\n\t0xE678, 0x8AC4, 0xE679, 0x8ACD, 0xE67A, 0x8AC2, 0xE67B, 0x8ADA,\n\t0xE67C, 0x8AEB, 0xE67D, 0x8AF3, 0xE67E, 0x8AE7, 0xE680, 0x8AE4,\n\t0xE681, 0x8AF1, 0xE682, 0x8B14, 0xE683, 0x8AE0, 0xE684, 0x8AE2,\n\t0xE685, 0x8AF7, 0xE686, 0x8ADE, 0xE687, 0x8ADB, 0xE688, 0x8B0C,\n\t0xE689, 0x8B07, 0xE68A, 0x8B1A, 0xE68B, 0x8AE1, 0xE68C, 0x8B16,\n\t0xE68D, 0x8B10, 0xE68E, 0x8B17, 0xE68F, 0x8B20, 0xE690, 0x8B33,\n\t0xE691, 0x97AB, 0xE692, 0x8B26, 0xE693, 0x8B2B, 0xE694, 0x8B3E,\n\t0xE695, 0x8B28, 0xE696, 0x8B41, 0xE697, 0x8B4C, 0xE698, 0x8B4F,\n\t0xE699, 0x8B4E, 0xE69A, 0x8B49, 0xE69B, 0x8B56, 0xE69C, 0x8B5B,\n\t0xE69D, 0x8B5A, 0xE69E, 0x8B6B, 0xE69F, 0x8B5F, 0xE6A0, 0x8B6C,\n\t0xE6A1, 0x8B6F, 0xE6A2, 0x8B74, 0xE6A3, 0x8B7D, 0xE6A4, 0x8B80,\n\t0xE6A5, 0x8B8C, 0xE6A6, 0x8B8E, 0xE6A7, 0x8B92, 0xE6A8, 0x8B93,\n\t0xE6A9, 0x8B96, 0xE6AA, 0x8B99, 0xE6AB, 0x8B9A, 0xE6AC, 0x8C3A,\n\t0xE6AD, 0x8C41, 0xE6AE, 0x8C3F, 0xE6AF, 0x8C48, 0xE6B0, 0x8C4C,\n\t0xE6B1, 0x8C4E, 0xE6B2, 0x8C50, 0xE6B3, 0x8C55, 0xE6B4, 0x8C62,\n\t0xE6B5, 0x8C6C, 0xE6B6, 0x8C78, 0xE6B7, 0x8C7A, 0xE6B8, 0x8C82,\n\t0xE6B9, 0x8C89, 0xE6BA, 0x8C85, 0xE6BB, 0x8C8A, 0xE6BC, 0x8C8D,\n\t0xE6BD, 0x8C8E, 0xE6BE, 0x8C94, 0xE6BF, 0x8C7C, 0xE6C0, 0x8C98,\n\t0xE6C1, 0x621D, 0xE6C2, 0x8CAD, 0xE6C3, 0x8CAA, 0xE6C4, 0x8CBD,\n\t0xE6C5, 0x8CB2, 0xE6C6, 0x8CB3, 0xE6C7, 0x8CAE, 0xE6C8, 0x8CB6,\n\t0xE6C9, 0x8CC8, 0xE6CA, 0x8CC1, 0xE6CB, 0x8CE4, 0xE6CC, 0x8CE3,\n\t0xE6CD, 0x8CDA, 0xE6CE, 0x8CFD, 0xE6CF, 0x8CFA, 0xE6D0, 0x8CFB,\n\t0xE6D1, 0x8D04, 0xE6D2, 0x8D05, 0xE6D3, 0x8D0A, 0xE6D4, 0x8D07,\n\t0xE6D5, 0x8D0F, 0xE6D6, 0x8D0D, 0xE6D7, 0x8D10, 0xE6D8, 0x9F4E,\n\t0xE6D9, 0x8D13, 0xE6DA, 0x8CCD, 0xE6DB, 0x8D14, 0xE6DC, 0x8D16,\n\t0xE6DD, 0x8D67, 0xE6DE, 0x8D6D, 0xE6DF, 0x8D71, 0xE6E0, 0x8D73,\n\t0xE6E1, 0x8D81, 0xE6E2, 0x8D99, 0xE6E3, 0x8DC2, 0xE6E4, 0x8DBE,\n\t0xE6E5, 0x8DBA, 0xE6E6, 0x8DCF, 0xE6E7, 0x8DDA, 0xE6E8, 0x8DD6,\n\t0xE6E9, 0x8DCC, 0xE6EA, 0x8DDB, 0xE6EB, 0x8DCB, 0xE6EC, 0x8DEA,\n\t0xE6ED, 0x8DEB, 0xE6EE, 0x8DDF, 0xE6EF, 0x8DE3, 0xE6F0, 0x8DFC,\n\t0xE6F1, 0x8E08, 0xE6F2, 0x8E09, 0xE6F3, 0x8DFF, 0xE6F4, 0x8E1D,\n\t0xE6F5, 0x8E1E, 0xE6F6, 0x8E10, 0xE6F7, 0x8E1F, 0xE6F8, 0x8E42,\n\t0xE6F9, 0x8E35, 0xE6FA, 0x8E30, 0xE6FB, 0x8E34, 0xE6FC, 0x8E4A,\n\t0xE740, 0x8E47, 0xE741, 0x8E49, 0xE742, 0x8E4C, 0xE743, 0x8E50,\n\t0xE744, 0x8E48, 0xE745, 0x8E59, 0xE746, 0x8E64, 0xE747, 0x8E60,\n\t0xE748, 0x8E2A, 0xE749, 0x8E63, 0xE74A, 0x8E55, 0xE74B, 0x8E76,\n\t0xE74C, 0x8E72, 0xE74D, 0x8E7C, 0xE74E, 0x8E81, 0xE74F, 0x8E87,\n\t0xE750, 0x8E85, 0xE751, 0x8E84, 0xE752, 0x8E8B, 0xE753, 0x8E8A,\n\t0xE754, 0x8E93, 0xE755, 0x8E91, 0xE756, 0x8E94, 0xE757, 0x8E99,\n\t0xE758, 0x8EAA, 0xE759, 0x8EA1, 0xE75A, 0x8EAC, 0xE75B, 0x8EB0,\n\t0xE75C, 0x8EC6, 0xE75D, 0x8EB1, 0xE75E, 0x8EBE, 0xE75F, 0x8EC5,\n\t0xE760, 0x8EC8, 0xE761, 0x8ECB, 0xE762, 0x8EDB, 0xE763, 0x8EE3,\n\t0xE764, 0x8EFC, 0xE765, 0x8EFB, 0xE766, 0x8EEB, 0xE767, 0x8EFE,\n\t0xE768, 0x8F0A, 0xE769, 0x8F05, 0xE76A, 0x8F15, 0xE76B, 0x8F12,\n\t0xE76C, 0x8F19, 0xE76D, 0x8F13, 0xE76E, 0x8F1C, 0xE76F, 0x8F1F,\n\t0xE770, 0x8F1B, 0xE771, 0x8F0C, 0xE772, 0x8F26, 0xE773, 0x8F33,\n\t0xE774, 0x8F3B, 0xE775, 0x8F39, 0xE776, 0x8F45, 0xE777, 0x8F42,\n\t0xE778, 0x8F3E, 0xE779, 0x8F4C, 0xE77A, 0x8F49, 0xE77B, 0x8F46,\n\t0xE77C, 0x8F4E, 0xE77D, 0x8F57, 0xE77E, 0x8F5C, 0xE780, 0x8F62,\n\t0xE781, 0x8F63, 0xE782, 0x8F64, 0xE783, 0x8F9C, 0xE784, 0x8F9F,\n\t0xE785, 0x8FA3, 0xE786, 0x8FAD, 0xE787, 0x8FAF, 0xE788, 0x8FB7,\n\t0xE789, 0x8FDA, 0xE78A, 0x8FE5, 0xE78B, 0x8FE2, 0xE78C, 0x8FEA,\n\t0xE78D, 0x8FEF, 0xE78E, 0x9087, 0xE78F, 0x8FF4, 0xE790, 0x9005,\n\t0xE791, 0x8FF9, 0xE792, 0x8FFA, 0xE793, 0x9011, 0xE794, 0x9015,\n\t0xE795, 0x9021, 0xE796, 0x900D, 0xE797, 0x901E, 0xE798, 0x9016,\n\t0xE799, 0x900B, 0xE79A, 0x9027, 0xE79B, 0x9036, 0xE79C, 0x9035,\n\t0xE79D, 0x9039, 0xE79E, 0x8FF8, 0xE79F, 0x904F, 0xE7A0, 0x9050,\n\t0xE7A1, 0x9051, 0xE7A2, 0x9052, 0xE7A3, 0x900E, 0xE7A4, 0x9049,\n\t0xE7A5, 0x903E, 0xE7A6, 0x9056, 0xE7A7, 0x9058, 0xE7A8, 0x905E,\n\t0xE7A9, 0x9068, 0xE7AA, 0x906F, 0xE7AB, 0x9076, 0xE7AC, 0x96A8,\n\t0xE7AD, 0x9072, 0xE7AE, 0x9082, 0xE7AF, 0x907D, 0xE7B0, 0x9081,\n\t0xE7B1, 0x9080, 0xE7B2, 0x908A, 0xE7B3, 0x9089, 0xE7B4, 0x908F,\n\t0xE7B5, 0x90A8, 0xE7B6, 0x90AF, 0xE7B7, 0x90B1, 0xE7B8, 0x90B5,\n\t0xE7B9, 0x90E2, 0xE7BA, 0x90E4, 0xE7BB, 0x6248, 0xE7BC, 0x90DB,\n\t0xE7BD, 0x9102, 0xE7BE, 0x9112, 0xE7BF, 0x9119, 0xE7C0, 0x9132,\n\t0xE7C1, 0x9130, 0xE7C2, 0x914A, 0xE7C3, 0x9156, 0xE7C4, 0x9158,\n\t0xE7C5, 0x9163, 0xE7C6, 0x9165, 0xE7C7, 0x9169, 0xE7C8, 0x9173,\n\t0xE7C9, 0x9172, 0xE7CA, 0x918B, 0xE7CB, 0x9189, 0xE7CC, 0x9182,\n\t0xE7CD, 0x91A2, 0xE7CE, 0x91AB, 0xE7CF, 0x91AF, 0xE7D0, 0x91AA,\n\t0xE7D1, 0x91B5, 0xE7D2, 0x91B4, 0xE7D3, 0x91BA, 0xE7D4, 0x91C0,\n\t0xE7D5, 0x91C1, 0xE7D6, 0x91C9, 0xE7D7, 0x91CB, 0xE7D8, 0x91D0,\n\t0xE7D9, 0x91D6, 0xE7DA, 0x91DF, 0xE7DB, 0x91E1, 0xE7DC, 0x91DB,\n\t0xE7DD, 0x91FC, 0xE7DE, 0x91F5, 0xE7DF, 0x91F6, 0xE7E0, 0x921E,\n\t0xE7E1, 0x91FF, 0xE7E2, 0x9214, 0xE7E3, 0x922C, 0xE7E4, 0x9215,\n\t0xE7E5, 0x9211, 0xE7E6, 0x925E, 0xE7E7, 0x9257, 0xE7E8, 0x9245,\n\t0xE7E9, 0x9249, 0xE7EA, 0x9264, 0xE7EB, 0x9248, 0xE7EC, 0x9295,\n\t0xE7ED, 0x923F, 0xE7EE, 0x924B, 0xE7EF, 0x9250, 0xE7F0, 0x929C,\n\t0xE7F1, 0x9296, 0xE7F2, 0x9293, 0xE7F3, 0x929B, 0xE7F4, 0x925A,\n\t0xE7F5, 0x92CF, 0xE7F6, 0x92B9, 0xE7F7, 0x92B7, 0xE7F8, 0x92E9,\n\t0xE7F9, 0x930F, 0xE7FA, 0x92FA, 0xE7FB, 0x9344, 0xE7FC, 0x932E,\n\t0xE840, 0x9319, 0xE841, 0x9322, 0xE842, 0x931A, 0xE843, 0x9323,\n\t0xE844, 0x933A, 0xE845, 0x9335, 0xE846, 0x933B, 0xE847, 0x935C,\n\t0xE848, 0x9360, 0xE849, 0x937C, 0xE84A, 0x936E, 0xE84B, 0x9356,\n\t0xE84C, 0x93B0, 0xE84D, 0x93AC, 0xE84E, 0x93AD, 0xE84F, 0x9394,\n\t0xE850, 0x93B9, 0xE851, 0x93D6, 0xE852, 0x93D7, 0xE853, 0x93E8,\n\t0xE854, 0x93E5, 0xE855, 0x93D8, 0xE856, 0x93C3, 0xE857, 0x93DD,\n\t0xE858, 0x93D0, 0xE859, 0x93C8, 0xE85A, 0x93E4, 0xE85B, 0x941A,\n\t0xE85C, 0x9414, 0xE85D, 0x9413, 0xE85E, 0x9403, 0xE85F, 0x9407,\n\t0xE860, 0x9410, 0xE861, 0x9436, 0xE862, 0x942B, 0xE863, 0x9435,\n\t0xE864, 0x9421, 0xE865, 0x943A, 0xE866, 0x9441, 0xE867, 0x9452,\n\t0xE868, 0x9444, 0xE869, 0x945B, 0xE86A, 0x9460, 0xE86B, 0x9462,\n\t0xE86C, 0x945E, 0xE86D, 0x946A, 0xE86E, 0x9229, 0xE86F, 0x9470,\n\t0xE870, 0x9475, 0xE871, 0x9477, 0xE872, 0x947D, 0xE873, 0x945A,\n\t0xE874, 0x947C, 0xE875, 0x947E, 0xE876, 0x9481, 0xE877, 0x947F,\n\t0xE878, 0x9582, 0xE879, 0x9587, 0xE87A, 0x958A, 0xE87B, 0x9594,\n\t0xE87C, 0x9596, 0xE87D, 0x9598, 0xE87E, 0x9599, 0xE880, 0x95A0,\n\t0xE881, 0x95A8, 0xE882, 0x95A7, 0xE883, 0x95AD, 0xE884, 0x95BC,\n\t0xE885, 0x95BB, 0xE886, 0x95B9, 0xE887, 0x95BE, 0xE888, 0x95CA,\n\t0xE889, 0x6FF6, 0xE88A, 0x95C3, 0xE88B, 0x95CD, 0xE88C, 0x95CC,\n\t0xE88D, 0x95D5, 0xE88E, 0x95D4, 0xE88F, 0x95D6, 0xE890, 0x95DC,\n\t0xE891, 0x95E1, 0xE892, 0x95E5, 0xE893, 0x95E2, 0xE894, 0x9621,\n\t0xE895, 0x9628, 0xE896, 0x962E, 0xE897, 0x962F, 0xE898, 0x9642,\n\t0xE899, 0x964C, 0xE89A, 0x964F, 0xE89B, 0x964B, 0xE89C, 0x9677,\n\t0xE89D, 0x965C, 0xE89E, 0x965E, 0xE89F, 0x965D, 0xE8A0, 0x965F,\n\t0xE8A1, 0x9666, 0xE8A2, 0x9672, 0xE8A3, 0x966C, 0xE8A4, 0x968D,\n\t0xE8A5, 0x9698, 0xE8A6, 0x9695, 0xE8A7, 0x9697, 0xE8A8, 0x96AA,\n\t0xE8A9, 0x96A7, 0xE8AA, 0x96B1, 0xE8AB, 0x96B2, 0xE8AC, 0x96B0,\n\t0xE8AD, 0x96B4, 0xE8AE, 0x96B6, 0xE8AF, 0x96B8, 0xE8B0, 0x96B9,\n\t0xE8B1, 0x96CE, 0xE8B2, 0x96CB, 0xE8B3, 0x96C9, 0xE8B4, 0x96CD,\n\t0xE8B5, 0x894D, 0xE8B6, 0x96DC, 0xE8B7, 0x970D, 0xE8B8, 0x96D5,\n\t0xE8B9, 0x96F9, 0xE8BA, 0x9704, 0xE8BB, 0x9706, 0xE8BC, 0x9708,\n\t0xE8BD, 0x9713, 0xE8BE, 0x970E, 0xE8BF, 0x9711, 0xE8C0, 0x970F,\n\t0xE8C1, 0x9716, 0xE8C2, 0x9719, 0xE8C3, 0x9724, 0xE8C4, 0x972A,\n\t0xE8C5, 0x9730, 0xE8C6, 0x9739, 0xE8C7, 0x973D, 0xE8C8, 0x973E,\n\t0xE8C9, 0x9744, 0xE8CA, 0x9746, 0xE8CB, 0x9748, 0xE8CC, 0x9742,\n\t0xE8CD, 0x9749, 0xE8CE, 0x975C, 0xE8CF, 0x9760, 0xE8D0, 0x9764,\n\t0xE8D1, 0x9766, 0xE8D2, 0x9768, 0xE8D3, 0x52D2, 0xE8D4, 0x976B,\n\t0xE8D5, 0x9771, 0xE8D6, 0x9779, 0xE8D7, 0x9785, 0xE8D8, 0x977C,\n\t0xE8D9, 0x9781, 0xE8DA, 0x977A, 0xE8DB, 0x9786, 0xE8DC, 0x978B,\n\t0xE8DD, 0x978F, 0xE8DE, 0x9790, 0xE8DF, 0x979C, 0xE8E0, 0x97A8,\n\t0xE8E1, 0x97A6, 0xE8E2, 0x97A3, 0xE8E3, 0x97B3, 0xE8E4, 0x97B4,\n\t0xE8E5, 0x97C3, 0xE8E6, 0x97C6, 0xE8E7, 0x97C8, 0xE8E8, 0x97CB,\n\t0xE8E9, 0x97DC, 0xE8EA, 0x97ED, 0xE8EB, 0x9F4F, 0xE8EC, 0x97F2,\n\t0xE8ED, 0x7ADF, 0xE8EE, 0x97F6, 0xE8EF, 0x97F5, 0xE8F0, 0x980F,\n\t0xE8F1, 0x980C, 0xE8F2, 0x9838, 0xE8F3, 0x9824, 0xE8F4, 0x9821,\n\t0xE8F5, 0x9837, 0xE8F6, 0x983D, 0xE8F7, 0x9846, 0xE8F8, 0x984F,\n\t0xE8F9, 0x984B, 0xE8FA, 0x986B, 0xE8FB, 0x986F, 0xE8FC, 0x9870,\n\t0xE940, 0x9871, 0xE941, 0x9874, 0xE942, 0x9873, 0xE943, 0x98AA,\n\t0xE944, 0x98AF, 0xE945, 0x98B1, 0xE946, 0x98B6, 0xE947, 0x98C4,\n\t0xE948, 0x98C3, 0xE949, 0x98C6, 0xE94A, 0x98E9, 0xE94B, 0x98EB,\n\t0xE94C, 0x9903, 0xE94D, 0x9909, 0xE94E, 0x9912, 0xE94F, 0x9914,\n\t0xE950, 0x9918, 0xE951, 0x9921, 0xE952, 0x991D, 0xE953, 0x991E,\n\t0xE954, 0x9924, 0xE955, 0x9920, 0xE956, 0x992C, 0xE957, 0x992E,\n\t0xE958, 0x993D, 0xE959, 0x993E, 0xE95A, 0x9942, 0xE95B, 0x9949,\n\t0xE95C, 0x9945, 0xE95D, 0x9950, 0xE95E, 0x994B, 0xE95F, 0x9951,\n\t0xE960, 0x9952, 0xE961, 0x994C, 0xE962, 0x9955, 0xE963, 0x9997,\n\t0xE964, 0x9998, 0xE965, 0x99A5, 0xE966, 0x99AD, 0xE967, 0x99AE,\n\t0xE968, 0x99BC, 0xE969, 0x99DF, 0xE96A, 0x99DB, 0xE96B, 0x99DD,\n\t0xE96C, 0x99D8, 0xE96D, 0x99D1, 0xE96E, 0x99ED, 0xE96F, 0x99EE,\n\t0xE970, 0x99F1, 0xE971, 0x99F2, 0xE972, 0x99FB, 0xE973, 0x99F8,\n\t0xE974, 0x9A01, 0xE975, 0x9A0F, 0xE976, 0x9A05, 0xE977, 0x99E2,\n\t0xE978, 0x9A19, 0xE979, 0x9A2B, 0xE97A, 0x9A37, 0xE97B, 0x9A45,\n\t0xE97C, 0x9A42, 0xE97D, 0x9A40, 0xE97E, 0x9A43, 0xE980, 0x9A3E,\n\t0xE981, 0x9A55, 0xE982, 0x9A4D, 0xE983, 0x9A5B, 0xE984, 0x9A57,\n\t0xE985, 0x9A5F, 0xE986, 0x9A62, 0xE987, 0x9A65, 0xE988, 0x9A64,\n\t0xE989, 0x9A69, 0xE98A, 0x9A6B, 0xE98B, 0x9A6A, 0xE98C, 0x9AAD,\n\t0xE98D, 0x9AB0, 0xE98E, 0x9ABC, 0xE98F, 0x9AC0, 0xE990, 0x9ACF,\n\t0xE991, 0x9AD1, 0xE992, 0x9AD3, 0xE993, 0x9AD4, 0xE994, 0x9ADE,\n\t0xE995, 0x9ADF, 0xE996, 0x9AE2, 0xE997, 0x9AE3, 0xE998, 0x9AE6,\n\t0xE999, 0x9AEF, 0xE99A, 0x9AEB, 0xE99B, 0x9AEE, 0xE99C, 0x9AF4,\n\t0xE99D, 0x9AF1, 0xE99E, 0x9AF7, 0xE99F, 0x9AFB, 0xE9A0, 0x9B06,\n\t0xE9A1, 0x9B18, 0xE9A2, 0x9B1A, 0xE9A3, 0x9B1F, 0xE9A4, 0x9B22,\n\t0xE9A5, 0x9B23, 0xE9A6, 0x9B25, 0xE9A7, 0x9B27, 0xE9A8, 0x9B28,\n\t0xE9A9, 0x9B29, 0xE9AA, 0x9B2A, 0xE9AB, 0x9B2E, 0xE9AC, 0x9B2F,\n\t0xE9AD, 0x9B32, 0xE9AE, 0x9B44, 0xE9AF, 0x9B43, 0xE9B0, 0x9B4F,\n\t0xE9B1, 0x9B4D, 0xE9B2, 0x9B4E, 0xE9B3, 0x9B51, 0xE9B4, 0x9B58,\n\t0xE9B5, 0x9B74, 0xE9B6, 0x9B93, 0xE9B7, 0x9B83, 0xE9B8, 0x9B91,\n\t0xE9B9, 0x9B96, 0xE9BA, 0x9B97, 0xE9BB, 0x9B9F, 0xE9BC, 0x9BA0,\n\t0xE9BD, 0x9BA8, 0xE9BE, 0x9BB4, 0xE9BF, 0x9BC0, 0xE9C0, 0x9BCA,\n\t0xE9C1, 0x9BB9, 0xE9C2, 0x9BC6, 0xE9C3, 0x9BCF, 0xE9C4, 0x9BD1,\n\t0xE9C5, 0x9BD2, 0xE9C6, 0x9BE3, 0xE9C7, 0x9BE2, 0xE9C8, 0x9BE4,\n\t0xE9C9, 0x9BD4, 0xE9CA, 0x9BE1, 0xE9CB, 0x9C3A, 0xE9CC, 0x9BF2,\n\t0xE9CD, 0x9BF1, 0xE9CE, 0x9BF0, 0xE9CF, 0x9C15, 0xE9D0, 0x9C14,\n\t0xE9D1, 0x9C09, 0xE9D2, 0x9C13, 0xE9D3, 0x9C0C, 0xE9D4, 0x9C06,\n\t0xE9D5, 0x9C08, 0xE9D6, 0x9C12, 0xE9D7, 0x9C0A, 0xE9D8, 0x9C04,\n\t0xE9D9, 0x9C2E, 0xE9DA, 0x9C1B, 0xE9DB, 0x9C25, 0xE9DC, 0x9C24,\n\t0xE9DD, 0x9C21, 0xE9DE, 0x9C30, 0xE9DF, 0x9C47, 0xE9E0, 0x9C32,\n\t0xE9E1, 0x9C46, 0xE9E2, 0x9C3E, 0xE9E3, 0x9C5A, 0xE9E4, 0x9C60,\n\t0xE9E5, 0x9C67, 0xE9E6, 0x9C76, 0xE9E7, 0x9C78, 0xE9E8, 0x9CE7,\n\t0xE9E9, 0x9CEC, 0xE9EA, 0x9CF0, 0xE9EB, 0x9D09, 0xE9EC, 0x9D08,\n\t0xE9ED, 0x9CEB, 0xE9EE, 0x9D03, 0xE9EF, 0x9D06, 0xE9F0, 0x9D2A,\n\t0xE9F1, 0x9D26, 0xE9F2, 0x9DAF, 0xE9F3, 0x9D23, 0xE9F4, 0x9D1F,\n\t0xE9F5, 0x9D44, 0xE9F6, 0x9D15, 0xE9F7, 0x9D12, 0xE9F8, 0x9D41,\n\t0xE9F9, 0x9D3F, 0xE9FA, 0x9D3E, 0xE9FB, 0x9D46, 0xE9FC, 0x9D48,\n\t0xEA40, 0x9D5D, 0xEA41, 0x9D5E, 0xEA42, 0x9D64, 0xEA43, 0x9D51,\n\t0xEA44, 0x9D50, 0xEA45, 0x9D59, 0xEA46, 0x9D72, 0xEA47, 0x9D89,\n\t0xEA48, 0x9D87, 0xEA49, 0x9DAB, 0xEA4A, 0x9D6F, 0xEA4B, 0x9D7A,\n\t0xEA4C, 0x9D9A, 0xEA4D, 0x9DA4, 0xEA4E, 0x9DA9, 0xEA4F, 0x9DB2,\n\t0xEA50, 0x9DC4, 0xEA51, 0x9DC1, 0xEA52, 0x9DBB, 0xEA53, 0x9DB8,\n\t0xEA54, 0x9DBA, 0xEA55, 0x9DC6, 0xEA56, 0x9DCF, 0xEA57, 0x9DC2,\n\t0xEA58, 0x9DD9, 0xEA59, 0x9DD3, 0xEA5A, 0x9DF8, 0xEA5B, 0x9DE6,\n\t0xEA5C, 0x9DED, 0xEA5D, 0x9DEF, 0xEA5E, 0x9DFD, 0xEA5F, 0x9E1A,\n\t0xEA60, 0x9E1B, 0xEA61, 0x9E1E, 0xEA62, 0x9E75, 0xEA63, 0x9E79,\n\t0xEA64, 0x9E7D, 0xEA65, 0x9E81, 0xEA66, 0x9E88, 0xEA67, 0x9E8B,\n\t0xEA68, 0x9E8C, 0xEA69, 0x9E92, 0xEA6A, 0x9E95, 0xEA6B, 0x9E91,\n\t0xEA6C, 0x9E9D, 0xEA6D, 0x9EA5, 0xEA6E, 0x9EA9, 0xEA6F, 0x9EB8,\n\t0xEA70, 0x9EAA, 0xEA71, 0x9EAD, 0xEA72, 0x9761, 0xEA73, 0x9ECC,\n\t0xEA74, 0x9ECE, 0xEA75, 0x9ECF, 0xEA76, 0x9ED0, 0xEA77, 0x9ED4,\n\t0xEA78, 0x9EDC, 0xEA79, 0x9EDE, 0xEA7A, 0x9EDD, 0xEA7B, 0x9EE0,\n\t0xEA7C, 0x9EE5, 0xEA7D, 0x9EE8, 0xEA7E, 0x9EEF, 0xEA80, 0x9EF4,\n\t0xEA81, 0x9EF6, 0xEA82, 0x9EF7, 0xEA83, 0x9EF9, 0xEA84, 0x9EFB,\n\t0xEA85, 0x9EFC, 0xEA86, 0x9EFD, 0xEA87, 0x9F07, 0xEA88, 0x9F08,\n\t0xEA89, 0x76B7, 0xEA8A, 0x9F15, 0xEA8B, 0x9F21, 0xEA8C, 0x9F2C,\n\t0xEA8D, 0x9F3E, 0xEA8E, 0x9F4A, 0xEA8F, 0x9F52, 0xEA90, 0x9F54,\n\t0xEA91, 0x9F63, 0xEA92, 0x9F5F, 0xEA93, 0x9F60, 0xEA94, 0x9F61,\n\t0xEA95, 0x9F66, 0xEA96, 0x9F67, 0xEA97, 0x9F6C, 0xEA98, 0x9F6A,\n\t0xEA99, 0x9F77, 0xEA9A, 0x9F72, 0xEA9B, 0x9F76, 0xEA9C, 0x9F95,\n\t0xEA9D, 0x9F9C, 0xEA9E, 0x9FA0, 0xEA9F, 0x582F, 0xEAA0, 0x69C7,\n\t0xEAA1, 0x9059, 0xEAA2, 0x7464, 0xEAA3, 0x51DC, 0xEAA4, 0x7199,\n\t0xFA40, 0x2170, 0xFA41, 0x2171, 0xFA42, 0x2172, 0xFA43, 0x2173,\n\t0xFA44, 0x2174, 0xFA45, 0x2175, 0xFA46, 0x2176, 0xFA47, 0x2177,\n\t0xFA48, 0x2178, 0xFA49, 0x2179, 0xFA55, 0xFFE4, 0xFA56, 0xFF07,\n\t0xFA57, 0xFF02, 0xFA5C, 0x7E8A, 0xFA5D, 0x891C, 0xFA5E, 0x9348,\n\t0xFA5F, 0x9288, 0xFA60, 0x84DC, 0xFA61, 0x4FC9, 0xFA62, 0x70BB,\n\t0xFA63, 0x6631, 0xFA64, 0x68C8, 0xFA65, 0x92F9, 0xFA66, 0x66FB,\n\t0xFA67, 0x5F45, 0xFA68, 0x4E28, 0xFA69, 0x4EE1, 0xFA6A, 0x4EFC,\n\t0xFA6B, 0x4F00, 0xFA6C, 0x4F03, 0xFA6D, 0x4F39, 0xFA6E, 0x4F56,\n\t0xFA6F, 0x4F92, 0xFA70, 0x4F8A, 0xFA71, 0x4F9A, 0xFA72, 0x4F94,\n\t0xFA73, 0x4FCD, 0xFA74, 0x5040, 0xFA75, 0x5022, 0xFA76, 0x4FFF,\n\t0xFA77, 0x501E, 0xFA78, 0x5046, 0xFA79, 0x5070, 0xFA7A, 0x5042,\n\t0xFA7B, 0x5094, 0xFA7C, 0x50F4, 0xFA7D, 0x50D8, 0xFA7E, 0x514A,\n\t0xFA80, 0x5164, 0xFA81, 0x519D, 0xFA82, 0x51BE, 0xFA83, 0x51EC,\n\t0xFA84, 0x5215, 0xFA85, 0x529C, 0xFA86, 0x52A6, 0xFA87, 0x52C0,\n\t0xFA88, 0x52DB, 0xFA89, 0x5300, 0xFA8A, 0x5307, 0xFA8B, 0x5324,\n\t0xFA8C, 0x5372, 0xFA8D, 0x5393, 0xFA8E, 0x53B2, 0xFA8F, 0x53DD,\n\t0xFA90, 0xFA0E, 0xFA91, 0x549C, 0xFA92, 0x548A, 0xFA93, 0x54A9,\n\t0xFA94, 0x54FF, 0xFA95, 0x5586, 0xFA96, 0x5759, 0xFA97, 0x5765,\n\t0xFA98, 0x57AC, 0xFA99, 0x57C8, 0xFA9A, 0x57C7, 0xFA9B, 0xFA0F,\n\t0xFA9C, 0xFA10, 0xFA9D, 0x589E, 0xFA9E, 0x58B2, 0xFA9F, 0x590B,\n\t0xFAA0, 0x5953, 0xFAA1, 0x595B, 0xFAA2, 0x595D, 0xFAA3, 0x5963,\n\t0xFAA4, 0x59A4, 0xFAA5, 0x59BA, 0xFAA6, 0x5B56, 0xFAA7, 0x5BC0,\n\t0xFAA8, 0x752F, 0xFAA9, 0x5BD8, 0xFAAA, 0x5BEC, 0xFAAB, 0x5C1E,\n\t0xFAAC, 0x5CA6, 0xFAAD, 0x5CBA, 0xFAAE, 0x5CF5, 0xFAAF, 0x5D27,\n\t0xFAB0, 0x5D53, 0xFAB1, 0xFA11, 0xFAB2, 0x5D42, 0xFAB3, 0x5D6D,\n\t0xFAB4, 0x5DB8, 0xFAB5, 0x5DB9, 0xFAB6, 0x5DD0, 0xFAB7, 0x5F21,\n\t0xFAB8, 0x5F34, 0xFAB9, 0x5F67, 0xFABA, 0x5FB7, 0xFABB, 0x5FDE,\n\t0xFABC, 0x605D, 0xFABD, 0x6085, 0xFABE, 0x608A, 0xFABF, 0x60DE,\n\t0xFAC0, 0x60D5, 0xFAC1, 0x6120, 0xFAC2, 0x60F2, 0xFAC3, 0x6111,\n\t0xFAC4, 0x6137, 0xFAC5, 0x6130, 0xFAC6, 0x6198, 0xFAC7, 0x6213,\n\t0xFAC8, 0x62A6, 0xFAC9, 0x63F5, 0xFACA, 0x6460, 0xFACB, 0x649D,\n\t0xFACC, 0x64CE, 0xFACD, 0x654E, 0xFACE, 0x6600, 0xFACF, 0x6615,\n\t0xFAD0, 0x663B, 0xFAD1, 0x6609, 0xFAD2, 0x662E, 0xFAD3, 0x661E,\n\t0xFAD4, 0x6624, 0xFAD5, 0x6665, 0xFAD6, 0x6657, 0xFAD7, 0x6659,\n\t0xFAD8, 0xFA12, 0xFAD9, 0x6673, 0xFADA, 0x6699, 0xFADB, 0x66A0,\n\t0xFADC, 0x66B2, 0xFADD, 0x66BF, 0xFADE, 0x66FA, 0xFADF, 0x670E,\n\t0xFAE0, 0xF929, 0xFAE1, 0x6766, 0xFAE2, 0x67BB, 0xFAE3, 0x6852,\n\t0xFAE4, 0x67C0, 0xFAE5, 0x6801, 0xFAE6, 0x6844, 0xFAE7, 0x68CF,\n\t0xFAE8, 0xFA13, 0xFAE9, 0x6968, 0xFAEA, 0xFA14, 0xFAEB, 0x6998,\n\t0xFAEC, 0x69E2, 0xFAED, 0x6A30, 0xFAEE, 0x6A6B, 0xFAEF, 0x6A46,\n\t0xFAF0, 0x6A73, 0xFAF1, 0x6A7E, 0xFAF2, 0x6AE2, 0xFAF3, 0x6AE4,\n\t0xFAF4, 0x6BD6, 0xFAF5, 0x6C3F, 0xFAF6, 0x6C5C, 0xFAF7, 0x6C86,\n\t0xFAF8, 0x6C6F, 0xFAF9, 0x6CDA, 0xFAFA, 0x6D04, 0xFAFB, 0x6D87,\n\t0xFAFC, 0x6D6F, 0xFB40, 0x6D96, 0xFB41, 0x6DAC, 0xFB42, 0x6DCF,\n\t0xFB43, 0x6DF8, 0xFB44, 0x6DF2, 0xFB45, 0x6DFC, 0xFB46, 0x6E39,\n\t0xFB47, 0x6E5C, 0xFB48, 0x6E27, 0xFB49, 0x6E3C, 0xFB4A, 0x6EBF,\n\t0xFB4B, 0x6F88, 0xFB4C, 0x6FB5, 0xFB4D, 0x6FF5, 0xFB4E, 0x7005,\n\t0xFB4F, 0x7007, 0xFB50, 0x7028, 0xFB51, 0x7085, 0xFB52, 0x70AB,\n\t0xFB53, 0x710F, 0xFB54, 0x7104, 0xFB55, 0x715C, 0xFB56, 0x7146,\n\t0xFB57, 0x7147, 0xFB58, 0xFA15, 0xFB59, 0x71C1, 0xFB5A, 0x71FE,\n\t0xFB5B, 0x72B1, 0xFB5C, 0x72BE, 0xFB5D, 0x7324, 0xFB5E, 0xFA16,\n\t0xFB5F, 0x7377, 0xFB60, 0x73BD, 0xFB61, 0x73C9, 0xFB62, 0x73D6,\n\t0xFB63, 0x73E3, 0xFB64, 0x73D2, 0xFB65, 0x7407, 0xFB66, 0x73F5,\n\t0xFB67, 0x7426, 0xFB68, 0x742A, 0xFB69, 0x7429, 0xFB6A, 0x742E,\n\t0xFB6B, 0x7462, 0xFB6C, 0x7489, 0xFB6D, 0x749F, 0xFB6E, 0x7501,\n\t0xFB6F, 0x756F, 0xFB70, 0x7682, 0xFB71, 0x769C, 0xFB72, 0x769E,\n\t0xFB73, 0x769B, 0xFB74, 0x76A6, 0xFB75, 0xFA17, 0xFB76, 0x7746,\n\t0xFB77, 0x52AF, 0xFB78, 0x7821, 0xFB79, 0x784E, 0xFB7A, 0x7864,\n\t0xFB7B, 0x787A, 0xFB7C, 0x7930, 0xFB7D, 0xFA18, 0xFB7E, 0xFA19,\n\t0xFB80, 0xFA1A, 0xFB81, 0x7994, 0xFB82, 0xFA1B, 0xFB83, 0x799B,\n\t0xFB84, 0x7AD1, 0xFB85, 0x7AE7, 0xFB86, 0xFA1C, 0xFB87, 0x7AEB,\n\t0xFB88, 0x7B9E, 0xFB89, 0xFA1D, 0xFB8A, 0x7D48, 0xFB8B, 0x7D5C,\n\t0xFB8C, 0x7DB7, 0xFB8D, 0x7DA0, 0xFB8E, 0x7DD6, 0xFB8F, 0x7E52,\n\t0xFB90, 0x7F47, 0xFB91, 0x7FA1, 0xFB92, 0xFA1E, 0xFB93, 0x8301,\n\t0xFB94, 0x8362, 0xFB95, 0x837F, 0xFB96, 0x83C7, 0xFB97, 0x83F6,\n\t0xFB98, 0x8448, 0xFB99, 0x84B4, 0xFB9A, 0x8553, 0xFB9B, 0x8559,\n\t0xFB9C, 0x856B, 0xFB9D, 0xFA1F, 0xFB9E, 0x85B0, 0xFB9F, 0xFA20,\n\t0xFBA0, 0xFA21, 0xFBA1, 0x8807, 0xFBA2, 0x88F5, 0xFBA3, 0x8A12,\n\t0xFBA4, 0x8A37, 0xFBA5, 0x8A79, 0xFBA6, 0x8AA7, 0xFBA7, 0x8ABE,\n\t0xFBA8, 0x8ADF, 0xFBA9, 0xFA22, 0xFBAA, 0x8AF6, 0xFBAB, 0x8B53,\n\t0xFBAC, 0x8B7F, 0xFBAD, 0x8CF0, 0xFBAE, 0x8CF4, 0xFBAF, 0x8D12,\n\t0xFBB0, 0x8D76, 0xFBB1, 0xFA23, 0xFBB2, 0x8ECF, 0xFBB3, 0xFA24,\n\t0xFBB4, 0xFA25, 0xFBB5, 0x9067, 0xFBB6, 0x90DE, 0xFBB7, 0xFA26,\n\t0xFBB8, 0x9115, 0xFBB9, 0x9127, 0xFBBA, 0x91DA, 0xFBBB, 0x91D7,\n\t0xFBBC, 0x91DE, 0xFBBD, 0x91ED, 0xFBBE, 0x91EE, 0xFBBF, 0x91E4,\n\t0xFBC0, 0x91E5, 0xFBC1, 0x9206, 0xFBC2, 0x9210, 0xFBC3, 0x920A,\n\t0xFBC4, 0x923A, 0xFBC5, 0x9240, 0xFBC6, 0x923C, 0xFBC7, 0x924E,\n\t0xFBC8, 0x9259, 0xFBC9, 0x9251, 0xFBCA, 0x9239, 0xFBCB, 0x9267,\n\t0xFBCC, 0x92A7, 0xFBCD, 0x9277, 0xFBCE, 0x9278, 0xFBCF, 0x92E7,\n\t0xFBD0, 0x92D7, 0xFBD1, 0x92D9, 0xFBD2, 0x92D0, 0xFBD3, 0xFA27,\n\t0xFBD4, 0x92D5, 0xFBD5, 0x92E0, 0xFBD6, 0x92D3, 0xFBD7, 0x9325,\n\t0xFBD8, 0x9321, 0xFBD9, 0x92FB, 0xFBDA, 0xFA28, 0xFBDB, 0x931E,\n\t0xFBDC, 0x92FF, 0xFBDD, 0x931D, 0xFBDE, 0x9302, 0xFBDF, 0x9370,\n\t0xFBE0, 0x9357, 0xFBE1, 0x93A4, 0xFBE2, 0x93C6, 0xFBE3, 0x93DE,\n\t0xFBE4, 0x93F8, 0xFBE5, 0x9431, 0xFBE6, 0x9445, 0xFBE7, 0x9448,\n\t0xFBE8, 0x9592, 0xFBE9, 0xF9DC, 0xFBEA, 0xFA29, 0xFBEB, 0x969D,\n\t0xFBEC, 0x96AF, 0xFBED, 0x9733, 0xFBEE, 0x973B, 0xFBEF, 0x9743,\n\t0xFBF0, 0x974D, 0xFBF1, 0x974F, 0xFBF2, 0x9751, 0xFBF3, 0x9755,\n\t0xFBF4, 0x9857, 0xFBF5, 0x9865, 0xFBF6, 0xFA2A, 0xFBF7, 0xFA2B,\n\t0xFBF8, 0x9927, 0xFBF9, 0xFA2C, 0xFBFA, 0x999E, 0xFBFB, 0x9A4E,\n\t0xFBFC, 0x9AD9, 0xFC40, 0x9ADC, 0xFC41, 0x9B75, 0xFC42, 0x9B72,\n\t0xFC43, 0x9B8F, 0xFC44, 0x9BB1, 0xFC45, 0x9BBB, 0xFC46, 0x9C00,\n\t0xFC47, 0x9D70, 0xFC48, 0x9D6B, 0xFC49, 0xFA2D, 0xFC4A, 0x9E19,\n\t0xFC4B, 0x9ED1, 0, 0\n};\n#endif\n\n\n\nWCHAR ff_convert (\t/* Converted code, 0 means conversion error */\n\tWCHAR\tchr,\t/* Character code to be converted */\n\tUINT\tdir\t\t/* 0: Unicode to OEM code, 1: OEM code to Unicode */\n)\n{\n\tconst WCHAR *p;\n\tWCHAR c;\n\tint i, n, li, hi;\n\n\n\tif (chr <= 0x80) {\t/* ASCII */\n\t\tc = chr;\n\t} else {\n#if !_TINY_TABLE\n\t\tif (dir) {\t\t/* OEM code to unicode */\n\t\t\tp = sjis2uni;\n\t\t\thi = sizeof sjis2uni / 4 - 1;\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tp = uni2sjis;\n\t\t\thi = sizeof uni2sjis / 4 - 1;\n\t\t}\n\t\tli = 0;\n\t\tfor (n = 16; n; n--) {\n\t\t\ti = li + (hi - li) / 2;\n\t\t\tif (chr == p[i * 2]) break;\n\t\t\tif (chr > p[i * 2])\n\t\t\t\tli = i;\n\t\t\telse\n\t\t\t\thi = i;\n\t\t}\n\t\tc = n ? p[i * 2 + 1] : 0;\n#else\n\t\tif (dir) {\t\t/* OEM code to unicode (Incremental search)*/\n\t\t\tp = &uni2sjis[1];\n\t\t\tdo {\n\t\t\t\tc = *p;\n\t\t\t\tp += 2;\n\t\t\t} while (c && c != chr);\n\t\t\tp -= 3;\n\t\t\tc = *p;\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tli = 0; hi = sizeof uni2sjis / 4 - 1;\n\t\t\tfor (n = 16; n; n--) {\n\t\t\t\ti = li + (hi - li) / 2;\n\t\t\t\tif (chr == uni2sjis[i * 2]) break;\n\t\t\t\tif (chr > uni2sjis[i * 2])\n\t\t\t\t\tli = i;\n\t\t\t\telse\n\t\t\t\t\thi = i;\n\t\t\t}\n\t\t\tc = n ? uni2sjis[i * 2 + 1] : 0;\n\t\t}\n#endif\n\t}\n\n\treturn c;\n}\n\n\n\nWCHAR ff_wtoupper (\t/* Returns upper converted character */\n\tWCHAR chr\t\t/* Unicode character to be upper converted (BMP only) */\n)\n{\n\t/* Compressed upper conversion table */\n\tstatic const WCHAR cvt1[] = {\t/* U+0000 - U+0FFF */\n\t\t/* Basic Latin */\n\t\t0x0061,0x031A,\n\t\t/* Latin-1 Supplement */\n\t\t0x00E0,0x0317,  0x00F8,0x0307,  0x00FF,0x0001,0x0178,\n\t\t/* Latin Extended-A */\n\t\t0x0100,0x0130,  0x0132,0x0106,  0x0139,0x0110,  0x014A,0x012E,  0x0179,0x0106,\n\t\t/* Latin Extended-B */\n\t\t0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,\n\t\t0x01CD,0x0110,  0x01DD,0x0001,0x018E,  0x01DE,0x0112,  0x01F3,0x0003,0x01F1,0x01F4,0x01F4,  0x01F8,0x0128,\n\t\t0x0222,0x0112,  0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,  0x0246,0x010A,\n\t\t/* IPA Extensions */\n\t\t0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,\n\t\t/* Greek, Coptic */\n\t\t0x037B,0x0003,0x03FD,0x03FE,0x03FF,  0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,  0x03B1,0x0311,\n\t\t0x03C2,0x0002,0x03A3,0x03A3,  0x03C4,0x0308,  0x03CC,0x0003,0x038C,0x038E,0x038F,  0x03D8,0x0118,\n\t\t0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,\n\t\t/* Cyrillic */\n\t\t0x0430,0x0320,  0x0450,0x0710,  0x0460,0x0122,  0x048A,0x0136,  0x04C1,0x010E,  0x04CF,0x0001,0x04C0,  0x04D0,0x0144,\n\t\t/* Armenian */\n\t\t0x0561,0x0426,\n\n\t\t0x0000\n\t};\n\tstatic const WCHAR cvt2[] = {\t/* U+1000 - U+FFFF */\n\t\t/* Phonetic Extensions */\n\t\t0x1D7D,0x0001,0x2C63,\n\t\t/* Latin Extended Additional */\n\t\t0x1E00,0x0196,  0x1EA0,0x015A,\n\t\t/* Greek Extended */\n\t\t0x1F00,0x0608,  0x1F10,0x0606,  0x1F20,0x0608,  0x1F30,0x0608,  0x1F40,0x0606,\n\t\t0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,  0x1F60,0x0608,\n\t\t0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,\n\t\t0x1F80,0x0608,  0x1F90,0x0608,  0x1FA0,0x0608,  0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,\n\t\t0x1FCC,0x0001,0x1FC3,  0x1FD0,0x0602,  0x1FE0,0x0602,  0x1FE5,0x0001,0x1FEC,  0x1FF2,0x0001,0x1FFC,\n\t\t/* Letterlike Symbols */\n\t\t0x214E,0x0001,0x2132,\n\t\t/* Number forms */\n\t\t0x2170,0x0210,  0x2184,0x0001,0x2183,\n\t\t/* Enclosed Alphanumerics */\n\t\t0x24D0,0x051A,  0x2C30,0x042F,\n\t\t/* Latin Extended-C */\n\t\t0x2C60,0x0102,  0x2C67,0x0106, 0x2C75,0x0102,\n\t\t/* Coptic */\n\t\t0x2C80,0x0164,\n\t\t/* Georgian Supplement */\n\t\t0x2D00,0x0826,\n\t\t/* Full-width */\n\t\t0xFF41,0x031A,\n\n\t\t0x0000\n\t};\n\tconst WCHAR *p;\n\tWCHAR bc, nc, cmd;\n\n\n\tp = chr < 0x1000 ? cvt1 : cvt2;\n\tfor (;;) {\n\t\tbc = *p++;\t\t\t\t\t\t\t\t/* Get block base */\n\t\tif (!bc || chr < bc) break;\n\t\tnc = *p++; cmd = nc >> 8; nc &= 0xFF;\t/* Get processing command and block size */\n\t\tif (chr < bc + nc) {\t/* In the block? */\n\t\t\tswitch (cmd) {\n\t\t\tcase 0:\tchr = p[chr - bc]; break;\t\t/* Table conversion */\n\t\t\tcase 1:\tchr -= (chr - bc) & 1; break;\t/* Case pairs */\n\t\t\tcase 2: chr -= 16; break;\t\t\t\t/* Shift -16 */\n\t\t\tcase 3:\tchr -= 32; break;\t\t\t\t/* Shift -32 */\n\t\t\tcase 4:\tchr -= 48; break;\t\t\t\t/* Shift -48 */\n\t\t\tcase 5:\tchr -= 26; break;\t\t\t\t/* Shift -26 */\n\t\t\tcase 6:\tchr += 8; break;\t\t\t\t/* Shift +8 */\n\t\t\tcase 7: chr -= 80; break;\t\t\t\t/* Shift -80 */\n\t\t\tcase 8:\tchr -= 0x1C60; break;\t\t\t/* Shift -0x1C60 */\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!cmd) p += nc;\n\t}\n\n\treturn chr;\n}\n\n"
  },
  {
    "path": "lib/fatfs/option/cc936.c",
    "content": "/*------------------------------------------------------------------------*/\n/* Unicode - OEM code bidirectional converter  (C)ChaN, 2015              */\n/* CP936 (Simplified Chinese GBK)                                         */\n/*------------------------------------------------------------------------*/\n\n#include \"../ff.h\"\n\n\n#if !_USE_LFN || _CODE_PAGE != 936\n#error This file is not needed in current configuration. Remove from the project.\n#endif\n\nstatic\nconst WCHAR uni2oem[] = {\n/*  Unicode - OEM,  Unicode - OEM,  Unicode - OEM,  Unicode - OEM */\n\t0x00A4, 0xA1E8, 0x00A7, 0xA1EC, 0x00A8, 0xA1A7, 0x00B0, 0xA1E3,\n\t0x00B1, 0xA1C0, 0x00B7, 0xA1A4, 0x00D7, 0xA1C1, 0x00E0, 0xA8A4,\n\t0x00E1, 0xA8A2, 0x00E8, 0xA8A8, 0x00E9, 0xA8A6, 0x00EA, 0xA8BA,\n\t0x00EC, 0xA8AC, 0x00ED, 0xA8AA, 0x00F2, 0xA8B0, 0x00F3, 0xA8AE,\n\t0x00F7, 0xA1C2, 0x00F9, 0xA8B4, 0x00FA, 0xA8B2, 0x00FC, 0xA8B9,\n\t0x0101, 0xA8A1, 0x0113, 0xA8A5, 0x011B, 0xA8A7, 0x012B, 0xA8A9,\n\t0x0144, 0xA8BD, 0x0148, 0xA8BE, 0x014D, 0xA8AD, 0x016B, 0xA8B1,\n\t0x01CE, 0xA8A3, 0x01D0, 0xA8AB, 0x01D2, 0xA8AF, 0x01D4, 0xA8B3,\n\t0x01D6, 0xA8B5, 0x01D8, 0xA8B6, 0x01DA, 0xA8B7, 0x01DC, 0xA8B8,\n\t0x0251, 0xA8BB, 0x0261, 0xA8C0, 0x02C7, 0xA1A6, 0x02C9, 0xA1A5,\n\t0x02CA, 0xA840, 0x02CB, 0xA841, 0x02D9, 0xA842, 0x0391, 0xA6A1,\n\t0x0392, 0xA6A2, 0x0393, 0xA6A3, 0x0394, 0xA6A4, 0x0395, 0xA6A5,\n\t0x0396, 0xA6A6, 0x0397, 0xA6A7, 0x0398, 0xA6A8, 0x0399, 0xA6A9,\n\t0x039A, 0xA6AA, 0x039B, 0xA6AB, 0x039C, 0xA6AC, 0x039D, 0xA6AD,\n\t0x039E, 0xA6AE, 0x039F, 0xA6AF, 0x03A0, 0xA6B0, 0x03A1, 0xA6B1,\n\t0x03A3, 0xA6B2, 0x03A4, 0xA6B3, 0x03A5, 0xA6B4, 0x03A6, 0xA6B5,\n\t0x03A7, 0xA6B6, 0x03A8, 0xA6B7, 0x03A9, 0xA6B8, 0x03B1, 0xA6C1,\n\t0x03B2, 0xA6C2, 0x03B3, 0xA6C3, 0x03B4, 0xA6C4, 0x03B5, 0xA6C5,\n\t0x03B6, 0xA6C6, 0x03B7, 0xA6C7, 0x03B8, 0xA6C8, 0x03B9, 0xA6C9,\n\t0x03BA, 0xA6CA, 0x03BB, 0xA6CB, 0x03BC, 0xA6CC, 0x03BD, 0xA6CD,\n\t0x03BE, 0xA6CE, 0x03BF, 0xA6CF, 0x03C0, 0xA6D0, 0x03C1, 0xA6D1,\n\t0x03C3, 0xA6D2, 0x03C4, 0xA6D3, 0x03C5, 0xA6D4, 0x03C6, 0xA6D5,\n\t0x03C7, 0xA6D6, 0x03C8, 0xA6D7, 0x03C9, 0xA6D8, 0x0401, 0xA7A7,\n\t0x0410, 0xA7A1, 0x0411, 0xA7A2, 0x0412, 0xA7A3, 0x0413, 0xA7A4,\n\t0x0414, 0xA7A5, 0x0415, 0xA7A6, 0x0416, 0xA7A8, 0x0417, 0xA7A9,\n\t0x0418, 0xA7AA, 0x0419, 0xA7AB, 0x041A, 0xA7AC, 0x041B, 0xA7AD,\n\t0x041C, 0xA7AE, 0x041D, 0xA7AF, 0x041E, 0xA7B0, 0x041F, 0xA7B1,\n\t0x0420, 0xA7B2, 0x0421, 0xA7B3, 0x0422, 0xA7B4, 0x0423, 0xA7B5,\n\t0x0424, 0xA7B6, 0x0425, 0xA7B7, 0x0426, 0xA7B8, 0x0427, 0xA7B9,\n\t0x0428, 0xA7BA, 0x0429, 0xA7BB, 0x042A, 0xA7BC, 0x042B, 0xA7BD,\n\t0x042C, 0xA7BE, 0x042D, 0xA7BF, 0x042E, 0xA7C0, 0x042F, 0xA7C1,\n\t0x0430, 0xA7D1, 0x0431, 0xA7D2, 0x0432, 0xA7D3, 0x0433, 0xA7D4,\n\t0x0434, 0xA7D5, 0x0435, 0xA7D6, 0x0436, 0xA7D8, 0x0437, 0xA7D9,\n\t0x0438, 0xA7DA, 0x0439, 0xA7DB, 0x043A, 0xA7DC, 0x043B, 0xA7DD,\n\t0x043C, 0xA7DE, 0x043D, 0xA7DF, 0x043E, 0xA7E0, 0x043F, 0xA7E1,\n\t0x0440, 0xA7E2, 0x0441, 0xA7E3, 0x0442, 0xA7E4, 0x0443, 0xA7E5,\n\t0x0444, 0xA7E6, 0x0445, 0xA7E7, 0x0446, 0xA7E8, 0x0447, 0xA7E9,\n\t0x0448, 0xA7EA, 0x0449, 0xA7EB, 0x044A, 0xA7EC, 0x044B, 0xA7ED,\n\t0x044C, 0xA7EE, 0x044D, 0xA7EF, 0x044E, 0xA7F0, 0x044F, 0xA7F1,\n\t0x0451, 0xA7D7, 0x2010, 0xA95C, 0x2013, 0xA843, 0x2014, 0xA1AA,\n\t0x2015, 0xA844, 0x2016, 0xA1AC, 0x2018, 0xA1AE, 0x2019, 0xA1AF,\n\t0x201C, 0xA1B0, 0x201D, 0xA1B1, 0x2025, 0xA845, 0x2026, 0xA1AD,\n\t0x2030, 0xA1EB, 0x2032, 0xA1E4, 0x2033, 0xA1E5, 0x2035, 0xA846,\n\t0x203B, 0xA1F9, 0x20AC, 0x0080, 0x2103, 0xA1E6, 0x2105, 0xA847,\n\t0x2109, 0xA848, 0x2116, 0xA1ED, 0x2121, 0xA959, 0x2160, 0xA2F1,\n\t0x2161, 0xA2F2, 0x2162, 0xA2F3, 0x2163, 0xA2F4, 0x2164, 0xA2F5,\n\t0x2165, 0xA2F6, 0x2166, 0xA2F7, 0x2167, 0xA2F8, 0x2168, 0xA2F9,\n\t0x2169, 0xA2FA, 0x216A, 0xA2FB, 0x216B, 0xA2FC, 0x2170, 0xA2A1,\n\t0x2171, 0xA2A2, 0x2172, 0xA2A3, 0x2173, 0xA2A4, 0x2174, 0xA2A5,\n\t0x2175, 0xA2A6, 0x2176, 0xA2A7, 0x2177, 0xA2A8, 0x2178, 0xA2A9,\n\t0x2179, 0xA2AA, 0x2190, 0xA1FB, 0x2191, 0xA1FC, 0x2192, 0xA1FA,\n\t0x2193, 0xA1FD, 0x2196, 0xA849, 0x2197, 0xA84A, 0x2198, 0xA84B,\n\t0x2199, 0xA84C, 0x2208, 0xA1CA, 0x220F, 0xA1C7, 0x2211, 0xA1C6,\n\t0x2215, 0xA84D, 0x221A, 0xA1CC, 0x221D, 0xA1D8, 0x221E, 0xA1DE,\n\t0x221F, 0xA84E, 0x2220, 0xA1CF, 0x2223, 0xA84F, 0x2225, 0xA1CE,\n\t0x2227, 0xA1C4, 0x2228, 0xA1C5, 0x2229, 0xA1C9, 0x222A, 0xA1C8,\n\t0x222B, 0xA1D2, 0x222E, 0xA1D3, 0x2234, 0xA1E0, 0x2235, 0xA1DF,\n\t0x2236, 0xA1C3, 0x2237, 0xA1CB, 0x223D, 0xA1D7, 0x2248, 0xA1D6,\n\t0x224C, 0xA1D5, 0x2252, 0xA850, 0x2260, 0xA1D9, 0x2261, 0xA1D4,\n\t0x2264, 0xA1DC, 0x2265, 0xA1DD, 0x2266, 0xA851, 0x2267, 0xA852,\n\t0x226E, 0xA1DA, 0x226F, 0xA1DB, 0x2295, 0xA892, 0x2299, 0xA1D1,\n\t0x22A5, 0xA1CD, 0x22BF, 0xA853, 0x2312, 0xA1D0, 0x2460, 0xA2D9,\n\t0x2461, 0xA2DA, 0x2462, 0xA2DB, 0x2463, 0xA2DC, 0x2464, 0xA2DD,\n\t0x2465, 0xA2DE, 0x2466, 0xA2DF, 0x2467, 0xA2E0, 0x2468, 0xA2E1,\n\t0x2469, 0xA2E2, 0x2474, 0xA2C5, 0x2475, 0xA2C6, 0x2476, 0xA2C7,\n\t0x2477, 0xA2C8, 0x2478, 0xA2C9, 0x2479, 0xA2CA, 0x247A, 0xA2CB,\n\t0x247B, 0xA2CC, 0x247C, 0xA2CD, 0x247D, 0xA2CE, 0x247E, 0xA2CF,\n\t0x247F, 0xA2D0, 0x2480, 0xA2D1, 0x2481, 0xA2D2, 0x2482, 0xA2D3,\n\t0x2483, 0xA2D4, 0x2484, 0xA2D5, 0x2485, 0xA2D6, 0x2486, 0xA2D7,\n\t0x2487, 0xA2D8, 0x2488, 0xA2B1, 0x2489, 0xA2B2, 0x248A, 0xA2B3,\n\t0x248B, 0xA2B4, 0x248C, 0xA2B5, 0x248D, 0xA2B6, 0x248E, 0xA2B7,\n\t0x248F, 0xA2B8, 0x2490, 0xA2B9, 0x2491, 0xA2BA, 0x2492, 0xA2BB,\n\t0x2493, 0xA2BC, 0x2494, 0xA2BD, 0x2495, 0xA2BE, 0x2496, 0xA2BF,\n\t0x2497, 0xA2C0, 0x2498, 0xA2C1, 0x2499, 0xA2C2, 0x249A, 0xA2C3,\n\t0x249B, 0xA2C4, 0x2500, 0xA9A4, 0x2501, 0xA9A5, 0x2502, 0xA9A6,\n\t0x2503, 0xA9A7, 0x2504, 0xA9A8, 0x2505, 0xA9A9, 0x2506, 0xA9AA,\n\t0x2507, 0xA9AB, 0x2508, 0xA9AC, 0x2509, 0xA9AD, 0x250A, 0xA9AE,\n\t0x250B, 0xA9AF, 0x250C, 0xA9B0, 0x250D, 0xA9B1, 0x250E, 0xA9B2,\n\t0x250F, 0xA9B3, 0x2510, 0xA9B4, 0x2511, 0xA9B5, 0x2512, 0xA9B6,\n\t0x2513, 0xA9B7, 0x2514, 0xA9B8, 0x2515, 0xA9B9, 0x2516, 0xA9BA,\n\t0x2517, 0xA9BB, 0x2518, 0xA9BC, 0x2519, 0xA9BD, 0x251A, 0xA9BE,\n\t0x251B, 0xA9BF, 0x251C, 0xA9C0, 0x251D, 0xA9C1, 0x251E, 0xA9C2,\n\t0x251F, 0xA9C3, 0x2520, 0xA9C4, 0x2521, 0xA9C5, 0x2522, 0xA9C6,\n\t0x2523, 0xA9C7, 0x2524, 0xA9C8, 0x2525, 0xA9C9, 0x2526, 0xA9CA,\n\t0x2527, 0xA9CB, 0x2528, 0xA9CC, 0x2529, 0xA9CD, 0x252A, 0xA9CE,\n\t0x252B, 0xA9CF, 0x252C, 0xA9D0, 0x252D, 0xA9D1, 0x252E, 0xA9D2,\n\t0x252F, 0xA9D3, 0x2530, 0xA9D4, 0x2531, 0xA9D5, 0x2532, 0xA9D6,\n\t0x2533, 0xA9D7, 0x2534, 0xA9D8, 0x2535, 0xA9D9, 0x2536, 0xA9DA,\n\t0x2537, 0xA9DB, 0x2538, 0xA9DC, 0x2539, 0xA9DD, 0x253A, 0xA9DE,\n\t0x253B, 0xA9DF, 0x253C, 0xA9E0, 0x253D, 0xA9E1, 0x253E, 0xA9E2,\n\t0x253F, 0xA9E3, 0x2540, 0xA9E4, 0x2541, 0xA9E5, 0x2542, 0xA9E6,\n\t0x2543, 0xA9E7, 0x2544, 0xA9E8, 0x2545, 0xA9E9, 0x2546, 0xA9EA,\n\t0x2547, 0xA9EB, 0x2548, 0xA9EC, 0x2549, 0xA9ED, 0x254A, 0xA9EE,\n\t0x254B, 0xA9EF, 0x2550, 0xA854, 0x2551, 0xA855, 0x2552, 0xA856,\n\t0x2553, 0xA857, 0x2554, 0xA858, 0x2555, 0xA859, 0x2556, 0xA85A,\n\t0x2557, 0xA85B, 0x2558, 0xA85C, 0x2559, 0xA85D, 0x255A, 0xA85E,\n\t0x255B, 0xA85F, 0x255C, 0xA860, 0x255D, 0xA861, 0x255E, 0xA862,\n\t0x255F, 0xA863, 0x2560, 0xA864, 0x2561, 0xA865, 0x2562, 0xA866,\n\t0x2563, 0xA867, 0x2564, 0xA868, 0x2565, 0xA869, 0x2566, 0xA86A,\n\t0x2567, 0xA86B, 0x2568, 0xA86C, 0x2569, 0xA86D, 0x256A, 0xA86E,\n\t0x256B, 0xA86F, 0x256C, 0xA870, 0x256D, 0xA871, 0x256E, 0xA872,\n\t0x256F, 0xA873, 0x2570, 0xA874, 0x2571, 0xA875, 0x2572, 0xA876,\n\t0x2573, 0xA877, 0x2581, 0xA878, 0x2582, 0xA879, 0x2583, 0xA87A,\n\t0x2584, 0xA87B, 0x2585, 0xA87C, 0x2586, 0xA87D, 0x2587, 0xA87E,\n\t0x2588, 0xA880, 0x2589, 0xA881, 0x258A, 0xA882, 0x258B, 0xA883,\n\t0x258C, 0xA884, 0x258D, 0xA885, 0x258E, 0xA886, 0x258F, 0xA887,\n\t0x2593, 0xA888, 0x2594, 0xA889, 0x2595, 0xA88A, 0x25A0, 0xA1F6,\n\t0x25A1, 0xA1F5, 0x25B2, 0xA1F8, 0x25B3, 0xA1F7, 0x25BC, 0xA88B,\n\t0x25BD, 0xA88C, 0x25C6, 0xA1F4, 0x25C7, 0xA1F3, 0x25CB, 0xA1F0,\n\t0x25CE, 0xA1F2, 0x25CF, 0xA1F1, 0x25E2, 0xA88D, 0x25E3, 0xA88E,\n\t0x25E4, 0xA88F, 0x25E5, 0xA890, 0x2605, 0xA1EF, 0x2606, 0xA1EE,\n\t0x2609, 0xA891, 0x2640, 0xA1E2, 0x2642, 0xA1E1, 0x3000, 0xA1A1,\n\t0x3001, 0xA1A2, 0x3002, 0xA1A3, 0x3003, 0xA1A8, 0x3005, 0xA1A9,\n\t0x3006, 0xA965, 0x3007, 0xA996, 0x3008, 0xA1B4, 0x3009, 0xA1B5,\n\t0x300A, 0xA1B6, 0x300B, 0xA1B7, 0x300C, 0xA1B8, 0x300D, 0xA1B9,\n\t0x300E, 0xA1BA, 0x300F, 0xA1BB, 0x3010, 0xA1BE, 0x3011, 0xA1BF,\n\t0x3012, 0xA893, 0x3013, 0xA1FE, 0x3014, 0xA1B2, 0x3015, 0xA1B3,\n\t0x3016, 0xA1BC, 0x3017, 0xA1BD, 0x301D, 0xA894, 0x301E, 0xA895,\n\t0x3021, 0xA940, 0x3022, 0xA941, 0x3023, 0xA942, 0x3024, 0xA943,\n\t0x3025, 0xA944, 0x3026, 0xA945, 0x3027, 0xA946, 0x3028, 0xA947,\n\t0x3029, 0xA948, 0x3041, 0xA4A1, 0x3042, 0xA4A2, 0x3043, 0xA4A3,\n\t0x3044, 0xA4A4, 0x3045, 0xA4A5, 0x3046, 0xA4A6, 0x3047, 0xA4A7,\n\t0x3048, 0xA4A8, 0x3049, 0xA4A9, 0x304A, 0xA4AA, 0x304B, 0xA4AB,\n\t0x304C, 0xA4AC, 0x304D, 0xA4AD, 0x304E, 0xA4AE, 0x304F, 0xA4AF,\n\t0x3050, 0xA4B0, 0x3051, 0xA4B1, 0x3052, 0xA4B2, 0x3053, 0xA4B3,\n\t0x3054, 0xA4B4, 0x3055, 0xA4B5, 0x3056, 0xA4B6, 0x3057, 0xA4B7,\n\t0x3058, 0xA4B8, 0x3059, 0xA4B9, 0x305A, 0xA4BA, 0x305B, 0xA4BB,\n\t0x305C, 0xA4BC, 0x305D, 0xA4BD, 0x305E, 0xA4BE, 0x305F, 0xA4BF,\n\t0x3060, 0xA4C0, 0x3061, 0xA4C1, 0x3062, 0xA4C2, 0x3063, 0xA4C3,\n\t0x3064, 0xA4C4, 0x3065, 0xA4C5, 0x3066, 0xA4C6, 0x3067, 0xA4C7,\n\t0x3068, 0xA4C8, 0x3069, 0xA4C9, 0x306A, 0xA4CA, 0x306B, 0xA4CB,\n\t0x306C, 0xA4CC, 0x306D, 0xA4CD, 0x306E, 0xA4CE, 0x306F, 0xA4CF,\n\t0x3070, 0xA4D0, 0x3071, 0xA4D1, 0x3072, 0xA4D2, 0x3073, 0xA4D3,\n\t0x3074, 0xA4D4, 0x3075, 0xA4D5, 0x3076, 0xA4D6, 0x3077, 0xA4D7,\n\t0x3078, 0xA4D8, 0x3079, 0xA4D9, 0x307A, 0xA4DA, 0x307B, 0xA4DB,\n\t0x307C, 0xA4DC, 0x307D, 0xA4DD, 0x307E, 0xA4DE, 0x307F, 0xA4DF,\n\t0x3080, 0xA4E0, 0x3081, 0xA4E1, 0x3082, 0xA4E2, 0x3083, 0xA4E3,\n\t0x3084, 0xA4E4, 0x3085, 0xA4E5, 0x3086, 0xA4E6, 0x3087, 0xA4E7,\n\t0x3088, 0xA4E8, 0x3089, 0xA4E9, 0x308A, 0xA4EA, 0x308B, 0xA4EB,\n\t0x308C, 0xA4EC, 0x308D, 0xA4ED, 0x308E, 0xA4EE, 0x308F, 0xA4EF,\n\t0x3090, 0xA4F0, 0x3091, 0xA4F1, 0x3092, 0xA4F2, 0x3093, 0xA4F3,\n\t0x309B, 0xA961, 0x309C, 0xA962, 0x309D, 0xA966, 0x309E, 0xA967,\n\t0x30A1, 0xA5A1, 0x30A2, 0xA5A2, 0x30A3, 0xA5A3, 0x30A4, 0xA5A4,\n\t0x30A5, 0xA5A5, 0x30A6, 0xA5A6, 0x30A7, 0xA5A7, 0x30A8, 0xA5A8,\n\t0x30A9, 0xA5A9, 0x30AA, 0xA5AA, 0x30AB, 0xA5AB, 0x30AC, 0xA5AC,\n\t0x30AD, 0xA5AD, 0x30AE, 0xA5AE, 0x30AF, 0xA5AF, 0x30B0, 0xA5B0,\n\t0x30B1, 0xA5B1, 0x30B2, 0xA5B2, 0x30B3, 0xA5B3, 0x30B4, 0xA5B4,\n\t0x30B5, 0xA5B5, 0x30B6, 0xA5B6, 0x30B7, 0xA5B7, 0x30B8, 0xA5B8,\n\t0x30B9, 0xA5B9, 0x30BA, 0xA5BA, 0x30BB, 0xA5BB, 0x30BC, 0xA5BC,\n\t0x30BD, 0xA5BD, 0x30BE, 0xA5BE, 0x30BF, 0xA5BF, 0x30C0, 0xA5C0,\n\t0x30C1, 0xA5C1, 0x30C2, 0xA5C2, 0x30C3, 0xA5C3, 0x30C4, 0xA5C4,\n\t0x30C5, 0xA5C5, 0x30C6, 0xA5C6, 0x30C7, 0xA5C7, 0x30C8, 0xA5C8,\n\t0x30C9, 0xA5C9, 0x30CA, 0xA5CA, 0x30CB, 0xA5CB, 0x30CC, 0xA5CC,\n\t0x30CD, 0xA5CD, 0x30CE, 0xA5CE, 0x30CF, 0xA5CF, 0x30D0, 0xA5D0,\n\t0x30D1, 0xA5D1, 0x30D2, 0xA5D2, 0x30D3, 0xA5D3, 0x30D4, 0xA5D4,\n\t0x30D5, 0xA5D5, 0x30D6, 0xA5D6, 0x30D7, 0xA5D7, 0x30D8, 0xA5D8,\n\t0x30D9, 0xA5D9, 0x30DA, 0xA5DA, 0x30DB, 0xA5DB, 0x30DC, 0xA5DC,\n\t0x30DD, 0xA5DD, 0x30DE, 0xA5DE, 0x30DF, 0xA5DF, 0x30E0, 0xA5E0,\n\t0x30E1, 0xA5E1, 0x30E2, 0xA5E2, 0x30E3, 0xA5E3, 0x30E4, 0xA5E4,\n\t0x30E5, 0xA5E5, 0x30E6, 0xA5E6, 0x30E7, 0xA5E7, 0x30E8, 0xA5E8,\n\t0x30E9, 0xA5E9, 0x30EA, 0xA5EA, 0x30EB, 0xA5EB, 0x30EC, 0xA5EC,\n\t0x30ED, 0xA5ED, 0x30EE, 0xA5EE, 0x30EF, 0xA5EF, 0x30F0, 0xA5F0,\n\t0x30F1, 0xA5F1, 0x30F2, 0xA5F2, 0x30F3, 0xA5F3, 0x30F4, 0xA5F4,\n\t0x30F5, 0xA5F5, 0x30F6, 0xA5F6, 0x30FC, 0xA960, 0x30FD, 0xA963,\n\t0x30FE, 0xA964, 0x3105, 0xA8C5, 0x3106, 0xA8C6, 0x3107, 0xA8C7,\n\t0x3108, 0xA8C8, 0x3109, 0xA8C9, 0x310A, 0xA8CA, 0x310B, 0xA8CB,\n\t0x310C, 0xA8CC, 0x310D, 0xA8CD, 0x310E, 0xA8CE, 0x310F, 0xA8CF,\n\t0x3110, 0xA8D0, 0x3111, 0xA8D1, 0x3112, 0xA8D2, 0x3113, 0xA8D3,\n\t0x3114, 0xA8D4, 0x3115, 0xA8D5, 0x3116, 0xA8D6, 0x3117, 0xA8D7,\n\t0x3118, 0xA8D8, 0x3119, 0xA8D9, 0x311A, 0xA8DA, 0x311B, 0xA8DB,\n\t0x311C, 0xA8DC, 0x311D, 0xA8DD, 0x311E, 0xA8DE, 0x311F, 0xA8DF,\n\t0x3120, 0xA8E0, 0x3121, 0xA8E1, 0x3122, 0xA8E2, 0x3123, 0xA8E3,\n\t0x3124, 0xA8E4, 0x3125, 0xA8E5, 0x3126, 0xA8E6, 0x3127, 0xA8E7,\n\t0x3128, 0xA8E8, 0x3129, 0xA8E9, 0x3220, 0xA2E5, 0x3221, 0xA2E6,\n\t0x3222, 0xA2E7, 0x3223, 0xA2E8, 0x3224, 0xA2E9, 0x3225, 0xA2EA,\n\t0x3226, 0xA2EB, 0x3227, 0xA2EC, 0x3228, 0xA2ED, 0x3229, 0xA2EE,\n\t0x3231, 0xA95A, 0x32A3, 0xA949, 0x338E, 0xA94A, 0x338F, 0xA94B,\n\t0x339C, 0xA94C, 0x339D, 0xA94D, 0x339E, 0xA94E, 0x33A1, 0xA94F,\n\t0x33C4, 0xA950, 0x33CE, 0xA951, 0x33D1, 0xA952, 0x33D2, 0xA953,\n\t0x33D5, 0xA954, 0x4E00, 0xD2BB, 0x4E01, 0xB6A1, 0x4E02, 0x8140,\n\t0x4E03, 0xC6DF, 0x4E04, 0x8141, 0x4E05, 0x8142, 0x4E06, 0x8143,\n\t0x4E07, 0xCDF2, 0x4E08, 0xD5C9, 0x4E09, 0xC8FD, 0x4E0A, 0xC9CF,\n\t0x4E0B, 0xCFC2, 0x4E0C, 0xD8A2, 0x4E0D, 0xB2BB, 0x4E0E, 0xD3EB,\n\t0x4E0F, 0x8144, 0x4E10, 0xD8A4, 0x4E11, 0xB3F3, 0x4E12, 0x8145,\n\t0x4E13, 0xD7A8, 0x4E14, 0xC7D2, 0x4E15, 0xD8A7, 0x4E16, 0xCAC0,\n\t0x4E17, 0x8146, 0x4E18, 0xC7F0, 0x4E19, 0xB1FB, 0x4E1A, 0xD2B5,\n\t0x4E1B, 0xB4D4, 0x4E1C, 0xB6AB, 0x4E1D, 0xCBBF, 0x4E1E, 0xD8A9,\n\t0x4E1F, 0x8147, 0x4E20, 0x8148, 0x4E21, 0x8149, 0x4E22, 0xB6AA,\n\t0x4E23, 0x814A, 0x4E24, 0xC1BD, 0x4E25, 0xD1CF, 0x4E26, 0x814B,\n\t0x4E27, 0xC9A5, 0x4E28, 0xD8AD, 0x4E29, 0x814C, 0x4E2A, 0xB8F6,\n\t0x4E2B, 0xD1BE, 0x4E2C, 0xE3DC, 0x4E2D, 0xD6D0, 0x4E2E, 0x814D,\n\t0x4E2F, 0x814E, 0x4E30, 0xB7E1, 0x4E31, 0x814F, 0x4E32, 0xB4AE,\n\t0x4E33, 0x8150, 0x4E34, 0xC1D9, 0x4E35, 0x8151, 0x4E36, 0xD8BC,\n\t0x4E37, 0x8152, 0x4E38, 0xCDE8, 0x4E39, 0xB5A4, 0x4E3A, 0xCEAA,\n\t0x4E3B, 0xD6F7, 0x4E3C, 0x8153, 0x4E3D, 0xC0F6, 0x4E3E, 0xBED9,\n\t0x4E3F, 0xD8AF, 0x4E40, 0x8154, 0x4E41, 0x8155, 0x4E42, 0x8156,\n\t0x4E43, 0xC4CB, 0x4E44, 0x8157, 0x4E45, 0xBEC3, 0x4E46, 0x8158,\n\t0x4E47, 0xD8B1, 0x4E48, 0xC3B4, 0x4E49, 0xD2E5, 0x4E4A, 0x8159,\n\t0x4E4B, 0xD6AE, 0x4E4C, 0xCEDA, 0x4E4D, 0xD5A7, 0x4E4E, 0xBAF5,\n\t0x4E4F, 0xB7A6, 0x4E50, 0xC0D6, 0x4E51, 0x815A, 0x4E52, 0xC6B9,\n\t0x4E53, 0xC5D2, 0x4E54, 0xC7C7, 0x4E55, 0x815B, 0x4E56, 0xB9D4,\n\t0x4E57, 0x815C, 0x4E58, 0xB3CB, 0x4E59, 0xD2D2, 0x4E5A, 0x815D,\n\t0x4E5B, 0x815E, 0x4E5C, 0xD8BF, 0x4E5D, 0xBEC5, 0x4E5E, 0xC6F2,\n\t0x4E5F, 0xD2B2, 0x4E60, 0xCFB0, 0x4E61, 0xCFE7, 0x4E62, 0x815F,\n\t0x4E63, 0x8160, 0x4E64, 0x8161, 0x4E65, 0x8162, 0x4E66, 0xCAE9,\n\t0x4E67, 0x8163, 0x4E68, 0x8164, 0x4E69, 0xD8C0, 0x4E6A, 0x8165,\n\t0x4E6B, 0x8166, 0x4E6C, 0x8167, 0x4E6D, 0x8168, 0x4E6E, 0x8169,\n\t0x4E6F, 0x816A, 0x4E70, 0xC2F2, 0x4E71, 0xC2D2, 0x4E72, 0x816B,\n\t0x4E73, 0xC8E9, 0x4E74, 0x816C, 0x4E75, 0x816D, 0x4E76, 0x816E,\n\t0x4E77, 0x816F, 0x4E78, 0x8170, 0x4E79, 0x8171, 0x4E7A, 0x8172,\n\t0x4E7B, 0x8173, 0x4E7C, 0x8174, 0x4E7D, 0x8175, 0x4E7E, 0xC7AC,\n\t0x4E7F, 0x8176, 0x4E80, 0x8177, 0x4E81, 0x8178, 0x4E82, 0x8179,\n\t0x4E83, 0x817A, 0x4E84, 0x817B, 0x4E85, 0x817C, 0x4E86, 0xC1CB,\n\t0x4E87, 0x817D, 0x4E88, 0xD3E8, 0x4E89, 0xD5F9, 0x4E8A, 0x817E,\n\t0x4E8B, 0xCAC2, 0x4E8C, 0xB6FE, 0x4E8D, 0xD8A1, 0x4E8E, 0xD3DA,\n\t0x4E8F, 0xBFF7, 0x4E90, 0x8180, 0x4E91, 0xD4C6, 0x4E92, 0xBBA5,\n\t0x4E93, 0xD8C1, 0x4E94, 0xCEE5, 0x4E95, 0xBEAE, 0x4E96, 0x8181,\n\t0x4E97, 0x8182, 0x4E98, 0xD8A8, 0x4E99, 0x8183, 0x4E9A, 0xD1C7,\n\t0x4E9B, 0xD0A9, 0x4E9C, 0x8184, 0x4E9D, 0x8185, 0x4E9E, 0x8186,\n\t0x4E9F, 0xD8BD, 0x4EA0, 0xD9EF, 0x4EA1, 0xCDF6, 0x4EA2, 0xBFBA,\n\t0x4EA3, 0x8187, 0x4EA4, 0xBDBB, 0x4EA5, 0xBAA5, 0x4EA6, 0xD2E0,\n\t0x4EA7, 0xB2FA, 0x4EA8, 0xBAE0, 0x4EA9, 0xC4B6, 0x4EAA, 0x8188,\n\t0x4EAB, 0xCFED, 0x4EAC, 0xBEA9, 0x4EAD, 0xCDA4, 0x4EAE, 0xC1C1,\n\t0x4EAF, 0x8189, 0x4EB0, 0x818A, 0x4EB1, 0x818B, 0x4EB2, 0xC7D7,\n\t0x4EB3, 0xD9F1, 0x4EB4, 0x818C, 0x4EB5, 0xD9F4, 0x4EB6, 0x818D,\n\t0x4EB7, 0x818E, 0x4EB8, 0x818F, 0x4EB9, 0x8190, 0x4EBA, 0xC8CB,\n\t0x4EBB, 0xD8E9, 0x4EBC, 0x8191, 0x4EBD, 0x8192, 0x4EBE, 0x8193,\n\t0x4EBF, 0xD2DA, 0x4EC0, 0xCAB2, 0x4EC1, 0xC8CA, 0x4EC2, 0xD8EC,\n\t0x4EC3, 0xD8EA, 0x4EC4, 0xD8C6, 0x4EC5, 0xBDF6, 0x4EC6, 0xC6CD,\n\t0x4EC7, 0xB3F0, 0x4EC8, 0x8194, 0x4EC9, 0xD8EB, 0x4ECA, 0xBDF1,\n\t0x4ECB, 0xBDE9, 0x4ECC, 0x8195, 0x4ECD, 0xC8D4, 0x4ECE, 0xB4D3,\n\t0x4ECF, 0x8196, 0x4ED0, 0x8197, 0x4ED1, 0xC2D8, 0x4ED2, 0x8198,\n\t0x4ED3, 0xB2D6, 0x4ED4, 0xD7D0, 0x4ED5, 0xCACB, 0x4ED6, 0xCBFB,\n\t0x4ED7, 0xD5CC, 0x4ED8, 0xB8B6, 0x4ED9, 0xCFC9, 0x4EDA, 0x8199,\n\t0x4EDB, 0x819A, 0x4EDC, 0x819B, 0x4EDD, 0xD9DA, 0x4EDE, 0xD8F0,\n\t0x4EDF, 0xC7AA, 0x4EE0, 0x819C, 0x4EE1, 0xD8EE, 0x4EE2, 0x819D,\n\t0x4EE3, 0xB4FA, 0x4EE4, 0xC1EE, 0x4EE5, 0xD2D4, 0x4EE6, 0x819E,\n\t0x4EE7, 0x819F, 0x4EE8, 0xD8ED, 0x4EE9, 0x81A0, 0x4EEA, 0xD2C7,\n\t0x4EEB, 0xD8EF, 0x4EEC, 0xC3C7, 0x4EED, 0x81A1, 0x4EEE, 0x81A2,\n\t0x4EEF, 0x81A3, 0x4EF0, 0xD1F6, 0x4EF1, 0x81A4, 0x4EF2, 0xD6D9,\n\t0x4EF3, 0xD8F2, 0x4EF4, 0x81A5, 0x4EF5, 0xD8F5, 0x4EF6, 0xBCFE,\n\t0x4EF7, 0xBCDB, 0x4EF8, 0x81A6, 0x4EF9, 0x81A7, 0x4EFA, 0x81A8,\n\t0x4EFB, 0xC8CE, 0x4EFC, 0x81A9, 0x4EFD, 0xB7DD, 0x4EFE, 0x81AA,\n\t0x4EFF, 0xB7C2, 0x4F00, 0x81AB, 0x4F01, 0xC6F3, 0x4F02, 0x81AC,\n\t0x4F03, 0x81AD, 0x4F04, 0x81AE, 0x4F05, 0x81AF, 0x4F06, 0x81B0,\n\t0x4F07, 0x81B1, 0x4F08, 0x81B2, 0x4F09, 0xD8F8, 0x4F0A, 0xD2C1,\n\t0x4F0B, 0x81B3, 0x4F0C, 0x81B4, 0x4F0D, 0xCEE9, 0x4F0E, 0xBCBF,\n\t0x4F0F, 0xB7FC, 0x4F10, 0xB7A5, 0x4F11, 0xD0DD, 0x4F12, 0x81B5,\n\t0x4F13, 0x81B6, 0x4F14, 0x81B7, 0x4F15, 0x81B8, 0x4F16, 0x81B9,\n\t0x4F17, 0xD6DA, 0x4F18, 0xD3C5, 0x4F19, 0xBBEF, 0x4F1A, 0xBBE1,\n\t0x4F1B, 0xD8F1, 0x4F1C, 0x81BA, 0x4F1D, 0x81BB, 0x4F1E, 0xC9A1,\n\t0x4F1F, 0xCEB0, 0x4F20, 0xB4AB, 0x4F21, 0x81BC, 0x4F22, 0xD8F3,\n\t0x4F23, 0x81BD, 0x4F24, 0xC9CB, 0x4F25, 0xD8F6, 0x4F26, 0xC2D7,\n\t0x4F27, 0xD8F7, 0x4F28, 0x81BE, 0x4F29, 0x81BF, 0x4F2A, 0xCEB1,\n\t0x4F2B, 0xD8F9, 0x4F2C, 0x81C0, 0x4F2D, 0x81C1, 0x4F2E, 0x81C2,\n\t0x4F2F, 0xB2AE, 0x4F30, 0xB9C0, 0x4F31, 0x81C3, 0x4F32, 0xD9A3,\n\t0x4F33, 0x81C4, 0x4F34, 0xB0E9, 0x4F35, 0x81C5, 0x4F36, 0xC1E6,\n\t0x4F37, 0x81C6, 0x4F38, 0xC9EC, 0x4F39, 0x81C7, 0x4F3A, 0xCBC5,\n\t0x4F3B, 0x81C8, 0x4F3C, 0xCBC6, 0x4F3D, 0xD9A4, 0x4F3E, 0x81C9,\n\t0x4F3F, 0x81CA, 0x4F40, 0x81CB, 0x4F41, 0x81CC, 0x4F42, 0x81CD,\n\t0x4F43, 0xB5E8, 0x4F44, 0x81CE, 0x4F45, 0x81CF, 0x4F46, 0xB5AB,\n\t0x4F47, 0x81D0, 0x4F48, 0x81D1, 0x4F49, 0x81D2, 0x4F4A, 0x81D3,\n\t0x4F4B, 0x81D4, 0x4F4C, 0x81D5, 0x4F4D, 0xCEBB, 0x4F4E, 0xB5CD,\n\t0x4F4F, 0xD7A1, 0x4F50, 0xD7F4, 0x4F51, 0xD3D3, 0x4F52, 0x81D6,\n\t0x4F53, 0xCCE5, 0x4F54, 0x81D7, 0x4F55, 0xBACE, 0x4F56, 0x81D8,\n\t0x4F57, 0xD9A2, 0x4F58, 0xD9DC, 0x4F59, 0xD3E0, 0x4F5A, 0xD8FD,\n\t0x4F5B, 0xB7F0, 0x4F5C, 0xD7F7, 0x4F5D, 0xD8FE, 0x4F5E, 0xD8FA,\n\t0x4F5F, 0xD9A1, 0x4F60, 0xC4E3, 0x4F61, 0x81D9, 0x4F62, 0x81DA,\n\t0x4F63, 0xD3B6, 0x4F64, 0xD8F4, 0x4F65, 0xD9DD, 0x4F66, 0x81DB,\n\t0x4F67, 0xD8FB, 0x4F68, 0x81DC, 0x4F69, 0xC5E5, 0x4F6A, 0x81DD,\n\t0x4F6B, 0x81DE, 0x4F6C, 0xC0D0, 0x4F6D, 0x81DF, 0x4F6E, 0x81E0,\n\t0x4F6F, 0xD1F0, 0x4F70, 0xB0DB, 0x4F71, 0x81E1, 0x4F72, 0x81E2,\n\t0x4F73, 0xBCD1, 0x4F74, 0xD9A6, 0x4F75, 0x81E3, 0x4F76, 0xD9A5,\n\t0x4F77, 0x81E4, 0x4F78, 0x81E5, 0x4F79, 0x81E6, 0x4F7A, 0x81E7,\n\t0x4F7B, 0xD9AC, 0x4F7C, 0xD9AE, 0x4F7D, 0x81E8, 0x4F7E, 0xD9AB,\n\t0x4F7F, 0xCAB9, 0x4F80, 0x81E9, 0x4F81, 0x81EA, 0x4F82, 0x81EB,\n\t0x4F83, 0xD9A9, 0x4F84, 0xD6B6, 0x4F85, 0x81EC, 0x4F86, 0x81ED,\n\t0x4F87, 0x81EE, 0x4F88, 0xB3DE, 0x4F89, 0xD9A8, 0x4F8A, 0x81EF,\n\t0x4F8B, 0xC0FD, 0x4F8C, 0x81F0, 0x4F8D, 0xCACC, 0x4F8E, 0x81F1,\n\t0x4F8F, 0xD9AA, 0x4F90, 0x81F2, 0x4F91, 0xD9A7, 0x4F92, 0x81F3,\n\t0x4F93, 0x81F4, 0x4F94, 0xD9B0, 0x4F95, 0x81F5, 0x4F96, 0x81F6,\n\t0x4F97, 0xB6B1, 0x4F98, 0x81F7, 0x4F99, 0x81F8, 0x4F9A, 0x81F9,\n\t0x4F9B, 0xB9A9, 0x4F9C, 0x81FA, 0x4F9D, 0xD2C0, 0x4F9E, 0x81FB,\n\t0x4F9F, 0x81FC, 0x4FA0, 0xCFC0, 0x4FA1, 0x81FD, 0x4FA2, 0x81FE,\n\t0x4FA3, 0xC2C2, 0x4FA4, 0x8240, 0x4FA5, 0xBDC4, 0x4FA6, 0xD5EC,\n\t0x4FA7, 0xB2E0, 0x4FA8, 0xC7C8, 0x4FA9, 0xBFEB, 0x4FAA, 0xD9AD,\n\t0x4FAB, 0x8241, 0x4FAC, 0xD9AF, 0x4FAD, 0x8242, 0x4FAE, 0xCEEA,\n\t0x4FAF, 0xBAEE, 0x4FB0, 0x8243, 0x4FB1, 0x8244, 0x4FB2, 0x8245,\n\t0x4FB3, 0x8246, 0x4FB4, 0x8247, 0x4FB5, 0xC7D6, 0x4FB6, 0x8248,\n\t0x4FB7, 0x8249, 0x4FB8, 0x824A, 0x4FB9, 0x824B, 0x4FBA, 0x824C,\n\t0x4FBB, 0x824D, 0x4FBC, 0x824E, 0x4FBD, 0x824F, 0x4FBE, 0x8250,\n\t0x4FBF, 0xB1E3, 0x4FC0, 0x8251, 0x4FC1, 0x8252, 0x4FC2, 0x8253,\n\t0x4FC3, 0xB4D9, 0x4FC4, 0xB6ED, 0x4FC5, 0xD9B4, 0x4FC6, 0x8254,\n\t0x4FC7, 0x8255, 0x4FC8, 0x8256, 0x4FC9, 0x8257, 0x4FCA, 0xBFA1,\n\t0x4FCB, 0x8258, 0x4FCC, 0x8259, 0x4FCD, 0x825A, 0x4FCE, 0xD9DE,\n\t0x4FCF, 0xC7CE, 0x4FD0, 0xC0FE, 0x4FD1, 0xD9B8, 0x4FD2, 0x825B,\n\t0x4FD3, 0x825C, 0x4FD4, 0x825D, 0x4FD5, 0x825E, 0x4FD6, 0x825F,\n\t0x4FD7, 0xCBD7, 0x4FD8, 0xB7FD, 0x4FD9, 0x8260, 0x4FDA, 0xD9B5,\n\t0x4FDB, 0x8261, 0x4FDC, 0xD9B7, 0x4FDD, 0xB1A3, 0x4FDE, 0xD3E1,\n\t0x4FDF, 0xD9B9, 0x4FE0, 0x8262, 0x4FE1, 0xD0C5, 0x4FE2, 0x8263,\n\t0x4FE3, 0xD9B6, 0x4FE4, 0x8264, 0x4FE5, 0x8265, 0x4FE6, 0xD9B1,\n\t0x4FE7, 0x8266, 0x4FE8, 0xD9B2, 0x4FE9, 0xC1A9, 0x4FEA, 0xD9B3,\n\t0x4FEB, 0x8267, 0x4FEC, 0x8268, 0x4FED, 0xBCF3, 0x4FEE, 0xD0DE,\n\t0x4FEF, 0xB8A9, 0x4FF0, 0x8269, 0x4FF1, 0xBEE3, 0x4FF2, 0x826A,\n\t0x4FF3, 0xD9BD, 0x4FF4, 0x826B, 0x4FF5, 0x826C, 0x4FF6, 0x826D,\n\t0x4FF7, 0x826E, 0x4FF8, 0xD9BA, 0x4FF9, 0x826F, 0x4FFA, 0xB0B3,\n\t0x4FFB, 0x8270, 0x4FFC, 0x8271, 0x4FFD, 0x8272, 0x4FFE, 0xD9C2,\n\t0x4FFF, 0x8273, 0x5000, 0x8274, 0x5001, 0x8275, 0x5002, 0x8276,\n\t0x5003, 0x8277, 0x5004, 0x8278, 0x5005, 0x8279, 0x5006, 0x827A,\n\t0x5007, 0x827B, 0x5008, 0x827C, 0x5009, 0x827D, 0x500A, 0x827E,\n\t0x500B, 0x8280, 0x500C, 0xD9C4, 0x500D, 0xB1B6, 0x500E, 0x8281,\n\t0x500F, 0xD9BF, 0x5010, 0x8282, 0x5011, 0x8283, 0x5012, 0xB5B9,\n\t0x5013, 0x8284, 0x5014, 0xBEF3, 0x5015, 0x8285, 0x5016, 0x8286,\n\t0x5017, 0x8287, 0x5018, 0xCCC8, 0x5019, 0xBAF2, 0x501A, 0xD2D0,\n\t0x501B, 0x8288, 0x501C, 0xD9C3, 0x501D, 0x8289, 0x501E, 0x828A,\n\t0x501F, 0xBDE8, 0x5020, 0x828B, 0x5021, 0xB3AB, 0x5022, 0x828C,\n\t0x5023, 0x828D, 0x5024, 0x828E, 0x5025, 0xD9C5, 0x5026, 0xBEEB,\n\t0x5027, 0x828F, 0x5028, 0xD9C6, 0x5029, 0xD9BB, 0x502A, 0xC4DF,\n\t0x502B, 0x8290, 0x502C, 0xD9BE, 0x502D, 0xD9C1, 0x502E, 0xD9C0,\n\t0x502F, 0x8291, 0x5030, 0x8292, 0x5031, 0x8293, 0x5032, 0x8294,\n\t0x5033, 0x8295, 0x5034, 0x8296, 0x5035, 0x8297, 0x5036, 0x8298,\n\t0x5037, 0x8299, 0x5038, 0x829A, 0x5039, 0x829B, 0x503A, 0xD5AE,\n\t0x503B, 0x829C, 0x503C, 0xD6B5, 0x503D, 0x829D, 0x503E, 0xC7E3,\n\t0x503F, 0x829E, 0x5040, 0x829F, 0x5041, 0x82A0, 0x5042, 0x82A1,\n\t0x5043, 0xD9C8, 0x5044, 0x82A2, 0x5045, 0x82A3, 0x5046, 0x82A4,\n\t0x5047, 0xBCD9, 0x5048, 0xD9CA, 0x5049, 0x82A5, 0x504A, 0x82A6,\n\t0x504B, 0x82A7, 0x504C, 0xD9BC, 0x504D, 0x82A8, 0x504E, 0xD9CB,\n\t0x504F, 0xC6AB, 0x5050, 0x82A9, 0x5051, 0x82AA, 0x5052, 0x82AB,\n\t0x5053, 0x82AC, 0x5054, 0x82AD, 0x5055, 0xD9C9, 0x5056, 0x82AE,\n\t0x5057, 0x82AF, 0x5058, 0x82B0, 0x5059, 0x82B1, 0x505A, 0xD7F6,\n\t0x505B, 0x82B2, 0x505C, 0xCDA3, 0x505D, 0x82B3, 0x505E, 0x82B4,\n\t0x505F, 0x82B5, 0x5060, 0x82B6, 0x5061, 0x82B7, 0x5062, 0x82B8,\n\t0x5063, 0x82B9, 0x5064, 0x82BA, 0x5065, 0xBDA1, 0x5066, 0x82BB,\n\t0x5067, 0x82BC, 0x5068, 0x82BD, 0x5069, 0x82BE, 0x506A, 0x82BF,\n\t0x506B, 0x82C0, 0x506C, 0xD9CC, 0x506D, 0x82C1, 0x506E, 0x82C2,\n\t0x506F, 0x82C3, 0x5070, 0x82C4, 0x5071, 0x82C5, 0x5072, 0x82C6,\n\t0x5073, 0x82C7, 0x5074, 0x82C8, 0x5075, 0x82C9, 0x5076, 0xC5BC,\n\t0x5077, 0xCDB5, 0x5078, 0x82CA, 0x5079, 0x82CB, 0x507A, 0x82CC,\n\t0x507B, 0xD9CD, 0x507C, 0x82CD, 0x507D, 0x82CE, 0x507E, 0xD9C7,\n\t0x507F, 0xB3A5, 0x5080, 0xBFFE, 0x5081, 0x82CF, 0x5082, 0x82D0,\n\t0x5083, 0x82D1, 0x5084, 0x82D2, 0x5085, 0xB8B5, 0x5086, 0x82D3,\n\t0x5087, 0x82D4, 0x5088, 0xC0FC, 0x5089, 0x82D5, 0x508A, 0x82D6,\n\t0x508B, 0x82D7, 0x508C, 0x82D8, 0x508D, 0xB0F8, 0x508E, 0x82D9,\n\t0x508F, 0x82DA, 0x5090, 0x82DB, 0x5091, 0x82DC, 0x5092, 0x82DD,\n\t0x5093, 0x82DE, 0x5094, 0x82DF, 0x5095, 0x82E0, 0x5096, 0x82E1,\n\t0x5097, 0x82E2, 0x5098, 0x82E3, 0x5099, 0x82E4, 0x509A, 0x82E5,\n\t0x509B, 0x82E6, 0x509C, 0x82E7, 0x509D, 0x82E8, 0x509E, 0x82E9,\n\t0x509F, 0x82EA, 0x50A0, 0x82EB, 0x50A1, 0x82EC, 0x50A2, 0x82ED,\n\t0x50A3, 0xB4F6, 0x50A4, 0x82EE, 0x50A5, 0xD9CE, 0x50A6, 0x82EF,\n\t0x50A7, 0xD9CF, 0x50A8, 0xB4A2, 0x50A9, 0xD9D0, 0x50AA, 0x82F0,\n\t0x50AB, 0x82F1, 0x50AC, 0xB4DF, 0x50AD, 0x82F2, 0x50AE, 0x82F3,\n\t0x50AF, 0x82F4, 0x50B0, 0x82F5, 0x50B1, 0x82F6, 0x50B2, 0xB0C1,\n\t0x50B3, 0x82F7, 0x50B4, 0x82F8, 0x50B5, 0x82F9, 0x50B6, 0x82FA,\n\t0x50B7, 0x82FB, 0x50B8, 0x82FC, 0x50B9, 0x82FD, 0x50BA, 0xD9D1,\n\t0x50BB, 0xC9B5, 0x50BC, 0x82FE, 0x50BD, 0x8340, 0x50BE, 0x8341,\n\t0x50BF, 0x8342, 0x50C0, 0x8343, 0x50C1, 0x8344, 0x50C2, 0x8345,\n\t0x50C3, 0x8346, 0x50C4, 0x8347, 0x50C5, 0x8348, 0x50C6, 0x8349,\n\t0x50C7, 0x834A, 0x50C8, 0x834B, 0x50C9, 0x834C, 0x50CA, 0x834D,\n\t0x50CB, 0x834E, 0x50CC, 0x834F, 0x50CD, 0x8350, 0x50CE, 0x8351,\n\t0x50CF, 0xCFF1, 0x50D0, 0x8352, 0x50D1, 0x8353, 0x50D2, 0x8354,\n\t0x50D3, 0x8355, 0x50D4, 0x8356, 0x50D5, 0x8357, 0x50D6, 0xD9D2,\n\t0x50D7, 0x8358, 0x50D8, 0x8359, 0x50D9, 0x835A, 0x50DA, 0xC1C5,\n\t0x50DB, 0x835B, 0x50DC, 0x835C, 0x50DD, 0x835D, 0x50DE, 0x835E,\n\t0x50DF, 0x835F, 0x50E0, 0x8360, 0x50E1, 0x8361, 0x50E2, 0x8362,\n\t0x50E3, 0x8363, 0x50E4, 0x8364, 0x50E5, 0x8365, 0x50E6, 0xD9D6,\n\t0x50E7, 0xC9AE, 0x50E8, 0x8366, 0x50E9, 0x8367, 0x50EA, 0x8368,\n\t0x50EB, 0x8369, 0x50EC, 0xD9D5, 0x50ED, 0xD9D4, 0x50EE, 0xD9D7,\n\t0x50EF, 0x836A, 0x50F0, 0x836B, 0x50F1, 0x836C, 0x50F2, 0x836D,\n\t0x50F3, 0xCBDB, 0x50F4, 0x836E, 0x50F5, 0xBDA9, 0x50F6, 0x836F,\n\t0x50F7, 0x8370, 0x50F8, 0x8371, 0x50F9, 0x8372, 0x50FA, 0x8373,\n\t0x50FB, 0xC6A7, 0x50FC, 0x8374, 0x50FD, 0x8375, 0x50FE, 0x8376,\n\t0x50FF, 0x8377, 0x5100, 0x8378, 0x5101, 0x8379, 0x5102, 0x837A,\n\t0x5103, 0x837B, 0x5104, 0x837C, 0x5105, 0x837D, 0x5106, 0xD9D3,\n\t0x5107, 0xD9D8, 0x5108, 0x837E, 0x5109, 0x8380, 0x510A, 0x8381,\n\t0x510B, 0xD9D9, 0x510C, 0x8382, 0x510D, 0x8383, 0x510E, 0x8384,\n\t0x510F, 0x8385, 0x5110, 0x8386, 0x5111, 0x8387, 0x5112, 0xC8E5,\n\t0x5113, 0x8388, 0x5114, 0x8389, 0x5115, 0x838A, 0x5116, 0x838B,\n\t0x5117, 0x838C, 0x5118, 0x838D, 0x5119, 0x838E, 0x511A, 0x838F,\n\t0x511B, 0x8390, 0x511C, 0x8391, 0x511D, 0x8392, 0x511E, 0x8393,\n\t0x511F, 0x8394, 0x5120, 0x8395, 0x5121, 0xC0DC, 0x5122, 0x8396,\n\t0x5123, 0x8397, 0x5124, 0x8398, 0x5125, 0x8399, 0x5126, 0x839A,\n\t0x5127, 0x839B, 0x5128, 0x839C, 0x5129, 0x839D, 0x512A, 0x839E,\n\t0x512B, 0x839F, 0x512C, 0x83A0, 0x512D, 0x83A1, 0x512E, 0x83A2,\n\t0x512F, 0x83A3, 0x5130, 0x83A4, 0x5131, 0x83A5, 0x5132, 0x83A6,\n\t0x5133, 0x83A7, 0x5134, 0x83A8, 0x5135, 0x83A9, 0x5136, 0x83AA,\n\t0x5137, 0x83AB, 0x5138, 0x83AC, 0x5139, 0x83AD, 0x513A, 0x83AE,\n\t0x513B, 0x83AF, 0x513C, 0x83B0, 0x513D, 0x83B1, 0x513E, 0x83B2,\n\t0x513F, 0xB6F9, 0x5140, 0xD8A3, 0x5141, 0xD4CA, 0x5142, 0x83B3,\n\t0x5143, 0xD4AA, 0x5144, 0xD0D6, 0x5145, 0xB3E4, 0x5146, 0xD5D7,\n\t0x5147, 0x83B4, 0x5148, 0xCFC8, 0x5149, 0xB9E2, 0x514A, 0x83B5,\n\t0x514B, 0xBFCB, 0x514C, 0x83B6, 0x514D, 0xC3E2, 0x514E, 0x83B7,\n\t0x514F, 0x83B8, 0x5150, 0x83B9, 0x5151, 0xB6D2, 0x5152, 0x83BA,\n\t0x5153, 0x83BB, 0x5154, 0xCDC3, 0x5155, 0xD9EE, 0x5156, 0xD9F0,\n\t0x5157, 0x83BC, 0x5158, 0x83BD, 0x5159, 0x83BE, 0x515A, 0xB5B3,\n\t0x515B, 0x83BF, 0x515C, 0xB6B5, 0x515D, 0x83C0, 0x515E, 0x83C1,\n\t0x515F, 0x83C2, 0x5160, 0x83C3, 0x5161, 0x83C4, 0x5162, 0xBEA4,\n\t0x5163, 0x83C5, 0x5164, 0x83C6, 0x5165, 0xC8EB, 0x5166, 0x83C7,\n\t0x5167, 0x83C8, 0x5168, 0xC8AB, 0x5169, 0x83C9, 0x516A, 0x83CA,\n\t0x516B, 0xB0CB, 0x516C, 0xB9AB, 0x516D, 0xC1F9, 0x516E, 0xD9E2,\n\t0x516F, 0x83CB, 0x5170, 0xC0BC, 0x5171, 0xB9B2, 0x5172, 0x83CC,\n\t0x5173, 0xB9D8, 0x5174, 0xD0CB, 0x5175, 0xB1F8, 0x5176, 0xC6E4,\n\t0x5177, 0xBEDF, 0x5178, 0xB5E4, 0x5179, 0xD7C8, 0x517A, 0x83CD,\n\t0x517B, 0xD1F8, 0x517C, 0xBCE6, 0x517D, 0xCADE, 0x517E, 0x83CE,\n\t0x517F, 0x83CF, 0x5180, 0xBCBD, 0x5181, 0xD9E6, 0x5182, 0xD8E7,\n\t0x5183, 0x83D0, 0x5184, 0x83D1, 0x5185, 0xC4DA, 0x5186, 0x83D2,\n\t0x5187, 0x83D3, 0x5188, 0xB8D4, 0x5189, 0xC8BD, 0x518A, 0x83D4,\n\t0x518B, 0x83D5, 0x518C, 0xB2E1, 0x518D, 0xD4D9, 0x518E, 0x83D6,\n\t0x518F, 0x83D7, 0x5190, 0x83D8, 0x5191, 0x83D9, 0x5192, 0xC3B0,\n\t0x5193, 0x83DA, 0x5194, 0x83DB, 0x5195, 0xC3E1, 0x5196, 0xDAA2,\n\t0x5197, 0xC8DF, 0x5198, 0x83DC, 0x5199, 0xD0B4, 0x519A, 0x83DD,\n\t0x519B, 0xBEFC, 0x519C, 0xC5A9, 0x519D, 0x83DE, 0x519E, 0x83DF,\n\t0x519F, 0x83E0, 0x51A0, 0xB9DA, 0x51A1, 0x83E1, 0x51A2, 0xDAA3,\n\t0x51A3, 0x83E2, 0x51A4, 0xD4A9, 0x51A5, 0xDAA4, 0x51A6, 0x83E3,\n\t0x51A7, 0x83E4, 0x51A8, 0x83E5, 0x51A9, 0x83E6, 0x51AA, 0x83E7,\n\t0x51AB, 0xD9FB, 0x51AC, 0xB6AC, 0x51AD, 0x83E8, 0x51AE, 0x83E9,\n\t0x51AF, 0xB7EB, 0x51B0, 0xB1F9, 0x51B1, 0xD9FC, 0x51B2, 0xB3E5,\n\t0x51B3, 0xBEF6, 0x51B4, 0x83EA, 0x51B5, 0xBFF6, 0x51B6, 0xD2B1,\n\t0x51B7, 0xC0E4, 0x51B8, 0x83EB, 0x51B9, 0x83EC, 0x51BA, 0x83ED,\n\t0x51BB, 0xB6B3, 0x51BC, 0xD9FE, 0x51BD, 0xD9FD, 0x51BE, 0x83EE,\n\t0x51BF, 0x83EF, 0x51C0, 0xBEBB, 0x51C1, 0x83F0, 0x51C2, 0x83F1,\n\t0x51C3, 0x83F2, 0x51C4, 0xC6E0, 0x51C5, 0x83F3, 0x51C6, 0xD7BC,\n\t0x51C7, 0xDAA1, 0x51C8, 0x83F4, 0x51C9, 0xC1B9, 0x51CA, 0x83F5,\n\t0x51CB, 0xB5F2, 0x51CC, 0xC1E8, 0x51CD, 0x83F6, 0x51CE, 0x83F7,\n\t0x51CF, 0xBCF5, 0x51D0, 0x83F8, 0x51D1, 0xB4D5, 0x51D2, 0x83F9,\n\t0x51D3, 0x83FA, 0x51D4, 0x83FB, 0x51D5, 0x83FC, 0x51D6, 0x83FD,\n\t0x51D7, 0x83FE, 0x51D8, 0x8440, 0x51D9, 0x8441, 0x51DA, 0x8442,\n\t0x51DB, 0xC1DD, 0x51DC, 0x8443, 0x51DD, 0xC4FD, 0x51DE, 0x8444,\n\t0x51DF, 0x8445, 0x51E0, 0xBCB8, 0x51E1, 0xB7B2, 0x51E2, 0x8446,\n\t0x51E3, 0x8447, 0x51E4, 0xB7EF, 0x51E5, 0x8448, 0x51E6, 0x8449,\n\t0x51E7, 0x844A, 0x51E8, 0x844B, 0x51E9, 0x844C, 0x51EA, 0x844D,\n\t0x51EB, 0xD9EC, 0x51EC, 0x844E, 0x51ED, 0xC6BE, 0x51EE, 0x844F,\n\t0x51EF, 0xBFAD, 0x51F0, 0xBBCB, 0x51F1, 0x8450, 0x51F2, 0x8451,\n\t0x51F3, 0xB5CA, 0x51F4, 0x8452, 0x51F5, 0xDBC9, 0x51F6, 0xD0D7,\n\t0x51F7, 0x8453, 0x51F8, 0xCDB9, 0x51F9, 0xB0BC, 0x51FA, 0xB3F6,\n\t0x51FB, 0xBBF7, 0x51FC, 0xDBCA, 0x51FD, 0xBAAF, 0x51FE, 0x8454,\n\t0x51FF, 0xD4E4, 0x5200, 0xB5B6, 0x5201, 0xB5F3, 0x5202, 0xD8D6,\n\t0x5203, 0xC8D0, 0x5204, 0x8455, 0x5205, 0x8456, 0x5206, 0xB7D6,\n\t0x5207, 0xC7D0, 0x5208, 0xD8D7, 0x5209, 0x8457, 0x520A, 0xBFAF,\n\t0x520B, 0x8458, 0x520C, 0x8459, 0x520D, 0xDBBB, 0x520E, 0xD8D8,\n\t0x520F, 0x845A, 0x5210, 0x845B, 0x5211, 0xD0CC, 0x5212, 0xBBAE,\n\t0x5213, 0x845C, 0x5214, 0x845D, 0x5215, 0x845E, 0x5216, 0xEBBE,\n\t0x5217, 0xC1D0, 0x5218, 0xC1F5, 0x5219, 0xD4F2, 0x521A, 0xB8D5,\n\t0x521B, 0xB4B4, 0x521C, 0x845F, 0x521D, 0xB3F5, 0x521E, 0x8460,\n\t0x521F, 0x8461, 0x5220, 0xC9BE, 0x5221, 0x8462, 0x5222, 0x8463,\n\t0x5223, 0x8464, 0x5224, 0xC5D0, 0x5225, 0x8465, 0x5226, 0x8466,\n\t0x5227, 0x8467, 0x5228, 0xC5D9, 0x5229, 0xC0FB, 0x522A, 0x8468,\n\t0x522B, 0xB1F0, 0x522C, 0x8469, 0x522D, 0xD8D9, 0x522E, 0xB9CE,\n\t0x522F, 0x846A, 0x5230, 0xB5BD, 0x5231, 0x846B, 0x5232, 0x846C,\n\t0x5233, 0xD8DA, 0x5234, 0x846D, 0x5235, 0x846E, 0x5236, 0xD6C6,\n\t0x5237, 0xCBA2, 0x5238, 0xC8AF, 0x5239, 0xC9B2, 0x523A, 0xB4CC,\n\t0x523B, 0xBFCC, 0x523C, 0x846F, 0x523D, 0xB9F4, 0x523E, 0x8470,\n\t0x523F, 0xD8DB, 0x5240, 0xD8DC, 0x5241, 0xB6E7, 0x5242, 0xBCC1,\n\t0x5243, 0xCCEA, 0x5244, 0x8471, 0x5245, 0x8472, 0x5246, 0x8473,\n\t0x5247, 0x8474, 0x5248, 0x8475, 0x5249, 0x8476, 0x524A, 0xCFF7,\n\t0x524B, 0x8477, 0x524C, 0xD8DD, 0x524D, 0xC7B0, 0x524E, 0x8478,\n\t0x524F, 0x8479, 0x5250, 0xB9D0, 0x5251, 0xBDA3, 0x5252, 0x847A,\n\t0x5253, 0x847B, 0x5254, 0xCCDE, 0x5255, 0x847C, 0x5256, 0xC6CA,\n\t0x5257, 0x847D, 0x5258, 0x847E, 0x5259, 0x8480, 0x525A, 0x8481,\n\t0x525B, 0x8482, 0x525C, 0xD8E0, 0x525D, 0x8483, 0x525E, 0xD8DE,\n\t0x525F, 0x8484, 0x5260, 0x8485, 0x5261, 0xD8DF, 0x5262, 0x8486,\n\t0x5263, 0x8487, 0x5264, 0x8488, 0x5265, 0xB0FE, 0x5266, 0x8489,\n\t0x5267, 0xBEE7, 0x5268, 0x848A, 0x5269, 0xCAA3, 0x526A, 0xBCF4,\n\t0x526B, 0x848B, 0x526C, 0x848C, 0x526D, 0x848D, 0x526E, 0x848E,\n\t0x526F, 0xB8B1, 0x5270, 0x848F, 0x5271, 0x8490, 0x5272, 0xB8EE,\n\t0x5273, 0x8491, 0x5274, 0x8492, 0x5275, 0x8493, 0x5276, 0x8494,\n\t0x5277, 0x8495, 0x5278, 0x8496, 0x5279, 0x8497, 0x527A, 0x8498,\n\t0x527B, 0x8499, 0x527C, 0x849A, 0x527D, 0xD8E2, 0x527E, 0x849B,\n\t0x527F, 0xBDCB, 0x5280, 0x849C, 0x5281, 0xD8E4, 0x5282, 0xD8E3,\n\t0x5283, 0x849D, 0x5284, 0x849E, 0x5285, 0x849F, 0x5286, 0x84A0,\n\t0x5287, 0x84A1, 0x5288, 0xC5FC, 0x5289, 0x84A2, 0x528A, 0x84A3,\n\t0x528B, 0x84A4, 0x528C, 0x84A5, 0x528D, 0x84A6, 0x528E, 0x84A7,\n\t0x528F, 0x84A8, 0x5290, 0xD8E5, 0x5291, 0x84A9, 0x5292, 0x84AA,\n\t0x5293, 0xD8E6, 0x5294, 0x84AB, 0x5295, 0x84AC, 0x5296, 0x84AD,\n\t0x5297, 0x84AE, 0x5298, 0x84AF, 0x5299, 0x84B0, 0x529A, 0x84B1,\n\t0x529B, 0xC1A6, 0x529C, 0x84B2, 0x529D, 0xC8B0, 0x529E, 0xB0EC,\n\t0x529F, 0xB9A6, 0x52A0, 0xBCD3, 0x52A1, 0xCEF1, 0x52A2, 0xDBBD,\n\t0x52A3, 0xC1D3, 0x52A4, 0x84B3, 0x52A5, 0x84B4, 0x52A6, 0x84B5,\n\t0x52A7, 0x84B6, 0x52A8, 0xB6AF, 0x52A9, 0xD6FA, 0x52AA, 0xC5AC,\n\t0x52AB, 0xBDD9, 0x52AC, 0xDBBE, 0x52AD, 0xDBBF, 0x52AE, 0x84B7,\n\t0x52AF, 0x84B8, 0x52B0, 0x84B9, 0x52B1, 0xC0F8, 0x52B2, 0xBEA2,\n\t0x52B3, 0xC0CD, 0x52B4, 0x84BA, 0x52B5, 0x84BB, 0x52B6, 0x84BC,\n\t0x52B7, 0x84BD, 0x52B8, 0x84BE, 0x52B9, 0x84BF, 0x52BA, 0x84C0,\n\t0x52BB, 0x84C1, 0x52BC, 0x84C2, 0x52BD, 0x84C3, 0x52BE, 0xDBC0,\n\t0x52BF, 0xCAC6, 0x52C0, 0x84C4, 0x52C1, 0x84C5, 0x52C2, 0x84C6,\n\t0x52C3, 0xB2AA, 0x52C4, 0x84C7, 0x52C5, 0x84C8, 0x52C6, 0x84C9,\n\t0x52C7, 0xD3C2, 0x52C8, 0x84CA, 0x52C9, 0xC3E3, 0x52CA, 0x84CB,\n\t0x52CB, 0xD1AB, 0x52CC, 0x84CC, 0x52CD, 0x84CD, 0x52CE, 0x84CE,\n\t0x52CF, 0x84CF, 0x52D0, 0xDBC2, 0x52D1, 0x84D0, 0x52D2, 0xC0D5,\n\t0x52D3, 0x84D1, 0x52D4, 0x84D2, 0x52D5, 0x84D3, 0x52D6, 0xDBC3,\n\t0x52D7, 0x84D4, 0x52D8, 0xBFB1, 0x52D9, 0x84D5, 0x52DA, 0x84D6,\n\t0x52DB, 0x84D7, 0x52DC, 0x84D8, 0x52DD, 0x84D9, 0x52DE, 0x84DA,\n\t0x52DF, 0xC4BC, 0x52E0, 0x84DB, 0x52E1, 0x84DC, 0x52E2, 0x84DD,\n\t0x52E3, 0x84DE, 0x52E4, 0xC7DA, 0x52E5, 0x84DF, 0x52E6, 0x84E0,\n\t0x52E7, 0x84E1, 0x52E8, 0x84E2, 0x52E9, 0x84E3, 0x52EA, 0x84E4,\n\t0x52EB, 0x84E5, 0x52EC, 0x84E6, 0x52ED, 0x84E7, 0x52EE, 0x84E8,\n\t0x52EF, 0x84E9, 0x52F0, 0xDBC4, 0x52F1, 0x84EA, 0x52F2, 0x84EB,\n\t0x52F3, 0x84EC, 0x52F4, 0x84ED, 0x52F5, 0x84EE, 0x52F6, 0x84EF,\n\t0x52F7, 0x84F0, 0x52F8, 0x84F1, 0x52F9, 0xD9E8, 0x52FA, 0xC9D7,\n\t0x52FB, 0x84F2, 0x52FC, 0x84F3, 0x52FD, 0x84F4, 0x52FE, 0xB9B4,\n\t0x52FF, 0xCEF0, 0x5300, 0xD4C8, 0x5301, 0x84F5, 0x5302, 0x84F6,\n\t0x5303, 0x84F7, 0x5304, 0x84F8, 0x5305, 0xB0FC, 0x5306, 0xB4D2,\n\t0x5307, 0x84F9, 0x5308, 0xD0D9, 0x5309, 0x84FA, 0x530A, 0x84FB,\n\t0x530B, 0x84FC, 0x530C, 0x84FD, 0x530D, 0xD9E9, 0x530E, 0x84FE,\n\t0x530F, 0xDECB, 0x5310, 0xD9EB, 0x5311, 0x8540, 0x5312, 0x8541,\n\t0x5313, 0x8542, 0x5314, 0x8543, 0x5315, 0xD8B0, 0x5316, 0xBBAF,\n\t0x5317, 0xB1B1, 0x5318, 0x8544, 0x5319, 0xB3D7, 0x531A, 0xD8CE,\n\t0x531B, 0x8545, 0x531C, 0x8546, 0x531D, 0xD4D1, 0x531E, 0x8547,\n\t0x531F, 0x8548, 0x5320, 0xBDB3, 0x5321, 0xBFEF, 0x5322, 0x8549,\n\t0x5323, 0xCFBB, 0x5324, 0x854A, 0x5325, 0x854B, 0x5326, 0xD8D0,\n\t0x5327, 0x854C, 0x5328, 0x854D, 0x5329, 0x854E, 0x532A, 0xB7CB,\n\t0x532B, 0x854F, 0x532C, 0x8550, 0x532D, 0x8551, 0x532E, 0xD8D1,\n\t0x532F, 0x8552, 0x5330, 0x8553, 0x5331, 0x8554, 0x5332, 0x8555,\n\t0x5333, 0x8556, 0x5334, 0x8557, 0x5335, 0x8558, 0x5336, 0x8559,\n\t0x5337, 0x855A, 0x5338, 0x855B, 0x5339, 0xC6A5, 0x533A, 0xC7F8,\n\t0x533B, 0xD2BD, 0x533C, 0x855C, 0x533D, 0x855D, 0x533E, 0xD8D2,\n\t0x533F, 0xC4E4, 0x5340, 0x855E, 0x5341, 0xCAAE, 0x5342, 0x855F,\n\t0x5343, 0xC7A7, 0x5344, 0x8560, 0x5345, 0xD8A6, 0x5346, 0x8561,\n\t0x5347, 0xC9FD, 0x5348, 0xCEE7, 0x5349, 0xBBDC, 0x534A, 0xB0EB,\n\t0x534B, 0x8562, 0x534C, 0x8563, 0x534D, 0x8564, 0x534E, 0xBBAA,\n\t0x534F, 0xD0AD, 0x5350, 0x8565, 0x5351, 0xB1B0, 0x5352, 0xD7E4,\n\t0x5353, 0xD7BF, 0x5354, 0x8566, 0x5355, 0xB5A5, 0x5356, 0xC2F4,\n\t0x5357, 0xC4CF, 0x5358, 0x8567, 0x5359, 0x8568, 0x535A, 0xB2A9,\n\t0x535B, 0x8569, 0x535C, 0xB2B7, 0x535D, 0x856A, 0x535E, 0xB1E5,\n\t0x535F, 0xDFB2, 0x5360, 0xD5BC, 0x5361, 0xBFA8, 0x5362, 0xC2AC,\n\t0x5363, 0xD8D5, 0x5364, 0xC2B1, 0x5365, 0x856B, 0x5366, 0xD8D4,\n\t0x5367, 0xCED4, 0x5368, 0x856C, 0x5369, 0xDAE0, 0x536A, 0x856D,\n\t0x536B, 0xCEC0, 0x536C, 0x856E, 0x536D, 0x856F, 0x536E, 0xD8B4,\n\t0x536F, 0xC3AE, 0x5370, 0xD3A1, 0x5371, 0xCEA3, 0x5372, 0x8570,\n\t0x5373, 0xBCB4, 0x5374, 0xC8B4, 0x5375, 0xC2D1, 0x5376, 0x8571,\n\t0x5377, 0xBEED, 0x5378, 0xD0B6, 0x5379, 0x8572, 0x537A, 0xDAE1,\n\t0x537B, 0x8573, 0x537C, 0x8574, 0x537D, 0x8575, 0x537E, 0x8576,\n\t0x537F, 0xC7E4, 0x5380, 0x8577, 0x5381, 0x8578, 0x5382, 0xB3A7,\n\t0x5383, 0x8579, 0x5384, 0xB6F2, 0x5385, 0xCCFC, 0x5386, 0xC0FA,\n\t0x5387, 0x857A, 0x5388, 0x857B, 0x5389, 0xC0F7, 0x538A, 0x857C,\n\t0x538B, 0xD1B9, 0x538C, 0xD1E1, 0x538D, 0xD8C7, 0x538E, 0x857D,\n\t0x538F, 0x857E, 0x5390, 0x8580, 0x5391, 0x8581, 0x5392, 0x8582,\n\t0x5393, 0x8583, 0x5394, 0x8584, 0x5395, 0xB2DE, 0x5396, 0x8585,\n\t0x5397, 0x8586, 0x5398, 0xC0E5, 0x5399, 0x8587, 0x539A, 0xBAF1,\n\t0x539B, 0x8588, 0x539C, 0x8589, 0x539D, 0xD8C8, 0x539E, 0x858A,\n\t0x539F, 0xD4AD, 0x53A0, 0x858B, 0x53A1, 0x858C, 0x53A2, 0xCFE1,\n\t0x53A3, 0xD8C9, 0x53A4, 0x858D, 0x53A5, 0xD8CA, 0x53A6, 0xCFC3,\n\t0x53A7, 0x858E, 0x53A8, 0xB3F8, 0x53A9, 0xBEC7, 0x53AA, 0x858F,\n\t0x53AB, 0x8590, 0x53AC, 0x8591, 0x53AD, 0x8592, 0x53AE, 0xD8CB,\n\t0x53AF, 0x8593, 0x53B0, 0x8594, 0x53B1, 0x8595, 0x53B2, 0x8596,\n\t0x53B3, 0x8597, 0x53B4, 0x8598, 0x53B5, 0x8599, 0x53B6, 0xDBCC,\n\t0x53B7, 0x859A, 0x53B8, 0x859B, 0x53B9, 0x859C, 0x53BA, 0x859D,\n\t0x53BB, 0xC8A5, 0x53BC, 0x859E, 0x53BD, 0x859F, 0x53BE, 0x85A0,\n\t0x53BF, 0xCFD8, 0x53C0, 0x85A1, 0x53C1, 0xC8FE, 0x53C2, 0xB2CE,\n\t0x53C3, 0x85A2, 0x53C4, 0x85A3, 0x53C5, 0x85A4, 0x53C6, 0x85A5,\n\t0x53C7, 0x85A6, 0x53C8, 0xD3D6, 0x53C9, 0xB2E6, 0x53CA, 0xBCB0,\n\t0x53CB, 0xD3D1, 0x53CC, 0xCBAB, 0x53CD, 0xB7B4, 0x53CE, 0x85A7,\n\t0x53CF, 0x85A8, 0x53D0, 0x85A9, 0x53D1, 0xB7A2, 0x53D2, 0x85AA,\n\t0x53D3, 0x85AB, 0x53D4, 0xCAE5, 0x53D5, 0x85AC, 0x53D6, 0xC8A1,\n\t0x53D7, 0xCADC, 0x53D8, 0xB1E4, 0x53D9, 0xD0F0, 0x53DA, 0x85AD,\n\t0x53DB, 0xC5D1, 0x53DC, 0x85AE, 0x53DD, 0x85AF, 0x53DE, 0x85B0,\n\t0x53DF, 0xDBC5, 0x53E0, 0xB5FE, 0x53E1, 0x85B1, 0x53E2, 0x85B2,\n\t0x53E3, 0xBFDA, 0x53E4, 0xB9C5, 0x53E5, 0xBEE4, 0x53E6, 0xC1ED,\n\t0x53E7, 0x85B3, 0x53E8, 0xDFB6, 0x53E9, 0xDFB5, 0x53EA, 0xD6BB,\n\t0x53EB, 0xBDD0, 0x53EC, 0xD5D9, 0x53ED, 0xB0C8, 0x53EE, 0xB6A3,\n\t0x53EF, 0xBFC9, 0x53F0, 0xCCA8, 0x53F1, 0xDFB3, 0x53F2, 0xCAB7,\n\t0x53F3, 0xD3D2, 0x53F4, 0x85B4, 0x53F5, 0xD8CF, 0x53F6, 0xD2B6,\n\t0x53F7, 0xBAC5, 0x53F8, 0xCBBE, 0x53F9, 0xCCBE, 0x53FA, 0x85B5,\n\t0x53FB, 0xDFB7, 0x53FC, 0xB5F0, 0x53FD, 0xDFB4, 0x53FE, 0x85B6,\n\t0x53FF, 0x85B7, 0x5400, 0x85B8, 0x5401, 0xD3F5, 0x5402, 0x85B9,\n\t0x5403, 0xB3D4, 0x5404, 0xB8F7, 0x5405, 0x85BA, 0x5406, 0xDFBA,\n\t0x5407, 0x85BB, 0x5408, 0xBACF, 0x5409, 0xBCAA, 0x540A, 0xB5F5,\n\t0x540B, 0x85BC, 0x540C, 0xCDAC, 0x540D, 0xC3FB, 0x540E, 0xBAF3,\n\t0x540F, 0xC0F4, 0x5410, 0xCDC2, 0x5411, 0xCFF2, 0x5412, 0xDFB8,\n\t0x5413, 0xCFC5, 0x5414, 0x85BD, 0x5415, 0xC2C0, 0x5416, 0xDFB9,\n\t0x5417, 0xC2F0, 0x5418, 0x85BE, 0x5419, 0x85BF, 0x541A, 0x85C0,\n\t0x541B, 0xBEFD, 0x541C, 0x85C1, 0x541D, 0xC1DF, 0x541E, 0xCDCC,\n\t0x541F, 0xD2F7, 0x5420, 0xB7CD, 0x5421, 0xDFC1, 0x5422, 0x85C2,\n\t0x5423, 0xDFC4, 0x5424, 0x85C3, 0x5425, 0x85C4, 0x5426, 0xB7F1,\n\t0x5427, 0xB0C9, 0x5428, 0xB6D6, 0x5429, 0xB7D4, 0x542A, 0x85C5,\n\t0x542B, 0xBAAC, 0x542C, 0xCCFD, 0x542D, 0xBFD4, 0x542E, 0xCBB1,\n\t0x542F, 0xC6F4, 0x5430, 0x85C6, 0x5431, 0xD6A8, 0x5432, 0xDFC5,\n\t0x5433, 0x85C7, 0x5434, 0xCEE2, 0x5435, 0xB3B3, 0x5436, 0x85C8,\n\t0x5437, 0x85C9, 0x5438, 0xCEFC, 0x5439, 0xB4B5, 0x543A, 0x85CA,\n\t0x543B, 0xCEC7, 0x543C, 0xBAF0, 0x543D, 0x85CB, 0x543E, 0xCEE1,\n\t0x543F, 0x85CC, 0x5440, 0xD1BD, 0x5441, 0x85CD, 0x5442, 0x85CE,\n\t0x5443, 0xDFC0, 0x5444, 0x85CF, 0x5445, 0x85D0, 0x5446, 0xB4F4,\n\t0x5447, 0x85D1, 0x5448, 0xB3CA, 0x5449, 0x85D2, 0x544A, 0xB8E6,\n\t0x544B, 0xDFBB, 0x544C, 0x85D3, 0x544D, 0x85D4, 0x544E, 0x85D5,\n\t0x544F, 0x85D6, 0x5450, 0xC4C5, 0x5451, 0x85D7, 0x5452, 0xDFBC,\n\t0x5453, 0xDFBD, 0x5454, 0xDFBE, 0x5455, 0xC5BB, 0x5456, 0xDFBF,\n\t0x5457, 0xDFC2, 0x5458, 0xD4B1, 0x5459, 0xDFC3, 0x545A, 0x85D8,\n\t0x545B, 0xC7BA, 0x545C, 0xCED8, 0x545D, 0x85D9, 0x545E, 0x85DA,\n\t0x545F, 0x85DB, 0x5460, 0x85DC, 0x5461, 0x85DD, 0x5462, 0xC4D8,\n\t0x5463, 0x85DE, 0x5464, 0xDFCA, 0x5465, 0x85DF, 0x5466, 0xDFCF,\n\t0x5467, 0x85E0, 0x5468, 0xD6DC, 0x5469, 0x85E1, 0x546A, 0x85E2,\n\t0x546B, 0x85E3, 0x546C, 0x85E4, 0x546D, 0x85E5, 0x546E, 0x85E6,\n\t0x546F, 0x85E7, 0x5470, 0x85E8, 0x5471, 0xDFC9, 0x5472, 0xDFDA,\n\t0x5473, 0xCEB6, 0x5474, 0x85E9, 0x5475, 0xBAC7, 0x5476, 0xDFCE,\n\t0x5477, 0xDFC8, 0x5478, 0xC5DE, 0x5479, 0x85EA, 0x547A, 0x85EB,\n\t0x547B, 0xC9EB, 0x547C, 0xBAF4, 0x547D, 0xC3FC, 0x547E, 0x85EC,\n\t0x547F, 0x85ED, 0x5480, 0xBED7, 0x5481, 0x85EE, 0x5482, 0xDFC6,\n\t0x5483, 0x85EF, 0x5484, 0xDFCD, 0x5485, 0x85F0, 0x5486, 0xC5D8,\n\t0x5487, 0x85F1, 0x5488, 0x85F2, 0x5489, 0x85F3, 0x548A, 0x85F4,\n\t0x548B, 0xD5A6, 0x548C, 0xBACD, 0x548D, 0x85F5, 0x548E, 0xBECC,\n\t0x548F, 0xD3BD, 0x5490, 0xB8C0, 0x5491, 0x85F6, 0x5492, 0xD6E4,\n\t0x5493, 0x85F7, 0x5494, 0xDFC7, 0x5495, 0xB9BE, 0x5496, 0xBFA7,\n\t0x5497, 0x85F8, 0x5498, 0x85F9, 0x5499, 0xC1FC, 0x549A, 0xDFCB,\n\t0x549B, 0xDFCC, 0x549C, 0x85FA, 0x549D, 0xDFD0, 0x549E, 0x85FB,\n\t0x549F, 0x85FC, 0x54A0, 0x85FD, 0x54A1, 0x85FE, 0x54A2, 0x8640,\n\t0x54A3, 0xDFDB, 0x54A4, 0xDFE5, 0x54A5, 0x8641, 0x54A6, 0xDFD7,\n\t0x54A7, 0xDFD6, 0x54A8, 0xD7C9, 0x54A9, 0xDFE3, 0x54AA, 0xDFE4,\n\t0x54AB, 0xE5EB, 0x54AC, 0xD2A7, 0x54AD, 0xDFD2, 0x54AE, 0x8642,\n\t0x54AF, 0xBFA9, 0x54B0, 0x8643, 0x54B1, 0xD4DB, 0x54B2, 0x8644,\n\t0x54B3, 0xBFC8, 0x54B4, 0xDFD4, 0x54B5, 0x8645, 0x54B6, 0x8646,\n\t0x54B7, 0x8647, 0x54B8, 0xCFCC, 0x54B9, 0x8648, 0x54BA, 0x8649,\n\t0x54BB, 0xDFDD, 0x54BC, 0x864A, 0x54BD, 0xD1CA, 0x54BE, 0x864B,\n\t0x54BF, 0xDFDE, 0x54C0, 0xB0A7, 0x54C1, 0xC6B7, 0x54C2, 0xDFD3,\n\t0x54C3, 0x864C, 0x54C4, 0xBAE5, 0x54C5, 0x864D, 0x54C6, 0xB6DF,\n\t0x54C7, 0xCDDB, 0x54C8, 0xB9FE, 0x54C9, 0xD4D5, 0x54CA, 0x864E,\n\t0x54CB, 0x864F, 0x54CC, 0xDFDF, 0x54CD, 0xCFEC, 0x54CE, 0xB0A5,\n\t0x54CF, 0xDFE7, 0x54D0, 0xDFD1, 0x54D1, 0xD1C6, 0x54D2, 0xDFD5,\n\t0x54D3, 0xDFD8, 0x54D4, 0xDFD9, 0x54D5, 0xDFDC, 0x54D6, 0x8650,\n\t0x54D7, 0xBBA9, 0x54D8, 0x8651, 0x54D9, 0xDFE0, 0x54DA, 0xDFE1,\n\t0x54DB, 0x8652, 0x54DC, 0xDFE2, 0x54DD, 0xDFE6, 0x54DE, 0xDFE8,\n\t0x54DF, 0xD3B4, 0x54E0, 0x8653, 0x54E1, 0x8654, 0x54E2, 0x8655,\n\t0x54E3, 0x8656, 0x54E4, 0x8657, 0x54E5, 0xB8E7, 0x54E6, 0xC5B6,\n\t0x54E7, 0xDFEA, 0x54E8, 0xC9DA, 0x54E9, 0xC1A8, 0x54EA, 0xC4C4,\n\t0x54EB, 0x8658, 0x54EC, 0x8659, 0x54ED, 0xBFDE, 0x54EE, 0xCFF8,\n\t0x54EF, 0x865A, 0x54F0, 0x865B, 0x54F1, 0x865C, 0x54F2, 0xD5DC,\n\t0x54F3, 0xDFEE, 0x54F4, 0x865D, 0x54F5, 0x865E, 0x54F6, 0x865F,\n\t0x54F7, 0x8660, 0x54F8, 0x8661, 0x54F9, 0x8662, 0x54FA, 0xB2B8,\n\t0x54FB, 0x8663, 0x54FC, 0xBADF, 0x54FD, 0xDFEC, 0x54FE, 0x8664,\n\t0x54FF, 0xDBC1, 0x5500, 0x8665, 0x5501, 0xD1E4, 0x5502, 0x8666,\n\t0x5503, 0x8667, 0x5504, 0x8668, 0x5505, 0x8669, 0x5506, 0xCBF4,\n\t0x5507, 0xB4BD, 0x5508, 0x866A, 0x5509, 0xB0A6, 0x550A, 0x866B,\n\t0x550B, 0x866C, 0x550C, 0x866D, 0x550D, 0x866E, 0x550E, 0x866F,\n\t0x550F, 0xDFF1, 0x5510, 0xCCC6, 0x5511, 0xDFF2, 0x5512, 0x8670,\n\t0x5513, 0x8671, 0x5514, 0xDFED, 0x5515, 0x8672, 0x5516, 0x8673,\n\t0x5517, 0x8674, 0x5518, 0x8675, 0x5519, 0x8676, 0x551A, 0x8677,\n\t0x551B, 0xDFE9, 0x551C, 0x8678, 0x551D, 0x8679, 0x551E, 0x867A,\n\t0x551F, 0x867B, 0x5520, 0xDFEB, 0x5521, 0x867C, 0x5522, 0xDFEF,\n\t0x5523, 0xDFF0, 0x5524, 0xBBBD, 0x5525, 0x867D, 0x5526, 0x867E,\n\t0x5527, 0xDFF3, 0x5528, 0x8680, 0x5529, 0x8681, 0x552A, 0xDFF4,\n\t0x552B, 0x8682, 0x552C, 0xBBA3, 0x552D, 0x8683, 0x552E, 0xCADB,\n\t0x552F, 0xCEA8, 0x5530, 0xE0A7, 0x5531, 0xB3AA, 0x5532, 0x8684,\n\t0x5533, 0xE0A6, 0x5534, 0x8685, 0x5535, 0x8686, 0x5536, 0x8687,\n\t0x5537, 0xE0A1, 0x5538, 0x8688, 0x5539, 0x8689, 0x553A, 0x868A,\n\t0x553B, 0x868B, 0x553C, 0xDFFE, 0x553D, 0x868C, 0x553E, 0xCDD9,\n\t0x553F, 0xDFFC, 0x5540, 0x868D, 0x5541, 0xDFFA, 0x5542, 0x868E,\n\t0x5543, 0xBFD0, 0x5544, 0xD7C4, 0x5545, 0x868F, 0x5546, 0xC9CC,\n\t0x5547, 0x8690, 0x5548, 0x8691, 0x5549, 0xDFF8, 0x554A, 0xB0A1,\n\t0x554B, 0x8692, 0x554C, 0x8693, 0x554D, 0x8694, 0x554E, 0x8695,\n\t0x554F, 0x8696, 0x5550, 0xDFFD, 0x5551, 0x8697, 0x5552, 0x8698,\n\t0x5553, 0x8699, 0x5554, 0x869A, 0x5555, 0xDFFB, 0x5556, 0xE0A2,\n\t0x5557, 0x869B, 0x5558, 0x869C, 0x5559, 0x869D, 0x555A, 0x869E,\n\t0x555B, 0x869F, 0x555C, 0xE0A8, 0x555D, 0x86A0, 0x555E, 0x86A1,\n\t0x555F, 0x86A2, 0x5560, 0x86A3, 0x5561, 0xB7C8, 0x5562, 0x86A4,\n\t0x5563, 0x86A5, 0x5564, 0xC6A1, 0x5565, 0xC9B6, 0x5566, 0xC0B2,\n\t0x5567, 0xDFF5, 0x5568, 0x86A6, 0x5569, 0x86A7, 0x556A, 0xC5BE,\n\t0x556B, 0x86A8, 0x556C, 0xD8C4, 0x556D, 0xDFF9, 0x556E, 0xC4F6,\n\t0x556F, 0x86A9, 0x5570, 0x86AA, 0x5571, 0x86AB, 0x5572, 0x86AC,\n\t0x5573, 0x86AD, 0x5574, 0x86AE, 0x5575, 0xE0A3, 0x5576, 0xE0A4,\n\t0x5577, 0xE0A5, 0x5578, 0xD0A5, 0x5579, 0x86AF, 0x557A, 0x86B0,\n\t0x557B, 0xE0B4, 0x557C, 0xCCE4, 0x557D, 0x86B1, 0x557E, 0xE0B1,\n\t0x557F, 0x86B2, 0x5580, 0xBFA6, 0x5581, 0xE0AF, 0x5582, 0xCEB9,\n\t0x5583, 0xE0AB, 0x5584, 0xC9C6, 0x5585, 0x86B3, 0x5586, 0x86B4,\n\t0x5587, 0xC0AE, 0x5588, 0xE0AE, 0x5589, 0xBAED, 0x558A, 0xBAB0,\n\t0x558B, 0xE0A9, 0x558C, 0x86B5, 0x558D, 0x86B6, 0x558E, 0x86B7,\n\t0x558F, 0xDFF6, 0x5590, 0x86B8, 0x5591, 0xE0B3, 0x5592, 0x86B9,\n\t0x5593, 0x86BA, 0x5594, 0xE0B8, 0x5595, 0x86BB, 0x5596, 0x86BC,\n\t0x5597, 0x86BD, 0x5598, 0xB4AD, 0x5599, 0xE0B9, 0x559A, 0x86BE,\n\t0x559B, 0x86BF, 0x559C, 0xCFB2, 0x559D, 0xBAC8, 0x559E, 0x86C0,\n\t0x559F, 0xE0B0, 0x55A0, 0x86C1, 0x55A1, 0x86C2, 0x55A2, 0x86C3,\n\t0x55A3, 0x86C4, 0x55A4, 0x86C5, 0x55A5, 0x86C6, 0x55A6, 0x86C7,\n\t0x55A7, 0xD0FA, 0x55A8, 0x86C8, 0x55A9, 0x86C9, 0x55AA, 0x86CA,\n\t0x55AB, 0x86CB, 0x55AC, 0x86CC, 0x55AD, 0x86CD, 0x55AE, 0x86CE,\n\t0x55AF, 0x86CF, 0x55B0, 0x86D0, 0x55B1, 0xE0AC, 0x55B2, 0x86D1,\n\t0x55B3, 0xD4FB, 0x55B4, 0x86D2, 0x55B5, 0xDFF7, 0x55B6, 0x86D3,\n\t0x55B7, 0xC5E7, 0x55B8, 0x86D4, 0x55B9, 0xE0AD, 0x55BA, 0x86D5,\n\t0x55BB, 0xD3F7, 0x55BC, 0x86D6, 0x55BD, 0xE0B6, 0x55BE, 0xE0B7,\n\t0x55BF, 0x86D7, 0x55C0, 0x86D8, 0x55C1, 0x86D9, 0x55C2, 0x86DA,\n\t0x55C3, 0x86DB, 0x55C4, 0xE0C4, 0x55C5, 0xD0E1, 0x55C6, 0x86DC,\n\t0x55C7, 0x86DD, 0x55C8, 0x86DE, 0x55C9, 0xE0BC, 0x55CA, 0x86DF,\n\t0x55CB, 0x86E0, 0x55CC, 0xE0C9, 0x55CD, 0xE0CA, 0x55CE, 0x86E1,\n\t0x55CF, 0x86E2, 0x55D0, 0x86E3, 0x55D1, 0xE0BE, 0x55D2, 0xE0AA,\n\t0x55D3, 0xC9A4, 0x55D4, 0xE0C1, 0x55D5, 0x86E4, 0x55D6, 0xE0B2,\n\t0x55D7, 0x86E5, 0x55D8, 0x86E6, 0x55D9, 0x86E7, 0x55DA, 0x86E8,\n\t0x55DB, 0x86E9, 0x55DC, 0xCAC8, 0x55DD, 0xE0C3, 0x55DE, 0x86EA,\n\t0x55DF, 0xE0B5, 0x55E0, 0x86EB, 0x55E1, 0xCECB, 0x55E2, 0x86EC,\n\t0x55E3, 0xCBC3, 0x55E4, 0xE0CD, 0x55E5, 0xE0C6, 0x55E6, 0xE0C2,\n\t0x55E7, 0x86ED, 0x55E8, 0xE0CB, 0x55E9, 0x86EE, 0x55EA, 0xE0BA,\n\t0x55EB, 0xE0BF, 0x55EC, 0xE0C0, 0x55ED, 0x86EF, 0x55EE, 0x86F0,\n\t0x55EF, 0xE0C5, 0x55F0, 0x86F1, 0x55F1, 0x86F2, 0x55F2, 0xE0C7,\n\t0x55F3, 0xE0C8, 0x55F4, 0x86F3, 0x55F5, 0xE0CC, 0x55F6, 0x86F4,\n\t0x55F7, 0xE0BB, 0x55F8, 0x86F5, 0x55F9, 0x86F6, 0x55FA, 0x86F7,\n\t0x55FB, 0x86F8, 0x55FC, 0x86F9, 0x55FD, 0xCBD4, 0x55FE, 0xE0D5,\n\t0x55FF, 0x86FA, 0x5600, 0xE0D6, 0x5601, 0xE0D2, 0x5602, 0x86FB,\n\t0x5603, 0x86FC, 0x5604, 0x86FD, 0x5605, 0x86FE, 0x5606, 0x8740,\n\t0x5607, 0x8741, 0x5608, 0xE0D0, 0x5609, 0xBCCE, 0x560A, 0x8742,\n\t0x560B, 0x8743, 0x560C, 0xE0D1, 0x560D, 0x8744, 0x560E, 0xB8C2,\n\t0x560F, 0xD8C5, 0x5610, 0x8745, 0x5611, 0x8746, 0x5612, 0x8747,\n\t0x5613, 0x8748, 0x5614, 0x8749, 0x5615, 0x874A, 0x5616, 0x874B,\n\t0x5617, 0x874C, 0x5618, 0xD0EA, 0x5619, 0x874D, 0x561A, 0x874E,\n\t0x561B, 0xC2EF, 0x561C, 0x874F, 0x561D, 0x8750, 0x561E, 0xE0CF,\n\t0x561F, 0xE0BD, 0x5620, 0x8751, 0x5621, 0x8752, 0x5622, 0x8753,\n\t0x5623, 0xE0D4, 0x5624, 0xE0D3, 0x5625, 0x8754, 0x5626, 0x8755,\n\t0x5627, 0xE0D7, 0x5628, 0x8756, 0x5629, 0x8757, 0x562A, 0x8758,\n\t0x562B, 0x8759, 0x562C, 0xE0DC, 0x562D, 0xE0D8, 0x562E, 0x875A,\n\t0x562F, 0x875B, 0x5630, 0x875C, 0x5631, 0xD6F6, 0x5632, 0xB3B0,\n\t0x5633, 0x875D, 0x5634, 0xD7EC, 0x5635, 0x875E, 0x5636, 0xCBBB,\n\t0x5637, 0x875F, 0x5638, 0x8760, 0x5639, 0xE0DA, 0x563A, 0x8761,\n\t0x563B, 0xCEFB, 0x563C, 0x8762, 0x563D, 0x8763, 0x563E, 0x8764,\n\t0x563F, 0xBAD9, 0x5640, 0x8765, 0x5641, 0x8766, 0x5642, 0x8767,\n\t0x5643, 0x8768, 0x5644, 0x8769, 0x5645, 0x876A, 0x5646, 0x876B,\n\t0x5647, 0x876C, 0x5648, 0x876D, 0x5649, 0x876E, 0x564A, 0x876F,\n\t0x564B, 0x8770, 0x564C, 0xE0E1, 0x564D, 0xE0DD, 0x564E, 0xD2AD,\n\t0x564F, 0x8771, 0x5650, 0x8772, 0x5651, 0x8773, 0x5652, 0x8774,\n\t0x5653, 0x8775, 0x5654, 0xE0E2, 0x5655, 0x8776, 0x5656, 0x8777,\n\t0x5657, 0xE0DB, 0x5658, 0xE0D9, 0x5659, 0xE0DF, 0x565A, 0x8778,\n\t0x565B, 0x8779, 0x565C, 0xE0E0, 0x565D, 0x877A, 0x565E, 0x877B,\n\t0x565F, 0x877C, 0x5660, 0x877D, 0x5661, 0x877E, 0x5662, 0xE0DE,\n\t0x5663, 0x8780, 0x5664, 0xE0E4, 0x5665, 0x8781, 0x5666, 0x8782,\n\t0x5667, 0x8783, 0x5668, 0xC6F7, 0x5669, 0xD8AC, 0x566A, 0xD4EB,\n\t0x566B, 0xE0E6, 0x566C, 0xCAC9, 0x566D, 0x8784, 0x566E, 0x8785,\n\t0x566F, 0x8786, 0x5670, 0x8787, 0x5671, 0xE0E5, 0x5672, 0x8788,\n\t0x5673, 0x8789, 0x5674, 0x878A, 0x5675, 0x878B, 0x5676, 0xB8C1,\n\t0x5677, 0x878C, 0x5678, 0x878D, 0x5679, 0x878E, 0x567A, 0x878F,\n\t0x567B, 0xE0E7, 0x567C, 0xE0E8, 0x567D, 0x8790, 0x567E, 0x8791,\n\t0x567F, 0x8792, 0x5680, 0x8793, 0x5681, 0x8794, 0x5682, 0x8795,\n\t0x5683, 0x8796, 0x5684, 0x8797, 0x5685, 0xE0E9, 0x5686, 0xE0E3,\n\t0x5687, 0x8798, 0x5688, 0x8799, 0x5689, 0x879A, 0x568A, 0x879B,\n\t0x568B, 0x879C, 0x568C, 0x879D, 0x568D, 0x879E, 0x568E, 0xBABF,\n\t0x568F, 0xCCE7, 0x5690, 0x879F, 0x5691, 0x87A0, 0x5692, 0x87A1,\n\t0x5693, 0xE0EA, 0x5694, 0x87A2, 0x5695, 0x87A3, 0x5696, 0x87A4,\n\t0x5697, 0x87A5, 0x5698, 0x87A6, 0x5699, 0x87A7, 0x569A, 0x87A8,\n\t0x569B, 0x87A9, 0x569C, 0x87AA, 0x569D, 0x87AB, 0x569E, 0x87AC,\n\t0x569F, 0x87AD, 0x56A0, 0x87AE, 0x56A1, 0x87AF, 0x56A2, 0x87B0,\n\t0x56A3, 0xCFF9, 0x56A4, 0x87B1, 0x56A5, 0x87B2, 0x56A6, 0x87B3,\n\t0x56A7, 0x87B4, 0x56A8, 0x87B5, 0x56A9, 0x87B6, 0x56AA, 0x87B7,\n\t0x56AB, 0x87B8, 0x56AC, 0x87B9, 0x56AD, 0x87BA, 0x56AE, 0x87BB,\n\t0x56AF, 0xE0EB, 0x56B0, 0x87BC, 0x56B1, 0x87BD, 0x56B2, 0x87BE,\n\t0x56B3, 0x87BF, 0x56B4, 0x87C0, 0x56B5, 0x87C1, 0x56B6, 0x87C2,\n\t0x56B7, 0xC8C2, 0x56B8, 0x87C3, 0x56B9, 0x87C4, 0x56BA, 0x87C5,\n\t0x56BB, 0x87C6, 0x56BC, 0xBDC0, 0x56BD, 0x87C7, 0x56BE, 0x87C8,\n\t0x56BF, 0x87C9, 0x56C0, 0x87CA, 0x56C1, 0x87CB, 0x56C2, 0x87CC,\n\t0x56C3, 0x87CD, 0x56C4, 0x87CE, 0x56C5, 0x87CF, 0x56C6, 0x87D0,\n\t0x56C7, 0x87D1, 0x56C8, 0x87D2, 0x56C9, 0x87D3, 0x56CA, 0xC4D2,\n\t0x56CB, 0x87D4, 0x56CC, 0x87D5, 0x56CD, 0x87D6, 0x56CE, 0x87D7,\n\t0x56CF, 0x87D8, 0x56D0, 0x87D9, 0x56D1, 0x87DA, 0x56D2, 0x87DB,\n\t0x56D3, 0x87DC, 0x56D4, 0xE0EC, 0x56D5, 0x87DD, 0x56D6, 0x87DE,\n\t0x56D7, 0xE0ED, 0x56D8, 0x87DF, 0x56D9, 0x87E0, 0x56DA, 0xC7F4,\n\t0x56DB, 0xCBC4, 0x56DC, 0x87E1, 0x56DD, 0xE0EE, 0x56DE, 0xBBD8,\n\t0x56DF, 0xD8B6, 0x56E0, 0xD2F2, 0x56E1, 0xE0EF, 0x56E2, 0xCDC5,\n\t0x56E3, 0x87E2, 0x56E4, 0xB6DA, 0x56E5, 0x87E3, 0x56E6, 0x87E4,\n\t0x56E7, 0x87E5, 0x56E8, 0x87E6, 0x56E9, 0x87E7, 0x56EA, 0x87E8,\n\t0x56EB, 0xE0F1, 0x56EC, 0x87E9, 0x56ED, 0xD4B0, 0x56EE, 0x87EA,\n\t0x56EF, 0x87EB, 0x56F0, 0xC0A7, 0x56F1, 0xB4D1, 0x56F2, 0x87EC,\n\t0x56F3, 0x87ED, 0x56F4, 0xCEA7, 0x56F5, 0xE0F0, 0x56F6, 0x87EE,\n\t0x56F7, 0x87EF, 0x56F8, 0x87F0, 0x56F9, 0xE0F2, 0x56FA, 0xB9CC,\n\t0x56FB, 0x87F1, 0x56FC, 0x87F2, 0x56FD, 0xB9FA, 0x56FE, 0xCDBC,\n\t0x56FF, 0xE0F3, 0x5700, 0x87F3, 0x5701, 0x87F4, 0x5702, 0x87F5,\n\t0x5703, 0xC6D4, 0x5704, 0xE0F4, 0x5705, 0x87F6, 0x5706, 0xD4B2,\n\t0x5707, 0x87F7, 0x5708, 0xC8A6, 0x5709, 0xE0F6, 0x570A, 0xE0F5,\n\t0x570B, 0x87F8, 0x570C, 0x87F9, 0x570D, 0x87FA, 0x570E, 0x87FB,\n\t0x570F, 0x87FC, 0x5710, 0x87FD, 0x5711, 0x87FE, 0x5712, 0x8840,\n\t0x5713, 0x8841, 0x5714, 0x8842, 0x5715, 0x8843, 0x5716, 0x8844,\n\t0x5717, 0x8845, 0x5718, 0x8846, 0x5719, 0x8847, 0x571A, 0x8848,\n\t0x571B, 0x8849, 0x571C, 0xE0F7, 0x571D, 0x884A, 0x571E, 0x884B,\n\t0x571F, 0xCDC1, 0x5720, 0x884C, 0x5721, 0x884D, 0x5722, 0x884E,\n\t0x5723, 0xCAA5, 0x5724, 0x884F, 0x5725, 0x8850, 0x5726, 0x8851,\n\t0x5727, 0x8852, 0x5728, 0xD4DA, 0x5729, 0xDBD7, 0x572A, 0xDBD9,\n\t0x572B, 0x8853, 0x572C, 0xDBD8, 0x572D, 0xB9E7, 0x572E, 0xDBDC,\n\t0x572F, 0xDBDD, 0x5730, 0xB5D8, 0x5731, 0x8854, 0x5732, 0x8855,\n\t0x5733, 0xDBDA, 0x5734, 0x8856, 0x5735, 0x8857, 0x5736, 0x8858,\n\t0x5737, 0x8859, 0x5738, 0x885A, 0x5739, 0xDBDB, 0x573A, 0xB3A1,\n\t0x573B, 0xDBDF, 0x573C, 0x885B, 0x573D, 0x885C, 0x573E, 0xBBF8,\n\t0x573F, 0x885D, 0x5740, 0xD6B7, 0x5741, 0x885E, 0x5742, 0xDBE0,\n\t0x5743, 0x885F, 0x5744, 0x8860, 0x5745, 0x8861, 0x5746, 0x8862,\n\t0x5747, 0xBEF9, 0x5748, 0x8863, 0x5749, 0x8864, 0x574A, 0xB7BB,\n\t0x574B, 0x8865, 0x574C, 0xDBD0, 0x574D, 0xCCAE, 0x574E, 0xBFB2,\n\t0x574F, 0xBBB5, 0x5750, 0xD7F8, 0x5751, 0xBFD3, 0x5752, 0x8866,\n\t0x5753, 0x8867, 0x5754, 0x8868, 0x5755, 0x8869, 0x5756, 0x886A,\n\t0x5757, 0xBFE9, 0x5758, 0x886B, 0x5759, 0x886C, 0x575A, 0xBCE1,\n\t0x575B, 0xCCB3, 0x575C, 0xDBDE, 0x575D, 0xB0D3, 0x575E, 0xCEEB,\n\t0x575F, 0xB7D8, 0x5760, 0xD7B9, 0x5761, 0xC6C2, 0x5762, 0x886D,\n\t0x5763, 0x886E, 0x5764, 0xC0A4, 0x5765, 0x886F, 0x5766, 0xCCB9,\n\t0x5767, 0x8870, 0x5768, 0xDBE7, 0x5769, 0xDBE1, 0x576A, 0xC6BA,\n\t0x576B, 0xDBE3, 0x576C, 0x8871, 0x576D, 0xDBE8, 0x576E, 0x8872,\n\t0x576F, 0xC5F7, 0x5770, 0x8873, 0x5771, 0x8874, 0x5772, 0x8875,\n\t0x5773, 0xDBEA, 0x5774, 0x8876, 0x5775, 0x8877, 0x5776, 0xDBE9,\n\t0x5777, 0xBFC0, 0x5778, 0x8878, 0x5779, 0x8879, 0x577A, 0x887A,\n\t0x577B, 0xDBE6, 0x577C, 0xDBE5, 0x577D, 0x887B, 0x577E, 0x887C,\n\t0x577F, 0x887D, 0x5780, 0x887E, 0x5781, 0x8880, 0x5782, 0xB4B9,\n\t0x5783, 0xC0AC, 0x5784, 0xC2A2, 0x5785, 0xDBE2, 0x5786, 0xDBE4,\n\t0x5787, 0x8881, 0x5788, 0x8882, 0x5789, 0x8883, 0x578A, 0x8884,\n\t0x578B, 0xD0CD, 0x578C, 0xDBED, 0x578D, 0x8885, 0x578E, 0x8886,\n\t0x578F, 0x8887, 0x5790, 0x8888, 0x5791, 0x8889, 0x5792, 0xC0DD,\n\t0x5793, 0xDBF2, 0x5794, 0x888A, 0x5795, 0x888B, 0x5796, 0x888C,\n\t0x5797, 0x888D, 0x5798, 0x888E, 0x5799, 0x888F, 0x579A, 0x8890,\n\t0x579B, 0xB6E2, 0x579C, 0x8891, 0x579D, 0x8892, 0x579E, 0x8893,\n\t0x579F, 0x8894, 0x57A0, 0xDBF3, 0x57A1, 0xDBD2, 0x57A2, 0xB9B8,\n\t0x57A3, 0xD4AB, 0x57A4, 0xDBEC, 0x57A5, 0x8895, 0x57A6, 0xBFD1,\n\t0x57A7, 0xDBF0, 0x57A8, 0x8896, 0x57A9, 0xDBD1, 0x57AA, 0x8897,\n\t0x57AB, 0xB5E6, 0x57AC, 0x8898, 0x57AD, 0xDBEB, 0x57AE, 0xBFE5,\n\t0x57AF, 0x8899, 0x57B0, 0x889A, 0x57B1, 0x889B, 0x57B2, 0xDBEE,\n\t0x57B3, 0x889C, 0x57B4, 0xDBF1, 0x57B5, 0x889D, 0x57B6, 0x889E,\n\t0x57B7, 0x889F, 0x57B8, 0xDBF9, 0x57B9, 0x88A0, 0x57BA, 0x88A1,\n\t0x57BB, 0x88A2, 0x57BC, 0x88A3, 0x57BD, 0x88A4, 0x57BE, 0x88A5,\n\t0x57BF, 0x88A6, 0x57C0, 0x88A7, 0x57C1, 0x88A8, 0x57C2, 0xB9A1,\n\t0x57C3, 0xB0A3, 0x57C4, 0x88A9, 0x57C5, 0x88AA, 0x57C6, 0x88AB,\n\t0x57C7, 0x88AC, 0x57C8, 0x88AD, 0x57C9, 0x88AE, 0x57CA, 0x88AF,\n\t0x57CB, 0xC2F1, 0x57CC, 0x88B0, 0x57CD, 0x88B1, 0x57CE, 0xB3C7,\n\t0x57CF, 0xDBEF, 0x57D0, 0x88B2, 0x57D1, 0x88B3, 0x57D2, 0xDBF8,\n\t0x57D3, 0x88B4, 0x57D4, 0xC6D2, 0x57D5, 0xDBF4, 0x57D6, 0x88B5,\n\t0x57D7, 0x88B6, 0x57D8, 0xDBF5, 0x57D9, 0xDBF7, 0x57DA, 0xDBF6,\n\t0x57DB, 0x88B7, 0x57DC, 0x88B8, 0x57DD, 0xDBFE, 0x57DE, 0x88B9,\n\t0x57DF, 0xD3F2, 0x57E0, 0xB2BA, 0x57E1, 0x88BA, 0x57E2, 0x88BB,\n\t0x57E3, 0x88BC, 0x57E4, 0xDBFD, 0x57E5, 0x88BD, 0x57E6, 0x88BE,\n\t0x57E7, 0x88BF, 0x57E8, 0x88C0, 0x57E9, 0x88C1, 0x57EA, 0x88C2,\n\t0x57EB, 0x88C3, 0x57EC, 0x88C4, 0x57ED, 0xDCA4, 0x57EE, 0x88C5,\n\t0x57EF, 0xDBFB, 0x57F0, 0x88C6, 0x57F1, 0x88C7, 0x57F2, 0x88C8,\n\t0x57F3, 0x88C9, 0x57F4, 0xDBFA, 0x57F5, 0x88CA, 0x57F6, 0x88CB,\n\t0x57F7, 0x88CC, 0x57F8, 0xDBFC, 0x57F9, 0xC5E0, 0x57FA, 0xBBF9,\n\t0x57FB, 0x88CD, 0x57FC, 0x88CE, 0x57FD, 0xDCA3, 0x57FE, 0x88CF,\n\t0x57FF, 0x88D0, 0x5800, 0xDCA5, 0x5801, 0x88D1, 0x5802, 0xCCC3,\n\t0x5803, 0x88D2, 0x5804, 0x88D3, 0x5805, 0x88D4, 0x5806, 0xB6D1,\n\t0x5807, 0xDDC0, 0x5808, 0x88D5, 0x5809, 0x88D6, 0x580A, 0x88D7,\n\t0x580B, 0xDCA1, 0x580C, 0x88D8, 0x580D, 0xDCA2, 0x580E, 0x88D9,\n\t0x580F, 0x88DA, 0x5810, 0x88DB, 0x5811, 0xC7B5, 0x5812, 0x88DC,\n\t0x5813, 0x88DD, 0x5814, 0x88DE, 0x5815, 0xB6E9, 0x5816, 0x88DF,\n\t0x5817, 0x88E0, 0x5818, 0x88E1, 0x5819, 0xDCA7, 0x581A, 0x88E2,\n\t0x581B, 0x88E3, 0x581C, 0x88E4, 0x581D, 0x88E5, 0x581E, 0xDCA6,\n\t0x581F, 0x88E6, 0x5820, 0xDCA9, 0x5821, 0xB1A4, 0x5822, 0x88E7,\n\t0x5823, 0x88E8, 0x5824, 0xB5CC, 0x5825, 0x88E9, 0x5826, 0x88EA,\n\t0x5827, 0x88EB, 0x5828, 0x88EC, 0x5829, 0x88ED, 0x582A, 0xBFB0,\n\t0x582B, 0x88EE, 0x582C, 0x88EF, 0x582D, 0x88F0, 0x582E, 0x88F1,\n\t0x582F, 0x88F2, 0x5830, 0xD1DF, 0x5831, 0x88F3, 0x5832, 0x88F4,\n\t0x5833, 0x88F5, 0x5834, 0x88F6, 0x5835, 0xB6C2, 0x5836, 0x88F7,\n\t0x5837, 0x88F8, 0x5838, 0x88F9, 0x5839, 0x88FA, 0x583A, 0x88FB,\n\t0x583B, 0x88FC, 0x583C, 0x88FD, 0x583D, 0x88FE, 0x583E, 0x8940,\n\t0x583F, 0x8941, 0x5840, 0x8942, 0x5841, 0x8943, 0x5842, 0x8944,\n\t0x5843, 0x8945, 0x5844, 0xDCA8, 0x5845, 0x8946, 0x5846, 0x8947,\n\t0x5847, 0x8948, 0x5848, 0x8949, 0x5849, 0x894A, 0x584A, 0x894B,\n\t0x584B, 0x894C, 0x584C, 0xCBFA, 0x584D, 0xEBF3, 0x584E, 0x894D,\n\t0x584F, 0x894E, 0x5850, 0x894F, 0x5851, 0xCBDC, 0x5852, 0x8950,\n\t0x5853, 0x8951, 0x5854, 0xCBFE, 0x5855, 0x8952, 0x5856, 0x8953,\n\t0x5857, 0x8954, 0x5858, 0xCCC1, 0x5859, 0x8955, 0x585A, 0x8956,\n\t0x585B, 0x8957, 0x585C, 0x8958, 0x585D, 0x8959, 0x585E, 0xC8FB,\n\t0x585F, 0x895A, 0x5860, 0x895B, 0x5861, 0x895C, 0x5862, 0x895D,\n\t0x5863, 0x895E, 0x5864, 0x895F, 0x5865, 0xDCAA, 0x5866, 0x8960,\n\t0x5867, 0x8961, 0x5868, 0x8962, 0x5869, 0x8963, 0x586A, 0x8964,\n\t0x586B, 0xCCEE, 0x586C, 0xDCAB, 0x586D, 0x8965, 0x586E, 0x8966,\n\t0x586F, 0x8967, 0x5870, 0x8968, 0x5871, 0x8969, 0x5872, 0x896A,\n\t0x5873, 0x896B, 0x5874, 0x896C, 0x5875, 0x896D, 0x5876, 0x896E,\n\t0x5877, 0x896F, 0x5878, 0x8970, 0x5879, 0x8971, 0x587A, 0x8972,\n\t0x587B, 0x8973, 0x587C, 0x8974, 0x587D, 0x8975, 0x587E, 0xDBD3,\n\t0x587F, 0x8976, 0x5880, 0xDCAF, 0x5881, 0xDCAC, 0x5882, 0x8977,\n\t0x5883, 0xBEB3, 0x5884, 0x8978, 0x5885, 0xCAFB, 0x5886, 0x8979,\n\t0x5887, 0x897A, 0x5888, 0x897B, 0x5889, 0xDCAD, 0x588A, 0x897C,\n\t0x588B, 0x897D, 0x588C, 0x897E, 0x588D, 0x8980, 0x588E, 0x8981,\n\t0x588F, 0x8982, 0x5890, 0x8983, 0x5891, 0x8984, 0x5892, 0xC9CA,\n\t0x5893, 0xC4B9, 0x5894, 0x8985, 0x5895, 0x8986, 0x5896, 0x8987,\n\t0x5897, 0x8988, 0x5898, 0x8989, 0x5899, 0xC7BD, 0x589A, 0xDCAE,\n\t0x589B, 0x898A, 0x589C, 0x898B, 0x589D, 0x898C, 0x589E, 0xD4F6,\n\t0x589F, 0xD0E6, 0x58A0, 0x898D, 0x58A1, 0x898E, 0x58A2, 0x898F,\n\t0x58A3, 0x8990, 0x58A4, 0x8991, 0x58A5, 0x8992, 0x58A6, 0x8993,\n\t0x58A7, 0x8994, 0x58A8, 0xC4AB, 0x58A9, 0xB6D5, 0x58AA, 0x8995,\n\t0x58AB, 0x8996, 0x58AC, 0x8997, 0x58AD, 0x8998, 0x58AE, 0x8999,\n\t0x58AF, 0x899A, 0x58B0, 0x899B, 0x58B1, 0x899C, 0x58B2, 0x899D,\n\t0x58B3, 0x899E, 0x58B4, 0x899F, 0x58B5, 0x89A0, 0x58B6, 0x89A1,\n\t0x58B7, 0x89A2, 0x58B8, 0x89A3, 0x58B9, 0x89A4, 0x58BA, 0x89A5,\n\t0x58BB, 0x89A6, 0x58BC, 0xDBD4, 0x58BD, 0x89A7, 0x58BE, 0x89A8,\n\t0x58BF, 0x89A9, 0x58C0, 0x89AA, 0x58C1, 0xB1DA, 0x58C2, 0x89AB,\n\t0x58C3, 0x89AC, 0x58C4, 0x89AD, 0x58C5, 0xDBD5, 0x58C6, 0x89AE,\n\t0x58C7, 0x89AF, 0x58C8, 0x89B0, 0x58C9, 0x89B1, 0x58CA, 0x89B2,\n\t0x58CB, 0x89B3, 0x58CC, 0x89B4, 0x58CD, 0x89B5, 0x58CE, 0x89B6,\n\t0x58CF, 0x89B7, 0x58D0, 0x89B8, 0x58D1, 0xDBD6, 0x58D2, 0x89B9,\n\t0x58D3, 0x89BA, 0x58D4, 0x89BB, 0x58D5, 0xBABE, 0x58D6, 0x89BC,\n\t0x58D7, 0x89BD, 0x58D8, 0x89BE, 0x58D9, 0x89BF, 0x58DA, 0x89C0,\n\t0x58DB, 0x89C1, 0x58DC, 0x89C2, 0x58DD, 0x89C3, 0x58DE, 0x89C4,\n\t0x58DF, 0x89C5, 0x58E0, 0x89C6, 0x58E1, 0x89C7, 0x58E2, 0x89C8,\n\t0x58E3, 0x89C9, 0x58E4, 0xC8C0, 0x58E5, 0x89CA, 0x58E6, 0x89CB,\n\t0x58E7, 0x89CC, 0x58E8, 0x89CD, 0x58E9, 0x89CE, 0x58EA, 0x89CF,\n\t0x58EB, 0xCABF, 0x58EC, 0xC8C9, 0x58ED, 0x89D0, 0x58EE, 0xD7B3,\n\t0x58EF, 0x89D1, 0x58F0, 0xC9F9, 0x58F1, 0x89D2, 0x58F2, 0x89D3,\n\t0x58F3, 0xBFC7, 0x58F4, 0x89D4, 0x58F5, 0x89D5, 0x58F6, 0xBAF8,\n\t0x58F7, 0x89D6, 0x58F8, 0x89D7, 0x58F9, 0xD2BC, 0x58FA, 0x89D8,\n\t0x58FB, 0x89D9, 0x58FC, 0x89DA, 0x58FD, 0x89DB, 0x58FE, 0x89DC,\n\t0x58FF, 0x89DD, 0x5900, 0x89DE, 0x5901, 0x89DF, 0x5902, 0xE2BA,\n\t0x5903, 0x89E0, 0x5904, 0xB4A6, 0x5905, 0x89E1, 0x5906, 0x89E2,\n\t0x5907, 0xB1B8, 0x5908, 0x89E3, 0x5909, 0x89E4, 0x590A, 0x89E5,\n\t0x590B, 0x89E6, 0x590C, 0x89E7, 0x590D, 0xB8B4, 0x590E, 0x89E8,\n\t0x590F, 0xCFC4, 0x5910, 0x89E9, 0x5911, 0x89EA, 0x5912, 0x89EB,\n\t0x5913, 0x89EC, 0x5914, 0xD9E7, 0x5915, 0xCFA6, 0x5916, 0xCDE2,\n\t0x5917, 0x89ED, 0x5918, 0x89EE, 0x5919, 0xD9ED, 0x591A, 0xB6E0,\n\t0x591B, 0x89EF, 0x591C, 0xD2B9, 0x591D, 0x89F0, 0x591E, 0x89F1,\n\t0x591F, 0xB9BB, 0x5920, 0x89F2, 0x5921, 0x89F3, 0x5922, 0x89F4,\n\t0x5923, 0x89F5, 0x5924, 0xE2B9, 0x5925, 0xE2B7, 0x5926, 0x89F6,\n\t0x5927, 0xB4F3, 0x5928, 0x89F7, 0x5929, 0xCCEC, 0x592A, 0xCCAB,\n\t0x592B, 0xB7F2, 0x592C, 0x89F8, 0x592D, 0xD8B2, 0x592E, 0xD1EB,\n\t0x592F, 0xBABB, 0x5930, 0x89F9, 0x5931, 0xCAA7, 0x5932, 0x89FA,\n\t0x5933, 0x89FB, 0x5934, 0xCDB7, 0x5935, 0x89FC, 0x5936, 0x89FD,\n\t0x5937, 0xD2C4, 0x5938, 0xBFE4, 0x5939, 0xBCD0, 0x593A, 0xB6E1,\n\t0x593B, 0x89FE, 0x593C, 0xDEC5, 0x593D, 0x8A40, 0x593E, 0x8A41,\n\t0x593F, 0x8A42, 0x5940, 0x8A43, 0x5941, 0xDEC6, 0x5942, 0xDBBC,\n\t0x5943, 0x8A44, 0x5944, 0xD1D9, 0x5945, 0x8A45, 0x5946, 0x8A46,\n\t0x5947, 0xC6E6, 0x5948, 0xC4CE, 0x5949, 0xB7EE, 0x594A, 0x8A47,\n\t0x594B, 0xB7DC, 0x594C, 0x8A48, 0x594D, 0x8A49, 0x594E, 0xBFFC,\n\t0x594F, 0xD7E0, 0x5950, 0x8A4A, 0x5951, 0xC6F5, 0x5952, 0x8A4B,\n\t0x5953, 0x8A4C, 0x5954, 0xB1BC, 0x5955, 0xDEC8, 0x5956, 0xBDB1,\n\t0x5957, 0xCCD7, 0x5958, 0xDECA, 0x5959, 0x8A4D, 0x595A, 0xDEC9,\n\t0x595B, 0x8A4E, 0x595C, 0x8A4F, 0x595D, 0x8A50, 0x595E, 0x8A51,\n\t0x595F, 0x8A52, 0x5960, 0xB5EC, 0x5961, 0x8A53, 0x5962, 0xC9DD,\n\t0x5963, 0x8A54, 0x5964, 0x8A55, 0x5965, 0xB0C2, 0x5966, 0x8A56,\n\t0x5967, 0x8A57, 0x5968, 0x8A58, 0x5969, 0x8A59, 0x596A, 0x8A5A,\n\t0x596B, 0x8A5B, 0x596C, 0x8A5C, 0x596D, 0x8A5D, 0x596E, 0x8A5E,\n\t0x596F, 0x8A5F, 0x5970, 0x8A60, 0x5971, 0x8A61, 0x5972, 0x8A62,\n\t0x5973, 0xC5AE, 0x5974, 0xC5AB, 0x5975, 0x8A63, 0x5976, 0xC4CC,\n\t0x5977, 0x8A64, 0x5978, 0xBCE9, 0x5979, 0xCBFD, 0x597A, 0x8A65,\n\t0x597B, 0x8A66, 0x597C, 0x8A67, 0x597D, 0xBAC3, 0x597E, 0x8A68,\n\t0x597F, 0x8A69, 0x5980, 0x8A6A, 0x5981, 0xE5F9, 0x5982, 0xC8E7,\n\t0x5983, 0xE5FA, 0x5984, 0xCDFD, 0x5985, 0x8A6B, 0x5986, 0xD7B1,\n\t0x5987, 0xB8BE, 0x5988, 0xC2E8, 0x5989, 0x8A6C, 0x598A, 0xC8D1,\n\t0x598B, 0x8A6D, 0x598C, 0x8A6E, 0x598D, 0xE5FB, 0x598E, 0x8A6F,\n\t0x598F, 0x8A70, 0x5990, 0x8A71, 0x5991, 0x8A72, 0x5992, 0xB6CA,\n\t0x5993, 0xBCCB, 0x5994, 0x8A73, 0x5995, 0x8A74, 0x5996, 0xD1FD,\n\t0x5997, 0xE6A1, 0x5998, 0x8A75, 0x5999, 0xC3EE, 0x599A, 0x8A76,\n\t0x599B, 0x8A77, 0x599C, 0x8A78, 0x599D, 0x8A79, 0x599E, 0xE6A4,\n\t0x599F, 0x8A7A, 0x59A0, 0x8A7B, 0x59A1, 0x8A7C, 0x59A2, 0x8A7D,\n\t0x59A3, 0xE5FE, 0x59A4, 0xE6A5, 0x59A5, 0xCDD7, 0x59A6, 0x8A7E,\n\t0x59A7, 0x8A80, 0x59A8, 0xB7C1, 0x59A9, 0xE5FC, 0x59AA, 0xE5FD,\n\t0x59AB, 0xE6A3, 0x59AC, 0x8A81, 0x59AD, 0x8A82, 0x59AE, 0xC4DD,\n\t0x59AF, 0xE6A8, 0x59B0, 0x8A83, 0x59B1, 0x8A84, 0x59B2, 0xE6A7,\n\t0x59B3, 0x8A85, 0x59B4, 0x8A86, 0x59B5, 0x8A87, 0x59B6, 0x8A88,\n\t0x59B7, 0x8A89, 0x59B8, 0x8A8A, 0x59B9, 0xC3C3, 0x59BA, 0x8A8B,\n\t0x59BB, 0xC6DE, 0x59BC, 0x8A8C, 0x59BD, 0x8A8D, 0x59BE, 0xE6AA,\n\t0x59BF, 0x8A8E, 0x59C0, 0x8A8F, 0x59C1, 0x8A90, 0x59C2, 0x8A91,\n\t0x59C3, 0x8A92, 0x59C4, 0x8A93, 0x59C5, 0x8A94, 0x59C6, 0xC4B7,\n\t0x59C7, 0x8A95, 0x59C8, 0x8A96, 0x59C9, 0x8A97, 0x59CA, 0xE6A2,\n\t0x59CB, 0xCABC, 0x59CC, 0x8A98, 0x59CD, 0x8A99, 0x59CE, 0x8A9A,\n\t0x59CF, 0x8A9B, 0x59D0, 0xBDE3, 0x59D1, 0xB9C3, 0x59D2, 0xE6A6,\n\t0x59D3, 0xD0D5, 0x59D4, 0xCEAF, 0x59D5, 0x8A9C, 0x59D6, 0x8A9D,\n\t0x59D7, 0xE6A9, 0x59D8, 0xE6B0, 0x59D9, 0x8A9E, 0x59DA, 0xD2A6,\n\t0x59DB, 0x8A9F, 0x59DC, 0xBDAA, 0x59DD, 0xE6AD, 0x59DE, 0x8AA0,\n\t0x59DF, 0x8AA1, 0x59E0, 0x8AA2, 0x59E1, 0x8AA3, 0x59E2, 0x8AA4,\n\t0x59E3, 0xE6AF, 0x59E4, 0x8AA5, 0x59E5, 0xC0D1, 0x59E6, 0x8AA6,\n\t0x59E7, 0x8AA7, 0x59E8, 0xD2CC, 0x59E9, 0x8AA8, 0x59EA, 0x8AA9,\n\t0x59EB, 0x8AAA, 0x59EC, 0xBCA7, 0x59ED, 0x8AAB, 0x59EE, 0x8AAC,\n\t0x59EF, 0x8AAD, 0x59F0, 0x8AAE, 0x59F1, 0x8AAF, 0x59F2, 0x8AB0,\n\t0x59F3, 0x8AB1, 0x59F4, 0x8AB2, 0x59F5, 0x8AB3, 0x59F6, 0x8AB4,\n\t0x59F7, 0x8AB5, 0x59F8, 0x8AB6, 0x59F9, 0xE6B1, 0x59FA, 0x8AB7,\n\t0x59FB, 0xD2F6, 0x59FC, 0x8AB8, 0x59FD, 0x8AB9, 0x59FE, 0x8ABA,\n\t0x59FF, 0xD7CB, 0x5A00, 0x8ABB, 0x5A01, 0xCDFE, 0x5A02, 0x8ABC,\n\t0x5A03, 0xCDDE, 0x5A04, 0xC2A6, 0x5A05, 0xE6AB, 0x5A06, 0xE6AC,\n\t0x5A07, 0xBDBF, 0x5A08, 0xE6AE, 0x5A09, 0xE6B3, 0x5A0A, 0x8ABD,\n\t0x5A0B, 0x8ABE, 0x5A0C, 0xE6B2, 0x5A0D, 0x8ABF, 0x5A0E, 0x8AC0,\n\t0x5A0F, 0x8AC1, 0x5A10, 0x8AC2, 0x5A11, 0xE6B6, 0x5A12, 0x8AC3,\n\t0x5A13, 0xE6B8, 0x5A14, 0x8AC4, 0x5A15, 0x8AC5, 0x5A16, 0x8AC6,\n\t0x5A17, 0x8AC7, 0x5A18, 0xC4EF, 0x5A19, 0x8AC8, 0x5A1A, 0x8AC9,\n\t0x5A1B, 0x8ACA, 0x5A1C, 0xC4C8, 0x5A1D, 0x8ACB, 0x5A1E, 0x8ACC,\n\t0x5A1F, 0xBEEA, 0x5A20, 0xC9EF, 0x5A21, 0x8ACD, 0x5A22, 0x8ACE,\n\t0x5A23, 0xE6B7, 0x5A24, 0x8ACF, 0x5A25, 0xB6F0, 0x5A26, 0x8AD0,\n\t0x5A27, 0x8AD1, 0x5A28, 0x8AD2, 0x5A29, 0xC3E4, 0x5A2A, 0x8AD3,\n\t0x5A2B, 0x8AD4, 0x5A2C, 0x8AD5, 0x5A2D, 0x8AD6, 0x5A2E, 0x8AD7,\n\t0x5A2F, 0x8AD8, 0x5A30, 0x8AD9, 0x5A31, 0xD3E9, 0x5A32, 0xE6B4,\n\t0x5A33, 0x8ADA, 0x5A34, 0xE6B5, 0x5A35, 0x8ADB, 0x5A36, 0xC8A2,\n\t0x5A37, 0x8ADC, 0x5A38, 0x8ADD, 0x5A39, 0x8ADE, 0x5A3A, 0x8ADF,\n\t0x5A3B, 0x8AE0, 0x5A3C, 0xE6BD, 0x5A3D, 0x8AE1, 0x5A3E, 0x8AE2,\n\t0x5A3F, 0x8AE3, 0x5A40, 0xE6B9, 0x5A41, 0x8AE4, 0x5A42, 0x8AE5,\n\t0x5A43, 0x8AE6, 0x5A44, 0x8AE7, 0x5A45, 0x8AE8, 0x5A46, 0xC6C5,\n\t0x5A47, 0x8AE9, 0x5A48, 0x8AEA, 0x5A49, 0xCDF1, 0x5A4A, 0xE6BB,\n\t0x5A4B, 0x8AEB, 0x5A4C, 0x8AEC, 0x5A4D, 0x8AED, 0x5A4E, 0x8AEE,\n\t0x5A4F, 0x8AEF, 0x5A50, 0x8AF0, 0x5A51, 0x8AF1, 0x5A52, 0x8AF2,\n\t0x5A53, 0x8AF3, 0x5A54, 0x8AF4, 0x5A55, 0xE6BC, 0x5A56, 0x8AF5,\n\t0x5A57, 0x8AF6, 0x5A58, 0x8AF7, 0x5A59, 0x8AF8, 0x5A5A, 0xBBE9,\n\t0x5A5B, 0x8AF9, 0x5A5C, 0x8AFA, 0x5A5D, 0x8AFB, 0x5A5E, 0x8AFC,\n\t0x5A5F, 0x8AFD, 0x5A60, 0x8AFE, 0x5A61, 0x8B40, 0x5A62, 0xE6BE,\n\t0x5A63, 0x8B41, 0x5A64, 0x8B42, 0x5A65, 0x8B43, 0x5A66, 0x8B44,\n\t0x5A67, 0xE6BA, 0x5A68, 0x8B45, 0x5A69, 0x8B46, 0x5A6A, 0xC0B7,\n\t0x5A6B, 0x8B47, 0x5A6C, 0x8B48, 0x5A6D, 0x8B49, 0x5A6E, 0x8B4A,\n\t0x5A6F, 0x8B4B, 0x5A70, 0x8B4C, 0x5A71, 0x8B4D, 0x5A72, 0x8B4E,\n\t0x5A73, 0x8B4F, 0x5A74, 0xD3A4, 0x5A75, 0xE6BF, 0x5A76, 0xC9F4,\n\t0x5A77, 0xE6C3, 0x5A78, 0x8B50, 0x5A79, 0x8B51, 0x5A7A, 0xE6C4,\n\t0x5A7B, 0x8B52, 0x5A7C, 0x8B53, 0x5A7D, 0x8B54, 0x5A7E, 0x8B55,\n\t0x5A7F, 0xD0F6, 0x5A80, 0x8B56, 0x5A81, 0x8B57, 0x5A82, 0x8B58,\n\t0x5A83, 0x8B59, 0x5A84, 0x8B5A, 0x5A85, 0x8B5B, 0x5A86, 0x8B5C,\n\t0x5A87, 0x8B5D, 0x5A88, 0x8B5E, 0x5A89, 0x8B5F, 0x5A8A, 0x8B60,\n\t0x5A8B, 0x8B61, 0x5A8C, 0x8B62, 0x5A8D, 0x8B63, 0x5A8E, 0x8B64,\n\t0x5A8F, 0x8B65, 0x5A90, 0x8B66, 0x5A91, 0x8B67, 0x5A92, 0xC3BD,\n\t0x5A93, 0x8B68, 0x5A94, 0x8B69, 0x5A95, 0x8B6A, 0x5A96, 0x8B6B,\n\t0x5A97, 0x8B6C, 0x5A98, 0x8B6D, 0x5A99, 0x8B6E, 0x5A9A, 0xC3C4,\n\t0x5A9B, 0xE6C2, 0x5A9C, 0x8B6F, 0x5A9D, 0x8B70, 0x5A9E, 0x8B71,\n\t0x5A9F, 0x8B72, 0x5AA0, 0x8B73, 0x5AA1, 0x8B74, 0x5AA2, 0x8B75,\n\t0x5AA3, 0x8B76, 0x5AA4, 0x8B77, 0x5AA5, 0x8B78, 0x5AA6, 0x8B79,\n\t0x5AA7, 0x8B7A, 0x5AA8, 0x8B7B, 0x5AA9, 0x8B7C, 0x5AAA, 0xE6C1,\n\t0x5AAB, 0x8B7D, 0x5AAC, 0x8B7E, 0x5AAD, 0x8B80, 0x5AAE, 0x8B81,\n\t0x5AAF, 0x8B82, 0x5AB0, 0x8B83, 0x5AB1, 0x8B84, 0x5AB2, 0xE6C7,\n\t0x5AB3, 0xCFB1, 0x5AB4, 0x8B85, 0x5AB5, 0xEBF4, 0x5AB6, 0x8B86,\n\t0x5AB7, 0x8B87, 0x5AB8, 0xE6CA, 0x5AB9, 0x8B88, 0x5ABA, 0x8B89,\n\t0x5ABB, 0x8B8A, 0x5ABC, 0x8B8B, 0x5ABD, 0x8B8C, 0x5ABE, 0xE6C5,\n\t0x5ABF, 0x8B8D, 0x5AC0, 0x8B8E, 0x5AC1, 0xBCDE, 0x5AC2, 0xC9A9,\n\t0x5AC3, 0x8B8F, 0x5AC4, 0x8B90, 0x5AC5, 0x8B91, 0x5AC6, 0x8B92,\n\t0x5AC7, 0x8B93, 0x5AC8, 0x8B94, 0x5AC9, 0xBCB5, 0x5ACA, 0x8B95,\n\t0x5ACB, 0x8B96, 0x5ACC, 0xCFD3, 0x5ACD, 0x8B97, 0x5ACE, 0x8B98,\n\t0x5ACF, 0x8B99, 0x5AD0, 0x8B9A, 0x5AD1, 0x8B9B, 0x5AD2, 0xE6C8,\n\t0x5AD3, 0x8B9C, 0x5AD4, 0xE6C9, 0x5AD5, 0x8B9D, 0x5AD6, 0xE6CE,\n\t0x5AD7, 0x8B9E, 0x5AD8, 0xE6D0, 0x5AD9, 0x8B9F, 0x5ADA, 0x8BA0,\n\t0x5ADB, 0x8BA1, 0x5ADC, 0xE6D1, 0x5ADD, 0x8BA2, 0x5ADE, 0x8BA3,\n\t0x5ADF, 0x8BA4, 0x5AE0, 0xE6CB, 0x5AE1, 0xB5D5, 0x5AE2, 0x8BA5,\n\t0x5AE3, 0xE6CC, 0x5AE4, 0x8BA6, 0x5AE5, 0x8BA7, 0x5AE6, 0xE6CF,\n\t0x5AE7, 0x8BA8, 0x5AE8, 0x8BA9, 0x5AE9, 0xC4DB, 0x5AEA, 0x8BAA,\n\t0x5AEB, 0xE6C6, 0x5AEC, 0x8BAB, 0x5AED, 0x8BAC, 0x5AEE, 0x8BAD,\n\t0x5AEF, 0x8BAE, 0x5AF0, 0x8BAF, 0x5AF1, 0xE6CD, 0x5AF2, 0x8BB0,\n\t0x5AF3, 0x8BB1, 0x5AF4, 0x8BB2, 0x5AF5, 0x8BB3, 0x5AF6, 0x8BB4,\n\t0x5AF7, 0x8BB5, 0x5AF8, 0x8BB6, 0x5AF9, 0x8BB7, 0x5AFA, 0x8BB8,\n\t0x5AFB, 0x8BB9, 0x5AFC, 0x8BBA, 0x5AFD, 0x8BBB, 0x5AFE, 0x8BBC,\n\t0x5AFF, 0x8BBD, 0x5B00, 0x8BBE, 0x5B01, 0x8BBF, 0x5B02, 0x8BC0,\n\t0x5B03, 0x8BC1, 0x5B04, 0x8BC2, 0x5B05, 0x8BC3, 0x5B06, 0x8BC4,\n\t0x5B07, 0x8BC5, 0x5B08, 0x8BC6, 0x5B09, 0xE6D2, 0x5B0A, 0x8BC7,\n\t0x5B0B, 0x8BC8, 0x5B0C, 0x8BC9, 0x5B0D, 0x8BCA, 0x5B0E, 0x8BCB,\n\t0x5B0F, 0x8BCC, 0x5B10, 0x8BCD, 0x5B11, 0x8BCE, 0x5B12, 0x8BCF,\n\t0x5B13, 0x8BD0, 0x5B14, 0x8BD1, 0x5B15, 0x8BD2, 0x5B16, 0xE6D4,\n\t0x5B17, 0xE6D3, 0x5B18, 0x8BD3, 0x5B19, 0x8BD4, 0x5B1A, 0x8BD5,\n\t0x5B1B, 0x8BD6, 0x5B1C, 0x8BD7, 0x5B1D, 0x8BD8, 0x5B1E, 0x8BD9,\n\t0x5B1F, 0x8BDA, 0x5B20, 0x8BDB, 0x5B21, 0x8BDC, 0x5B22, 0x8BDD,\n\t0x5B23, 0x8BDE, 0x5B24, 0x8BDF, 0x5B25, 0x8BE0, 0x5B26, 0x8BE1,\n\t0x5B27, 0x8BE2, 0x5B28, 0x8BE3, 0x5B29, 0x8BE4, 0x5B2A, 0x8BE5,\n\t0x5B2B, 0x8BE6, 0x5B2C, 0x8BE7, 0x5B2D, 0x8BE8, 0x5B2E, 0x8BE9,\n\t0x5B2F, 0x8BEA, 0x5B30, 0x8BEB, 0x5B31, 0x8BEC, 0x5B32, 0xE6D5,\n\t0x5B33, 0x8BED, 0x5B34, 0xD9F8, 0x5B35, 0x8BEE, 0x5B36, 0x8BEF,\n\t0x5B37, 0xE6D6, 0x5B38, 0x8BF0, 0x5B39, 0x8BF1, 0x5B3A, 0x8BF2,\n\t0x5B3B, 0x8BF3, 0x5B3C, 0x8BF4, 0x5B3D, 0x8BF5, 0x5B3E, 0x8BF6,\n\t0x5B3F, 0x8BF7, 0x5B40, 0xE6D7, 0x5B41, 0x8BF8, 0x5B42, 0x8BF9,\n\t0x5B43, 0x8BFA, 0x5B44, 0x8BFB, 0x5B45, 0x8BFC, 0x5B46, 0x8BFD,\n\t0x5B47, 0x8BFE, 0x5B48, 0x8C40, 0x5B49, 0x8C41, 0x5B4A, 0x8C42,\n\t0x5B4B, 0x8C43, 0x5B4C, 0x8C44, 0x5B4D, 0x8C45, 0x5B4E, 0x8C46,\n\t0x5B4F, 0x8C47, 0x5B50, 0xD7D3, 0x5B51, 0xE6DD, 0x5B52, 0x8C48,\n\t0x5B53, 0xE6DE, 0x5B54, 0xBFD7, 0x5B55, 0xD4D0, 0x5B56, 0x8C49,\n\t0x5B57, 0xD7D6, 0x5B58, 0xB4E6, 0x5B59, 0xCBEF, 0x5B5A, 0xE6DA,\n\t0x5B5B, 0xD8C3, 0x5B5C, 0xD7CE, 0x5B5D, 0xD0A2, 0x5B5E, 0x8C4A,\n\t0x5B5F, 0xC3CF, 0x5B60, 0x8C4B, 0x5B61, 0x8C4C, 0x5B62, 0xE6DF,\n\t0x5B63, 0xBCBE, 0x5B64, 0xB9C2, 0x5B65, 0xE6DB, 0x5B66, 0xD1A7,\n\t0x5B67, 0x8C4D, 0x5B68, 0x8C4E, 0x5B69, 0xBAA2, 0x5B6A, 0xC2CF,\n\t0x5B6B, 0x8C4F, 0x5B6C, 0xD8AB, 0x5B6D, 0x8C50, 0x5B6E, 0x8C51,\n\t0x5B6F, 0x8C52, 0x5B70, 0xCAEB, 0x5B71, 0xE5EE, 0x5B72, 0x8C53,\n\t0x5B73, 0xE6DC, 0x5B74, 0x8C54, 0x5B75, 0xB7F5, 0x5B76, 0x8C55,\n\t0x5B77, 0x8C56, 0x5B78, 0x8C57, 0x5B79, 0x8C58, 0x5B7A, 0xC8E6,\n\t0x5B7B, 0x8C59, 0x5B7C, 0x8C5A, 0x5B7D, 0xC4F5, 0x5B7E, 0x8C5B,\n\t0x5B7F, 0x8C5C, 0x5B80, 0xE5B2, 0x5B81, 0xC4FE, 0x5B82, 0x8C5D,\n\t0x5B83, 0xCBFC, 0x5B84, 0xE5B3, 0x5B85, 0xD5AC, 0x5B86, 0x8C5E,\n\t0x5B87, 0xD3EE, 0x5B88, 0xCAD8, 0x5B89, 0xB0B2, 0x5B8A, 0x8C5F,\n\t0x5B8B, 0xCBCE, 0x5B8C, 0xCDEA, 0x5B8D, 0x8C60, 0x5B8E, 0x8C61,\n\t0x5B8F, 0xBAEA, 0x5B90, 0x8C62, 0x5B91, 0x8C63, 0x5B92, 0x8C64,\n\t0x5B93, 0xE5B5, 0x5B94, 0x8C65, 0x5B95, 0xE5B4, 0x5B96, 0x8C66,\n\t0x5B97, 0xD7DA, 0x5B98, 0xB9D9, 0x5B99, 0xD6E6, 0x5B9A, 0xB6A8,\n\t0x5B9B, 0xCDF0, 0x5B9C, 0xD2CB, 0x5B9D, 0xB1A6, 0x5B9E, 0xCAB5,\n\t0x5B9F, 0x8C67, 0x5BA0, 0xB3E8, 0x5BA1, 0xC9F3, 0x5BA2, 0xBFCD,\n\t0x5BA3, 0xD0FB, 0x5BA4, 0xCAD2, 0x5BA5, 0xE5B6, 0x5BA6, 0xBBC2,\n\t0x5BA7, 0x8C68, 0x5BA8, 0x8C69, 0x5BA9, 0x8C6A, 0x5BAA, 0xCFDC,\n\t0x5BAB, 0xB9AC, 0x5BAC, 0x8C6B, 0x5BAD, 0x8C6C, 0x5BAE, 0x8C6D,\n\t0x5BAF, 0x8C6E, 0x5BB0, 0xD4D7, 0x5BB1, 0x8C6F, 0x5BB2, 0x8C70,\n\t0x5BB3, 0xBAA6, 0x5BB4, 0xD1E7, 0x5BB5, 0xCFFC, 0x5BB6, 0xBCD2,\n\t0x5BB7, 0x8C71, 0x5BB8, 0xE5B7, 0x5BB9, 0xC8DD, 0x5BBA, 0x8C72,\n\t0x5BBB, 0x8C73, 0x5BBC, 0x8C74, 0x5BBD, 0xBFED, 0x5BBE, 0xB1F6,\n\t0x5BBF, 0xCBDE, 0x5BC0, 0x8C75, 0x5BC1, 0x8C76, 0x5BC2, 0xBCC5,\n\t0x5BC3, 0x8C77, 0x5BC4, 0xBCC4, 0x5BC5, 0xD2FA, 0x5BC6, 0xC3DC,\n\t0x5BC7, 0xBFDC, 0x5BC8, 0x8C78, 0x5BC9, 0x8C79, 0x5BCA, 0x8C7A,\n\t0x5BCB, 0x8C7B, 0x5BCC, 0xB8BB, 0x5BCD, 0x8C7C, 0x5BCE, 0x8C7D,\n\t0x5BCF, 0x8C7E, 0x5BD0, 0xC3C2, 0x5BD1, 0x8C80, 0x5BD2, 0xBAAE,\n\t0x5BD3, 0xD4A2, 0x5BD4, 0x8C81, 0x5BD5, 0x8C82, 0x5BD6, 0x8C83,\n\t0x5BD7, 0x8C84, 0x5BD8, 0x8C85, 0x5BD9, 0x8C86, 0x5BDA, 0x8C87,\n\t0x5BDB, 0x8C88, 0x5BDC, 0x8C89, 0x5BDD, 0xC7DE, 0x5BDE, 0xC4AF,\n\t0x5BDF, 0xB2EC, 0x5BE0, 0x8C8A, 0x5BE1, 0xB9D1, 0x5BE2, 0x8C8B,\n\t0x5BE3, 0x8C8C, 0x5BE4, 0xE5BB, 0x5BE5, 0xC1C8, 0x5BE6, 0x8C8D,\n\t0x5BE7, 0x8C8E, 0x5BE8, 0xD5AF, 0x5BE9, 0x8C8F, 0x5BEA, 0x8C90,\n\t0x5BEB, 0x8C91, 0x5BEC, 0x8C92, 0x5BED, 0x8C93, 0x5BEE, 0xE5BC,\n\t0x5BEF, 0x8C94, 0x5BF0, 0xE5BE, 0x5BF1, 0x8C95, 0x5BF2, 0x8C96,\n\t0x5BF3, 0x8C97, 0x5BF4, 0x8C98, 0x5BF5, 0x8C99, 0x5BF6, 0x8C9A,\n\t0x5BF7, 0x8C9B, 0x5BF8, 0xB4E7, 0x5BF9, 0xB6D4, 0x5BFA, 0xCBC2,\n\t0x5BFB, 0xD1B0, 0x5BFC, 0xB5BC, 0x5BFD, 0x8C9C, 0x5BFE, 0x8C9D,\n\t0x5BFF, 0xCAD9, 0x5C00, 0x8C9E, 0x5C01, 0xB7E2, 0x5C02, 0x8C9F,\n\t0x5C03, 0x8CA0, 0x5C04, 0xC9E4, 0x5C05, 0x8CA1, 0x5C06, 0xBDAB,\n\t0x5C07, 0x8CA2, 0x5C08, 0x8CA3, 0x5C09, 0xCEBE, 0x5C0A, 0xD7F0,\n\t0x5C0B, 0x8CA4, 0x5C0C, 0x8CA5, 0x5C0D, 0x8CA6, 0x5C0E, 0x8CA7,\n\t0x5C0F, 0xD0A1, 0x5C10, 0x8CA8, 0x5C11, 0xC9D9, 0x5C12, 0x8CA9,\n\t0x5C13, 0x8CAA, 0x5C14, 0xB6FB, 0x5C15, 0xE6D8, 0x5C16, 0xBCE2,\n\t0x5C17, 0x8CAB, 0x5C18, 0xB3BE, 0x5C19, 0x8CAC, 0x5C1A, 0xC9D0,\n\t0x5C1B, 0x8CAD, 0x5C1C, 0xE6D9, 0x5C1D, 0xB3A2, 0x5C1E, 0x8CAE,\n\t0x5C1F, 0x8CAF, 0x5C20, 0x8CB0, 0x5C21, 0x8CB1, 0x5C22, 0xDECC,\n\t0x5C23, 0x8CB2, 0x5C24, 0xD3C8, 0x5C25, 0xDECD, 0x5C26, 0x8CB3,\n\t0x5C27, 0xD2A2, 0x5C28, 0x8CB4, 0x5C29, 0x8CB5, 0x5C2A, 0x8CB6,\n\t0x5C2B, 0x8CB7, 0x5C2C, 0xDECE, 0x5C2D, 0x8CB8, 0x5C2E, 0x8CB9,\n\t0x5C2F, 0x8CBA, 0x5C30, 0x8CBB, 0x5C31, 0xBECD, 0x5C32, 0x8CBC,\n\t0x5C33, 0x8CBD, 0x5C34, 0xDECF, 0x5C35, 0x8CBE, 0x5C36, 0x8CBF,\n\t0x5C37, 0x8CC0, 0x5C38, 0xCAAC, 0x5C39, 0xD2FC, 0x5C3A, 0xB3DF,\n\t0x5C3B, 0xE5EA, 0x5C3C, 0xC4E1, 0x5C3D, 0xBEA1, 0x5C3E, 0xCEB2,\n\t0x5C3F, 0xC4F2, 0x5C40, 0xBED6, 0x5C41, 0xC6A8, 0x5C42, 0xB2E3,\n\t0x5C43, 0x8CC1, 0x5C44, 0x8CC2, 0x5C45, 0xBED3, 0x5C46, 0x8CC3,\n\t0x5C47, 0x8CC4, 0x5C48, 0xC7FC, 0x5C49, 0xCCEB, 0x5C4A, 0xBDEC,\n\t0x5C4B, 0xCEDD, 0x5C4C, 0x8CC5, 0x5C4D, 0x8CC6, 0x5C4E, 0xCABA,\n\t0x5C4F, 0xC6C1, 0x5C50, 0xE5EC, 0x5C51, 0xD0BC, 0x5C52, 0x8CC7,\n\t0x5C53, 0x8CC8, 0x5C54, 0x8CC9, 0x5C55, 0xD5B9, 0x5C56, 0x8CCA,\n\t0x5C57, 0x8CCB, 0x5C58, 0x8CCC, 0x5C59, 0xE5ED, 0x5C5A, 0x8CCD,\n\t0x5C5B, 0x8CCE, 0x5C5C, 0x8CCF, 0x5C5D, 0x8CD0, 0x5C5E, 0xCAF4,\n\t0x5C5F, 0x8CD1, 0x5C60, 0xCDC0, 0x5C61, 0xC2C5, 0x5C62, 0x8CD2,\n\t0x5C63, 0xE5EF, 0x5C64, 0x8CD3, 0x5C65, 0xC2C4, 0x5C66, 0xE5F0,\n\t0x5C67, 0x8CD4, 0x5C68, 0x8CD5, 0x5C69, 0x8CD6, 0x5C6A, 0x8CD7,\n\t0x5C6B, 0x8CD8, 0x5C6C, 0x8CD9, 0x5C6D, 0x8CDA, 0x5C6E, 0xE5F8,\n\t0x5C6F, 0xCDCD, 0x5C70, 0x8CDB, 0x5C71, 0xC9BD, 0x5C72, 0x8CDC,\n\t0x5C73, 0x8CDD, 0x5C74, 0x8CDE, 0x5C75, 0x8CDF, 0x5C76, 0x8CE0,\n\t0x5C77, 0x8CE1, 0x5C78, 0x8CE2, 0x5C79, 0xD2D9, 0x5C7A, 0xE1A8,\n\t0x5C7B, 0x8CE3, 0x5C7C, 0x8CE4, 0x5C7D, 0x8CE5, 0x5C7E, 0x8CE6,\n\t0x5C7F, 0xD3EC, 0x5C80, 0x8CE7, 0x5C81, 0xCBEA, 0x5C82, 0xC6F1,\n\t0x5C83, 0x8CE8, 0x5C84, 0x8CE9, 0x5C85, 0x8CEA, 0x5C86, 0x8CEB,\n\t0x5C87, 0x8CEC, 0x5C88, 0xE1AC, 0x5C89, 0x8CED, 0x5C8A, 0x8CEE,\n\t0x5C8B, 0x8CEF, 0x5C8C, 0xE1A7, 0x5C8D, 0xE1A9, 0x5C8E, 0x8CF0,\n\t0x5C8F, 0x8CF1, 0x5C90, 0xE1AA, 0x5C91, 0xE1AF, 0x5C92, 0x8CF2,\n\t0x5C93, 0x8CF3, 0x5C94, 0xB2ED, 0x5C95, 0x8CF4, 0x5C96, 0xE1AB,\n\t0x5C97, 0xB8DA, 0x5C98, 0xE1AD, 0x5C99, 0xE1AE, 0x5C9A, 0xE1B0,\n\t0x5C9B, 0xB5BA, 0x5C9C, 0xE1B1, 0x5C9D, 0x8CF5, 0x5C9E, 0x8CF6,\n\t0x5C9F, 0x8CF7, 0x5CA0, 0x8CF8, 0x5CA1, 0x8CF9, 0x5CA2, 0xE1B3,\n\t0x5CA3, 0xE1B8, 0x5CA4, 0x8CFA, 0x5CA5, 0x8CFB, 0x5CA6, 0x8CFC,\n\t0x5CA7, 0x8CFD, 0x5CA8, 0x8CFE, 0x5CA9, 0xD1D2, 0x5CAA, 0x8D40,\n\t0x5CAB, 0xE1B6, 0x5CAC, 0xE1B5, 0x5CAD, 0xC1EB, 0x5CAE, 0x8D41,\n\t0x5CAF, 0x8D42, 0x5CB0, 0x8D43, 0x5CB1, 0xE1B7, 0x5CB2, 0x8D44,\n\t0x5CB3, 0xD4C0, 0x5CB4, 0x8D45, 0x5CB5, 0xE1B2, 0x5CB6, 0x8D46,\n\t0x5CB7, 0xE1BA, 0x5CB8, 0xB0B6, 0x5CB9, 0x8D47, 0x5CBA, 0x8D48,\n\t0x5CBB, 0x8D49, 0x5CBC, 0x8D4A, 0x5CBD, 0xE1B4, 0x5CBE, 0x8D4B,\n\t0x5CBF, 0xBFF9, 0x5CC0, 0x8D4C, 0x5CC1, 0xE1B9, 0x5CC2, 0x8D4D,\n\t0x5CC3, 0x8D4E, 0x5CC4, 0xE1BB, 0x5CC5, 0x8D4F, 0x5CC6, 0x8D50,\n\t0x5CC7, 0x8D51, 0x5CC8, 0x8D52, 0x5CC9, 0x8D53, 0x5CCA, 0x8D54,\n\t0x5CCB, 0xE1BE, 0x5CCC, 0x8D55, 0x5CCD, 0x8D56, 0x5CCE, 0x8D57,\n\t0x5CCF, 0x8D58, 0x5CD0, 0x8D59, 0x5CD1, 0x8D5A, 0x5CD2, 0xE1BC,\n\t0x5CD3, 0x8D5B, 0x5CD4, 0x8D5C, 0x5CD5, 0x8D5D, 0x5CD6, 0x8D5E,\n\t0x5CD7, 0x8D5F, 0x5CD8, 0x8D60, 0x5CD9, 0xD6C5, 0x5CDA, 0x8D61,\n\t0x5CDB, 0x8D62, 0x5CDC, 0x8D63, 0x5CDD, 0x8D64, 0x5CDE, 0x8D65,\n\t0x5CDF, 0x8D66, 0x5CE0, 0x8D67, 0x5CE1, 0xCFBF, 0x5CE2, 0x8D68,\n\t0x5CE3, 0x8D69, 0x5CE4, 0xE1BD, 0x5CE5, 0xE1BF, 0x5CE6, 0xC2CD,\n\t0x5CE7, 0x8D6A, 0x5CE8, 0xB6EB, 0x5CE9, 0x8D6B, 0x5CEA, 0xD3F8,\n\t0x5CEB, 0x8D6C, 0x5CEC, 0x8D6D, 0x5CED, 0xC7CD, 0x5CEE, 0x8D6E,\n\t0x5CEF, 0x8D6F, 0x5CF0, 0xB7E5, 0x5CF1, 0x8D70, 0x5CF2, 0x8D71,\n\t0x5CF3, 0x8D72, 0x5CF4, 0x8D73, 0x5CF5, 0x8D74, 0x5CF6, 0x8D75,\n\t0x5CF7, 0x8D76, 0x5CF8, 0x8D77, 0x5CF9, 0x8D78, 0x5CFA, 0x8D79,\n\t0x5CFB, 0xBEFE, 0x5CFC, 0x8D7A, 0x5CFD, 0x8D7B, 0x5CFE, 0x8D7C,\n\t0x5CFF, 0x8D7D, 0x5D00, 0x8D7E, 0x5D01, 0x8D80, 0x5D02, 0xE1C0,\n\t0x5D03, 0xE1C1, 0x5D04, 0x8D81, 0x5D05, 0x8D82, 0x5D06, 0xE1C7,\n\t0x5D07, 0xB3E7, 0x5D08, 0x8D83, 0x5D09, 0x8D84, 0x5D0A, 0x8D85,\n\t0x5D0B, 0x8D86, 0x5D0C, 0x8D87, 0x5D0D, 0x8D88, 0x5D0E, 0xC6E9,\n\t0x5D0F, 0x8D89, 0x5D10, 0x8D8A, 0x5D11, 0x8D8B, 0x5D12, 0x8D8C,\n\t0x5D13, 0x8D8D, 0x5D14, 0xB4DE, 0x5D15, 0x8D8E, 0x5D16, 0xD1C2,\n\t0x5D17, 0x8D8F, 0x5D18, 0x8D90, 0x5D19, 0x8D91, 0x5D1A, 0x8D92,\n\t0x5D1B, 0xE1C8, 0x5D1C, 0x8D93, 0x5D1D, 0x8D94, 0x5D1E, 0xE1C6,\n\t0x5D1F, 0x8D95, 0x5D20, 0x8D96, 0x5D21, 0x8D97, 0x5D22, 0x8D98,\n\t0x5D23, 0x8D99, 0x5D24, 0xE1C5, 0x5D25, 0x8D9A, 0x5D26, 0xE1C3,\n\t0x5D27, 0xE1C2, 0x5D28, 0x8D9B, 0x5D29, 0xB1C0, 0x5D2A, 0x8D9C,\n\t0x5D2B, 0x8D9D, 0x5D2C, 0x8D9E, 0x5D2D, 0xD5B8, 0x5D2E, 0xE1C4,\n\t0x5D2F, 0x8D9F, 0x5D30, 0x8DA0, 0x5D31, 0x8DA1, 0x5D32, 0x8DA2,\n\t0x5D33, 0x8DA3, 0x5D34, 0xE1CB, 0x5D35, 0x8DA4, 0x5D36, 0x8DA5,\n\t0x5D37, 0x8DA6, 0x5D38, 0x8DA7, 0x5D39, 0x8DA8, 0x5D3A, 0x8DA9,\n\t0x5D3B, 0x8DAA, 0x5D3C, 0x8DAB, 0x5D3D, 0xE1CC, 0x5D3E, 0xE1CA,\n\t0x5D3F, 0x8DAC, 0x5D40, 0x8DAD, 0x5D41, 0x8DAE, 0x5D42, 0x8DAF,\n\t0x5D43, 0x8DB0, 0x5D44, 0x8DB1, 0x5D45, 0x8DB2, 0x5D46, 0x8DB3,\n\t0x5D47, 0xEFFA, 0x5D48, 0x8DB4, 0x5D49, 0x8DB5, 0x5D4A, 0xE1D3,\n\t0x5D4B, 0xE1D2, 0x5D4C, 0xC7B6, 0x5D4D, 0x8DB6, 0x5D4E, 0x8DB7,\n\t0x5D4F, 0x8DB8, 0x5D50, 0x8DB9, 0x5D51, 0x8DBA, 0x5D52, 0x8DBB,\n\t0x5D53, 0x8DBC, 0x5D54, 0x8DBD, 0x5D55, 0x8DBE, 0x5D56, 0x8DBF,\n\t0x5D57, 0x8DC0, 0x5D58, 0xE1C9, 0x5D59, 0x8DC1, 0x5D5A, 0x8DC2,\n\t0x5D5B, 0xE1CE, 0x5D5C, 0x8DC3, 0x5D5D, 0xE1D0, 0x5D5E, 0x8DC4,\n\t0x5D5F, 0x8DC5, 0x5D60, 0x8DC6, 0x5D61, 0x8DC7, 0x5D62, 0x8DC8,\n\t0x5D63, 0x8DC9, 0x5D64, 0x8DCA, 0x5D65, 0x8DCB, 0x5D66, 0x8DCC,\n\t0x5D67, 0x8DCD, 0x5D68, 0x8DCE, 0x5D69, 0xE1D4, 0x5D6A, 0x8DCF,\n\t0x5D6B, 0xE1D1, 0x5D6C, 0xE1CD, 0x5D6D, 0x8DD0, 0x5D6E, 0x8DD1,\n\t0x5D6F, 0xE1CF, 0x5D70, 0x8DD2, 0x5D71, 0x8DD3, 0x5D72, 0x8DD4,\n\t0x5D73, 0x8DD5, 0x5D74, 0xE1D5, 0x5D75, 0x8DD6, 0x5D76, 0x8DD7,\n\t0x5D77, 0x8DD8, 0x5D78, 0x8DD9, 0x5D79, 0x8DDA, 0x5D7A, 0x8DDB,\n\t0x5D7B, 0x8DDC, 0x5D7C, 0x8DDD, 0x5D7D, 0x8DDE, 0x5D7E, 0x8DDF,\n\t0x5D7F, 0x8DE0, 0x5D80, 0x8DE1, 0x5D81, 0x8DE2, 0x5D82, 0xE1D6,\n\t0x5D83, 0x8DE3, 0x5D84, 0x8DE4, 0x5D85, 0x8DE5, 0x5D86, 0x8DE6,\n\t0x5D87, 0x8DE7, 0x5D88, 0x8DE8, 0x5D89, 0x8DE9, 0x5D8A, 0x8DEA,\n\t0x5D8B, 0x8DEB, 0x5D8C, 0x8DEC, 0x5D8D, 0x8DED, 0x5D8E, 0x8DEE,\n\t0x5D8F, 0x8DEF, 0x5D90, 0x8DF0, 0x5D91, 0x8DF1, 0x5D92, 0x8DF2,\n\t0x5D93, 0x8DF3, 0x5D94, 0x8DF4, 0x5D95, 0x8DF5, 0x5D96, 0x8DF6,\n\t0x5D97, 0x8DF7, 0x5D98, 0x8DF8, 0x5D99, 0xE1D7, 0x5D9A, 0x8DF9,\n\t0x5D9B, 0x8DFA, 0x5D9C, 0x8DFB, 0x5D9D, 0xE1D8, 0x5D9E, 0x8DFC,\n\t0x5D9F, 0x8DFD, 0x5DA0, 0x8DFE, 0x5DA1, 0x8E40, 0x5DA2, 0x8E41,\n\t0x5DA3, 0x8E42, 0x5DA4, 0x8E43, 0x5DA5, 0x8E44, 0x5DA6, 0x8E45,\n\t0x5DA7, 0x8E46, 0x5DA8, 0x8E47, 0x5DA9, 0x8E48, 0x5DAA, 0x8E49,\n\t0x5DAB, 0x8E4A, 0x5DAC, 0x8E4B, 0x5DAD, 0x8E4C, 0x5DAE, 0x8E4D,\n\t0x5DAF, 0x8E4E, 0x5DB0, 0x8E4F, 0x5DB1, 0x8E50, 0x5DB2, 0x8E51,\n\t0x5DB3, 0x8E52, 0x5DB4, 0x8E53, 0x5DB5, 0x8E54, 0x5DB6, 0x8E55,\n\t0x5DB7, 0xE1DA, 0x5DB8, 0x8E56, 0x5DB9, 0x8E57, 0x5DBA, 0x8E58,\n\t0x5DBB, 0x8E59, 0x5DBC, 0x8E5A, 0x5DBD, 0x8E5B, 0x5DBE, 0x8E5C,\n\t0x5DBF, 0x8E5D, 0x5DC0, 0x8E5E, 0x5DC1, 0x8E5F, 0x5DC2, 0x8E60,\n\t0x5DC3, 0x8E61, 0x5DC4, 0x8E62, 0x5DC5, 0xE1DB, 0x5DC6, 0x8E63,\n\t0x5DC7, 0x8E64, 0x5DC8, 0x8E65, 0x5DC9, 0x8E66, 0x5DCA, 0x8E67,\n\t0x5DCB, 0x8E68, 0x5DCC, 0x8E69, 0x5DCD, 0xCEA1, 0x5DCE, 0x8E6A,\n\t0x5DCF, 0x8E6B, 0x5DD0, 0x8E6C, 0x5DD1, 0x8E6D, 0x5DD2, 0x8E6E,\n\t0x5DD3, 0x8E6F, 0x5DD4, 0x8E70, 0x5DD5, 0x8E71, 0x5DD6, 0x8E72,\n\t0x5DD7, 0x8E73, 0x5DD8, 0x8E74, 0x5DD9, 0x8E75, 0x5DDA, 0x8E76,\n\t0x5DDB, 0xE7DD, 0x5DDC, 0x8E77, 0x5DDD, 0xB4A8, 0x5DDE, 0xD6DD,\n\t0x5DDF, 0x8E78, 0x5DE0, 0x8E79, 0x5DE1, 0xD1B2, 0x5DE2, 0xB3B2,\n\t0x5DE3, 0x8E7A, 0x5DE4, 0x8E7B, 0x5DE5, 0xB9A4, 0x5DE6, 0xD7F3,\n\t0x5DE7, 0xC7C9, 0x5DE8, 0xBEDE, 0x5DE9, 0xB9AE, 0x5DEA, 0x8E7C,\n\t0x5DEB, 0xCED7, 0x5DEC, 0x8E7D, 0x5DED, 0x8E7E, 0x5DEE, 0xB2EE,\n\t0x5DEF, 0xDBCF, 0x5DF0, 0x8E80, 0x5DF1, 0xBCBA, 0x5DF2, 0xD2D1,\n\t0x5DF3, 0xCBC8, 0x5DF4, 0xB0CD, 0x5DF5, 0x8E81, 0x5DF6, 0x8E82,\n\t0x5DF7, 0xCFEF, 0x5DF8, 0x8E83, 0x5DF9, 0x8E84, 0x5DFA, 0x8E85,\n\t0x5DFB, 0x8E86, 0x5DFC, 0x8E87, 0x5DFD, 0xD9E3, 0x5DFE, 0xBDED,\n\t0x5DFF, 0x8E88, 0x5E00, 0x8E89, 0x5E01, 0xB1D2, 0x5E02, 0xCAD0,\n\t0x5E03, 0xB2BC, 0x5E04, 0x8E8A, 0x5E05, 0xCBA7, 0x5E06, 0xB7AB,\n\t0x5E07, 0x8E8B, 0x5E08, 0xCAA6, 0x5E09, 0x8E8C, 0x5E0A, 0x8E8D,\n\t0x5E0B, 0x8E8E, 0x5E0C, 0xCFA3, 0x5E0D, 0x8E8F, 0x5E0E, 0x8E90,\n\t0x5E0F, 0xE0F8, 0x5E10, 0xD5CA, 0x5E11, 0xE0FB, 0x5E12, 0x8E91,\n\t0x5E13, 0x8E92, 0x5E14, 0xE0FA, 0x5E15, 0xC5C1, 0x5E16, 0xCCFB,\n\t0x5E17, 0x8E93, 0x5E18, 0xC1B1, 0x5E19, 0xE0F9, 0x5E1A, 0xD6E3,\n\t0x5E1B, 0xB2AF, 0x5E1C, 0xD6C4, 0x5E1D, 0xB5DB, 0x5E1E, 0x8E94,\n\t0x5E1F, 0x8E95, 0x5E20, 0x8E96, 0x5E21, 0x8E97, 0x5E22, 0x8E98,\n\t0x5E23, 0x8E99, 0x5E24, 0x8E9A, 0x5E25, 0x8E9B, 0x5E26, 0xB4F8,\n\t0x5E27, 0xD6A1, 0x5E28, 0x8E9C, 0x5E29, 0x8E9D, 0x5E2A, 0x8E9E,\n\t0x5E2B, 0x8E9F, 0x5E2C, 0x8EA0, 0x5E2D, 0xCFAF, 0x5E2E, 0xB0EF,\n\t0x5E2F, 0x8EA1, 0x5E30, 0x8EA2, 0x5E31, 0xE0FC, 0x5E32, 0x8EA3,\n\t0x5E33, 0x8EA4, 0x5E34, 0x8EA5, 0x5E35, 0x8EA6, 0x5E36, 0x8EA7,\n\t0x5E37, 0xE1A1, 0x5E38, 0xB3A3, 0x5E39, 0x8EA8, 0x5E3A, 0x8EA9,\n\t0x5E3B, 0xE0FD, 0x5E3C, 0xE0FE, 0x5E3D, 0xC3B1, 0x5E3E, 0x8EAA,\n\t0x5E3F, 0x8EAB, 0x5E40, 0x8EAC, 0x5E41, 0x8EAD, 0x5E42, 0xC3DD,\n\t0x5E43, 0x8EAE, 0x5E44, 0xE1A2, 0x5E45, 0xB7F9, 0x5E46, 0x8EAF,\n\t0x5E47, 0x8EB0, 0x5E48, 0x8EB1, 0x5E49, 0x8EB2, 0x5E4A, 0x8EB3,\n\t0x5E4B, 0x8EB4, 0x5E4C, 0xBBCF, 0x5E4D, 0x8EB5, 0x5E4E, 0x8EB6,\n\t0x5E4F, 0x8EB7, 0x5E50, 0x8EB8, 0x5E51, 0x8EB9, 0x5E52, 0x8EBA,\n\t0x5E53, 0x8EBB, 0x5E54, 0xE1A3, 0x5E55, 0xC4BB, 0x5E56, 0x8EBC,\n\t0x5E57, 0x8EBD, 0x5E58, 0x8EBE, 0x5E59, 0x8EBF, 0x5E5A, 0x8EC0,\n\t0x5E5B, 0xE1A4, 0x5E5C, 0x8EC1, 0x5E5D, 0x8EC2, 0x5E5E, 0xE1A5,\n\t0x5E5F, 0x8EC3, 0x5E60, 0x8EC4, 0x5E61, 0xE1A6, 0x5E62, 0xB4B1,\n\t0x5E63, 0x8EC5, 0x5E64, 0x8EC6, 0x5E65, 0x8EC7, 0x5E66, 0x8EC8,\n\t0x5E67, 0x8EC9, 0x5E68, 0x8ECA, 0x5E69, 0x8ECB, 0x5E6A, 0x8ECC,\n\t0x5E6B, 0x8ECD, 0x5E6C, 0x8ECE, 0x5E6D, 0x8ECF, 0x5E6E, 0x8ED0,\n\t0x5E6F, 0x8ED1, 0x5E70, 0x8ED2, 0x5E71, 0x8ED3, 0x5E72, 0xB8C9,\n\t0x5E73, 0xC6BD, 0x5E74, 0xC4EA, 0x5E75, 0x8ED4, 0x5E76, 0xB2A2,\n\t0x5E77, 0x8ED5, 0x5E78, 0xD0D2, 0x5E79, 0x8ED6, 0x5E7A, 0xE7DB,\n\t0x5E7B, 0xBBC3, 0x5E7C, 0xD3D7, 0x5E7D, 0xD3C4, 0x5E7E, 0x8ED7,\n\t0x5E7F, 0xB9E3, 0x5E80, 0xE2CF, 0x5E81, 0x8ED8, 0x5E82, 0x8ED9,\n\t0x5E83, 0x8EDA, 0x5E84, 0xD7AF, 0x5E85, 0x8EDB, 0x5E86, 0xC7EC,\n\t0x5E87, 0xB1D3, 0x5E88, 0x8EDC, 0x5E89, 0x8EDD, 0x5E8A, 0xB4B2,\n\t0x5E8B, 0xE2D1, 0x5E8C, 0x8EDE, 0x5E8D, 0x8EDF, 0x5E8E, 0x8EE0,\n\t0x5E8F, 0xD0F2, 0x5E90, 0xC2AE, 0x5E91, 0xE2D0, 0x5E92, 0x8EE1,\n\t0x5E93, 0xBFE2, 0x5E94, 0xD3A6, 0x5E95, 0xB5D7, 0x5E96, 0xE2D2,\n\t0x5E97, 0xB5EA, 0x5E98, 0x8EE2, 0x5E99, 0xC3ED, 0x5E9A, 0xB8FD,\n\t0x5E9B, 0x8EE3, 0x5E9C, 0xB8AE, 0x5E9D, 0x8EE4, 0x5E9E, 0xC5D3,\n\t0x5E9F, 0xB7CF, 0x5EA0, 0xE2D4, 0x5EA1, 0x8EE5, 0x5EA2, 0x8EE6,\n\t0x5EA3, 0x8EE7, 0x5EA4, 0x8EE8, 0x5EA5, 0xE2D3, 0x5EA6, 0xB6C8,\n\t0x5EA7, 0xD7F9, 0x5EA8, 0x8EE9, 0x5EA9, 0x8EEA, 0x5EAA, 0x8EEB,\n\t0x5EAB, 0x8EEC, 0x5EAC, 0x8EED, 0x5EAD, 0xCDA5, 0x5EAE, 0x8EEE,\n\t0x5EAF, 0x8EEF, 0x5EB0, 0x8EF0, 0x5EB1, 0x8EF1, 0x5EB2, 0x8EF2,\n\t0x5EB3, 0xE2D8, 0x5EB4, 0x8EF3, 0x5EB5, 0xE2D6, 0x5EB6, 0xCAFC,\n\t0x5EB7, 0xBFB5, 0x5EB8, 0xD3B9, 0x5EB9, 0xE2D5, 0x5EBA, 0x8EF4,\n\t0x5EBB, 0x8EF5, 0x5EBC, 0x8EF6, 0x5EBD, 0x8EF7, 0x5EBE, 0xE2D7,\n\t0x5EBF, 0x8EF8, 0x5EC0, 0x8EF9, 0x5EC1, 0x8EFA, 0x5EC2, 0x8EFB,\n\t0x5EC3, 0x8EFC, 0x5EC4, 0x8EFD, 0x5EC5, 0x8EFE, 0x5EC6, 0x8F40,\n\t0x5EC7, 0x8F41, 0x5EC8, 0x8F42, 0x5EC9, 0xC1AE, 0x5ECA, 0xC0C8,\n\t0x5ECB, 0x8F43, 0x5ECC, 0x8F44, 0x5ECD, 0x8F45, 0x5ECE, 0x8F46,\n\t0x5ECF, 0x8F47, 0x5ED0, 0x8F48, 0x5ED1, 0xE2DB, 0x5ED2, 0xE2DA,\n\t0x5ED3, 0xC0AA, 0x5ED4, 0x8F49, 0x5ED5, 0x8F4A, 0x5ED6, 0xC1CE,\n\t0x5ED7, 0x8F4B, 0x5ED8, 0x8F4C, 0x5ED9, 0x8F4D, 0x5EDA, 0x8F4E,\n\t0x5EDB, 0xE2DC, 0x5EDC, 0x8F4F, 0x5EDD, 0x8F50, 0x5EDE, 0x8F51,\n\t0x5EDF, 0x8F52, 0x5EE0, 0x8F53, 0x5EE1, 0x8F54, 0x5EE2, 0x8F55,\n\t0x5EE3, 0x8F56, 0x5EE4, 0x8F57, 0x5EE5, 0x8F58, 0x5EE6, 0x8F59,\n\t0x5EE7, 0x8F5A, 0x5EE8, 0xE2DD, 0x5EE9, 0x8F5B, 0x5EEA, 0xE2DE,\n\t0x5EEB, 0x8F5C, 0x5EEC, 0x8F5D, 0x5EED, 0x8F5E, 0x5EEE, 0x8F5F,\n\t0x5EEF, 0x8F60, 0x5EF0, 0x8F61, 0x5EF1, 0x8F62, 0x5EF2, 0x8F63,\n\t0x5EF3, 0x8F64, 0x5EF4, 0xDBC8, 0x5EF5, 0x8F65, 0x5EF6, 0xD1D3,\n\t0x5EF7, 0xCDA2, 0x5EF8, 0x8F66, 0x5EF9, 0x8F67, 0x5EFA, 0xBDA8,\n\t0x5EFB, 0x8F68, 0x5EFC, 0x8F69, 0x5EFD, 0x8F6A, 0x5EFE, 0xDEC3,\n\t0x5EFF, 0xD8A5, 0x5F00, 0xBFAA, 0x5F01, 0xDBCD, 0x5F02, 0xD2EC,\n\t0x5F03, 0xC6FA, 0x5F04, 0xC5AA, 0x5F05, 0x8F6B, 0x5F06, 0x8F6C,\n\t0x5F07, 0x8F6D, 0x5F08, 0xDEC4, 0x5F09, 0x8F6E, 0x5F0A, 0xB1D7,\n\t0x5F0B, 0xDFAE, 0x5F0C, 0x8F6F, 0x5F0D, 0x8F70, 0x5F0E, 0x8F71,\n\t0x5F0F, 0xCABD, 0x5F10, 0x8F72, 0x5F11, 0xDFB1, 0x5F12, 0x8F73,\n\t0x5F13, 0xB9AD, 0x5F14, 0x8F74, 0x5F15, 0xD2FD, 0x5F16, 0x8F75,\n\t0x5F17, 0xB8A5, 0x5F18, 0xBAEB, 0x5F19, 0x8F76, 0x5F1A, 0x8F77,\n\t0x5F1B, 0xB3DA, 0x5F1C, 0x8F78, 0x5F1D, 0x8F79, 0x5F1E, 0x8F7A,\n\t0x5F1F, 0xB5DC, 0x5F20, 0xD5C5, 0x5F21, 0x8F7B, 0x5F22, 0x8F7C,\n\t0x5F23, 0x8F7D, 0x5F24, 0x8F7E, 0x5F25, 0xC3D6, 0x5F26, 0xCFD2,\n\t0x5F27, 0xBBA1, 0x5F28, 0x8F80, 0x5F29, 0xE5F3, 0x5F2A, 0xE5F2,\n\t0x5F2B, 0x8F81, 0x5F2C, 0x8F82, 0x5F2D, 0xE5F4, 0x5F2E, 0x8F83,\n\t0x5F2F, 0xCDE4, 0x5F30, 0x8F84, 0x5F31, 0xC8F5, 0x5F32, 0x8F85,\n\t0x5F33, 0x8F86, 0x5F34, 0x8F87, 0x5F35, 0x8F88, 0x5F36, 0x8F89,\n\t0x5F37, 0x8F8A, 0x5F38, 0x8F8B, 0x5F39, 0xB5AF, 0x5F3A, 0xC7BF,\n\t0x5F3B, 0x8F8C, 0x5F3C, 0xE5F6, 0x5F3D, 0x8F8D, 0x5F3E, 0x8F8E,\n\t0x5F3F, 0x8F8F, 0x5F40, 0xECB0, 0x5F41, 0x8F90, 0x5F42, 0x8F91,\n\t0x5F43, 0x8F92, 0x5F44, 0x8F93, 0x5F45, 0x8F94, 0x5F46, 0x8F95,\n\t0x5F47, 0x8F96, 0x5F48, 0x8F97, 0x5F49, 0x8F98, 0x5F4A, 0x8F99,\n\t0x5F4B, 0x8F9A, 0x5F4C, 0x8F9B, 0x5F4D, 0x8F9C, 0x5F4E, 0x8F9D,\n\t0x5F4F, 0x8F9E, 0x5F50, 0xE5E6, 0x5F51, 0x8F9F, 0x5F52, 0xB9E9,\n\t0x5F53, 0xB5B1, 0x5F54, 0x8FA0, 0x5F55, 0xC2BC, 0x5F56, 0xE5E8,\n\t0x5F57, 0xE5E7, 0x5F58, 0xE5E9, 0x5F59, 0x8FA1, 0x5F5A, 0x8FA2,\n\t0x5F5B, 0x8FA3, 0x5F5C, 0x8FA4, 0x5F5D, 0xD2CD, 0x5F5E, 0x8FA5,\n\t0x5F5F, 0x8FA6, 0x5F60, 0x8FA7, 0x5F61, 0xE1EA, 0x5F62, 0xD0CE,\n\t0x5F63, 0x8FA8, 0x5F64, 0xCDAE, 0x5F65, 0x8FA9, 0x5F66, 0xD1E5,\n\t0x5F67, 0x8FAA, 0x5F68, 0x8FAB, 0x5F69, 0xB2CA, 0x5F6A, 0xB1EB,\n\t0x5F6B, 0x8FAC, 0x5F6C, 0xB1F2, 0x5F6D, 0xC5ED, 0x5F6E, 0x8FAD,\n\t0x5F6F, 0x8FAE, 0x5F70, 0xD5C3, 0x5F71, 0xD3B0, 0x5F72, 0x8FAF,\n\t0x5F73, 0xE1DC, 0x5F74, 0x8FB0, 0x5F75, 0x8FB1, 0x5F76, 0x8FB2,\n\t0x5F77, 0xE1DD, 0x5F78, 0x8FB3, 0x5F79, 0xD2DB, 0x5F7A, 0x8FB4,\n\t0x5F7B, 0xB3B9, 0x5F7C, 0xB1CB, 0x5F7D, 0x8FB5, 0x5F7E, 0x8FB6,\n\t0x5F7F, 0x8FB7, 0x5F80, 0xCDF9, 0x5F81, 0xD5F7, 0x5F82, 0xE1DE,\n\t0x5F83, 0x8FB8, 0x5F84, 0xBEB6, 0x5F85, 0xB4FD, 0x5F86, 0x8FB9,\n\t0x5F87, 0xE1DF, 0x5F88, 0xBADC, 0x5F89, 0xE1E0, 0x5F8A, 0xBBB2,\n\t0x5F8B, 0xC2C9, 0x5F8C, 0xE1E1, 0x5F8D, 0x8FBA, 0x5F8E, 0x8FBB,\n\t0x5F8F, 0x8FBC, 0x5F90, 0xD0EC, 0x5F91, 0x8FBD, 0x5F92, 0xCDBD,\n\t0x5F93, 0x8FBE, 0x5F94, 0x8FBF, 0x5F95, 0xE1E2, 0x5F96, 0x8FC0,\n\t0x5F97, 0xB5C3, 0x5F98, 0xC5C7, 0x5F99, 0xE1E3, 0x5F9A, 0x8FC1,\n\t0x5F9B, 0x8FC2, 0x5F9C, 0xE1E4, 0x5F9D, 0x8FC3, 0x5F9E, 0x8FC4,\n\t0x5F9F, 0x8FC5, 0x5FA0, 0x8FC6, 0x5FA1, 0xD3F9, 0x5FA2, 0x8FC7,\n\t0x5FA3, 0x8FC8, 0x5FA4, 0x8FC9, 0x5FA5, 0x8FCA, 0x5FA6, 0x8FCB,\n\t0x5FA7, 0x8FCC, 0x5FA8, 0xE1E5, 0x5FA9, 0x8FCD, 0x5FAA, 0xD1AD,\n\t0x5FAB, 0x8FCE, 0x5FAC, 0x8FCF, 0x5FAD, 0xE1E6, 0x5FAE, 0xCEA2,\n\t0x5FAF, 0x8FD0, 0x5FB0, 0x8FD1, 0x5FB1, 0x8FD2, 0x5FB2, 0x8FD3,\n\t0x5FB3, 0x8FD4, 0x5FB4, 0x8FD5, 0x5FB5, 0xE1E7, 0x5FB6, 0x8FD6,\n\t0x5FB7, 0xB5C2, 0x5FB8, 0x8FD7, 0x5FB9, 0x8FD8, 0x5FBA, 0x8FD9,\n\t0x5FBB, 0x8FDA, 0x5FBC, 0xE1E8, 0x5FBD, 0xBBD5, 0x5FBE, 0x8FDB,\n\t0x5FBF, 0x8FDC, 0x5FC0, 0x8FDD, 0x5FC1, 0x8FDE, 0x5FC2, 0x8FDF,\n\t0x5FC3, 0xD0C4, 0x5FC4, 0xE2E0, 0x5FC5, 0xB1D8, 0x5FC6, 0xD2E4,\n\t0x5FC7, 0x8FE0, 0x5FC8, 0x8FE1, 0x5FC9, 0xE2E1, 0x5FCA, 0x8FE2,\n\t0x5FCB, 0x8FE3, 0x5FCC, 0xBCC9, 0x5FCD, 0xC8CC, 0x5FCE, 0x8FE4,\n\t0x5FCF, 0xE2E3, 0x5FD0, 0xECFE, 0x5FD1, 0xECFD, 0x5FD2, 0xDFAF,\n\t0x5FD3, 0x8FE5, 0x5FD4, 0x8FE6, 0x5FD5, 0x8FE7, 0x5FD6, 0xE2E2,\n\t0x5FD7, 0xD6BE, 0x5FD8, 0xCDFC, 0x5FD9, 0xC3A6, 0x5FDA, 0x8FE8,\n\t0x5FDB, 0x8FE9, 0x5FDC, 0x8FEA, 0x5FDD, 0xE3C3, 0x5FDE, 0x8FEB,\n\t0x5FDF, 0x8FEC, 0x5FE0, 0xD6D2, 0x5FE1, 0xE2E7, 0x5FE2, 0x8FED,\n\t0x5FE3, 0x8FEE, 0x5FE4, 0xE2E8, 0x5FE5, 0x8FEF, 0x5FE6, 0x8FF0,\n\t0x5FE7, 0xD3C7, 0x5FE8, 0x8FF1, 0x5FE9, 0x8FF2, 0x5FEA, 0xE2EC,\n\t0x5FEB, 0xBFEC, 0x5FEC, 0x8FF3, 0x5FED, 0xE2ED, 0x5FEE, 0xE2E5,\n\t0x5FEF, 0x8FF4, 0x5FF0, 0x8FF5, 0x5FF1, 0xB3C0, 0x5FF2, 0x8FF6,\n\t0x5FF3, 0x8FF7, 0x5FF4, 0x8FF8, 0x5FF5, 0xC4EE, 0x5FF6, 0x8FF9,\n\t0x5FF7, 0x8FFA, 0x5FF8, 0xE2EE, 0x5FF9, 0x8FFB, 0x5FFA, 0x8FFC,\n\t0x5FFB, 0xD0C3, 0x5FFC, 0x8FFD, 0x5FFD, 0xBAF6, 0x5FFE, 0xE2E9,\n\t0x5FFF, 0xB7DE, 0x6000, 0xBBB3, 0x6001, 0xCCAC, 0x6002, 0xCBCB,\n\t0x6003, 0xE2E4, 0x6004, 0xE2E6, 0x6005, 0xE2EA, 0x6006, 0xE2EB,\n\t0x6007, 0x8FFE, 0x6008, 0x9040, 0x6009, 0x9041, 0x600A, 0xE2F7,\n\t0x600B, 0x9042, 0x600C, 0x9043, 0x600D, 0xE2F4, 0x600E, 0xD4F5,\n\t0x600F, 0xE2F3, 0x6010, 0x9044, 0x6011, 0x9045, 0x6012, 0xC5AD,\n\t0x6013, 0x9046, 0x6014, 0xD5FA, 0x6015, 0xC5C2, 0x6016, 0xB2C0,\n\t0x6017, 0x9047, 0x6018, 0x9048, 0x6019, 0xE2EF, 0x601A, 0x9049,\n\t0x601B, 0xE2F2, 0x601C, 0xC1AF, 0x601D, 0xCBBC, 0x601E, 0x904A,\n\t0x601F, 0x904B, 0x6020, 0xB5A1, 0x6021, 0xE2F9, 0x6022, 0x904C,\n\t0x6023, 0x904D, 0x6024, 0x904E, 0x6025, 0xBCB1, 0x6026, 0xE2F1,\n\t0x6027, 0xD0D4, 0x6028, 0xD4B9, 0x6029, 0xE2F5, 0x602A, 0xB9D6,\n\t0x602B, 0xE2F6, 0x602C, 0x904F, 0x602D, 0x9050, 0x602E, 0x9051,\n\t0x602F, 0xC7D3, 0x6030, 0x9052, 0x6031, 0x9053, 0x6032, 0x9054,\n\t0x6033, 0x9055, 0x6034, 0x9056, 0x6035, 0xE2F0, 0x6036, 0x9057,\n\t0x6037, 0x9058, 0x6038, 0x9059, 0x6039, 0x905A, 0x603A, 0x905B,\n\t0x603B, 0xD7DC, 0x603C, 0xEDA1, 0x603D, 0x905C, 0x603E, 0x905D,\n\t0x603F, 0xE2F8, 0x6040, 0x905E, 0x6041, 0xEDA5, 0x6042, 0xE2FE,\n\t0x6043, 0xCAD1, 0x6044, 0x905F, 0x6045, 0x9060, 0x6046, 0x9061,\n\t0x6047, 0x9062, 0x6048, 0x9063, 0x6049, 0x9064, 0x604A, 0x9065,\n\t0x604B, 0xC1B5, 0x604C, 0x9066, 0x604D, 0xBBD0, 0x604E, 0x9067,\n\t0x604F, 0x9068, 0x6050, 0xBFD6, 0x6051, 0x9069, 0x6052, 0xBAE3,\n\t0x6053, 0x906A, 0x6054, 0x906B, 0x6055, 0xCBA1, 0x6056, 0x906C,\n\t0x6057, 0x906D, 0x6058, 0x906E, 0x6059, 0xEDA6, 0x605A, 0xEDA3,\n\t0x605B, 0x906F, 0x605C, 0x9070, 0x605D, 0xEDA2, 0x605E, 0x9071,\n\t0x605F, 0x9072, 0x6060, 0x9073, 0x6061, 0x9074, 0x6062, 0xBBD6,\n\t0x6063, 0xEDA7, 0x6064, 0xD0F4, 0x6065, 0x9075, 0x6066, 0x9076,\n\t0x6067, 0xEDA4, 0x6068, 0xBADE, 0x6069, 0xB6F7, 0x606A, 0xE3A1,\n\t0x606B, 0xB6B2, 0x606C, 0xCCF1, 0x606D, 0xB9A7, 0x606E, 0x9077,\n\t0x606F, 0xCFA2, 0x6070, 0xC7A1, 0x6071, 0x9078, 0x6072, 0x9079,\n\t0x6073, 0xBFD2, 0x6074, 0x907A, 0x6075, 0x907B, 0x6076, 0xB6F1,\n\t0x6077, 0x907C, 0x6078, 0xE2FA, 0x6079, 0xE2FB, 0x607A, 0xE2FD,\n\t0x607B, 0xE2FC, 0x607C, 0xC4D5, 0x607D, 0xE3A2, 0x607E, 0x907D,\n\t0x607F, 0xD3C1, 0x6080, 0x907E, 0x6081, 0x9080, 0x6082, 0x9081,\n\t0x6083, 0xE3A7, 0x6084, 0xC7C4, 0x6085, 0x9082, 0x6086, 0x9083,\n\t0x6087, 0x9084, 0x6088, 0x9085, 0x6089, 0xCFA4, 0x608A, 0x9086,\n\t0x608B, 0x9087, 0x608C, 0xE3A9, 0x608D, 0xBAB7, 0x608E, 0x9088,\n\t0x608F, 0x9089, 0x6090, 0x908A, 0x6091, 0x908B, 0x6092, 0xE3A8,\n\t0x6093, 0x908C, 0x6094, 0xBBDA, 0x6095, 0x908D, 0x6096, 0xE3A3,\n\t0x6097, 0x908E, 0x6098, 0x908F, 0x6099, 0x9090, 0x609A, 0xE3A4,\n\t0x609B, 0xE3AA, 0x609C, 0x9091, 0x609D, 0xE3A6, 0x609E, 0x9092,\n\t0x609F, 0xCEF2, 0x60A0, 0xD3C6, 0x60A1, 0x9093, 0x60A2, 0x9094,\n\t0x60A3, 0xBBBC, 0x60A4, 0x9095, 0x60A5, 0x9096, 0x60A6, 0xD4C3,\n\t0x60A7, 0x9097, 0x60A8, 0xC4FA, 0x60A9, 0x9098, 0x60AA, 0x9099,\n\t0x60AB, 0xEDA8, 0x60AC, 0xD0FC, 0x60AD, 0xE3A5, 0x60AE, 0x909A,\n\t0x60AF, 0xC3F5, 0x60B0, 0x909B, 0x60B1, 0xE3AD, 0x60B2, 0xB1AF,\n\t0x60B3, 0x909C, 0x60B4, 0xE3B2, 0x60B5, 0x909D, 0x60B6, 0x909E,\n\t0x60B7, 0x909F, 0x60B8, 0xBCC2, 0x60B9, 0x90A0, 0x60BA, 0x90A1,\n\t0x60BB, 0xE3AC, 0x60BC, 0xB5BF, 0x60BD, 0x90A2, 0x60BE, 0x90A3,\n\t0x60BF, 0x90A4, 0x60C0, 0x90A5, 0x60C1, 0x90A6, 0x60C2, 0x90A7,\n\t0x60C3, 0x90A8, 0x60C4, 0x90A9, 0x60C5, 0xC7E9, 0x60C6, 0xE3B0,\n\t0x60C7, 0x90AA, 0x60C8, 0x90AB, 0x60C9, 0x90AC, 0x60CA, 0xBEAA,\n\t0x60CB, 0xCDEF, 0x60CC, 0x90AD, 0x60CD, 0x90AE, 0x60CE, 0x90AF,\n\t0x60CF, 0x90B0, 0x60D0, 0x90B1, 0x60D1, 0xBBF3, 0x60D2, 0x90B2,\n\t0x60D3, 0x90B3, 0x60D4, 0x90B4, 0x60D5, 0xCCE8, 0x60D6, 0x90B5,\n\t0x60D7, 0x90B6, 0x60D8, 0xE3AF, 0x60D9, 0x90B7, 0x60DA, 0xE3B1,\n\t0x60DB, 0x90B8, 0x60DC, 0xCFA7, 0x60DD, 0xE3AE, 0x60DE, 0x90B9,\n\t0x60DF, 0xCEA9, 0x60E0, 0xBBDD, 0x60E1, 0x90BA, 0x60E2, 0x90BB,\n\t0x60E3, 0x90BC, 0x60E4, 0x90BD, 0x60E5, 0x90BE, 0x60E6, 0xB5EB,\n\t0x60E7, 0xBEE5, 0x60E8, 0xB2D2, 0x60E9, 0xB3CD, 0x60EA, 0x90BF,\n\t0x60EB, 0xB1B9, 0x60EC, 0xE3AB, 0x60ED, 0xB2D1, 0x60EE, 0xB5AC,\n\t0x60EF, 0xB9DF, 0x60F0, 0xB6E8, 0x60F1, 0x90C0, 0x60F2, 0x90C1,\n\t0x60F3, 0xCFEB, 0x60F4, 0xE3B7, 0x60F5, 0x90C2, 0x60F6, 0xBBCC,\n\t0x60F7, 0x90C3, 0x60F8, 0x90C4, 0x60F9, 0xC8C7, 0x60FA, 0xD0CA,\n\t0x60FB, 0x90C5, 0x60FC, 0x90C6, 0x60FD, 0x90C7, 0x60FE, 0x90C8,\n\t0x60FF, 0x90C9, 0x6100, 0xE3B8, 0x6101, 0xB3EE, 0x6102, 0x90CA,\n\t0x6103, 0x90CB, 0x6104, 0x90CC, 0x6105, 0x90CD, 0x6106, 0xEDA9,\n\t0x6107, 0x90CE, 0x6108, 0xD3FA, 0x6109, 0xD3E4, 0x610A, 0x90CF,\n\t0x610B, 0x90D0, 0x610C, 0x90D1, 0x610D, 0xEDAA, 0x610E, 0xE3B9,\n\t0x610F, 0xD2E2, 0x6110, 0x90D2, 0x6111, 0x90D3, 0x6112, 0x90D4,\n\t0x6113, 0x90D5, 0x6114, 0x90D6, 0x6115, 0xE3B5, 0x6116, 0x90D7,\n\t0x6117, 0x90D8, 0x6118, 0x90D9, 0x6119, 0x90DA, 0x611A, 0xD3DE,\n\t0x611B, 0x90DB, 0x611C, 0x90DC, 0x611D, 0x90DD, 0x611E, 0x90DE,\n\t0x611F, 0xB8D0, 0x6120, 0xE3B3, 0x6121, 0x90DF, 0x6122, 0x90E0,\n\t0x6123, 0xE3B6, 0x6124, 0xB7DF, 0x6125, 0x90E1, 0x6126, 0xE3B4,\n\t0x6127, 0xC0A2, 0x6128, 0x90E2, 0x6129, 0x90E3, 0x612A, 0x90E4,\n\t0x612B, 0xE3BA, 0x612C, 0x90E5, 0x612D, 0x90E6, 0x612E, 0x90E7,\n\t0x612F, 0x90E8, 0x6130, 0x90E9, 0x6131, 0x90EA, 0x6132, 0x90EB,\n\t0x6133, 0x90EC, 0x6134, 0x90ED, 0x6135, 0x90EE, 0x6136, 0x90EF,\n\t0x6137, 0x90F0, 0x6138, 0x90F1, 0x6139, 0x90F2, 0x613A, 0x90F3,\n\t0x613B, 0x90F4, 0x613C, 0x90F5, 0x613D, 0x90F6, 0x613E, 0x90F7,\n\t0x613F, 0xD4B8, 0x6140, 0x90F8, 0x6141, 0x90F9, 0x6142, 0x90FA,\n\t0x6143, 0x90FB, 0x6144, 0x90FC, 0x6145, 0x90FD, 0x6146, 0x90FE,\n\t0x6147, 0x9140, 0x6148, 0xB4C8, 0x6149, 0x9141, 0x614A, 0xE3BB,\n\t0x614B, 0x9142, 0x614C, 0xBBC5, 0x614D, 0x9143, 0x614E, 0xC9F7,\n\t0x614F, 0x9144, 0x6150, 0x9145, 0x6151, 0xC9E5, 0x6152, 0x9146,\n\t0x6153, 0x9147, 0x6154, 0x9148, 0x6155, 0xC4BD, 0x6156, 0x9149,\n\t0x6157, 0x914A, 0x6158, 0x914B, 0x6159, 0x914C, 0x615A, 0x914D,\n\t0x615B, 0x914E, 0x615C, 0x914F, 0x615D, 0xEDAB, 0x615E, 0x9150,\n\t0x615F, 0x9151, 0x6160, 0x9152, 0x6161, 0x9153, 0x6162, 0xC2FD,\n\t0x6163, 0x9154, 0x6164, 0x9155, 0x6165, 0x9156, 0x6166, 0x9157,\n\t0x6167, 0xBBDB, 0x6168, 0xBFAE, 0x6169, 0x9158, 0x616A, 0x9159,\n\t0x616B, 0x915A, 0x616C, 0x915B, 0x616D, 0x915C, 0x616E, 0x915D,\n\t0x616F, 0x915E, 0x6170, 0xCEBF, 0x6171, 0x915F, 0x6172, 0x9160,\n\t0x6173, 0x9161, 0x6174, 0x9162, 0x6175, 0xE3BC, 0x6176, 0x9163,\n\t0x6177, 0xBFB6, 0x6178, 0x9164, 0x6179, 0x9165, 0x617A, 0x9166,\n\t0x617B, 0x9167, 0x617C, 0x9168, 0x617D, 0x9169, 0x617E, 0x916A,\n\t0x617F, 0x916B, 0x6180, 0x916C, 0x6181, 0x916D, 0x6182, 0x916E,\n\t0x6183, 0x916F, 0x6184, 0x9170, 0x6185, 0x9171, 0x6186, 0x9172,\n\t0x6187, 0x9173, 0x6188, 0x9174, 0x6189, 0x9175, 0x618A, 0x9176,\n\t0x618B, 0xB1EF, 0x618C, 0x9177, 0x618D, 0x9178, 0x618E, 0xD4F7,\n\t0x618F, 0x9179, 0x6190, 0x917A, 0x6191, 0x917B, 0x6192, 0x917C,\n\t0x6193, 0x917D, 0x6194, 0xE3BE, 0x6195, 0x917E, 0x6196, 0x9180,\n\t0x6197, 0x9181, 0x6198, 0x9182, 0x6199, 0x9183, 0x619A, 0x9184,\n\t0x619B, 0x9185, 0x619C, 0x9186, 0x619D, 0xEDAD, 0x619E, 0x9187,\n\t0x619F, 0x9188, 0x61A0, 0x9189, 0x61A1, 0x918A, 0x61A2, 0x918B,\n\t0x61A3, 0x918C, 0x61A4, 0x918D, 0x61A5, 0x918E, 0x61A6, 0x918F,\n\t0x61A7, 0xE3BF, 0x61A8, 0xBAA9, 0x61A9, 0xEDAC, 0x61AA, 0x9190,\n\t0x61AB, 0x9191, 0x61AC, 0xE3BD, 0x61AD, 0x9192, 0x61AE, 0x9193,\n\t0x61AF, 0x9194, 0x61B0, 0x9195, 0x61B1, 0x9196, 0x61B2, 0x9197,\n\t0x61B3, 0x9198, 0x61B4, 0x9199, 0x61B5, 0x919A, 0x61B6, 0x919B,\n\t0x61B7, 0xE3C0, 0x61B8, 0x919C, 0x61B9, 0x919D, 0x61BA, 0x919E,\n\t0x61BB, 0x919F, 0x61BC, 0x91A0, 0x61BD, 0x91A1, 0x61BE, 0xBAB6,\n\t0x61BF, 0x91A2, 0x61C0, 0x91A3, 0x61C1, 0x91A4, 0x61C2, 0xB6AE,\n\t0x61C3, 0x91A5, 0x61C4, 0x91A6, 0x61C5, 0x91A7, 0x61C6, 0x91A8,\n\t0x61C7, 0x91A9, 0x61C8, 0xD0B8, 0x61C9, 0x91AA, 0x61CA, 0xB0C3,\n\t0x61CB, 0xEDAE, 0x61CC, 0x91AB, 0x61CD, 0x91AC, 0x61CE, 0x91AD,\n\t0x61CF, 0x91AE, 0x61D0, 0x91AF, 0x61D1, 0xEDAF, 0x61D2, 0xC0C1,\n\t0x61D3, 0x91B0, 0x61D4, 0xE3C1, 0x61D5, 0x91B1, 0x61D6, 0x91B2,\n\t0x61D7, 0x91B3, 0x61D8, 0x91B4, 0x61D9, 0x91B5, 0x61DA, 0x91B6,\n\t0x61DB, 0x91B7, 0x61DC, 0x91B8, 0x61DD, 0x91B9, 0x61DE, 0x91BA,\n\t0x61DF, 0x91BB, 0x61E0, 0x91BC, 0x61E1, 0x91BD, 0x61E2, 0x91BE,\n\t0x61E3, 0x91BF, 0x61E4, 0x91C0, 0x61E5, 0x91C1, 0x61E6, 0xC5B3,\n\t0x61E7, 0x91C2, 0x61E8, 0x91C3, 0x61E9, 0x91C4, 0x61EA, 0x91C5,\n\t0x61EB, 0x91C6, 0x61EC, 0x91C7, 0x61ED, 0x91C8, 0x61EE, 0x91C9,\n\t0x61EF, 0x91CA, 0x61F0, 0x91CB, 0x61F1, 0x91CC, 0x61F2, 0x91CD,\n\t0x61F3, 0x91CE, 0x61F4, 0x91CF, 0x61F5, 0xE3C2, 0x61F6, 0x91D0,\n\t0x61F7, 0x91D1, 0x61F8, 0x91D2, 0x61F9, 0x91D3, 0x61FA, 0x91D4,\n\t0x61FB, 0x91D5, 0x61FC, 0x91D6, 0x61FD, 0x91D7, 0x61FE, 0x91D8,\n\t0x61FF, 0xDCB2, 0x6200, 0x91D9, 0x6201, 0x91DA, 0x6202, 0x91DB,\n\t0x6203, 0x91DC, 0x6204, 0x91DD, 0x6205, 0x91DE, 0x6206, 0xEDB0,\n\t0x6207, 0x91DF, 0x6208, 0xB8EA, 0x6209, 0x91E0, 0x620A, 0xCEEC,\n\t0x620B, 0xEAA7, 0x620C, 0xD0E7, 0x620D, 0xCAF9, 0x620E, 0xC8D6,\n\t0x620F, 0xCFB7, 0x6210, 0xB3C9, 0x6211, 0xCED2, 0x6212, 0xBDE4,\n\t0x6213, 0x91E1, 0x6214, 0x91E2, 0x6215, 0xE3DE, 0x6216, 0xBBF2,\n\t0x6217, 0xEAA8, 0x6218, 0xD5BD, 0x6219, 0x91E3, 0x621A, 0xC6DD,\n\t0x621B, 0xEAA9, 0x621C, 0x91E4, 0x621D, 0x91E5, 0x621E, 0x91E6,\n\t0x621F, 0xEAAA, 0x6220, 0x91E7, 0x6221, 0xEAAC, 0x6222, 0xEAAB,\n\t0x6223, 0x91E8, 0x6224, 0xEAAE, 0x6225, 0xEAAD, 0x6226, 0x91E9,\n\t0x6227, 0x91EA, 0x6228, 0x91EB, 0x6229, 0x91EC, 0x622A, 0xBDD8,\n\t0x622B, 0x91ED, 0x622C, 0xEAAF, 0x622D, 0x91EE, 0x622E, 0xC2BE,\n\t0x622F, 0x91EF, 0x6230, 0x91F0, 0x6231, 0x91F1, 0x6232, 0x91F2,\n\t0x6233, 0xB4C1, 0x6234, 0xB4F7, 0x6235, 0x91F3, 0x6236, 0x91F4,\n\t0x6237, 0xBBA7, 0x6238, 0x91F5, 0x6239, 0x91F6, 0x623A, 0x91F7,\n\t0x623B, 0x91F8, 0x623C, 0x91F9, 0x623D, 0xECE6, 0x623E, 0xECE5,\n\t0x623F, 0xB7BF, 0x6240, 0xCBF9, 0x6241, 0xB1E2, 0x6242, 0x91FA,\n\t0x6243, 0xECE7, 0x6244, 0x91FB, 0x6245, 0x91FC, 0x6246, 0x91FD,\n\t0x6247, 0xC9C8, 0x6248, 0xECE8, 0x6249, 0xECE9, 0x624A, 0x91FE,\n\t0x624B, 0xCAD6, 0x624C, 0xDED0, 0x624D, 0xB2C5, 0x624E, 0xD4FA,\n\t0x624F, 0x9240, 0x6250, 0x9241, 0x6251, 0xC6CB, 0x6252, 0xB0C7,\n\t0x6253, 0xB4F2, 0x6254, 0xC8D3, 0x6255, 0x9242, 0x6256, 0x9243,\n\t0x6257, 0x9244, 0x6258, 0xCDD0, 0x6259, 0x9245, 0x625A, 0x9246,\n\t0x625B, 0xBFB8, 0x625C, 0x9247, 0x625D, 0x9248, 0x625E, 0x9249,\n\t0x625F, 0x924A, 0x6260, 0x924B, 0x6261, 0x924C, 0x6262, 0x924D,\n\t0x6263, 0xBFDB, 0x6264, 0x924E, 0x6265, 0x924F, 0x6266, 0xC7A4,\n\t0x6267, 0xD6B4, 0x6268, 0x9250, 0x6269, 0xC0A9, 0x626A, 0xDED1,\n\t0x626B, 0xC9A8, 0x626C, 0xD1EF, 0x626D, 0xC5A4, 0x626E, 0xB0E7,\n\t0x626F, 0xB3B6, 0x6270, 0xC8C5, 0x6271, 0x9251, 0x6272, 0x9252,\n\t0x6273, 0xB0E2, 0x6274, 0x9253, 0x6275, 0x9254, 0x6276, 0xB7F6,\n\t0x6277, 0x9255, 0x6278, 0x9256, 0x6279, 0xC5FA, 0x627A, 0x9257,\n\t0x627B, 0x9258, 0x627C, 0xB6F3, 0x627D, 0x9259, 0x627E, 0xD5D2,\n\t0x627F, 0xB3D0, 0x6280, 0xBCBC, 0x6281, 0x925A, 0x6282, 0x925B,\n\t0x6283, 0x925C, 0x6284, 0xB3AD, 0x6285, 0x925D, 0x6286, 0x925E,\n\t0x6287, 0x925F, 0x6288, 0x9260, 0x6289, 0xBEF1, 0x628A, 0xB0D1,\n\t0x628B, 0x9261, 0x628C, 0x9262, 0x628D, 0x9263, 0x628E, 0x9264,\n\t0x628F, 0x9265, 0x6290, 0x9266, 0x6291, 0xD2D6, 0x6292, 0xCAE3,\n\t0x6293, 0xD7A5, 0x6294, 0x9267, 0x6295, 0xCDB6, 0x6296, 0xB6B6,\n\t0x6297, 0xBFB9, 0x6298, 0xD5DB, 0x6299, 0x9268, 0x629A, 0xB8A7,\n\t0x629B, 0xC5D7, 0x629C, 0x9269, 0x629D, 0x926A, 0x629E, 0x926B,\n\t0x629F, 0xDED2, 0x62A0, 0xBFD9, 0x62A1, 0xC2D5, 0x62A2, 0xC7C0,\n\t0x62A3, 0x926C, 0x62A4, 0xBBA4, 0x62A5, 0xB1A8, 0x62A6, 0x926D,\n\t0x62A7, 0x926E, 0x62A8, 0xC5EA, 0x62A9, 0x926F, 0x62AA, 0x9270,\n\t0x62AB, 0xC5FB, 0x62AC, 0xCCA7, 0x62AD, 0x9271, 0x62AE, 0x9272,\n\t0x62AF, 0x9273, 0x62B0, 0x9274, 0x62B1, 0xB1A7, 0x62B2, 0x9275,\n\t0x62B3, 0x9276, 0x62B4, 0x9277, 0x62B5, 0xB5D6, 0x62B6, 0x9278,\n\t0x62B7, 0x9279, 0x62B8, 0x927A, 0x62B9, 0xC4A8, 0x62BA, 0x927B,\n\t0x62BB, 0xDED3, 0x62BC, 0xD1BA, 0x62BD, 0xB3E9, 0x62BE, 0x927C,\n\t0x62BF, 0xC3F2, 0x62C0, 0x927D, 0x62C1, 0x927E, 0x62C2, 0xB7F7,\n\t0x62C3, 0x9280, 0x62C4, 0xD6F4, 0x62C5, 0xB5A3, 0x62C6, 0xB2F0,\n\t0x62C7, 0xC4B4, 0x62C8, 0xC4E9, 0x62C9, 0xC0AD, 0x62CA, 0xDED4,\n\t0x62CB, 0x9281, 0x62CC, 0xB0E8, 0x62CD, 0xC5C4, 0x62CE, 0xC1E0,\n\t0x62CF, 0x9282, 0x62D0, 0xB9D5, 0x62D1, 0x9283, 0x62D2, 0xBEDC,\n\t0x62D3, 0xCDD8, 0x62D4, 0xB0CE, 0x62D5, 0x9284, 0x62D6, 0xCDCF,\n\t0x62D7, 0xDED6, 0x62D8, 0xBED0, 0x62D9, 0xD7BE, 0x62DA, 0xDED5,\n\t0x62DB, 0xD5D0, 0x62DC, 0xB0DD, 0x62DD, 0x9285, 0x62DE, 0x9286,\n\t0x62DF, 0xC4E2, 0x62E0, 0x9287, 0x62E1, 0x9288, 0x62E2, 0xC2A3,\n\t0x62E3, 0xBCF0, 0x62E4, 0x9289, 0x62E5, 0xD3B5, 0x62E6, 0xC0B9,\n\t0x62E7, 0xC5A1, 0x62E8, 0xB2A6, 0x62E9, 0xD4F1, 0x62EA, 0x928A,\n\t0x62EB, 0x928B, 0x62EC, 0xC0A8, 0x62ED, 0xCAC3, 0x62EE, 0xDED7,\n\t0x62EF, 0xD5FC, 0x62F0, 0x928C, 0x62F1, 0xB9B0, 0x62F2, 0x928D,\n\t0x62F3, 0xC8AD, 0x62F4, 0xCBA9, 0x62F5, 0x928E, 0x62F6, 0xDED9,\n\t0x62F7, 0xBFBD, 0x62F8, 0x928F, 0x62F9, 0x9290, 0x62FA, 0x9291,\n\t0x62FB, 0x9292, 0x62FC, 0xC6B4, 0x62FD, 0xD7A7, 0x62FE, 0xCAB0,\n\t0x62FF, 0xC4C3, 0x6300, 0x9293, 0x6301, 0xB3D6, 0x6302, 0xB9D2,\n\t0x6303, 0x9294, 0x6304, 0x9295, 0x6305, 0x9296, 0x6306, 0x9297,\n\t0x6307, 0xD6B8, 0x6308, 0xEAFC, 0x6309, 0xB0B4, 0x630A, 0x9298,\n\t0x630B, 0x9299, 0x630C, 0x929A, 0x630D, 0x929B, 0x630E, 0xBFE6,\n\t0x630F, 0x929C, 0x6310, 0x929D, 0x6311, 0xCCF4, 0x6312, 0x929E,\n\t0x6313, 0x929F, 0x6314, 0x92A0, 0x6315, 0x92A1, 0x6316, 0xCDDA,\n\t0x6317, 0x92A2, 0x6318, 0x92A3, 0x6319, 0x92A4, 0x631A, 0xD6BF,\n\t0x631B, 0xC2CE, 0x631C, 0x92A5, 0x631D, 0xCECE, 0x631E, 0xCCA2,\n\t0x631F, 0xD0AE, 0x6320, 0xC4D3, 0x6321, 0xB5B2, 0x6322, 0xDED8,\n\t0x6323, 0xD5F5, 0x6324, 0xBCB7, 0x6325, 0xBBD3, 0x6326, 0x92A6,\n\t0x6327, 0x92A7, 0x6328, 0xB0A4, 0x6329, 0x92A8, 0x632A, 0xC5B2,\n\t0x632B, 0xB4EC, 0x632C, 0x92A9, 0x632D, 0x92AA, 0x632E, 0x92AB,\n\t0x632F, 0xD5F1, 0x6330, 0x92AC, 0x6331, 0x92AD, 0x6332, 0xEAFD,\n\t0x6333, 0x92AE, 0x6334, 0x92AF, 0x6335, 0x92B0, 0x6336, 0x92B1,\n\t0x6337, 0x92B2, 0x6338, 0x92B3, 0x6339, 0xDEDA, 0x633A, 0xCDA6,\n\t0x633B, 0x92B4, 0x633C, 0x92B5, 0x633D, 0xCDEC, 0x633E, 0x92B6,\n\t0x633F, 0x92B7, 0x6340, 0x92B8, 0x6341, 0x92B9, 0x6342, 0xCEE6,\n\t0x6343, 0xDEDC, 0x6344, 0x92BA, 0x6345, 0xCDB1, 0x6346, 0xC0A6,\n\t0x6347, 0x92BB, 0x6348, 0x92BC, 0x6349, 0xD7BD, 0x634A, 0x92BD,\n\t0x634B, 0xDEDB, 0x634C, 0xB0C6, 0x634D, 0xBAB4, 0x634E, 0xC9D3,\n\t0x634F, 0xC4F3, 0x6350, 0xBEE8, 0x6351, 0x92BE, 0x6352, 0x92BF,\n\t0x6353, 0x92C0, 0x6354, 0x92C1, 0x6355, 0xB2B6, 0x6356, 0x92C2,\n\t0x6357, 0x92C3, 0x6358, 0x92C4, 0x6359, 0x92C5, 0x635A, 0x92C6,\n\t0x635B, 0x92C7, 0x635C, 0x92C8, 0x635D, 0x92C9, 0x635E, 0xC0CC,\n\t0x635F, 0xCBF0, 0x6360, 0x92CA, 0x6361, 0xBCF1, 0x6362, 0xBBBB,\n\t0x6363, 0xB5B7, 0x6364, 0x92CB, 0x6365, 0x92CC, 0x6366, 0x92CD,\n\t0x6367, 0xC5F5, 0x6368, 0x92CE, 0x6369, 0xDEE6, 0x636A, 0x92CF,\n\t0x636B, 0x92D0, 0x636C, 0x92D1, 0x636D, 0xDEE3, 0x636E, 0xBEDD,\n\t0x636F, 0x92D2, 0x6370, 0x92D3, 0x6371, 0xDEDF, 0x6372, 0x92D4,\n\t0x6373, 0x92D5, 0x6374, 0x92D6, 0x6375, 0x92D7, 0x6376, 0xB4B7,\n\t0x6377, 0xBDDD, 0x6378, 0x92D8, 0x6379, 0x92D9, 0x637A, 0xDEE0,\n\t0x637B, 0xC4ED, 0x637C, 0x92DA, 0x637D, 0x92DB, 0x637E, 0x92DC,\n\t0x637F, 0x92DD, 0x6380, 0xCFC6, 0x6381, 0x92DE, 0x6382, 0xB5E0,\n\t0x6383, 0x92DF, 0x6384, 0x92E0, 0x6385, 0x92E1, 0x6386, 0x92E2,\n\t0x6387, 0xB6DE, 0x6388, 0xCADA, 0x6389, 0xB5F4, 0x638A, 0xDEE5,\n\t0x638B, 0x92E3, 0x638C, 0xD5C6, 0x638D, 0x92E4, 0x638E, 0xDEE1,\n\t0x638F, 0xCCCD, 0x6390, 0xC6FE, 0x6391, 0x92E5, 0x6392, 0xC5C5,\n\t0x6393, 0x92E6, 0x6394, 0x92E7, 0x6395, 0x92E8, 0x6396, 0xD2B4,\n\t0x6397, 0x92E9, 0x6398, 0xBEF2, 0x6399, 0x92EA, 0x639A, 0x92EB,\n\t0x639B, 0x92EC, 0x639C, 0x92ED, 0x639D, 0x92EE, 0x639E, 0x92EF,\n\t0x639F, 0x92F0, 0x63A0, 0xC2D3, 0x63A1, 0x92F1, 0x63A2, 0xCCBD,\n\t0x63A3, 0xB3B8, 0x63A4, 0x92F2, 0x63A5, 0xBDD3, 0x63A6, 0x92F3,\n\t0x63A7, 0xBFD8, 0x63A8, 0xCDC6, 0x63A9, 0xD1DA, 0x63AA, 0xB4EB,\n\t0x63AB, 0x92F4, 0x63AC, 0xDEE4, 0x63AD, 0xDEDD, 0x63AE, 0xDEE7,\n\t0x63AF, 0x92F5, 0x63B0, 0xEAFE, 0x63B1, 0x92F6, 0x63B2, 0x92F7,\n\t0x63B3, 0xC2B0, 0x63B4, 0xDEE2, 0x63B5, 0x92F8, 0x63B6, 0x92F9,\n\t0x63B7, 0xD6C0, 0x63B8, 0xB5A7, 0x63B9, 0x92FA, 0x63BA, 0xB2F4,\n\t0x63BB, 0x92FB, 0x63BC, 0xDEE8, 0x63BD, 0x92FC, 0x63BE, 0xDEF2,\n\t0x63BF, 0x92FD, 0x63C0, 0x92FE, 0x63C1, 0x9340, 0x63C2, 0x9341,\n\t0x63C3, 0x9342, 0x63C4, 0xDEED, 0x63C5, 0x9343, 0x63C6, 0xDEF1,\n\t0x63C7, 0x9344, 0x63C8, 0x9345, 0x63C9, 0xC8E0, 0x63CA, 0x9346,\n\t0x63CB, 0x9347, 0x63CC, 0x9348, 0x63CD, 0xD7E1, 0x63CE, 0xDEEF,\n\t0x63CF, 0xC3E8, 0x63D0, 0xCCE1, 0x63D1, 0x9349, 0x63D2, 0xB2E5,\n\t0x63D3, 0x934A, 0x63D4, 0x934B, 0x63D5, 0x934C, 0x63D6, 0xD2BE,\n\t0x63D7, 0x934D, 0x63D8, 0x934E, 0x63D9, 0x934F, 0x63DA, 0x9350,\n\t0x63DB, 0x9351, 0x63DC, 0x9352, 0x63DD, 0x9353, 0x63DE, 0xDEEE,\n\t0x63DF, 0x9354, 0x63E0, 0xDEEB, 0x63E1, 0xCED5, 0x63E2, 0x9355,\n\t0x63E3, 0xB4A7, 0x63E4, 0x9356, 0x63E5, 0x9357, 0x63E6, 0x9358,\n\t0x63E7, 0x9359, 0x63E8, 0x935A, 0x63E9, 0xBFAB, 0x63EA, 0xBEBE,\n\t0x63EB, 0x935B, 0x63EC, 0x935C, 0x63ED, 0xBDD2, 0x63EE, 0x935D,\n\t0x63EF, 0x935E, 0x63F0, 0x935F, 0x63F1, 0x9360, 0x63F2, 0xDEE9,\n\t0x63F3, 0x9361, 0x63F4, 0xD4AE, 0x63F5, 0x9362, 0x63F6, 0xDEDE,\n\t0x63F7, 0x9363, 0x63F8, 0xDEEA, 0x63F9, 0x9364, 0x63FA, 0x9365,\n\t0x63FB, 0x9366, 0x63FC, 0x9367, 0x63FD, 0xC0BF, 0x63FE, 0x9368,\n\t0x63FF, 0xDEEC, 0x6400, 0xB2F3, 0x6401, 0xB8E9, 0x6402, 0xC2A7,\n\t0x6403, 0x9369, 0x6404, 0x936A, 0x6405, 0xBDC1, 0x6406, 0x936B,\n\t0x6407, 0x936C, 0x6408, 0x936D, 0x6409, 0x936E, 0x640A, 0x936F,\n\t0x640B, 0xDEF5, 0x640C, 0xDEF8, 0x640D, 0x9370, 0x640E, 0x9371,\n\t0x640F, 0xB2AB, 0x6410, 0xB4A4, 0x6411, 0x9372, 0x6412, 0x9373,\n\t0x6413, 0xB4EA, 0x6414, 0xC9A6, 0x6415, 0x9374, 0x6416, 0x9375,\n\t0x6417, 0x9376, 0x6418, 0x9377, 0x6419, 0x9378, 0x641A, 0x9379,\n\t0x641B, 0xDEF6, 0x641C, 0xCBD1, 0x641D, 0x937A, 0x641E, 0xB8E3,\n\t0x641F, 0x937B, 0x6420, 0xDEF7, 0x6421, 0xDEFA, 0x6422, 0x937C,\n\t0x6423, 0x937D, 0x6424, 0x937E, 0x6425, 0x9380, 0x6426, 0xDEF9,\n\t0x6427, 0x9381, 0x6428, 0x9382, 0x6429, 0x9383, 0x642A, 0xCCC2,\n\t0x642B, 0x9384, 0x642C, 0xB0E1, 0x642D, 0xB4EE, 0x642E, 0x9385,\n\t0x642F, 0x9386, 0x6430, 0x9387, 0x6431, 0x9388, 0x6432, 0x9389,\n\t0x6433, 0x938A, 0x6434, 0xE5BA, 0x6435, 0x938B, 0x6436, 0x938C,\n\t0x6437, 0x938D, 0x6438, 0x938E, 0x6439, 0x938F, 0x643A, 0xD0AF,\n\t0x643B, 0x9390, 0x643C, 0x9391, 0x643D, 0xB2EB, 0x643E, 0x9392,\n\t0x643F, 0xEBA1, 0x6440, 0x9393, 0x6441, 0xDEF4, 0x6442, 0x9394,\n\t0x6443, 0x9395, 0x6444, 0xC9E3, 0x6445, 0xDEF3, 0x6446, 0xB0DA,\n\t0x6447, 0xD2A1, 0x6448, 0xB1F7, 0x6449, 0x9396, 0x644A, 0xCCAF,\n\t0x644B, 0x9397, 0x644C, 0x9398, 0x644D, 0x9399, 0x644E, 0x939A,\n\t0x644F, 0x939B, 0x6450, 0x939C, 0x6451, 0x939D, 0x6452, 0xDEF0,\n\t0x6453, 0x939E, 0x6454, 0xCBA4, 0x6455, 0x939F, 0x6456, 0x93A0,\n\t0x6457, 0x93A1, 0x6458, 0xD5AA, 0x6459, 0x93A2, 0x645A, 0x93A3,\n\t0x645B, 0x93A4, 0x645C, 0x93A5, 0x645D, 0x93A6, 0x645E, 0xDEFB,\n\t0x645F, 0x93A7, 0x6460, 0x93A8, 0x6461, 0x93A9, 0x6462, 0x93AA,\n\t0x6463, 0x93AB, 0x6464, 0x93AC, 0x6465, 0x93AD, 0x6466, 0x93AE,\n\t0x6467, 0xB4DD, 0x6468, 0x93AF, 0x6469, 0xC4A6, 0x646A, 0x93B0,\n\t0x646B, 0x93B1, 0x646C, 0x93B2, 0x646D, 0xDEFD, 0x646E, 0x93B3,\n\t0x646F, 0x93B4, 0x6470, 0x93B5, 0x6471, 0x93B6, 0x6472, 0x93B7,\n\t0x6473, 0x93B8, 0x6474, 0x93B9, 0x6475, 0x93BA, 0x6476, 0x93BB,\n\t0x6477, 0x93BC, 0x6478, 0xC3FE, 0x6479, 0xC4A1, 0x647A, 0xDFA1,\n\t0x647B, 0x93BD, 0x647C, 0x93BE, 0x647D, 0x93BF, 0x647E, 0x93C0,\n\t0x647F, 0x93C1, 0x6480, 0x93C2, 0x6481, 0x93C3, 0x6482, 0xC1CC,\n\t0x6483, 0x93C4, 0x6484, 0xDEFC, 0x6485, 0xBEEF, 0x6486, 0x93C5,\n\t0x6487, 0xC6B2, 0x6488, 0x93C6, 0x6489, 0x93C7, 0x648A, 0x93C8,\n\t0x648B, 0x93C9, 0x648C, 0x93CA, 0x648D, 0x93CB, 0x648E, 0x93CC,\n\t0x648F, 0x93CD, 0x6490, 0x93CE, 0x6491, 0xB3C5, 0x6492, 0xC8F6,\n\t0x6493, 0x93CF, 0x6494, 0x93D0, 0x6495, 0xCBBA, 0x6496, 0xDEFE,\n\t0x6497, 0x93D1, 0x6498, 0x93D2, 0x6499, 0xDFA4, 0x649A, 0x93D3,\n\t0x649B, 0x93D4, 0x649C, 0x93D5, 0x649D, 0x93D6, 0x649E, 0xD7B2,\n\t0x649F, 0x93D7, 0x64A0, 0x93D8, 0x64A1, 0x93D9, 0x64A2, 0x93DA,\n\t0x64A3, 0x93DB, 0x64A4, 0xB3B7, 0x64A5, 0x93DC, 0x64A6, 0x93DD,\n\t0x64A7, 0x93DE, 0x64A8, 0x93DF, 0x64A9, 0xC1C3, 0x64AA, 0x93E0,\n\t0x64AB, 0x93E1, 0x64AC, 0xC7CB, 0x64AD, 0xB2A5, 0x64AE, 0xB4E9,\n\t0x64AF, 0x93E2, 0x64B0, 0xD7AB, 0x64B1, 0x93E3, 0x64B2, 0x93E4,\n\t0x64B3, 0x93E5, 0x64B4, 0x93E6, 0x64B5, 0xC4EC, 0x64B6, 0x93E7,\n\t0x64B7, 0xDFA2, 0x64B8, 0xDFA3, 0x64B9, 0x93E8, 0x64BA, 0xDFA5,\n\t0x64BB, 0x93E9, 0x64BC, 0xBAB3, 0x64BD, 0x93EA, 0x64BE, 0x93EB,\n\t0x64BF, 0x93EC, 0x64C0, 0xDFA6, 0x64C1, 0x93ED, 0x64C2, 0xC0DE,\n\t0x64C3, 0x93EE, 0x64C4, 0x93EF, 0x64C5, 0xC9C3, 0x64C6, 0x93F0,\n\t0x64C7, 0x93F1, 0x64C8, 0x93F2, 0x64C9, 0x93F3, 0x64CA, 0x93F4,\n\t0x64CB, 0x93F5, 0x64CC, 0x93F6, 0x64CD, 0xB2D9, 0x64CE, 0xC7E6,\n\t0x64CF, 0x93F7, 0x64D0, 0xDFA7, 0x64D1, 0x93F8, 0x64D2, 0xC7DC,\n\t0x64D3, 0x93F9, 0x64D4, 0x93FA, 0x64D5, 0x93FB, 0x64D6, 0x93FC,\n\t0x64D7, 0xDFA8, 0x64D8, 0xEBA2, 0x64D9, 0x93FD, 0x64DA, 0x93FE,\n\t0x64DB, 0x9440, 0x64DC, 0x9441, 0x64DD, 0x9442, 0x64DE, 0xCBD3,\n\t0x64DF, 0x9443, 0x64E0, 0x9444, 0x64E1, 0x9445, 0x64E2, 0xDFAA,\n\t0x64E3, 0x9446, 0x64E4, 0xDFA9, 0x64E5, 0x9447, 0x64E6, 0xB2C1,\n\t0x64E7, 0x9448, 0x64E8, 0x9449, 0x64E9, 0x944A, 0x64EA, 0x944B,\n\t0x64EB, 0x944C, 0x64EC, 0x944D, 0x64ED, 0x944E, 0x64EE, 0x944F,\n\t0x64EF, 0x9450, 0x64F0, 0x9451, 0x64F1, 0x9452, 0x64F2, 0x9453,\n\t0x64F3, 0x9454, 0x64F4, 0x9455, 0x64F5, 0x9456, 0x64F6, 0x9457,\n\t0x64F7, 0x9458, 0x64F8, 0x9459, 0x64F9, 0x945A, 0x64FA, 0x945B,\n\t0x64FB, 0x945C, 0x64FC, 0x945D, 0x64FD, 0x945E, 0x64FE, 0x945F,\n\t0x64FF, 0x9460, 0x6500, 0xC5CA, 0x6501, 0x9461, 0x6502, 0x9462,\n\t0x6503, 0x9463, 0x6504, 0x9464, 0x6505, 0x9465, 0x6506, 0x9466,\n\t0x6507, 0x9467, 0x6508, 0x9468, 0x6509, 0xDFAB, 0x650A, 0x9469,\n\t0x650B, 0x946A, 0x650C, 0x946B, 0x650D, 0x946C, 0x650E, 0x946D,\n\t0x650F, 0x946E, 0x6510, 0x946F, 0x6511, 0x9470, 0x6512, 0xD4DC,\n\t0x6513, 0x9471, 0x6514, 0x9472, 0x6515, 0x9473, 0x6516, 0x9474,\n\t0x6517, 0x9475, 0x6518, 0xC8C1, 0x6519, 0x9476, 0x651A, 0x9477,\n\t0x651B, 0x9478, 0x651C, 0x9479, 0x651D, 0x947A, 0x651E, 0x947B,\n\t0x651F, 0x947C, 0x6520, 0x947D, 0x6521, 0x947E, 0x6522, 0x9480,\n\t0x6523, 0x9481, 0x6524, 0x9482, 0x6525, 0xDFAC, 0x6526, 0x9483,\n\t0x6527, 0x9484, 0x6528, 0x9485, 0x6529, 0x9486, 0x652A, 0x9487,\n\t0x652B, 0xBEF0, 0x652C, 0x9488, 0x652D, 0x9489, 0x652E, 0xDFAD,\n\t0x652F, 0xD6A7, 0x6530, 0x948A, 0x6531, 0x948B, 0x6532, 0x948C,\n\t0x6533, 0x948D, 0x6534, 0xEAB7, 0x6535, 0xEBB6, 0x6536, 0xCAD5,\n\t0x6537, 0x948E, 0x6538, 0xD8FC, 0x6539, 0xB8C4, 0x653A, 0x948F,\n\t0x653B, 0xB9A5, 0x653C, 0x9490, 0x653D, 0x9491, 0x653E, 0xB7C5,\n\t0x653F, 0xD5FE, 0x6540, 0x9492, 0x6541, 0x9493, 0x6542, 0x9494,\n\t0x6543, 0x9495, 0x6544, 0x9496, 0x6545, 0xB9CA, 0x6546, 0x9497,\n\t0x6547, 0x9498, 0x6548, 0xD0A7, 0x6549, 0xF4CD, 0x654A, 0x9499,\n\t0x654B, 0x949A, 0x654C, 0xB5D0, 0x654D, 0x949B, 0x654E, 0x949C,\n\t0x654F, 0xC3F4, 0x6550, 0x949D, 0x6551, 0xBEC8, 0x6552, 0x949E,\n\t0x6553, 0x949F, 0x6554, 0x94A0, 0x6555, 0xEBB7, 0x6556, 0xB0BD,\n\t0x6557, 0x94A1, 0x6558, 0x94A2, 0x6559, 0xBDCC, 0x655A, 0x94A3,\n\t0x655B, 0xC1B2, 0x655C, 0x94A4, 0x655D, 0xB1D6, 0x655E, 0xB3A8,\n\t0x655F, 0x94A5, 0x6560, 0x94A6, 0x6561, 0x94A7, 0x6562, 0xB8D2,\n\t0x6563, 0xC9A2, 0x6564, 0x94A8, 0x6565, 0x94A9, 0x6566, 0xB6D8,\n\t0x6567, 0x94AA, 0x6568, 0x94AB, 0x6569, 0x94AC, 0x656A, 0x94AD,\n\t0x656B, 0xEBB8, 0x656C, 0xBEB4, 0x656D, 0x94AE, 0x656E, 0x94AF,\n\t0x656F, 0x94B0, 0x6570, 0xCAFD, 0x6571, 0x94B1, 0x6572, 0xC7C3,\n\t0x6573, 0x94B2, 0x6574, 0xD5FB, 0x6575, 0x94B3, 0x6576, 0x94B4,\n\t0x6577, 0xB7F3, 0x6578, 0x94B5, 0x6579, 0x94B6, 0x657A, 0x94B7,\n\t0x657B, 0x94B8, 0x657C, 0x94B9, 0x657D, 0x94BA, 0x657E, 0x94BB,\n\t0x657F, 0x94BC, 0x6580, 0x94BD, 0x6581, 0x94BE, 0x6582, 0x94BF,\n\t0x6583, 0x94C0, 0x6584, 0x94C1, 0x6585, 0x94C2, 0x6586, 0x94C3,\n\t0x6587, 0xCEC4, 0x6588, 0x94C4, 0x6589, 0x94C5, 0x658A, 0x94C6,\n\t0x658B, 0xD5AB, 0x658C, 0xB1F3, 0x658D, 0x94C7, 0x658E, 0x94C8,\n\t0x658F, 0x94C9, 0x6590, 0xECB3, 0x6591, 0xB0DF, 0x6592, 0x94CA,\n\t0x6593, 0xECB5, 0x6594, 0x94CB, 0x6595, 0x94CC, 0x6596, 0x94CD,\n\t0x6597, 0xB6B7, 0x6598, 0x94CE, 0x6599, 0xC1CF, 0x659A, 0x94CF,\n\t0x659B, 0xF5FA, 0x659C, 0xD0B1, 0x659D, 0x94D0, 0x659E, 0x94D1,\n\t0x659F, 0xD5E5, 0x65A0, 0x94D2, 0x65A1, 0xCED3, 0x65A2, 0x94D3,\n\t0x65A3, 0x94D4, 0x65A4, 0xBDEF, 0x65A5, 0xB3E2, 0x65A6, 0x94D5,\n\t0x65A7, 0xB8AB, 0x65A8, 0x94D6, 0x65A9, 0xD5B6, 0x65AA, 0x94D7,\n\t0x65AB, 0xEDBD, 0x65AC, 0x94D8, 0x65AD, 0xB6CF, 0x65AE, 0x94D9,\n\t0x65AF, 0xCBB9, 0x65B0, 0xD0C2, 0x65B1, 0x94DA, 0x65B2, 0x94DB,\n\t0x65B3, 0x94DC, 0x65B4, 0x94DD, 0x65B5, 0x94DE, 0x65B6, 0x94DF,\n\t0x65B7, 0x94E0, 0x65B8, 0x94E1, 0x65B9, 0xB7BD, 0x65BA, 0x94E2,\n\t0x65BB, 0x94E3, 0x65BC, 0xECB6, 0x65BD, 0xCAA9, 0x65BE, 0x94E4,\n\t0x65BF, 0x94E5, 0x65C0, 0x94E6, 0x65C1, 0xC5D4, 0x65C2, 0x94E7,\n\t0x65C3, 0xECB9, 0x65C4, 0xECB8, 0x65C5, 0xC2C3, 0x65C6, 0xECB7,\n\t0x65C7, 0x94E8, 0x65C8, 0x94E9, 0x65C9, 0x94EA, 0x65CA, 0x94EB,\n\t0x65CB, 0xD0FD, 0x65CC, 0xECBA, 0x65CD, 0x94EC, 0x65CE, 0xECBB,\n\t0x65CF, 0xD7E5, 0x65D0, 0x94ED, 0x65D1, 0x94EE, 0x65D2, 0xECBC,\n\t0x65D3, 0x94EF, 0x65D4, 0x94F0, 0x65D5, 0x94F1, 0x65D6, 0xECBD,\n\t0x65D7, 0xC6EC, 0x65D8, 0x94F2, 0x65D9, 0x94F3, 0x65DA, 0x94F4,\n\t0x65DB, 0x94F5, 0x65DC, 0x94F6, 0x65DD, 0x94F7, 0x65DE, 0x94F8,\n\t0x65DF, 0x94F9, 0x65E0, 0xCEDE, 0x65E1, 0x94FA, 0x65E2, 0xBCC8,\n\t0x65E3, 0x94FB, 0x65E4, 0x94FC, 0x65E5, 0xC8D5, 0x65E6, 0xB5A9,\n\t0x65E7, 0xBEC9, 0x65E8, 0xD6BC, 0x65E9, 0xD4E7, 0x65EA, 0x94FD,\n\t0x65EB, 0x94FE, 0x65EC, 0xD1AE, 0x65ED, 0xD0F1, 0x65EE, 0xEAB8,\n\t0x65EF, 0xEAB9, 0x65F0, 0xEABA, 0x65F1, 0xBAB5, 0x65F2, 0x9540,\n\t0x65F3, 0x9541, 0x65F4, 0x9542, 0x65F5, 0x9543, 0x65F6, 0xCAB1,\n\t0x65F7, 0xBFF5, 0x65F8, 0x9544, 0x65F9, 0x9545, 0x65FA, 0xCDFA,\n\t0x65FB, 0x9546, 0x65FC, 0x9547, 0x65FD, 0x9548, 0x65FE, 0x9549,\n\t0x65FF, 0x954A, 0x6600, 0xEAC0, 0x6601, 0x954B, 0x6602, 0xB0BA,\n\t0x6603, 0xEABE, 0x6604, 0x954C, 0x6605, 0x954D, 0x6606, 0xC0A5,\n\t0x6607, 0x954E, 0x6608, 0x954F, 0x6609, 0x9550, 0x660A, 0xEABB,\n\t0x660B, 0x9551, 0x660C, 0xB2FD, 0x660D, 0x9552, 0x660E, 0xC3F7,\n\t0x660F, 0xBBE8, 0x6610, 0x9553, 0x6611, 0x9554, 0x6612, 0x9555,\n\t0x6613, 0xD2D7, 0x6614, 0xCEF4, 0x6615, 0xEABF, 0x6616, 0x9556,\n\t0x6617, 0x9557, 0x6618, 0x9558, 0x6619, 0xEABC, 0x661A, 0x9559,\n\t0x661B, 0x955A, 0x661C, 0x955B, 0x661D, 0xEAC3, 0x661E, 0x955C,\n\t0x661F, 0xD0C7, 0x6620, 0xD3B3, 0x6621, 0x955D, 0x6622, 0x955E,\n\t0x6623, 0x955F, 0x6624, 0x9560, 0x6625, 0xB4BA, 0x6626, 0x9561,\n\t0x6627, 0xC3C1, 0x6628, 0xD7F2, 0x6629, 0x9562, 0x662A, 0x9563,\n\t0x662B, 0x9564, 0x662C, 0x9565, 0x662D, 0xD5D1, 0x662E, 0x9566,\n\t0x662F, 0xCAC7, 0x6630, 0x9567, 0x6631, 0xEAC5, 0x6632, 0x9568,\n\t0x6633, 0x9569, 0x6634, 0xEAC4, 0x6635, 0xEAC7, 0x6636, 0xEAC6,\n\t0x6637, 0x956A, 0x6638, 0x956B, 0x6639, 0x956C, 0x663A, 0x956D,\n\t0x663B, 0x956E, 0x663C, 0xD6E7, 0x663D, 0x956F, 0x663E, 0xCFD4,\n\t0x663F, 0x9570, 0x6640, 0x9571, 0x6641, 0xEACB, 0x6642, 0x9572,\n\t0x6643, 0xBBCE, 0x6644, 0x9573, 0x6645, 0x9574, 0x6646, 0x9575,\n\t0x6647, 0x9576, 0x6648, 0x9577, 0x6649, 0x9578, 0x664A, 0x9579,\n\t0x664B, 0xBDFA, 0x664C, 0xC9CE, 0x664D, 0x957A, 0x664E, 0x957B,\n\t0x664F, 0xEACC, 0x6650, 0x957C, 0x6651, 0x957D, 0x6652, 0xC9B9,\n\t0x6653, 0xCFFE, 0x6654, 0xEACA, 0x6655, 0xD4CE, 0x6656, 0xEACD,\n\t0x6657, 0xEACF, 0x6658, 0x957E, 0x6659, 0x9580, 0x665A, 0xCDED,\n\t0x665B, 0x9581, 0x665C, 0x9582, 0x665D, 0x9583, 0x665E, 0x9584,\n\t0x665F, 0xEAC9, 0x6660, 0x9585, 0x6661, 0xEACE, 0x6662, 0x9586,\n\t0x6663, 0x9587, 0x6664, 0xCEEE, 0x6665, 0x9588, 0x6666, 0xBBDE,\n\t0x6667, 0x9589, 0x6668, 0xB3BF, 0x6669, 0x958A, 0x666A, 0x958B,\n\t0x666B, 0x958C, 0x666C, 0x958D, 0x666D, 0x958E, 0x666E, 0xC6D5,\n\t0x666F, 0xBEB0, 0x6670, 0xCEFA, 0x6671, 0x958F, 0x6672, 0x9590,\n\t0x6673, 0x9591, 0x6674, 0xC7E7, 0x6675, 0x9592, 0x6676, 0xBEA7,\n\t0x6677, 0xEAD0, 0x6678, 0x9593, 0x6679, 0x9594, 0x667A, 0xD6C7,\n\t0x667B, 0x9595, 0x667C, 0x9596, 0x667D, 0x9597, 0x667E, 0xC1C0,\n\t0x667F, 0x9598, 0x6680, 0x9599, 0x6681, 0x959A, 0x6682, 0xD4DD,\n\t0x6683, 0x959B, 0x6684, 0xEAD1, 0x6685, 0x959C, 0x6686, 0x959D,\n\t0x6687, 0xCFBE, 0x6688, 0x959E, 0x6689, 0x959F, 0x668A, 0x95A0,\n\t0x668B, 0x95A1, 0x668C, 0xEAD2, 0x668D, 0x95A2, 0x668E, 0x95A3,\n\t0x668F, 0x95A4, 0x6690, 0x95A5, 0x6691, 0xCAEE, 0x6692, 0x95A6,\n\t0x6693, 0x95A7, 0x6694, 0x95A8, 0x6695, 0x95A9, 0x6696, 0xC5AF,\n\t0x6697, 0xB0B5, 0x6698, 0x95AA, 0x6699, 0x95AB, 0x669A, 0x95AC,\n\t0x669B, 0x95AD, 0x669C, 0x95AE, 0x669D, 0xEAD4, 0x669E, 0x95AF,\n\t0x669F, 0x95B0, 0x66A0, 0x95B1, 0x66A1, 0x95B2, 0x66A2, 0x95B3,\n\t0x66A3, 0x95B4, 0x66A4, 0x95B5, 0x66A5, 0x95B6, 0x66A6, 0x95B7,\n\t0x66A7, 0xEAD3, 0x66A8, 0xF4DF, 0x66A9, 0x95B8, 0x66AA, 0x95B9,\n\t0x66AB, 0x95BA, 0x66AC, 0x95BB, 0x66AD, 0x95BC, 0x66AE, 0xC4BA,\n\t0x66AF, 0x95BD, 0x66B0, 0x95BE, 0x66B1, 0x95BF, 0x66B2, 0x95C0,\n\t0x66B3, 0x95C1, 0x66B4, 0xB1A9, 0x66B5, 0x95C2, 0x66B6, 0x95C3,\n\t0x66B7, 0x95C4, 0x66B8, 0x95C5, 0x66B9, 0xE5DF, 0x66BA, 0x95C6,\n\t0x66BB, 0x95C7, 0x66BC, 0x95C8, 0x66BD, 0x95C9, 0x66BE, 0xEAD5,\n\t0x66BF, 0x95CA, 0x66C0, 0x95CB, 0x66C1, 0x95CC, 0x66C2, 0x95CD,\n\t0x66C3, 0x95CE, 0x66C4, 0x95CF, 0x66C5, 0x95D0, 0x66C6, 0x95D1,\n\t0x66C7, 0x95D2, 0x66C8, 0x95D3, 0x66C9, 0x95D4, 0x66CA, 0x95D5,\n\t0x66CB, 0x95D6, 0x66CC, 0x95D7, 0x66CD, 0x95D8, 0x66CE, 0x95D9,\n\t0x66CF, 0x95DA, 0x66D0, 0x95DB, 0x66D1, 0x95DC, 0x66D2, 0x95DD,\n\t0x66D3, 0x95DE, 0x66D4, 0x95DF, 0x66D5, 0x95E0, 0x66D6, 0x95E1,\n\t0x66D7, 0x95E2, 0x66D8, 0x95E3, 0x66D9, 0xCAEF, 0x66DA, 0x95E4,\n\t0x66DB, 0xEAD6, 0x66DC, 0xEAD7, 0x66DD, 0xC6D8, 0x66DE, 0x95E5,\n\t0x66DF, 0x95E6, 0x66E0, 0x95E7, 0x66E1, 0x95E8, 0x66E2, 0x95E9,\n\t0x66E3, 0x95EA, 0x66E4, 0x95EB, 0x66E5, 0x95EC, 0x66E6, 0xEAD8,\n\t0x66E7, 0x95ED, 0x66E8, 0x95EE, 0x66E9, 0xEAD9, 0x66EA, 0x95EF,\n\t0x66EB, 0x95F0, 0x66EC, 0x95F1, 0x66ED, 0x95F2, 0x66EE, 0x95F3,\n\t0x66EF, 0x95F4, 0x66F0, 0xD4BB, 0x66F1, 0x95F5, 0x66F2, 0xC7FA,\n\t0x66F3, 0xD2B7, 0x66F4, 0xB8FC, 0x66F5, 0x95F6, 0x66F6, 0x95F7,\n\t0x66F7, 0xEAC2, 0x66F8, 0x95F8, 0x66F9, 0xB2DC, 0x66FA, 0x95F9,\n\t0x66FB, 0x95FA, 0x66FC, 0xC2FC, 0x66FD, 0x95FB, 0x66FE, 0xD4F8,\n\t0x66FF, 0xCCE6, 0x6700, 0xD7EE, 0x6701, 0x95FC, 0x6702, 0x95FD,\n\t0x6703, 0x95FE, 0x6704, 0x9640, 0x6705, 0x9641, 0x6706, 0x9642,\n\t0x6707, 0x9643, 0x6708, 0xD4C2, 0x6709, 0xD3D0, 0x670A, 0xEBC3,\n\t0x670B, 0xC5F3, 0x670C, 0x9644, 0x670D, 0xB7FE, 0x670E, 0x9645,\n\t0x670F, 0x9646, 0x6710, 0xEBD4, 0x6711, 0x9647, 0x6712, 0x9648,\n\t0x6713, 0x9649, 0x6714, 0xCBB7, 0x6715, 0xEBDE, 0x6716, 0x964A,\n\t0x6717, 0xC0CA, 0x6718, 0x964B, 0x6719, 0x964C, 0x671A, 0x964D,\n\t0x671B, 0xCDFB, 0x671C, 0x964E, 0x671D, 0xB3AF, 0x671E, 0x964F,\n\t0x671F, 0xC6DA, 0x6720, 0x9650, 0x6721, 0x9651, 0x6722, 0x9652,\n\t0x6723, 0x9653, 0x6724, 0x9654, 0x6725, 0x9655, 0x6726, 0xEBFC,\n\t0x6727, 0x9656, 0x6728, 0xC4BE, 0x6729, 0x9657, 0x672A, 0xCEB4,\n\t0x672B, 0xC4A9, 0x672C, 0xB1BE, 0x672D, 0xD4FD, 0x672E, 0x9658,\n\t0x672F, 0xCAF5, 0x6730, 0x9659, 0x6731, 0xD6EC, 0x6732, 0x965A,\n\t0x6733, 0x965B, 0x6734, 0xC6D3, 0x6735, 0xB6E4, 0x6736, 0x965C,\n\t0x6737, 0x965D, 0x6738, 0x965E, 0x6739, 0x965F, 0x673A, 0xBBFA,\n\t0x673B, 0x9660, 0x673C, 0x9661, 0x673D, 0xD0E0, 0x673E, 0x9662,\n\t0x673F, 0x9663, 0x6740, 0xC9B1, 0x6741, 0x9664, 0x6742, 0xD4D3,\n\t0x6743, 0xC8A8, 0x6744, 0x9665, 0x6745, 0x9666, 0x6746, 0xB8CB,\n\t0x6747, 0x9667, 0x6748, 0xE8BE, 0x6749, 0xC9BC, 0x674A, 0x9668,\n\t0x674B, 0x9669, 0x674C, 0xE8BB, 0x674D, 0x966A, 0x674E, 0xC0EE,\n\t0x674F, 0xD0D3, 0x6750, 0xB2C4, 0x6751, 0xB4E5, 0x6752, 0x966B,\n\t0x6753, 0xE8BC, 0x6754, 0x966C, 0x6755, 0x966D, 0x6756, 0xD5C8,\n\t0x6757, 0x966E, 0x6758, 0x966F, 0x6759, 0x9670, 0x675A, 0x9671,\n\t0x675B, 0x9672, 0x675C, 0xB6C5, 0x675D, 0x9673, 0x675E, 0xE8BD,\n\t0x675F, 0xCAF8, 0x6760, 0xB8DC, 0x6761, 0xCCF5, 0x6762, 0x9674,\n\t0x6763, 0x9675, 0x6764, 0x9676, 0x6765, 0xC0B4, 0x6766, 0x9677,\n\t0x6767, 0x9678, 0x6768, 0xD1EE, 0x6769, 0xE8BF, 0x676A, 0xE8C2,\n\t0x676B, 0x9679, 0x676C, 0x967A, 0x676D, 0xBABC, 0x676E, 0x967B,\n\t0x676F, 0xB1AD, 0x6770, 0xBDDC, 0x6771, 0x967C, 0x6772, 0xEABD,\n\t0x6773, 0xE8C3, 0x6774, 0x967D, 0x6775, 0xE8C6, 0x6776, 0x967E,\n\t0x6777, 0xE8CB, 0x6778, 0x9680, 0x6779, 0x9681, 0x677A, 0x9682,\n\t0x677B, 0x9683, 0x677C, 0xE8CC, 0x677D, 0x9684, 0x677E, 0xCBC9,\n\t0x677F, 0xB0E5, 0x6780, 0x9685, 0x6781, 0xBCAB, 0x6782, 0x9686,\n\t0x6783, 0x9687, 0x6784, 0xB9B9, 0x6785, 0x9688, 0x6786, 0x9689,\n\t0x6787, 0xE8C1, 0x6788, 0x968A, 0x6789, 0xCDF7, 0x678A, 0x968B,\n\t0x678B, 0xE8CA, 0x678C, 0x968C, 0x678D, 0x968D, 0x678E, 0x968E,\n\t0x678F, 0x968F, 0x6790, 0xCEF6, 0x6791, 0x9690, 0x6792, 0x9691,\n\t0x6793, 0x9692, 0x6794, 0x9693, 0x6795, 0xD5ED, 0x6796, 0x9694,\n\t0x6797, 0xC1D6, 0x6798, 0xE8C4, 0x6799, 0x9695, 0x679A, 0xC3B6,\n\t0x679B, 0x9696, 0x679C, 0xB9FB, 0x679D, 0xD6A6, 0x679E, 0xE8C8,\n\t0x679F, 0x9697, 0x67A0, 0x9698, 0x67A1, 0x9699, 0x67A2, 0xCAE0,\n\t0x67A3, 0xD4E6, 0x67A4, 0x969A, 0x67A5, 0xE8C0, 0x67A6, 0x969B,\n\t0x67A7, 0xE8C5, 0x67A8, 0xE8C7, 0x67A9, 0x969C, 0x67AA, 0xC7B9,\n\t0x67AB, 0xB7E3, 0x67AC, 0x969D, 0x67AD, 0xE8C9, 0x67AE, 0x969E,\n\t0x67AF, 0xBFDD, 0x67B0, 0xE8D2, 0x67B1, 0x969F, 0x67B2, 0x96A0,\n\t0x67B3, 0xE8D7, 0x67B4, 0x96A1, 0x67B5, 0xE8D5, 0x67B6, 0xBCDC,\n\t0x67B7, 0xBCCF, 0x67B8, 0xE8DB, 0x67B9, 0x96A2, 0x67BA, 0x96A3,\n\t0x67BB, 0x96A4, 0x67BC, 0x96A5, 0x67BD, 0x96A6, 0x67BE, 0x96A7,\n\t0x67BF, 0x96A8, 0x67C0, 0x96A9, 0x67C1, 0xE8DE, 0x67C2, 0x96AA,\n\t0x67C3, 0xE8DA, 0x67C4, 0xB1FA, 0x67C5, 0x96AB, 0x67C6, 0x96AC,\n\t0x67C7, 0x96AD, 0x67C8, 0x96AE, 0x67C9, 0x96AF, 0x67CA, 0x96B0,\n\t0x67CB, 0x96B1, 0x67CC, 0x96B2, 0x67CD, 0x96B3, 0x67CE, 0x96B4,\n\t0x67CF, 0xB0D8, 0x67D0, 0xC4B3, 0x67D1, 0xB8CC, 0x67D2, 0xC6E2,\n\t0x67D3, 0xC8BE, 0x67D4, 0xC8E1, 0x67D5, 0x96B5, 0x67D6, 0x96B6,\n\t0x67D7, 0x96B7, 0x67D8, 0xE8CF, 0x67D9, 0xE8D4, 0x67DA, 0xE8D6,\n\t0x67DB, 0x96B8, 0x67DC, 0xB9F1, 0x67DD, 0xE8D8, 0x67DE, 0xD7F5,\n\t0x67DF, 0x96B9, 0x67E0, 0xC4FB, 0x67E1, 0x96BA, 0x67E2, 0xE8DC,\n\t0x67E3, 0x96BB, 0x67E4, 0x96BC, 0x67E5, 0xB2E9, 0x67E6, 0x96BD,\n\t0x67E7, 0x96BE, 0x67E8, 0x96BF, 0x67E9, 0xE8D1, 0x67EA, 0x96C0,\n\t0x67EB, 0x96C1, 0x67EC, 0xBCED, 0x67ED, 0x96C2, 0x67EE, 0x96C3,\n\t0x67EF, 0xBFC2, 0x67F0, 0xE8CD, 0x67F1, 0xD6F9, 0x67F2, 0x96C4,\n\t0x67F3, 0xC1F8, 0x67F4, 0xB2F1, 0x67F5, 0x96C5, 0x67F6, 0x96C6,\n\t0x67F7, 0x96C7, 0x67F8, 0x96C8, 0x67F9, 0x96C9, 0x67FA, 0x96CA,\n\t0x67FB, 0x96CB, 0x67FC, 0x96CC, 0x67FD, 0xE8DF, 0x67FE, 0x96CD,\n\t0x67FF, 0xCAC1, 0x6800, 0xE8D9, 0x6801, 0x96CE, 0x6802, 0x96CF,\n\t0x6803, 0x96D0, 0x6804, 0x96D1, 0x6805, 0xD5A4, 0x6806, 0x96D2,\n\t0x6807, 0xB1EA, 0x6808, 0xD5BB, 0x6809, 0xE8CE, 0x680A, 0xE8D0,\n\t0x680B, 0xB6B0, 0x680C, 0xE8D3, 0x680D, 0x96D3, 0x680E, 0xE8DD,\n\t0x680F, 0xC0B8, 0x6810, 0x96D4, 0x6811, 0xCAF7, 0x6812, 0x96D5,\n\t0x6813, 0xCBA8, 0x6814, 0x96D6, 0x6815, 0x96D7, 0x6816, 0xC6DC,\n\t0x6817, 0xC0F5, 0x6818, 0x96D8, 0x6819, 0x96D9, 0x681A, 0x96DA,\n\t0x681B, 0x96DB, 0x681C, 0x96DC, 0x681D, 0xE8E9, 0x681E, 0x96DD,\n\t0x681F, 0x96DE, 0x6820, 0x96DF, 0x6821, 0xD0A3, 0x6822, 0x96E0,\n\t0x6823, 0x96E1, 0x6824, 0x96E2, 0x6825, 0x96E3, 0x6826, 0x96E4,\n\t0x6827, 0x96E5, 0x6828, 0x96E6, 0x6829, 0xE8F2, 0x682A, 0xD6EA,\n\t0x682B, 0x96E7, 0x682C, 0x96E8, 0x682D, 0x96E9, 0x682E, 0x96EA,\n\t0x682F, 0x96EB, 0x6830, 0x96EC, 0x6831, 0x96ED, 0x6832, 0xE8E0,\n\t0x6833, 0xE8E1, 0x6834, 0x96EE, 0x6835, 0x96EF, 0x6836, 0x96F0,\n\t0x6837, 0xD1F9, 0x6838, 0xBACB, 0x6839, 0xB8F9, 0x683A, 0x96F1,\n\t0x683B, 0x96F2, 0x683C, 0xB8F1, 0x683D, 0xD4D4, 0x683E, 0xE8EF,\n\t0x683F, 0x96F3, 0x6840, 0xE8EE, 0x6841, 0xE8EC, 0x6842, 0xB9F0,\n\t0x6843, 0xCCD2, 0x6844, 0xE8E6, 0x6845, 0xCEA6, 0x6846, 0xBFF2,\n\t0x6847, 0x96F4, 0x6848, 0xB0B8, 0x6849, 0xE8F1, 0x684A, 0xE8F0,\n\t0x684B, 0x96F5, 0x684C, 0xD7C0, 0x684D, 0x96F6, 0x684E, 0xE8E4,\n\t0x684F, 0x96F7, 0x6850, 0xCDA9, 0x6851, 0xC9A3, 0x6852, 0x96F8,\n\t0x6853, 0xBBB8, 0x6854, 0xBDDB, 0x6855, 0xE8EA, 0x6856, 0x96F9,\n\t0x6857, 0x96FA, 0x6858, 0x96FB, 0x6859, 0x96FC, 0x685A, 0x96FD,\n\t0x685B, 0x96FE, 0x685C, 0x9740, 0x685D, 0x9741, 0x685E, 0x9742,\n\t0x685F, 0x9743, 0x6860, 0xE8E2, 0x6861, 0xE8E3, 0x6862, 0xE8E5,\n\t0x6863, 0xB5B5, 0x6864, 0xE8E7, 0x6865, 0xC7C5, 0x6866, 0xE8EB,\n\t0x6867, 0xE8ED, 0x6868, 0xBDB0, 0x6869, 0xD7AE, 0x686A, 0x9744,\n\t0x686B, 0xE8F8, 0x686C, 0x9745, 0x686D, 0x9746, 0x686E, 0x9747,\n\t0x686F, 0x9748, 0x6870, 0x9749, 0x6871, 0x974A, 0x6872, 0x974B,\n\t0x6873, 0x974C, 0x6874, 0xE8F5, 0x6875, 0x974D, 0x6876, 0xCDB0,\n\t0x6877, 0xE8F6, 0x6878, 0x974E, 0x6879, 0x974F, 0x687A, 0x9750,\n\t0x687B, 0x9751, 0x687C, 0x9752, 0x687D, 0x9753, 0x687E, 0x9754,\n\t0x687F, 0x9755, 0x6880, 0x9756, 0x6881, 0xC1BA, 0x6882, 0x9757,\n\t0x6883, 0xE8E8, 0x6884, 0x9758, 0x6885, 0xC3B7, 0x6886, 0xB0F0,\n\t0x6887, 0x9759, 0x6888, 0x975A, 0x6889, 0x975B, 0x688A, 0x975C,\n\t0x688B, 0x975D, 0x688C, 0x975E, 0x688D, 0x975F, 0x688E, 0x9760,\n\t0x688F, 0xE8F4, 0x6890, 0x9761, 0x6891, 0x9762, 0x6892, 0x9763,\n\t0x6893, 0xE8F7, 0x6894, 0x9764, 0x6895, 0x9765, 0x6896, 0x9766,\n\t0x6897, 0xB9A3, 0x6898, 0x9767, 0x6899, 0x9768, 0x689A, 0x9769,\n\t0x689B, 0x976A, 0x689C, 0x976B, 0x689D, 0x976C, 0x689E, 0x976D,\n\t0x689F, 0x976E, 0x68A0, 0x976F, 0x68A1, 0x9770, 0x68A2, 0xC9D2,\n\t0x68A3, 0x9771, 0x68A4, 0x9772, 0x68A5, 0x9773, 0x68A6, 0xC3CE,\n\t0x68A7, 0xCEE0, 0x68A8, 0xC0E6, 0x68A9, 0x9774, 0x68AA, 0x9775,\n\t0x68AB, 0x9776, 0x68AC, 0x9777, 0x68AD, 0xCBF3, 0x68AE, 0x9778,\n\t0x68AF, 0xCCDD, 0x68B0, 0xD0B5, 0x68B1, 0x9779, 0x68B2, 0x977A,\n\t0x68B3, 0xCAE1, 0x68B4, 0x977B, 0x68B5, 0xE8F3, 0x68B6, 0x977C,\n\t0x68B7, 0x977D, 0x68B8, 0x977E, 0x68B9, 0x9780, 0x68BA, 0x9781,\n\t0x68BB, 0x9782, 0x68BC, 0x9783, 0x68BD, 0x9784, 0x68BE, 0x9785,\n\t0x68BF, 0x9786, 0x68C0, 0xBCEC, 0x68C1, 0x9787, 0x68C2, 0xE8F9,\n\t0x68C3, 0x9788, 0x68C4, 0x9789, 0x68C5, 0x978A, 0x68C6, 0x978B,\n\t0x68C7, 0x978C, 0x68C8, 0x978D, 0x68C9, 0xC3DE, 0x68CA, 0x978E,\n\t0x68CB, 0xC6E5, 0x68CC, 0x978F, 0x68CD, 0xB9F7, 0x68CE, 0x9790,\n\t0x68CF, 0x9791, 0x68D0, 0x9792, 0x68D1, 0x9793, 0x68D2, 0xB0F4,\n\t0x68D3, 0x9794, 0x68D4, 0x9795, 0x68D5, 0xD7D8, 0x68D6, 0x9796,\n\t0x68D7, 0x9797, 0x68D8, 0xBCAC, 0x68D9, 0x9798, 0x68DA, 0xC5EF,\n\t0x68DB, 0x9799, 0x68DC, 0x979A, 0x68DD, 0x979B, 0x68DE, 0x979C,\n\t0x68DF, 0x979D, 0x68E0, 0xCCC4, 0x68E1, 0x979E, 0x68E2, 0x979F,\n\t0x68E3, 0xE9A6, 0x68E4, 0x97A0, 0x68E5, 0x97A1, 0x68E6, 0x97A2,\n\t0x68E7, 0x97A3, 0x68E8, 0x97A4, 0x68E9, 0x97A5, 0x68EA, 0x97A6,\n\t0x68EB, 0x97A7, 0x68EC, 0x97A8, 0x68ED, 0x97A9, 0x68EE, 0xC9AD,\n\t0x68EF, 0x97AA, 0x68F0, 0xE9A2, 0x68F1, 0xC0E2, 0x68F2, 0x97AB,\n\t0x68F3, 0x97AC, 0x68F4, 0x97AD, 0x68F5, 0xBFC3, 0x68F6, 0x97AE,\n\t0x68F7, 0x97AF, 0x68F8, 0x97B0, 0x68F9, 0xE8FE, 0x68FA, 0xB9D7,\n\t0x68FB, 0x97B1, 0x68FC, 0xE8FB, 0x68FD, 0x97B2, 0x68FE, 0x97B3,\n\t0x68FF, 0x97B4, 0x6900, 0x97B5, 0x6901, 0xE9A4, 0x6902, 0x97B6,\n\t0x6903, 0x97B7, 0x6904, 0x97B8, 0x6905, 0xD2CE, 0x6906, 0x97B9,\n\t0x6907, 0x97BA, 0x6908, 0x97BB, 0x6909, 0x97BC, 0x690A, 0x97BD,\n\t0x690B, 0xE9A3, 0x690C, 0x97BE, 0x690D, 0xD6B2, 0x690E, 0xD7B5,\n\t0x690F, 0x97BF, 0x6910, 0xE9A7, 0x6911, 0x97C0, 0x6912, 0xBDB7,\n\t0x6913, 0x97C1, 0x6914, 0x97C2, 0x6915, 0x97C3, 0x6916, 0x97C4,\n\t0x6917, 0x97C5, 0x6918, 0x97C6, 0x6919, 0x97C7, 0x691A, 0x97C8,\n\t0x691B, 0x97C9, 0x691C, 0x97CA, 0x691D, 0x97CB, 0x691E, 0x97CC,\n\t0x691F, 0xE8FC, 0x6920, 0xE8FD, 0x6921, 0x97CD, 0x6922, 0x97CE,\n\t0x6923, 0x97CF, 0x6924, 0xE9A1, 0x6925, 0x97D0, 0x6926, 0x97D1,\n\t0x6927, 0x97D2, 0x6928, 0x97D3, 0x6929, 0x97D4, 0x692A, 0x97D5,\n\t0x692B, 0x97D6, 0x692C, 0x97D7, 0x692D, 0xCDD6, 0x692E, 0x97D8,\n\t0x692F, 0x97D9, 0x6930, 0xD2AC, 0x6931, 0x97DA, 0x6932, 0x97DB,\n\t0x6933, 0x97DC, 0x6934, 0xE9B2, 0x6935, 0x97DD, 0x6936, 0x97DE,\n\t0x6937, 0x97DF, 0x6938, 0x97E0, 0x6939, 0xE9A9, 0x693A, 0x97E1,\n\t0x693B, 0x97E2, 0x693C, 0x97E3, 0x693D, 0xB4AA, 0x693E, 0x97E4,\n\t0x693F, 0xB4BB, 0x6940, 0x97E5, 0x6941, 0x97E6, 0x6942, 0xE9AB,\n\t0x6943, 0x97E7, 0x6944, 0x97E8, 0x6945, 0x97E9, 0x6946, 0x97EA,\n\t0x6947, 0x97EB, 0x6948, 0x97EC, 0x6949, 0x97ED, 0x694A, 0x97EE,\n\t0x694B, 0x97EF, 0x694C, 0x97F0, 0x694D, 0x97F1, 0x694E, 0x97F2,\n\t0x694F, 0x97F3, 0x6950, 0x97F4, 0x6951, 0x97F5, 0x6952, 0x97F6,\n\t0x6953, 0x97F7, 0x6954, 0xD0A8, 0x6955, 0x97F8, 0x6956, 0x97F9,\n\t0x6957, 0xE9A5, 0x6958, 0x97FA, 0x6959, 0x97FB, 0x695A, 0xB3FE,\n\t0x695B, 0x97FC, 0x695C, 0x97FD, 0x695D, 0xE9AC, 0x695E, 0xC0E3,\n\t0x695F, 0x97FE, 0x6960, 0xE9AA, 0x6961, 0x9840, 0x6962, 0x9841,\n\t0x6963, 0xE9B9, 0x6964, 0x9842, 0x6965, 0x9843, 0x6966, 0xE9B8,\n\t0x6967, 0x9844, 0x6968, 0x9845, 0x6969, 0x9846, 0x696A, 0x9847,\n\t0x696B, 0xE9AE, 0x696C, 0x9848, 0x696D, 0x9849, 0x696E, 0xE8FA,\n\t0x696F, 0x984A, 0x6970, 0x984B, 0x6971, 0xE9A8, 0x6972, 0x984C,\n\t0x6973, 0x984D, 0x6974, 0x984E, 0x6975, 0x984F, 0x6976, 0x9850,\n\t0x6977, 0xBFAC, 0x6978, 0xE9B1, 0x6979, 0xE9BA, 0x697A, 0x9851,\n\t0x697B, 0x9852, 0x697C, 0xC2A5, 0x697D, 0x9853, 0x697E, 0x9854,\n\t0x697F, 0x9855, 0x6980, 0xE9AF, 0x6981, 0x9856, 0x6982, 0xB8C5,\n\t0x6983, 0x9857, 0x6984, 0xE9AD, 0x6985, 0x9858, 0x6986, 0xD3DC,\n\t0x6987, 0xE9B4, 0x6988, 0xE9B5, 0x6989, 0xE9B7, 0x698A, 0x9859,\n\t0x698B, 0x985A, 0x698C, 0x985B, 0x698D, 0xE9C7, 0x698E, 0x985C,\n\t0x698F, 0x985D, 0x6990, 0x985E, 0x6991, 0x985F, 0x6992, 0x9860,\n\t0x6993, 0x9861, 0x6994, 0xC0C6, 0x6995, 0xE9C5, 0x6996, 0x9862,\n\t0x6997, 0x9863, 0x6998, 0xE9B0, 0x6999, 0x9864, 0x699A, 0x9865,\n\t0x699B, 0xE9BB, 0x699C, 0xB0F1, 0x699D, 0x9866, 0x699E, 0x9867,\n\t0x699F, 0x9868, 0x69A0, 0x9869, 0x69A1, 0x986A, 0x69A2, 0x986B,\n\t0x69A3, 0x986C, 0x69A4, 0x986D, 0x69A5, 0x986E, 0x69A6, 0x986F,\n\t0x69A7, 0xE9BC, 0x69A8, 0xD5A5, 0x69A9, 0x9870, 0x69AA, 0x9871,\n\t0x69AB, 0xE9BE, 0x69AC, 0x9872, 0x69AD, 0xE9BF, 0x69AE, 0x9873,\n\t0x69AF, 0x9874, 0x69B0, 0x9875, 0x69B1, 0xE9C1, 0x69B2, 0x9876,\n\t0x69B3, 0x9877, 0x69B4, 0xC1F1, 0x69B5, 0x9878, 0x69B6, 0x9879,\n\t0x69B7, 0xC8B6, 0x69B8, 0x987A, 0x69B9, 0x987B, 0x69BA, 0x987C,\n\t0x69BB, 0xE9BD, 0x69BC, 0x987D, 0x69BD, 0x987E, 0x69BE, 0x9880,\n\t0x69BF, 0x9881, 0x69C0, 0x9882, 0x69C1, 0xE9C2, 0x69C2, 0x9883,\n\t0x69C3, 0x9884, 0x69C4, 0x9885, 0x69C5, 0x9886, 0x69C6, 0x9887,\n\t0x69C7, 0x9888, 0x69C8, 0x9889, 0x69C9, 0x988A, 0x69CA, 0xE9C3,\n\t0x69CB, 0x988B, 0x69CC, 0xE9B3, 0x69CD, 0x988C, 0x69CE, 0xE9B6,\n\t0x69CF, 0x988D, 0x69D0, 0xBBB1, 0x69D1, 0x988E, 0x69D2, 0x988F,\n\t0x69D3, 0x9890, 0x69D4, 0xE9C0, 0x69D5, 0x9891, 0x69D6, 0x9892,\n\t0x69D7, 0x9893, 0x69D8, 0x9894, 0x69D9, 0x9895, 0x69DA, 0x9896,\n\t0x69DB, 0xBCF7, 0x69DC, 0x9897, 0x69DD, 0x9898, 0x69DE, 0x9899,\n\t0x69DF, 0xE9C4, 0x69E0, 0xE9C6, 0x69E1, 0x989A, 0x69E2, 0x989B,\n\t0x69E3, 0x989C, 0x69E4, 0x989D, 0x69E5, 0x989E, 0x69E6, 0x989F,\n\t0x69E7, 0x98A0, 0x69E8, 0x98A1, 0x69E9, 0x98A2, 0x69EA, 0x98A3,\n\t0x69EB, 0x98A4, 0x69EC, 0x98A5, 0x69ED, 0xE9CA, 0x69EE, 0x98A6,\n\t0x69EF, 0x98A7, 0x69F0, 0x98A8, 0x69F1, 0x98A9, 0x69F2, 0xE9CE,\n\t0x69F3, 0x98AA, 0x69F4, 0x98AB, 0x69F5, 0x98AC, 0x69F6, 0x98AD,\n\t0x69F7, 0x98AE, 0x69F8, 0x98AF, 0x69F9, 0x98B0, 0x69FA, 0x98B1,\n\t0x69FB, 0x98B2, 0x69FC, 0x98B3, 0x69FD, 0xB2DB, 0x69FE, 0x98B4,\n\t0x69FF, 0xE9C8, 0x6A00, 0x98B5, 0x6A01, 0x98B6, 0x6A02, 0x98B7,\n\t0x6A03, 0x98B8, 0x6A04, 0x98B9, 0x6A05, 0x98BA, 0x6A06, 0x98BB,\n\t0x6A07, 0x98BC, 0x6A08, 0x98BD, 0x6A09, 0x98BE, 0x6A0A, 0xB7AE,\n\t0x6A0B, 0x98BF, 0x6A0C, 0x98C0, 0x6A0D, 0x98C1, 0x6A0E, 0x98C2,\n\t0x6A0F, 0x98C3, 0x6A10, 0x98C4, 0x6A11, 0x98C5, 0x6A12, 0x98C6,\n\t0x6A13, 0x98C7, 0x6A14, 0x98C8, 0x6A15, 0x98C9, 0x6A16, 0x98CA,\n\t0x6A17, 0xE9CB, 0x6A18, 0xE9CC, 0x6A19, 0x98CB, 0x6A1A, 0x98CC,\n\t0x6A1B, 0x98CD, 0x6A1C, 0x98CE, 0x6A1D, 0x98CF, 0x6A1E, 0x98D0,\n\t0x6A1F, 0xD5C1, 0x6A20, 0x98D1, 0x6A21, 0xC4A3, 0x6A22, 0x98D2,\n\t0x6A23, 0x98D3, 0x6A24, 0x98D4, 0x6A25, 0x98D5, 0x6A26, 0x98D6,\n\t0x6A27, 0x98D7, 0x6A28, 0xE9D8, 0x6A29, 0x98D8, 0x6A2A, 0xBAE1,\n\t0x6A2B, 0x98D9, 0x6A2C, 0x98DA, 0x6A2D, 0x98DB, 0x6A2E, 0x98DC,\n\t0x6A2F, 0xE9C9, 0x6A30, 0x98DD, 0x6A31, 0xD3A3, 0x6A32, 0x98DE,\n\t0x6A33, 0x98DF, 0x6A34, 0x98E0, 0x6A35, 0xE9D4, 0x6A36, 0x98E1,\n\t0x6A37, 0x98E2, 0x6A38, 0x98E3, 0x6A39, 0x98E4, 0x6A3A, 0x98E5,\n\t0x6A3B, 0x98E6, 0x6A3C, 0x98E7, 0x6A3D, 0xE9D7, 0x6A3E, 0xE9D0,\n\t0x6A3F, 0x98E8, 0x6A40, 0x98E9, 0x6A41, 0x98EA, 0x6A42, 0x98EB,\n\t0x6A43, 0x98EC, 0x6A44, 0xE9CF, 0x6A45, 0x98ED, 0x6A46, 0x98EE,\n\t0x6A47, 0xC7C1, 0x6A48, 0x98EF, 0x6A49, 0x98F0, 0x6A4A, 0x98F1,\n\t0x6A4B, 0x98F2, 0x6A4C, 0x98F3, 0x6A4D, 0x98F4, 0x6A4E, 0x98F5,\n\t0x6A4F, 0x98F6, 0x6A50, 0xE9D2, 0x6A51, 0x98F7, 0x6A52, 0x98F8,\n\t0x6A53, 0x98F9, 0x6A54, 0x98FA, 0x6A55, 0x98FB, 0x6A56, 0x98FC,\n\t0x6A57, 0x98FD, 0x6A58, 0xE9D9, 0x6A59, 0xB3C8, 0x6A5A, 0x98FE,\n\t0x6A5B, 0xE9D3, 0x6A5C, 0x9940, 0x6A5D, 0x9941, 0x6A5E, 0x9942,\n\t0x6A5F, 0x9943, 0x6A60, 0x9944, 0x6A61, 0xCFF0, 0x6A62, 0x9945,\n\t0x6A63, 0x9946, 0x6A64, 0x9947, 0x6A65, 0xE9CD, 0x6A66, 0x9948,\n\t0x6A67, 0x9949, 0x6A68, 0x994A, 0x6A69, 0x994B, 0x6A6A, 0x994C,\n\t0x6A6B, 0x994D, 0x6A6C, 0x994E, 0x6A6D, 0x994F, 0x6A6E, 0x9950,\n\t0x6A6F, 0x9951, 0x6A70, 0x9952, 0x6A71, 0xB3F7, 0x6A72, 0x9953,\n\t0x6A73, 0x9954, 0x6A74, 0x9955, 0x6A75, 0x9956, 0x6A76, 0x9957,\n\t0x6A77, 0x9958, 0x6A78, 0x9959, 0x6A79, 0xE9D6, 0x6A7A, 0x995A,\n\t0x6A7B, 0x995B, 0x6A7C, 0xE9DA, 0x6A7D, 0x995C, 0x6A7E, 0x995D,\n\t0x6A7F, 0x995E, 0x6A80, 0xCCB4, 0x6A81, 0x995F, 0x6A82, 0x9960,\n\t0x6A83, 0x9961, 0x6A84, 0xCFAD, 0x6A85, 0x9962, 0x6A86, 0x9963,\n\t0x6A87, 0x9964, 0x6A88, 0x9965, 0x6A89, 0x9966, 0x6A8A, 0x9967,\n\t0x6A8B, 0x9968, 0x6A8C, 0x9969, 0x6A8D, 0x996A, 0x6A8E, 0xE9D5,\n\t0x6A8F, 0x996B, 0x6A90, 0xE9DC, 0x6A91, 0xE9DB, 0x6A92, 0x996C,\n\t0x6A93, 0x996D, 0x6A94, 0x996E, 0x6A95, 0x996F, 0x6A96, 0x9970,\n\t0x6A97, 0xE9DE, 0x6A98, 0x9971, 0x6A99, 0x9972, 0x6A9A, 0x9973,\n\t0x6A9B, 0x9974, 0x6A9C, 0x9975, 0x6A9D, 0x9976, 0x6A9E, 0x9977,\n\t0x6A9F, 0x9978, 0x6AA0, 0xE9D1, 0x6AA1, 0x9979, 0x6AA2, 0x997A,\n\t0x6AA3, 0x997B, 0x6AA4, 0x997C, 0x6AA5, 0x997D, 0x6AA6, 0x997E,\n\t0x6AA7, 0x9980, 0x6AA8, 0x9981, 0x6AA9, 0xE9DD, 0x6AAA, 0x9982,\n\t0x6AAB, 0xE9DF, 0x6AAC, 0xC3CA, 0x6AAD, 0x9983, 0x6AAE, 0x9984,\n\t0x6AAF, 0x9985, 0x6AB0, 0x9986, 0x6AB1, 0x9987, 0x6AB2, 0x9988,\n\t0x6AB3, 0x9989, 0x6AB4, 0x998A, 0x6AB5, 0x998B, 0x6AB6, 0x998C,\n\t0x6AB7, 0x998D, 0x6AB8, 0x998E, 0x6AB9, 0x998F, 0x6ABA, 0x9990,\n\t0x6ABB, 0x9991, 0x6ABC, 0x9992, 0x6ABD, 0x9993, 0x6ABE, 0x9994,\n\t0x6ABF, 0x9995, 0x6AC0, 0x9996, 0x6AC1, 0x9997, 0x6AC2, 0x9998,\n\t0x6AC3, 0x9999, 0x6AC4, 0x999A, 0x6AC5, 0x999B, 0x6AC6, 0x999C,\n\t0x6AC7, 0x999D, 0x6AC8, 0x999E, 0x6AC9, 0x999F, 0x6ACA, 0x99A0,\n\t0x6ACB, 0x99A1, 0x6ACC, 0x99A2, 0x6ACD, 0x99A3, 0x6ACE, 0x99A4,\n\t0x6ACF, 0x99A5, 0x6AD0, 0x99A6, 0x6AD1, 0x99A7, 0x6AD2, 0x99A8,\n\t0x6AD3, 0x99A9, 0x6AD4, 0x99AA, 0x6AD5, 0x99AB, 0x6AD6, 0x99AC,\n\t0x6AD7, 0x99AD, 0x6AD8, 0x99AE, 0x6AD9, 0x99AF, 0x6ADA, 0x99B0,\n\t0x6ADB, 0x99B1, 0x6ADC, 0x99B2, 0x6ADD, 0x99B3, 0x6ADE, 0x99B4,\n\t0x6ADF, 0x99B5, 0x6AE0, 0x99B6, 0x6AE1, 0x99B7, 0x6AE2, 0x99B8,\n\t0x6AE3, 0x99B9, 0x6AE4, 0x99BA, 0x6AE5, 0x99BB, 0x6AE6, 0x99BC,\n\t0x6AE7, 0x99BD, 0x6AE8, 0x99BE, 0x6AE9, 0x99BF, 0x6AEA, 0x99C0,\n\t0x6AEB, 0x99C1, 0x6AEC, 0x99C2, 0x6AED, 0x99C3, 0x6AEE, 0x99C4,\n\t0x6AEF, 0x99C5, 0x6AF0, 0x99C6, 0x6AF1, 0x99C7, 0x6AF2, 0x99C8,\n\t0x6AF3, 0x99C9, 0x6AF4, 0x99CA, 0x6AF5, 0x99CB, 0x6AF6, 0x99CC,\n\t0x6AF7, 0x99CD, 0x6AF8, 0x99CE, 0x6AF9, 0x99CF, 0x6AFA, 0x99D0,\n\t0x6AFB, 0x99D1, 0x6AFC, 0x99D2, 0x6AFD, 0x99D3, 0x6AFE, 0x99D4,\n\t0x6AFF, 0x99D5, 0x6B00, 0x99D6, 0x6B01, 0x99D7, 0x6B02, 0x99D8,\n\t0x6B03, 0x99D9, 0x6B04, 0x99DA, 0x6B05, 0x99DB, 0x6B06, 0x99DC,\n\t0x6B07, 0x99DD, 0x6B08, 0x99DE, 0x6B09, 0x99DF, 0x6B0A, 0x99E0,\n\t0x6B0B, 0x99E1, 0x6B0C, 0x99E2, 0x6B0D, 0x99E3, 0x6B0E, 0x99E4,\n\t0x6B0F, 0x99E5, 0x6B10, 0x99E6, 0x6B11, 0x99E7, 0x6B12, 0x99E8,\n\t0x6B13, 0x99E9, 0x6B14, 0x99EA, 0x6B15, 0x99EB, 0x6B16, 0x99EC,\n\t0x6B17, 0x99ED, 0x6B18, 0x99EE, 0x6B19, 0x99EF, 0x6B1A, 0x99F0,\n\t0x6B1B, 0x99F1, 0x6B1C, 0x99F2, 0x6B1D, 0x99F3, 0x6B1E, 0x99F4,\n\t0x6B1F, 0x99F5, 0x6B20, 0xC7B7, 0x6B21, 0xB4CE, 0x6B22, 0xBBB6,\n\t0x6B23, 0xD0C0, 0x6B24, 0xECA3, 0x6B25, 0x99F6, 0x6B26, 0x99F7,\n\t0x6B27, 0xC5B7, 0x6B28, 0x99F8, 0x6B29, 0x99F9, 0x6B2A, 0x99FA,\n\t0x6B2B, 0x99FB, 0x6B2C, 0x99FC, 0x6B2D, 0x99FD, 0x6B2E, 0x99FE,\n\t0x6B2F, 0x9A40, 0x6B30, 0x9A41, 0x6B31, 0x9A42, 0x6B32, 0xD3FB,\n\t0x6B33, 0x9A43, 0x6B34, 0x9A44, 0x6B35, 0x9A45, 0x6B36, 0x9A46,\n\t0x6B37, 0xECA4, 0x6B38, 0x9A47, 0x6B39, 0xECA5, 0x6B3A, 0xC6DB,\n\t0x6B3B, 0x9A48, 0x6B3C, 0x9A49, 0x6B3D, 0x9A4A, 0x6B3E, 0xBFEE,\n\t0x6B3F, 0x9A4B, 0x6B40, 0x9A4C, 0x6B41, 0x9A4D, 0x6B42, 0x9A4E,\n\t0x6B43, 0xECA6, 0x6B44, 0x9A4F, 0x6B45, 0x9A50, 0x6B46, 0xECA7,\n\t0x6B47, 0xD0AA, 0x6B48, 0x9A51, 0x6B49, 0xC7B8, 0x6B4A, 0x9A52,\n\t0x6B4B, 0x9A53, 0x6B4C, 0xB8E8, 0x6B4D, 0x9A54, 0x6B4E, 0x9A55,\n\t0x6B4F, 0x9A56, 0x6B50, 0x9A57, 0x6B51, 0x9A58, 0x6B52, 0x9A59,\n\t0x6B53, 0x9A5A, 0x6B54, 0x9A5B, 0x6B55, 0x9A5C, 0x6B56, 0x9A5D,\n\t0x6B57, 0x9A5E, 0x6B58, 0x9A5F, 0x6B59, 0xECA8, 0x6B5A, 0x9A60,\n\t0x6B5B, 0x9A61, 0x6B5C, 0x9A62, 0x6B5D, 0x9A63, 0x6B5E, 0x9A64,\n\t0x6B5F, 0x9A65, 0x6B60, 0x9A66, 0x6B61, 0x9A67, 0x6B62, 0xD6B9,\n\t0x6B63, 0xD5FD, 0x6B64, 0xB4CB, 0x6B65, 0xB2BD, 0x6B66, 0xCEE4,\n\t0x6B67, 0xC6E7, 0x6B68, 0x9A68, 0x6B69, 0x9A69, 0x6B6A, 0xCDE1,\n\t0x6B6B, 0x9A6A, 0x6B6C, 0x9A6B, 0x6B6D, 0x9A6C, 0x6B6E, 0x9A6D,\n\t0x6B6F, 0x9A6E, 0x6B70, 0x9A6F, 0x6B71, 0x9A70, 0x6B72, 0x9A71,\n\t0x6B73, 0x9A72, 0x6B74, 0x9A73, 0x6B75, 0x9A74, 0x6B76, 0x9A75,\n\t0x6B77, 0x9A76, 0x6B78, 0x9A77, 0x6B79, 0xB4F5, 0x6B7A, 0x9A78,\n\t0x6B7B, 0xCBC0, 0x6B7C, 0xBCDF, 0x6B7D, 0x9A79, 0x6B7E, 0x9A7A,\n\t0x6B7F, 0x9A7B, 0x6B80, 0x9A7C, 0x6B81, 0xE9E2, 0x6B82, 0xE9E3,\n\t0x6B83, 0xD1EA, 0x6B84, 0xE9E5, 0x6B85, 0x9A7D, 0x6B86, 0xB4F9,\n\t0x6B87, 0xE9E4, 0x6B88, 0x9A7E, 0x6B89, 0xD1B3, 0x6B8A, 0xCAE2,\n\t0x6B8B, 0xB2D0, 0x6B8C, 0x9A80, 0x6B8D, 0xE9E8, 0x6B8E, 0x9A81,\n\t0x6B8F, 0x9A82, 0x6B90, 0x9A83, 0x6B91, 0x9A84, 0x6B92, 0xE9E6,\n\t0x6B93, 0xE9E7, 0x6B94, 0x9A85, 0x6B95, 0x9A86, 0x6B96, 0xD6B3,\n\t0x6B97, 0x9A87, 0x6B98, 0x9A88, 0x6B99, 0x9A89, 0x6B9A, 0xE9E9,\n\t0x6B9B, 0xE9EA, 0x6B9C, 0x9A8A, 0x6B9D, 0x9A8B, 0x6B9E, 0x9A8C,\n\t0x6B9F, 0x9A8D, 0x6BA0, 0x9A8E, 0x6BA1, 0xE9EB, 0x6BA2, 0x9A8F,\n\t0x6BA3, 0x9A90, 0x6BA4, 0x9A91, 0x6BA5, 0x9A92, 0x6BA6, 0x9A93,\n\t0x6BA7, 0x9A94, 0x6BA8, 0x9A95, 0x6BA9, 0x9A96, 0x6BAA, 0xE9EC,\n\t0x6BAB, 0x9A97, 0x6BAC, 0x9A98, 0x6BAD, 0x9A99, 0x6BAE, 0x9A9A,\n\t0x6BAF, 0x9A9B, 0x6BB0, 0x9A9C, 0x6BB1, 0x9A9D, 0x6BB2, 0x9A9E,\n\t0x6BB3, 0xECAF, 0x6BB4, 0xC5B9, 0x6BB5, 0xB6CE, 0x6BB6, 0x9A9F,\n\t0x6BB7, 0xD2F3, 0x6BB8, 0x9AA0, 0x6BB9, 0x9AA1, 0x6BBA, 0x9AA2,\n\t0x6BBB, 0x9AA3, 0x6BBC, 0x9AA4, 0x6BBD, 0x9AA5, 0x6BBE, 0x9AA6,\n\t0x6BBF, 0xB5EE, 0x6BC0, 0x9AA7, 0x6BC1, 0xBBD9, 0x6BC2, 0xECB1,\n\t0x6BC3, 0x9AA8, 0x6BC4, 0x9AA9, 0x6BC5, 0xD2E3, 0x6BC6, 0x9AAA,\n\t0x6BC7, 0x9AAB, 0x6BC8, 0x9AAC, 0x6BC9, 0x9AAD, 0x6BCA, 0x9AAE,\n\t0x6BCB, 0xCEE3, 0x6BCC, 0x9AAF, 0x6BCD, 0xC4B8, 0x6BCE, 0x9AB0,\n\t0x6BCF, 0xC3BF, 0x6BD0, 0x9AB1, 0x6BD1, 0x9AB2, 0x6BD2, 0xB6BE,\n\t0x6BD3, 0xD8B9, 0x6BD4, 0xB1C8, 0x6BD5, 0xB1CF, 0x6BD6, 0xB1D1,\n\t0x6BD7, 0xC5FE, 0x6BD8, 0x9AB3, 0x6BD9, 0xB1D0, 0x6BDA, 0x9AB4,\n\t0x6BDB, 0xC3AB, 0x6BDC, 0x9AB5, 0x6BDD, 0x9AB6, 0x6BDE, 0x9AB7,\n\t0x6BDF, 0x9AB8, 0x6BE0, 0x9AB9, 0x6BE1, 0xD5B1, 0x6BE2, 0x9ABA,\n\t0x6BE3, 0x9ABB, 0x6BE4, 0x9ABC, 0x6BE5, 0x9ABD, 0x6BE6, 0x9ABE,\n\t0x6BE7, 0x9ABF, 0x6BE8, 0x9AC0, 0x6BE9, 0x9AC1, 0x6BEA, 0xEBA4,\n\t0x6BEB, 0xBAC1, 0x6BEC, 0x9AC2, 0x6BED, 0x9AC3, 0x6BEE, 0x9AC4,\n\t0x6BEF, 0xCCBA, 0x6BF0, 0x9AC5, 0x6BF1, 0x9AC6, 0x6BF2, 0x9AC7,\n\t0x6BF3, 0xEBA5, 0x6BF4, 0x9AC8, 0x6BF5, 0xEBA7, 0x6BF6, 0x9AC9,\n\t0x6BF7, 0x9ACA, 0x6BF8, 0x9ACB, 0x6BF9, 0xEBA8, 0x6BFA, 0x9ACC,\n\t0x6BFB, 0x9ACD, 0x6BFC, 0x9ACE, 0x6BFD, 0xEBA6, 0x6BFE, 0x9ACF,\n\t0x6BFF, 0x9AD0, 0x6C00, 0x9AD1, 0x6C01, 0x9AD2, 0x6C02, 0x9AD3,\n\t0x6C03, 0x9AD4, 0x6C04, 0x9AD5, 0x6C05, 0xEBA9, 0x6C06, 0xEBAB,\n\t0x6C07, 0xEBAA, 0x6C08, 0x9AD6, 0x6C09, 0x9AD7, 0x6C0A, 0x9AD8,\n\t0x6C0B, 0x9AD9, 0x6C0C, 0x9ADA, 0x6C0D, 0xEBAC, 0x6C0E, 0x9ADB,\n\t0x6C0F, 0xCACF, 0x6C10, 0xD8B5, 0x6C11, 0xC3F1, 0x6C12, 0x9ADC,\n\t0x6C13, 0xC3A5, 0x6C14, 0xC6F8, 0x6C15, 0xEBAD, 0x6C16, 0xC4CA,\n\t0x6C17, 0x9ADD, 0x6C18, 0xEBAE, 0x6C19, 0xEBAF, 0x6C1A, 0xEBB0,\n\t0x6C1B, 0xB7D5, 0x6C1C, 0x9ADE, 0x6C1D, 0x9ADF, 0x6C1E, 0x9AE0,\n\t0x6C1F, 0xB7FA, 0x6C20, 0x9AE1, 0x6C21, 0xEBB1, 0x6C22, 0xC7E2,\n\t0x6C23, 0x9AE2, 0x6C24, 0xEBB3, 0x6C25, 0x9AE3, 0x6C26, 0xBAA4,\n\t0x6C27, 0xD1F5, 0x6C28, 0xB0B1, 0x6C29, 0xEBB2, 0x6C2A, 0xEBB4,\n\t0x6C2B, 0x9AE4, 0x6C2C, 0x9AE5, 0x6C2D, 0x9AE6, 0x6C2E, 0xB5AA,\n\t0x6C2F, 0xC2C8, 0x6C30, 0xC7E8, 0x6C31, 0x9AE7, 0x6C32, 0xEBB5,\n\t0x6C33, 0x9AE8, 0x6C34, 0xCBAE, 0x6C35, 0xE3DF, 0x6C36, 0x9AE9,\n\t0x6C37, 0x9AEA, 0x6C38, 0xD3C0, 0x6C39, 0x9AEB, 0x6C3A, 0x9AEC,\n\t0x6C3B, 0x9AED, 0x6C3C, 0x9AEE, 0x6C3D, 0xD9DB, 0x6C3E, 0x9AEF,\n\t0x6C3F, 0x9AF0, 0x6C40, 0xCDA1, 0x6C41, 0xD6AD, 0x6C42, 0xC7F3,\n\t0x6C43, 0x9AF1, 0x6C44, 0x9AF2, 0x6C45, 0x9AF3, 0x6C46, 0xD9E0,\n\t0x6C47, 0xBBE3, 0x6C48, 0x9AF4, 0x6C49, 0xBABA, 0x6C4A, 0xE3E2,\n\t0x6C4B, 0x9AF5, 0x6C4C, 0x9AF6, 0x6C4D, 0x9AF7, 0x6C4E, 0x9AF8,\n\t0x6C4F, 0x9AF9, 0x6C50, 0xCFAB, 0x6C51, 0x9AFA, 0x6C52, 0x9AFB,\n\t0x6C53, 0x9AFC, 0x6C54, 0xE3E0, 0x6C55, 0xC9C7, 0x6C56, 0x9AFD,\n\t0x6C57, 0xBAB9, 0x6C58, 0x9AFE, 0x6C59, 0x9B40, 0x6C5A, 0x9B41,\n\t0x6C5B, 0xD1B4, 0x6C5C, 0xE3E1, 0x6C5D, 0xC8EA, 0x6C5E, 0xB9AF,\n\t0x6C5F, 0xBDAD, 0x6C60, 0xB3D8, 0x6C61, 0xCEDB, 0x6C62, 0x9B42,\n\t0x6C63, 0x9B43, 0x6C64, 0xCCC0, 0x6C65, 0x9B44, 0x6C66, 0x9B45,\n\t0x6C67, 0x9B46, 0x6C68, 0xE3E8, 0x6C69, 0xE3E9, 0x6C6A, 0xCDF4,\n\t0x6C6B, 0x9B47, 0x6C6C, 0x9B48, 0x6C6D, 0x9B49, 0x6C6E, 0x9B4A,\n\t0x6C6F, 0x9B4B, 0x6C70, 0xCCAD, 0x6C71, 0x9B4C, 0x6C72, 0xBCB3,\n\t0x6C73, 0x9B4D, 0x6C74, 0xE3EA, 0x6C75, 0x9B4E, 0x6C76, 0xE3EB,\n\t0x6C77, 0x9B4F, 0x6C78, 0x9B50, 0x6C79, 0xD0DA, 0x6C7A, 0x9B51,\n\t0x6C7B, 0x9B52, 0x6C7C, 0x9B53, 0x6C7D, 0xC6FB, 0x6C7E, 0xB7DA,\n\t0x6C7F, 0x9B54, 0x6C80, 0x9B55, 0x6C81, 0xC7DF, 0x6C82, 0xD2CA,\n\t0x6C83, 0xCED6, 0x6C84, 0x9B56, 0x6C85, 0xE3E4, 0x6C86, 0xE3EC,\n\t0x6C87, 0x9B57, 0x6C88, 0xC9F2, 0x6C89, 0xB3C1, 0x6C8A, 0x9B58,\n\t0x6C8B, 0x9B59, 0x6C8C, 0xE3E7, 0x6C8D, 0x9B5A, 0x6C8E, 0x9B5B,\n\t0x6C8F, 0xC6E3, 0x6C90, 0xE3E5, 0x6C91, 0x9B5C, 0x6C92, 0x9B5D,\n\t0x6C93, 0xEDB3, 0x6C94, 0xE3E6, 0x6C95, 0x9B5E, 0x6C96, 0x9B5F,\n\t0x6C97, 0x9B60, 0x6C98, 0x9B61, 0x6C99, 0xC9B3, 0x6C9A, 0x9B62,\n\t0x6C9B, 0xC5E6, 0x6C9C, 0x9B63, 0x6C9D, 0x9B64, 0x6C9E, 0x9B65,\n\t0x6C9F, 0xB9B5, 0x6CA0, 0x9B66, 0x6CA1, 0xC3BB, 0x6CA2, 0x9B67,\n\t0x6CA3, 0xE3E3, 0x6CA4, 0xC5BD, 0x6CA5, 0xC1A4, 0x6CA6, 0xC2D9,\n\t0x6CA7, 0xB2D7, 0x6CA8, 0x9B68, 0x6CA9, 0xE3ED, 0x6CAA, 0xBBA6,\n\t0x6CAB, 0xC4AD, 0x6CAC, 0x9B69, 0x6CAD, 0xE3F0, 0x6CAE, 0xBEDA,\n\t0x6CAF, 0x9B6A, 0x6CB0, 0x9B6B, 0x6CB1, 0xE3FB, 0x6CB2, 0xE3F5,\n\t0x6CB3, 0xBAD3, 0x6CB4, 0x9B6C, 0x6CB5, 0x9B6D, 0x6CB6, 0x9B6E,\n\t0x6CB7, 0x9B6F, 0x6CB8, 0xB7D0, 0x6CB9, 0xD3CD, 0x6CBA, 0x9B70,\n\t0x6CBB, 0xD6CE, 0x6CBC, 0xD5D3, 0x6CBD, 0xB9C1, 0x6CBE, 0xD5B4,\n\t0x6CBF, 0xD1D8, 0x6CC0, 0x9B71, 0x6CC1, 0x9B72, 0x6CC2, 0x9B73,\n\t0x6CC3, 0x9B74, 0x6CC4, 0xD0B9, 0x6CC5, 0xC7F6, 0x6CC6, 0x9B75,\n\t0x6CC7, 0x9B76, 0x6CC8, 0x9B77, 0x6CC9, 0xC8AA, 0x6CCA, 0xB2B4,\n\t0x6CCB, 0x9B78, 0x6CCC, 0xC3DA, 0x6CCD, 0x9B79, 0x6CCE, 0x9B7A,\n\t0x6CCF, 0x9B7B, 0x6CD0, 0xE3EE, 0x6CD1, 0x9B7C, 0x6CD2, 0x9B7D,\n\t0x6CD3, 0xE3FC, 0x6CD4, 0xE3EF, 0x6CD5, 0xB7A8, 0x6CD6, 0xE3F7,\n\t0x6CD7, 0xE3F4, 0x6CD8, 0x9B7E, 0x6CD9, 0x9B80, 0x6CDA, 0x9B81,\n\t0x6CDB, 0xB7BA, 0x6CDC, 0x9B82, 0x6CDD, 0x9B83, 0x6CDE, 0xC5A2,\n\t0x6CDF, 0x9B84, 0x6CE0, 0xE3F6, 0x6CE1, 0xC5DD, 0x6CE2, 0xB2A8,\n\t0x6CE3, 0xC6FC, 0x6CE4, 0x9B85, 0x6CE5, 0xC4E0, 0x6CE6, 0x9B86,\n\t0x6CE7, 0x9B87, 0x6CE8, 0xD7A2, 0x6CE9, 0x9B88, 0x6CEA, 0xC0E1,\n\t0x6CEB, 0xE3F9, 0x6CEC, 0x9B89, 0x6CED, 0x9B8A, 0x6CEE, 0xE3FA,\n\t0x6CEF, 0xE3FD, 0x6CF0, 0xCCA9, 0x6CF1, 0xE3F3, 0x6CF2, 0x9B8B,\n\t0x6CF3, 0xD3BE, 0x6CF4, 0x9B8C, 0x6CF5, 0xB1C3, 0x6CF6, 0xEDB4,\n\t0x6CF7, 0xE3F1, 0x6CF8, 0xE3F2, 0x6CF9, 0x9B8D, 0x6CFA, 0xE3F8,\n\t0x6CFB, 0xD0BA, 0x6CFC, 0xC6C3, 0x6CFD, 0xD4F3, 0x6CFE, 0xE3FE,\n\t0x6CFF, 0x9B8E, 0x6D00, 0x9B8F, 0x6D01, 0xBDE0, 0x6D02, 0x9B90,\n\t0x6D03, 0x9B91, 0x6D04, 0xE4A7, 0x6D05, 0x9B92, 0x6D06, 0x9B93,\n\t0x6D07, 0xE4A6, 0x6D08, 0x9B94, 0x6D09, 0x9B95, 0x6D0A, 0x9B96,\n\t0x6D0B, 0xD1F3, 0x6D0C, 0xE4A3, 0x6D0D, 0x9B97, 0x6D0E, 0xE4A9,\n\t0x6D0F, 0x9B98, 0x6D10, 0x9B99, 0x6D11, 0x9B9A, 0x6D12, 0xC8F7,\n\t0x6D13, 0x9B9B, 0x6D14, 0x9B9C, 0x6D15, 0x9B9D, 0x6D16, 0x9B9E,\n\t0x6D17, 0xCFB4, 0x6D18, 0x9B9F, 0x6D19, 0xE4A8, 0x6D1A, 0xE4AE,\n\t0x6D1B, 0xC2E5, 0x6D1C, 0x9BA0, 0x6D1D, 0x9BA1, 0x6D1E, 0xB6B4,\n\t0x6D1F, 0x9BA2, 0x6D20, 0x9BA3, 0x6D21, 0x9BA4, 0x6D22, 0x9BA5,\n\t0x6D23, 0x9BA6, 0x6D24, 0x9BA7, 0x6D25, 0xBDF2, 0x6D26, 0x9BA8,\n\t0x6D27, 0xE4A2, 0x6D28, 0x9BA9, 0x6D29, 0x9BAA, 0x6D2A, 0xBAE9,\n\t0x6D2B, 0xE4AA, 0x6D2C, 0x9BAB, 0x6D2D, 0x9BAC, 0x6D2E, 0xE4AC,\n\t0x6D2F, 0x9BAD, 0x6D30, 0x9BAE, 0x6D31, 0xB6FD, 0x6D32, 0xD6DE,\n\t0x6D33, 0xE4B2, 0x6D34, 0x9BAF, 0x6D35, 0xE4AD, 0x6D36, 0x9BB0,\n\t0x6D37, 0x9BB1, 0x6D38, 0x9BB2, 0x6D39, 0xE4A1, 0x6D3A, 0x9BB3,\n\t0x6D3B, 0xBBEE, 0x6D3C, 0xCDDD, 0x6D3D, 0xC7A2, 0x6D3E, 0xC5C9,\n\t0x6D3F, 0x9BB4, 0x6D40, 0x9BB5, 0x6D41, 0xC1F7, 0x6D42, 0x9BB6,\n\t0x6D43, 0xE4A4, 0x6D44, 0x9BB7, 0x6D45, 0xC7B3, 0x6D46, 0xBDAC,\n\t0x6D47, 0xBDBD, 0x6D48, 0xE4A5, 0x6D49, 0x9BB8, 0x6D4A, 0xD7C7,\n\t0x6D4B, 0xB2E2, 0x6D4C, 0x9BB9, 0x6D4D, 0xE4AB, 0x6D4E, 0xBCC3,\n\t0x6D4F, 0xE4AF, 0x6D50, 0x9BBA, 0x6D51, 0xBBEB, 0x6D52, 0xE4B0,\n\t0x6D53, 0xC5A8, 0x6D54, 0xE4B1, 0x6D55, 0x9BBB, 0x6D56, 0x9BBC,\n\t0x6D57, 0x9BBD, 0x6D58, 0x9BBE, 0x6D59, 0xD5E3, 0x6D5A, 0xBFA3,\n\t0x6D5B, 0x9BBF, 0x6D5C, 0xE4BA, 0x6D5D, 0x9BC0, 0x6D5E, 0xE4B7,\n\t0x6D5F, 0x9BC1, 0x6D60, 0xE4BB, 0x6D61, 0x9BC2, 0x6D62, 0x9BC3,\n\t0x6D63, 0xE4BD, 0x6D64, 0x9BC4, 0x6D65, 0x9BC5, 0x6D66, 0xC6D6,\n\t0x6D67, 0x9BC6, 0x6D68, 0x9BC7, 0x6D69, 0xBAC6, 0x6D6A, 0xC0CB,\n\t0x6D6B, 0x9BC8, 0x6D6C, 0x9BC9, 0x6D6D, 0x9BCA, 0x6D6E, 0xB8A1,\n\t0x6D6F, 0xE4B4, 0x6D70, 0x9BCB, 0x6D71, 0x9BCC, 0x6D72, 0x9BCD,\n\t0x6D73, 0x9BCE, 0x6D74, 0xD4A1, 0x6D75, 0x9BCF, 0x6D76, 0x9BD0,\n\t0x6D77, 0xBAA3, 0x6D78, 0xBDFE, 0x6D79, 0x9BD1, 0x6D7A, 0x9BD2,\n\t0x6D7B, 0x9BD3, 0x6D7C, 0xE4BC, 0x6D7D, 0x9BD4, 0x6D7E, 0x9BD5,\n\t0x6D7F, 0x9BD6, 0x6D80, 0x9BD7, 0x6D81, 0x9BD8, 0x6D82, 0xCDBF,\n\t0x6D83, 0x9BD9, 0x6D84, 0x9BDA, 0x6D85, 0xC4F9, 0x6D86, 0x9BDB,\n\t0x6D87, 0x9BDC, 0x6D88, 0xCFFB, 0x6D89, 0xC9E6, 0x6D8A, 0x9BDD,\n\t0x6D8B, 0x9BDE, 0x6D8C, 0xD3BF, 0x6D8D, 0x9BDF, 0x6D8E, 0xCFD1,\n\t0x6D8F, 0x9BE0, 0x6D90, 0x9BE1, 0x6D91, 0xE4B3, 0x6D92, 0x9BE2,\n\t0x6D93, 0xE4B8, 0x6D94, 0xE4B9, 0x6D95, 0xCCE9, 0x6D96, 0x9BE3,\n\t0x6D97, 0x9BE4, 0x6D98, 0x9BE5, 0x6D99, 0x9BE6, 0x6D9A, 0x9BE7,\n\t0x6D9B, 0xCCCE, 0x6D9C, 0x9BE8, 0x6D9D, 0xC0D4, 0x6D9E, 0xE4B5,\n\t0x6D9F, 0xC1B0, 0x6DA0, 0xE4B6, 0x6DA1, 0xCED0, 0x6DA2, 0x9BE9,\n\t0x6DA3, 0xBBC1, 0x6DA4, 0xB5D3, 0x6DA5, 0x9BEA, 0x6DA6, 0xC8F3,\n\t0x6DA7, 0xBDA7, 0x6DA8, 0xD5C7, 0x6DA9, 0xC9AC, 0x6DAA, 0xB8A2,\n\t0x6DAB, 0xE4CA, 0x6DAC, 0x9BEB, 0x6DAD, 0x9BEC, 0x6DAE, 0xE4CC,\n\t0x6DAF, 0xD1C4, 0x6DB0, 0x9BED, 0x6DB1, 0x9BEE, 0x6DB2, 0xD2BA,\n\t0x6DB3, 0x9BEF, 0x6DB4, 0x9BF0, 0x6DB5, 0xBAAD, 0x6DB6, 0x9BF1,\n\t0x6DB7, 0x9BF2, 0x6DB8, 0xBAD4, 0x6DB9, 0x9BF3, 0x6DBA, 0x9BF4,\n\t0x6DBB, 0x9BF5, 0x6DBC, 0x9BF6, 0x6DBD, 0x9BF7, 0x6DBE, 0x9BF8,\n\t0x6DBF, 0xE4C3, 0x6DC0, 0xB5ED, 0x6DC1, 0x9BF9, 0x6DC2, 0x9BFA,\n\t0x6DC3, 0x9BFB, 0x6DC4, 0xD7CD, 0x6DC5, 0xE4C0, 0x6DC6, 0xCFFD,\n\t0x6DC7, 0xE4BF, 0x6DC8, 0x9BFC, 0x6DC9, 0x9BFD, 0x6DCA, 0x9BFE,\n\t0x6DCB, 0xC1DC, 0x6DCC, 0xCCCA, 0x6DCD, 0x9C40, 0x6DCE, 0x9C41,\n\t0x6DCF, 0x9C42, 0x6DD0, 0x9C43, 0x6DD1, 0xCAE7, 0x6DD2, 0x9C44,\n\t0x6DD3, 0x9C45, 0x6DD4, 0x9C46, 0x6DD5, 0x9C47, 0x6DD6, 0xC4D7,\n\t0x6DD7, 0x9C48, 0x6DD8, 0xCCD4, 0x6DD9, 0xE4C8, 0x6DDA, 0x9C49,\n\t0x6DDB, 0x9C4A, 0x6DDC, 0x9C4B, 0x6DDD, 0xE4C7, 0x6DDE, 0xE4C1,\n\t0x6DDF, 0x9C4C, 0x6DE0, 0xE4C4, 0x6DE1, 0xB5AD, 0x6DE2, 0x9C4D,\n\t0x6DE3, 0x9C4E, 0x6DE4, 0xD3D9, 0x6DE5, 0x9C4F, 0x6DE6, 0xE4C6,\n\t0x6DE7, 0x9C50, 0x6DE8, 0x9C51, 0x6DE9, 0x9C52, 0x6DEA, 0x9C53,\n\t0x6DEB, 0xD2F9, 0x6DEC, 0xB4E3, 0x6DED, 0x9C54, 0x6DEE, 0xBBB4,\n\t0x6DEF, 0x9C55, 0x6DF0, 0x9C56, 0x6DF1, 0xC9EE, 0x6DF2, 0x9C57,\n\t0x6DF3, 0xB4BE, 0x6DF4, 0x9C58, 0x6DF5, 0x9C59, 0x6DF6, 0x9C5A,\n\t0x6DF7, 0xBBEC, 0x6DF8, 0x9C5B, 0x6DF9, 0xD1CD, 0x6DFA, 0x9C5C,\n\t0x6DFB, 0xCCED, 0x6DFC, 0xEDB5, 0x6DFD, 0x9C5D, 0x6DFE, 0x9C5E,\n\t0x6DFF, 0x9C5F, 0x6E00, 0x9C60, 0x6E01, 0x9C61, 0x6E02, 0x9C62,\n\t0x6E03, 0x9C63, 0x6E04, 0x9C64, 0x6E05, 0xC7E5, 0x6E06, 0x9C65,\n\t0x6E07, 0x9C66, 0x6E08, 0x9C67, 0x6E09, 0x9C68, 0x6E0A, 0xD4A8,\n\t0x6E0B, 0x9C69, 0x6E0C, 0xE4CB, 0x6E0D, 0xD7D5, 0x6E0E, 0xE4C2,\n\t0x6E0F, 0x9C6A, 0x6E10, 0xBDA5, 0x6E11, 0xE4C5, 0x6E12, 0x9C6B,\n\t0x6E13, 0x9C6C, 0x6E14, 0xD3E6, 0x6E15, 0x9C6D, 0x6E16, 0xE4C9,\n\t0x6E17, 0xC9F8, 0x6E18, 0x9C6E, 0x6E19, 0x9C6F, 0x6E1A, 0xE4BE,\n\t0x6E1B, 0x9C70, 0x6E1C, 0x9C71, 0x6E1D, 0xD3E5, 0x6E1E, 0x9C72,\n\t0x6E1F, 0x9C73, 0x6E20, 0xC7FE, 0x6E21, 0xB6C9, 0x6E22, 0x9C74,\n\t0x6E23, 0xD4FC, 0x6E24, 0xB2B3, 0x6E25, 0xE4D7, 0x6E26, 0x9C75,\n\t0x6E27, 0x9C76, 0x6E28, 0x9C77, 0x6E29, 0xCEC2, 0x6E2A, 0x9C78,\n\t0x6E2B, 0xE4CD, 0x6E2C, 0x9C79, 0x6E2D, 0xCEBC, 0x6E2E, 0x9C7A,\n\t0x6E2F, 0xB8DB, 0x6E30, 0x9C7B, 0x6E31, 0x9C7C, 0x6E32, 0xE4D6,\n\t0x6E33, 0x9C7D, 0x6E34, 0xBFCA, 0x6E35, 0x9C7E, 0x6E36, 0x9C80,\n\t0x6E37, 0x9C81, 0x6E38, 0xD3CE, 0x6E39, 0x9C82, 0x6E3A, 0xC3EC,\n\t0x6E3B, 0x9C83, 0x6E3C, 0x9C84, 0x6E3D, 0x9C85, 0x6E3E, 0x9C86,\n\t0x6E3F, 0x9C87, 0x6E40, 0x9C88, 0x6E41, 0x9C89, 0x6E42, 0x9C8A,\n\t0x6E43, 0xC5C8, 0x6E44, 0xE4D8, 0x6E45, 0x9C8B, 0x6E46, 0x9C8C,\n\t0x6E47, 0x9C8D, 0x6E48, 0x9C8E, 0x6E49, 0x9C8F, 0x6E4A, 0x9C90,\n\t0x6E4B, 0x9C91, 0x6E4C, 0x9C92, 0x6E4D, 0xCDC4, 0x6E4E, 0xE4CF,\n\t0x6E4F, 0x9C93, 0x6E50, 0x9C94, 0x6E51, 0x9C95, 0x6E52, 0x9C96,\n\t0x6E53, 0xE4D4, 0x6E54, 0xE4D5, 0x6E55, 0x9C97, 0x6E56, 0xBAFE,\n\t0x6E57, 0x9C98, 0x6E58, 0xCFE6, 0x6E59, 0x9C99, 0x6E5A, 0x9C9A,\n\t0x6E5B, 0xD5BF, 0x6E5C, 0x9C9B, 0x6E5D, 0x9C9C, 0x6E5E, 0x9C9D,\n\t0x6E5F, 0xE4D2, 0x6E60, 0x9C9E, 0x6E61, 0x9C9F, 0x6E62, 0x9CA0,\n\t0x6E63, 0x9CA1, 0x6E64, 0x9CA2, 0x6E65, 0x9CA3, 0x6E66, 0x9CA4,\n\t0x6E67, 0x9CA5, 0x6E68, 0x9CA6, 0x6E69, 0x9CA7, 0x6E6A, 0x9CA8,\n\t0x6E6B, 0xE4D0, 0x6E6C, 0x9CA9, 0x6E6D, 0x9CAA, 0x6E6E, 0xE4CE,\n\t0x6E6F, 0x9CAB, 0x6E70, 0x9CAC, 0x6E71, 0x9CAD, 0x6E72, 0x9CAE,\n\t0x6E73, 0x9CAF, 0x6E74, 0x9CB0, 0x6E75, 0x9CB1, 0x6E76, 0x9CB2,\n\t0x6E77, 0x9CB3, 0x6E78, 0x9CB4, 0x6E79, 0x9CB5, 0x6E7A, 0x9CB6,\n\t0x6E7B, 0x9CB7, 0x6E7C, 0x9CB8, 0x6E7D, 0x9CB9, 0x6E7E, 0xCDE5,\n\t0x6E7F, 0xCAAA, 0x6E80, 0x9CBA, 0x6E81, 0x9CBB, 0x6E82, 0x9CBC,\n\t0x6E83, 0xC0A3, 0x6E84, 0x9CBD, 0x6E85, 0xBDA6, 0x6E86, 0xE4D3,\n\t0x6E87, 0x9CBE, 0x6E88, 0x9CBF, 0x6E89, 0xB8C8, 0x6E8A, 0x9CC0,\n\t0x6E8B, 0x9CC1, 0x6E8C, 0x9CC2, 0x6E8D, 0x9CC3, 0x6E8E, 0x9CC4,\n\t0x6E8F, 0xE4E7, 0x6E90, 0xD4B4, 0x6E91, 0x9CC5, 0x6E92, 0x9CC6,\n\t0x6E93, 0x9CC7, 0x6E94, 0x9CC8, 0x6E95, 0x9CC9, 0x6E96, 0x9CCA,\n\t0x6E97, 0x9CCB, 0x6E98, 0xE4DB, 0x6E99, 0x9CCC, 0x6E9A, 0x9CCD,\n\t0x6E9B, 0x9CCE, 0x6E9C, 0xC1EF, 0x6E9D, 0x9CCF, 0x6E9E, 0x9CD0,\n\t0x6E9F, 0xE4E9, 0x6EA0, 0x9CD1, 0x6EA1, 0x9CD2, 0x6EA2, 0xD2E7,\n\t0x6EA3, 0x9CD3, 0x6EA4, 0x9CD4, 0x6EA5, 0xE4DF, 0x6EA6, 0x9CD5,\n\t0x6EA7, 0xE4E0, 0x6EA8, 0x9CD6, 0x6EA9, 0x9CD7, 0x6EAA, 0xCFAA,\n\t0x6EAB, 0x9CD8, 0x6EAC, 0x9CD9, 0x6EAD, 0x9CDA, 0x6EAE, 0x9CDB,\n\t0x6EAF, 0xCBDD, 0x6EB0, 0x9CDC, 0x6EB1, 0xE4DA, 0x6EB2, 0xE4D1,\n\t0x6EB3, 0x9CDD, 0x6EB4, 0xE4E5, 0x6EB5, 0x9CDE, 0x6EB6, 0xC8DC,\n\t0x6EB7, 0xE4E3, 0x6EB8, 0x9CDF, 0x6EB9, 0x9CE0, 0x6EBA, 0xC4E7,\n\t0x6EBB, 0xE4E2, 0x6EBC, 0x9CE1, 0x6EBD, 0xE4E1, 0x6EBE, 0x9CE2,\n\t0x6EBF, 0x9CE3, 0x6EC0, 0x9CE4, 0x6EC1, 0xB3FC, 0x6EC2, 0xE4E8,\n\t0x6EC3, 0x9CE5, 0x6EC4, 0x9CE6, 0x6EC5, 0x9CE7, 0x6EC6, 0x9CE8,\n\t0x6EC7, 0xB5E1, 0x6EC8, 0x9CE9, 0x6EC9, 0x9CEA, 0x6ECA, 0x9CEB,\n\t0x6ECB, 0xD7CC, 0x6ECC, 0x9CEC, 0x6ECD, 0x9CED, 0x6ECE, 0x9CEE,\n\t0x6ECF, 0xE4E6, 0x6ED0, 0x9CEF, 0x6ED1, 0xBBAC, 0x6ED2, 0x9CF0,\n\t0x6ED3, 0xD7D2, 0x6ED4, 0xCCCF, 0x6ED5, 0xEBF8, 0x6ED6, 0x9CF1,\n\t0x6ED7, 0xE4E4, 0x6ED8, 0x9CF2, 0x6ED9, 0x9CF3, 0x6EDA, 0xB9F6,\n\t0x6EDB, 0x9CF4, 0x6EDC, 0x9CF5, 0x6EDD, 0x9CF6, 0x6EDE, 0xD6CD,\n\t0x6EDF, 0xE4D9, 0x6EE0, 0xE4DC, 0x6EE1, 0xC2FA, 0x6EE2, 0xE4DE,\n\t0x6EE3, 0x9CF7, 0x6EE4, 0xC2CB, 0x6EE5, 0xC0C4, 0x6EE6, 0xC2D0,\n\t0x6EE7, 0x9CF8, 0x6EE8, 0xB1F5, 0x6EE9, 0xCCB2, 0x6EEA, 0x9CF9,\n\t0x6EEB, 0x9CFA, 0x6EEC, 0x9CFB, 0x6EED, 0x9CFC, 0x6EEE, 0x9CFD,\n\t0x6EEF, 0x9CFE, 0x6EF0, 0x9D40, 0x6EF1, 0x9D41, 0x6EF2, 0x9D42,\n\t0x6EF3, 0x9D43, 0x6EF4, 0xB5CE, 0x6EF5, 0x9D44, 0x6EF6, 0x9D45,\n\t0x6EF7, 0x9D46, 0x6EF8, 0x9D47, 0x6EF9, 0xE4EF, 0x6EFA, 0x9D48,\n\t0x6EFB, 0x9D49, 0x6EFC, 0x9D4A, 0x6EFD, 0x9D4B, 0x6EFE, 0x9D4C,\n\t0x6EFF, 0x9D4D, 0x6F00, 0x9D4E, 0x6F01, 0x9D4F, 0x6F02, 0xC6AF,\n\t0x6F03, 0x9D50, 0x6F04, 0x9D51, 0x6F05, 0x9D52, 0x6F06, 0xC6E1,\n\t0x6F07, 0x9D53, 0x6F08, 0x9D54, 0x6F09, 0xE4F5, 0x6F0A, 0x9D55,\n\t0x6F0B, 0x9D56, 0x6F0C, 0x9D57, 0x6F0D, 0x9D58, 0x6F0E, 0x9D59,\n\t0x6F0F, 0xC2A9, 0x6F10, 0x9D5A, 0x6F11, 0x9D5B, 0x6F12, 0x9D5C,\n\t0x6F13, 0xC0EC, 0x6F14, 0xD1DD, 0x6F15, 0xE4EE, 0x6F16, 0x9D5D,\n\t0x6F17, 0x9D5E, 0x6F18, 0x9D5F, 0x6F19, 0x9D60, 0x6F1A, 0x9D61,\n\t0x6F1B, 0x9D62, 0x6F1C, 0x9D63, 0x6F1D, 0x9D64, 0x6F1E, 0x9D65,\n\t0x6F1F, 0x9D66, 0x6F20, 0xC4AE, 0x6F21, 0x9D67, 0x6F22, 0x9D68,\n\t0x6F23, 0x9D69, 0x6F24, 0xE4ED, 0x6F25, 0x9D6A, 0x6F26, 0x9D6B,\n\t0x6F27, 0x9D6C, 0x6F28, 0x9D6D, 0x6F29, 0xE4F6, 0x6F2A, 0xE4F4,\n\t0x6F2B, 0xC2FE, 0x6F2C, 0x9D6E, 0x6F2D, 0xE4DD, 0x6F2E, 0x9D6F,\n\t0x6F2F, 0xE4F0, 0x6F30, 0x9D70, 0x6F31, 0xCAFE, 0x6F32, 0x9D71,\n\t0x6F33, 0xD5C4, 0x6F34, 0x9D72, 0x6F35, 0x9D73, 0x6F36, 0xE4F1,\n\t0x6F37, 0x9D74, 0x6F38, 0x9D75, 0x6F39, 0x9D76, 0x6F3A, 0x9D77,\n\t0x6F3B, 0x9D78, 0x6F3C, 0x9D79, 0x6F3D, 0x9D7A, 0x6F3E, 0xD1FA,\n\t0x6F3F, 0x9D7B, 0x6F40, 0x9D7C, 0x6F41, 0x9D7D, 0x6F42, 0x9D7E,\n\t0x6F43, 0x9D80, 0x6F44, 0x9D81, 0x6F45, 0x9D82, 0x6F46, 0xE4EB,\n\t0x6F47, 0xE4EC, 0x6F48, 0x9D83, 0x6F49, 0x9D84, 0x6F4A, 0x9D85,\n\t0x6F4B, 0xE4F2, 0x6F4C, 0x9D86, 0x6F4D, 0xCEAB, 0x6F4E, 0x9D87,\n\t0x6F4F, 0x9D88, 0x6F50, 0x9D89, 0x6F51, 0x9D8A, 0x6F52, 0x9D8B,\n\t0x6F53, 0x9D8C, 0x6F54, 0x9D8D, 0x6F55, 0x9D8E, 0x6F56, 0x9D8F,\n\t0x6F57, 0x9D90, 0x6F58, 0xC5CB, 0x6F59, 0x9D91, 0x6F5A, 0x9D92,\n\t0x6F5B, 0x9D93, 0x6F5C, 0xC7B1, 0x6F5D, 0x9D94, 0x6F5E, 0xC2BA,\n\t0x6F5F, 0x9D95, 0x6F60, 0x9D96, 0x6F61, 0x9D97, 0x6F62, 0xE4EA,\n\t0x6F63, 0x9D98, 0x6F64, 0x9D99, 0x6F65, 0x9D9A, 0x6F66, 0xC1CA,\n\t0x6F67, 0x9D9B, 0x6F68, 0x9D9C, 0x6F69, 0x9D9D, 0x6F6A, 0x9D9E,\n\t0x6F6B, 0x9D9F, 0x6F6C, 0x9DA0, 0x6F6D, 0xCCB6, 0x6F6E, 0xB3B1,\n\t0x6F6F, 0x9DA1, 0x6F70, 0x9DA2, 0x6F71, 0x9DA3, 0x6F72, 0xE4FB,\n\t0x6F73, 0x9DA4, 0x6F74, 0xE4F3, 0x6F75, 0x9DA5, 0x6F76, 0x9DA6,\n\t0x6F77, 0x9DA7, 0x6F78, 0xE4FA, 0x6F79, 0x9DA8, 0x6F7A, 0xE4FD,\n\t0x6F7B, 0x9DA9, 0x6F7C, 0xE4FC, 0x6F7D, 0x9DAA, 0x6F7E, 0x9DAB,\n\t0x6F7F, 0x9DAC, 0x6F80, 0x9DAD, 0x6F81, 0x9DAE, 0x6F82, 0x9DAF,\n\t0x6F83, 0x9DB0, 0x6F84, 0xB3CE, 0x6F85, 0x9DB1, 0x6F86, 0x9DB2,\n\t0x6F87, 0x9DB3, 0x6F88, 0xB3BA, 0x6F89, 0xE4F7, 0x6F8A, 0x9DB4,\n\t0x6F8B, 0x9DB5, 0x6F8C, 0xE4F9, 0x6F8D, 0xE4F8, 0x6F8E, 0xC5EC,\n\t0x6F8F, 0x9DB6, 0x6F90, 0x9DB7, 0x6F91, 0x9DB8, 0x6F92, 0x9DB9,\n\t0x6F93, 0x9DBA, 0x6F94, 0x9DBB, 0x6F95, 0x9DBC, 0x6F96, 0x9DBD,\n\t0x6F97, 0x9DBE, 0x6F98, 0x9DBF, 0x6F99, 0x9DC0, 0x6F9A, 0x9DC1,\n\t0x6F9B, 0x9DC2, 0x6F9C, 0xC0BD, 0x6F9D, 0x9DC3, 0x6F9E, 0x9DC4,\n\t0x6F9F, 0x9DC5, 0x6FA0, 0x9DC6, 0x6FA1, 0xD4E8, 0x6FA2, 0x9DC7,\n\t0x6FA3, 0x9DC8, 0x6FA4, 0x9DC9, 0x6FA5, 0x9DCA, 0x6FA6, 0x9DCB,\n\t0x6FA7, 0xE5A2, 0x6FA8, 0x9DCC, 0x6FA9, 0x9DCD, 0x6FAA, 0x9DCE,\n\t0x6FAB, 0x9DCF, 0x6FAC, 0x9DD0, 0x6FAD, 0x9DD1, 0x6FAE, 0x9DD2,\n\t0x6FAF, 0x9DD3, 0x6FB0, 0x9DD4, 0x6FB1, 0x9DD5, 0x6FB2, 0x9DD6,\n\t0x6FB3, 0xB0C4, 0x6FB4, 0x9DD7, 0x6FB5, 0x9DD8, 0x6FB6, 0xE5A4,\n\t0x6FB7, 0x9DD9, 0x6FB8, 0x9DDA, 0x6FB9, 0xE5A3, 0x6FBA, 0x9DDB,\n\t0x6FBB, 0x9DDC, 0x6FBC, 0x9DDD, 0x6FBD, 0x9DDE, 0x6FBE, 0x9DDF,\n\t0x6FBF, 0x9DE0, 0x6FC0, 0xBCA4, 0x6FC1, 0x9DE1, 0x6FC2, 0xE5A5,\n\t0x6FC3, 0x9DE2, 0x6FC4, 0x9DE3, 0x6FC5, 0x9DE4, 0x6FC6, 0x9DE5,\n\t0x6FC7, 0x9DE6, 0x6FC8, 0x9DE7, 0x6FC9, 0xE5A1, 0x6FCA, 0x9DE8,\n\t0x6FCB, 0x9DE9, 0x6FCC, 0x9DEA, 0x6FCD, 0x9DEB, 0x6FCE, 0x9DEC,\n\t0x6FCF, 0x9DED, 0x6FD0, 0x9DEE, 0x6FD1, 0xE4FE, 0x6FD2, 0xB1F4,\n\t0x6FD3, 0x9DEF, 0x6FD4, 0x9DF0, 0x6FD5, 0x9DF1, 0x6FD6, 0x9DF2,\n\t0x6FD7, 0x9DF3, 0x6FD8, 0x9DF4, 0x6FD9, 0x9DF5, 0x6FDA, 0x9DF6,\n\t0x6FDB, 0x9DF7, 0x6FDC, 0x9DF8, 0x6FDD, 0x9DF9, 0x6FDE, 0xE5A8,\n\t0x6FDF, 0x9DFA, 0x6FE0, 0xE5A9, 0x6FE1, 0xE5A6, 0x6FE2, 0x9DFB,\n\t0x6FE3, 0x9DFC, 0x6FE4, 0x9DFD, 0x6FE5, 0x9DFE, 0x6FE6, 0x9E40,\n\t0x6FE7, 0x9E41, 0x6FE8, 0x9E42, 0x6FE9, 0x9E43, 0x6FEA, 0x9E44,\n\t0x6FEB, 0x9E45, 0x6FEC, 0x9E46, 0x6FED, 0x9E47, 0x6FEE, 0xE5A7,\n\t0x6FEF, 0xE5AA, 0x6FF0, 0x9E48, 0x6FF1, 0x9E49, 0x6FF2, 0x9E4A,\n\t0x6FF3, 0x9E4B, 0x6FF4, 0x9E4C, 0x6FF5, 0x9E4D, 0x6FF6, 0x9E4E,\n\t0x6FF7, 0x9E4F, 0x6FF8, 0x9E50, 0x6FF9, 0x9E51, 0x6FFA, 0x9E52,\n\t0x6FFB, 0x9E53, 0x6FFC, 0x9E54, 0x6FFD, 0x9E55, 0x6FFE, 0x9E56,\n\t0x6FFF, 0x9E57, 0x7000, 0x9E58, 0x7001, 0x9E59, 0x7002, 0x9E5A,\n\t0x7003, 0x9E5B, 0x7004, 0x9E5C, 0x7005, 0x9E5D, 0x7006, 0x9E5E,\n\t0x7007, 0x9E5F, 0x7008, 0x9E60, 0x7009, 0x9E61, 0x700A, 0x9E62,\n\t0x700B, 0x9E63, 0x700C, 0x9E64, 0x700D, 0x9E65, 0x700E, 0x9E66,\n\t0x700F, 0x9E67, 0x7010, 0x9E68, 0x7011, 0xC6D9, 0x7012, 0x9E69,\n\t0x7013, 0x9E6A, 0x7014, 0x9E6B, 0x7015, 0x9E6C, 0x7016, 0x9E6D,\n\t0x7017, 0x9E6E, 0x7018, 0x9E6F, 0x7019, 0x9E70, 0x701A, 0xE5AB,\n\t0x701B, 0xE5AD, 0x701C, 0x9E71, 0x701D, 0x9E72, 0x701E, 0x9E73,\n\t0x701F, 0x9E74, 0x7020, 0x9E75, 0x7021, 0x9E76, 0x7022, 0x9E77,\n\t0x7023, 0xE5AC, 0x7024, 0x9E78, 0x7025, 0x9E79, 0x7026, 0x9E7A,\n\t0x7027, 0x9E7B, 0x7028, 0x9E7C, 0x7029, 0x9E7D, 0x702A, 0x9E7E,\n\t0x702B, 0x9E80, 0x702C, 0x9E81, 0x702D, 0x9E82, 0x702E, 0x9E83,\n\t0x702F, 0x9E84, 0x7030, 0x9E85, 0x7031, 0x9E86, 0x7032, 0x9E87,\n\t0x7033, 0x9E88, 0x7034, 0x9E89, 0x7035, 0xE5AF, 0x7036, 0x9E8A,\n\t0x7037, 0x9E8B, 0x7038, 0x9E8C, 0x7039, 0xE5AE, 0x703A, 0x9E8D,\n\t0x703B, 0x9E8E, 0x703C, 0x9E8F, 0x703D, 0x9E90, 0x703E, 0x9E91,\n\t0x703F, 0x9E92, 0x7040, 0x9E93, 0x7041, 0x9E94, 0x7042, 0x9E95,\n\t0x7043, 0x9E96, 0x7044, 0x9E97, 0x7045, 0x9E98, 0x7046, 0x9E99,\n\t0x7047, 0x9E9A, 0x7048, 0x9E9B, 0x7049, 0x9E9C, 0x704A, 0x9E9D,\n\t0x704B, 0x9E9E, 0x704C, 0xB9E0, 0x704D, 0x9E9F, 0x704E, 0x9EA0,\n\t0x704F, 0xE5B0, 0x7050, 0x9EA1, 0x7051, 0x9EA2, 0x7052, 0x9EA3,\n\t0x7053, 0x9EA4, 0x7054, 0x9EA5, 0x7055, 0x9EA6, 0x7056, 0x9EA7,\n\t0x7057, 0x9EA8, 0x7058, 0x9EA9, 0x7059, 0x9EAA, 0x705A, 0x9EAB,\n\t0x705B, 0x9EAC, 0x705C, 0x9EAD, 0x705D, 0x9EAE, 0x705E, 0xE5B1,\n\t0x705F, 0x9EAF, 0x7060, 0x9EB0, 0x7061, 0x9EB1, 0x7062, 0x9EB2,\n\t0x7063, 0x9EB3, 0x7064, 0x9EB4, 0x7065, 0x9EB5, 0x7066, 0x9EB6,\n\t0x7067, 0x9EB7, 0x7068, 0x9EB8, 0x7069, 0x9EB9, 0x706A, 0x9EBA,\n\t0x706B, 0xBBF0, 0x706C, 0xECE1, 0x706D, 0xC3F0, 0x706E, 0x9EBB,\n\t0x706F, 0xB5C6, 0x7070, 0xBBD2, 0x7071, 0x9EBC, 0x7072, 0x9EBD,\n\t0x7073, 0x9EBE, 0x7074, 0x9EBF, 0x7075, 0xC1E9, 0x7076, 0xD4EE,\n\t0x7077, 0x9EC0, 0x7078, 0xBEC4, 0x7079, 0x9EC1, 0x707A, 0x9EC2,\n\t0x707B, 0x9EC3, 0x707C, 0xD7C6, 0x707D, 0x9EC4, 0x707E, 0xD4D6,\n\t0x707F, 0xB2D3, 0x7080, 0xECBE, 0x7081, 0x9EC5, 0x7082, 0x9EC6,\n\t0x7083, 0x9EC7, 0x7084, 0x9EC8, 0x7085, 0xEAC1, 0x7086, 0x9EC9,\n\t0x7087, 0x9ECA, 0x7088, 0x9ECB, 0x7089, 0xC2AF, 0x708A, 0xB4B6,\n\t0x708B, 0x9ECC, 0x708C, 0x9ECD, 0x708D, 0x9ECE, 0x708E, 0xD1D7,\n\t0x708F, 0x9ECF, 0x7090, 0x9ED0, 0x7091, 0x9ED1, 0x7092, 0xB3B4,\n\t0x7093, 0x9ED2, 0x7094, 0xC8B2, 0x7095, 0xBFBB, 0x7096, 0xECC0,\n\t0x7097, 0x9ED3, 0x7098, 0x9ED4, 0x7099, 0xD6CB, 0x709A, 0x9ED5,\n\t0x709B, 0x9ED6, 0x709C, 0xECBF, 0x709D, 0xECC1, 0x709E, 0x9ED7,\n\t0x709F, 0x9ED8, 0x70A0, 0x9ED9, 0x70A1, 0x9EDA, 0x70A2, 0x9EDB,\n\t0x70A3, 0x9EDC, 0x70A4, 0x9EDD, 0x70A5, 0x9EDE, 0x70A6, 0x9EDF,\n\t0x70A7, 0x9EE0, 0x70A8, 0x9EE1, 0x70A9, 0x9EE2, 0x70AA, 0x9EE3,\n\t0x70AB, 0xECC5, 0x70AC, 0xBEE6, 0x70AD, 0xCCBF, 0x70AE, 0xC5DA,\n\t0x70AF, 0xBEBC, 0x70B0, 0x9EE4, 0x70B1, 0xECC6, 0x70B2, 0x9EE5,\n\t0x70B3, 0xB1FE, 0x70B4, 0x9EE6, 0x70B5, 0x9EE7, 0x70B6, 0x9EE8,\n\t0x70B7, 0xECC4, 0x70B8, 0xD5A8, 0x70B9, 0xB5E3, 0x70BA, 0x9EE9,\n\t0x70BB, 0xECC2, 0x70BC, 0xC1B6, 0x70BD, 0xB3E3, 0x70BE, 0x9EEA,\n\t0x70BF, 0x9EEB, 0x70C0, 0xECC3, 0x70C1, 0xCBB8, 0x70C2, 0xC0C3,\n\t0x70C3, 0xCCFE, 0x70C4, 0x9EEC, 0x70C5, 0x9EED, 0x70C6, 0x9EEE,\n\t0x70C7, 0x9EEF, 0x70C8, 0xC1D2, 0x70C9, 0x9EF0, 0x70CA, 0xECC8,\n\t0x70CB, 0x9EF1, 0x70CC, 0x9EF2, 0x70CD, 0x9EF3, 0x70CE, 0x9EF4,\n\t0x70CF, 0x9EF5, 0x70D0, 0x9EF6, 0x70D1, 0x9EF7, 0x70D2, 0x9EF8,\n\t0x70D3, 0x9EF9, 0x70D4, 0x9EFA, 0x70D5, 0x9EFB, 0x70D6, 0x9EFC,\n\t0x70D7, 0x9EFD, 0x70D8, 0xBAE6, 0x70D9, 0xC0D3, 0x70DA, 0x9EFE,\n\t0x70DB, 0xD6F2, 0x70DC, 0x9F40, 0x70DD, 0x9F41, 0x70DE, 0x9F42,\n\t0x70DF, 0xD1CC, 0x70E0, 0x9F43, 0x70E1, 0x9F44, 0x70E2, 0x9F45,\n\t0x70E3, 0x9F46, 0x70E4, 0xBFBE, 0x70E5, 0x9F47, 0x70E6, 0xB7B3,\n\t0x70E7, 0xC9D5, 0x70E8, 0xECC7, 0x70E9, 0xBBE2, 0x70EA, 0x9F48,\n\t0x70EB, 0xCCCC, 0x70EC, 0xBDFD, 0x70ED, 0xC8C8, 0x70EE, 0x9F49,\n\t0x70EF, 0xCFA9, 0x70F0, 0x9F4A, 0x70F1, 0x9F4B, 0x70F2, 0x9F4C,\n\t0x70F3, 0x9F4D, 0x70F4, 0x9F4E, 0x70F5, 0x9F4F, 0x70F6, 0x9F50,\n\t0x70F7, 0xCDE9, 0x70F8, 0x9F51, 0x70F9, 0xC5EB, 0x70FA, 0x9F52,\n\t0x70FB, 0x9F53, 0x70FC, 0x9F54, 0x70FD, 0xB7E9, 0x70FE, 0x9F55,\n\t0x70FF, 0x9F56, 0x7100, 0x9F57, 0x7101, 0x9F58, 0x7102, 0x9F59,\n\t0x7103, 0x9F5A, 0x7104, 0x9F5B, 0x7105, 0x9F5C, 0x7106, 0x9F5D,\n\t0x7107, 0x9F5E, 0x7108, 0x9F5F, 0x7109, 0xD1C9, 0x710A, 0xBAB8,\n\t0x710B, 0x9F60, 0x710C, 0x9F61, 0x710D, 0x9F62, 0x710E, 0x9F63,\n\t0x710F, 0x9F64, 0x7110, 0xECC9, 0x7111, 0x9F65, 0x7112, 0x9F66,\n\t0x7113, 0xECCA, 0x7114, 0x9F67, 0x7115, 0xBBC0, 0x7116, 0xECCB,\n\t0x7117, 0x9F68, 0x7118, 0xECE2, 0x7119, 0xB1BA, 0x711A, 0xB7D9,\n\t0x711B, 0x9F69, 0x711C, 0x9F6A, 0x711D, 0x9F6B, 0x711E, 0x9F6C,\n\t0x711F, 0x9F6D, 0x7120, 0x9F6E, 0x7121, 0x9F6F, 0x7122, 0x9F70,\n\t0x7123, 0x9F71, 0x7124, 0x9F72, 0x7125, 0x9F73, 0x7126, 0xBDB9,\n\t0x7127, 0x9F74, 0x7128, 0x9F75, 0x7129, 0x9F76, 0x712A, 0x9F77,\n\t0x712B, 0x9F78, 0x712C, 0x9F79, 0x712D, 0x9F7A, 0x712E, 0x9F7B,\n\t0x712F, 0xECCC, 0x7130, 0xD1E6, 0x7131, 0xECCD, 0x7132, 0x9F7C,\n\t0x7133, 0x9F7D, 0x7134, 0x9F7E, 0x7135, 0x9F80, 0x7136, 0xC8BB,\n\t0x7137, 0x9F81, 0x7138, 0x9F82, 0x7139, 0x9F83, 0x713A, 0x9F84,\n\t0x713B, 0x9F85, 0x713C, 0x9F86, 0x713D, 0x9F87, 0x713E, 0x9F88,\n\t0x713F, 0x9F89, 0x7140, 0x9F8A, 0x7141, 0x9F8B, 0x7142, 0x9F8C,\n\t0x7143, 0x9F8D, 0x7144, 0x9F8E, 0x7145, 0xECD1, 0x7146, 0x9F8F,\n\t0x7147, 0x9F90, 0x7148, 0x9F91, 0x7149, 0x9F92, 0x714A, 0xECD3,\n\t0x714B, 0x9F93, 0x714C, 0xBBCD, 0x714D, 0x9F94, 0x714E, 0xBCE5,\n\t0x714F, 0x9F95, 0x7150, 0x9F96, 0x7151, 0x9F97, 0x7152, 0x9F98,\n\t0x7153, 0x9F99, 0x7154, 0x9F9A, 0x7155, 0x9F9B, 0x7156, 0x9F9C,\n\t0x7157, 0x9F9D, 0x7158, 0x9F9E, 0x7159, 0x9F9F, 0x715A, 0x9FA0,\n\t0x715B, 0x9FA1, 0x715C, 0xECCF, 0x715D, 0x9FA2, 0x715E, 0xC9B7,\n\t0x715F, 0x9FA3, 0x7160, 0x9FA4, 0x7161, 0x9FA5, 0x7162, 0x9FA6,\n\t0x7163, 0x9FA7, 0x7164, 0xC3BA, 0x7165, 0x9FA8, 0x7166, 0xECE3,\n\t0x7167, 0xD5D5, 0x7168, 0xECD0, 0x7169, 0x9FA9, 0x716A, 0x9FAA,\n\t0x716B, 0x9FAB, 0x716C, 0x9FAC, 0x716D, 0x9FAD, 0x716E, 0xD6F3,\n\t0x716F, 0x9FAE, 0x7170, 0x9FAF, 0x7171, 0x9FB0, 0x7172, 0xECD2,\n\t0x7173, 0xECCE, 0x7174, 0x9FB1, 0x7175, 0x9FB2, 0x7176, 0x9FB3,\n\t0x7177, 0x9FB4, 0x7178, 0xECD4, 0x7179, 0x9FB5, 0x717A, 0xECD5,\n\t0x717B, 0x9FB6, 0x717C, 0x9FB7, 0x717D, 0xC9BF, 0x717E, 0x9FB8,\n\t0x717F, 0x9FB9, 0x7180, 0x9FBA, 0x7181, 0x9FBB, 0x7182, 0x9FBC,\n\t0x7183, 0x9FBD, 0x7184, 0xCFA8, 0x7185, 0x9FBE, 0x7186, 0x9FBF,\n\t0x7187, 0x9FC0, 0x7188, 0x9FC1, 0x7189, 0x9FC2, 0x718A, 0xD0DC,\n\t0x718B, 0x9FC3, 0x718C, 0x9FC4, 0x718D, 0x9FC5, 0x718E, 0x9FC6,\n\t0x718F, 0xD1AC, 0x7190, 0x9FC7, 0x7191, 0x9FC8, 0x7192, 0x9FC9,\n\t0x7193, 0x9FCA, 0x7194, 0xC8DB, 0x7195, 0x9FCB, 0x7196, 0x9FCC,\n\t0x7197, 0x9FCD, 0x7198, 0xECD6, 0x7199, 0xCEF5, 0x719A, 0x9FCE,\n\t0x719B, 0x9FCF, 0x719C, 0x9FD0, 0x719D, 0x9FD1, 0x719E, 0x9FD2,\n\t0x719F, 0xCAEC, 0x71A0, 0xECDA, 0x71A1, 0x9FD3, 0x71A2, 0x9FD4,\n\t0x71A3, 0x9FD5, 0x71A4, 0x9FD6, 0x71A5, 0x9FD7, 0x71A6, 0x9FD8,\n\t0x71A7, 0x9FD9, 0x71A8, 0xECD9, 0x71A9, 0x9FDA, 0x71AA, 0x9FDB,\n\t0x71AB, 0x9FDC, 0x71AC, 0xB0BE, 0x71AD, 0x9FDD, 0x71AE, 0x9FDE,\n\t0x71AF, 0x9FDF, 0x71B0, 0x9FE0, 0x71B1, 0x9FE1, 0x71B2, 0x9FE2,\n\t0x71B3, 0xECD7, 0x71B4, 0x9FE3, 0x71B5, 0xECD8, 0x71B6, 0x9FE4,\n\t0x71B7, 0x9FE5, 0x71B8, 0x9FE6, 0x71B9, 0xECE4, 0x71BA, 0x9FE7,\n\t0x71BB, 0x9FE8, 0x71BC, 0x9FE9, 0x71BD, 0x9FEA, 0x71BE, 0x9FEB,\n\t0x71BF, 0x9FEC, 0x71C0, 0x9FED, 0x71C1, 0x9FEE, 0x71C2, 0x9FEF,\n\t0x71C3, 0xC8BC, 0x71C4, 0x9FF0, 0x71C5, 0x9FF1, 0x71C6, 0x9FF2,\n\t0x71C7, 0x9FF3, 0x71C8, 0x9FF4, 0x71C9, 0x9FF5, 0x71CA, 0x9FF6,\n\t0x71CB, 0x9FF7, 0x71CC, 0x9FF8, 0x71CD, 0x9FF9, 0x71CE, 0xC1C7,\n\t0x71CF, 0x9FFA, 0x71D0, 0x9FFB, 0x71D1, 0x9FFC, 0x71D2, 0x9FFD,\n\t0x71D3, 0x9FFE, 0x71D4, 0xECDC, 0x71D5, 0xD1E0, 0x71D6, 0xA040,\n\t0x71D7, 0xA041, 0x71D8, 0xA042, 0x71D9, 0xA043, 0x71DA, 0xA044,\n\t0x71DB, 0xA045, 0x71DC, 0xA046, 0x71DD, 0xA047, 0x71DE, 0xA048,\n\t0x71DF, 0xA049, 0x71E0, 0xECDB, 0x71E1, 0xA04A, 0x71E2, 0xA04B,\n\t0x71E3, 0xA04C, 0x71E4, 0xA04D, 0x71E5, 0xD4EF, 0x71E6, 0xA04E,\n\t0x71E7, 0xECDD, 0x71E8, 0xA04F, 0x71E9, 0xA050, 0x71EA, 0xA051,\n\t0x71EB, 0xA052, 0x71EC, 0xA053, 0x71ED, 0xA054, 0x71EE, 0xDBC6,\n\t0x71EF, 0xA055, 0x71F0, 0xA056, 0x71F1, 0xA057, 0x71F2, 0xA058,\n\t0x71F3, 0xA059, 0x71F4, 0xA05A, 0x71F5, 0xA05B, 0x71F6, 0xA05C,\n\t0x71F7, 0xA05D, 0x71F8, 0xA05E, 0x71F9, 0xECDE, 0x71FA, 0xA05F,\n\t0x71FB, 0xA060, 0x71FC, 0xA061, 0x71FD, 0xA062, 0x71FE, 0xA063,\n\t0x71FF, 0xA064, 0x7200, 0xA065, 0x7201, 0xA066, 0x7202, 0xA067,\n\t0x7203, 0xA068, 0x7204, 0xA069, 0x7205, 0xA06A, 0x7206, 0xB1AC,\n\t0x7207, 0xA06B, 0x7208, 0xA06C, 0x7209, 0xA06D, 0x720A, 0xA06E,\n\t0x720B, 0xA06F, 0x720C, 0xA070, 0x720D, 0xA071, 0x720E, 0xA072,\n\t0x720F, 0xA073, 0x7210, 0xA074, 0x7211, 0xA075, 0x7212, 0xA076,\n\t0x7213, 0xA077, 0x7214, 0xA078, 0x7215, 0xA079, 0x7216, 0xA07A,\n\t0x7217, 0xA07B, 0x7218, 0xA07C, 0x7219, 0xA07D, 0x721A, 0xA07E,\n\t0x721B, 0xA080, 0x721C, 0xA081, 0x721D, 0xECDF, 0x721E, 0xA082,\n\t0x721F, 0xA083, 0x7220, 0xA084, 0x7221, 0xA085, 0x7222, 0xA086,\n\t0x7223, 0xA087, 0x7224, 0xA088, 0x7225, 0xA089, 0x7226, 0xA08A,\n\t0x7227, 0xA08B, 0x7228, 0xECE0, 0x7229, 0xA08C, 0x722A, 0xD7A6,\n\t0x722B, 0xA08D, 0x722C, 0xC5C0, 0x722D, 0xA08E, 0x722E, 0xA08F,\n\t0x722F, 0xA090, 0x7230, 0xEBBC, 0x7231, 0xB0AE, 0x7232, 0xA091,\n\t0x7233, 0xA092, 0x7234, 0xA093, 0x7235, 0xBEF4, 0x7236, 0xB8B8,\n\t0x7237, 0xD2AF, 0x7238, 0xB0D6, 0x7239, 0xB5F9, 0x723A, 0xA094,\n\t0x723B, 0xD8B3, 0x723C, 0xA095, 0x723D, 0xCBAC, 0x723E, 0xA096,\n\t0x723F, 0xE3DD, 0x7240, 0xA097, 0x7241, 0xA098, 0x7242, 0xA099,\n\t0x7243, 0xA09A, 0x7244, 0xA09B, 0x7245, 0xA09C, 0x7246, 0xA09D,\n\t0x7247, 0xC6AC, 0x7248, 0xB0E6, 0x7249, 0xA09E, 0x724A, 0xA09F,\n\t0x724B, 0xA0A0, 0x724C, 0xC5C6, 0x724D, 0xEBB9, 0x724E, 0xA0A1,\n\t0x724F, 0xA0A2, 0x7250, 0xA0A3, 0x7251, 0xA0A4, 0x7252, 0xEBBA,\n\t0x7253, 0xA0A5, 0x7254, 0xA0A6, 0x7255, 0xA0A7, 0x7256, 0xEBBB,\n\t0x7257, 0xA0A8, 0x7258, 0xA0A9, 0x7259, 0xD1C0, 0x725A, 0xA0AA,\n\t0x725B, 0xC5A3, 0x725C, 0xA0AB, 0x725D, 0xEAF2, 0x725E, 0xA0AC,\n\t0x725F, 0xC4B2, 0x7260, 0xA0AD, 0x7261, 0xC4B5, 0x7262, 0xC0CE,\n\t0x7263, 0xA0AE, 0x7264, 0xA0AF, 0x7265, 0xA0B0, 0x7266, 0xEAF3,\n\t0x7267, 0xC4C1, 0x7268, 0xA0B1, 0x7269, 0xCEEF, 0x726A, 0xA0B2,\n\t0x726B, 0xA0B3, 0x726C, 0xA0B4, 0x726D, 0xA0B5, 0x726E, 0xEAF0,\n\t0x726F, 0xEAF4, 0x7270, 0xA0B6, 0x7271, 0xA0B7, 0x7272, 0xC9FC,\n\t0x7273, 0xA0B8, 0x7274, 0xA0B9, 0x7275, 0xC7A3, 0x7276, 0xA0BA,\n\t0x7277, 0xA0BB, 0x7278, 0xA0BC, 0x7279, 0xCCD8, 0x727A, 0xCEFE,\n\t0x727B, 0xA0BD, 0x727C, 0xA0BE, 0x727D, 0xA0BF, 0x727E, 0xEAF5,\n\t0x727F, 0xEAF6, 0x7280, 0xCFAC, 0x7281, 0xC0E7, 0x7282, 0xA0C0,\n\t0x7283, 0xA0C1, 0x7284, 0xEAF7, 0x7285, 0xA0C2, 0x7286, 0xA0C3,\n\t0x7287, 0xA0C4, 0x7288, 0xA0C5, 0x7289, 0xA0C6, 0x728A, 0xB6BF,\n\t0x728B, 0xEAF8, 0x728C, 0xA0C7, 0x728D, 0xEAF9, 0x728E, 0xA0C8,\n\t0x728F, 0xEAFA, 0x7290, 0xA0C9, 0x7291, 0xA0CA, 0x7292, 0xEAFB,\n\t0x7293, 0xA0CB, 0x7294, 0xA0CC, 0x7295, 0xA0CD, 0x7296, 0xA0CE,\n\t0x7297, 0xA0CF, 0x7298, 0xA0D0, 0x7299, 0xA0D1, 0x729A, 0xA0D2,\n\t0x729B, 0xA0D3, 0x729C, 0xA0D4, 0x729D, 0xA0D5, 0x729E, 0xA0D6,\n\t0x729F, 0xEAF1, 0x72A0, 0xA0D7, 0x72A1, 0xA0D8, 0x72A2, 0xA0D9,\n\t0x72A3, 0xA0DA, 0x72A4, 0xA0DB, 0x72A5, 0xA0DC, 0x72A6, 0xA0DD,\n\t0x72A7, 0xA0DE, 0x72A8, 0xA0DF, 0x72A9, 0xA0E0, 0x72AA, 0xA0E1,\n\t0x72AB, 0xA0E2, 0x72AC, 0xC8AE, 0x72AD, 0xE1EB, 0x72AE, 0xA0E3,\n\t0x72AF, 0xB7B8, 0x72B0, 0xE1EC, 0x72B1, 0xA0E4, 0x72B2, 0xA0E5,\n\t0x72B3, 0xA0E6, 0x72B4, 0xE1ED, 0x72B5, 0xA0E7, 0x72B6, 0xD7B4,\n\t0x72B7, 0xE1EE, 0x72B8, 0xE1EF, 0x72B9, 0xD3CC, 0x72BA, 0xA0E8,\n\t0x72BB, 0xA0E9, 0x72BC, 0xA0EA, 0x72BD, 0xA0EB, 0x72BE, 0xA0EC,\n\t0x72BF, 0xA0ED, 0x72C0, 0xA0EE, 0x72C1, 0xE1F1, 0x72C2, 0xBFF1,\n\t0x72C3, 0xE1F0, 0x72C4, 0xB5D2, 0x72C5, 0xA0EF, 0x72C6, 0xA0F0,\n\t0x72C7, 0xA0F1, 0x72C8, 0xB1B7, 0x72C9, 0xA0F2, 0x72CA, 0xA0F3,\n\t0x72CB, 0xA0F4, 0x72CC, 0xA0F5, 0x72CD, 0xE1F3, 0x72CE, 0xE1F2,\n\t0x72CF, 0xA0F6, 0x72D0, 0xBAFC, 0x72D1, 0xA0F7, 0x72D2, 0xE1F4,\n\t0x72D3, 0xA0F8, 0x72D4, 0xA0F9, 0x72D5, 0xA0FA, 0x72D6, 0xA0FB,\n\t0x72D7, 0xB9B7, 0x72D8, 0xA0FC, 0x72D9, 0xBED1, 0x72DA, 0xA0FD,\n\t0x72DB, 0xA0FE, 0x72DC, 0xAA40, 0x72DD, 0xAA41, 0x72DE, 0xC4FC,\n\t0x72DF, 0xAA42, 0x72E0, 0xBADD, 0x72E1, 0xBDC6, 0x72E2, 0xAA43,\n\t0x72E3, 0xAA44, 0x72E4, 0xAA45, 0x72E5, 0xAA46, 0x72E6, 0xAA47,\n\t0x72E7, 0xAA48, 0x72E8, 0xE1F5, 0x72E9, 0xE1F7, 0x72EA, 0xAA49,\n\t0x72EB, 0xAA4A, 0x72EC, 0xB6C0, 0x72ED, 0xCFC1, 0x72EE, 0xCAA8,\n\t0x72EF, 0xE1F6, 0x72F0, 0xD5F8, 0x72F1, 0xD3FC, 0x72F2, 0xE1F8,\n\t0x72F3, 0xE1FC, 0x72F4, 0xE1F9, 0x72F5, 0xAA4B, 0x72F6, 0xAA4C,\n\t0x72F7, 0xE1FA, 0x72F8, 0xC0EA, 0x72F9, 0xAA4D, 0x72FA, 0xE1FE,\n\t0x72FB, 0xE2A1, 0x72FC, 0xC0C7, 0x72FD, 0xAA4E, 0x72FE, 0xAA4F,\n\t0x72FF, 0xAA50, 0x7300, 0xAA51, 0x7301, 0xE1FB, 0x7302, 0xAA52,\n\t0x7303, 0xE1FD, 0x7304, 0xAA53, 0x7305, 0xAA54, 0x7306, 0xAA55,\n\t0x7307, 0xAA56, 0x7308, 0xAA57, 0x7309, 0xAA58, 0x730A, 0xE2A5,\n\t0x730B, 0xAA59, 0x730C, 0xAA5A, 0x730D, 0xAA5B, 0x730E, 0xC1D4,\n\t0x730F, 0xAA5C, 0x7310, 0xAA5D, 0x7311, 0xAA5E, 0x7312, 0xAA5F,\n\t0x7313, 0xE2A3, 0x7314, 0xAA60, 0x7315, 0xE2A8, 0x7316, 0xB2FE,\n\t0x7317, 0xE2A2, 0x7318, 0xAA61, 0x7319, 0xAA62, 0x731A, 0xAA63,\n\t0x731B, 0xC3CD, 0x731C, 0xB2C2, 0x731D, 0xE2A7, 0x731E, 0xE2A6,\n\t0x731F, 0xAA64, 0x7320, 0xAA65, 0x7321, 0xE2A4, 0x7322, 0xE2A9,\n\t0x7323, 0xAA66, 0x7324, 0xAA67, 0x7325, 0xE2AB, 0x7326, 0xAA68,\n\t0x7327, 0xAA69, 0x7328, 0xAA6A, 0x7329, 0xD0C9, 0x732A, 0xD6ED,\n\t0x732B, 0xC3A8, 0x732C, 0xE2AC, 0x732D, 0xAA6B, 0x732E, 0xCFD7,\n\t0x732F, 0xAA6C, 0x7330, 0xAA6D, 0x7331, 0xE2AE, 0x7332, 0xAA6E,\n\t0x7333, 0xAA6F, 0x7334, 0xBAEF, 0x7335, 0xAA70, 0x7336, 0xAA71,\n\t0x7337, 0xE9E0, 0x7338, 0xE2AD, 0x7339, 0xE2AA, 0x733A, 0xAA72,\n\t0x733B, 0xAA73, 0x733C, 0xAA74, 0x733D, 0xAA75, 0x733E, 0xBBAB,\n\t0x733F, 0xD4B3, 0x7340, 0xAA76, 0x7341, 0xAA77, 0x7342, 0xAA78,\n\t0x7343, 0xAA79, 0x7344, 0xAA7A, 0x7345, 0xAA7B, 0x7346, 0xAA7C,\n\t0x7347, 0xAA7D, 0x7348, 0xAA7E, 0x7349, 0xAA80, 0x734A, 0xAA81,\n\t0x734B, 0xAA82, 0x734C, 0xAA83, 0x734D, 0xE2B0, 0x734E, 0xAA84,\n\t0x734F, 0xAA85, 0x7350, 0xE2AF, 0x7351, 0xAA86, 0x7352, 0xE9E1,\n\t0x7353, 0xAA87, 0x7354, 0xAA88, 0x7355, 0xAA89, 0x7356, 0xAA8A,\n\t0x7357, 0xE2B1, 0x7358, 0xAA8B, 0x7359, 0xAA8C, 0x735A, 0xAA8D,\n\t0x735B, 0xAA8E, 0x735C, 0xAA8F, 0x735D, 0xAA90, 0x735E, 0xAA91,\n\t0x735F, 0xAA92, 0x7360, 0xE2B2, 0x7361, 0xAA93, 0x7362, 0xAA94,\n\t0x7363, 0xAA95, 0x7364, 0xAA96, 0x7365, 0xAA97, 0x7366, 0xAA98,\n\t0x7367, 0xAA99, 0x7368, 0xAA9A, 0x7369, 0xAA9B, 0x736A, 0xAA9C,\n\t0x736B, 0xAA9D, 0x736C, 0xE2B3, 0x736D, 0xCCA1, 0x736E, 0xAA9E,\n\t0x736F, 0xE2B4, 0x7370, 0xAA9F, 0x7371, 0xAAA0, 0x7372, 0xAB40,\n\t0x7373, 0xAB41, 0x7374, 0xAB42, 0x7375, 0xAB43, 0x7376, 0xAB44,\n\t0x7377, 0xAB45, 0x7378, 0xAB46, 0x7379, 0xAB47, 0x737A, 0xAB48,\n\t0x737B, 0xAB49, 0x737C, 0xAB4A, 0x737D, 0xAB4B, 0x737E, 0xE2B5,\n\t0x737F, 0xAB4C, 0x7380, 0xAB4D, 0x7381, 0xAB4E, 0x7382, 0xAB4F,\n\t0x7383, 0xAB50, 0x7384, 0xD0FE, 0x7385, 0xAB51, 0x7386, 0xAB52,\n\t0x7387, 0xC2CA, 0x7388, 0xAB53, 0x7389, 0xD3F1, 0x738A, 0xAB54,\n\t0x738B, 0xCDF5, 0x738C, 0xAB55, 0x738D, 0xAB56, 0x738E, 0xE7E0,\n\t0x738F, 0xAB57, 0x7390, 0xAB58, 0x7391, 0xE7E1, 0x7392, 0xAB59,\n\t0x7393, 0xAB5A, 0x7394, 0xAB5B, 0x7395, 0xAB5C, 0x7396, 0xBEC1,\n\t0x7397, 0xAB5D, 0x7398, 0xAB5E, 0x7399, 0xAB5F, 0x739A, 0xAB60,\n\t0x739B, 0xC2EA, 0x739C, 0xAB61, 0x739D, 0xAB62, 0x739E, 0xAB63,\n\t0x739F, 0xE7E4, 0x73A0, 0xAB64, 0x73A1, 0xAB65, 0x73A2, 0xE7E3,\n\t0x73A3, 0xAB66, 0x73A4, 0xAB67, 0x73A5, 0xAB68, 0x73A6, 0xAB69,\n\t0x73A7, 0xAB6A, 0x73A8, 0xAB6B, 0x73A9, 0xCDE6, 0x73AA, 0xAB6C,\n\t0x73AB, 0xC3B5, 0x73AC, 0xAB6D, 0x73AD, 0xAB6E, 0x73AE, 0xE7E2,\n\t0x73AF, 0xBBB7, 0x73B0, 0xCFD6, 0x73B1, 0xAB6F, 0x73B2, 0xC1E1,\n\t0x73B3, 0xE7E9, 0x73B4, 0xAB70, 0x73B5, 0xAB71, 0x73B6, 0xAB72,\n\t0x73B7, 0xE7E8, 0x73B8, 0xAB73, 0x73B9, 0xAB74, 0x73BA, 0xE7F4,\n\t0x73BB, 0xB2A3, 0x73BC, 0xAB75, 0x73BD, 0xAB76, 0x73BE, 0xAB77,\n\t0x73BF, 0xAB78, 0x73C0, 0xE7EA, 0x73C1, 0xAB79, 0x73C2, 0xE7E6,\n\t0x73C3, 0xAB7A, 0x73C4, 0xAB7B, 0x73C5, 0xAB7C, 0x73C6, 0xAB7D,\n\t0x73C7, 0xAB7E, 0x73C8, 0xE7EC, 0x73C9, 0xE7EB, 0x73CA, 0xC9BA,\n\t0x73CB, 0xAB80, 0x73CC, 0xAB81, 0x73CD, 0xD5E4, 0x73CE, 0xAB82,\n\t0x73CF, 0xE7E5, 0x73D0, 0xB7A9, 0x73D1, 0xE7E7, 0x73D2, 0xAB83,\n\t0x73D3, 0xAB84, 0x73D4, 0xAB85, 0x73D5, 0xAB86, 0x73D6, 0xAB87,\n\t0x73D7, 0xAB88, 0x73D8, 0xAB89, 0x73D9, 0xE7EE, 0x73DA, 0xAB8A,\n\t0x73DB, 0xAB8B, 0x73DC, 0xAB8C, 0x73DD, 0xAB8D, 0x73DE, 0xE7F3,\n\t0x73DF, 0xAB8E, 0x73E0, 0xD6E9, 0x73E1, 0xAB8F, 0x73E2, 0xAB90,\n\t0x73E3, 0xAB91, 0x73E4, 0xAB92, 0x73E5, 0xE7ED, 0x73E6, 0xAB93,\n\t0x73E7, 0xE7F2, 0x73E8, 0xAB94, 0x73E9, 0xE7F1, 0x73EA, 0xAB95,\n\t0x73EB, 0xAB96, 0x73EC, 0xAB97, 0x73ED, 0xB0E0, 0x73EE, 0xAB98,\n\t0x73EF, 0xAB99, 0x73F0, 0xAB9A, 0x73F1, 0xAB9B, 0x73F2, 0xE7F5,\n\t0x73F3, 0xAB9C, 0x73F4, 0xAB9D, 0x73F5, 0xAB9E, 0x73F6, 0xAB9F,\n\t0x73F7, 0xABA0, 0x73F8, 0xAC40, 0x73F9, 0xAC41, 0x73FA, 0xAC42,\n\t0x73FB, 0xAC43, 0x73FC, 0xAC44, 0x73FD, 0xAC45, 0x73FE, 0xAC46,\n\t0x73FF, 0xAC47, 0x7400, 0xAC48, 0x7401, 0xAC49, 0x7402, 0xAC4A,\n\t0x7403, 0xC7F2, 0x7404, 0xAC4B, 0x7405, 0xC0C5, 0x7406, 0xC0ED,\n\t0x7407, 0xAC4C, 0x7408, 0xAC4D, 0x7409, 0xC1F0, 0x740A, 0xE7F0,\n\t0x740B, 0xAC4E, 0x740C, 0xAC4F, 0x740D, 0xAC50, 0x740E, 0xAC51,\n\t0x740F, 0xE7F6, 0x7410, 0xCBF6, 0x7411, 0xAC52, 0x7412, 0xAC53,\n\t0x7413, 0xAC54, 0x7414, 0xAC55, 0x7415, 0xAC56, 0x7416, 0xAC57,\n\t0x7417, 0xAC58, 0x7418, 0xAC59, 0x7419, 0xAC5A, 0x741A, 0xE8A2,\n\t0x741B, 0xE8A1, 0x741C, 0xAC5B, 0x741D, 0xAC5C, 0x741E, 0xAC5D,\n\t0x741F, 0xAC5E, 0x7420, 0xAC5F, 0x7421, 0xAC60, 0x7422, 0xD7C1,\n\t0x7423, 0xAC61, 0x7424, 0xAC62, 0x7425, 0xE7FA, 0x7426, 0xE7F9,\n\t0x7427, 0xAC63, 0x7428, 0xE7FB, 0x7429, 0xAC64, 0x742A, 0xE7F7,\n\t0x742B, 0xAC65, 0x742C, 0xE7FE, 0x742D, 0xAC66, 0x742E, 0xE7FD,\n\t0x742F, 0xAC67, 0x7430, 0xE7FC, 0x7431, 0xAC68, 0x7432, 0xAC69,\n\t0x7433, 0xC1D5, 0x7434, 0xC7D9, 0x7435, 0xC5FD, 0x7436, 0xC5C3,\n\t0x7437, 0xAC6A, 0x7438, 0xAC6B, 0x7439, 0xAC6C, 0x743A, 0xAC6D,\n\t0x743B, 0xAC6E, 0x743C, 0xC7ED, 0x743D, 0xAC6F, 0x743E, 0xAC70,\n\t0x743F, 0xAC71, 0x7440, 0xAC72, 0x7441, 0xE8A3, 0x7442, 0xAC73,\n\t0x7443, 0xAC74, 0x7444, 0xAC75, 0x7445, 0xAC76, 0x7446, 0xAC77,\n\t0x7447, 0xAC78, 0x7448, 0xAC79, 0x7449, 0xAC7A, 0x744A, 0xAC7B,\n\t0x744B, 0xAC7C, 0x744C, 0xAC7D, 0x744D, 0xAC7E, 0x744E, 0xAC80,\n\t0x744F, 0xAC81, 0x7450, 0xAC82, 0x7451, 0xAC83, 0x7452, 0xAC84,\n\t0x7453, 0xAC85, 0x7454, 0xAC86, 0x7455, 0xE8A6, 0x7456, 0xAC87,\n\t0x7457, 0xE8A5, 0x7458, 0xAC88, 0x7459, 0xE8A7, 0x745A, 0xBAF7,\n\t0x745B, 0xE7F8, 0x745C, 0xE8A4, 0x745D, 0xAC89, 0x745E, 0xC8F0,\n\t0x745F, 0xC9AA, 0x7460, 0xAC8A, 0x7461, 0xAC8B, 0x7462, 0xAC8C,\n\t0x7463, 0xAC8D, 0x7464, 0xAC8E, 0x7465, 0xAC8F, 0x7466, 0xAC90,\n\t0x7467, 0xAC91, 0x7468, 0xAC92, 0x7469, 0xAC93, 0x746A, 0xAC94,\n\t0x746B, 0xAC95, 0x746C, 0xAC96, 0x746D, 0xE8A9, 0x746E, 0xAC97,\n\t0x746F, 0xAC98, 0x7470, 0xB9E5, 0x7471, 0xAC99, 0x7472, 0xAC9A,\n\t0x7473, 0xAC9B, 0x7474, 0xAC9C, 0x7475, 0xAC9D, 0x7476, 0xD1FE,\n\t0x7477, 0xE8A8, 0x7478, 0xAC9E, 0x7479, 0xAC9F, 0x747A, 0xACA0,\n\t0x747B, 0xAD40, 0x747C, 0xAD41, 0x747D, 0xAD42, 0x747E, 0xE8AA,\n\t0x747F, 0xAD43, 0x7480, 0xE8AD, 0x7481, 0xE8AE, 0x7482, 0xAD44,\n\t0x7483, 0xC1A7, 0x7484, 0xAD45, 0x7485, 0xAD46, 0x7486, 0xAD47,\n\t0x7487, 0xE8AF, 0x7488, 0xAD48, 0x7489, 0xAD49, 0x748A, 0xAD4A,\n\t0x748B, 0xE8B0, 0x748C, 0xAD4B, 0x748D, 0xAD4C, 0x748E, 0xE8AC,\n\t0x748F, 0xAD4D, 0x7490, 0xE8B4, 0x7491, 0xAD4E, 0x7492, 0xAD4F,\n\t0x7493, 0xAD50, 0x7494, 0xAD51, 0x7495, 0xAD52, 0x7496, 0xAD53,\n\t0x7497, 0xAD54, 0x7498, 0xAD55, 0x7499, 0xAD56, 0x749A, 0xAD57,\n\t0x749B, 0xAD58, 0x749C, 0xE8AB, 0x749D, 0xAD59, 0x749E, 0xE8B1,\n\t0x749F, 0xAD5A, 0x74A0, 0xAD5B, 0x74A1, 0xAD5C, 0x74A2, 0xAD5D,\n\t0x74A3, 0xAD5E, 0x74A4, 0xAD5F, 0x74A5, 0xAD60, 0x74A6, 0xAD61,\n\t0x74A7, 0xE8B5, 0x74A8, 0xE8B2, 0x74A9, 0xE8B3, 0x74AA, 0xAD62,\n\t0x74AB, 0xAD63, 0x74AC, 0xAD64, 0x74AD, 0xAD65, 0x74AE, 0xAD66,\n\t0x74AF, 0xAD67, 0x74B0, 0xAD68, 0x74B1, 0xAD69, 0x74B2, 0xAD6A,\n\t0x74B3, 0xAD6B, 0x74B4, 0xAD6C, 0x74B5, 0xAD6D, 0x74B6, 0xAD6E,\n\t0x74B7, 0xAD6F, 0x74B8, 0xAD70, 0x74B9, 0xAD71, 0x74BA, 0xE8B7,\n\t0x74BB, 0xAD72, 0x74BC, 0xAD73, 0x74BD, 0xAD74, 0x74BE, 0xAD75,\n\t0x74BF, 0xAD76, 0x74C0, 0xAD77, 0x74C1, 0xAD78, 0x74C2, 0xAD79,\n\t0x74C3, 0xAD7A, 0x74C4, 0xAD7B, 0x74C5, 0xAD7C, 0x74C6, 0xAD7D,\n\t0x74C7, 0xAD7E, 0x74C8, 0xAD80, 0x74C9, 0xAD81, 0x74CA, 0xAD82,\n\t0x74CB, 0xAD83, 0x74CC, 0xAD84, 0x74CD, 0xAD85, 0x74CE, 0xAD86,\n\t0x74CF, 0xAD87, 0x74D0, 0xAD88, 0x74D1, 0xAD89, 0x74D2, 0xE8B6,\n\t0x74D3, 0xAD8A, 0x74D4, 0xAD8B, 0x74D5, 0xAD8C, 0x74D6, 0xAD8D,\n\t0x74D7, 0xAD8E, 0x74D8, 0xAD8F, 0x74D9, 0xAD90, 0x74DA, 0xAD91,\n\t0x74DB, 0xAD92, 0x74DC, 0xB9CF, 0x74DD, 0xAD93, 0x74DE, 0xF0AC,\n\t0x74DF, 0xAD94, 0x74E0, 0xF0AD, 0x74E1, 0xAD95, 0x74E2, 0xC6B0,\n\t0x74E3, 0xB0EA, 0x74E4, 0xC8BF, 0x74E5, 0xAD96, 0x74E6, 0xCDDF,\n\t0x74E7, 0xAD97, 0x74E8, 0xAD98, 0x74E9, 0xAD99, 0x74EA, 0xAD9A,\n\t0x74EB, 0xAD9B, 0x74EC, 0xAD9C, 0x74ED, 0xAD9D, 0x74EE, 0xCECD,\n\t0x74EF, 0xEAB1, 0x74F0, 0xAD9E, 0x74F1, 0xAD9F, 0x74F2, 0xADA0,\n\t0x74F3, 0xAE40, 0x74F4, 0xEAB2, 0x74F5, 0xAE41, 0x74F6, 0xC6BF,\n\t0x74F7, 0xB4C9, 0x74F8, 0xAE42, 0x74F9, 0xAE43, 0x74FA, 0xAE44,\n\t0x74FB, 0xAE45, 0x74FC, 0xAE46, 0x74FD, 0xAE47, 0x74FE, 0xAE48,\n\t0x74FF, 0xEAB3, 0x7500, 0xAE49, 0x7501, 0xAE4A, 0x7502, 0xAE4B,\n\t0x7503, 0xAE4C, 0x7504, 0xD5E7, 0x7505, 0xAE4D, 0x7506, 0xAE4E,\n\t0x7507, 0xAE4F, 0x7508, 0xAE50, 0x7509, 0xAE51, 0x750A, 0xAE52,\n\t0x750B, 0xAE53, 0x750C, 0xAE54, 0x750D, 0xDDF9, 0x750E, 0xAE55,\n\t0x750F, 0xEAB4, 0x7510, 0xAE56, 0x7511, 0xEAB5, 0x7512, 0xAE57,\n\t0x7513, 0xEAB6, 0x7514, 0xAE58, 0x7515, 0xAE59, 0x7516, 0xAE5A,\n\t0x7517, 0xAE5B, 0x7518, 0xB8CA, 0x7519, 0xDFB0, 0x751A, 0xC9F5,\n\t0x751B, 0xAE5C, 0x751C, 0xCCF0, 0x751D, 0xAE5D, 0x751E, 0xAE5E,\n\t0x751F, 0xC9FA, 0x7520, 0xAE5F, 0x7521, 0xAE60, 0x7522, 0xAE61,\n\t0x7523, 0xAE62, 0x7524, 0xAE63, 0x7525, 0xC9FB, 0x7526, 0xAE64,\n\t0x7527, 0xAE65, 0x7528, 0xD3C3, 0x7529, 0xCBA6, 0x752A, 0xAE66,\n\t0x752B, 0xB8A6, 0x752C, 0xF0AE, 0x752D, 0xB1C2, 0x752E, 0xAE67,\n\t0x752F, 0xE5B8, 0x7530, 0xCCEF, 0x7531, 0xD3C9, 0x7532, 0xBCD7,\n\t0x7533, 0xC9EA, 0x7534, 0xAE68, 0x7535, 0xB5E7, 0x7536, 0xAE69,\n\t0x7537, 0xC4D0, 0x7538, 0xB5E9, 0x7539, 0xAE6A, 0x753A, 0xEEAE,\n\t0x753B, 0xBBAD, 0x753C, 0xAE6B, 0x753D, 0xAE6C, 0x753E, 0xE7DE,\n\t0x753F, 0xAE6D, 0x7540, 0xEEAF, 0x7541, 0xAE6E, 0x7542, 0xAE6F,\n\t0x7543, 0xAE70, 0x7544, 0xAE71, 0x7545, 0xB3A9, 0x7546, 0xAE72,\n\t0x7547, 0xAE73, 0x7548, 0xEEB2, 0x7549, 0xAE74, 0x754A, 0xAE75,\n\t0x754B, 0xEEB1, 0x754C, 0xBDE7, 0x754D, 0xAE76, 0x754E, 0xEEB0,\n\t0x754F, 0xCEB7, 0x7550, 0xAE77, 0x7551, 0xAE78, 0x7552, 0xAE79,\n\t0x7553, 0xAE7A, 0x7554, 0xC5CF, 0x7555, 0xAE7B, 0x7556, 0xAE7C,\n\t0x7557, 0xAE7D, 0x7558, 0xAE7E, 0x7559, 0xC1F4, 0x755A, 0xDBCE,\n\t0x755B, 0xEEB3, 0x755C, 0xD0F3, 0x755D, 0xAE80, 0x755E, 0xAE81,\n\t0x755F, 0xAE82, 0x7560, 0xAE83, 0x7561, 0xAE84, 0x7562, 0xAE85,\n\t0x7563, 0xAE86, 0x7564, 0xAE87, 0x7565, 0xC2D4, 0x7566, 0xC6E8,\n\t0x7567, 0xAE88, 0x7568, 0xAE89, 0x7569, 0xAE8A, 0x756A, 0xB7AC,\n\t0x756B, 0xAE8B, 0x756C, 0xAE8C, 0x756D, 0xAE8D, 0x756E, 0xAE8E,\n\t0x756F, 0xAE8F, 0x7570, 0xAE90, 0x7571, 0xAE91, 0x7572, 0xEEB4,\n\t0x7573, 0xAE92, 0x7574, 0xB3EB, 0x7575, 0xAE93, 0x7576, 0xAE94,\n\t0x7577, 0xAE95, 0x7578, 0xBBFB, 0x7579, 0xEEB5, 0x757A, 0xAE96,\n\t0x757B, 0xAE97, 0x757C, 0xAE98, 0x757D, 0xAE99, 0x757E, 0xAE9A,\n\t0x757F, 0xE7DC, 0x7580, 0xAE9B, 0x7581, 0xAE9C, 0x7582, 0xAE9D,\n\t0x7583, 0xEEB6, 0x7584, 0xAE9E, 0x7585, 0xAE9F, 0x7586, 0xBDAE,\n\t0x7587, 0xAEA0, 0x7588, 0xAF40, 0x7589, 0xAF41, 0x758A, 0xAF42,\n\t0x758B, 0xF1E2, 0x758C, 0xAF43, 0x758D, 0xAF44, 0x758E, 0xAF45,\n\t0x758F, 0xCAE8, 0x7590, 0xAF46, 0x7591, 0xD2C9, 0x7592, 0xF0DA,\n\t0x7593, 0xAF47, 0x7594, 0xF0DB, 0x7595, 0xAF48, 0x7596, 0xF0DC,\n\t0x7597, 0xC1C6, 0x7598, 0xAF49, 0x7599, 0xB8ED, 0x759A, 0xBECE,\n\t0x759B, 0xAF4A, 0x759C, 0xAF4B, 0x759D, 0xF0DE, 0x759E, 0xAF4C,\n\t0x759F, 0xC5B1, 0x75A0, 0xF0DD, 0x75A1, 0xD1F1, 0x75A2, 0xAF4D,\n\t0x75A3, 0xF0E0, 0x75A4, 0xB0CC, 0x75A5, 0xBDEA, 0x75A6, 0xAF4E,\n\t0x75A7, 0xAF4F, 0x75A8, 0xAF50, 0x75A9, 0xAF51, 0x75AA, 0xAF52,\n\t0x75AB, 0xD2DF, 0x75AC, 0xF0DF, 0x75AD, 0xAF53, 0x75AE, 0xB4AF,\n\t0x75AF, 0xB7E8, 0x75B0, 0xF0E6, 0x75B1, 0xF0E5, 0x75B2, 0xC6A3,\n\t0x75B3, 0xF0E1, 0x75B4, 0xF0E2, 0x75B5, 0xB4C3, 0x75B6, 0xAF54,\n\t0x75B7, 0xAF55, 0x75B8, 0xF0E3, 0x75B9, 0xD5EE, 0x75BA, 0xAF56,\n\t0x75BB, 0xAF57, 0x75BC, 0xCCDB, 0x75BD, 0xBED2, 0x75BE, 0xBCB2,\n\t0x75BF, 0xAF58, 0x75C0, 0xAF59, 0x75C1, 0xAF5A, 0x75C2, 0xF0E8,\n\t0x75C3, 0xF0E7, 0x75C4, 0xF0E4, 0x75C5, 0xB2A1, 0x75C6, 0xAF5B,\n\t0x75C7, 0xD6A2, 0x75C8, 0xD3B8, 0x75C9, 0xBEB7, 0x75CA, 0xC8AC,\n\t0x75CB, 0xAF5C, 0x75CC, 0xAF5D, 0x75CD, 0xF0EA, 0x75CE, 0xAF5E,\n\t0x75CF, 0xAF5F, 0x75D0, 0xAF60, 0x75D1, 0xAF61, 0x75D2, 0xD1F7,\n\t0x75D3, 0xAF62, 0x75D4, 0xD6CC, 0x75D5, 0xBADB, 0x75D6, 0xF0E9,\n\t0x75D7, 0xAF63, 0x75D8, 0xB6BB, 0x75D9, 0xAF64, 0x75DA, 0xAF65,\n\t0x75DB, 0xCDB4, 0x75DC, 0xAF66, 0x75DD, 0xAF67, 0x75DE, 0xC6A6,\n\t0x75DF, 0xAF68, 0x75E0, 0xAF69, 0x75E1, 0xAF6A, 0x75E2, 0xC1A1,\n\t0x75E3, 0xF0EB, 0x75E4, 0xF0EE, 0x75E5, 0xAF6B, 0x75E6, 0xF0ED,\n\t0x75E7, 0xF0F0, 0x75E8, 0xF0EC, 0x75E9, 0xAF6C, 0x75EA, 0xBBBE,\n\t0x75EB, 0xF0EF, 0x75EC, 0xAF6D, 0x75ED, 0xAF6E, 0x75EE, 0xAF6F,\n\t0x75EF, 0xAF70, 0x75F0, 0xCCB5, 0x75F1, 0xF0F2, 0x75F2, 0xAF71,\n\t0x75F3, 0xAF72, 0x75F4, 0xB3D5, 0x75F5, 0xAF73, 0x75F6, 0xAF74,\n\t0x75F7, 0xAF75, 0x75F8, 0xAF76, 0x75F9, 0xB1D4, 0x75FA, 0xAF77,\n\t0x75FB, 0xAF78, 0x75FC, 0xF0F3, 0x75FD, 0xAF79, 0x75FE, 0xAF7A,\n\t0x75FF, 0xF0F4, 0x7600, 0xF0F6, 0x7601, 0xB4E1, 0x7602, 0xAF7B,\n\t0x7603, 0xF0F1, 0x7604, 0xAF7C, 0x7605, 0xF0F7, 0x7606, 0xAF7D,\n\t0x7607, 0xAF7E, 0x7608, 0xAF80, 0x7609, 0xAF81, 0x760A, 0xF0FA,\n\t0x760B, 0xAF82, 0x760C, 0xF0F8, 0x760D, 0xAF83, 0x760E, 0xAF84,\n\t0x760F, 0xAF85, 0x7610, 0xF0F5, 0x7611, 0xAF86, 0x7612, 0xAF87,\n\t0x7613, 0xAF88, 0x7614, 0xAF89, 0x7615, 0xF0FD, 0x7616, 0xAF8A,\n\t0x7617, 0xF0F9, 0x7618, 0xF0FC, 0x7619, 0xF0FE, 0x761A, 0xAF8B,\n\t0x761B, 0xF1A1, 0x761C, 0xAF8C, 0x761D, 0xAF8D, 0x761E, 0xAF8E,\n\t0x761F, 0xCEC1, 0x7620, 0xF1A4, 0x7621, 0xAF8F, 0x7622, 0xF1A3,\n\t0x7623, 0xAF90, 0x7624, 0xC1F6, 0x7625, 0xF0FB, 0x7626, 0xCADD,\n\t0x7627, 0xAF91, 0x7628, 0xAF92, 0x7629, 0xB4F1, 0x762A, 0xB1F1,\n\t0x762B, 0xCCB1, 0x762C, 0xAF93, 0x762D, 0xF1A6, 0x762E, 0xAF94,\n\t0x762F, 0xAF95, 0x7630, 0xF1A7, 0x7631, 0xAF96, 0x7632, 0xAF97,\n\t0x7633, 0xF1AC, 0x7634, 0xD5CE, 0x7635, 0xF1A9, 0x7636, 0xAF98,\n\t0x7637, 0xAF99, 0x7638, 0xC8B3, 0x7639, 0xAF9A, 0x763A, 0xAF9B,\n\t0x763B, 0xAF9C, 0x763C, 0xF1A2, 0x763D, 0xAF9D, 0x763E, 0xF1AB,\n\t0x763F, 0xF1A8, 0x7640, 0xF1A5, 0x7641, 0xAF9E, 0x7642, 0xAF9F,\n\t0x7643, 0xF1AA, 0x7644, 0xAFA0, 0x7645, 0xB040, 0x7646, 0xB041,\n\t0x7647, 0xB042, 0x7648, 0xB043, 0x7649, 0xB044, 0x764A, 0xB045,\n\t0x764B, 0xB046, 0x764C, 0xB0A9, 0x764D, 0xF1AD, 0x764E, 0xB047,\n\t0x764F, 0xB048, 0x7650, 0xB049, 0x7651, 0xB04A, 0x7652, 0xB04B,\n\t0x7653, 0xB04C, 0x7654, 0xF1AF, 0x7655, 0xB04D, 0x7656, 0xF1B1,\n\t0x7657, 0xB04E, 0x7658, 0xB04F, 0x7659, 0xB050, 0x765A, 0xB051,\n\t0x765B, 0xB052, 0x765C, 0xF1B0, 0x765D, 0xB053, 0x765E, 0xF1AE,\n\t0x765F, 0xB054, 0x7660, 0xB055, 0x7661, 0xB056, 0x7662, 0xB057,\n\t0x7663, 0xD1A2, 0x7664, 0xB058, 0x7665, 0xB059, 0x7666, 0xB05A,\n\t0x7667, 0xB05B, 0x7668, 0xB05C, 0x7669, 0xB05D, 0x766A, 0xB05E,\n\t0x766B, 0xF1B2, 0x766C, 0xB05F, 0x766D, 0xB060, 0x766E, 0xB061,\n\t0x766F, 0xF1B3, 0x7670, 0xB062, 0x7671, 0xB063, 0x7672, 0xB064,\n\t0x7673, 0xB065, 0x7674, 0xB066, 0x7675, 0xB067, 0x7676, 0xB068,\n\t0x7677, 0xB069, 0x7678, 0xB9EF, 0x7679, 0xB06A, 0x767A, 0xB06B,\n\t0x767B, 0xB5C7, 0x767C, 0xB06C, 0x767D, 0xB0D7, 0x767E, 0xB0D9,\n\t0x767F, 0xB06D, 0x7680, 0xB06E, 0x7681, 0xB06F, 0x7682, 0xD4ED,\n\t0x7683, 0xB070, 0x7684, 0xB5C4, 0x7685, 0xB071, 0x7686, 0xBDD4,\n\t0x7687, 0xBBCA, 0x7688, 0xF0A7, 0x7689, 0xB072, 0x768A, 0xB073,\n\t0x768B, 0xB8DE, 0x768C, 0xB074, 0x768D, 0xB075, 0x768E, 0xF0A8,\n\t0x768F, 0xB076, 0x7690, 0xB077, 0x7691, 0xB0A8, 0x7692, 0xB078,\n\t0x7693, 0xF0A9, 0x7694, 0xB079, 0x7695, 0xB07A, 0x7696, 0xCDEE,\n\t0x7697, 0xB07B, 0x7698, 0xB07C, 0x7699, 0xF0AA, 0x769A, 0xB07D,\n\t0x769B, 0xB07E, 0x769C, 0xB080, 0x769D, 0xB081, 0x769E, 0xB082,\n\t0x769F, 0xB083, 0x76A0, 0xB084, 0x76A1, 0xB085, 0x76A2, 0xB086,\n\t0x76A3, 0xB087, 0x76A4, 0xF0AB, 0x76A5, 0xB088, 0x76A6, 0xB089,\n\t0x76A7, 0xB08A, 0x76A8, 0xB08B, 0x76A9, 0xB08C, 0x76AA, 0xB08D,\n\t0x76AB, 0xB08E, 0x76AC, 0xB08F, 0x76AD, 0xB090, 0x76AE, 0xC6A4,\n\t0x76AF, 0xB091, 0x76B0, 0xB092, 0x76B1, 0xD6E5, 0x76B2, 0xF1E4,\n\t0x76B3, 0xB093, 0x76B4, 0xF1E5, 0x76B5, 0xB094, 0x76B6, 0xB095,\n\t0x76B7, 0xB096, 0x76B8, 0xB097, 0x76B9, 0xB098, 0x76BA, 0xB099,\n\t0x76BB, 0xB09A, 0x76BC, 0xB09B, 0x76BD, 0xB09C, 0x76BE, 0xB09D,\n\t0x76BF, 0xC3F3, 0x76C0, 0xB09E, 0x76C1, 0xB09F, 0x76C2, 0xD3DB,\n\t0x76C3, 0xB0A0, 0x76C4, 0xB140, 0x76C5, 0xD6D1, 0x76C6, 0xC5E8,\n\t0x76C7, 0xB141, 0x76C8, 0xD3AF, 0x76C9, 0xB142, 0x76CA, 0xD2E6,\n\t0x76CB, 0xB143, 0x76CC, 0xB144, 0x76CD, 0xEEC1, 0x76CE, 0xB0BB,\n\t0x76CF, 0xD5B5, 0x76D0, 0xD1CE, 0x76D1, 0xBCE0, 0x76D2, 0xBAD0,\n\t0x76D3, 0xB145, 0x76D4, 0xBFF8, 0x76D5, 0xB146, 0x76D6, 0xB8C7,\n\t0x76D7, 0xB5C1, 0x76D8, 0xC5CC, 0x76D9, 0xB147, 0x76DA, 0xB148,\n\t0x76DB, 0xCAA2, 0x76DC, 0xB149, 0x76DD, 0xB14A, 0x76DE, 0xB14B,\n\t0x76DF, 0xC3CB, 0x76E0, 0xB14C, 0x76E1, 0xB14D, 0x76E2, 0xB14E,\n\t0x76E3, 0xB14F, 0x76E4, 0xB150, 0x76E5, 0xEEC2, 0x76E6, 0xB151,\n\t0x76E7, 0xB152, 0x76E8, 0xB153, 0x76E9, 0xB154, 0x76EA, 0xB155,\n\t0x76EB, 0xB156, 0x76EC, 0xB157, 0x76ED, 0xB158, 0x76EE, 0xC4BF,\n\t0x76EF, 0xB6A2, 0x76F0, 0xB159, 0x76F1, 0xEDEC, 0x76F2, 0xC3A4,\n\t0x76F3, 0xB15A, 0x76F4, 0xD6B1, 0x76F5, 0xB15B, 0x76F6, 0xB15C,\n\t0x76F7, 0xB15D, 0x76F8, 0xCFE0, 0x76F9, 0xEDEF, 0x76FA, 0xB15E,\n\t0x76FB, 0xB15F, 0x76FC, 0xC5CE, 0x76FD, 0xB160, 0x76FE, 0xB6DC,\n\t0x76FF, 0xB161, 0x7700, 0xB162, 0x7701, 0xCAA1, 0x7702, 0xB163,\n\t0x7703, 0xB164, 0x7704, 0xEDED, 0x7705, 0xB165, 0x7706, 0xB166,\n\t0x7707, 0xEDF0, 0x7708, 0xEDF1, 0x7709, 0xC3BC, 0x770A, 0xB167,\n\t0x770B, 0xBFB4, 0x770C, 0xB168, 0x770D, 0xEDEE, 0x770E, 0xB169,\n\t0x770F, 0xB16A, 0x7710, 0xB16B, 0x7711, 0xB16C, 0x7712, 0xB16D,\n\t0x7713, 0xB16E, 0x7714, 0xB16F, 0x7715, 0xB170, 0x7716, 0xB171,\n\t0x7717, 0xB172, 0x7718, 0xB173, 0x7719, 0xEDF4, 0x771A, 0xEDF2,\n\t0x771B, 0xB174, 0x771C, 0xB175, 0x771D, 0xB176, 0x771E, 0xB177,\n\t0x771F, 0xD5E6, 0x7720, 0xC3DF, 0x7721, 0xB178, 0x7722, 0xEDF3,\n\t0x7723, 0xB179, 0x7724, 0xB17A, 0x7725, 0xB17B, 0x7726, 0xEDF6,\n\t0x7727, 0xB17C, 0x7728, 0xD5A3, 0x7729, 0xD1A3, 0x772A, 0xB17D,\n\t0x772B, 0xB17E, 0x772C, 0xB180, 0x772D, 0xEDF5, 0x772E, 0xB181,\n\t0x772F, 0xC3D0, 0x7730, 0xB182, 0x7731, 0xB183, 0x7732, 0xB184,\n\t0x7733, 0xB185, 0x7734, 0xB186, 0x7735, 0xEDF7, 0x7736, 0xBFF4,\n\t0x7737, 0xBEEC, 0x7738, 0xEDF8, 0x7739, 0xB187, 0x773A, 0xCCF7,\n\t0x773B, 0xB188, 0x773C, 0xD1DB, 0x773D, 0xB189, 0x773E, 0xB18A,\n\t0x773F, 0xB18B, 0x7740, 0xD7C5, 0x7741, 0xD5F6, 0x7742, 0xB18C,\n\t0x7743, 0xEDFC, 0x7744, 0xB18D, 0x7745, 0xB18E, 0x7746, 0xB18F,\n\t0x7747, 0xEDFB, 0x7748, 0xB190, 0x7749, 0xB191, 0x774A, 0xB192,\n\t0x774B, 0xB193, 0x774C, 0xB194, 0x774D, 0xB195, 0x774E, 0xB196,\n\t0x774F, 0xB197, 0x7750, 0xEDF9, 0x7751, 0xEDFA, 0x7752, 0xB198,\n\t0x7753, 0xB199, 0x7754, 0xB19A, 0x7755, 0xB19B, 0x7756, 0xB19C,\n\t0x7757, 0xB19D, 0x7758, 0xB19E, 0x7759, 0xB19F, 0x775A, 0xEDFD,\n\t0x775B, 0xBEA6, 0x775C, 0xB1A0, 0x775D, 0xB240, 0x775E, 0xB241,\n\t0x775F, 0xB242, 0x7760, 0xB243, 0x7761, 0xCBAF, 0x7762, 0xEEA1,\n\t0x7763, 0xB6BD, 0x7764, 0xB244, 0x7765, 0xEEA2, 0x7766, 0xC4C0,\n\t0x7767, 0xB245, 0x7768, 0xEDFE, 0x7769, 0xB246, 0x776A, 0xB247,\n\t0x776B, 0xBDDE, 0x776C, 0xB2C7, 0x776D, 0xB248, 0x776E, 0xB249,\n\t0x776F, 0xB24A, 0x7770, 0xB24B, 0x7771, 0xB24C, 0x7772, 0xB24D,\n\t0x7773, 0xB24E, 0x7774, 0xB24F, 0x7775, 0xB250, 0x7776, 0xB251,\n\t0x7777, 0xB252, 0x7778, 0xB253, 0x7779, 0xB6C3, 0x777A, 0xB254,\n\t0x777B, 0xB255, 0x777C, 0xB256, 0x777D, 0xEEA5, 0x777E, 0xD8BA,\n\t0x777F, 0xEEA3, 0x7780, 0xEEA6, 0x7781, 0xB257, 0x7782, 0xB258,\n\t0x7783, 0xB259, 0x7784, 0xC3E9, 0x7785, 0xB3F2, 0x7786, 0xB25A,\n\t0x7787, 0xB25B, 0x7788, 0xB25C, 0x7789, 0xB25D, 0x778A, 0xB25E,\n\t0x778B, 0xB25F, 0x778C, 0xEEA7, 0x778D, 0xEEA4, 0x778E, 0xCFB9,\n\t0x778F, 0xB260, 0x7790, 0xB261, 0x7791, 0xEEA8, 0x7792, 0xC2F7,\n\t0x7793, 0xB262, 0x7794, 0xB263, 0x7795, 0xB264, 0x7796, 0xB265,\n\t0x7797, 0xB266, 0x7798, 0xB267, 0x7799, 0xB268, 0x779A, 0xB269,\n\t0x779B, 0xB26A, 0x779C, 0xB26B, 0x779D, 0xB26C, 0x779E, 0xB26D,\n\t0x779F, 0xEEA9, 0x77A0, 0xEEAA, 0x77A1, 0xB26E, 0x77A2, 0xDEAB,\n\t0x77A3, 0xB26F, 0x77A4, 0xB270, 0x77A5, 0xC6B3, 0x77A6, 0xB271,\n\t0x77A7, 0xC7C6, 0x77A8, 0xB272, 0x77A9, 0xD6F5, 0x77AA, 0xB5C9,\n\t0x77AB, 0xB273, 0x77AC, 0xCBB2, 0x77AD, 0xB274, 0x77AE, 0xB275,\n\t0x77AF, 0xB276, 0x77B0, 0xEEAB, 0x77B1, 0xB277, 0x77B2, 0xB278,\n\t0x77B3, 0xCDAB, 0x77B4, 0xB279, 0x77B5, 0xEEAC, 0x77B6, 0xB27A,\n\t0x77B7, 0xB27B, 0x77B8, 0xB27C, 0x77B9, 0xB27D, 0x77BA, 0xB27E,\n\t0x77BB, 0xD5B0, 0x77BC, 0xB280, 0x77BD, 0xEEAD, 0x77BE, 0xB281,\n\t0x77BF, 0xF6C4, 0x77C0, 0xB282, 0x77C1, 0xB283, 0x77C2, 0xB284,\n\t0x77C3, 0xB285, 0x77C4, 0xB286, 0x77C5, 0xB287, 0x77C6, 0xB288,\n\t0x77C7, 0xB289, 0x77C8, 0xB28A, 0x77C9, 0xB28B, 0x77CA, 0xB28C,\n\t0x77CB, 0xB28D, 0x77CC, 0xB28E, 0x77CD, 0xDBC7, 0x77CE, 0xB28F,\n\t0x77CF, 0xB290, 0x77D0, 0xB291, 0x77D1, 0xB292, 0x77D2, 0xB293,\n\t0x77D3, 0xB294, 0x77D4, 0xB295, 0x77D5, 0xB296, 0x77D6, 0xB297,\n\t0x77D7, 0xB4A3, 0x77D8, 0xB298, 0x77D9, 0xB299, 0x77DA, 0xB29A,\n\t0x77DB, 0xC3AC, 0x77DC, 0xF1E6, 0x77DD, 0xB29B, 0x77DE, 0xB29C,\n\t0x77DF, 0xB29D, 0x77E0, 0xB29E, 0x77E1, 0xB29F, 0x77E2, 0xCAB8,\n\t0x77E3, 0xD2D3, 0x77E4, 0xB2A0, 0x77E5, 0xD6AA, 0x77E6, 0xB340,\n\t0x77E7, 0xEFF2, 0x77E8, 0xB341, 0x77E9, 0xBED8, 0x77EA, 0xB342,\n\t0x77EB, 0xBDC3, 0x77EC, 0xEFF3, 0x77ED, 0xB6CC, 0x77EE, 0xB0AB,\n\t0x77EF, 0xB343, 0x77F0, 0xB344, 0x77F1, 0xB345, 0x77F2, 0xB346,\n\t0x77F3, 0xCAAF, 0x77F4, 0xB347, 0x77F5, 0xB348, 0x77F6, 0xEDB6,\n\t0x77F7, 0xB349, 0x77F8, 0xEDB7, 0x77F9, 0xB34A, 0x77FA, 0xB34B,\n\t0x77FB, 0xB34C, 0x77FC, 0xB34D, 0x77FD, 0xCEF9, 0x77FE, 0xB7AF,\n\t0x77FF, 0xBFF3, 0x7800, 0xEDB8, 0x7801, 0xC2EB, 0x7802, 0xC9B0,\n\t0x7803, 0xB34E, 0x7804, 0xB34F, 0x7805, 0xB350, 0x7806, 0xB351,\n\t0x7807, 0xB352, 0x7808, 0xB353, 0x7809, 0xEDB9, 0x780A, 0xB354,\n\t0x780B, 0xB355, 0x780C, 0xC6F6, 0x780D, 0xBFB3, 0x780E, 0xB356,\n\t0x780F, 0xB357, 0x7810, 0xB358, 0x7811, 0xEDBC, 0x7812, 0xC5F8,\n\t0x7813, 0xB359, 0x7814, 0xD1D0, 0x7815, 0xB35A, 0x7816, 0xD7A9,\n\t0x7817, 0xEDBA, 0x7818, 0xEDBB, 0x7819, 0xB35B, 0x781A, 0xD1E2,\n\t0x781B, 0xB35C, 0x781C, 0xEDBF, 0x781D, 0xEDC0, 0x781E, 0xB35D,\n\t0x781F, 0xEDC4, 0x7820, 0xB35E, 0x7821, 0xB35F, 0x7822, 0xB360,\n\t0x7823, 0xEDC8, 0x7824, 0xB361, 0x7825, 0xEDC6, 0x7826, 0xEDCE,\n\t0x7827, 0xD5E8, 0x7828, 0xB362, 0x7829, 0xEDC9, 0x782A, 0xB363,\n\t0x782B, 0xB364, 0x782C, 0xEDC7, 0x782D, 0xEDBE, 0x782E, 0xB365,\n\t0x782F, 0xB366, 0x7830, 0xC5E9, 0x7831, 0xB367, 0x7832, 0xB368,\n\t0x7833, 0xB369, 0x7834, 0xC6C6, 0x7835, 0xB36A, 0x7836, 0xB36B,\n\t0x7837, 0xC9E9, 0x7838, 0xD4D2, 0x7839, 0xEDC1, 0x783A, 0xEDC2,\n\t0x783B, 0xEDC3, 0x783C, 0xEDC5, 0x783D, 0xB36C, 0x783E, 0xC0F9,\n\t0x783F, 0xB36D, 0x7840, 0xB4A1, 0x7841, 0xB36E, 0x7842, 0xB36F,\n\t0x7843, 0xB370, 0x7844, 0xB371, 0x7845, 0xB9E8, 0x7846, 0xB372,\n\t0x7847, 0xEDD0, 0x7848, 0xB373, 0x7849, 0xB374, 0x784A, 0xB375,\n\t0x784B, 0xB376, 0x784C, 0xEDD1, 0x784D, 0xB377, 0x784E, 0xEDCA,\n\t0x784F, 0xB378, 0x7850, 0xEDCF, 0x7851, 0xB379, 0x7852, 0xCEF8,\n\t0x7853, 0xB37A, 0x7854, 0xB37B, 0x7855, 0xCBB6, 0x7856, 0xEDCC,\n\t0x7857, 0xEDCD, 0x7858, 0xB37C, 0x7859, 0xB37D, 0x785A, 0xB37E,\n\t0x785B, 0xB380, 0x785C, 0xB381, 0x785D, 0xCFF5, 0x785E, 0xB382,\n\t0x785F, 0xB383, 0x7860, 0xB384, 0x7861, 0xB385, 0x7862, 0xB386,\n\t0x7863, 0xB387, 0x7864, 0xB388, 0x7865, 0xB389, 0x7866, 0xB38A,\n\t0x7867, 0xB38B, 0x7868, 0xB38C, 0x7869, 0xB38D, 0x786A, 0xEDD2,\n\t0x786B, 0xC1F2, 0x786C, 0xD3B2, 0x786D, 0xEDCB, 0x786E, 0xC8B7,\n\t0x786F, 0xB38E, 0x7870, 0xB38F, 0x7871, 0xB390, 0x7872, 0xB391,\n\t0x7873, 0xB392, 0x7874, 0xB393, 0x7875, 0xB394, 0x7876, 0xB395,\n\t0x7877, 0xBCEF, 0x7878, 0xB396, 0x7879, 0xB397, 0x787A, 0xB398,\n\t0x787B, 0xB399, 0x787C, 0xC5F0, 0x787D, 0xB39A, 0x787E, 0xB39B,\n\t0x787F, 0xB39C, 0x7880, 0xB39D, 0x7881, 0xB39E, 0x7882, 0xB39F,\n\t0x7883, 0xB3A0, 0x7884, 0xB440, 0x7885, 0xB441, 0x7886, 0xB442,\n\t0x7887, 0xEDD6, 0x7888, 0xB443, 0x7889, 0xB5EF, 0x788A, 0xB444,\n\t0x788B, 0xB445, 0x788C, 0xC2B5, 0x788D, 0xB0AD, 0x788E, 0xCBE9,\n\t0x788F, 0xB446, 0x7890, 0xB447, 0x7891, 0xB1AE, 0x7892, 0xB448,\n\t0x7893, 0xEDD4, 0x7894, 0xB449, 0x7895, 0xB44A, 0x7896, 0xB44B,\n\t0x7897, 0xCDEB, 0x7898, 0xB5E2, 0x7899, 0xB44C, 0x789A, 0xEDD5,\n\t0x789B, 0xEDD3, 0x789C, 0xEDD7, 0x789D, 0xB44D, 0x789E, 0xB44E,\n\t0x789F, 0xB5FA, 0x78A0, 0xB44F, 0x78A1, 0xEDD8, 0x78A2, 0xB450,\n\t0x78A3, 0xEDD9, 0x78A4, 0xB451, 0x78A5, 0xEDDC, 0x78A6, 0xB452,\n\t0x78A7, 0xB1CC, 0x78A8, 0xB453, 0x78A9, 0xB454, 0x78AA, 0xB455,\n\t0x78AB, 0xB456, 0x78AC, 0xB457, 0x78AD, 0xB458, 0x78AE, 0xB459,\n\t0x78AF, 0xB45A, 0x78B0, 0xC5F6, 0x78B1, 0xBCEE, 0x78B2, 0xEDDA,\n\t0x78B3, 0xCCBC, 0x78B4, 0xB2EA, 0x78B5, 0xB45B, 0x78B6, 0xB45C,\n\t0x78B7, 0xB45D, 0x78B8, 0xB45E, 0x78B9, 0xEDDB, 0x78BA, 0xB45F,\n\t0x78BB, 0xB460, 0x78BC, 0xB461, 0x78BD, 0xB462, 0x78BE, 0xC4EB,\n\t0x78BF, 0xB463, 0x78C0, 0xB464, 0x78C1, 0xB4C5, 0x78C2, 0xB465,\n\t0x78C3, 0xB466, 0x78C4, 0xB467, 0x78C5, 0xB0F5, 0x78C6, 0xB468,\n\t0x78C7, 0xB469, 0x78C8, 0xB46A, 0x78C9, 0xEDDF, 0x78CA, 0xC0DA,\n\t0x78CB, 0xB4E8, 0x78CC, 0xB46B, 0x78CD, 0xB46C, 0x78CE, 0xB46D,\n\t0x78CF, 0xB46E, 0x78D0, 0xC5CD, 0x78D1, 0xB46F, 0x78D2, 0xB470,\n\t0x78D3, 0xB471, 0x78D4, 0xEDDD, 0x78D5, 0xBFC4, 0x78D6, 0xB472,\n\t0x78D7, 0xB473, 0x78D8, 0xB474, 0x78D9, 0xEDDE, 0x78DA, 0xB475,\n\t0x78DB, 0xB476, 0x78DC, 0xB477, 0x78DD, 0xB478, 0x78DE, 0xB479,\n\t0x78DF, 0xB47A, 0x78E0, 0xB47B, 0x78E1, 0xB47C, 0x78E2, 0xB47D,\n\t0x78E3, 0xB47E, 0x78E4, 0xB480, 0x78E5, 0xB481, 0x78E6, 0xB482,\n\t0x78E7, 0xB483, 0x78E8, 0xC4A5, 0x78E9, 0xB484, 0x78EA, 0xB485,\n\t0x78EB, 0xB486, 0x78EC, 0xEDE0, 0x78ED, 0xB487, 0x78EE, 0xB488,\n\t0x78EF, 0xB489, 0x78F0, 0xB48A, 0x78F1, 0xB48B, 0x78F2, 0xEDE1,\n\t0x78F3, 0xB48C, 0x78F4, 0xEDE3, 0x78F5, 0xB48D, 0x78F6, 0xB48E,\n\t0x78F7, 0xC1D7, 0x78F8, 0xB48F, 0x78F9, 0xB490, 0x78FA, 0xBBC7,\n\t0x78FB, 0xB491, 0x78FC, 0xB492, 0x78FD, 0xB493, 0x78FE, 0xB494,\n\t0x78FF, 0xB495, 0x7900, 0xB496, 0x7901, 0xBDB8, 0x7902, 0xB497,\n\t0x7903, 0xB498, 0x7904, 0xB499, 0x7905, 0xEDE2, 0x7906, 0xB49A,\n\t0x7907, 0xB49B, 0x7908, 0xB49C, 0x7909, 0xB49D, 0x790A, 0xB49E,\n\t0x790B, 0xB49F, 0x790C, 0xB4A0, 0x790D, 0xB540, 0x790E, 0xB541,\n\t0x790F, 0xB542, 0x7910, 0xB543, 0x7911, 0xB544, 0x7912, 0xB545,\n\t0x7913, 0xEDE4, 0x7914, 0xB546, 0x7915, 0xB547, 0x7916, 0xB548,\n\t0x7917, 0xB549, 0x7918, 0xB54A, 0x7919, 0xB54B, 0x791A, 0xB54C,\n\t0x791B, 0xB54D, 0x791C, 0xB54E, 0x791D, 0xB54F, 0x791E, 0xEDE6,\n\t0x791F, 0xB550, 0x7920, 0xB551, 0x7921, 0xB552, 0x7922, 0xB553,\n\t0x7923, 0xB554, 0x7924, 0xEDE5, 0x7925, 0xB555, 0x7926, 0xB556,\n\t0x7927, 0xB557, 0x7928, 0xB558, 0x7929, 0xB559, 0x792A, 0xB55A,\n\t0x792B, 0xB55B, 0x792C, 0xB55C, 0x792D, 0xB55D, 0x792E, 0xB55E,\n\t0x792F, 0xB55F, 0x7930, 0xB560, 0x7931, 0xB561, 0x7932, 0xB562,\n\t0x7933, 0xB563, 0x7934, 0xEDE7, 0x7935, 0xB564, 0x7936, 0xB565,\n\t0x7937, 0xB566, 0x7938, 0xB567, 0x7939, 0xB568, 0x793A, 0xCABE,\n\t0x793B, 0xECEA, 0x793C, 0xC0F1, 0x793D, 0xB569, 0x793E, 0xC9E7,\n\t0x793F, 0xB56A, 0x7940, 0xECEB, 0x7941, 0xC6EE, 0x7942, 0xB56B,\n\t0x7943, 0xB56C, 0x7944, 0xB56D, 0x7945, 0xB56E, 0x7946, 0xECEC,\n\t0x7947, 0xB56F, 0x7948, 0xC6ED, 0x7949, 0xECED, 0x794A, 0xB570,\n\t0x794B, 0xB571, 0x794C, 0xB572, 0x794D, 0xB573, 0x794E, 0xB574,\n\t0x794F, 0xB575, 0x7950, 0xB576, 0x7951, 0xB577, 0x7952, 0xB578,\n\t0x7953, 0xECF0, 0x7954, 0xB579, 0x7955, 0xB57A, 0x7956, 0xD7E6,\n\t0x7957, 0xECF3, 0x7958, 0xB57B, 0x7959, 0xB57C, 0x795A, 0xECF1,\n\t0x795B, 0xECEE, 0x795C, 0xECEF, 0x795D, 0xD7A3, 0x795E, 0xC9F1,\n\t0x795F, 0xCBEE, 0x7960, 0xECF4, 0x7961, 0xB57D, 0x7962, 0xECF2,\n\t0x7963, 0xB57E, 0x7964, 0xB580, 0x7965, 0xCFE9, 0x7966, 0xB581,\n\t0x7967, 0xECF6, 0x7968, 0xC6B1, 0x7969, 0xB582, 0x796A, 0xB583,\n\t0x796B, 0xB584, 0x796C, 0xB585, 0x796D, 0xBCC0, 0x796E, 0xB586,\n\t0x796F, 0xECF5, 0x7970, 0xB587, 0x7971, 0xB588, 0x7972, 0xB589,\n\t0x7973, 0xB58A, 0x7974, 0xB58B, 0x7975, 0xB58C, 0x7976, 0xB58D,\n\t0x7977, 0xB5BB, 0x7978, 0xBBF6, 0x7979, 0xB58E, 0x797A, 0xECF7,\n\t0x797B, 0xB58F, 0x797C, 0xB590, 0x797D, 0xB591, 0x797E, 0xB592,\n\t0x797F, 0xB593, 0x7980, 0xD9F7, 0x7981, 0xBDFB, 0x7982, 0xB594,\n\t0x7983, 0xB595, 0x7984, 0xC2BB, 0x7985, 0xECF8, 0x7986, 0xB596,\n\t0x7987, 0xB597, 0x7988, 0xB598, 0x7989, 0xB599, 0x798A, 0xECF9,\n\t0x798B, 0xB59A, 0x798C, 0xB59B, 0x798D, 0xB59C, 0x798E, 0xB59D,\n\t0x798F, 0xB8A3, 0x7990, 0xB59E, 0x7991, 0xB59F, 0x7992, 0xB5A0,\n\t0x7993, 0xB640, 0x7994, 0xB641, 0x7995, 0xB642, 0x7996, 0xB643,\n\t0x7997, 0xB644, 0x7998, 0xB645, 0x7999, 0xB646, 0x799A, 0xECFA,\n\t0x799B, 0xB647, 0x799C, 0xB648, 0x799D, 0xB649, 0x799E, 0xB64A,\n\t0x799F, 0xB64B, 0x79A0, 0xB64C, 0x79A1, 0xB64D, 0x79A2, 0xB64E,\n\t0x79A3, 0xB64F, 0x79A4, 0xB650, 0x79A5, 0xB651, 0x79A6, 0xB652,\n\t0x79A7, 0xECFB, 0x79A8, 0xB653, 0x79A9, 0xB654, 0x79AA, 0xB655,\n\t0x79AB, 0xB656, 0x79AC, 0xB657, 0x79AD, 0xB658, 0x79AE, 0xB659,\n\t0x79AF, 0xB65A, 0x79B0, 0xB65B, 0x79B1, 0xB65C, 0x79B2, 0xB65D,\n\t0x79B3, 0xECFC, 0x79B4, 0xB65E, 0x79B5, 0xB65F, 0x79B6, 0xB660,\n\t0x79B7, 0xB661, 0x79B8, 0xB662, 0x79B9, 0xD3ED, 0x79BA, 0xD8AE,\n\t0x79BB, 0xC0EB, 0x79BC, 0xB663, 0x79BD, 0xC7DD, 0x79BE, 0xBACC,\n\t0x79BF, 0xB664, 0x79C0, 0xD0E3, 0x79C1, 0xCBBD, 0x79C2, 0xB665,\n\t0x79C3, 0xCDBA, 0x79C4, 0xB666, 0x79C5, 0xB667, 0x79C6, 0xB8D1,\n\t0x79C7, 0xB668, 0x79C8, 0xB669, 0x79C9, 0xB1FC, 0x79CA, 0xB66A,\n\t0x79CB, 0xC7EF, 0x79CC, 0xB66B, 0x79CD, 0xD6D6, 0x79CE, 0xB66C,\n\t0x79CF, 0xB66D, 0x79D0, 0xB66E, 0x79D1, 0xBFC6, 0x79D2, 0xC3EB,\n\t0x79D3, 0xB66F, 0x79D4, 0xB670, 0x79D5, 0xEFF5, 0x79D6, 0xB671,\n\t0x79D7, 0xB672, 0x79D8, 0xC3D8, 0x79D9, 0xB673, 0x79DA, 0xB674,\n\t0x79DB, 0xB675, 0x79DC, 0xB676, 0x79DD, 0xB677, 0x79DE, 0xB678,\n\t0x79DF, 0xD7E2, 0x79E0, 0xB679, 0x79E1, 0xB67A, 0x79E2, 0xB67B,\n\t0x79E3, 0xEFF7, 0x79E4, 0xB3D3, 0x79E5, 0xB67C, 0x79E6, 0xC7D8,\n\t0x79E7, 0xD1ED, 0x79E8, 0xB67D, 0x79E9, 0xD6C8, 0x79EA, 0xB67E,\n\t0x79EB, 0xEFF8, 0x79EC, 0xB680, 0x79ED, 0xEFF6, 0x79EE, 0xB681,\n\t0x79EF, 0xBBFD, 0x79F0, 0xB3C6, 0x79F1, 0xB682, 0x79F2, 0xB683,\n\t0x79F3, 0xB684, 0x79F4, 0xB685, 0x79F5, 0xB686, 0x79F6, 0xB687,\n\t0x79F7, 0xB688, 0x79F8, 0xBDD5, 0x79F9, 0xB689, 0x79FA, 0xB68A,\n\t0x79FB, 0xD2C6, 0x79FC, 0xB68B, 0x79FD, 0xBBE0, 0x79FE, 0xB68C,\n\t0x79FF, 0xB68D, 0x7A00, 0xCFA1, 0x7A01, 0xB68E, 0x7A02, 0xEFFC,\n\t0x7A03, 0xEFFB, 0x7A04, 0xB68F, 0x7A05, 0xB690, 0x7A06, 0xEFF9,\n\t0x7A07, 0xB691, 0x7A08, 0xB692, 0x7A09, 0xB693, 0x7A0A, 0xB694,\n\t0x7A0B, 0xB3CC, 0x7A0C, 0xB695, 0x7A0D, 0xC9D4, 0x7A0E, 0xCBB0,\n\t0x7A0F, 0xB696, 0x7A10, 0xB697, 0x7A11, 0xB698, 0x7A12, 0xB699,\n\t0x7A13, 0xB69A, 0x7A14, 0xEFFE, 0x7A15, 0xB69B, 0x7A16, 0xB69C,\n\t0x7A17, 0xB0DE, 0x7A18, 0xB69D, 0x7A19, 0xB69E, 0x7A1A, 0xD6C9,\n\t0x7A1B, 0xB69F, 0x7A1C, 0xB6A0, 0x7A1D, 0xB740, 0x7A1E, 0xEFFD,\n\t0x7A1F, 0xB741, 0x7A20, 0xB3ED, 0x7A21, 0xB742, 0x7A22, 0xB743,\n\t0x7A23, 0xF6D5, 0x7A24, 0xB744, 0x7A25, 0xB745, 0x7A26, 0xB746,\n\t0x7A27, 0xB747, 0x7A28, 0xB748, 0x7A29, 0xB749, 0x7A2A, 0xB74A,\n\t0x7A2B, 0xB74B, 0x7A2C, 0xB74C, 0x7A2D, 0xB74D, 0x7A2E, 0xB74E,\n\t0x7A2F, 0xB74F, 0x7A30, 0xB750, 0x7A31, 0xB751, 0x7A32, 0xB752,\n\t0x7A33, 0xCEC8, 0x7A34, 0xB753, 0x7A35, 0xB754, 0x7A36, 0xB755,\n\t0x7A37, 0xF0A2, 0x7A38, 0xB756, 0x7A39, 0xF0A1, 0x7A3A, 0xB757,\n\t0x7A3B, 0xB5BE, 0x7A3C, 0xBCDA, 0x7A3D, 0xBBFC, 0x7A3E, 0xB758,\n\t0x7A3F, 0xB8E5, 0x7A40, 0xB759, 0x7A41, 0xB75A, 0x7A42, 0xB75B,\n\t0x7A43, 0xB75C, 0x7A44, 0xB75D, 0x7A45, 0xB75E, 0x7A46, 0xC4C2,\n\t0x7A47, 0xB75F, 0x7A48, 0xB760, 0x7A49, 0xB761, 0x7A4A, 0xB762,\n\t0x7A4B, 0xB763, 0x7A4C, 0xB764, 0x7A4D, 0xB765, 0x7A4E, 0xB766,\n\t0x7A4F, 0xB767, 0x7A50, 0xB768, 0x7A51, 0xF0A3, 0x7A52, 0xB769,\n\t0x7A53, 0xB76A, 0x7A54, 0xB76B, 0x7A55, 0xB76C, 0x7A56, 0xB76D,\n\t0x7A57, 0xCBEB, 0x7A58, 0xB76E, 0x7A59, 0xB76F, 0x7A5A, 0xB770,\n\t0x7A5B, 0xB771, 0x7A5C, 0xB772, 0x7A5D, 0xB773, 0x7A5E, 0xB774,\n\t0x7A5F, 0xB775, 0x7A60, 0xB776, 0x7A61, 0xB777, 0x7A62, 0xB778,\n\t0x7A63, 0xB779, 0x7A64, 0xB77A, 0x7A65, 0xB77B, 0x7A66, 0xB77C,\n\t0x7A67, 0xB77D, 0x7A68, 0xB77E, 0x7A69, 0xB780, 0x7A6A, 0xB781,\n\t0x7A6B, 0xB782, 0x7A6C, 0xB783, 0x7A6D, 0xB784, 0x7A6E, 0xB785,\n\t0x7A6F, 0xB786, 0x7A70, 0xF0A6, 0x7A71, 0xB787, 0x7A72, 0xB788,\n\t0x7A73, 0xB789, 0x7A74, 0xD1A8, 0x7A75, 0xB78A, 0x7A76, 0xBEBF,\n\t0x7A77, 0xC7EE, 0x7A78, 0xF1B6, 0x7A79, 0xF1B7, 0x7A7A, 0xBFD5,\n\t0x7A7B, 0xB78B, 0x7A7C, 0xB78C, 0x7A7D, 0xB78D, 0x7A7E, 0xB78E,\n\t0x7A7F, 0xB4A9, 0x7A80, 0xF1B8, 0x7A81, 0xCDBB, 0x7A82, 0xB78F,\n\t0x7A83, 0xC7D4, 0x7A84, 0xD5AD, 0x7A85, 0xB790, 0x7A86, 0xF1B9,\n\t0x7A87, 0xB791, 0x7A88, 0xF1BA, 0x7A89, 0xB792, 0x7A8A, 0xB793,\n\t0x7A8B, 0xB794, 0x7A8C, 0xB795, 0x7A8D, 0xC7CF, 0x7A8E, 0xB796,\n\t0x7A8F, 0xB797, 0x7A90, 0xB798, 0x7A91, 0xD2A4, 0x7A92, 0xD6CF,\n\t0x7A93, 0xB799, 0x7A94, 0xB79A, 0x7A95, 0xF1BB, 0x7A96, 0xBDD1,\n\t0x7A97, 0xB4B0, 0x7A98, 0xBEBD, 0x7A99, 0xB79B, 0x7A9A, 0xB79C,\n\t0x7A9B, 0xB79D, 0x7A9C, 0xB4DC, 0x7A9D, 0xCED1, 0x7A9E, 0xB79E,\n\t0x7A9F, 0xBFDF, 0x7AA0, 0xF1BD, 0x7AA1, 0xB79F, 0x7AA2, 0xB7A0,\n\t0x7AA3, 0xB840, 0x7AA4, 0xB841, 0x7AA5, 0xBFFA, 0x7AA6, 0xF1BC,\n\t0x7AA7, 0xB842, 0x7AA8, 0xF1BF, 0x7AA9, 0xB843, 0x7AAA, 0xB844,\n\t0x7AAB, 0xB845, 0x7AAC, 0xF1BE, 0x7AAD, 0xF1C0, 0x7AAE, 0xB846,\n\t0x7AAF, 0xB847, 0x7AB0, 0xB848, 0x7AB1, 0xB849, 0x7AB2, 0xB84A,\n\t0x7AB3, 0xF1C1, 0x7AB4, 0xB84B, 0x7AB5, 0xB84C, 0x7AB6, 0xB84D,\n\t0x7AB7, 0xB84E, 0x7AB8, 0xB84F, 0x7AB9, 0xB850, 0x7ABA, 0xB851,\n\t0x7ABB, 0xB852, 0x7ABC, 0xB853, 0x7ABD, 0xB854, 0x7ABE, 0xB855,\n\t0x7ABF, 0xC1FE, 0x7AC0, 0xB856, 0x7AC1, 0xB857, 0x7AC2, 0xB858,\n\t0x7AC3, 0xB859, 0x7AC4, 0xB85A, 0x7AC5, 0xB85B, 0x7AC6, 0xB85C,\n\t0x7AC7, 0xB85D, 0x7AC8, 0xB85E, 0x7AC9, 0xB85F, 0x7ACA, 0xB860,\n\t0x7ACB, 0xC1A2, 0x7ACC, 0xB861, 0x7ACD, 0xB862, 0x7ACE, 0xB863,\n\t0x7ACF, 0xB864, 0x7AD0, 0xB865, 0x7AD1, 0xB866, 0x7AD2, 0xB867,\n\t0x7AD3, 0xB868, 0x7AD4, 0xB869, 0x7AD5, 0xB86A, 0x7AD6, 0xCAFA,\n\t0x7AD7, 0xB86B, 0x7AD8, 0xB86C, 0x7AD9, 0xD5BE, 0x7ADA, 0xB86D,\n\t0x7ADB, 0xB86E, 0x7ADC, 0xB86F, 0x7ADD, 0xB870, 0x7ADE, 0xBEBA,\n\t0x7ADF, 0xBEB9, 0x7AE0, 0xD5C2, 0x7AE1, 0xB871, 0x7AE2, 0xB872,\n\t0x7AE3, 0xBFA2, 0x7AE4, 0xB873, 0x7AE5, 0xCDAF, 0x7AE6, 0xF1B5,\n\t0x7AE7, 0xB874, 0x7AE8, 0xB875, 0x7AE9, 0xB876, 0x7AEA, 0xB877,\n\t0x7AEB, 0xB878, 0x7AEC, 0xB879, 0x7AED, 0xBDDF, 0x7AEE, 0xB87A,\n\t0x7AEF, 0xB6CB, 0x7AF0, 0xB87B, 0x7AF1, 0xB87C, 0x7AF2, 0xB87D,\n\t0x7AF3, 0xB87E, 0x7AF4, 0xB880, 0x7AF5, 0xB881, 0x7AF6, 0xB882,\n\t0x7AF7, 0xB883, 0x7AF8, 0xB884, 0x7AF9, 0xD6F1, 0x7AFA, 0xF3C3,\n\t0x7AFB, 0xB885, 0x7AFC, 0xB886, 0x7AFD, 0xF3C4, 0x7AFE, 0xB887,\n\t0x7AFF, 0xB8CD, 0x7B00, 0xB888, 0x7B01, 0xB889, 0x7B02, 0xB88A,\n\t0x7B03, 0xF3C6, 0x7B04, 0xF3C7, 0x7B05, 0xB88B, 0x7B06, 0xB0CA,\n\t0x7B07, 0xB88C, 0x7B08, 0xF3C5, 0x7B09, 0xB88D, 0x7B0A, 0xF3C9,\n\t0x7B0B, 0xCBF1, 0x7B0C, 0xB88E, 0x7B0D, 0xB88F, 0x7B0E, 0xB890,\n\t0x7B0F, 0xF3CB, 0x7B10, 0xB891, 0x7B11, 0xD0A6, 0x7B12, 0xB892,\n\t0x7B13, 0xB893, 0x7B14, 0xB1CA, 0x7B15, 0xF3C8, 0x7B16, 0xB894,\n\t0x7B17, 0xB895, 0x7B18, 0xB896, 0x7B19, 0xF3CF, 0x7B1A, 0xB897,\n\t0x7B1B, 0xB5D1, 0x7B1C, 0xB898, 0x7B1D, 0xB899, 0x7B1E, 0xF3D7,\n\t0x7B1F, 0xB89A, 0x7B20, 0xF3D2, 0x7B21, 0xB89B, 0x7B22, 0xB89C,\n\t0x7B23, 0xB89D, 0x7B24, 0xF3D4, 0x7B25, 0xF3D3, 0x7B26, 0xB7FB,\n\t0x7B27, 0xB89E, 0x7B28, 0xB1BF, 0x7B29, 0xB89F, 0x7B2A, 0xF3CE,\n\t0x7B2B, 0xF3CA, 0x7B2C, 0xB5DA, 0x7B2D, 0xB8A0, 0x7B2E, 0xF3D0,\n\t0x7B2F, 0xB940, 0x7B30, 0xB941, 0x7B31, 0xF3D1, 0x7B32, 0xB942,\n\t0x7B33, 0xF3D5, 0x7B34, 0xB943, 0x7B35, 0xB944, 0x7B36, 0xB945,\n\t0x7B37, 0xB946, 0x7B38, 0xF3CD, 0x7B39, 0xB947, 0x7B3A, 0xBCE3,\n\t0x7B3B, 0xB948, 0x7B3C, 0xC1FD, 0x7B3D, 0xB949, 0x7B3E, 0xF3D6,\n\t0x7B3F, 0xB94A, 0x7B40, 0xB94B, 0x7B41, 0xB94C, 0x7B42, 0xB94D,\n\t0x7B43, 0xB94E, 0x7B44, 0xB94F, 0x7B45, 0xF3DA, 0x7B46, 0xB950,\n\t0x7B47, 0xF3CC, 0x7B48, 0xB951, 0x7B49, 0xB5C8, 0x7B4A, 0xB952,\n\t0x7B4B, 0xBDEE, 0x7B4C, 0xF3DC, 0x7B4D, 0xB953, 0x7B4E, 0xB954,\n\t0x7B4F, 0xB7A4, 0x7B50, 0xBFF0, 0x7B51, 0xD6FE, 0x7B52, 0xCDB2,\n\t0x7B53, 0xB955, 0x7B54, 0xB4F0, 0x7B55, 0xB956, 0x7B56, 0xB2DF,\n\t0x7B57, 0xB957, 0x7B58, 0xF3D8, 0x7B59, 0xB958, 0x7B5A, 0xF3D9,\n\t0x7B5B, 0xC9B8, 0x7B5C, 0xB959, 0x7B5D, 0xF3DD, 0x7B5E, 0xB95A,\n\t0x7B5F, 0xB95B, 0x7B60, 0xF3DE, 0x7B61, 0xB95C, 0x7B62, 0xF3E1,\n\t0x7B63, 0xB95D, 0x7B64, 0xB95E, 0x7B65, 0xB95F, 0x7B66, 0xB960,\n\t0x7B67, 0xB961, 0x7B68, 0xB962, 0x7B69, 0xB963, 0x7B6A, 0xB964,\n\t0x7B6B, 0xB965, 0x7B6C, 0xB966, 0x7B6D, 0xB967, 0x7B6E, 0xF3DF,\n\t0x7B6F, 0xB968, 0x7B70, 0xB969, 0x7B71, 0xF3E3, 0x7B72, 0xF3E2,\n\t0x7B73, 0xB96A, 0x7B74, 0xB96B, 0x7B75, 0xF3DB, 0x7B76, 0xB96C,\n\t0x7B77, 0xBFEA, 0x7B78, 0xB96D, 0x7B79, 0xB3EF, 0x7B7A, 0xB96E,\n\t0x7B7B, 0xF3E0, 0x7B7C, 0xB96F, 0x7B7D, 0xB970, 0x7B7E, 0xC7A9,\n\t0x7B7F, 0xB971, 0x7B80, 0xBCF2, 0x7B81, 0xB972, 0x7B82, 0xB973,\n\t0x7B83, 0xB974, 0x7B84, 0xB975, 0x7B85, 0xF3EB, 0x7B86, 0xB976,\n\t0x7B87, 0xB977, 0x7B88, 0xB978, 0x7B89, 0xB979, 0x7B8A, 0xB97A,\n\t0x7B8B, 0xB97B, 0x7B8C, 0xB97C, 0x7B8D, 0xB9BF, 0x7B8E, 0xB97D,\n\t0x7B8F, 0xB97E, 0x7B90, 0xF3E4, 0x7B91, 0xB980, 0x7B92, 0xB981,\n\t0x7B93, 0xB982, 0x7B94, 0xB2AD, 0x7B95, 0xBBFE, 0x7B96, 0xB983,\n\t0x7B97, 0xCBE3, 0x7B98, 0xB984, 0x7B99, 0xB985, 0x7B9A, 0xB986,\n\t0x7B9B, 0xB987, 0x7B9C, 0xF3ED, 0x7B9D, 0xF3E9, 0x7B9E, 0xB988,\n\t0x7B9F, 0xB989, 0x7BA0, 0xB98A, 0x7BA1, 0xB9DC, 0x7BA2, 0xF3EE,\n\t0x7BA3, 0xB98B, 0x7BA4, 0xB98C, 0x7BA5, 0xB98D, 0x7BA6, 0xF3E5,\n\t0x7BA7, 0xF3E6, 0x7BA8, 0xF3EA, 0x7BA9, 0xC2E1, 0x7BAA, 0xF3EC,\n\t0x7BAB, 0xF3EF, 0x7BAC, 0xF3E8, 0x7BAD, 0xBCFD, 0x7BAE, 0xB98E,\n\t0x7BAF, 0xB98F, 0x7BB0, 0xB990, 0x7BB1, 0xCFE4, 0x7BB2, 0xB991,\n\t0x7BB3, 0xB992, 0x7BB4, 0xF3F0, 0x7BB5, 0xB993, 0x7BB6, 0xB994,\n\t0x7BB7, 0xB995, 0x7BB8, 0xF3E7, 0x7BB9, 0xB996, 0x7BBA, 0xB997,\n\t0x7BBB, 0xB998, 0x7BBC, 0xB999, 0x7BBD, 0xB99A, 0x7BBE, 0xB99B,\n\t0x7BBF, 0xB99C, 0x7BC0, 0xB99D, 0x7BC1, 0xF3F2, 0x7BC2, 0xB99E,\n\t0x7BC3, 0xB99F, 0x7BC4, 0xB9A0, 0x7BC5, 0xBA40, 0x7BC6, 0xD7AD,\n\t0x7BC7, 0xC6AA, 0x7BC8, 0xBA41, 0x7BC9, 0xBA42, 0x7BCA, 0xBA43,\n\t0x7BCB, 0xBA44, 0x7BCC, 0xF3F3, 0x7BCD, 0xBA45, 0x7BCE, 0xBA46,\n\t0x7BCF, 0xBA47, 0x7BD0, 0xBA48, 0x7BD1, 0xF3F1, 0x7BD2, 0xBA49,\n\t0x7BD3, 0xC2A8, 0x7BD4, 0xBA4A, 0x7BD5, 0xBA4B, 0x7BD6, 0xBA4C,\n\t0x7BD7, 0xBA4D, 0x7BD8, 0xBA4E, 0x7BD9, 0xB8DD, 0x7BDA, 0xF3F5,\n\t0x7BDB, 0xBA4F, 0x7BDC, 0xBA50, 0x7BDD, 0xF3F4, 0x7BDE, 0xBA51,\n\t0x7BDF, 0xBA52, 0x7BE0, 0xBA53, 0x7BE1, 0xB4DB, 0x7BE2, 0xBA54,\n\t0x7BE3, 0xBA55, 0x7BE4, 0xBA56, 0x7BE5, 0xF3F6, 0x7BE6, 0xF3F7,\n\t0x7BE7, 0xBA57, 0x7BE8, 0xBA58, 0x7BE9, 0xBA59, 0x7BEA, 0xF3F8,\n\t0x7BEB, 0xBA5A, 0x7BEC, 0xBA5B, 0x7BED, 0xBA5C, 0x7BEE, 0xC0BA,\n\t0x7BEF, 0xBA5D, 0x7BF0, 0xBA5E, 0x7BF1, 0xC0E9, 0x7BF2, 0xBA5F,\n\t0x7BF3, 0xBA60, 0x7BF4, 0xBA61, 0x7BF5, 0xBA62, 0x7BF6, 0xBA63,\n\t0x7BF7, 0xC5F1, 0x7BF8, 0xBA64, 0x7BF9, 0xBA65, 0x7BFA, 0xBA66,\n\t0x7BFB, 0xBA67, 0x7BFC, 0xF3FB, 0x7BFD, 0xBA68, 0x7BFE, 0xF3FA,\n\t0x7BFF, 0xBA69, 0x7C00, 0xBA6A, 0x7C01, 0xBA6B, 0x7C02, 0xBA6C,\n\t0x7C03, 0xBA6D, 0x7C04, 0xBA6E, 0x7C05, 0xBA6F, 0x7C06, 0xBA70,\n\t0x7C07, 0xB4D8, 0x7C08, 0xBA71, 0x7C09, 0xBA72, 0x7C0A, 0xBA73,\n\t0x7C0B, 0xF3FE, 0x7C0C, 0xF3F9, 0x7C0D, 0xBA74, 0x7C0E, 0xBA75,\n\t0x7C0F, 0xF3FC, 0x7C10, 0xBA76, 0x7C11, 0xBA77, 0x7C12, 0xBA78,\n\t0x7C13, 0xBA79, 0x7C14, 0xBA7A, 0x7C15, 0xBA7B, 0x7C16, 0xF3FD,\n\t0x7C17, 0xBA7C, 0x7C18, 0xBA7D, 0x7C19, 0xBA7E, 0x7C1A, 0xBA80,\n\t0x7C1B, 0xBA81, 0x7C1C, 0xBA82, 0x7C1D, 0xBA83, 0x7C1E, 0xBA84,\n\t0x7C1F, 0xF4A1, 0x7C20, 0xBA85, 0x7C21, 0xBA86, 0x7C22, 0xBA87,\n\t0x7C23, 0xBA88, 0x7C24, 0xBA89, 0x7C25, 0xBA8A, 0x7C26, 0xF4A3,\n\t0x7C27, 0xBBC9, 0x7C28, 0xBA8B, 0x7C29, 0xBA8C, 0x7C2A, 0xF4A2,\n\t0x7C2B, 0xBA8D, 0x7C2C, 0xBA8E, 0x7C2D, 0xBA8F, 0x7C2E, 0xBA90,\n\t0x7C2F, 0xBA91, 0x7C30, 0xBA92, 0x7C31, 0xBA93, 0x7C32, 0xBA94,\n\t0x7C33, 0xBA95, 0x7C34, 0xBA96, 0x7C35, 0xBA97, 0x7C36, 0xBA98,\n\t0x7C37, 0xBA99, 0x7C38, 0xF4A4, 0x7C39, 0xBA9A, 0x7C3A, 0xBA9B,\n\t0x7C3B, 0xBA9C, 0x7C3C, 0xBA9D, 0x7C3D, 0xBA9E, 0x7C3E, 0xBA9F,\n\t0x7C3F, 0xB2BE, 0x7C40, 0xF4A6, 0x7C41, 0xF4A5, 0x7C42, 0xBAA0,\n\t0x7C43, 0xBB40, 0x7C44, 0xBB41, 0x7C45, 0xBB42, 0x7C46, 0xBB43,\n\t0x7C47, 0xBB44, 0x7C48, 0xBB45, 0x7C49, 0xBB46, 0x7C4A, 0xBB47,\n\t0x7C4B, 0xBB48, 0x7C4C, 0xBB49, 0x7C4D, 0xBCAE, 0x7C4E, 0xBB4A,\n\t0x7C4F, 0xBB4B, 0x7C50, 0xBB4C, 0x7C51, 0xBB4D, 0x7C52, 0xBB4E,\n\t0x7C53, 0xBB4F, 0x7C54, 0xBB50, 0x7C55, 0xBB51, 0x7C56, 0xBB52,\n\t0x7C57, 0xBB53, 0x7C58, 0xBB54, 0x7C59, 0xBB55, 0x7C5A, 0xBB56,\n\t0x7C5B, 0xBB57, 0x7C5C, 0xBB58, 0x7C5D, 0xBB59, 0x7C5E, 0xBB5A,\n\t0x7C5F, 0xBB5B, 0x7C60, 0xBB5C, 0x7C61, 0xBB5D, 0x7C62, 0xBB5E,\n\t0x7C63, 0xBB5F, 0x7C64, 0xBB60, 0x7C65, 0xBB61, 0x7C66, 0xBB62,\n\t0x7C67, 0xBB63, 0x7C68, 0xBB64, 0x7C69, 0xBB65, 0x7C6A, 0xBB66,\n\t0x7C6B, 0xBB67, 0x7C6C, 0xBB68, 0x7C6D, 0xBB69, 0x7C6E, 0xBB6A,\n\t0x7C6F, 0xBB6B, 0x7C70, 0xBB6C, 0x7C71, 0xBB6D, 0x7C72, 0xBB6E,\n\t0x7C73, 0xC3D7, 0x7C74, 0xD9E1, 0x7C75, 0xBB6F, 0x7C76, 0xBB70,\n\t0x7C77, 0xBB71, 0x7C78, 0xBB72, 0x7C79, 0xBB73, 0x7C7A, 0xBB74,\n\t0x7C7B, 0xC0E0, 0x7C7C, 0xF4CC, 0x7C7D, 0xD7D1, 0x7C7E, 0xBB75,\n\t0x7C7F, 0xBB76, 0x7C80, 0xBB77, 0x7C81, 0xBB78, 0x7C82, 0xBB79,\n\t0x7C83, 0xBB7A, 0x7C84, 0xBB7B, 0x7C85, 0xBB7C, 0x7C86, 0xBB7D,\n\t0x7C87, 0xBB7E, 0x7C88, 0xBB80, 0x7C89, 0xB7DB, 0x7C8A, 0xBB81,\n\t0x7C8B, 0xBB82, 0x7C8C, 0xBB83, 0x7C8D, 0xBB84, 0x7C8E, 0xBB85,\n\t0x7C8F, 0xBB86, 0x7C90, 0xBB87, 0x7C91, 0xF4CE, 0x7C92, 0xC1A3,\n\t0x7C93, 0xBB88, 0x7C94, 0xBB89, 0x7C95, 0xC6C9, 0x7C96, 0xBB8A,\n\t0x7C97, 0xB4D6, 0x7C98, 0xD5B3, 0x7C99, 0xBB8B, 0x7C9A, 0xBB8C,\n\t0x7C9B, 0xBB8D, 0x7C9C, 0xF4D0, 0x7C9D, 0xF4CF, 0x7C9E, 0xF4D1,\n\t0x7C9F, 0xCBDA, 0x7CA0, 0xBB8E, 0x7CA1, 0xBB8F, 0x7CA2, 0xF4D2,\n\t0x7CA3, 0xBB90, 0x7CA4, 0xD4C1, 0x7CA5, 0xD6E0, 0x7CA6, 0xBB91,\n\t0x7CA7, 0xBB92, 0x7CA8, 0xBB93, 0x7CA9, 0xBB94, 0x7CAA, 0xB7E0,\n\t0x7CAB, 0xBB95, 0x7CAC, 0xBB96, 0x7CAD, 0xBB97, 0x7CAE, 0xC1B8,\n\t0x7CAF, 0xBB98, 0x7CB0, 0xBB99, 0x7CB1, 0xC1BB, 0x7CB2, 0xF4D3,\n\t0x7CB3, 0xBEAC, 0x7CB4, 0xBB9A, 0x7CB5, 0xBB9B, 0x7CB6, 0xBB9C,\n\t0x7CB7, 0xBB9D, 0x7CB8, 0xBB9E, 0x7CB9, 0xB4E2, 0x7CBA, 0xBB9F,\n\t0x7CBB, 0xBBA0, 0x7CBC, 0xF4D4, 0x7CBD, 0xF4D5, 0x7CBE, 0xBEAB,\n\t0x7CBF, 0xBC40, 0x7CC0, 0xBC41, 0x7CC1, 0xF4D6, 0x7CC2, 0xBC42,\n\t0x7CC3, 0xBC43, 0x7CC4, 0xBC44, 0x7CC5, 0xF4DB, 0x7CC6, 0xBC45,\n\t0x7CC7, 0xF4D7, 0x7CC8, 0xF4DA, 0x7CC9, 0xBC46, 0x7CCA, 0xBAFD,\n\t0x7CCB, 0xBC47, 0x7CCC, 0xF4D8, 0x7CCD, 0xF4D9, 0x7CCE, 0xBC48,\n\t0x7CCF, 0xBC49, 0x7CD0, 0xBC4A, 0x7CD1, 0xBC4B, 0x7CD2, 0xBC4C,\n\t0x7CD3, 0xBC4D, 0x7CD4, 0xBC4E, 0x7CD5, 0xB8E2, 0x7CD6, 0xCCC7,\n\t0x7CD7, 0xF4DC, 0x7CD8, 0xBC4F, 0x7CD9, 0xB2DA, 0x7CDA, 0xBC50,\n\t0x7CDB, 0xBC51, 0x7CDC, 0xC3D3, 0x7CDD, 0xBC52, 0x7CDE, 0xBC53,\n\t0x7CDF, 0xD4E3, 0x7CE0, 0xBFB7, 0x7CE1, 0xBC54, 0x7CE2, 0xBC55,\n\t0x7CE3, 0xBC56, 0x7CE4, 0xBC57, 0x7CE5, 0xBC58, 0x7CE6, 0xBC59,\n\t0x7CE7, 0xBC5A, 0x7CE8, 0xF4DD, 0x7CE9, 0xBC5B, 0x7CEA, 0xBC5C,\n\t0x7CEB, 0xBC5D, 0x7CEC, 0xBC5E, 0x7CED, 0xBC5F, 0x7CEE, 0xBC60,\n\t0x7CEF, 0xC5B4, 0x7CF0, 0xBC61, 0x7CF1, 0xBC62, 0x7CF2, 0xBC63,\n\t0x7CF3, 0xBC64, 0x7CF4, 0xBC65, 0x7CF5, 0xBC66, 0x7CF6, 0xBC67,\n\t0x7CF7, 0xBC68, 0x7CF8, 0xF4E9, 0x7CF9, 0xBC69, 0x7CFA, 0xBC6A,\n\t0x7CFB, 0xCFB5, 0x7CFC, 0xBC6B, 0x7CFD, 0xBC6C, 0x7CFE, 0xBC6D,\n\t0x7CFF, 0xBC6E, 0x7D00, 0xBC6F, 0x7D01, 0xBC70, 0x7D02, 0xBC71,\n\t0x7D03, 0xBC72, 0x7D04, 0xBC73, 0x7D05, 0xBC74, 0x7D06, 0xBC75,\n\t0x7D07, 0xBC76, 0x7D08, 0xBC77, 0x7D09, 0xBC78, 0x7D0A, 0xCEC9,\n\t0x7D0B, 0xBC79, 0x7D0C, 0xBC7A, 0x7D0D, 0xBC7B, 0x7D0E, 0xBC7C,\n\t0x7D0F, 0xBC7D, 0x7D10, 0xBC7E, 0x7D11, 0xBC80, 0x7D12, 0xBC81,\n\t0x7D13, 0xBC82, 0x7D14, 0xBC83, 0x7D15, 0xBC84, 0x7D16, 0xBC85,\n\t0x7D17, 0xBC86, 0x7D18, 0xBC87, 0x7D19, 0xBC88, 0x7D1A, 0xBC89,\n\t0x7D1B, 0xBC8A, 0x7D1C, 0xBC8B, 0x7D1D, 0xBC8C, 0x7D1E, 0xBC8D,\n\t0x7D1F, 0xBC8E, 0x7D20, 0xCBD8, 0x7D21, 0xBC8F, 0x7D22, 0xCBF7,\n\t0x7D23, 0xBC90, 0x7D24, 0xBC91, 0x7D25, 0xBC92, 0x7D26, 0xBC93,\n\t0x7D27, 0xBDF4, 0x7D28, 0xBC94, 0x7D29, 0xBC95, 0x7D2A, 0xBC96,\n\t0x7D2B, 0xD7CF, 0x7D2C, 0xBC97, 0x7D2D, 0xBC98, 0x7D2E, 0xBC99,\n\t0x7D2F, 0xC0DB, 0x7D30, 0xBC9A, 0x7D31, 0xBC9B, 0x7D32, 0xBC9C,\n\t0x7D33, 0xBC9D, 0x7D34, 0xBC9E, 0x7D35, 0xBC9F, 0x7D36, 0xBCA0,\n\t0x7D37, 0xBD40, 0x7D38, 0xBD41, 0x7D39, 0xBD42, 0x7D3A, 0xBD43,\n\t0x7D3B, 0xBD44, 0x7D3C, 0xBD45, 0x7D3D, 0xBD46, 0x7D3E, 0xBD47,\n\t0x7D3F, 0xBD48, 0x7D40, 0xBD49, 0x7D41, 0xBD4A, 0x7D42, 0xBD4B,\n\t0x7D43, 0xBD4C, 0x7D44, 0xBD4D, 0x7D45, 0xBD4E, 0x7D46, 0xBD4F,\n\t0x7D47, 0xBD50, 0x7D48, 0xBD51, 0x7D49, 0xBD52, 0x7D4A, 0xBD53,\n\t0x7D4B, 0xBD54, 0x7D4C, 0xBD55, 0x7D4D, 0xBD56, 0x7D4E, 0xBD57,\n\t0x7D4F, 0xBD58, 0x7D50, 0xBD59, 0x7D51, 0xBD5A, 0x7D52, 0xBD5B,\n\t0x7D53, 0xBD5C, 0x7D54, 0xBD5D, 0x7D55, 0xBD5E, 0x7D56, 0xBD5F,\n\t0x7D57, 0xBD60, 0x7D58, 0xBD61, 0x7D59, 0xBD62, 0x7D5A, 0xBD63,\n\t0x7D5B, 0xBD64, 0x7D5C, 0xBD65, 0x7D5D, 0xBD66, 0x7D5E, 0xBD67,\n\t0x7D5F, 0xBD68, 0x7D60, 0xBD69, 0x7D61, 0xBD6A, 0x7D62, 0xBD6B,\n\t0x7D63, 0xBD6C, 0x7D64, 0xBD6D, 0x7D65, 0xBD6E, 0x7D66, 0xBD6F,\n\t0x7D67, 0xBD70, 0x7D68, 0xBD71, 0x7D69, 0xBD72, 0x7D6A, 0xBD73,\n\t0x7D6B, 0xBD74, 0x7D6C, 0xBD75, 0x7D6D, 0xBD76, 0x7D6E, 0xD0F5,\n\t0x7D6F, 0xBD77, 0x7D70, 0xBD78, 0x7D71, 0xBD79, 0x7D72, 0xBD7A,\n\t0x7D73, 0xBD7B, 0x7D74, 0xBD7C, 0x7D75, 0xBD7D, 0x7D76, 0xBD7E,\n\t0x7D77, 0xF4EA, 0x7D78, 0xBD80, 0x7D79, 0xBD81, 0x7D7A, 0xBD82,\n\t0x7D7B, 0xBD83, 0x7D7C, 0xBD84, 0x7D7D, 0xBD85, 0x7D7E, 0xBD86,\n\t0x7D7F, 0xBD87, 0x7D80, 0xBD88, 0x7D81, 0xBD89, 0x7D82, 0xBD8A,\n\t0x7D83, 0xBD8B, 0x7D84, 0xBD8C, 0x7D85, 0xBD8D, 0x7D86, 0xBD8E,\n\t0x7D87, 0xBD8F, 0x7D88, 0xBD90, 0x7D89, 0xBD91, 0x7D8A, 0xBD92,\n\t0x7D8B, 0xBD93, 0x7D8C, 0xBD94, 0x7D8D, 0xBD95, 0x7D8E, 0xBD96,\n\t0x7D8F, 0xBD97, 0x7D90, 0xBD98, 0x7D91, 0xBD99, 0x7D92, 0xBD9A,\n\t0x7D93, 0xBD9B, 0x7D94, 0xBD9C, 0x7D95, 0xBD9D, 0x7D96, 0xBD9E,\n\t0x7D97, 0xBD9F, 0x7D98, 0xBDA0, 0x7D99, 0xBE40, 0x7D9A, 0xBE41,\n\t0x7D9B, 0xBE42, 0x7D9C, 0xBE43, 0x7D9D, 0xBE44, 0x7D9E, 0xBE45,\n\t0x7D9F, 0xBE46, 0x7DA0, 0xBE47, 0x7DA1, 0xBE48, 0x7DA2, 0xBE49,\n\t0x7DA3, 0xBE4A, 0x7DA4, 0xBE4B, 0x7DA5, 0xBE4C, 0x7DA6, 0xF4EB,\n\t0x7DA7, 0xBE4D, 0x7DA8, 0xBE4E, 0x7DA9, 0xBE4F, 0x7DAA, 0xBE50,\n\t0x7DAB, 0xBE51, 0x7DAC, 0xBE52, 0x7DAD, 0xBE53, 0x7DAE, 0xF4EC,\n\t0x7DAF, 0xBE54, 0x7DB0, 0xBE55, 0x7DB1, 0xBE56, 0x7DB2, 0xBE57,\n\t0x7DB3, 0xBE58, 0x7DB4, 0xBE59, 0x7DB5, 0xBE5A, 0x7DB6, 0xBE5B,\n\t0x7DB7, 0xBE5C, 0x7DB8, 0xBE5D, 0x7DB9, 0xBE5E, 0x7DBA, 0xBE5F,\n\t0x7DBB, 0xBE60, 0x7DBC, 0xBE61, 0x7DBD, 0xBE62, 0x7DBE, 0xBE63,\n\t0x7DBF, 0xBE64, 0x7DC0, 0xBE65, 0x7DC1, 0xBE66, 0x7DC2, 0xBE67,\n\t0x7DC3, 0xBE68, 0x7DC4, 0xBE69, 0x7DC5, 0xBE6A, 0x7DC6, 0xBE6B,\n\t0x7DC7, 0xBE6C, 0x7DC8, 0xBE6D, 0x7DC9, 0xBE6E, 0x7DCA, 0xBE6F,\n\t0x7DCB, 0xBE70, 0x7DCC, 0xBE71, 0x7DCD, 0xBE72, 0x7DCE, 0xBE73,\n\t0x7DCF, 0xBE74, 0x7DD0, 0xBE75, 0x7DD1, 0xBE76, 0x7DD2, 0xBE77,\n\t0x7DD3, 0xBE78, 0x7DD4, 0xBE79, 0x7DD5, 0xBE7A, 0x7DD6, 0xBE7B,\n\t0x7DD7, 0xBE7C, 0x7DD8, 0xBE7D, 0x7DD9, 0xBE7E, 0x7DDA, 0xBE80,\n\t0x7DDB, 0xBE81, 0x7DDC, 0xBE82, 0x7DDD, 0xBE83, 0x7DDE, 0xBE84,\n\t0x7DDF, 0xBE85, 0x7DE0, 0xBE86, 0x7DE1, 0xBE87, 0x7DE2, 0xBE88,\n\t0x7DE3, 0xBE89, 0x7DE4, 0xBE8A, 0x7DE5, 0xBE8B, 0x7DE6, 0xBE8C,\n\t0x7DE7, 0xBE8D, 0x7DE8, 0xBE8E, 0x7DE9, 0xBE8F, 0x7DEA, 0xBE90,\n\t0x7DEB, 0xBE91, 0x7DEC, 0xBE92, 0x7DED, 0xBE93, 0x7DEE, 0xBE94,\n\t0x7DEF, 0xBE95, 0x7DF0, 0xBE96, 0x7DF1, 0xBE97, 0x7DF2, 0xBE98,\n\t0x7DF3, 0xBE99, 0x7DF4, 0xBE9A, 0x7DF5, 0xBE9B, 0x7DF6, 0xBE9C,\n\t0x7DF7, 0xBE9D, 0x7DF8, 0xBE9E, 0x7DF9, 0xBE9F, 0x7DFA, 0xBEA0,\n\t0x7DFB, 0xBF40, 0x7DFC, 0xBF41, 0x7DFD, 0xBF42, 0x7DFE, 0xBF43,\n\t0x7DFF, 0xBF44, 0x7E00, 0xBF45, 0x7E01, 0xBF46, 0x7E02, 0xBF47,\n\t0x7E03, 0xBF48, 0x7E04, 0xBF49, 0x7E05, 0xBF4A, 0x7E06, 0xBF4B,\n\t0x7E07, 0xBF4C, 0x7E08, 0xBF4D, 0x7E09, 0xBF4E, 0x7E0A, 0xBF4F,\n\t0x7E0B, 0xBF50, 0x7E0C, 0xBF51, 0x7E0D, 0xBF52, 0x7E0E, 0xBF53,\n\t0x7E0F, 0xBF54, 0x7E10, 0xBF55, 0x7E11, 0xBF56, 0x7E12, 0xBF57,\n\t0x7E13, 0xBF58, 0x7E14, 0xBF59, 0x7E15, 0xBF5A, 0x7E16, 0xBF5B,\n\t0x7E17, 0xBF5C, 0x7E18, 0xBF5D, 0x7E19, 0xBF5E, 0x7E1A, 0xBF5F,\n\t0x7E1B, 0xBF60, 0x7E1C, 0xBF61, 0x7E1D, 0xBF62, 0x7E1E, 0xBF63,\n\t0x7E1F, 0xBF64, 0x7E20, 0xBF65, 0x7E21, 0xBF66, 0x7E22, 0xBF67,\n\t0x7E23, 0xBF68, 0x7E24, 0xBF69, 0x7E25, 0xBF6A, 0x7E26, 0xBF6B,\n\t0x7E27, 0xBF6C, 0x7E28, 0xBF6D, 0x7E29, 0xBF6E, 0x7E2A, 0xBF6F,\n\t0x7E2B, 0xBF70, 0x7E2C, 0xBF71, 0x7E2D, 0xBF72, 0x7E2E, 0xBF73,\n\t0x7E2F, 0xBF74, 0x7E30, 0xBF75, 0x7E31, 0xBF76, 0x7E32, 0xBF77,\n\t0x7E33, 0xBF78, 0x7E34, 0xBF79, 0x7E35, 0xBF7A, 0x7E36, 0xBF7B,\n\t0x7E37, 0xBF7C, 0x7E38, 0xBF7D, 0x7E39, 0xBF7E, 0x7E3A, 0xBF80,\n\t0x7E3B, 0xF7E3, 0x7E3C, 0xBF81, 0x7E3D, 0xBF82, 0x7E3E, 0xBF83,\n\t0x7E3F, 0xBF84, 0x7E40, 0xBF85, 0x7E41, 0xB7B1, 0x7E42, 0xBF86,\n\t0x7E43, 0xBF87, 0x7E44, 0xBF88, 0x7E45, 0xBF89, 0x7E46, 0xBF8A,\n\t0x7E47, 0xF4ED, 0x7E48, 0xBF8B, 0x7E49, 0xBF8C, 0x7E4A, 0xBF8D,\n\t0x7E4B, 0xBF8E, 0x7E4C, 0xBF8F, 0x7E4D, 0xBF90, 0x7E4E, 0xBF91,\n\t0x7E4F, 0xBF92, 0x7E50, 0xBF93, 0x7E51, 0xBF94, 0x7E52, 0xBF95,\n\t0x7E53, 0xBF96, 0x7E54, 0xBF97, 0x7E55, 0xBF98, 0x7E56, 0xBF99,\n\t0x7E57, 0xBF9A, 0x7E58, 0xBF9B, 0x7E59, 0xBF9C, 0x7E5A, 0xBF9D,\n\t0x7E5B, 0xBF9E, 0x7E5C, 0xBF9F, 0x7E5D, 0xBFA0, 0x7E5E, 0xC040,\n\t0x7E5F, 0xC041, 0x7E60, 0xC042, 0x7E61, 0xC043, 0x7E62, 0xC044,\n\t0x7E63, 0xC045, 0x7E64, 0xC046, 0x7E65, 0xC047, 0x7E66, 0xC048,\n\t0x7E67, 0xC049, 0x7E68, 0xC04A, 0x7E69, 0xC04B, 0x7E6A, 0xC04C,\n\t0x7E6B, 0xC04D, 0x7E6C, 0xC04E, 0x7E6D, 0xC04F, 0x7E6E, 0xC050,\n\t0x7E6F, 0xC051, 0x7E70, 0xC052, 0x7E71, 0xC053, 0x7E72, 0xC054,\n\t0x7E73, 0xC055, 0x7E74, 0xC056, 0x7E75, 0xC057, 0x7E76, 0xC058,\n\t0x7E77, 0xC059, 0x7E78, 0xC05A, 0x7E79, 0xC05B, 0x7E7A, 0xC05C,\n\t0x7E7B, 0xC05D, 0x7E7C, 0xC05E, 0x7E7D, 0xC05F, 0x7E7E, 0xC060,\n\t0x7E7F, 0xC061, 0x7E80, 0xC062, 0x7E81, 0xC063, 0x7E82, 0xD7EB,\n\t0x7E83, 0xC064, 0x7E84, 0xC065, 0x7E85, 0xC066, 0x7E86, 0xC067,\n\t0x7E87, 0xC068, 0x7E88, 0xC069, 0x7E89, 0xC06A, 0x7E8A, 0xC06B,\n\t0x7E8B, 0xC06C, 0x7E8C, 0xC06D, 0x7E8D, 0xC06E, 0x7E8E, 0xC06F,\n\t0x7E8F, 0xC070, 0x7E90, 0xC071, 0x7E91, 0xC072, 0x7E92, 0xC073,\n\t0x7E93, 0xC074, 0x7E94, 0xC075, 0x7E95, 0xC076, 0x7E96, 0xC077,\n\t0x7E97, 0xC078, 0x7E98, 0xC079, 0x7E99, 0xC07A, 0x7E9A, 0xC07B,\n\t0x7E9B, 0xF4EE, 0x7E9C, 0xC07C, 0x7E9D, 0xC07D, 0x7E9E, 0xC07E,\n\t0x7E9F, 0xE6F9, 0x7EA0, 0xBEC0, 0x7EA1, 0xE6FA, 0x7EA2, 0xBAEC,\n\t0x7EA3, 0xE6FB, 0x7EA4, 0xCFCB, 0x7EA5, 0xE6FC, 0x7EA6, 0xD4BC,\n\t0x7EA7, 0xBCB6, 0x7EA8, 0xE6FD, 0x7EA9, 0xE6FE, 0x7EAA, 0xBCCD,\n\t0x7EAB, 0xC8D2, 0x7EAC, 0xCEB3, 0x7EAD, 0xE7A1, 0x7EAE, 0xC080,\n\t0x7EAF, 0xB4BF, 0x7EB0, 0xE7A2, 0x7EB1, 0xC9B4, 0x7EB2, 0xB8D9,\n\t0x7EB3, 0xC4C9, 0x7EB4, 0xC081, 0x7EB5, 0xD7DD, 0x7EB6, 0xC2DA,\n\t0x7EB7, 0xB7D7, 0x7EB8, 0xD6BD, 0x7EB9, 0xCEC6, 0x7EBA, 0xB7C4,\n\t0x7EBB, 0xC082, 0x7EBC, 0xC083, 0x7EBD, 0xC5A6, 0x7EBE, 0xE7A3,\n\t0x7EBF, 0xCFDF, 0x7EC0, 0xE7A4, 0x7EC1, 0xE7A5, 0x7EC2, 0xE7A6,\n\t0x7EC3, 0xC1B7, 0x7EC4, 0xD7E9, 0x7EC5, 0xC9F0, 0x7EC6, 0xCFB8,\n\t0x7EC7, 0xD6AF, 0x7EC8, 0xD6D5, 0x7EC9, 0xE7A7, 0x7ECA, 0xB0ED,\n\t0x7ECB, 0xE7A8, 0x7ECC, 0xE7A9, 0x7ECD, 0xC9DC, 0x7ECE, 0xD2EF,\n\t0x7ECF, 0xBEAD, 0x7ED0, 0xE7AA, 0x7ED1, 0xB0F3, 0x7ED2, 0xC8DE,\n\t0x7ED3, 0xBDE1, 0x7ED4, 0xE7AB, 0x7ED5, 0xC8C6, 0x7ED6, 0xC084,\n\t0x7ED7, 0xE7AC, 0x7ED8, 0xBBE6, 0x7ED9, 0xB8F8, 0x7EDA, 0xD1A4,\n\t0x7EDB, 0xE7AD, 0x7EDC, 0xC2E7, 0x7EDD, 0xBEF8, 0x7EDE, 0xBDCA,\n\t0x7EDF, 0xCDB3, 0x7EE0, 0xE7AE, 0x7EE1, 0xE7AF, 0x7EE2, 0xBEEE,\n\t0x7EE3, 0xD0E5, 0x7EE4, 0xC085, 0x7EE5, 0xCBE7, 0x7EE6, 0xCCD0,\n\t0x7EE7, 0xBCCC, 0x7EE8, 0xE7B0, 0x7EE9, 0xBCA8, 0x7EEA, 0xD0F7,\n\t0x7EEB, 0xE7B1, 0x7EEC, 0xC086, 0x7EED, 0xD0F8, 0x7EEE, 0xE7B2,\n\t0x7EEF, 0xE7B3, 0x7EF0, 0xB4C2, 0x7EF1, 0xE7B4, 0x7EF2, 0xE7B5,\n\t0x7EF3, 0xC9FE, 0x7EF4, 0xCEAC, 0x7EF5, 0xC3E0, 0x7EF6, 0xE7B7,\n\t0x7EF7, 0xB1C1, 0x7EF8, 0xB3F1, 0x7EF9, 0xC087, 0x7EFA, 0xE7B8,\n\t0x7EFB, 0xE7B9, 0x7EFC, 0xD7DB, 0x7EFD, 0xD5C0, 0x7EFE, 0xE7BA,\n\t0x7EFF, 0xC2CC, 0x7F00, 0xD7BA, 0x7F01, 0xE7BB, 0x7F02, 0xE7BC,\n\t0x7F03, 0xE7BD, 0x7F04, 0xBCEA, 0x7F05, 0xC3E5, 0x7F06, 0xC0C2,\n\t0x7F07, 0xE7BE, 0x7F08, 0xE7BF, 0x7F09, 0xBCA9, 0x7F0A, 0xC088,\n\t0x7F0B, 0xE7C0, 0x7F0C, 0xE7C1, 0x7F0D, 0xE7B6, 0x7F0E, 0xB6D0,\n\t0x7F0F, 0xE7C2, 0x7F10, 0xC089, 0x7F11, 0xE7C3, 0x7F12, 0xE7C4,\n\t0x7F13, 0xBBBA, 0x7F14, 0xB5DE, 0x7F15, 0xC2C6, 0x7F16, 0xB1E0,\n\t0x7F17, 0xE7C5, 0x7F18, 0xD4B5, 0x7F19, 0xE7C6, 0x7F1A, 0xB8BF,\n\t0x7F1B, 0xE7C8, 0x7F1C, 0xE7C7, 0x7F1D, 0xB7EC, 0x7F1E, 0xC08A,\n\t0x7F1F, 0xE7C9, 0x7F20, 0xB2F8, 0x7F21, 0xE7CA, 0x7F22, 0xE7CB,\n\t0x7F23, 0xE7CC, 0x7F24, 0xE7CD, 0x7F25, 0xE7CE, 0x7F26, 0xE7CF,\n\t0x7F27, 0xE7D0, 0x7F28, 0xD3A7, 0x7F29, 0xCBF5, 0x7F2A, 0xE7D1,\n\t0x7F2B, 0xE7D2, 0x7F2C, 0xE7D3, 0x7F2D, 0xE7D4, 0x7F2E, 0xC9C9,\n\t0x7F2F, 0xE7D5, 0x7F30, 0xE7D6, 0x7F31, 0xE7D7, 0x7F32, 0xE7D8,\n\t0x7F33, 0xE7D9, 0x7F34, 0xBDC9, 0x7F35, 0xE7DA, 0x7F36, 0xF3BE,\n\t0x7F37, 0xC08B, 0x7F38, 0xB8D7, 0x7F39, 0xC08C, 0x7F3A, 0xC8B1,\n\t0x7F3B, 0xC08D, 0x7F3C, 0xC08E, 0x7F3D, 0xC08F, 0x7F3E, 0xC090,\n\t0x7F3F, 0xC091, 0x7F40, 0xC092, 0x7F41, 0xC093, 0x7F42, 0xF3BF,\n\t0x7F43, 0xC094, 0x7F44, 0xF3C0, 0x7F45, 0xF3C1, 0x7F46, 0xC095,\n\t0x7F47, 0xC096, 0x7F48, 0xC097, 0x7F49, 0xC098, 0x7F4A, 0xC099,\n\t0x7F4B, 0xC09A, 0x7F4C, 0xC09B, 0x7F4D, 0xC09C, 0x7F4E, 0xC09D,\n\t0x7F4F, 0xC09E, 0x7F50, 0xB9DE, 0x7F51, 0xCDF8, 0x7F52, 0xC09F,\n\t0x7F53, 0xC0A0, 0x7F54, 0xD8E8, 0x7F55, 0xBAB1, 0x7F56, 0xC140,\n\t0x7F57, 0xC2DE, 0x7F58, 0xEEB7, 0x7F59, 0xC141, 0x7F5A, 0xB7A3,\n\t0x7F5B, 0xC142, 0x7F5C, 0xC143, 0x7F5D, 0xC144, 0x7F5E, 0xC145,\n\t0x7F5F, 0xEEB9, 0x7F60, 0xC146, 0x7F61, 0xEEB8, 0x7F62, 0xB0D5,\n\t0x7F63, 0xC147, 0x7F64, 0xC148, 0x7F65, 0xC149, 0x7F66, 0xC14A,\n\t0x7F67, 0xC14B, 0x7F68, 0xEEBB, 0x7F69, 0xD5D6, 0x7F6A, 0xD7EF,\n\t0x7F6B, 0xC14C, 0x7F6C, 0xC14D, 0x7F6D, 0xC14E, 0x7F6E, 0xD6C3,\n\t0x7F6F, 0xC14F, 0x7F70, 0xC150, 0x7F71, 0xEEBD, 0x7F72, 0xCAF0,\n\t0x7F73, 0xC151, 0x7F74, 0xEEBC, 0x7F75, 0xC152, 0x7F76, 0xC153,\n\t0x7F77, 0xC154, 0x7F78, 0xC155, 0x7F79, 0xEEBE, 0x7F7A, 0xC156,\n\t0x7F7B, 0xC157, 0x7F7C, 0xC158, 0x7F7D, 0xC159, 0x7F7E, 0xEEC0,\n\t0x7F7F, 0xC15A, 0x7F80, 0xC15B, 0x7F81, 0xEEBF, 0x7F82, 0xC15C,\n\t0x7F83, 0xC15D, 0x7F84, 0xC15E, 0x7F85, 0xC15F, 0x7F86, 0xC160,\n\t0x7F87, 0xC161, 0x7F88, 0xC162, 0x7F89, 0xC163, 0x7F8A, 0xD1F2,\n\t0x7F8B, 0xC164, 0x7F8C, 0xC7BC, 0x7F8D, 0xC165, 0x7F8E, 0xC3C0,\n\t0x7F8F, 0xC166, 0x7F90, 0xC167, 0x7F91, 0xC168, 0x7F92, 0xC169,\n\t0x7F93, 0xC16A, 0x7F94, 0xB8E1, 0x7F95, 0xC16B, 0x7F96, 0xC16C,\n\t0x7F97, 0xC16D, 0x7F98, 0xC16E, 0x7F99, 0xC16F, 0x7F9A, 0xC1E7,\n\t0x7F9B, 0xC170, 0x7F9C, 0xC171, 0x7F9D, 0xF4C6, 0x7F9E, 0xD0DF,\n\t0x7F9F, 0xF4C7, 0x7FA0, 0xC172, 0x7FA1, 0xCFDB, 0x7FA2, 0xC173,\n\t0x7FA3, 0xC174, 0x7FA4, 0xC8BA, 0x7FA5, 0xC175, 0x7FA6, 0xC176,\n\t0x7FA7, 0xF4C8, 0x7FA8, 0xC177, 0x7FA9, 0xC178, 0x7FAA, 0xC179,\n\t0x7FAB, 0xC17A, 0x7FAC, 0xC17B, 0x7FAD, 0xC17C, 0x7FAE, 0xC17D,\n\t0x7FAF, 0xF4C9, 0x7FB0, 0xF4CA, 0x7FB1, 0xC17E, 0x7FB2, 0xF4CB,\n\t0x7FB3, 0xC180, 0x7FB4, 0xC181, 0x7FB5, 0xC182, 0x7FB6, 0xC183,\n\t0x7FB7, 0xC184, 0x7FB8, 0xD9FA, 0x7FB9, 0xB8FE, 0x7FBA, 0xC185,\n\t0x7FBB, 0xC186, 0x7FBC, 0xE5F1, 0x7FBD, 0xD3F0, 0x7FBE, 0xC187,\n\t0x7FBF, 0xF4E0, 0x7FC0, 0xC188, 0x7FC1, 0xCECC, 0x7FC2, 0xC189,\n\t0x7FC3, 0xC18A, 0x7FC4, 0xC18B, 0x7FC5, 0xB3E1, 0x7FC6, 0xC18C,\n\t0x7FC7, 0xC18D, 0x7FC8, 0xC18E, 0x7FC9, 0xC18F, 0x7FCA, 0xF1B4,\n\t0x7FCB, 0xC190, 0x7FCC, 0xD2EE, 0x7FCD, 0xC191, 0x7FCE, 0xF4E1,\n\t0x7FCF, 0xC192, 0x7FD0, 0xC193, 0x7FD1, 0xC194, 0x7FD2, 0xC195,\n\t0x7FD3, 0xC196, 0x7FD4, 0xCFE8, 0x7FD5, 0xF4E2, 0x7FD6, 0xC197,\n\t0x7FD7, 0xC198, 0x7FD8, 0xC7CC, 0x7FD9, 0xC199, 0x7FDA, 0xC19A,\n\t0x7FDB, 0xC19B, 0x7FDC, 0xC19C, 0x7FDD, 0xC19D, 0x7FDE, 0xC19E,\n\t0x7FDF, 0xB5D4, 0x7FE0, 0xB4E4, 0x7FE1, 0xF4E4, 0x7FE2, 0xC19F,\n\t0x7FE3, 0xC1A0, 0x7FE4, 0xC240, 0x7FE5, 0xF4E3, 0x7FE6, 0xF4E5,\n\t0x7FE7, 0xC241, 0x7FE8, 0xC242, 0x7FE9, 0xF4E6, 0x7FEA, 0xC243,\n\t0x7FEB, 0xC244, 0x7FEC, 0xC245, 0x7FED, 0xC246, 0x7FEE, 0xF4E7,\n\t0x7FEF, 0xC247, 0x7FF0, 0xBAB2, 0x7FF1, 0xB0BF, 0x7FF2, 0xC248,\n\t0x7FF3, 0xF4E8, 0x7FF4, 0xC249, 0x7FF5, 0xC24A, 0x7FF6, 0xC24B,\n\t0x7FF7, 0xC24C, 0x7FF8, 0xC24D, 0x7FF9, 0xC24E, 0x7FFA, 0xC24F,\n\t0x7FFB, 0xB7AD, 0x7FFC, 0xD2ED, 0x7FFD, 0xC250, 0x7FFE, 0xC251,\n\t0x7FFF, 0xC252, 0x8000, 0xD2AB, 0x8001, 0xC0CF, 0x8002, 0xC253,\n\t0x8003, 0xBFBC, 0x8004, 0xEBA3, 0x8005, 0xD5DF, 0x8006, 0xEAC8,\n\t0x8007, 0xC254, 0x8008, 0xC255, 0x8009, 0xC256, 0x800A, 0xC257,\n\t0x800B, 0xF1F3, 0x800C, 0xB6F8, 0x800D, 0xCBA3, 0x800E, 0xC258,\n\t0x800F, 0xC259, 0x8010, 0xC4CD, 0x8011, 0xC25A, 0x8012, 0xF1E7,\n\t0x8013, 0xC25B, 0x8014, 0xF1E8, 0x8015, 0xB8FB, 0x8016, 0xF1E9,\n\t0x8017, 0xBAC4, 0x8018, 0xD4C5, 0x8019, 0xB0D2, 0x801A, 0xC25C,\n\t0x801B, 0xC25D, 0x801C, 0xF1EA, 0x801D, 0xC25E, 0x801E, 0xC25F,\n\t0x801F, 0xC260, 0x8020, 0xF1EB, 0x8021, 0xC261, 0x8022, 0xF1EC,\n\t0x8023, 0xC262, 0x8024, 0xC263, 0x8025, 0xF1ED, 0x8026, 0xF1EE,\n\t0x8027, 0xF1EF, 0x8028, 0xF1F1, 0x8029, 0xF1F0, 0x802A, 0xC5D5,\n\t0x802B, 0xC264, 0x802C, 0xC265, 0x802D, 0xC266, 0x802E, 0xC267,\n\t0x802F, 0xC268, 0x8030, 0xC269, 0x8031, 0xF1F2, 0x8032, 0xC26A,\n\t0x8033, 0xB6FA, 0x8034, 0xC26B, 0x8035, 0xF1F4, 0x8036, 0xD2AE,\n\t0x8037, 0xDEC7, 0x8038, 0xCBCA, 0x8039, 0xC26C, 0x803A, 0xC26D,\n\t0x803B, 0xB3DC, 0x803C, 0xC26E, 0x803D, 0xB5A2, 0x803E, 0xC26F,\n\t0x803F, 0xB9A2, 0x8040, 0xC270, 0x8041, 0xC271, 0x8042, 0xC4F4,\n\t0x8043, 0xF1F5, 0x8044, 0xC272, 0x8045, 0xC273, 0x8046, 0xF1F6,\n\t0x8047, 0xC274, 0x8048, 0xC275, 0x8049, 0xC276, 0x804A, 0xC1C4,\n\t0x804B, 0xC1FB, 0x804C, 0xD6B0, 0x804D, 0xF1F7, 0x804E, 0xC277,\n\t0x804F, 0xC278, 0x8050, 0xC279, 0x8051, 0xC27A, 0x8052, 0xF1F8,\n\t0x8053, 0xC27B, 0x8054, 0xC1AA, 0x8055, 0xC27C, 0x8056, 0xC27D,\n\t0x8057, 0xC27E, 0x8058, 0xC6B8, 0x8059, 0xC280, 0x805A, 0xBEDB,\n\t0x805B, 0xC281, 0x805C, 0xC282, 0x805D, 0xC283, 0x805E, 0xC284,\n\t0x805F, 0xC285, 0x8060, 0xC286, 0x8061, 0xC287, 0x8062, 0xC288,\n\t0x8063, 0xC289, 0x8064, 0xC28A, 0x8065, 0xC28B, 0x8066, 0xC28C,\n\t0x8067, 0xC28D, 0x8068, 0xC28E, 0x8069, 0xF1F9, 0x806A, 0xB4CF,\n\t0x806B, 0xC28F, 0x806C, 0xC290, 0x806D, 0xC291, 0x806E, 0xC292,\n\t0x806F, 0xC293, 0x8070, 0xC294, 0x8071, 0xF1FA, 0x8072, 0xC295,\n\t0x8073, 0xC296, 0x8074, 0xC297, 0x8075, 0xC298, 0x8076, 0xC299,\n\t0x8077, 0xC29A, 0x8078, 0xC29B, 0x8079, 0xC29C, 0x807A, 0xC29D,\n\t0x807B, 0xC29E, 0x807C, 0xC29F, 0x807D, 0xC2A0, 0x807E, 0xC340,\n\t0x807F, 0xEDB2, 0x8080, 0xEDB1, 0x8081, 0xC341, 0x8082, 0xC342,\n\t0x8083, 0xCBE0, 0x8084, 0xD2DE, 0x8085, 0xC343, 0x8086, 0xCBC1,\n\t0x8087, 0xD5D8, 0x8088, 0xC344, 0x8089, 0xC8E2, 0x808A, 0xC345,\n\t0x808B, 0xC0DF, 0x808C, 0xBCA1, 0x808D, 0xC346, 0x808E, 0xC347,\n\t0x808F, 0xC348, 0x8090, 0xC349, 0x8091, 0xC34A, 0x8092, 0xC34B,\n\t0x8093, 0xEBC1, 0x8094, 0xC34C, 0x8095, 0xC34D, 0x8096, 0xD0A4,\n\t0x8097, 0xC34E, 0x8098, 0xD6E2, 0x8099, 0xC34F, 0x809A, 0xB6C7,\n\t0x809B, 0xB8D8, 0x809C, 0xEBC0, 0x809D, 0xB8CE, 0x809E, 0xC350,\n\t0x809F, 0xEBBF, 0x80A0, 0xB3A6, 0x80A1, 0xB9C9, 0x80A2, 0xD6AB,\n\t0x80A3, 0xC351, 0x80A4, 0xB7F4, 0x80A5, 0xB7CA, 0x80A6, 0xC352,\n\t0x80A7, 0xC353, 0x80A8, 0xC354, 0x80A9, 0xBCE7, 0x80AA, 0xB7BE,\n\t0x80AB, 0xEBC6, 0x80AC, 0xC355, 0x80AD, 0xEBC7, 0x80AE, 0xB0B9,\n\t0x80AF, 0xBFCF, 0x80B0, 0xC356, 0x80B1, 0xEBC5, 0x80B2, 0xD3FD,\n\t0x80B3, 0xC357, 0x80B4, 0xEBC8, 0x80B5, 0xC358, 0x80B6, 0xC359,\n\t0x80B7, 0xEBC9, 0x80B8, 0xC35A, 0x80B9, 0xC35B, 0x80BA, 0xB7CE,\n\t0x80BB, 0xC35C, 0x80BC, 0xEBC2, 0x80BD, 0xEBC4, 0x80BE, 0xC9F6,\n\t0x80BF, 0xD6D7, 0x80C0, 0xD5CD, 0x80C1, 0xD0B2, 0x80C2, 0xEBCF,\n\t0x80C3, 0xCEB8, 0x80C4, 0xEBD0, 0x80C5, 0xC35D, 0x80C6, 0xB5A8,\n\t0x80C7, 0xC35E, 0x80C8, 0xC35F, 0x80C9, 0xC360, 0x80CA, 0xC361,\n\t0x80CB, 0xC362, 0x80CC, 0xB1B3, 0x80CD, 0xEBD2, 0x80CE, 0xCCA5,\n\t0x80CF, 0xC363, 0x80D0, 0xC364, 0x80D1, 0xC365, 0x80D2, 0xC366,\n\t0x80D3, 0xC367, 0x80D4, 0xC368, 0x80D5, 0xC369, 0x80D6, 0xC5D6,\n\t0x80D7, 0xEBD3, 0x80D8, 0xC36A, 0x80D9, 0xEBD1, 0x80DA, 0xC5DF,\n\t0x80DB, 0xEBCE, 0x80DC, 0xCAA4, 0x80DD, 0xEBD5, 0x80DE, 0xB0FB,\n\t0x80DF, 0xC36B, 0x80E0, 0xC36C, 0x80E1, 0xBAFA, 0x80E2, 0xC36D,\n\t0x80E3, 0xC36E, 0x80E4, 0xD8B7, 0x80E5, 0xF1E3, 0x80E6, 0xC36F,\n\t0x80E7, 0xEBCA, 0x80E8, 0xEBCB, 0x80E9, 0xEBCC, 0x80EA, 0xEBCD,\n\t0x80EB, 0xEBD6, 0x80EC, 0xE6C0, 0x80ED, 0xEBD9, 0x80EE, 0xC370,\n\t0x80EF, 0xBFE8, 0x80F0, 0xD2C8, 0x80F1, 0xEBD7, 0x80F2, 0xEBDC,\n\t0x80F3, 0xB8EC, 0x80F4, 0xEBD8, 0x80F5, 0xC371, 0x80F6, 0xBDBA,\n\t0x80F7, 0xC372, 0x80F8, 0xD0D8, 0x80F9, 0xC373, 0x80FA, 0xB0B7,\n\t0x80FB, 0xC374, 0x80FC, 0xEBDD, 0x80FD, 0xC4DC, 0x80FE, 0xC375,\n\t0x80FF, 0xC376, 0x8100, 0xC377, 0x8101, 0xC378, 0x8102, 0xD6AC,\n\t0x8103, 0xC379, 0x8104, 0xC37A, 0x8105, 0xC37B, 0x8106, 0xB4E0,\n\t0x8107, 0xC37C, 0x8108, 0xC37D, 0x8109, 0xC2F6, 0x810A, 0xBCB9,\n\t0x810B, 0xC37E, 0x810C, 0xC380, 0x810D, 0xEBDA, 0x810E, 0xEBDB,\n\t0x810F, 0xD4E0, 0x8110, 0xC6EA, 0x8111, 0xC4D4, 0x8112, 0xEBDF,\n\t0x8113, 0xC5A7, 0x8114, 0xD9F5, 0x8115, 0xC381, 0x8116, 0xB2B1,\n\t0x8117, 0xC382, 0x8118, 0xEBE4, 0x8119, 0xC383, 0x811A, 0xBDC5,\n\t0x811B, 0xC384, 0x811C, 0xC385, 0x811D, 0xC386, 0x811E, 0xEBE2,\n\t0x811F, 0xC387, 0x8120, 0xC388, 0x8121, 0xC389, 0x8122, 0xC38A,\n\t0x8123, 0xC38B, 0x8124, 0xC38C, 0x8125, 0xC38D, 0x8126, 0xC38E,\n\t0x8127, 0xC38F, 0x8128, 0xC390, 0x8129, 0xC391, 0x812A, 0xC392,\n\t0x812B, 0xC393, 0x812C, 0xEBE3, 0x812D, 0xC394, 0x812E, 0xC395,\n\t0x812F, 0xB8AC, 0x8130, 0xC396, 0x8131, 0xCDD1, 0x8132, 0xEBE5,\n\t0x8133, 0xC397, 0x8134, 0xC398, 0x8135, 0xC399, 0x8136, 0xEBE1,\n\t0x8137, 0xC39A, 0x8138, 0xC1B3, 0x8139, 0xC39B, 0x813A, 0xC39C,\n\t0x813B, 0xC39D, 0x813C, 0xC39E, 0x813D, 0xC39F, 0x813E, 0xC6A2,\n\t0x813F, 0xC3A0, 0x8140, 0xC440, 0x8141, 0xC441, 0x8142, 0xC442,\n\t0x8143, 0xC443, 0x8144, 0xC444, 0x8145, 0xC445, 0x8146, 0xCCF3,\n\t0x8147, 0xC446, 0x8148, 0xEBE6, 0x8149, 0xC447, 0x814A, 0xC0B0,\n\t0x814B, 0xD2B8, 0x814C, 0xEBE7, 0x814D, 0xC448, 0x814E, 0xC449,\n\t0x814F, 0xC44A, 0x8150, 0xB8AF, 0x8151, 0xB8AD, 0x8152, 0xC44B,\n\t0x8153, 0xEBE8, 0x8154, 0xC7BB, 0x8155, 0xCDF3, 0x8156, 0xC44C,\n\t0x8157, 0xC44D, 0x8158, 0xC44E, 0x8159, 0xEBEA, 0x815A, 0xEBEB,\n\t0x815B, 0xC44F, 0x815C, 0xC450, 0x815D, 0xC451, 0x815E, 0xC452,\n\t0x815F, 0xC453, 0x8160, 0xEBED, 0x8161, 0xC454, 0x8162, 0xC455,\n\t0x8163, 0xC456, 0x8164, 0xC457, 0x8165, 0xD0C8, 0x8166, 0xC458,\n\t0x8167, 0xEBF2, 0x8168, 0xC459, 0x8169, 0xEBEE, 0x816A, 0xC45A,\n\t0x816B, 0xC45B, 0x816C, 0xC45C, 0x816D, 0xEBF1, 0x816E, 0xC8F9,\n\t0x816F, 0xC45D, 0x8170, 0xD1FC, 0x8171, 0xEBEC, 0x8172, 0xC45E,\n\t0x8173, 0xC45F, 0x8174, 0xEBE9, 0x8175, 0xC460, 0x8176, 0xC461,\n\t0x8177, 0xC462, 0x8178, 0xC463, 0x8179, 0xB8B9, 0x817A, 0xCFD9,\n\t0x817B, 0xC4E5, 0x817C, 0xEBEF, 0x817D, 0xEBF0, 0x817E, 0xCCDA,\n\t0x817F, 0xCDC8, 0x8180, 0xB0F2, 0x8181, 0xC464, 0x8182, 0xEBF6,\n\t0x8183, 0xC465, 0x8184, 0xC466, 0x8185, 0xC467, 0x8186, 0xC468,\n\t0x8187, 0xC469, 0x8188, 0xEBF5, 0x8189, 0xC46A, 0x818A, 0xB2B2,\n\t0x818B, 0xC46B, 0x818C, 0xC46C, 0x818D, 0xC46D, 0x818E, 0xC46E,\n\t0x818F, 0xB8E0, 0x8190, 0xC46F, 0x8191, 0xEBF7, 0x8192, 0xC470,\n\t0x8193, 0xC471, 0x8194, 0xC472, 0x8195, 0xC473, 0x8196, 0xC474,\n\t0x8197, 0xC475, 0x8198, 0xB1EC, 0x8199, 0xC476, 0x819A, 0xC477,\n\t0x819B, 0xCCC5, 0x819C, 0xC4A4, 0x819D, 0xCFA5, 0x819E, 0xC478,\n\t0x819F, 0xC479, 0x81A0, 0xC47A, 0x81A1, 0xC47B, 0x81A2, 0xC47C,\n\t0x81A3, 0xEBF9, 0x81A4, 0xC47D, 0x81A5, 0xC47E, 0x81A6, 0xECA2,\n\t0x81A7, 0xC480, 0x81A8, 0xC5F2, 0x81A9, 0xC481, 0x81AA, 0xEBFA,\n\t0x81AB, 0xC482, 0x81AC, 0xC483, 0x81AD, 0xC484, 0x81AE, 0xC485,\n\t0x81AF, 0xC486, 0x81B0, 0xC487, 0x81B1, 0xC488, 0x81B2, 0xC489,\n\t0x81B3, 0xC9C5, 0x81B4, 0xC48A, 0x81B5, 0xC48B, 0x81B6, 0xC48C,\n\t0x81B7, 0xC48D, 0x81B8, 0xC48E, 0x81B9, 0xC48F, 0x81BA, 0xE2DF,\n\t0x81BB, 0xEBFE, 0x81BC, 0xC490, 0x81BD, 0xC491, 0x81BE, 0xC492,\n\t0x81BF, 0xC493, 0x81C0, 0xCDCE, 0x81C1, 0xECA1, 0x81C2, 0xB1DB,\n\t0x81C3, 0xD3B7, 0x81C4, 0xC494, 0x81C5, 0xC495, 0x81C6, 0xD2DC,\n\t0x81C7, 0xC496, 0x81C8, 0xC497, 0x81C9, 0xC498, 0x81CA, 0xEBFD,\n\t0x81CB, 0xC499, 0x81CC, 0xEBFB, 0x81CD, 0xC49A, 0x81CE, 0xC49B,\n\t0x81CF, 0xC49C, 0x81D0, 0xC49D, 0x81D1, 0xC49E, 0x81D2, 0xC49F,\n\t0x81D3, 0xC4A0, 0x81D4, 0xC540, 0x81D5, 0xC541, 0x81D6, 0xC542,\n\t0x81D7, 0xC543, 0x81D8, 0xC544, 0x81D9, 0xC545, 0x81DA, 0xC546,\n\t0x81DB, 0xC547, 0x81DC, 0xC548, 0x81DD, 0xC549, 0x81DE, 0xC54A,\n\t0x81DF, 0xC54B, 0x81E0, 0xC54C, 0x81E1, 0xC54D, 0x81E2, 0xC54E,\n\t0x81E3, 0xB3BC, 0x81E4, 0xC54F, 0x81E5, 0xC550, 0x81E6, 0xC551,\n\t0x81E7, 0xEAB0, 0x81E8, 0xC552, 0x81E9, 0xC553, 0x81EA, 0xD7D4,\n\t0x81EB, 0xC554, 0x81EC, 0xF4AB, 0x81ED, 0xB3F4, 0x81EE, 0xC555,\n\t0x81EF, 0xC556, 0x81F0, 0xC557, 0x81F1, 0xC558, 0x81F2, 0xC559,\n\t0x81F3, 0xD6C1, 0x81F4, 0xD6C2, 0x81F5, 0xC55A, 0x81F6, 0xC55B,\n\t0x81F7, 0xC55C, 0x81F8, 0xC55D, 0x81F9, 0xC55E, 0x81FA, 0xC55F,\n\t0x81FB, 0xD5E9, 0x81FC, 0xBECA, 0x81FD, 0xC560, 0x81FE, 0xF4A7,\n\t0x81FF, 0xC561, 0x8200, 0xD2A8, 0x8201, 0xF4A8, 0x8202, 0xF4A9,\n\t0x8203, 0xC562, 0x8204, 0xF4AA, 0x8205, 0xBECB, 0x8206, 0xD3DF,\n\t0x8207, 0xC563, 0x8208, 0xC564, 0x8209, 0xC565, 0x820A, 0xC566,\n\t0x820B, 0xC567, 0x820C, 0xC9E0, 0x820D, 0xC9E1, 0x820E, 0xC568,\n\t0x820F, 0xC569, 0x8210, 0xF3C2, 0x8211, 0xC56A, 0x8212, 0xCAE6,\n\t0x8213, 0xC56B, 0x8214, 0xCCF2, 0x8215, 0xC56C, 0x8216, 0xC56D,\n\t0x8217, 0xC56E, 0x8218, 0xC56F, 0x8219, 0xC570, 0x821A, 0xC571,\n\t0x821B, 0xE2B6, 0x821C, 0xCBB4, 0x821D, 0xC572, 0x821E, 0xCEE8,\n\t0x821F, 0xD6DB, 0x8220, 0xC573, 0x8221, 0xF4AD, 0x8222, 0xF4AE,\n\t0x8223, 0xF4AF, 0x8224, 0xC574, 0x8225, 0xC575, 0x8226, 0xC576,\n\t0x8227, 0xC577, 0x8228, 0xF4B2, 0x8229, 0xC578, 0x822A, 0xBABD,\n\t0x822B, 0xF4B3, 0x822C, 0xB0E3, 0x822D, 0xF4B0, 0x822E, 0xC579,\n\t0x822F, 0xF4B1, 0x8230, 0xBDA2, 0x8231, 0xB2D5, 0x8232, 0xC57A,\n\t0x8233, 0xF4B6, 0x8234, 0xF4B7, 0x8235, 0xB6E6, 0x8236, 0xB2B0,\n\t0x8237, 0xCFCF, 0x8238, 0xF4B4, 0x8239, 0xB4AC, 0x823A, 0xC57B,\n\t0x823B, 0xF4B5, 0x823C, 0xC57C, 0x823D, 0xC57D, 0x823E, 0xF4B8,\n\t0x823F, 0xC57E, 0x8240, 0xC580, 0x8241, 0xC581, 0x8242, 0xC582,\n\t0x8243, 0xC583, 0x8244, 0xF4B9, 0x8245, 0xC584, 0x8246, 0xC585,\n\t0x8247, 0xCDA7, 0x8248, 0xC586, 0x8249, 0xF4BA, 0x824A, 0xC587,\n\t0x824B, 0xF4BB, 0x824C, 0xC588, 0x824D, 0xC589, 0x824E, 0xC58A,\n\t0x824F, 0xF4BC, 0x8250, 0xC58B, 0x8251, 0xC58C, 0x8252, 0xC58D,\n\t0x8253, 0xC58E, 0x8254, 0xC58F, 0x8255, 0xC590, 0x8256, 0xC591,\n\t0x8257, 0xC592, 0x8258, 0xCBD2, 0x8259, 0xC593, 0x825A, 0xF4BD,\n\t0x825B, 0xC594, 0x825C, 0xC595, 0x825D, 0xC596, 0x825E, 0xC597,\n\t0x825F, 0xF4BE, 0x8260, 0xC598, 0x8261, 0xC599, 0x8262, 0xC59A,\n\t0x8263, 0xC59B, 0x8264, 0xC59C, 0x8265, 0xC59D, 0x8266, 0xC59E,\n\t0x8267, 0xC59F, 0x8268, 0xF4BF, 0x8269, 0xC5A0, 0x826A, 0xC640,\n\t0x826B, 0xC641, 0x826C, 0xC642, 0x826D, 0xC643, 0x826E, 0xF4DE,\n\t0x826F, 0xC1BC, 0x8270, 0xBCE8, 0x8271, 0xC644, 0x8272, 0xC9AB,\n\t0x8273, 0xD1DE, 0x8274, 0xE5F5, 0x8275, 0xC645, 0x8276, 0xC646,\n\t0x8277, 0xC647, 0x8278, 0xC648, 0x8279, 0xDCB3, 0x827A, 0xD2D5,\n\t0x827B, 0xC649, 0x827C, 0xC64A, 0x827D, 0xDCB4, 0x827E, 0xB0AC,\n\t0x827F, 0xDCB5, 0x8280, 0xC64B, 0x8281, 0xC64C, 0x8282, 0xBDDA,\n\t0x8283, 0xC64D, 0x8284, 0xDCB9, 0x8285, 0xC64E, 0x8286, 0xC64F,\n\t0x8287, 0xC650, 0x8288, 0xD8C2, 0x8289, 0xC651, 0x828A, 0xDCB7,\n\t0x828B, 0xD3F3, 0x828C, 0xC652, 0x828D, 0xC9D6, 0x828E, 0xDCBA,\n\t0x828F, 0xDCB6, 0x8290, 0xC653, 0x8291, 0xDCBB, 0x8292, 0xC3A2,\n\t0x8293, 0xC654, 0x8294, 0xC655, 0x8295, 0xC656, 0x8296, 0xC657,\n\t0x8297, 0xDCBC, 0x8298, 0xDCC5, 0x8299, 0xDCBD, 0x829A, 0xC658,\n\t0x829B, 0xC659, 0x829C, 0xCEDF, 0x829D, 0xD6A5, 0x829E, 0xC65A,\n\t0x829F, 0xDCCF, 0x82A0, 0xC65B, 0x82A1, 0xDCCD, 0x82A2, 0xC65C,\n\t0x82A3, 0xC65D, 0x82A4, 0xDCD2, 0x82A5, 0xBDE6, 0x82A6, 0xC2AB,\n\t0x82A7, 0xC65E, 0x82A8, 0xDCB8, 0x82A9, 0xDCCB, 0x82AA, 0xDCCE,\n\t0x82AB, 0xDCBE, 0x82AC, 0xB7D2, 0x82AD, 0xB0C5, 0x82AE, 0xDCC7,\n\t0x82AF, 0xD0BE, 0x82B0, 0xDCC1, 0x82B1, 0xBBA8, 0x82B2, 0xC65F,\n\t0x82B3, 0xB7BC, 0x82B4, 0xDCCC, 0x82B5, 0xC660, 0x82B6, 0xC661,\n\t0x82B7, 0xDCC6, 0x82B8, 0xDCBF, 0x82B9, 0xC7DB, 0x82BA, 0xC662,\n\t0x82BB, 0xC663, 0x82BC, 0xC664, 0x82BD, 0xD1BF, 0x82BE, 0xDCC0,\n\t0x82BF, 0xC665, 0x82C0, 0xC666, 0x82C1, 0xDCCA, 0x82C2, 0xC667,\n\t0x82C3, 0xC668, 0x82C4, 0xDCD0, 0x82C5, 0xC669, 0x82C6, 0xC66A,\n\t0x82C7, 0xCEAD, 0x82C8, 0xDCC2, 0x82C9, 0xC66B, 0x82CA, 0xDCC3,\n\t0x82CB, 0xDCC8, 0x82CC, 0xDCC9, 0x82CD, 0xB2D4, 0x82CE, 0xDCD1,\n\t0x82CF, 0xCBD5, 0x82D0, 0xC66C, 0x82D1, 0xD4B7, 0x82D2, 0xDCDB,\n\t0x82D3, 0xDCDF, 0x82D4, 0xCCA6, 0x82D5, 0xDCE6, 0x82D6, 0xC66D,\n\t0x82D7, 0xC3E7, 0x82D8, 0xDCDC, 0x82D9, 0xC66E, 0x82DA, 0xC66F,\n\t0x82DB, 0xBFC1, 0x82DC, 0xDCD9, 0x82DD, 0xC670, 0x82DE, 0xB0FA,\n\t0x82DF, 0xB9B6, 0x82E0, 0xDCE5, 0x82E1, 0xDCD3, 0x82E2, 0xC671,\n\t0x82E3, 0xDCC4, 0x82E4, 0xDCD6, 0x82E5, 0xC8F4, 0x82E6, 0xBFE0,\n\t0x82E7, 0xC672, 0x82E8, 0xC673, 0x82E9, 0xC674, 0x82EA, 0xC675,\n\t0x82EB, 0xC9BB, 0x82EC, 0xC676, 0x82ED, 0xC677, 0x82EE, 0xC678,\n\t0x82EF, 0xB1BD, 0x82F0, 0xC679, 0x82F1, 0xD3A2, 0x82F2, 0xC67A,\n\t0x82F3, 0xC67B, 0x82F4, 0xDCDA, 0x82F5, 0xC67C, 0x82F6, 0xC67D,\n\t0x82F7, 0xDCD5, 0x82F8, 0xC67E, 0x82F9, 0xC6BB, 0x82FA, 0xC680,\n\t0x82FB, 0xDCDE, 0x82FC, 0xC681, 0x82FD, 0xC682, 0x82FE, 0xC683,\n\t0x82FF, 0xC684, 0x8300, 0xC685, 0x8301, 0xD7C2, 0x8302, 0xC3AF,\n\t0x8303, 0xB7B6, 0x8304, 0xC7D1, 0x8305, 0xC3A9, 0x8306, 0xDCE2,\n\t0x8307, 0xDCD8, 0x8308, 0xDCEB, 0x8309, 0xDCD4, 0x830A, 0xC686,\n\t0x830B, 0xC687, 0x830C, 0xDCDD, 0x830D, 0xC688, 0x830E, 0xBEA5,\n\t0x830F, 0xDCD7, 0x8310, 0xC689, 0x8311, 0xDCE0, 0x8312, 0xC68A,\n\t0x8313, 0xC68B, 0x8314, 0xDCE3, 0x8315, 0xDCE4, 0x8316, 0xC68C,\n\t0x8317, 0xDCF8, 0x8318, 0xC68D, 0x8319, 0xC68E, 0x831A, 0xDCE1,\n\t0x831B, 0xDDA2, 0x831C, 0xDCE7, 0x831D, 0xC68F, 0x831E, 0xC690,\n\t0x831F, 0xC691, 0x8320, 0xC692, 0x8321, 0xC693, 0x8322, 0xC694,\n\t0x8323, 0xC695, 0x8324, 0xC696, 0x8325, 0xC697, 0x8326, 0xC698,\n\t0x8327, 0xBCEB, 0x8328, 0xB4C4, 0x8329, 0xC699, 0x832A, 0xC69A,\n\t0x832B, 0xC3A3, 0x832C, 0xB2E7, 0x832D, 0xDCFA, 0x832E, 0xC69B,\n\t0x832F, 0xDCF2, 0x8330, 0xC69C, 0x8331, 0xDCEF, 0x8332, 0xC69D,\n\t0x8333, 0xDCFC, 0x8334, 0xDCEE, 0x8335, 0xD2F0, 0x8336, 0xB2E8,\n\t0x8337, 0xC69E, 0x8338, 0xC8D7, 0x8339, 0xC8E3, 0x833A, 0xDCFB,\n\t0x833B, 0xC69F, 0x833C, 0xDCED, 0x833D, 0xC6A0, 0x833E, 0xC740,\n\t0x833F, 0xC741, 0x8340, 0xDCF7, 0x8341, 0xC742, 0x8342, 0xC743,\n\t0x8343, 0xDCF5, 0x8344, 0xC744, 0x8345, 0xC745, 0x8346, 0xBEA3,\n\t0x8347, 0xDCF4, 0x8348, 0xC746, 0x8349, 0xB2DD, 0x834A, 0xC747,\n\t0x834B, 0xC748, 0x834C, 0xC749, 0x834D, 0xC74A, 0x834E, 0xC74B,\n\t0x834F, 0xDCF3, 0x8350, 0xBCF6, 0x8351, 0xDCE8, 0x8352, 0xBBC4,\n\t0x8353, 0xC74C, 0x8354, 0xC0F3, 0x8355, 0xC74D, 0x8356, 0xC74E,\n\t0x8357, 0xC74F, 0x8358, 0xC750, 0x8359, 0xC751, 0x835A, 0xBCD4,\n\t0x835B, 0xDCE9, 0x835C, 0xDCEA, 0x835D, 0xC752, 0x835E, 0xDCF1,\n\t0x835F, 0xDCF6, 0x8360, 0xDCF9, 0x8361, 0xB5B4, 0x8362, 0xC753,\n\t0x8363, 0xC8D9, 0x8364, 0xBBE7, 0x8365, 0xDCFE, 0x8366, 0xDCFD,\n\t0x8367, 0xD3AB, 0x8368, 0xDDA1, 0x8369, 0xDDA3, 0x836A, 0xDDA5,\n\t0x836B, 0xD2F1, 0x836C, 0xDDA4, 0x836D, 0xDDA6, 0x836E, 0xDDA7,\n\t0x836F, 0xD2A9, 0x8370, 0xC754, 0x8371, 0xC755, 0x8372, 0xC756,\n\t0x8373, 0xC757, 0x8374, 0xC758, 0x8375, 0xC759, 0x8376, 0xC75A,\n\t0x8377, 0xBAC9, 0x8378, 0xDDA9, 0x8379, 0xC75B, 0x837A, 0xC75C,\n\t0x837B, 0xDDB6, 0x837C, 0xDDB1, 0x837D, 0xDDB4, 0x837E, 0xC75D,\n\t0x837F, 0xC75E, 0x8380, 0xC75F, 0x8381, 0xC760, 0x8382, 0xC761,\n\t0x8383, 0xC762, 0x8384, 0xC763, 0x8385, 0xDDB0, 0x8386, 0xC6CE,\n\t0x8387, 0xC764, 0x8388, 0xC765, 0x8389, 0xC0F2, 0x838A, 0xC766,\n\t0x838B, 0xC767, 0x838C, 0xC768, 0x838D, 0xC769, 0x838E, 0xC9AF,\n\t0x838F, 0xC76A, 0x8390, 0xC76B, 0x8391, 0xC76C, 0x8392, 0xDCEC,\n\t0x8393, 0xDDAE, 0x8394, 0xC76D, 0x8395, 0xC76E, 0x8396, 0xC76F,\n\t0x8397, 0xC770, 0x8398, 0xDDB7, 0x8399, 0xC771, 0x839A, 0xC772,\n\t0x839B, 0xDCF0, 0x839C, 0xDDAF, 0x839D, 0xC773, 0x839E, 0xDDB8,\n\t0x839F, 0xC774, 0x83A0, 0xDDAC, 0x83A1, 0xC775, 0x83A2, 0xC776,\n\t0x83A3, 0xC777, 0x83A4, 0xC778, 0x83A5, 0xC779, 0x83A6, 0xC77A,\n\t0x83A7, 0xC77B, 0x83A8, 0xDDB9, 0x83A9, 0xDDB3, 0x83AA, 0xDDAD,\n\t0x83AB, 0xC4AA, 0x83AC, 0xC77C, 0x83AD, 0xC77D, 0x83AE, 0xC77E,\n\t0x83AF, 0xC780, 0x83B0, 0xDDA8, 0x83B1, 0xC0B3, 0x83B2, 0xC1AB,\n\t0x83B3, 0xDDAA, 0x83B4, 0xDDAB, 0x83B5, 0xC781, 0x83B6, 0xDDB2,\n\t0x83B7, 0xBBF1, 0x83B8, 0xDDB5, 0x83B9, 0xD3A8, 0x83BA, 0xDDBA,\n\t0x83BB, 0xC782, 0x83BC, 0xDDBB, 0x83BD, 0xC3A7, 0x83BE, 0xC783,\n\t0x83BF, 0xC784, 0x83C0, 0xDDD2, 0x83C1, 0xDDBC, 0x83C2, 0xC785,\n\t0x83C3, 0xC786, 0x83C4, 0xC787, 0x83C5, 0xDDD1, 0x83C6, 0xC788,\n\t0x83C7, 0xB9BD, 0x83C8, 0xC789, 0x83C9, 0xC78A, 0x83CA, 0xBED5,\n\t0x83CB, 0xC78B, 0x83CC, 0xBEFA, 0x83CD, 0xC78C, 0x83CE, 0xC78D,\n\t0x83CF, 0xBACA, 0x83D0, 0xC78E, 0x83D1, 0xC78F, 0x83D2, 0xC790,\n\t0x83D3, 0xC791, 0x83D4, 0xDDCA, 0x83D5, 0xC792, 0x83D6, 0xDDC5,\n\t0x83D7, 0xC793, 0x83D8, 0xDDBF, 0x83D9, 0xC794, 0x83DA, 0xC795,\n\t0x83DB, 0xC796, 0x83DC, 0xB2CB, 0x83DD, 0xDDC3, 0x83DE, 0xC797,\n\t0x83DF, 0xDDCB, 0x83E0, 0xB2A4, 0x83E1, 0xDDD5, 0x83E2, 0xC798,\n\t0x83E3, 0xC799, 0x83E4, 0xC79A, 0x83E5, 0xDDBE, 0x83E6, 0xC79B,\n\t0x83E7, 0xC79C, 0x83E8, 0xC79D, 0x83E9, 0xC6D0, 0x83EA, 0xDDD0,\n\t0x83EB, 0xC79E, 0x83EC, 0xC79F, 0x83ED, 0xC7A0, 0x83EE, 0xC840,\n\t0x83EF, 0xC841, 0x83F0, 0xDDD4, 0x83F1, 0xC1E2, 0x83F2, 0xB7C6,\n\t0x83F3, 0xC842, 0x83F4, 0xC843, 0x83F5, 0xC844, 0x83F6, 0xC845,\n\t0x83F7, 0xC846, 0x83F8, 0xDDCE, 0x83F9, 0xDDCF, 0x83FA, 0xC847,\n\t0x83FB, 0xC848, 0x83FC, 0xC849, 0x83FD, 0xDDC4, 0x83FE, 0xC84A,\n\t0x83FF, 0xC84B, 0x8400, 0xC84C, 0x8401, 0xDDBD, 0x8402, 0xC84D,\n\t0x8403, 0xDDCD, 0x8404, 0xCCD1, 0x8405, 0xC84E, 0x8406, 0xDDC9,\n\t0x8407, 0xC84F, 0x8408, 0xC850, 0x8409, 0xC851, 0x840A, 0xC852,\n\t0x840B, 0xDDC2, 0x840C, 0xC3C8, 0x840D, 0xC6BC, 0x840E, 0xCEAE,\n\t0x840F, 0xDDCC, 0x8410, 0xC853, 0x8411, 0xDDC8, 0x8412, 0xC854,\n\t0x8413, 0xC855, 0x8414, 0xC856, 0x8415, 0xC857, 0x8416, 0xC858,\n\t0x8417, 0xC859, 0x8418, 0xDDC1, 0x8419, 0xC85A, 0x841A, 0xC85B,\n\t0x841B, 0xC85C, 0x841C, 0xDDC6, 0x841D, 0xC2DC, 0x841E, 0xC85D,\n\t0x841F, 0xC85E, 0x8420, 0xC85F, 0x8421, 0xC860, 0x8422, 0xC861,\n\t0x8423, 0xC862, 0x8424, 0xD3A9, 0x8425, 0xD3AA, 0x8426, 0xDDD3,\n\t0x8427, 0xCFF4, 0x8428, 0xC8F8, 0x8429, 0xC863, 0x842A, 0xC864,\n\t0x842B, 0xC865, 0x842C, 0xC866, 0x842D, 0xC867, 0x842E, 0xC868,\n\t0x842F, 0xC869, 0x8430, 0xC86A, 0x8431, 0xDDE6, 0x8432, 0xC86B,\n\t0x8433, 0xC86C, 0x8434, 0xC86D, 0x8435, 0xC86E, 0x8436, 0xC86F,\n\t0x8437, 0xC870, 0x8438, 0xDDC7, 0x8439, 0xC871, 0x843A, 0xC872,\n\t0x843B, 0xC873, 0x843C, 0xDDE0, 0x843D, 0xC2E4, 0x843E, 0xC874,\n\t0x843F, 0xC875, 0x8440, 0xC876, 0x8441, 0xC877, 0x8442, 0xC878,\n\t0x8443, 0xC879, 0x8444, 0xC87A, 0x8445, 0xC87B, 0x8446, 0xDDE1,\n\t0x8447, 0xC87C, 0x8448, 0xC87D, 0x8449, 0xC87E, 0x844A, 0xC880,\n\t0x844B, 0xC881, 0x844C, 0xC882, 0x844D, 0xC883, 0x844E, 0xC884,\n\t0x844F, 0xC885, 0x8450, 0xC886, 0x8451, 0xDDD7, 0x8452, 0xC887,\n\t0x8453, 0xC888, 0x8454, 0xC889, 0x8455, 0xC88A, 0x8456, 0xC88B,\n\t0x8457, 0xD6F8, 0x8458, 0xC88C, 0x8459, 0xDDD9, 0x845A, 0xDDD8,\n\t0x845B, 0xB8F0, 0x845C, 0xDDD6, 0x845D, 0xC88D, 0x845E, 0xC88E,\n\t0x845F, 0xC88F, 0x8460, 0xC890, 0x8461, 0xC6CF, 0x8462, 0xC891,\n\t0x8463, 0xB6AD, 0x8464, 0xC892, 0x8465, 0xC893, 0x8466, 0xC894,\n\t0x8467, 0xC895, 0x8468, 0xC896, 0x8469, 0xDDE2, 0x846A, 0xC897,\n\t0x846B, 0xBAF9, 0x846C, 0xD4E1, 0x846D, 0xDDE7, 0x846E, 0xC898,\n\t0x846F, 0xC899, 0x8470, 0xC89A, 0x8471, 0xB4D0, 0x8472, 0xC89B,\n\t0x8473, 0xDDDA, 0x8474, 0xC89C, 0x8475, 0xBFFB, 0x8476, 0xDDE3,\n\t0x8477, 0xC89D, 0x8478, 0xDDDF, 0x8479, 0xC89E, 0x847A, 0xDDDD,\n\t0x847B, 0xC89F, 0x847C, 0xC8A0, 0x847D, 0xC940, 0x847E, 0xC941,\n\t0x847F, 0xC942, 0x8480, 0xC943, 0x8481, 0xC944, 0x8482, 0xB5D9,\n\t0x8483, 0xC945, 0x8484, 0xC946, 0x8485, 0xC947, 0x8486, 0xC948,\n\t0x8487, 0xDDDB, 0x8488, 0xDDDC, 0x8489, 0xDDDE, 0x848A, 0xC949,\n\t0x848B, 0xBDAF, 0x848C, 0xDDE4, 0x848D, 0xC94A, 0x848E, 0xDDE5,\n\t0x848F, 0xC94B, 0x8490, 0xC94C, 0x8491, 0xC94D, 0x8492, 0xC94E,\n\t0x8493, 0xC94F, 0x8494, 0xC950, 0x8495, 0xC951, 0x8496, 0xC952,\n\t0x8497, 0xDDF5, 0x8498, 0xC953, 0x8499, 0xC3C9, 0x849A, 0xC954,\n\t0x849B, 0xC955, 0x849C, 0xCBE2, 0x849D, 0xC956, 0x849E, 0xC957,\n\t0x849F, 0xC958, 0x84A0, 0xC959, 0x84A1, 0xDDF2, 0x84A2, 0xC95A,\n\t0x84A3, 0xC95B, 0x84A4, 0xC95C, 0x84A5, 0xC95D, 0x84A6, 0xC95E,\n\t0x84A7, 0xC95F, 0x84A8, 0xC960, 0x84A9, 0xC961, 0x84AA, 0xC962,\n\t0x84AB, 0xC963, 0x84AC, 0xC964, 0x84AD, 0xC965, 0x84AE, 0xC966,\n\t0x84AF, 0xD8E1, 0x84B0, 0xC967, 0x84B1, 0xC968, 0x84B2, 0xC6D1,\n\t0x84B3, 0xC969, 0x84B4, 0xDDF4, 0x84B5, 0xC96A, 0x84B6, 0xC96B,\n\t0x84B7, 0xC96C, 0x84B8, 0xD5F4, 0x84B9, 0xDDF3, 0x84BA, 0xDDF0,\n\t0x84BB, 0xC96D, 0x84BC, 0xC96E, 0x84BD, 0xDDEC, 0x84BE, 0xC96F,\n\t0x84BF, 0xDDEF, 0x84C0, 0xC970, 0x84C1, 0xDDE8, 0x84C2, 0xC971,\n\t0x84C3, 0xC972, 0x84C4, 0xD0EE, 0x84C5, 0xC973, 0x84C6, 0xC974,\n\t0x84C7, 0xC975, 0x84C8, 0xC976, 0x84C9, 0xC8D8, 0x84CA, 0xDDEE,\n\t0x84CB, 0xC977, 0x84CC, 0xC978, 0x84CD, 0xDDE9, 0x84CE, 0xC979,\n\t0x84CF, 0xC97A, 0x84D0, 0xDDEA, 0x84D1, 0xCBF2, 0x84D2, 0xC97B,\n\t0x84D3, 0xDDED, 0x84D4, 0xC97C, 0x84D5, 0xC97D, 0x84D6, 0xB1CD,\n\t0x84D7, 0xC97E, 0x84D8, 0xC980, 0x84D9, 0xC981, 0x84DA, 0xC982,\n\t0x84DB, 0xC983, 0x84DC, 0xC984, 0x84DD, 0xC0B6, 0x84DE, 0xC985,\n\t0x84DF, 0xBCBB, 0x84E0, 0xDDF1, 0x84E1, 0xC986, 0x84E2, 0xC987,\n\t0x84E3, 0xDDF7, 0x84E4, 0xC988, 0x84E5, 0xDDF6, 0x84E6, 0xDDEB,\n\t0x84E7, 0xC989, 0x84E8, 0xC98A, 0x84E9, 0xC98B, 0x84EA, 0xC98C,\n\t0x84EB, 0xC98D, 0x84EC, 0xC5EE, 0x84ED, 0xC98E, 0x84EE, 0xC98F,\n\t0x84EF, 0xC990, 0x84F0, 0xDDFB, 0x84F1, 0xC991, 0x84F2, 0xC992,\n\t0x84F3, 0xC993, 0x84F4, 0xC994, 0x84F5, 0xC995, 0x84F6, 0xC996,\n\t0x84F7, 0xC997, 0x84F8, 0xC998, 0x84F9, 0xC999, 0x84FA, 0xC99A,\n\t0x84FB, 0xC99B, 0x84FC, 0xDEA4, 0x84FD, 0xC99C, 0x84FE, 0xC99D,\n\t0x84FF, 0xDEA3, 0x8500, 0xC99E, 0x8501, 0xC99F, 0x8502, 0xC9A0,\n\t0x8503, 0xCA40, 0x8504, 0xCA41, 0x8505, 0xCA42, 0x8506, 0xCA43,\n\t0x8507, 0xCA44, 0x8508, 0xCA45, 0x8509, 0xCA46, 0x850A, 0xCA47,\n\t0x850B, 0xCA48, 0x850C, 0xDDF8, 0x850D, 0xCA49, 0x850E, 0xCA4A,\n\t0x850F, 0xCA4B, 0x8510, 0xCA4C, 0x8511, 0xC3EF, 0x8512, 0xCA4D,\n\t0x8513, 0xC2FB, 0x8514, 0xCA4E, 0x8515, 0xCA4F, 0x8516, 0xCA50,\n\t0x8517, 0xD5E1, 0x8518, 0xCA51, 0x8519, 0xCA52, 0x851A, 0xCEB5,\n\t0x851B, 0xCA53, 0x851C, 0xCA54, 0x851D, 0xCA55, 0x851E, 0xCA56,\n\t0x851F, 0xDDFD, 0x8520, 0xCA57, 0x8521, 0xB2CC, 0x8522, 0xCA58,\n\t0x8523, 0xCA59, 0x8524, 0xCA5A, 0x8525, 0xCA5B, 0x8526, 0xCA5C,\n\t0x8527, 0xCA5D, 0x8528, 0xCA5E, 0x8529, 0xCA5F, 0x852A, 0xCA60,\n\t0x852B, 0xC4E8, 0x852C, 0xCADF, 0x852D, 0xCA61, 0x852E, 0xCA62,\n\t0x852F, 0xCA63, 0x8530, 0xCA64, 0x8531, 0xCA65, 0x8532, 0xCA66,\n\t0x8533, 0xCA67, 0x8534, 0xCA68, 0x8535, 0xCA69, 0x8536, 0xCA6A,\n\t0x8537, 0xC7BE, 0x8538, 0xDDFA, 0x8539, 0xDDFC, 0x853A, 0xDDFE,\n\t0x853B, 0xDEA2, 0x853C, 0xB0AA, 0x853D, 0xB1CE, 0x853E, 0xCA6B,\n\t0x853F, 0xCA6C, 0x8540, 0xCA6D, 0x8541, 0xCA6E, 0x8542, 0xCA6F,\n\t0x8543, 0xDEAC, 0x8544, 0xCA70, 0x8545, 0xCA71, 0x8546, 0xCA72,\n\t0x8547, 0xCA73, 0x8548, 0xDEA6, 0x8549, 0xBDB6, 0x854A, 0xC8EF,\n\t0x854B, 0xCA74, 0x854C, 0xCA75, 0x854D, 0xCA76, 0x854E, 0xCA77,\n\t0x854F, 0xCA78, 0x8550, 0xCA79, 0x8551, 0xCA7A, 0x8552, 0xCA7B,\n\t0x8553, 0xCA7C, 0x8554, 0xCA7D, 0x8555, 0xCA7E, 0x8556, 0xDEA1,\n\t0x8557, 0xCA80, 0x8558, 0xCA81, 0x8559, 0xDEA5, 0x855A, 0xCA82,\n\t0x855B, 0xCA83, 0x855C, 0xCA84, 0x855D, 0xCA85, 0x855E, 0xDEA9,\n\t0x855F, 0xCA86, 0x8560, 0xCA87, 0x8561, 0xCA88, 0x8562, 0xCA89,\n\t0x8563, 0xCA8A, 0x8564, 0xDEA8, 0x8565, 0xCA8B, 0x8566, 0xCA8C,\n\t0x8567, 0xCA8D, 0x8568, 0xDEA7, 0x8569, 0xCA8E, 0x856A, 0xCA8F,\n\t0x856B, 0xCA90, 0x856C, 0xCA91, 0x856D, 0xCA92, 0x856E, 0xCA93,\n\t0x856F, 0xCA94, 0x8570, 0xCA95, 0x8571, 0xCA96, 0x8572, 0xDEAD,\n\t0x8573, 0xCA97, 0x8574, 0xD4CC, 0x8575, 0xCA98, 0x8576, 0xCA99,\n\t0x8577, 0xCA9A, 0x8578, 0xCA9B, 0x8579, 0xDEB3, 0x857A, 0xDEAA,\n\t0x857B, 0xDEAE, 0x857C, 0xCA9C, 0x857D, 0xCA9D, 0x857E, 0xC0D9,\n\t0x857F, 0xCA9E, 0x8580, 0xCA9F, 0x8581, 0xCAA0, 0x8582, 0xCB40,\n\t0x8583, 0xCB41, 0x8584, 0xB1A1, 0x8585, 0xDEB6, 0x8586, 0xCB42,\n\t0x8587, 0xDEB1, 0x8588, 0xCB43, 0x8589, 0xCB44, 0x858A, 0xCB45,\n\t0x858B, 0xCB46, 0x858C, 0xCB47, 0x858D, 0xCB48, 0x858E, 0xCB49,\n\t0x858F, 0xDEB2, 0x8590, 0xCB4A, 0x8591, 0xCB4B, 0x8592, 0xCB4C,\n\t0x8593, 0xCB4D, 0x8594, 0xCB4E, 0x8595, 0xCB4F, 0x8596, 0xCB50,\n\t0x8597, 0xCB51, 0x8598, 0xCB52, 0x8599, 0xCB53, 0x859A, 0xCB54,\n\t0x859B, 0xD1A6, 0x859C, 0xDEB5, 0x859D, 0xCB55, 0x859E, 0xCB56,\n\t0x859F, 0xCB57, 0x85A0, 0xCB58, 0x85A1, 0xCB59, 0x85A2, 0xCB5A,\n\t0x85A3, 0xCB5B, 0x85A4, 0xDEAF, 0x85A5, 0xCB5C, 0x85A6, 0xCB5D,\n\t0x85A7, 0xCB5E, 0x85A8, 0xDEB0, 0x85A9, 0xCB5F, 0x85AA, 0xD0BD,\n\t0x85AB, 0xCB60, 0x85AC, 0xCB61, 0x85AD, 0xCB62, 0x85AE, 0xDEB4,\n\t0x85AF, 0xCAED, 0x85B0, 0xDEB9, 0x85B1, 0xCB63, 0x85B2, 0xCB64,\n\t0x85B3, 0xCB65, 0x85B4, 0xCB66, 0x85B5, 0xCB67, 0x85B6, 0xCB68,\n\t0x85B7, 0xDEB8, 0x85B8, 0xCB69, 0x85B9, 0xDEB7, 0x85BA, 0xCB6A,\n\t0x85BB, 0xCB6B, 0x85BC, 0xCB6C, 0x85BD, 0xCB6D, 0x85BE, 0xCB6E,\n\t0x85BF, 0xCB6F, 0x85C0, 0xCB70, 0x85C1, 0xDEBB, 0x85C2, 0xCB71,\n\t0x85C3, 0xCB72, 0x85C4, 0xCB73, 0x85C5, 0xCB74, 0x85C6, 0xCB75,\n\t0x85C7, 0xCB76, 0x85C8, 0xCB77, 0x85C9, 0xBDE5, 0x85CA, 0xCB78,\n\t0x85CB, 0xCB79, 0x85CC, 0xCB7A, 0x85CD, 0xCB7B, 0x85CE, 0xCB7C,\n\t0x85CF, 0xB2D8, 0x85D0, 0xC3EA, 0x85D1, 0xCB7D, 0x85D2, 0xCB7E,\n\t0x85D3, 0xDEBA, 0x85D4, 0xCB80, 0x85D5, 0xC5BA, 0x85D6, 0xCB81,\n\t0x85D7, 0xCB82, 0x85D8, 0xCB83, 0x85D9, 0xCB84, 0x85DA, 0xCB85,\n\t0x85DB, 0xCB86, 0x85DC, 0xDEBC, 0x85DD, 0xCB87, 0x85DE, 0xCB88,\n\t0x85DF, 0xCB89, 0x85E0, 0xCB8A, 0x85E1, 0xCB8B, 0x85E2, 0xCB8C,\n\t0x85E3, 0xCB8D, 0x85E4, 0xCCD9, 0x85E5, 0xCB8E, 0x85E6, 0xCB8F,\n\t0x85E7, 0xCB90, 0x85E8, 0xCB91, 0x85E9, 0xB7AA, 0x85EA, 0xCB92,\n\t0x85EB, 0xCB93, 0x85EC, 0xCB94, 0x85ED, 0xCB95, 0x85EE, 0xCB96,\n\t0x85EF, 0xCB97, 0x85F0, 0xCB98, 0x85F1, 0xCB99, 0x85F2, 0xCB9A,\n\t0x85F3, 0xCB9B, 0x85F4, 0xCB9C, 0x85F5, 0xCB9D, 0x85F6, 0xCB9E,\n\t0x85F7, 0xCB9F, 0x85F8, 0xCBA0, 0x85F9, 0xCC40, 0x85FA, 0xCC41,\n\t0x85FB, 0xD4E5, 0x85FC, 0xCC42, 0x85FD, 0xCC43, 0x85FE, 0xCC44,\n\t0x85FF, 0xDEBD, 0x8600, 0xCC45, 0x8601, 0xCC46, 0x8602, 0xCC47,\n\t0x8603, 0xCC48, 0x8604, 0xCC49, 0x8605, 0xDEBF, 0x8606, 0xCC4A,\n\t0x8607, 0xCC4B, 0x8608, 0xCC4C, 0x8609, 0xCC4D, 0x860A, 0xCC4E,\n\t0x860B, 0xCC4F, 0x860C, 0xCC50, 0x860D, 0xCC51, 0x860E, 0xCC52,\n\t0x860F, 0xCC53, 0x8610, 0xCC54, 0x8611, 0xC4A2, 0x8612, 0xCC55,\n\t0x8613, 0xCC56, 0x8614, 0xCC57, 0x8615, 0xCC58, 0x8616, 0xDEC1,\n\t0x8617, 0xCC59, 0x8618, 0xCC5A, 0x8619, 0xCC5B, 0x861A, 0xCC5C,\n\t0x861B, 0xCC5D, 0x861C, 0xCC5E, 0x861D, 0xCC5F, 0x861E, 0xCC60,\n\t0x861F, 0xCC61, 0x8620, 0xCC62, 0x8621, 0xCC63, 0x8622, 0xCC64,\n\t0x8623, 0xCC65, 0x8624, 0xCC66, 0x8625, 0xCC67, 0x8626, 0xCC68,\n\t0x8627, 0xDEBE, 0x8628, 0xCC69, 0x8629, 0xDEC0, 0x862A, 0xCC6A,\n\t0x862B, 0xCC6B, 0x862C, 0xCC6C, 0x862D, 0xCC6D, 0x862E, 0xCC6E,\n\t0x862F, 0xCC6F, 0x8630, 0xCC70, 0x8631, 0xCC71, 0x8632, 0xCC72,\n\t0x8633, 0xCC73, 0x8634, 0xCC74, 0x8635, 0xCC75, 0x8636, 0xCC76,\n\t0x8637, 0xCC77, 0x8638, 0xD5BA, 0x8639, 0xCC78, 0x863A, 0xCC79,\n\t0x863B, 0xCC7A, 0x863C, 0xDEC2, 0x863D, 0xCC7B, 0x863E, 0xCC7C,\n\t0x863F, 0xCC7D, 0x8640, 0xCC7E, 0x8641, 0xCC80, 0x8642, 0xCC81,\n\t0x8643, 0xCC82, 0x8644, 0xCC83, 0x8645, 0xCC84, 0x8646, 0xCC85,\n\t0x8647, 0xCC86, 0x8648, 0xCC87, 0x8649, 0xCC88, 0x864A, 0xCC89,\n\t0x864B, 0xCC8A, 0x864C, 0xCC8B, 0x864D, 0xF2AE, 0x864E, 0xBBA2,\n\t0x864F, 0xC2B2, 0x8650, 0xC5B0, 0x8651, 0xC2C7, 0x8652, 0xCC8C,\n\t0x8653, 0xCC8D, 0x8654, 0xF2AF, 0x8655, 0xCC8E, 0x8656, 0xCC8F,\n\t0x8657, 0xCC90, 0x8658, 0xCC91, 0x8659, 0xCC92, 0x865A, 0xD0E9,\n\t0x865B, 0xCC93, 0x865C, 0xCC94, 0x865D, 0xCC95, 0x865E, 0xD3DD,\n\t0x865F, 0xCC96, 0x8660, 0xCC97, 0x8661, 0xCC98, 0x8662, 0xEBBD,\n\t0x8663, 0xCC99, 0x8664, 0xCC9A, 0x8665, 0xCC9B, 0x8666, 0xCC9C,\n\t0x8667, 0xCC9D, 0x8668, 0xCC9E, 0x8669, 0xCC9F, 0x866A, 0xCCA0,\n\t0x866B, 0xB3E6, 0x866C, 0xF2B0, 0x866D, 0xCD40, 0x866E, 0xF2B1,\n\t0x866F, 0xCD41, 0x8670, 0xCD42, 0x8671, 0xCAAD, 0x8672, 0xCD43,\n\t0x8673, 0xCD44, 0x8674, 0xCD45, 0x8675, 0xCD46, 0x8676, 0xCD47,\n\t0x8677, 0xCD48, 0x8678, 0xCD49, 0x8679, 0xBAE7, 0x867A, 0xF2B3,\n\t0x867B, 0xF2B5, 0x867C, 0xF2B4, 0x867D, 0xCBE4, 0x867E, 0xCFBA,\n\t0x867F, 0xF2B2, 0x8680, 0xCAB4, 0x8681, 0xD2CF, 0x8682, 0xC2EC,\n\t0x8683, 0xCD4A, 0x8684, 0xCD4B, 0x8685, 0xCD4C, 0x8686, 0xCD4D,\n\t0x8687, 0xCD4E, 0x8688, 0xCD4F, 0x8689, 0xCD50, 0x868A, 0xCEC3,\n\t0x868B, 0xF2B8, 0x868C, 0xB0F6, 0x868D, 0xF2B7, 0x868E, 0xCD51,\n\t0x868F, 0xCD52, 0x8690, 0xCD53, 0x8691, 0xCD54, 0x8692, 0xCD55,\n\t0x8693, 0xF2BE, 0x8694, 0xCD56, 0x8695, 0xB2CF, 0x8696, 0xCD57,\n\t0x8697, 0xCD58, 0x8698, 0xCD59, 0x8699, 0xCD5A, 0x869A, 0xCD5B,\n\t0x869B, 0xCD5C, 0x869C, 0xD1C1, 0x869D, 0xF2BA, 0x869E, 0xCD5D,\n\t0x869F, 0xCD5E, 0x86A0, 0xCD5F, 0x86A1, 0xCD60, 0x86A2, 0xCD61,\n\t0x86A3, 0xF2BC, 0x86A4, 0xD4E9, 0x86A5, 0xCD62, 0x86A6, 0xCD63,\n\t0x86A7, 0xF2BB, 0x86A8, 0xF2B6, 0x86A9, 0xF2BF, 0x86AA, 0xF2BD,\n\t0x86AB, 0xCD64, 0x86AC, 0xF2B9, 0x86AD, 0xCD65, 0x86AE, 0xCD66,\n\t0x86AF, 0xF2C7, 0x86B0, 0xF2C4, 0x86B1, 0xF2C6, 0x86B2, 0xCD67,\n\t0x86B3, 0xCD68, 0x86B4, 0xF2CA, 0x86B5, 0xF2C2, 0x86B6, 0xF2C0,\n\t0x86B7, 0xCD69, 0x86B8, 0xCD6A, 0x86B9, 0xCD6B, 0x86BA, 0xF2C5,\n\t0x86BB, 0xCD6C, 0x86BC, 0xCD6D, 0x86BD, 0xCD6E, 0x86BE, 0xCD6F,\n\t0x86BF, 0xCD70, 0x86C0, 0xD6FB, 0x86C1, 0xCD71, 0x86C2, 0xCD72,\n\t0x86C3, 0xCD73, 0x86C4, 0xF2C1, 0x86C5, 0xCD74, 0x86C6, 0xC7F9,\n\t0x86C7, 0xC9DF, 0x86C8, 0xCD75, 0x86C9, 0xF2C8, 0x86CA, 0xB9C6,\n\t0x86CB, 0xB5B0, 0x86CC, 0xCD76, 0x86CD, 0xCD77, 0x86CE, 0xF2C3,\n\t0x86CF, 0xF2C9, 0x86D0, 0xF2D0, 0x86D1, 0xF2D6, 0x86D2, 0xCD78,\n\t0x86D3, 0xCD79, 0x86D4, 0xBBD7, 0x86D5, 0xCD7A, 0x86D6, 0xCD7B,\n\t0x86D7, 0xCD7C, 0x86D8, 0xF2D5, 0x86D9, 0xCDDC, 0x86DA, 0xCD7D,\n\t0x86DB, 0xD6EB, 0x86DC, 0xCD7E, 0x86DD, 0xCD80, 0x86DE, 0xF2D2,\n\t0x86DF, 0xF2D4, 0x86E0, 0xCD81, 0x86E1, 0xCD82, 0x86E2, 0xCD83,\n\t0x86E3, 0xCD84, 0x86E4, 0xB8F2, 0x86E5, 0xCD85, 0x86E6, 0xCD86,\n\t0x86E7, 0xCD87, 0x86E8, 0xCD88, 0x86E9, 0xF2CB, 0x86EA, 0xCD89,\n\t0x86EB, 0xCD8A, 0x86EC, 0xCD8B, 0x86ED, 0xF2CE, 0x86EE, 0xC2F9,\n\t0x86EF, 0xCD8C, 0x86F0, 0xD5DD, 0x86F1, 0xF2CC, 0x86F2, 0xF2CD,\n\t0x86F3, 0xF2CF, 0x86F4, 0xF2D3, 0x86F5, 0xCD8D, 0x86F6, 0xCD8E,\n\t0x86F7, 0xCD8F, 0x86F8, 0xF2D9, 0x86F9, 0xD3BC, 0x86FA, 0xCD90,\n\t0x86FB, 0xCD91, 0x86FC, 0xCD92, 0x86FD, 0xCD93, 0x86FE, 0xB6EA,\n\t0x86FF, 0xCD94, 0x8700, 0xCAF1, 0x8701, 0xCD95, 0x8702, 0xB7E4,\n\t0x8703, 0xF2D7, 0x8704, 0xCD96, 0x8705, 0xCD97, 0x8706, 0xCD98,\n\t0x8707, 0xF2D8, 0x8708, 0xF2DA, 0x8709, 0xF2DD, 0x870A, 0xF2DB,\n\t0x870B, 0xCD99, 0x870C, 0xCD9A, 0x870D, 0xF2DC, 0x870E, 0xCD9B,\n\t0x870F, 0xCD9C, 0x8710, 0xCD9D, 0x8711, 0xCD9E, 0x8712, 0xD1D1,\n\t0x8713, 0xF2D1, 0x8714, 0xCD9F, 0x8715, 0xCDC9, 0x8716, 0xCDA0,\n\t0x8717, 0xCECF, 0x8718, 0xD6A9, 0x8719, 0xCE40, 0x871A, 0xF2E3,\n\t0x871B, 0xCE41, 0x871C, 0xC3DB, 0x871D, 0xCE42, 0x871E, 0xF2E0,\n\t0x871F, 0xCE43, 0x8720, 0xCE44, 0x8721, 0xC0AF, 0x8722, 0xF2EC,\n\t0x8723, 0xF2DE, 0x8724, 0xCE45, 0x8725, 0xF2E1, 0x8726, 0xCE46,\n\t0x8727, 0xCE47, 0x8728, 0xCE48, 0x8729, 0xF2E8, 0x872A, 0xCE49,\n\t0x872B, 0xCE4A, 0x872C, 0xCE4B, 0x872D, 0xCE4C, 0x872E, 0xF2E2,\n\t0x872F, 0xCE4D, 0x8730, 0xCE4E, 0x8731, 0xF2E7, 0x8732, 0xCE4F,\n\t0x8733, 0xCE50, 0x8734, 0xF2E6, 0x8735, 0xCE51, 0x8736, 0xCE52,\n\t0x8737, 0xF2E9, 0x8738, 0xCE53, 0x8739, 0xCE54, 0x873A, 0xCE55,\n\t0x873B, 0xF2DF, 0x873C, 0xCE56, 0x873D, 0xCE57, 0x873E, 0xF2E4,\n\t0x873F, 0xF2EA, 0x8740, 0xCE58, 0x8741, 0xCE59, 0x8742, 0xCE5A,\n\t0x8743, 0xCE5B, 0x8744, 0xCE5C, 0x8745, 0xCE5D, 0x8746, 0xCE5E,\n\t0x8747, 0xD3AC, 0x8748, 0xF2E5, 0x8749, 0xB2F5, 0x874A, 0xCE5F,\n\t0x874B, 0xCE60, 0x874C, 0xF2F2, 0x874D, 0xCE61, 0x874E, 0xD0AB,\n\t0x874F, 0xCE62, 0x8750, 0xCE63, 0x8751, 0xCE64, 0x8752, 0xCE65,\n\t0x8753, 0xF2F5, 0x8754, 0xCE66, 0x8755, 0xCE67, 0x8756, 0xCE68,\n\t0x8757, 0xBBC8, 0x8758, 0xCE69, 0x8759, 0xF2F9, 0x875A, 0xCE6A,\n\t0x875B, 0xCE6B, 0x875C, 0xCE6C, 0x875D, 0xCE6D, 0x875E, 0xCE6E,\n\t0x875F, 0xCE6F, 0x8760, 0xF2F0, 0x8761, 0xCE70, 0x8762, 0xCE71,\n\t0x8763, 0xF2F6, 0x8764, 0xF2F8, 0x8765, 0xF2FA, 0x8766, 0xCE72,\n\t0x8767, 0xCE73, 0x8768, 0xCE74, 0x8769, 0xCE75, 0x876A, 0xCE76,\n\t0x876B, 0xCE77, 0x876C, 0xCE78, 0x876D, 0xCE79, 0x876E, 0xF2F3,\n\t0x876F, 0xCE7A, 0x8770, 0xF2F1, 0x8771, 0xCE7B, 0x8772, 0xCE7C,\n\t0x8773, 0xCE7D, 0x8774, 0xBAFB, 0x8775, 0xCE7E, 0x8776, 0xB5FB,\n\t0x8777, 0xCE80, 0x8778, 0xCE81, 0x8779, 0xCE82, 0x877A, 0xCE83,\n\t0x877B, 0xF2EF, 0x877C, 0xF2F7, 0x877D, 0xF2ED, 0x877E, 0xF2EE,\n\t0x877F, 0xCE84, 0x8780, 0xCE85, 0x8781, 0xCE86, 0x8782, 0xF2EB,\n\t0x8783, 0xF3A6, 0x8784, 0xCE87, 0x8785, 0xF3A3, 0x8786, 0xCE88,\n\t0x8787, 0xCE89, 0x8788, 0xF3A2, 0x8789, 0xCE8A, 0x878A, 0xCE8B,\n\t0x878B, 0xF2F4, 0x878C, 0xCE8C, 0x878D, 0xC8DA, 0x878E, 0xCE8D,\n\t0x878F, 0xCE8E, 0x8790, 0xCE8F, 0x8791, 0xCE90, 0x8792, 0xCE91,\n\t0x8793, 0xF2FB, 0x8794, 0xCE92, 0x8795, 0xCE93, 0x8796, 0xCE94,\n\t0x8797, 0xF3A5, 0x8798, 0xCE95, 0x8799, 0xCE96, 0x879A, 0xCE97,\n\t0x879B, 0xCE98, 0x879C, 0xCE99, 0x879D, 0xCE9A, 0x879E, 0xCE9B,\n\t0x879F, 0xC3F8, 0x87A0, 0xCE9C, 0x87A1, 0xCE9D, 0x87A2, 0xCE9E,\n\t0x87A3, 0xCE9F, 0x87A4, 0xCEA0, 0x87A5, 0xCF40, 0x87A6, 0xCF41,\n\t0x87A7, 0xCF42, 0x87A8, 0xF2FD, 0x87A9, 0xCF43, 0x87AA, 0xCF44,\n\t0x87AB, 0xF3A7, 0x87AC, 0xF3A9, 0x87AD, 0xF3A4, 0x87AE, 0xCF45,\n\t0x87AF, 0xF2FC, 0x87B0, 0xCF46, 0x87B1, 0xCF47, 0x87B2, 0xCF48,\n\t0x87B3, 0xF3AB, 0x87B4, 0xCF49, 0x87B5, 0xF3AA, 0x87B6, 0xCF4A,\n\t0x87B7, 0xCF4B, 0x87B8, 0xCF4C, 0x87B9, 0xCF4D, 0x87BA, 0xC2DD,\n\t0x87BB, 0xCF4E, 0x87BC, 0xCF4F, 0x87BD, 0xF3AE, 0x87BE, 0xCF50,\n\t0x87BF, 0xCF51, 0x87C0, 0xF3B0, 0x87C1, 0xCF52, 0x87C2, 0xCF53,\n\t0x87C3, 0xCF54, 0x87C4, 0xCF55, 0x87C5, 0xCF56, 0x87C6, 0xF3A1,\n\t0x87C7, 0xCF57, 0x87C8, 0xCF58, 0x87C9, 0xCF59, 0x87CA, 0xF3B1,\n\t0x87CB, 0xF3AC, 0x87CC, 0xCF5A, 0x87CD, 0xCF5B, 0x87CE, 0xCF5C,\n\t0x87CF, 0xCF5D, 0x87D0, 0xCF5E, 0x87D1, 0xF3AF, 0x87D2, 0xF2FE,\n\t0x87D3, 0xF3AD, 0x87D4, 0xCF5F, 0x87D5, 0xCF60, 0x87D6, 0xCF61,\n\t0x87D7, 0xCF62, 0x87D8, 0xCF63, 0x87D9, 0xCF64, 0x87DA, 0xCF65,\n\t0x87DB, 0xF3B2, 0x87DC, 0xCF66, 0x87DD, 0xCF67, 0x87DE, 0xCF68,\n\t0x87DF, 0xCF69, 0x87E0, 0xF3B4, 0x87E1, 0xCF6A, 0x87E2, 0xCF6B,\n\t0x87E3, 0xCF6C, 0x87E4, 0xCF6D, 0x87E5, 0xF3A8, 0x87E6, 0xCF6E,\n\t0x87E7, 0xCF6F, 0x87E8, 0xCF70, 0x87E9, 0xCF71, 0x87EA, 0xF3B3,\n\t0x87EB, 0xCF72, 0x87EC, 0xCF73, 0x87ED, 0xCF74, 0x87EE, 0xF3B5,\n\t0x87EF, 0xCF75, 0x87F0, 0xCF76, 0x87F1, 0xCF77, 0x87F2, 0xCF78,\n\t0x87F3, 0xCF79, 0x87F4, 0xCF7A, 0x87F5, 0xCF7B, 0x87F6, 0xCF7C,\n\t0x87F7, 0xCF7D, 0x87F8, 0xCF7E, 0x87F9, 0xD0B7, 0x87FA, 0xCF80,\n\t0x87FB, 0xCF81, 0x87FC, 0xCF82, 0x87FD, 0xCF83, 0x87FE, 0xF3B8,\n\t0x87FF, 0xCF84, 0x8800, 0xCF85, 0x8801, 0xCF86, 0x8802, 0xCF87,\n\t0x8803, 0xD9F9, 0x8804, 0xCF88, 0x8805, 0xCF89, 0x8806, 0xCF8A,\n\t0x8807, 0xCF8B, 0x8808, 0xCF8C, 0x8809, 0xCF8D, 0x880A, 0xF3B9,\n\t0x880B, 0xCF8E, 0x880C, 0xCF8F, 0x880D, 0xCF90, 0x880E, 0xCF91,\n\t0x880F, 0xCF92, 0x8810, 0xCF93, 0x8811, 0xCF94, 0x8812, 0xCF95,\n\t0x8813, 0xF3B7, 0x8814, 0xCF96, 0x8815, 0xC8E4, 0x8816, 0xF3B6,\n\t0x8817, 0xCF97, 0x8818, 0xCF98, 0x8819, 0xCF99, 0x881A, 0xCF9A,\n\t0x881B, 0xF3BA, 0x881C, 0xCF9B, 0x881D, 0xCF9C, 0x881E, 0xCF9D,\n\t0x881F, 0xCF9E, 0x8820, 0xCF9F, 0x8821, 0xF3BB, 0x8822, 0xB4C0,\n\t0x8823, 0xCFA0, 0x8824, 0xD040, 0x8825, 0xD041, 0x8826, 0xD042,\n\t0x8827, 0xD043, 0x8828, 0xD044, 0x8829, 0xD045, 0x882A, 0xD046,\n\t0x882B, 0xD047, 0x882C, 0xD048, 0x882D, 0xD049, 0x882E, 0xD04A,\n\t0x882F, 0xD04B, 0x8830, 0xD04C, 0x8831, 0xD04D, 0x8832, 0xEEC3,\n\t0x8833, 0xD04E, 0x8834, 0xD04F, 0x8835, 0xD050, 0x8836, 0xD051,\n\t0x8837, 0xD052, 0x8838, 0xD053, 0x8839, 0xF3BC, 0x883A, 0xD054,\n\t0x883B, 0xD055, 0x883C, 0xF3BD, 0x883D, 0xD056, 0x883E, 0xD057,\n\t0x883F, 0xD058, 0x8840, 0xD1AA, 0x8841, 0xD059, 0x8842, 0xD05A,\n\t0x8843, 0xD05B, 0x8844, 0xF4AC, 0x8845, 0xD0C6, 0x8846, 0xD05C,\n\t0x8847, 0xD05D, 0x8848, 0xD05E, 0x8849, 0xD05F, 0x884A, 0xD060,\n\t0x884B, 0xD061, 0x884C, 0xD0D0, 0x884D, 0xD1DC, 0x884E, 0xD062,\n\t0x884F, 0xD063, 0x8850, 0xD064, 0x8851, 0xD065, 0x8852, 0xD066,\n\t0x8853, 0xD067, 0x8854, 0xCFCE, 0x8855, 0xD068, 0x8856, 0xD069,\n\t0x8857, 0xBDD6, 0x8858, 0xD06A, 0x8859, 0xD1C3, 0x885A, 0xD06B,\n\t0x885B, 0xD06C, 0x885C, 0xD06D, 0x885D, 0xD06E, 0x885E, 0xD06F,\n\t0x885F, 0xD070, 0x8860, 0xD071, 0x8861, 0xBAE2, 0x8862, 0xE1E9,\n\t0x8863, 0xD2C2, 0x8864, 0xF1C2, 0x8865, 0xB2B9, 0x8866, 0xD072,\n\t0x8867, 0xD073, 0x8868, 0xB1ED, 0x8869, 0xF1C3, 0x886A, 0xD074,\n\t0x886B, 0xC9C0, 0x886C, 0xB3C4, 0x886D, 0xD075, 0x886E, 0xD9F2,\n\t0x886F, 0xD076, 0x8870, 0xCBA5, 0x8871, 0xD077, 0x8872, 0xF1C4,\n\t0x8873, 0xD078, 0x8874, 0xD079, 0x8875, 0xD07A, 0x8876, 0xD07B,\n\t0x8877, 0xD6D4, 0x8878, 0xD07C, 0x8879, 0xD07D, 0x887A, 0xD07E,\n\t0x887B, 0xD080, 0x887C, 0xD081, 0x887D, 0xF1C5, 0x887E, 0xF4C0,\n\t0x887F, 0xF1C6, 0x8880, 0xD082, 0x8881, 0xD4AC, 0x8882, 0xF1C7,\n\t0x8883, 0xD083, 0x8884, 0xB0C0, 0x8885, 0xF4C1, 0x8886, 0xD084,\n\t0x8887, 0xD085, 0x8888, 0xF4C2, 0x8889, 0xD086, 0x888A, 0xD087,\n\t0x888B, 0xB4FC, 0x888C, 0xD088, 0x888D, 0xC5DB, 0x888E, 0xD089,\n\t0x888F, 0xD08A, 0x8890, 0xD08B, 0x8891, 0xD08C, 0x8892, 0xCCBB,\n\t0x8893, 0xD08D, 0x8894, 0xD08E, 0x8895, 0xD08F, 0x8896, 0xD0E4,\n\t0x8897, 0xD090, 0x8898, 0xD091, 0x8899, 0xD092, 0x889A, 0xD093,\n\t0x889B, 0xD094, 0x889C, 0xCDE0, 0x889D, 0xD095, 0x889E, 0xD096,\n\t0x889F, 0xD097, 0x88A0, 0xD098, 0x88A1, 0xD099, 0x88A2, 0xF1C8,\n\t0x88A3, 0xD09A, 0x88A4, 0xD9F3, 0x88A5, 0xD09B, 0x88A6, 0xD09C,\n\t0x88A7, 0xD09D, 0x88A8, 0xD09E, 0x88A9, 0xD09F, 0x88AA, 0xD0A0,\n\t0x88AB, 0xB1BB, 0x88AC, 0xD140, 0x88AD, 0xCFAE, 0x88AE, 0xD141,\n\t0x88AF, 0xD142, 0x88B0, 0xD143, 0x88B1, 0xB8A4, 0x88B2, 0xD144,\n\t0x88B3, 0xD145, 0x88B4, 0xD146, 0x88B5, 0xD147, 0x88B6, 0xD148,\n\t0x88B7, 0xF1CA, 0x88B8, 0xD149, 0x88B9, 0xD14A, 0x88BA, 0xD14B,\n\t0x88BB, 0xD14C, 0x88BC, 0xF1CB, 0x88BD, 0xD14D, 0x88BE, 0xD14E,\n\t0x88BF, 0xD14F, 0x88C0, 0xD150, 0x88C1, 0xB2C3, 0x88C2, 0xC1D1,\n\t0x88C3, 0xD151, 0x88C4, 0xD152, 0x88C5, 0xD7B0, 0x88C6, 0xF1C9,\n\t0x88C7, 0xD153, 0x88C8, 0xD154, 0x88C9, 0xF1CC, 0x88CA, 0xD155,\n\t0x88CB, 0xD156, 0x88CC, 0xD157, 0x88CD, 0xD158, 0x88CE, 0xF1CE,\n\t0x88CF, 0xD159, 0x88D0, 0xD15A, 0x88D1, 0xD15B, 0x88D2, 0xD9F6,\n\t0x88D3, 0xD15C, 0x88D4, 0xD2E1, 0x88D5, 0xD4A3, 0x88D6, 0xD15D,\n\t0x88D7, 0xD15E, 0x88D8, 0xF4C3, 0x88D9, 0xC8B9, 0x88DA, 0xD15F,\n\t0x88DB, 0xD160, 0x88DC, 0xD161, 0x88DD, 0xD162, 0x88DE, 0xD163,\n\t0x88DF, 0xF4C4, 0x88E0, 0xD164, 0x88E1, 0xD165, 0x88E2, 0xF1CD,\n\t0x88E3, 0xF1CF, 0x88E4, 0xBFE3, 0x88E5, 0xF1D0, 0x88E6, 0xD166,\n\t0x88E7, 0xD167, 0x88E8, 0xF1D4, 0x88E9, 0xD168, 0x88EA, 0xD169,\n\t0x88EB, 0xD16A, 0x88EC, 0xD16B, 0x88ED, 0xD16C, 0x88EE, 0xD16D,\n\t0x88EF, 0xD16E, 0x88F0, 0xF1D6, 0x88F1, 0xF1D1, 0x88F2, 0xD16F,\n\t0x88F3, 0xC9D1, 0x88F4, 0xC5E1, 0x88F5, 0xD170, 0x88F6, 0xD171,\n\t0x88F7, 0xD172, 0x88F8, 0xC2E3, 0x88F9, 0xB9FC, 0x88FA, 0xD173,\n\t0x88FB, 0xD174, 0x88FC, 0xF1D3, 0x88FD, 0xD175, 0x88FE, 0xF1D5,\n\t0x88FF, 0xD176, 0x8900, 0xD177, 0x8901, 0xD178, 0x8902, 0xB9D3,\n\t0x8903, 0xD179, 0x8904, 0xD17A, 0x8905, 0xD17B, 0x8906, 0xD17C,\n\t0x8907, 0xD17D, 0x8908, 0xD17E, 0x8909, 0xD180, 0x890A, 0xF1DB,\n\t0x890B, 0xD181, 0x890C, 0xD182, 0x890D, 0xD183, 0x890E, 0xD184,\n\t0x890F, 0xD185, 0x8910, 0xBAD6, 0x8911, 0xD186, 0x8912, 0xB0FD,\n\t0x8913, 0xF1D9, 0x8914, 0xD187, 0x8915, 0xD188, 0x8916, 0xD189,\n\t0x8917, 0xD18A, 0x8918, 0xD18B, 0x8919, 0xF1D8, 0x891A, 0xF1D2,\n\t0x891B, 0xF1DA, 0x891C, 0xD18C, 0x891D, 0xD18D, 0x891E, 0xD18E,\n\t0x891F, 0xD18F, 0x8920, 0xD190, 0x8921, 0xF1D7, 0x8922, 0xD191,\n\t0x8923, 0xD192, 0x8924, 0xD193, 0x8925, 0xC8EC, 0x8926, 0xD194,\n\t0x8927, 0xD195, 0x8928, 0xD196, 0x8929, 0xD197, 0x892A, 0xCDCA,\n\t0x892B, 0xF1DD, 0x892C, 0xD198, 0x892D, 0xD199, 0x892E, 0xD19A,\n\t0x892F, 0xD19B, 0x8930, 0xE5BD, 0x8931, 0xD19C, 0x8932, 0xD19D,\n\t0x8933, 0xD19E, 0x8934, 0xF1DC, 0x8935, 0xD19F, 0x8936, 0xF1DE,\n\t0x8937, 0xD1A0, 0x8938, 0xD240, 0x8939, 0xD241, 0x893A, 0xD242,\n\t0x893B, 0xD243, 0x893C, 0xD244, 0x893D, 0xD245, 0x893E, 0xD246,\n\t0x893F, 0xD247, 0x8940, 0xD248, 0x8941, 0xF1DF, 0x8942, 0xD249,\n\t0x8943, 0xD24A, 0x8944, 0xCFE5, 0x8945, 0xD24B, 0x8946, 0xD24C,\n\t0x8947, 0xD24D, 0x8948, 0xD24E, 0x8949, 0xD24F, 0x894A, 0xD250,\n\t0x894B, 0xD251, 0x894C, 0xD252, 0x894D, 0xD253, 0x894E, 0xD254,\n\t0x894F, 0xD255, 0x8950, 0xD256, 0x8951, 0xD257, 0x8952, 0xD258,\n\t0x8953, 0xD259, 0x8954, 0xD25A, 0x8955, 0xD25B, 0x8956, 0xD25C,\n\t0x8957, 0xD25D, 0x8958, 0xD25E, 0x8959, 0xD25F, 0x895A, 0xD260,\n\t0x895B, 0xD261, 0x895C, 0xD262, 0x895D, 0xD263, 0x895E, 0xF4C5,\n\t0x895F, 0xBDF3, 0x8960, 0xD264, 0x8961, 0xD265, 0x8962, 0xD266,\n\t0x8963, 0xD267, 0x8964, 0xD268, 0x8965, 0xD269, 0x8966, 0xF1E0,\n\t0x8967, 0xD26A, 0x8968, 0xD26B, 0x8969, 0xD26C, 0x896A, 0xD26D,\n\t0x896B, 0xD26E, 0x896C, 0xD26F, 0x896D, 0xD270, 0x896E, 0xD271,\n\t0x896F, 0xD272, 0x8970, 0xD273, 0x8971, 0xD274, 0x8972, 0xD275,\n\t0x8973, 0xD276, 0x8974, 0xD277, 0x8975, 0xD278, 0x8976, 0xD279,\n\t0x8977, 0xD27A, 0x8978, 0xD27B, 0x8979, 0xD27C, 0x897A, 0xD27D,\n\t0x897B, 0xF1E1, 0x897C, 0xD27E, 0x897D, 0xD280, 0x897E, 0xD281,\n\t0x897F, 0xCEF7, 0x8980, 0xD282, 0x8981, 0xD2AA, 0x8982, 0xD283,\n\t0x8983, 0xF1FB, 0x8984, 0xD284, 0x8985, 0xD285, 0x8986, 0xB8B2,\n\t0x8987, 0xD286, 0x8988, 0xD287, 0x8989, 0xD288, 0x898A, 0xD289,\n\t0x898B, 0xD28A, 0x898C, 0xD28B, 0x898D, 0xD28C, 0x898E, 0xD28D,\n\t0x898F, 0xD28E, 0x8990, 0xD28F, 0x8991, 0xD290, 0x8992, 0xD291,\n\t0x8993, 0xD292, 0x8994, 0xD293, 0x8995, 0xD294, 0x8996, 0xD295,\n\t0x8997, 0xD296, 0x8998, 0xD297, 0x8999, 0xD298, 0x899A, 0xD299,\n\t0x899B, 0xD29A, 0x899C, 0xD29B, 0x899D, 0xD29C, 0x899E, 0xD29D,\n\t0x899F, 0xD29E, 0x89A0, 0xD29F, 0x89A1, 0xD2A0, 0x89A2, 0xD340,\n\t0x89A3, 0xD341, 0x89A4, 0xD342, 0x89A5, 0xD343, 0x89A6, 0xD344,\n\t0x89A7, 0xD345, 0x89A8, 0xD346, 0x89A9, 0xD347, 0x89AA, 0xD348,\n\t0x89AB, 0xD349, 0x89AC, 0xD34A, 0x89AD, 0xD34B, 0x89AE, 0xD34C,\n\t0x89AF, 0xD34D, 0x89B0, 0xD34E, 0x89B1, 0xD34F, 0x89B2, 0xD350,\n\t0x89B3, 0xD351, 0x89B4, 0xD352, 0x89B5, 0xD353, 0x89B6, 0xD354,\n\t0x89B7, 0xD355, 0x89B8, 0xD356, 0x89B9, 0xD357, 0x89BA, 0xD358,\n\t0x89BB, 0xD359, 0x89BC, 0xD35A, 0x89BD, 0xD35B, 0x89BE, 0xD35C,\n\t0x89BF, 0xD35D, 0x89C0, 0xD35E, 0x89C1, 0xBCFB, 0x89C2, 0xB9DB,\n\t0x89C3, 0xD35F, 0x89C4, 0xB9E6, 0x89C5, 0xC3D9, 0x89C6, 0xCAD3,\n\t0x89C7, 0xEAE8, 0x89C8, 0xC0C0, 0x89C9, 0xBEF5, 0x89CA, 0xEAE9,\n\t0x89CB, 0xEAEA, 0x89CC, 0xEAEB, 0x89CD, 0xD360, 0x89CE, 0xEAEC,\n\t0x89CF, 0xEAED, 0x89D0, 0xEAEE, 0x89D1, 0xEAEF, 0x89D2, 0xBDC7,\n\t0x89D3, 0xD361, 0x89D4, 0xD362, 0x89D5, 0xD363, 0x89D6, 0xF5FB,\n\t0x89D7, 0xD364, 0x89D8, 0xD365, 0x89D9, 0xD366, 0x89DA, 0xF5FD,\n\t0x89DB, 0xD367, 0x89DC, 0xF5FE, 0x89DD, 0xD368, 0x89DE, 0xF5FC,\n\t0x89DF, 0xD369, 0x89E0, 0xD36A, 0x89E1, 0xD36B, 0x89E2, 0xD36C,\n\t0x89E3, 0xBDE2, 0x89E4, 0xD36D, 0x89E5, 0xF6A1, 0x89E6, 0xB4A5,\n\t0x89E7, 0xD36E, 0x89E8, 0xD36F, 0x89E9, 0xD370, 0x89EA, 0xD371,\n\t0x89EB, 0xF6A2, 0x89EC, 0xD372, 0x89ED, 0xD373, 0x89EE, 0xD374,\n\t0x89EF, 0xF6A3, 0x89F0, 0xD375, 0x89F1, 0xD376, 0x89F2, 0xD377,\n\t0x89F3, 0xECB2, 0x89F4, 0xD378, 0x89F5, 0xD379, 0x89F6, 0xD37A,\n\t0x89F7, 0xD37B, 0x89F8, 0xD37C, 0x89F9, 0xD37D, 0x89FA, 0xD37E,\n\t0x89FB, 0xD380, 0x89FC, 0xD381, 0x89FD, 0xD382, 0x89FE, 0xD383,\n\t0x89FF, 0xD384, 0x8A00, 0xD1D4, 0x8A01, 0xD385, 0x8A02, 0xD386,\n\t0x8A03, 0xD387, 0x8A04, 0xD388, 0x8A05, 0xD389, 0x8A06, 0xD38A,\n\t0x8A07, 0xD9EA, 0x8A08, 0xD38B, 0x8A09, 0xD38C, 0x8A0A, 0xD38D,\n\t0x8A0B, 0xD38E, 0x8A0C, 0xD38F, 0x8A0D, 0xD390, 0x8A0E, 0xD391,\n\t0x8A0F, 0xD392, 0x8A10, 0xD393, 0x8A11, 0xD394, 0x8A12, 0xD395,\n\t0x8A13, 0xD396, 0x8A14, 0xD397, 0x8A15, 0xD398, 0x8A16, 0xD399,\n\t0x8A17, 0xD39A, 0x8A18, 0xD39B, 0x8A19, 0xD39C, 0x8A1A, 0xD39D,\n\t0x8A1B, 0xD39E, 0x8A1C, 0xD39F, 0x8A1D, 0xD3A0, 0x8A1E, 0xD440,\n\t0x8A1F, 0xD441, 0x8A20, 0xD442, 0x8A21, 0xD443, 0x8A22, 0xD444,\n\t0x8A23, 0xD445, 0x8A24, 0xD446, 0x8A25, 0xD447, 0x8A26, 0xD448,\n\t0x8A27, 0xD449, 0x8A28, 0xD44A, 0x8A29, 0xD44B, 0x8A2A, 0xD44C,\n\t0x8A2B, 0xD44D, 0x8A2C, 0xD44E, 0x8A2D, 0xD44F, 0x8A2E, 0xD450,\n\t0x8A2F, 0xD451, 0x8A30, 0xD452, 0x8A31, 0xD453, 0x8A32, 0xD454,\n\t0x8A33, 0xD455, 0x8A34, 0xD456, 0x8A35, 0xD457, 0x8A36, 0xD458,\n\t0x8A37, 0xD459, 0x8A38, 0xD45A, 0x8A39, 0xD45B, 0x8A3A, 0xD45C,\n\t0x8A3B, 0xD45D, 0x8A3C, 0xD45E, 0x8A3D, 0xD45F, 0x8A3E, 0xF6A4,\n\t0x8A3F, 0xD460, 0x8A40, 0xD461, 0x8A41, 0xD462, 0x8A42, 0xD463,\n\t0x8A43, 0xD464, 0x8A44, 0xD465, 0x8A45, 0xD466, 0x8A46, 0xD467,\n\t0x8A47, 0xD468, 0x8A48, 0xEEBA, 0x8A49, 0xD469, 0x8A4A, 0xD46A,\n\t0x8A4B, 0xD46B, 0x8A4C, 0xD46C, 0x8A4D, 0xD46D, 0x8A4E, 0xD46E,\n\t0x8A4F, 0xD46F, 0x8A50, 0xD470, 0x8A51, 0xD471, 0x8A52, 0xD472,\n\t0x8A53, 0xD473, 0x8A54, 0xD474, 0x8A55, 0xD475, 0x8A56, 0xD476,\n\t0x8A57, 0xD477, 0x8A58, 0xD478, 0x8A59, 0xD479, 0x8A5A, 0xD47A,\n\t0x8A5B, 0xD47B, 0x8A5C, 0xD47C, 0x8A5D, 0xD47D, 0x8A5E, 0xD47E,\n\t0x8A5F, 0xD480, 0x8A60, 0xD481, 0x8A61, 0xD482, 0x8A62, 0xD483,\n\t0x8A63, 0xD484, 0x8A64, 0xD485, 0x8A65, 0xD486, 0x8A66, 0xD487,\n\t0x8A67, 0xD488, 0x8A68, 0xD489, 0x8A69, 0xD48A, 0x8A6A, 0xD48B,\n\t0x8A6B, 0xD48C, 0x8A6C, 0xD48D, 0x8A6D, 0xD48E, 0x8A6E, 0xD48F,\n\t0x8A6F, 0xD490, 0x8A70, 0xD491, 0x8A71, 0xD492, 0x8A72, 0xD493,\n\t0x8A73, 0xD494, 0x8A74, 0xD495, 0x8A75, 0xD496, 0x8A76, 0xD497,\n\t0x8A77, 0xD498, 0x8A78, 0xD499, 0x8A79, 0xD5B2, 0x8A7A, 0xD49A,\n\t0x8A7B, 0xD49B, 0x8A7C, 0xD49C, 0x8A7D, 0xD49D, 0x8A7E, 0xD49E,\n\t0x8A7F, 0xD49F, 0x8A80, 0xD4A0, 0x8A81, 0xD540, 0x8A82, 0xD541,\n\t0x8A83, 0xD542, 0x8A84, 0xD543, 0x8A85, 0xD544, 0x8A86, 0xD545,\n\t0x8A87, 0xD546, 0x8A88, 0xD547, 0x8A89, 0xD3FE, 0x8A8A, 0xCCDC,\n\t0x8A8B, 0xD548, 0x8A8C, 0xD549, 0x8A8D, 0xD54A, 0x8A8E, 0xD54B,\n\t0x8A8F, 0xD54C, 0x8A90, 0xD54D, 0x8A91, 0xD54E, 0x8A92, 0xD54F,\n\t0x8A93, 0xCAC4, 0x8A94, 0xD550, 0x8A95, 0xD551, 0x8A96, 0xD552,\n\t0x8A97, 0xD553, 0x8A98, 0xD554, 0x8A99, 0xD555, 0x8A9A, 0xD556,\n\t0x8A9B, 0xD557, 0x8A9C, 0xD558, 0x8A9D, 0xD559, 0x8A9E, 0xD55A,\n\t0x8A9F, 0xD55B, 0x8AA0, 0xD55C, 0x8AA1, 0xD55D, 0x8AA2, 0xD55E,\n\t0x8AA3, 0xD55F, 0x8AA4, 0xD560, 0x8AA5, 0xD561, 0x8AA6, 0xD562,\n\t0x8AA7, 0xD563, 0x8AA8, 0xD564, 0x8AA9, 0xD565, 0x8AAA, 0xD566,\n\t0x8AAB, 0xD567, 0x8AAC, 0xD568, 0x8AAD, 0xD569, 0x8AAE, 0xD56A,\n\t0x8AAF, 0xD56B, 0x8AB0, 0xD56C, 0x8AB1, 0xD56D, 0x8AB2, 0xD56E,\n\t0x8AB3, 0xD56F, 0x8AB4, 0xD570, 0x8AB5, 0xD571, 0x8AB6, 0xD572,\n\t0x8AB7, 0xD573, 0x8AB8, 0xD574, 0x8AB9, 0xD575, 0x8ABA, 0xD576,\n\t0x8ABB, 0xD577, 0x8ABC, 0xD578, 0x8ABD, 0xD579, 0x8ABE, 0xD57A,\n\t0x8ABF, 0xD57B, 0x8AC0, 0xD57C, 0x8AC1, 0xD57D, 0x8AC2, 0xD57E,\n\t0x8AC3, 0xD580, 0x8AC4, 0xD581, 0x8AC5, 0xD582, 0x8AC6, 0xD583,\n\t0x8AC7, 0xD584, 0x8AC8, 0xD585, 0x8AC9, 0xD586, 0x8ACA, 0xD587,\n\t0x8ACB, 0xD588, 0x8ACC, 0xD589, 0x8ACD, 0xD58A, 0x8ACE, 0xD58B,\n\t0x8ACF, 0xD58C, 0x8AD0, 0xD58D, 0x8AD1, 0xD58E, 0x8AD2, 0xD58F,\n\t0x8AD3, 0xD590, 0x8AD4, 0xD591, 0x8AD5, 0xD592, 0x8AD6, 0xD593,\n\t0x8AD7, 0xD594, 0x8AD8, 0xD595, 0x8AD9, 0xD596, 0x8ADA, 0xD597,\n\t0x8ADB, 0xD598, 0x8ADC, 0xD599, 0x8ADD, 0xD59A, 0x8ADE, 0xD59B,\n\t0x8ADF, 0xD59C, 0x8AE0, 0xD59D, 0x8AE1, 0xD59E, 0x8AE2, 0xD59F,\n\t0x8AE3, 0xD5A0, 0x8AE4, 0xD640, 0x8AE5, 0xD641, 0x8AE6, 0xD642,\n\t0x8AE7, 0xD643, 0x8AE8, 0xD644, 0x8AE9, 0xD645, 0x8AEA, 0xD646,\n\t0x8AEB, 0xD647, 0x8AEC, 0xD648, 0x8AED, 0xD649, 0x8AEE, 0xD64A,\n\t0x8AEF, 0xD64B, 0x8AF0, 0xD64C, 0x8AF1, 0xD64D, 0x8AF2, 0xD64E,\n\t0x8AF3, 0xD64F, 0x8AF4, 0xD650, 0x8AF5, 0xD651, 0x8AF6, 0xD652,\n\t0x8AF7, 0xD653, 0x8AF8, 0xD654, 0x8AF9, 0xD655, 0x8AFA, 0xD656,\n\t0x8AFB, 0xD657, 0x8AFC, 0xD658, 0x8AFD, 0xD659, 0x8AFE, 0xD65A,\n\t0x8AFF, 0xD65B, 0x8B00, 0xD65C, 0x8B01, 0xD65D, 0x8B02, 0xD65E,\n\t0x8B03, 0xD65F, 0x8B04, 0xD660, 0x8B05, 0xD661, 0x8B06, 0xD662,\n\t0x8B07, 0xE5C0, 0x8B08, 0xD663, 0x8B09, 0xD664, 0x8B0A, 0xD665,\n\t0x8B0B, 0xD666, 0x8B0C, 0xD667, 0x8B0D, 0xD668, 0x8B0E, 0xD669,\n\t0x8B0F, 0xD66A, 0x8B10, 0xD66B, 0x8B11, 0xD66C, 0x8B12, 0xD66D,\n\t0x8B13, 0xD66E, 0x8B14, 0xD66F, 0x8B15, 0xD670, 0x8B16, 0xD671,\n\t0x8B17, 0xD672, 0x8B18, 0xD673, 0x8B19, 0xD674, 0x8B1A, 0xD675,\n\t0x8B1B, 0xD676, 0x8B1C, 0xD677, 0x8B1D, 0xD678, 0x8B1E, 0xD679,\n\t0x8B1F, 0xD67A, 0x8B20, 0xD67B, 0x8B21, 0xD67C, 0x8B22, 0xD67D,\n\t0x8B23, 0xD67E, 0x8B24, 0xD680, 0x8B25, 0xD681, 0x8B26, 0xF6A5,\n\t0x8B27, 0xD682, 0x8B28, 0xD683, 0x8B29, 0xD684, 0x8B2A, 0xD685,\n\t0x8B2B, 0xD686, 0x8B2C, 0xD687, 0x8B2D, 0xD688, 0x8B2E, 0xD689,\n\t0x8B2F, 0xD68A, 0x8B30, 0xD68B, 0x8B31, 0xD68C, 0x8B32, 0xD68D,\n\t0x8B33, 0xD68E, 0x8B34, 0xD68F, 0x8B35, 0xD690, 0x8B36, 0xD691,\n\t0x8B37, 0xD692, 0x8B38, 0xD693, 0x8B39, 0xD694, 0x8B3A, 0xD695,\n\t0x8B3B, 0xD696, 0x8B3C, 0xD697, 0x8B3D, 0xD698, 0x8B3E, 0xD699,\n\t0x8B3F, 0xD69A, 0x8B40, 0xD69B, 0x8B41, 0xD69C, 0x8B42, 0xD69D,\n\t0x8B43, 0xD69E, 0x8B44, 0xD69F, 0x8B45, 0xD6A0, 0x8B46, 0xD740,\n\t0x8B47, 0xD741, 0x8B48, 0xD742, 0x8B49, 0xD743, 0x8B4A, 0xD744,\n\t0x8B4B, 0xD745, 0x8B4C, 0xD746, 0x8B4D, 0xD747, 0x8B4E, 0xD748,\n\t0x8B4F, 0xD749, 0x8B50, 0xD74A, 0x8B51, 0xD74B, 0x8B52, 0xD74C,\n\t0x8B53, 0xD74D, 0x8B54, 0xD74E, 0x8B55, 0xD74F, 0x8B56, 0xD750,\n\t0x8B57, 0xD751, 0x8B58, 0xD752, 0x8B59, 0xD753, 0x8B5A, 0xD754,\n\t0x8B5B, 0xD755, 0x8B5C, 0xD756, 0x8B5D, 0xD757, 0x8B5E, 0xD758,\n\t0x8B5F, 0xD759, 0x8B60, 0xD75A, 0x8B61, 0xD75B, 0x8B62, 0xD75C,\n\t0x8B63, 0xD75D, 0x8B64, 0xD75E, 0x8B65, 0xD75F, 0x8B66, 0xBEAF,\n\t0x8B67, 0xD760, 0x8B68, 0xD761, 0x8B69, 0xD762, 0x8B6A, 0xD763,\n\t0x8B6B, 0xD764, 0x8B6C, 0xC6A9, 0x8B6D, 0xD765, 0x8B6E, 0xD766,\n\t0x8B6F, 0xD767, 0x8B70, 0xD768, 0x8B71, 0xD769, 0x8B72, 0xD76A,\n\t0x8B73, 0xD76B, 0x8B74, 0xD76C, 0x8B75, 0xD76D, 0x8B76, 0xD76E,\n\t0x8B77, 0xD76F, 0x8B78, 0xD770, 0x8B79, 0xD771, 0x8B7A, 0xD772,\n\t0x8B7B, 0xD773, 0x8B7C, 0xD774, 0x8B7D, 0xD775, 0x8B7E, 0xD776,\n\t0x8B7F, 0xD777, 0x8B80, 0xD778, 0x8B81, 0xD779, 0x8B82, 0xD77A,\n\t0x8B83, 0xD77B, 0x8B84, 0xD77C, 0x8B85, 0xD77D, 0x8B86, 0xD77E,\n\t0x8B87, 0xD780, 0x8B88, 0xD781, 0x8B89, 0xD782, 0x8B8A, 0xD783,\n\t0x8B8B, 0xD784, 0x8B8C, 0xD785, 0x8B8D, 0xD786, 0x8B8E, 0xD787,\n\t0x8B8F, 0xD788, 0x8B90, 0xD789, 0x8B91, 0xD78A, 0x8B92, 0xD78B,\n\t0x8B93, 0xD78C, 0x8B94, 0xD78D, 0x8B95, 0xD78E, 0x8B96, 0xD78F,\n\t0x8B97, 0xD790, 0x8B98, 0xD791, 0x8B99, 0xD792, 0x8B9A, 0xD793,\n\t0x8B9B, 0xD794, 0x8B9C, 0xD795, 0x8B9D, 0xD796, 0x8B9E, 0xD797,\n\t0x8B9F, 0xD798, 0x8BA0, 0xDAA5, 0x8BA1, 0xBCC6, 0x8BA2, 0xB6A9,\n\t0x8BA3, 0xB8BC, 0x8BA4, 0xC8CF, 0x8BA5, 0xBCA5, 0x8BA6, 0xDAA6,\n\t0x8BA7, 0xDAA7, 0x8BA8, 0xCCD6, 0x8BA9, 0xC8C3, 0x8BAA, 0xDAA8,\n\t0x8BAB, 0xC6FD, 0x8BAC, 0xD799, 0x8BAD, 0xD1B5, 0x8BAE, 0xD2E9,\n\t0x8BAF, 0xD1B6, 0x8BB0, 0xBCC7, 0x8BB1, 0xD79A, 0x8BB2, 0xBDB2,\n\t0x8BB3, 0xBBE4, 0x8BB4, 0xDAA9, 0x8BB5, 0xDAAA, 0x8BB6, 0xD1C8,\n\t0x8BB7, 0xDAAB, 0x8BB8, 0xD0ED, 0x8BB9, 0xB6EF, 0x8BBA, 0xC2DB,\n\t0x8BBB, 0xD79B, 0x8BBC, 0xCBCF, 0x8BBD, 0xB7ED, 0x8BBE, 0xC9E8,\n\t0x8BBF, 0xB7C3, 0x8BC0, 0xBEF7, 0x8BC1, 0xD6A4, 0x8BC2, 0xDAAC,\n\t0x8BC3, 0xDAAD, 0x8BC4, 0xC6C0, 0x8BC5, 0xD7E7, 0x8BC6, 0xCAB6,\n\t0x8BC7, 0xD79C, 0x8BC8, 0xD5A9, 0x8BC9, 0xCBDF, 0x8BCA, 0xD5EF,\n\t0x8BCB, 0xDAAE, 0x8BCC, 0xD6DF, 0x8BCD, 0xB4CA, 0x8BCE, 0xDAB0,\n\t0x8BCF, 0xDAAF, 0x8BD0, 0xD79D, 0x8BD1, 0xD2EB, 0x8BD2, 0xDAB1,\n\t0x8BD3, 0xDAB2, 0x8BD4, 0xDAB3, 0x8BD5, 0xCAD4, 0x8BD6, 0xDAB4,\n\t0x8BD7, 0xCAAB, 0x8BD8, 0xDAB5, 0x8BD9, 0xDAB6, 0x8BDA, 0xB3CF,\n\t0x8BDB, 0xD6EF, 0x8BDC, 0xDAB7, 0x8BDD, 0xBBB0, 0x8BDE, 0xB5AE,\n\t0x8BDF, 0xDAB8, 0x8BE0, 0xDAB9, 0x8BE1, 0xB9EE, 0x8BE2, 0xD1AF,\n\t0x8BE3, 0xD2E8, 0x8BE4, 0xDABA, 0x8BE5, 0xB8C3, 0x8BE6, 0xCFEA,\n\t0x8BE7, 0xB2EF, 0x8BE8, 0xDABB, 0x8BE9, 0xDABC, 0x8BEA, 0xD79E,\n\t0x8BEB, 0xBDEB, 0x8BEC, 0xCEDC, 0x8BED, 0xD3EF, 0x8BEE, 0xDABD,\n\t0x8BEF, 0xCEF3, 0x8BF0, 0xDABE, 0x8BF1, 0xD3D5, 0x8BF2, 0xBBE5,\n\t0x8BF3, 0xDABF, 0x8BF4, 0xCBB5, 0x8BF5, 0xCBD0, 0x8BF6, 0xDAC0,\n\t0x8BF7, 0xC7EB, 0x8BF8, 0xD6EE, 0x8BF9, 0xDAC1, 0x8BFA, 0xC5B5,\n\t0x8BFB, 0xB6C1, 0x8BFC, 0xDAC2, 0x8BFD, 0xB7CC, 0x8BFE, 0xBFCE,\n\t0x8BFF, 0xDAC3, 0x8C00, 0xDAC4, 0x8C01, 0xCBAD, 0x8C02, 0xDAC5,\n\t0x8C03, 0xB5F7, 0x8C04, 0xDAC6, 0x8C05, 0xC1C2, 0x8C06, 0xD7BB,\n\t0x8C07, 0xDAC7, 0x8C08, 0xCCB8, 0x8C09, 0xD79F, 0x8C0A, 0xD2EA,\n\t0x8C0B, 0xC4B1, 0x8C0C, 0xDAC8, 0x8C0D, 0xB5FD, 0x8C0E, 0xBBD1,\n\t0x8C0F, 0xDAC9, 0x8C10, 0xD0B3, 0x8C11, 0xDACA, 0x8C12, 0xDACB,\n\t0x8C13, 0xCEBD, 0x8C14, 0xDACC, 0x8C15, 0xDACD, 0x8C16, 0xDACE,\n\t0x8C17, 0xB2F7, 0x8C18, 0xDAD1, 0x8C19, 0xDACF, 0x8C1A, 0xD1E8,\n\t0x8C1B, 0xDAD0, 0x8C1C, 0xC3D5, 0x8C1D, 0xDAD2, 0x8C1E, 0xD7A0,\n\t0x8C1F, 0xDAD3, 0x8C20, 0xDAD4, 0x8C21, 0xDAD5, 0x8C22, 0xD0BB,\n\t0x8C23, 0xD2A5, 0x8C24, 0xB0F9, 0x8C25, 0xDAD6, 0x8C26, 0xC7AB,\n\t0x8C27, 0xDAD7, 0x8C28, 0xBDF7, 0x8C29, 0xC3A1, 0x8C2A, 0xDAD8,\n\t0x8C2B, 0xDAD9, 0x8C2C, 0xC3FD, 0x8C2D, 0xCCB7, 0x8C2E, 0xDADA,\n\t0x8C2F, 0xDADB, 0x8C30, 0xC0BE, 0x8C31, 0xC6D7, 0x8C32, 0xDADC,\n\t0x8C33, 0xDADD, 0x8C34, 0xC7B4, 0x8C35, 0xDADE, 0x8C36, 0xDADF,\n\t0x8C37, 0xB9C8, 0x8C38, 0xD840, 0x8C39, 0xD841, 0x8C3A, 0xD842,\n\t0x8C3B, 0xD843, 0x8C3C, 0xD844, 0x8C3D, 0xD845, 0x8C3E, 0xD846,\n\t0x8C3F, 0xD847, 0x8C40, 0xD848, 0x8C41, 0xBBED, 0x8C42, 0xD849,\n\t0x8C43, 0xD84A, 0x8C44, 0xD84B, 0x8C45, 0xD84C, 0x8C46, 0xB6B9,\n\t0x8C47, 0xF4F8, 0x8C48, 0xD84D, 0x8C49, 0xF4F9, 0x8C4A, 0xD84E,\n\t0x8C4B, 0xD84F, 0x8C4C, 0xCDE3, 0x8C4D, 0xD850, 0x8C4E, 0xD851,\n\t0x8C4F, 0xD852, 0x8C50, 0xD853, 0x8C51, 0xD854, 0x8C52, 0xD855,\n\t0x8C53, 0xD856, 0x8C54, 0xD857, 0x8C55, 0xF5B9, 0x8C56, 0xD858,\n\t0x8C57, 0xD859, 0x8C58, 0xD85A, 0x8C59, 0xD85B, 0x8C5A, 0xEBE0,\n\t0x8C5B, 0xD85C, 0x8C5C, 0xD85D, 0x8C5D, 0xD85E, 0x8C5E, 0xD85F,\n\t0x8C5F, 0xD860, 0x8C60, 0xD861, 0x8C61, 0xCFF3, 0x8C62, 0xBBBF,\n\t0x8C63, 0xD862, 0x8C64, 0xD863, 0x8C65, 0xD864, 0x8C66, 0xD865,\n\t0x8C67, 0xD866, 0x8C68, 0xD867, 0x8C69, 0xD868, 0x8C6A, 0xBAC0,\n\t0x8C6B, 0xD4A5, 0x8C6C, 0xD869, 0x8C6D, 0xD86A, 0x8C6E, 0xD86B,\n\t0x8C6F, 0xD86C, 0x8C70, 0xD86D, 0x8C71, 0xD86E, 0x8C72, 0xD86F,\n\t0x8C73, 0xE1D9, 0x8C74, 0xD870, 0x8C75, 0xD871, 0x8C76, 0xD872,\n\t0x8C77, 0xD873, 0x8C78, 0xF5F4, 0x8C79, 0xB1AA, 0x8C7A, 0xB2F2,\n\t0x8C7B, 0xD874, 0x8C7C, 0xD875, 0x8C7D, 0xD876, 0x8C7E, 0xD877,\n\t0x8C7F, 0xD878, 0x8C80, 0xD879, 0x8C81, 0xD87A, 0x8C82, 0xF5F5,\n\t0x8C83, 0xD87B, 0x8C84, 0xD87C, 0x8C85, 0xF5F7, 0x8C86, 0xD87D,\n\t0x8C87, 0xD87E, 0x8C88, 0xD880, 0x8C89, 0xBAD1, 0x8C8A, 0xF5F6,\n\t0x8C8B, 0xD881, 0x8C8C, 0xC3B2, 0x8C8D, 0xD882, 0x8C8E, 0xD883,\n\t0x8C8F, 0xD884, 0x8C90, 0xD885, 0x8C91, 0xD886, 0x8C92, 0xD887,\n\t0x8C93, 0xD888, 0x8C94, 0xF5F9, 0x8C95, 0xD889, 0x8C96, 0xD88A,\n\t0x8C97, 0xD88B, 0x8C98, 0xF5F8, 0x8C99, 0xD88C, 0x8C9A, 0xD88D,\n\t0x8C9B, 0xD88E, 0x8C9C, 0xD88F, 0x8C9D, 0xD890, 0x8C9E, 0xD891,\n\t0x8C9F, 0xD892, 0x8CA0, 0xD893, 0x8CA1, 0xD894, 0x8CA2, 0xD895,\n\t0x8CA3, 0xD896, 0x8CA4, 0xD897, 0x8CA5, 0xD898, 0x8CA6, 0xD899,\n\t0x8CA7, 0xD89A, 0x8CA8, 0xD89B, 0x8CA9, 0xD89C, 0x8CAA, 0xD89D,\n\t0x8CAB, 0xD89E, 0x8CAC, 0xD89F, 0x8CAD, 0xD8A0, 0x8CAE, 0xD940,\n\t0x8CAF, 0xD941, 0x8CB0, 0xD942, 0x8CB1, 0xD943, 0x8CB2, 0xD944,\n\t0x8CB3, 0xD945, 0x8CB4, 0xD946, 0x8CB5, 0xD947, 0x8CB6, 0xD948,\n\t0x8CB7, 0xD949, 0x8CB8, 0xD94A, 0x8CB9, 0xD94B, 0x8CBA, 0xD94C,\n\t0x8CBB, 0xD94D, 0x8CBC, 0xD94E, 0x8CBD, 0xD94F, 0x8CBE, 0xD950,\n\t0x8CBF, 0xD951, 0x8CC0, 0xD952, 0x8CC1, 0xD953, 0x8CC2, 0xD954,\n\t0x8CC3, 0xD955, 0x8CC4, 0xD956, 0x8CC5, 0xD957, 0x8CC6, 0xD958,\n\t0x8CC7, 0xD959, 0x8CC8, 0xD95A, 0x8CC9, 0xD95B, 0x8CCA, 0xD95C,\n\t0x8CCB, 0xD95D, 0x8CCC, 0xD95E, 0x8CCD, 0xD95F, 0x8CCE, 0xD960,\n\t0x8CCF, 0xD961, 0x8CD0, 0xD962, 0x8CD1, 0xD963, 0x8CD2, 0xD964,\n\t0x8CD3, 0xD965, 0x8CD4, 0xD966, 0x8CD5, 0xD967, 0x8CD6, 0xD968,\n\t0x8CD7, 0xD969, 0x8CD8, 0xD96A, 0x8CD9, 0xD96B, 0x8CDA, 0xD96C,\n\t0x8CDB, 0xD96D, 0x8CDC, 0xD96E, 0x8CDD, 0xD96F, 0x8CDE, 0xD970,\n\t0x8CDF, 0xD971, 0x8CE0, 0xD972, 0x8CE1, 0xD973, 0x8CE2, 0xD974,\n\t0x8CE3, 0xD975, 0x8CE4, 0xD976, 0x8CE5, 0xD977, 0x8CE6, 0xD978,\n\t0x8CE7, 0xD979, 0x8CE8, 0xD97A, 0x8CE9, 0xD97B, 0x8CEA, 0xD97C,\n\t0x8CEB, 0xD97D, 0x8CEC, 0xD97E, 0x8CED, 0xD980, 0x8CEE, 0xD981,\n\t0x8CEF, 0xD982, 0x8CF0, 0xD983, 0x8CF1, 0xD984, 0x8CF2, 0xD985,\n\t0x8CF3, 0xD986, 0x8CF4, 0xD987, 0x8CF5, 0xD988, 0x8CF6, 0xD989,\n\t0x8CF7, 0xD98A, 0x8CF8, 0xD98B, 0x8CF9, 0xD98C, 0x8CFA, 0xD98D,\n\t0x8CFB, 0xD98E, 0x8CFC, 0xD98F, 0x8CFD, 0xD990, 0x8CFE, 0xD991,\n\t0x8CFF, 0xD992, 0x8D00, 0xD993, 0x8D01, 0xD994, 0x8D02, 0xD995,\n\t0x8D03, 0xD996, 0x8D04, 0xD997, 0x8D05, 0xD998, 0x8D06, 0xD999,\n\t0x8D07, 0xD99A, 0x8D08, 0xD99B, 0x8D09, 0xD99C, 0x8D0A, 0xD99D,\n\t0x8D0B, 0xD99E, 0x8D0C, 0xD99F, 0x8D0D, 0xD9A0, 0x8D0E, 0xDA40,\n\t0x8D0F, 0xDA41, 0x8D10, 0xDA42, 0x8D11, 0xDA43, 0x8D12, 0xDA44,\n\t0x8D13, 0xDA45, 0x8D14, 0xDA46, 0x8D15, 0xDA47, 0x8D16, 0xDA48,\n\t0x8D17, 0xDA49, 0x8D18, 0xDA4A, 0x8D19, 0xDA4B, 0x8D1A, 0xDA4C,\n\t0x8D1B, 0xDA4D, 0x8D1C, 0xDA4E, 0x8D1D, 0xB1B4, 0x8D1E, 0xD5EA,\n\t0x8D1F, 0xB8BA, 0x8D20, 0xDA4F, 0x8D21, 0xB9B1, 0x8D22, 0xB2C6,\n\t0x8D23, 0xD4F0, 0x8D24, 0xCFCD, 0x8D25, 0xB0DC, 0x8D26, 0xD5CB,\n\t0x8D27, 0xBBF5, 0x8D28, 0xD6CA, 0x8D29, 0xB7B7, 0x8D2A, 0xCCB0,\n\t0x8D2B, 0xC6B6, 0x8D2C, 0xB1E1, 0x8D2D, 0xB9BA, 0x8D2E, 0xD6FC,\n\t0x8D2F, 0xB9E1, 0x8D30, 0xB7A1, 0x8D31, 0xBCFA, 0x8D32, 0xEADA,\n\t0x8D33, 0xEADB, 0x8D34, 0xCCF9, 0x8D35, 0xB9F3, 0x8D36, 0xEADC,\n\t0x8D37, 0xB4FB, 0x8D38, 0xC3B3, 0x8D39, 0xB7D1, 0x8D3A, 0xBAD8,\n\t0x8D3B, 0xEADD, 0x8D3C, 0xD4F4, 0x8D3D, 0xEADE, 0x8D3E, 0xBCD6,\n\t0x8D3F, 0xBBDF, 0x8D40, 0xEADF, 0x8D41, 0xC1DE, 0x8D42, 0xC2B8,\n\t0x8D43, 0xD4DF, 0x8D44, 0xD7CA, 0x8D45, 0xEAE0, 0x8D46, 0xEAE1,\n\t0x8D47, 0xEAE4, 0x8D48, 0xEAE2, 0x8D49, 0xEAE3, 0x8D4A, 0xC9DE,\n\t0x8D4B, 0xB8B3, 0x8D4C, 0xB6C4, 0x8D4D, 0xEAE5, 0x8D4E, 0xCAEA,\n\t0x8D4F, 0xC9CD, 0x8D50, 0xB4CD, 0x8D51, 0xDA50, 0x8D52, 0xDA51,\n\t0x8D53, 0xE2D9, 0x8D54, 0xC5E2, 0x8D55, 0xEAE6, 0x8D56, 0xC0B5,\n\t0x8D57, 0xDA52, 0x8D58, 0xD7B8, 0x8D59, 0xEAE7, 0x8D5A, 0xD7AC,\n\t0x8D5B, 0xC8FC, 0x8D5C, 0xD8D3, 0x8D5D, 0xD8CD, 0x8D5E, 0xD4DE,\n\t0x8D5F, 0xDA53, 0x8D60, 0xD4F9, 0x8D61, 0xC9C4, 0x8D62, 0xD3AE,\n\t0x8D63, 0xB8D3, 0x8D64, 0xB3E0, 0x8D65, 0xDA54, 0x8D66, 0xC9E2,\n\t0x8D67, 0xF4F6, 0x8D68, 0xDA55, 0x8D69, 0xDA56, 0x8D6A, 0xDA57,\n\t0x8D6B, 0xBAD5, 0x8D6C, 0xDA58, 0x8D6D, 0xF4F7, 0x8D6E, 0xDA59,\n\t0x8D6F, 0xDA5A, 0x8D70, 0xD7DF, 0x8D71, 0xDA5B, 0x8D72, 0xDA5C,\n\t0x8D73, 0xF4F1, 0x8D74, 0xB8B0, 0x8D75, 0xD5D4, 0x8D76, 0xB8CF,\n\t0x8D77, 0xC6F0, 0x8D78, 0xDA5D, 0x8D79, 0xDA5E, 0x8D7A, 0xDA5F,\n\t0x8D7B, 0xDA60, 0x8D7C, 0xDA61, 0x8D7D, 0xDA62, 0x8D7E, 0xDA63,\n\t0x8D7F, 0xDA64, 0x8D80, 0xDA65, 0x8D81, 0xB3C3, 0x8D82, 0xDA66,\n\t0x8D83, 0xDA67, 0x8D84, 0xF4F2, 0x8D85, 0xB3AC, 0x8D86, 0xDA68,\n\t0x8D87, 0xDA69, 0x8D88, 0xDA6A, 0x8D89, 0xDA6B, 0x8D8A, 0xD4BD,\n\t0x8D8B, 0xC7F7, 0x8D8C, 0xDA6C, 0x8D8D, 0xDA6D, 0x8D8E, 0xDA6E,\n\t0x8D8F, 0xDA6F, 0x8D90, 0xDA70, 0x8D91, 0xF4F4, 0x8D92, 0xDA71,\n\t0x8D93, 0xDA72, 0x8D94, 0xF4F3, 0x8D95, 0xDA73, 0x8D96, 0xDA74,\n\t0x8D97, 0xDA75, 0x8D98, 0xDA76, 0x8D99, 0xDA77, 0x8D9A, 0xDA78,\n\t0x8D9B, 0xDA79, 0x8D9C, 0xDA7A, 0x8D9D, 0xDA7B, 0x8D9E, 0xDA7C,\n\t0x8D9F, 0xCCCB, 0x8DA0, 0xDA7D, 0x8DA1, 0xDA7E, 0x8DA2, 0xDA80,\n\t0x8DA3, 0xC8A4, 0x8DA4, 0xDA81, 0x8DA5, 0xDA82, 0x8DA6, 0xDA83,\n\t0x8DA7, 0xDA84, 0x8DA8, 0xDA85, 0x8DA9, 0xDA86, 0x8DAA, 0xDA87,\n\t0x8DAB, 0xDA88, 0x8DAC, 0xDA89, 0x8DAD, 0xDA8A, 0x8DAE, 0xDA8B,\n\t0x8DAF, 0xDA8C, 0x8DB0, 0xDA8D, 0x8DB1, 0xF4F5, 0x8DB2, 0xDA8E,\n\t0x8DB3, 0xD7E3, 0x8DB4, 0xC5BF, 0x8DB5, 0xF5C0, 0x8DB6, 0xDA8F,\n\t0x8DB7, 0xDA90, 0x8DB8, 0xF5BB, 0x8DB9, 0xDA91, 0x8DBA, 0xF5C3,\n\t0x8DBB, 0xDA92, 0x8DBC, 0xF5C2, 0x8DBD, 0xDA93, 0x8DBE, 0xD6BA,\n\t0x8DBF, 0xF5C1, 0x8DC0, 0xDA94, 0x8DC1, 0xDA95, 0x8DC2, 0xDA96,\n\t0x8DC3, 0xD4BE, 0x8DC4, 0xF5C4, 0x8DC5, 0xDA97, 0x8DC6, 0xF5CC,\n\t0x8DC7, 0xDA98, 0x8DC8, 0xDA99, 0x8DC9, 0xDA9A, 0x8DCA, 0xDA9B,\n\t0x8DCB, 0xB0CF, 0x8DCC, 0xB5F8, 0x8DCD, 0xDA9C, 0x8DCE, 0xF5C9,\n\t0x8DCF, 0xF5CA, 0x8DD0, 0xDA9D, 0x8DD1, 0xC5DC, 0x8DD2, 0xDA9E,\n\t0x8DD3, 0xDA9F, 0x8DD4, 0xDAA0, 0x8DD5, 0xDB40, 0x8DD6, 0xF5C5,\n\t0x8DD7, 0xF5C6, 0x8DD8, 0xDB41, 0x8DD9, 0xDB42, 0x8DDA, 0xF5C7,\n\t0x8DDB, 0xF5CB, 0x8DDC, 0xDB43, 0x8DDD, 0xBEE0, 0x8DDE, 0xF5C8,\n\t0x8DDF, 0xB8FA, 0x8DE0, 0xDB44, 0x8DE1, 0xDB45, 0x8DE2, 0xDB46,\n\t0x8DE3, 0xF5D0, 0x8DE4, 0xF5D3, 0x8DE5, 0xDB47, 0x8DE6, 0xDB48,\n\t0x8DE7, 0xDB49, 0x8DE8, 0xBFE7, 0x8DE9, 0xDB4A, 0x8DEA, 0xB9F2,\n\t0x8DEB, 0xF5BC, 0x8DEC, 0xF5CD, 0x8DED, 0xDB4B, 0x8DEE, 0xDB4C,\n\t0x8DEF, 0xC2B7, 0x8DF0, 0xDB4D, 0x8DF1, 0xDB4E, 0x8DF2, 0xDB4F,\n\t0x8DF3, 0xCCF8, 0x8DF4, 0xDB50, 0x8DF5, 0xBCF9, 0x8DF6, 0xDB51,\n\t0x8DF7, 0xF5CE, 0x8DF8, 0xF5CF, 0x8DF9, 0xF5D1, 0x8DFA, 0xB6E5,\n\t0x8DFB, 0xF5D2, 0x8DFC, 0xDB52, 0x8DFD, 0xF5D5, 0x8DFE, 0xDB53,\n\t0x8DFF, 0xDB54, 0x8E00, 0xDB55, 0x8E01, 0xDB56, 0x8E02, 0xDB57,\n\t0x8E03, 0xDB58, 0x8E04, 0xDB59, 0x8E05, 0xF5BD, 0x8E06, 0xDB5A,\n\t0x8E07, 0xDB5B, 0x8E08, 0xDB5C, 0x8E09, 0xF5D4, 0x8E0A, 0xD3BB,\n\t0x8E0B, 0xDB5D, 0x8E0C, 0xB3EC, 0x8E0D, 0xDB5E, 0x8E0E, 0xDB5F,\n\t0x8E0F, 0xCCA4, 0x8E10, 0xDB60, 0x8E11, 0xDB61, 0x8E12, 0xDB62,\n\t0x8E13, 0xDB63, 0x8E14, 0xF5D6, 0x8E15, 0xDB64, 0x8E16, 0xDB65,\n\t0x8E17, 0xDB66, 0x8E18, 0xDB67, 0x8E19, 0xDB68, 0x8E1A, 0xDB69,\n\t0x8E1B, 0xDB6A, 0x8E1C, 0xDB6B, 0x8E1D, 0xF5D7, 0x8E1E, 0xBEE1,\n\t0x8E1F, 0xF5D8, 0x8E20, 0xDB6C, 0x8E21, 0xDB6D, 0x8E22, 0xCCDF,\n\t0x8E23, 0xF5DB, 0x8E24, 0xDB6E, 0x8E25, 0xDB6F, 0x8E26, 0xDB70,\n\t0x8E27, 0xDB71, 0x8E28, 0xDB72, 0x8E29, 0xB2C8, 0x8E2A, 0xD7D9,\n\t0x8E2B, 0xDB73, 0x8E2C, 0xF5D9, 0x8E2D, 0xDB74, 0x8E2E, 0xF5DA,\n\t0x8E2F, 0xF5DC, 0x8E30, 0xDB75, 0x8E31, 0xF5E2, 0x8E32, 0xDB76,\n\t0x8E33, 0xDB77, 0x8E34, 0xDB78, 0x8E35, 0xF5E0, 0x8E36, 0xDB79,\n\t0x8E37, 0xDB7A, 0x8E38, 0xDB7B, 0x8E39, 0xF5DF, 0x8E3A, 0xF5DD,\n\t0x8E3B, 0xDB7C, 0x8E3C, 0xDB7D, 0x8E3D, 0xF5E1, 0x8E3E, 0xDB7E,\n\t0x8E3F, 0xDB80, 0x8E40, 0xF5DE, 0x8E41, 0xF5E4, 0x8E42, 0xF5E5,\n\t0x8E43, 0xDB81, 0x8E44, 0xCCE3, 0x8E45, 0xDB82, 0x8E46, 0xDB83,\n\t0x8E47, 0xE5BF, 0x8E48, 0xB5B8, 0x8E49, 0xF5E3, 0x8E4A, 0xF5E8,\n\t0x8E4B, 0xCCA3, 0x8E4C, 0xDB84, 0x8E4D, 0xDB85, 0x8E4E, 0xDB86,\n\t0x8E4F, 0xDB87, 0x8E50, 0xDB88, 0x8E51, 0xF5E6, 0x8E52, 0xF5E7,\n\t0x8E53, 0xDB89, 0x8E54, 0xDB8A, 0x8E55, 0xDB8B, 0x8E56, 0xDB8C,\n\t0x8E57, 0xDB8D, 0x8E58, 0xDB8E, 0x8E59, 0xF5BE, 0x8E5A, 0xDB8F,\n\t0x8E5B, 0xDB90, 0x8E5C, 0xDB91, 0x8E5D, 0xDB92, 0x8E5E, 0xDB93,\n\t0x8E5F, 0xDB94, 0x8E60, 0xDB95, 0x8E61, 0xDB96, 0x8E62, 0xDB97,\n\t0x8E63, 0xDB98, 0x8E64, 0xDB99, 0x8E65, 0xDB9A, 0x8E66, 0xB1C4,\n\t0x8E67, 0xDB9B, 0x8E68, 0xDB9C, 0x8E69, 0xF5BF, 0x8E6A, 0xDB9D,\n\t0x8E6B, 0xDB9E, 0x8E6C, 0xB5C5, 0x8E6D, 0xB2E4, 0x8E6E, 0xDB9F,\n\t0x8E6F, 0xF5EC, 0x8E70, 0xF5E9, 0x8E71, 0xDBA0, 0x8E72, 0xB6D7,\n\t0x8E73, 0xDC40, 0x8E74, 0xF5ED, 0x8E75, 0xDC41, 0x8E76, 0xF5EA,\n\t0x8E77, 0xDC42, 0x8E78, 0xDC43, 0x8E79, 0xDC44, 0x8E7A, 0xDC45,\n\t0x8E7B, 0xDC46, 0x8E7C, 0xF5EB, 0x8E7D, 0xDC47, 0x8E7E, 0xDC48,\n\t0x8E7F, 0xB4DA, 0x8E80, 0xDC49, 0x8E81, 0xD4EA, 0x8E82, 0xDC4A,\n\t0x8E83, 0xDC4B, 0x8E84, 0xDC4C, 0x8E85, 0xF5EE, 0x8E86, 0xDC4D,\n\t0x8E87, 0xB3F9, 0x8E88, 0xDC4E, 0x8E89, 0xDC4F, 0x8E8A, 0xDC50,\n\t0x8E8B, 0xDC51, 0x8E8C, 0xDC52, 0x8E8D, 0xDC53, 0x8E8E, 0xDC54,\n\t0x8E8F, 0xF5EF, 0x8E90, 0xF5F1, 0x8E91, 0xDC55, 0x8E92, 0xDC56,\n\t0x8E93, 0xDC57, 0x8E94, 0xF5F0, 0x8E95, 0xDC58, 0x8E96, 0xDC59,\n\t0x8E97, 0xDC5A, 0x8E98, 0xDC5B, 0x8E99, 0xDC5C, 0x8E9A, 0xDC5D,\n\t0x8E9B, 0xDC5E, 0x8E9C, 0xF5F2, 0x8E9D, 0xDC5F, 0x8E9E, 0xF5F3,\n\t0x8E9F, 0xDC60, 0x8EA0, 0xDC61, 0x8EA1, 0xDC62, 0x8EA2, 0xDC63,\n\t0x8EA3, 0xDC64, 0x8EA4, 0xDC65, 0x8EA5, 0xDC66, 0x8EA6, 0xDC67,\n\t0x8EA7, 0xDC68, 0x8EA8, 0xDC69, 0x8EA9, 0xDC6A, 0x8EAA, 0xDC6B,\n\t0x8EAB, 0xC9ED, 0x8EAC, 0xB9AA, 0x8EAD, 0xDC6C, 0x8EAE, 0xDC6D,\n\t0x8EAF, 0xC7FB, 0x8EB0, 0xDC6E, 0x8EB1, 0xDC6F, 0x8EB2, 0xB6E3,\n\t0x8EB3, 0xDC70, 0x8EB4, 0xDC71, 0x8EB5, 0xDC72, 0x8EB6, 0xDC73,\n\t0x8EB7, 0xDC74, 0x8EB8, 0xDC75, 0x8EB9, 0xDC76, 0x8EBA, 0xCCC9,\n\t0x8EBB, 0xDC77, 0x8EBC, 0xDC78, 0x8EBD, 0xDC79, 0x8EBE, 0xDC7A,\n\t0x8EBF, 0xDC7B, 0x8EC0, 0xDC7C, 0x8EC1, 0xDC7D, 0x8EC2, 0xDC7E,\n\t0x8EC3, 0xDC80, 0x8EC4, 0xDC81, 0x8EC5, 0xDC82, 0x8EC6, 0xDC83,\n\t0x8EC7, 0xDC84, 0x8EC8, 0xDC85, 0x8EC9, 0xDC86, 0x8ECA, 0xDC87,\n\t0x8ECB, 0xDC88, 0x8ECC, 0xDC89, 0x8ECD, 0xDC8A, 0x8ECE, 0xEAA6,\n\t0x8ECF, 0xDC8B, 0x8ED0, 0xDC8C, 0x8ED1, 0xDC8D, 0x8ED2, 0xDC8E,\n\t0x8ED3, 0xDC8F, 0x8ED4, 0xDC90, 0x8ED5, 0xDC91, 0x8ED6, 0xDC92,\n\t0x8ED7, 0xDC93, 0x8ED8, 0xDC94, 0x8ED9, 0xDC95, 0x8EDA, 0xDC96,\n\t0x8EDB, 0xDC97, 0x8EDC, 0xDC98, 0x8EDD, 0xDC99, 0x8EDE, 0xDC9A,\n\t0x8EDF, 0xDC9B, 0x8EE0, 0xDC9C, 0x8EE1, 0xDC9D, 0x8EE2, 0xDC9E,\n\t0x8EE3, 0xDC9F, 0x8EE4, 0xDCA0, 0x8EE5, 0xDD40, 0x8EE6, 0xDD41,\n\t0x8EE7, 0xDD42, 0x8EE8, 0xDD43, 0x8EE9, 0xDD44, 0x8EEA, 0xDD45,\n\t0x8EEB, 0xDD46, 0x8EEC, 0xDD47, 0x8EED, 0xDD48, 0x8EEE, 0xDD49,\n\t0x8EEF, 0xDD4A, 0x8EF0, 0xDD4B, 0x8EF1, 0xDD4C, 0x8EF2, 0xDD4D,\n\t0x8EF3, 0xDD4E, 0x8EF4, 0xDD4F, 0x8EF5, 0xDD50, 0x8EF6, 0xDD51,\n\t0x8EF7, 0xDD52, 0x8EF8, 0xDD53, 0x8EF9, 0xDD54, 0x8EFA, 0xDD55,\n\t0x8EFB, 0xDD56, 0x8EFC, 0xDD57, 0x8EFD, 0xDD58, 0x8EFE, 0xDD59,\n\t0x8EFF, 0xDD5A, 0x8F00, 0xDD5B, 0x8F01, 0xDD5C, 0x8F02, 0xDD5D,\n\t0x8F03, 0xDD5E, 0x8F04, 0xDD5F, 0x8F05, 0xDD60, 0x8F06, 0xDD61,\n\t0x8F07, 0xDD62, 0x8F08, 0xDD63, 0x8F09, 0xDD64, 0x8F0A, 0xDD65,\n\t0x8F0B, 0xDD66, 0x8F0C, 0xDD67, 0x8F0D, 0xDD68, 0x8F0E, 0xDD69,\n\t0x8F0F, 0xDD6A, 0x8F10, 0xDD6B, 0x8F11, 0xDD6C, 0x8F12, 0xDD6D,\n\t0x8F13, 0xDD6E, 0x8F14, 0xDD6F, 0x8F15, 0xDD70, 0x8F16, 0xDD71,\n\t0x8F17, 0xDD72, 0x8F18, 0xDD73, 0x8F19, 0xDD74, 0x8F1A, 0xDD75,\n\t0x8F1B, 0xDD76, 0x8F1C, 0xDD77, 0x8F1D, 0xDD78, 0x8F1E, 0xDD79,\n\t0x8F1F, 0xDD7A, 0x8F20, 0xDD7B, 0x8F21, 0xDD7C, 0x8F22, 0xDD7D,\n\t0x8F23, 0xDD7E, 0x8F24, 0xDD80, 0x8F25, 0xDD81, 0x8F26, 0xDD82,\n\t0x8F27, 0xDD83, 0x8F28, 0xDD84, 0x8F29, 0xDD85, 0x8F2A, 0xDD86,\n\t0x8F2B, 0xDD87, 0x8F2C, 0xDD88, 0x8F2D, 0xDD89, 0x8F2E, 0xDD8A,\n\t0x8F2F, 0xDD8B, 0x8F30, 0xDD8C, 0x8F31, 0xDD8D, 0x8F32, 0xDD8E,\n\t0x8F33, 0xDD8F, 0x8F34, 0xDD90, 0x8F35, 0xDD91, 0x8F36, 0xDD92,\n\t0x8F37, 0xDD93, 0x8F38, 0xDD94, 0x8F39, 0xDD95, 0x8F3A, 0xDD96,\n\t0x8F3B, 0xDD97, 0x8F3C, 0xDD98, 0x8F3D, 0xDD99, 0x8F3E, 0xDD9A,\n\t0x8F3F, 0xDD9B, 0x8F40, 0xDD9C, 0x8F41, 0xDD9D, 0x8F42, 0xDD9E,\n\t0x8F43, 0xDD9F, 0x8F44, 0xDDA0, 0x8F45, 0xDE40, 0x8F46, 0xDE41,\n\t0x8F47, 0xDE42, 0x8F48, 0xDE43, 0x8F49, 0xDE44, 0x8F4A, 0xDE45,\n\t0x8F4B, 0xDE46, 0x8F4C, 0xDE47, 0x8F4D, 0xDE48, 0x8F4E, 0xDE49,\n\t0x8F4F, 0xDE4A, 0x8F50, 0xDE4B, 0x8F51, 0xDE4C, 0x8F52, 0xDE4D,\n\t0x8F53, 0xDE4E, 0x8F54, 0xDE4F, 0x8F55, 0xDE50, 0x8F56, 0xDE51,\n\t0x8F57, 0xDE52, 0x8F58, 0xDE53, 0x8F59, 0xDE54, 0x8F5A, 0xDE55,\n\t0x8F5B, 0xDE56, 0x8F5C, 0xDE57, 0x8F5D, 0xDE58, 0x8F5E, 0xDE59,\n\t0x8F5F, 0xDE5A, 0x8F60, 0xDE5B, 0x8F61, 0xDE5C, 0x8F62, 0xDE5D,\n\t0x8F63, 0xDE5E, 0x8F64, 0xDE5F, 0x8F65, 0xDE60, 0x8F66, 0xB3B5,\n\t0x8F67, 0xD4FE, 0x8F68, 0xB9EC, 0x8F69, 0xD0F9, 0x8F6A, 0xDE61,\n\t0x8F6B, 0xE9ED, 0x8F6C, 0xD7AA, 0x8F6D, 0xE9EE, 0x8F6E, 0xC2D6,\n\t0x8F6F, 0xC8ED, 0x8F70, 0xBAE4, 0x8F71, 0xE9EF, 0x8F72, 0xE9F0,\n\t0x8F73, 0xE9F1, 0x8F74, 0xD6E1, 0x8F75, 0xE9F2, 0x8F76, 0xE9F3,\n\t0x8F77, 0xE9F5, 0x8F78, 0xE9F4, 0x8F79, 0xE9F6, 0x8F7A, 0xE9F7,\n\t0x8F7B, 0xC7E1, 0x8F7C, 0xE9F8, 0x8F7D, 0xD4D8, 0x8F7E, 0xE9F9,\n\t0x8F7F, 0xBDCE, 0x8F80, 0xDE62, 0x8F81, 0xE9FA, 0x8F82, 0xE9FB,\n\t0x8F83, 0xBDCF, 0x8F84, 0xE9FC, 0x8F85, 0xB8A8, 0x8F86, 0xC1BE,\n\t0x8F87, 0xE9FD, 0x8F88, 0xB1B2, 0x8F89, 0xBBD4, 0x8F8A, 0xB9F5,\n\t0x8F8B, 0xE9FE, 0x8F8C, 0xDE63, 0x8F8D, 0xEAA1, 0x8F8E, 0xEAA2,\n\t0x8F8F, 0xEAA3, 0x8F90, 0xB7F8, 0x8F91, 0xBCAD, 0x8F92, 0xDE64,\n\t0x8F93, 0xCAE4, 0x8F94, 0xE0CE, 0x8F95, 0xD4AF, 0x8F96, 0xCFBD,\n\t0x8F97, 0xD5B7, 0x8F98, 0xEAA4, 0x8F99, 0xD5DE, 0x8F9A, 0xEAA5,\n\t0x8F9B, 0xD0C1, 0x8F9C, 0xB9BC, 0x8F9D, 0xDE65, 0x8F9E, 0xB4C7,\n\t0x8F9F, 0xB1D9, 0x8FA0, 0xDE66, 0x8FA1, 0xDE67, 0x8FA2, 0xDE68,\n\t0x8FA3, 0xC0B1, 0x8FA4, 0xDE69, 0x8FA5, 0xDE6A, 0x8FA6, 0xDE6B,\n\t0x8FA7, 0xDE6C, 0x8FA8, 0xB1E6, 0x8FA9, 0xB1E7, 0x8FAA, 0xDE6D,\n\t0x8FAB, 0xB1E8, 0x8FAC, 0xDE6E, 0x8FAD, 0xDE6F, 0x8FAE, 0xDE70,\n\t0x8FAF, 0xDE71, 0x8FB0, 0xB3BD, 0x8FB1, 0xC8E8, 0x8FB2, 0xDE72,\n\t0x8FB3, 0xDE73, 0x8FB4, 0xDE74, 0x8FB5, 0xDE75, 0x8FB6, 0xE5C1,\n\t0x8FB7, 0xDE76, 0x8FB8, 0xDE77, 0x8FB9, 0xB1DF, 0x8FBA, 0xDE78,\n\t0x8FBB, 0xDE79, 0x8FBC, 0xDE7A, 0x8FBD, 0xC1C9, 0x8FBE, 0xB4EF,\n\t0x8FBF, 0xDE7B, 0x8FC0, 0xDE7C, 0x8FC1, 0xC7A8, 0x8FC2, 0xD3D8,\n\t0x8FC3, 0xDE7D, 0x8FC4, 0xC6F9, 0x8FC5, 0xD1B8, 0x8FC6, 0xDE7E,\n\t0x8FC7, 0xB9FD, 0x8FC8, 0xC2F5, 0x8FC9, 0xDE80, 0x8FCA, 0xDE81,\n\t0x8FCB, 0xDE82, 0x8FCC, 0xDE83, 0x8FCD, 0xDE84, 0x8FCE, 0xD3AD,\n\t0x8FCF, 0xDE85, 0x8FD0, 0xD4CB, 0x8FD1, 0xBDFC, 0x8FD2, 0xDE86,\n\t0x8FD3, 0xE5C2, 0x8FD4, 0xB7B5, 0x8FD5, 0xE5C3, 0x8FD6, 0xDE87,\n\t0x8FD7, 0xDE88, 0x8FD8, 0xBBB9, 0x8FD9, 0xD5E2, 0x8FDA, 0xDE89,\n\t0x8FDB, 0xBDF8, 0x8FDC, 0xD4B6, 0x8FDD, 0xCEA5, 0x8FDE, 0xC1AC,\n\t0x8FDF, 0xB3D9, 0x8FE0, 0xDE8A, 0x8FE1, 0xDE8B, 0x8FE2, 0xCCF6,\n\t0x8FE3, 0xDE8C, 0x8FE4, 0xE5C6, 0x8FE5, 0xE5C4, 0x8FE6, 0xE5C8,\n\t0x8FE7, 0xDE8D, 0x8FE8, 0xE5CA, 0x8FE9, 0xE5C7, 0x8FEA, 0xB5CF,\n\t0x8FEB, 0xC6C8, 0x8FEC, 0xDE8E, 0x8FED, 0xB5FC, 0x8FEE, 0xE5C5,\n\t0x8FEF, 0xDE8F, 0x8FF0, 0xCAF6, 0x8FF1, 0xDE90, 0x8FF2, 0xDE91,\n\t0x8FF3, 0xE5C9, 0x8FF4, 0xDE92, 0x8FF5, 0xDE93, 0x8FF6, 0xDE94,\n\t0x8FF7, 0xC3D4, 0x8FF8, 0xB1C5, 0x8FF9, 0xBCA3, 0x8FFA, 0xDE95,\n\t0x8FFB, 0xDE96, 0x8FFC, 0xDE97, 0x8FFD, 0xD7B7, 0x8FFE, 0xDE98,\n\t0x8FFF, 0xDE99, 0x9000, 0xCDCB, 0x9001, 0xCBCD, 0x9002, 0xCACA,\n\t0x9003, 0xCCD3, 0x9004, 0xE5CC, 0x9005, 0xE5CB, 0x9006, 0xC4E6,\n\t0x9007, 0xDE9A, 0x9008, 0xDE9B, 0x9009, 0xD1A1, 0x900A, 0xD1B7,\n\t0x900B, 0xE5CD, 0x900C, 0xDE9C, 0x900D, 0xE5D0, 0x900E, 0xDE9D,\n\t0x900F, 0xCDB8, 0x9010, 0xD6F0, 0x9011, 0xE5CF, 0x9012, 0xB5DD,\n\t0x9013, 0xDE9E, 0x9014, 0xCDBE, 0x9015, 0xDE9F, 0x9016, 0xE5D1,\n\t0x9017, 0xB6BA, 0x9018, 0xDEA0, 0x9019, 0xDF40, 0x901A, 0xCDA8,\n\t0x901B, 0xB9E4, 0x901C, 0xDF41, 0x901D, 0xCAC5, 0x901E, 0xB3D1,\n\t0x901F, 0xCBD9, 0x9020, 0xD4EC, 0x9021, 0xE5D2, 0x9022, 0xB7EA,\n\t0x9023, 0xDF42, 0x9024, 0xDF43, 0x9025, 0xDF44, 0x9026, 0xE5CE,\n\t0x9027, 0xDF45, 0x9028, 0xDF46, 0x9029, 0xDF47, 0x902A, 0xDF48,\n\t0x902B, 0xDF49, 0x902C, 0xDF4A, 0x902D, 0xE5D5, 0x902E, 0xB4FE,\n\t0x902F, 0xE5D6, 0x9030, 0xDF4B, 0x9031, 0xDF4C, 0x9032, 0xDF4D,\n\t0x9033, 0xDF4E, 0x9034, 0xDF4F, 0x9035, 0xE5D3, 0x9036, 0xE5D4,\n\t0x9037, 0xDF50, 0x9038, 0xD2DD, 0x9039, 0xDF51, 0x903A, 0xDF52,\n\t0x903B, 0xC2DF, 0x903C, 0xB1C6, 0x903D, 0xDF53, 0x903E, 0xD3E2,\n\t0x903F, 0xDF54, 0x9040, 0xDF55, 0x9041, 0xB6DD, 0x9042, 0xCBEC,\n\t0x9043, 0xDF56, 0x9044, 0xE5D7, 0x9045, 0xDF57, 0x9046, 0xDF58,\n\t0x9047, 0xD3F6, 0x9048, 0xDF59, 0x9049, 0xDF5A, 0x904A, 0xDF5B,\n\t0x904B, 0xDF5C, 0x904C, 0xDF5D, 0x904D, 0xB1E9, 0x904E, 0xDF5E,\n\t0x904F, 0xB6F4, 0x9050, 0xE5DA, 0x9051, 0xE5D8, 0x9052, 0xE5D9,\n\t0x9053, 0xB5C0, 0x9054, 0xDF5F, 0x9055, 0xDF60, 0x9056, 0xDF61,\n\t0x9057, 0xD2C5, 0x9058, 0xE5DC, 0x9059, 0xDF62, 0x905A, 0xDF63,\n\t0x905B, 0xE5DE, 0x905C, 0xDF64, 0x905D, 0xDF65, 0x905E, 0xDF66,\n\t0x905F, 0xDF67, 0x9060, 0xDF68, 0x9061, 0xDF69, 0x9062, 0xE5DD,\n\t0x9063, 0xC7B2, 0x9064, 0xDF6A, 0x9065, 0xD2A3, 0x9066, 0xDF6B,\n\t0x9067, 0xDF6C, 0x9068, 0xE5DB, 0x9069, 0xDF6D, 0x906A, 0xDF6E,\n\t0x906B, 0xDF6F, 0x906C, 0xDF70, 0x906D, 0xD4E2, 0x906E, 0xD5DA,\n\t0x906F, 0xDF71, 0x9070, 0xDF72, 0x9071, 0xDF73, 0x9072, 0xDF74,\n\t0x9073, 0xDF75, 0x9074, 0xE5E0, 0x9075, 0xD7F1, 0x9076, 0xDF76,\n\t0x9077, 0xDF77, 0x9078, 0xDF78, 0x9079, 0xDF79, 0x907A, 0xDF7A,\n\t0x907B, 0xDF7B, 0x907C, 0xDF7C, 0x907D, 0xE5E1, 0x907E, 0xDF7D,\n\t0x907F, 0xB1DC, 0x9080, 0xD1FB, 0x9081, 0xDF7E, 0x9082, 0xE5E2,\n\t0x9083, 0xE5E4, 0x9084, 0xDF80, 0x9085, 0xDF81, 0x9086, 0xDF82,\n\t0x9087, 0xDF83, 0x9088, 0xE5E3, 0x9089, 0xDF84, 0x908A, 0xDF85,\n\t0x908B, 0xE5E5, 0x908C, 0xDF86, 0x908D, 0xDF87, 0x908E, 0xDF88,\n\t0x908F, 0xDF89, 0x9090, 0xDF8A, 0x9091, 0xD2D8, 0x9092, 0xDF8B,\n\t0x9093, 0xB5CB, 0x9094, 0xDF8C, 0x9095, 0xE7DF, 0x9096, 0xDF8D,\n\t0x9097, 0xDAF5, 0x9098, 0xDF8E, 0x9099, 0xDAF8, 0x909A, 0xDF8F,\n\t0x909B, 0xDAF6, 0x909C, 0xDF90, 0x909D, 0xDAF7, 0x909E, 0xDF91,\n\t0x909F, 0xDF92, 0x90A0, 0xDF93, 0x90A1, 0xDAFA, 0x90A2, 0xD0CF,\n\t0x90A3, 0xC4C7, 0x90A4, 0xDF94, 0x90A5, 0xDF95, 0x90A6, 0xB0EE,\n\t0x90A7, 0xDF96, 0x90A8, 0xDF97, 0x90A9, 0xDF98, 0x90AA, 0xD0B0,\n\t0x90AB, 0xDF99, 0x90AC, 0xDAF9, 0x90AD, 0xDF9A, 0x90AE, 0xD3CA,\n\t0x90AF, 0xBAAA, 0x90B0, 0xDBA2, 0x90B1, 0xC7F1, 0x90B2, 0xDF9B,\n\t0x90B3, 0xDAFC, 0x90B4, 0xDAFB, 0x90B5, 0xC9DB, 0x90B6, 0xDAFD,\n\t0x90B7, 0xDF9C, 0x90B8, 0xDBA1, 0x90B9, 0xD7DE, 0x90BA, 0xDAFE,\n\t0x90BB, 0xC1DA, 0x90BC, 0xDF9D, 0x90BD, 0xDF9E, 0x90BE, 0xDBA5,\n\t0x90BF, 0xDF9F, 0x90C0, 0xDFA0, 0x90C1, 0xD3F4, 0x90C2, 0xE040,\n\t0x90C3, 0xE041, 0x90C4, 0xDBA7, 0x90C5, 0xDBA4, 0x90C6, 0xE042,\n\t0x90C7, 0xDBA8, 0x90C8, 0xE043, 0x90C9, 0xE044, 0x90CA, 0xBDBC,\n\t0x90CB, 0xE045, 0x90CC, 0xE046, 0x90CD, 0xE047, 0x90CE, 0xC0C9,\n\t0x90CF, 0xDBA3, 0x90D0, 0xDBA6, 0x90D1, 0xD6A3, 0x90D2, 0xE048,\n\t0x90D3, 0xDBA9, 0x90D4, 0xE049, 0x90D5, 0xE04A, 0x90D6, 0xE04B,\n\t0x90D7, 0xDBAD, 0x90D8, 0xE04C, 0x90D9, 0xE04D, 0x90DA, 0xE04E,\n\t0x90DB, 0xDBAE, 0x90DC, 0xDBAC, 0x90DD, 0xBAC2, 0x90DE, 0xE04F,\n\t0x90DF, 0xE050, 0x90E0, 0xE051, 0x90E1, 0xBFA4, 0x90E2, 0xDBAB,\n\t0x90E3, 0xE052, 0x90E4, 0xE053, 0x90E5, 0xE054, 0x90E6, 0xDBAA,\n\t0x90E7, 0xD4C7, 0x90E8, 0xB2BF, 0x90E9, 0xE055, 0x90EA, 0xE056,\n\t0x90EB, 0xDBAF, 0x90EC, 0xE057, 0x90ED, 0xB9F9, 0x90EE, 0xE058,\n\t0x90EF, 0xDBB0, 0x90F0, 0xE059, 0x90F1, 0xE05A, 0x90F2, 0xE05B,\n\t0x90F3, 0xE05C, 0x90F4, 0xB3BB, 0x90F5, 0xE05D, 0x90F6, 0xE05E,\n\t0x90F7, 0xE05F, 0x90F8, 0xB5A6, 0x90F9, 0xE060, 0x90FA, 0xE061,\n\t0x90FB, 0xE062, 0x90FC, 0xE063, 0x90FD, 0xB6BC, 0x90FE, 0xDBB1,\n\t0x90FF, 0xE064, 0x9100, 0xE065, 0x9101, 0xE066, 0x9102, 0xB6F5,\n\t0x9103, 0xE067, 0x9104, 0xDBB2, 0x9105, 0xE068, 0x9106, 0xE069,\n\t0x9107, 0xE06A, 0x9108, 0xE06B, 0x9109, 0xE06C, 0x910A, 0xE06D,\n\t0x910B, 0xE06E, 0x910C, 0xE06F, 0x910D, 0xE070, 0x910E, 0xE071,\n\t0x910F, 0xE072, 0x9110, 0xE073, 0x9111, 0xE074, 0x9112, 0xE075,\n\t0x9113, 0xE076, 0x9114, 0xE077, 0x9115, 0xE078, 0x9116, 0xE079,\n\t0x9117, 0xE07A, 0x9118, 0xE07B, 0x9119, 0xB1C9, 0x911A, 0xE07C,\n\t0x911B, 0xE07D, 0x911C, 0xE07E, 0x911D, 0xE080, 0x911E, 0xDBB4,\n\t0x911F, 0xE081, 0x9120, 0xE082, 0x9121, 0xE083, 0x9122, 0xDBB3,\n\t0x9123, 0xDBB5, 0x9124, 0xE084, 0x9125, 0xE085, 0x9126, 0xE086,\n\t0x9127, 0xE087, 0x9128, 0xE088, 0x9129, 0xE089, 0x912A, 0xE08A,\n\t0x912B, 0xE08B, 0x912C, 0xE08C, 0x912D, 0xE08D, 0x912E, 0xE08E,\n\t0x912F, 0xDBB7, 0x9130, 0xE08F, 0x9131, 0xDBB6, 0x9132, 0xE090,\n\t0x9133, 0xE091, 0x9134, 0xE092, 0x9135, 0xE093, 0x9136, 0xE094,\n\t0x9137, 0xE095, 0x9138, 0xE096, 0x9139, 0xDBB8, 0x913A, 0xE097,\n\t0x913B, 0xE098, 0x913C, 0xE099, 0x913D, 0xE09A, 0x913E, 0xE09B,\n\t0x913F, 0xE09C, 0x9140, 0xE09D, 0x9141, 0xE09E, 0x9142, 0xE09F,\n\t0x9143, 0xDBB9, 0x9144, 0xE0A0, 0x9145, 0xE140, 0x9146, 0xDBBA,\n\t0x9147, 0xE141, 0x9148, 0xE142, 0x9149, 0xD3CF, 0x914A, 0xF4FA,\n\t0x914B, 0xC7F5, 0x914C, 0xD7C3, 0x914D, 0xC5E4, 0x914E, 0xF4FC,\n\t0x914F, 0xF4FD, 0x9150, 0xF4FB, 0x9151, 0xE143, 0x9152, 0xBEC6,\n\t0x9153, 0xE144, 0x9154, 0xE145, 0x9155, 0xE146, 0x9156, 0xE147,\n\t0x9157, 0xD0EF, 0x9158, 0xE148, 0x9159, 0xE149, 0x915A, 0xB7D3,\n\t0x915B, 0xE14A, 0x915C, 0xE14B, 0x915D, 0xD4CD, 0x915E, 0xCCAA,\n\t0x915F, 0xE14C, 0x9160, 0xE14D, 0x9161, 0xF5A2, 0x9162, 0xF5A1,\n\t0x9163, 0xBAA8, 0x9164, 0xF4FE, 0x9165, 0xCBD6, 0x9166, 0xE14E,\n\t0x9167, 0xE14F, 0x9168, 0xE150, 0x9169, 0xF5A4, 0x916A, 0xC0D2,\n\t0x916B, 0xE151, 0x916C, 0xB3EA, 0x916D, 0xE152, 0x916E, 0xCDAA,\n\t0x916F, 0xF5A5, 0x9170, 0xF5A3, 0x9171, 0xBDB4, 0x9172, 0xF5A8,\n\t0x9173, 0xE153, 0x9174, 0xF5A9, 0x9175, 0xBDCD, 0x9176, 0xC3B8,\n\t0x9177, 0xBFE1, 0x9178, 0xCBE1, 0x9179, 0xF5AA, 0x917A, 0xE154,\n\t0x917B, 0xE155, 0x917C, 0xE156, 0x917D, 0xF5A6, 0x917E, 0xF5A7,\n\t0x917F, 0xC4F0, 0x9180, 0xE157, 0x9181, 0xE158, 0x9182, 0xE159,\n\t0x9183, 0xE15A, 0x9184, 0xE15B, 0x9185, 0xF5AC, 0x9186, 0xE15C,\n\t0x9187, 0xB4BC, 0x9188, 0xE15D, 0x9189, 0xD7ED, 0x918A, 0xE15E,\n\t0x918B, 0xB4D7, 0x918C, 0xF5AB, 0x918D, 0xF5AE, 0x918E, 0xE15F,\n\t0x918F, 0xE160, 0x9190, 0xF5AD, 0x9191, 0xF5AF, 0x9192, 0xD0D1,\n\t0x9193, 0xE161, 0x9194, 0xE162, 0x9195, 0xE163, 0x9196, 0xE164,\n\t0x9197, 0xE165, 0x9198, 0xE166, 0x9199, 0xE167, 0x919A, 0xC3D1,\n\t0x919B, 0xC8A9, 0x919C, 0xE168, 0x919D, 0xE169, 0x919E, 0xE16A,\n\t0x919F, 0xE16B, 0x91A0, 0xE16C, 0x91A1, 0xE16D, 0x91A2, 0xF5B0,\n\t0x91A3, 0xF5B1, 0x91A4, 0xE16E, 0x91A5, 0xE16F, 0x91A6, 0xE170,\n\t0x91A7, 0xE171, 0x91A8, 0xE172, 0x91A9, 0xE173, 0x91AA, 0xF5B2,\n\t0x91AB, 0xE174, 0x91AC, 0xE175, 0x91AD, 0xF5B3, 0x91AE, 0xF5B4,\n\t0x91AF, 0xF5B5, 0x91B0, 0xE176, 0x91B1, 0xE177, 0x91B2, 0xE178,\n\t0x91B3, 0xE179, 0x91B4, 0xF5B7, 0x91B5, 0xF5B6, 0x91B6, 0xE17A,\n\t0x91B7, 0xE17B, 0x91B8, 0xE17C, 0x91B9, 0xE17D, 0x91BA, 0xF5B8,\n\t0x91BB, 0xE17E, 0x91BC, 0xE180, 0x91BD, 0xE181, 0x91BE, 0xE182,\n\t0x91BF, 0xE183, 0x91C0, 0xE184, 0x91C1, 0xE185, 0x91C2, 0xE186,\n\t0x91C3, 0xE187, 0x91C4, 0xE188, 0x91C5, 0xE189, 0x91C6, 0xE18A,\n\t0x91C7, 0xB2C9, 0x91C8, 0xE18B, 0x91C9, 0xD3D4, 0x91CA, 0xCACD,\n\t0x91CB, 0xE18C, 0x91CC, 0xC0EF, 0x91CD, 0xD6D8, 0x91CE, 0xD2B0,\n\t0x91CF, 0xC1BF, 0x91D0, 0xE18D, 0x91D1, 0xBDF0, 0x91D2, 0xE18E,\n\t0x91D3, 0xE18F, 0x91D4, 0xE190, 0x91D5, 0xE191, 0x91D6, 0xE192,\n\t0x91D7, 0xE193, 0x91D8, 0xE194, 0x91D9, 0xE195, 0x91DA, 0xE196,\n\t0x91DB, 0xE197, 0x91DC, 0xB8AA, 0x91DD, 0xE198, 0x91DE, 0xE199,\n\t0x91DF, 0xE19A, 0x91E0, 0xE19B, 0x91E1, 0xE19C, 0x91E2, 0xE19D,\n\t0x91E3, 0xE19E, 0x91E4, 0xE19F, 0x91E5, 0xE1A0, 0x91E6, 0xE240,\n\t0x91E7, 0xE241, 0x91E8, 0xE242, 0x91E9, 0xE243, 0x91EA, 0xE244,\n\t0x91EB, 0xE245, 0x91EC, 0xE246, 0x91ED, 0xE247, 0x91EE, 0xE248,\n\t0x91EF, 0xE249, 0x91F0, 0xE24A, 0x91F1, 0xE24B, 0x91F2, 0xE24C,\n\t0x91F3, 0xE24D, 0x91F4, 0xE24E, 0x91F5, 0xE24F, 0x91F6, 0xE250,\n\t0x91F7, 0xE251, 0x91F8, 0xE252, 0x91F9, 0xE253, 0x91FA, 0xE254,\n\t0x91FB, 0xE255, 0x91FC, 0xE256, 0x91FD, 0xE257, 0x91FE, 0xE258,\n\t0x91FF, 0xE259, 0x9200, 0xE25A, 0x9201, 0xE25B, 0x9202, 0xE25C,\n\t0x9203, 0xE25D, 0x9204, 0xE25E, 0x9205, 0xE25F, 0x9206, 0xE260,\n\t0x9207, 0xE261, 0x9208, 0xE262, 0x9209, 0xE263, 0x920A, 0xE264,\n\t0x920B, 0xE265, 0x920C, 0xE266, 0x920D, 0xE267, 0x920E, 0xE268,\n\t0x920F, 0xE269, 0x9210, 0xE26A, 0x9211, 0xE26B, 0x9212, 0xE26C,\n\t0x9213, 0xE26D, 0x9214, 0xE26E, 0x9215, 0xE26F, 0x9216, 0xE270,\n\t0x9217, 0xE271, 0x9218, 0xE272, 0x9219, 0xE273, 0x921A, 0xE274,\n\t0x921B, 0xE275, 0x921C, 0xE276, 0x921D, 0xE277, 0x921E, 0xE278,\n\t0x921F, 0xE279, 0x9220, 0xE27A, 0x9221, 0xE27B, 0x9222, 0xE27C,\n\t0x9223, 0xE27D, 0x9224, 0xE27E, 0x9225, 0xE280, 0x9226, 0xE281,\n\t0x9227, 0xE282, 0x9228, 0xE283, 0x9229, 0xE284, 0x922A, 0xE285,\n\t0x922B, 0xE286, 0x922C, 0xE287, 0x922D, 0xE288, 0x922E, 0xE289,\n\t0x922F, 0xE28A, 0x9230, 0xE28B, 0x9231, 0xE28C, 0x9232, 0xE28D,\n\t0x9233, 0xE28E, 0x9234, 0xE28F, 0x9235, 0xE290, 0x9236, 0xE291,\n\t0x9237, 0xE292, 0x9238, 0xE293, 0x9239, 0xE294, 0x923A, 0xE295,\n\t0x923B, 0xE296, 0x923C, 0xE297, 0x923D, 0xE298, 0x923E, 0xE299,\n\t0x923F, 0xE29A, 0x9240, 0xE29B, 0x9241, 0xE29C, 0x9242, 0xE29D,\n\t0x9243, 0xE29E, 0x9244, 0xE29F, 0x9245, 0xE2A0, 0x9246, 0xE340,\n\t0x9247, 0xE341, 0x9248, 0xE342, 0x9249, 0xE343, 0x924A, 0xE344,\n\t0x924B, 0xE345, 0x924C, 0xE346, 0x924D, 0xE347, 0x924E, 0xE348,\n\t0x924F, 0xE349, 0x9250, 0xE34A, 0x9251, 0xE34B, 0x9252, 0xE34C,\n\t0x9253, 0xE34D, 0x9254, 0xE34E, 0x9255, 0xE34F, 0x9256, 0xE350,\n\t0x9257, 0xE351, 0x9258, 0xE352, 0x9259, 0xE353, 0x925A, 0xE354,\n\t0x925B, 0xE355, 0x925C, 0xE356, 0x925D, 0xE357, 0x925E, 0xE358,\n\t0x925F, 0xE359, 0x9260, 0xE35A, 0x9261, 0xE35B, 0x9262, 0xE35C,\n\t0x9263, 0xE35D, 0x9264, 0xE35E, 0x9265, 0xE35F, 0x9266, 0xE360,\n\t0x9267, 0xE361, 0x9268, 0xE362, 0x9269, 0xE363, 0x926A, 0xE364,\n\t0x926B, 0xE365, 0x926C, 0xE366, 0x926D, 0xE367, 0x926E, 0xE368,\n\t0x926F, 0xE369, 0x9270, 0xE36A, 0x9271, 0xE36B, 0x9272, 0xE36C,\n\t0x9273, 0xE36D, 0x9274, 0xBCF8, 0x9275, 0xE36E, 0x9276, 0xE36F,\n\t0x9277, 0xE370, 0x9278, 0xE371, 0x9279, 0xE372, 0x927A, 0xE373,\n\t0x927B, 0xE374, 0x927C, 0xE375, 0x927D, 0xE376, 0x927E, 0xE377,\n\t0x927F, 0xE378, 0x9280, 0xE379, 0x9281, 0xE37A, 0x9282, 0xE37B,\n\t0x9283, 0xE37C, 0x9284, 0xE37D, 0x9285, 0xE37E, 0x9286, 0xE380,\n\t0x9287, 0xE381, 0x9288, 0xE382, 0x9289, 0xE383, 0x928A, 0xE384,\n\t0x928B, 0xE385, 0x928C, 0xE386, 0x928D, 0xE387, 0x928E, 0xF6C6,\n\t0x928F, 0xE388, 0x9290, 0xE389, 0x9291, 0xE38A, 0x9292, 0xE38B,\n\t0x9293, 0xE38C, 0x9294, 0xE38D, 0x9295, 0xE38E, 0x9296, 0xE38F,\n\t0x9297, 0xE390, 0x9298, 0xE391, 0x9299, 0xE392, 0x929A, 0xE393,\n\t0x929B, 0xE394, 0x929C, 0xE395, 0x929D, 0xE396, 0x929E, 0xE397,\n\t0x929F, 0xE398, 0x92A0, 0xE399, 0x92A1, 0xE39A, 0x92A2, 0xE39B,\n\t0x92A3, 0xE39C, 0x92A4, 0xE39D, 0x92A5, 0xE39E, 0x92A6, 0xE39F,\n\t0x92A7, 0xE3A0, 0x92A8, 0xE440, 0x92A9, 0xE441, 0x92AA, 0xE442,\n\t0x92AB, 0xE443, 0x92AC, 0xE444, 0x92AD, 0xE445, 0x92AE, 0xF6C7,\n\t0x92AF, 0xE446, 0x92B0, 0xE447, 0x92B1, 0xE448, 0x92B2, 0xE449,\n\t0x92B3, 0xE44A, 0x92B4, 0xE44B, 0x92B5, 0xE44C, 0x92B6, 0xE44D,\n\t0x92B7, 0xE44E, 0x92B8, 0xE44F, 0x92B9, 0xE450, 0x92BA, 0xE451,\n\t0x92BB, 0xE452, 0x92BC, 0xE453, 0x92BD, 0xE454, 0x92BE, 0xE455,\n\t0x92BF, 0xE456, 0x92C0, 0xE457, 0x92C1, 0xE458, 0x92C2, 0xE459,\n\t0x92C3, 0xE45A, 0x92C4, 0xE45B, 0x92C5, 0xE45C, 0x92C6, 0xE45D,\n\t0x92C7, 0xE45E, 0x92C8, 0xF6C8, 0x92C9, 0xE45F, 0x92CA, 0xE460,\n\t0x92CB, 0xE461, 0x92CC, 0xE462, 0x92CD, 0xE463, 0x92CE, 0xE464,\n\t0x92CF, 0xE465, 0x92D0, 0xE466, 0x92D1, 0xE467, 0x92D2, 0xE468,\n\t0x92D3, 0xE469, 0x92D4, 0xE46A, 0x92D5, 0xE46B, 0x92D6, 0xE46C,\n\t0x92D7, 0xE46D, 0x92D8, 0xE46E, 0x92D9, 0xE46F, 0x92DA, 0xE470,\n\t0x92DB, 0xE471, 0x92DC, 0xE472, 0x92DD, 0xE473, 0x92DE, 0xE474,\n\t0x92DF, 0xE475, 0x92E0, 0xE476, 0x92E1, 0xE477, 0x92E2, 0xE478,\n\t0x92E3, 0xE479, 0x92E4, 0xE47A, 0x92E5, 0xE47B, 0x92E6, 0xE47C,\n\t0x92E7, 0xE47D, 0x92E8, 0xE47E, 0x92E9, 0xE480, 0x92EA, 0xE481,\n\t0x92EB, 0xE482, 0x92EC, 0xE483, 0x92ED, 0xE484, 0x92EE, 0xE485,\n\t0x92EF, 0xE486, 0x92F0, 0xE487, 0x92F1, 0xE488, 0x92F2, 0xE489,\n\t0x92F3, 0xE48A, 0x92F4, 0xE48B, 0x92F5, 0xE48C, 0x92F6, 0xE48D,\n\t0x92F7, 0xE48E, 0x92F8, 0xE48F, 0x92F9, 0xE490, 0x92FA, 0xE491,\n\t0x92FB, 0xE492, 0x92FC, 0xE493, 0x92FD, 0xE494, 0x92FE, 0xE495,\n\t0x92FF, 0xE496, 0x9300, 0xE497, 0x9301, 0xE498, 0x9302, 0xE499,\n\t0x9303, 0xE49A, 0x9304, 0xE49B, 0x9305, 0xE49C, 0x9306, 0xE49D,\n\t0x9307, 0xE49E, 0x9308, 0xE49F, 0x9309, 0xE4A0, 0x930A, 0xE540,\n\t0x930B, 0xE541, 0x930C, 0xE542, 0x930D, 0xE543, 0x930E, 0xE544,\n\t0x930F, 0xE545, 0x9310, 0xE546, 0x9311, 0xE547, 0x9312, 0xE548,\n\t0x9313, 0xE549, 0x9314, 0xE54A, 0x9315, 0xE54B, 0x9316, 0xE54C,\n\t0x9317, 0xE54D, 0x9318, 0xE54E, 0x9319, 0xE54F, 0x931A, 0xE550,\n\t0x931B, 0xE551, 0x931C, 0xE552, 0x931D, 0xE553, 0x931E, 0xE554,\n\t0x931F, 0xE555, 0x9320, 0xE556, 0x9321, 0xE557, 0x9322, 0xE558,\n\t0x9323, 0xE559, 0x9324, 0xE55A, 0x9325, 0xE55B, 0x9326, 0xE55C,\n\t0x9327, 0xE55D, 0x9328, 0xE55E, 0x9329, 0xE55F, 0x932A, 0xE560,\n\t0x932B, 0xE561, 0x932C, 0xE562, 0x932D, 0xE563, 0x932E, 0xE564,\n\t0x932F, 0xE565, 0x9330, 0xE566, 0x9331, 0xE567, 0x9332, 0xE568,\n\t0x9333, 0xE569, 0x9334, 0xE56A, 0x9335, 0xE56B, 0x9336, 0xE56C,\n\t0x9337, 0xE56D, 0x9338, 0xE56E, 0x9339, 0xE56F, 0x933A, 0xE570,\n\t0x933B, 0xE571, 0x933C, 0xE572, 0x933D, 0xE573, 0x933E, 0xF6C9,\n\t0x933F, 0xE574, 0x9340, 0xE575, 0x9341, 0xE576, 0x9342, 0xE577,\n\t0x9343, 0xE578, 0x9344, 0xE579, 0x9345, 0xE57A, 0x9346, 0xE57B,\n\t0x9347, 0xE57C, 0x9348, 0xE57D, 0x9349, 0xE57E, 0x934A, 0xE580,\n\t0x934B, 0xE581, 0x934C, 0xE582, 0x934D, 0xE583, 0x934E, 0xE584,\n\t0x934F, 0xE585, 0x9350, 0xE586, 0x9351, 0xE587, 0x9352, 0xE588,\n\t0x9353, 0xE589, 0x9354, 0xE58A, 0x9355, 0xE58B, 0x9356, 0xE58C,\n\t0x9357, 0xE58D, 0x9358, 0xE58E, 0x9359, 0xE58F, 0x935A, 0xE590,\n\t0x935B, 0xE591, 0x935C, 0xE592, 0x935D, 0xE593, 0x935E, 0xE594,\n\t0x935F, 0xE595, 0x9360, 0xE596, 0x9361, 0xE597, 0x9362, 0xE598,\n\t0x9363, 0xE599, 0x9364, 0xE59A, 0x9365, 0xE59B, 0x9366, 0xE59C,\n\t0x9367, 0xE59D, 0x9368, 0xE59E, 0x9369, 0xE59F, 0x936A, 0xF6CA,\n\t0x936B, 0xE5A0, 0x936C, 0xE640, 0x936D, 0xE641, 0x936E, 0xE642,\n\t0x936F, 0xE643, 0x9370, 0xE644, 0x9371, 0xE645, 0x9372, 0xE646,\n\t0x9373, 0xE647, 0x9374, 0xE648, 0x9375, 0xE649, 0x9376, 0xE64A,\n\t0x9377, 0xE64B, 0x9378, 0xE64C, 0x9379, 0xE64D, 0x937A, 0xE64E,\n\t0x937B, 0xE64F, 0x937C, 0xE650, 0x937D, 0xE651, 0x937E, 0xE652,\n\t0x937F, 0xE653, 0x9380, 0xE654, 0x9381, 0xE655, 0x9382, 0xE656,\n\t0x9383, 0xE657, 0x9384, 0xE658, 0x9385, 0xE659, 0x9386, 0xE65A,\n\t0x9387, 0xE65B, 0x9388, 0xE65C, 0x9389, 0xE65D, 0x938A, 0xE65E,\n\t0x938B, 0xE65F, 0x938C, 0xE660, 0x938D, 0xE661, 0x938E, 0xE662,\n\t0x938F, 0xF6CC, 0x9390, 0xE663, 0x9391, 0xE664, 0x9392, 0xE665,\n\t0x9393, 0xE666, 0x9394, 0xE667, 0x9395, 0xE668, 0x9396, 0xE669,\n\t0x9397, 0xE66A, 0x9398, 0xE66B, 0x9399, 0xE66C, 0x939A, 0xE66D,\n\t0x939B, 0xE66E, 0x939C, 0xE66F, 0x939D, 0xE670, 0x939E, 0xE671,\n\t0x939F, 0xE672, 0x93A0, 0xE673, 0x93A1, 0xE674, 0x93A2, 0xE675,\n\t0x93A3, 0xE676, 0x93A4, 0xE677, 0x93A5, 0xE678, 0x93A6, 0xE679,\n\t0x93A7, 0xE67A, 0x93A8, 0xE67B, 0x93A9, 0xE67C, 0x93AA, 0xE67D,\n\t0x93AB, 0xE67E, 0x93AC, 0xE680, 0x93AD, 0xE681, 0x93AE, 0xE682,\n\t0x93AF, 0xE683, 0x93B0, 0xE684, 0x93B1, 0xE685, 0x93B2, 0xE686,\n\t0x93B3, 0xE687, 0x93B4, 0xE688, 0x93B5, 0xE689, 0x93B6, 0xE68A,\n\t0x93B7, 0xE68B, 0x93B8, 0xE68C, 0x93B9, 0xE68D, 0x93BA, 0xE68E,\n\t0x93BB, 0xE68F, 0x93BC, 0xE690, 0x93BD, 0xE691, 0x93BE, 0xE692,\n\t0x93BF, 0xE693, 0x93C0, 0xE694, 0x93C1, 0xE695, 0x93C2, 0xE696,\n\t0x93C3, 0xE697, 0x93C4, 0xE698, 0x93C5, 0xE699, 0x93C6, 0xE69A,\n\t0x93C7, 0xE69B, 0x93C8, 0xE69C, 0x93C9, 0xE69D, 0x93CA, 0xF6CB,\n\t0x93CB, 0xE69E, 0x93CC, 0xE69F, 0x93CD, 0xE6A0, 0x93CE, 0xE740,\n\t0x93CF, 0xE741, 0x93D0, 0xE742, 0x93D1, 0xE743, 0x93D2, 0xE744,\n\t0x93D3, 0xE745, 0x93D4, 0xE746, 0x93D5, 0xE747, 0x93D6, 0xF7E9,\n\t0x93D7, 0xE748, 0x93D8, 0xE749, 0x93D9, 0xE74A, 0x93DA, 0xE74B,\n\t0x93DB, 0xE74C, 0x93DC, 0xE74D, 0x93DD, 0xE74E, 0x93DE, 0xE74F,\n\t0x93DF, 0xE750, 0x93E0, 0xE751, 0x93E1, 0xE752, 0x93E2, 0xE753,\n\t0x93E3, 0xE754, 0x93E4, 0xE755, 0x93E5, 0xE756, 0x93E6, 0xE757,\n\t0x93E7, 0xE758, 0x93E8, 0xE759, 0x93E9, 0xE75A, 0x93EA, 0xE75B,\n\t0x93EB, 0xE75C, 0x93EC, 0xE75D, 0x93ED, 0xE75E, 0x93EE, 0xE75F,\n\t0x93EF, 0xE760, 0x93F0, 0xE761, 0x93F1, 0xE762, 0x93F2, 0xE763,\n\t0x93F3, 0xE764, 0x93F4, 0xE765, 0x93F5, 0xE766, 0x93F6, 0xE767,\n\t0x93F7, 0xE768, 0x93F8, 0xE769, 0x93F9, 0xE76A, 0x93FA, 0xE76B,\n\t0x93FB, 0xE76C, 0x93FC, 0xE76D, 0x93FD, 0xE76E, 0x93FE, 0xE76F,\n\t0x93FF, 0xE770, 0x9400, 0xE771, 0x9401, 0xE772, 0x9402, 0xE773,\n\t0x9403, 0xE774, 0x9404, 0xE775, 0x9405, 0xE776, 0x9406, 0xE777,\n\t0x9407, 0xE778, 0x9408, 0xE779, 0x9409, 0xE77A, 0x940A, 0xE77B,\n\t0x940B, 0xE77C, 0x940C, 0xE77D, 0x940D, 0xE77E, 0x940E, 0xE780,\n\t0x940F, 0xE781, 0x9410, 0xE782, 0x9411, 0xE783, 0x9412, 0xE784,\n\t0x9413, 0xE785, 0x9414, 0xE786, 0x9415, 0xE787, 0x9416, 0xE788,\n\t0x9417, 0xE789, 0x9418, 0xE78A, 0x9419, 0xE78B, 0x941A, 0xE78C,\n\t0x941B, 0xE78D, 0x941C, 0xE78E, 0x941D, 0xE78F, 0x941E, 0xE790,\n\t0x941F, 0xE791, 0x9420, 0xE792, 0x9421, 0xE793, 0x9422, 0xE794,\n\t0x9423, 0xE795, 0x9424, 0xE796, 0x9425, 0xE797, 0x9426, 0xE798,\n\t0x9427, 0xE799, 0x9428, 0xE79A, 0x9429, 0xE79B, 0x942A, 0xE79C,\n\t0x942B, 0xE79D, 0x942C, 0xE79E, 0x942D, 0xE79F, 0x942E, 0xE7A0,\n\t0x942F, 0xE840, 0x9430, 0xE841, 0x9431, 0xE842, 0x9432, 0xE843,\n\t0x9433, 0xE844, 0x9434, 0xE845, 0x9435, 0xE846, 0x9436, 0xE847,\n\t0x9437, 0xE848, 0x9438, 0xE849, 0x9439, 0xE84A, 0x943A, 0xE84B,\n\t0x943B, 0xE84C, 0x943C, 0xE84D, 0x943D, 0xE84E, 0x943E, 0xF6CD,\n\t0x943F, 0xE84F, 0x9440, 0xE850, 0x9441, 0xE851, 0x9442, 0xE852,\n\t0x9443, 0xE853, 0x9444, 0xE854, 0x9445, 0xE855, 0x9446, 0xE856,\n\t0x9447, 0xE857, 0x9448, 0xE858, 0x9449, 0xE859, 0x944A, 0xE85A,\n\t0x944B, 0xE85B, 0x944C, 0xE85C, 0x944D, 0xE85D, 0x944E, 0xE85E,\n\t0x944F, 0xE85F, 0x9450, 0xE860, 0x9451, 0xE861, 0x9452, 0xE862,\n\t0x9453, 0xE863, 0x9454, 0xE864, 0x9455, 0xE865, 0x9456, 0xE866,\n\t0x9457, 0xE867, 0x9458, 0xE868, 0x9459, 0xE869, 0x945A, 0xE86A,\n\t0x945B, 0xE86B, 0x945C, 0xE86C, 0x945D, 0xE86D, 0x945E, 0xE86E,\n\t0x945F, 0xE86F, 0x9460, 0xE870, 0x9461, 0xE871, 0x9462, 0xE872,\n\t0x9463, 0xE873, 0x9464, 0xE874, 0x9465, 0xE875, 0x9466, 0xE876,\n\t0x9467, 0xE877, 0x9468, 0xE878, 0x9469, 0xE879, 0x946A, 0xE87A,\n\t0x946B, 0xF6CE, 0x946C, 0xE87B, 0x946D, 0xE87C, 0x946E, 0xE87D,\n\t0x946F, 0xE87E, 0x9470, 0xE880, 0x9471, 0xE881, 0x9472, 0xE882,\n\t0x9473, 0xE883, 0x9474, 0xE884, 0x9475, 0xE885, 0x9476, 0xE886,\n\t0x9477, 0xE887, 0x9478, 0xE888, 0x9479, 0xE889, 0x947A, 0xE88A,\n\t0x947B, 0xE88B, 0x947C, 0xE88C, 0x947D, 0xE88D, 0x947E, 0xE88E,\n\t0x947F, 0xE88F, 0x9480, 0xE890, 0x9481, 0xE891, 0x9482, 0xE892,\n\t0x9483, 0xE893, 0x9484, 0xE894, 0x9485, 0xEEC4, 0x9486, 0xEEC5,\n\t0x9487, 0xEEC6, 0x9488, 0xD5EB, 0x9489, 0xB6A4, 0x948A, 0xEEC8,\n\t0x948B, 0xEEC7, 0x948C, 0xEEC9, 0x948D, 0xEECA, 0x948E, 0xC7A5,\n\t0x948F, 0xEECB, 0x9490, 0xEECC, 0x9491, 0xE895, 0x9492, 0xB7B0,\n\t0x9493, 0xB5F6, 0x9494, 0xEECD, 0x9495, 0xEECF, 0x9496, 0xE896,\n\t0x9497, 0xEECE, 0x9498, 0xE897, 0x9499, 0xB8C6, 0x949A, 0xEED0,\n\t0x949B, 0xEED1, 0x949C, 0xEED2, 0x949D, 0xB6DB, 0x949E, 0xB3AE,\n\t0x949F, 0xD6D3, 0x94A0, 0xC4C6, 0x94A1, 0xB1B5, 0x94A2, 0xB8D6,\n\t0x94A3, 0xEED3, 0x94A4, 0xEED4, 0x94A5, 0xD4BF, 0x94A6, 0xC7D5,\n\t0x94A7, 0xBEFB, 0x94A8, 0xCED9, 0x94A9, 0xB9B3, 0x94AA, 0xEED6,\n\t0x94AB, 0xEED5, 0x94AC, 0xEED8, 0x94AD, 0xEED7, 0x94AE, 0xC5A5,\n\t0x94AF, 0xEED9, 0x94B0, 0xEEDA, 0x94B1, 0xC7AE, 0x94B2, 0xEEDB,\n\t0x94B3, 0xC7AF, 0x94B4, 0xEEDC, 0x94B5, 0xB2A7, 0x94B6, 0xEEDD,\n\t0x94B7, 0xEEDE, 0x94B8, 0xEEDF, 0x94B9, 0xEEE0, 0x94BA, 0xEEE1,\n\t0x94BB, 0xD7EA, 0x94BC, 0xEEE2, 0x94BD, 0xEEE3, 0x94BE, 0xBCD8,\n\t0x94BF, 0xEEE4, 0x94C0, 0xD3CB, 0x94C1, 0xCCFA, 0x94C2, 0xB2AC,\n\t0x94C3, 0xC1E5, 0x94C4, 0xEEE5, 0x94C5, 0xC7A6, 0x94C6, 0xC3AD,\n\t0x94C7, 0xE898, 0x94C8, 0xEEE6, 0x94C9, 0xEEE7, 0x94CA, 0xEEE8,\n\t0x94CB, 0xEEE9, 0x94CC, 0xEEEA, 0x94CD, 0xEEEB, 0x94CE, 0xEEEC,\n\t0x94CF, 0xE899, 0x94D0, 0xEEED, 0x94D1, 0xEEEE, 0x94D2, 0xEEEF,\n\t0x94D3, 0xE89A, 0x94D4, 0xE89B, 0x94D5, 0xEEF0, 0x94D6, 0xEEF1,\n\t0x94D7, 0xEEF2, 0x94D8, 0xEEF4, 0x94D9, 0xEEF3, 0x94DA, 0xE89C,\n\t0x94DB, 0xEEF5, 0x94DC, 0xCDAD, 0x94DD, 0xC2C1, 0x94DE, 0xEEF6,\n\t0x94DF, 0xEEF7, 0x94E0, 0xEEF8, 0x94E1, 0xD5A1, 0x94E2, 0xEEF9,\n\t0x94E3, 0xCFB3, 0x94E4, 0xEEFA, 0x94E5, 0xEEFB, 0x94E6, 0xE89D,\n\t0x94E7, 0xEEFC, 0x94E8, 0xEEFD, 0x94E9, 0xEFA1, 0x94EA, 0xEEFE,\n\t0x94EB, 0xEFA2, 0x94EC, 0xB8F5, 0x94ED, 0xC3FA, 0x94EE, 0xEFA3,\n\t0x94EF, 0xEFA4, 0x94F0, 0xBDC2, 0x94F1, 0xD2BF, 0x94F2, 0xB2F9,\n\t0x94F3, 0xEFA5, 0x94F4, 0xEFA6, 0x94F5, 0xEFA7, 0x94F6, 0xD2F8,\n\t0x94F7, 0xEFA8, 0x94F8, 0xD6FD, 0x94F9, 0xEFA9, 0x94FA, 0xC6CC,\n\t0x94FB, 0xE89E, 0x94FC, 0xEFAA, 0x94FD, 0xEFAB, 0x94FE, 0xC1B4,\n\t0x94FF, 0xEFAC, 0x9500, 0xCFFA, 0x9501, 0xCBF8, 0x9502, 0xEFAE,\n\t0x9503, 0xEFAD, 0x9504, 0xB3FA, 0x9505, 0xB9F8, 0x9506, 0xEFAF,\n\t0x9507, 0xEFB0, 0x9508, 0xD0E2, 0x9509, 0xEFB1, 0x950A, 0xEFB2,\n\t0x950B, 0xB7E6, 0x950C, 0xD0BF, 0x950D, 0xEFB3, 0x950E, 0xEFB4,\n\t0x950F, 0xEFB5, 0x9510, 0xC8F1, 0x9511, 0xCCE0, 0x9512, 0xEFB6,\n\t0x9513, 0xEFB7, 0x9514, 0xEFB8, 0x9515, 0xEFB9, 0x9516, 0xEFBA,\n\t0x9517, 0xD5E0, 0x9518, 0xEFBB, 0x9519, 0xB4ED, 0x951A, 0xC3AA,\n\t0x951B, 0xEFBC, 0x951C, 0xE89F, 0x951D, 0xEFBD, 0x951E, 0xEFBE,\n\t0x951F, 0xEFBF, 0x9520, 0xE8A0, 0x9521, 0xCEFD, 0x9522, 0xEFC0,\n\t0x9523, 0xC2E0, 0x9524, 0xB4B8, 0x9525, 0xD7B6, 0x9526, 0xBDF5,\n\t0x9527, 0xE940, 0x9528, 0xCFC7, 0x9529, 0xEFC3, 0x952A, 0xEFC1,\n\t0x952B, 0xEFC2, 0x952C, 0xEFC4, 0x952D, 0xB6A7, 0x952E, 0xBCFC,\n\t0x952F, 0xBEE2, 0x9530, 0xC3CC, 0x9531, 0xEFC5, 0x9532, 0xEFC6,\n\t0x9533, 0xE941, 0x9534, 0xEFC7, 0x9535, 0xEFCF, 0x9536, 0xEFC8,\n\t0x9537, 0xEFC9, 0x9538, 0xEFCA, 0x9539, 0xC7C2, 0x953A, 0xEFF1,\n\t0x953B, 0xB6CD, 0x953C, 0xEFCB, 0x953D, 0xE942, 0x953E, 0xEFCC,\n\t0x953F, 0xEFCD, 0x9540, 0xB6C6, 0x9541, 0xC3BE, 0x9542, 0xEFCE,\n\t0x9543, 0xE943, 0x9544, 0xEFD0, 0x9545, 0xEFD1, 0x9546, 0xEFD2,\n\t0x9547, 0xD5F2, 0x9548, 0xE944, 0x9549, 0xEFD3, 0x954A, 0xC4F7,\n\t0x954B, 0xE945, 0x954C, 0xEFD4, 0x954D, 0xC4F8, 0x954E, 0xEFD5,\n\t0x954F, 0xEFD6, 0x9550, 0xB8E4, 0x9551, 0xB0F7, 0x9552, 0xEFD7,\n\t0x9553, 0xEFD8, 0x9554, 0xEFD9, 0x9555, 0xE946, 0x9556, 0xEFDA,\n\t0x9557, 0xEFDB, 0x9558, 0xEFDC, 0x9559, 0xEFDD, 0x955A, 0xE947,\n\t0x955B, 0xEFDE, 0x955C, 0xBEB5, 0x955D, 0xEFE1, 0x955E, 0xEFDF,\n\t0x955F, 0xEFE0, 0x9560, 0xE948, 0x9561, 0xEFE2, 0x9562, 0xEFE3,\n\t0x9563, 0xC1CD, 0x9564, 0xEFE4, 0x9565, 0xEFE5, 0x9566, 0xEFE6,\n\t0x9567, 0xEFE7, 0x9568, 0xEFE8, 0x9569, 0xEFE9, 0x956A, 0xEFEA,\n\t0x956B, 0xEFEB, 0x956C, 0xEFEC, 0x956D, 0xC0D8, 0x956E, 0xE949,\n\t0x956F, 0xEFED, 0x9570, 0xC1AD, 0x9571, 0xEFEE, 0x9572, 0xEFEF,\n\t0x9573, 0xEFF0, 0x9574, 0xE94A, 0x9575, 0xE94B, 0x9576, 0xCFE2,\n\t0x9577, 0xE94C, 0x9578, 0xE94D, 0x9579, 0xE94E, 0x957A, 0xE94F,\n\t0x957B, 0xE950, 0x957C, 0xE951, 0x957D, 0xE952, 0x957E, 0xE953,\n\t0x957F, 0xB3A4, 0x9580, 0xE954, 0x9581, 0xE955, 0x9582, 0xE956,\n\t0x9583, 0xE957, 0x9584, 0xE958, 0x9585, 0xE959, 0x9586, 0xE95A,\n\t0x9587, 0xE95B, 0x9588, 0xE95C, 0x9589, 0xE95D, 0x958A, 0xE95E,\n\t0x958B, 0xE95F, 0x958C, 0xE960, 0x958D, 0xE961, 0x958E, 0xE962,\n\t0x958F, 0xE963, 0x9590, 0xE964, 0x9591, 0xE965, 0x9592, 0xE966,\n\t0x9593, 0xE967, 0x9594, 0xE968, 0x9595, 0xE969, 0x9596, 0xE96A,\n\t0x9597, 0xE96B, 0x9598, 0xE96C, 0x9599, 0xE96D, 0x959A, 0xE96E,\n\t0x959B, 0xE96F, 0x959C, 0xE970, 0x959D, 0xE971, 0x959E, 0xE972,\n\t0x959F, 0xE973, 0x95A0, 0xE974, 0x95A1, 0xE975, 0x95A2, 0xE976,\n\t0x95A3, 0xE977, 0x95A4, 0xE978, 0x95A5, 0xE979, 0x95A6, 0xE97A,\n\t0x95A7, 0xE97B, 0x95A8, 0xE97C, 0x95A9, 0xE97D, 0x95AA, 0xE97E,\n\t0x95AB, 0xE980, 0x95AC, 0xE981, 0x95AD, 0xE982, 0x95AE, 0xE983,\n\t0x95AF, 0xE984, 0x95B0, 0xE985, 0x95B1, 0xE986, 0x95B2, 0xE987,\n\t0x95B3, 0xE988, 0x95B4, 0xE989, 0x95B5, 0xE98A, 0x95B6, 0xE98B,\n\t0x95B7, 0xE98C, 0x95B8, 0xE98D, 0x95B9, 0xE98E, 0x95BA, 0xE98F,\n\t0x95BB, 0xE990, 0x95BC, 0xE991, 0x95BD, 0xE992, 0x95BE, 0xE993,\n\t0x95BF, 0xE994, 0x95C0, 0xE995, 0x95C1, 0xE996, 0x95C2, 0xE997,\n\t0x95C3, 0xE998, 0x95C4, 0xE999, 0x95C5, 0xE99A, 0x95C6, 0xE99B,\n\t0x95C7, 0xE99C, 0x95C8, 0xE99D, 0x95C9, 0xE99E, 0x95CA, 0xE99F,\n\t0x95CB, 0xE9A0, 0x95CC, 0xEA40, 0x95CD, 0xEA41, 0x95CE, 0xEA42,\n\t0x95CF, 0xEA43, 0x95D0, 0xEA44, 0x95D1, 0xEA45, 0x95D2, 0xEA46,\n\t0x95D3, 0xEA47, 0x95D4, 0xEA48, 0x95D5, 0xEA49, 0x95D6, 0xEA4A,\n\t0x95D7, 0xEA4B, 0x95D8, 0xEA4C, 0x95D9, 0xEA4D, 0x95DA, 0xEA4E,\n\t0x95DB, 0xEA4F, 0x95DC, 0xEA50, 0x95DD, 0xEA51, 0x95DE, 0xEA52,\n\t0x95DF, 0xEA53, 0x95E0, 0xEA54, 0x95E1, 0xEA55, 0x95E2, 0xEA56,\n\t0x95E3, 0xEA57, 0x95E4, 0xEA58, 0x95E5, 0xEA59, 0x95E6, 0xEA5A,\n\t0x95E7, 0xEA5B, 0x95E8, 0xC3C5, 0x95E9, 0xE3C5, 0x95EA, 0xC9C1,\n\t0x95EB, 0xE3C6, 0x95EC, 0xEA5C, 0x95ED, 0xB1D5, 0x95EE, 0xCECA,\n\t0x95EF, 0xB4B3, 0x95F0, 0xC8F2, 0x95F1, 0xE3C7, 0x95F2, 0xCFD0,\n\t0x95F3, 0xE3C8, 0x95F4, 0xBCE4, 0x95F5, 0xE3C9, 0x95F6, 0xE3CA,\n\t0x95F7, 0xC3C6, 0x95F8, 0xD5A2, 0x95F9, 0xC4D6, 0x95FA, 0xB9EB,\n\t0x95FB, 0xCEC5, 0x95FC, 0xE3CB, 0x95FD, 0xC3F6, 0x95FE, 0xE3CC,\n\t0x95FF, 0xEA5D, 0x9600, 0xB7A7, 0x9601, 0xB8F3, 0x9602, 0xBAD2,\n\t0x9603, 0xE3CD, 0x9604, 0xE3CE, 0x9605, 0xD4C4, 0x9606, 0xE3CF,\n\t0x9607, 0xEA5E, 0x9608, 0xE3D0, 0x9609, 0xD1CB, 0x960A, 0xE3D1,\n\t0x960B, 0xE3D2, 0x960C, 0xE3D3, 0x960D, 0xE3D4, 0x960E, 0xD1D6,\n\t0x960F, 0xE3D5, 0x9610, 0xB2FB, 0x9611, 0xC0BB, 0x9612, 0xE3D6,\n\t0x9613, 0xEA5F, 0x9614, 0xC0AB, 0x9615, 0xE3D7, 0x9616, 0xE3D8,\n\t0x9617, 0xE3D9, 0x9618, 0xEA60, 0x9619, 0xE3DA, 0x961A, 0xE3DB,\n\t0x961B, 0xEA61, 0x961C, 0xB8B7, 0x961D, 0xDAE2, 0x961E, 0xEA62,\n\t0x961F, 0xB6D3, 0x9620, 0xEA63, 0x9621, 0xDAE4, 0x9622, 0xDAE3,\n\t0x9623, 0xEA64, 0x9624, 0xEA65, 0x9625, 0xEA66, 0x9626, 0xEA67,\n\t0x9627, 0xEA68, 0x9628, 0xEA69, 0x9629, 0xEA6A, 0x962A, 0xDAE6,\n\t0x962B, 0xEA6B, 0x962C, 0xEA6C, 0x962D, 0xEA6D, 0x962E, 0xC8EE,\n\t0x962F, 0xEA6E, 0x9630, 0xEA6F, 0x9631, 0xDAE5, 0x9632, 0xB7C0,\n\t0x9633, 0xD1F4, 0x9634, 0xD2F5, 0x9635, 0xD5F3, 0x9636, 0xBDD7,\n\t0x9637, 0xEA70, 0x9638, 0xEA71, 0x9639, 0xEA72, 0x963A, 0xEA73,\n\t0x963B, 0xD7E8, 0x963C, 0xDAE8, 0x963D, 0xDAE7, 0x963E, 0xEA74,\n\t0x963F, 0xB0A2, 0x9640, 0xCDD3, 0x9641, 0xEA75, 0x9642, 0xDAE9,\n\t0x9643, 0xEA76, 0x9644, 0xB8BD, 0x9645, 0xBCCA, 0x9646, 0xC2BD,\n\t0x9647, 0xC2A4, 0x9648, 0xB3C2, 0x9649, 0xDAEA, 0x964A, 0xEA77,\n\t0x964B, 0xC2AA, 0x964C, 0xC4B0, 0x964D, 0xBDB5, 0x964E, 0xEA78,\n\t0x964F, 0xEA79, 0x9650, 0xCFDE, 0x9651, 0xEA7A, 0x9652, 0xEA7B,\n\t0x9653, 0xEA7C, 0x9654, 0xDAEB, 0x9655, 0xC9C2, 0x9656, 0xEA7D,\n\t0x9657, 0xEA7E, 0x9658, 0xEA80, 0x9659, 0xEA81, 0x965A, 0xEA82,\n\t0x965B, 0xB1DD, 0x965C, 0xEA83, 0x965D, 0xEA84, 0x965E, 0xEA85,\n\t0x965F, 0xDAEC, 0x9660, 0xEA86, 0x9661, 0xB6B8, 0x9662, 0xD4BA,\n\t0x9663, 0xEA87, 0x9664, 0xB3FD, 0x9665, 0xEA88, 0x9666, 0xEA89,\n\t0x9667, 0xDAED, 0x9668, 0xD4C9, 0x9669, 0xCFD5, 0x966A, 0xC5E3,\n\t0x966B, 0xEA8A, 0x966C, 0xDAEE, 0x966D, 0xEA8B, 0x966E, 0xEA8C,\n\t0x966F, 0xEA8D, 0x9670, 0xEA8E, 0x9671, 0xEA8F, 0x9672, 0xDAEF,\n\t0x9673, 0xEA90, 0x9674, 0xDAF0, 0x9675, 0xC1EA, 0x9676, 0xCCD5,\n\t0x9677, 0xCFDD, 0x9678, 0xEA91, 0x9679, 0xEA92, 0x967A, 0xEA93,\n\t0x967B, 0xEA94, 0x967C, 0xEA95, 0x967D, 0xEA96, 0x967E, 0xEA97,\n\t0x967F, 0xEA98, 0x9680, 0xEA99, 0x9681, 0xEA9A, 0x9682, 0xEA9B,\n\t0x9683, 0xEA9C, 0x9684, 0xEA9D, 0x9685, 0xD3E7, 0x9686, 0xC2A1,\n\t0x9687, 0xEA9E, 0x9688, 0xDAF1, 0x9689, 0xEA9F, 0x968A, 0xEAA0,\n\t0x968B, 0xCBE5, 0x968C, 0xEB40, 0x968D, 0xDAF2, 0x968E, 0xEB41,\n\t0x968F, 0xCBE6, 0x9690, 0xD2FE, 0x9691, 0xEB42, 0x9692, 0xEB43,\n\t0x9693, 0xEB44, 0x9694, 0xB8F4, 0x9695, 0xEB45, 0x9696, 0xEB46,\n\t0x9697, 0xDAF3, 0x9698, 0xB0AF, 0x9699, 0xCFB6, 0x969A, 0xEB47,\n\t0x969B, 0xEB48, 0x969C, 0xD5CF, 0x969D, 0xEB49, 0x969E, 0xEB4A,\n\t0x969F, 0xEB4B, 0x96A0, 0xEB4C, 0x96A1, 0xEB4D, 0x96A2, 0xEB4E,\n\t0x96A3, 0xEB4F, 0x96A4, 0xEB50, 0x96A5, 0xEB51, 0x96A6, 0xEB52,\n\t0x96A7, 0xCBED, 0x96A8, 0xEB53, 0x96A9, 0xEB54, 0x96AA, 0xEB55,\n\t0x96AB, 0xEB56, 0x96AC, 0xEB57, 0x96AD, 0xEB58, 0x96AE, 0xEB59,\n\t0x96AF, 0xEB5A, 0x96B0, 0xDAF4, 0x96B1, 0xEB5B, 0x96B2, 0xEB5C,\n\t0x96B3, 0xE3C4, 0x96B4, 0xEB5D, 0x96B5, 0xEB5E, 0x96B6, 0xC1A5,\n\t0x96B7, 0xEB5F, 0x96B8, 0xEB60, 0x96B9, 0xF6BF, 0x96BA, 0xEB61,\n\t0x96BB, 0xEB62, 0x96BC, 0xF6C0, 0x96BD, 0xF6C1, 0x96BE, 0xC4D1,\n\t0x96BF, 0xEB63, 0x96C0, 0xC8B8, 0x96C1, 0xD1E3, 0x96C2, 0xEB64,\n\t0x96C3, 0xEB65, 0x96C4, 0xD0DB, 0x96C5, 0xD1C5, 0x96C6, 0xBCAF,\n\t0x96C7, 0xB9CD, 0x96C8, 0xEB66, 0x96C9, 0xEFF4, 0x96CA, 0xEB67,\n\t0x96CB, 0xEB68, 0x96CC, 0xB4C6, 0x96CD, 0xD3BA, 0x96CE, 0xF6C2,\n\t0x96CF, 0xB3FB, 0x96D0, 0xEB69, 0x96D1, 0xEB6A, 0x96D2, 0xF6C3,\n\t0x96D3, 0xEB6B, 0x96D4, 0xEB6C, 0x96D5, 0xB5F1, 0x96D6, 0xEB6D,\n\t0x96D7, 0xEB6E, 0x96D8, 0xEB6F, 0x96D9, 0xEB70, 0x96DA, 0xEB71,\n\t0x96DB, 0xEB72, 0x96DC, 0xEB73, 0x96DD, 0xEB74, 0x96DE, 0xEB75,\n\t0x96DF, 0xEB76, 0x96E0, 0xF6C5, 0x96E1, 0xEB77, 0x96E2, 0xEB78,\n\t0x96E3, 0xEB79, 0x96E4, 0xEB7A, 0x96E5, 0xEB7B, 0x96E6, 0xEB7C,\n\t0x96E7, 0xEB7D, 0x96E8, 0xD3EA, 0x96E9, 0xF6A7, 0x96EA, 0xD1A9,\n\t0x96EB, 0xEB7E, 0x96EC, 0xEB80, 0x96ED, 0xEB81, 0x96EE, 0xEB82,\n\t0x96EF, 0xF6A9, 0x96F0, 0xEB83, 0x96F1, 0xEB84, 0x96F2, 0xEB85,\n\t0x96F3, 0xF6A8, 0x96F4, 0xEB86, 0x96F5, 0xEB87, 0x96F6, 0xC1E3,\n\t0x96F7, 0xC0D7, 0x96F8, 0xEB88, 0x96F9, 0xB1A2, 0x96FA, 0xEB89,\n\t0x96FB, 0xEB8A, 0x96FC, 0xEB8B, 0x96FD, 0xEB8C, 0x96FE, 0xCEED,\n\t0x96FF, 0xEB8D, 0x9700, 0xD0E8, 0x9701, 0xF6AB, 0x9702, 0xEB8E,\n\t0x9703, 0xEB8F, 0x9704, 0xCFF6, 0x9705, 0xEB90, 0x9706, 0xF6AA,\n\t0x9707, 0xD5F0, 0x9708, 0xF6AC, 0x9709, 0xC3B9, 0x970A, 0xEB91,\n\t0x970B, 0xEB92, 0x970C, 0xEB93, 0x970D, 0xBBF4, 0x970E, 0xF6AE,\n\t0x970F, 0xF6AD, 0x9710, 0xEB94, 0x9711, 0xEB95, 0x9712, 0xEB96,\n\t0x9713, 0xC4DE, 0x9714, 0xEB97, 0x9715, 0xEB98, 0x9716, 0xC1D8,\n\t0x9717, 0xEB99, 0x9718, 0xEB9A, 0x9719, 0xEB9B, 0x971A, 0xEB9C,\n\t0x971B, 0xEB9D, 0x971C, 0xCBAA, 0x971D, 0xEB9E, 0x971E, 0xCFBC,\n\t0x971F, 0xEB9F, 0x9720, 0xEBA0, 0x9721, 0xEC40, 0x9722, 0xEC41,\n\t0x9723, 0xEC42, 0x9724, 0xEC43, 0x9725, 0xEC44, 0x9726, 0xEC45,\n\t0x9727, 0xEC46, 0x9728, 0xEC47, 0x9729, 0xEC48, 0x972A, 0xF6AF,\n\t0x972B, 0xEC49, 0x972C, 0xEC4A, 0x972D, 0xF6B0, 0x972E, 0xEC4B,\n\t0x972F, 0xEC4C, 0x9730, 0xF6B1, 0x9731, 0xEC4D, 0x9732, 0xC2B6,\n\t0x9733, 0xEC4E, 0x9734, 0xEC4F, 0x9735, 0xEC50, 0x9736, 0xEC51,\n\t0x9737, 0xEC52, 0x9738, 0xB0D4, 0x9739, 0xC5F9, 0x973A, 0xEC53,\n\t0x973B, 0xEC54, 0x973C, 0xEC55, 0x973D, 0xEC56, 0x973E, 0xF6B2,\n\t0x973F, 0xEC57, 0x9740, 0xEC58, 0x9741, 0xEC59, 0x9742, 0xEC5A,\n\t0x9743, 0xEC5B, 0x9744, 0xEC5C, 0x9745, 0xEC5D, 0x9746, 0xEC5E,\n\t0x9747, 0xEC5F, 0x9748, 0xEC60, 0x9749, 0xEC61, 0x974A, 0xEC62,\n\t0x974B, 0xEC63, 0x974C, 0xEC64, 0x974D, 0xEC65, 0x974E, 0xEC66,\n\t0x974F, 0xEC67, 0x9750, 0xEC68, 0x9751, 0xEC69, 0x9752, 0xC7E0,\n\t0x9753, 0xF6A6, 0x9754, 0xEC6A, 0x9755, 0xEC6B, 0x9756, 0xBEB8,\n\t0x9757, 0xEC6C, 0x9758, 0xEC6D, 0x9759, 0xBEB2, 0x975A, 0xEC6E,\n\t0x975B, 0xB5E5, 0x975C, 0xEC6F, 0x975D, 0xEC70, 0x975E, 0xB7C7,\n\t0x975F, 0xEC71, 0x9760, 0xBFBF, 0x9761, 0xC3D2, 0x9762, 0xC3E6,\n\t0x9763, 0xEC72, 0x9764, 0xEC73, 0x9765, 0xD8CC, 0x9766, 0xEC74,\n\t0x9767, 0xEC75, 0x9768, 0xEC76, 0x9769, 0xB8EF, 0x976A, 0xEC77,\n\t0x976B, 0xEC78, 0x976C, 0xEC79, 0x976D, 0xEC7A, 0x976E, 0xEC7B,\n\t0x976F, 0xEC7C, 0x9770, 0xEC7D, 0x9771, 0xEC7E, 0x9772, 0xEC80,\n\t0x9773, 0xBDF9, 0x9774, 0xD1A5, 0x9775, 0xEC81, 0x9776, 0xB0D0,\n\t0x9777, 0xEC82, 0x9778, 0xEC83, 0x9779, 0xEC84, 0x977A, 0xEC85,\n\t0x977B, 0xEC86, 0x977C, 0xF7B0, 0x977D, 0xEC87, 0x977E, 0xEC88,\n\t0x977F, 0xEC89, 0x9780, 0xEC8A, 0x9781, 0xEC8B, 0x9782, 0xEC8C,\n\t0x9783, 0xEC8D, 0x9784, 0xEC8E, 0x9785, 0xF7B1, 0x9786, 0xEC8F,\n\t0x9787, 0xEC90, 0x9788, 0xEC91, 0x9789, 0xEC92, 0x978A, 0xEC93,\n\t0x978B, 0xD0AC, 0x978C, 0xEC94, 0x978D, 0xB0B0, 0x978E, 0xEC95,\n\t0x978F, 0xEC96, 0x9790, 0xEC97, 0x9791, 0xF7B2, 0x9792, 0xF7B3,\n\t0x9793, 0xEC98, 0x9794, 0xF7B4, 0x9795, 0xEC99, 0x9796, 0xEC9A,\n\t0x9797, 0xEC9B, 0x9798, 0xC7CA, 0x9799, 0xEC9C, 0x979A, 0xEC9D,\n\t0x979B, 0xEC9E, 0x979C, 0xEC9F, 0x979D, 0xECA0, 0x979E, 0xED40,\n\t0x979F, 0xED41, 0x97A0, 0xBECF, 0x97A1, 0xED42, 0x97A2, 0xED43,\n\t0x97A3, 0xF7B7, 0x97A4, 0xED44, 0x97A5, 0xED45, 0x97A6, 0xED46,\n\t0x97A7, 0xED47, 0x97A8, 0xED48, 0x97A9, 0xED49, 0x97AA, 0xED4A,\n\t0x97AB, 0xF7B6, 0x97AC, 0xED4B, 0x97AD, 0xB1DE, 0x97AE, 0xED4C,\n\t0x97AF, 0xF7B5, 0x97B0, 0xED4D, 0x97B1, 0xED4E, 0x97B2, 0xF7B8,\n\t0x97B3, 0xED4F, 0x97B4, 0xF7B9, 0x97B5, 0xED50, 0x97B6, 0xED51,\n\t0x97B7, 0xED52, 0x97B8, 0xED53, 0x97B9, 0xED54, 0x97BA, 0xED55,\n\t0x97BB, 0xED56, 0x97BC, 0xED57, 0x97BD, 0xED58, 0x97BE, 0xED59,\n\t0x97BF, 0xED5A, 0x97C0, 0xED5B, 0x97C1, 0xED5C, 0x97C2, 0xED5D,\n\t0x97C3, 0xED5E, 0x97C4, 0xED5F, 0x97C5, 0xED60, 0x97C6, 0xED61,\n\t0x97C7, 0xED62, 0x97C8, 0xED63, 0x97C9, 0xED64, 0x97CA, 0xED65,\n\t0x97CB, 0xED66, 0x97CC, 0xED67, 0x97CD, 0xED68, 0x97CE, 0xED69,\n\t0x97CF, 0xED6A, 0x97D0, 0xED6B, 0x97D1, 0xED6C, 0x97D2, 0xED6D,\n\t0x97D3, 0xED6E, 0x97D4, 0xED6F, 0x97D5, 0xED70, 0x97D6, 0xED71,\n\t0x97D7, 0xED72, 0x97D8, 0xED73, 0x97D9, 0xED74, 0x97DA, 0xED75,\n\t0x97DB, 0xED76, 0x97DC, 0xED77, 0x97DD, 0xED78, 0x97DE, 0xED79,\n\t0x97DF, 0xED7A, 0x97E0, 0xED7B, 0x97E1, 0xED7C, 0x97E2, 0xED7D,\n\t0x97E3, 0xED7E, 0x97E4, 0xED80, 0x97E5, 0xED81, 0x97E6, 0xCEA4,\n\t0x97E7, 0xC8CD, 0x97E8, 0xED82, 0x97E9, 0xBAAB, 0x97EA, 0xE8B8,\n\t0x97EB, 0xE8B9, 0x97EC, 0xE8BA, 0x97ED, 0xBEC2, 0x97EE, 0xED83,\n\t0x97EF, 0xED84, 0x97F0, 0xED85, 0x97F1, 0xED86, 0x97F2, 0xED87,\n\t0x97F3, 0xD2F4, 0x97F4, 0xED88, 0x97F5, 0xD4CF, 0x97F6, 0xC9D8,\n\t0x97F7, 0xED89, 0x97F8, 0xED8A, 0x97F9, 0xED8B, 0x97FA, 0xED8C,\n\t0x97FB, 0xED8D, 0x97FC, 0xED8E, 0x97FD, 0xED8F, 0x97FE, 0xED90,\n\t0x97FF, 0xED91, 0x9800, 0xED92, 0x9801, 0xED93, 0x9802, 0xED94,\n\t0x9803, 0xED95, 0x9804, 0xED96, 0x9805, 0xED97, 0x9806, 0xED98,\n\t0x9807, 0xED99, 0x9808, 0xED9A, 0x9809, 0xED9B, 0x980A, 0xED9C,\n\t0x980B, 0xED9D, 0x980C, 0xED9E, 0x980D, 0xED9F, 0x980E, 0xEDA0,\n\t0x980F, 0xEE40, 0x9810, 0xEE41, 0x9811, 0xEE42, 0x9812, 0xEE43,\n\t0x9813, 0xEE44, 0x9814, 0xEE45, 0x9815, 0xEE46, 0x9816, 0xEE47,\n\t0x9817, 0xEE48, 0x9818, 0xEE49, 0x9819, 0xEE4A, 0x981A, 0xEE4B,\n\t0x981B, 0xEE4C, 0x981C, 0xEE4D, 0x981D, 0xEE4E, 0x981E, 0xEE4F,\n\t0x981F, 0xEE50, 0x9820, 0xEE51, 0x9821, 0xEE52, 0x9822, 0xEE53,\n\t0x9823, 0xEE54, 0x9824, 0xEE55, 0x9825, 0xEE56, 0x9826, 0xEE57,\n\t0x9827, 0xEE58, 0x9828, 0xEE59, 0x9829, 0xEE5A, 0x982A, 0xEE5B,\n\t0x982B, 0xEE5C, 0x982C, 0xEE5D, 0x982D, 0xEE5E, 0x982E, 0xEE5F,\n\t0x982F, 0xEE60, 0x9830, 0xEE61, 0x9831, 0xEE62, 0x9832, 0xEE63,\n\t0x9833, 0xEE64, 0x9834, 0xEE65, 0x9835, 0xEE66, 0x9836, 0xEE67,\n\t0x9837, 0xEE68, 0x9838, 0xEE69, 0x9839, 0xEE6A, 0x983A, 0xEE6B,\n\t0x983B, 0xEE6C, 0x983C, 0xEE6D, 0x983D, 0xEE6E, 0x983E, 0xEE6F,\n\t0x983F, 0xEE70, 0x9840, 0xEE71, 0x9841, 0xEE72, 0x9842, 0xEE73,\n\t0x9843, 0xEE74, 0x9844, 0xEE75, 0x9845, 0xEE76, 0x9846, 0xEE77,\n\t0x9847, 0xEE78, 0x9848, 0xEE79, 0x9849, 0xEE7A, 0x984A, 0xEE7B,\n\t0x984B, 0xEE7C, 0x984C, 0xEE7D, 0x984D, 0xEE7E, 0x984E, 0xEE80,\n\t0x984F, 0xEE81, 0x9850, 0xEE82, 0x9851, 0xEE83, 0x9852, 0xEE84,\n\t0x9853, 0xEE85, 0x9854, 0xEE86, 0x9855, 0xEE87, 0x9856, 0xEE88,\n\t0x9857, 0xEE89, 0x9858, 0xEE8A, 0x9859, 0xEE8B, 0x985A, 0xEE8C,\n\t0x985B, 0xEE8D, 0x985C, 0xEE8E, 0x985D, 0xEE8F, 0x985E, 0xEE90,\n\t0x985F, 0xEE91, 0x9860, 0xEE92, 0x9861, 0xEE93, 0x9862, 0xEE94,\n\t0x9863, 0xEE95, 0x9864, 0xEE96, 0x9865, 0xEE97, 0x9866, 0xEE98,\n\t0x9867, 0xEE99, 0x9868, 0xEE9A, 0x9869, 0xEE9B, 0x986A, 0xEE9C,\n\t0x986B, 0xEE9D, 0x986C, 0xEE9E, 0x986D, 0xEE9F, 0x986E, 0xEEA0,\n\t0x986F, 0xEF40, 0x9870, 0xEF41, 0x9871, 0xEF42, 0x9872, 0xEF43,\n\t0x9873, 0xEF44, 0x9874, 0xEF45, 0x9875, 0xD2B3, 0x9876, 0xB6A5,\n\t0x9877, 0xC7EA, 0x9878, 0xF1FC, 0x9879, 0xCFEE, 0x987A, 0xCBB3,\n\t0x987B, 0xD0EB, 0x987C, 0xE7EF, 0x987D, 0xCDE7, 0x987E, 0xB9CB,\n\t0x987F, 0xB6D9, 0x9880, 0xF1FD, 0x9881, 0xB0E4, 0x9882, 0xCBCC,\n\t0x9883, 0xF1FE, 0x9884, 0xD4A4, 0x9885, 0xC2AD, 0x9886, 0xC1EC,\n\t0x9887, 0xC6C4, 0x9888, 0xBEB1, 0x9889, 0xF2A1, 0x988A, 0xBCD5,\n\t0x988B, 0xEF46, 0x988C, 0xF2A2, 0x988D, 0xF2A3, 0x988E, 0xEF47,\n\t0x988F, 0xF2A4, 0x9890, 0xD2C3, 0x9891, 0xC6B5, 0x9892, 0xEF48,\n\t0x9893, 0xCDC7, 0x9894, 0xF2A5, 0x9895, 0xEF49, 0x9896, 0xD3B1,\n\t0x9897, 0xBFC5, 0x9898, 0xCCE2, 0x9899, 0xEF4A, 0x989A, 0xF2A6,\n\t0x989B, 0xF2A7, 0x989C, 0xD1D5, 0x989D, 0xB6EE, 0x989E, 0xF2A8,\n\t0x989F, 0xF2A9, 0x98A0, 0xB5DF, 0x98A1, 0xF2AA, 0x98A2, 0xF2AB,\n\t0x98A3, 0xEF4B, 0x98A4, 0xB2FC, 0x98A5, 0xF2AC, 0x98A6, 0xF2AD,\n\t0x98A7, 0xC8A7, 0x98A8, 0xEF4C, 0x98A9, 0xEF4D, 0x98AA, 0xEF4E,\n\t0x98AB, 0xEF4F, 0x98AC, 0xEF50, 0x98AD, 0xEF51, 0x98AE, 0xEF52,\n\t0x98AF, 0xEF53, 0x98B0, 0xEF54, 0x98B1, 0xEF55, 0x98B2, 0xEF56,\n\t0x98B3, 0xEF57, 0x98B4, 0xEF58, 0x98B5, 0xEF59, 0x98B6, 0xEF5A,\n\t0x98B7, 0xEF5B, 0x98B8, 0xEF5C, 0x98B9, 0xEF5D, 0x98BA, 0xEF5E,\n\t0x98BB, 0xEF5F, 0x98BC, 0xEF60, 0x98BD, 0xEF61, 0x98BE, 0xEF62,\n\t0x98BF, 0xEF63, 0x98C0, 0xEF64, 0x98C1, 0xEF65, 0x98C2, 0xEF66,\n\t0x98C3, 0xEF67, 0x98C4, 0xEF68, 0x98C5, 0xEF69, 0x98C6, 0xEF6A,\n\t0x98C7, 0xEF6B, 0x98C8, 0xEF6C, 0x98C9, 0xEF6D, 0x98CA, 0xEF6E,\n\t0x98CB, 0xEF6F, 0x98CC, 0xEF70, 0x98CD, 0xEF71, 0x98CE, 0xB7E7,\n\t0x98CF, 0xEF72, 0x98D0, 0xEF73, 0x98D1, 0xECA9, 0x98D2, 0xECAA,\n\t0x98D3, 0xECAB, 0x98D4, 0xEF74, 0x98D5, 0xECAC, 0x98D6, 0xEF75,\n\t0x98D7, 0xEF76, 0x98D8, 0xC6AE, 0x98D9, 0xECAD, 0x98DA, 0xECAE,\n\t0x98DB, 0xEF77, 0x98DC, 0xEF78, 0x98DD, 0xEF79, 0x98DE, 0xB7C9,\n\t0x98DF, 0xCAB3, 0x98E0, 0xEF7A, 0x98E1, 0xEF7B, 0x98E2, 0xEF7C,\n\t0x98E3, 0xEF7D, 0x98E4, 0xEF7E, 0x98E5, 0xEF80, 0x98E6, 0xEF81,\n\t0x98E7, 0xE2B8, 0x98E8, 0xF7CF, 0x98E9, 0xEF82, 0x98EA, 0xEF83,\n\t0x98EB, 0xEF84, 0x98EC, 0xEF85, 0x98ED, 0xEF86, 0x98EE, 0xEF87,\n\t0x98EF, 0xEF88, 0x98F0, 0xEF89, 0x98F1, 0xEF8A, 0x98F2, 0xEF8B,\n\t0x98F3, 0xEF8C, 0x98F4, 0xEF8D, 0x98F5, 0xEF8E, 0x98F6, 0xEF8F,\n\t0x98F7, 0xEF90, 0x98F8, 0xEF91, 0x98F9, 0xEF92, 0x98FA, 0xEF93,\n\t0x98FB, 0xEF94, 0x98FC, 0xEF95, 0x98FD, 0xEF96, 0x98FE, 0xEF97,\n\t0x98FF, 0xEF98, 0x9900, 0xEF99, 0x9901, 0xEF9A, 0x9902, 0xEF9B,\n\t0x9903, 0xEF9C, 0x9904, 0xEF9D, 0x9905, 0xEF9E, 0x9906, 0xEF9F,\n\t0x9907, 0xEFA0, 0x9908, 0xF040, 0x9909, 0xF041, 0x990A, 0xF042,\n\t0x990B, 0xF043, 0x990C, 0xF044, 0x990D, 0xF7D0, 0x990E, 0xF045,\n\t0x990F, 0xF046, 0x9910, 0xB2CD, 0x9911, 0xF047, 0x9912, 0xF048,\n\t0x9913, 0xF049, 0x9914, 0xF04A, 0x9915, 0xF04B, 0x9916, 0xF04C,\n\t0x9917, 0xF04D, 0x9918, 0xF04E, 0x9919, 0xF04F, 0x991A, 0xF050,\n\t0x991B, 0xF051, 0x991C, 0xF052, 0x991D, 0xF053, 0x991E, 0xF054,\n\t0x991F, 0xF055, 0x9920, 0xF056, 0x9921, 0xF057, 0x9922, 0xF058,\n\t0x9923, 0xF059, 0x9924, 0xF05A, 0x9925, 0xF05B, 0x9926, 0xF05C,\n\t0x9927, 0xF05D, 0x9928, 0xF05E, 0x9929, 0xF05F, 0x992A, 0xF060,\n\t0x992B, 0xF061, 0x992C, 0xF062, 0x992D, 0xF063, 0x992E, 0xF7D1,\n\t0x992F, 0xF064, 0x9930, 0xF065, 0x9931, 0xF066, 0x9932, 0xF067,\n\t0x9933, 0xF068, 0x9934, 0xF069, 0x9935, 0xF06A, 0x9936, 0xF06B,\n\t0x9937, 0xF06C, 0x9938, 0xF06D, 0x9939, 0xF06E, 0x993A, 0xF06F,\n\t0x993B, 0xF070, 0x993C, 0xF071, 0x993D, 0xF072, 0x993E, 0xF073,\n\t0x993F, 0xF074, 0x9940, 0xF075, 0x9941, 0xF076, 0x9942, 0xF077,\n\t0x9943, 0xF078, 0x9944, 0xF079, 0x9945, 0xF07A, 0x9946, 0xF07B,\n\t0x9947, 0xF07C, 0x9948, 0xF07D, 0x9949, 0xF07E, 0x994A, 0xF080,\n\t0x994B, 0xF081, 0x994C, 0xF082, 0x994D, 0xF083, 0x994E, 0xF084,\n\t0x994F, 0xF085, 0x9950, 0xF086, 0x9951, 0xF087, 0x9952, 0xF088,\n\t0x9953, 0xF089, 0x9954, 0xF7D3, 0x9955, 0xF7D2, 0x9956, 0xF08A,\n\t0x9957, 0xF08B, 0x9958, 0xF08C, 0x9959, 0xF08D, 0x995A, 0xF08E,\n\t0x995B, 0xF08F, 0x995C, 0xF090, 0x995D, 0xF091, 0x995E, 0xF092,\n\t0x995F, 0xF093, 0x9960, 0xF094, 0x9961, 0xF095, 0x9962, 0xF096,\n\t0x9963, 0xE2BB, 0x9964, 0xF097, 0x9965, 0xBCA2, 0x9966, 0xF098,\n\t0x9967, 0xE2BC, 0x9968, 0xE2BD, 0x9969, 0xE2BE, 0x996A, 0xE2BF,\n\t0x996B, 0xE2C0, 0x996C, 0xE2C1, 0x996D, 0xB7B9, 0x996E, 0xD2FB,\n\t0x996F, 0xBDA4, 0x9970, 0xCACE, 0x9971, 0xB1A5, 0x9972, 0xCBC7,\n\t0x9973, 0xF099, 0x9974, 0xE2C2, 0x9975, 0xB6FC, 0x9976, 0xC8C4,\n\t0x9977, 0xE2C3, 0x9978, 0xF09A, 0x9979, 0xF09B, 0x997A, 0xBDC8,\n\t0x997B, 0xF09C, 0x997C, 0xB1FD, 0x997D, 0xE2C4, 0x997E, 0xF09D,\n\t0x997F, 0xB6F6, 0x9980, 0xE2C5, 0x9981, 0xC4D9, 0x9982, 0xF09E,\n\t0x9983, 0xF09F, 0x9984, 0xE2C6, 0x9985, 0xCFDA, 0x9986, 0xB9DD,\n\t0x9987, 0xE2C7, 0x9988, 0xC0A1, 0x9989, 0xF0A0, 0x998A, 0xE2C8,\n\t0x998B, 0xB2F6, 0x998C, 0xF140, 0x998D, 0xE2C9, 0x998E, 0xF141,\n\t0x998F, 0xC1F3, 0x9990, 0xE2CA, 0x9991, 0xE2CB, 0x9992, 0xC2F8,\n\t0x9993, 0xE2CC, 0x9994, 0xE2CD, 0x9995, 0xE2CE, 0x9996, 0xCAD7,\n\t0x9997, 0xD8B8, 0x9998, 0xD9E5, 0x9999, 0xCFE3, 0x999A, 0xF142,\n\t0x999B, 0xF143, 0x999C, 0xF144, 0x999D, 0xF145, 0x999E, 0xF146,\n\t0x999F, 0xF147, 0x99A0, 0xF148, 0x99A1, 0xF149, 0x99A2, 0xF14A,\n\t0x99A3, 0xF14B, 0x99A4, 0xF14C, 0x99A5, 0xF0A5, 0x99A6, 0xF14D,\n\t0x99A7, 0xF14E, 0x99A8, 0xDCB0, 0x99A9, 0xF14F, 0x99AA, 0xF150,\n\t0x99AB, 0xF151, 0x99AC, 0xF152, 0x99AD, 0xF153, 0x99AE, 0xF154,\n\t0x99AF, 0xF155, 0x99B0, 0xF156, 0x99B1, 0xF157, 0x99B2, 0xF158,\n\t0x99B3, 0xF159, 0x99B4, 0xF15A, 0x99B5, 0xF15B, 0x99B6, 0xF15C,\n\t0x99B7, 0xF15D, 0x99B8, 0xF15E, 0x99B9, 0xF15F, 0x99BA, 0xF160,\n\t0x99BB, 0xF161, 0x99BC, 0xF162, 0x99BD, 0xF163, 0x99BE, 0xF164,\n\t0x99BF, 0xF165, 0x99C0, 0xF166, 0x99C1, 0xF167, 0x99C2, 0xF168,\n\t0x99C3, 0xF169, 0x99C4, 0xF16A, 0x99C5, 0xF16B, 0x99C6, 0xF16C,\n\t0x99C7, 0xF16D, 0x99C8, 0xF16E, 0x99C9, 0xF16F, 0x99CA, 0xF170,\n\t0x99CB, 0xF171, 0x99CC, 0xF172, 0x99CD, 0xF173, 0x99CE, 0xF174,\n\t0x99CF, 0xF175, 0x99D0, 0xF176, 0x99D1, 0xF177, 0x99D2, 0xF178,\n\t0x99D3, 0xF179, 0x99D4, 0xF17A, 0x99D5, 0xF17B, 0x99D6, 0xF17C,\n\t0x99D7, 0xF17D, 0x99D8, 0xF17E, 0x99D9, 0xF180, 0x99DA, 0xF181,\n\t0x99DB, 0xF182, 0x99DC, 0xF183, 0x99DD, 0xF184, 0x99DE, 0xF185,\n\t0x99DF, 0xF186, 0x99E0, 0xF187, 0x99E1, 0xF188, 0x99E2, 0xF189,\n\t0x99E3, 0xF18A, 0x99E4, 0xF18B, 0x99E5, 0xF18C, 0x99E6, 0xF18D,\n\t0x99E7, 0xF18E, 0x99E8, 0xF18F, 0x99E9, 0xF190, 0x99EA, 0xF191,\n\t0x99EB, 0xF192, 0x99EC, 0xF193, 0x99ED, 0xF194, 0x99EE, 0xF195,\n\t0x99EF, 0xF196, 0x99F0, 0xF197, 0x99F1, 0xF198, 0x99F2, 0xF199,\n\t0x99F3, 0xF19A, 0x99F4, 0xF19B, 0x99F5, 0xF19C, 0x99F6, 0xF19D,\n\t0x99F7, 0xF19E, 0x99F8, 0xF19F, 0x99F9, 0xF1A0, 0x99FA, 0xF240,\n\t0x99FB, 0xF241, 0x99FC, 0xF242, 0x99FD, 0xF243, 0x99FE, 0xF244,\n\t0x99FF, 0xF245, 0x9A00, 0xF246, 0x9A01, 0xF247, 0x9A02, 0xF248,\n\t0x9A03, 0xF249, 0x9A04, 0xF24A, 0x9A05, 0xF24B, 0x9A06, 0xF24C,\n\t0x9A07, 0xF24D, 0x9A08, 0xF24E, 0x9A09, 0xF24F, 0x9A0A, 0xF250,\n\t0x9A0B, 0xF251, 0x9A0C, 0xF252, 0x9A0D, 0xF253, 0x9A0E, 0xF254,\n\t0x9A0F, 0xF255, 0x9A10, 0xF256, 0x9A11, 0xF257, 0x9A12, 0xF258,\n\t0x9A13, 0xF259, 0x9A14, 0xF25A, 0x9A15, 0xF25B, 0x9A16, 0xF25C,\n\t0x9A17, 0xF25D, 0x9A18, 0xF25E, 0x9A19, 0xF25F, 0x9A1A, 0xF260,\n\t0x9A1B, 0xF261, 0x9A1C, 0xF262, 0x9A1D, 0xF263, 0x9A1E, 0xF264,\n\t0x9A1F, 0xF265, 0x9A20, 0xF266, 0x9A21, 0xF267, 0x9A22, 0xF268,\n\t0x9A23, 0xF269, 0x9A24, 0xF26A, 0x9A25, 0xF26B, 0x9A26, 0xF26C,\n\t0x9A27, 0xF26D, 0x9A28, 0xF26E, 0x9A29, 0xF26F, 0x9A2A, 0xF270,\n\t0x9A2B, 0xF271, 0x9A2C, 0xF272, 0x9A2D, 0xF273, 0x9A2E, 0xF274,\n\t0x9A2F, 0xF275, 0x9A30, 0xF276, 0x9A31, 0xF277, 0x9A32, 0xF278,\n\t0x9A33, 0xF279, 0x9A34, 0xF27A, 0x9A35, 0xF27B, 0x9A36, 0xF27C,\n\t0x9A37, 0xF27D, 0x9A38, 0xF27E, 0x9A39, 0xF280, 0x9A3A, 0xF281,\n\t0x9A3B, 0xF282, 0x9A3C, 0xF283, 0x9A3D, 0xF284, 0x9A3E, 0xF285,\n\t0x9A3F, 0xF286, 0x9A40, 0xF287, 0x9A41, 0xF288, 0x9A42, 0xF289,\n\t0x9A43, 0xF28A, 0x9A44, 0xF28B, 0x9A45, 0xF28C, 0x9A46, 0xF28D,\n\t0x9A47, 0xF28E, 0x9A48, 0xF28F, 0x9A49, 0xF290, 0x9A4A, 0xF291,\n\t0x9A4B, 0xF292, 0x9A4C, 0xF293, 0x9A4D, 0xF294, 0x9A4E, 0xF295,\n\t0x9A4F, 0xF296, 0x9A50, 0xF297, 0x9A51, 0xF298, 0x9A52, 0xF299,\n\t0x9A53, 0xF29A, 0x9A54, 0xF29B, 0x9A55, 0xF29C, 0x9A56, 0xF29D,\n\t0x9A57, 0xF29E, 0x9A58, 0xF29F, 0x9A59, 0xF2A0, 0x9A5A, 0xF340,\n\t0x9A5B, 0xF341, 0x9A5C, 0xF342, 0x9A5D, 0xF343, 0x9A5E, 0xF344,\n\t0x9A5F, 0xF345, 0x9A60, 0xF346, 0x9A61, 0xF347, 0x9A62, 0xF348,\n\t0x9A63, 0xF349, 0x9A64, 0xF34A, 0x9A65, 0xF34B, 0x9A66, 0xF34C,\n\t0x9A67, 0xF34D, 0x9A68, 0xF34E, 0x9A69, 0xF34F, 0x9A6A, 0xF350,\n\t0x9A6B, 0xF351, 0x9A6C, 0xC2ED, 0x9A6D, 0xD4A6, 0x9A6E, 0xCDD4,\n\t0x9A6F, 0xD1B1, 0x9A70, 0xB3DB, 0x9A71, 0xC7FD, 0x9A72, 0xF352,\n\t0x9A73, 0xB2B5, 0x9A74, 0xC2BF, 0x9A75, 0xE6E0, 0x9A76, 0xCABB,\n\t0x9A77, 0xE6E1, 0x9A78, 0xE6E2, 0x9A79, 0xBED4, 0x9A7A, 0xE6E3,\n\t0x9A7B, 0xD7A4, 0x9A7C, 0xCDD5, 0x9A7D, 0xE6E5, 0x9A7E, 0xBCDD,\n\t0x9A7F, 0xE6E4, 0x9A80, 0xE6E6, 0x9A81, 0xE6E7, 0x9A82, 0xC2EE,\n\t0x9A83, 0xF353, 0x9A84, 0xBDBE, 0x9A85, 0xE6E8, 0x9A86, 0xC2E6,\n\t0x9A87, 0xBAA7, 0x9A88, 0xE6E9, 0x9A89, 0xF354, 0x9A8A, 0xE6EA,\n\t0x9A8B, 0xB3D2, 0x9A8C, 0xD1E9, 0x9A8D, 0xF355, 0x9A8E, 0xF356,\n\t0x9A8F, 0xBFA5, 0x9A90, 0xE6EB, 0x9A91, 0xC6EF, 0x9A92, 0xE6EC,\n\t0x9A93, 0xE6ED, 0x9A94, 0xF357, 0x9A95, 0xF358, 0x9A96, 0xE6EE,\n\t0x9A97, 0xC6AD, 0x9A98, 0xE6EF, 0x9A99, 0xF359, 0x9A9A, 0xC9A7,\n\t0x9A9B, 0xE6F0, 0x9A9C, 0xE6F1, 0x9A9D, 0xE6F2, 0x9A9E, 0xE5B9,\n\t0x9A9F, 0xE6F3, 0x9AA0, 0xE6F4, 0x9AA1, 0xC2E2, 0x9AA2, 0xE6F5,\n\t0x9AA3, 0xE6F6, 0x9AA4, 0xD6E8, 0x9AA5, 0xE6F7, 0x9AA6, 0xF35A,\n\t0x9AA7, 0xE6F8, 0x9AA8, 0xB9C7, 0x9AA9, 0xF35B, 0x9AAA, 0xF35C,\n\t0x9AAB, 0xF35D, 0x9AAC, 0xF35E, 0x9AAD, 0xF35F, 0x9AAE, 0xF360,\n\t0x9AAF, 0xF361, 0x9AB0, 0xF7BB, 0x9AB1, 0xF7BA, 0x9AB2, 0xF362,\n\t0x9AB3, 0xF363, 0x9AB4, 0xF364, 0x9AB5, 0xF365, 0x9AB6, 0xF7BE,\n\t0x9AB7, 0xF7BC, 0x9AB8, 0xBAA1, 0x9AB9, 0xF366, 0x9ABA, 0xF7BF,\n\t0x9ABB, 0xF367, 0x9ABC, 0xF7C0, 0x9ABD, 0xF368, 0x9ABE, 0xF369,\n\t0x9ABF, 0xF36A, 0x9AC0, 0xF7C2, 0x9AC1, 0xF7C1, 0x9AC2, 0xF7C4,\n\t0x9AC3, 0xF36B, 0x9AC4, 0xF36C, 0x9AC5, 0xF7C3, 0x9AC6, 0xF36D,\n\t0x9AC7, 0xF36E, 0x9AC8, 0xF36F, 0x9AC9, 0xF370, 0x9ACA, 0xF371,\n\t0x9ACB, 0xF7C5, 0x9ACC, 0xF7C6, 0x9ACD, 0xF372, 0x9ACE, 0xF373,\n\t0x9ACF, 0xF374, 0x9AD0, 0xF375, 0x9AD1, 0xF7C7, 0x9AD2, 0xF376,\n\t0x9AD3, 0xCBE8, 0x9AD4, 0xF377, 0x9AD5, 0xF378, 0x9AD6, 0xF379,\n\t0x9AD7, 0xF37A, 0x9AD8, 0xB8DF, 0x9AD9, 0xF37B, 0x9ADA, 0xF37C,\n\t0x9ADB, 0xF37D, 0x9ADC, 0xF37E, 0x9ADD, 0xF380, 0x9ADE, 0xF381,\n\t0x9ADF, 0xF7D4, 0x9AE0, 0xF382, 0x9AE1, 0xF7D5, 0x9AE2, 0xF383,\n\t0x9AE3, 0xF384, 0x9AE4, 0xF385, 0x9AE5, 0xF386, 0x9AE6, 0xF7D6,\n\t0x9AE7, 0xF387, 0x9AE8, 0xF388, 0x9AE9, 0xF389, 0x9AEA, 0xF38A,\n\t0x9AEB, 0xF7D8, 0x9AEC, 0xF38B, 0x9AED, 0xF7DA, 0x9AEE, 0xF38C,\n\t0x9AEF, 0xF7D7, 0x9AF0, 0xF38D, 0x9AF1, 0xF38E, 0x9AF2, 0xF38F,\n\t0x9AF3, 0xF390, 0x9AF4, 0xF391, 0x9AF5, 0xF392, 0x9AF6, 0xF393,\n\t0x9AF7, 0xF394, 0x9AF8, 0xF395, 0x9AF9, 0xF7DB, 0x9AFA, 0xF396,\n\t0x9AFB, 0xF7D9, 0x9AFC, 0xF397, 0x9AFD, 0xF398, 0x9AFE, 0xF399,\n\t0x9AFF, 0xF39A, 0x9B00, 0xF39B, 0x9B01, 0xF39C, 0x9B02, 0xF39D,\n\t0x9B03, 0xD7D7, 0x9B04, 0xF39E, 0x9B05, 0xF39F, 0x9B06, 0xF3A0,\n\t0x9B07, 0xF440, 0x9B08, 0xF7DC, 0x9B09, 0xF441, 0x9B0A, 0xF442,\n\t0x9B0B, 0xF443, 0x9B0C, 0xF444, 0x9B0D, 0xF445, 0x9B0E, 0xF446,\n\t0x9B0F, 0xF7DD, 0x9B10, 0xF447, 0x9B11, 0xF448, 0x9B12, 0xF449,\n\t0x9B13, 0xF7DE, 0x9B14, 0xF44A, 0x9B15, 0xF44B, 0x9B16, 0xF44C,\n\t0x9B17, 0xF44D, 0x9B18, 0xF44E, 0x9B19, 0xF44F, 0x9B1A, 0xF450,\n\t0x9B1B, 0xF451, 0x9B1C, 0xF452, 0x9B1D, 0xF453, 0x9B1E, 0xF454,\n\t0x9B1F, 0xF7DF, 0x9B20, 0xF455, 0x9B21, 0xF456, 0x9B22, 0xF457,\n\t0x9B23, 0xF7E0, 0x9B24, 0xF458, 0x9B25, 0xF459, 0x9B26, 0xF45A,\n\t0x9B27, 0xF45B, 0x9B28, 0xF45C, 0x9B29, 0xF45D, 0x9B2A, 0xF45E,\n\t0x9B2B, 0xF45F, 0x9B2C, 0xF460, 0x9B2D, 0xF461, 0x9B2E, 0xF462,\n\t0x9B2F, 0xDBCB, 0x9B30, 0xF463, 0x9B31, 0xF464, 0x9B32, 0xD8AA,\n\t0x9B33, 0xF465, 0x9B34, 0xF466, 0x9B35, 0xF467, 0x9B36, 0xF468,\n\t0x9B37, 0xF469, 0x9B38, 0xF46A, 0x9B39, 0xF46B, 0x9B3A, 0xF46C,\n\t0x9B3B, 0xE5F7, 0x9B3C, 0xB9ED, 0x9B3D, 0xF46D, 0x9B3E, 0xF46E,\n\t0x9B3F, 0xF46F, 0x9B40, 0xF470, 0x9B41, 0xBFFD, 0x9B42, 0xBBEA,\n\t0x9B43, 0xF7C9, 0x9B44, 0xC6C7, 0x9B45, 0xF7C8, 0x9B46, 0xF471,\n\t0x9B47, 0xF7CA, 0x9B48, 0xF7CC, 0x9B49, 0xF7CB, 0x9B4A, 0xF472,\n\t0x9B4B, 0xF473, 0x9B4C, 0xF474, 0x9B4D, 0xF7CD, 0x9B4E, 0xF475,\n\t0x9B4F, 0xCEBA, 0x9B50, 0xF476, 0x9B51, 0xF7CE, 0x9B52, 0xF477,\n\t0x9B53, 0xF478, 0x9B54, 0xC4A7, 0x9B55, 0xF479, 0x9B56, 0xF47A,\n\t0x9B57, 0xF47B, 0x9B58, 0xF47C, 0x9B59, 0xF47D, 0x9B5A, 0xF47E,\n\t0x9B5B, 0xF480, 0x9B5C, 0xF481, 0x9B5D, 0xF482, 0x9B5E, 0xF483,\n\t0x9B5F, 0xF484, 0x9B60, 0xF485, 0x9B61, 0xF486, 0x9B62, 0xF487,\n\t0x9B63, 0xF488, 0x9B64, 0xF489, 0x9B65, 0xF48A, 0x9B66, 0xF48B,\n\t0x9B67, 0xF48C, 0x9B68, 0xF48D, 0x9B69, 0xF48E, 0x9B6A, 0xF48F,\n\t0x9B6B, 0xF490, 0x9B6C, 0xF491, 0x9B6D, 0xF492, 0x9B6E, 0xF493,\n\t0x9B6F, 0xF494, 0x9B70, 0xF495, 0x9B71, 0xF496, 0x9B72, 0xF497,\n\t0x9B73, 0xF498, 0x9B74, 0xF499, 0x9B75, 0xF49A, 0x9B76, 0xF49B,\n\t0x9B77, 0xF49C, 0x9B78, 0xF49D, 0x9B79, 0xF49E, 0x9B7A, 0xF49F,\n\t0x9B7B, 0xF4A0, 0x9B7C, 0xF540, 0x9B7D, 0xF541, 0x9B7E, 0xF542,\n\t0x9B7F, 0xF543, 0x9B80, 0xF544, 0x9B81, 0xF545, 0x9B82, 0xF546,\n\t0x9B83, 0xF547, 0x9B84, 0xF548, 0x9B85, 0xF549, 0x9B86, 0xF54A,\n\t0x9B87, 0xF54B, 0x9B88, 0xF54C, 0x9B89, 0xF54D, 0x9B8A, 0xF54E,\n\t0x9B8B, 0xF54F, 0x9B8C, 0xF550, 0x9B8D, 0xF551, 0x9B8E, 0xF552,\n\t0x9B8F, 0xF553, 0x9B90, 0xF554, 0x9B91, 0xF555, 0x9B92, 0xF556,\n\t0x9B93, 0xF557, 0x9B94, 0xF558, 0x9B95, 0xF559, 0x9B96, 0xF55A,\n\t0x9B97, 0xF55B, 0x9B98, 0xF55C, 0x9B99, 0xF55D, 0x9B9A, 0xF55E,\n\t0x9B9B, 0xF55F, 0x9B9C, 0xF560, 0x9B9D, 0xF561, 0x9B9E, 0xF562,\n\t0x9B9F, 0xF563, 0x9BA0, 0xF564, 0x9BA1, 0xF565, 0x9BA2, 0xF566,\n\t0x9BA3, 0xF567, 0x9BA4, 0xF568, 0x9BA5, 0xF569, 0x9BA6, 0xF56A,\n\t0x9BA7, 0xF56B, 0x9BA8, 0xF56C, 0x9BA9, 0xF56D, 0x9BAA, 0xF56E,\n\t0x9BAB, 0xF56F, 0x9BAC, 0xF570, 0x9BAD, 0xF571, 0x9BAE, 0xF572,\n\t0x9BAF, 0xF573, 0x9BB0, 0xF574, 0x9BB1, 0xF575, 0x9BB2, 0xF576,\n\t0x9BB3, 0xF577, 0x9BB4, 0xF578, 0x9BB5, 0xF579, 0x9BB6, 0xF57A,\n\t0x9BB7, 0xF57B, 0x9BB8, 0xF57C, 0x9BB9, 0xF57D, 0x9BBA, 0xF57E,\n\t0x9BBB, 0xF580, 0x9BBC, 0xF581, 0x9BBD, 0xF582, 0x9BBE, 0xF583,\n\t0x9BBF, 0xF584, 0x9BC0, 0xF585, 0x9BC1, 0xF586, 0x9BC2, 0xF587,\n\t0x9BC3, 0xF588, 0x9BC4, 0xF589, 0x9BC5, 0xF58A, 0x9BC6, 0xF58B,\n\t0x9BC7, 0xF58C, 0x9BC8, 0xF58D, 0x9BC9, 0xF58E, 0x9BCA, 0xF58F,\n\t0x9BCB, 0xF590, 0x9BCC, 0xF591, 0x9BCD, 0xF592, 0x9BCE, 0xF593,\n\t0x9BCF, 0xF594, 0x9BD0, 0xF595, 0x9BD1, 0xF596, 0x9BD2, 0xF597,\n\t0x9BD3, 0xF598, 0x9BD4, 0xF599, 0x9BD5, 0xF59A, 0x9BD6, 0xF59B,\n\t0x9BD7, 0xF59C, 0x9BD8, 0xF59D, 0x9BD9, 0xF59E, 0x9BDA, 0xF59F,\n\t0x9BDB, 0xF5A0, 0x9BDC, 0xF640, 0x9BDD, 0xF641, 0x9BDE, 0xF642,\n\t0x9BDF, 0xF643, 0x9BE0, 0xF644, 0x9BE1, 0xF645, 0x9BE2, 0xF646,\n\t0x9BE3, 0xF647, 0x9BE4, 0xF648, 0x9BE5, 0xF649, 0x9BE6, 0xF64A,\n\t0x9BE7, 0xF64B, 0x9BE8, 0xF64C, 0x9BE9, 0xF64D, 0x9BEA, 0xF64E,\n\t0x9BEB, 0xF64F, 0x9BEC, 0xF650, 0x9BED, 0xF651, 0x9BEE, 0xF652,\n\t0x9BEF, 0xF653, 0x9BF0, 0xF654, 0x9BF1, 0xF655, 0x9BF2, 0xF656,\n\t0x9BF3, 0xF657, 0x9BF4, 0xF658, 0x9BF5, 0xF659, 0x9BF6, 0xF65A,\n\t0x9BF7, 0xF65B, 0x9BF8, 0xF65C, 0x9BF9, 0xF65D, 0x9BFA, 0xF65E,\n\t0x9BFB, 0xF65F, 0x9BFC, 0xF660, 0x9BFD, 0xF661, 0x9BFE, 0xF662,\n\t0x9BFF, 0xF663, 0x9C00, 0xF664, 0x9C01, 0xF665, 0x9C02, 0xF666,\n\t0x9C03, 0xF667, 0x9C04, 0xF668, 0x9C05, 0xF669, 0x9C06, 0xF66A,\n\t0x9C07, 0xF66B, 0x9C08, 0xF66C, 0x9C09, 0xF66D, 0x9C0A, 0xF66E,\n\t0x9C0B, 0xF66F, 0x9C0C, 0xF670, 0x9C0D, 0xF671, 0x9C0E, 0xF672,\n\t0x9C0F, 0xF673, 0x9C10, 0xF674, 0x9C11, 0xF675, 0x9C12, 0xF676,\n\t0x9C13, 0xF677, 0x9C14, 0xF678, 0x9C15, 0xF679, 0x9C16, 0xF67A,\n\t0x9C17, 0xF67B, 0x9C18, 0xF67C, 0x9C19, 0xF67D, 0x9C1A, 0xF67E,\n\t0x9C1B, 0xF680, 0x9C1C, 0xF681, 0x9C1D, 0xF682, 0x9C1E, 0xF683,\n\t0x9C1F, 0xF684, 0x9C20, 0xF685, 0x9C21, 0xF686, 0x9C22, 0xF687,\n\t0x9C23, 0xF688, 0x9C24, 0xF689, 0x9C25, 0xF68A, 0x9C26, 0xF68B,\n\t0x9C27, 0xF68C, 0x9C28, 0xF68D, 0x9C29, 0xF68E, 0x9C2A, 0xF68F,\n\t0x9C2B, 0xF690, 0x9C2C, 0xF691, 0x9C2D, 0xF692, 0x9C2E, 0xF693,\n\t0x9C2F, 0xF694, 0x9C30, 0xF695, 0x9C31, 0xF696, 0x9C32, 0xF697,\n\t0x9C33, 0xF698, 0x9C34, 0xF699, 0x9C35, 0xF69A, 0x9C36, 0xF69B,\n\t0x9C37, 0xF69C, 0x9C38, 0xF69D, 0x9C39, 0xF69E, 0x9C3A, 0xF69F,\n\t0x9C3B, 0xF6A0, 0x9C3C, 0xF740, 0x9C3D, 0xF741, 0x9C3E, 0xF742,\n\t0x9C3F, 0xF743, 0x9C40, 0xF744, 0x9C41, 0xF745, 0x9C42, 0xF746,\n\t0x9C43, 0xF747, 0x9C44, 0xF748, 0x9C45, 0xF749, 0x9C46, 0xF74A,\n\t0x9C47, 0xF74B, 0x9C48, 0xF74C, 0x9C49, 0xF74D, 0x9C4A, 0xF74E,\n\t0x9C4B, 0xF74F, 0x9C4C, 0xF750, 0x9C4D, 0xF751, 0x9C4E, 0xF752,\n\t0x9C4F, 0xF753, 0x9C50, 0xF754, 0x9C51, 0xF755, 0x9C52, 0xF756,\n\t0x9C53, 0xF757, 0x9C54, 0xF758, 0x9C55, 0xF759, 0x9C56, 0xF75A,\n\t0x9C57, 0xF75B, 0x9C58, 0xF75C, 0x9C59, 0xF75D, 0x9C5A, 0xF75E,\n\t0x9C5B, 0xF75F, 0x9C5C, 0xF760, 0x9C5D, 0xF761, 0x9C5E, 0xF762,\n\t0x9C5F, 0xF763, 0x9C60, 0xF764, 0x9C61, 0xF765, 0x9C62, 0xF766,\n\t0x9C63, 0xF767, 0x9C64, 0xF768, 0x9C65, 0xF769, 0x9C66, 0xF76A,\n\t0x9C67, 0xF76B, 0x9C68, 0xF76C, 0x9C69, 0xF76D, 0x9C6A, 0xF76E,\n\t0x9C6B, 0xF76F, 0x9C6C, 0xF770, 0x9C6D, 0xF771, 0x9C6E, 0xF772,\n\t0x9C6F, 0xF773, 0x9C70, 0xF774, 0x9C71, 0xF775, 0x9C72, 0xF776,\n\t0x9C73, 0xF777, 0x9C74, 0xF778, 0x9C75, 0xF779, 0x9C76, 0xF77A,\n\t0x9C77, 0xF77B, 0x9C78, 0xF77C, 0x9C79, 0xF77D, 0x9C7A, 0xF77E,\n\t0x9C7B, 0xF780, 0x9C7C, 0xD3E3, 0x9C7D, 0xF781, 0x9C7E, 0xF782,\n\t0x9C7F, 0xF6CF, 0x9C80, 0xF783, 0x9C81, 0xC2B3, 0x9C82, 0xF6D0,\n\t0x9C83, 0xF784, 0x9C84, 0xF785, 0x9C85, 0xF6D1, 0x9C86, 0xF6D2,\n\t0x9C87, 0xF6D3, 0x9C88, 0xF6D4, 0x9C89, 0xF786, 0x9C8A, 0xF787,\n\t0x9C8B, 0xF6D6, 0x9C8C, 0xF788, 0x9C8D, 0xB1AB, 0x9C8E, 0xF6D7,\n\t0x9C8F, 0xF789, 0x9C90, 0xF6D8, 0x9C91, 0xF6D9, 0x9C92, 0xF6DA,\n\t0x9C93, 0xF78A, 0x9C94, 0xF6DB, 0x9C95, 0xF6DC, 0x9C96, 0xF78B,\n\t0x9C97, 0xF78C, 0x9C98, 0xF78D, 0x9C99, 0xF78E, 0x9C9A, 0xF6DD,\n\t0x9C9B, 0xF6DE, 0x9C9C, 0xCFCA, 0x9C9D, 0xF78F, 0x9C9E, 0xF6DF,\n\t0x9C9F, 0xF6E0, 0x9CA0, 0xF6E1, 0x9CA1, 0xF6E2, 0x9CA2, 0xF6E3,\n\t0x9CA3, 0xF6E4, 0x9CA4, 0xC0F0, 0x9CA5, 0xF6E5, 0x9CA6, 0xF6E6,\n\t0x9CA7, 0xF6E7, 0x9CA8, 0xF6E8, 0x9CA9, 0xF6E9, 0x9CAA, 0xF790,\n\t0x9CAB, 0xF6EA, 0x9CAC, 0xF791, 0x9CAD, 0xF6EB, 0x9CAE, 0xF6EC,\n\t0x9CAF, 0xF792, 0x9CB0, 0xF6ED, 0x9CB1, 0xF6EE, 0x9CB2, 0xF6EF,\n\t0x9CB3, 0xF6F0, 0x9CB4, 0xF6F1, 0x9CB5, 0xF6F2, 0x9CB6, 0xF6F3,\n\t0x9CB7, 0xF6F4, 0x9CB8, 0xBEA8, 0x9CB9, 0xF793, 0x9CBA, 0xF6F5,\n\t0x9CBB, 0xF6F6, 0x9CBC, 0xF6F7, 0x9CBD, 0xF6F8, 0x9CBE, 0xF794,\n\t0x9CBF, 0xF795, 0x9CC0, 0xF796, 0x9CC1, 0xF797, 0x9CC2, 0xF798,\n\t0x9CC3, 0xC8FA, 0x9CC4, 0xF6F9, 0x9CC5, 0xF6FA, 0x9CC6, 0xF6FB,\n\t0x9CC7, 0xF6FC, 0x9CC8, 0xF799, 0x9CC9, 0xF79A, 0x9CCA, 0xF6FD,\n\t0x9CCB, 0xF6FE, 0x9CCC, 0xF7A1, 0x9CCD, 0xF7A2, 0x9CCE, 0xF7A3,\n\t0x9CCF, 0xF7A4, 0x9CD0, 0xF7A5, 0x9CD1, 0xF79B, 0x9CD2, 0xF79C,\n\t0x9CD3, 0xF7A6, 0x9CD4, 0xF7A7, 0x9CD5, 0xF7A8, 0x9CD6, 0xB1EE,\n\t0x9CD7, 0xF7A9, 0x9CD8, 0xF7AA, 0x9CD9, 0xF7AB, 0x9CDA, 0xF79D,\n\t0x9CDB, 0xF79E, 0x9CDC, 0xF7AC, 0x9CDD, 0xF7AD, 0x9CDE, 0xC1DB,\n\t0x9CDF, 0xF7AE, 0x9CE0, 0xF79F, 0x9CE1, 0xF7A0, 0x9CE2, 0xF7AF,\n\t0x9CE3, 0xF840, 0x9CE4, 0xF841, 0x9CE5, 0xF842, 0x9CE6, 0xF843,\n\t0x9CE7, 0xF844, 0x9CE8, 0xF845, 0x9CE9, 0xF846, 0x9CEA, 0xF847,\n\t0x9CEB, 0xF848, 0x9CEC, 0xF849, 0x9CED, 0xF84A, 0x9CEE, 0xF84B,\n\t0x9CEF, 0xF84C, 0x9CF0, 0xF84D, 0x9CF1, 0xF84E, 0x9CF2, 0xF84F,\n\t0x9CF3, 0xF850, 0x9CF4, 0xF851, 0x9CF5, 0xF852, 0x9CF6, 0xF853,\n\t0x9CF7, 0xF854, 0x9CF8, 0xF855, 0x9CF9, 0xF856, 0x9CFA, 0xF857,\n\t0x9CFB, 0xF858, 0x9CFC, 0xF859, 0x9CFD, 0xF85A, 0x9CFE, 0xF85B,\n\t0x9CFF, 0xF85C, 0x9D00, 0xF85D, 0x9D01, 0xF85E, 0x9D02, 0xF85F,\n\t0x9D03, 0xF860, 0x9D04, 0xF861, 0x9D05, 0xF862, 0x9D06, 0xF863,\n\t0x9D07, 0xF864, 0x9D08, 0xF865, 0x9D09, 0xF866, 0x9D0A, 0xF867,\n\t0x9D0B, 0xF868, 0x9D0C, 0xF869, 0x9D0D, 0xF86A, 0x9D0E, 0xF86B,\n\t0x9D0F, 0xF86C, 0x9D10, 0xF86D, 0x9D11, 0xF86E, 0x9D12, 0xF86F,\n\t0x9D13, 0xF870, 0x9D14, 0xF871, 0x9D15, 0xF872, 0x9D16, 0xF873,\n\t0x9D17, 0xF874, 0x9D18, 0xF875, 0x9D19, 0xF876, 0x9D1A, 0xF877,\n\t0x9D1B, 0xF878, 0x9D1C, 0xF879, 0x9D1D, 0xF87A, 0x9D1E, 0xF87B,\n\t0x9D1F, 0xF87C, 0x9D20, 0xF87D, 0x9D21, 0xF87E, 0x9D22, 0xF880,\n\t0x9D23, 0xF881, 0x9D24, 0xF882, 0x9D25, 0xF883, 0x9D26, 0xF884,\n\t0x9D27, 0xF885, 0x9D28, 0xF886, 0x9D29, 0xF887, 0x9D2A, 0xF888,\n\t0x9D2B, 0xF889, 0x9D2C, 0xF88A, 0x9D2D, 0xF88B, 0x9D2E, 0xF88C,\n\t0x9D2F, 0xF88D, 0x9D30, 0xF88E, 0x9D31, 0xF88F, 0x9D32, 0xF890,\n\t0x9D33, 0xF891, 0x9D34, 0xF892, 0x9D35, 0xF893, 0x9D36, 0xF894,\n\t0x9D37, 0xF895, 0x9D38, 0xF896, 0x9D39, 0xF897, 0x9D3A, 0xF898,\n\t0x9D3B, 0xF899, 0x9D3C, 0xF89A, 0x9D3D, 0xF89B, 0x9D3E, 0xF89C,\n\t0x9D3F, 0xF89D, 0x9D40, 0xF89E, 0x9D41, 0xF89F, 0x9D42, 0xF8A0,\n\t0x9D43, 0xF940, 0x9D44, 0xF941, 0x9D45, 0xF942, 0x9D46, 0xF943,\n\t0x9D47, 0xF944, 0x9D48, 0xF945, 0x9D49, 0xF946, 0x9D4A, 0xF947,\n\t0x9D4B, 0xF948, 0x9D4C, 0xF949, 0x9D4D, 0xF94A, 0x9D4E, 0xF94B,\n\t0x9D4F, 0xF94C, 0x9D50, 0xF94D, 0x9D51, 0xF94E, 0x9D52, 0xF94F,\n\t0x9D53, 0xF950, 0x9D54, 0xF951, 0x9D55, 0xF952, 0x9D56, 0xF953,\n\t0x9D57, 0xF954, 0x9D58, 0xF955, 0x9D59, 0xF956, 0x9D5A, 0xF957,\n\t0x9D5B, 0xF958, 0x9D5C, 0xF959, 0x9D5D, 0xF95A, 0x9D5E, 0xF95B,\n\t0x9D5F, 0xF95C, 0x9D60, 0xF95D, 0x9D61, 0xF95E, 0x9D62, 0xF95F,\n\t0x9D63, 0xF960, 0x9D64, 0xF961, 0x9D65, 0xF962, 0x9D66, 0xF963,\n\t0x9D67, 0xF964, 0x9D68, 0xF965, 0x9D69, 0xF966, 0x9D6A, 0xF967,\n\t0x9D6B, 0xF968, 0x9D6C, 0xF969, 0x9D6D, 0xF96A, 0x9D6E, 0xF96B,\n\t0x9D6F, 0xF96C, 0x9D70, 0xF96D, 0x9D71, 0xF96E, 0x9D72, 0xF96F,\n\t0x9D73, 0xF970, 0x9D74, 0xF971, 0x9D75, 0xF972, 0x9D76, 0xF973,\n\t0x9D77, 0xF974, 0x9D78, 0xF975, 0x9D79, 0xF976, 0x9D7A, 0xF977,\n\t0x9D7B, 0xF978, 0x9D7C, 0xF979, 0x9D7D, 0xF97A, 0x9D7E, 0xF97B,\n\t0x9D7F, 0xF97C, 0x9D80, 0xF97D, 0x9D81, 0xF97E, 0x9D82, 0xF980,\n\t0x9D83, 0xF981, 0x9D84, 0xF982, 0x9D85, 0xF983, 0x9D86, 0xF984,\n\t0x9D87, 0xF985, 0x9D88, 0xF986, 0x9D89, 0xF987, 0x9D8A, 0xF988,\n\t0x9D8B, 0xF989, 0x9D8C, 0xF98A, 0x9D8D, 0xF98B, 0x9D8E, 0xF98C,\n\t0x9D8F, 0xF98D, 0x9D90, 0xF98E, 0x9D91, 0xF98F, 0x9D92, 0xF990,\n\t0x9D93, 0xF991, 0x9D94, 0xF992, 0x9D95, 0xF993, 0x9D96, 0xF994,\n\t0x9D97, 0xF995, 0x9D98, 0xF996, 0x9D99, 0xF997, 0x9D9A, 0xF998,\n\t0x9D9B, 0xF999, 0x9D9C, 0xF99A, 0x9D9D, 0xF99B, 0x9D9E, 0xF99C,\n\t0x9D9F, 0xF99D, 0x9DA0, 0xF99E, 0x9DA1, 0xF99F, 0x9DA2, 0xF9A0,\n\t0x9DA3, 0xFA40, 0x9DA4, 0xFA41, 0x9DA5, 0xFA42, 0x9DA6, 0xFA43,\n\t0x9DA7, 0xFA44, 0x9DA8, 0xFA45, 0x9DA9, 0xFA46, 0x9DAA, 0xFA47,\n\t0x9DAB, 0xFA48, 0x9DAC, 0xFA49, 0x9DAD, 0xFA4A, 0x9DAE, 0xFA4B,\n\t0x9DAF, 0xFA4C, 0x9DB0, 0xFA4D, 0x9DB1, 0xFA4E, 0x9DB2, 0xFA4F,\n\t0x9DB3, 0xFA50, 0x9DB4, 0xFA51, 0x9DB5, 0xFA52, 0x9DB6, 0xFA53,\n\t0x9DB7, 0xFA54, 0x9DB8, 0xFA55, 0x9DB9, 0xFA56, 0x9DBA, 0xFA57,\n\t0x9DBB, 0xFA58, 0x9DBC, 0xFA59, 0x9DBD, 0xFA5A, 0x9DBE, 0xFA5B,\n\t0x9DBF, 0xFA5C, 0x9DC0, 0xFA5D, 0x9DC1, 0xFA5E, 0x9DC2, 0xFA5F,\n\t0x9DC3, 0xFA60, 0x9DC4, 0xFA61, 0x9DC5, 0xFA62, 0x9DC6, 0xFA63,\n\t0x9DC7, 0xFA64, 0x9DC8, 0xFA65, 0x9DC9, 0xFA66, 0x9DCA, 0xFA67,\n\t0x9DCB, 0xFA68, 0x9DCC, 0xFA69, 0x9DCD, 0xFA6A, 0x9DCE, 0xFA6B,\n\t0x9DCF, 0xFA6C, 0x9DD0, 0xFA6D, 0x9DD1, 0xFA6E, 0x9DD2, 0xFA6F,\n\t0x9DD3, 0xFA70, 0x9DD4, 0xFA71, 0x9DD5, 0xFA72, 0x9DD6, 0xFA73,\n\t0x9DD7, 0xFA74, 0x9DD8, 0xFA75, 0x9DD9, 0xFA76, 0x9DDA, 0xFA77,\n\t0x9DDB, 0xFA78, 0x9DDC, 0xFA79, 0x9DDD, 0xFA7A, 0x9DDE, 0xFA7B,\n\t0x9DDF, 0xFA7C, 0x9DE0, 0xFA7D, 0x9DE1, 0xFA7E, 0x9DE2, 0xFA80,\n\t0x9DE3, 0xFA81, 0x9DE4, 0xFA82, 0x9DE5, 0xFA83, 0x9DE6, 0xFA84,\n\t0x9DE7, 0xFA85, 0x9DE8, 0xFA86, 0x9DE9, 0xFA87, 0x9DEA, 0xFA88,\n\t0x9DEB, 0xFA89, 0x9DEC, 0xFA8A, 0x9DED, 0xFA8B, 0x9DEE, 0xFA8C,\n\t0x9DEF, 0xFA8D, 0x9DF0, 0xFA8E, 0x9DF1, 0xFA8F, 0x9DF2, 0xFA90,\n\t0x9DF3, 0xFA91, 0x9DF4, 0xFA92, 0x9DF5, 0xFA93, 0x9DF6, 0xFA94,\n\t0x9DF7, 0xFA95, 0x9DF8, 0xFA96, 0x9DF9, 0xFA97, 0x9DFA, 0xFA98,\n\t0x9DFB, 0xFA99, 0x9DFC, 0xFA9A, 0x9DFD, 0xFA9B, 0x9DFE, 0xFA9C,\n\t0x9DFF, 0xFA9D, 0x9E00, 0xFA9E, 0x9E01, 0xFA9F, 0x9E02, 0xFAA0,\n\t0x9E03, 0xFB40, 0x9E04, 0xFB41, 0x9E05, 0xFB42, 0x9E06, 0xFB43,\n\t0x9E07, 0xFB44, 0x9E08, 0xFB45, 0x9E09, 0xFB46, 0x9E0A, 0xFB47,\n\t0x9E0B, 0xFB48, 0x9E0C, 0xFB49, 0x9E0D, 0xFB4A, 0x9E0E, 0xFB4B,\n\t0x9E0F, 0xFB4C, 0x9E10, 0xFB4D, 0x9E11, 0xFB4E, 0x9E12, 0xFB4F,\n\t0x9E13, 0xFB50, 0x9E14, 0xFB51, 0x9E15, 0xFB52, 0x9E16, 0xFB53,\n\t0x9E17, 0xFB54, 0x9E18, 0xFB55, 0x9E19, 0xFB56, 0x9E1A, 0xFB57,\n\t0x9E1B, 0xFB58, 0x9E1C, 0xFB59, 0x9E1D, 0xFB5A, 0x9E1E, 0xFB5B,\n\t0x9E1F, 0xC4F1, 0x9E20, 0xF0AF, 0x9E21, 0xBCA6, 0x9E22, 0xF0B0,\n\t0x9E23, 0xC3F9, 0x9E24, 0xFB5C, 0x9E25, 0xC5B8, 0x9E26, 0xD1BB,\n\t0x9E27, 0xFB5D, 0x9E28, 0xF0B1, 0x9E29, 0xF0B2, 0x9E2A, 0xF0B3,\n\t0x9E2B, 0xF0B4, 0x9E2C, 0xF0B5, 0x9E2D, 0xD1BC, 0x9E2E, 0xFB5E,\n\t0x9E2F, 0xD1EC, 0x9E30, 0xFB5F, 0x9E31, 0xF0B7, 0x9E32, 0xF0B6,\n\t0x9E33, 0xD4A7, 0x9E34, 0xFB60, 0x9E35, 0xCDD2, 0x9E36, 0xF0B8,\n\t0x9E37, 0xF0BA, 0x9E38, 0xF0B9, 0x9E39, 0xF0BB, 0x9E3A, 0xF0BC,\n\t0x9E3B, 0xFB61, 0x9E3C, 0xFB62, 0x9E3D, 0xB8EB, 0x9E3E, 0xF0BD,\n\t0x9E3F, 0xBAE8, 0x9E40, 0xFB63, 0x9E41, 0xF0BE, 0x9E42, 0xF0BF,\n\t0x9E43, 0xBEE9, 0x9E44, 0xF0C0, 0x9E45, 0xB6EC, 0x9E46, 0xF0C1,\n\t0x9E47, 0xF0C2, 0x9E48, 0xF0C3, 0x9E49, 0xF0C4, 0x9E4A, 0xC8B5,\n\t0x9E4B, 0xF0C5, 0x9E4C, 0xF0C6, 0x9E4D, 0xFB64, 0x9E4E, 0xF0C7,\n\t0x9E4F, 0xC5F4, 0x9E50, 0xFB65, 0x9E51, 0xF0C8, 0x9E52, 0xFB66,\n\t0x9E53, 0xFB67, 0x9E54, 0xFB68, 0x9E55, 0xF0C9, 0x9E56, 0xFB69,\n\t0x9E57, 0xF0CA, 0x9E58, 0xF7BD, 0x9E59, 0xFB6A, 0x9E5A, 0xF0CB,\n\t0x9E5B, 0xF0CC, 0x9E5C, 0xF0CD, 0x9E5D, 0xFB6B, 0x9E5E, 0xF0CE,\n\t0x9E5F, 0xFB6C, 0x9E60, 0xFB6D, 0x9E61, 0xFB6E, 0x9E62, 0xFB6F,\n\t0x9E63, 0xF0CF, 0x9E64, 0xBAD7, 0x9E65, 0xFB70, 0x9E66, 0xF0D0,\n\t0x9E67, 0xF0D1, 0x9E68, 0xF0D2, 0x9E69, 0xF0D3, 0x9E6A, 0xF0D4,\n\t0x9E6B, 0xF0D5, 0x9E6C, 0xF0D6, 0x9E6D, 0xF0D8, 0x9E6E, 0xFB71,\n\t0x9E6F, 0xFB72, 0x9E70, 0xD3A5, 0x9E71, 0xF0D7, 0x9E72, 0xFB73,\n\t0x9E73, 0xF0D9, 0x9E74, 0xFB74, 0x9E75, 0xFB75, 0x9E76, 0xFB76,\n\t0x9E77, 0xFB77, 0x9E78, 0xFB78, 0x9E79, 0xFB79, 0x9E7A, 0xFB7A,\n\t0x9E7B, 0xFB7B, 0x9E7C, 0xFB7C, 0x9E7D, 0xFB7D, 0x9E7E, 0xF5BA,\n\t0x9E7F, 0xC2B9, 0x9E80, 0xFB7E, 0x9E81, 0xFB80, 0x9E82, 0xF7E4,\n\t0x9E83, 0xFB81, 0x9E84, 0xFB82, 0x9E85, 0xFB83, 0x9E86, 0xFB84,\n\t0x9E87, 0xF7E5, 0x9E88, 0xF7E6, 0x9E89, 0xFB85, 0x9E8A, 0xFB86,\n\t0x9E8B, 0xF7E7, 0x9E8C, 0xFB87, 0x9E8D, 0xFB88, 0x9E8E, 0xFB89,\n\t0x9E8F, 0xFB8A, 0x9E90, 0xFB8B, 0x9E91, 0xFB8C, 0x9E92, 0xF7E8,\n\t0x9E93, 0xC2B4, 0x9E94, 0xFB8D, 0x9E95, 0xFB8E, 0x9E96, 0xFB8F,\n\t0x9E97, 0xFB90, 0x9E98, 0xFB91, 0x9E99, 0xFB92, 0x9E9A, 0xFB93,\n\t0x9E9B, 0xFB94, 0x9E9C, 0xFB95, 0x9E9D, 0xF7EA, 0x9E9E, 0xFB96,\n\t0x9E9F, 0xF7EB, 0x9EA0, 0xFB97, 0x9EA1, 0xFB98, 0x9EA2, 0xFB99,\n\t0x9EA3, 0xFB9A, 0x9EA4, 0xFB9B, 0x9EA5, 0xFB9C, 0x9EA6, 0xC2F3,\n\t0x9EA7, 0xFB9D, 0x9EA8, 0xFB9E, 0x9EA9, 0xFB9F, 0x9EAA, 0xFBA0,\n\t0x9EAB, 0xFC40, 0x9EAC, 0xFC41, 0x9EAD, 0xFC42, 0x9EAE, 0xFC43,\n\t0x9EAF, 0xFC44, 0x9EB0, 0xFC45, 0x9EB1, 0xFC46, 0x9EB2, 0xFC47,\n\t0x9EB3, 0xFC48, 0x9EB4, 0xF4F0, 0x9EB5, 0xFC49, 0x9EB6, 0xFC4A,\n\t0x9EB7, 0xFC4B, 0x9EB8, 0xF4EF, 0x9EB9, 0xFC4C, 0x9EBA, 0xFC4D,\n\t0x9EBB, 0xC2E9, 0x9EBC, 0xFC4E, 0x9EBD, 0xF7E1, 0x9EBE, 0xF7E2,\n\t0x9EBF, 0xFC4F, 0x9EC0, 0xFC50, 0x9EC1, 0xFC51, 0x9EC2, 0xFC52,\n\t0x9EC3, 0xFC53, 0x9EC4, 0xBBC6, 0x9EC5, 0xFC54, 0x9EC6, 0xFC55,\n\t0x9EC7, 0xFC56, 0x9EC8, 0xFC57, 0x9EC9, 0xD9E4, 0x9ECA, 0xFC58,\n\t0x9ECB, 0xFC59, 0x9ECC, 0xFC5A, 0x9ECD, 0xCAF2, 0x9ECE, 0xC0E8,\n\t0x9ECF, 0xF0A4, 0x9ED0, 0xFC5B, 0x9ED1, 0xBADA, 0x9ED2, 0xFC5C,\n\t0x9ED3, 0xFC5D, 0x9ED4, 0xC7AD, 0x9ED5, 0xFC5E, 0x9ED6, 0xFC5F,\n\t0x9ED7, 0xFC60, 0x9ED8, 0xC4AC, 0x9ED9, 0xFC61, 0x9EDA, 0xFC62,\n\t0x9EDB, 0xF7EC, 0x9EDC, 0xF7ED, 0x9EDD, 0xF7EE, 0x9EDE, 0xFC63,\n\t0x9EDF, 0xF7F0, 0x9EE0, 0xF7EF, 0x9EE1, 0xFC64, 0x9EE2, 0xF7F1,\n\t0x9EE3, 0xFC65, 0x9EE4, 0xFC66, 0x9EE5, 0xF7F4, 0x9EE6, 0xFC67,\n\t0x9EE7, 0xF7F3, 0x9EE8, 0xFC68, 0x9EE9, 0xF7F2, 0x9EEA, 0xF7F5,\n\t0x9EEB, 0xFC69, 0x9EEC, 0xFC6A, 0x9EED, 0xFC6B, 0x9EEE, 0xFC6C,\n\t0x9EEF, 0xF7F6, 0x9EF0, 0xFC6D, 0x9EF1, 0xFC6E, 0x9EF2, 0xFC6F,\n\t0x9EF3, 0xFC70, 0x9EF4, 0xFC71, 0x9EF5, 0xFC72, 0x9EF6, 0xFC73,\n\t0x9EF7, 0xFC74, 0x9EF8, 0xFC75, 0x9EF9, 0xEDE9, 0x9EFA, 0xFC76,\n\t0x9EFB, 0xEDEA, 0x9EFC, 0xEDEB, 0x9EFD, 0xFC77, 0x9EFE, 0xF6BC,\n\t0x9EFF, 0xFC78, 0x9F00, 0xFC79, 0x9F01, 0xFC7A, 0x9F02, 0xFC7B,\n\t0x9F03, 0xFC7C, 0x9F04, 0xFC7D, 0x9F05, 0xFC7E, 0x9F06, 0xFC80,\n\t0x9F07, 0xFC81, 0x9F08, 0xFC82, 0x9F09, 0xFC83, 0x9F0A, 0xFC84,\n\t0x9F0B, 0xF6BD, 0x9F0C, 0xFC85, 0x9F0D, 0xF6BE, 0x9F0E, 0xB6A6,\n\t0x9F0F, 0xFC86, 0x9F10, 0xD8BE, 0x9F11, 0xFC87, 0x9F12, 0xFC88,\n\t0x9F13, 0xB9C4, 0x9F14, 0xFC89, 0x9F15, 0xFC8A, 0x9F16, 0xFC8B,\n\t0x9F17, 0xD8BB, 0x9F18, 0xFC8C, 0x9F19, 0xDCB1, 0x9F1A, 0xFC8D,\n\t0x9F1B, 0xFC8E, 0x9F1C, 0xFC8F, 0x9F1D, 0xFC90, 0x9F1E, 0xFC91,\n\t0x9F1F, 0xFC92, 0x9F20, 0xCAF3, 0x9F21, 0xFC93, 0x9F22, 0xF7F7,\n\t0x9F23, 0xFC94, 0x9F24, 0xFC95, 0x9F25, 0xFC96, 0x9F26, 0xFC97,\n\t0x9F27, 0xFC98, 0x9F28, 0xFC99, 0x9F29, 0xFC9A, 0x9F2A, 0xFC9B,\n\t0x9F2B, 0xFC9C, 0x9F2C, 0xF7F8, 0x9F2D, 0xFC9D, 0x9F2E, 0xFC9E,\n\t0x9F2F, 0xF7F9, 0x9F30, 0xFC9F, 0x9F31, 0xFCA0, 0x9F32, 0xFD40,\n\t0x9F33, 0xFD41, 0x9F34, 0xFD42, 0x9F35, 0xFD43, 0x9F36, 0xFD44,\n\t0x9F37, 0xF7FB, 0x9F38, 0xFD45, 0x9F39, 0xF7FA, 0x9F3A, 0xFD46,\n\t0x9F3B, 0xB1C7, 0x9F3C, 0xFD47, 0x9F3D, 0xF7FC, 0x9F3E, 0xF7FD,\n\t0x9F3F, 0xFD48, 0x9F40, 0xFD49, 0x9F41, 0xFD4A, 0x9F42, 0xFD4B,\n\t0x9F43, 0xFD4C, 0x9F44, 0xF7FE, 0x9F45, 0xFD4D, 0x9F46, 0xFD4E,\n\t0x9F47, 0xFD4F, 0x9F48, 0xFD50, 0x9F49, 0xFD51, 0x9F4A, 0xFD52,\n\t0x9F4B, 0xFD53, 0x9F4C, 0xFD54, 0x9F4D, 0xFD55, 0x9F4E, 0xFD56,\n\t0x9F4F, 0xFD57, 0x9F50, 0xC6EB, 0x9F51, 0xECB4, 0x9F52, 0xFD58,\n\t0x9F53, 0xFD59, 0x9F54, 0xFD5A, 0x9F55, 0xFD5B, 0x9F56, 0xFD5C,\n\t0x9F57, 0xFD5D, 0x9F58, 0xFD5E, 0x9F59, 0xFD5F, 0x9F5A, 0xFD60,\n\t0x9F5B, 0xFD61, 0x9F5C, 0xFD62, 0x9F5D, 0xFD63, 0x9F5E, 0xFD64,\n\t0x9F5F, 0xFD65, 0x9F60, 0xFD66, 0x9F61, 0xFD67, 0x9F62, 0xFD68,\n\t0x9F63, 0xFD69, 0x9F64, 0xFD6A, 0x9F65, 0xFD6B, 0x9F66, 0xFD6C,\n\t0x9F67, 0xFD6D, 0x9F68, 0xFD6E, 0x9F69, 0xFD6F, 0x9F6A, 0xFD70,\n\t0x9F6B, 0xFD71, 0x9F6C, 0xFD72, 0x9F6D, 0xFD73, 0x9F6E, 0xFD74,\n\t0x9F6F, 0xFD75, 0x9F70, 0xFD76, 0x9F71, 0xFD77, 0x9F72, 0xFD78,\n\t0x9F73, 0xFD79, 0x9F74, 0xFD7A, 0x9F75, 0xFD7B, 0x9F76, 0xFD7C,\n\t0x9F77, 0xFD7D, 0x9F78, 0xFD7E, 0x9F79, 0xFD80, 0x9F7A, 0xFD81,\n\t0x9F7B, 0xFD82, 0x9F7C, 0xFD83, 0x9F7D, 0xFD84, 0x9F7E, 0xFD85,\n\t0x9F7F, 0xB3DD, 0x9F80, 0xF6B3, 0x9F81, 0xFD86, 0x9F82, 0xFD87,\n\t0x9F83, 0xF6B4, 0x9F84, 0xC1E4, 0x9F85, 0xF6B5, 0x9F86, 0xF6B6,\n\t0x9F87, 0xF6B7, 0x9F88, 0xF6B8, 0x9F89, 0xF6B9, 0x9F8A, 0xF6BA,\n\t0x9F8B, 0xC8A3, 0x9F8C, 0xF6BB, 0x9F8D, 0xFD88, 0x9F8E, 0xFD89,\n\t0x9F8F, 0xFD8A, 0x9F90, 0xFD8B, 0x9F91, 0xFD8C, 0x9F92, 0xFD8D,\n\t0x9F93, 0xFD8E, 0x9F94, 0xFD8F, 0x9F95, 0xFD90, 0x9F96, 0xFD91,\n\t0x9F97, 0xFD92, 0x9F98, 0xFD93, 0x9F99, 0xC1FA, 0x9F9A, 0xB9A8,\n\t0x9F9B, 0xEDE8, 0x9F9C, 0xFD94, 0x9F9D, 0xFD95, 0x9F9E, 0xFD96,\n\t0x9F9F, 0xB9EA, 0x9FA0, 0xD9DF, 0x9FA1, 0xFD97, 0x9FA2, 0xFD98,\n\t0x9FA3, 0xFD99, 0x9FA4, 0xFD9A, 0x9FA5, 0xFD9B, 0xF92C, 0xFD9C,\n\t0xF979, 0xFD9D, 0xF995, 0xFD9E, 0xF9E7, 0xFD9F, 0xF9F1, 0xFDA0,\n\t0xFA0C, 0xFE40, 0xFA0D, 0xFE41, 0xFA0E, 0xFE42, 0xFA0F, 0xFE43,\n\t0xFA11, 0xFE44, 0xFA13, 0xFE45, 0xFA14, 0xFE46, 0xFA18, 0xFE47,\n\t0xFA1F, 0xFE48, 0xFA20, 0xFE49, 0xFA21, 0xFE4A, 0xFA23, 0xFE4B,\n\t0xFA24, 0xFE4C, 0xFA27, 0xFE4D, 0xFA28, 0xFE4E, 0xFA29, 0xFE4F,\n\t0xFE30, 0xA955, 0xFE31, 0xA6F2, 0xFE33, 0xA6F4, 0xFE34, 0xA6F5,\n\t0xFE35, 0xA6E0, 0xFE36, 0xA6E1, 0xFE37, 0xA6F0, 0xFE38, 0xA6F1,\n\t0xFE39, 0xA6E2, 0xFE3A, 0xA6E3, 0xFE3B, 0xA6EE, 0xFE3C, 0xA6EF,\n\t0xFE3D, 0xA6E6, 0xFE3E, 0xA6E7, 0xFE3F, 0xA6E4, 0xFE40, 0xA6E5,\n\t0xFE41, 0xA6E8, 0xFE42, 0xA6E9, 0xFE43, 0xA6EA, 0xFE44, 0xA6EB,\n\t0xFE49, 0xA968, 0xFE4A, 0xA969, 0xFE4B, 0xA96A, 0xFE4C, 0xA96B,\n\t0xFE4D, 0xA96C, 0xFE4E, 0xA96D, 0xFE4F, 0xA96E, 0xFE50, 0xA96F,\n\t0xFE51, 0xA970, 0xFE52, 0xA971, 0xFE54, 0xA972, 0xFE55, 0xA973,\n\t0xFE56, 0xA974, 0xFE57, 0xA975, 0xFE59, 0xA976, 0xFE5A, 0xA977,\n\t0xFE5B, 0xA978, 0xFE5C, 0xA979, 0xFE5D, 0xA97A, 0xFE5E, 0xA97B,\n\t0xFE5F, 0xA97C, 0xFE60, 0xA97D, 0xFE61, 0xA97E, 0xFE62, 0xA980,\n\t0xFE63, 0xA981, 0xFE64, 0xA982, 0xFE65, 0xA983, 0xFE66, 0xA984,\n\t0xFE68, 0xA985, 0xFE69, 0xA986, 0xFE6A, 0xA987, 0xFE6B, 0xA988,\n\t0xFF01, 0xA3A1, 0xFF02, 0xA3A2, 0xFF03, 0xA3A3, 0xFF04, 0xA1E7,\n\t0xFF05, 0xA3A5, 0xFF06, 0xA3A6, 0xFF07, 0xA3A7, 0xFF08, 0xA3A8,\n\t0xFF09, 0xA3A9, 0xFF0A, 0xA3AA, 0xFF0B, 0xA3AB, 0xFF0C, 0xA3AC,\n\t0xFF0D, 0xA3AD, 0xFF0E, 0xA3AE, 0xFF0F, 0xA3AF, 0xFF10, 0xA3B0,\n\t0xFF11, 0xA3B1, 0xFF12, 0xA3B2, 0xFF13, 0xA3B3, 0xFF14, 0xA3B4,\n\t0xFF15, 0xA3B5, 0xFF16, 0xA3B6, 0xFF17, 0xA3B7, 0xFF18, 0xA3B8,\n\t0xFF19, 0xA3B9, 0xFF1A, 0xA3BA, 0xFF1B, 0xA3BB, 0xFF1C, 0xA3BC,\n\t0xFF1D, 0xA3BD, 0xFF1E, 0xA3BE, 0xFF1F, 0xA3BF, 0xFF20, 0xA3C0,\n\t0xFF21, 0xA3C1, 0xFF22, 0xA3C2, 0xFF23, 0xA3C3, 0xFF24, 0xA3C4,\n\t0xFF25, 0xA3C5, 0xFF26, 0xA3C6, 0xFF27, 0xA3C7, 0xFF28, 0xA3C8,\n\t0xFF29, 0xA3C9, 0xFF2A, 0xA3CA, 0xFF2B, 0xA3CB, 0xFF2C, 0xA3CC,\n\t0xFF2D, 0xA3CD, 0xFF2E, 0xA3CE, 0xFF2F, 0xA3CF, 0xFF30, 0xA3D0,\n\t0xFF31, 0xA3D1, 0xFF32, 0xA3D2, 0xFF33, 0xA3D3, 0xFF34, 0xA3D4,\n\t0xFF35, 0xA3D5, 0xFF36, 0xA3D6, 0xFF37, 0xA3D7, 0xFF38, 0xA3D8,\n\t0xFF39, 0xA3D9, 0xFF3A, 0xA3DA, 0xFF3B, 0xA3DB, 0xFF3C, 0xA3DC,\n\t0xFF3D, 0xA3DD, 0xFF3E, 0xA3DE, 0xFF3F, 0xA3DF, 0xFF40, 0xA3E0,\n\t0xFF41, 0xA3E1, 0xFF42, 0xA3E2, 0xFF43, 0xA3E3, 0xFF44, 0xA3E4,\n\t0xFF45, 0xA3E5, 0xFF46, 0xA3E6, 0xFF47, 0xA3E7, 0xFF48, 0xA3E8,\n\t0xFF49, 0xA3E9, 0xFF4A, 0xA3EA, 0xFF4B, 0xA3EB, 0xFF4C, 0xA3EC,\n\t0xFF4D, 0xA3ED, 0xFF4E, 0xA3EE, 0xFF4F, 0xA3EF, 0xFF50, 0xA3F0,\n\t0xFF51, 0xA3F1, 0xFF52, 0xA3F2, 0xFF53, 0xA3F3, 0xFF54, 0xA3F4,\n\t0xFF55, 0xA3F5, 0xFF56, 0xA3F6, 0xFF57, 0xA3F7, 0xFF58, 0xA3F8,\n\t0xFF59, 0xA3F9, 0xFF5A, 0xA3FA, 0xFF5B, 0xA3FB, 0xFF5C, 0xA3FC,\n\t0xFF5D, 0xA3FD, 0xFF5E, 0xA1AB, 0xFFE0, 0xA1E9, 0xFFE1, 0xA1EA,\n\t0xFFE2, 0xA956, 0xFFE3, 0xA3FE, 0xFFE4, 0xA957, 0xFFE5, 0xA3A4,\n\t0, 0\n};\n\nstatic\nconst WCHAR oem2uni[] = {\n/*\tOEM - Unicode,  OEM - Unicode,  OEM - Unicode,  OEM - Unicode */\n\t0x0080, 0x20AC, 0x8140, 0x4E02, 0x8141, 0x4E04, 0x8142, 0x4E05,\n\t0x8143, 0x4E06, 0x8144, 0x4E0F, 0x8145, 0x4E12, 0x8146, 0x4E17,\n\t0x8147, 0x4E1F, 0x8148, 0x4E20, 0x8149, 0x4E21, 0x814A, 0x4E23,\n\t0x814B, 0x4E26, 0x814C, 0x4E29, 0x814D, 0x4E2E, 0x814E, 0x4E2F,\n\t0x814F, 0x4E31, 0x8150, 0x4E33, 0x8151, 0x4E35, 0x8152, 0x4E37,\n\t0x8153, 0x4E3C, 0x8154, 0x4E40, 0x8155, 0x4E41, 0x8156, 0x4E42,\n\t0x8157, 0x4E44, 0x8158, 0x4E46, 0x8159, 0x4E4A, 0x815A, 0x4E51,\n\t0x815B, 0x4E55, 0x815C, 0x4E57, 0x815D, 0x4E5A, 0x815E, 0x4E5B,\n\t0x815F, 0x4E62, 0x8160, 0x4E63, 0x8161, 0x4E64, 0x8162, 0x4E65,\n\t0x8163, 0x4E67, 0x8164, 0x4E68, 0x8165, 0x4E6A, 0x8166, 0x4E6B,\n\t0x8167, 0x4E6C, 0x8168, 0x4E6D, 0x8169, 0x4E6E, 0x816A, 0x4E6F,\n\t0x816B, 0x4E72, 0x816C, 0x4E74, 0x816D, 0x4E75, 0x816E, 0x4E76,\n\t0x816F, 0x4E77, 0x8170, 0x4E78, 0x8171, 0x4E79, 0x8172, 0x4E7A,\n\t0x8173, 0x4E7B, 0x8174, 0x4E7C, 0x8175, 0x4E7D, 0x8176, 0x4E7F,\n\t0x8177, 0x4E80, 0x8178, 0x4E81, 0x8179, 0x4E82, 0x817A, 0x4E83,\n\t0x817B, 0x4E84, 0x817C, 0x4E85, 0x817D, 0x4E87, 0x817E, 0x4E8A,\n\t0x8180, 0x4E90, 0x8181, 0x4E96, 0x8182, 0x4E97, 0x8183, 0x4E99,\n\t0x8184, 0x4E9C, 0x8185, 0x4E9D, 0x8186, 0x4E9E, 0x8187, 0x4EA3,\n\t0x8188, 0x4EAA, 0x8189, 0x4EAF, 0x818A, 0x4EB0, 0x818B, 0x4EB1,\n\t0x818C, 0x4EB4, 0x818D, 0x4EB6, 0x818E, 0x4EB7, 0x818F, 0x4EB8,\n\t0x8190, 0x4EB9, 0x8191, 0x4EBC, 0x8192, 0x4EBD, 0x8193, 0x4EBE,\n\t0x8194, 0x4EC8, 0x8195, 0x4ECC, 0x8196, 0x4ECF, 0x8197, 0x4ED0,\n\t0x8198, 0x4ED2, 0x8199, 0x4EDA, 0x819A, 0x4EDB, 0x819B, 0x4EDC,\n\t0x819C, 0x4EE0, 0x819D, 0x4EE2, 0x819E, 0x4EE6, 0x819F, 0x4EE7,\n\t0x81A0, 0x4EE9, 0x81A1, 0x4EED, 0x81A2, 0x4EEE, 0x81A3, 0x4EEF,\n\t0x81A4, 0x4EF1, 0x81A5, 0x4EF4, 0x81A6, 0x4EF8, 0x81A7, 0x4EF9,\n\t0x81A8, 0x4EFA, 0x81A9, 0x4EFC, 0x81AA, 0x4EFE, 0x81AB, 0x4F00,\n\t0x81AC, 0x4F02, 0x81AD, 0x4F03, 0x81AE, 0x4F04, 0x81AF, 0x4F05,\n\t0x81B0, 0x4F06, 0x81B1, 0x4F07, 0x81B2, 0x4F08, 0x81B3, 0x4F0B,\n\t0x81B4, 0x4F0C, 0x81B5, 0x4F12, 0x81B6, 0x4F13, 0x81B7, 0x4F14,\n\t0x81B8, 0x4F15, 0x81B9, 0x4F16, 0x81BA, 0x4F1C, 0x81BB, 0x4F1D,\n\t0x81BC, 0x4F21, 0x81BD, 0x4F23, 0x81BE, 0x4F28, 0x81BF, 0x4F29,\n\t0x81C0, 0x4F2C, 0x81C1, 0x4F2D, 0x81C2, 0x4F2E, 0x81C3, 0x4F31,\n\t0x81C4, 0x4F33, 0x81C5, 0x4F35, 0x81C6, 0x4F37, 0x81C7, 0x4F39,\n\t0x81C8, 0x4F3B, 0x81C9, 0x4F3E, 0x81CA, 0x4F3F, 0x81CB, 0x4F40,\n\t0x81CC, 0x4F41, 0x81CD, 0x4F42, 0x81CE, 0x4F44, 0x81CF, 0x4F45,\n\t0x81D0, 0x4F47, 0x81D1, 0x4F48, 0x81D2, 0x4F49, 0x81D3, 0x4F4A,\n\t0x81D4, 0x4F4B, 0x81D5, 0x4F4C, 0x81D6, 0x4F52, 0x81D7, 0x4F54,\n\t0x81D8, 0x4F56, 0x81D9, 0x4F61, 0x81DA, 0x4F62, 0x81DB, 0x4F66,\n\t0x81DC, 0x4F68, 0x81DD, 0x4F6A, 0x81DE, 0x4F6B, 0x81DF, 0x4F6D,\n\t0x81E0, 0x4F6E, 0x81E1, 0x4F71, 0x81E2, 0x4F72, 0x81E3, 0x4F75,\n\t0x81E4, 0x4F77, 0x81E5, 0x4F78, 0x81E6, 0x4F79, 0x81E7, 0x4F7A,\n\t0x81E8, 0x4F7D, 0x81E9, 0x4F80, 0x81EA, 0x4F81, 0x81EB, 0x4F82,\n\t0x81EC, 0x4F85, 0x81ED, 0x4F86, 0x81EE, 0x4F87, 0x81EF, 0x4F8A,\n\t0x81F0, 0x4F8C, 0x81F1, 0x4F8E, 0x81F2, 0x4F90, 0x81F3, 0x4F92,\n\t0x81F4, 0x4F93, 0x81F5, 0x4F95, 0x81F6, 0x4F96, 0x81F7, 0x4F98,\n\t0x81F8, 0x4F99, 0x81F9, 0x4F9A, 0x81FA, 0x4F9C, 0x81FB, 0x4F9E,\n\t0x81FC, 0x4F9F, 0x81FD, 0x4FA1, 0x81FE, 0x4FA2, 0x8240, 0x4FA4,\n\t0x8241, 0x4FAB, 0x8242, 0x4FAD, 0x8243, 0x4FB0, 0x8244, 0x4FB1,\n\t0x8245, 0x4FB2, 0x8246, 0x4FB3, 0x8247, 0x4FB4, 0x8248, 0x4FB6,\n\t0x8249, 0x4FB7, 0x824A, 0x4FB8, 0x824B, 0x4FB9, 0x824C, 0x4FBA,\n\t0x824D, 0x4FBB, 0x824E, 0x4FBC, 0x824F, 0x4FBD, 0x8250, 0x4FBE,\n\t0x8251, 0x4FC0, 0x8252, 0x4FC1, 0x8253, 0x4FC2, 0x8254, 0x4FC6,\n\t0x8255, 0x4FC7, 0x8256, 0x4FC8, 0x8257, 0x4FC9, 0x8258, 0x4FCB,\n\t0x8259, 0x4FCC, 0x825A, 0x4FCD, 0x825B, 0x4FD2, 0x825C, 0x4FD3,\n\t0x825D, 0x4FD4, 0x825E, 0x4FD5, 0x825F, 0x4FD6, 0x8260, 0x4FD9,\n\t0x8261, 0x4FDB, 0x8262, 0x4FE0, 0x8263, 0x4FE2, 0x8264, 0x4FE4,\n\t0x8265, 0x4FE5, 0x8266, 0x4FE7, 0x8267, 0x4FEB, 0x8268, 0x4FEC,\n\t0x8269, 0x4FF0, 0x826A, 0x4FF2, 0x826B, 0x4FF4, 0x826C, 0x4FF5,\n\t0x826D, 0x4FF6, 0x826E, 0x4FF7, 0x826F, 0x4FF9, 0x8270, 0x4FFB,\n\t0x8271, 0x4FFC, 0x8272, 0x4FFD, 0x8273, 0x4FFF, 0x8274, 0x5000,\n\t0x8275, 0x5001, 0x8276, 0x5002, 0x8277, 0x5003, 0x8278, 0x5004,\n\t0x8279, 0x5005, 0x827A, 0x5006, 0x827B, 0x5007, 0x827C, 0x5008,\n\t0x827D, 0x5009, 0x827E, 0x500A, 0x8280, 0x500B, 0x8281, 0x500E,\n\t0x8282, 0x5010, 0x8283, 0x5011, 0x8284, 0x5013, 0x8285, 0x5015,\n\t0x8286, 0x5016, 0x8287, 0x5017, 0x8288, 0x501B, 0x8289, 0x501D,\n\t0x828A, 0x501E, 0x828B, 0x5020, 0x828C, 0x5022, 0x828D, 0x5023,\n\t0x828E, 0x5024, 0x828F, 0x5027, 0x8290, 0x502B, 0x8291, 0x502F,\n\t0x8292, 0x5030, 0x8293, 0x5031, 0x8294, 0x5032, 0x8295, 0x5033,\n\t0x8296, 0x5034, 0x8297, 0x5035, 0x8298, 0x5036, 0x8299, 0x5037,\n\t0x829A, 0x5038, 0x829B, 0x5039, 0x829C, 0x503B, 0x829D, 0x503D,\n\t0x829E, 0x503F, 0x829F, 0x5040, 0x82A0, 0x5041, 0x82A1, 0x5042,\n\t0x82A2, 0x5044, 0x82A3, 0x5045, 0x82A4, 0x5046, 0x82A5, 0x5049,\n\t0x82A6, 0x504A, 0x82A7, 0x504B, 0x82A8, 0x504D, 0x82A9, 0x5050,\n\t0x82AA, 0x5051, 0x82AB, 0x5052, 0x82AC, 0x5053, 0x82AD, 0x5054,\n\t0x82AE, 0x5056, 0x82AF, 0x5057, 0x82B0, 0x5058, 0x82B1, 0x5059,\n\t0x82B2, 0x505B, 0x82B3, 0x505D, 0x82B4, 0x505E, 0x82B5, 0x505F,\n\t0x82B6, 0x5060, 0x82B7, 0x5061, 0x82B8, 0x5062, 0x82B9, 0x5063,\n\t0x82BA, 0x5064, 0x82BB, 0x5066, 0x82BC, 0x5067, 0x82BD, 0x5068,\n\t0x82BE, 0x5069, 0x82BF, 0x506A, 0x82C0, 0x506B, 0x82C1, 0x506D,\n\t0x82C2, 0x506E, 0x82C3, 0x506F, 0x82C4, 0x5070, 0x82C5, 0x5071,\n\t0x82C6, 0x5072, 0x82C7, 0x5073, 0x82C8, 0x5074, 0x82C9, 0x5075,\n\t0x82CA, 0x5078, 0x82CB, 0x5079, 0x82CC, 0x507A, 0x82CD, 0x507C,\n\t0x82CE, 0x507D, 0x82CF, 0x5081, 0x82D0, 0x5082, 0x82D1, 0x5083,\n\t0x82D2, 0x5084, 0x82D3, 0x5086, 0x82D4, 0x5087, 0x82D5, 0x5089,\n\t0x82D6, 0x508A, 0x82D7, 0x508B, 0x82D8, 0x508C, 0x82D9, 0x508E,\n\t0x82DA, 0x508F, 0x82DB, 0x5090, 0x82DC, 0x5091, 0x82DD, 0x5092,\n\t0x82DE, 0x5093, 0x82DF, 0x5094, 0x82E0, 0x5095, 0x82E1, 0x5096,\n\t0x82E2, 0x5097, 0x82E3, 0x5098, 0x82E4, 0x5099, 0x82E5, 0x509A,\n\t0x82E6, 0x509B, 0x82E7, 0x509C, 0x82E8, 0x509D, 0x82E9, 0x509E,\n\t0x82EA, 0x509F, 0x82EB, 0x50A0, 0x82EC, 0x50A1, 0x82ED, 0x50A2,\n\t0x82EE, 0x50A4, 0x82EF, 0x50A6, 0x82F0, 0x50AA, 0x82F1, 0x50AB,\n\t0x82F2, 0x50AD, 0x82F3, 0x50AE, 0x82F4, 0x50AF, 0x82F5, 0x50B0,\n\t0x82F6, 0x50B1, 0x82F7, 0x50B3, 0x82F8, 0x50B4, 0x82F9, 0x50B5,\n\t0x82FA, 0x50B6, 0x82FB, 0x50B7, 0x82FC, 0x50B8, 0x82FD, 0x50B9,\n\t0x82FE, 0x50BC, 0x8340, 0x50BD, 0x8341, 0x50BE, 0x8342, 0x50BF,\n\t0x8343, 0x50C0, 0x8344, 0x50C1, 0x8345, 0x50C2, 0x8346, 0x50C3,\n\t0x8347, 0x50C4, 0x8348, 0x50C5, 0x8349, 0x50C6, 0x834A, 0x50C7,\n\t0x834B, 0x50C8, 0x834C, 0x50C9, 0x834D, 0x50CA, 0x834E, 0x50CB,\n\t0x834F, 0x50CC, 0x8350, 0x50CD, 0x8351, 0x50CE, 0x8352, 0x50D0,\n\t0x8353, 0x50D1, 0x8354, 0x50D2, 0x8355, 0x50D3, 0x8356, 0x50D4,\n\t0x8357, 0x50D5, 0x8358, 0x50D7, 0x8359, 0x50D8, 0x835A, 0x50D9,\n\t0x835B, 0x50DB, 0x835C, 0x50DC, 0x835D, 0x50DD, 0x835E, 0x50DE,\n\t0x835F, 0x50DF, 0x8360, 0x50E0, 0x8361, 0x50E1, 0x8362, 0x50E2,\n\t0x8363, 0x50E3, 0x8364, 0x50E4, 0x8365, 0x50E5, 0x8366, 0x50E8,\n\t0x8367, 0x50E9, 0x8368, 0x50EA, 0x8369, 0x50EB, 0x836A, 0x50EF,\n\t0x836B, 0x50F0, 0x836C, 0x50F1, 0x836D, 0x50F2, 0x836E, 0x50F4,\n\t0x836F, 0x50F6, 0x8370, 0x50F7, 0x8371, 0x50F8, 0x8372, 0x50F9,\n\t0x8373, 0x50FA, 0x8374, 0x50FC, 0x8375, 0x50FD, 0x8376, 0x50FE,\n\t0x8377, 0x50FF, 0x8378, 0x5100, 0x8379, 0x5101, 0x837A, 0x5102,\n\t0x837B, 0x5103, 0x837C, 0x5104, 0x837D, 0x5105, 0x837E, 0x5108,\n\t0x8380, 0x5109, 0x8381, 0x510A, 0x8382, 0x510C, 0x8383, 0x510D,\n\t0x8384, 0x510E, 0x8385, 0x510F, 0x8386, 0x5110, 0x8387, 0x5111,\n\t0x8388, 0x5113, 0x8389, 0x5114, 0x838A, 0x5115, 0x838B, 0x5116,\n\t0x838C, 0x5117, 0x838D, 0x5118, 0x838E, 0x5119, 0x838F, 0x511A,\n\t0x8390, 0x511B, 0x8391, 0x511C, 0x8392, 0x511D, 0x8393, 0x511E,\n\t0x8394, 0x511F, 0x8395, 0x5120, 0x8396, 0x5122, 0x8397, 0x5123,\n\t0x8398, 0x5124, 0x8399, 0x5125, 0x839A, 0x5126, 0x839B, 0x5127,\n\t0x839C, 0x5128, 0x839D, 0x5129, 0x839E, 0x512A, 0x839F, 0x512B,\n\t0x83A0, 0x512C, 0x83A1, 0x512D, 0x83A2, 0x512E, 0x83A3, 0x512F,\n\t0x83A4, 0x5130, 0x83A5, 0x5131, 0x83A6, 0x5132, 0x83A7, 0x5133,\n\t0x83A8, 0x5134, 0x83A9, 0x5135, 0x83AA, 0x5136, 0x83AB, 0x5137,\n\t0x83AC, 0x5138, 0x83AD, 0x5139, 0x83AE, 0x513A, 0x83AF, 0x513B,\n\t0x83B0, 0x513C, 0x83B1, 0x513D, 0x83B2, 0x513E, 0x83B3, 0x5142,\n\t0x83B4, 0x5147, 0x83B5, 0x514A, 0x83B6, 0x514C, 0x83B7, 0x514E,\n\t0x83B8, 0x514F, 0x83B9, 0x5150, 0x83BA, 0x5152, 0x83BB, 0x5153,\n\t0x83BC, 0x5157, 0x83BD, 0x5158, 0x83BE, 0x5159, 0x83BF, 0x515B,\n\t0x83C0, 0x515D, 0x83C1, 0x515E, 0x83C2, 0x515F, 0x83C3, 0x5160,\n\t0x83C4, 0x5161, 0x83C5, 0x5163, 0x83C6, 0x5164, 0x83C7, 0x5166,\n\t0x83C8, 0x5167, 0x83C9, 0x5169, 0x83CA, 0x516A, 0x83CB, 0x516F,\n\t0x83CC, 0x5172, 0x83CD, 0x517A, 0x83CE, 0x517E, 0x83CF, 0x517F,\n\t0x83D0, 0x5183, 0x83D1, 0x5184, 0x83D2, 0x5186, 0x83D3, 0x5187,\n\t0x83D4, 0x518A, 0x83D5, 0x518B, 0x83D6, 0x518E, 0x83D7, 0x518F,\n\t0x83D8, 0x5190, 0x83D9, 0x5191, 0x83DA, 0x5193, 0x83DB, 0x5194,\n\t0x83DC, 0x5198, 0x83DD, 0x519A, 0x83DE, 0x519D, 0x83DF, 0x519E,\n\t0x83E0, 0x519F, 0x83E1, 0x51A1, 0x83E2, 0x51A3, 0x83E3, 0x51A6,\n\t0x83E4, 0x51A7, 0x83E5, 0x51A8, 0x83E6, 0x51A9, 0x83E7, 0x51AA,\n\t0x83E8, 0x51AD, 0x83E9, 0x51AE, 0x83EA, 0x51B4, 0x83EB, 0x51B8,\n\t0x83EC, 0x51B9, 0x83ED, 0x51BA, 0x83EE, 0x51BE, 0x83EF, 0x51BF,\n\t0x83F0, 0x51C1, 0x83F1, 0x51C2, 0x83F2, 0x51C3, 0x83F3, 0x51C5,\n\t0x83F4, 0x51C8, 0x83F5, 0x51CA, 0x83F6, 0x51CD, 0x83F7, 0x51CE,\n\t0x83F8, 0x51D0, 0x83F9, 0x51D2, 0x83FA, 0x51D3, 0x83FB, 0x51D4,\n\t0x83FC, 0x51D5, 0x83FD, 0x51D6, 0x83FE, 0x51D7, 0x8440, 0x51D8,\n\t0x8441, 0x51D9, 0x8442, 0x51DA, 0x8443, 0x51DC, 0x8444, 0x51DE,\n\t0x8445, 0x51DF, 0x8446, 0x51E2, 0x8447, 0x51E3, 0x8448, 0x51E5,\n\t0x8449, 0x51E6, 0x844A, 0x51E7, 0x844B, 0x51E8, 0x844C, 0x51E9,\n\t0x844D, 0x51EA, 0x844E, 0x51EC, 0x844F, 0x51EE, 0x8450, 0x51F1,\n\t0x8451, 0x51F2, 0x8452, 0x51F4, 0x8453, 0x51F7, 0x8454, 0x51FE,\n\t0x8455, 0x5204, 0x8456, 0x5205, 0x8457, 0x5209, 0x8458, 0x520B,\n\t0x8459, 0x520C, 0x845A, 0x520F, 0x845B, 0x5210, 0x845C, 0x5213,\n\t0x845D, 0x5214, 0x845E, 0x5215, 0x845F, 0x521C, 0x8460, 0x521E,\n\t0x8461, 0x521F, 0x8462, 0x5221, 0x8463, 0x5222, 0x8464, 0x5223,\n\t0x8465, 0x5225, 0x8466, 0x5226, 0x8467, 0x5227, 0x8468, 0x522A,\n\t0x8469, 0x522C, 0x846A, 0x522F, 0x846B, 0x5231, 0x846C, 0x5232,\n\t0x846D, 0x5234, 0x846E, 0x5235, 0x846F, 0x523C, 0x8470, 0x523E,\n\t0x8471, 0x5244, 0x8472, 0x5245, 0x8473, 0x5246, 0x8474, 0x5247,\n\t0x8475, 0x5248, 0x8476, 0x5249, 0x8477, 0x524B, 0x8478, 0x524E,\n\t0x8479, 0x524F, 0x847A, 0x5252, 0x847B, 0x5253, 0x847C, 0x5255,\n\t0x847D, 0x5257, 0x847E, 0x5258, 0x8480, 0x5259, 0x8481, 0x525A,\n\t0x8482, 0x525B, 0x8483, 0x525D, 0x8484, 0x525F, 0x8485, 0x5260,\n\t0x8486, 0x5262, 0x8487, 0x5263, 0x8488, 0x5264, 0x8489, 0x5266,\n\t0x848A, 0x5268, 0x848B, 0x526B, 0x848C, 0x526C, 0x848D, 0x526D,\n\t0x848E, 0x526E, 0x848F, 0x5270, 0x8490, 0x5271, 0x8491, 0x5273,\n\t0x8492, 0x5274, 0x8493, 0x5275, 0x8494, 0x5276, 0x8495, 0x5277,\n\t0x8496, 0x5278, 0x8497, 0x5279, 0x8498, 0x527A, 0x8499, 0x527B,\n\t0x849A, 0x527C, 0x849B, 0x527E, 0x849C, 0x5280, 0x849D, 0x5283,\n\t0x849E, 0x5284, 0x849F, 0x5285, 0x84A0, 0x5286, 0x84A1, 0x5287,\n\t0x84A2, 0x5289, 0x84A3, 0x528A, 0x84A4, 0x528B, 0x84A5, 0x528C,\n\t0x84A6, 0x528D, 0x84A7, 0x528E, 0x84A8, 0x528F, 0x84A9, 0x5291,\n\t0x84AA, 0x5292, 0x84AB, 0x5294, 0x84AC, 0x5295, 0x84AD, 0x5296,\n\t0x84AE, 0x5297, 0x84AF, 0x5298, 0x84B0, 0x5299, 0x84B1, 0x529A,\n\t0x84B2, 0x529C, 0x84B3, 0x52A4, 0x84B4, 0x52A5, 0x84B5, 0x52A6,\n\t0x84B6, 0x52A7, 0x84B7, 0x52AE, 0x84B8, 0x52AF, 0x84B9, 0x52B0,\n\t0x84BA, 0x52B4, 0x84BB, 0x52B5, 0x84BC, 0x52B6, 0x84BD, 0x52B7,\n\t0x84BE, 0x52B8, 0x84BF, 0x52B9, 0x84C0, 0x52BA, 0x84C1, 0x52BB,\n\t0x84C2, 0x52BC, 0x84C3, 0x52BD, 0x84C4, 0x52C0, 0x84C5, 0x52C1,\n\t0x84C6, 0x52C2, 0x84C7, 0x52C4, 0x84C8, 0x52C5, 0x84C9, 0x52C6,\n\t0x84CA, 0x52C8, 0x84CB, 0x52CA, 0x84CC, 0x52CC, 0x84CD, 0x52CD,\n\t0x84CE, 0x52CE, 0x84CF, 0x52CF, 0x84D0, 0x52D1, 0x84D1, 0x52D3,\n\t0x84D2, 0x52D4, 0x84D3, 0x52D5, 0x84D4, 0x52D7, 0x84D5, 0x52D9,\n\t0x84D6, 0x52DA, 0x84D7, 0x52DB, 0x84D8, 0x52DC, 0x84D9, 0x52DD,\n\t0x84DA, 0x52DE, 0x84DB, 0x52E0, 0x84DC, 0x52E1, 0x84DD, 0x52E2,\n\t0x84DE, 0x52E3, 0x84DF, 0x52E5, 0x84E0, 0x52E6, 0x84E1, 0x52E7,\n\t0x84E2, 0x52E8, 0x84E3, 0x52E9, 0x84E4, 0x52EA, 0x84E5, 0x52EB,\n\t0x84E6, 0x52EC, 0x84E7, 0x52ED, 0x84E8, 0x52EE, 0x84E9, 0x52EF,\n\t0x84EA, 0x52F1, 0x84EB, 0x52F2, 0x84EC, 0x52F3, 0x84ED, 0x52F4,\n\t0x84EE, 0x52F5, 0x84EF, 0x52F6, 0x84F0, 0x52F7, 0x84F1, 0x52F8,\n\t0x84F2, 0x52FB, 0x84F3, 0x52FC, 0x84F4, 0x52FD, 0x84F5, 0x5301,\n\t0x84F6, 0x5302, 0x84F7, 0x5303, 0x84F8, 0x5304, 0x84F9, 0x5307,\n\t0x84FA, 0x5309, 0x84FB, 0x530A, 0x84FC, 0x530B, 0x84FD, 0x530C,\n\t0x84FE, 0x530E, 0x8540, 0x5311, 0x8541, 0x5312, 0x8542, 0x5313,\n\t0x8543, 0x5314, 0x8544, 0x5318, 0x8545, 0x531B, 0x8546, 0x531C,\n\t0x8547, 0x531E, 0x8548, 0x531F, 0x8549, 0x5322, 0x854A, 0x5324,\n\t0x854B, 0x5325, 0x854C, 0x5327, 0x854D, 0x5328, 0x854E, 0x5329,\n\t0x854F, 0x532B, 0x8550, 0x532C, 0x8551, 0x532D, 0x8552, 0x532F,\n\t0x8553, 0x5330, 0x8554, 0x5331, 0x8555, 0x5332, 0x8556, 0x5333,\n\t0x8557, 0x5334, 0x8558, 0x5335, 0x8559, 0x5336, 0x855A, 0x5337,\n\t0x855B, 0x5338, 0x855C, 0x533C, 0x855D, 0x533D, 0x855E, 0x5340,\n\t0x855F, 0x5342, 0x8560, 0x5344, 0x8561, 0x5346, 0x8562, 0x534B,\n\t0x8563, 0x534C, 0x8564, 0x534D, 0x8565, 0x5350, 0x8566, 0x5354,\n\t0x8567, 0x5358, 0x8568, 0x5359, 0x8569, 0x535B, 0x856A, 0x535D,\n\t0x856B, 0x5365, 0x856C, 0x5368, 0x856D, 0x536A, 0x856E, 0x536C,\n\t0x856F, 0x536D, 0x8570, 0x5372, 0x8571, 0x5376, 0x8572, 0x5379,\n\t0x8573, 0x537B, 0x8574, 0x537C, 0x8575, 0x537D, 0x8576, 0x537E,\n\t0x8577, 0x5380, 0x8578, 0x5381, 0x8579, 0x5383, 0x857A, 0x5387,\n\t0x857B, 0x5388, 0x857C, 0x538A, 0x857D, 0x538E, 0x857E, 0x538F,\n\t0x8580, 0x5390, 0x8581, 0x5391, 0x8582, 0x5392, 0x8583, 0x5393,\n\t0x8584, 0x5394, 0x8585, 0x5396, 0x8586, 0x5397, 0x8587, 0x5399,\n\t0x8588, 0x539B, 0x8589, 0x539C, 0x858A, 0x539E, 0x858B, 0x53A0,\n\t0x858C, 0x53A1, 0x858D, 0x53A4, 0x858E, 0x53A7, 0x858F, 0x53AA,\n\t0x8590, 0x53AB, 0x8591, 0x53AC, 0x8592, 0x53AD, 0x8593, 0x53AF,\n\t0x8594, 0x53B0, 0x8595, 0x53B1, 0x8596, 0x53B2, 0x8597, 0x53B3,\n\t0x8598, 0x53B4, 0x8599, 0x53B5, 0x859A, 0x53B7, 0x859B, 0x53B8,\n\t0x859C, 0x53B9, 0x859D, 0x53BA, 0x859E, 0x53BC, 0x859F, 0x53BD,\n\t0x85A0, 0x53BE, 0x85A1, 0x53C0, 0x85A2, 0x53C3, 0x85A3, 0x53C4,\n\t0x85A4, 0x53C5, 0x85A5, 0x53C6, 0x85A6, 0x53C7, 0x85A7, 0x53CE,\n\t0x85A8, 0x53CF, 0x85A9, 0x53D0, 0x85AA, 0x53D2, 0x85AB, 0x53D3,\n\t0x85AC, 0x53D5, 0x85AD, 0x53DA, 0x85AE, 0x53DC, 0x85AF, 0x53DD,\n\t0x85B0, 0x53DE, 0x85B1, 0x53E1, 0x85B2, 0x53E2, 0x85B3, 0x53E7,\n\t0x85B4, 0x53F4, 0x85B5, 0x53FA, 0x85B6, 0x53FE, 0x85B7, 0x53FF,\n\t0x85B8, 0x5400, 0x85B9, 0x5402, 0x85BA, 0x5405, 0x85BB, 0x5407,\n\t0x85BC, 0x540B, 0x85BD, 0x5414, 0x85BE, 0x5418, 0x85BF, 0x5419,\n\t0x85C0, 0x541A, 0x85C1, 0x541C, 0x85C2, 0x5422, 0x85C3, 0x5424,\n\t0x85C4, 0x5425, 0x85C5, 0x542A, 0x85C6, 0x5430, 0x85C7, 0x5433,\n\t0x85C8, 0x5436, 0x85C9, 0x5437, 0x85CA, 0x543A, 0x85CB, 0x543D,\n\t0x85CC, 0x543F, 0x85CD, 0x5441, 0x85CE, 0x5442, 0x85CF, 0x5444,\n\t0x85D0, 0x5445, 0x85D1, 0x5447, 0x85D2, 0x5449, 0x85D3, 0x544C,\n\t0x85D4, 0x544D, 0x85D5, 0x544E, 0x85D6, 0x544F, 0x85D7, 0x5451,\n\t0x85D8, 0x545A, 0x85D9, 0x545D, 0x85DA, 0x545E, 0x85DB, 0x545F,\n\t0x85DC, 0x5460, 0x85DD, 0x5461, 0x85DE, 0x5463, 0x85DF, 0x5465,\n\t0x85E0, 0x5467, 0x85E1, 0x5469, 0x85E2, 0x546A, 0x85E3, 0x546B,\n\t0x85E4, 0x546C, 0x85E5, 0x546D, 0x85E6, 0x546E, 0x85E7, 0x546F,\n\t0x85E8, 0x5470, 0x85E9, 0x5474, 0x85EA, 0x5479, 0x85EB, 0x547A,\n\t0x85EC, 0x547E, 0x85ED, 0x547F, 0x85EE, 0x5481, 0x85EF, 0x5483,\n\t0x85F0, 0x5485, 0x85F1, 0x5487, 0x85F2, 0x5488, 0x85F3, 0x5489,\n\t0x85F4, 0x548A, 0x85F5, 0x548D, 0x85F6, 0x5491, 0x85F7, 0x5493,\n\t0x85F8, 0x5497, 0x85F9, 0x5498, 0x85FA, 0x549C, 0x85FB, 0x549E,\n\t0x85FC, 0x549F, 0x85FD, 0x54A0, 0x85FE, 0x54A1, 0x8640, 0x54A2,\n\t0x8641, 0x54A5, 0x8642, 0x54AE, 0x8643, 0x54B0, 0x8644, 0x54B2,\n\t0x8645, 0x54B5, 0x8646, 0x54B6, 0x8647, 0x54B7, 0x8648, 0x54B9,\n\t0x8649, 0x54BA, 0x864A, 0x54BC, 0x864B, 0x54BE, 0x864C, 0x54C3,\n\t0x864D, 0x54C5, 0x864E, 0x54CA, 0x864F, 0x54CB, 0x8650, 0x54D6,\n\t0x8651, 0x54D8, 0x8652, 0x54DB, 0x8653, 0x54E0, 0x8654, 0x54E1,\n\t0x8655, 0x54E2, 0x8656, 0x54E3, 0x8657, 0x54E4, 0x8658, 0x54EB,\n\t0x8659, 0x54EC, 0x865A, 0x54EF, 0x865B, 0x54F0, 0x865C, 0x54F1,\n\t0x865D, 0x54F4, 0x865E, 0x54F5, 0x865F, 0x54F6, 0x8660, 0x54F7,\n\t0x8661, 0x54F8, 0x8662, 0x54F9, 0x8663, 0x54FB, 0x8664, 0x54FE,\n\t0x8665, 0x5500, 0x8666, 0x5502, 0x8667, 0x5503, 0x8668, 0x5504,\n\t0x8669, 0x5505, 0x866A, 0x5508, 0x866B, 0x550A, 0x866C, 0x550B,\n\t0x866D, 0x550C, 0x866E, 0x550D, 0x866F, 0x550E, 0x8670, 0x5512,\n\t0x8671, 0x5513, 0x8672, 0x5515, 0x8673, 0x5516, 0x8674, 0x5517,\n\t0x8675, 0x5518, 0x8676, 0x5519, 0x8677, 0x551A, 0x8678, 0x551C,\n\t0x8679, 0x551D, 0x867A, 0x551E, 0x867B, 0x551F, 0x867C, 0x5521,\n\t0x867D, 0x5525, 0x867E, 0x5526, 0x8680, 0x5528, 0x8681, 0x5529,\n\t0x8682, 0x552B, 0x8683, 0x552D, 0x8684, 0x5532, 0x8685, 0x5534,\n\t0x8686, 0x5535, 0x8687, 0x5536, 0x8688, 0x5538, 0x8689, 0x5539,\n\t0x868A, 0x553A, 0x868B, 0x553B, 0x868C, 0x553D, 0x868D, 0x5540,\n\t0x868E, 0x5542, 0x868F, 0x5545, 0x8690, 0x5547, 0x8691, 0x5548,\n\t0x8692, 0x554B, 0x8693, 0x554C, 0x8694, 0x554D, 0x8695, 0x554E,\n\t0x8696, 0x554F, 0x8697, 0x5551, 0x8698, 0x5552, 0x8699, 0x5553,\n\t0x869A, 0x5554, 0x869B, 0x5557, 0x869C, 0x5558, 0x869D, 0x5559,\n\t0x869E, 0x555A, 0x869F, 0x555B, 0x86A0, 0x555D, 0x86A1, 0x555E,\n\t0x86A2, 0x555F, 0x86A3, 0x5560, 0x86A4, 0x5562, 0x86A5, 0x5563,\n\t0x86A6, 0x5568, 0x86A7, 0x5569, 0x86A8, 0x556B, 0x86A9, 0x556F,\n\t0x86AA, 0x5570, 0x86AB, 0x5571, 0x86AC, 0x5572, 0x86AD, 0x5573,\n\t0x86AE, 0x5574, 0x86AF, 0x5579, 0x86B0, 0x557A, 0x86B1, 0x557D,\n\t0x86B2, 0x557F, 0x86B3, 0x5585, 0x86B4, 0x5586, 0x86B5, 0x558C,\n\t0x86B6, 0x558D, 0x86B7, 0x558E, 0x86B8, 0x5590, 0x86B9, 0x5592,\n\t0x86BA, 0x5593, 0x86BB, 0x5595, 0x86BC, 0x5596, 0x86BD, 0x5597,\n\t0x86BE, 0x559A, 0x86BF, 0x559B, 0x86C0, 0x559E, 0x86C1, 0x55A0,\n\t0x86C2, 0x55A1, 0x86C3, 0x55A2, 0x86C4, 0x55A3, 0x86C5, 0x55A4,\n\t0x86C6, 0x55A5, 0x86C7, 0x55A6, 0x86C8, 0x55A8, 0x86C9, 0x55A9,\n\t0x86CA, 0x55AA, 0x86CB, 0x55AB, 0x86CC, 0x55AC, 0x86CD, 0x55AD,\n\t0x86CE, 0x55AE, 0x86CF, 0x55AF, 0x86D0, 0x55B0, 0x86D1, 0x55B2,\n\t0x86D2, 0x55B4, 0x86D3, 0x55B6, 0x86D4, 0x55B8, 0x86D5, 0x55BA,\n\t0x86D6, 0x55BC, 0x86D7, 0x55BF, 0x86D8, 0x55C0, 0x86D9, 0x55C1,\n\t0x86DA, 0x55C2, 0x86DB, 0x55C3, 0x86DC, 0x55C6, 0x86DD, 0x55C7,\n\t0x86DE, 0x55C8, 0x86DF, 0x55CA, 0x86E0, 0x55CB, 0x86E1, 0x55CE,\n\t0x86E2, 0x55CF, 0x86E3, 0x55D0, 0x86E4, 0x55D5, 0x86E5, 0x55D7,\n\t0x86E6, 0x55D8, 0x86E7, 0x55D9, 0x86E8, 0x55DA, 0x86E9, 0x55DB,\n\t0x86EA, 0x55DE, 0x86EB, 0x55E0, 0x86EC, 0x55E2, 0x86ED, 0x55E7,\n\t0x86EE, 0x55E9, 0x86EF, 0x55ED, 0x86F0, 0x55EE, 0x86F1, 0x55F0,\n\t0x86F2, 0x55F1, 0x86F3, 0x55F4, 0x86F4, 0x55F6, 0x86F5, 0x55F8,\n\t0x86F6, 0x55F9, 0x86F7, 0x55FA, 0x86F8, 0x55FB, 0x86F9, 0x55FC,\n\t0x86FA, 0x55FF, 0x86FB, 0x5602, 0x86FC, 0x5603, 0x86FD, 0x5604,\n\t0x86FE, 0x5605, 0x8740, 0x5606, 0x8741, 0x5607, 0x8742, 0x560A,\n\t0x8743, 0x560B, 0x8744, 0x560D, 0x8745, 0x5610, 0x8746, 0x5611,\n\t0x8747, 0x5612, 0x8748, 0x5613, 0x8749, 0x5614, 0x874A, 0x5615,\n\t0x874B, 0x5616, 0x874C, 0x5617, 0x874D, 0x5619, 0x874E, 0x561A,\n\t0x874F, 0x561C, 0x8750, 0x561D, 0x8751, 0x5620, 0x8752, 0x5621,\n\t0x8753, 0x5622, 0x8754, 0x5625, 0x8755, 0x5626, 0x8756, 0x5628,\n\t0x8757, 0x5629, 0x8758, 0x562A, 0x8759, 0x562B, 0x875A, 0x562E,\n\t0x875B, 0x562F, 0x875C, 0x5630, 0x875D, 0x5633, 0x875E, 0x5635,\n\t0x875F, 0x5637, 0x8760, 0x5638, 0x8761, 0x563A, 0x8762, 0x563C,\n\t0x8763, 0x563D, 0x8764, 0x563E, 0x8765, 0x5640, 0x8766, 0x5641,\n\t0x8767, 0x5642, 0x8768, 0x5643, 0x8769, 0x5644, 0x876A, 0x5645,\n\t0x876B, 0x5646, 0x876C, 0x5647, 0x876D, 0x5648, 0x876E, 0x5649,\n\t0x876F, 0x564A, 0x8770, 0x564B, 0x8771, 0x564F, 0x8772, 0x5650,\n\t0x8773, 0x5651, 0x8774, 0x5652, 0x8775, 0x5653, 0x8776, 0x5655,\n\t0x8777, 0x5656, 0x8778, 0x565A, 0x8779, 0x565B, 0x877A, 0x565D,\n\t0x877B, 0x565E, 0x877C, 0x565F, 0x877D, 0x5660, 0x877E, 0x5661,\n\t0x8780, 0x5663, 0x8781, 0x5665, 0x8782, 0x5666, 0x8783, 0x5667,\n\t0x8784, 0x566D, 0x8785, 0x566E, 0x8786, 0x566F, 0x8787, 0x5670,\n\t0x8788, 0x5672, 0x8789, 0x5673, 0x878A, 0x5674, 0x878B, 0x5675,\n\t0x878C, 0x5677, 0x878D, 0x5678, 0x878E, 0x5679, 0x878F, 0x567A,\n\t0x8790, 0x567D, 0x8791, 0x567E, 0x8792, 0x567F, 0x8793, 0x5680,\n\t0x8794, 0x5681, 0x8795, 0x5682, 0x8796, 0x5683, 0x8797, 0x5684,\n\t0x8798, 0x5687, 0x8799, 0x5688, 0x879A, 0x5689, 0x879B, 0x568A,\n\t0x879C, 0x568B, 0x879D, 0x568C, 0x879E, 0x568D, 0x879F, 0x5690,\n\t0x87A0, 0x5691, 0x87A1, 0x5692, 0x87A2, 0x5694, 0x87A3, 0x5695,\n\t0x87A4, 0x5696, 0x87A5, 0x5697, 0x87A6, 0x5698, 0x87A7, 0x5699,\n\t0x87A8, 0x569A, 0x87A9, 0x569B, 0x87AA, 0x569C, 0x87AB, 0x569D,\n\t0x87AC, 0x569E, 0x87AD, 0x569F, 0x87AE, 0x56A0, 0x87AF, 0x56A1,\n\t0x87B0, 0x56A2, 0x87B1, 0x56A4, 0x87B2, 0x56A5, 0x87B3, 0x56A6,\n\t0x87B4, 0x56A7, 0x87B5, 0x56A8, 0x87B6, 0x56A9, 0x87B7, 0x56AA,\n\t0x87B8, 0x56AB, 0x87B9, 0x56AC, 0x87BA, 0x56AD, 0x87BB, 0x56AE,\n\t0x87BC, 0x56B0, 0x87BD, 0x56B1, 0x87BE, 0x56B2, 0x87BF, 0x56B3,\n\t0x87C0, 0x56B4, 0x87C1, 0x56B5, 0x87C2, 0x56B6, 0x87C3, 0x56B8,\n\t0x87C4, 0x56B9, 0x87C5, 0x56BA, 0x87C6, 0x56BB, 0x87C7, 0x56BD,\n\t0x87C8, 0x56BE, 0x87C9, 0x56BF, 0x87CA, 0x56C0, 0x87CB, 0x56C1,\n\t0x87CC, 0x56C2, 0x87CD, 0x56C3, 0x87CE, 0x56C4, 0x87CF, 0x56C5,\n\t0x87D0, 0x56C6, 0x87D1, 0x56C7, 0x87D2, 0x56C8, 0x87D3, 0x56C9,\n\t0x87D4, 0x56CB, 0x87D5, 0x56CC, 0x87D6, 0x56CD, 0x87D7, 0x56CE,\n\t0x87D8, 0x56CF, 0x87D9, 0x56D0, 0x87DA, 0x56D1, 0x87DB, 0x56D2,\n\t0x87DC, 0x56D3, 0x87DD, 0x56D5, 0x87DE, 0x56D6, 0x87DF, 0x56D8,\n\t0x87E0, 0x56D9, 0x87E1, 0x56DC, 0x87E2, 0x56E3, 0x87E3, 0x56E5,\n\t0x87E4, 0x56E6, 0x87E5, 0x56E7, 0x87E6, 0x56E8, 0x87E7, 0x56E9,\n\t0x87E8, 0x56EA, 0x87E9, 0x56EC, 0x87EA, 0x56EE, 0x87EB, 0x56EF,\n\t0x87EC, 0x56F2, 0x87ED, 0x56F3, 0x87EE, 0x56F6, 0x87EF, 0x56F7,\n\t0x87F0, 0x56F8, 0x87F1, 0x56FB, 0x87F2, 0x56FC, 0x87F3, 0x5700,\n\t0x87F4, 0x5701, 0x87F5, 0x5702, 0x87F6, 0x5705, 0x87F7, 0x5707,\n\t0x87F8, 0x570B, 0x87F9, 0x570C, 0x87FA, 0x570D, 0x87FB, 0x570E,\n\t0x87FC, 0x570F, 0x87FD, 0x5710, 0x87FE, 0x5711, 0x8840, 0x5712,\n\t0x8841, 0x5713, 0x8842, 0x5714, 0x8843, 0x5715, 0x8844, 0x5716,\n\t0x8845, 0x5717, 0x8846, 0x5718, 0x8847, 0x5719, 0x8848, 0x571A,\n\t0x8849, 0x571B, 0x884A, 0x571D, 0x884B, 0x571E, 0x884C, 0x5720,\n\t0x884D, 0x5721, 0x884E, 0x5722, 0x884F, 0x5724, 0x8850, 0x5725,\n\t0x8851, 0x5726, 0x8852, 0x5727, 0x8853, 0x572B, 0x8854, 0x5731,\n\t0x8855, 0x5732, 0x8856, 0x5734, 0x8857, 0x5735, 0x8858, 0x5736,\n\t0x8859, 0x5737, 0x885A, 0x5738, 0x885B, 0x573C, 0x885C, 0x573D,\n\t0x885D, 0x573F, 0x885E, 0x5741, 0x885F, 0x5743, 0x8860, 0x5744,\n\t0x8861, 0x5745, 0x8862, 0x5746, 0x8863, 0x5748, 0x8864, 0x5749,\n\t0x8865, 0x574B, 0x8866, 0x5752, 0x8867, 0x5753, 0x8868, 0x5754,\n\t0x8869, 0x5755, 0x886A, 0x5756, 0x886B, 0x5758, 0x886C, 0x5759,\n\t0x886D, 0x5762, 0x886E, 0x5763, 0x886F, 0x5765, 0x8870, 0x5767,\n\t0x8871, 0x576C, 0x8872, 0x576E, 0x8873, 0x5770, 0x8874, 0x5771,\n\t0x8875, 0x5772, 0x8876, 0x5774, 0x8877, 0x5775, 0x8878, 0x5778,\n\t0x8879, 0x5779, 0x887A, 0x577A, 0x887B, 0x577D, 0x887C, 0x577E,\n\t0x887D, 0x577F, 0x887E, 0x5780, 0x8880, 0x5781, 0x8881, 0x5787,\n\t0x8882, 0x5788, 0x8883, 0x5789, 0x8884, 0x578A, 0x8885, 0x578D,\n\t0x8886, 0x578E, 0x8887, 0x578F, 0x8888, 0x5790, 0x8889, 0x5791,\n\t0x888A, 0x5794, 0x888B, 0x5795, 0x888C, 0x5796, 0x888D, 0x5797,\n\t0x888E, 0x5798, 0x888F, 0x5799, 0x8890, 0x579A, 0x8891, 0x579C,\n\t0x8892, 0x579D, 0x8893, 0x579E, 0x8894, 0x579F, 0x8895, 0x57A5,\n\t0x8896, 0x57A8, 0x8897, 0x57AA, 0x8898, 0x57AC, 0x8899, 0x57AF,\n\t0x889A, 0x57B0, 0x889B, 0x57B1, 0x889C, 0x57B3, 0x889D, 0x57B5,\n\t0x889E, 0x57B6, 0x889F, 0x57B7, 0x88A0, 0x57B9, 0x88A1, 0x57BA,\n\t0x88A2, 0x57BB, 0x88A3, 0x57BC, 0x88A4, 0x57BD, 0x88A5, 0x57BE,\n\t0x88A6, 0x57BF, 0x88A7, 0x57C0, 0x88A8, 0x57C1, 0x88A9, 0x57C4,\n\t0x88AA, 0x57C5, 0x88AB, 0x57C6, 0x88AC, 0x57C7, 0x88AD, 0x57C8,\n\t0x88AE, 0x57C9, 0x88AF, 0x57CA, 0x88B0, 0x57CC, 0x88B1, 0x57CD,\n\t0x88B2, 0x57D0, 0x88B3, 0x57D1, 0x88B4, 0x57D3, 0x88B5, 0x57D6,\n\t0x88B6, 0x57D7, 0x88B7, 0x57DB, 0x88B8, 0x57DC, 0x88B9, 0x57DE,\n\t0x88BA, 0x57E1, 0x88BB, 0x57E2, 0x88BC, 0x57E3, 0x88BD, 0x57E5,\n\t0x88BE, 0x57E6, 0x88BF, 0x57E7, 0x88C0, 0x57E8, 0x88C1, 0x57E9,\n\t0x88C2, 0x57EA, 0x88C3, 0x57EB, 0x88C4, 0x57EC, 0x88C5, 0x57EE,\n\t0x88C6, 0x57F0, 0x88C7, 0x57F1, 0x88C8, 0x57F2, 0x88C9, 0x57F3,\n\t0x88CA, 0x57F5, 0x88CB, 0x57F6, 0x88CC, 0x57F7, 0x88CD, 0x57FB,\n\t0x88CE, 0x57FC, 0x88CF, 0x57FE, 0x88D0, 0x57FF, 0x88D1, 0x5801,\n\t0x88D2, 0x5803, 0x88D3, 0x5804, 0x88D4, 0x5805, 0x88D5, 0x5808,\n\t0x88D6, 0x5809, 0x88D7, 0x580A, 0x88D8, 0x580C, 0x88D9, 0x580E,\n\t0x88DA, 0x580F, 0x88DB, 0x5810, 0x88DC, 0x5812, 0x88DD, 0x5813,\n\t0x88DE, 0x5814, 0x88DF, 0x5816, 0x88E0, 0x5817, 0x88E1, 0x5818,\n\t0x88E2, 0x581A, 0x88E3, 0x581B, 0x88E4, 0x581C, 0x88E5, 0x581D,\n\t0x88E6, 0x581F, 0x88E7, 0x5822, 0x88E8, 0x5823, 0x88E9, 0x5825,\n\t0x88EA, 0x5826, 0x88EB, 0x5827, 0x88EC, 0x5828, 0x88ED, 0x5829,\n\t0x88EE, 0x582B, 0x88EF, 0x582C, 0x88F0, 0x582D, 0x88F1, 0x582E,\n\t0x88F2, 0x582F, 0x88F3, 0x5831, 0x88F4, 0x5832, 0x88F5, 0x5833,\n\t0x88F6, 0x5834, 0x88F7, 0x5836, 0x88F8, 0x5837, 0x88F9, 0x5838,\n\t0x88FA, 0x5839, 0x88FB, 0x583A, 0x88FC, 0x583B, 0x88FD, 0x583C,\n\t0x88FE, 0x583D, 0x8940, 0x583E, 0x8941, 0x583F, 0x8942, 0x5840,\n\t0x8943, 0x5841, 0x8944, 0x5842, 0x8945, 0x5843, 0x8946, 0x5845,\n\t0x8947, 0x5846, 0x8948, 0x5847, 0x8949, 0x5848, 0x894A, 0x5849,\n\t0x894B, 0x584A, 0x894C, 0x584B, 0x894D, 0x584E, 0x894E, 0x584F,\n\t0x894F, 0x5850, 0x8950, 0x5852, 0x8951, 0x5853, 0x8952, 0x5855,\n\t0x8953, 0x5856, 0x8954, 0x5857, 0x8955, 0x5859, 0x8956, 0x585A,\n\t0x8957, 0x585B, 0x8958, 0x585C, 0x8959, 0x585D, 0x895A, 0x585F,\n\t0x895B, 0x5860, 0x895C, 0x5861, 0x895D, 0x5862, 0x895E, 0x5863,\n\t0x895F, 0x5864, 0x8960, 0x5866, 0x8961, 0x5867, 0x8962, 0x5868,\n\t0x8963, 0x5869, 0x8964, 0x586A, 0x8965, 0x586D, 0x8966, 0x586E,\n\t0x8967, 0x586F, 0x8968, 0x5870, 0x8969, 0x5871, 0x896A, 0x5872,\n\t0x896B, 0x5873, 0x896C, 0x5874, 0x896D, 0x5875, 0x896E, 0x5876,\n\t0x896F, 0x5877, 0x8970, 0x5878, 0x8971, 0x5879, 0x8972, 0x587A,\n\t0x8973, 0x587B, 0x8974, 0x587C, 0x8975, 0x587D, 0x8976, 0x587F,\n\t0x8977, 0x5882, 0x8978, 0x5884, 0x8979, 0x5886, 0x897A, 0x5887,\n\t0x897B, 0x5888, 0x897C, 0x588A, 0x897D, 0x588B, 0x897E, 0x588C,\n\t0x8980, 0x588D, 0x8981, 0x588E, 0x8982, 0x588F, 0x8983, 0x5890,\n\t0x8984, 0x5891, 0x8985, 0x5894, 0x8986, 0x5895, 0x8987, 0x5896,\n\t0x8988, 0x5897, 0x8989, 0x5898, 0x898A, 0x589B, 0x898B, 0x589C,\n\t0x898C, 0x589D, 0x898D, 0x58A0, 0x898E, 0x58A1, 0x898F, 0x58A2,\n\t0x8990, 0x58A3, 0x8991, 0x58A4, 0x8992, 0x58A5, 0x8993, 0x58A6,\n\t0x8994, 0x58A7, 0x8995, 0x58AA, 0x8996, 0x58AB, 0x8997, 0x58AC,\n\t0x8998, 0x58AD, 0x8999, 0x58AE, 0x899A, 0x58AF, 0x899B, 0x58B0,\n\t0x899C, 0x58B1, 0x899D, 0x58B2, 0x899E, 0x58B3, 0x899F, 0x58B4,\n\t0x89A0, 0x58B5, 0x89A1, 0x58B6, 0x89A2, 0x58B7, 0x89A3, 0x58B8,\n\t0x89A4, 0x58B9, 0x89A5, 0x58BA, 0x89A6, 0x58BB, 0x89A7, 0x58BD,\n\t0x89A8, 0x58BE, 0x89A9, 0x58BF, 0x89AA, 0x58C0, 0x89AB, 0x58C2,\n\t0x89AC, 0x58C3, 0x89AD, 0x58C4, 0x89AE, 0x58C6, 0x89AF, 0x58C7,\n\t0x89B0, 0x58C8, 0x89B1, 0x58C9, 0x89B2, 0x58CA, 0x89B3, 0x58CB,\n\t0x89B4, 0x58CC, 0x89B5, 0x58CD, 0x89B6, 0x58CE, 0x89B7, 0x58CF,\n\t0x89B8, 0x58D0, 0x89B9, 0x58D2, 0x89BA, 0x58D3, 0x89BB, 0x58D4,\n\t0x89BC, 0x58D6, 0x89BD, 0x58D7, 0x89BE, 0x58D8, 0x89BF, 0x58D9,\n\t0x89C0, 0x58DA, 0x89C1, 0x58DB, 0x89C2, 0x58DC, 0x89C3, 0x58DD,\n\t0x89C4, 0x58DE, 0x89C5, 0x58DF, 0x89C6, 0x58E0, 0x89C7, 0x58E1,\n\t0x89C8, 0x58E2, 0x89C9, 0x58E3, 0x89CA, 0x58E5, 0x89CB, 0x58E6,\n\t0x89CC, 0x58E7, 0x89CD, 0x58E8, 0x89CE, 0x58E9, 0x89CF, 0x58EA,\n\t0x89D0, 0x58ED, 0x89D1, 0x58EF, 0x89D2, 0x58F1, 0x89D3, 0x58F2,\n\t0x89D4, 0x58F4, 0x89D5, 0x58F5, 0x89D6, 0x58F7, 0x89D7, 0x58F8,\n\t0x89D8, 0x58FA, 0x89D9, 0x58FB, 0x89DA, 0x58FC, 0x89DB, 0x58FD,\n\t0x89DC, 0x58FE, 0x89DD, 0x58FF, 0x89DE, 0x5900, 0x89DF, 0x5901,\n\t0x89E0, 0x5903, 0x89E1, 0x5905, 0x89E2, 0x5906, 0x89E3, 0x5908,\n\t0x89E4, 0x5909, 0x89E5, 0x590A, 0x89E6, 0x590B, 0x89E7, 0x590C,\n\t0x89E8, 0x590E, 0x89E9, 0x5910, 0x89EA, 0x5911, 0x89EB, 0x5912,\n\t0x89EC, 0x5913, 0x89ED, 0x5917, 0x89EE, 0x5918, 0x89EF, 0x591B,\n\t0x89F0, 0x591D, 0x89F1, 0x591E, 0x89F2, 0x5920, 0x89F3, 0x5921,\n\t0x89F4, 0x5922, 0x89F5, 0x5923, 0x89F6, 0x5926, 0x89F7, 0x5928,\n\t0x89F8, 0x592C, 0x89F9, 0x5930, 0x89FA, 0x5932, 0x89FB, 0x5933,\n\t0x89FC, 0x5935, 0x89FD, 0x5936, 0x89FE, 0x593B, 0x8A40, 0x593D,\n\t0x8A41, 0x593E, 0x8A42, 0x593F, 0x8A43, 0x5940, 0x8A44, 0x5943,\n\t0x8A45, 0x5945, 0x8A46, 0x5946, 0x8A47, 0x594A, 0x8A48, 0x594C,\n\t0x8A49, 0x594D, 0x8A4A, 0x5950, 0x8A4B, 0x5952, 0x8A4C, 0x5953,\n\t0x8A4D, 0x5959, 0x8A4E, 0x595B, 0x8A4F, 0x595C, 0x8A50, 0x595D,\n\t0x8A51, 0x595E, 0x8A52, 0x595F, 0x8A53, 0x5961, 0x8A54, 0x5963,\n\t0x8A55, 0x5964, 0x8A56, 0x5966, 0x8A57, 0x5967, 0x8A58, 0x5968,\n\t0x8A59, 0x5969, 0x8A5A, 0x596A, 0x8A5B, 0x596B, 0x8A5C, 0x596C,\n\t0x8A5D, 0x596D, 0x8A5E, 0x596E, 0x8A5F, 0x596F, 0x8A60, 0x5970,\n\t0x8A61, 0x5971, 0x8A62, 0x5972, 0x8A63, 0x5975, 0x8A64, 0x5977,\n\t0x8A65, 0x597A, 0x8A66, 0x597B, 0x8A67, 0x597C, 0x8A68, 0x597E,\n\t0x8A69, 0x597F, 0x8A6A, 0x5980, 0x8A6B, 0x5985, 0x8A6C, 0x5989,\n\t0x8A6D, 0x598B, 0x8A6E, 0x598C, 0x8A6F, 0x598E, 0x8A70, 0x598F,\n\t0x8A71, 0x5990, 0x8A72, 0x5991, 0x8A73, 0x5994, 0x8A74, 0x5995,\n\t0x8A75, 0x5998, 0x8A76, 0x599A, 0x8A77, 0x599B, 0x8A78, 0x599C,\n\t0x8A79, 0x599D, 0x8A7A, 0x599F, 0x8A7B, 0x59A0, 0x8A7C, 0x59A1,\n\t0x8A7D, 0x59A2, 0x8A7E, 0x59A6, 0x8A80, 0x59A7, 0x8A81, 0x59AC,\n\t0x8A82, 0x59AD, 0x8A83, 0x59B0, 0x8A84, 0x59B1, 0x8A85, 0x59B3,\n\t0x8A86, 0x59B4, 0x8A87, 0x59B5, 0x8A88, 0x59B6, 0x8A89, 0x59B7,\n\t0x8A8A, 0x59B8, 0x8A8B, 0x59BA, 0x8A8C, 0x59BC, 0x8A8D, 0x59BD,\n\t0x8A8E, 0x59BF, 0x8A8F, 0x59C0, 0x8A90, 0x59C1, 0x8A91, 0x59C2,\n\t0x8A92, 0x59C3, 0x8A93, 0x59C4, 0x8A94, 0x59C5, 0x8A95, 0x59C7,\n\t0x8A96, 0x59C8, 0x8A97, 0x59C9, 0x8A98, 0x59CC, 0x8A99, 0x59CD,\n\t0x8A9A, 0x59CE, 0x8A9B, 0x59CF, 0x8A9C, 0x59D5, 0x8A9D, 0x59D6,\n\t0x8A9E, 0x59D9, 0x8A9F, 0x59DB, 0x8AA0, 0x59DE, 0x8AA1, 0x59DF,\n\t0x8AA2, 0x59E0, 0x8AA3, 0x59E1, 0x8AA4, 0x59E2, 0x8AA5, 0x59E4,\n\t0x8AA6, 0x59E6, 0x8AA7, 0x59E7, 0x8AA8, 0x59E9, 0x8AA9, 0x59EA,\n\t0x8AAA, 0x59EB, 0x8AAB, 0x59ED, 0x8AAC, 0x59EE, 0x8AAD, 0x59EF,\n\t0x8AAE, 0x59F0, 0x8AAF, 0x59F1, 0x8AB0, 0x59F2, 0x8AB1, 0x59F3,\n\t0x8AB2, 0x59F4, 0x8AB3, 0x59F5, 0x8AB4, 0x59F6, 0x8AB5, 0x59F7,\n\t0x8AB6, 0x59F8, 0x8AB7, 0x59FA, 0x8AB8, 0x59FC, 0x8AB9, 0x59FD,\n\t0x8ABA, 0x59FE, 0x8ABB, 0x5A00, 0x8ABC, 0x5A02, 0x8ABD, 0x5A0A,\n\t0x8ABE, 0x5A0B, 0x8ABF, 0x5A0D, 0x8AC0, 0x5A0E, 0x8AC1, 0x5A0F,\n\t0x8AC2, 0x5A10, 0x8AC3, 0x5A12, 0x8AC4, 0x5A14, 0x8AC5, 0x5A15,\n\t0x8AC6, 0x5A16, 0x8AC7, 0x5A17, 0x8AC8, 0x5A19, 0x8AC9, 0x5A1A,\n\t0x8ACA, 0x5A1B, 0x8ACB, 0x5A1D, 0x8ACC, 0x5A1E, 0x8ACD, 0x5A21,\n\t0x8ACE, 0x5A22, 0x8ACF, 0x5A24, 0x8AD0, 0x5A26, 0x8AD1, 0x5A27,\n\t0x8AD2, 0x5A28, 0x8AD3, 0x5A2A, 0x8AD4, 0x5A2B, 0x8AD5, 0x5A2C,\n\t0x8AD6, 0x5A2D, 0x8AD7, 0x5A2E, 0x8AD8, 0x5A2F, 0x8AD9, 0x5A30,\n\t0x8ADA, 0x5A33, 0x8ADB, 0x5A35, 0x8ADC, 0x5A37, 0x8ADD, 0x5A38,\n\t0x8ADE, 0x5A39, 0x8ADF, 0x5A3A, 0x8AE0, 0x5A3B, 0x8AE1, 0x5A3D,\n\t0x8AE2, 0x5A3E, 0x8AE3, 0x5A3F, 0x8AE4, 0x5A41, 0x8AE5, 0x5A42,\n\t0x8AE6, 0x5A43, 0x8AE7, 0x5A44, 0x8AE8, 0x5A45, 0x8AE9, 0x5A47,\n\t0x8AEA, 0x5A48, 0x8AEB, 0x5A4B, 0x8AEC, 0x5A4C, 0x8AED, 0x5A4D,\n\t0x8AEE, 0x5A4E, 0x8AEF, 0x5A4F, 0x8AF0, 0x5A50, 0x8AF1, 0x5A51,\n\t0x8AF2, 0x5A52, 0x8AF3, 0x5A53, 0x8AF4, 0x5A54, 0x8AF5, 0x5A56,\n\t0x8AF6, 0x5A57, 0x8AF7, 0x5A58, 0x8AF8, 0x5A59, 0x8AF9, 0x5A5B,\n\t0x8AFA, 0x5A5C, 0x8AFB, 0x5A5D, 0x8AFC, 0x5A5E, 0x8AFD, 0x5A5F,\n\t0x8AFE, 0x5A60, 0x8B40, 0x5A61, 0x8B41, 0x5A63, 0x8B42, 0x5A64,\n\t0x8B43, 0x5A65, 0x8B44, 0x5A66, 0x8B45, 0x5A68, 0x8B46, 0x5A69,\n\t0x8B47, 0x5A6B, 0x8B48, 0x5A6C, 0x8B49, 0x5A6D, 0x8B4A, 0x5A6E,\n\t0x8B4B, 0x5A6F, 0x8B4C, 0x5A70, 0x8B4D, 0x5A71, 0x8B4E, 0x5A72,\n\t0x8B4F, 0x5A73, 0x8B50, 0x5A78, 0x8B51, 0x5A79, 0x8B52, 0x5A7B,\n\t0x8B53, 0x5A7C, 0x8B54, 0x5A7D, 0x8B55, 0x5A7E, 0x8B56, 0x5A80,\n\t0x8B57, 0x5A81, 0x8B58, 0x5A82, 0x8B59, 0x5A83, 0x8B5A, 0x5A84,\n\t0x8B5B, 0x5A85, 0x8B5C, 0x5A86, 0x8B5D, 0x5A87, 0x8B5E, 0x5A88,\n\t0x8B5F, 0x5A89, 0x8B60, 0x5A8A, 0x8B61, 0x5A8B, 0x8B62, 0x5A8C,\n\t0x8B63, 0x5A8D, 0x8B64, 0x5A8E, 0x8B65, 0x5A8F, 0x8B66, 0x5A90,\n\t0x8B67, 0x5A91, 0x8B68, 0x5A93, 0x8B69, 0x5A94, 0x8B6A, 0x5A95,\n\t0x8B6B, 0x5A96, 0x8B6C, 0x5A97, 0x8B6D, 0x5A98, 0x8B6E, 0x5A99,\n\t0x8B6F, 0x5A9C, 0x8B70, 0x5A9D, 0x8B71, 0x5A9E, 0x8B72, 0x5A9F,\n\t0x8B73, 0x5AA0, 0x8B74, 0x5AA1, 0x8B75, 0x5AA2, 0x8B76, 0x5AA3,\n\t0x8B77, 0x5AA4, 0x8B78, 0x5AA5, 0x8B79, 0x5AA6, 0x8B7A, 0x5AA7,\n\t0x8B7B, 0x5AA8, 0x8B7C, 0x5AA9, 0x8B7D, 0x5AAB, 0x8B7E, 0x5AAC,\n\t0x8B80, 0x5AAD, 0x8B81, 0x5AAE, 0x8B82, 0x5AAF, 0x8B83, 0x5AB0,\n\t0x8B84, 0x5AB1, 0x8B85, 0x5AB4, 0x8B86, 0x5AB6, 0x8B87, 0x5AB7,\n\t0x8B88, 0x5AB9, 0x8B89, 0x5ABA, 0x8B8A, 0x5ABB, 0x8B8B, 0x5ABC,\n\t0x8B8C, 0x5ABD, 0x8B8D, 0x5ABF, 0x8B8E, 0x5AC0, 0x8B8F, 0x5AC3,\n\t0x8B90, 0x5AC4, 0x8B91, 0x5AC5, 0x8B92, 0x5AC6, 0x8B93, 0x5AC7,\n\t0x8B94, 0x5AC8, 0x8B95, 0x5ACA, 0x8B96, 0x5ACB, 0x8B97, 0x5ACD,\n\t0x8B98, 0x5ACE, 0x8B99, 0x5ACF, 0x8B9A, 0x5AD0, 0x8B9B, 0x5AD1,\n\t0x8B9C, 0x5AD3, 0x8B9D, 0x5AD5, 0x8B9E, 0x5AD7, 0x8B9F, 0x5AD9,\n\t0x8BA0, 0x5ADA, 0x8BA1, 0x5ADB, 0x8BA2, 0x5ADD, 0x8BA3, 0x5ADE,\n\t0x8BA4, 0x5ADF, 0x8BA5, 0x5AE2, 0x8BA6, 0x5AE4, 0x8BA7, 0x5AE5,\n\t0x8BA8, 0x5AE7, 0x8BA9, 0x5AE8, 0x8BAA, 0x5AEA, 0x8BAB, 0x5AEC,\n\t0x8BAC, 0x5AED, 0x8BAD, 0x5AEE, 0x8BAE, 0x5AEF, 0x8BAF, 0x5AF0,\n\t0x8BB0, 0x5AF2, 0x8BB1, 0x5AF3, 0x8BB2, 0x5AF4, 0x8BB3, 0x5AF5,\n\t0x8BB4, 0x5AF6, 0x8BB5, 0x5AF7, 0x8BB6, 0x5AF8, 0x8BB7, 0x5AF9,\n\t0x8BB8, 0x5AFA, 0x8BB9, 0x5AFB, 0x8BBA, 0x5AFC, 0x8BBB, 0x5AFD,\n\t0x8BBC, 0x5AFE, 0x8BBD, 0x5AFF, 0x8BBE, 0x5B00, 0x8BBF, 0x5B01,\n\t0x8BC0, 0x5B02, 0x8BC1, 0x5B03, 0x8BC2, 0x5B04, 0x8BC3, 0x5B05,\n\t0x8BC4, 0x5B06, 0x8BC5, 0x5B07, 0x8BC6, 0x5B08, 0x8BC7, 0x5B0A,\n\t0x8BC8, 0x5B0B, 0x8BC9, 0x5B0C, 0x8BCA, 0x5B0D, 0x8BCB, 0x5B0E,\n\t0x8BCC, 0x5B0F, 0x8BCD, 0x5B10, 0x8BCE, 0x5B11, 0x8BCF, 0x5B12,\n\t0x8BD0, 0x5B13, 0x8BD1, 0x5B14, 0x8BD2, 0x5B15, 0x8BD3, 0x5B18,\n\t0x8BD4, 0x5B19, 0x8BD5, 0x5B1A, 0x8BD6, 0x5B1B, 0x8BD7, 0x5B1C,\n\t0x8BD8, 0x5B1D, 0x8BD9, 0x5B1E, 0x8BDA, 0x5B1F, 0x8BDB, 0x5B20,\n\t0x8BDC, 0x5B21, 0x8BDD, 0x5B22, 0x8BDE, 0x5B23, 0x8BDF, 0x5B24,\n\t0x8BE0, 0x5B25, 0x8BE1, 0x5B26, 0x8BE2, 0x5B27, 0x8BE3, 0x5B28,\n\t0x8BE4, 0x5B29, 0x8BE5, 0x5B2A, 0x8BE6, 0x5B2B, 0x8BE7, 0x5B2C,\n\t0x8BE8, 0x5B2D, 0x8BE9, 0x5B2E, 0x8BEA, 0x5B2F, 0x8BEB, 0x5B30,\n\t0x8BEC, 0x5B31, 0x8BED, 0x5B33, 0x8BEE, 0x5B35, 0x8BEF, 0x5B36,\n\t0x8BF0, 0x5B38, 0x8BF1, 0x5B39, 0x8BF2, 0x5B3A, 0x8BF3, 0x5B3B,\n\t0x8BF4, 0x5B3C, 0x8BF5, 0x5B3D, 0x8BF6, 0x5B3E, 0x8BF7, 0x5B3F,\n\t0x8BF8, 0x5B41, 0x8BF9, 0x5B42, 0x8BFA, 0x5B43, 0x8BFB, 0x5B44,\n\t0x8BFC, 0x5B45, 0x8BFD, 0x5B46, 0x8BFE, 0x5B47, 0x8C40, 0x5B48,\n\t0x8C41, 0x5B49, 0x8C42, 0x5B4A, 0x8C43, 0x5B4B, 0x8C44, 0x5B4C,\n\t0x8C45, 0x5B4D, 0x8C46, 0x5B4E, 0x8C47, 0x5B4F, 0x8C48, 0x5B52,\n\t0x8C49, 0x5B56, 0x8C4A, 0x5B5E, 0x8C4B, 0x5B60, 0x8C4C, 0x5B61,\n\t0x8C4D, 0x5B67, 0x8C4E, 0x5B68, 0x8C4F, 0x5B6B, 0x8C50, 0x5B6D,\n\t0x8C51, 0x5B6E, 0x8C52, 0x5B6F, 0x8C53, 0x5B72, 0x8C54, 0x5B74,\n\t0x8C55, 0x5B76, 0x8C56, 0x5B77, 0x8C57, 0x5B78, 0x8C58, 0x5B79,\n\t0x8C59, 0x5B7B, 0x8C5A, 0x5B7C, 0x8C5B, 0x5B7E, 0x8C5C, 0x5B7F,\n\t0x8C5D, 0x5B82, 0x8C5E, 0x5B86, 0x8C5F, 0x5B8A, 0x8C60, 0x5B8D,\n\t0x8C61, 0x5B8E, 0x8C62, 0x5B90, 0x8C63, 0x5B91, 0x8C64, 0x5B92,\n\t0x8C65, 0x5B94, 0x8C66, 0x5B96, 0x8C67, 0x5B9F, 0x8C68, 0x5BA7,\n\t0x8C69, 0x5BA8, 0x8C6A, 0x5BA9, 0x8C6B, 0x5BAC, 0x8C6C, 0x5BAD,\n\t0x8C6D, 0x5BAE, 0x8C6E, 0x5BAF, 0x8C6F, 0x5BB1, 0x8C70, 0x5BB2,\n\t0x8C71, 0x5BB7, 0x8C72, 0x5BBA, 0x8C73, 0x5BBB, 0x8C74, 0x5BBC,\n\t0x8C75, 0x5BC0, 0x8C76, 0x5BC1, 0x8C77, 0x5BC3, 0x8C78, 0x5BC8,\n\t0x8C79, 0x5BC9, 0x8C7A, 0x5BCA, 0x8C7B, 0x5BCB, 0x8C7C, 0x5BCD,\n\t0x8C7D, 0x5BCE, 0x8C7E, 0x5BCF, 0x8C80, 0x5BD1, 0x8C81, 0x5BD4,\n\t0x8C82, 0x5BD5, 0x8C83, 0x5BD6, 0x8C84, 0x5BD7, 0x8C85, 0x5BD8,\n\t0x8C86, 0x5BD9, 0x8C87, 0x5BDA, 0x8C88, 0x5BDB, 0x8C89, 0x5BDC,\n\t0x8C8A, 0x5BE0, 0x8C8B, 0x5BE2, 0x8C8C, 0x5BE3, 0x8C8D, 0x5BE6,\n\t0x8C8E, 0x5BE7, 0x8C8F, 0x5BE9, 0x8C90, 0x5BEA, 0x8C91, 0x5BEB,\n\t0x8C92, 0x5BEC, 0x8C93, 0x5BED, 0x8C94, 0x5BEF, 0x8C95, 0x5BF1,\n\t0x8C96, 0x5BF2, 0x8C97, 0x5BF3, 0x8C98, 0x5BF4, 0x8C99, 0x5BF5,\n\t0x8C9A, 0x5BF6, 0x8C9B, 0x5BF7, 0x8C9C, 0x5BFD, 0x8C9D, 0x5BFE,\n\t0x8C9E, 0x5C00, 0x8C9F, 0x5C02, 0x8CA0, 0x5C03, 0x8CA1, 0x5C05,\n\t0x8CA2, 0x5C07, 0x8CA3, 0x5C08, 0x8CA4, 0x5C0B, 0x8CA5, 0x5C0C,\n\t0x8CA6, 0x5C0D, 0x8CA7, 0x5C0E, 0x8CA8, 0x5C10, 0x8CA9, 0x5C12,\n\t0x8CAA, 0x5C13, 0x8CAB, 0x5C17, 0x8CAC, 0x5C19, 0x8CAD, 0x5C1B,\n\t0x8CAE, 0x5C1E, 0x8CAF, 0x5C1F, 0x8CB0, 0x5C20, 0x8CB1, 0x5C21,\n\t0x8CB2, 0x5C23, 0x8CB3, 0x5C26, 0x8CB4, 0x5C28, 0x8CB5, 0x5C29,\n\t0x8CB6, 0x5C2A, 0x8CB7, 0x5C2B, 0x8CB8, 0x5C2D, 0x8CB9, 0x5C2E,\n\t0x8CBA, 0x5C2F, 0x8CBB, 0x5C30, 0x8CBC, 0x5C32, 0x8CBD, 0x5C33,\n\t0x8CBE, 0x5C35, 0x8CBF, 0x5C36, 0x8CC0, 0x5C37, 0x8CC1, 0x5C43,\n\t0x8CC2, 0x5C44, 0x8CC3, 0x5C46, 0x8CC4, 0x5C47, 0x8CC5, 0x5C4C,\n\t0x8CC6, 0x5C4D, 0x8CC7, 0x5C52, 0x8CC8, 0x5C53, 0x8CC9, 0x5C54,\n\t0x8CCA, 0x5C56, 0x8CCB, 0x5C57, 0x8CCC, 0x5C58, 0x8CCD, 0x5C5A,\n\t0x8CCE, 0x5C5B, 0x8CCF, 0x5C5C, 0x8CD0, 0x5C5D, 0x8CD1, 0x5C5F,\n\t0x8CD2, 0x5C62, 0x8CD3, 0x5C64, 0x8CD4, 0x5C67, 0x8CD5, 0x5C68,\n\t0x8CD6, 0x5C69, 0x8CD7, 0x5C6A, 0x8CD8, 0x5C6B, 0x8CD9, 0x5C6C,\n\t0x8CDA, 0x5C6D, 0x8CDB, 0x5C70, 0x8CDC, 0x5C72, 0x8CDD, 0x5C73,\n\t0x8CDE, 0x5C74, 0x8CDF, 0x5C75, 0x8CE0, 0x5C76, 0x8CE1, 0x5C77,\n\t0x8CE2, 0x5C78, 0x8CE3, 0x5C7B, 0x8CE4, 0x5C7C, 0x8CE5, 0x5C7D,\n\t0x8CE6, 0x5C7E, 0x8CE7, 0x5C80, 0x8CE8, 0x5C83, 0x8CE9, 0x5C84,\n\t0x8CEA, 0x5C85, 0x8CEB, 0x5C86, 0x8CEC, 0x5C87, 0x8CED, 0x5C89,\n\t0x8CEE, 0x5C8A, 0x8CEF, 0x5C8B, 0x8CF0, 0x5C8E, 0x8CF1, 0x5C8F,\n\t0x8CF2, 0x5C92, 0x8CF3, 0x5C93, 0x8CF4, 0x5C95, 0x8CF5, 0x5C9D,\n\t0x8CF6, 0x5C9E, 0x8CF7, 0x5C9F, 0x8CF8, 0x5CA0, 0x8CF9, 0x5CA1,\n\t0x8CFA, 0x5CA4, 0x8CFB, 0x5CA5, 0x8CFC, 0x5CA6, 0x8CFD, 0x5CA7,\n\t0x8CFE, 0x5CA8, 0x8D40, 0x5CAA, 0x8D41, 0x5CAE, 0x8D42, 0x5CAF,\n\t0x8D43, 0x5CB0, 0x8D44, 0x5CB2, 0x8D45, 0x5CB4, 0x8D46, 0x5CB6,\n\t0x8D47, 0x5CB9, 0x8D48, 0x5CBA, 0x8D49, 0x5CBB, 0x8D4A, 0x5CBC,\n\t0x8D4B, 0x5CBE, 0x8D4C, 0x5CC0, 0x8D4D, 0x5CC2, 0x8D4E, 0x5CC3,\n\t0x8D4F, 0x5CC5, 0x8D50, 0x5CC6, 0x8D51, 0x5CC7, 0x8D52, 0x5CC8,\n\t0x8D53, 0x5CC9, 0x8D54, 0x5CCA, 0x8D55, 0x5CCC, 0x8D56, 0x5CCD,\n\t0x8D57, 0x5CCE, 0x8D58, 0x5CCF, 0x8D59, 0x5CD0, 0x8D5A, 0x5CD1,\n\t0x8D5B, 0x5CD3, 0x8D5C, 0x5CD4, 0x8D5D, 0x5CD5, 0x8D5E, 0x5CD6,\n\t0x8D5F, 0x5CD7, 0x8D60, 0x5CD8, 0x8D61, 0x5CDA, 0x8D62, 0x5CDB,\n\t0x8D63, 0x5CDC, 0x8D64, 0x5CDD, 0x8D65, 0x5CDE, 0x8D66, 0x5CDF,\n\t0x8D67, 0x5CE0, 0x8D68, 0x5CE2, 0x8D69, 0x5CE3, 0x8D6A, 0x5CE7,\n\t0x8D6B, 0x5CE9, 0x8D6C, 0x5CEB, 0x8D6D, 0x5CEC, 0x8D6E, 0x5CEE,\n\t0x8D6F, 0x5CEF, 0x8D70, 0x5CF1, 0x8D71, 0x5CF2, 0x8D72, 0x5CF3,\n\t0x8D73, 0x5CF4, 0x8D74, 0x5CF5, 0x8D75, 0x5CF6, 0x8D76, 0x5CF7,\n\t0x8D77, 0x5CF8, 0x8D78, 0x5CF9, 0x8D79, 0x5CFA, 0x8D7A, 0x5CFC,\n\t0x8D7B, 0x5CFD, 0x8D7C, 0x5CFE, 0x8D7D, 0x5CFF, 0x8D7E, 0x5D00,\n\t0x8D80, 0x5D01, 0x8D81, 0x5D04, 0x8D82, 0x5D05, 0x8D83, 0x5D08,\n\t0x8D84, 0x5D09, 0x8D85, 0x5D0A, 0x8D86, 0x5D0B, 0x8D87, 0x5D0C,\n\t0x8D88, 0x5D0D, 0x8D89, 0x5D0F, 0x8D8A, 0x5D10, 0x8D8B, 0x5D11,\n\t0x8D8C, 0x5D12, 0x8D8D, 0x5D13, 0x8D8E, 0x5D15, 0x8D8F, 0x5D17,\n\t0x8D90, 0x5D18, 0x8D91, 0x5D19, 0x8D92, 0x5D1A, 0x8D93, 0x5D1C,\n\t0x8D94, 0x5D1D, 0x8D95, 0x5D1F, 0x8D96, 0x5D20, 0x8D97, 0x5D21,\n\t0x8D98, 0x5D22, 0x8D99, 0x5D23, 0x8D9A, 0x5D25, 0x8D9B, 0x5D28,\n\t0x8D9C, 0x5D2A, 0x8D9D, 0x5D2B, 0x8D9E, 0x5D2C, 0x8D9F, 0x5D2F,\n\t0x8DA0, 0x5D30, 0x8DA1, 0x5D31, 0x8DA2, 0x5D32, 0x8DA3, 0x5D33,\n\t0x8DA4, 0x5D35, 0x8DA5, 0x5D36, 0x8DA6, 0x5D37, 0x8DA7, 0x5D38,\n\t0x8DA8, 0x5D39, 0x8DA9, 0x5D3A, 0x8DAA, 0x5D3B, 0x8DAB, 0x5D3C,\n\t0x8DAC, 0x5D3F, 0x8DAD, 0x5D40, 0x8DAE, 0x5D41, 0x8DAF, 0x5D42,\n\t0x8DB0, 0x5D43, 0x8DB1, 0x5D44, 0x8DB2, 0x5D45, 0x8DB3, 0x5D46,\n\t0x8DB4, 0x5D48, 0x8DB5, 0x5D49, 0x8DB6, 0x5D4D, 0x8DB7, 0x5D4E,\n\t0x8DB8, 0x5D4F, 0x8DB9, 0x5D50, 0x8DBA, 0x5D51, 0x8DBB, 0x5D52,\n\t0x8DBC, 0x5D53, 0x8DBD, 0x5D54, 0x8DBE, 0x5D55, 0x8DBF, 0x5D56,\n\t0x8DC0, 0x5D57, 0x8DC1, 0x5D59, 0x8DC2, 0x5D5A, 0x8DC3, 0x5D5C,\n\t0x8DC4, 0x5D5E, 0x8DC5, 0x5D5F, 0x8DC6, 0x5D60, 0x8DC7, 0x5D61,\n\t0x8DC8, 0x5D62, 0x8DC9, 0x5D63, 0x8DCA, 0x5D64, 0x8DCB, 0x5D65,\n\t0x8DCC, 0x5D66, 0x8DCD, 0x5D67, 0x8DCE, 0x5D68, 0x8DCF, 0x5D6A,\n\t0x8DD0, 0x5D6D, 0x8DD1, 0x5D6E, 0x8DD2, 0x5D70, 0x8DD3, 0x5D71,\n\t0x8DD4, 0x5D72, 0x8DD5, 0x5D73, 0x8DD6, 0x5D75, 0x8DD7, 0x5D76,\n\t0x8DD8, 0x5D77, 0x8DD9, 0x5D78, 0x8DDA, 0x5D79, 0x8DDB, 0x5D7A,\n\t0x8DDC, 0x5D7B, 0x8DDD, 0x5D7C, 0x8DDE, 0x5D7D, 0x8DDF, 0x5D7E,\n\t0x8DE0, 0x5D7F, 0x8DE1, 0x5D80, 0x8DE2, 0x5D81, 0x8DE3, 0x5D83,\n\t0x8DE4, 0x5D84, 0x8DE5, 0x5D85, 0x8DE6, 0x5D86, 0x8DE7, 0x5D87,\n\t0x8DE8, 0x5D88, 0x8DE9, 0x5D89, 0x8DEA, 0x5D8A, 0x8DEB, 0x5D8B,\n\t0x8DEC, 0x5D8C, 0x8DED, 0x5D8D, 0x8DEE, 0x5D8E, 0x8DEF, 0x5D8F,\n\t0x8DF0, 0x5D90, 0x8DF1, 0x5D91, 0x8DF2, 0x5D92, 0x8DF3, 0x5D93,\n\t0x8DF4, 0x5D94, 0x8DF5, 0x5D95, 0x8DF6, 0x5D96, 0x8DF7, 0x5D97,\n\t0x8DF8, 0x5D98, 0x8DF9, 0x5D9A, 0x8DFA, 0x5D9B, 0x8DFB, 0x5D9C,\n\t0x8DFC, 0x5D9E, 0x8DFD, 0x5D9F, 0x8DFE, 0x5DA0, 0x8E40, 0x5DA1,\n\t0x8E41, 0x5DA2, 0x8E42, 0x5DA3, 0x8E43, 0x5DA4, 0x8E44, 0x5DA5,\n\t0x8E45, 0x5DA6, 0x8E46, 0x5DA7, 0x8E47, 0x5DA8, 0x8E48, 0x5DA9,\n\t0x8E49, 0x5DAA, 0x8E4A, 0x5DAB, 0x8E4B, 0x5DAC, 0x8E4C, 0x5DAD,\n\t0x8E4D, 0x5DAE, 0x8E4E, 0x5DAF, 0x8E4F, 0x5DB0, 0x8E50, 0x5DB1,\n\t0x8E51, 0x5DB2, 0x8E52, 0x5DB3, 0x8E53, 0x5DB4, 0x8E54, 0x5DB5,\n\t0x8E55, 0x5DB6, 0x8E56, 0x5DB8, 0x8E57, 0x5DB9, 0x8E58, 0x5DBA,\n\t0x8E59, 0x5DBB, 0x8E5A, 0x5DBC, 0x8E5B, 0x5DBD, 0x8E5C, 0x5DBE,\n\t0x8E5D, 0x5DBF, 0x8E5E, 0x5DC0, 0x8E5F, 0x5DC1, 0x8E60, 0x5DC2,\n\t0x8E61, 0x5DC3, 0x8E62, 0x5DC4, 0x8E63, 0x5DC6, 0x8E64, 0x5DC7,\n\t0x8E65, 0x5DC8, 0x8E66, 0x5DC9, 0x8E67, 0x5DCA, 0x8E68, 0x5DCB,\n\t0x8E69, 0x5DCC, 0x8E6A, 0x5DCE, 0x8E6B, 0x5DCF, 0x8E6C, 0x5DD0,\n\t0x8E6D, 0x5DD1, 0x8E6E, 0x5DD2, 0x8E6F, 0x5DD3, 0x8E70, 0x5DD4,\n\t0x8E71, 0x5DD5, 0x8E72, 0x5DD6, 0x8E73, 0x5DD7, 0x8E74, 0x5DD8,\n\t0x8E75, 0x5DD9, 0x8E76, 0x5DDA, 0x8E77, 0x5DDC, 0x8E78, 0x5DDF,\n\t0x8E79, 0x5DE0, 0x8E7A, 0x5DE3, 0x8E7B, 0x5DE4, 0x8E7C, 0x5DEA,\n\t0x8E7D, 0x5DEC, 0x8E7E, 0x5DED, 0x8E80, 0x5DF0, 0x8E81, 0x5DF5,\n\t0x8E82, 0x5DF6, 0x8E83, 0x5DF8, 0x8E84, 0x5DF9, 0x8E85, 0x5DFA,\n\t0x8E86, 0x5DFB, 0x8E87, 0x5DFC, 0x8E88, 0x5DFF, 0x8E89, 0x5E00,\n\t0x8E8A, 0x5E04, 0x8E8B, 0x5E07, 0x8E8C, 0x5E09, 0x8E8D, 0x5E0A,\n\t0x8E8E, 0x5E0B, 0x8E8F, 0x5E0D, 0x8E90, 0x5E0E, 0x8E91, 0x5E12,\n\t0x8E92, 0x5E13, 0x8E93, 0x5E17, 0x8E94, 0x5E1E, 0x8E95, 0x5E1F,\n\t0x8E96, 0x5E20, 0x8E97, 0x5E21, 0x8E98, 0x5E22, 0x8E99, 0x5E23,\n\t0x8E9A, 0x5E24, 0x8E9B, 0x5E25, 0x8E9C, 0x5E28, 0x8E9D, 0x5E29,\n\t0x8E9E, 0x5E2A, 0x8E9F, 0x5E2B, 0x8EA0, 0x5E2C, 0x8EA1, 0x5E2F,\n\t0x8EA2, 0x5E30, 0x8EA3, 0x5E32, 0x8EA4, 0x5E33, 0x8EA5, 0x5E34,\n\t0x8EA6, 0x5E35, 0x8EA7, 0x5E36, 0x8EA8, 0x5E39, 0x8EA9, 0x5E3A,\n\t0x8EAA, 0x5E3E, 0x8EAB, 0x5E3F, 0x8EAC, 0x5E40, 0x8EAD, 0x5E41,\n\t0x8EAE, 0x5E43, 0x8EAF, 0x5E46, 0x8EB0, 0x5E47, 0x8EB1, 0x5E48,\n\t0x8EB2, 0x5E49, 0x8EB3, 0x5E4A, 0x8EB4, 0x5E4B, 0x8EB5, 0x5E4D,\n\t0x8EB6, 0x5E4E, 0x8EB7, 0x5E4F, 0x8EB8, 0x5E50, 0x8EB9, 0x5E51,\n\t0x8EBA, 0x5E52, 0x8EBB, 0x5E53, 0x8EBC, 0x5E56, 0x8EBD, 0x5E57,\n\t0x8EBE, 0x5E58, 0x8EBF, 0x5E59, 0x8EC0, 0x5E5A, 0x8EC1, 0x5E5C,\n\t0x8EC2, 0x5E5D, 0x8EC3, 0x5E5F, 0x8EC4, 0x5E60, 0x8EC5, 0x5E63,\n\t0x8EC6, 0x5E64, 0x8EC7, 0x5E65, 0x8EC8, 0x5E66, 0x8EC9, 0x5E67,\n\t0x8ECA, 0x5E68, 0x8ECB, 0x5E69, 0x8ECC, 0x5E6A, 0x8ECD, 0x5E6B,\n\t0x8ECE, 0x5E6C, 0x8ECF, 0x5E6D, 0x8ED0, 0x5E6E, 0x8ED1, 0x5E6F,\n\t0x8ED2, 0x5E70, 0x8ED3, 0x5E71, 0x8ED4, 0x5E75, 0x8ED5, 0x5E77,\n\t0x8ED6, 0x5E79, 0x8ED7, 0x5E7E, 0x8ED8, 0x5E81, 0x8ED9, 0x5E82,\n\t0x8EDA, 0x5E83, 0x8EDB, 0x5E85, 0x8EDC, 0x5E88, 0x8EDD, 0x5E89,\n\t0x8EDE, 0x5E8C, 0x8EDF, 0x5E8D, 0x8EE0, 0x5E8E, 0x8EE1, 0x5E92,\n\t0x8EE2, 0x5E98, 0x8EE3, 0x5E9B, 0x8EE4, 0x5E9D, 0x8EE5, 0x5EA1,\n\t0x8EE6, 0x5EA2, 0x8EE7, 0x5EA3, 0x8EE8, 0x5EA4, 0x8EE9, 0x5EA8,\n\t0x8EEA, 0x5EA9, 0x8EEB, 0x5EAA, 0x8EEC, 0x5EAB, 0x8EED, 0x5EAC,\n\t0x8EEE, 0x5EAE, 0x8EEF, 0x5EAF, 0x8EF0, 0x5EB0, 0x8EF1, 0x5EB1,\n\t0x8EF2, 0x5EB2, 0x8EF3, 0x5EB4, 0x8EF4, 0x5EBA, 0x8EF5, 0x5EBB,\n\t0x8EF6, 0x5EBC, 0x8EF7, 0x5EBD, 0x8EF8, 0x5EBF, 0x8EF9, 0x5EC0,\n\t0x8EFA, 0x5EC1, 0x8EFB, 0x5EC2, 0x8EFC, 0x5EC3, 0x8EFD, 0x5EC4,\n\t0x8EFE, 0x5EC5, 0x8F40, 0x5EC6, 0x8F41, 0x5EC7, 0x8F42, 0x5EC8,\n\t0x8F43, 0x5ECB, 0x8F44, 0x5ECC, 0x8F45, 0x5ECD, 0x8F46, 0x5ECE,\n\t0x8F47, 0x5ECF, 0x8F48, 0x5ED0, 0x8F49, 0x5ED4, 0x8F4A, 0x5ED5,\n\t0x8F4B, 0x5ED7, 0x8F4C, 0x5ED8, 0x8F4D, 0x5ED9, 0x8F4E, 0x5EDA,\n\t0x8F4F, 0x5EDC, 0x8F50, 0x5EDD, 0x8F51, 0x5EDE, 0x8F52, 0x5EDF,\n\t0x8F53, 0x5EE0, 0x8F54, 0x5EE1, 0x8F55, 0x5EE2, 0x8F56, 0x5EE3,\n\t0x8F57, 0x5EE4, 0x8F58, 0x5EE5, 0x8F59, 0x5EE6, 0x8F5A, 0x5EE7,\n\t0x8F5B, 0x5EE9, 0x8F5C, 0x5EEB, 0x8F5D, 0x5EEC, 0x8F5E, 0x5EED,\n\t0x8F5F, 0x5EEE, 0x8F60, 0x5EEF, 0x8F61, 0x5EF0, 0x8F62, 0x5EF1,\n\t0x8F63, 0x5EF2, 0x8F64, 0x5EF3, 0x8F65, 0x5EF5, 0x8F66, 0x5EF8,\n\t0x8F67, 0x5EF9, 0x8F68, 0x5EFB, 0x8F69, 0x5EFC, 0x8F6A, 0x5EFD,\n\t0x8F6B, 0x5F05, 0x8F6C, 0x5F06, 0x8F6D, 0x5F07, 0x8F6E, 0x5F09,\n\t0x8F6F, 0x5F0C, 0x8F70, 0x5F0D, 0x8F71, 0x5F0E, 0x8F72, 0x5F10,\n\t0x8F73, 0x5F12, 0x8F74, 0x5F14, 0x8F75, 0x5F16, 0x8F76, 0x5F19,\n\t0x8F77, 0x5F1A, 0x8F78, 0x5F1C, 0x8F79, 0x5F1D, 0x8F7A, 0x5F1E,\n\t0x8F7B, 0x5F21, 0x8F7C, 0x5F22, 0x8F7D, 0x5F23, 0x8F7E, 0x5F24,\n\t0x8F80, 0x5F28, 0x8F81, 0x5F2B, 0x8F82, 0x5F2C, 0x8F83, 0x5F2E,\n\t0x8F84, 0x5F30, 0x8F85, 0x5F32, 0x8F86, 0x5F33, 0x8F87, 0x5F34,\n\t0x8F88, 0x5F35, 0x8F89, 0x5F36, 0x8F8A, 0x5F37, 0x8F8B, 0x5F38,\n\t0x8F8C, 0x5F3B, 0x8F8D, 0x5F3D, 0x8F8E, 0x5F3E, 0x8F8F, 0x5F3F,\n\t0x8F90, 0x5F41, 0x8F91, 0x5F42, 0x8F92, 0x5F43, 0x8F93, 0x5F44,\n\t0x8F94, 0x5F45, 0x8F95, 0x5F46, 0x8F96, 0x5F47, 0x8F97, 0x5F48,\n\t0x8F98, 0x5F49, 0x8F99, 0x5F4A, 0x8F9A, 0x5F4B, 0x8F9B, 0x5F4C,\n\t0x8F9C, 0x5F4D, 0x8F9D, 0x5F4E, 0x8F9E, 0x5F4F, 0x8F9F, 0x5F51,\n\t0x8FA0, 0x5F54, 0x8FA1, 0x5F59, 0x8FA2, 0x5F5A, 0x8FA3, 0x5F5B,\n\t0x8FA4, 0x5F5C, 0x8FA5, 0x5F5E, 0x8FA6, 0x5F5F, 0x8FA7, 0x5F60,\n\t0x8FA8, 0x5F63, 0x8FA9, 0x5F65, 0x8FAA, 0x5F67, 0x8FAB, 0x5F68,\n\t0x8FAC, 0x5F6B, 0x8FAD, 0x5F6E, 0x8FAE, 0x5F6F, 0x8FAF, 0x5F72,\n\t0x8FB0, 0x5F74, 0x8FB1, 0x5F75, 0x8FB2, 0x5F76, 0x8FB3, 0x5F78,\n\t0x8FB4, 0x5F7A, 0x8FB5, 0x5F7D, 0x8FB6, 0x5F7E, 0x8FB7, 0x5F7F,\n\t0x8FB8, 0x5F83, 0x8FB9, 0x5F86, 0x8FBA, 0x5F8D, 0x8FBB, 0x5F8E,\n\t0x8FBC, 0x5F8F, 0x8FBD, 0x5F91, 0x8FBE, 0x5F93, 0x8FBF, 0x5F94,\n\t0x8FC0, 0x5F96, 0x8FC1, 0x5F9A, 0x8FC2, 0x5F9B, 0x8FC3, 0x5F9D,\n\t0x8FC4, 0x5F9E, 0x8FC5, 0x5F9F, 0x8FC6, 0x5FA0, 0x8FC7, 0x5FA2,\n\t0x8FC8, 0x5FA3, 0x8FC9, 0x5FA4, 0x8FCA, 0x5FA5, 0x8FCB, 0x5FA6,\n\t0x8FCC, 0x5FA7, 0x8FCD, 0x5FA9, 0x8FCE, 0x5FAB, 0x8FCF, 0x5FAC,\n\t0x8FD0, 0x5FAF, 0x8FD1, 0x5FB0, 0x8FD2, 0x5FB1, 0x8FD3, 0x5FB2,\n\t0x8FD4, 0x5FB3, 0x8FD5, 0x5FB4, 0x8FD6, 0x5FB6, 0x8FD7, 0x5FB8,\n\t0x8FD8, 0x5FB9, 0x8FD9, 0x5FBA, 0x8FDA, 0x5FBB, 0x8FDB, 0x5FBE,\n\t0x8FDC, 0x5FBF, 0x8FDD, 0x5FC0, 0x8FDE, 0x5FC1, 0x8FDF, 0x5FC2,\n\t0x8FE0, 0x5FC7, 0x8FE1, 0x5FC8, 0x8FE2, 0x5FCA, 0x8FE3, 0x5FCB,\n\t0x8FE4, 0x5FCE, 0x8FE5, 0x5FD3, 0x8FE6, 0x5FD4, 0x8FE7, 0x5FD5,\n\t0x8FE8, 0x5FDA, 0x8FE9, 0x5FDB, 0x8FEA, 0x5FDC, 0x8FEB, 0x5FDE,\n\t0x8FEC, 0x5FDF, 0x8FED, 0x5FE2, 0x8FEE, 0x5FE3, 0x8FEF, 0x5FE5,\n\t0x8FF0, 0x5FE6, 0x8FF1, 0x5FE8, 0x8FF2, 0x5FE9, 0x8FF3, 0x5FEC,\n\t0x8FF4, 0x5FEF, 0x8FF5, 0x5FF0, 0x8FF6, 0x5FF2, 0x8FF7, 0x5FF3,\n\t0x8FF8, 0x5FF4, 0x8FF9, 0x5FF6, 0x8FFA, 0x5FF7, 0x8FFB, 0x5FF9,\n\t0x8FFC, 0x5FFA, 0x8FFD, 0x5FFC, 0x8FFE, 0x6007, 0x9040, 0x6008,\n\t0x9041, 0x6009, 0x9042, 0x600B, 0x9043, 0x600C, 0x9044, 0x6010,\n\t0x9045, 0x6011, 0x9046, 0x6013, 0x9047, 0x6017, 0x9048, 0x6018,\n\t0x9049, 0x601A, 0x904A, 0x601E, 0x904B, 0x601F, 0x904C, 0x6022,\n\t0x904D, 0x6023, 0x904E, 0x6024, 0x904F, 0x602C, 0x9050, 0x602D,\n\t0x9051, 0x602E, 0x9052, 0x6030, 0x9053, 0x6031, 0x9054, 0x6032,\n\t0x9055, 0x6033, 0x9056, 0x6034, 0x9057, 0x6036, 0x9058, 0x6037,\n\t0x9059, 0x6038, 0x905A, 0x6039, 0x905B, 0x603A, 0x905C, 0x603D,\n\t0x905D, 0x603E, 0x905E, 0x6040, 0x905F, 0x6044, 0x9060, 0x6045,\n\t0x9061, 0x6046, 0x9062, 0x6047, 0x9063, 0x6048, 0x9064, 0x6049,\n\t0x9065, 0x604A, 0x9066, 0x604C, 0x9067, 0x604E, 0x9068, 0x604F,\n\t0x9069, 0x6051, 0x906A, 0x6053, 0x906B, 0x6054, 0x906C, 0x6056,\n\t0x906D, 0x6057, 0x906E, 0x6058, 0x906F, 0x605B, 0x9070, 0x605C,\n\t0x9071, 0x605E, 0x9072, 0x605F, 0x9073, 0x6060, 0x9074, 0x6061,\n\t0x9075, 0x6065, 0x9076, 0x6066, 0x9077, 0x606E, 0x9078, 0x6071,\n\t0x9079, 0x6072, 0x907A, 0x6074, 0x907B, 0x6075, 0x907C, 0x6077,\n\t0x907D, 0x607E, 0x907E, 0x6080, 0x9080, 0x6081, 0x9081, 0x6082,\n\t0x9082, 0x6085, 0x9083, 0x6086, 0x9084, 0x6087, 0x9085, 0x6088,\n\t0x9086, 0x608A, 0x9087, 0x608B, 0x9088, 0x608E, 0x9089, 0x608F,\n\t0x908A, 0x6090, 0x908B, 0x6091, 0x908C, 0x6093, 0x908D, 0x6095,\n\t0x908E, 0x6097, 0x908F, 0x6098, 0x9090, 0x6099, 0x9091, 0x609C,\n\t0x9092, 0x609E, 0x9093, 0x60A1, 0x9094, 0x60A2, 0x9095, 0x60A4,\n\t0x9096, 0x60A5, 0x9097, 0x60A7, 0x9098, 0x60A9, 0x9099, 0x60AA,\n\t0x909A, 0x60AE, 0x909B, 0x60B0, 0x909C, 0x60B3, 0x909D, 0x60B5,\n\t0x909E, 0x60B6, 0x909F, 0x60B7, 0x90A0, 0x60B9, 0x90A1, 0x60BA,\n\t0x90A2, 0x60BD, 0x90A3, 0x60BE, 0x90A4, 0x60BF, 0x90A5, 0x60C0,\n\t0x90A6, 0x60C1, 0x90A7, 0x60C2, 0x90A8, 0x60C3, 0x90A9, 0x60C4,\n\t0x90AA, 0x60C7, 0x90AB, 0x60C8, 0x90AC, 0x60C9, 0x90AD, 0x60CC,\n\t0x90AE, 0x60CD, 0x90AF, 0x60CE, 0x90B0, 0x60CF, 0x90B1, 0x60D0,\n\t0x90B2, 0x60D2, 0x90B3, 0x60D3, 0x90B4, 0x60D4, 0x90B5, 0x60D6,\n\t0x90B6, 0x60D7, 0x90B7, 0x60D9, 0x90B8, 0x60DB, 0x90B9, 0x60DE,\n\t0x90BA, 0x60E1, 0x90BB, 0x60E2, 0x90BC, 0x60E3, 0x90BD, 0x60E4,\n\t0x90BE, 0x60E5, 0x90BF, 0x60EA, 0x90C0, 0x60F1, 0x90C1, 0x60F2,\n\t0x90C2, 0x60F5, 0x90C3, 0x60F7, 0x90C4, 0x60F8, 0x90C5, 0x60FB,\n\t0x90C6, 0x60FC, 0x90C7, 0x60FD, 0x90C8, 0x60FE, 0x90C9, 0x60FF,\n\t0x90CA, 0x6102, 0x90CB, 0x6103, 0x90CC, 0x6104, 0x90CD, 0x6105,\n\t0x90CE, 0x6107, 0x90CF, 0x610A, 0x90D0, 0x610B, 0x90D1, 0x610C,\n\t0x90D2, 0x6110, 0x90D3, 0x6111, 0x90D4, 0x6112, 0x90D5, 0x6113,\n\t0x90D6, 0x6114, 0x90D7, 0x6116, 0x90D8, 0x6117, 0x90D9, 0x6118,\n\t0x90DA, 0x6119, 0x90DB, 0x611B, 0x90DC, 0x611C, 0x90DD, 0x611D,\n\t0x90DE, 0x611E, 0x90DF, 0x6121, 0x90E0, 0x6122, 0x90E1, 0x6125,\n\t0x90E2, 0x6128, 0x90E3, 0x6129, 0x90E4, 0x612A, 0x90E5, 0x612C,\n\t0x90E6, 0x612D, 0x90E7, 0x612E, 0x90E8, 0x612F, 0x90E9, 0x6130,\n\t0x90EA, 0x6131, 0x90EB, 0x6132, 0x90EC, 0x6133, 0x90ED, 0x6134,\n\t0x90EE, 0x6135, 0x90EF, 0x6136, 0x90F0, 0x6137, 0x90F1, 0x6138,\n\t0x90F2, 0x6139, 0x90F3, 0x613A, 0x90F4, 0x613B, 0x90F5, 0x613C,\n\t0x90F6, 0x613D, 0x90F7, 0x613E, 0x90F8, 0x6140, 0x90F9, 0x6141,\n\t0x90FA, 0x6142, 0x90FB, 0x6143, 0x90FC, 0x6144, 0x90FD, 0x6145,\n\t0x90FE, 0x6146, 0x9140, 0x6147, 0x9141, 0x6149, 0x9142, 0x614B,\n\t0x9143, 0x614D, 0x9144, 0x614F, 0x9145, 0x6150, 0x9146, 0x6152,\n\t0x9147, 0x6153, 0x9148, 0x6154, 0x9149, 0x6156, 0x914A, 0x6157,\n\t0x914B, 0x6158, 0x914C, 0x6159, 0x914D, 0x615A, 0x914E, 0x615B,\n\t0x914F, 0x615C, 0x9150, 0x615E, 0x9151, 0x615F, 0x9152, 0x6160,\n\t0x9153, 0x6161, 0x9154, 0x6163, 0x9155, 0x6164, 0x9156, 0x6165,\n\t0x9157, 0x6166, 0x9158, 0x6169, 0x9159, 0x616A, 0x915A, 0x616B,\n\t0x915B, 0x616C, 0x915C, 0x616D, 0x915D, 0x616E, 0x915E, 0x616F,\n\t0x915F, 0x6171, 0x9160, 0x6172, 0x9161, 0x6173, 0x9162, 0x6174,\n\t0x9163, 0x6176, 0x9164, 0x6178, 0x9165, 0x6179, 0x9166, 0x617A,\n\t0x9167, 0x617B, 0x9168, 0x617C, 0x9169, 0x617D, 0x916A, 0x617E,\n\t0x916B, 0x617F, 0x916C, 0x6180, 0x916D, 0x6181, 0x916E, 0x6182,\n\t0x916F, 0x6183, 0x9170, 0x6184, 0x9171, 0x6185, 0x9172, 0x6186,\n\t0x9173, 0x6187, 0x9174, 0x6188, 0x9175, 0x6189, 0x9176, 0x618A,\n\t0x9177, 0x618C, 0x9178, 0x618D, 0x9179, 0x618F, 0x917A, 0x6190,\n\t0x917B, 0x6191, 0x917C, 0x6192, 0x917D, 0x6193, 0x917E, 0x6195,\n\t0x9180, 0x6196, 0x9181, 0x6197, 0x9182, 0x6198, 0x9183, 0x6199,\n\t0x9184, 0x619A, 0x9185, 0x619B, 0x9186, 0x619C, 0x9187, 0x619E,\n\t0x9188, 0x619F, 0x9189, 0x61A0, 0x918A, 0x61A1, 0x918B, 0x61A2,\n\t0x918C, 0x61A3, 0x918D, 0x61A4, 0x918E, 0x61A5, 0x918F, 0x61A6,\n\t0x9190, 0x61AA, 0x9191, 0x61AB, 0x9192, 0x61AD, 0x9193, 0x61AE,\n\t0x9194, 0x61AF, 0x9195, 0x61B0, 0x9196, 0x61B1, 0x9197, 0x61B2,\n\t0x9198, 0x61B3, 0x9199, 0x61B4, 0x919A, 0x61B5, 0x919B, 0x61B6,\n\t0x919C, 0x61B8, 0x919D, 0x61B9, 0x919E, 0x61BA, 0x919F, 0x61BB,\n\t0x91A0, 0x61BC, 0x91A1, 0x61BD, 0x91A2, 0x61BF, 0x91A3, 0x61C0,\n\t0x91A4, 0x61C1, 0x91A5, 0x61C3, 0x91A6, 0x61C4, 0x91A7, 0x61C5,\n\t0x91A8, 0x61C6, 0x91A9, 0x61C7, 0x91AA, 0x61C9, 0x91AB, 0x61CC,\n\t0x91AC, 0x61CD, 0x91AD, 0x61CE, 0x91AE, 0x61CF, 0x91AF, 0x61D0,\n\t0x91B0, 0x61D3, 0x91B1, 0x61D5, 0x91B2, 0x61D6, 0x91B3, 0x61D7,\n\t0x91B4, 0x61D8, 0x91B5, 0x61D9, 0x91B6, 0x61DA, 0x91B7, 0x61DB,\n\t0x91B8, 0x61DC, 0x91B9, 0x61DD, 0x91BA, 0x61DE, 0x91BB, 0x61DF,\n\t0x91BC, 0x61E0, 0x91BD, 0x61E1, 0x91BE, 0x61E2, 0x91BF, 0x61E3,\n\t0x91C0, 0x61E4, 0x91C1, 0x61E5, 0x91C2, 0x61E7, 0x91C3, 0x61E8,\n\t0x91C4, 0x61E9, 0x91C5, 0x61EA, 0x91C6, 0x61EB, 0x91C7, 0x61EC,\n\t0x91C8, 0x61ED, 0x91C9, 0x61EE, 0x91CA, 0x61EF, 0x91CB, 0x61F0,\n\t0x91CC, 0x61F1, 0x91CD, 0x61F2, 0x91CE, 0x61F3, 0x91CF, 0x61F4,\n\t0x91D0, 0x61F6, 0x91D1, 0x61F7, 0x91D2, 0x61F8, 0x91D3, 0x61F9,\n\t0x91D4, 0x61FA, 0x91D5, 0x61FB, 0x91D6, 0x61FC, 0x91D7, 0x61FD,\n\t0x91D8, 0x61FE, 0x91D9, 0x6200, 0x91DA, 0x6201, 0x91DB, 0x6202,\n\t0x91DC, 0x6203, 0x91DD, 0x6204, 0x91DE, 0x6205, 0x91DF, 0x6207,\n\t0x91E0, 0x6209, 0x91E1, 0x6213, 0x91E2, 0x6214, 0x91E3, 0x6219,\n\t0x91E4, 0x621C, 0x91E5, 0x621D, 0x91E6, 0x621E, 0x91E7, 0x6220,\n\t0x91E8, 0x6223, 0x91E9, 0x6226, 0x91EA, 0x6227, 0x91EB, 0x6228,\n\t0x91EC, 0x6229, 0x91ED, 0x622B, 0x91EE, 0x622D, 0x91EF, 0x622F,\n\t0x91F0, 0x6230, 0x91F1, 0x6231, 0x91F2, 0x6232, 0x91F3, 0x6235,\n\t0x91F4, 0x6236, 0x91F5, 0x6238, 0x91F6, 0x6239, 0x91F7, 0x623A,\n\t0x91F8, 0x623B, 0x91F9, 0x623C, 0x91FA, 0x6242, 0x91FB, 0x6244,\n\t0x91FC, 0x6245, 0x91FD, 0x6246, 0x91FE, 0x624A, 0x9240, 0x624F,\n\t0x9241, 0x6250, 0x9242, 0x6255, 0x9243, 0x6256, 0x9244, 0x6257,\n\t0x9245, 0x6259, 0x9246, 0x625A, 0x9247, 0x625C, 0x9248, 0x625D,\n\t0x9249, 0x625E, 0x924A, 0x625F, 0x924B, 0x6260, 0x924C, 0x6261,\n\t0x924D, 0x6262, 0x924E, 0x6264, 0x924F, 0x6265, 0x9250, 0x6268,\n\t0x9251, 0x6271, 0x9252, 0x6272, 0x9253, 0x6274, 0x9254, 0x6275,\n\t0x9255, 0x6277, 0x9256, 0x6278, 0x9257, 0x627A, 0x9258, 0x627B,\n\t0x9259, 0x627D, 0x925A, 0x6281, 0x925B, 0x6282, 0x925C, 0x6283,\n\t0x925D, 0x6285, 0x925E, 0x6286, 0x925F, 0x6287, 0x9260, 0x6288,\n\t0x9261, 0x628B, 0x9262, 0x628C, 0x9263, 0x628D, 0x9264, 0x628E,\n\t0x9265, 0x628F, 0x9266, 0x6290, 0x9267, 0x6294, 0x9268, 0x6299,\n\t0x9269, 0x629C, 0x926A, 0x629D, 0x926B, 0x629E, 0x926C, 0x62A3,\n\t0x926D, 0x62A6, 0x926E, 0x62A7, 0x926F, 0x62A9, 0x9270, 0x62AA,\n\t0x9271, 0x62AD, 0x9272, 0x62AE, 0x9273, 0x62AF, 0x9274, 0x62B0,\n\t0x9275, 0x62B2, 0x9276, 0x62B3, 0x9277, 0x62B4, 0x9278, 0x62B6,\n\t0x9279, 0x62B7, 0x927A, 0x62B8, 0x927B, 0x62BA, 0x927C, 0x62BE,\n\t0x927D, 0x62C0, 0x927E, 0x62C1, 0x9280, 0x62C3, 0x9281, 0x62CB,\n\t0x9282, 0x62CF, 0x9283, 0x62D1, 0x9284, 0x62D5, 0x9285, 0x62DD,\n\t0x9286, 0x62DE, 0x9287, 0x62E0, 0x9288, 0x62E1, 0x9289, 0x62E4,\n\t0x928A, 0x62EA, 0x928B, 0x62EB, 0x928C, 0x62F0, 0x928D, 0x62F2,\n\t0x928E, 0x62F5, 0x928F, 0x62F8, 0x9290, 0x62F9, 0x9291, 0x62FA,\n\t0x9292, 0x62FB, 0x9293, 0x6300, 0x9294, 0x6303, 0x9295, 0x6304,\n\t0x9296, 0x6305, 0x9297, 0x6306, 0x9298, 0x630A, 0x9299, 0x630B,\n\t0x929A, 0x630C, 0x929B, 0x630D, 0x929C, 0x630F, 0x929D, 0x6310,\n\t0x929E, 0x6312, 0x929F, 0x6313, 0x92A0, 0x6314, 0x92A1, 0x6315,\n\t0x92A2, 0x6317, 0x92A3, 0x6318, 0x92A4, 0x6319, 0x92A5, 0x631C,\n\t0x92A6, 0x6326, 0x92A7, 0x6327, 0x92A8, 0x6329, 0x92A9, 0x632C,\n\t0x92AA, 0x632D, 0x92AB, 0x632E, 0x92AC, 0x6330, 0x92AD, 0x6331,\n\t0x92AE, 0x6333, 0x92AF, 0x6334, 0x92B0, 0x6335, 0x92B1, 0x6336,\n\t0x92B2, 0x6337, 0x92B3, 0x6338, 0x92B4, 0x633B, 0x92B5, 0x633C,\n\t0x92B6, 0x633E, 0x92B7, 0x633F, 0x92B8, 0x6340, 0x92B9, 0x6341,\n\t0x92BA, 0x6344, 0x92BB, 0x6347, 0x92BC, 0x6348, 0x92BD, 0x634A,\n\t0x92BE, 0x6351, 0x92BF, 0x6352, 0x92C0, 0x6353, 0x92C1, 0x6354,\n\t0x92C2, 0x6356, 0x92C3, 0x6357, 0x92C4, 0x6358, 0x92C5, 0x6359,\n\t0x92C6, 0x635A, 0x92C7, 0x635B, 0x92C8, 0x635C, 0x92C9, 0x635D,\n\t0x92CA, 0x6360, 0x92CB, 0x6364, 0x92CC, 0x6365, 0x92CD, 0x6366,\n\t0x92CE, 0x6368, 0x92CF, 0x636A, 0x92D0, 0x636B, 0x92D1, 0x636C,\n\t0x92D2, 0x636F, 0x92D3, 0x6370, 0x92D4, 0x6372, 0x92D5, 0x6373,\n\t0x92D6, 0x6374, 0x92D7, 0x6375, 0x92D8, 0x6378, 0x92D9, 0x6379,\n\t0x92DA, 0x637C, 0x92DB, 0x637D, 0x92DC, 0x637E, 0x92DD, 0x637F,\n\t0x92DE, 0x6381, 0x92DF, 0x6383, 0x92E0, 0x6384, 0x92E1, 0x6385,\n\t0x92E2, 0x6386, 0x92E3, 0x638B, 0x92E4, 0x638D, 0x92E5, 0x6391,\n\t0x92E6, 0x6393, 0x92E7, 0x6394, 0x92E8, 0x6395, 0x92E9, 0x6397,\n\t0x92EA, 0x6399, 0x92EB, 0x639A, 0x92EC, 0x639B, 0x92ED, 0x639C,\n\t0x92EE, 0x639D, 0x92EF, 0x639E, 0x92F0, 0x639F, 0x92F1, 0x63A1,\n\t0x92F2, 0x63A4, 0x92F3, 0x63A6, 0x92F4, 0x63AB, 0x92F5, 0x63AF,\n\t0x92F6, 0x63B1, 0x92F7, 0x63B2, 0x92F8, 0x63B5, 0x92F9, 0x63B6,\n\t0x92FA, 0x63B9, 0x92FB, 0x63BB, 0x92FC, 0x63BD, 0x92FD, 0x63BF,\n\t0x92FE, 0x63C0, 0x9340, 0x63C1, 0x9341, 0x63C2, 0x9342, 0x63C3,\n\t0x9343, 0x63C5, 0x9344, 0x63C7, 0x9345, 0x63C8, 0x9346, 0x63CA,\n\t0x9347, 0x63CB, 0x9348, 0x63CC, 0x9349, 0x63D1, 0x934A, 0x63D3,\n\t0x934B, 0x63D4, 0x934C, 0x63D5, 0x934D, 0x63D7, 0x934E, 0x63D8,\n\t0x934F, 0x63D9, 0x9350, 0x63DA, 0x9351, 0x63DB, 0x9352, 0x63DC,\n\t0x9353, 0x63DD, 0x9354, 0x63DF, 0x9355, 0x63E2, 0x9356, 0x63E4,\n\t0x9357, 0x63E5, 0x9358, 0x63E6, 0x9359, 0x63E7, 0x935A, 0x63E8,\n\t0x935B, 0x63EB, 0x935C, 0x63EC, 0x935D, 0x63EE, 0x935E, 0x63EF,\n\t0x935F, 0x63F0, 0x9360, 0x63F1, 0x9361, 0x63F3, 0x9362, 0x63F5,\n\t0x9363, 0x63F7, 0x9364, 0x63F9, 0x9365, 0x63FA, 0x9366, 0x63FB,\n\t0x9367, 0x63FC, 0x9368, 0x63FE, 0x9369, 0x6403, 0x936A, 0x6404,\n\t0x936B, 0x6406, 0x936C, 0x6407, 0x936D, 0x6408, 0x936E, 0x6409,\n\t0x936F, 0x640A, 0x9370, 0x640D, 0x9371, 0x640E, 0x9372, 0x6411,\n\t0x9373, 0x6412, 0x9374, 0x6415, 0x9375, 0x6416, 0x9376, 0x6417,\n\t0x9377, 0x6418, 0x9378, 0x6419, 0x9379, 0x641A, 0x937A, 0x641D,\n\t0x937B, 0x641F, 0x937C, 0x6422, 0x937D, 0x6423, 0x937E, 0x6424,\n\t0x9380, 0x6425, 0x9381, 0x6427, 0x9382, 0x6428, 0x9383, 0x6429,\n\t0x9384, 0x642B, 0x9385, 0x642E, 0x9386, 0x642F, 0x9387, 0x6430,\n\t0x9388, 0x6431, 0x9389, 0x6432, 0x938A, 0x6433, 0x938B, 0x6435,\n\t0x938C, 0x6436, 0x938D, 0x6437, 0x938E, 0x6438, 0x938F, 0x6439,\n\t0x9390, 0x643B, 0x9391, 0x643C, 0x9392, 0x643E, 0x9393, 0x6440,\n\t0x9394, 0x6442, 0x9395, 0x6443, 0x9396, 0x6449, 0x9397, 0x644B,\n\t0x9398, 0x644C, 0x9399, 0x644D, 0x939A, 0x644E, 0x939B, 0x644F,\n\t0x939C, 0x6450, 0x939D, 0x6451, 0x939E, 0x6453, 0x939F, 0x6455,\n\t0x93A0, 0x6456, 0x93A1, 0x6457, 0x93A2, 0x6459, 0x93A3, 0x645A,\n\t0x93A4, 0x645B, 0x93A5, 0x645C, 0x93A6, 0x645D, 0x93A7, 0x645F,\n\t0x93A8, 0x6460, 0x93A9, 0x6461, 0x93AA, 0x6462, 0x93AB, 0x6463,\n\t0x93AC, 0x6464, 0x93AD, 0x6465, 0x93AE, 0x6466, 0x93AF, 0x6468,\n\t0x93B0, 0x646A, 0x93B1, 0x646B, 0x93B2, 0x646C, 0x93B3, 0x646E,\n\t0x93B4, 0x646F, 0x93B5, 0x6470, 0x93B6, 0x6471, 0x93B7, 0x6472,\n\t0x93B8, 0x6473, 0x93B9, 0x6474, 0x93BA, 0x6475, 0x93BB, 0x6476,\n\t0x93BC, 0x6477, 0x93BD, 0x647B, 0x93BE, 0x647C, 0x93BF, 0x647D,\n\t0x93C0, 0x647E, 0x93C1, 0x647F, 0x93C2, 0x6480, 0x93C3, 0x6481,\n\t0x93C4, 0x6483, 0x93C5, 0x6486, 0x93C6, 0x6488, 0x93C7, 0x6489,\n\t0x93C8, 0x648A, 0x93C9, 0x648B, 0x93CA, 0x648C, 0x93CB, 0x648D,\n\t0x93CC, 0x648E, 0x93CD, 0x648F, 0x93CE, 0x6490, 0x93CF, 0x6493,\n\t0x93D0, 0x6494, 0x93D1, 0x6497, 0x93D2, 0x6498, 0x93D3, 0x649A,\n\t0x93D4, 0x649B, 0x93D5, 0x649C, 0x93D6, 0x649D, 0x93D7, 0x649F,\n\t0x93D8, 0x64A0, 0x93D9, 0x64A1, 0x93DA, 0x64A2, 0x93DB, 0x64A3,\n\t0x93DC, 0x64A5, 0x93DD, 0x64A6, 0x93DE, 0x64A7, 0x93DF, 0x64A8,\n\t0x93E0, 0x64AA, 0x93E1, 0x64AB, 0x93E2, 0x64AF, 0x93E3, 0x64B1,\n\t0x93E4, 0x64B2, 0x93E5, 0x64B3, 0x93E6, 0x64B4, 0x93E7, 0x64B6,\n\t0x93E8, 0x64B9, 0x93E9, 0x64BB, 0x93EA, 0x64BD, 0x93EB, 0x64BE,\n\t0x93EC, 0x64BF, 0x93ED, 0x64C1, 0x93EE, 0x64C3, 0x93EF, 0x64C4,\n\t0x93F0, 0x64C6, 0x93F1, 0x64C7, 0x93F2, 0x64C8, 0x93F3, 0x64C9,\n\t0x93F4, 0x64CA, 0x93F5, 0x64CB, 0x93F6, 0x64CC, 0x93F7, 0x64CF,\n\t0x93F8, 0x64D1, 0x93F9, 0x64D3, 0x93FA, 0x64D4, 0x93FB, 0x64D5,\n\t0x93FC, 0x64D6, 0x93FD, 0x64D9, 0x93FE, 0x64DA, 0x9440, 0x64DB,\n\t0x9441, 0x64DC, 0x9442, 0x64DD, 0x9443, 0x64DF, 0x9444, 0x64E0,\n\t0x9445, 0x64E1, 0x9446, 0x64E3, 0x9447, 0x64E5, 0x9448, 0x64E7,\n\t0x9449, 0x64E8, 0x944A, 0x64E9, 0x944B, 0x64EA, 0x944C, 0x64EB,\n\t0x944D, 0x64EC, 0x944E, 0x64ED, 0x944F, 0x64EE, 0x9450, 0x64EF,\n\t0x9451, 0x64F0, 0x9452, 0x64F1, 0x9453, 0x64F2, 0x9454, 0x64F3,\n\t0x9455, 0x64F4, 0x9456, 0x64F5, 0x9457, 0x64F6, 0x9458, 0x64F7,\n\t0x9459, 0x64F8, 0x945A, 0x64F9, 0x945B, 0x64FA, 0x945C, 0x64FB,\n\t0x945D, 0x64FC, 0x945E, 0x64FD, 0x945F, 0x64FE, 0x9460, 0x64FF,\n\t0x9461, 0x6501, 0x9462, 0x6502, 0x9463, 0x6503, 0x9464, 0x6504,\n\t0x9465, 0x6505, 0x9466, 0x6506, 0x9467, 0x6507, 0x9468, 0x6508,\n\t0x9469, 0x650A, 0x946A, 0x650B, 0x946B, 0x650C, 0x946C, 0x650D,\n\t0x946D, 0x650E, 0x946E, 0x650F, 0x946F, 0x6510, 0x9470, 0x6511,\n\t0x9471, 0x6513, 0x9472, 0x6514, 0x9473, 0x6515, 0x9474, 0x6516,\n\t0x9475, 0x6517, 0x9476, 0x6519, 0x9477, 0x651A, 0x9478, 0x651B,\n\t0x9479, 0x651C, 0x947A, 0x651D, 0x947B, 0x651E, 0x947C, 0x651F,\n\t0x947D, 0x6520, 0x947E, 0x6521, 0x9480, 0x6522, 0x9481, 0x6523,\n\t0x9482, 0x6524, 0x9483, 0x6526, 0x9484, 0x6527, 0x9485, 0x6528,\n\t0x9486, 0x6529, 0x9487, 0x652A, 0x9488, 0x652C, 0x9489, 0x652D,\n\t0x948A, 0x6530, 0x948B, 0x6531, 0x948C, 0x6532, 0x948D, 0x6533,\n\t0x948E, 0x6537, 0x948F, 0x653A, 0x9490, 0x653C, 0x9491, 0x653D,\n\t0x9492, 0x6540, 0x9493, 0x6541, 0x9494, 0x6542, 0x9495, 0x6543,\n\t0x9496, 0x6544, 0x9497, 0x6546, 0x9498, 0x6547, 0x9499, 0x654A,\n\t0x949A, 0x654B, 0x949B, 0x654D, 0x949C, 0x654E, 0x949D, 0x6550,\n\t0x949E, 0x6552, 0x949F, 0x6553, 0x94A0, 0x6554, 0x94A1, 0x6557,\n\t0x94A2, 0x6558, 0x94A3, 0x655A, 0x94A4, 0x655C, 0x94A5, 0x655F,\n\t0x94A6, 0x6560, 0x94A7, 0x6561, 0x94A8, 0x6564, 0x94A9, 0x6565,\n\t0x94AA, 0x6567, 0x94AB, 0x6568, 0x94AC, 0x6569, 0x94AD, 0x656A,\n\t0x94AE, 0x656D, 0x94AF, 0x656E, 0x94B0, 0x656F, 0x94B1, 0x6571,\n\t0x94B2, 0x6573, 0x94B3, 0x6575, 0x94B4, 0x6576, 0x94B5, 0x6578,\n\t0x94B6, 0x6579, 0x94B7, 0x657A, 0x94B8, 0x657B, 0x94B9, 0x657C,\n\t0x94BA, 0x657D, 0x94BB, 0x657E, 0x94BC, 0x657F, 0x94BD, 0x6580,\n\t0x94BE, 0x6581, 0x94BF, 0x6582, 0x94C0, 0x6583, 0x94C1, 0x6584,\n\t0x94C2, 0x6585, 0x94C3, 0x6586, 0x94C4, 0x6588, 0x94C5, 0x6589,\n\t0x94C6, 0x658A, 0x94C7, 0x658D, 0x94C8, 0x658E, 0x94C9, 0x658F,\n\t0x94CA, 0x6592, 0x94CB, 0x6594, 0x94CC, 0x6595, 0x94CD, 0x6596,\n\t0x94CE, 0x6598, 0x94CF, 0x659A, 0x94D0, 0x659D, 0x94D1, 0x659E,\n\t0x94D2, 0x65A0, 0x94D3, 0x65A2, 0x94D4, 0x65A3, 0x94D5, 0x65A6,\n\t0x94D6, 0x65A8, 0x94D7, 0x65AA, 0x94D8, 0x65AC, 0x94D9, 0x65AE,\n\t0x94DA, 0x65B1, 0x94DB, 0x65B2, 0x94DC, 0x65B3, 0x94DD, 0x65B4,\n\t0x94DE, 0x65B5, 0x94DF, 0x65B6, 0x94E0, 0x65B7, 0x94E1, 0x65B8,\n\t0x94E2, 0x65BA, 0x94E3, 0x65BB, 0x94E4, 0x65BE, 0x94E5, 0x65BF,\n\t0x94E6, 0x65C0, 0x94E7, 0x65C2, 0x94E8, 0x65C7, 0x94E9, 0x65C8,\n\t0x94EA, 0x65C9, 0x94EB, 0x65CA, 0x94EC, 0x65CD, 0x94ED, 0x65D0,\n\t0x94EE, 0x65D1, 0x94EF, 0x65D3, 0x94F0, 0x65D4, 0x94F1, 0x65D5,\n\t0x94F2, 0x65D8, 0x94F3, 0x65D9, 0x94F4, 0x65DA, 0x94F5, 0x65DB,\n\t0x94F6, 0x65DC, 0x94F7, 0x65DD, 0x94F8, 0x65DE, 0x94F9, 0x65DF,\n\t0x94FA, 0x65E1, 0x94FB, 0x65E3, 0x94FC, 0x65E4, 0x94FD, 0x65EA,\n\t0x94FE, 0x65EB, 0x9540, 0x65F2, 0x9541, 0x65F3, 0x9542, 0x65F4,\n\t0x9543, 0x65F5, 0x9544, 0x65F8, 0x9545, 0x65F9, 0x9546, 0x65FB,\n\t0x9547, 0x65FC, 0x9548, 0x65FD, 0x9549, 0x65FE, 0x954A, 0x65FF,\n\t0x954B, 0x6601, 0x954C, 0x6604, 0x954D, 0x6605, 0x954E, 0x6607,\n\t0x954F, 0x6608, 0x9550, 0x6609, 0x9551, 0x660B, 0x9552, 0x660D,\n\t0x9553, 0x6610, 0x9554, 0x6611, 0x9555, 0x6612, 0x9556, 0x6616,\n\t0x9557, 0x6617, 0x9558, 0x6618, 0x9559, 0x661A, 0x955A, 0x661B,\n\t0x955B, 0x661C, 0x955C, 0x661E, 0x955D, 0x6621, 0x955E, 0x6622,\n\t0x955F, 0x6623, 0x9560, 0x6624, 0x9561, 0x6626, 0x9562, 0x6629,\n\t0x9563, 0x662A, 0x9564, 0x662B, 0x9565, 0x662C, 0x9566, 0x662E,\n\t0x9567, 0x6630, 0x9568, 0x6632, 0x9569, 0x6633, 0x956A, 0x6637,\n\t0x956B, 0x6638, 0x956C, 0x6639, 0x956D, 0x663A, 0x956E, 0x663B,\n\t0x956F, 0x663D, 0x9570, 0x663F, 0x9571, 0x6640, 0x9572, 0x6642,\n\t0x9573, 0x6644, 0x9574, 0x6645, 0x9575, 0x6646, 0x9576, 0x6647,\n\t0x9577, 0x6648, 0x9578, 0x6649, 0x9579, 0x664A, 0x957A, 0x664D,\n\t0x957B, 0x664E, 0x957C, 0x6650, 0x957D, 0x6651, 0x957E, 0x6658,\n\t0x9580, 0x6659, 0x9581, 0x665B, 0x9582, 0x665C, 0x9583, 0x665D,\n\t0x9584, 0x665E, 0x9585, 0x6660, 0x9586, 0x6662, 0x9587, 0x6663,\n\t0x9588, 0x6665, 0x9589, 0x6667, 0x958A, 0x6669, 0x958B, 0x666A,\n\t0x958C, 0x666B, 0x958D, 0x666C, 0x958E, 0x666D, 0x958F, 0x6671,\n\t0x9590, 0x6672, 0x9591, 0x6673, 0x9592, 0x6675, 0x9593, 0x6678,\n\t0x9594, 0x6679, 0x9595, 0x667B, 0x9596, 0x667C, 0x9597, 0x667D,\n\t0x9598, 0x667F, 0x9599, 0x6680, 0x959A, 0x6681, 0x959B, 0x6683,\n\t0x959C, 0x6685, 0x959D, 0x6686, 0x959E, 0x6688, 0x959F, 0x6689,\n\t0x95A0, 0x668A, 0x95A1, 0x668B, 0x95A2, 0x668D, 0x95A3, 0x668E,\n\t0x95A4, 0x668F, 0x95A5, 0x6690, 0x95A6, 0x6692, 0x95A7, 0x6693,\n\t0x95A8, 0x6694, 0x95A9, 0x6695, 0x95AA, 0x6698, 0x95AB, 0x6699,\n\t0x95AC, 0x669A, 0x95AD, 0x669B, 0x95AE, 0x669C, 0x95AF, 0x669E,\n\t0x95B0, 0x669F, 0x95B1, 0x66A0, 0x95B2, 0x66A1, 0x95B3, 0x66A2,\n\t0x95B4, 0x66A3, 0x95B5, 0x66A4, 0x95B6, 0x66A5, 0x95B7, 0x66A6,\n\t0x95B8, 0x66A9, 0x95B9, 0x66AA, 0x95BA, 0x66AB, 0x95BB, 0x66AC,\n\t0x95BC, 0x66AD, 0x95BD, 0x66AF, 0x95BE, 0x66B0, 0x95BF, 0x66B1,\n\t0x95C0, 0x66B2, 0x95C1, 0x66B3, 0x95C2, 0x66B5, 0x95C3, 0x66B6,\n\t0x95C4, 0x66B7, 0x95C5, 0x66B8, 0x95C6, 0x66BA, 0x95C7, 0x66BB,\n\t0x95C8, 0x66BC, 0x95C9, 0x66BD, 0x95CA, 0x66BF, 0x95CB, 0x66C0,\n\t0x95CC, 0x66C1, 0x95CD, 0x66C2, 0x95CE, 0x66C3, 0x95CF, 0x66C4,\n\t0x95D0, 0x66C5, 0x95D1, 0x66C6, 0x95D2, 0x66C7, 0x95D3, 0x66C8,\n\t0x95D4, 0x66C9, 0x95D5, 0x66CA, 0x95D6, 0x66CB, 0x95D7, 0x66CC,\n\t0x95D8, 0x66CD, 0x95D9, 0x66CE, 0x95DA, 0x66CF, 0x95DB, 0x66D0,\n\t0x95DC, 0x66D1, 0x95DD, 0x66D2, 0x95DE, 0x66D3, 0x95DF, 0x66D4,\n\t0x95E0, 0x66D5, 0x95E1, 0x66D6, 0x95E2, 0x66D7, 0x95E3, 0x66D8,\n\t0x95E4, 0x66DA, 0x95E5, 0x66DE, 0x95E6, 0x66DF, 0x95E7, 0x66E0,\n\t0x95E8, 0x66E1, 0x95E9, 0x66E2, 0x95EA, 0x66E3, 0x95EB, 0x66E4,\n\t0x95EC, 0x66E5, 0x95ED, 0x66E7, 0x95EE, 0x66E8, 0x95EF, 0x66EA,\n\t0x95F0, 0x66EB, 0x95F1, 0x66EC, 0x95F2, 0x66ED, 0x95F3, 0x66EE,\n\t0x95F4, 0x66EF, 0x95F5, 0x66F1, 0x95F6, 0x66F5, 0x95F7, 0x66F6,\n\t0x95F8, 0x66F8, 0x95F9, 0x66FA, 0x95FA, 0x66FB, 0x95FB, 0x66FD,\n\t0x95FC, 0x6701, 0x95FD, 0x6702, 0x95FE, 0x6703, 0x9640, 0x6704,\n\t0x9641, 0x6705, 0x9642, 0x6706, 0x9643, 0x6707, 0x9644, 0x670C,\n\t0x9645, 0x670E, 0x9646, 0x670F, 0x9647, 0x6711, 0x9648, 0x6712,\n\t0x9649, 0x6713, 0x964A, 0x6716, 0x964B, 0x6718, 0x964C, 0x6719,\n\t0x964D, 0x671A, 0x964E, 0x671C, 0x964F, 0x671E, 0x9650, 0x6720,\n\t0x9651, 0x6721, 0x9652, 0x6722, 0x9653, 0x6723, 0x9654, 0x6724,\n\t0x9655, 0x6725, 0x9656, 0x6727, 0x9657, 0x6729, 0x9658, 0x672E,\n\t0x9659, 0x6730, 0x965A, 0x6732, 0x965B, 0x6733, 0x965C, 0x6736,\n\t0x965D, 0x6737, 0x965E, 0x6738, 0x965F, 0x6739, 0x9660, 0x673B,\n\t0x9661, 0x673C, 0x9662, 0x673E, 0x9663, 0x673F, 0x9664, 0x6741,\n\t0x9665, 0x6744, 0x9666, 0x6745, 0x9667, 0x6747, 0x9668, 0x674A,\n\t0x9669, 0x674B, 0x966A, 0x674D, 0x966B, 0x6752, 0x966C, 0x6754,\n\t0x966D, 0x6755, 0x966E, 0x6757, 0x966F, 0x6758, 0x9670, 0x6759,\n\t0x9671, 0x675A, 0x9672, 0x675B, 0x9673, 0x675D, 0x9674, 0x6762,\n\t0x9675, 0x6763, 0x9676, 0x6764, 0x9677, 0x6766, 0x9678, 0x6767,\n\t0x9679, 0x676B, 0x967A, 0x676C, 0x967B, 0x676E, 0x967C, 0x6771,\n\t0x967D, 0x6774, 0x967E, 0x6776, 0x9680, 0x6778, 0x9681, 0x6779,\n\t0x9682, 0x677A, 0x9683, 0x677B, 0x9684, 0x677D, 0x9685, 0x6780,\n\t0x9686, 0x6782, 0x9687, 0x6783, 0x9688, 0x6785, 0x9689, 0x6786,\n\t0x968A, 0x6788, 0x968B, 0x678A, 0x968C, 0x678C, 0x968D, 0x678D,\n\t0x968E, 0x678E, 0x968F, 0x678F, 0x9690, 0x6791, 0x9691, 0x6792,\n\t0x9692, 0x6793, 0x9693, 0x6794, 0x9694, 0x6796, 0x9695, 0x6799,\n\t0x9696, 0x679B, 0x9697, 0x679F, 0x9698, 0x67A0, 0x9699, 0x67A1,\n\t0x969A, 0x67A4, 0x969B, 0x67A6, 0x969C, 0x67A9, 0x969D, 0x67AC,\n\t0x969E, 0x67AE, 0x969F, 0x67B1, 0x96A0, 0x67B2, 0x96A1, 0x67B4,\n\t0x96A2, 0x67B9, 0x96A3, 0x67BA, 0x96A4, 0x67BB, 0x96A5, 0x67BC,\n\t0x96A6, 0x67BD, 0x96A7, 0x67BE, 0x96A8, 0x67BF, 0x96A9, 0x67C0,\n\t0x96AA, 0x67C2, 0x96AB, 0x67C5, 0x96AC, 0x67C6, 0x96AD, 0x67C7,\n\t0x96AE, 0x67C8, 0x96AF, 0x67C9, 0x96B0, 0x67CA, 0x96B1, 0x67CB,\n\t0x96B2, 0x67CC, 0x96B3, 0x67CD, 0x96B4, 0x67CE, 0x96B5, 0x67D5,\n\t0x96B6, 0x67D6, 0x96B7, 0x67D7, 0x96B8, 0x67DB, 0x96B9, 0x67DF,\n\t0x96BA, 0x67E1, 0x96BB, 0x67E3, 0x96BC, 0x67E4, 0x96BD, 0x67E6,\n\t0x96BE, 0x67E7, 0x96BF, 0x67E8, 0x96C0, 0x67EA, 0x96C1, 0x67EB,\n\t0x96C2, 0x67ED, 0x96C3, 0x67EE, 0x96C4, 0x67F2, 0x96C5, 0x67F5,\n\t0x96C6, 0x67F6, 0x96C7, 0x67F7, 0x96C8, 0x67F8, 0x96C9, 0x67F9,\n\t0x96CA, 0x67FA, 0x96CB, 0x67FB, 0x96CC, 0x67FC, 0x96CD, 0x67FE,\n\t0x96CE, 0x6801, 0x96CF, 0x6802, 0x96D0, 0x6803, 0x96D1, 0x6804,\n\t0x96D2, 0x6806, 0x96D3, 0x680D, 0x96D4, 0x6810, 0x96D5, 0x6812,\n\t0x96D6, 0x6814, 0x96D7, 0x6815, 0x96D8, 0x6818, 0x96D9, 0x6819,\n\t0x96DA, 0x681A, 0x96DB, 0x681B, 0x96DC, 0x681C, 0x96DD, 0x681E,\n\t0x96DE, 0x681F, 0x96DF, 0x6820, 0x96E0, 0x6822, 0x96E1, 0x6823,\n\t0x96E2, 0x6824, 0x96E3, 0x6825, 0x96E4, 0x6826, 0x96E5, 0x6827,\n\t0x96E6, 0x6828, 0x96E7, 0x682B, 0x96E8, 0x682C, 0x96E9, 0x682D,\n\t0x96EA, 0x682E, 0x96EB, 0x682F, 0x96EC, 0x6830, 0x96ED, 0x6831,\n\t0x96EE, 0x6834, 0x96EF, 0x6835, 0x96F0, 0x6836, 0x96F1, 0x683A,\n\t0x96F2, 0x683B, 0x96F3, 0x683F, 0x96F4, 0x6847, 0x96F5, 0x684B,\n\t0x96F6, 0x684D, 0x96F7, 0x684F, 0x96F8, 0x6852, 0x96F9, 0x6856,\n\t0x96FA, 0x6857, 0x96FB, 0x6858, 0x96FC, 0x6859, 0x96FD, 0x685A,\n\t0x96FE, 0x685B, 0x9740, 0x685C, 0x9741, 0x685D, 0x9742, 0x685E,\n\t0x9743, 0x685F, 0x9744, 0x686A, 0x9745, 0x686C, 0x9746, 0x686D,\n\t0x9747, 0x686E, 0x9748, 0x686F, 0x9749, 0x6870, 0x974A, 0x6871,\n\t0x974B, 0x6872, 0x974C, 0x6873, 0x974D, 0x6875, 0x974E, 0x6878,\n\t0x974F, 0x6879, 0x9750, 0x687A, 0x9751, 0x687B, 0x9752, 0x687C,\n\t0x9753, 0x687D, 0x9754, 0x687E, 0x9755, 0x687F, 0x9756, 0x6880,\n\t0x9757, 0x6882, 0x9758, 0x6884, 0x9759, 0x6887, 0x975A, 0x6888,\n\t0x975B, 0x6889, 0x975C, 0x688A, 0x975D, 0x688B, 0x975E, 0x688C,\n\t0x975F, 0x688D, 0x9760, 0x688E, 0x9761, 0x6890, 0x9762, 0x6891,\n\t0x9763, 0x6892, 0x9764, 0x6894, 0x9765, 0x6895, 0x9766, 0x6896,\n\t0x9767, 0x6898, 0x9768, 0x6899, 0x9769, 0x689A, 0x976A, 0x689B,\n\t0x976B, 0x689C, 0x976C, 0x689D, 0x976D, 0x689E, 0x976E, 0x689F,\n\t0x976F, 0x68A0, 0x9770, 0x68A1, 0x9771, 0x68A3, 0x9772, 0x68A4,\n\t0x9773, 0x68A5, 0x9774, 0x68A9, 0x9775, 0x68AA, 0x9776, 0x68AB,\n\t0x9777, 0x68AC, 0x9778, 0x68AE, 0x9779, 0x68B1, 0x977A, 0x68B2,\n\t0x977B, 0x68B4, 0x977C, 0x68B6, 0x977D, 0x68B7, 0x977E, 0x68B8,\n\t0x9780, 0x68B9, 0x9781, 0x68BA, 0x9782, 0x68BB, 0x9783, 0x68BC,\n\t0x9784, 0x68BD, 0x9785, 0x68BE, 0x9786, 0x68BF, 0x9787, 0x68C1,\n\t0x9788, 0x68C3, 0x9789, 0x68C4, 0x978A, 0x68C5, 0x978B, 0x68C6,\n\t0x978C, 0x68C7, 0x978D, 0x68C8, 0x978E, 0x68CA, 0x978F, 0x68CC,\n\t0x9790, 0x68CE, 0x9791, 0x68CF, 0x9792, 0x68D0, 0x9793, 0x68D1,\n\t0x9794, 0x68D3, 0x9795, 0x68D4, 0x9796, 0x68D6, 0x9797, 0x68D7,\n\t0x9798, 0x68D9, 0x9799, 0x68DB, 0x979A, 0x68DC, 0x979B, 0x68DD,\n\t0x979C, 0x68DE, 0x979D, 0x68DF, 0x979E, 0x68E1, 0x979F, 0x68E2,\n\t0x97A0, 0x68E4, 0x97A1, 0x68E5, 0x97A2, 0x68E6, 0x97A3, 0x68E7,\n\t0x97A4, 0x68E8, 0x97A5, 0x68E9, 0x97A6, 0x68EA, 0x97A7, 0x68EB,\n\t0x97A8, 0x68EC, 0x97A9, 0x68ED, 0x97AA, 0x68EF, 0x97AB, 0x68F2,\n\t0x97AC, 0x68F3, 0x97AD, 0x68F4, 0x97AE, 0x68F6, 0x97AF, 0x68F7,\n\t0x97B0, 0x68F8, 0x97B1, 0x68FB, 0x97B2, 0x68FD, 0x97B3, 0x68FE,\n\t0x97B4, 0x68FF, 0x97B5, 0x6900, 0x97B6, 0x6902, 0x97B7, 0x6903,\n\t0x97B8, 0x6904, 0x97B9, 0x6906, 0x97BA, 0x6907, 0x97BB, 0x6908,\n\t0x97BC, 0x6909, 0x97BD, 0x690A, 0x97BE, 0x690C, 0x97BF, 0x690F,\n\t0x97C0, 0x6911, 0x97C1, 0x6913, 0x97C2, 0x6914, 0x97C3, 0x6915,\n\t0x97C4, 0x6916, 0x97C5, 0x6917, 0x97C6, 0x6918, 0x97C7, 0x6919,\n\t0x97C8, 0x691A, 0x97C9, 0x691B, 0x97CA, 0x691C, 0x97CB, 0x691D,\n\t0x97CC, 0x691E, 0x97CD, 0x6921, 0x97CE, 0x6922, 0x97CF, 0x6923,\n\t0x97D0, 0x6925, 0x97D1, 0x6926, 0x97D2, 0x6927, 0x97D3, 0x6928,\n\t0x97D4, 0x6929, 0x97D5, 0x692A, 0x97D6, 0x692B, 0x97D7, 0x692C,\n\t0x97D8, 0x692E, 0x97D9, 0x692F, 0x97DA, 0x6931, 0x97DB, 0x6932,\n\t0x97DC, 0x6933, 0x97DD, 0x6935, 0x97DE, 0x6936, 0x97DF, 0x6937,\n\t0x97E0, 0x6938, 0x97E1, 0x693A, 0x97E2, 0x693B, 0x97E3, 0x693C,\n\t0x97E4, 0x693E, 0x97E5, 0x6940, 0x97E6, 0x6941, 0x97E7, 0x6943,\n\t0x97E8, 0x6944, 0x97E9, 0x6945, 0x97EA, 0x6946, 0x97EB, 0x6947,\n\t0x97EC, 0x6948, 0x97ED, 0x6949, 0x97EE, 0x694A, 0x97EF, 0x694B,\n\t0x97F0, 0x694C, 0x97F1, 0x694D, 0x97F2, 0x694E, 0x97F3, 0x694F,\n\t0x97F4, 0x6950, 0x97F5, 0x6951, 0x97F6, 0x6952, 0x97F7, 0x6953,\n\t0x97F8, 0x6955, 0x97F9, 0x6956, 0x97FA, 0x6958, 0x97FB, 0x6959,\n\t0x97FC, 0x695B, 0x97FD, 0x695C, 0x97FE, 0x695F, 0x9840, 0x6961,\n\t0x9841, 0x6962, 0x9842, 0x6964, 0x9843, 0x6965, 0x9844, 0x6967,\n\t0x9845, 0x6968, 0x9846, 0x6969, 0x9847, 0x696A, 0x9848, 0x696C,\n\t0x9849, 0x696D, 0x984A, 0x696F, 0x984B, 0x6970, 0x984C, 0x6972,\n\t0x984D, 0x6973, 0x984E, 0x6974, 0x984F, 0x6975, 0x9850, 0x6976,\n\t0x9851, 0x697A, 0x9852, 0x697B, 0x9853, 0x697D, 0x9854, 0x697E,\n\t0x9855, 0x697F, 0x9856, 0x6981, 0x9857, 0x6983, 0x9858, 0x6985,\n\t0x9859, 0x698A, 0x985A, 0x698B, 0x985B, 0x698C, 0x985C, 0x698E,\n\t0x985D, 0x698F, 0x985E, 0x6990, 0x985F, 0x6991, 0x9860, 0x6992,\n\t0x9861, 0x6993, 0x9862, 0x6996, 0x9863, 0x6997, 0x9864, 0x6999,\n\t0x9865, 0x699A, 0x9866, 0x699D, 0x9867, 0x699E, 0x9868, 0x699F,\n\t0x9869, 0x69A0, 0x986A, 0x69A1, 0x986B, 0x69A2, 0x986C, 0x69A3,\n\t0x986D, 0x69A4, 0x986E, 0x69A5, 0x986F, 0x69A6, 0x9870, 0x69A9,\n\t0x9871, 0x69AA, 0x9872, 0x69AC, 0x9873, 0x69AE, 0x9874, 0x69AF,\n\t0x9875, 0x69B0, 0x9876, 0x69B2, 0x9877, 0x69B3, 0x9878, 0x69B5,\n\t0x9879, 0x69B6, 0x987A, 0x69B8, 0x987B, 0x69B9, 0x987C, 0x69BA,\n\t0x987D, 0x69BC, 0x987E, 0x69BD, 0x9880, 0x69BE, 0x9881, 0x69BF,\n\t0x9882, 0x69C0, 0x9883, 0x69C2, 0x9884, 0x69C3, 0x9885, 0x69C4,\n\t0x9886, 0x69C5, 0x9887, 0x69C6, 0x9888, 0x69C7, 0x9889, 0x69C8,\n\t0x988A, 0x69C9, 0x988B, 0x69CB, 0x988C, 0x69CD, 0x988D, 0x69CF,\n\t0x988E, 0x69D1, 0x988F, 0x69D2, 0x9890, 0x69D3, 0x9891, 0x69D5,\n\t0x9892, 0x69D6, 0x9893, 0x69D7, 0x9894, 0x69D8, 0x9895, 0x69D9,\n\t0x9896, 0x69DA, 0x9897, 0x69DC, 0x9898, 0x69DD, 0x9899, 0x69DE,\n\t0x989A, 0x69E1, 0x989B, 0x69E2, 0x989C, 0x69E3, 0x989D, 0x69E4,\n\t0x989E, 0x69E5, 0x989F, 0x69E6, 0x98A0, 0x69E7, 0x98A1, 0x69E8,\n\t0x98A2, 0x69E9, 0x98A3, 0x69EA, 0x98A4, 0x69EB, 0x98A5, 0x69EC,\n\t0x98A6, 0x69EE, 0x98A7, 0x69EF, 0x98A8, 0x69F0, 0x98A9, 0x69F1,\n\t0x98AA, 0x69F3, 0x98AB, 0x69F4, 0x98AC, 0x69F5, 0x98AD, 0x69F6,\n\t0x98AE, 0x69F7, 0x98AF, 0x69F8, 0x98B0, 0x69F9, 0x98B1, 0x69FA,\n\t0x98B2, 0x69FB, 0x98B3, 0x69FC, 0x98B4, 0x69FE, 0x98B5, 0x6A00,\n\t0x98B6, 0x6A01, 0x98B7, 0x6A02, 0x98B8, 0x6A03, 0x98B9, 0x6A04,\n\t0x98BA, 0x6A05, 0x98BB, 0x6A06, 0x98BC, 0x6A07, 0x98BD, 0x6A08,\n\t0x98BE, 0x6A09, 0x98BF, 0x6A0B, 0x98C0, 0x6A0C, 0x98C1, 0x6A0D,\n\t0x98C2, 0x6A0E, 0x98C3, 0x6A0F, 0x98C4, 0x6A10, 0x98C5, 0x6A11,\n\t0x98C6, 0x6A12, 0x98C7, 0x6A13, 0x98C8, 0x6A14, 0x98C9, 0x6A15,\n\t0x98CA, 0x6A16, 0x98CB, 0x6A19, 0x98CC, 0x6A1A, 0x98CD, 0x6A1B,\n\t0x98CE, 0x6A1C, 0x98CF, 0x6A1D, 0x98D0, 0x6A1E, 0x98D1, 0x6A20,\n\t0x98D2, 0x6A22, 0x98D3, 0x6A23, 0x98D4, 0x6A24, 0x98D5, 0x6A25,\n\t0x98D6, 0x6A26, 0x98D7, 0x6A27, 0x98D8, 0x6A29, 0x98D9, 0x6A2B,\n\t0x98DA, 0x6A2C, 0x98DB, 0x6A2D, 0x98DC, 0x6A2E, 0x98DD, 0x6A30,\n\t0x98DE, 0x6A32, 0x98DF, 0x6A33, 0x98E0, 0x6A34, 0x98E1, 0x6A36,\n\t0x98E2, 0x6A37, 0x98E3, 0x6A38, 0x98E4, 0x6A39, 0x98E5, 0x6A3A,\n\t0x98E6, 0x6A3B, 0x98E7, 0x6A3C, 0x98E8, 0x6A3F, 0x98E9, 0x6A40,\n\t0x98EA, 0x6A41, 0x98EB, 0x6A42, 0x98EC, 0x6A43, 0x98ED, 0x6A45,\n\t0x98EE, 0x6A46, 0x98EF, 0x6A48, 0x98F0, 0x6A49, 0x98F1, 0x6A4A,\n\t0x98F2, 0x6A4B, 0x98F3, 0x6A4C, 0x98F4, 0x6A4D, 0x98F5, 0x6A4E,\n\t0x98F6, 0x6A4F, 0x98F7, 0x6A51, 0x98F8, 0x6A52, 0x98F9, 0x6A53,\n\t0x98FA, 0x6A54, 0x98FB, 0x6A55, 0x98FC, 0x6A56, 0x98FD, 0x6A57,\n\t0x98FE, 0x6A5A, 0x9940, 0x6A5C, 0x9941, 0x6A5D, 0x9942, 0x6A5E,\n\t0x9943, 0x6A5F, 0x9944, 0x6A60, 0x9945, 0x6A62, 0x9946, 0x6A63,\n\t0x9947, 0x6A64, 0x9948, 0x6A66, 0x9949, 0x6A67, 0x994A, 0x6A68,\n\t0x994B, 0x6A69, 0x994C, 0x6A6A, 0x994D, 0x6A6B, 0x994E, 0x6A6C,\n\t0x994F, 0x6A6D, 0x9950, 0x6A6E, 0x9951, 0x6A6F, 0x9952, 0x6A70,\n\t0x9953, 0x6A72, 0x9954, 0x6A73, 0x9955, 0x6A74, 0x9956, 0x6A75,\n\t0x9957, 0x6A76, 0x9958, 0x6A77, 0x9959, 0x6A78, 0x995A, 0x6A7A,\n\t0x995B, 0x6A7B, 0x995C, 0x6A7D, 0x995D, 0x6A7E, 0x995E, 0x6A7F,\n\t0x995F, 0x6A81, 0x9960, 0x6A82, 0x9961, 0x6A83, 0x9962, 0x6A85,\n\t0x9963, 0x6A86, 0x9964, 0x6A87, 0x9965, 0x6A88, 0x9966, 0x6A89,\n\t0x9967, 0x6A8A, 0x9968, 0x6A8B, 0x9969, 0x6A8C, 0x996A, 0x6A8D,\n\t0x996B, 0x6A8F, 0x996C, 0x6A92, 0x996D, 0x6A93, 0x996E, 0x6A94,\n\t0x996F, 0x6A95, 0x9970, 0x6A96, 0x9971, 0x6A98, 0x9972, 0x6A99,\n\t0x9973, 0x6A9A, 0x9974, 0x6A9B, 0x9975, 0x6A9C, 0x9976, 0x6A9D,\n\t0x9977, 0x6A9E, 0x9978, 0x6A9F, 0x9979, 0x6AA1, 0x997A, 0x6AA2,\n\t0x997B, 0x6AA3, 0x997C, 0x6AA4, 0x997D, 0x6AA5, 0x997E, 0x6AA6,\n\t0x9980, 0x6AA7, 0x9981, 0x6AA8, 0x9982, 0x6AAA, 0x9983, 0x6AAD,\n\t0x9984, 0x6AAE, 0x9985, 0x6AAF, 0x9986, 0x6AB0, 0x9987, 0x6AB1,\n\t0x9988, 0x6AB2, 0x9989, 0x6AB3, 0x998A, 0x6AB4, 0x998B, 0x6AB5,\n\t0x998C, 0x6AB6, 0x998D, 0x6AB7, 0x998E, 0x6AB8, 0x998F, 0x6AB9,\n\t0x9990, 0x6ABA, 0x9991, 0x6ABB, 0x9992, 0x6ABC, 0x9993, 0x6ABD,\n\t0x9994, 0x6ABE, 0x9995, 0x6ABF, 0x9996, 0x6AC0, 0x9997, 0x6AC1,\n\t0x9998, 0x6AC2, 0x9999, 0x6AC3, 0x999A, 0x6AC4, 0x999B, 0x6AC5,\n\t0x999C, 0x6AC6, 0x999D, 0x6AC7, 0x999E, 0x6AC8, 0x999F, 0x6AC9,\n\t0x99A0, 0x6ACA, 0x99A1, 0x6ACB, 0x99A2, 0x6ACC, 0x99A3, 0x6ACD,\n\t0x99A4, 0x6ACE, 0x99A5, 0x6ACF, 0x99A6, 0x6AD0, 0x99A7, 0x6AD1,\n\t0x99A8, 0x6AD2, 0x99A9, 0x6AD3, 0x99AA, 0x6AD4, 0x99AB, 0x6AD5,\n\t0x99AC, 0x6AD6, 0x99AD, 0x6AD7, 0x99AE, 0x6AD8, 0x99AF, 0x6AD9,\n\t0x99B0, 0x6ADA, 0x99B1, 0x6ADB, 0x99B2, 0x6ADC, 0x99B3, 0x6ADD,\n\t0x99B4, 0x6ADE, 0x99B5, 0x6ADF, 0x99B6, 0x6AE0, 0x99B7, 0x6AE1,\n\t0x99B8, 0x6AE2, 0x99B9, 0x6AE3, 0x99BA, 0x6AE4, 0x99BB, 0x6AE5,\n\t0x99BC, 0x6AE6, 0x99BD, 0x6AE7, 0x99BE, 0x6AE8, 0x99BF, 0x6AE9,\n\t0x99C0, 0x6AEA, 0x99C1, 0x6AEB, 0x99C2, 0x6AEC, 0x99C3, 0x6AED,\n\t0x99C4, 0x6AEE, 0x99C5, 0x6AEF, 0x99C6, 0x6AF0, 0x99C7, 0x6AF1,\n\t0x99C8, 0x6AF2, 0x99C9, 0x6AF3, 0x99CA, 0x6AF4, 0x99CB, 0x6AF5,\n\t0x99CC, 0x6AF6, 0x99CD, 0x6AF7, 0x99CE, 0x6AF8, 0x99CF, 0x6AF9,\n\t0x99D0, 0x6AFA, 0x99D1, 0x6AFB, 0x99D2, 0x6AFC, 0x99D3, 0x6AFD,\n\t0x99D4, 0x6AFE, 0x99D5, 0x6AFF, 0x99D6, 0x6B00, 0x99D7, 0x6B01,\n\t0x99D8, 0x6B02, 0x99D9, 0x6B03, 0x99DA, 0x6B04, 0x99DB, 0x6B05,\n\t0x99DC, 0x6B06, 0x99DD, 0x6B07, 0x99DE, 0x6B08, 0x99DF, 0x6B09,\n\t0x99E0, 0x6B0A, 0x99E1, 0x6B0B, 0x99E2, 0x6B0C, 0x99E3, 0x6B0D,\n\t0x99E4, 0x6B0E, 0x99E5, 0x6B0F, 0x99E6, 0x6B10, 0x99E7, 0x6B11,\n\t0x99E8, 0x6B12, 0x99E9, 0x6B13, 0x99EA, 0x6B14, 0x99EB, 0x6B15,\n\t0x99EC, 0x6B16, 0x99ED, 0x6B17, 0x99EE, 0x6B18, 0x99EF, 0x6B19,\n\t0x99F0, 0x6B1A, 0x99F1, 0x6B1B, 0x99F2, 0x6B1C, 0x99F3, 0x6B1D,\n\t0x99F4, 0x6B1E, 0x99F5, 0x6B1F, 0x99F6, 0x6B25, 0x99F7, 0x6B26,\n\t0x99F8, 0x6B28, 0x99F9, 0x6B29, 0x99FA, 0x6B2A, 0x99FB, 0x6B2B,\n\t0x99FC, 0x6B2C, 0x99FD, 0x6B2D, 0x99FE, 0x6B2E, 0x9A40, 0x6B2F,\n\t0x9A41, 0x6B30, 0x9A42, 0x6B31, 0x9A43, 0x6B33, 0x9A44, 0x6B34,\n\t0x9A45, 0x6B35, 0x9A46, 0x6B36, 0x9A47, 0x6B38, 0x9A48, 0x6B3B,\n\t0x9A49, 0x6B3C, 0x9A4A, 0x6B3D, 0x9A4B, 0x6B3F, 0x9A4C, 0x6B40,\n\t0x9A4D, 0x6B41, 0x9A4E, 0x6B42, 0x9A4F, 0x6B44, 0x9A50, 0x6B45,\n\t0x9A51, 0x6B48, 0x9A52, 0x6B4A, 0x9A53, 0x6B4B, 0x9A54, 0x6B4D,\n\t0x9A55, 0x6B4E, 0x9A56, 0x6B4F, 0x9A57, 0x6B50, 0x9A58, 0x6B51,\n\t0x9A59, 0x6B52, 0x9A5A, 0x6B53, 0x9A5B, 0x6B54, 0x9A5C, 0x6B55,\n\t0x9A5D, 0x6B56, 0x9A5E, 0x6B57, 0x9A5F, 0x6B58, 0x9A60, 0x6B5A,\n\t0x9A61, 0x6B5B, 0x9A62, 0x6B5C, 0x9A63, 0x6B5D, 0x9A64, 0x6B5E,\n\t0x9A65, 0x6B5F, 0x9A66, 0x6B60, 0x9A67, 0x6B61, 0x9A68, 0x6B68,\n\t0x9A69, 0x6B69, 0x9A6A, 0x6B6B, 0x9A6B, 0x6B6C, 0x9A6C, 0x6B6D,\n\t0x9A6D, 0x6B6E, 0x9A6E, 0x6B6F, 0x9A6F, 0x6B70, 0x9A70, 0x6B71,\n\t0x9A71, 0x6B72, 0x9A72, 0x6B73, 0x9A73, 0x6B74, 0x9A74, 0x6B75,\n\t0x9A75, 0x6B76, 0x9A76, 0x6B77, 0x9A77, 0x6B78, 0x9A78, 0x6B7A,\n\t0x9A79, 0x6B7D, 0x9A7A, 0x6B7E, 0x9A7B, 0x6B7F, 0x9A7C, 0x6B80,\n\t0x9A7D, 0x6B85, 0x9A7E, 0x6B88, 0x9A80, 0x6B8C, 0x9A81, 0x6B8E,\n\t0x9A82, 0x6B8F, 0x9A83, 0x6B90, 0x9A84, 0x6B91, 0x9A85, 0x6B94,\n\t0x9A86, 0x6B95, 0x9A87, 0x6B97, 0x9A88, 0x6B98, 0x9A89, 0x6B99,\n\t0x9A8A, 0x6B9C, 0x9A8B, 0x6B9D, 0x9A8C, 0x6B9E, 0x9A8D, 0x6B9F,\n\t0x9A8E, 0x6BA0, 0x9A8F, 0x6BA2, 0x9A90, 0x6BA3, 0x9A91, 0x6BA4,\n\t0x9A92, 0x6BA5, 0x9A93, 0x6BA6, 0x9A94, 0x6BA7, 0x9A95, 0x6BA8,\n\t0x9A96, 0x6BA9, 0x9A97, 0x6BAB, 0x9A98, 0x6BAC, 0x9A99, 0x6BAD,\n\t0x9A9A, 0x6BAE, 0x9A9B, 0x6BAF, 0x9A9C, 0x6BB0, 0x9A9D, 0x6BB1,\n\t0x9A9E, 0x6BB2, 0x9A9F, 0x6BB6, 0x9AA0, 0x6BB8, 0x9AA1, 0x6BB9,\n\t0x9AA2, 0x6BBA, 0x9AA3, 0x6BBB, 0x9AA4, 0x6BBC, 0x9AA5, 0x6BBD,\n\t0x9AA6, 0x6BBE, 0x9AA7, 0x6BC0, 0x9AA8, 0x6BC3, 0x9AA9, 0x6BC4,\n\t0x9AAA, 0x6BC6, 0x9AAB, 0x6BC7, 0x9AAC, 0x6BC8, 0x9AAD, 0x6BC9,\n\t0x9AAE, 0x6BCA, 0x9AAF, 0x6BCC, 0x9AB0, 0x6BCE, 0x9AB1, 0x6BD0,\n\t0x9AB2, 0x6BD1, 0x9AB3, 0x6BD8, 0x9AB4, 0x6BDA, 0x9AB5, 0x6BDC,\n\t0x9AB6, 0x6BDD, 0x9AB7, 0x6BDE, 0x9AB8, 0x6BDF, 0x9AB9, 0x6BE0,\n\t0x9ABA, 0x6BE2, 0x9ABB, 0x6BE3, 0x9ABC, 0x6BE4, 0x9ABD, 0x6BE5,\n\t0x9ABE, 0x6BE6, 0x9ABF, 0x6BE7, 0x9AC0, 0x6BE8, 0x9AC1, 0x6BE9,\n\t0x9AC2, 0x6BEC, 0x9AC3, 0x6BED, 0x9AC4, 0x6BEE, 0x9AC5, 0x6BF0,\n\t0x9AC6, 0x6BF1, 0x9AC7, 0x6BF2, 0x9AC8, 0x6BF4, 0x9AC9, 0x6BF6,\n\t0x9ACA, 0x6BF7, 0x9ACB, 0x6BF8, 0x9ACC, 0x6BFA, 0x9ACD, 0x6BFB,\n\t0x9ACE, 0x6BFC, 0x9ACF, 0x6BFE, 0x9AD0, 0x6BFF, 0x9AD1, 0x6C00,\n\t0x9AD2, 0x6C01, 0x9AD3, 0x6C02, 0x9AD4, 0x6C03, 0x9AD5, 0x6C04,\n\t0x9AD6, 0x6C08, 0x9AD7, 0x6C09, 0x9AD8, 0x6C0A, 0x9AD9, 0x6C0B,\n\t0x9ADA, 0x6C0C, 0x9ADB, 0x6C0E, 0x9ADC, 0x6C12, 0x9ADD, 0x6C17,\n\t0x9ADE, 0x6C1C, 0x9ADF, 0x6C1D, 0x9AE0, 0x6C1E, 0x9AE1, 0x6C20,\n\t0x9AE2, 0x6C23, 0x9AE3, 0x6C25, 0x9AE4, 0x6C2B, 0x9AE5, 0x6C2C,\n\t0x9AE6, 0x6C2D, 0x9AE7, 0x6C31, 0x9AE8, 0x6C33, 0x9AE9, 0x6C36,\n\t0x9AEA, 0x6C37, 0x9AEB, 0x6C39, 0x9AEC, 0x6C3A, 0x9AED, 0x6C3B,\n\t0x9AEE, 0x6C3C, 0x9AEF, 0x6C3E, 0x9AF0, 0x6C3F, 0x9AF1, 0x6C43,\n\t0x9AF2, 0x6C44, 0x9AF3, 0x6C45, 0x9AF4, 0x6C48, 0x9AF5, 0x6C4B,\n\t0x9AF6, 0x6C4C, 0x9AF7, 0x6C4D, 0x9AF8, 0x6C4E, 0x9AF9, 0x6C4F,\n\t0x9AFA, 0x6C51, 0x9AFB, 0x6C52, 0x9AFC, 0x6C53, 0x9AFD, 0x6C56,\n\t0x9AFE, 0x6C58, 0x9B40, 0x6C59, 0x9B41, 0x6C5A, 0x9B42, 0x6C62,\n\t0x9B43, 0x6C63, 0x9B44, 0x6C65, 0x9B45, 0x6C66, 0x9B46, 0x6C67,\n\t0x9B47, 0x6C6B, 0x9B48, 0x6C6C, 0x9B49, 0x6C6D, 0x9B4A, 0x6C6E,\n\t0x9B4B, 0x6C6F, 0x9B4C, 0x6C71, 0x9B4D, 0x6C73, 0x9B4E, 0x6C75,\n\t0x9B4F, 0x6C77, 0x9B50, 0x6C78, 0x9B51, 0x6C7A, 0x9B52, 0x6C7B,\n\t0x9B53, 0x6C7C, 0x9B54, 0x6C7F, 0x9B55, 0x6C80, 0x9B56, 0x6C84,\n\t0x9B57, 0x6C87, 0x9B58, 0x6C8A, 0x9B59, 0x6C8B, 0x9B5A, 0x6C8D,\n\t0x9B5B, 0x6C8E, 0x9B5C, 0x6C91, 0x9B5D, 0x6C92, 0x9B5E, 0x6C95,\n\t0x9B5F, 0x6C96, 0x9B60, 0x6C97, 0x9B61, 0x6C98, 0x9B62, 0x6C9A,\n\t0x9B63, 0x6C9C, 0x9B64, 0x6C9D, 0x9B65, 0x6C9E, 0x9B66, 0x6CA0,\n\t0x9B67, 0x6CA2, 0x9B68, 0x6CA8, 0x9B69, 0x6CAC, 0x9B6A, 0x6CAF,\n\t0x9B6B, 0x6CB0, 0x9B6C, 0x6CB4, 0x9B6D, 0x6CB5, 0x9B6E, 0x6CB6,\n\t0x9B6F, 0x6CB7, 0x9B70, 0x6CBA, 0x9B71, 0x6CC0, 0x9B72, 0x6CC1,\n\t0x9B73, 0x6CC2, 0x9B74, 0x6CC3, 0x9B75, 0x6CC6, 0x9B76, 0x6CC7,\n\t0x9B77, 0x6CC8, 0x9B78, 0x6CCB, 0x9B79, 0x6CCD, 0x9B7A, 0x6CCE,\n\t0x9B7B, 0x6CCF, 0x9B7C, 0x6CD1, 0x9B7D, 0x6CD2, 0x9B7E, 0x6CD8,\n\t0x9B80, 0x6CD9, 0x9B81, 0x6CDA, 0x9B82, 0x6CDC, 0x9B83, 0x6CDD,\n\t0x9B84, 0x6CDF, 0x9B85, 0x6CE4, 0x9B86, 0x6CE6, 0x9B87, 0x6CE7,\n\t0x9B88, 0x6CE9, 0x9B89, 0x6CEC, 0x9B8A, 0x6CED, 0x9B8B, 0x6CF2,\n\t0x9B8C, 0x6CF4, 0x9B8D, 0x6CF9, 0x9B8E, 0x6CFF, 0x9B8F, 0x6D00,\n\t0x9B90, 0x6D02, 0x9B91, 0x6D03, 0x9B92, 0x6D05, 0x9B93, 0x6D06,\n\t0x9B94, 0x6D08, 0x9B95, 0x6D09, 0x9B96, 0x6D0A, 0x9B97, 0x6D0D,\n\t0x9B98, 0x6D0F, 0x9B99, 0x6D10, 0x9B9A, 0x6D11, 0x9B9B, 0x6D13,\n\t0x9B9C, 0x6D14, 0x9B9D, 0x6D15, 0x9B9E, 0x6D16, 0x9B9F, 0x6D18,\n\t0x9BA0, 0x6D1C, 0x9BA1, 0x6D1D, 0x9BA2, 0x6D1F, 0x9BA3, 0x6D20,\n\t0x9BA4, 0x6D21, 0x9BA5, 0x6D22, 0x9BA6, 0x6D23, 0x9BA7, 0x6D24,\n\t0x9BA8, 0x6D26, 0x9BA9, 0x6D28, 0x9BAA, 0x6D29, 0x9BAB, 0x6D2C,\n\t0x9BAC, 0x6D2D, 0x9BAD, 0x6D2F, 0x9BAE, 0x6D30, 0x9BAF, 0x6D34,\n\t0x9BB0, 0x6D36, 0x9BB1, 0x6D37, 0x9BB2, 0x6D38, 0x9BB3, 0x6D3A,\n\t0x9BB4, 0x6D3F, 0x9BB5, 0x6D40, 0x9BB6, 0x6D42, 0x9BB7, 0x6D44,\n\t0x9BB8, 0x6D49, 0x9BB9, 0x6D4C, 0x9BBA, 0x6D50, 0x9BBB, 0x6D55,\n\t0x9BBC, 0x6D56, 0x9BBD, 0x6D57, 0x9BBE, 0x6D58, 0x9BBF, 0x6D5B,\n\t0x9BC0, 0x6D5D, 0x9BC1, 0x6D5F, 0x9BC2, 0x6D61, 0x9BC3, 0x6D62,\n\t0x9BC4, 0x6D64, 0x9BC5, 0x6D65, 0x9BC6, 0x6D67, 0x9BC7, 0x6D68,\n\t0x9BC8, 0x6D6B, 0x9BC9, 0x6D6C, 0x9BCA, 0x6D6D, 0x9BCB, 0x6D70,\n\t0x9BCC, 0x6D71, 0x9BCD, 0x6D72, 0x9BCE, 0x6D73, 0x9BCF, 0x6D75,\n\t0x9BD0, 0x6D76, 0x9BD1, 0x6D79, 0x9BD2, 0x6D7A, 0x9BD3, 0x6D7B,\n\t0x9BD4, 0x6D7D, 0x9BD5, 0x6D7E, 0x9BD6, 0x6D7F, 0x9BD7, 0x6D80,\n\t0x9BD8, 0x6D81, 0x9BD9, 0x6D83, 0x9BDA, 0x6D84, 0x9BDB, 0x6D86,\n\t0x9BDC, 0x6D87, 0x9BDD, 0x6D8A, 0x9BDE, 0x6D8B, 0x9BDF, 0x6D8D,\n\t0x9BE0, 0x6D8F, 0x9BE1, 0x6D90, 0x9BE2, 0x6D92, 0x9BE3, 0x6D96,\n\t0x9BE4, 0x6D97, 0x9BE5, 0x6D98, 0x9BE6, 0x6D99, 0x9BE7, 0x6D9A,\n\t0x9BE8, 0x6D9C, 0x9BE9, 0x6DA2, 0x9BEA, 0x6DA5, 0x9BEB, 0x6DAC,\n\t0x9BEC, 0x6DAD, 0x9BED, 0x6DB0, 0x9BEE, 0x6DB1, 0x9BEF, 0x6DB3,\n\t0x9BF0, 0x6DB4, 0x9BF1, 0x6DB6, 0x9BF2, 0x6DB7, 0x9BF3, 0x6DB9,\n\t0x9BF4, 0x6DBA, 0x9BF5, 0x6DBB, 0x9BF6, 0x6DBC, 0x9BF7, 0x6DBD,\n\t0x9BF8, 0x6DBE, 0x9BF9, 0x6DC1, 0x9BFA, 0x6DC2, 0x9BFB, 0x6DC3,\n\t0x9BFC, 0x6DC8, 0x9BFD, 0x6DC9, 0x9BFE, 0x6DCA, 0x9C40, 0x6DCD,\n\t0x9C41, 0x6DCE, 0x9C42, 0x6DCF, 0x9C43, 0x6DD0, 0x9C44, 0x6DD2,\n\t0x9C45, 0x6DD3, 0x9C46, 0x6DD4, 0x9C47, 0x6DD5, 0x9C48, 0x6DD7,\n\t0x9C49, 0x6DDA, 0x9C4A, 0x6DDB, 0x9C4B, 0x6DDC, 0x9C4C, 0x6DDF,\n\t0x9C4D, 0x6DE2, 0x9C4E, 0x6DE3, 0x9C4F, 0x6DE5, 0x9C50, 0x6DE7,\n\t0x9C51, 0x6DE8, 0x9C52, 0x6DE9, 0x9C53, 0x6DEA, 0x9C54, 0x6DED,\n\t0x9C55, 0x6DEF, 0x9C56, 0x6DF0, 0x9C57, 0x6DF2, 0x9C58, 0x6DF4,\n\t0x9C59, 0x6DF5, 0x9C5A, 0x6DF6, 0x9C5B, 0x6DF8, 0x9C5C, 0x6DFA,\n\t0x9C5D, 0x6DFD, 0x9C5E, 0x6DFE, 0x9C5F, 0x6DFF, 0x9C60, 0x6E00,\n\t0x9C61, 0x6E01, 0x9C62, 0x6E02, 0x9C63, 0x6E03, 0x9C64, 0x6E04,\n\t0x9C65, 0x6E06, 0x9C66, 0x6E07, 0x9C67, 0x6E08, 0x9C68, 0x6E09,\n\t0x9C69, 0x6E0B, 0x9C6A, 0x6E0F, 0x9C6B, 0x6E12, 0x9C6C, 0x6E13,\n\t0x9C6D, 0x6E15, 0x9C6E, 0x6E18, 0x9C6F, 0x6E19, 0x9C70, 0x6E1B,\n\t0x9C71, 0x6E1C, 0x9C72, 0x6E1E, 0x9C73, 0x6E1F, 0x9C74, 0x6E22,\n\t0x9C75, 0x6E26, 0x9C76, 0x6E27, 0x9C77, 0x6E28, 0x9C78, 0x6E2A,\n\t0x9C79, 0x6E2C, 0x9C7A, 0x6E2E, 0x9C7B, 0x6E30, 0x9C7C, 0x6E31,\n\t0x9C7D, 0x6E33, 0x9C7E, 0x6E35, 0x9C80, 0x6E36, 0x9C81, 0x6E37,\n\t0x9C82, 0x6E39, 0x9C83, 0x6E3B, 0x9C84, 0x6E3C, 0x9C85, 0x6E3D,\n\t0x9C86, 0x6E3E, 0x9C87, 0x6E3F, 0x9C88, 0x6E40, 0x9C89, 0x6E41,\n\t0x9C8A, 0x6E42, 0x9C8B, 0x6E45, 0x9C8C, 0x6E46, 0x9C8D, 0x6E47,\n\t0x9C8E, 0x6E48, 0x9C8F, 0x6E49, 0x9C90, 0x6E4A, 0x9C91, 0x6E4B,\n\t0x9C92, 0x6E4C, 0x9C93, 0x6E4F, 0x9C94, 0x6E50, 0x9C95, 0x6E51,\n\t0x9C96, 0x6E52, 0x9C97, 0x6E55, 0x9C98, 0x6E57, 0x9C99, 0x6E59,\n\t0x9C9A, 0x6E5A, 0x9C9B, 0x6E5C, 0x9C9C, 0x6E5D, 0x9C9D, 0x6E5E,\n\t0x9C9E, 0x6E60, 0x9C9F, 0x6E61, 0x9CA0, 0x6E62, 0x9CA1, 0x6E63,\n\t0x9CA2, 0x6E64, 0x9CA3, 0x6E65, 0x9CA4, 0x6E66, 0x9CA5, 0x6E67,\n\t0x9CA6, 0x6E68, 0x9CA7, 0x6E69, 0x9CA8, 0x6E6A, 0x9CA9, 0x6E6C,\n\t0x9CAA, 0x6E6D, 0x9CAB, 0x6E6F, 0x9CAC, 0x6E70, 0x9CAD, 0x6E71,\n\t0x9CAE, 0x6E72, 0x9CAF, 0x6E73, 0x9CB0, 0x6E74, 0x9CB1, 0x6E75,\n\t0x9CB2, 0x6E76, 0x9CB3, 0x6E77, 0x9CB4, 0x6E78, 0x9CB5, 0x6E79,\n\t0x9CB6, 0x6E7A, 0x9CB7, 0x6E7B, 0x9CB8, 0x6E7C, 0x9CB9, 0x6E7D,\n\t0x9CBA, 0x6E80, 0x9CBB, 0x6E81, 0x9CBC, 0x6E82, 0x9CBD, 0x6E84,\n\t0x9CBE, 0x6E87, 0x9CBF, 0x6E88, 0x9CC0, 0x6E8A, 0x9CC1, 0x6E8B,\n\t0x9CC2, 0x6E8C, 0x9CC3, 0x6E8D, 0x9CC4, 0x6E8E, 0x9CC5, 0x6E91,\n\t0x9CC6, 0x6E92, 0x9CC7, 0x6E93, 0x9CC8, 0x6E94, 0x9CC9, 0x6E95,\n\t0x9CCA, 0x6E96, 0x9CCB, 0x6E97, 0x9CCC, 0x6E99, 0x9CCD, 0x6E9A,\n\t0x9CCE, 0x6E9B, 0x9CCF, 0x6E9D, 0x9CD0, 0x6E9E, 0x9CD1, 0x6EA0,\n\t0x9CD2, 0x6EA1, 0x9CD3, 0x6EA3, 0x9CD4, 0x6EA4, 0x9CD5, 0x6EA6,\n\t0x9CD6, 0x6EA8, 0x9CD7, 0x6EA9, 0x9CD8, 0x6EAB, 0x9CD9, 0x6EAC,\n\t0x9CDA, 0x6EAD, 0x9CDB, 0x6EAE, 0x9CDC, 0x6EB0, 0x9CDD, 0x6EB3,\n\t0x9CDE, 0x6EB5, 0x9CDF, 0x6EB8, 0x9CE0, 0x6EB9, 0x9CE1, 0x6EBC,\n\t0x9CE2, 0x6EBE, 0x9CE3, 0x6EBF, 0x9CE4, 0x6EC0, 0x9CE5, 0x6EC3,\n\t0x9CE6, 0x6EC4, 0x9CE7, 0x6EC5, 0x9CE8, 0x6EC6, 0x9CE9, 0x6EC8,\n\t0x9CEA, 0x6EC9, 0x9CEB, 0x6ECA, 0x9CEC, 0x6ECC, 0x9CED, 0x6ECD,\n\t0x9CEE, 0x6ECE, 0x9CEF, 0x6ED0, 0x9CF0, 0x6ED2, 0x9CF1, 0x6ED6,\n\t0x9CF2, 0x6ED8, 0x9CF3, 0x6ED9, 0x9CF4, 0x6EDB, 0x9CF5, 0x6EDC,\n\t0x9CF6, 0x6EDD, 0x9CF7, 0x6EE3, 0x9CF8, 0x6EE7, 0x9CF9, 0x6EEA,\n\t0x9CFA, 0x6EEB, 0x9CFB, 0x6EEC, 0x9CFC, 0x6EED, 0x9CFD, 0x6EEE,\n\t0x9CFE, 0x6EEF, 0x9D40, 0x6EF0, 0x9D41, 0x6EF1, 0x9D42, 0x6EF2,\n\t0x9D43, 0x6EF3, 0x9D44, 0x6EF5, 0x9D45, 0x6EF6, 0x9D46, 0x6EF7,\n\t0x9D47, 0x6EF8, 0x9D48, 0x6EFA, 0x9D49, 0x6EFB, 0x9D4A, 0x6EFC,\n\t0x9D4B, 0x6EFD, 0x9D4C, 0x6EFE, 0x9D4D, 0x6EFF, 0x9D4E, 0x6F00,\n\t0x9D4F, 0x6F01, 0x9D50, 0x6F03, 0x9D51, 0x6F04, 0x9D52, 0x6F05,\n\t0x9D53, 0x6F07, 0x9D54, 0x6F08, 0x9D55, 0x6F0A, 0x9D56, 0x6F0B,\n\t0x9D57, 0x6F0C, 0x9D58, 0x6F0D, 0x9D59, 0x6F0E, 0x9D5A, 0x6F10,\n\t0x9D5B, 0x6F11, 0x9D5C, 0x6F12, 0x9D5D, 0x6F16, 0x9D5E, 0x6F17,\n\t0x9D5F, 0x6F18, 0x9D60, 0x6F19, 0x9D61, 0x6F1A, 0x9D62, 0x6F1B,\n\t0x9D63, 0x6F1C, 0x9D64, 0x6F1D, 0x9D65, 0x6F1E, 0x9D66, 0x6F1F,\n\t0x9D67, 0x6F21, 0x9D68, 0x6F22, 0x9D69, 0x6F23, 0x9D6A, 0x6F25,\n\t0x9D6B, 0x6F26, 0x9D6C, 0x6F27, 0x9D6D, 0x6F28, 0x9D6E, 0x6F2C,\n\t0x9D6F, 0x6F2E, 0x9D70, 0x6F30, 0x9D71, 0x6F32, 0x9D72, 0x6F34,\n\t0x9D73, 0x6F35, 0x9D74, 0x6F37, 0x9D75, 0x6F38, 0x9D76, 0x6F39,\n\t0x9D77, 0x6F3A, 0x9D78, 0x6F3B, 0x9D79, 0x6F3C, 0x9D7A, 0x6F3D,\n\t0x9D7B, 0x6F3F, 0x9D7C, 0x6F40, 0x9D7D, 0x6F41, 0x9D7E, 0x6F42,\n\t0x9D80, 0x6F43, 0x9D81, 0x6F44, 0x9D82, 0x6F45, 0x9D83, 0x6F48,\n\t0x9D84, 0x6F49, 0x9D85, 0x6F4A, 0x9D86, 0x6F4C, 0x9D87, 0x6F4E,\n\t0x9D88, 0x6F4F, 0x9D89, 0x6F50, 0x9D8A, 0x6F51, 0x9D8B, 0x6F52,\n\t0x9D8C, 0x6F53, 0x9D8D, 0x6F54, 0x9D8E, 0x6F55, 0x9D8F, 0x6F56,\n\t0x9D90, 0x6F57, 0x9D91, 0x6F59, 0x9D92, 0x6F5A, 0x9D93, 0x6F5B,\n\t0x9D94, 0x6F5D, 0x9D95, 0x6F5F, 0x9D96, 0x6F60, 0x9D97, 0x6F61,\n\t0x9D98, 0x6F63, 0x9D99, 0x6F64, 0x9D9A, 0x6F65, 0x9D9B, 0x6F67,\n\t0x9D9C, 0x6F68, 0x9D9D, 0x6F69, 0x9D9E, 0x6F6A, 0x9D9F, 0x6F6B,\n\t0x9DA0, 0x6F6C, 0x9DA1, 0x6F6F, 0x9DA2, 0x6F70, 0x9DA3, 0x6F71,\n\t0x9DA4, 0x6F73, 0x9DA5, 0x6F75, 0x9DA6, 0x6F76, 0x9DA7, 0x6F77,\n\t0x9DA8, 0x6F79, 0x9DA9, 0x6F7B, 0x9DAA, 0x6F7D, 0x9DAB, 0x6F7E,\n\t0x9DAC, 0x6F7F, 0x9DAD, 0x6F80, 0x9DAE, 0x6F81, 0x9DAF, 0x6F82,\n\t0x9DB0, 0x6F83, 0x9DB1, 0x6F85, 0x9DB2, 0x6F86, 0x9DB3, 0x6F87,\n\t0x9DB4, 0x6F8A, 0x9DB5, 0x6F8B, 0x9DB6, 0x6F8F, 0x9DB7, 0x6F90,\n\t0x9DB8, 0x6F91, 0x9DB9, 0x6F92, 0x9DBA, 0x6F93, 0x9DBB, 0x6F94,\n\t0x9DBC, 0x6F95, 0x9DBD, 0x6F96, 0x9DBE, 0x6F97, 0x9DBF, 0x6F98,\n\t0x9DC0, 0x6F99, 0x9DC1, 0x6F9A, 0x9DC2, 0x6F9B, 0x9DC3, 0x6F9D,\n\t0x9DC4, 0x6F9E, 0x9DC5, 0x6F9F, 0x9DC6, 0x6FA0, 0x9DC7, 0x6FA2,\n\t0x9DC8, 0x6FA3, 0x9DC9, 0x6FA4, 0x9DCA, 0x6FA5, 0x9DCB, 0x6FA6,\n\t0x9DCC, 0x6FA8, 0x9DCD, 0x6FA9, 0x9DCE, 0x6FAA, 0x9DCF, 0x6FAB,\n\t0x9DD0, 0x6FAC, 0x9DD1, 0x6FAD, 0x9DD2, 0x6FAE, 0x9DD3, 0x6FAF,\n\t0x9DD4, 0x6FB0, 0x9DD5, 0x6FB1, 0x9DD6, 0x6FB2, 0x9DD7, 0x6FB4,\n\t0x9DD8, 0x6FB5, 0x9DD9, 0x6FB7, 0x9DDA, 0x6FB8, 0x9DDB, 0x6FBA,\n\t0x9DDC, 0x6FBB, 0x9DDD, 0x6FBC, 0x9DDE, 0x6FBD, 0x9DDF, 0x6FBE,\n\t0x9DE0, 0x6FBF, 0x9DE1, 0x6FC1, 0x9DE2, 0x6FC3, 0x9DE3, 0x6FC4,\n\t0x9DE4, 0x6FC5, 0x9DE5, 0x6FC6, 0x9DE6, 0x6FC7, 0x9DE7, 0x6FC8,\n\t0x9DE8, 0x6FCA, 0x9DE9, 0x6FCB, 0x9DEA, 0x6FCC, 0x9DEB, 0x6FCD,\n\t0x9DEC, 0x6FCE, 0x9DED, 0x6FCF, 0x9DEE, 0x6FD0, 0x9DEF, 0x6FD3,\n\t0x9DF0, 0x6FD4, 0x9DF1, 0x6FD5, 0x9DF2, 0x6FD6, 0x9DF3, 0x6FD7,\n\t0x9DF4, 0x6FD8, 0x9DF5, 0x6FD9, 0x9DF6, 0x6FDA, 0x9DF7, 0x6FDB,\n\t0x9DF8, 0x6FDC, 0x9DF9, 0x6FDD, 0x9DFA, 0x6FDF, 0x9DFB, 0x6FE2,\n\t0x9DFC, 0x6FE3, 0x9DFD, 0x6FE4, 0x9DFE, 0x6FE5, 0x9E40, 0x6FE6,\n\t0x9E41, 0x6FE7, 0x9E42, 0x6FE8, 0x9E43, 0x6FE9, 0x9E44, 0x6FEA,\n\t0x9E45, 0x6FEB, 0x9E46, 0x6FEC, 0x9E47, 0x6FED, 0x9E48, 0x6FF0,\n\t0x9E49, 0x6FF1, 0x9E4A, 0x6FF2, 0x9E4B, 0x6FF3, 0x9E4C, 0x6FF4,\n\t0x9E4D, 0x6FF5, 0x9E4E, 0x6FF6, 0x9E4F, 0x6FF7, 0x9E50, 0x6FF8,\n\t0x9E51, 0x6FF9, 0x9E52, 0x6FFA, 0x9E53, 0x6FFB, 0x9E54, 0x6FFC,\n\t0x9E55, 0x6FFD, 0x9E56, 0x6FFE, 0x9E57, 0x6FFF, 0x9E58, 0x7000,\n\t0x9E59, 0x7001, 0x9E5A, 0x7002, 0x9E5B, 0x7003, 0x9E5C, 0x7004,\n\t0x9E5D, 0x7005, 0x9E5E, 0x7006, 0x9E5F, 0x7007, 0x9E60, 0x7008,\n\t0x9E61, 0x7009, 0x9E62, 0x700A, 0x9E63, 0x700B, 0x9E64, 0x700C,\n\t0x9E65, 0x700D, 0x9E66, 0x700E, 0x9E67, 0x700F, 0x9E68, 0x7010,\n\t0x9E69, 0x7012, 0x9E6A, 0x7013, 0x9E6B, 0x7014, 0x9E6C, 0x7015,\n\t0x9E6D, 0x7016, 0x9E6E, 0x7017, 0x9E6F, 0x7018, 0x9E70, 0x7019,\n\t0x9E71, 0x701C, 0x9E72, 0x701D, 0x9E73, 0x701E, 0x9E74, 0x701F,\n\t0x9E75, 0x7020, 0x9E76, 0x7021, 0x9E77, 0x7022, 0x9E78, 0x7024,\n\t0x9E79, 0x7025, 0x9E7A, 0x7026, 0x9E7B, 0x7027, 0x9E7C, 0x7028,\n\t0x9E7D, 0x7029, 0x9E7E, 0x702A, 0x9E80, 0x702B, 0x9E81, 0x702C,\n\t0x9E82, 0x702D, 0x9E83, 0x702E, 0x9E84, 0x702F, 0x9E85, 0x7030,\n\t0x9E86, 0x7031, 0x9E87, 0x7032, 0x9E88, 0x7033, 0x9E89, 0x7034,\n\t0x9E8A, 0x7036, 0x9E8B, 0x7037, 0x9E8C, 0x7038, 0x9E8D, 0x703A,\n\t0x9E8E, 0x703B, 0x9E8F, 0x703C, 0x9E90, 0x703D, 0x9E91, 0x703E,\n\t0x9E92, 0x703F, 0x9E93, 0x7040, 0x9E94, 0x7041, 0x9E95, 0x7042,\n\t0x9E96, 0x7043, 0x9E97, 0x7044, 0x9E98, 0x7045, 0x9E99, 0x7046,\n\t0x9E9A, 0x7047, 0x9E9B, 0x7048, 0x9E9C, 0x7049, 0x9E9D, 0x704A,\n\t0x9E9E, 0x704B, 0x9E9F, 0x704D, 0x9EA0, 0x704E, 0x9EA1, 0x7050,\n\t0x9EA2, 0x7051, 0x9EA3, 0x7052, 0x9EA4, 0x7053, 0x9EA5, 0x7054,\n\t0x9EA6, 0x7055, 0x9EA7, 0x7056, 0x9EA8, 0x7057, 0x9EA9, 0x7058,\n\t0x9EAA, 0x7059, 0x9EAB, 0x705A, 0x9EAC, 0x705B, 0x9EAD, 0x705C,\n\t0x9EAE, 0x705D, 0x9EAF, 0x705F, 0x9EB0, 0x7060, 0x9EB1, 0x7061,\n\t0x9EB2, 0x7062, 0x9EB3, 0x7063, 0x9EB4, 0x7064, 0x9EB5, 0x7065,\n\t0x9EB6, 0x7066, 0x9EB7, 0x7067, 0x9EB8, 0x7068, 0x9EB9, 0x7069,\n\t0x9EBA, 0x706A, 0x9EBB, 0x706E, 0x9EBC, 0x7071, 0x9EBD, 0x7072,\n\t0x9EBE, 0x7073, 0x9EBF, 0x7074, 0x9EC0, 0x7077, 0x9EC1, 0x7079,\n\t0x9EC2, 0x707A, 0x9EC3, 0x707B, 0x9EC4, 0x707D, 0x9EC5, 0x7081,\n\t0x9EC6, 0x7082, 0x9EC7, 0x7083, 0x9EC8, 0x7084, 0x9EC9, 0x7086,\n\t0x9ECA, 0x7087, 0x9ECB, 0x7088, 0x9ECC, 0x708B, 0x9ECD, 0x708C,\n\t0x9ECE, 0x708D, 0x9ECF, 0x708F, 0x9ED0, 0x7090, 0x9ED1, 0x7091,\n\t0x9ED2, 0x7093, 0x9ED3, 0x7097, 0x9ED4, 0x7098, 0x9ED5, 0x709A,\n\t0x9ED6, 0x709B, 0x9ED7, 0x709E, 0x9ED8, 0x709F, 0x9ED9, 0x70A0,\n\t0x9EDA, 0x70A1, 0x9EDB, 0x70A2, 0x9EDC, 0x70A3, 0x9EDD, 0x70A4,\n\t0x9EDE, 0x70A5, 0x9EDF, 0x70A6, 0x9EE0, 0x70A7, 0x9EE1, 0x70A8,\n\t0x9EE2, 0x70A9, 0x9EE3, 0x70AA, 0x9EE4, 0x70B0, 0x9EE5, 0x70B2,\n\t0x9EE6, 0x70B4, 0x9EE7, 0x70B5, 0x9EE8, 0x70B6, 0x9EE9, 0x70BA,\n\t0x9EEA, 0x70BE, 0x9EEB, 0x70BF, 0x9EEC, 0x70C4, 0x9EED, 0x70C5,\n\t0x9EEE, 0x70C6, 0x9EEF, 0x70C7, 0x9EF0, 0x70C9, 0x9EF1, 0x70CB,\n\t0x9EF2, 0x70CC, 0x9EF3, 0x70CD, 0x9EF4, 0x70CE, 0x9EF5, 0x70CF,\n\t0x9EF6, 0x70D0, 0x9EF7, 0x70D1, 0x9EF8, 0x70D2, 0x9EF9, 0x70D3,\n\t0x9EFA, 0x70D4, 0x9EFB, 0x70D5, 0x9EFC, 0x70D6, 0x9EFD, 0x70D7,\n\t0x9EFE, 0x70DA, 0x9F40, 0x70DC, 0x9F41, 0x70DD, 0x9F42, 0x70DE,\n\t0x9F43, 0x70E0, 0x9F44, 0x70E1, 0x9F45, 0x70E2, 0x9F46, 0x70E3,\n\t0x9F47, 0x70E5, 0x9F48, 0x70EA, 0x9F49, 0x70EE, 0x9F4A, 0x70F0,\n\t0x9F4B, 0x70F1, 0x9F4C, 0x70F2, 0x9F4D, 0x70F3, 0x9F4E, 0x70F4,\n\t0x9F4F, 0x70F5, 0x9F50, 0x70F6, 0x9F51, 0x70F8, 0x9F52, 0x70FA,\n\t0x9F53, 0x70FB, 0x9F54, 0x70FC, 0x9F55, 0x70FE, 0x9F56, 0x70FF,\n\t0x9F57, 0x7100, 0x9F58, 0x7101, 0x9F59, 0x7102, 0x9F5A, 0x7103,\n\t0x9F5B, 0x7104, 0x9F5C, 0x7105, 0x9F5D, 0x7106, 0x9F5E, 0x7107,\n\t0x9F5F, 0x7108, 0x9F60, 0x710B, 0x9F61, 0x710C, 0x9F62, 0x710D,\n\t0x9F63, 0x710E, 0x9F64, 0x710F, 0x9F65, 0x7111, 0x9F66, 0x7112,\n\t0x9F67, 0x7114, 0x9F68, 0x7117, 0x9F69, 0x711B, 0x9F6A, 0x711C,\n\t0x9F6B, 0x711D, 0x9F6C, 0x711E, 0x9F6D, 0x711F, 0x9F6E, 0x7120,\n\t0x9F6F, 0x7121, 0x9F70, 0x7122, 0x9F71, 0x7123, 0x9F72, 0x7124,\n\t0x9F73, 0x7125, 0x9F74, 0x7127, 0x9F75, 0x7128, 0x9F76, 0x7129,\n\t0x9F77, 0x712A, 0x9F78, 0x712B, 0x9F79, 0x712C, 0x9F7A, 0x712D,\n\t0x9F7B, 0x712E, 0x9F7C, 0x7132, 0x9F7D, 0x7133, 0x9F7E, 0x7134,\n\t0x9F80, 0x7135, 0x9F81, 0x7137, 0x9F82, 0x7138, 0x9F83, 0x7139,\n\t0x9F84, 0x713A, 0x9F85, 0x713B, 0x9F86, 0x713C, 0x9F87, 0x713D,\n\t0x9F88, 0x713E, 0x9F89, 0x713F, 0x9F8A, 0x7140, 0x9F8B, 0x7141,\n\t0x9F8C, 0x7142, 0x9F8D, 0x7143, 0x9F8E, 0x7144, 0x9F8F, 0x7146,\n\t0x9F90, 0x7147, 0x9F91, 0x7148, 0x9F92, 0x7149, 0x9F93, 0x714B,\n\t0x9F94, 0x714D, 0x9F95, 0x714F, 0x9F96, 0x7150, 0x9F97, 0x7151,\n\t0x9F98, 0x7152, 0x9F99, 0x7153, 0x9F9A, 0x7154, 0x9F9B, 0x7155,\n\t0x9F9C, 0x7156, 0x9F9D, 0x7157, 0x9F9E, 0x7158, 0x9F9F, 0x7159,\n\t0x9FA0, 0x715A, 0x9FA1, 0x715B, 0x9FA2, 0x715D, 0x9FA3, 0x715F,\n\t0x9FA4, 0x7160, 0x9FA5, 0x7161, 0x9FA6, 0x7162, 0x9FA7, 0x7163,\n\t0x9FA8, 0x7165, 0x9FA9, 0x7169, 0x9FAA, 0x716A, 0x9FAB, 0x716B,\n\t0x9FAC, 0x716C, 0x9FAD, 0x716D, 0x9FAE, 0x716F, 0x9FAF, 0x7170,\n\t0x9FB0, 0x7171, 0x9FB1, 0x7174, 0x9FB2, 0x7175, 0x9FB3, 0x7176,\n\t0x9FB4, 0x7177, 0x9FB5, 0x7179, 0x9FB6, 0x717B, 0x9FB7, 0x717C,\n\t0x9FB8, 0x717E, 0x9FB9, 0x717F, 0x9FBA, 0x7180, 0x9FBB, 0x7181,\n\t0x9FBC, 0x7182, 0x9FBD, 0x7183, 0x9FBE, 0x7185, 0x9FBF, 0x7186,\n\t0x9FC0, 0x7187, 0x9FC1, 0x7188, 0x9FC2, 0x7189, 0x9FC3, 0x718B,\n\t0x9FC4, 0x718C, 0x9FC5, 0x718D, 0x9FC6, 0x718E, 0x9FC7, 0x7190,\n\t0x9FC8, 0x7191, 0x9FC9, 0x7192, 0x9FCA, 0x7193, 0x9FCB, 0x7195,\n\t0x9FCC, 0x7196, 0x9FCD, 0x7197, 0x9FCE, 0x719A, 0x9FCF, 0x719B,\n\t0x9FD0, 0x719C, 0x9FD1, 0x719D, 0x9FD2, 0x719E, 0x9FD3, 0x71A1,\n\t0x9FD4, 0x71A2, 0x9FD5, 0x71A3, 0x9FD6, 0x71A4, 0x9FD7, 0x71A5,\n\t0x9FD8, 0x71A6, 0x9FD9, 0x71A7, 0x9FDA, 0x71A9, 0x9FDB, 0x71AA,\n\t0x9FDC, 0x71AB, 0x9FDD, 0x71AD, 0x9FDE, 0x71AE, 0x9FDF, 0x71AF,\n\t0x9FE0, 0x71B0, 0x9FE1, 0x71B1, 0x9FE2, 0x71B2, 0x9FE3, 0x71B4,\n\t0x9FE4, 0x71B6, 0x9FE5, 0x71B7, 0x9FE6, 0x71B8, 0x9FE7, 0x71BA,\n\t0x9FE8, 0x71BB, 0x9FE9, 0x71BC, 0x9FEA, 0x71BD, 0x9FEB, 0x71BE,\n\t0x9FEC, 0x71BF, 0x9FED, 0x71C0, 0x9FEE, 0x71C1, 0x9FEF, 0x71C2,\n\t0x9FF0, 0x71C4, 0x9FF1, 0x71C5, 0x9FF2, 0x71C6, 0x9FF3, 0x71C7,\n\t0x9FF4, 0x71C8, 0x9FF5, 0x71C9, 0x9FF6, 0x71CA, 0x9FF7, 0x71CB,\n\t0x9FF8, 0x71CC, 0x9FF9, 0x71CD, 0x9FFA, 0x71CF, 0x9FFB, 0x71D0,\n\t0x9FFC, 0x71D1, 0x9FFD, 0x71D2, 0x9FFE, 0x71D3, 0xA040, 0x71D6,\n\t0xA041, 0x71D7, 0xA042, 0x71D8, 0xA043, 0x71D9, 0xA044, 0x71DA,\n\t0xA045, 0x71DB, 0xA046, 0x71DC, 0xA047, 0x71DD, 0xA048, 0x71DE,\n\t0xA049, 0x71DF, 0xA04A, 0x71E1, 0xA04B, 0x71E2, 0xA04C, 0x71E3,\n\t0xA04D, 0x71E4, 0xA04E, 0x71E6, 0xA04F, 0x71E8, 0xA050, 0x71E9,\n\t0xA051, 0x71EA, 0xA052, 0x71EB, 0xA053, 0x71EC, 0xA054, 0x71ED,\n\t0xA055, 0x71EF, 0xA056, 0x71F0, 0xA057, 0x71F1, 0xA058, 0x71F2,\n\t0xA059, 0x71F3, 0xA05A, 0x71F4, 0xA05B, 0x71F5, 0xA05C, 0x71F6,\n\t0xA05D, 0x71F7, 0xA05E, 0x71F8, 0xA05F, 0x71FA, 0xA060, 0x71FB,\n\t0xA061, 0x71FC, 0xA062, 0x71FD, 0xA063, 0x71FE, 0xA064, 0x71FF,\n\t0xA065, 0x7200, 0xA066, 0x7201, 0xA067, 0x7202, 0xA068, 0x7203,\n\t0xA069, 0x7204, 0xA06A, 0x7205, 0xA06B, 0x7207, 0xA06C, 0x7208,\n\t0xA06D, 0x7209, 0xA06E, 0x720A, 0xA06F, 0x720B, 0xA070, 0x720C,\n\t0xA071, 0x720D, 0xA072, 0x720E, 0xA073, 0x720F, 0xA074, 0x7210,\n\t0xA075, 0x7211, 0xA076, 0x7212, 0xA077, 0x7213, 0xA078, 0x7214,\n\t0xA079, 0x7215, 0xA07A, 0x7216, 0xA07B, 0x7217, 0xA07C, 0x7218,\n\t0xA07D, 0x7219, 0xA07E, 0x721A, 0xA080, 0x721B, 0xA081, 0x721C,\n\t0xA082, 0x721E, 0xA083, 0x721F, 0xA084, 0x7220, 0xA085, 0x7221,\n\t0xA086, 0x7222, 0xA087, 0x7223, 0xA088, 0x7224, 0xA089, 0x7225,\n\t0xA08A, 0x7226, 0xA08B, 0x7227, 0xA08C, 0x7229, 0xA08D, 0x722B,\n\t0xA08E, 0x722D, 0xA08F, 0x722E, 0xA090, 0x722F, 0xA091, 0x7232,\n\t0xA092, 0x7233, 0xA093, 0x7234, 0xA094, 0x723A, 0xA095, 0x723C,\n\t0xA096, 0x723E, 0xA097, 0x7240, 0xA098, 0x7241, 0xA099, 0x7242,\n\t0xA09A, 0x7243, 0xA09B, 0x7244, 0xA09C, 0x7245, 0xA09D, 0x7246,\n\t0xA09E, 0x7249, 0xA09F, 0x724A, 0xA0A0, 0x724B, 0xA0A1, 0x724E,\n\t0xA0A2, 0x724F, 0xA0A3, 0x7250, 0xA0A4, 0x7251, 0xA0A5, 0x7253,\n\t0xA0A6, 0x7254, 0xA0A7, 0x7255, 0xA0A8, 0x7257, 0xA0A9, 0x7258,\n\t0xA0AA, 0x725A, 0xA0AB, 0x725C, 0xA0AC, 0x725E, 0xA0AD, 0x7260,\n\t0xA0AE, 0x7263, 0xA0AF, 0x7264, 0xA0B0, 0x7265, 0xA0B1, 0x7268,\n\t0xA0B2, 0x726A, 0xA0B3, 0x726B, 0xA0B4, 0x726C, 0xA0B5, 0x726D,\n\t0xA0B6, 0x7270, 0xA0B7, 0x7271, 0xA0B8, 0x7273, 0xA0B9, 0x7274,\n\t0xA0BA, 0x7276, 0xA0BB, 0x7277, 0xA0BC, 0x7278, 0xA0BD, 0x727B,\n\t0xA0BE, 0x727C, 0xA0BF, 0x727D, 0xA0C0, 0x7282, 0xA0C1, 0x7283,\n\t0xA0C2, 0x7285, 0xA0C3, 0x7286, 0xA0C4, 0x7287, 0xA0C5, 0x7288,\n\t0xA0C6, 0x7289, 0xA0C7, 0x728C, 0xA0C8, 0x728E, 0xA0C9, 0x7290,\n\t0xA0CA, 0x7291, 0xA0CB, 0x7293, 0xA0CC, 0x7294, 0xA0CD, 0x7295,\n\t0xA0CE, 0x7296, 0xA0CF, 0x7297, 0xA0D0, 0x7298, 0xA0D1, 0x7299,\n\t0xA0D2, 0x729A, 0xA0D3, 0x729B, 0xA0D4, 0x729C, 0xA0D5, 0x729D,\n\t0xA0D6, 0x729E, 0xA0D7, 0x72A0, 0xA0D8, 0x72A1, 0xA0D9, 0x72A2,\n\t0xA0DA, 0x72A3, 0xA0DB, 0x72A4, 0xA0DC, 0x72A5, 0xA0DD, 0x72A6,\n\t0xA0DE, 0x72A7, 0xA0DF, 0x72A8, 0xA0E0, 0x72A9, 0xA0E1, 0x72AA,\n\t0xA0E2, 0x72AB, 0xA0E3, 0x72AE, 0xA0E4, 0x72B1, 0xA0E5, 0x72B2,\n\t0xA0E6, 0x72B3, 0xA0E7, 0x72B5, 0xA0E8, 0x72BA, 0xA0E9, 0x72BB,\n\t0xA0EA, 0x72BC, 0xA0EB, 0x72BD, 0xA0EC, 0x72BE, 0xA0ED, 0x72BF,\n\t0xA0EE, 0x72C0, 0xA0EF, 0x72C5, 0xA0F0, 0x72C6, 0xA0F1, 0x72C7,\n\t0xA0F2, 0x72C9, 0xA0F3, 0x72CA, 0xA0F4, 0x72CB, 0xA0F5, 0x72CC,\n\t0xA0F6, 0x72CF, 0xA0F7, 0x72D1, 0xA0F8, 0x72D3, 0xA0F9, 0x72D4,\n\t0xA0FA, 0x72D5, 0xA0FB, 0x72D6, 0xA0FC, 0x72D8, 0xA0FD, 0x72DA,\n\t0xA0FE, 0x72DB, 0xA1A1, 0x3000, 0xA1A2, 0x3001, 0xA1A3, 0x3002,\n\t0xA1A4, 0x00B7, 0xA1A5, 0x02C9, 0xA1A6, 0x02C7, 0xA1A7, 0x00A8,\n\t0xA1A8, 0x3003, 0xA1A9, 0x3005, 0xA1AA, 0x2014, 0xA1AB, 0xFF5E,\n\t0xA1AC, 0x2016, 0xA1AD, 0x2026, 0xA1AE, 0x2018, 0xA1AF, 0x2019,\n\t0xA1B0, 0x201C, 0xA1B1, 0x201D, 0xA1B2, 0x3014, 0xA1B3, 0x3015,\n\t0xA1B4, 0x3008, 0xA1B5, 0x3009, 0xA1B6, 0x300A, 0xA1B7, 0x300B,\n\t0xA1B8, 0x300C, 0xA1B9, 0x300D, 0xA1BA, 0x300E, 0xA1BB, 0x300F,\n\t0xA1BC, 0x3016, 0xA1BD, 0x3017, 0xA1BE, 0x3010, 0xA1BF, 0x3011,\n\t0xA1C0, 0x00B1, 0xA1C1, 0x00D7, 0xA1C2, 0x00F7, 0xA1C3, 0x2236,\n\t0xA1C4, 0x2227, 0xA1C5, 0x2228, 0xA1C6, 0x2211, 0xA1C7, 0x220F,\n\t0xA1C8, 0x222A, 0xA1C9, 0x2229, 0xA1CA, 0x2208, 0xA1CB, 0x2237,\n\t0xA1CC, 0x221A, 0xA1CD, 0x22A5, 0xA1CE, 0x2225, 0xA1CF, 0x2220,\n\t0xA1D0, 0x2312, 0xA1D1, 0x2299, 0xA1D2, 0x222B, 0xA1D3, 0x222E,\n\t0xA1D4, 0x2261, 0xA1D5, 0x224C, 0xA1D6, 0x2248, 0xA1D7, 0x223D,\n\t0xA1D8, 0x221D, 0xA1D9, 0x2260, 0xA1DA, 0x226E, 0xA1DB, 0x226F,\n\t0xA1DC, 0x2264, 0xA1DD, 0x2265, 0xA1DE, 0x221E, 0xA1DF, 0x2235,\n\t0xA1E0, 0x2234, 0xA1E1, 0x2642, 0xA1E2, 0x2640, 0xA1E3, 0x00B0,\n\t0xA1E4, 0x2032, 0xA1E5, 0x2033, 0xA1E6, 0x2103, 0xA1E7, 0xFF04,\n\t0xA1E8, 0x00A4, 0xA1E9, 0xFFE0, 0xA1EA, 0xFFE1, 0xA1EB, 0x2030,\n\t0xA1EC, 0x00A7, 0xA1ED, 0x2116, 0xA1EE, 0x2606, 0xA1EF, 0x2605,\n\t0xA1F0, 0x25CB, 0xA1F1, 0x25CF, 0xA1F2, 0x25CE, 0xA1F3, 0x25C7,\n\t0xA1F4, 0x25C6, 0xA1F5, 0x25A1, 0xA1F6, 0x25A0, 0xA1F7, 0x25B3,\n\t0xA1F8, 0x25B2, 0xA1F9, 0x203B, 0xA1FA, 0x2192, 0xA1FB, 0x2190,\n\t0xA1FC, 0x2191, 0xA1FD, 0x2193, 0xA1FE, 0x3013, 0xA2A1, 0x2170,\n\t0xA2A2, 0x2171, 0xA2A3, 0x2172, 0xA2A4, 0x2173, 0xA2A5, 0x2174,\n\t0xA2A6, 0x2175, 0xA2A7, 0x2176, 0xA2A8, 0x2177, 0xA2A9, 0x2178,\n\t0xA2AA, 0x2179, 0xA2B1, 0x2488, 0xA2B2, 0x2489, 0xA2B3, 0x248A,\n\t0xA2B4, 0x248B, 0xA2B5, 0x248C, 0xA2B6, 0x248D, 0xA2B7, 0x248E,\n\t0xA2B8, 0x248F, 0xA2B9, 0x2490, 0xA2BA, 0x2491, 0xA2BB, 0x2492,\n\t0xA2BC, 0x2493, 0xA2BD, 0x2494, 0xA2BE, 0x2495, 0xA2BF, 0x2496,\n\t0xA2C0, 0x2497, 0xA2C1, 0x2498, 0xA2C2, 0x2499, 0xA2C3, 0x249A,\n\t0xA2C4, 0x249B, 0xA2C5, 0x2474, 0xA2C6, 0x2475, 0xA2C7, 0x2476,\n\t0xA2C8, 0x2477, 0xA2C9, 0x2478, 0xA2CA, 0x2479, 0xA2CB, 0x247A,\n\t0xA2CC, 0x247B, 0xA2CD, 0x247C, 0xA2CE, 0x247D, 0xA2CF, 0x247E,\n\t0xA2D0, 0x247F, 0xA2D1, 0x2480, 0xA2D2, 0x2481, 0xA2D3, 0x2482,\n\t0xA2D4, 0x2483, 0xA2D5, 0x2484, 0xA2D6, 0x2485, 0xA2D7, 0x2486,\n\t0xA2D8, 0x2487, 0xA2D9, 0x2460, 0xA2DA, 0x2461, 0xA2DB, 0x2462,\n\t0xA2DC, 0x2463, 0xA2DD, 0x2464, 0xA2DE, 0x2465, 0xA2DF, 0x2466,\n\t0xA2E0, 0x2467, 0xA2E1, 0x2468, 0xA2E2, 0x2469, 0xA2E5, 0x3220,\n\t0xA2E6, 0x3221, 0xA2E7, 0x3222, 0xA2E8, 0x3223, 0xA2E9, 0x3224,\n\t0xA2EA, 0x3225, 0xA2EB, 0x3226, 0xA2EC, 0x3227, 0xA2ED, 0x3228,\n\t0xA2EE, 0x3229, 0xA2F1, 0x2160, 0xA2F2, 0x2161, 0xA2F3, 0x2162,\n\t0xA2F4, 0x2163, 0xA2F5, 0x2164, 0xA2F6, 0x2165, 0xA2F7, 0x2166,\n\t0xA2F8, 0x2167, 0xA2F9, 0x2168, 0xA2FA, 0x2169, 0xA2FB, 0x216A,\n\t0xA2FC, 0x216B, 0xA3A1, 0xFF01, 0xA3A2, 0xFF02, 0xA3A3, 0xFF03,\n\t0xA3A4, 0xFFE5, 0xA3A5, 0xFF05, 0xA3A6, 0xFF06, 0xA3A7, 0xFF07,\n\t0xA3A8, 0xFF08, 0xA3A9, 0xFF09, 0xA3AA, 0xFF0A, 0xA3AB, 0xFF0B,\n\t0xA3AC, 0xFF0C, 0xA3AD, 0xFF0D, 0xA3AE, 0xFF0E, 0xA3AF, 0xFF0F,\n\t0xA3B0, 0xFF10, 0xA3B1, 0xFF11, 0xA3B2, 0xFF12, 0xA3B3, 0xFF13,\n\t0xA3B4, 0xFF14, 0xA3B5, 0xFF15, 0xA3B6, 0xFF16, 0xA3B7, 0xFF17,\n\t0xA3B8, 0xFF18, 0xA3B9, 0xFF19, 0xA3BA, 0xFF1A, 0xA3BB, 0xFF1B,\n\t0xA3BC, 0xFF1C, 0xA3BD, 0xFF1D, 0xA3BE, 0xFF1E, 0xA3BF, 0xFF1F,\n\t0xA3C0, 0xFF20, 0xA3C1, 0xFF21, 0xA3C2, 0xFF22, 0xA3C3, 0xFF23,\n\t0xA3C4, 0xFF24, 0xA3C5, 0xFF25, 0xA3C6, 0xFF26, 0xA3C7, 0xFF27,\n\t0xA3C8, 0xFF28, 0xA3C9, 0xFF29, 0xA3CA, 0xFF2A, 0xA3CB, 0xFF2B,\n\t0xA3CC, 0xFF2C, 0xA3CD, 0xFF2D, 0xA3CE, 0xFF2E, 0xA3CF, 0xFF2F,\n\t0xA3D0, 0xFF30, 0xA3D1, 0xFF31, 0xA3D2, 0xFF32, 0xA3D3, 0xFF33,\n\t0xA3D4, 0xFF34, 0xA3D5, 0xFF35, 0xA3D6, 0xFF36, 0xA3D7, 0xFF37,\n\t0xA3D8, 0xFF38, 0xA3D9, 0xFF39, 0xA3DA, 0xFF3A, 0xA3DB, 0xFF3B,\n\t0xA3DC, 0xFF3C, 0xA3DD, 0xFF3D, 0xA3DE, 0xFF3E, 0xA3DF, 0xFF3F,\n\t0xA3E0, 0xFF40, 0xA3E1, 0xFF41, 0xA3E2, 0xFF42, 0xA3E3, 0xFF43,\n\t0xA3E4, 0xFF44, 0xA3E5, 0xFF45, 0xA3E6, 0xFF46, 0xA3E7, 0xFF47,\n\t0xA3E8, 0xFF48, 0xA3E9, 0xFF49, 0xA3EA, 0xFF4A, 0xA3EB, 0xFF4B,\n\t0xA3EC, 0xFF4C, 0xA3ED, 0xFF4D, 0xA3EE, 0xFF4E, 0xA3EF, 0xFF4F,\n\t0xA3F0, 0xFF50, 0xA3F1, 0xFF51, 0xA3F2, 0xFF52, 0xA3F3, 0xFF53,\n\t0xA3F4, 0xFF54, 0xA3F5, 0xFF55, 0xA3F6, 0xFF56, 0xA3F7, 0xFF57,\n\t0xA3F8, 0xFF58, 0xA3F9, 0xFF59, 0xA3FA, 0xFF5A, 0xA3FB, 0xFF5B,\n\t0xA3FC, 0xFF5C, 0xA3FD, 0xFF5D, 0xA3FE, 0xFFE3, 0xA4A1, 0x3041,\n\t0xA4A2, 0x3042, 0xA4A3, 0x3043, 0xA4A4, 0x3044, 0xA4A5, 0x3045,\n\t0xA4A6, 0x3046, 0xA4A7, 0x3047, 0xA4A8, 0x3048, 0xA4A9, 0x3049,\n\t0xA4AA, 0x304A, 0xA4AB, 0x304B, 0xA4AC, 0x304C, 0xA4AD, 0x304D,\n\t0xA4AE, 0x304E, 0xA4AF, 0x304F, 0xA4B0, 0x3050, 0xA4B1, 0x3051,\n\t0xA4B2, 0x3052, 0xA4B3, 0x3053, 0xA4B4, 0x3054, 0xA4B5, 0x3055,\n\t0xA4B6, 0x3056, 0xA4B7, 0x3057, 0xA4B8, 0x3058, 0xA4B9, 0x3059,\n\t0xA4BA, 0x305A, 0xA4BB, 0x305B, 0xA4BC, 0x305C, 0xA4BD, 0x305D,\n\t0xA4BE, 0x305E, 0xA4BF, 0x305F, 0xA4C0, 0x3060, 0xA4C1, 0x3061,\n\t0xA4C2, 0x3062, 0xA4C3, 0x3063, 0xA4C4, 0x3064, 0xA4C5, 0x3065,\n\t0xA4C6, 0x3066, 0xA4C7, 0x3067, 0xA4C8, 0x3068, 0xA4C9, 0x3069,\n\t0xA4CA, 0x306A, 0xA4CB, 0x306B, 0xA4CC, 0x306C, 0xA4CD, 0x306D,\n\t0xA4CE, 0x306E, 0xA4CF, 0x306F, 0xA4D0, 0x3070, 0xA4D1, 0x3071,\n\t0xA4D2, 0x3072, 0xA4D3, 0x3073, 0xA4D4, 0x3074, 0xA4D5, 0x3075,\n\t0xA4D6, 0x3076, 0xA4D7, 0x3077, 0xA4D8, 0x3078, 0xA4D9, 0x3079,\n\t0xA4DA, 0x307A, 0xA4DB, 0x307B, 0xA4DC, 0x307C, 0xA4DD, 0x307D,\n\t0xA4DE, 0x307E, 0xA4DF, 0x307F, 0xA4E0, 0x3080, 0xA4E1, 0x3081,\n\t0xA4E2, 0x3082, 0xA4E3, 0x3083, 0xA4E4, 0x3084, 0xA4E5, 0x3085,\n\t0xA4E6, 0x3086, 0xA4E7, 0x3087, 0xA4E8, 0x3088, 0xA4E9, 0x3089,\n\t0xA4EA, 0x308A, 0xA4EB, 0x308B, 0xA4EC, 0x308C, 0xA4ED, 0x308D,\n\t0xA4EE, 0x308E, 0xA4EF, 0x308F, 0xA4F0, 0x3090, 0xA4F1, 0x3091,\n\t0xA4F2, 0x3092, 0xA4F3, 0x3093, 0xA5A1, 0x30A1, 0xA5A2, 0x30A2,\n\t0xA5A3, 0x30A3, 0xA5A4, 0x30A4, 0xA5A5, 0x30A5, 0xA5A6, 0x30A6,\n\t0xA5A7, 0x30A7, 0xA5A8, 0x30A8, 0xA5A9, 0x30A9, 0xA5AA, 0x30AA,\n\t0xA5AB, 0x30AB, 0xA5AC, 0x30AC, 0xA5AD, 0x30AD, 0xA5AE, 0x30AE,\n\t0xA5AF, 0x30AF, 0xA5B0, 0x30B0, 0xA5B1, 0x30B1, 0xA5B2, 0x30B2,\n\t0xA5B3, 0x30B3, 0xA5B4, 0x30B4, 0xA5B5, 0x30B5, 0xA5B6, 0x30B6,\n\t0xA5B7, 0x30B7, 0xA5B8, 0x30B8, 0xA5B9, 0x30B9, 0xA5BA, 0x30BA,\n\t0xA5BB, 0x30BB, 0xA5BC, 0x30BC, 0xA5BD, 0x30BD, 0xA5BE, 0x30BE,\n\t0xA5BF, 0x30BF, 0xA5C0, 0x30C0, 0xA5C1, 0x30C1, 0xA5C2, 0x30C2,\n\t0xA5C3, 0x30C3, 0xA5C4, 0x30C4, 0xA5C5, 0x30C5, 0xA5C6, 0x30C6,\n\t0xA5C7, 0x30C7, 0xA5C8, 0x30C8, 0xA5C9, 0x30C9, 0xA5CA, 0x30CA,\n\t0xA5CB, 0x30CB, 0xA5CC, 0x30CC, 0xA5CD, 0x30CD, 0xA5CE, 0x30CE,\n\t0xA5CF, 0x30CF, 0xA5D0, 0x30D0, 0xA5D1, 0x30D1, 0xA5D2, 0x30D2,\n\t0xA5D3, 0x30D3, 0xA5D4, 0x30D4, 0xA5D5, 0x30D5, 0xA5D6, 0x30D6,\n\t0xA5D7, 0x30D7, 0xA5D8, 0x30D8, 0xA5D9, 0x30D9, 0xA5DA, 0x30DA,\n\t0xA5DB, 0x30DB, 0xA5DC, 0x30DC, 0xA5DD, 0x30DD, 0xA5DE, 0x30DE,\n\t0xA5DF, 0x30DF, 0xA5E0, 0x30E0, 0xA5E1, 0x30E1, 0xA5E2, 0x30E2,\n\t0xA5E3, 0x30E3, 0xA5E4, 0x30E4, 0xA5E5, 0x30E5, 0xA5E6, 0x30E6,\n\t0xA5E7, 0x30E7, 0xA5E8, 0x30E8, 0xA5E9, 0x30E9, 0xA5EA, 0x30EA,\n\t0xA5EB, 0x30EB, 0xA5EC, 0x30EC, 0xA5ED, 0x30ED, 0xA5EE, 0x30EE,\n\t0xA5EF, 0x30EF, 0xA5F0, 0x30F0, 0xA5F1, 0x30F1, 0xA5F2, 0x30F2,\n\t0xA5F3, 0x30F3, 0xA5F4, 0x30F4, 0xA5F5, 0x30F5, 0xA5F6, 0x30F6,\n\t0xA6A1, 0x0391, 0xA6A2, 0x0392, 0xA6A3, 0x0393, 0xA6A4, 0x0394,\n\t0xA6A5, 0x0395, 0xA6A6, 0x0396, 0xA6A7, 0x0397, 0xA6A8, 0x0398,\n\t0xA6A9, 0x0399, 0xA6AA, 0x039A, 0xA6AB, 0x039B, 0xA6AC, 0x039C,\n\t0xA6AD, 0x039D, 0xA6AE, 0x039E, 0xA6AF, 0x039F, 0xA6B0, 0x03A0,\n\t0xA6B1, 0x03A1, 0xA6B2, 0x03A3, 0xA6B3, 0x03A4, 0xA6B4, 0x03A5,\n\t0xA6B5, 0x03A6, 0xA6B6, 0x03A7, 0xA6B7, 0x03A8, 0xA6B8, 0x03A9,\n\t0xA6C1, 0x03B1, 0xA6C2, 0x03B2, 0xA6C3, 0x03B3, 0xA6C4, 0x03B4,\n\t0xA6C5, 0x03B5, 0xA6C6, 0x03B6, 0xA6C7, 0x03B7, 0xA6C8, 0x03B8,\n\t0xA6C9, 0x03B9, 0xA6CA, 0x03BA, 0xA6CB, 0x03BB, 0xA6CC, 0x03BC,\n\t0xA6CD, 0x03BD, 0xA6CE, 0x03BE, 0xA6CF, 0x03BF, 0xA6D0, 0x03C0,\n\t0xA6D1, 0x03C1, 0xA6D2, 0x03C3, 0xA6D3, 0x03C4, 0xA6D4, 0x03C5,\n\t0xA6D5, 0x03C6, 0xA6D6, 0x03C7, 0xA6D7, 0x03C8, 0xA6D8, 0x03C9,\n\t0xA6E0, 0xFE35, 0xA6E1, 0xFE36, 0xA6E2, 0xFE39, 0xA6E3, 0xFE3A,\n\t0xA6E4, 0xFE3F, 0xA6E5, 0xFE40, 0xA6E6, 0xFE3D, 0xA6E7, 0xFE3E,\n\t0xA6E8, 0xFE41, 0xA6E9, 0xFE42, 0xA6EA, 0xFE43, 0xA6EB, 0xFE44,\n\t0xA6EE, 0xFE3B, 0xA6EF, 0xFE3C, 0xA6F0, 0xFE37, 0xA6F1, 0xFE38,\n\t0xA6F2, 0xFE31, 0xA6F4, 0xFE33, 0xA6F5, 0xFE34, 0xA7A1, 0x0410,\n\t0xA7A2, 0x0411, 0xA7A3, 0x0412, 0xA7A4, 0x0413, 0xA7A5, 0x0414,\n\t0xA7A6, 0x0415, 0xA7A7, 0x0401, 0xA7A8, 0x0416, 0xA7A9, 0x0417,\n\t0xA7AA, 0x0418, 0xA7AB, 0x0419, 0xA7AC, 0x041A, 0xA7AD, 0x041B,\n\t0xA7AE, 0x041C, 0xA7AF, 0x041D, 0xA7B0, 0x041E, 0xA7B1, 0x041F,\n\t0xA7B2, 0x0420, 0xA7B3, 0x0421, 0xA7B4, 0x0422, 0xA7B5, 0x0423,\n\t0xA7B6, 0x0424, 0xA7B7, 0x0425, 0xA7B8, 0x0426, 0xA7B9, 0x0427,\n\t0xA7BA, 0x0428, 0xA7BB, 0x0429, 0xA7BC, 0x042A, 0xA7BD, 0x042B,\n\t0xA7BE, 0x042C, 0xA7BF, 0x042D, 0xA7C0, 0x042E, 0xA7C1, 0x042F,\n\t0xA7D1, 0x0430, 0xA7D2, 0x0431, 0xA7D3, 0x0432, 0xA7D4, 0x0433,\n\t0xA7D5, 0x0434, 0xA7D6, 0x0435, 0xA7D7, 0x0451, 0xA7D8, 0x0436,\n\t0xA7D9, 0x0437, 0xA7DA, 0x0438, 0xA7DB, 0x0439, 0xA7DC, 0x043A,\n\t0xA7DD, 0x043B, 0xA7DE, 0x043C, 0xA7DF, 0x043D, 0xA7E0, 0x043E,\n\t0xA7E1, 0x043F, 0xA7E2, 0x0440, 0xA7E3, 0x0441, 0xA7E4, 0x0442,\n\t0xA7E5, 0x0443, 0xA7E6, 0x0444, 0xA7E7, 0x0445, 0xA7E8, 0x0446,\n\t0xA7E9, 0x0447, 0xA7EA, 0x0448, 0xA7EB, 0x0449, 0xA7EC, 0x044A,\n\t0xA7ED, 0x044B, 0xA7EE, 0x044C, 0xA7EF, 0x044D, 0xA7F0, 0x044E,\n\t0xA7F1, 0x044F, 0xA840, 0x02CA, 0xA841, 0x02CB, 0xA842, 0x02D9,\n\t0xA843, 0x2013, 0xA844, 0x2015, 0xA845, 0x2025, 0xA846, 0x2035,\n\t0xA847, 0x2105, 0xA848, 0x2109, 0xA849, 0x2196, 0xA84A, 0x2197,\n\t0xA84B, 0x2198, 0xA84C, 0x2199, 0xA84D, 0x2215, 0xA84E, 0x221F,\n\t0xA84F, 0x2223, 0xA850, 0x2252, 0xA851, 0x2266, 0xA852, 0x2267,\n\t0xA853, 0x22BF, 0xA854, 0x2550, 0xA855, 0x2551, 0xA856, 0x2552,\n\t0xA857, 0x2553, 0xA858, 0x2554, 0xA859, 0x2555, 0xA85A, 0x2556,\n\t0xA85B, 0x2557, 0xA85C, 0x2558, 0xA85D, 0x2559, 0xA85E, 0x255A,\n\t0xA85F, 0x255B, 0xA860, 0x255C, 0xA861, 0x255D, 0xA862, 0x255E,\n\t0xA863, 0x255F, 0xA864, 0x2560, 0xA865, 0x2561, 0xA866, 0x2562,\n\t0xA867, 0x2563, 0xA868, 0x2564, 0xA869, 0x2565, 0xA86A, 0x2566,\n\t0xA86B, 0x2567, 0xA86C, 0x2568, 0xA86D, 0x2569, 0xA86E, 0x256A,\n\t0xA86F, 0x256B, 0xA870, 0x256C, 0xA871, 0x256D, 0xA872, 0x256E,\n\t0xA873, 0x256F, 0xA874, 0x2570, 0xA875, 0x2571, 0xA876, 0x2572,\n\t0xA877, 0x2573, 0xA878, 0x2581, 0xA879, 0x2582, 0xA87A, 0x2583,\n\t0xA87B, 0x2584, 0xA87C, 0x2585, 0xA87D, 0x2586, 0xA87E, 0x2587,\n\t0xA880, 0x2588, 0xA881, 0x2589, 0xA882, 0x258A, 0xA883, 0x258B,\n\t0xA884, 0x258C, 0xA885, 0x258D, 0xA886, 0x258E, 0xA887, 0x258F,\n\t0xA888, 0x2593, 0xA889, 0x2594, 0xA88A, 0x2595, 0xA88B, 0x25BC,\n\t0xA88C, 0x25BD, 0xA88D, 0x25E2, 0xA88E, 0x25E3, 0xA88F, 0x25E4,\n\t0xA890, 0x25E5, 0xA891, 0x2609, 0xA892, 0x2295, 0xA893, 0x3012,\n\t0xA894, 0x301D, 0xA895, 0x301E, 0xA8A1, 0x0101, 0xA8A2, 0x00E1,\n\t0xA8A3, 0x01CE, 0xA8A4, 0x00E0, 0xA8A5, 0x0113, 0xA8A6, 0x00E9,\n\t0xA8A7, 0x011B, 0xA8A8, 0x00E8, 0xA8A9, 0x012B, 0xA8AA, 0x00ED,\n\t0xA8AB, 0x01D0, 0xA8AC, 0x00EC, 0xA8AD, 0x014D, 0xA8AE, 0x00F3,\n\t0xA8AF, 0x01D2, 0xA8B0, 0x00F2, 0xA8B1, 0x016B, 0xA8B2, 0x00FA,\n\t0xA8B3, 0x01D4, 0xA8B4, 0x00F9, 0xA8B5, 0x01D6, 0xA8B6, 0x01D8,\n\t0xA8B7, 0x01DA, 0xA8B8, 0x01DC, 0xA8B9, 0x00FC, 0xA8BA, 0x00EA,\n\t0xA8BB, 0x0251, 0xA8BD, 0x0144, 0xA8BE, 0x0148, 0xA8C0, 0x0261,\n\t0xA8C5, 0x3105, 0xA8C6, 0x3106, 0xA8C7, 0x3107, 0xA8C8, 0x3108,\n\t0xA8C9, 0x3109, 0xA8CA, 0x310A, 0xA8CB, 0x310B, 0xA8CC, 0x310C,\n\t0xA8CD, 0x310D, 0xA8CE, 0x310E, 0xA8CF, 0x310F, 0xA8D0, 0x3110,\n\t0xA8D1, 0x3111, 0xA8D2, 0x3112, 0xA8D3, 0x3113, 0xA8D4, 0x3114,\n\t0xA8D5, 0x3115, 0xA8D6, 0x3116, 0xA8D7, 0x3117, 0xA8D8, 0x3118,\n\t0xA8D9, 0x3119, 0xA8DA, 0x311A, 0xA8DB, 0x311B, 0xA8DC, 0x311C,\n\t0xA8DD, 0x311D, 0xA8DE, 0x311E, 0xA8DF, 0x311F, 0xA8E0, 0x3120,\n\t0xA8E1, 0x3121, 0xA8E2, 0x3122, 0xA8E3, 0x3123, 0xA8E4, 0x3124,\n\t0xA8E5, 0x3125, 0xA8E6, 0x3126, 0xA8E7, 0x3127, 0xA8E8, 0x3128,\n\t0xA8E9, 0x3129, 0xA940, 0x3021, 0xA941, 0x3022, 0xA942, 0x3023,\n\t0xA943, 0x3024, 0xA944, 0x3025, 0xA945, 0x3026, 0xA946, 0x3027,\n\t0xA947, 0x3028, 0xA948, 0x3029, 0xA949, 0x32A3, 0xA94A, 0x338E,\n\t0xA94B, 0x338F, 0xA94C, 0x339C, 0xA94D, 0x339D, 0xA94E, 0x339E,\n\t0xA94F, 0x33A1, 0xA950, 0x33C4, 0xA951, 0x33CE, 0xA952, 0x33D1,\n\t0xA953, 0x33D2, 0xA954, 0x33D5, 0xA955, 0xFE30, 0xA956, 0xFFE2,\n\t0xA957, 0xFFE4, 0xA959, 0x2121, 0xA95A, 0x3231, 0xA95C, 0x2010,\n\t0xA960, 0x30FC, 0xA961, 0x309B, 0xA962, 0x309C, 0xA963, 0x30FD,\n\t0xA964, 0x30FE, 0xA965, 0x3006, 0xA966, 0x309D, 0xA967, 0x309E,\n\t0xA968, 0xFE49, 0xA969, 0xFE4A, 0xA96A, 0xFE4B, 0xA96B, 0xFE4C,\n\t0xA96C, 0xFE4D, 0xA96D, 0xFE4E, 0xA96E, 0xFE4F, 0xA96F, 0xFE50,\n\t0xA970, 0xFE51, 0xA971, 0xFE52, 0xA972, 0xFE54, 0xA973, 0xFE55,\n\t0xA974, 0xFE56, 0xA975, 0xFE57, 0xA976, 0xFE59, 0xA977, 0xFE5A,\n\t0xA978, 0xFE5B, 0xA979, 0xFE5C, 0xA97A, 0xFE5D, 0xA97B, 0xFE5E,\n\t0xA97C, 0xFE5F, 0xA97D, 0xFE60, 0xA97E, 0xFE61, 0xA980, 0xFE62,\n\t0xA981, 0xFE63, 0xA982, 0xFE64, 0xA983, 0xFE65, 0xA984, 0xFE66,\n\t0xA985, 0xFE68, 0xA986, 0xFE69, 0xA987, 0xFE6A, 0xA988, 0xFE6B,\n\t0xA996, 0x3007, 0xA9A4, 0x2500, 0xA9A5, 0x2501, 0xA9A6, 0x2502,\n\t0xA9A7, 0x2503, 0xA9A8, 0x2504, 0xA9A9, 0x2505, 0xA9AA, 0x2506,\n\t0xA9AB, 0x2507, 0xA9AC, 0x2508, 0xA9AD, 0x2509, 0xA9AE, 0x250A,\n\t0xA9AF, 0x250B, 0xA9B0, 0x250C, 0xA9B1, 0x250D, 0xA9B2, 0x250E,\n\t0xA9B3, 0x250F, 0xA9B4, 0x2510, 0xA9B5, 0x2511, 0xA9B6, 0x2512,\n\t0xA9B7, 0x2513, 0xA9B8, 0x2514, 0xA9B9, 0x2515, 0xA9BA, 0x2516,\n\t0xA9BB, 0x2517, 0xA9BC, 0x2518, 0xA9BD, 0x2519, 0xA9BE, 0x251A,\n\t0xA9BF, 0x251B, 0xA9C0, 0x251C, 0xA9C1, 0x251D, 0xA9C2, 0x251E,\n\t0xA9C3, 0x251F, 0xA9C4, 0x2520, 0xA9C5, 0x2521, 0xA9C6, 0x2522,\n\t0xA9C7, 0x2523, 0xA9C8, 0x2524, 0xA9C9, 0x2525, 0xA9CA, 0x2526,\n\t0xA9CB, 0x2527, 0xA9CC, 0x2528, 0xA9CD, 0x2529, 0xA9CE, 0x252A,\n\t0xA9CF, 0x252B, 0xA9D0, 0x252C, 0xA9D1, 0x252D, 0xA9D2, 0x252E,\n\t0xA9D3, 0x252F, 0xA9D4, 0x2530, 0xA9D5, 0x2531, 0xA9D6, 0x2532,\n\t0xA9D7, 0x2533, 0xA9D8, 0x2534, 0xA9D9, 0x2535, 0xA9DA, 0x2536,\n\t0xA9DB, 0x2537, 0xA9DC, 0x2538, 0xA9DD, 0x2539, 0xA9DE, 0x253A,\n\t0xA9DF, 0x253B, 0xA9E0, 0x253C, 0xA9E1, 0x253D, 0xA9E2, 0x253E,\n\t0xA9E3, 0x253F, 0xA9E4, 0x2540, 0xA9E5, 0x2541, 0xA9E6, 0x2542,\n\t0xA9E7, 0x2543, 0xA9E8, 0x2544, 0xA9E9, 0x2545, 0xA9EA, 0x2546,\n\t0xA9EB, 0x2547, 0xA9EC, 0x2548, 0xA9ED, 0x2549, 0xA9EE, 0x254A,\n\t0xA9EF, 0x254B, 0xAA40, 0x72DC, 0xAA41, 0x72DD, 0xAA42, 0x72DF,\n\t0xAA43, 0x72E2, 0xAA44, 0x72E3, 0xAA45, 0x72E4, 0xAA46, 0x72E5,\n\t0xAA47, 0x72E6, 0xAA48, 0x72E7, 0xAA49, 0x72EA, 0xAA4A, 0x72EB,\n\t0xAA4B, 0x72F5, 0xAA4C, 0x72F6, 0xAA4D, 0x72F9, 0xAA4E, 0x72FD,\n\t0xAA4F, 0x72FE, 0xAA50, 0x72FF, 0xAA51, 0x7300, 0xAA52, 0x7302,\n\t0xAA53, 0x7304, 0xAA54, 0x7305, 0xAA55, 0x7306, 0xAA56, 0x7307,\n\t0xAA57, 0x7308, 0xAA58, 0x7309, 0xAA59, 0x730B, 0xAA5A, 0x730C,\n\t0xAA5B, 0x730D, 0xAA5C, 0x730F, 0xAA5D, 0x7310, 0xAA5E, 0x7311,\n\t0xAA5F, 0x7312, 0xAA60, 0x7314, 0xAA61, 0x7318, 0xAA62, 0x7319,\n\t0xAA63, 0x731A, 0xAA64, 0x731F, 0xAA65, 0x7320, 0xAA66, 0x7323,\n\t0xAA67, 0x7324, 0xAA68, 0x7326, 0xAA69, 0x7327, 0xAA6A, 0x7328,\n\t0xAA6B, 0x732D, 0xAA6C, 0x732F, 0xAA6D, 0x7330, 0xAA6E, 0x7332,\n\t0xAA6F, 0x7333, 0xAA70, 0x7335, 0xAA71, 0x7336, 0xAA72, 0x733A,\n\t0xAA73, 0x733B, 0xAA74, 0x733C, 0xAA75, 0x733D, 0xAA76, 0x7340,\n\t0xAA77, 0x7341, 0xAA78, 0x7342, 0xAA79, 0x7343, 0xAA7A, 0x7344,\n\t0xAA7B, 0x7345, 0xAA7C, 0x7346, 0xAA7D, 0x7347, 0xAA7E, 0x7348,\n\t0xAA80, 0x7349, 0xAA81, 0x734A, 0xAA82, 0x734B, 0xAA83, 0x734C,\n\t0xAA84, 0x734E, 0xAA85, 0x734F, 0xAA86, 0x7351, 0xAA87, 0x7353,\n\t0xAA88, 0x7354, 0xAA89, 0x7355, 0xAA8A, 0x7356, 0xAA8B, 0x7358,\n\t0xAA8C, 0x7359, 0xAA8D, 0x735A, 0xAA8E, 0x735B, 0xAA8F, 0x735C,\n\t0xAA90, 0x735D, 0xAA91, 0x735E, 0xAA92, 0x735F, 0xAA93, 0x7361,\n\t0xAA94, 0x7362, 0xAA95, 0x7363, 0xAA96, 0x7364, 0xAA97, 0x7365,\n\t0xAA98, 0x7366, 0xAA99, 0x7367, 0xAA9A, 0x7368, 0xAA9B, 0x7369,\n\t0xAA9C, 0x736A, 0xAA9D, 0x736B, 0xAA9E, 0x736E, 0xAA9F, 0x7370,\n\t0xAAA0, 0x7371, 0xAB40, 0x7372, 0xAB41, 0x7373, 0xAB42, 0x7374,\n\t0xAB43, 0x7375, 0xAB44, 0x7376, 0xAB45, 0x7377, 0xAB46, 0x7378,\n\t0xAB47, 0x7379, 0xAB48, 0x737A, 0xAB49, 0x737B, 0xAB4A, 0x737C,\n\t0xAB4B, 0x737D, 0xAB4C, 0x737F, 0xAB4D, 0x7380, 0xAB4E, 0x7381,\n\t0xAB4F, 0x7382, 0xAB50, 0x7383, 0xAB51, 0x7385, 0xAB52, 0x7386,\n\t0xAB53, 0x7388, 0xAB54, 0x738A, 0xAB55, 0x738C, 0xAB56, 0x738D,\n\t0xAB57, 0x738F, 0xAB58, 0x7390, 0xAB59, 0x7392, 0xAB5A, 0x7393,\n\t0xAB5B, 0x7394, 0xAB5C, 0x7395, 0xAB5D, 0x7397, 0xAB5E, 0x7398,\n\t0xAB5F, 0x7399, 0xAB60, 0x739A, 0xAB61, 0x739C, 0xAB62, 0x739D,\n\t0xAB63, 0x739E, 0xAB64, 0x73A0, 0xAB65, 0x73A1, 0xAB66, 0x73A3,\n\t0xAB67, 0x73A4, 0xAB68, 0x73A5, 0xAB69, 0x73A6, 0xAB6A, 0x73A7,\n\t0xAB6B, 0x73A8, 0xAB6C, 0x73AA, 0xAB6D, 0x73AC, 0xAB6E, 0x73AD,\n\t0xAB6F, 0x73B1, 0xAB70, 0x73B4, 0xAB71, 0x73B5, 0xAB72, 0x73B6,\n\t0xAB73, 0x73B8, 0xAB74, 0x73B9, 0xAB75, 0x73BC, 0xAB76, 0x73BD,\n\t0xAB77, 0x73BE, 0xAB78, 0x73BF, 0xAB79, 0x73C1, 0xAB7A, 0x73C3,\n\t0xAB7B, 0x73C4, 0xAB7C, 0x73C5, 0xAB7D, 0x73C6, 0xAB7E, 0x73C7,\n\t0xAB80, 0x73CB, 0xAB81, 0x73CC, 0xAB82, 0x73CE, 0xAB83, 0x73D2,\n\t0xAB84, 0x73D3, 0xAB85, 0x73D4, 0xAB86, 0x73D5, 0xAB87, 0x73D6,\n\t0xAB88, 0x73D7, 0xAB89, 0x73D8, 0xAB8A, 0x73DA, 0xAB8B, 0x73DB,\n\t0xAB8C, 0x73DC, 0xAB8D, 0x73DD, 0xAB8E, 0x73DF, 0xAB8F, 0x73E1,\n\t0xAB90, 0x73E2, 0xAB91, 0x73E3, 0xAB92, 0x73E4, 0xAB93, 0x73E6,\n\t0xAB94, 0x73E8, 0xAB95, 0x73EA, 0xAB96, 0x73EB, 0xAB97, 0x73EC,\n\t0xAB98, 0x73EE, 0xAB99, 0x73EF, 0xAB9A, 0x73F0, 0xAB9B, 0x73F1,\n\t0xAB9C, 0x73F3, 0xAB9D, 0x73F4, 0xAB9E, 0x73F5, 0xAB9F, 0x73F6,\n\t0xABA0, 0x73F7, 0xAC40, 0x73F8, 0xAC41, 0x73F9, 0xAC42, 0x73FA,\n\t0xAC43, 0x73FB, 0xAC44, 0x73FC, 0xAC45, 0x73FD, 0xAC46, 0x73FE,\n\t0xAC47, 0x73FF, 0xAC48, 0x7400, 0xAC49, 0x7401, 0xAC4A, 0x7402,\n\t0xAC4B, 0x7404, 0xAC4C, 0x7407, 0xAC4D, 0x7408, 0xAC4E, 0x740B,\n\t0xAC4F, 0x740C, 0xAC50, 0x740D, 0xAC51, 0x740E, 0xAC52, 0x7411,\n\t0xAC53, 0x7412, 0xAC54, 0x7413, 0xAC55, 0x7414, 0xAC56, 0x7415,\n\t0xAC57, 0x7416, 0xAC58, 0x7417, 0xAC59, 0x7418, 0xAC5A, 0x7419,\n\t0xAC5B, 0x741C, 0xAC5C, 0x741D, 0xAC5D, 0x741E, 0xAC5E, 0x741F,\n\t0xAC5F, 0x7420, 0xAC60, 0x7421, 0xAC61, 0x7423, 0xAC62, 0x7424,\n\t0xAC63, 0x7427, 0xAC64, 0x7429, 0xAC65, 0x742B, 0xAC66, 0x742D,\n\t0xAC67, 0x742F, 0xAC68, 0x7431, 0xAC69, 0x7432, 0xAC6A, 0x7437,\n\t0xAC6B, 0x7438, 0xAC6C, 0x7439, 0xAC6D, 0x743A, 0xAC6E, 0x743B,\n\t0xAC6F, 0x743D, 0xAC70, 0x743E, 0xAC71, 0x743F, 0xAC72, 0x7440,\n\t0xAC73, 0x7442, 0xAC74, 0x7443, 0xAC75, 0x7444, 0xAC76, 0x7445,\n\t0xAC77, 0x7446, 0xAC78, 0x7447, 0xAC79, 0x7448, 0xAC7A, 0x7449,\n\t0xAC7B, 0x744A, 0xAC7C, 0x744B, 0xAC7D, 0x744C, 0xAC7E, 0x744D,\n\t0xAC80, 0x744E, 0xAC81, 0x744F, 0xAC82, 0x7450, 0xAC83, 0x7451,\n\t0xAC84, 0x7452, 0xAC85, 0x7453, 0xAC86, 0x7454, 0xAC87, 0x7456,\n\t0xAC88, 0x7458, 0xAC89, 0x745D, 0xAC8A, 0x7460, 0xAC8B, 0x7461,\n\t0xAC8C, 0x7462, 0xAC8D, 0x7463, 0xAC8E, 0x7464, 0xAC8F, 0x7465,\n\t0xAC90, 0x7466, 0xAC91, 0x7467, 0xAC92, 0x7468, 0xAC93, 0x7469,\n\t0xAC94, 0x746A, 0xAC95, 0x746B, 0xAC96, 0x746C, 0xAC97, 0x746E,\n\t0xAC98, 0x746F, 0xAC99, 0x7471, 0xAC9A, 0x7472, 0xAC9B, 0x7473,\n\t0xAC9C, 0x7474, 0xAC9D, 0x7475, 0xAC9E, 0x7478, 0xAC9F, 0x7479,\n\t0xACA0, 0x747A, 0xAD40, 0x747B, 0xAD41, 0x747C, 0xAD42, 0x747D,\n\t0xAD43, 0x747F, 0xAD44, 0x7482, 0xAD45, 0x7484, 0xAD46, 0x7485,\n\t0xAD47, 0x7486, 0xAD48, 0x7488, 0xAD49, 0x7489, 0xAD4A, 0x748A,\n\t0xAD4B, 0x748C, 0xAD4C, 0x748D, 0xAD4D, 0x748F, 0xAD4E, 0x7491,\n\t0xAD4F, 0x7492, 0xAD50, 0x7493, 0xAD51, 0x7494, 0xAD52, 0x7495,\n\t0xAD53, 0x7496, 0xAD54, 0x7497, 0xAD55, 0x7498, 0xAD56, 0x7499,\n\t0xAD57, 0x749A, 0xAD58, 0x749B, 0xAD59, 0x749D, 0xAD5A, 0x749F,\n\t0xAD5B, 0x74A0, 0xAD5C, 0x74A1, 0xAD5D, 0x74A2, 0xAD5E, 0x74A3,\n\t0xAD5F, 0x74A4, 0xAD60, 0x74A5, 0xAD61, 0x74A6, 0xAD62, 0x74AA,\n\t0xAD63, 0x74AB, 0xAD64, 0x74AC, 0xAD65, 0x74AD, 0xAD66, 0x74AE,\n\t0xAD67, 0x74AF, 0xAD68, 0x74B0, 0xAD69, 0x74B1, 0xAD6A, 0x74B2,\n\t0xAD6B, 0x74B3, 0xAD6C, 0x74B4, 0xAD6D, 0x74B5, 0xAD6E, 0x74B6,\n\t0xAD6F, 0x74B7, 0xAD70, 0x74B8, 0xAD71, 0x74B9, 0xAD72, 0x74BB,\n\t0xAD73, 0x74BC, 0xAD74, 0x74BD, 0xAD75, 0x74BE, 0xAD76, 0x74BF,\n\t0xAD77, 0x74C0, 0xAD78, 0x74C1, 0xAD79, 0x74C2, 0xAD7A, 0x74C3,\n\t0xAD7B, 0x74C4, 0xAD7C, 0x74C5, 0xAD7D, 0x74C6, 0xAD7E, 0x74C7,\n\t0xAD80, 0x74C8, 0xAD81, 0x74C9, 0xAD82, 0x74CA, 0xAD83, 0x74CB,\n\t0xAD84, 0x74CC, 0xAD85, 0x74CD, 0xAD86, 0x74CE, 0xAD87, 0x74CF,\n\t0xAD88, 0x74D0, 0xAD89, 0x74D1, 0xAD8A, 0x74D3, 0xAD8B, 0x74D4,\n\t0xAD8C, 0x74D5, 0xAD8D, 0x74D6, 0xAD8E, 0x74D7, 0xAD8F, 0x74D8,\n\t0xAD90, 0x74D9, 0xAD91, 0x74DA, 0xAD92, 0x74DB, 0xAD93, 0x74DD,\n\t0xAD94, 0x74DF, 0xAD95, 0x74E1, 0xAD96, 0x74E5, 0xAD97, 0x74E7,\n\t0xAD98, 0x74E8, 0xAD99, 0x74E9, 0xAD9A, 0x74EA, 0xAD9B, 0x74EB,\n\t0xAD9C, 0x74EC, 0xAD9D, 0x74ED, 0xAD9E, 0x74F0, 0xAD9F, 0x74F1,\n\t0xADA0, 0x74F2, 0xAE40, 0x74F3, 0xAE41, 0x74F5, 0xAE42, 0x74F8,\n\t0xAE43, 0x74F9, 0xAE44, 0x74FA, 0xAE45, 0x74FB, 0xAE46, 0x74FC,\n\t0xAE47, 0x74FD, 0xAE48, 0x74FE, 0xAE49, 0x7500, 0xAE4A, 0x7501,\n\t0xAE4B, 0x7502, 0xAE4C, 0x7503, 0xAE4D, 0x7505, 0xAE4E, 0x7506,\n\t0xAE4F, 0x7507, 0xAE50, 0x7508, 0xAE51, 0x7509, 0xAE52, 0x750A,\n\t0xAE53, 0x750B, 0xAE54, 0x750C, 0xAE55, 0x750E, 0xAE56, 0x7510,\n\t0xAE57, 0x7512, 0xAE58, 0x7514, 0xAE59, 0x7515, 0xAE5A, 0x7516,\n\t0xAE5B, 0x7517, 0xAE5C, 0x751B, 0xAE5D, 0x751D, 0xAE5E, 0x751E,\n\t0xAE5F, 0x7520, 0xAE60, 0x7521, 0xAE61, 0x7522, 0xAE62, 0x7523,\n\t0xAE63, 0x7524, 0xAE64, 0x7526, 0xAE65, 0x7527, 0xAE66, 0x752A,\n\t0xAE67, 0x752E, 0xAE68, 0x7534, 0xAE69, 0x7536, 0xAE6A, 0x7539,\n\t0xAE6B, 0x753C, 0xAE6C, 0x753D, 0xAE6D, 0x753F, 0xAE6E, 0x7541,\n\t0xAE6F, 0x7542, 0xAE70, 0x7543, 0xAE71, 0x7544, 0xAE72, 0x7546,\n\t0xAE73, 0x7547, 0xAE74, 0x7549, 0xAE75, 0x754A, 0xAE76, 0x754D,\n\t0xAE77, 0x7550, 0xAE78, 0x7551, 0xAE79, 0x7552, 0xAE7A, 0x7553,\n\t0xAE7B, 0x7555, 0xAE7C, 0x7556, 0xAE7D, 0x7557, 0xAE7E, 0x7558,\n\t0xAE80, 0x755D, 0xAE81, 0x755E, 0xAE82, 0x755F, 0xAE83, 0x7560,\n\t0xAE84, 0x7561, 0xAE85, 0x7562, 0xAE86, 0x7563, 0xAE87, 0x7564,\n\t0xAE88, 0x7567, 0xAE89, 0x7568, 0xAE8A, 0x7569, 0xAE8B, 0x756B,\n\t0xAE8C, 0x756C, 0xAE8D, 0x756D, 0xAE8E, 0x756E, 0xAE8F, 0x756F,\n\t0xAE90, 0x7570, 0xAE91, 0x7571, 0xAE92, 0x7573, 0xAE93, 0x7575,\n\t0xAE94, 0x7576, 0xAE95, 0x7577, 0xAE96, 0x757A, 0xAE97, 0x757B,\n\t0xAE98, 0x757C, 0xAE99, 0x757D, 0xAE9A, 0x757E, 0xAE9B, 0x7580,\n\t0xAE9C, 0x7581, 0xAE9D, 0x7582, 0xAE9E, 0x7584, 0xAE9F, 0x7585,\n\t0xAEA0, 0x7587, 0xAF40, 0x7588, 0xAF41, 0x7589, 0xAF42, 0x758A,\n\t0xAF43, 0x758C, 0xAF44, 0x758D, 0xAF45, 0x758E, 0xAF46, 0x7590,\n\t0xAF47, 0x7593, 0xAF48, 0x7595, 0xAF49, 0x7598, 0xAF4A, 0x759B,\n\t0xAF4B, 0x759C, 0xAF4C, 0x759E, 0xAF4D, 0x75A2, 0xAF4E, 0x75A6,\n\t0xAF4F, 0x75A7, 0xAF50, 0x75A8, 0xAF51, 0x75A9, 0xAF52, 0x75AA,\n\t0xAF53, 0x75AD, 0xAF54, 0x75B6, 0xAF55, 0x75B7, 0xAF56, 0x75BA,\n\t0xAF57, 0x75BB, 0xAF58, 0x75BF, 0xAF59, 0x75C0, 0xAF5A, 0x75C1,\n\t0xAF5B, 0x75C6, 0xAF5C, 0x75CB, 0xAF5D, 0x75CC, 0xAF5E, 0x75CE,\n\t0xAF5F, 0x75CF, 0xAF60, 0x75D0, 0xAF61, 0x75D1, 0xAF62, 0x75D3,\n\t0xAF63, 0x75D7, 0xAF64, 0x75D9, 0xAF65, 0x75DA, 0xAF66, 0x75DC,\n\t0xAF67, 0x75DD, 0xAF68, 0x75DF, 0xAF69, 0x75E0, 0xAF6A, 0x75E1,\n\t0xAF6B, 0x75E5, 0xAF6C, 0x75E9, 0xAF6D, 0x75EC, 0xAF6E, 0x75ED,\n\t0xAF6F, 0x75EE, 0xAF70, 0x75EF, 0xAF71, 0x75F2, 0xAF72, 0x75F3,\n\t0xAF73, 0x75F5, 0xAF74, 0x75F6, 0xAF75, 0x75F7, 0xAF76, 0x75F8,\n\t0xAF77, 0x75FA, 0xAF78, 0x75FB, 0xAF79, 0x75FD, 0xAF7A, 0x75FE,\n\t0xAF7B, 0x7602, 0xAF7C, 0x7604, 0xAF7D, 0x7606, 0xAF7E, 0x7607,\n\t0xAF80, 0x7608, 0xAF81, 0x7609, 0xAF82, 0x760B, 0xAF83, 0x760D,\n\t0xAF84, 0x760E, 0xAF85, 0x760F, 0xAF86, 0x7611, 0xAF87, 0x7612,\n\t0xAF88, 0x7613, 0xAF89, 0x7614, 0xAF8A, 0x7616, 0xAF8B, 0x761A,\n\t0xAF8C, 0x761C, 0xAF8D, 0x761D, 0xAF8E, 0x761E, 0xAF8F, 0x7621,\n\t0xAF90, 0x7623, 0xAF91, 0x7627, 0xAF92, 0x7628, 0xAF93, 0x762C,\n\t0xAF94, 0x762E, 0xAF95, 0x762F, 0xAF96, 0x7631, 0xAF97, 0x7632,\n\t0xAF98, 0x7636, 0xAF99, 0x7637, 0xAF9A, 0x7639, 0xAF9B, 0x763A,\n\t0xAF9C, 0x763B, 0xAF9D, 0x763D, 0xAF9E, 0x7641, 0xAF9F, 0x7642,\n\t0xAFA0, 0x7644, 0xB040, 0x7645, 0xB041, 0x7646, 0xB042, 0x7647,\n\t0xB043, 0x7648, 0xB044, 0x7649, 0xB045, 0x764A, 0xB046, 0x764B,\n\t0xB047, 0x764E, 0xB048, 0x764F, 0xB049, 0x7650, 0xB04A, 0x7651,\n\t0xB04B, 0x7652, 0xB04C, 0x7653, 0xB04D, 0x7655, 0xB04E, 0x7657,\n\t0xB04F, 0x7658, 0xB050, 0x7659, 0xB051, 0x765A, 0xB052, 0x765B,\n\t0xB053, 0x765D, 0xB054, 0x765F, 0xB055, 0x7660, 0xB056, 0x7661,\n\t0xB057, 0x7662, 0xB058, 0x7664, 0xB059, 0x7665, 0xB05A, 0x7666,\n\t0xB05B, 0x7667, 0xB05C, 0x7668, 0xB05D, 0x7669, 0xB05E, 0x766A,\n\t0xB05F, 0x766C, 0xB060, 0x766D, 0xB061, 0x766E, 0xB062, 0x7670,\n\t0xB063, 0x7671, 0xB064, 0x7672, 0xB065, 0x7673, 0xB066, 0x7674,\n\t0xB067, 0x7675, 0xB068, 0x7676, 0xB069, 0x7677, 0xB06A, 0x7679,\n\t0xB06B, 0x767A, 0xB06C, 0x767C, 0xB06D, 0x767F, 0xB06E, 0x7680,\n\t0xB06F, 0x7681, 0xB070, 0x7683, 0xB071, 0x7685, 0xB072, 0x7689,\n\t0xB073, 0x768A, 0xB074, 0x768C, 0xB075, 0x768D, 0xB076, 0x768F,\n\t0xB077, 0x7690, 0xB078, 0x7692, 0xB079, 0x7694, 0xB07A, 0x7695,\n\t0xB07B, 0x7697, 0xB07C, 0x7698, 0xB07D, 0x769A, 0xB07E, 0x769B,\n\t0xB080, 0x769C, 0xB081, 0x769D, 0xB082, 0x769E, 0xB083, 0x769F,\n\t0xB084, 0x76A0, 0xB085, 0x76A1, 0xB086, 0x76A2, 0xB087, 0x76A3,\n\t0xB088, 0x76A5, 0xB089, 0x76A6, 0xB08A, 0x76A7, 0xB08B, 0x76A8,\n\t0xB08C, 0x76A9, 0xB08D, 0x76AA, 0xB08E, 0x76AB, 0xB08F, 0x76AC,\n\t0xB090, 0x76AD, 0xB091, 0x76AF, 0xB092, 0x76B0, 0xB093, 0x76B3,\n\t0xB094, 0x76B5, 0xB095, 0x76B6, 0xB096, 0x76B7, 0xB097, 0x76B8,\n\t0xB098, 0x76B9, 0xB099, 0x76BA, 0xB09A, 0x76BB, 0xB09B, 0x76BC,\n\t0xB09C, 0x76BD, 0xB09D, 0x76BE, 0xB09E, 0x76C0, 0xB09F, 0x76C1,\n\t0xB0A0, 0x76C3, 0xB0A1, 0x554A, 0xB0A2, 0x963F, 0xB0A3, 0x57C3,\n\t0xB0A4, 0x6328, 0xB0A5, 0x54CE, 0xB0A6, 0x5509, 0xB0A7, 0x54C0,\n\t0xB0A8, 0x7691, 0xB0A9, 0x764C, 0xB0AA, 0x853C, 0xB0AB, 0x77EE,\n\t0xB0AC, 0x827E, 0xB0AD, 0x788D, 0xB0AE, 0x7231, 0xB0AF, 0x9698,\n\t0xB0B0, 0x978D, 0xB0B1, 0x6C28, 0xB0B2, 0x5B89, 0xB0B3, 0x4FFA,\n\t0xB0B4, 0x6309, 0xB0B5, 0x6697, 0xB0B6, 0x5CB8, 0xB0B7, 0x80FA,\n\t0xB0B8, 0x6848, 0xB0B9, 0x80AE, 0xB0BA, 0x6602, 0xB0BB, 0x76CE,\n\t0xB0BC, 0x51F9, 0xB0BD, 0x6556, 0xB0BE, 0x71AC, 0xB0BF, 0x7FF1,\n\t0xB0C0, 0x8884, 0xB0C1, 0x50B2, 0xB0C2, 0x5965, 0xB0C3, 0x61CA,\n\t0xB0C4, 0x6FB3, 0xB0C5, 0x82AD, 0xB0C6, 0x634C, 0xB0C7, 0x6252,\n\t0xB0C8, 0x53ED, 0xB0C9, 0x5427, 0xB0CA, 0x7B06, 0xB0CB, 0x516B,\n\t0xB0CC, 0x75A4, 0xB0CD, 0x5DF4, 0xB0CE, 0x62D4, 0xB0CF, 0x8DCB,\n\t0xB0D0, 0x9776, 0xB0D1, 0x628A, 0xB0D2, 0x8019, 0xB0D3, 0x575D,\n\t0xB0D4, 0x9738, 0xB0D5, 0x7F62, 0xB0D6, 0x7238, 0xB0D7, 0x767D,\n\t0xB0D8, 0x67CF, 0xB0D9, 0x767E, 0xB0DA, 0x6446, 0xB0DB, 0x4F70,\n\t0xB0DC, 0x8D25, 0xB0DD, 0x62DC, 0xB0DE, 0x7A17, 0xB0DF, 0x6591,\n\t0xB0E0, 0x73ED, 0xB0E1, 0x642C, 0xB0E2, 0x6273, 0xB0E3, 0x822C,\n\t0xB0E4, 0x9881, 0xB0E5, 0x677F, 0xB0E6, 0x7248, 0xB0E7, 0x626E,\n\t0xB0E8, 0x62CC, 0xB0E9, 0x4F34, 0xB0EA, 0x74E3, 0xB0EB, 0x534A,\n\t0xB0EC, 0x529E, 0xB0ED, 0x7ECA, 0xB0EE, 0x90A6, 0xB0EF, 0x5E2E,\n\t0xB0F0, 0x6886, 0xB0F1, 0x699C, 0xB0F2, 0x8180, 0xB0F3, 0x7ED1,\n\t0xB0F4, 0x68D2, 0xB0F5, 0x78C5, 0xB0F6, 0x868C, 0xB0F7, 0x9551,\n\t0xB0F8, 0x508D, 0xB0F9, 0x8C24, 0xB0FA, 0x82DE, 0xB0FB, 0x80DE,\n\t0xB0FC, 0x5305, 0xB0FD, 0x8912, 0xB0FE, 0x5265, 0xB140, 0x76C4,\n\t0xB141, 0x76C7, 0xB142, 0x76C9, 0xB143, 0x76CB, 0xB144, 0x76CC,\n\t0xB145, 0x76D3, 0xB146, 0x76D5, 0xB147, 0x76D9, 0xB148, 0x76DA,\n\t0xB149, 0x76DC, 0xB14A, 0x76DD, 0xB14B, 0x76DE, 0xB14C, 0x76E0,\n\t0xB14D, 0x76E1, 0xB14E, 0x76E2, 0xB14F, 0x76E3, 0xB150, 0x76E4,\n\t0xB151, 0x76E6, 0xB152, 0x76E7, 0xB153, 0x76E8, 0xB154, 0x76E9,\n\t0xB155, 0x76EA, 0xB156, 0x76EB, 0xB157, 0x76EC, 0xB158, 0x76ED,\n\t0xB159, 0x76F0, 0xB15A, 0x76F3, 0xB15B, 0x76F5, 0xB15C, 0x76F6,\n\t0xB15D, 0x76F7, 0xB15E, 0x76FA, 0xB15F, 0x76FB, 0xB160, 0x76FD,\n\t0xB161, 0x76FF, 0xB162, 0x7700, 0xB163, 0x7702, 0xB164, 0x7703,\n\t0xB165, 0x7705, 0xB166, 0x7706, 0xB167, 0x770A, 0xB168, 0x770C,\n\t0xB169, 0x770E, 0xB16A, 0x770F, 0xB16B, 0x7710, 0xB16C, 0x7711,\n\t0xB16D, 0x7712, 0xB16E, 0x7713, 0xB16F, 0x7714, 0xB170, 0x7715,\n\t0xB171, 0x7716, 0xB172, 0x7717, 0xB173, 0x7718, 0xB174, 0x771B,\n\t0xB175, 0x771C, 0xB176, 0x771D, 0xB177, 0x771E, 0xB178, 0x7721,\n\t0xB179, 0x7723, 0xB17A, 0x7724, 0xB17B, 0x7725, 0xB17C, 0x7727,\n\t0xB17D, 0x772A, 0xB17E, 0x772B, 0xB180, 0x772C, 0xB181, 0x772E,\n\t0xB182, 0x7730, 0xB183, 0x7731, 0xB184, 0x7732, 0xB185, 0x7733,\n\t0xB186, 0x7734, 0xB187, 0x7739, 0xB188, 0x773B, 0xB189, 0x773D,\n\t0xB18A, 0x773E, 0xB18B, 0x773F, 0xB18C, 0x7742, 0xB18D, 0x7744,\n\t0xB18E, 0x7745, 0xB18F, 0x7746, 0xB190, 0x7748, 0xB191, 0x7749,\n\t0xB192, 0x774A, 0xB193, 0x774B, 0xB194, 0x774C, 0xB195, 0x774D,\n\t0xB196, 0x774E, 0xB197, 0x774F, 0xB198, 0x7752, 0xB199, 0x7753,\n\t0xB19A, 0x7754, 0xB19B, 0x7755, 0xB19C, 0x7756, 0xB19D, 0x7757,\n\t0xB19E, 0x7758, 0xB19F, 0x7759, 0xB1A0, 0x775C, 0xB1A1, 0x8584,\n\t0xB1A2, 0x96F9, 0xB1A3, 0x4FDD, 0xB1A4, 0x5821, 0xB1A5, 0x9971,\n\t0xB1A6, 0x5B9D, 0xB1A7, 0x62B1, 0xB1A8, 0x62A5, 0xB1A9, 0x66B4,\n\t0xB1AA, 0x8C79, 0xB1AB, 0x9C8D, 0xB1AC, 0x7206, 0xB1AD, 0x676F,\n\t0xB1AE, 0x7891, 0xB1AF, 0x60B2, 0xB1B0, 0x5351, 0xB1B1, 0x5317,\n\t0xB1B2, 0x8F88, 0xB1B3, 0x80CC, 0xB1B4, 0x8D1D, 0xB1B5, 0x94A1,\n\t0xB1B6, 0x500D, 0xB1B7, 0x72C8, 0xB1B8, 0x5907, 0xB1B9, 0x60EB,\n\t0xB1BA, 0x7119, 0xB1BB, 0x88AB, 0xB1BC, 0x5954, 0xB1BD, 0x82EF,\n\t0xB1BE, 0x672C, 0xB1BF, 0x7B28, 0xB1C0, 0x5D29, 0xB1C1, 0x7EF7,\n\t0xB1C2, 0x752D, 0xB1C3, 0x6CF5, 0xB1C4, 0x8E66, 0xB1C5, 0x8FF8,\n\t0xB1C6, 0x903C, 0xB1C7, 0x9F3B, 0xB1C8, 0x6BD4, 0xB1C9, 0x9119,\n\t0xB1CA, 0x7B14, 0xB1CB, 0x5F7C, 0xB1CC, 0x78A7, 0xB1CD, 0x84D6,\n\t0xB1CE, 0x853D, 0xB1CF, 0x6BD5, 0xB1D0, 0x6BD9, 0xB1D1, 0x6BD6,\n\t0xB1D2, 0x5E01, 0xB1D3, 0x5E87, 0xB1D4, 0x75F9, 0xB1D5, 0x95ED,\n\t0xB1D6, 0x655D, 0xB1D7, 0x5F0A, 0xB1D8, 0x5FC5, 0xB1D9, 0x8F9F,\n\t0xB1DA, 0x58C1, 0xB1DB, 0x81C2, 0xB1DC, 0x907F, 0xB1DD, 0x965B,\n\t0xB1DE, 0x97AD, 0xB1DF, 0x8FB9, 0xB1E0, 0x7F16, 0xB1E1, 0x8D2C,\n\t0xB1E2, 0x6241, 0xB1E3, 0x4FBF, 0xB1E4, 0x53D8, 0xB1E5, 0x535E,\n\t0xB1E6, 0x8FA8, 0xB1E7, 0x8FA9, 0xB1E8, 0x8FAB, 0xB1E9, 0x904D,\n\t0xB1EA, 0x6807, 0xB1EB, 0x5F6A, 0xB1EC, 0x8198, 0xB1ED, 0x8868,\n\t0xB1EE, 0x9CD6, 0xB1EF, 0x618B, 0xB1F0, 0x522B, 0xB1F1, 0x762A,\n\t0xB1F2, 0x5F6C, 0xB1F3, 0x658C, 0xB1F4, 0x6FD2, 0xB1F5, 0x6EE8,\n\t0xB1F6, 0x5BBE, 0xB1F7, 0x6448, 0xB1F8, 0x5175, 0xB1F9, 0x51B0,\n\t0xB1FA, 0x67C4, 0xB1FB, 0x4E19, 0xB1FC, 0x79C9, 0xB1FD, 0x997C,\n\t0xB1FE, 0x70B3, 0xB240, 0x775D, 0xB241, 0x775E, 0xB242, 0x775F,\n\t0xB243, 0x7760, 0xB244, 0x7764, 0xB245, 0x7767, 0xB246, 0x7769,\n\t0xB247, 0x776A, 0xB248, 0x776D, 0xB249, 0x776E, 0xB24A, 0x776F,\n\t0xB24B, 0x7770, 0xB24C, 0x7771, 0xB24D, 0x7772, 0xB24E, 0x7773,\n\t0xB24F, 0x7774, 0xB250, 0x7775, 0xB251, 0x7776, 0xB252, 0x7777,\n\t0xB253, 0x7778, 0xB254, 0x777A, 0xB255, 0x777B, 0xB256, 0x777C,\n\t0xB257, 0x7781, 0xB258, 0x7782, 0xB259, 0x7783, 0xB25A, 0x7786,\n\t0xB25B, 0x7787, 0xB25C, 0x7788, 0xB25D, 0x7789, 0xB25E, 0x778A,\n\t0xB25F, 0x778B, 0xB260, 0x778F, 0xB261, 0x7790, 0xB262, 0x7793,\n\t0xB263, 0x7794, 0xB264, 0x7795, 0xB265, 0x7796, 0xB266, 0x7797,\n\t0xB267, 0x7798, 0xB268, 0x7799, 0xB269, 0x779A, 0xB26A, 0x779B,\n\t0xB26B, 0x779C, 0xB26C, 0x779D, 0xB26D, 0x779E, 0xB26E, 0x77A1,\n\t0xB26F, 0x77A3, 0xB270, 0x77A4, 0xB271, 0x77A6, 0xB272, 0x77A8,\n\t0xB273, 0x77AB, 0xB274, 0x77AD, 0xB275, 0x77AE, 0xB276, 0x77AF,\n\t0xB277, 0x77B1, 0xB278, 0x77B2, 0xB279, 0x77B4, 0xB27A, 0x77B6,\n\t0xB27B, 0x77B7, 0xB27C, 0x77B8, 0xB27D, 0x77B9, 0xB27E, 0x77BA,\n\t0xB280, 0x77BC, 0xB281, 0x77BE, 0xB282, 0x77C0, 0xB283, 0x77C1,\n\t0xB284, 0x77C2, 0xB285, 0x77C3, 0xB286, 0x77C4, 0xB287, 0x77C5,\n\t0xB288, 0x77C6, 0xB289, 0x77C7, 0xB28A, 0x77C8, 0xB28B, 0x77C9,\n\t0xB28C, 0x77CA, 0xB28D, 0x77CB, 0xB28E, 0x77CC, 0xB28F, 0x77CE,\n\t0xB290, 0x77CF, 0xB291, 0x77D0, 0xB292, 0x77D1, 0xB293, 0x77D2,\n\t0xB294, 0x77D3, 0xB295, 0x77D4, 0xB296, 0x77D5, 0xB297, 0x77D6,\n\t0xB298, 0x77D8, 0xB299, 0x77D9, 0xB29A, 0x77DA, 0xB29B, 0x77DD,\n\t0xB29C, 0x77DE, 0xB29D, 0x77DF, 0xB29E, 0x77E0, 0xB29F, 0x77E1,\n\t0xB2A0, 0x77E4, 0xB2A1, 0x75C5, 0xB2A2, 0x5E76, 0xB2A3, 0x73BB,\n\t0xB2A4, 0x83E0, 0xB2A5, 0x64AD, 0xB2A6, 0x62E8, 0xB2A7, 0x94B5,\n\t0xB2A8, 0x6CE2, 0xB2A9, 0x535A, 0xB2AA, 0x52C3, 0xB2AB, 0x640F,\n\t0xB2AC, 0x94C2, 0xB2AD, 0x7B94, 0xB2AE, 0x4F2F, 0xB2AF, 0x5E1B,\n\t0xB2B0, 0x8236, 0xB2B1, 0x8116, 0xB2B2, 0x818A, 0xB2B3, 0x6E24,\n\t0xB2B4, 0x6CCA, 0xB2B5, 0x9A73, 0xB2B6, 0x6355, 0xB2B7, 0x535C,\n\t0xB2B8, 0x54FA, 0xB2B9, 0x8865, 0xB2BA, 0x57E0, 0xB2BB, 0x4E0D,\n\t0xB2BC, 0x5E03, 0xB2BD, 0x6B65, 0xB2BE, 0x7C3F, 0xB2BF, 0x90E8,\n\t0xB2C0, 0x6016, 0xB2C1, 0x64E6, 0xB2C2, 0x731C, 0xB2C3, 0x88C1,\n\t0xB2C4, 0x6750, 0xB2C5, 0x624D, 0xB2C6, 0x8D22, 0xB2C7, 0x776C,\n\t0xB2C8, 0x8E29, 0xB2C9, 0x91C7, 0xB2CA, 0x5F69, 0xB2CB, 0x83DC,\n\t0xB2CC, 0x8521, 0xB2CD, 0x9910, 0xB2CE, 0x53C2, 0xB2CF, 0x8695,\n\t0xB2D0, 0x6B8B, 0xB2D1, 0x60ED, 0xB2D2, 0x60E8, 0xB2D3, 0x707F,\n\t0xB2D4, 0x82CD, 0xB2D5, 0x8231, 0xB2D6, 0x4ED3, 0xB2D7, 0x6CA7,\n\t0xB2D8, 0x85CF, 0xB2D9, 0x64CD, 0xB2DA, 0x7CD9, 0xB2DB, 0x69FD,\n\t0xB2DC, 0x66F9, 0xB2DD, 0x8349, 0xB2DE, 0x5395, 0xB2DF, 0x7B56,\n\t0xB2E0, 0x4FA7, 0xB2E1, 0x518C, 0xB2E2, 0x6D4B, 0xB2E3, 0x5C42,\n\t0xB2E4, 0x8E6D, 0xB2E5, 0x63D2, 0xB2E6, 0x53C9, 0xB2E7, 0x832C,\n\t0xB2E8, 0x8336, 0xB2E9, 0x67E5, 0xB2EA, 0x78B4, 0xB2EB, 0x643D,\n\t0xB2EC, 0x5BDF, 0xB2ED, 0x5C94, 0xB2EE, 0x5DEE, 0xB2EF, 0x8BE7,\n\t0xB2F0, 0x62C6, 0xB2F1, 0x67F4, 0xB2F2, 0x8C7A, 0xB2F3, 0x6400,\n\t0xB2F4, 0x63BA, 0xB2F5, 0x8749, 0xB2F6, 0x998B, 0xB2F7, 0x8C17,\n\t0xB2F8, 0x7F20, 0xB2F9, 0x94F2, 0xB2FA, 0x4EA7, 0xB2FB, 0x9610,\n\t0xB2FC, 0x98A4, 0xB2FD, 0x660C, 0xB2FE, 0x7316, 0xB340, 0x77E6,\n\t0xB341, 0x77E8, 0xB342, 0x77EA, 0xB343, 0x77EF, 0xB344, 0x77F0,\n\t0xB345, 0x77F1, 0xB346, 0x77F2, 0xB347, 0x77F4, 0xB348, 0x77F5,\n\t0xB349, 0x77F7, 0xB34A, 0x77F9, 0xB34B, 0x77FA, 0xB34C, 0x77FB,\n\t0xB34D, 0x77FC, 0xB34E, 0x7803, 0xB34F, 0x7804, 0xB350, 0x7805,\n\t0xB351, 0x7806, 0xB352, 0x7807, 0xB353, 0x7808, 0xB354, 0x780A,\n\t0xB355, 0x780B, 0xB356, 0x780E, 0xB357, 0x780F, 0xB358, 0x7810,\n\t0xB359, 0x7813, 0xB35A, 0x7815, 0xB35B, 0x7819, 0xB35C, 0x781B,\n\t0xB35D, 0x781E, 0xB35E, 0x7820, 0xB35F, 0x7821, 0xB360, 0x7822,\n\t0xB361, 0x7824, 0xB362, 0x7828, 0xB363, 0x782A, 0xB364, 0x782B,\n\t0xB365, 0x782E, 0xB366, 0x782F, 0xB367, 0x7831, 0xB368, 0x7832,\n\t0xB369, 0x7833, 0xB36A, 0x7835, 0xB36B, 0x7836, 0xB36C, 0x783D,\n\t0xB36D, 0x783F, 0xB36E, 0x7841, 0xB36F, 0x7842, 0xB370, 0x7843,\n\t0xB371, 0x7844, 0xB372, 0x7846, 0xB373, 0x7848, 0xB374, 0x7849,\n\t0xB375, 0x784A, 0xB376, 0x784B, 0xB377, 0x784D, 0xB378, 0x784F,\n\t0xB379, 0x7851, 0xB37A, 0x7853, 0xB37B, 0x7854, 0xB37C, 0x7858,\n\t0xB37D, 0x7859, 0xB37E, 0x785A, 0xB380, 0x785B, 0xB381, 0x785C,\n\t0xB382, 0x785E, 0xB383, 0x785F, 0xB384, 0x7860, 0xB385, 0x7861,\n\t0xB386, 0x7862, 0xB387, 0x7863, 0xB388, 0x7864, 0xB389, 0x7865,\n\t0xB38A, 0x7866, 0xB38B, 0x7867, 0xB38C, 0x7868, 0xB38D, 0x7869,\n\t0xB38E, 0x786F, 0xB38F, 0x7870, 0xB390, 0x7871, 0xB391, 0x7872,\n\t0xB392, 0x7873, 0xB393, 0x7874, 0xB394, 0x7875, 0xB395, 0x7876,\n\t0xB396, 0x7878, 0xB397, 0x7879, 0xB398, 0x787A, 0xB399, 0x787B,\n\t0xB39A, 0x787D, 0xB39B, 0x787E, 0xB39C, 0x787F, 0xB39D, 0x7880,\n\t0xB39E, 0x7881, 0xB39F, 0x7882, 0xB3A0, 0x7883, 0xB3A1, 0x573A,\n\t0xB3A2, 0x5C1D, 0xB3A3, 0x5E38, 0xB3A4, 0x957F, 0xB3A5, 0x507F,\n\t0xB3A6, 0x80A0, 0xB3A7, 0x5382, 0xB3A8, 0x655E, 0xB3A9, 0x7545,\n\t0xB3AA, 0x5531, 0xB3AB, 0x5021, 0xB3AC, 0x8D85, 0xB3AD, 0x6284,\n\t0xB3AE, 0x949E, 0xB3AF, 0x671D, 0xB3B0, 0x5632, 0xB3B1, 0x6F6E,\n\t0xB3B2, 0x5DE2, 0xB3B3, 0x5435, 0xB3B4, 0x7092, 0xB3B5, 0x8F66,\n\t0xB3B6, 0x626F, 0xB3B7, 0x64A4, 0xB3B8, 0x63A3, 0xB3B9, 0x5F7B,\n\t0xB3BA, 0x6F88, 0xB3BB, 0x90F4, 0xB3BC, 0x81E3, 0xB3BD, 0x8FB0,\n\t0xB3BE, 0x5C18, 0xB3BF, 0x6668, 0xB3C0, 0x5FF1, 0xB3C1, 0x6C89,\n\t0xB3C2, 0x9648, 0xB3C3, 0x8D81, 0xB3C4, 0x886C, 0xB3C5, 0x6491,\n\t0xB3C6, 0x79F0, 0xB3C7, 0x57CE, 0xB3C8, 0x6A59, 0xB3C9, 0x6210,\n\t0xB3CA, 0x5448, 0xB3CB, 0x4E58, 0xB3CC, 0x7A0B, 0xB3CD, 0x60E9,\n\t0xB3CE, 0x6F84, 0xB3CF, 0x8BDA, 0xB3D0, 0x627F, 0xB3D1, 0x901E,\n\t0xB3D2, 0x9A8B, 0xB3D3, 0x79E4, 0xB3D4, 0x5403, 0xB3D5, 0x75F4,\n\t0xB3D6, 0x6301, 0xB3D7, 0x5319, 0xB3D8, 0x6C60, 0xB3D9, 0x8FDF,\n\t0xB3DA, 0x5F1B, 0xB3DB, 0x9A70, 0xB3DC, 0x803B, 0xB3DD, 0x9F7F,\n\t0xB3DE, 0x4F88, 0xB3DF, 0x5C3A, 0xB3E0, 0x8D64, 0xB3E1, 0x7FC5,\n\t0xB3E2, 0x65A5, 0xB3E3, 0x70BD, 0xB3E4, 0x5145, 0xB3E5, 0x51B2,\n\t0xB3E6, 0x866B, 0xB3E7, 0x5D07, 0xB3E8, 0x5BA0, 0xB3E9, 0x62BD,\n\t0xB3EA, 0x916C, 0xB3EB, 0x7574, 0xB3EC, 0x8E0C, 0xB3ED, 0x7A20,\n\t0xB3EE, 0x6101, 0xB3EF, 0x7B79, 0xB3F0, 0x4EC7, 0xB3F1, 0x7EF8,\n\t0xB3F2, 0x7785, 0xB3F3, 0x4E11, 0xB3F4, 0x81ED, 0xB3F5, 0x521D,\n\t0xB3F6, 0x51FA, 0xB3F7, 0x6A71, 0xB3F8, 0x53A8, 0xB3F9, 0x8E87,\n\t0xB3FA, 0x9504, 0xB3FB, 0x96CF, 0xB3FC, 0x6EC1, 0xB3FD, 0x9664,\n\t0xB3FE, 0x695A, 0xB440, 0x7884, 0xB441, 0x7885, 0xB442, 0x7886,\n\t0xB443, 0x7888, 0xB444, 0x788A, 0xB445, 0x788B, 0xB446, 0x788F,\n\t0xB447, 0x7890, 0xB448, 0x7892, 0xB449, 0x7894, 0xB44A, 0x7895,\n\t0xB44B, 0x7896, 0xB44C, 0x7899, 0xB44D, 0x789D, 0xB44E, 0x789E,\n\t0xB44F, 0x78A0, 0xB450, 0x78A2, 0xB451, 0x78A4, 0xB452, 0x78A6,\n\t0xB453, 0x78A8, 0xB454, 0x78A9, 0xB455, 0x78AA, 0xB456, 0x78AB,\n\t0xB457, 0x78AC, 0xB458, 0x78AD, 0xB459, 0x78AE, 0xB45A, 0x78AF,\n\t0xB45B, 0x78B5, 0xB45C, 0x78B6, 0xB45D, 0x78B7, 0xB45E, 0x78B8,\n\t0xB45F, 0x78BA, 0xB460, 0x78BB, 0xB461, 0x78BC, 0xB462, 0x78BD,\n\t0xB463, 0x78BF, 0xB464, 0x78C0, 0xB465, 0x78C2, 0xB466, 0x78C3,\n\t0xB467, 0x78C4, 0xB468, 0x78C6, 0xB469, 0x78C7, 0xB46A, 0x78C8,\n\t0xB46B, 0x78CC, 0xB46C, 0x78CD, 0xB46D, 0x78CE, 0xB46E, 0x78CF,\n\t0xB46F, 0x78D1, 0xB470, 0x78D2, 0xB471, 0x78D3, 0xB472, 0x78D6,\n\t0xB473, 0x78D7, 0xB474, 0x78D8, 0xB475, 0x78DA, 0xB476, 0x78DB,\n\t0xB477, 0x78DC, 0xB478, 0x78DD, 0xB479, 0x78DE, 0xB47A, 0x78DF,\n\t0xB47B, 0x78E0, 0xB47C, 0x78E1, 0xB47D, 0x78E2, 0xB47E, 0x78E3,\n\t0xB480, 0x78E4, 0xB481, 0x78E5, 0xB482, 0x78E6, 0xB483, 0x78E7,\n\t0xB484, 0x78E9, 0xB485, 0x78EA, 0xB486, 0x78EB, 0xB487, 0x78ED,\n\t0xB488, 0x78EE, 0xB489, 0x78EF, 0xB48A, 0x78F0, 0xB48B, 0x78F1,\n\t0xB48C, 0x78F3, 0xB48D, 0x78F5, 0xB48E, 0x78F6, 0xB48F, 0x78F8,\n\t0xB490, 0x78F9, 0xB491, 0x78FB, 0xB492, 0x78FC, 0xB493, 0x78FD,\n\t0xB494, 0x78FE, 0xB495, 0x78FF, 0xB496, 0x7900, 0xB497, 0x7902,\n\t0xB498, 0x7903, 0xB499, 0x7904, 0xB49A, 0x7906, 0xB49B, 0x7907,\n\t0xB49C, 0x7908, 0xB49D, 0x7909, 0xB49E, 0x790A, 0xB49F, 0x790B,\n\t0xB4A0, 0x790C, 0xB4A1, 0x7840, 0xB4A2, 0x50A8, 0xB4A3, 0x77D7,\n\t0xB4A4, 0x6410, 0xB4A5, 0x89E6, 0xB4A6, 0x5904, 0xB4A7, 0x63E3,\n\t0xB4A8, 0x5DDD, 0xB4A9, 0x7A7F, 0xB4AA, 0x693D, 0xB4AB, 0x4F20,\n\t0xB4AC, 0x8239, 0xB4AD, 0x5598, 0xB4AE, 0x4E32, 0xB4AF, 0x75AE,\n\t0xB4B0, 0x7A97, 0xB4B1, 0x5E62, 0xB4B2, 0x5E8A, 0xB4B3, 0x95EF,\n\t0xB4B4, 0x521B, 0xB4B5, 0x5439, 0xB4B6, 0x708A, 0xB4B7, 0x6376,\n\t0xB4B8, 0x9524, 0xB4B9, 0x5782, 0xB4BA, 0x6625, 0xB4BB, 0x693F,\n\t0xB4BC, 0x9187, 0xB4BD, 0x5507, 0xB4BE, 0x6DF3, 0xB4BF, 0x7EAF,\n\t0xB4C0, 0x8822, 0xB4C1, 0x6233, 0xB4C2, 0x7EF0, 0xB4C3, 0x75B5,\n\t0xB4C4, 0x8328, 0xB4C5, 0x78C1, 0xB4C6, 0x96CC, 0xB4C7, 0x8F9E,\n\t0xB4C8, 0x6148, 0xB4C9, 0x74F7, 0xB4CA, 0x8BCD, 0xB4CB, 0x6B64,\n\t0xB4CC, 0x523A, 0xB4CD, 0x8D50, 0xB4CE, 0x6B21, 0xB4CF, 0x806A,\n\t0xB4D0, 0x8471, 0xB4D1, 0x56F1, 0xB4D2, 0x5306, 0xB4D3, 0x4ECE,\n\t0xB4D4, 0x4E1B, 0xB4D5, 0x51D1, 0xB4D6, 0x7C97, 0xB4D7, 0x918B,\n\t0xB4D8, 0x7C07, 0xB4D9, 0x4FC3, 0xB4DA, 0x8E7F, 0xB4DB, 0x7BE1,\n\t0xB4DC, 0x7A9C, 0xB4DD, 0x6467, 0xB4DE, 0x5D14, 0xB4DF, 0x50AC,\n\t0xB4E0, 0x8106, 0xB4E1, 0x7601, 0xB4E2, 0x7CB9, 0xB4E3, 0x6DEC,\n\t0xB4E4, 0x7FE0, 0xB4E5, 0x6751, 0xB4E6, 0x5B58, 0xB4E7, 0x5BF8,\n\t0xB4E8, 0x78CB, 0xB4E9, 0x64AE, 0xB4EA, 0x6413, 0xB4EB, 0x63AA,\n\t0xB4EC, 0x632B, 0xB4ED, 0x9519, 0xB4EE, 0x642D, 0xB4EF, 0x8FBE,\n\t0xB4F0, 0x7B54, 0xB4F1, 0x7629, 0xB4F2, 0x6253, 0xB4F3, 0x5927,\n\t0xB4F4, 0x5446, 0xB4F5, 0x6B79, 0xB4F6, 0x50A3, 0xB4F7, 0x6234,\n\t0xB4F8, 0x5E26, 0xB4F9, 0x6B86, 0xB4FA, 0x4EE3, 0xB4FB, 0x8D37,\n\t0xB4FC, 0x888B, 0xB4FD, 0x5F85, 0xB4FE, 0x902E, 0xB540, 0x790D,\n\t0xB541, 0x790E, 0xB542, 0x790F, 0xB543, 0x7910, 0xB544, 0x7911,\n\t0xB545, 0x7912, 0xB546, 0x7914, 0xB547, 0x7915, 0xB548, 0x7916,\n\t0xB549, 0x7917, 0xB54A, 0x7918, 0xB54B, 0x7919, 0xB54C, 0x791A,\n\t0xB54D, 0x791B, 0xB54E, 0x791C, 0xB54F, 0x791D, 0xB550, 0x791F,\n\t0xB551, 0x7920, 0xB552, 0x7921, 0xB553, 0x7922, 0xB554, 0x7923,\n\t0xB555, 0x7925, 0xB556, 0x7926, 0xB557, 0x7927, 0xB558, 0x7928,\n\t0xB559, 0x7929, 0xB55A, 0x792A, 0xB55B, 0x792B, 0xB55C, 0x792C,\n\t0xB55D, 0x792D, 0xB55E, 0x792E, 0xB55F, 0x792F, 0xB560, 0x7930,\n\t0xB561, 0x7931, 0xB562, 0x7932, 0xB563, 0x7933, 0xB564, 0x7935,\n\t0xB565, 0x7936, 0xB566, 0x7937, 0xB567, 0x7938, 0xB568, 0x7939,\n\t0xB569, 0x793D, 0xB56A, 0x793F, 0xB56B, 0x7942, 0xB56C, 0x7943,\n\t0xB56D, 0x7944, 0xB56E, 0x7945, 0xB56F, 0x7947, 0xB570, 0x794A,\n\t0xB571, 0x794B, 0xB572, 0x794C, 0xB573, 0x794D, 0xB574, 0x794E,\n\t0xB575, 0x794F, 0xB576, 0x7950, 0xB577, 0x7951, 0xB578, 0x7952,\n\t0xB579, 0x7954, 0xB57A, 0x7955, 0xB57B, 0x7958, 0xB57C, 0x7959,\n\t0xB57D, 0x7961, 0xB57E, 0x7963, 0xB580, 0x7964, 0xB581, 0x7966,\n\t0xB582, 0x7969, 0xB583, 0x796A, 0xB584, 0x796B, 0xB585, 0x796C,\n\t0xB586, 0x796E, 0xB587, 0x7970, 0xB588, 0x7971, 0xB589, 0x7972,\n\t0xB58A, 0x7973, 0xB58B, 0x7974, 0xB58C, 0x7975, 0xB58D, 0x7976,\n\t0xB58E, 0x7979, 0xB58F, 0x797B, 0xB590, 0x797C, 0xB591, 0x797D,\n\t0xB592, 0x797E, 0xB593, 0x797F, 0xB594, 0x7982, 0xB595, 0x7983,\n\t0xB596, 0x7986, 0xB597, 0x7987, 0xB598, 0x7988, 0xB599, 0x7989,\n\t0xB59A, 0x798B, 0xB59B, 0x798C, 0xB59C, 0x798D, 0xB59D, 0x798E,\n\t0xB59E, 0x7990, 0xB59F, 0x7991, 0xB5A0, 0x7992, 0xB5A1, 0x6020,\n\t0xB5A2, 0x803D, 0xB5A3, 0x62C5, 0xB5A4, 0x4E39, 0xB5A5, 0x5355,\n\t0xB5A6, 0x90F8, 0xB5A7, 0x63B8, 0xB5A8, 0x80C6, 0xB5A9, 0x65E6,\n\t0xB5AA, 0x6C2E, 0xB5AB, 0x4F46, 0xB5AC, 0x60EE, 0xB5AD, 0x6DE1,\n\t0xB5AE, 0x8BDE, 0xB5AF, 0x5F39, 0xB5B0, 0x86CB, 0xB5B1, 0x5F53,\n\t0xB5B2, 0x6321, 0xB5B3, 0x515A, 0xB5B4, 0x8361, 0xB5B5, 0x6863,\n\t0xB5B6, 0x5200, 0xB5B7, 0x6363, 0xB5B8, 0x8E48, 0xB5B9, 0x5012,\n\t0xB5BA, 0x5C9B, 0xB5BB, 0x7977, 0xB5BC, 0x5BFC, 0xB5BD, 0x5230,\n\t0xB5BE, 0x7A3B, 0xB5BF, 0x60BC, 0xB5C0, 0x9053, 0xB5C1, 0x76D7,\n\t0xB5C2, 0x5FB7, 0xB5C3, 0x5F97, 0xB5C4, 0x7684, 0xB5C5, 0x8E6C,\n\t0xB5C6, 0x706F, 0xB5C7, 0x767B, 0xB5C8, 0x7B49, 0xB5C9, 0x77AA,\n\t0xB5CA, 0x51F3, 0xB5CB, 0x9093, 0xB5CC, 0x5824, 0xB5CD, 0x4F4E,\n\t0xB5CE, 0x6EF4, 0xB5CF, 0x8FEA, 0xB5D0, 0x654C, 0xB5D1, 0x7B1B,\n\t0xB5D2, 0x72C4, 0xB5D3, 0x6DA4, 0xB5D4, 0x7FDF, 0xB5D5, 0x5AE1,\n\t0xB5D6, 0x62B5, 0xB5D7, 0x5E95, 0xB5D8, 0x5730, 0xB5D9, 0x8482,\n\t0xB5DA, 0x7B2C, 0xB5DB, 0x5E1D, 0xB5DC, 0x5F1F, 0xB5DD, 0x9012,\n\t0xB5DE, 0x7F14, 0xB5DF, 0x98A0, 0xB5E0, 0x6382, 0xB5E1, 0x6EC7,\n\t0xB5E2, 0x7898, 0xB5E3, 0x70B9, 0xB5E4, 0x5178, 0xB5E5, 0x975B,\n\t0xB5E6, 0x57AB, 0xB5E7, 0x7535, 0xB5E8, 0x4F43, 0xB5E9, 0x7538,\n\t0xB5EA, 0x5E97, 0xB5EB, 0x60E6, 0xB5EC, 0x5960, 0xB5ED, 0x6DC0,\n\t0xB5EE, 0x6BBF, 0xB5EF, 0x7889, 0xB5F0, 0x53FC, 0xB5F1, 0x96D5,\n\t0xB5F2, 0x51CB, 0xB5F3, 0x5201, 0xB5F4, 0x6389, 0xB5F5, 0x540A,\n\t0xB5F6, 0x9493, 0xB5F7, 0x8C03, 0xB5F8, 0x8DCC, 0xB5F9, 0x7239,\n\t0xB5FA, 0x789F, 0xB5FB, 0x8776, 0xB5FC, 0x8FED, 0xB5FD, 0x8C0D,\n\t0xB5FE, 0x53E0, 0xB640, 0x7993, 0xB641, 0x7994, 0xB642, 0x7995,\n\t0xB643, 0x7996, 0xB644, 0x7997, 0xB645, 0x7998, 0xB646, 0x7999,\n\t0xB647, 0x799B, 0xB648, 0x799C, 0xB649, 0x799D, 0xB64A, 0x799E,\n\t0xB64B, 0x799F, 0xB64C, 0x79A0, 0xB64D, 0x79A1, 0xB64E, 0x79A2,\n\t0xB64F, 0x79A3, 0xB650, 0x79A4, 0xB651, 0x79A5, 0xB652, 0x79A6,\n\t0xB653, 0x79A8, 0xB654, 0x79A9, 0xB655, 0x79AA, 0xB656, 0x79AB,\n\t0xB657, 0x79AC, 0xB658, 0x79AD, 0xB659, 0x79AE, 0xB65A, 0x79AF,\n\t0xB65B, 0x79B0, 0xB65C, 0x79B1, 0xB65D, 0x79B2, 0xB65E, 0x79B4,\n\t0xB65F, 0x79B5, 0xB660, 0x79B6, 0xB661, 0x79B7, 0xB662, 0x79B8,\n\t0xB663, 0x79BC, 0xB664, 0x79BF, 0xB665, 0x79C2, 0xB666, 0x79C4,\n\t0xB667, 0x79C5, 0xB668, 0x79C7, 0xB669, 0x79C8, 0xB66A, 0x79CA,\n\t0xB66B, 0x79CC, 0xB66C, 0x79CE, 0xB66D, 0x79CF, 0xB66E, 0x79D0,\n\t0xB66F, 0x79D3, 0xB670, 0x79D4, 0xB671, 0x79D6, 0xB672, 0x79D7,\n\t0xB673, 0x79D9, 0xB674, 0x79DA, 0xB675, 0x79DB, 0xB676, 0x79DC,\n\t0xB677, 0x79DD, 0xB678, 0x79DE, 0xB679, 0x79E0, 0xB67A, 0x79E1,\n\t0xB67B, 0x79E2, 0xB67C, 0x79E5, 0xB67D, 0x79E8, 0xB67E, 0x79EA,\n\t0xB680, 0x79EC, 0xB681, 0x79EE, 0xB682, 0x79F1, 0xB683, 0x79F2,\n\t0xB684, 0x79F3, 0xB685, 0x79F4, 0xB686, 0x79F5, 0xB687, 0x79F6,\n\t0xB688, 0x79F7, 0xB689, 0x79F9, 0xB68A, 0x79FA, 0xB68B, 0x79FC,\n\t0xB68C, 0x79FE, 0xB68D, 0x79FF, 0xB68E, 0x7A01, 0xB68F, 0x7A04,\n\t0xB690, 0x7A05, 0xB691, 0x7A07, 0xB692, 0x7A08, 0xB693, 0x7A09,\n\t0xB694, 0x7A0A, 0xB695, 0x7A0C, 0xB696, 0x7A0F, 0xB697, 0x7A10,\n\t0xB698, 0x7A11, 0xB699, 0x7A12, 0xB69A, 0x7A13, 0xB69B, 0x7A15,\n\t0xB69C, 0x7A16, 0xB69D, 0x7A18, 0xB69E, 0x7A19, 0xB69F, 0x7A1B,\n\t0xB6A0, 0x7A1C, 0xB6A1, 0x4E01, 0xB6A2, 0x76EF, 0xB6A3, 0x53EE,\n\t0xB6A4, 0x9489, 0xB6A5, 0x9876, 0xB6A6, 0x9F0E, 0xB6A7, 0x952D,\n\t0xB6A8, 0x5B9A, 0xB6A9, 0x8BA2, 0xB6AA, 0x4E22, 0xB6AB, 0x4E1C,\n\t0xB6AC, 0x51AC, 0xB6AD, 0x8463, 0xB6AE, 0x61C2, 0xB6AF, 0x52A8,\n\t0xB6B0, 0x680B, 0xB6B1, 0x4F97, 0xB6B2, 0x606B, 0xB6B3, 0x51BB,\n\t0xB6B4, 0x6D1E, 0xB6B5, 0x515C, 0xB6B6, 0x6296, 0xB6B7, 0x6597,\n\t0xB6B8, 0x9661, 0xB6B9, 0x8C46, 0xB6BA, 0x9017, 0xB6BB, 0x75D8,\n\t0xB6BC, 0x90FD, 0xB6BD, 0x7763, 0xB6BE, 0x6BD2, 0xB6BF, 0x728A,\n\t0xB6C0, 0x72EC, 0xB6C1, 0x8BFB, 0xB6C2, 0x5835, 0xB6C3, 0x7779,\n\t0xB6C4, 0x8D4C, 0xB6C5, 0x675C, 0xB6C6, 0x9540, 0xB6C7, 0x809A,\n\t0xB6C8, 0x5EA6, 0xB6C9, 0x6E21, 0xB6CA, 0x5992, 0xB6CB, 0x7AEF,\n\t0xB6CC, 0x77ED, 0xB6CD, 0x953B, 0xB6CE, 0x6BB5, 0xB6CF, 0x65AD,\n\t0xB6D0, 0x7F0E, 0xB6D1, 0x5806, 0xB6D2, 0x5151, 0xB6D3, 0x961F,\n\t0xB6D4, 0x5BF9, 0xB6D5, 0x58A9, 0xB6D6, 0x5428, 0xB6D7, 0x8E72,\n\t0xB6D8, 0x6566, 0xB6D9, 0x987F, 0xB6DA, 0x56E4, 0xB6DB, 0x949D,\n\t0xB6DC, 0x76FE, 0xB6DD, 0x9041, 0xB6DE, 0x6387, 0xB6DF, 0x54C6,\n\t0xB6E0, 0x591A, 0xB6E1, 0x593A, 0xB6E2, 0x579B, 0xB6E3, 0x8EB2,\n\t0xB6E4, 0x6735, 0xB6E5, 0x8DFA, 0xB6E6, 0x8235, 0xB6E7, 0x5241,\n\t0xB6E8, 0x60F0, 0xB6E9, 0x5815, 0xB6EA, 0x86FE, 0xB6EB, 0x5CE8,\n\t0xB6EC, 0x9E45, 0xB6ED, 0x4FC4, 0xB6EE, 0x989D, 0xB6EF, 0x8BB9,\n\t0xB6F0, 0x5A25, 0xB6F1, 0x6076, 0xB6F2, 0x5384, 0xB6F3, 0x627C,\n\t0xB6F4, 0x904F, 0xB6F5, 0x9102, 0xB6F6, 0x997F, 0xB6F7, 0x6069,\n\t0xB6F8, 0x800C, 0xB6F9, 0x513F, 0xB6FA, 0x8033, 0xB6FB, 0x5C14,\n\t0xB6FC, 0x9975, 0xB6FD, 0x6D31, 0xB6FE, 0x4E8C, 0xB740, 0x7A1D,\n\t0xB741, 0x7A1F, 0xB742, 0x7A21, 0xB743, 0x7A22, 0xB744, 0x7A24,\n\t0xB745, 0x7A25, 0xB746, 0x7A26, 0xB747, 0x7A27, 0xB748, 0x7A28,\n\t0xB749, 0x7A29, 0xB74A, 0x7A2A, 0xB74B, 0x7A2B, 0xB74C, 0x7A2C,\n\t0xB74D, 0x7A2D, 0xB74E, 0x7A2E, 0xB74F, 0x7A2F, 0xB750, 0x7A30,\n\t0xB751, 0x7A31, 0xB752, 0x7A32, 0xB753, 0x7A34, 0xB754, 0x7A35,\n\t0xB755, 0x7A36, 0xB756, 0x7A38, 0xB757, 0x7A3A, 0xB758, 0x7A3E,\n\t0xB759, 0x7A40, 0xB75A, 0x7A41, 0xB75B, 0x7A42, 0xB75C, 0x7A43,\n\t0xB75D, 0x7A44, 0xB75E, 0x7A45, 0xB75F, 0x7A47, 0xB760, 0x7A48,\n\t0xB761, 0x7A49, 0xB762, 0x7A4A, 0xB763, 0x7A4B, 0xB764, 0x7A4C,\n\t0xB765, 0x7A4D, 0xB766, 0x7A4E, 0xB767, 0x7A4F, 0xB768, 0x7A50,\n\t0xB769, 0x7A52, 0xB76A, 0x7A53, 0xB76B, 0x7A54, 0xB76C, 0x7A55,\n\t0xB76D, 0x7A56, 0xB76E, 0x7A58, 0xB76F, 0x7A59, 0xB770, 0x7A5A,\n\t0xB771, 0x7A5B, 0xB772, 0x7A5C, 0xB773, 0x7A5D, 0xB774, 0x7A5E,\n\t0xB775, 0x7A5F, 0xB776, 0x7A60, 0xB777, 0x7A61, 0xB778, 0x7A62,\n\t0xB779, 0x7A63, 0xB77A, 0x7A64, 0xB77B, 0x7A65, 0xB77C, 0x7A66,\n\t0xB77D, 0x7A67, 0xB77E, 0x7A68, 0xB780, 0x7A69, 0xB781, 0x7A6A,\n\t0xB782, 0x7A6B, 0xB783, 0x7A6C, 0xB784, 0x7A6D, 0xB785, 0x7A6E,\n\t0xB786, 0x7A6F, 0xB787, 0x7A71, 0xB788, 0x7A72, 0xB789, 0x7A73,\n\t0xB78A, 0x7A75, 0xB78B, 0x7A7B, 0xB78C, 0x7A7C, 0xB78D, 0x7A7D,\n\t0xB78E, 0x7A7E, 0xB78F, 0x7A82, 0xB790, 0x7A85, 0xB791, 0x7A87,\n\t0xB792, 0x7A89, 0xB793, 0x7A8A, 0xB794, 0x7A8B, 0xB795, 0x7A8C,\n\t0xB796, 0x7A8E, 0xB797, 0x7A8F, 0xB798, 0x7A90, 0xB799, 0x7A93,\n\t0xB79A, 0x7A94, 0xB79B, 0x7A99, 0xB79C, 0x7A9A, 0xB79D, 0x7A9B,\n\t0xB79E, 0x7A9E, 0xB79F, 0x7AA1, 0xB7A0, 0x7AA2, 0xB7A1, 0x8D30,\n\t0xB7A2, 0x53D1, 0xB7A3, 0x7F5A, 0xB7A4, 0x7B4F, 0xB7A5, 0x4F10,\n\t0xB7A6, 0x4E4F, 0xB7A7, 0x9600, 0xB7A8, 0x6CD5, 0xB7A9, 0x73D0,\n\t0xB7AA, 0x85E9, 0xB7AB, 0x5E06, 0xB7AC, 0x756A, 0xB7AD, 0x7FFB,\n\t0xB7AE, 0x6A0A, 0xB7AF, 0x77FE, 0xB7B0, 0x9492, 0xB7B1, 0x7E41,\n\t0xB7B2, 0x51E1, 0xB7B3, 0x70E6, 0xB7B4, 0x53CD, 0xB7B5, 0x8FD4,\n\t0xB7B6, 0x8303, 0xB7B7, 0x8D29, 0xB7B8, 0x72AF, 0xB7B9, 0x996D,\n\t0xB7BA, 0x6CDB, 0xB7BB, 0x574A, 0xB7BC, 0x82B3, 0xB7BD, 0x65B9,\n\t0xB7BE, 0x80AA, 0xB7BF, 0x623F, 0xB7C0, 0x9632, 0xB7C1, 0x59A8,\n\t0xB7C2, 0x4EFF, 0xB7C3, 0x8BBF, 0xB7C4, 0x7EBA, 0xB7C5, 0x653E,\n\t0xB7C6, 0x83F2, 0xB7C7, 0x975E, 0xB7C8, 0x5561, 0xB7C9, 0x98DE,\n\t0xB7CA, 0x80A5, 0xB7CB, 0x532A, 0xB7CC, 0x8BFD, 0xB7CD, 0x5420,\n\t0xB7CE, 0x80BA, 0xB7CF, 0x5E9F, 0xB7D0, 0x6CB8, 0xB7D1, 0x8D39,\n\t0xB7D2, 0x82AC, 0xB7D3, 0x915A, 0xB7D4, 0x5429, 0xB7D5, 0x6C1B,\n\t0xB7D6, 0x5206, 0xB7D7, 0x7EB7, 0xB7D8, 0x575F, 0xB7D9, 0x711A,\n\t0xB7DA, 0x6C7E, 0xB7DB, 0x7C89, 0xB7DC, 0x594B, 0xB7DD, 0x4EFD,\n\t0xB7DE, 0x5FFF, 0xB7DF, 0x6124, 0xB7E0, 0x7CAA, 0xB7E1, 0x4E30,\n\t0xB7E2, 0x5C01, 0xB7E3, 0x67AB, 0xB7E4, 0x8702, 0xB7E5, 0x5CF0,\n\t0xB7E6, 0x950B, 0xB7E7, 0x98CE, 0xB7E8, 0x75AF, 0xB7E9, 0x70FD,\n\t0xB7EA, 0x9022, 0xB7EB, 0x51AF, 0xB7EC, 0x7F1D, 0xB7ED, 0x8BBD,\n\t0xB7EE, 0x5949, 0xB7EF, 0x51E4, 0xB7F0, 0x4F5B, 0xB7F1, 0x5426,\n\t0xB7F2, 0x592B, 0xB7F3, 0x6577, 0xB7F4, 0x80A4, 0xB7F5, 0x5B75,\n\t0xB7F6, 0x6276, 0xB7F7, 0x62C2, 0xB7F8, 0x8F90, 0xB7F9, 0x5E45,\n\t0xB7FA, 0x6C1F, 0xB7FB, 0x7B26, 0xB7FC, 0x4F0F, 0xB7FD, 0x4FD8,\n\t0xB7FE, 0x670D, 0xB840, 0x7AA3, 0xB841, 0x7AA4, 0xB842, 0x7AA7,\n\t0xB843, 0x7AA9, 0xB844, 0x7AAA, 0xB845, 0x7AAB, 0xB846, 0x7AAE,\n\t0xB847, 0x7AAF, 0xB848, 0x7AB0, 0xB849, 0x7AB1, 0xB84A, 0x7AB2,\n\t0xB84B, 0x7AB4, 0xB84C, 0x7AB5, 0xB84D, 0x7AB6, 0xB84E, 0x7AB7,\n\t0xB84F, 0x7AB8, 0xB850, 0x7AB9, 0xB851, 0x7ABA, 0xB852, 0x7ABB,\n\t0xB853, 0x7ABC, 0xB854, 0x7ABD, 0xB855, 0x7ABE, 0xB856, 0x7AC0,\n\t0xB857, 0x7AC1, 0xB858, 0x7AC2, 0xB859, 0x7AC3, 0xB85A, 0x7AC4,\n\t0xB85B, 0x7AC5, 0xB85C, 0x7AC6, 0xB85D, 0x7AC7, 0xB85E, 0x7AC8,\n\t0xB85F, 0x7AC9, 0xB860, 0x7ACA, 0xB861, 0x7ACC, 0xB862, 0x7ACD,\n\t0xB863, 0x7ACE, 0xB864, 0x7ACF, 0xB865, 0x7AD0, 0xB866, 0x7AD1,\n\t0xB867, 0x7AD2, 0xB868, 0x7AD3, 0xB869, 0x7AD4, 0xB86A, 0x7AD5,\n\t0xB86B, 0x7AD7, 0xB86C, 0x7AD8, 0xB86D, 0x7ADA, 0xB86E, 0x7ADB,\n\t0xB86F, 0x7ADC, 0xB870, 0x7ADD, 0xB871, 0x7AE1, 0xB872, 0x7AE2,\n\t0xB873, 0x7AE4, 0xB874, 0x7AE7, 0xB875, 0x7AE8, 0xB876, 0x7AE9,\n\t0xB877, 0x7AEA, 0xB878, 0x7AEB, 0xB879, 0x7AEC, 0xB87A, 0x7AEE,\n\t0xB87B, 0x7AF0, 0xB87C, 0x7AF1, 0xB87D, 0x7AF2, 0xB87E, 0x7AF3,\n\t0xB880, 0x7AF4, 0xB881, 0x7AF5, 0xB882, 0x7AF6, 0xB883, 0x7AF7,\n\t0xB884, 0x7AF8, 0xB885, 0x7AFB, 0xB886, 0x7AFC, 0xB887, 0x7AFE,\n\t0xB888, 0x7B00, 0xB889, 0x7B01, 0xB88A, 0x7B02, 0xB88B, 0x7B05,\n\t0xB88C, 0x7B07, 0xB88D, 0x7B09, 0xB88E, 0x7B0C, 0xB88F, 0x7B0D,\n\t0xB890, 0x7B0E, 0xB891, 0x7B10, 0xB892, 0x7B12, 0xB893, 0x7B13,\n\t0xB894, 0x7B16, 0xB895, 0x7B17, 0xB896, 0x7B18, 0xB897, 0x7B1A,\n\t0xB898, 0x7B1C, 0xB899, 0x7B1D, 0xB89A, 0x7B1F, 0xB89B, 0x7B21,\n\t0xB89C, 0x7B22, 0xB89D, 0x7B23, 0xB89E, 0x7B27, 0xB89F, 0x7B29,\n\t0xB8A0, 0x7B2D, 0xB8A1, 0x6D6E, 0xB8A2, 0x6DAA, 0xB8A3, 0x798F,\n\t0xB8A4, 0x88B1, 0xB8A5, 0x5F17, 0xB8A6, 0x752B, 0xB8A7, 0x629A,\n\t0xB8A8, 0x8F85, 0xB8A9, 0x4FEF, 0xB8AA, 0x91DC, 0xB8AB, 0x65A7,\n\t0xB8AC, 0x812F, 0xB8AD, 0x8151, 0xB8AE, 0x5E9C, 0xB8AF, 0x8150,\n\t0xB8B0, 0x8D74, 0xB8B1, 0x526F, 0xB8B2, 0x8986, 0xB8B3, 0x8D4B,\n\t0xB8B4, 0x590D, 0xB8B5, 0x5085, 0xB8B6, 0x4ED8, 0xB8B7, 0x961C,\n\t0xB8B8, 0x7236, 0xB8B9, 0x8179, 0xB8BA, 0x8D1F, 0xB8BB, 0x5BCC,\n\t0xB8BC, 0x8BA3, 0xB8BD, 0x9644, 0xB8BE, 0x5987, 0xB8BF, 0x7F1A,\n\t0xB8C0, 0x5490, 0xB8C1, 0x5676, 0xB8C2, 0x560E, 0xB8C3, 0x8BE5,\n\t0xB8C4, 0x6539, 0xB8C5, 0x6982, 0xB8C6, 0x9499, 0xB8C7, 0x76D6,\n\t0xB8C8, 0x6E89, 0xB8C9, 0x5E72, 0xB8CA, 0x7518, 0xB8CB, 0x6746,\n\t0xB8CC, 0x67D1, 0xB8CD, 0x7AFF, 0xB8CE, 0x809D, 0xB8CF, 0x8D76,\n\t0xB8D0, 0x611F, 0xB8D1, 0x79C6, 0xB8D2, 0x6562, 0xB8D3, 0x8D63,\n\t0xB8D4, 0x5188, 0xB8D5, 0x521A, 0xB8D6, 0x94A2, 0xB8D7, 0x7F38,\n\t0xB8D8, 0x809B, 0xB8D9, 0x7EB2, 0xB8DA, 0x5C97, 0xB8DB, 0x6E2F,\n\t0xB8DC, 0x6760, 0xB8DD, 0x7BD9, 0xB8DE, 0x768B, 0xB8DF, 0x9AD8,\n\t0xB8E0, 0x818F, 0xB8E1, 0x7F94, 0xB8E2, 0x7CD5, 0xB8E3, 0x641E,\n\t0xB8E4, 0x9550, 0xB8E5, 0x7A3F, 0xB8E6, 0x544A, 0xB8E7, 0x54E5,\n\t0xB8E8, 0x6B4C, 0xB8E9, 0x6401, 0xB8EA, 0x6208, 0xB8EB, 0x9E3D,\n\t0xB8EC, 0x80F3, 0xB8ED, 0x7599, 0xB8EE, 0x5272, 0xB8EF, 0x9769,\n\t0xB8F0, 0x845B, 0xB8F1, 0x683C, 0xB8F2, 0x86E4, 0xB8F3, 0x9601,\n\t0xB8F4, 0x9694, 0xB8F5, 0x94EC, 0xB8F6, 0x4E2A, 0xB8F7, 0x5404,\n\t0xB8F8, 0x7ED9, 0xB8F9, 0x6839, 0xB8FA, 0x8DDF, 0xB8FB, 0x8015,\n\t0xB8FC, 0x66F4, 0xB8FD, 0x5E9A, 0xB8FE, 0x7FB9, 0xB940, 0x7B2F,\n\t0xB941, 0x7B30, 0xB942, 0x7B32, 0xB943, 0x7B34, 0xB944, 0x7B35,\n\t0xB945, 0x7B36, 0xB946, 0x7B37, 0xB947, 0x7B39, 0xB948, 0x7B3B,\n\t0xB949, 0x7B3D, 0xB94A, 0x7B3F, 0xB94B, 0x7B40, 0xB94C, 0x7B41,\n\t0xB94D, 0x7B42, 0xB94E, 0x7B43, 0xB94F, 0x7B44, 0xB950, 0x7B46,\n\t0xB951, 0x7B48, 0xB952, 0x7B4A, 0xB953, 0x7B4D, 0xB954, 0x7B4E,\n\t0xB955, 0x7B53, 0xB956, 0x7B55, 0xB957, 0x7B57, 0xB958, 0x7B59,\n\t0xB959, 0x7B5C, 0xB95A, 0x7B5E, 0xB95B, 0x7B5F, 0xB95C, 0x7B61,\n\t0xB95D, 0x7B63, 0xB95E, 0x7B64, 0xB95F, 0x7B65, 0xB960, 0x7B66,\n\t0xB961, 0x7B67, 0xB962, 0x7B68, 0xB963, 0x7B69, 0xB964, 0x7B6A,\n\t0xB965, 0x7B6B, 0xB966, 0x7B6C, 0xB967, 0x7B6D, 0xB968, 0x7B6F,\n\t0xB969, 0x7B70, 0xB96A, 0x7B73, 0xB96B, 0x7B74, 0xB96C, 0x7B76,\n\t0xB96D, 0x7B78, 0xB96E, 0x7B7A, 0xB96F, 0x7B7C, 0xB970, 0x7B7D,\n\t0xB971, 0x7B7F, 0xB972, 0x7B81, 0xB973, 0x7B82, 0xB974, 0x7B83,\n\t0xB975, 0x7B84, 0xB976, 0x7B86, 0xB977, 0x7B87, 0xB978, 0x7B88,\n\t0xB979, 0x7B89, 0xB97A, 0x7B8A, 0xB97B, 0x7B8B, 0xB97C, 0x7B8C,\n\t0xB97D, 0x7B8E, 0xB97E, 0x7B8F, 0xB980, 0x7B91, 0xB981, 0x7B92,\n\t0xB982, 0x7B93, 0xB983, 0x7B96, 0xB984, 0x7B98, 0xB985, 0x7B99,\n\t0xB986, 0x7B9A, 0xB987, 0x7B9B, 0xB988, 0x7B9E, 0xB989, 0x7B9F,\n\t0xB98A, 0x7BA0, 0xB98B, 0x7BA3, 0xB98C, 0x7BA4, 0xB98D, 0x7BA5,\n\t0xB98E, 0x7BAE, 0xB98F, 0x7BAF, 0xB990, 0x7BB0, 0xB991, 0x7BB2,\n\t0xB992, 0x7BB3, 0xB993, 0x7BB5, 0xB994, 0x7BB6, 0xB995, 0x7BB7,\n\t0xB996, 0x7BB9, 0xB997, 0x7BBA, 0xB998, 0x7BBB, 0xB999, 0x7BBC,\n\t0xB99A, 0x7BBD, 0xB99B, 0x7BBE, 0xB99C, 0x7BBF, 0xB99D, 0x7BC0,\n\t0xB99E, 0x7BC2, 0xB99F, 0x7BC3, 0xB9A0, 0x7BC4, 0xB9A1, 0x57C2,\n\t0xB9A2, 0x803F, 0xB9A3, 0x6897, 0xB9A4, 0x5DE5, 0xB9A5, 0x653B,\n\t0xB9A6, 0x529F, 0xB9A7, 0x606D, 0xB9A8, 0x9F9A, 0xB9A9, 0x4F9B,\n\t0xB9AA, 0x8EAC, 0xB9AB, 0x516C, 0xB9AC, 0x5BAB, 0xB9AD, 0x5F13,\n\t0xB9AE, 0x5DE9, 0xB9AF, 0x6C5E, 0xB9B0, 0x62F1, 0xB9B1, 0x8D21,\n\t0xB9B2, 0x5171, 0xB9B3, 0x94A9, 0xB9B4, 0x52FE, 0xB9B5, 0x6C9F,\n\t0xB9B6, 0x82DF, 0xB9B7, 0x72D7, 0xB9B8, 0x57A2, 0xB9B9, 0x6784,\n\t0xB9BA, 0x8D2D, 0xB9BB, 0x591F, 0xB9BC, 0x8F9C, 0xB9BD, 0x83C7,\n\t0xB9BE, 0x5495, 0xB9BF, 0x7B8D, 0xB9C0, 0x4F30, 0xB9C1, 0x6CBD,\n\t0xB9C2, 0x5B64, 0xB9C3, 0x59D1, 0xB9C4, 0x9F13, 0xB9C5, 0x53E4,\n\t0xB9C6, 0x86CA, 0xB9C7, 0x9AA8, 0xB9C8, 0x8C37, 0xB9C9, 0x80A1,\n\t0xB9CA, 0x6545, 0xB9CB, 0x987E, 0xB9CC, 0x56FA, 0xB9CD, 0x96C7,\n\t0xB9CE, 0x522E, 0xB9CF, 0x74DC, 0xB9D0, 0x5250, 0xB9D1, 0x5BE1,\n\t0xB9D2, 0x6302, 0xB9D3, 0x8902, 0xB9D4, 0x4E56, 0xB9D5, 0x62D0,\n\t0xB9D6, 0x602A, 0xB9D7, 0x68FA, 0xB9D8, 0x5173, 0xB9D9, 0x5B98,\n\t0xB9DA, 0x51A0, 0xB9DB, 0x89C2, 0xB9DC, 0x7BA1, 0xB9DD, 0x9986,\n\t0xB9DE, 0x7F50, 0xB9DF, 0x60EF, 0xB9E0, 0x704C, 0xB9E1, 0x8D2F,\n\t0xB9E2, 0x5149, 0xB9E3, 0x5E7F, 0xB9E4, 0x901B, 0xB9E5, 0x7470,\n\t0xB9E6, 0x89C4, 0xB9E7, 0x572D, 0xB9E8, 0x7845, 0xB9E9, 0x5F52,\n\t0xB9EA, 0x9F9F, 0xB9EB, 0x95FA, 0xB9EC, 0x8F68, 0xB9ED, 0x9B3C,\n\t0xB9EE, 0x8BE1, 0xB9EF, 0x7678, 0xB9F0, 0x6842, 0xB9F1, 0x67DC,\n\t0xB9F2, 0x8DEA, 0xB9F3, 0x8D35, 0xB9F4, 0x523D, 0xB9F5, 0x8F8A,\n\t0xB9F6, 0x6EDA, 0xB9F7, 0x68CD, 0xB9F8, 0x9505, 0xB9F9, 0x90ED,\n\t0xB9FA, 0x56FD, 0xB9FB, 0x679C, 0xB9FC, 0x88F9, 0xB9FD, 0x8FC7,\n\t0xB9FE, 0x54C8, 0xBA40, 0x7BC5, 0xBA41, 0x7BC8, 0xBA42, 0x7BC9,\n\t0xBA43, 0x7BCA, 0xBA44, 0x7BCB, 0xBA45, 0x7BCD, 0xBA46, 0x7BCE,\n\t0xBA47, 0x7BCF, 0xBA48, 0x7BD0, 0xBA49, 0x7BD2, 0xBA4A, 0x7BD4,\n\t0xBA4B, 0x7BD5, 0xBA4C, 0x7BD6, 0xBA4D, 0x7BD7, 0xBA4E, 0x7BD8,\n\t0xBA4F, 0x7BDB, 0xBA50, 0x7BDC, 0xBA51, 0x7BDE, 0xBA52, 0x7BDF,\n\t0xBA53, 0x7BE0, 0xBA54, 0x7BE2, 0xBA55, 0x7BE3, 0xBA56, 0x7BE4,\n\t0xBA57, 0x7BE7, 0xBA58, 0x7BE8, 0xBA59, 0x7BE9, 0xBA5A, 0x7BEB,\n\t0xBA5B, 0x7BEC, 0xBA5C, 0x7BED, 0xBA5D, 0x7BEF, 0xBA5E, 0x7BF0,\n\t0xBA5F, 0x7BF2, 0xBA60, 0x7BF3, 0xBA61, 0x7BF4, 0xBA62, 0x7BF5,\n\t0xBA63, 0x7BF6, 0xBA64, 0x7BF8, 0xBA65, 0x7BF9, 0xBA66, 0x7BFA,\n\t0xBA67, 0x7BFB, 0xBA68, 0x7BFD, 0xBA69, 0x7BFF, 0xBA6A, 0x7C00,\n\t0xBA6B, 0x7C01, 0xBA6C, 0x7C02, 0xBA6D, 0x7C03, 0xBA6E, 0x7C04,\n\t0xBA6F, 0x7C05, 0xBA70, 0x7C06, 0xBA71, 0x7C08, 0xBA72, 0x7C09,\n\t0xBA73, 0x7C0A, 0xBA74, 0x7C0D, 0xBA75, 0x7C0E, 0xBA76, 0x7C10,\n\t0xBA77, 0x7C11, 0xBA78, 0x7C12, 0xBA79, 0x7C13, 0xBA7A, 0x7C14,\n\t0xBA7B, 0x7C15, 0xBA7C, 0x7C17, 0xBA7D, 0x7C18, 0xBA7E, 0x7C19,\n\t0xBA80, 0x7C1A, 0xBA81, 0x7C1B, 0xBA82, 0x7C1C, 0xBA83, 0x7C1D,\n\t0xBA84, 0x7C1E, 0xBA85, 0x7C20, 0xBA86, 0x7C21, 0xBA87, 0x7C22,\n\t0xBA88, 0x7C23, 0xBA89, 0x7C24, 0xBA8A, 0x7C25, 0xBA8B, 0x7C28,\n\t0xBA8C, 0x7C29, 0xBA8D, 0x7C2B, 0xBA8E, 0x7C2C, 0xBA8F, 0x7C2D,\n\t0xBA90, 0x7C2E, 0xBA91, 0x7C2F, 0xBA92, 0x7C30, 0xBA93, 0x7C31,\n\t0xBA94, 0x7C32, 0xBA95, 0x7C33, 0xBA96, 0x7C34, 0xBA97, 0x7C35,\n\t0xBA98, 0x7C36, 0xBA99, 0x7C37, 0xBA9A, 0x7C39, 0xBA9B, 0x7C3A,\n\t0xBA9C, 0x7C3B, 0xBA9D, 0x7C3C, 0xBA9E, 0x7C3D, 0xBA9F, 0x7C3E,\n\t0xBAA0, 0x7C42, 0xBAA1, 0x9AB8, 0xBAA2, 0x5B69, 0xBAA3, 0x6D77,\n\t0xBAA4, 0x6C26, 0xBAA5, 0x4EA5, 0xBAA6, 0x5BB3, 0xBAA7, 0x9A87,\n\t0xBAA8, 0x9163, 0xBAA9, 0x61A8, 0xBAAA, 0x90AF, 0xBAAB, 0x97E9,\n\t0xBAAC, 0x542B, 0xBAAD, 0x6DB5, 0xBAAE, 0x5BD2, 0xBAAF, 0x51FD,\n\t0xBAB0, 0x558A, 0xBAB1, 0x7F55, 0xBAB2, 0x7FF0, 0xBAB3, 0x64BC,\n\t0xBAB4, 0x634D, 0xBAB5, 0x65F1, 0xBAB6, 0x61BE, 0xBAB7, 0x608D,\n\t0xBAB8, 0x710A, 0xBAB9, 0x6C57, 0xBABA, 0x6C49, 0xBABB, 0x592F,\n\t0xBABC, 0x676D, 0xBABD, 0x822A, 0xBABE, 0x58D5, 0xBABF, 0x568E,\n\t0xBAC0, 0x8C6A, 0xBAC1, 0x6BEB, 0xBAC2, 0x90DD, 0xBAC3, 0x597D,\n\t0xBAC4, 0x8017, 0xBAC5, 0x53F7, 0xBAC6, 0x6D69, 0xBAC7, 0x5475,\n\t0xBAC8, 0x559D, 0xBAC9, 0x8377, 0xBACA, 0x83CF, 0xBACB, 0x6838,\n\t0xBACC, 0x79BE, 0xBACD, 0x548C, 0xBACE, 0x4F55, 0xBACF, 0x5408,\n\t0xBAD0, 0x76D2, 0xBAD1, 0x8C89, 0xBAD2, 0x9602, 0xBAD3, 0x6CB3,\n\t0xBAD4, 0x6DB8, 0xBAD5, 0x8D6B, 0xBAD6, 0x8910, 0xBAD7, 0x9E64,\n\t0xBAD8, 0x8D3A, 0xBAD9, 0x563F, 0xBADA, 0x9ED1, 0xBADB, 0x75D5,\n\t0xBADC, 0x5F88, 0xBADD, 0x72E0, 0xBADE, 0x6068, 0xBADF, 0x54FC,\n\t0xBAE0, 0x4EA8, 0xBAE1, 0x6A2A, 0xBAE2, 0x8861, 0xBAE3, 0x6052,\n\t0xBAE4, 0x8F70, 0xBAE5, 0x54C4, 0xBAE6, 0x70D8, 0xBAE7, 0x8679,\n\t0xBAE8, 0x9E3F, 0xBAE9, 0x6D2A, 0xBAEA, 0x5B8F, 0xBAEB, 0x5F18,\n\t0xBAEC, 0x7EA2, 0xBAED, 0x5589, 0xBAEE, 0x4FAF, 0xBAEF, 0x7334,\n\t0xBAF0, 0x543C, 0xBAF1, 0x539A, 0xBAF2, 0x5019, 0xBAF3, 0x540E,\n\t0xBAF4, 0x547C, 0xBAF5, 0x4E4E, 0xBAF6, 0x5FFD, 0xBAF7, 0x745A,\n\t0xBAF8, 0x58F6, 0xBAF9, 0x846B, 0xBAFA, 0x80E1, 0xBAFB, 0x8774,\n\t0xBAFC, 0x72D0, 0xBAFD, 0x7CCA, 0xBAFE, 0x6E56, 0xBB40, 0x7C43,\n\t0xBB41, 0x7C44, 0xBB42, 0x7C45, 0xBB43, 0x7C46, 0xBB44, 0x7C47,\n\t0xBB45, 0x7C48, 0xBB46, 0x7C49, 0xBB47, 0x7C4A, 0xBB48, 0x7C4B,\n\t0xBB49, 0x7C4C, 0xBB4A, 0x7C4E, 0xBB4B, 0x7C4F, 0xBB4C, 0x7C50,\n\t0xBB4D, 0x7C51, 0xBB4E, 0x7C52, 0xBB4F, 0x7C53, 0xBB50, 0x7C54,\n\t0xBB51, 0x7C55, 0xBB52, 0x7C56, 0xBB53, 0x7C57, 0xBB54, 0x7C58,\n\t0xBB55, 0x7C59, 0xBB56, 0x7C5A, 0xBB57, 0x7C5B, 0xBB58, 0x7C5C,\n\t0xBB59, 0x7C5D, 0xBB5A, 0x7C5E, 0xBB5B, 0x7C5F, 0xBB5C, 0x7C60,\n\t0xBB5D, 0x7C61, 0xBB5E, 0x7C62, 0xBB5F, 0x7C63, 0xBB60, 0x7C64,\n\t0xBB61, 0x7C65, 0xBB62, 0x7C66, 0xBB63, 0x7C67, 0xBB64, 0x7C68,\n\t0xBB65, 0x7C69, 0xBB66, 0x7C6A, 0xBB67, 0x7C6B, 0xBB68, 0x7C6C,\n\t0xBB69, 0x7C6D, 0xBB6A, 0x7C6E, 0xBB6B, 0x7C6F, 0xBB6C, 0x7C70,\n\t0xBB6D, 0x7C71, 0xBB6E, 0x7C72, 0xBB6F, 0x7C75, 0xBB70, 0x7C76,\n\t0xBB71, 0x7C77, 0xBB72, 0x7C78, 0xBB73, 0x7C79, 0xBB74, 0x7C7A,\n\t0xBB75, 0x7C7E, 0xBB76, 0x7C7F, 0xBB77, 0x7C80, 0xBB78, 0x7C81,\n\t0xBB79, 0x7C82, 0xBB7A, 0x7C83, 0xBB7B, 0x7C84, 0xBB7C, 0x7C85,\n\t0xBB7D, 0x7C86, 0xBB7E, 0x7C87, 0xBB80, 0x7C88, 0xBB81, 0x7C8A,\n\t0xBB82, 0x7C8B, 0xBB83, 0x7C8C, 0xBB84, 0x7C8D, 0xBB85, 0x7C8E,\n\t0xBB86, 0x7C8F, 0xBB87, 0x7C90, 0xBB88, 0x7C93, 0xBB89, 0x7C94,\n\t0xBB8A, 0x7C96, 0xBB8B, 0x7C99, 0xBB8C, 0x7C9A, 0xBB8D, 0x7C9B,\n\t0xBB8E, 0x7CA0, 0xBB8F, 0x7CA1, 0xBB90, 0x7CA3, 0xBB91, 0x7CA6,\n\t0xBB92, 0x7CA7, 0xBB93, 0x7CA8, 0xBB94, 0x7CA9, 0xBB95, 0x7CAB,\n\t0xBB96, 0x7CAC, 0xBB97, 0x7CAD, 0xBB98, 0x7CAF, 0xBB99, 0x7CB0,\n\t0xBB9A, 0x7CB4, 0xBB9B, 0x7CB5, 0xBB9C, 0x7CB6, 0xBB9D, 0x7CB7,\n\t0xBB9E, 0x7CB8, 0xBB9F, 0x7CBA, 0xBBA0, 0x7CBB, 0xBBA1, 0x5F27,\n\t0xBBA2, 0x864E, 0xBBA3, 0x552C, 0xBBA4, 0x62A4, 0xBBA5, 0x4E92,\n\t0xBBA6, 0x6CAA, 0xBBA7, 0x6237, 0xBBA8, 0x82B1, 0xBBA9, 0x54D7,\n\t0xBBAA, 0x534E, 0xBBAB, 0x733E, 0xBBAC, 0x6ED1, 0xBBAD, 0x753B,\n\t0xBBAE, 0x5212, 0xBBAF, 0x5316, 0xBBB0, 0x8BDD, 0xBBB1, 0x69D0,\n\t0xBBB2, 0x5F8A, 0xBBB3, 0x6000, 0xBBB4, 0x6DEE, 0xBBB5, 0x574F,\n\t0xBBB6, 0x6B22, 0xBBB7, 0x73AF, 0xBBB8, 0x6853, 0xBBB9, 0x8FD8,\n\t0xBBBA, 0x7F13, 0xBBBB, 0x6362, 0xBBBC, 0x60A3, 0xBBBD, 0x5524,\n\t0xBBBE, 0x75EA, 0xBBBF, 0x8C62, 0xBBC0, 0x7115, 0xBBC1, 0x6DA3,\n\t0xBBC2, 0x5BA6, 0xBBC3, 0x5E7B, 0xBBC4, 0x8352, 0xBBC5, 0x614C,\n\t0xBBC6, 0x9EC4, 0xBBC7, 0x78FA, 0xBBC8, 0x8757, 0xBBC9, 0x7C27,\n\t0xBBCA, 0x7687, 0xBBCB, 0x51F0, 0xBBCC, 0x60F6, 0xBBCD, 0x714C,\n\t0xBBCE, 0x6643, 0xBBCF, 0x5E4C, 0xBBD0, 0x604D, 0xBBD1, 0x8C0E,\n\t0xBBD2, 0x7070, 0xBBD3, 0x6325, 0xBBD4, 0x8F89, 0xBBD5, 0x5FBD,\n\t0xBBD6, 0x6062, 0xBBD7, 0x86D4, 0xBBD8, 0x56DE, 0xBBD9, 0x6BC1,\n\t0xBBDA, 0x6094, 0xBBDB, 0x6167, 0xBBDC, 0x5349, 0xBBDD, 0x60E0,\n\t0xBBDE, 0x6666, 0xBBDF, 0x8D3F, 0xBBE0, 0x79FD, 0xBBE1, 0x4F1A,\n\t0xBBE2, 0x70E9, 0xBBE3, 0x6C47, 0xBBE4, 0x8BB3, 0xBBE5, 0x8BF2,\n\t0xBBE6, 0x7ED8, 0xBBE7, 0x8364, 0xBBE8, 0x660F, 0xBBE9, 0x5A5A,\n\t0xBBEA, 0x9B42, 0xBBEB, 0x6D51, 0xBBEC, 0x6DF7, 0xBBED, 0x8C41,\n\t0xBBEE, 0x6D3B, 0xBBEF, 0x4F19, 0xBBF0, 0x706B, 0xBBF1, 0x83B7,\n\t0xBBF2, 0x6216, 0xBBF3, 0x60D1, 0xBBF4, 0x970D, 0xBBF5, 0x8D27,\n\t0xBBF6, 0x7978, 0xBBF7, 0x51FB, 0xBBF8, 0x573E, 0xBBF9, 0x57FA,\n\t0xBBFA, 0x673A, 0xBBFB, 0x7578, 0xBBFC, 0x7A3D, 0xBBFD, 0x79EF,\n\t0xBBFE, 0x7B95, 0xBC40, 0x7CBF, 0xBC41, 0x7CC0, 0xBC42, 0x7CC2,\n\t0xBC43, 0x7CC3, 0xBC44, 0x7CC4, 0xBC45, 0x7CC6, 0xBC46, 0x7CC9,\n\t0xBC47, 0x7CCB, 0xBC48, 0x7CCE, 0xBC49, 0x7CCF, 0xBC4A, 0x7CD0,\n\t0xBC4B, 0x7CD1, 0xBC4C, 0x7CD2, 0xBC4D, 0x7CD3, 0xBC4E, 0x7CD4,\n\t0xBC4F, 0x7CD8, 0xBC50, 0x7CDA, 0xBC51, 0x7CDB, 0xBC52, 0x7CDD,\n\t0xBC53, 0x7CDE, 0xBC54, 0x7CE1, 0xBC55, 0x7CE2, 0xBC56, 0x7CE3,\n\t0xBC57, 0x7CE4, 0xBC58, 0x7CE5, 0xBC59, 0x7CE6, 0xBC5A, 0x7CE7,\n\t0xBC5B, 0x7CE9, 0xBC5C, 0x7CEA, 0xBC5D, 0x7CEB, 0xBC5E, 0x7CEC,\n\t0xBC5F, 0x7CED, 0xBC60, 0x7CEE, 0xBC61, 0x7CF0, 0xBC62, 0x7CF1,\n\t0xBC63, 0x7CF2, 0xBC64, 0x7CF3, 0xBC65, 0x7CF4, 0xBC66, 0x7CF5,\n\t0xBC67, 0x7CF6, 0xBC68, 0x7CF7, 0xBC69, 0x7CF9, 0xBC6A, 0x7CFA,\n\t0xBC6B, 0x7CFC, 0xBC6C, 0x7CFD, 0xBC6D, 0x7CFE, 0xBC6E, 0x7CFF,\n\t0xBC6F, 0x7D00, 0xBC70, 0x7D01, 0xBC71, 0x7D02, 0xBC72, 0x7D03,\n\t0xBC73, 0x7D04, 0xBC74, 0x7D05, 0xBC75, 0x7D06, 0xBC76, 0x7D07,\n\t0xBC77, 0x7D08, 0xBC78, 0x7D09, 0xBC79, 0x7D0B, 0xBC7A, 0x7D0C,\n\t0xBC7B, 0x7D0D, 0xBC7C, 0x7D0E, 0xBC7D, 0x7D0F, 0xBC7E, 0x7D10,\n\t0xBC80, 0x7D11, 0xBC81, 0x7D12, 0xBC82, 0x7D13, 0xBC83, 0x7D14,\n\t0xBC84, 0x7D15, 0xBC85, 0x7D16, 0xBC86, 0x7D17, 0xBC87, 0x7D18,\n\t0xBC88, 0x7D19, 0xBC89, 0x7D1A, 0xBC8A, 0x7D1B, 0xBC8B, 0x7D1C,\n\t0xBC8C, 0x7D1D, 0xBC8D, 0x7D1E, 0xBC8E, 0x7D1F, 0xBC8F, 0x7D21,\n\t0xBC90, 0x7D23, 0xBC91, 0x7D24, 0xBC92, 0x7D25, 0xBC93, 0x7D26,\n\t0xBC94, 0x7D28, 0xBC95, 0x7D29, 0xBC96, 0x7D2A, 0xBC97, 0x7D2C,\n\t0xBC98, 0x7D2D, 0xBC99, 0x7D2E, 0xBC9A, 0x7D30, 0xBC9B, 0x7D31,\n\t0xBC9C, 0x7D32, 0xBC9D, 0x7D33, 0xBC9E, 0x7D34, 0xBC9F, 0x7D35,\n\t0xBCA0, 0x7D36, 0xBCA1, 0x808C, 0xBCA2, 0x9965, 0xBCA3, 0x8FF9,\n\t0xBCA4, 0x6FC0, 0xBCA5, 0x8BA5, 0xBCA6, 0x9E21, 0xBCA7, 0x59EC,\n\t0xBCA8, 0x7EE9, 0xBCA9, 0x7F09, 0xBCAA, 0x5409, 0xBCAB, 0x6781,\n\t0xBCAC, 0x68D8, 0xBCAD, 0x8F91, 0xBCAE, 0x7C4D, 0xBCAF, 0x96C6,\n\t0xBCB0, 0x53CA, 0xBCB1, 0x6025, 0xBCB2, 0x75BE, 0xBCB3, 0x6C72,\n\t0xBCB4, 0x5373, 0xBCB5, 0x5AC9, 0xBCB6, 0x7EA7, 0xBCB7, 0x6324,\n\t0xBCB8, 0x51E0, 0xBCB9, 0x810A, 0xBCBA, 0x5DF1, 0xBCBB, 0x84DF,\n\t0xBCBC, 0x6280, 0xBCBD, 0x5180, 0xBCBE, 0x5B63, 0xBCBF, 0x4F0E,\n\t0xBCC0, 0x796D, 0xBCC1, 0x5242, 0xBCC2, 0x60B8, 0xBCC3, 0x6D4E,\n\t0xBCC4, 0x5BC4, 0xBCC5, 0x5BC2, 0xBCC6, 0x8BA1, 0xBCC7, 0x8BB0,\n\t0xBCC8, 0x65E2, 0xBCC9, 0x5FCC, 0xBCCA, 0x9645, 0xBCCB, 0x5993,\n\t0xBCCC, 0x7EE7, 0xBCCD, 0x7EAA, 0xBCCE, 0x5609, 0xBCCF, 0x67B7,\n\t0xBCD0, 0x5939, 0xBCD1, 0x4F73, 0xBCD2, 0x5BB6, 0xBCD3, 0x52A0,\n\t0xBCD4, 0x835A, 0xBCD5, 0x988A, 0xBCD6, 0x8D3E, 0xBCD7, 0x7532,\n\t0xBCD8, 0x94BE, 0xBCD9, 0x5047, 0xBCDA, 0x7A3C, 0xBCDB, 0x4EF7,\n\t0xBCDC, 0x67B6, 0xBCDD, 0x9A7E, 0xBCDE, 0x5AC1, 0xBCDF, 0x6B7C,\n\t0xBCE0, 0x76D1, 0xBCE1, 0x575A, 0xBCE2, 0x5C16, 0xBCE3, 0x7B3A,\n\t0xBCE4, 0x95F4, 0xBCE5, 0x714E, 0xBCE6, 0x517C, 0xBCE7, 0x80A9,\n\t0xBCE8, 0x8270, 0xBCE9, 0x5978, 0xBCEA, 0x7F04, 0xBCEB, 0x8327,\n\t0xBCEC, 0x68C0, 0xBCED, 0x67EC, 0xBCEE, 0x78B1, 0xBCEF, 0x7877,\n\t0xBCF0, 0x62E3, 0xBCF1, 0x6361, 0xBCF2, 0x7B80, 0xBCF3, 0x4FED,\n\t0xBCF4, 0x526A, 0xBCF5, 0x51CF, 0xBCF6, 0x8350, 0xBCF7, 0x69DB,\n\t0xBCF8, 0x9274, 0xBCF9, 0x8DF5, 0xBCFA, 0x8D31, 0xBCFB, 0x89C1,\n\t0xBCFC, 0x952E, 0xBCFD, 0x7BAD, 0xBCFE, 0x4EF6, 0xBD40, 0x7D37,\n\t0xBD41, 0x7D38, 0xBD42, 0x7D39, 0xBD43, 0x7D3A, 0xBD44, 0x7D3B,\n\t0xBD45, 0x7D3C, 0xBD46, 0x7D3D, 0xBD47, 0x7D3E, 0xBD48, 0x7D3F,\n\t0xBD49, 0x7D40, 0xBD4A, 0x7D41, 0xBD4B, 0x7D42, 0xBD4C, 0x7D43,\n\t0xBD4D, 0x7D44, 0xBD4E, 0x7D45, 0xBD4F, 0x7D46, 0xBD50, 0x7D47,\n\t0xBD51, 0x7D48, 0xBD52, 0x7D49, 0xBD53, 0x7D4A, 0xBD54, 0x7D4B,\n\t0xBD55, 0x7D4C, 0xBD56, 0x7D4D, 0xBD57, 0x7D4E, 0xBD58, 0x7D4F,\n\t0xBD59, 0x7D50, 0xBD5A, 0x7D51, 0xBD5B, 0x7D52, 0xBD5C, 0x7D53,\n\t0xBD5D, 0x7D54, 0xBD5E, 0x7D55, 0xBD5F, 0x7D56, 0xBD60, 0x7D57,\n\t0xBD61, 0x7D58, 0xBD62, 0x7D59, 0xBD63, 0x7D5A, 0xBD64, 0x7D5B,\n\t0xBD65, 0x7D5C, 0xBD66, 0x7D5D, 0xBD67, 0x7D5E, 0xBD68, 0x7D5F,\n\t0xBD69, 0x7D60, 0xBD6A, 0x7D61, 0xBD6B, 0x7D62, 0xBD6C, 0x7D63,\n\t0xBD6D, 0x7D64, 0xBD6E, 0x7D65, 0xBD6F, 0x7D66, 0xBD70, 0x7D67,\n\t0xBD71, 0x7D68, 0xBD72, 0x7D69, 0xBD73, 0x7D6A, 0xBD74, 0x7D6B,\n\t0xBD75, 0x7D6C, 0xBD76, 0x7D6D, 0xBD77, 0x7D6F, 0xBD78, 0x7D70,\n\t0xBD79, 0x7D71, 0xBD7A, 0x7D72, 0xBD7B, 0x7D73, 0xBD7C, 0x7D74,\n\t0xBD7D, 0x7D75, 0xBD7E, 0x7D76, 0xBD80, 0x7D78, 0xBD81, 0x7D79,\n\t0xBD82, 0x7D7A, 0xBD83, 0x7D7B, 0xBD84, 0x7D7C, 0xBD85, 0x7D7D,\n\t0xBD86, 0x7D7E, 0xBD87, 0x7D7F, 0xBD88, 0x7D80, 0xBD89, 0x7D81,\n\t0xBD8A, 0x7D82, 0xBD8B, 0x7D83, 0xBD8C, 0x7D84, 0xBD8D, 0x7D85,\n\t0xBD8E, 0x7D86, 0xBD8F, 0x7D87, 0xBD90, 0x7D88, 0xBD91, 0x7D89,\n\t0xBD92, 0x7D8A, 0xBD93, 0x7D8B, 0xBD94, 0x7D8C, 0xBD95, 0x7D8D,\n\t0xBD96, 0x7D8E, 0xBD97, 0x7D8F, 0xBD98, 0x7D90, 0xBD99, 0x7D91,\n\t0xBD9A, 0x7D92, 0xBD9B, 0x7D93, 0xBD9C, 0x7D94, 0xBD9D, 0x7D95,\n\t0xBD9E, 0x7D96, 0xBD9F, 0x7D97, 0xBDA0, 0x7D98, 0xBDA1, 0x5065,\n\t0xBDA2, 0x8230, 0xBDA3, 0x5251, 0xBDA4, 0x996F, 0xBDA5, 0x6E10,\n\t0xBDA6, 0x6E85, 0xBDA7, 0x6DA7, 0xBDA8, 0x5EFA, 0xBDA9, 0x50F5,\n\t0xBDAA, 0x59DC, 0xBDAB, 0x5C06, 0xBDAC, 0x6D46, 0xBDAD, 0x6C5F,\n\t0xBDAE, 0x7586, 0xBDAF, 0x848B, 0xBDB0, 0x6868, 0xBDB1, 0x5956,\n\t0xBDB2, 0x8BB2, 0xBDB3, 0x5320, 0xBDB4, 0x9171, 0xBDB5, 0x964D,\n\t0xBDB6, 0x8549, 0xBDB7, 0x6912, 0xBDB8, 0x7901, 0xBDB9, 0x7126,\n\t0xBDBA, 0x80F6, 0xBDBB, 0x4EA4, 0xBDBC, 0x90CA, 0xBDBD, 0x6D47,\n\t0xBDBE, 0x9A84, 0xBDBF, 0x5A07, 0xBDC0, 0x56BC, 0xBDC1, 0x6405,\n\t0xBDC2, 0x94F0, 0xBDC3, 0x77EB, 0xBDC4, 0x4FA5, 0xBDC5, 0x811A,\n\t0xBDC6, 0x72E1, 0xBDC7, 0x89D2, 0xBDC8, 0x997A, 0xBDC9, 0x7F34,\n\t0xBDCA, 0x7EDE, 0xBDCB, 0x527F, 0xBDCC, 0x6559, 0xBDCD, 0x9175,\n\t0xBDCE, 0x8F7F, 0xBDCF, 0x8F83, 0xBDD0, 0x53EB, 0xBDD1, 0x7A96,\n\t0xBDD2, 0x63ED, 0xBDD3, 0x63A5, 0xBDD4, 0x7686, 0xBDD5, 0x79F8,\n\t0xBDD6, 0x8857, 0xBDD7, 0x9636, 0xBDD8, 0x622A, 0xBDD9, 0x52AB,\n\t0xBDDA, 0x8282, 0xBDDB, 0x6854, 0xBDDC, 0x6770, 0xBDDD, 0x6377,\n\t0xBDDE, 0x776B, 0xBDDF, 0x7AED, 0xBDE0, 0x6D01, 0xBDE1, 0x7ED3,\n\t0xBDE2, 0x89E3, 0xBDE3, 0x59D0, 0xBDE4, 0x6212, 0xBDE5, 0x85C9,\n\t0xBDE6, 0x82A5, 0xBDE7, 0x754C, 0xBDE8, 0x501F, 0xBDE9, 0x4ECB,\n\t0xBDEA, 0x75A5, 0xBDEB, 0x8BEB, 0xBDEC, 0x5C4A, 0xBDED, 0x5DFE,\n\t0xBDEE, 0x7B4B, 0xBDEF, 0x65A4, 0xBDF0, 0x91D1, 0xBDF1, 0x4ECA,\n\t0xBDF2, 0x6D25, 0xBDF3, 0x895F, 0xBDF4, 0x7D27, 0xBDF5, 0x9526,\n\t0xBDF6, 0x4EC5, 0xBDF7, 0x8C28, 0xBDF8, 0x8FDB, 0xBDF9, 0x9773,\n\t0xBDFA, 0x664B, 0xBDFB, 0x7981, 0xBDFC, 0x8FD1, 0xBDFD, 0x70EC,\n\t0xBDFE, 0x6D78, 0xBE40, 0x7D99, 0xBE41, 0x7D9A, 0xBE42, 0x7D9B,\n\t0xBE43, 0x7D9C, 0xBE44, 0x7D9D, 0xBE45, 0x7D9E, 0xBE46, 0x7D9F,\n\t0xBE47, 0x7DA0, 0xBE48, 0x7DA1, 0xBE49, 0x7DA2, 0xBE4A, 0x7DA3,\n\t0xBE4B, 0x7DA4, 0xBE4C, 0x7DA5, 0xBE4D, 0x7DA7, 0xBE4E, 0x7DA8,\n\t0xBE4F, 0x7DA9, 0xBE50, 0x7DAA, 0xBE51, 0x7DAB, 0xBE52, 0x7DAC,\n\t0xBE53, 0x7DAD, 0xBE54, 0x7DAF, 0xBE55, 0x7DB0, 0xBE56, 0x7DB1,\n\t0xBE57, 0x7DB2, 0xBE58, 0x7DB3, 0xBE59, 0x7DB4, 0xBE5A, 0x7DB5,\n\t0xBE5B, 0x7DB6, 0xBE5C, 0x7DB7, 0xBE5D, 0x7DB8, 0xBE5E, 0x7DB9,\n\t0xBE5F, 0x7DBA, 0xBE60, 0x7DBB, 0xBE61, 0x7DBC, 0xBE62, 0x7DBD,\n\t0xBE63, 0x7DBE, 0xBE64, 0x7DBF, 0xBE65, 0x7DC0, 0xBE66, 0x7DC1,\n\t0xBE67, 0x7DC2, 0xBE68, 0x7DC3, 0xBE69, 0x7DC4, 0xBE6A, 0x7DC5,\n\t0xBE6B, 0x7DC6, 0xBE6C, 0x7DC7, 0xBE6D, 0x7DC8, 0xBE6E, 0x7DC9,\n\t0xBE6F, 0x7DCA, 0xBE70, 0x7DCB, 0xBE71, 0x7DCC, 0xBE72, 0x7DCD,\n\t0xBE73, 0x7DCE, 0xBE74, 0x7DCF, 0xBE75, 0x7DD0, 0xBE76, 0x7DD1,\n\t0xBE77, 0x7DD2, 0xBE78, 0x7DD3, 0xBE79, 0x7DD4, 0xBE7A, 0x7DD5,\n\t0xBE7B, 0x7DD6, 0xBE7C, 0x7DD7, 0xBE7D, 0x7DD8, 0xBE7E, 0x7DD9,\n\t0xBE80, 0x7DDA, 0xBE81, 0x7DDB, 0xBE82, 0x7DDC, 0xBE83, 0x7DDD,\n\t0xBE84, 0x7DDE, 0xBE85, 0x7DDF, 0xBE86, 0x7DE0, 0xBE87, 0x7DE1,\n\t0xBE88, 0x7DE2, 0xBE89, 0x7DE3, 0xBE8A, 0x7DE4, 0xBE8B, 0x7DE5,\n\t0xBE8C, 0x7DE6, 0xBE8D, 0x7DE7, 0xBE8E, 0x7DE8, 0xBE8F, 0x7DE9,\n\t0xBE90, 0x7DEA, 0xBE91, 0x7DEB, 0xBE92, 0x7DEC, 0xBE93, 0x7DED,\n\t0xBE94, 0x7DEE, 0xBE95, 0x7DEF, 0xBE96, 0x7DF0, 0xBE97, 0x7DF1,\n\t0xBE98, 0x7DF2, 0xBE99, 0x7DF3, 0xBE9A, 0x7DF4, 0xBE9B, 0x7DF5,\n\t0xBE9C, 0x7DF6, 0xBE9D, 0x7DF7, 0xBE9E, 0x7DF8, 0xBE9F, 0x7DF9,\n\t0xBEA0, 0x7DFA, 0xBEA1, 0x5C3D, 0xBEA2, 0x52B2, 0xBEA3, 0x8346,\n\t0xBEA4, 0x5162, 0xBEA5, 0x830E, 0xBEA6, 0x775B, 0xBEA7, 0x6676,\n\t0xBEA8, 0x9CB8, 0xBEA9, 0x4EAC, 0xBEAA, 0x60CA, 0xBEAB, 0x7CBE,\n\t0xBEAC, 0x7CB3, 0xBEAD, 0x7ECF, 0xBEAE, 0x4E95, 0xBEAF, 0x8B66,\n\t0xBEB0, 0x666F, 0xBEB1, 0x9888, 0xBEB2, 0x9759, 0xBEB3, 0x5883,\n\t0xBEB4, 0x656C, 0xBEB5, 0x955C, 0xBEB6, 0x5F84, 0xBEB7, 0x75C9,\n\t0xBEB8, 0x9756, 0xBEB9, 0x7ADF, 0xBEBA, 0x7ADE, 0xBEBB, 0x51C0,\n\t0xBEBC, 0x70AF, 0xBEBD, 0x7A98, 0xBEBE, 0x63EA, 0xBEBF, 0x7A76,\n\t0xBEC0, 0x7EA0, 0xBEC1, 0x7396, 0xBEC2, 0x97ED, 0xBEC3, 0x4E45,\n\t0xBEC4, 0x7078, 0xBEC5, 0x4E5D, 0xBEC6, 0x9152, 0xBEC7, 0x53A9,\n\t0xBEC8, 0x6551, 0xBEC9, 0x65E7, 0xBECA, 0x81FC, 0xBECB, 0x8205,\n\t0xBECC, 0x548E, 0xBECD, 0x5C31, 0xBECE, 0x759A, 0xBECF, 0x97A0,\n\t0xBED0, 0x62D8, 0xBED1, 0x72D9, 0xBED2, 0x75BD, 0xBED3, 0x5C45,\n\t0xBED4, 0x9A79, 0xBED5, 0x83CA, 0xBED6, 0x5C40, 0xBED7, 0x5480,\n\t0xBED8, 0x77E9, 0xBED9, 0x4E3E, 0xBEDA, 0x6CAE, 0xBEDB, 0x805A,\n\t0xBEDC, 0x62D2, 0xBEDD, 0x636E, 0xBEDE, 0x5DE8, 0xBEDF, 0x5177,\n\t0xBEE0, 0x8DDD, 0xBEE1, 0x8E1E, 0xBEE2, 0x952F, 0xBEE3, 0x4FF1,\n\t0xBEE4, 0x53E5, 0xBEE5, 0x60E7, 0xBEE6, 0x70AC, 0xBEE7, 0x5267,\n\t0xBEE8, 0x6350, 0xBEE9, 0x9E43, 0xBEEA, 0x5A1F, 0xBEEB, 0x5026,\n\t0xBEEC, 0x7737, 0xBEED, 0x5377, 0xBEEE, 0x7EE2, 0xBEEF, 0x6485,\n\t0xBEF0, 0x652B, 0xBEF1, 0x6289, 0xBEF2, 0x6398, 0xBEF3, 0x5014,\n\t0xBEF4, 0x7235, 0xBEF5, 0x89C9, 0xBEF6, 0x51B3, 0xBEF7, 0x8BC0,\n\t0xBEF8, 0x7EDD, 0xBEF9, 0x5747, 0xBEFA, 0x83CC, 0xBEFB, 0x94A7,\n\t0xBEFC, 0x519B, 0xBEFD, 0x541B, 0xBEFE, 0x5CFB, 0xBF40, 0x7DFB,\n\t0xBF41, 0x7DFC, 0xBF42, 0x7DFD, 0xBF43, 0x7DFE, 0xBF44, 0x7DFF,\n\t0xBF45, 0x7E00, 0xBF46, 0x7E01, 0xBF47, 0x7E02, 0xBF48, 0x7E03,\n\t0xBF49, 0x7E04, 0xBF4A, 0x7E05, 0xBF4B, 0x7E06, 0xBF4C, 0x7E07,\n\t0xBF4D, 0x7E08, 0xBF4E, 0x7E09, 0xBF4F, 0x7E0A, 0xBF50, 0x7E0B,\n\t0xBF51, 0x7E0C, 0xBF52, 0x7E0D, 0xBF53, 0x7E0E, 0xBF54, 0x7E0F,\n\t0xBF55, 0x7E10, 0xBF56, 0x7E11, 0xBF57, 0x7E12, 0xBF58, 0x7E13,\n\t0xBF59, 0x7E14, 0xBF5A, 0x7E15, 0xBF5B, 0x7E16, 0xBF5C, 0x7E17,\n\t0xBF5D, 0x7E18, 0xBF5E, 0x7E19, 0xBF5F, 0x7E1A, 0xBF60, 0x7E1B,\n\t0xBF61, 0x7E1C, 0xBF62, 0x7E1D, 0xBF63, 0x7E1E, 0xBF64, 0x7E1F,\n\t0xBF65, 0x7E20, 0xBF66, 0x7E21, 0xBF67, 0x7E22, 0xBF68, 0x7E23,\n\t0xBF69, 0x7E24, 0xBF6A, 0x7E25, 0xBF6B, 0x7E26, 0xBF6C, 0x7E27,\n\t0xBF6D, 0x7E28, 0xBF6E, 0x7E29, 0xBF6F, 0x7E2A, 0xBF70, 0x7E2B,\n\t0xBF71, 0x7E2C, 0xBF72, 0x7E2D, 0xBF73, 0x7E2E, 0xBF74, 0x7E2F,\n\t0xBF75, 0x7E30, 0xBF76, 0x7E31, 0xBF77, 0x7E32, 0xBF78, 0x7E33,\n\t0xBF79, 0x7E34, 0xBF7A, 0x7E35, 0xBF7B, 0x7E36, 0xBF7C, 0x7E37,\n\t0xBF7D, 0x7E38, 0xBF7E, 0x7E39, 0xBF80, 0x7E3A, 0xBF81, 0x7E3C,\n\t0xBF82, 0x7E3D, 0xBF83, 0x7E3E, 0xBF84, 0x7E3F, 0xBF85, 0x7E40,\n\t0xBF86, 0x7E42, 0xBF87, 0x7E43, 0xBF88, 0x7E44, 0xBF89, 0x7E45,\n\t0xBF8A, 0x7E46, 0xBF8B, 0x7E48, 0xBF8C, 0x7E49, 0xBF8D, 0x7E4A,\n\t0xBF8E, 0x7E4B, 0xBF8F, 0x7E4C, 0xBF90, 0x7E4D, 0xBF91, 0x7E4E,\n\t0xBF92, 0x7E4F, 0xBF93, 0x7E50, 0xBF94, 0x7E51, 0xBF95, 0x7E52,\n\t0xBF96, 0x7E53, 0xBF97, 0x7E54, 0xBF98, 0x7E55, 0xBF99, 0x7E56,\n\t0xBF9A, 0x7E57, 0xBF9B, 0x7E58, 0xBF9C, 0x7E59, 0xBF9D, 0x7E5A,\n\t0xBF9E, 0x7E5B, 0xBF9F, 0x7E5C, 0xBFA0, 0x7E5D, 0xBFA1, 0x4FCA,\n\t0xBFA2, 0x7AE3, 0xBFA3, 0x6D5A, 0xBFA4, 0x90E1, 0xBFA5, 0x9A8F,\n\t0xBFA6, 0x5580, 0xBFA7, 0x5496, 0xBFA8, 0x5361, 0xBFA9, 0x54AF,\n\t0xBFAA, 0x5F00, 0xBFAB, 0x63E9, 0xBFAC, 0x6977, 0xBFAD, 0x51EF,\n\t0xBFAE, 0x6168, 0xBFAF, 0x520A, 0xBFB0, 0x582A, 0xBFB1, 0x52D8,\n\t0xBFB2, 0x574E, 0xBFB3, 0x780D, 0xBFB4, 0x770B, 0xBFB5, 0x5EB7,\n\t0xBFB6, 0x6177, 0xBFB7, 0x7CE0, 0xBFB8, 0x625B, 0xBFB9, 0x6297,\n\t0xBFBA, 0x4EA2, 0xBFBB, 0x7095, 0xBFBC, 0x8003, 0xBFBD, 0x62F7,\n\t0xBFBE, 0x70E4, 0xBFBF, 0x9760, 0xBFC0, 0x5777, 0xBFC1, 0x82DB,\n\t0xBFC2, 0x67EF, 0xBFC3, 0x68F5, 0xBFC4, 0x78D5, 0xBFC5, 0x9897,\n\t0xBFC6, 0x79D1, 0xBFC7, 0x58F3, 0xBFC8, 0x54B3, 0xBFC9, 0x53EF,\n\t0xBFCA, 0x6E34, 0xBFCB, 0x514B, 0xBFCC, 0x523B, 0xBFCD, 0x5BA2,\n\t0xBFCE, 0x8BFE, 0xBFCF, 0x80AF, 0xBFD0, 0x5543, 0xBFD1, 0x57A6,\n\t0xBFD2, 0x6073, 0xBFD3, 0x5751, 0xBFD4, 0x542D, 0xBFD5, 0x7A7A,\n\t0xBFD6, 0x6050, 0xBFD7, 0x5B54, 0xBFD8, 0x63A7, 0xBFD9, 0x62A0,\n\t0xBFDA, 0x53E3, 0xBFDB, 0x6263, 0xBFDC, 0x5BC7, 0xBFDD, 0x67AF,\n\t0xBFDE, 0x54ED, 0xBFDF, 0x7A9F, 0xBFE0, 0x82E6, 0xBFE1, 0x9177,\n\t0xBFE2, 0x5E93, 0xBFE3, 0x88E4, 0xBFE4, 0x5938, 0xBFE5, 0x57AE,\n\t0xBFE6, 0x630E, 0xBFE7, 0x8DE8, 0xBFE8, 0x80EF, 0xBFE9, 0x5757,\n\t0xBFEA, 0x7B77, 0xBFEB, 0x4FA9, 0xBFEC, 0x5FEB, 0xBFED, 0x5BBD,\n\t0xBFEE, 0x6B3E, 0xBFEF, 0x5321, 0xBFF0, 0x7B50, 0xBFF1, 0x72C2,\n\t0xBFF2, 0x6846, 0xBFF3, 0x77FF, 0xBFF4, 0x7736, 0xBFF5, 0x65F7,\n\t0xBFF6, 0x51B5, 0xBFF7, 0x4E8F, 0xBFF8, 0x76D4, 0xBFF9, 0x5CBF,\n\t0xBFFA, 0x7AA5, 0xBFFB, 0x8475, 0xBFFC, 0x594E, 0xBFFD, 0x9B41,\n\t0xBFFE, 0x5080, 0xC040, 0x7E5E, 0xC041, 0x7E5F, 0xC042, 0x7E60,\n\t0xC043, 0x7E61, 0xC044, 0x7E62, 0xC045, 0x7E63, 0xC046, 0x7E64,\n\t0xC047, 0x7E65, 0xC048, 0x7E66, 0xC049, 0x7E67, 0xC04A, 0x7E68,\n\t0xC04B, 0x7E69, 0xC04C, 0x7E6A, 0xC04D, 0x7E6B, 0xC04E, 0x7E6C,\n\t0xC04F, 0x7E6D, 0xC050, 0x7E6E, 0xC051, 0x7E6F, 0xC052, 0x7E70,\n\t0xC053, 0x7E71, 0xC054, 0x7E72, 0xC055, 0x7E73, 0xC056, 0x7E74,\n\t0xC057, 0x7E75, 0xC058, 0x7E76, 0xC059, 0x7E77, 0xC05A, 0x7E78,\n\t0xC05B, 0x7E79, 0xC05C, 0x7E7A, 0xC05D, 0x7E7B, 0xC05E, 0x7E7C,\n\t0xC05F, 0x7E7D, 0xC060, 0x7E7E, 0xC061, 0x7E7F, 0xC062, 0x7E80,\n\t0xC063, 0x7E81, 0xC064, 0x7E83, 0xC065, 0x7E84, 0xC066, 0x7E85,\n\t0xC067, 0x7E86, 0xC068, 0x7E87, 0xC069, 0x7E88, 0xC06A, 0x7E89,\n\t0xC06B, 0x7E8A, 0xC06C, 0x7E8B, 0xC06D, 0x7E8C, 0xC06E, 0x7E8D,\n\t0xC06F, 0x7E8E, 0xC070, 0x7E8F, 0xC071, 0x7E90, 0xC072, 0x7E91,\n\t0xC073, 0x7E92, 0xC074, 0x7E93, 0xC075, 0x7E94, 0xC076, 0x7E95,\n\t0xC077, 0x7E96, 0xC078, 0x7E97, 0xC079, 0x7E98, 0xC07A, 0x7E99,\n\t0xC07B, 0x7E9A, 0xC07C, 0x7E9C, 0xC07D, 0x7E9D, 0xC07E, 0x7E9E,\n\t0xC080, 0x7EAE, 0xC081, 0x7EB4, 0xC082, 0x7EBB, 0xC083, 0x7EBC,\n\t0xC084, 0x7ED6, 0xC085, 0x7EE4, 0xC086, 0x7EEC, 0xC087, 0x7EF9,\n\t0xC088, 0x7F0A, 0xC089, 0x7F10, 0xC08A, 0x7F1E, 0xC08B, 0x7F37,\n\t0xC08C, 0x7F39, 0xC08D, 0x7F3B, 0xC08E, 0x7F3C, 0xC08F, 0x7F3D,\n\t0xC090, 0x7F3E, 0xC091, 0x7F3F, 0xC092, 0x7F40, 0xC093, 0x7F41,\n\t0xC094, 0x7F43, 0xC095, 0x7F46, 0xC096, 0x7F47, 0xC097, 0x7F48,\n\t0xC098, 0x7F49, 0xC099, 0x7F4A, 0xC09A, 0x7F4B, 0xC09B, 0x7F4C,\n\t0xC09C, 0x7F4D, 0xC09D, 0x7F4E, 0xC09E, 0x7F4F, 0xC09F, 0x7F52,\n\t0xC0A0, 0x7F53, 0xC0A1, 0x9988, 0xC0A2, 0x6127, 0xC0A3, 0x6E83,\n\t0xC0A4, 0x5764, 0xC0A5, 0x6606, 0xC0A6, 0x6346, 0xC0A7, 0x56F0,\n\t0xC0A8, 0x62EC, 0xC0A9, 0x6269, 0xC0AA, 0x5ED3, 0xC0AB, 0x9614,\n\t0xC0AC, 0x5783, 0xC0AD, 0x62C9, 0xC0AE, 0x5587, 0xC0AF, 0x8721,\n\t0xC0B0, 0x814A, 0xC0B1, 0x8FA3, 0xC0B2, 0x5566, 0xC0B3, 0x83B1,\n\t0xC0B4, 0x6765, 0xC0B5, 0x8D56, 0xC0B6, 0x84DD, 0xC0B7, 0x5A6A,\n\t0xC0B8, 0x680F, 0xC0B9, 0x62E6, 0xC0BA, 0x7BEE, 0xC0BB, 0x9611,\n\t0xC0BC, 0x5170, 0xC0BD, 0x6F9C, 0xC0BE, 0x8C30, 0xC0BF, 0x63FD,\n\t0xC0C0, 0x89C8, 0xC0C1, 0x61D2, 0xC0C2, 0x7F06, 0xC0C3, 0x70C2,\n\t0xC0C4, 0x6EE5, 0xC0C5, 0x7405, 0xC0C6, 0x6994, 0xC0C7, 0x72FC,\n\t0xC0C8, 0x5ECA, 0xC0C9, 0x90CE, 0xC0CA, 0x6717, 0xC0CB, 0x6D6A,\n\t0xC0CC, 0x635E, 0xC0CD, 0x52B3, 0xC0CE, 0x7262, 0xC0CF, 0x8001,\n\t0xC0D0, 0x4F6C, 0xC0D1, 0x59E5, 0xC0D2, 0x916A, 0xC0D3, 0x70D9,\n\t0xC0D4, 0x6D9D, 0xC0D5, 0x52D2, 0xC0D6, 0x4E50, 0xC0D7, 0x96F7,\n\t0xC0D8, 0x956D, 0xC0D9, 0x857E, 0xC0DA, 0x78CA, 0xC0DB, 0x7D2F,\n\t0xC0DC, 0x5121, 0xC0DD, 0x5792, 0xC0DE, 0x64C2, 0xC0DF, 0x808B,\n\t0xC0E0, 0x7C7B, 0xC0E1, 0x6CEA, 0xC0E2, 0x68F1, 0xC0E3, 0x695E,\n\t0xC0E4, 0x51B7, 0xC0E5, 0x5398, 0xC0E6, 0x68A8, 0xC0E7, 0x7281,\n\t0xC0E8, 0x9ECE, 0xC0E9, 0x7BF1, 0xC0EA, 0x72F8, 0xC0EB, 0x79BB,\n\t0xC0EC, 0x6F13, 0xC0ED, 0x7406, 0xC0EE, 0x674E, 0xC0EF, 0x91CC,\n\t0xC0F0, 0x9CA4, 0xC0F1, 0x793C, 0xC0F2, 0x8389, 0xC0F3, 0x8354,\n\t0xC0F4, 0x540F, 0xC0F5, 0x6817, 0xC0F6, 0x4E3D, 0xC0F7, 0x5389,\n\t0xC0F8, 0x52B1, 0xC0F9, 0x783E, 0xC0FA, 0x5386, 0xC0FB, 0x5229,\n\t0xC0FC, 0x5088, 0xC0FD, 0x4F8B, 0xC0FE, 0x4FD0, 0xC140, 0x7F56,\n\t0xC141, 0x7F59, 0xC142, 0x7F5B, 0xC143, 0x7F5C, 0xC144, 0x7F5D,\n\t0xC145, 0x7F5E, 0xC146, 0x7F60, 0xC147, 0x7F63, 0xC148, 0x7F64,\n\t0xC149, 0x7F65, 0xC14A, 0x7F66, 0xC14B, 0x7F67, 0xC14C, 0x7F6B,\n\t0xC14D, 0x7F6C, 0xC14E, 0x7F6D, 0xC14F, 0x7F6F, 0xC150, 0x7F70,\n\t0xC151, 0x7F73, 0xC152, 0x7F75, 0xC153, 0x7F76, 0xC154, 0x7F77,\n\t0xC155, 0x7F78, 0xC156, 0x7F7A, 0xC157, 0x7F7B, 0xC158, 0x7F7C,\n\t0xC159, 0x7F7D, 0xC15A, 0x7F7F, 0xC15B, 0x7F80, 0xC15C, 0x7F82,\n\t0xC15D, 0x7F83, 0xC15E, 0x7F84, 0xC15F, 0x7F85, 0xC160, 0x7F86,\n\t0xC161, 0x7F87, 0xC162, 0x7F88, 0xC163, 0x7F89, 0xC164, 0x7F8B,\n\t0xC165, 0x7F8D, 0xC166, 0x7F8F, 0xC167, 0x7F90, 0xC168, 0x7F91,\n\t0xC169, 0x7F92, 0xC16A, 0x7F93, 0xC16B, 0x7F95, 0xC16C, 0x7F96,\n\t0xC16D, 0x7F97, 0xC16E, 0x7F98, 0xC16F, 0x7F99, 0xC170, 0x7F9B,\n\t0xC171, 0x7F9C, 0xC172, 0x7FA0, 0xC173, 0x7FA2, 0xC174, 0x7FA3,\n\t0xC175, 0x7FA5, 0xC176, 0x7FA6, 0xC177, 0x7FA8, 0xC178, 0x7FA9,\n\t0xC179, 0x7FAA, 0xC17A, 0x7FAB, 0xC17B, 0x7FAC, 0xC17C, 0x7FAD,\n\t0xC17D, 0x7FAE, 0xC17E, 0x7FB1, 0xC180, 0x7FB3, 0xC181, 0x7FB4,\n\t0xC182, 0x7FB5, 0xC183, 0x7FB6, 0xC184, 0x7FB7, 0xC185, 0x7FBA,\n\t0xC186, 0x7FBB, 0xC187, 0x7FBE, 0xC188, 0x7FC0, 0xC189, 0x7FC2,\n\t0xC18A, 0x7FC3, 0xC18B, 0x7FC4, 0xC18C, 0x7FC6, 0xC18D, 0x7FC7,\n\t0xC18E, 0x7FC8, 0xC18F, 0x7FC9, 0xC190, 0x7FCB, 0xC191, 0x7FCD,\n\t0xC192, 0x7FCF, 0xC193, 0x7FD0, 0xC194, 0x7FD1, 0xC195, 0x7FD2,\n\t0xC196, 0x7FD3, 0xC197, 0x7FD6, 0xC198, 0x7FD7, 0xC199, 0x7FD9,\n\t0xC19A, 0x7FDA, 0xC19B, 0x7FDB, 0xC19C, 0x7FDC, 0xC19D, 0x7FDD,\n\t0xC19E, 0x7FDE, 0xC19F, 0x7FE2, 0xC1A0, 0x7FE3, 0xC1A1, 0x75E2,\n\t0xC1A2, 0x7ACB, 0xC1A3, 0x7C92, 0xC1A4, 0x6CA5, 0xC1A5, 0x96B6,\n\t0xC1A6, 0x529B, 0xC1A7, 0x7483, 0xC1A8, 0x54E9, 0xC1A9, 0x4FE9,\n\t0xC1AA, 0x8054, 0xC1AB, 0x83B2, 0xC1AC, 0x8FDE, 0xC1AD, 0x9570,\n\t0xC1AE, 0x5EC9, 0xC1AF, 0x601C, 0xC1B0, 0x6D9F, 0xC1B1, 0x5E18,\n\t0xC1B2, 0x655B, 0xC1B3, 0x8138, 0xC1B4, 0x94FE, 0xC1B5, 0x604B,\n\t0xC1B6, 0x70BC, 0xC1B7, 0x7EC3, 0xC1B8, 0x7CAE, 0xC1B9, 0x51C9,\n\t0xC1BA, 0x6881, 0xC1BB, 0x7CB1, 0xC1BC, 0x826F, 0xC1BD, 0x4E24,\n\t0xC1BE, 0x8F86, 0xC1BF, 0x91CF, 0xC1C0, 0x667E, 0xC1C1, 0x4EAE,\n\t0xC1C2, 0x8C05, 0xC1C3, 0x64A9, 0xC1C4, 0x804A, 0xC1C5, 0x50DA,\n\t0xC1C6, 0x7597, 0xC1C7, 0x71CE, 0xC1C8, 0x5BE5, 0xC1C9, 0x8FBD,\n\t0xC1CA, 0x6F66, 0xC1CB, 0x4E86, 0xC1CC, 0x6482, 0xC1CD, 0x9563,\n\t0xC1CE, 0x5ED6, 0xC1CF, 0x6599, 0xC1D0, 0x5217, 0xC1D1, 0x88C2,\n\t0xC1D2, 0x70C8, 0xC1D3, 0x52A3, 0xC1D4, 0x730E, 0xC1D5, 0x7433,\n\t0xC1D6, 0x6797, 0xC1D7, 0x78F7, 0xC1D8, 0x9716, 0xC1D9, 0x4E34,\n\t0xC1DA, 0x90BB, 0xC1DB, 0x9CDE, 0xC1DC, 0x6DCB, 0xC1DD, 0x51DB,\n\t0xC1DE, 0x8D41, 0xC1DF, 0x541D, 0xC1E0, 0x62CE, 0xC1E1, 0x73B2,\n\t0xC1E2, 0x83F1, 0xC1E3, 0x96F6, 0xC1E4, 0x9F84, 0xC1E5, 0x94C3,\n\t0xC1E6, 0x4F36, 0xC1E7, 0x7F9A, 0xC1E8, 0x51CC, 0xC1E9, 0x7075,\n\t0xC1EA, 0x9675, 0xC1EB, 0x5CAD, 0xC1EC, 0x9886, 0xC1ED, 0x53E6,\n\t0xC1EE, 0x4EE4, 0xC1EF, 0x6E9C, 0xC1F0, 0x7409, 0xC1F1, 0x69B4,\n\t0xC1F2, 0x786B, 0xC1F3, 0x998F, 0xC1F4, 0x7559, 0xC1F5, 0x5218,\n\t0xC1F6, 0x7624, 0xC1F7, 0x6D41, 0xC1F8, 0x67F3, 0xC1F9, 0x516D,\n\t0xC1FA, 0x9F99, 0xC1FB, 0x804B, 0xC1FC, 0x5499, 0xC1FD, 0x7B3C,\n\t0xC1FE, 0x7ABF, 0xC240, 0x7FE4, 0xC241, 0x7FE7, 0xC242, 0x7FE8,\n\t0xC243, 0x7FEA, 0xC244, 0x7FEB, 0xC245, 0x7FEC, 0xC246, 0x7FED,\n\t0xC247, 0x7FEF, 0xC248, 0x7FF2, 0xC249, 0x7FF4, 0xC24A, 0x7FF5,\n\t0xC24B, 0x7FF6, 0xC24C, 0x7FF7, 0xC24D, 0x7FF8, 0xC24E, 0x7FF9,\n\t0xC24F, 0x7FFA, 0xC250, 0x7FFD, 0xC251, 0x7FFE, 0xC252, 0x7FFF,\n\t0xC253, 0x8002, 0xC254, 0x8007, 0xC255, 0x8008, 0xC256, 0x8009,\n\t0xC257, 0x800A, 0xC258, 0x800E, 0xC259, 0x800F, 0xC25A, 0x8011,\n\t0xC25B, 0x8013, 0xC25C, 0x801A, 0xC25D, 0x801B, 0xC25E, 0x801D,\n\t0xC25F, 0x801E, 0xC260, 0x801F, 0xC261, 0x8021, 0xC262, 0x8023,\n\t0xC263, 0x8024, 0xC264, 0x802B, 0xC265, 0x802C, 0xC266, 0x802D,\n\t0xC267, 0x802E, 0xC268, 0x802F, 0xC269, 0x8030, 0xC26A, 0x8032,\n\t0xC26B, 0x8034, 0xC26C, 0x8039, 0xC26D, 0x803A, 0xC26E, 0x803C,\n\t0xC26F, 0x803E, 0xC270, 0x8040, 0xC271, 0x8041, 0xC272, 0x8044,\n\t0xC273, 0x8045, 0xC274, 0x8047, 0xC275, 0x8048, 0xC276, 0x8049,\n\t0xC277, 0x804E, 0xC278, 0x804F, 0xC279, 0x8050, 0xC27A, 0x8051,\n\t0xC27B, 0x8053, 0xC27C, 0x8055, 0xC27D, 0x8056, 0xC27E, 0x8057,\n\t0xC280, 0x8059, 0xC281, 0x805B, 0xC282, 0x805C, 0xC283, 0x805D,\n\t0xC284, 0x805E, 0xC285, 0x805F, 0xC286, 0x8060, 0xC287, 0x8061,\n\t0xC288, 0x8062, 0xC289, 0x8063, 0xC28A, 0x8064, 0xC28B, 0x8065,\n\t0xC28C, 0x8066, 0xC28D, 0x8067, 0xC28E, 0x8068, 0xC28F, 0x806B,\n\t0xC290, 0x806C, 0xC291, 0x806D, 0xC292, 0x806E, 0xC293, 0x806F,\n\t0xC294, 0x8070, 0xC295, 0x8072, 0xC296, 0x8073, 0xC297, 0x8074,\n\t0xC298, 0x8075, 0xC299, 0x8076, 0xC29A, 0x8077, 0xC29B, 0x8078,\n\t0xC29C, 0x8079, 0xC29D, 0x807A, 0xC29E, 0x807B, 0xC29F, 0x807C,\n\t0xC2A0, 0x807D, 0xC2A1, 0x9686, 0xC2A2, 0x5784, 0xC2A3, 0x62E2,\n\t0xC2A4, 0x9647, 0xC2A5, 0x697C, 0xC2A6, 0x5A04, 0xC2A7, 0x6402,\n\t0xC2A8, 0x7BD3, 0xC2A9, 0x6F0F, 0xC2AA, 0x964B, 0xC2AB, 0x82A6,\n\t0xC2AC, 0x5362, 0xC2AD, 0x9885, 0xC2AE, 0x5E90, 0xC2AF, 0x7089,\n\t0xC2B0, 0x63B3, 0xC2B1, 0x5364, 0xC2B2, 0x864F, 0xC2B3, 0x9C81,\n\t0xC2B4, 0x9E93, 0xC2B5, 0x788C, 0xC2B6, 0x9732, 0xC2B7, 0x8DEF,\n\t0xC2B8, 0x8D42, 0xC2B9, 0x9E7F, 0xC2BA, 0x6F5E, 0xC2BB, 0x7984,\n\t0xC2BC, 0x5F55, 0xC2BD, 0x9646, 0xC2BE, 0x622E, 0xC2BF, 0x9A74,\n\t0xC2C0, 0x5415, 0xC2C1, 0x94DD, 0xC2C2, 0x4FA3, 0xC2C3, 0x65C5,\n\t0xC2C4, 0x5C65, 0xC2C5, 0x5C61, 0xC2C6, 0x7F15, 0xC2C7, 0x8651,\n\t0xC2C8, 0x6C2F, 0xC2C9, 0x5F8B, 0xC2CA, 0x7387, 0xC2CB, 0x6EE4,\n\t0xC2CC, 0x7EFF, 0xC2CD, 0x5CE6, 0xC2CE, 0x631B, 0xC2CF, 0x5B6A,\n\t0xC2D0, 0x6EE6, 0xC2D1, 0x5375, 0xC2D2, 0x4E71, 0xC2D3, 0x63A0,\n\t0xC2D4, 0x7565, 0xC2D5, 0x62A1, 0xC2D6, 0x8F6E, 0xC2D7, 0x4F26,\n\t0xC2D8, 0x4ED1, 0xC2D9, 0x6CA6, 0xC2DA, 0x7EB6, 0xC2DB, 0x8BBA,\n\t0xC2DC, 0x841D, 0xC2DD, 0x87BA, 0xC2DE, 0x7F57, 0xC2DF, 0x903B,\n\t0xC2E0, 0x9523, 0xC2E1, 0x7BA9, 0xC2E2, 0x9AA1, 0xC2E3, 0x88F8,\n\t0xC2E4, 0x843D, 0xC2E5, 0x6D1B, 0xC2E6, 0x9A86, 0xC2E7, 0x7EDC,\n\t0xC2E8, 0x5988, 0xC2E9, 0x9EBB, 0xC2EA, 0x739B, 0xC2EB, 0x7801,\n\t0xC2EC, 0x8682, 0xC2ED, 0x9A6C, 0xC2EE, 0x9A82, 0xC2EF, 0x561B,\n\t0xC2F0, 0x5417, 0xC2F1, 0x57CB, 0xC2F2, 0x4E70, 0xC2F3, 0x9EA6,\n\t0xC2F4, 0x5356, 0xC2F5, 0x8FC8, 0xC2F6, 0x8109, 0xC2F7, 0x7792,\n\t0xC2F8, 0x9992, 0xC2F9, 0x86EE, 0xC2FA, 0x6EE1, 0xC2FB, 0x8513,\n\t0xC2FC, 0x66FC, 0xC2FD, 0x6162, 0xC2FE, 0x6F2B, 0xC340, 0x807E,\n\t0xC341, 0x8081, 0xC342, 0x8082, 0xC343, 0x8085, 0xC344, 0x8088,\n\t0xC345, 0x808A, 0xC346, 0x808D, 0xC347, 0x808E, 0xC348, 0x808F,\n\t0xC349, 0x8090, 0xC34A, 0x8091, 0xC34B, 0x8092, 0xC34C, 0x8094,\n\t0xC34D, 0x8095, 0xC34E, 0x8097, 0xC34F, 0x8099, 0xC350, 0x809E,\n\t0xC351, 0x80A3, 0xC352, 0x80A6, 0xC353, 0x80A7, 0xC354, 0x80A8,\n\t0xC355, 0x80AC, 0xC356, 0x80B0, 0xC357, 0x80B3, 0xC358, 0x80B5,\n\t0xC359, 0x80B6, 0xC35A, 0x80B8, 0xC35B, 0x80B9, 0xC35C, 0x80BB,\n\t0xC35D, 0x80C5, 0xC35E, 0x80C7, 0xC35F, 0x80C8, 0xC360, 0x80C9,\n\t0xC361, 0x80CA, 0xC362, 0x80CB, 0xC363, 0x80CF, 0xC364, 0x80D0,\n\t0xC365, 0x80D1, 0xC366, 0x80D2, 0xC367, 0x80D3, 0xC368, 0x80D4,\n\t0xC369, 0x80D5, 0xC36A, 0x80D8, 0xC36B, 0x80DF, 0xC36C, 0x80E0,\n\t0xC36D, 0x80E2, 0xC36E, 0x80E3, 0xC36F, 0x80E6, 0xC370, 0x80EE,\n\t0xC371, 0x80F5, 0xC372, 0x80F7, 0xC373, 0x80F9, 0xC374, 0x80FB,\n\t0xC375, 0x80FE, 0xC376, 0x80FF, 0xC377, 0x8100, 0xC378, 0x8101,\n\t0xC379, 0x8103, 0xC37A, 0x8104, 0xC37B, 0x8105, 0xC37C, 0x8107,\n\t0xC37D, 0x8108, 0xC37E, 0x810B, 0xC380, 0x810C, 0xC381, 0x8115,\n\t0xC382, 0x8117, 0xC383, 0x8119, 0xC384, 0x811B, 0xC385, 0x811C,\n\t0xC386, 0x811D, 0xC387, 0x811F, 0xC388, 0x8120, 0xC389, 0x8121,\n\t0xC38A, 0x8122, 0xC38B, 0x8123, 0xC38C, 0x8124, 0xC38D, 0x8125,\n\t0xC38E, 0x8126, 0xC38F, 0x8127, 0xC390, 0x8128, 0xC391, 0x8129,\n\t0xC392, 0x812A, 0xC393, 0x812B, 0xC394, 0x812D, 0xC395, 0x812E,\n\t0xC396, 0x8130, 0xC397, 0x8133, 0xC398, 0x8134, 0xC399, 0x8135,\n\t0xC39A, 0x8137, 0xC39B, 0x8139, 0xC39C, 0x813A, 0xC39D, 0x813B,\n\t0xC39E, 0x813C, 0xC39F, 0x813D, 0xC3A0, 0x813F, 0xC3A1, 0x8C29,\n\t0xC3A2, 0x8292, 0xC3A3, 0x832B, 0xC3A4, 0x76F2, 0xC3A5, 0x6C13,\n\t0xC3A6, 0x5FD9, 0xC3A7, 0x83BD, 0xC3A8, 0x732B, 0xC3A9, 0x8305,\n\t0xC3AA, 0x951A, 0xC3AB, 0x6BDB, 0xC3AC, 0x77DB, 0xC3AD, 0x94C6,\n\t0xC3AE, 0x536F, 0xC3AF, 0x8302, 0xC3B0, 0x5192, 0xC3B1, 0x5E3D,\n\t0xC3B2, 0x8C8C, 0xC3B3, 0x8D38, 0xC3B4, 0x4E48, 0xC3B5, 0x73AB,\n\t0xC3B6, 0x679A, 0xC3B7, 0x6885, 0xC3B8, 0x9176, 0xC3B9, 0x9709,\n\t0xC3BA, 0x7164, 0xC3BB, 0x6CA1, 0xC3BC, 0x7709, 0xC3BD, 0x5A92,\n\t0xC3BE, 0x9541, 0xC3BF, 0x6BCF, 0xC3C0, 0x7F8E, 0xC3C1, 0x6627,\n\t0xC3C2, 0x5BD0, 0xC3C3, 0x59B9, 0xC3C4, 0x5A9A, 0xC3C5, 0x95E8,\n\t0xC3C6, 0x95F7, 0xC3C7, 0x4EEC, 0xC3C8, 0x840C, 0xC3C9, 0x8499,\n\t0xC3CA, 0x6AAC, 0xC3CB, 0x76DF, 0xC3CC, 0x9530, 0xC3CD, 0x731B,\n\t0xC3CE, 0x68A6, 0xC3CF, 0x5B5F, 0xC3D0, 0x772F, 0xC3D1, 0x919A,\n\t0xC3D2, 0x9761, 0xC3D3, 0x7CDC, 0xC3D4, 0x8FF7, 0xC3D5, 0x8C1C,\n\t0xC3D6, 0x5F25, 0xC3D7, 0x7C73, 0xC3D8, 0x79D8, 0xC3D9, 0x89C5,\n\t0xC3DA, 0x6CCC, 0xC3DB, 0x871C, 0xC3DC, 0x5BC6, 0xC3DD, 0x5E42,\n\t0xC3DE, 0x68C9, 0xC3DF, 0x7720, 0xC3E0, 0x7EF5, 0xC3E1, 0x5195,\n\t0xC3E2, 0x514D, 0xC3E3, 0x52C9, 0xC3E4, 0x5A29, 0xC3E5, 0x7F05,\n\t0xC3E6, 0x9762, 0xC3E7, 0x82D7, 0xC3E8, 0x63CF, 0xC3E9, 0x7784,\n\t0xC3EA, 0x85D0, 0xC3EB, 0x79D2, 0xC3EC, 0x6E3A, 0xC3ED, 0x5E99,\n\t0xC3EE, 0x5999, 0xC3EF, 0x8511, 0xC3F0, 0x706D, 0xC3F1, 0x6C11,\n\t0xC3F2, 0x62BF, 0xC3F3, 0x76BF, 0xC3F4, 0x654F, 0xC3F5, 0x60AF,\n\t0xC3F6, 0x95FD, 0xC3F7, 0x660E, 0xC3F8, 0x879F, 0xC3F9, 0x9E23,\n\t0xC3FA, 0x94ED, 0xC3FB, 0x540D, 0xC3FC, 0x547D, 0xC3FD, 0x8C2C,\n\t0xC3FE, 0x6478, 0xC440, 0x8140, 0xC441, 0x8141, 0xC442, 0x8142,\n\t0xC443, 0x8143, 0xC444, 0x8144, 0xC445, 0x8145, 0xC446, 0x8147,\n\t0xC447, 0x8149, 0xC448, 0x814D, 0xC449, 0x814E, 0xC44A, 0x814F,\n\t0xC44B, 0x8152, 0xC44C, 0x8156, 0xC44D, 0x8157, 0xC44E, 0x8158,\n\t0xC44F, 0x815B, 0xC450, 0x815C, 0xC451, 0x815D, 0xC452, 0x815E,\n\t0xC453, 0x815F, 0xC454, 0x8161, 0xC455, 0x8162, 0xC456, 0x8163,\n\t0xC457, 0x8164, 0xC458, 0x8166, 0xC459, 0x8168, 0xC45A, 0x816A,\n\t0xC45B, 0x816B, 0xC45C, 0x816C, 0xC45D, 0x816F, 0xC45E, 0x8172,\n\t0xC45F, 0x8173, 0xC460, 0x8175, 0xC461, 0x8176, 0xC462, 0x8177,\n\t0xC463, 0x8178, 0xC464, 0x8181, 0xC465, 0x8183, 0xC466, 0x8184,\n\t0xC467, 0x8185, 0xC468, 0x8186, 0xC469, 0x8187, 0xC46A, 0x8189,\n\t0xC46B, 0x818B, 0xC46C, 0x818C, 0xC46D, 0x818D, 0xC46E, 0x818E,\n\t0xC46F, 0x8190, 0xC470, 0x8192, 0xC471, 0x8193, 0xC472, 0x8194,\n\t0xC473, 0x8195, 0xC474, 0x8196, 0xC475, 0x8197, 0xC476, 0x8199,\n\t0xC477, 0x819A, 0xC478, 0x819E, 0xC479, 0x819F, 0xC47A, 0x81A0,\n\t0xC47B, 0x81A1, 0xC47C, 0x81A2, 0xC47D, 0x81A4, 0xC47E, 0x81A5,\n\t0xC480, 0x81A7, 0xC481, 0x81A9, 0xC482, 0x81AB, 0xC483, 0x81AC,\n\t0xC484, 0x81AD, 0xC485, 0x81AE, 0xC486, 0x81AF, 0xC487, 0x81B0,\n\t0xC488, 0x81B1, 0xC489, 0x81B2, 0xC48A, 0x81B4, 0xC48B, 0x81B5,\n\t0xC48C, 0x81B6, 0xC48D, 0x81B7, 0xC48E, 0x81B8, 0xC48F, 0x81B9,\n\t0xC490, 0x81BC, 0xC491, 0x81BD, 0xC492, 0x81BE, 0xC493, 0x81BF,\n\t0xC494, 0x81C4, 0xC495, 0x81C5, 0xC496, 0x81C7, 0xC497, 0x81C8,\n\t0xC498, 0x81C9, 0xC499, 0x81CB, 0xC49A, 0x81CD, 0xC49B, 0x81CE,\n\t0xC49C, 0x81CF, 0xC49D, 0x81D0, 0xC49E, 0x81D1, 0xC49F, 0x81D2,\n\t0xC4A0, 0x81D3, 0xC4A1, 0x6479, 0xC4A2, 0x8611, 0xC4A3, 0x6A21,\n\t0xC4A4, 0x819C, 0xC4A5, 0x78E8, 0xC4A6, 0x6469, 0xC4A7, 0x9B54,\n\t0xC4A8, 0x62B9, 0xC4A9, 0x672B, 0xC4AA, 0x83AB, 0xC4AB, 0x58A8,\n\t0xC4AC, 0x9ED8, 0xC4AD, 0x6CAB, 0xC4AE, 0x6F20, 0xC4AF, 0x5BDE,\n\t0xC4B0, 0x964C, 0xC4B1, 0x8C0B, 0xC4B2, 0x725F, 0xC4B3, 0x67D0,\n\t0xC4B4, 0x62C7, 0xC4B5, 0x7261, 0xC4B6, 0x4EA9, 0xC4B7, 0x59C6,\n\t0xC4B8, 0x6BCD, 0xC4B9, 0x5893, 0xC4BA, 0x66AE, 0xC4BB, 0x5E55,\n\t0xC4BC, 0x52DF, 0xC4BD, 0x6155, 0xC4BE, 0x6728, 0xC4BF, 0x76EE,\n\t0xC4C0, 0x7766, 0xC4C1, 0x7267, 0xC4C2, 0x7A46, 0xC4C3, 0x62FF,\n\t0xC4C4, 0x54EA, 0xC4C5, 0x5450, 0xC4C6, 0x94A0, 0xC4C7, 0x90A3,\n\t0xC4C8, 0x5A1C, 0xC4C9, 0x7EB3, 0xC4CA, 0x6C16, 0xC4CB, 0x4E43,\n\t0xC4CC, 0x5976, 0xC4CD, 0x8010, 0xC4CE, 0x5948, 0xC4CF, 0x5357,\n\t0xC4D0, 0x7537, 0xC4D1, 0x96BE, 0xC4D2, 0x56CA, 0xC4D3, 0x6320,\n\t0xC4D4, 0x8111, 0xC4D5, 0x607C, 0xC4D6, 0x95F9, 0xC4D7, 0x6DD6,\n\t0xC4D8, 0x5462, 0xC4D9, 0x9981, 0xC4DA, 0x5185, 0xC4DB, 0x5AE9,\n\t0xC4DC, 0x80FD, 0xC4DD, 0x59AE, 0xC4DE, 0x9713, 0xC4DF, 0x502A,\n\t0xC4E0, 0x6CE5, 0xC4E1, 0x5C3C, 0xC4E2, 0x62DF, 0xC4E3, 0x4F60,\n\t0xC4E4, 0x533F, 0xC4E5, 0x817B, 0xC4E6, 0x9006, 0xC4E7, 0x6EBA,\n\t0xC4E8, 0x852B, 0xC4E9, 0x62C8, 0xC4EA, 0x5E74, 0xC4EB, 0x78BE,\n\t0xC4EC, 0x64B5, 0xC4ED, 0x637B, 0xC4EE, 0x5FF5, 0xC4EF, 0x5A18,\n\t0xC4F0, 0x917F, 0xC4F1, 0x9E1F, 0xC4F2, 0x5C3F, 0xC4F3, 0x634F,\n\t0xC4F4, 0x8042, 0xC4F5, 0x5B7D, 0xC4F6, 0x556E, 0xC4F7, 0x954A,\n\t0xC4F8, 0x954D, 0xC4F9, 0x6D85, 0xC4FA, 0x60A8, 0xC4FB, 0x67E0,\n\t0xC4FC, 0x72DE, 0xC4FD, 0x51DD, 0xC4FE, 0x5B81, 0xC540, 0x81D4,\n\t0xC541, 0x81D5, 0xC542, 0x81D6, 0xC543, 0x81D7, 0xC544, 0x81D8,\n\t0xC545, 0x81D9, 0xC546, 0x81DA, 0xC547, 0x81DB, 0xC548, 0x81DC,\n\t0xC549, 0x81DD, 0xC54A, 0x81DE, 0xC54B, 0x81DF, 0xC54C, 0x81E0,\n\t0xC54D, 0x81E1, 0xC54E, 0x81E2, 0xC54F, 0x81E4, 0xC550, 0x81E5,\n\t0xC551, 0x81E6, 0xC552, 0x81E8, 0xC553, 0x81E9, 0xC554, 0x81EB,\n\t0xC555, 0x81EE, 0xC556, 0x81EF, 0xC557, 0x81F0, 0xC558, 0x81F1,\n\t0xC559, 0x81F2, 0xC55A, 0x81F5, 0xC55B, 0x81F6, 0xC55C, 0x81F7,\n\t0xC55D, 0x81F8, 0xC55E, 0x81F9, 0xC55F, 0x81FA, 0xC560, 0x81FD,\n\t0xC561, 0x81FF, 0xC562, 0x8203, 0xC563, 0x8207, 0xC564, 0x8208,\n\t0xC565, 0x8209, 0xC566, 0x820A, 0xC567, 0x820B, 0xC568, 0x820E,\n\t0xC569, 0x820F, 0xC56A, 0x8211, 0xC56B, 0x8213, 0xC56C, 0x8215,\n\t0xC56D, 0x8216, 0xC56E, 0x8217, 0xC56F, 0x8218, 0xC570, 0x8219,\n\t0xC571, 0x821A, 0xC572, 0x821D, 0xC573, 0x8220, 0xC574, 0x8224,\n\t0xC575, 0x8225, 0xC576, 0x8226, 0xC577, 0x8227, 0xC578, 0x8229,\n\t0xC579, 0x822E, 0xC57A, 0x8232, 0xC57B, 0x823A, 0xC57C, 0x823C,\n\t0xC57D, 0x823D, 0xC57E, 0x823F, 0xC580, 0x8240, 0xC581, 0x8241,\n\t0xC582, 0x8242, 0xC583, 0x8243, 0xC584, 0x8245, 0xC585, 0x8246,\n\t0xC586, 0x8248, 0xC587, 0x824A, 0xC588, 0x824C, 0xC589, 0x824D,\n\t0xC58A, 0x824E, 0xC58B, 0x8250, 0xC58C, 0x8251, 0xC58D, 0x8252,\n\t0xC58E, 0x8253, 0xC58F, 0x8254, 0xC590, 0x8255, 0xC591, 0x8256,\n\t0xC592, 0x8257, 0xC593, 0x8259, 0xC594, 0x825B, 0xC595, 0x825C,\n\t0xC596, 0x825D, 0xC597, 0x825E, 0xC598, 0x8260, 0xC599, 0x8261,\n\t0xC59A, 0x8262, 0xC59B, 0x8263, 0xC59C, 0x8264, 0xC59D, 0x8265,\n\t0xC59E, 0x8266, 0xC59F, 0x8267, 0xC5A0, 0x8269, 0xC5A1, 0x62E7,\n\t0xC5A2, 0x6CDE, 0xC5A3, 0x725B, 0xC5A4, 0x626D, 0xC5A5, 0x94AE,\n\t0xC5A6, 0x7EBD, 0xC5A7, 0x8113, 0xC5A8, 0x6D53, 0xC5A9, 0x519C,\n\t0xC5AA, 0x5F04, 0xC5AB, 0x5974, 0xC5AC, 0x52AA, 0xC5AD, 0x6012,\n\t0xC5AE, 0x5973, 0xC5AF, 0x6696, 0xC5B0, 0x8650, 0xC5B1, 0x759F,\n\t0xC5B2, 0x632A, 0xC5B3, 0x61E6, 0xC5B4, 0x7CEF, 0xC5B5, 0x8BFA,\n\t0xC5B6, 0x54E6, 0xC5B7, 0x6B27, 0xC5B8, 0x9E25, 0xC5B9, 0x6BB4,\n\t0xC5BA, 0x85D5, 0xC5BB, 0x5455, 0xC5BC, 0x5076, 0xC5BD, 0x6CA4,\n\t0xC5BE, 0x556A, 0xC5BF, 0x8DB4, 0xC5C0, 0x722C, 0xC5C1, 0x5E15,\n\t0xC5C2, 0x6015, 0xC5C3, 0x7436, 0xC5C4, 0x62CD, 0xC5C5, 0x6392,\n\t0xC5C6, 0x724C, 0xC5C7, 0x5F98, 0xC5C8, 0x6E43, 0xC5C9, 0x6D3E,\n\t0xC5CA, 0x6500, 0xC5CB, 0x6F58, 0xC5CC, 0x76D8, 0xC5CD, 0x78D0,\n\t0xC5CE, 0x76FC, 0xC5CF, 0x7554, 0xC5D0, 0x5224, 0xC5D1, 0x53DB,\n\t0xC5D2, 0x4E53, 0xC5D3, 0x5E9E, 0xC5D4, 0x65C1, 0xC5D5, 0x802A,\n\t0xC5D6, 0x80D6, 0xC5D7, 0x629B, 0xC5D8, 0x5486, 0xC5D9, 0x5228,\n\t0xC5DA, 0x70AE, 0xC5DB, 0x888D, 0xC5DC, 0x8DD1, 0xC5DD, 0x6CE1,\n\t0xC5DE, 0x5478, 0xC5DF, 0x80DA, 0xC5E0, 0x57F9, 0xC5E1, 0x88F4,\n\t0xC5E2, 0x8D54, 0xC5E3, 0x966A, 0xC5E4, 0x914D, 0xC5E5, 0x4F69,\n\t0xC5E6, 0x6C9B, 0xC5E7, 0x55B7, 0xC5E8, 0x76C6, 0xC5E9, 0x7830,\n\t0xC5EA, 0x62A8, 0xC5EB, 0x70F9, 0xC5EC, 0x6F8E, 0xC5ED, 0x5F6D,\n\t0xC5EE, 0x84EC, 0xC5EF, 0x68DA, 0xC5F0, 0x787C, 0xC5F1, 0x7BF7,\n\t0xC5F2, 0x81A8, 0xC5F3, 0x670B, 0xC5F4, 0x9E4F, 0xC5F5, 0x6367,\n\t0xC5F6, 0x78B0, 0xC5F7, 0x576F, 0xC5F8, 0x7812, 0xC5F9, 0x9739,\n\t0xC5FA, 0x6279, 0xC5FB, 0x62AB, 0xC5FC, 0x5288, 0xC5FD, 0x7435,\n\t0xC5FE, 0x6BD7, 0xC640, 0x826A, 0xC641, 0x826B, 0xC642, 0x826C,\n\t0xC643, 0x826D, 0xC644, 0x8271, 0xC645, 0x8275, 0xC646, 0x8276,\n\t0xC647, 0x8277, 0xC648, 0x8278, 0xC649, 0x827B, 0xC64A, 0x827C,\n\t0xC64B, 0x8280, 0xC64C, 0x8281, 0xC64D, 0x8283, 0xC64E, 0x8285,\n\t0xC64F, 0x8286, 0xC650, 0x8287, 0xC651, 0x8289, 0xC652, 0x828C,\n\t0xC653, 0x8290, 0xC654, 0x8293, 0xC655, 0x8294, 0xC656, 0x8295,\n\t0xC657, 0x8296, 0xC658, 0x829A, 0xC659, 0x829B, 0xC65A, 0x829E,\n\t0xC65B, 0x82A0, 0xC65C, 0x82A2, 0xC65D, 0x82A3, 0xC65E, 0x82A7,\n\t0xC65F, 0x82B2, 0xC660, 0x82B5, 0xC661, 0x82B6, 0xC662, 0x82BA,\n\t0xC663, 0x82BB, 0xC664, 0x82BC, 0xC665, 0x82BF, 0xC666, 0x82C0,\n\t0xC667, 0x82C2, 0xC668, 0x82C3, 0xC669, 0x82C5, 0xC66A, 0x82C6,\n\t0xC66B, 0x82C9, 0xC66C, 0x82D0, 0xC66D, 0x82D6, 0xC66E, 0x82D9,\n\t0xC66F, 0x82DA, 0xC670, 0x82DD, 0xC671, 0x82E2, 0xC672, 0x82E7,\n\t0xC673, 0x82E8, 0xC674, 0x82E9, 0xC675, 0x82EA, 0xC676, 0x82EC,\n\t0xC677, 0x82ED, 0xC678, 0x82EE, 0xC679, 0x82F0, 0xC67A, 0x82F2,\n\t0xC67B, 0x82F3, 0xC67C, 0x82F5, 0xC67D, 0x82F6, 0xC67E, 0x82F8,\n\t0xC680, 0x82FA, 0xC681, 0x82FC, 0xC682, 0x82FD, 0xC683, 0x82FE,\n\t0xC684, 0x82FF, 0xC685, 0x8300, 0xC686, 0x830A, 0xC687, 0x830B,\n\t0xC688, 0x830D, 0xC689, 0x8310, 0xC68A, 0x8312, 0xC68B, 0x8313,\n\t0xC68C, 0x8316, 0xC68D, 0x8318, 0xC68E, 0x8319, 0xC68F, 0x831D,\n\t0xC690, 0x831E, 0xC691, 0x831F, 0xC692, 0x8320, 0xC693, 0x8321,\n\t0xC694, 0x8322, 0xC695, 0x8323, 0xC696, 0x8324, 0xC697, 0x8325,\n\t0xC698, 0x8326, 0xC699, 0x8329, 0xC69A, 0x832A, 0xC69B, 0x832E,\n\t0xC69C, 0x8330, 0xC69D, 0x8332, 0xC69E, 0x8337, 0xC69F, 0x833B,\n\t0xC6A0, 0x833D, 0xC6A1, 0x5564, 0xC6A2, 0x813E, 0xC6A3, 0x75B2,\n\t0xC6A4, 0x76AE, 0xC6A5, 0x5339, 0xC6A6, 0x75DE, 0xC6A7, 0x50FB,\n\t0xC6A8, 0x5C41, 0xC6A9, 0x8B6C, 0xC6AA, 0x7BC7, 0xC6AB, 0x504F,\n\t0xC6AC, 0x7247, 0xC6AD, 0x9A97, 0xC6AE, 0x98D8, 0xC6AF, 0x6F02,\n\t0xC6B0, 0x74E2, 0xC6B1, 0x7968, 0xC6B2, 0x6487, 0xC6B3, 0x77A5,\n\t0xC6B4, 0x62FC, 0xC6B5, 0x9891, 0xC6B6, 0x8D2B, 0xC6B7, 0x54C1,\n\t0xC6B8, 0x8058, 0xC6B9, 0x4E52, 0xC6BA, 0x576A, 0xC6BB, 0x82F9,\n\t0xC6BC, 0x840D, 0xC6BD, 0x5E73, 0xC6BE, 0x51ED, 0xC6BF, 0x74F6,\n\t0xC6C0, 0x8BC4, 0xC6C1, 0x5C4F, 0xC6C2, 0x5761, 0xC6C3, 0x6CFC,\n\t0xC6C4, 0x9887, 0xC6C5, 0x5A46, 0xC6C6, 0x7834, 0xC6C7, 0x9B44,\n\t0xC6C8, 0x8FEB, 0xC6C9, 0x7C95, 0xC6CA, 0x5256, 0xC6CB, 0x6251,\n\t0xC6CC, 0x94FA, 0xC6CD, 0x4EC6, 0xC6CE, 0x8386, 0xC6CF, 0x8461,\n\t0xC6D0, 0x83E9, 0xC6D1, 0x84B2, 0xC6D2, 0x57D4, 0xC6D3, 0x6734,\n\t0xC6D4, 0x5703, 0xC6D5, 0x666E, 0xC6D6, 0x6D66, 0xC6D7, 0x8C31,\n\t0xC6D8, 0x66DD, 0xC6D9, 0x7011, 0xC6DA, 0x671F, 0xC6DB, 0x6B3A,\n\t0xC6DC, 0x6816, 0xC6DD, 0x621A, 0xC6DE, 0x59BB, 0xC6DF, 0x4E03,\n\t0xC6E0, 0x51C4, 0xC6E1, 0x6F06, 0xC6E2, 0x67D2, 0xC6E3, 0x6C8F,\n\t0xC6E4, 0x5176, 0xC6E5, 0x68CB, 0xC6E6, 0x5947, 0xC6E7, 0x6B67,\n\t0xC6E8, 0x7566, 0xC6E9, 0x5D0E, 0xC6EA, 0x8110, 0xC6EB, 0x9F50,\n\t0xC6EC, 0x65D7, 0xC6ED, 0x7948, 0xC6EE, 0x7941, 0xC6EF, 0x9A91,\n\t0xC6F0, 0x8D77, 0xC6F1, 0x5C82, 0xC6F2, 0x4E5E, 0xC6F3, 0x4F01,\n\t0xC6F4, 0x542F, 0xC6F5, 0x5951, 0xC6F6, 0x780C, 0xC6F7, 0x5668,\n\t0xC6F8, 0x6C14, 0xC6F9, 0x8FC4, 0xC6FA, 0x5F03, 0xC6FB, 0x6C7D,\n\t0xC6FC, 0x6CE3, 0xC6FD, 0x8BAB, 0xC6FE, 0x6390, 0xC740, 0x833E,\n\t0xC741, 0x833F, 0xC742, 0x8341, 0xC743, 0x8342, 0xC744, 0x8344,\n\t0xC745, 0x8345, 0xC746, 0x8348, 0xC747, 0x834A, 0xC748, 0x834B,\n\t0xC749, 0x834C, 0xC74A, 0x834D, 0xC74B, 0x834E, 0xC74C, 0x8353,\n\t0xC74D, 0x8355, 0xC74E, 0x8356, 0xC74F, 0x8357, 0xC750, 0x8358,\n\t0xC751, 0x8359, 0xC752, 0x835D, 0xC753, 0x8362, 0xC754, 0x8370,\n\t0xC755, 0x8371, 0xC756, 0x8372, 0xC757, 0x8373, 0xC758, 0x8374,\n\t0xC759, 0x8375, 0xC75A, 0x8376, 0xC75B, 0x8379, 0xC75C, 0x837A,\n\t0xC75D, 0x837E, 0xC75E, 0x837F, 0xC75F, 0x8380, 0xC760, 0x8381,\n\t0xC761, 0x8382, 0xC762, 0x8383, 0xC763, 0x8384, 0xC764, 0x8387,\n\t0xC765, 0x8388, 0xC766, 0x838A, 0xC767, 0x838B, 0xC768, 0x838C,\n\t0xC769, 0x838D, 0xC76A, 0x838F, 0xC76B, 0x8390, 0xC76C, 0x8391,\n\t0xC76D, 0x8394, 0xC76E, 0x8395, 0xC76F, 0x8396, 0xC770, 0x8397,\n\t0xC771, 0x8399, 0xC772, 0x839A, 0xC773, 0x839D, 0xC774, 0x839F,\n\t0xC775, 0x83A1, 0xC776, 0x83A2, 0xC777, 0x83A3, 0xC778, 0x83A4,\n\t0xC779, 0x83A5, 0xC77A, 0x83A6, 0xC77B, 0x83A7, 0xC77C, 0x83AC,\n\t0xC77D, 0x83AD, 0xC77E, 0x83AE, 0xC780, 0x83AF, 0xC781, 0x83B5,\n\t0xC782, 0x83BB, 0xC783, 0x83BE, 0xC784, 0x83BF, 0xC785, 0x83C2,\n\t0xC786, 0x83C3, 0xC787, 0x83C4, 0xC788, 0x83C6, 0xC789, 0x83C8,\n\t0xC78A, 0x83C9, 0xC78B, 0x83CB, 0xC78C, 0x83CD, 0xC78D, 0x83CE,\n\t0xC78E, 0x83D0, 0xC78F, 0x83D1, 0xC790, 0x83D2, 0xC791, 0x83D3,\n\t0xC792, 0x83D5, 0xC793, 0x83D7, 0xC794, 0x83D9, 0xC795, 0x83DA,\n\t0xC796, 0x83DB, 0xC797, 0x83DE, 0xC798, 0x83E2, 0xC799, 0x83E3,\n\t0xC79A, 0x83E4, 0xC79B, 0x83E6, 0xC79C, 0x83E7, 0xC79D, 0x83E8,\n\t0xC79E, 0x83EB, 0xC79F, 0x83EC, 0xC7A0, 0x83ED, 0xC7A1, 0x6070,\n\t0xC7A2, 0x6D3D, 0xC7A3, 0x7275, 0xC7A4, 0x6266, 0xC7A5, 0x948E,\n\t0xC7A6, 0x94C5, 0xC7A7, 0x5343, 0xC7A8, 0x8FC1, 0xC7A9, 0x7B7E,\n\t0xC7AA, 0x4EDF, 0xC7AB, 0x8C26, 0xC7AC, 0x4E7E, 0xC7AD, 0x9ED4,\n\t0xC7AE, 0x94B1, 0xC7AF, 0x94B3, 0xC7B0, 0x524D, 0xC7B1, 0x6F5C,\n\t0xC7B2, 0x9063, 0xC7B3, 0x6D45, 0xC7B4, 0x8C34, 0xC7B5, 0x5811,\n\t0xC7B6, 0x5D4C, 0xC7B7, 0x6B20, 0xC7B8, 0x6B49, 0xC7B9, 0x67AA,\n\t0xC7BA, 0x545B, 0xC7BB, 0x8154, 0xC7BC, 0x7F8C, 0xC7BD, 0x5899,\n\t0xC7BE, 0x8537, 0xC7BF, 0x5F3A, 0xC7C0, 0x62A2, 0xC7C1, 0x6A47,\n\t0xC7C2, 0x9539, 0xC7C3, 0x6572, 0xC7C4, 0x6084, 0xC7C5, 0x6865,\n\t0xC7C6, 0x77A7, 0xC7C7, 0x4E54, 0xC7C8, 0x4FA8, 0xC7C9, 0x5DE7,\n\t0xC7CA, 0x9798, 0xC7CB, 0x64AC, 0xC7CC, 0x7FD8, 0xC7CD, 0x5CED,\n\t0xC7CE, 0x4FCF, 0xC7CF, 0x7A8D, 0xC7D0, 0x5207, 0xC7D1, 0x8304,\n\t0xC7D2, 0x4E14, 0xC7D3, 0x602F, 0xC7D4, 0x7A83, 0xC7D5, 0x94A6,\n\t0xC7D6, 0x4FB5, 0xC7D7, 0x4EB2, 0xC7D8, 0x79E6, 0xC7D9, 0x7434,\n\t0xC7DA, 0x52E4, 0xC7DB, 0x82B9, 0xC7DC, 0x64D2, 0xC7DD, 0x79BD,\n\t0xC7DE, 0x5BDD, 0xC7DF, 0x6C81, 0xC7E0, 0x9752, 0xC7E1, 0x8F7B,\n\t0xC7E2, 0x6C22, 0xC7E3, 0x503E, 0xC7E4, 0x537F, 0xC7E5, 0x6E05,\n\t0xC7E6, 0x64CE, 0xC7E7, 0x6674, 0xC7E8, 0x6C30, 0xC7E9, 0x60C5,\n\t0xC7EA, 0x9877, 0xC7EB, 0x8BF7, 0xC7EC, 0x5E86, 0xC7ED, 0x743C,\n\t0xC7EE, 0x7A77, 0xC7EF, 0x79CB, 0xC7F0, 0x4E18, 0xC7F1, 0x90B1,\n\t0xC7F2, 0x7403, 0xC7F3, 0x6C42, 0xC7F4, 0x56DA, 0xC7F5, 0x914B,\n\t0xC7F6, 0x6CC5, 0xC7F7, 0x8D8B, 0xC7F8, 0x533A, 0xC7F9, 0x86C6,\n\t0xC7FA, 0x66F2, 0xC7FB, 0x8EAF, 0xC7FC, 0x5C48, 0xC7FD, 0x9A71,\n\t0xC7FE, 0x6E20, 0xC840, 0x83EE, 0xC841, 0x83EF, 0xC842, 0x83F3,\n\t0xC843, 0x83F4, 0xC844, 0x83F5, 0xC845, 0x83F6, 0xC846, 0x83F7,\n\t0xC847, 0x83FA, 0xC848, 0x83FB, 0xC849, 0x83FC, 0xC84A, 0x83FE,\n\t0xC84B, 0x83FF, 0xC84C, 0x8400, 0xC84D, 0x8402, 0xC84E, 0x8405,\n\t0xC84F, 0x8407, 0xC850, 0x8408, 0xC851, 0x8409, 0xC852, 0x840A,\n\t0xC853, 0x8410, 0xC854, 0x8412, 0xC855, 0x8413, 0xC856, 0x8414,\n\t0xC857, 0x8415, 0xC858, 0x8416, 0xC859, 0x8417, 0xC85A, 0x8419,\n\t0xC85B, 0x841A, 0xC85C, 0x841B, 0xC85D, 0x841E, 0xC85E, 0x841F,\n\t0xC85F, 0x8420, 0xC860, 0x8421, 0xC861, 0x8422, 0xC862, 0x8423,\n\t0xC863, 0x8429, 0xC864, 0x842A, 0xC865, 0x842B, 0xC866, 0x842C,\n\t0xC867, 0x842D, 0xC868, 0x842E, 0xC869, 0x842F, 0xC86A, 0x8430,\n\t0xC86B, 0x8432, 0xC86C, 0x8433, 0xC86D, 0x8434, 0xC86E, 0x8435,\n\t0xC86F, 0x8436, 0xC870, 0x8437, 0xC871, 0x8439, 0xC872, 0x843A,\n\t0xC873, 0x843B, 0xC874, 0x843E, 0xC875, 0x843F, 0xC876, 0x8440,\n\t0xC877, 0x8441, 0xC878, 0x8442, 0xC879, 0x8443, 0xC87A, 0x8444,\n\t0xC87B, 0x8445, 0xC87C, 0x8447, 0xC87D, 0x8448, 0xC87E, 0x8449,\n\t0xC880, 0x844A, 0xC881, 0x844B, 0xC882, 0x844C, 0xC883, 0x844D,\n\t0xC884, 0x844E, 0xC885, 0x844F, 0xC886, 0x8450, 0xC887, 0x8452,\n\t0xC888, 0x8453, 0xC889, 0x8454, 0xC88A, 0x8455, 0xC88B, 0x8456,\n\t0xC88C, 0x8458, 0xC88D, 0x845D, 0xC88E, 0x845E, 0xC88F, 0x845F,\n\t0xC890, 0x8460, 0xC891, 0x8462, 0xC892, 0x8464, 0xC893, 0x8465,\n\t0xC894, 0x8466, 0xC895, 0x8467, 0xC896, 0x8468, 0xC897, 0x846A,\n\t0xC898, 0x846E, 0xC899, 0x846F, 0xC89A, 0x8470, 0xC89B, 0x8472,\n\t0xC89C, 0x8474, 0xC89D, 0x8477, 0xC89E, 0x8479, 0xC89F, 0x847B,\n\t0xC8A0, 0x847C, 0xC8A1, 0x53D6, 0xC8A2, 0x5A36, 0xC8A3, 0x9F8B,\n\t0xC8A4, 0x8DA3, 0xC8A5, 0x53BB, 0xC8A6, 0x5708, 0xC8A7, 0x98A7,\n\t0xC8A8, 0x6743, 0xC8A9, 0x919B, 0xC8AA, 0x6CC9, 0xC8AB, 0x5168,\n\t0xC8AC, 0x75CA, 0xC8AD, 0x62F3, 0xC8AE, 0x72AC, 0xC8AF, 0x5238,\n\t0xC8B0, 0x529D, 0xC8B1, 0x7F3A, 0xC8B2, 0x7094, 0xC8B3, 0x7638,\n\t0xC8B4, 0x5374, 0xC8B5, 0x9E4A, 0xC8B6, 0x69B7, 0xC8B7, 0x786E,\n\t0xC8B8, 0x96C0, 0xC8B9, 0x88D9, 0xC8BA, 0x7FA4, 0xC8BB, 0x7136,\n\t0xC8BC, 0x71C3, 0xC8BD, 0x5189, 0xC8BE, 0x67D3, 0xC8BF, 0x74E4,\n\t0xC8C0, 0x58E4, 0xC8C1, 0x6518, 0xC8C2, 0x56B7, 0xC8C3, 0x8BA9,\n\t0xC8C4, 0x9976, 0xC8C5, 0x6270, 0xC8C6, 0x7ED5, 0xC8C7, 0x60F9,\n\t0xC8C8, 0x70ED, 0xC8C9, 0x58EC, 0xC8CA, 0x4EC1, 0xC8CB, 0x4EBA,\n\t0xC8CC, 0x5FCD, 0xC8CD, 0x97E7, 0xC8CE, 0x4EFB, 0xC8CF, 0x8BA4,\n\t0xC8D0, 0x5203, 0xC8D1, 0x598A, 0xC8D2, 0x7EAB, 0xC8D3, 0x6254,\n\t0xC8D4, 0x4ECD, 0xC8D5, 0x65E5, 0xC8D6, 0x620E, 0xC8D7, 0x8338,\n\t0xC8D8, 0x84C9, 0xC8D9, 0x8363, 0xC8DA, 0x878D, 0xC8DB, 0x7194,\n\t0xC8DC, 0x6EB6, 0xC8DD, 0x5BB9, 0xC8DE, 0x7ED2, 0xC8DF, 0x5197,\n\t0xC8E0, 0x63C9, 0xC8E1, 0x67D4, 0xC8E2, 0x8089, 0xC8E3, 0x8339,\n\t0xC8E4, 0x8815, 0xC8E5, 0x5112, 0xC8E6, 0x5B7A, 0xC8E7, 0x5982,\n\t0xC8E8, 0x8FB1, 0xC8E9, 0x4E73, 0xC8EA, 0x6C5D, 0xC8EB, 0x5165,\n\t0xC8EC, 0x8925, 0xC8ED, 0x8F6F, 0xC8EE, 0x962E, 0xC8EF, 0x854A,\n\t0xC8F0, 0x745E, 0xC8F1, 0x9510, 0xC8F2, 0x95F0, 0xC8F3, 0x6DA6,\n\t0xC8F4, 0x82E5, 0xC8F5, 0x5F31, 0xC8F6, 0x6492, 0xC8F7, 0x6D12,\n\t0xC8F8, 0x8428, 0xC8F9, 0x816E, 0xC8FA, 0x9CC3, 0xC8FB, 0x585E,\n\t0xC8FC, 0x8D5B, 0xC8FD, 0x4E09, 0xC8FE, 0x53C1, 0xC940, 0x847D,\n\t0xC941, 0x847E, 0xC942, 0x847F, 0xC943, 0x8480, 0xC944, 0x8481,\n\t0xC945, 0x8483, 0xC946, 0x8484, 0xC947, 0x8485, 0xC948, 0x8486,\n\t0xC949, 0x848A, 0xC94A, 0x848D, 0xC94B, 0x848F, 0xC94C, 0x8490,\n\t0xC94D, 0x8491, 0xC94E, 0x8492, 0xC94F, 0x8493, 0xC950, 0x8494,\n\t0xC951, 0x8495, 0xC952, 0x8496, 0xC953, 0x8498, 0xC954, 0x849A,\n\t0xC955, 0x849B, 0xC956, 0x849D, 0xC957, 0x849E, 0xC958, 0x849F,\n\t0xC959, 0x84A0, 0xC95A, 0x84A2, 0xC95B, 0x84A3, 0xC95C, 0x84A4,\n\t0xC95D, 0x84A5, 0xC95E, 0x84A6, 0xC95F, 0x84A7, 0xC960, 0x84A8,\n\t0xC961, 0x84A9, 0xC962, 0x84AA, 0xC963, 0x84AB, 0xC964, 0x84AC,\n\t0xC965, 0x84AD, 0xC966, 0x84AE, 0xC967, 0x84B0, 0xC968, 0x84B1,\n\t0xC969, 0x84B3, 0xC96A, 0x84B5, 0xC96B, 0x84B6, 0xC96C, 0x84B7,\n\t0xC96D, 0x84BB, 0xC96E, 0x84BC, 0xC96F, 0x84BE, 0xC970, 0x84C0,\n\t0xC971, 0x84C2, 0xC972, 0x84C3, 0xC973, 0x84C5, 0xC974, 0x84C6,\n\t0xC975, 0x84C7, 0xC976, 0x84C8, 0xC977, 0x84CB, 0xC978, 0x84CC,\n\t0xC979, 0x84CE, 0xC97A, 0x84CF, 0xC97B, 0x84D2, 0xC97C, 0x84D4,\n\t0xC97D, 0x84D5, 0xC97E, 0x84D7, 0xC980, 0x84D8, 0xC981, 0x84D9,\n\t0xC982, 0x84DA, 0xC983, 0x84DB, 0xC984, 0x84DC, 0xC985, 0x84DE,\n\t0xC986, 0x84E1, 0xC987, 0x84E2, 0xC988, 0x84E4, 0xC989, 0x84E7,\n\t0xC98A, 0x84E8, 0xC98B, 0x84E9, 0xC98C, 0x84EA, 0xC98D, 0x84EB,\n\t0xC98E, 0x84ED, 0xC98F, 0x84EE, 0xC990, 0x84EF, 0xC991, 0x84F1,\n\t0xC992, 0x84F2, 0xC993, 0x84F3, 0xC994, 0x84F4, 0xC995, 0x84F5,\n\t0xC996, 0x84F6, 0xC997, 0x84F7, 0xC998, 0x84F8, 0xC999, 0x84F9,\n\t0xC99A, 0x84FA, 0xC99B, 0x84FB, 0xC99C, 0x84FD, 0xC99D, 0x84FE,\n\t0xC99E, 0x8500, 0xC99F, 0x8501, 0xC9A0, 0x8502, 0xC9A1, 0x4F1E,\n\t0xC9A2, 0x6563, 0xC9A3, 0x6851, 0xC9A4, 0x55D3, 0xC9A5, 0x4E27,\n\t0xC9A6, 0x6414, 0xC9A7, 0x9A9A, 0xC9A8, 0x626B, 0xC9A9, 0x5AC2,\n\t0xC9AA, 0x745F, 0xC9AB, 0x8272, 0xC9AC, 0x6DA9, 0xC9AD, 0x68EE,\n\t0xC9AE, 0x50E7, 0xC9AF, 0x838E, 0xC9B0, 0x7802, 0xC9B1, 0x6740,\n\t0xC9B2, 0x5239, 0xC9B3, 0x6C99, 0xC9B4, 0x7EB1, 0xC9B5, 0x50BB,\n\t0xC9B6, 0x5565, 0xC9B7, 0x715E, 0xC9B8, 0x7B5B, 0xC9B9, 0x6652,\n\t0xC9BA, 0x73CA, 0xC9BB, 0x82EB, 0xC9BC, 0x6749, 0xC9BD, 0x5C71,\n\t0xC9BE, 0x5220, 0xC9BF, 0x717D, 0xC9C0, 0x886B, 0xC9C1, 0x95EA,\n\t0xC9C2, 0x9655, 0xC9C3, 0x64C5, 0xC9C4, 0x8D61, 0xC9C5, 0x81B3,\n\t0xC9C6, 0x5584, 0xC9C7, 0x6C55, 0xC9C8, 0x6247, 0xC9C9, 0x7F2E,\n\t0xC9CA, 0x5892, 0xC9CB, 0x4F24, 0xC9CC, 0x5546, 0xC9CD, 0x8D4F,\n\t0xC9CE, 0x664C, 0xC9CF, 0x4E0A, 0xC9D0, 0x5C1A, 0xC9D1, 0x88F3,\n\t0xC9D2, 0x68A2, 0xC9D3, 0x634E, 0xC9D4, 0x7A0D, 0xC9D5, 0x70E7,\n\t0xC9D6, 0x828D, 0xC9D7, 0x52FA, 0xC9D8, 0x97F6, 0xC9D9, 0x5C11,\n\t0xC9DA, 0x54E8, 0xC9DB, 0x90B5, 0xC9DC, 0x7ECD, 0xC9DD, 0x5962,\n\t0xC9DE, 0x8D4A, 0xC9DF, 0x86C7, 0xC9E0, 0x820C, 0xC9E1, 0x820D,\n\t0xC9E2, 0x8D66, 0xC9E3, 0x6444, 0xC9E4, 0x5C04, 0xC9E5, 0x6151,\n\t0xC9E6, 0x6D89, 0xC9E7, 0x793E, 0xC9E8, 0x8BBE, 0xC9E9, 0x7837,\n\t0xC9EA, 0x7533, 0xC9EB, 0x547B, 0xC9EC, 0x4F38, 0xC9ED, 0x8EAB,\n\t0xC9EE, 0x6DF1, 0xC9EF, 0x5A20, 0xC9F0, 0x7EC5, 0xC9F1, 0x795E,\n\t0xC9F2, 0x6C88, 0xC9F3, 0x5BA1, 0xC9F4, 0x5A76, 0xC9F5, 0x751A,\n\t0xC9F6, 0x80BE, 0xC9F7, 0x614E, 0xC9F8, 0x6E17, 0xC9F9, 0x58F0,\n\t0xC9FA, 0x751F, 0xC9FB, 0x7525, 0xC9FC, 0x7272, 0xC9FD, 0x5347,\n\t0xC9FE, 0x7EF3, 0xCA40, 0x8503, 0xCA41, 0x8504, 0xCA42, 0x8505,\n\t0xCA43, 0x8506, 0xCA44, 0x8507, 0xCA45, 0x8508, 0xCA46, 0x8509,\n\t0xCA47, 0x850A, 0xCA48, 0x850B, 0xCA49, 0x850D, 0xCA4A, 0x850E,\n\t0xCA4B, 0x850F, 0xCA4C, 0x8510, 0xCA4D, 0x8512, 0xCA4E, 0x8514,\n\t0xCA4F, 0x8515, 0xCA50, 0x8516, 0xCA51, 0x8518, 0xCA52, 0x8519,\n\t0xCA53, 0x851B, 0xCA54, 0x851C, 0xCA55, 0x851D, 0xCA56, 0x851E,\n\t0xCA57, 0x8520, 0xCA58, 0x8522, 0xCA59, 0x8523, 0xCA5A, 0x8524,\n\t0xCA5B, 0x8525, 0xCA5C, 0x8526, 0xCA5D, 0x8527, 0xCA5E, 0x8528,\n\t0xCA5F, 0x8529, 0xCA60, 0x852A, 0xCA61, 0x852D, 0xCA62, 0x852E,\n\t0xCA63, 0x852F, 0xCA64, 0x8530, 0xCA65, 0x8531, 0xCA66, 0x8532,\n\t0xCA67, 0x8533, 0xCA68, 0x8534, 0xCA69, 0x8535, 0xCA6A, 0x8536,\n\t0xCA6B, 0x853E, 0xCA6C, 0x853F, 0xCA6D, 0x8540, 0xCA6E, 0x8541,\n\t0xCA6F, 0x8542, 0xCA70, 0x8544, 0xCA71, 0x8545, 0xCA72, 0x8546,\n\t0xCA73, 0x8547, 0xCA74, 0x854B, 0xCA75, 0x854C, 0xCA76, 0x854D,\n\t0xCA77, 0x854E, 0xCA78, 0x854F, 0xCA79, 0x8550, 0xCA7A, 0x8551,\n\t0xCA7B, 0x8552, 0xCA7C, 0x8553, 0xCA7D, 0x8554, 0xCA7E, 0x8555,\n\t0xCA80, 0x8557, 0xCA81, 0x8558, 0xCA82, 0x855A, 0xCA83, 0x855B,\n\t0xCA84, 0x855C, 0xCA85, 0x855D, 0xCA86, 0x855F, 0xCA87, 0x8560,\n\t0xCA88, 0x8561, 0xCA89, 0x8562, 0xCA8A, 0x8563, 0xCA8B, 0x8565,\n\t0xCA8C, 0x8566, 0xCA8D, 0x8567, 0xCA8E, 0x8569, 0xCA8F, 0x856A,\n\t0xCA90, 0x856B, 0xCA91, 0x856C, 0xCA92, 0x856D, 0xCA93, 0x856E,\n\t0xCA94, 0x856F, 0xCA95, 0x8570, 0xCA96, 0x8571, 0xCA97, 0x8573,\n\t0xCA98, 0x8575, 0xCA99, 0x8576, 0xCA9A, 0x8577, 0xCA9B, 0x8578,\n\t0xCA9C, 0x857C, 0xCA9D, 0x857D, 0xCA9E, 0x857F, 0xCA9F, 0x8580,\n\t0xCAA0, 0x8581, 0xCAA1, 0x7701, 0xCAA2, 0x76DB, 0xCAA3, 0x5269,\n\t0xCAA4, 0x80DC, 0xCAA5, 0x5723, 0xCAA6, 0x5E08, 0xCAA7, 0x5931,\n\t0xCAA8, 0x72EE, 0xCAA9, 0x65BD, 0xCAAA, 0x6E7F, 0xCAAB, 0x8BD7,\n\t0xCAAC, 0x5C38, 0xCAAD, 0x8671, 0xCAAE, 0x5341, 0xCAAF, 0x77F3,\n\t0xCAB0, 0x62FE, 0xCAB1, 0x65F6, 0xCAB2, 0x4EC0, 0xCAB3, 0x98DF,\n\t0xCAB4, 0x8680, 0xCAB5, 0x5B9E, 0xCAB6, 0x8BC6, 0xCAB7, 0x53F2,\n\t0xCAB8, 0x77E2, 0xCAB9, 0x4F7F, 0xCABA, 0x5C4E, 0xCABB, 0x9A76,\n\t0xCABC, 0x59CB, 0xCABD, 0x5F0F, 0xCABE, 0x793A, 0xCABF, 0x58EB,\n\t0xCAC0, 0x4E16, 0xCAC1, 0x67FF, 0xCAC2, 0x4E8B, 0xCAC3, 0x62ED,\n\t0xCAC4, 0x8A93, 0xCAC5, 0x901D, 0xCAC6, 0x52BF, 0xCAC7, 0x662F,\n\t0xCAC8, 0x55DC, 0xCAC9, 0x566C, 0xCACA, 0x9002, 0xCACB, 0x4ED5,\n\t0xCACC, 0x4F8D, 0xCACD, 0x91CA, 0xCACE, 0x9970, 0xCACF, 0x6C0F,\n\t0xCAD0, 0x5E02, 0xCAD1, 0x6043, 0xCAD2, 0x5BA4, 0xCAD3, 0x89C6,\n\t0xCAD4, 0x8BD5, 0xCAD5, 0x6536, 0xCAD6, 0x624B, 0xCAD7, 0x9996,\n\t0xCAD8, 0x5B88, 0xCAD9, 0x5BFF, 0xCADA, 0x6388, 0xCADB, 0x552E,\n\t0xCADC, 0x53D7, 0xCADD, 0x7626, 0xCADE, 0x517D, 0xCADF, 0x852C,\n\t0xCAE0, 0x67A2, 0xCAE1, 0x68B3, 0xCAE2, 0x6B8A, 0xCAE3, 0x6292,\n\t0xCAE4, 0x8F93, 0xCAE5, 0x53D4, 0xCAE6, 0x8212, 0xCAE7, 0x6DD1,\n\t0xCAE8, 0x758F, 0xCAE9, 0x4E66, 0xCAEA, 0x8D4E, 0xCAEB, 0x5B70,\n\t0xCAEC, 0x719F, 0xCAED, 0x85AF, 0xCAEE, 0x6691, 0xCAEF, 0x66D9,\n\t0xCAF0, 0x7F72, 0xCAF1, 0x8700, 0xCAF2, 0x9ECD, 0xCAF3, 0x9F20,\n\t0xCAF4, 0x5C5E, 0xCAF5, 0x672F, 0xCAF6, 0x8FF0, 0xCAF7, 0x6811,\n\t0xCAF8, 0x675F, 0xCAF9, 0x620D, 0xCAFA, 0x7AD6, 0xCAFB, 0x5885,\n\t0xCAFC, 0x5EB6, 0xCAFD, 0x6570, 0xCAFE, 0x6F31, 0xCB40, 0x8582,\n\t0xCB41, 0x8583, 0xCB42, 0x8586, 0xCB43, 0x8588, 0xCB44, 0x8589,\n\t0xCB45, 0x858A, 0xCB46, 0x858B, 0xCB47, 0x858C, 0xCB48, 0x858D,\n\t0xCB49, 0x858E, 0xCB4A, 0x8590, 0xCB4B, 0x8591, 0xCB4C, 0x8592,\n\t0xCB4D, 0x8593, 0xCB4E, 0x8594, 0xCB4F, 0x8595, 0xCB50, 0x8596,\n\t0xCB51, 0x8597, 0xCB52, 0x8598, 0xCB53, 0x8599, 0xCB54, 0x859A,\n\t0xCB55, 0x859D, 0xCB56, 0x859E, 0xCB57, 0x859F, 0xCB58, 0x85A0,\n\t0xCB59, 0x85A1, 0xCB5A, 0x85A2, 0xCB5B, 0x85A3, 0xCB5C, 0x85A5,\n\t0xCB5D, 0x85A6, 0xCB5E, 0x85A7, 0xCB5F, 0x85A9, 0xCB60, 0x85AB,\n\t0xCB61, 0x85AC, 0xCB62, 0x85AD, 0xCB63, 0x85B1, 0xCB64, 0x85B2,\n\t0xCB65, 0x85B3, 0xCB66, 0x85B4, 0xCB67, 0x85B5, 0xCB68, 0x85B6,\n\t0xCB69, 0x85B8, 0xCB6A, 0x85BA, 0xCB6B, 0x85BB, 0xCB6C, 0x85BC,\n\t0xCB6D, 0x85BD, 0xCB6E, 0x85BE, 0xCB6F, 0x85BF, 0xCB70, 0x85C0,\n\t0xCB71, 0x85C2, 0xCB72, 0x85C3, 0xCB73, 0x85C4, 0xCB74, 0x85C5,\n\t0xCB75, 0x85C6, 0xCB76, 0x85C7, 0xCB77, 0x85C8, 0xCB78, 0x85CA,\n\t0xCB79, 0x85CB, 0xCB7A, 0x85CC, 0xCB7B, 0x85CD, 0xCB7C, 0x85CE,\n\t0xCB7D, 0x85D1, 0xCB7E, 0x85D2, 0xCB80, 0x85D4, 0xCB81, 0x85D6,\n\t0xCB82, 0x85D7, 0xCB83, 0x85D8, 0xCB84, 0x85D9, 0xCB85, 0x85DA,\n\t0xCB86, 0x85DB, 0xCB87, 0x85DD, 0xCB88, 0x85DE, 0xCB89, 0x85DF,\n\t0xCB8A, 0x85E0, 0xCB8B, 0x85E1, 0xCB8C, 0x85E2, 0xCB8D, 0x85E3,\n\t0xCB8E, 0x85E5, 0xCB8F, 0x85E6, 0xCB90, 0x85E7, 0xCB91, 0x85E8,\n\t0xCB92, 0x85EA, 0xCB93, 0x85EB, 0xCB94, 0x85EC, 0xCB95, 0x85ED,\n\t0xCB96, 0x85EE, 0xCB97, 0x85EF, 0xCB98, 0x85F0, 0xCB99, 0x85F1,\n\t0xCB9A, 0x85F2, 0xCB9B, 0x85F3, 0xCB9C, 0x85F4, 0xCB9D, 0x85F5,\n\t0xCB9E, 0x85F6, 0xCB9F, 0x85F7, 0xCBA0, 0x85F8, 0xCBA1, 0x6055,\n\t0xCBA2, 0x5237, 0xCBA3, 0x800D, 0xCBA4, 0x6454, 0xCBA5, 0x8870,\n\t0xCBA6, 0x7529, 0xCBA7, 0x5E05, 0xCBA8, 0x6813, 0xCBA9, 0x62F4,\n\t0xCBAA, 0x971C, 0xCBAB, 0x53CC, 0xCBAC, 0x723D, 0xCBAD, 0x8C01,\n\t0xCBAE, 0x6C34, 0xCBAF, 0x7761, 0xCBB0, 0x7A0E, 0xCBB1, 0x542E,\n\t0xCBB2, 0x77AC, 0xCBB3, 0x987A, 0xCBB4, 0x821C, 0xCBB5, 0x8BF4,\n\t0xCBB6, 0x7855, 0xCBB7, 0x6714, 0xCBB8, 0x70C1, 0xCBB9, 0x65AF,\n\t0xCBBA, 0x6495, 0xCBBB, 0x5636, 0xCBBC, 0x601D, 0xCBBD, 0x79C1,\n\t0xCBBE, 0x53F8, 0xCBBF, 0x4E1D, 0xCBC0, 0x6B7B, 0xCBC1, 0x8086,\n\t0xCBC2, 0x5BFA, 0xCBC3, 0x55E3, 0xCBC4, 0x56DB, 0xCBC5, 0x4F3A,\n\t0xCBC6, 0x4F3C, 0xCBC7, 0x9972, 0xCBC8, 0x5DF3, 0xCBC9, 0x677E,\n\t0xCBCA, 0x8038, 0xCBCB, 0x6002, 0xCBCC, 0x9882, 0xCBCD, 0x9001,\n\t0xCBCE, 0x5B8B, 0xCBCF, 0x8BBC, 0xCBD0, 0x8BF5, 0xCBD1, 0x641C,\n\t0xCBD2, 0x8258, 0xCBD3, 0x64DE, 0xCBD4, 0x55FD, 0xCBD5, 0x82CF,\n\t0xCBD6, 0x9165, 0xCBD7, 0x4FD7, 0xCBD8, 0x7D20, 0xCBD9, 0x901F,\n\t0xCBDA, 0x7C9F, 0xCBDB, 0x50F3, 0xCBDC, 0x5851, 0xCBDD, 0x6EAF,\n\t0xCBDE, 0x5BBF, 0xCBDF, 0x8BC9, 0xCBE0, 0x8083, 0xCBE1, 0x9178,\n\t0xCBE2, 0x849C, 0xCBE3, 0x7B97, 0xCBE4, 0x867D, 0xCBE5, 0x968B,\n\t0xCBE6, 0x968F, 0xCBE7, 0x7EE5, 0xCBE8, 0x9AD3, 0xCBE9, 0x788E,\n\t0xCBEA, 0x5C81, 0xCBEB, 0x7A57, 0xCBEC, 0x9042, 0xCBED, 0x96A7,\n\t0xCBEE, 0x795F, 0xCBEF, 0x5B59, 0xCBF0, 0x635F, 0xCBF1, 0x7B0B,\n\t0xCBF2, 0x84D1, 0xCBF3, 0x68AD, 0xCBF4, 0x5506, 0xCBF5, 0x7F29,\n\t0xCBF6, 0x7410, 0xCBF7, 0x7D22, 0xCBF8, 0x9501, 0xCBF9, 0x6240,\n\t0xCBFA, 0x584C, 0xCBFB, 0x4ED6, 0xCBFC, 0x5B83, 0xCBFD, 0x5979,\n\t0xCBFE, 0x5854, 0xCC40, 0x85F9, 0xCC41, 0x85FA, 0xCC42, 0x85FC,\n\t0xCC43, 0x85FD, 0xCC44, 0x85FE, 0xCC45, 0x8600, 0xCC46, 0x8601,\n\t0xCC47, 0x8602, 0xCC48, 0x8603, 0xCC49, 0x8604, 0xCC4A, 0x8606,\n\t0xCC4B, 0x8607, 0xCC4C, 0x8608, 0xCC4D, 0x8609, 0xCC4E, 0x860A,\n\t0xCC4F, 0x860B, 0xCC50, 0x860C, 0xCC51, 0x860D, 0xCC52, 0x860E,\n\t0xCC53, 0x860F, 0xCC54, 0x8610, 0xCC55, 0x8612, 0xCC56, 0x8613,\n\t0xCC57, 0x8614, 0xCC58, 0x8615, 0xCC59, 0x8617, 0xCC5A, 0x8618,\n\t0xCC5B, 0x8619, 0xCC5C, 0x861A, 0xCC5D, 0x861B, 0xCC5E, 0x861C,\n\t0xCC5F, 0x861D, 0xCC60, 0x861E, 0xCC61, 0x861F, 0xCC62, 0x8620,\n\t0xCC63, 0x8621, 0xCC64, 0x8622, 0xCC65, 0x8623, 0xCC66, 0x8624,\n\t0xCC67, 0x8625, 0xCC68, 0x8626, 0xCC69, 0x8628, 0xCC6A, 0x862A,\n\t0xCC6B, 0x862B, 0xCC6C, 0x862C, 0xCC6D, 0x862D, 0xCC6E, 0x862E,\n\t0xCC6F, 0x862F, 0xCC70, 0x8630, 0xCC71, 0x8631, 0xCC72, 0x8632,\n\t0xCC73, 0x8633, 0xCC74, 0x8634, 0xCC75, 0x8635, 0xCC76, 0x8636,\n\t0xCC77, 0x8637, 0xCC78, 0x8639, 0xCC79, 0x863A, 0xCC7A, 0x863B,\n\t0xCC7B, 0x863D, 0xCC7C, 0x863E, 0xCC7D, 0x863F, 0xCC7E, 0x8640,\n\t0xCC80, 0x8641, 0xCC81, 0x8642, 0xCC82, 0x8643, 0xCC83, 0x8644,\n\t0xCC84, 0x8645, 0xCC85, 0x8646, 0xCC86, 0x8647, 0xCC87, 0x8648,\n\t0xCC88, 0x8649, 0xCC89, 0x864A, 0xCC8A, 0x864B, 0xCC8B, 0x864C,\n\t0xCC8C, 0x8652, 0xCC8D, 0x8653, 0xCC8E, 0x8655, 0xCC8F, 0x8656,\n\t0xCC90, 0x8657, 0xCC91, 0x8658, 0xCC92, 0x8659, 0xCC93, 0x865B,\n\t0xCC94, 0x865C, 0xCC95, 0x865D, 0xCC96, 0x865F, 0xCC97, 0x8660,\n\t0xCC98, 0x8661, 0xCC99, 0x8663, 0xCC9A, 0x8664, 0xCC9B, 0x8665,\n\t0xCC9C, 0x8666, 0xCC9D, 0x8667, 0xCC9E, 0x8668, 0xCC9F, 0x8669,\n\t0xCCA0, 0x866A, 0xCCA1, 0x736D, 0xCCA2, 0x631E, 0xCCA3, 0x8E4B,\n\t0xCCA4, 0x8E0F, 0xCCA5, 0x80CE, 0xCCA6, 0x82D4, 0xCCA7, 0x62AC,\n\t0xCCA8, 0x53F0, 0xCCA9, 0x6CF0, 0xCCAA, 0x915E, 0xCCAB, 0x592A,\n\t0xCCAC, 0x6001, 0xCCAD, 0x6C70, 0xCCAE, 0x574D, 0xCCAF, 0x644A,\n\t0xCCB0, 0x8D2A, 0xCCB1, 0x762B, 0xCCB2, 0x6EE9, 0xCCB3, 0x575B,\n\t0xCCB4, 0x6A80, 0xCCB5, 0x75F0, 0xCCB6, 0x6F6D, 0xCCB7, 0x8C2D,\n\t0xCCB8, 0x8C08, 0xCCB9, 0x5766, 0xCCBA, 0x6BEF, 0xCCBB, 0x8892,\n\t0xCCBC, 0x78B3, 0xCCBD, 0x63A2, 0xCCBE, 0x53F9, 0xCCBF, 0x70AD,\n\t0xCCC0, 0x6C64, 0xCCC1, 0x5858, 0xCCC2, 0x642A, 0xCCC3, 0x5802,\n\t0xCCC4, 0x68E0, 0xCCC5, 0x819B, 0xCCC6, 0x5510, 0xCCC7, 0x7CD6,\n\t0xCCC8, 0x5018, 0xCCC9, 0x8EBA, 0xCCCA, 0x6DCC, 0xCCCB, 0x8D9F,\n\t0xCCCC, 0x70EB, 0xCCCD, 0x638F, 0xCCCE, 0x6D9B, 0xCCCF, 0x6ED4,\n\t0xCCD0, 0x7EE6, 0xCCD1, 0x8404, 0xCCD2, 0x6843, 0xCCD3, 0x9003,\n\t0xCCD4, 0x6DD8, 0xCCD5, 0x9676, 0xCCD6, 0x8BA8, 0xCCD7, 0x5957,\n\t0xCCD8, 0x7279, 0xCCD9, 0x85E4, 0xCCDA, 0x817E, 0xCCDB, 0x75BC,\n\t0xCCDC, 0x8A8A, 0xCCDD, 0x68AF, 0xCCDE, 0x5254, 0xCCDF, 0x8E22,\n\t0xCCE0, 0x9511, 0xCCE1, 0x63D0, 0xCCE2, 0x9898, 0xCCE3, 0x8E44,\n\t0xCCE4, 0x557C, 0xCCE5, 0x4F53, 0xCCE6, 0x66FF, 0xCCE7, 0x568F,\n\t0xCCE8, 0x60D5, 0xCCE9, 0x6D95, 0xCCEA, 0x5243, 0xCCEB, 0x5C49,\n\t0xCCEC, 0x5929, 0xCCED, 0x6DFB, 0xCCEE, 0x586B, 0xCCEF, 0x7530,\n\t0xCCF0, 0x751C, 0xCCF1, 0x606C, 0xCCF2, 0x8214, 0xCCF3, 0x8146,\n\t0xCCF4, 0x6311, 0xCCF5, 0x6761, 0xCCF6, 0x8FE2, 0xCCF7, 0x773A,\n\t0xCCF8, 0x8DF3, 0xCCF9, 0x8D34, 0xCCFA, 0x94C1, 0xCCFB, 0x5E16,\n\t0xCCFC, 0x5385, 0xCCFD, 0x542C, 0xCCFE, 0x70C3, 0xCD40, 0x866D,\n\t0xCD41, 0x866F, 0xCD42, 0x8670, 0xCD43, 0x8672, 0xCD44, 0x8673,\n\t0xCD45, 0x8674, 0xCD46, 0x8675, 0xCD47, 0x8676, 0xCD48, 0x8677,\n\t0xCD49, 0x8678, 0xCD4A, 0x8683, 0xCD4B, 0x8684, 0xCD4C, 0x8685,\n\t0xCD4D, 0x8686, 0xCD4E, 0x8687, 0xCD4F, 0x8688, 0xCD50, 0x8689,\n\t0xCD51, 0x868E, 0xCD52, 0x868F, 0xCD53, 0x8690, 0xCD54, 0x8691,\n\t0xCD55, 0x8692, 0xCD56, 0x8694, 0xCD57, 0x8696, 0xCD58, 0x8697,\n\t0xCD59, 0x8698, 0xCD5A, 0x8699, 0xCD5B, 0x869A, 0xCD5C, 0x869B,\n\t0xCD5D, 0x869E, 0xCD5E, 0x869F, 0xCD5F, 0x86A0, 0xCD60, 0x86A1,\n\t0xCD61, 0x86A2, 0xCD62, 0x86A5, 0xCD63, 0x86A6, 0xCD64, 0x86AB,\n\t0xCD65, 0x86AD, 0xCD66, 0x86AE, 0xCD67, 0x86B2, 0xCD68, 0x86B3,\n\t0xCD69, 0x86B7, 0xCD6A, 0x86B8, 0xCD6B, 0x86B9, 0xCD6C, 0x86BB,\n\t0xCD6D, 0x86BC, 0xCD6E, 0x86BD, 0xCD6F, 0x86BE, 0xCD70, 0x86BF,\n\t0xCD71, 0x86C1, 0xCD72, 0x86C2, 0xCD73, 0x86C3, 0xCD74, 0x86C5,\n\t0xCD75, 0x86C8, 0xCD76, 0x86CC, 0xCD77, 0x86CD, 0xCD78, 0x86D2,\n\t0xCD79, 0x86D3, 0xCD7A, 0x86D5, 0xCD7B, 0x86D6, 0xCD7C, 0x86D7,\n\t0xCD7D, 0x86DA, 0xCD7E, 0x86DC, 0xCD80, 0x86DD, 0xCD81, 0x86E0,\n\t0xCD82, 0x86E1, 0xCD83, 0x86E2, 0xCD84, 0x86E3, 0xCD85, 0x86E5,\n\t0xCD86, 0x86E6, 0xCD87, 0x86E7, 0xCD88, 0x86E8, 0xCD89, 0x86EA,\n\t0xCD8A, 0x86EB, 0xCD8B, 0x86EC, 0xCD8C, 0x86EF, 0xCD8D, 0x86F5,\n\t0xCD8E, 0x86F6, 0xCD8F, 0x86F7, 0xCD90, 0x86FA, 0xCD91, 0x86FB,\n\t0xCD92, 0x86FC, 0xCD93, 0x86FD, 0xCD94, 0x86FF, 0xCD95, 0x8701,\n\t0xCD96, 0x8704, 0xCD97, 0x8705, 0xCD98, 0x8706, 0xCD99, 0x870B,\n\t0xCD9A, 0x870C, 0xCD9B, 0x870E, 0xCD9C, 0x870F, 0xCD9D, 0x8710,\n\t0xCD9E, 0x8711, 0xCD9F, 0x8714, 0xCDA0, 0x8716, 0xCDA1, 0x6C40,\n\t0xCDA2, 0x5EF7, 0xCDA3, 0x505C, 0xCDA4, 0x4EAD, 0xCDA5, 0x5EAD,\n\t0xCDA6, 0x633A, 0xCDA7, 0x8247, 0xCDA8, 0x901A, 0xCDA9, 0x6850,\n\t0xCDAA, 0x916E, 0xCDAB, 0x77B3, 0xCDAC, 0x540C, 0xCDAD, 0x94DC,\n\t0xCDAE, 0x5F64, 0xCDAF, 0x7AE5, 0xCDB0, 0x6876, 0xCDB1, 0x6345,\n\t0xCDB2, 0x7B52, 0xCDB3, 0x7EDF, 0xCDB4, 0x75DB, 0xCDB5, 0x5077,\n\t0xCDB6, 0x6295, 0xCDB7, 0x5934, 0xCDB8, 0x900F, 0xCDB9, 0x51F8,\n\t0xCDBA, 0x79C3, 0xCDBB, 0x7A81, 0xCDBC, 0x56FE, 0xCDBD, 0x5F92,\n\t0xCDBE, 0x9014, 0xCDBF, 0x6D82, 0xCDC0, 0x5C60, 0xCDC1, 0x571F,\n\t0xCDC2, 0x5410, 0xCDC3, 0x5154, 0xCDC4, 0x6E4D, 0xCDC5, 0x56E2,\n\t0xCDC6, 0x63A8, 0xCDC7, 0x9893, 0xCDC8, 0x817F, 0xCDC9, 0x8715,\n\t0xCDCA, 0x892A, 0xCDCB, 0x9000, 0xCDCC, 0x541E, 0xCDCD, 0x5C6F,\n\t0xCDCE, 0x81C0, 0xCDCF, 0x62D6, 0xCDD0, 0x6258, 0xCDD1, 0x8131,\n\t0xCDD2, 0x9E35, 0xCDD3, 0x9640, 0xCDD4, 0x9A6E, 0xCDD5, 0x9A7C,\n\t0xCDD6, 0x692D, 0xCDD7, 0x59A5, 0xCDD8, 0x62D3, 0xCDD9, 0x553E,\n\t0xCDDA, 0x6316, 0xCDDB, 0x54C7, 0xCDDC, 0x86D9, 0xCDDD, 0x6D3C,\n\t0xCDDE, 0x5A03, 0xCDDF, 0x74E6, 0xCDE0, 0x889C, 0xCDE1, 0x6B6A,\n\t0xCDE2, 0x5916, 0xCDE3, 0x8C4C, 0xCDE4, 0x5F2F, 0xCDE5, 0x6E7E,\n\t0xCDE6, 0x73A9, 0xCDE7, 0x987D, 0xCDE8, 0x4E38, 0xCDE9, 0x70F7,\n\t0xCDEA, 0x5B8C, 0xCDEB, 0x7897, 0xCDEC, 0x633D, 0xCDED, 0x665A,\n\t0xCDEE, 0x7696, 0xCDEF, 0x60CB, 0xCDF0, 0x5B9B, 0xCDF1, 0x5A49,\n\t0xCDF2, 0x4E07, 0xCDF3, 0x8155, 0xCDF4, 0x6C6A, 0xCDF5, 0x738B,\n\t0xCDF6, 0x4EA1, 0xCDF7, 0x6789, 0xCDF8, 0x7F51, 0xCDF9, 0x5F80,\n\t0xCDFA, 0x65FA, 0xCDFB, 0x671B, 0xCDFC, 0x5FD8, 0xCDFD, 0x5984,\n\t0xCDFE, 0x5A01, 0xCE40, 0x8719, 0xCE41, 0x871B, 0xCE42, 0x871D,\n\t0xCE43, 0x871F, 0xCE44, 0x8720, 0xCE45, 0x8724, 0xCE46, 0x8726,\n\t0xCE47, 0x8727, 0xCE48, 0x8728, 0xCE49, 0x872A, 0xCE4A, 0x872B,\n\t0xCE4B, 0x872C, 0xCE4C, 0x872D, 0xCE4D, 0x872F, 0xCE4E, 0x8730,\n\t0xCE4F, 0x8732, 0xCE50, 0x8733, 0xCE51, 0x8735, 0xCE52, 0x8736,\n\t0xCE53, 0x8738, 0xCE54, 0x8739, 0xCE55, 0x873A, 0xCE56, 0x873C,\n\t0xCE57, 0x873D, 0xCE58, 0x8740, 0xCE59, 0x8741, 0xCE5A, 0x8742,\n\t0xCE5B, 0x8743, 0xCE5C, 0x8744, 0xCE5D, 0x8745, 0xCE5E, 0x8746,\n\t0xCE5F, 0x874A, 0xCE60, 0x874B, 0xCE61, 0x874D, 0xCE62, 0x874F,\n\t0xCE63, 0x8750, 0xCE64, 0x8751, 0xCE65, 0x8752, 0xCE66, 0x8754,\n\t0xCE67, 0x8755, 0xCE68, 0x8756, 0xCE69, 0x8758, 0xCE6A, 0x875A,\n\t0xCE6B, 0x875B, 0xCE6C, 0x875C, 0xCE6D, 0x875D, 0xCE6E, 0x875E,\n\t0xCE6F, 0x875F, 0xCE70, 0x8761, 0xCE71, 0x8762, 0xCE72, 0x8766,\n\t0xCE73, 0x8767, 0xCE74, 0x8768, 0xCE75, 0x8769, 0xCE76, 0x876A,\n\t0xCE77, 0x876B, 0xCE78, 0x876C, 0xCE79, 0x876D, 0xCE7A, 0x876F,\n\t0xCE7B, 0x8771, 0xCE7C, 0x8772, 0xCE7D, 0x8773, 0xCE7E, 0x8775,\n\t0xCE80, 0x8777, 0xCE81, 0x8778, 0xCE82, 0x8779, 0xCE83, 0x877A,\n\t0xCE84, 0x877F, 0xCE85, 0x8780, 0xCE86, 0x8781, 0xCE87, 0x8784,\n\t0xCE88, 0x8786, 0xCE89, 0x8787, 0xCE8A, 0x8789, 0xCE8B, 0x878A,\n\t0xCE8C, 0x878C, 0xCE8D, 0x878E, 0xCE8E, 0x878F, 0xCE8F, 0x8790,\n\t0xCE90, 0x8791, 0xCE91, 0x8792, 0xCE92, 0x8794, 0xCE93, 0x8795,\n\t0xCE94, 0x8796, 0xCE95, 0x8798, 0xCE96, 0x8799, 0xCE97, 0x879A,\n\t0xCE98, 0x879B, 0xCE99, 0x879C, 0xCE9A, 0x879D, 0xCE9B, 0x879E,\n\t0xCE9C, 0x87A0, 0xCE9D, 0x87A1, 0xCE9E, 0x87A2, 0xCE9F, 0x87A3,\n\t0xCEA0, 0x87A4, 0xCEA1, 0x5DCD, 0xCEA2, 0x5FAE, 0xCEA3, 0x5371,\n\t0xCEA4, 0x97E6, 0xCEA5, 0x8FDD, 0xCEA6, 0x6845, 0xCEA7, 0x56F4,\n\t0xCEA8, 0x552F, 0xCEA9, 0x60DF, 0xCEAA, 0x4E3A, 0xCEAB, 0x6F4D,\n\t0xCEAC, 0x7EF4, 0xCEAD, 0x82C7, 0xCEAE, 0x840E, 0xCEAF, 0x59D4,\n\t0xCEB0, 0x4F1F, 0xCEB1, 0x4F2A, 0xCEB2, 0x5C3E, 0xCEB3, 0x7EAC,\n\t0xCEB4, 0x672A, 0xCEB5, 0x851A, 0xCEB6, 0x5473, 0xCEB7, 0x754F,\n\t0xCEB8, 0x80C3, 0xCEB9, 0x5582, 0xCEBA, 0x9B4F, 0xCEBB, 0x4F4D,\n\t0xCEBC, 0x6E2D, 0xCEBD, 0x8C13, 0xCEBE, 0x5C09, 0xCEBF, 0x6170,\n\t0xCEC0, 0x536B, 0xCEC1, 0x761F, 0xCEC2, 0x6E29, 0xCEC3, 0x868A,\n\t0xCEC4, 0x6587, 0xCEC5, 0x95FB, 0xCEC6, 0x7EB9, 0xCEC7, 0x543B,\n\t0xCEC8, 0x7A33, 0xCEC9, 0x7D0A, 0xCECA, 0x95EE, 0xCECB, 0x55E1,\n\t0xCECC, 0x7FC1, 0xCECD, 0x74EE, 0xCECE, 0x631D, 0xCECF, 0x8717,\n\t0xCED0, 0x6DA1, 0xCED1, 0x7A9D, 0xCED2, 0x6211, 0xCED3, 0x65A1,\n\t0xCED4, 0x5367, 0xCED5, 0x63E1, 0xCED6, 0x6C83, 0xCED7, 0x5DEB,\n\t0xCED8, 0x545C, 0xCED9, 0x94A8, 0xCEDA, 0x4E4C, 0xCEDB, 0x6C61,\n\t0xCEDC, 0x8BEC, 0xCEDD, 0x5C4B, 0xCEDE, 0x65E0, 0xCEDF, 0x829C,\n\t0xCEE0, 0x68A7, 0xCEE1, 0x543E, 0xCEE2, 0x5434, 0xCEE3, 0x6BCB,\n\t0xCEE4, 0x6B66, 0xCEE5, 0x4E94, 0xCEE6, 0x6342, 0xCEE7, 0x5348,\n\t0xCEE8, 0x821E, 0xCEE9, 0x4F0D, 0xCEEA, 0x4FAE, 0xCEEB, 0x575E,\n\t0xCEEC, 0x620A, 0xCEED, 0x96FE, 0xCEEE, 0x6664, 0xCEEF, 0x7269,\n\t0xCEF0, 0x52FF, 0xCEF1, 0x52A1, 0xCEF2, 0x609F, 0xCEF3, 0x8BEF,\n\t0xCEF4, 0x6614, 0xCEF5, 0x7199, 0xCEF6, 0x6790, 0xCEF7, 0x897F,\n\t0xCEF8, 0x7852, 0xCEF9, 0x77FD, 0xCEFA, 0x6670, 0xCEFB, 0x563B,\n\t0xCEFC, 0x5438, 0xCEFD, 0x9521, 0xCEFE, 0x727A, 0xCF40, 0x87A5,\n\t0xCF41, 0x87A6, 0xCF42, 0x87A7, 0xCF43, 0x87A9, 0xCF44, 0x87AA,\n\t0xCF45, 0x87AE, 0xCF46, 0x87B0, 0xCF47, 0x87B1, 0xCF48, 0x87B2,\n\t0xCF49, 0x87B4, 0xCF4A, 0x87B6, 0xCF4B, 0x87B7, 0xCF4C, 0x87B8,\n\t0xCF4D, 0x87B9, 0xCF4E, 0x87BB, 0xCF4F, 0x87BC, 0xCF50, 0x87BE,\n\t0xCF51, 0x87BF, 0xCF52, 0x87C1, 0xCF53, 0x87C2, 0xCF54, 0x87C3,\n\t0xCF55, 0x87C4, 0xCF56, 0x87C5, 0xCF57, 0x87C7, 0xCF58, 0x87C8,\n\t0xCF59, 0x87C9, 0xCF5A, 0x87CC, 0xCF5B, 0x87CD, 0xCF5C, 0x87CE,\n\t0xCF5D, 0x87CF, 0xCF5E, 0x87D0, 0xCF5F, 0x87D4, 0xCF60, 0x87D5,\n\t0xCF61, 0x87D6, 0xCF62, 0x87D7, 0xCF63, 0x87D8, 0xCF64, 0x87D9,\n\t0xCF65, 0x87DA, 0xCF66, 0x87DC, 0xCF67, 0x87DD, 0xCF68, 0x87DE,\n\t0xCF69, 0x87DF, 0xCF6A, 0x87E1, 0xCF6B, 0x87E2, 0xCF6C, 0x87E3,\n\t0xCF6D, 0x87E4, 0xCF6E, 0x87E6, 0xCF6F, 0x87E7, 0xCF70, 0x87E8,\n\t0xCF71, 0x87E9, 0xCF72, 0x87EB, 0xCF73, 0x87EC, 0xCF74, 0x87ED,\n\t0xCF75, 0x87EF, 0xCF76, 0x87F0, 0xCF77, 0x87F1, 0xCF78, 0x87F2,\n\t0xCF79, 0x87F3, 0xCF7A, 0x87F4, 0xCF7B, 0x87F5, 0xCF7C, 0x87F6,\n\t0xCF7D, 0x87F7, 0xCF7E, 0x87F8, 0xCF80, 0x87FA, 0xCF81, 0x87FB,\n\t0xCF82, 0x87FC, 0xCF83, 0x87FD, 0xCF84, 0x87FF, 0xCF85, 0x8800,\n\t0xCF86, 0x8801, 0xCF87, 0x8802, 0xCF88, 0x8804, 0xCF89, 0x8805,\n\t0xCF8A, 0x8806, 0xCF8B, 0x8807, 0xCF8C, 0x8808, 0xCF8D, 0x8809,\n\t0xCF8E, 0x880B, 0xCF8F, 0x880C, 0xCF90, 0x880D, 0xCF91, 0x880E,\n\t0xCF92, 0x880F, 0xCF93, 0x8810, 0xCF94, 0x8811, 0xCF95, 0x8812,\n\t0xCF96, 0x8814, 0xCF97, 0x8817, 0xCF98, 0x8818, 0xCF99, 0x8819,\n\t0xCF9A, 0x881A, 0xCF9B, 0x881C, 0xCF9C, 0x881D, 0xCF9D, 0x881E,\n\t0xCF9E, 0x881F, 0xCF9F, 0x8820, 0xCFA0, 0x8823, 0xCFA1, 0x7A00,\n\t0xCFA2, 0x606F, 0xCFA3, 0x5E0C, 0xCFA4, 0x6089, 0xCFA5, 0x819D,\n\t0xCFA6, 0x5915, 0xCFA7, 0x60DC, 0xCFA8, 0x7184, 0xCFA9, 0x70EF,\n\t0xCFAA, 0x6EAA, 0xCFAB, 0x6C50, 0xCFAC, 0x7280, 0xCFAD, 0x6A84,\n\t0xCFAE, 0x88AD, 0xCFAF, 0x5E2D, 0xCFB0, 0x4E60, 0xCFB1, 0x5AB3,\n\t0xCFB2, 0x559C, 0xCFB3, 0x94E3, 0xCFB4, 0x6D17, 0xCFB5, 0x7CFB,\n\t0xCFB6, 0x9699, 0xCFB7, 0x620F, 0xCFB8, 0x7EC6, 0xCFB9, 0x778E,\n\t0xCFBA, 0x867E, 0xCFBB, 0x5323, 0xCFBC, 0x971E, 0xCFBD, 0x8F96,\n\t0xCFBE, 0x6687, 0xCFBF, 0x5CE1, 0xCFC0, 0x4FA0, 0xCFC1, 0x72ED,\n\t0xCFC2, 0x4E0B, 0xCFC3, 0x53A6, 0xCFC4, 0x590F, 0xCFC5, 0x5413,\n\t0xCFC6, 0x6380, 0xCFC7, 0x9528, 0xCFC8, 0x5148, 0xCFC9, 0x4ED9,\n\t0xCFCA, 0x9C9C, 0xCFCB, 0x7EA4, 0xCFCC, 0x54B8, 0xCFCD, 0x8D24,\n\t0xCFCE, 0x8854, 0xCFCF, 0x8237, 0xCFD0, 0x95F2, 0xCFD1, 0x6D8E,\n\t0xCFD2, 0x5F26, 0xCFD3, 0x5ACC, 0xCFD4, 0x663E, 0xCFD5, 0x9669,\n\t0xCFD6, 0x73B0, 0xCFD7, 0x732E, 0xCFD8, 0x53BF, 0xCFD9, 0x817A,\n\t0xCFDA, 0x9985, 0xCFDB, 0x7FA1, 0xCFDC, 0x5BAA, 0xCFDD, 0x9677,\n\t0xCFDE, 0x9650, 0xCFDF, 0x7EBF, 0xCFE0, 0x76F8, 0xCFE1, 0x53A2,\n\t0xCFE2, 0x9576, 0xCFE3, 0x9999, 0xCFE4, 0x7BB1, 0xCFE5, 0x8944,\n\t0xCFE6, 0x6E58, 0xCFE7, 0x4E61, 0xCFE8, 0x7FD4, 0xCFE9, 0x7965,\n\t0xCFEA, 0x8BE6, 0xCFEB, 0x60F3, 0xCFEC, 0x54CD, 0xCFED, 0x4EAB,\n\t0xCFEE, 0x9879, 0xCFEF, 0x5DF7, 0xCFF0, 0x6A61, 0xCFF1, 0x50CF,\n\t0xCFF2, 0x5411, 0xCFF3, 0x8C61, 0xCFF4, 0x8427, 0xCFF5, 0x785D,\n\t0xCFF6, 0x9704, 0xCFF7, 0x524A, 0xCFF8, 0x54EE, 0xCFF9, 0x56A3,\n\t0xCFFA, 0x9500, 0xCFFB, 0x6D88, 0xCFFC, 0x5BB5, 0xCFFD, 0x6DC6,\n\t0xCFFE, 0x6653, 0xD040, 0x8824, 0xD041, 0x8825, 0xD042, 0x8826,\n\t0xD043, 0x8827, 0xD044, 0x8828, 0xD045, 0x8829, 0xD046, 0x882A,\n\t0xD047, 0x882B, 0xD048, 0x882C, 0xD049, 0x882D, 0xD04A, 0x882E,\n\t0xD04B, 0x882F, 0xD04C, 0x8830, 0xD04D, 0x8831, 0xD04E, 0x8833,\n\t0xD04F, 0x8834, 0xD050, 0x8835, 0xD051, 0x8836, 0xD052, 0x8837,\n\t0xD053, 0x8838, 0xD054, 0x883A, 0xD055, 0x883B, 0xD056, 0x883D,\n\t0xD057, 0x883E, 0xD058, 0x883F, 0xD059, 0x8841, 0xD05A, 0x8842,\n\t0xD05B, 0x8843, 0xD05C, 0x8846, 0xD05D, 0x8847, 0xD05E, 0x8848,\n\t0xD05F, 0x8849, 0xD060, 0x884A, 0xD061, 0x884B, 0xD062, 0x884E,\n\t0xD063, 0x884F, 0xD064, 0x8850, 0xD065, 0x8851, 0xD066, 0x8852,\n\t0xD067, 0x8853, 0xD068, 0x8855, 0xD069, 0x8856, 0xD06A, 0x8858,\n\t0xD06B, 0x885A, 0xD06C, 0x885B, 0xD06D, 0x885C, 0xD06E, 0x885D,\n\t0xD06F, 0x885E, 0xD070, 0x885F, 0xD071, 0x8860, 0xD072, 0x8866,\n\t0xD073, 0x8867, 0xD074, 0x886A, 0xD075, 0x886D, 0xD076, 0x886F,\n\t0xD077, 0x8871, 0xD078, 0x8873, 0xD079, 0x8874, 0xD07A, 0x8875,\n\t0xD07B, 0x8876, 0xD07C, 0x8878, 0xD07D, 0x8879, 0xD07E, 0x887A,\n\t0xD080, 0x887B, 0xD081, 0x887C, 0xD082, 0x8880, 0xD083, 0x8883,\n\t0xD084, 0x8886, 0xD085, 0x8887, 0xD086, 0x8889, 0xD087, 0x888A,\n\t0xD088, 0x888C, 0xD089, 0x888E, 0xD08A, 0x888F, 0xD08B, 0x8890,\n\t0xD08C, 0x8891, 0xD08D, 0x8893, 0xD08E, 0x8894, 0xD08F, 0x8895,\n\t0xD090, 0x8897, 0xD091, 0x8898, 0xD092, 0x8899, 0xD093, 0x889A,\n\t0xD094, 0x889B, 0xD095, 0x889D, 0xD096, 0x889E, 0xD097, 0x889F,\n\t0xD098, 0x88A0, 0xD099, 0x88A1, 0xD09A, 0x88A3, 0xD09B, 0x88A5,\n\t0xD09C, 0x88A6, 0xD09D, 0x88A7, 0xD09E, 0x88A8, 0xD09F, 0x88A9,\n\t0xD0A0, 0x88AA, 0xD0A1, 0x5C0F, 0xD0A2, 0x5B5D, 0xD0A3, 0x6821,\n\t0xD0A4, 0x8096, 0xD0A5, 0x5578, 0xD0A6, 0x7B11, 0xD0A7, 0x6548,\n\t0xD0A8, 0x6954, 0xD0A9, 0x4E9B, 0xD0AA, 0x6B47, 0xD0AB, 0x874E,\n\t0xD0AC, 0x978B, 0xD0AD, 0x534F, 0xD0AE, 0x631F, 0xD0AF, 0x643A,\n\t0xD0B0, 0x90AA, 0xD0B1, 0x659C, 0xD0B2, 0x80C1, 0xD0B3, 0x8C10,\n\t0xD0B4, 0x5199, 0xD0B5, 0x68B0, 0xD0B6, 0x5378, 0xD0B7, 0x87F9,\n\t0xD0B8, 0x61C8, 0xD0B9, 0x6CC4, 0xD0BA, 0x6CFB, 0xD0BB, 0x8C22,\n\t0xD0BC, 0x5C51, 0xD0BD, 0x85AA, 0xD0BE, 0x82AF, 0xD0BF, 0x950C,\n\t0xD0C0, 0x6B23, 0xD0C1, 0x8F9B, 0xD0C2, 0x65B0, 0xD0C3, 0x5FFB,\n\t0xD0C4, 0x5FC3, 0xD0C5, 0x4FE1, 0xD0C6, 0x8845, 0xD0C7, 0x661F,\n\t0xD0C8, 0x8165, 0xD0C9, 0x7329, 0xD0CA, 0x60FA, 0xD0CB, 0x5174,\n\t0xD0CC, 0x5211, 0xD0CD, 0x578B, 0xD0CE, 0x5F62, 0xD0CF, 0x90A2,\n\t0xD0D0, 0x884C, 0xD0D1, 0x9192, 0xD0D2, 0x5E78, 0xD0D3, 0x674F,\n\t0xD0D4, 0x6027, 0xD0D5, 0x59D3, 0xD0D6, 0x5144, 0xD0D7, 0x51F6,\n\t0xD0D8, 0x80F8, 0xD0D9, 0x5308, 0xD0DA, 0x6C79, 0xD0DB, 0x96C4,\n\t0xD0DC, 0x718A, 0xD0DD, 0x4F11, 0xD0DE, 0x4FEE, 0xD0DF, 0x7F9E,\n\t0xD0E0, 0x673D, 0xD0E1, 0x55C5, 0xD0E2, 0x9508, 0xD0E3, 0x79C0,\n\t0xD0E4, 0x8896, 0xD0E5, 0x7EE3, 0xD0E6, 0x589F, 0xD0E7, 0x620C,\n\t0xD0E8, 0x9700, 0xD0E9, 0x865A, 0xD0EA, 0x5618, 0xD0EB, 0x987B,\n\t0xD0EC, 0x5F90, 0xD0ED, 0x8BB8, 0xD0EE, 0x84C4, 0xD0EF, 0x9157,\n\t0xD0F0, 0x53D9, 0xD0F1, 0x65ED, 0xD0F2, 0x5E8F, 0xD0F3, 0x755C,\n\t0xD0F4, 0x6064, 0xD0F5, 0x7D6E, 0xD0F6, 0x5A7F, 0xD0F7, 0x7EEA,\n\t0xD0F8, 0x7EED, 0xD0F9, 0x8F69, 0xD0FA, 0x55A7, 0xD0FB, 0x5BA3,\n\t0xD0FC, 0x60AC, 0xD0FD, 0x65CB, 0xD0FE, 0x7384, 0xD140, 0x88AC,\n\t0xD141, 0x88AE, 0xD142, 0x88AF, 0xD143, 0x88B0, 0xD144, 0x88B2,\n\t0xD145, 0x88B3, 0xD146, 0x88B4, 0xD147, 0x88B5, 0xD148, 0x88B6,\n\t0xD149, 0x88B8, 0xD14A, 0x88B9, 0xD14B, 0x88BA, 0xD14C, 0x88BB,\n\t0xD14D, 0x88BD, 0xD14E, 0x88BE, 0xD14F, 0x88BF, 0xD150, 0x88C0,\n\t0xD151, 0x88C3, 0xD152, 0x88C4, 0xD153, 0x88C7, 0xD154, 0x88C8,\n\t0xD155, 0x88CA, 0xD156, 0x88CB, 0xD157, 0x88CC, 0xD158, 0x88CD,\n\t0xD159, 0x88CF, 0xD15A, 0x88D0, 0xD15B, 0x88D1, 0xD15C, 0x88D3,\n\t0xD15D, 0x88D6, 0xD15E, 0x88D7, 0xD15F, 0x88DA, 0xD160, 0x88DB,\n\t0xD161, 0x88DC, 0xD162, 0x88DD, 0xD163, 0x88DE, 0xD164, 0x88E0,\n\t0xD165, 0x88E1, 0xD166, 0x88E6, 0xD167, 0x88E7, 0xD168, 0x88E9,\n\t0xD169, 0x88EA, 0xD16A, 0x88EB, 0xD16B, 0x88EC, 0xD16C, 0x88ED,\n\t0xD16D, 0x88EE, 0xD16E, 0x88EF, 0xD16F, 0x88F2, 0xD170, 0x88F5,\n\t0xD171, 0x88F6, 0xD172, 0x88F7, 0xD173, 0x88FA, 0xD174, 0x88FB,\n\t0xD175, 0x88FD, 0xD176, 0x88FF, 0xD177, 0x8900, 0xD178, 0x8901,\n\t0xD179, 0x8903, 0xD17A, 0x8904, 0xD17B, 0x8905, 0xD17C, 0x8906,\n\t0xD17D, 0x8907, 0xD17E, 0x8908, 0xD180, 0x8909, 0xD181, 0x890B,\n\t0xD182, 0x890C, 0xD183, 0x890D, 0xD184, 0x890E, 0xD185, 0x890F,\n\t0xD186, 0x8911, 0xD187, 0x8914, 0xD188, 0x8915, 0xD189, 0x8916,\n\t0xD18A, 0x8917, 0xD18B, 0x8918, 0xD18C, 0x891C, 0xD18D, 0x891D,\n\t0xD18E, 0x891E, 0xD18F, 0x891F, 0xD190, 0x8920, 0xD191, 0x8922,\n\t0xD192, 0x8923, 0xD193, 0x8924, 0xD194, 0x8926, 0xD195, 0x8927,\n\t0xD196, 0x8928, 0xD197, 0x8929, 0xD198, 0x892C, 0xD199, 0x892D,\n\t0xD19A, 0x892E, 0xD19B, 0x892F, 0xD19C, 0x8931, 0xD19D, 0x8932,\n\t0xD19E, 0x8933, 0xD19F, 0x8935, 0xD1A0, 0x8937, 0xD1A1, 0x9009,\n\t0xD1A2, 0x7663, 0xD1A3, 0x7729, 0xD1A4, 0x7EDA, 0xD1A5, 0x9774,\n\t0xD1A6, 0x859B, 0xD1A7, 0x5B66, 0xD1A8, 0x7A74, 0xD1A9, 0x96EA,\n\t0xD1AA, 0x8840, 0xD1AB, 0x52CB, 0xD1AC, 0x718F, 0xD1AD, 0x5FAA,\n\t0xD1AE, 0x65EC, 0xD1AF, 0x8BE2, 0xD1B0, 0x5BFB, 0xD1B1, 0x9A6F,\n\t0xD1B2, 0x5DE1, 0xD1B3, 0x6B89, 0xD1B4, 0x6C5B, 0xD1B5, 0x8BAD,\n\t0xD1B6, 0x8BAF, 0xD1B7, 0x900A, 0xD1B8, 0x8FC5, 0xD1B9, 0x538B,\n\t0xD1BA, 0x62BC, 0xD1BB, 0x9E26, 0xD1BC, 0x9E2D, 0xD1BD, 0x5440,\n\t0xD1BE, 0x4E2B, 0xD1BF, 0x82BD, 0xD1C0, 0x7259, 0xD1C1, 0x869C,\n\t0xD1C2, 0x5D16, 0xD1C3, 0x8859, 0xD1C4, 0x6DAF, 0xD1C5, 0x96C5,\n\t0xD1C6, 0x54D1, 0xD1C7, 0x4E9A, 0xD1C8, 0x8BB6, 0xD1C9, 0x7109,\n\t0xD1CA, 0x54BD, 0xD1CB, 0x9609, 0xD1CC, 0x70DF, 0xD1CD, 0x6DF9,\n\t0xD1CE, 0x76D0, 0xD1CF, 0x4E25, 0xD1D0, 0x7814, 0xD1D1, 0x8712,\n\t0xD1D2, 0x5CA9, 0xD1D3, 0x5EF6, 0xD1D4, 0x8A00, 0xD1D5, 0x989C,\n\t0xD1D6, 0x960E, 0xD1D7, 0x708E, 0xD1D8, 0x6CBF, 0xD1D9, 0x5944,\n\t0xD1DA, 0x63A9, 0xD1DB, 0x773C, 0xD1DC, 0x884D, 0xD1DD, 0x6F14,\n\t0xD1DE, 0x8273, 0xD1DF, 0x5830, 0xD1E0, 0x71D5, 0xD1E1, 0x538C,\n\t0xD1E2, 0x781A, 0xD1E3, 0x96C1, 0xD1E4, 0x5501, 0xD1E5, 0x5F66,\n\t0xD1E6, 0x7130, 0xD1E7, 0x5BB4, 0xD1E8, 0x8C1A, 0xD1E9, 0x9A8C,\n\t0xD1EA, 0x6B83, 0xD1EB, 0x592E, 0xD1EC, 0x9E2F, 0xD1ED, 0x79E7,\n\t0xD1EE, 0x6768, 0xD1EF, 0x626C, 0xD1F0, 0x4F6F, 0xD1F1, 0x75A1,\n\t0xD1F2, 0x7F8A, 0xD1F3, 0x6D0B, 0xD1F4, 0x9633, 0xD1F5, 0x6C27,\n\t0xD1F6, 0x4EF0, 0xD1F7, 0x75D2, 0xD1F8, 0x517B, 0xD1F9, 0x6837,\n\t0xD1FA, 0x6F3E, 0xD1FB, 0x9080, 0xD1FC, 0x8170, 0xD1FD, 0x5996,\n\t0xD1FE, 0x7476, 0xD240, 0x8938, 0xD241, 0x8939, 0xD242, 0x893A,\n\t0xD243, 0x893B, 0xD244, 0x893C, 0xD245, 0x893D, 0xD246, 0x893E,\n\t0xD247, 0x893F, 0xD248, 0x8940, 0xD249, 0x8942, 0xD24A, 0x8943,\n\t0xD24B, 0x8945, 0xD24C, 0x8946, 0xD24D, 0x8947, 0xD24E, 0x8948,\n\t0xD24F, 0x8949, 0xD250, 0x894A, 0xD251, 0x894B, 0xD252, 0x894C,\n\t0xD253, 0x894D, 0xD254, 0x894E, 0xD255, 0x894F, 0xD256, 0x8950,\n\t0xD257, 0x8951, 0xD258, 0x8952, 0xD259, 0x8953, 0xD25A, 0x8954,\n\t0xD25B, 0x8955, 0xD25C, 0x8956, 0xD25D, 0x8957, 0xD25E, 0x8958,\n\t0xD25F, 0x8959, 0xD260, 0x895A, 0xD261, 0x895B, 0xD262, 0x895C,\n\t0xD263, 0x895D, 0xD264, 0x8960, 0xD265, 0x8961, 0xD266, 0x8962,\n\t0xD267, 0x8963, 0xD268, 0x8964, 0xD269, 0x8965, 0xD26A, 0x8967,\n\t0xD26B, 0x8968, 0xD26C, 0x8969, 0xD26D, 0x896A, 0xD26E, 0x896B,\n\t0xD26F, 0x896C, 0xD270, 0x896D, 0xD271, 0x896E, 0xD272, 0x896F,\n\t0xD273, 0x8970, 0xD274, 0x8971, 0xD275, 0x8972, 0xD276, 0x8973,\n\t0xD277, 0x8974, 0xD278, 0x8975, 0xD279, 0x8976, 0xD27A, 0x8977,\n\t0xD27B, 0x8978, 0xD27C, 0x8979, 0xD27D, 0x897A, 0xD27E, 0x897C,\n\t0xD280, 0x897D, 0xD281, 0x897E, 0xD282, 0x8980, 0xD283, 0x8982,\n\t0xD284, 0x8984, 0xD285, 0x8985, 0xD286, 0x8987, 0xD287, 0x8988,\n\t0xD288, 0x8989, 0xD289, 0x898A, 0xD28A, 0x898B, 0xD28B, 0x898C,\n\t0xD28C, 0x898D, 0xD28D, 0x898E, 0xD28E, 0x898F, 0xD28F, 0x8990,\n\t0xD290, 0x8991, 0xD291, 0x8992, 0xD292, 0x8993, 0xD293, 0x8994,\n\t0xD294, 0x8995, 0xD295, 0x8996, 0xD296, 0x8997, 0xD297, 0x8998,\n\t0xD298, 0x8999, 0xD299, 0x899A, 0xD29A, 0x899B, 0xD29B, 0x899C,\n\t0xD29C, 0x899D, 0xD29D, 0x899E, 0xD29E, 0x899F, 0xD29F, 0x89A0,\n\t0xD2A0, 0x89A1, 0xD2A1, 0x6447, 0xD2A2, 0x5C27, 0xD2A3, 0x9065,\n\t0xD2A4, 0x7A91, 0xD2A5, 0x8C23, 0xD2A6, 0x59DA, 0xD2A7, 0x54AC,\n\t0xD2A8, 0x8200, 0xD2A9, 0x836F, 0xD2AA, 0x8981, 0xD2AB, 0x8000,\n\t0xD2AC, 0x6930, 0xD2AD, 0x564E, 0xD2AE, 0x8036, 0xD2AF, 0x7237,\n\t0xD2B0, 0x91CE, 0xD2B1, 0x51B6, 0xD2B2, 0x4E5F, 0xD2B3, 0x9875,\n\t0xD2B4, 0x6396, 0xD2B5, 0x4E1A, 0xD2B6, 0x53F6, 0xD2B7, 0x66F3,\n\t0xD2B8, 0x814B, 0xD2B9, 0x591C, 0xD2BA, 0x6DB2, 0xD2BB, 0x4E00,\n\t0xD2BC, 0x58F9, 0xD2BD, 0x533B, 0xD2BE, 0x63D6, 0xD2BF, 0x94F1,\n\t0xD2C0, 0x4F9D, 0xD2C1, 0x4F0A, 0xD2C2, 0x8863, 0xD2C3, 0x9890,\n\t0xD2C4, 0x5937, 0xD2C5, 0x9057, 0xD2C6, 0x79FB, 0xD2C7, 0x4EEA,\n\t0xD2C8, 0x80F0, 0xD2C9, 0x7591, 0xD2CA, 0x6C82, 0xD2CB, 0x5B9C,\n\t0xD2CC, 0x59E8, 0xD2CD, 0x5F5D, 0xD2CE, 0x6905, 0xD2CF, 0x8681,\n\t0xD2D0, 0x501A, 0xD2D1, 0x5DF2, 0xD2D2, 0x4E59, 0xD2D3, 0x77E3,\n\t0xD2D4, 0x4EE5, 0xD2D5, 0x827A, 0xD2D6, 0x6291, 0xD2D7, 0x6613,\n\t0xD2D8, 0x9091, 0xD2D9, 0x5C79, 0xD2DA, 0x4EBF, 0xD2DB, 0x5F79,\n\t0xD2DC, 0x81C6, 0xD2DD, 0x9038, 0xD2DE, 0x8084, 0xD2DF, 0x75AB,\n\t0xD2E0, 0x4EA6, 0xD2E1, 0x88D4, 0xD2E2, 0x610F, 0xD2E3, 0x6BC5,\n\t0xD2E4, 0x5FC6, 0xD2E5, 0x4E49, 0xD2E6, 0x76CA, 0xD2E7, 0x6EA2,\n\t0xD2E8, 0x8BE3, 0xD2E9, 0x8BAE, 0xD2EA, 0x8C0A, 0xD2EB, 0x8BD1,\n\t0xD2EC, 0x5F02, 0xD2ED, 0x7FFC, 0xD2EE, 0x7FCC, 0xD2EF, 0x7ECE,\n\t0xD2F0, 0x8335, 0xD2F1, 0x836B, 0xD2F2, 0x56E0, 0xD2F3, 0x6BB7,\n\t0xD2F4, 0x97F3, 0xD2F5, 0x9634, 0xD2F6, 0x59FB, 0xD2F7, 0x541F,\n\t0xD2F8, 0x94F6, 0xD2F9, 0x6DEB, 0xD2FA, 0x5BC5, 0xD2FB, 0x996E,\n\t0xD2FC, 0x5C39, 0xD2FD, 0x5F15, 0xD2FE, 0x9690, 0xD340, 0x89A2,\n\t0xD341, 0x89A3, 0xD342, 0x89A4, 0xD343, 0x89A5, 0xD344, 0x89A6,\n\t0xD345, 0x89A7, 0xD346, 0x89A8, 0xD347, 0x89A9, 0xD348, 0x89AA,\n\t0xD349, 0x89AB, 0xD34A, 0x89AC, 0xD34B, 0x89AD, 0xD34C, 0x89AE,\n\t0xD34D, 0x89AF, 0xD34E, 0x89B0, 0xD34F, 0x89B1, 0xD350, 0x89B2,\n\t0xD351, 0x89B3, 0xD352, 0x89B4, 0xD353, 0x89B5, 0xD354, 0x89B6,\n\t0xD355, 0x89B7, 0xD356, 0x89B8, 0xD357, 0x89B9, 0xD358, 0x89BA,\n\t0xD359, 0x89BB, 0xD35A, 0x89BC, 0xD35B, 0x89BD, 0xD35C, 0x89BE,\n\t0xD35D, 0x89BF, 0xD35E, 0x89C0, 0xD35F, 0x89C3, 0xD360, 0x89CD,\n\t0xD361, 0x89D3, 0xD362, 0x89D4, 0xD363, 0x89D5, 0xD364, 0x89D7,\n\t0xD365, 0x89D8, 0xD366, 0x89D9, 0xD367, 0x89DB, 0xD368, 0x89DD,\n\t0xD369, 0x89DF, 0xD36A, 0x89E0, 0xD36B, 0x89E1, 0xD36C, 0x89E2,\n\t0xD36D, 0x89E4, 0xD36E, 0x89E7, 0xD36F, 0x89E8, 0xD370, 0x89E9,\n\t0xD371, 0x89EA, 0xD372, 0x89EC, 0xD373, 0x89ED, 0xD374, 0x89EE,\n\t0xD375, 0x89F0, 0xD376, 0x89F1, 0xD377, 0x89F2, 0xD378, 0x89F4,\n\t0xD379, 0x89F5, 0xD37A, 0x89F6, 0xD37B, 0x89F7, 0xD37C, 0x89F8,\n\t0xD37D, 0x89F9, 0xD37E, 0x89FA, 0xD380, 0x89FB, 0xD381, 0x89FC,\n\t0xD382, 0x89FD, 0xD383, 0x89FE, 0xD384, 0x89FF, 0xD385, 0x8A01,\n\t0xD386, 0x8A02, 0xD387, 0x8A03, 0xD388, 0x8A04, 0xD389, 0x8A05,\n\t0xD38A, 0x8A06, 0xD38B, 0x8A08, 0xD38C, 0x8A09, 0xD38D, 0x8A0A,\n\t0xD38E, 0x8A0B, 0xD38F, 0x8A0C, 0xD390, 0x8A0D, 0xD391, 0x8A0E,\n\t0xD392, 0x8A0F, 0xD393, 0x8A10, 0xD394, 0x8A11, 0xD395, 0x8A12,\n\t0xD396, 0x8A13, 0xD397, 0x8A14, 0xD398, 0x8A15, 0xD399, 0x8A16,\n\t0xD39A, 0x8A17, 0xD39B, 0x8A18, 0xD39C, 0x8A19, 0xD39D, 0x8A1A,\n\t0xD39E, 0x8A1B, 0xD39F, 0x8A1C, 0xD3A0, 0x8A1D, 0xD3A1, 0x5370,\n\t0xD3A2, 0x82F1, 0xD3A3, 0x6A31, 0xD3A4, 0x5A74, 0xD3A5, 0x9E70,\n\t0xD3A6, 0x5E94, 0xD3A7, 0x7F28, 0xD3A8, 0x83B9, 0xD3A9, 0x8424,\n\t0xD3AA, 0x8425, 0xD3AB, 0x8367, 0xD3AC, 0x8747, 0xD3AD, 0x8FCE,\n\t0xD3AE, 0x8D62, 0xD3AF, 0x76C8, 0xD3B0, 0x5F71, 0xD3B1, 0x9896,\n\t0xD3B2, 0x786C, 0xD3B3, 0x6620, 0xD3B4, 0x54DF, 0xD3B5, 0x62E5,\n\t0xD3B6, 0x4F63, 0xD3B7, 0x81C3, 0xD3B8, 0x75C8, 0xD3B9, 0x5EB8,\n\t0xD3BA, 0x96CD, 0xD3BB, 0x8E0A, 0xD3BC, 0x86F9, 0xD3BD, 0x548F,\n\t0xD3BE, 0x6CF3, 0xD3BF, 0x6D8C, 0xD3C0, 0x6C38, 0xD3C1, 0x607F,\n\t0xD3C2, 0x52C7, 0xD3C3, 0x7528, 0xD3C4, 0x5E7D, 0xD3C5, 0x4F18,\n\t0xD3C6, 0x60A0, 0xD3C7, 0x5FE7, 0xD3C8, 0x5C24, 0xD3C9, 0x7531,\n\t0xD3CA, 0x90AE, 0xD3CB, 0x94C0, 0xD3CC, 0x72B9, 0xD3CD, 0x6CB9,\n\t0xD3CE, 0x6E38, 0xD3CF, 0x9149, 0xD3D0, 0x6709, 0xD3D1, 0x53CB,\n\t0xD3D2, 0x53F3, 0xD3D3, 0x4F51, 0xD3D4, 0x91C9, 0xD3D5, 0x8BF1,\n\t0xD3D6, 0x53C8, 0xD3D7, 0x5E7C, 0xD3D8, 0x8FC2, 0xD3D9, 0x6DE4,\n\t0xD3DA, 0x4E8E, 0xD3DB, 0x76C2, 0xD3DC, 0x6986, 0xD3DD, 0x865E,\n\t0xD3DE, 0x611A, 0xD3DF, 0x8206, 0xD3E0, 0x4F59, 0xD3E1, 0x4FDE,\n\t0xD3E2, 0x903E, 0xD3E3, 0x9C7C, 0xD3E4, 0x6109, 0xD3E5, 0x6E1D,\n\t0xD3E6, 0x6E14, 0xD3E7, 0x9685, 0xD3E8, 0x4E88, 0xD3E9, 0x5A31,\n\t0xD3EA, 0x96E8, 0xD3EB, 0x4E0E, 0xD3EC, 0x5C7F, 0xD3ED, 0x79B9,\n\t0xD3EE, 0x5B87, 0xD3EF, 0x8BED, 0xD3F0, 0x7FBD, 0xD3F1, 0x7389,\n\t0xD3F2, 0x57DF, 0xD3F3, 0x828B, 0xD3F4, 0x90C1, 0xD3F5, 0x5401,\n\t0xD3F6, 0x9047, 0xD3F7, 0x55BB, 0xD3F8, 0x5CEA, 0xD3F9, 0x5FA1,\n\t0xD3FA, 0x6108, 0xD3FB, 0x6B32, 0xD3FC, 0x72F1, 0xD3FD, 0x80B2,\n\t0xD3FE, 0x8A89, 0xD440, 0x8A1E, 0xD441, 0x8A1F, 0xD442, 0x8A20,\n\t0xD443, 0x8A21, 0xD444, 0x8A22, 0xD445, 0x8A23, 0xD446, 0x8A24,\n\t0xD447, 0x8A25, 0xD448, 0x8A26, 0xD449, 0x8A27, 0xD44A, 0x8A28,\n\t0xD44B, 0x8A29, 0xD44C, 0x8A2A, 0xD44D, 0x8A2B, 0xD44E, 0x8A2C,\n\t0xD44F, 0x8A2D, 0xD450, 0x8A2E, 0xD451, 0x8A2F, 0xD452, 0x8A30,\n\t0xD453, 0x8A31, 0xD454, 0x8A32, 0xD455, 0x8A33, 0xD456, 0x8A34,\n\t0xD457, 0x8A35, 0xD458, 0x8A36, 0xD459, 0x8A37, 0xD45A, 0x8A38,\n\t0xD45B, 0x8A39, 0xD45C, 0x8A3A, 0xD45D, 0x8A3B, 0xD45E, 0x8A3C,\n\t0xD45F, 0x8A3D, 0xD460, 0x8A3F, 0xD461, 0x8A40, 0xD462, 0x8A41,\n\t0xD463, 0x8A42, 0xD464, 0x8A43, 0xD465, 0x8A44, 0xD466, 0x8A45,\n\t0xD467, 0x8A46, 0xD468, 0x8A47, 0xD469, 0x8A49, 0xD46A, 0x8A4A,\n\t0xD46B, 0x8A4B, 0xD46C, 0x8A4C, 0xD46D, 0x8A4D, 0xD46E, 0x8A4E,\n\t0xD46F, 0x8A4F, 0xD470, 0x8A50, 0xD471, 0x8A51, 0xD472, 0x8A52,\n\t0xD473, 0x8A53, 0xD474, 0x8A54, 0xD475, 0x8A55, 0xD476, 0x8A56,\n\t0xD477, 0x8A57, 0xD478, 0x8A58, 0xD479, 0x8A59, 0xD47A, 0x8A5A,\n\t0xD47B, 0x8A5B, 0xD47C, 0x8A5C, 0xD47D, 0x8A5D, 0xD47E, 0x8A5E,\n\t0xD480, 0x8A5F, 0xD481, 0x8A60, 0xD482, 0x8A61, 0xD483, 0x8A62,\n\t0xD484, 0x8A63, 0xD485, 0x8A64, 0xD486, 0x8A65, 0xD487, 0x8A66,\n\t0xD488, 0x8A67, 0xD489, 0x8A68, 0xD48A, 0x8A69, 0xD48B, 0x8A6A,\n\t0xD48C, 0x8A6B, 0xD48D, 0x8A6C, 0xD48E, 0x8A6D, 0xD48F, 0x8A6E,\n\t0xD490, 0x8A6F, 0xD491, 0x8A70, 0xD492, 0x8A71, 0xD493, 0x8A72,\n\t0xD494, 0x8A73, 0xD495, 0x8A74, 0xD496, 0x8A75, 0xD497, 0x8A76,\n\t0xD498, 0x8A77, 0xD499, 0x8A78, 0xD49A, 0x8A7A, 0xD49B, 0x8A7B,\n\t0xD49C, 0x8A7C, 0xD49D, 0x8A7D, 0xD49E, 0x8A7E, 0xD49F, 0x8A7F,\n\t0xD4A0, 0x8A80, 0xD4A1, 0x6D74, 0xD4A2, 0x5BD3, 0xD4A3, 0x88D5,\n\t0xD4A4, 0x9884, 0xD4A5, 0x8C6B, 0xD4A6, 0x9A6D, 0xD4A7, 0x9E33,\n\t0xD4A8, 0x6E0A, 0xD4A9, 0x51A4, 0xD4AA, 0x5143, 0xD4AB, 0x57A3,\n\t0xD4AC, 0x8881, 0xD4AD, 0x539F, 0xD4AE, 0x63F4, 0xD4AF, 0x8F95,\n\t0xD4B0, 0x56ED, 0xD4B1, 0x5458, 0xD4B2, 0x5706, 0xD4B3, 0x733F,\n\t0xD4B4, 0x6E90, 0xD4B5, 0x7F18, 0xD4B6, 0x8FDC, 0xD4B7, 0x82D1,\n\t0xD4B8, 0x613F, 0xD4B9, 0x6028, 0xD4BA, 0x9662, 0xD4BB, 0x66F0,\n\t0xD4BC, 0x7EA6, 0xD4BD, 0x8D8A, 0xD4BE, 0x8DC3, 0xD4BF, 0x94A5,\n\t0xD4C0, 0x5CB3, 0xD4C1, 0x7CA4, 0xD4C2, 0x6708, 0xD4C3, 0x60A6,\n\t0xD4C4, 0x9605, 0xD4C5, 0x8018, 0xD4C6, 0x4E91, 0xD4C7, 0x90E7,\n\t0xD4C8, 0x5300, 0xD4C9, 0x9668, 0xD4CA, 0x5141, 0xD4CB, 0x8FD0,\n\t0xD4CC, 0x8574, 0xD4CD, 0x915D, 0xD4CE, 0x6655, 0xD4CF, 0x97F5,\n\t0xD4D0, 0x5B55, 0xD4D1, 0x531D, 0xD4D2, 0x7838, 0xD4D3, 0x6742,\n\t0xD4D4, 0x683D, 0xD4D5, 0x54C9, 0xD4D6, 0x707E, 0xD4D7, 0x5BB0,\n\t0xD4D8, 0x8F7D, 0xD4D9, 0x518D, 0xD4DA, 0x5728, 0xD4DB, 0x54B1,\n\t0xD4DC, 0x6512, 0xD4DD, 0x6682, 0xD4DE, 0x8D5E, 0xD4DF, 0x8D43,\n\t0xD4E0, 0x810F, 0xD4E1, 0x846C, 0xD4E2, 0x906D, 0xD4E3, 0x7CDF,\n\t0xD4E4, 0x51FF, 0xD4E5, 0x85FB, 0xD4E6, 0x67A3, 0xD4E7, 0x65E9,\n\t0xD4E8, 0x6FA1, 0xD4E9, 0x86A4, 0xD4EA, 0x8E81, 0xD4EB, 0x566A,\n\t0xD4EC, 0x9020, 0xD4ED, 0x7682, 0xD4EE, 0x7076, 0xD4EF, 0x71E5,\n\t0xD4F0, 0x8D23, 0xD4F1, 0x62E9, 0xD4F2, 0x5219, 0xD4F3, 0x6CFD,\n\t0xD4F4, 0x8D3C, 0xD4F5, 0x600E, 0xD4F6, 0x589E, 0xD4F7, 0x618E,\n\t0xD4F8, 0x66FE, 0xD4F9, 0x8D60, 0xD4FA, 0x624E, 0xD4FB, 0x55B3,\n\t0xD4FC, 0x6E23, 0xD4FD, 0x672D, 0xD4FE, 0x8F67, 0xD540, 0x8A81,\n\t0xD541, 0x8A82, 0xD542, 0x8A83, 0xD543, 0x8A84, 0xD544, 0x8A85,\n\t0xD545, 0x8A86, 0xD546, 0x8A87, 0xD547, 0x8A88, 0xD548, 0x8A8B,\n\t0xD549, 0x8A8C, 0xD54A, 0x8A8D, 0xD54B, 0x8A8E, 0xD54C, 0x8A8F,\n\t0xD54D, 0x8A90, 0xD54E, 0x8A91, 0xD54F, 0x8A92, 0xD550, 0x8A94,\n\t0xD551, 0x8A95, 0xD552, 0x8A96, 0xD553, 0x8A97, 0xD554, 0x8A98,\n\t0xD555, 0x8A99, 0xD556, 0x8A9A, 0xD557, 0x8A9B, 0xD558, 0x8A9C,\n\t0xD559, 0x8A9D, 0xD55A, 0x8A9E, 0xD55B, 0x8A9F, 0xD55C, 0x8AA0,\n\t0xD55D, 0x8AA1, 0xD55E, 0x8AA2, 0xD55F, 0x8AA3, 0xD560, 0x8AA4,\n\t0xD561, 0x8AA5, 0xD562, 0x8AA6, 0xD563, 0x8AA7, 0xD564, 0x8AA8,\n\t0xD565, 0x8AA9, 0xD566, 0x8AAA, 0xD567, 0x8AAB, 0xD568, 0x8AAC,\n\t0xD569, 0x8AAD, 0xD56A, 0x8AAE, 0xD56B, 0x8AAF, 0xD56C, 0x8AB0,\n\t0xD56D, 0x8AB1, 0xD56E, 0x8AB2, 0xD56F, 0x8AB3, 0xD570, 0x8AB4,\n\t0xD571, 0x8AB5, 0xD572, 0x8AB6, 0xD573, 0x8AB7, 0xD574, 0x8AB8,\n\t0xD575, 0x8AB9, 0xD576, 0x8ABA, 0xD577, 0x8ABB, 0xD578, 0x8ABC,\n\t0xD579, 0x8ABD, 0xD57A, 0x8ABE, 0xD57B, 0x8ABF, 0xD57C, 0x8AC0,\n\t0xD57D, 0x8AC1, 0xD57E, 0x8AC2, 0xD580, 0x8AC3, 0xD581, 0x8AC4,\n\t0xD582, 0x8AC5, 0xD583, 0x8AC6, 0xD584, 0x8AC7, 0xD585, 0x8AC8,\n\t0xD586, 0x8AC9, 0xD587, 0x8ACA, 0xD588, 0x8ACB, 0xD589, 0x8ACC,\n\t0xD58A, 0x8ACD, 0xD58B, 0x8ACE, 0xD58C, 0x8ACF, 0xD58D, 0x8AD0,\n\t0xD58E, 0x8AD1, 0xD58F, 0x8AD2, 0xD590, 0x8AD3, 0xD591, 0x8AD4,\n\t0xD592, 0x8AD5, 0xD593, 0x8AD6, 0xD594, 0x8AD7, 0xD595, 0x8AD8,\n\t0xD596, 0x8AD9, 0xD597, 0x8ADA, 0xD598, 0x8ADB, 0xD599, 0x8ADC,\n\t0xD59A, 0x8ADD, 0xD59B, 0x8ADE, 0xD59C, 0x8ADF, 0xD59D, 0x8AE0,\n\t0xD59E, 0x8AE1, 0xD59F, 0x8AE2, 0xD5A0, 0x8AE3, 0xD5A1, 0x94E1,\n\t0xD5A2, 0x95F8, 0xD5A3, 0x7728, 0xD5A4, 0x6805, 0xD5A5, 0x69A8,\n\t0xD5A6, 0x548B, 0xD5A7, 0x4E4D, 0xD5A8, 0x70B8, 0xD5A9, 0x8BC8,\n\t0xD5AA, 0x6458, 0xD5AB, 0x658B, 0xD5AC, 0x5B85, 0xD5AD, 0x7A84,\n\t0xD5AE, 0x503A, 0xD5AF, 0x5BE8, 0xD5B0, 0x77BB, 0xD5B1, 0x6BE1,\n\t0xD5B2, 0x8A79, 0xD5B3, 0x7C98, 0xD5B4, 0x6CBE, 0xD5B5, 0x76CF,\n\t0xD5B6, 0x65A9, 0xD5B7, 0x8F97, 0xD5B8, 0x5D2D, 0xD5B9, 0x5C55,\n\t0xD5BA, 0x8638, 0xD5BB, 0x6808, 0xD5BC, 0x5360, 0xD5BD, 0x6218,\n\t0xD5BE, 0x7AD9, 0xD5BF, 0x6E5B, 0xD5C0, 0x7EFD, 0xD5C1, 0x6A1F,\n\t0xD5C2, 0x7AE0, 0xD5C3, 0x5F70, 0xD5C4, 0x6F33, 0xD5C5, 0x5F20,\n\t0xD5C6, 0x638C, 0xD5C7, 0x6DA8, 0xD5C8, 0x6756, 0xD5C9, 0x4E08,\n\t0xD5CA, 0x5E10, 0xD5CB, 0x8D26, 0xD5CC, 0x4ED7, 0xD5CD, 0x80C0,\n\t0xD5CE, 0x7634, 0xD5CF, 0x969C, 0xD5D0, 0x62DB, 0xD5D1, 0x662D,\n\t0xD5D2, 0x627E, 0xD5D3, 0x6CBC, 0xD5D4, 0x8D75, 0xD5D5, 0x7167,\n\t0xD5D6, 0x7F69, 0xD5D7, 0x5146, 0xD5D8, 0x8087, 0xD5D9, 0x53EC,\n\t0xD5DA, 0x906E, 0xD5DB, 0x6298, 0xD5DC, 0x54F2, 0xD5DD, 0x86F0,\n\t0xD5DE, 0x8F99, 0xD5DF, 0x8005, 0xD5E0, 0x9517, 0xD5E1, 0x8517,\n\t0xD5E2, 0x8FD9, 0xD5E3, 0x6D59, 0xD5E4, 0x73CD, 0xD5E5, 0x659F,\n\t0xD5E6, 0x771F, 0xD5E7, 0x7504, 0xD5E8, 0x7827, 0xD5E9, 0x81FB,\n\t0xD5EA, 0x8D1E, 0xD5EB, 0x9488, 0xD5EC, 0x4FA6, 0xD5ED, 0x6795,\n\t0xD5EE, 0x75B9, 0xD5EF, 0x8BCA, 0xD5F0, 0x9707, 0xD5F1, 0x632F,\n\t0xD5F2, 0x9547, 0xD5F3, 0x9635, 0xD5F4, 0x84B8, 0xD5F5, 0x6323,\n\t0xD5F6, 0x7741, 0xD5F7, 0x5F81, 0xD5F8, 0x72F0, 0xD5F9, 0x4E89,\n\t0xD5FA, 0x6014, 0xD5FB, 0x6574, 0xD5FC, 0x62EF, 0xD5FD, 0x6B63,\n\t0xD5FE, 0x653F, 0xD640, 0x8AE4, 0xD641, 0x8AE5, 0xD642, 0x8AE6,\n\t0xD643, 0x8AE7, 0xD644, 0x8AE8, 0xD645, 0x8AE9, 0xD646, 0x8AEA,\n\t0xD647, 0x8AEB, 0xD648, 0x8AEC, 0xD649, 0x8AED, 0xD64A, 0x8AEE,\n\t0xD64B, 0x8AEF, 0xD64C, 0x8AF0, 0xD64D, 0x8AF1, 0xD64E, 0x8AF2,\n\t0xD64F, 0x8AF3, 0xD650, 0x8AF4, 0xD651, 0x8AF5, 0xD652, 0x8AF6,\n\t0xD653, 0x8AF7, 0xD654, 0x8AF8, 0xD655, 0x8AF9, 0xD656, 0x8AFA,\n\t0xD657, 0x8AFB, 0xD658, 0x8AFC, 0xD659, 0x8AFD, 0xD65A, 0x8AFE,\n\t0xD65B, 0x8AFF, 0xD65C, 0x8B00, 0xD65D, 0x8B01, 0xD65E, 0x8B02,\n\t0xD65F, 0x8B03, 0xD660, 0x8B04, 0xD661, 0x8B05, 0xD662, 0x8B06,\n\t0xD663, 0x8B08, 0xD664, 0x8B09, 0xD665, 0x8B0A, 0xD666, 0x8B0B,\n\t0xD667, 0x8B0C, 0xD668, 0x8B0D, 0xD669, 0x8B0E, 0xD66A, 0x8B0F,\n\t0xD66B, 0x8B10, 0xD66C, 0x8B11, 0xD66D, 0x8B12, 0xD66E, 0x8B13,\n\t0xD66F, 0x8B14, 0xD670, 0x8B15, 0xD671, 0x8B16, 0xD672, 0x8B17,\n\t0xD673, 0x8B18, 0xD674, 0x8B19, 0xD675, 0x8B1A, 0xD676, 0x8B1B,\n\t0xD677, 0x8B1C, 0xD678, 0x8B1D, 0xD679, 0x8B1E, 0xD67A, 0x8B1F,\n\t0xD67B, 0x8B20, 0xD67C, 0x8B21, 0xD67D, 0x8B22, 0xD67E, 0x8B23,\n\t0xD680, 0x8B24, 0xD681, 0x8B25, 0xD682, 0x8B27, 0xD683, 0x8B28,\n\t0xD684, 0x8B29, 0xD685, 0x8B2A, 0xD686, 0x8B2B, 0xD687, 0x8B2C,\n\t0xD688, 0x8B2D, 0xD689, 0x8B2E, 0xD68A, 0x8B2F, 0xD68B, 0x8B30,\n\t0xD68C, 0x8B31, 0xD68D, 0x8B32, 0xD68E, 0x8B33, 0xD68F, 0x8B34,\n\t0xD690, 0x8B35, 0xD691, 0x8B36, 0xD692, 0x8B37, 0xD693, 0x8B38,\n\t0xD694, 0x8B39, 0xD695, 0x8B3A, 0xD696, 0x8B3B, 0xD697, 0x8B3C,\n\t0xD698, 0x8B3D, 0xD699, 0x8B3E, 0xD69A, 0x8B3F, 0xD69B, 0x8B40,\n\t0xD69C, 0x8B41, 0xD69D, 0x8B42, 0xD69E, 0x8B43, 0xD69F, 0x8B44,\n\t0xD6A0, 0x8B45, 0xD6A1, 0x5E27, 0xD6A2, 0x75C7, 0xD6A3, 0x90D1,\n\t0xD6A4, 0x8BC1, 0xD6A5, 0x829D, 0xD6A6, 0x679D, 0xD6A7, 0x652F,\n\t0xD6A8, 0x5431, 0xD6A9, 0x8718, 0xD6AA, 0x77E5, 0xD6AB, 0x80A2,\n\t0xD6AC, 0x8102, 0xD6AD, 0x6C41, 0xD6AE, 0x4E4B, 0xD6AF, 0x7EC7,\n\t0xD6B0, 0x804C, 0xD6B1, 0x76F4, 0xD6B2, 0x690D, 0xD6B3, 0x6B96,\n\t0xD6B4, 0x6267, 0xD6B5, 0x503C, 0xD6B6, 0x4F84, 0xD6B7, 0x5740,\n\t0xD6B8, 0x6307, 0xD6B9, 0x6B62, 0xD6BA, 0x8DBE, 0xD6BB, 0x53EA,\n\t0xD6BC, 0x65E8, 0xD6BD, 0x7EB8, 0xD6BE, 0x5FD7, 0xD6BF, 0x631A,\n\t0xD6C0, 0x63B7, 0xD6C1, 0x81F3, 0xD6C2, 0x81F4, 0xD6C3, 0x7F6E,\n\t0xD6C4, 0x5E1C, 0xD6C5, 0x5CD9, 0xD6C6, 0x5236, 0xD6C7, 0x667A,\n\t0xD6C8, 0x79E9, 0xD6C9, 0x7A1A, 0xD6CA, 0x8D28, 0xD6CB, 0x7099,\n\t0xD6CC, 0x75D4, 0xD6CD, 0x6EDE, 0xD6CE, 0x6CBB, 0xD6CF, 0x7A92,\n\t0xD6D0, 0x4E2D, 0xD6D1, 0x76C5, 0xD6D2, 0x5FE0, 0xD6D3, 0x949F,\n\t0xD6D4, 0x8877, 0xD6D5, 0x7EC8, 0xD6D6, 0x79CD, 0xD6D7, 0x80BF,\n\t0xD6D8, 0x91CD, 0xD6D9, 0x4EF2, 0xD6DA, 0x4F17, 0xD6DB, 0x821F,\n\t0xD6DC, 0x5468, 0xD6DD, 0x5DDE, 0xD6DE, 0x6D32, 0xD6DF, 0x8BCC,\n\t0xD6E0, 0x7CA5, 0xD6E1, 0x8F74, 0xD6E2, 0x8098, 0xD6E3, 0x5E1A,\n\t0xD6E4, 0x5492, 0xD6E5, 0x76B1, 0xD6E6, 0x5B99, 0xD6E7, 0x663C,\n\t0xD6E8, 0x9AA4, 0xD6E9, 0x73E0, 0xD6EA, 0x682A, 0xD6EB, 0x86DB,\n\t0xD6EC, 0x6731, 0xD6ED, 0x732A, 0xD6EE, 0x8BF8, 0xD6EF, 0x8BDB,\n\t0xD6F0, 0x9010, 0xD6F1, 0x7AF9, 0xD6F2, 0x70DB, 0xD6F3, 0x716E,\n\t0xD6F4, 0x62C4, 0xD6F5, 0x77A9, 0xD6F6, 0x5631, 0xD6F7, 0x4E3B,\n\t0xD6F8, 0x8457, 0xD6F9, 0x67F1, 0xD6FA, 0x52A9, 0xD6FB, 0x86C0,\n\t0xD6FC, 0x8D2E, 0xD6FD, 0x94F8, 0xD6FE, 0x7B51, 0xD740, 0x8B46,\n\t0xD741, 0x8B47, 0xD742, 0x8B48, 0xD743, 0x8B49, 0xD744, 0x8B4A,\n\t0xD745, 0x8B4B, 0xD746, 0x8B4C, 0xD747, 0x8B4D, 0xD748, 0x8B4E,\n\t0xD749, 0x8B4F, 0xD74A, 0x8B50, 0xD74B, 0x8B51, 0xD74C, 0x8B52,\n\t0xD74D, 0x8B53, 0xD74E, 0x8B54, 0xD74F, 0x8B55, 0xD750, 0x8B56,\n\t0xD751, 0x8B57, 0xD752, 0x8B58, 0xD753, 0x8B59, 0xD754, 0x8B5A,\n\t0xD755, 0x8B5B, 0xD756, 0x8B5C, 0xD757, 0x8B5D, 0xD758, 0x8B5E,\n\t0xD759, 0x8B5F, 0xD75A, 0x8B60, 0xD75B, 0x8B61, 0xD75C, 0x8B62,\n\t0xD75D, 0x8B63, 0xD75E, 0x8B64, 0xD75F, 0x8B65, 0xD760, 0x8B67,\n\t0xD761, 0x8B68, 0xD762, 0x8B69, 0xD763, 0x8B6A, 0xD764, 0x8B6B,\n\t0xD765, 0x8B6D, 0xD766, 0x8B6E, 0xD767, 0x8B6F, 0xD768, 0x8B70,\n\t0xD769, 0x8B71, 0xD76A, 0x8B72, 0xD76B, 0x8B73, 0xD76C, 0x8B74,\n\t0xD76D, 0x8B75, 0xD76E, 0x8B76, 0xD76F, 0x8B77, 0xD770, 0x8B78,\n\t0xD771, 0x8B79, 0xD772, 0x8B7A, 0xD773, 0x8B7B, 0xD774, 0x8B7C,\n\t0xD775, 0x8B7D, 0xD776, 0x8B7E, 0xD777, 0x8B7F, 0xD778, 0x8B80,\n\t0xD779, 0x8B81, 0xD77A, 0x8B82, 0xD77B, 0x8B83, 0xD77C, 0x8B84,\n\t0xD77D, 0x8B85, 0xD77E, 0x8B86, 0xD780, 0x8B87, 0xD781, 0x8B88,\n\t0xD782, 0x8B89, 0xD783, 0x8B8A, 0xD784, 0x8B8B, 0xD785, 0x8B8C,\n\t0xD786, 0x8B8D, 0xD787, 0x8B8E, 0xD788, 0x8B8F, 0xD789, 0x8B90,\n\t0xD78A, 0x8B91, 0xD78B, 0x8B92, 0xD78C, 0x8B93, 0xD78D, 0x8B94,\n\t0xD78E, 0x8B95, 0xD78F, 0x8B96, 0xD790, 0x8B97, 0xD791, 0x8B98,\n\t0xD792, 0x8B99, 0xD793, 0x8B9A, 0xD794, 0x8B9B, 0xD795, 0x8B9C,\n\t0xD796, 0x8B9D, 0xD797, 0x8B9E, 0xD798, 0x8B9F, 0xD799, 0x8BAC,\n\t0xD79A, 0x8BB1, 0xD79B, 0x8BBB, 0xD79C, 0x8BC7, 0xD79D, 0x8BD0,\n\t0xD79E, 0x8BEA, 0xD79F, 0x8C09, 0xD7A0, 0x8C1E, 0xD7A1, 0x4F4F,\n\t0xD7A2, 0x6CE8, 0xD7A3, 0x795D, 0xD7A4, 0x9A7B, 0xD7A5, 0x6293,\n\t0xD7A6, 0x722A, 0xD7A7, 0x62FD, 0xD7A8, 0x4E13, 0xD7A9, 0x7816,\n\t0xD7AA, 0x8F6C, 0xD7AB, 0x64B0, 0xD7AC, 0x8D5A, 0xD7AD, 0x7BC6,\n\t0xD7AE, 0x6869, 0xD7AF, 0x5E84, 0xD7B0, 0x88C5, 0xD7B1, 0x5986,\n\t0xD7B2, 0x649E, 0xD7B3, 0x58EE, 0xD7B4, 0x72B6, 0xD7B5, 0x690E,\n\t0xD7B6, 0x9525, 0xD7B7, 0x8FFD, 0xD7B8, 0x8D58, 0xD7B9, 0x5760,\n\t0xD7BA, 0x7F00, 0xD7BB, 0x8C06, 0xD7BC, 0x51C6, 0xD7BD, 0x6349,\n\t0xD7BE, 0x62D9, 0xD7BF, 0x5353, 0xD7C0, 0x684C, 0xD7C1, 0x7422,\n\t0xD7C2, 0x8301, 0xD7C3, 0x914C, 0xD7C4, 0x5544, 0xD7C5, 0x7740,\n\t0xD7C6, 0x707C, 0xD7C7, 0x6D4A, 0xD7C8, 0x5179, 0xD7C9, 0x54A8,\n\t0xD7CA, 0x8D44, 0xD7CB, 0x59FF, 0xD7CC, 0x6ECB, 0xD7CD, 0x6DC4,\n\t0xD7CE, 0x5B5C, 0xD7CF, 0x7D2B, 0xD7D0, 0x4ED4, 0xD7D1, 0x7C7D,\n\t0xD7D2, 0x6ED3, 0xD7D3, 0x5B50, 0xD7D4, 0x81EA, 0xD7D5, 0x6E0D,\n\t0xD7D6, 0x5B57, 0xD7D7, 0x9B03, 0xD7D8, 0x68D5, 0xD7D9, 0x8E2A,\n\t0xD7DA, 0x5B97, 0xD7DB, 0x7EFC, 0xD7DC, 0x603B, 0xD7DD, 0x7EB5,\n\t0xD7DE, 0x90B9, 0xD7DF, 0x8D70, 0xD7E0, 0x594F, 0xD7E1, 0x63CD,\n\t0xD7E2, 0x79DF, 0xD7E3, 0x8DB3, 0xD7E4, 0x5352, 0xD7E5, 0x65CF,\n\t0xD7E6, 0x7956, 0xD7E7, 0x8BC5, 0xD7E8, 0x963B, 0xD7E9, 0x7EC4,\n\t0xD7EA, 0x94BB, 0xD7EB, 0x7E82, 0xD7EC, 0x5634, 0xD7ED, 0x9189,\n\t0xD7EE, 0x6700, 0xD7EF, 0x7F6A, 0xD7F0, 0x5C0A, 0xD7F1, 0x9075,\n\t0xD7F2, 0x6628, 0xD7F3, 0x5DE6, 0xD7F4, 0x4F50, 0xD7F5, 0x67DE,\n\t0xD7F6, 0x505A, 0xD7F7, 0x4F5C, 0xD7F8, 0x5750, 0xD7F9, 0x5EA7,\n\t0xD840, 0x8C38, 0xD841, 0x8C39, 0xD842, 0x8C3A, 0xD843, 0x8C3B,\n\t0xD844, 0x8C3C, 0xD845, 0x8C3D, 0xD846, 0x8C3E, 0xD847, 0x8C3F,\n\t0xD848, 0x8C40, 0xD849, 0x8C42, 0xD84A, 0x8C43, 0xD84B, 0x8C44,\n\t0xD84C, 0x8C45, 0xD84D, 0x8C48, 0xD84E, 0x8C4A, 0xD84F, 0x8C4B,\n\t0xD850, 0x8C4D, 0xD851, 0x8C4E, 0xD852, 0x8C4F, 0xD853, 0x8C50,\n\t0xD854, 0x8C51, 0xD855, 0x8C52, 0xD856, 0x8C53, 0xD857, 0x8C54,\n\t0xD858, 0x8C56, 0xD859, 0x8C57, 0xD85A, 0x8C58, 0xD85B, 0x8C59,\n\t0xD85C, 0x8C5B, 0xD85D, 0x8C5C, 0xD85E, 0x8C5D, 0xD85F, 0x8C5E,\n\t0xD860, 0x8C5F, 0xD861, 0x8C60, 0xD862, 0x8C63, 0xD863, 0x8C64,\n\t0xD864, 0x8C65, 0xD865, 0x8C66, 0xD866, 0x8C67, 0xD867, 0x8C68,\n\t0xD868, 0x8C69, 0xD869, 0x8C6C, 0xD86A, 0x8C6D, 0xD86B, 0x8C6E,\n\t0xD86C, 0x8C6F, 0xD86D, 0x8C70, 0xD86E, 0x8C71, 0xD86F, 0x8C72,\n\t0xD870, 0x8C74, 0xD871, 0x8C75, 0xD872, 0x8C76, 0xD873, 0x8C77,\n\t0xD874, 0x8C7B, 0xD875, 0x8C7C, 0xD876, 0x8C7D, 0xD877, 0x8C7E,\n\t0xD878, 0x8C7F, 0xD879, 0x8C80, 0xD87A, 0x8C81, 0xD87B, 0x8C83,\n\t0xD87C, 0x8C84, 0xD87D, 0x8C86, 0xD87E, 0x8C87, 0xD880, 0x8C88,\n\t0xD881, 0x8C8B, 0xD882, 0x8C8D, 0xD883, 0x8C8E, 0xD884, 0x8C8F,\n\t0xD885, 0x8C90, 0xD886, 0x8C91, 0xD887, 0x8C92, 0xD888, 0x8C93,\n\t0xD889, 0x8C95, 0xD88A, 0x8C96, 0xD88B, 0x8C97, 0xD88C, 0x8C99,\n\t0xD88D, 0x8C9A, 0xD88E, 0x8C9B, 0xD88F, 0x8C9C, 0xD890, 0x8C9D,\n\t0xD891, 0x8C9E, 0xD892, 0x8C9F, 0xD893, 0x8CA0, 0xD894, 0x8CA1,\n\t0xD895, 0x8CA2, 0xD896, 0x8CA3, 0xD897, 0x8CA4, 0xD898, 0x8CA5,\n\t0xD899, 0x8CA6, 0xD89A, 0x8CA7, 0xD89B, 0x8CA8, 0xD89C, 0x8CA9,\n\t0xD89D, 0x8CAA, 0xD89E, 0x8CAB, 0xD89F, 0x8CAC, 0xD8A0, 0x8CAD,\n\t0xD8A1, 0x4E8D, 0xD8A2, 0x4E0C, 0xD8A3, 0x5140, 0xD8A4, 0x4E10,\n\t0xD8A5, 0x5EFF, 0xD8A6, 0x5345, 0xD8A7, 0x4E15, 0xD8A8, 0x4E98,\n\t0xD8A9, 0x4E1E, 0xD8AA, 0x9B32, 0xD8AB, 0x5B6C, 0xD8AC, 0x5669,\n\t0xD8AD, 0x4E28, 0xD8AE, 0x79BA, 0xD8AF, 0x4E3F, 0xD8B0, 0x5315,\n\t0xD8B1, 0x4E47, 0xD8B2, 0x592D, 0xD8B3, 0x723B, 0xD8B4, 0x536E,\n\t0xD8B5, 0x6C10, 0xD8B6, 0x56DF, 0xD8B7, 0x80E4, 0xD8B8, 0x9997,\n\t0xD8B9, 0x6BD3, 0xD8BA, 0x777E, 0xD8BB, 0x9F17, 0xD8BC, 0x4E36,\n\t0xD8BD, 0x4E9F, 0xD8BE, 0x9F10, 0xD8BF, 0x4E5C, 0xD8C0, 0x4E69,\n\t0xD8C1, 0x4E93, 0xD8C2, 0x8288, 0xD8C3, 0x5B5B, 0xD8C4, 0x556C,\n\t0xD8C5, 0x560F, 0xD8C6, 0x4EC4, 0xD8C7, 0x538D, 0xD8C8, 0x539D,\n\t0xD8C9, 0x53A3, 0xD8CA, 0x53A5, 0xD8CB, 0x53AE, 0xD8CC, 0x9765,\n\t0xD8CD, 0x8D5D, 0xD8CE, 0x531A, 0xD8CF, 0x53F5, 0xD8D0, 0x5326,\n\t0xD8D1, 0x532E, 0xD8D2, 0x533E, 0xD8D3, 0x8D5C, 0xD8D4, 0x5366,\n\t0xD8D5, 0x5363, 0xD8D6, 0x5202, 0xD8D7, 0x5208, 0xD8D8, 0x520E,\n\t0xD8D9, 0x522D, 0xD8DA, 0x5233, 0xD8DB, 0x523F, 0xD8DC, 0x5240,\n\t0xD8DD, 0x524C, 0xD8DE, 0x525E, 0xD8DF, 0x5261, 0xD8E0, 0x525C,\n\t0xD8E1, 0x84AF, 0xD8E2, 0x527D, 0xD8E3, 0x5282, 0xD8E4, 0x5281,\n\t0xD8E5, 0x5290, 0xD8E6, 0x5293, 0xD8E7, 0x5182, 0xD8E8, 0x7F54,\n\t0xD8E9, 0x4EBB, 0xD8EA, 0x4EC3, 0xD8EB, 0x4EC9, 0xD8EC, 0x4EC2,\n\t0xD8ED, 0x4EE8, 0xD8EE, 0x4EE1, 0xD8EF, 0x4EEB, 0xD8F0, 0x4EDE,\n\t0xD8F1, 0x4F1B, 0xD8F2, 0x4EF3, 0xD8F3, 0x4F22, 0xD8F4, 0x4F64,\n\t0xD8F5, 0x4EF5, 0xD8F6, 0x4F25, 0xD8F7, 0x4F27, 0xD8F8, 0x4F09,\n\t0xD8F9, 0x4F2B, 0xD8FA, 0x4F5E, 0xD8FB, 0x4F67, 0xD8FC, 0x6538,\n\t0xD8FD, 0x4F5A, 0xD8FE, 0x4F5D, 0xD940, 0x8CAE, 0xD941, 0x8CAF,\n\t0xD942, 0x8CB0, 0xD943, 0x8CB1, 0xD944, 0x8CB2, 0xD945, 0x8CB3,\n\t0xD946, 0x8CB4, 0xD947, 0x8CB5, 0xD948, 0x8CB6, 0xD949, 0x8CB7,\n\t0xD94A, 0x8CB8, 0xD94B, 0x8CB9, 0xD94C, 0x8CBA, 0xD94D, 0x8CBB,\n\t0xD94E, 0x8CBC, 0xD94F, 0x8CBD, 0xD950, 0x8CBE, 0xD951, 0x8CBF,\n\t0xD952, 0x8CC0, 0xD953, 0x8CC1, 0xD954, 0x8CC2, 0xD955, 0x8CC3,\n\t0xD956, 0x8CC4, 0xD957, 0x8CC5, 0xD958, 0x8CC6, 0xD959, 0x8CC7,\n\t0xD95A, 0x8CC8, 0xD95B, 0x8CC9, 0xD95C, 0x8CCA, 0xD95D, 0x8CCB,\n\t0xD95E, 0x8CCC, 0xD95F, 0x8CCD, 0xD960, 0x8CCE, 0xD961, 0x8CCF,\n\t0xD962, 0x8CD0, 0xD963, 0x8CD1, 0xD964, 0x8CD2, 0xD965, 0x8CD3,\n\t0xD966, 0x8CD4, 0xD967, 0x8CD5, 0xD968, 0x8CD6, 0xD969, 0x8CD7,\n\t0xD96A, 0x8CD8, 0xD96B, 0x8CD9, 0xD96C, 0x8CDA, 0xD96D, 0x8CDB,\n\t0xD96E, 0x8CDC, 0xD96F, 0x8CDD, 0xD970, 0x8CDE, 0xD971, 0x8CDF,\n\t0xD972, 0x8CE0, 0xD973, 0x8CE1, 0xD974, 0x8CE2, 0xD975, 0x8CE3,\n\t0xD976, 0x8CE4, 0xD977, 0x8CE5, 0xD978, 0x8CE6, 0xD979, 0x8CE7,\n\t0xD97A, 0x8CE8, 0xD97B, 0x8CE9, 0xD97C, 0x8CEA, 0xD97D, 0x8CEB,\n\t0xD97E, 0x8CEC, 0xD980, 0x8CED, 0xD981, 0x8CEE, 0xD982, 0x8CEF,\n\t0xD983, 0x8CF0, 0xD984, 0x8CF1, 0xD985, 0x8CF2, 0xD986, 0x8CF3,\n\t0xD987, 0x8CF4, 0xD988, 0x8CF5, 0xD989, 0x8CF6, 0xD98A, 0x8CF7,\n\t0xD98B, 0x8CF8, 0xD98C, 0x8CF9, 0xD98D, 0x8CFA, 0xD98E, 0x8CFB,\n\t0xD98F, 0x8CFC, 0xD990, 0x8CFD, 0xD991, 0x8CFE, 0xD992, 0x8CFF,\n\t0xD993, 0x8D00, 0xD994, 0x8D01, 0xD995, 0x8D02, 0xD996, 0x8D03,\n\t0xD997, 0x8D04, 0xD998, 0x8D05, 0xD999, 0x8D06, 0xD99A, 0x8D07,\n\t0xD99B, 0x8D08, 0xD99C, 0x8D09, 0xD99D, 0x8D0A, 0xD99E, 0x8D0B,\n\t0xD99F, 0x8D0C, 0xD9A0, 0x8D0D, 0xD9A1, 0x4F5F, 0xD9A2, 0x4F57,\n\t0xD9A3, 0x4F32, 0xD9A4, 0x4F3D, 0xD9A5, 0x4F76, 0xD9A6, 0x4F74,\n\t0xD9A7, 0x4F91, 0xD9A8, 0x4F89, 0xD9A9, 0x4F83, 0xD9AA, 0x4F8F,\n\t0xD9AB, 0x4F7E, 0xD9AC, 0x4F7B, 0xD9AD, 0x4FAA, 0xD9AE, 0x4F7C,\n\t0xD9AF, 0x4FAC, 0xD9B0, 0x4F94, 0xD9B1, 0x4FE6, 0xD9B2, 0x4FE8,\n\t0xD9B3, 0x4FEA, 0xD9B4, 0x4FC5, 0xD9B5, 0x4FDA, 0xD9B6, 0x4FE3,\n\t0xD9B7, 0x4FDC, 0xD9B8, 0x4FD1, 0xD9B9, 0x4FDF, 0xD9BA, 0x4FF8,\n\t0xD9BB, 0x5029, 0xD9BC, 0x504C, 0xD9BD, 0x4FF3, 0xD9BE, 0x502C,\n\t0xD9BF, 0x500F, 0xD9C0, 0x502E, 0xD9C1, 0x502D, 0xD9C2, 0x4FFE,\n\t0xD9C3, 0x501C, 0xD9C4, 0x500C, 0xD9C5, 0x5025, 0xD9C6, 0x5028,\n\t0xD9C7, 0x507E, 0xD9C8, 0x5043, 0xD9C9, 0x5055, 0xD9CA, 0x5048,\n\t0xD9CB, 0x504E, 0xD9CC, 0x506C, 0xD9CD, 0x507B, 0xD9CE, 0x50A5,\n\t0xD9CF, 0x50A7, 0xD9D0, 0x50A9, 0xD9D1, 0x50BA, 0xD9D2, 0x50D6,\n\t0xD9D3, 0x5106, 0xD9D4, 0x50ED, 0xD9D5, 0x50EC, 0xD9D6, 0x50E6,\n\t0xD9D7, 0x50EE, 0xD9D8, 0x5107, 0xD9D9, 0x510B, 0xD9DA, 0x4EDD,\n\t0xD9DB, 0x6C3D, 0xD9DC, 0x4F58, 0xD9DD, 0x4F65, 0xD9DE, 0x4FCE,\n\t0xD9DF, 0x9FA0, 0xD9E0, 0x6C46, 0xD9E1, 0x7C74, 0xD9E2, 0x516E,\n\t0xD9E3, 0x5DFD, 0xD9E4, 0x9EC9, 0xD9E5, 0x9998, 0xD9E6, 0x5181,\n\t0xD9E7, 0x5914, 0xD9E8, 0x52F9, 0xD9E9, 0x530D, 0xD9EA, 0x8A07,\n\t0xD9EB, 0x5310, 0xD9EC, 0x51EB, 0xD9ED, 0x5919, 0xD9EE, 0x5155,\n\t0xD9EF, 0x4EA0, 0xD9F0, 0x5156, 0xD9F1, 0x4EB3, 0xD9F2, 0x886E,\n\t0xD9F3, 0x88A4, 0xD9F4, 0x4EB5, 0xD9F5, 0x8114, 0xD9F6, 0x88D2,\n\t0xD9F7, 0x7980, 0xD9F8, 0x5B34, 0xD9F9, 0x8803, 0xD9FA, 0x7FB8,\n\t0xD9FB, 0x51AB, 0xD9FC, 0x51B1, 0xD9FD, 0x51BD, 0xD9FE, 0x51BC,\n\t0xDA40, 0x8D0E, 0xDA41, 0x8D0F, 0xDA42, 0x8D10, 0xDA43, 0x8D11,\n\t0xDA44, 0x8D12, 0xDA45, 0x8D13, 0xDA46, 0x8D14, 0xDA47, 0x8D15,\n\t0xDA48, 0x8D16, 0xDA49, 0x8D17, 0xDA4A, 0x8D18, 0xDA4B, 0x8D19,\n\t0xDA4C, 0x8D1A, 0xDA4D, 0x8D1B, 0xDA4E, 0x8D1C, 0xDA4F, 0x8D20,\n\t0xDA50, 0x8D51, 0xDA51, 0x8D52, 0xDA52, 0x8D57, 0xDA53, 0x8D5F,\n\t0xDA54, 0x8D65, 0xDA55, 0x8D68, 0xDA56, 0x8D69, 0xDA57, 0x8D6A,\n\t0xDA58, 0x8D6C, 0xDA59, 0x8D6E, 0xDA5A, 0x8D6F, 0xDA5B, 0x8D71,\n\t0xDA5C, 0x8D72, 0xDA5D, 0x8D78, 0xDA5E, 0x8D79, 0xDA5F, 0x8D7A,\n\t0xDA60, 0x8D7B, 0xDA61, 0x8D7C, 0xDA62, 0x8D7D, 0xDA63, 0x8D7E,\n\t0xDA64, 0x8D7F, 0xDA65, 0x8D80, 0xDA66, 0x8D82, 0xDA67, 0x8D83,\n\t0xDA68, 0x8D86, 0xDA69, 0x8D87, 0xDA6A, 0x8D88, 0xDA6B, 0x8D89,\n\t0xDA6C, 0x8D8C, 0xDA6D, 0x8D8D, 0xDA6E, 0x8D8E, 0xDA6F, 0x8D8F,\n\t0xDA70, 0x8D90, 0xDA71, 0x8D92, 0xDA72, 0x8D93, 0xDA73, 0x8D95,\n\t0xDA74, 0x8D96, 0xDA75, 0x8D97, 0xDA76, 0x8D98, 0xDA77, 0x8D99,\n\t0xDA78, 0x8D9A, 0xDA79, 0x8D9B, 0xDA7A, 0x8D9C, 0xDA7B, 0x8D9D,\n\t0xDA7C, 0x8D9E, 0xDA7D, 0x8DA0, 0xDA7E, 0x8DA1, 0xDA80, 0x8DA2,\n\t0xDA81, 0x8DA4, 0xDA82, 0x8DA5, 0xDA83, 0x8DA6, 0xDA84, 0x8DA7,\n\t0xDA85, 0x8DA8, 0xDA86, 0x8DA9, 0xDA87, 0x8DAA, 0xDA88, 0x8DAB,\n\t0xDA89, 0x8DAC, 0xDA8A, 0x8DAD, 0xDA8B, 0x8DAE, 0xDA8C, 0x8DAF,\n\t0xDA8D, 0x8DB0, 0xDA8E, 0x8DB2, 0xDA8F, 0x8DB6, 0xDA90, 0x8DB7,\n\t0xDA91, 0x8DB9, 0xDA92, 0x8DBB, 0xDA93, 0x8DBD, 0xDA94, 0x8DC0,\n\t0xDA95, 0x8DC1, 0xDA96, 0x8DC2, 0xDA97, 0x8DC5, 0xDA98, 0x8DC7,\n\t0xDA99, 0x8DC8, 0xDA9A, 0x8DC9, 0xDA9B, 0x8DCA, 0xDA9C, 0x8DCD,\n\t0xDA9D, 0x8DD0, 0xDA9E, 0x8DD2, 0xDA9F, 0x8DD3, 0xDAA0, 0x8DD4,\n\t0xDAA1, 0x51C7, 0xDAA2, 0x5196, 0xDAA3, 0x51A2, 0xDAA4, 0x51A5,\n\t0xDAA5, 0x8BA0, 0xDAA6, 0x8BA6, 0xDAA7, 0x8BA7, 0xDAA8, 0x8BAA,\n\t0xDAA9, 0x8BB4, 0xDAAA, 0x8BB5, 0xDAAB, 0x8BB7, 0xDAAC, 0x8BC2,\n\t0xDAAD, 0x8BC3, 0xDAAE, 0x8BCB, 0xDAAF, 0x8BCF, 0xDAB0, 0x8BCE,\n\t0xDAB1, 0x8BD2, 0xDAB2, 0x8BD3, 0xDAB3, 0x8BD4, 0xDAB4, 0x8BD6,\n\t0xDAB5, 0x8BD8, 0xDAB6, 0x8BD9, 0xDAB7, 0x8BDC, 0xDAB8, 0x8BDF,\n\t0xDAB9, 0x8BE0, 0xDABA, 0x8BE4, 0xDABB, 0x8BE8, 0xDABC, 0x8BE9,\n\t0xDABD, 0x8BEE, 0xDABE, 0x8BF0, 0xDABF, 0x8BF3, 0xDAC0, 0x8BF6,\n\t0xDAC1, 0x8BF9, 0xDAC2, 0x8BFC, 0xDAC3, 0x8BFF, 0xDAC4, 0x8C00,\n\t0xDAC5, 0x8C02, 0xDAC6, 0x8C04, 0xDAC7, 0x8C07, 0xDAC8, 0x8C0C,\n\t0xDAC9, 0x8C0F, 0xDACA, 0x8C11, 0xDACB, 0x8C12, 0xDACC, 0x8C14,\n\t0xDACD, 0x8C15, 0xDACE, 0x8C16, 0xDACF, 0x8C19, 0xDAD0, 0x8C1B,\n\t0xDAD1, 0x8C18, 0xDAD2, 0x8C1D, 0xDAD3, 0x8C1F, 0xDAD4, 0x8C20,\n\t0xDAD5, 0x8C21, 0xDAD6, 0x8C25, 0xDAD7, 0x8C27, 0xDAD8, 0x8C2A,\n\t0xDAD9, 0x8C2B, 0xDADA, 0x8C2E, 0xDADB, 0x8C2F, 0xDADC, 0x8C32,\n\t0xDADD, 0x8C33, 0xDADE, 0x8C35, 0xDADF, 0x8C36, 0xDAE0, 0x5369,\n\t0xDAE1, 0x537A, 0xDAE2, 0x961D, 0xDAE3, 0x9622, 0xDAE4, 0x9621,\n\t0xDAE5, 0x9631, 0xDAE6, 0x962A, 0xDAE7, 0x963D, 0xDAE8, 0x963C,\n\t0xDAE9, 0x9642, 0xDAEA, 0x9649, 0xDAEB, 0x9654, 0xDAEC, 0x965F,\n\t0xDAED, 0x9667, 0xDAEE, 0x966C, 0xDAEF, 0x9672, 0xDAF0, 0x9674,\n\t0xDAF1, 0x9688, 0xDAF2, 0x968D, 0xDAF3, 0x9697, 0xDAF4, 0x96B0,\n\t0xDAF5, 0x9097, 0xDAF6, 0x909B, 0xDAF7, 0x909D, 0xDAF8, 0x9099,\n\t0xDAF9, 0x90AC, 0xDAFA, 0x90A1, 0xDAFB, 0x90B4, 0xDAFC, 0x90B3,\n\t0xDAFD, 0x90B6, 0xDAFE, 0x90BA, 0xDB40, 0x8DD5, 0xDB41, 0x8DD8,\n\t0xDB42, 0x8DD9, 0xDB43, 0x8DDC, 0xDB44, 0x8DE0, 0xDB45, 0x8DE1,\n\t0xDB46, 0x8DE2, 0xDB47, 0x8DE5, 0xDB48, 0x8DE6, 0xDB49, 0x8DE7,\n\t0xDB4A, 0x8DE9, 0xDB4B, 0x8DED, 0xDB4C, 0x8DEE, 0xDB4D, 0x8DF0,\n\t0xDB4E, 0x8DF1, 0xDB4F, 0x8DF2, 0xDB50, 0x8DF4, 0xDB51, 0x8DF6,\n\t0xDB52, 0x8DFC, 0xDB53, 0x8DFE, 0xDB54, 0x8DFF, 0xDB55, 0x8E00,\n\t0xDB56, 0x8E01, 0xDB57, 0x8E02, 0xDB58, 0x8E03, 0xDB59, 0x8E04,\n\t0xDB5A, 0x8E06, 0xDB5B, 0x8E07, 0xDB5C, 0x8E08, 0xDB5D, 0x8E0B,\n\t0xDB5E, 0x8E0D, 0xDB5F, 0x8E0E, 0xDB60, 0x8E10, 0xDB61, 0x8E11,\n\t0xDB62, 0x8E12, 0xDB63, 0x8E13, 0xDB64, 0x8E15, 0xDB65, 0x8E16,\n\t0xDB66, 0x8E17, 0xDB67, 0x8E18, 0xDB68, 0x8E19, 0xDB69, 0x8E1A,\n\t0xDB6A, 0x8E1B, 0xDB6B, 0x8E1C, 0xDB6C, 0x8E20, 0xDB6D, 0x8E21,\n\t0xDB6E, 0x8E24, 0xDB6F, 0x8E25, 0xDB70, 0x8E26, 0xDB71, 0x8E27,\n\t0xDB72, 0x8E28, 0xDB73, 0x8E2B, 0xDB74, 0x8E2D, 0xDB75, 0x8E30,\n\t0xDB76, 0x8E32, 0xDB77, 0x8E33, 0xDB78, 0x8E34, 0xDB79, 0x8E36,\n\t0xDB7A, 0x8E37, 0xDB7B, 0x8E38, 0xDB7C, 0x8E3B, 0xDB7D, 0x8E3C,\n\t0xDB7E, 0x8E3E, 0xDB80, 0x8E3F, 0xDB81, 0x8E43, 0xDB82, 0x8E45,\n\t0xDB83, 0x8E46, 0xDB84, 0x8E4C, 0xDB85, 0x8E4D, 0xDB86, 0x8E4E,\n\t0xDB87, 0x8E4F, 0xDB88, 0x8E50, 0xDB89, 0x8E53, 0xDB8A, 0x8E54,\n\t0xDB8B, 0x8E55, 0xDB8C, 0x8E56, 0xDB8D, 0x8E57, 0xDB8E, 0x8E58,\n\t0xDB8F, 0x8E5A, 0xDB90, 0x8E5B, 0xDB91, 0x8E5C, 0xDB92, 0x8E5D,\n\t0xDB93, 0x8E5E, 0xDB94, 0x8E5F, 0xDB95, 0x8E60, 0xDB96, 0x8E61,\n\t0xDB97, 0x8E62, 0xDB98, 0x8E63, 0xDB99, 0x8E64, 0xDB9A, 0x8E65,\n\t0xDB9B, 0x8E67, 0xDB9C, 0x8E68, 0xDB9D, 0x8E6A, 0xDB9E, 0x8E6B,\n\t0xDB9F, 0x8E6E, 0xDBA0, 0x8E71, 0xDBA1, 0x90B8, 0xDBA2, 0x90B0,\n\t0xDBA3, 0x90CF, 0xDBA4, 0x90C5, 0xDBA5, 0x90BE, 0xDBA6, 0x90D0,\n\t0xDBA7, 0x90C4, 0xDBA8, 0x90C7, 0xDBA9, 0x90D3, 0xDBAA, 0x90E6,\n\t0xDBAB, 0x90E2, 0xDBAC, 0x90DC, 0xDBAD, 0x90D7, 0xDBAE, 0x90DB,\n\t0xDBAF, 0x90EB, 0xDBB0, 0x90EF, 0xDBB1, 0x90FE, 0xDBB2, 0x9104,\n\t0xDBB3, 0x9122, 0xDBB4, 0x911E, 0xDBB5, 0x9123, 0xDBB6, 0x9131,\n\t0xDBB7, 0x912F, 0xDBB8, 0x9139, 0xDBB9, 0x9143, 0xDBBA, 0x9146,\n\t0xDBBB, 0x520D, 0xDBBC, 0x5942, 0xDBBD, 0x52A2, 0xDBBE, 0x52AC,\n\t0xDBBF, 0x52AD, 0xDBC0, 0x52BE, 0xDBC1, 0x54FF, 0xDBC2, 0x52D0,\n\t0xDBC3, 0x52D6, 0xDBC4, 0x52F0, 0xDBC5, 0x53DF, 0xDBC6, 0x71EE,\n\t0xDBC7, 0x77CD, 0xDBC8, 0x5EF4, 0xDBC9, 0x51F5, 0xDBCA, 0x51FC,\n\t0xDBCB, 0x9B2F, 0xDBCC, 0x53B6, 0xDBCD, 0x5F01, 0xDBCE, 0x755A,\n\t0xDBCF, 0x5DEF, 0xDBD0, 0x574C, 0xDBD1, 0x57A9, 0xDBD2, 0x57A1,\n\t0xDBD3, 0x587E, 0xDBD4, 0x58BC, 0xDBD5, 0x58C5, 0xDBD6, 0x58D1,\n\t0xDBD7, 0x5729, 0xDBD8, 0x572C, 0xDBD9, 0x572A, 0xDBDA, 0x5733,\n\t0xDBDB, 0x5739, 0xDBDC, 0x572E, 0xDBDD, 0x572F, 0xDBDE, 0x575C,\n\t0xDBDF, 0x573B, 0xDBE0, 0x5742, 0xDBE1, 0x5769, 0xDBE2, 0x5785,\n\t0xDBE3, 0x576B, 0xDBE4, 0x5786, 0xDBE5, 0x577C, 0xDBE6, 0x577B,\n\t0xDBE7, 0x5768, 0xDBE8, 0x576D, 0xDBE9, 0x5776, 0xDBEA, 0x5773,\n\t0xDBEB, 0x57AD, 0xDBEC, 0x57A4, 0xDBED, 0x578C, 0xDBEE, 0x57B2,\n\t0xDBEF, 0x57CF, 0xDBF0, 0x57A7, 0xDBF1, 0x57B4, 0xDBF2, 0x5793,\n\t0xDBF3, 0x57A0, 0xDBF4, 0x57D5, 0xDBF5, 0x57D8, 0xDBF6, 0x57DA,\n\t0xDBF7, 0x57D9, 0xDBF8, 0x57D2, 0xDBF9, 0x57B8, 0xDBFA, 0x57F4,\n\t0xDBFB, 0x57EF, 0xDBFC, 0x57F8, 0xDBFD, 0x57E4, 0xDBFE, 0x57DD,\n\t0xDC40, 0x8E73, 0xDC41, 0x8E75, 0xDC42, 0x8E77, 0xDC43, 0x8E78,\n\t0xDC44, 0x8E79, 0xDC45, 0x8E7A, 0xDC46, 0x8E7B, 0xDC47, 0x8E7D,\n\t0xDC48, 0x8E7E, 0xDC49, 0x8E80, 0xDC4A, 0x8E82, 0xDC4B, 0x8E83,\n\t0xDC4C, 0x8E84, 0xDC4D, 0x8E86, 0xDC4E, 0x8E88, 0xDC4F, 0x8E89,\n\t0xDC50, 0x8E8A, 0xDC51, 0x8E8B, 0xDC52, 0x8E8C, 0xDC53, 0x8E8D,\n\t0xDC54, 0x8E8E, 0xDC55, 0x8E91, 0xDC56, 0x8E92, 0xDC57, 0x8E93,\n\t0xDC58, 0x8E95, 0xDC59, 0x8E96, 0xDC5A, 0x8E97, 0xDC5B, 0x8E98,\n\t0xDC5C, 0x8E99, 0xDC5D, 0x8E9A, 0xDC5E, 0x8E9B, 0xDC5F, 0x8E9D,\n\t0xDC60, 0x8E9F, 0xDC61, 0x8EA0, 0xDC62, 0x8EA1, 0xDC63, 0x8EA2,\n\t0xDC64, 0x8EA3, 0xDC65, 0x8EA4, 0xDC66, 0x8EA5, 0xDC67, 0x8EA6,\n\t0xDC68, 0x8EA7, 0xDC69, 0x8EA8, 0xDC6A, 0x8EA9, 0xDC6B, 0x8EAA,\n\t0xDC6C, 0x8EAD, 0xDC6D, 0x8EAE, 0xDC6E, 0x8EB0, 0xDC6F, 0x8EB1,\n\t0xDC70, 0x8EB3, 0xDC71, 0x8EB4, 0xDC72, 0x8EB5, 0xDC73, 0x8EB6,\n\t0xDC74, 0x8EB7, 0xDC75, 0x8EB8, 0xDC76, 0x8EB9, 0xDC77, 0x8EBB,\n\t0xDC78, 0x8EBC, 0xDC79, 0x8EBD, 0xDC7A, 0x8EBE, 0xDC7B, 0x8EBF,\n\t0xDC7C, 0x8EC0, 0xDC7D, 0x8EC1, 0xDC7E, 0x8EC2, 0xDC80, 0x8EC3,\n\t0xDC81, 0x8EC4, 0xDC82, 0x8EC5, 0xDC83, 0x8EC6, 0xDC84, 0x8EC7,\n\t0xDC85, 0x8EC8, 0xDC86, 0x8EC9, 0xDC87, 0x8ECA, 0xDC88, 0x8ECB,\n\t0xDC89, 0x8ECC, 0xDC8A, 0x8ECD, 0xDC8B, 0x8ECF, 0xDC8C, 0x8ED0,\n\t0xDC8D, 0x8ED1, 0xDC8E, 0x8ED2, 0xDC8F, 0x8ED3, 0xDC90, 0x8ED4,\n\t0xDC91, 0x8ED5, 0xDC92, 0x8ED6, 0xDC93, 0x8ED7, 0xDC94, 0x8ED8,\n\t0xDC95, 0x8ED9, 0xDC96, 0x8EDA, 0xDC97, 0x8EDB, 0xDC98, 0x8EDC,\n\t0xDC99, 0x8EDD, 0xDC9A, 0x8EDE, 0xDC9B, 0x8EDF, 0xDC9C, 0x8EE0,\n\t0xDC9D, 0x8EE1, 0xDC9E, 0x8EE2, 0xDC9F, 0x8EE3, 0xDCA0, 0x8EE4,\n\t0xDCA1, 0x580B, 0xDCA2, 0x580D, 0xDCA3, 0x57FD, 0xDCA4, 0x57ED,\n\t0xDCA5, 0x5800, 0xDCA6, 0x581E, 0xDCA7, 0x5819, 0xDCA8, 0x5844,\n\t0xDCA9, 0x5820, 0xDCAA, 0x5865, 0xDCAB, 0x586C, 0xDCAC, 0x5881,\n\t0xDCAD, 0x5889, 0xDCAE, 0x589A, 0xDCAF, 0x5880, 0xDCB0, 0x99A8,\n\t0xDCB1, 0x9F19, 0xDCB2, 0x61FF, 0xDCB3, 0x8279, 0xDCB4, 0x827D,\n\t0xDCB5, 0x827F, 0xDCB6, 0x828F, 0xDCB7, 0x828A, 0xDCB8, 0x82A8,\n\t0xDCB9, 0x8284, 0xDCBA, 0x828E, 0xDCBB, 0x8291, 0xDCBC, 0x8297,\n\t0xDCBD, 0x8299, 0xDCBE, 0x82AB, 0xDCBF, 0x82B8, 0xDCC0, 0x82BE,\n\t0xDCC1, 0x82B0, 0xDCC2, 0x82C8, 0xDCC3, 0x82CA, 0xDCC4, 0x82E3,\n\t0xDCC5, 0x8298, 0xDCC6, 0x82B7, 0xDCC7, 0x82AE, 0xDCC8, 0x82CB,\n\t0xDCC9, 0x82CC, 0xDCCA, 0x82C1, 0xDCCB, 0x82A9, 0xDCCC, 0x82B4,\n\t0xDCCD, 0x82A1, 0xDCCE, 0x82AA, 0xDCCF, 0x829F, 0xDCD0, 0x82C4,\n\t0xDCD1, 0x82CE, 0xDCD2, 0x82A4, 0xDCD3, 0x82E1, 0xDCD4, 0x8309,\n\t0xDCD5, 0x82F7, 0xDCD6, 0x82E4, 0xDCD7, 0x830F, 0xDCD8, 0x8307,\n\t0xDCD9, 0x82DC, 0xDCDA, 0x82F4, 0xDCDB, 0x82D2, 0xDCDC, 0x82D8,\n\t0xDCDD, 0x830C, 0xDCDE, 0x82FB, 0xDCDF, 0x82D3, 0xDCE0, 0x8311,\n\t0xDCE1, 0x831A, 0xDCE2, 0x8306, 0xDCE3, 0x8314, 0xDCE4, 0x8315,\n\t0xDCE5, 0x82E0, 0xDCE6, 0x82D5, 0xDCE7, 0x831C, 0xDCE8, 0x8351,\n\t0xDCE9, 0x835B, 0xDCEA, 0x835C, 0xDCEB, 0x8308, 0xDCEC, 0x8392,\n\t0xDCED, 0x833C, 0xDCEE, 0x8334, 0xDCEF, 0x8331, 0xDCF0, 0x839B,\n\t0xDCF1, 0x835E, 0xDCF2, 0x832F, 0xDCF3, 0x834F, 0xDCF4, 0x8347,\n\t0xDCF5, 0x8343, 0xDCF6, 0x835F, 0xDCF7, 0x8340, 0xDCF8, 0x8317,\n\t0xDCF9, 0x8360, 0xDCFA, 0x832D, 0xDCFB, 0x833A, 0xDCFC, 0x8333,\n\t0xDCFD, 0x8366, 0xDCFE, 0x8365, 0xDD40, 0x8EE5, 0xDD41, 0x8EE6,\n\t0xDD42, 0x8EE7, 0xDD43, 0x8EE8, 0xDD44, 0x8EE9, 0xDD45, 0x8EEA,\n\t0xDD46, 0x8EEB, 0xDD47, 0x8EEC, 0xDD48, 0x8EED, 0xDD49, 0x8EEE,\n\t0xDD4A, 0x8EEF, 0xDD4B, 0x8EF0, 0xDD4C, 0x8EF1, 0xDD4D, 0x8EF2,\n\t0xDD4E, 0x8EF3, 0xDD4F, 0x8EF4, 0xDD50, 0x8EF5, 0xDD51, 0x8EF6,\n\t0xDD52, 0x8EF7, 0xDD53, 0x8EF8, 0xDD54, 0x8EF9, 0xDD55, 0x8EFA,\n\t0xDD56, 0x8EFB, 0xDD57, 0x8EFC, 0xDD58, 0x8EFD, 0xDD59, 0x8EFE,\n\t0xDD5A, 0x8EFF, 0xDD5B, 0x8F00, 0xDD5C, 0x8F01, 0xDD5D, 0x8F02,\n\t0xDD5E, 0x8F03, 0xDD5F, 0x8F04, 0xDD60, 0x8F05, 0xDD61, 0x8F06,\n\t0xDD62, 0x8F07, 0xDD63, 0x8F08, 0xDD64, 0x8F09, 0xDD65, 0x8F0A,\n\t0xDD66, 0x8F0B, 0xDD67, 0x8F0C, 0xDD68, 0x8F0D, 0xDD69, 0x8F0E,\n\t0xDD6A, 0x8F0F, 0xDD6B, 0x8F10, 0xDD6C, 0x8F11, 0xDD6D, 0x8F12,\n\t0xDD6E, 0x8F13, 0xDD6F, 0x8F14, 0xDD70, 0x8F15, 0xDD71, 0x8F16,\n\t0xDD72, 0x8F17, 0xDD73, 0x8F18, 0xDD74, 0x8F19, 0xDD75, 0x8F1A,\n\t0xDD76, 0x8F1B, 0xDD77, 0x8F1C, 0xDD78, 0x8F1D, 0xDD79, 0x8F1E,\n\t0xDD7A, 0x8F1F, 0xDD7B, 0x8F20, 0xDD7C, 0x8F21, 0xDD7D, 0x8F22,\n\t0xDD7E, 0x8F23, 0xDD80, 0x8F24, 0xDD81, 0x8F25, 0xDD82, 0x8F26,\n\t0xDD83, 0x8F27, 0xDD84, 0x8F28, 0xDD85, 0x8F29, 0xDD86, 0x8F2A,\n\t0xDD87, 0x8F2B, 0xDD88, 0x8F2C, 0xDD89, 0x8F2D, 0xDD8A, 0x8F2E,\n\t0xDD8B, 0x8F2F, 0xDD8C, 0x8F30, 0xDD8D, 0x8F31, 0xDD8E, 0x8F32,\n\t0xDD8F, 0x8F33, 0xDD90, 0x8F34, 0xDD91, 0x8F35, 0xDD92, 0x8F36,\n\t0xDD93, 0x8F37, 0xDD94, 0x8F38, 0xDD95, 0x8F39, 0xDD96, 0x8F3A,\n\t0xDD97, 0x8F3B, 0xDD98, 0x8F3C, 0xDD99, 0x8F3D, 0xDD9A, 0x8F3E,\n\t0xDD9B, 0x8F3F, 0xDD9C, 0x8F40, 0xDD9D, 0x8F41, 0xDD9E, 0x8F42,\n\t0xDD9F, 0x8F43, 0xDDA0, 0x8F44, 0xDDA1, 0x8368, 0xDDA2, 0x831B,\n\t0xDDA3, 0x8369, 0xDDA4, 0x836C, 0xDDA5, 0x836A, 0xDDA6, 0x836D,\n\t0xDDA7, 0x836E, 0xDDA8, 0x83B0, 0xDDA9, 0x8378, 0xDDAA, 0x83B3,\n\t0xDDAB, 0x83B4, 0xDDAC, 0x83A0, 0xDDAD, 0x83AA, 0xDDAE, 0x8393,\n\t0xDDAF, 0x839C, 0xDDB0, 0x8385, 0xDDB1, 0x837C, 0xDDB2, 0x83B6,\n\t0xDDB3, 0x83A9, 0xDDB4, 0x837D, 0xDDB5, 0x83B8, 0xDDB6, 0x837B,\n\t0xDDB7, 0x8398, 0xDDB8, 0x839E, 0xDDB9, 0x83A8, 0xDDBA, 0x83BA,\n\t0xDDBB, 0x83BC, 0xDDBC, 0x83C1, 0xDDBD, 0x8401, 0xDDBE, 0x83E5,\n\t0xDDBF, 0x83D8, 0xDDC0, 0x5807, 0xDDC1, 0x8418, 0xDDC2, 0x840B,\n\t0xDDC3, 0x83DD, 0xDDC4, 0x83FD, 0xDDC5, 0x83D6, 0xDDC6, 0x841C,\n\t0xDDC7, 0x8438, 0xDDC8, 0x8411, 0xDDC9, 0x8406, 0xDDCA, 0x83D4,\n\t0xDDCB, 0x83DF, 0xDDCC, 0x840F, 0xDDCD, 0x8403, 0xDDCE, 0x83F8,\n\t0xDDCF, 0x83F9, 0xDDD0, 0x83EA, 0xDDD1, 0x83C5, 0xDDD2, 0x83C0,\n\t0xDDD3, 0x8426, 0xDDD4, 0x83F0, 0xDDD5, 0x83E1, 0xDDD6, 0x845C,\n\t0xDDD7, 0x8451, 0xDDD8, 0x845A, 0xDDD9, 0x8459, 0xDDDA, 0x8473,\n\t0xDDDB, 0x8487, 0xDDDC, 0x8488, 0xDDDD, 0x847A, 0xDDDE, 0x8489,\n\t0xDDDF, 0x8478, 0xDDE0, 0x843C, 0xDDE1, 0x8446, 0xDDE2, 0x8469,\n\t0xDDE3, 0x8476, 0xDDE4, 0x848C, 0xDDE5, 0x848E, 0xDDE6, 0x8431,\n\t0xDDE7, 0x846D, 0xDDE8, 0x84C1, 0xDDE9, 0x84CD, 0xDDEA, 0x84D0,\n\t0xDDEB, 0x84E6, 0xDDEC, 0x84BD, 0xDDED, 0x84D3, 0xDDEE, 0x84CA,\n\t0xDDEF, 0x84BF, 0xDDF0, 0x84BA, 0xDDF1, 0x84E0, 0xDDF2, 0x84A1,\n\t0xDDF3, 0x84B9, 0xDDF4, 0x84B4, 0xDDF5, 0x8497, 0xDDF6, 0x84E5,\n\t0xDDF7, 0x84E3, 0xDDF8, 0x850C, 0xDDF9, 0x750D, 0xDDFA, 0x8538,\n\t0xDDFB, 0x84F0, 0xDDFC, 0x8539, 0xDDFD, 0x851F, 0xDDFE, 0x853A,\n\t0xDE40, 0x8F45, 0xDE41, 0x8F46, 0xDE42, 0x8F47, 0xDE43, 0x8F48,\n\t0xDE44, 0x8F49, 0xDE45, 0x8F4A, 0xDE46, 0x8F4B, 0xDE47, 0x8F4C,\n\t0xDE48, 0x8F4D, 0xDE49, 0x8F4E, 0xDE4A, 0x8F4F, 0xDE4B, 0x8F50,\n\t0xDE4C, 0x8F51, 0xDE4D, 0x8F52, 0xDE4E, 0x8F53, 0xDE4F, 0x8F54,\n\t0xDE50, 0x8F55, 0xDE51, 0x8F56, 0xDE52, 0x8F57, 0xDE53, 0x8F58,\n\t0xDE54, 0x8F59, 0xDE55, 0x8F5A, 0xDE56, 0x8F5B, 0xDE57, 0x8F5C,\n\t0xDE58, 0x8F5D, 0xDE59, 0x8F5E, 0xDE5A, 0x8F5F, 0xDE5B, 0x8F60,\n\t0xDE5C, 0x8F61, 0xDE5D, 0x8F62, 0xDE5E, 0x8F63, 0xDE5F, 0x8F64,\n\t0xDE60, 0x8F65, 0xDE61, 0x8F6A, 0xDE62, 0x8F80, 0xDE63, 0x8F8C,\n\t0xDE64, 0x8F92, 0xDE65, 0x8F9D, 0xDE66, 0x8FA0, 0xDE67, 0x8FA1,\n\t0xDE68, 0x8FA2, 0xDE69, 0x8FA4, 0xDE6A, 0x8FA5, 0xDE6B, 0x8FA6,\n\t0xDE6C, 0x8FA7, 0xDE6D, 0x8FAA, 0xDE6E, 0x8FAC, 0xDE6F, 0x8FAD,\n\t0xDE70, 0x8FAE, 0xDE71, 0x8FAF, 0xDE72, 0x8FB2, 0xDE73, 0x8FB3,\n\t0xDE74, 0x8FB4, 0xDE75, 0x8FB5, 0xDE76, 0x8FB7, 0xDE77, 0x8FB8,\n\t0xDE78, 0x8FBA, 0xDE79, 0x8FBB, 0xDE7A, 0x8FBC, 0xDE7B, 0x8FBF,\n\t0xDE7C, 0x8FC0, 0xDE7D, 0x8FC3, 0xDE7E, 0x8FC6, 0xDE80, 0x8FC9,\n\t0xDE81, 0x8FCA, 0xDE82, 0x8FCB, 0xDE83, 0x8FCC, 0xDE84, 0x8FCD,\n\t0xDE85, 0x8FCF, 0xDE86, 0x8FD2, 0xDE87, 0x8FD6, 0xDE88, 0x8FD7,\n\t0xDE89, 0x8FDA, 0xDE8A, 0x8FE0, 0xDE8B, 0x8FE1, 0xDE8C, 0x8FE3,\n\t0xDE8D, 0x8FE7, 0xDE8E, 0x8FEC, 0xDE8F, 0x8FEF, 0xDE90, 0x8FF1,\n\t0xDE91, 0x8FF2, 0xDE92, 0x8FF4, 0xDE93, 0x8FF5, 0xDE94, 0x8FF6,\n\t0xDE95, 0x8FFA, 0xDE96, 0x8FFB, 0xDE97, 0x8FFC, 0xDE98, 0x8FFE,\n\t0xDE99, 0x8FFF, 0xDE9A, 0x9007, 0xDE9B, 0x9008, 0xDE9C, 0x900C,\n\t0xDE9D, 0x900E, 0xDE9E, 0x9013, 0xDE9F, 0x9015, 0xDEA0, 0x9018,\n\t0xDEA1, 0x8556, 0xDEA2, 0x853B, 0xDEA3, 0x84FF, 0xDEA4, 0x84FC,\n\t0xDEA5, 0x8559, 0xDEA6, 0x8548, 0xDEA7, 0x8568, 0xDEA8, 0x8564,\n\t0xDEA9, 0x855E, 0xDEAA, 0x857A, 0xDEAB, 0x77A2, 0xDEAC, 0x8543,\n\t0xDEAD, 0x8572, 0xDEAE, 0x857B, 0xDEAF, 0x85A4, 0xDEB0, 0x85A8,\n\t0xDEB1, 0x8587, 0xDEB2, 0x858F, 0xDEB3, 0x8579, 0xDEB4, 0x85AE,\n\t0xDEB5, 0x859C, 0xDEB6, 0x8585, 0xDEB7, 0x85B9, 0xDEB8, 0x85B7,\n\t0xDEB9, 0x85B0, 0xDEBA, 0x85D3, 0xDEBB, 0x85C1, 0xDEBC, 0x85DC,\n\t0xDEBD, 0x85FF, 0xDEBE, 0x8627, 0xDEBF, 0x8605, 0xDEC0, 0x8629,\n\t0xDEC1, 0x8616, 0xDEC2, 0x863C, 0xDEC3, 0x5EFE, 0xDEC4, 0x5F08,\n\t0xDEC5, 0x593C, 0xDEC6, 0x5941, 0xDEC7, 0x8037, 0xDEC8, 0x5955,\n\t0xDEC9, 0x595A, 0xDECA, 0x5958, 0xDECB, 0x530F, 0xDECC, 0x5C22,\n\t0xDECD, 0x5C25, 0xDECE, 0x5C2C, 0xDECF, 0x5C34, 0xDED0, 0x624C,\n\t0xDED1, 0x626A, 0xDED2, 0x629F, 0xDED3, 0x62BB, 0xDED4, 0x62CA,\n\t0xDED5, 0x62DA, 0xDED6, 0x62D7, 0xDED7, 0x62EE, 0xDED8, 0x6322,\n\t0xDED9, 0x62F6, 0xDEDA, 0x6339, 0xDEDB, 0x634B, 0xDEDC, 0x6343,\n\t0xDEDD, 0x63AD, 0xDEDE, 0x63F6, 0xDEDF, 0x6371, 0xDEE0, 0x637A,\n\t0xDEE1, 0x638E, 0xDEE2, 0x63B4, 0xDEE3, 0x636D, 0xDEE4, 0x63AC,\n\t0xDEE5, 0x638A, 0xDEE6, 0x6369, 0xDEE7, 0x63AE, 0xDEE8, 0x63BC,\n\t0xDEE9, 0x63F2, 0xDEEA, 0x63F8, 0xDEEB, 0x63E0, 0xDEEC, 0x63FF,\n\t0xDEED, 0x63C4, 0xDEEE, 0x63DE, 0xDEEF, 0x63CE, 0xDEF0, 0x6452,\n\t0xDEF1, 0x63C6, 0xDEF2, 0x63BE, 0xDEF3, 0x6445, 0xDEF4, 0x6441,\n\t0xDEF5, 0x640B, 0xDEF6, 0x641B, 0xDEF7, 0x6420, 0xDEF8, 0x640C,\n\t0xDEF9, 0x6426, 0xDEFA, 0x6421, 0xDEFB, 0x645E, 0xDEFC, 0x6484,\n\t0xDEFD, 0x646D, 0xDEFE, 0x6496, 0xDF40, 0x9019, 0xDF41, 0x901C,\n\t0xDF42, 0x9023, 0xDF43, 0x9024, 0xDF44, 0x9025, 0xDF45, 0x9027,\n\t0xDF46, 0x9028, 0xDF47, 0x9029, 0xDF48, 0x902A, 0xDF49, 0x902B,\n\t0xDF4A, 0x902C, 0xDF4B, 0x9030, 0xDF4C, 0x9031, 0xDF4D, 0x9032,\n\t0xDF4E, 0x9033, 0xDF4F, 0x9034, 0xDF50, 0x9037, 0xDF51, 0x9039,\n\t0xDF52, 0x903A, 0xDF53, 0x903D, 0xDF54, 0x903F, 0xDF55, 0x9040,\n\t0xDF56, 0x9043, 0xDF57, 0x9045, 0xDF58, 0x9046, 0xDF59, 0x9048,\n\t0xDF5A, 0x9049, 0xDF5B, 0x904A, 0xDF5C, 0x904B, 0xDF5D, 0x904C,\n\t0xDF5E, 0x904E, 0xDF5F, 0x9054, 0xDF60, 0x9055, 0xDF61, 0x9056,\n\t0xDF62, 0x9059, 0xDF63, 0x905A, 0xDF64, 0x905C, 0xDF65, 0x905D,\n\t0xDF66, 0x905E, 0xDF67, 0x905F, 0xDF68, 0x9060, 0xDF69, 0x9061,\n\t0xDF6A, 0x9064, 0xDF6B, 0x9066, 0xDF6C, 0x9067, 0xDF6D, 0x9069,\n\t0xDF6E, 0x906A, 0xDF6F, 0x906B, 0xDF70, 0x906C, 0xDF71, 0x906F,\n\t0xDF72, 0x9070, 0xDF73, 0x9071, 0xDF74, 0x9072, 0xDF75, 0x9073,\n\t0xDF76, 0x9076, 0xDF77, 0x9077, 0xDF78, 0x9078, 0xDF79, 0x9079,\n\t0xDF7A, 0x907A, 0xDF7B, 0x907B, 0xDF7C, 0x907C, 0xDF7D, 0x907E,\n\t0xDF7E, 0x9081, 0xDF80, 0x9084, 0xDF81, 0x9085, 0xDF82, 0x9086,\n\t0xDF83, 0x9087, 0xDF84, 0x9089, 0xDF85, 0x908A, 0xDF86, 0x908C,\n\t0xDF87, 0x908D, 0xDF88, 0x908E, 0xDF89, 0x908F, 0xDF8A, 0x9090,\n\t0xDF8B, 0x9092, 0xDF8C, 0x9094, 0xDF8D, 0x9096, 0xDF8E, 0x9098,\n\t0xDF8F, 0x909A, 0xDF90, 0x909C, 0xDF91, 0x909E, 0xDF92, 0x909F,\n\t0xDF93, 0x90A0, 0xDF94, 0x90A4, 0xDF95, 0x90A5, 0xDF96, 0x90A7,\n\t0xDF97, 0x90A8, 0xDF98, 0x90A9, 0xDF99, 0x90AB, 0xDF9A, 0x90AD,\n\t0xDF9B, 0x90B2, 0xDF9C, 0x90B7, 0xDF9D, 0x90BC, 0xDF9E, 0x90BD,\n\t0xDF9F, 0x90BF, 0xDFA0, 0x90C0, 0xDFA1, 0x647A, 0xDFA2, 0x64B7,\n\t0xDFA3, 0x64B8, 0xDFA4, 0x6499, 0xDFA5, 0x64BA, 0xDFA6, 0x64C0,\n\t0xDFA7, 0x64D0, 0xDFA8, 0x64D7, 0xDFA9, 0x64E4, 0xDFAA, 0x64E2,\n\t0xDFAB, 0x6509, 0xDFAC, 0x6525, 0xDFAD, 0x652E, 0xDFAE, 0x5F0B,\n\t0xDFAF, 0x5FD2, 0xDFB0, 0x7519, 0xDFB1, 0x5F11, 0xDFB2, 0x535F,\n\t0xDFB3, 0x53F1, 0xDFB4, 0x53FD, 0xDFB5, 0x53E9, 0xDFB6, 0x53E8,\n\t0xDFB7, 0x53FB, 0xDFB8, 0x5412, 0xDFB9, 0x5416, 0xDFBA, 0x5406,\n\t0xDFBB, 0x544B, 0xDFBC, 0x5452, 0xDFBD, 0x5453, 0xDFBE, 0x5454,\n\t0xDFBF, 0x5456, 0xDFC0, 0x5443, 0xDFC1, 0x5421, 0xDFC2, 0x5457,\n\t0xDFC3, 0x5459, 0xDFC4, 0x5423, 0xDFC5, 0x5432, 0xDFC6, 0x5482,\n\t0xDFC7, 0x5494, 0xDFC8, 0x5477, 0xDFC9, 0x5471, 0xDFCA, 0x5464,\n\t0xDFCB, 0x549A, 0xDFCC, 0x549B, 0xDFCD, 0x5484, 0xDFCE, 0x5476,\n\t0xDFCF, 0x5466, 0xDFD0, 0x549D, 0xDFD1, 0x54D0, 0xDFD2, 0x54AD,\n\t0xDFD3, 0x54C2, 0xDFD4, 0x54B4, 0xDFD5, 0x54D2, 0xDFD6, 0x54A7,\n\t0xDFD7, 0x54A6, 0xDFD8, 0x54D3, 0xDFD9, 0x54D4, 0xDFDA, 0x5472,\n\t0xDFDB, 0x54A3, 0xDFDC, 0x54D5, 0xDFDD, 0x54BB, 0xDFDE, 0x54BF,\n\t0xDFDF, 0x54CC, 0xDFE0, 0x54D9, 0xDFE1, 0x54DA, 0xDFE2, 0x54DC,\n\t0xDFE3, 0x54A9, 0xDFE4, 0x54AA, 0xDFE5, 0x54A4, 0xDFE6, 0x54DD,\n\t0xDFE7, 0x54CF, 0xDFE8, 0x54DE, 0xDFE9, 0x551B, 0xDFEA, 0x54E7,\n\t0xDFEB, 0x5520, 0xDFEC, 0x54FD, 0xDFED, 0x5514, 0xDFEE, 0x54F3,\n\t0xDFEF, 0x5522, 0xDFF0, 0x5523, 0xDFF1, 0x550F, 0xDFF2, 0x5511,\n\t0xDFF3, 0x5527, 0xDFF4, 0x552A, 0xDFF5, 0x5567, 0xDFF6, 0x558F,\n\t0xDFF7, 0x55B5, 0xDFF8, 0x5549, 0xDFF9, 0x556D, 0xDFFA, 0x5541,\n\t0xDFFB, 0x5555, 0xDFFC, 0x553F, 0xDFFD, 0x5550, 0xDFFE, 0x553C,\n\t0xE040, 0x90C2, 0xE041, 0x90C3, 0xE042, 0x90C6, 0xE043, 0x90C8,\n\t0xE044, 0x90C9, 0xE045, 0x90CB, 0xE046, 0x90CC, 0xE047, 0x90CD,\n\t0xE048, 0x90D2, 0xE049, 0x90D4, 0xE04A, 0x90D5, 0xE04B, 0x90D6,\n\t0xE04C, 0x90D8, 0xE04D, 0x90D9, 0xE04E, 0x90DA, 0xE04F, 0x90DE,\n\t0xE050, 0x90DF, 0xE051, 0x90E0, 0xE052, 0x90E3, 0xE053, 0x90E4,\n\t0xE054, 0x90E5, 0xE055, 0x90E9, 0xE056, 0x90EA, 0xE057, 0x90EC,\n\t0xE058, 0x90EE, 0xE059, 0x90F0, 0xE05A, 0x90F1, 0xE05B, 0x90F2,\n\t0xE05C, 0x90F3, 0xE05D, 0x90F5, 0xE05E, 0x90F6, 0xE05F, 0x90F7,\n\t0xE060, 0x90F9, 0xE061, 0x90FA, 0xE062, 0x90FB, 0xE063, 0x90FC,\n\t0xE064, 0x90FF, 0xE065, 0x9100, 0xE066, 0x9101, 0xE067, 0x9103,\n\t0xE068, 0x9105, 0xE069, 0x9106, 0xE06A, 0x9107, 0xE06B, 0x9108,\n\t0xE06C, 0x9109, 0xE06D, 0x910A, 0xE06E, 0x910B, 0xE06F, 0x910C,\n\t0xE070, 0x910D, 0xE071, 0x910E, 0xE072, 0x910F, 0xE073, 0x9110,\n\t0xE074, 0x9111, 0xE075, 0x9112, 0xE076, 0x9113, 0xE077, 0x9114,\n\t0xE078, 0x9115, 0xE079, 0x9116, 0xE07A, 0x9117, 0xE07B, 0x9118,\n\t0xE07C, 0x911A, 0xE07D, 0x911B, 0xE07E, 0x911C, 0xE080, 0x911D,\n\t0xE081, 0x911F, 0xE082, 0x9120, 0xE083, 0x9121, 0xE084, 0x9124,\n\t0xE085, 0x9125, 0xE086, 0x9126, 0xE087, 0x9127, 0xE088, 0x9128,\n\t0xE089, 0x9129, 0xE08A, 0x912A, 0xE08B, 0x912B, 0xE08C, 0x912C,\n\t0xE08D, 0x912D, 0xE08E, 0x912E, 0xE08F, 0x9130, 0xE090, 0x9132,\n\t0xE091, 0x9133, 0xE092, 0x9134, 0xE093, 0x9135, 0xE094, 0x9136,\n\t0xE095, 0x9137, 0xE096, 0x9138, 0xE097, 0x913A, 0xE098, 0x913B,\n\t0xE099, 0x913C, 0xE09A, 0x913D, 0xE09B, 0x913E, 0xE09C, 0x913F,\n\t0xE09D, 0x9140, 0xE09E, 0x9141, 0xE09F, 0x9142, 0xE0A0, 0x9144,\n\t0xE0A1, 0x5537, 0xE0A2, 0x5556, 0xE0A3, 0x5575, 0xE0A4, 0x5576,\n\t0xE0A5, 0x5577, 0xE0A6, 0x5533, 0xE0A7, 0x5530, 0xE0A8, 0x555C,\n\t0xE0A9, 0x558B, 0xE0AA, 0x55D2, 0xE0AB, 0x5583, 0xE0AC, 0x55B1,\n\t0xE0AD, 0x55B9, 0xE0AE, 0x5588, 0xE0AF, 0x5581, 0xE0B0, 0x559F,\n\t0xE0B1, 0x557E, 0xE0B2, 0x55D6, 0xE0B3, 0x5591, 0xE0B4, 0x557B,\n\t0xE0B5, 0x55DF, 0xE0B6, 0x55BD, 0xE0B7, 0x55BE, 0xE0B8, 0x5594,\n\t0xE0B9, 0x5599, 0xE0BA, 0x55EA, 0xE0BB, 0x55F7, 0xE0BC, 0x55C9,\n\t0xE0BD, 0x561F, 0xE0BE, 0x55D1, 0xE0BF, 0x55EB, 0xE0C0, 0x55EC,\n\t0xE0C1, 0x55D4, 0xE0C2, 0x55E6, 0xE0C3, 0x55DD, 0xE0C4, 0x55C4,\n\t0xE0C5, 0x55EF, 0xE0C6, 0x55E5, 0xE0C7, 0x55F2, 0xE0C8, 0x55F3,\n\t0xE0C9, 0x55CC, 0xE0CA, 0x55CD, 0xE0CB, 0x55E8, 0xE0CC, 0x55F5,\n\t0xE0CD, 0x55E4, 0xE0CE, 0x8F94, 0xE0CF, 0x561E, 0xE0D0, 0x5608,\n\t0xE0D1, 0x560C, 0xE0D2, 0x5601, 0xE0D3, 0x5624, 0xE0D4, 0x5623,\n\t0xE0D5, 0x55FE, 0xE0D6, 0x5600, 0xE0D7, 0x5627, 0xE0D8, 0x562D,\n\t0xE0D9, 0x5658, 0xE0DA, 0x5639, 0xE0DB, 0x5657, 0xE0DC, 0x562C,\n\t0xE0DD, 0x564D, 0xE0DE, 0x5662, 0xE0DF, 0x5659, 0xE0E0, 0x565C,\n\t0xE0E1, 0x564C, 0xE0E2, 0x5654, 0xE0E3, 0x5686, 0xE0E4, 0x5664,\n\t0xE0E5, 0x5671, 0xE0E6, 0x566B, 0xE0E7, 0x567B, 0xE0E8, 0x567C,\n\t0xE0E9, 0x5685, 0xE0EA, 0x5693, 0xE0EB, 0x56AF, 0xE0EC, 0x56D4,\n\t0xE0ED, 0x56D7, 0xE0EE, 0x56DD, 0xE0EF, 0x56E1, 0xE0F0, 0x56F5,\n\t0xE0F1, 0x56EB, 0xE0F2, 0x56F9, 0xE0F3, 0x56FF, 0xE0F4, 0x5704,\n\t0xE0F5, 0x570A, 0xE0F6, 0x5709, 0xE0F7, 0x571C, 0xE0F8, 0x5E0F,\n\t0xE0F9, 0x5E19, 0xE0FA, 0x5E14, 0xE0FB, 0x5E11, 0xE0FC, 0x5E31,\n\t0xE0FD, 0x5E3B, 0xE0FE, 0x5E3C, 0xE140, 0x9145, 0xE141, 0x9147,\n\t0xE142, 0x9148, 0xE143, 0x9151, 0xE144, 0x9153, 0xE145, 0x9154,\n\t0xE146, 0x9155, 0xE147, 0x9156, 0xE148, 0x9158, 0xE149, 0x9159,\n\t0xE14A, 0x915B, 0xE14B, 0x915C, 0xE14C, 0x915F, 0xE14D, 0x9160,\n\t0xE14E, 0x9166, 0xE14F, 0x9167, 0xE150, 0x9168, 0xE151, 0x916B,\n\t0xE152, 0x916D, 0xE153, 0x9173, 0xE154, 0x917A, 0xE155, 0x917B,\n\t0xE156, 0x917C, 0xE157, 0x9180, 0xE158, 0x9181, 0xE159, 0x9182,\n\t0xE15A, 0x9183, 0xE15B, 0x9184, 0xE15C, 0x9186, 0xE15D, 0x9188,\n\t0xE15E, 0x918A, 0xE15F, 0x918E, 0xE160, 0x918F, 0xE161, 0x9193,\n\t0xE162, 0x9194, 0xE163, 0x9195, 0xE164, 0x9196, 0xE165, 0x9197,\n\t0xE166, 0x9198, 0xE167, 0x9199, 0xE168, 0x919C, 0xE169, 0x919D,\n\t0xE16A, 0x919E, 0xE16B, 0x919F, 0xE16C, 0x91A0, 0xE16D, 0x91A1,\n\t0xE16E, 0x91A4, 0xE16F, 0x91A5, 0xE170, 0x91A6, 0xE171, 0x91A7,\n\t0xE172, 0x91A8, 0xE173, 0x91A9, 0xE174, 0x91AB, 0xE175, 0x91AC,\n\t0xE176, 0x91B0, 0xE177, 0x91B1, 0xE178, 0x91B2, 0xE179, 0x91B3,\n\t0xE17A, 0x91B6, 0xE17B, 0x91B7, 0xE17C, 0x91B8, 0xE17D, 0x91B9,\n\t0xE17E, 0x91BB, 0xE180, 0x91BC, 0xE181, 0x91BD, 0xE182, 0x91BE,\n\t0xE183, 0x91BF, 0xE184, 0x91C0, 0xE185, 0x91C1, 0xE186, 0x91C2,\n\t0xE187, 0x91C3, 0xE188, 0x91C4, 0xE189, 0x91C5, 0xE18A, 0x91C6,\n\t0xE18B, 0x91C8, 0xE18C, 0x91CB, 0xE18D, 0x91D0, 0xE18E, 0x91D2,\n\t0xE18F, 0x91D3, 0xE190, 0x91D4, 0xE191, 0x91D5, 0xE192, 0x91D6,\n\t0xE193, 0x91D7, 0xE194, 0x91D8, 0xE195, 0x91D9, 0xE196, 0x91DA,\n\t0xE197, 0x91DB, 0xE198, 0x91DD, 0xE199, 0x91DE, 0xE19A, 0x91DF,\n\t0xE19B, 0x91E0, 0xE19C, 0x91E1, 0xE19D, 0x91E2, 0xE19E, 0x91E3,\n\t0xE19F, 0x91E4, 0xE1A0, 0x91E5, 0xE1A1, 0x5E37, 0xE1A2, 0x5E44,\n\t0xE1A3, 0x5E54, 0xE1A4, 0x5E5B, 0xE1A5, 0x5E5E, 0xE1A6, 0x5E61,\n\t0xE1A7, 0x5C8C, 0xE1A8, 0x5C7A, 0xE1A9, 0x5C8D, 0xE1AA, 0x5C90,\n\t0xE1AB, 0x5C96, 0xE1AC, 0x5C88, 0xE1AD, 0x5C98, 0xE1AE, 0x5C99,\n\t0xE1AF, 0x5C91, 0xE1B0, 0x5C9A, 0xE1B1, 0x5C9C, 0xE1B2, 0x5CB5,\n\t0xE1B3, 0x5CA2, 0xE1B4, 0x5CBD, 0xE1B5, 0x5CAC, 0xE1B6, 0x5CAB,\n\t0xE1B7, 0x5CB1, 0xE1B8, 0x5CA3, 0xE1B9, 0x5CC1, 0xE1BA, 0x5CB7,\n\t0xE1BB, 0x5CC4, 0xE1BC, 0x5CD2, 0xE1BD, 0x5CE4, 0xE1BE, 0x5CCB,\n\t0xE1BF, 0x5CE5, 0xE1C0, 0x5D02, 0xE1C1, 0x5D03, 0xE1C2, 0x5D27,\n\t0xE1C3, 0x5D26, 0xE1C4, 0x5D2E, 0xE1C5, 0x5D24, 0xE1C6, 0x5D1E,\n\t0xE1C7, 0x5D06, 0xE1C8, 0x5D1B, 0xE1C9, 0x5D58, 0xE1CA, 0x5D3E,\n\t0xE1CB, 0x5D34, 0xE1CC, 0x5D3D, 0xE1CD, 0x5D6C, 0xE1CE, 0x5D5B,\n\t0xE1CF, 0x5D6F, 0xE1D0, 0x5D5D, 0xE1D1, 0x5D6B, 0xE1D2, 0x5D4B,\n\t0xE1D3, 0x5D4A, 0xE1D4, 0x5D69, 0xE1D5, 0x5D74, 0xE1D6, 0x5D82,\n\t0xE1D7, 0x5D99, 0xE1D8, 0x5D9D, 0xE1D9, 0x8C73, 0xE1DA, 0x5DB7,\n\t0xE1DB, 0x5DC5, 0xE1DC, 0x5F73, 0xE1DD, 0x5F77, 0xE1DE, 0x5F82,\n\t0xE1DF, 0x5F87, 0xE1E0, 0x5F89, 0xE1E1, 0x5F8C, 0xE1E2, 0x5F95,\n\t0xE1E3, 0x5F99, 0xE1E4, 0x5F9C, 0xE1E5, 0x5FA8, 0xE1E6, 0x5FAD,\n\t0xE1E7, 0x5FB5, 0xE1E8, 0x5FBC, 0xE1E9, 0x8862, 0xE1EA, 0x5F61,\n\t0xE1EB, 0x72AD, 0xE1EC, 0x72B0, 0xE1ED, 0x72B4, 0xE1EE, 0x72B7,\n\t0xE1EF, 0x72B8, 0xE1F0, 0x72C3, 0xE1F1, 0x72C1, 0xE1F2, 0x72CE,\n\t0xE1F3, 0x72CD, 0xE1F4, 0x72D2, 0xE1F5, 0x72E8, 0xE1F6, 0x72EF,\n\t0xE1F7, 0x72E9, 0xE1F8, 0x72F2, 0xE1F9, 0x72F4, 0xE1FA, 0x72F7,\n\t0xE1FB, 0x7301, 0xE1FC, 0x72F3, 0xE1FD, 0x7303, 0xE1FE, 0x72FA,\n\t0xE240, 0x91E6, 0xE241, 0x91E7, 0xE242, 0x91E8, 0xE243, 0x91E9,\n\t0xE244, 0x91EA, 0xE245, 0x91EB, 0xE246, 0x91EC, 0xE247, 0x91ED,\n\t0xE248, 0x91EE, 0xE249, 0x91EF, 0xE24A, 0x91F0, 0xE24B, 0x91F1,\n\t0xE24C, 0x91F2, 0xE24D, 0x91F3, 0xE24E, 0x91F4, 0xE24F, 0x91F5,\n\t0xE250, 0x91F6, 0xE251, 0x91F7, 0xE252, 0x91F8, 0xE253, 0x91F9,\n\t0xE254, 0x91FA, 0xE255, 0x91FB, 0xE256, 0x91FC, 0xE257, 0x91FD,\n\t0xE258, 0x91FE, 0xE259, 0x91FF, 0xE25A, 0x9200, 0xE25B, 0x9201,\n\t0xE25C, 0x9202, 0xE25D, 0x9203, 0xE25E, 0x9204, 0xE25F, 0x9205,\n\t0xE260, 0x9206, 0xE261, 0x9207, 0xE262, 0x9208, 0xE263, 0x9209,\n\t0xE264, 0x920A, 0xE265, 0x920B, 0xE266, 0x920C, 0xE267, 0x920D,\n\t0xE268, 0x920E, 0xE269, 0x920F, 0xE26A, 0x9210, 0xE26B, 0x9211,\n\t0xE26C, 0x9212, 0xE26D, 0x9213, 0xE26E, 0x9214, 0xE26F, 0x9215,\n\t0xE270, 0x9216, 0xE271, 0x9217, 0xE272, 0x9218, 0xE273, 0x9219,\n\t0xE274, 0x921A, 0xE275, 0x921B, 0xE276, 0x921C, 0xE277, 0x921D,\n\t0xE278, 0x921E, 0xE279, 0x921F, 0xE27A, 0x9220, 0xE27B, 0x9221,\n\t0xE27C, 0x9222, 0xE27D, 0x9223, 0xE27E, 0x9224, 0xE280, 0x9225,\n\t0xE281, 0x9226, 0xE282, 0x9227, 0xE283, 0x9228, 0xE284, 0x9229,\n\t0xE285, 0x922A, 0xE286, 0x922B, 0xE287, 0x922C, 0xE288, 0x922D,\n\t0xE289, 0x922E, 0xE28A, 0x922F, 0xE28B, 0x9230, 0xE28C, 0x9231,\n\t0xE28D, 0x9232, 0xE28E, 0x9233, 0xE28F, 0x9234, 0xE290, 0x9235,\n\t0xE291, 0x9236, 0xE292, 0x9237, 0xE293, 0x9238, 0xE294, 0x9239,\n\t0xE295, 0x923A, 0xE296, 0x923B, 0xE297, 0x923C, 0xE298, 0x923D,\n\t0xE299, 0x923E, 0xE29A, 0x923F, 0xE29B, 0x9240, 0xE29C, 0x9241,\n\t0xE29D, 0x9242, 0xE29E, 0x9243, 0xE29F, 0x9244, 0xE2A0, 0x9245,\n\t0xE2A1, 0x72FB, 0xE2A2, 0x7317, 0xE2A3, 0x7313, 0xE2A4, 0x7321,\n\t0xE2A5, 0x730A, 0xE2A6, 0x731E, 0xE2A7, 0x731D, 0xE2A8, 0x7315,\n\t0xE2A9, 0x7322, 0xE2AA, 0x7339, 0xE2AB, 0x7325, 0xE2AC, 0x732C,\n\t0xE2AD, 0x7338, 0xE2AE, 0x7331, 0xE2AF, 0x7350, 0xE2B0, 0x734D,\n\t0xE2B1, 0x7357, 0xE2B2, 0x7360, 0xE2B3, 0x736C, 0xE2B4, 0x736F,\n\t0xE2B5, 0x737E, 0xE2B6, 0x821B, 0xE2B7, 0x5925, 0xE2B8, 0x98E7,\n\t0xE2B9, 0x5924, 0xE2BA, 0x5902, 0xE2BB, 0x9963, 0xE2BC, 0x9967,\n\t0xE2BD, 0x9968, 0xE2BE, 0x9969, 0xE2BF, 0x996A, 0xE2C0, 0x996B,\n\t0xE2C1, 0x996C, 0xE2C2, 0x9974, 0xE2C3, 0x9977, 0xE2C4, 0x997D,\n\t0xE2C5, 0x9980, 0xE2C6, 0x9984, 0xE2C7, 0x9987, 0xE2C8, 0x998A,\n\t0xE2C9, 0x998D, 0xE2CA, 0x9990, 0xE2CB, 0x9991, 0xE2CC, 0x9993,\n\t0xE2CD, 0x9994, 0xE2CE, 0x9995, 0xE2CF, 0x5E80, 0xE2D0, 0x5E91,\n\t0xE2D1, 0x5E8B, 0xE2D2, 0x5E96, 0xE2D3, 0x5EA5, 0xE2D4, 0x5EA0,\n\t0xE2D5, 0x5EB9, 0xE2D6, 0x5EB5, 0xE2D7, 0x5EBE, 0xE2D8, 0x5EB3,\n\t0xE2D9, 0x8D53, 0xE2DA, 0x5ED2, 0xE2DB, 0x5ED1, 0xE2DC, 0x5EDB,\n\t0xE2DD, 0x5EE8, 0xE2DE, 0x5EEA, 0xE2DF, 0x81BA, 0xE2E0, 0x5FC4,\n\t0xE2E1, 0x5FC9, 0xE2E2, 0x5FD6, 0xE2E3, 0x5FCF, 0xE2E4, 0x6003,\n\t0xE2E5, 0x5FEE, 0xE2E6, 0x6004, 0xE2E7, 0x5FE1, 0xE2E8, 0x5FE4,\n\t0xE2E9, 0x5FFE, 0xE2EA, 0x6005, 0xE2EB, 0x6006, 0xE2EC, 0x5FEA,\n\t0xE2ED, 0x5FED, 0xE2EE, 0x5FF8, 0xE2EF, 0x6019, 0xE2F0, 0x6035,\n\t0xE2F1, 0x6026, 0xE2F2, 0x601B, 0xE2F3, 0x600F, 0xE2F4, 0x600D,\n\t0xE2F5, 0x6029, 0xE2F6, 0x602B, 0xE2F7, 0x600A, 0xE2F8, 0x603F,\n\t0xE2F9, 0x6021, 0xE2FA, 0x6078, 0xE2FB, 0x6079, 0xE2FC, 0x607B,\n\t0xE2FD, 0x607A, 0xE2FE, 0x6042, 0xE340, 0x9246, 0xE341, 0x9247,\n\t0xE342, 0x9248, 0xE343, 0x9249, 0xE344, 0x924A, 0xE345, 0x924B,\n\t0xE346, 0x924C, 0xE347, 0x924D, 0xE348, 0x924E, 0xE349, 0x924F,\n\t0xE34A, 0x9250, 0xE34B, 0x9251, 0xE34C, 0x9252, 0xE34D, 0x9253,\n\t0xE34E, 0x9254, 0xE34F, 0x9255, 0xE350, 0x9256, 0xE351, 0x9257,\n\t0xE352, 0x9258, 0xE353, 0x9259, 0xE354, 0x925A, 0xE355, 0x925B,\n\t0xE356, 0x925C, 0xE357, 0x925D, 0xE358, 0x925E, 0xE359, 0x925F,\n\t0xE35A, 0x9260, 0xE35B, 0x9261, 0xE35C, 0x9262, 0xE35D, 0x9263,\n\t0xE35E, 0x9264, 0xE35F, 0x9265, 0xE360, 0x9266, 0xE361, 0x9267,\n\t0xE362, 0x9268, 0xE363, 0x9269, 0xE364, 0x926A, 0xE365, 0x926B,\n\t0xE366, 0x926C, 0xE367, 0x926D, 0xE368, 0x926E, 0xE369, 0x926F,\n\t0xE36A, 0x9270, 0xE36B, 0x9271, 0xE36C, 0x9272, 0xE36D, 0x9273,\n\t0xE36E, 0x9275, 0xE36F, 0x9276, 0xE370, 0x9277, 0xE371, 0x9278,\n\t0xE372, 0x9279, 0xE373, 0x927A, 0xE374, 0x927B, 0xE375, 0x927C,\n\t0xE376, 0x927D, 0xE377, 0x927E, 0xE378, 0x927F, 0xE379, 0x9280,\n\t0xE37A, 0x9281, 0xE37B, 0x9282, 0xE37C, 0x9283, 0xE37D, 0x9284,\n\t0xE37E, 0x9285, 0xE380, 0x9286, 0xE381, 0x9287, 0xE382, 0x9288,\n\t0xE383, 0x9289, 0xE384, 0x928A, 0xE385, 0x928B, 0xE386, 0x928C,\n\t0xE387, 0x928D, 0xE388, 0x928F, 0xE389, 0x9290, 0xE38A, 0x9291,\n\t0xE38B, 0x9292, 0xE38C, 0x9293, 0xE38D, 0x9294, 0xE38E, 0x9295,\n\t0xE38F, 0x9296, 0xE390, 0x9297, 0xE391, 0x9298, 0xE392, 0x9299,\n\t0xE393, 0x929A, 0xE394, 0x929B, 0xE395, 0x929C, 0xE396, 0x929D,\n\t0xE397, 0x929E, 0xE398, 0x929F, 0xE399, 0x92A0, 0xE39A, 0x92A1,\n\t0xE39B, 0x92A2, 0xE39C, 0x92A3, 0xE39D, 0x92A4, 0xE39E, 0x92A5,\n\t0xE39F, 0x92A6, 0xE3A0, 0x92A7, 0xE3A1, 0x606A, 0xE3A2, 0x607D,\n\t0xE3A3, 0x6096, 0xE3A4, 0x609A, 0xE3A5, 0x60AD, 0xE3A6, 0x609D,\n\t0xE3A7, 0x6083, 0xE3A8, 0x6092, 0xE3A9, 0x608C, 0xE3AA, 0x609B,\n\t0xE3AB, 0x60EC, 0xE3AC, 0x60BB, 0xE3AD, 0x60B1, 0xE3AE, 0x60DD,\n\t0xE3AF, 0x60D8, 0xE3B0, 0x60C6, 0xE3B1, 0x60DA, 0xE3B2, 0x60B4,\n\t0xE3B3, 0x6120, 0xE3B4, 0x6126, 0xE3B5, 0x6115, 0xE3B6, 0x6123,\n\t0xE3B7, 0x60F4, 0xE3B8, 0x6100, 0xE3B9, 0x610E, 0xE3BA, 0x612B,\n\t0xE3BB, 0x614A, 0xE3BC, 0x6175, 0xE3BD, 0x61AC, 0xE3BE, 0x6194,\n\t0xE3BF, 0x61A7, 0xE3C0, 0x61B7, 0xE3C1, 0x61D4, 0xE3C2, 0x61F5,\n\t0xE3C3, 0x5FDD, 0xE3C4, 0x96B3, 0xE3C5, 0x95E9, 0xE3C6, 0x95EB,\n\t0xE3C7, 0x95F1, 0xE3C8, 0x95F3, 0xE3C9, 0x95F5, 0xE3CA, 0x95F6,\n\t0xE3CB, 0x95FC, 0xE3CC, 0x95FE, 0xE3CD, 0x9603, 0xE3CE, 0x9604,\n\t0xE3CF, 0x9606, 0xE3D0, 0x9608, 0xE3D1, 0x960A, 0xE3D2, 0x960B,\n\t0xE3D3, 0x960C, 0xE3D4, 0x960D, 0xE3D5, 0x960F, 0xE3D6, 0x9612,\n\t0xE3D7, 0x9615, 0xE3D8, 0x9616, 0xE3D9, 0x9617, 0xE3DA, 0x9619,\n\t0xE3DB, 0x961A, 0xE3DC, 0x4E2C, 0xE3DD, 0x723F, 0xE3DE, 0x6215,\n\t0xE3DF, 0x6C35, 0xE3E0, 0x6C54, 0xE3E1, 0x6C5C, 0xE3E2, 0x6C4A,\n\t0xE3E3, 0x6CA3, 0xE3E4, 0x6C85, 0xE3E5, 0x6C90, 0xE3E6, 0x6C94,\n\t0xE3E7, 0x6C8C, 0xE3E8, 0x6C68, 0xE3E9, 0x6C69, 0xE3EA, 0x6C74,\n\t0xE3EB, 0x6C76, 0xE3EC, 0x6C86, 0xE3ED, 0x6CA9, 0xE3EE, 0x6CD0,\n\t0xE3EF, 0x6CD4, 0xE3F0, 0x6CAD, 0xE3F1, 0x6CF7, 0xE3F2, 0x6CF8,\n\t0xE3F3, 0x6CF1, 0xE3F4, 0x6CD7, 0xE3F5, 0x6CB2, 0xE3F6, 0x6CE0,\n\t0xE3F7, 0x6CD6, 0xE3F8, 0x6CFA, 0xE3F9, 0x6CEB, 0xE3FA, 0x6CEE,\n\t0xE3FB, 0x6CB1, 0xE3FC, 0x6CD3, 0xE3FD, 0x6CEF, 0xE3FE, 0x6CFE,\n\t0xE440, 0x92A8, 0xE441, 0x92A9, 0xE442, 0x92AA, 0xE443, 0x92AB,\n\t0xE444, 0x92AC, 0xE445, 0x92AD, 0xE446, 0x92AF, 0xE447, 0x92B0,\n\t0xE448, 0x92B1, 0xE449, 0x92B2, 0xE44A, 0x92B3, 0xE44B, 0x92B4,\n\t0xE44C, 0x92B5, 0xE44D, 0x92B6, 0xE44E, 0x92B7, 0xE44F, 0x92B8,\n\t0xE450, 0x92B9, 0xE451, 0x92BA, 0xE452, 0x92BB, 0xE453, 0x92BC,\n\t0xE454, 0x92BD, 0xE455, 0x92BE, 0xE456, 0x92BF, 0xE457, 0x92C0,\n\t0xE458, 0x92C1, 0xE459, 0x92C2, 0xE45A, 0x92C3, 0xE45B, 0x92C4,\n\t0xE45C, 0x92C5, 0xE45D, 0x92C6, 0xE45E, 0x92C7, 0xE45F, 0x92C9,\n\t0xE460, 0x92CA, 0xE461, 0x92CB, 0xE462, 0x92CC, 0xE463, 0x92CD,\n\t0xE464, 0x92CE, 0xE465, 0x92CF, 0xE466, 0x92D0, 0xE467, 0x92D1,\n\t0xE468, 0x92D2, 0xE469, 0x92D3, 0xE46A, 0x92D4, 0xE46B, 0x92D5,\n\t0xE46C, 0x92D6, 0xE46D, 0x92D7, 0xE46E, 0x92D8, 0xE46F, 0x92D9,\n\t0xE470, 0x92DA, 0xE471, 0x92DB, 0xE472, 0x92DC, 0xE473, 0x92DD,\n\t0xE474, 0x92DE, 0xE475, 0x92DF, 0xE476, 0x92E0, 0xE477, 0x92E1,\n\t0xE478, 0x92E2, 0xE479, 0x92E3, 0xE47A, 0x92E4, 0xE47B, 0x92E5,\n\t0xE47C, 0x92E6, 0xE47D, 0x92E7, 0xE47E, 0x92E8, 0xE480, 0x92E9,\n\t0xE481, 0x92EA, 0xE482, 0x92EB, 0xE483, 0x92EC, 0xE484, 0x92ED,\n\t0xE485, 0x92EE, 0xE486, 0x92EF, 0xE487, 0x92F0, 0xE488, 0x92F1,\n\t0xE489, 0x92F2, 0xE48A, 0x92F3, 0xE48B, 0x92F4, 0xE48C, 0x92F5,\n\t0xE48D, 0x92F6, 0xE48E, 0x92F7, 0xE48F, 0x92F8, 0xE490, 0x92F9,\n\t0xE491, 0x92FA, 0xE492, 0x92FB, 0xE493, 0x92FC, 0xE494, 0x92FD,\n\t0xE495, 0x92FE, 0xE496, 0x92FF, 0xE497, 0x9300, 0xE498, 0x9301,\n\t0xE499, 0x9302, 0xE49A, 0x9303, 0xE49B, 0x9304, 0xE49C, 0x9305,\n\t0xE49D, 0x9306, 0xE49E, 0x9307, 0xE49F, 0x9308, 0xE4A0, 0x9309,\n\t0xE4A1, 0x6D39, 0xE4A2, 0x6D27, 0xE4A3, 0x6D0C, 0xE4A4, 0x6D43,\n\t0xE4A5, 0x6D48, 0xE4A6, 0x6D07, 0xE4A7, 0x6D04, 0xE4A8, 0x6D19,\n\t0xE4A9, 0x6D0E, 0xE4AA, 0x6D2B, 0xE4AB, 0x6D4D, 0xE4AC, 0x6D2E,\n\t0xE4AD, 0x6D35, 0xE4AE, 0x6D1A, 0xE4AF, 0x6D4F, 0xE4B0, 0x6D52,\n\t0xE4B1, 0x6D54, 0xE4B2, 0x6D33, 0xE4B3, 0x6D91, 0xE4B4, 0x6D6F,\n\t0xE4B5, 0x6D9E, 0xE4B6, 0x6DA0, 0xE4B7, 0x6D5E, 0xE4B8, 0x6D93,\n\t0xE4B9, 0x6D94, 0xE4BA, 0x6D5C, 0xE4BB, 0x6D60, 0xE4BC, 0x6D7C,\n\t0xE4BD, 0x6D63, 0xE4BE, 0x6E1A, 0xE4BF, 0x6DC7, 0xE4C0, 0x6DC5,\n\t0xE4C1, 0x6DDE, 0xE4C2, 0x6E0E, 0xE4C3, 0x6DBF, 0xE4C4, 0x6DE0,\n\t0xE4C5, 0x6E11, 0xE4C6, 0x6DE6, 0xE4C7, 0x6DDD, 0xE4C8, 0x6DD9,\n\t0xE4C9, 0x6E16, 0xE4CA, 0x6DAB, 0xE4CB, 0x6E0C, 0xE4CC, 0x6DAE,\n\t0xE4CD, 0x6E2B, 0xE4CE, 0x6E6E, 0xE4CF, 0x6E4E, 0xE4D0, 0x6E6B,\n\t0xE4D1, 0x6EB2, 0xE4D2, 0x6E5F, 0xE4D3, 0x6E86, 0xE4D4, 0x6E53,\n\t0xE4D5, 0x6E54, 0xE4D6, 0x6E32, 0xE4D7, 0x6E25, 0xE4D8, 0x6E44,\n\t0xE4D9, 0x6EDF, 0xE4DA, 0x6EB1, 0xE4DB, 0x6E98, 0xE4DC, 0x6EE0,\n\t0xE4DD, 0x6F2D, 0xE4DE, 0x6EE2, 0xE4DF, 0x6EA5, 0xE4E0, 0x6EA7,\n\t0xE4E1, 0x6EBD, 0xE4E2, 0x6EBB, 0xE4E3, 0x6EB7, 0xE4E4, 0x6ED7,\n\t0xE4E5, 0x6EB4, 0xE4E6, 0x6ECF, 0xE4E7, 0x6E8F, 0xE4E8, 0x6EC2,\n\t0xE4E9, 0x6E9F, 0xE4EA, 0x6F62, 0xE4EB, 0x6F46, 0xE4EC, 0x6F47,\n\t0xE4ED, 0x6F24, 0xE4EE, 0x6F15, 0xE4EF, 0x6EF9, 0xE4F0, 0x6F2F,\n\t0xE4F1, 0x6F36, 0xE4F2, 0x6F4B, 0xE4F3, 0x6F74, 0xE4F4, 0x6F2A,\n\t0xE4F5, 0x6F09, 0xE4F6, 0x6F29, 0xE4F7, 0x6F89, 0xE4F8, 0x6F8D,\n\t0xE4F9, 0x6F8C, 0xE4FA, 0x6F78, 0xE4FB, 0x6F72, 0xE4FC, 0x6F7C,\n\t0xE4FD, 0x6F7A, 0xE4FE, 0x6FD1, 0xE540, 0x930A, 0xE541, 0x930B,\n\t0xE542, 0x930C, 0xE543, 0x930D, 0xE544, 0x930E, 0xE545, 0x930F,\n\t0xE546, 0x9310, 0xE547, 0x9311, 0xE548, 0x9312, 0xE549, 0x9313,\n\t0xE54A, 0x9314, 0xE54B, 0x9315, 0xE54C, 0x9316, 0xE54D, 0x9317,\n\t0xE54E, 0x9318, 0xE54F, 0x9319, 0xE550, 0x931A, 0xE551, 0x931B,\n\t0xE552, 0x931C, 0xE553, 0x931D, 0xE554, 0x931E, 0xE555, 0x931F,\n\t0xE556, 0x9320, 0xE557, 0x9321, 0xE558, 0x9322, 0xE559, 0x9323,\n\t0xE55A, 0x9324, 0xE55B, 0x9325, 0xE55C, 0x9326, 0xE55D, 0x9327,\n\t0xE55E, 0x9328, 0xE55F, 0x9329, 0xE560, 0x932A, 0xE561, 0x932B,\n\t0xE562, 0x932C, 0xE563, 0x932D, 0xE564, 0x932E, 0xE565, 0x932F,\n\t0xE566, 0x9330, 0xE567, 0x9331, 0xE568, 0x9332, 0xE569, 0x9333,\n\t0xE56A, 0x9334, 0xE56B, 0x9335, 0xE56C, 0x9336, 0xE56D, 0x9337,\n\t0xE56E, 0x9338, 0xE56F, 0x9339, 0xE570, 0x933A, 0xE571, 0x933B,\n\t0xE572, 0x933C, 0xE573, 0x933D, 0xE574, 0x933F, 0xE575, 0x9340,\n\t0xE576, 0x9341, 0xE577, 0x9342, 0xE578, 0x9343, 0xE579, 0x9344,\n\t0xE57A, 0x9345, 0xE57B, 0x9346, 0xE57C, 0x9347, 0xE57D, 0x9348,\n\t0xE57E, 0x9349, 0xE580, 0x934A, 0xE581, 0x934B, 0xE582, 0x934C,\n\t0xE583, 0x934D, 0xE584, 0x934E, 0xE585, 0x934F, 0xE586, 0x9350,\n\t0xE587, 0x9351, 0xE588, 0x9352, 0xE589, 0x9353, 0xE58A, 0x9354,\n\t0xE58B, 0x9355, 0xE58C, 0x9356, 0xE58D, 0x9357, 0xE58E, 0x9358,\n\t0xE58F, 0x9359, 0xE590, 0x935A, 0xE591, 0x935B, 0xE592, 0x935C,\n\t0xE593, 0x935D, 0xE594, 0x935E, 0xE595, 0x935F, 0xE596, 0x9360,\n\t0xE597, 0x9361, 0xE598, 0x9362, 0xE599, 0x9363, 0xE59A, 0x9364,\n\t0xE59B, 0x9365, 0xE59C, 0x9366, 0xE59D, 0x9367, 0xE59E, 0x9368,\n\t0xE59F, 0x9369, 0xE5A0, 0x936B, 0xE5A1, 0x6FC9, 0xE5A2, 0x6FA7,\n\t0xE5A3, 0x6FB9, 0xE5A4, 0x6FB6, 0xE5A5, 0x6FC2, 0xE5A6, 0x6FE1,\n\t0xE5A7, 0x6FEE, 0xE5A8, 0x6FDE, 0xE5A9, 0x6FE0, 0xE5AA, 0x6FEF,\n\t0xE5AB, 0x701A, 0xE5AC, 0x7023, 0xE5AD, 0x701B, 0xE5AE, 0x7039,\n\t0xE5AF, 0x7035, 0xE5B0, 0x704F, 0xE5B1, 0x705E, 0xE5B2, 0x5B80,\n\t0xE5B3, 0x5B84, 0xE5B4, 0x5B95, 0xE5B5, 0x5B93, 0xE5B6, 0x5BA5,\n\t0xE5B7, 0x5BB8, 0xE5B8, 0x752F, 0xE5B9, 0x9A9E, 0xE5BA, 0x6434,\n\t0xE5BB, 0x5BE4, 0xE5BC, 0x5BEE, 0xE5BD, 0x8930, 0xE5BE, 0x5BF0,\n\t0xE5BF, 0x8E47, 0xE5C0, 0x8B07, 0xE5C1, 0x8FB6, 0xE5C2, 0x8FD3,\n\t0xE5C3, 0x8FD5, 0xE5C4, 0x8FE5, 0xE5C5, 0x8FEE, 0xE5C6, 0x8FE4,\n\t0xE5C7, 0x8FE9, 0xE5C8, 0x8FE6, 0xE5C9, 0x8FF3, 0xE5CA, 0x8FE8,\n\t0xE5CB, 0x9005, 0xE5CC, 0x9004, 0xE5CD, 0x900B, 0xE5CE, 0x9026,\n\t0xE5CF, 0x9011, 0xE5D0, 0x900D, 0xE5D1, 0x9016, 0xE5D2, 0x9021,\n\t0xE5D3, 0x9035, 0xE5D4, 0x9036, 0xE5D5, 0x902D, 0xE5D6, 0x902F,\n\t0xE5D7, 0x9044, 0xE5D8, 0x9051, 0xE5D9, 0x9052, 0xE5DA, 0x9050,\n\t0xE5DB, 0x9068, 0xE5DC, 0x9058, 0xE5DD, 0x9062, 0xE5DE, 0x905B,\n\t0xE5DF, 0x66B9, 0xE5E0, 0x9074, 0xE5E1, 0x907D, 0xE5E2, 0x9082,\n\t0xE5E3, 0x9088, 0xE5E4, 0x9083, 0xE5E5, 0x908B, 0xE5E6, 0x5F50,\n\t0xE5E7, 0x5F57, 0xE5E8, 0x5F56, 0xE5E9, 0x5F58, 0xE5EA, 0x5C3B,\n\t0xE5EB, 0x54AB, 0xE5EC, 0x5C50, 0xE5ED, 0x5C59, 0xE5EE, 0x5B71,\n\t0xE5EF, 0x5C63, 0xE5F0, 0x5C66, 0xE5F1, 0x7FBC, 0xE5F2, 0x5F2A,\n\t0xE5F3, 0x5F29, 0xE5F4, 0x5F2D, 0xE5F5, 0x8274, 0xE5F6, 0x5F3C,\n\t0xE5F7, 0x9B3B, 0xE5F8, 0x5C6E, 0xE5F9, 0x5981, 0xE5FA, 0x5983,\n\t0xE5FB, 0x598D, 0xE5FC, 0x59A9, 0xE5FD, 0x59AA, 0xE5FE, 0x59A3,\n\t0xE640, 0x936C, 0xE641, 0x936D, 0xE642, 0x936E, 0xE643, 0x936F,\n\t0xE644, 0x9370, 0xE645, 0x9371, 0xE646, 0x9372, 0xE647, 0x9373,\n\t0xE648, 0x9374, 0xE649, 0x9375, 0xE64A, 0x9376, 0xE64B, 0x9377,\n\t0xE64C, 0x9378, 0xE64D, 0x9379, 0xE64E, 0x937A, 0xE64F, 0x937B,\n\t0xE650, 0x937C, 0xE651, 0x937D, 0xE652, 0x937E, 0xE653, 0x937F,\n\t0xE654, 0x9380, 0xE655, 0x9381, 0xE656, 0x9382, 0xE657, 0x9383,\n\t0xE658, 0x9384, 0xE659, 0x9385, 0xE65A, 0x9386, 0xE65B, 0x9387,\n\t0xE65C, 0x9388, 0xE65D, 0x9389, 0xE65E, 0x938A, 0xE65F, 0x938B,\n\t0xE660, 0x938C, 0xE661, 0x938D, 0xE662, 0x938E, 0xE663, 0x9390,\n\t0xE664, 0x9391, 0xE665, 0x9392, 0xE666, 0x9393, 0xE667, 0x9394,\n\t0xE668, 0x9395, 0xE669, 0x9396, 0xE66A, 0x9397, 0xE66B, 0x9398,\n\t0xE66C, 0x9399, 0xE66D, 0x939A, 0xE66E, 0x939B, 0xE66F, 0x939C,\n\t0xE670, 0x939D, 0xE671, 0x939E, 0xE672, 0x939F, 0xE673, 0x93A0,\n\t0xE674, 0x93A1, 0xE675, 0x93A2, 0xE676, 0x93A3, 0xE677, 0x93A4,\n\t0xE678, 0x93A5, 0xE679, 0x93A6, 0xE67A, 0x93A7, 0xE67B, 0x93A8,\n\t0xE67C, 0x93A9, 0xE67D, 0x93AA, 0xE67E, 0x93AB, 0xE680, 0x93AC,\n\t0xE681, 0x93AD, 0xE682, 0x93AE, 0xE683, 0x93AF, 0xE684, 0x93B0,\n\t0xE685, 0x93B1, 0xE686, 0x93B2, 0xE687, 0x93B3, 0xE688, 0x93B4,\n\t0xE689, 0x93B5, 0xE68A, 0x93B6, 0xE68B, 0x93B7, 0xE68C, 0x93B8,\n\t0xE68D, 0x93B9, 0xE68E, 0x93BA, 0xE68F, 0x93BB, 0xE690, 0x93BC,\n\t0xE691, 0x93BD, 0xE692, 0x93BE, 0xE693, 0x93BF, 0xE694, 0x93C0,\n\t0xE695, 0x93C1, 0xE696, 0x93C2, 0xE697, 0x93C3, 0xE698, 0x93C4,\n\t0xE699, 0x93C5, 0xE69A, 0x93C6, 0xE69B, 0x93C7, 0xE69C, 0x93C8,\n\t0xE69D, 0x93C9, 0xE69E, 0x93CB, 0xE69F, 0x93CC, 0xE6A0, 0x93CD,\n\t0xE6A1, 0x5997, 0xE6A2, 0x59CA, 0xE6A3, 0x59AB, 0xE6A4, 0x599E,\n\t0xE6A5, 0x59A4, 0xE6A6, 0x59D2, 0xE6A7, 0x59B2, 0xE6A8, 0x59AF,\n\t0xE6A9, 0x59D7, 0xE6AA, 0x59BE, 0xE6AB, 0x5A05, 0xE6AC, 0x5A06,\n\t0xE6AD, 0x59DD, 0xE6AE, 0x5A08, 0xE6AF, 0x59E3, 0xE6B0, 0x59D8,\n\t0xE6B1, 0x59F9, 0xE6B2, 0x5A0C, 0xE6B3, 0x5A09, 0xE6B4, 0x5A32,\n\t0xE6B5, 0x5A34, 0xE6B6, 0x5A11, 0xE6B7, 0x5A23, 0xE6B8, 0x5A13,\n\t0xE6B9, 0x5A40, 0xE6BA, 0x5A67, 0xE6BB, 0x5A4A, 0xE6BC, 0x5A55,\n\t0xE6BD, 0x5A3C, 0xE6BE, 0x5A62, 0xE6BF, 0x5A75, 0xE6C0, 0x80EC,\n\t0xE6C1, 0x5AAA, 0xE6C2, 0x5A9B, 0xE6C3, 0x5A77, 0xE6C4, 0x5A7A,\n\t0xE6C5, 0x5ABE, 0xE6C6, 0x5AEB, 0xE6C7, 0x5AB2, 0xE6C8, 0x5AD2,\n\t0xE6C9, 0x5AD4, 0xE6CA, 0x5AB8, 0xE6CB, 0x5AE0, 0xE6CC, 0x5AE3,\n\t0xE6CD, 0x5AF1, 0xE6CE, 0x5AD6, 0xE6CF, 0x5AE6, 0xE6D0, 0x5AD8,\n\t0xE6D1, 0x5ADC, 0xE6D2, 0x5B09, 0xE6D3, 0x5B17, 0xE6D4, 0x5B16,\n\t0xE6D5, 0x5B32, 0xE6D6, 0x5B37, 0xE6D7, 0x5B40, 0xE6D8, 0x5C15,\n\t0xE6D9, 0x5C1C, 0xE6DA, 0x5B5A, 0xE6DB, 0x5B65, 0xE6DC, 0x5B73,\n\t0xE6DD, 0x5B51, 0xE6DE, 0x5B53, 0xE6DF, 0x5B62, 0xE6E0, 0x9A75,\n\t0xE6E1, 0x9A77, 0xE6E2, 0x9A78, 0xE6E3, 0x9A7A, 0xE6E4, 0x9A7F,\n\t0xE6E5, 0x9A7D, 0xE6E6, 0x9A80, 0xE6E7, 0x9A81, 0xE6E8, 0x9A85,\n\t0xE6E9, 0x9A88, 0xE6EA, 0x9A8A, 0xE6EB, 0x9A90, 0xE6EC, 0x9A92,\n\t0xE6ED, 0x9A93, 0xE6EE, 0x9A96, 0xE6EF, 0x9A98, 0xE6F0, 0x9A9B,\n\t0xE6F1, 0x9A9C, 0xE6F2, 0x9A9D, 0xE6F3, 0x9A9F, 0xE6F4, 0x9AA0,\n\t0xE6F5, 0x9AA2, 0xE6F6, 0x9AA3, 0xE6F7, 0x9AA5, 0xE6F8, 0x9AA7,\n\t0xE6F9, 0x7E9F, 0xE6FA, 0x7EA1, 0xE6FB, 0x7EA3, 0xE6FC, 0x7EA5,\n\t0xE6FD, 0x7EA8, 0xE6FE, 0x7EA9, 0xE740, 0x93CE, 0xE741, 0x93CF,\n\t0xE742, 0x93D0, 0xE743, 0x93D1, 0xE744, 0x93D2, 0xE745, 0x93D3,\n\t0xE746, 0x93D4, 0xE747, 0x93D5, 0xE748, 0x93D7, 0xE749, 0x93D8,\n\t0xE74A, 0x93D9, 0xE74B, 0x93DA, 0xE74C, 0x93DB, 0xE74D, 0x93DC,\n\t0xE74E, 0x93DD, 0xE74F, 0x93DE, 0xE750, 0x93DF, 0xE751, 0x93E0,\n\t0xE752, 0x93E1, 0xE753, 0x93E2, 0xE754, 0x93E3, 0xE755, 0x93E4,\n\t0xE756, 0x93E5, 0xE757, 0x93E6, 0xE758, 0x93E7, 0xE759, 0x93E8,\n\t0xE75A, 0x93E9, 0xE75B, 0x93EA, 0xE75C, 0x93EB, 0xE75D, 0x93EC,\n\t0xE75E, 0x93ED, 0xE75F, 0x93EE, 0xE760, 0x93EF, 0xE761, 0x93F0,\n\t0xE762, 0x93F1, 0xE763, 0x93F2, 0xE764, 0x93F3, 0xE765, 0x93F4,\n\t0xE766, 0x93F5, 0xE767, 0x93F6, 0xE768, 0x93F7, 0xE769, 0x93F8,\n\t0xE76A, 0x93F9, 0xE76B, 0x93FA, 0xE76C, 0x93FB, 0xE76D, 0x93FC,\n\t0xE76E, 0x93FD, 0xE76F, 0x93FE, 0xE770, 0x93FF, 0xE771, 0x9400,\n\t0xE772, 0x9401, 0xE773, 0x9402, 0xE774, 0x9403, 0xE775, 0x9404,\n\t0xE776, 0x9405, 0xE777, 0x9406, 0xE778, 0x9407, 0xE779, 0x9408,\n\t0xE77A, 0x9409, 0xE77B, 0x940A, 0xE77C, 0x940B, 0xE77D, 0x940C,\n\t0xE77E, 0x940D, 0xE780, 0x940E, 0xE781, 0x940F, 0xE782, 0x9410,\n\t0xE783, 0x9411, 0xE784, 0x9412, 0xE785, 0x9413, 0xE786, 0x9414,\n\t0xE787, 0x9415, 0xE788, 0x9416, 0xE789, 0x9417, 0xE78A, 0x9418,\n\t0xE78B, 0x9419, 0xE78C, 0x941A, 0xE78D, 0x941B, 0xE78E, 0x941C,\n\t0xE78F, 0x941D, 0xE790, 0x941E, 0xE791, 0x941F, 0xE792, 0x9420,\n\t0xE793, 0x9421, 0xE794, 0x9422, 0xE795, 0x9423, 0xE796, 0x9424,\n\t0xE797, 0x9425, 0xE798, 0x9426, 0xE799, 0x9427, 0xE79A, 0x9428,\n\t0xE79B, 0x9429, 0xE79C, 0x942A, 0xE79D, 0x942B, 0xE79E, 0x942C,\n\t0xE79F, 0x942D, 0xE7A0, 0x942E, 0xE7A1, 0x7EAD, 0xE7A2, 0x7EB0,\n\t0xE7A3, 0x7EBE, 0xE7A4, 0x7EC0, 0xE7A5, 0x7EC1, 0xE7A6, 0x7EC2,\n\t0xE7A7, 0x7EC9, 0xE7A8, 0x7ECB, 0xE7A9, 0x7ECC, 0xE7AA, 0x7ED0,\n\t0xE7AB, 0x7ED4, 0xE7AC, 0x7ED7, 0xE7AD, 0x7EDB, 0xE7AE, 0x7EE0,\n\t0xE7AF, 0x7EE1, 0xE7B0, 0x7EE8, 0xE7B1, 0x7EEB, 0xE7B2, 0x7EEE,\n\t0xE7B3, 0x7EEF, 0xE7B4, 0x7EF1, 0xE7B5, 0x7EF2, 0xE7B6, 0x7F0D,\n\t0xE7B7, 0x7EF6, 0xE7B8, 0x7EFA, 0xE7B9, 0x7EFB, 0xE7BA, 0x7EFE,\n\t0xE7BB, 0x7F01, 0xE7BC, 0x7F02, 0xE7BD, 0x7F03, 0xE7BE, 0x7F07,\n\t0xE7BF, 0x7F08, 0xE7C0, 0x7F0B, 0xE7C1, 0x7F0C, 0xE7C2, 0x7F0F,\n\t0xE7C3, 0x7F11, 0xE7C4, 0x7F12, 0xE7C5, 0x7F17, 0xE7C6, 0x7F19,\n\t0xE7C7, 0x7F1C, 0xE7C8, 0x7F1B, 0xE7C9, 0x7F1F, 0xE7CA, 0x7F21,\n\t0xE7CB, 0x7F22, 0xE7CC, 0x7F23, 0xE7CD, 0x7F24, 0xE7CE, 0x7F25,\n\t0xE7CF, 0x7F26, 0xE7D0, 0x7F27, 0xE7D1, 0x7F2A, 0xE7D2, 0x7F2B,\n\t0xE7D3, 0x7F2C, 0xE7D4, 0x7F2D, 0xE7D5, 0x7F2F, 0xE7D6, 0x7F30,\n\t0xE7D7, 0x7F31, 0xE7D8, 0x7F32, 0xE7D9, 0x7F33, 0xE7DA, 0x7F35,\n\t0xE7DB, 0x5E7A, 0xE7DC, 0x757F, 0xE7DD, 0x5DDB, 0xE7DE, 0x753E,\n\t0xE7DF, 0x9095, 0xE7E0, 0x738E, 0xE7E1, 0x7391, 0xE7E2, 0x73AE,\n\t0xE7E3, 0x73A2, 0xE7E4, 0x739F, 0xE7E5, 0x73CF, 0xE7E6, 0x73C2,\n\t0xE7E7, 0x73D1, 0xE7E8, 0x73B7, 0xE7E9, 0x73B3, 0xE7EA, 0x73C0,\n\t0xE7EB, 0x73C9, 0xE7EC, 0x73C8, 0xE7ED, 0x73E5, 0xE7EE, 0x73D9,\n\t0xE7EF, 0x987C, 0xE7F0, 0x740A, 0xE7F1, 0x73E9, 0xE7F2, 0x73E7,\n\t0xE7F3, 0x73DE, 0xE7F4, 0x73BA, 0xE7F5, 0x73F2, 0xE7F6, 0x740F,\n\t0xE7F7, 0x742A, 0xE7F8, 0x745B, 0xE7F9, 0x7426, 0xE7FA, 0x7425,\n\t0xE7FB, 0x7428, 0xE7FC, 0x7430, 0xE7FD, 0x742E, 0xE7FE, 0x742C,\n\t0xE840, 0x942F, 0xE841, 0x9430, 0xE842, 0x9431, 0xE843, 0x9432,\n\t0xE844, 0x9433, 0xE845, 0x9434, 0xE846, 0x9435, 0xE847, 0x9436,\n\t0xE848, 0x9437, 0xE849, 0x9438, 0xE84A, 0x9439, 0xE84B, 0x943A,\n\t0xE84C, 0x943B, 0xE84D, 0x943C, 0xE84E, 0x943D, 0xE84F, 0x943F,\n\t0xE850, 0x9440, 0xE851, 0x9441, 0xE852, 0x9442, 0xE853, 0x9443,\n\t0xE854, 0x9444, 0xE855, 0x9445, 0xE856, 0x9446, 0xE857, 0x9447,\n\t0xE858, 0x9448, 0xE859, 0x9449, 0xE85A, 0x944A, 0xE85B, 0x944B,\n\t0xE85C, 0x944C, 0xE85D, 0x944D, 0xE85E, 0x944E, 0xE85F, 0x944F,\n\t0xE860, 0x9450, 0xE861, 0x9451, 0xE862, 0x9452, 0xE863, 0x9453,\n\t0xE864, 0x9454, 0xE865, 0x9455, 0xE866, 0x9456, 0xE867, 0x9457,\n\t0xE868, 0x9458, 0xE869, 0x9459, 0xE86A, 0x945A, 0xE86B, 0x945B,\n\t0xE86C, 0x945C, 0xE86D, 0x945D, 0xE86E, 0x945E, 0xE86F, 0x945F,\n\t0xE870, 0x9460, 0xE871, 0x9461, 0xE872, 0x9462, 0xE873, 0x9463,\n\t0xE874, 0x9464, 0xE875, 0x9465, 0xE876, 0x9466, 0xE877, 0x9467,\n\t0xE878, 0x9468, 0xE879, 0x9469, 0xE87A, 0x946A, 0xE87B, 0x946C,\n\t0xE87C, 0x946D, 0xE87D, 0x946E, 0xE87E, 0x946F, 0xE880, 0x9470,\n\t0xE881, 0x9471, 0xE882, 0x9472, 0xE883, 0x9473, 0xE884, 0x9474,\n\t0xE885, 0x9475, 0xE886, 0x9476, 0xE887, 0x9477, 0xE888, 0x9478,\n\t0xE889, 0x9479, 0xE88A, 0x947A, 0xE88B, 0x947B, 0xE88C, 0x947C,\n\t0xE88D, 0x947D, 0xE88E, 0x947E, 0xE88F, 0x947F, 0xE890, 0x9480,\n\t0xE891, 0x9481, 0xE892, 0x9482, 0xE893, 0x9483, 0xE894, 0x9484,\n\t0xE895, 0x9491, 0xE896, 0x9496, 0xE897, 0x9498, 0xE898, 0x94C7,\n\t0xE899, 0x94CF, 0xE89A, 0x94D3, 0xE89B, 0x94D4, 0xE89C, 0x94DA,\n\t0xE89D, 0x94E6, 0xE89E, 0x94FB, 0xE89F, 0x951C, 0xE8A0, 0x9520,\n\t0xE8A1, 0x741B, 0xE8A2, 0x741A, 0xE8A3, 0x7441, 0xE8A4, 0x745C,\n\t0xE8A5, 0x7457, 0xE8A6, 0x7455, 0xE8A7, 0x7459, 0xE8A8, 0x7477,\n\t0xE8A9, 0x746D, 0xE8AA, 0x747E, 0xE8AB, 0x749C, 0xE8AC, 0x748E,\n\t0xE8AD, 0x7480, 0xE8AE, 0x7481, 0xE8AF, 0x7487, 0xE8B0, 0x748B,\n\t0xE8B1, 0x749E, 0xE8B2, 0x74A8, 0xE8B3, 0x74A9, 0xE8B4, 0x7490,\n\t0xE8B5, 0x74A7, 0xE8B6, 0x74D2, 0xE8B7, 0x74BA, 0xE8B8, 0x97EA,\n\t0xE8B9, 0x97EB, 0xE8BA, 0x97EC, 0xE8BB, 0x674C, 0xE8BC, 0x6753,\n\t0xE8BD, 0x675E, 0xE8BE, 0x6748, 0xE8BF, 0x6769, 0xE8C0, 0x67A5,\n\t0xE8C1, 0x6787, 0xE8C2, 0x676A, 0xE8C3, 0x6773, 0xE8C4, 0x6798,\n\t0xE8C5, 0x67A7, 0xE8C6, 0x6775, 0xE8C7, 0x67A8, 0xE8C8, 0x679E,\n\t0xE8C9, 0x67AD, 0xE8CA, 0x678B, 0xE8CB, 0x6777, 0xE8CC, 0x677C,\n\t0xE8CD, 0x67F0, 0xE8CE, 0x6809, 0xE8CF, 0x67D8, 0xE8D0, 0x680A,\n\t0xE8D1, 0x67E9, 0xE8D2, 0x67B0, 0xE8D3, 0x680C, 0xE8D4, 0x67D9,\n\t0xE8D5, 0x67B5, 0xE8D6, 0x67DA, 0xE8D7, 0x67B3, 0xE8D8, 0x67DD,\n\t0xE8D9, 0x6800, 0xE8DA, 0x67C3, 0xE8DB, 0x67B8, 0xE8DC, 0x67E2,\n\t0xE8DD, 0x680E, 0xE8DE, 0x67C1, 0xE8DF, 0x67FD, 0xE8E0, 0x6832,\n\t0xE8E1, 0x6833, 0xE8E2, 0x6860, 0xE8E3, 0x6861, 0xE8E4, 0x684E,\n\t0xE8E5, 0x6862, 0xE8E6, 0x6844, 0xE8E7, 0x6864, 0xE8E8, 0x6883,\n\t0xE8E9, 0x681D, 0xE8EA, 0x6855, 0xE8EB, 0x6866, 0xE8EC, 0x6841,\n\t0xE8ED, 0x6867, 0xE8EE, 0x6840, 0xE8EF, 0x683E, 0xE8F0, 0x684A,\n\t0xE8F1, 0x6849, 0xE8F2, 0x6829, 0xE8F3, 0x68B5, 0xE8F4, 0x688F,\n\t0xE8F5, 0x6874, 0xE8F6, 0x6877, 0xE8F7, 0x6893, 0xE8F8, 0x686B,\n\t0xE8F9, 0x68C2, 0xE8FA, 0x696E, 0xE8FB, 0x68FC, 0xE8FC, 0x691F,\n\t0xE8FD, 0x6920, 0xE8FE, 0x68F9, 0xE940, 0x9527, 0xE941, 0x9533,\n\t0xE942, 0x953D, 0xE943, 0x9543, 0xE944, 0x9548, 0xE945, 0x954B,\n\t0xE946, 0x9555, 0xE947, 0x955A, 0xE948, 0x9560, 0xE949, 0x956E,\n\t0xE94A, 0x9574, 0xE94B, 0x9575, 0xE94C, 0x9577, 0xE94D, 0x9578,\n\t0xE94E, 0x9579, 0xE94F, 0x957A, 0xE950, 0x957B, 0xE951, 0x957C,\n\t0xE952, 0x957D, 0xE953, 0x957E, 0xE954, 0x9580, 0xE955, 0x9581,\n\t0xE956, 0x9582, 0xE957, 0x9583, 0xE958, 0x9584, 0xE959, 0x9585,\n\t0xE95A, 0x9586, 0xE95B, 0x9587, 0xE95C, 0x9588, 0xE95D, 0x9589,\n\t0xE95E, 0x958A, 0xE95F, 0x958B, 0xE960, 0x958C, 0xE961, 0x958D,\n\t0xE962, 0x958E, 0xE963, 0x958F, 0xE964, 0x9590, 0xE965, 0x9591,\n\t0xE966, 0x9592, 0xE967, 0x9593, 0xE968, 0x9594, 0xE969, 0x9595,\n\t0xE96A, 0x9596, 0xE96B, 0x9597, 0xE96C, 0x9598, 0xE96D, 0x9599,\n\t0xE96E, 0x959A, 0xE96F, 0x959B, 0xE970, 0x959C, 0xE971, 0x959D,\n\t0xE972, 0x959E, 0xE973, 0x959F, 0xE974, 0x95A0, 0xE975, 0x95A1,\n\t0xE976, 0x95A2, 0xE977, 0x95A3, 0xE978, 0x95A4, 0xE979, 0x95A5,\n\t0xE97A, 0x95A6, 0xE97B, 0x95A7, 0xE97C, 0x95A8, 0xE97D, 0x95A9,\n\t0xE97E, 0x95AA, 0xE980, 0x95AB, 0xE981, 0x95AC, 0xE982, 0x95AD,\n\t0xE983, 0x95AE, 0xE984, 0x95AF, 0xE985, 0x95B0, 0xE986, 0x95B1,\n\t0xE987, 0x95B2, 0xE988, 0x95B3, 0xE989, 0x95B4, 0xE98A, 0x95B5,\n\t0xE98B, 0x95B6, 0xE98C, 0x95B7, 0xE98D, 0x95B8, 0xE98E, 0x95B9,\n\t0xE98F, 0x95BA, 0xE990, 0x95BB, 0xE991, 0x95BC, 0xE992, 0x95BD,\n\t0xE993, 0x95BE, 0xE994, 0x95BF, 0xE995, 0x95C0, 0xE996, 0x95C1,\n\t0xE997, 0x95C2, 0xE998, 0x95C3, 0xE999, 0x95C4, 0xE99A, 0x95C5,\n\t0xE99B, 0x95C6, 0xE99C, 0x95C7, 0xE99D, 0x95C8, 0xE99E, 0x95C9,\n\t0xE99F, 0x95CA, 0xE9A0, 0x95CB, 0xE9A1, 0x6924, 0xE9A2, 0x68F0,\n\t0xE9A3, 0x690B, 0xE9A4, 0x6901, 0xE9A5, 0x6957, 0xE9A6, 0x68E3,\n\t0xE9A7, 0x6910, 0xE9A8, 0x6971, 0xE9A9, 0x6939, 0xE9AA, 0x6960,\n\t0xE9AB, 0x6942, 0xE9AC, 0x695D, 0xE9AD, 0x6984, 0xE9AE, 0x696B,\n\t0xE9AF, 0x6980, 0xE9B0, 0x6998, 0xE9B1, 0x6978, 0xE9B2, 0x6934,\n\t0xE9B3, 0x69CC, 0xE9B4, 0x6987, 0xE9B5, 0x6988, 0xE9B6, 0x69CE,\n\t0xE9B7, 0x6989, 0xE9B8, 0x6966, 0xE9B9, 0x6963, 0xE9BA, 0x6979,\n\t0xE9BB, 0x699B, 0xE9BC, 0x69A7, 0xE9BD, 0x69BB, 0xE9BE, 0x69AB,\n\t0xE9BF, 0x69AD, 0xE9C0, 0x69D4, 0xE9C1, 0x69B1, 0xE9C2, 0x69C1,\n\t0xE9C3, 0x69CA, 0xE9C4, 0x69DF, 0xE9C5, 0x6995, 0xE9C6, 0x69E0,\n\t0xE9C7, 0x698D, 0xE9C8, 0x69FF, 0xE9C9, 0x6A2F, 0xE9CA, 0x69ED,\n\t0xE9CB, 0x6A17, 0xE9CC, 0x6A18, 0xE9CD, 0x6A65, 0xE9CE, 0x69F2,\n\t0xE9CF, 0x6A44, 0xE9D0, 0x6A3E, 0xE9D1, 0x6AA0, 0xE9D2, 0x6A50,\n\t0xE9D3, 0x6A5B, 0xE9D4, 0x6A35, 0xE9D5, 0x6A8E, 0xE9D6, 0x6A79,\n\t0xE9D7, 0x6A3D, 0xE9D8, 0x6A28, 0xE9D9, 0x6A58, 0xE9DA, 0x6A7C,\n\t0xE9DB, 0x6A91, 0xE9DC, 0x6A90, 0xE9DD, 0x6AA9, 0xE9DE, 0x6A97,\n\t0xE9DF, 0x6AAB, 0xE9E0, 0x7337, 0xE9E1, 0x7352, 0xE9E2, 0x6B81,\n\t0xE9E3, 0x6B82, 0xE9E4, 0x6B87, 0xE9E5, 0x6B84, 0xE9E6, 0x6B92,\n\t0xE9E7, 0x6B93, 0xE9E8, 0x6B8D, 0xE9E9, 0x6B9A, 0xE9EA, 0x6B9B,\n\t0xE9EB, 0x6BA1, 0xE9EC, 0x6BAA, 0xE9ED, 0x8F6B, 0xE9EE, 0x8F6D,\n\t0xE9EF, 0x8F71, 0xE9F0, 0x8F72, 0xE9F1, 0x8F73, 0xE9F2, 0x8F75,\n\t0xE9F3, 0x8F76, 0xE9F4, 0x8F78, 0xE9F5, 0x8F77, 0xE9F6, 0x8F79,\n\t0xE9F7, 0x8F7A, 0xE9F8, 0x8F7C, 0xE9F9, 0x8F7E, 0xE9FA, 0x8F81,\n\t0xE9FB, 0x8F82, 0xE9FC, 0x8F84, 0xE9FD, 0x8F87, 0xE9FE, 0x8F8B,\n\t0xEA40, 0x95CC, 0xEA41, 0x95CD, 0xEA42, 0x95CE, 0xEA43, 0x95CF,\n\t0xEA44, 0x95D0, 0xEA45, 0x95D1, 0xEA46, 0x95D2, 0xEA47, 0x95D3,\n\t0xEA48, 0x95D4, 0xEA49, 0x95D5, 0xEA4A, 0x95D6, 0xEA4B, 0x95D7,\n\t0xEA4C, 0x95D8, 0xEA4D, 0x95D9, 0xEA4E, 0x95DA, 0xEA4F, 0x95DB,\n\t0xEA50, 0x95DC, 0xEA51, 0x95DD, 0xEA52, 0x95DE, 0xEA53, 0x95DF,\n\t0xEA54, 0x95E0, 0xEA55, 0x95E1, 0xEA56, 0x95E2, 0xEA57, 0x95E3,\n\t0xEA58, 0x95E4, 0xEA59, 0x95E5, 0xEA5A, 0x95E6, 0xEA5B, 0x95E7,\n\t0xEA5C, 0x95EC, 0xEA5D, 0x95FF, 0xEA5E, 0x9607, 0xEA5F, 0x9613,\n\t0xEA60, 0x9618, 0xEA61, 0x961B, 0xEA62, 0x961E, 0xEA63, 0x9620,\n\t0xEA64, 0x9623, 0xEA65, 0x9624, 0xEA66, 0x9625, 0xEA67, 0x9626,\n\t0xEA68, 0x9627, 0xEA69, 0x9628, 0xEA6A, 0x9629, 0xEA6B, 0x962B,\n\t0xEA6C, 0x962C, 0xEA6D, 0x962D, 0xEA6E, 0x962F, 0xEA6F, 0x9630,\n\t0xEA70, 0x9637, 0xEA71, 0x9638, 0xEA72, 0x9639, 0xEA73, 0x963A,\n\t0xEA74, 0x963E, 0xEA75, 0x9641, 0xEA76, 0x9643, 0xEA77, 0x964A,\n\t0xEA78, 0x964E, 0xEA79, 0x964F, 0xEA7A, 0x9651, 0xEA7B, 0x9652,\n\t0xEA7C, 0x9653, 0xEA7D, 0x9656, 0xEA7E, 0x9657, 0xEA80, 0x9658,\n\t0xEA81, 0x9659, 0xEA82, 0x965A, 0xEA83, 0x965C, 0xEA84, 0x965D,\n\t0xEA85, 0x965E, 0xEA86, 0x9660, 0xEA87, 0x9663, 0xEA88, 0x9665,\n\t0xEA89, 0x9666, 0xEA8A, 0x966B, 0xEA8B, 0x966D, 0xEA8C, 0x966E,\n\t0xEA8D, 0x966F, 0xEA8E, 0x9670, 0xEA8F, 0x9671, 0xEA90, 0x9673,\n\t0xEA91, 0x9678, 0xEA92, 0x9679, 0xEA93, 0x967A, 0xEA94, 0x967B,\n\t0xEA95, 0x967C, 0xEA96, 0x967D, 0xEA97, 0x967E, 0xEA98, 0x967F,\n\t0xEA99, 0x9680, 0xEA9A, 0x9681, 0xEA9B, 0x9682, 0xEA9C, 0x9683,\n\t0xEA9D, 0x9684, 0xEA9E, 0x9687, 0xEA9F, 0x9689, 0xEAA0, 0x968A,\n\t0xEAA1, 0x8F8D, 0xEAA2, 0x8F8E, 0xEAA3, 0x8F8F, 0xEAA4, 0x8F98,\n\t0xEAA5, 0x8F9A, 0xEAA6, 0x8ECE, 0xEAA7, 0x620B, 0xEAA8, 0x6217,\n\t0xEAA9, 0x621B, 0xEAAA, 0x621F, 0xEAAB, 0x6222, 0xEAAC, 0x6221,\n\t0xEAAD, 0x6225, 0xEAAE, 0x6224, 0xEAAF, 0x622C, 0xEAB0, 0x81E7,\n\t0xEAB1, 0x74EF, 0xEAB2, 0x74F4, 0xEAB3, 0x74FF, 0xEAB4, 0x750F,\n\t0xEAB5, 0x7511, 0xEAB6, 0x7513, 0xEAB7, 0x6534, 0xEAB8, 0x65EE,\n\t0xEAB9, 0x65EF, 0xEABA, 0x65F0, 0xEABB, 0x660A, 0xEABC, 0x6619,\n\t0xEABD, 0x6772, 0xEABE, 0x6603, 0xEABF, 0x6615, 0xEAC0, 0x6600,\n\t0xEAC1, 0x7085, 0xEAC2, 0x66F7, 0xEAC3, 0x661D, 0xEAC4, 0x6634,\n\t0xEAC5, 0x6631, 0xEAC6, 0x6636, 0xEAC7, 0x6635, 0xEAC8, 0x8006,\n\t0xEAC9, 0x665F, 0xEACA, 0x6654, 0xEACB, 0x6641, 0xEACC, 0x664F,\n\t0xEACD, 0x6656, 0xEACE, 0x6661, 0xEACF, 0x6657, 0xEAD0, 0x6677,\n\t0xEAD1, 0x6684, 0xEAD2, 0x668C, 0xEAD3, 0x66A7, 0xEAD4, 0x669D,\n\t0xEAD5, 0x66BE, 0xEAD6, 0x66DB, 0xEAD7, 0x66DC, 0xEAD8, 0x66E6,\n\t0xEAD9, 0x66E9, 0xEADA, 0x8D32, 0xEADB, 0x8D33, 0xEADC, 0x8D36,\n\t0xEADD, 0x8D3B, 0xEADE, 0x8D3D, 0xEADF, 0x8D40, 0xEAE0, 0x8D45,\n\t0xEAE1, 0x8D46, 0xEAE2, 0x8D48, 0xEAE3, 0x8D49, 0xEAE4, 0x8D47,\n\t0xEAE5, 0x8D4D, 0xEAE6, 0x8D55, 0xEAE7, 0x8D59, 0xEAE8, 0x89C7,\n\t0xEAE9, 0x89CA, 0xEAEA, 0x89CB, 0xEAEB, 0x89CC, 0xEAEC, 0x89CE,\n\t0xEAED, 0x89CF, 0xEAEE, 0x89D0, 0xEAEF, 0x89D1, 0xEAF0, 0x726E,\n\t0xEAF1, 0x729F, 0xEAF2, 0x725D, 0xEAF3, 0x7266, 0xEAF4, 0x726F,\n\t0xEAF5, 0x727E, 0xEAF6, 0x727F, 0xEAF7, 0x7284, 0xEAF8, 0x728B,\n\t0xEAF9, 0x728D, 0xEAFA, 0x728F, 0xEAFB, 0x7292, 0xEAFC, 0x6308,\n\t0xEAFD, 0x6332, 0xEAFE, 0x63B0, 0xEB40, 0x968C, 0xEB41, 0x968E,\n\t0xEB42, 0x9691, 0xEB43, 0x9692, 0xEB44, 0x9693, 0xEB45, 0x9695,\n\t0xEB46, 0x9696, 0xEB47, 0x969A, 0xEB48, 0x969B, 0xEB49, 0x969D,\n\t0xEB4A, 0x969E, 0xEB4B, 0x969F, 0xEB4C, 0x96A0, 0xEB4D, 0x96A1,\n\t0xEB4E, 0x96A2, 0xEB4F, 0x96A3, 0xEB50, 0x96A4, 0xEB51, 0x96A5,\n\t0xEB52, 0x96A6, 0xEB53, 0x96A8, 0xEB54, 0x96A9, 0xEB55, 0x96AA,\n\t0xEB56, 0x96AB, 0xEB57, 0x96AC, 0xEB58, 0x96AD, 0xEB59, 0x96AE,\n\t0xEB5A, 0x96AF, 0xEB5B, 0x96B1, 0xEB5C, 0x96B2, 0xEB5D, 0x96B4,\n\t0xEB5E, 0x96B5, 0xEB5F, 0x96B7, 0xEB60, 0x96B8, 0xEB61, 0x96BA,\n\t0xEB62, 0x96BB, 0xEB63, 0x96BF, 0xEB64, 0x96C2, 0xEB65, 0x96C3,\n\t0xEB66, 0x96C8, 0xEB67, 0x96CA, 0xEB68, 0x96CB, 0xEB69, 0x96D0,\n\t0xEB6A, 0x96D1, 0xEB6B, 0x96D3, 0xEB6C, 0x96D4, 0xEB6D, 0x96D6,\n\t0xEB6E, 0x96D7, 0xEB6F, 0x96D8, 0xEB70, 0x96D9, 0xEB71, 0x96DA,\n\t0xEB72, 0x96DB, 0xEB73, 0x96DC, 0xEB74, 0x96DD, 0xEB75, 0x96DE,\n\t0xEB76, 0x96DF, 0xEB77, 0x96E1, 0xEB78, 0x96E2, 0xEB79, 0x96E3,\n\t0xEB7A, 0x96E4, 0xEB7B, 0x96E5, 0xEB7C, 0x96E6, 0xEB7D, 0x96E7,\n\t0xEB7E, 0x96EB, 0xEB80, 0x96EC, 0xEB81, 0x96ED, 0xEB82, 0x96EE,\n\t0xEB83, 0x96F0, 0xEB84, 0x96F1, 0xEB85, 0x96F2, 0xEB86, 0x96F4,\n\t0xEB87, 0x96F5, 0xEB88, 0x96F8, 0xEB89, 0x96FA, 0xEB8A, 0x96FB,\n\t0xEB8B, 0x96FC, 0xEB8C, 0x96FD, 0xEB8D, 0x96FF, 0xEB8E, 0x9702,\n\t0xEB8F, 0x9703, 0xEB90, 0x9705, 0xEB91, 0x970A, 0xEB92, 0x970B,\n\t0xEB93, 0x970C, 0xEB94, 0x9710, 0xEB95, 0x9711, 0xEB96, 0x9712,\n\t0xEB97, 0x9714, 0xEB98, 0x9715, 0xEB99, 0x9717, 0xEB9A, 0x9718,\n\t0xEB9B, 0x9719, 0xEB9C, 0x971A, 0xEB9D, 0x971B, 0xEB9E, 0x971D,\n\t0xEB9F, 0x971F, 0xEBA0, 0x9720, 0xEBA1, 0x643F, 0xEBA2, 0x64D8,\n\t0xEBA3, 0x8004, 0xEBA4, 0x6BEA, 0xEBA5, 0x6BF3, 0xEBA6, 0x6BFD,\n\t0xEBA7, 0x6BF5, 0xEBA8, 0x6BF9, 0xEBA9, 0x6C05, 0xEBAA, 0x6C07,\n\t0xEBAB, 0x6C06, 0xEBAC, 0x6C0D, 0xEBAD, 0x6C15, 0xEBAE, 0x6C18,\n\t0xEBAF, 0x6C19, 0xEBB0, 0x6C1A, 0xEBB1, 0x6C21, 0xEBB2, 0x6C29,\n\t0xEBB3, 0x6C24, 0xEBB4, 0x6C2A, 0xEBB5, 0x6C32, 0xEBB6, 0x6535,\n\t0xEBB7, 0x6555, 0xEBB8, 0x656B, 0xEBB9, 0x724D, 0xEBBA, 0x7252,\n\t0xEBBB, 0x7256, 0xEBBC, 0x7230, 0xEBBD, 0x8662, 0xEBBE, 0x5216,\n\t0xEBBF, 0x809F, 0xEBC0, 0x809C, 0xEBC1, 0x8093, 0xEBC2, 0x80BC,\n\t0xEBC3, 0x670A, 0xEBC4, 0x80BD, 0xEBC5, 0x80B1, 0xEBC6, 0x80AB,\n\t0xEBC7, 0x80AD, 0xEBC8, 0x80B4, 0xEBC9, 0x80B7, 0xEBCA, 0x80E7,\n\t0xEBCB, 0x80E8, 0xEBCC, 0x80E9, 0xEBCD, 0x80EA, 0xEBCE, 0x80DB,\n\t0xEBCF, 0x80C2, 0xEBD0, 0x80C4, 0xEBD1, 0x80D9, 0xEBD2, 0x80CD,\n\t0xEBD3, 0x80D7, 0xEBD4, 0x6710, 0xEBD5, 0x80DD, 0xEBD6, 0x80EB,\n\t0xEBD7, 0x80F1, 0xEBD8, 0x80F4, 0xEBD9, 0x80ED, 0xEBDA, 0x810D,\n\t0xEBDB, 0x810E, 0xEBDC, 0x80F2, 0xEBDD, 0x80FC, 0xEBDE, 0x6715,\n\t0xEBDF, 0x8112, 0xEBE0, 0x8C5A, 0xEBE1, 0x8136, 0xEBE2, 0x811E,\n\t0xEBE3, 0x812C, 0xEBE4, 0x8118, 0xEBE5, 0x8132, 0xEBE6, 0x8148,\n\t0xEBE7, 0x814C, 0xEBE8, 0x8153, 0xEBE9, 0x8174, 0xEBEA, 0x8159,\n\t0xEBEB, 0x815A, 0xEBEC, 0x8171, 0xEBED, 0x8160, 0xEBEE, 0x8169,\n\t0xEBEF, 0x817C, 0xEBF0, 0x817D, 0xEBF1, 0x816D, 0xEBF2, 0x8167,\n\t0xEBF3, 0x584D, 0xEBF4, 0x5AB5, 0xEBF5, 0x8188, 0xEBF6, 0x8182,\n\t0xEBF7, 0x8191, 0xEBF8, 0x6ED5, 0xEBF9, 0x81A3, 0xEBFA, 0x81AA,\n\t0xEBFB, 0x81CC, 0xEBFC, 0x6726, 0xEBFD, 0x81CA, 0xEBFE, 0x81BB,\n\t0xEC40, 0x9721, 0xEC41, 0x9722, 0xEC42, 0x9723, 0xEC43, 0x9724,\n\t0xEC44, 0x9725, 0xEC45, 0x9726, 0xEC46, 0x9727, 0xEC47, 0x9728,\n\t0xEC48, 0x9729, 0xEC49, 0x972B, 0xEC4A, 0x972C, 0xEC4B, 0x972E,\n\t0xEC4C, 0x972F, 0xEC4D, 0x9731, 0xEC4E, 0x9733, 0xEC4F, 0x9734,\n\t0xEC50, 0x9735, 0xEC51, 0x9736, 0xEC52, 0x9737, 0xEC53, 0x973A,\n\t0xEC54, 0x973B, 0xEC55, 0x973C, 0xEC56, 0x973D, 0xEC57, 0x973F,\n\t0xEC58, 0x9740, 0xEC59, 0x9741, 0xEC5A, 0x9742, 0xEC5B, 0x9743,\n\t0xEC5C, 0x9744, 0xEC5D, 0x9745, 0xEC5E, 0x9746, 0xEC5F, 0x9747,\n\t0xEC60, 0x9748, 0xEC61, 0x9749, 0xEC62, 0x974A, 0xEC63, 0x974B,\n\t0xEC64, 0x974C, 0xEC65, 0x974D, 0xEC66, 0x974E, 0xEC67, 0x974F,\n\t0xEC68, 0x9750, 0xEC69, 0x9751, 0xEC6A, 0x9754, 0xEC6B, 0x9755,\n\t0xEC6C, 0x9757, 0xEC6D, 0x9758, 0xEC6E, 0x975A, 0xEC6F, 0x975C,\n\t0xEC70, 0x975D, 0xEC71, 0x975F, 0xEC72, 0x9763, 0xEC73, 0x9764,\n\t0xEC74, 0x9766, 0xEC75, 0x9767, 0xEC76, 0x9768, 0xEC77, 0x976A,\n\t0xEC78, 0x976B, 0xEC79, 0x976C, 0xEC7A, 0x976D, 0xEC7B, 0x976E,\n\t0xEC7C, 0x976F, 0xEC7D, 0x9770, 0xEC7E, 0x9771, 0xEC80, 0x9772,\n\t0xEC81, 0x9775, 0xEC82, 0x9777, 0xEC83, 0x9778, 0xEC84, 0x9779,\n\t0xEC85, 0x977A, 0xEC86, 0x977B, 0xEC87, 0x977D, 0xEC88, 0x977E,\n\t0xEC89, 0x977F, 0xEC8A, 0x9780, 0xEC8B, 0x9781, 0xEC8C, 0x9782,\n\t0xEC8D, 0x9783, 0xEC8E, 0x9784, 0xEC8F, 0x9786, 0xEC90, 0x9787,\n\t0xEC91, 0x9788, 0xEC92, 0x9789, 0xEC93, 0x978A, 0xEC94, 0x978C,\n\t0xEC95, 0x978E, 0xEC96, 0x978F, 0xEC97, 0x9790, 0xEC98, 0x9793,\n\t0xEC99, 0x9795, 0xEC9A, 0x9796, 0xEC9B, 0x9797, 0xEC9C, 0x9799,\n\t0xEC9D, 0x979A, 0xEC9E, 0x979B, 0xEC9F, 0x979C, 0xECA0, 0x979D,\n\t0xECA1, 0x81C1, 0xECA2, 0x81A6, 0xECA3, 0x6B24, 0xECA4, 0x6B37,\n\t0xECA5, 0x6B39, 0xECA6, 0x6B43, 0xECA7, 0x6B46, 0xECA8, 0x6B59,\n\t0xECA9, 0x98D1, 0xECAA, 0x98D2, 0xECAB, 0x98D3, 0xECAC, 0x98D5,\n\t0xECAD, 0x98D9, 0xECAE, 0x98DA, 0xECAF, 0x6BB3, 0xECB0, 0x5F40,\n\t0xECB1, 0x6BC2, 0xECB2, 0x89F3, 0xECB3, 0x6590, 0xECB4, 0x9F51,\n\t0xECB5, 0x6593, 0xECB6, 0x65BC, 0xECB7, 0x65C6, 0xECB8, 0x65C4,\n\t0xECB9, 0x65C3, 0xECBA, 0x65CC, 0xECBB, 0x65CE, 0xECBC, 0x65D2,\n\t0xECBD, 0x65D6, 0xECBE, 0x7080, 0xECBF, 0x709C, 0xECC0, 0x7096,\n\t0xECC1, 0x709D, 0xECC2, 0x70BB, 0xECC3, 0x70C0, 0xECC4, 0x70B7,\n\t0xECC5, 0x70AB, 0xECC6, 0x70B1, 0xECC7, 0x70E8, 0xECC8, 0x70CA,\n\t0xECC9, 0x7110, 0xECCA, 0x7113, 0xECCB, 0x7116, 0xECCC, 0x712F,\n\t0xECCD, 0x7131, 0xECCE, 0x7173, 0xECCF, 0x715C, 0xECD0, 0x7168,\n\t0xECD1, 0x7145, 0xECD2, 0x7172, 0xECD3, 0x714A, 0xECD4, 0x7178,\n\t0xECD5, 0x717A, 0xECD6, 0x7198, 0xECD7, 0x71B3, 0xECD8, 0x71B5,\n\t0xECD9, 0x71A8, 0xECDA, 0x71A0, 0xECDB, 0x71E0, 0xECDC, 0x71D4,\n\t0xECDD, 0x71E7, 0xECDE, 0x71F9, 0xECDF, 0x721D, 0xECE0, 0x7228,\n\t0xECE1, 0x706C, 0xECE2, 0x7118, 0xECE3, 0x7166, 0xECE4, 0x71B9,\n\t0xECE5, 0x623E, 0xECE6, 0x623D, 0xECE7, 0x6243, 0xECE8, 0x6248,\n\t0xECE9, 0x6249, 0xECEA, 0x793B, 0xECEB, 0x7940, 0xECEC, 0x7946,\n\t0xECED, 0x7949, 0xECEE, 0x795B, 0xECEF, 0x795C, 0xECF0, 0x7953,\n\t0xECF1, 0x795A, 0xECF2, 0x7962, 0xECF3, 0x7957, 0xECF4, 0x7960,\n\t0xECF5, 0x796F, 0xECF6, 0x7967, 0xECF7, 0x797A, 0xECF8, 0x7985,\n\t0xECF9, 0x798A, 0xECFA, 0x799A, 0xECFB, 0x79A7, 0xECFC, 0x79B3,\n\t0xECFD, 0x5FD1, 0xECFE, 0x5FD0, 0xED40, 0x979E, 0xED41, 0x979F,\n\t0xED42, 0x97A1, 0xED43, 0x97A2, 0xED44, 0x97A4, 0xED45, 0x97A5,\n\t0xED46, 0x97A6, 0xED47, 0x97A7, 0xED48, 0x97A8, 0xED49, 0x97A9,\n\t0xED4A, 0x97AA, 0xED4B, 0x97AC, 0xED4C, 0x97AE, 0xED4D, 0x97B0,\n\t0xED4E, 0x97B1, 0xED4F, 0x97B3, 0xED50, 0x97B5, 0xED51, 0x97B6,\n\t0xED52, 0x97B7, 0xED53, 0x97B8, 0xED54, 0x97B9, 0xED55, 0x97BA,\n\t0xED56, 0x97BB, 0xED57, 0x97BC, 0xED58, 0x97BD, 0xED59, 0x97BE,\n\t0xED5A, 0x97BF, 0xED5B, 0x97C0, 0xED5C, 0x97C1, 0xED5D, 0x97C2,\n\t0xED5E, 0x97C3, 0xED5F, 0x97C4, 0xED60, 0x97C5, 0xED61, 0x97C6,\n\t0xED62, 0x97C7, 0xED63, 0x97C8, 0xED64, 0x97C9, 0xED65, 0x97CA,\n\t0xED66, 0x97CB, 0xED67, 0x97CC, 0xED68, 0x97CD, 0xED69, 0x97CE,\n\t0xED6A, 0x97CF, 0xED6B, 0x97D0, 0xED6C, 0x97D1, 0xED6D, 0x97D2,\n\t0xED6E, 0x97D3, 0xED6F, 0x97D4, 0xED70, 0x97D5, 0xED71, 0x97D6,\n\t0xED72, 0x97D7, 0xED73, 0x97D8, 0xED74, 0x97D9, 0xED75, 0x97DA,\n\t0xED76, 0x97DB, 0xED77, 0x97DC, 0xED78, 0x97DD, 0xED79, 0x97DE,\n\t0xED7A, 0x97DF, 0xED7B, 0x97E0, 0xED7C, 0x97E1, 0xED7D, 0x97E2,\n\t0xED7E, 0x97E3, 0xED80, 0x97E4, 0xED81, 0x97E5, 0xED82, 0x97E8,\n\t0xED83, 0x97EE, 0xED84, 0x97EF, 0xED85, 0x97F0, 0xED86, 0x97F1,\n\t0xED87, 0x97F2, 0xED88, 0x97F4, 0xED89, 0x97F7, 0xED8A, 0x97F8,\n\t0xED8B, 0x97F9, 0xED8C, 0x97FA, 0xED8D, 0x97FB, 0xED8E, 0x97FC,\n\t0xED8F, 0x97FD, 0xED90, 0x97FE, 0xED91, 0x97FF, 0xED92, 0x9800,\n\t0xED93, 0x9801, 0xED94, 0x9802, 0xED95, 0x9803, 0xED96, 0x9804,\n\t0xED97, 0x9805, 0xED98, 0x9806, 0xED99, 0x9807, 0xED9A, 0x9808,\n\t0xED9B, 0x9809, 0xED9C, 0x980A, 0xED9D, 0x980B, 0xED9E, 0x980C,\n\t0xED9F, 0x980D, 0xEDA0, 0x980E, 0xEDA1, 0x603C, 0xEDA2, 0x605D,\n\t0xEDA3, 0x605A, 0xEDA4, 0x6067, 0xEDA5, 0x6041, 0xEDA6, 0x6059,\n\t0xEDA7, 0x6063, 0xEDA8, 0x60AB, 0xEDA9, 0x6106, 0xEDAA, 0x610D,\n\t0xEDAB, 0x615D, 0xEDAC, 0x61A9, 0xEDAD, 0x619D, 0xEDAE, 0x61CB,\n\t0xEDAF, 0x61D1, 0xEDB0, 0x6206, 0xEDB1, 0x8080, 0xEDB2, 0x807F,\n\t0xEDB3, 0x6C93, 0xEDB4, 0x6CF6, 0xEDB5, 0x6DFC, 0xEDB6, 0x77F6,\n\t0xEDB7, 0x77F8, 0xEDB8, 0x7800, 0xEDB9, 0x7809, 0xEDBA, 0x7817,\n\t0xEDBB, 0x7818, 0xEDBC, 0x7811, 0xEDBD, 0x65AB, 0xEDBE, 0x782D,\n\t0xEDBF, 0x781C, 0xEDC0, 0x781D, 0xEDC1, 0x7839, 0xEDC2, 0x783A,\n\t0xEDC3, 0x783B, 0xEDC4, 0x781F, 0xEDC5, 0x783C, 0xEDC6, 0x7825,\n\t0xEDC7, 0x782C, 0xEDC8, 0x7823, 0xEDC9, 0x7829, 0xEDCA, 0x784E,\n\t0xEDCB, 0x786D, 0xEDCC, 0x7856, 0xEDCD, 0x7857, 0xEDCE, 0x7826,\n\t0xEDCF, 0x7850, 0xEDD0, 0x7847, 0xEDD1, 0x784C, 0xEDD2, 0x786A,\n\t0xEDD3, 0x789B, 0xEDD4, 0x7893, 0xEDD5, 0x789A, 0xEDD6, 0x7887,\n\t0xEDD7, 0x789C, 0xEDD8, 0x78A1, 0xEDD9, 0x78A3, 0xEDDA, 0x78B2,\n\t0xEDDB, 0x78B9, 0xEDDC, 0x78A5, 0xEDDD, 0x78D4, 0xEDDE, 0x78D9,\n\t0xEDDF, 0x78C9, 0xEDE0, 0x78EC, 0xEDE1, 0x78F2, 0xEDE2, 0x7905,\n\t0xEDE3, 0x78F4, 0xEDE4, 0x7913, 0xEDE5, 0x7924, 0xEDE6, 0x791E,\n\t0xEDE7, 0x7934, 0xEDE8, 0x9F9B, 0xEDE9, 0x9EF9, 0xEDEA, 0x9EFB,\n\t0xEDEB, 0x9EFC, 0xEDEC, 0x76F1, 0xEDED, 0x7704, 0xEDEE, 0x770D,\n\t0xEDEF, 0x76F9, 0xEDF0, 0x7707, 0xEDF1, 0x7708, 0xEDF2, 0x771A,\n\t0xEDF3, 0x7722, 0xEDF4, 0x7719, 0xEDF5, 0x772D, 0xEDF6, 0x7726,\n\t0xEDF7, 0x7735, 0xEDF8, 0x7738, 0xEDF9, 0x7750, 0xEDFA, 0x7751,\n\t0xEDFB, 0x7747, 0xEDFC, 0x7743, 0xEDFD, 0x775A, 0xEDFE, 0x7768,\n\t0xEE40, 0x980F, 0xEE41, 0x9810, 0xEE42, 0x9811, 0xEE43, 0x9812,\n\t0xEE44, 0x9813, 0xEE45, 0x9814, 0xEE46, 0x9815, 0xEE47, 0x9816,\n\t0xEE48, 0x9817, 0xEE49, 0x9818, 0xEE4A, 0x9819, 0xEE4B, 0x981A,\n\t0xEE4C, 0x981B, 0xEE4D, 0x981C, 0xEE4E, 0x981D, 0xEE4F, 0x981E,\n\t0xEE50, 0x981F, 0xEE51, 0x9820, 0xEE52, 0x9821, 0xEE53, 0x9822,\n\t0xEE54, 0x9823, 0xEE55, 0x9824, 0xEE56, 0x9825, 0xEE57, 0x9826,\n\t0xEE58, 0x9827, 0xEE59, 0x9828, 0xEE5A, 0x9829, 0xEE5B, 0x982A,\n\t0xEE5C, 0x982B, 0xEE5D, 0x982C, 0xEE5E, 0x982D, 0xEE5F, 0x982E,\n\t0xEE60, 0x982F, 0xEE61, 0x9830, 0xEE62, 0x9831, 0xEE63, 0x9832,\n\t0xEE64, 0x9833, 0xEE65, 0x9834, 0xEE66, 0x9835, 0xEE67, 0x9836,\n\t0xEE68, 0x9837, 0xEE69, 0x9838, 0xEE6A, 0x9839, 0xEE6B, 0x983A,\n\t0xEE6C, 0x983B, 0xEE6D, 0x983C, 0xEE6E, 0x983D, 0xEE6F, 0x983E,\n\t0xEE70, 0x983F, 0xEE71, 0x9840, 0xEE72, 0x9841, 0xEE73, 0x9842,\n\t0xEE74, 0x9843, 0xEE75, 0x9844, 0xEE76, 0x9845, 0xEE77, 0x9846,\n\t0xEE78, 0x9847, 0xEE79, 0x9848, 0xEE7A, 0x9849, 0xEE7B, 0x984A,\n\t0xEE7C, 0x984B, 0xEE7D, 0x984C, 0xEE7E, 0x984D, 0xEE80, 0x984E,\n\t0xEE81, 0x984F, 0xEE82, 0x9850, 0xEE83, 0x9851, 0xEE84, 0x9852,\n\t0xEE85, 0x9853, 0xEE86, 0x9854, 0xEE87, 0x9855, 0xEE88, 0x9856,\n\t0xEE89, 0x9857, 0xEE8A, 0x9858, 0xEE8B, 0x9859, 0xEE8C, 0x985A,\n\t0xEE8D, 0x985B, 0xEE8E, 0x985C, 0xEE8F, 0x985D, 0xEE90, 0x985E,\n\t0xEE91, 0x985F, 0xEE92, 0x9860, 0xEE93, 0x9861, 0xEE94, 0x9862,\n\t0xEE95, 0x9863, 0xEE96, 0x9864, 0xEE97, 0x9865, 0xEE98, 0x9866,\n\t0xEE99, 0x9867, 0xEE9A, 0x9868, 0xEE9B, 0x9869, 0xEE9C, 0x986A,\n\t0xEE9D, 0x986B, 0xEE9E, 0x986C, 0xEE9F, 0x986D, 0xEEA0, 0x986E,\n\t0xEEA1, 0x7762, 0xEEA2, 0x7765, 0xEEA3, 0x777F, 0xEEA4, 0x778D,\n\t0xEEA5, 0x777D, 0xEEA6, 0x7780, 0xEEA7, 0x778C, 0xEEA8, 0x7791,\n\t0xEEA9, 0x779F, 0xEEAA, 0x77A0, 0xEEAB, 0x77B0, 0xEEAC, 0x77B5,\n\t0xEEAD, 0x77BD, 0xEEAE, 0x753A, 0xEEAF, 0x7540, 0xEEB0, 0x754E,\n\t0xEEB1, 0x754B, 0xEEB2, 0x7548, 0xEEB3, 0x755B, 0xEEB4, 0x7572,\n\t0xEEB5, 0x7579, 0xEEB6, 0x7583, 0xEEB7, 0x7F58, 0xEEB8, 0x7F61,\n\t0xEEB9, 0x7F5F, 0xEEBA, 0x8A48, 0xEEBB, 0x7F68, 0xEEBC, 0x7F74,\n\t0xEEBD, 0x7F71, 0xEEBE, 0x7F79, 0xEEBF, 0x7F81, 0xEEC0, 0x7F7E,\n\t0xEEC1, 0x76CD, 0xEEC2, 0x76E5, 0xEEC3, 0x8832, 0xEEC4, 0x9485,\n\t0xEEC5, 0x9486, 0xEEC6, 0x9487, 0xEEC7, 0x948B, 0xEEC8, 0x948A,\n\t0xEEC9, 0x948C, 0xEECA, 0x948D, 0xEECB, 0x948F, 0xEECC, 0x9490,\n\t0xEECD, 0x9494, 0xEECE, 0x9497, 0xEECF, 0x9495, 0xEED0, 0x949A,\n\t0xEED1, 0x949B, 0xEED2, 0x949C, 0xEED3, 0x94A3, 0xEED4, 0x94A4,\n\t0xEED5, 0x94AB, 0xEED6, 0x94AA, 0xEED7, 0x94AD, 0xEED8, 0x94AC,\n\t0xEED9, 0x94AF, 0xEEDA, 0x94B0, 0xEEDB, 0x94B2, 0xEEDC, 0x94B4,\n\t0xEEDD, 0x94B6, 0xEEDE, 0x94B7, 0xEEDF, 0x94B8, 0xEEE0, 0x94B9,\n\t0xEEE1, 0x94BA, 0xEEE2, 0x94BC, 0xEEE3, 0x94BD, 0xEEE4, 0x94BF,\n\t0xEEE5, 0x94C4, 0xEEE6, 0x94C8, 0xEEE7, 0x94C9, 0xEEE8, 0x94CA,\n\t0xEEE9, 0x94CB, 0xEEEA, 0x94CC, 0xEEEB, 0x94CD, 0xEEEC, 0x94CE,\n\t0xEEED, 0x94D0, 0xEEEE, 0x94D1, 0xEEEF, 0x94D2, 0xEEF0, 0x94D5,\n\t0xEEF1, 0x94D6, 0xEEF2, 0x94D7, 0xEEF3, 0x94D9, 0xEEF4, 0x94D8,\n\t0xEEF5, 0x94DB, 0xEEF6, 0x94DE, 0xEEF7, 0x94DF, 0xEEF8, 0x94E0,\n\t0xEEF9, 0x94E2, 0xEEFA, 0x94E4, 0xEEFB, 0x94E5, 0xEEFC, 0x94E7,\n\t0xEEFD, 0x94E8, 0xEEFE, 0x94EA, 0xEF40, 0x986F, 0xEF41, 0x9870,\n\t0xEF42, 0x9871, 0xEF43, 0x9872, 0xEF44, 0x9873, 0xEF45, 0x9874,\n\t0xEF46, 0x988B, 0xEF47, 0x988E, 0xEF48, 0x9892, 0xEF49, 0x9895,\n\t0xEF4A, 0x9899, 0xEF4B, 0x98A3, 0xEF4C, 0x98A8, 0xEF4D, 0x98A9,\n\t0xEF4E, 0x98AA, 0xEF4F, 0x98AB, 0xEF50, 0x98AC, 0xEF51, 0x98AD,\n\t0xEF52, 0x98AE, 0xEF53, 0x98AF, 0xEF54, 0x98B0, 0xEF55, 0x98B1,\n\t0xEF56, 0x98B2, 0xEF57, 0x98B3, 0xEF58, 0x98B4, 0xEF59, 0x98B5,\n\t0xEF5A, 0x98B6, 0xEF5B, 0x98B7, 0xEF5C, 0x98B8, 0xEF5D, 0x98B9,\n\t0xEF5E, 0x98BA, 0xEF5F, 0x98BB, 0xEF60, 0x98BC, 0xEF61, 0x98BD,\n\t0xEF62, 0x98BE, 0xEF63, 0x98BF, 0xEF64, 0x98C0, 0xEF65, 0x98C1,\n\t0xEF66, 0x98C2, 0xEF67, 0x98C3, 0xEF68, 0x98C4, 0xEF69, 0x98C5,\n\t0xEF6A, 0x98C6, 0xEF6B, 0x98C7, 0xEF6C, 0x98C8, 0xEF6D, 0x98C9,\n\t0xEF6E, 0x98CA, 0xEF6F, 0x98CB, 0xEF70, 0x98CC, 0xEF71, 0x98CD,\n\t0xEF72, 0x98CF, 0xEF73, 0x98D0, 0xEF74, 0x98D4, 0xEF75, 0x98D6,\n\t0xEF76, 0x98D7, 0xEF77, 0x98DB, 0xEF78, 0x98DC, 0xEF79, 0x98DD,\n\t0xEF7A, 0x98E0, 0xEF7B, 0x98E1, 0xEF7C, 0x98E2, 0xEF7D, 0x98E3,\n\t0xEF7E, 0x98E4, 0xEF80, 0x98E5, 0xEF81, 0x98E6, 0xEF82, 0x98E9,\n\t0xEF83, 0x98EA, 0xEF84, 0x98EB, 0xEF85, 0x98EC, 0xEF86, 0x98ED,\n\t0xEF87, 0x98EE, 0xEF88, 0x98EF, 0xEF89, 0x98F0, 0xEF8A, 0x98F1,\n\t0xEF8B, 0x98F2, 0xEF8C, 0x98F3, 0xEF8D, 0x98F4, 0xEF8E, 0x98F5,\n\t0xEF8F, 0x98F6, 0xEF90, 0x98F7, 0xEF91, 0x98F8, 0xEF92, 0x98F9,\n\t0xEF93, 0x98FA, 0xEF94, 0x98FB, 0xEF95, 0x98FC, 0xEF96, 0x98FD,\n\t0xEF97, 0x98FE, 0xEF98, 0x98FF, 0xEF99, 0x9900, 0xEF9A, 0x9901,\n\t0xEF9B, 0x9902, 0xEF9C, 0x9903, 0xEF9D, 0x9904, 0xEF9E, 0x9905,\n\t0xEF9F, 0x9906, 0xEFA0, 0x9907, 0xEFA1, 0x94E9, 0xEFA2, 0x94EB,\n\t0xEFA3, 0x94EE, 0xEFA4, 0x94EF, 0xEFA5, 0x94F3, 0xEFA6, 0x94F4,\n\t0xEFA7, 0x94F5, 0xEFA8, 0x94F7, 0xEFA9, 0x94F9, 0xEFAA, 0x94FC,\n\t0xEFAB, 0x94FD, 0xEFAC, 0x94FF, 0xEFAD, 0x9503, 0xEFAE, 0x9502,\n\t0xEFAF, 0x9506, 0xEFB0, 0x9507, 0xEFB1, 0x9509, 0xEFB2, 0x950A,\n\t0xEFB3, 0x950D, 0xEFB4, 0x950E, 0xEFB5, 0x950F, 0xEFB6, 0x9512,\n\t0xEFB7, 0x9513, 0xEFB8, 0x9514, 0xEFB9, 0x9515, 0xEFBA, 0x9516,\n\t0xEFBB, 0x9518, 0xEFBC, 0x951B, 0xEFBD, 0x951D, 0xEFBE, 0x951E,\n\t0xEFBF, 0x951F, 0xEFC0, 0x9522, 0xEFC1, 0x952A, 0xEFC2, 0x952B,\n\t0xEFC3, 0x9529, 0xEFC4, 0x952C, 0xEFC5, 0x9531, 0xEFC6, 0x9532,\n\t0xEFC7, 0x9534, 0xEFC8, 0x9536, 0xEFC9, 0x9537, 0xEFCA, 0x9538,\n\t0xEFCB, 0x953C, 0xEFCC, 0x953E, 0xEFCD, 0x953F, 0xEFCE, 0x9542,\n\t0xEFCF, 0x9535, 0xEFD0, 0x9544, 0xEFD1, 0x9545, 0xEFD2, 0x9546,\n\t0xEFD3, 0x9549, 0xEFD4, 0x954C, 0xEFD5, 0x954E, 0xEFD6, 0x954F,\n\t0xEFD7, 0x9552, 0xEFD8, 0x9553, 0xEFD9, 0x9554, 0xEFDA, 0x9556,\n\t0xEFDB, 0x9557, 0xEFDC, 0x9558, 0xEFDD, 0x9559, 0xEFDE, 0x955B,\n\t0xEFDF, 0x955E, 0xEFE0, 0x955F, 0xEFE1, 0x955D, 0xEFE2, 0x9561,\n\t0xEFE3, 0x9562, 0xEFE4, 0x9564, 0xEFE5, 0x9565, 0xEFE6, 0x9566,\n\t0xEFE7, 0x9567, 0xEFE8, 0x9568, 0xEFE9, 0x9569, 0xEFEA, 0x956A,\n\t0xEFEB, 0x956B, 0xEFEC, 0x956C, 0xEFED, 0x956F, 0xEFEE, 0x9571,\n\t0xEFEF, 0x9572, 0xEFF0, 0x9573, 0xEFF1, 0x953A, 0xEFF2, 0x77E7,\n\t0xEFF3, 0x77EC, 0xEFF4, 0x96C9, 0xEFF5, 0x79D5, 0xEFF6, 0x79ED,\n\t0xEFF7, 0x79E3, 0xEFF8, 0x79EB, 0xEFF9, 0x7A06, 0xEFFA, 0x5D47,\n\t0xEFFB, 0x7A03, 0xEFFC, 0x7A02, 0xEFFD, 0x7A1E, 0xEFFE, 0x7A14,\n\t0xF040, 0x9908, 0xF041, 0x9909, 0xF042, 0x990A, 0xF043, 0x990B,\n\t0xF044, 0x990C, 0xF045, 0x990E, 0xF046, 0x990F, 0xF047, 0x9911,\n\t0xF048, 0x9912, 0xF049, 0x9913, 0xF04A, 0x9914, 0xF04B, 0x9915,\n\t0xF04C, 0x9916, 0xF04D, 0x9917, 0xF04E, 0x9918, 0xF04F, 0x9919,\n\t0xF050, 0x991A, 0xF051, 0x991B, 0xF052, 0x991C, 0xF053, 0x991D,\n\t0xF054, 0x991E, 0xF055, 0x991F, 0xF056, 0x9920, 0xF057, 0x9921,\n\t0xF058, 0x9922, 0xF059, 0x9923, 0xF05A, 0x9924, 0xF05B, 0x9925,\n\t0xF05C, 0x9926, 0xF05D, 0x9927, 0xF05E, 0x9928, 0xF05F, 0x9929,\n\t0xF060, 0x992A, 0xF061, 0x992B, 0xF062, 0x992C, 0xF063, 0x992D,\n\t0xF064, 0x992F, 0xF065, 0x9930, 0xF066, 0x9931, 0xF067, 0x9932,\n\t0xF068, 0x9933, 0xF069, 0x9934, 0xF06A, 0x9935, 0xF06B, 0x9936,\n\t0xF06C, 0x9937, 0xF06D, 0x9938, 0xF06E, 0x9939, 0xF06F, 0x993A,\n\t0xF070, 0x993B, 0xF071, 0x993C, 0xF072, 0x993D, 0xF073, 0x993E,\n\t0xF074, 0x993F, 0xF075, 0x9940, 0xF076, 0x9941, 0xF077, 0x9942,\n\t0xF078, 0x9943, 0xF079, 0x9944, 0xF07A, 0x9945, 0xF07B, 0x9946,\n\t0xF07C, 0x9947, 0xF07D, 0x9948, 0xF07E, 0x9949, 0xF080, 0x994A,\n\t0xF081, 0x994B, 0xF082, 0x994C, 0xF083, 0x994D, 0xF084, 0x994E,\n\t0xF085, 0x994F, 0xF086, 0x9950, 0xF087, 0x9951, 0xF088, 0x9952,\n\t0xF089, 0x9953, 0xF08A, 0x9956, 0xF08B, 0x9957, 0xF08C, 0x9958,\n\t0xF08D, 0x9959, 0xF08E, 0x995A, 0xF08F, 0x995B, 0xF090, 0x995C,\n\t0xF091, 0x995D, 0xF092, 0x995E, 0xF093, 0x995F, 0xF094, 0x9960,\n\t0xF095, 0x9961, 0xF096, 0x9962, 0xF097, 0x9964, 0xF098, 0x9966,\n\t0xF099, 0x9973, 0xF09A, 0x9978, 0xF09B, 0x9979, 0xF09C, 0x997B,\n\t0xF09D, 0x997E, 0xF09E, 0x9982, 0xF09F, 0x9983, 0xF0A0, 0x9989,\n\t0xF0A1, 0x7A39, 0xF0A2, 0x7A37, 0xF0A3, 0x7A51, 0xF0A4, 0x9ECF,\n\t0xF0A5, 0x99A5, 0xF0A6, 0x7A70, 0xF0A7, 0x7688, 0xF0A8, 0x768E,\n\t0xF0A9, 0x7693, 0xF0AA, 0x7699, 0xF0AB, 0x76A4, 0xF0AC, 0x74DE,\n\t0xF0AD, 0x74E0, 0xF0AE, 0x752C, 0xF0AF, 0x9E20, 0xF0B0, 0x9E22,\n\t0xF0B1, 0x9E28, 0xF0B2, 0x9E29, 0xF0B3, 0x9E2A, 0xF0B4, 0x9E2B,\n\t0xF0B5, 0x9E2C, 0xF0B6, 0x9E32, 0xF0B7, 0x9E31, 0xF0B8, 0x9E36,\n\t0xF0B9, 0x9E38, 0xF0BA, 0x9E37, 0xF0BB, 0x9E39, 0xF0BC, 0x9E3A,\n\t0xF0BD, 0x9E3E, 0xF0BE, 0x9E41, 0xF0BF, 0x9E42, 0xF0C0, 0x9E44,\n\t0xF0C1, 0x9E46, 0xF0C2, 0x9E47, 0xF0C3, 0x9E48, 0xF0C4, 0x9E49,\n\t0xF0C5, 0x9E4B, 0xF0C6, 0x9E4C, 0xF0C7, 0x9E4E, 0xF0C8, 0x9E51,\n\t0xF0C9, 0x9E55, 0xF0CA, 0x9E57, 0xF0CB, 0x9E5A, 0xF0CC, 0x9E5B,\n\t0xF0CD, 0x9E5C, 0xF0CE, 0x9E5E, 0xF0CF, 0x9E63, 0xF0D0, 0x9E66,\n\t0xF0D1, 0x9E67, 0xF0D2, 0x9E68, 0xF0D3, 0x9E69, 0xF0D4, 0x9E6A,\n\t0xF0D5, 0x9E6B, 0xF0D6, 0x9E6C, 0xF0D7, 0x9E71, 0xF0D8, 0x9E6D,\n\t0xF0D9, 0x9E73, 0xF0DA, 0x7592, 0xF0DB, 0x7594, 0xF0DC, 0x7596,\n\t0xF0DD, 0x75A0, 0xF0DE, 0x759D, 0xF0DF, 0x75AC, 0xF0E0, 0x75A3,\n\t0xF0E1, 0x75B3, 0xF0E2, 0x75B4, 0xF0E3, 0x75B8, 0xF0E4, 0x75C4,\n\t0xF0E5, 0x75B1, 0xF0E6, 0x75B0, 0xF0E7, 0x75C3, 0xF0E8, 0x75C2,\n\t0xF0E9, 0x75D6, 0xF0EA, 0x75CD, 0xF0EB, 0x75E3, 0xF0EC, 0x75E8,\n\t0xF0ED, 0x75E6, 0xF0EE, 0x75E4, 0xF0EF, 0x75EB, 0xF0F0, 0x75E7,\n\t0xF0F1, 0x7603, 0xF0F2, 0x75F1, 0xF0F3, 0x75FC, 0xF0F4, 0x75FF,\n\t0xF0F5, 0x7610, 0xF0F6, 0x7600, 0xF0F7, 0x7605, 0xF0F8, 0x760C,\n\t0xF0F9, 0x7617, 0xF0FA, 0x760A, 0xF0FB, 0x7625, 0xF0FC, 0x7618,\n\t0xF0FD, 0x7615, 0xF0FE, 0x7619, 0xF140, 0x998C, 0xF141, 0x998E,\n\t0xF142, 0x999A, 0xF143, 0x999B, 0xF144, 0x999C, 0xF145, 0x999D,\n\t0xF146, 0x999E, 0xF147, 0x999F, 0xF148, 0x99A0, 0xF149, 0x99A1,\n\t0xF14A, 0x99A2, 0xF14B, 0x99A3, 0xF14C, 0x99A4, 0xF14D, 0x99A6,\n\t0xF14E, 0x99A7, 0xF14F, 0x99A9, 0xF150, 0x99AA, 0xF151, 0x99AB,\n\t0xF152, 0x99AC, 0xF153, 0x99AD, 0xF154, 0x99AE, 0xF155, 0x99AF,\n\t0xF156, 0x99B0, 0xF157, 0x99B1, 0xF158, 0x99B2, 0xF159, 0x99B3,\n\t0xF15A, 0x99B4, 0xF15B, 0x99B5, 0xF15C, 0x99B6, 0xF15D, 0x99B7,\n\t0xF15E, 0x99B8, 0xF15F, 0x99B9, 0xF160, 0x99BA, 0xF161, 0x99BB,\n\t0xF162, 0x99BC, 0xF163, 0x99BD, 0xF164, 0x99BE, 0xF165, 0x99BF,\n\t0xF166, 0x99C0, 0xF167, 0x99C1, 0xF168, 0x99C2, 0xF169, 0x99C3,\n\t0xF16A, 0x99C4, 0xF16B, 0x99C5, 0xF16C, 0x99C6, 0xF16D, 0x99C7,\n\t0xF16E, 0x99C8, 0xF16F, 0x99C9, 0xF170, 0x99CA, 0xF171, 0x99CB,\n\t0xF172, 0x99CC, 0xF173, 0x99CD, 0xF174, 0x99CE, 0xF175, 0x99CF,\n\t0xF176, 0x99D0, 0xF177, 0x99D1, 0xF178, 0x99D2, 0xF179, 0x99D3,\n\t0xF17A, 0x99D4, 0xF17B, 0x99D5, 0xF17C, 0x99D6, 0xF17D, 0x99D7,\n\t0xF17E, 0x99D8, 0xF180, 0x99D9, 0xF181, 0x99DA, 0xF182, 0x99DB,\n\t0xF183, 0x99DC, 0xF184, 0x99DD, 0xF185, 0x99DE, 0xF186, 0x99DF,\n\t0xF187, 0x99E0, 0xF188, 0x99E1, 0xF189, 0x99E2, 0xF18A, 0x99E3,\n\t0xF18B, 0x99E4, 0xF18C, 0x99E5, 0xF18D, 0x99E6, 0xF18E, 0x99E7,\n\t0xF18F, 0x99E8, 0xF190, 0x99E9, 0xF191, 0x99EA, 0xF192, 0x99EB,\n\t0xF193, 0x99EC, 0xF194, 0x99ED, 0xF195, 0x99EE, 0xF196, 0x99EF,\n\t0xF197, 0x99F0, 0xF198, 0x99F1, 0xF199, 0x99F2, 0xF19A, 0x99F3,\n\t0xF19B, 0x99F4, 0xF19C, 0x99F5, 0xF19D, 0x99F6, 0xF19E, 0x99F7,\n\t0xF19F, 0x99F8, 0xF1A0, 0x99F9, 0xF1A1, 0x761B, 0xF1A2, 0x763C,\n\t0xF1A3, 0x7622, 0xF1A4, 0x7620, 0xF1A5, 0x7640, 0xF1A6, 0x762D,\n\t0xF1A7, 0x7630, 0xF1A8, 0x763F, 0xF1A9, 0x7635, 0xF1AA, 0x7643,\n\t0xF1AB, 0x763E, 0xF1AC, 0x7633, 0xF1AD, 0x764D, 0xF1AE, 0x765E,\n\t0xF1AF, 0x7654, 0xF1B0, 0x765C, 0xF1B1, 0x7656, 0xF1B2, 0x766B,\n\t0xF1B3, 0x766F, 0xF1B4, 0x7FCA, 0xF1B5, 0x7AE6, 0xF1B6, 0x7A78,\n\t0xF1B7, 0x7A79, 0xF1B8, 0x7A80, 0xF1B9, 0x7A86, 0xF1BA, 0x7A88,\n\t0xF1BB, 0x7A95, 0xF1BC, 0x7AA6, 0xF1BD, 0x7AA0, 0xF1BE, 0x7AAC,\n\t0xF1BF, 0x7AA8, 0xF1C0, 0x7AAD, 0xF1C1, 0x7AB3, 0xF1C2, 0x8864,\n\t0xF1C3, 0x8869, 0xF1C4, 0x8872, 0xF1C5, 0x887D, 0xF1C6, 0x887F,\n\t0xF1C7, 0x8882, 0xF1C8, 0x88A2, 0xF1C9, 0x88C6, 0xF1CA, 0x88B7,\n\t0xF1CB, 0x88BC, 0xF1CC, 0x88C9, 0xF1CD, 0x88E2, 0xF1CE, 0x88CE,\n\t0xF1CF, 0x88E3, 0xF1D0, 0x88E5, 0xF1D1, 0x88F1, 0xF1D2, 0x891A,\n\t0xF1D3, 0x88FC, 0xF1D4, 0x88E8, 0xF1D5, 0x88FE, 0xF1D6, 0x88F0,\n\t0xF1D7, 0x8921, 0xF1D8, 0x8919, 0xF1D9, 0x8913, 0xF1DA, 0x891B,\n\t0xF1DB, 0x890A, 0xF1DC, 0x8934, 0xF1DD, 0x892B, 0xF1DE, 0x8936,\n\t0xF1DF, 0x8941, 0xF1E0, 0x8966, 0xF1E1, 0x897B, 0xF1E2, 0x758B,\n\t0xF1E3, 0x80E5, 0xF1E4, 0x76B2, 0xF1E5, 0x76B4, 0xF1E6, 0x77DC,\n\t0xF1E7, 0x8012, 0xF1E8, 0x8014, 0xF1E9, 0x8016, 0xF1EA, 0x801C,\n\t0xF1EB, 0x8020, 0xF1EC, 0x8022, 0xF1ED, 0x8025, 0xF1EE, 0x8026,\n\t0xF1EF, 0x8027, 0xF1F0, 0x8029, 0xF1F1, 0x8028, 0xF1F2, 0x8031,\n\t0xF1F3, 0x800B, 0xF1F4, 0x8035, 0xF1F5, 0x8043, 0xF1F6, 0x8046,\n\t0xF1F7, 0x804D, 0xF1F8, 0x8052, 0xF1F9, 0x8069, 0xF1FA, 0x8071,\n\t0xF1FB, 0x8983, 0xF1FC, 0x9878, 0xF1FD, 0x9880, 0xF1FE, 0x9883,\n\t0xF240, 0x99FA, 0xF241, 0x99FB, 0xF242, 0x99FC, 0xF243, 0x99FD,\n\t0xF244, 0x99FE, 0xF245, 0x99FF, 0xF246, 0x9A00, 0xF247, 0x9A01,\n\t0xF248, 0x9A02, 0xF249, 0x9A03, 0xF24A, 0x9A04, 0xF24B, 0x9A05,\n\t0xF24C, 0x9A06, 0xF24D, 0x9A07, 0xF24E, 0x9A08, 0xF24F, 0x9A09,\n\t0xF250, 0x9A0A, 0xF251, 0x9A0B, 0xF252, 0x9A0C, 0xF253, 0x9A0D,\n\t0xF254, 0x9A0E, 0xF255, 0x9A0F, 0xF256, 0x9A10, 0xF257, 0x9A11,\n\t0xF258, 0x9A12, 0xF259, 0x9A13, 0xF25A, 0x9A14, 0xF25B, 0x9A15,\n\t0xF25C, 0x9A16, 0xF25D, 0x9A17, 0xF25E, 0x9A18, 0xF25F, 0x9A19,\n\t0xF260, 0x9A1A, 0xF261, 0x9A1B, 0xF262, 0x9A1C, 0xF263, 0x9A1D,\n\t0xF264, 0x9A1E, 0xF265, 0x9A1F, 0xF266, 0x9A20, 0xF267, 0x9A21,\n\t0xF268, 0x9A22, 0xF269, 0x9A23, 0xF26A, 0x9A24, 0xF26B, 0x9A25,\n\t0xF26C, 0x9A26, 0xF26D, 0x9A27, 0xF26E, 0x9A28, 0xF26F, 0x9A29,\n\t0xF270, 0x9A2A, 0xF271, 0x9A2B, 0xF272, 0x9A2C, 0xF273, 0x9A2D,\n\t0xF274, 0x9A2E, 0xF275, 0x9A2F, 0xF276, 0x9A30, 0xF277, 0x9A31,\n\t0xF278, 0x9A32, 0xF279, 0x9A33, 0xF27A, 0x9A34, 0xF27B, 0x9A35,\n\t0xF27C, 0x9A36, 0xF27D, 0x9A37, 0xF27E, 0x9A38, 0xF280, 0x9A39,\n\t0xF281, 0x9A3A, 0xF282, 0x9A3B, 0xF283, 0x9A3C, 0xF284, 0x9A3D,\n\t0xF285, 0x9A3E, 0xF286, 0x9A3F, 0xF287, 0x9A40, 0xF288, 0x9A41,\n\t0xF289, 0x9A42, 0xF28A, 0x9A43, 0xF28B, 0x9A44, 0xF28C, 0x9A45,\n\t0xF28D, 0x9A46, 0xF28E, 0x9A47, 0xF28F, 0x9A48, 0xF290, 0x9A49,\n\t0xF291, 0x9A4A, 0xF292, 0x9A4B, 0xF293, 0x9A4C, 0xF294, 0x9A4D,\n\t0xF295, 0x9A4E, 0xF296, 0x9A4F, 0xF297, 0x9A50, 0xF298, 0x9A51,\n\t0xF299, 0x9A52, 0xF29A, 0x9A53, 0xF29B, 0x9A54, 0xF29C, 0x9A55,\n\t0xF29D, 0x9A56, 0xF29E, 0x9A57, 0xF29F, 0x9A58, 0xF2A0, 0x9A59,\n\t0xF2A1, 0x9889, 0xF2A2, 0x988C, 0xF2A3, 0x988D, 0xF2A4, 0x988F,\n\t0xF2A5, 0x9894, 0xF2A6, 0x989A, 0xF2A7, 0x989B, 0xF2A8, 0x989E,\n\t0xF2A9, 0x989F, 0xF2AA, 0x98A1, 0xF2AB, 0x98A2, 0xF2AC, 0x98A5,\n\t0xF2AD, 0x98A6, 0xF2AE, 0x864D, 0xF2AF, 0x8654, 0xF2B0, 0x866C,\n\t0xF2B1, 0x866E, 0xF2B2, 0x867F, 0xF2B3, 0x867A, 0xF2B4, 0x867C,\n\t0xF2B5, 0x867B, 0xF2B6, 0x86A8, 0xF2B7, 0x868D, 0xF2B8, 0x868B,\n\t0xF2B9, 0x86AC, 0xF2BA, 0x869D, 0xF2BB, 0x86A7, 0xF2BC, 0x86A3,\n\t0xF2BD, 0x86AA, 0xF2BE, 0x8693, 0xF2BF, 0x86A9, 0xF2C0, 0x86B6,\n\t0xF2C1, 0x86C4, 0xF2C2, 0x86B5, 0xF2C3, 0x86CE, 0xF2C4, 0x86B0,\n\t0xF2C5, 0x86BA, 0xF2C6, 0x86B1, 0xF2C7, 0x86AF, 0xF2C8, 0x86C9,\n\t0xF2C9, 0x86CF, 0xF2CA, 0x86B4, 0xF2CB, 0x86E9, 0xF2CC, 0x86F1,\n\t0xF2CD, 0x86F2, 0xF2CE, 0x86ED, 0xF2CF, 0x86F3, 0xF2D0, 0x86D0,\n\t0xF2D1, 0x8713, 0xF2D2, 0x86DE, 0xF2D3, 0x86F4, 0xF2D4, 0x86DF,\n\t0xF2D5, 0x86D8, 0xF2D6, 0x86D1, 0xF2D7, 0x8703, 0xF2D8, 0x8707,\n\t0xF2D9, 0x86F8, 0xF2DA, 0x8708, 0xF2DB, 0x870A, 0xF2DC, 0x870D,\n\t0xF2DD, 0x8709, 0xF2DE, 0x8723, 0xF2DF, 0x873B, 0xF2E0, 0x871E,\n\t0xF2E1, 0x8725, 0xF2E2, 0x872E, 0xF2E3, 0x871A, 0xF2E4, 0x873E,\n\t0xF2E5, 0x8748, 0xF2E6, 0x8734, 0xF2E7, 0x8731, 0xF2E8, 0x8729,\n\t0xF2E9, 0x8737, 0xF2EA, 0x873F, 0xF2EB, 0x8782, 0xF2EC, 0x8722,\n\t0xF2ED, 0x877D, 0xF2EE, 0x877E, 0xF2EF, 0x877B, 0xF2F0, 0x8760,\n\t0xF2F1, 0x8770, 0xF2F2, 0x874C, 0xF2F3, 0x876E, 0xF2F4, 0x878B,\n\t0xF2F5, 0x8753, 0xF2F6, 0x8763, 0xF2F7, 0x877C, 0xF2F8, 0x8764,\n\t0xF2F9, 0x8759, 0xF2FA, 0x8765, 0xF2FB, 0x8793, 0xF2FC, 0x87AF,\n\t0xF2FD, 0x87A8, 0xF2FE, 0x87D2, 0xF340, 0x9A5A, 0xF341, 0x9A5B,\n\t0xF342, 0x9A5C, 0xF343, 0x9A5D, 0xF344, 0x9A5E, 0xF345, 0x9A5F,\n\t0xF346, 0x9A60, 0xF347, 0x9A61, 0xF348, 0x9A62, 0xF349, 0x9A63,\n\t0xF34A, 0x9A64, 0xF34B, 0x9A65, 0xF34C, 0x9A66, 0xF34D, 0x9A67,\n\t0xF34E, 0x9A68, 0xF34F, 0x9A69, 0xF350, 0x9A6A, 0xF351, 0x9A6B,\n\t0xF352, 0x9A72, 0xF353, 0x9A83, 0xF354, 0x9A89, 0xF355, 0x9A8D,\n\t0xF356, 0x9A8E, 0xF357, 0x9A94, 0xF358, 0x9A95, 0xF359, 0x9A99,\n\t0xF35A, 0x9AA6, 0xF35B, 0x9AA9, 0xF35C, 0x9AAA, 0xF35D, 0x9AAB,\n\t0xF35E, 0x9AAC, 0xF35F, 0x9AAD, 0xF360, 0x9AAE, 0xF361, 0x9AAF,\n\t0xF362, 0x9AB2, 0xF363, 0x9AB3, 0xF364, 0x9AB4, 0xF365, 0x9AB5,\n\t0xF366, 0x9AB9, 0xF367, 0x9ABB, 0xF368, 0x9ABD, 0xF369, 0x9ABE,\n\t0xF36A, 0x9ABF, 0xF36B, 0x9AC3, 0xF36C, 0x9AC4, 0xF36D, 0x9AC6,\n\t0xF36E, 0x9AC7, 0xF36F, 0x9AC8, 0xF370, 0x9AC9, 0xF371, 0x9ACA,\n\t0xF372, 0x9ACD, 0xF373, 0x9ACE, 0xF374, 0x9ACF, 0xF375, 0x9AD0,\n\t0xF376, 0x9AD2, 0xF377, 0x9AD4, 0xF378, 0x9AD5, 0xF379, 0x9AD6,\n\t0xF37A, 0x9AD7, 0xF37B, 0x9AD9, 0xF37C, 0x9ADA, 0xF37D, 0x9ADB,\n\t0xF37E, 0x9ADC, 0xF380, 0x9ADD, 0xF381, 0x9ADE, 0xF382, 0x9AE0,\n\t0xF383, 0x9AE2, 0xF384, 0x9AE3, 0xF385, 0x9AE4, 0xF386, 0x9AE5,\n\t0xF387, 0x9AE7, 0xF388, 0x9AE8, 0xF389, 0x9AE9, 0xF38A, 0x9AEA,\n\t0xF38B, 0x9AEC, 0xF38C, 0x9AEE, 0xF38D, 0x9AF0, 0xF38E, 0x9AF1,\n\t0xF38F, 0x9AF2, 0xF390, 0x9AF3, 0xF391, 0x9AF4, 0xF392, 0x9AF5,\n\t0xF393, 0x9AF6, 0xF394, 0x9AF7, 0xF395, 0x9AF8, 0xF396, 0x9AFA,\n\t0xF397, 0x9AFC, 0xF398, 0x9AFD, 0xF399, 0x9AFE, 0xF39A, 0x9AFF,\n\t0xF39B, 0x9B00, 0xF39C, 0x9B01, 0xF39D, 0x9B02, 0xF39E, 0x9B04,\n\t0xF39F, 0x9B05, 0xF3A0, 0x9B06, 0xF3A1, 0x87C6, 0xF3A2, 0x8788,\n\t0xF3A3, 0x8785, 0xF3A4, 0x87AD, 0xF3A5, 0x8797, 0xF3A6, 0x8783,\n\t0xF3A7, 0x87AB, 0xF3A8, 0x87E5, 0xF3A9, 0x87AC, 0xF3AA, 0x87B5,\n\t0xF3AB, 0x87B3, 0xF3AC, 0x87CB, 0xF3AD, 0x87D3, 0xF3AE, 0x87BD,\n\t0xF3AF, 0x87D1, 0xF3B0, 0x87C0, 0xF3B1, 0x87CA, 0xF3B2, 0x87DB,\n\t0xF3B3, 0x87EA, 0xF3B4, 0x87E0, 0xF3B5, 0x87EE, 0xF3B6, 0x8816,\n\t0xF3B7, 0x8813, 0xF3B8, 0x87FE, 0xF3B9, 0x880A, 0xF3BA, 0x881B,\n\t0xF3BB, 0x8821, 0xF3BC, 0x8839, 0xF3BD, 0x883C, 0xF3BE, 0x7F36,\n\t0xF3BF, 0x7F42, 0xF3C0, 0x7F44, 0xF3C1, 0x7F45, 0xF3C2, 0x8210,\n\t0xF3C3, 0x7AFA, 0xF3C4, 0x7AFD, 0xF3C5, 0x7B08, 0xF3C6, 0x7B03,\n\t0xF3C7, 0x7B04, 0xF3C8, 0x7B15, 0xF3C9, 0x7B0A, 0xF3CA, 0x7B2B,\n\t0xF3CB, 0x7B0F, 0xF3CC, 0x7B47, 0xF3CD, 0x7B38, 0xF3CE, 0x7B2A,\n\t0xF3CF, 0x7B19, 0xF3D0, 0x7B2E, 0xF3D1, 0x7B31, 0xF3D2, 0x7B20,\n\t0xF3D3, 0x7B25, 0xF3D4, 0x7B24, 0xF3D5, 0x7B33, 0xF3D6, 0x7B3E,\n\t0xF3D7, 0x7B1E, 0xF3D8, 0x7B58, 0xF3D9, 0x7B5A, 0xF3DA, 0x7B45,\n\t0xF3DB, 0x7B75, 0xF3DC, 0x7B4C, 0xF3DD, 0x7B5D, 0xF3DE, 0x7B60,\n\t0xF3DF, 0x7B6E, 0xF3E0, 0x7B7B, 0xF3E1, 0x7B62, 0xF3E2, 0x7B72,\n\t0xF3E3, 0x7B71, 0xF3E4, 0x7B90, 0xF3E5, 0x7BA6, 0xF3E6, 0x7BA7,\n\t0xF3E7, 0x7BB8, 0xF3E8, 0x7BAC, 0xF3E9, 0x7B9D, 0xF3EA, 0x7BA8,\n\t0xF3EB, 0x7B85, 0xF3EC, 0x7BAA, 0xF3ED, 0x7B9C, 0xF3EE, 0x7BA2,\n\t0xF3EF, 0x7BAB, 0xF3F0, 0x7BB4, 0xF3F1, 0x7BD1, 0xF3F2, 0x7BC1,\n\t0xF3F3, 0x7BCC, 0xF3F4, 0x7BDD, 0xF3F5, 0x7BDA, 0xF3F6, 0x7BE5,\n\t0xF3F7, 0x7BE6, 0xF3F8, 0x7BEA, 0xF3F9, 0x7C0C, 0xF3FA, 0x7BFE,\n\t0xF3FB, 0x7BFC, 0xF3FC, 0x7C0F, 0xF3FD, 0x7C16, 0xF3FE, 0x7C0B,\n\t0xF440, 0x9B07, 0xF441, 0x9B09, 0xF442, 0x9B0A, 0xF443, 0x9B0B,\n\t0xF444, 0x9B0C, 0xF445, 0x9B0D, 0xF446, 0x9B0E, 0xF447, 0x9B10,\n\t0xF448, 0x9B11, 0xF449, 0x9B12, 0xF44A, 0x9B14, 0xF44B, 0x9B15,\n\t0xF44C, 0x9B16, 0xF44D, 0x9B17, 0xF44E, 0x9B18, 0xF44F, 0x9B19,\n\t0xF450, 0x9B1A, 0xF451, 0x9B1B, 0xF452, 0x9B1C, 0xF453, 0x9B1D,\n\t0xF454, 0x9B1E, 0xF455, 0x9B20, 0xF456, 0x9B21, 0xF457, 0x9B22,\n\t0xF458, 0x9B24, 0xF459, 0x9B25, 0xF45A, 0x9B26, 0xF45B, 0x9B27,\n\t0xF45C, 0x9B28, 0xF45D, 0x9B29, 0xF45E, 0x9B2A, 0xF45F, 0x9B2B,\n\t0xF460, 0x9B2C, 0xF461, 0x9B2D, 0xF462, 0x9B2E, 0xF463, 0x9B30,\n\t0xF464, 0x9B31, 0xF465, 0x9B33, 0xF466, 0x9B34, 0xF467, 0x9B35,\n\t0xF468, 0x9B36, 0xF469, 0x9B37, 0xF46A, 0x9B38, 0xF46B, 0x9B39,\n\t0xF46C, 0x9B3A, 0xF46D, 0x9B3D, 0xF46E, 0x9B3E, 0xF46F, 0x9B3F,\n\t0xF470, 0x9B40, 0xF471, 0x9B46, 0xF472, 0x9B4A, 0xF473, 0x9B4B,\n\t0xF474, 0x9B4C, 0xF475, 0x9B4E, 0xF476, 0x9B50, 0xF477, 0x9B52,\n\t0xF478, 0x9B53, 0xF479, 0x9B55, 0xF47A, 0x9B56, 0xF47B, 0x9B57,\n\t0xF47C, 0x9B58, 0xF47D, 0x9B59, 0xF47E, 0x9B5A, 0xF480, 0x9B5B,\n\t0xF481, 0x9B5C, 0xF482, 0x9B5D, 0xF483, 0x9B5E, 0xF484, 0x9B5F,\n\t0xF485, 0x9B60, 0xF486, 0x9B61, 0xF487, 0x9B62, 0xF488, 0x9B63,\n\t0xF489, 0x9B64, 0xF48A, 0x9B65, 0xF48B, 0x9B66, 0xF48C, 0x9B67,\n\t0xF48D, 0x9B68, 0xF48E, 0x9B69, 0xF48F, 0x9B6A, 0xF490, 0x9B6B,\n\t0xF491, 0x9B6C, 0xF492, 0x9B6D, 0xF493, 0x9B6E, 0xF494, 0x9B6F,\n\t0xF495, 0x9B70, 0xF496, 0x9B71, 0xF497, 0x9B72, 0xF498, 0x9B73,\n\t0xF499, 0x9B74, 0xF49A, 0x9B75, 0xF49B, 0x9B76, 0xF49C, 0x9B77,\n\t0xF49D, 0x9B78, 0xF49E, 0x9B79, 0xF49F, 0x9B7A, 0xF4A0, 0x9B7B,\n\t0xF4A1, 0x7C1F, 0xF4A2, 0x7C2A, 0xF4A3, 0x7C26, 0xF4A4, 0x7C38,\n\t0xF4A5, 0x7C41, 0xF4A6, 0x7C40, 0xF4A7, 0x81FE, 0xF4A8, 0x8201,\n\t0xF4A9, 0x8202, 0xF4AA, 0x8204, 0xF4AB, 0x81EC, 0xF4AC, 0x8844,\n\t0xF4AD, 0x8221, 0xF4AE, 0x8222, 0xF4AF, 0x8223, 0xF4B0, 0x822D,\n\t0xF4B1, 0x822F, 0xF4B2, 0x8228, 0xF4B3, 0x822B, 0xF4B4, 0x8238,\n\t0xF4B5, 0x823B, 0xF4B6, 0x8233, 0xF4B7, 0x8234, 0xF4B8, 0x823E,\n\t0xF4B9, 0x8244, 0xF4BA, 0x8249, 0xF4BB, 0x824B, 0xF4BC, 0x824F,\n\t0xF4BD, 0x825A, 0xF4BE, 0x825F, 0xF4BF, 0x8268, 0xF4C0, 0x887E,\n\t0xF4C1, 0x8885, 0xF4C2, 0x8888, 0xF4C3, 0x88D8, 0xF4C4, 0x88DF,\n\t0xF4C5, 0x895E, 0xF4C6, 0x7F9D, 0xF4C7, 0x7F9F, 0xF4C8, 0x7FA7,\n\t0xF4C9, 0x7FAF, 0xF4CA, 0x7FB0, 0xF4CB, 0x7FB2, 0xF4CC, 0x7C7C,\n\t0xF4CD, 0x6549, 0xF4CE, 0x7C91, 0xF4CF, 0x7C9D, 0xF4D0, 0x7C9C,\n\t0xF4D1, 0x7C9E, 0xF4D2, 0x7CA2, 0xF4D3, 0x7CB2, 0xF4D4, 0x7CBC,\n\t0xF4D5, 0x7CBD, 0xF4D6, 0x7CC1, 0xF4D7, 0x7CC7, 0xF4D8, 0x7CCC,\n\t0xF4D9, 0x7CCD, 0xF4DA, 0x7CC8, 0xF4DB, 0x7CC5, 0xF4DC, 0x7CD7,\n\t0xF4DD, 0x7CE8, 0xF4DE, 0x826E, 0xF4DF, 0x66A8, 0xF4E0, 0x7FBF,\n\t0xF4E1, 0x7FCE, 0xF4E2, 0x7FD5, 0xF4E3, 0x7FE5, 0xF4E4, 0x7FE1,\n\t0xF4E5, 0x7FE6, 0xF4E6, 0x7FE9, 0xF4E7, 0x7FEE, 0xF4E8, 0x7FF3,\n\t0xF4E9, 0x7CF8, 0xF4EA, 0x7D77, 0xF4EB, 0x7DA6, 0xF4EC, 0x7DAE,\n\t0xF4ED, 0x7E47, 0xF4EE, 0x7E9B, 0xF4EF, 0x9EB8, 0xF4F0, 0x9EB4,\n\t0xF4F1, 0x8D73, 0xF4F2, 0x8D84, 0xF4F3, 0x8D94, 0xF4F4, 0x8D91,\n\t0xF4F5, 0x8DB1, 0xF4F6, 0x8D67, 0xF4F7, 0x8D6D, 0xF4F8, 0x8C47,\n\t0xF4F9, 0x8C49, 0xF4FA, 0x914A, 0xF4FB, 0x9150, 0xF4FC, 0x914E,\n\t0xF4FD, 0x914F, 0xF4FE, 0x9164, 0xF540, 0x9B7C, 0xF541, 0x9B7D,\n\t0xF542, 0x9B7E, 0xF543, 0x9B7F, 0xF544, 0x9B80, 0xF545, 0x9B81,\n\t0xF546, 0x9B82, 0xF547, 0x9B83, 0xF548, 0x9B84, 0xF549, 0x9B85,\n\t0xF54A, 0x9B86, 0xF54B, 0x9B87, 0xF54C, 0x9B88, 0xF54D, 0x9B89,\n\t0xF54E, 0x9B8A, 0xF54F, 0x9B8B, 0xF550, 0x9B8C, 0xF551, 0x9B8D,\n\t0xF552, 0x9B8E, 0xF553, 0x9B8F, 0xF554, 0x9B90, 0xF555, 0x9B91,\n\t0xF556, 0x9B92, 0xF557, 0x9B93, 0xF558, 0x9B94, 0xF559, 0x9B95,\n\t0xF55A, 0x9B96, 0xF55B, 0x9B97, 0xF55C, 0x9B98, 0xF55D, 0x9B99,\n\t0xF55E, 0x9B9A, 0xF55F, 0x9B9B, 0xF560, 0x9B9C, 0xF561, 0x9B9D,\n\t0xF562, 0x9B9E, 0xF563, 0x9B9F, 0xF564, 0x9BA0, 0xF565, 0x9BA1,\n\t0xF566, 0x9BA2, 0xF567, 0x9BA3, 0xF568, 0x9BA4, 0xF569, 0x9BA5,\n\t0xF56A, 0x9BA6, 0xF56B, 0x9BA7, 0xF56C, 0x9BA8, 0xF56D, 0x9BA9,\n\t0xF56E, 0x9BAA, 0xF56F, 0x9BAB, 0xF570, 0x9BAC, 0xF571, 0x9BAD,\n\t0xF572, 0x9BAE, 0xF573, 0x9BAF, 0xF574, 0x9BB0, 0xF575, 0x9BB1,\n\t0xF576, 0x9BB2, 0xF577, 0x9BB3, 0xF578, 0x9BB4, 0xF579, 0x9BB5,\n\t0xF57A, 0x9BB6, 0xF57B, 0x9BB7, 0xF57C, 0x9BB8, 0xF57D, 0x9BB9,\n\t0xF57E, 0x9BBA, 0xF580, 0x9BBB, 0xF581, 0x9BBC, 0xF582, 0x9BBD,\n\t0xF583, 0x9BBE, 0xF584, 0x9BBF, 0xF585, 0x9BC0, 0xF586, 0x9BC1,\n\t0xF587, 0x9BC2, 0xF588, 0x9BC3, 0xF589, 0x9BC4, 0xF58A, 0x9BC5,\n\t0xF58B, 0x9BC6, 0xF58C, 0x9BC7, 0xF58D, 0x9BC8, 0xF58E, 0x9BC9,\n\t0xF58F, 0x9BCA, 0xF590, 0x9BCB, 0xF591, 0x9BCC, 0xF592, 0x9BCD,\n\t0xF593, 0x9BCE, 0xF594, 0x9BCF, 0xF595, 0x9BD0, 0xF596, 0x9BD1,\n\t0xF597, 0x9BD2, 0xF598, 0x9BD3, 0xF599, 0x9BD4, 0xF59A, 0x9BD5,\n\t0xF59B, 0x9BD6, 0xF59C, 0x9BD7, 0xF59D, 0x9BD8, 0xF59E, 0x9BD9,\n\t0xF59F, 0x9BDA, 0xF5A0, 0x9BDB, 0xF5A1, 0x9162, 0xF5A2, 0x9161,\n\t0xF5A3, 0x9170, 0xF5A4, 0x9169, 0xF5A5, 0x916F, 0xF5A6, 0x917D,\n\t0xF5A7, 0x917E, 0xF5A8, 0x9172, 0xF5A9, 0x9174, 0xF5AA, 0x9179,\n\t0xF5AB, 0x918C, 0xF5AC, 0x9185, 0xF5AD, 0x9190, 0xF5AE, 0x918D,\n\t0xF5AF, 0x9191, 0xF5B0, 0x91A2, 0xF5B1, 0x91A3, 0xF5B2, 0x91AA,\n\t0xF5B3, 0x91AD, 0xF5B4, 0x91AE, 0xF5B5, 0x91AF, 0xF5B6, 0x91B5,\n\t0xF5B7, 0x91B4, 0xF5B8, 0x91BA, 0xF5B9, 0x8C55, 0xF5BA, 0x9E7E,\n\t0xF5BB, 0x8DB8, 0xF5BC, 0x8DEB, 0xF5BD, 0x8E05, 0xF5BE, 0x8E59,\n\t0xF5BF, 0x8E69, 0xF5C0, 0x8DB5, 0xF5C1, 0x8DBF, 0xF5C2, 0x8DBC,\n\t0xF5C3, 0x8DBA, 0xF5C4, 0x8DC4, 0xF5C5, 0x8DD6, 0xF5C6, 0x8DD7,\n\t0xF5C7, 0x8DDA, 0xF5C8, 0x8DDE, 0xF5C9, 0x8DCE, 0xF5CA, 0x8DCF,\n\t0xF5CB, 0x8DDB, 0xF5CC, 0x8DC6, 0xF5CD, 0x8DEC, 0xF5CE, 0x8DF7,\n\t0xF5CF, 0x8DF8, 0xF5D0, 0x8DE3, 0xF5D1, 0x8DF9, 0xF5D2, 0x8DFB,\n\t0xF5D3, 0x8DE4, 0xF5D4, 0x8E09, 0xF5D5, 0x8DFD, 0xF5D6, 0x8E14,\n\t0xF5D7, 0x8E1D, 0xF5D8, 0x8E1F, 0xF5D9, 0x8E2C, 0xF5DA, 0x8E2E,\n\t0xF5DB, 0x8E23, 0xF5DC, 0x8E2F, 0xF5DD, 0x8E3A, 0xF5DE, 0x8E40,\n\t0xF5DF, 0x8E39, 0xF5E0, 0x8E35, 0xF5E1, 0x8E3D, 0xF5E2, 0x8E31,\n\t0xF5E3, 0x8E49, 0xF5E4, 0x8E41, 0xF5E5, 0x8E42, 0xF5E6, 0x8E51,\n\t0xF5E7, 0x8E52, 0xF5E8, 0x8E4A, 0xF5E9, 0x8E70, 0xF5EA, 0x8E76,\n\t0xF5EB, 0x8E7C, 0xF5EC, 0x8E6F, 0xF5ED, 0x8E74, 0xF5EE, 0x8E85,\n\t0xF5EF, 0x8E8F, 0xF5F0, 0x8E94, 0xF5F1, 0x8E90, 0xF5F2, 0x8E9C,\n\t0xF5F3, 0x8E9E, 0xF5F4, 0x8C78, 0xF5F5, 0x8C82, 0xF5F6, 0x8C8A,\n\t0xF5F7, 0x8C85, 0xF5F8, 0x8C98, 0xF5F9, 0x8C94, 0xF5FA, 0x659B,\n\t0xF5FB, 0x89D6, 0xF5FC, 0x89DE, 0xF5FD, 0x89DA, 0xF5FE, 0x89DC,\n\t0xF640, 0x9BDC, 0xF641, 0x9BDD, 0xF642, 0x9BDE, 0xF643, 0x9BDF,\n\t0xF644, 0x9BE0, 0xF645, 0x9BE1, 0xF646, 0x9BE2, 0xF647, 0x9BE3,\n\t0xF648, 0x9BE4, 0xF649, 0x9BE5, 0xF64A, 0x9BE6, 0xF64B, 0x9BE7,\n\t0xF64C, 0x9BE8, 0xF64D, 0x9BE9, 0xF64E, 0x9BEA, 0xF64F, 0x9BEB,\n\t0xF650, 0x9BEC, 0xF651, 0x9BED, 0xF652, 0x9BEE, 0xF653, 0x9BEF,\n\t0xF654, 0x9BF0, 0xF655, 0x9BF1, 0xF656, 0x9BF2, 0xF657, 0x9BF3,\n\t0xF658, 0x9BF4, 0xF659, 0x9BF5, 0xF65A, 0x9BF6, 0xF65B, 0x9BF7,\n\t0xF65C, 0x9BF8, 0xF65D, 0x9BF9, 0xF65E, 0x9BFA, 0xF65F, 0x9BFB,\n\t0xF660, 0x9BFC, 0xF661, 0x9BFD, 0xF662, 0x9BFE, 0xF663, 0x9BFF,\n\t0xF664, 0x9C00, 0xF665, 0x9C01, 0xF666, 0x9C02, 0xF667, 0x9C03,\n\t0xF668, 0x9C04, 0xF669, 0x9C05, 0xF66A, 0x9C06, 0xF66B, 0x9C07,\n\t0xF66C, 0x9C08, 0xF66D, 0x9C09, 0xF66E, 0x9C0A, 0xF66F, 0x9C0B,\n\t0xF670, 0x9C0C, 0xF671, 0x9C0D, 0xF672, 0x9C0E, 0xF673, 0x9C0F,\n\t0xF674, 0x9C10, 0xF675, 0x9C11, 0xF676, 0x9C12, 0xF677, 0x9C13,\n\t0xF678, 0x9C14, 0xF679, 0x9C15, 0xF67A, 0x9C16, 0xF67B, 0x9C17,\n\t0xF67C, 0x9C18, 0xF67D, 0x9C19, 0xF67E, 0x9C1A, 0xF680, 0x9C1B,\n\t0xF681, 0x9C1C, 0xF682, 0x9C1D, 0xF683, 0x9C1E, 0xF684, 0x9C1F,\n\t0xF685, 0x9C20, 0xF686, 0x9C21, 0xF687, 0x9C22, 0xF688, 0x9C23,\n\t0xF689, 0x9C24, 0xF68A, 0x9C25, 0xF68B, 0x9C26, 0xF68C, 0x9C27,\n\t0xF68D, 0x9C28, 0xF68E, 0x9C29, 0xF68F, 0x9C2A, 0xF690, 0x9C2B,\n\t0xF691, 0x9C2C, 0xF692, 0x9C2D, 0xF693, 0x9C2E, 0xF694, 0x9C2F,\n\t0xF695, 0x9C30, 0xF696, 0x9C31, 0xF697, 0x9C32, 0xF698, 0x9C33,\n\t0xF699, 0x9C34, 0xF69A, 0x9C35, 0xF69B, 0x9C36, 0xF69C, 0x9C37,\n\t0xF69D, 0x9C38, 0xF69E, 0x9C39, 0xF69F, 0x9C3A, 0xF6A0, 0x9C3B,\n\t0xF6A1, 0x89E5, 0xF6A2, 0x89EB, 0xF6A3, 0x89EF, 0xF6A4, 0x8A3E,\n\t0xF6A5, 0x8B26, 0xF6A6, 0x9753, 0xF6A7, 0x96E9, 0xF6A8, 0x96F3,\n\t0xF6A9, 0x96EF, 0xF6AA, 0x9706, 0xF6AB, 0x9701, 0xF6AC, 0x9708,\n\t0xF6AD, 0x970F, 0xF6AE, 0x970E, 0xF6AF, 0x972A, 0xF6B0, 0x972D,\n\t0xF6B1, 0x9730, 0xF6B2, 0x973E, 0xF6B3, 0x9F80, 0xF6B4, 0x9F83,\n\t0xF6B5, 0x9F85, 0xF6B6, 0x9F86, 0xF6B7, 0x9F87, 0xF6B8, 0x9F88,\n\t0xF6B9, 0x9F89, 0xF6BA, 0x9F8A, 0xF6BB, 0x9F8C, 0xF6BC, 0x9EFE,\n\t0xF6BD, 0x9F0B, 0xF6BE, 0x9F0D, 0xF6BF, 0x96B9, 0xF6C0, 0x96BC,\n\t0xF6C1, 0x96BD, 0xF6C2, 0x96CE, 0xF6C3, 0x96D2, 0xF6C4, 0x77BF,\n\t0xF6C5, 0x96E0, 0xF6C6, 0x928E, 0xF6C7, 0x92AE, 0xF6C8, 0x92C8,\n\t0xF6C9, 0x933E, 0xF6CA, 0x936A, 0xF6CB, 0x93CA, 0xF6CC, 0x938F,\n\t0xF6CD, 0x943E, 0xF6CE, 0x946B, 0xF6CF, 0x9C7F, 0xF6D0, 0x9C82,\n\t0xF6D1, 0x9C85, 0xF6D2, 0x9C86, 0xF6D3, 0x9C87, 0xF6D4, 0x9C88,\n\t0xF6D5, 0x7A23, 0xF6D6, 0x9C8B, 0xF6D7, 0x9C8E, 0xF6D8, 0x9C90,\n\t0xF6D9, 0x9C91, 0xF6DA, 0x9C92, 0xF6DB, 0x9C94, 0xF6DC, 0x9C95,\n\t0xF6DD, 0x9C9A, 0xF6DE, 0x9C9B, 0xF6DF, 0x9C9E, 0xF6E0, 0x9C9F,\n\t0xF6E1, 0x9CA0, 0xF6E2, 0x9CA1, 0xF6E3, 0x9CA2, 0xF6E4, 0x9CA3,\n\t0xF6E5, 0x9CA5, 0xF6E6, 0x9CA6, 0xF6E7, 0x9CA7, 0xF6E8, 0x9CA8,\n\t0xF6E9, 0x9CA9, 0xF6EA, 0x9CAB, 0xF6EB, 0x9CAD, 0xF6EC, 0x9CAE,\n\t0xF6ED, 0x9CB0, 0xF6EE, 0x9CB1, 0xF6EF, 0x9CB2, 0xF6F0, 0x9CB3,\n\t0xF6F1, 0x9CB4, 0xF6F2, 0x9CB5, 0xF6F3, 0x9CB6, 0xF6F4, 0x9CB7,\n\t0xF6F5, 0x9CBA, 0xF6F6, 0x9CBB, 0xF6F7, 0x9CBC, 0xF6F8, 0x9CBD,\n\t0xF6F9, 0x9CC4, 0xF6FA, 0x9CC5, 0xF6FB, 0x9CC6, 0xF6FC, 0x9CC7,\n\t0xF6FD, 0x9CCA, 0xF6FE, 0x9CCB, 0xF740, 0x9C3C, 0xF741, 0x9C3D,\n\t0xF742, 0x9C3E, 0xF743, 0x9C3F, 0xF744, 0x9C40, 0xF745, 0x9C41,\n\t0xF746, 0x9C42, 0xF747, 0x9C43, 0xF748, 0x9C44, 0xF749, 0x9C45,\n\t0xF74A, 0x9C46, 0xF74B, 0x9C47, 0xF74C, 0x9C48, 0xF74D, 0x9C49,\n\t0xF74E, 0x9C4A, 0xF74F, 0x9C4B, 0xF750, 0x9C4C, 0xF751, 0x9C4D,\n\t0xF752, 0x9C4E, 0xF753, 0x9C4F, 0xF754, 0x9C50, 0xF755, 0x9C51,\n\t0xF756, 0x9C52, 0xF757, 0x9C53, 0xF758, 0x9C54, 0xF759, 0x9C55,\n\t0xF75A, 0x9C56, 0xF75B, 0x9C57, 0xF75C, 0x9C58, 0xF75D, 0x9C59,\n\t0xF75E, 0x9C5A, 0xF75F, 0x9C5B, 0xF760, 0x9C5C, 0xF761, 0x9C5D,\n\t0xF762, 0x9C5E, 0xF763, 0x9C5F, 0xF764, 0x9C60, 0xF765, 0x9C61,\n\t0xF766, 0x9C62, 0xF767, 0x9C63, 0xF768, 0x9C64, 0xF769, 0x9C65,\n\t0xF76A, 0x9C66, 0xF76B, 0x9C67, 0xF76C, 0x9C68, 0xF76D, 0x9C69,\n\t0xF76E, 0x9C6A, 0xF76F, 0x9C6B, 0xF770, 0x9C6C, 0xF771, 0x9C6D,\n\t0xF772, 0x9C6E, 0xF773, 0x9C6F, 0xF774, 0x9C70, 0xF775, 0x9C71,\n\t0xF776, 0x9C72, 0xF777, 0x9C73, 0xF778, 0x9C74, 0xF779, 0x9C75,\n\t0xF77A, 0x9C76, 0xF77B, 0x9C77, 0xF77C, 0x9C78, 0xF77D, 0x9C79,\n\t0xF77E, 0x9C7A, 0xF780, 0x9C7B, 0xF781, 0x9C7D, 0xF782, 0x9C7E,\n\t0xF783, 0x9C80, 0xF784, 0x9C83, 0xF785, 0x9C84, 0xF786, 0x9C89,\n\t0xF787, 0x9C8A, 0xF788, 0x9C8C, 0xF789, 0x9C8F, 0xF78A, 0x9C93,\n\t0xF78B, 0x9C96, 0xF78C, 0x9C97, 0xF78D, 0x9C98, 0xF78E, 0x9C99,\n\t0xF78F, 0x9C9D, 0xF790, 0x9CAA, 0xF791, 0x9CAC, 0xF792, 0x9CAF,\n\t0xF793, 0x9CB9, 0xF794, 0x9CBE, 0xF795, 0x9CBF, 0xF796, 0x9CC0,\n\t0xF797, 0x9CC1, 0xF798, 0x9CC2, 0xF799, 0x9CC8, 0xF79A, 0x9CC9,\n\t0xF79B, 0x9CD1, 0xF79C, 0x9CD2, 0xF79D, 0x9CDA, 0xF79E, 0x9CDB,\n\t0xF79F, 0x9CE0, 0xF7A0, 0x9CE1, 0xF7A1, 0x9CCC, 0xF7A2, 0x9CCD,\n\t0xF7A3, 0x9CCE, 0xF7A4, 0x9CCF, 0xF7A5, 0x9CD0, 0xF7A6, 0x9CD3,\n\t0xF7A7, 0x9CD4, 0xF7A8, 0x9CD5, 0xF7A9, 0x9CD7, 0xF7AA, 0x9CD8,\n\t0xF7AB, 0x9CD9, 0xF7AC, 0x9CDC, 0xF7AD, 0x9CDD, 0xF7AE, 0x9CDF,\n\t0xF7AF, 0x9CE2, 0xF7B0, 0x977C, 0xF7B1, 0x9785, 0xF7B2, 0x9791,\n\t0xF7B3, 0x9792, 0xF7B4, 0x9794, 0xF7B5, 0x97AF, 0xF7B6, 0x97AB,\n\t0xF7B7, 0x97A3, 0xF7B8, 0x97B2, 0xF7B9, 0x97B4, 0xF7BA, 0x9AB1,\n\t0xF7BB, 0x9AB0, 0xF7BC, 0x9AB7, 0xF7BD, 0x9E58, 0xF7BE, 0x9AB6,\n\t0xF7BF, 0x9ABA, 0xF7C0, 0x9ABC, 0xF7C1, 0x9AC1, 0xF7C2, 0x9AC0,\n\t0xF7C3, 0x9AC5, 0xF7C4, 0x9AC2, 0xF7C5, 0x9ACB, 0xF7C6, 0x9ACC,\n\t0xF7C7, 0x9AD1, 0xF7C8, 0x9B45, 0xF7C9, 0x9B43, 0xF7CA, 0x9B47,\n\t0xF7CB, 0x9B49, 0xF7CC, 0x9B48, 0xF7CD, 0x9B4D, 0xF7CE, 0x9B51,\n\t0xF7CF, 0x98E8, 0xF7D0, 0x990D, 0xF7D1, 0x992E, 0xF7D2, 0x9955,\n\t0xF7D3, 0x9954, 0xF7D4, 0x9ADF, 0xF7D5, 0x9AE1, 0xF7D6, 0x9AE6,\n\t0xF7D7, 0x9AEF, 0xF7D8, 0x9AEB, 0xF7D9, 0x9AFB, 0xF7DA, 0x9AED,\n\t0xF7DB, 0x9AF9, 0xF7DC, 0x9B08, 0xF7DD, 0x9B0F, 0xF7DE, 0x9B13,\n\t0xF7DF, 0x9B1F, 0xF7E0, 0x9B23, 0xF7E1, 0x9EBD, 0xF7E2, 0x9EBE,\n\t0xF7E3, 0x7E3B, 0xF7E4, 0x9E82, 0xF7E5, 0x9E87, 0xF7E6, 0x9E88,\n\t0xF7E7, 0x9E8B, 0xF7E8, 0x9E92, 0xF7E9, 0x93D6, 0xF7EA, 0x9E9D,\n\t0xF7EB, 0x9E9F, 0xF7EC, 0x9EDB, 0xF7ED, 0x9EDC, 0xF7EE, 0x9EDD,\n\t0xF7EF, 0x9EE0, 0xF7F0, 0x9EDF, 0xF7F1, 0x9EE2, 0xF7F2, 0x9EE9,\n\t0xF7F3, 0x9EE7, 0xF7F4, 0x9EE5, 0xF7F5, 0x9EEA, 0xF7F6, 0x9EEF,\n\t0xF7F7, 0x9F22, 0xF7F8, 0x9F2C, 0xF7F9, 0x9F2F, 0xF7FA, 0x9F39,\n\t0xF7FB, 0x9F37, 0xF7FC, 0x9F3D, 0xF7FD, 0x9F3E, 0xF7FE, 0x9F44,\n\t0xF840, 0x9CE3, 0xF841, 0x9CE4, 0xF842, 0x9CE5, 0xF843, 0x9CE6,\n\t0xF844, 0x9CE7, 0xF845, 0x9CE8, 0xF846, 0x9CE9, 0xF847, 0x9CEA,\n\t0xF848, 0x9CEB, 0xF849, 0x9CEC, 0xF84A, 0x9CED, 0xF84B, 0x9CEE,\n\t0xF84C, 0x9CEF, 0xF84D, 0x9CF0, 0xF84E, 0x9CF1, 0xF84F, 0x9CF2,\n\t0xF850, 0x9CF3, 0xF851, 0x9CF4, 0xF852, 0x9CF5, 0xF853, 0x9CF6,\n\t0xF854, 0x9CF7, 0xF855, 0x9CF8, 0xF856, 0x9CF9, 0xF857, 0x9CFA,\n\t0xF858, 0x9CFB, 0xF859, 0x9CFC, 0xF85A, 0x9CFD, 0xF85B, 0x9CFE,\n\t0xF85C, 0x9CFF, 0xF85D, 0x9D00, 0xF85E, 0x9D01, 0xF85F, 0x9D02,\n\t0xF860, 0x9D03, 0xF861, 0x9D04, 0xF862, 0x9D05, 0xF863, 0x9D06,\n\t0xF864, 0x9D07, 0xF865, 0x9D08, 0xF866, 0x9D09, 0xF867, 0x9D0A,\n\t0xF868, 0x9D0B, 0xF869, 0x9D0C, 0xF86A, 0x9D0D, 0xF86B, 0x9D0E,\n\t0xF86C, 0x9D0F, 0xF86D, 0x9D10, 0xF86E, 0x9D11, 0xF86F, 0x9D12,\n\t0xF870, 0x9D13, 0xF871, 0x9D14, 0xF872, 0x9D15, 0xF873, 0x9D16,\n\t0xF874, 0x9D17, 0xF875, 0x9D18, 0xF876, 0x9D19, 0xF877, 0x9D1A,\n\t0xF878, 0x9D1B, 0xF879, 0x9D1C, 0xF87A, 0x9D1D, 0xF87B, 0x9D1E,\n\t0xF87C, 0x9D1F, 0xF87D, 0x9D20, 0xF87E, 0x9D21, 0xF880, 0x9D22,\n\t0xF881, 0x9D23, 0xF882, 0x9D24, 0xF883, 0x9D25, 0xF884, 0x9D26,\n\t0xF885, 0x9D27, 0xF886, 0x9D28, 0xF887, 0x9D29, 0xF888, 0x9D2A,\n\t0xF889, 0x9D2B, 0xF88A, 0x9D2C, 0xF88B, 0x9D2D, 0xF88C, 0x9D2E,\n\t0xF88D, 0x9D2F, 0xF88E, 0x9D30, 0xF88F, 0x9D31, 0xF890, 0x9D32,\n\t0xF891, 0x9D33, 0xF892, 0x9D34, 0xF893, 0x9D35, 0xF894, 0x9D36,\n\t0xF895, 0x9D37, 0xF896, 0x9D38, 0xF897, 0x9D39, 0xF898, 0x9D3A,\n\t0xF899, 0x9D3B, 0xF89A, 0x9D3C, 0xF89B, 0x9D3D, 0xF89C, 0x9D3E,\n\t0xF89D, 0x9D3F, 0xF89E, 0x9D40, 0xF89F, 0x9D41, 0xF8A0, 0x9D42,\n\t0xF940, 0x9D43, 0xF941, 0x9D44, 0xF942, 0x9D45, 0xF943, 0x9D46,\n\t0xF944, 0x9D47, 0xF945, 0x9D48, 0xF946, 0x9D49, 0xF947, 0x9D4A,\n\t0xF948, 0x9D4B, 0xF949, 0x9D4C, 0xF94A, 0x9D4D, 0xF94B, 0x9D4E,\n\t0xF94C, 0x9D4F, 0xF94D, 0x9D50, 0xF94E, 0x9D51, 0xF94F, 0x9D52,\n\t0xF950, 0x9D53, 0xF951, 0x9D54, 0xF952, 0x9D55, 0xF953, 0x9D56,\n\t0xF954, 0x9D57, 0xF955, 0x9D58, 0xF956, 0x9D59, 0xF957, 0x9D5A,\n\t0xF958, 0x9D5B, 0xF959, 0x9D5C, 0xF95A, 0x9D5D, 0xF95B, 0x9D5E,\n\t0xF95C, 0x9D5F, 0xF95D, 0x9D60, 0xF95E, 0x9D61, 0xF95F, 0x9D62,\n\t0xF960, 0x9D63, 0xF961, 0x9D64, 0xF962, 0x9D65, 0xF963, 0x9D66,\n\t0xF964, 0x9D67, 0xF965, 0x9D68, 0xF966, 0x9D69, 0xF967, 0x9D6A,\n\t0xF968, 0x9D6B, 0xF969, 0x9D6C, 0xF96A, 0x9D6D, 0xF96B, 0x9D6E,\n\t0xF96C, 0x9D6F, 0xF96D, 0x9D70, 0xF96E, 0x9D71, 0xF96F, 0x9D72,\n\t0xF970, 0x9D73, 0xF971, 0x9D74, 0xF972, 0x9D75, 0xF973, 0x9D76,\n\t0xF974, 0x9D77, 0xF975, 0x9D78, 0xF976, 0x9D79, 0xF977, 0x9D7A,\n\t0xF978, 0x9D7B, 0xF979, 0x9D7C, 0xF97A, 0x9D7D, 0xF97B, 0x9D7E,\n\t0xF97C, 0x9D7F, 0xF97D, 0x9D80, 0xF97E, 0x9D81, 0xF980, 0x9D82,\n\t0xF981, 0x9D83, 0xF982, 0x9D84, 0xF983, 0x9D85, 0xF984, 0x9D86,\n\t0xF985, 0x9D87, 0xF986, 0x9D88, 0xF987, 0x9D89, 0xF988, 0x9D8A,\n\t0xF989, 0x9D8B, 0xF98A, 0x9D8C, 0xF98B, 0x9D8D, 0xF98C, 0x9D8E,\n\t0xF98D, 0x9D8F, 0xF98E, 0x9D90, 0xF98F, 0x9D91, 0xF990, 0x9D92,\n\t0xF991, 0x9D93, 0xF992, 0x9D94, 0xF993, 0x9D95, 0xF994, 0x9D96,\n\t0xF995, 0x9D97, 0xF996, 0x9D98, 0xF997, 0x9D99, 0xF998, 0x9D9A,\n\t0xF999, 0x9D9B, 0xF99A, 0x9D9C, 0xF99B, 0x9D9D, 0xF99C, 0x9D9E,\n\t0xF99D, 0x9D9F, 0xF99E, 0x9DA0, 0xF99F, 0x9DA1, 0xF9A0, 0x9DA2,\n\t0xFA40, 0x9DA3, 0xFA41, 0x9DA4, 0xFA42, 0x9DA5, 0xFA43, 0x9DA6,\n\t0xFA44, 0x9DA7, 0xFA45, 0x9DA8, 0xFA46, 0x9DA9, 0xFA47, 0x9DAA,\n\t0xFA48, 0x9DAB, 0xFA49, 0x9DAC, 0xFA4A, 0x9DAD, 0xFA4B, 0x9DAE,\n\t0xFA4C, 0x9DAF, 0xFA4D, 0x9DB0, 0xFA4E, 0x9DB1, 0xFA4F, 0x9DB2,\n\t0xFA50, 0x9DB3, 0xFA51, 0x9DB4, 0xFA52, 0x9DB5, 0xFA53, 0x9DB6,\n\t0xFA54, 0x9DB7, 0xFA55, 0x9DB8, 0xFA56, 0x9DB9, 0xFA57, 0x9DBA,\n\t0xFA58, 0x9DBB, 0xFA59, 0x9DBC, 0xFA5A, 0x9DBD, 0xFA5B, 0x9DBE,\n\t0xFA5C, 0x9DBF, 0xFA5D, 0x9DC0, 0xFA5E, 0x9DC1, 0xFA5F, 0x9DC2,\n\t0xFA60, 0x9DC3, 0xFA61, 0x9DC4, 0xFA62, 0x9DC5, 0xFA63, 0x9DC6,\n\t0xFA64, 0x9DC7, 0xFA65, 0x9DC8, 0xFA66, 0x9DC9, 0xFA67, 0x9DCA,\n\t0xFA68, 0x9DCB, 0xFA69, 0x9DCC, 0xFA6A, 0x9DCD, 0xFA6B, 0x9DCE,\n\t0xFA6C, 0x9DCF, 0xFA6D, 0x9DD0, 0xFA6E, 0x9DD1, 0xFA6F, 0x9DD2,\n\t0xFA70, 0x9DD3, 0xFA71, 0x9DD4, 0xFA72, 0x9DD5, 0xFA73, 0x9DD6,\n\t0xFA74, 0x9DD7, 0xFA75, 0x9DD8, 0xFA76, 0x9DD9, 0xFA77, 0x9DDA,\n\t0xFA78, 0x9DDB, 0xFA79, 0x9DDC, 0xFA7A, 0x9DDD, 0xFA7B, 0x9DDE,\n\t0xFA7C, 0x9DDF, 0xFA7D, 0x9DE0, 0xFA7E, 0x9DE1, 0xFA80, 0x9DE2,\n\t0xFA81, 0x9DE3, 0xFA82, 0x9DE4, 0xFA83, 0x9DE5, 0xFA84, 0x9DE6,\n\t0xFA85, 0x9DE7, 0xFA86, 0x9DE8, 0xFA87, 0x9DE9, 0xFA88, 0x9DEA,\n\t0xFA89, 0x9DEB, 0xFA8A, 0x9DEC, 0xFA8B, 0x9DED, 0xFA8C, 0x9DEE,\n\t0xFA8D, 0x9DEF, 0xFA8E, 0x9DF0, 0xFA8F, 0x9DF1, 0xFA90, 0x9DF2,\n\t0xFA91, 0x9DF3, 0xFA92, 0x9DF4, 0xFA93, 0x9DF5, 0xFA94, 0x9DF6,\n\t0xFA95, 0x9DF7, 0xFA96, 0x9DF8, 0xFA97, 0x9DF9, 0xFA98, 0x9DFA,\n\t0xFA99, 0x9DFB, 0xFA9A, 0x9DFC, 0xFA9B, 0x9DFD, 0xFA9C, 0x9DFE,\n\t0xFA9D, 0x9DFF, 0xFA9E, 0x9E00, 0xFA9F, 0x9E01, 0xFAA0, 0x9E02,\n\t0xFB40, 0x9E03, 0xFB41, 0x9E04, 0xFB42, 0x9E05, 0xFB43, 0x9E06,\n\t0xFB44, 0x9E07, 0xFB45, 0x9E08, 0xFB46, 0x9E09, 0xFB47, 0x9E0A,\n\t0xFB48, 0x9E0B, 0xFB49, 0x9E0C, 0xFB4A, 0x9E0D, 0xFB4B, 0x9E0E,\n\t0xFB4C, 0x9E0F, 0xFB4D, 0x9E10, 0xFB4E, 0x9E11, 0xFB4F, 0x9E12,\n\t0xFB50, 0x9E13, 0xFB51, 0x9E14, 0xFB52, 0x9E15, 0xFB53, 0x9E16,\n\t0xFB54, 0x9E17, 0xFB55, 0x9E18, 0xFB56, 0x9E19, 0xFB57, 0x9E1A,\n\t0xFB58, 0x9E1B, 0xFB59, 0x9E1C, 0xFB5A, 0x9E1D, 0xFB5B, 0x9E1E,\n\t0xFB5C, 0x9E24, 0xFB5D, 0x9E27, 0xFB5E, 0x9E2E, 0xFB5F, 0x9E30,\n\t0xFB60, 0x9E34, 0xFB61, 0x9E3B, 0xFB62, 0x9E3C, 0xFB63, 0x9E40,\n\t0xFB64, 0x9E4D, 0xFB65, 0x9E50, 0xFB66, 0x9E52, 0xFB67, 0x9E53,\n\t0xFB68, 0x9E54, 0xFB69, 0x9E56, 0xFB6A, 0x9E59, 0xFB6B, 0x9E5D,\n\t0xFB6C, 0x9E5F, 0xFB6D, 0x9E60, 0xFB6E, 0x9E61, 0xFB6F, 0x9E62,\n\t0xFB70, 0x9E65, 0xFB71, 0x9E6E, 0xFB72, 0x9E6F, 0xFB73, 0x9E72,\n\t0xFB74, 0x9E74, 0xFB75, 0x9E75, 0xFB76, 0x9E76, 0xFB77, 0x9E77,\n\t0xFB78, 0x9E78, 0xFB79, 0x9E79, 0xFB7A, 0x9E7A, 0xFB7B, 0x9E7B,\n\t0xFB7C, 0x9E7C, 0xFB7D, 0x9E7D, 0xFB7E, 0x9E80, 0xFB80, 0x9E81,\n\t0xFB81, 0x9E83, 0xFB82, 0x9E84, 0xFB83, 0x9E85, 0xFB84, 0x9E86,\n\t0xFB85, 0x9E89, 0xFB86, 0x9E8A, 0xFB87, 0x9E8C, 0xFB88, 0x9E8D,\n\t0xFB89, 0x9E8E, 0xFB8A, 0x9E8F, 0xFB8B, 0x9E90, 0xFB8C, 0x9E91,\n\t0xFB8D, 0x9E94, 0xFB8E, 0x9E95, 0xFB8F, 0x9E96, 0xFB90, 0x9E97,\n\t0xFB91, 0x9E98, 0xFB92, 0x9E99, 0xFB93, 0x9E9A, 0xFB94, 0x9E9B,\n\t0xFB95, 0x9E9C, 0xFB96, 0x9E9E, 0xFB97, 0x9EA0, 0xFB98, 0x9EA1,\n\t0xFB99, 0x9EA2, 0xFB9A, 0x9EA3, 0xFB9B, 0x9EA4, 0xFB9C, 0x9EA5,\n\t0xFB9D, 0x9EA7, 0xFB9E, 0x9EA8, 0xFB9F, 0x9EA9, 0xFBA0, 0x9EAA,\n\t0xFC40, 0x9EAB, 0xFC41, 0x9EAC, 0xFC42, 0x9EAD, 0xFC43, 0x9EAE,\n\t0xFC44, 0x9EAF, 0xFC45, 0x9EB0, 0xFC46, 0x9EB1, 0xFC47, 0x9EB2,\n\t0xFC48, 0x9EB3, 0xFC49, 0x9EB5, 0xFC4A, 0x9EB6, 0xFC4B, 0x9EB7,\n\t0xFC4C, 0x9EB9, 0xFC4D, 0x9EBA, 0xFC4E, 0x9EBC, 0xFC4F, 0x9EBF,\n\t0xFC50, 0x9EC0, 0xFC51, 0x9EC1, 0xFC52, 0x9EC2, 0xFC53, 0x9EC3,\n\t0xFC54, 0x9EC5, 0xFC55, 0x9EC6, 0xFC56, 0x9EC7, 0xFC57, 0x9EC8,\n\t0xFC58, 0x9ECA, 0xFC59, 0x9ECB, 0xFC5A, 0x9ECC, 0xFC5B, 0x9ED0,\n\t0xFC5C, 0x9ED2, 0xFC5D, 0x9ED3, 0xFC5E, 0x9ED5, 0xFC5F, 0x9ED6,\n\t0xFC60, 0x9ED7, 0xFC61, 0x9ED9, 0xFC62, 0x9EDA, 0xFC63, 0x9EDE,\n\t0xFC64, 0x9EE1, 0xFC65, 0x9EE3, 0xFC66, 0x9EE4, 0xFC67, 0x9EE6,\n\t0xFC68, 0x9EE8, 0xFC69, 0x9EEB, 0xFC6A, 0x9EEC, 0xFC6B, 0x9EED,\n\t0xFC6C, 0x9EEE, 0xFC6D, 0x9EF0, 0xFC6E, 0x9EF1, 0xFC6F, 0x9EF2,\n\t0xFC70, 0x9EF3, 0xFC71, 0x9EF4, 0xFC72, 0x9EF5, 0xFC73, 0x9EF6,\n\t0xFC74, 0x9EF7, 0xFC75, 0x9EF8, 0xFC76, 0x9EFA, 0xFC77, 0x9EFD,\n\t0xFC78, 0x9EFF, 0xFC79, 0x9F00, 0xFC7A, 0x9F01, 0xFC7B, 0x9F02,\n\t0xFC7C, 0x9F03, 0xFC7D, 0x9F04, 0xFC7E, 0x9F05, 0xFC80, 0x9F06,\n\t0xFC81, 0x9F07, 0xFC82, 0x9F08, 0xFC83, 0x9F09, 0xFC84, 0x9F0A,\n\t0xFC85, 0x9F0C, 0xFC86, 0x9F0F, 0xFC87, 0x9F11, 0xFC88, 0x9F12,\n\t0xFC89, 0x9F14, 0xFC8A, 0x9F15, 0xFC8B, 0x9F16, 0xFC8C, 0x9F18,\n\t0xFC8D, 0x9F1A, 0xFC8E, 0x9F1B, 0xFC8F, 0x9F1C, 0xFC90, 0x9F1D,\n\t0xFC91, 0x9F1E, 0xFC92, 0x9F1F, 0xFC93, 0x9F21, 0xFC94, 0x9F23,\n\t0xFC95, 0x9F24, 0xFC96, 0x9F25, 0xFC97, 0x9F26, 0xFC98, 0x9F27,\n\t0xFC99, 0x9F28, 0xFC9A, 0x9F29, 0xFC9B, 0x9F2A, 0xFC9C, 0x9F2B,\n\t0xFC9D, 0x9F2D, 0xFC9E, 0x9F2E, 0xFC9F, 0x9F30, 0xFCA0, 0x9F31,\n\t0xFD40, 0x9F32, 0xFD41, 0x9F33, 0xFD42, 0x9F34, 0xFD43, 0x9F35,\n\t0xFD44, 0x9F36, 0xFD45, 0x9F38, 0xFD46, 0x9F3A, 0xFD47, 0x9F3C,\n\t0xFD48, 0x9F3F, 0xFD49, 0x9F40, 0xFD4A, 0x9F41, 0xFD4B, 0x9F42,\n\t0xFD4C, 0x9F43, 0xFD4D, 0x9F45, 0xFD4E, 0x9F46, 0xFD4F, 0x9F47,\n\t0xFD50, 0x9F48, 0xFD51, 0x9F49, 0xFD52, 0x9F4A, 0xFD53, 0x9F4B,\n\t0xFD54, 0x9F4C, 0xFD55, 0x9F4D, 0xFD56, 0x9F4E, 0xFD57, 0x9F4F,\n\t0xFD58, 0x9F52, 0xFD59, 0x9F53, 0xFD5A, 0x9F54, 0xFD5B, 0x9F55,\n\t0xFD5C, 0x9F56, 0xFD5D, 0x9F57, 0xFD5E, 0x9F58, 0xFD5F, 0x9F59,\n\t0xFD60, 0x9F5A, 0xFD61, 0x9F5B, 0xFD62, 0x9F5C, 0xFD63, 0x9F5D,\n\t0xFD64, 0x9F5E, 0xFD65, 0x9F5F, 0xFD66, 0x9F60, 0xFD67, 0x9F61,\n\t0xFD68, 0x9F62, 0xFD69, 0x9F63, 0xFD6A, 0x9F64, 0xFD6B, 0x9F65,\n\t0xFD6C, 0x9F66, 0xFD6D, 0x9F67, 0xFD6E, 0x9F68, 0xFD6F, 0x9F69,\n\t0xFD70, 0x9F6A, 0xFD71, 0x9F6B, 0xFD72, 0x9F6C, 0xFD73, 0x9F6D,\n\t0xFD74, 0x9F6E, 0xFD75, 0x9F6F, 0xFD76, 0x9F70, 0xFD77, 0x9F71,\n\t0xFD78, 0x9F72, 0xFD79, 0x9F73, 0xFD7A, 0x9F74, 0xFD7B, 0x9F75,\n\t0xFD7C, 0x9F76, 0xFD7D, 0x9F77, 0xFD7E, 0x9F78, 0xFD80, 0x9F79,\n\t0xFD81, 0x9F7A, 0xFD82, 0x9F7B, 0xFD83, 0x9F7C, 0xFD84, 0x9F7D,\n\t0xFD85, 0x9F7E, 0xFD86, 0x9F81, 0xFD87, 0x9F82, 0xFD88, 0x9F8D,\n\t0xFD89, 0x9F8E, 0xFD8A, 0x9F8F, 0xFD8B, 0x9F90, 0xFD8C, 0x9F91,\n\t0xFD8D, 0x9F92, 0xFD8E, 0x9F93, 0xFD8F, 0x9F94, 0xFD90, 0x9F95,\n\t0xFD91, 0x9F96, 0xFD92, 0x9F97, 0xFD93, 0x9F98, 0xFD94, 0x9F9C,\n\t0xFD95, 0x9F9D, 0xFD96, 0x9F9E, 0xFD97, 0x9FA1, 0xFD98, 0x9FA2,\n\t0xFD99, 0x9FA3, 0xFD9A, 0x9FA4, 0xFD9B, 0x9FA5, 0xFD9C, 0xF92C,\n\t0xFD9D, 0xF979, 0xFD9E, 0xF995, 0xFD9F, 0xF9E7, 0xFDA0, 0xF9F1,\n\t0xFE40, 0xFA0C, 0xFE41, 0xFA0D, 0xFE42, 0xFA0E, 0xFE43, 0xFA0F,\n\t0xFE44, 0xFA11, 0xFE45, 0xFA13, 0xFE46, 0xFA14, 0xFE47, 0xFA18,\n\t0xFE48, 0xFA1F, 0xFE49, 0xFA20, 0xFE4A, 0xFA21, 0xFE4B, 0xFA23,\n\t0xFE4C, 0xFA24, 0xFE4D, 0xFA27, 0xFE4E, 0xFA28, 0xFE4F, 0xFA29,\n\t0, 0\n};\n\n\n\nWCHAR ff_convert (\t/* Converted code, 0 means conversion error */\n\tWCHAR\tchr,\t/* Character code to be converted */\n\tUINT\tdir\t\t/* 0: Unicode to OEM code, 1: OEM code to Unicode */\n)\n{\n\tconst WCHAR *p;\n\tWCHAR c;\n\tint i, n, li, hi;\n\n\n\tif (chr < 0x80) {\t/* ASCII */\n\t\tc = chr;\n\t} else {\n\t\tif (dir) {\t\t/* OEM code to unicode */\n\t\t\tp = oem2uni;\n\t\t\thi = sizeof oem2uni / 4 - 1;\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tp = uni2oem;\n\t\t\thi = sizeof uni2oem / 4 - 1;\n\t\t}\n\t\tli = 0;\n\t\tfor (n = 16; n; n--) {\n\t\t\ti = li + (hi - li) / 2;\n\t\t\tif (chr == p[i * 2]) break;\n\t\t\tif (chr > p[i * 2])\n\t\t\t\tli = i;\n\t\t\telse\n\t\t\t\thi = i;\n\t\t}\n\t\tc = n ? p[i * 2 + 1] : 0;\n\t}\n\n\treturn c;\n}\n\n\n\nWCHAR ff_wtoupper (\t/* Returns upper converted character */\n\tWCHAR chr\t\t/* Unicode character to be upper converted (BMP only) */\n)\n{\n\t/* Compressed upper conversion table */\n\tstatic const WCHAR cvt1[] = {\t/* U+0000 - U+0FFF */\n\t\t/* Basic Latin */\n\t\t0x0061,0x031A,\n\t\t/* Latin-1 Supplement */\n\t\t0x00E0,0x0317,  0x00F8,0x0307,  0x00FF,0x0001,0x0178,\n\t\t/* Latin Extended-A */\n\t\t0x0100,0x0130,  0x0132,0x0106,  0x0139,0x0110,  0x014A,0x012E,  0x0179,0x0106,\n\t\t/* Latin Extended-B */\n\t\t0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,\n\t\t0x01CD,0x0110,  0x01DD,0x0001,0x018E,  0x01DE,0x0112,  0x01F3,0x0003,0x01F1,0x01F4,0x01F4,  0x01F8,0x0128,\n\t\t0x0222,0x0112,  0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,  0x0246,0x010A,\n\t\t/* IPA Extensions */\n\t\t0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,\n\t\t/* Greek, Coptic */\n\t\t0x037B,0x0003,0x03FD,0x03FE,0x03FF,  0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,  0x03B1,0x0311,\n\t\t0x03C2,0x0002,0x03A3,0x03A3,  0x03C4,0x0308,  0x03CC,0x0003,0x038C,0x038E,0x038F,  0x03D8,0x0118,\n\t\t0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,\n\t\t/* Cyrillic */\n\t\t0x0430,0x0320,  0x0450,0x0710,  0x0460,0x0122,  0x048A,0x0136,  0x04C1,0x010E,  0x04CF,0x0001,0x04C0,  0x04D0,0x0144,\n\t\t/* Armenian */\n\t\t0x0561,0x0426,\n\n\t\t0x0000\n\t};\n\tstatic const WCHAR cvt2[] = {\t/* U+1000 - U+FFFF */\n\t\t/* Phonetic Extensions */\n\t\t0x1D7D,0x0001,0x2C63,\n\t\t/* Latin Extended Additional */\n\t\t0x1E00,0x0196,  0x1EA0,0x015A,\n\t\t/* Greek Extended */\n\t\t0x1F00,0x0608,  0x1F10,0x0606,  0x1F20,0x0608,  0x1F30,0x0608,  0x1F40,0x0606,\n\t\t0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,  0x1F60,0x0608,\n\t\t0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,\n\t\t0x1F80,0x0608,  0x1F90,0x0608,  0x1FA0,0x0608,  0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,\n\t\t0x1FCC,0x0001,0x1FC3,  0x1FD0,0x0602,  0x1FE0,0x0602,  0x1FE5,0x0001,0x1FEC,  0x1FF2,0x0001,0x1FFC,\n\t\t/* Letterlike Symbols */\n\t\t0x214E,0x0001,0x2132,\n\t\t/* Number forms */\n\t\t0x2170,0x0210,  0x2184,0x0001,0x2183,\n\t\t/* Enclosed Alphanumerics */\n\t\t0x24D0,0x051A,  0x2C30,0x042F,\n\t\t/* Latin Extended-C */\n\t\t0x2C60,0x0102,  0x2C67,0x0106, 0x2C75,0x0102,\n\t\t/* Coptic */\n\t\t0x2C80,0x0164,\n\t\t/* Georgian Supplement */\n\t\t0x2D00,0x0826,\n\t\t/* Full-width */\n\t\t0xFF41,0x031A,\n\n\t\t0x0000\n\t};\n\tconst WCHAR *p;\n\tWCHAR bc, nc, cmd;\n\n\n\tp = chr < 0x1000 ? cvt1 : cvt2;\n\tfor (;;) {\n\t\tbc = *p++;\t\t\t\t\t\t\t\t/* Get block base */\n\t\tif (!bc || chr < bc) break;\n\t\tnc = *p++; cmd = nc >> 8; nc &= 0xFF;\t/* Get processing command and block size */\n\t\tif (chr < bc + nc) {\t/* In the block? */\n\t\t\tswitch (cmd) {\n\t\t\tcase 0:\tchr = p[chr - bc]; break;\t\t/* Table conversion */\n\t\t\tcase 1:\tchr -= (chr - bc) & 1; break;\t/* Case pairs */\n\t\t\tcase 2: chr -= 16; break;\t\t\t\t/* Shift -16 */\n\t\t\tcase 3:\tchr -= 32; break;\t\t\t\t/* Shift -32 */\n\t\t\tcase 4:\tchr -= 48; break;\t\t\t\t/* Shift -48 */\n\t\t\tcase 5:\tchr -= 26; break;\t\t\t\t/* Shift -26 */\n\t\t\tcase 6:\tchr += 8; break;\t\t\t\t/* Shift +8 */\n\t\t\tcase 7: chr -= 80; break;\t\t\t\t/* Shift -80 */\n\t\t\tcase 8:\tchr -= 0x1C60; break;\t\t\t/* Shift -0x1C60 */\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!cmd) p += nc;\n\t}\n\n\treturn chr;\n}\n\n"
  },
  {
    "path": "lib/fatfs/option/cc949.c",
    "content": "/*------------------------------------------------------------------------*/\n/* Unicode - OEM code bidirectional converter  (C)ChaN, 2015              */\n/* CP949 (Korean EUC-KR)                                                  */\n/*------------------------------------------------------------------------*/\n\n#include \"../ff.h\"\n\n\n#if !_USE_LFN || _CODE_PAGE != 949\n#error This file is not needed in current configuration. Remove from the project.\n#endif\n\n\nstatic\nconst WCHAR uni2oem[] = {\n/*  Unicode - OEM,  Unicode - OEM,  Unicode - OEM,  Unicode - OEM */\n\t0x00A1, 0xA2AE, 0x00A4, 0xA2B4, 0x00A7, 0xA1D7, 0x00A8, 0xA1A7,\n\t0x00AA, 0xA8A3, 0x00AD, 0xA1A9, 0x00AE, 0xA2E7, 0x00B0, 0xA1C6,\n\t0x00B1, 0xA1BE, 0x00B2, 0xA9F7, 0x00B3, 0xA9F8, 0x00B4, 0xA2A5,\n\t0x00B6, 0xA2D2, 0x00B7, 0xA1A4, 0x00B8, 0xA2AC, 0x00B9, 0xA9F6,\n\t0x00BA, 0xA8AC, 0x00BC, 0xA8F9, 0x00BD, 0xA8F6, 0x00BE, 0xA8FA,\n\t0x00BF, 0xA2AF, 0x00C6, 0xA8A1, 0x00D0, 0xA8A2, 0x00D7, 0xA1BF,\n\t0x00D8, 0xA8AA, 0x00DE, 0xA8AD, 0x00DF, 0xA9AC, 0x00E6, 0xA9A1,\n\t0x00F0, 0xA9A3, 0x00F7, 0xA1C0, 0x00F8, 0xA9AA, 0x00FE, 0xA9AD,\n\t0x0111, 0xA9A2, 0x0126, 0xA8A4, 0x0127, 0xA9A4, 0x0131, 0xA9A5,\n\t0x0132, 0xA8A6, 0x0133, 0xA9A6, 0x0138, 0xA9A7, 0x013F, 0xA8A8,\n\t0x0140, 0xA9A8, 0x0141, 0xA8A9, 0x0142, 0xA9A9, 0x0149, 0xA9B0,\n\t0x014A, 0xA8AF, 0x014B, 0xA9AF, 0x0152, 0xA8AB, 0x0153, 0xA9AB,\n\t0x0166, 0xA8AE, 0x0167, 0xA9AE, 0x02C7, 0xA2A7, 0x02D0, 0xA2B0,\n\t0x02D8, 0xA2A8, 0x02D9, 0xA2AB, 0x02DA, 0xA2AA, 0x02DB, 0xA2AD,\n\t0x02DD, 0xA2A9, 0x0391, 0xA5C1, 0x0392, 0xA5C2, 0x0393, 0xA5C3,\n\t0x0394, 0xA5C4, 0x0395, 0xA5C5, 0x0396, 0xA5C6, 0x0397, 0xA5C7,\n\t0x0398, 0xA5C8, 0x0399, 0xA5C9, 0x039A, 0xA5CA, 0x039B, 0xA5CB,\n\t0x039C, 0xA5CC, 0x039D, 0xA5CD, 0x039E, 0xA5CE, 0x039F, 0xA5CF,\n\t0x03A0, 0xA5D0, 0x03A1, 0xA5D1, 0x03A3, 0xA5D2, 0x03A4, 0xA5D3,\n\t0x03A5, 0xA5D4, 0x03A6, 0xA5D5, 0x03A7, 0xA5D6, 0x03A8, 0xA5D7,\n\t0x03A9, 0xA5D8, 0x03B1, 0xA5E1, 0x03B2, 0xA5E2, 0x03B3, 0xA5E3,\n\t0x03B4, 0xA5E4, 0x03B5, 0xA5E5, 0x03B6, 0xA5E6, 0x03B7, 0xA5E7,\n\t0x03B8, 0xA5E8, 0x03B9, 0xA5E9, 0x03BA, 0xA5EA, 0x03BB, 0xA5EB,\n\t0x03BC, 0xA5EC, 0x03BD, 0xA5ED, 0x03BE, 0xA5EE, 0x03BF, 0xA5EF,\n\t0x03C0, 0xA5F0, 0x03C1, 0xA5F1, 0x03C3, 0xA5F2, 0x03C4, 0xA5F3,\n\t0x03C5, 0xA5F4, 0x03C6, 0xA5F5, 0x03C7, 0xA5F6, 0x03C8, 0xA5F7,\n\t0x03C9, 0xA5F8, 0x0401, 0xACA7, 0x0410, 0xACA1, 0x0411, 0xACA2,\n\t0x0412, 0xACA3, 0x0413, 0xACA4, 0x0414, 0xACA5, 0x0415, 0xACA6,\n\t0x0416, 0xACA8, 0x0417, 0xACA9, 0x0418, 0xACAA, 0x0419, 0xACAB,\n\t0x041A, 0xACAC, 0x041B, 0xACAD, 0x041C, 0xACAE, 0x041D, 0xACAF,\n\t0x041E, 0xACB0, 0x041F, 0xACB1, 0x0420, 0xACB2, 0x0421, 0xACB3,\n\t0x0422, 0xACB4, 0x0423, 0xACB5, 0x0424, 0xACB6, 0x0425, 0xACB7,\n\t0x0426, 0xACB8, 0x0427, 0xACB9, 0x0428, 0xACBA, 0x0429, 0xACBB,\n\t0x042A, 0xACBC, 0x042B, 0xACBD, 0x042C, 0xACBE, 0x042D, 0xACBF,\n\t0x042E, 0xACC0, 0x042F, 0xACC1, 0x0430, 0xACD1, 0x0431, 0xACD2,\n\t0x0432, 0xACD3, 0x0433, 0xACD4, 0x0434, 0xACD5, 0x0435, 0xACD6,\n\t0x0436, 0xACD8, 0x0437, 0xACD9, 0x0438, 0xACDA, 0x0439, 0xACDB,\n\t0x043A, 0xACDC, 0x043B, 0xACDD, 0x043C, 0xACDE, 0x043D, 0xACDF,\n\t0x043E, 0xACE0, 0x043F, 0xACE1, 0x0440, 0xACE2, 0x0441, 0xACE3,\n\t0x0442, 0xACE4, 0x0443, 0xACE5, 0x0444, 0xACE6, 0x0445, 0xACE7,\n\t0x0446, 0xACE8, 0x0447, 0xACE9, 0x0448, 0xACEA, 0x0449, 0xACEB,\n\t0x044A, 0xACEC, 0x044B, 0xACED, 0x044C, 0xACEE, 0x044D, 0xACEF,\n\t0x044E, 0xACF0, 0x044F, 0xACF1, 0x0451, 0xACD7, 0x2015, 0xA1AA,\n\t0x2018, 0xA1AE, 0x2019, 0xA1AF, 0x201C, 0xA1B0, 0x201D, 0xA1B1,\n\t0x2020, 0xA2D3, 0x2021, 0xA2D4, 0x2025, 0xA1A5, 0x2026, 0xA1A6,\n\t0x2030, 0xA2B6, 0x2032, 0xA1C7, 0x2033, 0xA1C8, 0x203B, 0xA1D8,\n\t0x2074, 0xA9F9, 0x207F, 0xA9FA, 0x2081, 0xA9FB, 0x2082, 0xA9FC,\n\t0x2083, 0xA9FD, 0x2084, 0xA9FE, 0x20AC, 0xA2E6, 0x2103, 0xA1C9,\n\t0x2109, 0xA2B5, 0x2113, 0xA7A4, 0x2116, 0xA2E0, 0x2121, 0xA2E5,\n\t0x2122, 0xA2E2, 0x2126, 0xA7D9, 0x212B, 0xA1CA, 0x2153, 0xA8F7,\n\t0x2154, 0xA8F8, 0x215B, 0xA8FB, 0x215C, 0xA8FC, 0x215D, 0xA8FD,\n\t0x215E, 0xA8FE, 0x2160, 0xA5B0, 0x2161, 0xA5B1, 0x2162, 0xA5B2,\n\t0x2163, 0xA5B3, 0x2164, 0xA5B4, 0x2165, 0xA5B5, 0x2166, 0xA5B6,\n\t0x2167, 0xA5B7, 0x2168, 0xA5B8, 0x2169, 0xA5B9, 0x2170, 0xA5A1,\n\t0x2171, 0xA5A2, 0x2172, 0xA5A3, 0x2173, 0xA5A4, 0x2174, 0xA5A5,\n\t0x2175, 0xA5A6, 0x2176, 0xA5A7, 0x2177, 0xA5A8, 0x2178, 0xA5A9,\n\t0x2179, 0xA5AA, 0x2190, 0xA1E7, 0x2191, 0xA1E8, 0x2192, 0xA1E6,\n\t0x2193, 0xA1E9, 0x2194, 0xA1EA, 0x2195, 0xA2D5, 0x2196, 0xA2D8,\n\t0x2197, 0xA2D6, 0x2198, 0xA2D9, 0x2199, 0xA2D7, 0x21D2, 0xA2A1,\n\t0x21D4, 0xA2A2, 0x2200, 0xA2A3, 0x2202, 0xA1D3, 0x2203, 0xA2A4,\n\t0x2207, 0xA1D4, 0x2208, 0xA1F4, 0x220B, 0xA1F5, 0x220F, 0xA2B3,\n\t0x2211, 0xA2B2, 0x221A, 0xA1EE, 0x221D, 0xA1F0, 0x221E, 0xA1C4,\n\t0x2220, 0xA1D0, 0x2225, 0xA1AB, 0x2227, 0xA1FC, 0x2228, 0xA1FD,\n\t0x2229, 0xA1FB, 0x222A, 0xA1FA, 0x222B, 0xA1F2, 0x222C, 0xA1F3,\n\t0x222E, 0xA2B1, 0x2234, 0xA1C5, 0x2235, 0xA1F1, 0x223C, 0xA1AD,\n\t0x223D, 0xA1EF, 0x2252, 0xA1D6, 0x2260, 0xA1C1, 0x2261, 0xA1D5,\n\t0x2264, 0xA1C2, 0x2265, 0xA1C3, 0x226A, 0xA1EC, 0x226B, 0xA1ED,\n\t0x2282, 0xA1F8, 0x2283, 0xA1F9, 0x2286, 0xA1F6, 0x2287, 0xA1F7,\n\t0x2299, 0xA2C1, 0x22A5, 0xA1D1, 0x2312, 0xA1D2, 0x2460, 0xA8E7,\n\t0x2461, 0xA8E8, 0x2462, 0xA8E9, 0x2463, 0xA8EA, 0x2464, 0xA8EB,\n\t0x2465, 0xA8EC, 0x2466, 0xA8ED, 0x2467, 0xA8EE, 0x2468, 0xA8EF,\n\t0x2469, 0xA8F0, 0x246A, 0xA8F1, 0x246B, 0xA8F2, 0x246C, 0xA8F3,\n\t0x246D, 0xA8F4, 0x246E, 0xA8F5, 0x2474, 0xA9E7, 0x2475, 0xA9E8,\n\t0x2476, 0xA9E9, 0x2477, 0xA9EA, 0x2478, 0xA9EB, 0x2479, 0xA9EC,\n\t0x247A, 0xA9ED, 0x247B, 0xA9EE, 0x247C, 0xA9EF, 0x247D, 0xA9F0,\n\t0x247E, 0xA9F1, 0x247F, 0xA9F2, 0x2480, 0xA9F3, 0x2481, 0xA9F4,\n\t0x2482, 0xA9F5, 0x249C, 0xA9CD, 0x249D, 0xA9CE, 0x249E, 0xA9CF,\n\t0x249F, 0xA9D0, 0x24A0, 0xA9D1, 0x24A1, 0xA9D2, 0x24A2, 0xA9D3,\n\t0x24A3, 0xA9D4, 0x24A4, 0xA9D5, 0x24A5, 0xA9D6, 0x24A6, 0xA9D7,\n\t0x24A7, 0xA9D8, 0x24A8, 0xA9D9, 0x24A9, 0xA9DA, 0x24AA, 0xA9DB,\n\t0x24AB, 0xA9DC, 0x24AC, 0xA9DD, 0x24AD, 0xA9DE, 0x24AE, 0xA9DF,\n\t0x24AF, 0xA9E0, 0x24B0, 0xA9E1, 0x24B1, 0xA9E2, 0x24B2, 0xA9E3,\n\t0x24B3, 0xA9E4, 0x24B4, 0xA9E5, 0x24B5, 0xA9E6, 0x24D0, 0xA8CD,\n\t0x24D1, 0xA8CE, 0x24D2, 0xA8CF, 0x24D3, 0xA8D0, 0x24D4, 0xA8D1,\n\t0x24D5, 0xA8D2, 0x24D6, 0xA8D3, 0x24D7, 0xA8D4, 0x24D8, 0xA8D5,\n\t0x24D9, 0xA8D6, 0x24DA, 0xA8D7, 0x24DB, 0xA8D8, 0x24DC, 0xA8D9,\n\t0x24DD, 0xA8DA, 0x24DE, 0xA8DB, 0x24DF, 0xA8DC, 0x24E0, 0xA8DD,\n\t0x24E1, 0xA8DE, 0x24E2, 0xA8DF, 0x24E3, 0xA8E0, 0x24E4, 0xA8E1,\n\t0x24E5, 0xA8E2, 0x24E6, 0xA8E3, 0x24E7, 0xA8E4, 0x24E8, 0xA8E5,\n\t0x24E9, 0xA8E6, 0x2500, 0xA6A1, 0x2501, 0xA6AC, 0x2502, 0xA6A2,\n\t0x2503, 0xA6AD, 0x250C, 0xA6A3, 0x250D, 0xA6C8, 0x250E, 0xA6C7,\n\t0x250F, 0xA6AE, 0x2510, 0xA6A4, 0x2511, 0xA6C2, 0x2512, 0xA6C1,\n\t0x2513, 0xA6AF, 0x2514, 0xA6A6, 0x2515, 0xA6C6, 0x2516, 0xA6C5,\n\t0x2517, 0xA6B1, 0x2518, 0xA6A5, 0x2519, 0xA6C4, 0x251A, 0xA6C3,\n\t0x251B, 0xA6B0, 0x251C, 0xA6A7, 0x251D, 0xA6BC, 0x251E, 0xA6C9,\n\t0x251F, 0xA6CA, 0x2520, 0xA6B7, 0x2521, 0xA6CB, 0x2522, 0xA6CC,\n\t0x2523, 0xA6B2, 0x2524, 0xA6A9, 0x2525, 0xA6BE, 0x2526, 0xA6CD,\n\t0x2527, 0xA6CE, 0x2528, 0xA6B9, 0x2529, 0xA6CF, 0x252A, 0xA6D0,\n\t0x252B, 0xA6B4, 0x252C, 0xA6A8, 0x252D, 0xA6D1, 0x252E, 0xA6D2,\n\t0x252F, 0xA6B8, 0x2530, 0xA6BD, 0x2531, 0xA6D3, 0x2532, 0xA6D4,\n\t0x2533, 0xA6B3, 0x2534, 0xA6AA, 0x2535, 0xA6D5, 0x2536, 0xA6D6,\n\t0x2537, 0xA6BA, 0x2538, 0xA6BF, 0x2539, 0xA6D7, 0x253A, 0xA6D8,\n\t0x253B, 0xA6B5, 0x253C, 0xA6AB, 0x253D, 0xA6D9, 0x253E, 0xA6DA,\n\t0x253F, 0xA6BB, 0x2540, 0xA6DB, 0x2541, 0xA6DC, 0x2542, 0xA6C0,\n\t0x2543, 0xA6DD, 0x2544, 0xA6DE, 0x2545, 0xA6DF, 0x2546, 0xA6E0,\n\t0x2547, 0xA6E1, 0x2548, 0xA6E2, 0x2549, 0xA6E3, 0x254A, 0xA6E4,\n\t0x254B, 0xA6B6, 0x2592, 0xA2C6, 0x25A0, 0xA1E1, 0x25A1, 0xA1E0,\n\t0x25A3, 0xA2C3, 0x25A4, 0xA2C7, 0x25A5, 0xA2C8, 0x25A6, 0xA2CB,\n\t0x25A7, 0xA2CA, 0x25A8, 0xA2C9, 0x25A9, 0xA2CC, 0x25B2, 0xA1E3,\n\t0x25B3, 0xA1E2, 0x25B6, 0xA2BA, 0x25B7, 0xA2B9, 0x25BC, 0xA1E5,\n\t0x25BD, 0xA1E4, 0x25C0, 0xA2B8, 0x25C1, 0xA2B7, 0x25C6, 0xA1DF,\n\t0x25C7, 0xA1DE, 0x25C8, 0xA2C2, 0x25CB, 0xA1DB, 0x25CE, 0xA1DD,\n\t0x25CF, 0xA1DC, 0x25D0, 0xA2C4, 0x25D1, 0xA2C5, 0x2605, 0xA1DA,\n\t0x2606, 0xA1D9, 0x260E, 0xA2CF, 0x260F, 0xA2CE, 0x261C, 0xA2D0,\n\t0x261E, 0xA2D1, 0x2640, 0xA1CF, 0x2642, 0xA1CE, 0x2660, 0xA2BC,\n\t0x2661, 0xA2BD, 0x2663, 0xA2C0, 0x2664, 0xA2BB, 0x2665, 0xA2BE,\n\t0x2667, 0xA2BF, 0x2668, 0xA2CD, 0x2669, 0xA2DB, 0x266A, 0xA2DC,\n\t0x266C, 0xA2DD, 0x266D, 0xA2DA, 0x3000, 0xA1A1, 0x3001, 0xA1A2,\n\t0x3002, 0xA1A3, 0x3003, 0xA1A8, 0x3008, 0xA1B4, 0x3009, 0xA1B5,\n\t0x300A, 0xA1B6, 0x300B, 0xA1B7, 0x300C, 0xA1B8, 0x300D, 0xA1B9,\n\t0x300E, 0xA1BA, 0x300F, 0xA1BB, 0x3010, 0xA1BC, 0x3011, 0xA1BD,\n\t0x3013, 0xA1EB, 0x3014, 0xA1B2, 0x3015, 0xA1B3, 0x3041, 0xAAA1,\n\t0x3042, 0xAAA2, 0x3043, 0xAAA3, 0x3044, 0xAAA4, 0x3045, 0xAAA5,\n\t0x3046, 0xAAA6, 0x3047, 0xAAA7, 0x3048, 0xAAA8, 0x3049, 0xAAA9,\n\t0x304A, 0xAAAA, 0x304B, 0xAAAB, 0x304C, 0xAAAC, 0x304D, 0xAAAD,\n\t0x304E, 0xAAAE, 0x304F, 0xAAAF, 0x3050, 0xAAB0, 0x3051, 0xAAB1,\n\t0x3052, 0xAAB2, 0x3053, 0xAAB3, 0x3054, 0xAAB4, 0x3055, 0xAAB5,\n\t0x3056, 0xAAB6, 0x3057, 0xAAB7, 0x3058, 0xAAB8, 0x3059, 0xAAB9,\n\t0x305A, 0xAABA, 0x305B, 0xAABB, 0x305C, 0xAABC, 0x305D, 0xAABD,\n\t0x305E, 0xAABE, 0x305F, 0xAABF, 0x3060, 0xAAC0, 0x3061, 0xAAC1,\n\t0x3062, 0xAAC2, 0x3063, 0xAAC3, 0x3064, 0xAAC4, 0x3065, 0xAAC5,\n\t0x3066, 0xAAC6, 0x3067, 0xAAC7, 0x3068, 0xAAC8, 0x3069, 0xAAC9,\n\t0x306A, 0xAACA, 0x306B, 0xAACB, 0x306C, 0xAACC, 0x306D, 0xAACD,\n\t0x306E, 0xAACE, 0x306F, 0xAACF, 0x3070, 0xAAD0, 0x3071, 0xAAD1,\n\t0x3072, 0xAAD2, 0x3073, 0xAAD3, 0x3074, 0xAAD4, 0x3075, 0xAAD5,\n\t0x3076, 0xAAD6, 0x3077, 0xAAD7, 0x3078, 0xAAD8, 0x3079, 0xAAD9,\n\t0x307A, 0xAADA, 0x307B, 0xAADB, 0x307C, 0xAADC, 0x307D, 0xAADD,\n\t0x307E, 0xAADE, 0x307F, 0xAADF, 0x3080, 0xAAE0, 0x3081, 0xAAE1,\n\t0x3082, 0xAAE2, 0x3083, 0xAAE3, 0x3084, 0xAAE4, 0x3085, 0xAAE5,\n\t0x3086, 0xAAE6, 0x3087, 0xAAE7, 0x3088, 0xAAE8, 0x3089, 0xAAE9,\n\t0x308A, 0xAAEA, 0x308B, 0xAAEB, 0x308C, 0xAAEC, 0x308D, 0xAAED,\n\t0x308E, 0xAAEE, 0x308F, 0xAAEF, 0x3090, 0xAAF0, 0x3091, 0xAAF1,\n\t0x3092, 0xAAF2, 0x3093, 0xAAF3, 0x30A1, 0xABA1, 0x30A2, 0xABA2,\n\t0x30A3, 0xABA3, 0x30A4, 0xABA4, 0x30A5, 0xABA5, 0x30A6, 0xABA6,\n\t0x30A7, 0xABA7, 0x30A8, 0xABA8, 0x30A9, 0xABA9, 0x30AA, 0xABAA,\n\t0x30AB, 0xABAB, 0x30AC, 0xABAC, 0x30AD, 0xABAD, 0x30AE, 0xABAE,\n\t0x30AF, 0xABAF, 0x30B0, 0xABB0, 0x30B1, 0xABB1, 0x30B2, 0xABB2,\n\t0x30B3, 0xABB3, 0x30B4, 0xABB4, 0x30B5, 0xABB5, 0x30B6, 0xABB6,\n\t0x30B7, 0xABB7, 0x30B8, 0xABB8, 0x30B9, 0xABB9, 0x30BA, 0xABBA,\n\t0x30BB, 0xABBB, 0x30BC, 0xABBC, 0x30BD, 0xABBD, 0x30BE, 0xABBE,\n\t0x30BF, 0xABBF, 0x30C0, 0xABC0, 0x30C1, 0xABC1, 0x30C2, 0xABC2,\n\t0x30C3, 0xABC3, 0x30C4, 0xABC4, 0x30C5, 0xABC5, 0x30C6, 0xABC6,\n\t0x30C7, 0xABC7, 0x30C8, 0xABC8, 0x30C9, 0xABC9, 0x30CA, 0xABCA,\n\t0x30CB, 0xABCB, 0x30CC, 0xABCC, 0x30CD, 0xABCD, 0x30CE, 0xABCE,\n\t0x30CF, 0xABCF, 0x30D0, 0xABD0, 0x30D1, 0xABD1, 0x30D2, 0xABD2,\n\t0x30D3, 0xABD3, 0x30D4, 0xABD4, 0x30D5, 0xABD5, 0x30D6, 0xABD6,\n\t0x30D7, 0xABD7, 0x30D8, 0xABD8, 0x30D9, 0xABD9, 0x30DA, 0xABDA,\n\t0x30DB, 0xABDB, 0x30DC, 0xABDC, 0x30DD, 0xABDD, 0x30DE, 0xABDE,\n\t0x30DF, 0xABDF, 0x30E0, 0xABE0, 0x30E1, 0xABE1, 0x30E2, 0xABE2,\n\t0x30E3, 0xABE3, 0x30E4, 0xABE4, 0x30E5, 0xABE5, 0x30E6, 0xABE6,\n\t0x30E7, 0xABE7, 0x30E8, 0xABE8, 0x30E9, 0xABE9, 0x30EA, 0xABEA,\n\t0x30EB, 0xABEB, 0x30EC, 0xABEC, 0x30ED, 0xABED, 0x30EE, 0xABEE,\n\t0x30EF, 0xABEF, 0x30F0, 0xABF0, 0x30F1, 0xABF1, 0x30F2, 0xABF2,\n\t0x30F3, 0xABF3, 0x30F4, 0xABF4, 0x30F5, 0xABF5, 0x30F6, 0xABF6,\n\t0x3131, 0xA4A1, 0x3132, 0xA4A2, 0x3133, 0xA4A3, 0x3134, 0xA4A4,\n\t0x3135, 0xA4A5, 0x3136, 0xA4A6, 0x3137, 0xA4A7, 0x3138, 0xA4A8,\n\t0x3139, 0xA4A9, 0x313A, 0xA4AA, 0x313B, 0xA4AB, 0x313C, 0xA4AC,\n\t0x313D, 0xA4AD, 0x313E, 0xA4AE, 0x313F, 0xA4AF, 0x3140, 0xA4B0,\n\t0x3141, 0xA4B1, 0x3142, 0xA4B2, 0x3143, 0xA4B3, 0x3144, 0xA4B4,\n\t0x3145, 0xA4B5, 0x3146, 0xA4B6, 0x3147, 0xA4B7, 0x3148, 0xA4B8,\n\t0x3149, 0xA4B9, 0x314A, 0xA4BA, 0x314B, 0xA4BB, 0x314C, 0xA4BC,\n\t0x314D, 0xA4BD, 0x314E, 0xA4BE, 0x314F, 0xA4BF, 0x3150, 0xA4C0,\n\t0x3151, 0xA4C1, 0x3152, 0xA4C2, 0x3153, 0xA4C3, 0x3154, 0xA4C4,\n\t0x3155, 0xA4C5, 0x3156, 0xA4C6, 0x3157, 0xA4C7, 0x3158, 0xA4C8,\n\t0x3159, 0xA4C9, 0x315A, 0xA4CA, 0x315B, 0xA4CB, 0x315C, 0xA4CC,\n\t0x315D, 0xA4CD, 0x315E, 0xA4CE, 0x315F, 0xA4CF, 0x3160, 0xA4D0,\n\t0x3161, 0xA4D1, 0x3162, 0xA4D2, 0x3163, 0xA4D3, 0x3164, 0xA4D4,\n\t0x3165, 0xA4D5, 0x3166, 0xA4D6, 0x3167, 0xA4D7, 0x3168, 0xA4D8,\n\t0x3169, 0xA4D9, 0x316A, 0xA4DA, 0x316B, 0xA4DB, 0x316C, 0xA4DC,\n\t0x316D, 0xA4DD, 0x316E, 0xA4DE, 0x316F, 0xA4DF, 0x3170, 0xA4E0,\n\t0x3171, 0xA4E1, 0x3172, 0xA4E2, 0x3173, 0xA4E3, 0x3174, 0xA4E4,\n\t0x3175, 0xA4E5, 0x3176, 0xA4E6, 0x3177, 0xA4E7, 0x3178, 0xA4E8,\n\t0x3179, 0xA4E9, 0x317A, 0xA4EA, 0x317B, 0xA4EB, 0x317C, 0xA4EC,\n\t0x317D, 0xA4ED, 0x317E, 0xA4EE, 0x317F, 0xA4EF, 0x3180, 0xA4F0,\n\t0x3181, 0xA4F1, 0x3182, 0xA4F2, 0x3183, 0xA4F3, 0x3184, 0xA4F4,\n\t0x3185, 0xA4F5, 0x3186, 0xA4F6, 0x3187, 0xA4F7, 0x3188, 0xA4F8,\n\t0x3189, 0xA4F9, 0x318A, 0xA4FA, 0x318B, 0xA4FB, 0x318C, 0xA4FC,\n\t0x318D, 0xA4FD, 0x318E, 0xA4FE, 0x3200, 0xA9B1, 0x3201, 0xA9B2,\n\t0x3202, 0xA9B3, 0x3203, 0xA9B4, 0x3204, 0xA9B5, 0x3205, 0xA9B6,\n\t0x3206, 0xA9B7, 0x3207, 0xA9B8, 0x3208, 0xA9B9, 0x3209, 0xA9BA,\n\t0x320A, 0xA9BB, 0x320B, 0xA9BC, 0x320C, 0xA9BD, 0x320D, 0xA9BE,\n\t0x320E, 0xA9BF, 0x320F, 0xA9C0, 0x3210, 0xA9C1, 0x3211, 0xA9C2,\n\t0x3212, 0xA9C3, 0x3213, 0xA9C4, 0x3214, 0xA9C5, 0x3215, 0xA9C6,\n\t0x3216, 0xA9C7, 0x3217, 0xA9C8, 0x3218, 0xA9C9, 0x3219, 0xA9CA,\n\t0x321A, 0xA9CB, 0x321B, 0xA9CC, 0x321C, 0xA2DF, 0x3260, 0xA8B1,\n\t0x3261, 0xA8B2, 0x3262, 0xA8B3, 0x3263, 0xA8B4, 0x3264, 0xA8B5,\n\t0x3265, 0xA8B6, 0x3266, 0xA8B7, 0x3267, 0xA8B8, 0x3268, 0xA8B9,\n\t0x3269, 0xA8BA, 0x326A, 0xA8BB, 0x326B, 0xA8BC, 0x326C, 0xA8BD,\n\t0x326D, 0xA8BE, 0x326E, 0xA8BF, 0x326F, 0xA8C0, 0x3270, 0xA8C1,\n\t0x3271, 0xA8C2, 0x3272, 0xA8C3, 0x3273, 0xA8C4, 0x3274, 0xA8C5,\n\t0x3275, 0xA8C6, 0x3276, 0xA8C7, 0x3277, 0xA8C8, 0x3278, 0xA8C9,\n\t0x3279, 0xA8CA, 0x327A, 0xA8CB, 0x327B, 0xA8CC, 0x327F, 0xA2DE,\n\t0x3380, 0xA7C9, 0x3381, 0xA7CA, 0x3382, 0xA7CB, 0x3383, 0xA7CC,\n\t0x3384, 0xA7CD, 0x3388, 0xA7BA, 0x3389, 0xA7BB, 0x338A, 0xA7DC,\n\t0x338B, 0xA7DD, 0x338C, 0xA7DE, 0x338D, 0xA7B6, 0x338E, 0xA7B7,\n\t0x338F, 0xA7B8, 0x3390, 0xA7D4, 0x3391, 0xA7D5, 0x3392, 0xA7D6,\n\t0x3393, 0xA7D7, 0x3394, 0xA7D8, 0x3395, 0xA7A1, 0x3396, 0xA7A2,\n\t0x3397, 0xA7A3, 0x3398, 0xA7A5, 0x3399, 0xA7AB, 0x339A, 0xA7AC,\n\t0x339B, 0xA7AD, 0x339C, 0xA7AE, 0x339D, 0xA7AF, 0x339E, 0xA7B0,\n\t0x339F, 0xA7B1, 0x33A0, 0xA7B2, 0x33A1, 0xA7B3, 0x33A2, 0xA7B4,\n\t0x33A3, 0xA7A7, 0x33A4, 0xA7A8, 0x33A5, 0xA7A9, 0x33A6, 0xA7AA,\n\t0x33A7, 0xA7BD, 0x33A8, 0xA7BE, 0x33A9, 0xA7E5, 0x33AA, 0xA7E6,\n\t0x33AB, 0xA7E7, 0x33AC, 0xA7E8, 0x33AD, 0xA7E1, 0x33AE, 0xA7E2,\n\t0x33AF, 0xA7E3, 0x33B0, 0xA7BF, 0x33B1, 0xA7C0, 0x33B2, 0xA7C1,\n\t0x33B3, 0xA7C2, 0x33B4, 0xA7C3, 0x33B5, 0xA7C4, 0x33B6, 0xA7C5,\n\t0x33B7, 0xA7C6, 0x33B8, 0xA7C7, 0x33B9, 0xA7C8, 0x33BA, 0xA7CE,\n\t0x33BB, 0xA7CF, 0x33BC, 0xA7D0, 0x33BD, 0xA7D1, 0x33BE, 0xA7D2,\n\t0x33BF, 0xA7D3, 0x33C0, 0xA7DA, 0x33C1, 0xA7DB, 0x33C2, 0xA2E3,\n\t0x33C3, 0xA7EC, 0x33C4, 0xA7A6, 0x33C5, 0xA7E0, 0x33C6, 0xA7EF,\n\t0x33C7, 0xA2E1, 0x33C8, 0xA7BC, 0x33C9, 0xA7ED, 0x33CA, 0xA7B5,\n\t0x33CF, 0xA7B9, 0x33D0, 0xA7EA, 0x33D3, 0xA7EB, 0x33D6, 0xA7DF,\n\t0x33D8, 0xA2E4, 0x33DB, 0xA7E4, 0x33DC, 0xA7EE, 0x33DD, 0xA7E9,\n\t0x4E00, 0xECE9, 0x4E01, 0xEFCB, 0x4E03, 0xF6D2, 0x4E07, 0xD8B2,\n\t0x4E08, 0xEDDB, 0x4E09, 0xDFB2, 0x4E0A, 0xDFBE, 0x4E0B, 0xF9BB,\n\t0x4E0D, 0xDCF4, 0x4E11, 0xF5E4, 0x4E14, 0xF3A6, 0x4E15, 0xDDE0,\n\t0x4E16, 0xE1A6, 0x4E18, 0xCEF8, 0x4E19, 0xDCB0, 0x4E1E, 0xE3AA,\n\t0x4E2D, 0xF1E9, 0x4E32, 0xCDFA, 0x4E38, 0xFCAF, 0x4E39, 0xD3A1,\n\t0x4E3B, 0xF1AB, 0x4E42, 0xE7D1, 0x4E43, 0xD2AC, 0x4E45, 0xCEF9,\n\t0x4E4B, 0xF1FD, 0x4E4D, 0xDEBF, 0x4E4E, 0xFBBA, 0x4E4F, 0xF9B9,\n\t0x4E56, 0xCED2, 0x4E58, 0xE3AB, 0x4E59, 0xEBE0, 0x4E5D, 0xCEFA,\n\t0x4E5E, 0xCBF7, 0x4E5F, 0xE5A5, 0x4E6B, 0xCAE1, 0x4E6D, 0xD4CC,\n\t0x4E73, 0xEAE1, 0x4E76, 0xDCE3, 0x4E77, 0xDFAD, 0x4E7E, 0xCBEB,\n\t0x4E82, 0xD5AF, 0x4E86, 0xD6F5, 0x4E88, 0xE5F8, 0x4E8B, 0xDEC0,\n\t0x4E8C, 0xECA3, 0x4E8E, 0xE9CD, 0x4E90, 0xEAA7, 0x4E91, 0xE9F6,\n\t0x4E92, 0xFBBB, 0x4E94, 0xE7E9, 0x4E95, 0xEFCC, 0x4E98, 0xD0E6,\n\t0x4E9B, 0xDEC1, 0x4E9E, 0xE4AC, 0x4EA1, 0xD8CC, 0x4EA2, 0xF9F1,\n\t0x4EA4, 0xCEDF, 0x4EA5, 0xFAA4, 0x4EA6, 0xE6B2, 0x4EA8, 0xFAFB,\n\t0x4EAB, 0xFABD, 0x4EAC, 0xCCC8, 0x4EAD, 0xEFCD, 0x4EAE, 0xD5D5,\n\t0x4EB6, 0xD3A2, 0x4EBA, 0xECD1, 0x4EC0, 0xE4A7, 0x4EC1, 0xECD2,\n\t0x4EC4, 0xF6B1, 0x4EC7, 0xCEFB, 0x4ECA, 0xD0D1, 0x4ECB, 0xCBBF,\n\t0x4ECD, 0xEDA4, 0x4ED4, 0xEDA8, 0x4ED5, 0xDEC2, 0x4ED6, 0xF6E2,\n\t0x4ED7, 0xEDDC, 0x4ED8, 0xDCF5, 0x4ED9, 0xE0B9, 0x4EDD, 0xD4CE,\n\t0x4EDF, 0xF4B5, 0x4EE3, 0xD3DB, 0x4EE4, 0xD6B5, 0x4EE5, 0xECA4,\n\t0x4EF0, 0xE4E6, 0x4EF2, 0xF1EA, 0x4EF6, 0xCBEC, 0x4EF7, 0xCBC0,\n\t0x4EFB, 0xECF2, 0x4F01, 0xD0EA, 0x4F09, 0xF9F2, 0x4F0A, 0xECA5,\n\t0x4F0B, 0xD0DF, 0x4F0D, 0xE7EA, 0x4F0E, 0xD0EB, 0x4F0F, 0xDCD1,\n\t0x4F10, 0xDBE9, 0x4F11, 0xFDCC, 0x4F2F, 0xDBD7, 0x4F34, 0xDAE1,\n\t0x4F36, 0xD6B6, 0x4F38, 0xE3DF, 0x4F3A, 0xDEC3, 0x4F3C, 0xDEC4,\n\t0x4F3D, 0xCAA1, 0x4F43, 0xEEEC, 0x4F46, 0xD3A3, 0x4F47, 0xEEB7,\n\t0x4F48, 0xF8CF, 0x4F4D, 0xEAC8, 0x4F4E, 0xEEB8, 0x4F4F, 0xF1AC,\n\t0x4F50, 0xF1A5, 0x4F51, 0xE9CE, 0x4F55, 0xF9BC, 0x4F59, 0xE5F9,\n\t0x4F5A, 0xECEA, 0x4F5B, 0xDDD6, 0x4F5C, 0xEDC2, 0x4F69, 0xF8A5,\n\t0x4F6F, 0xE5BA, 0x4F70, 0xDBD8, 0x4F73, 0xCAA2, 0x4F76, 0xD1CD,\n\t0x4F7A, 0xEEED, 0x4F7E, 0xECEB, 0x4F7F, 0xDEC5, 0x4F81, 0xE3E0,\n\t0x4F83, 0xCAC9, 0x4F84, 0xF2E9, 0x4F86, 0xD5CE, 0x4F88, 0xF6B6,\n\t0x4F8A, 0xCEC2, 0x4F8B, 0xD6C7, 0x4F8D, 0xE3B4, 0x4F8F, 0xF1AD,\n\t0x4F91, 0xEAE2, 0x4F96, 0xD7C2, 0x4F98, 0xF3A7, 0x4F9B, 0xCDEA,\n\t0x4F9D, 0xEBEE, 0x4FAE, 0xD9B2, 0x4FAF, 0xFDA5, 0x4FB5, 0xF6D5,\n\t0x4FB6, 0xD5E2, 0x4FBF, 0xF8B5, 0x4FC2, 0xCCF5, 0x4FC3, 0xF5B5,\n\t0x4FC4, 0xE4AD, 0x4FC9, 0xE7EB, 0x4FCA, 0xF1D5, 0x4FCE, 0xF0BB,\n\t0x4FD1, 0xE9B5, 0x4FD3, 0xCCC9, 0x4FD4, 0xFAD5, 0x4FD7, 0xE1D4,\n\t0x4FDA, 0xD7D6, 0x4FDD, 0xDCC1, 0x4FDF, 0xDEC6, 0x4FE0, 0xFAEF,\n\t0x4FE1, 0xE3E1, 0x4FEE, 0xE1F3, 0x4FEF, 0xDCF6, 0x4FF1, 0xCEFC,\n\t0x4FF3, 0xDBC4, 0x4FF5, 0xF8F1, 0x4FF8, 0xDCE4, 0x4FFA, 0xE5EF,\n\t0x5002, 0xDCB1, 0x5006, 0xD5D6, 0x5009, 0xF3DA, 0x500B, 0xCBC1,\n\t0x500D, 0xDBC3, 0x5011, 0xD9FA, 0x5012, 0xD3EE, 0x5016, 0xFAB8,\n\t0x5019, 0xFDA6, 0x501A, 0xEBEF, 0x501C, 0xF4A6, 0x501E, 0xCCCA,\n\t0x501F, 0xF3A8, 0x5021, 0xF3DB, 0x5023, 0xDBA7, 0x5024, 0xF6B7,\n\t0x5026, 0xCFE6, 0x5027, 0xF0F2, 0x5028, 0xCBDA, 0x502A, 0xE7D2,\n\t0x502B, 0xD7C3, 0x502C, 0xF6F0, 0x502D, 0xE8DE, 0x503B, 0xE5A6,\n\t0x5043, 0xE5E7, 0x5047, 0xCAA3, 0x5048, 0xCCA7, 0x5049, 0xEAC9,\n\t0x504F, 0xF8B6, 0x5055, 0xFAA5, 0x505A, 0xF1AE, 0x505C, 0xEFCE,\n\t0x5065, 0xCBED, 0x5074, 0xF6B0, 0x5075, 0xEFCF, 0x5076, 0xE9CF,\n\t0x5078, 0xF7DE, 0x5080, 0xCED3, 0x5085, 0xDCF7, 0x508D, 0xDBA8,\n\t0x5091, 0xCBF8, 0x5098, 0xDFA1, 0x5099, 0xDDE1, 0x50AC, 0xF5CA,\n\t0x50AD, 0xE9B6, 0x50B2, 0xE7EC, 0x50B3, 0xEEEE, 0x50B5, 0xF3F0,\n\t0x50B7, 0xDFBF, 0x50BE, 0xCCCB, 0x50C5, 0xD0C1, 0x50C9, 0xF4D2,\n\t0x50CA, 0xE0BA, 0x50CF, 0xDFC0, 0x50D1, 0xCEE0, 0x50D5, 0xDCD2,\n\t0x50D6, 0xFDEA, 0x50DA, 0xD6F6, 0x50DE, 0xEACA, 0x50E5, 0xE8E9,\n\t0x50E7, 0xE3AC, 0x50ED, 0xF3D0, 0x50F9, 0xCAA4, 0x50FB, 0xDBF8,\n\t0x50FF, 0xDEC7, 0x5100, 0xEBF0, 0x5101, 0xF1D6, 0x5104, 0xE5E2,\n\t0x5106, 0xCCCC, 0x5109, 0xCBFB, 0x5112, 0xEAE3, 0x511F, 0xDFC1,\n\t0x5121, 0xD6ED, 0x512A, 0xE9D0, 0x5132, 0xEEB9, 0x5137, 0xD5E3,\n\t0x513A, 0xD1D3, 0x513C, 0xE5F0, 0x5140, 0xE8B4, 0x5141, 0xEBC3,\n\t0x5143, 0xEAAA, 0x5144, 0xFAFC, 0x5145, 0xF5F6, 0x5146, 0xF0BC,\n\t0x5147, 0xFDD4, 0x5148, 0xE0BB, 0x5149, 0xCEC3, 0x514B, 0xD0BA,\n\t0x514C, 0xF7BA, 0x514D, 0xD8F3, 0x514E, 0xF7CD, 0x5152, 0xE4AE,\n\t0x515C, 0xD4DF, 0x5162, 0xD0E7, 0x5165, 0xECFD, 0x5167, 0xD2AE,\n\t0x5168, 0xEEEF, 0x5169, 0xD5D7, 0x516A, 0xEAE4, 0x516B, 0xF8A2,\n\t0x516C, 0xCDEB, 0x516D, 0xD7BF, 0x516E, 0xFBB1, 0x5171, 0xCDEC,\n\t0x5175, 0xDCB2, 0x5176, 0xD0EC, 0x5177, 0xCEFD, 0x5178, 0xEEF0,\n\t0x517C, 0xCCC2, 0x5180, 0xD0ED, 0x5186, 0xE5F7, 0x518A, 0xF3FC,\n\t0x518D, 0xEEA2, 0x5192, 0xD9B3, 0x5195, 0xD8F4, 0x5197, 0xE9B7,\n\t0x51A0, 0xCEAE, 0x51A5, 0xD9A2, 0x51AA, 0xD8F1, 0x51AC, 0xD4CF,\n\t0x51B6, 0xE5A7, 0x51B7, 0xD5D2, 0x51BD, 0xD6A9, 0x51C4, 0xF4A2,\n\t0x51C6, 0xF1D7, 0x51C9, 0xD5D8, 0x51CB, 0xF0BD, 0x51CC, 0xD7D0,\n\t0x51CD, 0xD4D0, 0x51DC, 0xD7CF, 0x51DD, 0xEBEA, 0x51DE, 0xFDEB,\n\t0x51E1, 0xDBED, 0x51F0, 0xFCC5, 0x51F1, 0xCBC2, 0x51F6, 0xFDD5,\n\t0x51F8, 0xF4C8, 0x51F9, 0xE8EA, 0x51FA, 0xF5F3, 0x51FD, 0xF9DE,\n\t0x5200, 0xD3EF, 0x5203, 0xECD3, 0x5206, 0xDDC2, 0x5207, 0xEFB7,\n\t0x5208, 0xE7D4, 0x520A, 0xCACA, 0x520E, 0xD9FB, 0x5211, 0xFAFD,\n\t0x5217, 0xD6AA, 0x521D, 0xF4F8, 0x5224, 0xF7F7, 0x5225, 0xDCAC,\n\t0x5229, 0xD7D7, 0x522A, 0xDFA2, 0x522E, 0xCEBE, 0x5230, 0xD3F0,\n\t0x5236, 0xF0A4, 0x5237, 0xE1EC, 0x5238, 0xCFE7, 0x5239, 0xF3CB,\n\t0x523A, 0xEDA9, 0x523B, 0xCABE, 0x5243, 0xF4EF, 0x5247, 0xF6CE,\n\t0x524A, 0xDEFB, 0x524B, 0xD0BB, 0x524C, 0xD5B7, 0x524D, 0xEEF1,\n\t0x5254, 0xF4A8, 0x5256, 0xDCF8, 0x525B, 0xCBA7, 0x525D, 0xDACE,\n\t0x5261, 0xE0E6, 0x5269, 0xEDA5, 0x526A, 0xEEF2, 0x526F, 0xDCF9,\n\t0x5272, 0xF9DC, 0x5275, 0xF3DC, 0x527D, 0xF8F2, 0x527F, 0xF4F9,\n\t0x5283, 0xFCF1, 0x5287, 0xD0BC, 0x5288, 0xDBF9, 0x5289, 0xD7B1,\n\t0x528D, 0xCBFC, 0x5291, 0xF0A5, 0x5292, 0xCBFD, 0x529B, 0xD5F4,\n\t0x529F, 0xCDED, 0x52A0, 0xCAA5, 0x52A3, 0xD6AB, 0x52A4, 0xD0C2,\n\t0x52A9, 0xF0BE, 0x52AA, 0xD2BD, 0x52AB, 0xCCA4, 0x52BE, 0xFAB6,\n\t0x52C1, 0xCCCD, 0x52C3, 0xDAFA, 0x52C5, 0xF6CF, 0x52C7, 0xE9B8,\n\t0x52C9, 0xD8F5, 0x52CD, 0xCCCE, 0x52D2, 0xD7CD, 0x52D5, 0xD4D1,\n\t0x52D6, 0xE9ED, 0x52D8, 0xCAEB, 0x52D9, 0xD9E2, 0x52DB, 0xFDB2,\n\t0x52DD, 0xE3AD, 0x52DE, 0xD6CC, 0x52DF, 0xD9B4, 0x52E2, 0xE1A7,\n\t0x52E3, 0xEED3, 0x52E4, 0xD0C3, 0x52F3, 0xFDB3, 0x52F5, 0xD5E4,\n\t0x52F8, 0xCFE8, 0x52FA, 0xEDC3, 0x52FB, 0xD0B2, 0x52FE, 0xCEFE,\n\t0x52FF, 0xDAA8, 0x5305, 0xF8D0, 0x5308, 0xFDD6, 0x530D, 0xF8D1,\n\t0x530F, 0xF8D2, 0x5310, 0xDCD3, 0x5315, 0xDDE2, 0x5316, 0xFBF9,\n\t0x5317, 0xDDC1, 0x5319, 0xE3B5, 0x5320, 0xEDDD, 0x5321, 0xCEC4,\n\t0x5323, 0xCBA1, 0x532A, 0xDDE3, 0x532F, 0xFCDD, 0x5339, 0xF9AF,\n\t0x533F, 0xD2FB, 0x5340, 0xCFA1, 0x5341, 0xE4A8, 0x5343, 0xF4B6,\n\t0x5344, 0xECFE, 0x5347, 0xE3AE, 0x5348, 0xE7ED, 0x5349, 0xFDC1,\n\t0x534A, 0xDAE2, 0x534D, 0xD8B3, 0x5351, 0xDDE4, 0x5352, 0xF0EF,\n\t0x5353, 0xF6F1, 0x5354, 0xFAF0, 0x5357, 0xD1F5, 0x535A, 0xDACF,\n\t0x535C, 0xDCD4, 0x535E, 0xDCA6, 0x5360, 0xEFBF, 0x5366, 0xCECF,\n\t0x5368, 0xE0D9, 0x536F, 0xD9D6, 0x5370, 0xECD4, 0x5371, 0xEACB,\n\t0x5374, 0xCABF, 0x5375, 0xD5B0, 0x5377, 0xCFE9, 0x537D, 0xF1ED,\n\t0x537F, 0xCCCF, 0x5384, 0xE4F8, 0x5393, 0xE4ED, 0x5398, 0xD7D8,\n\t0x539A, 0xFDA7, 0x539F, 0xEAAB, 0x53A0, 0xF6B2, 0x53A5, 0xCFF0,\n\t0x53A6, 0xF9BD, 0x53AD, 0xE6F4, 0x53BB, 0xCBDB, 0x53C3, 0xF3D1,\n\t0x53C8, 0xE9D1, 0x53C9, 0xF3A9, 0x53CA, 0xD0E0, 0x53CB, 0xE9D2,\n\t0x53CD, 0xDAE3, 0x53D4, 0xE2D2, 0x53D6, 0xF6A2, 0x53D7, 0xE1F4,\n\t0x53DB, 0xDAE4, 0x53E1, 0xE7D5, 0x53E2, 0xF5BF, 0x53E3, 0xCFA2,\n\t0x53E4, 0xCDAF, 0x53E5, 0xCFA3, 0x53E9, 0xCDB0, 0x53EA, 0xF1FE,\n\t0x53EB, 0xD0A3, 0x53EC, 0xE1AF, 0x53ED, 0xF8A3, 0x53EF, 0xCAA6,\n\t0x53F0, 0xF7BB, 0x53F1, 0xF2EA, 0x53F2, 0xDEC8, 0x53F3, 0xE9D3,\n\t0x53F8, 0xDEC9, 0x5403, 0xFDDE, 0x5404, 0xCAC0, 0x5408, 0xF9EA,\n\t0x5409, 0xD1CE, 0x540A, 0xEED4, 0x540C, 0xD4D2, 0x540D, 0xD9A3,\n\t0x540E, 0xFDA8, 0x540F, 0xD7D9, 0x5410, 0xF7CE, 0x5411, 0xFABE,\n\t0x541B, 0xCFD6, 0x541D, 0xD7F0, 0x541F, 0xEBE1, 0x5420, 0xF8C5,\n\t0x5426, 0xDCFA, 0x5429, 0xDDC3, 0x542B, 0xF9DF, 0x5433, 0xE7EF,\n\t0x5438, 0xFDE5, 0x5439, 0xF6A3, 0x543B, 0xD9FC, 0x543C, 0xFDA9,\n\t0x543E, 0xE7EE, 0x5442, 0xD5E5, 0x5448, 0xEFD0, 0x544A, 0xCDB1,\n\t0x5451, 0xF7A2, 0x5468, 0xF1B2, 0x546A, 0xF1B1, 0x5471, 0xCDB2,\n\t0x5473, 0xDAAB, 0x5475, 0xCAA7, 0x547B, 0xE3E2, 0x547C, 0xFBBC,\n\t0x547D, 0xD9A4, 0x5480, 0xEEBA, 0x5486, 0xF8D3, 0x548C, 0xFBFA,\n\t0x548E, 0xCFA4, 0x5490, 0xDCFB, 0x54A4, 0xF6E3, 0x54A8, 0xEDAA,\n\t0x54AB, 0xF2A1, 0x54AC, 0xCEE1, 0x54B3, 0xFAA6, 0x54B8, 0xF9E0,\n\t0x54BD, 0xECD6, 0x54C0, 0xE4EE, 0x54C1, 0xF9A1, 0x54C4, 0xFBEF,\n\t0x54C8, 0xF9EB, 0x54C9, 0xEEA3, 0x54E1, 0xEAAC, 0x54E5, 0xCAA8,\n\t0x54E8, 0xF4FA, 0x54ED, 0xCDD6, 0x54EE, 0xFCF6, 0x54F2, 0xF4C9,\n\t0x54FA, 0xF8D4, 0x5504, 0xF8A6, 0x5506, 0xDECA, 0x5507, 0xF2C6,\n\t0x550E, 0xD7DA, 0x5510, 0xD3D0, 0x551C, 0xD8C5, 0x552F, 0xEAE6,\n\t0x5531, 0xF3DD, 0x5535, 0xE4DA, 0x553E, 0xF6E4, 0x5544, 0xF6F2,\n\t0x5546, 0xDFC2, 0x554F, 0xD9FD, 0x5553, 0xCCF6, 0x5556, 0xD3BA,\n\t0x555E, 0xE4AF, 0x5563, 0xF9E1, 0x557C, 0xF0A6, 0x5580, 0xCBD3,\n\t0x5584, 0xE0BC, 0x5586, 0xF4CA, 0x5587, 0xD4FA, 0x5589, 0xFDAA,\n\t0x558A, 0xF9E2, 0x5598, 0xF4B7, 0x5599, 0xFDC2, 0x559A, 0xFCB0,\n\t0x559C, 0xFDEC, 0x559D, 0xCAE2, 0x55A7, 0xFDBD, 0x55A9, 0xEAE7,\n\t0x55AA, 0xDFC3, 0x55AB, 0xD1D2, 0x55AC, 0xCEE2, 0x55AE, 0xD3A4,\n\t0x55C5, 0xFDAB, 0x55C7, 0xDFE0, 0x55D4, 0xF2C7, 0x55DA, 0xE7F0,\n\t0x55DC, 0xD0EE, 0x55DF, 0xF3AA, 0x55E3, 0xDECB, 0x55E4, 0xF6B8,\n\t0x55FD, 0xE1F5, 0x55FE, 0xF1B3, 0x5606, 0xF7A3, 0x5609, 0xCAA9,\n\t0x5614, 0xCFA5, 0x5617, 0xDFC4, 0x562F, 0xE1B0, 0x5632, 0xF0BF,\n\t0x5634, 0xF6A4, 0x5636, 0xE3B6, 0x5653, 0xFAC6, 0x5668, 0xD0EF,\n\t0x566B, 0xFDED, 0x5674, 0xDDC4, 0x5686, 0xFCF7, 0x56A5, 0xE6BF,\n\t0x56AC, 0xDEAD, 0x56AE, 0xFABF, 0x56B4, 0xE5F1, 0x56BC, 0xEDC4,\n\t0x56CA, 0xD2A5, 0x56CD, 0xFDEE, 0x56D1, 0xF5B6, 0x56DA, 0xE1F6,\n\t0x56DB, 0xDECC, 0x56DE, 0xFCDE, 0x56E0, 0xECD7, 0x56F0, 0xCDDD,\n\t0x56F9, 0xD6B7, 0x56FA, 0xCDB3, 0x5703, 0xF8D5, 0x5704, 0xE5D8,\n\t0x5708, 0xCFEA, 0x570B, 0xCFD0, 0x570D, 0xEACC, 0x5712, 0xEAAE,\n\t0x5713, 0xEAAD, 0x5716, 0xD3F1, 0x5718, 0xD3A5, 0x571F, 0xF7CF,\n\t0x5728, 0xEEA4, 0x572D, 0xD0A4, 0x5730, 0xF2A2, 0x573B, 0xD0F0,\n\t0x5740, 0xF2A3, 0x5742, 0xF7F8, 0x5747, 0xD0B3, 0x574A, 0xDBA9,\n\t0x574D, 0xD3BB, 0x574E, 0xCAEC, 0x5750, 0xF1A6, 0x5751, 0xCBD5,\n\t0x5761, 0xF7E7, 0x5764, 0xCDDE, 0x5766, 0xF7A4, 0x576A, 0xF8C0,\n\t0x576E, 0xD3DD, 0x5770, 0xCCD0, 0x5775, 0xCFA6, 0x577C, 0xF6F3,\n\t0x5782, 0xE1F7, 0x5788, 0xD3DC, 0x578B, 0xFAFE, 0x5793, 0xFAA7,\n\t0x57A0, 0xEBD9, 0x57A2, 0xCFA7, 0x57A3, 0xEAAF, 0x57C3, 0xE4EF,\n\t0x57C7, 0xE9B9, 0x57C8, 0xF1D8, 0x57CB, 0xD8D8, 0x57CE, 0xE0F2,\n\t0x57DF, 0xE6B4, 0x57E0, 0xDCFC, 0x57F0, 0xF3F1, 0x57F4, 0xE3D0,\n\t0x57F7, 0xF2FB, 0x57F9, 0xDBC6, 0x57FA, 0xD0F1, 0x57FC, 0xD0F2,\n\t0x5800, 0xCFDC, 0x5802, 0xD3D1, 0x5805, 0xCCB1, 0x5806, 0xF7D8,\n\t0x5808, 0xCBA8, 0x5809, 0xEBBC, 0x580A, 0xE4BE, 0x581E, 0xF4DC,\n\t0x5821, 0xDCC2, 0x5824, 0xF0A7, 0x5827, 0xE6C0, 0x582A, 0xCAED,\n\t0x582F, 0xE8EB, 0x5830, 0xE5E8, 0x5831, 0xDCC3, 0x5834, 0xEDDE,\n\t0x5835, 0xD3F2, 0x583A, 0xCCF7, 0x584A, 0xCED4, 0x584B, 0xE7AB,\n\t0x584F, 0xCBC3, 0x5851, 0xE1B1, 0x5854, 0xF7B2, 0x5857, 0xD3F3,\n\t0x5858, 0xD3D2, 0x585A, 0xF5C0, 0x585E, 0xDFDD, 0x5861, 0xEEF3,\n\t0x5862, 0xE7F1, 0x5864, 0xFDB4, 0x5875, 0xF2C8, 0x5879, 0xF3D2,\n\t0x587C, 0xEEF4, 0x587E, 0xE2D3, 0x5883, 0xCCD1, 0x5885, 0xDFEA,\n\t0x5889, 0xE9BA, 0x5893, 0xD9D7, 0x589C, 0xF5CD, 0x589E, 0xF1F2,\n\t0x589F, 0xFAC7, 0x58A8, 0xD9F8, 0x58A9, 0xD4C2, 0x58AE, 0xF6E5,\n\t0x58B3, 0xDDC5, 0x58BA, 0xE7F2, 0x58BB, 0xEDDF, 0x58BE, 0xCACB,\n\t0x58C1, 0xDBFA, 0x58C5, 0xE8B5, 0x58C7, 0xD3A6, 0x58CE, 0xFDB5,\n\t0x58D1, 0xF9C9, 0x58D3, 0xE4E2, 0x58D5, 0xFBBD, 0x58D8, 0xD7A4,\n\t0x58D9, 0xCEC5, 0x58DE, 0xCED5, 0x58DF, 0xD6E6, 0x58E4, 0xE5BD,\n\t0x58EB, 0xDECD, 0x58EC, 0xECF3, 0x58EF, 0xEDE0, 0x58F9, 0xECEC,\n\t0x58FA, 0xFBBE, 0x58FB, 0xDFEB, 0x58FD, 0xE1F8, 0x590F, 0xF9BE,\n\t0x5914, 0xD0F3, 0x5915, 0xE0AA, 0x5916, 0xE8E2, 0x5919, 0xE2D4,\n\t0x591A, 0xD2FD, 0x591C, 0xE5A8, 0x5922, 0xD9D3, 0x5927, 0xD3DE,\n\t0x5929, 0xF4B8, 0x592A, 0xF7BC, 0x592B, 0xDCFD, 0x592D, 0xE8EC,\n\t0x592E, 0xE4E7, 0x5931, 0xE3F7, 0x5937, 0xECA8, 0x593E, 0xFAF1,\n\t0x5944, 0xE5F2, 0x5947, 0xD0F4, 0x5948, 0xD2AF, 0x5949, 0xDCE5,\n\t0x594E, 0xD0A5, 0x594F, 0xF1B4, 0x5950, 0xFCB1, 0x5951, 0xCCF8,\n\t0x5954, 0xDDC6, 0x5955, 0xFAD1, 0x5957, 0xF7DF, 0x595A, 0xFAA8,\n\t0x5960, 0xEEF5, 0x5962, 0xDECE, 0x5967, 0xE7F3, 0x596A, 0xF7AC,\n\t0x596B, 0xEBC4, 0x596C, 0xEDE1, 0x596D, 0xE0AB, 0x596E, 0xDDC7,\n\t0x5973, 0xD2B3, 0x5974, 0xD2BF, 0x5978, 0xCACC, 0x597D, 0xFBBF,\n\t0x5982, 0xE5FD, 0x5983, 0xDDE5, 0x5984, 0xD8CD, 0x598A, 0xECF4,\n\t0x5993, 0xD0F5, 0x5996, 0xE8ED, 0x5997, 0xD0D2, 0x5999, 0xD9D8,\n\t0x59A5, 0xF6E6, 0x59A8, 0xDBAA, 0x59AC, 0xF7E0, 0x59B9, 0xD8D9,\n\t0x59BB, 0xF4A3, 0x59BE, 0xF4DD, 0x59C3, 0xEFD1, 0x59C6, 0xD9B5,\n\t0x59C9, 0xEDAB, 0x59CB, 0xE3B7, 0x59D0, 0xEEBB, 0x59D1, 0xCDB4,\n\t0x59D3, 0xE0F3, 0x59D4, 0xEACD, 0x59D9, 0xECF5, 0x59DA, 0xE8EE,\n\t0x59DC, 0xCBA9, 0x59DD, 0xF1AF, 0x59E6, 0xCACD, 0x59E8, 0xECA9,\n\t0x59EA, 0xF2EB, 0x59EC, 0xFDEF, 0x59EE, 0xF9F3, 0x59F8, 0xE6C1,\n\t0x59FB, 0xECD8, 0x59FF, 0xEDAC, 0x5A01, 0xEACE, 0x5A03, 0xE8DF,\n\t0x5A11, 0xDECF, 0x5A18, 0xD2A6, 0x5A1B, 0xE7F4, 0x5A1C, 0xD1D6,\n\t0x5A1F, 0xE6C2, 0x5A20, 0xE3E3, 0x5A25, 0xE4B0, 0x5A29, 0xD8B4,\n\t0x5A36, 0xF6A5, 0x5A3C, 0xF3DE, 0x5A41, 0xD7A5, 0x5A46, 0xF7E8,\n\t0x5A49, 0xE8C6, 0x5A5A, 0xFBE6, 0x5A62, 0xDDE6, 0x5A66, 0xDCFE,\n\t0x5A92, 0xD8DA, 0x5A9A, 0xDAAC, 0x5A9B, 0xEAB0, 0x5AA4, 0xE3B8,\n\t0x5AC1, 0xCAAA, 0x5AC2, 0xE1F9, 0x5AC4, 0xEAB1, 0x5AC9, 0xF2EC,\n\t0x5ACC, 0xFAEE, 0x5AE1, 0xEED5, 0x5AE6, 0xF9F4, 0x5AE9, 0xD2EC,\n\t0x5B05, 0xFBFB, 0x5B09, 0xFDF0, 0x5B0B, 0xE0BD, 0x5B0C, 0xCEE3,\n\t0x5B16, 0xF8C6, 0x5B2A, 0xDEAE, 0x5B40, 0xDFC5, 0x5B43, 0xE5BE,\n\t0x5B50, 0xEDAD, 0x5B51, 0xFAEA, 0x5B54, 0xCDEE, 0x5B55, 0xEDA6,\n\t0x5B57, 0xEDAE, 0x5B58, 0xF0ED, 0x5B5A, 0xDDA1, 0x5B5C, 0xEDAF,\n\t0x5B5D, 0xFCF8, 0x5B5F, 0xD8EB, 0x5B63, 0xCCF9, 0x5B64, 0xCDB5,\n\t0x5B69, 0xFAA9, 0x5B6B, 0xE1DD, 0x5B70, 0xE2D5, 0x5B71, 0xEDCF,\n\t0x5B75, 0xDDA2, 0x5B78, 0xF9CA, 0x5B7A, 0xEAE8, 0x5B7C, 0xE5ED,\n\t0x5B85, 0xD3EB, 0x5B87, 0xE9D4, 0x5B88, 0xE1FA, 0x5B89, 0xE4CC,\n\t0x5B8B, 0xE1E4, 0x5B8C, 0xE8C7, 0x5B8F, 0xCEDB, 0x5B93, 0xDCD5,\n\t0x5B95, 0xF7B5, 0x5B96, 0xFCF3, 0x5B97, 0xF0F3, 0x5B98, 0xCEAF,\n\t0x5B99, 0xF1B5, 0x5B9A, 0xEFD2, 0x5B9B, 0xE8C8, 0x5B9C, 0xEBF1,\n\t0x5BA2, 0xCBD4, 0x5BA3, 0xE0BE, 0x5BA4, 0xE3F8, 0x5BA5, 0xEAE9,\n\t0x5BA6, 0xFCB2, 0x5BAC, 0xE0F4, 0x5BAE, 0xCFE0, 0x5BB0, 0xEEA5,\n\t0x5BB3, 0xFAAA, 0x5BB4, 0xE6C3, 0x5BB5, 0xE1B2, 0x5BB6, 0xCAAB,\n\t0x5BB8, 0xE3E4, 0x5BB9, 0xE9BB, 0x5BBF, 0xE2D6, 0x5BC0, 0xF3F2,\n\t0x5BC2, 0xEED6, 0x5BC3, 0xEAB2, 0x5BC4, 0xD0F6, 0x5BC5, 0xECD9,\n\t0x5BC6, 0xDACB, 0x5BC7, 0xCFA8, 0x5BCC, 0xDDA3, 0x5BD0, 0xD8DB,\n\t0x5BD2, 0xF9CE, 0x5BD3, 0xE9D5, 0x5BD4, 0xE3D1, 0x5BD7, 0xD2BC,\n\t0x5BDE, 0xD8AC, 0x5BDF, 0xF3CC, 0x5BE1, 0xCDFB, 0x5BE2, 0xF6D6,\n\t0x5BE4, 0xE7F5, 0x5BE5, 0xE8EF, 0x5BE6, 0xE3F9, 0x5BE7, 0xD2BB,\n\t0x5BE8, 0xF3F3, 0x5BE9, 0xE3FB, 0x5BEB, 0xDED0, 0x5BEC, 0xCEB0,\n\t0x5BEE, 0xD6F7, 0x5BEF, 0xF1D9, 0x5BF5, 0xF5C1, 0x5BF6, 0xDCC4,\n\t0x5BF8, 0xF5BB, 0x5BFA, 0xDED1, 0x5C01, 0xDCE6, 0x5C04, 0xDED2,\n\t0x5C07, 0xEDE2, 0x5C08, 0xEEF6, 0x5C09, 0xEACF, 0x5C0A, 0xF0EE,\n\t0x5C0B, 0xE3FC, 0x5C0D, 0xD3DF, 0x5C0E, 0xD3F4, 0x5C0F, 0xE1B3,\n\t0x5C11, 0xE1B4, 0x5C16, 0xF4D3, 0x5C19, 0xDFC6, 0x5C24, 0xE9D6,\n\t0x5C28, 0xDBAB, 0x5C31, 0xF6A6, 0x5C38, 0xE3B9, 0x5C39, 0xEBC5,\n\t0x5C3A, 0xF4A9, 0x5C3B, 0xCDB6, 0x5C3C, 0xD2F9, 0x5C3E, 0xDAAD,\n\t0x5C3F, 0xD2E3, 0x5C40, 0xCFD1, 0x5C45, 0xCBDC, 0x5C46, 0xCCFA,\n\t0x5C48, 0xCFDD, 0x5C4B, 0xE8A9, 0x5C4D, 0xE3BB, 0x5C4E, 0xE3BA,\n\t0x5C51, 0xE0DA, 0x5C55, 0xEEF7, 0x5C5B, 0xDCB3, 0x5C60, 0xD3F5,\n\t0x5C62, 0xD7A6, 0x5C64, 0xF6B5, 0x5C65, 0xD7DB, 0x5C6C, 0xE1D5,\n\t0x5C6F, 0xD4EA, 0x5C71, 0xDFA3, 0x5C79, 0xFDDF, 0x5C90, 0xD0F7,\n\t0x5C91, 0xEDD4, 0x5CA1, 0xCBAA, 0x5CA9, 0xE4DB, 0x5CAB, 0xE1FB,\n\t0x5CAC, 0xCBA2, 0x5CB1, 0xD3E0, 0x5CB3, 0xE4BF, 0x5CB5, 0xFBC0,\n\t0x5CB7, 0xDABE, 0x5CB8, 0xE4CD, 0x5CBA, 0xD6B9, 0x5CBE, 0xEFC0,\n\t0x5CC0, 0xE1FC, 0x5CD9, 0xF6B9, 0x5CE0, 0xDFC7, 0x5CE8, 0xE4B1,\n\t0x5CEF, 0xDCE7, 0x5CF0, 0xDCE8, 0x5CF4, 0xFAD6, 0x5CF6, 0xD3F6,\n\t0x5CFB, 0xF1DA, 0x5CFD, 0xFAF2, 0x5D07, 0xE2FD, 0x5D0D, 0xD5CF,\n\t0x5D0E, 0xD0F8, 0x5D11, 0xCDDF, 0x5D14, 0xF5CB, 0x5D16, 0xE4F0,\n\t0x5D17, 0xCBAB, 0x5D19, 0xD7C4, 0x5D27, 0xE2FE, 0x5D29, 0xDDDA,\n\t0x5D4B, 0xDAAE, 0x5D4C, 0xCAEE, 0x5D50, 0xD5B9, 0x5D69, 0xE3A1,\n\t0x5D6C, 0xE8E3, 0x5D6F, 0xF3AB, 0x5D87, 0xCFA9, 0x5D8B, 0xD3F7,\n\t0x5D9D, 0xD4F1, 0x5DA0, 0xCEE4, 0x5DA2, 0xE8F2, 0x5DAA, 0xE5F5,\n\t0x5DB8, 0xE7AE, 0x5DBA, 0xD6BA, 0x5DBC, 0xDFEC, 0x5DBD, 0xE4C0,\n\t0x5DCD, 0xE8E4, 0x5DD2, 0xD8B5, 0x5DD6, 0xE4DC, 0x5DDD, 0xF4B9,\n\t0x5DDE, 0xF1B6, 0x5DE1, 0xE2DE, 0x5DE2, 0xE1B5, 0x5DE5, 0xCDEF,\n\t0x5DE6, 0xF1A7, 0x5DE7, 0xCEE5, 0x5DE8, 0xCBDD, 0x5DEB, 0xD9E3,\n\t0x5DEE, 0xF3AC, 0x5DF1, 0xD0F9, 0x5DF2, 0xECAB, 0x5DF3, 0xDED3,\n\t0x5DF4, 0xF7E9, 0x5DF7, 0xF9F5, 0x5DFD, 0xE1DE, 0x5DFE, 0xCBEE,\n\t0x5E02, 0xE3BC, 0x5E03, 0xF8D6, 0x5E06, 0xDBEE, 0x5E0C, 0xFDF1,\n\t0x5E11, 0xF7B6, 0x5E16, 0xF4DE, 0x5E19, 0xF2ED, 0x5E1B, 0xDBD9,\n\t0x5E1D, 0xF0A8, 0x5E25, 0xE1FD, 0x5E2B, 0xDED4, 0x5E2D, 0xE0AC,\n\t0x5E33, 0xEDE3, 0x5E36, 0xD3E1, 0x5E38, 0xDFC8, 0x5E3D, 0xD9B6,\n\t0x5E3F, 0xFDAC, 0x5E40, 0xEFD3, 0x5E44, 0xE4C1, 0x5E45, 0xF8EB,\n\t0x5E47, 0xDBAC, 0x5E4C, 0xFCC6, 0x5E55, 0xD8AD, 0x5E5F, 0xF6BA,\n\t0x5E61, 0xDBDF, 0x5E62, 0xD3D3, 0x5E63, 0xF8C7, 0x5E72, 0xCACE,\n\t0x5E73, 0xF8C1, 0x5E74, 0xD2B4, 0x5E77, 0xDCB4, 0x5E78, 0xFAB9,\n\t0x5E79, 0xCACF, 0x5E7B, 0xFCB3, 0x5E7C, 0xEAEA, 0x5E7D, 0xEAEB,\n\t0x5E7E, 0xD0FA, 0x5E84, 0xEDE4, 0x5E87, 0xDDE7, 0x5E8A, 0xDFC9,\n\t0x5E8F, 0xDFED, 0x5E95, 0xEEBC, 0x5E97, 0xEFC1, 0x5E9A, 0xCCD2,\n\t0x5E9C, 0xDDA4, 0x5EA0, 0xDFCA, 0x5EA6, 0xD3F8, 0x5EA7, 0xF1A8,\n\t0x5EAB, 0xCDB7, 0x5EAD, 0xEFD4, 0x5EB5, 0xE4DD, 0x5EB6, 0xDFEE,\n\t0x5EB7, 0xCBAC, 0x5EB8, 0xE9BC, 0x5EBE, 0xEAEC, 0x5EC2, 0xDFCB,\n\t0x5EC8, 0xF9BF, 0x5EC9, 0xD6AF, 0x5ECA, 0xD5C6, 0x5ED0, 0xCFAA,\n\t0x5ED3, 0xCEA9, 0x5ED6, 0xD6F8, 0x5EDA, 0xF1B7, 0x5EDB, 0xEEF8,\n\t0x5EDF, 0xD9D9, 0x5EE0, 0xF3DF, 0x5EE2, 0xF8C8, 0x5EE3, 0xCEC6,\n\t0x5EEC, 0xD5E6, 0x5EF3, 0xF4E6, 0x5EF6, 0xE6C5, 0x5EF7, 0xEFD5,\n\t0x5EFA, 0xCBEF, 0x5EFB, 0xFCDF, 0x5F01, 0xDCA7, 0x5F04, 0xD6E7,\n\t0x5F0A, 0xF8C9, 0x5F0F, 0xE3D2, 0x5F11, 0xE3BD, 0x5F13, 0xCFE1,\n\t0x5F14, 0xF0C0, 0x5F15, 0xECDA, 0x5F17, 0xDDD7, 0x5F18, 0xFBF0,\n\t0x5F1B, 0xECAC, 0x5F1F, 0xF0A9, 0x5F26, 0xFAD7, 0x5F27, 0xFBC1,\n\t0x5F29, 0xD2C0, 0x5F31, 0xE5B0, 0x5F35, 0xEDE5, 0x5F3A, 0xCBAD,\n\t0x5F3C, 0xF9B0, 0x5F48, 0xF7A5, 0x5F4A, 0xCBAE, 0x5F4C, 0xDAAF,\n\t0x5F4E, 0xD8B6, 0x5F56, 0xD3A7, 0x5F57, 0xFBB2, 0x5F59, 0xFDC4,\n\t0x5F5B, 0xECAD, 0x5F62, 0xFBA1, 0x5F66, 0xE5E9, 0x5F67, 0xE9EE,\n\t0x5F69, 0xF3F4, 0x5F6A, 0xF8F3, 0x5F6B, 0xF0C1, 0x5F6C, 0xDEAF,\n\t0x5F6D, 0xF8B0, 0x5F70, 0xF3E0, 0x5F71, 0xE7AF, 0x5F77, 0xDBAD,\n\t0x5F79, 0xE6B5, 0x5F7C, 0xF9A8, 0x5F7F, 0xDDD8, 0x5F80, 0xE8D9,\n\t0x5F81, 0xEFD6, 0x5F85, 0xD3E2, 0x5F87, 0xE2DF, 0x5F8A, 0xFCE0,\n\t0x5F8B, 0xD7C8, 0x5F8C, 0xFDAD, 0x5F90, 0xDFEF, 0x5F91, 0xCCD3,\n\t0x5F92, 0xD3F9, 0x5F97, 0xD4F0, 0x5F98, 0xDBC7, 0x5F99, 0xDED5,\n\t0x5F9E, 0xF0F4, 0x5FA0, 0xD5D0, 0x5FA1, 0xE5D9, 0x5FA8, 0xFCC7,\n\t0x5FA9, 0xDCD6, 0x5FAA, 0xE2E0, 0x5FAE, 0xDAB0, 0x5FB5, 0xF3A3,\n\t0x5FB7, 0xD3EC, 0x5FB9, 0xF4CB, 0x5FBD, 0xFDC5, 0x5FC3, 0xE3FD,\n\t0x5FC5, 0xF9B1, 0x5FCC, 0xD0FB, 0x5FCD, 0xECDB, 0x5FD6, 0xF5BC,\n\t0x5FD7, 0xF2A4, 0x5FD8, 0xD8CE, 0x5FD9, 0xD8CF, 0x5FE0, 0xF5F7,\n\t0x5FEB, 0xF6E1, 0x5FF5, 0xD2B7, 0x5FFD, 0xFBEC, 0x5FFF, 0xDDC8,\n\t0x600F, 0xE4E8, 0x6012, 0xD2C1, 0x6016, 0xF8D7, 0x601C, 0xD6BB,\n\t0x601D, 0xDED6, 0x6020, 0xF7BD, 0x6021, 0xECAE, 0x6025, 0xD0E1,\n\t0x6027, 0xE0F5, 0x6028, 0xEAB3, 0x602A, 0xCED6, 0x602F, 0xCCA5,\n\t0x6041, 0xECF6, 0x6042, 0xE2E1, 0x6043, 0xE3BE, 0x604D, 0xFCC8,\n\t0x6050, 0xCDF0, 0x6052, 0xF9F6, 0x6055, 0xDFF0, 0x6059, 0xE5BF,\n\t0x605D, 0xCEBF, 0x6062, 0xFCE1, 0x6063, 0xEDB0, 0x6064, 0xFDD1,\n\t0x6065, 0xF6BB, 0x6068, 0xF9CF, 0x6069, 0xEBDA, 0x606A, 0xCAC1,\n\t0x606C, 0xD2B8, 0x606D, 0xCDF1, 0x606F, 0xE3D3, 0x6070, 0xFDE6,\n\t0x6085, 0xE6ED, 0x6089, 0xE3FA, 0x608C, 0xF0AA, 0x608D, 0xF9D0,\n\t0x6094, 0xFCE2, 0x6096, 0xF8A7, 0x609A, 0xE1E5, 0x609B, 0xEEF9,\n\t0x609F, 0xE7F6, 0x60A0, 0xEAED, 0x60A3, 0xFCB4, 0x60A4, 0xF5C2,\n\t0x60A7, 0xD7DC, 0x60B0, 0xF0F5, 0x60B2, 0xDDE8, 0x60B3, 0xD3ED,\n\t0x60B4, 0xF5FC, 0x60B6, 0xDABF, 0x60B8, 0xCCFB, 0x60BC, 0xD3FA,\n\t0x60BD, 0xF4A4, 0x60C5, 0xEFD7, 0x60C7, 0xD4C3, 0x60D1, 0xFBE3,\n\t0x60DA, 0xFBED, 0x60DC, 0xE0AD, 0x60DF, 0xEAEE, 0x60E0, 0xFBB3,\n\t0x60E1, 0xE4C2, 0x60F0, 0xF6E7, 0x60F1, 0xD2DD, 0x60F3, 0xDFCC,\n\t0x60F6, 0xFCC9, 0x60F9, 0xE5A9, 0x60FA, 0xE0F6, 0x60FB, 0xF6B3,\n\t0x6101, 0xE1FE, 0x6106, 0xCBF0, 0x6108, 0xEAEF, 0x6109, 0xEAF0,\n\t0x610D, 0xDAC0, 0x610E, 0xF8B4, 0x610F, 0xEBF2, 0x6115, 0xE4C3,\n\t0x611A, 0xE9D7, 0x611B, 0xE4F1, 0x611F, 0xCAEF, 0x6127, 0xCED7,\n\t0x6130, 0xFCCA, 0x6134, 0xF3E1, 0x6137, 0xCBC4, 0x613C, 0xE3E5,\n\t0x613E, 0xCBC5, 0x613F, 0xEAB4, 0x6142, 0xE9BD, 0x6144, 0xD7C9,\n\t0x6147, 0xEBDB, 0x6148, 0xEDB1, 0x614A, 0xCCC3, 0x614B, 0xF7BE,\n\t0x614C, 0xFCCB, 0x6153, 0xF8F4, 0x6155, 0xD9B7, 0x6158, 0xF3D3,\n\t0x6159, 0xF3D4, 0x615D, 0xF7E4, 0x615F, 0xF7D1, 0x6162, 0xD8B7,\n\t0x6163, 0xCEB1, 0x6164, 0xCAC2, 0x6167, 0xFBB4, 0x6168, 0xCBC6,\n\t0x616B, 0xF0F6, 0x616E, 0xD5E7, 0x6170, 0xEAD0, 0x6176, 0xCCD4,\n\t0x6177, 0xCBAF, 0x617D, 0xF4AA, 0x617E, 0xE9AF, 0x6181, 0xF5C3,\n\t0x6182, 0xE9D8, 0x618A, 0xDDE9, 0x618E, 0xF1F3, 0x6190, 0xD5FB,\n\t0x6191, 0xDEBB, 0x6194, 0xF4FB, 0x6198, 0xFDF3, 0x6199, 0xFDF2,\n\t0x619A, 0xF7A6, 0x61A4, 0xDDC9, 0x61A7, 0xD4D3, 0x61A9, 0xCCA8,\n\t0x61AB, 0xDAC1, 0x61AC, 0xCCD5, 0x61AE, 0xD9E4, 0x61B2, 0xFACA,\n\t0x61B6, 0xE5E3, 0x61BA, 0xD3BC, 0x61BE, 0xCAF0, 0x61C3, 0xD0C4,\n\t0x61C7, 0xCAD0, 0x61C8, 0xFAAB, 0x61C9, 0xEBEB, 0x61CA, 0xE7F8,\n\t0x61CB, 0xD9E5, 0x61E6, 0xD1D7, 0x61F2, 0xF3A4, 0x61F6, 0xD4FB,\n\t0x61F7, 0xFCE3, 0x61F8, 0xFAD8, 0x61FA, 0xF3D5, 0x61FC, 0xCFAB,\n\t0x61FF, 0xEBF3, 0x6200, 0xD5FC, 0x6207, 0xD3D4, 0x6208, 0xCDFC,\n\t0x620A, 0xD9E6, 0x620C, 0xE2F9, 0x620D, 0xE2A1, 0x620E, 0xEBD4,\n\t0x6210, 0xE0F7, 0x6211, 0xE4B2, 0x6212, 0xCCFC, 0x6216, 0xFBE4,\n\t0x621A, 0xF4AB, 0x621F, 0xD0BD, 0x6221, 0xCAF1, 0x622A, 0xEFB8,\n\t0x622E, 0xD7C0, 0x6230, 0xEEFA, 0x6231, 0xFDF4, 0x6234, 0xD3E3,\n\t0x6236, 0xFBC2, 0x623E, 0xD5E8, 0x623F, 0xDBAE, 0x6240, 0xE1B6,\n\t0x6241, 0xF8B7, 0x6247, 0xE0BF, 0x6248, 0xFBC3, 0x6249, 0xDDEA,\n\t0x624B, 0xE2A2, 0x624D, 0xEEA6, 0x6253, 0xF6E8, 0x6258, 0xF6F5,\n\t0x626E, 0xDDCA, 0x6271, 0xD0E2, 0x6276, 0xDDA6, 0x6279, 0xDDEB,\n\t0x627C, 0xE4F9, 0x627F, 0xE3AF, 0x6280, 0xD0FC, 0x6284, 0xF4FC,\n\t0x6289, 0xCCBC, 0x628A, 0xF7EA, 0x6291, 0xE5E4, 0x6292, 0xDFF1,\n\t0x6295, 0xF7E1, 0x6297, 0xF9F7, 0x6298, 0xEFB9, 0x629B, 0xF8D8,\n\t0x62AB, 0xF9A9, 0x62B1, 0xF8D9, 0x62B5, 0xEEBD, 0x62B9, 0xD8C6,\n\t0x62BC, 0xE4E3, 0x62BD, 0xF5CE, 0x62C2, 0xDDD9, 0x62C7, 0xD9E7,\n\t0x62C8, 0xD2B9, 0x62C9, 0xD5C3, 0x62CC, 0xDAE5, 0x62CD, 0xDAD0,\n\t0x62CF, 0xD1D9, 0x62D0, 0xCED8, 0x62D2, 0xCBDE, 0x62D3, 0xF4AC,\n\t0x62D4, 0xDAFB, 0x62D6, 0xF6E9, 0x62D7, 0xE8F3, 0x62D8, 0xCFAC,\n\t0x62D9, 0xF0F0, 0x62DB, 0xF4FD, 0x62DC, 0xDBC8, 0x62EC, 0xCEC0,\n\t0x62ED, 0xE3D4, 0x62EE, 0xD1CF, 0x62EF, 0xF1F5, 0x62F1, 0xCDF2,\n\t0x62F3, 0xCFEB, 0x62F7, 0xCDB8, 0x62FE, 0xE3A6, 0x62FF, 0xD1DA,\n\t0x6301, 0xF2A5, 0x6307, 0xF2A6, 0x6309, 0xE4CE, 0x6311, 0xD3FB,\n\t0x632B, 0xF1A9, 0x632F, 0xF2C9, 0x633A, 0xEFD8, 0x633B, 0xE6C9,\n\t0x633D, 0xD8B8, 0x633E, 0xFAF3, 0x6349, 0xF3B5, 0x634C, 0xF8A4,\n\t0x634F, 0xD1F3, 0x6350, 0xE6C8, 0x6355, 0xF8DA, 0x6367, 0xDCE9,\n\t0x6368, 0xDED7, 0x636E, 0xCBDF, 0x6372, 0xCFEC, 0x6377, 0xF4DF,\n\t0x637A, 0xD1F4, 0x637B, 0xD2BA, 0x637F, 0xDFF2, 0x6383, 0xE1B7,\n\t0x6388, 0xE2A3, 0x6389, 0xD3FC, 0x638C, 0xEDE6, 0x6392, 0xDBC9,\n\t0x6396, 0xE4FA, 0x6398, 0xCFDE, 0x639B, 0xCED0, 0x63A0, 0xD5D3,\n\t0x63A1, 0xF3F5, 0x63A2, 0xF7AE, 0x63A5, 0xEFC8, 0x63A7, 0xCDF3,\n\t0x63A8, 0xF5CF, 0x63A9, 0xE5F3, 0x63AA, 0xF0C2, 0x63C0, 0xCAD1,\n\t0x63C4, 0xEAF1, 0x63C6, 0xD0A6, 0x63CF, 0xD9DA, 0x63D0, 0xF0AB,\n\t0x63D6, 0xEBE7, 0x63DA, 0xE5C0, 0x63DB, 0xFCB5, 0x63E1, 0xE4C4,\n\t0x63ED, 0xCCA9, 0x63EE, 0xFDC6, 0x63F4, 0xEAB5, 0x63F6, 0xE5AA,\n\t0x63F7, 0xDFBA, 0x640D, 0xE1DF, 0x640F, 0xDAD1, 0x6414, 0xE1B8,\n\t0x6416, 0xE8F4, 0x6417, 0xD3FD, 0x641C, 0xE2A4, 0x6422, 0xF2CA,\n\t0x642C, 0xDAE6, 0x642D, 0xF7B3, 0x643A, 0xFDCD, 0x643E, 0xF3B6,\n\t0x6458, 0xEED7, 0x6460, 0xF5C4, 0x6469, 0xD8A4, 0x646F, 0xF2A7,\n\t0x6478, 0xD9B8, 0x6479, 0xD9B9, 0x647A, 0xEFC9, 0x6488, 0xD6CE,\n\t0x6491, 0xF7CB, 0x6492, 0xDFAE, 0x6493, 0xE8F5, 0x649A, 0xD2B5,\n\t0x649E, 0xD3D5, 0x64A4, 0xF4CC, 0x64A5, 0xDAFC, 0x64AB, 0xD9E8,\n\t0x64AD, 0xF7EB, 0x64AE, 0xF5C9, 0x64B0, 0xF3BC, 0x64B2, 0xDAD2,\n\t0x64BB, 0xD3B5, 0x64C1, 0xE8B6, 0x64C4, 0xD6CF, 0x64C5, 0xF4BA,\n\t0x64C7, 0xF7C9, 0x64CA, 0xCCAA, 0x64CD, 0xF0C3, 0x64CE, 0xCCD6,\n\t0x64D2, 0xD0D3, 0x64D4, 0xD3BD, 0x64D8, 0xDBFB, 0x64DA, 0xCBE0,\n\t0x64E1, 0xD3E4, 0x64E2, 0xF6F7, 0x64E5, 0xD5BA, 0x64E6, 0xF3CD,\n\t0x64E7, 0xCBE1, 0x64EC, 0xEBF4, 0x64F2, 0xF4AD, 0x64F4, 0xFCAA,\n\t0x64FA, 0xF7EC, 0x64FE, 0xE8F6, 0x6500, 0xDAE7, 0x6504, 0xF7CC,\n\t0x6518, 0xE5C1, 0x651D, 0xE0EE, 0x6523, 0xD5FD, 0x652A, 0xCEE6,\n\t0x652B, 0xFCAB, 0x652C, 0xD5BB, 0x652F, 0xF2A8, 0x6536, 0xE2A5,\n\t0x6537, 0xCDB9, 0x6538, 0xEAF2, 0x6539, 0xCBC7, 0x653B, 0xCDF4,\n\t0x653E, 0xDBAF, 0x653F, 0xEFD9, 0x6545, 0xCDBA, 0x6548, 0xFCF9,\n\t0x654D, 0xDFF3, 0x654E, 0xCEE7, 0x654F, 0xDAC2, 0x6551, 0xCFAD,\n\t0x6556, 0xE7F9, 0x6557, 0xF8A8, 0x655E, 0xF3E2, 0x6562, 0xCAF2,\n\t0x6563, 0xDFA4, 0x6566, 0xD4C4, 0x656C, 0xCCD7, 0x656D, 0xE5C2,\n\t0x6572, 0xCDBB, 0x6574, 0xEFDA, 0x6575, 0xEED8, 0x6577, 0xDDA7,\n\t0x6578, 0xE2A6, 0x657E, 0xE0C0, 0x6582, 0xD6B0, 0x6583, 0xF8CA,\n\t0x6585, 0xFCFA, 0x6587, 0xD9FE, 0x658C, 0xDEB0, 0x6590, 0xDDEC,\n\t0x6591, 0xDAE8, 0x6597, 0xD4E0, 0x6599, 0xD6F9, 0x659B, 0xCDD7,\n\t0x659C, 0xDED8, 0x659F, 0xF2F8, 0x65A1, 0xE4D6, 0x65A4, 0xD0C5,\n\t0x65A5, 0xF4AE, 0x65A7, 0xDDA8, 0x65AB, 0xEDC5, 0x65AC, 0xF3D6,\n\t0x65AF, 0xDED9, 0x65B0, 0xE3E6, 0x65B7, 0xD3A8, 0x65B9, 0xDBB0,\n\t0x65BC, 0xE5DA, 0x65BD, 0xE3BF, 0x65C1, 0xDBB1, 0x65C5, 0xD5E9,\n\t0x65CB, 0xE0C1, 0x65CC, 0xEFDB, 0x65CF, 0xF0E9, 0x65D2, 0xD7B2,\n\t0x65D7, 0xD0FD, 0x65E0, 0xD9E9, 0x65E3, 0xD0FE, 0x65E5, 0xECED,\n\t0x65E6, 0xD3A9, 0x65E8, 0xF2A9, 0x65E9, 0xF0C4, 0x65EC, 0xE2E2,\n\t0x65ED, 0xE9EF, 0x65F1, 0xF9D1, 0x65F4, 0xE9D9, 0x65FA, 0xE8DA,\n\t0x65FB, 0xDAC3, 0x65FC, 0xDAC4, 0x65FD, 0xD4C5, 0x65FF, 0xE7FA,\n\t0x6606, 0xCDE0, 0x6607, 0xE3B0, 0x6609, 0xDBB2, 0x660A, 0xFBC4,\n\t0x660C, 0xF3E3, 0x660E, 0xD9A5, 0x660F, 0xFBE7, 0x6610, 0xDDCB,\n\t0x6611, 0xD0D4, 0x6613, 0xE6B6, 0x6614, 0xE0AE, 0x6615, 0xFDDA,\n\t0x661E, 0xDCB5, 0x661F, 0xE0F8, 0x6620, 0xE7B1, 0x6625, 0xF5F0,\n\t0x6627, 0xD8DC, 0x6628, 0xEDC6, 0x662D, 0xE1B9, 0x662F, 0xE3C0,\n\t0x6630, 0xF9C0, 0x6631, 0xE9F0, 0x6634, 0xD9DB, 0x6636, 0xF3E4,\n\t0x663A, 0xDCB6, 0x663B, 0xE4E9, 0x6641, 0xF0C5, 0x6642, 0xE3C1,\n\t0x6643, 0xFCCC, 0x6644, 0xFCCD, 0x6649, 0xF2CB, 0x664B, 0xF2CC,\n\t0x664F, 0xE4CF, 0x6659, 0xF1DB, 0x665B, 0xFAD9, 0x665D, 0xF1B8,\n\t0x665E, 0xFDF5, 0x665F, 0xE0F9, 0x6664, 0xE7FB, 0x6665, 0xFCB7,\n\t0x6666, 0xFCE4, 0x6667, 0xFBC5, 0x6668, 0xE3E7, 0x6669, 0xD8B9,\n\t0x666B, 0xF6F8, 0x666E, 0xDCC5, 0x666F, 0xCCD8, 0x6673, 0xE0AF,\n\t0x6674, 0xF4E7, 0x6676, 0xEFDC, 0x6677, 0xCFFC, 0x6678, 0xEFDD,\n\t0x667A, 0xF2AA, 0x6684, 0xFDBE, 0x6687, 0xCAAC, 0x6688, 0xFDBB,\n\t0x6689, 0xFDC7, 0x668E, 0xE7B2, 0x6690, 0xEAD1, 0x6691, 0xDFF4,\n\t0x6696, 0xD1EC, 0x6697, 0xE4DE, 0x6698, 0xE5C3, 0x669D, 0xD9A6,\n\t0x66A0, 0xCDBC, 0x66A2, 0xF3E5, 0x66AB, 0xEDD5, 0x66AE, 0xD9BA,\n\t0x66B2, 0xEDE7, 0x66B3, 0xFBB5, 0x66B4, 0xF8EC, 0x66B9, 0xE0E7,\n\t0x66BB, 0xCCD9, 0x66BE, 0xD4C6, 0x66C4, 0xE7A5, 0x66C6, 0xD5F5,\n\t0x66C7, 0xD3BE, 0x66C9, 0xFCFB, 0x66D6, 0xE4F2, 0x66D9, 0xDFF5,\n\t0x66DC, 0xE8F8, 0x66DD, 0xF8ED, 0x66E0, 0xCEC7, 0x66E6, 0xFDF6,\n\t0x66F0, 0xE8D8, 0x66F2, 0xCDD8, 0x66F3, 0xE7D6, 0x66F4, 0xCCDA,\n\t0x66F7, 0xCAE3, 0x66F8, 0xDFF6, 0x66F9, 0xF0C7, 0x66FA, 0xF0C6,\n\t0x66FC, 0xD8BA, 0x66FE, 0xF1F4, 0x66FF, 0xF4F0, 0x6700, 0xF5CC,\n\t0x6703, 0xFCE5, 0x6708, 0xEAC5, 0x6709, 0xEAF3, 0x670B, 0xDDDB,\n\t0x670D, 0xDCD7, 0x6714, 0xDEFD, 0x6715, 0xF2F9, 0x6717, 0xD5C7,\n\t0x671B, 0xD8D0, 0x671D, 0xF0C8, 0x671E, 0xD1A1, 0x671F, 0xD1A2,\n\t0x6726, 0xD9D4, 0x6727, 0xD6E8, 0x6728, 0xD9CA, 0x672A, 0xDAB1,\n\t0x672B, 0xD8C7, 0x672C, 0xDCE2, 0x672D, 0xF3CE, 0x672E, 0xF5F4,\n\t0x6731, 0xF1B9, 0x6734, 0xDAD3, 0x6736, 0xF6EA, 0x673A, 0xCFF5,\n\t0x673D, 0xFDAE, 0x6746, 0xCAD2, 0x6749, 0xDFB4, 0x674E, 0xD7DD,\n\t0x674F, 0xFABA, 0x6750, 0xEEA7, 0x6751, 0xF5BD, 0x6753, 0xF8F5,\n\t0x6756, 0xEDE8, 0x675C, 0xD4E1, 0x675E, 0xD1A3, 0x675F, 0xE1D6,\n\t0x676D, 0xF9F8, 0x676F, 0xDBCA, 0x6770, 0xCBF9, 0x6771, 0xD4D4,\n\t0x6773, 0xD9DC, 0x6775, 0xEEBE, 0x6777, 0xF7ED, 0x677B, 0xD2EE,\n\t0x677E, 0xE1E6, 0x677F, 0xF7F9, 0x6787, 0xDDED, 0x6789, 0xE8DB,\n\t0x678B, 0xDBB3, 0x678F, 0xD1F7, 0x6790, 0xE0B0, 0x6793, 0xD4E2,\n\t0x6795, 0xF6D7, 0x6797, 0xD7F9, 0x679A, 0xD8DD, 0x679C, 0xCDFD,\n\t0x679D, 0xF2AB, 0x67AF, 0xCDBD, 0x67B0, 0xF8C2, 0x67B3, 0xF2AC,\n\t0x67B6, 0xCAAD, 0x67B7, 0xCAAE, 0x67B8, 0xCFAE, 0x67BE, 0xE3C2,\n\t0x67C4, 0xDCB7, 0x67CF, 0xDBDA, 0x67D0, 0xD9BB, 0x67D1, 0xCAF3,\n\t0x67D2, 0xF6D3, 0x67D3, 0xE6F8, 0x67D4, 0xEAF5, 0x67DA, 0xEAF6,\n\t0x67DD, 0xF6F9, 0x67E9, 0xCFAF, 0x67EC, 0xCAD3, 0x67EF, 0xCAAF,\n\t0x67F0, 0xD2B0, 0x67F1, 0xF1BA, 0x67F3, 0xD7B3, 0x67F4, 0xE3C3,\n\t0x67F5, 0xF3FD, 0x67F6, 0xDEDA, 0x67FB, 0xDEDB, 0x67FE, 0xEFDE,\n\t0x6812, 0xE2E3, 0x6813, 0xEEFB, 0x6816, 0xDFF7, 0x6817, 0xD7CA,\n\t0x6821, 0xCEE8, 0x6822, 0xDBDB, 0x682A, 0xF1BB, 0x682F, 0xE9F1,\n\t0x6838, 0xFAB7, 0x6839, 0xD0C6, 0x683C, 0xCCAB, 0x683D, 0xEEA8,\n\t0x6840, 0xCBFA, 0x6841, 0xF9F9, 0x6842, 0xCCFD, 0x6843, 0xD3FE,\n\t0x6848, 0xE4D0, 0x684E, 0xF2EE, 0x6850, 0xD4D5, 0x6851, 0xDFCD,\n\t0x6853, 0xFCB8, 0x6854, 0xD1D0, 0x686D, 0xF2CD, 0x6876, 0xF7D2,\n\t0x687F, 0xCAD4, 0x6881, 0xD5D9, 0x6885, 0xD8DE, 0x688F, 0xCDD9,\n\t0x6893, 0xEEA9, 0x6894, 0xF6BC, 0x6897, 0xCCDB, 0x689D, 0xF0C9,\n\t0x689F, 0xFCFC, 0x68A1, 0xE8C9, 0x68A2, 0xF4FE, 0x68A7, 0xE7FC,\n\t0x68A8, 0xD7DE, 0x68AD, 0xDEDC, 0x68AF, 0xF0AC, 0x68B0, 0xCCFE,\n\t0x68B1, 0xCDE1, 0x68B3, 0xE1BA, 0x68B5, 0xDBEF, 0x68B6, 0xDAB2,\n\t0x68C4, 0xD1A5, 0x68C5, 0xDCB8, 0x68C9, 0xD8F6, 0x68CB, 0xD1A4,\n\t0x68CD, 0xCDE2, 0x68D2, 0xDCEA, 0x68D5, 0xF0F7, 0x68D7, 0xF0CA,\n\t0x68D8, 0xD0BE, 0x68DA, 0xDDDC, 0x68DF, 0xD4D6, 0x68E0, 0xD3D6,\n\t0x68E7, 0xEDD0, 0x68E8, 0xCDA1, 0x68EE, 0xDFB5, 0x68F2, 0xDFF8,\n\t0x68F9, 0xD4A1, 0x68FA, 0xCEB2, 0x6900, 0xE8CA, 0x6905, 0xEBF5,\n\t0x690D, 0xE3D5, 0x690E, 0xF5D0, 0x6912, 0xF5A1, 0x6927, 0xD9A7,\n\t0x6930, 0xE5AB, 0x693D, 0xE6CB, 0x693F, 0xF5F1, 0x694A, 0xE5C5,\n\t0x6953, 0xF9A3, 0x6954, 0xE0DB, 0x6955, 0xF6EB, 0x6957, 0xCBF1,\n\t0x6959, 0xD9EA, 0x695A, 0xF5A2, 0x695E, 0xD7D1, 0x6960, 0xD1F8,\n\t0x6961, 0xEAF8, 0x6962, 0xEAF9, 0x6963, 0xDAB3, 0x6968, 0xEFDF,\n\t0x696B, 0xF1EF, 0x696D, 0xE5F6, 0x696E, 0xEEBF, 0x696F, 0xE2E4,\n\t0x6975, 0xD0BF, 0x6977, 0xFAAC, 0x6978, 0xF5D1, 0x6979, 0xE7B3,\n\t0x6995, 0xE9BE, 0x699B, 0xF2CE, 0x699C, 0xDBB4, 0x69A5, 0xFCCE,\n\t0x69A7, 0xDDEE, 0x69AE, 0xE7B4, 0x69B4, 0xD7B4, 0x69BB, 0xF7B4,\n\t0x69C1, 0xCDBE, 0x69C3, 0xDAE9, 0x69CB, 0xCFB0, 0x69CC, 0xF7D9,\n\t0x69CD, 0xF3E6, 0x69D0, 0xCED9, 0x69E8, 0xCEAA, 0x69EA, 0xCBC8,\n\t0x69FB, 0xD0A7, 0x69FD, 0xF0CB, 0x69FF, 0xD0C7, 0x6A02, 0xE4C5,\n\t0x6A0A, 0xDBE0, 0x6A11, 0xD5DA, 0x6A13, 0xD7A7, 0x6A17, 0xEEC0,\n\t0x6A19, 0xF8F6, 0x6A1E, 0xF5D2, 0x6A1F, 0xEDE9, 0x6A21, 0xD9BC,\n\t0x6A23, 0xE5C6, 0x6A35, 0xF5A3, 0x6A38, 0xDAD4, 0x6A39, 0xE2A7,\n\t0x6A3A, 0xFBFC, 0x6A3D, 0xF1DC, 0x6A44, 0xCAF4, 0x6A48, 0xE8FA,\n\t0x6A4B, 0xCEE9, 0x6A52, 0xE9F8, 0x6A53, 0xE2E5, 0x6A58, 0xD0B9,\n\t0x6A59, 0xD4F2, 0x6A5F, 0xD1A6, 0x6A61, 0xDFCE, 0x6A6B, 0xFCF4,\n\t0x6A80, 0xD3AA, 0x6A84, 0xCCAC, 0x6A89, 0xEFE0, 0x6A8D, 0xE5E5,\n\t0x6A8E, 0xD0D5, 0x6A97, 0xDBFC, 0x6A9C, 0xFCE6, 0x6AA2, 0xCBFE,\n\t0x6AA3, 0xEDEA, 0x6AB3, 0xDEB1, 0x6ABB, 0xF9E3, 0x6AC2, 0xD4A2,\n\t0x6AC3, 0xCFF6, 0x6AD3, 0xD6D0, 0x6ADA, 0xD5EA, 0x6ADB, 0xF1EE,\n\t0x6AF6, 0xFACB, 0x6AFB, 0xE5A1, 0x6B04, 0xD5B1, 0x6B0A, 0xCFED,\n\t0x6B0C, 0xEDEB, 0x6B12, 0xD5B2, 0x6B16, 0xD5BC, 0x6B20, 0xFDE2,\n\t0x6B21, 0xF3AD, 0x6B23, 0xFDDB, 0x6B32, 0xE9B0, 0x6B3A, 0xD1A7,\n\t0x6B3D, 0xFDE3, 0x6B3E, 0xCEB3, 0x6B46, 0xFDE4, 0x6B47, 0xFACE,\n\t0x6B4C, 0xCAB0, 0x6B4E, 0xF7A7, 0x6B50, 0xCFB1, 0x6B5F, 0xE6A2,\n\t0x6B61, 0xFCB6, 0x6B62, 0xF2AD, 0x6B63, 0xEFE1, 0x6B64, 0xF3AE,\n\t0x6B65, 0xDCC6, 0x6B66, 0xD9EB, 0x6B6A, 0xE8E0, 0x6B72, 0xE1A8,\n\t0x6B77, 0xD5F6, 0x6B78, 0xCFFD, 0x6B7B, 0xDEDD, 0x6B7F, 0xD9D1,\n\t0x6B83, 0xE4EA, 0x6B84, 0xF2CF, 0x6B86, 0xF7BF, 0x6B89, 0xE2E6,\n\t0x6B8A, 0xE2A8, 0x6B96, 0xE3D6, 0x6B98, 0xEDD1, 0x6B9E, 0xE9F9,\n\t0x6BAE, 0xD6B1, 0x6BAF, 0xDEB2, 0x6BB2, 0xE0E8, 0x6BB5, 0xD3AB,\n\t0x6BB7, 0xEBDC, 0x6BBA, 0xDFAF, 0x6BBC, 0xCAC3, 0x6BBF, 0xEEFC,\n\t0x6BC1, 0xFDC3, 0x6BC5, 0xEBF6, 0x6BC6, 0xCFB2, 0x6BCB, 0xD9EC,\n\t0x6BCD, 0xD9BD, 0x6BCF, 0xD8DF, 0x6BD2, 0xD4B8, 0x6BD3, 0xEBBE,\n\t0x6BD4, 0xDDEF, 0x6BD6, 0xDDF0, 0x6BD7, 0xDDF1, 0x6BD8, 0xDDF2,\n\t0x6BDB, 0xD9BE, 0x6BEB, 0xFBC6, 0x6BEC, 0xCFB3, 0x6C08, 0xEEFD,\n\t0x6C0F, 0xE4AB, 0x6C11, 0xDAC5, 0x6C13, 0xD8EC, 0x6C23, 0xD1A8,\n\t0x6C34, 0xE2A9, 0x6C37, 0xDEBC, 0x6C38, 0xE7B5, 0x6C3E, 0xDBF0,\n\t0x6C40, 0xEFE2, 0x6C41, 0xF1F0, 0x6C42, 0xCFB4, 0x6C4E, 0xDBF1,\n\t0x6C50, 0xE0B1, 0x6C55, 0xDFA5, 0x6C57, 0xF9D2, 0x6C5A, 0xE7FD,\n\t0x6C5D, 0xE6A3, 0x6C5E, 0xFBF1, 0x6C5F, 0xCBB0, 0x6C60, 0xF2AE,\n\t0x6C68, 0xCDE7, 0x6C6A, 0xE8DC, 0x6C6D, 0xE7D7, 0x6C70, 0xF7C0,\n\t0x6C72, 0xD0E3, 0x6C76, 0xDAA1, 0x6C7A, 0xCCBD, 0x6C7D, 0xD1A9,\n\t0x6C7E, 0xDDCC, 0x6C81, 0xE3FE, 0x6C82, 0xD1AA, 0x6C83, 0xE8AA,\n\t0x6C85, 0xEAB6, 0x6C86, 0xF9FA, 0x6C87, 0xE6CC, 0x6C88, 0xF6D8,\n\t0x6C8C, 0xD4C7, 0x6C90, 0xD9CB, 0x6C92, 0xD9D2, 0x6C93, 0xD3CB,\n\t0x6C94, 0xD8F7, 0x6C95, 0xDAA9, 0x6C96, 0xF5F8, 0x6C99, 0xDEDE,\n\t0x6C9A, 0xF2AF, 0x6C9B, 0xF8A9, 0x6CAB, 0xD8C8, 0x6CAE, 0xEEC1,\n\t0x6CB3, 0xF9C1, 0x6CB8, 0xDDF3, 0x6CB9, 0xEAFA, 0x6CBB, 0xF6BD,\n\t0x6CBC, 0xE1BB, 0x6CBD, 0xCDBF, 0x6CBE, 0xF4D4, 0x6CBF, 0xE6CD,\n\t0x6CC1, 0xFCCF, 0x6CC2, 0xFBA2, 0x6CC4, 0xE0DC, 0x6CC9, 0xF4BB,\n\t0x6CCA, 0xDAD5, 0x6CCC, 0xF9B2, 0x6CD3, 0xFBF2, 0x6CD5, 0xDBF6,\n\t0x6CD7, 0xDEDF, 0x6CDB, 0xDBF2, 0x6CE1, 0xF8DC, 0x6CE2, 0xF7EE,\n\t0x6CE3, 0xEBE8, 0x6CE5, 0xD2FA, 0x6CE8, 0xF1BC, 0x6CEB, 0xFADA,\n\t0x6CEE, 0xDAEA, 0x6CEF, 0xDAC6, 0x6CF0, 0xF7C1, 0x6CF3, 0xE7B6,\n\t0x6D0B, 0xE5C7, 0x6D0C, 0xD6AC, 0x6D11, 0xDCC7, 0x6D17, 0xE1A9,\n\t0x6D19, 0xE2AA, 0x6D1B, 0xD5A6, 0x6D1E, 0xD4D7, 0x6D25, 0xF2D0,\n\t0x6D27, 0xEAFB, 0x6D29, 0xE0DD, 0x6D2A, 0xFBF3, 0x6D32, 0xF1BD,\n\t0x6D35, 0xE2E7, 0x6D36, 0xFDD7, 0x6D38, 0xCEC8, 0x6D39, 0xEAB7,\n\t0x6D3B, 0xFCC0, 0x6D3D, 0xFDE7, 0x6D3E, 0xF7EF, 0x6D41, 0xD7B5,\n\t0x6D59, 0xEFBA, 0x6D5A, 0xF1DD, 0x6D5C, 0xDEB3, 0x6D63, 0xE8CB,\n\t0x6D66, 0xF8DD, 0x6D69, 0xFBC7, 0x6D6A, 0xD5C8, 0x6D6C, 0xD7DF,\n\t0x6D6E, 0xDDA9, 0x6D74, 0xE9B1, 0x6D77, 0xFAAD, 0x6D78, 0xF6D9,\n\t0x6D79, 0xFAF4, 0x6D7F, 0xF8AA, 0x6D85, 0xE6EE, 0x6D87, 0xCCDC,\n\t0x6D88, 0xE1BC, 0x6D89, 0xE0EF, 0x6D8C, 0xE9BF, 0x6D8D, 0xFCFD,\n\t0x6D8E, 0xE6CE, 0x6D91, 0xE1D7, 0x6D93, 0xE6CF, 0x6D95, 0xF4F1,\n\t0x6DAF, 0xE4F3, 0x6DB2, 0xE4FB, 0x6DB5, 0xF9E4, 0x6DC0, 0xEFE3,\n\t0x6DC3, 0xCFEE, 0x6DC4, 0xF6BE, 0x6DC5, 0xE0B2, 0x6DC6, 0xFCFE,\n\t0x6DC7, 0xD1AB, 0x6DCB, 0xD7FA, 0x6DCF, 0xFBC8, 0x6DD1, 0xE2D7,\n\t0x6DD8, 0xD4A3, 0x6DD9, 0xF0F8, 0x6DDA, 0xD7A8, 0x6DDE, 0xE1E7,\n\t0x6DE1, 0xD3BF, 0x6DE8, 0xEFE4, 0x6DEA, 0xD7C5, 0x6DEB, 0xEBE2,\n\t0x6DEE, 0xFCE7, 0x6DF1, 0xE4A2, 0x6DF3, 0xE2E8, 0x6DF5, 0xE6D0,\n\t0x6DF7, 0xFBE8, 0x6DF8, 0xF4E8, 0x6DF9, 0xE5F4, 0x6DFA, 0xF4BC,\n\t0x6DFB, 0xF4D5, 0x6E17, 0xDFB6, 0x6E19, 0xFCB9, 0x6E1A, 0xEEC2,\n\t0x6E1B, 0xCAF5, 0x6E1F, 0xEFE5, 0x6E20, 0xCBE2, 0x6E21, 0xD4A4,\n\t0x6E23, 0xDEE0, 0x6E24, 0xDAFD, 0x6E25, 0xE4C6, 0x6E26, 0xE8BE,\n\t0x6E2B, 0xE0DE, 0x6E2C, 0xF6B4, 0x6E2D, 0xEAD2, 0x6E2F, 0xF9FB,\n\t0x6E32, 0xE0C2, 0x6E34, 0xCAE4, 0x6E36, 0xE7B7, 0x6E38, 0xEAFD,\n\t0x6E3A, 0xD9DD, 0x6E3C, 0xDAB4, 0x6E3D, 0xEEAA, 0x6E3E, 0xFBE9,\n\t0x6E43, 0xDBCB, 0x6E44, 0xDAB5, 0x6E4A, 0xF1BE, 0x6E4D, 0xD3AC,\n\t0x6E56, 0xFBC9, 0x6E58, 0xDFCF, 0x6E5B, 0xD3C0, 0x6E5C, 0xE3D7,\n\t0x6E5E, 0xEFE6, 0x6E5F, 0xFCD0, 0x6E67, 0xE9C0, 0x6E6B, 0xF5D3,\n\t0x6E6E, 0xECDC, 0x6E6F, 0xF7B7, 0x6E72, 0xEAB8, 0x6E73, 0xD1F9,\n\t0x6E7A, 0xDCC8, 0x6E90, 0xEAB9, 0x6E96, 0xF1DE, 0x6E9C, 0xD7B6,\n\t0x6E9D, 0xCFB5, 0x6E9F, 0xD9A8, 0x6EA2, 0xECEE, 0x6EA5, 0xDDAA,\n\t0x6EAA, 0xCDA2, 0x6EAB, 0xE8AE, 0x6EAF, 0xE1BD, 0x6EB1, 0xF2D1,\n\t0x6EB6, 0xE9C1, 0x6EBA, 0xD2FC, 0x6EC2, 0xDBB5, 0x6EC4, 0xF3E7,\n\t0x6EC5, 0xD8FE, 0x6EC9, 0xFCD1, 0x6ECB, 0xEDB2, 0x6ECC, 0xF4AF,\n\t0x6ECE, 0xFBA3, 0x6ED1, 0xFCC1, 0x6ED3, 0xEEAB, 0x6ED4, 0xD4A5,\n\t0x6EEF, 0xF4F2, 0x6EF4, 0xEED9, 0x6EF8, 0xFBCA, 0x6EFE, 0xCDE3,\n\t0x6EFF, 0xD8BB, 0x6F01, 0xE5DB, 0x6F02, 0xF8F7, 0x6F06, 0xF6D4,\n\t0x6F0F, 0xD7A9, 0x6F11, 0xCBC9, 0x6F14, 0xE6D1, 0x6F15, 0xF0CC,\n\t0x6F20, 0xD8AE, 0x6F22, 0xF9D3, 0x6F23, 0xD5FE, 0x6F2B, 0xD8BC,\n\t0x6F2C, 0xF2B0, 0x6F31, 0xE2AB, 0x6F32, 0xF3E8, 0x6F38, 0xEFC2,\n\t0x6F3F, 0xEDEC, 0x6F41, 0xE7B8, 0x6F51, 0xDAFE, 0x6F54, 0xCCBE,\n\t0x6F57, 0xF2FC, 0x6F58, 0xDAEB, 0x6F5A, 0xE2D8, 0x6F5B, 0xEDD6,\n\t0x6F5E, 0xD6D1, 0x6F5F, 0xE0B3, 0x6F62, 0xFCD2, 0x6F64, 0xEBC8,\n\t0x6F6D, 0xD3C1, 0x6F6E, 0xF0CD, 0x6F70, 0xCFF7, 0x6F7A, 0xEDD2,\n\t0x6F7C, 0xD4D8, 0x6F7D, 0xDCC9, 0x6F7E, 0xD7F1, 0x6F81, 0xDFBB,\n\t0x6F84, 0xF3A5, 0x6F88, 0xF4CD, 0x6F8D, 0xF1BF, 0x6F8E, 0xF8B1,\n\t0x6F90, 0xE9FA, 0x6F94, 0xFBCB, 0x6F97, 0xCAD5, 0x6FA3, 0xF9D4,\n\t0x6FA4, 0xF7CA, 0x6FA7, 0xD6C8, 0x6FAE, 0xFCE8, 0x6FAF, 0xF3BD,\n\t0x6FB1, 0xEEFE, 0x6FB3, 0xE7FE, 0x6FB9, 0xD3C2, 0x6FBE, 0xD3B6,\n\t0x6FC0, 0xCCAD, 0x6FC1, 0xF6FA, 0x6FC2, 0xD6B2, 0x6FC3, 0xD2D8,\n\t0x6FCA, 0xE7D8, 0x6FD5, 0xE3A5, 0x6FDA, 0xE7B9, 0x6FDF, 0xF0AD,\n\t0x6FE0, 0xFBCC, 0x6FE1, 0xEBA1, 0x6FE4, 0xD4A6, 0x6FE9, 0xFBCD,\n\t0x6FEB, 0xD5BD, 0x6FEC, 0xF1DF, 0x6FEF, 0xF6FB, 0x6FF1, 0xDEB4,\n\t0x6FFE, 0xD5EB, 0x7001, 0xE5C8, 0x7005, 0xFBA4, 0x7006, 0xD4B9,\n\t0x7009, 0xDEE1, 0x700B, 0xE4A3, 0x700F, 0xD7B7, 0x7011, 0xF8EE,\n\t0x7015, 0xDEB5, 0x7018, 0xD6D2, 0x701A, 0xF9D5, 0x701B, 0xE7BA,\n\t0x701C, 0xEBD5, 0x701D, 0xD5F7, 0x701E, 0xEFE7, 0x701F, 0xE1BE,\n\t0x7023, 0xFAAE, 0x7027, 0xD6E9, 0x7028, 0xD6EE, 0x702F, 0xE7BB,\n\t0x7037, 0xECCB, 0x703E, 0xD5B3, 0x704C, 0xCEB4, 0x7050, 0xFBA5,\n\t0x7051, 0xE1EE, 0x7058, 0xF7A8, 0x705D, 0xFBCE, 0x7063, 0xD8BD,\n\t0x706B, 0xFBFD, 0x7070, 0xFCE9, 0x7078, 0xCFB6, 0x707C, 0xEDC7,\n\t0x707D, 0xEEAC, 0x7085, 0xCCDD, 0x708A, 0xF6A7, 0x708E, 0xE6FA,\n\t0x7092, 0xF5A4, 0x7098, 0xFDDC, 0x7099, 0xEDB3, 0x709A, 0xCEC9,\n\t0x70A1, 0xEFE8, 0x70A4, 0xE1BF, 0x70AB, 0xFADB, 0x70AC, 0xCBE3,\n\t0x70AD, 0xF7A9, 0x70AF, 0xFBA6, 0x70B3, 0xDCB9, 0x70B7, 0xF1C0,\n\t0x70B8, 0xEDC8, 0x70B9, 0xEFC3, 0x70C8, 0xD6AD, 0x70CB, 0xFDCE,\n\t0x70CF, 0xE8A1, 0x70D8, 0xFBF4, 0x70D9, 0xD5A7, 0x70DD, 0xF1F6,\n\t0x70DF, 0xE6D3, 0x70F1, 0xCCDE, 0x70F9, 0xF8B2, 0x70FD, 0xDCEB,\n\t0x7104, 0xFDB6, 0x7109, 0xE5EA, 0x710C, 0xF1E0, 0x7119, 0xDBCC,\n\t0x711A, 0xDDCD, 0x711E, 0xD4C8, 0x7121, 0xD9ED, 0x7126, 0xF5A5,\n\t0x7130, 0xE6FB, 0x7136, 0xE6D4, 0x7147, 0xFDC8, 0x7149, 0xD6A1,\n\t0x714A, 0xFDBF, 0x714C, 0xFCD3, 0x714E, 0xEFA1, 0x7150, 0xE7BC,\n\t0x7156, 0xD1EE, 0x7159, 0xE6D5, 0x715C, 0xE9F2, 0x715E, 0xDFB0,\n\t0x7164, 0xD8E0, 0x7165, 0xFCBA, 0x7166, 0xFDAF, 0x7167, 0xF0CE,\n\t0x7169, 0xDBE1, 0x716C, 0xE5C9, 0x716E, 0xEDB4, 0x717D, 0xE0C3,\n\t0x7184, 0xE3D8, 0x7189, 0xE9FB, 0x718A, 0xEAA8, 0x718F, 0xFDB7,\n\t0x7192, 0xFBA7, 0x7194, 0xE9C2, 0x7199, 0xFDF7, 0x719F, 0xE2D9,\n\t0x71A2, 0xDCEC, 0x71AC, 0xE8A2, 0x71B1, 0xE6F0, 0x71B9, 0xFDF8,\n\t0x71BA, 0xFDF9, 0x71BE, 0xF6BF, 0x71C1, 0xE7A7, 0x71C3, 0xE6D7,\n\t0x71C8, 0xD4F3, 0x71C9, 0xD4C9, 0x71CE, 0xD6FA, 0x71D0, 0xD7F2,\n\t0x71D2, 0xE1C0, 0x71D4, 0xDBE2, 0x71D5, 0xE6D8, 0x71DF, 0xE7BD,\n\t0x71E5, 0xF0CF, 0x71E6, 0xF3BE, 0x71E7, 0xE2AC, 0x71ED, 0xF5B7,\n\t0x71EE, 0xE0F0, 0x71FB, 0xFDB8, 0x71FC, 0xE3E8, 0x71FE, 0xD4A7,\n\t0x71FF, 0xE8FC, 0x7200, 0xFAD2, 0x7206, 0xF8EF, 0x7210, 0xD6D3,\n\t0x721B, 0xD5B4, 0x722A, 0xF0D0, 0x722C, 0xF7F0, 0x722D, 0xEEB3,\n\t0x7230, 0xEABA, 0x7232, 0xEAD3, 0x7235, 0xEDC9, 0x7236, 0xDDAB,\n\t0x723A, 0xE5AC, 0x723B, 0xFDA1, 0x723D, 0xDFD0, 0x723E, 0xECB3,\n\t0x7240, 0xDFD1, 0x7246, 0xEDED, 0x7247, 0xF8B8, 0x7248, 0xF7FA,\n\t0x724C, 0xF8AB, 0x7252, 0xF4E0, 0x7258, 0xD4BA, 0x7259, 0xE4B3,\n\t0x725B, 0xE9DA, 0x725D, 0xDEB6, 0x725F, 0xD9BF, 0x7261, 0xD9C0,\n\t0x7262, 0xD6EF, 0x7267, 0xD9CC, 0x7269, 0xDAAA, 0x7272, 0xDFE5,\n\t0x7279, 0xF7E5, 0x727D, 0xCCB2, 0x7280, 0xDFF9, 0x7281, 0xD7E0,\n\t0x72A2, 0xD4BB, 0x72A7, 0xFDFA, 0x72AC, 0xCCB3, 0x72AF, 0xDBF3,\n\t0x72C0, 0xDFD2, 0x72C2, 0xCECA, 0x72C4, 0xEEDA, 0x72CE, 0xE4E4,\n\t0x72D0, 0xFBCF, 0x72D7, 0xCFB7, 0x72D9, 0xEEC3, 0x72E1, 0xCEEA,\n\t0x72E9, 0xE2AD, 0x72F8, 0xD7E1, 0x72F9, 0xFAF5, 0x72FC, 0xD5C9,\n\t0x72FD, 0xF8AC, 0x730A, 0xE7D9, 0x7316, 0xF3E9, 0x731B, 0xD8ED,\n\t0x731C, 0xE3C4, 0x731D, 0xF0F1, 0x7325, 0xE8E5, 0x7329, 0xE0FA,\n\t0x732A, 0xEEC4, 0x732B, 0xD9DE, 0x7336, 0xEBA2, 0x7337, 0xEBA3,\n\t0x733E, 0xFCC2, 0x733F, 0xEABB, 0x7344, 0xE8AB, 0x7345, 0xDEE2,\n\t0x7350, 0xEDEF, 0x7352, 0xE8A3, 0x7357, 0xCFF1, 0x7368, 0xD4BC,\n\t0x736A, 0xFCEA, 0x7370, 0xE7BE, 0x7372, 0xFCF2, 0x7375, 0xD6B4,\n\t0x7378, 0xE2AE, 0x737A, 0xD3B7, 0x737B, 0xFACC, 0x7384, 0xFADC,\n\t0x7386, 0xEDB5, 0x7387, 0xE1E3, 0x7389, 0xE8AC, 0x738B, 0xE8DD,\n\t0x738E, 0xEFE9, 0x7394, 0xF4BD, 0x7396, 0xCFB8, 0x7397, 0xE9DB,\n\t0x7398, 0xD1AC, 0x739F, 0xDAC7, 0x73A7, 0xEBC9, 0x73A9, 0xE8CC,\n\t0x73AD, 0xDEB7, 0x73B2, 0xD6BC, 0x73B3, 0xD3E5, 0x73B9, 0xFADD,\n\t0x73C0, 0xDAD6, 0x73C2, 0xCAB1, 0x73C9, 0xDAC8, 0x73CA, 0xDFA6,\n\t0x73CC, 0xF9B3, 0x73CD, 0xF2D2, 0x73CF, 0xCAC4, 0x73D6, 0xCECB,\n\t0x73D9, 0xCDF5, 0x73DD, 0xFDB0, 0x73DE, 0xD5A8, 0x73E0, 0xF1C1,\n\t0x73E3, 0xE2E9, 0x73E4, 0xDCCA, 0x73E5, 0xECB4, 0x73E6, 0xFAC0,\n\t0x73E9, 0xFBA8, 0x73EA, 0xD0A8, 0x73ED, 0xDAEC, 0x73F7, 0xD9EE,\n\t0x73F9, 0xE0FB, 0x73FD, 0xEFEA, 0x73FE, 0xFADE, 0x7401, 0xE0C4,\n\t0x7403, 0xCFB9, 0x7405, 0xD5CA, 0x7406, 0xD7E2, 0x7407, 0xE2AF,\n\t0x7409, 0xD7B8, 0x7413, 0xE8CD, 0x741B, 0xF6DA, 0x7420, 0xEFA2,\n\t0x7421, 0xE2DA, 0x7422, 0xF6FC, 0x7425, 0xFBD0, 0x7426, 0xD1AD,\n\t0x7428, 0xCDE4, 0x742A, 0xD1AE, 0x742B, 0xDCED, 0x742C, 0xE8CE,\n\t0x742E, 0xF0F9, 0x742F, 0xCEB5, 0x7430, 0xE6FC, 0x7433, 0xD7FB,\n\t0x7434, 0xD0D6, 0x7435, 0xDDF5, 0x7436, 0xF7F1, 0x7438, 0xF6FD,\n\t0x743A, 0xDBF7, 0x743F, 0xFBEA, 0x7440, 0xE9DC, 0x7441, 0xD9C1,\n\t0x7443, 0xF5F2, 0x7444, 0xE0C5, 0x744B, 0xEAD4, 0x7455, 0xF9C2,\n\t0x7457, 0xEABC, 0x7459, 0xD2C5, 0x745A, 0xFBD1, 0x745B, 0xE7C0,\n\t0x745C, 0xEBA5, 0x745E, 0xDFFA, 0x745F, 0xE3A2, 0x7460, 0xD7B9,\n\t0x7462, 0xE9C3, 0x7464, 0xE8FD, 0x7465, 0xE8AF, 0x7468, 0xF2D3,\n\t0x7469, 0xFBA9, 0x746A, 0xD8A5, 0x746F, 0xD5CB, 0x747E, 0xD0C8,\n\t0x7482, 0xD1AF, 0x7483, 0xD7E3, 0x7487, 0xE0C6, 0x7489, 0xD6A2,\n\t0x748B, 0xEDF0, 0x7498, 0xD7F3, 0x749C, 0xFCD4, 0x749E, 0xDAD7,\n\t0x749F, 0xCCDF, 0x74A1, 0xF2D4, 0x74A3, 0xD1B0, 0x74A5, 0xCCE0,\n\t0x74A7, 0xDBFD, 0x74A8, 0xF3BF, 0x74AA, 0xF0D1, 0x74B0, 0xFCBB,\n\t0x74B2, 0xE2B0, 0x74B5, 0xE6A5, 0x74B9, 0xE2DB, 0x74BD, 0xDFDE,\n\t0x74BF, 0xE0C7, 0x74C6, 0xF2EF, 0x74CA, 0xCCE1, 0x74CF, 0xD6EA,\n\t0x74D4, 0xE7C2, 0x74D8, 0xCEB6, 0x74DA, 0xF3C0, 0x74DC, 0xCDFE,\n\t0x74E0, 0xFBD2, 0x74E2, 0xF8F8, 0x74E3, 0xF7FB, 0x74E6, 0xE8BF,\n\t0x74EE, 0xE8B7, 0x74F7, 0xEDB6, 0x7501, 0xDCBA, 0x7504, 0xCCB4,\n\t0x7511, 0xF1F7, 0x7515, 0xE8B8, 0x7518, 0xCAF6, 0x751A, 0xE4A4,\n\t0x751B, 0xF4D6, 0x751F, 0xDFE6, 0x7523, 0xDFA7, 0x7525, 0xDFE7,\n\t0x7526, 0xE1C1, 0x7528, 0xE9C4, 0x752B, 0xDCCB, 0x752C, 0xE9C5,\n\t0x7530, 0xEFA3, 0x7531, 0xEBA6, 0x7532, 0xCBA3, 0x7533, 0xE3E9,\n\t0x7537, 0xD1FB, 0x7538, 0xEFA4, 0x753A, 0xEFEB, 0x7547, 0xD0B4,\n\t0x754C, 0xCDA3, 0x754F, 0xE8E6, 0x7551, 0xEFA5, 0x7553, 0xD3CC,\n\t0x7554, 0xDAED, 0x7559, 0xD7BA, 0x755B, 0xF2D5, 0x755C, 0xF5E5,\n\t0x755D, 0xD9EF, 0x7562, 0xF9B4, 0x7565, 0xD5D4, 0x7566, 0xFDCF,\n\t0x756A, 0xDBE3, 0x756F, 0xF1E1, 0x7570, 0xECB6, 0x7575, 0xFBFE,\n\t0x7576, 0xD3D7, 0x7578, 0xD1B1, 0x757A, 0xCBB1, 0x757F, 0xD1B2,\n\t0x7586, 0xCBB2, 0x7587, 0xF1C2, 0x758A, 0xF4E1, 0x758B, 0xF9B5,\n\t0x758E, 0xE1C3, 0x758F, 0xE1C2, 0x7591, 0xEBF7, 0x759D, 0xDFA8,\n\t0x75A5, 0xCBCA, 0x75AB, 0xE6B9, 0x75B1, 0xF8DE, 0x75B2, 0xF9AA,\n\t0x75B3, 0xCAF7, 0x75B5, 0xEDB7, 0x75B8, 0xD3B8, 0x75B9, 0xF2D6,\n\t0x75BC, 0xD4D9, 0x75BD, 0xEEC5, 0x75BE, 0xF2F0, 0x75C2, 0xCAB2,\n\t0x75C5, 0xDCBB, 0x75C7, 0xF1F8, 0x75CD, 0xECB7, 0x75D2, 0xE5CA,\n\t0x75D4, 0xF6C0, 0x75D5, 0xFDDD, 0x75D8, 0xD4E3, 0x75D9, 0xCCE2,\n\t0x75DB, 0xF7D4, 0x75E2, 0xD7E5, 0x75F0, 0xD3C3, 0x75F2, 0xD8A6,\n\t0x75F4, 0xF6C1, 0x75FA, 0xDDF6, 0x75FC, 0xCDC0, 0x7600, 0xE5DC,\n\t0x760D, 0xE5CB, 0x7619, 0xE1C4, 0x761F, 0xE8B0, 0x7620, 0xF4B0,\n\t0x7621, 0xF3EA, 0x7622, 0xDAEE, 0x7624, 0xD7BB, 0x7626, 0xE2B1,\n\t0x763B, 0xD7AA, 0x7642, 0xD6FB, 0x764C, 0xE4DF, 0x764E, 0xCAD6,\n\t0x7652, 0xEBA8, 0x7656, 0xDBFE, 0x7661, 0xF6C2, 0x7664, 0xEFBB,\n\t0x7669, 0xD4FD, 0x766C, 0xE0C8, 0x7670, 0xE8B9, 0x7672, 0xEFA6,\n\t0x7678, 0xCDA4, 0x767B, 0xD4F4, 0x767C, 0xDBA1, 0x767D, 0xDBDC,\n\t0x767E, 0xDBDD, 0x7684, 0xEEDC, 0x7686, 0xCBCB, 0x7687, 0xFCD5,\n\t0x768E, 0xCEEB, 0x7690, 0xCDC1, 0x7693, 0xFBD3, 0x76AE, 0xF9AB,\n\t0x76BA, 0xF5D4, 0x76BF, 0xD9A9, 0x76C2, 0xE9DD, 0x76C3, 0xDBCD,\n\t0x76C6, 0xDDCE, 0x76C8, 0xE7C3, 0x76CA, 0xECCC, 0x76D2, 0xF9EC,\n\t0x76D6, 0xCBCC, 0x76DB, 0xE0FC, 0x76DC, 0xD4A8, 0x76DE, 0xEDD3,\n\t0x76DF, 0xD8EF, 0x76E1, 0xF2D7, 0x76E3, 0xCAF8, 0x76E4, 0xDAEF,\n\t0x76E7, 0xD6D4, 0x76EE, 0xD9CD, 0x76F2, 0xD8EE, 0x76F4, 0xF2C1,\n\t0x76F8, 0xDFD3, 0x76FC, 0xDAF0, 0x76FE, 0xE2EA, 0x7701, 0xE0FD,\n\t0x7704, 0xD8F8, 0x7708, 0xF7AF, 0x7709, 0xDAB6, 0x770B, 0xCAD7,\n\t0x771E, 0xF2D8, 0x7720, 0xD8F9, 0x7729, 0xFADF, 0x7737, 0xCFEF,\n\t0x7738, 0xD9C2, 0x773A, 0xF0D2, 0x773C, 0xE4D1, 0x7740, 0xF3B7,\n\t0x774D, 0xFAE0, 0x775B, 0xEFEC, 0x7761, 0xE2B2, 0x7763, 0xD4BD,\n\t0x7766, 0xD9CE, 0x776B, 0xF4E2, 0x7779, 0xD4A9, 0x777E, 0xCDC2,\n\t0x777F, 0xE7DA, 0x778B, 0xF2D9, 0x7791, 0xD9AA, 0x779E, 0xD8BE,\n\t0x77A5, 0xDCAD, 0x77AC, 0xE2EB, 0x77AD, 0xD6FC, 0x77B0, 0xCAF9,\n\t0x77B3, 0xD4DA, 0x77BB, 0xF4D7, 0x77BC, 0xCCA1, 0x77BF, 0xCFBA,\n\t0x77D7, 0xF5B8, 0x77DB, 0xD9C3, 0x77DC, 0xD0E8, 0x77E2, 0xE3C5,\n\t0x77E3, 0xEBF8, 0x77E5, 0xF2B1, 0x77E9, 0xCFBB, 0x77ED, 0xD3AD,\n\t0x77EE, 0xE8E1, 0x77EF, 0xCEEC, 0x77F3, 0xE0B4, 0x7802, 0xDEE3,\n\t0x7812, 0xDDF7, 0x7825, 0xF2B2, 0x7826, 0xF3F6, 0x7827, 0xF6DB,\n\t0x782C, 0xD7FE, 0x7832, 0xF8DF, 0x7834, 0xF7F2, 0x7845, 0xD0A9,\n\t0x784F, 0xE6DA, 0x785D, 0xF5A6, 0x786B, 0xD7BC, 0x786C, 0xCCE3,\n\t0x786F, 0xE6DB, 0x787C, 0xDDDD, 0x7881, 0xD1B3, 0x7887, 0xEFED,\n\t0x788C, 0xD6DE, 0x788D, 0xE4F4, 0x788E, 0xE1EF, 0x7891, 0xDDF8,\n\t0x7897, 0xE8CF, 0x78A3, 0xCAE5, 0x78A7, 0xDCA1, 0x78A9, 0xE0B5,\n\t0x78BA, 0xFCAC, 0x78BB, 0xFCAD, 0x78BC, 0xD8A7, 0x78C1, 0xEDB8,\n\t0x78C5, 0xDBB6, 0x78CA, 0xD6F0, 0x78CB, 0xF3AF, 0x78CE, 0xCDA5,\n\t0x78D0, 0xDAF1, 0x78E8, 0xD8A8, 0x78EC, 0xCCE4, 0x78EF, 0xD1B4,\n\t0x78F5, 0xCAD8, 0x78FB, 0xDAF2, 0x7901, 0xF5A7, 0x790E, 0xF5A8,\n\t0x7916, 0xE6A6, 0x792A, 0xD5EC, 0x792B, 0xD5F8, 0x792C, 0xDAF3,\n\t0x793A, 0xE3C6, 0x793E, 0xDEE4, 0x7940, 0xDEE5, 0x7941, 0xD1B5,\n\t0x7947, 0xD1B6, 0x7948, 0xD1B7, 0x7949, 0xF2B3, 0x7950, 0xE9DE,\n\t0x7956, 0xF0D3, 0x7957, 0xF2B4, 0x795A, 0xF0D4, 0x795B, 0xCBE4,\n\t0x795C, 0xFBD4, 0x795D, 0xF5E6, 0x795E, 0xE3EA, 0x7960, 0xDEE6,\n\t0x7965, 0xDFD4, 0x7968, 0xF8F9, 0x796D, 0xF0AE, 0x797A, 0xD1B8,\n\t0x797F, 0xD6DF, 0x7981, 0xD0D7, 0x798D, 0xFCA1, 0x798E, 0xEFEE,\n\t0x798F, 0xDCD8, 0x7991, 0xE9DF, 0x79A6, 0xE5DD, 0x79A7, 0xFDFB,\n\t0x79AA, 0xE0C9, 0x79AE, 0xD6C9, 0x79B1, 0xD4AA, 0x79B3, 0xE5CC,\n\t0x79B9, 0xE9E0, 0x79BD, 0xD0D8, 0x79BE, 0xFCA2, 0x79BF, 0xD4BE,\n\t0x79C0, 0xE2B3, 0x79C1, 0xDEE7, 0x79C9, 0xDCBC, 0x79CA, 0xD2B6,\n\t0x79CB, 0xF5D5, 0x79D1, 0xCEA1, 0x79D2, 0xF5A9, 0x79D5, 0xDDF9,\n\t0x79D8, 0xDDFA, 0x79DF, 0xF0D5, 0x79E4, 0xF6DF, 0x79E6, 0xF2DA,\n\t0x79E7, 0xE4EB, 0x79E9, 0xF2F1, 0x79FB, 0xECB9, 0x7A00, 0xFDFC,\n\t0x7A05, 0xE1AA, 0x7A08, 0xCAD9, 0x7A0B, 0xEFEF, 0x7A0D, 0xF5AA,\n\t0x7A14, 0xECF9, 0x7A17, 0xF8AD, 0x7A19, 0xF2C2, 0x7A1A, 0xF6C3,\n\t0x7A1C, 0xD7D2, 0x7A1F, 0xF9A2, 0x7A20, 0xF0D6, 0x7A2E, 0xF0FA,\n\t0x7A31, 0xF6E0, 0x7A36, 0xE9F3, 0x7A37, 0xF2C3, 0x7A3B, 0xD4AB,\n\t0x7A3C, 0xCAB3, 0x7A3D, 0xCDA6, 0x7A3F, 0xCDC3, 0x7A40, 0xCDDA,\n\t0x7A46, 0xD9CF, 0x7A49, 0xF6C4, 0x7A4D, 0xEEDD, 0x7A4E, 0xE7C4,\n\t0x7A57, 0xE2B4, 0x7A61, 0xDFE2, 0x7A62, 0xE7DB, 0x7A69, 0xE8B1,\n\t0x7A6B, 0xFCAE, 0x7A70, 0xE5CD, 0x7A74, 0xFAEB, 0x7A76, 0xCFBC,\n\t0x7A79, 0xCFE2, 0x7A7A, 0xCDF6, 0x7A7D, 0xEFF0, 0x7A7F, 0xF4BE,\n\t0x7A81, 0xD4CD, 0x7A84, 0xF3B8, 0x7A88, 0xE9A1, 0x7A92, 0xF2F2,\n\t0x7A93, 0xF3EB, 0x7A95, 0xF0D7, 0x7A98, 0xCFD7, 0x7A9F, 0xCFDF,\n\t0x7AA9, 0xE8C0, 0x7AAA, 0xE8C1, 0x7AAE, 0xCFE3, 0x7AAF, 0xE9A2,\n\t0x7ABA, 0xD0AA, 0x7AC4, 0xF3C1, 0x7AC5, 0xD0AB, 0x7AC7, 0xD4E4,\n\t0x7ACA, 0xEFBC, 0x7ACB, 0xD8A1, 0x7AD7, 0xD9DF, 0x7AD9, 0xF3D7,\n\t0x7ADD, 0xDCBD, 0x7ADF, 0xCCE5, 0x7AE0, 0xEDF1, 0x7AE3, 0xF1E2,\n\t0x7AE5, 0xD4DB, 0x7AEA, 0xE2B5, 0x7AED, 0xCAE6, 0x7AEF, 0xD3AE,\n\t0x7AF6, 0xCCE6, 0x7AF9, 0xF1D3, 0x7AFA, 0xF5E7, 0x7AFF, 0xCADA,\n\t0x7B0F, 0xFBEE, 0x7B11, 0xE1C5, 0x7B19, 0xDFE9, 0x7B1B, 0xEEDE,\n\t0x7B1E, 0xF7C2, 0x7B20, 0xD8A2, 0x7B26, 0xDDAC, 0x7B2C, 0xF0AF,\n\t0x7B2D, 0xD6BD, 0x7B39, 0xE1AB, 0x7B46, 0xF9B6, 0x7B49, 0xD4F5,\n\t0x7B4B, 0xD0C9, 0x7B4C, 0xEFA7, 0x7B4D, 0xE2EC, 0x7B4F, 0xDBEA,\n\t0x7B50, 0xCECC, 0x7B51, 0xF5E8, 0x7B52, 0xF7D5, 0x7B54, 0xD3CD,\n\t0x7B56, 0xF3FE, 0x7B60, 0xD0B5, 0x7B6C, 0xE0FE, 0x7B6E, 0xDFFB,\n\t0x7B75, 0xE6DD, 0x7B7D, 0xE8A4, 0x7B87, 0xCBCD, 0x7B8B, 0xEFA8,\n\t0x7B8F, 0xEEB4, 0x7B94, 0xDAD8, 0x7B95, 0xD1B9, 0x7B97, 0xDFA9,\n\t0x7B9A, 0xF3B0, 0x7B9D, 0xCCC4, 0x7BA1, 0xCEB7, 0x7BAD, 0xEFA9,\n\t0x7BB1, 0xDFD5, 0x7BB4, 0xEDD7, 0x7BB8, 0xEEC6, 0x7BC0, 0xEFBD,\n\t0x7BC1, 0xFCD6, 0x7BC4, 0xDBF4, 0x7BC6, 0xEFAA, 0x7BC7, 0xF8B9,\n\t0x7BC9, 0xF5E9, 0x7BD2, 0xE3D9, 0x7BE0, 0xE1C6, 0x7BE4, 0xD4BF,\n\t0x7BE9, 0xDEE8, 0x7C07, 0xF0EA, 0x7C12, 0xF3C2, 0x7C1E, 0xD3AF,\n\t0x7C21, 0xCADB, 0x7C27, 0xFCD7, 0x7C2A, 0xEDD8, 0x7C2B, 0xE1C7,\n\t0x7C3D, 0xF4D8, 0x7C3E, 0xD6B3, 0x7C3F, 0xDDAD, 0x7C43, 0xD5BE,\n\t0x7C4C, 0xF1C3, 0x7C4D, 0xEEDF, 0x7C60, 0xD6EB, 0x7C64, 0xF4D9,\n\t0x7C6C, 0xD7E6, 0x7C73, 0xDAB7, 0x7C83, 0xDDFB, 0x7C89, 0xDDCF,\n\t0x7C92, 0xD8A3, 0x7C95, 0xDAD9, 0x7C97, 0xF0D8, 0x7C98, 0xEFC4,\n\t0x7C9F, 0xE1D8, 0x7CA5, 0xF1D4, 0x7CA7, 0xEDF2, 0x7CAE, 0xD5DB,\n\t0x7CB1, 0xD5DC, 0x7CB2, 0xF3C4, 0x7CB3, 0xCBD7, 0x7CB9, 0xE2B6,\n\t0x7CBE, 0xEFF1, 0x7CCA, 0xFBD5, 0x7CD6, 0xD3D8, 0x7CDE, 0xDDD0,\n\t0x7CDF, 0xF0D9, 0x7CE0, 0xCBB3, 0x7CE7, 0xD5DD, 0x7CFB, 0xCDA7,\n\t0x7CFE, 0xD0AC, 0x7D00, 0xD1BA, 0x7D02, 0xF1C4, 0x7D04, 0xE5B3,\n\t0x7D05, 0xFBF5, 0x7D06, 0xE9E1, 0x7D07, 0xFDE0, 0x7D08, 0xFCBC,\n\t0x7D0A, 0xDAA2, 0x7D0B, 0xDAA3, 0x7D0D, 0xD2A1, 0x7D10, 0xD2EF,\n\t0x7D14, 0xE2ED, 0x7D17, 0xDEE9, 0x7D18, 0xCEDC, 0x7D19, 0xF2B5,\n\t0x7D1A, 0xD0E4, 0x7D1B, 0xDDD1, 0x7D20, 0xE1C8, 0x7D21, 0xDBB7,\n\t0x7D22, 0xDFE3, 0x7D2B, 0xEDB9, 0x7D2C, 0xF1C5, 0x7D2E, 0xF3CF,\n\t0x7D2F, 0xD7AB, 0x7D30, 0xE1AC, 0x7D33, 0xE3EB, 0x7D35, 0xEEC7,\n\t0x7D39, 0xE1C9, 0x7D3A, 0xCAFA, 0x7D42, 0xF0FB, 0x7D43, 0xFAE1,\n\t0x7D44, 0xF0DA, 0x7D45, 0xCCE7, 0x7D46, 0xDAF4, 0x7D50, 0xCCBF,\n\t0x7D5E, 0xCEED, 0x7D61, 0xD5A9, 0x7D62, 0xFAE2, 0x7D66, 0xD0E5,\n\t0x7D68, 0xEBD6, 0x7D6A, 0xECDF, 0x7D6E, 0xDFFC, 0x7D71, 0xF7D6,\n\t0x7D72, 0xDEEA, 0x7D73, 0xCBB4, 0x7D76, 0xEFBE, 0x7D79, 0xCCB5,\n\t0x7D7F, 0xCFBD, 0x7D8E, 0xEFF2, 0x7D8F, 0xE2B7, 0x7D93, 0xCCE8,\n\t0x7D9C, 0xF0FC, 0x7DA0, 0xD6E0, 0x7DA2, 0xF1C6, 0x7DAC, 0xE2B8,\n\t0x7DAD, 0xEBAB, 0x7DB1, 0xCBB5, 0x7DB2, 0xD8D1, 0x7DB4, 0xF4CE,\n\t0x7DB5, 0xF3F7, 0x7DB8, 0xD7C6, 0x7DBA, 0xD1BB, 0x7DBB, 0xF7AA,\n\t0x7DBD, 0xEDCA, 0x7DBE, 0xD7D3, 0x7DBF, 0xD8FA, 0x7DC7, 0xF6C5,\n\t0x7DCA, 0xD1CC, 0x7DCB, 0xDDFC, 0x7DD6, 0xDFFD, 0x7DD8, 0xF9E5,\n\t0x7DDA, 0xE0CA, 0x7DDD, 0xF2FD, 0x7DDE, 0xD3B0, 0x7DE0, 0xF4F3,\n\t0x7DE1, 0xDAC9, 0x7DE3, 0xE6DE, 0x7DE8, 0xF8BA, 0x7DE9, 0xE8D0,\n\t0x7DEC, 0xD8FB, 0x7DEF, 0xEAD5, 0x7DF4, 0xD6A3, 0x7DFB, 0xF6C6,\n\t0x7E09, 0xF2DB, 0x7E0A, 0xE4FC, 0x7E15, 0xE8B2, 0x7E1B, 0xDADA,\n\t0x7E1D, 0xF2DC, 0x7E1E, 0xFBD6, 0x7E1F, 0xE9B2, 0x7E21, 0xEEAD,\n\t0x7E23, 0xFAE3, 0x7E2B, 0xDCEE, 0x7E2E, 0xF5EA, 0x7E2F, 0xE6E0,\n\t0x7E31, 0xF0FD, 0x7E37, 0xD7AC, 0x7E3D, 0xF5C5, 0x7E3E, 0xEEE0,\n\t0x7E41, 0xDBE5, 0x7E43, 0xDDDE, 0x7E46, 0xD9F0, 0x7E47, 0xE9A3,\n\t0x7E52, 0xF1F9, 0x7E54, 0xF2C4, 0x7E55, 0xE0CB, 0x7E5E, 0xE9A4,\n\t0x7E61, 0xE2B9, 0x7E69, 0xE3B1, 0x7E6A, 0xFCEB, 0x7E6B, 0xCDA8,\n\t0x7E6D, 0xCCB6, 0x7E70, 0xF0DB, 0x7E79, 0xE6BA, 0x7E7C, 0xCDA9,\n\t0x7E82, 0xF3C3, 0x7E8C, 0xE1D9, 0x7E8F, 0xEFAB, 0x7E93, 0xE7C5,\n\t0x7E96, 0xE0E9, 0x7E98, 0xF3C5, 0x7E9B, 0xD4C0, 0x7E9C, 0xD5BF,\n\t0x7F36, 0xDDAE, 0x7F38, 0xF9FC, 0x7F3A, 0xCCC0, 0x7F4C, 0xE5A2,\n\t0x7F50, 0xCEB8, 0x7F54, 0xD8D2, 0x7F55, 0xF9D6, 0x7F6A, 0xF1AA,\n\t0x7F6B, 0xCED1, 0x7F6E, 0xF6C7, 0x7F70, 0xDBEB, 0x7F72, 0xDFFE,\n\t0x7F75, 0xD8E1, 0x7F77, 0xF7F3, 0x7F79, 0xD7E7, 0x7F85, 0xD4FE,\n\t0x7F88, 0xD1BC, 0x7F8A, 0xE5CF, 0x7F8C, 0xCBB6, 0x7F8E, 0xDAB8,\n\t0x7F94, 0xCDC4, 0x7F9A, 0xD6BE, 0x7F9E, 0xE2BA, 0x7FA4, 0xCFD8,\n\t0x7FA8, 0xE0CC, 0x7FA9, 0xEBF9, 0x7FB2, 0xFDFD, 0x7FB8, 0xD7E8,\n\t0x7FB9, 0xCBD8, 0x7FBD, 0xE9E2, 0x7FC1, 0xE8BA, 0x7FC5, 0xE3C7,\n\t0x7FCA, 0xECCD, 0x7FCC, 0xECCE, 0x7FCE, 0xD6BF, 0x7FD2, 0xE3A7,\n\t0x7FD4, 0xDFD6, 0x7FD5, 0xFDE8, 0x7FDF, 0xEEE1, 0x7FE0, 0xF6A8,\n\t0x7FE1, 0xDDFD, 0x7FE9, 0xF8BB, 0x7FEB, 0xE8D1, 0x7FF0, 0xF9D7,\n\t0x7FF9, 0xCEEE, 0x7FFC, 0xECCF, 0x8000, 0xE9A5, 0x8001, 0xD6D5,\n\t0x8003, 0xCDC5, 0x8005, 0xEDBA, 0x8006, 0xD1BD, 0x8009, 0xCFBE,\n\t0x800C, 0xECBB, 0x8010, 0xD2B1, 0x8015, 0xCCE9, 0x8017, 0xD9C4,\n\t0x8018, 0xE9FC, 0x802D, 0xD1BE, 0x8033, 0xECBC, 0x8036, 0xE5AD,\n\t0x803D, 0xF7B0, 0x803F, 0xCCEA, 0x8043, 0xD3C4, 0x8046, 0xD6C0,\n\t0x804A, 0xD6FD, 0x8056, 0xE1A1, 0x8058, 0xDEBD, 0x805A, 0xF6A9,\n\t0x805E, 0xDAA4, 0x806F, 0xD6A4, 0x8070, 0xF5C6, 0x8072, 0xE1A2,\n\t0x8073, 0xE9C6, 0x8077, 0xF2C5, 0x807D, 0xF4E9, 0x807E, 0xD6EC,\n\t0x807F, 0xEBD3, 0x8084, 0xECBD, 0x8085, 0xE2DC, 0x8086, 0xDEEB,\n\t0x8087, 0xF0DC, 0x8089, 0xEBBF, 0x808B, 0xD7CE, 0x808C, 0xD1BF,\n\t0x8096, 0xF5AB, 0x809B, 0xF9FD, 0x809D, 0xCADC, 0x80A1, 0xCDC6,\n\t0x80A2, 0xF2B6, 0x80A5, 0xDDFE, 0x80A9, 0xCCB7, 0x80AA, 0xDBB8,\n\t0x80AF, 0xD0E9, 0x80B1, 0xCEDD, 0x80B2, 0xEBC0, 0x80B4, 0xFDA2,\n\t0x80BA, 0xF8CB, 0x80C3, 0xEAD6, 0x80C4, 0xF1B0, 0x80CC, 0xDBCE,\n\t0x80CE, 0xF7C3, 0x80DA, 0xDBCF, 0x80DB, 0xCBA4, 0x80DE, 0xF8E0,\n\t0x80E1, 0xFBD7, 0x80E4, 0xEBCA, 0x80E5, 0xE0A1, 0x80F1, 0xCECD,\n\t0x80F4, 0xD4DC, 0x80F8, 0xFDD8, 0x80FD, 0xD2F6, 0x8102, 0xF2B7,\n\t0x8105, 0xFAF6, 0x8106, 0xF6AA, 0x8107, 0xFAF7, 0x8108, 0xD8E6,\n\t0x810A, 0xF4B1, 0x8118, 0xE8D2, 0x811A, 0xCAC5, 0x811B, 0xCCEB,\n\t0x8123, 0xE2EE, 0x8129, 0xE2BB, 0x812B, 0xF7AD, 0x812F, 0xF8E1,\n\t0x8139, 0xF3EC, 0x813E, 0xDEA1, 0x814B, 0xE4FD, 0x814E, 0xE3EC,\n\t0x8150, 0xDDAF, 0x8151, 0xDDB0, 0x8154, 0xCBB7, 0x8155, 0xE8D3,\n\t0x8165, 0xE1A3, 0x8166, 0xD2E0, 0x816B, 0xF0FE, 0x8170, 0xE9A6,\n\t0x8171, 0xCBF2, 0x8178, 0xEDF3, 0x8179, 0xDCD9, 0x817A, 0xE0CD,\n\t0x817F, 0xF7DA, 0x8180, 0xDBB9, 0x8188, 0xCCAE, 0x818A, 0xDADB,\n\t0x818F, 0xCDC7, 0x819A, 0xDDB1, 0x819C, 0xD8AF, 0x819D, 0xE3A3,\n\t0x81A0, 0xCEEF, 0x81A3, 0xF2F3, 0x81A8, 0xF8B3, 0x81B3, 0xE0CE,\n\t0x81B5, 0xF5FD, 0x81BA, 0xEBEC, 0x81BD, 0xD3C5, 0x81BE, 0xFCEC,\n\t0x81BF, 0xD2DB, 0x81C0, 0xD4EB, 0x81C2, 0xDEA2, 0x81C6, 0xE5E6,\n\t0x81CD, 0xF0B0, 0x81D8, 0xD5C4, 0x81DF, 0xEDF4, 0x81E3, 0xE3ED,\n\t0x81E5, 0xE8C2, 0x81E7, 0xEDF5, 0x81E8, 0xD7FC, 0x81EA, 0xEDBB,\n\t0x81ED, 0xF6AB, 0x81F3, 0xF2B8, 0x81F4, 0xF6C8, 0x81FA, 0xD3E6,\n\t0x81FB, 0xF2DD, 0x81FC, 0xCFBF, 0x81FE, 0xEBAC, 0x8205, 0xCFC0,\n\t0x8207, 0xE6A8, 0x8208, 0xFDE9, 0x820A, 0xCFC1, 0x820C, 0xE0DF,\n\t0x820D, 0xDEEC, 0x8212, 0xE0A2, 0x821B, 0xF4BF, 0x821C, 0xE2EF,\n\t0x821E, 0xD9F1, 0x821F, 0xF1C7, 0x8221, 0xCBB8, 0x822A, 0xF9FE,\n\t0x822B, 0xDBBA, 0x822C, 0xDAF5, 0x8235, 0xF6EC, 0x8236, 0xDADC,\n\t0x8237, 0xFAE4, 0x8239, 0xE0CF, 0x8240, 0xDDB2, 0x8245, 0xE6A9,\n\t0x8247, 0xEFF3, 0x8259, 0xF3ED, 0x8264, 0xEBFA, 0x8266, 0xF9E6,\n\t0x826E, 0xCADD, 0x826F, 0xD5DE, 0x8271, 0xCADE, 0x8272, 0xDFE4,\n\t0x8276, 0xE6FD, 0x8278, 0xF5AC, 0x827E, 0xE4F5, 0x828B, 0xE9E3,\n\t0x828D, 0xEDCB, 0x828E, 0xCFE4, 0x8292, 0xD8D3, 0x8299, 0xDDB3,\n\t0x829A, 0xD4EC, 0x829D, 0xF2B9, 0x829F, 0xDFB7, 0x82A5, 0xCBCE,\n\t0x82A6, 0xFBD8, 0x82A9, 0xD0D9, 0x82AC, 0xDDD2, 0x82AD, 0xF7F4,\n\t0x82AE, 0xE7DC, 0x82AF, 0xE4A5, 0x82B1, 0xFCA3, 0x82B3, 0xDBBB,\n\t0x82B7, 0xF2BA, 0x82B8, 0xE9FD, 0x82B9, 0xD0CA, 0x82BB, 0xF5D6,\n\t0x82BC, 0xD9C5, 0x82BD, 0xE4B4, 0x82BF, 0xEDA7, 0x82D1, 0xEABD,\n\t0x82D2, 0xE6FE, 0x82D4, 0xF7C4, 0x82D5, 0xF5AD, 0x82D7, 0xD9E0,\n\t0x82DB, 0xCAB4, 0x82DE, 0xF8E2, 0x82DF, 0xCFC2, 0x82E1, 0xECBE,\n\t0x82E5, 0xE5B4, 0x82E6, 0xCDC8, 0x82E7, 0xEEC8, 0x82F1, 0xE7C8,\n\t0x82FD, 0xCDC9, 0x82FE, 0xF9B7, 0x8301, 0xF1E8, 0x8302, 0xD9F2,\n\t0x8303, 0xDBF5, 0x8304, 0xCAB5, 0x8305, 0xD9C6, 0x8309, 0xD8C9,\n\t0x8317, 0xD9AB, 0x8328, 0xEDBC, 0x832B, 0xD8D4, 0x832F, 0xDCDA,\n\t0x8331, 0xE2BC, 0x8334, 0xFCED, 0x8335, 0xECE0, 0x8336, 0xD2FE,\n\t0x8338, 0xE9C7, 0x8339, 0xE6AA, 0x8340, 0xE2F0, 0x8347, 0xFABB,\n\t0x8349, 0xF5AE, 0x834A, 0xFBAA, 0x834F, 0xECFB, 0x8351, 0xECBF,\n\t0x8352, 0xFCD8, 0x8373, 0xD4E5, 0x8377, 0xF9C3, 0x837B, 0xEEE2,\n\t0x8389, 0xD7E9, 0x838A, 0xEDF6, 0x838E, 0xDEED, 0x8396, 0xCCEC,\n\t0x8398, 0xE3EE, 0x839E, 0xE8D4, 0x83A2, 0xFAF8, 0x83A9, 0xDDB4,\n\t0x83AA, 0xE4B5, 0x83AB, 0xD8B0, 0x83BD, 0xD8D5, 0x83C1, 0xF4EA,\n\t0x83C5, 0xCEB9, 0x83C9, 0xD6E1, 0x83CA, 0xCFD2, 0x83CC, 0xD0B6,\n\t0x83D3, 0xCEA2, 0x83D6, 0xF3EE, 0x83DC, 0xF3F8, 0x83E9, 0xDCCC,\n\t0x83EB, 0xD0CB, 0x83EF, 0xFCA4, 0x83F0, 0xCDCA, 0x83F1, 0xD7D4,\n\t0x83F2, 0xDEA3, 0x83F4, 0xE4E0, 0x83F9, 0xEEC9, 0x83FD, 0xE2DD,\n\t0x8403, 0xF5FE, 0x8404, 0xD4AC, 0x840A, 0xD5D1, 0x840C, 0xD8F0,\n\t0x840D, 0xF8C3, 0x840E, 0xEAD7, 0x8429, 0xF5D7, 0x842C, 0xD8BF,\n\t0x8431, 0xFDC0, 0x8438, 0xEBAD, 0x843D, 0xD5AA, 0x8449, 0xE7A8,\n\t0x8457, 0xEECA, 0x845B, 0xCAE7, 0x8461, 0xF8E3, 0x8463, 0xD4DD,\n\t0x8466, 0xEAD8, 0x846B, 0xFBD9, 0x846C, 0xEDF7, 0x846F, 0xE5B5,\n\t0x8475, 0xD0AD, 0x847A, 0xF1F1, 0x8490, 0xE2BD, 0x8494, 0xE3C8,\n\t0x8499, 0xD9D5, 0x849C, 0xDFAA, 0x84A1, 0xDBBC, 0x84B2, 0xF8E4,\n\t0x84B8, 0xF1FA, 0x84BB, 0xE5B6, 0x84BC, 0xF3EF, 0x84BF, 0xFBDA,\n\t0x84C0, 0xE1E0, 0x84C2, 0xD9AC, 0x84C4, 0xF5EB, 0x84C6, 0xE0B6,\n\t0x84C9, 0xE9C8, 0x84CB, 0xCBCF, 0x84CD, 0xE3C9, 0x84D1, 0xDEEE,\n\t0x84DA, 0xE2BE, 0x84EC, 0xDCEF, 0x84EE, 0xD6A5, 0x84F4, 0xE2F1,\n\t0x84FC, 0xD6FE, 0x8511, 0xD9A1, 0x8513, 0xD8C0, 0x8514, 0xDCDB,\n\t0x8517, 0xEDBD, 0x8518, 0xDFB8, 0x851A, 0xEAA5, 0x851E, 0xD7AD,\n\t0x8521, 0xF3F9, 0x8523, 0xEDF8, 0x8525, 0xF5C7, 0x852C, 0xE1CA,\n\t0x852D, 0xEBE3, 0x852F, 0xF2DE, 0x853D, 0xF8CC, 0x853F, 0xEAD9,\n\t0x8541, 0xD3C6, 0x8543, 0xDBE6, 0x8549, 0xF5AF, 0x854E, 0xCEF0,\n\t0x8553, 0xE9FE, 0x8559, 0xFBB6, 0x8563, 0xE2F2, 0x8568, 0xCFF2,\n\t0x8569, 0xF7B9, 0x856A, 0xD9F3, 0x856D, 0xE1CB, 0x8584, 0xDADD,\n\t0x8587, 0xDAB9, 0x858F, 0xEBFB, 0x8591, 0xCBB9, 0x8594, 0xEDF9,\n\t0x859B, 0xE0E0, 0x85A6, 0xF4C0, 0x85A8, 0xFDBC, 0x85A9, 0xDFB1,\n\t0x85AA, 0xE3EF, 0x85AF, 0xE0A3, 0x85B0, 0xFDB9, 0x85BA, 0xF0B1,\n\t0x85C1, 0xCDCB, 0x85C9, 0xEDBE, 0x85CD, 0xD5C0, 0x85CE, 0xE3F0,\n\t0x85CF, 0xEDFA, 0x85D5, 0xE9E4, 0x85DC, 0xD5ED, 0x85DD, 0xE7DD,\n\t0x85E4, 0xD4F6, 0x85E5, 0xE5B7, 0x85E9, 0xDBE7, 0x85EA, 0xE2BF,\n\t0x85F7, 0xEECB, 0x85FA, 0xD7F4, 0x85FB, 0xF0DD, 0x85FF, 0xCEAB,\n\t0x8602, 0xE7DE, 0x8606, 0xD6D6, 0x8607, 0xE1CC, 0x860A, 0xE8B3,\n\t0x8616, 0xE5EE, 0x8617, 0xDCA2, 0x861A, 0xE0D0, 0x862D, 0xD5B5,\n\t0x863F, 0xD5A1, 0x864E, 0xFBDB, 0x8650, 0xF9CB, 0x8654, 0xCBF3,\n\t0x8655, 0xF4A5, 0x865B, 0xFAC8, 0x865C, 0xD6D7, 0x865E, 0xE9E5,\n\t0x865F, 0xFBDC, 0x8667, 0xFDD0, 0x8679, 0xFBF6, 0x868A, 0xDAA5,\n\t0x868C, 0xDBBD, 0x8693, 0xECE2, 0x86A3, 0xCDF7, 0x86A4, 0xF0DE,\n\t0x86A9, 0xF6C9, 0x86C7, 0xDEEF, 0x86CB, 0xD3B1, 0x86D4, 0xFCEE,\n\t0x86D9, 0xE8C3, 0x86DB, 0xF1C8, 0x86DF, 0xCEF1, 0x86E4, 0xF9ED,\n\t0x86ED, 0xF2F4, 0x86FE, 0xE4B6, 0x8700, 0xF5B9, 0x8702, 0xDCF0,\n\t0x8703, 0xE3F1, 0x8708, 0xE8A5, 0x8718, 0xF2BB, 0x871A, 0xDEA4,\n\t0x871C, 0xDACC, 0x874E, 0xCAE9, 0x8755, 0xE3DA, 0x8757, 0xFCD9,\n\t0x875F, 0xEADA, 0x8766, 0xF9C4, 0x8768, 0xE3A4, 0x8774, 0xFBDD,\n\t0x8776, 0xEFCA, 0x8778, 0xE8C4, 0x8782, 0xD5CC, 0x878D, 0xEBD7,\n\t0x879F, 0xD9AD, 0x87A2, 0xFBAB, 0x87B3, 0xD3D9, 0x87BA, 0xD5A2,\n\t0x87C4, 0xF6DE, 0x87E0, 0xDAF6, 0x87EC, 0xE0D1, 0x87EF, 0xE9A8,\n\t0x87F2, 0xF5F9, 0x87F9, 0xFAAF, 0x87FB, 0xEBFC, 0x87FE, 0xE0EA,\n\t0x8805, 0xE3B2, 0x881F, 0xD5C5, 0x8822, 0xF1E3, 0x8823, 0xD5EE,\n\t0x8831, 0xCDCC, 0x8836, 0xEDD9, 0x883B, 0xD8C1, 0x8840, 0xFAEC,\n\t0x8846, 0xF1EB, 0x884C, 0xFABC, 0x884D, 0xE6E2, 0x8852, 0xFAE5,\n\t0x8853, 0xE2FA, 0x8857, 0xCAB6, 0x8859, 0xE4B7, 0x885B, 0xEADB,\n\t0x885D, 0xF5FA, 0x8861, 0xFBAC, 0x8862, 0xCFC3, 0x8863, 0xEBFD,\n\t0x8868, 0xF8FA, 0x886B, 0xDFB9, 0x8870, 0xE1F1, 0x8872, 0xD2A4,\n\t0x8877, 0xF5FB, 0x887E, 0xD0DA, 0x887F, 0xD0DB, 0x8881, 0xEABE,\n\t0x8882, 0xD9B1, 0x8888, 0xCAB7, 0x888B, 0xD3E7, 0x888D, 0xF8E5,\n\t0x8892, 0xD3B2, 0x8896, 0xE2C0, 0x8897, 0xF2DF, 0x889E, 0xCDE5,\n\t0x88AB, 0xF9AC, 0x88B4, 0xCDCD, 0x88C1, 0xEEAE, 0x88C2, 0xD6AE,\n\t0x88CF, 0xD7EA, 0x88D4, 0xE7E0, 0x88D5, 0xEBAE, 0x88D9, 0xCFD9,\n\t0x88DC, 0xDCCD, 0x88DD, 0xEDFB, 0x88DF, 0xDEF0, 0x88E1, 0xD7EB,\n\t0x88E8, 0xDEA5, 0x88F3, 0xDFD7, 0x88F4, 0xDBD0, 0x88F5, 0xDBD1,\n\t0x88F8, 0xD5A3, 0x88FD, 0xF0B2, 0x8907, 0xDCDC, 0x8910, 0xCAE8,\n\t0x8912, 0xF8E6, 0x8913, 0xDCCE, 0x8918, 0xEADC, 0x8919, 0xDBD2,\n\t0x8925, 0xE9B3, 0x892A, 0xF7DB, 0x8936, 0xE3A8, 0x8938, 0xD7AE,\n\t0x893B, 0xE0E1, 0x8941, 0xCBBA, 0x8944, 0xE5D1, 0x895F, 0xD0DC,\n\t0x8964, 0xD5C1, 0x896A, 0xD8CA, 0x8972, 0xE3A9, 0x897F, 0xE0A4,\n\t0x8981, 0xE9A9, 0x8983, 0xD3C7, 0x8986, 0xDCDD, 0x8987, 0xF8AE,\n\t0x898B, 0xCCB8, 0x898F, 0xD0AE, 0x8993, 0xD8F2, 0x8996, 0xE3CA,\n\t0x89A1, 0xCCAF, 0x89A9, 0xD4AD, 0x89AA, 0xF6D1, 0x89B2, 0xD0CC,\n\t0x89BA, 0xCAC6, 0x89BD, 0xD5C2, 0x89C0, 0xCEBA, 0x89D2, 0xCAC7,\n\t0x89E3, 0xFAB0, 0x89F4, 0xDFD8, 0x89F8, 0xF5BA, 0x8A00, 0xE5EB,\n\t0x8A02, 0xEFF4, 0x8A03, 0xDDB5, 0x8A08, 0xCDAA, 0x8A0A, 0xE3F2,\n\t0x8A0C, 0xFBF7, 0x8A0E, 0xF7D0, 0x8A13, 0xFDBA, 0x8A16, 0xFDE1,\n\t0x8A17, 0xF6FE, 0x8A18, 0xD1C0, 0x8A1B, 0xE8C5, 0x8A1D, 0xE4B8,\n\t0x8A1F, 0xE1E8, 0x8A23, 0xCCC1, 0x8A25, 0xD2ED, 0x8A2A, 0xDBBE,\n\t0x8A2D, 0xE0E2, 0x8A31, 0xFAC9, 0x8A34, 0xE1CD, 0x8A36, 0xCAB8,\n\t0x8A3A, 0xF2E0, 0x8A3B, 0xF1C9, 0x8A50, 0xDEF1, 0x8A54, 0xF0DF,\n\t0x8A55, 0xF8C4, 0x8A5B, 0xEECC, 0x8A5E, 0xDEF2, 0x8A60, 0xE7C9,\n\t0x8A62, 0xE2F3, 0x8A63, 0xE7E1, 0x8A66, 0xE3CB, 0x8A69, 0xE3CC,\n\t0x8A6D, 0xCFF8, 0x8A6E, 0xEFAC, 0x8A70, 0xFDFE, 0x8A71, 0xFCA5,\n\t0x8A72, 0xFAB1, 0x8A73, 0xDFD9, 0x8A75, 0xE0D2, 0x8A79, 0xF4DA,\n\t0x8A85, 0xF1CA, 0x8A87, 0xCEA3, 0x8A8C, 0xF2BC, 0x8A8D, 0xECE3,\n\t0x8A93, 0xE0A5, 0x8A95, 0xF7AB, 0x8A98, 0xEBAF, 0x8A9E, 0xE5DE,\n\t0x8AA0, 0xE1A4, 0x8AA1, 0xCDAB, 0x8AA3, 0xD9F4, 0x8AA4, 0xE8A6,\n\t0x8AA5, 0xCDCE, 0x8AA6, 0xE1E9, 0x8AA8, 0xFCEF, 0x8AAA, 0xE0E3,\n\t0x8AB0, 0xE2C1, 0x8AB2, 0xCEA4, 0x8AB9, 0xDEA6, 0x8ABC, 0xEBFE,\n\t0x8ABE, 0xEBDD, 0x8ABF, 0xF0E0, 0x8AC2, 0xF4DB, 0x8AC4, 0xE2F4,\n\t0x8AC7, 0xD3C8, 0x8ACB, 0xF4EB, 0x8ACD, 0xEEB5, 0x8ACF, 0xF5D8,\n\t0x8AD2, 0xD5DF, 0x8AD6, 0xD6E5, 0x8ADB, 0xEBB0, 0x8ADC, 0xF4E3,\n\t0x8AE1, 0xE3CD, 0x8AE6, 0xF4F4, 0x8AE7, 0xFAB2, 0x8AEA, 0xEFF5,\n\t0x8AEB, 0xCADF, 0x8AED, 0xEBB1, 0x8AEE, 0xEDBF, 0x8AF1, 0xFDC9,\n\t0x8AF6, 0xE4A6, 0x8AF7, 0xF9A4, 0x8AF8, 0xF0B3, 0x8AFA, 0xE5EC,\n\t0x8AFE, 0xD1E7, 0x8B00, 0xD9C7, 0x8B01, 0xE4D7, 0x8B02, 0xEADD,\n\t0x8B04, 0xD4F7, 0x8B0E, 0xDABA, 0x8B10, 0xDACD, 0x8B14, 0xF9CC,\n\t0x8B16, 0xE1DA, 0x8B17, 0xDBBF, 0x8B19, 0xCCC5, 0x8B1A, 0xECD0,\n\t0x8B1B, 0xCBBB, 0x8B1D, 0xDEF3, 0x8B20, 0xE9AA, 0x8B28, 0xD9C8,\n\t0x8B2B, 0xEEE3, 0x8B2C, 0xD7BD, 0x8B33, 0xCFC4, 0x8B39, 0xD0CD,\n\t0x8B41, 0xFCA6, 0x8B49, 0xF1FB, 0x8B4E, 0xFDD2, 0x8B4F, 0xD1C1,\n\t0x8B58, 0xE3DB, 0x8B5A, 0xD3C9, 0x8B5C, 0xDCCF, 0x8B66, 0xCCED,\n\t0x8B6C, 0xDEA7, 0x8B6F, 0xE6BB, 0x8B70, 0xECA1, 0x8B74, 0xCCB9,\n\t0x8B77, 0xFBDE, 0x8B7D, 0xE7E2, 0x8B80, 0xD4C1, 0x8B8A, 0xDCA8,\n\t0x8B90, 0xE2C2, 0x8B92, 0xF3D8, 0x8B93, 0xE5D3, 0x8B96, 0xF3D9,\n\t0x8B9A, 0xF3C6, 0x8C37, 0xCDDB, 0x8C3F, 0xCDAC, 0x8C41, 0xFCC3,\n\t0x8C46, 0xD4E7, 0x8C48, 0xD1C2, 0x8C4A, 0xF9A5, 0x8C4C, 0xE8D5,\n\t0x8C55, 0xE3CE, 0x8C5A, 0xD4CA, 0x8C61, 0xDFDA, 0x8C6A, 0xFBDF,\n\t0x8C6B, 0xE7E3, 0x8C79, 0xF8FB, 0x8C7A, 0xE3CF, 0x8C82, 0xF5B0,\n\t0x8C8A, 0xD8E7, 0x8C8C, 0xD9C9, 0x8C9D, 0xF8AF, 0x8C9E, 0xEFF6,\n\t0x8CA0, 0xDDB6, 0x8CA1, 0xEEAF, 0x8CA2, 0xCDF8, 0x8CA7, 0xDEB8,\n\t0x8CA8, 0xFCA7, 0x8CA9, 0xF7FC, 0x8CAA, 0xF7B1, 0x8CAB, 0xCEBB,\n\t0x8CAC, 0xF4A1, 0x8CAF, 0xEECD, 0x8CB0, 0xE1AE, 0x8CB3, 0xECC3,\n\t0x8CB4, 0xCFFE, 0x8CB6, 0xF8BF, 0x8CB7, 0xD8E2, 0x8CB8, 0xD3E8,\n\t0x8CBB, 0xDEA8, 0x8CBC, 0xF4E4, 0x8CBD, 0xECC2, 0x8CBF, 0xD9F5,\n\t0x8CC0, 0xF9C5, 0x8CC1, 0xDDD3, 0x8CC2, 0xD6F1, 0x8CC3, 0xECFC,\n\t0x8CC4, 0xFCF0, 0x8CC7, 0xEDC0, 0x8CC8, 0xCAB9, 0x8CCA, 0xEEE4,\n\t0x8CD1, 0xF2E1, 0x8CD3, 0xDEB9, 0x8CDA, 0xD6F2, 0x8CDC, 0xDEF4,\n\t0x8CDE, 0xDFDB, 0x8CE0, 0xDBD3, 0x8CE2, 0xFAE7, 0x8CE3, 0xD8E3,\n\t0x8CE4, 0xF4C1, 0x8CE6, 0xDDB7, 0x8CEA, 0xF2F5, 0x8CED, 0xD4AE,\n\t0x8CF4, 0xD6F3, 0x8CFB, 0xDDB8, 0x8CFC, 0xCFC5, 0x8CFD, 0xDFDF,\n\t0x8D04, 0xF2BE, 0x8D05, 0xF6A1, 0x8D07, 0xEBCB, 0x8D08, 0xF1FC,\n\t0x8D0A, 0xF3C7, 0x8D0D, 0xE0EB, 0x8D13, 0xEDFC, 0x8D16, 0xE1DB,\n\t0x8D64, 0xEEE5, 0x8D66, 0xDEF5, 0x8D6B, 0xFAD3, 0x8D70, 0xF1CB,\n\t0x8D73, 0xD0AF, 0x8D74, 0xDDB9, 0x8D77, 0xD1C3, 0x8D85, 0xF5B1,\n\t0x8D8A, 0xEAC6, 0x8D99, 0xF0E1, 0x8DA3, 0xF6AC, 0x8DA8, 0xF5D9,\n\t0x8DB3, 0xF0EB, 0x8DBA, 0xDDBA, 0x8DBE, 0xF2BF, 0x8DC6, 0xF7C5,\n\t0x8DCB, 0xDBA2, 0x8DCC, 0xF2F6, 0x8DCF, 0xCABA, 0x8DDB, 0xF7F5,\n\t0x8DDD, 0xCBE5, 0x8DE1, 0xEEE6, 0x8DE3, 0xE0D3, 0x8DE8, 0xCEA5,\n\t0x8DEF, 0xD6D8, 0x8DF3, 0xD4AF, 0x8E0A, 0xE9C9, 0x8E0F, 0xD3CE,\n\t0x8E10, 0xF4C2, 0x8E1E, 0xCBE6, 0x8E2A, 0xF1A1, 0x8E30, 0xEBB2,\n\t0x8E35, 0xF1A2, 0x8E42, 0xEBB3, 0x8E44, 0xF0B4, 0x8E47, 0xCBF4,\n\t0x8E48, 0xD4B0, 0x8E49, 0xF3B2, 0x8E4A, 0xFBB7, 0x8E59, 0xF5EC,\n\t0x8E5F, 0xEEE7, 0x8E60, 0xF4B2, 0x8E74, 0xF5ED, 0x8E76, 0xCFF3,\n\t0x8E81, 0xF0E2, 0x8E87, 0xEECE, 0x8E8A, 0xF1CC, 0x8E8D, 0xE5B8,\n\t0x8EAA, 0xD7F5, 0x8EAB, 0xE3F3, 0x8EAC, 0xCFE5, 0x8EC0, 0xCFC6,\n\t0x8ECA, 0xF3B3, 0x8ECB, 0xE4D8, 0x8ECC, 0xCFF9, 0x8ECD, 0xCFDA,\n\t0x8ED2, 0xFACD, 0x8EDF, 0xE6E3, 0x8EEB, 0xF2E2, 0x8EF8, 0xF5EE,\n\t0x8EFB, 0xCABB, 0x8EFE, 0xE3DC, 0x8F03, 0xCEF2, 0x8F05, 0xD6D9,\n\t0x8F09, 0xEEB0, 0x8F12, 0xF4E5, 0x8F13, 0xD8C2, 0x8F14, 0xDCD0,\n\t0x8F15, 0xCCEE, 0x8F1B, 0xD5E0, 0x8F1C, 0xF6CA, 0x8F1D, 0xFDCA,\n\t0x8F1E, 0xD8D6, 0x8F1F, 0xF4CF, 0x8F26, 0xD6A6, 0x8F27, 0xDCBE,\n\t0x8F29, 0xDBD4, 0x8F2A, 0xD7C7, 0x8F2F, 0xF2FE, 0x8F33, 0xF1CD,\n\t0x8F38, 0xE2C3, 0x8F39, 0xDCDE, 0x8F3B, 0xDCDF, 0x8F3E, 0xEFAD,\n\t0x8F3F, 0xE6AB, 0x8F44, 0xF9DD, 0x8F45, 0xEABF, 0x8F49, 0xEFAE,\n\t0x8F4D, 0xF4D0, 0x8F4E, 0xCEF3, 0x8F5D, 0xE6AC, 0x8F5F, 0xCEDE,\n\t0x8F62, 0xD5F9, 0x8F9B, 0xE3F4, 0x8F9C, 0xCDD0, 0x8FA3, 0xD5B8,\n\t0x8FA6, 0xF7FD, 0x8FA8, 0xDCA9, 0x8FAD, 0xDEF6, 0x8FAF, 0xDCAA,\n\t0x8FB0, 0xF2E3, 0x8FB1, 0xE9B4, 0x8FB2, 0xD2DC, 0x8FC2, 0xE9E6,\n\t0x8FC5, 0xE3F6, 0x8FCE, 0xE7CA, 0x8FD1, 0xD0CE, 0x8FD4, 0xDAF7,\n\t0x8FE6, 0xCABC, 0x8FEA, 0xEEE8, 0x8FEB, 0xDADE, 0x8FED, 0xF2F7,\n\t0x8FF0, 0xE2FB, 0x8FF2, 0xCCA6, 0x8FF7, 0xDABB, 0x8FF9, 0xEEE9,\n\t0x8FFD, 0xF5DA, 0x9000, 0xF7DC, 0x9001, 0xE1EA, 0x9002, 0xCEC1,\n\t0x9003, 0xD4B1, 0x9005, 0xFDB1, 0x9006, 0xE6BD, 0x9008, 0xFBAD,\n\t0x900B, 0xF8E7, 0x900D, 0xE1CE, 0x900F, 0xF7E2, 0x9010, 0xF5EF,\n\t0x9011, 0xCFC7, 0x9014, 0xD4B2, 0x9015, 0xCCEF, 0x9017, 0xD4E8,\n\t0x9019, 0xEECF, 0x901A, 0xF7D7, 0x901D, 0xE0A6, 0x901E, 0xD6C1,\n\t0x901F, 0xE1DC, 0x9020, 0xF0E3, 0x9021, 0xF1E4, 0x9022, 0xDCF1,\n\t0x9023, 0xD6A7, 0x902E, 0xF4F5, 0x9031, 0xF1CE, 0x9032, 0xF2E4,\n\t0x9035, 0xD0B0, 0x9038, 0xECEF, 0x903C, 0xF9BA, 0x903E, 0xEBB5,\n\t0x9041, 0xD4ED, 0x9042, 0xE2C4, 0x9047, 0xE9E7, 0x904A, 0xEBB4,\n\t0x904B, 0xEAA1, 0x904D, 0xF8BC, 0x904E, 0xCEA6, 0x9050, 0xF9C6,\n\t0x9051, 0xFCDA, 0x9053, 0xD4B3, 0x9054, 0xD3B9, 0x9055, 0xEADE,\n\t0x9059, 0xE9AB, 0x905C, 0xE1E1, 0x905D, 0xD3CF, 0x905E, 0xF4F6,\n\t0x9060, 0xEAC0, 0x9061, 0xE1CF, 0x9063, 0xCCBA, 0x9069, 0xEEEA,\n\t0x906D, 0xF0E4, 0x906E, 0xF3B4, 0x906F, 0xD4EE, 0x9072, 0xF2C0,\n\t0x9075, 0xF1E5, 0x9077, 0xF4C3, 0x9078, 0xE0D4, 0x907A, 0xEBB6,\n\t0x907C, 0xD7A1, 0x907D, 0xCBE8, 0x907F, 0xF9AD, 0x9080, 0xE9AD,\n\t0x9081, 0xD8E4, 0x9082, 0xFAB3, 0x9083, 0xE2C5, 0x9084, 0xFCBD,\n\t0x9087, 0xECC4, 0x9088, 0xD8B1, 0x908A, 0xDCAB, 0x908F, 0xD5A4,\n\t0x9091, 0xEBE9, 0x9095, 0xE8BB, 0x9099, 0xD8D7, 0x90A2, 0xFBAE,\n\t0x90A3, 0xD1E1, 0x90A6, 0xDBC0, 0x90A8, 0xF5BE, 0x90AA, 0xDEF7,\n\t0x90AF, 0xCAFB, 0x90B0, 0xF7C6, 0x90B1, 0xCFC8, 0x90B5, 0xE1D0,\n\t0x90B8, 0xEED0, 0x90C1, 0xE9F4, 0x90CA, 0xCEF4, 0x90DE, 0xD5CD,\n\t0x90E1, 0xCFDB, 0x90E8, 0xDDBB, 0x90ED, 0xCEAC, 0x90F5, 0xE9E8,\n\t0x90FD, 0xD4B4, 0x9102, 0xE4C7, 0x9112, 0xF5DB, 0x9115, 0xFAC1,\n\t0x9119, 0xDEA9, 0x9127, 0xD4F8, 0x912D, 0xEFF7, 0x9132, 0xD3B3,\n\t0x9149, 0xEBB7, 0x914A, 0xEFF8, 0x914B, 0xF5DC, 0x914C, 0xEDCC,\n\t0x914D, 0xDBD5, 0x914E, 0xF1CF, 0x9152, 0xF1D0, 0x9162, 0xF5B2,\n\t0x9169, 0xD9AE, 0x916A, 0xD5AC, 0x916C, 0xE2C6, 0x9175, 0xFDA3,\n\t0x9177, 0xFBE5, 0x9178, 0xDFAB, 0x9187, 0xE2F5, 0x9189, 0xF6AD,\n\t0x918B, 0xF5B3, 0x918D, 0xF0B5, 0x9192, 0xE1A5, 0x919C, 0xF5DD,\n\t0x91AB, 0xECA2, 0x91AC, 0xEDFD, 0x91AE, 0xF5B4, 0x91AF, 0xFBB8,\n\t0x91B1, 0xDBA3, 0x91B4, 0xD6CA, 0x91B5, 0xCBD9, 0x91C0, 0xE5D4,\n\t0x91C7, 0xF3FA, 0x91C9, 0xEBB8, 0x91CB, 0xE0B7, 0x91CC, 0xD7EC,\n\t0x91CD, 0xF1EC, 0x91CE, 0xE5AF, 0x91CF, 0xD5E1, 0x91D0, 0xD7ED,\n\t0x91D1, 0xD1D1, 0x91D7, 0xE1F2, 0x91D8, 0xEFF9, 0x91DC, 0xDDBC,\n\t0x91DD, 0xF6DC, 0x91E3, 0xF0E5, 0x91E7, 0xF4C4, 0x91EA, 0xE9E9,\n\t0x91F5, 0xF3FB, 0x920D, 0xD4EF, 0x9210, 0xCCA2, 0x9211, 0xF7FE,\n\t0x9212, 0xDFBC, 0x9217, 0xEBCD, 0x921E, 0xD0B7, 0x9234, 0xD6C2,\n\t0x923A, 0xE8AD, 0x923F, 0xEFAF, 0x9240, 0xCBA5, 0x9245, 0xCBE9,\n\t0x9249, 0xFAE8, 0x9257, 0xCCC6, 0x925B, 0xE6E7, 0x925E, 0xEAC7,\n\t0x9262, 0xDBA4, 0x9264, 0xCFC9, 0x9265, 0xE2FC, 0x9266, 0xEFFA,\n\t0x9280, 0xEBDE, 0x9283, 0xF5C8, 0x9285, 0xD4DE, 0x9291, 0xE0D5,\n\t0x9293, 0xEFB0, 0x9296, 0xE2C7, 0x9298, 0xD9AF, 0x929C, 0xF9E7,\n\t0x92B3, 0xE7E5, 0x92B6, 0xCFCA, 0x92B7, 0xE1D1, 0x92B9, 0xE2C8,\n\t0x92CC, 0xEFFB, 0x92CF, 0xFAF9, 0x92D2, 0xDCF2, 0x92E4, 0xE0A7,\n\t0x92EA, 0xF8E8, 0x92F8, 0xCBEA, 0x92FC, 0xCBBC, 0x9304, 0xD6E2,\n\t0x9310, 0xF5DE, 0x9318, 0xF5DF, 0x931A, 0xEEB6, 0x931E, 0xE2F6,\n\t0x931F, 0xD3CA, 0x9320, 0xEFFC, 0x9321, 0xD1C4, 0x9322, 0xEFB1,\n\t0x9324, 0xD1C5, 0x9326, 0xD0DE, 0x9328, 0xD9E1, 0x932B, 0xE0B8,\n\t0x932E, 0xCDD1, 0x932F, 0xF3B9, 0x9348, 0xE7CC, 0x934A, 0xD6A8,\n\t0x934B, 0xCEA7, 0x934D, 0xD4B5, 0x9354, 0xE4C8, 0x935B, 0xD3B4,\n\t0x936E, 0xEBB9, 0x9375, 0xCBF5, 0x937C, 0xF6DD, 0x937E, 0xF1A3,\n\t0x938C, 0xCCC7, 0x9394, 0xE9CA, 0x9396, 0xE1F0, 0x939A, 0xF5E0,\n\t0x93A3, 0xFBAF, 0x93A7, 0xCBD1, 0x93AC, 0xFBE0, 0x93AD, 0xF2E5,\n\t0x93B0, 0xECF0, 0x93C3, 0xF0EC, 0x93D1, 0xEEEB, 0x93DE, 0xE9CB,\n\t0x93E1, 0xCCF0, 0x93E4, 0xD7AF, 0x93F6, 0xF3A1, 0x9404, 0xFCF5,\n\t0x9418, 0xF1A4, 0x9425, 0xE0D6, 0x942B, 0xEFB2, 0x9435, 0xF4D1,\n\t0x9438, 0xF7A1, 0x9444, 0xF1D1, 0x9451, 0xCAFC, 0x9452, 0xCAFD,\n\t0x945B, 0xCECE, 0x947D, 0xF3C8, 0x947F, 0xF3BA, 0x9577, 0xEDFE,\n\t0x9580, 0xDAA6, 0x9583, 0xE0EC, 0x9589, 0xF8CD, 0x958B, 0xCBD2,\n\t0x958F, 0xEBCE, 0x9591, 0xF9D8, 0x9592, 0xF9D9, 0x9593, 0xCAE0,\n\t0x9594, 0xDACA, 0x9598, 0xCBA6, 0x95A3, 0xCAC8, 0x95A4, 0xF9EE,\n\t0x95A5, 0xDBEC, 0x95A8, 0xD0B1, 0x95AD, 0xD5EF, 0x95B1, 0xE6F3,\n\t0x95BB, 0xE7A2, 0x95BC, 0xE4D9, 0x95C7, 0xE4E1, 0x95CA, 0xFCC4,\n\t0x95D4, 0xF9EF, 0x95D5, 0xCFF4, 0x95D6, 0xF7E6, 0x95DC, 0xCEBC,\n\t0x95E1, 0xF4C5, 0x95E2, 0xDCA3, 0x961C, 0xDDBD, 0x9621, 0xF4C6,\n\t0x962A, 0xF8A1, 0x962E, 0xE8D6, 0x9632, 0xDBC1, 0x963B, 0xF0E6,\n\t0x963F, 0xE4B9, 0x9640, 0xF6ED, 0x9642, 0xF9AE, 0x9644, 0xDDBE,\n\t0x964B, 0xD7B0, 0x964C, 0xD8E8, 0x964D, 0xCBBD, 0x9650, 0xF9DA,\n\t0x965B, 0xF8CE, 0x965C, 0xF9F0, 0x965D, 0xE0ED, 0x965E, 0xE3B3,\n\t0x965F, 0xF4B3, 0x9662, 0xEAC2, 0x9663, 0xF2E6, 0x9664, 0xF0B6,\n\t0x966A, 0xDBD6, 0x9670, 0xEBE4, 0x9673, 0xF2E7, 0x9675, 0xD7D5,\n\t0x9676, 0xD4B6, 0x9677, 0xF9E8, 0x9678, 0xD7C1, 0x967D, 0xE5D5,\n\t0x9685, 0xE9EA, 0x9686, 0xD7CC, 0x968A, 0xD3E9, 0x968B, 0xE2C9,\n\t0x968D, 0xFCDB, 0x968E, 0xCDAD, 0x9694, 0xCCB0, 0x9695, 0xEAA2,\n\t0x9698, 0xE4F6, 0x9699, 0xD0C0, 0x969B, 0xF0B7, 0x969C, 0xEEA1,\n\t0x96A3, 0xD7F6, 0x96A7, 0xE2CA, 0x96A8, 0xE2CB, 0x96AA, 0xFACF,\n\t0x96B1, 0xEBDF, 0x96B7, 0xD6CB, 0x96BB, 0xF4B4, 0x96C0, 0xEDCD,\n\t0x96C1, 0xE4D2, 0x96C4, 0xEAA9, 0x96C5, 0xE4BA, 0x96C6, 0xF3A2,\n\t0x96C7, 0xCDD2, 0x96C9, 0xF6CB, 0x96CB, 0xF1E6, 0x96CC, 0xEDC1,\n\t0x96CD, 0xE8BC, 0x96CE, 0xEED1, 0x96D5, 0xF0E7, 0x96D6, 0xE2CC,\n\t0x96D9, 0xE4AA, 0x96DB, 0xF5E1, 0x96DC, 0xEDDA, 0x96E2, 0xD7EE,\n\t0x96E3, 0xD1F1, 0x96E8, 0xE9EB, 0x96E9, 0xE9EC, 0x96EA, 0xE0E4,\n\t0x96EF, 0xDAA7, 0x96F0, 0xDDD4, 0x96F2, 0xEAA3, 0x96F6, 0xD6C3,\n\t0x96F7, 0xD6F4, 0x96F9, 0xDADF, 0x96FB, 0xEFB3, 0x9700, 0xE2CD,\n\t0x9706, 0xEFFD, 0x9707, 0xF2E8, 0x9711, 0xEFC5, 0x9713, 0xE7E7,\n\t0x9716, 0xD7FD, 0x9719, 0xE7CE, 0x971C, 0xDFDC, 0x971E, 0xF9C7,\n\t0x9727, 0xD9F6, 0x9730, 0xDFAC, 0x9732, 0xD6DA, 0x9739, 0xDCA4,\n\t0x973D, 0xF0B8, 0x9742, 0xD5FA, 0x9744, 0xE4F7, 0x9748, 0xD6C4,\n\t0x9751, 0xF4EC, 0x9756, 0xEFFE, 0x975C, 0xF0A1, 0x975E, 0xDEAA,\n\t0x9761, 0xDABC, 0x9762, 0xD8FC, 0x9769, 0xFAD4, 0x976D, 0xECE5,\n\t0x9774, 0xFCA8, 0x9777, 0xECE6, 0x977A, 0xD8CB, 0x978B, 0xFBB9,\n\t0x978D, 0xE4D3, 0x978F, 0xCDF9, 0x97A0, 0xCFD3, 0x97A8, 0xCAEA,\n\t0x97AB, 0xCFD4, 0x97AD, 0xF8BD, 0x97C6, 0xF4C7, 0x97CB, 0xEADF,\n\t0x97D3, 0xF9DB, 0x97DC, 0xD4B7, 0x97F3, 0xEBE5, 0x97F6, 0xE1D2,\n\t0x97FB, 0xEAA4, 0x97FF, 0xFAC2, 0x9800, 0xFBE1, 0x9801, 0xFAED,\n\t0x9802, 0xF0A2, 0x9803, 0xCCF1, 0x9805, 0xFAA3, 0x9806, 0xE2F7,\n\t0x9808, 0xE2CE, 0x980A, 0xE9F5, 0x980C, 0xE1EB, 0x9810, 0xE7E8,\n\t0x9811, 0xE8D7, 0x9812, 0xDAF8, 0x9813, 0xD4CB, 0x9817, 0xF7F6,\n\t0x9818, 0xD6C5, 0x982D, 0xD4E9, 0x9830, 0xFAFA, 0x9838, 0xCCF2,\n\t0x9839, 0xF7DD, 0x983B, 0xDEBA, 0x9846, 0xCEA8, 0x984C, 0xF0B9,\n\t0x984D, 0xE4FE, 0x984E, 0xE4C9, 0x9854, 0xE4D4, 0x9858, 0xEAC3,\n\t0x985A, 0xEFB4, 0x985E, 0xD7BE, 0x9865, 0xFBE2, 0x9867, 0xCDD3,\n\t0x986B, 0xEFB5, 0x986F, 0xFAE9, 0x98A8, 0xF9A6, 0x98AF, 0xDFBD,\n\t0x98B1, 0xF7C7, 0x98C4, 0xF8FD, 0x98C7, 0xF8FC, 0x98DB, 0xDEAB,\n\t0x98DC, 0xDBE8, 0x98DF, 0xE3DD, 0x98E1, 0xE1E2, 0x98E2, 0xD1C6,\n\t0x98ED, 0xF6D0, 0x98EE, 0xEBE6, 0x98EF, 0xDAF9, 0x98F4, 0xECC7,\n\t0x98FC, 0xDEF8, 0x98FD, 0xF8E9, 0x98FE, 0xE3DE, 0x9903, 0xCEF5,\n\t0x9909, 0xFAC3, 0x990A, 0xE5D7, 0x990C, 0xECC8, 0x9910, 0xF3C9,\n\t0x9913, 0xE4BB, 0x9918, 0xE6AE, 0x991E, 0xEFB6, 0x9920, 0xDCBF,\n\t0x9928, 0xCEBD, 0x9945, 0xD8C3, 0x9949, 0xD0CF, 0x994B, 0xCFFA,\n\t0x994C, 0xF3CA, 0x994D, 0xE0D7, 0x9951, 0xD1C7, 0x9952, 0xE9AE,\n\t0x9954, 0xE8BD, 0x9957, 0xFAC4, 0x9996, 0xE2CF, 0x9999, 0xFAC5,\n\t0x999D, 0xF9B8, 0x99A5, 0xDCE0, 0x99A8, 0xFBB0, 0x99AC, 0xD8A9,\n\t0x99AD, 0xE5DF, 0x99AE, 0xF9A7, 0x99B1, 0xF6EE, 0x99B3, 0xF6CC,\n\t0x99B4, 0xE2F8, 0x99B9, 0xECF1, 0x99C1, 0xDAE0, 0x99D0, 0xF1D2,\n\t0x99D1, 0xD2CC, 0x99D2, 0xCFCB, 0x99D5, 0xCABD, 0x99D9, 0xDDBF,\n\t0x99DD, 0xF6EF, 0x99DF, 0xDEF9, 0x99ED, 0xFAB4, 0x99F1, 0xD5AD,\n\t0x99FF, 0xF1E7, 0x9A01, 0xDEBE, 0x9A08, 0xDCC0, 0x9A0E, 0xD1C8,\n\t0x9A0F, 0xD1C9, 0x9A19, 0xF8BE, 0x9A2B, 0xCBF6, 0x9A30, 0xD4F9,\n\t0x9A36, 0xF5E2, 0x9A37, 0xE1D3, 0x9A40, 0xD8E9, 0x9A43, 0xF8FE,\n\t0x9A45, 0xCFCC, 0x9A4D, 0xFDA4, 0x9A55, 0xCEF6, 0x9A57, 0xFAD0,\n\t0x9A5A, 0xCCF3, 0x9A5B, 0xE6BE, 0x9A5F, 0xF6AE, 0x9A62, 0xD5F0,\n\t0x9A65, 0xD1CA, 0x9A69, 0xFCBE, 0x9A6A, 0xD5F1, 0x9AA8, 0xCDE9,\n\t0x9AB8, 0xFAB5, 0x9AD3, 0xE2D0, 0x9AD4, 0xF4F7, 0x9AD8, 0xCDD4,\n\t0x9AE5, 0xE7A3, 0x9AEE, 0xDBA5, 0x9B1A, 0xE2D1, 0x9B27, 0xD7A2,\n\t0x9B2A, 0xF7E3, 0x9B31, 0xEAA6, 0x9B3C, 0xD0A1, 0x9B41, 0xCEDA,\n\t0x9B42, 0xFBEB, 0x9B43, 0xDBA6, 0x9B44, 0xDBDE, 0x9B45, 0xD8E5,\n\t0x9B4F, 0xEAE0, 0x9B54, 0xD8AA, 0x9B5A, 0xE5E0, 0x9B6F, 0xD6DB,\n\t0x9B8E, 0xEFC6, 0x9B91, 0xF8EA, 0x9B9F, 0xE4D5, 0x9BAB, 0xCEF7,\n\t0x9BAE, 0xE0D8, 0x9BC9, 0xD7EF, 0x9BD6, 0xF4ED, 0x9BE4, 0xCDE6,\n\t0x9BE8, 0xCCF4, 0x9C0D, 0xF5E3, 0x9C10, 0xE4CA, 0x9C12, 0xDCE1,\n\t0x9C15, 0xF9C8, 0x9C25, 0xFCBF, 0x9C32, 0xE8A7, 0x9C3B, 0xD8C4,\n\t0x9C47, 0xCBBE, 0x9C49, 0xDCAE, 0x9C57, 0xD7F7, 0x9CE5, 0xF0E8,\n\t0x9CE7, 0xDDC0, 0x9CE9, 0xCFCD, 0x9CF3, 0xDCF3, 0x9CF4, 0xD9B0,\n\t0x9CF6, 0xE6E9, 0x9D09, 0xE4BC, 0x9D1B, 0xEAC4, 0x9D26, 0xE4EC,\n\t0x9D28, 0xE4E5, 0x9D3B, 0xFBF8, 0x9D51, 0xCCBB, 0x9D5D, 0xE4BD,\n\t0x9D60, 0xCDDC, 0x9D61, 0xD9F7, 0x9D6C, 0xDDDF, 0x9D72, 0xEDCE,\n\t0x9DA9, 0xD9D0, 0x9DAF, 0xE5A3, 0x9DB4, 0xF9CD, 0x9DC4, 0xCDAE,\n\t0x9DD7, 0xCFCE, 0x9DF2, 0xF6AF, 0x9DF8, 0xFDD3, 0x9DF9, 0xEBED,\n\t0x9DFA, 0xD6DC, 0x9E1A, 0xE5A4, 0x9E1E, 0xD5B6, 0x9E75, 0xD6DD,\n\t0x9E79, 0xF9E9, 0x9E7D, 0xE7A4, 0x9E7F, 0xD6E3, 0x9E92, 0xD1CB,\n\t0x9E93, 0xD6E4, 0x9E97, 0xD5F2, 0x9E9D, 0xDEFA, 0x9E9F, 0xD7F8,\n\t0x9EA5, 0xD8EA, 0x9EB4, 0xCFD5, 0x9EB5, 0xD8FD, 0x9EBB, 0xD8AB,\n\t0x9EBE, 0xFDCB, 0x9EC3, 0xFCDC, 0x9ECD, 0xE0A8, 0x9ECE, 0xD5F3,\n\t0x9ED1, 0xFDD9, 0x9ED4, 0xCCA3, 0x9ED8, 0xD9F9, 0x9EDB, 0xD3EA,\n\t0x9EDC, 0xF5F5, 0x9EDE, 0xEFC7, 0x9EE8, 0xD3DA, 0x9EF4, 0xDABD,\n\t0x9F07, 0xE8A8, 0x9F08, 0xDCAF, 0x9F0E, 0xF0A3, 0x9F13, 0xCDD5,\n\t0x9F20, 0xE0A9, 0x9F3B, 0xDEAC, 0x9F4A, 0xF0BA, 0x9F4B, 0xEEB1,\n\t0x9F4E, 0xEEB2, 0x9F52, 0xF6CD, 0x9F5F, 0xEED2, 0x9F61, 0xD6C6,\n\t0x9F67, 0xE0E5, 0x9F6A, 0xF3BB, 0x9F6C, 0xE5E1, 0x9F77, 0xE4CB,\n\t0x9F8D, 0xD7A3, 0x9F90, 0xDBC2, 0x9F95, 0xCAFE, 0x9F9C, 0xCFCF,\n\t0xAC00, 0xB0A1, 0xAC01, 0xB0A2, 0xAC02, 0x8141, 0xAC03, 0x8142,\n\t0xAC04, 0xB0A3, 0xAC05, 0x8143, 0xAC06, 0x8144, 0xAC07, 0xB0A4,\n\t0xAC08, 0xB0A5, 0xAC09, 0xB0A6, 0xAC0A, 0xB0A7, 0xAC0B, 0x8145,\n\t0xAC0C, 0x8146, 0xAC0D, 0x8147, 0xAC0E, 0x8148, 0xAC0F, 0x8149,\n\t0xAC10, 0xB0A8, 0xAC11, 0xB0A9, 0xAC12, 0xB0AA, 0xAC13, 0xB0AB,\n\t0xAC14, 0xB0AC, 0xAC15, 0xB0AD, 0xAC16, 0xB0AE, 0xAC17, 0xB0AF,\n\t0xAC18, 0x814A, 0xAC19, 0xB0B0, 0xAC1A, 0xB0B1, 0xAC1B, 0xB0B2,\n\t0xAC1C, 0xB0B3, 0xAC1D, 0xB0B4, 0xAC1E, 0x814B, 0xAC1F, 0x814C,\n\t0xAC20, 0xB0B5, 0xAC21, 0x814D, 0xAC22, 0x814E, 0xAC23, 0x814F,\n\t0xAC24, 0xB0B6, 0xAC25, 0x8150, 0xAC26, 0x8151, 0xAC27, 0x8152,\n\t0xAC28, 0x8153, 0xAC29, 0x8154, 0xAC2A, 0x8155, 0xAC2B, 0x8156,\n\t0xAC2C, 0xB0B7, 0xAC2D, 0xB0B8, 0xAC2E, 0x8157, 0xAC2F, 0xB0B9,\n\t0xAC30, 0xB0BA, 0xAC31, 0xB0BB, 0xAC32, 0x8158, 0xAC33, 0x8159,\n\t0xAC34, 0x815A, 0xAC35, 0x8161, 0xAC36, 0x8162, 0xAC37, 0x8163,\n\t0xAC38, 0xB0BC, 0xAC39, 0xB0BD, 0xAC3A, 0x8164, 0xAC3B, 0x8165,\n\t0xAC3C, 0xB0BE, 0xAC3D, 0x8166, 0xAC3E, 0x8167, 0xAC3F, 0x8168,\n\t0xAC40, 0xB0BF, 0xAC41, 0x8169, 0xAC42, 0x816A, 0xAC43, 0x816B,\n\t0xAC44, 0x816C, 0xAC45, 0x816D, 0xAC46, 0x816E, 0xAC47, 0x816F,\n\t0xAC48, 0x8170, 0xAC49, 0x8171, 0xAC4A, 0x8172, 0xAC4B, 0xB0C0,\n\t0xAC4C, 0x8173, 0xAC4D, 0xB0C1, 0xAC4E, 0x8174, 0xAC4F, 0x8175,\n\t0xAC50, 0x8176, 0xAC51, 0x8177, 0xAC52, 0x8178, 0xAC53, 0x8179,\n\t0xAC54, 0xB0C2, 0xAC55, 0x817A, 0xAC56, 0x8181, 0xAC57, 0x8182,\n\t0xAC58, 0xB0C3, 0xAC59, 0x8183, 0xAC5A, 0x8184, 0xAC5B, 0x8185,\n\t0xAC5C, 0xB0C4, 0xAC5D, 0x8186, 0xAC5E, 0x8187, 0xAC5F, 0x8188,\n\t0xAC60, 0x8189, 0xAC61, 0x818A, 0xAC62, 0x818B, 0xAC63, 0x818C,\n\t0xAC64, 0x818D, 0xAC65, 0x818E, 0xAC66, 0x818F, 0xAC67, 0x8190,\n\t0xAC68, 0x8191, 0xAC69, 0x8192, 0xAC6A, 0x8193, 0xAC6B, 0x8194,\n\t0xAC6C, 0x8195, 0xAC6D, 0x8196, 0xAC6E, 0x8197, 0xAC6F, 0x8198,\n\t0xAC70, 0xB0C5, 0xAC71, 0xB0C6, 0xAC72, 0x8199, 0xAC73, 0x819A,\n\t0xAC74, 0xB0C7, 0xAC75, 0x819B, 0xAC76, 0x819C, 0xAC77, 0xB0C8,\n\t0xAC78, 0xB0C9, 0xAC79, 0x819D, 0xAC7A, 0xB0CA, 0xAC7B, 0x819E,\n\t0xAC7C, 0x819F, 0xAC7D, 0x81A0, 0xAC7E, 0x81A1, 0xAC7F, 0x81A2,\n\t0xAC80, 0xB0CB, 0xAC81, 0xB0CC, 0xAC82, 0x81A3, 0xAC83, 0xB0CD,\n\t0xAC84, 0xB0CE, 0xAC85, 0xB0CF, 0xAC86, 0xB0D0, 0xAC87, 0x81A4,\n\t0xAC88, 0x81A5, 0xAC89, 0xB0D1, 0xAC8A, 0xB0D2, 0xAC8B, 0xB0D3,\n\t0xAC8C, 0xB0D4, 0xAC8D, 0x81A6, 0xAC8E, 0x81A7, 0xAC8F, 0x81A8,\n\t0xAC90, 0xB0D5, 0xAC91, 0x81A9, 0xAC92, 0x81AA, 0xAC93, 0x81AB,\n\t0xAC94, 0xB0D6, 0xAC95, 0x81AC, 0xAC96, 0x81AD, 0xAC97, 0x81AE,\n\t0xAC98, 0x81AF, 0xAC99, 0x81B0, 0xAC9A, 0x81B1, 0xAC9B, 0x81B2,\n\t0xAC9C, 0xB0D7, 0xAC9D, 0xB0D8, 0xAC9E, 0x81B3, 0xAC9F, 0xB0D9,\n\t0xACA0, 0xB0DA, 0xACA1, 0xB0DB, 0xACA2, 0x81B4, 0xACA3, 0x81B5,\n\t0xACA4, 0x81B6, 0xACA5, 0x81B7, 0xACA6, 0x81B8, 0xACA7, 0x81B9,\n\t0xACA8, 0xB0DC, 0xACA9, 0xB0DD, 0xACAA, 0xB0DE, 0xACAB, 0x81BA,\n\t0xACAC, 0xB0DF, 0xACAD, 0x81BB, 0xACAE, 0x81BC, 0xACAF, 0xB0E0,\n\t0xACB0, 0xB0E1, 0xACB1, 0x81BD, 0xACB2, 0x81BE, 0xACB3, 0x81BF,\n\t0xACB4, 0x81C0, 0xACB5, 0x81C1, 0xACB6, 0x81C2, 0xACB7, 0x81C3,\n\t0xACB8, 0xB0E2, 0xACB9, 0xB0E3, 0xACBA, 0x81C4, 0xACBB, 0xB0E4,\n\t0xACBC, 0xB0E5, 0xACBD, 0xB0E6, 0xACBE, 0x81C5, 0xACBF, 0x81C6,\n\t0xACC0, 0x81C7, 0xACC1, 0xB0E7, 0xACC2, 0x81C8, 0xACC3, 0x81C9,\n\t0xACC4, 0xB0E8, 0xACC5, 0x81CA, 0xACC6, 0x81CB, 0xACC7, 0x81CC,\n\t0xACC8, 0xB0E9, 0xACC9, 0x81CD, 0xACCA, 0x81CE, 0xACCB, 0x81CF,\n\t0xACCC, 0xB0EA, 0xACCD, 0x81D0, 0xACCE, 0x81D1, 0xACCF, 0x81D2,\n\t0xACD0, 0x81D3, 0xACD1, 0x81D4, 0xACD2, 0x81D5, 0xACD3, 0x81D6,\n\t0xACD4, 0x81D7, 0xACD5, 0xB0EB, 0xACD6, 0x81D8, 0xACD7, 0xB0EC,\n\t0xACD8, 0x81D9, 0xACD9, 0x81DA, 0xACDA, 0x81DB, 0xACDB, 0x81DC,\n\t0xACDC, 0x81DD, 0xACDD, 0x81DE, 0xACDE, 0x81DF, 0xACDF, 0x81E0,\n\t0xACE0, 0xB0ED, 0xACE1, 0xB0EE, 0xACE2, 0x81E1, 0xACE3, 0x81E2,\n\t0xACE4, 0xB0EF, 0xACE5, 0x81E3, 0xACE6, 0x81E4, 0xACE7, 0xB0F0,\n\t0xACE8, 0xB0F1, 0xACE9, 0x81E5, 0xACEA, 0xB0F2, 0xACEB, 0x81E6,\n\t0xACEC, 0xB0F3, 0xACED, 0x81E7, 0xACEE, 0x81E8, 0xACEF, 0xB0F4,\n\t0xACF0, 0xB0F5, 0xACF1, 0xB0F6, 0xACF2, 0x81E9, 0xACF3, 0xB0F7,\n\t0xACF4, 0x81EA, 0xACF5, 0xB0F8, 0xACF6, 0xB0F9, 0xACF7, 0x81EB,\n\t0xACF8, 0x81EC, 0xACF9, 0x81ED, 0xACFA, 0x81EE, 0xACFB, 0x81EF,\n\t0xACFC, 0xB0FA, 0xACFD, 0xB0FB, 0xACFE, 0x81F0, 0xACFF, 0x81F1,\n\t0xAD00, 0xB0FC, 0xAD01, 0x81F2, 0xAD02, 0x81F3, 0xAD03, 0x81F4,\n\t0xAD04, 0xB0FD, 0xAD05, 0x81F5, 0xAD06, 0xB0FE, 0xAD07, 0x81F6,\n\t0xAD08, 0x81F7, 0xAD09, 0x81F8, 0xAD0A, 0x81F9, 0xAD0B, 0x81FA,\n\t0xAD0C, 0xB1A1, 0xAD0D, 0xB1A2, 0xAD0E, 0x81FB, 0xAD0F, 0xB1A3,\n\t0xAD10, 0x81FC, 0xAD11, 0xB1A4, 0xAD12, 0x81FD, 0xAD13, 0x81FE,\n\t0xAD14, 0x8241, 0xAD15, 0x8242, 0xAD16, 0x8243, 0xAD17, 0x8244,\n\t0xAD18, 0xB1A5, 0xAD19, 0x8245, 0xAD1A, 0x8246, 0xAD1B, 0x8247,\n\t0xAD1C, 0xB1A6, 0xAD1D, 0x8248, 0xAD1E, 0x8249, 0xAD1F, 0x824A,\n\t0xAD20, 0xB1A7, 0xAD21, 0x824B, 0xAD22, 0x824C, 0xAD23, 0x824D,\n\t0xAD24, 0x824E, 0xAD25, 0x824F, 0xAD26, 0x8250, 0xAD27, 0x8251,\n\t0xAD28, 0x8252, 0xAD29, 0xB1A8, 0xAD2A, 0x8253, 0xAD2B, 0x8254,\n\t0xAD2C, 0xB1A9, 0xAD2D, 0xB1AA, 0xAD2E, 0x8255, 0xAD2F, 0x8256,\n\t0xAD30, 0x8257, 0xAD31, 0x8258, 0xAD32, 0x8259, 0xAD33, 0x825A,\n\t0xAD34, 0xB1AB, 0xAD35, 0xB1AC, 0xAD36, 0x8261, 0xAD37, 0x8262,\n\t0xAD38, 0xB1AD, 0xAD39, 0x8263, 0xAD3A, 0x8264, 0xAD3B, 0x8265,\n\t0xAD3C, 0xB1AE, 0xAD3D, 0x8266, 0xAD3E, 0x8267, 0xAD3F, 0x8268,\n\t0xAD40, 0x8269, 0xAD41, 0x826A, 0xAD42, 0x826B, 0xAD43, 0x826C,\n\t0xAD44, 0xB1AF, 0xAD45, 0xB1B0, 0xAD46, 0x826D, 0xAD47, 0xB1B1,\n\t0xAD48, 0x826E, 0xAD49, 0xB1B2, 0xAD4A, 0x826F, 0xAD4B, 0x8270,\n\t0xAD4C, 0x8271, 0xAD4D, 0x8272, 0xAD4E, 0x8273, 0xAD4F, 0x8274,\n\t0xAD50, 0xB1B3, 0xAD51, 0x8275, 0xAD52, 0x8276, 0xAD53, 0x8277,\n\t0xAD54, 0xB1B4, 0xAD55, 0x8278, 0xAD56, 0x8279, 0xAD57, 0x827A,\n\t0xAD58, 0xB1B5, 0xAD59, 0x8281, 0xAD5A, 0x8282, 0xAD5B, 0x8283,\n\t0xAD5C, 0x8284, 0xAD5D, 0x8285, 0xAD5E, 0x8286, 0xAD5F, 0x8287,\n\t0xAD60, 0x8288, 0xAD61, 0xB1B6, 0xAD62, 0x8289, 0xAD63, 0xB1B7,\n\t0xAD64, 0x828A, 0xAD65, 0x828B, 0xAD66, 0x828C, 0xAD67, 0x828D,\n\t0xAD68, 0x828E, 0xAD69, 0x828F, 0xAD6A, 0x8290, 0xAD6B, 0x8291,\n\t0xAD6C, 0xB1B8, 0xAD6D, 0xB1B9, 0xAD6E, 0x8292, 0xAD6F, 0x8293,\n\t0xAD70, 0xB1BA, 0xAD71, 0x8294, 0xAD72, 0x8295, 0xAD73, 0xB1BB,\n\t0xAD74, 0xB1BC, 0xAD75, 0xB1BD, 0xAD76, 0xB1BE, 0xAD77, 0x8296,\n\t0xAD78, 0x8297, 0xAD79, 0x8298, 0xAD7A, 0x8299, 0xAD7B, 0xB1BF,\n\t0xAD7C, 0xB1C0, 0xAD7D, 0xB1C1, 0xAD7E, 0x829A, 0xAD7F, 0xB1C2,\n\t0xAD80, 0x829B, 0xAD81, 0xB1C3, 0xAD82, 0xB1C4, 0xAD83, 0x829C,\n\t0xAD84, 0x829D, 0xAD85, 0x829E, 0xAD86, 0x829F, 0xAD87, 0x82A0,\n\t0xAD88, 0xB1C5, 0xAD89, 0xB1C6, 0xAD8A, 0x82A1, 0xAD8B, 0x82A2,\n\t0xAD8C, 0xB1C7, 0xAD8D, 0x82A3, 0xAD8E, 0x82A4, 0xAD8F, 0x82A5,\n\t0xAD90, 0xB1C8, 0xAD91, 0x82A6, 0xAD92, 0x82A7, 0xAD93, 0x82A8,\n\t0xAD94, 0x82A9, 0xAD95, 0x82AA, 0xAD96, 0x82AB, 0xAD97, 0x82AC,\n\t0xAD98, 0x82AD, 0xAD99, 0x82AE, 0xAD9A, 0x82AF, 0xAD9B, 0x82B0,\n\t0xAD9C, 0xB1C9, 0xAD9D, 0xB1CA, 0xAD9E, 0x82B1, 0xAD9F, 0x82B2,\n\t0xADA0, 0x82B3, 0xADA1, 0x82B4, 0xADA2, 0x82B5, 0xADA3, 0x82B6,\n\t0xADA4, 0xB1CB, 0xADA5, 0x82B7, 0xADA6, 0x82B8, 0xADA7, 0x82B9,\n\t0xADA8, 0x82BA, 0xADA9, 0x82BB, 0xADAA, 0x82BC, 0xADAB, 0x82BD,\n\t0xADAC, 0x82BE, 0xADAD, 0x82BF, 0xADAE, 0x82C0, 0xADAF, 0x82C1,\n\t0xADB0, 0x82C2, 0xADB1, 0x82C3, 0xADB2, 0x82C4, 0xADB3, 0x82C5,\n\t0xADB4, 0x82C6, 0xADB5, 0x82C7, 0xADB6, 0x82C8, 0xADB7, 0xB1CC,\n\t0xADB8, 0x82C9, 0xADB9, 0x82CA, 0xADBA, 0x82CB, 0xADBB, 0x82CC,\n\t0xADBC, 0x82CD, 0xADBD, 0x82CE, 0xADBE, 0x82CF, 0xADBF, 0x82D0,\n\t0xADC0, 0xB1CD, 0xADC1, 0xB1CE, 0xADC2, 0x82D1, 0xADC3, 0x82D2,\n\t0xADC4, 0xB1CF, 0xADC5, 0x82D3, 0xADC6, 0x82D4, 0xADC7, 0x82D5,\n\t0xADC8, 0xB1D0, 0xADC9, 0x82D6, 0xADCA, 0x82D7, 0xADCB, 0x82D8,\n\t0xADCC, 0x82D9, 0xADCD, 0x82DA, 0xADCE, 0x82DB, 0xADCF, 0x82DC,\n\t0xADD0, 0xB1D1, 0xADD1, 0xB1D2, 0xADD2, 0x82DD, 0xADD3, 0xB1D3,\n\t0xADD4, 0x82DE, 0xADD5, 0x82DF, 0xADD6, 0x82E0, 0xADD7, 0x82E1,\n\t0xADD8, 0x82E2, 0xADD9, 0x82E3, 0xADDA, 0x82E4, 0xADDB, 0x82E5,\n\t0xADDC, 0xB1D4, 0xADDD, 0x82E6, 0xADDE, 0x82E7, 0xADDF, 0x82E8,\n\t0xADE0, 0xB1D5, 0xADE1, 0x82E9, 0xADE2, 0x82EA, 0xADE3, 0x82EB,\n\t0xADE4, 0xB1D6, 0xADE5, 0x82EC, 0xADE6, 0x82ED, 0xADE7, 0x82EE,\n\t0xADE8, 0x82EF, 0xADE9, 0x82F0, 0xADEA, 0x82F1, 0xADEB, 0x82F2,\n\t0xADEC, 0x82F3, 0xADED, 0x82F4, 0xADEE, 0x82F5, 0xADEF, 0x82F6,\n\t0xADF0, 0x82F7, 0xADF1, 0x82F8, 0xADF2, 0x82F9, 0xADF3, 0x82FA,\n\t0xADF4, 0x82FB, 0xADF5, 0x82FC, 0xADF6, 0x82FD, 0xADF7, 0x82FE,\n\t0xADF8, 0xB1D7, 0xADF9, 0xB1D8, 0xADFA, 0x8341, 0xADFB, 0x8342,\n\t0xADFC, 0xB1D9, 0xADFD, 0x8343, 0xADFE, 0x8344, 0xADFF, 0xB1DA,\n\t0xAE00, 0xB1DB, 0xAE01, 0xB1DC, 0xAE02, 0x8345, 0xAE03, 0x8346,\n\t0xAE04, 0x8347, 0xAE05, 0x8348, 0xAE06, 0x8349, 0xAE07, 0x834A,\n\t0xAE08, 0xB1DD, 0xAE09, 0xB1DE, 0xAE0A, 0x834B, 0xAE0B, 0xB1DF,\n\t0xAE0C, 0x834C, 0xAE0D, 0xB1E0, 0xAE0E, 0x834D, 0xAE0F, 0x834E,\n\t0xAE10, 0x834F, 0xAE11, 0x8350, 0xAE12, 0x8351, 0xAE13, 0x8352,\n\t0xAE14, 0xB1E1, 0xAE15, 0x8353, 0xAE16, 0x8354, 0xAE17, 0x8355,\n\t0xAE18, 0x8356, 0xAE19, 0x8357, 0xAE1A, 0x8358, 0xAE1B, 0x8359,\n\t0xAE1C, 0x835A, 0xAE1D, 0x8361, 0xAE1E, 0x8362, 0xAE1F, 0x8363,\n\t0xAE20, 0x8364, 0xAE21, 0x8365, 0xAE22, 0x8366, 0xAE23, 0x8367,\n\t0xAE24, 0x8368, 0xAE25, 0x8369, 0xAE26, 0x836A, 0xAE27, 0x836B,\n\t0xAE28, 0x836C, 0xAE29, 0x836D, 0xAE2A, 0x836E, 0xAE2B, 0x836F,\n\t0xAE2C, 0x8370, 0xAE2D, 0x8371, 0xAE2E, 0x8372, 0xAE2F, 0x8373,\n\t0xAE30, 0xB1E2, 0xAE31, 0xB1E3, 0xAE32, 0x8374, 0xAE33, 0x8375,\n\t0xAE34, 0xB1E4, 0xAE35, 0x8376, 0xAE36, 0x8377, 0xAE37, 0xB1E5,\n\t0xAE38, 0xB1E6, 0xAE39, 0x8378, 0xAE3A, 0xB1E7, 0xAE3B, 0x8379,\n\t0xAE3C, 0x837A, 0xAE3D, 0x8381, 0xAE3E, 0x8382, 0xAE3F, 0x8383,\n\t0xAE40, 0xB1E8, 0xAE41, 0xB1E9, 0xAE42, 0x8384, 0xAE43, 0xB1EA,\n\t0xAE44, 0x8385, 0xAE45, 0xB1EB, 0xAE46, 0xB1EC, 0xAE47, 0x8386,\n\t0xAE48, 0x8387, 0xAE49, 0x8388, 0xAE4A, 0xB1ED, 0xAE4B, 0x8389,\n\t0xAE4C, 0xB1EE, 0xAE4D, 0xB1EF, 0xAE4E, 0xB1F0, 0xAE4F, 0x838A,\n\t0xAE50, 0xB1F1, 0xAE51, 0x838B, 0xAE52, 0x838C, 0xAE53, 0x838D,\n\t0xAE54, 0xB1F2, 0xAE55, 0x838E, 0xAE56, 0xB1F3, 0xAE57, 0x838F,\n\t0xAE58, 0x8390, 0xAE59, 0x8391, 0xAE5A, 0x8392, 0xAE5B, 0x8393,\n\t0xAE5C, 0xB1F4, 0xAE5D, 0xB1F5, 0xAE5E, 0x8394, 0xAE5F, 0xB1F6,\n\t0xAE60, 0xB1F7, 0xAE61, 0xB1F8, 0xAE62, 0x8395, 0xAE63, 0x8396,\n\t0xAE64, 0x8397, 0xAE65, 0xB1F9, 0xAE66, 0x8398, 0xAE67, 0x8399,\n\t0xAE68, 0xB1FA, 0xAE69, 0xB1FB, 0xAE6A, 0x839A, 0xAE6B, 0x839B,\n\t0xAE6C, 0xB1FC, 0xAE6D, 0x839C, 0xAE6E, 0x839D, 0xAE6F, 0x839E,\n\t0xAE70, 0xB1FD, 0xAE71, 0x839F, 0xAE72, 0x83A0, 0xAE73, 0x83A1,\n\t0xAE74, 0x83A2, 0xAE75, 0x83A3, 0xAE76, 0x83A4, 0xAE77, 0x83A5,\n\t0xAE78, 0xB1FE, 0xAE79, 0xB2A1, 0xAE7A, 0x83A6, 0xAE7B, 0xB2A2,\n\t0xAE7C, 0xB2A3, 0xAE7D, 0xB2A4, 0xAE7E, 0x83A7, 0xAE7F, 0x83A8,\n\t0xAE80, 0x83A9, 0xAE81, 0x83AA, 0xAE82, 0x83AB, 0xAE83, 0x83AC,\n\t0xAE84, 0xB2A5, 0xAE85, 0xB2A6, 0xAE86, 0x83AD, 0xAE87, 0x83AE,\n\t0xAE88, 0x83AF, 0xAE89, 0x83B0, 0xAE8A, 0x83B1, 0xAE8B, 0x83B2,\n\t0xAE8C, 0xB2A7, 0xAE8D, 0x83B3, 0xAE8E, 0x83B4, 0xAE8F, 0x83B5,\n\t0xAE90, 0x83B6, 0xAE91, 0x83B7, 0xAE92, 0x83B8, 0xAE93, 0x83B9,\n\t0xAE94, 0x83BA, 0xAE95, 0x83BB, 0xAE96, 0x83BC, 0xAE97, 0x83BD,\n\t0xAE98, 0x83BE, 0xAE99, 0x83BF, 0xAE9A, 0x83C0, 0xAE9B, 0x83C1,\n\t0xAE9C, 0x83C2, 0xAE9D, 0x83C3, 0xAE9E, 0x83C4, 0xAE9F, 0x83C5,\n\t0xAEA0, 0x83C6, 0xAEA1, 0x83C7, 0xAEA2, 0x83C8, 0xAEA3, 0x83C9,\n\t0xAEA4, 0x83CA, 0xAEA5, 0x83CB, 0xAEA6, 0x83CC, 0xAEA7, 0x83CD,\n\t0xAEA8, 0x83CE, 0xAEA9, 0x83CF, 0xAEAA, 0x83D0, 0xAEAB, 0x83D1,\n\t0xAEAC, 0x83D2, 0xAEAD, 0x83D3, 0xAEAE, 0x83D4, 0xAEAF, 0x83D5,\n\t0xAEB0, 0x83D6, 0xAEB1, 0x83D7, 0xAEB2, 0x83D8, 0xAEB3, 0x83D9,\n\t0xAEB4, 0x83DA, 0xAEB5, 0x83DB, 0xAEB6, 0x83DC, 0xAEB7, 0x83DD,\n\t0xAEB8, 0x83DE, 0xAEB9, 0x83DF, 0xAEBA, 0x83E0, 0xAEBB, 0x83E1,\n\t0xAEBC, 0xB2A8, 0xAEBD, 0xB2A9, 0xAEBE, 0xB2AA, 0xAEBF, 0x83E2,\n\t0xAEC0, 0xB2AB, 0xAEC1, 0x83E3, 0xAEC2, 0x83E4, 0xAEC3, 0x83E5,\n\t0xAEC4, 0xB2AC, 0xAEC5, 0x83E6, 0xAEC6, 0x83E7, 0xAEC7, 0x83E8,\n\t0xAEC8, 0x83E9, 0xAEC9, 0x83EA, 0xAECA, 0x83EB, 0xAECB, 0x83EC,\n\t0xAECC, 0xB2AD, 0xAECD, 0xB2AE, 0xAECE, 0x83ED, 0xAECF, 0xB2AF,\n\t0xAED0, 0xB2B0, 0xAED1, 0xB2B1, 0xAED2, 0x83EE, 0xAED3, 0x83EF,\n\t0xAED4, 0x83F0, 0xAED5, 0x83F1, 0xAED6, 0x83F2, 0xAED7, 0x83F3,\n\t0xAED8, 0xB2B2, 0xAED9, 0xB2B3, 0xAEDA, 0x83F4, 0xAEDB, 0x83F5,\n\t0xAEDC, 0xB2B4, 0xAEDD, 0x83F6, 0xAEDE, 0x83F7, 0xAEDF, 0x83F8,\n\t0xAEE0, 0x83F9, 0xAEE1, 0x83FA, 0xAEE2, 0x83FB, 0xAEE3, 0x83FC,\n\t0xAEE4, 0x83FD, 0xAEE5, 0x83FE, 0xAEE6, 0x8441, 0xAEE7, 0x8442,\n\t0xAEE8, 0xB2B5, 0xAEE9, 0x8443, 0xAEEA, 0x8444, 0xAEEB, 0xB2B6,\n\t0xAEEC, 0x8445, 0xAEED, 0xB2B7, 0xAEEE, 0x8446, 0xAEEF, 0x8447,\n\t0xAEF0, 0x8448, 0xAEF1, 0x8449, 0xAEF2, 0x844A, 0xAEF3, 0x844B,\n\t0xAEF4, 0xB2B8, 0xAEF5, 0x844C, 0xAEF6, 0x844D, 0xAEF7, 0x844E,\n\t0xAEF8, 0xB2B9, 0xAEF9, 0x844F, 0xAEFA, 0x8450, 0xAEFB, 0x8451,\n\t0xAEFC, 0xB2BA, 0xAEFD, 0x8452, 0xAEFE, 0x8453, 0xAEFF, 0x8454,\n\t0xAF00, 0x8455, 0xAF01, 0x8456, 0xAF02, 0x8457, 0xAF03, 0x8458,\n\t0xAF04, 0x8459, 0xAF05, 0x845A, 0xAF06, 0x8461, 0xAF07, 0xB2BB,\n\t0xAF08, 0xB2BC, 0xAF09, 0x8462, 0xAF0A, 0x8463, 0xAF0B, 0x8464,\n\t0xAF0C, 0x8465, 0xAF0D, 0xB2BD, 0xAF0E, 0x8466, 0xAF0F, 0x8467,\n\t0xAF10, 0xB2BE, 0xAF11, 0x8468, 0xAF12, 0x8469, 0xAF13, 0x846A,\n\t0xAF14, 0x846B, 0xAF15, 0x846C, 0xAF16, 0x846D, 0xAF17, 0x846E,\n\t0xAF18, 0x846F, 0xAF19, 0x8470, 0xAF1A, 0x8471, 0xAF1B, 0x8472,\n\t0xAF1C, 0x8473, 0xAF1D, 0x8474, 0xAF1E, 0x8475, 0xAF1F, 0x8476,\n\t0xAF20, 0x8477, 0xAF21, 0x8478, 0xAF22, 0x8479, 0xAF23, 0x847A,\n\t0xAF24, 0x8481, 0xAF25, 0x8482, 0xAF26, 0x8483, 0xAF27, 0x8484,\n\t0xAF28, 0x8485, 0xAF29, 0x8486, 0xAF2A, 0x8487, 0xAF2B, 0x8488,\n\t0xAF2C, 0xB2BF, 0xAF2D, 0xB2C0, 0xAF2E, 0x8489, 0xAF2F, 0x848A,\n\t0xAF30, 0xB2C1, 0xAF31, 0x848B, 0xAF32, 0xB2C2, 0xAF33, 0x848C,\n\t0xAF34, 0xB2C3, 0xAF35, 0x848D, 0xAF36, 0x848E, 0xAF37, 0x848F,\n\t0xAF38, 0x8490, 0xAF39, 0x8491, 0xAF3A, 0x8492, 0xAF3B, 0x8493,\n\t0xAF3C, 0xB2C4, 0xAF3D, 0xB2C5, 0xAF3E, 0x8494, 0xAF3F, 0xB2C6,\n\t0xAF40, 0x8495, 0xAF41, 0xB2C7, 0xAF42, 0xB2C8, 0xAF43, 0xB2C9,\n\t0xAF44, 0x8496, 0xAF45, 0x8497, 0xAF46, 0x8498, 0xAF47, 0x8499,\n\t0xAF48, 0xB2CA, 0xAF49, 0xB2CB, 0xAF4A, 0x849A, 0xAF4B, 0x849B,\n\t0xAF4C, 0x849C, 0xAF4D, 0x849D, 0xAF4E, 0x849E, 0xAF4F, 0x849F,\n\t0xAF50, 0xB2CC, 0xAF51, 0x84A0, 0xAF52, 0x84A1, 0xAF53, 0x84A2,\n\t0xAF54, 0x84A3, 0xAF55, 0x84A4, 0xAF56, 0x84A5, 0xAF57, 0x84A6,\n\t0xAF58, 0x84A7, 0xAF59, 0x84A8, 0xAF5A, 0x84A9, 0xAF5B, 0x84AA,\n\t0xAF5C, 0xB2CD, 0xAF5D, 0xB2CE, 0xAF5E, 0x84AB, 0xAF5F, 0x84AC,\n\t0xAF60, 0x84AD, 0xAF61, 0x84AE, 0xAF62, 0x84AF, 0xAF63, 0x84B0,\n\t0xAF64, 0xB2CF, 0xAF65, 0xB2D0, 0xAF66, 0x84B1, 0xAF67, 0x84B2,\n\t0xAF68, 0x84B3, 0xAF69, 0x84B4, 0xAF6A, 0x84B5, 0xAF6B, 0x84B6,\n\t0xAF6C, 0x84B7, 0xAF6D, 0x84B8, 0xAF6E, 0x84B9, 0xAF6F, 0x84BA,\n\t0xAF70, 0x84BB, 0xAF71, 0x84BC, 0xAF72, 0x84BD, 0xAF73, 0x84BE,\n\t0xAF74, 0x84BF, 0xAF75, 0x84C0, 0xAF76, 0x84C1, 0xAF77, 0x84C2,\n\t0xAF78, 0x84C3, 0xAF79, 0xB2D1, 0xAF7A, 0x84C4, 0xAF7B, 0x84C5,\n\t0xAF7C, 0x84C6, 0xAF7D, 0x84C7, 0xAF7E, 0x84C8, 0xAF7F, 0x84C9,\n\t0xAF80, 0xB2D2, 0xAF81, 0x84CA, 0xAF82, 0x84CB, 0xAF83, 0x84CC,\n\t0xAF84, 0xB2D3, 0xAF85, 0x84CD, 0xAF86, 0x84CE, 0xAF87, 0x84CF,\n\t0xAF88, 0xB2D4, 0xAF89, 0x84D0, 0xAF8A, 0x84D1, 0xAF8B, 0x84D2,\n\t0xAF8C, 0x84D3, 0xAF8D, 0x84D4, 0xAF8E, 0x84D5, 0xAF8F, 0x84D6,\n\t0xAF90, 0xB2D5, 0xAF91, 0xB2D6, 0xAF92, 0x84D7, 0xAF93, 0x84D8,\n\t0xAF94, 0x84D9, 0xAF95, 0xB2D7, 0xAF96, 0x84DA, 0xAF97, 0x84DB,\n\t0xAF98, 0x84DC, 0xAF99, 0x84DD, 0xAF9A, 0x84DE, 0xAF9B, 0x84DF,\n\t0xAF9C, 0xB2D8, 0xAF9D, 0x84E0, 0xAF9E, 0x84E1, 0xAF9F, 0x84E2,\n\t0xAFA0, 0x84E3, 0xAFA1, 0x84E4, 0xAFA2, 0x84E5, 0xAFA3, 0x84E6,\n\t0xAFA4, 0x84E7, 0xAFA5, 0x84E8, 0xAFA6, 0x84E9, 0xAFA7, 0x84EA,\n\t0xAFA8, 0x84EB, 0xAFA9, 0x84EC, 0xAFAA, 0x84ED, 0xAFAB, 0x84EE,\n\t0xAFAC, 0x84EF, 0xAFAD, 0x84F0, 0xAFAE, 0x84F1, 0xAFAF, 0x84F2,\n\t0xAFB0, 0x84F3, 0xAFB1, 0x84F4, 0xAFB2, 0x84F5, 0xAFB3, 0x84F6,\n\t0xAFB4, 0x84F7, 0xAFB5, 0x84F8, 0xAFB6, 0x84F9, 0xAFB7, 0x84FA,\n\t0xAFB8, 0xB2D9, 0xAFB9, 0xB2DA, 0xAFBA, 0x84FB, 0xAFBB, 0x84FC,\n\t0xAFBC, 0xB2DB, 0xAFBD, 0x84FD, 0xAFBE, 0x84FE, 0xAFBF, 0x8541,\n\t0xAFC0, 0xB2DC, 0xAFC1, 0x8542, 0xAFC2, 0x8543, 0xAFC3, 0x8544,\n\t0xAFC4, 0x8545, 0xAFC5, 0x8546, 0xAFC6, 0x8547, 0xAFC7, 0xB2DD,\n\t0xAFC8, 0xB2DE, 0xAFC9, 0xB2DF, 0xAFCA, 0x8548, 0xAFCB, 0xB2E0,\n\t0xAFCC, 0x8549, 0xAFCD, 0xB2E1, 0xAFCE, 0xB2E2, 0xAFCF, 0x854A,\n\t0xAFD0, 0x854B, 0xAFD1, 0x854C, 0xAFD2, 0x854D, 0xAFD3, 0x854E,\n\t0xAFD4, 0xB2E3, 0xAFD5, 0x854F, 0xAFD6, 0x8550, 0xAFD7, 0x8551,\n\t0xAFD8, 0x8552, 0xAFD9, 0x8553, 0xAFDA, 0x8554, 0xAFDB, 0x8555,\n\t0xAFDC, 0xB2E4, 0xAFDD, 0x8556, 0xAFDE, 0x8557, 0xAFDF, 0x8558,\n\t0xAFE0, 0x8559, 0xAFE1, 0x855A, 0xAFE2, 0x8561, 0xAFE3, 0x8562,\n\t0xAFE4, 0x8563, 0xAFE5, 0x8564, 0xAFE6, 0x8565, 0xAFE7, 0x8566,\n\t0xAFE8, 0xB2E5, 0xAFE9, 0xB2E6, 0xAFEA, 0x8567, 0xAFEB, 0x8568,\n\t0xAFEC, 0x8569, 0xAFED, 0x856A, 0xAFEE, 0x856B, 0xAFEF, 0x856C,\n\t0xAFF0, 0xB2E7, 0xAFF1, 0xB2E8, 0xAFF2, 0x856D, 0xAFF3, 0x856E,\n\t0xAFF4, 0xB2E9, 0xAFF5, 0x856F, 0xAFF6, 0x8570, 0xAFF7, 0x8571,\n\t0xAFF8, 0xB2EA, 0xAFF9, 0x8572, 0xAFFA, 0x8573, 0xAFFB, 0x8574,\n\t0xAFFC, 0x8575, 0xAFFD, 0x8576, 0xAFFE, 0x8577, 0xAFFF, 0x8578,\n\t0xB000, 0xB2EB, 0xB001, 0xB2EC, 0xB002, 0x8579, 0xB003, 0x857A,\n\t0xB004, 0xB2ED, 0xB005, 0x8581, 0xB006, 0x8582, 0xB007, 0x8583,\n\t0xB008, 0x8584, 0xB009, 0x8585, 0xB00A, 0x8586, 0xB00B, 0x8587,\n\t0xB00C, 0xB2EE, 0xB00D, 0x8588, 0xB00E, 0x8589, 0xB00F, 0x858A,\n\t0xB010, 0xB2EF, 0xB011, 0x858B, 0xB012, 0x858C, 0xB013, 0x858D,\n\t0xB014, 0xB2F0, 0xB015, 0x858E, 0xB016, 0x858F, 0xB017, 0x8590,\n\t0xB018, 0x8591, 0xB019, 0x8592, 0xB01A, 0x8593, 0xB01B, 0x8594,\n\t0xB01C, 0xB2F1, 0xB01D, 0xB2F2, 0xB01E, 0x8595, 0xB01F, 0x8596,\n\t0xB020, 0x8597, 0xB021, 0x8598, 0xB022, 0x8599, 0xB023, 0x859A,\n\t0xB024, 0x859B, 0xB025, 0x859C, 0xB026, 0x859D, 0xB027, 0x859E,\n\t0xB028, 0xB2F3, 0xB029, 0x859F, 0xB02A, 0x85A0, 0xB02B, 0x85A1,\n\t0xB02C, 0x85A2, 0xB02D, 0x85A3, 0xB02E, 0x85A4, 0xB02F, 0x85A5,\n\t0xB030, 0x85A6, 0xB031, 0x85A7, 0xB032, 0x85A8, 0xB033, 0x85A9,\n\t0xB034, 0x85AA, 0xB035, 0x85AB, 0xB036, 0x85AC, 0xB037, 0x85AD,\n\t0xB038, 0x85AE, 0xB039, 0x85AF, 0xB03A, 0x85B0, 0xB03B, 0x85B1,\n\t0xB03C, 0x85B2, 0xB03D, 0x85B3, 0xB03E, 0x85B4, 0xB03F, 0x85B5,\n\t0xB040, 0x85B6, 0xB041, 0x85B7, 0xB042, 0x85B8, 0xB043, 0x85B9,\n\t0xB044, 0xB2F4, 0xB045, 0xB2F5, 0xB046, 0x85BA, 0xB047, 0x85BB,\n\t0xB048, 0xB2F6, 0xB049, 0x85BC, 0xB04A, 0xB2F7, 0xB04B, 0x85BD,\n\t0xB04C, 0xB2F8, 0xB04D, 0x85BE, 0xB04E, 0xB2F9, 0xB04F, 0x85BF,\n\t0xB050, 0x85C0, 0xB051, 0x85C1, 0xB052, 0x85C2, 0xB053, 0xB2FA,\n\t0xB054, 0xB2FB, 0xB055, 0xB2FC, 0xB056, 0x85C3, 0xB057, 0xB2FD,\n\t0xB058, 0x85C4, 0xB059, 0xB2FE, 0xB05A, 0x85C5, 0xB05B, 0x85C6,\n\t0xB05C, 0x85C7, 0xB05D, 0xB3A1, 0xB05E, 0x85C8, 0xB05F, 0x85C9,\n\t0xB060, 0x85CA, 0xB061, 0x85CB, 0xB062, 0x85CC, 0xB063, 0x85CD,\n\t0xB064, 0x85CE, 0xB065, 0x85CF, 0xB066, 0x85D0, 0xB067, 0x85D1,\n\t0xB068, 0x85D2, 0xB069, 0x85D3, 0xB06A, 0x85D4, 0xB06B, 0x85D5,\n\t0xB06C, 0x85D6, 0xB06D, 0x85D7, 0xB06E, 0x85D8, 0xB06F, 0x85D9,\n\t0xB070, 0x85DA, 0xB071, 0x85DB, 0xB072, 0x85DC, 0xB073, 0x85DD,\n\t0xB074, 0x85DE, 0xB075, 0x85DF, 0xB076, 0x85E0, 0xB077, 0x85E1,\n\t0xB078, 0x85E2, 0xB079, 0x85E3, 0xB07A, 0x85E4, 0xB07B, 0x85E5,\n\t0xB07C, 0xB3A2, 0xB07D, 0xB3A3, 0xB07E, 0x85E6, 0xB07F, 0x85E7,\n\t0xB080, 0xB3A4, 0xB081, 0x85E8, 0xB082, 0x85E9, 0xB083, 0x85EA,\n\t0xB084, 0xB3A5, 0xB085, 0x85EB, 0xB086, 0x85EC, 0xB087, 0x85ED,\n\t0xB088, 0x85EE, 0xB089, 0x85EF, 0xB08A, 0x85F0, 0xB08B, 0x85F1,\n\t0xB08C, 0xB3A6, 0xB08D, 0xB3A7, 0xB08E, 0x85F2, 0xB08F, 0xB3A8,\n\t0xB090, 0x85F3, 0xB091, 0xB3A9, 0xB092, 0x85F4, 0xB093, 0x85F5,\n\t0xB094, 0x85F6, 0xB095, 0x85F7, 0xB096, 0x85F8, 0xB097, 0x85F9,\n\t0xB098, 0xB3AA, 0xB099, 0xB3AB, 0xB09A, 0xB3AC, 0xB09B, 0x85FA,\n\t0xB09C, 0xB3AD, 0xB09D, 0x85FB, 0xB09E, 0x85FC, 0xB09F, 0xB3AE,\n\t0xB0A0, 0xB3AF, 0xB0A1, 0xB3B0, 0xB0A2, 0xB3B1, 0xB0A3, 0x85FD,\n\t0xB0A4, 0x85FE, 0xB0A5, 0x8641, 0xB0A6, 0x8642, 0xB0A7, 0x8643,\n\t0xB0A8, 0xB3B2, 0xB0A9, 0xB3B3, 0xB0AA, 0x8644, 0xB0AB, 0xB3B4,\n\t0xB0AC, 0xB3B5, 0xB0AD, 0xB3B6, 0xB0AE, 0xB3B7, 0xB0AF, 0xB3B8,\n\t0xB0B0, 0x8645, 0xB0B1, 0xB3B9, 0xB0B2, 0x8646, 0xB0B3, 0xB3BA,\n\t0xB0B4, 0xB3BB, 0xB0B5, 0xB3BC, 0xB0B6, 0x8647, 0xB0B7, 0x8648,\n\t0xB0B8, 0xB3BD, 0xB0B9, 0x8649, 0xB0BA, 0x864A, 0xB0BB, 0x864B,\n\t0xB0BC, 0xB3BE, 0xB0BD, 0x864C, 0xB0BE, 0x864D, 0xB0BF, 0x864E,\n\t0xB0C0, 0x864F, 0xB0C1, 0x8650, 0xB0C2, 0x8651, 0xB0C3, 0x8652,\n\t0xB0C4, 0xB3BF, 0xB0C5, 0xB3C0, 0xB0C6, 0x8653, 0xB0C7, 0xB3C1,\n\t0xB0C8, 0xB3C2, 0xB0C9, 0xB3C3, 0xB0CA, 0x8654, 0xB0CB, 0x8655,\n\t0xB0CC, 0x8656, 0xB0CD, 0x8657, 0xB0CE, 0x8658, 0xB0CF, 0x8659,\n\t0xB0D0, 0xB3C4, 0xB0D1, 0xB3C5, 0xB0D2, 0x865A, 0xB0D3, 0x8661,\n\t0xB0D4, 0xB3C6, 0xB0D5, 0x8662, 0xB0D6, 0x8663, 0xB0D7, 0x8664,\n\t0xB0D8, 0xB3C7, 0xB0D9, 0x8665, 0xB0DA, 0x8666, 0xB0DB, 0x8667,\n\t0xB0DC, 0x8668, 0xB0DD, 0x8669, 0xB0DE, 0x866A, 0xB0DF, 0x866B,\n\t0xB0E0, 0xB3C8, 0xB0E1, 0x866C, 0xB0E2, 0x866D, 0xB0E3, 0x866E,\n\t0xB0E4, 0x866F, 0xB0E5, 0xB3C9, 0xB0E6, 0x8670, 0xB0E7, 0x8671,\n\t0xB0E8, 0x8672, 0xB0E9, 0x8673, 0xB0EA, 0x8674, 0xB0EB, 0x8675,\n\t0xB0EC, 0x8676, 0xB0ED, 0x8677, 0xB0EE, 0x8678, 0xB0EF, 0x8679,\n\t0xB0F0, 0x867A, 0xB0F1, 0x8681, 0xB0F2, 0x8682, 0xB0F3, 0x8683,\n\t0xB0F4, 0x8684, 0xB0F5, 0x8685, 0xB0F6, 0x8686, 0xB0F7, 0x8687,\n\t0xB0F8, 0x8688, 0xB0F9, 0x8689, 0xB0FA, 0x868A, 0xB0FB, 0x868B,\n\t0xB0FC, 0x868C, 0xB0FD, 0x868D, 0xB0FE, 0x868E, 0xB0FF, 0x868F,\n\t0xB100, 0x8690, 0xB101, 0x8691, 0xB102, 0x8692, 0xB103, 0x8693,\n\t0xB104, 0x8694, 0xB105, 0x8695, 0xB106, 0x8696, 0xB107, 0x8697,\n\t0xB108, 0xB3CA, 0xB109, 0xB3CB, 0xB10A, 0x8698, 0xB10B, 0xB3CC,\n\t0xB10C, 0xB3CD, 0xB10D, 0x8699, 0xB10E, 0x869A, 0xB10F, 0x869B,\n\t0xB110, 0xB3CE, 0xB111, 0x869C, 0xB112, 0xB3CF, 0xB113, 0xB3D0,\n\t0xB114, 0x869D, 0xB115, 0x869E, 0xB116, 0x869F, 0xB117, 0x86A0,\n\t0xB118, 0xB3D1, 0xB119, 0xB3D2, 0xB11A, 0x86A1, 0xB11B, 0xB3D3,\n\t0xB11C, 0xB3D4, 0xB11D, 0xB3D5, 0xB11E, 0x86A2, 0xB11F, 0x86A3,\n\t0xB120, 0x86A4, 0xB121, 0x86A5, 0xB122, 0x86A6, 0xB123, 0xB3D6,\n\t0xB124, 0xB3D7, 0xB125, 0xB3D8, 0xB126, 0x86A7, 0xB127, 0x86A8,\n\t0xB128, 0xB3D9, 0xB129, 0x86A9, 0xB12A, 0x86AA, 0xB12B, 0x86AB,\n\t0xB12C, 0xB3DA, 0xB12D, 0x86AC, 0xB12E, 0x86AD, 0xB12F, 0x86AE,\n\t0xB130, 0x86AF, 0xB131, 0x86B0, 0xB132, 0x86B1, 0xB133, 0x86B2,\n\t0xB134, 0xB3DB, 0xB135, 0xB3DC, 0xB136, 0x86B3, 0xB137, 0xB3DD,\n\t0xB138, 0xB3DE, 0xB139, 0xB3DF, 0xB13A, 0x86B4, 0xB13B, 0x86B5,\n\t0xB13C, 0x86B6, 0xB13D, 0x86B7, 0xB13E, 0x86B8, 0xB13F, 0x86B9,\n\t0xB140, 0xB3E0, 0xB141, 0xB3E1, 0xB142, 0x86BA, 0xB143, 0x86BB,\n\t0xB144, 0xB3E2, 0xB145, 0x86BC, 0xB146, 0x86BD, 0xB147, 0x86BE,\n\t0xB148, 0xB3E3, 0xB149, 0x86BF, 0xB14A, 0x86C0, 0xB14B, 0x86C1,\n\t0xB14C, 0x86C2, 0xB14D, 0x86C3, 0xB14E, 0x86C4, 0xB14F, 0x86C5,\n\t0xB150, 0xB3E4, 0xB151, 0xB3E5, 0xB152, 0x86C6, 0xB153, 0x86C7,\n\t0xB154, 0xB3E6, 0xB155, 0xB3E7, 0xB156, 0x86C8, 0xB157, 0x86C9,\n\t0xB158, 0xB3E8, 0xB159, 0x86CA, 0xB15A, 0x86CB, 0xB15B, 0x86CC,\n\t0xB15C, 0xB3E9, 0xB15D, 0x86CD, 0xB15E, 0x86CE, 0xB15F, 0x86CF,\n\t0xB160, 0xB3EA, 0xB161, 0x86D0, 0xB162, 0x86D1, 0xB163, 0x86D2,\n\t0xB164, 0x86D3, 0xB165, 0x86D4, 0xB166, 0x86D5, 0xB167, 0x86D6,\n\t0xB168, 0x86D7, 0xB169, 0x86D8, 0xB16A, 0x86D9, 0xB16B, 0x86DA,\n\t0xB16C, 0x86DB, 0xB16D, 0x86DC, 0xB16E, 0x86DD, 0xB16F, 0x86DE,\n\t0xB170, 0x86DF, 0xB171, 0x86E0, 0xB172, 0x86E1, 0xB173, 0x86E2,\n\t0xB174, 0x86E3, 0xB175, 0x86E4, 0xB176, 0x86E5, 0xB177, 0x86E6,\n\t0xB178, 0xB3EB, 0xB179, 0xB3EC, 0xB17A, 0x86E7, 0xB17B, 0x86E8,\n\t0xB17C, 0xB3ED, 0xB17D, 0x86E9, 0xB17E, 0x86EA, 0xB17F, 0x86EB,\n\t0xB180, 0xB3EE, 0xB181, 0x86EC, 0xB182, 0xB3EF, 0xB183, 0x86ED,\n\t0xB184, 0x86EE, 0xB185, 0x86EF, 0xB186, 0x86F0, 0xB187, 0x86F1,\n\t0xB188, 0xB3F0, 0xB189, 0xB3F1, 0xB18A, 0x86F2, 0xB18B, 0xB3F2,\n\t0xB18C, 0x86F3, 0xB18D, 0xB3F3, 0xB18E, 0x86F4, 0xB18F, 0x86F5,\n\t0xB190, 0x86F6, 0xB191, 0x86F7, 0xB192, 0xB3F4, 0xB193, 0xB3F5,\n\t0xB194, 0xB3F6, 0xB195, 0x86F8, 0xB196, 0x86F9, 0xB197, 0x86FA,\n\t0xB198, 0xB3F7, 0xB199, 0x86FB, 0xB19A, 0x86FC, 0xB19B, 0x86FD,\n\t0xB19C, 0xB3F8, 0xB19D, 0x86FE, 0xB19E, 0x8741, 0xB19F, 0x8742,\n\t0xB1A0, 0x8743, 0xB1A1, 0x8744, 0xB1A2, 0x8745, 0xB1A3, 0x8746,\n\t0xB1A4, 0x8747, 0xB1A5, 0x8748, 0xB1A6, 0x8749, 0xB1A7, 0x874A,\n\t0xB1A8, 0xB3F9, 0xB1A9, 0x874B, 0xB1AA, 0x874C, 0xB1AB, 0x874D,\n\t0xB1AC, 0x874E, 0xB1AD, 0x874F, 0xB1AE, 0x8750, 0xB1AF, 0x8751,\n\t0xB1B0, 0x8752, 0xB1B1, 0x8753, 0xB1B2, 0x8754, 0xB1B3, 0x8755,\n\t0xB1B4, 0x8756, 0xB1B5, 0x8757, 0xB1B6, 0x8758, 0xB1B7, 0x8759,\n\t0xB1B8, 0x875A, 0xB1B9, 0x8761, 0xB1BA, 0x8762, 0xB1BB, 0x8763,\n\t0xB1BC, 0x8764, 0xB1BD, 0x8765, 0xB1BE, 0x8766, 0xB1BF, 0x8767,\n\t0xB1C0, 0x8768, 0xB1C1, 0x8769, 0xB1C2, 0x876A, 0xB1C3, 0x876B,\n\t0xB1C4, 0x876C, 0xB1C5, 0x876D, 0xB1C6, 0x876E, 0xB1C7, 0x876F,\n\t0xB1C8, 0x8770, 0xB1C9, 0x8771, 0xB1CA, 0x8772, 0xB1CB, 0x8773,\n\t0xB1CC, 0xB3FA, 0xB1CD, 0x8774, 0xB1CE, 0x8775, 0xB1CF, 0x8776,\n\t0xB1D0, 0xB3FB, 0xB1D1, 0x8777, 0xB1D2, 0x8778, 0xB1D3, 0x8779,\n\t0xB1D4, 0xB3FC, 0xB1D5, 0x877A, 0xB1D6, 0x8781, 0xB1D7, 0x8782,\n\t0xB1D8, 0x8783, 0xB1D9, 0x8784, 0xB1DA, 0x8785, 0xB1DB, 0x8786,\n\t0xB1DC, 0xB3FD, 0xB1DD, 0xB3FE, 0xB1DE, 0x8787, 0xB1DF, 0xB4A1,\n\t0xB1E0, 0x8788, 0xB1E1, 0x8789, 0xB1E2, 0x878A, 0xB1E3, 0x878B,\n\t0xB1E4, 0x878C, 0xB1E5, 0x878D, 0xB1E6, 0x878E, 0xB1E7, 0x878F,\n\t0xB1E8, 0xB4A2, 0xB1E9, 0xB4A3, 0xB1EA, 0x8790, 0xB1EB, 0x8791,\n\t0xB1EC, 0xB4A4, 0xB1ED, 0x8792, 0xB1EE, 0x8793, 0xB1EF, 0x8794,\n\t0xB1F0, 0xB4A5, 0xB1F1, 0x8795, 0xB1F2, 0x8796, 0xB1F3, 0x8797,\n\t0xB1F4, 0x8798, 0xB1F5, 0x8799, 0xB1F6, 0x879A, 0xB1F7, 0x879B,\n\t0xB1F8, 0x879C, 0xB1F9, 0xB4A6, 0xB1FA, 0x879D, 0xB1FB, 0xB4A7,\n\t0xB1FC, 0x879E, 0xB1FD, 0xB4A8, 0xB1FE, 0x879F, 0xB1FF, 0x87A0,\n\t0xB200, 0x87A1, 0xB201, 0x87A2, 0xB202, 0x87A3, 0xB203, 0x87A4,\n\t0xB204, 0xB4A9, 0xB205, 0xB4AA, 0xB206, 0x87A5, 0xB207, 0x87A6,\n\t0xB208, 0xB4AB, 0xB209, 0x87A7, 0xB20A, 0x87A8, 0xB20B, 0xB4AC,\n\t0xB20C, 0xB4AD, 0xB20D, 0x87A9, 0xB20E, 0x87AA, 0xB20F, 0x87AB,\n\t0xB210, 0x87AC, 0xB211, 0x87AD, 0xB212, 0x87AE, 0xB213, 0x87AF,\n\t0xB214, 0xB4AE, 0xB215, 0xB4AF, 0xB216, 0x87B0, 0xB217, 0xB4B0,\n\t0xB218, 0x87B1, 0xB219, 0xB4B1, 0xB21A, 0x87B2, 0xB21B, 0x87B3,\n\t0xB21C, 0x87B4, 0xB21D, 0x87B5, 0xB21E, 0x87B6, 0xB21F, 0x87B7,\n\t0xB220, 0xB4B2, 0xB221, 0x87B8, 0xB222, 0x87B9, 0xB223, 0x87BA,\n\t0xB224, 0x87BB, 0xB225, 0x87BC, 0xB226, 0x87BD, 0xB227, 0x87BE,\n\t0xB228, 0x87BF, 0xB229, 0x87C0, 0xB22A, 0x87C1, 0xB22B, 0x87C2,\n\t0xB22C, 0x87C3, 0xB22D, 0x87C4, 0xB22E, 0x87C5, 0xB22F, 0x87C6,\n\t0xB230, 0x87C7, 0xB231, 0x87C8, 0xB232, 0x87C9, 0xB233, 0x87CA,\n\t0xB234, 0xB4B3, 0xB235, 0x87CB, 0xB236, 0x87CC, 0xB237, 0x87CD,\n\t0xB238, 0x87CE, 0xB239, 0x87CF, 0xB23A, 0x87D0, 0xB23B, 0x87D1,\n\t0xB23C, 0xB4B4, 0xB23D, 0x87D2, 0xB23E, 0x87D3, 0xB23F, 0x87D4,\n\t0xB240, 0x87D5, 0xB241, 0x87D6, 0xB242, 0x87D7, 0xB243, 0x87D8,\n\t0xB244, 0x87D9, 0xB245, 0x87DA, 0xB246, 0x87DB, 0xB247, 0x87DC,\n\t0xB248, 0x87DD, 0xB249, 0x87DE, 0xB24A, 0x87DF, 0xB24B, 0x87E0,\n\t0xB24C, 0x87E1, 0xB24D, 0x87E2, 0xB24E, 0x87E3, 0xB24F, 0x87E4,\n\t0xB250, 0x87E5, 0xB251, 0x87E6, 0xB252, 0x87E7, 0xB253, 0x87E8,\n\t0xB254, 0x87E9, 0xB255, 0x87EA, 0xB256, 0x87EB, 0xB257, 0x87EC,\n\t0xB258, 0xB4B5, 0xB259, 0x87ED, 0xB25A, 0x87EE, 0xB25B, 0x87EF,\n\t0xB25C, 0xB4B6, 0xB25D, 0x87F0, 0xB25E, 0x87F1, 0xB25F, 0x87F2,\n\t0xB260, 0xB4B7, 0xB261, 0x87F3, 0xB262, 0x87F4, 0xB263, 0x87F5,\n\t0xB264, 0x87F6, 0xB265, 0x87F7, 0xB266, 0x87F8, 0xB267, 0x87F9,\n\t0xB268, 0xB4B8, 0xB269, 0xB4B9, 0xB26A, 0x87FA, 0xB26B, 0x87FB,\n\t0xB26C, 0x87FC, 0xB26D, 0x87FD, 0xB26E, 0x87FE, 0xB26F, 0x8841,\n\t0xB270, 0x8842, 0xB271, 0x8843, 0xB272, 0x8844, 0xB273, 0x8845,\n\t0xB274, 0xB4BA, 0xB275, 0xB4BB, 0xB276, 0x8846, 0xB277, 0x8847,\n\t0xB278, 0x8848, 0xB279, 0x8849, 0xB27A, 0x884A, 0xB27B, 0x884B,\n\t0xB27C, 0xB4BC, 0xB27D, 0x884C, 0xB27E, 0x884D, 0xB27F, 0x884E,\n\t0xB280, 0x884F, 0xB281, 0x8850, 0xB282, 0x8851, 0xB283, 0x8852,\n\t0xB284, 0xB4BD, 0xB285, 0xB4BE, 0xB286, 0x8853, 0xB287, 0x8854,\n\t0xB288, 0x8855, 0xB289, 0xB4BF, 0xB28A, 0x8856, 0xB28B, 0x8857,\n\t0xB28C, 0x8858, 0xB28D, 0x8859, 0xB28E, 0x885A, 0xB28F, 0x8861,\n\t0xB290, 0xB4C0, 0xB291, 0xB4C1, 0xB292, 0x8862, 0xB293, 0x8863,\n\t0xB294, 0xB4C2, 0xB295, 0x8864, 0xB296, 0x8865, 0xB297, 0x8866,\n\t0xB298, 0xB4C3, 0xB299, 0xB4C4, 0xB29A, 0xB4C5, 0xB29B, 0x8867,\n\t0xB29C, 0x8868, 0xB29D, 0x8869, 0xB29E, 0x886A, 0xB29F, 0x886B,\n\t0xB2A0, 0xB4C6, 0xB2A1, 0xB4C7, 0xB2A2, 0x886C, 0xB2A3, 0xB4C8,\n\t0xB2A4, 0x886D, 0xB2A5, 0xB4C9, 0xB2A6, 0xB4CA, 0xB2A7, 0x886E,\n\t0xB2A8, 0x886F, 0xB2A9, 0x8870, 0xB2AA, 0xB4CB, 0xB2AB, 0x8871,\n\t0xB2AC, 0xB4CC, 0xB2AD, 0x8872, 0xB2AE, 0x8873, 0xB2AF, 0x8874,\n\t0xB2B0, 0xB4CD, 0xB2B1, 0x8875, 0xB2B2, 0x8876, 0xB2B3, 0x8877,\n\t0xB2B4, 0xB4CE, 0xB2B5, 0x8878, 0xB2B6, 0x8879, 0xB2B7, 0x887A,\n\t0xB2B8, 0x8881, 0xB2B9, 0x8882, 0xB2BA, 0x8883, 0xB2BB, 0x8884,\n\t0xB2BC, 0x8885, 0xB2BD, 0x8886, 0xB2BE, 0x8887, 0xB2BF, 0x8888,\n\t0xB2C0, 0x8889, 0xB2C1, 0x888A, 0xB2C2, 0x888B, 0xB2C3, 0x888C,\n\t0xB2C4, 0x888D, 0xB2C5, 0x888E, 0xB2C6, 0x888F, 0xB2C7, 0x8890,\n\t0xB2C8, 0xB4CF, 0xB2C9, 0xB4D0, 0xB2CA, 0x8891, 0xB2CB, 0x8892,\n\t0xB2CC, 0xB4D1, 0xB2CD, 0x8893, 0xB2CE, 0x8894, 0xB2CF, 0x8895,\n\t0xB2D0, 0xB4D2, 0xB2D1, 0x8896, 0xB2D2, 0xB4D3, 0xB2D3, 0x8897,\n\t0xB2D4, 0x8898, 0xB2D5, 0x8899, 0xB2D6, 0x889A, 0xB2D7, 0x889B,\n\t0xB2D8, 0xB4D4, 0xB2D9, 0xB4D5, 0xB2DA, 0x889C, 0xB2DB, 0xB4D6,\n\t0xB2DC, 0x889D, 0xB2DD, 0xB4D7, 0xB2DE, 0x889E, 0xB2DF, 0x889F,\n\t0xB2E0, 0x88A0, 0xB2E1, 0x88A1, 0xB2E2, 0xB4D8, 0xB2E3, 0x88A2,\n\t0xB2E4, 0xB4D9, 0xB2E5, 0xB4DA, 0xB2E6, 0xB4DB, 0xB2E7, 0x88A3,\n\t0xB2E8, 0xB4DC, 0xB2E9, 0x88A4, 0xB2EA, 0x88A5, 0xB2EB, 0xB4DD,\n\t0xB2EC, 0xB4DE, 0xB2ED, 0xB4DF, 0xB2EE, 0xB4E0, 0xB2EF, 0xB4E1,\n\t0xB2F0, 0x88A6, 0xB2F1, 0x88A7, 0xB2F2, 0x88A8, 0xB2F3, 0xB4E2,\n\t0xB2F4, 0xB4E3, 0xB2F5, 0xB4E4, 0xB2F6, 0x88A9, 0xB2F7, 0xB4E5,\n\t0xB2F8, 0xB4E6, 0xB2F9, 0xB4E7, 0xB2FA, 0xB4E8, 0xB2FB, 0xB4E9,\n\t0xB2FC, 0x88AA, 0xB2FD, 0x88AB, 0xB2FE, 0x88AC, 0xB2FF, 0xB4EA,\n\t0xB300, 0xB4EB, 0xB301, 0xB4EC, 0xB302, 0x88AD, 0xB303, 0x88AE,\n\t0xB304, 0xB4ED, 0xB305, 0x88AF, 0xB306, 0x88B0, 0xB307, 0x88B1,\n\t0xB308, 0xB4EE, 0xB309, 0x88B2, 0xB30A, 0x88B3, 0xB30B, 0x88B4,\n\t0xB30C, 0x88B5, 0xB30D, 0x88B6, 0xB30E, 0x88B7, 0xB30F, 0x88B8,\n\t0xB310, 0xB4EF, 0xB311, 0xB4F0, 0xB312, 0x88B9, 0xB313, 0xB4F1,\n\t0xB314, 0xB4F2, 0xB315, 0xB4F3, 0xB316, 0x88BA, 0xB317, 0x88BB,\n\t0xB318, 0x88BC, 0xB319, 0x88BD, 0xB31A, 0x88BE, 0xB31B, 0x88BF,\n\t0xB31C, 0xB4F4, 0xB31D, 0x88C0, 0xB31E, 0x88C1, 0xB31F, 0x88C2,\n\t0xB320, 0x88C3, 0xB321, 0x88C4, 0xB322, 0x88C5, 0xB323, 0x88C6,\n\t0xB324, 0x88C7, 0xB325, 0x88C8, 0xB326, 0x88C9, 0xB327, 0x88CA,\n\t0xB328, 0x88CB, 0xB329, 0x88CC, 0xB32A, 0x88CD, 0xB32B, 0x88CE,\n\t0xB32C, 0x88CF, 0xB32D, 0x88D0, 0xB32E, 0x88D1, 0xB32F, 0x88D2,\n\t0xB330, 0x88D3, 0xB331, 0x88D4, 0xB332, 0x88D5, 0xB333, 0x88D6,\n\t0xB334, 0x88D7, 0xB335, 0x88D8, 0xB336, 0x88D9, 0xB337, 0x88DA,\n\t0xB338, 0x88DB, 0xB339, 0x88DC, 0xB33A, 0x88DD, 0xB33B, 0x88DE,\n\t0xB33C, 0x88DF, 0xB33D, 0x88E0, 0xB33E, 0x88E1, 0xB33F, 0x88E2,\n\t0xB340, 0x88E3, 0xB341, 0x88E4, 0xB342, 0x88E5, 0xB343, 0x88E6,\n\t0xB344, 0x88E7, 0xB345, 0x88E8, 0xB346, 0x88E9, 0xB347, 0x88EA,\n\t0xB348, 0x88EB, 0xB349, 0x88EC, 0xB34A, 0x88ED, 0xB34B, 0x88EE,\n\t0xB34C, 0x88EF, 0xB34D, 0x88F0, 0xB34E, 0x88F1, 0xB34F, 0x88F2,\n\t0xB350, 0x88F3, 0xB351, 0x88F4, 0xB352, 0x88F5, 0xB353, 0x88F6,\n\t0xB354, 0xB4F5, 0xB355, 0xB4F6, 0xB356, 0xB4F7, 0xB357, 0x88F7,\n\t0xB358, 0xB4F8, 0xB359, 0x88F8, 0xB35A, 0x88F9, 0xB35B, 0xB4F9,\n\t0xB35C, 0xB4FA, 0xB35D, 0x88FA, 0xB35E, 0xB4FB, 0xB35F, 0xB4FC,\n\t0xB360, 0x88FB, 0xB361, 0x88FC, 0xB362, 0x88FD, 0xB363, 0x88FE,\n\t0xB364, 0xB4FD, 0xB365, 0xB4FE, 0xB366, 0x8941, 0xB367, 0xB5A1,\n\t0xB368, 0x8942, 0xB369, 0xB5A2, 0xB36A, 0x8943, 0xB36B, 0xB5A3,\n\t0xB36C, 0x8944, 0xB36D, 0x8945, 0xB36E, 0xB5A4, 0xB36F, 0x8946,\n\t0xB370, 0xB5A5, 0xB371, 0xB5A6, 0xB372, 0x8947, 0xB373, 0x8948,\n\t0xB374, 0xB5A7, 0xB375, 0x8949, 0xB376, 0x894A, 0xB377, 0x894B,\n\t0xB378, 0xB5A8, 0xB379, 0x894C, 0xB37A, 0x894D, 0xB37B, 0x894E,\n\t0xB37C, 0x894F, 0xB37D, 0x8950, 0xB37E, 0x8951, 0xB37F, 0x8952,\n\t0xB380, 0xB5A9, 0xB381, 0xB5AA, 0xB382, 0x8953, 0xB383, 0xB5AB,\n\t0xB384, 0xB5AC, 0xB385, 0xB5AD, 0xB386, 0x8954, 0xB387, 0x8955,\n\t0xB388, 0x8956, 0xB389, 0x8957, 0xB38A, 0x8958, 0xB38B, 0x8959,\n\t0xB38C, 0xB5AE, 0xB38D, 0x895A, 0xB38E, 0x8961, 0xB38F, 0x8962,\n\t0xB390, 0xB5AF, 0xB391, 0x8963, 0xB392, 0x8964, 0xB393, 0x8965,\n\t0xB394, 0xB5B0, 0xB395, 0x8966, 0xB396, 0x8967, 0xB397, 0x8968,\n\t0xB398, 0x8969, 0xB399, 0x896A, 0xB39A, 0x896B, 0xB39B, 0x896C,\n\t0xB39C, 0x896D, 0xB39D, 0x896E, 0xB39E, 0x896F, 0xB39F, 0x8970,\n\t0xB3A0, 0xB5B1, 0xB3A1, 0xB5B2, 0xB3A2, 0x8971, 0xB3A3, 0x8972,\n\t0xB3A4, 0x8973, 0xB3A5, 0x8974, 0xB3A6, 0x8975, 0xB3A7, 0x8976,\n\t0xB3A8, 0xB5B3, 0xB3A9, 0x8977, 0xB3AA, 0x8978, 0xB3AB, 0x8979,\n\t0xB3AC, 0xB5B4, 0xB3AD, 0x897A, 0xB3AE, 0x8981, 0xB3AF, 0x8982,\n\t0xB3B0, 0x8983, 0xB3B1, 0x8984, 0xB3B2, 0x8985, 0xB3B3, 0x8986,\n\t0xB3B4, 0x8987, 0xB3B5, 0x8988, 0xB3B6, 0x8989, 0xB3B7, 0x898A,\n\t0xB3B8, 0x898B, 0xB3B9, 0x898C, 0xB3BA, 0x898D, 0xB3BB, 0x898E,\n\t0xB3BC, 0x898F, 0xB3BD, 0x8990, 0xB3BE, 0x8991, 0xB3BF, 0x8992,\n\t0xB3C0, 0x8993, 0xB3C1, 0x8994, 0xB3C2, 0x8995, 0xB3C3, 0x8996,\n\t0xB3C4, 0xB5B5, 0xB3C5, 0xB5B6, 0xB3C6, 0x8997, 0xB3C7, 0x8998,\n\t0xB3C8, 0xB5B7, 0xB3C9, 0x8999, 0xB3CA, 0x899A, 0xB3CB, 0xB5B8,\n\t0xB3CC, 0xB5B9, 0xB3CD, 0x899B, 0xB3CE, 0xB5BA, 0xB3CF, 0x899C,\n\t0xB3D0, 0xB5BB, 0xB3D1, 0x899D, 0xB3D2, 0x899E, 0xB3D3, 0x899F,\n\t0xB3D4, 0xB5BC, 0xB3D5, 0xB5BD, 0xB3D6, 0x89A0, 0xB3D7, 0xB5BE,\n\t0xB3D8, 0x89A1, 0xB3D9, 0xB5BF, 0xB3DA, 0x89A2, 0xB3DB, 0xB5C0,\n\t0xB3DC, 0x89A3, 0xB3DD, 0xB5C1, 0xB3DE, 0x89A4, 0xB3DF, 0x89A5,\n\t0xB3E0, 0xB5C2, 0xB3E1, 0x89A6, 0xB3E2, 0x89A7, 0xB3E3, 0x89A8,\n\t0xB3E4, 0xB5C3, 0xB3E5, 0x89A9, 0xB3E6, 0x89AA, 0xB3E7, 0x89AB,\n\t0xB3E8, 0xB5C4, 0xB3E9, 0x89AC, 0xB3EA, 0x89AD, 0xB3EB, 0x89AE,\n\t0xB3EC, 0x89AF, 0xB3ED, 0x89B0, 0xB3EE, 0x89B1, 0xB3EF, 0x89B2,\n\t0xB3F0, 0x89B3, 0xB3F1, 0x89B4, 0xB3F2, 0x89B5, 0xB3F3, 0x89B6,\n\t0xB3F4, 0x89B7, 0xB3F5, 0x89B8, 0xB3F6, 0x89B9, 0xB3F7, 0x89BA,\n\t0xB3F8, 0x89BB, 0xB3F9, 0x89BC, 0xB3FA, 0x89BD, 0xB3FB, 0x89BE,\n\t0xB3FC, 0xB5C5, 0xB3FD, 0x89BF, 0xB3FE, 0x89C0, 0xB3FF, 0x89C1,\n\t0xB400, 0x89C2, 0xB401, 0x89C3, 0xB402, 0x89C4, 0xB403, 0x89C5,\n\t0xB404, 0x89C6, 0xB405, 0x89C7, 0xB406, 0x89C8, 0xB407, 0x89C9,\n\t0xB408, 0x89CA, 0xB409, 0x89CB, 0xB40A, 0x89CC, 0xB40B, 0x89CD,\n\t0xB40C, 0x89CE, 0xB40D, 0x89CF, 0xB40E, 0x89D0, 0xB40F, 0x89D1,\n\t0xB410, 0xB5C6, 0xB411, 0x89D2, 0xB412, 0x89D3, 0xB413, 0x89D4,\n\t0xB414, 0x89D5, 0xB415, 0x89D6, 0xB416, 0x89D7, 0xB417, 0x89D8,\n\t0xB418, 0xB5C7, 0xB419, 0x89D9, 0xB41A, 0x89DA, 0xB41B, 0x89DB,\n\t0xB41C, 0xB5C8, 0xB41D, 0x89DC, 0xB41E, 0x89DD, 0xB41F, 0x89DE,\n\t0xB420, 0xB5C9, 0xB421, 0x89DF, 0xB422, 0x89E0, 0xB423, 0x89E1,\n\t0xB424, 0x89E2, 0xB425, 0x89E3, 0xB426, 0x89E4, 0xB427, 0x89E5,\n\t0xB428, 0xB5CA, 0xB429, 0xB5CB, 0xB42A, 0x89E6, 0xB42B, 0xB5CC,\n\t0xB42C, 0x89E7, 0xB42D, 0x89E8, 0xB42E, 0x89E9, 0xB42F, 0x89EA,\n\t0xB430, 0x89EB, 0xB431, 0x89EC, 0xB432, 0x89ED, 0xB433, 0x89EE,\n\t0xB434, 0xB5CD, 0xB435, 0x89EF, 0xB436, 0x89F0, 0xB437, 0x89F1,\n\t0xB438, 0x89F2, 0xB439, 0x89F3, 0xB43A, 0x89F4, 0xB43B, 0x89F5,\n\t0xB43C, 0x89F6, 0xB43D, 0x89F7, 0xB43E, 0x89F8, 0xB43F, 0x89F9,\n\t0xB440, 0x89FA, 0xB441, 0x89FB, 0xB442, 0x89FC, 0xB443, 0x89FD,\n\t0xB444, 0x89FE, 0xB445, 0x8A41, 0xB446, 0x8A42, 0xB447, 0x8A43,\n\t0xB448, 0x8A44, 0xB449, 0x8A45, 0xB44A, 0x8A46, 0xB44B, 0x8A47,\n\t0xB44C, 0x8A48, 0xB44D, 0x8A49, 0xB44E, 0x8A4A, 0xB44F, 0x8A4B,\n\t0xB450, 0xB5CE, 0xB451, 0xB5CF, 0xB452, 0x8A4C, 0xB453, 0x8A4D,\n\t0xB454, 0xB5D0, 0xB455, 0x8A4E, 0xB456, 0x8A4F, 0xB457, 0x8A50,\n\t0xB458, 0xB5D1, 0xB459, 0x8A51, 0xB45A, 0x8A52, 0xB45B, 0x8A53,\n\t0xB45C, 0x8A54, 0xB45D, 0x8A55, 0xB45E, 0x8A56, 0xB45F, 0x8A57,\n\t0xB460, 0xB5D2, 0xB461, 0xB5D3, 0xB462, 0x8A58, 0xB463, 0xB5D4,\n\t0xB464, 0x8A59, 0xB465, 0xB5D5, 0xB466, 0x8A5A, 0xB467, 0x8A61,\n\t0xB468, 0x8A62, 0xB469, 0x8A63, 0xB46A, 0x8A64, 0xB46B, 0x8A65,\n\t0xB46C, 0xB5D6, 0xB46D, 0x8A66, 0xB46E, 0x8A67, 0xB46F, 0x8A68,\n\t0xB470, 0x8A69, 0xB471, 0x8A6A, 0xB472, 0x8A6B, 0xB473, 0x8A6C,\n\t0xB474, 0x8A6D, 0xB475, 0x8A6E, 0xB476, 0x8A6F, 0xB477, 0x8A70,\n\t0xB478, 0x8A71, 0xB479, 0x8A72, 0xB47A, 0x8A73, 0xB47B, 0x8A74,\n\t0xB47C, 0x8A75, 0xB47D, 0x8A76, 0xB47E, 0x8A77, 0xB47F, 0x8A78,\n\t0xB480, 0xB5D7, 0xB481, 0x8A79, 0xB482, 0x8A7A, 0xB483, 0x8A81,\n\t0xB484, 0x8A82, 0xB485, 0x8A83, 0xB486, 0x8A84, 0xB487, 0x8A85,\n\t0xB488, 0xB5D8, 0xB489, 0x8A86, 0xB48A, 0x8A87, 0xB48B, 0x8A88,\n\t0xB48C, 0x8A89, 0xB48D, 0x8A8A, 0xB48E, 0x8A8B, 0xB48F, 0x8A8C,\n\t0xB490, 0x8A8D, 0xB491, 0x8A8E, 0xB492, 0x8A8F, 0xB493, 0x8A90,\n\t0xB494, 0x8A91, 0xB495, 0x8A92, 0xB496, 0x8A93, 0xB497, 0x8A94,\n\t0xB498, 0x8A95, 0xB499, 0x8A96, 0xB49A, 0x8A97, 0xB49B, 0x8A98,\n\t0xB49C, 0x8A99, 0xB49D, 0xB5D9, 0xB49E, 0x8A9A, 0xB49F, 0x8A9B,\n\t0xB4A0, 0x8A9C, 0xB4A1, 0x8A9D, 0xB4A2, 0x8A9E, 0xB4A3, 0x8A9F,\n\t0xB4A4, 0xB5DA, 0xB4A5, 0x8AA0, 0xB4A6, 0x8AA1, 0xB4A7, 0x8AA2,\n\t0xB4A8, 0xB5DB, 0xB4A9, 0x8AA3, 0xB4AA, 0x8AA4, 0xB4AB, 0x8AA5,\n\t0xB4AC, 0xB5DC, 0xB4AD, 0x8AA6, 0xB4AE, 0x8AA7, 0xB4AF, 0x8AA8,\n\t0xB4B0, 0x8AA9, 0xB4B1, 0x8AAA, 0xB4B2, 0x8AAB, 0xB4B3, 0x8AAC,\n\t0xB4B4, 0x8AAD, 0xB4B5, 0xB5DD, 0xB4B6, 0x8AAE, 0xB4B7, 0xB5DE,\n\t0xB4B8, 0x8AAF, 0xB4B9, 0xB5DF, 0xB4BA, 0x8AB0, 0xB4BB, 0x8AB1,\n\t0xB4BC, 0x8AB2, 0xB4BD, 0x8AB3, 0xB4BE, 0x8AB4, 0xB4BF, 0x8AB5,\n\t0xB4C0, 0xB5E0, 0xB4C1, 0x8AB6, 0xB4C2, 0x8AB7, 0xB4C3, 0x8AB8,\n\t0xB4C4, 0xB5E1, 0xB4C5, 0x8AB9, 0xB4C6, 0x8ABA, 0xB4C7, 0x8ABB,\n\t0xB4C8, 0xB5E2, 0xB4C9, 0x8ABC, 0xB4CA, 0x8ABD, 0xB4CB, 0x8ABE,\n\t0xB4CC, 0x8ABF, 0xB4CD, 0x8AC0, 0xB4CE, 0x8AC1, 0xB4CF, 0x8AC2,\n\t0xB4D0, 0xB5E3, 0xB4D1, 0x8AC3, 0xB4D2, 0x8AC4, 0xB4D3, 0x8AC5,\n\t0xB4D4, 0x8AC6, 0xB4D5, 0xB5E4, 0xB4D6, 0x8AC7, 0xB4D7, 0x8AC8,\n\t0xB4D8, 0x8AC9, 0xB4D9, 0x8ACA, 0xB4DA, 0x8ACB, 0xB4DB, 0x8ACC,\n\t0xB4DC, 0xB5E5, 0xB4DD, 0xB5E6, 0xB4DE, 0x8ACD, 0xB4DF, 0x8ACE,\n\t0xB4E0, 0xB5E7, 0xB4E1, 0x8ACF, 0xB4E2, 0x8AD0, 0xB4E3, 0xB5E8,\n\t0xB4E4, 0xB5E9, 0xB4E5, 0x8AD1, 0xB4E6, 0xB5EA, 0xB4E7, 0x8AD2,\n\t0xB4E8, 0x8AD3, 0xB4E9, 0x8AD4, 0xB4EA, 0x8AD5, 0xB4EB, 0x8AD6,\n\t0xB4EC, 0xB5EB, 0xB4ED, 0xB5EC, 0xB4EE, 0x8AD7, 0xB4EF, 0xB5ED,\n\t0xB4F0, 0x8AD8, 0xB4F1, 0xB5EE, 0xB4F2, 0x8AD9, 0xB4F3, 0x8ADA,\n\t0xB4F4, 0x8ADB, 0xB4F5, 0x8ADC, 0xB4F6, 0x8ADD, 0xB4F7, 0x8ADE,\n\t0xB4F8, 0xB5EF, 0xB4F9, 0x8ADF, 0xB4FA, 0x8AE0, 0xB4FB, 0x8AE1,\n\t0xB4FC, 0x8AE2, 0xB4FD, 0x8AE3, 0xB4FE, 0x8AE4, 0xB4FF, 0x8AE5,\n\t0xB500, 0x8AE6, 0xB501, 0x8AE7, 0xB502, 0x8AE8, 0xB503, 0x8AE9,\n\t0xB504, 0x8AEA, 0xB505, 0x8AEB, 0xB506, 0x8AEC, 0xB507, 0x8AED,\n\t0xB508, 0x8AEE, 0xB509, 0x8AEF, 0xB50A, 0x8AF0, 0xB50B, 0x8AF1,\n\t0xB50C, 0x8AF2, 0xB50D, 0x8AF3, 0xB50E, 0x8AF4, 0xB50F, 0x8AF5,\n\t0xB510, 0x8AF6, 0xB511, 0x8AF7, 0xB512, 0x8AF8, 0xB513, 0x8AF9,\n\t0xB514, 0xB5F0, 0xB515, 0xB5F1, 0xB516, 0x8AFA, 0xB517, 0x8AFB,\n\t0xB518, 0xB5F2, 0xB519, 0x8AFC, 0xB51A, 0x8AFD, 0xB51B, 0xB5F3,\n\t0xB51C, 0xB5F4, 0xB51D, 0x8AFE, 0xB51E, 0x8B41, 0xB51F, 0x8B42,\n\t0xB520, 0x8B43, 0xB521, 0x8B44, 0xB522, 0x8B45, 0xB523, 0x8B46,\n\t0xB524, 0xB5F5, 0xB525, 0xB5F6, 0xB526, 0x8B47, 0xB527, 0xB5F7,\n\t0xB528, 0xB5F8, 0xB529, 0xB5F9, 0xB52A, 0xB5FA, 0xB52B, 0x8B48,\n\t0xB52C, 0x8B49, 0xB52D, 0x8B4A, 0xB52E, 0x8B4B, 0xB52F, 0x8B4C,\n\t0xB530, 0xB5FB, 0xB531, 0xB5FC, 0xB532, 0x8B4D, 0xB533, 0x8B4E,\n\t0xB534, 0xB5FD, 0xB535, 0x8B4F, 0xB536, 0x8B50, 0xB537, 0x8B51,\n\t0xB538, 0xB5FE, 0xB539, 0x8B52, 0xB53A, 0x8B53, 0xB53B, 0x8B54,\n\t0xB53C, 0x8B55, 0xB53D, 0x8B56, 0xB53E, 0x8B57, 0xB53F, 0x8B58,\n\t0xB540, 0xB6A1, 0xB541, 0xB6A2, 0xB542, 0x8B59, 0xB543, 0xB6A3,\n\t0xB544, 0xB6A4, 0xB545, 0xB6A5, 0xB546, 0x8B5A, 0xB547, 0x8B61,\n\t0xB548, 0x8B62, 0xB549, 0x8B63, 0xB54A, 0x8B64, 0xB54B, 0xB6A6,\n\t0xB54C, 0xB6A7, 0xB54D, 0xB6A8, 0xB54E, 0x8B65, 0xB54F, 0x8B66,\n\t0xB550, 0xB6A9, 0xB551, 0x8B67, 0xB552, 0x8B68, 0xB553, 0x8B69,\n\t0xB554, 0xB6AA, 0xB555, 0x8B6A, 0xB556, 0x8B6B, 0xB557, 0x8B6C,\n\t0xB558, 0x8B6D, 0xB559, 0x8B6E, 0xB55A, 0x8B6F, 0xB55B, 0x8B70,\n\t0xB55C, 0xB6AB, 0xB55D, 0xB6AC, 0xB55E, 0x8B71, 0xB55F, 0xB6AD,\n\t0xB560, 0xB6AE, 0xB561, 0xB6AF, 0xB562, 0x8B72, 0xB563, 0x8B73,\n\t0xB564, 0x8B74, 0xB565, 0x8B75, 0xB566, 0x8B76, 0xB567, 0x8B77,\n\t0xB568, 0x8B78, 0xB569, 0x8B79, 0xB56A, 0x8B7A, 0xB56B, 0x8B81,\n\t0xB56C, 0x8B82, 0xB56D, 0x8B83, 0xB56E, 0x8B84, 0xB56F, 0x8B85,\n\t0xB570, 0x8B86, 0xB571, 0x8B87, 0xB572, 0x8B88, 0xB573, 0x8B89,\n\t0xB574, 0x8B8A, 0xB575, 0x8B8B, 0xB576, 0x8B8C, 0xB577, 0x8B8D,\n\t0xB578, 0x8B8E, 0xB579, 0x8B8F, 0xB57A, 0x8B90, 0xB57B, 0x8B91,\n\t0xB57C, 0x8B92, 0xB57D, 0x8B93, 0xB57E, 0x8B94, 0xB57F, 0x8B95,\n\t0xB580, 0x8B96, 0xB581, 0x8B97, 0xB582, 0x8B98, 0xB583, 0x8B99,\n\t0xB584, 0x8B9A, 0xB585, 0x8B9B, 0xB586, 0x8B9C, 0xB587, 0x8B9D,\n\t0xB588, 0x8B9E, 0xB589, 0x8B9F, 0xB58A, 0x8BA0, 0xB58B, 0x8BA1,\n\t0xB58C, 0x8BA2, 0xB58D, 0x8BA3, 0xB58E, 0x8BA4, 0xB58F, 0x8BA5,\n\t0xB590, 0x8BA6, 0xB591, 0x8BA7, 0xB592, 0x8BA8, 0xB593, 0x8BA9,\n\t0xB594, 0x8BAA, 0xB595, 0x8BAB, 0xB596, 0x8BAC, 0xB597, 0x8BAD,\n\t0xB598, 0x8BAE, 0xB599, 0x8BAF, 0xB59A, 0x8BB0, 0xB59B, 0x8BB1,\n\t0xB59C, 0x8BB2, 0xB59D, 0x8BB3, 0xB59E, 0x8BB4, 0xB59F, 0x8BB5,\n\t0xB5A0, 0xB6B0, 0xB5A1, 0xB6B1, 0xB5A2, 0x8BB6, 0xB5A3, 0x8BB7,\n\t0xB5A4, 0xB6B2, 0xB5A5, 0x8BB8, 0xB5A6, 0x8BB9, 0xB5A7, 0x8BBA,\n\t0xB5A8, 0xB6B3, 0xB5A9, 0x8BBB, 0xB5AA, 0xB6B4, 0xB5AB, 0xB6B5,\n\t0xB5AC, 0x8BBC, 0xB5AD, 0x8BBD, 0xB5AE, 0x8BBE, 0xB5AF, 0x8BBF,\n\t0xB5B0, 0xB6B6, 0xB5B1, 0xB6B7, 0xB5B2, 0x8BC0, 0xB5B3, 0xB6B8,\n\t0xB5B4, 0xB6B9, 0xB5B5, 0xB6BA, 0xB5B6, 0x8BC1, 0xB5B7, 0x8BC2,\n\t0xB5B8, 0x8BC3, 0xB5B9, 0x8BC4, 0xB5BA, 0x8BC5, 0xB5BB, 0xB6BB,\n\t0xB5BC, 0xB6BC, 0xB5BD, 0xB6BD, 0xB5BE, 0x8BC6, 0xB5BF, 0x8BC7,\n\t0xB5C0, 0xB6BE, 0xB5C1, 0x8BC8, 0xB5C2, 0x8BC9, 0xB5C3, 0x8BCA,\n\t0xB5C4, 0xB6BF, 0xB5C5, 0x8BCB, 0xB5C6, 0x8BCC, 0xB5C7, 0x8BCD,\n\t0xB5C8, 0x8BCE, 0xB5C9, 0x8BCF, 0xB5CA, 0x8BD0, 0xB5CB, 0x8BD1,\n\t0xB5CC, 0xB6C0, 0xB5CD, 0xB6C1, 0xB5CE, 0x8BD2, 0xB5CF, 0xB6C2,\n\t0xB5D0, 0xB6C3, 0xB5D1, 0xB6C4, 0xB5D2, 0x8BD3, 0xB5D3, 0x8BD4,\n\t0xB5D4, 0x8BD5, 0xB5D5, 0x8BD6, 0xB5D6, 0x8BD7, 0xB5D7, 0x8BD8,\n\t0xB5D8, 0xB6C5, 0xB5D9, 0x8BD9, 0xB5DA, 0x8BDA, 0xB5DB, 0x8BDB,\n\t0xB5DC, 0x8BDC, 0xB5DD, 0x8BDD, 0xB5DE, 0x8BDE, 0xB5DF, 0x8BDF,\n\t0xB5E0, 0x8BE0, 0xB5E1, 0x8BE1, 0xB5E2, 0x8BE2, 0xB5E3, 0x8BE3,\n\t0xB5E4, 0x8BE4, 0xB5E5, 0x8BE5, 0xB5E6, 0x8BE6, 0xB5E7, 0x8BE7,\n\t0xB5E8, 0x8BE8, 0xB5E9, 0x8BE9, 0xB5EA, 0x8BEA, 0xB5EB, 0x8BEB,\n\t0xB5EC, 0xB6C6, 0xB5ED, 0x8BEC, 0xB5EE, 0x8BED, 0xB5EF, 0x8BEE,\n\t0xB5F0, 0x8BEF, 0xB5F1, 0x8BF0, 0xB5F2, 0x8BF1, 0xB5F3, 0x8BF2,\n\t0xB5F4, 0x8BF3, 0xB5F5, 0x8BF4, 0xB5F6, 0x8BF5, 0xB5F7, 0x8BF6,\n\t0xB5F8, 0x8BF7, 0xB5F9, 0x8BF8, 0xB5FA, 0x8BF9, 0xB5FB, 0x8BFA,\n\t0xB5FC, 0x8BFB, 0xB5FD, 0x8BFC, 0xB5FE, 0x8BFD, 0xB5FF, 0x8BFE,\n\t0xB600, 0x8C41, 0xB601, 0x8C42, 0xB602, 0x8C43, 0xB603, 0x8C44,\n\t0xB604, 0x8C45, 0xB605, 0x8C46, 0xB606, 0x8C47, 0xB607, 0x8C48,\n\t0xB608, 0x8C49, 0xB609, 0x8C4A, 0xB60A, 0x8C4B, 0xB60B, 0x8C4C,\n\t0xB60C, 0x8C4D, 0xB60D, 0x8C4E, 0xB60E, 0x8C4F, 0xB60F, 0x8C50,\n\t0xB610, 0xB6C7, 0xB611, 0xB6C8, 0xB612, 0x8C51, 0xB613, 0x8C52,\n\t0xB614, 0xB6C9, 0xB615, 0x8C53, 0xB616, 0x8C54, 0xB617, 0x8C55,\n\t0xB618, 0xB6CA, 0xB619, 0x8C56, 0xB61A, 0x8C57, 0xB61B, 0x8C58,\n\t0xB61C, 0x8C59, 0xB61D, 0x8C5A, 0xB61E, 0x8C61, 0xB61F, 0x8C62,\n\t0xB620, 0x8C63, 0xB621, 0x8C64, 0xB622, 0x8C65, 0xB623, 0x8C66,\n\t0xB624, 0x8C67, 0xB625, 0xB6CB, 0xB626, 0x8C68, 0xB627, 0x8C69,\n\t0xB628, 0x8C6A, 0xB629, 0x8C6B, 0xB62A, 0x8C6C, 0xB62B, 0x8C6D,\n\t0xB62C, 0xB6CC, 0xB62D, 0x8C6E, 0xB62E, 0x8C6F, 0xB62F, 0x8C70,\n\t0xB630, 0x8C71, 0xB631, 0x8C72, 0xB632, 0x8C73, 0xB633, 0x8C74,\n\t0xB634, 0xB6CD, 0xB635, 0x8C75, 0xB636, 0x8C76, 0xB637, 0x8C77,\n\t0xB638, 0x8C78, 0xB639, 0x8C79, 0xB63A, 0x8C7A, 0xB63B, 0x8C81,\n\t0xB63C, 0x8C82, 0xB63D, 0x8C83, 0xB63E, 0x8C84, 0xB63F, 0x8C85,\n\t0xB640, 0x8C86, 0xB641, 0x8C87, 0xB642, 0x8C88, 0xB643, 0x8C89,\n\t0xB644, 0x8C8A, 0xB645, 0x8C8B, 0xB646, 0x8C8C, 0xB647, 0x8C8D,\n\t0xB648, 0xB6CE, 0xB649, 0x8C8E, 0xB64A, 0x8C8F, 0xB64B, 0x8C90,\n\t0xB64C, 0x8C91, 0xB64D, 0x8C92, 0xB64E, 0x8C93, 0xB64F, 0x8C94,\n\t0xB650, 0x8C95, 0xB651, 0x8C96, 0xB652, 0x8C97, 0xB653, 0x8C98,\n\t0xB654, 0x8C99, 0xB655, 0x8C9A, 0xB656, 0x8C9B, 0xB657, 0x8C9C,\n\t0xB658, 0x8C9D, 0xB659, 0x8C9E, 0xB65A, 0x8C9F, 0xB65B, 0x8CA0,\n\t0xB65C, 0x8CA1, 0xB65D, 0x8CA2, 0xB65E, 0x8CA3, 0xB65F, 0x8CA4,\n\t0xB660, 0x8CA5, 0xB661, 0x8CA6, 0xB662, 0x8CA7, 0xB663, 0x8CA8,\n\t0xB664, 0xB6CF, 0xB665, 0x8CA9, 0xB666, 0x8CAA, 0xB667, 0x8CAB,\n\t0xB668, 0xB6D0, 0xB669, 0x8CAC, 0xB66A, 0x8CAD, 0xB66B, 0x8CAE,\n\t0xB66C, 0x8CAF, 0xB66D, 0x8CB0, 0xB66E, 0x8CB1, 0xB66F, 0x8CB2,\n\t0xB670, 0x8CB3, 0xB671, 0x8CB4, 0xB672, 0x8CB5, 0xB673, 0x8CB6,\n\t0xB674, 0x8CB7, 0xB675, 0x8CB8, 0xB676, 0x8CB9, 0xB677, 0x8CBA,\n\t0xB678, 0x8CBB, 0xB679, 0x8CBC, 0xB67A, 0x8CBD, 0xB67B, 0x8CBE,\n\t0xB67C, 0x8CBF, 0xB67D, 0x8CC0, 0xB67E, 0x8CC1, 0xB67F, 0x8CC2,\n\t0xB680, 0x8CC3, 0xB681, 0x8CC4, 0xB682, 0x8CC5, 0xB683, 0x8CC6,\n\t0xB684, 0x8CC7, 0xB685, 0x8CC8, 0xB686, 0x8CC9, 0xB687, 0x8CCA,\n\t0xB688, 0x8CCB, 0xB689, 0x8CCC, 0xB68A, 0x8CCD, 0xB68B, 0x8CCE,\n\t0xB68C, 0x8CCF, 0xB68D, 0x8CD0, 0xB68E, 0x8CD1, 0xB68F, 0x8CD2,\n\t0xB690, 0x8CD3, 0xB691, 0x8CD4, 0xB692, 0x8CD5, 0xB693, 0x8CD6,\n\t0xB694, 0x8CD7, 0xB695, 0x8CD8, 0xB696, 0x8CD9, 0xB697, 0x8CDA,\n\t0xB698, 0x8CDB, 0xB699, 0x8CDC, 0xB69A, 0x8CDD, 0xB69B, 0x8CDE,\n\t0xB69C, 0xB6D1, 0xB69D, 0xB6D2, 0xB69E, 0x8CDF, 0xB69F, 0x8CE0,\n\t0xB6A0, 0xB6D3, 0xB6A1, 0x8CE1, 0xB6A2, 0x8CE2, 0xB6A3, 0x8CE3,\n\t0xB6A4, 0xB6D4, 0xB6A5, 0x8CE4, 0xB6A6, 0x8CE5, 0xB6A7, 0x8CE6,\n\t0xB6A8, 0x8CE7, 0xB6A9, 0x8CE8, 0xB6AA, 0x8CE9, 0xB6AB, 0xB6D5,\n\t0xB6AC, 0xB6D6, 0xB6AD, 0x8CEA, 0xB6AE, 0x8CEB, 0xB6AF, 0x8CEC,\n\t0xB6B0, 0x8CED, 0xB6B1, 0xB6D7, 0xB6B2, 0x8CEE, 0xB6B3, 0x8CEF,\n\t0xB6B4, 0x8CF0, 0xB6B5, 0x8CF1, 0xB6B6, 0x8CF2, 0xB6B7, 0x8CF3,\n\t0xB6B8, 0x8CF4, 0xB6B9, 0x8CF5, 0xB6BA, 0x8CF6, 0xB6BB, 0x8CF7,\n\t0xB6BC, 0x8CF8, 0xB6BD, 0x8CF9, 0xB6BE, 0x8CFA, 0xB6BF, 0x8CFB,\n\t0xB6C0, 0x8CFC, 0xB6C1, 0x8CFD, 0xB6C2, 0x8CFE, 0xB6C3, 0x8D41,\n\t0xB6C4, 0x8D42, 0xB6C5, 0x8D43, 0xB6C6, 0x8D44, 0xB6C7, 0x8D45,\n\t0xB6C8, 0x8D46, 0xB6C9, 0x8D47, 0xB6CA, 0x8D48, 0xB6CB, 0x8D49,\n\t0xB6CC, 0x8D4A, 0xB6CD, 0x8D4B, 0xB6CE, 0x8D4C, 0xB6CF, 0x8D4D,\n\t0xB6D0, 0x8D4E, 0xB6D1, 0x8D4F, 0xB6D2, 0x8D50, 0xB6D3, 0x8D51,\n\t0xB6D4, 0xB6D8, 0xB6D5, 0x8D52, 0xB6D6, 0x8D53, 0xB6D7, 0x8D54,\n\t0xB6D8, 0x8D55, 0xB6D9, 0x8D56, 0xB6DA, 0x8D57, 0xB6DB, 0x8D58,\n\t0xB6DC, 0x8D59, 0xB6DD, 0x8D5A, 0xB6DE, 0x8D61, 0xB6DF, 0x8D62,\n\t0xB6E0, 0x8D63, 0xB6E1, 0x8D64, 0xB6E2, 0x8D65, 0xB6E3, 0x8D66,\n\t0xB6E4, 0x8D67, 0xB6E5, 0x8D68, 0xB6E6, 0x8D69, 0xB6E7, 0x8D6A,\n\t0xB6E8, 0x8D6B, 0xB6E9, 0x8D6C, 0xB6EA, 0x8D6D, 0xB6EB, 0x8D6E,\n\t0xB6EC, 0x8D6F, 0xB6ED, 0x8D70, 0xB6EE, 0x8D71, 0xB6EF, 0x8D72,\n\t0xB6F0, 0xB6D9, 0xB6F1, 0x8D73, 0xB6F2, 0x8D74, 0xB6F3, 0x8D75,\n\t0xB6F4, 0xB6DA, 0xB6F5, 0x8D76, 0xB6F6, 0x8D77, 0xB6F7, 0x8D78,\n\t0xB6F8, 0xB6DB, 0xB6F9, 0x8D79, 0xB6FA, 0x8D7A, 0xB6FB, 0x8D81,\n\t0xB6FC, 0x8D82, 0xB6FD, 0x8D83, 0xB6FE, 0x8D84, 0xB6FF, 0x8D85,\n\t0xB700, 0xB6DC, 0xB701, 0xB6DD, 0xB702, 0x8D86, 0xB703, 0x8D87,\n\t0xB704, 0x8D88, 0xB705, 0xB6DE, 0xB706, 0x8D89, 0xB707, 0x8D8A,\n\t0xB708, 0x8D8B, 0xB709, 0x8D8C, 0xB70A, 0x8D8D, 0xB70B, 0x8D8E,\n\t0xB70C, 0x8D8F, 0xB70D, 0x8D90, 0xB70E, 0x8D91, 0xB70F, 0x8D92,\n\t0xB710, 0x8D93, 0xB711, 0x8D94, 0xB712, 0x8D95, 0xB713, 0x8D96,\n\t0xB714, 0x8D97, 0xB715, 0x8D98, 0xB716, 0x8D99, 0xB717, 0x8D9A,\n\t0xB718, 0x8D9B, 0xB719, 0x8D9C, 0xB71A, 0x8D9D, 0xB71B, 0x8D9E,\n\t0xB71C, 0x8D9F, 0xB71D, 0x8DA0, 0xB71E, 0x8DA1, 0xB71F, 0x8DA2,\n\t0xB720, 0x8DA3, 0xB721, 0x8DA4, 0xB722, 0x8DA5, 0xB723, 0x8DA6,\n\t0xB724, 0x8DA7, 0xB725, 0x8DA8, 0xB726, 0x8DA9, 0xB727, 0x8DAA,\n\t0xB728, 0xB6DF, 0xB729, 0xB6E0, 0xB72A, 0x8DAB, 0xB72B, 0x8DAC,\n\t0xB72C, 0xB6E1, 0xB72D, 0x8DAD, 0xB72E, 0x8DAE, 0xB72F, 0xB6E2,\n\t0xB730, 0xB6E3, 0xB731, 0x8DAF, 0xB732, 0x8DB0, 0xB733, 0x8DB1,\n\t0xB734, 0x8DB2, 0xB735, 0x8DB3, 0xB736, 0x8DB4, 0xB737, 0x8DB5,\n\t0xB738, 0xB6E4, 0xB739, 0xB6E5, 0xB73A, 0x8DB6, 0xB73B, 0xB6E6,\n\t0xB73C, 0x8DB7, 0xB73D, 0x8DB8, 0xB73E, 0x8DB9, 0xB73F, 0x8DBA,\n\t0xB740, 0x8DBB, 0xB741, 0x8DBC, 0xB742, 0x8DBD, 0xB743, 0x8DBE,\n\t0xB744, 0xB6E7, 0xB745, 0x8DBF, 0xB746, 0x8DC0, 0xB747, 0x8DC1,\n\t0xB748, 0xB6E8, 0xB749, 0x8DC2, 0xB74A, 0x8DC3, 0xB74B, 0x8DC4,\n\t0xB74C, 0xB6E9, 0xB74D, 0x8DC5, 0xB74E, 0x8DC6, 0xB74F, 0x8DC7,\n\t0xB750, 0x8DC8, 0xB751, 0x8DC9, 0xB752, 0x8DCA, 0xB753, 0x8DCB,\n\t0xB754, 0xB6EA, 0xB755, 0xB6EB, 0xB756, 0x8DCC, 0xB757, 0x8DCD,\n\t0xB758, 0x8DCE, 0xB759, 0x8DCF, 0xB75A, 0x8DD0, 0xB75B, 0x8DD1,\n\t0xB75C, 0x8DD2, 0xB75D, 0x8DD3, 0xB75E, 0x8DD4, 0xB75F, 0x8DD5,\n\t0xB760, 0xB6EC, 0xB761, 0x8DD6, 0xB762, 0x8DD7, 0xB763, 0x8DD8,\n\t0xB764, 0xB6ED, 0xB765, 0x8DD9, 0xB766, 0x8DDA, 0xB767, 0x8DDB,\n\t0xB768, 0xB6EE, 0xB769, 0x8DDC, 0xB76A, 0x8DDD, 0xB76B, 0x8DDE,\n\t0xB76C, 0x8DDF, 0xB76D, 0x8DE0, 0xB76E, 0x8DE1, 0xB76F, 0x8DE2,\n\t0xB770, 0xB6EF, 0xB771, 0xB6F0, 0xB772, 0x8DE3, 0xB773, 0xB6F1,\n\t0xB774, 0x8DE4, 0xB775, 0xB6F2, 0xB776, 0x8DE5, 0xB777, 0x8DE6,\n\t0xB778, 0x8DE7, 0xB779, 0x8DE8, 0xB77A, 0x8DE9, 0xB77B, 0x8DEA,\n\t0xB77C, 0xB6F3, 0xB77D, 0xB6F4, 0xB77E, 0x8DEB, 0xB77F, 0x8DEC,\n\t0xB780, 0xB6F5, 0xB781, 0x8DED, 0xB782, 0x8DEE, 0xB783, 0x8DEF,\n\t0xB784, 0xB6F6, 0xB785, 0x8DF0, 0xB786, 0x8DF1, 0xB787, 0x8DF2,\n\t0xB788, 0x8DF3, 0xB789, 0x8DF4, 0xB78A, 0x8DF5, 0xB78B, 0x8DF6,\n\t0xB78C, 0xB6F7, 0xB78D, 0xB6F8, 0xB78E, 0x8DF7, 0xB78F, 0xB6F9,\n\t0xB790, 0xB6FA, 0xB791, 0xB6FB, 0xB792, 0xB6FC, 0xB793, 0x8DF8,\n\t0xB794, 0x8DF9, 0xB795, 0x8DFA, 0xB796, 0xB6FD, 0xB797, 0xB6FE,\n\t0xB798, 0xB7A1, 0xB799, 0xB7A2, 0xB79A, 0x8DFB, 0xB79B, 0x8DFC,\n\t0xB79C, 0xB7A3, 0xB79D, 0x8DFD, 0xB79E, 0x8DFE, 0xB79F, 0x8E41,\n\t0xB7A0, 0xB7A4, 0xB7A1, 0x8E42, 0xB7A2, 0x8E43, 0xB7A3, 0x8E44,\n\t0xB7A4, 0x8E45, 0xB7A5, 0x8E46, 0xB7A6, 0x8E47, 0xB7A7, 0x8E48,\n\t0xB7A8, 0xB7A5, 0xB7A9, 0xB7A6, 0xB7AA, 0x8E49, 0xB7AB, 0xB7A7,\n\t0xB7AC, 0xB7A8, 0xB7AD, 0xB7A9, 0xB7AE, 0x8E4A, 0xB7AF, 0x8E4B,\n\t0xB7B0, 0x8E4C, 0xB7B1, 0x8E4D, 0xB7B2, 0x8E4E, 0xB7B3, 0x8E4F,\n\t0xB7B4, 0xB7AA, 0xB7B5, 0xB7AB, 0xB7B6, 0x8E50, 0xB7B7, 0x8E51,\n\t0xB7B8, 0xB7AC, 0xB7B9, 0x8E52, 0xB7BA, 0x8E53, 0xB7BB, 0x8E54,\n\t0xB7BC, 0x8E55, 0xB7BD, 0x8E56, 0xB7BE, 0x8E57, 0xB7BF, 0x8E58,\n\t0xB7C0, 0x8E59, 0xB7C1, 0x8E5A, 0xB7C2, 0x8E61, 0xB7C3, 0x8E62,\n\t0xB7C4, 0x8E63, 0xB7C5, 0x8E64, 0xB7C6, 0x8E65, 0xB7C7, 0xB7AD,\n\t0xB7C8, 0x8E66, 0xB7C9, 0xB7AE, 0xB7CA, 0x8E67, 0xB7CB, 0x8E68,\n\t0xB7CC, 0x8E69, 0xB7CD, 0x8E6A, 0xB7CE, 0x8E6B, 0xB7CF, 0x8E6C,\n\t0xB7D0, 0x8E6D, 0xB7D1, 0x8E6E, 0xB7D2, 0x8E6F, 0xB7D3, 0x8E70,\n\t0xB7D4, 0x8E71, 0xB7D5, 0x8E72, 0xB7D6, 0x8E73, 0xB7D7, 0x8E74,\n\t0xB7D8, 0x8E75, 0xB7D9, 0x8E76, 0xB7DA, 0x8E77, 0xB7DB, 0x8E78,\n\t0xB7DC, 0x8E79, 0xB7DD, 0x8E7A, 0xB7DE, 0x8E81, 0xB7DF, 0x8E82,\n\t0xB7E0, 0x8E83, 0xB7E1, 0x8E84, 0xB7E2, 0x8E85, 0xB7E3, 0x8E86,\n\t0xB7E4, 0x8E87, 0xB7E5, 0x8E88, 0xB7E6, 0x8E89, 0xB7E7, 0x8E8A,\n\t0xB7E8, 0x8E8B, 0xB7E9, 0x8E8C, 0xB7EA, 0x8E8D, 0xB7EB, 0x8E8E,\n\t0xB7EC, 0xB7AF, 0xB7ED, 0xB7B0, 0xB7EE, 0x8E8F, 0xB7EF, 0x8E90,\n\t0xB7F0, 0xB7B1, 0xB7F1, 0x8E91, 0xB7F2, 0x8E92, 0xB7F3, 0x8E93,\n\t0xB7F4, 0xB7B2, 0xB7F5, 0x8E94, 0xB7F6, 0x8E95, 0xB7F7, 0x8E96,\n\t0xB7F8, 0x8E97, 0xB7F9, 0x8E98, 0xB7FA, 0x8E99, 0xB7FB, 0x8E9A,\n\t0xB7FC, 0xB7B3, 0xB7FD, 0xB7B4, 0xB7FE, 0x8E9B, 0xB7FF, 0xB7B5,\n\t0xB800, 0xB7B6, 0xB801, 0xB7B7, 0xB802, 0x8E9C, 0xB803, 0x8E9D,\n\t0xB804, 0x8E9E, 0xB805, 0x8E9F, 0xB806, 0x8EA0, 0xB807, 0xB7B8,\n\t0xB808, 0xB7B9, 0xB809, 0xB7BA, 0xB80A, 0x8EA1, 0xB80B, 0x8EA2,\n\t0xB80C, 0xB7BB, 0xB80D, 0x8EA3, 0xB80E, 0x8EA4, 0xB80F, 0x8EA5,\n\t0xB810, 0xB7BC, 0xB811, 0x8EA6, 0xB812, 0x8EA7, 0xB813, 0x8EA8,\n\t0xB814, 0x8EA9, 0xB815, 0x8EAA, 0xB816, 0x8EAB, 0xB817, 0x8EAC,\n\t0xB818, 0xB7BD, 0xB819, 0xB7BE, 0xB81A, 0x8EAD, 0xB81B, 0xB7BF,\n\t0xB81C, 0x8EAE, 0xB81D, 0xB7C0, 0xB81E, 0x8EAF, 0xB81F, 0x8EB0,\n\t0xB820, 0x8EB1, 0xB821, 0x8EB2, 0xB822, 0x8EB3, 0xB823, 0x8EB4,\n\t0xB824, 0xB7C1, 0xB825, 0xB7C2, 0xB826, 0x8EB5, 0xB827, 0x8EB6,\n\t0xB828, 0xB7C3, 0xB829, 0x8EB7, 0xB82A, 0x8EB8, 0xB82B, 0x8EB9,\n\t0xB82C, 0xB7C4, 0xB82D, 0x8EBA, 0xB82E, 0x8EBB, 0xB82F, 0x8EBC,\n\t0xB830, 0x8EBD, 0xB831, 0x8EBE, 0xB832, 0x8EBF, 0xB833, 0x8EC0,\n\t0xB834, 0xB7C5, 0xB835, 0xB7C6, 0xB836, 0x8EC1, 0xB837, 0xB7C7,\n\t0xB838, 0xB7C8, 0xB839, 0xB7C9, 0xB83A, 0x8EC2, 0xB83B, 0x8EC3,\n\t0xB83C, 0x8EC4, 0xB83D, 0x8EC5, 0xB83E, 0x8EC6, 0xB83F, 0x8EC7,\n\t0xB840, 0xB7CA, 0xB841, 0x8EC8, 0xB842, 0x8EC9, 0xB843, 0x8ECA,\n\t0xB844, 0xB7CB, 0xB845, 0x8ECB, 0xB846, 0x8ECC, 0xB847, 0x8ECD,\n\t0xB848, 0x8ECE, 0xB849, 0x8ECF, 0xB84A, 0x8ED0, 0xB84B, 0x8ED1,\n\t0xB84C, 0x8ED2, 0xB84D, 0x8ED3, 0xB84E, 0x8ED4, 0xB84F, 0x8ED5,\n\t0xB850, 0x8ED6, 0xB851, 0xB7CC, 0xB852, 0x8ED7, 0xB853, 0xB7CD,\n\t0xB854, 0x8ED8, 0xB855, 0x8ED9, 0xB856, 0x8EDA, 0xB857, 0x8EDB,\n\t0xB858, 0x8EDC, 0xB859, 0x8EDD, 0xB85A, 0x8EDE, 0xB85B, 0x8EDF,\n\t0xB85C, 0xB7CE, 0xB85D, 0xB7CF, 0xB85E, 0x8EE0, 0xB85F, 0x8EE1,\n\t0xB860, 0xB7D0, 0xB861, 0x8EE2, 0xB862, 0x8EE3, 0xB863, 0x8EE4,\n\t0xB864, 0xB7D1, 0xB865, 0x8EE5, 0xB866, 0x8EE6, 0xB867, 0x8EE7,\n\t0xB868, 0x8EE8, 0xB869, 0x8EE9, 0xB86A, 0x8EEA, 0xB86B, 0x8EEB,\n\t0xB86C, 0xB7D2, 0xB86D, 0xB7D3, 0xB86E, 0x8EEC, 0xB86F, 0xB7D4,\n\t0xB870, 0x8EED, 0xB871, 0xB7D5, 0xB872, 0x8EEE, 0xB873, 0x8EEF,\n\t0xB874, 0x8EF0, 0xB875, 0x8EF1, 0xB876, 0x8EF2, 0xB877, 0x8EF3,\n\t0xB878, 0xB7D6, 0xB879, 0x8EF4, 0xB87A, 0x8EF5, 0xB87B, 0x8EF6,\n\t0xB87C, 0xB7D7, 0xB87D, 0x8EF7, 0xB87E, 0x8EF8, 0xB87F, 0x8EF9,\n\t0xB880, 0x8EFA, 0xB881, 0x8EFB, 0xB882, 0x8EFC, 0xB883, 0x8EFD,\n\t0xB884, 0x8EFE, 0xB885, 0x8F41, 0xB886, 0x8F42, 0xB887, 0x8F43,\n\t0xB888, 0x8F44, 0xB889, 0x8F45, 0xB88A, 0x8F46, 0xB88B, 0x8F47,\n\t0xB88C, 0x8F48, 0xB88D, 0xB7D8, 0xB88E, 0x8F49, 0xB88F, 0x8F4A,\n\t0xB890, 0x8F4B, 0xB891, 0x8F4C, 0xB892, 0x8F4D, 0xB893, 0x8F4E,\n\t0xB894, 0x8F4F, 0xB895, 0x8F50, 0xB896, 0x8F51, 0xB897, 0x8F52,\n\t0xB898, 0x8F53, 0xB899, 0x8F54, 0xB89A, 0x8F55, 0xB89B, 0x8F56,\n\t0xB89C, 0x8F57, 0xB89D, 0x8F58, 0xB89E, 0x8F59, 0xB89F, 0x8F5A,\n\t0xB8A0, 0x8F61, 0xB8A1, 0x8F62, 0xB8A2, 0x8F63, 0xB8A3, 0x8F64,\n\t0xB8A4, 0x8F65, 0xB8A5, 0x8F66, 0xB8A6, 0x8F67, 0xB8A7, 0x8F68,\n\t0xB8A8, 0xB7D9, 0xB8A9, 0x8F69, 0xB8AA, 0x8F6A, 0xB8AB, 0x8F6B,\n\t0xB8AC, 0x8F6C, 0xB8AD, 0x8F6D, 0xB8AE, 0x8F6E, 0xB8AF, 0x8F6F,\n\t0xB8B0, 0xB7DA, 0xB8B1, 0x8F70, 0xB8B2, 0x8F71, 0xB8B3, 0x8F72,\n\t0xB8B4, 0xB7DB, 0xB8B5, 0x8F73, 0xB8B6, 0x8F74, 0xB8B7, 0x8F75,\n\t0xB8B8, 0xB7DC, 0xB8B9, 0x8F76, 0xB8BA, 0x8F77, 0xB8BB, 0x8F78,\n\t0xB8BC, 0x8F79, 0xB8BD, 0x8F7A, 0xB8BE, 0x8F81, 0xB8BF, 0x8F82,\n\t0xB8C0, 0xB7DD, 0xB8C1, 0xB7DE, 0xB8C2, 0x8F83, 0xB8C3, 0xB7DF,\n\t0xB8C4, 0x8F84, 0xB8C5, 0xB7E0, 0xB8C6, 0x8F85, 0xB8C7, 0x8F86,\n\t0xB8C8, 0x8F87, 0xB8C9, 0x8F88, 0xB8CA, 0x8F89, 0xB8CB, 0x8F8A,\n\t0xB8CC, 0xB7E1, 0xB8CD, 0x8F8B, 0xB8CE, 0x8F8C, 0xB8CF, 0x8F8D,\n\t0xB8D0, 0xB7E2, 0xB8D1, 0x8F8E, 0xB8D2, 0x8F8F, 0xB8D3, 0x8F90,\n\t0xB8D4, 0xB7E3, 0xB8D5, 0x8F91, 0xB8D6, 0x8F92, 0xB8D7, 0x8F93,\n\t0xB8D8, 0x8F94, 0xB8D9, 0x8F95, 0xB8DA, 0x8F96, 0xB8DB, 0x8F97,\n\t0xB8DC, 0x8F98, 0xB8DD, 0xB7E4, 0xB8DE, 0x8F99, 0xB8DF, 0xB7E5,\n\t0xB8E0, 0x8F9A, 0xB8E1, 0xB7E6, 0xB8E2, 0x8F9B, 0xB8E3, 0x8F9C,\n\t0xB8E4, 0x8F9D, 0xB8E5, 0x8F9E, 0xB8E6, 0x8F9F, 0xB8E7, 0x8FA0,\n\t0xB8E8, 0xB7E7, 0xB8E9, 0xB7E8, 0xB8EA, 0x8FA1, 0xB8EB, 0x8FA2,\n\t0xB8EC, 0xB7E9, 0xB8ED, 0x8FA3, 0xB8EE, 0x8FA4, 0xB8EF, 0x8FA5,\n\t0xB8F0, 0xB7EA, 0xB8F1, 0x8FA6, 0xB8F2, 0x8FA7, 0xB8F3, 0x8FA8,\n\t0xB8F4, 0x8FA9, 0xB8F5, 0x8FAA, 0xB8F6, 0x8FAB, 0xB8F7, 0x8FAC,\n\t0xB8F8, 0xB7EB, 0xB8F9, 0xB7EC, 0xB8FA, 0x8FAD, 0xB8FB, 0xB7ED,\n\t0xB8FC, 0x8FAE, 0xB8FD, 0xB7EE, 0xB8FE, 0x8FAF, 0xB8FF, 0x8FB0,\n\t0xB900, 0x8FB1, 0xB901, 0x8FB2, 0xB902, 0x8FB3, 0xB903, 0x8FB4,\n\t0xB904, 0xB7EF, 0xB905, 0x8FB5, 0xB906, 0x8FB6, 0xB907, 0x8FB7,\n\t0xB908, 0x8FB8, 0xB909, 0x8FB9, 0xB90A, 0x8FBA, 0xB90B, 0x8FBB,\n\t0xB90C, 0x8FBC, 0xB90D, 0x8FBD, 0xB90E, 0x8FBE, 0xB90F, 0x8FBF,\n\t0xB910, 0x8FC0, 0xB911, 0x8FC1, 0xB912, 0x8FC2, 0xB913, 0x8FC3,\n\t0xB914, 0x8FC4, 0xB915, 0x8FC5, 0xB916, 0x8FC6, 0xB917, 0x8FC7,\n\t0xB918, 0xB7F0, 0xB919, 0x8FC8, 0xB91A, 0x8FC9, 0xB91B, 0x8FCA,\n\t0xB91C, 0x8FCB, 0xB91D, 0x8FCC, 0xB91E, 0x8FCD, 0xB91F, 0x8FCE,\n\t0xB920, 0xB7F1, 0xB921, 0x8FCF, 0xB922, 0x8FD0, 0xB923, 0x8FD1,\n\t0xB924, 0x8FD2, 0xB925, 0x8FD3, 0xB926, 0x8FD4, 0xB927, 0x8FD5,\n\t0xB928, 0x8FD6, 0xB929, 0x8FD7, 0xB92A, 0x8FD8, 0xB92B, 0x8FD9,\n\t0xB92C, 0x8FDA, 0xB92D, 0x8FDB, 0xB92E, 0x8FDC, 0xB92F, 0x8FDD,\n\t0xB930, 0x8FDE, 0xB931, 0x8FDF, 0xB932, 0x8FE0, 0xB933, 0x8FE1,\n\t0xB934, 0x8FE2, 0xB935, 0x8FE3, 0xB936, 0x8FE4, 0xB937, 0x8FE5,\n\t0xB938, 0x8FE6, 0xB939, 0x8FE7, 0xB93A, 0x8FE8, 0xB93B, 0x8FE9,\n\t0xB93C, 0xB7F2, 0xB93D, 0xB7F3, 0xB93E, 0x8FEA, 0xB93F, 0x8FEB,\n\t0xB940, 0xB7F4, 0xB941, 0x8FEC, 0xB942, 0x8FED, 0xB943, 0x8FEE,\n\t0xB944, 0xB7F5, 0xB945, 0x8FEF, 0xB946, 0x8FF0, 0xB947, 0x8FF1,\n\t0xB948, 0x8FF2, 0xB949, 0x8FF3, 0xB94A, 0x8FF4, 0xB94B, 0x8FF5,\n\t0xB94C, 0xB7F6, 0xB94D, 0x8FF6, 0xB94E, 0x8FF7, 0xB94F, 0xB7F7,\n\t0xB950, 0x8FF8, 0xB951, 0xB7F8, 0xB952, 0x8FF9, 0xB953, 0x8FFA,\n\t0xB954, 0x8FFB, 0xB955, 0x8FFC, 0xB956, 0x8FFD, 0xB957, 0x8FFE,\n\t0xB958, 0xB7F9, 0xB959, 0xB7FA, 0xB95A, 0x9041, 0xB95B, 0x9042,\n\t0xB95C, 0xB7FB, 0xB95D, 0x9043, 0xB95E, 0x9044, 0xB95F, 0x9045,\n\t0xB960, 0xB7FC, 0xB961, 0x9046, 0xB962, 0x9047, 0xB963, 0x9048,\n\t0xB964, 0x9049, 0xB965, 0x904A, 0xB966, 0x904B, 0xB967, 0x904C,\n\t0xB968, 0xB7FD, 0xB969, 0xB7FE, 0xB96A, 0x904D, 0xB96B, 0xB8A1,\n\t0xB96C, 0x904E, 0xB96D, 0xB8A2, 0xB96E, 0x904F, 0xB96F, 0x9050,\n\t0xB970, 0x9051, 0xB971, 0x9052, 0xB972, 0x9053, 0xB973, 0x9054,\n\t0xB974, 0xB8A3, 0xB975, 0xB8A4, 0xB976, 0x9055, 0xB977, 0x9056,\n\t0xB978, 0xB8A5, 0xB979, 0x9057, 0xB97A, 0x9058, 0xB97B, 0x9059,\n\t0xB97C, 0xB8A6, 0xB97D, 0x905A, 0xB97E, 0x9061, 0xB97F, 0x9062,\n\t0xB980, 0x9063, 0xB981, 0x9064, 0xB982, 0x9065, 0xB983, 0x9066,\n\t0xB984, 0xB8A7, 0xB985, 0xB8A8, 0xB986, 0x9067, 0xB987, 0xB8A9,\n\t0xB988, 0x9068, 0xB989, 0xB8AA, 0xB98A, 0xB8AB, 0xB98B, 0x9069,\n\t0xB98C, 0x906A, 0xB98D, 0xB8AC, 0xB98E, 0xB8AD, 0xB98F, 0x906B,\n\t0xB990, 0x906C, 0xB991, 0x906D, 0xB992, 0x906E, 0xB993, 0x906F,\n\t0xB994, 0x9070, 0xB995, 0x9071, 0xB996, 0x9072, 0xB997, 0x9073,\n\t0xB998, 0x9074, 0xB999, 0x9075, 0xB99A, 0x9076, 0xB99B, 0x9077,\n\t0xB99C, 0x9078, 0xB99D, 0x9079, 0xB99E, 0x907A, 0xB99F, 0x9081,\n\t0xB9A0, 0x9082, 0xB9A1, 0x9083, 0xB9A2, 0x9084, 0xB9A3, 0x9085,\n\t0xB9A4, 0x9086, 0xB9A5, 0x9087, 0xB9A6, 0x9088, 0xB9A7, 0x9089,\n\t0xB9A8, 0x908A, 0xB9A9, 0x908B, 0xB9AA, 0x908C, 0xB9AB, 0x908D,\n\t0xB9AC, 0xB8AE, 0xB9AD, 0xB8AF, 0xB9AE, 0x908E, 0xB9AF, 0x908F,\n\t0xB9B0, 0xB8B0, 0xB9B1, 0x9090, 0xB9B2, 0x9091, 0xB9B3, 0x9092,\n\t0xB9B4, 0xB8B1, 0xB9B5, 0x9093, 0xB9B6, 0x9094, 0xB9B7, 0x9095,\n\t0xB9B8, 0x9096, 0xB9B9, 0x9097, 0xB9BA, 0x9098, 0xB9BB, 0x9099,\n\t0xB9BC, 0xB8B2, 0xB9BD, 0xB8B3, 0xB9BE, 0x909A, 0xB9BF, 0xB8B4,\n\t0xB9C0, 0x909B, 0xB9C1, 0xB8B5, 0xB9C2, 0x909C, 0xB9C3, 0x909D,\n\t0xB9C4, 0x909E, 0xB9C5, 0x909F, 0xB9C6, 0x90A0, 0xB9C7, 0x90A1,\n\t0xB9C8, 0xB8B6, 0xB9C9, 0xB8B7, 0xB9CA, 0x90A2, 0xB9CB, 0x90A3,\n\t0xB9CC, 0xB8B8, 0xB9CD, 0x90A4, 0xB9CE, 0xB8B9, 0xB9CF, 0xB8BA,\n\t0xB9D0, 0xB8BB, 0xB9D1, 0xB8BC, 0xB9D2, 0xB8BD, 0xB9D3, 0x90A5,\n\t0xB9D4, 0x90A6, 0xB9D5, 0x90A7, 0xB9D6, 0x90A8, 0xB9D7, 0x90A9,\n\t0xB9D8, 0xB8BE, 0xB9D9, 0xB8BF, 0xB9DA, 0x90AA, 0xB9DB, 0xB8C0,\n\t0xB9DC, 0x90AB, 0xB9DD, 0xB8C1, 0xB9DE, 0xB8C2, 0xB9DF, 0x90AC,\n\t0xB9E0, 0x90AD, 0xB9E1, 0xB8C3, 0xB9E2, 0x90AE, 0xB9E3, 0xB8C4,\n\t0xB9E4, 0xB8C5, 0xB9E5, 0xB8C6, 0xB9E6, 0x90AF, 0xB9E7, 0x90B0,\n\t0xB9E8, 0xB8C7, 0xB9E9, 0x90B1, 0xB9EA, 0x90B2, 0xB9EB, 0x90B3,\n\t0xB9EC, 0xB8C8, 0xB9ED, 0x90B4, 0xB9EE, 0x90B5, 0xB9EF, 0x90B6,\n\t0xB9F0, 0x90B7, 0xB9F1, 0x90B8, 0xB9F2, 0x90B9, 0xB9F3, 0x90BA,\n\t0xB9F4, 0xB8C9, 0xB9F5, 0xB8CA, 0xB9F6, 0x90BB, 0xB9F7, 0xB8CB,\n\t0xB9F8, 0xB8CC, 0xB9F9, 0xB8CD, 0xB9FA, 0xB8CE, 0xB9FB, 0x90BC,\n\t0xB9FC, 0x90BD, 0xB9FD, 0x90BE, 0xB9FE, 0x90BF, 0xB9FF, 0x90C0,\n\t0xBA00, 0xB8CF, 0xBA01, 0xB8D0, 0xBA02, 0x90C1, 0xBA03, 0x90C2,\n\t0xBA04, 0x90C3, 0xBA05, 0x90C4, 0xBA06, 0x90C5, 0xBA07, 0x90C6,\n\t0xBA08, 0xB8D1, 0xBA09, 0x90C7, 0xBA0A, 0x90C8, 0xBA0B, 0x90C9,\n\t0xBA0C, 0x90CA, 0xBA0D, 0x90CB, 0xBA0E, 0x90CC, 0xBA0F, 0x90CD,\n\t0xBA10, 0x90CE, 0xBA11, 0x90CF, 0xBA12, 0x90D0, 0xBA13, 0x90D1,\n\t0xBA14, 0x90D2, 0xBA15, 0xB8D2, 0xBA16, 0x90D3, 0xBA17, 0x90D4,\n\t0xBA18, 0x90D5, 0xBA19, 0x90D6, 0xBA1A, 0x90D7, 0xBA1B, 0x90D8,\n\t0xBA1C, 0x90D9, 0xBA1D, 0x90DA, 0xBA1E, 0x90DB, 0xBA1F, 0x90DC,\n\t0xBA20, 0x90DD, 0xBA21, 0x90DE, 0xBA22, 0x90DF, 0xBA23, 0x90E0,\n\t0xBA24, 0x90E1, 0xBA25, 0x90E2, 0xBA26, 0x90E3, 0xBA27, 0x90E4,\n\t0xBA28, 0x90E5, 0xBA29, 0x90E6, 0xBA2A, 0x90E7, 0xBA2B, 0x90E8,\n\t0xBA2C, 0x90E9, 0xBA2D, 0x90EA, 0xBA2E, 0x90EB, 0xBA2F, 0x90EC,\n\t0xBA30, 0x90ED, 0xBA31, 0x90EE, 0xBA32, 0x90EF, 0xBA33, 0x90F0,\n\t0xBA34, 0x90F1, 0xBA35, 0x90F2, 0xBA36, 0x90F3, 0xBA37, 0x90F4,\n\t0xBA38, 0xB8D3, 0xBA39, 0xB8D4, 0xBA3A, 0x90F5, 0xBA3B, 0x90F6,\n\t0xBA3C, 0xB8D5, 0xBA3D, 0x90F7, 0xBA3E, 0x90F8, 0xBA3F, 0x90F9,\n\t0xBA40, 0xB8D6, 0xBA41, 0x90FA, 0xBA42, 0xB8D7, 0xBA43, 0x90FB,\n\t0xBA44, 0x90FC, 0xBA45, 0x90FD, 0xBA46, 0x90FE, 0xBA47, 0x9141,\n\t0xBA48, 0xB8D8, 0xBA49, 0xB8D9, 0xBA4A, 0x9142, 0xBA4B, 0xB8DA,\n\t0xBA4C, 0x9143, 0xBA4D, 0xB8DB, 0xBA4E, 0xB8DC, 0xBA4F, 0x9144,\n\t0xBA50, 0x9145, 0xBA51, 0x9146, 0xBA52, 0x9147, 0xBA53, 0xB8DD,\n\t0xBA54, 0xB8DE, 0xBA55, 0xB8DF, 0xBA56, 0x9148, 0xBA57, 0x9149,\n\t0xBA58, 0xB8E0, 0xBA59, 0x914A, 0xBA5A, 0x914B, 0xBA5B, 0x914C,\n\t0xBA5C, 0xB8E1, 0xBA5D, 0x914D, 0xBA5E, 0x914E, 0xBA5F, 0x914F,\n\t0xBA60, 0x9150, 0xBA61, 0x9151, 0xBA62, 0x9152, 0xBA63, 0x9153,\n\t0xBA64, 0xB8E2, 0xBA65, 0xB8E3, 0xBA66, 0x9154, 0xBA67, 0xB8E4,\n\t0xBA68, 0xB8E5, 0xBA69, 0xB8E6, 0xBA6A, 0x9155, 0xBA6B, 0x9156,\n\t0xBA6C, 0x9157, 0xBA6D, 0x9158, 0xBA6E, 0x9159, 0xBA6F, 0x915A,\n\t0xBA70, 0xB8E7, 0xBA71, 0xB8E8, 0xBA72, 0x9161, 0xBA73, 0x9162,\n\t0xBA74, 0xB8E9, 0xBA75, 0x9163, 0xBA76, 0x9164, 0xBA77, 0x9165,\n\t0xBA78, 0xB8EA, 0xBA79, 0x9166, 0xBA7A, 0x9167, 0xBA7B, 0x9168,\n\t0xBA7C, 0x9169, 0xBA7D, 0x916A, 0xBA7E, 0x916B, 0xBA7F, 0x916C,\n\t0xBA80, 0x916D, 0xBA81, 0x916E, 0xBA82, 0x916F, 0xBA83, 0xB8EB,\n\t0xBA84, 0xB8EC, 0xBA85, 0xB8ED, 0xBA86, 0x9170, 0xBA87, 0xB8EE,\n\t0xBA88, 0x9171, 0xBA89, 0x9172, 0xBA8A, 0x9173, 0xBA8B, 0x9174,\n\t0xBA8C, 0xB8EF, 0xBA8D, 0x9175, 0xBA8E, 0x9176, 0xBA8F, 0x9177,\n\t0xBA90, 0x9178, 0xBA91, 0x9179, 0xBA92, 0x917A, 0xBA93, 0x9181,\n\t0xBA94, 0x9182, 0xBA95, 0x9183, 0xBA96, 0x9184, 0xBA97, 0x9185,\n\t0xBA98, 0x9186, 0xBA99, 0x9187, 0xBA9A, 0x9188, 0xBA9B, 0x9189,\n\t0xBA9C, 0x918A, 0xBA9D, 0x918B, 0xBA9E, 0x918C, 0xBA9F, 0x918D,\n\t0xBAA0, 0x918E, 0xBAA1, 0x918F, 0xBAA2, 0x9190, 0xBAA3, 0x9191,\n\t0xBAA4, 0x9192, 0xBAA5, 0x9193, 0xBAA6, 0x9194, 0xBAA7, 0x9195,\n\t0xBAA8, 0xB8F0, 0xBAA9, 0xB8F1, 0xBAAA, 0x9196, 0xBAAB, 0xB8F2,\n\t0xBAAC, 0xB8F3, 0xBAAD, 0x9197, 0xBAAE, 0x9198, 0xBAAF, 0x9199,\n\t0xBAB0, 0xB8F4, 0xBAB1, 0x919A, 0xBAB2, 0xB8F5, 0xBAB3, 0x919B,\n\t0xBAB4, 0x919C, 0xBAB5, 0x919D, 0xBAB6, 0x919E, 0xBAB7, 0x919F,\n\t0xBAB8, 0xB8F6, 0xBAB9, 0xB8F7, 0xBABA, 0x91A0, 0xBABB, 0xB8F8,\n\t0xBABC, 0x91A1, 0xBABD, 0xB8F9, 0xBABE, 0x91A2, 0xBABF, 0x91A3,\n\t0xBAC0, 0x91A4, 0xBAC1, 0x91A5, 0xBAC2, 0x91A6, 0xBAC3, 0x91A7,\n\t0xBAC4, 0xB8FA, 0xBAC5, 0x91A8, 0xBAC6, 0x91A9, 0xBAC7, 0x91AA,\n\t0xBAC8, 0xB8FB, 0xBAC9, 0x91AB, 0xBACA, 0x91AC, 0xBACB, 0x91AD,\n\t0xBACC, 0x91AE, 0xBACD, 0x91AF, 0xBACE, 0x91B0, 0xBACF, 0x91B1,\n\t0xBAD0, 0x91B2, 0xBAD1, 0x91B3, 0xBAD2, 0x91B4, 0xBAD3, 0x91B5,\n\t0xBAD4, 0x91B6, 0xBAD5, 0x91B7, 0xBAD6, 0x91B8, 0xBAD7, 0x91B9,\n\t0xBAD8, 0xB8FC, 0xBAD9, 0xB8FD, 0xBADA, 0x91BA, 0xBADB, 0x91BB,\n\t0xBADC, 0x91BC, 0xBADD, 0x91BD, 0xBADE, 0x91BE, 0xBADF, 0x91BF,\n\t0xBAE0, 0x91C0, 0xBAE1, 0x91C1, 0xBAE2, 0x91C2, 0xBAE3, 0x91C3,\n\t0xBAE4, 0x91C4, 0xBAE5, 0x91C5, 0xBAE6, 0x91C6, 0xBAE7, 0x91C7,\n\t0xBAE8, 0x91C8, 0xBAE9, 0x91C9, 0xBAEA, 0x91CA, 0xBAEB, 0x91CB,\n\t0xBAEC, 0x91CC, 0xBAED, 0x91CD, 0xBAEE, 0x91CE, 0xBAEF, 0x91CF,\n\t0xBAF0, 0x91D0, 0xBAF1, 0x91D1, 0xBAF2, 0x91D2, 0xBAF3, 0x91D3,\n\t0xBAF4, 0x91D4, 0xBAF5, 0x91D5, 0xBAF6, 0x91D6, 0xBAF7, 0x91D7,\n\t0xBAF8, 0x91D8, 0xBAF9, 0x91D9, 0xBAFA, 0x91DA, 0xBAFB, 0x91DB,\n\t0xBAFC, 0xB8FE, 0xBAFD, 0x91DC, 0xBAFE, 0x91DD, 0xBAFF, 0x91DE,\n\t0xBB00, 0xB9A1, 0xBB01, 0x91DF, 0xBB02, 0x91E0, 0xBB03, 0x91E1,\n\t0xBB04, 0xB9A2, 0xBB05, 0x91E2, 0xBB06, 0x91E3, 0xBB07, 0x91E4,\n\t0xBB08, 0x91E5, 0xBB09, 0x91E6, 0xBB0A, 0x91E7, 0xBB0B, 0x91E8,\n\t0xBB0C, 0x91E9, 0xBB0D, 0xB9A3, 0xBB0E, 0x91EA, 0xBB0F, 0xB9A4,\n\t0xBB10, 0x91EB, 0xBB11, 0xB9A5, 0xBB12, 0x91EC, 0xBB13, 0x91ED,\n\t0xBB14, 0x91EE, 0xBB15, 0x91EF, 0xBB16, 0x91F0, 0xBB17, 0x91F1,\n\t0xBB18, 0xB9A6, 0xBB19, 0x91F2, 0xBB1A, 0x91F3, 0xBB1B, 0x91F4,\n\t0xBB1C, 0xB9A7, 0xBB1D, 0x91F5, 0xBB1E, 0x91F6, 0xBB1F, 0x91F7,\n\t0xBB20, 0xB9A8, 0xBB21, 0x91F8, 0xBB22, 0x91F9, 0xBB23, 0x91FA,\n\t0xBB24, 0x91FB, 0xBB25, 0x91FC, 0xBB26, 0x91FD, 0xBB27, 0x91FE,\n\t0xBB28, 0x9241, 0xBB29, 0xB9A9, 0xBB2A, 0x9242, 0xBB2B, 0xB9AA,\n\t0xBB2C, 0x9243, 0xBB2D, 0x9244, 0xBB2E, 0x9245, 0xBB2F, 0x9246,\n\t0xBB30, 0x9247, 0xBB31, 0x9248, 0xBB32, 0x9249, 0xBB33, 0x924A,\n\t0xBB34, 0xB9AB, 0xBB35, 0xB9AC, 0xBB36, 0xB9AD, 0xBB37, 0x924B,\n\t0xBB38, 0xB9AE, 0xBB39, 0x924C, 0xBB3A, 0x924D, 0xBB3B, 0xB9AF,\n\t0xBB3C, 0xB9B0, 0xBB3D, 0xB9B1, 0xBB3E, 0xB9B2, 0xBB3F, 0x924E,\n\t0xBB40, 0x924F, 0xBB41, 0x9250, 0xBB42, 0x9251, 0xBB43, 0x9252,\n\t0xBB44, 0xB9B3, 0xBB45, 0xB9B4, 0xBB46, 0x9253, 0xBB47, 0xB9B5,\n\t0xBB48, 0x9254, 0xBB49, 0xB9B6, 0xBB4A, 0x9255, 0xBB4B, 0x9256,\n\t0xBB4C, 0x9257, 0xBB4D, 0xB9B7, 0xBB4E, 0x9258, 0xBB4F, 0xB9B8,\n\t0xBB50, 0xB9B9, 0xBB51, 0x9259, 0xBB52, 0x925A, 0xBB53, 0x9261,\n\t0xBB54, 0xB9BA, 0xBB55, 0x9262, 0xBB56, 0x9263, 0xBB57, 0x9264,\n\t0xBB58, 0xB9BB, 0xBB59, 0x9265, 0xBB5A, 0x9266, 0xBB5B, 0x9267,\n\t0xBB5C, 0x9268, 0xBB5D, 0x9269, 0xBB5E, 0x926A, 0xBB5F, 0x926B,\n\t0xBB60, 0x926C, 0xBB61, 0xB9BC, 0xBB62, 0x926D, 0xBB63, 0xB9BD,\n\t0xBB64, 0x926E, 0xBB65, 0x926F, 0xBB66, 0x9270, 0xBB67, 0x9271,\n\t0xBB68, 0x9272, 0xBB69, 0x9273, 0xBB6A, 0x9274, 0xBB6B, 0x9275,\n\t0xBB6C, 0xB9BE, 0xBB6D, 0x9276, 0xBB6E, 0x9277, 0xBB6F, 0x9278,\n\t0xBB70, 0x9279, 0xBB71, 0x927A, 0xBB72, 0x9281, 0xBB73, 0x9282,\n\t0xBB74, 0x9283, 0xBB75, 0x9284, 0xBB76, 0x9285, 0xBB77, 0x9286,\n\t0xBB78, 0x9287, 0xBB79, 0x9288, 0xBB7A, 0x9289, 0xBB7B, 0x928A,\n\t0xBB7C, 0x928B, 0xBB7D, 0x928C, 0xBB7E, 0x928D, 0xBB7F, 0x928E,\n\t0xBB80, 0x928F, 0xBB81, 0x9290, 0xBB82, 0x9291, 0xBB83, 0x9292,\n\t0xBB84, 0x9293, 0xBB85, 0x9294, 0xBB86, 0x9295, 0xBB87, 0x9296,\n\t0xBB88, 0xB9BF, 0xBB89, 0x9297, 0xBB8A, 0x9298, 0xBB8B, 0x9299,\n\t0xBB8C, 0xB9C0, 0xBB8D, 0x929A, 0xBB8E, 0x929B, 0xBB8F, 0x929C,\n\t0xBB90, 0xB9C1, 0xBB91, 0x929D, 0xBB92, 0x929E, 0xBB93, 0x929F,\n\t0xBB94, 0x92A0, 0xBB95, 0x92A1, 0xBB96, 0x92A2, 0xBB97, 0x92A3,\n\t0xBB98, 0x92A4, 0xBB99, 0x92A5, 0xBB9A, 0x92A6, 0xBB9B, 0x92A7,\n\t0xBB9C, 0x92A8, 0xBB9D, 0x92A9, 0xBB9E, 0x92AA, 0xBB9F, 0x92AB,\n\t0xBBA0, 0x92AC, 0xBBA1, 0x92AD, 0xBBA2, 0x92AE, 0xBBA3, 0x92AF,\n\t0xBBA4, 0xB9C2, 0xBBA5, 0x92B0, 0xBBA6, 0x92B1, 0xBBA7, 0x92B2,\n\t0xBBA8, 0xB9C3, 0xBBA9, 0x92B3, 0xBBAA, 0x92B4, 0xBBAB, 0x92B5,\n\t0xBBAC, 0xB9C4, 0xBBAD, 0x92B6, 0xBBAE, 0x92B7, 0xBBAF, 0x92B8,\n\t0xBBB0, 0x92B9, 0xBBB1, 0x92BA, 0xBBB2, 0x92BB, 0xBBB3, 0x92BC,\n\t0xBBB4, 0xB9C5, 0xBBB5, 0x92BD, 0xBBB6, 0x92BE, 0xBBB7, 0xB9C6,\n\t0xBBB8, 0x92BF, 0xBBB9, 0x92C0, 0xBBBA, 0x92C1, 0xBBBB, 0x92C2,\n\t0xBBBC, 0x92C3, 0xBBBD, 0x92C4, 0xBBBE, 0x92C5, 0xBBBF, 0x92C6,\n\t0xBBC0, 0xB9C7, 0xBBC1, 0x92C7, 0xBBC2, 0x92C8, 0xBBC3, 0x92C9,\n\t0xBBC4, 0xB9C8, 0xBBC5, 0x92CA, 0xBBC6, 0x92CB, 0xBBC7, 0x92CC,\n\t0xBBC8, 0xB9C9, 0xBBC9, 0x92CD, 0xBBCA, 0x92CE, 0xBBCB, 0x92CF,\n\t0xBBCC, 0x92D0, 0xBBCD, 0x92D1, 0xBBCE, 0x92D2, 0xBBCF, 0x92D3,\n\t0xBBD0, 0xB9CA, 0xBBD1, 0x92D4, 0xBBD2, 0x92D5, 0xBBD3, 0xB9CB,\n\t0xBBD4, 0x92D6, 0xBBD5, 0x92D7, 0xBBD6, 0x92D8, 0xBBD7, 0x92D9,\n\t0xBBD8, 0x92DA, 0xBBD9, 0x92DB, 0xBBDA, 0x92DC, 0xBBDB, 0x92DD,\n\t0xBBDC, 0x92DE, 0xBBDD, 0x92DF, 0xBBDE, 0x92E0, 0xBBDF, 0x92E1,\n\t0xBBE0, 0x92E2, 0xBBE1, 0x92E3, 0xBBE2, 0x92E4, 0xBBE3, 0x92E5,\n\t0xBBE4, 0x92E6, 0xBBE5, 0x92E7, 0xBBE6, 0x92E8, 0xBBE7, 0x92E9,\n\t0xBBE8, 0x92EA, 0xBBE9, 0x92EB, 0xBBEA, 0x92EC, 0xBBEB, 0x92ED,\n\t0xBBEC, 0x92EE, 0xBBED, 0x92EF, 0xBBEE, 0x92F0, 0xBBEF, 0x92F1,\n\t0xBBF0, 0x92F2, 0xBBF1, 0x92F3, 0xBBF2, 0x92F4, 0xBBF3, 0x92F5,\n\t0xBBF4, 0x92F6, 0xBBF5, 0x92F7, 0xBBF6, 0x92F8, 0xBBF7, 0x92F9,\n\t0xBBF8, 0xB9CC, 0xBBF9, 0xB9CD, 0xBBFA, 0x92FA, 0xBBFB, 0x92FB,\n\t0xBBFC, 0xB9CE, 0xBBFD, 0x92FC, 0xBBFE, 0x92FD, 0xBBFF, 0xB9CF,\n\t0xBC00, 0xB9D0, 0xBC01, 0x92FE, 0xBC02, 0xB9D1, 0xBC03, 0x9341,\n\t0xBC04, 0x9342, 0xBC05, 0x9343, 0xBC06, 0x9344, 0xBC07, 0x9345,\n\t0xBC08, 0xB9D2, 0xBC09, 0xB9D3, 0xBC0A, 0x9346, 0xBC0B, 0xB9D4,\n\t0xBC0C, 0xB9D5, 0xBC0D, 0xB9D6, 0xBC0E, 0x9347, 0xBC0F, 0xB9D7,\n\t0xBC10, 0x9348, 0xBC11, 0xB9D8, 0xBC12, 0x9349, 0xBC13, 0x934A,\n\t0xBC14, 0xB9D9, 0xBC15, 0xB9DA, 0xBC16, 0xB9DB, 0xBC17, 0xB9DC,\n\t0xBC18, 0xB9DD, 0xBC19, 0x934B, 0xBC1A, 0x934C, 0xBC1B, 0xB9DE,\n\t0xBC1C, 0xB9DF, 0xBC1D, 0xB9E0, 0xBC1E, 0xB9E1, 0xBC1F, 0xB9E2,\n\t0xBC20, 0x934D, 0xBC21, 0x934E, 0xBC22, 0x934F, 0xBC23, 0x9350,\n\t0xBC24, 0xB9E3, 0xBC25, 0xB9E4, 0xBC26, 0x9351, 0xBC27, 0xB9E5,\n\t0xBC28, 0x9352, 0xBC29, 0xB9E6, 0xBC2A, 0x9353, 0xBC2B, 0x9354,\n\t0xBC2C, 0x9355, 0xBC2D, 0xB9E7, 0xBC2E, 0x9356, 0xBC2F, 0x9357,\n\t0xBC30, 0xB9E8, 0xBC31, 0xB9E9, 0xBC32, 0x9358, 0xBC33, 0x9359,\n\t0xBC34, 0xB9EA, 0xBC35, 0x935A, 0xBC36, 0x9361, 0xBC37, 0x9362,\n\t0xBC38, 0xB9EB, 0xBC39, 0x9363, 0xBC3A, 0x9364, 0xBC3B, 0x9365,\n\t0xBC3C, 0x9366, 0xBC3D, 0x9367, 0xBC3E, 0x9368, 0xBC3F, 0x9369,\n\t0xBC40, 0xB9EC, 0xBC41, 0xB9ED, 0xBC42, 0x936A, 0xBC43, 0xB9EE,\n\t0xBC44, 0xB9EF, 0xBC45, 0xB9F0, 0xBC46, 0x936B, 0xBC47, 0x936C,\n\t0xBC48, 0x936D, 0xBC49, 0xB9F1, 0xBC4A, 0x936E, 0xBC4B, 0x936F,\n\t0xBC4C, 0xB9F2, 0xBC4D, 0xB9F3, 0xBC4E, 0x9370, 0xBC4F, 0x9371,\n\t0xBC50, 0xB9F4, 0xBC51, 0x9372, 0xBC52, 0x9373, 0xBC53, 0x9374,\n\t0xBC54, 0x9375, 0xBC55, 0x9376, 0xBC56, 0x9377, 0xBC57, 0x9378,\n\t0xBC58, 0x9379, 0xBC59, 0x937A, 0xBC5A, 0x9381, 0xBC5B, 0x9382,\n\t0xBC5C, 0x9383, 0xBC5D, 0xB9F5, 0xBC5E, 0x9384, 0xBC5F, 0x9385,\n\t0xBC60, 0x9386, 0xBC61, 0x9387, 0xBC62, 0x9388, 0xBC63, 0x9389,\n\t0xBC64, 0x938A, 0xBC65, 0x938B, 0xBC66, 0x938C, 0xBC67, 0x938D,\n\t0xBC68, 0x938E, 0xBC69, 0x938F, 0xBC6A, 0x9390, 0xBC6B, 0x9391,\n\t0xBC6C, 0x9392, 0xBC6D, 0x9393, 0xBC6E, 0x9394, 0xBC6F, 0x9395,\n\t0xBC70, 0x9396, 0xBC71, 0x9397, 0xBC72, 0x9398, 0xBC73, 0x9399,\n\t0xBC74, 0x939A, 0xBC75, 0x939B, 0xBC76, 0x939C, 0xBC77, 0x939D,\n\t0xBC78, 0x939E, 0xBC79, 0x939F, 0xBC7A, 0x93A0, 0xBC7B, 0x93A1,\n\t0xBC7C, 0x93A2, 0xBC7D, 0x93A3, 0xBC7E, 0x93A4, 0xBC7F, 0x93A5,\n\t0xBC80, 0x93A6, 0xBC81, 0x93A7, 0xBC82, 0x93A8, 0xBC83, 0x93A9,\n\t0xBC84, 0xB9F6, 0xBC85, 0xB9F7, 0xBC86, 0x93AA, 0xBC87, 0x93AB,\n\t0xBC88, 0xB9F8, 0xBC89, 0x93AC, 0xBC8A, 0x93AD, 0xBC8B, 0xB9F9,\n\t0xBC8C, 0xB9FA, 0xBC8D, 0x93AE, 0xBC8E, 0xB9FB, 0xBC8F, 0x93AF,\n\t0xBC90, 0x93B0, 0xBC91, 0x93B1, 0xBC92, 0x93B2, 0xBC93, 0x93B3,\n\t0xBC94, 0xB9FC, 0xBC95, 0xB9FD, 0xBC96, 0x93B4, 0xBC97, 0xB9FE,\n\t0xBC98, 0x93B5, 0xBC99, 0xBAA1, 0xBC9A, 0xBAA2, 0xBC9B, 0x93B6,\n\t0xBC9C, 0x93B7, 0xBC9D, 0x93B8, 0xBC9E, 0x93B9, 0xBC9F, 0x93BA,\n\t0xBCA0, 0xBAA3, 0xBCA1, 0xBAA4, 0xBCA2, 0x93BB, 0xBCA3, 0x93BC,\n\t0xBCA4, 0xBAA5, 0xBCA5, 0x93BD, 0xBCA6, 0x93BE, 0xBCA7, 0xBAA6,\n\t0xBCA8, 0xBAA7, 0xBCA9, 0x93BF, 0xBCAA, 0x93C0, 0xBCAB, 0x93C1,\n\t0xBCAC, 0x93C2, 0xBCAD, 0x93C3, 0xBCAE, 0x93C4, 0xBCAF, 0x93C5,\n\t0xBCB0, 0xBAA8, 0xBCB1, 0xBAA9, 0xBCB2, 0x93C6, 0xBCB3, 0xBAAA,\n\t0xBCB4, 0xBAAB, 0xBCB5, 0xBAAC, 0xBCB6, 0x93C7, 0xBCB7, 0x93C8,\n\t0xBCB8, 0x93C9, 0xBCB9, 0x93CA, 0xBCBA, 0x93CB, 0xBCBB, 0x93CC,\n\t0xBCBC, 0xBAAD, 0xBCBD, 0xBAAE, 0xBCBE, 0x93CD, 0xBCBF, 0x93CE,\n\t0xBCC0, 0xBAAF, 0xBCC1, 0x93CF, 0xBCC2, 0x93D0, 0xBCC3, 0x93D1,\n\t0xBCC4, 0xBAB0, 0xBCC5, 0x93D2, 0xBCC6, 0x93D3, 0xBCC7, 0x93D4,\n\t0xBCC8, 0x93D5, 0xBCC9, 0x93D6, 0xBCCA, 0x93D7, 0xBCCB, 0x93D8,\n\t0xBCCC, 0x93D9, 0xBCCD, 0xBAB1, 0xBCCE, 0x93DA, 0xBCCF, 0xBAB2,\n\t0xBCD0, 0xBAB3, 0xBCD1, 0xBAB4, 0xBCD2, 0x93DB, 0xBCD3, 0x93DC,\n\t0xBCD4, 0x93DD, 0xBCD5, 0xBAB5, 0xBCD6, 0x93DE, 0xBCD7, 0x93DF,\n\t0xBCD8, 0xBAB6, 0xBCD9, 0x93E0, 0xBCDA, 0x93E1, 0xBCDB, 0x93E2,\n\t0xBCDC, 0xBAB7, 0xBCDD, 0x93E3, 0xBCDE, 0x93E4, 0xBCDF, 0x93E5,\n\t0xBCE0, 0x93E6, 0xBCE1, 0x93E7, 0xBCE2, 0x93E8, 0xBCE3, 0x93E9,\n\t0xBCE4, 0x93EA, 0xBCE5, 0x93EB, 0xBCE6, 0x93EC, 0xBCE7, 0x93ED,\n\t0xBCE8, 0x93EE, 0xBCE9, 0x93EF, 0xBCEA, 0x93F0, 0xBCEB, 0x93F1,\n\t0xBCEC, 0x93F2, 0xBCED, 0x93F3, 0xBCEE, 0x93F4, 0xBCEF, 0x93F5,\n\t0xBCF0, 0x93F6, 0xBCF1, 0x93F7, 0xBCF2, 0x93F8, 0xBCF3, 0x93F9,\n\t0xBCF4, 0xBAB8, 0xBCF5, 0xBAB9, 0xBCF6, 0xBABA, 0xBCF7, 0x93FA,\n\t0xBCF8, 0xBABB, 0xBCF9, 0x93FB, 0xBCFA, 0x93FC, 0xBCFB, 0x93FD,\n\t0xBCFC, 0xBABC, 0xBCFD, 0x93FE, 0xBCFE, 0x9441, 0xBCFF, 0x9442,\n\t0xBD00, 0x9443, 0xBD01, 0x9444, 0xBD02, 0x9445, 0xBD03, 0x9446,\n\t0xBD04, 0xBABD, 0xBD05, 0xBABE, 0xBD06, 0x9447, 0xBD07, 0xBABF,\n\t0xBD08, 0x9448, 0xBD09, 0xBAC0, 0xBD0A, 0x9449, 0xBD0B, 0x944A,\n\t0xBD0C, 0x944B, 0xBD0D, 0x944C, 0xBD0E, 0x944D, 0xBD0F, 0x944E,\n\t0xBD10, 0xBAC1, 0xBD11, 0x944F, 0xBD12, 0x9450, 0xBD13, 0x9451,\n\t0xBD14, 0xBAC2, 0xBD15, 0x9452, 0xBD16, 0x9453, 0xBD17, 0x9454,\n\t0xBD18, 0x9455, 0xBD19, 0x9456, 0xBD1A, 0x9457, 0xBD1B, 0x9458,\n\t0xBD1C, 0x9459, 0xBD1D, 0x945A, 0xBD1E, 0x9461, 0xBD1F, 0x9462,\n\t0xBD20, 0x9463, 0xBD21, 0x9464, 0xBD22, 0x9465, 0xBD23, 0x9466,\n\t0xBD24, 0xBAC3, 0xBD25, 0x9467, 0xBD26, 0x9468, 0xBD27, 0x9469,\n\t0xBD28, 0x946A, 0xBD29, 0x946B, 0xBD2A, 0x946C, 0xBD2B, 0x946D,\n\t0xBD2C, 0xBAC4, 0xBD2D, 0x946E, 0xBD2E, 0x946F, 0xBD2F, 0x9470,\n\t0xBD30, 0x9471, 0xBD31, 0x9472, 0xBD32, 0x9473, 0xBD33, 0x9474,\n\t0xBD34, 0x9475, 0xBD35, 0x9476, 0xBD36, 0x9477, 0xBD37, 0x9478,\n\t0xBD38, 0x9479, 0xBD39, 0x947A, 0xBD3A, 0x9481, 0xBD3B, 0x9482,\n\t0xBD3C, 0x9483, 0xBD3D, 0x9484, 0xBD3E, 0x9485, 0xBD3F, 0x9486,\n\t0xBD40, 0xBAC5, 0xBD41, 0x9487, 0xBD42, 0x9488, 0xBD43, 0x9489,\n\t0xBD44, 0x948A, 0xBD45, 0x948B, 0xBD46, 0x948C, 0xBD47, 0x948D,\n\t0xBD48, 0xBAC6, 0xBD49, 0xBAC7, 0xBD4A, 0x948E, 0xBD4B, 0x948F,\n\t0xBD4C, 0xBAC8, 0xBD4D, 0x9490, 0xBD4E, 0x9491, 0xBD4F, 0x9492,\n\t0xBD50, 0xBAC9, 0xBD51, 0x9493, 0xBD52, 0x9494, 0xBD53, 0x9495,\n\t0xBD54, 0x9496, 0xBD55, 0x9497, 0xBD56, 0x9498, 0xBD57, 0x9499,\n\t0xBD58, 0xBACA, 0xBD59, 0xBACB, 0xBD5A, 0x949A, 0xBD5B, 0x949B,\n\t0xBD5C, 0x949C, 0xBD5D, 0x949D, 0xBD5E, 0x949E, 0xBD5F, 0x949F,\n\t0xBD60, 0x94A0, 0xBD61, 0x94A1, 0xBD62, 0x94A2, 0xBD63, 0x94A3,\n\t0xBD64, 0xBACC, 0xBD65, 0x94A4, 0xBD66, 0x94A5, 0xBD67, 0x94A6,\n\t0xBD68, 0xBACD, 0xBD69, 0x94A7, 0xBD6A, 0x94A8, 0xBD6B, 0x94A9,\n\t0xBD6C, 0x94AA, 0xBD6D, 0x94AB, 0xBD6E, 0x94AC, 0xBD6F, 0x94AD,\n\t0xBD70, 0x94AE, 0xBD71, 0x94AF, 0xBD72, 0x94B0, 0xBD73, 0x94B1,\n\t0xBD74, 0x94B2, 0xBD75, 0x94B3, 0xBD76, 0x94B4, 0xBD77, 0x94B5,\n\t0xBD78, 0x94B6, 0xBD79, 0x94B7, 0xBD7A, 0x94B8, 0xBD7B, 0x94B9,\n\t0xBD7C, 0x94BA, 0xBD7D, 0x94BB, 0xBD7E, 0x94BC, 0xBD7F, 0x94BD,\n\t0xBD80, 0xBACE, 0xBD81, 0xBACF, 0xBD82, 0x94BE, 0xBD83, 0x94BF,\n\t0xBD84, 0xBAD0, 0xBD85, 0x94C0, 0xBD86, 0x94C1, 0xBD87, 0xBAD1,\n\t0xBD88, 0xBAD2, 0xBD89, 0xBAD3, 0xBD8A, 0xBAD4, 0xBD8B, 0x94C2,\n\t0xBD8C, 0x94C3, 0xBD8D, 0x94C4, 0xBD8E, 0x94C5, 0xBD8F, 0x94C6,\n\t0xBD90, 0xBAD5, 0xBD91, 0xBAD6, 0xBD92, 0x94C7, 0xBD93, 0xBAD7,\n\t0xBD94, 0x94C8, 0xBD95, 0xBAD8, 0xBD96, 0x94C9, 0xBD97, 0x94CA,\n\t0xBD98, 0x94CB, 0xBD99, 0xBAD9, 0xBD9A, 0xBADA, 0xBD9B, 0x94CC,\n\t0xBD9C, 0xBADB, 0xBD9D, 0x94CD, 0xBD9E, 0x94CE, 0xBD9F, 0x94CF,\n\t0xBDA0, 0x94D0, 0xBDA1, 0x94D1, 0xBDA2, 0x94D2, 0xBDA3, 0x94D3,\n\t0xBDA4, 0xBADC, 0xBDA5, 0x94D4, 0xBDA6, 0x94D5, 0xBDA7, 0x94D6,\n\t0xBDA8, 0x94D7, 0xBDA9, 0x94D8, 0xBDAA, 0x94D9, 0xBDAB, 0x94DA,\n\t0xBDAC, 0x94DB, 0xBDAD, 0x94DC, 0xBDAE, 0x94DD, 0xBDAF, 0x94DE,\n\t0xBDB0, 0xBADD, 0xBDB1, 0x94DF, 0xBDB2, 0x94E0, 0xBDB3, 0x94E1,\n\t0xBDB4, 0x94E2, 0xBDB5, 0x94E3, 0xBDB6, 0x94E4, 0xBDB7, 0x94E5,\n\t0xBDB8, 0xBADE, 0xBDB9, 0x94E6, 0xBDBA, 0x94E7, 0xBDBB, 0x94E8,\n\t0xBDBC, 0x94E9, 0xBDBD, 0x94EA, 0xBDBE, 0x94EB, 0xBDBF, 0x94EC,\n\t0xBDC0, 0x94ED, 0xBDC1, 0x94EE, 0xBDC2, 0x94EF, 0xBDC3, 0x94F0,\n\t0xBDC4, 0x94F1, 0xBDC5, 0x94F2, 0xBDC6, 0x94F3, 0xBDC7, 0x94F4,\n\t0xBDC8, 0x94F5, 0xBDC9, 0x94F6, 0xBDCA, 0x94F7, 0xBDCB, 0x94F8,\n\t0xBDCC, 0x94F9, 0xBDCD, 0x94FA, 0xBDCE, 0x94FB, 0xBDCF, 0x94FC,\n\t0xBDD0, 0x94FD, 0xBDD1, 0x94FE, 0xBDD2, 0x9541, 0xBDD3, 0x9542,\n\t0xBDD4, 0xBADF, 0xBDD5, 0xBAE0, 0xBDD6, 0x9543, 0xBDD7, 0x9544,\n\t0xBDD8, 0xBAE1, 0xBDD9, 0x9545, 0xBDDA, 0x9546, 0xBDDB, 0x9547,\n\t0xBDDC, 0xBAE2, 0xBDDD, 0x9548, 0xBDDE, 0x9549, 0xBDDF, 0x954A,\n\t0xBDE0, 0x954B, 0xBDE1, 0x954C, 0xBDE2, 0x954D, 0xBDE3, 0x954E,\n\t0xBDE4, 0x954F, 0xBDE5, 0x9550, 0xBDE6, 0x9551, 0xBDE7, 0x9552,\n\t0xBDE8, 0x9553, 0xBDE9, 0xBAE3, 0xBDEA, 0x9554, 0xBDEB, 0x9555,\n\t0xBDEC, 0x9556, 0xBDED, 0x9557, 0xBDEE, 0x9558, 0xBDEF, 0x9559,\n\t0xBDF0, 0xBAE4, 0xBDF1, 0x955A, 0xBDF2, 0x9561, 0xBDF3, 0x9562,\n\t0xBDF4, 0xBAE5, 0xBDF5, 0x9563, 0xBDF6, 0x9564, 0xBDF7, 0x9565,\n\t0xBDF8, 0xBAE6, 0xBDF9, 0x9566, 0xBDFA, 0x9567, 0xBDFB, 0x9568,\n\t0xBDFC, 0x9569, 0xBDFD, 0x956A, 0xBDFE, 0x956B, 0xBDFF, 0x956C,\n\t0xBE00, 0xBAE7, 0xBE01, 0x956D, 0xBE02, 0x956E, 0xBE03, 0xBAE8,\n\t0xBE04, 0x956F, 0xBE05, 0xBAE9, 0xBE06, 0x9570, 0xBE07, 0x9571,\n\t0xBE08, 0x9572, 0xBE09, 0x9573, 0xBE0A, 0x9574, 0xBE0B, 0x9575,\n\t0xBE0C, 0xBAEA, 0xBE0D, 0xBAEB, 0xBE0E, 0x9576, 0xBE0F, 0x9577,\n\t0xBE10, 0xBAEC, 0xBE11, 0x9578, 0xBE12, 0x9579, 0xBE13, 0x957A,\n\t0xBE14, 0xBAED, 0xBE15, 0x9581, 0xBE16, 0x9582, 0xBE17, 0x9583,\n\t0xBE18, 0x9584, 0xBE19, 0x9585, 0xBE1A, 0x9586, 0xBE1B, 0x9587,\n\t0xBE1C, 0xBAEE, 0xBE1D, 0xBAEF, 0xBE1E, 0x9588, 0xBE1F, 0xBAF0,\n\t0xBE20, 0x9589, 0xBE21, 0x958A, 0xBE22, 0x958B, 0xBE23, 0x958C,\n\t0xBE24, 0x958D, 0xBE25, 0x958E, 0xBE26, 0x958F, 0xBE27, 0x9590,\n\t0xBE28, 0x9591, 0xBE29, 0x9592, 0xBE2A, 0x9593, 0xBE2B, 0x9594,\n\t0xBE2C, 0x9595, 0xBE2D, 0x9596, 0xBE2E, 0x9597, 0xBE2F, 0x9598,\n\t0xBE30, 0x9599, 0xBE31, 0x959A, 0xBE32, 0x959B, 0xBE33, 0x959C,\n\t0xBE34, 0x959D, 0xBE35, 0x959E, 0xBE36, 0x959F, 0xBE37, 0x95A0,\n\t0xBE38, 0x95A1, 0xBE39, 0x95A2, 0xBE3A, 0x95A3, 0xBE3B, 0x95A4,\n\t0xBE3C, 0x95A5, 0xBE3D, 0x95A6, 0xBE3E, 0x95A7, 0xBE3F, 0x95A8,\n\t0xBE40, 0x95A9, 0xBE41, 0x95AA, 0xBE42, 0x95AB, 0xBE43, 0x95AC,\n\t0xBE44, 0xBAF1, 0xBE45, 0xBAF2, 0xBE46, 0x95AD, 0xBE47, 0x95AE,\n\t0xBE48, 0xBAF3, 0xBE49, 0x95AF, 0xBE4A, 0x95B0, 0xBE4B, 0x95B1,\n\t0xBE4C, 0xBAF4, 0xBE4D, 0x95B2, 0xBE4E, 0xBAF5, 0xBE4F, 0x95B3,\n\t0xBE50, 0x95B4, 0xBE51, 0x95B5, 0xBE52, 0x95B6, 0xBE53, 0x95B7,\n\t0xBE54, 0xBAF6, 0xBE55, 0xBAF7, 0xBE56, 0x95B8, 0xBE57, 0xBAF8,\n\t0xBE58, 0x95B9, 0xBE59, 0xBAF9, 0xBE5A, 0xBAFA, 0xBE5B, 0xBAFB,\n\t0xBE5C, 0x95BA, 0xBE5D, 0x95BB, 0xBE5E, 0x95BC, 0xBE5F, 0x95BD,\n\t0xBE60, 0xBAFC, 0xBE61, 0xBAFD, 0xBE62, 0x95BE, 0xBE63, 0x95BF,\n\t0xBE64, 0xBAFE, 0xBE65, 0x95C0, 0xBE66, 0x95C1, 0xBE67, 0x95C2,\n\t0xBE68, 0xBBA1, 0xBE69, 0x95C3, 0xBE6A, 0xBBA2, 0xBE6B, 0x95C4,\n\t0xBE6C, 0x95C5, 0xBE6D, 0x95C6, 0xBE6E, 0x95C7, 0xBE6F, 0x95C8,\n\t0xBE70, 0xBBA3, 0xBE71, 0xBBA4, 0xBE72, 0x95C9, 0xBE73, 0xBBA5,\n\t0xBE74, 0xBBA6, 0xBE75, 0xBBA7, 0xBE76, 0x95CA, 0xBE77, 0x95CB,\n\t0xBE78, 0x95CC, 0xBE79, 0x95CD, 0xBE7A, 0x95CE, 0xBE7B, 0xBBA8,\n\t0xBE7C, 0xBBA9, 0xBE7D, 0xBBAA, 0xBE7E, 0x95CF, 0xBE7F, 0x95D0,\n\t0xBE80, 0xBBAB, 0xBE81, 0x95D1, 0xBE82, 0x95D2, 0xBE83, 0x95D3,\n\t0xBE84, 0xBBAC, 0xBE85, 0x95D4, 0xBE86, 0x95D5, 0xBE87, 0x95D6,\n\t0xBE88, 0x95D7, 0xBE89, 0x95D8, 0xBE8A, 0x95D9, 0xBE8B, 0x95DA,\n\t0xBE8C, 0xBBAD, 0xBE8D, 0xBBAE, 0xBE8E, 0x95DB, 0xBE8F, 0xBBAF,\n\t0xBE90, 0xBBB0, 0xBE91, 0xBBB1, 0xBE92, 0x95DC, 0xBE93, 0x95DD,\n\t0xBE94, 0x95DE, 0xBE95, 0x95DF, 0xBE96, 0x95E0, 0xBE97, 0x95E1,\n\t0xBE98, 0xBBB2, 0xBE99, 0xBBB3, 0xBE9A, 0x95E2, 0xBE9B, 0x95E3,\n\t0xBE9C, 0x95E4, 0xBE9D, 0x95E5, 0xBE9E, 0x95E6, 0xBE9F, 0x95E7,\n\t0xBEA0, 0x95E8, 0xBEA1, 0x95E9, 0xBEA2, 0x95EA, 0xBEA3, 0x95EB,\n\t0xBEA4, 0x95EC, 0xBEA5, 0x95ED, 0xBEA6, 0x95EE, 0xBEA7, 0x95EF,\n\t0xBEA8, 0xBBB4, 0xBEA9, 0x95F0, 0xBEAA, 0x95F1, 0xBEAB, 0x95F2,\n\t0xBEAC, 0x95F3, 0xBEAD, 0x95F4, 0xBEAE, 0x95F5, 0xBEAF, 0x95F6,\n\t0xBEB0, 0x95F7, 0xBEB1, 0x95F8, 0xBEB2, 0x95F9, 0xBEB3, 0x95FA,\n\t0xBEB4, 0x95FB, 0xBEB5, 0x95FC, 0xBEB6, 0x95FD, 0xBEB7, 0x95FE,\n\t0xBEB8, 0x9641, 0xBEB9, 0x9642, 0xBEBA, 0x9643, 0xBEBB, 0x9644,\n\t0xBEBC, 0x9645, 0xBEBD, 0x9646, 0xBEBE, 0x9647, 0xBEBF, 0x9648,\n\t0xBEC0, 0x9649, 0xBEC1, 0x964A, 0xBEC2, 0x964B, 0xBEC3, 0x964C,\n\t0xBEC4, 0x964D, 0xBEC5, 0x964E, 0xBEC6, 0x964F, 0xBEC7, 0x9650,\n\t0xBEC8, 0x9651, 0xBEC9, 0x9652, 0xBECA, 0x9653, 0xBECB, 0x9654,\n\t0xBECC, 0x9655, 0xBECD, 0x9656, 0xBECE, 0x9657, 0xBECF, 0x9658,\n\t0xBED0, 0xBBB5, 0xBED1, 0xBBB6, 0xBED2, 0x9659, 0xBED3, 0x965A,\n\t0xBED4, 0xBBB7, 0xBED5, 0x9661, 0xBED6, 0x9662, 0xBED7, 0xBBB8,\n\t0xBED8, 0xBBB9, 0xBED9, 0x9663, 0xBEDA, 0x9664, 0xBEDB, 0x9665,\n\t0xBEDC, 0x9666, 0xBEDD, 0x9667, 0xBEDE, 0x9668, 0xBEDF, 0x9669,\n\t0xBEE0, 0xBBBA, 0xBEE1, 0x966A, 0xBEE2, 0x966B, 0xBEE3, 0xBBBB,\n\t0xBEE4, 0xBBBC, 0xBEE5, 0xBBBD, 0xBEE6, 0x966C, 0xBEE7, 0x966D,\n\t0xBEE8, 0x966E, 0xBEE9, 0x966F, 0xBEEA, 0x9670, 0xBEEB, 0x9671,\n\t0xBEEC, 0xBBBE, 0xBEED, 0x9672, 0xBEEE, 0x9673, 0xBEEF, 0x9674,\n\t0xBEF0, 0x9675, 0xBEF1, 0x9676, 0xBEF2, 0x9677, 0xBEF3, 0x9678,\n\t0xBEF4, 0x9679, 0xBEF5, 0x967A, 0xBEF6, 0x9681, 0xBEF7, 0x9682,\n\t0xBEF8, 0x9683, 0xBEF9, 0x9684, 0xBEFA, 0x9685, 0xBEFB, 0x9686,\n\t0xBEFC, 0x9687, 0xBEFD, 0x9688, 0xBEFE, 0x9689, 0xBEFF, 0x968A,\n\t0xBF00, 0x968B, 0xBF01, 0xBBBF, 0xBF02, 0x968C, 0xBF03, 0x968D,\n\t0xBF04, 0x968E, 0xBF05, 0x968F, 0xBF06, 0x9690, 0xBF07, 0x9691,\n\t0xBF08, 0xBBC0, 0xBF09, 0xBBC1, 0xBF0A, 0x9692, 0xBF0B, 0x9693,\n\t0xBF0C, 0x9694, 0xBF0D, 0x9695, 0xBF0E, 0x9696, 0xBF0F, 0x9697,\n\t0xBF10, 0x9698, 0xBF11, 0x9699, 0xBF12, 0x969A, 0xBF13, 0x969B,\n\t0xBF14, 0x969C, 0xBF15, 0x969D, 0xBF16, 0x969E, 0xBF17, 0x969F,\n\t0xBF18, 0xBBC2, 0xBF19, 0xBBC3, 0xBF1A, 0x96A0, 0xBF1B, 0xBBC4,\n\t0xBF1C, 0xBBC5, 0xBF1D, 0xBBC6, 0xBF1E, 0x96A1, 0xBF1F, 0x96A2,\n\t0xBF20, 0x96A3, 0xBF21, 0x96A4, 0xBF22, 0x96A5, 0xBF23, 0x96A6,\n\t0xBF24, 0x96A7, 0xBF25, 0x96A8, 0xBF26, 0x96A9, 0xBF27, 0x96AA,\n\t0xBF28, 0x96AB, 0xBF29, 0x96AC, 0xBF2A, 0x96AD, 0xBF2B, 0x96AE,\n\t0xBF2C, 0x96AF, 0xBF2D, 0x96B0, 0xBF2E, 0x96B1, 0xBF2F, 0x96B2,\n\t0xBF30, 0x96B3, 0xBF31, 0x96B4, 0xBF32, 0x96B5, 0xBF33, 0x96B6,\n\t0xBF34, 0x96B7, 0xBF35, 0x96B8, 0xBF36, 0x96B9, 0xBF37, 0x96BA,\n\t0xBF38, 0x96BB, 0xBF39, 0x96BC, 0xBF3A, 0x96BD, 0xBF3B, 0x96BE,\n\t0xBF3C, 0x96BF, 0xBF3D, 0x96C0, 0xBF3E, 0x96C1, 0xBF3F, 0x96C2,\n\t0xBF40, 0xBBC7, 0xBF41, 0xBBC8, 0xBF42, 0x96C3, 0xBF43, 0x96C4,\n\t0xBF44, 0xBBC9, 0xBF45, 0x96C5, 0xBF46, 0x96C6, 0xBF47, 0x96C7,\n\t0xBF48, 0xBBCA, 0xBF49, 0x96C8, 0xBF4A, 0x96C9, 0xBF4B, 0x96CA,\n\t0xBF4C, 0x96CB, 0xBF4D, 0x96CC, 0xBF4E, 0x96CD, 0xBF4F, 0x96CE,\n\t0xBF50, 0xBBCB, 0xBF51, 0xBBCC, 0xBF52, 0x96CF, 0xBF53, 0x96D0,\n\t0xBF54, 0x96D1, 0xBF55, 0xBBCD, 0xBF56, 0x96D2, 0xBF57, 0x96D3,\n\t0xBF58, 0x96D4, 0xBF59, 0x96D5, 0xBF5A, 0x96D6, 0xBF5B, 0x96D7,\n\t0xBF5C, 0x96D8, 0xBF5D, 0x96D9, 0xBF5E, 0x96DA, 0xBF5F, 0x96DB,\n\t0xBF60, 0x96DC, 0xBF61, 0x96DD, 0xBF62, 0x96DE, 0xBF63, 0x96DF,\n\t0xBF64, 0x96E0, 0xBF65, 0x96E1, 0xBF66, 0x96E2, 0xBF67, 0x96E3,\n\t0xBF68, 0x96E4, 0xBF69, 0x96E5, 0xBF6A, 0x96E6, 0xBF6B, 0x96E7,\n\t0xBF6C, 0x96E8, 0xBF6D, 0x96E9, 0xBF6E, 0x96EA, 0xBF6F, 0x96EB,\n\t0xBF70, 0x96EC, 0xBF71, 0x96ED, 0xBF72, 0x96EE, 0xBF73, 0x96EF,\n\t0xBF74, 0x96F0, 0xBF75, 0x96F1, 0xBF76, 0x96F2, 0xBF77, 0x96F3,\n\t0xBF78, 0x96F4, 0xBF79, 0x96F5, 0xBF7A, 0x96F6, 0xBF7B, 0x96F7,\n\t0xBF7C, 0x96F8, 0xBF7D, 0x96F9, 0xBF7E, 0x96FA, 0xBF7F, 0x96FB,\n\t0xBF80, 0x96FC, 0xBF81, 0x96FD, 0xBF82, 0x96FE, 0xBF83, 0x9741,\n\t0xBF84, 0x9742, 0xBF85, 0x9743, 0xBF86, 0x9744, 0xBF87, 0x9745,\n\t0xBF88, 0x9746, 0xBF89, 0x9747, 0xBF8A, 0x9748, 0xBF8B, 0x9749,\n\t0xBF8C, 0x974A, 0xBF8D, 0x974B, 0xBF8E, 0x974C, 0xBF8F, 0x974D,\n\t0xBF90, 0x974E, 0xBF91, 0x974F, 0xBF92, 0x9750, 0xBF93, 0x9751,\n\t0xBF94, 0xBBCE, 0xBF95, 0x9752, 0xBF96, 0x9753, 0xBF97, 0x9754,\n\t0xBF98, 0x9755, 0xBF99, 0x9756, 0xBF9A, 0x9757, 0xBF9B, 0x9758,\n\t0xBF9C, 0x9759, 0xBF9D, 0x975A, 0xBF9E, 0x9761, 0xBF9F, 0x9762,\n\t0xBFA0, 0x9763, 0xBFA1, 0x9764, 0xBFA2, 0x9765, 0xBFA3, 0x9766,\n\t0xBFA4, 0x9767, 0xBFA5, 0x9768, 0xBFA6, 0x9769, 0xBFA7, 0x976A,\n\t0xBFA8, 0x976B, 0xBFA9, 0x976C, 0xBFAA, 0x976D, 0xBFAB, 0x976E,\n\t0xBFAC, 0x976F, 0xBFAD, 0x9770, 0xBFAE, 0x9771, 0xBFAF, 0x9772,\n\t0xBFB0, 0xBBCF, 0xBFB1, 0x9773, 0xBFB2, 0x9774, 0xBFB3, 0x9775,\n\t0xBFB4, 0x9776, 0xBFB5, 0x9777, 0xBFB6, 0x9778, 0xBFB7, 0x9779,\n\t0xBFB8, 0x977A, 0xBFB9, 0x9781, 0xBFBA, 0x9782, 0xBFBB, 0x9783,\n\t0xBFBC, 0x9784, 0xBFBD, 0x9785, 0xBFBE, 0x9786, 0xBFBF, 0x9787,\n\t0xBFC0, 0x9788, 0xBFC1, 0x9789, 0xBFC2, 0x978A, 0xBFC3, 0x978B,\n\t0xBFC4, 0x978C, 0xBFC5, 0xBBD0, 0xBFC6, 0x978D, 0xBFC7, 0x978E,\n\t0xBFC8, 0x978F, 0xBFC9, 0x9790, 0xBFCA, 0x9791, 0xBFCB, 0x9792,\n\t0xBFCC, 0xBBD1, 0xBFCD, 0xBBD2, 0xBFCE, 0x9793, 0xBFCF, 0x9794,\n\t0xBFD0, 0xBBD3, 0xBFD1, 0x9795, 0xBFD2, 0x9796, 0xBFD3, 0x9797,\n\t0xBFD4, 0xBBD4, 0xBFD5, 0x9798, 0xBFD6, 0x9799, 0xBFD7, 0x979A,\n\t0xBFD8, 0x979B, 0xBFD9, 0x979C, 0xBFDA, 0x979D, 0xBFDB, 0x979E,\n\t0xBFDC, 0xBBD5, 0xBFDD, 0x979F, 0xBFDE, 0x97A0, 0xBFDF, 0xBBD6,\n\t0xBFE0, 0x97A1, 0xBFE1, 0xBBD7, 0xBFE2, 0x97A2, 0xBFE3, 0x97A3,\n\t0xBFE4, 0x97A4, 0xBFE5, 0x97A5, 0xBFE6, 0x97A6, 0xBFE7, 0x97A7,\n\t0xBFE8, 0x97A8, 0xBFE9, 0x97A9, 0xBFEA, 0x97AA, 0xBFEB, 0x97AB,\n\t0xBFEC, 0x97AC, 0xBFED, 0x97AD, 0xBFEE, 0x97AE, 0xBFEF, 0x97AF,\n\t0xBFF0, 0x97B0, 0xBFF1, 0x97B1, 0xBFF2, 0x97B2, 0xBFF3, 0x97B3,\n\t0xBFF4, 0x97B4, 0xBFF5, 0x97B5, 0xBFF6, 0x97B6, 0xBFF7, 0x97B7,\n\t0xBFF8, 0x97B8, 0xBFF9, 0x97B9, 0xBFFA, 0x97BA, 0xBFFB, 0x97BB,\n\t0xBFFC, 0x97BC, 0xBFFD, 0x97BD, 0xBFFE, 0x97BE, 0xBFFF, 0x97BF,\n\t0xC000, 0x97C0, 0xC001, 0x97C1, 0xC002, 0x97C2, 0xC003, 0x97C3,\n\t0xC004, 0x97C4, 0xC005, 0x97C5, 0xC006, 0x97C6, 0xC007, 0x97C7,\n\t0xC008, 0x97C8, 0xC009, 0x97C9, 0xC00A, 0x97CA, 0xC00B, 0x97CB,\n\t0xC00C, 0x97CC, 0xC00D, 0x97CD, 0xC00E, 0x97CE, 0xC00F, 0x97CF,\n\t0xC010, 0x97D0, 0xC011, 0x97D1, 0xC012, 0x97D2, 0xC013, 0x97D3,\n\t0xC014, 0x97D4, 0xC015, 0x97D5, 0xC016, 0x97D6, 0xC017, 0x97D7,\n\t0xC018, 0x97D8, 0xC019, 0x97D9, 0xC01A, 0x97DA, 0xC01B, 0x97DB,\n\t0xC01C, 0x97DC, 0xC01D, 0x97DD, 0xC01E, 0x97DE, 0xC01F, 0x97DF,\n\t0xC020, 0x97E0, 0xC021, 0x97E1, 0xC022, 0x97E2, 0xC023, 0x97E3,\n\t0xC024, 0x97E4, 0xC025, 0x97E5, 0xC026, 0x97E6, 0xC027, 0x97E7,\n\t0xC028, 0x97E8, 0xC029, 0x97E9, 0xC02A, 0x97EA, 0xC02B, 0x97EB,\n\t0xC02C, 0x97EC, 0xC02D, 0x97ED, 0xC02E, 0x97EE, 0xC02F, 0x97EF,\n\t0xC030, 0x97F0, 0xC031, 0x97F1, 0xC032, 0x97F2, 0xC033, 0x97F3,\n\t0xC034, 0x97F4, 0xC035, 0x97F5, 0xC036, 0x97F6, 0xC037, 0x97F7,\n\t0xC038, 0x97F8, 0xC039, 0x97F9, 0xC03A, 0x97FA, 0xC03B, 0x97FB,\n\t0xC03C, 0xBBD8, 0xC03D, 0x97FC, 0xC03E, 0x97FD, 0xC03F, 0x97FE,\n\t0xC040, 0x9841, 0xC041, 0x9842, 0xC042, 0x9843, 0xC043, 0x9844,\n\t0xC044, 0x9845, 0xC045, 0x9846, 0xC046, 0x9847, 0xC047, 0x9848,\n\t0xC048, 0x9849, 0xC049, 0x984A, 0xC04A, 0x984B, 0xC04B, 0x984C,\n\t0xC04C, 0x984D, 0xC04D, 0x984E, 0xC04E, 0x984F, 0xC04F, 0x9850,\n\t0xC050, 0x9851, 0xC051, 0xBBD9, 0xC052, 0x9852, 0xC053, 0x9853,\n\t0xC054, 0x9854, 0xC055, 0x9855, 0xC056, 0x9856, 0xC057, 0x9857,\n\t0xC058, 0xBBDA, 0xC059, 0x9858, 0xC05A, 0x9859, 0xC05B, 0x985A,\n\t0xC05C, 0xBBDB, 0xC05D, 0x9861, 0xC05E, 0x9862, 0xC05F, 0x9863,\n\t0xC060, 0xBBDC, 0xC061, 0x9864, 0xC062, 0x9865, 0xC063, 0x9866,\n\t0xC064, 0x9867, 0xC065, 0x9868, 0xC066, 0x9869, 0xC067, 0x986A,\n\t0xC068, 0xBBDD, 0xC069, 0xBBDE, 0xC06A, 0x986B, 0xC06B, 0x986C,\n\t0xC06C, 0x986D, 0xC06D, 0x986E, 0xC06E, 0x986F, 0xC06F, 0x9870,\n\t0xC070, 0x9871, 0xC071, 0x9872, 0xC072, 0x9873, 0xC073, 0x9874,\n\t0xC074, 0x9875, 0xC075, 0x9876, 0xC076, 0x9877, 0xC077, 0x9878,\n\t0xC078, 0x9879, 0xC079, 0x987A, 0xC07A, 0x9881, 0xC07B, 0x9882,\n\t0xC07C, 0x9883, 0xC07D, 0x9884, 0xC07E, 0x9885, 0xC07F, 0x9886,\n\t0xC080, 0x9887, 0xC081, 0x9888, 0xC082, 0x9889, 0xC083, 0x988A,\n\t0xC084, 0x988B, 0xC085, 0x988C, 0xC086, 0x988D, 0xC087, 0x988E,\n\t0xC088, 0x988F, 0xC089, 0x9890, 0xC08A, 0x9891, 0xC08B, 0x9892,\n\t0xC08C, 0x9893, 0xC08D, 0x9894, 0xC08E, 0x9895, 0xC08F, 0x9896,\n\t0xC090, 0xBBDF, 0xC091, 0xBBE0, 0xC092, 0x9897, 0xC093, 0x9898,\n\t0xC094, 0xBBE1, 0xC095, 0x9899, 0xC096, 0x989A, 0xC097, 0x989B,\n\t0xC098, 0xBBE2, 0xC099, 0x989C, 0xC09A, 0x989D, 0xC09B, 0x989E,\n\t0xC09C, 0x989F, 0xC09D, 0x98A0, 0xC09E, 0x98A1, 0xC09F, 0x98A2,\n\t0xC0A0, 0xBBE3, 0xC0A1, 0xBBE4, 0xC0A2, 0x98A3, 0xC0A3, 0xBBE5,\n\t0xC0A4, 0x98A4, 0xC0A5, 0xBBE6, 0xC0A6, 0x98A5, 0xC0A7, 0x98A6,\n\t0xC0A8, 0x98A7, 0xC0A9, 0x98A8, 0xC0AA, 0x98A9, 0xC0AB, 0x98AA,\n\t0xC0AC, 0xBBE7, 0xC0AD, 0xBBE8, 0xC0AE, 0x98AB, 0xC0AF, 0xBBE9,\n\t0xC0B0, 0xBBEA, 0xC0B1, 0x98AC, 0xC0B2, 0x98AD, 0xC0B3, 0xBBEB,\n\t0xC0B4, 0xBBEC, 0xC0B5, 0xBBED, 0xC0B6, 0xBBEE, 0xC0B7, 0x98AE,\n\t0xC0B8, 0x98AF, 0xC0B9, 0x98B0, 0xC0BA, 0x98B1, 0xC0BB, 0x98B2,\n\t0xC0BC, 0xBBEF, 0xC0BD, 0xBBF0, 0xC0BE, 0x98B3, 0xC0BF, 0xBBF1,\n\t0xC0C0, 0xBBF2, 0xC0C1, 0xBBF3, 0xC0C2, 0x98B4, 0xC0C3, 0x98B5,\n\t0xC0C4, 0x98B6, 0xC0C5, 0xBBF4, 0xC0C6, 0x98B7, 0xC0C7, 0x98B8,\n\t0xC0C8, 0xBBF5, 0xC0C9, 0xBBF6, 0xC0CA, 0x98B9, 0xC0CB, 0x98BA,\n\t0xC0CC, 0xBBF7, 0xC0CD, 0x98BB, 0xC0CE, 0x98BC, 0xC0CF, 0x98BD,\n\t0xC0D0, 0xBBF8, 0xC0D1, 0x98BE, 0xC0D2, 0x98BF, 0xC0D3, 0x98C0,\n\t0xC0D4, 0x98C1, 0xC0D5, 0x98C2, 0xC0D6, 0x98C3, 0xC0D7, 0x98C4,\n\t0xC0D8, 0xBBF9, 0xC0D9, 0xBBFA, 0xC0DA, 0x98C5, 0xC0DB, 0xBBFB,\n\t0xC0DC, 0xBBFC, 0xC0DD, 0xBBFD, 0xC0DE, 0x98C6, 0xC0DF, 0x98C7,\n\t0xC0E0, 0x98C8, 0xC0E1, 0x98C9, 0xC0E2, 0x98CA, 0xC0E3, 0x98CB,\n\t0xC0E4, 0xBBFE, 0xC0E5, 0xBCA1, 0xC0E6, 0x98CC, 0xC0E7, 0x98CD,\n\t0xC0E8, 0xBCA2, 0xC0E9, 0x98CE, 0xC0EA, 0x98CF, 0xC0EB, 0x98D0,\n\t0xC0EC, 0xBCA3, 0xC0ED, 0x98D1, 0xC0EE, 0x98D2, 0xC0EF, 0x98D3,\n\t0xC0F0, 0x98D4, 0xC0F1, 0x98D5, 0xC0F2, 0x98D6, 0xC0F3, 0x98D7,\n\t0xC0F4, 0xBCA4, 0xC0F5, 0xBCA5, 0xC0F6, 0x98D8, 0xC0F7, 0xBCA6,\n\t0xC0F8, 0x98D9, 0xC0F9, 0xBCA7, 0xC0FA, 0x98DA, 0xC0FB, 0x98DB,\n\t0xC0FC, 0x98DC, 0xC0FD, 0x98DD, 0xC0FE, 0x98DE, 0xC0FF, 0x98DF,\n\t0xC100, 0xBCA8, 0xC101, 0x98E0, 0xC102, 0x98E1, 0xC103, 0x98E2,\n\t0xC104, 0xBCA9, 0xC105, 0x98E3, 0xC106, 0x98E4, 0xC107, 0x98E5,\n\t0xC108, 0xBCAA, 0xC109, 0x98E6, 0xC10A, 0x98E7, 0xC10B, 0x98E8,\n\t0xC10C, 0x98E9, 0xC10D, 0x98EA, 0xC10E, 0x98EB, 0xC10F, 0x98EC,\n\t0xC110, 0xBCAB, 0xC111, 0x98ED, 0xC112, 0x98EE, 0xC113, 0x98EF,\n\t0xC114, 0x98F0, 0xC115, 0xBCAC, 0xC116, 0x98F1, 0xC117, 0x98F2,\n\t0xC118, 0x98F3, 0xC119, 0x98F4, 0xC11A, 0x98F5, 0xC11B, 0x98F6,\n\t0xC11C, 0xBCAD, 0xC11D, 0xBCAE, 0xC11E, 0xBCAF, 0xC11F, 0xBCB0,\n\t0xC120, 0xBCB1, 0xC121, 0x98F7, 0xC122, 0x98F8, 0xC123, 0xBCB2,\n\t0xC124, 0xBCB3, 0xC125, 0x98F9, 0xC126, 0xBCB4, 0xC127, 0xBCB5,\n\t0xC128, 0x98FA, 0xC129, 0x98FB, 0xC12A, 0x98FC, 0xC12B, 0x98FD,\n\t0xC12C, 0xBCB6, 0xC12D, 0xBCB7, 0xC12E, 0x98FE, 0xC12F, 0xBCB8,\n\t0xC130, 0xBCB9, 0xC131, 0xBCBA, 0xC132, 0x9941, 0xC133, 0x9942,\n\t0xC134, 0x9943, 0xC135, 0x9944, 0xC136, 0xBCBB, 0xC137, 0x9945,\n\t0xC138, 0xBCBC, 0xC139, 0xBCBD, 0xC13A, 0x9946, 0xC13B, 0x9947,\n\t0xC13C, 0xBCBE, 0xC13D, 0x9948, 0xC13E, 0x9949, 0xC13F, 0x994A,\n\t0xC140, 0xBCBF, 0xC141, 0x994B, 0xC142, 0x994C, 0xC143, 0x994D,\n\t0xC144, 0x994E, 0xC145, 0x994F, 0xC146, 0x9950, 0xC147, 0x9951,\n\t0xC148, 0xBCC0, 0xC149, 0xBCC1, 0xC14A, 0x9952, 0xC14B, 0xBCC2,\n\t0xC14C, 0xBCC3, 0xC14D, 0xBCC4, 0xC14E, 0x9953, 0xC14F, 0x9954,\n\t0xC150, 0x9955, 0xC151, 0x9956, 0xC152, 0x9957, 0xC153, 0x9958,\n\t0xC154, 0xBCC5, 0xC155, 0xBCC6, 0xC156, 0x9959, 0xC157, 0x995A,\n\t0xC158, 0xBCC7, 0xC159, 0x9961, 0xC15A, 0x9962, 0xC15B, 0x9963,\n\t0xC15C, 0xBCC8, 0xC15D, 0x9964, 0xC15E, 0x9965, 0xC15F, 0x9966,\n\t0xC160, 0x9967, 0xC161, 0x9968, 0xC162, 0x9969, 0xC163, 0x996A,\n\t0xC164, 0xBCC9, 0xC165, 0xBCCA, 0xC166, 0x996B, 0xC167, 0xBCCB,\n\t0xC168, 0xBCCC, 0xC169, 0xBCCD, 0xC16A, 0x996C, 0xC16B, 0x996D,\n\t0xC16C, 0x996E, 0xC16D, 0x996F, 0xC16E, 0x9970, 0xC16F, 0x9971,\n\t0xC170, 0xBCCE, 0xC171, 0x9972, 0xC172, 0x9973, 0xC173, 0x9974,\n\t0xC174, 0xBCCF, 0xC175, 0x9975, 0xC176, 0x9976, 0xC177, 0x9977,\n\t0xC178, 0xBCD0, 0xC179, 0x9978, 0xC17A, 0x9979, 0xC17B, 0x997A,\n\t0xC17C, 0x9981, 0xC17D, 0x9982, 0xC17E, 0x9983, 0xC17F, 0x9984,\n\t0xC180, 0x9985, 0xC181, 0x9986, 0xC182, 0x9987, 0xC183, 0x9988,\n\t0xC184, 0x9989, 0xC185, 0xBCD1, 0xC186, 0x998A, 0xC187, 0x998B,\n\t0xC188, 0x998C, 0xC189, 0x998D, 0xC18A, 0x998E, 0xC18B, 0x998F,\n\t0xC18C, 0xBCD2, 0xC18D, 0xBCD3, 0xC18E, 0xBCD4, 0xC18F, 0x9990,\n\t0xC190, 0xBCD5, 0xC191, 0x9991, 0xC192, 0x9992, 0xC193, 0x9993,\n\t0xC194, 0xBCD6, 0xC195, 0x9994, 0xC196, 0xBCD7, 0xC197, 0x9995,\n\t0xC198, 0x9996, 0xC199, 0x9997, 0xC19A, 0x9998, 0xC19B, 0x9999,\n\t0xC19C, 0xBCD8, 0xC19D, 0xBCD9, 0xC19E, 0x999A, 0xC19F, 0xBCDA,\n\t0xC1A0, 0x999B, 0xC1A1, 0xBCDB, 0xC1A2, 0x999C, 0xC1A3, 0x999D,\n\t0xC1A4, 0x999E, 0xC1A5, 0xBCDC, 0xC1A6, 0x999F, 0xC1A7, 0x99A0,\n\t0xC1A8, 0xBCDD, 0xC1A9, 0xBCDE, 0xC1AA, 0x99A1, 0xC1AB, 0x99A2,\n\t0xC1AC, 0xBCDF, 0xC1AD, 0x99A3, 0xC1AE, 0x99A4, 0xC1AF, 0x99A5,\n\t0xC1B0, 0xBCE0, 0xC1B1, 0x99A6, 0xC1B2, 0x99A7, 0xC1B3, 0x99A8,\n\t0xC1B4, 0x99A9, 0xC1B5, 0x99AA, 0xC1B6, 0x99AB, 0xC1B7, 0x99AC,\n\t0xC1B8, 0x99AD, 0xC1B9, 0x99AE, 0xC1BA, 0x99AF, 0xC1BB, 0x99B0,\n\t0xC1BC, 0x99B1, 0xC1BD, 0xBCE1, 0xC1BE, 0x99B2, 0xC1BF, 0x99B3,\n\t0xC1C0, 0x99B4, 0xC1C1, 0x99B5, 0xC1C2, 0x99B6, 0xC1C3, 0x99B7,\n\t0xC1C4, 0xBCE2, 0xC1C5, 0x99B8, 0xC1C6, 0x99B9, 0xC1C7, 0x99BA,\n\t0xC1C8, 0xBCE3, 0xC1C9, 0x99BB, 0xC1CA, 0x99BC, 0xC1CB, 0x99BD,\n\t0xC1CC, 0xBCE4, 0xC1CD, 0x99BE, 0xC1CE, 0x99BF, 0xC1CF, 0x99C0,\n\t0xC1D0, 0x99C1, 0xC1D1, 0x99C2, 0xC1D2, 0x99C3, 0xC1D3, 0x99C4,\n\t0xC1D4, 0xBCE5, 0xC1D5, 0x99C5, 0xC1D6, 0x99C6, 0xC1D7, 0xBCE6,\n\t0xC1D8, 0xBCE7, 0xC1D9, 0x99C7, 0xC1DA, 0x99C8, 0xC1DB, 0x99C9,\n\t0xC1DC, 0x99CA, 0xC1DD, 0x99CB, 0xC1DE, 0x99CC, 0xC1DF, 0x99CD,\n\t0xC1E0, 0xBCE8, 0xC1E1, 0x99CE, 0xC1E2, 0x99CF, 0xC1E3, 0x99D0,\n\t0xC1E4, 0xBCE9, 0xC1E5, 0x99D1, 0xC1E6, 0x99D2, 0xC1E7, 0x99D3,\n\t0xC1E8, 0xBCEA, 0xC1E9, 0x99D4, 0xC1EA, 0x99D5, 0xC1EB, 0x99D6,\n\t0xC1EC, 0x99D7, 0xC1ED, 0x99D8, 0xC1EE, 0x99D9, 0xC1EF, 0x99DA,\n\t0xC1F0, 0xBCEB, 0xC1F1, 0xBCEC, 0xC1F2, 0x99DB, 0xC1F3, 0xBCED,\n\t0xC1F4, 0x99DC, 0xC1F5, 0x99DD, 0xC1F6, 0x99DE, 0xC1F7, 0x99DF,\n\t0xC1F8, 0x99E0, 0xC1F9, 0x99E1, 0xC1FA, 0x99E2, 0xC1FB, 0x99E3,\n\t0xC1FC, 0xBCEE, 0xC1FD, 0xBCEF, 0xC1FE, 0x99E4, 0xC1FF, 0x99E5,\n\t0xC200, 0xBCF0, 0xC201, 0x99E6, 0xC202, 0x99E7, 0xC203, 0x99E8,\n\t0xC204, 0xBCF1, 0xC205, 0x99E9, 0xC206, 0x99EA, 0xC207, 0x99EB,\n\t0xC208, 0x99EC, 0xC209, 0x99ED, 0xC20A, 0x99EE, 0xC20B, 0x99EF,\n\t0xC20C, 0xBCF2, 0xC20D, 0xBCF3, 0xC20E, 0x99F0, 0xC20F, 0xBCF4,\n\t0xC210, 0x99F1, 0xC211, 0xBCF5, 0xC212, 0x99F2, 0xC213, 0x99F3,\n\t0xC214, 0x99F4, 0xC215, 0x99F5, 0xC216, 0x99F6, 0xC217, 0x99F7,\n\t0xC218, 0xBCF6, 0xC219, 0xBCF7, 0xC21A, 0x99F8, 0xC21B, 0x99F9,\n\t0xC21C, 0xBCF8, 0xC21D, 0x99FA, 0xC21E, 0x99FB, 0xC21F, 0xBCF9,\n\t0xC220, 0xBCFA, 0xC221, 0x99FC, 0xC222, 0x99FD, 0xC223, 0x99FE,\n\t0xC224, 0x9A41, 0xC225, 0x9A42, 0xC226, 0x9A43, 0xC227, 0x9A44,\n\t0xC228, 0xBCFB, 0xC229, 0xBCFC, 0xC22A, 0x9A45, 0xC22B, 0xBCFD,\n\t0xC22C, 0x9A46, 0xC22D, 0xBCFE, 0xC22E, 0x9A47, 0xC22F, 0xBDA1,\n\t0xC230, 0x9A48, 0xC231, 0xBDA2, 0xC232, 0xBDA3, 0xC233, 0x9A49,\n\t0xC234, 0xBDA4, 0xC235, 0x9A4A, 0xC236, 0x9A4B, 0xC237, 0x9A4C,\n\t0xC238, 0x9A4D, 0xC239, 0x9A4E, 0xC23A, 0x9A4F, 0xC23B, 0x9A50,\n\t0xC23C, 0x9A51, 0xC23D, 0x9A52, 0xC23E, 0x9A53, 0xC23F, 0x9A54,\n\t0xC240, 0x9A55, 0xC241, 0x9A56, 0xC242, 0x9A57, 0xC243, 0x9A58,\n\t0xC244, 0x9A59, 0xC245, 0x9A5A, 0xC246, 0x9A61, 0xC247, 0x9A62,\n\t0xC248, 0xBDA5, 0xC249, 0x9A63, 0xC24A, 0x9A64, 0xC24B, 0x9A65,\n\t0xC24C, 0x9A66, 0xC24D, 0x9A67, 0xC24E, 0x9A68, 0xC24F, 0x9A69,\n\t0xC250, 0xBDA6, 0xC251, 0xBDA7, 0xC252, 0x9A6A, 0xC253, 0x9A6B,\n\t0xC254, 0xBDA8, 0xC255, 0x9A6C, 0xC256, 0x9A6D, 0xC257, 0x9A6E,\n\t0xC258, 0xBDA9, 0xC259, 0x9A6F, 0xC25A, 0x9A70, 0xC25B, 0x9A71,\n\t0xC25C, 0x9A72, 0xC25D, 0x9A73, 0xC25E, 0x9A74, 0xC25F, 0x9A75,\n\t0xC260, 0xBDAA, 0xC261, 0x9A76, 0xC262, 0x9A77, 0xC263, 0x9A78,\n\t0xC264, 0x9A79, 0xC265, 0xBDAB, 0xC266, 0x9A7A, 0xC267, 0x9A81,\n\t0xC268, 0x9A82, 0xC269, 0x9A83, 0xC26A, 0x9A84, 0xC26B, 0x9A85,\n\t0xC26C, 0xBDAC, 0xC26D, 0xBDAD, 0xC26E, 0x9A86, 0xC26F, 0x9A87,\n\t0xC270, 0xBDAE, 0xC271, 0x9A88, 0xC272, 0x9A89, 0xC273, 0x9A8A,\n\t0xC274, 0xBDAF, 0xC275, 0x9A8B, 0xC276, 0x9A8C, 0xC277, 0x9A8D,\n\t0xC278, 0x9A8E, 0xC279, 0x9A8F, 0xC27A, 0x9A90, 0xC27B, 0x9A91,\n\t0xC27C, 0xBDB0, 0xC27D, 0xBDB1, 0xC27E, 0x9A92, 0xC27F, 0xBDB2,\n\t0xC280, 0x9A93, 0xC281, 0xBDB3, 0xC282, 0x9A94, 0xC283, 0x9A95,\n\t0xC284, 0x9A96, 0xC285, 0x9A97, 0xC286, 0x9A98, 0xC287, 0x9A99,\n\t0xC288, 0xBDB4, 0xC289, 0xBDB5, 0xC28A, 0x9A9A, 0xC28B, 0x9A9B,\n\t0xC28C, 0x9A9C, 0xC28D, 0x9A9D, 0xC28E, 0x9A9E, 0xC28F, 0x9A9F,\n\t0xC290, 0xBDB6, 0xC291, 0x9AA0, 0xC292, 0x9AA1, 0xC293, 0x9AA2,\n\t0xC294, 0x9AA3, 0xC295, 0x9AA4, 0xC296, 0x9AA5, 0xC297, 0x9AA6,\n\t0xC298, 0xBDB7, 0xC299, 0x9AA7, 0xC29A, 0x9AA8, 0xC29B, 0xBDB8,\n\t0xC29C, 0x9AA9, 0xC29D, 0xBDB9, 0xC29E, 0x9AAA, 0xC29F, 0x9AAB,\n\t0xC2A0, 0x9AAC, 0xC2A1, 0x9AAD, 0xC2A2, 0x9AAE, 0xC2A3, 0x9AAF,\n\t0xC2A4, 0xBDBA, 0xC2A5, 0xBDBB, 0xC2A6, 0x9AB0, 0xC2A7, 0x9AB1,\n\t0xC2A8, 0xBDBC, 0xC2A9, 0x9AB2, 0xC2AA, 0x9AB3, 0xC2AB, 0x9AB4,\n\t0xC2AC, 0xBDBD, 0xC2AD, 0xBDBE, 0xC2AE, 0x9AB5, 0xC2AF, 0x9AB6,\n\t0xC2B0, 0x9AB7, 0xC2B1, 0x9AB8, 0xC2B2, 0x9AB9, 0xC2B3, 0x9ABA,\n\t0xC2B4, 0xBDBF, 0xC2B5, 0xBDC0, 0xC2B6, 0x9ABB, 0xC2B7, 0xBDC1,\n\t0xC2B8, 0x9ABC, 0xC2B9, 0xBDC2, 0xC2BA, 0x9ABD, 0xC2BB, 0x9ABE,\n\t0xC2BC, 0x9ABF, 0xC2BD, 0x9AC0, 0xC2BE, 0x9AC1, 0xC2BF, 0x9AC2,\n\t0xC2C0, 0x9AC3, 0xC2C1, 0x9AC4, 0xC2C2, 0x9AC5, 0xC2C3, 0x9AC6,\n\t0xC2C4, 0x9AC7, 0xC2C5, 0x9AC8, 0xC2C6, 0x9AC9, 0xC2C7, 0x9ACA,\n\t0xC2C8, 0x9ACB, 0xC2C9, 0x9ACC, 0xC2CA, 0x9ACD, 0xC2CB, 0x9ACE,\n\t0xC2CC, 0x9ACF, 0xC2CD, 0x9AD0, 0xC2CE, 0x9AD1, 0xC2CF, 0x9AD2,\n\t0xC2D0, 0x9AD3, 0xC2D1, 0x9AD4, 0xC2D2, 0x9AD5, 0xC2D3, 0x9AD6,\n\t0xC2D4, 0x9AD7, 0xC2D5, 0x9AD8, 0xC2D6, 0x9AD9, 0xC2D7, 0x9ADA,\n\t0xC2D8, 0x9ADB, 0xC2D9, 0x9ADC, 0xC2DA, 0x9ADD, 0xC2DB, 0x9ADE,\n\t0xC2DC, 0xBDC3, 0xC2DD, 0xBDC4, 0xC2DE, 0x9ADF, 0xC2DF, 0x9AE0,\n\t0xC2E0, 0xBDC5, 0xC2E1, 0x9AE1, 0xC2E2, 0x9AE2, 0xC2E3, 0xBDC6,\n\t0xC2E4, 0xBDC7, 0xC2E5, 0x9AE3, 0xC2E6, 0x9AE4, 0xC2E7, 0x9AE5,\n\t0xC2E8, 0x9AE6, 0xC2E9, 0x9AE7, 0xC2EA, 0x9AE8, 0xC2EB, 0xBDC8,\n\t0xC2EC, 0xBDC9, 0xC2ED, 0xBDCA, 0xC2EE, 0x9AE9, 0xC2EF, 0xBDCB,\n\t0xC2F0, 0x9AEA, 0xC2F1, 0xBDCC, 0xC2F2, 0x9AEB, 0xC2F3, 0x9AEC,\n\t0xC2F4, 0x9AED, 0xC2F5, 0x9AEE, 0xC2F6, 0xBDCD, 0xC2F7, 0x9AEF,\n\t0xC2F8, 0xBDCE, 0xC2F9, 0xBDCF, 0xC2FA, 0x9AF0, 0xC2FB, 0xBDD0,\n\t0xC2FC, 0xBDD1, 0xC2FD, 0x9AF1, 0xC2FE, 0x9AF2, 0xC2FF, 0x9AF3,\n\t0xC300, 0xBDD2, 0xC301, 0x9AF4, 0xC302, 0x9AF5, 0xC303, 0x9AF6,\n\t0xC304, 0x9AF7, 0xC305, 0x9AF8, 0xC306, 0x9AF9, 0xC307, 0x9AFA,\n\t0xC308, 0xBDD3, 0xC309, 0xBDD4, 0xC30A, 0x9AFB, 0xC30B, 0x9AFC,\n\t0xC30C, 0xBDD5, 0xC30D, 0xBDD6, 0xC30E, 0x9AFD, 0xC30F, 0x9AFE,\n\t0xC310, 0x9B41, 0xC311, 0x9B42, 0xC312, 0x9B43, 0xC313, 0xBDD7,\n\t0xC314, 0xBDD8, 0xC315, 0xBDD9, 0xC316, 0x9B44, 0xC317, 0x9B45,\n\t0xC318, 0xBDDA, 0xC319, 0x9B46, 0xC31A, 0x9B47, 0xC31B, 0x9B48,\n\t0xC31C, 0xBDDB, 0xC31D, 0x9B49, 0xC31E, 0x9B4A, 0xC31F, 0x9B4B,\n\t0xC320, 0x9B4C, 0xC321, 0x9B4D, 0xC322, 0x9B4E, 0xC323, 0x9B4F,\n\t0xC324, 0xBDDC, 0xC325, 0xBDDD, 0xC326, 0x9B50, 0xC327, 0x9B51,\n\t0xC328, 0xBDDE, 0xC329, 0xBDDF, 0xC32A, 0x9B52, 0xC32B, 0x9B53,\n\t0xC32C, 0x9B54, 0xC32D, 0x9B55, 0xC32E, 0x9B56, 0xC32F, 0x9B57,\n\t0xC330, 0x9B58, 0xC331, 0x9B59, 0xC332, 0x9B5A, 0xC333, 0x9B61,\n\t0xC334, 0x9B62, 0xC335, 0x9B63, 0xC336, 0x9B64, 0xC337, 0x9B65,\n\t0xC338, 0x9B66, 0xC339, 0x9B67, 0xC33A, 0x9B68, 0xC33B, 0x9B69,\n\t0xC33C, 0x9B6A, 0xC33D, 0x9B6B, 0xC33E, 0x9B6C, 0xC33F, 0x9B6D,\n\t0xC340, 0x9B6E, 0xC341, 0x9B6F, 0xC342, 0x9B70, 0xC343, 0x9B71,\n\t0xC344, 0x9B72, 0xC345, 0xBDE0, 0xC346, 0x9B73, 0xC347, 0x9B74,\n\t0xC348, 0x9B75, 0xC349, 0x9B76, 0xC34A, 0x9B77, 0xC34B, 0x9B78,\n\t0xC34C, 0x9B79, 0xC34D, 0x9B7A, 0xC34E, 0x9B81, 0xC34F, 0x9B82,\n\t0xC350, 0x9B83, 0xC351, 0x9B84, 0xC352, 0x9B85, 0xC353, 0x9B86,\n\t0xC354, 0x9B87, 0xC355, 0x9B88, 0xC356, 0x9B89, 0xC357, 0x9B8A,\n\t0xC358, 0x9B8B, 0xC359, 0x9B8C, 0xC35A, 0x9B8D, 0xC35B, 0x9B8E,\n\t0xC35C, 0x9B8F, 0xC35D, 0x9B90, 0xC35E, 0x9B91, 0xC35F, 0x9B92,\n\t0xC360, 0x9B93, 0xC361, 0x9B94, 0xC362, 0x9B95, 0xC363, 0x9B96,\n\t0xC364, 0x9B97, 0xC365, 0x9B98, 0xC366, 0x9B99, 0xC367, 0x9B9A,\n\t0xC368, 0xBDE1, 0xC369, 0xBDE2, 0xC36A, 0x9B9B, 0xC36B, 0x9B9C,\n\t0xC36C, 0xBDE3, 0xC36D, 0x9B9D, 0xC36E, 0x9B9E, 0xC36F, 0x9B9F,\n\t0xC370, 0xBDE4, 0xC371, 0x9BA0, 0xC372, 0xBDE5, 0xC373, 0x9BA1,\n\t0xC374, 0x9BA2, 0xC375, 0x9BA3, 0xC376, 0x9BA4, 0xC377, 0x9BA5,\n\t0xC378, 0xBDE6, 0xC379, 0xBDE7, 0xC37A, 0x9BA6, 0xC37B, 0x9BA7,\n\t0xC37C, 0xBDE8, 0xC37D, 0xBDE9, 0xC37E, 0x9BA8, 0xC37F, 0x9BA9,\n\t0xC380, 0x9BAA, 0xC381, 0x9BAB, 0xC382, 0x9BAC, 0xC383, 0x9BAD,\n\t0xC384, 0xBDEA, 0xC385, 0x9BAE, 0xC386, 0x9BAF, 0xC387, 0x9BB0,\n\t0xC388, 0xBDEB, 0xC389, 0x9BB1, 0xC38A, 0x9BB2, 0xC38B, 0x9BB3,\n\t0xC38C, 0xBDEC, 0xC38D, 0x9BB4, 0xC38E, 0x9BB5, 0xC38F, 0x9BB6,\n\t0xC390, 0x9BB7, 0xC391, 0x9BB8, 0xC392, 0x9BB9, 0xC393, 0x9BBA,\n\t0xC394, 0x9BBB, 0xC395, 0x9BBC, 0xC396, 0x9BBD, 0xC397, 0x9BBE,\n\t0xC398, 0x9BBF, 0xC399, 0x9BC0, 0xC39A, 0x9BC1, 0xC39B, 0x9BC2,\n\t0xC39C, 0x9BC3, 0xC39D, 0x9BC4, 0xC39E, 0x9BC5, 0xC39F, 0x9BC6,\n\t0xC3A0, 0x9BC7, 0xC3A1, 0x9BC8, 0xC3A2, 0x9BC9, 0xC3A3, 0x9BCA,\n\t0xC3A4, 0x9BCB, 0xC3A5, 0x9BCC, 0xC3A6, 0x9BCD, 0xC3A7, 0x9BCE,\n\t0xC3A8, 0x9BCF, 0xC3A9, 0x9BD0, 0xC3AA, 0x9BD1, 0xC3AB, 0x9BD2,\n\t0xC3AC, 0x9BD3, 0xC3AD, 0x9BD4, 0xC3AE, 0x9BD5, 0xC3AF, 0x9BD6,\n\t0xC3B0, 0x9BD7, 0xC3B1, 0x9BD8, 0xC3B2, 0x9BD9, 0xC3B3, 0x9BDA,\n\t0xC3B4, 0x9BDB, 0xC3B5, 0x9BDC, 0xC3B6, 0x9BDD, 0xC3B7, 0x9BDE,\n\t0xC3B8, 0x9BDF, 0xC3B9, 0x9BE0, 0xC3BA, 0x9BE1, 0xC3BB, 0x9BE2,\n\t0xC3BC, 0x9BE3, 0xC3BD, 0x9BE4, 0xC3BE, 0x9BE5, 0xC3BF, 0x9BE6,\n\t0xC3C0, 0xBDED, 0xC3C1, 0x9BE7, 0xC3C2, 0x9BE8, 0xC3C3, 0x9BE9,\n\t0xC3C4, 0x9BEA, 0xC3C5, 0x9BEB, 0xC3C6, 0x9BEC, 0xC3C7, 0x9BED,\n\t0xC3C8, 0x9BEE, 0xC3C9, 0x9BEF, 0xC3CA, 0x9BF0, 0xC3CB, 0x9BF1,\n\t0xC3CC, 0x9BF2, 0xC3CD, 0x9BF3, 0xC3CE, 0x9BF4, 0xC3CF, 0x9BF5,\n\t0xC3D0, 0x9BF6, 0xC3D1, 0x9BF7, 0xC3D2, 0x9BF8, 0xC3D3, 0x9BF9,\n\t0xC3D4, 0x9BFA, 0xC3D5, 0x9BFB, 0xC3D6, 0x9BFC, 0xC3D7, 0x9BFD,\n\t0xC3D8, 0xBDEE, 0xC3D9, 0xBDEF, 0xC3DA, 0x9BFE, 0xC3DB, 0x9C41,\n\t0xC3DC, 0xBDF0, 0xC3DD, 0x9C42, 0xC3DE, 0x9C43, 0xC3DF, 0xBDF1,\n\t0xC3E0, 0xBDF2, 0xC3E1, 0x9C44, 0xC3E2, 0xBDF3, 0xC3E3, 0x9C45,\n\t0xC3E4, 0x9C46, 0xC3E5, 0x9C47, 0xC3E6, 0x9C48, 0xC3E7, 0x9C49,\n\t0xC3E8, 0xBDF4, 0xC3E9, 0xBDF5, 0xC3EA, 0x9C4A, 0xC3EB, 0x9C4B,\n\t0xC3EC, 0x9C4C, 0xC3ED, 0xBDF6, 0xC3EE, 0x9C4D, 0xC3EF, 0x9C4E,\n\t0xC3F0, 0x9C4F, 0xC3F1, 0x9C50, 0xC3F2, 0x9C51, 0xC3F3, 0x9C52,\n\t0xC3F4, 0xBDF7, 0xC3F5, 0xBDF8, 0xC3F6, 0x9C53, 0xC3F7, 0x9C54,\n\t0xC3F8, 0xBDF9, 0xC3F9, 0x9C55, 0xC3FA, 0x9C56, 0xC3FB, 0x9C57,\n\t0xC3FC, 0x9C58, 0xC3FD, 0x9C59, 0xC3FE, 0x9C5A, 0xC3FF, 0x9C61,\n\t0xC400, 0x9C62, 0xC401, 0x9C63, 0xC402, 0x9C64, 0xC403, 0x9C65,\n\t0xC404, 0x9C66, 0xC405, 0x9C67, 0xC406, 0x9C68, 0xC407, 0x9C69,\n\t0xC408, 0xBDFA, 0xC409, 0x9C6A, 0xC40A, 0x9C6B, 0xC40B, 0x9C6C,\n\t0xC40C, 0x9C6D, 0xC40D, 0x9C6E, 0xC40E, 0x9C6F, 0xC40F, 0x9C70,\n\t0xC410, 0xBDFB, 0xC411, 0x9C71, 0xC412, 0x9C72, 0xC413, 0x9C73,\n\t0xC414, 0x9C74, 0xC415, 0x9C75, 0xC416, 0x9C76, 0xC417, 0x9C77,\n\t0xC418, 0x9C78, 0xC419, 0x9C79, 0xC41A, 0x9C7A, 0xC41B, 0x9C81,\n\t0xC41C, 0x9C82, 0xC41D, 0x9C83, 0xC41E, 0x9C84, 0xC41F, 0x9C85,\n\t0xC420, 0x9C86, 0xC421, 0x9C87, 0xC422, 0x9C88, 0xC423, 0x9C89,\n\t0xC424, 0xBDFC, 0xC425, 0x9C8A, 0xC426, 0x9C8B, 0xC427, 0x9C8C,\n\t0xC428, 0x9C8D, 0xC429, 0x9C8E, 0xC42A, 0x9C8F, 0xC42B, 0x9C90,\n\t0xC42C, 0xBDFD, 0xC42D, 0x9C91, 0xC42E, 0x9C92, 0xC42F, 0x9C93,\n\t0xC430, 0xBDFE, 0xC431, 0x9C94, 0xC432, 0x9C95, 0xC433, 0x9C96,\n\t0xC434, 0xBEA1, 0xC435, 0x9C97, 0xC436, 0x9C98, 0xC437, 0x9C99,\n\t0xC438, 0x9C9A, 0xC439, 0x9C9B, 0xC43A, 0x9C9C, 0xC43B, 0x9C9D,\n\t0xC43C, 0xBEA2, 0xC43D, 0xBEA3, 0xC43E, 0x9C9E, 0xC43F, 0x9C9F,\n\t0xC440, 0x9CA0, 0xC441, 0x9CA1, 0xC442, 0x9CA2, 0xC443, 0x9CA3,\n\t0xC444, 0x9CA4, 0xC445, 0x9CA5, 0xC446, 0x9CA6, 0xC447, 0x9CA7,\n\t0xC448, 0xBEA4, 0xC449, 0x9CA8, 0xC44A, 0x9CA9, 0xC44B, 0x9CAA,\n\t0xC44C, 0x9CAB, 0xC44D, 0x9CAC, 0xC44E, 0x9CAD, 0xC44F, 0x9CAE,\n\t0xC450, 0x9CAF, 0xC451, 0x9CB0, 0xC452, 0x9CB1, 0xC453, 0x9CB2,\n\t0xC454, 0x9CB3, 0xC455, 0x9CB4, 0xC456, 0x9CB5, 0xC457, 0x9CB6,\n\t0xC458, 0x9CB7, 0xC459, 0x9CB8, 0xC45A, 0x9CB9, 0xC45B, 0x9CBA,\n\t0xC45C, 0x9CBB, 0xC45D, 0x9CBC, 0xC45E, 0x9CBD, 0xC45F, 0x9CBE,\n\t0xC460, 0x9CBF, 0xC461, 0x9CC0, 0xC462, 0x9CC1, 0xC463, 0x9CC2,\n\t0xC464, 0xBEA5, 0xC465, 0xBEA6, 0xC466, 0x9CC3, 0xC467, 0x9CC4,\n\t0xC468, 0xBEA7, 0xC469, 0x9CC5, 0xC46A, 0x9CC6, 0xC46B, 0x9CC7,\n\t0xC46C, 0xBEA8, 0xC46D, 0x9CC8, 0xC46E, 0x9CC9, 0xC46F, 0x9CCA,\n\t0xC470, 0x9CCB, 0xC471, 0x9CCC, 0xC472, 0x9CCD, 0xC473, 0x9CCE,\n\t0xC474, 0xBEA9, 0xC475, 0xBEAA, 0xC476, 0x9CCF, 0xC477, 0x9CD0,\n\t0xC478, 0x9CD1, 0xC479, 0xBEAB, 0xC47A, 0x9CD2, 0xC47B, 0x9CD3,\n\t0xC47C, 0x9CD4, 0xC47D, 0x9CD5, 0xC47E, 0x9CD6, 0xC47F, 0x9CD7,\n\t0xC480, 0xBEAC, 0xC481, 0x9CD8, 0xC482, 0x9CD9, 0xC483, 0x9CDA,\n\t0xC484, 0x9CDB, 0xC485, 0x9CDC, 0xC486, 0x9CDD, 0xC487, 0x9CDE,\n\t0xC488, 0x9CDF, 0xC489, 0x9CE0, 0xC48A, 0x9CE1, 0xC48B, 0x9CE2,\n\t0xC48C, 0x9CE3, 0xC48D, 0x9CE4, 0xC48E, 0x9CE5, 0xC48F, 0x9CE6,\n\t0xC490, 0x9CE7, 0xC491, 0x9CE8, 0xC492, 0x9CE9, 0xC493, 0x9CEA,\n\t0xC494, 0xBEAD, 0xC495, 0x9CEB, 0xC496, 0x9CEC, 0xC497, 0x9CED,\n\t0xC498, 0x9CEE, 0xC499, 0x9CEF, 0xC49A, 0x9CF0, 0xC49B, 0x9CF1,\n\t0xC49C, 0xBEAE, 0xC49D, 0x9CF2, 0xC49E, 0x9CF3, 0xC49F, 0x9CF4,\n\t0xC4A0, 0x9CF5, 0xC4A1, 0x9CF6, 0xC4A2, 0x9CF7, 0xC4A3, 0x9CF8,\n\t0xC4A4, 0x9CF9, 0xC4A5, 0x9CFA, 0xC4A6, 0x9CFB, 0xC4A7, 0x9CFC,\n\t0xC4A8, 0x9CFD, 0xC4A9, 0x9CFE, 0xC4AA, 0x9D41, 0xC4AB, 0x9D42,\n\t0xC4AC, 0x9D43, 0xC4AD, 0x9D44, 0xC4AE, 0x9D45, 0xC4AF, 0x9D46,\n\t0xC4B0, 0x9D47, 0xC4B1, 0x9D48, 0xC4B2, 0x9D49, 0xC4B3, 0x9D4A,\n\t0xC4B4, 0x9D4B, 0xC4B5, 0x9D4C, 0xC4B6, 0x9D4D, 0xC4B7, 0x9D4E,\n\t0xC4B8, 0xBEAF, 0xC4B9, 0x9D4F, 0xC4BA, 0x9D50, 0xC4BB, 0x9D51,\n\t0xC4BC, 0xBEB0, 0xC4BD, 0x9D52, 0xC4BE, 0x9D53, 0xC4BF, 0x9D54,\n\t0xC4C0, 0x9D55, 0xC4C1, 0x9D56, 0xC4C2, 0x9D57, 0xC4C3, 0x9D58,\n\t0xC4C4, 0x9D59, 0xC4C5, 0x9D5A, 0xC4C6, 0x9D61, 0xC4C7, 0x9D62,\n\t0xC4C8, 0x9D63, 0xC4C9, 0x9D64, 0xC4CA, 0x9D65, 0xC4CB, 0x9D66,\n\t0xC4CC, 0x9D67, 0xC4CD, 0x9D68, 0xC4CE, 0x9D69, 0xC4CF, 0x9D6A,\n\t0xC4D0, 0x9D6B, 0xC4D1, 0x9D6C, 0xC4D2, 0x9D6D, 0xC4D3, 0x9D6E,\n\t0xC4D4, 0x9D6F, 0xC4D5, 0x9D70, 0xC4D6, 0x9D71, 0xC4D7, 0x9D72,\n\t0xC4D8, 0x9D73, 0xC4D9, 0x9D74, 0xC4DA, 0x9D75, 0xC4DB, 0x9D76,\n\t0xC4DC, 0x9D77, 0xC4DD, 0x9D78, 0xC4DE, 0x9D79, 0xC4DF, 0x9D7A,\n\t0xC4E0, 0x9D81, 0xC4E1, 0x9D82, 0xC4E2, 0x9D83, 0xC4E3, 0x9D84,\n\t0xC4E4, 0x9D85, 0xC4E5, 0x9D86, 0xC4E6, 0x9D87, 0xC4E7, 0x9D88,\n\t0xC4E8, 0x9D89, 0xC4E9, 0xBEB1, 0xC4EA, 0x9D8A, 0xC4EB, 0x9D8B,\n\t0xC4EC, 0x9D8C, 0xC4ED, 0x9D8D, 0xC4EE, 0x9D8E, 0xC4EF, 0x9D8F,\n\t0xC4F0, 0xBEB2, 0xC4F1, 0xBEB3, 0xC4F2, 0x9D90, 0xC4F3, 0x9D91,\n\t0xC4F4, 0xBEB4, 0xC4F5, 0x9D92, 0xC4F6, 0x9D93, 0xC4F7, 0x9D94,\n\t0xC4F8, 0xBEB5, 0xC4F9, 0x9D95, 0xC4FA, 0xBEB6, 0xC4FB, 0x9D96,\n\t0xC4FC, 0x9D97, 0xC4FD, 0x9D98, 0xC4FE, 0x9D99, 0xC4FF, 0xBEB7,\n\t0xC500, 0xBEB8, 0xC501, 0xBEB9, 0xC502, 0x9D9A, 0xC503, 0x9D9B,\n\t0xC504, 0x9D9C, 0xC505, 0x9D9D, 0xC506, 0x9D9E, 0xC507, 0x9D9F,\n\t0xC508, 0x9DA0, 0xC509, 0x9DA1, 0xC50A, 0x9DA2, 0xC50B, 0x9DA3,\n\t0xC50C, 0xBEBA, 0xC50D, 0x9DA4, 0xC50E, 0x9DA5, 0xC50F, 0x9DA6,\n\t0xC510, 0xBEBB, 0xC511, 0x9DA7, 0xC512, 0x9DA8, 0xC513, 0x9DA9,\n\t0xC514, 0xBEBC, 0xC515, 0x9DAA, 0xC516, 0x9DAB, 0xC517, 0x9DAC,\n\t0xC518, 0x9DAD, 0xC519, 0x9DAE, 0xC51A, 0x9DAF, 0xC51B, 0x9DB0,\n\t0xC51C, 0xBEBD, 0xC51D, 0x9DB1, 0xC51E, 0x9DB2, 0xC51F, 0x9DB3,\n\t0xC520, 0x9DB4, 0xC521, 0x9DB5, 0xC522, 0x9DB6, 0xC523, 0x9DB7,\n\t0xC524, 0x9DB8, 0xC525, 0x9DB9, 0xC526, 0x9DBA, 0xC527, 0x9DBB,\n\t0xC528, 0xBEBE, 0xC529, 0xBEBF, 0xC52A, 0x9DBC, 0xC52B, 0x9DBD,\n\t0xC52C, 0xBEC0, 0xC52D, 0x9DBE, 0xC52E, 0x9DBF, 0xC52F, 0x9DC0,\n\t0xC530, 0xBEC1, 0xC531, 0x9DC1, 0xC532, 0x9DC2, 0xC533, 0x9DC3,\n\t0xC534, 0x9DC4, 0xC535, 0x9DC5, 0xC536, 0x9DC6, 0xC537, 0x9DC7,\n\t0xC538, 0xBEC2, 0xC539, 0xBEC3, 0xC53A, 0x9DC8, 0xC53B, 0xBEC4,\n\t0xC53C, 0x9DC9, 0xC53D, 0xBEC5, 0xC53E, 0x9DCA, 0xC53F, 0x9DCB,\n\t0xC540, 0x9DCC, 0xC541, 0x9DCD, 0xC542, 0x9DCE, 0xC543, 0x9DCF,\n\t0xC544, 0xBEC6, 0xC545, 0xBEC7, 0xC546, 0x9DD0, 0xC547, 0x9DD1,\n\t0xC548, 0xBEC8, 0xC549, 0xBEC9, 0xC54A, 0xBECA, 0xC54B, 0x9DD2,\n\t0xC54C, 0xBECB, 0xC54D, 0xBECC, 0xC54E, 0xBECD, 0xC54F, 0x9DD3,\n\t0xC550, 0x9DD4, 0xC551, 0x9DD5, 0xC552, 0x9DD6, 0xC553, 0xBECE,\n\t0xC554, 0xBECF, 0xC555, 0xBED0, 0xC556, 0x9DD7, 0xC557, 0xBED1,\n\t0xC558, 0xBED2, 0xC559, 0xBED3, 0xC55A, 0x9DD8, 0xC55B, 0x9DD9,\n\t0xC55C, 0x9DDA, 0xC55D, 0xBED4, 0xC55E, 0xBED5, 0xC55F, 0x9DDB,\n\t0xC560, 0xBED6, 0xC561, 0xBED7, 0xC562, 0x9DDC, 0xC563, 0x9DDD,\n\t0xC564, 0xBED8, 0xC565, 0x9DDE, 0xC566, 0x9DDF, 0xC567, 0x9DE0,\n\t0xC568, 0xBED9, 0xC569, 0x9DE1, 0xC56A, 0x9DE2, 0xC56B, 0x9DE3,\n\t0xC56C, 0x9DE4, 0xC56D, 0x9DE5, 0xC56E, 0x9DE6, 0xC56F, 0x9DE7,\n\t0xC570, 0xBEDA, 0xC571, 0xBEDB, 0xC572, 0x9DE8, 0xC573, 0xBEDC,\n\t0xC574, 0xBEDD, 0xC575, 0xBEDE, 0xC576, 0x9DE9, 0xC577, 0x9DEA,\n\t0xC578, 0x9DEB, 0xC579, 0x9DEC, 0xC57A, 0x9DED, 0xC57B, 0x9DEE,\n\t0xC57C, 0xBEDF, 0xC57D, 0xBEE0, 0xC57E, 0x9DEF, 0xC57F, 0x9DF0,\n\t0xC580, 0xBEE1, 0xC581, 0x9DF1, 0xC582, 0x9DF2, 0xC583, 0x9DF3,\n\t0xC584, 0xBEE2, 0xC585, 0x9DF4, 0xC586, 0x9DF5, 0xC587, 0xBEE3,\n\t0xC588, 0x9DF6, 0xC589, 0x9DF7, 0xC58A, 0x9DF8, 0xC58B, 0x9DF9,\n\t0xC58C, 0xBEE4, 0xC58D, 0xBEE5, 0xC58E, 0x9DFA, 0xC58F, 0xBEE6,\n\t0xC590, 0x9DFB, 0xC591, 0xBEE7, 0xC592, 0x9DFC, 0xC593, 0x9DFD,\n\t0xC594, 0x9DFE, 0xC595, 0xBEE8, 0xC596, 0x9E41, 0xC597, 0xBEE9,\n\t0xC598, 0xBEEA, 0xC599, 0x9E42, 0xC59A, 0x9E43, 0xC59B, 0x9E44,\n\t0xC59C, 0xBEEB, 0xC59D, 0x9E45, 0xC59E, 0x9E46, 0xC59F, 0x9E47,\n\t0xC5A0, 0xBEEC, 0xC5A1, 0x9E48, 0xC5A2, 0x9E49, 0xC5A3, 0x9E4A,\n\t0xC5A4, 0x9E4B, 0xC5A5, 0x9E4C, 0xC5A6, 0x9E4D, 0xC5A7, 0x9E4E,\n\t0xC5A8, 0x9E4F, 0xC5A9, 0xBEED, 0xC5AA, 0x9E50, 0xC5AB, 0x9E51,\n\t0xC5AC, 0x9E52, 0xC5AD, 0x9E53, 0xC5AE, 0x9E54, 0xC5AF, 0x9E55,\n\t0xC5B0, 0x9E56, 0xC5B1, 0x9E57, 0xC5B2, 0x9E58, 0xC5B3, 0x9E59,\n\t0xC5B4, 0xBEEE, 0xC5B5, 0xBEEF, 0xC5B6, 0x9E5A, 0xC5B7, 0x9E61,\n\t0xC5B8, 0xBEF0, 0xC5B9, 0xBEF1, 0xC5BA, 0x9E62, 0xC5BB, 0xBEF2,\n\t0xC5BC, 0xBEF3, 0xC5BD, 0xBEF4, 0xC5BE, 0xBEF5, 0xC5BF, 0x9E63,\n\t0xC5C0, 0x9E64, 0xC5C1, 0x9E65, 0xC5C2, 0x9E66, 0xC5C3, 0x9E67,\n\t0xC5C4, 0xBEF6, 0xC5C5, 0xBEF7, 0xC5C6, 0xBEF8, 0xC5C7, 0xBEF9,\n\t0xC5C8, 0xBEFA, 0xC5C9, 0xBEFB, 0xC5CA, 0xBEFC, 0xC5CB, 0x9E68,\n\t0xC5CC, 0xBEFD, 0xC5CD, 0x9E69, 0xC5CE, 0xBEFE, 0xC5CF, 0x9E6A,\n\t0xC5D0, 0xBFA1, 0xC5D1, 0xBFA2, 0xC5D2, 0x9E6B, 0xC5D3, 0x9E6C,\n\t0xC5D4, 0xBFA3, 0xC5D5, 0x9E6D, 0xC5D6, 0x9E6E, 0xC5D7, 0x9E6F,\n\t0xC5D8, 0xBFA4, 0xC5D9, 0x9E70, 0xC5DA, 0x9E71, 0xC5DB, 0x9E72,\n\t0xC5DC, 0x9E73, 0xC5DD, 0x9E74, 0xC5DE, 0x9E75, 0xC5DF, 0x9E76,\n\t0xC5E0, 0xBFA5, 0xC5E1, 0xBFA6, 0xC5E2, 0x9E77, 0xC5E3, 0xBFA7,\n\t0xC5E4, 0x9E78, 0xC5E5, 0xBFA8, 0xC5E6, 0x9E79, 0xC5E7, 0x9E7A,\n\t0xC5E8, 0x9E81, 0xC5E9, 0x9E82, 0xC5EA, 0x9E83, 0xC5EB, 0x9E84,\n\t0xC5EC, 0xBFA9, 0xC5ED, 0xBFAA, 0xC5EE, 0xBFAB, 0xC5EF, 0x9E85,\n\t0xC5F0, 0xBFAC, 0xC5F1, 0x9E86, 0xC5F2, 0x9E87, 0xC5F3, 0x9E88,\n\t0xC5F4, 0xBFAD, 0xC5F5, 0x9E89, 0xC5F6, 0xBFAE, 0xC5F7, 0xBFAF,\n\t0xC5F8, 0x9E8A, 0xC5F9, 0x9E8B, 0xC5FA, 0x9E8C, 0xC5FB, 0x9E8D,\n\t0xC5FC, 0xBFB0, 0xC5FD, 0xBFB1, 0xC5FE, 0xBFB2, 0xC5FF, 0xBFB3,\n\t0xC600, 0xBFB4, 0xC601, 0xBFB5, 0xC602, 0x9E8E, 0xC603, 0x9E8F,\n\t0xC604, 0x9E90, 0xC605, 0xBFB6, 0xC606, 0xBFB7, 0xC607, 0xBFB8,\n\t0xC608, 0xBFB9, 0xC609, 0x9E91, 0xC60A, 0x9E92, 0xC60B, 0x9E93,\n\t0xC60C, 0xBFBA, 0xC60D, 0x9E94, 0xC60E, 0x9E95, 0xC60F, 0x9E96,\n\t0xC610, 0xBFBB, 0xC611, 0x9E97, 0xC612, 0x9E98, 0xC613, 0x9E99,\n\t0xC614, 0x9E9A, 0xC615, 0x9E9B, 0xC616, 0x9E9C, 0xC617, 0x9E9D,\n\t0xC618, 0xBFBC, 0xC619, 0xBFBD, 0xC61A, 0x9E9E, 0xC61B, 0xBFBE,\n\t0xC61C, 0xBFBF, 0xC61D, 0x9E9F, 0xC61E, 0x9EA0, 0xC61F, 0x9EA1,\n\t0xC620, 0x9EA2, 0xC621, 0x9EA3, 0xC622, 0x9EA4, 0xC623, 0x9EA5,\n\t0xC624, 0xBFC0, 0xC625, 0xBFC1, 0xC626, 0x9EA6, 0xC627, 0x9EA7,\n\t0xC628, 0xBFC2, 0xC629, 0x9EA8, 0xC62A, 0x9EA9, 0xC62B, 0x9EAA,\n\t0xC62C, 0xBFC3, 0xC62D, 0xBFC4, 0xC62E, 0xBFC5, 0xC62F, 0x9EAB,\n\t0xC630, 0xBFC6, 0xC631, 0x9EAC, 0xC632, 0x9EAD, 0xC633, 0xBFC7,\n\t0xC634, 0xBFC8, 0xC635, 0xBFC9, 0xC636, 0x9EAE, 0xC637, 0xBFCA,\n\t0xC638, 0x9EAF, 0xC639, 0xBFCB, 0xC63A, 0x9EB0, 0xC63B, 0xBFCC,\n\t0xC63C, 0x9EB1, 0xC63D, 0x9EB2, 0xC63E, 0x9EB3, 0xC63F, 0x9EB4,\n\t0xC640, 0xBFCD, 0xC641, 0xBFCE, 0xC642, 0x9EB5, 0xC643, 0x9EB6,\n\t0xC644, 0xBFCF, 0xC645, 0x9EB7, 0xC646, 0x9EB8, 0xC647, 0x9EB9,\n\t0xC648, 0xBFD0, 0xC649, 0x9EBA, 0xC64A, 0x9EBB, 0xC64B, 0x9EBC,\n\t0xC64C, 0x9EBD, 0xC64D, 0x9EBE, 0xC64E, 0x9EBF, 0xC64F, 0x9EC0,\n\t0xC650, 0xBFD1, 0xC651, 0xBFD2, 0xC652, 0x9EC1, 0xC653, 0xBFD3,\n\t0xC654, 0xBFD4, 0xC655, 0xBFD5, 0xC656, 0x9EC2, 0xC657, 0x9EC3,\n\t0xC658, 0x9EC4, 0xC659, 0x9EC5, 0xC65A, 0x9EC6, 0xC65B, 0x9EC7,\n\t0xC65C, 0xBFD6, 0xC65D, 0xBFD7, 0xC65E, 0x9EC8, 0xC65F, 0x9EC9,\n\t0xC660, 0xBFD8, 0xC661, 0x9ECA, 0xC662, 0x9ECB, 0xC663, 0x9ECC,\n\t0xC664, 0x9ECD, 0xC665, 0x9ECE, 0xC666, 0x9ECF, 0xC667, 0x9ED0,\n\t0xC668, 0x9ED1, 0xC669, 0x9ED2, 0xC66A, 0x9ED3, 0xC66B, 0x9ED4,\n\t0xC66C, 0xBFD9, 0xC66D, 0x9ED5, 0xC66E, 0x9ED6, 0xC66F, 0xBFDA,\n\t0xC670, 0x9ED7, 0xC671, 0xBFDB, 0xC672, 0x9ED8, 0xC673, 0x9ED9,\n\t0xC674, 0x9EDA, 0xC675, 0x9EDB, 0xC676, 0x9EDC, 0xC677, 0x9EDD,\n\t0xC678, 0xBFDC, 0xC679, 0xBFDD, 0xC67A, 0x9EDE, 0xC67B, 0x9EDF,\n\t0xC67C, 0xBFDE, 0xC67D, 0x9EE0, 0xC67E, 0x9EE1, 0xC67F, 0x9EE2,\n\t0xC680, 0xBFDF, 0xC681, 0x9EE3, 0xC682, 0x9EE4, 0xC683, 0x9EE5,\n\t0xC684, 0x9EE6, 0xC685, 0x9EE7, 0xC686, 0x9EE8, 0xC687, 0x9EE9,\n\t0xC688, 0xBFE0, 0xC689, 0xBFE1, 0xC68A, 0x9EEA, 0xC68B, 0xBFE2,\n\t0xC68C, 0x9EEB, 0xC68D, 0xBFE3, 0xC68E, 0x9EEC, 0xC68F, 0x9EED,\n\t0xC690, 0x9EEE, 0xC691, 0x9EEF, 0xC692, 0x9EF0, 0xC693, 0x9EF1,\n\t0xC694, 0xBFE4, 0xC695, 0xBFE5, 0xC696, 0x9EF2, 0xC697, 0x9EF3,\n\t0xC698, 0xBFE6, 0xC699, 0x9EF4, 0xC69A, 0x9EF5, 0xC69B, 0x9EF6,\n\t0xC69C, 0xBFE7, 0xC69D, 0x9EF7, 0xC69E, 0x9EF8, 0xC69F, 0x9EF9,\n\t0xC6A0, 0x9EFA, 0xC6A1, 0x9EFB, 0xC6A2, 0x9EFC, 0xC6A3, 0x9EFD,\n\t0xC6A4, 0xBFE8, 0xC6A5, 0xBFE9, 0xC6A6, 0x9EFE, 0xC6A7, 0xBFEA,\n\t0xC6A8, 0x9F41, 0xC6A9, 0xBFEB, 0xC6AA, 0x9F42, 0xC6AB, 0x9F43,\n\t0xC6AC, 0x9F44, 0xC6AD, 0x9F45, 0xC6AE, 0x9F46, 0xC6AF, 0x9F47,\n\t0xC6B0, 0xBFEC, 0xC6B1, 0xBFED, 0xC6B2, 0x9F48, 0xC6B3, 0x9F49,\n\t0xC6B4, 0xBFEE, 0xC6B5, 0x9F4A, 0xC6B6, 0x9F4B, 0xC6B7, 0x9F4C,\n\t0xC6B8, 0xBFEF, 0xC6B9, 0xBFF0, 0xC6BA, 0xBFF1, 0xC6BB, 0x9F4D,\n\t0xC6BC, 0x9F4E, 0xC6BD, 0x9F4F, 0xC6BE, 0x9F50, 0xC6BF, 0x9F51,\n\t0xC6C0, 0xBFF2, 0xC6C1, 0xBFF3, 0xC6C2, 0x9F52, 0xC6C3, 0xBFF4,\n\t0xC6C4, 0x9F53, 0xC6C5, 0xBFF5, 0xC6C6, 0x9F54, 0xC6C7, 0x9F55,\n\t0xC6C8, 0x9F56, 0xC6C9, 0x9F57, 0xC6CA, 0x9F58, 0xC6CB, 0x9F59,\n\t0xC6CC, 0xBFF6, 0xC6CD, 0xBFF7, 0xC6CE, 0x9F5A, 0xC6CF, 0x9F61,\n\t0xC6D0, 0xBFF8, 0xC6D1, 0x9F62, 0xC6D2, 0x9F63, 0xC6D3, 0x9F64,\n\t0xC6D4, 0xBFF9, 0xC6D5, 0x9F65, 0xC6D6, 0x9F66, 0xC6D7, 0x9F67,\n\t0xC6D8, 0x9F68, 0xC6D9, 0x9F69, 0xC6DA, 0x9F6A, 0xC6DB, 0x9F6B,\n\t0xC6DC, 0xBFFA, 0xC6DD, 0xBFFB, 0xC6DE, 0x9F6C, 0xC6DF, 0x9F6D,\n\t0xC6E0, 0xBFFC, 0xC6E1, 0xBFFD, 0xC6E2, 0x9F6E, 0xC6E3, 0x9F6F,\n\t0xC6E4, 0x9F70, 0xC6E5, 0x9F71, 0xC6E6, 0x9F72, 0xC6E7, 0x9F73,\n\t0xC6E8, 0xBFFE, 0xC6E9, 0xC0A1, 0xC6EA, 0x9F74, 0xC6EB, 0x9F75,\n\t0xC6EC, 0xC0A2, 0xC6ED, 0x9F76, 0xC6EE, 0x9F77, 0xC6EF, 0x9F78,\n\t0xC6F0, 0xC0A3, 0xC6F1, 0x9F79, 0xC6F2, 0x9F7A, 0xC6F3, 0x9F81,\n\t0xC6F4, 0x9F82, 0xC6F5, 0x9F83, 0xC6F6, 0x9F84, 0xC6F7, 0x9F85,\n\t0xC6F8, 0xC0A4, 0xC6F9, 0xC0A5, 0xC6FA, 0x9F86, 0xC6FB, 0x9F87,\n\t0xC6FC, 0x9F88, 0xC6FD, 0xC0A6, 0xC6FE, 0x9F89, 0xC6FF, 0x9F8A,\n\t0xC700, 0x9F8B, 0xC701, 0x9F8C, 0xC702, 0x9F8D, 0xC703, 0x9F8E,\n\t0xC704, 0xC0A7, 0xC705, 0xC0A8, 0xC706, 0x9F8F, 0xC707, 0x9F90,\n\t0xC708, 0xC0A9, 0xC709, 0x9F91, 0xC70A, 0x9F92, 0xC70B, 0x9F93,\n\t0xC70C, 0xC0AA, 0xC70D, 0x9F94, 0xC70E, 0x9F95, 0xC70F, 0x9F96,\n\t0xC710, 0x9F97, 0xC711, 0x9F98, 0xC712, 0x9F99, 0xC713, 0x9F9A,\n\t0xC714, 0xC0AB, 0xC715, 0xC0AC, 0xC716, 0x9F9B, 0xC717, 0xC0AD,\n\t0xC718, 0x9F9C, 0xC719, 0xC0AE, 0xC71A, 0x9F9D, 0xC71B, 0x9F9E,\n\t0xC71C, 0x9F9F, 0xC71D, 0x9FA0, 0xC71E, 0x9FA1, 0xC71F, 0x9FA2,\n\t0xC720, 0xC0AF, 0xC721, 0xC0B0, 0xC722, 0x9FA3, 0xC723, 0x9FA4,\n\t0xC724, 0xC0B1, 0xC725, 0x9FA5, 0xC726, 0x9FA6, 0xC727, 0x9FA7,\n\t0xC728, 0xC0B2, 0xC729, 0x9FA8, 0xC72A, 0x9FA9, 0xC72B, 0x9FAA,\n\t0xC72C, 0x9FAB, 0xC72D, 0x9FAC, 0xC72E, 0x9FAD, 0xC72F, 0x9FAE,\n\t0xC730, 0xC0B3, 0xC731, 0xC0B4, 0xC732, 0x9FAF, 0xC733, 0xC0B5,\n\t0xC734, 0x9FB0, 0xC735, 0xC0B6, 0xC736, 0x9FB1, 0xC737, 0xC0B7,\n\t0xC738, 0x9FB2, 0xC739, 0x9FB3, 0xC73A, 0x9FB4, 0xC73B, 0x9FB5,\n\t0xC73C, 0xC0B8, 0xC73D, 0xC0B9, 0xC73E, 0x9FB6, 0xC73F, 0x9FB7,\n\t0xC740, 0xC0BA, 0xC741, 0x9FB8, 0xC742, 0x9FB9, 0xC743, 0x9FBA,\n\t0xC744, 0xC0BB, 0xC745, 0x9FBB, 0xC746, 0x9FBC, 0xC747, 0x9FBD,\n\t0xC748, 0x9FBE, 0xC749, 0x9FBF, 0xC74A, 0xC0BC, 0xC74B, 0x9FC0,\n\t0xC74C, 0xC0BD, 0xC74D, 0xC0BE, 0xC74E, 0x9FC1, 0xC74F, 0xC0BF,\n\t0xC750, 0x9FC2, 0xC751, 0xC0C0, 0xC752, 0xC0C1, 0xC753, 0xC0C2,\n\t0xC754, 0xC0C3, 0xC755, 0xC0C4, 0xC756, 0xC0C5, 0xC757, 0xC0C6,\n\t0xC758, 0xC0C7, 0xC759, 0x9FC3, 0xC75A, 0x9FC4, 0xC75B, 0x9FC5,\n\t0xC75C, 0xC0C8, 0xC75D, 0x9FC6, 0xC75E, 0x9FC7, 0xC75F, 0x9FC8,\n\t0xC760, 0xC0C9, 0xC761, 0x9FC9, 0xC762, 0x9FCA, 0xC763, 0x9FCB,\n\t0xC764, 0x9FCC, 0xC765, 0x9FCD, 0xC766, 0x9FCE, 0xC767, 0x9FCF,\n\t0xC768, 0xC0CA, 0xC769, 0x9FD0, 0xC76A, 0x9FD1, 0xC76B, 0xC0CB,\n\t0xC76C, 0x9FD2, 0xC76D, 0x9FD3, 0xC76E, 0x9FD4, 0xC76F, 0x9FD5,\n\t0xC770, 0x9FD6, 0xC771, 0x9FD7, 0xC772, 0x9FD8, 0xC773, 0x9FD9,\n\t0xC774, 0xC0CC, 0xC775, 0xC0CD, 0xC776, 0x9FDA, 0xC777, 0x9FDB,\n\t0xC778, 0xC0CE, 0xC779, 0x9FDC, 0xC77A, 0x9FDD, 0xC77B, 0x9FDE,\n\t0xC77C, 0xC0CF, 0xC77D, 0xC0D0, 0xC77E, 0xC0D1, 0xC77F, 0x9FDF,\n\t0xC780, 0x9FE0, 0xC781, 0x9FE1, 0xC782, 0x9FE2, 0xC783, 0xC0D2,\n\t0xC784, 0xC0D3, 0xC785, 0xC0D4, 0xC786, 0x9FE3, 0xC787, 0xC0D5,\n\t0xC788, 0xC0D6, 0xC789, 0xC0D7, 0xC78A, 0xC0D8, 0xC78B, 0x9FE4,\n\t0xC78C, 0x9FE5, 0xC78D, 0x9FE6, 0xC78E, 0xC0D9, 0xC78F, 0x9FE7,\n\t0xC790, 0xC0DA, 0xC791, 0xC0DB, 0xC792, 0x9FE8, 0xC793, 0x9FE9,\n\t0xC794, 0xC0DC, 0xC795, 0x9FEA, 0xC796, 0xC0DD, 0xC797, 0xC0DE,\n\t0xC798, 0xC0DF, 0xC799, 0x9FEB, 0xC79A, 0xC0E0, 0xC79B, 0x9FEC,\n\t0xC79C, 0x9FED, 0xC79D, 0x9FEE, 0xC79E, 0x9FEF, 0xC79F, 0x9FF0,\n\t0xC7A0, 0xC0E1, 0xC7A1, 0xC0E2, 0xC7A2, 0x9FF1, 0xC7A3, 0xC0E3,\n\t0xC7A4, 0xC0E4, 0xC7A5, 0xC0E5, 0xC7A6, 0xC0E6, 0xC7A7, 0x9FF2,\n\t0xC7A8, 0x9FF3, 0xC7A9, 0x9FF4, 0xC7AA, 0x9FF5, 0xC7AB, 0x9FF6,\n\t0xC7AC, 0xC0E7, 0xC7AD, 0xC0E8, 0xC7AE, 0x9FF7, 0xC7AF, 0x9FF8,\n\t0xC7B0, 0xC0E9, 0xC7B1, 0x9FF9, 0xC7B2, 0x9FFA, 0xC7B3, 0x9FFB,\n\t0xC7B4, 0xC0EA, 0xC7B5, 0x9FFC, 0xC7B6, 0x9FFD, 0xC7B7, 0x9FFE,\n\t0xC7B8, 0xA041, 0xC7B9, 0xA042, 0xC7BA, 0xA043, 0xC7BB, 0xA044,\n\t0xC7BC, 0xC0EB, 0xC7BD, 0xC0EC, 0xC7BE, 0xA045, 0xC7BF, 0xC0ED,\n\t0xC7C0, 0xC0EE, 0xC7C1, 0xC0EF, 0xC7C2, 0xA046, 0xC7C3, 0xA047,\n\t0xC7C4, 0xA048, 0xC7C5, 0xA049, 0xC7C6, 0xA04A, 0xC7C7, 0xA04B,\n\t0xC7C8, 0xC0F0, 0xC7C9, 0xC0F1, 0xC7CA, 0xA04C, 0xC7CB, 0xA04D,\n\t0xC7CC, 0xC0F2, 0xC7CD, 0xA04E, 0xC7CE, 0xC0F3, 0xC7CF, 0xA04F,\n\t0xC7D0, 0xC0F4, 0xC7D1, 0xA050, 0xC7D2, 0xA051, 0xC7D3, 0xA052,\n\t0xC7D4, 0xA053, 0xC7D5, 0xA054, 0xC7D6, 0xA055, 0xC7D7, 0xA056,\n\t0xC7D8, 0xC0F5, 0xC7D9, 0xA057, 0xC7DA, 0xA058, 0xC7DB, 0xA059,\n\t0xC7DC, 0xA05A, 0xC7DD, 0xC0F6, 0xC7DE, 0xA061, 0xC7DF, 0xA062,\n\t0xC7E0, 0xA063, 0xC7E1, 0xA064, 0xC7E2, 0xA065, 0xC7E3, 0xA066,\n\t0xC7E4, 0xC0F7, 0xC7E5, 0xA067, 0xC7E6, 0xA068, 0xC7E7, 0xA069,\n\t0xC7E8, 0xC0F8, 0xC7E9, 0xA06A, 0xC7EA, 0xA06B, 0xC7EB, 0xA06C,\n\t0xC7EC, 0xC0F9, 0xC7ED, 0xA06D, 0xC7EE, 0xA06E, 0xC7EF, 0xA06F,\n\t0xC7F0, 0xA070, 0xC7F1, 0xA071, 0xC7F2, 0xA072, 0xC7F3, 0xA073,\n\t0xC7F4, 0xA074, 0xC7F5, 0xA075, 0xC7F6, 0xA076, 0xC7F7, 0xA077,\n\t0xC7F8, 0xA078, 0xC7F9, 0xA079, 0xC7FA, 0xA07A, 0xC7FB, 0xA081,\n\t0xC7FC, 0xA082, 0xC7FD, 0xA083, 0xC7FE, 0xA084, 0xC7FF, 0xA085,\n\t0xC800, 0xC0FA, 0xC801, 0xC0FB, 0xC802, 0xA086, 0xC803, 0xA087,\n\t0xC804, 0xC0FC, 0xC805, 0xA088, 0xC806, 0xA089, 0xC807, 0xA08A,\n\t0xC808, 0xC0FD, 0xC809, 0xA08B, 0xC80A, 0xC0FE, 0xC80B, 0xA08C,\n\t0xC80C, 0xA08D, 0xC80D, 0xA08E, 0xC80E, 0xA08F, 0xC80F, 0xA090,\n\t0xC810, 0xC1A1, 0xC811, 0xC1A2, 0xC812, 0xA091, 0xC813, 0xC1A3,\n\t0xC814, 0xA092, 0xC815, 0xC1A4, 0xC816, 0xC1A5, 0xC817, 0xA093,\n\t0xC818, 0xA094, 0xC819, 0xA095, 0xC81A, 0xA096, 0xC81B, 0xA097,\n\t0xC81C, 0xC1A6, 0xC81D, 0xC1A7, 0xC81E, 0xA098, 0xC81F, 0xA099,\n\t0xC820, 0xC1A8, 0xC821, 0xA09A, 0xC822, 0xA09B, 0xC823, 0xA09C,\n\t0xC824, 0xC1A9, 0xC825, 0xA09D, 0xC826, 0xA09E, 0xC827, 0xA09F,\n\t0xC828, 0xA0A0, 0xC829, 0xA0A1, 0xC82A, 0xA0A2, 0xC82B, 0xA0A3,\n\t0xC82C, 0xC1AA, 0xC82D, 0xC1AB, 0xC82E, 0xA0A4, 0xC82F, 0xC1AC,\n\t0xC830, 0xA0A5, 0xC831, 0xC1AD, 0xC832, 0xA0A6, 0xC833, 0xA0A7,\n\t0xC834, 0xA0A8, 0xC835, 0xA0A9, 0xC836, 0xA0AA, 0xC837, 0xA0AB,\n\t0xC838, 0xC1AE, 0xC839, 0xA0AC, 0xC83A, 0xA0AD, 0xC83B, 0xA0AE,\n\t0xC83C, 0xC1AF, 0xC83D, 0xA0AF, 0xC83E, 0xA0B0, 0xC83F, 0xA0B1,\n\t0xC840, 0xC1B0, 0xC841, 0xA0B2, 0xC842, 0xA0B3, 0xC843, 0xA0B4,\n\t0xC844, 0xA0B5, 0xC845, 0xA0B6, 0xC846, 0xA0B7, 0xC847, 0xA0B8,\n\t0xC848, 0xC1B1, 0xC849, 0xC1B2, 0xC84A, 0xA0B9, 0xC84B, 0xA0BA,\n\t0xC84C, 0xC1B3, 0xC84D, 0xC1B4, 0xC84E, 0xA0BB, 0xC84F, 0xA0BC,\n\t0xC850, 0xA0BD, 0xC851, 0xA0BE, 0xC852, 0xA0BF, 0xC853, 0xA0C0,\n\t0xC854, 0xC1B5, 0xC855, 0xA0C1, 0xC856, 0xA0C2, 0xC857, 0xA0C3,\n\t0xC858, 0xA0C4, 0xC859, 0xA0C5, 0xC85A, 0xA0C6, 0xC85B, 0xA0C7,\n\t0xC85C, 0xA0C8, 0xC85D, 0xA0C9, 0xC85E, 0xA0CA, 0xC85F, 0xA0CB,\n\t0xC860, 0xA0CC, 0xC861, 0xA0CD, 0xC862, 0xA0CE, 0xC863, 0xA0CF,\n\t0xC864, 0xA0D0, 0xC865, 0xA0D1, 0xC866, 0xA0D2, 0xC867, 0xA0D3,\n\t0xC868, 0xA0D4, 0xC869, 0xA0D5, 0xC86A, 0xA0D6, 0xC86B, 0xA0D7,\n\t0xC86C, 0xA0D8, 0xC86D, 0xA0D9, 0xC86E, 0xA0DA, 0xC86F, 0xA0DB,\n\t0xC870, 0xC1B6, 0xC871, 0xC1B7, 0xC872, 0xA0DC, 0xC873, 0xA0DD,\n\t0xC874, 0xC1B8, 0xC875, 0xA0DE, 0xC876, 0xA0DF, 0xC877, 0xA0E0,\n\t0xC878, 0xC1B9, 0xC879, 0xA0E1, 0xC87A, 0xC1BA, 0xC87B, 0xA0E2,\n\t0xC87C, 0xA0E3, 0xC87D, 0xA0E4, 0xC87E, 0xA0E5, 0xC87F, 0xA0E6,\n\t0xC880, 0xC1BB, 0xC881, 0xC1BC, 0xC882, 0xA0E7, 0xC883, 0xC1BD,\n\t0xC884, 0xA0E8, 0xC885, 0xC1BE, 0xC886, 0xC1BF, 0xC887, 0xC1C0,\n\t0xC888, 0xA0E9, 0xC889, 0xA0EA, 0xC88A, 0xA0EB, 0xC88B, 0xC1C1,\n\t0xC88C, 0xC1C2, 0xC88D, 0xC1C3, 0xC88E, 0xA0EC, 0xC88F, 0xA0ED,\n\t0xC890, 0xA0EE, 0xC891, 0xA0EF, 0xC892, 0xA0F0, 0xC893, 0xA0F1,\n\t0xC894, 0xC1C4, 0xC895, 0xA0F2, 0xC896, 0xA0F3, 0xC897, 0xA0F4,\n\t0xC898, 0xA0F5, 0xC899, 0xA0F6, 0xC89A, 0xA0F7, 0xC89B, 0xA0F8,\n\t0xC89C, 0xA0F9, 0xC89D, 0xC1C5, 0xC89E, 0xA0FA, 0xC89F, 0xC1C6,\n\t0xC8A0, 0xA0FB, 0xC8A1, 0xC1C7, 0xC8A2, 0xA0FC, 0xC8A3, 0xA0FD,\n\t0xC8A4, 0xA0FE, 0xC8A5, 0xA141, 0xC8A6, 0xA142, 0xC8A7, 0xA143,\n\t0xC8A8, 0xC1C8, 0xC8A9, 0xA144, 0xC8AA, 0xA145, 0xC8AB, 0xA146,\n\t0xC8AC, 0xA147, 0xC8AD, 0xA148, 0xC8AE, 0xA149, 0xC8AF, 0xA14A,\n\t0xC8B0, 0xA14B, 0xC8B1, 0xA14C, 0xC8B2, 0xA14D, 0xC8B3, 0xA14E,\n\t0xC8B4, 0xA14F, 0xC8B5, 0xA150, 0xC8B6, 0xA151, 0xC8B7, 0xA152,\n\t0xC8B8, 0xA153, 0xC8B9, 0xA154, 0xC8BA, 0xA155, 0xC8BB, 0xA156,\n\t0xC8BC, 0xC1C9, 0xC8BD, 0xC1CA, 0xC8BE, 0xA157, 0xC8BF, 0xA158,\n\t0xC8C0, 0xA159, 0xC8C1, 0xA15A, 0xC8C2, 0xA161, 0xC8C3, 0xA162,\n\t0xC8C4, 0xC1CB, 0xC8C5, 0xA163, 0xC8C6, 0xA164, 0xC8C7, 0xA165,\n\t0xC8C8, 0xC1CC, 0xC8C9, 0xA166, 0xC8CA, 0xA167, 0xC8CB, 0xA168,\n\t0xC8CC, 0xC1CD, 0xC8CD, 0xA169, 0xC8CE, 0xA16A, 0xC8CF, 0xA16B,\n\t0xC8D0, 0xA16C, 0xC8D1, 0xA16D, 0xC8D2, 0xA16E, 0xC8D3, 0xA16F,\n\t0xC8D4, 0xC1CE, 0xC8D5, 0xC1CF, 0xC8D6, 0xA170, 0xC8D7, 0xC1D0,\n\t0xC8D8, 0xA171, 0xC8D9, 0xC1D1, 0xC8DA, 0xA172, 0xC8DB, 0xA173,\n\t0xC8DC, 0xA174, 0xC8DD, 0xA175, 0xC8DE, 0xA176, 0xC8DF, 0xA177,\n\t0xC8E0, 0xC1D2, 0xC8E1, 0xC1D3, 0xC8E2, 0xA178, 0xC8E3, 0xA179,\n\t0xC8E4, 0xC1D4, 0xC8E5, 0xA17A, 0xC8E6, 0xA181, 0xC8E7, 0xA182,\n\t0xC8E8, 0xA183, 0xC8E9, 0xA184, 0xC8EA, 0xA185, 0xC8EB, 0xA186,\n\t0xC8EC, 0xA187, 0xC8ED, 0xA188, 0xC8EE, 0xA189, 0xC8EF, 0xA18A,\n\t0xC8F0, 0xA18B, 0xC8F1, 0xA18C, 0xC8F2, 0xA18D, 0xC8F3, 0xA18E,\n\t0xC8F4, 0xA18F, 0xC8F5, 0xC1D5, 0xC8F6, 0xA190, 0xC8F7, 0xA191,\n\t0xC8F8, 0xA192, 0xC8F9, 0xA193, 0xC8FA, 0xA194, 0xC8FB, 0xA195,\n\t0xC8FC, 0xC1D6, 0xC8FD, 0xC1D7, 0xC8FE, 0xA196, 0xC8FF, 0xA197,\n\t0xC900, 0xC1D8, 0xC901, 0xA198, 0xC902, 0xA199, 0xC903, 0xA19A,\n\t0xC904, 0xC1D9, 0xC905, 0xC1DA, 0xC906, 0xC1DB, 0xC907, 0xA19B,\n\t0xC908, 0xA19C, 0xC909, 0xA19D, 0xC90A, 0xA19E, 0xC90B, 0xA19F,\n\t0xC90C, 0xC1DC, 0xC90D, 0xC1DD, 0xC90E, 0xA1A0, 0xC90F, 0xC1DE,\n\t0xC910, 0xA241, 0xC911, 0xC1DF, 0xC912, 0xA242, 0xC913, 0xA243,\n\t0xC914, 0xA244, 0xC915, 0xA245, 0xC916, 0xA246, 0xC917, 0xA247,\n\t0xC918, 0xC1E0, 0xC919, 0xA248, 0xC91A, 0xA249, 0xC91B, 0xA24A,\n\t0xC91C, 0xA24B, 0xC91D, 0xA24C, 0xC91E, 0xA24D, 0xC91F, 0xA24E,\n\t0xC920, 0xA24F, 0xC921, 0xA250, 0xC922, 0xA251, 0xC923, 0xA252,\n\t0xC924, 0xA253, 0xC925, 0xA254, 0xC926, 0xA255, 0xC927, 0xA256,\n\t0xC928, 0xA257, 0xC929, 0xA258, 0xC92A, 0xA259, 0xC92B, 0xA25A,\n\t0xC92C, 0xC1E1, 0xC92D, 0xA261, 0xC92E, 0xA262, 0xC92F, 0xA263,\n\t0xC930, 0xA264, 0xC931, 0xA265, 0xC932, 0xA266, 0xC933, 0xA267,\n\t0xC934, 0xC1E2, 0xC935, 0xA268, 0xC936, 0xA269, 0xC937, 0xA26A,\n\t0xC938, 0xA26B, 0xC939, 0xA26C, 0xC93A, 0xA26D, 0xC93B, 0xA26E,\n\t0xC93C, 0xA26F, 0xC93D, 0xA270, 0xC93E, 0xA271, 0xC93F, 0xA272,\n\t0xC940, 0xA273, 0xC941, 0xA274, 0xC942, 0xA275, 0xC943, 0xA276,\n\t0xC944, 0xA277, 0xC945, 0xA278, 0xC946, 0xA279, 0xC947, 0xA27A,\n\t0xC948, 0xA281, 0xC949, 0xA282, 0xC94A, 0xA283, 0xC94B, 0xA284,\n\t0xC94C, 0xA285, 0xC94D, 0xA286, 0xC94E, 0xA287, 0xC94F, 0xA288,\n\t0xC950, 0xC1E3, 0xC951, 0xC1E4, 0xC952, 0xA289, 0xC953, 0xA28A,\n\t0xC954, 0xC1E5, 0xC955, 0xA28B, 0xC956, 0xA28C, 0xC957, 0xA28D,\n\t0xC958, 0xC1E6, 0xC959, 0xA28E, 0xC95A, 0xA28F, 0xC95B, 0xA290,\n\t0xC95C, 0xA291, 0xC95D, 0xA292, 0xC95E, 0xA293, 0xC95F, 0xA294,\n\t0xC960, 0xC1E7, 0xC961, 0xC1E8, 0xC962, 0xA295, 0xC963, 0xC1E9,\n\t0xC964, 0xA296, 0xC965, 0xA297, 0xC966, 0xA298, 0xC967, 0xA299,\n\t0xC968, 0xA29A, 0xC969, 0xA29B, 0xC96A, 0xA29C, 0xC96B, 0xA29D,\n\t0xC96C, 0xC1EA, 0xC96D, 0xA29E, 0xC96E, 0xA29F, 0xC96F, 0xA2A0,\n\t0xC970, 0xC1EB, 0xC971, 0xA341, 0xC972, 0xA342, 0xC973, 0xA343,\n\t0xC974, 0xC1EC, 0xC975, 0xA344, 0xC976, 0xA345, 0xC977, 0xA346,\n\t0xC978, 0xA347, 0xC979, 0xA348, 0xC97A, 0xA349, 0xC97B, 0xA34A,\n\t0xC97C, 0xC1ED, 0xC97D, 0xA34B, 0xC97E, 0xA34C, 0xC97F, 0xA34D,\n\t0xC980, 0xA34E, 0xC981, 0xA34F, 0xC982, 0xA350, 0xC983, 0xA351,\n\t0xC984, 0xA352, 0xC985, 0xA353, 0xC986, 0xA354, 0xC987, 0xA355,\n\t0xC988, 0xC1EE, 0xC989, 0xC1EF, 0xC98A, 0xA356, 0xC98B, 0xA357,\n\t0xC98C, 0xC1F0, 0xC98D, 0xA358, 0xC98E, 0xA359, 0xC98F, 0xA35A,\n\t0xC990, 0xC1F1, 0xC991, 0xA361, 0xC992, 0xA362, 0xC993, 0xA363,\n\t0xC994, 0xA364, 0xC995, 0xA365, 0xC996, 0xA366, 0xC997, 0xA367,\n\t0xC998, 0xC1F2, 0xC999, 0xC1F3, 0xC99A, 0xA368, 0xC99B, 0xC1F4,\n\t0xC99C, 0xA369, 0xC99D, 0xC1F5, 0xC99E, 0xA36A, 0xC99F, 0xA36B,\n\t0xC9A0, 0xA36C, 0xC9A1, 0xA36D, 0xC9A2, 0xA36E, 0xC9A3, 0xA36F,\n\t0xC9A4, 0xA370, 0xC9A5, 0xA371, 0xC9A6, 0xA372, 0xC9A7, 0xA373,\n\t0xC9A8, 0xA374, 0xC9A9, 0xA375, 0xC9AA, 0xA376, 0xC9AB, 0xA377,\n\t0xC9AC, 0xA378, 0xC9AD, 0xA379, 0xC9AE, 0xA37A, 0xC9AF, 0xA381,\n\t0xC9B0, 0xA382, 0xC9B1, 0xA383, 0xC9B2, 0xA384, 0xC9B3, 0xA385,\n\t0xC9B4, 0xA386, 0xC9B5, 0xA387, 0xC9B6, 0xA388, 0xC9B7, 0xA389,\n\t0xC9B8, 0xA38A, 0xC9B9, 0xA38B, 0xC9BA, 0xA38C, 0xC9BB, 0xA38D,\n\t0xC9BC, 0xA38E, 0xC9BD, 0xA38F, 0xC9BE, 0xA390, 0xC9BF, 0xA391,\n\t0xC9C0, 0xC1F6, 0xC9C1, 0xC1F7, 0xC9C2, 0xA392, 0xC9C3, 0xA393,\n\t0xC9C4, 0xC1F8, 0xC9C5, 0xA394, 0xC9C6, 0xA395, 0xC9C7, 0xC1F9,\n\t0xC9C8, 0xC1FA, 0xC9C9, 0xA396, 0xC9CA, 0xC1FB, 0xC9CB, 0xA397,\n\t0xC9CC, 0xA398, 0xC9CD, 0xA399, 0xC9CE, 0xA39A, 0xC9CF, 0xA39B,\n\t0xC9D0, 0xC1FC, 0xC9D1, 0xC1FD, 0xC9D2, 0xA39C, 0xC9D3, 0xC1FE,\n\t0xC9D4, 0xA39D, 0xC9D5, 0xC2A1, 0xC9D6, 0xC2A2, 0xC9D7, 0xA39E,\n\t0xC9D8, 0xA39F, 0xC9D9, 0xC2A3, 0xC9DA, 0xC2A4, 0xC9DB, 0xA3A0,\n\t0xC9DC, 0xC2A5, 0xC9DD, 0xC2A6, 0xC9DE, 0xA441, 0xC9DF, 0xA442,\n\t0xC9E0, 0xC2A7, 0xC9E1, 0xA443, 0xC9E2, 0xC2A8, 0xC9E3, 0xA444,\n\t0xC9E4, 0xC2A9, 0xC9E5, 0xA445, 0xC9E6, 0xA446, 0xC9E7, 0xC2AA,\n\t0xC9E8, 0xA447, 0xC9E9, 0xA448, 0xC9EA, 0xA449, 0xC9EB, 0xA44A,\n\t0xC9EC, 0xC2AB, 0xC9ED, 0xC2AC, 0xC9EE, 0xA44B, 0xC9EF, 0xC2AD,\n\t0xC9F0, 0xC2AE, 0xC9F1, 0xC2AF, 0xC9F2, 0xA44C, 0xC9F3, 0xA44D,\n\t0xC9F4, 0xA44E, 0xC9F5, 0xA44F, 0xC9F6, 0xA450, 0xC9F7, 0xA451,\n\t0xC9F8, 0xC2B0, 0xC9F9, 0xC2B1, 0xC9FA, 0xA452, 0xC9FB, 0xA453,\n\t0xC9FC, 0xC2B2, 0xC9FD, 0xA454, 0xC9FE, 0xA455, 0xC9FF, 0xA456,\n\t0xCA00, 0xC2B3, 0xCA01, 0xA457, 0xCA02, 0xA458, 0xCA03, 0xA459,\n\t0xCA04, 0xA45A, 0xCA05, 0xA461, 0xCA06, 0xA462, 0xCA07, 0xA463,\n\t0xCA08, 0xC2B4, 0xCA09, 0xC2B5, 0xCA0A, 0xA464, 0xCA0B, 0xC2B6,\n\t0xCA0C, 0xC2B7, 0xCA0D, 0xC2B8, 0xCA0E, 0xA465, 0xCA0F, 0xA466,\n\t0xCA10, 0xA467, 0xCA11, 0xA468, 0xCA12, 0xA469, 0xCA13, 0xA46A,\n\t0xCA14, 0xC2B9, 0xCA15, 0xA46B, 0xCA16, 0xA46C, 0xCA17, 0xA46D,\n\t0xCA18, 0xC2BA, 0xCA19, 0xA46E, 0xCA1A, 0xA46F, 0xCA1B, 0xA470,\n\t0xCA1C, 0xA471, 0xCA1D, 0xA472, 0xCA1E, 0xA473, 0xCA1F, 0xA474,\n\t0xCA20, 0xA475, 0xCA21, 0xA476, 0xCA22, 0xA477, 0xCA23, 0xA478,\n\t0xCA24, 0xA479, 0xCA25, 0xA47A, 0xCA26, 0xA481, 0xCA27, 0xA482,\n\t0xCA28, 0xA483, 0xCA29, 0xC2BB, 0xCA2A, 0xA484, 0xCA2B, 0xA485,\n\t0xCA2C, 0xA486, 0xCA2D, 0xA487, 0xCA2E, 0xA488, 0xCA2F, 0xA489,\n\t0xCA30, 0xA48A, 0xCA31, 0xA48B, 0xCA32, 0xA48C, 0xCA33, 0xA48D,\n\t0xCA34, 0xA48E, 0xCA35, 0xA48F, 0xCA36, 0xA490, 0xCA37, 0xA491,\n\t0xCA38, 0xA492, 0xCA39, 0xA493, 0xCA3A, 0xA494, 0xCA3B, 0xA495,\n\t0xCA3C, 0xA496, 0xCA3D, 0xA497, 0xCA3E, 0xA498, 0xCA3F, 0xA499,\n\t0xCA40, 0xA49A, 0xCA41, 0xA49B, 0xCA42, 0xA49C, 0xCA43, 0xA49D,\n\t0xCA44, 0xA49E, 0xCA45, 0xA49F, 0xCA46, 0xA4A0, 0xCA47, 0xA541,\n\t0xCA48, 0xA542, 0xCA49, 0xA543, 0xCA4A, 0xA544, 0xCA4B, 0xA545,\n\t0xCA4C, 0xC2BC, 0xCA4D, 0xC2BD, 0xCA4E, 0xA546, 0xCA4F, 0xA547,\n\t0xCA50, 0xC2BE, 0xCA51, 0xA548, 0xCA52, 0xA549, 0xCA53, 0xA54A,\n\t0xCA54, 0xC2BF, 0xCA55, 0xA54B, 0xCA56, 0xA54C, 0xCA57, 0xA54D,\n\t0xCA58, 0xA54E, 0xCA59, 0xA54F, 0xCA5A, 0xA550, 0xCA5B, 0xA551,\n\t0xCA5C, 0xC2C0, 0xCA5D, 0xC2C1, 0xCA5E, 0xA552, 0xCA5F, 0xC2C2,\n\t0xCA60, 0xC2C3, 0xCA61, 0xC2C4, 0xCA62, 0xA553, 0xCA63, 0xA554,\n\t0xCA64, 0xA555, 0xCA65, 0xA556, 0xCA66, 0xA557, 0xCA67, 0xA558,\n\t0xCA68, 0xC2C5, 0xCA69, 0xA559, 0xCA6A, 0xA55A, 0xCA6B, 0xA561,\n\t0xCA6C, 0xA562, 0xCA6D, 0xA563, 0xCA6E, 0xA564, 0xCA6F, 0xA565,\n\t0xCA70, 0xA566, 0xCA71, 0xA567, 0xCA72, 0xA568, 0xCA73, 0xA569,\n\t0xCA74, 0xA56A, 0xCA75, 0xA56B, 0xCA76, 0xA56C, 0xCA77, 0xA56D,\n\t0xCA78, 0xA56E, 0xCA79, 0xA56F, 0xCA7A, 0xA570, 0xCA7B, 0xA571,\n\t0xCA7C, 0xA572, 0xCA7D, 0xC2C6, 0xCA7E, 0xA573, 0xCA7F, 0xA574,\n\t0xCA80, 0xA575, 0xCA81, 0xA576, 0xCA82, 0xA577, 0xCA83, 0xA578,\n\t0xCA84, 0xC2C7, 0xCA85, 0xA579, 0xCA86, 0xA57A, 0xCA87, 0xA581,\n\t0xCA88, 0xA582, 0xCA89, 0xA583, 0xCA8A, 0xA584, 0xCA8B, 0xA585,\n\t0xCA8C, 0xA586, 0xCA8D, 0xA587, 0xCA8E, 0xA588, 0xCA8F, 0xA589,\n\t0xCA90, 0xA58A, 0xCA91, 0xA58B, 0xCA92, 0xA58C, 0xCA93, 0xA58D,\n\t0xCA94, 0xA58E, 0xCA95, 0xA58F, 0xCA96, 0xA590, 0xCA97, 0xA591,\n\t0xCA98, 0xC2C8, 0xCA99, 0xA592, 0xCA9A, 0xA593, 0xCA9B, 0xA594,\n\t0xCA9C, 0xA595, 0xCA9D, 0xA596, 0xCA9E, 0xA597, 0xCA9F, 0xA598,\n\t0xCAA0, 0xA599, 0xCAA1, 0xA59A, 0xCAA2, 0xA59B, 0xCAA3, 0xA59C,\n\t0xCAA4, 0xA59D, 0xCAA5, 0xA59E, 0xCAA6, 0xA59F, 0xCAA7, 0xA5A0,\n\t0xCAA8, 0xA641, 0xCAA9, 0xA642, 0xCAAA, 0xA643, 0xCAAB, 0xA644,\n\t0xCAAC, 0xA645, 0xCAAD, 0xA646, 0xCAAE, 0xA647, 0xCAAF, 0xA648,\n\t0xCAB0, 0xA649, 0xCAB1, 0xA64A, 0xCAB2, 0xA64B, 0xCAB3, 0xA64C,\n\t0xCAB4, 0xA64D, 0xCAB5, 0xA64E, 0xCAB6, 0xA64F, 0xCAB7, 0xA650,\n\t0xCAB8, 0xA651, 0xCAB9, 0xA652, 0xCABA, 0xA653, 0xCABB, 0xA654,\n\t0xCABC, 0xC2C9, 0xCABD, 0xC2CA, 0xCABE, 0xA655, 0xCABF, 0xA656,\n\t0xCAC0, 0xC2CB, 0xCAC1, 0xA657, 0xCAC2, 0xA658, 0xCAC3, 0xA659,\n\t0xCAC4, 0xC2CC, 0xCAC5, 0xA65A, 0xCAC6, 0xA661, 0xCAC7, 0xA662,\n\t0xCAC8, 0xA663, 0xCAC9, 0xA664, 0xCACA, 0xA665, 0xCACB, 0xA666,\n\t0xCACC, 0xC2CD, 0xCACD, 0xC2CE, 0xCACE, 0xA667, 0xCACF, 0xC2CF,\n\t0xCAD0, 0xA668, 0xCAD1, 0xC2D0, 0xCAD2, 0xA669, 0xCAD3, 0xC2D1,\n\t0xCAD4, 0xA66A, 0xCAD5, 0xA66B, 0xCAD6, 0xA66C, 0xCAD7, 0xA66D,\n\t0xCAD8, 0xC2D2, 0xCAD9, 0xC2D3, 0xCADA, 0xA66E, 0xCADB, 0xA66F,\n\t0xCADC, 0xA670, 0xCADD, 0xA671, 0xCADE, 0xA672, 0xCADF, 0xA673,\n\t0xCAE0, 0xC2D4, 0xCAE1, 0xA674, 0xCAE2, 0xA675, 0xCAE3, 0xA676,\n\t0xCAE4, 0xA677, 0xCAE5, 0xA678, 0xCAE6, 0xA679, 0xCAE7, 0xA67A,\n\t0xCAE8, 0xA681, 0xCAE9, 0xA682, 0xCAEA, 0xA683, 0xCAEB, 0xA684,\n\t0xCAEC, 0xC2D5, 0xCAED, 0xA685, 0xCAEE, 0xA686, 0xCAEF, 0xA687,\n\t0xCAF0, 0xA688, 0xCAF1, 0xA689, 0xCAF2, 0xA68A, 0xCAF3, 0xA68B,\n\t0xCAF4, 0xC2D6, 0xCAF5, 0xA68C, 0xCAF6, 0xA68D, 0xCAF7, 0xA68E,\n\t0xCAF8, 0xA68F, 0xCAF9, 0xA690, 0xCAFA, 0xA691, 0xCAFB, 0xA692,\n\t0xCAFC, 0xA693, 0xCAFD, 0xA694, 0xCAFE, 0xA695, 0xCAFF, 0xA696,\n\t0xCB00, 0xA697, 0xCB01, 0xA698, 0xCB02, 0xA699, 0xCB03, 0xA69A,\n\t0xCB04, 0xA69B, 0xCB05, 0xA69C, 0xCB06, 0xA69D, 0xCB07, 0xA69E,\n\t0xCB08, 0xC2D7, 0xCB09, 0xA69F, 0xCB0A, 0xA6A0, 0xCB0B, 0xA741,\n\t0xCB0C, 0xA742, 0xCB0D, 0xA743, 0xCB0E, 0xA744, 0xCB0F, 0xA745,\n\t0xCB10, 0xC2D8, 0xCB11, 0xA746, 0xCB12, 0xA747, 0xCB13, 0xA748,\n\t0xCB14, 0xC2D9, 0xCB15, 0xA749, 0xCB16, 0xA74A, 0xCB17, 0xA74B,\n\t0xCB18, 0xC2DA, 0xCB19, 0xA74C, 0xCB1A, 0xA74D, 0xCB1B, 0xA74E,\n\t0xCB1C, 0xA74F, 0xCB1D, 0xA750, 0xCB1E, 0xA751, 0xCB1F, 0xA752,\n\t0xCB20, 0xC2DB, 0xCB21, 0xC2DC, 0xCB22, 0xA753, 0xCB23, 0xA754,\n\t0xCB24, 0xA755, 0xCB25, 0xA756, 0xCB26, 0xA757, 0xCB27, 0xA758,\n\t0xCB28, 0xA759, 0xCB29, 0xA75A, 0xCB2A, 0xA761, 0xCB2B, 0xA762,\n\t0xCB2C, 0xA763, 0xCB2D, 0xA764, 0xCB2E, 0xA765, 0xCB2F, 0xA766,\n\t0xCB30, 0xA767, 0xCB31, 0xA768, 0xCB32, 0xA769, 0xCB33, 0xA76A,\n\t0xCB34, 0xA76B, 0xCB35, 0xA76C, 0xCB36, 0xA76D, 0xCB37, 0xA76E,\n\t0xCB38, 0xA76F, 0xCB39, 0xA770, 0xCB3A, 0xA771, 0xCB3B, 0xA772,\n\t0xCB3C, 0xA773, 0xCB3D, 0xA774, 0xCB3E, 0xA775, 0xCB3F, 0xA776,\n\t0xCB40, 0xA777, 0xCB41, 0xC2DD, 0xCB42, 0xA778, 0xCB43, 0xA779,\n\t0xCB44, 0xA77A, 0xCB45, 0xA781, 0xCB46, 0xA782, 0xCB47, 0xA783,\n\t0xCB48, 0xC2DE, 0xCB49, 0xC2DF, 0xCB4A, 0xA784, 0xCB4B, 0xA785,\n\t0xCB4C, 0xC2E0, 0xCB4D, 0xA786, 0xCB4E, 0xA787, 0xCB4F, 0xA788,\n\t0xCB50, 0xC2E1, 0xCB51, 0xA789, 0xCB52, 0xA78A, 0xCB53, 0xA78B,\n\t0xCB54, 0xA78C, 0xCB55, 0xA78D, 0xCB56, 0xA78E, 0xCB57, 0xA78F,\n\t0xCB58, 0xC2E2, 0xCB59, 0xC2E3, 0xCB5A, 0xA790, 0xCB5B, 0xA791,\n\t0xCB5C, 0xA792, 0xCB5D, 0xC2E4, 0xCB5E, 0xA793, 0xCB5F, 0xA794,\n\t0xCB60, 0xA795, 0xCB61, 0xA796, 0xCB62, 0xA797, 0xCB63, 0xA798,\n\t0xCB64, 0xC2E5, 0xCB65, 0xA799, 0xCB66, 0xA79A, 0xCB67, 0xA79B,\n\t0xCB68, 0xA79C, 0xCB69, 0xA79D, 0xCB6A, 0xA79E, 0xCB6B, 0xA79F,\n\t0xCB6C, 0xA7A0, 0xCB6D, 0xA841, 0xCB6E, 0xA842, 0xCB6F, 0xA843,\n\t0xCB70, 0xA844, 0xCB71, 0xA845, 0xCB72, 0xA846, 0xCB73, 0xA847,\n\t0xCB74, 0xA848, 0xCB75, 0xA849, 0xCB76, 0xA84A, 0xCB77, 0xA84B,\n\t0xCB78, 0xC2E6, 0xCB79, 0xC2E7, 0xCB7A, 0xA84C, 0xCB7B, 0xA84D,\n\t0xCB7C, 0xA84E, 0xCB7D, 0xA84F, 0xCB7E, 0xA850, 0xCB7F, 0xA851,\n\t0xCB80, 0xA852, 0xCB81, 0xA853, 0xCB82, 0xA854, 0xCB83, 0xA855,\n\t0xCB84, 0xA856, 0xCB85, 0xA857, 0xCB86, 0xA858, 0xCB87, 0xA859,\n\t0xCB88, 0xA85A, 0xCB89, 0xA861, 0xCB8A, 0xA862, 0xCB8B, 0xA863,\n\t0xCB8C, 0xA864, 0xCB8D, 0xA865, 0xCB8E, 0xA866, 0xCB8F, 0xA867,\n\t0xCB90, 0xA868, 0xCB91, 0xA869, 0xCB92, 0xA86A, 0xCB93, 0xA86B,\n\t0xCB94, 0xA86C, 0xCB95, 0xA86D, 0xCB96, 0xA86E, 0xCB97, 0xA86F,\n\t0xCB98, 0xA870, 0xCB99, 0xA871, 0xCB9A, 0xA872, 0xCB9B, 0xA873,\n\t0xCB9C, 0xC2E8, 0xCB9D, 0xA874, 0xCB9E, 0xA875, 0xCB9F, 0xA876,\n\t0xCBA0, 0xA877, 0xCBA1, 0xA878, 0xCBA2, 0xA879, 0xCBA3, 0xA87A,\n\t0xCBA4, 0xA881, 0xCBA5, 0xA882, 0xCBA6, 0xA883, 0xCBA7, 0xA884,\n\t0xCBA8, 0xA885, 0xCBA9, 0xA886, 0xCBAA, 0xA887, 0xCBAB, 0xA888,\n\t0xCBAC, 0xA889, 0xCBAD, 0xA88A, 0xCBAE, 0xA88B, 0xCBAF, 0xA88C,\n\t0xCBB0, 0xA88D, 0xCBB1, 0xA88E, 0xCBB2, 0xA88F, 0xCBB3, 0xA890,\n\t0xCBB4, 0xA891, 0xCBB5, 0xA892, 0xCBB6, 0xA893, 0xCBB7, 0xA894,\n\t0xCBB8, 0xC2E9, 0xCBB9, 0xA895, 0xCBBA, 0xA896, 0xCBBB, 0xA897,\n\t0xCBBC, 0xA898, 0xCBBD, 0xA899, 0xCBBE, 0xA89A, 0xCBBF, 0xA89B,\n\t0xCBC0, 0xA89C, 0xCBC1, 0xA89D, 0xCBC2, 0xA89E, 0xCBC3, 0xA89F,\n\t0xCBC4, 0xA8A0, 0xCBC5, 0xA941, 0xCBC6, 0xA942, 0xCBC7, 0xA943,\n\t0xCBC8, 0xA944, 0xCBC9, 0xA945, 0xCBCA, 0xA946, 0xCBCB, 0xA947,\n\t0xCBCC, 0xA948, 0xCBCD, 0xA949, 0xCBCE, 0xA94A, 0xCBCF, 0xA94B,\n\t0xCBD0, 0xA94C, 0xCBD1, 0xA94D, 0xCBD2, 0xA94E, 0xCBD3, 0xA94F,\n\t0xCBD4, 0xC2EA, 0xCBD5, 0xA950, 0xCBD6, 0xA951, 0xCBD7, 0xA952,\n\t0xCBD8, 0xA953, 0xCBD9, 0xA954, 0xCBDA, 0xA955, 0xCBDB, 0xA956,\n\t0xCBDC, 0xA957, 0xCBDD, 0xA958, 0xCBDE, 0xA959, 0xCBDF, 0xA95A,\n\t0xCBE0, 0xA961, 0xCBE1, 0xA962, 0xCBE2, 0xA963, 0xCBE3, 0xA964,\n\t0xCBE4, 0xC2EB, 0xCBE5, 0xA965, 0xCBE6, 0xA966, 0xCBE7, 0xC2EC,\n\t0xCBE8, 0xA967, 0xCBE9, 0xC2ED, 0xCBEA, 0xA968, 0xCBEB, 0xA969,\n\t0xCBEC, 0xA96A, 0xCBED, 0xA96B, 0xCBEE, 0xA96C, 0xCBEF, 0xA96D,\n\t0xCBF0, 0xA96E, 0xCBF1, 0xA96F, 0xCBF2, 0xA970, 0xCBF3, 0xA971,\n\t0xCBF4, 0xA972, 0xCBF5, 0xA973, 0xCBF6, 0xA974, 0xCBF7, 0xA975,\n\t0xCBF8, 0xA976, 0xCBF9, 0xA977, 0xCBFA, 0xA978, 0xCBFB, 0xA979,\n\t0xCBFC, 0xA97A, 0xCBFD, 0xA981, 0xCBFE, 0xA982, 0xCBFF, 0xA983,\n\t0xCC00, 0xA984, 0xCC01, 0xA985, 0xCC02, 0xA986, 0xCC03, 0xA987,\n\t0xCC04, 0xA988, 0xCC05, 0xA989, 0xCC06, 0xA98A, 0xCC07, 0xA98B,\n\t0xCC08, 0xA98C, 0xCC09, 0xA98D, 0xCC0A, 0xA98E, 0xCC0B, 0xA98F,\n\t0xCC0C, 0xC2EE, 0xCC0D, 0xC2EF, 0xCC0E, 0xA990, 0xCC0F, 0xA991,\n\t0xCC10, 0xC2F0, 0xCC11, 0xA992, 0xCC12, 0xA993, 0xCC13, 0xA994,\n\t0xCC14, 0xC2F1, 0xCC15, 0xA995, 0xCC16, 0xA996, 0xCC17, 0xA997,\n\t0xCC18, 0xA998, 0xCC19, 0xA999, 0xCC1A, 0xA99A, 0xCC1B, 0xA99B,\n\t0xCC1C, 0xC2F2, 0xCC1D, 0xC2F3, 0xCC1E, 0xA99C, 0xCC1F, 0xA99D,\n\t0xCC20, 0xA99E, 0xCC21, 0xC2F4, 0xCC22, 0xC2F5, 0xCC23, 0xA99F,\n\t0xCC24, 0xA9A0, 0xCC25, 0xAA41, 0xCC26, 0xAA42, 0xCC27, 0xC2F6,\n\t0xCC28, 0xC2F7, 0xCC29, 0xC2F8, 0xCC2A, 0xAA43, 0xCC2B, 0xAA44,\n\t0xCC2C, 0xC2F9, 0xCC2D, 0xAA45, 0xCC2E, 0xC2FA, 0xCC2F, 0xAA46,\n\t0xCC30, 0xC2FB, 0xCC31, 0xAA47, 0xCC32, 0xAA48, 0xCC33, 0xAA49,\n\t0xCC34, 0xAA4A, 0xCC35, 0xAA4B, 0xCC36, 0xAA4C, 0xCC37, 0xAA4D,\n\t0xCC38, 0xC2FC, 0xCC39, 0xC2FD, 0xCC3A, 0xAA4E, 0xCC3B, 0xC2FE,\n\t0xCC3C, 0xC3A1, 0xCC3D, 0xC3A2, 0xCC3E, 0xC3A3, 0xCC3F, 0xAA4F,\n\t0xCC40, 0xAA50, 0xCC41, 0xAA51, 0xCC42, 0xAA52, 0xCC43, 0xAA53,\n\t0xCC44, 0xC3A4, 0xCC45, 0xC3A5, 0xCC46, 0xAA54, 0xCC47, 0xAA55,\n\t0xCC48, 0xC3A6, 0xCC49, 0xAA56, 0xCC4A, 0xAA57, 0xCC4B, 0xAA58,\n\t0xCC4C, 0xC3A7, 0xCC4D, 0xAA59, 0xCC4E, 0xAA5A, 0xCC4F, 0xAA61,\n\t0xCC50, 0xAA62, 0xCC51, 0xAA63, 0xCC52, 0xAA64, 0xCC53, 0xAA65,\n\t0xCC54, 0xC3A8, 0xCC55, 0xC3A9, 0xCC56, 0xAA66, 0xCC57, 0xC3AA,\n\t0xCC58, 0xC3AB, 0xCC59, 0xC3AC, 0xCC5A, 0xAA67, 0xCC5B, 0xAA68,\n\t0xCC5C, 0xAA69, 0xCC5D, 0xAA6A, 0xCC5E, 0xAA6B, 0xCC5F, 0xAA6C,\n\t0xCC60, 0xC3AD, 0xCC61, 0xAA6D, 0xCC62, 0xAA6E, 0xCC63, 0xAA6F,\n\t0xCC64, 0xC3AE, 0xCC65, 0xAA70, 0xCC66, 0xC3AF, 0xCC67, 0xAA71,\n\t0xCC68, 0xC3B0, 0xCC69, 0xAA72, 0xCC6A, 0xAA73, 0xCC6B, 0xAA74,\n\t0xCC6C, 0xAA75, 0xCC6D, 0xAA76, 0xCC6E, 0xAA77, 0xCC6F, 0xAA78,\n\t0xCC70, 0xC3B1, 0xCC71, 0xAA79, 0xCC72, 0xAA7A, 0xCC73, 0xAA81,\n\t0xCC74, 0xAA82, 0xCC75, 0xC3B2, 0xCC76, 0xAA83, 0xCC77, 0xAA84,\n\t0xCC78, 0xAA85, 0xCC79, 0xAA86, 0xCC7A, 0xAA87, 0xCC7B, 0xAA88,\n\t0xCC7C, 0xAA89, 0xCC7D, 0xAA8A, 0xCC7E, 0xAA8B, 0xCC7F, 0xAA8C,\n\t0xCC80, 0xAA8D, 0xCC81, 0xAA8E, 0xCC82, 0xAA8F, 0xCC83, 0xAA90,\n\t0xCC84, 0xAA91, 0xCC85, 0xAA92, 0xCC86, 0xAA93, 0xCC87, 0xAA94,\n\t0xCC88, 0xAA95, 0xCC89, 0xAA96, 0xCC8A, 0xAA97, 0xCC8B, 0xAA98,\n\t0xCC8C, 0xAA99, 0xCC8D, 0xAA9A, 0xCC8E, 0xAA9B, 0xCC8F, 0xAA9C,\n\t0xCC90, 0xAA9D, 0xCC91, 0xAA9E, 0xCC92, 0xAA9F, 0xCC93, 0xAAA0,\n\t0xCC94, 0xAB41, 0xCC95, 0xAB42, 0xCC96, 0xAB43, 0xCC97, 0xAB44,\n\t0xCC98, 0xC3B3, 0xCC99, 0xC3B4, 0xCC9A, 0xAB45, 0xCC9B, 0xAB46,\n\t0xCC9C, 0xC3B5, 0xCC9D, 0xAB47, 0xCC9E, 0xAB48, 0xCC9F, 0xAB49,\n\t0xCCA0, 0xC3B6, 0xCCA1, 0xAB4A, 0xCCA2, 0xAB4B, 0xCCA3, 0xAB4C,\n\t0xCCA4, 0xAB4D, 0xCCA5, 0xAB4E, 0xCCA6, 0xAB4F, 0xCCA7, 0xAB50,\n\t0xCCA8, 0xC3B7, 0xCCA9, 0xC3B8, 0xCCAA, 0xAB51, 0xCCAB, 0xC3B9,\n\t0xCCAC, 0xC3BA, 0xCCAD, 0xC3BB, 0xCCAE, 0xAB52, 0xCCAF, 0xAB53,\n\t0xCCB0, 0xAB54, 0xCCB1, 0xAB55, 0xCCB2, 0xAB56, 0xCCB3, 0xAB57,\n\t0xCCB4, 0xC3BC, 0xCCB5, 0xC3BD, 0xCCB6, 0xAB58, 0xCCB7, 0xAB59,\n\t0xCCB8, 0xC3BE, 0xCCB9, 0xAB5A, 0xCCBA, 0xAB61, 0xCCBB, 0xAB62,\n\t0xCCBC, 0xC3BF, 0xCCBD, 0xAB63, 0xCCBE, 0xAB64, 0xCCBF, 0xAB65,\n\t0xCCC0, 0xAB66, 0xCCC1, 0xAB67, 0xCCC2, 0xAB68, 0xCCC3, 0xAB69,\n\t0xCCC4, 0xC3C0, 0xCCC5, 0xC3C1, 0xCCC6, 0xAB6A, 0xCCC7, 0xC3C2,\n\t0xCCC8, 0xAB6B, 0xCCC9, 0xC3C3, 0xCCCA, 0xAB6C, 0xCCCB, 0xAB6D,\n\t0xCCCC, 0xAB6E, 0xCCCD, 0xAB6F, 0xCCCE, 0xAB70, 0xCCCF, 0xAB71,\n\t0xCCD0, 0xC3C4, 0xCCD1, 0xAB72, 0xCCD2, 0xAB73, 0xCCD3, 0xAB74,\n\t0xCCD4, 0xC3C5, 0xCCD5, 0xAB75, 0xCCD6, 0xAB76, 0xCCD7, 0xAB77,\n\t0xCCD8, 0xAB78, 0xCCD9, 0xAB79, 0xCCDA, 0xAB7A, 0xCCDB, 0xAB81,\n\t0xCCDC, 0xAB82, 0xCCDD, 0xAB83, 0xCCDE, 0xAB84, 0xCCDF, 0xAB85,\n\t0xCCE0, 0xAB86, 0xCCE1, 0xAB87, 0xCCE2, 0xAB88, 0xCCE3, 0xAB89,\n\t0xCCE4, 0xC3C6, 0xCCE5, 0xAB8A, 0xCCE6, 0xAB8B, 0xCCE7, 0xAB8C,\n\t0xCCE8, 0xAB8D, 0xCCE9, 0xAB8E, 0xCCEA, 0xAB8F, 0xCCEB, 0xAB90,\n\t0xCCEC, 0xC3C7, 0xCCED, 0xAB91, 0xCCEE, 0xAB92, 0xCCEF, 0xAB93,\n\t0xCCF0, 0xC3C8, 0xCCF1, 0xAB94, 0xCCF2, 0xAB95, 0xCCF3, 0xAB96,\n\t0xCCF4, 0xAB97, 0xCCF5, 0xAB98, 0xCCF6, 0xAB99, 0xCCF7, 0xAB9A,\n\t0xCCF8, 0xAB9B, 0xCCF9, 0xAB9C, 0xCCFA, 0xAB9D, 0xCCFB, 0xAB9E,\n\t0xCCFC, 0xAB9F, 0xCCFD, 0xABA0, 0xCCFE, 0xAC41, 0xCCFF, 0xAC42,\n\t0xCD00, 0xAC43, 0xCD01, 0xC3C9, 0xCD02, 0xAC44, 0xCD03, 0xAC45,\n\t0xCD04, 0xAC46, 0xCD05, 0xAC47, 0xCD06, 0xAC48, 0xCD07, 0xAC49,\n\t0xCD08, 0xC3CA, 0xCD09, 0xC3CB, 0xCD0A, 0xAC4A, 0xCD0B, 0xAC4B,\n\t0xCD0C, 0xC3CC, 0xCD0D, 0xAC4C, 0xCD0E, 0xAC4D, 0xCD0F, 0xAC4E,\n\t0xCD10, 0xC3CD, 0xCD11, 0xAC4F, 0xCD12, 0xAC50, 0xCD13, 0xAC51,\n\t0xCD14, 0xAC52, 0xCD15, 0xAC53, 0xCD16, 0xAC54, 0xCD17, 0xAC55,\n\t0xCD18, 0xC3CE, 0xCD19, 0xC3CF, 0xCD1A, 0xAC56, 0xCD1B, 0xC3D0,\n\t0xCD1C, 0xAC57, 0xCD1D, 0xC3D1, 0xCD1E, 0xAC58, 0xCD1F, 0xAC59,\n\t0xCD20, 0xAC5A, 0xCD21, 0xAC61, 0xCD22, 0xAC62, 0xCD23, 0xAC63,\n\t0xCD24, 0xC3D2, 0xCD25, 0xAC64, 0xCD26, 0xAC65, 0xCD27, 0xAC66,\n\t0xCD28, 0xC3D3, 0xCD29, 0xAC67, 0xCD2A, 0xAC68, 0xCD2B, 0xAC69,\n\t0xCD2C, 0xC3D4, 0xCD2D, 0xAC6A, 0xCD2E, 0xAC6B, 0xCD2F, 0xAC6C,\n\t0xCD30, 0xAC6D, 0xCD31, 0xAC6E, 0xCD32, 0xAC6F, 0xCD33, 0xAC70,\n\t0xCD34, 0xAC71, 0xCD35, 0xAC72, 0xCD36, 0xAC73, 0xCD37, 0xAC74,\n\t0xCD38, 0xAC75, 0xCD39, 0xC3D5, 0xCD3A, 0xAC76, 0xCD3B, 0xAC77,\n\t0xCD3C, 0xAC78, 0xCD3D, 0xAC79, 0xCD3E, 0xAC7A, 0xCD3F, 0xAC81,\n\t0xCD40, 0xAC82, 0xCD41, 0xAC83, 0xCD42, 0xAC84, 0xCD43, 0xAC85,\n\t0xCD44, 0xAC86, 0xCD45, 0xAC87, 0xCD46, 0xAC88, 0xCD47, 0xAC89,\n\t0xCD48, 0xAC8A, 0xCD49, 0xAC8B, 0xCD4A, 0xAC8C, 0xCD4B, 0xAC8D,\n\t0xCD4C, 0xAC8E, 0xCD4D, 0xAC8F, 0xCD4E, 0xAC90, 0xCD4F, 0xAC91,\n\t0xCD50, 0xAC92, 0xCD51, 0xAC93, 0xCD52, 0xAC94, 0xCD53, 0xAC95,\n\t0xCD54, 0xAC96, 0xCD55, 0xAC97, 0xCD56, 0xAC98, 0xCD57, 0xAC99,\n\t0xCD58, 0xAC9A, 0xCD59, 0xAC9B, 0xCD5A, 0xAC9C, 0xCD5B, 0xAC9D,\n\t0xCD5C, 0xC3D6, 0xCD5D, 0xAC9E, 0xCD5E, 0xAC9F, 0xCD5F, 0xACA0,\n\t0xCD60, 0xC3D7, 0xCD61, 0xAD41, 0xCD62, 0xAD42, 0xCD63, 0xAD43,\n\t0xCD64, 0xC3D8, 0xCD65, 0xAD44, 0xCD66, 0xAD45, 0xCD67, 0xAD46,\n\t0xCD68, 0xAD47, 0xCD69, 0xAD48, 0xCD6A, 0xAD49, 0xCD6B, 0xAD4A,\n\t0xCD6C, 0xC3D9, 0xCD6D, 0xC3DA, 0xCD6E, 0xAD4B, 0xCD6F, 0xC3DB,\n\t0xCD70, 0xAD4C, 0xCD71, 0xC3DC, 0xCD72, 0xAD4D, 0xCD73, 0xAD4E,\n\t0xCD74, 0xAD4F, 0xCD75, 0xAD50, 0xCD76, 0xAD51, 0xCD77, 0xAD52,\n\t0xCD78, 0xC3DD, 0xCD79, 0xAD53, 0xCD7A, 0xAD54, 0xCD7B, 0xAD55,\n\t0xCD7C, 0xAD56, 0xCD7D, 0xAD57, 0xCD7E, 0xAD58, 0xCD7F, 0xAD59,\n\t0xCD80, 0xAD5A, 0xCD81, 0xAD61, 0xCD82, 0xAD62, 0xCD83, 0xAD63,\n\t0xCD84, 0xAD64, 0xCD85, 0xAD65, 0xCD86, 0xAD66, 0xCD87, 0xAD67,\n\t0xCD88, 0xC3DE, 0xCD89, 0xAD68, 0xCD8A, 0xAD69, 0xCD8B, 0xAD6A,\n\t0xCD8C, 0xAD6B, 0xCD8D, 0xAD6C, 0xCD8E, 0xAD6D, 0xCD8F, 0xAD6E,\n\t0xCD90, 0xAD6F, 0xCD91, 0xAD70, 0xCD92, 0xAD71, 0xCD93, 0xAD72,\n\t0xCD94, 0xC3DF, 0xCD95, 0xC3E0, 0xCD96, 0xAD73, 0xCD97, 0xAD74,\n\t0xCD98, 0xC3E1, 0xCD99, 0xAD75, 0xCD9A, 0xAD76, 0xCD9B, 0xAD77,\n\t0xCD9C, 0xC3E2, 0xCD9D, 0xAD78, 0xCD9E, 0xAD79, 0xCD9F, 0xAD7A,\n\t0xCDA0, 0xAD81, 0xCDA1, 0xAD82, 0xCDA2, 0xAD83, 0xCDA3, 0xAD84,\n\t0xCDA4, 0xC3E3, 0xCDA5, 0xC3E4, 0xCDA6, 0xAD85, 0xCDA7, 0xC3E5,\n\t0xCDA8, 0xAD86, 0xCDA9, 0xC3E6, 0xCDAA, 0xAD87, 0xCDAB, 0xAD88,\n\t0xCDAC, 0xAD89, 0xCDAD, 0xAD8A, 0xCDAE, 0xAD8B, 0xCDAF, 0xAD8C,\n\t0xCDB0, 0xC3E7, 0xCDB1, 0xAD8D, 0xCDB2, 0xAD8E, 0xCDB3, 0xAD8F,\n\t0xCDB4, 0xAD90, 0xCDB5, 0xAD91, 0xCDB6, 0xAD92, 0xCDB7, 0xAD93,\n\t0xCDB8, 0xAD94, 0xCDB9, 0xAD95, 0xCDBA, 0xAD96, 0xCDBB, 0xAD97,\n\t0xCDBC, 0xAD98, 0xCDBD, 0xAD99, 0xCDBE, 0xAD9A, 0xCDBF, 0xAD9B,\n\t0xCDC0, 0xAD9C, 0xCDC1, 0xAD9D, 0xCDC2, 0xAD9E, 0xCDC3, 0xAD9F,\n\t0xCDC4, 0xC3E8, 0xCDC5, 0xADA0, 0xCDC6, 0xAE41, 0xCDC7, 0xAE42,\n\t0xCDC8, 0xAE43, 0xCDC9, 0xAE44, 0xCDCA, 0xAE45, 0xCDCB, 0xAE46,\n\t0xCDCC, 0xC3E9, 0xCDCD, 0xAE47, 0xCDCE, 0xAE48, 0xCDCF, 0xAE49,\n\t0xCDD0, 0xC3EA, 0xCDD1, 0xAE4A, 0xCDD2, 0xAE4B, 0xCDD3, 0xAE4C,\n\t0xCDD4, 0xAE4D, 0xCDD5, 0xAE4E, 0xCDD6, 0xAE4F, 0xCDD7, 0xAE50,\n\t0xCDD8, 0xAE51, 0xCDD9, 0xAE52, 0xCDDA, 0xAE53, 0xCDDB, 0xAE54,\n\t0xCDDC, 0xAE55, 0xCDDD, 0xAE56, 0xCDDE, 0xAE57, 0xCDDF, 0xAE58,\n\t0xCDE0, 0xAE59, 0xCDE1, 0xAE5A, 0xCDE2, 0xAE61, 0xCDE3, 0xAE62,\n\t0xCDE4, 0xAE63, 0xCDE5, 0xAE64, 0xCDE6, 0xAE65, 0xCDE7, 0xAE66,\n\t0xCDE8, 0xC3EB, 0xCDE9, 0xAE67, 0xCDEA, 0xAE68, 0xCDEB, 0xAE69,\n\t0xCDEC, 0xC3EC, 0xCDED, 0xAE6A, 0xCDEE, 0xAE6B, 0xCDEF, 0xAE6C,\n\t0xCDF0, 0xC3ED, 0xCDF1, 0xAE6D, 0xCDF2, 0xAE6E, 0xCDF3, 0xAE6F,\n\t0xCDF4, 0xAE70, 0xCDF5, 0xAE71, 0xCDF6, 0xAE72, 0xCDF7, 0xAE73,\n\t0xCDF8, 0xC3EE, 0xCDF9, 0xC3EF, 0xCDFA, 0xAE74, 0xCDFB, 0xC3F0,\n\t0xCDFC, 0xAE75, 0xCDFD, 0xC3F1, 0xCDFE, 0xAE76, 0xCDFF, 0xAE77,\n\t0xCE00, 0xAE78, 0xCE01, 0xAE79, 0xCE02, 0xAE7A, 0xCE03, 0xAE81,\n\t0xCE04, 0xC3F2, 0xCE05, 0xAE82, 0xCE06, 0xAE83, 0xCE07, 0xAE84,\n\t0xCE08, 0xC3F3, 0xCE09, 0xAE85, 0xCE0A, 0xAE86, 0xCE0B, 0xAE87,\n\t0xCE0C, 0xC3F4, 0xCE0D, 0xAE88, 0xCE0E, 0xAE89, 0xCE0F, 0xAE8A,\n\t0xCE10, 0xAE8B, 0xCE11, 0xAE8C, 0xCE12, 0xAE8D, 0xCE13, 0xAE8E,\n\t0xCE14, 0xC3F5, 0xCE15, 0xAE8F, 0xCE16, 0xAE90, 0xCE17, 0xAE91,\n\t0xCE18, 0xAE92, 0xCE19, 0xC3F6, 0xCE1A, 0xAE93, 0xCE1B, 0xAE94,\n\t0xCE1C, 0xAE95, 0xCE1D, 0xAE96, 0xCE1E, 0xAE97, 0xCE1F, 0xAE98,\n\t0xCE20, 0xC3F7, 0xCE21, 0xC3F8, 0xCE22, 0xAE99, 0xCE23, 0xAE9A,\n\t0xCE24, 0xC3F9, 0xCE25, 0xAE9B, 0xCE26, 0xAE9C, 0xCE27, 0xAE9D,\n\t0xCE28, 0xC3FA, 0xCE29, 0xAE9E, 0xCE2A, 0xAE9F, 0xCE2B, 0xAEA0,\n\t0xCE2C, 0xAF41, 0xCE2D, 0xAF42, 0xCE2E, 0xAF43, 0xCE2F, 0xAF44,\n\t0xCE30, 0xC3FB, 0xCE31, 0xC3FC, 0xCE32, 0xAF45, 0xCE33, 0xC3FD,\n\t0xCE34, 0xAF46, 0xCE35, 0xC3FE, 0xCE36, 0xAF47, 0xCE37, 0xAF48,\n\t0xCE38, 0xAF49, 0xCE39, 0xAF4A, 0xCE3A, 0xAF4B, 0xCE3B, 0xAF4C,\n\t0xCE3C, 0xAF4D, 0xCE3D, 0xAF4E, 0xCE3E, 0xAF4F, 0xCE3F, 0xAF50,\n\t0xCE40, 0xAF51, 0xCE41, 0xAF52, 0xCE42, 0xAF53, 0xCE43, 0xAF54,\n\t0xCE44, 0xAF55, 0xCE45, 0xAF56, 0xCE46, 0xAF57, 0xCE47, 0xAF58,\n\t0xCE48, 0xAF59, 0xCE49, 0xAF5A, 0xCE4A, 0xAF61, 0xCE4B, 0xAF62,\n\t0xCE4C, 0xAF63, 0xCE4D, 0xAF64, 0xCE4E, 0xAF65, 0xCE4F, 0xAF66,\n\t0xCE50, 0xAF67, 0xCE51, 0xAF68, 0xCE52, 0xAF69, 0xCE53, 0xAF6A,\n\t0xCE54, 0xAF6B, 0xCE55, 0xAF6C, 0xCE56, 0xAF6D, 0xCE57, 0xAF6E,\n\t0xCE58, 0xC4A1, 0xCE59, 0xC4A2, 0xCE5A, 0xAF6F, 0xCE5B, 0xAF70,\n\t0xCE5C, 0xC4A3, 0xCE5D, 0xAF71, 0xCE5E, 0xAF72, 0xCE5F, 0xC4A4,\n\t0xCE60, 0xC4A5, 0xCE61, 0xC4A6, 0xCE62, 0xAF73, 0xCE63, 0xAF74,\n\t0xCE64, 0xAF75, 0xCE65, 0xAF76, 0xCE66, 0xAF77, 0xCE67, 0xAF78,\n\t0xCE68, 0xC4A7, 0xCE69, 0xC4A8, 0xCE6A, 0xAF79, 0xCE6B, 0xC4A9,\n\t0xCE6C, 0xAF7A, 0xCE6D, 0xC4AA, 0xCE6E, 0xAF81, 0xCE6F, 0xAF82,\n\t0xCE70, 0xAF83, 0xCE71, 0xAF84, 0xCE72, 0xAF85, 0xCE73, 0xAF86,\n\t0xCE74, 0xC4AB, 0xCE75, 0xC4AC, 0xCE76, 0xAF87, 0xCE77, 0xAF88,\n\t0xCE78, 0xC4AD, 0xCE79, 0xAF89, 0xCE7A, 0xAF8A, 0xCE7B, 0xAF8B,\n\t0xCE7C, 0xC4AE, 0xCE7D, 0xAF8C, 0xCE7E, 0xAF8D, 0xCE7F, 0xAF8E,\n\t0xCE80, 0xAF8F, 0xCE81, 0xAF90, 0xCE82, 0xAF91, 0xCE83, 0xAF92,\n\t0xCE84, 0xC4AF, 0xCE85, 0xC4B0, 0xCE86, 0xAF93, 0xCE87, 0xC4B1,\n\t0xCE88, 0xAF94, 0xCE89, 0xC4B2, 0xCE8A, 0xAF95, 0xCE8B, 0xAF96,\n\t0xCE8C, 0xAF97, 0xCE8D, 0xAF98, 0xCE8E, 0xAF99, 0xCE8F, 0xAF9A,\n\t0xCE90, 0xC4B3, 0xCE91, 0xC4B4, 0xCE92, 0xAF9B, 0xCE93, 0xAF9C,\n\t0xCE94, 0xC4B5, 0xCE95, 0xAF9D, 0xCE96, 0xAF9E, 0xCE97, 0xAF9F,\n\t0xCE98, 0xC4B6, 0xCE99, 0xAFA0, 0xCE9A, 0xB041, 0xCE9B, 0xB042,\n\t0xCE9C, 0xB043, 0xCE9D, 0xB044, 0xCE9E, 0xB045, 0xCE9F, 0xB046,\n\t0xCEA0, 0xC4B7, 0xCEA1, 0xC4B8, 0xCEA2, 0xB047, 0xCEA3, 0xC4B9,\n\t0xCEA4, 0xC4BA, 0xCEA5, 0xC4BB, 0xCEA6, 0xB048, 0xCEA7, 0xB049,\n\t0xCEA8, 0xB04A, 0xCEA9, 0xB04B, 0xCEAA, 0xB04C, 0xCEAB, 0xB04D,\n\t0xCEAC, 0xC4BC, 0xCEAD, 0xC4BD, 0xCEAE, 0xB04E, 0xCEAF, 0xB04F,\n\t0xCEB0, 0xB050, 0xCEB1, 0xB051, 0xCEB2, 0xB052, 0xCEB3, 0xB053,\n\t0xCEB4, 0xB054, 0xCEB5, 0xB055, 0xCEB6, 0xB056, 0xCEB7, 0xB057,\n\t0xCEB8, 0xB058, 0xCEB9, 0xB059, 0xCEBA, 0xB05A, 0xCEBB, 0xB061,\n\t0xCEBC, 0xB062, 0xCEBD, 0xB063, 0xCEBE, 0xB064, 0xCEBF, 0xB065,\n\t0xCEC0, 0xB066, 0xCEC1, 0xC4BE, 0xCEC2, 0xB067, 0xCEC3, 0xB068,\n\t0xCEC4, 0xB069, 0xCEC5, 0xB06A, 0xCEC6, 0xB06B, 0xCEC7, 0xB06C,\n\t0xCEC8, 0xB06D, 0xCEC9, 0xB06E, 0xCECA, 0xB06F, 0xCECB, 0xB070,\n\t0xCECC, 0xB071, 0xCECD, 0xB072, 0xCECE, 0xB073, 0xCECF, 0xB074,\n\t0xCED0, 0xB075, 0xCED1, 0xB076, 0xCED2, 0xB077, 0xCED3, 0xB078,\n\t0xCED4, 0xB079, 0xCED5, 0xB07A, 0xCED6, 0xB081, 0xCED7, 0xB082,\n\t0xCED8, 0xB083, 0xCED9, 0xB084, 0xCEDA, 0xB085, 0xCEDB, 0xB086,\n\t0xCEDC, 0xB087, 0xCEDD, 0xB088, 0xCEDE, 0xB089, 0xCEDF, 0xB08A,\n\t0xCEE0, 0xB08B, 0xCEE1, 0xB08C, 0xCEE2, 0xB08D, 0xCEE3, 0xB08E,\n\t0xCEE4, 0xC4BF, 0xCEE5, 0xC4C0, 0xCEE6, 0xB08F, 0xCEE7, 0xB090,\n\t0xCEE8, 0xC4C1, 0xCEE9, 0xB091, 0xCEEA, 0xB092, 0xCEEB, 0xC4C2,\n\t0xCEEC, 0xC4C3, 0xCEED, 0xB093, 0xCEEE, 0xB094, 0xCEEF, 0xB095,\n\t0xCEF0, 0xB096, 0xCEF1, 0xB097, 0xCEF2, 0xB098, 0xCEF3, 0xB099,\n\t0xCEF4, 0xC4C4, 0xCEF5, 0xC4C5, 0xCEF6, 0xB09A, 0xCEF7, 0xC4C6,\n\t0xCEF8, 0xC4C7, 0xCEF9, 0xC4C8, 0xCEFA, 0xB09B, 0xCEFB, 0xB09C,\n\t0xCEFC, 0xB09D, 0xCEFD, 0xB09E, 0xCEFE, 0xB09F, 0xCEFF, 0xB0A0,\n\t0xCF00, 0xC4C9, 0xCF01, 0xC4CA, 0xCF02, 0xB141, 0xCF03, 0xB142,\n\t0xCF04, 0xC4CB, 0xCF05, 0xB143, 0xCF06, 0xB144, 0xCF07, 0xB145,\n\t0xCF08, 0xC4CC, 0xCF09, 0xB146, 0xCF0A, 0xB147, 0xCF0B, 0xB148,\n\t0xCF0C, 0xB149, 0xCF0D, 0xB14A, 0xCF0E, 0xB14B, 0xCF0F, 0xB14C,\n\t0xCF10, 0xC4CD, 0xCF11, 0xC4CE, 0xCF12, 0xB14D, 0xCF13, 0xC4CF,\n\t0xCF14, 0xB14E, 0xCF15, 0xC4D0, 0xCF16, 0xB14F, 0xCF17, 0xB150,\n\t0xCF18, 0xB151, 0xCF19, 0xB152, 0xCF1A, 0xB153, 0xCF1B, 0xB154,\n\t0xCF1C, 0xC4D1, 0xCF1D, 0xB155, 0xCF1E, 0xB156, 0xCF1F, 0xB157,\n\t0xCF20, 0xC4D2, 0xCF21, 0xB158, 0xCF22, 0xB159, 0xCF23, 0xB15A,\n\t0xCF24, 0xC4D3, 0xCF25, 0xB161, 0xCF26, 0xB162, 0xCF27, 0xB163,\n\t0xCF28, 0xB164, 0xCF29, 0xB165, 0xCF2A, 0xB166, 0xCF2B, 0xB167,\n\t0xCF2C, 0xC4D4, 0xCF2D, 0xC4D5, 0xCF2E, 0xB168, 0xCF2F, 0xC4D6,\n\t0xCF30, 0xC4D7, 0xCF31, 0xC4D8, 0xCF32, 0xB169, 0xCF33, 0xB16A,\n\t0xCF34, 0xB16B, 0xCF35, 0xB16C, 0xCF36, 0xB16D, 0xCF37, 0xB16E,\n\t0xCF38, 0xC4D9, 0xCF39, 0xB16F, 0xCF3A, 0xB170, 0xCF3B, 0xB171,\n\t0xCF3C, 0xB172, 0xCF3D, 0xB173, 0xCF3E, 0xB174, 0xCF3F, 0xB175,\n\t0xCF40, 0xB176, 0xCF41, 0xB177, 0xCF42, 0xB178, 0xCF43, 0xB179,\n\t0xCF44, 0xB17A, 0xCF45, 0xB181, 0xCF46, 0xB182, 0xCF47, 0xB183,\n\t0xCF48, 0xB184, 0xCF49, 0xB185, 0xCF4A, 0xB186, 0xCF4B, 0xB187,\n\t0xCF4C, 0xB188, 0xCF4D, 0xB189, 0xCF4E, 0xB18A, 0xCF4F, 0xB18B,\n\t0xCF50, 0xB18C, 0xCF51, 0xB18D, 0xCF52, 0xB18E, 0xCF53, 0xB18F,\n\t0xCF54, 0xC4DA, 0xCF55, 0xC4DB, 0xCF56, 0xB190, 0xCF57, 0xB191,\n\t0xCF58, 0xC4DC, 0xCF59, 0xB192, 0xCF5A, 0xB193, 0xCF5B, 0xB194,\n\t0xCF5C, 0xC4DD, 0xCF5D, 0xB195, 0xCF5E, 0xB196, 0xCF5F, 0xB197,\n\t0xCF60, 0xB198, 0xCF61, 0xB199, 0xCF62, 0xB19A, 0xCF63, 0xB19B,\n\t0xCF64, 0xC4DE, 0xCF65, 0xC4DF, 0xCF66, 0xB19C, 0xCF67, 0xC4E0,\n\t0xCF68, 0xB19D, 0xCF69, 0xC4E1, 0xCF6A, 0xB19E, 0xCF6B, 0xB19F,\n\t0xCF6C, 0xB1A0, 0xCF6D, 0xB241, 0xCF6E, 0xB242, 0xCF6F, 0xB243,\n\t0xCF70, 0xC4E2, 0xCF71, 0xC4E3, 0xCF72, 0xB244, 0xCF73, 0xB245,\n\t0xCF74, 0xC4E4, 0xCF75, 0xB246, 0xCF76, 0xB247, 0xCF77, 0xB248,\n\t0xCF78, 0xC4E5, 0xCF79, 0xB249, 0xCF7A, 0xB24A, 0xCF7B, 0xB24B,\n\t0xCF7C, 0xB24C, 0xCF7D, 0xB24D, 0xCF7E, 0xB24E, 0xCF7F, 0xB24F,\n\t0xCF80, 0xC4E6, 0xCF81, 0xB250, 0xCF82, 0xB251, 0xCF83, 0xB252,\n\t0xCF84, 0xB253, 0xCF85, 0xC4E7, 0xCF86, 0xB254, 0xCF87, 0xB255,\n\t0xCF88, 0xB256, 0xCF89, 0xB257, 0xCF8A, 0xB258, 0xCF8B, 0xB259,\n\t0xCF8C, 0xC4E8, 0xCF8D, 0xB25A, 0xCF8E, 0xB261, 0xCF8F, 0xB262,\n\t0xCF90, 0xB263, 0xCF91, 0xB264, 0xCF92, 0xB265, 0xCF93, 0xB266,\n\t0xCF94, 0xB267, 0xCF95, 0xB268, 0xCF96, 0xB269, 0xCF97, 0xB26A,\n\t0xCF98, 0xB26B, 0xCF99, 0xB26C, 0xCF9A, 0xB26D, 0xCF9B, 0xB26E,\n\t0xCF9C, 0xB26F, 0xCF9D, 0xB270, 0xCF9E, 0xB271, 0xCF9F, 0xB272,\n\t0xCFA0, 0xB273, 0xCFA1, 0xC4E9, 0xCFA2, 0xB274, 0xCFA3, 0xB275,\n\t0xCFA4, 0xB276, 0xCFA5, 0xB277, 0xCFA6, 0xB278, 0xCFA7, 0xB279,\n\t0xCFA8, 0xC4EA, 0xCFA9, 0xB27A, 0xCFAA, 0xB281, 0xCFAB, 0xB282,\n\t0xCFAC, 0xB283, 0xCFAD, 0xB284, 0xCFAE, 0xB285, 0xCFAF, 0xB286,\n\t0xCFB0, 0xC4EB, 0xCFB1, 0xB287, 0xCFB2, 0xB288, 0xCFB3, 0xB289,\n\t0xCFB4, 0xB28A, 0xCFB5, 0xB28B, 0xCFB6, 0xB28C, 0xCFB7, 0xB28D,\n\t0xCFB8, 0xB28E, 0xCFB9, 0xB28F, 0xCFBA, 0xB290, 0xCFBB, 0xB291,\n\t0xCFBC, 0xB292, 0xCFBD, 0xB293, 0xCFBE, 0xB294, 0xCFBF, 0xB295,\n\t0xCFC0, 0xB296, 0xCFC1, 0xB297, 0xCFC2, 0xB298, 0xCFC3, 0xB299,\n\t0xCFC4, 0xC4EC, 0xCFC5, 0xB29A, 0xCFC6, 0xB29B, 0xCFC7, 0xB29C,\n\t0xCFC8, 0xB29D, 0xCFC9, 0xB29E, 0xCFCA, 0xB29F, 0xCFCB, 0xB2A0,\n\t0xCFCC, 0xB341, 0xCFCD, 0xB342, 0xCFCE, 0xB343, 0xCFCF, 0xB344,\n\t0xCFD0, 0xB345, 0xCFD1, 0xB346, 0xCFD2, 0xB347, 0xCFD3, 0xB348,\n\t0xCFD4, 0xB349, 0xCFD5, 0xB34A, 0xCFD6, 0xB34B, 0xCFD7, 0xB34C,\n\t0xCFD8, 0xB34D, 0xCFD9, 0xB34E, 0xCFDA, 0xB34F, 0xCFDB, 0xB350,\n\t0xCFDC, 0xB351, 0xCFDD, 0xB352, 0xCFDE, 0xB353, 0xCFDF, 0xB354,\n\t0xCFE0, 0xC4ED, 0xCFE1, 0xC4EE, 0xCFE2, 0xB355, 0xCFE3, 0xB356,\n\t0xCFE4, 0xC4EF, 0xCFE5, 0xB357, 0xCFE6, 0xB358, 0xCFE7, 0xB359,\n\t0xCFE8, 0xC4F0, 0xCFE9, 0xB35A, 0xCFEA, 0xB361, 0xCFEB, 0xB362,\n\t0xCFEC, 0xB363, 0xCFED, 0xB364, 0xCFEE, 0xB365, 0xCFEF, 0xB366,\n\t0xCFF0, 0xC4F1, 0xCFF1, 0xC4F2, 0xCFF2, 0xB367, 0xCFF3, 0xC4F3,\n\t0xCFF4, 0xB368, 0xCFF5, 0xC4F4, 0xCFF6, 0xB369, 0xCFF7, 0xB36A,\n\t0xCFF8, 0xB36B, 0xCFF9, 0xB36C, 0xCFFA, 0xB36D, 0xCFFB, 0xB36E,\n\t0xCFFC, 0xC4F5, 0xCFFD, 0xB36F, 0xCFFE, 0xB370, 0xCFFF, 0xB371,\n\t0xD000, 0xC4F6, 0xD001, 0xB372, 0xD002, 0xB373, 0xD003, 0xB374,\n\t0xD004, 0xC4F7, 0xD005, 0xB375, 0xD006, 0xB376, 0xD007, 0xB377,\n\t0xD008, 0xB378, 0xD009, 0xB379, 0xD00A, 0xB37A, 0xD00B, 0xB381,\n\t0xD00C, 0xB382, 0xD00D, 0xB383, 0xD00E, 0xB384, 0xD00F, 0xB385,\n\t0xD010, 0xB386, 0xD011, 0xC4F8, 0xD012, 0xB387, 0xD013, 0xB388,\n\t0xD014, 0xB389, 0xD015, 0xB38A, 0xD016, 0xB38B, 0xD017, 0xB38C,\n\t0xD018, 0xC4F9, 0xD019, 0xB38D, 0xD01A, 0xB38E, 0xD01B, 0xB38F,\n\t0xD01C, 0xB390, 0xD01D, 0xB391, 0xD01E, 0xB392, 0xD01F, 0xB393,\n\t0xD020, 0xB394, 0xD021, 0xB395, 0xD022, 0xB396, 0xD023, 0xB397,\n\t0xD024, 0xB398, 0xD025, 0xB399, 0xD026, 0xB39A, 0xD027, 0xB39B,\n\t0xD028, 0xB39C, 0xD029, 0xB39D, 0xD02A, 0xB39E, 0xD02B, 0xB39F,\n\t0xD02C, 0xB3A0, 0xD02D, 0xC4FA, 0xD02E, 0xB441, 0xD02F, 0xB442,\n\t0xD030, 0xB443, 0xD031, 0xB444, 0xD032, 0xB445, 0xD033, 0xB446,\n\t0xD034, 0xC4FB, 0xD035, 0xC4FC, 0xD036, 0xB447, 0xD037, 0xB448,\n\t0xD038, 0xC4FD, 0xD039, 0xB449, 0xD03A, 0xB44A, 0xD03B, 0xB44B,\n\t0xD03C, 0xC4FE, 0xD03D, 0xB44C, 0xD03E, 0xB44D, 0xD03F, 0xB44E,\n\t0xD040, 0xB44F, 0xD041, 0xB450, 0xD042, 0xB451, 0xD043, 0xB452,\n\t0xD044, 0xC5A1, 0xD045, 0xC5A2, 0xD046, 0xB453, 0xD047, 0xC5A3,\n\t0xD048, 0xB454, 0xD049, 0xC5A4, 0xD04A, 0xB455, 0xD04B, 0xB456,\n\t0xD04C, 0xB457, 0xD04D, 0xB458, 0xD04E, 0xB459, 0xD04F, 0xB45A,\n\t0xD050, 0xC5A5, 0xD051, 0xB461, 0xD052, 0xB462, 0xD053, 0xB463,\n\t0xD054, 0xC5A6, 0xD055, 0xB464, 0xD056, 0xB465, 0xD057, 0xB466,\n\t0xD058, 0xC5A7, 0xD059, 0xB467, 0xD05A, 0xB468, 0xD05B, 0xB469,\n\t0xD05C, 0xB46A, 0xD05D, 0xB46B, 0xD05E, 0xB46C, 0xD05F, 0xB46D,\n\t0xD060, 0xC5A8, 0xD061, 0xB46E, 0xD062, 0xB46F, 0xD063, 0xB470,\n\t0xD064, 0xB471, 0xD065, 0xB472, 0xD066, 0xB473, 0xD067, 0xB474,\n\t0xD068, 0xB475, 0xD069, 0xB476, 0xD06A, 0xB477, 0xD06B, 0xB478,\n\t0xD06C, 0xC5A9, 0xD06D, 0xC5AA, 0xD06E, 0xB479, 0xD06F, 0xB47A,\n\t0xD070, 0xC5AB, 0xD071, 0xB481, 0xD072, 0xB482, 0xD073, 0xB483,\n\t0xD074, 0xC5AC, 0xD075, 0xB484, 0xD076, 0xB485, 0xD077, 0xB486,\n\t0xD078, 0xB487, 0xD079, 0xB488, 0xD07A, 0xB489, 0xD07B, 0xB48A,\n\t0xD07C, 0xC5AD, 0xD07D, 0xC5AE, 0xD07E, 0xB48B, 0xD07F, 0xB48C,\n\t0xD080, 0xB48D, 0xD081, 0xC5AF, 0xD082, 0xB48E, 0xD083, 0xB48F,\n\t0xD084, 0xB490, 0xD085, 0xB491, 0xD086, 0xB492, 0xD087, 0xB493,\n\t0xD088, 0xB494, 0xD089, 0xB495, 0xD08A, 0xB496, 0xD08B, 0xB497,\n\t0xD08C, 0xB498, 0xD08D, 0xB499, 0xD08E, 0xB49A, 0xD08F, 0xB49B,\n\t0xD090, 0xB49C, 0xD091, 0xB49D, 0xD092, 0xB49E, 0xD093, 0xB49F,\n\t0xD094, 0xB4A0, 0xD095, 0xB541, 0xD096, 0xB542, 0xD097, 0xB543,\n\t0xD098, 0xB544, 0xD099, 0xB545, 0xD09A, 0xB546, 0xD09B, 0xB547,\n\t0xD09C, 0xB548, 0xD09D, 0xB549, 0xD09E, 0xB54A, 0xD09F, 0xB54B,\n\t0xD0A0, 0xB54C, 0xD0A1, 0xB54D, 0xD0A2, 0xB54E, 0xD0A3, 0xB54F,\n\t0xD0A4, 0xC5B0, 0xD0A5, 0xC5B1, 0xD0A6, 0xB550, 0xD0A7, 0xB551,\n\t0xD0A8, 0xC5B2, 0xD0A9, 0xB552, 0xD0AA, 0xB553, 0xD0AB, 0xB554,\n\t0xD0AC, 0xC5B3, 0xD0AD, 0xB555, 0xD0AE, 0xB556, 0xD0AF, 0xB557,\n\t0xD0B0, 0xB558, 0xD0B1, 0xB559, 0xD0B2, 0xB55A, 0xD0B3, 0xB561,\n\t0xD0B4, 0xC5B4, 0xD0B5, 0xC5B5, 0xD0B6, 0xB562, 0xD0B7, 0xC5B6,\n\t0xD0B8, 0xB563, 0xD0B9, 0xC5B7, 0xD0BA, 0xB564, 0xD0BB, 0xB565,\n\t0xD0BC, 0xB566, 0xD0BD, 0xB567, 0xD0BE, 0xB568, 0xD0BF, 0xB569,\n\t0xD0C0, 0xC5B8, 0xD0C1, 0xC5B9, 0xD0C2, 0xB56A, 0xD0C3, 0xB56B,\n\t0xD0C4, 0xC5BA, 0xD0C5, 0xB56C, 0xD0C6, 0xB56D, 0xD0C7, 0xB56E,\n\t0xD0C8, 0xC5BB, 0xD0C9, 0xC5BC, 0xD0CA, 0xB56F, 0xD0CB, 0xB570,\n\t0xD0CC, 0xB571, 0xD0CD, 0xB572, 0xD0CE, 0xB573, 0xD0CF, 0xB574,\n\t0xD0D0, 0xC5BD, 0xD0D1, 0xC5BE, 0xD0D2, 0xB575, 0xD0D3, 0xC5BF,\n\t0xD0D4, 0xC5C0, 0xD0D5, 0xC5C1, 0xD0D6, 0xB576, 0xD0D7, 0xB577,\n\t0xD0D8, 0xB578, 0xD0D9, 0xB579, 0xD0DA, 0xB57A, 0xD0DB, 0xB581,\n\t0xD0DC, 0xC5C2, 0xD0DD, 0xC5C3, 0xD0DE, 0xB582, 0xD0DF, 0xB583,\n\t0xD0E0, 0xC5C4, 0xD0E1, 0xB584, 0xD0E2, 0xB585, 0xD0E3, 0xB586,\n\t0xD0E4, 0xC5C5, 0xD0E5, 0xB587, 0xD0E6, 0xB588, 0xD0E7, 0xB589,\n\t0xD0E8, 0xB58A, 0xD0E9, 0xB58B, 0xD0EA, 0xB58C, 0xD0EB, 0xB58D,\n\t0xD0EC, 0xC5C6, 0xD0ED, 0xC5C7, 0xD0EE, 0xB58E, 0xD0EF, 0xC5C8,\n\t0xD0F0, 0xC5C9, 0xD0F1, 0xC5CA, 0xD0F2, 0xB58F, 0xD0F3, 0xB590,\n\t0xD0F4, 0xB591, 0xD0F5, 0xB592, 0xD0F6, 0xB593, 0xD0F7, 0xB594,\n\t0xD0F8, 0xC5CB, 0xD0F9, 0xB595, 0xD0FA, 0xB596, 0xD0FB, 0xB597,\n\t0xD0FC, 0xB598, 0xD0FD, 0xB599, 0xD0FE, 0xB59A, 0xD0FF, 0xB59B,\n\t0xD100, 0xB59C, 0xD101, 0xB59D, 0xD102, 0xB59E, 0xD103, 0xB59F,\n\t0xD104, 0xB5A0, 0xD105, 0xB641, 0xD106, 0xB642, 0xD107, 0xB643,\n\t0xD108, 0xB644, 0xD109, 0xB645, 0xD10A, 0xB646, 0xD10B, 0xB647,\n\t0xD10C, 0xB648, 0xD10D, 0xC5CC, 0xD10E, 0xB649, 0xD10F, 0xB64A,\n\t0xD110, 0xB64B, 0xD111, 0xB64C, 0xD112, 0xB64D, 0xD113, 0xB64E,\n\t0xD114, 0xB64F, 0xD115, 0xB650, 0xD116, 0xB651, 0xD117, 0xB652,\n\t0xD118, 0xB653, 0xD119, 0xB654, 0xD11A, 0xB655, 0xD11B, 0xB656,\n\t0xD11C, 0xB657, 0xD11D, 0xB658, 0xD11E, 0xB659, 0xD11F, 0xB65A,\n\t0xD120, 0xB661, 0xD121, 0xB662, 0xD122, 0xB663, 0xD123, 0xB664,\n\t0xD124, 0xB665, 0xD125, 0xB666, 0xD126, 0xB667, 0xD127, 0xB668,\n\t0xD128, 0xB669, 0xD129, 0xB66A, 0xD12A, 0xB66B, 0xD12B, 0xB66C,\n\t0xD12C, 0xB66D, 0xD12D, 0xB66E, 0xD12E, 0xB66F, 0xD12F, 0xB670,\n\t0xD130, 0xC5CD, 0xD131, 0xC5CE, 0xD132, 0xB671, 0xD133, 0xB672,\n\t0xD134, 0xC5CF, 0xD135, 0xB673, 0xD136, 0xB674, 0xD137, 0xB675,\n\t0xD138, 0xC5D0, 0xD139, 0xB676, 0xD13A, 0xC5D1, 0xD13B, 0xB677,\n\t0xD13C, 0xB678, 0xD13D, 0xB679, 0xD13E, 0xB67A, 0xD13F, 0xB681,\n\t0xD140, 0xC5D2, 0xD141, 0xC5D3, 0xD142, 0xB682, 0xD143, 0xC5D4,\n\t0xD144, 0xC5D5, 0xD145, 0xC5D6, 0xD146, 0xB683, 0xD147, 0xB684,\n\t0xD148, 0xB685, 0xD149, 0xB686, 0xD14A, 0xB687, 0xD14B, 0xB688,\n\t0xD14C, 0xC5D7, 0xD14D, 0xC5D8, 0xD14E, 0xB689, 0xD14F, 0xB68A,\n\t0xD150, 0xC5D9, 0xD151, 0xB68B, 0xD152, 0xB68C, 0xD153, 0xB68D,\n\t0xD154, 0xC5DA, 0xD155, 0xB68E, 0xD156, 0xB68F, 0xD157, 0xB690,\n\t0xD158, 0xB691, 0xD159, 0xB692, 0xD15A, 0xB693, 0xD15B, 0xB694,\n\t0xD15C, 0xC5DB, 0xD15D, 0xC5DC, 0xD15E, 0xB695, 0xD15F, 0xC5DD,\n\t0xD160, 0xB696, 0xD161, 0xC5DE, 0xD162, 0xB697, 0xD163, 0xB698,\n\t0xD164, 0xB699, 0xD165, 0xB69A, 0xD166, 0xB69B, 0xD167, 0xB69C,\n\t0xD168, 0xC5DF, 0xD169, 0xB69D, 0xD16A, 0xB69E, 0xD16B, 0xB69F,\n\t0xD16C, 0xC5E0, 0xD16D, 0xB6A0, 0xD16E, 0xB741, 0xD16F, 0xB742,\n\t0xD170, 0xB743, 0xD171, 0xB744, 0xD172, 0xB745, 0xD173, 0xB746,\n\t0xD174, 0xB747, 0xD175, 0xB748, 0xD176, 0xB749, 0xD177, 0xB74A,\n\t0xD178, 0xB74B, 0xD179, 0xB74C, 0xD17A, 0xB74D, 0xD17B, 0xB74E,\n\t0xD17C, 0xC5E1, 0xD17D, 0xB74F, 0xD17E, 0xB750, 0xD17F, 0xB751,\n\t0xD180, 0xB752, 0xD181, 0xB753, 0xD182, 0xB754, 0xD183, 0xB755,\n\t0xD184, 0xC5E2, 0xD185, 0xB756, 0xD186, 0xB757, 0xD187, 0xB758,\n\t0xD188, 0xC5E3, 0xD189, 0xB759, 0xD18A, 0xB75A, 0xD18B, 0xB761,\n\t0xD18C, 0xB762, 0xD18D, 0xB763, 0xD18E, 0xB764, 0xD18F, 0xB765,\n\t0xD190, 0xB766, 0xD191, 0xB767, 0xD192, 0xB768, 0xD193, 0xB769,\n\t0xD194, 0xB76A, 0xD195, 0xB76B, 0xD196, 0xB76C, 0xD197, 0xB76D,\n\t0xD198, 0xB76E, 0xD199, 0xB76F, 0xD19A, 0xB770, 0xD19B, 0xB771,\n\t0xD19C, 0xB772, 0xD19D, 0xB773, 0xD19E, 0xB774, 0xD19F, 0xB775,\n\t0xD1A0, 0xC5E4, 0xD1A1, 0xC5E5, 0xD1A2, 0xB776, 0xD1A3, 0xB777,\n\t0xD1A4, 0xC5E6, 0xD1A5, 0xB778, 0xD1A6, 0xB779, 0xD1A7, 0xB77A,\n\t0xD1A8, 0xC5E7, 0xD1A9, 0xB781, 0xD1AA, 0xB782, 0xD1AB, 0xB783,\n\t0xD1AC, 0xB784, 0xD1AD, 0xB785, 0xD1AE, 0xB786, 0xD1AF, 0xB787,\n\t0xD1B0, 0xC5E8, 0xD1B1, 0xC5E9, 0xD1B2, 0xB788, 0xD1B3, 0xC5EA,\n\t0xD1B4, 0xB789, 0xD1B5, 0xC5EB, 0xD1B6, 0xB78A, 0xD1B7, 0xB78B,\n\t0xD1B8, 0xB78C, 0xD1B9, 0xB78D, 0xD1BA, 0xC5EC, 0xD1BB, 0xB78E,\n\t0xD1BC, 0xC5ED, 0xD1BD, 0xB78F, 0xD1BE, 0xB790, 0xD1BF, 0xB791,\n\t0xD1C0, 0xC5EE, 0xD1C1, 0xB792, 0xD1C2, 0xB793, 0xD1C3, 0xB794,\n\t0xD1C4, 0xB795, 0xD1C5, 0xB796, 0xD1C6, 0xB797, 0xD1C7, 0xB798,\n\t0xD1C8, 0xB799, 0xD1C9, 0xB79A, 0xD1CA, 0xB79B, 0xD1CB, 0xB79C,\n\t0xD1CC, 0xB79D, 0xD1CD, 0xB79E, 0xD1CE, 0xB79F, 0xD1CF, 0xB7A0,\n\t0xD1D0, 0xB841, 0xD1D1, 0xB842, 0xD1D2, 0xB843, 0xD1D3, 0xB844,\n\t0xD1D4, 0xB845, 0xD1D5, 0xB846, 0xD1D6, 0xB847, 0xD1D7, 0xB848,\n\t0xD1D8, 0xC5EF, 0xD1D9, 0xB849, 0xD1DA, 0xB84A, 0xD1DB, 0xB84B,\n\t0xD1DC, 0xB84C, 0xD1DD, 0xB84D, 0xD1DE, 0xB84E, 0xD1DF, 0xB84F,\n\t0xD1E0, 0xB850, 0xD1E1, 0xB851, 0xD1E2, 0xB852, 0xD1E3, 0xB853,\n\t0xD1E4, 0xB854, 0xD1E5, 0xB855, 0xD1E6, 0xB856, 0xD1E7, 0xB857,\n\t0xD1E8, 0xB858, 0xD1E9, 0xB859, 0xD1EA, 0xB85A, 0xD1EB, 0xB861,\n\t0xD1EC, 0xB862, 0xD1ED, 0xB863, 0xD1EE, 0xB864, 0xD1EF, 0xB865,\n\t0xD1F0, 0xB866, 0xD1F1, 0xB867, 0xD1F2, 0xB868, 0xD1F3, 0xB869,\n\t0xD1F4, 0xC5F0, 0xD1F5, 0xB86A, 0xD1F6, 0xB86B, 0xD1F7, 0xB86C,\n\t0xD1F8, 0xC5F1, 0xD1F9, 0xB86D, 0xD1FA, 0xB86E, 0xD1FB, 0xB86F,\n\t0xD1FC, 0xB870, 0xD1FD, 0xB871, 0xD1FE, 0xB872, 0xD1FF, 0xB873,\n\t0xD200, 0xB874, 0xD201, 0xB875, 0xD202, 0xB876, 0xD203, 0xB877,\n\t0xD204, 0xB878, 0xD205, 0xB879, 0xD206, 0xB87A, 0xD207, 0xC5F2,\n\t0xD208, 0xB881, 0xD209, 0xC5F3, 0xD20A, 0xB882, 0xD20B, 0xB883,\n\t0xD20C, 0xB884, 0xD20D, 0xB885, 0xD20E, 0xB886, 0xD20F, 0xB887,\n\t0xD210, 0xC5F4, 0xD211, 0xB888, 0xD212, 0xB889, 0xD213, 0xB88A,\n\t0xD214, 0xB88B, 0xD215, 0xB88C, 0xD216, 0xB88D, 0xD217, 0xB88E,\n\t0xD218, 0xB88F, 0xD219, 0xB890, 0xD21A, 0xB891, 0xD21B, 0xB892,\n\t0xD21C, 0xB893, 0xD21D, 0xB894, 0xD21E, 0xB895, 0xD21F, 0xB896,\n\t0xD220, 0xB897, 0xD221, 0xB898, 0xD222, 0xB899, 0xD223, 0xB89A,\n\t0xD224, 0xB89B, 0xD225, 0xB89C, 0xD226, 0xB89D, 0xD227, 0xB89E,\n\t0xD228, 0xB89F, 0xD229, 0xB8A0, 0xD22A, 0xB941, 0xD22B, 0xB942,\n\t0xD22C, 0xC5F5, 0xD22D, 0xC5F6, 0xD22E, 0xB943, 0xD22F, 0xB944,\n\t0xD230, 0xC5F7, 0xD231, 0xB945, 0xD232, 0xB946, 0xD233, 0xB947,\n\t0xD234, 0xC5F8, 0xD235, 0xB948, 0xD236, 0xB949, 0xD237, 0xB94A,\n\t0xD238, 0xB94B, 0xD239, 0xB94C, 0xD23A, 0xB94D, 0xD23B, 0xB94E,\n\t0xD23C, 0xC5F9, 0xD23D, 0xC5FA, 0xD23E, 0xB94F, 0xD23F, 0xC5FB,\n\t0xD240, 0xB950, 0xD241, 0xC5FC, 0xD242, 0xB951, 0xD243, 0xB952,\n\t0xD244, 0xB953, 0xD245, 0xB954, 0xD246, 0xB955, 0xD247, 0xB956,\n\t0xD248, 0xC5FD, 0xD249, 0xB957, 0xD24A, 0xB958, 0xD24B, 0xB959,\n\t0xD24C, 0xB95A, 0xD24D, 0xB961, 0xD24E, 0xB962, 0xD24F, 0xB963,\n\t0xD250, 0xB964, 0xD251, 0xB965, 0xD252, 0xB966, 0xD253, 0xB967,\n\t0xD254, 0xB968, 0xD255, 0xB969, 0xD256, 0xB96A, 0xD257, 0xB96B,\n\t0xD258, 0xB96C, 0xD259, 0xB96D, 0xD25A, 0xB96E, 0xD25B, 0xB96F,\n\t0xD25C, 0xC5FE, 0xD25D, 0xB970, 0xD25E, 0xB971, 0xD25F, 0xB972,\n\t0xD260, 0xB973, 0xD261, 0xB974, 0xD262, 0xB975, 0xD263, 0xB976,\n\t0xD264, 0xC6A1, 0xD265, 0xB977, 0xD266, 0xB978, 0xD267, 0xB979,\n\t0xD268, 0xB97A, 0xD269, 0xB981, 0xD26A, 0xB982, 0xD26B, 0xB983,\n\t0xD26C, 0xB984, 0xD26D, 0xB985, 0xD26E, 0xB986, 0xD26F, 0xB987,\n\t0xD270, 0xB988, 0xD271, 0xB989, 0xD272, 0xB98A, 0xD273, 0xB98B,\n\t0xD274, 0xB98C, 0xD275, 0xB98D, 0xD276, 0xB98E, 0xD277, 0xB98F,\n\t0xD278, 0xB990, 0xD279, 0xB991, 0xD27A, 0xB992, 0xD27B, 0xB993,\n\t0xD27C, 0xB994, 0xD27D, 0xB995, 0xD27E, 0xB996, 0xD27F, 0xB997,\n\t0xD280, 0xC6A2, 0xD281, 0xC6A3, 0xD282, 0xB998, 0xD283, 0xB999,\n\t0xD284, 0xC6A4, 0xD285, 0xB99A, 0xD286, 0xB99B, 0xD287, 0xB99C,\n\t0xD288, 0xC6A5, 0xD289, 0xB99D, 0xD28A, 0xB99E, 0xD28B, 0xB99F,\n\t0xD28C, 0xB9A0, 0xD28D, 0xBA41, 0xD28E, 0xBA42, 0xD28F, 0xBA43,\n\t0xD290, 0xC6A6, 0xD291, 0xC6A7, 0xD292, 0xBA44, 0xD293, 0xBA45,\n\t0xD294, 0xBA46, 0xD295, 0xC6A8, 0xD296, 0xBA47, 0xD297, 0xBA48,\n\t0xD298, 0xBA49, 0xD299, 0xBA4A, 0xD29A, 0xBA4B, 0xD29B, 0xBA4C,\n\t0xD29C, 0xC6A9, 0xD29D, 0xBA4D, 0xD29E, 0xBA4E, 0xD29F, 0xBA4F,\n\t0xD2A0, 0xC6AA, 0xD2A1, 0xBA50, 0xD2A2, 0xBA51, 0xD2A3, 0xBA52,\n\t0xD2A4, 0xC6AB, 0xD2A5, 0xBA53, 0xD2A6, 0xBA54, 0xD2A7, 0xBA55,\n\t0xD2A8, 0xBA56, 0xD2A9, 0xBA57, 0xD2AA, 0xBA58, 0xD2AB, 0xBA59,\n\t0xD2AC, 0xC6AC, 0xD2AD, 0xBA5A, 0xD2AE, 0xBA61, 0xD2AF, 0xBA62,\n\t0xD2B0, 0xBA63, 0xD2B1, 0xC6AD, 0xD2B2, 0xBA64, 0xD2B3, 0xBA65,\n\t0xD2B4, 0xBA66, 0xD2B5, 0xBA67, 0xD2B6, 0xBA68, 0xD2B7, 0xBA69,\n\t0xD2B8, 0xC6AE, 0xD2B9, 0xC6AF, 0xD2BA, 0xBA6A, 0xD2BB, 0xBA6B,\n\t0xD2BC, 0xC6B0, 0xD2BD, 0xBA6C, 0xD2BE, 0xBA6D, 0xD2BF, 0xC6B1,\n\t0xD2C0, 0xC6B2, 0xD2C1, 0xBA6E, 0xD2C2, 0xC6B3, 0xD2C3, 0xBA6F,\n\t0xD2C4, 0xBA70, 0xD2C5, 0xBA71, 0xD2C6, 0xBA72, 0xD2C7, 0xBA73,\n\t0xD2C8, 0xC6B4, 0xD2C9, 0xC6B5, 0xD2CA, 0xBA74, 0xD2CB, 0xC6B6,\n\t0xD2CC, 0xBA75, 0xD2CD, 0xBA76, 0xD2CE, 0xBA77, 0xD2CF, 0xBA78,\n\t0xD2D0, 0xBA79, 0xD2D1, 0xBA7A, 0xD2D2, 0xBA81, 0xD2D3, 0xBA82,\n\t0xD2D4, 0xC6B7, 0xD2D5, 0xBA83, 0xD2D6, 0xBA84, 0xD2D7, 0xBA85,\n\t0xD2D8, 0xC6B8, 0xD2D9, 0xBA86, 0xD2DA, 0xBA87, 0xD2DB, 0xBA88,\n\t0xD2DC, 0xC6B9, 0xD2DD, 0xBA89, 0xD2DE, 0xBA8A, 0xD2DF, 0xBA8B,\n\t0xD2E0, 0xBA8C, 0xD2E1, 0xBA8D, 0xD2E2, 0xBA8E, 0xD2E3, 0xBA8F,\n\t0xD2E4, 0xC6BA, 0xD2E5, 0xC6BB, 0xD2E6, 0xBA90, 0xD2E7, 0xBA91,\n\t0xD2E8, 0xBA92, 0xD2E9, 0xBA93, 0xD2EA, 0xBA94, 0xD2EB, 0xBA95,\n\t0xD2EC, 0xBA96, 0xD2ED, 0xBA97, 0xD2EE, 0xBA98, 0xD2EF, 0xBA99,\n\t0xD2F0, 0xC6BC, 0xD2F1, 0xC6BD, 0xD2F2, 0xBA9A, 0xD2F3, 0xBA9B,\n\t0xD2F4, 0xC6BE, 0xD2F5, 0xBA9C, 0xD2F6, 0xBA9D, 0xD2F7, 0xBA9E,\n\t0xD2F8, 0xC6BF, 0xD2F9, 0xBA9F, 0xD2FA, 0xBAA0, 0xD2FB, 0xBB41,\n\t0xD2FC, 0xBB42, 0xD2FD, 0xBB43, 0xD2FE, 0xBB44, 0xD2FF, 0xBB45,\n\t0xD300, 0xC6C0, 0xD301, 0xC6C1, 0xD302, 0xBB46, 0xD303, 0xC6C2,\n\t0xD304, 0xBB47, 0xD305, 0xC6C3, 0xD306, 0xBB48, 0xD307, 0xBB49,\n\t0xD308, 0xBB4A, 0xD309, 0xBB4B, 0xD30A, 0xBB4C, 0xD30B, 0xBB4D,\n\t0xD30C, 0xC6C4, 0xD30D, 0xC6C5, 0xD30E, 0xC6C6, 0xD30F, 0xBB4E,\n\t0xD310, 0xC6C7, 0xD311, 0xBB4F, 0xD312, 0xBB50, 0xD313, 0xBB51,\n\t0xD314, 0xC6C8, 0xD315, 0xBB52, 0xD316, 0xC6C9, 0xD317, 0xBB53,\n\t0xD318, 0xBB54, 0xD319, 0xBB55, 0xD31A, 0xBB56, 0xD31B, 0xBB57,\n\t0xD31C, 0xC6CA, 0xD31D, 0xC6CB, 0xD31E, 0xBB58, 0xD31F, 0xC6CC,\n\t0xD320, 0xC6CD, 0xD321, 0xC6CE, 0xD322, 0xBB59, 0xD323, 0xBB5A,\n\t0xD324, 0xBB61, 0xD325, 0xC6CF, 0xD326, 0xBB62, 0xD327, 0xBB63,\n\t0xD328, 0xC6D0, 0xD329, 0xC6D1, 0xD32A, 0xBB64, 0xD32B, 0xBB65,\n\t0xD32C, 0xC6D2, 0xD32D, 0xBB66, 0xD32E, 0xBB67, 0xD32F, 0xBB68,\n\t0xD330, 0xC6D3, 0xD331, 0xBB69, 0xD332, 0xBB6A, 0xD333, 0xBB6B,\n\t0xD334, 0xBB6C, 0xD335, 0xBB6D, 0xD336, 0xBB6E, 0xD337, 0xBB6F,\n\t0xD338, 0xC6D4, 0xD339, 0xC6D5, 0xD33A, 0xBB70, 0xD33B, 0xC6D6,\n\t0xD33C, 0xC6D7, 0xD33D, 0xC6D8, 0xD33E, 0xBB71, 0xD33F, 0xBB72,\n\t0xD340, 0xBB73, 0xD341, 0xBB74, 0xD342, 0xBB75, 0xD343, 0xBB76,\n\t0xD344, 0xC6D9, 0xD345, 0xC6DA, 0xD346, 0xBB77, 0xD347, 0xBB78,\n\t0xD348, 0xBB79, 0xD349, 0xBB7A, 0xD34A, 0xBB81, 0xD34B, 0xBB82,\n\t0xD34C, 0xBB83, 0xD34D, 0xBB84, 0xD34E, 0xBB85, 0xD34F, 0xBB86,\n\t0xD350, 0xBB87, 0xD351, 0xBB88, 0xD352, 0xBB89, 0xD353, 0xBB8A,\n\t0xD354, 0xBB8B, 0xD355, 0xBB8C, 0xD356, 0xBB8D, 0xD357, 0xBB8E,\n\t0xD358, 0xBB8F, 0xD359, 0xBB90, 0xD35A, 0xBB91, 0xD35B, 0xBB92,\n\t0xD35C, 0xBB93, 0xD35D, 0xBB94, 0xD35E, 0xBB95, 0xD35F, 0xBB96,\n\t0xD360, 0xBB97, 0xD361, 0xBB98, 0xD362, 0xBB99, 0xD363, 0xBB9A,\n\t0xD364, 0xBB9B, 0xD365, 0xBB9C, 0xD366, 0xBB9D, 0xD367, 0xBB9E,\n\t0xD368, 0xBB9F, 0xD369, 0xBBA0, 0xD36A, 0xBC41, 0xD36B, 0xBC42,\n\t0xD36C, 0xBC43, 0xD36D, 0xBC44, 0xD36E, 0xBC45, 0xD36F, 0xBC46,\n\t0xD370, 0xBC47, 0xD371, 0xBC48, 0xD372, 0xBC49, 0xD373, 0xBC4A,\n\t0xD374, 0xBC4B, 0xD375, 0xBC4C, 0xD376, 0xBC4D, 0xD377, 0xBC4E,\n\t0xD378, 0xBC4F, 0xD379, 0xBC50, 0xD37A, 0xBC51, 0xD37B, 0xBC52,\n\t0xD37C, 0xC6DB, 0xD37D, 0xC6DC, 0xD37E, 0xBC53, 0xD37F, 0xBC54,\n\t0xD380, 0xC6DD, 0xD381, 0xBC55, 0xD382, 0xBC56, 0xD383, 0xBC57,\n\t0xD384, 0xC6DE, 0xD385, 0xBC58, 0xD386, 0xBC59, 0xD387, 0xBC5A,\n\t0xD388, 0xBC61, 0xD389, 0xBC62, 0xD38A, 0xBC63, 0xD38B, 0xBC64,\n\t0xD38C, 0xC6DF, 0xD38D, 0xC6E0, 0xD38E, 0xBC65, 0xD38F, 0xC6E1,\n\t0xD390, 0xC6E2, 0xD391, 0xC6E3, 0xD392, 0xBC66, 0xD393, 0xBC67,\n\t0xD394, 0xBC68, 0xD395, 0xBC69, 0xD396, 0xBC6A, 0xD397, 0xBC6B,\n\t0xD398, 0xC6E4, 0xD399, 0xC6E5, 0xD39A, 0xBC6C, 0xD39B, 0xBC6D,\n\t0xD39C, 0xC6E6, 0xD39D, 0xBC6E, 0xD39E, 0xBC6F, 0xD39F, 0xBC70,\n\t0xD3A0, 0xC6E7, 0xD3A1, 0xBC71, 0xD3A2, 0xBC72, 0xD3A3, 0xBC73,\n\t0xD3A4, 0xBC74, 0xD3A5, 0xBC75, 0xD3A6, 0xBC76, 0xD3A7, 0xBC77,\n\t0xD3A8, 0xC6E8, 0xD3A9, 0xC6E9, 0xD3AA, 0xBC78, 0xD3AB, 0xC6EA,\n\t0xD3AC, 0xBC79, 0xD3AD, 0xC6EB, 0xD3AE, 0xBC7A, 0xD3AF, 0xBC81,\n\t0xD3B0, 0xBC82, 0xD3B1, 0xBC83, 0xD3B2, 0xBC84, 0xD3B3, 0xBC85,\n\t0xD3B4, 0xC6EC, 0xD3B5, 0xBC86, 0xD3B6, 0xBC87, 0xD3B7, 0xBC88,\n\t0xD3B8, 0xC6ED, 0xD3B9, 0xBC89, 0xD3BA, 0xBC8A, 0xD3BB, 0xBC8B,\n\t0xD3BC, 0xC6EE, 0xD3BD, 0xBC8C, 0xD3BE, 0xBC8D, 0xD3BF, 0xBC8E,\n\t0xD3C0, 0xBC8F, 0xD3C1, 0xBC90, 0xD3C2, 0xBC91, 0xD3C3, 0xBC92,\n\t0xD3C4, 0xC6EF, 0xD3C5, 0xC6F0, 0xD3C6, 0xBC93, 0xD3C7, 0xBC94,\n\t0xD3C8, 0xC6F1, 0xD3C9, 0xC6F2, 0xD3CA, 0xBC95, 0xD3CB, 0xBC96,\n\t0xD3CC, 0xBC97, 0xD3CD, 0xBC98, 0xD3CE, 0xBC99, 0xD3CF, 0xBC9A,\n\t0xD3D0, 0xC6F3, 0xD3D1, 0xBC9B, 0xD3D2, 0xBC9C, 0xD3D3, 0xBC9D,\n\t0xD3D4, 0xBC9E, 0xD3D5, 0xBC9F, 0xD3D6, 0xBCA0, 0xD3D7, 0xBD41,\n\t0xD3D8, 0xC6F4, 0xD3D9, 0xBD42, 0xD3DA, 0xBD43, 0xD3DB, 0xBD44,\n\t0xD3DC, 0xBD45, 0xD3DD, 0xBD46, 0xD3DE, 0xBD47, 0xD3DF, 0xBD48,\n\t0xD3E0, 0xBD49, 0xD3E1, 0xC6F5, 0xD3E2, 0xBD4A, 0xD3E3, 0xC6F6,\n\t0xD3E4, 0xBD4B, 0xD3E5, 0xBD4C, 0xD3E6, 0xBD4D, 0xD3E7, 0xBD4E,\n\t0xD3E8, 0xBD4F, 0xD3E9, 0xBD50, 0xD3EA, 0xBD51, 0xD3EB, 0xBD52,\n\t0xD3EC, 0xC6F7, 0xD3ED, 0xC6F8, 0xD3EE, 0xBD53, 0xD3EF, 0xBD54,\n\t0xD3F0, 0xC6F9, 0xD3F1, 0xBD55, 0xD3F2, 0xBD56, 0xD3F3, 0xBD57,\n\t0xD3F4, 0xC6FA, 0xD3F5, 0xBD58, 0xD3F6, 0xBD59, 0xD3F7, 0xBD5A,\n\t0xD3F8, 0xBD61, 0xD3F9, 0xBD62, 0xD3FA, 0xBD63, 0xD3FB, 0xBD64,\n\t0xD3FC, 0xC6FB, 0xD3FD, 0xC6FC, 0xD3FE, 0xBD65, 0xD3FF, 0xC6FD,\n\t0xD400, 0xBD66, 0xD401, 0xC6FE, 0xD402, 0xBD67, 0xD403, 0xBD68,\n\t0xD404, 0xBD69, 0xD405, 0xBD6A, 0xD406, 0xBD6B, 0xD407, 0xBD6C,\n\t0xD408, 0xC7A1, 0xD409, 0xBD6D, 0xD40A, 0xBD6E, 0xD40B, 0xBD6F,\n\t0xD40C, 0xBD70, 0xD40D, 0xBD71, 0xD40E, 0xBD72, 0xD40F, 0xBD73,\n\t0xD410, 0xBD74, 0xD411, 0xBD75, 0xD412, 0xBD76, 0xD413, 0xBD77,\n\t0xD414, 0xBD78, 0xD415, 0xBD79, 0xD416, 0xBD7A, 0xD417, 0xBD81,\n\t0xD418, 0xBD82, 0xD419, 0xBD83, 0xD41A, 0xBD84, 0xD41B, 0xBD85,\n\t0xD41C, 0xBD86, 0xD41D, 0xC7A2, 0xD41E, 0xBD87, 0xD41F, 0xBD88,\n\t0xD420, 0xBD89, 0xD421, 0xBD8A, 0xD422, 0xBD8B, 0xD423, 0xBD8C,\n\t0xD424, 0xBD8D, 0xD425, 0xBD8E, 0xD426, 0xBD8F, 0xD427, 0xBD90,\n\t0xD428, 0xBD91, 0xD429, 0xBD92, 0xD42A, 0xBD93, 0xD42B, 0xBD94,\n\t0xD42C, 0xBD95, 0xD42D, 0xBD96, 0xD42E, 0xBD97, 0xD42F, 0xBD98,\n\t0xD430, 0xBD99, 0xD431, 0xBD9A, 0xD432, 0xBD9B, 0xD433, 0xBD9C,\n\t0xD434, 0xBD9D, 0xD435, 0xBD9E, 0xD436, 0xBD9F, 0xD437, 0xBDA0,\n\t0xD438, 0xBE41, 0xD439, 0xBE42, 0xD43A, 0xBE43, 0xD43B, 0xBE44,\n\t0xD43C, 0xBE45, 0xD43D, 0xBE46, 0xD43E, 0xBE47, 0xD43F, 0xBE48,\n\t0xD440, 0xC7A3, 0xD441, 0xBE49, 0xD442, 0xBE4A, 0xD443, 0xBE4B,\n\t0xD444, 0xC7A4, 0xD445, 0xBE4C, 0xD446, 0xBE4D, 0xD447, 0xBE4E,\n\t0xD448, 0xBE4F, 0xD449, 0xBE50, 0xD44A, 0xBE51, 0xD44B, 0xBE52,\n\t0xD44C, 0xBE53, 0xD44D, 0xBE54, 0xD44E, 0xBE55, 0xD44F, 0xBE56,\n\t0xD450, 0xBE57, 0xD451, 0xBE58, 0xD452, 0xBE59, 0xD453, 0xBE5A,\n\t0xD454, 0xBE61, 0xD455, 0xBE62, 0xD456, 0xBE63, 0xD457, 0xBE64,\n\t0xD458, 0xBE65, 0xD459, 0xBE66, 0xD45A, 0xBE67, 0xD45B, 0xBE68,\n\t0xD45C, 0xC7A5, 0xD45D, 0xBE69, 0xD45E, 0xBE6A, 0xD45F, 0xBE6B,\n\t0xD460, 0xC7A6, 0xD461, 0xBE6C, 0xD462, 0xBE6D, 0xD463, 0xBE6E,\n\t0xD464, 0xC7A7, 0xD465, 0xBE6F, 0xD466, 0xBE70, 0xD467, 0xBE71,\n\t0xD468, 0xBE72, 0xD469, 0xBE73, 0xD46A, 0xBE74, 0xD46B, 0xBE75,\n\t0xD46C, 0xBE76, 0xD46D, 0xC7A8, 0xD46E, 0xBE77, 0xD46F, 0xC7A9,\n\t0xD470, 0xBE78, 0xD471, 0xBE79, 0xD472, 0xBE7A, 0xD473, 0xBE81,\n\t0xD474, 0xBE82, 0xD475, 0xBE83, 0xD476, 0xBE84, 0xD477, 0xBE85,\n\t0xD478, 0xC7AA, 0xD479, 0xC7AB, 0xD47A, 0xBE86, 0xD47B, 0xBE87,\n\t0xD47C, 0xC7AC, 0xD47D, 0xBE88, 0xD47E, 0xBE89, 0xD47F, 0xC7AD,\n\t0xD480, 0xC7AE, 0xD481, 0xBE8A, 0xD482, 0xC7AF, 0xD483, 0xBE8B,\n\t0xD484, 0xBE8C, 0xD485, 0xBE8D, 0xD486, 0xBE8E, 0xD487, 0xBE8F,\n\t0xD488, 0xC7B0, 0xD489, 0xC7B1, 0xD48A, 0xBE90, 0xD48B, 0xC7B2,\n\t0xD48C, 0xBE91, 0xD48D, 0xC7B3, 0xD48E, 0xBE92, 0xD48F, 0xBE93,\n\t0xD490, 0xBE94, 0xD491, 0xBE95, 0xD492, 0xBE96, 0xD493, 0xBE97,\n\t0xD494, 0xC7B4, 0xD495, 0xBE98, 0xD496, 0xBE99, 0xD497, 0xBE9A,\n\t0xD498, 0xBE9B, 0xD499, 0xBE9C, 0xD49A, 0xBE9D, 0xD49B, 0xBE9E,\n\t0xD49C, 0xBE9F, 0xD49D, 0xBEA0, 0xD49E, 0xBF41, 0xD49F, 0xBF42,\n\t0xD4A0, 0xBF43, 0xD4A1, 0xBF44, 0xD4A2, 0xBF45, 0xD4A3, 0xBF46,\n\t0xD4A4, 0xBF47, 0xD4A5, 0xBF48, 0xD4A6, 0xBF49, 0xD4A7, 0xBF4A,\n\t0xD4A8, 0xBF4B, 0xD4A9, 0xC7B5, 0xD4AA, 0xBF4C, 0xD4AB, 0xBF4D,\n\t0xD4AC, 0xBF4E, 0xD4AD, 0xBF4F, 0xD4AE, 0xBF50, 0xD4AF, 0xBF51,\n\t0xD4B0, 0xBF52, 0xD4B1, 0xBF53, 0xD4B2, 0xBF54, 0xD4B3, 0xBF55,\n\t0xD4B4, 0xBF56, 0xD4B5, 0xBF57, 0xD4B6, 0xBF58, 0xD4B7, 0xBF59,\n\t0xD4B8, 0xBF5A, 0xD4B9, 0xBF61, 0xD4BA, 0xBF62, 0xD4BB, 0xBF63,\n\t0xD4BC, 0xBF64, 0xD4BD, 0xBF65, 0xD4BE, 0xBF66, 0xD4BF, 0xBF67,\n\t0xD4C0, 0xBF68, 0xD4C1, 0xBF69, 0xD4C2, 0xBF6A, 0xD4C3, 0xBF6B,\n\t0xD4C4, 0xBF6C, 0xD4C5, 0xBF6D, 0xD4C6, 0xBF6E, 0xD4C7, 0xBF6F,\n\t0xD4C8, 0xBF70, 0xD4C9, 0xBF71, 0xD4CA, 0xBF72, 0xD4CB, 0xBF73,\n\t0xD4CC, 0xC7B6, 0xD4CD, 0xBF74, 0xD4CE, 0xBF75, 0xD4CF, 0xBF76,\n\t0xD4D0, 0xC7B7, 0xD4D1, 0xBF77, 0xD4D2, 0xBF78, 0xD4D3, 0xBF79,\n\t0xD4D4, 0xC7B8, 0xD4D5, 0xBF7A, 0xD4D6, 0xBF81, 0xD4D7, 0xBF82,\n\t0xD4D8, 0xBF83, 0xD4D9, 0xBF84, 0xD4DA, 0xBF85, 0xD4DB, 0xBF86,\n\t0xD4DC, 0xC7B9, 0xD4DD, 0xBF87, 0xD4DE, 0xBF88, 0xD4DF, 0xC7BA,\n\t0xD4E0, 0xBF89, 0xD4E1, 0xBF8A, 0xD4E2, 0xBF8B, 0xD4E3, 0xBF8C,\n\t0xD4E4, 0xBF8D, 0xD4E5, 0xBF8E, 0xD4E6, 0xBF8F, 0xD4E7, 0xBF90,\n\t0xD4E8, 0xC7BB, 0xD4E9, 0xBF91, 0xD4EA, 0xBF92, 0xD4EB, 0xBF93,\n\t0xD4EC, 0xC7BC, 0xD4ED, 0xBF94, 0xD4EE, 0xBF95, 0xD4EF, 0xBF96,\n\t0xD4F0, 0xC7BD, 0xD4F1, 0xBF97, 0xD4F2, 0xBF98, 0xD4F3, 0xBF99,\n\t0xD4F4, 0xBF9A, 0xD4F5, 0xBF9B, 0xD4F6, 0xBF9C, 0xD4F7, 0xBF9D,\n\t0xD4F8, 0xC7BE, 0xD4F9, 0xBF9E, 0xD4FA, 0xBF9F, 0xD4FB, 0xC7BF,\n\t0xD4FC, 0xBFA0, 0xD4FD, 0xC7C0, 0xD4FE, 0xC041, 0xD4FF, 0xC042,\n\t0xD500, 0xC043, 0xD501, 0xC044, 0xD502, 0xC045, 0xD503, 0xC046,\n\t0xD504, 0xC7C1, 0xD505, 0xC047, 0xD506, 0xC048, 0xD507, 0xC049,\n\t0xD508, 0xC7C2, 0xD509, 0xC04A, 0xD50A, 0xC04B, 0xD50B, 0xC04C,\n\t0xD50C, 0xC7C3, 0xD50D, 0xC04D, 0xD50E, 0xC04E, 0xD50F, 0xC04F,\n\t0xD510, 0xC050, 0xD511, 0xC051, 0xD512, 0xC052, 0xD513, 0xC053,\n\t0xD514, 0xC7C4, 0xD515, 0xC7C5, 0xD516, 0xC054, 0xD517, 0xC7C6,\n\t0xD518, 0xC055, 0xD519, 0xC056, 0xD51A, 0xC057, 0xD51B, 0xC058,\n\t0xD51C, 0xC059, 0xD51D, 0xC05A, 0xD51E, 0xC061, 0xD51F, 0xC062,\n\t0xD520, 0xC063, 0xD521, 0xC064, 0xD522, 0xC065, 0xD523, 0xC066,\n\t0xD524, 0xC067, 0xD525, 0xC068, 0xD526, 0xC069, 0xD527, 0xC06A,\n\t0xD528, 0xC06B, 0xD529, 0xC06C, 0xD52A, 0xC06D, 0xD52B, 0xC06E,\n\t0xD52C, 0xC06F, 0xD52D, 0xC070, 0xD52E, 0xC071, 0xD52F, 0xC072,\n\t0xD530, 0xC073, 0xD531, 0xC074, 0xD532, 0xC075, 0xD533, 0xC076,\n\t0xD534, 0xC077, 0xD535, 0xC078, 0xD536, 0xC079, 0xD537, 0xC07A,\n\t0xD538, 0xC081, 0xD539, 0xC082, 0xD53A, 0xC083, 0xD53B, 0xC084,\n\t0xD53C, 0xC7C7, 0xD53D, 0xC7C8, 0xD53E, 0xC085, 0xD53F, 0xC086,\n\t0xD540, 0xC7C9, 0xD541, 0xC087, 0xD542, 0xC088, 0xD543, 0xC089,\n\t0xD544, 0xC7CA, 0xD545, 0xC08A, 0xD546, 0xC08B, 0xD547, 0xC08C,\n\t0xD548, 0xC08D, 0xD549, 0xC08E, 0xD54A, 0xC08F, 0xD54B, 0xC090,\n\t0xD54C, 0xC7CB, 0xD54D, 0xC7CC, 0xD54E, 0xC091, 0xD54F, 0xC7CD,\n\t0xD550, 0xC092, 0xD551, 0xC7CE, 0xD552, 0xC093, 0xD553, 0xC094,\n\t0xD554, 0xC095, 0xD555, 0xC096, 0xD556, 0xC097, 0xD557, 0xC098,\n\t0xD558, 0xC7CF, 0xD559, 0xC7D0, 0xD55A, 0xC099, 0xD55B, 0xC09A,\n\t0xD55C, 0xC7D1, 0xD55D, 0xC09B, 0xD55E, 0xC09C, 0xD55F, 0xC09D,\n\t0xD560, 0xC7D2, 0xD561, 0xC09E, 0xD562, 0xC09F, 0xD563, 0xC0A0,\n\t0xD564, 0xC141, 0xD565, 0xC7D3, 0xD566, 0xC142, 0xD567, 0xC143,\n\t0xD568, 0xC7D4, 0xD569, 0xC7D5, 0xD56A, 0xC144, 0xD56B, 0xC7D6,\n\t0xD56C, 0xC145, 0xD56D, 0xC7D7, 0xD56E, 0xC146, 0xD56F, 0xC147,\n\t0xD570, 0xC148, 0xD571, 0xC149, 0xD572, 0xC14A, 0xD573, 0xC14B,\n\t0xD574, 0xC7D8, 0xD575, 0xC7D9, 0xD576, 0xC14C, 0xD577, 0xC14D,\n\t0xD578, 0xC7DA, 0xD579, 0xC14E, 0xD57A, 0xC14F, 0xD57B, 0xC150,\n\t0xD57C, 0xC7DB, 0xD57D, 0xC151, 0xD57E, 0xC152, 0xD57F, 0xC153,\n\t0xD580, 0xC154, 0xD581, 0xC155, 0xD582, 0xC156, 0xD583, 0xC157,\n\t0xD584, 0xC7DC, 0xD585, 0xC7DD, 0xD586, 0xC158, 0xD587, 0xC7DE,\n\t0xD588, 0xC7DF, 0xD589, 0xC7E0, 0xD58A, 0xC159, 0xD58B, 0xC15A,\n\t0xD58C, 0xC161, 0xD58D, 0xC162, 0xD58E, 0xC163, 0xD58F, 0xC164,\n\t0xD590, 0xC7E1, 0xD591, 0xC165, 0xD592, 0xC166, 0xD593, 0xC167,\n\t0xD594, 0xC168, 0xD595, 0xC169, 0xD596, 0xC16A, 0xD597, 0xC16B,\n\t0xD598, 0xC16C, 0xD599, 0xC16D, 0xD59A, 0xC16E, 0xD59B, 0xC16F,\n\t0xD59C, 0xC170, 0xD59D, 0xC171, 0xD59E, 0xC172, 0xD59F, 0xC173,\n\t0xD5A0, 0xC174, 0xD5A1, 0xC175, 0xD5A2, 0xC176, 0xD5A3, 0xC177,\n\t0xD5A4, 0xC178, 0xD5A5, 0xC7E2, 0xD5A6, 0xC179, 0xD5A7, 0xC17A,\n\t0xD5A8, 0xC181, 0xD5A9, 0xC182, 0xD5AA, 0xC183, 0xD5AB, 0xC184,\n\t0xD5AC, 0xC185, 0xD5AD, 0xC186, 0xD5AE, 0xC187, 0xD5AF, 0xC188,\n\t0xD5B0, 0xC189, 0xD5B1, 0xC18A, 0xD5B2, 0xC18B, 0xD5B3, 0xC18C,\n\t0xD5B4, 0xC18D, 0xD5B5, 0xC18E, 0xD5B6, 0xC18F, 0xD5B7, 0xC190,\n\t0xD5B8, 0xC191, 0xD5B9, 0xC192, 0xD5BA, 0xC193, 0xD5BB, 0xC194,\n\t0xD5BC, 0xC195, 0xD5BD, 0xC196, 0xD5BE, 0xC197, 0xD5BF, 0xC198,\n\t0xD5C0, 0xC199, 0xD5C1, 0xC19A, 0xD5C2, 0xC19B, 0xD5C3, 0xC19C,\n\t0xD5C4, 0xC19D, 0xD5C5, 0xC19E, 0xD5C6, 0xC19F, 0xD5C7, 0xC1A0,\n\t0xD5C8, 0xC7E3, 0xD5C9, 0xC7E4, 0xD5CA, 0xC241, 0xD5CB, 0xC242,\n\t0xD5CC, 0xC7E5, 0xD5CD, 0xC243, 0xD5CE, 0xC244, 0xD5CF, 0xC245,\n\t0xD5D0, 0xC7E6, 0xD5D1, 0xC246, 0xD5D2, 0xC7E7, 0xD5D3, 0xC247,\n\t0xD5D4, 0xC248, 0xD5D5, 0xC249, 0xD5D6, 0xC24A, 0xD5D7, 0xC24B,\n\t0xD5D8, 0xC7E8, 0xD5D9, 0xC7E9, 0xD5DA, 0xC24C, 0xD5DB, 0xC7EA,\n\t0xD5DC, 0xC24D, 0xD5DD, 0xC7EB, 0xD5DE, 0xC24E, 0xD5DF, 0xC24F,\n\t0xD5E0, 0xC250, 0xD5E1, 0xC251, 0xD5E2, 0xC252, 0xD5E3, 0xC253,\n\t0xD5E4, 0xC7EC, 0xD5E5, 0xC7ED, 0xD5E6, 0xC254, 0xD5E7, 0xC255,\n\t0xD5E8, 0xC7EE, 0xD5E9, 0xC256, 0xD5EA, 0xC257, 0xD5EB, 0xC258,\n\t0xD5EC, 0xC7EF, 0xD5ED, 0xC259, 0xD5EE, 0xC25A, 0xD5EF, 0xC261,\n\t0xD5F0, 0xC262, 0xD5F1, 0xC263, 0xD5F2, 0xC264, 0xD5F3, 0xC265,\n\t0xD5F4, 0xC7F0, 0xD5F5, 0xC7F1, 0xD5F6, 0xC266, 0xD5F7, 0xC7F2,\n\t0xD5F8, 0xC267, 0xD5F9, 0xC7F3, 0xD5FA, 0xC268, 0xD5FB, 0xC269,\n\t0xD5FC, 0xC26A, 0xD5FD, 0xC26B, 0xD5FE, 0xC26C, 0xD5FF, 0xC26D,\n\t0xD600, 0xC7F4, 0xD601, 0xC7F5, 0xD602, 0xC26E, 0xD603, 0xC26F,\n\t0xD604, 0xC7F6, 0xD605, 0xC270, 0xD606, 0xC271, 0xD607, 0xC272,\n\t0xD608, 0xC7F7, 0xD609, 0xC273, 0xD60A, 0xC274, 0xD60B, 0xC275,\n\t0xD60C, 0xC276, 0xD60D, 0xC277, 0xD60E, 0xC278, 0xD60F, 0xC279,\n\t0xD610, 0xC7F8, 0xD611, 0xC7F9, 0xD612, 0xC27A, 0xD613, 0xC7FA,\n\t0xD614, 0xC7FB, 0xD615, 0xC7FC, 0xD616, 0xC281, 0xD617, 0xC282,\n\t0xD618, 0xC283, 0xD619, 0xC284, 0xD61A, 0xC285, 0xD61B, 0xC286,\n\t0xD61C, 0xC7FD, 0xD61D, 0xC287, 0xD61E, 0xC288, 0xD61F, 0xC289,\n\t0xD620, 0xC7FE, 0xD621, 0xC28A, 0xD622, 0xC28B, 0xD623, 0xC28C,\n\t0xD624, 0xC8A1, 0xD625, 0xC28D, 0xD626, 0xC28E, 0xD627, 0xC28F,\n\t0xD628, 0xC290, 0xD629, 0xC291, 0xD62A, 0xC292, 0xD62B, 0xC293,\n\t0xD62C, 0xC294, 0xD62D, 0xC8A2, 0xD62E, 0xC295, 0xD62F, 0xC296,\n\t0xD630, 0xC297, 0xD631, 0xC298, 0xD632, 0xC299, 0xD633, 0xC29A,\n\t0xD634, 0xC29B, 0xD635, 0xC29C, 0xD636, 0xC29D, 0xD637, 0xC29E,\n\t0xD638, 0xC8A3, 0xD639, 0xC8A4, 0xD63A, 0xC29F, 0xD63B, 0xC2A0,\n\t0xD63C, 0xC8A5, 0xD63D, 0xC341, 0xD63E, 0xC342, 0xD63F, 0xC343,\n\t0xD640, 0xC8A6, 0xD641, 0xC344, 0xD642, 0xC345, 0xD643, 0xC346,\n\t0xD644, 0xC347, 0xD645, 0xC8A7, 0xD646, 0xC348, 0xD647, 0xC349,\n\t0xD648, 0xC8A8, 0xD649, 0xC8A9, 0xD64A, 0xC34A, 0xD64B, 0xC8AA,\n\t0xD64C, 0xC34B, 0xD64D, 0xC8AB, 0xD64E, 0xC34C, 0xD64F, 0xC34D,\n\t0xD650, 0xC34E, 0xD651, 0xC8AC, 0xD652, 0xC34F, 0xD653, 0xC350,\n\t0xD654, 0xC8AD, 0xD655, 0xC8AE, 0xD656, 0xC351, 0xD657, 0xC352,\n\t0xD658, 0xC8AF, 0xD659, 0xC353, 0xD65A, 0xC354, 0xD65B, 0xC355,\n\t0xD65C, 0xC8B0, 0xD65D, 0xC356, 0xD65E, 0xC357, 0xD65F, 0xC358,\n\t0xD660, 0xC359, 0xD661, 0xC35A, 0xD662, 0xC361, 0xD663, 0xC362,\n\t0xD664, 0xC363, 0xD665, 0xC364, 0xD666, 0xC365, 0xD667, 0xC8B1,\n\t0xD668, 0xC366, 0xD669, 0xC8B2, 0xD66A, 0xC367, 0xD66B, 0xC368,\n\t0xD66C, 0xC369, 0xD66D, 0xC36A, 0xD66E, 0xC36B, 0xD66F, 0xC36C,\n\t0xD670, 0xC8B3, 0xD671, 0xC8B4, 0xD672, 0xC36D, 0xD673, 0xC36E,\n\t0xD674, 0xC8B5, 0xD675, 0xC36F, 0xD676, 0xC370, 0xD677, 0xC371,\n\t0xD678, 0xC372, 0xD679, 0xC373, 0xD67A, 0xC374, 0xD67B, 0xC375,\n\t0xD67C, 0xC376, 0xD67D, 0xC377, 0xD67E, 0xC378, 0xD67F, 0xC379,\n\t0xD680, 0xC37A, 0xD681, 0xC381, 0xD682, 0xC382, 0xD683, 0xC8B6,\n\t0xD684, 0xC383, 0xD685, 0xC8B7, 0xD686, 0xC384, 0xD687, 0xC385,\n\t0xD688, 0xC386, 0xD689, 0xC387, 0xD68A, 0xC388, 0xD68B, 0xC389,\n\t0xD68C, 0xC8B8, 0xD68D, 0xC8B9, 0xD68E, 0xC38A, 0xD68F, 0xC38B,\n\t0xD690, 0xC8BA, 0xD691, 0xC38C, 0xD692, 0xC38D, 0xD693, 0xC38E,\n\t0xD694, 0xC8BB, 0xD695, 0xC38F, 0xD696, 0xC390, 0xD697, 0xC391,\n\t0xD698, 0xC392, 0xD699, 0xC393, 0xD69A, 0xC394, 0xD69B, 0xC395,\n\t0xD69C, 0xC396, 0xD69D, 0xC8BC, 0xD69E, 0xC397, 0xD69F, 0xC8BD,\n\t0xD6A0, 0xC398, 0xD6A1, 0xC8BE, 0xD6A2, 0xC399, 0xD6A3, 0xC39A,\n\t0xD6A4, 0xC39B, 0xD6A5, 0xC39C, 0xD6A6, 0xC39D, 0xD6A7, 0xC39E,\n\t0xD6A8, 0xC8BF, 0xD6A9, 0xC39F, 0xD6AA, 0xC3A0, 0xD6AB, 0xC441,\n\t0xD6AC, 0xC8C0, 0xD6AD, 0xC442, 0xD6AE, 0xC443, 0xD6AF, 0xC444,\n\t0xD6B0, 0xC8C1, 0xD6B1, 0xC445, 0xD6B2, 0xC446, 0xD6B3, 0xC447,\n\t0xD6B4, 0xC448, 0xD6B5, 0xC449, 0xD6B6, 0xC44A, 0xD6B7, 0xC44B,\n\t0xD6B8, 0xC44C, 0xD6B9, 0xC8C2, 0xD6BA, 0xC44D, 0xD6BB, 0xC8C3,\n\t0xD6BC, 0xC44E, 0xD6BD, 0xC44F, 0xD6BE, 0xC450, 0xD6BF, 0xC451,\n\t0xD6C0, 0xC452, 0xD6C1, 0xC453, 0xD6C2, 0xC454, 0xD6C3, 0xC455,\n\t0xD6C4, 0xC8C4, 0xD6C5, 0xC8C5, 0xD6C6, 0xC456, 0xD6C7, 0xC457,\n\t0xD6C8, 0xC8C6, 0xD6C9, 0xC458, 0xD6CA, 0xC459, 0xD6CB, 0xC45A,\n\t0xD6CC, 0xC8C7, 0xD6CD, 0xC461, 0xD6CE, 0xC462, 0xD6CF, 0xC463,\n\t0xD6D0, 0xC464, 0xD6D1, 0xC8C8, 0xD6D2, 0xC465, 0xD6D3, 0xC466,\n\t0xD6D4, 0xC8C9, 0xD6D5, 0xC467, 0xD6D6, 0xC468, 0xD6D7, 0xC8CA,\n\t0xD6D8, 0xC469, 0xD6D9, 0xC8CB, 0xD6DA, 0xC46A, 0xD6DB, 0xC46B,\n\t0xD6DC, 0xC46C, 0xD6DD, 0xC46D, 0xD6DE, 0xC46E, 0xD6DF, 0xC46F,\n\t0xD6E0, 0xC8CC, 0xD6E1, 0xC470, 0xD6E2, 0xC471, 0xD6E3, 0xC472,\n\t0xD6E4, 0xC8CD, 0xD6E5, 0xC473, 0xD6E6, 0xC474, 0xD6E7, 0xC475,\n\t0xD6E8, 0xC8CE, 0xD6E9, 0xC476, 0xD6EA, 0xC477, 0xD6EB, 0xC478,\n\t0xD6EC, 0xC479, 0xD6ED, 0xC47A, 0xD6EE, 0xC481, 0xD6EF, 0xC482,\n\t0xD6F0, 0xC8CF, 0xD6F1, 0xC483, 0xD6F2, 0xC484, 0xD6F3, 0xC485,\n\t0xD6F4, 0xC486, 0xD6F5, 0xC8D0, 0xD6F6, 0xC487, 0xD6F7, 0xC488,\n\t0xD6F8, 0xC489, 0xD6F9, 0xC48A, 0xD6FA, 0xC48B, 0xD6FB, 0xC48C,\n\t0xD6FC, 0xC8D1, 0xD6FD, 0xC8D2, 0xD6FE, 0xC48D, 0xD6FF, 0xC48E,\n\t0xD700, 0xC8D3, 0xD701, 0xC48F, 0xD702, 0xC490, 0xD703, 0xC491,\n\t0xD704, 0xC8D4, 0xD705, 0xC492, 0xD706, 0xC493, 0xD707, 0xC494,\n\t0xD708, 0xC495, 0xD709, 0xC496, 0xD70A, 0xC497, 0xD70B, 0xC498,\n\t0xD70C, 0xC499, 0xD70D, 0xC49A, 0xD70E, 0xC49B, 0xD70F, 0xC49C,\n\t0xD710, 0xC49D, 0xD711, 0xC8D5, 0xD712, 0xC49E, 0xD713, 0xC49F,\n\t0xD714, 0xC4A0, 0xD715, 0xC541, 0xD716, 0xC542, 0xD717, 0xC543,\n\t0xD718, 0xC8D6, 0xD719, 0xC8D7, 0xD71A, 0xC544, 0xD71B, 0xC545,\n\t0xD71C, 0xC8D8, 0xD71D, 0xC546, 0xD71E, 0xC547, 0xD71F, 0xC548,\n\t0xD720, 0xC8D9, 0xD721, 0xC549, 0xD722, 0xC54A, 0xD723, 0xC54B,\n\t0xD724, 0xC54C, 0xD725, 0xC54D, 0xD726, 0xC54E, 0xD727, 0xC54F,\n\t0xD728, 0xC8DA, 0xD729, 0xC8DB, 0xD72A, 0xC550, 0xD72B, 0xC8DC,\n\t0xD72C, 0xC551, 0xD72D, 0xC8DD, 0xD72E, 0xC552, 0xD72F, 0xC553,\n\t0xD730, 0xC554, 0xD731, 0xC555, 0xD732, 0xC556, 0xD733, 0xC557,\n\t0xD734, 0xC8DE, 0xD735, 0xC8DF, 0xD736, 0xC558, 0xD737, 0xC559,\n\t0xD738, 0xC8E0, 0xD739, 0xC55A, 0xD73A, 0xC561, 0xD73B, 0xC562,\n\t0xD73C, 0xC8E1, 0xD73D, 0xC563, 0xD73E, 0xC564, 0xD73F, 0xC565,\n\t0xD740, 0xC566, 0xD741, 0xC567, 0xD742, 0xC568, 0xD743, 0xC569,\n\t0xD744, 0xC8E2, 0xD745, 0xC56A, 0xD746, 0xC56B, 0xD747, 0xC8E3,\n\t0xD748, 0xC56C, 0xD749, 0xC8E4, 0xD74A, 0xC56D, 0xD74B, 0xC56E,\n\t0xD74C, 0xC56F, 0xD74D, 0xC570, 0xD74E, 0xC571, 0xD74F, 0xC572,\n\t0xD750, 0xC8E5, 0xD751, 0xC8E6, 0xD752, 0xC573, 0xD753, 0xC574,\n\t0xD754, 0xC8E7, 0xD755, 0xC575, 0xD756, 0xC8E8, 0xD757, 0xC8E9,\n\t0xD758, 0xC8EA, 0xD759, 0xC8EB, 0xD75A, 0xC576, 0xD75B, 0xC577,\n\t0xD75C, 0xC578, 0xD75D, 0xC579, 0xD75E, 0xC57A, 0xD75F, 0xC581,\n\t0xD760, 0xC8EC, 0xD761, 0xC8ED, 0xD762, 0xC582, 0xD763, 0xC8EE,\n\t0xD764, 0xC583, 0xD765, 0xC8EF, 0xD766, 0xC584, 0xD767, 0xC585,\n\t0xD768, 0xC586, 0xD769, 0xC8F0, 0xD76A, 0xC587, 0xD76B, 0xC588,\n\t0xD76C, 0xC8F1, 0xD76D, 0xC589, 0xD76E, 0xC58A, 0xD76F, 0xC58B,\n\t0xD770, 0xC8F2, 0xD771, 0xC58C, 0xD772, 0xC58D, 0xD773, 0xC58E,\n\t0xD774, 0xC8F3, 0xD775, 0xC58F, 0xD776, 0xC590, 0xD777, 0xC591,\n\t0xD778, 0xC592, 0xD779, 0xC593, 0xD77A, 0xC594, 0xD77B, 0xC595,\n\t0xD77C, 0xC8F4, 0xD77D, 0xC8F5, 0xD77E, 0xC596, 0xD77F, 0xC597,\n\t0xD780, 0xC598, 0xD781, 0xC8F6, 0xD782, 0xC599, 0xD783, 0xC59A,\n\t0xD784, 0xC59B, 0xD785, 0xC59C, 0xD786, 0xC59D, 0xD787, 0xC59E,\n\t0xD788, 0xC8F7, 0xD789, 0xC8F8, 0xD78A, 0xC59F, 0xD78B, 0xC5A0,\n\t0xD78C, 0xC8F9, 0xD78D, 0xC641, 0xD78E, 0xC642, 0xD78F, 0xC643,\n\t0xD790, 0xC8FA, 0xD791, 0xC644, 0xD792, 0xC645, 0xD793, 0xC646,\n\t0xD794, 0xC647, 0xD795, 0xC648, 0xD796, 0xC649, 0xD797, 0xC64A,\n\t0xD798, 0xC8FB, 0xD799, 0xC8FC, 0xD79A, 0xC64B, 0xD79B, 0xC8FD,\n\t0xD79C, 0xC64C, 0xD79D, 0xC8FE, 0xD79E, 0xC64D, 0xD79F, 0xC64E,\n\t0xD7A0, 0xC64F, 0xD7A1, 0xC650, 0xD7A2, 0xC651, 0xD7A3, 0xC652,\n\t0xF900, 0xCBD0, 0xF901, 0xCBD6, 0xF902, 0xCBE7, 0xF903, 0xCDCF,\n\t0xF904, 0xCDE8, 0xF905, 0xCEAD, 0xF906, 0xCFFB, 0xF907, 0xD0A2,\n\t0xF908, 0xD0B8, 0xF909, 0xD0D0, 0xF90A, 0xD0DD, 0xF90B, 0xD1D4,\n\t0xF90C, 0xD1D5, 0xF90D, 0xD1D8, 0xF90E, 0xD1DB, 0xF90F, 0xD1DC,\n\t0xF910, 0xD1DD, 0xF911, 0xD1DE, 0xF912, 0xD1DF, 0xF913, 0xD1E0,\n\t0xF914, 0xD1E2, 0xF915, 0xD1E3, 0xF916, 0xD1E4, 0xF917, 0xD1E5,\n\t0xF918, 0xD1E6, 0xF919, 0xD1E8, 0xF91A, 0xD1E9, 0xF91B, 0xD1EA,\n\t0xF91C, 0xD1EB, 0xF91D, 0xD1ED, 0xF91E, 0xD1EF, 0xF91F, 0xD1F0,\n\t0xF920, 0xD1F2, 0xF921, 0xD1F6, 0xF922, 0xD1FA, 0xF923, 0xD1FC,\n\t0xF924, 0xD1FD, 0xF925, 0xD1FE, 0xF926, 0xD2A2, 0xF927, 0xD2A3,\n\t0xF928, 0xD2A7, 0xF929, 0xD2A8, 0xF92A, 0xD2A9, 0xF92B, 0xD2AA,\n\t0xF92C, 0xD2AB, 0xF92D, 0xD2AD, 0xF92E, 0xD2B2, 0xF92F, 0xD2BE,\n\t0xF930, 0xD2C2, 0xF931, 0xD2C3, 0xF932, 0xD2C4, 0xF933, 0xD2C6,\n\t0xF934, 0xD2C7, 0xF935, 0xD2C8, 0xF936, 0xD2C9, 0xF937, 0xD2CA,\n\t0xF938, 0xD2CB, 0xF939, 0xD2CD, 0xF93A, 0xD2CE, 0xF93B, 0xD2CF,\n\t0xF93C, 0xD2D0, 0xF93D, 0xD2D1, 0xF93E, 0xD2D2, 0xF93F, 0xD2D3,\n\t0xF940, 0xD2D4, 0xF941, 0xD2D5, 0xF942, 0xD2D6, 0xF943, 0xD2D7,\n\t0xF944, 0xD2D9, 0xF945, 0xD2DA, 0xF946, 0xD2DE, 0xF947, 0xD2DF,\n\t0xF948, 0xD2E1, 0xF949, 0xD2E2, 0xF94A, 0xD2E4, 0xF94B, 0xD2E5,\n\t0xF94C, 0xD2E6, 0xF94D, 0xD2E7, 0xF94E, 0xD2E8, 0xF94F, 0xD2E9,\n\t0xF950, 0xD2EA, 0xF951, 0xD2EB, 0xF952, 0xD2F0, 0xF953, 0xD2F1,\n\t0xF954, 0xD2F2, 0xF955, 0xD2F3, 0xF956, 0xD2F4, 0xF957, 0xD2F5,\n\t0xF958, 0xD2F7, 0xF959, 0xD2F8, 0xF95A, 0xD4E6, 0xF95B, 0xD4FC,\n\t0xF95C, 0xD5A5, 0xF95D, 0xD5AB, 0xF95E, 0xD5AE, 0xF95F, 0xD6B8,\n\t0xF960, 0xD6CD, 0xF961, 0xD7CB, 0xF962, 0xD7E4, 0xF963, 0xDBC5,\n\t0xF964, 0xDBE4, 0xF965, 0xDCA5, 0xF966, 0xDDA5, 0xF967, 0xDDD5,\n\t0xF968, 0xDDF4, 0xF969, 0xDEFC, 0xF96A, 0xDEFE, 0xF96B, 0xDFB3,\n\t0xF96C, 0xDFE1, 0xF96D, 0xDFE8, 0xF96E, 0xE0F1, 0xF96F, 0xE1AD,\n\t0xF970, 0xE1ED, 0xF971, 0xE3F5, 0xF972, 0xE4A1, 0xF973, 0xE4A9,\n\t0xF974, 0xE5AE, 0xF975, 0xE5B1, 0xF976, 0xE5B2, 0xF977, 0xE5B9,\n\t0xF978, 0xE5BB, 0xF979, 0xE5BC, 0xF97A, 0xE5C4, 0xF97B, 0xE5CE,\n\t0xF97C, 0xE5D0, 0xF97D, 0xE5D2, 0xF97E, 0xE5D6, 0xF97F, 0xE5FA,\n\t0xF980, 0xE5FB, 0xF981, 0xE5FC, 0xF982, 0xE5FE, 0xF983, 0xE6A1,\n\t0xF984, 0xE6A4, 0xF985, 0xE6A7, 0xF986, 0xE6AD, 0xF987, 0xE6AF,\n\t0xF988, 0xE6B0, 0xF989, 0xE6B1, 0xF98A, 0xE6B3, 0xF98B, 0xE6B7,\n\t0xF98C, 0xE6B8, 0xF98D, 0xE6BC, 0xF98E, 0xE6C4, 0xF98F, 0xE6C6,\n\t0xF990, 0xE6C7, 0xF991, 0xE6CA, 0xF992, 0xE6D2, 0xF993, 0xE6D6,\n\t0xF994, 0xE6D9, 0xF995, 0xE6DC, 0xF996, 0xE6DF, 0xF997, 0xE6E1,\n\t0xF998, 0xE6E4, 0xF999, 0xE6E5, 0xF99A, 0xE6E6, 0xF99B, 0xE6E8,\n\t0xF99C, 0xE6EA, 0xF99D, 0xE6EB, 0xF99E, 0xE6EC, 0xF99F, 0xE6EF,\n\t0xF9A0, 0xE6F1, 0xF9A1, 0xE6F2, 0xF9A2, 0xE6F5, 0xF9A3, 0xE6F6,\n\t0xF9A4, 0xE6F7, 0xF9A5, 0xE6F9, 0xF9A6, 0xE7A1, 0xF9A7, 0xE7A6,\n\t0xF9A8, 0xE7A9, 0xF9A9, 0xE7AA, 0xF9AA, 0xE7AC, 0xF9AB, 0xE7AD,\n\t0xF9AC, 0xE7B0, 0xF9AD, 0xE7BF, 0xF9AE, 0xE7C1, 0xF9AF, 0xE7C6,\n\t0xF9B0, 0xE7C7, 0xF9B1, 0xE7CB, 0xF9B2, 0xE7CD, 0xF9B3, 0xE7CF,\n\t0xF9B4, 0xE7D0, 0xF9B5, 0xE7D3, 0xF9B6, 0xE7DF, 0xF9B7, 0xE7E4,\n\t0xF9B8, 0xE7E6, 0xF9B9, 0xE7F7, 0xF9BA, 0xE8E7, 0xF9BB, 0xE8E8,\n\t0xF9BC, 0xE8F0, 0xF9BD, 0xE8F1, 0xF9BE, 0xE8F7, 0xF9BF, 0xE8F9,\n\t0xF9C0, 0xE8FB, 0xF9C1, 0xE8FE, 0xF9C2, 0xE9A7, 0xF9C3, 0xE9AC,\n\t0xF9C4, 0xE9CC, 0xF9C5, 0xE9F7, 0xF9C6, 0xEAC1, 0xF9C7, 0xEAE5,\n\t0xF9C8, 0xEAF4, 0xF9C9, 0xEAF7, 0xF9CA, 0xEAFC, 0xF9CB, 0xEAFE,\n\t0xF9CC, 0xEBA4, 0xF9CD, 0xEBA7, 0xF9CE, 0xEBA9, 0xF9CF, 0xEBAA,\n\t0xF9D0, 0xEBBA, 0xF9D1, 0xEBBB, 0xF9D2, 0xEBBD, 0xF9D3, 0xEBC1,\n\t0xF9D4, 0xEBC2, 0xF9D5, 0xEBC6, 0xF9D6, 0xEBC7, 0xF9D7, 0xEBCC,\n\t0xF9D8, 0xEBCF, 0xF9D9, 0xEBD0, 0xF9DA, 0xEBD1, 0xF9DB, 0xEBD2,\n\t0xF9DC, 0xEBD8, 0xF9DD, 0xECA6, 0xF9DE, 0xECA7, 0xF9DF, 0xECAA,\n\t0xF9E0, 0xECAF, 0xF9E1, 0xECB0, 0xF9E2, 0xECB1, 0xF9E3, 0xECB2,\n\t0xF9E4, 0xECB5, 0xF9E5, 0xECB8, 0xF9E6, 0xECBA, 0xF9E7, 0xECC0,\n\t0xF9E8, 0xECC1, 0xF9E9, 0xECC5, 0xF9EA, 0xECC6, 0xF9EB, 0xECC9,\n\t0xF9EC, 0xECCA, 0xF9ED, 0xECD5, 0xF9EE, 0xECDD, 0xF9EF, 0xECDE,\n\t0xF9F0, 0xECE1, 0xF9F1, 0xECE4, 0xF9F2, 0xECE7, 0xF9F3, 0xECE8,\n\t0xF9F4, 0xECF7, 0xF9F5, 0xECF8, 0xF9F6, 0xECFA, 0xF9F7, 0xEDA1,\n\t0xF9F8, 0xEDA2, 0xF9F9, 0xEDA3, 0xF9FA, 0xEDEE, 0xF9FB, 0xEEDB,\n\t0xF9FC, 0xF2BD, 0xF9FD, 0xF2FA, 0xF9FE, 0xF3B1, 0xF9FF, 0xF4A7,\n\t0xFA00, 0xF4EE, 0xFA01, 0xF6F4, 0xFA02, 0xF6F6, 0xFA03, 0xF7B8,\n\t0xFA04, 0xF7C8, 0xFA05, 0xF7D3, 0xFA06, 0xF8DB, 0xFA07, 0xF8F0,\n\t0xFA08, 0xFAA1, 0xFA09, 0xFAA2, 0xFA0A, 0xFAE6, 0xFA0B, 0xFCA9,\n\t0xFF01, 0xA3A1, 0xFF02, 0xA3A2, 0xFF03, 0xA3A3, 0xFF04, 0xA3A4,\n\t0xFF05, 0xA3A5, 0xFF06, 0xA3A6, 0xFF07, 0xA3A7, 0xFF08, 0xA3A8,\n\t0xFF09, 0xA3A9, 0xFF0A, 0xA3AA, 0xFF0B, 0xA3AB, 0xFF0C, 0xA3AC,\n\t0xFF0D, 0xA3AD, 0xFF0E, 0xA3AE, 0xFF0F, 0xA3AF, 0xFF10, 0xA3B0,\n\t0xFF11, 0xA3B1, 0xFF12, 0xA3B2, 0xFF13, 0xA3B3, 0xFF14, 0xA3B4,\n\t0xFF15, 0xA3B5, 0xFF16, 0xA3B6, 0xFF17, 0xA3B7, 0xFF18, 0xA3B8,\n\t0xFF19, 0xA3B9, 0xFF1A, 0xA3BA, 0xFF1B, 0xA3BB, 0xFF1C, 0xA3BC,\n\t0xFF1D, 0xA3BD, 0xFF1E, 0xA3BE, 0xFF1F, 0xA3BF, 0xFF20, 0xA3C0,\n\t0xFF21, 0xA3C1, 0xFF22, 0xA3C2, 0xFF23, 0xA3C3, 0xFF24, 0xA3C4,\n\t0xFF25, 0xA3C5, 0xFF26, 0xA3C6, 0xFF27, 0xA3C7, 0xFF28, 0xA3C8,\n\t0xFF29, 0xA3C9, 0xFF2A, 0xA3CA, 0xFF2B, 0xA3CB, 0xFF2C, 0xA3CC,\n\t0xFF2D, 0xA3CD, 0xFF2E, 0xA3CE, 0xFF2F, 0xA3CF, 0xFF30, 0xA3D0,\n\t0xFF31, 0xA3D1, 0xFF32, 0xA3D2, 0xFF33, 0xA3D3, 0xFF34, 0xA3D4,\n\t0xFF35, 0xA3D5, 0xFF36, 0xA3D6, 0xFF37, 0xA3D7, 0xFF38, 0xA3D8,\n\t0xFF39, 0xA3D9, 0xFF3A, 0xA3DA, 0xFF3B, 0xA3DB, 0xFF3C, 0xA1AC,\n\t0xFF3D, 0xA3DD, 0xFF3E, 0xA3DE, 0xFF3F, 0xA3DF, 0xFF40, 0xA3E0,\n\t0xFF41, 0xA3E1, 0xFF42, 0xA3E2, 0xFF43, 0xA3E3, 0xFF44, 0xA3E4,\n\t0xFF45, 0xA3E5, 0xFF46, 0xA3E6, 0xFF47, 0xA3E7, 0xFF48, 0xA3E8,\n\t0xFF49, 0xA3E9, 0xFF4A, 0xA3EA, 0xFF4B, 0xA3EB, 0xFF4C, 0xA3EC,\n\t0xFF4D, 0xA3ED, 0xFF4E, 0xA3EE, 0xFF4F, 0xA3EF, 0xFF50, 0xA3F0,\n\t0xFF51, 0xA3F1, 0xFF52, 0xA3F2, 0xFF53, 0xA3F3, 0xFF54, 0xA3F4,\n\t0xFF55, 0xA3F5, 0xFF56, 0xA3F6, 0xFF57, 0xA3F7, 0xFF58, 0xA3F8,\n\t0xFF59, 0xA3F9, 0xFF5A, 0xA3FA, 0xFF5B, 0xA3FB, 0xFF5C, 0xA3FC,\n\t0xFF5D, 0xA3FD, 0xFF5E, 0xA2A6, 0xFFE0, 0xA1CB, 0xFFE1, 0xA1CC,\n\t0xFFE2, 0xA1FE, 0xFFE3, 0xA3FE, 0xFFE5, 0xA1CD, 0xFFE6, 0xA3DC,\n\t0, 0\n};\n\nstatic\nconst WCHAR oem2uni[] = {\n/*\tOEM - Unicode,  OEM - Unicode,  OEM - Unicode,  OEM - Unicode */\n\t0x8141, 0xAC02, 0x8142, 0xAC03, 0x8143, 0xAC05, 0x8144, 0xAC06,\n\t0x8145, 0xAC0B, 0x8146, 0xAC0C, 0x8147, 0xAC0D, 0x8148, 0xAC0E,\n\t0x8149, 0xAC0F, 0x814A, 0xAC18, 0x814B, 0xAC1E, 0x814C, 0xAC1F,\n\t0x814D, 0xAC21, 0x814E, 0xAC22, 0x814F, 0xAC23, 0x8150, 0xAC25,\n\t0x8151, 0xAC26, 0x8152, 0xAC27, 0x8153, 0xAC28, 0x8154, 0xAC29,\n\t0x8155, 0xAC2A, 0x8156, 0xAC2B, 0x8157, 0xAC2E, 0x8158, 0xAC32,\n\t0x8159, 0xAC33, 0x815A, 0xAC34, 0x8161, 0xAC35, 0x8162, 0xAC36,\n\t0x8163, 0xAC37, 0x8164, 0xAC3A, 0x8165, 0xAC3B, 0x8166, 0xAC3D,\n\t0x8167, 0xAC3E, 0x8168, 0xAC3F, 0x8169, 0xAC41, 0x816A, 0xAC42,\n\t0x816B, 0xAC43, 0x816C, 0xAC44, 0x816D, 0xAC45, 0x816E, 0xAC46,\n\t0x816F, 0xAC47, 0x8170, 0xAC48, 0x8171, 0xAC49, 0x8172, 0xAC4A,\n\t0x8173, 0xAC4C, 0x8174, 0xAC4E, 0x8175, 0xAC4F, 0x8176, 0xAC50,\n\t0x8177, 0xAC51, 0x8178, 0xAC52, 0x8179, 0xAC53, 0x817A, 0xAC55,\n\t0x8181, 0xAC56, 0x8182, 0xAC57, 0x8183, 0xAC59, 0x8184, 0xAC5A,\n\t0x8185, 0xAC5B, 0x8186, 0xAC5D, 0x8187, 0xAC5E, 0x8188, 0xAC5F,\n\t0x8189, 0xAC60, 0x818A, 0xAC61, 0x818B, 0xAC62, 0x818C, 0xAC63,\n\t0x818D, 0xAC64, 0x818E, 0xAC65, 0x818F, 0xAC66, 0x8190, 0xAC67,\n\t0x8191, 0xAC68, 0x8192, 0xAC69, 0x8193, 0xAC6A, 0x8194, 0xAC6B,\n\t0x8195, 0xAC6C, 0x8196, 0xAC6D, 0x8197, 0xAC6E, 0x8198, 0xAC6F,\n\t0x8199, 0xAC72, 0x819A, 0xAC73, 0x819B, 0xAC75, 0x819C, 0xAC76,\n\t0x819D, 0xAC79, 0x819E, 0xAC7B, 0x819F, 0xAC7C, 0x81A0, 0xAC7D,\n\t0x81A1, 0xAC7E, 0x81A2, 0xAC7F, 0x81A3, 0xAC82, 0x81A4, 0xAC87,\n\t0x81A5, 0xAC88, 0x81A6, 0xAC8D, 0x81A7, 0xAC8E, 0x81A8, 0xAC8F,\n\t0x81A9, 0xAC91, 0x81AA, 0xAC92, 0x81AB, 0xAC93, 0x81AC, 0xAC95,\n\t0x81AD, 0xAC96, 0x81AE, 0xAC97, 0x81AF, 0xAC98, 0x81B0, 0xAC99,\n\t0x81B1, 0xAC9A, 0x81B2, 0xAC9B, 0x81B3, 0xAC9E, 0x81B4, 0xACA2,\n\t0x81B5, 0xACA3, 0x81B6, 0xACA4, 0x81B7, 0xACA5, 0x81B8, 0xACA6,\n\t0x81B9, 0xACA7, 0x81BA, 0xACAB, 0x81BB, 0xACAD, 0x81BC, 0xACAE,\n\t0x81BD, 0xACB1, 0x81BE, 0xACB2, 0x81BF, 0xACB3, 0x81C0, 0xACB4,\n\t0x81C1, 0xACB5, 0x81C2, 0xACB6, 0x81C3, 0xACB7, 0x81C4, 0xACBA,\n\t0x81C5, 0xACBE, 0x81C6, 0xACBF, 0x81C7, 0xACC0, 0x81C8, 0xACC2,\n\t0x81C9, 0xACC3, 0x81CA, 0xACC5, 0x81CB, 0xACC6, 0x81CC, 0xACC7,\n\t0x81CD, 0xACC9, 0x81CE, 0xACCA, 0x81CF, 0xACCB, 0x81D0, 0xACCD,\n\t0x81D1, 0xACCE, 0x81D2, 0xACCF, 0x81D3, 0xACD0, 0x81D4, 0xACD1,\n\t0x81D5, 0xACD2, 0x81D6, 0xACD3, 0x81D7, 0xACD4, 0x81D8, 0xACD6,\n\t0x81D9, 0xACD8, 0x81DA, 0xACD9, 0x81DB, 0xACDA, 0x81DC, 0xACDB,\n\t0x81DD, 0xACDC, 0x81DE, 0xACDD, 0x81DF, 0xACDE, 0x81E0, 0xACDF,\n\t0x81E1, 0xACE2, 0x81E2, 0xACE3, 0x81E3, 0xACE5, 0x81E4, 0xACE6,\n\t0x81E5, 0xACE9, 0x81E6, 0xACEB, 0x81E7, 0xACED, 0x81E8, 0xACEE,\n\t0x81E9, 0xACF2, 0x81EA, 0xACF4, 0x81EB, 0xACF7, 0x81EC, 0xACF8,\n\t0x81ED, 0xACF9, 0x81EE, 0xACFA, 0x81EF, 0xACFB, 0x81F0, 0xACFE,\n\t0x81F1, 0xACFF, 0x81F2, 0xAD01, 0x81F3, 0xAD02, 0x81F4, 0xAD03,\n\t0x81F5, 0xAD05, 0x81F6, 0xAD07, 0x81F7, 0xAD08, 0x81F8, 0xAD09,\n\t0x81F9, 0xAD0A, 0x81FA, 0xAD0B, 0x81FB, 0xAD0E, 0x81FC, 0xAD10,\n\t0x81FD, 0xAD12, 0x81FE, 0xAD13, 0x8241, 0xAD14, 0x8242, 0xAD15,\n\t0x8243, 0xAD16, 0x8244, 0xAD17, 0x8245, 0xAD19, 0x8246, 0xAD1A,\n\t0x8247, 0xAD1B, 0x8248, 0xAD1D, 0x8249, 0xAD1E, 0x824A, 0xAD1F,\n\t0x824B, 0xAD21, 0x824C, 0xAD22, 0x824D, 0xAD23, 0x824E, 0xAD24,\n\t0x824F, 0xAD25, 0x8250, 0xAD26, 0x8251, 0xAD27, 0x8252, 0xAD28,\n\t0x8253, 0xAD2A, 0x8254, 0xAD2B, 0x8255, 0xAD2E, 0x8256, 0xAD2F,\n\t0x8257, 0xAD30, 0x8258, 0xAD31, 0x8259, 0xAD32, 0x825A, 0xAD33,\n\t0x8261, 0xAD36, 0x8262, 0xAD37, 0x8263, 0xAD39, 0x8264, 0xAD3A,\n\t0x8265, 0xAD3B, 0x8266, 0xAD3D, 0x8267, 0xAD3E, 0x8268, 0xAD3F,\n\t0x8269, 0xAD40, 0x826A, 0xAD41, 0x826B, 0xAD42, 0x826C, 0xAD43,\n\t0x826D, 0xAD46, 0x826E, 0xAD48, 0x826F, 0xAD4A, 0x8270, 0xAD4B,\n\t0x8271, 0xAD4C, 0x8272, 0xAD4D, 0x8273, 0xAD4E, 0x8274, 0xAD4F,\n\t0x8275, 0xAD51, 0x8276, 0xAD52, 0x8277, 0xAD53, 0x8278, 0xAD55,\n\t0x8279, 0xAD56, 0x827A, 0xAD57, 0x8281, 0xAD59, 0x8282, 0xAD5A,\n\t0x8283, 0xAD5B, 0x8284, 0xAD5C, 0x8285, 0xAD5D, 0x8286, 0xAD5E,\n\t0x8287, 0xAD5F, 0x8288, 0xAD60, 0x8289, 0xAD62, 0x828A, 0xAD64,\n\t0x828B, 0xAD65, 0x828C, 0xAD66, 0x828D, 0xAD67, 0x828E, 0xAD68,\n\t0x828F, 0xAD69, 0x8290, 0xAD6A, 0x8291, 0xAD6B, 0x8292, 0xAD6E,\n\t0x8293, 0xAD6F, 0x8294, 0xAD71, 0x8295, 0xAD72, 0x8296, 0xAD77,\n\t0x8297, 0xAD78, 0x8298, 0xAD79, 0x8299, 0xAD7A, 0x829A, 0xAD7E,\n\t0x829B, 0xAD80, 0x829C, 0xAD83, 0x829D, 0xAD84, 0x829E, 0xAD85,\n\t0x829F, 0xAD86, 0x82A0, 0xAD87, 0x82A1, 0xAD8A, 0x82A2, 0xAD8B,\n\t0x82A3, 0xAD8D, 0x82A4, 0xAD8E, 0x82A5, 0xAD8F, 0x82A6, 0xAD91,\n\t0x82A7, 0xAD92, 0x82A8, 0xAD93, 0x82A9, 0xAD94, 0x82AA, 0xAD95,\n\t0x82AB, 0xAD96, 0x82AC, 0xAD97, 0x82AD, 0xAD98, 0x82AE, 0xAD99,\n\t0x82AF, 0xAD9A, 0x82B0, 0xAD9B, 0x82B1, 0xAD9E, 0x82B2, 0xAD9F,\n\t0x82B3, 0xADA0, 0x82B4, 0xADA1, 0x82B5, 0xADA2, 0x82B6, 0xADA3,\n\t0x82B7, 0xADA5, 0x82B8, 0xADA6, 0x82B9, 0xADA7, 0x82BA, 0xADA8,\n\t0x82BB, 0xADA9, 0x82BC, 0xADAA, 0x82BD, 0xADAB, 0x82BE, 0xADAC,\n\t0x82BF, 0xADAD, 0x82C0, 0xADAE, 0x82C1, 0xADAF, 0x82C2, 0xADB0,\n\t0x82C3, 0xADB1, 0x82C4, 0xADB2, 0x82C5, 0xADB3, 0x82C6, 0xADB4,\n\t0x82C7, 0xADB5, 0x82C8, 0xADB6, 0x82C9, 0xADB8, 0x82CA, 0xADB9,\n\t0x82CB, 0xADBA, 0x82CC, 0xADBB, 0x82CD, 0xADBC, 0x82CE, 0xADBD,\n\t0x82CF, 0xADBE, 0x82D0, 0xADBF, 0x82D1, 0xADC2, 0x82D2, 0xADC3,\n\t0x82D3, 0xADC5, 0x82D4, 0xADC6, 0x82D5, 0xADC7, 0x82D6, 0xADC9,\n\t0x82D7, 0xADCA, 0x82D8, 0xADCB, 0x82D9, 0xADCC, 0x82DA, 0xADCD,\n\t0x82DB, 0xADCE, 0x82DC, 0xADCF, 0x82DD, 0xADD2, 0x82DE, 0xADD4,\n\t0x82DF, 0xADD5, 0x82E0, 0xADD6, 0x82E1, 0xADD7, 0x82E2, 0xADD8,\n\t0x82E3, 0xADD9, 0x82E4, 0xADDA, 0x82E5, 0xADDB, 0x82E6, 0xADDD,\n\t0x82E7, 0xADDE, 0x82E8, 0xADDF, 0x82E9, 0xADE1, 0x82EA, 0xADE2,\n\t0x82EB, 0xADE3, 0x82EC, 0xADE5, 0x82ED, 0xADE6, 0x82EE, 0xADE7,\n\t0x82EF, 0xADE8, 0x82F0, 0xADE9, 0x82F1, 0xADEA, 0x82F2, 0xADEB,\n\t0x82F3, 0xADEC, 0x82F4, 0xADED, 0x82F5, 0xADEE, 0x82F6, 0xADEF,\n\t0x82F7, 0xADF0, 0x82F8, 0xADF1, 0x82F9, 0xADF2, 0x82FA, 0xADF3,\n\t0x82FB, 0xADF4, 0x82FC, 0xADF5, 0x82FD, 0xADF6, 0x82FE, 0xADF7,\n\t0x8341, 0xADFA, 0x8342, 0xADFB, 0x8343, 0xADFD, 0x8344, 0xADFE,\n\t0x8345, 0xAE02, 0x8346, 0xAE03, 0x8347, 0xAE04, 0x8348, 0xAE05,\n\t0x8349, 0xAE06, 0x834A, 0xAE07, 0x834B, 0xAE0A, 0x834C, 0xAE0C,\n\t0x834D, 0xAE0E, 0x834E, 0xAE0F, 0x834F, 0xAE10, 0x8350, 0xAE11,\n\t0x8351, 0xAE12, 0x8352, 0xAE13, 0x8353, 0xAE15, 0x8354, 0xAE16,\n\t0x8355, 0xAE17, 0x8356, 0xAE18, 0x8357, 0xAE19, 0x8358, 0xAE1A,\n\t0x8359, 0xAE1B, 0x835A, 0xAE1C, 0x8361, 0xAE1D, 0x8362, 0xAE1E,\n\t0x8363, 0xAE1F, 0x8364, 0xAE20, 0x8365, 0xAE21, 0x8366, 0xAE22,\n\t0x8367, 0xAE23, 0x8368, 0xAE24, 0x8369, 0xAE25, 0x836A, 0xAE26,\n\t0x836B, 0xAE27, 0x836C, 0xAE28, 0x836D, 0xAE29, 0x836E, 0xAE2A,\n\t0x836F, 0xAE2B, 0x8370, 0xAE2C, 0x8371, 0xAE2D, 0x8372, 0xAE2E,\n\t0x8373, 0xAE2F, 0x8374, 0xAE32, 0x8375, 0xAE33, 0x8376, 0xAE35,\n\t0x8377, 0xAE36, 0x8378, 0xAE39, 0x8379, 0xAE3B, 0x837A, 0xAE3C,\n\t0x8381, 0xAE3D, 0x8382, 0xAE3E, 0x8383, 0xAE3F, 0x8384, 0xAE42,\n\t0x8385, 0xAE44, 0x8386, 0xAE47, 0x8387, 0xAE48, 0x8388, 0xAE49,\n\t0x8389, 0xAE4B, 0x838A, 0xAE4F, 0x838B, 0xAE51, 0x838C, 0xAE52,\n\t0x838D, 0xAE53, 0x838E, 0xAE55, 0x838F, 0xAE57, 0x8390, 0xAE58,\n\t0x8391, 0xAE59, 0x8392, 0xAE5A, 0x8393, 0xAE5B, 0x8394, 0xAE5E,\n\t0x8395, 0xAE62, 0x8396, 0xAE63, 0x8397, 0xAE64, 0x8398, 0xAE66,\n\t0x8399, 0xAE67, 0x839A, 0xAE6A, 0x839B, 0xAE6B, 0x839C, 0xAE6D,\n\t0x839D, 0xAE6E, 0x839E, 0xAE6F, 0x839F, 0xAE71, 0x83A0, 0xAE72,\n\t0x83A1, 0xAE73, 0x83A2, 0xAE74, 0x83A3, 0xAE75, 0x83A4, 0xAE76,\n\t0x83A5, 0xAE77, 0x83A6, 0xAE7A, 0x83A7, 0xAE7E, 0x83A8, 0xAE7F,\n\t0x83A9, 0xAE80, 0x83AA, 0xAE81, 0x83AB, 0xAE82, 0x83AC, 0xAE83,\n\t0x83AD, 0xAE86, 0x83AE, 0xAE87, 0x83AF, 0xAE88, 0x83B0, 0xAE89,\n\t0x83B1, 0xAE8A, 0x83B2, 0xAE8B, 0x83B3, 0xAE8D, 0x83B4, 0xAE8E,\n\t0x83B5, 0xAE8F, 0x83B6, 0xAE90, 0x83B7, 0xAE91, 0x83B8, 0xAE92,\n\t0x83B9, 0xAE93, 0x83BA, 0xAE94, 0x83BB, 0xAE95, 0x83BC, 0xAE96,\n\t0x83BD, 0xAE97, 0x83BE, 0xAE98, 0x83BF, 0xAE99, 0x83C0, 0xAE9A,\n\t0x83C1, 0xAE9B, 0x83C2, 0xAE9C, 0x83C3, 0xAE9D, 0x83C4, 0xAE9E,\n\t0x83C5, 0xAE9F, 0x83C6, 0xAEA0, 0x83C7, 0xAEA1, 0x83C8, 0xAEA2,\n\t0x83C9, 0xAEA3, 0x83CA, 0xAEA4, 0x83CB, 0xAEA5, 0x83CC, 0xAEA6,\n\t0x83CD, 0xAEA7, 0x83CE, 0xAEA8, 0x83CF, 0xAEA9, 0x83D0, 0xAEAA,\n\t0x83D1, 0xAEAB, 0x83D2, 0xAEAC, 0x83D3, 0xAEAD, 0x83D4, 0xAEAE,\n\t0x83D5, 0xAEAF, 0x83D6, 0xAEB0, 0x83D7, 0xAEB1, 0x83D8, 0xAEB2,\n\t0x83D9, 0xAEB3, 0x83DA, 0xAEB4, 0x83DB, 0xAEB5, 0x83DC, 0xAEB6,\n\t0x83DD, 0xAEB7, 0x83DE, 0xAEB8, 0x83DF, 0xAEB9, 0x83E0, 0xAEBA,\n\t0x83E1, 0xAEBB, 0x83E2, 0xAEBF, 0x83E3, 0xAEC1, 0x83E4, 0xAEC2,\n\t0x83E5, 0xAEC3, 0x83E6, 0xAEC5, 0x83E7, 0xAEC6, 0x83E8, 0xAEC7,\n\t0x83E9, 0xAEC8, 0x83EA, 0xAEC9, 0x83EB, 0xAECA, 0x83EC, 0xAECB,\n\t0x83ED, 0xAECE, 0x83EE, 0xAED2, 0x83EF, 0xAED3, 0x83F0, 0xAED4,\n\t0x83F1, 0xAED5, 0x83F2, 0xAED6, 0x83F3, 0xAED7, 0x83F4, 0xAEDA,\n\t0x83F5, 0xAEDB, 0x83F6, 0xAEDD, 0x83F7, 0xAEDE, 0x83F8, 0xAEDF,\n\t0x83F9, 0xAEE0, 0x83FA, 0xAEE1, 0x83FB, 0xAEE2, 0x83FC, 0xAEE3,\n\t0x83FD, 0xAEE4, 0x83FE, 0xAEE5, 0x8441, 0xAEE6, 0x8442, 0xAEE7,\n\t0x8443, 0xAEE9, 0x8444, 0xAEEA, 0x8445, 0xAEEC, 0x8446, 0xAEEE,\n\t0x8447, 0xAEEF, 0x8448, 0xAEF0, 0x8449, 0xAEF1, 0x844A, 0xAEF2,\n\t0x844B, 0xAEF3, 0x844C, 0xAEF5, 0x844D, 0xAEF6, 0x844E, 0xAEF7,\n\t0x844F, 0xAEF9, 0x8450, 0xAEFA, 0x8451, 0xAEFB, 0x8452, 0xAEFD,\n\t0x8453, 0xAEFE, 0x8454, 0xAEFF, 0x8455, 0xAF00, 0x8456, 0xAF01,\n\t0x8457, 0xAF02, 0x8458, 0xAF03, 0x8459, 0xAF04, 0x845A, 0xAF05,\n\t0x8461, 0xAF06, 0x8462, 0xAF09, 0x8463, 0xAF0A, 0x8464, 0xAF0B,\n\t0x8465, 0xAF0C, 0x8466, 0xAF0E, 0x8467, 0xAF0F, 0x8468, 0xAF11,\n\t0x8469, 0xAF12, 0x846A, 0xAF13, 0x846B, 0xAF14, 0x846C, 0xAF15,\n\t0x846D, 0xAF16, 0x846E, 0xAF17, 0x846F, 0xAF18, 0x8470, 0xAF19,\n\t0x8471, 0xAF1A, 0x8472, 0xAF1B, 0x8473, 0xAF1C, 0x8474, 0xAF1D,\n\t0x8475, 0xAF1E, 0x8476, 0xAF1F, 0x8477, 0xAF20, 0x8478, 0xAF21,\n\t0x8479, 0xAF22, 0x847A, 0xAF23, 0x8481, 0xAF24, 0x8482, 0xAF25,\n\t0x8483, 0xAF26, 0x8484, 0xAF27, 0x8485, 0xAF28, 0x8486, 0xAF29,\n\t0x8487, 0xAF2A, 0x8488, 0xAF2B, 0x8489, 0xAF2E, 0x848A, 0xAF2F,\n\t0x848B, 0xAF31, 0x848C, 0xAF33, 0x848D, 0xAF35, 0x848E, 0xAF36,\n\t0x848F, 0xAF37, 0x8490, 0xAF38, 0x8491, 0xAF39, 0x8492, 0xAF3A,\n\t0x8493, 0xAF3B, 0x8494, 0xAF3E, 0x8495, 0xAF40, 0x8496, 0xAF44,\n\t0x8497, 0xAF45, 0x8498, 0xAF46, 0x8499, 0xAF47, 0x849A, 0xAF4A,\n\t0x849B, 0xAF4B, 0x849C, 0xAF4C, 0x849D, 0xAF4D, 0x849E, 0xAF4E,\n\t0x849F, 0xAF4F, 0x84A0, 0xAF51, 0x84A1, 0xAF52, 0x84A2, 0xAF53,\n\t0x84A3, 0xAF54, 0x84A4, 0xAF55, 0x84A5, 0xAF56, 0x84A6, 0xAF57,\n\t0x84A7, 0xAF58, 0x84A8, 0xAF59, 0x84A9, 0xAF5A, 0x84AA, 0xAF5B,\n\t0x84AB, 0xAF5E, 0x84AC, 0xAF5F, 0x84AD, 0xAF60, 0x84AE, 0xAF61,\n\t0x84AF, 0xAF62, 0x84B0, 0xAF63, 0x84B1, 0xAF66, 0x84B2, 0xAF67,\n\t0x84B3, 0xAF68, 0x84B4, 0xAF69, 0x84B5, 0xAF6A, 0x84B6, 0xAF6B,\n\t0x84B7, 0xAF6C, 0x84B8, 0xAF6D, 0x84B9, 0xAF6E, 0x84BA, 0xAF6F,\n\t0x84BB, 0xAF70, 0x84BC, 0xAF71, 0x84BD, 0xAF72, 0x84BE, 0xAF73,\n\t0x84BF, 0xAF74, 0x84C0, 0xAF75, 0x84C1, 0xAF76, 0x84C2, 0xAF77,\n\t0x84C3, 0xAF78, 0x84C4, 0xAF7A, 0x84C5, 0xAF7B, 0x84C6, 0xAF7C,\n\t0x84C7, 0xAF7D, 0x84C8, 0xAF7E, 0x84C9, 0xAF7F, 0x84CA, 0xAF81,\n\t0x84CB, 0xAF82, 0x84CC, 0xAF83, 0x84CD, 0xAF85, 0x84CE, 0xAF86,\n\t0x84CF, 0xAF87, 0x84D0, 0xAF89, 0x84D1, 0xAF8A, 0x84D2, 0xAF8B,\n\t0x84D3, 0xAF8C, 0x84D4, 0xAF8D, 0x84D5, 0xAF8E, 0x84D6, 0xAF8F,\n\t0x84D7, 0xAF92, 0x84D8, 0xAF93, 0x84D9, 0xAF94, 0x84DA, 0xAF96,\n\t0x84DB, 0xAF97, 0x84DC, 0xAF98, 0x84DD, 0xAF99, 0x84DE, 0xAF9A,\n\t0x84DF, 0xAF9B, 0x84E0, 0xAF9D, 0x84E1, 0xAF9E, 0x84E2, 0xAF9F,\n\t0x84E3, 0xAFA0, 0x84E4, 0xAFA1, 0x84E5, 0xAFA2, 0x84E6, 0xAFA3,\n\t0x84E7, 0xAFA4, 0x84E8, 0xAFA5, 0x84E9, 0xAFA6, 0x84EA, 0xAFA7,\n\t0x84EB, 0xAFA8, 0x84EC, 0xAFA9, 0x84ED, 0xAFAA, 0x84EE, 0xAFAB,\n\t0x84EF, 0xAFAC, 0x84F0, 0xAFAD, 0x84F1, 0xAFAE, 0x84F2, 0xAFAF,\n\t0x84F3, 0xAFB0, 0x84F4, 0xAFB1, 0x84F5, 0xAFB2, 0x84F6, 0xAFB3,\n\t0x84F7, 0xAFB4, 0x84F8, 0xAFB5, 0x84F9, 0xAFB6, 0x84FA, 0xAFB7,\n\t0x84FB, 0xAFBA, 0x84FC, 0xAFBB, 0x84FD, 0xAFBD, 0x84FE, 0xAFBE,\n\t0x8541, 0xAFBF, 0x8542, 0xAFC1, 0x8543, 0xAFC2, 0x8544, 0xAFC3,\n\t0x8545, 0xAFC4, 0x8546, 0xAFC5, 0x8547, 0xAFC6, 0x8548, 0xAFCA,\n\t0x8549, 0xAFCC, 0x854A, 0xAFCF, 0x854B, 0xAFD0, 0x854C, 0xAFD1,\n\t0x854D, 0xAFD2, 0x854E, 0xAFD3, 0x854F, 0xAFD5, 0x8550, 0xAFD6,\n\t0x8551, 0xAFD7, 0x8552, 0xAFD8, 0x8553, 0xAFD9, 0x8554, 0xAFDA,\n\t0x8555, 0xAFDB, 0x8556, 0xAFDD, 0x8557, 0xAFDE, 0x8558, 0xAFDF,\n\t0x8559, 0xAFE0, 0x855A, 0xAFE1, 0x8561, 0xAFE2, 0x8562, 0xAFE3,\n\t0x8563, 0xAFE4, 0x8564, 0xAFE5, 0x8565, 0xAFE6, 0x8566, 0xAFE7,\n\t0x8567, 0xAFEA, 0x8568, 0xAFEB, 0x8569, 0xAFEC, 0x856A, 0xAFED,\n\t0x856B, 0xAFEE, 0x856C, 0xAFEF, 0x856D, 0xAFF2, 0x856E, 0xAFF3,\n\t0x856F, 0xAFF5, 0x8570, 0xAFF6, 0x8571, 0xAFF7, 0x8572, 0xAFF9,\n\t0x8573, 0xAFFA, 0x8574, 0xAFFB, 0x8575, 0xAFFC, 0x8576, 0xAFFD,\n\t0x8577, 0xAFFE, 0x8578, 0xAFFF, 0x8579, 0xB002, 0x857A, 0xB003,\n\t0x8581, 0xB005, 0x8582, 0xB006, 0x8583, 0xB007, 0x8584, 0xB008,\n\t0x8585, 0xB009, 0x8586, 0xB00A, 0x8587, 0xB00B, 0x8588, 0xB00D,\n\t0x8589, 0xB00E, 0x858A, 0xB00F, 0x858B, 0xB011, 0x858C, 0xB012,\n\t0x858D, 0xB013, 0x858E, 0xB015, 0x858F, 0xB016, 0x8590, 0xB017,\n\t0x8591, 0xB018, 0x8592, 0xB019, 0x8593, 0xB01A, 0x8594, 0xB01B,\n\t0x8595, 0xB01E, 0x8596, 0xB01F, 0x8597, 0xB020, 0x8598, 0xB021,\n\t0x8599, 0xB022, 0x859A, 0xB023, 0x859B, 0xB024, 0x859C, 0xB025,\n\t0x859D, 0xB026, 0x859E, 0xB027, 0x859F, 0xB029, 0x85A0, 0xB02A,\n\t0x85A1, 0xB02B, 0x85A2, 0xB02C, 0x85A3, 0xB02D, 0x85A4, 0xB02E,\n\t0x85A5, 0xB02F, 0x85A6, 0xB030, 0x85A7, 0xB031, 0x85A8, 0xB032,\n\t0x85A9, 0xB033, 0x85AA, 0xB034, 0x85AB, 0xB035, 0x85AC, 0xB036,\n\t0x85AD, 0xB037, 0x85AE, 0xB038, 0x85AF, 0xB039, 0x85B0, 0xB03A,\n\t0x85B1, 0xB03B, 0x85B2, 0xB03C, 0x85B3, 0xB03D, 0x85B4, 0xB03E,\n\t0x85B5, 0xB03F, 0x85B6, 0xB040, 0x85B7, 0xB041, 0x85B8, 0xB042,\n\t0x85B9, 0xB043, 0x85BA, 0xB046, 0x85BB, 0xB047, 0x85BC, 0xB049,\n\t0x85BD, 0xB04B, 0x85BE, 0xB04D, 0x85BF, 0xB04F, 0x85C0, 0xB050,\n\t0x85C1, 0xB051, 0x85C2, 0xB052, 0x85C3, 0xB056, 0x85C4, 0xB058,\n\t0x85C5, 0xB05A, 0x85C6, 0xB05B, 0x85C7, 0xB05C, 0x85C8, 0xB05E,\n\t0x85C9, 0xB05F, 0x85CA, 0xB060, 0x85CB, 0xB061, 0x85CC, 0xB062,\n\t0x85CD, 0xB063, 0x85CE, 0xB064, 0x85CF, 0xB065, 0x85D0, 0xB066,\n\t0x85D1, 0xB067, 0x85D2, 0xB068, 0x85D3, 0xB069, 0x85D4, 0xB06A,\n\t0x85D5, 0xB06B, 0x85D6, 0xB06C, 0x85D7, 0xB06D, 0x85D8, 0xB06E,\n\t0x85D9, 0xB06F, 0x85DA, 0xB070, 0x85DB, 0xB071, 0x85DC, 0xB072,\n\t0x85DD, 0xB073, 0x85DE, 0xB074, 0x85DF, 0xB075, 0x85E0, 0xB076,\n\t0x85E1, 0xB077, 0x85E2, 0xB078, 0x85E3, 0xB079, 0x85E4, 0xB07A,\n\t0x85E5, 0xB07B, 0x85E6, 0xB07E, 0x85E7, 0xB07F, 0x85E8, 0xB081,\n\t0x85E9, 0xB082, 0x85EA, 0xB083, 0x85EB, 0xB085, 0x85EC, 0xB086,\n\t0x85ED, 0xB087, 0x85EE, 0xB088, 0x85EF, 0xB089, 0x85F0, 0xB08A,\n\t0x85F1, 0xB08B, 0x85F2, 0xB08E, 0x85F3, 0xB090, 0x85F4, 0xB092,\n\t0x85F5, 0xB093, 0x85F6, 0xB094, 0x85F7, 0xB095, 0x85F8, 0xB096,\n\t0x85F9, 0xB097, 0x85FA, 0xB09B, 0x85FB, 0xB09D, 0x85FC, 0xB09E,\n\t0x85FD, 0xB0A3, 0x85FE, 0xB0A4, 0x8641, 0xB0A5, 0x8642, 0xB0A6,\n\t0x8643, 0xB0A7, 0x8644, 0xB0AA, 0x8645, 0xB0B0, 0x8646, 0xB0B2,\n\t0x8647, 0xB0B6, 0x8648, 0xB0B7, 0x8649, 0xB0B9, 0x864A, 0xB0BA,\n\t0x864B, 0xB0BB, 0x864C, 0xB0BD, 0x864D, 0xB0BE, 0x864E, 0xB0BF,\n\t0x864F, 0xB0C0, 0x8650, 0xB0C1, 0x8651, 0xB0C2, 0x8652, 0xB0C3,\n\t0x8653, 0xB0C6, 0x8654, 0xB0CA, 0x8655, 0xB0CB, 0x8656, 0xB0CC,\n\t0x8657, 0xB0CD, 0x8658, 0xB0CE, 0x8659, 0xB0CF, 0x865A, 0xB0D2,\n\t0x8661, 0xB0D3, 0x8662, 0xB0D5, 0x8663, 0xB0D6, 0x8664, 0xB0D7,\n\t0x8665, 0xB0D9, 0x8666, 0xB0DA, 0x8667, 0xB0DB, 0x8668, 0xB0DC,\n\t0x8669, 0xB0DD, 0x866A, 0xB0DE, 0x866B, 0xB0DF, 0x866C, 0xB0E1,\n\t0x866D, 0xB0E2, 0x866E, 0xB0E3, 0x866F, 0xB0E4, 0x8670, 0xB0E6,\n\t0x8671, 0xB0E7, 0x8672, 0xB0E8, 0x8673, 0xB0E9, 0x8674, 0xB0EA,\n\t0x8675, 0xB0EB, 0x8676, 0xB0EC, 0x8677, 0xB0ED, 0x8678, 0xB0EE,\n\t0x8679, 0xB0EF, 0x867A, 0xB0F0, 0x8681, 0xB0F1, 0x8682, 0xB0F2,\n\t0x8683, 0xB0F3, 0x8684, 0xB0F4, 0x8685, 0xB0F5, 0x8686, 0xB0F6,\n\t0x8687, 0xB0F7, 0x8688, 0xB0F8, 0x8689, 0xB0F9, 0x868A, 0xB0FA,\n\t0x868B, 0xB0FB, 0x868C, 0xB0FC, 0x868D, 0xB0FD, 0x868E, 0xB0FE,\n\t0x868F, 0xB0FF, 0x8690, 0xB100, 0x8691, 0xB101, 0x8692, 0xB102,\n\t0x8693, 0xB103, 0x8694, 0xB104, 0x8695, 0xB105, 0x8696, 0xB106,\n\t0x8697, 0xB107, 0x8698, 0xB10A, 0x8699, 0xB10D, 0x869A, 0xB10E,\n\t0x869B, 0xB10F, 0x869C, 0xB111, 0x869D, 0xB114, 0x869E, 0xB115,\n\t0x869F, 0xB116, 0x86A0, 0xB117, 0x86A1, 0xB11A, 0x86A2, 0xB11E,\n\t0x86A3, 0xB11F, 0x86A4, 0xB120, 0x86A5, 0xB121, 0x86A6, 0xB122,\n\t0x86A7, 0xB126, 0x86A8, 0xB127, 0x86A9, 0xB129, 0x86AA, 0xB12A,\n\t0x86AB, 0xB12B, 0x86AC, 0xB12D, 0x86AD, 0xB12E, 0x86AE, 0xB12F,\n\t0x86AF, 0xB130, 0x86B0, 0xB131, 0x86B1, 0xB132, 0x86B2, 0xB133,\n\t0x86B3, 0xB136, 0x86B4, 0xB13A, 0x86B5, 0xB13B, 0x86B6, 0xB13C,\n\t0x86B7, 0xB13D, 0x86B8, 0xB13E, 0x86B9, 0xB13F, 0x86BA, 0xB142,\n\t0x86BB, 0xB143, 0x86BC, 0xB145, 0x86BD, 0xB146, 0x86BE, 0xB147,\n\t0x86BF, 0xB149, 0x86C0, 0xB14A, 0x86C1, 0xB14B, 0x86C2, 0xB14C,\n\t0x86C3, 0xB14D, 0x86C4, 0xB14E, 0x86C5, 0xB14F, 0x86C6, 0xB152,\n\t0x86C7, 0xB153, 0x86C8, 0xB156, 0x86C9, 0xB157, 0x86CA, 0xB159,\n\t0x86CB, 0xB15A, 0x86CC, 0xB15B, 0x86CD, 0xB15D, 0x86CE, 0xB15E,\n\t0x86CF, 0xB15F, 0x86D0, 0xB161, 0x86D1, 0xB162, 0x86D2, 0xB163,\n\t0x86D3, 0xB164, 0x86D4, 0xB165, 0x86D5, 0xB166, 0x86D6, 0xB167,\n\t0x86D7, 0xB168, 0x86D8, 0xB169, 0x86D9, 0xB16A, 0x86DA, 0xB16B,\n\t0x86DB, 0xB16C, 0x86DC, 0xB16D, 0x86DD, 0xB16E, 0x86DE, 0xB16F,\n\t0x86DF, 0xB170, 0x86E0, 0xB171, 0x86E1, 0xB172, 0x86E2, 0xB173,\n\t0x86E3, 0xB174, 0x86E4, 0xB175, 0x86E5, 0xB176, 0x86E6, 0xB177,\n\t0x86E7, 0xB17A, 0x86E8, 0xB17B, 0x86E9, 0xB17D, 0x86EA, 0xB17E,\n\t0x86EB, 0xB17F, 0x86EC, 0xB181, 0x86ED, 0xB183, 0x86EE, 0xB184,\n\t0x86EF, 0xB185, 0x86F0, 0xB186, 0x86F1, 0xB187, 0x86F2, 0xB18A,\n\t0x86F3, 0xB18C, 0x86F4, 0xB18E, 0x86F5, 0xB18F, 0x86F6, 0xB190,\n\t0x86F7, 0xB191, 0x86F8, 0xB195, 0x86F9, 0xB196, 0x86FA, 0xB197,\n\t0x86FB, 0xB199, 0x86FC, 0xB19A, 0x86FD, 0xB19B, 0x86FE, 0xB19D,\n\t0x8741, 0xB19E, 0x8742, 0xB19F, 0x8743, 0xB1A0, 0x8744, 0xB1A1,\n\t0x8745, 0xB1A2, 0x8746, 0xB1A3, 0x8747, 0xB1A4, 0x8748, 0xB1A5,\n\t0x8749, 0xB1A6, 0x874A, 0xB1A7, 0x874B, 0xB1A9, 0x874C, 0xB1AA,\n\t0x874D, 0xB1AB, 0x874E, 0xB1AC, 0x874F, 0xB1AD, 0x8750, 0xB1AE,\n\t0x8751, 0xB1AF, 0x8752, 0xB1B0, 0x8753, 0xB1B1, 0x8754, 0xB1B2,\n\t0x8755, 0xB1B3, 0x8756, 0xB1B4, 0x8757, 0xB1B5, 0x8758, 0xB1B6,\n\t0x8759, 0xB1B7, 0x875A, 0xB1B8, 0x8761, 0xB1B9, 0x8762, 0xB1BA,\n\t0x8763, 0xB1BB, 0x8764, 0xB1BC, 0x8765, 0xB1BD, 0x8766, 0xB1BE,\n\t0x8767, 0xB1BF, 0x8768, 0xB1C0, 0x8769, 0xB1C1, 0x876A, 0xB1C2,\n\t0x876B, 0xB1C3, 0x876C, 0xB1C4, 0x876D, 0xB1C5, 0x876E, 0xB1C6,\n\t0x876F, 0xB1C7, 0x8770, 0xB1C8, 0x8771, 0xB1C9, 0x8772, 0xB1CA,\n\t0x8773, 0xB1CB, 0x8774, 0xB1CD, 0x8775, 0xB1CE, 0x8776, 0xB1CF,\n\t0x8777, 0xB1D1, 0x8778, 0xB1D2, 0x8779, 0xB1D3, 0x877A, 0xB1D5,\n\t0x8781, 0xB1D6, 0x8782, 0xB1D7, 0x8783, 0xB1D8, 0x8784, 0xB1D9,\n\t0x8785, 0xB1DA, 0x8786, 0xB1DB, 0x8787, 0xB1DE, 0x8788, 0xB1E0,\n\t0x8789, 0xB1E1, 0x878A, 0xB1E2, 0x878B, 0xB1E3, 0x878C, 0xB1E4,\n\t0x878D, 0xB1E5, 0x878E, 0xB1E6, 0x878F, 0xB1E7, 0x8790, 0xB1EA,\n\t0x8791, 0xB1EB, 0x8792, 0xB1ED, 0x8793, 0xB1EE, 0x8794, 0xB1EF,\n\t0x8795, 0xB1F1, 0x8796, 0xB1F2, 0x8797, 0xB1F3, 0x8798, 0xB1F4,\n\t0x8799, 0xB1F5, 0x879A, 0xB1F6, 0x879B, 0xB1F7, 0x879C, 0xB1F8,\n\t0x879D, 0xB1FA, 0x879E, 0xB1FC, 0x879F, 0xB1FE, 0x87A0, 0xB1FF,\n\t0x87A1, 0xB200, 0x87A2, 0xB201, 0x87A3, 0xB202, 0x87A4, 0xB203,\n\t0x87A5, 0xB206, 0x87A6, 0xB207, 0x87A7, 0xB209, 0x87A8, 0xB20A,\n\t0x87A9, 0xB20D, 0x87AA, 0xB20E, 0x87AB, 0xB20F, 0x87AC, 0xB210,\n\t0x87AD, 0xB211, 0x87AE, 0xB212, 0x87AF, 0xB213, 0x87B0, 0xB216,\n\t0x87B1, 0xB218, 0x87B2, 0xB21A, 0x87B3, 0xB21B, 0x87B4, 0xB21C,\n\t0x87B5, 0xB21D, 0x87B6, 0xB21E, 0x87B7, 0xB21F, 0x87B8, 0xB221,\n\t0x87B9, 0xB222, 0x87BA, 0xB223, 0x87BB, 0xB224, 0x87BC, 0xB225,\n\t0x87BD, 0xB226, 0x87BE, 0xB227, 0x87BF, 0xB228, 0x87C0, 0xB229,\n\t0x87C1, 0xB22A, 0x87C2, 0xB22B, 0x87C3, 0xB22C, 0x87C4, 0xB22D,\n\t0x87C5, 0xB22E, 0x87C6, 0xB22F, 0x87C7, 0xB230, 0x87C8, 0xB231,\n\t0x87C9, 0xB232, 0x87CA, 0xB233, 0x87CB, 0xB235, 0x87CC, 0xB236,\n\t0x87CD, 0xB237, 0x87CE, 0xB238, 0x87CF, 0xB239, 0x87D0, 0xB23A,\n\t0x87D1, 0xB23B, 0x87D2, 0xB23D, 0x87D3, 0xB23E, 0x87D4, 0xB23F,\n\t0x87D5, 0xB240, 0x87D6, 0xB241, 0x87D7, 0xB242, 0x87D8, 0xB243,\n\t0x87D9, 0xB244, 0x87DA, 0xB245, 0x87DB, 0xB246, 0x87DC, 0xB247,\n\t0x87DD, 0xB248, 0x87DE, 0xB249, 0x87DF, 0xB24A, 0x87E0, 0xB24B,\n\t0x87E1, 0xB24C, 0x87E2, 0xB24D, 0x87E3, 0xB24E, 0x87E4, 0xB24F,\n\t0x87E5, 0xB250, 0x87E6, 0xB251, 0x87E7, 0xB252, 0x87E8, 0xB253,\n\t0x87E9, 0xB254, 0x87EA, 0xB255, 0x87EB, 0xB256, 0x87EC, 0xB257,\n\t0x87ED, 0xB259, 0x87EE, 0xB25A, 0x87EF, 0xB25B, 0x87F0, 0xB25D,\n\t0x87F1, 0xB25E, 0x87F2, 0xB25F, 0x87F3, 0xB261, 0x87F4, 0xB262,\n\t0x87F5, 0xB263, 0x87F6, 0xB264, 0x87F7, 0xB265, 0x87F8, 0xB266,\n\t0x87F9, 0xB267, 0x87FA, 0xB26A, 0x87FB, 0xB26B, 0x87FC, 0xB26C,\n\t0x87FD, 0xB26D, 0x87FE, 0xB26E, 0x8841, 0xB26F, 0x8842, 0xB270,\n\t0x8843, 0xB271, 0x8844, 0xB272, 0x8845, 0xB273, 0x8846, 0xB276,\n\t0x8847, 0xB277, 0x8848, 0xB278, 0x8849, 0xB279, 0x884A, 0xB27A,\n\t0x884B, 0xB27B, 0x884C, 0xB27D, 0x884D, 0xB27E, 0x884E, 0xB27F,\n\t0x884F, 0xB280, 0x8850, 0xB281, 0x8851, 0xB282, 0x8852, 0xB283,\n\t0x8853, 0xB286, 0x8854, 0xB287, 0x8855, 0xB288, 0x8856, 0xB28A,\n\t0x8857, 0xB28B, 0x8858, 0xB28C, 0x8859, 0xB28D, 0x885A, 0xB28E,\n\t0x8861, 0xB28F, 0x8862, 0xB292, 0x8863, 0xB293, 0x8864, 0xB295,\n\t0x8865, 0xB296, 0x8866, 0xB297, 0x8867, 0xB29B, 0x8868, 0xB29C,\n\t0x8869, 0xB29D, 0x886A, 0xB29E, 0x886B, 0xB29F, 0x886C, 0xB2A2,\n\t0x886D, 0xB2A4, 0x886E, 0xB2A7, 0x886F, 0xB2A8, 0x8870, 0xB2A9,\n\t0x8871, 0xB2AB, 0x8872, 0xB2AD, 0x8873, 0xB2AE, 0x8874, 0xB2AF,\n\t0x8875, 0xB2B1, 0x8876, 0xB2B2, 0x8877, 0xB2B3, 0x8878, 0xB2B5,\n\t0x8879, 0xB2B6, 0x887A, 0xB2B7, 0x8881, 0xB2B8, 0x8882, 0xB2B9,\n\t0x8883, 0xB2BA, 0x8884, 0xB2BB, 0x8885, 0xB2BC, 0x8886, 0xB2BD,\n\t0x8887, 0xB2BE, 0x8888, 0xB2BF, 0x8889, 0xB2C0, 0x888A, 0xB2C1,\n\t0x888B, 0xB2C2, 0x888C, 0xB2C3, 0x888D, 0xB2C4, 0x888E, 0xB2C5,\n\t0x888F, 0xB2C6, 0x8890, 0xB2C7, 0x8891, 0xB2CA, 0x8892, 0xB2CB,\n\t0x8893, 0xB2CD, 0x8894, 0xB2CE, 0x8895, 0xB2CF, 0x8896, 0xB2D1,\n\t0x8897, 0xB2D3, 0x8898, 0xB2D4, 0x8899, 0xB2D5, 0x889A, 0xB2D6,\n\t0x889B, 0xB2D7, 0x889C, 0xB2DA, 0x889D, 0xB2DC, 0x889E, 0xB2DE,\n\t0x889F, 0xB2DF, 0x88A0, 0xB2E0, 0x88A1, 0xB2E1, 0x88A2, 0xB2E3,\n\t0x88A3, 0xB2E7, 0x88A4, 0xB2E9, 0x88A5, 0xB2EA, 0x88A6, 0xB2F0,\n\t0x88A7, 0xB2F1, 0x88A8, 0xB2F2, 0x88A9, 0xB2F6, 0x88AA, 0xB2FC,\n\t0x88AB, 0xB2FD, 0x88AC, 0xB2FE, 0x88AD, 0xB302, 0x88AE, 0xB303,\n\t0x88AF, 0xB305, 0x88B0, 0xB306, 0x88B1, 0xB307, 0x88B2, 0xB309,\n\t0x88B3, 0xB30A, 0x88B4, 0xB30B, 0x88B5, 0xB30C, 0x88B6, 0xB30D,\n\t0x88B7, 0xB30E, 0x88B8, 0xB30F, 0x88B9, 0xB312, 0x88BA, 0xB316,\n\t0x88BB, 0xB317, 0x88BC, 0xB318, 0x88BD, 0xB319, 0x88BE, 0xB31A,\n\t0x88BF, 0xB31B, 0x88C0, 0xB31D, 0x88C1, 0xB31E, 0x88C2, 0xB31F,\n\t0x88C3, 0xB320, 0x88C4, 0xB321, 0x88C5, 0xB322, 0x88C6, 0xB323,\n\t0x88C7, 0xB324, 0x88C8, 0xB325, 0x88C9, 0xB326, 0x88CA, 0xB327,\n\t0x88CB, 0xB328, 0x88CC, 0xB329, 0x88CD, 0xB32A, 0x88CE, 0xB32B,\n\t0x88CF, 0xB32C, 0x88D0, 0xB32D, 0x88D1, 0xB32E, 0x88D2, 0xB32F,\n\t0x88D3, 0xB330, 0x88D4, 0xB331, 0x88D5, 0xB332, 0x88D6, 0xB333,\n\t0x88D7, 0xB334, 0x88D8, 0xB335, 0x88D9, 0xB336, 0x88DA, 0xB337,\n\t0x88DB, 0xB338, 0x88DC, 0xB339, 0x88DD, 0xB33A, 0x88DE, 0xB33B,\n\t0x88DF, 0xB33C, 0x88E0, 0xB33D, 0x88E1, 0xB33E, 0x88E2, 0xB33F,\n\t0x88E3, 0xB340, 0x88E4, 0xB341, 0x88E5, 0xB342, 0x88E6, 0xB343,\n\t0x88E7, 0xB344, 0x88E8, 0xB345, 0x88E9, 0xB346, 0x88EA, 0xB347,\n\t0x88EB, 0xB348, 0x88EC, 0xB349, 0x88ED, 0xB34A, 0x88EE, 0xB34B,\n\t0x88EF, 0xB34C, 0x88F0, 0xB34D, 0x88F1, 0xB34E, 0x88F2, 0xB34F,\n\t0x88F3, 0xB350, 0x88F4, 0xB351, 0x88F5, 0xB352, 0x88F6, 0xB353,\n\t0x88F7, 0xB357, 0x88F8, 0xB359, 0x88F9, 0xB35A, 0x88FA, 0xB35D,\n\t0x88FB, 0xB360, 0x88FC, 0xB361, 0x88FD, 0xB362, 0x88FE, 0xB363,\n\t0x8941, 0xB366, 0x8942, 0xB368, 0x8943, 0xB36A, 0x8944, 0xB36C,\n\t0x8945, 0xB36D, 0x8946, 0xB36F, 0x8947, 0xB372, 0x8948, 0xB373,\n\t0x8949, 0xB375, 0x894A, 0xB376, 0x894B, 0xB377, 0x894C, 0xB379,\n\t0x894D, 0xB37A, 0x894E, 0xB37B, 0x894F, 0xB37C, 0x8950, 0xB37D,\n\t0x8951, 0xB37E, 0x8952, 0xB37F, 0x8953, 0xB382, 0x8954, 0xB386,\n\t0x8955, 0xB387, 0x8956, 0xB388, 0x8957, 0xB389, 0x8958, 0xB38A,\n\t0x8959, 0xB38B, 0x895A, 0xB38D, 0x8961, 0xB38E, 0x8962, 0xB38F,\n\t0x8963, 0xB391, 0x8964, 0xB392, 0x8965, 0xB393, 0x8966, 0xB395,\n\t0x8967, 0xB396, 0x8968, 0xB397, 0x8969, 0xB398, 0x896A, 0xB399,\n\t0x896B, 0xB39A, 0x896C, 0xB39B, 0x896D, 0xB39C, 0x896E, 0xB39D,\n\t0x896F, 0xB39E, 0x8970, 0xB39F, 0x8971, 0xB3A2, 0x8972, 0xB3A3,\n\t0x8973, 0xB3A4, 0x8974, 0xB3A5, 0x8975, 0xB3A6, 0x8976, 0xB3A7,\n\t0x8977, 0xB3A9, 0x8978, 0xB3AA, 0x8979, 0xB3AB, 0x897A, 0xB3AD,\n\t0x8981, 0xB3AE, 0x8982, 0xB3AF, 0x8983, 0xB3B0, 0x8984, 0xB3B1,\n\t0x8985, 0xB3B2, 0x8986, 0xB3B3, 0x8987, 0xB3B4, 0x8988, 0xB3B5,\n\t0x8989, 0xB3B6, 0x898A, 0xB3B7, 0x898B, 0xB3B8, 0x898C, 0xB3B9,\n\t0x898D, 0xB3BA, 0x898E, 0xB3BB, 0x898F, 0xB3BC, 0x8990, 0xB3BD,\n\t0x8991, 0xB3BE, 0x8992, 0xB3BF, 0x8993, 0xB3C0, 0x8994, 0xB3C1,\n\t0x8995, 0xB3C2, 0x8996, 0xB3C3, 0x8997, 0xB3C6, 0x8998, 0xB3C7,\n\t0x8999, 0xB3C9, 0x899A, 0xB3CA, 0x899B, 0xB3CD, 0x899C, 0xB3CF,\n\t0x899D, 0xB3D1, 0x899E, 0xB3D2, 0x899F, 0xB3D3, 0x89A0, 0xB3D6,\n\t0x89A1, 0xB3D8, 0x89A2, 0xB3DA, 0x89A3, 0xB3DC, 0x89A4, 0xB3DE,\n\t0x89A5, 0xB3DF, 0x89A6, 0xB3E1, 0x89A7, 0xB3E2, 0x89A8, 0xB3E3,\n\t0x89A9, 0xB3E5, 0x89AA, 0xB3E6, 0x89AB, 0xB3E7, 0x89AC, 0xB3E9,\n\t0x89AD, 0xB3EA, 0x89AE, 0xB3EB, 0x89AF, 0xB3EC, 0x89B0, 0xB3ED,\n\t0x89B1, 0xB3EE, 0x89B2, 0xB3EF, 0x89B3, 0xB3F0, 0x89B4, 0xB3F1,\n\t0x89B5, 0xB3F2, 0x89B6, 0xB3F3, 0x89B7, 0xB3F4, 0x89B8, 0xB3F5,\n\t0x89B9, 0xB3F6, 0x89BA, 0xB3F7, 0x89BB, 0xB3F8, 0x89BC, 0xB3F9,\n\t0x89BD, 0xB3FA, 0x89BE, 0xB3FB, 0x89BF, 0xB3FD, 0x89C0, 0xB3FE,\n\t0x89C1, 0xB3FF, 0x89C2, 0xB400, 0x89C3, 0xB401, 0x89C4, 0xB402,\n\t0x89C5, 0xB403, 0x89C6, 0xB404, 0x89C7, 0xB405, 0x89C8, 0xB406,\n\t0x89C9, 0xB407, 0x89CA, 0xB408, 0x89CB, 0xB409, 0x89CC, 0xB40A,\n\t0x89CD, 0xB40B, 0x89CE, 0xB40C, 0x89CF, 0xB40D, 0x89D0, 0xB40E,\n\t0x89D1, 0xB40F, 0x89D2, 0xB411, 0x89D3, 0xB412, 0x89D4, 0xB413,\n\t0x89D5, 0xB414, 0x89D6, 0xB415, 0x89D7, 0xB416, 0x89D8, 0xB417,\n\t0x89D9, 0xB419, 0x89DA, 0xB41A, 0x89DB, 0xB41B, 0x89DC, 0xB41D,\n\t0x89DD, 0xB41E, 0x89DE, 0xB41F, 0x89DF, 0xB421, 0x89E0, 0xB422,\n\t0x89E1, 0xB423, 0x89E2, 0xB424, 0x89E3, 0xB425, 0x89E4, 0xB426,\n\t0x89E5, 0xB427, 0x89E6, 0xB42A, 0x89E7, 0xB42C, 0x89E8, 0xB42D,\n\t0x89E9, 0xB42E, 0x89EA, 0xB42F, 0x89EB, 0xB430, 0x89EC, 0xB431,\n\t0x89ED, 0xB432, 0x89EE, 0xB433, 0x89EF, 0xB435, 0x89F0, 0xB436,\n\t0x89F1, 0xB437, 0x89F2, 0xB438, 0x89F3, 0xB439, 0x89F4, 0xB43A,\n\t0x89F5, 0xB43B, 0x89F6, 0xB43C, 0x89F7, 0xB43D, 0x89F8, 0xB43E,\n\t0x89F9, 0xB43F, 0x89FA, 0xB440, 0x89FB, 0xB441, 0x89FC, 0xB442,\n\t0x89FD, 0xB443, 0x89FE, 0xB444, 0x8A41, 0xB445, 0x8A42, 0xB446,\n\t0x8A43, 0xB447, 0x8A44, 0xB448, 0x8A45, 0xB449, 0x8A46, 0xB44A,\n\t0x8A47, 0xB44B, 0x8A48, 0xB44C, 0x8A49, 0xB44D, 0x8A4A, 0xB44E,\n\t0x8A4B, 0xB44F, 0x8A4C, 0xB452, 0x8A4D, 0xB453, 0x8A4E, 0xB455,\n\t0x8A4F, 0xB456, 0x8A50, 0xB457, 0x8A51, 0xB459, 0x8A52, 0xB45A,\n\t0x8A53, 0xB45B, 0x8A54, 0xB45C, 0x8A55, 0xB45D, 0x8A56, 0xB45E,\n\t0x8A57, 0xB45F, 0x8A58, 0xB462, 0x8A59, 0xB464, 0x8A5A, 0xB466,\n\t0x8A61, 0xB467, 0x8A62, 0xB468, 0x8A63, 0xB469, 0x8A64, 0xB46A,\n\t0x8A65, 0xB46B, 0x8A66, 0xB46D, 0x8A67, 0xB46E, 0x8A68, 0xB46F,\n\t0x8A69, 0xB470, 0x8A6A, 0xB471, 0x8A6B, 0xB472, 0x8A6C, 0xB473,\n\t0x8A6D, 0xB474, 0x8A6E, 0xB475, 0x8A6F, 0xB476, 0x8A70, 0xB477,\n\t0x8A71, 0xB478, 0x8A72, 0xB479, 0x8A73, 0xB47A, 0x8A74, 0xB47B,\n\t0x8A75, 0xB47C, 0x8A76, 0xB47D, 0x8A77, 0xB47E, 0x8A78, 0xB47F,\n\t0x8A79, 0xB481, 0x8A7A, 0xB482, 0x8A81, 0xB483, 0x8A82, 0xB484,\n\t0x8A83, 0xB485, 0x8A84, 0xB486, 0x8A85, 0xB487, 0x8A86, 0xB489,\n\t0x8A87, 0xB48A, 0x8A88, 0xB48B, 0x8A89, 0xB48C, 0x8A8A, 0xB48D,\n\t0x8A8B, 0xB48E, 0x8A8C, 0xB48F, 0x8A8D, 0xB490, 0x8A8E, 0xB491,\n\t0x8A8F, 0xB492, 0x8A90, 0xB493, 0x8A91, 0xB494, 0x8A92, 0xB495,\n\t0x8A93, 0xB496, 0x8A94, 0xB497, 0x8A95, 0xB498, 0x8A96, 0xB499,\n\t0x8A97, 0xB49A, 0x8A98, 0xB49B, 0x8A99, 0xB49C, 0x8A9A, 0xB49E,\n\t0x8A9B, 0xB49F, 0x8A9C, 0xB4A0, 0x8A9D, 0xB4A1, 0x8A9E, 0xB4A2,\n\t0x8A9F, 0xB4A3, 0x8AA0, 0xB4A5, 0x8AA1, 0xB4A6, 0x8AA2, 0xB4A7,\n\t0x8AA3, 0xB4A9, 0x8AA4, 0xB4AA, 0x8AA5, 0xB4AB, 0x8AA6, 0xB4AD,\n\t0x8AA7, 0xB4AE, 0x8AA8, 0xB4AF, 0x8AA9, 0xB4B0, 0x8AAA, 0xB4B1,\n\t0x8AAB, 0xB4B2, 0x8AAC, 0xB4B3, 0x8AAD, 0xB4B4, 0x8AAE, 0xB4B6,\n\t0x8AAF, 0xB4B8, 0x8AB0, 0xB4BA, 0x8AB1, 0xB4BB, 0x8AB2, 0xB4BC,\n\t0x8AB3, 0xB4BD, 0x8AB4, 0xB4BE, 0x8AB5, 0xB4BF, 0x8AB6, 0xB4C1,\n\t0x8AB7, 0xB4C2, 0x8AB8, 0xB4C3, 0x8AB9, 0xB4C5, 0x8ABA, 0xB4C6,\n\t0x8ABB, 0xB4C7, 0x8ABC, 0xB4C9, 0x8ABD, 0xB4CA, 0x8ABE, 0xB4CB,\n\t0x8ABF, 0xB4CC, 0x8AC0, 0xB4CD, 0x8AC1, 0xB4CE, 0x8AC2, 0xB4CF,\n\t0x8AC3, 0xB4D1, 0x8AC4, 0xB4D2, 0x8AC5, 0xB4D3, 0x8AC6, 0xB4D4,\n\t0x8AC7, 0xB4D6, 0x8AC8, 0xB4D7, 0x8AC9, 0xB4D8, 0x8ACA, 0xB4D9,\n\t0x8ACB, 0xB4DA, 0x8ACC, 0xB4DB, 0x8ACD, 0xB4DE, 0x8ACE, 0xB4DF,\n\t0x8ACF, 0xB4E1, 0x8AD0, 0xB4E2, 0x8AD1, 0xB4E5, 0x8AD2, 0xB4E7,\n\t0x8AD3, 0xB4E8, 0x8AD4, 0xB4E9, 0x8AD5, 0xB4EA, 0x8AD6, 0xB4EB,\n\t0x8AD7, 0xB4EE, 0x8AD8, 0xB4F0, 0x8AD9, 0xB4F2, 0x8ADA, 0xB4F3,\n\t0x8ADB, 0xB4F4, 0x8ADC, 0xB4F5, 0x8ADD, 0xB4F6, 0x8ADE, 0xB4F7,\n\t0x8ADF, 0xB4F9, 0x8AE0, 0xB4FA, 0x8AE1, 0xB4FB, 0x8AE2, 0xB4FC,\n\t0x8AE3, 0xB4FD, 0x8AE4, 0xB4FE, 0x8AE5, 0xB4FF, 0x8AE6, 0xB500,\n\t0x8AE7, 0xB501, 0x8AE8, 0xB502, 0x8AE9, 0xB503, 0x8AEA, 0xB504,\n\t0x8AEB, 0xB505, 0x8AEC, 0xB506, 0x8AED, 0xB507, 0x8AEE, 0xB508,\n\t0x8AEF, 0xB509, 0x8AF0, 0xB50A, 0x8AF1, 0xB50B, 0x8AF2, 0xB50C,\n\t0x8AF3, 0xB50D, 0x8AF4, 0xB50E, 0x8AF5, 0xB50F, 0x8AF6, 0xB510,\n\t0x8AF7, 0xB511, 0x8AF8, 0xB512, 0x8AF9, 0xB513, 0x8AFA, 0xB516,\n\t0x8AFB, 0xB517, 0x8AFC, 0xB519, 0x8AFD, 0xB51A, 0x8AFE, 0xB51D,\n\t0x8B41, 0xB51E, 0x8B42, 0xB51F, 0x8B43, 0xB520, 0x8B44, 0xB521,\n\t0x8B45, 0xB522, 0x8B46, 0xB523, 0x8B47, 0xB526, 0x8B48, 0xB52B,\n\t0x8B49, 0xB52C, 0x8B4A, 0xB52D, 0x8B4B, 0xB52E, 0x8B4C, 0xB52F,\n\t0x8B4D, 0xB532, 0x8B4E, 0xB533, 0x8B4F, 0xB535, 0x8B50, 0xB536,\n\t0x8B51, 0xB537, 0x8B52, 0xB539, 0x8B53, 0xB53A, 0x8B54, 0xB53B,\n\t0x8B55, 0xB53C, 0x8B56, 0xB53D, 0x8B57, 0xB53E, 0x8B58, 0xB53F,\n\t0x8B59, 0xB542, 0x8B5A, 0xB546, 0x8B61, 0xB547, 0x8B62, 0xB548,\n\t0x8B63, 0xB549, 0x8B64, 0xB54A, 0x8B65, 0xB54E, 0x8B66, 0xB54F,\n\t0x8B67, 0xB551, 0x8B68, 0xB552, 0x8B69, 0xB553, 0x8B6A, 0xB555,\n\t0x8B6B, 0xB556, 0x8B6C, 0xB557, 0x8B6D, 0xB558, 0x8B6E, 0xB559,\n\t0x8B6F, 0xB55A, 0x8B70, 0xB55B, 0x8B71, 0xB55E, 0x8B72, 0xB562,\n\t0x8B73, 0xB563, 0x8B74, 0xB564, 0x8B75, 0xB565, 0x8B76, 0xB566,\n\t0x8B77, 0xB567, 0x8B78, 0xB568, 0x8B79, 0xB569, 0x8B7A, 0xB56A,\n\t0x8B81, 0xB56B, 0x8B82, 0xB56C, 0x8B83, 0xB56D, 0x8B84, 0xB56E,\n\t0x8B85, 0xB56F, 0x8B86, 0xB570, 0x8B87, 0xB571, 0x8B88, 0xB572,\n\t0x8B89, 0xB573, 0x8B8A, 0xB574, 0x8B8B, 0xB575, 0x8B8C, 0xB576,\n\t0x8B8D, 0xB577, 0x8B8E, 0xB578, 0x8B8F, 0xB579, 0x8B90, 0xB57A,\n\t0x8B91, 0xB57B, 0x8B92, 0xB57C, 0x8B93, 0xB57D, 0x8B94, 0xB57E,\n\t0x8B95, 0xB57F, 0x8B96, 0xB580, 0x8B97, 0xB581, 0x8B98, 0xB582,\n\t0x8B99, 0xB583, 0x8B9A, 0xB584, 0x8B9B, 0xB585, 0x8B9C, 0xB586,\n\t0x8B9D, 0xB587, 0x8B9E, 0xB588, 0x8B9F, 0xB589, 0x8BA0, 0xB58A,\n\t0x8BA1, 0xB58B, 0x8BA2, 0xB58C, 0x8BA3, 0xB58D, 0x8BA4, 0xB58E,\n\t0x8BA5, 0xB58F, 0x8BA6, 0xB590, 0x8BA7, 0xB591, 0x8BA8, 0xB592,\n\t0x8BA9, 0xB593, 0x8BAA, 0xB594, 0x8BAB, 0xB595, 0x8BAC, 0xB596,\n\t0x8BAD, 0xB597, 0x8BAE, 0xB598, 0x8BAF, 0xB599, 0x8BB0, 0xB59A,\n\t0x8BB1, 0xB59B, 0x8BB2, 0xB59C, 0x8BB3, 0xB59D, 0x8BB4, 0xB59E,\n\t0x8BB5, 0xB59F, 0x8BB6, 0xB5A2, 0x8BB7, 0xB5A3, 0x8BB8, 0xB5A5,\n\t0x8BB9, 0xB5A6, 0x8BBA, 0xB5A7, 0x8BBB, 0xB5A9, 0x8BBC, 0xB5AC,\n\t0x8BBD, 0xB5AD, 0x8BBE, 0xB5AE, 0x8BBF, 0xB5AF, 0x8BC0, 0xB5B2,\n\t0x8BC1, 0xB5B6, 0x8BC2, 0xB5B7, 0x8BC3, 0xB5B8, 0x8BC4, 0xB5B9,\n\t0x8BC5, 0xB5BA, 0x8BC6, 0xB5BE, 0x8BC7, 0xB5BF, 0x8BC8, 0xB5C1,\n\t0x8BC9, 0xB5C2, 0x8BCA, 0xB5C3, 0x8BCB, 0xB5C5, 0x8BCC, 0xB5C6,\n\t0x8BCD, 0xB5C7, 0x8BCE, 0xB5C8, 0x8BCF, 0xB5C9, 0x8BD0, 0xB5CA,\n\t0x8BD1, 0xB5CB, 0x8BD2, 0xB5CE, 0x8BD3, 0xB5D2, 0x8BD4, 0xB5D3,\n\t0x8BD5, 0xB5D4, 0x8BD6, 0xB5D5, 0x8BD7, 0xB5D6, 0x8BD8, 0xB5D7,\n\t0x8BD9, 0xB5D9, 0x8BDA, 0xB5DA, 0x8BDB, 0xB5DB, 0x8BDC, 0xB5DC,\n\t0x8BDD, 0xB5DD, 0x8BDE, 0xB5DE, 0x8BDF, 0xB5DF, 0x8BE0, 0xB5E0,\n\t0x8BE1, 0xB5E1, 0x8BE2, 0xB5E2, 0x8BE3, 0xB5E3, 0x8BE4, 0xB5E4,\n\t0x8BE5, 0xB5E5, 0x8BE6, 0xB5E6, 0x8BE7, 0xB5E7, 0x8BE8, 0xB5E8,\n\t0x8BE9, 0xB5E9, 0x8BEA, 0xB5EA, 0x8BEB, 0xB5EB, 0x8BEC, 0xB5ED,\n\t0x8BED, 0xB5EE, 0x8BEE, 0xB5EF, 0x8BEF, 0xB5F0, 0x8BF0, 0xB5F1,\n\t0x8BF1, 0xB5F2, 0x8BF2, 0xB5F3, 0x8BF3, 0xB5F4, 0x8BF4, 0xB5F5,\n\t0x8BF5, 0xB5F6, 0x8BF6, 0xB5F7, 0x8BF7, 0xB5F8, 0x8BF8, 0xB5F9,\n\t0x8BF9, 0xB5FA, 0x8BFA, 0xB5FB, 0x8BFB, 0xB5FC, 0x8BFC, 0xB5FD,\n\t0x8BFD, 0xB5FE, 0x8BFE, 0xB5FF, 0x8C41, 0xB600, 0x8C42, 0xB601,\n\t0x8C43, 0xB602, 0x8C44, 0xB603, 0x8C45, 0xB604, 0x8C46, 0xB605,\n\t0x8C47, 0xB606, 0x8C48, 0xB607, 0x8C49, 0xB608, 0x8C4A, 0xB609,\n\t0x8C4B, 0xB60A, 0x8C4C, 0xB60B, 0x8C4D, 0xB60C, 0x8C4E, 0xB60D,\n\t0x8C4F, 0xB60E, 0x8C50, 0xB60F, 0x8C51, 0xB612, 0x8C52, 0xB613,\n\t0x8C53, 0xB615, 0x8C54, 0xB616, 0x8C55, 0xB617, 0x8C56, 0xB619,\n\t0x8C57, 0xB61A, 0x8C58, 0xB61B, 0x8C59, 0xB61C, 0x8C5A, 0xB61D,\n\t0x8C61, 0xB61E, 0x8C62, 0xB61F, 0x8C63, 0xB620, 0x8C64, 0xB621,\n\t0x8C65, 0xB622, 0x8C66, 0xB623, 0x8C67, 0xB624, 0x8C68, 0xB626,\n\t0x8C69, 0xB627, 0x8C6A, 0xB628, 0x8C6B, 0xB629, 0x8C6C, 0xB62A,\n\t0x8C6D, 0xB62B, 0x8C6E, 0xB62D, 0x8C6F, 0xB62E, 0x8C70, 0xB62F,\n\t0x8C71, 0xB630, 0x8C72, 0xB631, 0x8C73, 0xB632, 0x8C74, 0xB633,\n\t0x8C75, 0xB635, 0x8C76, 0xB636, 0x8C77, 0xB637, 0x8C78, 0xB638,\n\t0x8C79, 0xB639, 0x8C7A, 0xB63A, 0x8C81, 0xB63B, 0x8C82, 0xB63C,\n\t0x8C83, 0xB63D, 0x8C84, 0xB63E, 0x8C85, 0xB63F, 0x8C86, 0xB640,\n\t0x8C87, 0xB641, 0x8C88, 0xB642, 0x8C89, 0xB643, 0x8C8A, 0xB644,\n\t0x8C8B, 0xB645, 0x8C8C, 0xB646, 0x8C8D, 0xB647, 0x8C8E, 0xB649,\n\t0x8C8F, 0xB64A, 0x8C90, 0xB64B, 0x8C91, 0xB64C, 0x8C92, 0xB64D,\n\t0x8C93, 0xB64E, 0x8C94, 0xB64F, 0x8C95, 0xB650, 0x8C96, 0xB651,\n\t0x8C97, 0xB652, 0x8C98, 0xB653, 0x8C99, 0xB654, 0x8C9A, 0xB655,\n\t0x8C9B, 0xB656, 0x8C9C, 0xB657, 0x8C9D, 0xB658, 0x8C9E, 0xB659,\n\t0x8C9F, 0xB65A, 0x8CA0, 0xB65B, 0x8CA1, 0xB65C, 0x8CA2, 0xB65D,\n\t0x8CA3, 0xB65E, 0x8CA4, 0xB65F, 0x8CA5, 0xB660, 0x8CA6, 0xB661,\n\t0x8CA7, 0xB662, 0x8CA8, 0xB663, 0x8CA9, 0xB665, 0x8CAA, 0xB666,\n\t0x8CAB, 0xB667, 0x8CAC, 0xB669, 0x8CAD, 0xB66A, 0x8CAE, 0xB66B,\n\t0x8CAF, 0xB66C, 0x8CB0, 0xB66D, 0x8CB1, 0xB66E, 0x8CB2, 0xB66F,\n\t0x8CB3, 0xB670, 0x8CB4, 0xB671, 0x8CB5, 0xB672, 0x8CB6, 0xB673,\n\t0x8CB7, 0xB674, 0x8CB8, 0xB675, 0x8CB9, 0xB676, 0x8CBA, 0xB677,\n\t0x8CBB, 0xB678, 0x8CBC, 0xB679, 0x8CBD, 0xB67A, 0x8CBE, 0xB67B,\n\t0x8CBF, 0xB67C, 0x8CC0, 0xB67D, 0x8CC1, 0xB67E, 0x8CC2, 0xB67F,\n\t0x8CC3, 0xB680, 0x8CC4, 0xB681, 0x8CC5, 0xB682, 0x8CC6, 0xB683,\n\t0x8CC7, 0xB684, 0x8CC8, 0xB685, 0x8CC9, 0xB686, 0x8CCA, 0xB687,\n\t0x8CCB, 0xB688, 0x8CCC, 0xB689, 0x8CCD, 0xB68A, 0x8CCE, 0xB68B,\n\t0x8CCF, 0xB68C, 0x8CD0, 0xB68D, 0x8CD1, 0xB68E, 0x8CD2, 0xB68F,\n\t0x8CD3, 0xB690, 0x8CD4, 0xB691, 0x8CD5, 0xB692, 0x8CD6, 0xB693,\n\t0x8CD7, 0xB694, 0x8CD8, 0xB695, 0x8CD9, 0xB696, 0x8CDA, 0xB697,\n\t0x8CDB, 0xB698, 0x8CDC, 0xB699, 0x8CDD, 0xB69A, 0x8CDE, 0xB69B,\n\t0x8CDF, 0xB69E, 0x8CE0, 0xB69F, 0x8CE1, 0xB6A1, 0x8CE2, 0xB6A2,\n\t0x8CE3, 0xB6A3, 0x8CE4, 0xB6A5, 0x8CE5, 0xB6A6, 0x8CE6, 0xB6A7,\n\t0x8CE7, 0xB6A8, 0x8CE8, 0xB6A9, 0x8CE9, 0xB6AA, 0x8CEA, 0xB6AD,\n\t0x8CEB, 0xB6AE, 0x8CEC, 0xB6AF, 0x8CED, 0xB6B0, 0x8CEE, 0xB6B2,\n\t0x8CEF, 0xB6B3, 0x8CF0, 0xB6B4, 0x8CF1, 0xB6B5, 0x8CF2, 0xB6B6,\n\t0x8CF3, 0xB6B7, 0x8CF4, 0xB6B8, 0x8CF5, 0xB6B9, 0x8CF6, 0xB6BA,\n\t0x8CF7, 0xB6BB, 0x8CF8, 0xB6BC, 0x8CF9, 0xB6BD, 0x8CFA, 0xB6BE,\n\t0x8CFB, 0xB6BF, 0x8CFC, 0xB6C0, 0x8CFD, 0xB6C1, 0x8CFE, 0xB6C2,\n\t0x8D41, 0xB6C3, 0x8D42, 0xB6C4, 0x8D43, 0xB6C5, 0x8D44, 0xB6C6,\n\t0x8D45, 0xB6C7, 0x8D46, 0xB6C8, 0x8D47, 0xB6C9, 0x8D48, 0xB6CA,\n\t0x8D49, 0xB6CB, 0x8D4A, 0xB6CC, 0x8D4B, 0xB6CD, 0x8D4C, 0xB6CE,\n\t0x8D4D, 0xB6CF, 0x8D4E, 0xB6D0, 0x8D4F, 0xB6D1, 0x8D50, 0xB6D2,\n\t0x8D51, 0xB6D3, 0x8D52, 0xB6D5, 0x8D53, 0xB6D6, 0x8D54, 0xB6D7,\n\t0x8D55, 0xB6D8, 0x8D56, 0xB6D9, 0x8D57, 0xB6DA, 0x8D58, 0xB6DB,\n\t0x8D59, 0xB6DC, 0x8D5A, 0xB6DD, 0x8D61, 0xB6DE, 0x8D62, 0xB6DF,\n\t0x8D63, 0xB6E0, 0x8D64, 0xB6E1, 0x8D65, 0xB6E2, 0x8D66, 0xB6E3,\n\t0x8D67, 0xB6E4, 0x8D68, 0xB6E5, 0x8D69, 0xB6E6, 0x8D6A, 0xB6E7,\n\t0x8D6B, 0xB6E8, 0x8D6C, 0xB6E9, 0x8D6D, 0xB6EA, 0x8D6E, 0xB6EB,\n\t0x8D6F, 0xB6EC, 0x8D70, 0xB6ED, 0x8D71, 0xB6EE, 0x8D72, 0xB6EF,\n\t0x8D73, 0xB6F1, 0x8D74, 0xB6F2, 0x8D75, 0xB6F3, 0x8D76, 0xB6F5,\n\t0x8D77, 0xB6F6, 0x8D78, 0xB6F7, 0x8D79, 0xB6F9, 0x8D7A, 0xB6FA,\n\t0x8D81, 0xB6FB, 0x8D82, 0xB6FC, 0x8D83, 0xB6FD, 0x8D84, 0xB6FE,\n\t0x8D85, 0xB6FF, 0x8D86, 0xB702, 0x8D87, 0xB703, 0x8D88, 0xB704,\n\t0x8D89, 0xB706, 0x8D8A, 0xB707, 0x8D8B, 0xB708, 0x8D8C, 0xB709,\n\t0x8D8D, 0xB70A, 0x8D8E, 0xB70B, 0x8D8F, 0xB70C, 0x8D90, 0xB70D,\n\t0x8D91, 0xB70E, 0x8D92, 0xB70F, 0x8D93, 0xB710, 0x8D94, 0xB711,\n\t0x8D95, 0xB712, 0x8D96, 0xB713, 0x8D97, 0xB714, 0x8D98, 0xB715,\n\t0x8D99, 0xB716, 0x8D9A, 0xB717, 0x8D9B, 0xB718, 0x8D9C, 0xB719,\n\t0x8D9D, 0xB71A, 0x8D9E, 0xB71B, 0x8D9F, 0xB71C, 0x8DA0, 0xB71D,\n\t0x8DA1, 0xB71E, 0x8DA2, 0xB71F, 0x8DA3, 0xB720, 0x8DA4, 0xB721,\n\t0x8DA5, 0xB722, 0x8DA6, 0xB723, 0x8DA7, 0xB724, 0x8DA8, 0xB725,\n\t0x8DA9, 0xB726, 0x8DAA, 0xB727, 0x8DAB, 0xB72A, 0x8DAC, 0xB72B,\n\t0x8DAD, 0xB72D, 0x8DAE, 0xB72E, 0x8DAF, 0xB731, 0x8DB0, 0xB732,\n\t0x8DB1, 0xB733, 0x8DB2, 0xB734, 0x8DB3, 0xB735, 0x8DB4, 0xB736,\n\t0x8DB5, 0xB737, 0x8DB6, 0xB73A, 0x8DB7, 0xB73C, 0x8DB8, 0xB73D,\n\t0x8DB9, 0xB73E, 0x8DBA, 0xB73F, 0x8DBB, 0xB740, 0x8DBC, 0xB741,\n\t0x8DBD, 0xB742, 0x8DBE, 0xB743, 0x8DBF, 0xB745, 0x8DC0, 0xB746,\n\t0x8DC1, 0xB747, 0x8DC2, 0xB749, 0x8DC3, 0xB74A, 0x8DC4, 0xB74B,\n\t0x8DC5, 0xB74D, 0x8DC6, 0xB74E, 0x8DC7, 0xB74F, 0x8DC8, 0xB750,\n\t0x8DC9, 0xB751, 0x8DCA, 0xB752, 0x8DCB, 0xB753, 0x8DCC, 0xB756,\n\t0x8DCD, 0xB757, 0x8DCE, 0xB758, 0x8DCF, 0xB759, 0x8DD0, 0xB75A,\n\t0x8DD1, 0xB75B, 0x8DD2, 0xB75C, 0x8DD3, 0xB75D, 0x8DD4, 0xB75E,\n\t0x8DD5, 0xB75F, 0x8DD6, 0xB761, 0x8DD7, 0xB762, 0x8DD8, 0xB763,\n\t0x8DD9, 0xB765, 0x8DDA, 0xB766, 0x8DDB, 0xB767, 0x8DDC, 0xB769,\n\t0x8DDD, 0xB76A, 0x8DDE, 0xB76B, 0x8DDF, 0xB76C, 0x8DE0, 0xB76D,\n\t0x8DE1, 0xB76E, 0x8DE2, 0xB76F, 0x8DE3, 0xB772, 0x8DE4, 0xB774,\n\t0x8DE5, 0xB776, 0x8DE6, 0xB777, 0x8DE7, 0xB778, 0x8DE8, 0xB779,\n\t0x8DE9, 0xB77A, 0x8DEA, 0xB77B, 0x8DEB, 0xB77E, 0x8DEC, 0xB77F,\n\t0x8DED, 0xB781, 0x8DEE, 0xB782, 0x8DEF, 0xB783, 0x8DF0, 0xB785,\n\t0x8DF1, 0xB786, 0x8DF2, 0xB787, 0x8DF3, 0xB788, 0x8DF4, 0xB789,\n\t0x8DF5, 0xB78A, 0x8DF6, 0xB78B, 0x8DF7, 0xB78E, 0x8DF8, 0xB793,\n\t0x8DF9, 0xB794, 0x8DFA, 0xB795, 0x8DFB, 0xB79A, 0x8DFC, 0xB79B,\n\t0x8DFD, 0xB79D, 0x8DFE, 0xB79E, 0x8E41, 0xB79F, 0x8E42, 0xB7A1,\n\t0x8E43, 0xB7A2, 0x8E44, 0xB7A3, 0x8E45, 0xB7A4, 0x8E46, 0xB7A5,\n\t0x8E47, 0xB7A6, 0x8E48, 0xB7A7, 0x8E49, 0xB7AA, 0x8E4A, 0xB7AE,\n\t0x8E4B, 0xB7AF, 0x8E4C, 0xB7B0, 0x8E4D, 0xB7B1, 0x8E4E, 0xB7B2,\n\t0x8E4F, 0xB7B3, 0x8E50, 0xB7B6, 0x8E51, 0xB7B7, 0x8E52, 0xB7B9,\n\t0x8E53, 0xB7BA, 0x8E54, 0xB7BB, 0x8E55, 0xB7BC, 0x8E56, 0xB7BD,\n\t0x8E57, 0xB7BE, 0x8E58, 0xB7BF, 0x8E59, 0xB7C0, 0x8E5A, 0xB7C1,\n\t0x8E61, 0xB7C2, 0x8E62, 0xB7C3, 0x8E63, 0xB7C4, 0x8E64, 0xB7C5,\n\t0x8E65, 0xB7C6, 0x8E66, 0xB7C8, 0x8E67, 0xB7CA, 0x8E68, 0xB7CB,\n\t0x8E69, 0xB7CC, 0x8E6A, 0xB7CD, 0x8E6B, 0xB7CE, 0x8E6C, 0xB7CF,\n\t0x8E6D, 0xB7D0, 0x8E6E, 0xB7D1, 0x8E6F, 0xB7D2, 0x8E70, 0xB7D3,\n\t0x8E71, 0xB7D4, 0x8E72, 0xB7D5, 0x8E73, 0xB7D6, 0x8E74, 0xB7D7,\n\t0x8E75, 0xB7D8, 0x8E76, 0xB7D9, 0x8E77, 0xB7DA, 0x8E78, 0xB7DB,\n\t0x8E79, 0xB7DC, 0x8E7A, 0xB7DD, 0x8E81, 0xB7DE, 0x8E82, 0xB7DF,\n\t0x8E83, 0xB7E0, 0x8E84, 0xB7E1, 0x8E85, 0xB7E2, 0x8E86, 0xB7E3,\n\t0x8E87, 0xB7E4, 0x8E88, 0xB7E5, 0x8E89, 0xB7E6, 0x8E8A, 0xB7E7,\n\t0x8E8B, 0xB7E8, 0x8E8C, 0xB7E9, 0x8E8D, 0xB7EA, 0x8E8E, 0xB7EB,\n\t0x8E8F, 0xB7EE, 0x8E90, 0xB7EF, 0x8E91, 0xB7F1, 0x8E92, 0xB7F2,\n\t0x8E93, 0xB7F3, 0x8E94, 0xB7F5, 0x8E95, 0xB7F6, 0x8E96, 0xB7F7,\n\t0x8E97, 0xB7F8, 0x8E98, 0xB7F9, 0x8E99, 0xB7FA, 0x8E9A, 0xB7FB,\n\t0x8E9B, 0xB7FE, 0x8E9C, 0xB802, 0x8E9D, 0xB803, 0x8E9E, 0xB804,\n\t0x8E9F, 0xB805, 0x8EA0, 0xB806, 0x8EA1, 0xB80A, 0x8EA2, 0xB80B,\n\t0x8EA3, 0xB80D, 0x8EA4, 0xB80E, 0x8EA5, 0xB80F, 0x8EA6, 0xB811,\n\t0x8EA7, 0xB812, 0x8EA8, 0xB813, 0x8EA9, 0xB814, 0x8EAA, 0xB815,\n\t0x8EAB, 0xB816, 0x8EAC, 0xB817, 0x8EAD, 0xB81A, 0x8EAE, 0xB81C,\n\t0x8EAF, 0xB81E, 0x8EB0, 0xB81F, 0x8EB1, 0xB820, 0x8EB2, 0xB821,\n\t0x8EB3, 0xB822, 0x8EB4, 0xB823, 0x8EB5, 0xB826, 0x8EB6, 0xB827,\n\t0x8EB7, 0xB829, 0x8EB8, 0xB82A, 0x8EB9, 0xB82B, 0x8EBA, 0xB82D,\n\t0x8EBB, 0xB82E, 0x8EBC, 0xB82F, 0x8EBD, 0xB830, 0x8EBE, 0xB831,\n\t0x8EBF, 0xB832, 0x8EC0, 0xB833, 0x8EC1, 0xB836, 0x8EC2, 0xB83A,\n\t0x8EC3, 0xB83B, 0x8EC4, 0xB83C, 0x8EC5, 0xB83D, 0x8EC6, 0xB83E,\n\t0x8EC7, 0xB83F, 0x8EC8, 0xB841, 0x8EC9, 0xB842, 0x8ECA, 0xB843,\n\t0x8ECB, 0xB845, 0x8ECC, 0xB846, 0x8ECD, 0xB847, 0x8ECE, 0xB848,\n\t0x8ECF, 0xB849, 0x8ED0, 0xB84A, 0x8ED1, 0xB84B, 0x8ED2, 0xB84C,\n\t0x8ED3, 0xB84D, 0x8ED4, 0xB84E, 0x8ED5, 0xB84F, 0x8ED6, 0xB850,\n\t0x8ED7, 0xB852, 0x8ED8, 0xB854, 0x8ED9, 0xB855, 0x8EDA, 0xB856,\n\t0x8EDB, 0xB857, 0x8EDC, 0xB858, 0x8EDD, 0xB859, 0x8EDE, 0xB85A,\n\t0x8EDF, 0xB85B, 0x8EE0, 0xB85E, 0x8EE1, 0xB85F, 0x8EE2, 0xB861,\n\t0x8EE3, 0xB862, 0x8EE4, 0xB863, 0x8EE5, 0xB865, 0x8EE6, 0xB866,\n\t0x8EE7, 0xB867, 0x8EE8, 0xB868, 0x8EE9, 0xB869, 0x8EEA, 0xB86A,\n\t0x8EEB, 0xB86B, 0x8EEC, 0xB86E, 0x8EED, 0xB870, 0x8EEE, 0xB872,\n\t0x8EEF, 0xB873, 0x8EF0, 0xB874, 0x8EF1, 0xB875, 0x8EF2, 0xB876,\n\t0x8EF3, 0xB877, 0x8EF4, 0xB879, 0x8EF5, 0xB87A, 0x8EF6, 0xB87B,\n\t0x8EF7, 0xB87D, 0x8EF8, 0xB87E, 0x8EF9, 0xB87F, 0x8EFA, 0xB880,\n\t0x8EFB, 0xB881, 0x8EFC, 0xB882, 0x8EFD, 0xB883, 0x8EFE, 0xB884,\n\t0x8F41, 0xB885, 0x8F42, 0xB886, 0x8F43, 0xB887, 0x8F44, 0xB888,\n\t0x8F45, 0xB889, 0x8F46, 0xB88A, 0x8F47, 0xB88B, 0x8F48, 0xB88C,\n\t0x8F49, 0xB88E, 0x8F4A, 0xB88F, 0x8F4B, 0xB890, 0x8F4C, 0xB891,\n\t0x8F4D, 0xB892, 0x8F4E, 0xB893, 0x8F4F, 0xB894, 0x8F50, 0xB895,\n\t0x8F51, 0xB896, 0x8F52, 0xB897, 0x8F53, 0xB898, 0x8F54, 0xB899,\n\t0x8F55, 0xB89A, 0x8F56, 0xB89B, 0x8F57, 0xB89C, 0x8F58, 0xB89D,\n\t0x8F59, 0xB89E, 0x8F5A, 0xB89F, 0x8F61, 0xB8A0, 0x8F62, 0xB8A1,\n\t0x8F63, 0xB8A2, 0x8F64, 0xB8A3, 0x8F65, 0xB8A4, 0x8F66, 0xB8A5,\n\t0x8F67, 0xB8A6, 0x8F68, 0xB8A7, 0x8F69, 0xB8A9, 0x8F6A, 0xB8AA,\n\t0x8F6B, 0xB8AB, 0x8F6C, 0xB8AC, 0x8F6D, 0xB8AD, 0x8F6E, 0xB8AE,\n\t0x8F6F, 0xB8AF, 0x8F70, 0xB8B1, 0x8F71, 0xB8B2, 0x8F72, 0xB8B3,\n\t0x8F73, 0xB8B5, 0x8F74, 0xB8B6, 0x8F75, 0xB8B7, 0x8F76, 0xB8B9,\n\t0x8F77, 0xB8BA, 0x8F78, 0xB8BB, 0x8F79, 0xB8BC, 0x8F7A, 0xB8BD,\n\t0x8F81, 0xB8BE, 0x8F82, 0xB8BF, 0x8F83, 0xB8C2, 0x8F84, 0xB8C4,\n\t0x8F85, 0xB8C6, 0x8F86, 0xB8C7, 0x8F87, 0xB8C8, 0x8F88, 0xB8C9,\n\t0x8F89, 0xB8CA, 0x8F8A, 0xB8CB, 0x8F8B, 0xB8CD, 0x8F8C, 0xB8CE,\n\t0x8F8D, 0xB8CF, 0x8F8E, 0xB8D1, 0x8F8F, 0xB8D2, 0x8F90, 0xB8D3,\n\t0x8F91, 0xB8D5, 0x8F92, 0xB8D6, 0x8F93, 0xB8D7, 0x8F94, 0xB8D8,\n\t0x8F95, 0xB8D9, 0x8F96, 0xB8DA, 0x8F97, 0xB8DB, 0x8F98, 0xB8DC,\n\t0x8F99, 0xB8DE, 0x8F9A, 0xB8E0, 0x8F9B, 0xB8E2, 0x8F9C, 0xB8E3,\n\t0x8F9D, 0xB8E4, 0x8F9E, 0xB8E5, 0x8F9F, 0xB8E6, 0x8FA0, 0xB8E7,\n\t0x8FA1, 0xB8EA, 0x8FA2, 0xB8EB, 0x8FA3, 0xB8ED, 0x8FA4, 0xB8EE,\n\t0x8FA5, 0xB8EF, 0x8FA6, 0xB8F1, 0x8FA7, 0xB8F2, 0x8FA8, 0xB8F3,\n\t0x8FA9, 0xB8F4, 0x8FAA, 0xB8F5, 0x8FAB, 0xB8F6, 0x8FAC, 0xB8F7,\n\t0x8FAD, 0xB8FA, 0x8FAE, 0xB8FC, 0x8FAF, 0xB8FE, 0x8FB0, 0xB8FF,\n\t0x8FB1, 0xB900, 0x8FB2, 0xB901, 0x8FB3, 0xB902, 0x8FB4, 0xB903,\n\t0x8FB5, 0xB905, 0x8FB6, 0xB906, 0x8FB7, 0xB907, 0x8FB8, 0xB908,\n\t0x8FB9, 0xB909, 0x8FBA, 0xB90A, 0x8FBB, 0xB90B, 0x8FBC, 0xB90C,\n\t0x8FBD, 0xB90D, 0x8FBE, 0xB90E, 0x8FBF, 0xB90F, 0x8FC0, 0xB910,\n\t0x8FC1, 0xB911, 0x8FC2, 0xB912, 0x8FC3, 0xB913, 0x8FC4, 0xB914,\n\t0x8FC5, 0xB915, 0x8FC6, 0xB916, 0x8FC7, 0xB917, 0x8FC8, 0xB919,\n\t0x8FC9, 0xB91A, 0x8FCA, 0xB91B, 0x8FCB, 0xB91C, 0x8FCC, 0xB91D,\n\t0x8FCD, 0xB91E, 0x8FCE, 0xB91F, 0x8FCF, 0xB921, 0x8FD0, 0xB922,\n\t0x8FD1, 0xB923, 0x8FD2, 0xB924, 0x8FD3, 0xB925, 0x8FD4, 0xB926,\n\t0x8FD5, 0xB927, 0x8FD6, 0xB928, 0x8FD7, 0xB929, 0x8FD8, 0xB92A,\n\t0x8FD9, 0xB92B, 0x8FDA, 0xB92C, 0x8FDB, 0xB92D, 0x8FDC, 0xB92E,\n\t0x8FDD, 0xB92F, 0x8FDE, 0xB930, 0x8FDF, 0xB931, 0x8FE0, 0xB932,\n\t0x8FE1, 0xB933, 0x8FE2, 0xB934, 0x8FE3, 0xB935, 0x8FE4, 0xB936,\n\t0x8FE5, 0xB937, 0x8FE6, 0xB938, 0x8FE7, 0xB939, 0x8FE8, 0xB93A,\n\t0x8FE9, 0xB93B, 0x8FEA, 0xB93E, 0x8FEB, 0xB93F, 0x8FEC, 0xB941,\n\t0x8FED, 0xB942, 0x8FEE, 0xB943, 0x8FEF, 0xB945, 0x8FF0, 0xB946,\n\t0x8FF1, 0xB947, 0x8FF2, 0xB948, 0x8FF3, 0xB949, 0x8FF4, 0xB94A,\n\t0x8FF5, 0xB94B, 0x8FF6, 0xB94D, 0x8FF7, 0xB94E, 0x8FF8, 0xB950,\n\t0x8FF9, 0xB952, 0x8FFA, 0xB953, 0x8FFB, 0xB954, 0x8FFC, 0xB955,\n\t0x8FFD, 0xB956, 0x8FFE, 0xB957, 0x9041, 0xB95A, 0x9042, 0xB95B,\n\t0x9043, 0xB95D, 0x9044, 0xB95E, 0x9045, 0xB95F, 0x9046, 0xB961,\n\t0x9047, 0xB962, 0x9048, 0xB963, 0x9049, 0xB964, 0x904A, 0xB965,\n\t0x904B, 0xB966, 0x904C, 0xB967, 0x904D, 0xB96A, 0x904E, 0xB96C,\n\t0x904F, 0xB96E, 0x9050, 0xB96F, 0x9051, 0xB970, 0x9052, 0xB971,\n\t0x9053, 0xB972, 0x9054, 0xB973, 0x9055, 0xB976, 0x9056, 0xB977,\n\t0x9057, 0xB979, 0x9058, 0xB97A, 0x9059, 0xB97B, 0x905A, 0xB97D,\n\t0x9061, 0xB97E, 0x9062, 0xB97F, 0x9063, 0xB980, 0x9064, 0xB981,\n\t0x9065, 0xB982, 0x9066, 0xB983, 0x9067, 0xB986, 0x9068, 0xB988,\n\t0x9069, 0xB98B, 0x906A, 0xB98C, 0x906B, 0xB98F, 0x906C, 0xB990,\n\t0x906D, 0xB991, 0x906E, 0xB992, 0x906F, 0xB993, 0x9070, 0xB994,\n\t0x9071, 0xB995, 0x9072, 0xB996, 0x9073, 0xB997, 0x9074, 0xB998,\n\t0x9075, 0xB999, 0x9076, 0xB99A, 0x9077, 0xB99B, 0x9078, 0xB99C,\n\t0x9079, 0xB99D, 0x907A, 0xB99E, 0x9081, 0xB99F, 0x9082, 0xB9A0,\n\t0x9083, 0xB9A1, 0x9084, 0xB9A2, 0x9085, 0xB9A3, 0x9086, 0xB9A4,\n\t0x9087, 0xB9A5, 0x9088, 0xB9A6, 0x9089, 0xB9A7, 0x908A, 0xB9A8,\n\t0x908B, 0xB9A9, 0x908C, 0xB9AA, 0x908D, 0xB9AB, 0x908E, 0xB9AE,\n\t0x908F, 0xB9AF, 0x9090, 0xB9B1, 0x9091, 0xB9B2, 0x9092, 0xB9B3,\n\t0x9093, 0xB9B5, 0x9094, 0xB9B6, 0x9095, 0xB9B7, 0x9096, 0xB9B8,\n\t0x9097, 0xB9B9, 0x9098, 0xB9BA, 0x9099, 0xB9BB, 0x909A, 0xB9BE,\n\t0x909B, 0xB9C0, 0x909C, 0xB9C2, 0x909D, 0xB9C3, 0x909E, 0xB9C4,\n\t0x909F, 0xB9C5, 0x90A0, 0xB9C6, 0x90A1, 0xB9C7, 0x90A2, 0xB9CA,\n\t0x90A3, 0xB9CB, 0x90A4, 0xB9CD, 0x90A5, 0xB9D3, 0x90A6, 0xB9D4,\n\t0x90A7, 0xB9D5, 0x90A8, 0xB9D6, 0x90A9, 0xB9D7, 0x90AA, 0xB9DA,\n\t0x90AB, 0xB9DC, 0x90AC, 0xB9DF, 0x90AD, 0xB9E0, 0x90AE, 0xB9E2,\n\t0x90AF, 0xB9E6, 0x90B0, 0xB9E7, 0x90B1, 0xB9E9, 0x90B2, 0xB9EA,\n\t0x90B3, 0xB9EB, 0x90B4, 0xB9ED, 0x90B5, 0xB9EE, 0x90B6, 0xB9EF,\n\t0x90B7, 0xB9F0, 0x90B8, 0xB9F1, 0x90B9, 0xB9F2, 0x90BA, 0xB9F3,\n\t0x90BB, 0xB9F6, 0x90BC, 0xB9FB, 0x90BD, 0xB9FC, 0x90BE, 0xB9FD,\n\t0x90BF, 0xB9FE, 0x90C0, 0xB9FF, 0x90C1, 0xBA02, 0x90C2, 0xBA03,\n\t0x90C3, 0xBA04, 0x90C4, 0xBA05, 0x90C5, 0xBA06, 0x90C6, 0xBA07,\n\t0x90C7, 0xBA09, 0x90C8, 0xBA0A, 0x90C9, 0xBA0B, 0x90CA, 0xBA0C,\n\t0x90CB, 0xBA0D, 0x90CC, 0xBA0E, 0x90CD, 0xBA0F, 0x90CE, 0xBA10,\n\t0x90CF, 0xBA11, 0x90D0, 0xBA12, 0x90D1, 0xBA13, 0x90D2, 0xBA14,\n\t0x90D3, 0xBA16, 0x90D4, 0xBA17, 0x90D5, 0xBA18, 0x90D6, 0xBA19,\n\t0x90D7, 0xBA1A, 0x90D8, 0xBA1B, 0x90D9, 0xBA1C, 0x90DA, 0xBA1D,\n\t0x90DB, 0xBA1E, 0x90DC, 0xBA1F, 0x90DD, 0xBA20, 0x90DE, 0xBA21,\n\t0x90DF, 0xBA22, 0x90E0, 0xBA23, 0x90E1, 0xBA24, 0x90E2, 0xBA25,\n\t0x90E3, 0xBA26, 0x90E4, 0xBA27, 0x90E5, 0xBA28, 0x90E6, 0xBA29,\n\t0x90E7, 0xBA2A, 0x90E8, 0xBA2B, 0x90E9, 0xBA2C, 0x90EA, 0xBA2D,\n\t0x90EB, 0xBA2E, 0x90EC, 0xBA2F, 0x90ED, 0xBA30, 0x90EE, 0xBA31,\n\t0x90EF, 0xBA32, 0x90F0, 0xBA33, 0x90F1, 0xBA34, 0x90F2, 0xBA35,\n\t0x90F3, 0xBA36, 0x90F4, 0xBA37, 0x90F5, 0xBA3A, 0x90F6, 0xBA3B,\n\t0x90F7, 0xBA3D, 0x90F8, 0xBA3E, 0x90F9, 0xBA3F, 0x90FA, 0xBA41,\n\t0x90FB, 0xBA43, 0x90FC, 0xBA44, 0x90FD, 0xBA45, 0x90FE, 0xBA46,\n\t0x9141, 0xBA47, 0x9142, 0xBA4A, 0x9143, 0xBA4C, 0x9144, 0xBA4F,\n\t0x9145, 0xBA50, 0x9146, 0xBA51, 0x9147, 0xBA52, 0x9148, 0xBA56,\n\t0x9149, 0xBA57, 0x914A, 0xBA59, 0x914B, 0xBA5A, 0x914C, 0xBA5B,\n\t0x914D, 0xBA5D, 0x914E, 0xBA5E, 0x914F, 0xBA5F, 0x9150, 0xBA60,\n\t0x9151, 0xBA61, 0x9152, 0xBA62, 0x9153, 0xBA63, 0x9154, 0xBA66,\n\t0x9155, 0xBA6A, 0x9156, 0xBA6B, 0x9157, 0xBA6C, 0x9158, 0xBA6D,\n\t0x9159, 0xBA6E, 0x915A, 0xBA6F, 0x9161, 0xBA72, 0x9162, 0xBA73,\n\t0x9163, 0xBA75, 0x9164, 0xBA76, 0x9165, 0xBA77, 0x9166, 0xBA79,\n\t0x9167, 0xBA7A, 0x9168, 0xBA7B, 0x9169, 0xBA7C, 0x916A, 0xBA7D,\n\t0x916B, 0xBA7E, 0x916C, 0xBA7F, 0x916D, 0xBA80, 0x916E, 0xBA81,\n\t0x916F, 0xBA82, 0x9170, 0xBA86, 0x9171, 0xBA88, 0x9172, 0xBA89,\n\t0x9173, 0xBA8A, 0x9174, 0xBA8B, 0x9175, 0xBA8D, 0x9176, 0xBA8E,\n\t0x9177, 0xBA8F, 0x9178, 0xBA90, 0x9179, 0xBA91, 0x917A, 0xBA92,\n\t0x9181, 0xBA93, 0x9182, 0xBA94, 0x9183, 0xBA95, 0x9184, 0xBA96,\n\t0x9185, 0xBA97, 0x9186, 0xBA98, 0x9187, 0xBA99, 0x9188, 0xBA9A,\n\t0x9189, 0xBA9B, 0x918A, 0xBA9C, 0x918B, 0xBA9D, 0x918C, 0xBA9E,\n\t0x918D, 0xBA9F, 0x918E, 0xBAA0, 0x918F, 0xBAA1, 0x9190, 0xBAA2,\n\t0x9191, 0xBAA3, 0x9192, 0xBAA4, 0x9193, 0xBAA5, 0x9194, 0xBAA6,\n\t0x9195, 0xBAA7, 0x9196, 0xBAAA, 0x9197, 0xBAAD, 0x9198, 0xBAAE,\n\t0x9199, 0xBAAF, 0x919A, 0xBAB1, 0x919B, 0xBAB3, 0x919C, 0xBAB4,\n\t0x919D, 0xBAB5, 0x919E, 0xBAB6, 0x919F, 0xBAB7, 0x91A0, 0xBABA,\n\t0x91A1, 0xBABC, 0x91A2, 0xBABE, 0x91A3, 0xBABF, 0x91A4, 0xBAC0,\n\t0x91A5, 0xBAC1, 0x91A6, 0xBAC2, 0x91A7, 0xBAC3, 0x91A8, 0xBAC5,\n\t0x91A9, 0xBAC6, 0x91AA, 0xBAC7, 0x91AB, 0xBAC9, 0x91AC, 0xBACA,\n\t0x91AD, 0xBACB, 0x91AE, 0xBACC, 0x91AF, 0xBACD, 0x91B0, 0xBACE,\n\t0x91B1, 0xBACF, 0x91B2, 0xBAD0, 0x91B3, 0xBAD1, 0x91B4, 0xBAD2,\n\t0x91B5, 0xBAD3, 0x91B6, 0xBAD4, 0x91B7, 0xBAD5, 0x91B8, 0xBAD6,\n\t0x91B9, 0xBAD7, 0x91BA, 0xBADA, 0x91BB, 0xBADB, 0x91BC, 0xBADC,\n\t0x91BD, 0xBADD, 0x91BE, 0xBADE, 0x91BF, 0xBADF, 0x91C0, 0xBAE0,\n\t0x91C1, 0xBAE1, 0x91C2, 0xBAE2, 0x91C3, 0xBAE3, 0x91C4, 0xBAE4,\n\t0x91C5, 0xBAE5, 0x91C6, 0xBAE6, 0x91C7, 0xBAE7, 0x91C8, 0xBAE8,\n\t0x91C9, 0xBAE9, 0x91CA, 0xBAEA, 0x91CB, 0xBAEB, 0x91CC, 0xBAEC,\n\t0x91CD, 0xBAED, 0x91CE, 0xBAEE, 0x91CF, 0xBAEF, 0x91D0, 0xBAF0,\n\t0x91D1, 0xBAF1, 0x91D2, 0xBAF2, 0x91D3, 0xBAF3, 0x91D4, 0xBAF4,\n\t0x91D5, 0xBAF5, 0x91D6, 0xBAF6, 0x91D7, 0xBAF7, 0x91D8, 0xBAF8,\n\t0x91D9, 0xBAF9, 0x91DA, 0xBAFA, 0x91DB, 0xBAFB, 0x91DC, 0xBAFD,\n\t0x91DD, 0xBAFE, 0x91DE, 0xBAFF, 0x91DF, 0xBB01, 0x91E0, 0xBB02,\n\t0x91E1, 0xBB03, 0x91E2, 0xBB05, 0x91E3, 0xBB06, 0x91E4, 0xBB07,\n\t0x91E5, 0xBB08, 0x91E6, 0xBB09, 0x91E7, 0xBB0A, 0x91E8, 0xBB0B,\n\t0x91E9, 0xBB0C, 0x91EA, 0xBB0E, 0x91EB, 0xBB10, 0x91EC, 0xBB12,\n\t0x91ED, 0xBB13, 0x91EE, 0xBB14, 0x91EF, 0xBB15, 0x91F0, 0xBB16,\n\t0x91F1, 0xBB17, 0x91F2, 0xBB19, 0x91F3, 0xBB1A, 0x91F4, 0xBB1B,\n\t0x91F5, 0xBB1D, 0x91F6, 0xBB1E, 0x91F7, 0xBB1F, 0x91F8, 0xBB21,\n\t0x91F9, 0xBB22, 0x91FA, 0xBB23, 0x91FB, 0xBB24, 0x91FC, 0xBB25,\n\t0x91FD, 0xBB26, 0x91FE, 0xBB27, 0x9241, 0xBB28, 0x9242, 0xBB2A,\n\t0x9243, 0xBB2C, 0x9244, 0xBB2D, 0x9245, 0xBB2E, 0x9246, 0xBB2F,\n\t0x9247, 0xBB30, 0x9248, 0xBB31, 0x9249, 0xBB32, 0x924A, 0xBB33,\n\t0x924B, 0xBB37, 0x924C, 0xBB39, 0x924D, 0xBB3A, 0x924E, 0xBB3F,\n\t0x924F, 0xBB40, 0x9250, 0xBB41, 0x9251, 0xBB42, 0x9252, 0xBB43,\n\t0x9253, 0xBB46, 0x9254, 0xBB48, 0x9255, 0xBB4A, 0x9256, 0xBB4B,\n\t0x9257, 0xBB4C, 0x9258, 0xBB4E, 0x9259, 0xBB51, 0x925A, 0xBB52,\n\t0x9261, 0xBB53, 0x9262, 0xBB55, 0x9263, 0xBB56, 0x9264, 0xBB57,\n\t0x9265, 0xBB59, 0x9266, 0xBB5A, 0x9267, 0xBB5B, 0x9268, 0xBB5C,\n\t0x9269, 0xBB5D, 0x926A, 0xBB5E, 0x926B, 0xBB5F, 0x926C, 0xBB60,\n\t0x926D, 0xBB62, 0x926E, 0xBB64, 0x926F, 0xBB65, 0x9270, 0xBB66,\n\t0x9271, 0xBB67, 0x9272, 0xBB68, 0x9273, 0xBB69, 0x9274, 0xBB6A,\n\t0x9275, 0xBB6B, 0x9276, 0xBB6D, 0x9277, 0xBB6E, 0x9278, 0xBB6F,\n\t0x9279, 0xBB70, 0x927A, 0xBB71, 0x9281, 0xBB72, 0x9282, 0xBB73,\n\t0x9283, 0xBB74, 0x9284, 0xBB75, 0x9285, 0xBB76, 0x9286, 0xBB77,\n\t0x9287, 0xBB78, 0x9288, 0xBB79, 0x9289, 0xBB7A, 0x928A, 0xBB7B,\n\t0x928B, 0xBB7C, 0x928C, 0xBB7D, 0x928D, 0xBB7E, 0x928E, 0xBB7F,\n\t0x928F, 0xBB80, 0x9290, 0xBB81, 0x9291, 0xBB82, 0x9292, 0xBB83,\n\t0x9293, 0xBB84, 0x9294, 0xBB85, 0x9295, 0xBB86, 0x9296, 0xBB87,\n\t0x9297, 0xBB89, 0x9298, 0xBB8A, 0x9299, 0xBB8B, 0x929A, 0xBB8D,\n\t0x929B, 0xBB8E, 0x929C, 0xBB8F, 0x929D, 0xBB91, 0x929E, 0xBB92,\n\t0x929F, 0xBB93, 0x92A0, 0xBB94, 0x92A1, 0xBB95, 0x92A2, 0xBB96,\n\t0x92A3, 0xBB97, 0x92A4, 0xBB98, 0x92A5, 0xBB99, 0x92A6, 0xBB9A,\n\t0x92A7, 0xBB9B, 0x92A8, 0xBB9C, 0x92A9, 0xBB9D, 0x92AA, 0xBB9E,\n\t0x92AB, 0xBB9F, 0x92AC, 0xBBA0, 0x92AD, 0xBBA1, 0x92AE, 0xBBA2,\n\t0x92AF, 0xBBA3, 0x92B0, 0xBBA5, 0x92B1, 0xBBA6, 0x92B2, 0xBBA7,\n\t0x92B3, 0xBBA9, 0x92B4, 0xBBAA, 0x92B5, 0xBBAB, 0x92B6, 0xBBAD,\n\t0x92B7, 0xBBAE, 0x92B8, 0xBBAF, 0x92B9, 0xBBB0, 0x92BA, 0xBBB1,\n\t0x92BB, 0xBBB2, 0x92BC, 0xBBB3, 0x92BD, 0xBBB5, 0x92BE, 0xBBB6,\n\t0x92BF, 0xBBB8, 0x92C0, 0xBBB9, 0x92C1, 0xBBBA, 0x92C2, 0xBBBB,\n\t0x92C3, 0xBBBC, 0x92C4, 0xBBBD, 0x92C5, 0xBBBE, 0x92C6, 0xBBBF,\n\t0x92C7, 0xBBC1, 0x92C8, 0xBBC2, 0x92C9, 0xBBC3, 0x92CA, 0xBBC5,\n\t0x92CB, 0xBBC6, 0x92CC, 0xBBC7, 0x92CD, 0xBBC9, 0x92CE, 0xBBCA,\n\t0x92CF, 0xBBCB, 0x92D0, 0xBBCC, 0x92D1, 0xBBCD, 0x92D2, 0xBBCE,\n\t0x92D3, 0xBBCF, 0x92D4, 0xBBD1, 0x92D5, 0xBBD2, 0x92D6, 0xBBD4,\n\t0x92D7, 0xBBD5, 0x92D8, 0xBBD6, 0x92D9, 0xBBD7, 0x92DA, 0xBBD8,\n\t0x92DB, 0xBBD9, 0x92DC, 0xBBDA, 0x92DD, 0xBBDB, 0x92DE, 0xBBDC,\n\t0x92DF, 0xBBDD, 0x92E0, 0xBBDE, 0x92E1, 0xBBDF, 0x92E2, 0xBBE0,\n\t0x92E3, 0xBBE1, 0x92E4, 0xBBE2, 0x92E5, 0xBBE3, 0x92E6, 0xBBE4,\n\t0x92E7, 0xBBE5, 0x92E8, 0xBBE6, 0x92E9, 0xBBE7, 0x92EA, 0xBBE8,\n\t0x92EB, 0xBBE9, 0x92EC, 0xBBEA, 0x92ED, 0xBBEB, 0x92EE, 0xBBEC,\n\t0x92EF, 0xBBED, 0x92F0, 0xBBEE, 0x92F1, 0xBBEF, 0x92F2, 0xBBF0,\n\t0x92F3, 0xBBF1, 0x92F4, 0xBBF2, 0x92F5, 0xBBF3, 0x92F6, 0xBBF4,\n\t0x92F7, 0xBBF5, 0x92F8, 0xBBF6, 0x92F9, 0xBBF7, 0x92FA, 0xBBFA,\n\t0x92FB, 0xBBFB, 0x92FC, 0xBBFD, 0x92FD, 0xBBFE, 0x92FE, 0xBC01,\n\t0x9341, 0xBC03, 0x9342, 0xBC04, 0x9343, 0xBC05, 0x9344, 0xBC06,\n\t0x9345, 0xBC07, 0x9346, 0xBC0A, 0x9347, 0xBC0E, 0x9348, 0xBC10,\n\t0x9349, 0xBC12, 0x934A, 0xBC13, 0x934B, 0xBC19, 0x934C, 0xBC1A,\n\t0x934D, 0xBC20, 0x934E, 0xBC21, 0x934F, 0xBC22, 0x9350, 0xBC23,\n\t0x9351, 0xBC26, 0x9352, 0xBC28, 0x9353, 0xBC2A, 0x9354, 0xBC2B,\n\t0x9355, 0xBC2C, 0x9356, 0xBC2E, 0x9357, 0xBC2F, 0x9358, 0xBC32,\n\t0x9359, 0xBC33, 0x935A, 0xBC35, 0x9361, 0xBC36, 0x9362, 0xBC37,\n\t0x9363, 0xBC39, 0x9364, 0xBC3A, 0x9365, 0xBC3B, 0x9366, 0xBC3C,\n\t0x9367, 0xBC3D, 0x9368, 0xBC3E, 0x9369, 0xBC3F, 0x936A, 0xBC42,\n\t0x936B, 0xBC46, 0x936C, 0xBC47, 0x936D, 0xBC48, 0x936E, 0xBC4A,\n\t0x936F, 0xBC4B, 0x9370, 0xBC4E, 0x9371, 0xBC4F, 0x9372, 0xBC51,\n\t0x9373, 0xBC52, 0x9374, 0xBC53, 0x9375, 0xBC54, 0x9376, 0xBC55,\n\t0x9377, 0xBC56, 0x9378, 0xBC57, 0x9379, 0xBC58, 0x937A, 0xBC59,\n\t0x9381, 0xBC5A, 0x9382, 0xBC5B, 0x9383, 0xBC5C, 0x9384, 0xBC5E,\n\t0x9385, 0xBC5F, 0x9386, 0xBC60, 0x9387, 0xBC61, 0x9388, 0xBC62,\n\t0x9389, 0xBC63, 0x938A, 0xBC64, 0x938B, 0xBC65, 0x938C, 0xBC66,\n\t0x938D, 0xBC67, 0x938E, 0xBC68, 0x938F, 0xBC69, 0x9390, 0xBC6A,\n\t0x9391, 0xBC6B, 0x9392, 0xBC6C, 0x9393, 0xBC6D, 0x9394, 0xBC6E,\n\t0x9395, 0xBC6F, 0x9396, 0xBC70, 0x9397, 0xBC71, 0x9398, 0xBC72,\n\t0x9399, 0xBC73, 0x939A, 0xBC74, 0x939B, 0xBC75, 0x939C, 0xBC76,\n\t0x939D, 0xBC77, 0x939E, 0xBC78, 0x939F, 0xBC79, 0x93A0, 0xBC7A,\n\t0x93A1, 0xBC7B, 0x93A2, 0xBC7C, 0x93A3, 0xBC7D, 0x93A4, 0xBC7E,\n\t0x93A5, 0xBC7F, 0x93A6, 0xBC80, 0x93A7, 0xBC81, 0x93A8, 0xBC82,\n\t0x93A9, 0xBC83, 0x93AA, 0xBC86, 0x93AB, 0xBC87, 0x93AC, 0xBC89,\n\t0x93AD, 0xBC8A, 0x93AE, 0xBC8D, 0x93AF, 0xBC8F, 0x93B0, 0xBC90,\n\t0x93B1, 0xBC91, 0x93B2, 0xBC92, 0x93B3, 0xBC93, 0x93B4, 0xBC96,\n\t0x93B5, 0xBC98, 0x93B6, 0xBC9B, 0x93B7, 0xBC9C, 0x93B8, 0xBC9D,\n\t0x93B9, 0xBC9E, 0x93BA, 0xBC9F, 0x93BB, 0xBCA2, 0x93BC, 0xBCA3,\n\t0x93BD, 0xBCA5, 0x93BE, 0xBCA6, 0x93BF, 0xBCA9, 0x93C0, 0xBCAA,\n\t0x93C1, 0xBCAB, 0x93C2, 0xBCAC, 0x93C3, 0xBCAD, 0x93C4, 0xBCAE,\n\t0x93C5, 0xBCAF, 0x93C6, 0xBCB2, 0x93C7, 0xBCB6, 0x93C8, 0xBCB7,\n\t0x93C9, 0xBCB8, 0x93CA, 0xBCB9, 0x93CB, 0xBCBA, 0x93CC, 0xBCBB,\n\t0x93CD, 0xBCBE, 0x93CE, 0xBCBF, 0x93CF, 0xBCC1, 0x93D0, 0xBCC2,\n\t0x93D1, 0xBCC3, 0x93D2, 0xBCC5, 0x93D3, 0xBCC6, 0x93D4, 0xBCC7,\n\t0x93D5, 0xBCC8, 0x93D6, 0xBCC9, 0x93D7, 0xBCCA, 0x93D8, 0xBCCB,\n\t0x93D9, 0xBCCC, 0x93DA, 0xBCCE, 0x93DB, 0xBCD2, 0x93DC, 0xBCD3,\n\t0x93DD, 0xBCD4, 0x93DE, 0xBCD6, 0x93DF, 0xBCD7, 0x93E0, 0xBCD9,\n\t0x93E1, 0xBCDA, 0x93E2, 0xBCDB, 0x93E3, 0xBCDD, 0x93E4, 0xBCDE,\n\t0x93E5, 0xBCDF, 0x93E6, 0xBCE0, 0x93E7, 0xBCE1, 0x93E8, 0xBCE2,\n\t0x93E9, 0xBCE3, 0x93EA, 0xBCE4, 0x93EB, 0xBCE5, 0x93EC, 0xBCE6,\n\t0x93ED, 0xBCE7, 0x93EE, 0xBCE8, 0x93EF, 0xBCE9, 0x93F0, 0xBCEA,\n\t0x93F1, 0xBCEB, 0x93F2, 0xBCEC, 0x93F3, 0xBCED, 0x93F4, 0xBCEE,\n\t0x93F5, 0xBCEF, 0x93F6, 0xBCF0, 0x93F7, 0xBCF1, 0x93F8, 0xBCF2,\n\t0x93F9, 0xBCF3, 0x93FA, 0xBCF7, 0x93FB, 0xBCF9, 0x93FC, 0xBCFA,\n\t0x93FD, 0xBCFB, 0x93FE, 0xBCFD, 0x9441, 0xBCFE, 0x9442, 0xBCFF,\n\t0x9443, 0xBD00, 0x9444, 0xBD01, 0x9445, 0xBD02, 0x9446, 0xBD03,\n\t0x9447, 0xBD06, 0x9448, 0xBD08, 0x9449, 0xBD0A, 0x944A, 0xBD0B,\n\t0x944B, 0xBD0C, 0x944C, 0xBD0D, 0x944D, 0xBD0E, 0x944E, 0xBD0F,\n\t0x944F, 0xBD11, 0x9450, 0xBD12, 0x9451, 0xBD13, 0x9452, 0xBD15,\n\t0x9453, 0xBD16, 0x9454, 0xBD17, 0x9455, 0xBD18, 0x9456, 0xBD19,\n\t0x9457, 0xBD1A, 0x9458, 0xBD1B, 0x9459, 0xBD1C, 0x945A, 0xBD1D,\n\t0x9461, 0xBD1E, 0x9462, 0xBD1F, 0x9463, 0xBD20, 0x9464, 0xBD21,\n\t0x9465, 0xBD22, 0x9466, 0xBD23, 0x9467, 0xBD25, 0x9468, 0xBD26,\n\t0x9469, 0xBD27, 0x946A, 0xBD28, 0x946B, 0xBD29, 0x946C, 0xBD2A,\n\t0x946D, 0xBD2B, 0x946E, 0xBD2D, 0x946F, 0xBD2E, 0x9470, 0xBD2F,\n\t0x9471, 0xBD30, 0x9472, 0xBD31, 0x9473, 0xBD32, 0x9474, 0xBD33,\n\t0x9475, 0xBD34, 0x9476, 0xBD35, 0x9477, 0xBD36, 0x9478, 0xBD37,\n\t0x9479, 0xBD38, 0x947A, 0xBD39, 0x9481, 0xBD3A, 0x9482, 0xBD3B,\n\t0x9483, 0xBD3C, 0x9484, 0xBD3D, 0x9485, 0xBD3E, 0x9486, 0xBD3F,\n\t0x9487, 0xBD41, 0x9488, 0xBD42, 0x9489, 0xBD43, 0x948A, 0xBD44,\n\t0x948B, 0xBD45, 0x948C, 0xBD46, 0x948D, 0xBD47, 0x948E, 0xBD4A,\n\t0x948F, 0xBD4B, 0x9490, 0xBD4D, 0x9491, 0xBD4E, 0x9492, 0xBD4F,\n\t0x9493, 0xBD51, 0x9494, 0xBD52, 0x9495, 0xBD53, 0x9496, 0xBD54,\n\t0x9497, 0xBD55, 0x9498, 0xBD56, 0x9499, 0xBD57, 0x949A, 0xBD5A,\n\t0x949B, 0xBD5B, 0x949C, 0xBD5C, 0x949D, 0xBD5D, 0x949E, 0xBD5E,\n\t0x949F, 0xBD5F, 0x94A0, 0xBD60, 0x94A1, 0xBD61, 0x94A2, 0xBD62,\n\t0x94A3, 0xBD63, 0x94A4, 0xBD65, 0x94A5, 0xBD66, 0x94A6, 0xBD67,\n\t0x94A7, 0xBD69, 0x94A8, 0xBD6A, 0x94A9, 0xBD6B, 0x94AA, 0xBD6C,\n\t0x94AB, 0xBD6D, 0x94AC, 0xBD6E, 0x94AD, 0xBD6F, 0x94AE, 0xBD70,\n\t0x94AF, 0xBD71, 0x94B0, 0xBD72, 0x94B1, 0xBD73, 0x94B2, 0xBD74,\n\t0x94B3, 0xBD75, 0x94B4, 0xBD76, 0x94B5, 0xBD77, 0x94B6, 0xBD78,\n\t0x94B7, 0xBD79, 0x94B8, 0xBD7A, 0x94B9, 0xBD7B, 0x94BA, 0xBD7C,\n\t0x94BB, 0xBD7D, 0x94BC, 0xBD7E, 0x94BD, 0xBD7F, 0x94BE, 0xBD82,\n\t0x94BF, 0xBD83, 0x94C0, 0xBD85, 0x94C1, 0xBD86, 0x94C2, 0xBD8B,\n\t0x94C3, 0xBD8C, 0x94C4, 0xBD8D, 0x94C5, 0xBD8E, 0x94C6, 0xBD8F,\n\t0x94C7, 0xBD92, 0x94C8, 0xBD94, 0x94C9, 0xBD96, 0x94CA, 0xBD97,\n\t0x94CB, 0xBD98, 0x94CC, 0xBD9B, 0x94CD, 0xBD9D, 0x94CE, 0xBD9E,\n\t0x94CF, 0xBD9F, 0x94D0, 0xBDA0, 0x94D1, 0xBDA1, 0x94D2, 0xBDA2,\n\t0x94D3, 0xBDA3, 0x94D4, 0xBDA5, 0x94D5, 0xBDA6, 0x94D6, 0xBDA7,\n\t0x94D7, 0xBDA8, 0x94D8, 0xBDA9, 0x94D9, 0xBDAA, 0x94DA, 0xBDAB,\n\t0x94DB, 0xBDAC, 0x94DC, 0xBDAD, 0x94DD, 0xBDAE, 0x94DE, 0xBDAF,\n\t0x94DF, 0xBDB1, 0x94E0, 0xBDB2, 0x94E1, 0xBDB3, 0x94E2, 0xBDB4,\n\t0x94E3, 0xBDB5, 0x94E4, 0xBDB6, 0x94E5, 0xBDB7, 0x94E6, 0xBDB9,\n\t0x94E7, 0xBDBA, 0x94E8, 0xBDBB, 0x94E9, 0xBDBC, 0x94EA, 0xBDBD,\n\t0x94EB, 0xBDBE, 0x94EC, 0xBDBF, 0x94ED, 0xBDC0, 0x94EE, 0xBDC1,\n\t0x94EF, 0xBDC2, 0x94F0, 0xBDC3, 0x94F1, 0xBDC4, 0x94F2, 0xBDC5,\n\t0x94F3, 0xBDC6, 0x94F4, 0xBDC7, 0x94F5, 0xBDC8, 0x94F6, 0xBDC9,\n\t0x94F7, 0xBDCA, 0x94F8, 0xBDCB, 0x94F9, 0xBDCC, 0x94FA, 0xBDCD,\n\t0x94FB, 0xBDCE, 0x94FC, 0xBDCF, 0x94FD, 0xBDD0, 0x94FE, 0xBDD1,\n\t0x9541, 0xBDD2, 0x9542, 0xBDD3, 0x9543, 0xBDD6, 0x9544, 0xBDD7,\n\t0x9545, 0xBDD9, 0x9546, 0xBDDA, 0x9547, 0xBDDB, 0x9548, 0xBDDD,\n\t0x9549, 0xBDDE, 0x954A, 0xBDDF, 0x954B, 0xBDE0, 0x954C, 0xBDE1,\n\t0x954D, 0xBDE2, 0x954E, 0xBDE3, 0x954F, 0xBDE4, 0x9550, 0xBDE5,\n\t0x9551, 0xBDE6, 0x9552, 0xBDE7, 0x9553, 0xBDE8, 0x9554, 0xBDEA,\n\t0x9555, 0xBDEB, 0x9556, 0xBDEC, 0x9557, 0xBDED, 0x9558, 0xBDEE,\n\t0x9559, 0xBDEF, 0x955A, 0xBDF1, 0x9561, 0xBDF2, 0x9562, 0xBDF3,\n\t0x9563, 0xBDF5, 0x9564, 0xBDF6, 0x9565, 0xBDF7, 0x9566, 0xBDF9,\n\t0x9567, 0xBDFA, 0x9568, 0xBDFB, 0x9569, 0xBDFC, 0x956A, 0xBDFD,\n\t0x956B, 0xBDFE, 0x956C, 0xBDFF, 0x956D, 0xBE01, 0x956E, 0xBE02,\n\t0x956F, 0xBE04, 0x9570, 0xBE06, 0x9571, 0xBE07, 0x9572, 0xBE08,\n\t0x9573, 0xBE09, 0x9574, 0xBE0A, 0x9575, 0xBE0B, 0x9576, 0xBE0E,\n\t0x9577, 0xBE0F, 0x9578, 0xBE11, 0x9579, 0xBE12, 0x957A, 0xBE13,\n\t0x9581, 0xBE15, 0x9582, 0xBE16, 0x9583, 0xBE17, 0x9584, 0xBE18,\n\t0x9585, 0xBE19, 0x9586, 0xBE1A, 0x9587, 0xBE1B, 0x9588, 0xBE1E,\n\t0x9589, 0xBE20, 0x958A, 0xBE21, 0x958B, 0xBE22, 0x958C, 0xBE23,\n\t0x958D, 0xBE24, 0x958E, 0xBE25, 0x958F, 0xBE26, 0x9590, 0xBE27,\n\t0x9591, 0xBE28, 0x9592, 0xBE29, 0x9593, 0xBE2A, 0x9594, 0xBE2B,\n\t0x9595, 0xBE2C, 0x9596, 0xBE2D, 0x9597, 0xBE2E, 0x9598, 0xBE2F,\n\t0x9599, 0xBE30, 0x959A, 0xBE31, 0x959B, 0xBE32, 0x959C, 0xBE33,\n\t0x959D, 0xBE34, 0x959E, 0xBE35, 0x959F, 0xBE36, 0x95A0, 0xBE37,\n\t0x95A1, 0xBE38, 0x95A2, 0xBE39, 0x95A3, 0xBE3A, 0x95A4, 0xBE3B,\n\t0x95A5, 0xBE3C, 0x95A6, 0xBE3D, 0x95A7, 0xBE3E, 0x95A8, 0xBE3F,\n\t0x95A9, 0xBE40, 0x95AA, 0xBE41, 0x95AB, 0xBE42, 0x95AC, 0xBE43,\n\t0x95AD, 0xBE46, 0x95AE, 0xBE47, 0x95AF, 0xBE49, 0x95B0, 0xBE4A,\n\t0x95B1, 0xBE4B, 0x95B2, 0xBE4D, 0x95B3, 0xBE4F, 0x95B4, 0xBE50,\n\t0x95B5, 0xBE51, 0x95B6, 0xBE52, 0x95B7, 0xBE53, 0x95B8, 0xBE56,\n\t0x95B9, 0xBE58, 0x95BA, 0xBE5C, 0x95BB, 0xBE5D, 0x95BC, 0xBE5E,\n\t0x95BD, 0xBE5F, 0x95BE, 0xBE62, 0x95BF, 0xBE63, 0x95C0, 0xBE65,\n\t0x95C1, 0xBE66, 0x95C2, 0xBE67, 0x95C3, 0xBE69, 0x95C4, 0xBE6B,\n\t0x95C5, 0xBE6C, 0x95C6, 0xBE6D, 0x95C7, 0xBE6E, 0x95C8, 0xBE6F,\n\t0x95C9, 0xBE72, 0x95CA, 0xBE76, 0x95CB, 0xBE77, 0x95CC, 0xBE78,\n\t0x95CD, 0xBE79, 0x95CE, 0xBE7A, 0x95CF, 0xBE7E, 0x95D0, 0xBE7F,\n\t0x95D1, 0xBE81, 0x95D2, 0xBE82, 0x95D3, 0xBE83, 0x95D4, 0xBE85,\n\t0x95D5, 0xBE86, 0x95D6, 0xBE87, 0x95D7, 0xBE88, 0x95D8, 0xBE89,\n\t0x95D9, 0xBE8A, 0x95DA, 0xBE8B, 0x95DB, 0xBE8E, 0x95DC, 0xBE92,\n\t0x95DD, 0xBE93, 0x95DE, 0xBE94, 0x95DF, 0xBE95, 0x95E0, 0xBE96,\n\t0x95E1, 0xBE97, 0x95E2, 0xBE9A, 0x95E3, 0xBE9B, 0x95E4, 0xBE9C,\n\t0x95E5, 0xBE9D, 0x95E6, 0xBE9E, 0x95E7, 0xBE9F, 0x95E8, 0xBEA0,\n\t0x95E9, 0xBEA1, 0x95EA, 0xBEA2, 0x95EB, 0xBEA3, 0x95EC, 0xBEA4,\n\t0x95ED, 0xBEA5, 0x95EE, 0xBEA6, 0x95EF, 0xBEA7, 0x95F0, 0xBEA9,\n\t0x95F1, 0xBEAA, 0x95F2, 0xBEAB, 0x95F3, 0xBEAC, 0x95F4, 0xBEAD,\n\t0x95F5, 0xBEAE, 0x95F6, 0xBEAF, 0x95F7, 0xBEB0, 0x95F8, 0xBEB1,\n\t0x95F9, 0xBEB2, 0x95FA, 0xBEB3, 0x95FB, 0xBEB4, 0x95FC, 0xBEB5,\n\t0x95FD, 0xBEB6, 0x95FE, 0xBEB7, 0x9641, 0xBEB8, 0x9642, 0xBEB9,\n\t0x9643, 0xBEBA, 0x9644, 0xBEBB, 0x9645, 0xBEBC, 0x9646, 0xBEBD,\n\t0x9647, 0xBEBE, 0x9648, 0xBEBF, 0x9649, 0xBEC0, 0x964A, 0xBEC1,\n\t0x964B, 0xBEC2, 0x964C, 0xBEC3, 0x964D, 0xBEC4, 0x964E, 0xBEC5,\n\t0x964F, 0xBEC6, 0x9650, 0xBEC7, 0x9651, 0xBEC8, 0x9652, 0xBEC9,\n\t0x9653, 0xBECA, 0x9654, 0xBECB, 0x9655, 0xBECC, 0x9656, 0xBECD,\n\t0x9657, 0xBECE, 0x9658, 0xBECF, 0x9659, 0xBED2, 0x965A, 0xBED3,\n\t0x9661, 0xBED5, 0x9662, 0xBED6, 0x9663, 0xBED9, 0x9664, 0xBEDA,\n\t0x9665, 0xBEDB, 0x9666, 0xBEDC, 0x9667, 0xBEDD, 0x9668, 0xBEDE,\n\t0x9669, 0xBEDF, 0x966A, 0xBEE1, 0x966B, 0xBEE2, 0x966C, 0xBEE6,\n\t0x966D, 0xBEE7, 0x966E, 0xBEE8, 0x966F, 0xBEE9, 0x9670, 0xBEEA,\n\t0x9671, 0xBEEB, 0x9672, 0xBEED, 0x9673, 0xBEEE, 0x9674, 0xBEEF,\n\t0x9675, 0xBEF0, 0x9676, 0xBEF1, 0x9677, 0xBEF2, 0x9678, 0xBEF3,\n\t0x9679, 0xBEF4, 0x967A, 0xBEF5, 0x9681, 0xBEF6, 0x9682, 0xBEF7,\n\t0x9683, 0xBEF8, 0x9684, 0xBEF9, 0x9685, 0xBEFA, 0x9686, 0xBEFB,\n\t0x9687, 0xBEFC, 0x9688, 0xBEFD, 0x9689, 0xBEFE, 0x968A, 0xBEFF,\n\t0x968B, 0xBF00, 0x968C, 0xBF02, 0x968D, 0xBF03, 0x968E, 0xBF04,\n\t0x968F, 0xBF05, 0x9690, 0xBF06, 0x9691, 0xBF07, 0x9692, 0xBF0A,\n\t0x9693, 0xBF0B, 0x9694, 0xBF0C, 0x9695, 0xBF0D, 0x9696, 0xBF0E,\n\t0x9697, 0xBF0F, 0x9698, 0xBF10, 0x9699, 0xBF11, 0x969A, 0xBF12,\n\t0x969B, 0xBF13, 0x969C, 0xBF14, 0x969D, 0xBF15, 0x969E, 0xBF16,\n\t0x969F, 0xBF17, 0x96A0, 0xBF1A, 0x96A1, 0xBF1E, 0x96A2, 0xBF1F,\n\t0x96A3, 0xBF20, 0x96A4, 0xBF21, 0x96A5, 0xBF22, 0x96A6, 0xBF23,\n\t0x96A7, 0xBF24, 0x96A8, 0xBF25, 0x96A9, 0xBF26, 0x96AA, 0xBF27,\n\t0x96AB, 0xBF28, 0x96AC, 0xBF29, 0x96AD, 0xBF2A, 0x96AE, 0xBF2B,\n\t0x96AF, 0xBF2C, 0x96B0, 0xBF2D, 0x96B1, 0xBF2E, 0x96B2, 0xBF2F,\n\t0x96B3, 0xBF30, 0x96B4, 0xBF31, 0x96B5, 0xBF32, 0x96B6, 0xBF33,\n\t0x96B7, 0xBF34, 0x96B8, 0xBF35, 0x96B9, 0xBF36, 0x96BA, 0xBF37,\n\t0x96BB, 0xBF38, 0x96BC, 0xBF39, 0x96BD, 0xBF3A, 0x96BE, 0xBF3B,\n\t0x96BF, 0xBF3C, 0x96C0, 0xBF3D, 0x96C1, 0xBF3E, 0x96C2, 0xBF3F,\n\t0x96C3, 0xBF42, 0x96C4, 0xBF43, 0x96C5, 0xBF45, 0x96C6, 0xBF46,\n\t0x96C7, 0xBF47, 0x96C8, 0xBF49, 0x96C9, 0xBF4A, 0x96CA, 0xBF4B,\n\t0x96CB, 0xBF4C, 0x96CC, 0xBF4D, 0x96CD, 0xBF4E, 0x96CE, 0xBF4F,\n\t0x96CF, 0xBF52, 0x96D0, 0xBF53, 0x96D1, 0xBF54, 0x96D2, 0xBF56,\n\t0x96D3, 0xBF57, 0x96D4, 0xBF58, 0x96D5, 0xBF59, 0x96D6, 0xBF5A,\n\t0x96D7, 0xBF5B, 0x96D8, 0xBF5C, 0x96D9, 0xBF5D, 0x96DA, 0xBF5E,\n\t0x96DB, 0xBF5F, 0x96DC, 0xBF60, 0x96DD, 0xBF61, 0x96DE, 0xBF62,\n\t0x96DF, 0xBF63, 0x96E0, 0xBF64, 0x96E1, 0xBF65, 0x96E2, 0xBF66,\n\t0x96E3, 0xBF67, 0x96E4, 0xBF68, 0x96E5, 0xBF69, 0x96E6, 0xBF6A,\n\t0x96E7, 0xBF6B, 0x96E8, 0xBF6C, 0x96E9, 0xBF6D, 0x96EA, 0xBF6E,\n\t0x96EB, 0xBF6F, 0x96EC, 0xBF70, 0x96ED, 0xBF71, 0x96EE, 0xBF72,\n\t0x96EF, 0xBF73, 0x96F0, 0xBF74, 0x96F1, 0xBF75, 0x96F2, 0xBF76,\n\t0x96F3, 0xBF77, 0x96F4, 0xBF78, 0x96F5, 0xBF79, 0x96F6, 0xBF7A,\n\t0x96F7, 0xBF7B, 0x96F8, 0xBF7C, 0x96F9, 0xBF7D, 0x96FA, 0xBF7E,\n\t0x96FB, 0xBF7F, 0x96FC, 0xBF80, 0x96FD, 0xBF81, 0x96FE, 0xBF82,\n\t0x9741, 0xBF83, 0x9742, 0xBF84, 0x9743, 0xBF85, 0x9744, 0xBF86,\n\t0x9745, 0xBF87, 0x9746, 0xBF88, 0x9747, 0xBF89, 0x9748, 0xBF8A,\n\t0x9749, 0xBF8B, 0x974A, 0xBF8C, 0x974B, 0xBF8D, 0x974C, 0xBF8E,\n\t0x974D, 0xBF8F, 0x974E, 0xBF90, 0x974F, 0xBF91, 0x9750, 0xBF92,\n\t0x9751, 0xBF93, 0x9752, 0xBF95, 0x9753, 0xBF96, 0x9754, 0xBF97,\n\t0x9755, 0xBF98, 0x9756, 0xBF99, 0x9757, 0xBF9A, 0x9758, 0xBF9B,\n\t0x9759, 0xBF9C, 0x975A, 0xBF9D, 0x9761, 0xBF9E, 0x9762, 0xBF9F,\n\t0x9763, 0xBFA0, 0x9764, 0xBFA1, 0x9765, 0xBFA2, 0x9766, 0xBFA3,\n\t0x9767, 0xBFA4, 0x9768, 0xBFA5, 0x9769, 0xBFA6, 0x976A, 0xBFA7,\n\t0x976B, 0xBFA8, 0x976C, 0xBFA9, 0x976D, 0xBFAA, 0x976E, 0xBFAB,\n\t0x976F, 0xBFAC, 0x9770, 0xBFAD, 0x9771, 0xBFAE, 0x9772, 0xBFAF,\n\t0x9773, 0xBFB1, 0x9774, 0xBFB2, 0x9775, 0xBFB3, 0x9776, 0xBFB4,\n\t0x9777, 0xBFB5, 0x9778, 0xBFB6, 0x9779, 0xBFB7, 0x977A, 0xBFB8,\n\t0x9781, 0xBFB9, 0x9782, 0xBFBA, 0x9783, 0xBFBB, 0x9784, 0xBFBC,\n\t0x9785, 0xBFBD, 0x9786, 0xBFBE, 0x9787, 0xBFBF, 0x9788, 0xBFC0,\n\t0x9789, 0xBFC1, 0x978A, 0xBFC2, 0x978B, 0xBFC3, 0x978C, 0xBFC4,\n\t0x978D, 0xBFC6, 0x978E, 0xBFC7, 0x978F, 0xBFC8, 0x9790, 0xBFC9,\n\t0x9791, 0xBFCA, 0x9792, 0xBFCB, 0x9793, 0xBFCE, 0x9794, 0xBFCF,\n\t0x9795, 0xBFD1, 0x9796, 0xBFD2, 0x9797, 0xBFD3, 0x9798, 0xBFD5,\n\t0x9799, 0xBFD6, 0x979A, 0xBFD7, 0x979B, 0xBFD8, 0x979C, 0xBFD9,\n\t0x979D, 0xBFDA, 0x979E, 0xBFDB, 0x979F, 0xBFDD, 0x97A0, 0xBFDE,\n\t0x97A1, 0xBFE0, 0x97A2, 0xBFE2, 0x97A3, 0xBFE3, 0x97A4, 0xBFE4,\n\t0x97A5, 0xBFE5, 0x97A6, 0xBFE6, 0x97A7, 0xBFE7, 0x97A8, 0xBFE8,\n\t0x97A9, 0xBFE9, 0x97AA, 0xBFEA, 0x97AB, 0xBFEB, 0x97AC, 0xBFEC,\n\t0x97AD, 0xBFED, 0x97AE, 0xBFEE, 0x97AF, 0xBFEF, 0x97B0, 0xBFF0,\n\t0x97B1, 0xBFF1, 0x97B2, 0xBFF2, 0x97B3, 0xBFF3, 0x97B4, 0xBFF4,\n\t0x97B5, 0xBFF5, 0x97B6, 0xBFF6, 0x97B7, 0xBFF7, 0x97B8, 0xBFF8,\n\t0x97B9, 0xBFF9, 0x97BA, 0xBFFA, 0x97BB, 0xBFFB, 0x97BC, 0xBFFC,\n\t0x97BD, 0xBFFD, 0x97BE, 0xBFFE, 0x97BF, 0xBFFF, 0x97C0, 0xC000,\n\t0x97C1, 0xC001, 0x97C2, 0xC002, 0x97C3, 0xC003, 0x97C4, 0xC004,\n\t0x97C5, 0xC005, 0x97C6, 0xC006, 0x97C7, 0xC007, 0x97C8, 0xC008,\n\t0x97C9, 0xC009, 0x97CA, 0xC00A, 0x97CB, 0xC00B, 0x97CC, 0xC00C,\n\t0x97CD, 0xC00D, 0x97CE, 0xC00E, 0x97CF, 0xC00F, 0x97D0, 0xC010,\n\t0x97D1, 0xC011, 0x97D2, 0xC012, 0x97D3, 0xC013, 0x97D4, 0xC014,\n\t0x97D5, 0xC015, 0x97D6, 0xC016, 0x97D7, 0xC017, 0x97D8, 0xC018,\n\t0x97D9, 0xC019, 0x97DA, 0xC01A, 0x97DB, 0xC01B, 0x97DC, 0xC01C,\n\t0x97DD, 0xC01D, 0x97DE, 0xC01E, 0x97DF, 0xC01F, 0x97E0, 0xC020,\n\t0x97E1, 0xC021, 0x97E2, 0xC022, 0x97E3, 0xC023, 0x97E4, 0xC024,\n\t0x97E5, 0xC025, 0x97E6, 0xC026, 0x97E7, 0xC027, 0x97E8, 0xC028,\n\t0x97E9, 0xC029, 0x97EA, 0xC02A, 0x97EB, 0xC02B, 0x97EC, 0xC02C,\n\t0x97ED, 0xC02D, 0x97EE, 0xC02E, 0x97EF, 0xC02F, 0x97F0, 0xC030,\n\t0x97F1, 0xC031, 0x97F2, 0xC032, 0x97F3, 0xC033, 0x97F4, 0xC034,\n\t0x97F5, 0xC035, 0x97F6, 0xC036, 0x97F7, 0xC037, 0x97F8, 0xC038,\n\t0x97F9, 0xC039, 0x97FA, 0xC03A, 0x97FB, 0xC03B, 0x97FC, 0xC03D,\n\t0x97FD, 0xC03E, 0x97FE, 0xC03F, 0x9841, 0xC040, 0x9842, 0xC041,\n\t0x9843, 0xC042, 0x9844, 0xC043, 0x9845, 0xC044, 0x9846, 0xC045,\n\t0x9847, 0xC046, 0x9848, 0xC047, 0x9849, 0xC048, 0x984A, 0xC049,\n\t0x984B, 0xC04A, 0x984C, 0xC04B, 0x984D, 0xC04C, 0x984E, 0xC04D,\n\t0x984F, 0xC04E, 0x9850, 0xC04F, 0x9851, 0xC050, 0x9852, 0xC052,\n\t0x9853, 0xC053, 0x9854, 0xC054, 0x9855, 0xC055, 0x9856, 0xC056,\n\t0x9857, 0xC057, 0x9858, 0xC059, 0x9859, 0xC05A, 0x985A, 0xC05B,\n\t0x9861, 0xC05D, 0x9862, 0xC05E, 0x9863, 0xC05F, 0x9864, 0xC061,\n\t0x9865, 0xC062, 0x9866, 0xC063, 0x9867, 0xC064, 0x9868, 0xC065,\n\t0x9869, 0xC066, 0x986A, 0xC067, 0x986B, 0xC06A, 0x986C, 0xC06B,\n\t0x986D, 0xC06C, 0x986E, 0xC06D, 0x986F, 0xC06E, 0x9870, 0xC06F,\n\t0x9871, 0xC070, 0x9872, 0xC071, 0x9873, 0xC072, 0x9874, 0xC073,\n\t0x9875, 0xC074, 0x9876, 0xC075, 0x9877, 0xC076, 0x9878, 0xC077,\n\t0x9879, 0xC078, 0x987A, 0xC079, 0x9881, 0xC07A, 0x9882, 0xC07B,\n\t0x9883, 0xC07C, 0x9884, 0xC07D, 0x9885, 0xC07E, 0x9886, 0xC07F,\n\t0x9887, 0xC080, 0x9888, 0xC081, 0x9889, 0xC082, 0x988A, 0xC083,\n\t0x988B, 0xC084, 0x988C, 0xC085, 0x988D, 0xC086, 0x988E, 0xC087,\n\t0x988F, 0xC088, 0x9890, 0xC089, 0x9891, 0xC08A, 0x9892, 0xC08B,\n\t0x9893, 0xC08C, 0x9894, 0xC08D, 0x9895, 0xC08E, 0x9896, 0xC08F,\n\t0x9897, 0xC092, 0x9898, 0xC093, 0x9899, 0xC095, 0x989A, 0xC096,\n\t0x989B, 0xC097, 0x989C, 0xC099, 0x989D, 0xC09A, 0x989E, 0xC09B,\n\t0x989F, 0xC09C, 0x98A0, 0xC09D, 0x98A1, 0xC09E, 0x98A2, 0xC09F,\n\t0x98A3, 0xC0A2, 0x98A4, 0xC0A4, 0x98A5, 0xC0A6, 0x98A6, 0xC0A7,\n\t0x98A7, 0xC0A8, 0x98A8, 0xC0A9, 0x98A9, 0xC0AA, 0x98AA, 0xC0AB,\n\t0x98AB, 0xC0AE, 0x98AC, 0xC0B1, 0x98AD, 0xC0B2, 0x98AE, 0xC0B7,\n\t0x98AF, 0xC0B8, 0x98B0, 0xC0B9, 0x98B1, 0xC0BA, 0x98B2, 0xC0BB,\n\t0x98B3, 0xC0BE, 0x98B4, 0xC0C2, 0x98B5, 0xC0C3, 0x98B6, 0xC0C4,\n\t0x98B7, 0xC0C6, 0x98B8, 0xC0C7, 0x98B9, 0xC0CA, 0x98BA, 0xC0CB,\n\t0x98BB, 0xC0CD, 0x98BC, 0xC0CE, 0x98BD, 0xC0CF, 0x98BE, 0xC0D1,\n\t0x98BF, 0xC0D2, 0x98C0, 0xC0D3, 0x98C1, 0xC0D4, 0x98C2, 0xC0D5,\n\t0x98C3, 0xC0D6, 0x98C4, 0xC0D7, 0x98C5, 0xC0DA, 0x98C6, 0xC0DE,\n\t0x98C7, 0xC0DF, 0x98C8, 0xC0E0, 0x98C9, 0xC0E1, 0x98CA, 0xC0E2,\n\t0x98CB, 0xC0E3, 0x98CC, 0xC0E6, 0x98CD, 0xC0E7, 0x98CE, 0xC0E9,\n\t0x98CF, 0xC0EA, 0x98D0, 0xC0EB, 0x98D1, 0xC0ED, 0x98D2, 0xC0EE,\n\t0x98D3, 0xC0EF, 0x98D4, 0xC0F0, 0x98D5, 0xC0F1, 0x98D6, 0xC0F2,\n\t0x98D7, 0xC0F3, 0x98D8, 0xC0F6, 0x98D9, 0xC0F8, 0x98DA, 0xC0FA,\n\t0x98DB, 0xC0FB, 0x98DC, 0xC0FC, 0x98DD, 0xC0FD, 0x98DE, 0xC0FE,\n\t0x98DF, 0xC0FF, 0x98E0, 0xC101, 0x98E1, 0xC102, 0x98E2, 0xC103,\n\t0x98E3, 0xC105, 0x98E4, 0xC106, 0x98E5, 0xC107, 0x98E6, 0xC109,\n\t0x98E7, 0xC10A, 0x98E8, 0xC10B, 0x98E9, 0xC10C, 0x98EA, 0xC10D,\n\t0x98EB, 0xC10E, 0x98EC, 0xC10F, 0x98ED, 0xC111, 0x98EE, 0xC112,\n\t0x98EF, 0xC113, 0x98F0, 0xC114, 0x98F1, 0xC116, 0x98F2, 0xC117,\n\t0x98F3, 0xC118, 0x98F4, 0xC119, 0x98F5, 0xC11A, 0x98F6, 0xC11B,\n\t0x98F7, 0xC121, 0x98F8, 0xC122, 0x98F9, 0xC125, 0x98FA, 0xC128,\n\t0x98FB, 0xC129, 0x98FC, 0xC12A, 0x98FD, 0xC12B, 0x98FE, 0xC12E,\n\t0x9941, 0xC132, 0x9942, 0xC133, 0x9943, 0xC134, 0x9944, 0xC135,\n\t0x9945, 0xC137, 0x9946, 0xC13A, 0x9947, 0xC13B, 0x9948, 0xC13D,\n\t0x9949, 0xC13E, 0x994A, 0xC13F, 0x994B, 0xC141, 0x994C, 0xC142,\n\t0x994D, 0xC143, 0x994E, 0xC144, 0x994F, 0xC145, 0x9950, 0xC146,\n\t0x9951, 0xC147, 0x9952, 0xC14A, 0x9953, 0xC14E, 0x9954, 0xC14F,\n\t0x9955, 0xC150, 0x9956, 0xC151, 0x9957, 0xC152, 0x9958, 0xC153,\n\t0x9959, 0xC156, 0x995A, 0xC157, 0x9961, 0xC159, 0x9962, 0xC15A,\n\t0x9963, 0xC15B, 0x9964, 0xC15D, 0x9965, 0xC15E, 0x9966, 0xC15F,\n\t0x9967, 0xC160, 0x9968, 0xC161, 0x9969, 0xC162, 0x996A, 0xC163,\n\t0x996B, 0xC166, 0x996C, 0xC16A, 0x996D, 0xC16B, 0x996E, 0xC16C,\n\t0x996F, 0xC16D, 0x9970, 0xC16E, 0x9971, 0xC16F, 0x9972, 0xC171,\n\t0x9973, 0xC172, 0x9974, 0xC173, 0x9975, 0xC175, 0x9976, 0xC176,\n\t0x9977, 0xC177, 0x9978, 0xC179, 0x9979, 0xC17A, 0x997A, 0xC17B,\n\t0x9981, 0xC17C, 0x9982, 0xC17D, 0x9983, 0xC17E, 0x9984, 0xC17F,\n\t0x9985, 0xC180, 0x9986, 0xC181, 0x9987, 0xC182, 0x9988, 0xC183,\n\t0x9989, 0xC184, 0x998A, 0xC186, 0x998B, 0xC187, 0x998C, 0xC188,\n\t0x998D, 0xC189, 0x998E, 0xC18A, 0x998F, 0xC18B, 0x9990, 0xC18F,\n\t0x9991, 0xC191, 0x9992, 0xC192, 0x9993, 0xC193, 0x9994, 0xC195,\n\t0x9995, 0xC197, 0x9996, 0xC198, 0x9997, 0xC199, 0x9998, 0xC19A,\n\t0x9999, 0xC19B, 0x999A, 0xC19E, 0x999B, 0xC1A0, 0x999C, 0xC1A2,\n\t0x999D, 0xC1A3, 0x999E, 0xC1A4, 0x999F, 0xC1A6, 0x99A0, 0xC1A7,\n\t0x99A1, 0xC1AA, 0x99A2, 0xC1AB, 0x99A3, 0xC1AD, 0x99A4, 0xC1AE,\n\t0x99A5, 0xC1AF, 0x99A6, 0xC1B1, 0x99A7, 0xC1B2, 0x99A8, 0xC1B3,\n\t0x99A9, 0xC1B4, 0x99AA, 0xC1B5, 0x99AB, 0xC1B6, 0x99AC, 0xC1B7,\n\t0x99AD, 0xC1B8, 0x99AE, 0xC1B9, 0x99AF, 0xC1BA, 0x99B0, 0xC1BB,\n\t0x99B1, 0xC1BC, 0x99B2, 0xC1BE, 0x99B3, 0xC1BF, 0x99B4, 0xC1C0,\n\t0x99B5, 0xC1C1, 0x99B6, 0xC1C2, 0x99B7, 0xC1C3, 0x99B8, 0xC1C5,\n\t0x99B9, 0xC1C6, 0x99BA, 0xC1C7, 0x99BB, 0xC1C9, 0x99BC, 0xC1CA,\n\t0x99BD, 0xC1CB, 0x99BE, 0xC1CD, 0x99BF, 0xC1CE, 0x99C0, 0xC1CF,\n\t0x99C1, 0xC1D0, 0x99C2, 0xC1D1, 0x99C3, 0xC1D2, 0x99C4, 0xC1D3,\n\t0x99C5, 0xC1D5, 0x99C6, 0xC1D6, 0x99C7, 0xC1D9, 0x99C8, 0xC1DA,\n\t0x99C9, 0xC1DB, 0x99CA, 0xC1DC, 0x99CB, 0xC1DD, 0x99CC, 0xC1DE,\n\t0x99CD, 0xC1DF, 0x99CE, 0xC1E1, 0x99CF, 0xC1E2, 0x99D0, 0xC1E3,\n\t0x99D1, 0xC1E5, 0x99D2, 0xC1E6, 0x99D3, 0xC1E7, 0x99D4, 0xC1E9,\n\t0x99D5, 0xC1EA, 0x99D6, 0xC1EB, 0x99D7, 0xC1EC, 0x99D8, 0xC1ED,\n\t0x99D9, 0xC1EE, 0x99DA, 0xC1EF, 0x99DB, 0xC1F2, 0x99DC, 0xC1F4,\n\t0x99DD, 0xC1F5, 0x99DE, 0xC1F6, 0x99DF, 0xC1F7, 0x99E0, 0xC1F8,\n\t0x99E1, 0xC1F9, 0x99E2, 0xC1FA, 0x99E3, 0xC1FB, 0x99E4, 0xC1FE,\n\t0x99E5, 0xC1FF, 0x99E6, 0xC201, 0x99E7, 0xC202, 0x99E8, 0xC203,\n\t0x99E9, 0xC205, 0x99EA, 0xC206, 0x99EB, 0xC207, 0x99EC, 0xC208,\n\t0x99ED, 0xC209, 0x99EE, 0xC20A, 0x99EF, 0xC20B, 0x99F0, 0xC20E,\n\t0x99F1, 0xC210, 0x99F2, 0xC212, 0x99F3, 0xC213, 0x99F4, 0xC214,\n\t0x99F5, 0xC215, 0x99F6, 0xC216, 0x99F7, 0xC217, 0x99F8, 0xC21A,\n\t0x99F9, 0xC21B, 0x99FA, 0xC21D, 0x99FB, 0xC21E, 0x99FC, 0xC221,\n\t0x99FD, 0xC222, 0x99FE, 0xC223, 0x9A41, 0xC224, 0x9A42, 0xC225,\n\t0x9A43, 0xC226, 0x9A44, 0xC227, 0x9A45, 0xC22A, 0x9A46, 0xC22C,\n\t0x9A47, 0xC22E, 0x9A48, 0xC230, 0x9A49, 0xC233, 0x9A4A, 0xC235,\n\t0x9A4B, 0xC236, 0x9A4C, 0xC237, 0x9A4D, 0xC238, 0x9A4E, 0xC239,\n\t0x9A4F, 0xC23A, 0x9A50, 0xC23B, 0x9A51, 0xC23C, 0x9A52, 0xC23D,\n\t0x9A53, 0xC23E, 0x9A54, 0xC23F, 0x9A55, 0xC240, 0x9A56, 0xC241,\n\t0x9A57, 0xC242, 0x9A58, 0xC243, 0x9A59, 0xC244, 0x9A5A, 0xC245,\n\t0x9A61, 0xC246, 0x9A62, 0xC247, 0x9A63, 0xC249, 0x9A64, 0xC24A,\n\t0x9A65, 0xC24B, 0x9A66, 0xC24C, 0x9A67, 0xC24D, 0x9A68, 0xC24E,\n\t0x9A69, 0xC24F, 0x9A6A, 0xC252, 0x9A6B, 0xC253, 0x9A6C, 0xC255,\n\t0x9A6D, 0xC256, 0x9A6E, 0xC257, 0x9A6F, 0xC259, 0x9A70, 0xC25A,\n\t0x9A71, 0xC25B, 0x9A72, 0xC25C, 0x9A73, 0xC25D, 0x9A74, 0xC25E,\n\t0x9A75, 0xC25F, 0x9A76, 0xC261, 0x9A77, 0xC262, 0x9A78, 0xC263,\n\t0x9A79, 0xC264, 0x9A7A, 0xC266, 0x9A81, 0xC267, 0x9A82, 0xC268,\n\t0x9A83, 0xC269, 0x9A84, 0xC26A, 0x9A85, 0xC26B, 0x9A86, 0xC26E,\n\t0x9A87, 0xC26F, 0x9A88, 0xC271, 0x9A89, 0xC272, 0x9A8A, 0xC273,\n\t0x9A8B, 0xC275, 0x9A8C, 0xC276, 0x9A8D, 0xC277, 0x9A8E, 0xC278,\n\t0x9A8F, 0xC279, 0x9A90, 0xC27A, 0x9A91, 0xC27B, 0x9A92, 0xC27E,\n\t0x9A93, 0xC280, 0x9A94, 0xC282, 0x9A95, 0xC283, 0x9A96, 0xC284,\n\t0x9A97, 0xC285, 0x9A98, 0xC286, 0x9A99, 0xC287, 0x9A9A, 0xC28A,\n\t0x9A9B, 0xC28B, 0x9A9C, 0xC28C, 0x9A9D, 0xC28D, 0x9A9E, 0xC28E,\n\t0x9A9F, 0xC28F, 0x9AA0, 0xC291, 0x9AA1, 0xC292, 0x9AA2, 0xC293,\n\t0x9AA3, 0xC294, 0x9AA4, 0xC295, 0x9AA5, 0xC296, 0x9AA6, 0xC297,\n\t0x9AA7, 0xC299, 0x9AA8, 0xC29A, 0x9AA9, 0xC29C, 0x9AAA, 0xC29E,\n\t0x9AAB, 0xC29F, 0x9AAC, 0xC2A0, 0x9AAD, 0xC2A1, 0x9AAE, 0xC2A2,\n\t0x9AAF, 0xC2A3, 0x9AB0, 0xC2A6, 0x9AB1, 0xC2A7, 0x9AB2, 0xC2A9,\n\t0x9AB3, 0xC2AA, 0x9AB4, 0xC2AB, 0x9AB5, 0xC2AE, 0x9AB6, 0xC2AF,\n\t0x9AB7, 0xC2B0, 0x9AB8, 0xC2B1, 0x9AB9, 0xC2B2, 0x9ABA, 0xC2B3,\n\t0x9ABB, 0xC2B6, 0x9ABC, 0xC2B8, 0x9ABD, 0xC2BA, 0x9ABE, 0xC2BB,\n\t0x9ABF, 0xC2BC, 0x9AC0, 0xC2BD, 0x9AC1, 0xC2BE, 0x9AC2, 0xC2BF,\n\t0x9AC3, 0xC2C0, 0x9AC4, 0xC2C1, 0x9AC5, 0xC2C2, 0x9AC6, 0xC2C3,\n\t0x9AC7, 0xC2C4, 0x9AC8, 0xC2C5, 0x9AC9, 0xC2C6, 0x9ACA, 0xC2C7,\n\t0x9ACB, 0xC2C8, 0x9ACC, 0xC2C9, 0x9ACD, 0xC2CA, 0x9ACE, 0xC2CB,\n\t0x9ACF, 0xC2CC, 0x9AD0, 0xC2CD, 0x9AD1, 0xC2CE, 0x9AD2, 0xC2CF,\n\t0x9AD3, 0xC2D0, 0x9AD4, 0xC2D1, 0x9AD5, 0xC2D2, 0x9AD6, 0xC2D3,\n\t0x9AD7, 0xC2D4, 0x9AD8, 0xC2D5, 0x9AD9, 0xC2D6, 0x9ADA, 0xC2D7,\n\t0x9ADB, 0xC2D8, 0x9ADC, 0xC2D9, 0x9ADD, 0xC2DA, 0x9ADE, 0xC2DB,\n\t0x9ADF, 0xC2DE, 0x9AE0, 0xC2DF, 0x9AE1, 0xC2E1, 0x9AE2, 0xC2E2,\n\t0x9AE3, 0xC2E5, 0x9AE4, 0xC2E6, 0x9AE5, 0xC2E7, 0x9AE6, 0xC2E8,\n\t0x9AE7, 0xC2E9, 0x9AE8, 0xC2EA, 0x9AE9, 0xC2EE, 0x9AEA, 0xC2F0,\n\t0x9AEB, 0xC2F2, 0x9AEC, 0xC2F3, 0x9AED, 0xC2F4, 0x9AEE, 0xC2F5,\n\t0x9AEF, 0xC2F7, 0x9AF0, 0xC2FA, 0x9AF1, 0xC2FD, 0x9AF2, 0xC2FE,\n\t0x9AF3, 0xC2FF, 0x9AF4, 0xC301, 0x9AF5, 0xC302, 0x9AF6, 0xC303,\n\t0x9AF7, 0xC304, 0x9AF8, 0xC305, 0x9AF9, 0xC306, 0x9AFA, 0xC307,\n\t0x9AFB, 0xC30A, 0x9AFC, 0xC30B, 0x9AFD, 0xC30E, 0x9AFE, 0xC30F,\n\t0x9B41, 0xC310, 0x9B42, 0xC311, 0x9B43, 0xC312, 0x9B44, 0xC316,\n\t0x9B45, 0xC317, 0x9B46, 0xC319, 0x9B47, 0xC31A, 0x9B48, 0xC31B,\n\t0x9B49, 0xC31D, 0x9B4A, 0xC31E, 0x9B4B, 0xC31F, 0x9B4C, 0xC320,\n\t0x9B4D, 0xC321, 0x9B4E, 0xC322, 0x9B4F, 0xC323, 0x9B50, 0xC326,\n\t0x9B51, 0xC327, 0x9B52, 0xC32A, 0x9B53, 0xC32B, 0x9B54, 0xC32C,\n\t0x9B55, 0xC32D, 0x9B56, 0xC32E, 0x9B57, 0xC32F, 0x9B58, 0xC330,\n\t0x9B59, 0xC331, 0x9B5A, 0xC332, 0x9B61, 0xC333, 0x9B62, 0xC334,\n\t0x9B63, 0xC335, 0x9B64, 0xC336, 0x9B65, 0xC337, 0x9B66, 0xC338,\n\t0x9B67, 0xC339, 0x9B68, 0xC33A, 0x9B69, 0xC33B, 0x9B6A, 0xC33C,\n\t0x9B6B, 0xC33D, 0x9B6C, 0xC33E, 0x9B6D, 0xC33F, 0x9B6E, 0xC340,\n\t0x9B6F, 0xC341, 0x9B70, 0xC342, 0x9B71, 0xC343, 0x9B72, 0xC344,\n\t0x9B73, 0xC346, 0x9B74, 0xC347, 0x9B75, 0xC348, 0x9B76, 0xC349,\n\t0x9B77, 0xC34A, 0x9B78, 0xC34B, 0x9B79, 0xC34C, 0x9B7A, 0xC34D,\n\t0x9B81, 0xC34E, 0x9B82, 0xC34F, 0x9B83, 0xC350, 0x9B84, 0xC351,\n\t0x9B85, 0xC352, 0x9B86, 0xC353, 0x9B87, 0xC354, 0x9B88, 0xC355,\n\t0x9B89, 0xC356, 0x9B8A, 0xC357, 0x9B8B, 0xC358, 0x9B8C, 0xC359,\n\t0x9B8D, 0xC35A, 0x9B8E, 0xC35B, 0x9B8F, 0xC35C, 0x9B90, 0xC35D,\n\t0x9B91, 0xC35E, 0x9B92, 0xC35F, 0x9B93, 0xC360, 0x9B94, 0xC361,\n\t0x9B95, 0xC362, 0x9B96, 0xC363, 0x9B97, 0xC364, 0x9B98, 0xC365,\n\t0x9B99, 0xC366, 0x9B9A, 0xC367, 0x9B9B, 0xC36A, 0x9B9C, 0xC36B,\n\t0x9B9D, 0xC36D, 0x9B9E, 0xC36E, 0x9B9F, 0xC36F, 0x9BA0, 0xC371,\n\t0x9BA1, 0xC373, 0x9BA2, 0xC374, 0x9BA3, 0xC375, 0x9BA4, 0xC376,\n\t0x9BA5, 0xC377, 0x9BA6, 0xC37A, 0x9BA7, 0xC37B, 0x9BA8, 0xC37E,\n\t0x9BA9, 0xC37F, 0x9BAA, 0xC380, 0x9BAB, 0xC381, 0x9BAC, 0xC382,\n\t0x9BAD, 0xC383, 0x9BAE, 0xC385, 0x9BAF, 0xC386, 0x9BB0, 0xC387,\n\t0x9BB1, 0xC389, 0x9BB2, 0xC38A, 0x9BB3, 0xC38B, 0x9BB4, 0xC38D,\n\t0x9BB5, 0xC38E, 0x9BB6, 0xC38F, 0x9BB7, 0xC390, 0x9BB8, 0xC391,\n\t0x9BB9, 0xC392, 0x9BBA, 0xC393, 0x9BBB, 0xC394, 0x9BBC, 0xC395,\n\t0x9BBD, 0xC396, 0x9BBE, 0xC397, 0x9BBF, 0xC398, 0x9BC0, 0xC399,\n\t0x9BC1, 0xC39A, 0x9BC2, 0xC39B, 0x9BC3, 0xC39C, 0x9BC4, 0xC39D,\n\t0x9BC5, 0xC39E, 0x9BC6, 0xC39F, 0x9BC7, 0xC3A0, 0x9BC8, 0xC3A1,\n\t0x9BC9, 0xC3A2, 0x9BCA, 0xC3A3, 0x9BCB, 0xC3A4, 0x9BCC, 0xC3A5,\n\t0x9BCD, 0xC3A6, 0x9BCE, 0xC3A7, 0x9BCF, 0xC3A8, 0x9BD0, 0xC3A9,\n\t0x9BD1, 0xC3AA, 0x9BD2, 0xC3AB, 0x9BD3, 0xC3AC, 0x9BD4, 0xC3AD,\n\t0x9BD5, 0xC3AE, 0x9BD6, 0xC3AF, 0x9BD7, 0xC3B0, 0x9BD8, 0xC3B1,\n\t0x9BD9, 0xC3B2, 0x9BDA, 0xC3B3, 0x9BDB, 0xC3B4, 0x9BDC, 0xC3B5,\n\t0x9BDD, 0xC3B6, 0x9BDE, 0xC3B7, 0x9BDF, 0xC3B8, 0x9BE0, 0xC3B9,\n\t0x9BE1, 0xC3BA, 0x9BE2, 0xC3BB, 0x9BE3, 0xC3BC, 0x9BE4, 0xC3BD,\n\t0x9BE5, 0xC3BE, 0x9BE6, 0xC3BF, 0x9BE7, 0xC3C1, 0x9BE8, 0xC3C2,\n\t0x9BE9, 0xC3C3, 0x9BEA, 0xC3C4, 0x9BEB, 0xC3C5, 0x9BEC, 0xC3C6,\n\t0x9BED, 0xC3C7, 0x9BEE, 0xC3C8, 0x9BEF, 0xC3C9, 0x9BF0, 0xC3CA,\n\t0x9BF1, 0xC3CB, 0x9BF2, 0xC3CC, 0x9BF3, 0xC3CD, 0x9BF4, 0xC3CE,\n\t0x9BF5, 0xC3CF, 0x9BF6, 0xC3D0, 0x9BF7, 0xC3D1, 0x9BF8, 0xC3D2,\n\t0x9BF9, 0xC3D3, 0x9BFA, 0xC3D4, 0x9BFB, 0xC3D5, 0x9BFC, 0xC3D6,\n\t0x9BFD, 0xC3D7, 0x9BFE, 0xC3DA, 0x9C41, 0xC3DB, 0x9C42, 0xC3DD,\n\t0x9C43, 0xC3DE, 0x9C44, 0xC3E1, 0x9C45, 0xC3E3, 0x9C46, 0xC3E4,\n\t0x9C47, 0xC3E5, 0x9C48, 0xC3E6, 0x9C49, 0xC3E7, 0x9C4A, 0xC3EA,\n\t0x9C4B, 0xC3EB, 0x9C4C, 0xC3EC, 0x9C4D, 0xC3EE, 0x9C4E, 0xC3EF,\n\t0x9C4F, 0xC3F0, 0x9C50, 0xC3F1, 0x9C51, 0xC3F2, 0x9C52, 0xC3F3,\n\t0x9C53, 0xC3F6, 0x9C54, 0xC3F7, 0x9C55, 0xC3F9, 0x9C56, 0xC3FA,\n\t0x9C57, 0xC3FB, 0x9C58, 0xC3FC, 0x9C59, 0xC3FD, 0x9C5A, 0xC3FE,\n\t0x9C61, 0xC3FF, 0x9C62, 0xC400, 0x9C63, 0xC401, 0x9C64, 0xC402,\n\t0x9C65, 0xC403, 0x9C66, 0xC404, 0x9C67, 0xC405, 0x9C68, 0xC406,\n\t0x9C69, 0xC407, 0x9C6A, 0xC409, 0x9C6B, 0xC40A, 0x9C6C, 0xC40B,\n\t0x9C6D, 0xC40C, 0x9C6E, 0xC40D, 0x9C6F, 0xC40E, 0x9C70, 0xC40F,\n\t0x9C71, 0xC411, 0x9C72, 0xC412, 0x9C73, 0xC413, 0x9C74, 0xC414,\n\t0x9C75, 0xC415, 0x9C76, 0xC416, 0x9C77, 0xC417, 0x9C78, 0xC418,\n\t0x9C79, 0xC419, 0x9C7A, 0xC41A, 0x9C81, 0xC41B, 0x9C82, 0xC41C,\n\t0x9C83, 0xC41D, 0x9C84, 0xC41E, 0x9C85, 0xC41F, 0x9C86, 0xC420,\n\t0x9C87, 0xC421, 0x9C88, 0xC422, 0x9C89, 0xC423, 0x9C8A, 0xC425,\n\t0x9C8B, 0xC426, 0x9C8C, 0xC427, 0x9C8D, 0xC428, 0x9C8E, 0xC429,\n\t0x9C8F, 0xC42A, 0x9C90, 0xC42B, 0x9C91, 0xC42D, 0x9C92, 0xC42E,\n\t0x9C93, 0xC42F, 0x9C94, 0xC431, 0x9C95, 0xC432, 0x9C96, 0xC433,\n\t0x9C97, 0xC435, 0x9C98, 0xC436, 0x9C99, 0xC437, 0x9C9A, 0xC438,\n\t0x9C9B, 0xC439, 0x9C9C, 0xC43A, 0x9C9D, 0xC43B, 0x9C9E, 0xC43E,\n\t0x9C9F, 0xC43F, 0x9CA0, 0xC440, 0x9CA1, 0xC441, 0x9CA2, 0xC442,\n\t0x9CA3, 0xC443, 0x9CA4, 0xC444, 0x9CA5, 0xC445, 0x9CA6, 0xC446,\n\t0x9CA7, 0xC447, 0x9CA8, 0xC449, 0x9CA9, 0xC44A, 0x9CAA, 0xC44B,\n\t0x9CAB, 0xC44C, 0x9CAC, 0xC44D, 0x9CAD, 0xC44E, 0x9CAE, 0xC44F,\n\t0x9CAF, 0xC450, 0x9CB0, 0xC451, 0x9CB1, 0xC452, 0x9CB2, 0xC453,\n\t0x9CB3, 0xC454, 0x9CB4, 0xC455, 0x9CB5, 0xC456, 0x9CB6, 0xC457,\n\t0x9CB7, 0xC458, 0x9CB8, 0xC459, 0x9CB9, 0xC45A, 0x9CBA, 0xC45B,\n\t0x9CBB, 0xC45C, 0x9CBC, 0xC45D, 0x9CBD, 0xC45E, 0x9CBE, 0xC45F,\n\t0x9CBF, 0xC460, 0x9CC0, 0xC461, 0x9CC1, 0xC462, 0x9CC2, 0xC463,\n\t0x9CC3, 0xC466, 0x9CC4, 0xC467, 0x9CC5, 0xC469, 0x9CC6, 0xC46A,\n\t0x9CC7, 0xC46B, 0x9CC8, 0xC46D, 0x9CC9, 0xC46E, 0x9CCA, 0xC46F,\n\t0x9CCB, 0xC470, 0x9CCC, 0xC471, 0x9CCD, 0xC472, 0x9CCE, 0xC473,\n\t0x9CCF, 0xC476, 0x9CD0, 0xC477, 0x9CD1, 0xC478, 0x9CD2, 0xC47A,\n\t0x9CD3, 0xC47B, 0x9CD4, 0xC47C, 0x9CD5, 0xC47D, 0x9CD6, 0xC47E,\n\t0x9CD7, 0xC47F, 0x9CD8, 0xC481, 0x9CD9, 0xC482, 0x9CDA, 0xC483,\n\t0x9CDB, 0xC484, 0x9CDC, 0xC485, 0x9CDD, 0xC486, 0x9CDE, 0xC487,\n\t0x9CDF, 0xC488, 0x9CE0, 0xC489, 0x9CE1, 0xC48A, 0x9CE2, 0xC48B,\n\t0x9CE3, 0xC48C, 0x9CE4, 0xC48D, 0x9CE5, 0xC48E, 0x9CE6, 0xC48F,\n\t0x9CE7, 0xC490, 0x9CE8, 0xC491, 0x9CE9, 0xC492, 0x9CEA, 0xC493,\n\t0x9CEB, 0xC495, 0x9CEC, 0xC496, 0x9CED, 0xC497, 0x9CEE, 0xC498,\n\t0x9CEF, 0xC499, 0x9CF0, 0xC49A, 0x9CF1, 0xC49B, 0x9CF2, 0xC49D,\n\t0x9CF3, 0xC49E, 0x9CF4, 0xC49F, 0x9CF5, 0xC4A0, 0x9CF6, 0xC4A1,\n\t0x9CF7, 0xC4A2, 0x9CF8, 0xC4A3, 0x9CF9, 0xC4A4, 0x9CFA, 0xC4A5,\n\t0x9CFB, 0xC4A6, 0x9CFC, 0xC4A7, 0x9CFD, 0xC4A8, 0x9CFE, 0xC4A9,\n\t0x9D41, 0xC4AA, 0x9D42, 0xC4AB, 0x9D43, 0xC4AC, 0x9D44, 0xC4AD,\n\t0x9D45, 0xC4AE, 0x9D46, 0xC4AF, 0x9D47, 0xC4B0, 0x9D48, 0xC4B1,\n\t0x9D49, 0xC4B2, 0x9D4A, 0xC4B3, 0x9D4B, 0xC4B4, 0x9D4C, 0xC4B5,\n\t0x9D4D, 0xC4B6, 0x9D4E, 0xC4B7, 0x9D4F, 0xC4B9, 0x9D50, 0xC4BA,\n\t0x9D51, 0xC4BB, 0x9D52, 0xC4BD, 0x9D53, 0xC4BE, 0x9D54, 0xC4BF,\n\t0x9D55, 0xC4C0, 0x9D56, 0xC4C1, 0x9D57, 0xC4C2, 0x9D58, 0xC4C3,\n\t0x9D59, 0xC4C4, 0x9D5A, 0xC4C5, 0x9D61, 0xC4C6, 0x9D62, 0xC4C7,\n\t0x9D63, 0xC4C8, 0x9D64, 0xC4C9, 0x9D65, 0xC4CA, 0x9D66, 0xC4CB,\n\t0x9D67, 0xC4CC, 0x9D68, 0xC4CD, 0x9D69, 0xC4CE, 0x9D6A, 0xC4CF,\n\t0x9D6B, 0xC4D0, 0x9D6C, 0xC4D1, 0x9D6D, 0xC4D2, 0x9D6E, 0xC4D3,\n\t0x9D6F, 0xC4D4, 0x9D70, 0xC4D5, 0x9D71, 0xC4D6, 0x9D72, 0xC4D7,\n\t0x9D73, 0xC4D8, 0x9D74, 0xC4D9, 0x9D75, 0xC4DA, 0x9D76, 0xC4DB,\n\t0x9D77, 0xC4DC, 0x9D78, 0xC4DD, 0x9D79, 0xC4DE, 0x9D7A, 0xC4DF,\n\t0x9D81, 0xC4E0, 0x9D82, 0xC4E1, 0x9D83, 0xC4E2, 0x9D84, 0xC4E3,\n\t0x9D85, 0xC4E4, 0x9D86, 0xC4E5, 0x9D87, 0xC4E6, 0x9D88, 0xC4E7,\n\t0x9D89, 0xC4E8, 0x9D8A, 0xC4EA, 0x9D8B, 0xC4EB, 0x9D8C, 0xC4EC,\n\t0x9D8D, 0xC4ED, 0x9D8E, 0xC4EE, 0x9D8F, 0xC4EF, 0x9D90, 0xC4F2,\n\t0x9D91, 0xC4F3, 0x9D92, 0xC4F5, 0x9D93, 0xC4F6, 0x9D94, 0xC4F7,\n\t0x9D95, 0xC4F9, 0x9D96, 0xC4FB, 0x9D97, 0xC4FC, 0x9D98, 0xC4FD,\n\t0x9D99, 0xC4FE, 0x9D9A, 0xC502, 0x9D9B, 0xC503, 0x9D9C, 0xC504,\n\t0x9D9D, 0xC505, 0x9D9E, 0xC506, 0x9D9F, 0xC507, 0x9DA0, 0xC508,\n\t0x9DA1, 0xC509, 0x9DA2, 0xC50A, 0x9DA3, 0xC50B, 0x9DA4, 0xC50D,\n\t0x9DA5, 0xC50E, 0x9DA6, 0xC50F, 0x9DA7, 0xC511, 0x9DA8, 0xC512,\n\t0x9DA9, 0xC513, 0x9DAA, 0xC515, 0x9DAB, 0xC516, 0x9DAC, 0xC517,\n\t0x9DAD, 0xC518, 0x9DAE, 0xC519, 0x9DAF, 0xC51A, 0x9DB0, 0xC51B,\n\t0x9DB1, 0xC51D, 0x9DB2, 0xC51E, 0x9DB3, 0xC51F, 0x9DB4, 0xC520,\n\t0x9DB5, 0xC521, 0x9DB6, 0xC522, 0x9DB7, 0xC523, 0x9DB8, 0xC524,\n\t0x9DB9, 0xC525, 0x9DBA, 0xC526, 0x9DBB, 0xC527, 0x9DBC, 0xC52A,\n\t0x9DBD, 0xC52B, 0x9DBE, 0xC52D, 0x9DBF, 0xC52E, 0x9DC0, 0xC52F,\n\t0x9DC1, 0xC531, 0x9DC2, 0xC532, 0x9DC3, 0xC533, 0x9DC4, 0xC534,\n\t0x9DC5, 0xC535, 0x9DC6, 0xC536, 0x9DC7, 0xC537, 0x9DC8, 0xC53A,\n\t0x9DC9, 0xC53C, 0x9DCA, 0xC53E, 0x9DCB, 0xC53F, 0x9DCC, 0xC540,\n\t0x9DCD, 0xC541, 0x9DCE, 0xC542, 0x9DCF, 0xC543, 0x9DD0, 0xC546,\n\t0x9DD1, 0xC547, 0x9DD2, 0xC54B, 0x9DD3, 0xC54F, 0x9DD4, 0xC550,\n\t0x9DD5, 0xC551, 0x9DD6, 0xC552, 0x9DD7, 0xC556, 0x9DD8, 0xC55A,\n\t0x9DD9, 0xC55B, 0x9DDA, 0xC55C, 0x9DDB, 0xC55F, 0x9DDC, 0xC562,\n\t0x9DDD, 0xC563, 0x9DDE, 0xC565, 0x9DDF, 0xC566, 0x9DE0, 0xC567,\n\t0x9DE1, 0xC569, 0x9DE2, 0xC56A, 0x9DE3, 0xC56B, 0x9DE4, 0xC56C,\n\t0x9DE5, 0xC56D, 0x9DE6, 0xC56E, 0x9DE7, 0xC56F, 0x9DE8, 0xC572,\n\t0x9DE9, 0xC576, 0x9DEA, 0xC577, 0x9DEB, 0xC578, 0x9DEC, 0xC579,\n\t0x9DED, 0xC57A, 0x9DEE, 0xC57B, 0x9DEF, 0xC57E, 0x9DF0, 0xC57F,\n\t0x9DF1, 0xC581, 0x9DF2, 0xC582, 0x9DF3, 0xC583, 0x9DF4, 0xC585,\n\t0x9DF5, 0xC586, 0x9DF6, 0xC588, 0x9DF7, 0xC589, 0x9DF8, 0xC58A,\n\t0x9DF9, 0xC58B, 0x9DFA, 0xC58E, 0x9DFB, 0xC590, 0x9DFC, 0xC592,\n\t0x9DFD, 0xC593, 0x9DFE, 0xC594, 0x9E41, 0xC596, 0x9E42, 0xC599,\n\t0x9E43, 0xC59A, 0x9E44, 0xC59B, 0x9E45, 0xC59D, 0x9E46, 0xC59E,\n\t0x9E47, 0xC59F, 0x9E48, 0xC5A1, 0x9E49, 0xC5A2, 0x9E4A, 0xC5A3,\n\t0x9E4B, 0xC5A4, 0x9E4C, 0xC5A5, 0x9E4D, 0xC5A6, 0x9E4E, 0xC5A7,\n\t0x9E4F, 0xC5A8, 0x9E50, 0xC5AA, 0x9E51, 0xC5AB, 0x9E52, 0xC5AC,\n\t0x9E53, 0xC5AD, 0x9E54, 0xC5AE, 0x9E55, 0xC5AF, 0x9E56, 0xC5B0,\n\t0x9E57, 0xC5B1, 0x9E58, 0xC5B2, 0x9E59, 0xC5B3, 0x9E5A, 0xC5B6,\n\t0x9E61, 0xC5B7, 0x9E62, 0xC5BA, 0x9E63, 0xC5BF, 0x9E64, 0xC5C0,\n\t0x9E65, 0xC5C1, 0x9E66, 0xC5C2, 0x9E67, 0xC5C3, 0x9E68, 0xC5CB,\n\t0x9E69, 0xC5CD, 0x9E6A, 0xC5CF, 0x9E6B, 0xC5D2, 0x9E6C, 0xC5D3,\n\t0x9E6D, 0xC5D5, 0x9E6E, 0xC5D6, 0x9E6F, 0xC5D7, 0x9E70, 0xC5D9,\n\t0x9E71, 0xC5DA, 0x9E72, 0xC5DB, 0x9E73, 0xC5DC, 0x9E74, 0xC5DD,\n\t0x9E75, 0xC5DE, 0x9E76, 0xC5DF, 0x9E77, 0xC5E2, 0x9E78, 0xC5E4,\n\t0x9E79, 0xC5E6, 0x9E7A, 0xC5E7, 0x9E81, 0xC5E8, 0x9E82, 0xC5E9,\n\t0x9E83, 0xC5EA, 0x9E84, 0xC5EB, 0x9E85, 0xC5EF, 0x9E86, 0xC5F1,\n\t0x9E87, 0xC5F2, 0x9E88, 0xC5F3, 0x9E89, 0xC5F5, 0x9E8A, 0xC5F8,\n\t0x9E8B, 0xC5F9, 0x9E8C, 0xC5FA, 0x9E8D, 0xC5FB, 0x9E8E, 0xC602,\n\t0x9E8F, 0xC603, 0x9E90, 0xC604, 0x9E91, 0xC609, 0x9E92, 0xC60A,\n\t0x9E93, 0xC60B, 0x9E94, 0xC60D, 0x9E95, 0xC60E, 0x9E96, 0xC60F,\n\t0x9E97, 0xC611, 0x9E98, 0xC612, 0x9E99, 0xC613, 0x9E9A, 0xC614,\n\t0x9E9B, 0xC615, 0x9E9C, 0xC616, 0x9E9D, 0xC617, 0x9E9E, 0xC61A,\n\t0x9E9F, 0xC61D, 0x9EA0, 0xC61E, 0x9EA1, 0xC61F, 0x9EA2, 0xC620,\n\t0x9EA3, 0xC621, 0x9EA4, 0xC622, 0x9EA5, 0xC623, 0x9EA6, 0xC626,\n\t0x9EA7, 0xC627, 0x9EA8, 0xC629, 0x9EA9, 0xC62A, 0x9EAA, 0xC62B,\n\t0x9EAB, 0xC62F, 0x9EAC, 0xC631, 0x9EAD, 0xC632, 0x9EAE, 0xC636,\n\t0x9EAF, 0xC638, 0x9EB0, 0xC63A, 0x9EB1, 0xC63C, 0x9EB2, 0xC63D,\n\t0x9EB3, 0xC63E, 0x9EB4, 0xC63F, 0x9EB5, 0xC642, 0x9EB6, 0xC643,\n\t0x9EB7, 0xC645, 0x9EB8, 0xC646, 0x9EB9, 0xC647, 0x9EBA, 0xC649,\n\t0x9EBB, 0xC64A, 0x9EBC, 0xC64B, 0x9EBD, 0xC64C, 0x9EBE, 0xC64D,\n\t0x9EBF, 0xC64E, 0x9EC0, 0xC64F, 0x9EC1, 0xC652, 0x9EC2, 0xC656,\n\t0x9EC3, 0xC657, 0x9EC4, 0xC658, 0x9EC5, 0xC659, 0x9EC6, 0xC65A,\n\t0x9EC7, 0xC65B, 0x9EC8, 0xC65E, 0x9EC9, 0xC65F, 0x9ECA, 0xC661,\n\t0x9ECB, 0xC662, 0x9ECC, 0xC663, 0x9ECD, 0xC664, 0x9ECE, 0xC665,\n\t0x9ECF, 0xC666, 0x9ED0, 0xC667, 0x9ED1, 0xC668, 0x9ED2, 0xC669,\n\t0x9ED3, 0xC66A, 0x9ED4, 0xC66B, 0x9ED5, 0xC66D, 0x9ED6, 0xC66E,\n\t0x9ED7, 0xC670, 0x9ED8, 0xC672, 0x9ED9, 0xC673, 0x9EDA, 0xC674,\n\t0x9EDB, 0xC675, 0x9EDC, 0xC676, 0x9EDD, 0xC677, 0x9EDE, 0xC67A,\n\t0x9EDF, 0xC67B, 0x9EE0, 0xC67D, 0x9EE1, 0xC67E, 0x9EE2, 0xC67F,\n\t0x9EE3, 0xC681, 0x9EE4, 0xC682, 0x9EE5, 0xC683, 0x9EE6, 0xC684,\n\t0x9EE7, 0xC685, 0x9EE8, 0xC686, 0x9EE9, 0xC687, 0x9EEA, 0xC68A,\n\t0x9EEB, 0xC68C, 0x9EEC, 0xC68E, 0x9EED, 0xC68F, 0x9EEE, 0xC690,\n\t0x9EEF, 0xC691, 0x9EF0, 0xC692, 0x9EF1, 0xC693, 0x9EF2, 0xC696,\n\t0x9EF3, 0xC697, 0x9EF4, 0xC699, 0x9EF5, 0xC69A, 0x9EF6, 0xC69B,\n\t0x9EF7, 0xC69D, 0x9EF8, 0xC69E, 0x9EF9, 0xC69F, 0x9EFA, 0xC6A0,\n\t0x9EFB, 0xC6A1, 0x9EFC, 0xC6A2, 0x9EFD, 0xC6A3, 0x9EFE, 0xC6A6,\n\t0x9F41, 0xC6A8, 0x9F42, 0xC6AA, 0x9F43, 0xC6AB, 0x9F44, 0xC6AC,\n\t0x9F45, 0xC6AD, 0x9F46, 0xC6AE, 0x9F47, 0xC6AF, 0x9F48, 0xC6B2,\n\t0x9F49, 0xC6B3, 0x9F4A, 0xC6B5, 0x9F4B, 0xC6B6, 0x9F4C, 0xC6B7,\n\t0x9F4D, 0xC6BB, 0x9F4E, 0xC6BC, 0x9F4F, 0xC6BD, 0x9F50, 0xC6BE,\n\t0x9F51, 0xC6BF, 0x9F52, 0xC6C2, 0x9F53, 0xC6C4, 0x9F54, 0xC6C6,\n\t0x9F55, 0xC6C7, 0x9F56, 0xC6C8, 0x9F57, 0xC6C9, 0x9F58, 0xC6CA,\n\t0x9F59, 0xC6CB, 0x9F5A, 0xC6CE, 0x9F61, 0xC6CF, 0x9F62, 0xC6D1,\n\t0x9F63, 0xC6D2, 0x9F64, 0xC6D3, 0x9F65, 0xC6D5, 0x9F66, 0xC6D6,\n\t0x9F67, 0xC6D7, 0x9F68, 0xC6D8, 0x9F69, 0xC6D9, 0x9F6A, 0xC6DA,\n\t0x9F6B, 0xC6DB, 0x9F6C, 0xC6DE, 0x9F6D, 0xC6DF, 0x9F6E, 0xC6E2,\n\t0x9F6F, 0xC6E3, 0x9F70, 0xC6E4, 0x9F71, 0xC6E5, 0x9F72, 0xC6E6,\n\t0x9F73, 0xC6E7, 0x9F74, 0xC6EA, 0x9F75, 0xC6EB, 0x9F76, 0xC6ED,\n\t0x9F77, 0xC6EE, 0x9F78, 0xC6EF, 0x9F79, 0xC6F1, 0x9F7A, 0xC6F2,\n\t0x9F81, 0xC6F3, 0x9F82, 0xC6F4, 0x9F83, 0xC6F5, 0x9F84, 0xC6F6,\n\t0x9F85, 0xC6F7, 0x9F86, 0xC6FA, 0x9F87, 0xC6FB, 0x9F88, 0xC6FC,\n\t0x9F89, 0xC6FE, 0x9F8A, 0xC6FF, 0x9F8B, 0xC700, 0x9F8C, 0xC701,\n\t0x9F8D, 0xC702, 0x9F8E, 0xC703, 0x9F8F, 0xC706, 0x9F90, 0xC707,\n\t0x9F91, 0xC709, 0x9F92, 0xC70A, 0x9F93, 0xC70B, 0x9F94, 0xC70D,\n\t0x9F95, 0xC70E, 0x9F96, 0xC70F, 0x9F97, 0xC710, 0x9F98, 0xC711,\n\t0x9F99, 0xC712, 0x9F9A, 0xC713, 0x9F9B, 0xC716, 0x9F9C, 0xC718,\n\t0x9F9D, 0xC71A, 0x9F9E, 0xC71B, 0x9F9F, 0xC71C, 0x9FA0, 0xC71D,\n\t0x9FA1, 0xC71E, 0x9FA2, 0xC71F, 0x9FA3, 0xC722, 0x9FA4, 0xC723,\n\t0x9FA5, 0xC725, 0x9FA6, 0xC726, 0x9FA7, 0xC727, 0x9FA8, 0xC729,\n\t0x9FA9, 0xC72A, 0x9FAA, 0xC72B, 0x9FAB, 0xC72C, 0x9FAC, 0xC72D,\n\t0x9FAD, 0xC72E, 0x9FAE, 0xC72F, 0x9FAF, 0xC732, 0x9FB0, 0xC734,\n\t0x9FB1, 0xC736, 0x9FB2, 0xC738, 0x9FB3, 0xC739, 0x9FB4, 0xC73A,\n\t0x9FB5, 0xC73B, 0x9FB6, 0xC73E, 0x9FB7, 0xC73F, 0x9FB8, 0xC741,\n\t0x9FB9, 0xC742, 0x9FBA, 0xC743, 0x9FBB, 0xC745, 0x9FBC, 0xC746,\n\t0x9FBD, 0xC747, 0x9FBE, 0xC748, 0x9FBF, 0xC749, 0x9FC0, 0xC74B,\n\t0x9FC1, 0xC74E, 0x9FC2, 0xC750, 0x9FC3, 0xC759, 0x9FC4, 0xC75A,\n\t0x9FC5, 0xC75B, 0x9FC6, 0xC75D, 0x9FC7, 0xC75E, 0x9FC8, 0xC75F,\n\t0x9FC9, 0xC761, 0x9FCA, 0xC762, 0x9FCB, 0xC763, 0x9FCC, 0xC764,\n\t0x9FCD, 0xC765, 0x9FCE, 0xC766, 0x9FCF, 0xC767, 0x9FD0, 0xC769,\n\t0x9FD1, 0xC76A, 0x9FD2, 0xC76C, 0x9FD3, 0xC76D, 0x9FD4, 0xC76E,\n\t0x9FD5, 0xC76F, 0x9FD6, 0xC770, 0x9FD7, 0xC771, 0x9FD8, 0xC772,\n\t0x9FD9, 0xC773, 0x9FDA, 0xC776, 0x9FDB, 0xC777, 0x9FDC, 0xC779,\n\t0x9FDD, 0xC77A, 0x9FDE, 0xC77B, 0x9FDF, 0xC77F, 0x9FE0, 0xC780,\n\t0x9FE1, 0xC781, 0x9FE2, 0xC782, 0x9FE3, 0xC786, 0x9FE4, 0xC78B,\n\t0x9FE5, 0xC78C, 0x9FE6, 0xC78D, 0x9FE7, 0xC78F, 0x9FE8, 0xC792,\n\t0x9FE9, 0xC793, 0x9FEA, 0xC795, 0x9FEB, 0xC799, 0x9FEC, 0xC79B,\n\t0x9FED, 0xC79C, 0x9FEE, 0xC79D, 0x9FEF, 0xC79E, 0x9FF0, 0xC79F,\n\t0x9FF1, 0xC7A2, 0x9FF2, 0xC7A7, 0x9FF3, 0xC7A8, 0x9FF4, 0xC7A9,\n\t0x9FF5, 0xC7AA, 0x9FF6, 0xC7AB, 0x9FF7, 0xC7AE, 0x9FF8, 0xC7AF,\n\t0x9FF9, 0xC7B1, 0x9FFA, 0xC7B2, 0x9FFB, 0xC7B3, 0x9FFC, 0xC7B5,\n\t0x9FFD, 0xC7B6, 0x9FFE, 0xC7B7, 0xA041, 0xC7B8, 0xA042, 0xC7B9,\n\t0xA043, 0xC7BA, 0xA044, 0xC7BB, 0xA045, 0xC7BE, 0xA046, 0xC7C2,\n\t0xA047, 0xC7C3, 0xA048, 0xC7C4, 0xA049, 0xC7C5, 0xA04A, 0xC7C6,\n\t0xA04B, 0xC7C7, 0xA04C, 0xC7CA, 0xA04D, 0xC7CB, 0xA04E, 0xC7CD,\n\t0xA04F, 0xC7CF, 0xA050, 0xC7D1, 0xA051, 0xC7D2, 0xA052, 0xC7D3,\n\t0xA053, 0xC7D4, 0xA054, 0xC7D5, 0xA055, 0xC7D6, 0xA056, 0xC7D7,\n\t0xA057, 0xC7D9, 0xA058, 0xC7DA, 0xA059, 0xC7DB, 0xA05A, 0xC7DC,\n\t0xA061, 0xC7DE, 0xA062, 0xC7DF, 0xA063, 0xC7E0, 0xA064, 0xC7E1,\n\t0xA065, 0xC7E2, 0xA066, 0xC7E3, 0xA067, 0xC7E5, 0xA068, 0xC7E6,\n\t0xA069, 0xC7E7, 0xA06A, 0xC7E9, 0xA06B, 0xC7EA, 0xA06C, 0xC7EB,\n\t0xA06D, 0xC7ED, 0xA06E, 0xC7EE, 0xA06F, 0xC7EF, 0xA070, 0xC7F0,\n\t0xA071, 0xC7F1, 0xA072, 0xC7F2, 0xA073, 0xC7F3, 0xA074, 0xC7F4,\n\t0xA075, 0xC7F5, 0xA076, 0xC7F6, 0xA077, 0xC7F7, 0xA078, 0xC7F8,\n\t0xA079, 0xC7F9, 0xA07A, 0xC7FA, 0xA081, 0xC7FB, 0xA082, 0xC7FC,\n\t0xA083, 0xC7FD, 0xA084, 0xC7FE, 0xA085, 0xC7FF, 0xA086, 0xC802,\n\t0xA087, 0xC803, 0xA088, 0xC805, 0xA089, 0xC806, 0xA08A, 0xC807,\n\t0xA08B, 0xC809, 0xA08C, 0xC80B, 0xA08D, 0xC80C, 0xA08E, 0xC80D,\n\t0xA08F, 0xC80E, 0xA090, 0xC80F, 0xA091, 0xC812, 0xA092, 0xC814,\n\t0xA093, 0xC817, 0xA094, 0xC818, 0xA095, 0xC819, 0xA096, 0xC81A,\n\t0xA097, 0xC81B, 0xA098, 0xC81E, 0xA099, 0xC81F, 0xA09A, 0xC821,\n\t0xA09B, 0xC822, 0xA09C, 0xC823, 0xA09D, 0xC825, 0xA09E, 0xC826,\n\t0xA09F, 0xC827, 0xA0A0, 0xC828, 0xA0A1, 0xC829, 0xA0A2, 0xC82A,\n\t0xA0A3, 0xC82B, 0xA0A4, 0xC82E, 0xA0A5, 0xC830, 0xA0A6, 0xC832,\n\t0xA0A7, 0xC833, 0xA0A8, 0xC834, 0xA0A9, 0xC835, 0xA0AA, 0xC836,\n\t0xA0AB, 0xC837, 0xA0AC, 0xC839, 0xA0AD, 0xC83A, 0xA0AE, 0xC83B,\n\t0xA0AF, 0xC83D, 0xA0B0, 0xC83E, 0xA0B1, 0xC83F, 0xA0B2, 0xC841,\n\t0xA0B3, 0xC842, 0xA0B4, 0xC843, 0xA0B5, 0xC844, 0xA0B6, 0xC845,\n\t0xA0B7, 0xC846, 0xA0B8, 0xC847, 0xA0B9, 0xC84A, 0xA0BA, 0xC84B,\n\t0xA0BB, 0xC84E, 0xA0BC, 0xC84F, 0xA0BD, 0xC850, 0xA0BE, 0xC851,\n\t0xA0BF, 0xC852, 0xA0C0, 0xC853, 0xA0C1, 0xC855, 0xA0C2, 0xC856,\n\t0xA0C3, 0xC857, 0xA0C4, 0xC858, 0xA0C5, 0xC859, 0xA0C6, 0xC85A,\n\t0xA0C7, 0xC85B, 0xA0C8, 0xC85C, 0xA0C9, 0xC85D, 0xA0CA, 0xC85E,\n\t0xA0CB, 0xC85F, 0xA0CC, 0xC860, 0xA0CD, 0xC861, 0xA0CE, 0xC862,\n\t0xA0CF, 0xC863, 0xA0D0, 0xC864, 0xA0D1, 0xC865, 0xA0D2, 0xC866,\n\t0xA0D3, 0xC867, 0xA0D4, 0xC868, 0xA0D5, 0xC869, 0xA0D6, 0xC86A,\n\t0xA0D7, 0xC86B, 0xA0D8, 0xC86C, 0xA0D9, 0xC86D, 0xA0DA, 0xC86E,\n\t0xA0DB, 0xC86F, 0xA0DC, 0xC872, 0xA0DD, 0xC873, 0xA0DE, 0xC875,\n\t0xA0DF, 0xC876, 0xA0E0, 0xC877, 0xA0E1, 0xC879, 0xA0E2, 0xC87B,\n\t0xA0E3, 0xC87C, 0xA0E4, 0xC87D, 0xA0E5, 0xC87E, 0xA0E6, 0xC87F,\n\t0xA0E7, 0xC882, 0xA0E8, 0xC884, 0xA0E9, 0xC888, 0xA0EA, 0xC889,\n\t0xA0EB, 0xC88A, 0xA0EC, 0xC88E, 0xA0ED, 0xC88F, 0xA0EE, 0xC890,\n\t0xA0EF, 0xC891, 0xA0F0, 0xC892, 0xA0F1, 0xC893, 0xA0F2, 0xC895,\n\t0xA0F3, 0xC896, 0xA0F4, 0xC897, 0xA0F5, 0xC898, 0xA0F6, 0xC899,\n\t0xA0F7, 0xC89A, 0xA0F8, 0xC89B, 0xA0F9, 0xC89C, 0xA0FA, 0xC89E,\n\t0xA0FB, 0xC8A0, 0xA0FC, 0xC8A2, 0xA0FD, 0xC8A3, 0xA0FE, 0xC8A4,\n\t0xA141, 0xC8A5, 0xA142, 0xC8A6, 0xA143, 0xC8A7, 0xA144, 0xC8A9,\n\t0xA145, 0xC8AA, 0xA146, 0xC8AB, 0xA147, 0xC8AC, 0xA148, 0xC8AD,\n\t0xA149, 0xC8AE, 0xA14A, 0xC8AF, 0xA14B, 0xC8B0, 0xA14C, 0xC8B1,\n\t0xA14D, 0xC8B2, 0xA14E, 0xC8B3, 0xA14F, 0xC8B4, 0xA150, 0xC8B5,\n\t0xA151, 0xC8B6, 0xA152, 0xC8B7, 0xA153, 0xC8B8, 0xA154, 0xC8B9,\n\t0xA155, 0xC8BA, 0xA156, 0xC8BB, 0xA157, 0xC8BE, 0xA158, 0xC8BF,\n\t0xA159, 0xC8C0, 0xA15A, 0xC8C1, 0xA161, 0xC8C2, 0xA162, 0xC8C3,\n\t0xA163, 0xC8C5, 0xA164, 0xC8C6, 0xA165, 0xC8C7, 0xA166, 0xC8C9,\n\t0xA167, 0xC8CA, 0xA168, 0xC8CB, 0xA169, 0xC8CD, 0xA16A, 0xC8CE,\n\t0xA16B, 0xC8CF, 0xA16C, 0xC8D0, 0xA16D, 0xC8D1, 0xA16E, 0xC8D2,\n\t0xA16F, 0xC8D3, 0xA170, 0xC8D6, 0xA171, 0xC8D8, 0xA172, 0xC8DA,\n\t0xA173, 0xC8DB, 0xA174, 0xC8DC, 0xA175, 0xC8DD, 0xA176, 0xC8DE,\n\t0xA177, 0xC8DF, 0xA178, 0xC8E2, 0xA179, 0xC8E3, 0xA17A, 0xC8E5,\n\t0xA181, 0xC8E6, 0xA182, 0xC8E7, 0xA183, 0xC8E8, 0xA184, 0xC8E9,\n\t0xA185, 0xC8EA, 0xA186, 0xC8EB, 0xA187, 0xC8EC, 0xA188, 0xC8ED,\n\t0xA189, 0xC8EE, 0xA18A, 0xC8EF, 0xA18B, 0xC8F0, 0xA18C, 0xC8F1,\n\t0xA18D, 0xC8F2, 0xA18E, 0xC8F3, 0xA18F, 0xC8F4, 0xA190, 0xC8F6,\n\t0xA191, 0xC8F7, 0xA192, 0xC8F8, 0xA193, 0xC8F9, 0xA194, 0xC8FA,\n\t0xA195, 0xC8FB, 0xA196, 0xC8FE, 0xA197, 0xC8FF, 0xA198, 0xC901,\n\t0xA199, 0xC902, 0xA19A, 0xC903, 0xA19B, 0xC907, 0xA19C, 0xC908,\n\t0xA19D, 0xC909, 0xA19E, 0xC90A, 0xA19F, 0xC90B, 0xA1A0, 0xC90E,\n\t0xA1A1, 0x3000, 0xA1A2, 0x3001, 0xA1A3, 0x3002, 0xA1A4, 0x00B7,\n\t0xA1A5, 0x2025, 0xA1A6, 0x2026, 0xA1A7, 0x00A8, 0xA1A8, 0x3003,\n\t0xA1A9, 0x00AD, 0xA1AA, 0x2015, 0xA1AB, 0x2225, 0xA1AC, 0xFF3C,\n\t0xA1AD, 0x223C, 0xA1AE, 0x2018, 0xA1AF, 0x2019, 0xA1B0, 0x201C,\n\t0xA1B1, 0x201D, 0xA1B2, 0x3014, 0xA1B3, 0x3015, 0xA1B4, 0x3008,\n\t0xA1B5, 0x3009, 0xA1B6, 0x300A, 0xA1B7, 0x300B, 0xA1B8, 0x300C,\n\t0xA1B9, 0x300D, 0xA1BA, 0x300E, 0xA1BB, 0x300F, 0xA1BC, 0x3010,\n\t0xA1BD, 0x3011, 0xA1BE, 0x00B1, 0xA1BF, 0x00D7, 0xA1C0, 0x00F7,\n\t0xA1C1, 0x2260, 0xA1C2, 0x2264, 0xA1C3, 0x2265, 0xA1C4, 0x221E,\n\t0xA1C5, 0x2234, 0xA1C6, 0x00B0, 0xA1C7, 0x2032, 0xA1C8, 0x2033,\n\t0xA1C9, 0x2103, 0xA1CA, 0x212B, 0xA1CB, 0xFFE0, 0xA1CC, 0xFFE1,\n\t0xA1CD, 0xFFE5, 0xA1CE, 0x2642, 0xA1CF, 0x2640, 0xA1D0, 0x2220,\n\t0xA1D1, 0x22A5, 0xA1D2, 0x2312, 0xA1D3, 0x2202, 0xA1D4, 0x2207,\n\t0xA1D5, 0x2261, 0xA1D6, 0x2252, 0xA1D7, 0x00A7, 0xA1D8, 0x203B,\n\t0xA1D9, 0x2606, 0xA1DA, 0x2605, 0xA1DB, 0x25CB, 0xA1DC, 0x25CF,\n\t0xA1DD, 0x25CE, 0xA1DE, 0x25C7, 0xA1DF, 0x25C6, 0xA1E0, 0x25A1,\n\t0xA1E1, 0x25A0, 0xA1E2, 0x25B3, 0xA1E3, 0x25B2, 0xA1E4, 0x25BD,\n\t0xA1E5, 0x25BC, 0xA1E6, 0x2192, 0xA1E7, 0x2190, 0xA1E8, 0x2191,\n\t0xA1E9, 0x2193, 0xA1EA, 0x2194, 0xA1EB, 0x3013, 0xA1EC, 0x226A,\n\t0xA1ED, 0x226B, 0xA1EE, 0x221A, 0xA1EF, 0x223D, 0xA1F0, 0x221D,\n\t0xA1F1, 0x2235, 0xA1F2, 0x222B, 0xA1F3, 0x222C, 0xA1F4, 0x2208,\n\t0xA1F5, 0x220B, 0xA1F6, 0x2286, 0xA1F7, 0x2287, 0xA1F8, 0x2282,\n\t0xA1F9, 0x2283, 0xA1FA, 0x222A, 0xA1FB, 0x2229, 0xA1FC, 0x2227,\n\t0xA1FD, 0x2228, 0xA1FE, 0xFFE2, 0xA241, 0xC910, 0xA242, 0xC912,\n\t0xA243, 0xC913, 0xA244, 0xC914, 0xA245, 0xC915, 0xA246, 0xC916,\n\t0xA247, 0xC917, 0xA248, 0xC919, 0xA249, 0xC91A, 0xA24A, 0xC91B,\n\t0xA24B, 0xC91C, 0xA24C, 0xC91D, 0xA24D, 0xC91E, 0xA24E, 0xC91F,\n\t0xA24F, 0xC920, 0xA250, 0xC921, 0xA251, 0xC922, 0xA252, 0xC923,\n\t0xA253, 0xC924, 0xA254, 0xC925, 0xA255, 0xC926, 0xA256, 0xC927,\n\t0xA257, 0xC928, 0xA258, 0xC929, 0xA259, 0xC92A, 0xA25A, 0xC92B,\n\t0xA261, 0xC92D, 0xA262, 0xC92E, 0xA263, 0xC92F, 0xA264, 0xC930,\n\t0xA265, 0xC931, 0xA266, 0xC932, 0xA267, 0xC933, 0xA268, 0xC935,\n\t0xA269, 0xC936, 0xA26A, 0xC937, 0xA26B, 0xC938, 0xA26C, 0xC939,\n\t0xA26D, 0xC93A, 0xA26E, 0xC93B, 0xA26F, 0xC93C, 0xA270, 0xC93D,\n\t0xA271, 0xC93E, 0xA272, 0xC93F, 0xA273, 0xC940, 0xA274, 0xC941,\n\t0xA275, 0xC942, 0xA276, 0xC943, 0xA277, 0xC944, 0xA278, 0xC945,\n\t0xA279, 0xC946, 0xA27A, 0xC947, 0xA281, 0xC948, 0xA282, 0xC949,\n\t0xA283, 0xC94A, 0xA284, 0xC94B, 0xA285, 0xC94C, 0xA286, 0xC94D,\n\t0xA287, 0xC94E, 0xA288, 0xC94F, 0xA289, 0xC952, 0xA28A, 0xC953,\n\t0xA28B, 0xC955, 0xA28C, 0xC956, 0xA28D, 0xC957, 0xA28E, 0xC959,\n\t0xA28F, 0xC95A, 0xA290, 0xC95B, 0xA291, 0xC95C, 0xA292, 0xC95D,\n\t0xA293, 0xC95E, 0xA294, 0xC95F, 0xA295, 0xC962, 0xA296, 0xC964,\n\t0xA297, 0xC965, 0xA298, 0xC966, 0xA299, 0xC967, 0xA29A, 0xC968,\n\t0xA29B, 0xC969, 0xA29C, 0xC96A, 0xA29D, 0xC96B, 0xA29E, 0xC96D,\n\t0xA29F, 0xC96E, 0xA2A0, 0xC96F, 0xA2A1, 0x21D2, 0xA2A2, 0x21D4,\n\t0xA2A3, 0x2200, 0xA2A4, 0x2203, 0xA2A5, 0x00B4, 0xA2A6, 0xFF5E,\n\t0xA2A7, 0x02C7, 0xA2A8, 0x02D8, 0xA2A9, 0x02DD, 0xA2AA, 0x02DA,\n\t0xA2AB, 0x02D9, 0xA2AC, 0x00B8, 0xA2AD, 0x02DB, 0xA2AE, 0x00A1,\n\t0xA2AF, 0x00BF, 0xA2B0, 0x02D0, 0xA2B1, 0x222E, 0xA2B2, 0x2211,\n\t0xA2B3, 0x220F, 0xA2B4, 0x00A4, 0xA2B5, 0x2109, 0xA2B6, 0x2030,\n\t0xA2B7, 0x25C1, 0xA2B8, 0x25C0, 0xA2B9, 0x25B7, 0xA2BA, 0x25B6,\n\t0xA2BB, 0x2664, 0xA2BC, 0x2660, 0xA2BD, 0x2661, 0xA2BE, 0x2665,\n\t0xA2BF, 0x2667, 0xA2C0, 0x2663, 0xA2C1, 0x2299, 0xA2C2, 0x25C8,\n\t0xA2C3, 0x25A3, 0xA2C4, 0x25D0, 0xA2C5, 0x25D1, 0xA2C6, 0x2592,\n\t0xA2C7, 0x25A4, 0xA2C8, 0x25A5, 0xA2C9, 0x25A8, 0xA2CA, 0x25A7,\n\t0xA2CB, 0x25A6, 0xA2CC, 0x25A9, 0xA2CD, 0x2668, 0xA2CE, 0x260F,\n\t0xA2CF, 0x260E, 0xA2D0, 0x261C, 0xA2D1, 0x261E, 0xA2D2, 0x00B6,\n\t0xA2D3, 0x2020, 0xA2D4, 0x2021, 0xA2D5, 0x2195, 0xA2D6, 0x2197,\n\t0xA2D7, 0x2199, 0xA2D8, 0x2196, 0xA2D9, 0x2198, 0xA2DA, 0x266D,\n\t0xA2DB, 0x2669, 0xA2DC, 0x266A, 0xA2DD, 0x266C, 0xA2DE, 0x327F,\n\t0xA2DF, 0x321C, 0xA2E0, 0x2116, 0xA2E1, 0x33C7, 0xA2E2, 0x2122,\n\t0xA2E3, 0x33C2, 0xA2E4, 0x33D8, 0xA2E5, 0x2121, 0xA2E6, 0x20AC,\n\t0xA2E7, 0x00AE, 0xA341, 0xC971, 0xA342, 0xC972, 0xA343, 0xC973,\n\t0xA344, 0xC975, 0xA345, 0xC976, 0xA346, 0xC977, 0xA347, 0xC978,\n\t0xA348, 0xC979, 0xA349, 0xC97A, 0xA34A, 0xC97B, 0xA34B, 0xC97D,\n\t0xA34C, 0xC97E, 0xA34D, 0xC97F, 0xA34E, 0xC980, 0xA34F, 0xC981,\n\t0xA350, 0xC982, 0xA351, 0xC983, 0xA352, 0xC984, 0xA353, 0xC985,\n\t0xA354, 0xC986, 0xA355, 0xC987, 0xA356, 0xC98A, 0xA357, 0xC98B,\n\t0xA358, 0xC98D, 0xA359, 0xC98E, 0xA35A, 0xC98F, 0xA361, 0xC991,\n\t0xA362, 0xC992, 0xA363, 0xC993, 0xA364, 0xC994, 0xA365, 0xC995,\n\t0xA366, 0xC996, 0xA367, 0xC997, 0xA368, 0xC99A, 0xA369, 0xC99C,\n\t0xA36A, 0xC99E, 0xA36B, 0xC99F, 0xA36C, 0xC9A0, 0xA36D, 0xC9A1,\n\t0xA36E, 0xC9A2, 0xA36F, 0xC9A3, 0xA370, 0xC9A4, 0xA371, 0xC9A5,\n\t0xA372, 0xC9A6, 0xA373, 0xC9A7, 0xA374, 0xC9A8, 0xA375, 0xC9A9,\n\t0xA376, 0xC9AA, 0xA377, 0xC9AB, 0xA378, 0xC9AC, 0xA379, 0xC9AD,\n\t0xA37A, 0xC9AE, 0xA381, 0xC9AF, 0xA382, 0xC9B0, 0xA383, 0xC9B1,\n\t0xA384, 0xC9B2, 0xA385, 0xC9B3, 0xA386, 0xC9B4, 0xA387, 0xC9B5,\n\t0xA388, 0xC9B6, 0xA389, 0xC9B7, 0xA38A, 0xC9B8, 0xA38B, 0xC9B9,\n\t0xA38C, 0xC9BA, 0xA38D, 0xC9BB, 0xA38E, 0xC9BC, 0xA38F, 0xC9BD,\n\t0xA390, 0xC9BE, 0xA391, 0xC9BF, 0xA392, 0xC9C2, 0xA393, 0xC9C3,\n\t0xA394, 0xC9C5, 0xA395, 0xC9C6, 0xA396, 0xC9C9, 0xA397, 0xC9CB,\n\t0xA398, 0xC9CC, 0xA399, 0xC9CD, 0xA39A, 0xC9CE, 0xA39B, 0xC9CF,\n\t0xA39C, 0xC9D2, 0xA39D, 0xC9D4, 0xA39E, 0xC9D7, 0xA39F, 0xC9D8,\n\t0xA3A0, 0xC9DB, 0xA3A1, 0xFF01, 0xA3A2, 0xFF02, 0xA3A3, 0xFF03,\n\t0xA3A4, 0xFF04, 0xA3A5, 0xFF05, 0xA3A6, 0xFF06, 0xA3A7, 0xFF07,\n\t0xA3A8, 0xFF08, 0xA3A9, 0xFF09, 0xA3AA, 0xFF0A, 0xA3AB, 0xFF0B,\n\t0xA3AC, 0xFF0C, 0xA3AD, 0xFF0D, 0xA3AE, 0xFF0E, 0xA3AF, 0xFF0F,\n\t0xA3B0, 0xFF10, 0xA3B1, 0xFF11, 0xA3B2, 0xFF12, 0xA3B3, 0xFF13,\n\t0xA3B4, 0xFF14, 0xA3B5, 0xFF15, 0xA3B6, 0xFF16, 0xA3B7, 0xFF17,\n\t0xA3B8, 0xFF18, 0xA3B9, 0xFF19, 0xA3BA, 0xFF1A, 0xA3BB, 0xFF1B,\n\t0xA3BC, 0xFF1C, 0xA3BD, 0xFF1D, 0xA3BE, 0xFF1E, 0xA3BF, 0xFF1F,\n\t0xA3C0, 0xFF20, 0xA3C1, 0xFF21, 0xA3C2, 0xFF22, 0xA3C3, 0xFF23,\n\t0xA3C4, 0xFF24, 0xA3C5, 0xFF25, 0xA3C6, 0xFF26, 0xA3C7, 0xFF27,\n\t0xA3C8, 0xFF28, 0xA3C9, 0xFF29, 0xA3CA, 0xFF2A, 0xA3CB, 0xFF2B,\n\t0xA3CC, 0xFF2C, 0xA3CD, 0xFF2D, 0xA3CE, 0xFF2E, 0xA3CF, 0xFF2F,\n\t0xA3D0, 0xFF30, 0xA3D1, 0xFF31, 0xA3D2, 0xFF32, 0xA3D3, 0xFF33,\n\t0xA3D4, 0xFF34, 0xA3D5, 0xFF35, 0xA3D6, 0xFF36, 0xA3D7, 0xFF37,\n\t0xA3D8, 0xFF38, 0xA3D9, 0xFF39, 0xA3DA, 0xFF3A, 0xA3DB, 0xFF3B,\n\t0xA3DC, 0xFFE6, 0xA3DD, 0xFF3D, 0xA3DE, 0xFF3E, 0xA3DF, 0xFF3F,\n\t0xA3E0, 0xFF40, 0xA3E1, 0xFF41, 0xA3E2, 0xFF42, 0xA3E3, 0xFF43,\n\t0xA3E4, 0xFF44, 0xA3E5, 0xFF45, 0xA3E6, 0xFF46, 0xA3E7, 0xFF47,\n\t0xA3E8, 0xFF48, 0xA3E9, 0xFF49, 0xA3EA, 0xFF4A, 0xA3EB, 0xFF4B,\n\t0xA3EC, 0xFF4C, 0xA3ED, 0xFF4D, 0xA3EE, 0xFF4E, 0xA3EF, 0xFF4F,\n\t0xA3F0, 0xFF50, 0xA3F1, 0xFF51, 0xA3F2, 0xFF52, 0xA3F3, 0xFF53,\n\t0xA3F4, 0xFF54, 0xA3F5, 0xFF55, 0xA3F6, 0xFF56, 0xA3F7, 0xFF57,\n\t0xA3F8, 0xFF58, 0xA3F9, 0xFF59, 0xA3FA, 0xFF5A, 0xA3FB, 0xFF5B,\n\t0xA3FC, 0xFF5C, 0xA3FD, 0xFF5D, 0xA3FE, 0xFFE3, 0xA441, 0xC9DE,\n\t0xA442, 0xC9DF, 0xA443, 0xC9E1, 0xA444, 0xC9E3, 0xA445, 0xC9E5,\n\t0xA446, 0xC9E6, 0xA447, 0xC9E8, 0xA448, 0xC9E9, 0xA449, 0xC9EA,\n\t0xA44A, 0xC9EB, 0xA44B, 0xC9EE, 0xA44C, 0xC9F2, 0xA44D, 0xC9F3,\n\t0xA44E, 0xC9F4, 0xA44F, 0xC9F5, 0xA450, 0xC9F6, 0xA451, 0xC9F7,\n\t0xA452, 0xC9FA, 0xA453, 0xC9FB, 0xA454, 0xC9FD, 0xA455, 0xC9FE,\n\t0xA456, 0xC9FF, 0xA457, 0xCA01, 0xA458, 0xCA02, 0xA459, 0xCA03,\n\t0xA45A, 0xCA04, 0xA461, 0xCA05, 0xA462, 0xCA06, 0xA463, 0xCA07,\n\t0xA464, 0xCA0A, 0xA465, 0xCA0E, 0xA466, 0xCA0F, 0xA467, 0xCA10,\n\t0xA468, 0xCA11, 0xA469, 0xCA12, 0xA46A, 0xCA13, 0xA46B, 0xCA15,\n\t0xA46C, 0xCA16, 0xA46D, 0xCA17, 0xA46E, 0xCA19, 0xA46F, 0xCA1A,\n\t0xA470, 0xCA1B, 0xA471, 0xCA1C, 0xA472, 0xCA1D, 0xA473, 0xCA1E,\n\t0xA474, 0xCA1F, 0xA475, 0xCA20, 0xA476, 0xCA21, 0xA477, 0xCA22,\n\t0xA478, 0xCA23, 0xA479, 0xCA24, 0xA47A, 0xCA25, 0xA481, 0xCA26,\n\t0xA482, 0xCA27, 0xA483, 0xCA28, 0xA484, 0xCA2A, 0xA485, 0xCA2B,\n\t0xA486, 0xCA2C, 0xA487, 0xCA2D, 0xA488, 0xCA2E, 0xA489, 0xCA2F,\n\t0xA48A, 0xCA30, 0xA48B, 0xCA31, 0xA48C, 0xCA32, 0xA48D, 0xCA33,\n\t0xA48E, 0xCA34, 0xA48F, 0xCA35, 0xA490, 0xCA36, 0xA491, 0xCA37,\n\t0xA492, 0xCA38, 0xA493, 0xCA39, 0xA494, 0xCA3A, 0xA495, 0xCA3B,\n\t0xA496, 0xCA3C, 0xA497, 0xCA3D, 0xA498, 0xCA3E, 0xA499, 0xCA3F,\n\t0xA49A, 0xCA40, 0xA49B, 0xCA41, 0xA49C, 0xCA42, 0xA49D, 0xCA43,\n\t0xA49E, 0xCA44, 0xA49F, 0xCA45, 0xA4A0, 0xCA46, 0xA4A1, 0x3131,\n\t0xA4A2, 0x3132, 0xA4A3, 0x3133, 0xA4A4, 0x3134, 0xA4A5, 0x3135,\n\t0xA4A6, 0x3136, 0xA4A7, 0x3137, 0xA4A8, 0x3138, 0xA4A9, 0x3139,\n\t0xA4AA, 0x313A, 0xA4AB, 0x313B, 0xA4AC, 0x313C, 0xA4AD, 0x313D,\n\t0xA4AE, 0x313E, 0xA4AF, 0x313F, 0xA4B0, 0x3140, 0xA4B1, 0x3141,\n\t0xA4B2, 0x3142, 0xA4B3, 0x3143, 0xA4B4, 0x3144, 0xA4B5, 0x3145,\n\t0xA4B6, 0x3146, 0xA4B7, 0x3147, 0xA4B8, 0x3148, 0xA4B9, 0x3149,\n\t0xA4BA, 0x314A, 0xA4BB, 0x314B, 0xA4BC, 0x314C, 0xA4BD, 0x314D,\n\t0xA4BE, 0x314E, 0xA4BF, 0x314F, 0xA4C0, 0x3150, 0xA4C1, 0x3151,\n\t0xA4C2, 0x3152, 0xA4C3, 0x3153, 0xA4C4, 0x3154, 0xA4C5, 0x3155,\n\t0xA4C6, 0x3156, 0xA4C7, 0x3157, 0xA4C8, 0x3158, 0xA4C9, 0x3159,\n\t0xA4CA, 0x315A, 0xA4CB, 0x315B, 0xA4CC, 0x315C, 0xA4CD, 0x315D,\n\t0xA4CE, 0x315E, 0xA4CF, 0x315F, 0xA4D0, 0x3160, 0xA4D1, 0x3161,\n\t0xA4D2, 0x3162, 0xA4D3, 0x3163, 0xA4D4, 0x3164, 0xA4D5, 0x3165,\n\t0xA4D6, 0x3166, 0xA4D7, 0x3167, 0xA4D8, 0x3168, 0xA4D9, 0x3169,\n\t0xA4DA, 0x316A, 0xA4DB, 0x316B, 0xA4DC, 0x316C, 0xA4DD, 0x316D,\n\t0xA4DE, 0x316E, 0xA4DF, 0x316F, 0xA4E0, 0x3170, 0xA4E1, 0x3171,\n\t0xA4E2, 0x3172, 0xA4E3, 0x3173, 0xA4E4, 0x3174, 0xA4E5, 0x3175,\n\t0xA4E6, 0x3176, 0xA4E7, 0x3177, 0xA4E8, 0x3178, 0xA4E9, 0x3179,\n\t0xA4EA, 0x317A, 0xA4EB, 0x317B, 0xA4EC, 0x317C, 0xA4ED, 0x317D,\n\t0xA4EE, 0x317E, 0xA4EF, 0x317F, 0xA4F0, 0x3180, 0xA4F1, 0x3181,\n\t0xA4F2, 0x3182, 0xA4F3, 0x3183, 0xA4F4, 0x3184, 0xA4F5, 0x3185,\n\t0xA4F6, 0x3186, 0xA4F7, 0x3187, 0xA4F8, 0x3188, 0xA4F9, 0x3189,\n\t0xA4FA, 0x318A, 0xA4FB, 0x318B, 0xA4FC, 0x318C, 0xA4FD, 0x318D,\n\t0xA4FE, 0x318E, 0xA541, 0xCA47, 0xA542, 0xCA48, 0xA543, 0xCA49,\n\t0xA544, 0xCA4A, 0xA545, 0xCA4B, 0xA546, 0xCA4E, 0xA547, 0xCA4F,\n\t0xA548, 0xCA51, 0xA549, 0xCA52, 0xA54A, 0xCA53, 0xA54B, 0xCA55,\n\t0xA54C, 0xCA56, 0xA54D, 0xCA57, 0xA54E, 0xCA58, 0xA54F, 0xCA59,\n\t0xA550, 0xCA5A, 0xA551, 0xCA5B, 0xA552, 0xCA5E, 0xA553, 0xCA62,\n\t0xA554, 0xCA63, 0xA555, 0xCA64, 0xA556, 0xCA65, 0xA557, 0xCA66,\n\t0xA558, 0xCA67, 0xA559, 0xCA69, 0xA55A, 0xCA6A, 0xA561, 0xCA6B,\n\t0xA562, 0xCA6C, 0xA563, 0xCA6D, 0xA564, 0xCA6E, 0xA565, 0xCA6F,\n\t0xA566, 0xCA70, 0xA567, 0xCA71, 0xA568, 0xCA72, 0xA569, 0xCA73,\n\t0xA56A, 0xCA74, 0xA56B, 0xCA75, 0xA56C, 0xCA76, 0xA56D, 0xCA77,\n\t0xA56E, 0xCA78, 0xA56F, 0xCA79, 0xA570, 0xCA7A, 0xA571, 0xCA7B,\n\t0xA572, 0xCA7C, 0xA573, 0xCA7E, 0xA574, 0xCA7F, 0xA575, 0xCA80,\n\t0xA576, 0xCA81, 0xA577, 0xCA82, 0xA578, 0xCA83, 0xA579, 0xCA85,\n\t0xA57A, 0xCA86, 0xA581, 0xCA87, 0xA582, 0xCA88, 0xA583, 0xCA89,\n\t0xA584, 0xCA8A, 0xA585, 0xCA8B, 0xA586, 0xCA8C, 0xA587, 0xCA8D,\n\t0xA588, 0xCA8E, 0xA589, 0xCA8F, 0xA58A, 0xCA90, 0xA58B, 0xCA91,\n\t0xA58C, 0xCA92, 0xA58D, 0xCA93, 0xA58E, 0xCA94, 0xA58F, 0xCA95,\n\t0xA590, 0xCA96, 0xA591, 0xCA97, 0xA592, 0xCA99, 0xA593, 0xCA9A,\n\t0xA594, 0xCA9B, 0xA595, 0xCA9C, 0xA596, 0xCA9D, 0xA597, 0xCA9E,\n\t0xA598, 0xCA9F, 0xA599, 0xCAA0, 0xA59A, 0xCAA1, 0xA59B, 0xCAA2,\n\t0xA59C, 0xCAA3, 0xA59D, 0xCAA4, 0xA59E, 0xCAA5, 0xA59F, 0xCAA6,\n\t0xA5A0, 0xCAA7, 0xA5A1, 0x2170, 0xA5A2, 0x2171, 0xA5A3, 0x2172,\n\t0xA5A4, 0x2173, 0xA5A5, 0x2174, 0xA5A6, 0x2175, 0xA5A7, 0x2176,\n\t0xA5A8, 0x2177, 0xA5A9, 0x2178, 0xA5AA, 0x2179, 0xA5B0, 0x2160,\n\t0xA5B1, 0x2161, 0xA5B2, 0x2162, 0xA5B3, 0x2163, 0xA5B4, 0x2164,\n\t0xA5B5, 0x2165, 0xA5B6, 0x2166, 0xA5B7, 0x2167, 0xA5B8, 0x2168,\n\t0xA5B9, 0x2169, 0xA5C1, 0x0391, 0xA5C2, 0x0392, 0xA5C3, 0x0393,\n\t0xA5C4, 0x0394, 0xA5C5, 0x0395, 0xA5C6, 0x0396, 0xA5C7, 0x0397,\n\t0xA5C8, 0x0398, 0xA5C9, 0x0399, 0xA5CA, 0x039A, 0xA5CB, 0x039B,\n\t0xA5CC, 0x039C, 0xA5CD, 0x039D, 0xA5CE, 0x039E, 0xA5CF, 0x039F,\n\t0xA5D0, 0x03A0, 0xA5D1, 0x03A1, 0xA5D2, 0x03A3, 0xA5D3, 0x03A4,\n\t0xA5D4, 0x03A5, 0xA5D5, 0x03A6, 0xA5D6, 0x03A7, 0xA5D7, 0x03A8,\n\t0xA5D8, 0x03A9, 0xA5E1, 0x03B1, 0xA5E2, 0x03B2, 0xA5E3, 0x03B3,\n\t0xA5E4, 0x03B4, 0xA5E5, 0x03B5, 0xA5E6, 0x03B6, 0xA5E7, 0x03B7,\n\t0xA5E8, 0x03B8, 0xA5E9, 0x03B9, 0xA5EA, 0x03BA, 0xA5EB, 0x03BB,\n\t0xA5EC, 0x03BC, 0xA5ED, 0x03BD, 0xA5EE, 0x03BE, 0xA5EF, 0x03BF,\n\t0xA5F0, 0x03C0, 0xA5F1, 0x03C1, 0xA5F2, 0x03C3, 0xA5F3, 0x03C4,\n\t0xA5F4, 0x03C5, 0xA5F5, 0x03C6, 0xA5F6, 0x03C7, 0xA5F7, 0x03C8,\n\t0xA5F8, 0x03C9, 0xA641, 0xCAA8, 0xA642, 0xCAA9, 0xA643, 0xCAAA,\n\t0xA644, 0xCAAB, 0xA645, 0xCAAC, 0xA646, 0xCAAD, 0xA647, 0xCAAE,\n\t0xA648, 0xCAAF, 0xA649, 0xCAB0, 0xA64A, 0xCAB1, 0xA64B, 0xCAB2,\n\t0xA64C, 0xCAB3, 0xA64D, 0xCAB4, 0xA64E, 0xCAB5, 0xA64F, 0xCAB6,\n\t0xA650, 0xCAB7, 0xA651, 0xCAB8, 0xA652, 0xCAB9, 0xA653, 0xCABA,\n\t0xA654, 0xCABB, 0xA655, 0xCABE, 0xA656, 0xCABF, 0xA657, 0xCAC1,\n\t0xA658, 0xCAC2, 0xA659, 0xCAC3, 0xA65A, 0xCAC5, 0xA661, 0xCAC6,\n\t0xA662, 0xCAC7, 0xA663, 0xCAC8, 0xA664, 0xCAC9, 0xA665, 0xCACA,\n\t0xA666, 0xCACB, 0xA667, 0xCACE, 0xA668, 0xCAD0, 0xA669, 0xCAD2,\n\t0xA66A, 0xCAD4, 0xA66B, 0xCAD5, 0xA66C, 0xCAD6, 0xA66D, 0xCAD7,\n\t0xA66E, 0xCADA, 0xA66F, 0xCADB, 0xA670, 0xCADC, 0xA671, 0xCADD,\n\t0xA672, 0xCADE, 0xA673, 0xCADF, 0xA674, 0xCAE1, 0xA675, 0xCAE2,\n\t0xA676, 0xCAE3, 0xA677, 0xCAE4, 0xA678, 0xCAE5, 0xA679, 0xCAE6,\n\t0xA67A, 0xCAE7, 0xA681, 0xCAE8, 0xA682, 0xCAE9, 0xA683, 0xCAEA,\n\t0xA684, 0xCAEB, 0xA685, 0xCAED, 0xA686, 0xCAEE, 0xA687, 0xCAEF,\n\t0xA688, 0xCAF0, 0xA689, 0xCAF1, 0xA68A, 0xCAF2, 0xA68B, 0xCAF3,\n\t0xA68C, 0xCAF5, 0xA68D, 0xCAF6, 0xA68E, 0xCAF7, 0xA68F, 0xCAF8,\n\t0xA690, 0xCAF9, 0xA691, 0xCAFA, 0xA692, 0xCAFB, 0xA693, 0xCAFC,\n\t0xA694, 0xCAFD, 0xA695, 0xCAFE, 0xA696, 0xCAFF, 0xA697, 0xCB00,\n\t0xA698, 0xCB01, 0xA699, 0xCB02, 0xA69A, 0xCB03, 0xA69B, 0xCB04,\n\t0xA69C, 0xCB05, 0xA69D, 0xCB06, 0xA69E, 0xCB07, 0xA69F, 0xCB09,\n\t0xA6A0, 0xCB0A, 0xA6A1, 0x2500, 0xA6A2, 0x2502, 0xA6A3, 0x250C,\n\t0xA6A4, 0x2510, 0xA6A5, 0x2518, 0xA6A6, 0x2514, 0xA6A7, 0x251C,\n\t0xA6A8, 0x252C, 0xA6A9, 0x2524, 0xA6AA, 0x2534, 0xA6AB, 0x253C,\n\t0xA6AC, 0x2501, 0xA6AD, 0x2503, 0xA6AE, 0x250F, 0xA6AF, 0x2513,\n\t0xA6B0, 0x251B, 0xA6B1, 0x2517, 0xA6B2, 0x2523, 0xA6B3, 0x2533,\n\t0xA6B4, 0x252B, 0xA6B5, 0x253B, 0xA6B6, 0x254B, 0xA6B7, 0x2520,\n\t0xA6B8, 0x252F, 0xA6B9, 0x2528, 0xA6BA, 0x2537, 0xA6BB, 0x253F,\n\t0xA6BC, 0x251D, 0xA6BD, 0x2530, 0xA6BE, 0x2525, 0xA6BF, 0x2538,\n\t0xA6C0, 0x2542, 0xA6C1, 0x2512, 0xA6C2, 0x2511, 0xA6C3, 0x251A,\n\t0xA6C4, 0x2519, 0xA6C5, 0x2516, 0xA6C6, 0x2515, 0xA6C7, 0x250E,\n\t0xA6C8, 0x250D, 0xA6C9, 0x251E, 0xA6CA, 0x251F, 0xA6CB, 0x2521,\n\t0xA6CC, 0x2522, 0xA6CD, 0x2526, 0xA6CE, 0x2527, 0xA6CF, 0x2529,\n\t0xA6D0, 0x252A, 0xA6D1, 0x252D, 0xA6D2, 0x252E, 0xA6D3, 0x2531,\n\t0xA6D4, 0x2532, 0xA6D5, 0x2535, 0xA6D6, 0x2536, 0xA6D7, 0x2539,\n\t0xA6D8, 0x253A, 0xA6D9, 0x253D, 0xA6DA, 0x253E, 0xA6DB, 0x2540,\n\t0xA6DC, 0x2541, 0xA6DD, 0x2543, 0xA6DE, 0x2544, 0xA6DF, 0x2545,\n\t0xA6E0, 0x2546, 0xA6E1, 0x2547, 0xA6E2, 0x2548, 0xA6E3, 0x2549,\n\t0xA6E4, 0x254A, 0xA741, 0xCB0B, 0xA742, 0xCB0C, 0xA743, 0xCB0D,\n\t0xA744, 0xCB0E, 0xA745, 0xCB0F, 0xA746, 0xCB11, 0xA747, 0xCB12,\n\t0xA748, 0xCB13, 0xA749, 0xCB15, 0xA74A, 0xCB16, 0xA74B, 0xCB17,\n\t0xA74C, 0xCB19, 0xA74D, 0xCB1A, 0xA74E, 0xCB1B, 0xA74F, 0xCB1C,\n\t0xA750, 0xCB1D, 0xA751, 0xCB1E, 0xA752, 0xCB1F, 0xA753, 0xCB22,\n\t0xA754, 0xCB23, 0xA755, 0xCB24, 0xA756, 0xCB25, 0xA757, 0xCB26,\n\t0xA758, 0xCB27, 0xA759, 0xCB28, 0xA75A, 0xCB29, 0xA761, 0xCB2A,\n\t0xA762, 0xCB2B, 0xA763, 0xCB2C, 0xA764, 0xCB2D, 0xA765, 0xCB2E,\n\t0xA766, 0xCB2F, 0xA767, 0xCB30, 0xA768, 0xCB31, 0xA769, 0xCB32,\n\t0xA76A, 0xCB33, 0xA76B, 0xCB34, 0xA76C, 0xCB35, 0xA76D, 0xCB36,\n\t0xA76E, 0xCB37, 0xA76F, 0xCB38, 0xA770, 0xCB39, 0xA771, 0xCB3A,\n\t0xA772, 0xCB3B, 0xA773, 0xCB3C, 0xA774, 0xCB3D, 0xA775, 0xCB3E,\n\t0xA776, 0xCB3F, 0xA777, 0xCB40, 0xA778, 0xCB42, 0xA779, 0xCB43,\n\t0xA77A, 0xCB44, 0xA781, 0xCB45, 0xA782, 0xCB46, 0xA783, 0xCB47,\n\t0xA784, 0xCB4A, 0xA785, 0xCB4B, 0xA786, 0xCB4D, 0xA787, 0xCB4E,\n\t0xA788, 0xCB4F, 0xA789, 0xCB51, 0xA78A, 0xCB52, 0xA78B, 0xCB53,\n\t0xA78C, 0xCB54, 0xA78D, 0xCB55, 0xA78E, 0xCB56, 0xA78F, 0xCB57,\n\t0xA790, 0xCB5A, 0xA791, 0xCB5B, 0xA792, 0xCB5C, 0xA793, 0xCB5E,\n\t0xA794, 0xCB5F, 0xA795, 0xCB60, 0xA796, 0xCB61, 0xA797, 0xCB62,\n\t0xA798, 0xCB63, 0xA799, 0xCB65, 0xA79A, 0xCB66, 0xA79B, 0xCB67,\n\t0xA79C, 0xCB68, 0xA79D, 0xCB69, 0xA79E, 0xCB6A, 0xA79F, 0xCB6B,\n\t0xA7A0, 0xCB6C, 0xA7A1, 0x3395, 0xA7A2, 0x3396, 0xA7A3, 0x3397,\n\t0xA7A4, 0x2113, 0xA7A5, 0x3398, 0xA7A6, 0x33C4, 0xA7A7, 0x33A3,\n\t0xA7A8, 0x33A4, 0xA7A9, 0x33A5, 0xA7AA, 0x33A6, 0xA7AB, 0x3399,\n\t0xA7AC, 0x339A, 0xA7AD, 0x339B, 0xA7AE, 0x339C, 0xA7AF, 0x339D,\n\t0xA7B0, 0x339E, 0xA7B1, 0x339F, 0xA7B2, 0x33A0, 0xA7B3, 0x33A1,\n\t0xA7B4, 0x33A2, 0xA7B5, 0x33CA, 0xA7B6, 0x338D, 0xA7B7, 0x338E,\n\t0xA7B8, 0x338F, 0xA7B9, 0x33CF, 0xA7BA, 0x3388, 0xA7BB, 0x3389,\n\t0xA7BC, 0x33C8, 0xA7BD, 0x33A7, 0xA7BE, 0x33A8, 0xA7BF, 0x33B0,\n\t0xA7C0, 0x33B1, 0xA7C1, 0x33B2, 0xA7C2, 0x33B3, 0xA7C3, 0x33B4,\n\t0xA7C4, 0x33B5, 0xA7C5, 0x33B6, 0xA7C6, 0x33B7, 0xA7C7, 0x33B8,\n\t0xA7C8, 0x33B9, 0xA7C9, 0x3380, 0xA7CA, 0x3381, 0xA7CB, 0x3382,\n\t0xA7CC, 0x3383, 0xA7CD, 0x3384, 0xA7CE, 0x33BA, 0xA7CF, 0x33BB,\n\t0xA7D0, 0x33BC, 0xA7D1, 0x33BD, 0xA7D2, 0x33BE, 0xA7D3, 0x33BF,\n\t0xA7D4, 0x3390, 0xA7D5, 0x3391, 0xA7D6, 0x3392, 0xA7D7, 0x3393,\n\t0xA7D8, 0x3394, 0xA7D9, 0x2126, 0xA7DA, 0x33C0, 0xA7DB, 0x33C1,\n\t0xA7DC, 0x338A, 0xA7DD, 0x338B, 0xA7DE, 0x338C, 0xA7DF, 0x33D6,\n\t0xA7E0, 0x33C5, 0xA7E1, 0x33AD, 0xA7E2, 0x33AE, 0xA7E3, 0x33AF,\n\t0xA7E4, 0x33DB, 0xA7E5, 0x33A9, 0xA7E6, 0x33AA, 0xA7E7, 0x33AB,\n\t0xA7E8, 0x33AC, 0xA7E9, 0x33DD, 0xA7EA, 0x33D0, 0xA7EB, 0x33D3,\n\t0xA7EC, 0x33C3, 0xA7ED, 0x33C9, 0xA7EE, 0x33DC, 0xA7EF, 0x33C6,\n\t0xA841, 0xCB6D, 0xA842, 0xCB6E, 0xA843, 0xCB6F, 0xA844, 0xCB70,\n\t0xA845, 0xCB71, 0xA846, 0xCB72, 0xA847, 0xCB73, 0xA848, 0xCB74,\n\t0xA849, 0xCB75, 0xA84A, 0xCB76, 0xA84B, 0xCB77, 0xA84C, 0xCB7A,\n\t0xA84D, 0xCB7B, 0xA84E, 0xCB7C, 0xA84F, 0xCB7D, 0xA850, 0xCB7E,\n\t0xA851, 0xCB7F, 0xA852, 0xCB80, 0xA853, 0xCB81, 0xA854, 0xCB82,\n\t0xA855, 0xCB83, 0xA856, 0xCB84, 0xA857, 0xCB85, 0xA858, 0xCB86,\n\t0xA859, 0xCB87, 0xA85A, 0xCB88, 0xA861, 0xCB89, 0xA862, 0xCB8A,\n\t0xA863, 0xCB8B, 0xA864, 0xCB8C, 0xA865, 0xCB8D, 0xA866, 0xCB8E,\n\t0xA867, 0xCB8F, 0xA868, 0xCB90, 0xA869, 0xCB91, 0xA86A, 0xCB92,\n\t0xA86B, 0xCB93, 0xA86C, 0xCB94, 0xA86D, 0xCB95, 0xA86E, 0xCB96,\n\t0xA86F, 0xCB97, 0xA870, 0xCB98, 0xA871, 0xCB99, 0xA872, 0xCB9A,\n\t0xA873, 0xCB9B, 0xA874, 0xCB9D, 0xA875, 0xCB9E, 0xA876, 0xCB9F,\n\t0xA877, 0xCBA0, 0xA878, 0xCBA1, 0xA879, 0xCBA2, 0xA87A, 0xCBA3,\n\t0xA881, 0xCBA4, 0xA882, 0xCBA5, 0xA883, 0xCBA6, 0xA884, 0xCBA7,\n\t0xA885, 0xCBA8, 0xA886, 0xCBA9, 0xA887, 0xCBAA, 0xA888, 0xCBAB,\n\t0xA889, 0xCBAC, 0xA88A, 0xCBAD, 0xA88B, 0xCBAE, 0xA88C, 0xCBAF,\n\t0xA88D, 0xCBB0, 0xA88E, 0xCBB1, 0xA88F, 0xCBB2, 0xA890, 0xCBB3,\n\t0xA891, 0xCBB4, 0xA892, 0xCBB5, 0xA893, 0xCBB6, 0xA894, 0xCBB7,\n\t0xA895, 0xCBB9, 0xA896, 0xCBBA, 0xA897, 0xCBBB, 0xA898, 0xCBBC,\n\t0xA899, 0xCBBD, 0xA89A, 0xCBBE, 0xA89B, 0xCBBF, 0xA89C, 0xCBC0,\n\t0xA89D, 0xCBC1, 0xA89E, 0xCBC2, 0xA89F, 0xCBC3, 0xA8A0, 0xCBC4,\n\t0xA8A1, 0x00C6, 0xA8A2, 0x00D0, 0xA8A3, 0x00AA, 0xA8A4, 0x0126,\n\t0xA8A6, 0x0132, 0xA8A8, 0x013F, 0xA8A9, 0x0141, 0xA8AA, 0x00D8,\n\t0xA8AB, 0x0152, 0xA8AC, 0x00BA, 0xA8AD, 0x00DE, 0xA8AE, 0x0166,\n\t0xA8AF, 0x014A, 0xA8B1, 0x3260, 0xA8B2, 0x3261, 0xA8B3, 0x3262,\n\t0xA8B4, 0x3263, 0xA8B5, 0x3264, 0xA8B6, 0x3265, 0xA8B7, 0x3266,\n\t0xA8B8, 0x3267, 0xA8B9, 0x3268, 0xA8BA, 0x3269, 0xA8BB, 0x326A,\n\t0xA8BC, 0x326B, 0xA8BD, 0x326C, 0xA8BE, 0x326D, 0xA8BF, 0x326E,\n\t0xA8C0, 0x326F, 0xA8C1, 0x3270, 0xA8C2, 0x3271, 0xA8C3, 0x3272,\n\t0xA8C4, 0x3273, 0xA8C5, 0x3274, 0xA8C6, 0x3275, 0xA8C7, 0x3276,\n\t0xA8C8, 0x3277, 0xA8C9, 0x3278, 0xA8CA, 0x3279, 0xA8CB, 0x327A,\n\t0xA8CC, 0x327B, 0xA8CD, 0x24D0, 0xA8CE, 0x24D1, 0xA8CF, 0x24D2,\n\t0xA8D0, 0x24D3, 0xA8D1, 0x24D4, 0xA8D2, 0x24D5, 0xA8D3, 0x24D6,\n\t0xA8D4, 0x24D7, 0xA8D5, 0x24D8, 0xA8D6, 0x24D9, 0xA8D7, 0x24DA,\n\t0xA8D8, 0x24DB, 0xA8D9, 0x24DC, 0xA8DA, 0x24DD, 0xA8DB, 0x24DE,\n\t0xA8DC, 0x24DF, 0xA8DD, 0x24E0, 0xA8DE, 0x24E1, 0xA8DF, 0x24E2,\n\t0xA8E0, 0x24E3, 0xA8E1, 0x24E4, 0xA8E2, 0x24E5, 0xA8E3, 0x24E6,\n\t0xA8E4, 0x24E7, 0xA8E5, 0x24E8, 0xA8E6, 0x24E9, 0xA8E7, 0x2460,\n\t0xA8E8, 0x2461, 0xA8E9, 0x2462, 0xA8EA, 0x2463, 0xA8EB, 0x2464,\n\t0xA8EC, 0x2465, 0xA8ED, 0x2466, 0xA8EE, 0x2467, 0xA8EF, 0x2468,\n\t0xA8F0, 0x2469, 0xA8F1, 0x246A, 0xA8F2, 0x246B, 0xA8F3, 0x246C,\n\t0xA8F4, 0x246D, 0xA8F5, 0x246E, 0xA8F6, 0x00BD, 0xA8F7, 0x2153,\n\t0xA8F8, 0x2154, 0xA8F9, 0x00BC, 0xA8FA, 0x00BE, 0xA8FB, 0x215B,\n\t0xA8FC, 0x215C, 0xA8FD, 0x215D, 0xA8FE, 0x215E, 0xA941, 0xCBC5,\n\t0xA942, 0xCBC6, 0xA943, 0xCBC7, 0xA944, 0xCBC8, 0xA945, 0xCBC9,\n\t0xA946, 0xCBCA, 0xA947, 0xCBCB, 0xA948, 0xCBCC, 0xA949, 0xCBCD,\n\t0xA94A, 0xCBCE, 0xA94B, 0xCBCF, 0xA94C, 0xCBD0, 0xA94D, 0xCBD1,\n\t0xA94E, 0xCBD2, 0xA94F, 0xCBD3, 0xA950, 0xCBD5, 0xA951, 0xCBD6,\n\t0xA952, 0xCBD7, 0xA953, 0xCBD8, 0xA954, 0xCBD9, 0xA955, 0xCBDA,\n\t0xA956, 0xCBDB, 0xA957, 0xCBDC, 0xA958, 0xCBDD, 0xA959, 0xCBDE,\n\t0xA95A, 0xCBDF, 0xA961, 0xCBE0, 0xA962, 0xCBE1, 0xA963, 0xCBE2,\n\t0xA964, 0xCBE3, 0xA965, 0xCBE5, 0xA966, 0xCBE6, 0xA967, 0xCBE8,\n\t0xA968, 0xCBEA, 0xA969, 0xCBEB, 0xA96A, 0xCBEC, 0xA96B, 0xCBED,\n\t0xA96C, 0xCBEE, 0xA96D, 0xCBEF, 0xA96E, 0xCBF0, 0xA96F, 0xCBF1,\n\t0xA970, 0xCBF2, 0xA971, 0xCBF3, 0xA972, 0xCBF4, 0xA973, 0xCBF5,\n\t0xA974, 0xCBF6, 0xA975, 0xCBF7, 0xA976, 0xCBF8, 0xA977, 0xCBF9,\n\t0xA978, 0xCBFA, 0xA979, 0xCBFB, 0xA97A, 0xCBFC, 0xA981, 0xCBFD,\n\t0xA982, 0xCBFE, 0xA983, 0xCBFF, 0xA984, 0xCC00, 0xA985, 0xCC01,\n\t0xA986, 0xCC02, 0xA987, 0xCC03, 0xA988, 0xCC04, 0xA989, 0xCC05,\n\t0xA98A, 0xCC06, 0xA98B, 0xCC07, 0xA98C, 0xCC08, 0xA98D, 0xCC09,\n\t0xA98E, 0xCC0A, 0xA98F, 0xCC0B, 0xA990, 0xCC0E, 0xA991, 0xCC0F,\n\t0xA992, 0xCC11, 0xA993, 0xCC12, 0xA994, 0xCC13, 0xA995, 0xCC15,\n\t0xA996, 0xCC16, 0xA997, 0xCC17, 0xA998, 0xCC18, 0xA999, 0xCC19,\n\t0xA99A, 0xCC1A, 0xA99B, 0xCC1B, 0xA99C, 0xCC1E, 0xA99D, 0xCC1F,\n\t0xA99E, 0xCC20, 0xA99F, 0xCC23, 0xA9A0, 0xCC24, 0xA9A1, 0x00E6,\n\t0xA9A2, 0x0111, 0xA9A3, 0x00F0, 0xA9A4, 0x0127, 0xA9A5, 0x0131,\n\t0xA9A6, 0x0133, 0xA9A7, 0x0138, 0xA9A8, 0x0140, 0xA9A9, 0x0142,\n\t0xA9AA, 0x00F8, 0xA9AB, 0x0153, 0xA9AC, 0x00DF, 0xA9AD, 0x00FE,\n\t0xA9AE, 0x0167, 0xA9AF, 0x014B, 0xA9B0, 0x0149, 0xA9B1, 0x3200,\n\t0xA9B2, 0x3201, 0xA9B3, 0x3202, 0xA9B4, 0x3203, 0xA9B5, 0x3204,\n\t0xA9B6, 0x3205, 0xA9B7, 0x3206, 0xA9B8, 0x3207, 0xA9B9, 0x3208,\n\t0xA9BA, 0x3209, 0xA9BB, 0x320A, 0xA9BC, 0x320B, 0xA9BD, 0x320C,\n\t0xA9BE, 0x320D, 0xA9BF, 0x320E, 0xA9C0, 0x320F, 0xA9C1, 0x3210,\n\t0xA9C2, 0x3211, 0xA9C3, 0x3212, 0xA9C4, 0x3213, 0xA9C5, 0x3214,\n\t0xA9C6, 0x3215, 0xA9C7, 0x3216, 0xA9C8, 0x3217, 0xA9C9, 0x3218,\n\t0xA9CA, 0x3219, 0xA9CB, 0x321A, 0xA9CC, 0x321B, 0xA9CD, 0x249C,\n\t0xA9CE, 0x249D, 0xA9CF, 0x249E, 0xA9D0, 0x249F, 0xA9D1, 0x24A0,\n\t0xA9D2, 0x24A1, 0xA9D3, 0x24A2, 0xA9D4, 0x24A3, 0xA9D5, 0x24A4,\n\t0xA9D6, 0x24A5, 0xA9D7, 0x24A6, 0xA9D8, 0x24A7, 0xA9D9, 0x24A8,\n\t0xA9DA, 0x24A9, 0xA9DB, 0x24AA, 0xA9DC, 0x24AB, 0xA9DD, 0x24AC,\n\t0xA9DE, 0x24AD, 0xA9DF, 0x24AE, 0xA9E0, 0x24AF, 0xA9E1, 0x24B0,\n\t0xA9E2, 0x24B1, 0xA9E3, 0x24B2, 0xA9E4, 0x24B3, 0xA9E5, 0x24B4,\n\t0xA9E6, 0x24B5, 0xA9E7, 0x2474, 0xA9E8, 0x2475, 0xA9E9, 0x2476,\n\t0xA9EA, 0x2477, 0xA9EB, 0x2478, 0xA9EC, 0x2479, 0xA9ED, 0x247A,\n\t0xA9EE, 0x247B, 0xA9EF, 0x247C, 0xA9F0, 0x247D, 0xA9F1, 0x247E,\n\t0xA9F2, 0x247F, 0xA9F3, 0x2480, 0xA9F4, 0x2481, 0xA9F5, 0x2482,\n\t0xA9F6, 0x00B9, 0xA9F7, 0x00B2, 0xA9F8, 0x00B3, 0xA9F9, 0x2074,\n\t0xA9FA, 0x207F, 0xA9FB, 0x2081, 0xA9FC, 0x2082, 0xA9FD, 0x2083,\n\t0xA9FE, 0x2084, 0xAA41, 0xCC25, 0xAA42, 0xCC26, 0xAA43, 0xCC2A,\n\t0xAA44, 0xCC2B, 0xAA45, 0xCC2D, 0xAA46, 0xCC2F, 0xAA47, 0xCC31,\n\t0xAA48, 0xCC32, 0xAA49, 0xCC33, 0xAA4A, 0xCC34, 0xAA4B, 0xCC35,\n\t0xAA4C, 0xCC36, 0xAA4D, 0xCC37, 0xAA4E, 0xCC3A, 0xAA4F, 0xCC3F,\n\t0xAA50, 0xCC40, 0xAA51, 0xCC41, 0xAA52, 0xCC42, 0xAA53, 0xCC43,\n\t0xAA54, 0xCC46, 0xAA55, 0xCC47, 0xAA56, 0xCC49, 0xAA57, 0xCC4A,\n\t0xAA58, 0xCC4B, 0xAA59, 0xCC4D, 0xAA5A, 0xCC4E, 0xAA61, 0xCC4F,\n\t0xAA62, 0xCC50, 0xAA63, 0xCC51, 0xAA64, 0xCC52, 0xAA65, 0xCC53,\n\t0xAA66, 0xCC56, 0xAA67, 0xCC5A, 0xAA68, 0xCC5B, 0xAA69, 0xCC5C,\n\t0xAA6A, 0xCC5D, 0xAA6B, 0xCC5E, 0xAA6C, 0xCC5F, 0xAA6D, 0xCC61,\n\t0xAA6E, 0xCC62, 0xAA6F, 0xCC63, 0xAA70, 0xCC65, 0xAA71, 0xCC67,\n\t0xAA72, 0xCC69, 0xAA73, 0xCC6A, 0xAA74, 0xCC6B, 0xAA75, 0xCC6C,\n\t0xAA76, 0xCC6D, 0xAA77, 0xCC6E, 0xAA78, 0xCC6F, 0xAA79, 0xCC71,\n\t0xAA7A, 0xCC72, 0xAA81, 0xCC73, 0xAA82, 0xCC74, 0xAA83, 0xCC76,\n\t0xAA84, 0xCC77, 0xAA85, 0xCC78, 0xAA86, 0xCC79, 0xAA87, 0xCC7A,\n\t0xAA88, 0xCC7B, 0xAA89, 0xCC7C, 0xAA8A, 0xCC7D, 0xAA8B, 0xCC7E,\n\t0xAA8C, 0xCC7F, 0xAA8D, 0xCC80, 0xAA8E, 0xCC81, 0xAA8F, 0xCC82,\n\t0xAA90, 0xCC83, 0xAA91, 0xCC84, 0xAA92, 0xCC85, 0xAA93, 0xCC86,\n\t0xAA94, 0xCC87, 0xAA95, 0xCC88, 0xAA96, 0xCC89, 0xAA97, 0xCC8A,\n\t0xAA98, 0xCC8B, 0xAA99, 0xCC8C, 0xAA9A, 0xCC8D, 0xAA9B, 0xCC8E,\n\t0xAA9C, 0xCC8F, 0xAA9D, 0xCC90, 0xAA9E, 0xCC91, 0xAA9F, 0xCC92,\n\t0xAAA0, 0xCC93, 0xAAA1, 0x3041, 0xAAA2, 0x3042, 0xAAA3, 0x3043,\n\t0xAAA4, 0x3044, 0xAAA5, 0x3045, 0xAAA6, 0x3046, 0xAAA7, 0x3047,\n\t0xAAA8, 0x3048, 0xAAA9, 0x3049, 0xAAAA, 0x304A, 0xAAAB, 0x304B,\n\t0xAAAC, 0x304C, 0xAAAD, 0x304D, 0xAAAE, 0x304E, 0xAAAF, 0x304F,\n\t0xAAB0, 0x3050, 0xAAB1, 0x3051, 0xAAB2, 0x3052, 0xAAB3, 0x3053,\n\t0xAAB4, 0x3054, 0xAAB5, 0x3055, 0xAAB6, 0x3056, 0xAAB7, 0x3057,\n\t0xAAB8, 0x3058, 0xAAB9, 0x3059, 0xAABA, 0x305A, 0xAABB, 0x305B,\n\t0xAABC, 0x305C, 0xAABD, 0x305D, 0xAABE, 0x305E, 0xAABF, 0x305F,\n\t0xAAC0, 0x3060, 0xAAC1, 0x3061, 0xAAC2, 0x3062, 0xAAC3, 0x3063,\n\t0xAAC4, 0x3064, 0xAAC5, 0x3065, 0xAAC6, 0x3066, 0xAAC7, 0x3067,\n\t0xAAC8, 0x3068, 0xAAC9, 0x3069, 0xAACA, 0x306A, 0xAACB, 0x306B,\n\t0xAACC, 0x306C, 0xAACD, 0x306D, 0xAACE, 0x306E, 0xAACF, 0x306F,\n\t0xAAD0, 0x3070, 0xAAD1, 0x3071, 0xAAD2, 0x3072, 0xAAD3, 0x3073,\n\t0xAAD4, 0x3074, 0xAAD5, 0x3075, 0xAAD6, 0x3076, 0xAAD7, 0x3077,\n\t0xAAD8, 0x3078, 0xAAD9, 0x3079, 0xAADA, 0x307A, 0xAADB, 0x307B,\n\t0xAADC, 0x307C, 0xAADD, 0x307D, 0xAADE, 0x307E, 0xAADF, 0x307F,\n\t0xAAE0, 0x3080, 0xAAE1, 0x3081, 0xAAE2, 0x3082, 0xAAE3, 0x3083,\n\t0xAAE4, 0x3084, 0xAAE5, 0x3085, 0xAAE6, 0x3086, 0xAAE7, 0x3087,\n\t0xAAE8, 0x3088, 0xAAE9, 0x3089, 0xAAEA, 0x308A, 0xAAEB, 0x308B,\n\t0xAAEC, 0x308C, 0xAAED, 0x308D, 0xAAEE, 0x308E, 0xAAEF, 0x308F,\n\t0xAAF0, 0x3090, 0xAAF1, 0x3091, 0xAAF2, 0x3092, 0xAAF3, 0x3093,\n\t0xAB41, 0xCC94, 0xAB42, 0xCC95, 0xAB43, 0xCC96, 0xAB44, 0xCC97,\n\t0xAB45, 0xCC9A, 0xAB46, 0xCC9B, 0xAB47, 0xCC9D, 0xAB48, 0xCC9E,\n\t0xAB49, 0xCC9F, 0xAB4A, 0xCCA1, 0xAB4B, 0xCCA2, 0xAB4C, 0xCCA3,\n\t0xAB4D, 0xCCA4, 0xAB4E, 0xCCA5, 0xAB4F, 0xCCA6, 0xAB50, 0xCCA7,\n\t0xAB51, 0xCCAA, 0xAB52, 0xCCAE, 0xAB53, 0xCCAF, 0xAB54, 0xCCB0,\n\t0xAB55, 0xCCB1, 0xAB56, 0xCCB2, 0xAB57, 0xCCB3, 0xAB58, 0xCCB6,\n\t0xAB59, 0xCCB7, 0xAB5A, 0xCCB9, 0xAB61, 0xCCBA, 0xAB62, 0xCCBB,\n\t0xAB63, 0xCCBD, 0xAB64, 0xCCBE, 0xAB65, 0xCCBF, 0xAB66, 0xCCC0,\n\t0xAB67, 0xCCC1, 0xAB68, 0xCCC2, 0xAB69, 0xCCC3, 0xAB6A, 0xCCC6,\n\t0xAB6B, 0xCCC8, 0xAB6C, 0xCCCA, 0xAB6D, 0xCCCB, 0xAB6E, 0xCCCC,\n\t0xAB6F, 0xCCCD, 0xAB70, 0xCCCE, 0xAB71, 0xCCCF, 0xAB72, 0xCCD1,\n\t0xAB73, 0xCCD2, 0xAB74, 0xCCD3, 0xAB75, 0xCCD5, 0xAB76, 0xCCD6,\n\t0xAB77, 0xCCD7, 0xAB78, 0xCCD8, 0xAB79, 0xCCD9, 0xAB7A, 0xCCDA,\n\t0xAB81, 0xCCDB, 0xAB82, 0xCCDC, 0xAB83, 0xCCDD, 0xAB84, 0xCCDE,\n\t0xAB85, 0xCCDF, 0xAB86, 0xCCE0, 0xAB87, 0xCCE1, 0xAB88, 0xCCE2,\n\t0xAB89, 0xCCE3, 0xAB8A, 0xCCE5, 0xAB8B, 0xCCE6, 0xAB8C, 0xCCE7,\n\t0xAB8D, 0xCCE8, 0xAB8E, 0xCCE9, 0xAB8F, 0xCCEA, 0xAB90, 0xCCEB,\n\t0xAB91, 0xCCED, 0xAB92, 0xCCEE, 0xAB93, 0xCCEF, 0xAB94, 0xCCF1,\n\t0xAB95, 0xCCF2, 0xAB96, 0xCCF3, 0xAB97, 0xCCF4, 0xAB98, 0xCCF5,\n\t0xAB99, 0xCCF6, 0xAB9A, 0xCCF7, 0xAB9B, 0xCCF8, 0xAB9C, 0xCCF9,\n\t0xAB9D, 0xCCFA, 0xAB9E, 0xCCFB, 0xAB9F, 0xCCFC, 0xABA0, 0xCCFD,\n\t0xABA1, 0x30A1, 0xABA2, 0x30A2, 0xABA3, 0x30A3, 0xABA4, 0x30A4,\n\t0xABA5, 0x30A5, 0xABA6, 0x30A6, 0xABA7, 0x30A7, 0xABA8, 0x30A8,\n\t0xABA9, 0x30A9, 0xABAA, 0x30AA, 0xABAB, 0x30AB, 0xABAC, 0x30AC,\n\t0xABAD, 0x30AD, 0xABAE, 0x30AE, 0xABAF, 0x30AF, 0xABB0, 0x30B0,\n\t0xABB1, 0x30B1, 0xABB2, 0x30B2, 0xABB3, 0x30B3, 0xABB4, 0x30B4,\n\t0xABB5, 0x30B5, 0xABB6, 0x30B6, 0xABB7, 0x30B7, 0xABB8, 0x30B8,\n\t0xABB9, 0x30B9, 0xABBA, 0x30BA, 0xABBB, 0x30BB, 0xABBC, 0x30BC,\n\t0xABBD, 0x30BD, 0xABBE, 0x30BE, 0xABBF, 0x30BF, 0xABC0, 0x30C0,\n\t0xABC1, 0x30C1, 0xABC2, 0x30C2, 0xABC3, 0x30C3, 0xABC4, 0x30C4,\n\t0xABC5, 0x30C5, 0xABC6, 0x30C6, 0xABC7, 0x30C7, 0xABC8, 0x30C8,\n\t0xABC9, 0x30C9, 0xABCA, 0x30CA, 0xABCB, 0x30CB, 0xABCC, 0x30CC,\n\t0xABCD, 0x30CD, 0xABCE, 0x30CE, 0xABCF, 0x30CF, 0xABD0, 0x30D0,\n\t0xABD1, 0x30D1, 0xABD2, 0x30D2, 0xABD3, 0x30D3, 0xABD4, 0x30D4,\n\t0xABD5, 0x30D5, 0xABD6, 0x30D6, 0xABD7, 0x30D7, 0xABD8, 0x30D8,\n\t0xABD9, 0x30D9, 0xABDA, 0x30DA, 0xABDB, 0x30DB, 0xABDC, 0x30DC,\n\t0xABDD, 0x30DD, 0xABDE, 0x30DE, 0xABDF, 0x30DF, 0xABE0, 0x30E0,\n\t0xABE1, 0x30E1, 0xABE2, 0x30E2, 0xABE3, 0x30E3, 0xABE4, 0x30E4,\n\t0xABE5, 0x30E5, 0xABE6, 0x30E6, 0xABE7, 0x30E7, 0xABE8, 0x30E8,\n\t0xABE9, 0x30E9, 0xABEA, 0x30EA, 0xABEB, 0x30EB, 0xABEC, 0x30EC,\n\t0xABED, 0x30ED, 0xABEE, 0x30EE, 0xABEF, 0x30EF, 0xABF0, 0x30F0,\n\t0xABF1, 0x30F1, 0xABF2, 0x30F2, 0xABF3, 0x30F3, 0xABF4, 0x30F4,\n\t0xABF5, 0x30F5, 0xABF6, 0x30F6, 0xAC41, 0xCCFE, 0xAC42, 0xCCFF,\n\t0xAC43, 0xCD00, 0xAC44, 0xCD02, 0xAC45, 0xCD03, 0xAC46, 0xCD04,\n\t0xAC47, 0xCD05, 0xAC48, 0xCD06, 0xAC49, 0xCD07, 0xAC4A, 0xCD0A,\n\t0xAC4B, 0xCD0B, 0xAC4C, 0xCD0D, 0xAC4D, 0xCD0E, 0xAC4E, 0xCD0F,\n\t0xAC4F, 0xCD11, 0xAC50, 0xCD12, 0xAC51, 0xCD13, 0xAC52, 0xCD14,\n\t0xAC53, 0xCD15, 0xAC54, 0xCD16, 0xAC55, 0xCD17, 0xAC56, 0xCD1A,\n\t0xAC57, 0xCD1C, 0xAC58, 0xCD1E, 0xAC59, 0xCD1F, 0xAC5A, 0xCD20,\n\t0xAC61, 0xCD21, 0xAC62, 0xCD22, 0xAC63, 0xCD23, 0xAC64, 0xCD25,\n\t0xAC65, 0xCD26, 0xAC66, 0xCD27, 0xAC67, 0xCD29, 0xAC68, 0xCD2A,\n\t0xAC69, 0xCD2B, 0xAC6A, 0xCD2D, 0xAC6B, 0xCD2E, 0xAC6C, 0xCD2F,\n\t0xAC6D, 0xCD30, 0xAC6E, 0xCD31, 0xAC6F, 0xCD32, 0xAC70, 0xCD33,\n\t0xAC71, 0xCD34, 0xAC72, 0xCD35, 0xAC73, 0xCD36, 0xAC74, 0xCD37,\n\t0xAC75, 0xCD38, 0xAC76, 0xCD3A, 0xAC77, 0xCD3B, 0xAC78, 0xCD3C,\n\t0xAC79, 0xCD3D, 0xAC7A, 0xCD3E, 0xAC81, 0xCD3F, 0xAC82, 0xCD40,\n\t0xAC83, 0xCD41, 0xAC84, 0xCD42, 0xAC85, 0xCD43, 0xAC86, 0xCD44,\n\t0xAC87, 0xCD45, 0xAC88, 0xCD46, 0xAC89, 0xCD47, 0xAC8A, 0xCD48,\n\t0xAC8B, 0xCD49, 0xAC8C, 0xCD4A, 0xAC8D, 0xCD4B, 0xAC8E, 0xCD4C,\n\t0xAC8F, 0xCD4D, 0xAC90, 0xCD4E, 0xAC91, 0xCD4F, 0xAC92, 0xCD50,\n\t0xAC93, 0xCD51, 0xAC94, 0xCD52, 0xAC95, 0xCD53, 0xAC96, 0xCD54,\n\t0xAC97, 0xCD55, 0xAC98, 0xCD56, 0xAC99, 0xCD57, 0xAC9A, 0xCD58,\n\t0xAC9B, 0xCD59, 0xAC9C, 0xCD5A, 0xAC9D, 0xCD5B, 0xAC9E, 0xCD5D,\n\t0xAC9F, 0xCD5E, 0xACA0, 0xCD5F, 0xACA1, 0x0410, 0xACA2, 0x0411,\n\t0xACA3, 0x0412, 0xACA4, 0x0413, 0xACA5, 0x0414, 0xACA6, 0x0415,\n\t0xACA7, 0x0401, 0xACA8, 0x0416, 0xACA9, 0x0417, 0xACAA, 0x0418,\n\t0xACAB, 0x0419, 0xACAC, 0x041A, 0xACAD, 0x041B, 0xACAE, 0x041C,\n\t0xACAF, 0x041D, 0xACB0, 0x041E, 0xACB1, 0x041F, 0xACB2, 0x0420,\n\t0xACB3, 0x0421, 0xACB4, 0x0422, 0xACB5, 0x0423, 0xACB6, 0x0424,\n\t0xACB7, 0x0425, 0xACB8, 0x0426, 0xACB9, 0x0427, 0xACBA, 0x0428,\n\t0xACBB, 0x0429, 0xACBC, 0x042A, 0xACBD, 0x042B, 0xACBE, 0x042C,\n\t0xACBF, 0x042D, 0xACC0, 0x042E, 0xACC1, 0x042F, 0xACD1, 0x0430,\n\t0xACD2, 0x0431, 0xACD3, 0x0432, 0xACD4, 0x0433, 0xACD5, 0x0434,\n\t0xACD6, 0x0435, 0xACD7, 0x0451, 0xACD8, 0x0436, 0xACD9, 0x0437,\n\t0xACDA, 0x0438, 0xACDB, 0x0439, 0xACDC, 0x043A, 0xACDD, 0x043B,\n\t0xACDE, 0x043C, 0xACDF, 0x043D, 0xACE0, 0x043E, 0xACE1, 0x043F,\n\t0xACE2, 0x0440, 0xACE3, 0x0441, 0xACE4, 0x0442, 0xACE5, 0x0443,\n\t0xACE6, 0x0444, 0xACE7, 0x0445, 0xACE8, 0x0446, 0xACE9, 0x0447,\n\t0xACEA, 0x0448, 0xACEB, 0x0449, 0xACEC, 0x044A, 0xACED, 0x044B,\n\t0xACEE, 0x044C, 0xACEF, 0x044D, 0xACF0, 0x044E, 0xACF1, 0x044F,\n\t0xAD41, 0xCD61, 0xAD42, 0xCD62, 0xAD43, 0xCD63, 0xAD44, 0xCD65,\n\t0xAD45, 0xCD66, 0xAD46, 0xCD67, 0xAD47, 0xCD68, 0xAD48, 0xCD69,\n\t0xAD49, 0xCD6A, 0xAD4A, 0xCD6B, 0xAD4B, 0xCD6E, 0xAD4C, 0xCD70,\n\t0xAD4D, 0xCD72, 0xAD4E, 0xCD73, 0xAD4F, 0xCD74, 0xAD50, 0xCD75,\n\t0xAD51, 0xCD76, 0xAD52, 0xCD77, 0xAD53, 0xCD79, 0xAD54, 0xCD7A,\n\t0xAD55, 0xCD7B, 0xAD56, 0xCD7C, 0xAD57, 0xCD7D, 0xAD58, 0xCD7E,\n\t0xAD59, 0xCD7F, 0xAD5A, 0xCD80, 0xAD61, 0xCD81, 0xAD62, 0xCD82,\n\t0xAD63, 0xCD83, 0xAD64, 0xCD84, 0xAD65, 0xCD85, 0xAD66, 0xCD86,\n\t0xAD67, 0xCD87, 0xAD68, 0xCD89, 0xAD69, 0xCD8A, 0xAD6A, 0xCD8B,\n\t0xAD6B, 0xCD8C, 0xAD6C, 0xCD8D, 0xAD6D, 0xCD8E, 0xAD6E, 0xCD8F,\n\t0xAD6F, 0xCD90, 0xAD70, 0xCD91, 0xAD71, 0xCD92, 0xAD72, 0xCD93,\n\t0xAD73, 0xCD96, 0xAD74, 0xCD97, 0xAD75, 0xCD99, 0xAD76, 0xCD9A,\n\t0xAD77, 0xCD9B, 0xAD78, 0xCD9D, 0xAD79, 0xCD9E, 0xAD7A, 0xCD9F,\n\t0xAD81, 0xCDA0, 0xAD82, 0xCDA1, 0xAD83, 0xCDA2, 0xAD84, 0xCDA3,\n\t0xAD85, 0xCDA6, 0xAD86, 0xCDA8, 0xAD87, 0xCDAA, 0xAD88, 0xCDAB,\n\t0xAD89, 0xCDAC, 0xAD8A, 0xCDAD, 0xAD8B, 0xCDAE, 0xAD8C, 0xCDAF,\n\t0xAD8D, 0xCDB1, 0xAD8E, 0xCDB2, 0xAD8F, 0xCDB3, 0xAD90, 0xCDB4,\n\t0xAD91, 0xCDB5, 0xAD92, 0xCDB6, 0xAD93, 0xCDB7, 0xAD94, 0xCDB8,\n\t0xAD95, 0xCDB9, 0xAD96, 0xCDBA, 0xAD97, 0xCDBB, 0xAD98, 0xCDBC,\n\t0xAD99, 0xCDBD, 0xAD9A, 0xCDBE, 0xAD9B, 0xCDBF, 0xAD9C, 0xCDC0,\n\t0xAD9D, 0xCDC1, 0xAD9E, 0xCDC2, 0xAD9F, 0xCDC3, 0xADA0, 0xCDC5,\n\t0xAE41, 0xCDC6, 0xAE42, 0xCDC7, 0xAE43, 0xCDC8, 0xAE44, 0xCDC9,\n\t0xAE45, 0xCDCA, 0xAE46, 0xCDCB, 0xAE47, 0xCDCD, 0xAE48, 0xCDCE,\n\t0xAE49, 0xCDCF, 0xAE4A, 0xCDD1, 0xAE4B, 0xCDD2, 0xAE4C, 0xCDD3,\n\t0xAE4D, 0xCDD4, 0xAE4E, 0xCDD5, 0xAE4F, 0xCDD6, 0xAE50, 0xCDD7,\n\t0xAE51, 0xCDD8, 0xAE52, 0xCDD9, 0xAE53, 0xCDDA, 0xAE54, 0xCDDB,\n\t0xAE55, 0xCDDC, 0xAE56, 0xCDDD, 0xAE57, 0xCDDE, 0xAE58, 0xCDDF,\n\t0xAE59, 0xCDE0, 0xAE5A, 0xCDE1, 0xAE61, 0xCDE2, 0xAE62, 0xCDE3,\n\t0xAE63, 0xCDE4, 0xAE64, 0xCDE5, 0xAE65, 0xCDE6, 0xAE66, 0xCDE7,\n\t0xAE67, 0xCDE9, 0xAE68, 0xCDEA, 0xAE69, 0xCDEB, 0xAE6A, 0xCDED,\n\t0xAE6B, 0xCDEE, 0xAE6C, 0xCDEF, 0xAE6D, 0xCDF1, 0xAE6E, 0xCDF2,\n\t0xAE6F, 0xCDF3, 0xAE70, 0xCDF4, 0xAE71, 0xCDF5, 0xAE72, 0xCDF6,\n\t0xAE73, 0xCDF7, 0xAE74, 0xCDFA, 0xAE75, 0xCDFC, 0xAE76, 0xCDFE,\n\t0xAE77, 0xCDFF, 0xAE78, 0xCE00, 0xAE79, 0xCE01, 0xAE7A, 0xCE02,\n\t0xAE81, 0xCE03, 0xAE82, 0xCE05, 0xAE83, 0xCE06, 0xAE84, 0xCE07,\n\t0xAE85, 0xCE09, 0xAE86, 0xCE0A, 0xAE87, 0xCE0B, 0xAE88, 0xCE0D,\n\t0xAE89, 0xCE0E, 0xAE8A, 0xCE0F, 0xAE8B, 0xCE10, 0xAE8C, 0xCE11,\n\t0xAE8D, 0xCE12, 0xAE8E, 0xCE13, 0xAE8F, 0xCE15, 0xAE90, 0xCE16,\n\t0xAE91, 0xCE17, 0xAE92, 0xCE18, 0xAE93, 0xCE1A, 0xAE94, 0xCE1B,\n\t0xAE95, 0xCE1C, 0xAE96, 0xCE1D, 0xAE97, 0xCE1E, 0xAE98, 0xCE1F,\n\t0xAE99, 0xCE22, 0xAE9A, 0xCE23, 0xAE9B, 0xCE25, 0xAE9C, 0xCE26,\n\t0xAE9D, 0xCE27, 0xAE9E, 0xCE29, 0xAE9F, 0xCE2A, 0xAEA0, 0xCE2B,\n\t0xAF41, 0xCE2C, 0xAF42, 0xCE2D, 0xAF43, 0xCE2E, 0xAF44, 0xCE2F,\n\t0xAF45, 0xCE32, 0xAF46, 0xCE34, 0xAF47, 0xCE36, 0xAF48, 0xCE37,\n\t0xAF49, 0xCE38, 0xAF4A, 0xCE39, 0xAF4B, 0xCE3A, 0xAF4C, 0xCE3B,\n\t0xAF4D, 0xCE3C, 0xAF4E, 0xCE3D, 0xAF4F, 0xCE3E, 0xAF50, 0xCE3F,\n\t0xAF51, 0xCE40, 0xAF52, 0xCE41, 0xAF53, 0xCE42, 0xAF54, 0xCE43,\n\t0xAF55, 0xCE44, 0xAF56, 0xCE45, 0xAF57, 0xCE46, 0xAF58, 0xCE47,\n\t0xAF59, 0xCE48, 0xAF5A, 0xCE49, 0xAF61, 0xCE4A, 0xAF62, 0xCE4B,\n\t0xAF63, 0xCE4C, 0xAF64, 0xCE4D, 0xAF65, 0xCE4E, 0xAF66, 0xCE4F,\n\t0xAF67, 0xCE50, 0xAF68, 0xCE51, 0xAF69, 0xCE52, 0xAF6A, 0xCE53,\n\t0xAF6B, 0xCE54, 0xAF6C, 0xCE55, 0xAF6D, 0xCE56, 0xAF6E, 0xCE57,\n\t0xAF6F, 0xCE5A, 0xAF70, 0xCE5B, 0xAF71, 0xCE5D, 0xAF72, 0xCE5E,\n\t0xAF73, 0xCE62, 0xAF74, 0xCE63, 0xAF75, 0xCE64, 0xAF76, 0xCE65,\n\t0xAF77, 0xCE66, 0xAF78, 0xCE67, 0xAF79, 0xCE6A, 0xAF7A, 0xCE6C,\n\t0xAF81, 0xCE6E, 0xAF82, 0xCE6F, 0xAF83, 0xCE70, 0xAF84, 0xCE71,\n\t0xAF85, 0xCE72, 0xAF86, 0xCE73, 0xAF87, 0xCE76, 0xAF88, 0xCE77,\n\t0xAF89, 0xCE79, 0xAF8A, 0xCE7A, 0xAF8B, 0xCE7B, 0xAF8C, 0xCE7D,\n\t0xAF8D, 0xCE7E, 0xAF8E, 0xCE7F, 0xAF8F, 0xCE80, 0xAF90, 0xCE81,\n\t0xAF91, 0xCE82, 0xAF92, 0xCE83, 0xAF93, 0xCE86, 0xAF94, 0xCE88,\n\t0xAF95, 0xCE8A, 0xAF96, 0xCE8B, 0xAF97, 0xCE8C, 0xAF98, 0xCE8D,\n\t0xAF99, 0xCE8E, 0xAF9A, 0xCE8F, 0xAF9B, 0xCE92, 0xAF9C, 0xCE93,\n\t0xAF9D, 0xCE95, 0xAF9E, 0xCE96, 0xAF9F, 0xCE97, 0xAFA0, 0xCE99,\n\t0xB041, 0xCE9A, 0xB042, 0xCE9B, 0xB043, 0xCE9C, 0xB044, 0xCE9D,\n\t0xB045, 0xCE9E, 0xB046, 0xCE9F, 0xB047, 0xCEA2, 0xB048, 0xCEA6,\n\t0xB049, 0xCEA7, 0xB04A, 0xCEA8, 0xB04B, 0xCEA9, 0xB04C, 0xCEAA,\n\t0xB04D, 0xCEAB, 0xB04E, 0xCEAE, 0xB04F, 0xCEAF, 0xB050, 0xCEB0,\n\t0xB051, 0xCEB1, 0xB052, 0xCEB2, 0xB053, 0xCEB3, 0xB054, 0xCEB4,\n\t0xB055, 0xCEB5, 0xB056, 0xCEB6, 0xB057, 0xCEB7, 0xB058, 0xCEB8,\n\t0xB059, 0xCEB9, 0xB05A, 0xCEBA, 0xB061, 0xCEBB, 0xB062, 0xCEBC,\n\t0xB063, 0xCEBD, 0xB064, 0xCEBE, 0xB065, 0xCEBF, 0xB066, 0xCEC0,\n\t0xB067, 0xCEC2, 0xB068, 0xCEC3, 0xB069, 0xCEC4, 0xB06A, 0xCEC5,\n\t0xB06B, 0xCEC6, 0xB06C, 0xCEC7, 0xB06D, 0xCEC8, 0xB06E, 0xCEC9,\n\t0xB06F, 0xCECA, 0xB070, 0xCECB, 0xB071, 0xCECC, 0xB072, 0xCECD,\n\t0xB073, 0xCECE, 0xB074, 0xCECF, 0xB075, 0xCED0, 0xB076, 0xCED1,\n\t0xB077, 0xCED2, 0xB078, 0xCED3, 0xB079, 0xCED4, 0xB07A, 0xCED5,\n\t0xB081, 0xCED6, 0xB082, 0xCED7, 0xB083, 0xCED8, 0xB084, 0xCED9,\n\t0xB085, 0xCEDA, 0xB086, 0xCEDB, 0xB087, 0xCEDC, 0xB088, 0xCEDD,\n\t0xB089, 0xCEDE, 0xB08A, 0xCEDF, 0xB08B, 0xCEE0, 0xB08C, 0xCEE1,\n\t0xB08D, 0xCEE2, 0xB08E, 0xCEE3, 0xB08F, 0xCEE6, 0xB090, 0xCEE7,\n\t0xB091, 0xCEE9, 0xB092, 0xCEEA, 0xB093, 0xCEED, 0xB094, 0xCEEE,\n\t0xB095, 0xCEEF, 0xB096, 0xCEF0, 0xB097, 0xCEF1, 0xB098, 0xCEF2,\n\t0xB099, 0xCEF3, 0xB09A, 0xCEF6, 0xB09B, 0xCEFA, 0xB09C, 0xCEFB,\n\t0xB09D, 0xCEFC, 0xB09E, 0xCEFD, 0xB09F, 0xCEFE, 0xB0A0, 0xCEFF,\n\t0xB0A1, 0xAC00, 0xB0A2, 0xAC01, 0xB0A3, 0xAC04, 0xB0A4, 0xAC07,\n\t0xB0A5, 0xAC08, 0xB0A6, 0xAC09, 0xB0A7, 0xAC0A, 0xB0A8, 0xAC10,\n\t0xB0A9, 0xAC11, 0xB0AA, 0xAC12, 0xB0AB, 0xAC13, 0xB0AC, 0xAC14,\n\t0xB0AD, 0xAC15, 0xB0AE, 0xAC16, 0xB0AF, 0xAC17, 0xB0B0, 0xAC19,\n\t0xB0B1, 0xAC1A, 0xB0B2, 0xAC1B, 0xB0B3, 0xAC1C, 0xB0B4, 0xAC1D,\n\t0xB0B5, 0xAC20, 0xB0B6, 0xAC24, 0xB0B7, 0xAC2C, 0xB0B8, 0xAC2D,\n\t0xB0B9, 0xAC2F, 0xB0BA, 0xAC30, 0xB0BB, 0xAC31, 0xB0BC, 0xAC38,\n\t0xB0BD, 0xAC39, 0xB0BE, 0xAC3C, 0xB0BF, 0xAC40, 0xB0C0, 0xAC4B,\n\t0xB0C1, 0xAC4D, 0xB0C2, 0xAC54, 0xB0C3, 0xAC58, 0xB0C4, 0xAC5C,\n\t0xB0C5, 0xAC70, 0xB0C6, 0xAC71, 0xB0C7, 0xAC74, 0xB0C8, 0xAC77,\n\t0xB0C9, 0xAC78, 0xB0CA, 0xAC7A, 0xB0CB, 0xAC80, 0xB0CC, 0xAC81,\n\t0xB0CD, 0xAC83, 0xB0CE, 0xAC84, 0xB0CF, 0xAC85, 0xB0D0, 0xAC86,\n\t0xB0D1, 0xAC89, 0xB0D2, 0xAC8A, 0xB0D3, 0xAC8B, 0xB0D4, 0xAC8C,\n\t0xB0D5, 0xAC90, 0xB0D6, 0xAC94, 0xB0D7, 0xAC9C, 0xB0D8, 0xAC9D,\n\t0xB0D9, 0xAC9F, 0xB0DA, 0xACA0, 0xB0DB, 0xACA1, 0xB0DC, 0xACA8,\n\t0xB0DD, 0xACA9, 0xB0DE, 0xACAA, 0xB0DF, 0xACAC, 0xB0E0, 0xACAF,\n\t0xB0E1, 0xACB0, 0xB0E2, 0xACB8, 0xB0E3, 0xACB9, 0xB0E4, 0xACBB,\n\t0xB0E5, 0xACBC, 0xB0E6, 0xACBD, 0xB0E7, 0xACC1, 0xB0E8, 0xACC4,\n\t0xB0E9, 0xACC8, 0xB0EA, 0xACCC, 0xB0EB, 0xACD5, 0xB0EC, 0xACD7,\n\t0xB0ED, 0xACE0, 0xB0EE, 0xACE1, 0xB0EF, 0xACE4, 0xB0F0, 0xACE7,\n\t0xB0F1, 0xACE8, 0xB0F2, 0xACEA, 0xB0F3, 0xACEC, 0xB0F4, 0xACEF,\n\t0xB0F5, 0xACF0, 0xB0F6, 0xACF1, 0xB0F7, 0xACF3, 0xB0F8, 0xACF5,\n\t0xB0F9, 0xACF6, 0xB0FA, 0xACFC, 0xB0FB, 0xACFD, 0xB0FC, 0xAD00,\n\t0xB0FD, 0xAD04, 0xB0FE, 0xAD06, 0xB141, 0xCF02, 0xB142, 0xCF03,\n\t0xB143, 0xCF05, 0xB144, 0xCF06, 0xB145, 0xCF07, 0xB146, 0xCF09,\n\t0xB147, 0xCF0A, 0xB148, 0xCF0B, 0xB149, 0xCF0C, 0xB14A, 0xCF0D,\n\t0xB14B, 0xCF0E, 0xB14C, 0xCF0F, 0xB14D, 0xCF12, 0xB14E, 0xCF14,\n\t0xB14F, 0xCF16, 0xB150, 0xCF17, 0xB151, 0xCF18, 0xB152, 0xCF19,\n\t0xB153, 0xCF1A, 0xB154, 0xCF1B, 0xB155, 0xCF1D, 0xB156, 0xCF1E,\n\t0xB157, 0xCF1F, 0xB158, 0xCF21, 0xB159, 0xCF22, 0xB15A, 0xCF23,\n\t0xB161, 0xCF25, 0xB162, 0xCF26, 0xB163, 0xCF27, 0xB164, 0xCF28,\n\t0xB165, 0xCF29, 0xB166, 0xCF2A, 0xB167, 0xCF2B, 0xB168, 0xCF2E,\n\t0xB169, 0xCF32, 0xB16A, 0xCF33, 0xB16B, 0xCF34, 0xB16C, 0xCF35,\n\t0xB16D, 0xCF36, 0xB16E, 0xCF37, 0xB16F, 0xCF39, 0xB170, 0xCF3A,\n\t0xB171, 0xCF3B, 0xB172, 0xCF3C, 0xB173, 0xCF3D, 0xB174, 0xCF3E,\n\t0xB175, 0xCF3F, 0xB176, 0xCF40, 0xB177, 0xCF41, 0xB178, 0xCF42,\n\t0xB179, 0xCF43, 0xB17A, 0xCF44, 0xB181, 0xCF45, 0xB182, 0xCF46,\n\t0xB183, 0xCF47, 0xB184, 0xCF48, 0xB185, 0xCF49, 0xB186, 0xCF4A,\n\t0xB187, 0xCF4B, 0xB188, 0xCF4C, 0xB189, 0xCF4D, 0xB18A, 0xCF4E,\n\t0xB18B, 0xCF4F, 0xB18C, 0xCF50, 0xB18D, 0xCF51, 0xB18E, 0xCF52,\n\t0xB18F, 0xCF53, 0xB190, 0xCF56, 0xB191, 0xCF57, 0xB192, 0xCF59,\n\t0xB193, 0xCF5A, 0xB194, 0xCF5B, 0xB195, 0xCF5D, 0xB196, 0xCF5E,\n\t0xB197, 0xCF5F, 0xB198, 0xCF60, 0xB199, 0xCF61, 0xB19A, 0xCF62,\n\t0xB19B, 0xCF63, 0xB19C, 0xCF66, 0xB19D, 0xCF68, 0xB19E, 0xCF6A,\n\t0xB19F, 0xCF6B, 0xB1A0, 0xCF6C, 0xB1A1, 0xAD0C, 0xB1A2, 0xAD0D,\n\t0xB1A3, 0xAD0F, 0xB1A4, 0xAD11, 0xB1A5, 0xAD18, 0xB1A6, 0xAD1C,\n\t0xB1A7, 0xAD20, 0xB1A8, 0xAD29, 0xB1A9, 0xAD2C, 0xB1AA, 0xAD2D,\n\t0xB1AB, 0xAD34, 0xB1AC, 0xAD35, 0xB1AD, 0xAD38, 0xB1AE, 0xAD3C,\n\t0xB1AF, 0xAD44, 0xB1B0, 0xAD45, 0xB1B1, 0xAD47, 0xB1B2, 0xAD49,\n\t0xB1B3, 0xAD50, 0xB1B4, 0xAD54, 0xB1B5, 0xAD58, 0xB1B6, 0xAD61,\n\t0xB1B7, 0xAD63, 0xB1B8, 0xAD6C, 0xB1B9, 0xAD6D, 0xB1BA, 0xAD70,\n\t0xB1BB, 0xAD73, 0xB1BC, 0xAD74, 0xB1BD, 0xAD75, 0xB1BE, 0xAD76,\n\t0xB1BF, 0xAD7B, 0xB1C0, 0xAD7C, 0xB1C1, 0xAD7D, 0xB1C2, 0xAD7F,\n\t0xB1C3, 0xAD81, 0xB1C4, 0xAD82, 0xB1C5, 0xAD88, 0xB1C6, 0xAD89,\n\t0xB1C7, 0xAD8C, 0xB1C8, 0xAD90, 0xB1C9, 0xAD9C, 0xB1CA, 0xAD9D,\n\t0xB1CB, 0xADA4, 0xB1CC, 0xADB7, 0xB1CD, 0xADC0, 0xB1CE, 0xADC1,\n\t0xB1CF, 0xADC4, 0xB1D0, 0xADC8, 0xB1D1, 0xADD0, 0xB1D2, 0xADD1,\n\t0xB1D3, 0xADD3, 0xB1D4, 0xADDC, 0xB1D5, 0xADE0, 0xB1D6, 0xADE4,\n\t0xB1D7, 0xADF8, 0xB1D8, 0xADF9, 0xB1D9, 0xADFC, 0xB1DA, 0xADFF,\n\t0xB1DB, 0xAE00, 0xB1DC, 0xAE01, 0xB1DD, 0xAE08, 0xB1DE, 0xAE09,\n\t0xB1DF, 0xAE0B, 0xB1E0, 0xAE0D, 0xB1E1, 0xAE14, 0xB1E2, 0xAE30,\n\t0xB1E3, 0xAE31, 0xB1E4, 0xAE34, 0xB1E5, 0xAE37, 0xB1E6, 0xAE38,\n\t0xB1E7, 0xAE3A, 0xB1E8, 0xAE40, 0xB1E9, 0xAE41, 0xB1EA, 0xAE43,\n\t0xB1EB, 0xAE45, 0xB1EC, 0xAE46, 0xB1ED, 0xAE4A, 0xB1EE, 0xAE4C,\n\t0xB1EF, 0xAE4D, 0xB1F0, 0xAE4E, 0xB1F1, 0xAE50, 0xB1F2, 0xAE54,\n\t0xB1F3, 0xAE56, 0xB1F4, 0xAE5C, 0xB1F5, 0xAE5D, 0xB1F6, 0xAE5F,\n\t0xB1F7, 0xAE60, 0xB1F8, 0xAE61, 0xB1F9, 0xAE65, 0xB1FA, 0xAE68,\n\t0xB1FB, 0xAE69, 0xB1FC, 0xAE6C, 0xB1FD, 0xAE70, 0xB1FE, 0xAE78,\n\t0xB241, 0xCF6D, 0xB242, 0xCF6E, 0xB243, 0xCF6F, 0xB244, 0xCF72,\n\t0xB245, 0xCF73, 0xB246, 0xCF75, 0xB247, 0xCF76, 0xB248, 0xCF77,\n\t0xB249, 0xCF79, 0xB24A, 0xCF7A, 0xB24B, 0xCF7B, 0xB24C, 0xCF7C,\n\t0xB24D, 0xCF7D, 0xB24E, 0xCF7E, 0xB24F, 0xCF7F, 0xB250, 0xCF81,\n\t0xB251, 0xCF82, 0xB252, 0xCF83, 0xB253, 0xCF84, 0xB254, 0xCF86,\n\t0xB255, 0xCF87, 0xB256, 0xCF88, 0xB257, 0xCF89, 0xB258, 0xCF8A,\n\t0xB259, 0xCF8B, 0xB25A, 0xCF8D, 0xB261, 0xCF8E, 0xB262, 0xCF8F,\n\t0xB263, 0xCF90, 0xB264, 0xCF91, 0xB265, 0xCF92, 0xB266, 0xCF93,\n\t0xB267, 0xCF94, 0xB268, 0xCF95, 0xB269, 0xCF96, 0xB26A, 0xCF97,\n\t0xB26B, 0xCF98, 0xB26C, 0xCF99, 0xB26D, 0xCF9A, 0xB26E, 0xCF9B,\n\t0xB26F, 0xCF9C, 0xB270, 0xCF9D, 0xB271, 0xCF9E, 0xB272, 0xCF9F,\n\t0xB273, 0xCFA0, 0xB274, 0xCFA2, 0xB275, 0xCFA3, 0xB276, 0xCFA4,\n\t0xB277, 0xCFA5, 0xB278, 0xCFA6, 0xB279, 0xCFA7, 0xB27A, 0xCFA9,\n\t0xB281, 0xCFAA, 0xB282, 0xCFAB, 0xB283, 0xCFAC, 0xB284, 0xCFAD,\n\t0xB285, 0xCFAE, 0xB286, 0xCFAF, 0xB287, 0xCFB1, 0xB288, 0xCFB2,\n\t0xB289, 0xCFB3, 0xB28A, 0xCFB4, 0xB28B, 0xCFB5, 0xB28C, 0xCFB6,\n\t0xB28D, 0xCFB7, 0xB28E, 0xCFB8, 0xB28F, 0xCFB9, 0xB290, 0xCFBA,\n\t0xB291, 0xCFBB, 0xB292, 0xCFBC, 0xB293, 0xCFBD, 0xB294, 0xCFBE,\n\t0xB295, 0xCFBF, 0xB296, 0xCFC0, 0xB297, 0xCFC1, 0xB298, 0xCFC2,\n\t0xB299, 0xCFC3, 0xB29A, 0xCFC5, 0xB29B, 0xCFC6, 0xB29C, 0xCFC7,\n\t0xB29D, 0xCFC8, 0xB29E, 0xCFC9, 0xB29F, 0xCFCA, 0xB2A0, 0xCFCB,\n\t0xB2A1, 0xAE79, 0xB2A2, 0xAE7B, 0xB2A3, 0xAE7C, 0xB2A4, 0xAE7D,\n\t0xB2A5, 0xAE84, 0xB2A6, 0xAE85, 0xB2A7, 0xAE8C, 0xB2A8, 0xAEBC,\n\t0xB2A9, 0xAEBD, 0xB2AA, 0xAEBE, 0xB2AB, 0xAEC0, 0xB2AC, 0xAEC4,\n\t0xB2AD, 0xAECC, 0xB2AE, 0xAECD, 0xB2AF, 0xAECF, 0xB2B0, 0xAED0,\n\t0xB2B1, 0xAED1, 0xB2B2, 0xAED8, 0xB2B3, 0xAED9, 0xB2B4, 0xAEDC,\n\t0xB2B5, 0xAEE8, 0xB2B6, 0xAEEB, 0xB2B7, 0xAEED, 0xB2B8, 0xAEF4,\n\t0xB2B9, 0xAEF8, 0xB2BA, 0xAEFC, 0xB2BB, 0xAF07, 0xB2BC, 0xAF08,\n\t0xB2BD, 0xAF0D, 0xB2BE, 0xAF10, 0xB2BF, 0xAF2C, 0xB2C0, 0xAF2D,\n\t0xB2C1, 0xAF30, 0xB2C2, 0xAF32, 0xB2C3, 0xAF34, 0xB2C4, 0xAF3C,\n\t0xB2C5, 0xAF3D, 0xB2C6, 0xAF3F, 0xB2C7, 0xAF41, 0xB2C8, 0xAF42,\n\t0xB2C9, 0xAF43, 0xB2CA, 0xAF48, 0xB2CB, 0xAF49, 0xB2CC, 0xAF50,\n\t0xB2CD, 0xAF5C, 0xB2CE, 0xAF5D, 0xB2CF, 0xAF64, 0xB2D0, 0xAF65,\n\t0xB2D1, 0xAF79, 0xB2D2, 0xAF80, 0xB2D3, 0xAF84, 0xB2D4, 0xAF88,\n\t0xB2D5, 0xAF90, 0xB2D6, 0xAF91, 0xB2D7, 0xAF95, 0xB2D8, 0xAF9C,\n\t0xB2D9, 0xAFB8, 0xB2DA, 0xAFB9, 0xB2DB, 0xAFBC, 0xB2DC, 0xAFC0,\n\t0xB2DD, 0xAFC7, 0xB2DE, 0xAFC8, 0xB2DF, 0xAFC9, 0xB2E0, 0xAFCB,\n\t0xB2E1, 0xAFCD, 0xB2E2, 0xAFCE, 0xB2E3, 0xAFD4, 0xB2E4, 0xAFDC,\n\t0xB2E5, 0xAFE8, 0xB2E6, 0xAFE9, 0xB2E7, 0xAFF0, 0xB2E8, 0xAFF1,\n\t0xB2E9, 0xAFF4, 0xB2EA, 0xAFF8, 0xB2EB, 0xB000, 0xB2EC, 0xB001,\n\t0xB2ED, 0xB004, 0xB2EE, 0xB00C, 0xB2EF, 0xB010, 0xB2F0, 0xB014,\n\t0xB2F1, 0xB01C, 0xB2F2, 0xB01D, 0xB2F3, 0xB028, 0xB2F4, 0xB044,\n\t0xB2F5, 0xB045, 0xB2F6, 0xB048, 0xB2F7, 0xB04A, 0xB2F8, 0xB04C,\n\t0xB2F9, 0xB04E, 0xB2FA, 0xB053, 0xB2FB, 0xB054, 0xB2FC, 0xB055,\n\t0xB2FD, 0xB057, 0xB2FE, 0xB059, 0xB341, 0xCFCC, 0xB342, 0xCFCD,\n\t0xB343, 0xCFCE, 0xB344, 0xCFCF, 0xB345, 0xCFD0, 0xB346, 0xCFD1,\n\t0xB347, 0xCFD2, 0xB348, 0xCFD3, 0xB349, 0xCFD4, 0xB34A, 0xCFD5,\n\t0xB34B, 0xCFD6, 0xB34C, 0xCFD7, 0xB34D, 0xCFD8, 0xB34E, 0xCFD9,\n\t0xB34F, 0xCFDA, 0xB350, 0xCFDB, 0xB351, 0xCFDC, 0xB352, 0xCFDD,\n\t0xB353, 0xCFDE, 0xB354, 0xCFDF, 0xB355, 0xCFE2, 0xB356, 0xCFE3,\n\t0xB357, 0xCFE5, 0xB358, 0xCFE6, 0xB359, 0xCFE7, 0xB35A, 0xCFE9,\n\t0xB361, 0xCFEA, 0xB362, 0xCFEB, 0xB363, 0xCFEC, 0xB364, 0xCFED,\n\t0xB365, 0xCFEE, 0xB366, 0xCFEF, 0xB367, 0xCFF2, 0xB368, 0xCFF4,\n\t0xB369, 0xCFF6, 0xB36A, 0xCFF7, 0xB36B, 0xCFF8, 0xB36C, 0xCFF9,\n\t0xB36D, 0xCFFA, 0xB36E, 0xCFFB, 0xB36F, 0xCFFD, 0xB370, 0xCFFE,\n\t0xB371, 0xCFFF, 0xB372, 0xD001, 0xB373, 0xD002, 0xB374, 0xD003,\n\t0xB375, 0xD005, 0xB376, 0xD006, 0xB377, 0xD007, 0xB378, 0xD008,\n\t0xB379, 0xD009, 0xB37A, 0xD00A, 0xB381, 0xD00B, 0xB382, 0xD00C,\n\t0xB383, 0xD00D, 0xB384, 0xD00E, 0xB385, 0xD00F, 0xB386, 0xD010,\n\t0xB387, 0xD012, 0xB388, 0xD013, 0xB389, 0xD014, 0xB38A, 0xD015,\n\t0xB38B, 0xD016, 0xB38C, 0xD017, 0xB38D, 0xD019, 0xB38E, 0xD01A,\n\t0xB38F, 0xD01B, 0xB390, 0xD01C, 0xB391, 0xD01D, 0xB392, 0xD01E,\n\t0xB393, 0xD01F, 0xB394, 0xD020, 0xB395, 0xD021, 0xB396, 0xD022,\n\t0xB397, 0xD023, 0xB398, 0xD024, 0xB399, 0xD025, 0xB39A, 0xD026,\n\t0xB39B, 0xD027, 0xB39C, 0xD028, 0xB39D, 0xD029, 0xB39E, 0xD02A,\n\t0xB39F, 0xD02B, 0xB3A0, 0xD02C, 0xB3A1, 0xB05D, 0xB3A2, 0xB07C,\n\t0xB3A3, 0xB07D, 0xB3A4, 0xB080, 0xB3A5, 0xB084, 0xB3A6, 0xB08C,\n\t0xB3A7, 0xB08D, 0xB3A8, 0xB08F, 0xB3A9, 0xB091, 0xB3AA, 0xB098,\n\t0xB3AB, 0xB099, 0xB3AC, 0xB09A, 0xB3AD, 0xB09C, 0xB3AE, 0xB09F,\n\t0xB3AF, 0xB0A0, 0xB3B0, 0xB0A1, 0xB3B1, 0xB0A2, 0xB3B2, 0xB0A8,\n\t0xB3B3, 0xB0A9, 0xB3B4, 0xB0AB, 0xB3B5, 0xB0AC, 0xB3B6, 0xB0AD,\n\t0xB3B7, 0xB0AE, 0xB3B8, 0xB0AF, 0xB3B9, 0xB0B1, 0xB3BA, 0xB0B3,\n\t0xB3BB, 0xB0B4, 0xB3BC, 0xB0B5, 0xB3BD, 0xB0B8, 0xB3BE, 0xB0BC,\n\t0xB3BF, 0xB0C4, 0xB3C0, 0xB0C5, 0xB3C1, 0xB0C7, 0xB3C2, 0xB0C8,\n\t0xB3C3, 0xB0C9, 0xB3C4, 0xB0D0, 0xB3C5, 0xB0D1, 0xB3C6, 0xB0D4,\n\t0xB3C7, 0xB0D8, 0xB3C8, 0xB0E0, 0xB3C9, 0xB0E5, 0xB3CA, 0xB108,\n\t0xB3CB, 0xB109, 0xB3CC, 0xB10B, 0xB3CD, 0xB10C, 0xB3CE, 0xB110,\n\t0xB3CF, 0xB112, 0xB3D0, 0xB113, 0xB3D1, 0xB118, 0xB3D2, 0xB119,\n\t0xB3D3, 0xB11B, 0xB3D4, 0xB11C, 0xB3D5, 0xB11D, 0xB3D6, 0xB123,\n\t0xB3D7, 0xB124, 0xB3D8, 0xB125, 0xB3D9, 0xB128, 0xB3DA, 0xB12C,\n\t0xB3DB, 0xB134, 0xB3DC, 0xB135, 0xB3DD, 0xB137, 0xB3DE, 0xB138,\n\t0xB3DF, 0xB139, 0xB3E0, 0xB140, 0xB3E1, 0xB141, 0xB3E2, 0xB144,\n\t0xB3E3, 0xB148, 0xB3E4, 0xB150, 0xB3E5, 0xB151, 0xB3E6, 0xB154,\n\t0xB3E7, 0xB155, 0xB3E8, 0xB158, 0xB3E9, 0xB15C, 0xB3EA, 0xB160,\n\t0xB3EB, 0xB178, 0xB3EC, 0xB179, 0xB3ED, 0xB17C, 0xB3EE, 0xB180,\n\t0xB3EF, 0xB182, 0xB3F0, 0xB188, 0xB3F1, 0xB189, 0xB3F2, 0xB18B,\n\t0xB3F3, 0xB18D, 0xB3F4, 0xB192, 0xB3F5, 0xB193, 0xB3F6, 0xB194,\n\t0xB3F7, 0xB198, 0xB3F8, 0xB19C, 0xB3F9, 0xB1A8, 0xB3FA, 0xB1CC,\n\t0xB3FB, 0xB1D0, 0xB3FC, 0xB1D4, 0xB3FD, 0xB1DC, 0xB3FE, 0xB1DD,\n\t0xB441, 0xD02E, 0xB442, 0xD02F, 0xB443, 0xD030, 0xB444, 0xD031,\n\t0xB445, 0xD032, 0xB446, 0xD033, 0xB447, 0xD036, 0xB448, 0xD037,\n\t0xB449, 0xD039, 0xB44A, 0xD03A, 0xB44B, 0xD03B, 0xB44C, 0xD03D,\n\t0xB44D, 0xD03E, 0xB44E, 0xD03F, 0xB44F, 0xD040, 0xB450, 0xD041,\n\t0xB451, 0xD042, 0xB452, 0xD043, 0xB453, 0xD046, 0xB454, 0xD048,\n\t0xB455, 0xD04A, 0xB456, 0xD04B, 0xB457, 0xD04C, 0xB458, 0xD04D,\n\t0xB459, 0xD04E, 0xB45A, 0xD04F, 0xB461, 0xD051, 0xB462, 0xD052,\n\t0xB463, 0xD053, 0xB464, 0xD055, 0xB465, 0xD056, 0xB466, 0xD057,\n\t0xB467, 0xD059, 0xB468, 0xD05A, 0xB469, 0xD05B, 0xB46A, 0xD05C,\n\t0xB46B, 0xD05D, 0xB46C, 0xD05E, 0xB46D, 0xD05F, 0xB46E, 0xD061,\n\t0xB46F, 0xD062, 0xB470, 0xD063, 0xB471, 0xD064, 0xB472, 0xD065,\n\t0xB473, 0xD066, 0xB474, 0xD067, 0xB475, 0xD068, 0xB476, 0xD069,\n\t0xB477, 0xD06A, 0xB478, 0xD06B, 0xB479, 0xD06E, 0xB47A, 0xD06F,\n\t0xB481, 0xD071, 0xB482, 0xD072, 0xB483, 0xD073, 0xB484, 0xD075,\n\t0xB485, 0xD076, 0xB486, 0xD077, 0xB487, 0xD078, 0xB488, 0xD079,\n\t0xB489, 0xD07A, 0xB48A, 0xD07B, 0xB48B, 0xD07E, 0xB48C, 0xD07F,\n\t0xB48D, 0xD080, 0xB48E, 0xD082, 0xB48F, 0xD083, 0xB490, 0xD084,\n\t0xB491, 0xD085, 0xB492, 0xD086, 0xB493, 0xD087, 0xB494, 0xD088,\n\t0xB495, 0xD089, 0xB496, 0xD08A, 0xB497, 0xD08B, 0xB498, 0xD08C,\n\t0xB499, 0xD08D, 0xB49A, 0xD08E, 0xB49B, 0xD08F, 0xB49C, 0xD090,\n\t0xB49D, 0xD091, 0xB49E, 0xD092, 0xB49F, 0xD093, 0xB4A0, 0xD094,\n\t0xB4A1, 0xB1DF, 0xB4A2, 0xB1E8, 0xB4A3, 0xB1E9, 0xB4A4, 0xB1EC,\n\t0xB4A5, 0xB1F0, 0xB4A6, 0xB1F9, 0xB4A7, 0xB1FB, 0xB4A8, 0xB1FD,\n\t0xB4A9, 0xB204, 0xB4AA, 0xB205, 0xB4AB, 0xB208, 0xB4AC, 0xB20B,\n\t0xB4AD, 0xB20C, 0xB4AE, 0xB214, 0xB4AF, 0xB215, 0xB4B0, 0xB217,\n\t0xB4B1, 0xB219, 0xB4B2, 0xB220, 0xB4B3, 0xB234, 0xB4B4, 0xB23C,\n\t0xB4B5, 0xB258, 0xB4B6, 0xB25C, 0xB4B7, 0xB260, 0xB4B8, 0xB268,\n\t0xB4B9, 0xB269, 0xB4BA, 0xB274, 0xB4BB, 0xB275, 0xB4BC, 0xB27C,\n\t0xB4BD, 0xB284, 0xB4BE, 0xB285, 0xB4BF, 0xB289, 0xB4C0, 0xB290,\n\t0xB4C1, 0xB291, 0xB4C2, 0xB294, 0xB4C3, 0xB298, 0xB4C4, 0xB299,\n\t0xB4C5, 0xB29A, 0xB4C6, 0xB2A0, 0xB4C7, 0xB2A1, 0xB4C8, 0xB2A3,\n\t0xB4C9, 0xB2A5, 0xB4CA, 0xB2A6, 0xB4CB, 0xB2AA, 0xB4CC, 0xB2AC,\n\t0xB4CD, 0xB2B0, 0xB4CE, 0xB2B4, 0xB4CF, 0xB2C8, 0xB4D0, 0xB2C9,\n\t0xB4D1, 0xB2CC, 0xB4D2, 0xB2D0, 0xB4D3, 0xB2D2, 0xB4D4, 0xB2D8,\n\t0xB4D5, 0xB2D9, 0xB4D6, 0xB2DB, 0xB4D7, 0xB2DD, 0xB4D8, 0xB2E2,\n\t0xB4D9, 0xB2E4, 0xB4DA, 0xB2E5, 0xB4DB, 0xB2E6, 0xB4DC, 0xB2E8,\n\t0xB4DD, 0xB2EB, 0xB4DE, 0xB2EC, 0xB4DF, 0xB2ED, 0xB4E0, 0xB2EE,\n\t0xB4E1, 0xB2EF, 0xB4E2, 0xB2F3, 0xB4E3, 0xB2F4, 0xB4E4, 0xB2F5,\n\t0xB4E5, 0xB2F7, 0xB4E6, 0xB2F8, 0xB4E7, 0xB2F9, 0xB4E8, 0xB2FA,\n\t0xB4E9, 0xB2FB, 0xB4EA, 0xB2FF, 0xB4EB, 0xB300, 0xB4EC, 0xB301,\n\t0xB4ED, 0xB304, 0xB4EE, 0xB308, 0xB4EF, 0xB310, 0xB4F0, 0xB311,\n\t0xB4F1, 0xB313, 0xB4F2, 0xB314, 0xB4F3, 0xB315, 0xB4F4, 0xB31C,\n\t0xB4F5, 0xB354, 0xB4F6, 0xB355, 0xB4F7, 0xB356, 0xB4F8, 0xB358,\n\t0xB4F9, 0xB35B, 0xB4FA, 0xB35C, 0xB4FB, 0xB35E, 0xB4FC, 0xB35F,\n\t0xB4FD, 0xB364, 0xB4FE, 0xB365, 0xB541, 0xD095, 0xB542, 0xD096,\n\t0xB543, 0xD097, 0xB544, 0xD098, 0xB545, 0xD099, 0xB546, 0xD09A,\n\t0xB547, 0xD09B, 0xB548, 0xD09C, 0xB549, 0xD09D, 0xB54A, 0xD09E,\n\t0xB54B, 0xD09F, 0xB54C, 0xD0A0, 0xB54D, 0xD0A1, 0xB54E, 0xD0A2,\n\t0xB54F, 0xD0A3, 0xB550, 0xD0A6, 0xB551, 0xD0A7, 0xB552, 0xD0A9,\n\t0xB553, 0xD0AA, 0xB554, 0xD0AB, 0xB555, 0xD0AD, 0xB556, 0xD0AE,\n\t0xB557, 0xD0AF, 0xB558, 0xD0B0, 0xB559, 0xD0B1, 0xB55A, 0xD0B2,\n\t0xB561, 0xD0B3, 0xB562, 0xD0B6, 0xB563, 0xD0B8, 0xB564, 0xD0BA,\n\t0xB565, 0xD0BB, 0xB566, 0xD0BC, 0xB567, 0xD0BD, 0xB568, 0xD0BE,\n\t0xB569, 0xD0BF, 0xB56A, 0xD0C2, 0xB56B, 0xD0C3, 0xB56C, 0xD0C5,\n\t0xB56D, 0xD0C6, 0xB56E, 0xD0C7, 0xB56F, 0xD0CA, 0xB570, 0xD0CB,\n\t0xB571, 0xD0CC, 0xB572, 0xD0CD, 0xB573, 0xD0CE, 0xB574, 0xD0CF,\n\t0xB575, 0xD0D2, 0xB576, 0xD0D6, 0xB577, 0xD0D7, 0xB578, 0xD0D8,\n\t0xB579, 0xD0D9, 0xB57A, 0xD0DA, 0xB581, 0xD0DB, 0xB582, 0xD0DE,\n\t0xB583, 0xD0DF, 0xB584, 0xD0E1, 0xB585, 0xD0E2, 0xB586, 0xD0E3,\n\t0xB587, 0xD0E5, 0xB588, 0xD0E6, 0xB589, 0xD0E7, 0xB58A, 0xD0E8,\n\t0xB58B, 0xD0E9, 0xB58C, 0xD0EA, 0xB58D, 0xD0EB, 0xB58E, 0xD0EE,\n\t0xB58F, 0xD0F2, 0xB590, 0xD0F3, 0xB591, 0xD0F4, 0xB592, 0xD0F5,\n\t0xB593, 0xD0F6, 0xB594, 0xD0F7, 0xB595, 0xD0F9, 0xB596, 0xD0FA,\n\t0xB597, 0xD0FB, 0xB598, 0xD0FC, 0xB599, 0xD0FD, 0xB59A, 0xD0FE,\n\t0xB59B, 0xD0FF, 0xB59C, 0xD100, 0xB59D, 0xD101, 0xB59E, 0xD102,\n\t0xB59F, 0xD103, 0xB5A0, 0xD104, 0xB5A1, 0xB367, 0xB5A2, 0xB369,\n\t0xB5A3, 0xB36B, 0xB5A4, 0xB36E, 0xB5A5, 0xB370, 0xB5A6, 0xB371,\n\t0xB5A7, 0xB374, 0xB5A8, 0xB378, 0xB5A9, 0xB380, 0xB5AA, 0xB381,\n\t0xB5AB, 0xB383, 0xB5AC, 0xB384, 0xB5AD, 0xB385, 0xB5AE, 0xB38C,\n\t0xB5AF, 0xB390, 0xB5B0, 0xB394, 0xB5B1, 0xB3A0, 0xB5B2, 0xB3A1,\n\t0xB5B3, 0xB3A8, 0xB5B4, 0xB3AC, 0xB5B5, 0xB3C4, 0xB5B6, 0xB3C5,\n\t0xB5B7, 0xB3C8, 0xB5B8, 0xB3CB, 0xB5B9, 0xB3CC, 0xB5BA, 0xB3CE,\n\t0xB5BB, 0xB3D0, 0xB5BC, 0xB3D4, 0xB5BD, 0xB3D5, 0xB5BE, 0xB3D7,\n\t0xB5BF, 0xB3D9, 0xB5C0, 0xB3DB, 0xB5C1, 0xB3DD, 0xB5C2, 0xB3E0,\n\t0xB5C3, 0xB3E4, 0xB5C4, 0xB3E8, 0xB5C5, 0xB3FC, 0xB5C6, 0xB410,\n\t0xB5C7, 0xB418, 0xB5C8, 0xB41C, 0xB5C9, 0xB420, 0xB5CA, 0xB428,\n\t0xB5CB, 0xB429, 0xB5CC, 0xB42B, 0xB5CD, 0xB434, 0xB5CE, 0xB450,\n\t0xB5CF, 0xB451, 0xB5D0, 0xB454, 0xB5D1, 0xB458, 0xB5D2, 0xB460,\n\t0xB5D3, 0xB461, 0xB5D4, 0xB463, 0xB5D5, 0xB465, 0xB5D6, 0xB46C,\n\t0xB5D7, 0xB480, 0xB5D8, 0xB488, 0xB5D9, 0xB49D, 0xB5DA, 0xB4A4,\n\t0xB5DB, 0xB4A8, 0xB5DC, 0xB4AC, 0xB5DD, 0xB4B5, 0xB5DE, 0xB4B7,\n\t0xB5DF, 0xB4B9, 0xB5E0, 0xB4C0, 0xB5E1, 0xB4C4, 0xB5E2, 0xB4C8,\n\t0xB5E3, 0xB4D0, 0xB5E4, 0xB4D5, 0xB5E5, 0xB4DC, 0xB5E6, 0xB4DD,\n\t0xB5E7, 0xB4E0, 0xB5E8, 0xB4E3, 0xB5E9, 0xB4E4, 0xB5EA, 0xB4E6,\n\t0xB5EB, 0xB4EC, 0xB5EC, 0xB4ED, 0xB5ED, 0xB4EF, 0xB5EE, 0xB4F1,\n\t0xB5EF, 0xB4F8, 0xB5F0, 0xB514, 0xB5F1, 0xB515, 0xB5F2, 0xB518,\n\t0xB5F3, 0xB51B, 0xB5F4, 0xB51C, 0xB5F5, 0xB524, 0xB5F6, 0xB525,\n\t0xB5F7, 0xB527, 0xB5F8, 0xB528, 0xB5F9, 0xB529, 0xB5FA, 0xB52A,\n\t0xB5FB, 0xB530, 0xB5FC, 0xB531, 0xB5FD, 0xB534, 0xB5FE, 0xB538,\n\t0xB641, 0xD105, 0xB642, 0xD106, 0xB643, 0xD107, 0xB644, 0xD108,\n\t0xB645, 0xD109, 0xB646, 0xD10A, 0xB647, 0xD10B, 0xB648, 0xD10C,\n\t0xB649, 0xD10E, 0xB64A, 0xD10F, 0xB64B, 0xD110, 0xB64C, 0xD111,\n\t0xB64D, 0xD112, 0xB64E, 0xD113, 0xB64F, 0xD114, 0xB650, 0xD115,\n\t0xB651, 0xD116, 0xB652, 0xD117, 0xB653, 0xD118, 0xB654, 0xD119,\n\t0xB655, 0xD11A, 0xB656, 0xD11B, 0xB657, 0xD11C, 0xB658, 0xD11D,\n\t0xB659, 0xD11E, 0xB65A, 0xD11F, 0xB661, 0xD120, 0xB662, 0xD121,\n\t0xB663, 0xD122, 0xB664, 0xD123, 0xB665, 0xD124, 0xB666, 0xD125,\n\t0xB667, 0xD126, 0xB668, 0xD127, 0xB669, 0xD128, 0xB66A, 0xD129,\n\t0xB66B, 0xD12A, 0xB66C, 0xD12B, 0xB66D, 0xD12C, 0xB66E, 0xD12D,\n\t0xB66F, 0xD12E, 0xB670, 0xD12F, 0xB671, 0xD132, 0xB672, 0xD133,\n\t0xB673, 0xD135, 0xB674, 0xD136, 0xB675, 0xD137, 0xB676, 0xD139,\n\t0xB677, 0xD13B, 0xB678, 0xD13C, 0xB679, 0xD13D, 0xB67A, 0xD13E,\n\t0xB681, 0xD13F, 0xB682, 0xD142, 0xB683, 0xD146, 0xB684, 0xD147,\n\t0xB685, 0xD148, 0xB686, 0xD149, 0xB687, 0xD14A, 0xB688, 0xD14B,\n\t0xB689, 0xD14E, 0xB68A, 0xD14F, 0xB68B, 0xD151, 0xB68C, 0xD152,\n\t0xB68D, 0xD153, 0xB68E, 0xD155, 0xB68F, 0xD156, 0xB690, 0xD157,\n\t0xB691, 0xD158, 0xB692, 0xD159, 0xB693, 0xD15A, 0xB694, 0xD15B,\n\t0xB695, 0xD15E, 0xB696, 0xD160, 0xB697, 0xD162, 0xB698, 0xD163,\n\t0xB699, 0xD164, 0xB69A, 0xD165, 0xB69B, 0xD166, 0xB69C, 0xD167,\n\t0xB69D, 0xD169, 0xB69E, 0xD16A, 0xB69F, 0xD16B, 0xB6A0, 0xD16D,\n\t0xB6A1, 0xB540, 0xB6A2, 0xB541, 0xB6A3, 0xB543, 0xB6A4, 0xB544,\n\t0xB6A5, 0xB545, 0xB6A6, 0xB54B, 0xB6A7, 0xB54C, 0xB6A8, 0xB54D,\n\t0xB6A9, 0xB550, 0xB6AA, 0xB554, 0xB6AB, 0xB55C, 0xB6AC, 0xB55D,\n\t0xB6AD, 0xB55F, 0xB6AE, 0xB560, 0xB6AF, 0xB561, 0xB6B0, 0xB5A0,\n\t0xB6B1, 0xB5A1, 0xB6B2, 0xB5A4, 0xB6B3, 0xB5A8, 0xB6B4, 0xB5AA,\n\t0xB6B5, 0xB5AB, 0xB6B6, 0xB5B0, 0xB6B7, 0xB5B1, 0xB6B8, 0xB5B3,\n\t0xB6B9, 0xB5B4, 0xB6BA, 0xB5B5, 0xB6BB, 0xB5BB, 0xB6BC, 0xB5BC,\n\t0xB6BD, 0xB5BD, 0xB6BE, 0xB5C0, 0xB6BF, 0xB5C4, 0xB6C0, 0xB5CC,\n\t0xB6C1, 0xB5CD, 0xB6C2, 0xB5CF, 0xB6C3, 0xB5D0, 0xB6C4, 0xB5D1,\n\t0xB6C5, 0xB5D8, 0xB6C6, 0xB5EC, 0xB6C7, 0xB610, 0xB6C8, 0xB611,\n\t0xB6C9, 0xB614, 0xB6CA, 0xB618, 0xB6CB, 0xB625, 0xB6CC, 0xB62C,\n\t0xB6CD, 0xB634, 0xB6CE, 0xB648, 0xB6CF, 0xB664, 0xB6D0, 0xB668,\n\t0xB6D1, 0xB69C, 0xB6D2, 0xB69D, 0xB6D3, 0xB6A0, 0xB6D4, 0xB6A4,\n\t0xB6D5, 0xB6AB, 0xB6D6, 0xB6AC, 0xB6D7, 0xB6B1, 0xB6D8, 0xB6D4,\n\t0xB6D9, 0xB6F0, 0xB6DA, 0xB6F4, 0xB6DB, 0xB6F8, 0xB6DC, 0xB700,\n\t0xB6DD, 0xB701, 0xB6DE, 0xB705, 0xB6DF, 0xB728, 0xB6E0, 0xB729,\n\t0xB6E1, 0xB72C, 0xB6E2, 0xB72F, 0xB6E3, 0xB730, 0xB6E4, 0xB738,\n\t0xB6E5, 0xB739, 0xB6E6, 0xB73B, 0xB6E7, 0xB744, 0xB6E8, 0xB748,\n\t0xB6E9, 0xB74C, 0xB6EA, 0xB754, 0xB6EB, 0xB755, 0xB6EC, 0xB760,\n\t0xB6ED, 0xB764, 0xB6EE, 0xB768, 0xB6EF, 0xB770, 0xB6F0, 0xB771,\n\t0xB6F1, 0xB773, 0xB6F2, 0xB775, 0xB6F3, 0xB77C, 0xB6F4, 0xB77D,\n\t0xB6F5, 0xB780, 0xB6F6, 0xB784, 0xB6F7, 0xB78C, 0xB6F8, 0xB78D,\n\t0xB6F9, 0xB78F, 0xB6FA, 0xB790, 0xB6FB, 0xB791, 0xB6FC, 0xB792,\n\t0xB6FD, 0xB796, 0xB6FE, 0xB797, 0xB741, 0xD16E, 0xB742, 0xD16F,\n\t0xB743, 0xD170, 0xB744, 0xD171, 0xB745, 0xD172, 0xB746, 0xD173,\n\t0xB747, 0xD174, 0xB748, 0xD175, 0xB749, 0xD176, 0xB74A, 0xD177,\n\t0xB74B, 0xD178, 0xB74C, 0xD179, 0xB74D, 0xD17A, 0xB74E, 0xD17B,\n\t0xB74F, 0xD17D, 0xB750, 0xD17E, 0xB751, 0xD17F, 0xB752, 0xD180,\n\t0xB753, 0xD181, 0xB754, 0xD182, 0xB755, 0xD183, 0xB756, 0xD185,\n\t0xB757, 0xD186, 0xB758, 0xD187, 0xB759, 0xD189, 0xB75A, 0xD18A,\n\t0xB761, 0xD18B, 0xB762, 0xD18C, 0xB763, 0xD18D, 0xB764, 0xD18E,\n\t0xB765, 0xD18F, 0xB766, 0xD190, 0xB767, 0xD191, 0xB768, 0xD192,\n\t0xB769, 0xD193, 0xB76A, 0xD194, 0xB76B, 0xD195, 0xB76C, 0xD196,\n\t0xB76D, 0xD197, 0xB76E, 0xD198, 0xB76F, 0xD199, 0xB770, 0xD19A,\n\t0xB771, 0xD19B, 0xB772, 0xD19C, 0xB773, 0xD19D, 0xB774, 0xD19E,\n\t0xB775, 0xD19F, 0xB776, 0xD1A2, 0xB777, 0xD1A3, 0xB778, 0xD1A5,\n\t0xB779, 0xD1A6, 0xB77A, 0xD1A7, 0xB781, 0xD1A9, 0xB782, 0xD1AA,\n\t0xB783, 0xD1AB, 0xB784, 0xD1AC, 0xB785, 0xD1AD, 0xB786, 0xD1AE,\n\t0xB787, 0xD1AF, 0xB788, 0xD1B2, 0xB789, 0xD1B4, 0xB78A, 0xD1B6,\n\t0xB78B, 0xD1B7, 0xB78C, 0xD1B8, 0xB78D, 0xD1B9, 0xB78E, 0xD1BB,\n\t0xB78F, 0xD1BD, 0xB790, 0xD1BE, 0xB791, 0xD1BF, 0xB792, 0xD1C1,\n\t0xB793, 0xD1C2, 0xB794, 0xD1C3, 0xB795, 0xD1C4, 0xB796, 0xD1C5,\n\t0xB797, 0xD1C6, 0xB798, 0xD1C7, 0xB799, 0xD1C8, 0xB79A, 0xD1C9,\n\t0xB79B, 0xD1CA, 0xB79C, 0xD1CB, 0xB79D, 0xD1CC, 0xB79E, 0xD1CD,\n\t0xB79F, 0xD1CE, 0xB7A0, 0xD1CF, 0xB7A1, 0xB798, 0xB7A2, 0xB799,\n\t0xB7A3, 0xB79C, 0xB7A4, 0xB7A0, 0xB7A5, 0xB7A8, 0xB7A6, 0xB7A9,\n\t0xB7A7, 0xB7AB, 0xB7A8, 0xB7AC, 0xB7A9, 0xB7AD, 0xB7AA, 0xB7B4,\n\t0xB7AB, 0xB7B5, 0xB7AC, 0xB7B8, 0xB7AD, 0xB7C7, 0xB7AE, 0xB7C9,\n\t0xB7AF, 0xB7EC, 0xB7B0, 0xB7ED, 0xB7B1, 0xB7F0, 0xB7B2, 0xB7F4,\n\t0xB7B3, 0xB7FC, 0xB7B4, 0xB7FD, 0xB7B5, 0xB7FF, 0xB7B6, 0xB800,\n\t0xB7B7, 0xB801, 0xB7B8, 0xB807, 0xB7B9, 0xB808, 0xB7BA, 0xB809,\n\t0xB7BB, 0xB80C, 0xB7BC, 0xB810, 0xB7BD, 0xB818, 0xB7BE, 0xB819,\n\t0xB7BF, 0xB81B, 0xB7C0, 0xB81D, 0xB7C1, 0xB824, 0xB7C2, 0xB825,\n\t0xB7C3, 0xB828, 0xB7C4, 0xB82C, 0xB7C5, 0xB834, 0xB7C6, 0xB835,\n\t0xB7C7, 0xB837, 0xB7C8, 0xB838, 0xB7C9, 0xB839, 0xB7CA, 0xB840,\n\t0xB7CB, 0xB844, 0xB7CC, 0xB851, 0xB7CD, 0xB853, 0xB7CE, 0xB85C,\n\t0xB7CF, 0xB85D, 0xB7D0, 0xB860, 0xB7D1, 0xB864, 0xB7D2, 0xB86C,\n\t0xB7D3, 0xB86D, 0xB7D4, 0xB86F, 0xB7D5, 0xB871, 0xB7D6, 0xB878,\n\t0xB7D7, 0xB87C, 0xB7D8, 0xB88D, 0xB7D9, 0xB8A8, 0xB7DA, 0xB8B0,\n\t0xB7DB, 0xB8B4, 0xB7DC, 0xB8B8, 0xB7DD, 0xB8C0, 0xB7DE, 0xB8C1,\n\t0xB7DF, 0xB8C3, 0xB7E0, 0xB8C5, 0xB7E1, 0xB8CC, 0xB7E2, 0xB8D0,\n\t0xB7E3, 0xB8D4, 0xB7E4, 0xB8DD, 0xB7E5, 0xB8DF, 0xB7E6, 0xB8E1,\n\t0xB7E7, 0xB8E8, 0xB7E8, 0xB8E9, 0xB7E9, 0xB8EC, 0xB7EA, 0xB8F0,\n\t0xB7EB, 0xB8F8, 0xB7EC, 0xB8F9, 0xB7ED, 0xB8FB, 0xB7EE, 0xB8FD,\n\t0xB7EF, 0xB904, 0xB7F0, 0xB918, 0xB7F1, 0xB920, 0xB7F2, 0xB93C,\n\t0xB7F3, 0xB93D, 0xB7F4, 0xB940, 0xB7F5, 0xB944, 0xB7F6, 0xB94C,\n\t0xB7F7, 0xB94F, 0xB7F8, 0xB951, 0xB7F9, 0xB958, 0xB7FA, 0xB959,\n\t0xB7FB, 0xB95C, 0xB7FC, 0xB960, 0xB7FD, 0xB968, 0xB7FE, 0xB969,\n\t0xB841, 0xD1D0, 0xB842, 0xD1D1, 0xB843, 0xD1D2, 0xB844, 0xD1D3,\n\t0xB845, 0xD1D4, 0xB846, 0xD1D5, 0xB847, 0xD1D6, 0xB848, 0xD1D7,\n\t0xB849, 0xD1D9, 0xB84A, 0xD1DA, 0xB84B, 0xD1DB, 0xB84C, 0xD1DC,\n\t0xB84D, 0xD1DD, 0xB84E, 0xD1DE, 0xB84F, 0xD1DF, 0xB850, 0xD1E0,\n\t0xB851, 0xD1E1, 0xB852, 0xD1E2, 0xB853, 0xD1E3, 0xB854, 0xD1E4,\n\t0xB855, 0xD1E5, 0xB856, 0xD1E6, 0xB857, 0xD1E7, 0xB858, 0xD1E8,\n\t0xB859, 0xD1E9, 0xB85A, 0xD1EA, 0xB861, 0xD1EB, 0xB862, 0xD1EC,\n\t0xB863, 0xD1ED, 0xB864, 0xD1EE, 0xB865, 0xD1EF, 0xB866, 0xD1F0,\n\t0xB867, 0xD1F1, 0xB868, 0xD1F2, 0xB869, 0xD1F3, 0xB86A, 0xD1F5,\n\t0xB86B, 0xD1F6, 0xB86C, 0xD1F7, 0xB86D, 0xD1F9, 0xB86E, 0xD1FA,\n\t0xB86F, 0xD1FB, 0xB870, 0xD1FC, 0xB871, 0xD1FD, 0xB872, 0xD1FE,\n\t0xB873, 0xD1FF, 0xB874, 0xD200, 0xB875, 0xD201, 0xB876, 0xD202,\n\t0xB877, 0xD203, 0xB878, 0xD204, 0xB879, 0xD205, 0xB87A, 0xD206,\n\t0xB881, 0xD208, 0xB882, 0xD20A, 0xB883, 0xD20B, 0xB884, 0xD20C,\n\t0xB885, 0xD20D, 0xB886, 0xD20E, 0xB887, 0xD20F, 0xB888, 0xD211,\n\t0xB889, 0xD212, 0xB88A, 0xD213, 0xB88B, 0xD214, 0xB88C, 0xD215,\n\t0xB88D, 0xD216, 0xB88E, 0xD217, 0xB88F, 0xD218, 0xB890, 0xD219,\n\t0xB891, 0xD21A, 0xB892, 0xD21B, 0xB893, 0xD21C, 0xB894, 0xD21D,\n\t0xB895, 0xD21E, 0xB896, 0xD21F, 0xB897, 0xD220, 0xB898, 0xD221,\n\t0xB899, 0xD222, 0xB89A, 0xD223, 0xB89B, 0xD224, 0xB89C, 0xD225,\n\t0xB89D, 0xD226, 0xB89E, 0xD227, 0xB89F, 0xD228, 0xB8A0, 0xD229,\n\t0xB8A1, 0xB96B, 0xB8A2, 0xB96D, 0xB8A3, 0xB974, 0xB8A4, 0xB975,\n\t0xB8A5, 0xB978, 0xB8A6, 0xB97C, 0xB8A7, 0xB984, 0xB8A8, 0xB985,\n\t0xB8A9, 0xB987, 0xB8AA, 0xB989, 0xB8AB, 0xB98A, 0xB8AC, 0xB98D,\n\t0xB8AD, 0xB98E, 0xB8AE, 0xB9AC, 0xB8AF, 0xB9AD, 0xB8B0, 0xB9B0,\n\t0xB8B1, 0xB9B4, 0xB8B2, 0xB9BC, 0xB8B3, 0xB9BD, 0xB8B4, 0xB9BF,\n\t0xB8B5, 0xB9C1, 0xB8B6, 0xB9C8, 0xB8B7, 0xB9C9, 0xB8B8, 0xB9CC,\n\t0xB8B9, 0xB9CE, 0xB8BA, 0xB9CF, 0xB8BB, 0xB9D0, 0xB8BC, 0xB9D1,\n\t0xB8BD, 0xB9D2, 0xB8BE, 0xB9D8, 0xB8BF, 0xB9D9, 0xB8C0, 0xB9DB,\n\t0xB8C1, 0xB9DD, 0xB8C2, 0xB9DE, 0xB8C3, 0xB9E1, 0xB8C4, 0xB9E3,\n\t0xB8C5, 0xB9E4, 0xB8C6, 0xB9E5, 0xB8C7, 0xB9E8, 0xB8C8, 0xB9EC,\n\t0xB8C9, 0xB9F4, 0xB8CA, 0xB9F5, 0xB8CB, 0xB9F7, 0xB8CC, 0xB9F8,\n\t0xB8CD, 0xB9F9, 0xB8CE, 0xB9FA, 0xB8CF, 0xBA00, 0xB8D0, 0xBA01,\n\t0xB8D1, 0xBA08, 0xB8D2, 0xBA15, 0xB8D3, 0xBA38, 0xB8D4, 0xBA39,\n\t0xB8D5, 0xBA3C, 0xB8D6, 0xBA40, 0xB8D7, 0xBA42, 0xB8D8, 0xBA48,\n\t0xB8D9, 0xBA49, 0xB8DA, 0xBA4B, 0xB8DB, 0xBA4D, 0xB8DC, 0xBA4E,\n\t0xB8DD, 0xBA53, 0xB8DE, 0xBA54, 0xB8DF, 0xBA55, 0xB8E0, 0xBA58,\n\t0xB8E1, 0xBA5C, 0xB8E2, 0xBA64, 0xB8E3, 0xBA65, 0xB8E4, 0xBA67,\n\t0xB8E5, 0xBA68, 0xB8E6, 0xBA69, 0xB8E7, 0xBA70, 0xB8E8, 0xBA71,\n\t0xB8E9, 0xBA74, 0xB8EA, 0xBA78, 0xB8EB, 0xBA83, 0xB8EC, 0xBA84,\n\t0xB8ED, 0xBA85, 0xB8EE, 0xBA87, 0xB8EF, 0xBA8C, 0xB8F0, 0xBAA8,\n\t0xB8F1, 0xBAA9, 0xB8F2, 0xBAAB, 0xB8F3, 0xBAAC, 0xB8F4, 0xBAB0,\n\t0xB8F5, 0xBAB2, 0xB8F6, 0xBAB8, 0xB8F7, 0xBAB9, 0xB8F8, 0xBABB,\n\t0xB8F9, 0xBABD, 0xB8FA, 0xBAC4, 0xB8FB, 0xBAC8, 0xB8FC, 0xBAD8,\n\t0xB8FD, 0xBAD9, 0xB8FE, 0xBAFC, 0xB941, 0xD22A, 0xB942, 0xD22B,\n\t0xB943, 0xD22E, 0xB944, 0xD22F, 0xB945, 0xD231, 0xB946, 0xD232,\n\t0xB947, 0xD233, 0xB948, 0xD235, 0xB949, 0xD236, 0xB94A, 0xD237,\n\t0xB94B, 0xD238, 0xB94C, 0xD239, 0xB94D, 0xD23A, 0xB94E, 0xD23B,\n\t0xB94F, 0xD23E, 0xB950, 0xD240, 0xB951, 0xD242, 0xB952, 0xD243,\n\t0xB953, 0xD244, 0xB954, 0xD245, 0xB955, 0xD246, 0xB956, 0xD247,\n\t0xB957, 0xD249, 0xB958, 0xD24A, 0xB959, 0xD24B, 0xB95A, 0xD24C,\n\t0xB961, 0xD24D, 0xB962, 0xD24E, 0xB963, 0xD24F, 0xB964, 0xD250,\n\t0xB965, 0xD251, 0xB966, 0xD252, 0xB967, 0xD253, 0xB968, 0xD254,\n\t0xB969, 0xD255, 0xB96A, 0xD256, 0xB96B, 0xD257, 0xB96C, 0xD258,\n\t0xB96D, 0xD259, 0xB96E, 0xD25A, 0xB96F, 0xD25B, 0xB970, 0xD25D,\n\t0xB971, 0xD25E, 0xB972, 0xD25F, 0xB973, 0xD260, 0xB974, 0xD261,\n\t0xB975, 0xD262, 0xB976, 0xD263, 0xB977, 0xD265, 0xB978, 0xD266,\n\t0xB979, 0xD267, 0xB97A, 0xD268, 0xB981, 0xD269, 0xB982, 0xD26A,\n\t0xB983, 0xD26B, 0xB984, 0xD26C, 0xB985, 0xD26D, 0xB986, 0xD26E,\n\t0xB987, 0xD26F, 0xB988, 0xD270, 0xB989, 0xD271, 0xB98A, 0xD272,\n\t0xB98B, 0xD273, 0xB98C, 0xD274, 0xB98D, 0xD275, 0xB98E, 0xD276,\n\t0xB98F, 0xD277, 0xB990, 0xD278, 0xB991, 0xD279, 0xB992, 0xD27A,\n\t0xB993, 0xD27B, 0xB994, 0xD27C, 0xB995, 0xD27D, 0xB996, 0xD27E,\n\t0xB997, 0xD27F, 0xB998, 0xD282, 0xB999, 0xD283, 0xB99A, 0xD285,\n\t0xB99B, 0xD286, 0xB99C, 0xD287, 0xB99D, 0xD289, 0xB99E, 0xD28A,\n\t0xB99F, 0xD28B, 0xB9A0, 0xD28C, 0xB9A1, 0xBB00, 0xB9A2, 0xBB04,\n\t0xB9A3, 0xBB0D, 0xB9A4, 0xBB0F, 0xB9A5, 0xBB11, 0xB9A6, 0xBB18,\n\t0xB9A7, 0xBB1C, 0xB9A8, 0xBB20, 0xB9A9, 0xBB29, 0xB9AA, 0xBB2B,\n\t0xB9AB, 0xBB34, 0xB9AC, 0xBB35, 0xB9AD, 0xBB36, 0xB9AE, 0xBB38,\n\t0xB9AF, 0xBB3B, 0xB9B0, 0xBB3C, 0xB9B1, 0xBB3D, 0xB9B2, 0xBB3E,\n\t0xB9B3, 0xBB44, 0xB9B4, 0xBB45, 0xB9B5, 0xBB47, 0xB9B6, 0xBB49,\n\t0xB9B7, 0xBB4D, 0xB9B8, 0xBB4F, 0xB9B9, 0xBB50, 0xB9BA, 0xBB54,\n\t0xB9BB, 0xBB58, 0xB9BC, 0xBB61, 0xB9BD, 0xBB63, 0xB9BE, 0xBB6C,\n\t0xB9BF, 0xBB88, 0xB9C0, 0xBB8C, 0xB9C1, 0xBB90, 0xB9C2, 0xBBA4,\n\t0xB9C3, 0xBBA8, 0xB9C4, 0xBBAC, 0xB9C5, 0xBBB4, 0xB9C6, 0xBBB7,\n\t0xB9C7, 0xBBC0, 0xB9C8, 0xBBC4, 0xB9C9, 0xBBC8, 0xB9CA, 0xBBD0,\n\t0xB9CB, 0xBBD3, 0xB9CC, 0xBBF8, 0xB9CD, 0xBBF9, 0xB9CE, 0xBBFC,\n\t0xB9CF, 0xBBFF, 0xB9D0, 0xBC00, 0xB9D1, 0xBC02, 0xB9D2, 0xBC08,\n\t0xB9D3, 0xBC09, 0xB9D4, 0xBC0B, 0xB9D5, 0xBC0C, 0xB9D6, 0xBC0D,\n\t0xB9D7, 0xBC0F, 0xB9D8, 0xBC11, 0xB9D9, 0xBC14, 0xB9DA, 0xBC15,\n\t0xB9DB, 0xBC16, 0xB9DC, 0xBC17, 0xB9DD, 0xBC18, 0xB9DE, 0xBC1B,\n\t0xB9DF, 0xBC1C, 0xB9E0, 0xBC1D, 0xB9E1, 0xBC1E, 0xB9E2, 0xBC1F,\n\t0xB9E3, 0xBC24, 0xB9E4, 0xBC25, 0xB9E5, 0xBC27, 0xB9E6, 0xBC29,\n\t0xB9E7, 0xBC2D, 0xB9E8, 0xBC30, 0xB9E9, 0xBC31, 0xB9EA, 0xBC34,\n\t0xB9EB, 0xBC38, 0xB9EC, 0xBC40, 0xB9ED, 0xBC41, 0xB9EE, 0xBC43,\n\t0xB9EF, 0xBC44, 0xB9F0, 0xBC45, 0xB9F1, 0xBC49, 0xB9F2, 0xBC4C,\n\t0xB9F3, 0xBC4D, 0xB9F4, 0xBC50, 0xB9F5, 0xBC5D, 0xB9F6, 0xBC84,\n\t0xB9F7, 0xBC85, 0xB9F8, 0xBC88, 0xB9F9, 0xBC8B, 0xB9FA, 0xBC8C,\n\t0xB9FB, 0xBC8E, 0xB9FC, 0xBC94, 0xB9FD, 0xBC95, 0xB9FE, 0xBC97,\n\t0xBA41, 0xD28D, 0xBA42, 0xD28E, 0xBA43, 0xD28F, 0xBA44, 0xD292,\n\t0xBA45, 0xD293, 0xBA46, 0xD294, 0xBA47, 0xD296, 0xBA48, 0xD297,\n\t0xBA49, 0xD298, 0xBA4A, 0xD299, 0xBA4B, 0xD29A, 0xBA4C, 0xD29B,\n\t0xBA4D, 0xD29D, 0xBA4E, 0xD29E, 0xBA4F, 0xD29F, 0xBA50, 0xD2A1,\n\t0xBA51, 0xD2A2, 0xBA52, 0xD2A3, 0xBA53, 0xD2A5, 0xBA54, 0xD2A6,\n\t0xBA55, 0xD2A7, 0xBA56, 0xD2A8, 0xBA57, 0xD2A9, 0xBA58, 0xD2AA,\n\t0xBA59, 0xD2AB, 0xBA5A, 0xD2AD, 0xBA61, 0xD2AE, 0xBA62, 0xD2AF,\n\t0xBA63, 0xD2B0, 0xBA64, 0xD2B2, 0xBA65, 0xD2B3, 0xBA66, 0xD2B4,\n\t0xBA67, 0xD2B5, 0xBA68, 0xD2B6, 0xBA69, 0xD2B7, 0xBA6A, 0xD2BA,\n\t0xBA6B, 0xD2BB, 0xBA6C, 0xD2BD, 0xBA6D, 0xD2BE, 0xBA6E, 0xD2C1,\n\t0xBA6F, 0xD2C3, 0xBA70, 0xD2C4, 0xBA71, 0xD2C5, 0xBA72, 0xD2C6,\n\t0xBA73, 0xD2C7, 0xBA74, 0xD2CA, 0xBA75, 0xD2CC, 0xBA76, 0xD2CD,\n\t0xBA77, 0xD2CE, 0xBA78, 0xD2CF, 0xBA79, 0xD2D0, 0xBA7A, 0xD2D1,\n\t0xBA81, 0xD2D2, 0xBA82, 0xD2D3, 0xBA83, 0xD2D5, 0xBA84, 0xD2D6,\n\t0xBA85, 0xD2D7, 0xBA86, 0xD2D9, 0xBA87, 0xD2DA, 0xBA88, 0xD2DB,\n\t0xBA89, 0xD2DD, 0xBA8A, 0xD2DE, 0xBA8B, 0xD2DF, 0xBA8C, 0xD2E0,\n\t0xBA8D, 0xD2E1, 0xBA8E, 0xD2E2, 0xBA8F, 0xD2E3, 0xBA90, 0xD2E6,\n\t0xBA91, 0xD2E7, 0xBA92, 0xD2E8, 0xBA93, 0xD2E9, 0xBA94, 0xD2EA,\n\t0xBA95, 0xD2EB, 0xBA96, 0xD2EC, 0xBA97, 0xD2ED, 0xBA98, 0xD2EE,\n\t0xBA99, 0xD2EF, 0xBA9A, 0xD2F2, 0xBA9B, 0xD2F3, 0xBA9C, 0xD2F5,\n\t0xBA9D, 0xD2F6, 0xBA9E, 0xD2F7, 0xBA9F, 0xD2F9, 0xBAA0, 0xD2FA,\n\t0xBAA1, 0xBC99, 0xBAA2, 0xBC9A, 0xBAA3, 0xBCA0, 0xBAA4, 0xBCA1,\n\t0xBAA5, 0xBCA4, 0xBAA6, 0xBCA7, 0xBAA7, 0xBCA8, 0xBAA8, 0xBCB0,\n\t0xBAA9, 0xBCB1, 0xBAAA, 0xBCB3, 0xBAAB, 0xBCB4, 0xBAAC, 0xBCB5,\n\t0xBAAD, 0xBCBC, 0xBAAE, 0xBCBD, 0xBAAF, 0xBCC0, 0xBAB0, 0xBCC4,\n\t0xBAB1, 0xBCCD, 0xBAB2, 0xBCCF, 0xBAB3, 0xBCD0, 0xBAB4, 0xBCD1,\n\t0xBAB5, 0xBCD5, 0xBAB6, 0xBCD8, 0xBAB7, 0xBCDC, 0xBAB8, 0xBCF4,\n\t0xBAB9, 0xBCF5, 0xBABA, 0xBCF6, 0xBABB, 0xBCF8, 0xBABC, 0xBCFC,\n\t0xBABD, 0xBD04, 0xBABE, 0xBD05, 0xBABF, 0xBD07, 0xBAC0, 0xBD09,\n\t0xBAC1, 0xBD10, 0xBAC2, 0xBD14, 0xBAC3, 0xBD24, 0xBAC4, 0xBD2C,\n\t0xBAC5, 0xBD40, 0xBAC6, 0xBD48, 0xBAC7, 0xBD49, 0xBAC8, 0xBD4C,\n\t0xBAC9, 0xBD50, 0xBACA, 0xBD58, 0xBACB, 0xBD59, 0xBACC, 0xBD64,\n\t0xBACD, 0xBD68, 0xBACE, 0xBD80, 0xBACF, 0xBD81, 0xBAD0, 0xBD84,\n\t0xBAD1, 0xBD87, 0xBAD2, 0xBD88, 0xBAD3, 0xBD89, 0xBAD4, 0xBD8A,\n\t0xBAD5, 0xBD90, 0xBAD6, 0xBD91, 0xBAD7, 0xBD93, 0xBAD8, 0xBD95,\n\t0xBAD9, 0xBD99, 0xBADA, 0xBD9A, 0xBADB, 0xBD9C, 0xBADC, 0xBDA4,\n\t0xBADD, 0xBDB0, 0xBADE, 0xBDB8, 0xBADF, 0xBDD4, 0xBAE0, 0xBDD5,\n\t0xBAE1, 0xBDD8, 0xBAE2, 0xBDDC, 0xBAE3, 0xBDE9, 0xBAE4, 0xBDF0,\n\t0xBAE5, 0xBDF4, 0xBAE6, 0xBDF8, 0xBAE7, 0xBE00, 0xBAE8, 0xBE03,\n\t0xBAE9, 0xBE05, 0xBAEA, 0xBE0C, 0xBAEB, 0xBE0D, 0xBAEC, 0xBE10,\n\t0xBAED, 0xBE14, 0xBAEE, 0xBE1C, 0xBAEF, 0xBE1D, 0xBAF0, 0xBE1F,\n\t0xBAF1, 0xBE44, 0xBAF2, 0xBE45, 0xBAF3, 0xBE48, 0xBAF4, 0xBE4C,\n\t0xBAF5, 0xBE4E, 0xBAF6, 0xBE54, 0xBAF7, 0xBE55, 0xBAF8, 0xBE57,\n\t0xBAF9, 0xBE59, 0xBAFA, 0xBE5A, 0xBAFB, 0xBE5B, 0xBAFC, 0xBE60,\n\t0xBAFD, 0xBE61, 0xBAFE, 0xBE64, 0xBB41, 0xD2FB, 0xBB42, 0xD2FC,\n\t0xBB43, 0xD2FD, 0xBB44, 0xD2FE, 0xBB45, 0xD2FF, 0xBB46, 0xD302,\n\t0xBB47, 0xD304, 0xBB48, 0xD306, 0xBB49, 0xD307, 0xBB4A, 0xD308,\n\t0xBB4B, 0xD309, 0xBB4C, 0xD30A, 0xBB4D, 0xD30B, 0xBB4E, 0xD30F,\n\t0xBB4F, 0xD311, 0xBB50, 0xD312, 0xBB51, 0xD313, 0xBB52, 0xD315,\n\t0xBB53, 0xD317, 0xBB54, 0xD318, 0xBB55, 0xD319, 0xBB56, 0xD31A,\n\t0xBB57, 0xD31B, 0xBB58, 0xD31E, 0xBB59, 0xD322, 0xBB5A, 0xD323,\n\t0xBB61, 0xD324, 0xBB62, 0xD326, 0xBB63, 0xD327, 0xBB64, 0xD32A,\n\t0xBB65, 0xD32B, 0xBB66, 0xD32D, 0xBB67, 0xD32E, 0xBB68, 0xD32F,\n\t0xBB69, 0xD331, 0xBB6A, 0xD332, 0xBB6B, 0xD333, 0xBB6C, 0xD334,\n\t0xBB6D, 0xD335, 0xBB6E, 0xD336, 0xBB6F, 0xD337, 0xBB70, 0xD33A,\n\t0xBB71, 0xD33E, 0xBB72, 0xD33F, 0xBB73, 0xD340, 0xBB74, 0xD341,\n\t0xBB75, 0xD342, 0xBB76, 0xD343, 0xBB77, 0xD346, 0xBB78, 0xD347,\n\t0xBB79, 0xD348, 0xBB7A, 0xD349, 0xBB81, 0xD34A, 0xBB82, 0xD34B,\n\t0xBB83, 0xD34C, 0xBB84, 0xD34D, 0xBB85, 0xD34E, 0xBB86, 0xD34F,\n\t0xBB87, 0xD350, 0xBB88, 0xD351, 0xBB89, 0xD352, 0xBB8A, 0xD353,\n\t0xBB8B, 0xD354, 0xBB8C, 0xD355, 0xBB8D, 0xD356, 0xBB8E, 0xD357,\n\t0xBB8F, 0xD358, 0xBB90, 0xD359, 0xBB91, 0xD35A, 0xBB92, 0xD35B,\n\t0xBB93, 0xD35C, 0xBB94, 0xD35D, 0xBB95, 0xD35E, 0xBB96, 0xD35F,\n\t0xBB97, 0xD360, 0xBB98, 0xD361, 0xBB99, 0xD362, 0xBB9A, 0xD363,\n\t0xBB9B, 0xD364, 0xBB9C, 0xD365, 0xBB9D, 0xD366, 0xBB9E, 0xD367,\n\t0xBB9F, 0xD368, 0xBBA0, 0xD369, 0xBBA1, 0xBE68, 0xBBA2, 0xBE6A,\n\t0xBBA3, 0xBE70, 0xBBA4, 0xBE71, 0xBBA5, 0xBE73, 0xBBA6, 0xBE74,\n\t0xBBA7, 0xBE75, 0xBBA8, 0xBE7B, 0xBBA9, 0xBE7C, 0xBBAA, 0xBE7D,\n\t0xBBAB, 0xBE80, 0xBBAC, 0xBE84, 0xBBAD, 0xBE8C, 0xBBAE, 0xBE8D,\n\t0xBBAF, 0xBE8F, 0xBBB0, 0xBE90, 0xBBB1, 0xBE91, 0xBBB2, 0xBE98,\n\t0xBBB3, 0xBE99, 0xBBB4, 0xBEA8, 0xBBB5, 0xBED0, 0xBBB6, 0xBED1,\n\t0xBBB7, 0xBED4, 0xBBB8, 0xBED7, 0xBBB9, 0xBED8, 0xBBBA, 0xBEE0,\n\t0xBBBB, 0xBEE3, 0xBBBC, 0xBEE4, 0xBBBD, 0xBEE5, 0xBBBE, 0xBEEC,\n\t0xBBBF, 0xBF01, 0xBBC0, 0xBF08, 0xBBC1, 0xBF09, 0xBBC2, 0xBF18,\n\t0xBBC3, 0xBF19, 0xBBC4, 0xBF1B, 0xBBC5, 0xBF1C, 0xBBC6, 0xBF1D,\n\t0xBBC7, 0xBF40, 0xBBC8, 0xBF41, 0xBBC9, 0xBF44, 0xBBCA, 0xBF48,\n\t0xBBCB, 0xBF50, 0xBBCC, 0xBF51, 0xBBCD, 0xBF55, 0xBBCE, 0xBF94,\n\t0xBBCF, 0xBFB0, 0xBBD0, 0xBFC5, 0xBBD1, 0xBFCC, 0xBBD2, 0xBFCD,\n\t0xBBD3, 0xBFD0, 0xBBD4, 0xBFD4, 0xBBD5, 0xBFDC, 0xBBD6, 0xBFDF,\n\t0xBBD7, 0xBFE1, 0xBBD8, 0xC03C, 0xBBD9, 0xC051, 0xBBDA, 0xC058,\n\t0xBBDB, 0xC05C, 0xBBDC, 0xC060, 0xBBDD, 0xC068, 0xBBDE, 0xC069,\n\t0xBBDF, 0xC090, 0xBBE0, 0xC091, 0xBBE1, 0xC094, 0xBBE2, 0xC098,\n\t0xBBE3, 0xC0A0, 0xBBE4, 0xC0A1, 0xBBE5, 0xC0A3, 0xBBE6, 0xC0A5,\n\t0xBBE7, 0xC0AC, 0xBBE8, 0xC0AD, 0xBBE9, 0xC0AF, 0xBBEA, 0xC0B0,\n\t0xBBEB, 0xC0B3, 0xBBEC, 0xC0B4, 0xBBED, 0xC0B5, 0xBBEE, 0xC0B6,\n\t0xBBEF, 0xC0BC, 0xBBF0, 0xC0BD, 0xBBF1, 0xC0BF, 0xBBF2, 0xC0C0,\n\t0xBBF3, 0xC0C1, 0xBBF4, 0xC0C5, 0xBBF5, 0xC0C8, 0xBBF6, 0xC0C9,\n\t0xBBF7, 0xC0CC, 0xBBF8, 0xC0D0, 0xBBF9, 0xC0D8, 0xBBFA, 0xC0D9,\n\t0xBBFB, 0xC0DB, 0xBBFC, 0xC0DC, 0xBBFD, 0xC0DD, 0xBBFE, 0xC0E4,\n\t0xBC41, 0xD36A, 0xBC42, 0xD36B, 0xBC43, 0xD36C, 0xBC44, 0xD36D,\n\t0xBC45, 0xD36E, 0xBC46, 0xD36F, 0xBC47, 0xD370, 0xBC48, 0xD371,\n\t0xBC49, 0xD372, 0xBC4A, 0xD373, 0xBC4B, 0xD374, 0xBC4C, 0xD375,\n\t0xBC4D, 0xD376, 0xBC4E, 0xD377, 0xBC4F, 0xD378, 0xBC50, 0xD379,\n\t0xBC51, 0xD37A, 0xBC52, 0xD37B, 0xBC53, 0xD37E, 0xBC54, 0xD37F,\n\t0xBC55, 0xD381, 0xBC56, 0xD382, 0xBC57, 0xD383, 0xBC58, 0xD385,\n\t0xBC59, 0xD386, 0xBC5A, 0xD387, 0xBC61, 0xD388, 0xBC62, 0xD389,\n\t0xBC63, 0xD38A, 0xBC64, 0xD38B, 0xBC65, 0xD38E, 0xBC66, 0xD392,\n\t0xBC67, 0xD393, 0xBC68, 0xD394, 0xBC69, 0xD395, 0xBC6A, 0xD396,\n\t0xBC6B, 0xD397, 0xBC6C, 0xD39A, 0xBC6D, 0xD39B, 0xBC6E, 0xD39D,\n\t0xBC6F, 0xD39E, 0xBC70, 0xD39F, 0xBC71, 0xD3A1, 0xBC72, 0xD3A2,\n\t0xBC73, 0xD3A3, 0xBC74, 0xD3A4, 0xBC75, 0xD3A5, 0xBC76, 0xD3A6,\n\t0xBC77, 0xD3A7, 0xBC78, 0xD3AA, 0xBC79, 0xD3AC, 0xBC7A, 0xD3AE,\n\t0xBC81, 0xD3AF, 0xBC82, 0xD3B0, 0xBC83, 0xD3B1, 0xBC84, 0xD3B2,\n\t0xBC85, 0xD3B3, 0xBC86, 0xD3B5, 0xBC87, 0xD3B6, 0xBC88, 0xD3B7,\n\t0xBC89, 0xD3B9, 0xBC8A, 0xD3BA, 0xBC8B, 0xD3BB, 0xBC8C, 0xD3BD,\n\t0xBC8D, 0xD3BE, 0xBC8E, 0xD3BF, 0xBC8F, 0xD3C0, 0xBC90, 0xD3C1,\n\t0xBC91, 0xD3C2, 0xBC92, 0xD3C3, 0xBC93, 0xD3C6, 0xBC94, 0xD3C7,\n\t0xBC95, 0xD3CA, 0xBC96, 0xD3CB, 0xBC97, 0xD3CC, 0xBC98, 0xD3CD,\n\t0xBC99, 0xD3CE, 0xBC9A, 0xD3CF, 0xBC9B, 0xD3D1, 0xBC9C, 0xD3D2,\n\t0xBC9D, 0xD3D3, 0xBC9E, 0xD3D4, 0xBC9F, 0xD3D5, 0xBCA0, 0xD3D6,\n\t0xBCA1, 0xC0E5, 0xBCA2, 0xC0E8, 0xBCA3, 0xC0EC, 0xBCA4, 0xC0F4,\n\t0xBCA5, 0xC0F5, 0xBCA6, 0xC0F7, 0xBCA7, 0xC0F9, 0xBCA8, 0xC100,\n\t0xBCA9, 0xC104, 0xBCAA, 0xC108, 0xBCAB, 0xC110, 0xBCAC, 0xC115,\n\t0xBCAD, 0xC11C, 0xBCAE, 0xC11D, 0xBCAF, 0xC11E, 0xBCB0, 0xC11F,\n\t0xBCB1, 0xC120, 0xBCB2, 0xC123, 0xBCB3, 0xC124, 0xBCB4, 0xC126,\n\t0xBCB5, 0xC127, 0xBCB6, 0xC12C, 0xBCB7, 0xC12D, 0xBCB8, 0xC12F,\n\t0xBCB9, 0xC130, 0xBCBA, 0xC131, 0xBCBB, 0xC136, 0xBCBC, 0xC138,\n\t0xBCBD, 0xC139, 0xBCBE, 0xC13C, 0xBCBF, 0xC140, 0xBCC0, 0xC148,\n\t0xBCC1, 0xC149, 0xBCC2, 0xC14B, 0xBCC3, 0xC14C, 0xBCC4, 0xC14D,\n\t0xBCC5, 0xC154, 0xBCC6, 0xC155, 0xBCC7, 0xC158, 0xBCC8, 0xC15C,\n\t0xBCC9, 0xC164, 0xBCCA, 0xC165, 0xBCCB, 0xC167, 0xBCCC, 0xC168,\n\t0xBCCD, 0xC169, 0xBCCE, 0xC170, 0xBCCF, 0xC174, 0xBCD0, 0xC178,\n\t0xBCD1, 0xC185, 0xBCD2, 0xC18C, 0xBCD3, 0xC18D, 0xBCD4, 0xC18E,\n\t0xBCD5, 0xC190, 0xBCD6, 0xC194, 0xBCD7, 0xC196, 0xBCD8, 0xC19C,\n\t0xBCD9, 0xC19D, 0xBCDA, 0xC19F, 0xBCDB, 0xC1A1, 0xBCDC, 0xC1A5,\n\t0xBCDD, 0xC1A8, 0xBCDE, 0xC1A9, 0xBCDF, 0xC1AC, 0xBCE0, 0xC1B0,\n\t0xBCE1, 0xC1BD, 0xBCE2, 0xC1C4, 0xBCE3, 0xC1C8, 0xBCE4, 0xC1CC,\n\t0xBCE5, 0xC1D4, 0xBCE6, 0xC1D7, 0xBCE7, 0xC1D8, 0xBCE8, 0xC1E0,\n\t0xBCE9, 0xC1E4, 0xBCEA, 0xC1E8, 0xBCEB, 0xC1F0, 0xBCEC, 0xC1F1,\n\t0xBCED, 0xC1F3, 0xBCEE, 0xC1FC, 0xBCEF, 0xC1FD, 0xBCF0, 0xC200,\n\t0xBCF1, 0xC204, 0xBCF2, 0xC20C, 0xBCF3, 0xC20D, 0xBCF4, 0xC20F,\n\t0xBCF5, 0xC211, 0xBCF6, 0xC218, 0xBCF7, 0xC219, 0xBCF8, 0xC21C,\n\t0xBCF9, 0xC21F, 0xBCFA, 0xC220, 0xBCFB, 0xC228, 0xBCFC, 0xC229,\n\t0xBCFD, 0xC22B, 0xBCFE, 0xC22D, 0xBD41, 0xD3D7, 0xBD42, 0xD3D9,\n\t0xBD43, 0xD3DA, 0xBD44, 0xD3DB, 0xBD45, 0xD3DC, 0xBD46, 0xD3DD,\n\t0xBD47, 0xD3DE, 0xBD48, 0xD3DF, 0xBD49, 0xD3E0, 0xBD4A, 0xD3E2,\n\t0xBD4B, 0xD3E4, 0xBD4C, 0xD3E5, 0xBD4D, 0xD3E6, 0xBD4E, 0xD3E7,\n\t0xBD4F, 0xD3E8, 0xBD50, 0xD3E9, 0xBD51, 0xD3EA, 0xBD52, 0xD3EB,\n\t0xBD53, 0xD3EE, 0xBD54, 0xD3EF, 0xBD55, 0xD3F1, 0xBD56, 0xD3F2,\n\t0xBD57, 0xD3F3, 0xBD58, 0xD3F5, 0xBD59, 0xD3F6, 0xBD5A, 0xD3F7,\n\t0xBD61, 0xD3F8, 0xBD62, 0xD3F9, 0xBD63, 0xD3FA, 0xBD64, 0xD3FB,\n\t0xBD65, 0xD3FE, 0xBD66, 0xD400, 0xBD67, 0xD402, 0xBD68, 0xD403,\n\t0xBD69, 0xD404, 0xBD6A, 0xD405, 0xBD6B, 0xD406, 0xBD6C, 0xD407,\n\t0xBD6D, 0xD409, 0xBD6E, 0xD40A, 0xBD6F, 0xD40B, 0xBD70, 0xD40C,\n\t0xBD71, 0xD40D, 0xBD72, 0xD40E, 0xBD73, 0xD40F, 0xBD74, 0xD410,\n\t0xBD75, 0xD411, 0xBD76, 0xD412, 0xBD77, 0xD413, 0xBD78, 0xD414,\n\t0xBD79, 0xD415, 0xBD7A, 0xD416, 0xBD81, 0xD417, 0xBD82, 0xD418,\n\t0xBD83, 0xD419, 0xBD84, 0xD41A, 0xBD85, 0xD41B, 0xBD86, 0xD41C,\n\t0xBD87, 0xD41E, 0xBD88, 0xD41F, 0xBD89, 0xD420, 0xBD8A, 0xD421,\n\t0xBD8B, 0xD422, 0xBD8C, 0xD423, 0xBD8D, 0xD424, 0xBD8E, 0xD425,\n\t0xBD8F, 0xD426, 0xBD90, 0xD427, 0xBD91, 0xD428, 0xBD92, 0xD429,\n\t0xBD93, 0xD42A, 0xBD94, 0xD42B, 0xBD95, 0xD42C, 0xBD96, 0xD42D,\n\t0xBD97, 0xD42E, 0xBD98, 0xD42F, 0xBD99, 0xD430, 0xBD9A, 0xD431,\n\t0xBD9B, 0xD432, 0xBD9C, 0xD433, 0xBD9D, 0xD434, 0xBD9E, 0xD435,\n\t0xBD9F, 0xD436, 0xBDA0, 0xD437, 0xBDA1, 0xC22F, 0xBDA2, 0xC231,\n\t0xBDA3, 0xC232, 0xBDA4, 0xC234, 0xBDA5, 0xC248, 0xBDA6, 0xC250,\n\t0xBDA7, 0xC251, 0xBDA8, 0xC254, 0xBDA9, 0xC258, 0xBDAA, 0xC260,\n\t0xBDAB, 0xC265, 0xBDAC, 0xC26C, 0xBDAD, 0xC26D, 0xBDAE, 0xC270,\n\t0xBDAF, 0xC274, 0xBDB0, 0xC27C, 0xBDB1, 0xC27D, 0xBDB2, 0xC27F,\n\t0xBDB3, 0xC281, 0xBDB4, 0xC288, 0xBDB5, 0xC289, 0xBDB6, 0xC290,\n\t0xBDB7, 0xC298, 0xBDB8, 0xC29B, 0xBDB9, 0xC29D, 0xBDBA, 0xC2A4,\n\t0xBDBB, 0xC2A5, 0xBDBC, 0xC2A8, 0xBDBD, 0xC2AC, 0xBDBE, 0xC2AD,\n\t0xBDBF, 0xC2B4, 0xBDC0, 0xC2B5, 0xBDC1, 0xC2B7, 0xBDC2, 0xC2B9,\n\t0xBDC3, 0xC2DC, 0xBDC4, 0xC2DD, 0xBDC5, 0xC2E0, 0xBDC6, 0xC2E3,\n\t0xBDC7, 0xC2E4, 0xBDC8, 0xC2EB, 0xBDC9, 0xC2EC, 0xBDCA, 0xC2ED,\n\t0xBDCB, 0xC2EF, 0xBDCC, 0xC2F1, 0xBDCD, 0xC2F6, 0xBDCE, 0xC2F8,\n\t0xBDCF, 0xC2F9, 0xBDD0, 0xC2FB, 0xBDD1, 0xC2FC, 0xBDD2, 0xC300,\n\t0xBDD3, 0xC308, 0xBDD4, 0xC309, 0xBDD5, 0xC30C, 0xBDD6, 0xC30D,\n\t0xBDD7, 0xC313, 0xBDD8, 0xC314, 0xBDD9, 0xC315, 0xBDDA, 0xC318,\n\t0xBDDB, 0xC31C, 0xBDDC, 0xC324, 0xBDDD, 0xC325, 0xBDDE, 0xC328,\n\t0xBDDF, 0xC329, 0xBDE0, 0xC345, 0xBDE1, 0xC368, 0xBDE2, 0xC369,\n\t0xBDE3, 0xC36C, 0xBDE4, 0xC370, 0xBDE5, 0xC372, 0xBDE6, 0xC378,\n\t0xBDE7, 0xC379, 0xBDE8, 0xC37C, 0xBDE9, 0xC37D, 0xBDEA, 0xC384,\n\t0xBDEB, 0xC388, 0xBDEC, 0xC38C, 0xBDED, 0xC3C0, 0xBDEE, 0xC3D8,\n\t0xBDEF, 0xC3D9, 0xBDF0, 0xC3DC, 0xBDF1, 0xC3DF, 0xBDF2, 0xC3E0,\n\t0xBDF3, 0xC3E2, 0xBDF4, 0xC3E8, 0xBDF5, 0xC3E9, 0xBDF6, 0xC3ED,\n\t0xBDF7, 0xC3F4, 0xBDF8, 0xC3F5, 0xBDF9, 0xC3F8, 0xBDFA, 0xC408,\n\t0xBDFB, 0xC410, 0xBDFC, 0xC424, 0xBDFD, 0xC42C, 0xBDFE, 0xC430,\n\t0xBE41, 0xD438, 0xBE42, 0xD439, 0xBE43, 0xD43A, 0xBE44, 0xD43B,\n\t0xBE45, 0xD43C, 0xBE46, 0xD43D, 0xBE47, 0xD43E, 0xBE48, 0xD43F,\n\t0xBE49, 0xD441, 0xBE4A, 0xD442, 0xBE4B, 0xD443, 0xBE4C, 0xD445,\n\t0xBE4D, 0xD446, 0xBE4E, 0xD447, 0xBE4F, 0xD448, 0xBE50, 0xD449,\n\t0xBE51, 0xD44A, 0xBE52, 0xD44B, 0xBE53, 0xD44C, 0xBE54, 0xD44D,\n\t0xBE55, 0xD44E, 0xBE56, 0xD44F, 0xBE57, 0xD450, 0xBE58, 0xD451,\n\t0xBE59, 0xD452, 0xBE5A, 0xD453, 0xBE61, 0xD454, 0xBE62, 0xD455,\n\t0xBE63, 0xD456, 0xBE64, 0xD457, 0xBE65, 0xD458, 0xBE66, 0xD459,\n\t0xBE67, 0xD45A, 0xBE68, 0xD45B, 0xBE69, 0xD45D, 0xBE6A, 0xD45E,\n\t0xBE6B, 0xD45F, 0xBE6C, 0xD461, 0xBE6D, 0xD462, 0xBE6E, 0xD463,\n\t0xBE6F, 0xD465, 0xBE70, 0xD466, 0xBE71, 0xD467, 0xBE72, 0xD468,\n\t0xBE73, 0xD469, 0xBE74, 0xD46A, 0xBE75, 0xD46B, 0xBE76, 0xD46C,\n\t0xBE77, 0xD46E, 0xBE78, 0xD470, 0xBE79, 0xD471, 0xBE7A, 0xD472,\n\t0xBE81, 0xD473, 0xBE82, 0xD474, 0xBE83, 0xD475, 0xBE84, 0xD476,\n\t0xBE85, 0xD477, 0xBE86, 0xD47A, 0xBE87, 0xD47B, 0xBE88, 0xD47D,\n\t0xBE89, 0xD47E, 0xBE8A, 0xD481, 0xBE8B, 0xD483, 0xBE8C, 0xD484,\n\t0xBE8D, 0xD485, 0xBE8E, 0xD486, 0xBE8F, 0xD487, 0xBE90, 0xD48A,\n\t0xBE91, 0xD48C, 0xBE92, 0xD48E, 0xBE93, 0xD48F, 0xBE94, 0xD490,\n\t0xBE95, 0xD491, 0xBE96, 0xD492, 0xBE97, 0xD493, 0xBE98, 0xD495,\n\t0xBE99, 0xD496, 0xBE9A, 0xD497, 0xBE9B, 0xD498, 0xBE9C, 0xD499,\n\t0xBE9D, 0xD49A, 0xBE9E, 0xD49B, 0xBE9F, 0xD49C, 0xBEA0, 0xD49D,\n\t0xBEA1, 0xC434, 0xBEA2, 0xC43C, 0xBEA3, 0xC43D, 0xBEA4, 0xC448,\n\t0xBEA5, 0xC464, 0xBEA6, 0xC465, 0xBEA7, 0xC468, 0xBEA8, 0xC46C,\n\t0xBEA9, 0xC474, 0xBEAA, 0xC475, 0xBEAB, 0xC479, 0xBEAC, 0xC480,\n\t0xBEAD, 0xC494, 0xBEAE, 0xC49C, 0xBEAF, 0xC4B8, 0xBEB0, 0xC4BC,\n\t0xBEB1, 0xC4E9, 0xBEB2, 0xC4F0, 0xBEB3, 0xC4F1, 0xBEB4, 0xC4F4,\n\t0xBEB5, 0xC4F8, 0xBEB6, 0xC4FA, 0xBEB7, 0xC4FF, 0xBEB8, 0xC500,\n\t0xBEB9, 0xC501, 0xBEBA, 0xC50C, 0xBEBB, 0xC510, 0xBEBC, 0xC514,\n\t0xBEBD, 0xC51C, 0xBEBE, 0xC528, 0xBEBF, 0xC529, 0xBEC0, 0xC52C,\n\t0xBEC1, 0xC530, 0xBEC2, 0xC538, 0xBEC3, 0xC539, 0xBEC4, 0xC53B,\n\t0xBEC5, 0xC53D, 0xBEC6, 0xC544, 0xBEC7, 0xC545, 0xBEC8, 0xC548,\n\t0xBEC9, 0xC549, 0xBECA, 0xC54A, 0xBECB, 0xC54C, 0xBECC, 0xC54D,\n\t0xBECD, 0xC54E, 0xBECE, 0xC553, 0xBECF, 0xC554, 0xBED0, 0xC555,\n\t0xBED1, 0xC557, 0xBED2, 0xC558, 0xBED3, 0xC559, 0xBED4, 0xC55D,\n\t0xBED5, 0xC55E, 0xBED6, 0xC560, 0xBED7, 0xC561, 0xBED8, 0xC564,\n\t0xBED9, 0xC568, 0xBEDA, 0xC570, 0xBEDB, 0xC571, 0xBEDC, 0xC573,\n\t0xBEDD, 0xC574, 0xBEDE, 0xC575, 0xBEDF, 0xC57C, 0xBEE0, 0xC57D,\n\t0xBEE1, 0xC580, 0xBEE2, 0xC584, 0xBEE3, 0xC587, 0xBEE4, 0xC58C,\n\t0xBEE5, 0xC58D, 0xBEE6, 0xC58F, 0xBEE7, 0xC591, 0xBEE8, 0xC595,\n\t0xBEE9, 0xC597, 0xBEEA, 0xC598, 0xBEEB, 0xC59C, 0xBEEC, 0xC5A0,\n\t0xBEED, 0xC5A9, 0xBEEE, 0xC5B4, 0xBEEF, 0xC5B5, 0xBEF0, 0xC5B8,\n\t0xBEF1, 0xC5B9, 0xBEF2, 0xC5BB, 0xBEF3, 0xC5BC, 0xBEF4, 0xC5BD,\n\t0xBEF5, 0xC5BE, 0xBEF6, 0xC5C4, 0xBEF7, 0xC5C5, 0xBEF8, 0xC5C6,\n\t0xBEF9, 0xC5C7, 0xBEFA, 0xC5C8, 0xBEFB, 0xC5C9, 0xBEFC, 0xC5CA,\n\t0xBEFD, 0xC5CC, 0xBEFE, 0xC5CE, 0xBF41, 0xD49E, 0xBF42, 0xD49F,\n\t0xBF43, 0xD4A0, 0xBF44, 0xD4A1, 0xBF45, 0xD4A2, 0xBF46, 0xD4A3,\n\t0xBF47, 0xD4A4, 0xBF48, 0xD4A5, 0xBF49, 0xD4A6, 0xBF4A, 0xD4A7,\n\t0xBF4B, 0xD4A8, 0xBF4C, 0xD4AA, 0xBF4D, 0xD4AB, 0xBF4E, 0xD4AC,\n\t0xBF4F, 0xD4AD, 0xBF50, 0xD4AE, 0xBF51, 0xD4AF, 0xBF52, 0xD4B0,\n\t0xBF53, 0xD4B1, 0xBF54, 0xD4B2, 0xBF55, 0xD4B3, 0xBF56, 0xD4B4,\n\t0xBF57, 0xD4B5, 0xBF58, 0xD4B6, 0xBF59, 0xD4B7, 0xBF5A, 0xD4B8,\n\t0xBF61, 0xD4B9, 0xBF62, 0xD4BA, 0xBF63, 0xD4BB, 0xBF64, 0xD4BC,\n\t0xBF65, 0xD4BD, 0xBF66, 0xD4BE, 0xBF67, 0xD4BF, 0xBF68, 0xD4C0,\n\t0xBF69, 0xD4C1, 0xBF6A, 0xD4C2, 0xBF6B, 0xD4C3, 0xBF6C, 0xD4C4,\n\t0xBF6D, 0xD4C5, 0xBF6E, 0xD4C6, 0xBF6F, 0xD4C7, 0xBF70, 0xD4C8,\n\t0xBF71, 0xD4C9, 0xBF72, 0xD4CA, 0xBF73, 0xD4CB, 0xBF74, 0xD4CD,\n\t0xBF75, 0xD4CE, 0xBF76, 0xD4CF, 0xBF77, 0xD4D1, 0xBF78, 0xD4D2,\n\t0xBF79, 0xD4D3, 0xBF7A, 0xD4D5, 0xBF81, 0xD4D6, 0xBF82, 0xD4D7,\n\t0xBF83, 0xD4D8, 0xBF84, 0xD4D9, 0xBF85, 0xD4DA, 0xBF86, 0xD4DB,\n\t0xBF87, 0xD4DD, 0xBF88, 0xD4DE, 0xBF89, 0xD4E0, 0xBF8A, 0xD4E1,\n\t0xBF8B, 0xD4E2, 0xBF8C, 0xD4E3, 0xBF8D, 0xD4E4, 0xBF8E, 0xD4E5,\n\t0xBF8F, 0xD4E6, 0xBF90, 0xD4E7, 0xBF91, 0xD4E9, 0xBF92, 0xD4EA,\n\t0xBF93, 0xD4EB, 0xBF94, 0xD4ED, 0xBF95, 0xD4EE, 0xBF96, 0xD4EF,\n\t0xBF97, 0xD4F1, 0xBF98, 0xD4F2, 0xBF99, 0xD4F3, 0xBF9A, 0xD4F4,\n\t0xBF9B, 0xD4F5, 0xBF9C, 0xD4F6, 0xBF9D, 0xD4F7, 0xBF9E, 0xD4F9,\n\t0xBF9F, 0xD4FA, 0xBFA0, 0xD4FC, 0xBFA1, 0xC5D0, 0xBFA2, 0xC5D1,\n\t0xBFA3, 0xC5D4, 0xBFA4, 0xC5D8, 0xBFA5, 0xC5E0, 0xBFA6, 0xC5E1,\n\t0xBFA7, 0xC5E3, 0xBFA8, 0xC5E5, 0xBFA9, 0xC5EC, 0xBFAA, 0xC5ED,\n\t0xBFAB, 0xC5EE, 0xBFAC, 0xC5F0, 0xBFAD, 0xC5F4, 0xBFAE, 0xC5F6,\n\t0xBFAF, 0xC5F7, 0xBFB0, 0xC5FC, 0xBFB1, 0xC5FD, 0xBFB2, 0xC5FE,\n\t0xBFB3, 0xC5FF, 0xBFB4, 0xC600, 0xBFB5, 0xC601, 0xBFB6, 0xC605,\n\t0xBFB7, 0xC606, 0xBFB8, 0xC607, 0xBFB9, 0xC608, 0xBFBA, 0xC60C,\n\t0xBFBB, 0xC610, 0xBFBC, 0xC618, 0xBFBD, 0xC619, 0xBFBE, 0xC61B,\n\t0xBFBF, 0xC61C, 0xBFC0, 0xC624, 0xBFC1, 0xC625, 0xBFC2, 0xC628,\n\t0xBFC3, 0xC62C, 0xBFC4, 0xC62D, 0xBFC5, 0xC62E, 0xBFC6, 0xC630,\n\t0xBFC7, 0xC633, 0xBFC8, 0xC634, 0xBFC9, 0xC635, 0xBFCA, 0xC637,\n\t0xBFCB, 0xC639, 0xBFCC, 0xC63B, 0xBFCD, 0xC640, 0xBFCE, 0xC641,\n\t0xBFCF, 0xC644, 0xBFD0, 0xC648, 0xBFD1, 0xC650, 0xBFD2, 0xC651,\n\t0xBFD3, 0xC653, 0xBFD4, 0xC654, 0xBFD5, 0xC655, 0xBFD6, 0xC65C,\n\t0xBFD7, 0xC65D, 0xBFD8, 0xC660, 0xBFD9, 0xC66C, 0xBFDA, 0xC66F,\n\t0xBFDB, 0xC671, 0xBFDC, 0xC678, 0xBFDD, 0xC679, 0xBFDE, 0xC67C,\n\t0xBFDF, 0xC680, 0xBFE0, 0xC688, 0xBFE1, 0xC689, 0xBFE2, 0xC68B,\n\t0xBFE3, 0xC68D, 0xBFE4, 0xC694, 0xBFE5, 0xC695, 0xBFE6, 0xC698,\n\t0xBFE7, 0xC69C, 0xBFE8, 0xC6A4, 0xBFE9, 0xC6A5, 0xBFEA, 0xC6A7,\n\t0xBFEB, 0xC6A9, 0xBFEC, 0xC6B0, 0xBFED, 0xC6B1, 0xBFEE, 0xC6B4,\n\t0xBFEF, 0xC6B8, 0xBFF0, 0xC6B9, 0xBFF1, 0xC6BA, 0xBFF2, 0xC6C0,\n\t0xBFF3, 0xC6C1, 0xBFF4, 0xC6C3, 0xBFF5, 0xC6C5, 0xBFF6, 0xC6CC,\n\t0xBFF7, 0xC6CD, 0xBFF8, 0xC6D0, 0xBFF9, 0xC6D4, 0xBFFA, 0xC6DC,\n\t0xBFFB, 0xC6DD, 0xBFFC, 0xC6E0, 0xBFFD, 0xC6E1, 0xBFFE, 0xC6E8,\n\t0xC041, 0xD4FE, 0xC042, 0xD4FF, 0xC043, 0xD500, 0xC044, 0xD501,\n\t0xC045, 0xD502, 0xC046, 0xD503, 0xC047, 0xD505, 0xC048, 0xD506,\n\t0xC049, 0xD507, 0xC04A, 0xD509, 0xC04B, 0xD50A, 0xC04C, 0xD50B,\n\t0xC04D, 0xD50D, 0xC04E, 0xD50E, 0xC04F, 0xD50F, 0xC050, 0xD510,\n\t0xC051, 0xD511, 0xC052, 0xD512, 0xC053, 0xD513, 0xC054, 0xD516,\n\t0xC055, 0xD518, 0xC056, 0xD519, 0xC057, 0xD51A, 0xC058, 0xD51B,\n\t0xC059, 0xD51C, 0xC05A, 0xD51D, 0xC061, 0xD51E, 0xC062, 0xD51F,\n\t0xC063, 0xD520, 0xC064, 0xD521, 0xC065, 0xD522, 0xC066, 0xD523,\n\t0xC067, 0xD524, 0xC068, 0xD525, 0xC069, 0xD526, 0xC06A, 0xD527,\n\t0xC06B, 0xD528, 0xC06C, 0xD529, 0xC06D, 0xD52A, 0xC06E, 0xD52B,\n\t0xC06F, 0xD52C, 0xC070, 0xD52D, 0xC071, 0xD52E, 0xC072, 0xD52F,\n\t0xC073, 0xD530, 0xC074, 0xD531, 0xC075, 0xD532, 0xC076, 0xD533,\n\t0xC077, 0xD534, 0xC078, 0xD535, 0xC079, 0xD536, 0xC07A, 0xD537,\n\t0xC081, 0xD538, 0xC082, 0xD539, 0xC083, 0xD53A, 0xC084, 0xD53B,\n\t0xC085, 0xD53E, 0xC086, 0xD53F, 0xC087, 0xD541, 0xC088, 0xD542,\n\t0xC089, 0xD543, 0xC08A, 0xD545, 0xC08B, 0xD546, 0xC08C, 0xD547,\n\t0xC08D, 0xD548, 0xC08E, 0xD549, 0xC08F, 0xD54A, 0xC090, 0xD54B,\n\t0xC091, 0xD54E, 0xC092, 0xD550, 0xC093, 0xD552, 0xC094, 0xD553,\n\t0xC095, 0xD554, 0xC096, 0xD555, 0xC097, 0xD556, 0xC098, 0xD557,\n\t0xC099, 0xD55A, 0xC09A, 0xD55B, 0xC09B, 0xD55D, 0xC09C, 0xD55E,\n\t0xC09D, 0xD55F, 0xC09E, 0xD561, 0xC09F, 0xD562, 0xC0A0, 0xD563,\n\t0xC0A1, 0xC6E9, 0xC0A2, 0xC6EC, 0xC0A3, 0xC6F0, 0xC0A4, 0xC6F8,\n\t0xC0A5, 0xC6F9, 0xC0A6, 0xC6FD, 0xC0A7, 0xC704, 0xC0A8, 0xC705,\n\t0xC0A9, 0xC708, 0xC0AA, 0xC70C, 0xC0AB, 0xC714, 0xC0AC, 0xC715,\n\t0xC0AD, 0xC717, 0xC0AE, 0xC719, 0xC0AF, 0xC720, 0xC0B0, 0xC721,\n\t0xC0B1, 0xC724, 0xC0B2, 0xC728, 0xC0B3, 0xC730, 0xC0B4, 0xC731,\n\t0xC0B5, 0xC733, 0xC0B6, 0xC735, 0xC0B7, 0xC737, 0xC0B8, 0xC73C,\n\t0xC0B9, 0xC73D, 0xC0BA, 0xC740, 0xC0BB, 0xC744, 0xC0BC, 0xC74A,\n\t0xC0BD, 0xC74C, 0xC0BE, 0xC74D, 0xC0BF, 0xC74F, 0xC0C0, 0xC751,\n\t0xC0C1, 0xC752, 0xC0C2, 0xC753, 0xC0C3, 0xC754, 0xC0C4, 0xC755,\n\t0xC0C5, 0xC756, 0xC0C6, 0xC757, 0xC0C7, 0xC758, 0xC0C8, 0xC75C,\n\t0xC0C9, 0xC760, 0xC0CA, 0xC768, 0xC0CB, 0xC76B, 0xC0CC, 0xC774,\n\t0xC0CD, 0xC775, 0xC0CE, 0xC778, 0xC0CF, 0xC77C, 0xC0D0, 0xC77D,\n\t0xC0D1, 0xC77E, 0xC0D2, 0xC783, 0xC0D3, 0xC784, 0xC0D4, 0xC785,\n\t0xC0D5, 0xC787, 0xC0D6, 0xC788, 0xC0D7, 0xC789, 0xC0D8, 0xC78A,\n\t0xC0D9, 0xC78E, 0xC0DA, 0xC790, 0xC0DB, 0xC791, 0xC0DC, 0xC794,\n\t0xC0DD, 0xC796, 0xC0DE, 0xC797, 0xC0DF, 0xC798, 0xC0E0, 0xC79A,\n\t0xC0E1, 0xC7A0, 0xC0E2, 0xC7A1, 0xC0E3, 0xC7A3, 0xC0E4, 0xC7A4,\n\t0xC0E5, 0xC7A5, 0xC0E6, 0xC7A6, 0xC0E7, 0xC7AC, 0xC0E8, 0xC7AD,\n\t0xC0E9, 0xC7B0, 0xC0EA, 0xC7B4, 0xC0EB, 0xC7BC, 0xC0EC, 0xC7BD,\n\t0xC0ED, 0xC7BF, 0xC0EE, 0xC7C0, 0xC0EF, 0xC7C1, 0xC0F0, 0xC7C8,\n\t0xC0F1, 0xC7C9, 0xC0F2, 0xC7CC, 0xC0F3, 0xC7CE, 0xC0F4, 0xC7D0,\n\t0xC0F5, 0xC7D8, 0xC0F6, 0xC7DD, 0xC0F7, 0xC7E4, 0xC0F8, 0xC7E8,\n\t0xC0F9, 0xC7EC, 0xC0FA, 0xC800, 0xC0FB, 0xC801, 0xC0FC, 0xC804,\n\t0xC0FD, 0xC808, 0xC0FE, 0xC80A, 0xC141, 0xD564, 0xC142, 0xD566,\n\t0xC143, 0xD567, 0xC144, 0xD56A, 0xC145, 0xD56C, 0xC146, 0xD56E,\n\t0xC147, 0xD56F, 0xC148, 0xD570, 0xC149, 0xD571, 0xC14A, 0xD572,\n\t0xC14B, 0xD573, 0xC14C, 0xD576, 0xC14D, 0xD577, 0xC14E, 0xD579,\n\t0xC14F, 0xD57A, 0xC150, 0xD57B, 0xC151, 0xD57D, 0xC152, 0xD57E,\n\t0xC153, 0xD57F, 0xC154, 0xD580, 0xC155, 0xD581, 0xC156, 0xD582,\n\t0xC157, 0xD583, 0xC158, 0xD586, 0xC159, 0xD58A, 0xC15A, 0xD58B,\n\t0xC161, 0xD58C, 0xC162, 0xD58D, 0xC163, 0xD58E, 0xC164, 0xD58F,\n\t0xC165, 0xD591, 0xC166, 0xD592, 0xC167, 0xD593, 0xC168, 0xD594,\n\t0xC169, 0xD595, 0xC16A, 0xD596, 0xC16B, 0xD597, 0xC16C, 0xD598,\n\t0xC16D, 0xD599, 0xC16E, 0xD59A, 0xC16F, 0xD59B, 0xC170, 0xD59C,\n\t0xC171, 0xD59D, 0xC172, 0xD59E, 0xC173, 0xD59F, 0xC174, 0xD5A0,\n\t0xC175, 0xD5A1, 0xC176, 0xD5A2, 0xC177, 0xD5A3, 0xC178, 0xD5A4,\n\t0xC179, 0xD5A6, 0xC17A, 0xD5A7, 0xC181, 0xD5A8, 0xC182, 0xD5A9,\n\t0xC183, 0xD5AA, 0xC184, 0xD5AB, 0xC185, 0xD5AC, 0xC186, 0xD5AD,\n\t0xC187, 0xD5AE, 0xC188, 0xD5AF, 0xC189, 0xD5B0, 0xC18A, 0xD5B1,\n\t0xC18B, 0xD5B2, 0xC18C, 0xD5B3, 0xC18D, 0xD5B4, 0xC18E, 0xD5B5,\n\t0xC18F, 0xD5B6, 0xC190, 0xD5B7, 0xC191, 0xD5B8, 0xC192, 0xD5B9,\n\t0xC193, 0xD5BA, 0xC194, 0xD5BB, 0xC195, 0xD5BC, 0xC196, 0xD5BD,\n\t0xC197, 0xD5BE, 0xC198, 0xD5BF, 0xC199, 0xD5C0, 0xC19A, 0xD5C1,\n\t0xC19B, 0xD5C2, 0xC19C, 0xD5C3, 0xC19D, 0xD5C4, 0xC19E, 0xD5C5,\n\t0xC19F, 0xD5C6, 0xC1A0, 0xD5C7, 0xC1A1, 0xC810, 0xC1A2, 0xC811,\n\t0xC1A3, 0xC813, 0xC1A4, 0xC815, 0xC1A5, 0xC816, 0xC1A6, 0xC81C,\n\t0xC1A7, 0xC81D, 0xC1A8, 0xC820, 0xC1A9, 0xC824, 0xC1AA, 0xC82C,\n\t0xC1AB, 0xC82D, 0xC1AC, 0xC82F, 0xC1AD, 0xC831, 0xC1AE, 0xC838,\n\t0xC1AF, 0xC83C, 0xC1B0, 0xC840, 0xC1B1, 0xC848, 0xC1B2, 0xC849,\n\t0xC1B3, 0xC84C, 0xC1B4, 0xC84D, 0xC1B5, 0xC854, 0xC1B6, 0xC870,\n\t0xC1B7, 0xC871, 0xC1B8, 0xC874, 0xC1B9, 0xC878, 0xC1BA, 0xC87A,\n\t0xC1BB, 0xC880, 0xC1BC, 0xC881, 0xC1BD, 0xC883, 0xC1BE, 0xC885,\n\t0xC1BF, 0xC886, 0xC1C0, 0xC887, 0xC1C1, 0xC88B, 0xC1C2, 0xC88C,\n\t0xC1C3, 0xC88D, 0xC1C4, 0xC894, 0xC1C5, 0xC89D, 0xC1C6, 0xC89F,\n\t0xC1C7, 0xC8A1, 0xC1C8, 0xC8A8, 0xC1C9, 0xC8BC, 0xC1CA, 0xC8BD,\n\t0xC1CB, 0xC8C4, 0xC1CC, 0xC8C8, 0xC1CD, 0xC8CC, 0xC1CE, 0xC8D4,\n\t0xC1CF, 0xC8D5, 0xC1D0, 0xC8D7, 0xC1D1, 0xC8D9, 0xC1D2, 0xC8E0,\n\t0xC1D3, 0xC8E1, 0xC1D4, 0xC8E4, 0xC1D5, 0xC8F5, 0xC1D6, 0xC8FC,\n\t0xC1D7, 0xC8FD, 0xC1D8, 0xC900, 0xC1D9, 0xC904, 0xC1DA, 0xC905,\n\t0xC1DB, 0xC906, 0xC1DC, 0xC90C, 0xC1DD, 0xC90D, 0xC1DE, 0xC90F,\n\t0xC1DF, 0xC911, 0xC1E0, 0xC918, 0xC1E1, 0xC92C, 0xC1E2, 0xC934,\n\t0xC1E3, 0xC950, 0xC1E4, 0xC951, 0xC1E5, 0xC954, 0xC1E6, 0xC958,\n\t0xC1E7, 0xC960, 0xC1E8, 0xC961, 0xC1E9, 0xC963, 0xC1EA, 0xC96C,\n\t0xC1EB, 0xC970, 0xC1EC, 0xC974, 0xC1ED, 0xC97C, 0xC1EE, 0xC988,\n\t0xC1EF, 0xC989, 0xC1F0, 0xC98C, 0xC1F1, 0xC990, 0xC1F2, 0xC998,\n\t0xC1F3, 0xC999, 0xC1F4, 0xC99B, 0xC1F5, 0xC99D, 0xC1F6, 0xC9C0,\n\t0xC1F7, 0xC9C1, 0xC1F8, 0xC9C4, 0xC1F9, 0xC9C7, 0xC1FA, 0xC9C8,\n\t0xC1FB, 0xC9CA, 0xC1FC, 0xC9D0, 0xC1FD, 0xC9D1, 0xC1FE, 0xC9D3,\n\t0xC241, 0xD5CA, 0xC242, 0xD5CB, 0xC243, 0xD5CD, 0xC244, 0xD5CE,\n\t0xC245, 0xD5CF, 0xC246, 0xD5D1, 0xC247, 0xD5D3, 0xC248, 0xD5D4,\n\t0xC249, 0xD5D5, 0xC24A, 0xD5D6, 0xC24B, 0xD5D7, 0xC24C, 0xD5DA,\n\t0xC24D, 0xD5DC, 0xC24E, 0xD5DE, 0xC24F, 0xD5DF, 0xC250, 0xD5E0,\n\t0xC251, 0xD5E1, 0xC252, 0xD5E2, 0xC253, 0xD5E3, 0xC254, 0xD5E6,\n\t0xC255, 0xD5E7, 0xC256, 0xD5E9, 0xC257, 0xD5EA, 0xC258, 0xD5EB,\n\t0xC259, 0xD5ED, 0xC25A, 0xD5EE, 0xC261, 0xD5EF, 0xC262, 0xD5F0,\n\t0xC263, 0xD5F1, 0xC264, 0xD5F2, 0xC265, 0xD5F3, 0xC266, 0xD5F6,\n\t0xC267, 0xD5F8, 0xC268, 0xD5FA, 0xC269, 0xD5FB, 0xC26A, 0xD5FC,\n\t0xC26B, 0xD5FD, 0xC26C, 0xD5FE, 0xC26D, 0xD5FF, 0xC26E, 0xD602,\n\t0xC26F, 0xD603, 0xC270, 0xD605, 0xC271, 0xD606, 0xC272, 0xD607,\n\t0xC273, 0xD609, 0xC274, 0xD60A, 0xC275, 0xD60B, 0xC276, 0xD60C,\n\t0xC277, 0xD60D, 0xC278, 0xD60E, 0xC279, 0xD60F, 0xC27A, 0xD612,\n\t0xC281, 0xD616, 0xC282, 0xD617, 0xC283, 0xD618, 0xC284, 0xD619,\n\t0xC285, 0xD61A, 0xC286, 0xD61B, 0xC287, 0xD61D, 0xC288, 0xD61E,\n\t0xC289, 0xD61F, 0xC28A, 0xD621, 0xC28B, 0xD622, 0xC28C, 0xD623,\n\t0xC28D, 0xD625, 0xC28E, 0xD626, 0xC28F, 0xD627, 0xC290, 0xD628,\n\t0xC291, 0xD629, 0xC292, 0xD62A, 0xC293, 0xD62B, 0xC294, 0xD62C,\n\t0xC295, 0xD62E, 0xC296, 0xD62F, 0xC297, 0xD630, 0xC298, 0xD631,\n\t0xC299, 0xD632, 0xC29A, 0xD633, 0xC29B, 0xD634, 0xC29C, 0xD635,\n\t0xC29D, 0xD636, 0xC29E, 0xD637, 0xC29F, 0xD63A, 0xC2A0, 0xD63B,\n\t0xC2A1, 0xC9D5, 0xC2A2, 0xC9D6, 0xC2A3, 0xC9D9, 0xC2A4, 0xC9DA,\n\t0xC2A5, 0xC9DC, 0xC2A6, 0xC9DD, 0xC2A7, 0xC9E0, 0xC2A8, 0xC9E2,\n\t0xC2A9, 0xC9E4, 0xC2AA, 0xC9E7, 0xC2AB, 0xC9EC, 0xC2AC, 0xC9ED,\n\t0xC2AD, 0xC9EF, 0xC2AE, 0xC9F0, 0xC2AF, 0xC9F1, 0xC2B0, 0xC9F8,\n\t0xC2B1, 0xC9F9, 0xC2B2, 0xC9FC, 0xC2B3, 0xCA00, 0xC2B4, 0xCA08,\n\t0xC2B5, 0xCA09, 0xC2B6, 0xCA0B, 0xC2B7, 0xCA0C, 0xC2B8, 0xCA0D,\n\t0xC2B9, 0xCA14, 0xC2BA, 0xCA18, 0xC2BB, 0xCA29, 0xC2BC, 0xCA4C,\n\t0xC2BD, 0xCA4D, 0xC2BE, 0xCA50, 0xC2BF, 0xCA54, 0xC2C0, 0xCA5C,\n\t0xC2C1, 0xCA5D, 0xC2C2, 0xCA5F, 0xC2C3, 0xCA60, 0xC2C4, 0xCA61,\n\t0xC2C5, 0xCA68, 0xC2C6, 0xCA7D, 0xC2C7, 0xCA84, 0xC2C8, 0xCA98,\n\t0xC2C9, 0xCABC, 0xC2CA, 0xCABD, 0xC2CB, 0xCAC0, 0xC2CC, 0xCAC4,\n\t0xC2CD, 0xCACC, 0xC2CE, 0xCACD, 0xC2CF, 0xCACF, 0xC2D0, 0xCAD1,\n\t0xC2D1, 0xCAD3, 0xC2D2, 0xCAD8, 0xC2D3, 0xCAD9, 0xC2D4, 0xCAE0,\n\t0xC2D5, 0xCAEC, 0xC2D6, 0xCAF4, 0xC2D7, 0xCB08, 0xC2D8, 0xCB10,\n\t0xC2D9, 0xCB14, 0xC2DA, 0xCB18, 0xC2DB, 0xCB20, 0xC2DC, 0xCB21,\n\t0xC2DD, 0xCB41, 0xC2DE, 0xCB48, 0xC2DF, 0xCB49, 0xC2E0, 0xCB4C,\n\t0xC2E1, 0xCB50, 0xC2E2, 0xCB58, 0xC2E3, 0xCB59, 0xC2E4, 0xCB5D,\n\t0xC2E5, 0xCB64, 0xC2E6, 0xCB78, 0xC2E7, 0xCB79, 0xC2E8, 0xCB9C,\n\t0xC2E9, 0xCBB8, 0xC2EA, 0xCBD4, 0xC2EB, 0xCBE4, 0xC2EC, 0xCBE7,\n\t0xC2ED, 0xCBE9, 0xC2EE, 0xCC0C, 0xC2EF, 0xCC0D, 0xC2F0, 0xCC10,\n\t0xC2F1, 0xCC14, 0xC2F2, 0xCC1C, 0xC2F3, 0xCC1D, 0xC2F4, 0xCC21,\n\t0xC2F5, 0xCC22, 0xC2F6, 0xCC27, 0xC2F7, 0xCC28, 0xC2F8, 0xCC29,\n\t0xC2F9, 0xCC2C, 0xC2FA, 0xCC2E, 0xC2FB, 0xCC30, 0xC2FC, 0xCC38,\n\t0xC2FD, 0xCC39, 0xC2FE, 0xCC3B, 0xC341, 0xD63D, 0xC342, 0xD63E,\n\t0xC343, 0xD63F, 0xC344, 0xD641, 0xC345, 0xD642, 0xC346, 0xD643,\n\t0xC347, 0xD644, 0xC348, 0xD646, 0xC349, 0xD647, 0xC34A, 0xD64A,\n\t0xC34B, 0xD64C, 0xC34C, 0xD64E, 0xC34D, 0xD64F, 0xC34E, 0xD650,\n\t0xC34F, 0xD652, 0xC350, 0xD653, 0xC351, 0xD656, 0xC352, 0xD657,\n\t0xC353, 0xD659, 0xC354, 0xD65A, 0xC355, 0xD65B, 0xC356, 0xD65D,\n\t0xC357, 0xD65E, 0xC358, 0xD65F, 0xC359, 0xD660, 0xC35A, 0xD661,\n\t0xC361, 0xD662, 0xC362, 0xD663, 0xC363, 0xD664, 0xC364, 0xD665,\n\t0xC365, 0xD666, 0xC366, 0xD668, 0xC367, 0xD66A, 0xC368, 0xD66B,\n\t0xC369, 0xD66C, 0xC36A, 0xD66D, 0xC36B, 0xD66E, 0xC36C, 0xD66F,\n\t0xC36D, 0xD672, 0xC36E, 0xD673, 0xC36F, 0xD675, 0xC370, 0xD676,\n\t0xC371, 0xD677, 0xC372, 0xD678, 0xC373, 0xD679, 0xC374, 0xD67A,\n\t0xC375, 0xD67B, 0xC376, 0xD67C, 0xC377, 0xD67D, 0xC378, 0xD67E,\n\t0xC379, 0xD67F, 0xC37A, 0xD680, 0xC381, 0xD681, 0xC382, 0xD682,\n\t0xC383, 0xD684, 0xC384, 0xD686, 0xC385, 0xD687, 0xC386, 0xD688,\n\t0xC387, 0xD689, 0xC388, 0xD68A, 0xC389, 0xD68B, 0xC38A, 0xD68E,\n\t0xC38B, 0xD68F, 0xC38C, 0xD691, 0xC38D, 0xD692, 0xC38E, 0xD693,\n\t0xC38F, 0xD695, 0xC390, 0xD696, 0xC391, 0xD697, 0xC392, 0xD698,\n\t0xC393, 0xD699, 0xC394, 0xD69A, 0xC395, 0xD69B, 0xC396, 0xD69C,\n\t0xC397, 0xD69E, 0xC398, 0xD6A0, 0xC399, 0xD6A2, 0xC39A, 0xD6A3,\n\t0xC39B, 0xD6A4, 0xC39C, 0xD6A5, 0xC39D, 0xD6A6, 0xC39E, 0xD6A7,\n\t0xC39F, 0xD6A9, 0xC3A0, 0xD6AA, 0xC3A1, 0xCC3C, 0xC3A2, 0xCC3D,\n\t0xC3A3, 0xCC3E, 0xC3A4, 0xCC44, 0xC3A5, 0xCC45, 0xC3A6, 0xCC48,\n\t0xC3A7, 0xCC4C, 0xC3A8, 0xCC54, 0xC3A9, 0xCC55, 0xC3AA, 0xCC57,\n\t0xC3AB, 0xCC58, 0xC3AC, 0xCC59, 0xC3AD, 0xCC60, 0xC3AE, 0xCC64,\n\t0xC3AF, 0xCC66, 0xC3B0, 0xCC68, 0xC3B1, 0xCC70, 0xC3B2, 0xCC75,\n\t0xC3B3, 0xCC98, 0xC3B4, 0xCC99, 0xC3B5, 0xCC9C, 0xC3B6, 0xCCA0,\n\t0xC3B7, 0xCCA8, 0xC3B8, 0xCCA9, 0xC3B9, 0xCCAB, 0xC3BA, 0xCCAC,\n\t0xC3BB, 0xCCAD, 0xC3BC, 0xCCB4, 0xC3BD, 0xCCB5, 0xC3BE, 0xCCB8,\n\t0xC3BF, 0xCCBC, 0xC3C0, 0xCCC4, 0xC3C1, 0xCCC5, 0xC3C2, 0xCCC7,\n\t0xC3C3, 0xCCC9, 0xC3C4, 0xCCD0, 0xC3C5, 0xCCD4, 0xC3C6, 0xCCE4,\n\t0xC3C7, 0xCCEC, 0xC3C8, 0xCCF0, 0xC3C9, 0xCD01, 0xC3CA, 0xCD08,\n\t0xC3CB, 0xCD09, 0xC3CC, 0xCD0C, 0xC3CD, 0xCD10, 0xC3CE, 0xCD18,\n\t0xC3CF, 0xCD19, 0xC3D0, 0xCD1B, 0xC3D1, 0xCD1D, 0xC3D2, 0xCD24,\n\t0xC3D3, 0xCD28, 0xC3D4, 0xCD2C, 0xC3D5, 0xCD39, 0xC3D6, 0xCD5C,\n\t0xC3D7, 0xCD60, 0xC3D8, 0xCD64, 0xC3D9, 0xCD6C, 0xC3DA, 0xCD6D,\n\t0xC3DB, 0xCD6F, 0xC3DC, 0xCD71, 0xC3DD, 0xCD78, 0xC3DE, 0xCD88,\n\t0xC3DF, 0xCD94, 0xC3E0, 0xCD95, 0xC3E1, 0xCD98, 0xC3E2, 0xCD9C,\n\t0xC3E3, 0xCDA4, 0xC3E4, 0xCDA5, 0xC3E5, 0xCDA7, 0xC3E6, 0xCDA9,\n\t0xC3E7, 0xCDB0, 0xC3E8, 0xCDC4, 0xC3E9, 0xCDCC, 0xC3EA, 0xCDD0,\n\t0xC3EB, 0xCDE8, 0xC3EC, 0xCDEC, 0xC3ED, 0xCDF0, 0xC3EE, 0xCDF8,\n\t0xC3EF, 0xCDF9, 0xC3F0, 0xCDFB, 0xC3F1, 0xCDFD, 0xC3F2, 0xCE04,\n\t0xC3F3, 0xCE08, 0xC3F4, 0xCE0C, 0xC3F5, 0xCE14, 0xC3F6, 0xCE19,\n\t0xC3F7, 0xCE20, 0xC3F8, 0xCE21, 0xC3F9, 0xCE24, 0xC3FA, 0xCE28,\n\t0xC3FB, 0xCE30, 0xC3FC, 0xCE31, 0xC3FD, 0xCE33, 0xC3FE, 0xCE35,\n\t0xC441, 0xD6AB, 0xC442, 0xD6AD, 0xC443, 0xD6AE, 0xC444, 0xD6AF,\n\t0xC445, 0xD6B1, 0xC446, 0xD6B2, 0xC447, 0xD6B3, 0xC448, 0xD6B4,\n\t0xC449, 0xD6B5, 0xC44A, 0xD6B6, 0xC44B, 0xD6B7, 0xC44C, 0xD6B8,\n\t0xC44D, 0xD6BA, 0xC44E, 0xD6BC, 0xC44F, 0xD6BD, 0xC450, 0xD6BE,\n\t0xC451, 0xD6BF, 0xC452, 0xD6C0, 0xC453, 0xD6C1, 0xC454, 0xD6C2,\n\t0xC455, 0xD6C3, 0xC456, 0xD6C6, 0xC457, 0xD6C7, 0xC458, 0xD6C9,\n\t0xC459, 0xD6CA, 0xC45A, 0xD6CB, 0xC461, 0xD6CD, 0xC462, 0xD6CE,\n\t0xC463, 0xD6CF, 0xC464, 0xD6D0, 0xC465, 0xD6D2, 0xC466, 0xD6D3,\n\t0xC467, 0xD6D5, 0xC468, 0xD6D6, 0xC469, 0xD6D8, 0xC46A, 0xD6DA,\n\t0xC46B, 0xD6DB, 0xC46C, 0xD6DC, 0xC46D, 0xD6DD, 0xC46E, 0xD6DE,\n\t0xC46F, 0xD6DF, 0xC470, 0xD6E1, 0xC471, 0xD6E2, 0xC472, 0xD6E3,\n\t0xC473, 0xD6E5, 0xC474, 0xD6E6, 0xC475, 0xD6E7, 0xC476, 0xD6E9,\n\t0xC477, 0xD6EA, 0xC478, 0xD6EB, 0xC479, 0xD6EC, 0xC47A, 0xD6ED,\n\t0xC481, 0xD6EE, 0xC482, 0xD6EF, 0xC483, 0xD6F1, 0xC484, 0xD6F2,\n\t0xC485, 0xD6F3, 0xC486, 0xD6F4, 0xC487, 0xD6F6, 0xC488, 0xD6F7,\n\t0xC489, 0xD6F8, 0xC48A, 0xD6F9, 0xC48B, 0xD6FA, 0xC48C, 0xD6FB,\n\t0xC48D, 0xD6FE, 0xC48E, 0xD6FF, 0xC48F, 0xD701, 0xC490, 0xD702,\n\t0xC491, 0xD703, 0xC492, 0xD705, 0xC493, 0xD706, 0xC494, 0xD707,\n\t0xC495, 0xD708, 0xC496, 0xD709, 0xC497, 0xD70A, 0xC498, 0xD70B,\n\t0xC499, 0xD70C, 0xC49A, 0xD70D, 0xC49B, 0xD70E, 0xC49C, 0xD70F,\n\t0xC49D, 0xD710, 0xC49E, 0xD712, 0xC49F, 0xD713, 0xC4A0, 0xD714,\n\t0xC4A1, 0xCE58, 0xC4A2, 0xCE59, 0xC4A3, 0xCE5C, 0xC4A4, 0xCE5F,\n\t0xC4A5, 0xCE60, 0xC4A6, 0xCE61, 0xC4A7, 0xCE68, 0xC4A8, 0xCE69,\n\t0xC4A9, 0xCE6B, 0xC4AA, 0xCE6D, 0xC4AB, 0xCE74, 0xC4AC, 0xCE75,\n\t0xC4AD, 0xCE78, 0xC4AE, 0xCE7C, 0xC4AF, 0xCE84, 0xC4B0, 0xCE85,\n\t0xC4B1, 0xCE87, 0xC4B2, 0xCE89, 0xC4B3, 0xCE90, 0xC4B4, 0xCE91,\n\t0xC4B5, 0xCE94, 0xC4B6, 0xCE98, 0xC4B7, 0xCEA0, 0xC4B8, 0xCEA1,\n\t0xC4B9, 0xCEA3, 0xC4BA, 0xCEA4, 0xC4BB, 0xCEA5, 0xC4BC, 0xCEAC,\n\t0xC4BD, 0xCEAD, 0xC4BE, 0xCEC1, 0xC4BF, 0xCEE4, 0xC4C0, 0xCEE5,\n\t0xC4C1, 0xCEE8, 0xC4C2, 0xCEEB, 0xC4C3, 0xCEEC, 0xC4C4, 0xCEF4,\n\t0xC4C5, 0xCEF5, 0xC4C6, 0xCEF7, 0xC4C7, 0xCEF8, 0xC4C8, 0xCEF9,\n\t0xC4C9, 0xCF00, 0xC4CA, 0xCF01, 0xC4CB, 0xCF04, 0xC4CC, 0xCF08,\n\t0xC4CD, 0xCF10, 0xC4CE, 0xCF11, 0xC4CF, 0xCF13, 0xC4D0, 0xCF15,\n\t0xC4D1, 0xCF1C, 0xC4D2, 0xCF20, 0xC4D3, 0xCF24, 0xC4D4, 0xCF2C,\n\t0xC4D5, 0xCF2D, 0xC4D6, 0xCF2F, 0xC4D7, 0xCF30, 0xC4D8, 0xCF31,\n\t0xC4D9, 0xCF38, 0xC4DA, 0xCF54, 0xC4DB, 0xCF55, 0xC4DC, 0xCF58,\n\t0xC4DD, 0xCF5C, 0xC4DE, 0xCF64, 0xC4DF, 0xCF65, 0xC4E0, 0xCF67,\n\t0xC4E1, 0xCF69, 0xC4E2, 0xCF70, 0xC4E3, 0xCF71, 0xC4E4, 0xCF74,\n\t0xC4E5, 0xCF78, 0xC4E6, 0xCF80, 0xC4E7, 0xCF85, 0xC4E8, 0xCF8C,\n\t0xC4E9, 0xCFA1, 0xC4EA, 0xCFA8, 0xC4EB, 0xCFB0, 0xC4EC, 0xCFC4,\n\t0xC4ED, 0xCFE0, 0xC4EE, 0xCFE1, 0xC4EF, 0xCFE4, 0xC4F0, 0xCFE8,\n\t0xC4F1, 0xCFF0, 0xC4F2, 0xCFF1, 0xC4F3, 0xCFF3, 0xC4F4, 0xCFF5,\n\t0xC4F5, 0xCFFC, 0xC4F6, 0xD000, 0xC4F7, 0xD004, 0xC4F8, 0xD011,\n\t0xC4F9, 0xD018, 0xC4FA, 0xD02D, 0xC4FB, 0xD034, 0xC4FC, 0xD035,\n\t0xC4FD, 0xD038, 0xC4FE, 0xD03C, 0xC541, 0xD715, 0xC542, 0xD716,\n\t0xC543, 0xD717, 0xC544, 0xD71A, 0xC545, 0xD71B, 0xC546, 0xD71D,\n\t0xC547, 0xD71E, 0xC548, 0xD71F, 0xC549, 0xD721, 0xC54A, 0xD722,\n\t0xC54B, 0xD723, 0xC54C, 0xD724, 0xC54D, 0xD725, 0xC54E, 0xD726,\n\t0xC54F, 0xD727, 0xC550, 0xD72A, 0xC551, 0xD72C, 0xC552, 0xD72E,\n\t0xC553, 0xD72F, 0xC554, 0xD730, 0xC555, 0xD731, 0xC556, 0xD732,\n\t0xC557, 0xD733, 0xC558, 0xD736, 0xC559, 0xD737, 0xC55A, 0xD739,\n\t0xC561, 0xD73A, 0xC562, 0xD73B, 0xC563, 0xD73D, 0xC564, 0xD73E,\n\t0xC565, 0xD73F, 0xC566, 0xD740, 0xC567, 0xD741, 0xC568, 0xD742,\n\t0xC569, 0xD743, 0xC56A, 0xD745, 0xC56B, 0xD746, 0xC56C, 0xD748,\n\t0xC56D, 0xD74A, 0xC56E, 0xD74B, 0xC56F, 0xD74C, 0xC570, 0xD74D,\n\t0xC571, 0xD74E, 0xC572, 0xD74F, 0xC573, 0xD752, 0xC574, 0xD753,\n\t0xC575, 0xD755, 0xC576, 0xD75A, 0xC577, 0xD75B, 0xC578, 0xD75C,\n\t0xC579, 0xD75D, 0xC57A, 0xD75E, 0xC581, 0xD75F, 0xC582, 0xD762,\n\t0xC583, 0xD764, 0xC584, 0xD766, 0xC585, 0xD767, 0xC586, 0xD768,\n\t0xC587, 0xD76A, 0xC588, 0xD76B, 0xC589, 0xD76D, 0xC58A, 0xD76E,\n\t0xC58B, 0xD76F, 0xC58C, 0xD771, 0xC58D, 0xD772, 0xC58E, 0xD773,\n\t0xC58F, 0xD775, 0xC590, 0xD776, 0xC591, 0xD777, 0xC592, 0xD778,\n\t0xC593, 0xD779, 0xC594, 0xD77A, 0xC595, 0xD77B, 0xC596, 0xD77E,\n\t0xC597, 0xD77F, 0xC598, 0xD780, 0xC599, 0xD782, 0xC59A, 0xD783,\n\t0xC59B, 0xD784, 0xC59C, 0xD785, 0xC59D, 0xD786, 0xC59E, 0xD787,\n\t0xC59F, 0xD78A, 0xC5A0, 0xD78B, 0xC5A1, 0xD044, 0xC5A2, 0xD045,\n\t0xC5A3, 0xD047, 0xC5A4, 0xD049, 0xC5A5, 0xD050, 0xC5A6, 0xD054,\n\t0xC5A7, 0xD058, 0xC5A8, 0xD060, 0xC5A9, 0xD06C, 0xC5AA, 0xD06D,\n\t0xC5AB, 0xD070, 0xC5AC, 0xD074, 0xC5AD, 0xD07C, 0xC5AE, 0xD07D,\n\t0xC5AF, 0xD081, 0xC5B0, 0xD0A4, 0xC5B1, 0xD0A5, 0xC5B2, 0xD0A8,\n\t0xC5B3, 0xD0AC, 0xC5B4, 0xD0B4, 0xC5B5, 0xD0B5, 0xC5B6, 0xD0B7,\n\t0xC5B7, 0xD0B9, 0xC5B8, 0xD0C0, 0xC5B9, 0xD0C1, 0xC5BA, 0xD0C4,\n\t0xC5BB, 0xD0C8, 0xC5BC, 0xD0C9, 0xC5BD, 0xD0D0, 0xC5BE, 0xD0D1,\n\t0xC5BF, 0xD0D3, 0xC5C0, 0xD0D4, 0xC5C1, 0xD0D5, 0xC5C2, 0xD0DC,\n\t0xC5C3, 0xD0DD, 0xC5C4, 0xD0E0, 0xC5C5, 0xD0E4, 0xC5C6, 0xD0EC,\n\t0xC5C7, 0xD0ED, 0xC5C8, 0xD0EF, 0xC5C9, 0xD0F0, 0xC5CA, 0xD0F1,\n\t0xC5CB, 0xD0F8, 0xC5CC, 0xD10D, 0xC5CD, 0xD130, 0xC5CE, 0xD131,\n\t0xC5CF, 0xD134, 0xC5D0, 0xD138, 0xC5D1, 0xD13A, 0xC5D2, 0xD140,\n\t0xC5D3, 0xD141, 0xC5D4, 0xD143, 0xC5D5, 0xD144, 0xC5D6, 0xD145,\n\t0xC5D7, 0xD14C, 0xC5D8, 0xD14D, 0xC5D9, 0xD150, 0xC5DA, 0xD154,\n\t0xC5DB, 0xD15C, 0xC5DC, 0xD15D, 0xC5DD, 0xD15F, 0xC5DE, 0xD161,\n\t0xC5DF, 0xD168, 0xC5E0, 0xD16C, 0xC5E1, 0xD17C, 0xC5E2, 0xD184,\n\t0xC5E3, 0xD188, 0xC5E4, 0xD1A0, 0xC5E5, 0xD1A1, 0xC5E6, 0xD1A4,\n\t0xC5E7, 0xD1A8, 0xC5E8, 0xD1B0, 0xC5E9, 0xD1B1, 0xC5EA, 0xD1B3,\n\t0xC5EB, 0xD1B5, 0xC5EC, 0xD1BA, 0xC5ED, 0xD1BC, 0xC5EE, 0xD1C0,\n\t0xC5EF, 0xD1D8, 0xC5F0, 0xD1F4, 0xC5F1, 0xD1F8, 0xC5F2, 0xD207,\n\t0xC5F3, 0xD209, 0xC5F4, 0xD210, 0xC5F5, 0xD22C, 0xC5F6, 0xD22D,\n\t0xC5F7, 0xD230, 0xC5F8, 0xD234, 0xC5F9, 0xD23C, 0xC5FA, 0xD23D,\n\t0xC5FB, 0xD23F, 0xC5FC, 0xD241, 0xC5FD, 0xD248, 0xC5FE, 0xD25C,\n\t0xC641, 0xD78D, 0xC642, 0xD78E, 0xC643, 0xD78F, 0xC644, 0xD791,\n\t0xC645, 0xD792, 0xC646, 0xD793, 0xC647, 0xD794, 0xC648, 0xD795,\n\t0xC649, 0xD796, 0xC64A, 0xD797, 0xC64B, 0xD79A, 0xC64C, 0xD79C,\n\t0xC64D, 0xD79E, 0xC64E, 0xD79F, 0xC64F, 0xD7A0, 0xC650, 0xD7A1,\n\t0xC651, 0xD7A2, 0xC652, 0xD7A3, 0xC6A1, 0xD264, 0xC6A2, 0xD280,\n\t0xC6A3, 0xD281, 0xC6A4, 0xD284, 0xC6A5, 0xD288, 0xC6A6, 0xD290,\n\t0xC6A7, 0xD291, 0xC6A8, 0xD295, 0xC6A9, 0xD29C, 0xC6AA, 0xD2A0,\n\t0xC6AB, 0xD2A4, 0xC6AC, 0xD2AC, 0xC6AD, 0xD2B1, 0xC6AE, 0xD2B8,\n\t0xC6AF, 0xD2B9, 0xC6B0, 0xD2BC, 0xC6B1, 0xD2BF, 0xC6B2, 0xD2C0,\n\t0xC6B3, 0xD2C2, 0xC6B4, 0xD2C8, 0xC6B5, 0xD2C9, 0xC6B6, 0xD2CB,\n\t0xC6B7, 0xD2D4, 0xC6B8, 0xD2D8, 0xC6B9, 0xD2DC, 0xC6BA, 0xD2E4,\n\t0xC6BB, 0xD2E5, 0xC6BC, 0xD2F0, 0xC6BD, 0xD2F1, 0xC6BE, 0xD2F4,\n\t0xC6BF, 0xD2F8, 0xC6C0, 0xD300, 0xC6C1, 0xD301, 0xC6C2, 0xD303,\n\t0xC6C3, 0xD305, 0xC6C4, 0xD30C, 0xC6C5, 0xD30D, 0xC6C6, 0xD30E,\n\t0xC6C7, 0xD310, 0xC6C8, 0xD314, 0xC6C9, 0xD316, 0xC6CA, 0xD31C,\n\t0xC6CB, 0xD31D, 0xC6CC, 0xD31F, 0xC6CD, 0xD320, 0xC6CE, 0xD321,\n\t0xC6CF, 0xD325, 0xC6D0, 0xD328, 0xC6D1, 0xD329, 0xC6D2, 0xD32C,\n\t0xC6D3, 0xD330, 0xC6D4, 0xD338, 0xC6D5, 0xD339, 0xC6D6, 0xD33B,\n\t0xC6D7, 0xD33C, 0xC6D8, 0xD33D, 0xC6D9, 0xD344, 0xC6DA, 0xD345,\n\t0xC6DB, 0xD37C, 0xC6DC, 0xD37D, 0xC6DD, 0xD380, 0xC6DE, 0xD384,\n\t0xC6DF, 0xD38C, 0xC6E0, 0xD38D, 0xC6E1, 0xD38F, 0xC6E2, 0xD390,\n\t0xC6E3, 0xD391, 0xC6E4, 0xD398, 0xC6E5, 0xD399, 0xC6E6, 0xD39C,\n\t0xC6E7, 0xD3A0, 0xC6E8, 0xD3A8, 0xC6E9, 0xD3A9, 0xC6EA, 0xD3AB,\n\t0xC6EB, 0xD3AD, 0xC6EC, 0xD3B4, 0xC6ED, 0xD3B8, 0xC6EE, 0xD3BC,\n\t0xC6EF, 0xD3C4, 0xC6F0, 0xD3C5, 0xC6F1, 0xD3C8, 0xC6F2, 0xD3C9,\n\t0xC6F3, 0xD3D0, 0xC6F4, 0xD3D8, 0xC6F5, 0xD3E1, 0xC6F6, 0xD3E3,\n\t0xC6F7, 0xD3EC, 0xC6F8, 0xD3ED, 0xC6F9, 0xD3F0, 0xC6FA, 0xD3F4,\n\t0xC6FB, 0xD3FC, 0xC6FC, 0xD3FD, 0xC6FD, 0xD3FF, 0xC6FE, 0xD401,\n\t0xC7A1, 0xD408, 0xC7A2, 0xD41D, 0xC7A3, 0xD440, 0xC7A4, 0xD444,\n\t0xC7A5, 0xD45C, 0xC7A6, 0xD460, 0xC7A7, 0xD464, 0xC7A8, 0xD46D,\n\t0xC7A9, 0xD46F, 0xC7AA, 0xD478, 0xC7AB, 0xD479, 0xC7AC, 0xD47C,\n\t0xC7AD, 0xD47F, 0xC7AE, 0xD480, 0xC7AF, 0xD482, 0xC7B0, 0xD488,\n\t0xC7B1, 0xD489, 0xC7B2, 0xD48B, 0xC7B3, 0xD48D, 0xC7B4, 0xD494,\n\t0xC7B5, 0xD4A9, 0xC7B6, 0xD4CC, 0xC7B7, 0xD4D0, 0xC7B8, 0xD4D4,\n\t0xC7B9, 0xD4DC, 0xC7BA, 0xD4DF, 0xC7BB, 0xD4E8, 0xC7BC, 0xD4EC,\n\t0xC7BD, 0xD4F0, 0xC7BE, 0xD4F8, 0xC7BF, 0xD4FB, 0xC7C0, 0xD4FD,\n\t0xC7C1, 0xD504, 0xC7C2, 0xD508, 0xC7C3, 0xD50C, 0xC7C4, 0xD514,\n\t0xC7C5, 0xD515, 0xC7C6, 0xD517, 0xC7C7, 0xD53C, 0xC7C8, 0xD53D,\n\t0xC7C9, 0xD540, 0xC7CA, 0xD544, 0xC7CB, 0xD54C, 0xC7CC, 0xD54D,\n\t0xC7CD, 0xD54F, 0xC7CE, 0xD551, 0xC7CF, 0xD558, 0xC7D0, 0xD559,\n\t0xC7D1, 0xD55C, 0xC7D2, 0xD560, 0xC7D3, 0xD565, 0xC7D4, 0xD568,\n\t0xC7D5, 0xD569, 0xC7D6, 0xD56B, 0xC7D7, 0xD56D, 0xC7D8, 0xD574,\n\t0xC7D9, 0xD575, 0xC7DA, 0xD578, 0xC7DB, 0xD57C, 0xC7DC, 0xD584,\n\t0xC7DD, 0xD585, 0xC7DE, 0xD587, 0xC7DF, 0xD588, 0xC7E0, 0xD589,\n\t0xC7E1, 0xD590, 0xC7E2, 0xD5A5, 0xC7E3, 0xD5C8, 0xC7E4, 0xD5C9,\n\t0xC7E5, 0xD5CC, 0xC7E6, 0xD5D0, 0xC7E7, 0xD5D2, 0xC7E8, 0xD5D8,\n\t0xC7E9, 0xD5D9, 0xC7EA, 0xD5DB, 0xC7EB, 0xD5DD, 0xC7EC, 0xD5E4,\n\t0xC7ED, 0xD5E5, 0xC7EE, 0xD5E8, 0xC7EF, 0xD5EC, 0xC7F0, 0xD5F4,\n\t0xC7F1, 0xD5F5, 0xC7F2, 0xD5F7, 0xC7F3, 0xD5F9, 0xC7F4, 0xD600,\n\t0xC7F5, 0xD601, 0xC7F6, 0xD604, 0xC7F7, 0xD608, 0xC7F8, 0xD610,\n\t0xC7F9, 0xD611, 0xC7FA, 0xD613, 0xC7FB, 0xD614, 0xC7FC, 0xD615,\n\t0xC7FD, 0xD61C, 0xC7FE, 0xD620, 0xC8A1, 0xD624, 0xC8A2, 0xD62D,\n\t0xC8A3, 0xD638, 0xC8A4, 0xD639, 0xC8A5, 0xD63C, 0xC8A6, 0xD640,\n\t0xC8A7, 0xD645, 0xC8A8, 0xD648, 0xC8A9, 0xD649, 0xC8AA, 0xD64B,\n\t0xC8AB, 0xD64D, 0xC8AC, 0xD651, 0xC8AD, 0xD654, 0xC8AE, 0xD655,\n\t0xC8AF, 0xD658, 0xC8B0, 0xD65C, 0xC8B1, 0xD667, 0xC8B2, 0xD669,\n\t0xC8B3, 0xD670, 0xC8B4, 0xD671, 0xC8B5, 0xD674, 0xC8B6, 0xD683,\n\t0xC8B7, 0xD685, 0xC8B8, 0xD68C, 0xC8B9, 0xD68D, 0xC8BA, 0xD690,\n\t0xC8BB, 0xD694, 0xC8BC, 0xD69D, 0xC8BD, 0xD69F, 0xC8BE, 0xD6A1,\n\t0xC8BF, 0xD6A8, 0xC8C0, 0xD6AC, 0xC8C1, 0xD6B0, 0xC8C2, 0xD6B9,\n\t0xC8C3, 0xD6BB, 0xC8C4, 0xD6C4, 0xC8C5, 0xD6C5, 0xC8C6, 0xD6C8,\n\t0xC8C7, 0xD6CC, 0xC8C8, 0xD6D1, 0xC8C9, 0xD6D4, 0xC8CA, 0xD6D7,\n\t0xC8CB, 0xD6D9, 0xC8CC, 0xD6E0, 0xC8CD, 0xD6E4, 0xC8CE, 0xD6E8,\n\t0xC8CF, 0xD6F0, 0xC8D0, 0xD6F5, 0xC8D1, 0xD6FC, 0xC8D2, 0xD6FD,\n\t0xC8D3, 0xD700, 0xC8D4, 0xD704, 0xC8D5, 0xD711, 0xC8D6, 0xD718,\n\t0xC8D7, 0xD719, 0xC8D8, 0xD71C, 0xC8D9, 0xD720, 0xC8DA, 0xD728,\n\t0xC8DB, 0xD729, 0xC8DC, 0xD72B, 0xC8DD, 0xD72D, 0xC8DE, 0xD734,\n\t0xC8DF, 0xD735, 0xC8E0, 0xD738, 0xC8E1, 0xD73C, 0xC8E2, 0xD744,\n\t0xC8E3, 0xD747, 0xC8E4, 0xD749, 0xC8E5, 0xD750, 0xC8E6, 0xD751,\n\t0xC8E7, 0xD754, 0xC8E8, 0xD756, 0xC8E9, 0xD757, 0xC8EA, 0xD758,\n\t0xC8EB, 0xD759, 0xC8EC, 0xD760, 0xC8ED, 0xD761, 0xC8EE, 0xD763,\n\t0xC8EF, 0xD765, 0xC8F0, 0xD769, 0xC8F1, 0xD76C, 0xC8F2, 0xD770,\n\t0xC8F3, 0xD774, 0xC8F4, 0xD77C, 0xC8F5, 0xD77D, 0xC8F6, 0xD781,\n\t0xC8F7, 0xD788, 0xC8F8, 0xD789, 0xC8F9, 0xD78C, 0xC8FA, 0xD790,\n\t0xC8FB, 0xD798, 0xC8FC, 0xD799, 0xC8FD, 0xD79B, 0xC8FE, 0xD79D,\n\t0xCAA1, 0x4F3D, 0xCAA2, 0x4F73, 0xCAA3, 0x5047, 0xCAA4, 0x50F9,\n\t0xCAA5, 0x52A0, 0xCAA6, 0x53EF, 0xCAA7, 0x5475, 0xCAA8, 0x54E5,\n\t0xCAA9, 0x5609, 0xCAAA, 0x5AC1, 0xCAAB, 0x5BB6, 0xCAAC, 0x6687,\n\t0xCAAD, 0x67B6, 0xCAAE, 0x67B7, 0xCAAF, 0x67EF, 0xCAB0, 0x6B4C,\n\t0xCAB1, 0x73C2, 0xCAB2, 0x75C2, 0xCAB3, 0x7A3C, 0xCAB4, 0x82DB,\n\t0xCAB5, 0x8304, 0xCAB6, 0x8857, 0xCAB7, 0x8888, 0xCAB8, 0x8A36,\n\t0xCAB9, 0x8CC8, 0xCABA, 0x8DCF, 0xCABB, 0x8EFB, 0xCABC, 0x8FE6,\n\t0xCABD, 0x99D5, 0xCABE, 0x523B, 0xCABF, 0x5374, 0xCAC0, 0x5404,\n\t0xCAC1, 0x606A, 0xCAC2, 0x6164, 0xCAC3, 0x6BBC, 0xCAC4, 0x73CF,\n\t0xCAC5, 0x811A, 0xCAC6, 0x89BA, 0xCAC7, 0x89D2, 0xCAC8, 0x95A3,\n\t0xCAC9, 0x4F83, 0xCACA, 0x520A, 0xCACB, 0x58BE, 0xCACC, 0x5978,\n\t0xCACD, 0x59E6, 0xCACE, 0x5E72, 0xCACF, 0x5E79, 0xCAD0, 0x61C7,\n\t0xCAD1, 0x63C0, 0xCAD2, 0x6746, 0xCAD3, 0x67EC, 0xCAD4, 0x687F,\n\t0xCAD5, 0x6F97, 0xCAD6, 0x764E, 0xCAD7, 0x770B, 0xCAD8, 0x78F5,\n\t0xCAD9, 0x7A08, 0xCADA, 0x7AFF, 0xCADB, 0x7C21, 0xCADC, 0x809D,\n\t0xCADD, 0x826E, 0xCADE, 0x8271, 0xCADF, 0x8AEB, 0xCAE0, 0x9593,\n\t0xCAE1, 0x4E6B, 0xCAE2, 0x559D, 0xCAE3, 0x66F7, 0xCAE4, 0x6E34,\n\t0xCAE5, 0x78A3, 0xCAE6, 0x7AED, 0xCAE7, 0x845B, 0xCAE8, 0x8910,\n\t0xCAE9, 0x874E, 0xCAEA, 0x97A8, 0xCAEB, 0x52D8, 0xCAEC, 0x574E,\n\t0xCAED, 0x582A, 0xCAEE, 0x5D4C, 0xCAEF, 0x611F, 0xCAF0, 0x61BE,\n\t0xCAF1, 0x6221, 0xCAF2, 0x6562, 0xCAF3, 0x67D1, 0xCAF4, 0x6A44,\n\t0xCAF5, 0x6E1B, 0xCAF6, 0x7518, 0xCAF7, 0x75B3, 0xCAF8, 0x76E3,\n\t0xCAF9, 0x77B0, 0xCAFA, 0x7D3A, 0xCAFB, 0x90AF, 0xCAFC, 0x9451,\n\t0xCAFD, 0x9452, 0xCAFE, 0x9F95, 0xCBA1, 0x5323, 0xCBA2, 0x5CAC,\n\t0xCBA3, 0x7532, 0xCBA4, 0x80DB, 0xCBA5, 0x9240, 0xCBA6, 0x9598,\n\t0xCBA7, 0x525B, 0xCBA8, 0x5808, 0xCBA9, 0x59DC, 0xCBAA, 0x5CA1,\n\t0xCBAB, 0x5D17, 0xCBAC, 0x5EB7, 0xCBAD, 0x5F3A, 0xCBAE, 0x5F4A,\n\t0xCBAF, 0x6177, 0xCBB0, 0x6C5F, 0xCBB1, 0x757A, 0xCBB2, 0x7586,\n\t0xCBB3, 0x7CE0, 0xCBB4, 0x7D73, 0xCBB5, 0x7DB1, 0xCBB6, 0x7F8C,\n\t0xCBB7, 0x8154, 0xCBB8, 0x8221, 0xCBB9, 0x8591, 0xCBBA, 0x8941,\n\t0xCBBB, 0x8B1B, 0xCBBC, 0x92FC, 0xCBBD, 0x964D, 0xCBBE, 0x9C47,\n\t0xCBBF, 0x4ECB, 0xCBC0, 0x4EF7, 0xCBC1, 0x500B, 0xCBC2, 0x51F1,\n\t0xCBC3, 0x584F, 0xCBC4, 0x6137, 0xCBC5, 0x613E, 0xCBC6, 0x6168,\n\t0xCBC7, 0x6539, 0xCBC8, 0x69EA, 0xCBC9, 0x6F11, 0xCBCA, 0x75A5,\n\t0xCBCB, 0x7686, 0xCBCC, 0x76D6, 0xCBCD, 0x7B87, 0xCBCE, 0x82A5,\n\t0xCBCF, 0x84CB, 0xCBD0, 0xF900, 0xCBD1, 0x93A7, 0xCBD2, 0x958B,\n\t0xCBD3, 0x5580, 0xCBD4, 0x5BA2, 0xCBD5, 0x5751, 0xCBD6, 0xF901,\n\t0xCBD7, 0x7CB3, 0xCBD8, 0x7FB9, 0xCBD9, 0x91B5, 0xCBDA, 0x5028,\n\t0xCBDB, 0x53BB, 0xCBDC, 0x5C45, 0xCBDD, 0x5DE8, 0xCBDE, 0x62D2,\n\t0xCBDF, 0x636E, 0xCBE0, 0x64DA, 0xCBE1, 0x64E7, 0xCBE2, 0x6E20,\n\t0xCBE3, 0x70AC, 0xCBE4, 0x795B, 0xCBE5, 0x8DDD, 0xCBE6, 0x8E1E,\n\t0xCBE7, 0xF902, 0xCBE8, 0x907D, 0xCBE9, 0x9245, 0xCBEA, 0x92F8,\n\t0xCBEB, 0x4E7E, 0xCBEC, 0x4EF6, 0xCBED, 0x5065, 0xCBEE, 0x5DFE,\n\t0xCBEF, 0x5EFA, 0xCBF0, 0x6106, 0xCBF1, 0x6957, 0xCBF2, 0x8171,\n\t0xCBF3, 0x8654, 0xCBF4, 0x8E47, 0xCBF5, 0x9375, 0xCBF6, 0x9A2B,\n\t0xCBF7, 0x4E5E, 0xCBF8, 0x5091, 0xCBF9, 0x6770, 0xCBFA, 0x6840,\n\t0xCBFB, 0x5109, 0xCBFC, 0x528D, 0xCBFD, 0x5292, 0xCBFE, 0x6AA2,\n\t0xCCA1, 0x77BC, 0xCCA2, 0x9210, 0xCCA3, 0x9ED4, 0xCCA4, 0x52AB,\n\t0xCCA5, 0x602F, 0xCCA6, 0x8FF2, 0xCCA7, 0x5048, 0xCCA8, 0x61A9,\n\t0xCCA9, 0x63ED, 0xCCAA, 0x64CA, 0xCCAB, 0x683C, 0xCCAC, 0x6A84,\n\t0xCCAD, 0x6FC0, 0xCCAE, 0x8188, 0xCCAF, 0x89A1, 0xCCB0, 0x9694,\n\t0xCCB1, 0x5805, 0xCCB2, 0x727D, 0xCCB3, 0x72AC, 0xCCB4, 0x7504,\n\t0xCCB5, 0x7D79, 0xCCB6, 0x7E6D, 0xCCB7, 0x80A9, 0xCCB8, 0x898B,\n\t0xCCB9, 0x8B74, 0xCCBA, 0x9063, 0xCCBB, 0x9D51, 0xCCBC, 0x6289,\n\t0xCCBD, 0x6C7A, 0xCCBE, 0x6F54, 0xCCBF, 0x7D50, 0xCCC0, 0x7F3A,\n\t0xCCC1, 0x8A23, 0xCCC2, 0x517C, 0xCCC3, 0x614A, 0xCCC4, 0x7B9D,\n\t0xCCC5, 0x8B19, 0xCCC6, 0x9257, 0xCCC7, 0x938C, 0xCCC8, 0x4EAC,\n\t0xCCC9, 0x4FD3, 0xCCCA, 0x501E, 0xCCCB, 0x50BE, 0xCCCC, 0x5106,\n\t0xCCCD, 0x52C1, 0xCCCE, 0x52CD, 0xCCCF, 0x537F, 0xCCD0, 0x5770,\n\t0xCCD1, 0x5883, 0xCCD2, 0x5E9A, 0xCCD3, 0x5F91, 0xCCD4, 0x6176,\n\t0xCCD5, 0x61AC, 0xCCD6, 0x64CE, 0xCCD7, 0x656C, 0xCCD8, 0x666F,\n\t0xCCD9, 0x66BB, 0xCCDA, 0x66F4, 0xCCDB, 0x6897, 0xCCDC, 0x6D87,\n\t0xCCDD, 0x7085, 0xCCDE, 0x70F1, 0xCCDF, 0x749F, 0xCCE0, 0x74A5,\n\t0xCCE1, 0x74CA, 0xCCE2, 0x75D9, 0xCCE3, 0x786C, 0xCCE4, 0x78EC,\n\t0xCCE5, 0x7ADF, 0xCCE6, 0x7AF6, 0xCCE7, 0x7D45, 0xCCE8, 0x7D93,\n\t0xCCE9, 0x8015, 0xCCEA, 0x803F, 0xCCEB, 0x811B, 0xCCEC, 0x8396,\n\t0xCCED, 0x8B66, 0xCCEE, 0x8F15, 0xCCEF, 0x9015, 0xCCF0, 0x93E1,\n\t0xCCF1, 0x9803, 0xCCF2, 0x9838, 0xCCF3, 0x9A5A, 0xCCF4, 0x9BE8,\n\t0xCCF5, 0x4FC2, 0xCCF6, 0x5553, 0xCCF7, 0x583A, 0xCCF8, 0x5951,\n\t0xCCF9, 0x5B63, 0xCCFA, 0x5C46, 0xCCFB, 0x60B8, 0xCCFC, 0x6212,\n\t0xCCFD, 0x6842, 0xCCFE, 0x68B0, 0xCDA1, 0x68E8, 0xCDA2, 0x6EAA,\n\t0xCDA3, 0x754C, 0xCDA4, 0x7678, 0xCDA5, 0x78CE, 0xCDA6, 0x7A3D,\n\t0xCDA7, 0x7CFB, 0xCDA8, 0x7E6B, 0xCDA9, 0x7E7C, 0xCDAA, 0x8A08,\n\t0xCDAB, 0x8AA1, 0xCDAC, 0x8C3F, 0xCDAD, 0x968E, 0xCDAE, 0x9DC4,\n\t0xCDAF, 0x53E4, 0xCDB0, 0x53E9, 0xCDB1, 0x544A, 0xCDB2, 0x5471,\n\t0xCDB3, 0x56FA, 0xCDB4, 0x59D1, 0xCDB5, 0x5B64, 0xCDB6, 0x5C3B,\n\t0xCDB7, 0x5EAB, 0xCDB8, 0x62F7, 0xCDB9, 0x6537, 0xCDBA, 0x6545,\n\t0xCDBB, 0x6572, 0xCDBC, 0x66A0, 0xCDBD, 0x67AF, 0xCDBE, 0x69C1,\n\t0xCDBF, 0x6CBD, 0xCDC0, 0x75FC, 0xCDC1, 0x7690, 0xCDC2, 0x777E,\n\t0xCDC3, 0x7A3F, 0xCDC4, 0x7F94, 0xCDC5, 0x8003, 0xCDC6, 0x80A1,\n\t0xCDC7, 0x818F, 0xCDC8, 0x82E6, 0xCDC9, 0x82FD, 0xCDCA, 0x83F0,\n\t0xCDCB, 0x85C1, 0xCDCC, 0x8831, 0xCDCD, 0x88B4, 0xCDCE, 0x8AA5,\n\t0xCDCF, 0xF903, 0xCDD0, 0x8F9C, 0xCDD1, 0x932E, 0xCDD2, 0x96C7,\n\t0xCDD3, 0x9867, 0xCDD4, 0x9AD8, 0xCDD5, 0x9F13, 0xCDD6, 0x54ED,\n\t0xCDD7, 0x659B, 0xCDD8, 0x66F2, 0xCDD9, 0x688F, 0xCDDA, 0x7A40,\n\t0xCDDB, 0x8C37, 0xCDDC, 0x9D60, 0xCDDD, 0x56F0, 0xCDDE, 0x5764,\n\t0xCDDF, 0x5D11, 0xCDE0, 0x6606, 0xCDE1, 0x68B1, 0xCDE2, 0x68CD,\n\t0xCDE3, 0x6EFE, 0xCDE4, 0x7428, 0xCDE5, 0x889E, 0xCDE6, 0x9BE4,\n\t0xCDE7, 0x6C68, 0xCDE8, 0xF904, 0xCDE9, 0x9AA8, 0xCDEA, 0x4F9B,\n\t0xCDEB, 0x516C, 0xCDEC, 0x5171, 0xCDED, 0x529F, 0xCDEE, 0x5B54,\n\t0xCDEF, 0x5DE5, 0xCDF0, 0x6050, 0xCDF1, 0x606D, 0xCDF2, 0x62F1,\n\t0xCDF3, 0x63A7, 0xCDF4, 0x653B, 0xCDF5, 0x73D9, 0xCDF6, 0x7A7A,\n\t0xCDF7, 0x86A3, 0xCDF8, 0x8CA2, 0xCDF9, 0x978F, 0xCDFA, 0x4E32,\n\t0xCDFB, 0x5BE1, 0xCDFC, 0x6208, 0xCDFD, 0x679C, 0xCDFE, 0x74DC,\n\t0xCEA1, 0x79D1, 0xCEA2, 0x83D3, 0xCEA3, 0x8A87, 0xCEA4, 0x8AB2,\n\t0xCEA5, 0x8DE8, 0xCEA6, 0x904E, 0xCEA7, 0x934B, 0xCEA8, 0x9846,\n\t0xCEA9, 0x5ED3, 0xCEAA, 0x69E8, 0xCEAB, 0x85FF, 0xCEAC, 0x90ED,\n\t0xCEAD, 0xF905, 0xCEAE, 0x51A0, 0xCEAF, 0x5B98, 0xCEB0, 0x5BEC,\n\t0xCEB1, 0x6163, 0xCEB2, 0x68FA, 0xCEB3, 0x6B3E, 0xCEB4, 0x704C,\n\t0xCEB5, 0x742F, 0xCEB6, 0x74D8, 0xCEB7, 0x7BA1, 0xCEB8, 0x7F50,\n\t0xCEB9, 0x83C5, 0xCEBA, 0x89C0, 0xCEBB, 0x8CAB, 0xCEBC, 0x95DC,\n\t0xCEBD, 0x9928, 0xCEBE, 0x522E, 0xCEBF, 0x605D, 0xCEC0, 0x62EC,\n\t0xCEC1, 0x9002, 0xCEC2, 0x4F8A, 0xCEC3, 0x5149, 0xCEC4, 0x5321,\n\t0xCEC5, 0x58D9, 0xCEC6, 0x5EE3, 0xCEC7, 0x66E0, 0xCEC8, 0x6D38,\n\t0xCEC9, 0x709A, 0xCECA, 0x72C2, 0xCECB, 0x73D6, 0xCECC, 0x7B50,\n\t0xCECD, 0x80F1, 0xCECE, 0x945B, 0xCECF, 0x5366, 0xCED0, 0x639B,\n\t0xCED1, 0x7F6B, 0xCED2, 0x4E56, 0xCED3, 0x5080, 0xCED4, 0x584A,\n\t0xCED5, 0x58DE, 0xCED6, 0x602A, 0xCED7, 0x6127, 0xCED8, 0x62D0,\n\t0xCED9, 0x69D0, 0xCEDA, 0x9B41, 0xCEDB, 0x5B8F, 0xCEDC, 0x7D18,\n\t0xCEDD, 0x80B1, 0xCEDE, 0x8F5F, 0xCEDF, 0x4EA4, 0xCEE0, 0x50D1,\n\t0xCEE1, 0x54AC, 0xCEE2, 0x55AC, 0xCEE3, 0x5B0C, 0xCEE4, 0x5DA0,\n\t0xCEE5, 0x5DE7, 0xCEE6, 0x652A, 0xCEE7, 0x654E, 0xCEE8, 0x6821,\n\t0xCEE9, 0x6A4B, 0xCEEA, 0x72E1, 0xCEEB, 0x768E, 0xCEEC, 0x77EF,\n\t0xCEED, 0x7D5E, 0xCEEE, 0x7FF9, 0xCEEF, 0x81A0, 0xCEF0, 0x854E,\n\t0xCEF1, 0x86DF, 0xCEF2, 0x8F03, 0xCEF3, 0x8F4E, 0xCEF4, 0x90CA,\n\t0xCEF5, 0x9903, 0xCEF6, 0x9A55, 0xCEF7, 0x9BAB, 0xCEF8, 0x4E18,\n\t0xCEF9, 0x4E45, 0xCEFA, 0x4E5D, 0xCEFB, 0x4EC7, 0xCEFC, 0x4FF1,\n\t0xCEFD, 0x5177, 0xCEFE, 0x52FE, 0xCFA1, 0x5340, 0xCFA2, 0x53E3,\n\t0xCFA3, 0x53E5, 0xCFA4, 0x548E, 0xCFA5, 0x5614, 0xCFA6, 0x5775,\n\t0xCFA7, 0x57A2, 0xCFA8, 0x5BC7, 0xCFA9, 0x5D87, 0xCFAA, 0x5ED0,\n\t0xCFAB, 0x61FC, 0xCFAC, 0x62D8, 0xCFAD, 0x6551, 0xCFAE, 0x67B8,\n\t0xCFAF, 0x67E9, 0xCFB0, 0x69CB, 0xCFB1, 0x6B50, 0xCFB2, 0x6BC6,\n\t0xCFB3, 0x6BEC, 0xCFB4, 0x6C42, 0xCFB5, 0x6E9D, 0xCFB6, 0x7078,\n\t0xCFB7, 0x72D7, 0xCFB8, 0x7396, 0xCFB9, 0x7403, 0xCFBA, 0x77BF,\n\t0xCFBB, 0x77E9, 0xCFBC, 0x7A76, 0xCFBD, 0x7D7F, 0xCFBE, 0x8009,\n\t0xCFBF, 0x81FC, 0xCFC0, 0x8205, 0xCFC1, 0x820A, 0xCFC2, 0x82DF,\n\t0xCFC3, 0x8862, 0xCFC4, 0x8B33, 0xCFC5, 0x8CFC, 0xCFC6, 0x8EC0,\n\t0xCFC7, 0x9011, 0xCFC8, 0x90B1, 0xCFC9, 0x9264, 0xCFCA, 0x92B6,\n\t0xCFCB, 0x99D2, 0xCFCC, 0x9A45, 0xCFCD, 0x9CE9, 0xCFCE, 0x9DD7,\n\t0xCFCF, 0x9F9C, 0xCFD0, 0x570B, 0xCFD1, 0x5C40, 0xCFD2, 0x83CA,\n\t0xCFD3, 0x97A0, 0xCFD4, 0x97AB, 0xCFD5, 0x9EB4, 0xCFD6, 0x541B,\n\t0xCFD7, 0x7A98, 0xCFD8, 0x7FA4, 0xCFD9, 0x88D9, 0xCFDA, 0x8ECD,\n\t0xCFDB, 0x90E1, 0xCFDC, 0x5800, 0xCFDD, 0x5C48, 0xCFDE, 0x6398,\n\t0xCFDF, 0x7A9F, 0xCFE0, 0x5BAE, 0xCFE1, 0x5F13, 0xCFE2, 0x7A79,\n\t0xCFE3, 0x7AAE, 0xCFE4, 0x828E, 0xCFE5, 0x8EAC, 0xCFE6, 0x5026,\n\t0xCFE7, 0x5238, 0xCFE8, 0x52F8, 0xCFE9, 0x5377, 0xCFEA, 0x5708,\n\t0xCFEB, 0x62F3, 0xCFEC, 0x6372, 0xCFED, 0x6B0A, 0xCFEE, 0x6DC3,\n\t0xCFEF, 0x7737, 0xCFF0, 0x53A5, 0xCFF1, 0x7357, 0xCFF2, 0x8568,\n\t0xCFF3, 0x8E76, 0xCFF4, 0x95D5, 0xCFF5, 0x673A, 0xCFF6, 0x6AC3,\n\t0xCFF7, 0x6F70, 0xCFF8, 0x8A6D, 0xCFF9, 0x8ECC, 0xCFFA, 0x994B,\n\t0xCFFB, 0xF906, 0xCFFC, 0x6677, 0xCFFD, 0x6B78, 0xCFFE, 0x8CB4,\n\t0xD0A1, 0x9B3C, 0xD0A2, 0xF907, 0xD0A3, 0x53EB, 0xD0A4, 0x572D,\n\t0xD0A5, 0x594E, 0xD0A6, 0x63C6, 0xD0A7, 0x69FB, 0xD0A8, 0x73EA,\n\t0xD0A9, 0x7845, 0xD0AA, 0x7ABA, 0xD0AB, 0x7AC5, 0xD0AC, 0x7CFE,\n\t0xD0AD, 0x8475, 0xD0AE, 0x898F, 0xD0AF, 0x8D73, 0xD0B0, 0x9035,\n\t0xD0B1, 0x95A8, 0xD0B2, 0x52FB, 0xD0B3, 0x5747, 0xD0B4, 0x7547,\n\t0xD0B5, 0x7B60, 0xD0B6, 0x83CC, 0xD0B7, 0x921E, 0xD0B8, 0xF908,\n\t0xD0B9, 0x6A58, 0xD0BA, 0x514B, 0xD0BB, 0x524B, 0xD0BC, 0x5287,\n\t0xD0BD, 0x621F, 0xD0BE, 0x68D8, 0xD0BF, 0x6975, 0xD0C0, 0x9699,\n\t0xD0C1, 0x50C5, 0xD0C2, 0x52A4, 0xD0C3, 0x52E4, 0xD0C4, 0x61C3,\n\t0xD0C5, 0x65A4, 0xD0C6, 0x6839, 0xD0C7, 0x69FF, 0xD0C8, 0x747E,\n\t0xD0C9, 0x7B4B, 0xD0CA, 0x82B9, 0xD0CB, 0x83EB, 0xD0CC, 0x89B2,\n\t0xD0CD, 0x8B39, 0xD0CE, 0x8FD1, 0xD0CF, 0x9949, 0xD0D0, 0xF909,\n\t0xD0D1, 0x4ECA, 0xD0D2, 0x5997, 0xD0D3, 0x64D2, 0xD0D4, 0x6611,\n\t0xD0D5, 0x6A8E, 0xD0D6, 0x7434, 0xD0D7, 0x7981, 0xD0D8, 0x79BD,\n\t0xD0D9, 0x82A9, 0xD0DA, 0x887E, 0xD0DB, 0x887F, 0xD0DC, 0x895F,\n\t0xD0DD, 0xF90A, 0xD0DE, 0x9326, 0xD0DF, 0x4F0B, 0xD0E0, 0x53CA,\n\t0xD0E1, 0x6025, 0xD0E2, 0x6271, 0xD0E3, 0x6C72, 0xD0E4, 0x7D1A,\n\t0xD0E5, 0x7D66, 0xD0E6, 0x4E98, 0xD0E7, 0x5162, 0xD0E8, 0x77DC,\n\t0xD0E9, 0x80AF, 0xD0EA, 0x4F01, 0xD0EB, 0x4F0E, 0xD0EC, 0x5176,\n\t0xD0ED, 0x5180, 0xD0EE, 0x55DC, 0xD0EF, 0x5668, 0xD0F0, 0x573B,\n\t0xD0F1, 0x57FA, 0xD0F2, 0x57FC, 0xD0F3, 0x5914, 0xD0F4, 0x5947,\n\t0xD0F5, 0x5993, 0xD0F6, 0x5BC4, 0xD0F7, 0x5C90, 0xD0F8, 0x5D0E,\n\t0xD0F9, 0x5DF1, 0xD0FA, 0x5E7E, 0xD0FB, 0x5FCC, 0xD0FC, 0x6280,\n\t0xD0FD, 0x65D7, 0xD0FE, 0x65E3, 0xD1A1, 0x671E, 0xD1A2, 0x671F,\n\t0xD1A3, 0x675E, 0xD1A4, 0x68CB, 0xD1A5, 0x68C4, 0xD1A6, 0x6A5F,\n\t0xD1A7, 0x6B3A, 0xD1A8, 0x6C23, 0xD1A9, 0x6C7D, 0xD1AA, 0x6C82,\n\t0xD1AB, 0x6DC7, 0xD1AC, 0x7398, 0xD1AD, 0x7426, 0xD1AE, 0x742A,\n\t0xD1AF, 0x7482, 0xD1B0, 0x74A3, 0xD1B1, 0x7578, 0xD1B2, 0x757F,\n\t0xD1B3, 0x7881, 0xD1B4, 0x78EF, 0xD1B5, 0x7941, 0xD1B6, 0x7947,\n\t0xD1B7, 0x7948, 0xD1B8, 0x797A, 0xD1B9, 0x7B95, 0xD1BA, 0x7D00,\n\t0xD1BB, 0x7DBA, 0xD1BC, 0x7F88, 0xD1BD, 0x8006, 0xD1BE, 0x802D,\n\t0xD1BF, 0x808C, 0xD1C0, 0x8A18, 0xD1C1, 0x8B4F, 0xD1C2, 0x8C48,\n\t0xD1C3, 0x8D77, 0xD1C4, 0x9321, 0xD1C5, 0x9324, 0xD1C6, 0x98E2,\n\t0xD1C7, 0x9951, 0xD1C8, 0x9A0E, 0xD1C9, 0x9A0F, 0xD1CA, 0x9A65,\n\t0xD1CB, 0x9E92, 0xD1CC, 0x7DCA, 0xD1CD, 0x4F76, 0xD1CE, 0x5409,\n\t0xD1CF, 0x62EE, 0xD1D0, 0x6854, 0xD1D1, 0x91D1, 0xD1D2, 0x55AB,\n\t0xD1D3, 0x513A, 0xD1D4, 0xF90B, 0xD1D5, 0xF90C, 0xD1D6, 0x5A1C,\n\t0xD1D7, 0x61E6, 0xD1D8, 0xF90D, 0xD1D9, 0x62CF, 0xD1DA, 0x62FF,\n\t0xD1DB, 0xF90E, 0xD1DC, 0xF90F, 0xD1DD, 0xF910, 0xD1DE, 0xF911,\n\t0xD1DF, 0xF912, 0xD1E0, 0xF913, 0xD1E1, 0x90A3, 0xD1E2, 0xF914,\n\t0xD1E3, 0xF915, 0xD1E4, 0xF916, 0xD1E5, 0xF917, 0xD1E6, 0xF918,\n\t0xD1E7, 0x8AFE, 0xD1E8, 0xF919, 0xD1E9, 0xF91A, 0xD1EA, 0xF91B,\n\t0xD1EB, 0xF91C, 0xD1EC, 0x6696, 0xD1ED, 0xF91D, 0xD1EE, 0x7156,\n\t0xD1EF, 0xF91E, 0xD1F0, 0xF91F, 0xD1F1, 0x96E3, 0xD1F2, 0xF920,\n\t0xD1F3, 0x634F, 0xD1F4, 0x637A, 0xD1F5, 0x5357, 0xD1F6, 0xF921,\n\t0xD1F7, 0x678F, 0xD1F8, 0x6960, 0xD1F9, 0x6E73, 0xD1FA, 0xF922,\n\t0xD1FB, 0x7537, 0xD1FC, 0xF923, 0xD1FD, 0xF924, 0xD1FE, 0xF925,\n\t0xD2A1, 0x7D0D, 0xD2A2, 0xF926, 0xD2A3, 0xF927, 0xD2A4, 0x8872,\n\t0xD2A5, 0x56CA, 0xD2A6, 0x5A18, 0xD2A7, 0xF928, 0xD2A8, 0xF929,\n\t0xD2A9, 0xF92A, 0xD2AA, 0xF92B, 0xD2AB, 0xF92C, 0xD2AC, 0x4E43,\n\t0xD2AD, 0xF92D, 0xD2AE, 0x5167, 0xD2AF, 0x5948, 0xD2B0, 0x67F0,\n\t0xD2B1, 0x8010, 0xD2B2, 0xF92E, 0xD2B3, 0x5973, 0xD2B4, 0x5E74,\n\t0xD2B5, 0x649A, 0xD2B6, 0x79CA, 0xD2B7, 0x5FF5, 0xD2B8, 0x606C,\n\t0xD2B9, 0x62C8, 0xD2BA, 0x637B, 0xD2BB, 0x5BE7, 0xD2BC, 0x5BD7,\n\t0xD2BD, 0x52AA, 0xD2BE, 0xF92F, 0xD2BF, 0x5974, 0xD2C0, 0x5F29,\n\t0xD2C1, 0x6012, 0xD2C2, 0xF930, 0xD2C3, 0xF931, 0xD2C4, 0xF932,\n\t0xD2C5, 0x7459, 0xD2C6, 0xF933, 0xD2C7, 0xF934, 0xD2C8, 0xF935,\n\t0xD2C9, 0xF936, 0xD2CA, 0xF937, 0xD2CB, 0xF938, 0xD2CC, 0x99D1,\n\t0xD2CD, 0xF939, 0xD2CE, 0xF93A, 0xD2CF, 0xF93B, 0xD2D0, 0xF93C,\n\t0xD2D1, 0xF93D, 0xD2D2, 0xF93E, 0xD2D3, 0xF93F, 0xD2D4, 0xF940,\n\t0xD2D5, 0xF941, 0xD2D6, 0xF942, 0xD2D7, 0xF943, 0xD2D8, 0x6FC3,\n\t0xD2D9, 0xF944, 0xD2DA, 0xF945, 0xD2DB, 0x81BF, 0xD2DC, 0x8FB2,\n\t0xD2DD, 0x60F1, 0xD2DE, 0xF946, 0xD2DF, 0xF947, 0xD2E0, 0x8166,\n\t0xD2E1, 0xF948, 0xD2E2, 0xF949, 0xD2E3, 0x5C3F, 0xD2E4, 0xF94A,\n\t0xD2E5, 0xF94B, 0xD2E6, 0xF94C, 0xD2E7, 0xF94D, 0xD2E8, 0xF94E,\n\t0xD2E9, 0xF94F, 0xD2EA, 0xF950, 0xD2EB, 0xF951, 0xD2EC, 0x5AE9,\n\t0xD2ED, 0x8A25, 0xD2EE, 0x677B, 0xD2EF, 0x7D10, 0xD2F0, 0xF952,\n\t0xD2F1, 0xF953, 0xD2F2, 0xF954, 0xD2F3, 0xF955, 0xD2F4, 0xF956,\n\t0xD2F5, 0xF957, 0xD2F6, 0x80FD, 0xD2F7, 0xF958, 0xD2F8, 0xF959,\n\t0xD2F9, 0x5C3C, 0xD2FA, 0x6CE5, 0xD2FB, 0x533F, 0xD2FC, 0x6EBA,\n\t0xD2FD, 0x591A, 0xD2FE, 0x8336, 0xD3A1, 0x4E39, 0xD3A2, 0x4EB6,\n\t0xD3A3, 0x4F46, 0xD3A4, 0x55AE, 0xD3A5, 0x5718, 0xD3A6, 0x58C7,\n\t0xD3A7, 0x5F56, 0xD3A8, 0x65B7, 0xD3A9, 0x65E6, 0xD3AA, 0x6A80,\n\t0xD3AB, 0x6BB5, 0xD3AC, 0x6E4D, 0xD3AD, 0x77ED, 0xD3AE, 0x7AEF,\n\t0xD3AF, 0x7C1E, 0xD3B0, 0x7DDE, 0xD3B1, 0x86CB, 0xD3B2, 0x8892,\n\t0xD3B3, 0x9132, 0xD3B4, 0x935B, 0xD3B5, 0x64BB, 0xD3B6, 0x6FBE,\n\t0xD3B7, 0x737A, 0xD3B8, 0x75B8, 0xD3B9, 0x9054, 0xD3BA, 0x5556,\n\t0xD3BB, 0x574D, 0xD3BC, 0x61BA, 0xD3BD, 0x64D4, 0xD3BE, 0x66C7,\n\t0xD3BF, 0x6DE1, 0xD3C0, 0x6E5B, 0xD3C1, 0x6F6D, 0xD3C2, 0x6FB9,\n\t0xD3C3, 0x75F0, 0xD3C4, 0x8043, 0xD3C5, 0x81BD, 0xD3C6, 0x8541,\n\t0xD3C7, 0x8983, 0xD3C8, 0x8AC7, 0xD3C9, 0x8B5A, 0xD3CA, 0x931F,\n\t0xD3CB, 0x6C93, 0xD3CC, 0x7553, 0xD3CD, 0x7B54, 0xD3CE, 0x8E0F,\n\t0xD3CF, 0x905D, 0xD3D0, 0x5510, 0xD3D1, 0x5802, 0xD3D2, 0x5858,\n\t0xD3D3, 0x5E62, 0xD3D4, 0x6207, 0xD3D5, 0x649E, 0xD3D6, 0x68E0,\n\t0xD3D7, 0x7576, 0xD3D8, 0x7CD6, 0xD3D9, 0x87B3, 0xD3DA, 0x9EE8,\n\t0xD3DB, 0x4EE3, 0xD3DC, 0x5788, 0xD3DD, 0x576E, 0xD3DE, 0x5927,\n\t0xD3DF, 0x5C0D, 0xD3E0, 0x5CB1, 0xD3E1, 0x5E36, 0xD3E2, 0x5F85,\n\t0xD3E3, 0x6234, 0xD3E4, 0x64E1, 0xD3E5, 0x73B3, 0xD3E6, 0x81FA,\n\t0xD3E7, 0x888B, 0xD3E8, 0x8CB8, 0xD3E9, 0x968A, 0xD3EA, 0x9EDB,\n\t0xD3EB, 0x5B85, 0xD3EC, 0x5FB7, 0xD3ED, 0x60B3, 0xD3EE, 0x5012,\n\t0xD3EF, 0x5200, 0xD3F0, 0x5230, 0xD3F1, 0x5716, 0xD3F2, 0x5835,\n\t0xD3F3, 0x5857, 0xD3F4, 0x5C0E, 0xD3F5, 0x5C60, 0xD3F6, 0x5CF6,\n\t0xD3F7, 0x5D8B, 0xD3F8, 0x5EA6, 0xD3F9, 0x5F92, 0xD3FA, 0x60BC,\n\t0xD3FB, 0x6311, 0xD3FC, 0x6389, 0xD3FD, 0x6417, 0xD3FE, 0x6843,\n\t0xD4A1, 0x68F9, 0xD4A2, 0x6AC2, 0xD4A3, 0x6DD8, 0xD4A4, 0x6E21,\n\t0xD4A5, 0x6ED4, 0xD4A6, 0x6FE4, 0xD4A7, 0x71FE, 0xD4A8, 0x76DC,\n\t0xD4A9, 0x7779, 0xD4AA, 0x79B1, 0xD4AB, 0x7A3B, 0xD4AC, 0x8404,\n\t0xD4AD, 0x89A9, 0xD4AE, 0x8CED, 0xD4AF, 0x8DF3, 0xD4B0, 0x8E48,\n\t0xD4B1, 0x9003, 0xD4B2, 0x9014, 0xD4B3, 0x9053, 0xD4B4, 0x90FD,\n\t0xD4B5, 0x934D, 0xD4B6, 0x9676, 0xD4B7, 0x97DC, 0xD4B8, 0x6BD2,\n\t0xD4B9, 0x7006, 0xD4BA, 0x7258, 0xD4BB, 0x72A2, 0xD4BC, 0x7368,\n\t0xD4BD, 0x7763, 0xD4BE, 0x79BF, 0xD4BF, 0x7BE4, 0xD4C0, 0x7E9B,\n\t0xD4C1, 0x8B80, 0xD4C2, 0x58A9, 0xD4C3, 0x60C7, 0xD4C4, 0x6566,\n\t0xD4C5, 0x65FD, 0xD4C6, 0x66BE, 0xD4C7, 0x6C8C, 0xD4C8, 0x711E,\n\t0xD4C9, 0x71C9, 0xD4CA, 0x8C5A, 0xD4CB, 0x9813, 0xD4CC, 0x4E6D,\n\t0xD4CD, 0x7A81, 0xD4CE, 0x4EDD, 0xD4CF, 0x51AC, 0xD4D0, 0x51CD,\n\t0xD4D1, 0x52D5, 0xD4D2, 0x540C, 0xD4D3, 0x61A7, 0xD4D4, 0x6771,\n\t0xD4D5, 0x6850, 0xD4D6, 0x68DF, 0xD4D7, 0x6D1E, 0xD4D8, 0x6F7C,\n\t0xD4D9, 0x75BC, 0xD4DA, 0x77B3, 0xD4DB, 0x7AE5, 0xD4DC, 0x80F4,\n\t0xD4DD, 0x8463, 0xD4DE, 0x9285, 0xD4DF, 0x515C, 0xD4E0, 0x6597,\n\t0xD4E1, 0x675C, 0xD4E2, 0x6793, 0xD4E3, 0x75D8, 0xD4E4, 0x7AC7,\n\t0xD4E5, 0x8373, 0xD4E6, 0xF95A, 0xD4E7, 0x8C46, 0xD4E8, 0x9017,\n\t0xD4E9, 0x982D, 0xD4EA, 0x5C6F, 0xD4EB, 0x81C0, 0xD4EC, 0x829A,\n\t0xD4ED, 0x9041, 0xD4EE, 0x906F, 0xD4EF, 0x920D, 0xD4F0, 0x5F97,\n\t0xD4F1, 0x5D9D, 0xD4F2, 0x6A59, 0xD4F3, 0x71C8, 0xD4F4, 0x767B,\n\t0xD4F5, 0x7B49, 0xD4F6, 0x85E4, 0xD4F7, 0x8B04, 0xD4F8, 0x9127,\n\t0xD4F9, 0x9A30, 0xD4FA, 0x5587, 0xD4FB, 0x61F6, 0xD4FC, 0xF95B,\n\t0xD4FD, 0x7669, 0xD4FE, 0x7F85, 0xD5A1, 0x863F, 0xD5A2, 0x87BA,\n\t0xD5A3, 0x88F8, 0xD5A4, 0x908F, 0xD5A5, 0xF95C, 0xD5A6, 0x6D1B,\n\t0xD5A7, 0x70D9, 0xD5A8, 0x73DE, 0xD5A9, 0x7D61, 0xD5AA, 0x843D,\n\t0xD5AB, 0xF95D, 0xD5AC, 0x916A, 0xD5AD, 0x99F1, 0xD5AE, 0xF95E,\n\t0xD5AF, 0x4E82, 0xD5B0, 0x5375, 0xD5B1, 0x6B04, 0xD5B2, 0x6B12,\n\t0xD5B3, 0x703E, 0xD5B4, 0x721B, 0xD5B5, 0x862D, 0xD5B6, 0x9E1E,\n\t0xD5B7, 0x524C, 0xD5B8, 0x8FA3, 0xD5B9, 0x5D50, 0xD5BA, 0x64E5,\n\t0xD5BB, 0x652C, 0xD5BC, 0x6B16, 0xD5BD, 0x6FEB, 0xD5BE, 0x7C43,\n\t0xD5BF, 0x7E9C, 0xD5C0, 0x85CD, 0xD5C1, 0x8964, 0xD5C2, 0x89BD,\n\t0xD5C3, 0x62C9, 0xD5C4, 0x81D8, 0xD5C5, 0x881F, 0xD5C6, 0x5ECA,\n\t0xD5C7, 0x6717, 0xD5C8, 0x6D6A, 0xD5C9, 0x72FC, 0xD5CA, 0x7405,\n\t0xD5CB, 0x746F, 0xD5CC, 0x8782, 0xD5CD, 0x90DE, 0xD5CE, 0x4F86,\n\t0xD5CF, 0x5D0D, 0xD5D0, 0x5FA0, 0xD5D1, 0x840A, 0xD5D2, 0x51B7,\n\t0xD5D3, 0x63A0, 0xD5D4, 0x7565, 0xD5D5, 0x4EAE, 0xD5D6, 0x5006,\n\t0xD5D7, 0x5169, 0xD5D8, 0x51C9, 0xD5D9, 0x6881, 0xD5DA, 0x6A11,\n\t0xD5DB, 0x7CAE, 0xD5DC, 0x7CB1, 0xD5DD, 0x7CE7, 0xD5DE, 0x826F,\n\t0xD5DF, 0x8AD2, 0xD5E0, 0x8F1B, 0xD5E1, 0x91CF, 0xD5E2, 0x4FB6,\n\t0xD5E3, 0x5137, 0xD5E4, 0x52F5, 0xD5E5, 0x5442, 0xD5E6, 0x5EEC,\n\t0xD5E7, 0x616E, 0xD5E8, 0x623E, 0xD5E9, 0x65C5, 0xD5EA, 0x6ADA,\n\t0xD5EB, 0x6FFE, 0xD5EC, 0x792A, 0xD5ED, 0x85DC, 0xD5EE, 0x8823,\n\t0xD5EF, 0x95AD, 0xD5F0, 0x9A62, 0xD5F1, 0x9A6A, 0xD5F2, 0x9E97,\n\t0xD5F3, 0x9ECE, 0xD5F4, 0x529B, 0xD5F5, 0x66C6, 0xD5F6, 0x6B77,\n\t0xD5F7, 0x701D, 0xD5F8, 0x792B, 0xD5F9, 0x8F62, 0xD5FA, 0x9742,\n\t0xD5FB, 0x6190, 0xD5FC, 0x6200, 0xD5FD, 0x6523, 0xD5FE, 0x6F23,\n\t0xD6A1, 0x7149, 0xD6A2, 0x7489, 0xD6A3, 0x7DF4, 0xD6A4, 0x806F,\n\t0xD6A5, 0x84EE, 0xD6A6, 0x8F26, 0xD6A7, 0x9023, 0xD6A8, 0x934A,\n\t0xD6A9, 0x51BD, 0xD6AA, 0x5217, 0xD6AB, 0x52A3, 0xD6AC, 0x6D0C,\n\t0xD6AD, 0x70C8, 0xD6AE, 0x88C2, 0xD6AF, 0x5EC9, 0xD6B0, 0x6582,\n\t0xD6B1, 0x6BAE, 0xD6B2, 0x6FC2, 0xD6B3, 0x7C3E, 0xD6B4, 0x7375,\n\t0xD6B5, 0x4EE4, 0xD6B6, 0x4F36, 0xD6B7, 0x56F9, 0xD6B8, 0xF95F,\n\t0xD6B9, 0x5CBA, 0xD6BA, 0x5DBA, 0xD6BB, 0x601C, 0xD6BC, 0x73B2,\n\t0xD6BD, 0x7B2D, 0xD6BE, 0x7F9A, 0xD6BF, 0x7FCE, 0xD6C0, 0x8046,\n\t0xD6C1, 0x901E, 0xD6C2, 0x9234, 0xD6C3, 0x96F6, 0xD6C4, 0x9748,\n\t0xD6C5, 0x9818, 0xD6C6, 0x9F61, 0xD6C7, 0x4F8B, 0xD6C8, 0x6FA7,\n\t0xD6C9, 0x79AE, 0xD6CA, 0x91B4, 0xD6CB, 0x96B7, 0xD6CC, 0x52DE,\n\t0xD6CD, 0xF960, 0xD6CE, 0x6488, 0xD6CF, 0x64C4, 0xD6D0, 0x6AD3,\n\t0xD6D1, 0x6F5E, 0xD6D2, 0x7018, 0xD6D3, 0x7210, 0xD6D4, 0x76E7,\n\t0xD6D5, 0x8001, 0xD6D6, 0x8606, 0xD6D7, 0x865C, 0xD6D8, 0x8DEF,\n\t0xD6D9, 0x8F05, 0xD6DA, 0x9732, 0xD6DB, 0x9B6F, 0xD6DC, 0x9DFA,\n\t0xD6DD, 0x9E75, 0xD6DE, 0x788C, 0xD6DF, 0x797F, 0xD6E0, 0x7DA0,\n\t0xD6E1, 0x83C9, 0xD6E2, 0x9304, 0xD6E3, 0x9E7F, 0xD6E4, 0x9E93,\n\t0xD6E5, 0x8AD6, 0xD6E6, 0x58DF, 0xD6E7, 0x5F04, 0xD6E8, 0x6727,\n\t0xD6E9, 0x7027, 0xD6EA, 0x74CF, 0xD6EB, 0x7C60, 0xD6EC, 0x807E,\n\t0xD6ED, 0x5121, 0xD6EE, 0x7028, 0xD6EF, 0x7262, 0xD6F0, 0x78CA,\n\t0xD6F1, 0x8CC2, 0xD6F2, 0x8CDA, 0xD6F3, 0x8CF4, 0xD6F4, 0x96F7,\n\t0xD6F5, 0x4E86, 0xD6F6, 0x50DA, 0xD6F7, 0x5BEE, 0xD6F8, 0x5ED6,\n\t0xD6F9, 0x6599, 0xD6FA, 0x71CE, 0xD6FB, 0x7642, 0xD6FC, 0x77AD,\n\t0xD6FD, 0x804A, 0xD6FE, 0x84FC, 0xD7A1, 0x907C, 0xD7A2, 0x9B27,\n\t0xD7A3, 0x9F8D, 0xD7A4, 0x58D8, 0xD7A5, 0x5A41, 0xD7A6, 0x5C62,\n\t0xD7A7, 0x6A13, 0xD7A8, 0x6DDA, 0xD7A9, 0x6F0F, 0xD7AA, 0x763B,\n\t0xD7AB, 0x7D2F, 0xD7AC, 0x7E37, 0xD7AD, 0x851E, 0xD7AE, 0x8938,\n\t0xD7AF, 0x93E4, 0xD7B0, 0x964B, 0xD7B1, 0x5289, 0xD7B2, 0x65D2,\n\t0xD7B3, 0x67F3, 0xD7B4, 0x69B4, 0xD7B5, 0x6D41, 0xD7B6, 0x6E9C,\n\t0xD7B7, 0x700F, 0xD7B8, 0x7409, 0xD7B9, 0x7460, 0xD7BA, 0x7559,\n\t0xD7BB, 0x7624, 0xD7BC, 0x786B, 0xD7BD, 0x8B2C, 0xD7BE, 0x985E,\n\t0xD7BF, 0x516D, 0xD7C0, 0x622E, 0xD7C1, 0x9678, 0xD7C2, 0x4F96,\n\t0xD7C3, 0x502B, 0xD7C4, 0x5D19, 0xD7C5, 0x6DEA, 0xD7C6, 0x7DB8,\n\t0xD7C7, 0x8F2A, 0xD7C8, 0x5F8B, 0xD7C9, 0x6144, 0xD7CA, 0x6817,\n\t0xD7CB, 0xF961, 0xD7CC, 0x9686, 0xD7CD, 0x52D2, 0xD7CE, 0x808B,\n\t0xD7CF, 0x51DC, 0xD7D0, 0x51CC, 0xD7D1, 0x695E, 0xD7D2, 0x7A1C,\n\t0xD7D3, 0x7DBE, 0xD7D4, 0x83F1, 0xD7D5, 0x9675, 0xD7D6, 0x4FDA,\n\t0xD7D7, 0x5229, 0xD7D8, 0x5398, 0xD7D9, 0x540F, 0xD7DA, 0x550E,\n\t0xD7DB, 0x5C65, 0xD7DC, 0x60A7, 0xD7DD, 0x674E, 0xD7DE, 0x68A8,\n\t0xD7DF, 0x6D6C, 0xD7E0, 0x7281, 0xD7E1, 0x72F8, 0xD7E2, 0x7406,\n\t0xD7E3, 0x7483, 0xD7E4, 0xF962, 0xD7E5, 0x75E2, 0xD7E6, 0x7C6C,\n\t0xD7E7, 0x7F79, 0xD7E8, 0x7FB8, 0xD7E9, 0x8389, 0xD7EA, 0x88CF,\n\t0xD7EB, 0x88E1, 0xD7EC, 0x91CC, 0xD7ED, 0x91D0, 0xD7EE, 0x96E2,\n\t0xD7EF, 0x9BC9, 0xD7F0, 0x541D, 0xD7F1, 0x6F7E, 0xD7F2, 0x71D0,\n\t0xD7F3, 0x7498, 0xD7F4, 0x85FA, 0xD7F5, 0x8EAA, 0xD7F6, 0x96A3,\n\t0xD7F7, 0x9C57, 0xD7F8, 0x9E9F, 0xD7F9, 0x6797, 0xD7FA, 0x6DCB,\n\t0xD7FB, 0x7433, 0xD7FC, 0x81E8, 0xD7FD, 0x9716, 0xD7FE, 0x782C,\n\t0xD8A1, 0x7ACB, 0xD8A2, 0x7B20, 0xD8A3, 0x7C92, 0xD8A4, 0x6469,\n\t0xD8A5, 0x746A, 0xD8A6, 0x75F2, 0xD8A7, 0x78BC, 0xD8A8, 0x78E8,\n\t0xD8A9, 0x99AC, 0xD8AA, 0x9B54, 0xD8AB, 0x9EBB, 0xD8AC, 0x5BDE,\n\t0xD8AD, 0x5E55, 0xD8AE, 0x6F20, 0xD8AF, 0x819C, 0xD8B0, 0x83AB,\n\t0xD8B1, 0x9088, 0xD8B2, 0x4E07, 0xD8B3, 0x534D, 0xD8B4, 0x5A29,\n\t0xD8B5, 0x5DD2, 0xD8B6, 0x5F4E, 0xD8B7, 0x6162, 0xD8B8, 0x633D,\n\t0xD8B9, 0x6669, 0xD8BA, 0x66FC, 0xD8BB, 0x6EFF, 0xD8BC, 0x6F2B,\n\t0xD8BD, 0x7063, 0xD8BE, 0x779E, 0xD8BF, 0x842C, 0xD8C0, 0x8513,\n\t0xD8C1, 0x883B, 0xD8C2, 0x8F13, 0xD8C3, 0x9945, 0xD8C4, 0x9C3B,\n\t0xD8C5, 0x551C, 0xD8C6, 0x62B9, 0xD8C7, 0x672B, 0xD8C8, 0x6CAB,\n\t0xD8C9, 0x8309, 0xD8CA, 0x896A, 0xD8CB, 0x977A, 0xD8CC, 0x4EA1,\n\t0xD8CD, 0x5984, 0xD8CE, 0x5FD8, 0xD8CF, 0x5FD9, 0xD8D0, 0x671B,\n\t0xD8D1, 0x7DB2, 0xD8D2, 0x7F54, 0xD8D3, 0x8292, 0xD8D4, 0x832B,\n\t0xD8D5, 0x83BD, 0xD8D6, 0x8F1E, 0xD8D7, 0x9099, 0xD8D8, 0x57CB,\n\t0xD8D9, 0x59B9, 0xD8DA, 0x5A92, 0xD8DB, 0x5BD0, 0xD8DC, 0x6627,\n\t0xD8DD, 0x679A, 0xD8DE, 0x6885, 0xD8DF, 0x6BCF, 0xD8E0, 0x7164,\n\t0xD8E1, 0x7F75, 0xD8E2, 0x8CB7, 0xD8E3, 0x8CE3, 0xD8E4, 0x9081,\n\t0xD8E5, 0x9B45, 0xD8E6, 0x8108, 0xD8E7, 0x8C8A, 0xD8E8, 0x964C,\n\t0xD8E9, 0x9A40, 0xD8EA, 0x9EA5, 0xD8EB, 0x5B5F, 0xD8EC, 0x6C13,\n\t0xD8ED, 0x731B, 0xD8EE, 0x76F2, 0xD8EF, 0x76DF, 0xD8F0, 0x840C,\n\t0xD8F1, 0x51AA, 0xD8F2, 0x8993, 0xD8F3, 0x514D, 0xD8F4, 0x5195,\n\t0xD8F5, 0x52C9, 0xD8F6, 0x68C9, 0xD8F7, 0x6C94, 0xD8F8, 0x7704,\n\t0xD8F9, 0x7720, 0xD8FA, 0x7DBF, 0xD8FB, 0x7DEC, 0xD8FC, 0x9762,\n\t0xD8FD, 0x9EB5, 0xD8FE, 0x6EC5, 0xD9A1, 0x8511, 0xD9A2, 0x51A5,\n\t0xD9A3, 0x540D, 0xD9A4, 0x547D, 0xD9A5, 0x660E, 0xD9A6, 0x669D,\n\t0xD9A7, 0x6927, 0xD9A8, 0x6E9F, 0xD9A9, 0x76BF, 0xD9AA, 0x7791,\n\t0xD9AB, 0x8317, 0xD9AC, 0x84C2, 0xD9AD, 0x879F, 0xD9AE, 0x9169,\n\t0xD9AF, 0x9298, 0xD9B0, 0x9CF4, 0xD9B1, 0x8882, 0xD9B2, 0x4FAE,\n\t0xD9B3, 0x5192, 0xD9B4, 0x52DF, 0xD9B5, 0x59C6, 0xD9B6, 0x5E3D,\n\t0xD9B7, 0x6155, 0xD9B8, 0x6478, 0xD9B9, 0x6479, 0xD9BA, 0x66AE,\n\t0xD9BB, 0x67D0, 0xD9BC, 0x6A21, 0xD9BD, 0x6BCD, 0xD9BE, 0x6BDB,\n\t0xD9BF, 0x725F, 0xD9C0, 0x7261, 0xD9C1, 0x7441, 0xD9C2, 0x7738,\n\t0xD9C3, 0x77DB, 0xD9C4, 0x8017, 0xD9C5, 0x82BC, 0xD9C6, 0x8305,\n\t0xD9C7, 0x8B00, 0xD9C8, 0x8B28, 0xD9C9, 0x8C8C, 0xD9CA, 0x6728,\n\t0xD9CB, 0x6C90, 0xD9CC, 0x7267, 0xD9CD, 0x76EE, 0xD9CE, 0x7766,\n\t0xD9CF, 0x7A46, 0xD9D0, 0x9DA9, 0xD9D1, 0x6B7F, 0xD9D2, 0x6C92,\n\t0xD9D3, 0x5922, 0xD9D4, 0x6726, 0xD9D5, 0x8499, 0xD9D6, 0x536F,\n\t0xD9D7, 0x5893, 0xD9D8, 0x5999, 0xD9D9, 0x5EDF, 0xD9DA, 0x63CF,\n\t0xD9DB, 0x6634, 0xD9DC, 0x6773, 0xD9DD, 0x6E3A, 0xD9DE, 0x732B,\n\t0xD9DF, 0x7AD7, 0xD9E0, 0x82D7, 0xD9E1, 0x9328, 0xD9E2, 0x52D9,\n\t0xD9E3, 0x5DEB, 0xD9E4, 0x61AE, 0xD9E5, 0x61CB, 0xD9E6, 0x620A,\n\t0xD9E7, 0x62C7, 0xD9E8, 0x64AB, 0xD9E9, 0x65E0, 0xD9EA, 0x6959,\n\t0xD9EB, 0x6B66, 0xD9EC, 0x6BCB, 0xD9ED, 0x7121, 0xD9EE, 0x73F7,\n\t0xD9EF, 0x755D, 0xD9F0, 0x7E46, 0xD9F1, 0x821E, 0xD9F2, 0x8302,\n\t0xD9F3, 0x856A, 0xD9F4, 0x8AA3, 0xD9F5, 0x8CBF, 0xD9F6, 0x9727,\n\t0xD9F7, 0x9D61, 0xD9F8, 0x58A8, 0xD9F9, 0x9ED8, 0xD9FA, 0x5011,\n\t0xD9FB, 0x520E, 0xD9FC, 0x543B, 0xD9FD, 0x554F, 0xD9FE, 0x6587,\n\t0xDAA1, 0x6C76, 0xDAA2, 0x7D0A, 0xDAA3, 0x7D0B, 0xDAA4, 0x805E,\n\t0xDAA5, 0x868A, 0xDAA6, 0x9580, 0xDAA7, 0x96EF, 0xDAA8, 0x52FF,\n\t0xDAA9, 0x6C95, 0xDAAA, 0x7269, 0xDAAB, 0x5473, 0xDAAC, 0x5A9A,\n\t0xDAAD, 0x5C3E, 0xDAAE, 0x5D4B, 0xDAAF, 0x5F4C, 0xDAB0, 0x5FAE,\n\t0xDAB1, 0x672A, 0xDAB2, 0x68B6, 0xDAB3, 0x6963, 0xDAB4, 0x6E3C,\n\t0xDAB5, 0x6E44, 0xDAB6, 0x7709, 0xDAB7, 0x7C73, 0xDAB8, 0x7F8E,\n\t0xDAB9, 0x8587, 0xDABA, 0x8B0E, 0xDABB, 0x8FF7, 0xDABC, 0x9761,\n\t0xDABD, 0x9EF4, 0xDABE, 0x5CB7, 0xDABF, 0x60B6, 0xDAC0, 0x610D,\n\t0xDAC1, 0x61AB, 0xDAC2, 0x654F, 0xDAC3, 0x65FB, 0xDAC4, 0x65FC,\n\t0xDAC5, 0x6C11, 0xDAC6, 0x6CEF, 0xDAC7, 0x739F, 0xDAC8, 0x73C9,\n\t0xDAC9, 0x7DE1, 0xDACA, 0x9594, 0xDACB, 0x5BC6, 0xDACC, 0x871C,\n\t0xDACD, 0x8B10, 0xDACE, 0x525D, 0xDACF, 0x535A, 0xDAD0, 0x62CD,\n\t0xDAD1, 0x640F, 0xDAD2, 0x64B2, 0xDAD3, 0x6734, 0xDAD4, 0x6A38,\n\t0xDAD5, 0x6CCA, 0xDAD6, 0x73C0, 0xDAD7, 0x749E, 0xDAD8, 0x7B94,\n\t0xDAD9, 0x7C95, 0xDADA, 0x7E1B, 0xDADB, 0x818A, 0xDADC, 0x8236,\n\t0xDADD, 0x8584, 0xDADE, 0x8FEB, 0xDADF, 0x96F9, 0xDAE0, 0x99C1,\n\t0xDAE1, 0x4F34, 0xDAE2, 0x534A, 0xDAE3, 0x53CD, 0xDAE4, 0x53DB,\n\t0xDAE5, 0x62CC, 0xDAE6, 0x642C, 0xDAE7, 0x6500, 0xDAE8, 0x6591,\n\t0xDAE9, 0x69C3, 0xDAEA, 0x6CEE, 0xDAEB, 0x6F58, 0xDAEC, 0x73ED,\n\t0xDAED, 0x7554, 0xDAEE, 0x7622, 0xDAEF, 0x76E4, 0xDAF0, 0x76FC,\n\t0xDAF1, 0x78D0, 0xDAF2, 0x78FB, 0xDAF3, 0x792C, 0xDAF4, 0x7D46,\n\t0xDAF5, 0x822C, 0xDAF6, 0x87E0, 0xDAF7, 0x8FD4, 0xDAF8, 0x9812,\n\t0xDAF9, 0x98EF, 0xDAFA, 0x52C3, 0xDAFB, 0x62D4, 0xDAFC, 0x64A5,\n\t0xDAFD, 0x6E24, 0xDAFE, 0x6F51, 0xDBA1, 0x767C, 0xDBA2, 0x8DCB,\n\t0xDBA3, 0x91B1, 0xDBA4, 0x9262, 0xDBA5, 0x9AEE, 0xDBA6, 0x9B43,\n\t0xDBA7, 0x5023, 0xDBA8, 0x508D, 0xDBA9, 0x574A, 0xDBAA, 0x59A8,\n\t0xDBAB, 0x5C28, 0xDBAC, 0x5E47, 0xDBAD, 0x5F77, 0xDBAE, 0x623F,\n\t0xDBAF, 0x653E, 0xDBB0, 0x65B9, 0xDBB1, 0x65C1, 0xDBB2, 0x6609,\n\t0xDBB3, 0x678B, 0xDBB4, 0x699C, 0xDBB5, 0x6EC2, 0xDBB6, 0x78C5,\n\t0xDBB7, 0x7D21, 0xDBB8, 0x80AA, 0xDBB9, 0x8180, 0xDBBA, 0x822B,\n\t0xDBBB, 0x82B3, 0xDBBC, 0x84A1, 0xDBBD, 0x868C, 0xDBBE, 0x8A2A,\n\t0xDBBF, 0x8B17, 0xDBC0, 0x90A6, 0xDBC1, 0x9632, 0xDBC2, 0x9F90,\n\t0xDBC3, 0x500D, 0xDBC4, 0x4FF3, 0xDBC5, 0xF963, 0xDBC6, 0x57F9,\n\t0xDBC7, 0x5F98, 0xDBC8, 0x62DC, 0xDBC9, 0x6392, 0xDBCA, 0x676F,\n\t0xDBCB, 0x6E43, 0xDBCC, 0x7119, 0xDBCD, 0x76C3, 0xDBCE, 0x80CC,\n\t0xDBCF, 0x80DA, 0xDBD0, 0x88F4, 0xDBD1, 0x88F5, 0xDBD2, 0x8919,\n\t0xDBD3, 0x8CE0, 0xDBD4, 0x8F29, 0xDBD5, 0x914D, 0xDBD6, 0x966A,\n\t0xDBD7, 0x4F2F, 0xDBD8, 0x4F70, 0xDBD9, 0x5E1B, 0xDBDA, 0x67CF,\n\t0xDBDB, 0x6822, 0xDBDC, 0x767D, 0xDBDD, 0x767E, 0xDBDE, 0x9B44,\n\t0xDBDF, 0x5E61, 0xDBE0, 0x6A0A, 0xDBE1, 0x7169, 0xDBE2, 0x71D4,\n\t0xDBE3, 0x756A, 0xDBE4, 0xF964, 0xDBE5, 0x7E41, 0xDBE6, 0x8543,\n\t0xDBE7, 0x85E9, 0xDBE8, 0x98DC, 0xDBE9, 0x4F10, 0xDBEA, 0x7B4F,\n\t0xDBEB, 0x7F70, 0xDBEC, 0x95A5, 0xDBED, 0x51E1, 0xDBEE, 0x5E06,\n\t0xDBEF, 0x68B5, 0xDBF0, 0x6C3E, 0xDBF1, 0x6C4E, 0xDBF2, 0x6CDB,\n\t0xDBF3, 0x72AF, 0xDBF4, 0x7BC4, 0xDBF5, 0x8303, 0xDBF6, 0x6CD5,\n\t0xDBF7, 0x743A, 0xDBF8, 0x50FB, 0xDBF9, 0x5288, 0xDBFA, 0x58C1,\n\t0xDBFB, 0x64D8, 0xDBFC, 0x6A97, 0xDBFD, 0x74A7, 0xDBFE, 0x7656,\n\t0xDCA1, 0x78A7, 0xDCA2, 0x8617, 0xDCA3, 0x95E2, 0xDCA4, 0x9739,\n\t0xDCA5, 0xF965, 0xDCA6, 0x535E, 0xDCA7, 0x5F01, 0xDCA8, 0x8B8A,\n\t0xDCA9, 0x8FA8, 0xDCAA, 0x8FAF, 0xDCAB, 0x908A, 0xDCAC, 0x5225,\n\t0xDCAD, 0x77A5, 0xDCAE, 0x9C49, 0xDCAF, 0x9F08, 0xDCB0, 0x4E19,\n\t0xDCB1, 0x5002, 0xDCB2, 0x5175, 0xDCB3, 0x5C5B, 0xDCB4, 0x5E77,\n\t0xDCB5, 0x661E, 0xDCB6, 0x663A, 0xDCB7, 0x67C4, 0xDCB8, 0x68C5,\n\t0xDCB9, 0x70B3, 0xDCBA, 0x7501, 0xDCBB, 0x75C5, 0xDCBC, 0x79C9,\n\t0xDCBD, 0x7ADD, 0xDCBE, 0x8F27, 0xDCBF, 0x9920, 0xDCC0, 0x9A08,\n\t0xDCC1, 0x4FDD, 0xDCC2, 0x5821, 0xDCC3, 0x5831, 0xDCC4, 0x5BF6,\n\t0xDCC5, 0x666E, 0xDCC6, 0x6B65, 0xDCC7, 0x6D11, 0xDCC8, 0x6E7A,\n\t0xDCC9, 0x6F7D, 0xDCCA, 0x73E4, 0xDCCB, 0x752B, 0xDCCC, 0x83E9,\n\t0xDCCD, 0x88DC, 0xDCCE, 0x8913, 0xDCCF, 0x8B5C, 0xDCD0, 0x8F14,\n\t0xDCD1, 0x4F0F, 0xDCD2, 0x50D5, 0xDCD3, 0x5310, 0xDCD4, 0x535C,\n\t0xDCD5, 0x5B93, 0xDCD6, 0x5FA9, 0xDCD7, 0x670D, 0xDCD8, 0x798F,\n\t0xDCD9, 0x8179, 0xDCDA, 0x832F, 0xDCDB, 0x8514, 0xDCDC, 0x8907,\n\t0xDCDD, 0x8986, 0xDCDE, 0x8F39, 0xDCDF, 0x8F3B, 0xDCE0, 0x99A5,\n\t0xDCE1, 0x9C12, 0xDCE2, 0x672C, 0xDCE3, 0x4E76, 0xDCE4, 0x4FF8,\n\t0xDCE5, 0x5949, 0xDCE6, 0x5C01, 0xDCE7, 0x5CEF, 0xDCE8, 0x5CF0,\n\t0xDCE9, 0x6367, 0xDCEA, 0x68D2, 0xDCEB, 0x70FD, 0xDCEC, 0x71A2,\n\t0xDCED, 0x742B, 0xDCEE, 0x7E2B, 0xDCEF, 0x84EC, 0xDCF0, 0x8702,\n\t0xDCF1, 0x9022, 0xDCF2, 0x92D2, 0xDCF3, 0x9CF3, 0xDCF4, 0x4E0D,\n\t0xDCF5, 0x4ED8, 0xDCF6, 0x4FEF, 0xDCF7, 0x5085, 0xDCF8, 0x5256,\n\t0xDCF9, 0x526F, 0xDCFA, 0x5426, 0xDCFB, 0x5490, 0xDCFC, 0x57E0,\n\t0xDCFD, 0x592B, 0xDCFE, 0x5A66, 0xDDA1, 0x5B5A, 0xDDA2, 0x5B75,\n\t0xDDA3, 0x5BCC, 0xDDA4, 0x5E9C, 0xDDA5, 0xF966, 0xDDA6, 0x6276,\n\t0xDDA7, 0x6577, 0xDDA8, 0x65A7, 0xDDA9, 0x6D6E, 0xDDAA, 0x6EA5,\n\t0xDDAB, 0x7236, 0xDDAC, 0x7B26, 0xDDAD, 0x7C3F, 0xDDAE, 0x7F36,\n\t0xDDAF, 0x8150, 0xDDB0, 0x8151, 0xDDB1, 0x819A, 0xDDB2, 0x8240,\n\t0xDDB3, 0x8299, 0xDDB4, 0x83A9, 0xDDB5, 0x8A03, 0xDDB6, 0x8CA0,\n\t0xDDB7, 0x8CE6, 0xDDB8, 0x8CFB, 0xDDB9, 0x8D74, 0xDDBA, 0x8DBA,\n\t0xDDBB, 0x90E8, 0xDDBC, 0x91DC, 0xDDBD, 0x961C, 0xDDBE, 0x9644,\n\t0xDDBF, 0x99D9, 0xDDC0, 0x9CE7, 0xDDC1, 0x5317, 0xDDC2, 0x5206,\n\t0xDDC3, 0x5429, 0xDDC4, 0x5674, 0xDDC5, 0x58B3, 0xDDC6, 0x5954,\n\t0xDDC7, 0x596E, 0xDDC8, 0x5FFF, 0xDDC9, 0x61A4, 0xDDCA, 0x626E,\n\t0xDDCB, 0x6610, 0xDDCC, 0x6C7E, 0xDDCD, 0x711A, 0xDDCE, 0x76C6,\n\t0xDDCF, 0x7C89, 0xDDD0, 0x7CDE, 0xDDD1, 0x7D1B, 0xDDD2, 0x82AC,\n\t0xDDD3, 0x8CC1, 0xDDD4, 0x96F0, 0xDDD5, 0xF967, 0xDDD6, 0x4F5B,\n\t0xDDD7, 0x5F17, 0xDDD8, 0x5F7F, 0xDDD9, 0x62C2, 0xDDDA, 0x5D29,\n\t0xDDDB, 0x670B, 0xDDDC, 0x68DA, 0xDDDD, 0x787C, 0xDDDE, 0x7E43,\n\t0xDDDF, 0x9D6C, 0xDDE0, 0x4E15, 0xDDE1, 0x5099, 0xDDE2, 0x5315,\n\t0xDDE3, 0x532A, 0xDDE4, 0x5351, 0xDDE5, 0x5983, 0xDDE6, 0x5A62,\n\t0xDDE7, 0x5E87, 0xDDE8, 0x60B2, 0xDDE9, 0x618A, 0xDDEA, 0x6249,\n\t0xDDEB, 0x6279, 0xDDEC, 0x6590, 0xDDED, 0x6787, 0xDDEE, 0x69A7,\n\t0xDDEF, 0x6BD4, 0xDDF0, 0x6BD6, 0xDDF1, 0x6BD7, 0xDDF2, 0x6BD8,\n\t0xDDF3, 0x6CB8, 0xDDF4, 0xF968, 0xDDF5, 0x7435, 0xDDF6, 0x75FA,\n\t0xDDF7, 0x7812, 0xDDF8, 0x7891, 0xDDF9, 0x79D5, 0xDDFA, 0x79D8,\n\t0xDDFB, 0x7C83, 0xDDFC, 0x7DCB, 0xDDFD, 0x7FE1, 0xDDFE, 0x80A5,\n\t0xDEA1, 0x813E, 0xDEA2, 0x81C2, 0xDEA3, 0x83F2, 0xDEA4, 0x871A,\n\t0xDEA5, 0x88E8, 0xDEA6, 0x8AB9, 0xDEA7, 0x8B6C, 0xDEA8, 0x8CBB,\n\t0xDEA9, 0x9119, 0xDEAA, 0x975E, 0xDEAB, 0x98DB, 0xDEAC, 0x9F3B,\n\t0xDEAD, 0x56AC, 0xDEAE, 0x5B2A, 0xDEAF, 0x5F6C, 0xDEB0, 0x658C,\n\t0xDEB1, 0x6AB3, 0xDEB2, 0x6BAF, 0xDEB3, 0x6D5C, 0xDEB4, 0x6FF1,\n\t0xDEB5, 0x7015, 0xDEB6, 0x725D, 0xDEB7, 0x73AD, 0xDEB8, 0x8CA7,\n\t0xDEB9, 0x8CD3, 0xDEBA, 0x983B, 0xDEBB, 0x6191, 0xDEBC, 0x6C37,\n\t0xDEBD, 0x8058, 0xDEBE, 0x9A01, 0xDEBF, 0x4E4D, 0xDEC0, 0x4E8B,\n\t0xDEC1, 0x4E9B, 0xDEC2, 0x4ED5, 0xDEC3, 0x4F3A, 0xDEC4, 0x4F3C,\n\t0xDEC5, 0x4F7F, 0xDEC6, 0x4FDF, 0xDEC7, 0x50FF, 0xDEC8, 0x53F2,\n\t0xDEC9, 0x53F8, 0xDECA, 0x5506, 0xDECB, 0x55E3, 0xDECC, 0x56DB,\n\t0xDECD, 0x58EB, 0xDECE, 0x5962, 0xDECF, 0x5A11, 0xDED0, 0x5BEB,\n\t0xDED1, 0x5BFA, 0xDED2, 0x5C04, 0xDED3, 0x5DF3, 0xDED4, 0x5E2B,\n\t0xDED5, 0x5F99, 0xDED6, 0x601D, 0xDED7, 0x6368, 0xDED8, 0x659C,\n\t0xDED9, 0x65AF, 0xDEDA, 0x67F6, 0xDEDB, 0x67FB, 0xDEDC, 0x68AD,\n\t0xDEDD, 0x6B7B, 0xDEDE, 0x6C99, 0xDEDF, 0x6CD7, 0xDEE0, 0x6E23,\n\t0xDEE1, 0x7009, 0xDEE2, 0x7345, 0xDEE3, 0x7802, 0xDEE4, 0x793E,\n\t0xDEE5, 0x7940, 0xDEE6, 0x7960, 0xDEE7, 0x79C1, 0xDEE8, 0x7BE9,\n\t0xDEE9, 0x7D17, 0xDEEA, 0x7D72, 0xDEEB, 0x8086, 0xDEEC, 0x820D,\n\t0xDEED, 0x838E, 0xDEEE, 0x84D1, 0xDEEF, 0x86C7, 0xDEF0, 0x88DF,\n\t0xDEF1, 0x8A50, 0xDEF2, 0x8A5E, 0xDEF3, 0x8B1D, 0xDEF4, 0x8CDC,\n\t0xDEF5, 0x8D66, 0xDEF6, 0x8FAD, 0xDEF7, 0x90AA, 0xDEF8, 0x98FC,\n\t0xDEF9, 0x99DF, 0xDEFA, 0x9E9D, 0xDEFB, 0x524A, 0xDEFC, 0xF969,\n\t0xDEFD, 0x6714, 0xDEFE, 0xF96A, 0xDFA1, 0x5098, 0xDFA2, 0x522A,\n\t0xDFA3, 0x5C71, 0xDFA4, 0x6563, 0xDFA5, 0x6C55, 0xDFA6, 0x73CA,\n\t0xDFA7, 0x7523, 0xDFA8, 0x759D, 0xDFA9, 0x7B97, 0xDFAA, 0x849C,\n\t0xDFAB, 0x9178, 0xDFAC, 0x9730, 0xDFAD, 0x4E77, 0xDFAE, 0x6492,\n\t0xDFAF, 0x6BBA, 0xDFB0, 0x715E, 0xDFB1, 0x85A9, 0xDFB2, 0x4E09,\n\t0xDFB3, 0xF96B, 0xDFB4, 0x6749, 0xDFB5, 0x68EE, 0xDFB6, 0x6E17,\n\t0xDFB7, 0x829F, 0xDFB8, 0x8518, 0xDFB9, 0x886B, 0xDFBA, 0x63F7,\n\t0xDFBB, 0x6F81, 0xDFBC, 0x9212, 0xDFBD, 0x98AF, 0xDFBE, 0x4E0A,\n\t0xDFBF, 0x50B7, 0xDFC0, 0x50CF, 0xDFC1, 0x511F, 0xDFC2, 0x5546,\n\t0xDFC3, 0x55AA, 0xDFC4, 0x5617, 0xDFC5, 0x5B40, 0xDFC6, 0x5C19,\n\t0xDFC7, 0x5CE0, 0xDFC8, 0x5E38, 0xDFC9, 0x5E8A, 0xDFCA, 0x5EA0,\n\t0xDFCB, 0x5EC2, 0xDFCC, 0x60F3, 0xDFCD, 0x6851, 0xDFCE, 0x6A61,\n\t0xDFCF, 0x6E58, 0xDFD0, 0x723D, 0xDFD1, 0x7240, 0xDFD2, 0x72C0,\n\t0xDFD3, 0x76F8, 0xDFD4, 0x7965, 0xDFD5, 0x7BB1, 0xDFD6, 0x7FD4,\n\t0xDFD7, 0x88F3, 0xDFD8, 0x89F4, 0xDFD9, 0x8A73, 0xDFDA, 0x8C61,\n\t0xDFDB, 0x8CDE, 0xDFDC, 0x971C, 0xDFDD, 0x585E, 0xDFDE, 0x74BD,\n\t0xDFDF, 0x8CFD, 0xDFE0, 0x55C7, 0xDFE1, 0xF96C, 0xDFE2, 0x7A61,\n\t0xDFE3, 0x7D22, 0xDFE4, 0x8272, 0xDFE5, 0x7272, 0xDFE6, 0x751F,\n\t0xDFE7, 0x7525, 0xDFE8, 0xF96D, 0xDFE9, 0x7B19, 0xDFEA, 0x5885,\n\t0xDFEB, 0x58FB, 0xDFEC, 0x5DBC, 0xDFED, 0x5E8F, 0xDFEE, 0x5EB6,\n\t0xDFEF, 0x5F90, 0xDFF0, 0x6055, 0xDFF1, 0x6292, 0xDFF2, 0x637F,\n\t0xDFF3, 0x654D, 0xDFF4, 0x6691, 0xDFF5, 0x66D9, 0xDFF6, 0x66F8,\n\t0xDFF7, 0x6816, 0xDFF8, 0x68F2, 0xDFF9, 0x7280, 0xDFFA, 0x745E,\n\t0xDFFB, 0x7B6E, 0xDFFC, 0x7D6E, 0xDFFD, 0x7DD6, 0xDFFE, 0x7F72,\n\t0xE0A1, 0x80E5, 0xE0A2, 0x8212, 0xE0A3, 0x85AF, 0xE0A4, 0x897F,\n\t0xE0A5, 0x8A93, 0xE0A6, 0x901D, 0xE0A7, 0x92E4, 0xE0A8, 0x9ECD,\n\t0xE0A9, 0x9F20, 0xE0AA, 0x5915, 0xE0AB, 0x596D, 0xE0AC, 0x5E2D,\n\t0xE0AD, 0x60DC, 0xE0AE, 0x6614, 0xE0AF, 0x6673, 0xE0B0, 0x6790,\n\t0xE0B1, 0x6C50, 0xE0B2, 0x6DC5, 0xE0B3, 0x6F5F, 0xE0B4, 0x77F3,\n\t0xE0B5, 0x78A9, 0xE0B6, 0x84C6, 0xE0B7, 0x91CB, 0xE0B8, 0x932B,\n\t0xE0B9, 0x4ED9, 0xE0BA, 0x50CA, 0xE0BB, 0x5148, 0xE0BC, 0x5584,\n\t0xE0BD, 0x5B0B, 0xE0BE, 0x5BA3, 0xE0BF, 0x6247, 0xE0C0, 0x657E,\n\t0xE0C1, 0x65CB, 0xE0C2, 0x6E32, 0xE0C3, 0x717D, 0xE0C4, 0x7401,\n\t0xE0C5, 0x7444, 0xE0C6, 0x7487, 0xE0C7, 0x74BF, 0xE0C8, 0x766C,\n\t0xE0C9, 0x79AA, 0xE0CA, 0x7DDA, 0xE0CB, 0x7E55, 0xE0CC, 0x7FA8,\n\t0xE0CD, 0x817A, 0xE0CE, 0x81B3, 0xE0CF, 0x8239, 0xE0D0, 0x861A,\n\t0xE0D1, 0x87EC, 0xE0D2, 0x8A75, 0xE0D3, 0x8DE3, 0xE0D4, 0x9078,\n\t0xE0D5, 0x9291, 0xE0D6, 0x9425, 0xE0D7, 0x994D, 0xE0D8, 0x9BAE,\n\t0xE0D9, 0x5368, 0xE0DA, 0x5C51, 0xE0DB, 0x6954, 0xE0DC, 0x6CC4,\n\t0xE0DD, 0x6D29, 0xE0DE, 0x6E2B, 0xE0DF, 0x820C, 0xE0E0, 0x859B,\n\t0xE0E1, 0x893B, 0xE0E2, 0x8A2D, 0xE0E3, 0x8AAA, 0xE0E4, 0x96EA,\n\t0xE0E5, 0x9F67, 0xE0E6, 0x5261, 0xE0E7, 0x66B9, 0xE0E8, 0x6BB2,\n\t0xE0E9, 0x7E96, 0xE0EA, 0x87FE, 0xE0EB, 0x8D0D, 0xE0EC, 0x9583,\n\t0xE0ED, 0x965D, 0xE0EE, 0x651D, 0xE0EF, 0x6D89, 0xE0F0, 0x71EE,\n\t0xE0F1, 0xF96E, 0xE0F2, 0x57CE, 0xE0F3, 0x59D3, 0xE0F4, 0x5BAC,\n\t0xE0F5, 0x6027, 0xE0F6, 0x60FA, 0xE0F7, 0x6210, 0xE0F8, 0x661F,\n\t0xE0F9, 0x665F, 0xE0FA, 0x7329, 0xE0FB, 0x73F9, 0xE0FC, 0x76DB,\n\t0xE0FD, 0x7701, 0xE0FE, 0x7B6C, 0xE1A1, 0x8056, 0xE1A2, 0x8072,\n\t0xE1A3, 0x8165, 0xE1A4, 0x8AA0, 0xE1A5, 0x9192, 0xE1A6, 0x4E16,\n\t0xE1A7, 0x52E2, 0xE1A8, 0x6B72, 0xE1A9, 0x6D17, 0xE1AA, 0x7A05,\n\t0xE1AB, 0x7B39, 0xE1AC, 0x7D30, 0xE1AD, 0xF96F, 0xE1AE, 0x8CB0,\n\t0xE1AF, 0x53EC, 0xE1B0, 0x562F, 0xE1B1, 0x5851, 0xE1B2, 0x5BB5,\n\t0xE1B3, 0x5C0F, 0xE1B4, 0x5C11, 0xE1B5, 0x5DE2, 0xE1B6, 0x6240,\n\t0xE1B7, 0x6383, 0xE1B8, 0x6414, 0xE1B9, 0x662D, 0xE1BA, 0x68B3,\n\t0xE1BB, 0x6CBC, 0xE1BC, 0x6D88, 0xE1BD, 0x6EAF, 0xE1BE, 0x701F,\n\t0xE1BF, 0x70A4, 0xE1C0, 0x71D2, 0xE1C1, 0x7526, 0xE1C2, 0x758F,\n\t0xE1C3, 0x758E, 0xE1C4, 0x7619, 0xE1C5, 0x7B11, 0xE1C6, 0x7BE0,\n\t0xE1C7, 0x7C2B, 0xE1C8, 0x7D20, 0xE1C9, 0x7D39, 0xE1CA, 0x852C,\n\t0xE1CB, 0x856D, 0xE1CC, 0x8607, 0xE1CD, 0x8A34, 0xE1CE, 0x900D,\n\t0xE1CF, 0x9061, 0xE1D0, 0x90B5, 0xE1D1, 0x92B7, 0xE1D2, 0x97F6,\n\t0xE1D3, 0x9A37, 0xE1D4, 0x4FD7, 0xE1D5, 0x5C6C, 0xE1D6, 0x675F,\n\t0xE1D7, 0x6D91, 0xE1D8, 0x7C9F, 0xE1D9, 0x7E8C, 0xE1DA, 0x8B16,\n\t0xE1DB, 0x8D16, 0xE1DC, 0x901F, 0xE1DD, 0x5B6B, 0xE1DE, 0x5DFD,\n\t0xE1DF, 0x640D, 0xE1E0, 0x84C0, 0xE1E1, 0x905C, 0xE1E2, 0x98E1,\n\t0xE1E3, 0x7387, 0xE1E4, 0x5B8B, 0xE1E5, 0x609A, 0xE1E6, 0x677E,\n\t0xE1E7, 0x6DDE, 0xE1E8, 0x8A1F, 0xE1E9, 0x8AA6, 0xE1EA, 0x9001,\n\t0xE1EB, 0x980C, 0xE1EC, 0x5237, 0xE1ED, 0xF970, 0xE1EE, 0x7051,\n\t0xE1EF, 0x788E, 0xE1F0, 0x9396, 0xE1F1, 0x8870, 0xE1F2, 0x91D7,\n\t0xE1F3, 0x4FEE, 0xE1F4, 0x53D7, 0xE1F5, 0x55FD, 0xE1F6, 0x56DA,\n\t0xE1F7, 0x5782, 0xE1F8, 0x58FD, 0xE1F9, 0x5AC2, 0xE1FA, 0x5B88,\n\t0xE1FB, 0x5CAB, 0xE1FC, 0x5CC0, 0xE1FD, 0x5E25, 0xE1FE, 0x6101,\n\t0xE2A1, 0x620D, 0xE2A2, 0x624B, 0xE2A3, 0x6388, 0xE2A4, 0x641C,\n\t0xE2A5, 0x6536, 0xE2A6, 0x6578, 0xE2A7, 0x6A39, 0xE2A8, 0x6B8A,\n\t0xE2A9, 0x6C34, 0xE2AA, 0x6D19, 0xE2AB, 0x6F31, 0xE2AC, 0x71E7,\n\t0xE2AD, 0x72E9, 0xE2AE, 0x7378, 0xE2AF, 0x7407, 0xE2B0, 0x74B2,\n\t0xE2B1, 0x7626, 0xE2B2, 0x7761, 0xE2B3, 0x79C0, 0xE2B4, 0x7A57,\n\t0xE2B5, 0x7AEA, 0xE2B6, 0x7CB9, 0xE2B7, 0x7D8F, 0xE2B8, 0x7DAC,\n\t0xE2B9, 0x7E61, 0xE2BA, 0x7F9E, 0xE2BB, 0x8129, 0xE2BC, 0x8331,\n\t0xE2BD, 0x8490, 0xE2BE, 0x84DA, 0xE2BF, 0x85EA, 0xE2C0, 0x8896,\n\t0xE2C1, 0x8AB0, 0xE2C2, 0x8B90, 0xE2C3, 0x8F38, 0xE2C4, 0x9042,\n\t0xE2C5, 0x9083, 0xE2C6, 0x916C, 0xE2C7, 0x9296, 0xE2C8, 0x92B9,\n\t0xE2C9, 0x968B, 0xE2CA, 0x96A7, 0xE2CB, 0x96A8, 0xE2CC, 0x96D6,\n\t0xE2CD, 0x9700, 0xE2CE, 0x9808, 0xE2CF, 0x9996, 0xE2D0, 0x9AD3,\n\t0xE2D1, 0x9B1A, 0xE2D2, 0x53D4, 0xE2D3, 0x587E, 0xE2D4, 0x5919,\n\t0xE2D5, 0x5B70, 0xE2D6, 0x5BBF, 0xE2D7, 0x6DD1, 0xE2D8, 0x6F5A,\n\t0xE2D9, 0x719F, 0xE2DA, 0x7421, 0xE2DB, 0x74B9, 0xE2DC, 0x8085,\n\t0xE2DD, 0x83FD, 0xE2DE, 0x5DE1, 0xE2DF, 0x5F87, 0xE2E0, 0x5FAA,\n\t0xE2E1, 0x6042, 0xE2E2, 0x65EC, 0xE2E3, 0x6812, 0xE2E4, 0x696F,\n\t0xE2E5, 0x6A53, 0xE2E6, 0x6B89, 0xE2E7, 0x6D35, 0xE2E8, 0x6DF3,\n\t0xE2E9, 0x73E3, 0xE2EA, 0x76FE, 0xE2EB, 0x77AC, 0xE2EC, 0x7B4D,\n\t0xE2ED, 0x7D14, 0xE2EE, 0x8123, 0xE2EF, 0x821C, 0xE2F0, 0x8340,\n\t0xE2F1, 0x84F4, 0xE2F2, 0x8563, 0xE2F3, 0x8A62, 0xE2F4, 0x8AC4,\n\t0xE2F5, 0x9187, 0xE2F6, 0x931E, 0xE2F7, 0x9806, 0xE2F8, 0x99B4,\n\t0xE2F9, 0x620C, 0xE2FA, 0x8853, 0xE2FB, 0x8FF0, 0xE2FC, 0x9265,\n\t0xE2FD, 0x5D07, 0xE2FE, 0x5D27, 0xE3A1, 0x5D69, 0xE3A2, 0x745F,\n\t0xE3A3, 0x819D, 0xE3A4, 0x8768, 0xE3A5, 0x6FD5, 0xE3A6, 0x62FE,\n\t0xE3A7, 0x7FD2, 0xE3A8, 0x8936, 0xE3A9, 0x8972, 0xE3AA, 0x4E1E,\n\t0xE3AB, 0x4E58, 0xE3AC, 0x50E7, 0xE3AD, 0x52DD, 0xE3AE, 0x5347,\n\t0xE3AF, 0x627F, 0xE3B0, 0x6607, 0xE3B1, 0x7E69, 0xE3B2, 0x8805,\n\t0xE3B3, 0x965E, 0xE3B4, 0x4F8D, 0xE3B5, 0x5319, 0xE3B6, 0x5636,\n\t0xE3B7, 0x59CB, 0xE3B8, 0x5AA4, 0xE3B9, 0x5C38, 0xE3BA, 0x5C4E,\n\t0xE3BB, 0x5C4D, 0xE3BC, 0x5E02, 0xE3BD, 0x5F11, 0xE3BE, 0x6043,\n\t0xE3BF, 0x65BD, 0xE3C0, 0x662F, 0xE3C1, 0x6642, 0xE3C2, 0x67BE,\n\t0xE3C3, 0x67F4, 0xE3C4, 0x731C, 0xE3C5, 0x77E2, 0xE3C6, 0x793A,\n\t0xE3C7, 0x7FC5, 0xE3C8, 0x8494, 0xE3C9, 0x84CD, 0xE3CA, 0x8996,\n\t0xE3CB, 0x8A66, 0xE3CC, 0x8A69, 0xE3CD, 0x8AE1, 0xE3CE, 0x8C55,\n\t0xE3CF, 0x8C7A, 0xE3D0, 0x57F4, 0xE3D1, 0x5BD4, 0xE3D2, 0x5F0F,\n\t0xE3D3, 0x606F, 0xE3D4, 0x62ED, 0xE3D5, 0x690D, 0xE3D6, 0x6B96,\n\t0xE3D7, 0x6E5C, 0xE3D8, 0x7184, 0xE3D9, 0x7BD2, 0xE3DA, 0x8755,\n\t0xE3DB, 0x8B58, 0xE3DC, 0x8EFE, 0xE3DD, 0x98DF, 0xE3DE, 0x98FE,\n\t0xE3DF, 0x4F38, 0xE3E0, 0x4F81, 0xE3E1, 0x4FE1, 0xE3E2, 0x547B,\n\t0xE3E3, 0x5A20, 0xE3E4, 0x5BB8, 0xE3E5, 0x613C, 0xE3E6, 0x65B0,\n\t0xE3E7, 0x6668, 0xE3E8, 0x71FC, 0xE3E9, 0x7533, 0xE3EA, 0x795E,\n\t0xE3EB, 0x7D33, 0xE3EC, 0x814E, 0xE3ED, 0x81E3, 0xE3EE, 0x8398,\n\t0xE3EF, 0x85AA, 0xE3F0, 0x85CE, 0xE3F1, 0x8703, 0xE3F2, 0x8A0A,\n\t0xE3F3, 0x8EAB, 0xE3F4, 0x8F9B, 0xE3F5, 0xF971, 0xE3F6, 0x8FC5,\n\t0xE3F7, 0x5931, 0xE3F8, 0x5BA4, 0xE3F9, 0x5BE6, 0xE3FA, 0x6089,\n\t0xE3FB, 0x5BE9, 0xE3FC, 0x5C0B, 0xE3FD, 0x5FC3, 0xE3FE, 0x6C81,\n\t0xE4A1, 0xF972, 0xE4A2, 0x6DF1, 0xE4A3, 0x700B, 0xE4A4, 0x751A,\n\t0xE4A5, 0x82AF, 0xE4A6, 0x8AF6, 0xE4A7, 0x4EC0, 0xE4A8, 0x5341,\n\t0xE4A9, 0xF973, 0xE4AA, 0x96D9, 0xE4AB, 0x6C0F, 0xE4AC, 0x4E9E,\n\t0xE4AD, 0x4FC4, 0xE4AE, 0x5152, 0xE4AF, 0x555E, 0xE4B0, 0x5A25,\n\t0xE4B1, 0x5CE8, 0xE4B2, 0x6211, 0xE4B3, 0x7259, 0xE4B4, 0x82BD,\n\t0xE4B5, 0x83AA, 0xE4B6, 0x86FE, 0xE4B7, 0x8859, 0xE4B8, 0x8A1D,\n\t0xE4B9, 0x963F, 0xE4BA, 0x96C5, 0xE4BB, 0x9913, 0xE4BC, 0x9D09,\n\t0xE4BD, 0x9D5D, 0xE4BE, 0x580A, 0xE4BF, 0x5CB3, 0xE4C0, 0x5DBD,\n\t0xE4C1, 0x5E44, 0xE4C2, 0x60E1, 0xE4C3, 0x6115, 0xE4C4, 0x63E1,\n\t0xE4C5, 0x6A02, 0xE4C6, 0x6E25, 0xE4C7, 0x9102, 0xE4C8, 0x9354,\n\t0xE4C9, 0x984E, 0xE4CA, 0x9C10, 0xE4CB, 0x9F77, 0xE4CC, 0x5B89,\n\t0xE4CD, 0x5CB8, 0xE4CE, 0x6309, 0xE4CF, 0x664F, 0xE4D0, 0x6848,\n\t0xE4D1, 0x773C, 0xE4D2, 0x96C1, 0xE4D3, 0x978D, 0xE4D4, 0x9854,\n\t0xE4D5, 0x9B9F, 0xE4D6, 0x65A1, 0xE4D7, 0x8B01, 0xE4D8, 0x8ECB,\n\t0xE4D9, 0x95BC, 0xE4DA, 0x5535, 0xE4DB, 0x5CA9, 0xE4DC, 0x5DD6,\n\t0xE4DD, 0x5EB5, 0xE4DE, 0x6697, 0xE4DF, 0x764C, 0xE4E0, 0x83F4,\n\t0xE4E1, 0x95C7, 0xE4E2, 0x58D3, 0xE4E3, 0x62BC, 0xE4E4, 0x72CE,\n\t0xE4E5, 0x9D28, 0xE4E6, 0x4EF0, 0xE4E7, 0x592E, 0xE4E8, 0x600F,\n\t0xE4E9, 0x663B, 0xE4EA, 0x6B83, 0xE4EB, 0x79E7, 0xE4EC, 0x9D26,\n\t0xE4ED, 0x5393, 0xE4EE, 0x54C0, 0xE4EF, 0x57C3, 0xE4F0, 0x5D16,\n\t0xE4F1, 0x611B, 0xE4F2, 0x66D6, 0xE4F3, 0x6DAF, 0xE4F4, 0x788D,\n\t0xE4F5, 0x827E, 0xE4F6, 0x9698, 0xE4F7, 0x9744, 0xE4F8, 0x5384,\n\t0xE4F9, 0x627C, 0xE4FA, 0x6396, 0xE4FB, 0x6DB2, 0xE4FC, 0x7E0A,\n\t0xE4FD, 0x814B, 0xE4FE, 0x984D, 0xE5A1, 0x6AFB, 0xE5A2, 0x7F4C,\n\t0xE5A3, 0x9DAF, 0xE5A4, 0x9E1A, 0xE5A5, 0x4E5F, 0xE5A6, 0x503B,\n\t0xE5A7, 0x51B6, 0xE5A8, 0x591C, 0xE5A9, 0x60F9, 0xE5AA, 0x63F6,\n\t0xE5AB, 0x6930, 0xE5AC, 0x723A, 0xE5AD, 0x8036, 0xE5AE, 0xF974,\n\t0xE5AF, 0x91CE, 0xE5B0, 0x5F31, 0xE5B1, 0xF975, 0xE5B2, 0xF976,\n\t0xE5B3, 0x7D04, 0xE5B4, 0x82E5, 0xE5B5, 0x846F, 0xE5B6, 0x84BB,\n\t0xE5B7, 0x85E5, 0xE5B8, 0x8E8D, 0xE5B9, 0xF977, 0xE5BA, 0x4F6F,\n\t0xE5BB, 0xF978, 0xE5BC, 0xF979, 0xE5BD, 0x58E4, 0xE5BE, 0x5B43,\n\t0xE5BF, 0x6059, 0xE5C0, 0x63DA, 0xE5C1, 0x6518, 0xE5C2, 0x656D,\n\t0xE5C3, 0x6698, 0xE5C4, 0xF97A, 0xE5C5, 0x694A, 0xE5C6, 0x6A23,\n\t0xE5C7, 0x6D0B, 0xE5C8, 0x7001, 0xE5C9, 0x716C, 0xE5CA, 0x75D2,\n\t0xE5CB, 0x760D, 0xE5CC, 0x79B3, 0xE5CD, 0x7A70, 0xE5CE, 0xF97B,\n\t0xE5CF, 0x7F8A, 0xE5D0, 0xF97C, 0xE5D1, 0x8944, 0xE5D2, 0xF97D,\n\t0xE5D3, 0x8B93, 0xE5D4, 0x91C0, 0xE5D5, 0x967D, 0xE5D6, 0xF97E,\n\t0xE5D7, 0x990A, 0xE5D8, 0x5704, 0xE5D9, 0x5FA1, 0xE5DA, 0x65BC,\n\t0xE5DB, 0x6F01, 0xE5DC, 0x7600, 0xE5DD, 0x79A6, 0xE5DE, 0x8A9E,\n\t0xE5DF, 0x99AD, 0xE5E0, 0x9B5A, 0xE5E1, 0x9F6C, 0xE5E2, 0x5104,\n\t0xE5E3, 0x61B6, 0xE5E4, 0x6291, 0xE5E5, 0x6A8D, 0xE5E6, 0x81C6,\n\t0xE5E7, 0x5043, 0xE5E8, 0x5830, 0xE5E9, 0x5F66, 0xE5EA, 0x7109,\n\t0xE5EB, 0x8A00, 0xE5EC, 0x8AFA, 0xE5ED, 0x5B7C, 0xE5EE, 0x8616,\n\t0xE5EF, 0x4FFA, 0xE5F0, 0x513C, 0xE5F1, 0x56B4, 0xE5F2, 0x5944,\n\t0xE5F3, 0x63A9, 0xE5F4, 0x6DF9, 0xE5F5, 0x5DAA, 0xE5F6, 0x696D,\n\t0xE5F7, 0x5186, 0xE5F8, 0x4E88, 0xE5F9, 0x4F59, 0xE5FA, 0xF97F,\n\t0xE5FB, 0xF980, 0xE5FC, 0xF981, 0xE5FD, 0x5982, 0xE5FE, 0xF982,\n\t0xE6A1, 0xF983, 0xE6A2, 0x6B5F, 0xE6A3, 0x6C5D, 0xE6A4, 0xF984,\n\t0xE6A5, 0x74B5, 0xE6A6, 0x7916, 0xE6A7, 0xF985, 0xE6A8, 0x8207,\n\t0xE6A9, 0x8245, 0xE6AA, 0x8339, 0xE6AB, 0x8F3F, 0xE6AC, 0x8F5D,\n\t0xE6AD, 0xF986, 0xE6AE, 0x9918, 0xE6AF, 0xF987, 0xE6B0, 0xF988,\n\t0xE6B1, 0xF989, 0xE6B2, 0x4EA6, 0xE6B3, 0xF98A, 0xE6B4, 0x57DF,\n\t0xE6B5, 0x5F79, 0xE6B6, 0x6613, 0xE6B7, 0xF98B, 0xE6B8, 0xF98C,\n\t0xE6B9, 0x75AB, 0xE6BA, 0x7E79, 0xE6BB, 0x8B6F, 0xE6BC, 0xF98D,\n\t0xE6BD, 0x9006, 0xE6BE, 0x9A5B, 0xE6BF, 0x56A5, 0xE6C0, 0x5827,\n\t0xE6C1, 0x59F8, 0xE6C2, 0x5A1F, 0xE6C3, 0x5BB4, 0xE6C4, 0xF98E,\n\t0xE6C5, 0x5EF6, 0xE6C6, 0xF98F, 0xE6C7, 0xF990, 0xE6C8, 0x6350,\n\t0xE6C9, 0x633B, 0xE6CA, 0xF991, 0xE6CB, 0x693D, 0xE6CC, 0x6C87,\n\t0xE6CD, 0x6CBF, 0xE6CE, 0x6D8E, 0xE6CF, 0x6D93, 0xE6D0, 0x6DF5,\n\t0xE6D1, 0x6F14, 0xE6D2, 0xF992, 0xE6D3, 0x70DF, 0xE6D4, 0x7136,\n\t0xE6D5, 0x7159, 0xE6D6, 0xF993, 0xE6D7, 0x71C3, 0xE6D8, 0x71D5,\n\t0xE6D9, 0xF994, 0xE6DA, 0x784F, 0xE6DB, 0x786F, 0xE6DC, 0xF995,\n\t0xE6DD, 0x7B75, 0xE6DE, 0x7DE3, 0xE6DF, 0xF996, 0xE6E0, 0x7E2F,\n\t0xE6E1, 0xF997, 0xE6E2, 0x884D, 0xE6E3, 0x8EDF, 0xE6E4, 0xF998,\n\t0xE6E5, 0xF999, 0xE6E6, 0xF99A, 0xE6E7, 0x925B, 0xE6E8, 0xF99B,\n\t0xE6E9, 0x9CF6, 0xE6EA, 0xF99C, 0xE6EB, 0xF99D, 0xE6EC, 0xF99E,\n\t0xE6ED, 0x6085, 0xE6EE, 0x6D85, 0xE6EF, 0xF99F, 0xE6F0, 0x71B1,\n\t0xE6F1, 0xF9A0, 0xE6F2, 0xF9A1, 0xE6F3, 0x95B1, 0xE6F4, 0x53AD,\n\t0xE6F5, 0xF9A2, 0xE6F6, 0xF9A3, 0xE6F7, 0xF9A4, 0xE6F8, 0x67D3,\n\t0xE6F9, 0xF9A5, 0xE6FA, 0x708E, 0xE6FB, 0x7130, 0xE6FC, 0x7430,\n\t0xE6FD, 0x8276, 0xE6FE, 0x82D2, 0xE7A1, 0xF9A6, 0xE7A2, 0x95BB,\n\t0xE7A3, 0x9AE5, 0xE7A4, 0x9E7D, 0xE7A5, 0x66C4, 0xE7A6, 0xF9A7,\n\t0xE7A7, 0x71C1, 0xE7A8, 0x8449, 0xE7A9, 0xF9A8, 0xE7AA, 0xF9A9,\n\t0xE7AB, 0x584B, 0xE7AC, 0xF9AA, 0xE7AD, 0xF9AB, 0xE7AE, 0x5DB8,\n\t0xE7AF, 0x5F71, 0xE7B0, 0xF9AC, 0xE7B1, 0x6620, 0xE7B2, 0x668E,\n\t0xE7B3, 0x6979, 0xE7B4, 0x69AE, 0xE7B5, 0x6C38, 0xE7B6, 0x6CF3,\n\t0xE7B7, 0x6E36, 0xE7B8, 0x6F41, 0xE7B9, 0x6FDA, 0xE7BA, 0x701B,\n\t0xE7BB, 0x702F, 0xE7BC, 0x7150, 0xE7BD, 0x71DF, 0xE7BE, 0x7370,\n\t0xE7BF, 0xF9AD, 0xE7C0, 0x745B, 0xE7C1, 0xF9AE, 0xE7C2, 0x74D4,\n\t0xE7C3, 0x76C8, 0xE7C4, 0x7A4E, 0xE7C5, 0x7E93, 0xE7C6, 0xF9AF,\n\t0xE7C7, 0xF9B0, 0xE7C8, 0x82F1, 0xE7C9, 0x8A60, 0xE7CA, 0x8FCE,\n\t0xE7CB, 0xF9B1, 0xE7CC, 0x9348, 0xE7CD, 0xF9B2, 0xE7CE, 0x9719,\n\t0xE7CF, 0xF9B3, 0xE7D0, 0xF9B4, 0xE7D1, 0x4E42, 0xE7D2, 0x502A,\n\t0xE7D3, 0xF9B5, 0xE7D4, 0x5208, 0xE7D5, 0x53E1, 0xE7D6, 0x66F3,\n\t0xE7D7, 0x6C6D, 0xE7D8, 0x6FCA, 0xE7D9, 0x730A, 0xE7DA, 0x777F,\n\t0xE7DB, 0x7A62, 0xE7DC, 0x82AE, 0xE7DD, 0x85DD, 0xE7DE, 0x8602,\n\t0xE7DF, 0xF9B6, 0xE7E0, 0x88D4, 0xE7E1, 0x8A63, 0xE7E2, 0x8B7D,\n\t0xE7E3, 0x8C6B, 0xE7E4, 0xF9B7, 0xE7E5, 0x92B3, 0xE7E6, 0xF9B8,\n\t0xE7E7, 0x9713, 0xE7E8, 0x9810, 0xE7E9, 0x4E94, 0xE7EA, 0x4F0D,\n\t0xE7EB, 0x4FC9, 0xE7EC, 0x50B2, 0xE7ED, 0x5348, 0xE7EE, 0x543E,\n\t0xE7EF, 0x5433, 0xE7F0, 0x55DA, 0xE7F1, 0x5862, 0xE7F2, 0x58BA,\n\t0xE7F3, 0x5967, 0xE7F4, 0x5A1B, 0xE7F5, 0x5BE4, 0xE7F6, 0x609F,\n\t0xE7F7, 0xF9B9, 0xE7F8, 0x61CA, 0xE7F9, 0x6556, 0xE7FA, 0x65FF,\n\t0xE7FB, 0x6664, 0xE7FC, 0x68A7, 0xE7FD, 0x6C5A, 0xE7FE, 0x6FB3,\n\t0xE8A1, 0x70CF, 0xE8A2, 0x71AC, 0xE8A3, 0x7352, 0xE8A4, 0x7B7D,\n\t0xE8A5, 0x8708, 0xE8A6, 0x8AA4, 0xE8A7, 0x9C32, 0xE8A8, 0x9F07,\n\t0xE8A9, 0x5C4B, 0xE8AA, 0x6C83, 0xE8AB, 0x7344, 0xE8AC, 0x7389,\n\t0xE8AD, 0x923A, 0xE8AE, 0x6EAB, 0xE8AF, 0x7465, 0xE8B0, 0x761F,\n\t0xE8B1, 0x7A69, 0xE8B2, 0x7E15, 0xE8B3, 0x860A, 0xE8B4, 0x5140,\n\t0xE8B5, 0x58C5, 0xE8B6, 0x64C1, 0xE8B7, 0x74EE, 0xE8B8, 0x7515,\n\t0xE8B9, 0x7670, 0xE8BA, 0x7FC1, 0xE8BB, 0x9095, 0xE8BC, 0x96CD,\n\t0xE8BD, 0x9954, 0xE8BE, 0x6E26, 0xE8BF, 0x74E6, 0xE8C0, 0x7AA9,\n\t0xE8C1, 0x7AAA, 0xE8C2, 0x81E5, 0xE8C3, 0x86D9, 0xE8C4, 0x8778,\n\t0xE8C5, 0x8A1B, 0xE8C6, 0x5A49, 0xE8C7, 0x5B8C, 0xE8C8, 0x5B9B,\n\t0xE8C9, 0x68A1, 0xE8CA, 0x6900, 0xE8CB, 0x6D63, 0xE8CC, 0x73A9,\n\t0xE8CD, 0x7413, 0xE8CE, 0x742C, 0xE8CF, 0x7897, 0xE8D0, 0x7DE9,\n\t0xE8D1, 0x7FEB, 0xE8D2, 0x8118, 0xE8D3, 0x8155, 0xE8D4, 0x839E,\n\t0xE8D5, 0x8C4C, 0xE8D6, 0x962E, 0xE8D7, 0x9811, 0xE8D8, 0x66F0,\n\t0xE8D9, 0x5F80, 0xE8DA, 0x65FA, 0xE8DB, 0x6789, 0xE8DC, 0x6C6A,\n\t0xE8DD, 0x738B, 0xE8DE, 0x502D, 0xE8DF, 0x5A03, 0xE8E0, 0x6B6A,\n\t0xE8E1, 0x77EE, 0xE8E2, 0x5916, 0xE8E3, 0x5D6C, 0xE8E4, 0x5DCD,\n\t0xE8E5, 0x7325, 0xE8E6, 0x754F, 0xE8E7, 0xF9BA, 0xE8E8, 0xF9BB,\n\t0xE8E9, 0x50E5, 0xE8EA, 0x51F9, 0xE8EB, 0x582F, 0xE8EC, 0x592D,\n\t0xE8ED, 0x5996, 0xE8EE, 0x59DA, 0xE8EF, 0x5BE5, 0xE8F0, 0xF9BC,\n\t0xE8F1, 0xF9BD, 0xE8F2, 0x5DA2, 0xE8F3, 0x62D7, 0xE8F4, 0x6416,\n\t0xE8F5, 0x6493, 0xE8F6, 0x64FE, 0xE8F7, 0xF9BE, 0xE8F8, 0x66DC,\n\t0xE8F9, 0xF9BF, 0xE8FA, 0x6A48, 0xE8FB, 0xF9C0, 0xE8FC, 0x71FF,\n\t0xE8FD, 0x7464, 0xE8FE, 0xF9C1, 0xE9A1, 0x7A88, 0xE9A2, 0x7AAF,\n\t0xE9A3, 0x7E47, 0xE9A4, 0x7E5E, 0xE9A5, 0x8000, 0xE9A6, 0x8170,\n\t0xE9A7, 0xF9C2, 0xE9A8, 0x87EF, 0xE9A9, 0x8981, 0xE9AA, 0x8B20,\n\t0xE9AB, 0x9059, 0xE9AC, 0xF9C3, 0xE9AD, 0x9080, 0xE9AE, 0x9952,\n\t0xE9AF, 0x617E, 0xE9B0, 0x6B32, 0xE9B1, 0x6D74, 0xE9B2, 0x7E1F,\n\t0xE9B3, 0x8925, 0xE9B4, 0x8FB1, 0xE9B5, 0x4FD1, 0xE9B6, 0x50AD,\n\t0xE9B7, 0x5197, 0xE9B8, 0x52C7, 0xE9B9, 0x57C7, 0xE9BA, 0x5889,\n\t0xE9BB, 0x5BB9, 0xE9BC, 0x5EB8, 0xE9BD, 0x6142, 0xE9BE, 0x6995,\n\t0xE9BF, 0x6D8C, 0xE9C0, 0x6E67, 0xE9C1, 0x6EB6, 0xE9C2, 0x7194,\n\t0xE9C3, 0x7462, 0xE9C4, 0x7528, 0xE9C5, 0x752C, 0xE9C6, 0x8073,\n\t0xE9C7, 0x8338, 0xE9C8, 0x84C9, 0xE9C9, 0x8E0A, 0xE9CA, 0x9394,\n\t0xE9CB, 0x93DE, 0xE9CC, 0xF9C4, 0xE9CD, 0x4E8E, 0xE9CE, 0x4F51,\n\t0xE9CF, 0x5076, 0xE9D0, 0x512A, 0xE9D1, 0x53C8, 0xE9D2, 0x53CB,\n\t0xE9D3, 0x53F3, 0xE9D4, 0x5B87, 0xE9D5, 0x5BD3, 0xE9D6, 0x5C24,\n\t0xE9D7, 0x611A, 0xE9D8, 0x6182, 0xE9D9, 0x65F4, 0xE9DA, 0x725B,\n\t0xE9DB, 0x7397, 0xE9DC, 0x7440, 0xE9DD, 0x76C2, 0xE9DE, 0x7950,\n\t0xE9DF, 0x7991, 0xE9E0, 0x79B9, 0xE9E1, 0x7D06, 0xE9E2, 0x7FBD,\n\t0xE9E3, 0x828B, 0xE9E4, 0x85D5, 0xE9E5, 0x865E, 0xE9E6, 0x8FC2,\n\t0xE9E7, 0x9047, 0xE9E8, 0x90F5, 0xE9E9, 0x91EA, 0xE9EA, 0x9685,\n\t0xE9EB, 0x96E8, 0xE9EC, 0x96E9, 0xE9ED, 0x52D6, 0xE9EE, 0x5F67,\n\t0xE9EF, 0x65ED, 0xE9F0, 0x6631, 0xE9F1, 0x682F, 0xE9F2, 0x715C,\n\t0xE9F3, 0x7A36, 0xE9F4, 0x90C1, 0xE9F5, 0x980A, 0xE9F6, 0x4E91,\n\t0xE9F7, 0xF9C5, 0xE9F8, 0x6A52, 0xE9F9, 0x6B9E, 0xE9FA, 0x6F90,\n\t0xE9FB, 0x7189, 0xE9FC, 0x8018, 0xE9FD, 0x82B8, 0xE9FE, 0x8553,\n\t0xEAA1, 0x904B, 0xEAA2, 0x9695, 0xEAA3, 0x96F2, 0xEAA4, 0x97FB,\n\t0xEAA5, 0x851A, 0xEAA6, 0x9B31, 0xEAA7, 0x4E90, 0xEAA8, 0x718A,\n\t0xEAA9, 0x96C4, 0xEAAA, 0x5143, 0xEAAB, 0x539F, 0xEAAC, 0x54E1,\n\t0xEAAD, 0x5713, 0xEAAE, 0x5712, 0xEAAF, 0x57A3, 0xEAB0, 0x5A9B,\n\t0xEAB1, 0x5AC4, 0xEAB2, 0x5BC3, 0xEAB3, 0x6028, 0xEAB4, 0x613F,\n\t0xEAB5, 0x63F4, 0xEAB6, 0x6C85, 0xEAB7, 0x6D39, 0xEAB8, 0x6E72,\n\t0xEAB9, 0x6E90, 0xEABA, 0x7230, 0xEABB, 0x733F, 0xEABC, 0x7457,\n\t0xEABD, 0x82D1, 0xEABE, 0x8881, 0xEABF, 0x8F45, 0xEAC0, 0x9060,\n\t0xEAC1, 0xF9C6, 0xEAC2, 0x9662, 0xEAC3, 0x9858, 0xEAC4, 0x9D1B,\n\t0xEAC5, 0x6708, 0xEAC6, 0x8D8A, 0xEAC7, 0x925E, 0xEAC8, 0x4F4D,\n\t0xEAC9, 0x5049, 0xEACA, 0x50DE, 0xEACB, 0x5371, 0xEACC, 0x570D,\n\t0xEACD, 0x59D4, 0xEACE, 0x5A01, 0xEACF, 0x5C09, 0xEAD0, 0x6170,\n\t0xEAD1, 0x6690, 0xEAD2, 0x6E2D, 0xEAD3, 0x7232, 0xEAD4, 0x744B,\n\t0xEAD5, 0x7DEF, 0xEAD6, 0x80C3, 0xEAD7, 0x840E, 0xEAD8, 0x8466,\n\t0xEAD9, 0x853F, 0xEADA, 0x875F, 0xEADB, 0x885B, 0xEADC, 0x8918,\n\t0xEADD, 0x8B02, 0xEADE, 0x9055, 0xEADF, 0x97CB, 0xEAE0, 0x9B4F,\n\t0xEAE1, 0x4E73, 0xEAE2, 0x4F91, 0xEAE3, 0x5112, 0xEAE4, 0x516A,\n\t0xEAE5, 0xF9C7, 0xEAE6, 0x552F, 0xEAE7, 0x55A9, 0xEAE8, 0x5B7A,\n\t0xEAE9, 0x5BA5, 0xEAEA, 0x5E7C, 0xEAEB, 0x5E7D, 0xEAEC, 0x5EBE,\n\t0xEAED, 0x60A0, 0xEAEE, 0x60DF, 0xEAEF, 0x6108, 0xEAF0, 0x6109,\n\t0xEAF1, 0x63C4, 0xEAF2, 0x6538, 0xEAF3, 0x6709, 0xEAF4, 0xF9C8,\n\t0xEAF5, 0x67D4, 0xEAF6, 0x67DA, 0xEAF7, 0xF9C9, 0xEAF8, 0x6961,\n\t0xEAF9, 0x6962, 0xEAFA, 0x6CB9, 0xEAFB, 0x6D27, 0xEAFC, 0xF9CA,\n\t0xEAFD, 0x6E38, 0xEAFE, 0xF9CB, 0xEBA1, 0x6FE1, 0xEBA2, 0x7336,\n\t0xEBA3, 0x7337, 0xEBA4, 0xF9CC, 0xEBA5, 0x745C, 0xEBA6, 0x7531,\n\t0xEBA7, 0xF9CD, 0xEBA8, 0x7652, 0xEBA9, 0xF9CE, 0xEBAA, 0xF9CF,\n\t0xEBAB, 0x7DAD, 0xEBAC, 0x81FE, 0xEBAD, 0x8438, 0xEBAE, 0x88D5,\n\t0xEBAF, 0x8A98, 0xEBB0, 0x8ADB, 0xEBB1, 0x8AED, 0xEBB2, 0x8E30,\n\t0xEBB3, 0x8E42, 0xEBB4, 0x904A, 0xEBB5, 0x903E, 0xEBB6, 0x907A,\n\t0xEBB7, 0x9149, 0xEBB8, 0x91C9, 0xEBB9, 0x936E, 0xEBBA, 0xF9D0,\n\t0xEBBB, 0xF9D1, 0xEBBC, 0x5809, 0xEBBD, 0xF9D2, 0xEBBE, 0x6BD3,\n\t0xEBBF, 0x8089, 0xEBC0, 0x80B2, 0xEBC1, 0xF9D3, 0xEBC2, 0xF9D4,\n\t0xEBC3, 0x5141, 0xEBC4, 0x596B, 0xEBC5, 0x5C39, 0xEBC6, 0xF9D5,\n\t0xEBC7, 0xF9D6, 0xEBC8, 0x6F64, 0xEBC9, 0x73A7, 0xEBCA, 0x80E4,\n\t0xEBCB, 0x8D07, 0xEBCC, 0xF9D7, 0xEBCD, 0x9217, 0xEBCE, 0x958F,\n\t0xEBCF, 0xF9D8, 0xEBD0, 0xF9D9, 0xEBD1, 0xF9DA, 0xEBD2, 0xF9DB,\n\t0xEBD3, 0x807F, 0xEBD4, 0x620E, 0xEBD5, 0x701C, 0xEBD6, 0x7D68,\n\t0xEBD7, 0x878D, 0xEBD8, 0xF9DC, 0xEBD9, 0x57A0, 0xEBDA, 0x6069,\n\t0xEBDB, 0x6147, 0xEBDC, 0x6BB7, 0xEBDD, 0x8ABE, 0xEBDE, 0x9280,\n\t0xEBDF, 0x96B1, 0xEBE0, 0x4E59, 0xEBE1, 0x541F, 0xEBE2, 0x6DEB,\n\t0xEBE3, 0x852D, 0xEBE4, 0x9670, 0xEBE5, 0x97F3, 0xEBE6, 0x98EE,\n\t0xEBE7, 0x63D6, 0xEBE8, 0x6CE3, 0xEBE9, 0x9091, 0xEBEA, 0x51DD,\n\t0xEBEB, 0x61C9, 0xEBEC, 0x81BA, 0xEBED, 0x9DF9, 0xEBEE, 0x4F9D,\n\t0xEBEF, 0x501A, 0xEBF0, 0x5100, 0xEBF1, 0x5B9C, 0xEBF2, 0x610F,\n\t0xEBF3, 0x61FF, 0xEBF4, 0x64EC, 0xEBF5, 0x6905, 0xEBF6, 0x6BC5,\n\t0xEBF7, 0x7591, 0xEBF8, 0x77E3, 0xEBF9, 0x7FA9, 0xEBFA, 0x8264,\n\t0xEBFB, 0x858F, 0xEBFC, 0x87FB, 0xEBFD, 0x8863, 0xEBFE, 0x8ABC,\n\t0xECA1, 0x8B70, 0xECA2, 0x91AB, 0xECA3, 0x4E8C, 0xECA4, 0x4EE5,\n\t0xECA5, 0x4F0A, 0xECA6, 0xF9DD, 0xECA7, 0xF9DE, 0xECA8, 0x5937,\n\t0xECA9, 0x59E8, 0xECAA, 0xF9DF, 0xECAB, 0x5DF2, 0xECAC, 0x5F1B,\n\t0xECAD, 0x5F5B, 0xECAE, 0x6021, 0xECAF, 0xF9E0, 0xECB0, 0xF9E1,\n\t0xECB1, 0xF9E2, 0xECB2, 0xF9E3, 0xECB3, 0x723E, 0xECB4, 0x73E5,\n\t0xECB5, 0xF9E4, 0xECB6, 0x7570, 0xECB7, 0x75CD, 0xECB8, 0xF9E5,\n\t0xECB9, 0x79FB, 0xECBA, 0xF9E6, 0xECBB, 0x800C, 0xECBC, 0x8033,\n\t0xECBD, 0x8084, 0xECBE, 0x82E1, 0xECBF, 0x8351, 0xECC0, 0xF9E7,\n\t0xECC1, 0xF9E8, 0xECC2, 0x8CBD, 0xECC3, 0x8CB3, 0xECC4, 0x9087,\n\t0xECC5, 0xF9E9, 0xECC6, 0xF9EA, 0xECC7, 0x98F4, 0xECC8, 0x990C,\n\t0xECC9, 0xF9EB, 0xECCA, 0xF9EC, 0xECCB, 0x7037, 0xECCC, 0x76CA,\n\t0xECCD, 0x7FCA, 0xECCE, 0x7FCC, 0xECCF, 0x7FFC, 0xECD0, 0x8B1A,\n\t0xECD1, 0x4EBA, 0xECD2, 0x4EC1, 0xECD3, 0x5203, 0xECD4, 0x5370,\n\t0xECD5, 0xF9ED, 0xECD6, 0x54BD, 0xECD7, 0x56E0, 0xECD8, 0x59FB,\n\t0xECD9, 0x5BC5, 0xECDA, 0x5F15, 0xECDB, 0x5FCD, 0xECDC, 0x6E6E,\n\t0xECDD, 0xF9EE, 0xECDE, 0xF9EF, 0xECDF, 0x7D6A, 0xECE0, 0x8335,\n\t0xECE1, 0xF9F0, 0xECE2, 0x8693, 0xECE3, 0x8A8D, 0xECE4, 0xF9F1,\n\t0xECE5, 0x976D, 0xECE6, 0x9777, 0xECE7, 0xF9F2, 0xECE8, 0xF9F3,\n\t0xECE9, 0x4E00, 0xECEA, 0x4F5A, 0xECEB, 0x4F7E, 0xECEC, 0x58F9,\n\t0xECED, 0x65E5, 0xECEE, 0x6EA2, 0xECEF, 0x9038, 0xECF0, 0x93B0,\n\t0xECF1, 0x99B9, 0xECF2, 0x4EFB, 0xECF3, 0x58EC, 0xECF4, 0x598A,\n\t0xECF5, 0x59D9, 0xECF6, 0x6041, 0xECF7, 0xF9F4, 0xECF8, 0xF9F5,\n\t0xECF9, 0x7A14, 0xECFA, 0xF9F6, 0xECFB, 0x834F, 0xECFC, 0x8CC3,\n\t0xECFD, 0x5165, 0xECFE, 0x5344, 0xEDA1, 0xF9F7, 0xEDA2, 0xF9F8,\n\t0xEDA3, 0xF9F9, 0xEDA4, 0x4ECD, 0xEDA5, 0x5269, 0xEDA6, 0x5B55,\n\t0xEDA7, 0x82BF, 0xEDA8, 0x4ED4, 0xEDA9, 0x523A, 0xEDAA, 0x54A8,\n\t0xEDAB, 0x59C9, 0xEDAC, 0x59FF, 0xEDAD, 0x5B50, 0xEDAE, 0x5B57,\n\t0xEDAF, 0x5B5C, 0xEDB0, 0x6063, 0xEDB1, 0x6148, 0xEDB2, 0x6ECB,\n\t0xEDB3, 0x7099, 0xEDB4, 0x716E, 0xEDB5, 0x7386, 0xEDB6, 0x74F7,\n\t0xEDB7, 0x75B5, 0xEDB8, 0x78C1, 0xEDB9, 0x7D2B, 0xEDBA, 0x8005,\n\t0xEDBB, 0x81EA, 0xEDBC, 0x8328, 0xEDBD, 0x8517, 0xEDBE, 0x85C9,\n\t0xEDBF, 0x8AEE, 0xEDC0, 0x8CC7, 0xEDC1, 0x96CC, 0xEDC2, 0x4F5C,\n\t0xEDC3, 0x52FA, 0xEDC4, 0x56BC, 0xEDC5, 0x65AB, 0xEDC6, 0x6628,\n\t0xEDC7, 0x707C, 0xEDC8, 0x70B8, 0xEDC9, 0x7235, 0xEDCA, 0x7DBD,\n\t0xEDCB, 0x828D, 0xEDCC, 0x914C, 0xEDCD, 0x96C0, 0xEDCE, 0x9D72,\n\t0xEDCF, 0x5B71, 0xEDD0, 0x68E7, 0xEDD1, 0x6B98, 0xEDD2, 0x6F7A,\n\t0xEDD3, 0x76DE, 0xEDD4, 0x5C91, 0xEDD5, 0x66AB, 0xEDD6, 0x6F5B,\n\t0xEDD7, 0x7BB4, 0xEDD8, 0x7C2A, 0xEDD9, 0x8836, 0xEDDA, 0x96DC,\n\t0xEDDB, 0x4E08, 0xEDDC, 0x4ED7, 0xEDDD, 0x5320, 0xEDDE, 0x5834,\n\t0xEDDF, 0x58BB, 0xEDE0, 0x58EF, 0xEDE1, 0x596C, 0xEDE2, 0x5C07,\n\t0xEDE3, 0x5E33, 0xEDE4, 0x5E84, 0xEDE5, 0x5F35, 0xEDE6, 0x638C,\n\t0xEDE7, 0x66B2, 0xEDE8, 0x6756, 0xEDE9, 0x6A1F, 0xEDEA, 0x6AA3,\n\t0xEDEB, 0x6B0C, 0xEDEC, 0x6F3F, 0xEDED, 0x7246, 0xEDEE, 0xF9FA,\n\t0xEDEF, 0x7350, 0xEDF0, 0x748B, 0xEDF1, 0x7AE0, 0xEDF2, 0x7CA7,\n\t0xEDF3, 0x8178, 0xEDF4, 0x81DF, 0xEDF5, 0x81E7, 0xEDF6, 0x838A,\n\t0xEDF7, 0x846C, 0xEDF8, 0x8523, 0xEDF9, 0x8594, 0xEDFA, 0x85CF,\n\t0xEDFB, 0x88DD, 0xEDFC, 0x8D13, 0xEDFD, 0x91AC, 0xEDFE, 0x9577,\n\t0xEEA1, 0x969C, 0xEEA2, 0x518D, 0xEEA3, 0x54C9, 0xEEA4, 0x5728,\n\t0xEEA5, 0x5BB0, 0xEEA6, 0x624D, 0xEEA7, 0x6750, 0xEEA8, 0x683D,\n\t0xEEA9, 0x6893, 0xEEAA, 0x6E3D, 0xEEAB, 0x6ED3, 0xEEAC, 0x707D,\n\t0xEEAD, 0x7E21, 0xEEAE, 0x88C1, 0xEEAF, 0x8CA1, 0xEEB0, 0x8F09,\n\t0xEEB1, 0x9F4B, 0xEEB2, 0x9F4E, 0xEEB3, 0x722D, 0xEEB4, 0x7B8F,\n\t0xEEB5, 0x8ACD, 0xEEB6, 0x931A, 0xEEB7, 0x4F47, 0xEEB8, 0x4F4E,\n\t0xEEB9, 0x5132, 0xEEBA, 0x5480, 0xEEBB, 0x59D0, 0xEEBC, 0x5E95,\n\t0xEEBD, 0x62B5, 0xEEBE, 0x6775, 0xEEBF, 0x696E, 0xEEC0, 0x6A17,\n\t0xEEC1, 0x6CAE, 0xEEC2, 0x6E1A, 0xEEC3, 0x72D9, 0xEEC4, 0x732A,\n\t0xEEC5, 0x75BD, 0xEEC6, 0x7BB8, 0xEEC7, 0x7D35, 0xEEC8, 0x82E7,\n\t0xEEC9, 0x83F9, 0xEECA, 0x8457, 0xEECB, 0x85F7, 0xEECC, 0x8A5B,\n\t0xEECD, 0x8CAF, 0xEECE, 0x8E87, 0xEECF, 0x9019, 0xEED0, 0x90B8,\n\t0xEED1, 0x96CE, 0xEED2, 0x9F5F, 0xEED3, 0x52E3, 0xEED4, 0x540A,\n\t0xEED5, 0x5AE1, 0xEED6, 0x5BC2, 0xEED7, 0x6458, 0xEED8, 0x6575,\n\t0xEED9, 0x6EF4, 0xEEDA, 0x72C4, 0xEEDB, 0xF9FB, 0xEEDC, 0x7684,\n\t0xEEDD, 0x7A4D, 0xEEDE, 0x7B1B, 0xEEDF, 0x7C4D, 0xEEE0, 0x7E3E,\n\t0xEEE1, 0x7FDF, 0xEEE2, 0x837B, 0xEEE3, 0x8B2B, 0xEEE4, 0x8CCA,\n\t0xEEE5, 0x8D64, 0xEEE6, 0x8DE1, 0xEEE7, 0x8E5F, 0xEEE8, 0x8FEA,\n\t0xEEE9, 0x8FF9, 0xEEEA, 0x9069, 0xEEEB, 0x93D1, 0xEEEC, 0x4F43,\n\t0xEEED, 0x4F7A, 0xEEEE, 0x50B3, 0xEEEF, 0x5168, 0xEEF0, 0x5178,\n\t0xEEF1, 0x524D, 0xEEF2, 0x526A, 0xEEF3, 0x5861, 0xEEF4, 0x587C,\n\t0xEEF5, 0x5960, 0xEEF6, 0x5C08, 0xEEF7, 0x5C55, 0xEEF8, 0x5EDB,\n\t0xEEF9, 0x609B, 0xEEFA, 0x6230, 0xEEFB, 0x6813, 0xEEFC, 0x6BBF,\n\t0xEEFD, 0x6C08, 0xEEFE, 0x6FB1, 0xEFA1, 0x714E, 0xEFA2, 0x7420,\n\t0xEFA3, 0x7530, 0xEFA4, 0x7538, 0xEFA5, 0x7551, 0xEFA6, 0x7672,\n\t0xEFA7, 0x7B4C, 0xEFA8, 0x7B8B, 0xEFA9, 0x7BAD, 0xEFAA, 0x7BC6,\n\t0xEFAB, 0x7E8F, 0xEFAC, 0x8A6E, 0xEFAD, 0x8F3E, 0xEFAE, 0x8F49,\n\t0xEFAF, 0x923F, 0xEFB0, 0x9293, 0xEFB1, 0x9322, 0xEFB2, 0x942B,\n\t0xEFB3, 0x96FB, 0xEFB4, 0x985A, 0xEFB5, 0x986B, 0xEFB6, 0x991E,\n\t0xEFB7, 0x5207, 0xEFB8, 0x622A, 0xEFB9, 0x6298, 0xEFBA, 0x6D59,\n\t0xEFBB, 0x7664, 0xEFBC, 0x7ACA, 0xEFBD, 0x7BC0, 0xEFBE, 0x7D76,\n\t0xEFBF, 0x5360, 0xEFC0, 0x5CBE, 0xEFC1, 0x5E97, 0xEFC2, 0x6F38,\n\t0xEFC3, 0x70B9, 0xEFC4, 0x7C98, 0xEFC5, 0x9711, 0xEFC6, 0x9B8E,\n\t0xEFC7, 0x9EDE, 0xEFC8, 0x63A5, 0xEFC9, 0x647A, 0xEFCA, 0x8776,\n\t0xEFCB, 0x4E01, 0xEFCC, 0x4E95, 0xEFCD, 0x4EAD, 0xEFCE, 0x505C,\n\t0xEFCF, 0x5075, 0xEFD0, 0x5448, 0xEFD1, 0x59C3, 0xEFD2, 0x5B9A,\n\t0xEFD3, 0x5E40, 0xEFD4, 0x5EAD, 0xEFD5, 0x5EF7, 0xEFD6, 0x5F81,\n\t0xEFD7, 0x60C5, 0xEFD8, 0x633A, 0xEFD9, 0x653F, 0xEFDA, 0x6574,\n\t0xEFDB, 0x65CC, 0xEFDC, 0x6676, 0xEFDD, 0x6678, 0xEFDE, 0x67FE,\n\t0xEFDF, 0x6968, 0xEFE0, 0x6A89, 0xEFE1, 0x6B63, 0xEFE2, 0x6C40,\n\t0xEFE3, 0x6DC0, 0xEFE4, 0x6DE8, 0xEFE5, 0x6E1F, 0xEFE6, 0x6E5E,\n\t0xEFE7, 0x701E, 0xEFE8, 0x70A1, 0xEFE9, 0x738E, 0xEFEA, 0x73FD,\n\t0xEFEB, 0x753A, 0xEFEC, 0x775B, 0xEFED, 0x7887, 0xEFEE, 0x798E,\n\t0xEFEF, 0x7A0B, 0xEFF0, 0x7A7D, 0xEFF1, 0x7CBE, 0xEFF2, 0x7D8E,\n\t0xEFF3, 0x8247, 0xEFF4, 0x8A02, 0xEFF5, 0x8AEA, 0xEFF6, 0x8C9E,\n\t0xEFF7, 0x912D, 0xEFF8, 0x914A, 0xEFF9, 0x91D8, 0xEFFA, 0x9266,\n\t0xEFFB, 0x92CC, 0xEFFC, 0x9320, 0xEFFD, 0x9706, 0xEFFE, 0x9756,\n\t0xF0A1, 0x975C, 0xF0A2, 0x9802, 0xF0A3, 0x9F0E, 0xF0A4, 0x5236,\n\t0xF0A5, 0x5291, 0xF0A6, 0x557C, 0xF0A7, 0x5824, 0xF0A8, 0x5E1D,\n\t0xF0A9, 0x5F1F, 0xF0AA, 0x608C, 0xF0AB, 0x63D0, 0xF0AC, 0x68AF,\n\t0xF0AD, 0x6FDF, 0xF0AE, 0x796D, 0xF0AF, 0x7B2C, 0xF0B0, 0x81CD,\n\t0xF0B1, 0x85BA, 0xF0B2, 0x88FD, 0xF0B3, 0x8AF8, 0xF0B4, 0x8E44,\n\t0xF0B5, 0x918D, 0xF0B6, 0x9664, 0xF0B7, 0x969B, 0xF0B8, 0x973D,\n\t0xF0B9, 0x984C, 0xF0BA, 0x9F4A, 0xF0BB, 0x4FCE, 0xF0BC, 0x5146,\n\t0xF0BD, 0x51CB, 0xF0BE, 0x52A9, 0xF0BF, 0x5632, 0xF0C0, 0x5F14,\n\t0xF0C1, 0x5F6B, 0xF0C2, 0x63AA, 0xF0C3, 0x64CD, 0xF0C4, 0x65E9,\n\t0xF0C5, 0x6641, 0xF0C6, 0x66FA, 0xF0C7, 0x66F9, 0xF0C8, 0x671D,\n\t0xF0C9, 0x689D, 0xF0CA, 0x68D7, 0xF0CB, 0x69FD, 0xF0CC, 0x6F15,\n\t0xF0CD, 0x6F6E, 0xF0CE, 0x7167, 0xF0CF, 0x71E5, 0xF0D0, 0x722A,\n\t0xF0D1, 0x74AA, 0xF0D2, 0x773A, 0xF0D3, 0x7956, 0xF0D4, 0x795A,\n\t0xF0D5, 0x79DF, 0xF0D6, 0x7A20, 0xF0D7, 0x7A95, 0xF0D8, 0x7C97,\n\t0xF0D9, 0x7CDF, 0xF0DA, 0x7D44, 0xF0DB, 0x7E70, 0xF0DC, 0x8087,\n\t0xF0DD, 0x85FB, 0xF0DE, 0x86A4, 0xF0DF, 0x8A54, 0xF0E0, 0x8ABF,\n\t0xF0E1, 0x8D99, 0xF0E2, 0x8E81, 0xF0E3, 0x9020, 0xF0E4, 0x906D,\n\t0xF0E5, 0x91E3, 0xF0E6, 0x963B, 0xF0E7, 0x96D5, 0xF0E8, 0x9CE5,\n\t0xF0E9, 0x65CF, 0xF0EA, 0x7C07, 0xF0EB, 0x8DB3, 0xF0EC, 0x93C3,\n\t0xF0ED, 0x5B58, 0xF0EE, 0x5C0A, 0xF0EF, 0x5352, 0xF0F0, 0x62D9,\n\t0xF0F1, 0x731D, 0xF0F2, 0x5027, 0xF0F3, 0x5B97, 0xF0F4, 0x5F9E,\n\t0xF0F5, 0x60B0, 0xF0F6, 0x616B, 0xF0F7, 0x68D5, 0xF0F8, 0x6DD9,\n\t0xF0F9, 0x742E, 0xF0FA, 0x7A2E, 0xF0FB, 0x7D42, 0xF0FC, 0x7D9C,\n\t0xF0FD, 0x7E31, 0xF0FE, 0x816B, 0xF1A1, 0x8E2A, 0xF1A2, 0x8E35,\n\t0xF1A3, 0x937E, 0xF1A4, 0x9418, 0xF1A5, 0x4F50, 0xF1A6, 0x5750,\n\t0xF1A7, 0x5DE6, 0xF1A8, 0x5EA7, 0xF1A9, 0x632B, 0xF1AA, 0x7F6A,\n\t0xF1AB, 0x4E3B, 0xF1AC, 0x4F4F, 0xF1AD, 0x4F8F, 0xF1AE, 0x505A,\n\t0xF1AF, 0x59DD, 0xF1B0, 0x80C4, 0xF1B1, 0x546A, 0xF1B2, 0x5468,\n\t0xF1B3, 0x55FE, 0xF1B4, 0x594F, 0xF1B5, 0x5B99, 0xF1B6, 0x5DDE,\n\t0xF1B7, 0x5EDA, 0xF1B8, 0x665D, 0xF1B9, 0x6731, 0xF1BA, 0x67F1,\n\t0xF1BB, 0x682A, 0xF1BC, 0x6CE8, 0xF1BD, 0x6D32, 0xF1BE, 0x6E4A,\n\t0xF1BF, 0x6F8D, 0xF1C0, 0x70B7, 0xF1C1, 0x73E0, 0xF1C2, 0x7587,\n\t0xF1C3, 0x7C4C, 0xF1C4, 0x7D02, 0xF1C5, 0x7D2C, 0xF1C6, 0x7DA2,\n\t0xF1C7, 0x821F, 0xF1C8, 0x86DB, 0xF1C9, 0x8A3B, 0xF1CA, 0x8A85,\n\t0xF1CB, 0x8D70, 0xF1CC, 0x8E8A, 0xF1CD, 0x8F33, 0xF1CE, 0x9031,\n\t0xF1CF, 0x914E, 0xF1D0, 0x9152, 0xF1D1, 0x9444, 0xF1D2, 0x99D0,\n\t0xF1D3, 0x7AF9, 0xF1D4, 0x7CA5, 0xF1D5, 0x4FCA, 0xF1D6, 0x5101,\n\t0xF1D7, 0x51C6, 0xF1D8, 0x57C8, 0xF1D9, 0x5BEF, 0xF1DA, 0x5CFB,\n\t0xF1DB, 0x6659, 0xF1DC, 0x6A3D, 0xF1DD, 0x6D5A, 0xF1DE, 0x6E96,\n\t0xF1DF, 0x6FEC, 0xF1E0, 0x710C, 0xF1E1, 0x756F, 0xF1E2, 0x7AE3,\n\t0xF1E3, 0x8822, 0xF1E4, 0x9021, 0xF1E5, 0x9075, 0xF1E6, 0x96CB,\n\t0xF1E7, 0x99FF, 0xF1E8, 0x8301, 0xF1E9, 0x4E2D, 0xF1EA, 0x4EF2,\n\t0xF1EB, 0x8846, 0xF1EC, 0x91CD, 0xF1ED, 0x537D, 0xF1EE, 0x6ADB,\n\t0xF1EF, 0x696B, 0xF1F0, 0x6C41, 0xF1F1, 0x847A, 0xF1F2, 0x589E,\n\t0xF1F3, 0x618E, 0xF1F4, 0x66FE, 0xF1F5, 0x62EF, 0xF1F6, 0x70DD,\n\t0xF1F7, 0x7511, 0xF1F8, 0x75C7, 0xF1F9, 0x7E52, 0xF1FA, 0x84B8,\n\t0xF1FB, 0x8B49, 0xF1FC, 0x8D08, 0xF1FD, 0x4E4B, 0xF1FE, 0x53EA,\n\t0xF2A1, 0x54AB, 0xF2A2, 0x5730, 0xF2A3, 0x5740, 0xF2A4, 0x5FD7,\n\t0xF2A5, 0x6301, 0xF2A6, 0x6307, 0xF2A7, 0x646F, 0xF2A8, 0x652F,\n\t0xF2A9, 0x65E8, 0xF2AA, 0x667A, 0xF2AB, 0x679D, 0xF2AC, 0x67B3,\n\t0xF2AD, 0x6B62, 0xF2AE, 0x6C60, 0xF2AF, 0x6C9A, 0xF2B0, 0x6F2C,\n\t0xF2B1, 0x77E5, 0xF2B2, 0x7825, 0xF2B3, 0x7949, 0xF2B4, 0x7957,\n\t0xF2B5, 0x7D19, 0xF2B6, 0x80A2, 0xF2B7, 0x8102, 0xF2B8, 0x81F3,\n\t0xF2B9, 0x829D, 0xF2BA, 0x82B7, 0xF2BB, 0x8718, 0xF2BC, 0x8A8C,\n\t0xF2BD, 0xF9FC, 0xF2BE, 0x8D04, 0xF2BF, 0x8DBE, 0xF2C0, 0x9072,\n\t0xF2C1, 0x76F4, 0xF2C2, 0x7A19, 0xF2C3, 0x7A37, 0xF2C4, 0x7E54,\n\t0xF2C5, 0x8077, 0xF2C6, 0x5507, 0xF2C7, 0x55D4, 0xF2C8, 0x5875,\n\t0xF2C9, 0x632F, 0xF2CA, 0x6422, 0xF2CB, 0x6649, 0xF2CC, 0x664B,\n\t0xF2CD, 0x686D, 0xF2CE, 0x699B, 0xF2CF, 0x6B84, 0xF2D0, 0x6D25,\n\t0xF2D1, 0x6EB1, 0xF2D2, 0x73CD, 0xF2D3, 0x7468, 0xF2D4, 0x74A1,\n\t0xF2D5, 0x755B, 0xF2D6, 0x75B9, 0xF2D7, 0x76E1, 0xF2D8, 0x771E,\n\t0xF2D9, 0x778B, 0xF2DA, 0x79E6, 0xF2DB, 0x7E09, 0xF2DC, 0x7E1D,\n\t0xF2DD, 0x81FB, 0xF2DE, 0x852F, 0xF2DF, 0x8897, 0xF2E0, 0x8A3A,\n\t0xF2E1, 0x8CD1, 0xF2E2, 0x8EEB, 0xF2E3, 0x8FB0, 0xF2E4, 0x9032,\n\t0xF2E5, 0x93AD, 0xF2E6, 0x9663, 0xF2E7, 0x9673, 0xF2E8, 0x9707,\n\t0xF2E9, 0x4F84, 0xF2EA, 0x53F1, 0xF2EB, 0x59EA, 0xF2EC, 0x5AC9,\n\t0xF2ED, 0x5E19, 0xF2EE, 0x684E, 0xF2EF, 0x74C6, 0xF2F0, 0x75BE,\n\t0xF2F1, 0x79E9, 0xF2F2, 0x7A92, 0xF2F3, 0x81A3, 0xF2F4, 0x86ED,\n\t0xF2F5, 0x8CEA, 0xF2F6, 0x8DCC, 0xF2F7, 0x8FED, 0xF2F8, 0x659F,\n\t0xF2F9, 0x6715, 0xF2FA, 0xF9FD, 0xF2FB, 0x57F7, 0xF2FC, 0x6F57,\n\t0xF2FD, 0x7DDD, 0xF2FE, 0x8F2F, 0xF3A1, 0x93F6, 0xF3A2, 0x96C6,\n\t0xF3A3, 0x5FB5, 0xF3A4, 0x61F2, 0xF3A5, 0x6F84, 0xF3A6, 0x4E14,\n\t0xF3A7, 0x4F98, 0xF3A8, 0x501F, 0xF3A9, 0x53C9, 0xF3AA, 0x55DF,\n\t0xF3AB, 0x5D6F, 0xF3AC, 0x5DEE, 0xF3AD, 0x6B21, 0xF3AE, 0x6B64,\n\t0xF3AF, 0x78CB, 0xF3B0, 0x7B9A, 0xF3B1, 0xF9FE, 0xF3B2, 0x8E49,\n\t0xF3B3, 0x8ECA, 0xF3B4, 0x906E, 0xF3B5, 0x6349, 0xF3B6, 0x643E,\n\t0xF3B7, 0x7740, 0xF3B8, 0x7A84, 0xF3B9, 0x932F, 0xF3BA, 0x947F,\n\t0xF3BB, 0x9F6A, 0xF3BC, 0x64B0, 0xF3BD, 0x6FAF, 0xF3BE, 0x71E6,\n\t0xF3BF, 0x74A8, 0xF3C0, 0x74DA, 0xF3C1, 0x7AC4, 0xF3C2, 0x7C12,\n\t0xF3C3, 0x7E82, 0xF3C4, 0x7CB2, 0xF3C5, 0x7E98, 0xF3C6, 0x8B9A,\n\t0xF3C7, 0x8D0A, 0xF3C8, 0x947D, 0xF3C9, 0x9910, 0xF3CA, 0x994C,\n\t0xF3CB, 0x5239, 0xF3CC, 0x5BDF, 0xF3CD, 0x64E6, 0xF3CE, 0x672D,\n\t0xF3CF, 0x7D2E, 0xF3D0, 0x50ED, 0xF3D1, 0x53C3, 0xF3D2, 0x5879,\n\t0xF3D3, 0x6158, 0xF3D4, 0x6159, 0xF3D5, 0x61FA, 0xF3D6, 0x65AC,\n\t0xF3D7, 0x7AD9, 0xF3D8, 0x8B92, 0xF3D9, 0x8B96, 0xF3DA, 0x5009,\n\t0xF3DB, 0x5021, 0xF3DC, 0x5275, 0xF3DD, 0x5531, 0xF3DE, 0x5A3C,\n\t0xF3DF, 0x5EE0, 0xF3E0, 0x5F70, 0xF3E1, 0x6134, 0xF3E2, 0x655E,\n\t0xF3E3, 0x660C, 0xF3E4, 0x6636, 0xF3E5, 0x66A2, 0xF3E6, 0x69CD,\n\t0xF3E7, 0x6EC4, 0xF3E8, 0x6F32, 0xF3E9, 0x7316, 0xF3EA, 0x7621,\n\t0xF3EB, 0x7A93, 0xF3EC, 0x8139, 0xF3ED, 0x8259, 0xF3EE, 0x83D6,\n\t0xF3EF, 0x84BC, 0xF3F0, 0x50B5, 0xF3F1, 0x57F0, 0xF3F2, 0x5BC0,\n\t0xF3F3, 0x5BE8, 0xF3F4, 0x5F69, 0xF3F5, 0x63A1, 0xF3F6, 0x7826,\n\t0xF3F7, 0x7DB5, 0xF3F8, 0x83DC, 0xF3F9, 0x8521, 0xF3FA, 0x91C7,\n\t0xF3FB, 0x91F5, 0xF3FC, 0x518A, 0xF3FD, 0x67F5, 0xF3FE, 0x7B56,\n\t0xF4A1, 0x8CAC, 0xF4A2, 0x51C4, 0xF4A3, 0x59BB, 0xF4A4, 0x60BD,\n\t0xF4A5, 0x8655, 0xF4A6, 0x501C, 0xF4A7, 0xF9FF, 0xF4A8, 0x5254,\n\t0xF4A9, 0x5C3A, 0xF4AA, 0x617D, 0xF4AB, 0x621A, 0xF4AC, 0x62D3,\n\t0xF4AD, 0x64F2, 0xF4AE, 0x65A5, 0xF4AF, 0x6ECC, 0xF4B0, 0x7620,\n\t0xF4B1, 0x810A, 0xF4B2, 0x8E60, 0xF4B3, 0x965F, 0xF4B4, 0x96BB,\n\t0xF4B5, 0x4EDF, 0xF4B6, 0x5343, 0xF4B7, 0x5598, 0xF4B8, 0x5929,\n\t0xF4B9, 0x5DDD, 0xF4BA, 0x64C5, 0xF4BB, 0x6CC9, 0xF4BC, 0x6DFA,\n\t0xF4BD, 0x7394, 0xF4BE, 0x7A7F, 0xF4BF, 0x821B, 0xF4C0, 0x85A6,\n\t0xF4C1, 0x8CE4, 0xF4C2, 0x8E10, 0xF4C3, 0x9077, 0xF4C4, 0x91E7,\n\t0xF4C5, 0x95E1, 0xF4C6, 0x9621, 0xF4C7, 0x97C6, 0xF4C8, 0x51F8,\n\t0xF4C9, 0x54F2, 0xF4CA, 0x5586, 0xF4CB, 0x5FB9, 0xF4CC, 0x64A4,\n\t0xF4CD, 0x6F88, 0xF4CE, 0x7DB4, 0xF4CF, 0x8F1F, 0xF4D0, 0x8F4D,\n\t0xF4D1, 0x9435, 0xF4D2, 0x50C9, 0xF4D3, 0x5C16, 0xF4D4, 0x6CBE,\n\t0xF4D5, 0x6DFB, 0xF4D6, 0x751B, 0xF4D7, 0x77BB, 0xF4D8, 0x7C3D,\n\t0xF4D9, 0x7C64, 0xF4DA, 0x8A79, 0xF4DB, 0x8AC2, 0xF4DC, 0x581E,\n\t0xF4DD, 0x59BE, 0xF4DE, 0x5E16, 0xF4DF, 0x6377, 0xF4E0, 0x7252,\n\t0xF4E1, 0x758A, 0xF4E2, 0x776B, 0xF4E3, 0x8ADC, 0xF4E4, 0x8CBC,\n\t0xF4E5, 0x8F12, 0xF4E6, 0x5EF3, 0xF4E7, 0x6674, 0xF4E8, 0x6DF8,\n\t0xF4E9, 0x807D, 0xF4EA, 0x83C1, 0xF4EB, 0x8ACB, 0xF4EC, 0x9751,\n\t0xF4ED, 0x9BD6, 0xF4EE, 0xFA00, 0xF4EF, 0x5243, 0xF4F0, 0x66FF,\n\t0xF4F1, 0x6D95, 0xF4F2, 0x6EEF, 0xF4F3, 0x7DE0, 0xF4F4, 0x8AE6,\n\t0xF4F5, 0x902E, 0xF4F6, 0x905E, 0xF4F7, 0x9AD4, 0xF4F8, 0x521D,\n\t0xF4F9, 0x527F, 0xF4FA, 0x54E8, 0xF4FB, 0x6194, 0xF4FC, 0x6284,\n\t0xF4FD, 0x62DB, 0xF4FE, 0x68A2, 0xF5A1, 0x6912, 0xF5A2, 0x695A,\n\t0xF5A3, 0x6A35, 0xF5A4, 0x7092, 0xF5A5, 0x7126, 0xF5A6, 0x785D,\n\t0xF5A7, 0x7901, 0xF5A8, 0x790E, 0xF5A9, 0x79D2, 0xF5AA, 0x7A0D,\n\t0xF5AB, 0x8096, 0xF5AC, 0x8278, 0xF5AD, 0x82D5, 0xF5AE, 0x8349,\n\t0xF5AF, 0x8549, 0xF5B0, 0x8C82, 0xF5B1, 0x8D85, 0xF5B2, 0x9162,\n\t0xF5B3, 0x918B, 0xF5B4, 0x91AE, 0xF5B5, 0x4FC3, 0xF5B6, 0x56D1,\n\t0xF5B7, 0x71ED, 0xF5B8, 0x77D7, 0xF5B9, 0x8700, 0xF5BA, 0x89F8,\n\t0xF5BB, 0x5BF8, 0xF5BC, 0x5FD6, 0xF5BD, 0x6751, 0xF5BE, 0x90A8,\n\t0xF5BF, 0x53E2, 0xF5C0, 0x585A, 0xF5C1, 0x5BF5, 0xF5C2, 0x60A4,\n\t0xF5C3, 0x6181, 0xF5C4, 0x6460, 0xF5C5, 0x7E3D, 0xF5C6, 0x8070,\n\t0xF5C7, 0x8525, 0xF5C8, 0x9283, 0xF5C9, 0x64AE, 0xF5CA, 0x50AC,\n\t0xF5CB, 0x5D14, 0xF5CC, 0x6700, 0xF5CD, 0x589C, 0xF5CE, 0x62BD,\n\t0xF5CF, 0x63A8, 0xF5D0, 0x690E, 0xF5D1, 0x6978, 0xF5D2, 0x6A1E,\n\t0xF5D3, 0x6E6B, 0xF5D4, 0x76BA, 0xF5D5, 0x79CB, 0xF5D6, 0x82BB,\n\t0xF5D7, 0x8429, 0xF5D8, 0x8ACF, 0xF5D9, 0x8DA8, 0xF5DA, 0x8FFD,\n\t0xF5DB, 0x9112, 0xF5DC, 0x914B, 0xF5DD, 0x919C, 0xF5DE, 0x9310,\n\t0xF5DF, 0x9318, 0xF5E0, 0x939A, 0xF5E1, 0x96DB, 0xF5E2, 0x9A36,\n\t0xF5E3, 0x9C0D, 0xF5E4, 0x4E11, 0xF5E5, 0x755C, 0xF5E6, 0x795D,\n\t0xF5E7, 0x7AFA, 0xF5E8, 0x7B51, 0xF5E9, 0x7BC9, 0xF5EA, 0x7E2E,\n\t0xF5EB, 0x84C4, 0xF5EC, 0x8E59, 0xF5ED, 0x8E74, 0xF5EE, 0x8EF8,\n\t0xF5EF, 0x9010, 0xF5F0, 0x6625, 0xF5F1, 0x693F, 0xF5F2, 0x7443,\n\t0xF5F3, 0x51FA, 0xF5F4, 0x672E, 0xF5F5, 0x9EDC, 0xF5F6, 0x5145,\n\t0xF5F7, 0x5FE0, 0xF5F8, 0x6C96, 0xF5F9, 0x87F2, 0xF5FA, 0x885D,\n\t0xF5FB, 0x8877, 0xF5FC, 0x60B4, 0xF5FD, 0x81B5, 0xF5FE, 0x8403,\n\t0xF6A1, 0x8D05, 0xF6A2, 0x53D6, 0xF6A3, 0x5439, 0xF6A4, 0x5634,\n\t0xF6A5, 0x5A36, 0xF6A6, 0x5C31, 0xF6A7, 0x708A, 0xF6A8, 0x7FE0,\n\t0xF6A9, 0x805A, 0xF6AA, 0x8106, 0xF6AB, 0x81ED, 0xF6AC, 0x8DA3,\n\t0xF6AD, 0x9189, 0xF6AE, 0x9A5F, 0xF6AF, 0x9DF2, 0xF6B0, 0x5074,\n\t0xF6B1, 0x4EC4, 0xF6B2, 0x53A0, 0xF6B3, 0x60FB, 0xF6B4, 0x6E2C,\n\t0xF6B5, 0x5C64, 0xF6B6, 0x4F88, 0xF6B7, 0x5024, 0xF6B8, 0x55E4,\n\t0xF6B9, 0x5CD9, 0xF6BA, 0x5E5F, 0xF6BB, 0x6065, 0xF6BC, 0x6894,\n\t0xF6BD, 0x6CBB, 0xF6BE, 0x6DC4, 0xF6BF, 0x71BE, 0xF6C0, 0x75D4,\n\t0xF6C1, 0x75F4, 0xF6C2, 0x7661, 0xF6C3, 0x7A1A, 0xF6C4, 0x7A49,\n\t0xF6C5, 0x7DC7, 0xF6C6, 0x7DFB, 0xF6C7, 0x7F6E, 0xF6C8, 0x81F4,\n\t0xF6C9, 0x86A9, 0xF6CA, 0x8F1C, 0xF6CB, 0x96C9, 0xF6CC, 0x99B3,\n\t0xF6CD, 0x9F52, 0xF6CE, 0x5247, 0xF6CF, 0x52C5, 0xF6D0, 0x98ED,\n\t0xF6D1, 0x89AA, 0xF6D2, 0x4E03, 0xF6D3, 0x67D2, 0xF6D4, 0x6F06,\n\t0xF6D5, 0x4FB5, 0xF6D6, 0x5BE2, 0xF6D7, 0x6795, 0xF6D8, 0x6C88,\n\t0xF6D9, 0x6D78, 0xF6DA, 0x741B, 0xF6DB, 0x7827, 0xF6DC, 0x91DD,\n\t0xF6DD, 0x937C, 0xF6DE, 0x87C4, 0xF6DF, 0x79E4, 0xF6E0, 0x7A31,\n\t0xF6E1, 0x5FEB, 0xF6E2, 0x4ED6, 0xF6E3, 0x54A4, 0xF6E4, 0x553E,\n\t0xF6E5, 0x58AE, 0xF6E6, 0x59A5, 0xF6E7, 0x60F0, 0xF6E8, 0x6253,\n\t0xF6E9, 0x62D6, 0xF6EA, 0x6736, 0xF6EB, 0x6955, 0xF6EC, 0x8235,\n\t0xF6ED, 0x9640, 0xF6EE, 0x99B1, 0xF6EF, 0x99DD, 0xF6F0, 0x502C,\n\t0xF6F1, 0x5353, 0xF6F2, 0x5544, 0xF6F3, 0x577C, 0xF6F4, 0xFA01,\n\t0xF6F5, 0x6258, 0xF6F6, 0xFA02, 0xF6F7, 0x64E2, 0xF6F8, 0x666B,\n\t0xF6F9, 0x67DD, 0xF6FA, 0x6FC1, 0xF6FB, 0x6FEF, 0xF6FC, 0x7422,\n\t0xF6FD, 0x7438, 0xF6FE, 0x8A17, 0xF7A1, 0x9438, 0xF7A2, 0x5451,\n\t0xF7A3, 0x5606, 0xF7A4, 0x5766, 0xF7A5, 0x5F48, 0xF7A6, 0x619A,\n\t0xF7A7, 0x6B4E, 0xF7A8, 0x7058, 0xF7A9, 0x70AD, 0xF7AA, 0x7DBB,\n\t0xF7AB, 0x8A95, 0xF7AC, 0x596A, 0xF7AD, 0x812B, 0xF7AE, 0x63A2,\n\t0xF7AF, 0x7708, 0xF7B0, 0x803D, 0xF7B1, 0x8CAA, 0xF7B2, 0x5854,\n\t0xF7B3, 0x642D, 0xF7B4, 0x69BB, 0xF7B5, 0x5B95, 0xF7B6, 0x5E11,\n\t0xF7B7, 0x6E6F, 0xF7B8, 0xFA03, 0xF7B9, 0x8569, 0xF7BA, 0x514C,\n\t0xF7BB, 0x53F0, 0xF7BC, 0x592A, 0xF7BD, 0x6020, 0xF7BE, 0x614B,\n\t0xF7BF, 0x6B86, 0xF7C0, 0x6C70, 0xF7C1, 0x6CF0, 0xF7C2, 0x7B1E,\n\t0xF7C3, 0x80CE, 0xF7C4, 0x82D4, 0xF7C5, 0x8DC6, 0xF7C6, 0x90B0,\n\t0xF7C7, 0x98B1, 0xF7C8, 0xFA04, 0xF7C9, 0x64C7, 0xF7CA, 0x6FA4,\n\t0xF7CB, 0x6491, 0xF7CC, 0x6504, 0xF7CD, 0x514E, 0xF7CE, 0x5410,\n\t0xF7CF, 0x571F, 0xF7D0, 0x8A0E, 0xF7D1, 0x615F, 0xF7D2, 0x6876,\n\t0xF7D3, 0xFA05, 0xF7D4, 0x75DB, 0xF7D5, 0x7B52, 0xF7D6, 0x7D71,\n\t0xF7D7, 0x901A, 0xF7D8, 0x5806, 0xF7D9, 0x69CC, 0xF7DA, 0x817F,\n\t0xF7DB, 0x892A, 0xF7DC, 0x9000, 0xF7DD, 0x9839, 0xF7DE, 0x5078,\n\t0xF7DF, 0x5957, 0xF7E0, 0x59AC, 0xF7E1, 0x6295, 0xF7E2, 0x900F,\n\t0xF7E3, 0x9B2A, 0xF7E4, 0x615D, 0xF7E5, 0x7279, 0xF7E6, 0x95D6,\n\t0xF7E7, 0x5761, 0xF7E8, 0x5A46, 0xF7E9, 0x5DF4, 0xF7EA, 0x628A,\n\t0xF7EB, 0x64AD, 0xF7EC, 0x64FA, 0xF7ED, 0x6777, 0xF7EE, 0x6CE2,\n\t0xF7EF, 0x6D3E, 0xF7F0, 0x722C, 0xF7F1, 0x7436, 0xF7F2, 0x7834,\n\t0xF7F3, 0x7F77, 0xF7F4, 0x82AD, 0xF7F5, 0x8DDB, 0xF7F6, 0x9817,\n\t0xF7F7, 0x5224, 0xF7F8, 0x5742, 0xF7F9, 0x677F, 0xF7FA, 0x7248,\n\t0xF7FB, 0x74E3, 0xF7FC, 0x8CA9, 0xF7FD, 0x8FA6, 0xF7FE, 0x9211,\n\t0xF8A1, 0x962A, 0xF8A2, 0x516B, 0xF8A3, 0x53ED, 0xF8A4, 0x634C,\n\t0xF8A5, 0x4F69, 0xF8A6, 0x5504, 0xF8A7, 0x6096, 0xF8A8, 0x6557,\n\t0xF8A9, 0x6C9B, 0xF8AA, 0x6D7F, 0xF8AB, 0x724C, 0xF8AC, 0x72FD,\n\t0xF8AD, 0x7A17, 0xF8AE, 0x8987, 0xF8AF, 0x8C9D, 0xF8B0, 0x5F6D,\n\t0xF8B1, 0x6F8E, 0xF8B2, 0x70F9, 0xF8B3, 0x81A8, 0xF8B4, 0x610E,\n\t0xF8B5, 0x4FBF, 0xF8B6, 0x504F, 0xF8B7, 0x6241, 0xF8B8, 0x7247,\n\t0xF8B9, 0x7BC7, 0xF8BA, 0x7DE8, 0xF8BB, 0x7FE9, 0xF8BC, 0x904D,\n\t0xF8BD, 0x97AD, 0xF8BE, 0x9A19, 0xF8BF, 0x8CB6, 0xF8C0, 0x576A,\n\t0xF8C1, 0x5E73, 0xF8C2, 0x67B0, 0xF8C3, 0x840D, 0xF8C4, 0x8A55,\n\t0xF8C5, 0x5420, 0xF8C6, 0x5B16, 0xF8C7, 0x5E63, 0xF8C8, 0x5EE2,\n\t0xF8C9, 0x5F0A, 0xF8CA, 0x6583, 0xF8CB, 0x80BA, 0xF8CC, 0x853D,\n\t0xF8CD, 0x9589, 0xF8CE, 0x965B, 0xF8CF, 0x4F48, 0xF8D0, 0x5305,\n\t0xF8D1, 0x530D, 0xF8D2, 0x530F, 0xF8D3, 0x5486, 0xF8D4, 0x54FA,\n\t0xF8D5, 0x5703, 0xF8D6, 0x5E03, 0xF8D7, 0x6016, 0xF8D8, 0x629B,\n\t0xF8D9, 0x62B1, 0xF8DA, 0x6355, 0xF8DB, 0xFA06, 0xF8DC, 0x6CE1,\n\t0xF8DD, 0x6D66, 0xF8DE, 0x75B1, 0xF8DF, 0x7832, 0xF8E0, 0x80DE,\n\t0xF8E1, 0x812F, 0xF8E2, 0x82DE, 0xF8E3, 0x8461, 0xF8E4, 0x84B2,\n\t0xF8E5, 0x888D, 0xF8E6, 0x8912, 0xF8E7, 0x900B, 0xF8E8, 0x92EA,\n\t0xF8E9, 0x98FD, 0xF8EA, 0x9B91, 0xF8EB, 0x5E45, 0xF8EC, 0x66B4,\n\t0xF8ED, 0x66DD, 0xF8EE, 0x7011, 0xF8EF, 0x7206, 0xF8F0, 0xFA07,\n\t0xF8F1, 0x4FF5, 0xF8F2, 0x527D, 0xF8F3, 0x5F6A, 0xF8F4, 0x6153,\n\t0xF8F5, 0x6753, 0xF8F6, 0x6A19, 0xF8F7, 0x6F02, 0xF8F8, 0x74E2,\n\t0xF8F9, 0x7968, 0xF8FA, 0x8868, 0xF8FB, 0x8C79, 0xF8FC, 0x98C7,\n\t0xF8FD, 0x98C4, 0xF8FE, 0x9A43, 0xF9A1, 0x54C1, 0xF9A2, 0x7A1F,\n\t0xF9A3, 0x6953, 0xF9A4, 0x8AF7, 0xF9A5, 0x8C4A, 0xF9A6, 0x98A8,\n\t0xF9A7, 0x99AE, 0xF9A8, 0x5F7C, 0xF9A9, 0x62AB, 0xF9AA, 0x75B2,\n\t0xF9AB, 0x76AE, 0xF9AC, 0x88AB, 0xF9AD, 0x907F, 0xF9AE, 0x9642,\n\t0xF9AF, 0x5339, 0xF9B0, 0x5F3C, 0xF9B1, 0x5FC5, 0xF9B2, 0x6CCC,\n\t0xF9B3, 0x73CC, 0xF9B4, 0x7562, 0xF9B5, 0x758B, 0xF9B6, 0x7B46,\n\t0xF9B7, 0x82FE, 0xF9B8, 0x999D, 0xF9B9, 0x4E4F, 0xF9BA, 0x903C,\n\t0xF9BB, 0x4E0B, 0xF9BC, 0x4F55, 0xF9BD, 0x53A6, 0xF9BE, 0x590F,\n\t0xF9BF, 0x5EC8, 0xF9C0, 0x6630, 0xF9C1, 0x6CB3, 0xF9C2, 0x7455,\n\t0xF9C3, 0x8377, 0xF9C4, 0x8766, 0xF9C5, 0x8CC0, 0xF9C6, 0x9050,\n\t0xF9C7, 0x971E, 0xF9C8, 0x9C15, 0xF9C9, 0x58D1, 0xF9CA, 0x5B78,\n\t0xF9CB, 0x8650, 0xF9CC, 0x8B14, 0xF9CD, 0x9DB4, 0xF9CE, 0x5BD2,\n\t0xF9CF, 0x6068, 0xF9D0, 0x608D, 0xF9D1, 0x65F1, 0xF9D2, 0x6C57,\n\t0xF9D3, 0x6F22, 0xF9D4, 0x6FA3, 0xF9D5, 0x701A, 0xF9D6, 0x7F55,\n\t0xF9D7, 0x7FF0, 0xF9D8, 0x9591, 0xF9D9, 0x9592, 0xF9DA, 0x9650,\n\t0xF9DB, 0x97D3, 0xF9DC, 0x5272, 0xF9DD, 0x8F44, 0xF9DE, 0x51FD,\n\t0xF9DF, 0x542B, 0xF9E0, 0x54B8, 0xF9E1, 0x5563, 0xF9E2, 0x558A,\n\t0xF9E3, 0x6ABB, 0xF9E4, 0x6DB5, 0xF9E5, 0x7DD8, 0xF9E6, 0x8266,\n\t0xF9E7, 0x929C, 0xF9E8, 0x9677, 0xF9E9, 0x9E79, 0xF9EA, 0x5408,\n\t0xF9EB, 0x54C8, 0xF9EC, 0x76D2, 0xF9ED, 0x86E4, 0xF9EE, 0x95A4,\n\t0xF9EF, 0x95D4, 0xF9F0, 0x965C, 0xF9F1, 0x4EA2, 0xF9F2, 0x4F09,\n\t0xF9F3, 0x59EE, 0xF9F4, 0x5AE6, 0xF9F5, 0x5DF7, 0xF9F6, 0x6052,\n\t0xF9F7, 0x6297, 0xF9F8, 0x676D, 0xF9F9, 0x6841, 0xF9FA, 0x6C86,\n\t0xF9FB, 0x6E2F, 0xF9FC, 0x7F38, 0xF9FD, 0x809B, 0xF9FE, 0x822A,\n\t0xFAA1, 0xFA08, 0xFAA2, 0xFA09, 0xFAA3, 0x9805, 0xFAA4, 0x4EA5,\n\t0xFAA5, 0x5055, 0xFAA6, 0x54B3, 0xFAA7, 0x5793, 0xFAA8, 0x595A,\n\t0xFAA9, 0x5B69, 0xFAAA, 0x5BB3, 0xFAAB, 0x61C8, 0xFAAC, 0x6977,\n\t0xFAAD, 0x6D77, 0xFAAE, 0x7023, 0xFAAF, 0x87F9, 0xFAB0, 0x89E3,\n\t0xFAB1, 0x8A72, 0xFAB2, 0x8AE7, 0xFAB3, 0x9082, 0xFAB4, 0x99ED,\n\t0xFAB5, 0x9AB8, 0xFAB6, 0x52BE, 0xFAB7, 0x6838, 0xFAB8, 0x5016,\n\t0xFAB9, 0x5E78, 0xFABA, 0x674F, 0xFABB, 0x8347, 0xFABC, 0x884C,\n\t0xFABD, 0x4EAB, 0xFABE, 0x5411, 0xFABF, 0x56AE, 0xFAC0, 0x73E6,\n\t0xFAC1, 0x9115, 0xFAC2, 0x97FF, 0xFAC3, 0x9909, 0xFAC4, 0x9957,\n\t0xFAC5, 0x9999, 0xFAC6, 0x5653, 0xFAC7, 0x589F, 0xFAC8, 0x865B,\n\t0xFAC9, 0x8A31, 0xFACA, 0x61B2, 0xFACB, 0x6AF6, 0xFACC, 0x737B,\n\t0xFACD, 0x8ED2, 0xFACE, 0x6B47, 0xFACF, 0x96AA, 0xFAD0, 0x9A57,\n\t0xFAD1, 0x5955, 0xFAD2, 0x7200, 0xFAD3, 0x8D6B, 0xFAD4, 0x9769,\n\t0xFAD5, 0x4FD4, 0xFAD6, 0x5CF4, 0xFAD7, 0x5F26, 0xFAD8, 0x61F8,\n\t0xFAD9, 0x665B, 0xFADA, 0x6CEB, 0xFADB, 0x70AB, 0xFADC, 0x7384,\n\t0xFADD, 0x73B9, 0xFADE, 0x73FE, 0xFADF, 0x7729, 0xFAE0, 0x774D,\n\t0xFAE1, 0x7D43, 0xFAE2, 0x7D62, 0xFAE3, 0x7E23, 0xFAE4, 0x8237,\n\t0xFAE5, 0x8852, 0xFAE6, 0xFA0A, 0xFAE7, 0x8CE2, 0xFAE8, 0x9249,\n\t0xFAE9, 0x986F, 0xFAEA, 0x5B51, 0xFAEB, 0x7A74, 0xFAEC, 0x8840,\n\t0xFAED, 0x9801, 0xFAEE, 0x5ACC, 0xFAEF, 0x4FE0, 0xFAF0, 0x5354,\n\t0xFAF1, 0x593E, 0xFAF2, 0x5CFD, 0xFAF3, 0x633E, 0xFAF4, 0x6D79,\n\t0xFAF5, 0x72F9, 0xFAF6, 0x8105, 0xFAF7, 0x8107, 0xFAF8, 0x83A2,\n\t0xFAF9, 0x92CF, 0xFAFA, 0x9830, 0xFAFB, 0x4EA8, 0xFAFC, 0x5144,\n\t0xFAFD, 0x5211, 0xFAFE, 0x578B, 0xFBA1, 0x5F62, 0xFBA2, 0x6CC2,\n\t0xFBA3, 0x6ECE, 0xFBA4, 0x7005, 0xFBA5, 0x7050, 0xFBA6, 0x70AF,\n\t0xFBA7, 0x7192, 0xFBA8, 0x73E9, 0xFBA9, 0x7469, 0xFBAA, 0x834A,\n\t0xFBAB, 0x87A2, 0xFBAC, 0x8861, 0xFBAD, 0x9008, 0xFBAE, 0x90A2,\n\t0xFBAF, 0x93A3, 0xFBB0, 0x99A8, 0xFBB1, 0x516E, 0xFBB2, 0x5F57,\n\t0xFBB3, 0x60E0, 0xFBB4, 0x6167, 0xFBB5, 0x66B3, 0xFBB6, 0x8559,\n\t0xFBB7, 0x8E4A, 0xFBB8, 0x91AF, 0xFBB9, 0x978B, 0xFBBA, 0x4E4E,\n\t0xFBBB, 0x4E92, 0xFBBC, 0x547C, 0xFBBD, 0x58D5, 0xFBBE, 0x58FA,\n\t0xFBBF, 0x597D, 0xFBC0, 0x5CB5, 0xFBC1, 0x5F27, 0xFBC2, 0x6236,\n\t0xFBC3, 0x6248, 0xFBC4, 0x660A, 0xFBC5, 0x6667, 0xFBC6, 0x6BEB,\n\t0xFBC7, 0x6D69, 0xFBC8, 0x6DCF, 0xFBC9, 0x6E56, 0xFBCA, 0x6EF8,\n\t0xFBCB, 0x6F94, 0xFBCC, 0x6FE0, 0xFBCD, 0x6FE9, 0xFBCE, 0x705D,\n\t0xFBCF, 0x72D0, 0xFBD0, 0x7425, 0xFBD1, 0x745A, 0xFBD2, 0x74E0,\n\t0xFBD3, 0x7693, 0xFBD4, 0x795C, 0xFBD5, 0x7CCA, 0xFBD6, 0x7E1E,\n\t0xFBD7, 0x80E1, 0xFBD8, 0x82A6, 0xFBD9, 0x846B, 0xFBDA, 0x84BF,\n\t0xFBDB, 0x864E, 0xFBDC, 0x865F, 0xFBDD, 0x8774, 0xFBDE, 0x8B77,\n\t0xFBDF, 0x8C6A, 0xFBE0, 0x93AC, 0xFBE1, 0x9800, 0xFBE2, 0x9865,\n\t0xFBE3, 0x60D1, 0xFBE4, 0x6216, 0xFBE5, 0x9177, 0xFBE6, 0x5A5A,\n\t0xFBE7, 0x660F, 0xFBE8, 0x6DF7, 0xFBE9, 0x6E3E, 0xFBEA, 0x743F,\n\t0xFBEB, 0x9B42, 0xFBEC, 0x5FFD, 0xFBED, 0x60DA, 0xFBEE, 0x7B0F,\n\t0xFBEF, 0x54C4, 0xFBF0, 0x5F18, 0xFBF1, 0x6C5E, 0xFBF2, 0x6CD3,\n\t0xFBF3, 0x6D2A, 0xFBF4, 0x70D8, 0xFBF5, 0x7D05, 0xFBF6, 0x8679,\n\t0xFBF7, 0x8A0C, 0xFBF8, 0x9D3B, 0xFBF9, 0x5316, 0xFBFA, 0x548C,\n\t0xFBFB, 0x5B05, 0xFBFC, 0x6A3A, 0xFBFD, 0x706B, 0xFBFE, 0x7575,\n\t0xFCA1, 0x798D, 0xFCA2, 0x79BE, 0xFCA3, 0x82B1, 0xFCA4, 0x83EF,\n\t0xFCA5, 0x8A71, 0xFCA6, 0x8B41, 0xFCA7, 0x8CA8, 0xFCA8, 0x9774,\n\t0xFCA9, 0xFA0B, 0xFCAA, 0x64F4, 0xFCAB, 0x652B, 0xFCAC, 0x78BA,\n\t0xFCAD, 0x78BB, 0xFCAE, 0x7A6B, 0xFCAF, 0x4E38, 0xFCB0, 0x559A,\n\t0xFCB1, 0x5950, 0xFCB2, 0x5BA6, 0xFCB3, 0x5E7B, 0xFCB4, 0x60A3,\n\t0xFCB5, 0x63DB, 0xFCB6, 0x6B61, 0xFCB7, 0x6665, 0xFCB8, 0x6853,\n\t0xFCB9, 0x6E19, 0xFCBA, 0x7165, 0xFCBB, 0x74B0, 0xFCBC, 0x7D08,\n\t0xFCBD, 0x9084, 0xFCBE, 0x9A69, 0xFCBF, 0x9C25, 0xFCC0, 0x6D3B,\n\t0xFCC1, 0x6ED1, 0xFCC2, 0x733E, 0xFCC3, 0x8C41, 0xFCC4, 0x95CA,\n\t0xFCC5, 0x51F0, 0xFCC6, 0x5E4C, 0xFCC7, 0x5FA8, 0xFCC8, 0x604D,\n\t0xFCC9, 0x60F6, 0xFCCA, 0x6130, 0xFCCB, 0x614C, 0xFCCC, 0x6643,\n\t0xFCCD, 0x6644, 0xFCCE, 0x69A5, 0xFCCF, 0x6CC1, 0xFCD0, 0x6E5F,\n\t0xFCD1, 0x6EC9, 0xFCD2, 0x6F62, 0xFCD3, 0x714C, 0xFCD4, 0x749C,\n\t0xFCD5, 0x7687, 0xFCD6, 0x7BC1, 0xFCD7, 0x7C27, 0xFCD8, 0x8352,\n\t0xFCD9, 0x8757, 0xFCDA, 0x9051, 0xFCDB, 0x968D, 0xFCDC, 0x9EC3,\n\t0xFCDD, 0x532F, 0xFCDE, 0x56DE, 0xFCDF, 0x5EFB, 0xFCE0, 0x5F8A,\n\t0xFCE1, 0x6062, 0xFCE2, 0x6094, 0xFCE3, 0x61F7, 0xFCE4, 0x6666,\n\t0xFCE5, 0x6703, 0xFCE6, 0x6A9C, 0xFCE7, 0x6DEE, 0xFCE8, 0x6FAE,\n\t0xFCE9, 0x7070, 0xFCEA, 0x736A, 0xFCEB, 0x7E6A, 0xFCEC, 0x81BE,\n\t0xFCED, 0x8334, 0xFCEE, 0x86D4, 0xFCEF, 0x8AA8, 0xFCF0, 0x8CC4,\n\t0xFCF1, 0x5283, 0xFCF2, 0x7372, 0xFCF3, 0x5B96, 0xFCF4, 0x6A6B,\n\t0xFCF5, 0x9404, 0xFCF6, 0x54EE, 0xFCF7, 0x5686, 0xFCF8, 0x5B5D,\n\t0xFCF9, 0x6548, 0xFCFA, 0x6585, 0xFCFB, 0x66C9, 0xFCFC, 0x689F,\n\t0xFCFD, 0x6D8D, 0xFCFE, 0x6DC6, 0xFDA1, 0x723B, 0xFDA2, 0x80B4,\n\t0xFDA3, 0x9175, 0xFDA4, 0x9A4D, 0xFDA5, 0x4FAF, 0xFDA6, 0x5019,\n\t0xFDA7, 0x539A, 0xFDA8, 0x540E, 0xFDA9, 0x543C, 0xFDAA, 0x5589,\n\t0xFDAB, 0x55C5, 0xFDAC, 0x5E3F, 0xFDAD, 0x5F8C, 0xFDAE, 0x673D,\n\t0xFDAF, 0x7166, 0xFDB0, 0x73DD, 0xFDB1, 0x9005, 0xFDB2, 0x52DB,\n\t0xFDB3, 0x52F3, 0xFDB4, 0x5864, 0xFDB5, 0x58CE, 0xFDB6, 0x7104,\n\t0xFDB7, 0x718F, 0xFDB8, 0x71FB, 0xFDB9, 0x85B0, 0xFDBA, 0x8A13,\n\t0xFDBB, 0x6688, 0xFDBC, 0x85A8, 0xFDBD, 0x55A7, 0xFDBE, 0x6684,\n\t0xFDBF, 0x714A, 0xFDC0, 0x8431, 0xFDC1, 0x5349, 0xFDC2, 0x5599,\n\t0xFDC3, 0x6BC1, 0xFDC4, 0x5F59, 0xFDC5, 0x5FBD, 0xFDC6, 0x63EE,\n\t0xFDC7, 0x6689, 0xFDC8, 0x7147, 0xFDC9, 0x8AF1, 0xFDCA, 0x8F1D,\n\t0xFDCB, 0x9EBE, 0xFDCC, 0x4F11, 0xFDCD, 0x643A, 0xFDCE, 0x70CB,\n\t0xFDCF, 0x7566, 0xFDD0, 0x8667, 0xFDD1, 0x6064, 0xFDD2, 0x8B4E,\n\t0xFDD3, 0x9DF8, 0xFDD4, 0x5147, 0xFDD5, 0x51F6, 0xFDD6, 0x5308,\n\t0xFDD7, 0x6D36, 0xFDD8, 0x80F8, 0xFDD9, 0x9ED1, 0xFDDA, 0x6615,\n\t0xFDDB, 0x6B23, 0xFDDC, 0x7098, 0xFDDD, 0x75D5, 0xFDDE, 0x5403,\n\t0xFDDF, 0x5C79, 0xFDE0, 0x7D07, 0xFDE1, 0x8A16, 0xFDE2, 0x6B20,\n\t0xFDE3, 0x6B3D, 0xFDE4, 0x6B46, 0xFDE5, 0x5438, 0xFDE6, 0x6070,\n\t0xFDE7, 0x6D3D, 0xFDE8, 0x7FD5, 0xFDE9, 0x8208, 0xFDEA, 0x50D6,\n\t0xFDEB, 0x51DE, 0xFDEC, 0x559C, 0xFDED, 0x566B, 0xFDEE, 0x56CD,\n\t0xFDEF, 0x59EC, 0xFDF0, 0x5B09, 0xFDF1, 0x5E0C, 0xFDF2, 0x6199,\n\t0xFDF3, 0x6198, 0xFDF4, 0x6231, 0xFDF5, 0x665E, 0xFDF6, 0x66E6,\n\t0xFDF7, 0x7199, 0xFDF8, 0x71B9, 0xFDF9, 0x71BA, 0xFDFA, 0x72A7,\n\t0xFDFB, 0x79A7, 0xFDFC, 0x7A00, 0xFDFD, 0x7FB2, 0xFDFE, 0x8A70,\n\t0, 0\n};\n\n\n\nWCHAR ff_convert (\t/* Converted code, 0 means conversion error */\n\tWCHAR\tchr,\t/* Character code to be converted */\n\tUINT\tdir\t\t/* 0: Unicode to OEM code, 1: OEM code to Unicode */\n)\n{\n\tconst WCHAR *p;\n\tWCHAR c;\n\tint i, n, li, hi;\n\n\n\tif (chr < 0x80) {\t/* ASCII */\n\t\tc = chr;\n\t} else {\n\t\tif (dir) {\t\t/* OEM code to unicode */\n\t\t\tp = oem2uni;\n\t\t\thi = sizeof oem2uni / 4 - 1;\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tp = uni2oem;\n\t\t\thi = sizeof uni2oem / 4 - 1;\n\t\t}\n\t\tli = 0;\n\t\tfor (n = 16; n; n--) {\n\t\t\ti = li + (hi - li) / 2;\n\t\t\tif (chr == p[i * 2]) break;\n\t\t\tif (chr > p[i * 2])\n\t\t\t\tli = i;\n\t\t\telse\n\t\t\t\thi = i;\n\t\t}\n\t\tc = n ? p[i * 2 + 1] : 0;\n\t}\n\n\treturn c;\n}\n\n\n\nWCHAR ff_wtoupper (\t/* Returns upper converted character */\n\tWCHAR chr\t\t/* Unicode character to be upper converted (BMP only) */\n)\n{\n\t/* Compressed upper conversion table */\n\tstatic const WCHAR cvt1[] = {\t/* U+0000 - U+0FFF */\n\t\t/* Basic Latin */\n\t\t0x0061,0x031A,\n\t\t/* Latin-1 Supplement */\n\t\t0x00E0,0x0317,  0x00F8,0x0307,  0x00FF,0x0001,0x0178,\n\t\t/* Latin Extended-A */\n\t\t0x0100,0x0130,  0x0132,0x0106,  0x0139,0x0110,  0x014A,0x012E,  0x0179,0x0106,\n\t\t/* Latin Extended-B */\n\t\t0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,\n\t\t0x01CD,0x0110,  0x01DD,0x0001,0x018E,  0x01DE,0x0112,  0x01F3,0x0003,0x01F1,0x01F4,0x01F4,  0x01F8,0x0128,\n\t\t0x0222,0x0112,  0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,  0x0246,0x010A,\n\t\t/* IPA Extensions */\n\t\t0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,\n\t\t/* Greek, Coptic */\n\t\t0x037B,0x0003,0x03FD,0x03FE,0x03FF,  0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,  0x03B1,0x0311,\n\t\t0x03C2,0x0002,0x03A3,0x03A3,  0x03C4,0x0308,  0x03CC,0x0003,0x038C,0x038E,0x038F,  0x03D8,0x0118,\n\t\t0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,\n\t\t/* Cyrillic */\n\t\t0x0430,0x0320,  0x0450,0x0710,  0x0460,0x0122,  0x048A,0x0136,  0x04C1,0x010E,  0x04CF,0x0001,0x04C0,  0x04D0,0x0144,\n\t\t/* Armenian */\n\t\t0x0561,0x0426,\n\n\t\t0x0000\n\t};\n\tstatic const WCHAR cvt2[] = {\t/* U+1000 - U+FFFF */\n\t\t/* Phonetic Extensions */\n\t\t0x1D7D,0x0001,0x2C63,\n\t\t/* Latin Extended Additional */\n\t\t0x1E00,0x0196,  0x1EA0,0x015A,\n\t\t/* Greek Extended */\n\t\t0x1F00,0x0608,  0x1F10,0x0606,  0x1F20,0x0608,  0x1F30,0x0608,  0x1F40,0x0606,\n\t\t0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,  0x1F60,0x0608,\n\t\t0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,\n\t\t0x1F80,0x0608,  0x1F90,0x0608,  0x1FA0,0x0608,  0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,\n\t\t0x1FCC,0x0001,0x1FC3,  0x1FD0,0x0602,  0x1FE0,0x0602,  0x1FE5,0x0001,0x1FEC,  0x1FF2,0x0001,0x1FFC,\n\t\t/* Letterlike Symbols */\n\t\t0x214E,0x0001,0x2132,\n\t\t/* Number forms */\n\t\t0x2170,0x0210,  0x2184,0x0001,0x2183,\n\t\t/* Enclosed Alphanumerics */\n\t\t0x24D0,0x051A,  0x2C30,0x042F,\n\t\t/* Latin Extended-C */\n\t\t0x2C60,0x0102,  0x2C67,0x0106, 0x2C75,0x0102,\n\t\t/* Coptic */\n\t\t0x2C80,0x0164,\n\t\t/* Georgian Supplement */\n\t\t0x2D00,0x0826,\n\t\t/* Full-width */\n\t\t0xFF41,0x031A,\n\n\t\t0x0000\n\t};\n\tconst WCHAR *p;\n\tWCHAR bc, nc, cmd;\n\n\n\tp = chr < 0x1000 ? cvt1 : cvt2;\n\tfor (;;) {\n\t\tbc = *p++;\t\t\t\t\t\t\t\t/* Get block base */\n\t\tif (!bc || chr < bc) break;\n\t\tnc = *p++; cmd = nc >> 8; nc &= 0xFF;\t/* Get processing command and block size */\n\t\tif (chr < bc + nc) {\t/* In the block? */\n\t\t\tswitch (cmd) {\n\t\t\tcase 0:\tchr = p[chr - bc]; break;\t\t/* Table conversion */\n\t\t\tcase 1:\tchr -= (chr - bc) & 1; break;\t/* Case pairs */\n\t\t\tcase 2: chr -= 16; break;\t\t\t\t/* Shift -16 */\n\t\t\tcase 3:\tchr -= 32; break;\t\t\t\t/* Shift -32 */\n\t\t\tcase 4:\tchr -= 48; break;\t\t\t\t/* Shift -48 */\n\t\t\tcase 5:\tchr -= 26; break;\t\t\t\t/* Shift -26 */\n\t\t\tcase 6:\tchr += 8; break;\t\t\t\t/* Shift +8 */\n\t\t\tcase 7: chr -= 80; break;\t\t\t\t/* Shift -80 */\n\t\t\tcase 8:\tchr -= 0x1C60; break;\t\t\t/* Shift -0x1C60 */\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!cmd) p += nc;\n\t}\n\n\treturn chr;\n}\n\n"
  },
  {
    "path": "lib/fatfs/option/cc950.c",
    "content": "/*------------------------------------------------------------------------*/\n/* Unicode - OEM code bidirectional converter  (C)ChaN, 2015              */\n/* CP950 (Traditional Chinese Big5)                                       */\n/*------------------------------------------------------------------------*/\n\n#include \"../ff.h\"\n\n\n#if !_USE_LFN || _CODE_PAGE != 950\n#error This file is not needed in current configuration. Remove from the project.\n#endif\n\n\nstatic\nconst WCHAR uni2oem[] = {\n/*  Unicode - OEM,  Unicode - OEM,  Unicode - OEM,  Unicode - OEM */\n\t0x00A7, 0xA1B1, 0x00AF, 0xA1C2, 0x00B0, 0xA258, 0x00B1, 0xA1D3,\n\t0x00B7, 0xA150, 0x00D7, 0xA1D1, 0x00F7, 0xA1D2, 0x02C7, 0xA3BE,\n\t0x02C9, 0xA3BC, 0x02CA, 0xA3BD, 0x02CB, 0xA3BF, 0x02CD, 0xA1C5,\n\t0x02D9, 0xA3BB, 0x0391, 0xA344, 0x0392, 0xA345, 0x0393, 0xA346,\n\t0x0394, 0xA347, 0x0395, 0xA348, 0x0396, 0xA349, 0x0397, 0xA34A,\n\t0x0398, 0xA34B, 0x0399, 0xA34C, 0x039A, 0xA34D, 0x039B, 0xA34E,\n\t0x039C, 0xA34F, 0x039D, 0xA350, 0x039E, 0xA351, 0x039F, 0xA352,\n\t0x03A0, 0xA353, 0x03A1, 0xA354, 0x03A3, 0xA355, 0x03A4, 0xA356,\n\t0x03A5, 0xA357, 0x03A6, 0xA358, 0x03A7, 0xA359, 0x03A8, 0xA35A,\n\t0x03A9, 0xA35B, 0x03B1, 0xA35C, 0x03B2, 0xA35D, 0x03B3, 0xA35E,\n\t0x03B4, 0xA35F, 0x03B5, 0xA360, 0x03B6, 0xA361, 0x03B7, 0xA362,\n\t0x03B8, 0xA363, 0x03B9, 0xA364, 0x03BA, 0xA365, 0x03BB, 0xA366,\n\t0x03BC, 0xA367, 0x03BD, 0xA368, 0x03BE, 0xA369, 0x03BF, 0xA36A,\n\t0x03C0, 0xA36B, 0x03C1, 0xA36C, 0x03C3, 0xA36D, 0x03C4, 0xA36E,\n\t0x03C5, 0xA36F, 0x03C6, 0xA370, 0x03C7, 0xA371, 0x03C8, 0xA372,\n\t0x03C9, 0xA373, 0x2013, 0xA156, 0x2014, 0xA158, 0x2018, 0xA1A5,\n\t0x2019, 0xA1A6, 0x201C, 0xA1A7, 0x201D, 0xA1A8, 0x2025, 0xA14C,\n\t0x2026, 0xA14B, 0x2027, 0xA145, 0x2032, 0xA1AC, 0x2035, 0xA1AB,\n\t0x203B, 0xA1B0, 0x20AC, 0xA3E1, 0x2103, 0xA24A, 0x2105, 0xA1C1,\n\t0x2109, 0xA24B, 0x2160, 0xA2B9, 0x2161, 0xA2BA, 0x2162, 0xA2BB,\n\t0x2163, 0xA2BC, 0x2164, 0xA2BD, 0x2165, 0xA2BE, 0x2166, 0xA2BF,\n\t0x2167, 0xA2C0, 0x2168, 0xA2C1, 0x2169, 0xA2C2, 0x2190, 0xA1F6,\n\t0x2191, 0xA1F4, 0x2192, 0xA1F7, 0x2193, 0xA1F5, 0x2196, 0xA1F8,\n\t0x2197, 0xA1F9, 0x2198, 0xA1FB, 0x2199, 0xA1FA, 0x2215, 0xA241,\n\t0x221A, 0xA1D4, 0x221E, 0xA1DB, 0x221F, 0xA1E8, 0x2220, 0xA1E7,\n\t0x2223, 0xA1FD, 0x2225, 0xA1FC, 0x2229, 0xA1E4, 0x222A, 0xA1E5,\n\t0x222B, 0xA1EC, 0x222E, 0xA1ED, 0x2234, 0xA1EF, 0x2235, 0xA1EE,\n\t0x2252, 0xA1DC, 0x2260, 0xA1DA, 0x2261, 0xA1DD, 0x2266, 0xA1D8,\n\t0x2267, 0xA1D9, 0x2295, 0xA1F2, 0x2299, 0xA1F3, 0x22A5, 0xA1E6,\n\t0x22BF, 0xA1E9, 0x2500, 0xA277, 0x2502, 0xA278, 0x250C, 0xA27A,\n\t0x2510, 0xA27B, 0x2514, 0xA27C, 0x2518, 0xA27D, 0x251C, 0xA275,\n\t0x2524, 0xA274, 0x252C, 0xA273, 0x2534, 0xA272, 0x253C, 0xA271,\n\t0x2550, 0xA2A4, 0x2550, 0xF9F9, 0x2551, 0xF9F8, 0x2552, 0xF9E6,\n\t0x2553, 0xF9EF, 0x2554, 0xF9DD, 0x2555, 0xF9E8, 0x2556, 0xF9F1,\n\t0x2557, 0xF9DF, 0x2558, 0xF9EC, 0x2559, 0xF9F5, 0x255A, 0xF9E3,\n\t0x255B, 0xF9EE, 0x255C, 0xF9F7, 0x255D, 0xF9E5, 0x255E, 0xA2A5,\n\t0x255E, 0xF9E9, 0x255F, 0xF9F2, 0x2560, 0xF9E0, 0x2561, 0xA2A7,\n\t0x2561, 0xF9EB, 0x2562, 0xF9F4, 0x2563, 0xF9E2, 0x2564, 0xF9E7,\n\t0x2565, 0xF9F0, 0x2566, 0xF9DE, 0x2567, 0xF9ED, 0x2568, 0xF9F6,\n\t0x2569, 0xF9E4, 0x256A, 0xA2A6, 0x256A, 0xF9EA, 0x256B, 0xF9F3,\n\t0x256C, 0xF9E1, 0x256D, 0xA27E, 0x256D, 0xF9FA, 0x256E, 0xA2A1,\n\t0x256E, 0xF9FB, 0x256F, 0xA2A3, 0x256F, 0xF9FD, 0x2570, 0xA2A2,\n\t0x2570, 0xF9FC, 0x2571, 0xA2AC, 0x2572, 0xA2AD, 0x2573, 0xA2AE,\n\t0x2574, 0xA15A, 0x2581, 0xA262, 0x2582, 0xA263, 0x2583, 0xA264,\n\t0x2584, 0xA265, 0x2585, 0xA266, 0x2586, 0xA267, 0x2587, 0xA268,\n\t0x2588, 0xA269, 0x2589, 0xA270, 0x258A, 0xA26F, 0x258B, 0xA26E,\n\t0x258C, 0xA26D, 0x258D, 0xA26C, 0x258E, 0xA26B, 0x258F, 0xA26A,\n\t0x2593, 0xF9FE, 0x2594, 0xA276, 0x2595, 0xA279, 0x25A0, 0xA1BD,\n\t0x25A1, 0xA1BC, 0x25B2, 0xA1B6, 0x25B3, 0xA1B5, 0x25BC, 0xA1BF,\n\t0x25BD, 0xA1BE, 0x25C6, 0xA1BB, 0x25C7, 0xA1BA, 0x25CB, 0xA1B3,\n\t0x25CE, 0xA1B7, 0x25CF, 0xA1B4, 0x25E2, 0xA2A8, 0x25E3, 0xA2A9,\n\t0x25E4, 0xA2AB, 0x25E5, 0xA2AA, 0x2605, 0xA1B9, 0x2606, 0xA1B8,\n\t0x2640, 0xA1F0, 0x2642, 0xA1F1, 0x3000, 0xA140, 0x3001, 0xA142,\n\t0x3002, 0xA143, 0x3003, 0xA1B2, 0x3008, 0xA171, 0x3009, 0xA172,\n\t0x300A, 0xA16D, 0x300B, 0xA16E, 0x300C, 0xA175, 0x300D, 0xA176,\n\t0x300E, 0xA179, 0x300F, 0xA17A, 0x3010, 0xA169, 0x3011, 0xA16A,\n\t0x3012, 0xA245, 0x3014, 0xA165, 0x3015, 0xA166, 0x301D, 0xA1A9,\n\t0x301E, 0xA1AA, 0x3021, 0xA2C3, 0x3022, 0xA2C4, 0x3023, 0xA2C5,\n\t0x3024, 0xA2C6, 0x3025, 0xA2C7, 0x3026, 0xA2C8, 0x3027, 0xA2C9,\n\t0x3028, 0xA2CA, 0x3029, 0xA2CB, 0x3105, 0xA374, 0x3106, 0xA375,\n\t0x3107, 0xA376, 0x3108, 0xA377, 0x3109, 0xA378, 0x310A, 0xA379,\n\t0x310B, 0xA37A, 0x310C, 0xA37B, 0x310D, 0xA37C, 0x310E, 0xA37D,\n\t0x310F, 0xA37E, 0x3110, 0xA3A1, 0x3111, 0xA3A2, 0x3112, 0xA3A3,\n\t0x3113, 0xA3A4, 0x3114, 0xA3A5, 0x3115, 0xA3A6, 0x3116, 0xA3A7,\n\t0x3117, 0xA3A8, 0x3118, 0xA3A9, 0x3119, 0xA3AA, 0x311A, 0xA3AB,\n\t0x311B, 0xA3AC, 0x311C, 0xA3AD, 0x311D, 0xA3AE, 0x311E, 0xA3AF,\n\t0x311F, 0xA3B0, 0x3120, 0xA3B1, 0x3121, 0xA3B2, 0x3122, 0xA3B3,\n\t0x3123, 0xA3B4, 0x3124, 0xA3B5, 0x3125, 0xA3B6, 0x3126, 0xA3B7,\n\t0x3127, 0xA3B8, 0x3128, 0xA3B9, 0x3129, 0xA3BA, 0x32A3, 0xA1C0,\n\t0x338E, 0xA255, 0x338F, 0xA256, 0x339C, 0xA250, 0x339D, 0xA251,\n\t0x339E, 0xA252, 0x33A1, 0xA254, 0x33C4, 0xA257, 0x33CE, 0xA253,\n\t0x33D1, 0xA1EB, 0x33D2, 0xA1EA, 0x33D5, 0xA24F, 0x4E00, 0xA440,\n\t0x4E01, 0xA442, 0x4E03, 0xA443, 0x4E07, 0xC945, 0x4E08, 0xA456,\n\t0x4E09, 0xA454, 0x4E0A, 0xA457, 0x4E0B, 0xA455, 0x4E0C, 0xC946,\n\t0x4E0D, 0xA4A3, 0x4E0E, 0xC94F, 0x4E0F, 0xC94D, 0x4E10, 0xA4A2,\n\t0x4E11, 0xA4A1, 0x4E14, 0xA542, 0x4E15, 0xA541, 0x4E16, 0xA540,\n\t0x4E18, 0xA543, 0x4E19, 0xA4FE, 0x4E1E, 0xA5E0, 0x4E1F, 0xA5E1,\n\t0x4E26, 0xA8C3, 0x4E2B, 0xA458, 0x4E2D, 0xA4A4, 0x4E2E, 0xC950,\n\t0x4E30, 0xA4A5, 0x4E31, 0xC963, 0x4E32, 0xA6EA, 0x4E33, 0xCBB1,\n\t0x4E38, 0xA459, 0x4E39, 0xA4A6, 0x4E3B, 0xA544, 0x4E3C, 0xC964,\n\t0x4E42, 0xC940, 0x4E43, 0xA444, 0x4E45, 0xA45B, 0x4E47, 0xC947,\n\t0x4E48, 0xA45C, 0x4E4B, 0xA4A7, 0x4E4D, 0xA545, 0x4E4E, 0xA547,\n\t0x4E4F, 0xA546, 0x4E52, 0xA5E2, 0x4E53, 0xA5E3, 0x4E56, 0xA8C4,\n\t0x4E58, 0xADBC, 0x4E59, 0xA441, 0x4E5C, 0xC941, 0x4E5D, 0xA445,\n\t0x4E5E, 0xA45E, 0x4E5F, 0xA45D, 0x4E69, 0xA5E4, 0x4E73, 0xA8C5,\n\t0x4E7E, 0xB0AE, 0x4E7F, 0xD44B, 0x4E82, 0xB6C3, 0x4E83, 0xDCB1,\n\t0x4E84, 0xDCB2, 0x4E86, 0xA446, 0x4E88, 0xA4A9, 0x4E8B, 0xA8C6,\n\t0x4E8C, 0xA447, 0x4E8D, 0xC948, 0x4E8E, 0xA45F, 0x4E91, 0xA4AA,\n\t0x4E92, 0xA4AC, 0x4E93, 0xC951, 0x4E94, 0xA4AD, 0x4E95, 0xA4AB,\n\t0x4E99, 0xA5E5, 0x4E9B, 0xA8C7, 0x4E9E, 0xA8C8, 0x4E9F, 0xAB45,\n\t0x4EA1, 0xA460, 0x4EA2, 0xA4AE, 0x4EA4, 0xA5E6, 0x4EA5, 0xA5E8,\n\t0x4EA6, 0xA5E7, 0x4EA8, 0xA6EB, 0x4EAB, 0xA8C9, 0x4EAC, 0xA8CA,\n\t0x4EAD, 0xAB46, 0x4EAE, 0xAB47, 0x4EB3, 0xADBD, 0x4EB6, 0xDCB3,\n\t0x4EB9, 0xF6D6, 0x4EBA, 0xA448, 0x4EC0, 0xA4B0, 0x4EC1, 0xA4AF,\n\t0x4EC2, 0xC952, 0x4EC3, 0xA4B1, 0x4EC4, 0xA4B7, 0x4EC6, 0xA4B2,\n\t0x4EC7, 0xA4B3, 0x4EC8, 0xC954, 0x4EC9, 0xC953, 0x4ECA, 0xA4B5,\n\t0x4ECB, 0xA4B6, 0x4ECD, 0xA4B4, 0x4ED4, 0xA54A, 0x4ED5, 0xA54B,\n\t0x4ED6, 0xA54C, 0x4ED7, 0xA54D, 0x4ED8, 0xA549, 0x4ED9, 0xA550,\n\t0x4EDA, 0xC96A, 0x4EDC, 0xC966, 0x4EDD, 0xC969, 0x4EDE, 0xA551,\n\t0x4EDF, 0xA561, 0x4EE1, 0xC968, 0x4EE3, 0xA54E, 0x4EE4, 0xA54F,\n\t0x4EE5, 0xA548, 0x4EE8, 0xC965, 0x4EE9, 0xC967, 0x4EF0, 0xA5F5,\n\t0x4EF1, 0xC9B0, 0x4EF2, 0xA5F2, 0x4EF3, 0xA5F6, 0x4EF4, 0xC9BA,\n\t0x4EF5, 0xC9AE, 0x4EF6, 0xA5F3, 0x4EF7, 0xC9B2, 0x4EFB, 0xA5F4,\n\t0x4EFD, 0xA5F7, 0x4EFF, 0xA5E9, 0x4F00, 0xC9B1, 0x4F01, 0xA5F8,\n\t0x4F02, 0xC9B5, 0x4F04, 0xC9B9, 0x4F05, 0xC9B6, 0x4F08, 0xC9B3,\n\t0x4F09, 0xA5EA, 0x4F0A, 0xA5EC, 0x4F0B, 0xA5F9, 0x4F0D, 0xA5EE,\n\t0x4F0E, 0xC9AB, 0x4F0F, 0xA5F1, 0x4F10, 0xA5EF, 0x4F11, 0xA5F0,\n\t0x4F12, 0xC9BB, 0x4F13, 0xC9B8, 0x4F14, 0xC9AF, 0x4F15, 0xA5ED,\n\t0x4F18, 0xC9AC, 0x4F19, 0xA5EB, 0x4F1D, 0xC9B4, 0x4F22, 0xC9B7,\n\t0x4F2C, 0xC9AD, 0x4F2D, 0xCA66, 0x4F2F, 0xA742, 0x4F30, 0xA6F4,\n\t0x4F33, 0xCA67, 0x4F34, 0xA6F1, 0x4F36, 0xA744, 0x4F38, 0xA6F9,\n\t0x4F3A, 0xA6F8, 0x4F3B, 0xCA5B, 0x4F3C, 0xA6FC, 0x4F3D, 0xA6F7,\n\t0x4F3E, 0xCA60, 0x4F3F, 0xCA68, 0x4F41, 0xCA64, 0x4F43, 0xA6FA,\n\t0x4F46, 0xA6FD, 0x4F47, 0xA6EE, 0x4F48, 0xA747, 0x4F49, 0xCA5D,\n\t0x4F4C, 0xCBBD, 0x4F4D, 0xA6EC, 0x4F4E, 0xA743, 0x4F4F, 0xA6ED,\n\t0x4F50, 0xA6F5, 0x4F51, 0xA6F6, 0x4F52, 0xCA62, 0x4F53, 0xCA5E,\n\t0x4F54, 0xA6FB, 0x4F55, 0xA6F3, 0x4F56, 0xCA5A, 0x4F57, 0xA6EF,\n\t0x4F58, 0xCA65, 0x4F59, 0xA745, 0x4F5A, 0xA748, 0x4F5B, 0xA6F2,\n\t0x4F5C, 0xA740, 0x4F5D, 0xA746, 0x4F5E, 0xA6F0, 0x4F5F, 0xCA63,\n\t0x4F60, 0xA741, 0x4F61, 0xCA69, 0x4F62, 0xCA5C, 0x4F63, 0xA6FE,\n\t0x4F64, 0xCA5F, 0x4F67, 0xCA61, 0x4F69, 0xA8D8, 0x4F6A, 0xCBBF,\n\t0x4F6B, 0xCBCB, 0x4F6C, 0xA8D0, 0x4F6E, 0xCBCC, 0x4F6F, 0xA8CB,\n\t0x4F70, 0xA8D5, 0x4F73, 0xA8CE, 0x4F74, 0xCBB9, 0x4F75, 0xA8D6,\n\t0x4F76, 0xCBB8, 0x4F77, 0xCBBC, 0x4F78, 0xCBC3, 0x4F79, 0xCBC1,\n\t0x4F7A, 0xA8DE, 0x4F7B, 0xA8D9, 0x4F7C, 0xCBB3, 0x4F7D, 0xCBB5,\n\t0x4F7E, 0xA8DB, 0x4F7F, 0xA8CF, 0x4F80, 0xCBB6, 0x4F81, 0xCBC2,\n\t0x4F82, 0xCBC9, 0x4F83, 0xA8D4, 0x4F84, 0xCBBB, 0x4F85, 0xCBB4,\n\t0x4F86, 0xA8D3, 0x4F87, 0xCBB7, 0x4F88, 0xA8D7, 0x4F89, 0xCBBA,\n\t0x4F8B, 0xA8D2, 0x4F8D, 0xA8CD, 0x4F8F, 0xA8DC, 0x4F90, 0xCBC4,\n\t0x4F91, 0xA8DD, 0x4F92, 0xCBC8, 0x4F94, 0xCBC6, 0x4F95, 0xCBCA,\n\t0x4F96, 0xA8DA, 0x4F97, 0xCBBE, 0x4F98, 0xCBB2, 0x4F9A, 0xCBC0,\n\t0x4F9B, 0xA8D1, 0x4F9C, 0xCBC5, 0x4F9D, 0xA8CC, 0x4F9E, 0xCBC7,\n\t0x4FAE, 0xAB56, 0x4FAF, 0xAB4A, 0x4FB2, 0xCDE0, 0x4FB3, 0xCDE8,\n\t0x4FB5, 0xAB49, 0x4FB6, 0xAB51, 0x4FB7, 0xAB5D, 0x4FB9, 0xCDEE,\n\t0x4FBA, 0xCDEC, 0x4FBB, 0xCDE7, 0x4FBF, 0xAB4B, 0x4FC0, 0xCDED,\n\t0x4FC1, 0xCDE3, 0x4FC2, 0xAB59, 0x4FC3, 0xAB50, 0x4FC4, 0xAB58,\n\t0x4FC5, 0xCDDE, 0x4FC7, 0xCDEA, 0x4FC9, 0xCDE1, 0x4FCA, 0xAB54,\n\t0x4FCB, 0xCDE2, 0x4FCD, 0xCDDD, 0x4FCE, 0xAB5B, 0x4FCF, 0xAB4E,\n\t0x4FD0, 0xAB57, 0x4FD1, 0xAB4D, 0x4FD3, 0xCDDF, 0x4FD4, 0xCDE4,\n\t0x4FD6, 0xCDEB, 0x4FD7, 0xAB55, 0x4FD8, 0xAB52, 0x4FD9, 0xCDE6,\n\t0x4FDA, 0xAB5A, 0x4FDB, 0xCDE9, 0x4FDC, 0xCDE5, 0x4FDD, 0xAB4F,\n\t0x4FDE, 0xAB5C, 0x4FDF, 0xAB53, 0x4FE0, 0xAB4C, 0x4FE1, 0xAB48,\n\t0x4FEC, 0xCDEF, 0x4FEE, 0xADD7, 0x4FEF, 0xADC1, 0x4FF1, 0xADD1,\n\t0x4FF3, 0xADD6, 0x4FF4, 0xD0D0, 0x4FF5, 0xD0CF, 0x4FF6, 0xD0D4,\n\t0x4FF7, 0xD0D5, 0x4FF8, 0xADC4, 0x4FFA, 0xADCD, 0x4FFE, 0xADDA,\n\t0x5000, 0xADCE, 0x5005, 0xD0C9, 0x5006, 0xADC7, 0x5007, 0xD0CA,\n\t0x5009, 0xADDC, 0x500B, 0xADD3, 0x500C, 0xADBE, 0x500D, 0xADBF,\n\t0x500E, 0xD0DD, 0x500F, 0xB0BF, 0x5011, 0xADCC, 0x5012, 0xADCB,\n\t0x5013, 0xD0CB, 0x5014, 0xADCF, 0x5015, 0xD45B, 0x5016, 0xADC6,\n\t0x5017, 0xD0D6, 0x5018, 0xADD5, 0x5019, 0xADD4, 0x501A, 0xADCA,\n\t0x501B, 0xD0CE, 0x501C, 0xD0D7, 0x501E, 0xD0C8, 0x501F, 0xADC9,\n\t0x5020, 0xD0D8, 0x5021, 0xADD2, 0x5022, 0xD0CC, 0x5023, 0xADC0,\n\t0x5025, 0xADC3, 0x5026, 0xADC2, 0x5027, 0xD0D9, 0x5028, 0xADD0,\n\t0x5029, 0xADC5, 0x502A, 0xADD9, 0x502B, 0xADDB, 0x502C, 0xD0D3,\n\t0x502D, 0xADD8, 0x502F, 0xD0DB, 0x5030, 0xD0CD, 0x5031, 0xD0DC,\n\t0x5033, 0xD0D1, 0x5035, 0xD0DA, 0x5037, 0xD0D2, 0x503C, 0xADC8,\n\t0x5040, 0xD463, 0x5041, 0xD457, 0x5043, 0xB0B3, 0x5045, 0xD45C,\n\t0x5046, 0xD462, 0x5047, 0xB0B2, 0x5048, 0xD455, 0x5049, 0xB0B6,\n\t0x504A, 0xD459, 0x504B, 0xD452, 0x504C, 0xB0B4, 0x504D, 0xD456,\n\t0x504E, 0xB0B9, 0x504F, 0xB0BE, 0x5051, 0xD467, 0x5053, 0xD451,\n\t0x5055, 0xB0BA, 0x5057, 0xD466, 0x505A, 0xB0B5, 0x505B, 0xD458,\n\t0x505C, 0xB0B1, 0x505D, 0xD453, 0x505E, 0xD44F, 0x505F, 0xD45D,\n\t0x5060, 0xD450, 0x5061, 0xD44E, 0x5062, 0xD45A, 0x5063, 0xD460,\n\t0x5064, 0xD461, 0x5065, 0xB0B7, 0x5068, 0xD85B, 0x5069, 0xD45E,\n\t0x506A, 0xD44D, 0x506B, 0xD45F, 0x506D, 0xB0C1, 0x506E, 0xD464,\n\t0x506F, 0xB0C0, 0x5070, 0xD44C, 0x5072, 0xD454, 0x5073, 0xD465,\n\t0x5074, 0xB0BC, 0x5075, 0xB0BB, 0x5076, 0xB0B8, 0x5077, 0xB0BD,\n\t0x507A, 0xB0AF, 0x507D, 0xB0B0, 0x5080, 0xB3C8, 0x5082, 0xD85E,\n\t0x5083, 0xD857, 0x5085, 0xB3C5, 0x5087, 0xD85F, 0x508B, 0xD855,\n\t0x508C, 0xD858, 0x508D, 0xB3C4, 0x508E, 0xD859, 0x5091, 0xB3C7,\n\t0x5092, 0xD85D, 0x5094, 0xD853, 0x5095, 0xD852, 0x5096, 0xB3C9,\n\t0x5098, 0xB3CA, 0x5099, 0xB3C6, 0x509A, 0xB3CB, 0x509B, 0xD851,\n\t0x509C, 0xD85C, 0x509D, 0xD85A, 0x509E, 0xD854, 0x50A2, 0xB3C3,\n\t0x50A3, 0xD856, 0x50AC, 0xB6CA, 0x50AD, 0xB6C4, 0x50AE, 0xDCB7,\n\t0x50AF, 0xB6CD, 0x50B0, 0xDCBD, 0x50B1, 0xDCC0, 0x50B2, 0xB6C6,\n\t0x50B3, 0xB6C7, 0x50B4, 0xDCBA, 0x50B5, 0xB6C5, 0x50B6, 0xDCC3,\n\t0x50B7, 0xB6CB, 0x50B8, 0xDCC4, 0x50BA, 0xDCBF, 0x50BB, 0xB6CC,\n\t0x50BD, 0xDCB4, 0x50BE, 0xB6C9, 0x50BF, 0xDCB5, 0x50C1, 0xDCBE,\n\t0x50C2, 0xDCBC, 0x50C4, 0xDCB8, 0x50C5, 0xB6C8, 0x50C6, 0xDCB6,\n\t0x50C7, 0xB6CE, 0x50C8, 0xDCBB, 0x50C9, 0xDCC2, 0x50CA, 0xDCB9,\n\t0x50CB, 0xDCC1, 0x50CE, 0xB9B6, 0x50CF, 0xB9B3, 0x50D1, 0xB9B4,\n\t0x50D3, 0xE0F9, 0x50D4, 0xE0F1, 0x50D5, 0xB9B2, 0x50D6, 0xB9AF,\n\t0x50D7, 0xE0F2, 0x50DA, 0xB9B1, 0x50DB, 0xE0F5, 0x50DD, 0xE0F7,\n\t0x50E0, 0xE0FE, 0x50E3, 0xE0FD, 0x50E4, 0xE0F8, 0x50E5, 0xB9AE,\n\t0x50E6, 0xE0F0, 0x50E7, 0xB9AC, 0x50E8, 0xE0F3, 0x50E9, 0xB9B7,\n\t0x50EA, 0xE0F6, 0x50EC, 0xE0FA, 0x50ED, 0xB9B0, 0x50EE, 0xB9AD,\n\t0x50EF, 0xE0FC, 0x50F0, 0xE0FB, 0x50F1, 0xB9B5, 0x50F3, 0xE0F4,\n\t0x50F5, 0xBBF8, 0x50F6, 0xE4EC, 0x50F8, 0xE4E9, 0x50F9, 0xBBF9,\n\t0x50FB, 0xBBF7, 0x50FD, 0xE4F0, 0x50FE, 0xE4ED, 0x50FF, 0xE4E6,\n\t0x5100, 0xBBF6, 0x5102, 0xBBFA, 0x5103, 0xE4E7, 0x5104, 0xBBF5,\n\t0x5105, 0xBBFD, 0x5106, 0xE4EA, 0x5107, 0xE4EB, 0x5108, 0xBBFB,\n\t0x5109, 0xBBFC, 0x510A, 0xE4F1, 0x510B, 0xE4EE, 0x510C, 0xE4EF,\n\t0x5110, 0xBEAA, 0x5111, 0xE8F8, 0x5112, 0xBEA7, 0x5113, 0xE8F5,\n\t0x5114, 0xBEA9, 0x5115, 0xBEAB, 0x5117, 0xE8F6, 0x5118, 0xBEA8,\n\t0x511A, 0xE8F7, 0x511C, 0xE8F4, 0x511F, 0xC076, 0x5120, 0xECBD,\n\t0x5121, 0xC077, 0x5122, 0xECBB, 0x5124, 0xECBC, 0x5125, 0xECBA,\n\t0x5126, 0xECB9, 0x5129, 0xECBE, 0x512A, 0xC075, 0x512D, 0xEFB8,\n\t0x512E, 0xEFB9, 0x5130, 0xE4E8, 0x5131, 0xEFB7, 0x5132, 0xC078,\n\t0x5133, 0xC35F, 0x5134, 0xF1EB, 0x5135, 0xF1EC, 0x5137, 0xC4D7,\n\t0x5138, 0xC4D8, 0x5139, 0xF5C1, 0x513A, 0xF5C0, 0x513B, 0xC56C,\n\t0x513C, 0xC56B, 0x513D, 0xF7D0, 0x513F, 0xA449, 0x5140, 0xA461,\n\t0x5141, 0xA4B9, 0x5143, 0xA4B8, 0x5144, 0xA553, 0x5145, 0xA552,\n\t0x5146, 0xA5FC, 0x5147, 0xA5FB, 0x5148, 0xA5FD, 0x5149, 0xA5FA,\n\t0x514B, 0xA74A, 0x514C, 0xA749, 0x514D, 0xA74B, 0x5152, 0xA8E0,\n\t0x5154, 0xA8DF, 0x5155, 0xA8E1, 0x5157, 0xAB5E, 0x5159, 0xA259,\n\t0x515A, 0xD0DE, 0x515B, 0xA25A, 0x515C, 0xB0C2, 0x515D, 0xA25C,\n\t0x515E, 0xA25B, 0x515F, 0xD860, 0x5161, 0xA25D, 0x5162, 0xB9B8,\n\t0x5163, 0xA25E, 0x5165, 0xA44A, 0x5167, 0xA4BA, 0x5168, 0xA5FE,\n\t0x5169, 0xA8E2, 0x516B, 0xA44B, 0x516C, 0xA4BD, 0x516D, 0xA4BB,\n\t0x516E, 0xA4BC, 0x5171, 0xA640, 0x5175, 0xA74C, 0x5176, 0xA8E4,\n\t0x5177, 0xA8E3, 0x5178, 0xA8E5, 0x517C, 0xADDD, 0x5180, 0xBEAC,\n\t0x5187, 0xC94E, 0x5189, 0xA554, 0x518A, 0xA555, 0x518D, 0xA641,\n\t0x518F, 0xCA6A, 0x5191, 0xAB60, 0x5192, 0xAB5F, 0x5193, 0xD0E0,\n\t0x5194, 0xD0DF, 0x5195, 0xB0C3, 0x5197, 0xA4BE, 0x5198, 0xC955,\n\t0x519E, 0xCBCD, 0x51A0, 0xAB61, 0x51A2, 0xADE0, 0x51A4, 0xADDE,\n\t0x51A5, 0xADDF, 0x51AA, 0xBEAD, 0x51AC, 0xA556, 0x51B0, 0xA642,\n\t0x51B1, 0xC9BC, 0x51B6, 0xA74D, 0x51B7, 0xA74E, 0x51B9, 0xCA6B,\n\t0x51BC, 0xCBCE, 0x51BD, 0xA8E6, 0x51BE, 0xCBCF, 0x51C4, 0xD0E2,\n\t0x51C5, 0xD0E3, 0x51C6, 0xADE3, 0x51C8, 0xD0E4, 0x51CA, 0xD0E1,\n\t0x51CB, 0xADE4, 0x51CC, 0xADE2, 0x51CD, 0xADE1, 0x51CE, 0xD0E5,\n\t0x51D0, 0xD468, 0x51D4, 0xD861, 0x51D7, 0xDCC5, 0x51D8, 0xE140,\n\t0x51DC, 0xBBFE, 0x51DD, 0xBEAE, 0x51DE, 0xE8F9, 0x51E0, 0xA44C,\n\t0x51E1, 0xA45A, 0x51F0, 0xB0C4, 0x51F1, 0xB3CD, 0x51F3, 0xB9B9,\n\t0x51F5, 0xC942, 0x51F6, 0xA4BF, 0x51F8, 0xA559, 0x51F9, 0xA557,\n\t0x51FA, 0xA558, 0x51FD, 0xA8E7, 0x5200, 0xA44D, 0x5201, 0xA44E,\n\t0x5203, 0xA462, 0x5206, 0xA4C0, 0x5207, 0xA4C1, 0x5208, 0xA4C2,\n\t0x5209, 0xC9BE, 0x520A, 0xA55A, 0x520C, 0xC96B, 0x520E, 0xA646,\n\t0x5210, 0xC9BF, 0x5211, 0xA644, 0x5212, 0xA645, 0x5213, 0xC9BD,\n\t0x5216, 0xA647, 0x5217, 0xA643, 0x521C, 0xCA6C, 0x521D, 0xAAEC,\n\t0x521E, 0xCA6D, 0x5221, 0xCA6E, 0x5224, 0xA750, 0x5225, 0xA74F,\n\t0x5228, 0xA753, 0x5229, 0xA751, 0x522A, 0xA752, 0x522E, 0xA8ED,\n\t0x5230, 0xA8EC, 0x5231, 0xCBD4, 0x5232, 0xCBD1, 0x5233, 0xCBD2,\n\t0x5235, 0xCBD0, 0x5236, 0xA8EE, 0x5237, 0xA8EA, 0x5238, 0xA8E9,\n\t0x523A, 0xA8EB, 0x523B, 0xA8E8, 0x5241, 0xA8EF, 0x5243, 0xAB63,\n\t0x5244, 0xCDF0, 0x5246, 0xCBD3, 0x5247, 0xAB68, 0x5249, 0xCDF1,\n\t0x524A, 0xAB64, 0x524B, 0xAB67, 0x524C, 0xAB66, 0x524D, 0xAB65,\n\t0x524E, 0xAB62, 0x5252, 0xD0E8, 0x5254, 0xADE7, 0x5255, 0xD0EB,\n\t0x5256, 0xADE5, 0x525A, 0xD0E7, 0x525B, 0xADE8, 0x525C, 0xADE6,\n\t0x525D, 0xADE9, 0x525E, 0xD0E9, 0x525F, 0xD0EA, 0x5261, 0xD0E6,\n\t0x5262, 0xD0EC, 0x5269, 0xB3D1, 0x526A, 0xB0C5, 0x526B, 0xD469,\n\t0x526C, 0xD46B, 0x526D, 0xD46A, 0x526E, 0xD46C, 0x526F, 0xB0C6,\n\t0x5272, 0xB3CE, 0x5274, 0xB3CF, 0x5275, 0xB3D0, 0x5277, 0xB6D0,\n\t0x5278, 0xDCC7, 0x527A, 0xDCC6, 0x527B, 0xDCC8, 0x527C, 0xDCC9,\n\t0x527D, 0xB6D1, 0x527F, 0xB6CF, 0x5280, 0xE141, 0x5281, 0xE142,\n\t0x5282, 0xB9BB, 0x5283, 0xB9BA, 0x5284, 0xE35A, 0x5287, 0xBC40,\n\t0x5288, 0xBC41, 0x5289, 0xBC42, 0x528A, 0xBC44, 0x528B, 0xE4F2,\n\t0x528C, 0xE4F3, 0x528D, 0xBC43, 0x5291, 0xBEAF, 0x5293, 0xBEB0,\n\t0x5296, 0xF1ED, 0x5297, 0xF5C3, 0x5298, 0xF5C2, 0x5299, 0xF7D1,\n\t0x529B, 0xA44F, 0x529F, 0xA55C, 0x52A0, 0xA55B, 0x52A3, 0xA648,\n\t0x52A6, 0xC9C0, 0x52A9, 0xA755, 0x52AA, 0xA756, 0x52AB, 0xA754,\n\t0x52AC, 0xA757, 0x52AD, 0xCA6F, 0x52AE, 0xCA70, 0x52BB, 0xA8F1,\n\t0x52BC, 0xCBD5, 0x52BE, 0xA8F0, 0x52C0, 0xCDF2, 0x52C1, 0xAB6C,\n\t0x52C2, 0xCDF3, 0x52C3, 0xAB6B, 0x52C7, 0xAB69, 0x52C9, 0xAB6A,\n\t0x52CD, 0xD0ED, 0x52D2, 0xB0C7, 0x52D3, 0xD46E, 0x52D5, 0xB0CA,\n\t0x52D6, 0xD46D, 0x52D7, 0xB1E5, 0x52D8, 0xB0C9, 0x52D9, 0xB0C8,\n\t0x52DB, 0xB3D4, 0x52DD, 0xB3D3, 0x52DE, 0xB3D2, 0x52DF, 0xB6D2,\n\t0x52E2, 0xB6D5, 0x52E3, 0xB6D6, 0x52E4, 0xB6D4, 0x52E6, 0xB6D3,\n\t0x52E9, 0xE143, 0x52EB, 0xE144, 0x52EF, 0xE4F5, 0x52F0, 0xBC45,\n\t0x52F1, 0xE4F4, 0x52F3, 0xBEB1, 0x52F4, 0xECBF, 0x52F5, 0xC079,\n\t0x52F7, 0xF1EE, 0x52F8, 0xC455, 0x52FA, 0xA463, 0x52FB, 0xA4C3,\n\t0x52FC, 0xC956, 0x52FE, 0xA4C4, 0x52FF, 0xA4C5, 0x5305, 0xA55D,\n\t0x5306, 0xA55E, 0x5308, 0xA649, 0x5309, 0xCA71, 0x530A, 0xCBD6,\n\t0x530B, 0xCBD7, 0x530D, 0xAB6D, 0x530E, 0xD0EE, 0x530F, 0xB0CC,\n\t0x5310, 0xB0CB, 0x5311, 0xD863, 0x5312, 0xD862, 0x5315, 0xA450,\n\t0x5316, 0xA4C6, 0x5317, 0xA55F, 0x5319, 0xB0CD, 0x531A, 0xC943,\n\t0x531C, 0xC96C, 0x531D, 0xA560, 0x531F, 0xC9C2, 0x5320, 0xA64B,\n\t0x5321, 0xA64A, 0x5322, 0xC9C1, 0x5323, 0xA758, 0x532A, 0xADEA,\n\t0x532D, 0xD46F, 0x532F, 0xB6D7, 0x5330, 0xE145, 0x5331, 0xB9BC,\n\t0x5334, 0xE8FA, 0x5337, 0xF3FD, 0x5339, 0xA4C7, 0x533C, 0xCBD8,\n\t0x533D, 0xCDF4, 0x533E, 0xB0D0, 0x533F, 0xB0CE, 0x5340, 0xB0CF,\n\t0x5341, 0xA2CC, 0x5341, 0xA451, 0x5343, 0xA464, 0x5344, 0xA2CD,\n\t0x5345, 0xA2CE, 0x5345, 0xA4CA, 0x5347, 0xA4C9, 0x5348, 0xA4C8,\n\t0x5349, 0xA563, 0x534A, 0xA562, 0x534C, 0xC96D, 0x534D, 0xC9C3,\n\t0x5351, 0xA8F5, 0x5352, 0xA8F2, 0x5353, 0xA8F4, 0x5354, 0xA8F3,\n\t0x5357, 0xAB6E, 0x535A, 0xB3D5, 0x535C, 0xA452, 0x535E, 0xA4CB,\n\t0x5360, 0xA565, 0x5361, 0xA564, 0x5363, 0xCA72, 0x5366, 0xA8F6,\n\t0x536C, 0xC957, 0x536E, 0xA567, 0x536F, 0xA566, 0x5370, 0xA64C,\n\t0x5371, 0xA64D, 0x5372, 0xCA73, 0x5373, 0xA759, 0x5375, 0xA75A,\n\t0x5377, 0xA8F7, 0x5378, 0xA8F8, 0x5379, 0xA8F9, 0x537B, 0xAB6F,\n\t0x537C, 0xCDF5, 0x537F, 0xADEB, 0x5382, 0xC944, 0x5384, 0xA4CC,\n\t0x538A, 0xC9C4, 0x538E, 0xCA74, 0x538F, 0xCA75, 0x5392, 0xCBD9,\n\t0x5394, 0xCBDA, 0x5396, 0xCDF7, 0x5397, 0xCDF6, 0x5398, 0xCDF9,\n\t0x5399, 0xCDF8, 0x539A, 0xAB70, 0x539C, 0xD470, 0x539D, 0xADED,\n\t0x539E, 0xD0EF, 0x539F, 0xADEC, 0x53A4, 0xD864, 0x53A5, 0xB3D6,\n\t0x53A7, 0xD865, 0x53AC, 0xE146, 0x53AD, 0xB9BD, 0x53B2, 0xBC46,\n\t0x53B4, 0xF1EF, 0x53B9, 0xC958, 0x53BB, 0xA568, 0x53C3, 0xB0D1,\n\t0x53C8, 0xA453, 0x53C9, 0xA465, 0x53CA, 0xA4CE, 0x53CB, 0xA4CD,\n\t0x53CD, 0xA4CF, 0x53D4, 0xA8FB, 0x53D6, 0xA8FA, 0x53D7, 0xA8FC,\n\t0x53DB, 0xAB71, 0x53DF, 0xADEE, 0x53E1, 0xE8FB, 0x53E2, 0xC24F,\n\t0x53E3, 0xA466, 0x53E4, 0xA56A, 0x53E5, 0xA579, 0x53E6, 0xA574,\n\t0x53E8, 0xA56F, 0x53E9, 0xA56E, 0x53EA, 0xA575, 0x53EB, 0xA573,\n\t0x53EC, 0xA56C, 0x53ED, 0xA57A, 0x53EE, 0xA56D, 0x53EF, 0xA569,\n\t0x53F0, 0xA578, 0x53F1, 0xA577, 0x53F2, 0xA576, 0x53F3, 0xA56B,\n\t0x53F5, 0xA572, 0x53F8, 0xA571, 0x53FB, 0xA57B, 0x53FC, 0xA570,\n\t0x5401, 0xA653, 0x5403, 0xA659, 0x5404, 0xA655, 0x5406, 0xA65B,\n\t0x5407, 0xC9C5, 0x5408, 0xA658, 0x5409, 0xA64E, 0x540A, 0xA651,\n\t0x540B, 0xA654, 0x540C, 0xA650, 0x540D, 0xA657, 0x540E, 0xA65A,\n\t0x540F, 0xA64F, 0x5410, 0xA652, 0x5411, 0xA656, 0x5412, 0xA65C,\n\t0x5418, 0xCA7E, 0x5419, 0xCA7B, 0x541B, 0xA767, 0x541C, 0xCA7C,\n\t0x541D, 0xA75B, 0x541E, 0xA75D, 0x541F, 0xA775, 0x5420, 0xA770,\n\t0x5424, 0xCAA5, 0x5425, 0xCA7D, 0x5426, 0xA75F, 0x5427, 0xA761,\n\t0x5428, 0xCAA4, 0x5429, 0xA768, 0x542A, 0xCA78, 0x542B, 0xA774,\n\t0x542C, 0xA776, 0x542D, 0xA75C, 0x542E, 0xA76D, 0x5430, 0xCA76,\n\t0x5431, 0xA773, 0x5433, 0xA764, 0x5435, 0xA76E, 0x5436, 0xA76F,\n\t0x5437, 0xCA77, 0x5438, 0xA76C, 0x5439, 0xA76A, 0x543B, 0xA76B,\n\t0x543C, 0xA771, 0x543D, 0xCAA1, 0x543E, 0xA75E, 0x5440, 0xA772,\n\t0x5441, 0xCAA3, 0x5442, 0xA766, 0x5443, 0xA763, 0x5445, 0xCA7A,\n\t0x5446, 0xA762, 0x5447, 0xCAA6, 0x5448, 0xA765, 0x544A, 0xA769,\n\t0x544E, 0xA760, 0x544F, 0xCAA2, 0x5454, 0xCA79, 0x5460, 0xCBEB,\n\t0x5461, 0xCBEA, 0x5462, 0xA94F, 0x5463, 0xCBED, 0x5464, 0xCBEF,\n\t0x5465, 0xCBE4, 0x5466, 0xCBE7, 0x5467, 0xCBEE, 0x5468, 0xA950,\n\t0x546B, 0xCBE1, 0x546C, 0xCBE5, 0x546F, 0xCBE9, 0x5470, 0xCE49,\n\t0x5471, 0xA94B, 0x5472, 0xCE4D, 0x5473, 0xA8FD, 0x5474, 0xCBE6,\n\t0x5475, 0xA8FE, 0x5476, 0xA94C, 0x5477, 0xA945, 0x5478, 0xA941,\n\t0x547A, 0xCBE2, 0x547B, 0xA944, 0x547C, 0xA949, 0x547D, 0xA952,\n\t0x547E, 0xCBE3, 0x547F, 0xCBDC, 0x5480, 0xA943, 0x5481, 0xCBDD,\n\t0x5482, 0xCBDF, 0x5484, 0xA946, 0x5486, 0xA948, 0x5487, 0xCBDB,\n\t0x5488, 0xCBE0, 0x548B, 0xA951, 0x548C, 0xA94D, 0x548D, 0xCBE8,\n\t0x548E, 0xA953, 0x5490, 0xA94A, 0x5491, 0xCBDE, 0x5492, 0xA947,\n\t0x5495, 0xA942, 0x5496, 0xA940, 0x5498, 0xCBEC, 0x549A, 0xA94E,\n\t0x54A0, 0xCE48, 0x54A1, 0xCDFB, 0x54A2, 0xCE4B, 0x54A5, 0xCDFD,\n\t0x54A6, 0xAB78, 0x54A7, 0xABA8, 0x54A8, 0xAB74, 0x54A9, 0xABA7,\n\t0x54AA, 0xAB7D, 0x54AB, 0xABA4, 0x54AC, 0xAB72, 0x54AD, 0xCDFC,\n\t0x54AE, 0xCE43, 0x54AF, 0xABA3, 0x54B0, 0xCE4F, 0x54B1, 0xABA5,\n\t0x54B3, 0xAB79, 0x54B6, 0xCE45, 0x54B7, 0xCE42, 0x54B8, 0xAB77,\n\t0x54BA, 0xCDFA, 0x54BB, 0xABA6, 0x54BC, 0xCE4A, 0x54BD, 0xAB7C,\n\t0x54BE, 0xCE4C, 0x54BF, 0xABA9, 0x54C0, 0xAB73, 0x54C1, 0xAB7E,\n\t0x54C2, 0xAB7B, 0x54C3, 0xCE40, 0x54C4, 0xABA1, 0x54C5, 0xCE46,\n\t0x54C6, 0xCE47, 0x54C7, 0xAB7A, 0x54C8, 0xABA2, 0x54C9, 0xAB76,\n\t0x54CE, 0xAB75, 0x54CF, 0xCDFE, 0x54D6, 0xCE44, 0x54DE, 0xCE4E,\n\t0x54E0, 0xD144, 0x54E1, 0xADFB, 0x54E2, 0xD0F1, 0x54E4, 0xD0F6,\n\t0x54E5, 0xADF4, 0x54E6, 0xAE40, 0x54E7, 0xD0F4, 0x54E8, 0xADEF,\n\t0x54E9, 0xADF9, 0x54EA, 0xADFE, 0x54EB, 0xD0FB, 0x54ED, 0xADFA,\n\t0x54EE, 0xADFD, 0x54F1, 0xD0FE, 0x54F2, 0xADF5, 0x54F3, 0xD0F5,\n\t0x54F7, 0xD142, 0x54F8, 0xD143, 0x54FA, 0xADF7, 0x54FB, 0xD141,\n\t0x54FC, 0xADF3, 0x54FD, 0xAE43, 0x54FF, 0xD0F8, 0x5501, 0xADF1,\n\t0x5503, 0xD146, 0x5504, 0xD0F9, 0x5505, 0xD0FD, 0x5506, 0xADF6,\n\t0x5507, 0xAE42, 0x5508, 0xD0FA, 0x5509, 0xADFC, 0x550A, 0xD140,\n\t0x550B, 0xD147, 0x550C, 0xD4A1, 0x550E, 0xD145, 0x550F, 0xAE44,\n\t0x5510, 0xADF0, 0x5511, 0xD0FC, 0x5512, 0xD0F3, 0x5514, 0xADF8,\n\t0x5517, 0xD0F2, 0x551A, 0xD0F7, 0x5526, 0xD0F0, 0x5527, 0xAE41,\n\t0x552A, 0xD477, 0x552C, 0xB0E4, 0x552D, 0xD4A7, 0x552E, 0xB0E2,\n\t0x552F, 0xB0DF, 0x5530, 0xD47C, 0x5531, 0xB0DB, 0x5532, 0xD4A2,\n\t0x5533, 0xB0E6, 0x5534, 0xD476, 0x5535, 0xD47B, 0x5536, 0xD47A,\n\t0x5537, 0xADF2, 0x5538, 0xB0E1, 0x5539, 0xD4A5, 0x553B, 0xD4A8,\n\t0x553C, 0xD473, 0x553E, 0xB3E8, 0x5540, 0xD4A9, 0x5541, 0xB0E7,\n\t0x5543, 0xB0D9, 0x5544, 0xB0D6, 0x5545, 0xD47E, 0x5546, 0xB0D3,\n\t0x5548, 0xD4A6, 0x554A, 0xB0DA, 0x554B, 0xD4AA, 0x554D, 0xD474,\n\t0x554E, 0xD4A4, 0x554F, 0xB0DD, 0x5550, 0xD475, 0x5551, 0xD478,\n\t0x5552, 0xD47D, 0x5555, 0xB0DE, 0x5556, 0xB0DC, 0x5557, 0xB0E8,\n\t0x555C, 0xB0E3, 0x555E, 0xB0D7, 0x555F, 0xB1D2, 0x5561, 0xB0D8,\n\t0x5562, 0xD479, 0x5563, 0xB0E5, 0x5564, 0xB0E0, 0x5565, 0xD4A3,\n\t0x5566, 0xB0D5, 0x556A, 0xB0D4, 0x5575, 0xD471, 0x5576, 0xD472,\n\t0x5577, 0xD86A, 0x557B, 0xB3D7, 0x557C, 0xB3DA, 0x557D, 0xD875,\n\t0x557E, 0xB3EE, 0x557F, 0xD878, 0x5580, 0xB3D8, 0x5581, 0xD871,\n\t0x5582, 0xB3DE, 0x5583, 0xB3E4, 0x5584, 0xB5BD, 0x5587, 0xB3E2,\n\t0x5588, 0xD86E, 0x5589, 0xB3EF, 0x558A, 0xB3DB, 0x558B, 0xB3E3,\n\t0x558C, 0xD876, 0x558D, 0xDCD7, 0x558E, 0xD87B, 0x558F, 0xD86F,\n\t0x5591, 0xD866, 0x5592, 0xD873, 0x5593, 0xD86D, 0x5594, 0xB3E1,\n\t0x5595, 0xD879, 0x5598, 0xB3DD, 0x5599, 0xB3F1, 0x559A, 0xB3EA,\n\t0x559C, 0xB3DF, 0x559D, 0xB3DC, 0x559F, 0xB3E7, 0x55A1, 0xD87A,\n\t0x55A2, 0xD86C, 0x55A3, 0xD872, 0x55A4, 0xD874, 0x55A5, 0xD868,\n\t0x55A6, 0xD877, 0x55A7, 0xB3D9, 0x55A8, 0xD867, 0x55AA, 0xB3E0,\n\t0x55AB, 0xB3F0, 0x55AC, 0xB3EC, 0x55AD, 0xD869, 0x55AE, 0xB3E6,\n\t0x55B1, 0xB3ED, 0x55B2, 0xB3E9, 0x55B3, 0xB3E5, 0x55B5, 0xD870,\n\t0x55BB, 0xB3EB, 0x55BF, 0xDCD5, 0x55C0, 0xDCD1, 0x55C2, 0xDCE0,\n\t0x55C3, 0xDCCA, 0x55C4, 0xDCD3, 0x55C5, 0xB6E5, 0x55C6, 0xB6E6,\n\t0x55C7, 0xB6DE, 0x55C8, 0xDCDC, 0x55C9, 0xB6E8, 0x55CA, 0xDCCF,\n\t0x55CB, 0xDCCE, 0x55CC, 0xDCCC, 0x55CD, 0xDCDE, 0x55CE, 0xB6DC,\n\t0x55CF, 0xDCD8, 0x55D0, 0xDCCD, 0x55D1, 0xB6DF, 0x55D2, 0xDCD6,\n\t0x55D3, 0xB6DA, 0x55D4, 0xDCD2, 0x55D5, 0xDCD9, 0x55D6, 0xDCDB,\n\t0x55D9, 0xDCDF, 0x55DA, 0xB6E3, 0x55DB, 0xDCCB, 0x55DC, 0xB6DD,\n\t0x55DD, 0xDCD0, 0x55DF, 0xB6D8, 0x55E1, 0xB6E4, 0x55E2, 0xDCDA,\n\t0x55E3, 0xB6E0, 0x55E4, 0xB6E1, 0x55E5, 0xB6E7, 0x55E6, 0xB6DB,\n\t0x55E7, 0xA25F, 0x55E8, 0xB6D9, 0x55E9, 0xDCD4, 0x55EF, 0xB6E2,\n\t0x55F2, 0xDCDD, 0x55F6, 0xB9CD, 0x55F7, 0xB9C8, 0x55F9, 0xE155,\n\t0x55FA, 0xE151, 0x55FC, 0xE14B, 0x55FD, 0xB9C2, 0x55FE, 0xB9BE,\n\t0x55FF, 0xE154, 0x5600, 0xB9BF, 0x5601, 0xE14E, 0x5602, 0xE150,\n\t0x5604, 0xE153, 0x5606, 0xB9C4, 0x5608, 0xB9CB, 0x5609, 0xB9C5,\n\t0x560C, 0xE149, 0x560D, 0xB9C6, 0x560E, 0xB9C7, 0x560F, 0xE14C,\n\t0x5610, 0xB9CC, 0x5612, 0xE14A, 0x5613, 0xE14F, 0x5614, 0xB9C3,\n\t0x5615, 0xE148, 0x5616, 0xB9C9, 0x5617, 0xB9C1, 0x561B, 0xB9C0,\n\t0x561C, 0xE14D, 0x561D, 0xE152, 0x561F, 0xB9CA, 0x5627, 0xE147,\n\t0x5629, 0xBC4D, 0x562A, 0xE547, 0x562C, 0xE544, 0x562E, 0xBC47,\n\t0x562F, 0xBC53, 0x5630, 0xBC54, 0x5632, 0xBC4A, 0x5633, 0xE542,\n\t0x5634, 0xBC4C, 0x5635, 0xE4F9, 0x5636, 0xBC52, 0x5638, 0xE546,\n\t0x5639, 0xBC49, 0x563A, 0xE548, 0x563B, 0xBC48, 0x563D, 0xE543,\n\t0x563E, 0xE545, 0x563F, 0xBC4B, 0x5640, 0xE541, 0x5641, 0xE4FA,\n\t0x5642, 0xE4F7, 0x5645, 0xD86B, 0x5646, 0xE4FD, 0x5648, 0xE4F6,\n\t0x5649, 0xE4FC, 0x564A, 0xE4FB, 0x564C, 0xE4F8, 0x564E, 0xBC4F,\n\t0x5653, 0xBC4E, 0x5657, 0xBC50, 0x5658, 0xE4FE, 0x5659, 0xBEB2,\n\t0x565A, 0xE540, 0x565E, 0xE945, 0x5660, 0xE8FD, 0x5662, 0xBEBE,\n\t0x5663, 0xE942, 0x5664, 0xBEB6, 0x5665, 0xBEBA, 0x5666, 0xE941,\n\t0x5668, 0xBEB9, 0x5669, 0xBEB5, 0x566A, 0xBEB8, 0x566B, 0xBEB3,\n\t0x566C, 0xBEBD, 0x566D, 0xE943, 0x566E, 0xE8FE, 0x566F, 0xBEBC,\n\t0x5670, 0xE8FC, 0x5671, 0xBEBB, 0x5672, 0xE944, 0x5673, 0xE940,\n\t0x5674, 0xBC51, 0x5676, 0xBEBF, 0x5677, 0xE946, 0x5678, 0xBEB7,\n\t0x5679, 0xBEB4, 0x567E, 0xECC6, 0x567F, 0xECC8, 0x5680, 0xC07B,\n\t0x5681, 0xECC9, 0x5682, 0xECC7, 0x5683, 0xECC5, 0x5684, 0xECC4,\n\t0x5685, 0xC07D, 0x5686, 0xECC3, 0x5687, 0xC07E, 0x568C, 0xECC1,\n\t0x568D, 0xECC2, 0x568E, 0xC07A, 0x568F, 0xC0A1, 0x5690, 0xC07C,\n\t0x5693, 0xECC0, 0x5695, 0xC250, 0x5697, 0xEFBC, 0x5698, 0xEFBA,\n\t0x5699, 0xEFBF, 0x569A, 0xEFBD, 0x569C, 0xEFBB, 0x569D, 0xEFBE,\n\t0x56A5, 0xC360, 0x56A6, 0xF1F2, 0x56A7, 0xF1F3, 0x56A8, 0xC456,\n\t0x56AA, 0xF1F4, 0x56AB, 0xF1F0, 0x56AC, 0xF1F5, 0x56AD, 0xF1F1,\n\t0x56AE, 0xC251, 0x56B2, 0xF3FE, 0x56B3, 0xF441, 0x56B4, 0xC459,\n\t0x56B5, 0xF440, 0x56B6, 0xC458, 0x56B7, 0xC457, 0x56BC, 0xC45A,\n\t0x56BD, 0xF5C5, 0x56BE, 0xF5C6, 0x56C0, 0xC4DA, 0x56C1, 0xC4D9,\n\t0x56C2, 0xC4DB, 0x56C3, 0xF5C4, 0x56C5, 0xF6D8, 0x56C6, 0xF6D7,\n\t0x56C8, 0xC56D, 0x56C9, 0xC56F, 0x56CA, 0xC56E, 0x56CB, 0xF6D9,\n\t0x56CC, 0xC5C8, 0x56CD, 0xF8A6, 0x56D1, 0xC5F1, 0x56D3, 0xF8A5,\n\t0x56D4, 0xF8EE, 0x56D7, 0xC949, 0x56DA, 0xA57D, 0x56DB, 0xA57C,\n\t0x56DD, 0xA65F, 0x56DE, 0xA65E, 0x56DF, 0xC9C7, 0x56E0, 0xA65D,\n\t0x56E1, 0xC9C6, 0x56E4, 0xA779, 0x56E5, 0xCAA9, 0x56E7, 0xCAA8,\n\t0x56EA, 0xA777, 0x56EB, 0xA77A, 0x56EE, 0xCAA7, 0x56F0, 0xA778,\n\t0x56F7, 0xCBF0, 0x56F9, 0xCBF1, 0x56FA, 0xA954, 0x56FF, 0xABAA,\n\t0x5701, 0xD148, 0x5702, 0xD149, 0x5703, 0xAE45, 0x5704, 0xAE46,\n\t0x5707, 0xD4AC, 0x5708, 0xB0E9, 0x5709, 0xB0EB, 0x570A, 0xD4AB,\n\t0x570B, 0xB0EA, 0x570C, 0xD87C, 0x570D, 0xB3F2, 0x5712, 0xB6E9,\n\t0x5713, 0xB6EA, 0x5714, 0xDCE1, 0x5716, 0xB9CF, 0x5718, 0xB9CE,\n\t0x571A, 0xE549, 0x571B, 0xE948, 0x571C, 0xE947, 0x571E, 0xF96B,\n\t0x571F, 0xA467, 0x5720, 0xC959, 0x5722, 0xC96E, 0x5723, 0xC96F,\n\t0x5728, 0xA662, 0x5729, 0xA666, 0x572A, 0xC9C9, 0x572C, 0xA664,\n\t0x572D, 0xA663, 0x572E, 0xC9C8, 0x572F, 0xA665, 0x5730, 0xA661,\n\t0x5733, 0xA660, 0x5734, 0xC9CA, 0x573B, 0xA7A6, 0x573E, 0xA7A3,\n\t0x5740, 0xA77D, 0x5741, 0xCAAA, 0x5745, 0xCAAB, 0x5747, 0xA7A1,\n\t0x5749, 0xCAAD, 0x574A, 0xA77B, 0x574B, 0xCAAE, 0x574C, 0xCAAC,\n\t0x574D, 0xA77E, 0x574E, 0xA7A2, 0x574F, 0xA7A5, 0x5750, 0xA7A4,\n\t0x5751, 0xA77C, 0x5752, 0xCAAF, 0x5761, 0xA959, 0x5762, 0xCBFE,\n\t0x5764, 0xA95B, 0x5766, 0xA95A, 0x5768, 0xCC40, 0x5769, 0xA958,\n\t0x576A, 0xA957, 0x576B, 0xCBF5, 0x576D, 0xCBF4, 0x576F, 0xCBF2,\n\t0x5770, 0xCBF7, 0x5771, 0xCBF6, 0x5772, 0xCBF3, 0x5773, 0xCBFC,\n\t0x5774, 0xCBFD, 0x5775, 0xCBFA, 0x5776, 0xCBF8, 0x5777, 0xA956,\n\t0x577B, 0xCBFB, 0x577C, 0xA95C, 0x577D, 0xCC41, 0x5780, 0xCBF9,\n\t0x5782, 0xABAB, 0x5783, 0xA955, 0x578B, 0xABAC, 0x578C, 0xCE54,\n\t0x578F, 0xCE5A, 0x5793, 0xABB2, 0x5794, 0xCE58, 0x5795, 0xCE5E,\n\t0x5797, 0xCE55, 0x5798, 0xCE59, 0x5799, 0xCE5B, 0x579A, 0xCE5D,\n\t0x579B, 0xCE57, 0x579D, 0xCE56, 0x579E, 0xCE51, 0x579F, 0xCE52,\n\t0x57A0, 0xABAD, 0x57A2, 0xABAF, 0x57A3, 0xABAE, 0x57A4, 0xCE53,\n\t0x57A5, 0xCE5C, 0x57AE, 0xABB1, 0x57B5, 0xCE50, 0x57B6, 0xD153,\n\t0x57B8, 0xD152, 0x57B9, 0xD157, 0x57BA, 0xD14E, 0x57BC, 0xD151,\n\t0x57BD, 0xD150, 0x57BF, 0xD154, 0x57C1, 0xD158, 0x57C2, 0xAE47,\n\t0x57C3, 0xAE4A, 0x57C6, 0xD14F, 0x57C7, 0xD155, 0x57CB, 0xAE49,\n\t0x57CC, 0xD14A, 0x57CE, 0xABB0, 0x57CF, 0xD4BA, 0x57D0, 0xD156,\n\t0x57D2, 0xD14D, 0x57D4, 0xAE48, 0x57D5, 0xD14C, 0x57DC, 0xD4B1,\n\t0x57DF, 0xB0EC, 0x57E0, 0xB0F0, 0x57E1, 0xD4C1, 0x57E2, 0xD4AF,\n\t0x57E3, 0xD4BD, 0x57E4, 0xB0F1, 0x57E5, 0xD4BF, 0x57E7, 0xD4C5,\n\t0x57E9, 0xD4C9, 0x57EC, 0xD4C0, 0x57ED, 0xD4B4, 0x57EE, 0xD4BC,\n\t0x57F0, 0xD4CA, 0x57F1, 0xD4C8, 0x57F2, 0xD4BE, 0x57F3, 0xD4B9,\n\t0x57F4, 0xD4B2, 0x57F5, 0xD8A6, 0x57F6, 0xD4B0, 0x57F7, 0xB0F5,\n\t0x57F8, 0xD4B7, 0x57F9, 0xB0F6, 0x57FA, 0xB0F2, 0x57FB, 0xD4AD,\n\t0x57FC, 0xD4C3, 0x57FD, 0xD4B5, 0x5800, 0xD4B3, 0x5801, 0xD4C6,\n\t0x5802, 0xB0F3, 0x5804, 0xD4CC, 0x5805, 0xB0ED, 0x5806, 0xB0EF,\n\t0x5807, 0xD4BB, 0x5808, 0xD4B6, 0x5809, 0xAE4B, 0x580A, 0xB0EE,\n\t0x580B, 0xD4B8, 0x580C, 0xD4C7, 0x580D, 0xD4CB, 0x580E, 0xD4C2,\n\t0x5810, 0xD4C4, 0x5814, 0xD4AE, 0x5819, 0xD8A1, 0x581B, 0xD8AA,\n\t0x581C, 0xD8A9, 0x581D, 0xB3FA, 0x581E, 0xD8A2, 0x5820, 0xB3FB,\n\t0x5821, 0xB3F9, 0x5823, 0xD8A4, 0x5824, 0xB3F6, 0x5825, 0xD8A8,\n\t0x5827, 0xD8A3, 0x5828, 0xD8A5, 0x5829, 0xD87D, 0x582A, 0xB3F4,\n\t0x582C, 0xD8B2, 0x582D, 0xD8B1, 0x582E, 0xD8AE, 0x582F, 0xB3F3,\n\t0x5830, 0xB3F7, 0x5831, 0xB3F8, 0x5832, 0xD14B, 0x5833, 0xD8AB,\n\t0x5834, 0xB3F5, 0x5835, 0xB0F4, 0x5836, 0xD8AD, 0x5837, 0xD87E,\n\t0x5838, 0xD8B0, 0x5839, 0xD8AF, 0x583B, 0xD8B3, 0x583D, 0xDCEF,\n\t0x583F, 0xD8AC, 0x5848, 0xD8A7, 0x5849, 0xDCE7, 0x584A, 0xB6F4,\n\t0x584B, 0xB6F7, 0x584C, 0xB6F2, 0x584D, 0xDCE6, 0x584E, 0xDCEA,\n\t0x584F, 0xDCE5, 0x5851, 0xB6EC, 0x5852, 0xB6F6, 0x5853, 0xDCE2,\n\t0x5854, 0xB6F0, 0x5855, 0xDCE9, 0x5857, 0xB6EE, 0x5858, 0xB6ED,\n\t0x5859, 0xDCEC, 0x585A, 0xB6EF, 0x585B, 0xDCEE, 0x585D, 0xDCEB,\n\t0x585E, 0xB6EB, 0x5862, 0xB6F5, 0x5863, 0xDCF0, 0x5864, 0xDCE4,\n\t0x5865, 0xDCED, 0x5868, 0xDCE3, 0x586B, 0xB6F1, 0x586D, 0xB6F3,\n\t0x586F, 0xDCE8, 0x5871, 0xDCF1, 0x5874, 0xE15D, 0x5875, 0xB9D0,\n\t0x5876, 0xE163, 0x5879, 0xB9D5, 0x587A, 0xE15F, 0x587B, 0xE166,\n\t0x587C, 0xE157, 0x587D, 0xB9D7, 0x587E, 0xB9D1, 0x587F, 0xE15C,\n\t0x5880, 0xBC55, 0x5881, 0xE15B, 0x5882, 0xE164, 0x5883, 0xB9D2,\n\t0x5885, 0xB9D6, 0x5886, 0xE15A, 0x5887, 0xE160, 0x5888, 0xE165,\n\t0x5889, 0xE156, 0x588A, 0xB9D4, 0x588B, 0xE15E, 0x588E, 0xE162,\n\t0x588F, 0xE168, 0x5890, 0xE158, 0x5891, 0xE161, 0x5893, 0xB9D3,\n\t0x5894, 0xE167, 0x5898, 0xE159, 0x589C, 0xBC59, 0x589D, 0xE54B,\n\t0x589E, 0xBC57, 0x589F, 0xBC56, 0x58A0, 0xE54D, 0x58A1, 0xE552,\n\t0x58A3, 0xE54E, 0x58A5, 0xE551, 0x58A6, 0xBC5C, 0x58A8, 0xBEA5,\n\t0x58A9, 0xBC5B, 0x58AB, 0xE54A, 0x58AC, 0xE550, 0x58AE, 0xBC5A,\n\t0x58AF, 0xE54F, 0x58B1, 0xE54C, 0x58B3, 0xBC58, 0x58BA, 0xE94D,\n\t0x58BB, 0xF9D9, 0x58BC, 0xE94F, 0x58BD, 0xE94A, 0x58BE, 0xBEC1,\n\t0x58BF, 0xE94C, 0x58C1, 0xBEC0, 0x58C2, 0xE94E, 0x58C5, 0xBEC3,\n\t0x58C6, 0xE950, 0x58C7, 0xBEC2, 0x58C8, 0xE949, 0x58C9, 0xE94B,\n\t0x58CE, 0xC0A5, 0x58CF, 0xECCC, 0x58D1, 0xC0A4, 0x58D2, 0xECCD,\n\t0x58D3, 0xC0A3, 0x58D4, 0xECCB, 0x58D5, 0xC0A2, 0x58D6, 0xECCA,\n\t0x58D8, 0xC253, 0x58D9, 0xC252, 0x58DA, 0xF1F6, 0x58DB, 0xF1F8,\n\t0x58DD, 0xF1F7, 0x58DE, 0xC361, 0x58DF, 0xC362, 0x58E2, 0xC363,\n\t0x58E3, 0xF442, 0x58E4, 0xC45B, 0x58E7, 0xF7D3, 0x58E8, 0xF7D2,\n\t0x58E9, 0xC5F2, 0x58EB, 0xA468, 0x58EC, 0xA4D0, 0x58EF, 0xA7A7,\n\t0x58F4, 0xCE5F, 0x58F9, 0xB3FC, 0x58FA, 0xB3FD, 0x58FC, 0xDCF2,\n\t0x58FD, 0xB9D8, 0x58FE, 0xE169, 0x58FF, 0xE553, 0x5903, 0xC95A,\n\t0x5906, 0xCAB0, 0x590C, 0xCC42, 0x590D, 0xCE60, 0x590E, 0xD159,\n\t0x590F, 0xAE4C, 0x5912, 0xF1F9, 0x5914, 0xC4DC, 0x5915, 0xA469,\n\t0x5916, 0xA57E, 0x5917, 0xC970, 0x5919, 0xA667, 0x591A, 0xA668,\n\t0x591C, 0xA95D, 0x5920, 0xB0F7, 0x5922, 0xB9DA, 0x5924, 0xB9DB,\n\t0x5925, 0xB9D9, 0x5927, 0xA46A, 0x5929, 0xA4D1, 0x592A, 0xA4D3,\n\t0x592B, 0xA4D2, 0x592C, 0xC95B, 0x592D, 0xA4D4, 0x592E, 0xA5A1,\n\t0x592F, 0xC971, 0x5931, 0xA5A2, 0x5937, 0xA669, 0x5938, 0xA66A,\n\t0x593C, 0xC9CB, 0x593E, 0xA7A8, 0x5940, 0xCAB1, 0x5944, 0xA961,\n\t0x5945, 0xCC43, 0x5947, 0xA95F, 0x5948, 0xA960, 0x5949, 0xA95E,\n\t0x594A, 0xD15A, 0x594E, 0xABB6, 0x594F, 0xABB5, 0x5950, 0xABB7,\n\t0x5951, 0xABB4, 0x5953, 0xCE61, 0x5954, 0xA962, 0x5955, 0xABB3,\n\t0x5957, 0xAE4D, 0x5958, 0xAE4E, 0x595A, 0xAE4F, 0x595C, 0xD4CD,\n\t0x5960, 0xB3FE, 0x5961, 0xD8B4, 0x5962, 0xB0F8, 0x5967, 0xB6F8,\n\t0x5969, 0xB9DD, 0x596A, 0xB9DC, 0x596B, 0xE16A, 0x596D, 0xBC5D,\n\t0x596E, 0xBEC4, 0x5970, 0xEFC0, 0x5971, 0xF6DA, 0x5972, 0xF7D4,\n\t0x5973, 0xA46B, 0x5974, 0xA5A3, 0x5976, 0xA5A4, 0x5977, 0xC9D1,\n\t0x5978, 0xA66C, 0x5979, 0xA66F, 0x597B, 0xC9CF, 0x597C, 0xC9CD,\n\t0x597D, 0xA66E, 0x597E, 0xC9D0, 0x597F, 0xC9D2, 0x5980, 0xC9CC,\n\t0x5981, 0xA671, 0x5982, 0xA670, 0x5983, 0xA66D, 0x5984, 0xA66B,\n\t0x5985, 0xC9CE, 0x598A, 0xA7B3, 0x598D, 0xA7B0, 0x598E, 0xCAB6,\n\t0x598F, 0xCAB9, 0x5990, 0xCAB8, 0x5992, 0xA7AA, 0x5993, 0xA7B2,\n\t0x5996, 0xA7AF, 0x5997, 0xCAB5, 0x5998, 0xCAB3, 0x5999, 0xA7AE,\n\t0x599D, 0xA7A9, 0x599E, 0xA7AC, 0x59A0, 0xCAB4, 0x59A1, 0xCABB,\n\t0x59A2, 0xCAB7, 0x59A3, 0xA7AD, 0x59A4, 0xA7B1, 0x59A5, 0xA7B4,\n\t0x59A6, 0xCAB2, 0x59A7, 0xCABA, 0x59A8, 0xA7AB, 0x59AE, 0xA967,\n\t0x59AF, 0xA96F, 0x59B1, 0xCC4F, 0x59B2, 0xCC48, 0x59B3, 0xA970,\n\t0x59B4, 0xCC53, 0x59B5, 0xCC44, 0x59B6, 0xCC4B, 0x59B9, 0xA966,\n\t0x59BA, 0xCC45, 0x59BB, 0xA964, 0x59BC, 0xCC4C, 0x59BD, 0xCC50,\n\t0x59BE, 0xA963, 0x59C0, 0xCC51, 0x59C1, 0xCC4A, 0x59C3, 0xCC4D,\n\t0x59C5, 0xA972, 0x59C6, 0xA969, 0x59C7, 0xCC54, 0x59C8, 0xCC52,\n\t0x59CA, 0xA96E, 0x59CB, 0xA96C, 0x59CC, 0xCC49, 0x59CD, 0xA96B,\n\t0x59CE, 0xCC47, 0x59CF, 0xCC46, 0x59D0, 0xA96A, 0x59D1, 0xA968,\n\t0x59D2, 0xA971, 0x59D3, 0xA96D, 0x59D4, 0xA965, 0x59D6, 0xCC4E,\n\t0x59D8, 0xABB9, 0x59DA, 0xABC0, 0x59DB, 0xCE6F, 0x59DC, 0xABB8,\n\t0x59DD, 0xCE67, 0x59DE, 0xCE63, 0x59E0, 0xCE73, 0x59E1, 0xCE62,\n\t0x59E3, 0xABBB, 0x59E4, 0xCE6C, 0x59E5, 0xABBE, 0x59E6, 0xABC1,\n\t0x59E8, 0xABBC, 0x59E9, 0xCE70, 0x59EA, 0xABBF, 0x59EC, 0xAE56,\n\t0x59ED, 0xCE76, 0x59EE, 0xCE64, 0x59F1, 0xCE66, 0x59F2, 0xCE6D,\n\t0x59F3, 0xCE71, 0x59F4, 0xCE75, 0x59F5, 0xCE72, 0x59F6, 0xCE6B,\n\t0x59F7, 0xCE6E, 0x59FA, 0xCE68, 0x59FB, 0xABC3, 0x59FC, 0xCE6A,\n\t0x59FD, 0xCE69, 0x59FE, 0xCE74, 0x59FF, 0xABBA, 0x5A00, 0xCE65,\n\t0x5A01, 0xABC2, 0x5A03, 0xABBD, 0x5A09, 0xAE5C, 0x5A0A, 0xD162,\n\t0x5A0C, 0xAE5B, 0x5A0F, 0xD160, 0x5A11, 0xAE50, 0x5A13, 0xAE55,\n\t0x5A15, 0xD15F, 0x5A16, 0xD15C, 0x5A17, 0xD161, 0x5A18, 0xAE51,\n\t0x5A19, 0xD15B, 0x5A1B, 0xAE54, 0x5A1C, 0xAE52, 0x5A1E, 0xD163,\n\t0x5A1F, 0xAE53, 0x5A20, 0xAE57, 0x5A23, 0xAE58, 0x5A25, 0xAE5A,\n\t0x5A29, 0xAE59, 0x5A2D, 0xD15D, 0x5A2E, 0xD15E, 0x5A33, 0xD164,\n\t0x5A35, 0xD4D4, 0x5A36, 0xB0F9, 0x5A37, 0xD8C2, 0x5A38, 0xD4D3,\n\t0x5A39, 0xD4E6, 0x5A3C, 0xB140, 0x5A3E, 0xD4E4, 0x5A40, 0xB0FE,\n\t0x5A41, 0xB0FA, 0x5A42, 0xD4ED, 0x5A43, 0xD4DD, 0x5A44, 0xD4E0,\n\t0x5A46, 0xB143, 0x5A47, 0xD4EA, 0x5A48, 0xD4E2, 0x5A49, 0xB0FB,\n\t0x5A4A, 0xB144, 0x5A4C, 0xD4E7, 0x5A4D, 0xD4E5, 0x5A50, 0xD4D6,\n\t0x5A51, 0xD4EB, 0x5A52, 0xD4DF, 0x5A53, 0xD4DA, 0x5A55, 0xD4D0,\n\t0x5A56, 0xD4EC, 0x5A57, 0xD4DC, 0x5A58, 0xD4CF, 0x5A5A, 0xB142,\n\t0x5A5B, 0xD4E1, 0x5A5C, 0xD4EE, 0x5A5D, 0xD4DE, 0x5A5E, 0xD4D2,\n\t0x5A5F, 0xD4D7, 0x5A60, 0xD4CE, 0x5A62, 0xB141, 0x5A64, 0xD4DB,\n\t0x5A65, 0xD4D8, 0x5A66, 0xB0FC, 0x5A67, 0xD4D1, 0x5A69, 0xD4E9,\n\t0x5A6A, 0xB0FD, 0x5A6C, 0xD4D9, 0x5A6D, 0xD4D5, 0x5A70, 0xD4E8,\n\t0x5A77, 0xB440, 0x5A78, 0xD8BB, 0x5A7A, 0xD8B8, 0x5A7B, 0xD8C9,\n\t0x5A7C, 0xD8BD, 0x5A7D, 0xD8CA, 0x5A7F, 0xB442, 0x5A83, 0xD8C6,\n\t0x5A84, 0xD8C3, 0x5A8A, 0xD8C4, 0x5A8B, 0xD8C7, 0x5A8C, 0xD8CB,\n\t0x5A8E, 0xD4E3, 0x5A8F, 0xD8CD, 0x5A90, 0xDD47, 0x5A92, 0xB443,\n\t0x5A93, 0xD8CE, 0x5A94, 0xD8B6, 0x5A95, 0xD8C0, 0x5A97, 0xD8C5,\n\t0x5A9A, 0xB441, 0x5A9B, 0xB444, 0x5A9C, 0xD8CC, 0x5A9D, 0xD8CF,\n\t0x5A9E, 0xD8BA, 0x5A9F, 0xD8B7, 0x5AA2, 0xD8B9, 0x5AA5, 0xD8BE,\n\t0x5AA6, 0xD8BC, 0x5AA7, 0xB445, 0x5AA9, 0xD8C8, 0x5AAC, 0xD8BF,\n\t0x5AAE, 0xD8C1, 0x5AAF, 0xD8B5, 0x5AB0, 0xDCFA, 0x5AB1, 0xDCF8,\n\t0x5AB2, 0xB742, 0x5AB3, 0xB740, 0x5AB4, 0xDD43, 0x5AB5, 0xDCF9,\n\t0x5AB6, 0xDD44, 0x5AB7, 0xDD40, 0x5AB8, 0xDCF7, 0x5AB9, 0xDD46,\n\t0x5ABA, 0xDCF6, 0x5ABB, 0xDCFD, 0x5ABC, 0xB6FE, 0x5ABD, 0xB6FD,\n\t0x5ABE, 0xB6FC, 0x5ABF, 0xDCFB, 0x5AC0, 0xDD41, 0x5AC1, 0xB6F9,\n\t0x5AC2, 0xB741, 0x5AC4, 0xDCF4, 0x5AC6, 0xDCFE, 0x5AC7, 0xDCF3,\n\t0x5AC8, 0xDCFC, 0x5AC9, 0xB6FA, 0x5ACA, 0xDD42, 0x5ACB, 0xDCF5,\n\t0x5ACC, 0xB6FB, 0x5ACD, 0xDD45, 0x5AD5, 0xE16E, 0x5AD6, 0xB9E2,\n\t0x5AD7, 0xB9E1, 0x5AD8, 0xB9E3, 0x5AD9, 0xE17A, 0x5ADA, 0xE170,\n\t0x5ADB, 0xE176, 0x5ADC, 0xE16B, 0x5ADD, 0xE179, 0x5ADE, 0xE178,\n\t0x5ADF, 0xE17C, 0x5AE0, 0xE175, 0x5AE1, 0xB9DE, 0x5AE2, 0xE174,\n\t0x5AE3, 0xB9E4, 0x5AE5, 0xE16D, 0x5AE6, 0xB9DF, 0x5AE8, 0xE17B,\n\t0x5AE9, 0xB9E0, 0x5AEA, 0xE16F, 0x5AEB, 0xE172, 0x5AEC, 0xE177,\n\t0x5AED, 0xE171, 0x5AEE, 0xE16C, 0x5AF3, 0xE173, 0x5AF4, 0xE555,\n\t0x5AF5, 0xBC61, 0x5AF6, 0xE558, 0x5AF7, 0xE557, 0x5AF8, 0xE55A,\n\t0x5AF9, 0xE55C, 0x5AFA, 0xF9DC, 0x5AFB, 0xBC5F, 0x5AFD, 0xE556,\n\t0x5AFF, 0xE554, 0x5B01, 0xE55D, 0x5B02, 0xE55B, 0x5B03, 0xE559,\n\t0x5B05, 0xE55F, 0x5B07, 0xE55E, 0x5B08, 0xBC63, 0x5B09, 0xBC5E,\n\t0x5B0B, 0xBC60, 0x5B0C, 0xBC62, 0x5B0F, 0xE560, 0x5B10, 0xE957,\n\t0x5B13, 0xE956, 0x5B14, 0xE955, 0x5B16, 0xE958, 0x5B17, 0xE951,\n\t0x5B19, 0xE952, 0x5B1A, 0xE95A, 0x5B1B, 0xE953, 0x5B1D, 0xBEC5,\n\t0x5B1E, 0xE95C, 0x5B20, 0xE95B, 0x5B21, 0xE954, 0x5B23, 0xECD1,\n\t0x5B24, 0xC0A8, 0x5B25, 0xECCF, 0x5B26, 0xECD4, 0x5B27, 0xECD3,\n\t0x5B28, 0xE959, 0x5B2A, 0xC0A7, 0x5B2C, 0xECD2, 0x5B2D, 0xECCE,\n\t0x5B2E, 0xECD6, 0x5B2F, 0xECD5, 0x5B30, 0xC0A6, 0x5B32, 0xECD0,\n\t0x5B34, 0xBEC6, 0x5B38, 0xC254, 0x5B3C, 0xEFC1, 0x5B3D, 0xF1FA,\n\t0x5B3E, 0xF1FB, 0x5B3F, 0xF1FC, 0x5B40, 0xC45C, 0x5B43, 0xC45D,\n\t0x5B45, 0xF443, 0x5B47, 0xF5C8, 0x5B48, 0xF5C7, 0x5B4B, 0xF6DB,\n\t0x5B4C, 0xF6DC, 0x5B4D, 0xF7D5, 0x5B4E, 0xF8A7, 0x5B50, 0xA46C,\n\t0x5B51, 0xA46D, 0x5B53, 0xA46E, 0x5B54, 0xA4D5, 0x5B55, 0xA5A5,\n\t0x5B56, 0xC9D3, 0x5B57, 0xA672, 0x5B58, 0xA673, 0x5B5A, 0xA7B7,\n\t0x5B5B, 0xA7B8, 0x5B5C, 0xA7B6, 0x5B5D, 0xA7B5, 0x5B5F, 0xA973,\n\t0x5B62, 0xCC55, 0x5B63, 0xA975, 0x5B64, 0xA974, 0x5B65, 0xCC56,\n\t0x5B69, 0xABC4, 0x5B6B, 0xAE5D, 0x5B6C, 0xD165, 0x5B6E, 0xD4F0,\n\t0x5B70, 0xB145, 0x5B71, 0xB447, 0x5B72, 0xD4EF, 0x5B73, 0xB446,\n\t0x5B75, 0xB9E5, 0x5B77, 0xE17D, 0x5B78, 0xBEC7, 0x5B7A, 0xC0A9,\n\t0x5B7B, 0xECD7, 0x5B7D, 0xC45E, 0x5B7F, 0xC570, 0x5B81, 0xC972,\n\t0x5B83, 0xA5A6, 0x5B84, 0xC973, 0x5B85, 0xA676, 0x5B87, 0xA674,\n\t0x5B88, 0xA675, 0x5B89, 0xA677, 0x5B8B, 0xA7BA, 0x5B8C, 0xA7B9,\n\t0x5B8E, 0xCABC, 0x5B8F, 0xA7BB, 0x5B92, 0xCABD, 0x5B93, 0xCC57,\n\t0x5B95, 0xCC58, 0x5B97, 0xA976, 0x5B98, 0xA978, 0x5B99, 0xA97A,\n\t0x5B9A, 0xA977, 0x5B9B, 0xA97B, 0x5B9C, 0xA979, 0x5BA2, 0xABC8,\n\t0x5BA3, 0xABC5, 0x5BA4, 0xABC7, 0x5BA5, 0xABC9, 0x5BA6, 0xABC6,\n\t0x5BA7, 0xD166, 0x5BA8, 0xCE77, 0x5BAC, 0xD168, 0x5BAD, 0xD167,\n\t0x5BAE, 0xAE63, 0x5BB0, 0xAE5F, 0x5BB3, 0xAE60, 0x5BB4, 0xAE62,\n\t0x5BB5, 0xAE64, 0x5BB6, 0xAE61, 0x5BB8, 0xAE66, 0x5BB9, 0xAE65,\n\t0x5BBF, 0xB14A, 0x5BC0, 0xD4F2, 0x5BC1, 0xD4F1, 0x5BC2, 0xB149,\n\t0x5BC4, 0xB148, 0x5BC5, 0xB147, 0x5BC6, 0xB14B, 0x5BC7, 0xB146,\n\t0x5BCA, 0xD8D5, 0x5BCB, 0xD8D2, 0x5BCC, 0xB449, 0x5BCD, 0xD8D1,\n\t0x5BCE, 0xD8D6, 0x5BD0, 0xB44B, 0x5BD1, 0xD8D4, 0x5BD2, 0xB448,\n\t0x5BD3, 0xB44A, 0x5BD4, 0xD8D3, 0x5BD6, 0xDD48, 0x5BD8, 0xDD49,\n\t0x5BD9, 0xDD4A, 0x5BDE, 0xB9E6, 0x5BDF, 0xB9EE, 0x5BE0, 0xE17E,\n\t0x5BE1, 0xB9E8, 0x5BE2, 0xB9EC, 0x5BE3, 0xE1A1, 0x5BE4, 0xB9ED,\n\t0x5BE5, 0xB9E9, 0x5BE6, 0xB9EA, 0x5BE7, 0xB9E7, 0x5BE8, 0xB9EB,\n\t0x5BE9, 0xBC66, 0x5BEA, 0xD8D0, 0x5BEB, 0xBC67, 0x5BEC, 0xBC65,\n\t0x5BEE, 0xBC64, 0x5BEF, 0xE95D, 0x5BF0, 0xBEC8, 0x5BF1, 0xECD8,\n\t0x5BF2, 0xECD9, 0x5BF5, 0xC364, 0x5BF6, 0xC45F, 0x5BF8, 0xA46F,\n\t0x5BFA, 0xA678, 0x5C01, 0xABCA, 0x5C03, 0xD169, 0x5C04, 0xAE67,\n\t0x5C07, 0xB14E, 0x5C08, 0xB14D, 0x5C09, 0xB14C, 0x5C0A, 0xB44C,\n\t0x5C0B, 0xB44D, 0x5C0C, 0xD8D7, 0x5C0D, 0xB9EF, 0x5C0E, 0xBEC9,\n\t0x5C0F, 0xA470, 0x5C10, 0xC95C, 0x5C11, 0xA4D6, 0x5C12, 0xC974,\n\t0x5C15, 0xC9D4, 0x5C16, 0xA679, 0x5C1A, 0xA97C, 0x5C1F, 0xDD4B,\n\t0x5C22, 0xA471, 0x5C24, 0xA4D7, 0x5C25, 0xC9D5, 0x5C28, 0xCABE,\n\t0x5C2A, 0xCABF, 0x5C2C, 0xA7BC, 0x5C30, 0xD8D8, 0x5C31, 0xB44E,\n\t0x5C33, 0xDD4C, 0x5C37, 0xC0AA, 0x5C38, 0xA472, 0x5C39, 0xA4A8,\n\t0x5C3A, 0xA4D8, 0x5C3B, 0xC975, 0x5C3C, 0xA5A7, 0x5C3E, 0xA7C0,\n\t0x5C3F, 0xA7BF, 0x5C40, 0xA7BD, 0x5C41, 0xA7BE, 0x5C44, 0xCC59,\n\t0x5C45, 0xA97E, 0x5C46, 0xA9A1, 0x5C47, 0xCC5A, 0x5C48, 0xA97D,\n\t0x5C4B, 0xABCE, 0x5C4C, 0xCE78, 0x5C4D, 0xABCD, 0x5C4E, 0xABCB,\n\t0x5C4F, 0xABCC, 0x5C50, 0xAE6A, 0x5C51, 0xAE68, 0x5C54, 0xD16B,\n\t0x5C55, 0xAE69, 0x5C56, 0xD16A, 0x5C58, 0xAE5E, 0x5C59, 0xD4F3,\n\t0x5C5C, 0xB150, 0x5C5D, 0xB151, 0x5C60, 0xB14F, 0x5C62, 0xB9F0,\n\t0x5C63, 0xE1A2, 0x5C64, 0xBC68, 0x5C65, 0xBC69, 0x5C67, 0xE561,\n\t0x5C68, 0xC0AB, 0x5C69, 0xEFC2, 0x5C6A, 0xEFC3, 0x5C6C, 0xC4DD,\n\t0x5C6D, 0xF8A8, 0x5C6E, 0xC94B, 0x5C6F, 0xA4D9, 0x5C71, 0xA473,\n\t0x5C73, 0xC977, 0x5C74, 0xC976, 0x5C79, 0xA67A, 0x5C7A, 0xC9D7,\n\t0x5C7B, 0xC9D8, 0x5C7C, 0xC9D6, 0x5C7E, 0xC9D9, 0x5C86, 0xCAC7,\n\t0x5C88, 0xCAC2, 0x5C89, 0xCAC4, 0x5C8A, 0xCAC6, 0x5C8B, 0xCAC3,\n\t0x5C8C, 0xA7C4, 0x5C8D, 0xCAC0, 0x5C8F, 0xCAC1, 0x5C90, 0xA7C1,\n\t0x5C91, 0xA7C2, 0x5C92, 0xCAC5, 0x5C93, 0xCAC8, 0x5C94, 0xA7C3,\n\t0x5C95, 0xCAC9, 0x5C9D, 0xCC68, 0x5C9F, 0xCC62, 0x5CA0, 0xCC5D,\n\t0x5CA1, 0xA9A3, 0x5CA2, 0xCC65, 0x5CA3, 0xCC63, 0x5CA4, 0xCC5C,\n\t0x5CA5, 0xCC69, 0x5CA6, 0xCC6C, 0x5CA7, 0xCC67, 0x5CA8, 0xCC60,\n\t0x5CA9, 0xA9A5, 0x5CAA, 0xCC66, 0x5CAB, 0xA9A6, 0x5CAC, 0xCC61,\n\t0x5CAD, 0xCC64, 0x5CAE, 0xCC5B, 0x5CAF, 0xCC5F, 0x5CB0, 0xCC6B,\n\t0x5CB1, 0xA9A7, 0x5CB3, 0xA9A8, 0x5CB5, 0xCC5E, 0x5CB6, 0xCC6A,\n\t0x5CB7, 0xA9A2, 0x5CB8, 0xA9A4, 0x5CC6, 0xCEAB, 0x5CC7, 0xCEA4,\n\t0x5CC8, 0xCEAA, 0x5CC9, 0xCEA3, 0x5CCA, 0xCEA5, 0x5CCB, 0xCE7D,\n\t0x5CCC, 0xCE7B, 0x5CCE, 0xCEAC, 0x5CCF, 0xCEA9, 0x5CD0, 0xCE79,\n\t0x5CD2, 0xABD0, 0x5CD3, 0xCEA7, 0x5CD4, 0xCEA8, 0x5CD6, 0xCEA6,\n\t0x5CD7, 0xCE7C, 0x5CD8, 0xCE7A, 0x5CD9, 0xABCF, 0x5CDA, 0xCEA2,\n\t0x5CDB, 0xCE7E, 0x5CDE, 0xCEA1, 0x5CDF, 0xCEAD, 0x5CE8, 0xAE6F,\n\t0x5CEA, 0xAE6E, 0x5CEC, 0xD16C, 0x5CED, 0xAE6B, 0x5CEE, 0xD16E,\n\t0x5CF0, 0xAE70, 0x5CF1, 0xD16F, 0x5CF4, 0xAE73, 0x5CF6, 0xAE71,\n\t0x5CF7, 0xD170, 0x5CF8, 0xCEAE, 0x5CF9, 0xD172, 0x5CFB, 0xAE6D,\n\t0x5CFD, 0xAE6C, 0x5CFF, 0xD16D, 0x5D00, 0xD171, 0x5D01, 0xAE72,\n\t0x5D06, 0xB153, 0x5D07, 0xB152, 0x5D0B, 0xD4F5, 0x5D0C, 0xD4F9,\n\t0x5D0D, 0xD4FB, 0x5D0E, 0xB154, 0x5D0F, 0xD4FE, 0x5D11, 0xB158,\n\t0x5D12, 0xD541, 0x5D14, 0xB15A, 0x5D16, 0xB156, 0x5D17, 0xB15E,\n\t0x5D19, 0xB15B, 0x5D1A, 0xD4F7, 0x5D1B, 0xB155, 0x5D1D, 0xD4F6,\n\t0x5D1E, 0xD4F4, 0x5D1F, 0xD543, 0x5D20, 0xD4F8, 0x5D22, 0xB157,\n\t0x5D23, 0xD542, 0x5D24, 0xB15C, 0x5D25, 0xD4FD, 0x5D26, 0xD4FC,\n\t0x5D27, 0xB15D, 0x5D28, 0xD4FA, 0x5D29, 0xB159, 0x5D2E, 0xD544,\n\t0x5D30, 0xD540, 0x5D31, 0xD8E7, 0x5D32, 0xD8EE, 0x5D33, 0xD8E3,\n\t0x5D34, 0xB451, 0x5D35, 0xD8DF, 0x5D36, 0xD8EF, 0x5D37, 0xD8D9,\n\t0x5D38, 0xD8EC, 0x5D39, 0xD8EA, 0x5D3A, 0xD8E4, 0x5D3C, 0xD8ED,\n\t0x5D3D, 0xD8E6, 0x5D3F, 0xD8DE, 0x5D40, 0xD8F0, 0x5D41, 0xD8DC,\n\t0x5D42, 0xD8E9, 0x5D43, 0xD8DA, 0x5D45, 0xD8F1, 0x5D47, 0xB452,\n\t0x5D49, 0xD8EB, 0x5D4A, 0xDD4F, 0x5D4B, 0xD8DD, 0x5D4C, 0xB44F,\n\t0x5D4E, 0xD8E1, 0x5D50, 0xB450, 0x5D51, 0xD8E0, 0x5D52, 0xD8E5,\n\t0x5D55, 0xD8E2, 0x5D59, 0xD8E8, 0x5D5E, 0xDD53, 0x5D62, 0xDD56,\n\t0x5D63, 0xDD4E, 0x5D65, 0xDD50, 0x5D67, 0xDD55, 0x5D68, 0xDD54,\n\t0x5D69, 0xB743, 0x5D6B, 0xD8DB, 0x5D6C, 0xDD52, 0x5D6F, 0xB744,\n\t0x5D71, 0xDD4D, 0x5D72, 0xDD51, 0x5D77, 0xE1A9, 0x5D79, 0xE1B0,\n\t0x5D7A, 0xE1A7, 0x5D7C, 0xE1AE, 0x5D7D, 0xE1A5, 0x5D7E, 0xE1AD,\n\t0x5D7F, 0xE1B1, 0x5D80, 0xE1A4, 0x5D81, 0xE1A8, 0x5D82, 0xE1A3,\n\t0x5D84, 0xB9F1, 0x5D86, 0xE1A6, 0x5D87, 0xB9F2, 0x5D88, 0xE1AC,\n\t0x5D89, 0xE1AB, 0x5D8A, 0xE1AA, 0x5D8D, 0xE1AF, 0x5D92, 0xE565,\n\t0x5D93, 0xE567, 0x5D94, 0xBC6B, 0x5D95, 0xE568, 0x5D97, 0xE563,\n\t0x5D99, 0xE562, 0x5D9A, 0xE56C, 0x5D9C, 0xE56A, 0x5D9D, 0xBC6A,\n\t0x5D9E, 0xE56D, 0x5D9F, 0xE564, 0x5DA0, 0xE569, 0x5DA1, 0xE56B,\n\t0x5DA2, 0xE566, 0x5DA7, 0xE961, 0x5DA8, 0xE966, 0x5DA9, 0xE960,\n\t0x5DAA, 0xE965, 0x5DAC, 0xE95E, 0x5DAD, 0xE968, 0x5DAE, 0xE964,\n\t0x5DAF, 0xE969, 0x5DB0, 0xE963, 0x5DB1, 0xE95F, 0x5DB2, 0xE967,\n\t0x5DB4, 0xE96A, 0x5DB5, 0xE962, 0x5DB7, 0xECDA, 0x5DB8, 0xC0AF,\n\t0x5DBA, 0xC0AD, 0x5DBC, 0xC0AC, 0x5DBD, 0xC0AE, 0x5DC0, 0xEFC4,\n\t0x5DC2, 0xF172, 0x5DC3, 0xF1FD, 0x5DC6, 0xF444, 0x5DC7, 0xF445,\n\t0x5DC9, 0xC460, 0x5DCB, 0xF5C9, 0x5DCD, 0xC4DE, 0x5DCF, 0xF5CA,\n\t0x5DD1, 0xF6DE, 0x5DD2, 0xC572, 0x5DD4, 0xC571, 0x5DD5, 0xF6DD,\n\t0x5DD6, 0xC5C9, 0x5DD8, 0xF7D6, 0x5DDD, 0xA474, 0x5DDE, 0xA67B,\n\t0x5DDF, 0xC9DA, 0x5DE0, 0xCACA, 0x5DE1, 0xA8B5, 0x5DE2, 0xB15F,\n\t0x5DE5, 0xA475, 0x5DE6, 0xA5AA, 0x5DE7, 0xA5A9, 0x5DE8, 0xA5A8,\n\t0x5DEB, 0xA7C5, 0x5DEE, 0xAE74, 0x5DF0, 0xDD57, 0x5DF1, 0xA476,\n\t0x5DF2, 0xA477, 0x5DF3, 0xA478, 0x5DF4, 0xA4DA, 0x5DF7, 0xABD1,\n\t0x5DF9, 0xCEAF, 0x5DFD, 0xB453, 0x5DFE, 0xA479, 0x5DFF, 0xC95D,\n\t0x5E02, 0xA5AB, 0x5E03, 0xA5AC, 0x5E04, 0xC978, 0x5E06, 0xA67C,\n\t0x5E0A, 0xCACB, 0x5E0C, 0xA7C6, 0x5E0E, 0xCACC, 0x5E11, 0xA9AE,\n\t0x5E14, 0xCC6E, 0x5E15, 0xA9AC, 0x5E16, 0xA9AB, 0x5E17, 0xCC6D,\n\t0x5E18, 0xA9A9, 0x5E19, 0xCC6F, 0x5E1A, 0xA9AA, 0x5E1B, 0xA9AD,\n\t0x5E1D, 0xABD2, 0x5E1F, 0xABD4, 0x5E20, 0xCEB3, 0x5E21, 0xCEB0,\n\t0x5E22, 0xCEB1, 0x5E23, 0xCEB2, 0x5E24, 0xCEB4, 0x5E25, 0xABD3,\n\t0x5E28, 0xD174, 0x5E29, 0xD173, 0x5E2B, 0xAE76, 0x5E2D, 0xAE75,\n\t0x5E33, 0xB162, 0x5E34, 0xD546, 0x5E36, 0xB161, 0x5E37, 0xB163,\n\t0x5E38, 0xB160, 0x5E3D, 0xB455, 0x5E3E, 0xD545, 0x5E40, 0xB456,\n\t0x5E41, 0xD8F3, 0x5E43, 0xB457, 0x5E44, 0xD8F2, 0x5E45, 0xB454,\n\t0x5E4A, 0xDD5A, 0x5E4B, 0xDD5C, 0x5E4C, 0xB745, 0x5E4D, 0xDD5B,\n\t0x5E4E, 0xDD59, 0x5E4F, 0xDD58, 0x5E53, 0xE1B4, 0x5E54, 0xB9F7,\n\t0x5E55, 0xB9F5, 0x5E57, 0xB9F6, 0x5E58, 0xE1B2, 0x5E59, 0xE1B3,\n\t0x5E5B, 0xB9F3, 0x5E5C, 0xE571, 0x5E5D, 0xE56F, 0x5E5F, 0xBC6D,\n\t0x5E60, 0xE570, 0x5E61, 0xBC6E, 0x5E62, 0xBC6C, 0x5E63, 0xB9F4,\n\t0x5E66, 0xE96D, 0x5E67, 0xE96B, 0x5E68, 0xE96C, 0x5E69, 0xE56E,\n\t0x5E6A, 0xECDC, 0x5E6B, 0xC0B0, 0x5E6C, 0xECDB, 0x5E6D, 0xEFC5,\n\t0x5E6E, 0xEFC6, 0x5E6F, 0xE96E, 0x5E70, 0xF1FE, 0x5E72, 0xA47A,\n\t0x5E73, 0xA5AD, 0x5E74, 0xA67E, 0x5E75, 0xC9DB, 0x5E76, 0xA67D,\n\t0x5E78, 0xA9AF, 0x5E79, 0xB746, 0x5E7B, 0xA4DB, 0x5E7C, 0xA5AE,\n\t0x5E7D, 0xABD5, 0x5E7E, 0xB458, 0x5E80, 0xC979, 0x5E82, 0xC97A,\n\t0x5E84, 0xC9DC, 0x5E87, 0xA7C8, 0x5E88, 0xCAD0, 0x5E89, 0xCACE,\n\t0x5E8A, 0xA7C9, 0x5E8B, 0xCACD, 0x5E8C, 0xCACF, 0x5E8D, 0xCAD1,\n\t0x5E8F, 0xA7C7, 0x5E95, 0xA9B3, 0x5E96, 0xA9B4, 0x5E97, 0xA9B1,\n\t0x5E9A, 0xA9B0, 0x5E9B, 0xCEB8, 0x5E9C, 0xA9B2, 0x5EA0, 0xABD6,\n\t0x5EA2, 0xCEB7, 0x5EA3, 0xCEB9, 0x5EA4, 0xCEB6, 0x5EA5, 0xCEBA,\n\t0x5EA6, 0xABD7, 0x5EA7, 0xAE79, 0x5EA8, 0xD175, 0x5EAA, 0xD177,\n\t0x5EAB, 0xAE77, 0x5EAC, 0xD178, 0x5EAD, 0xAE78, 0x5EAE, 0xD176,\n\t0x5EB0, 0xCEB5, 0x5EB1, 0xD547, 0x5EB2, 0xD54A, 0x5EB3, 0xD54B,\n\t0x5EB4, 0xD548, 0x5EB5, 0xB167, 0x5EB6, 0xB166, 0x5EB7, 0xB164,\n\t0x5EB8, 0xB165, 0x5EB9, 0xD549, 0x5EBE, 0xB168, 0x5EC1, 0xB45A,\n\t0x5EC2, 0xB45B, 0x5EC4, 0xB45C, 0x5EC5, 0xDD5D, 0x5EC6, 0xDD5F,\n\t0x5EC7, 0xDD61, 0x5EC8, 0xB748, 0x5EC9, 0xB747, 0x5ECA, 0xB459,\n\t0x5ECB, 0xDD60, 0x5ECC, 0xDD5E, 0x5ECE, 0xE1B8, 0x5ED1, 0xE1B6,\n\t0x5ED2, 0xE1BC, 0x5ED3, 0xB9F8, 0x5ED4, 0xE1BD, 0x5ED5, 0xE1BA,\n\t0x5ED6, 0xB9F9, 0x5ED7, 0xE1B7, 0x5ED8, 0xE1B5, 0x5ED9, 0xE1BB,\n\t0x5EDA, 0xBC70, 0x5EDB, 0xE573, 0x5EDC, 0xE1B9, 0x5EDD, 0xBC72,\n\t0x5EDE, 0xE574, 0x5EDF, 0xBC71, 0x5EE0, 0xBC74, 0x5EE1, 0xE575,\n\t0x5EE2, 0xBC6F, 0x5EE3, 0xBC73, 0x5EE5, 0xE973, 0x5EE6, 0xE971,\n\t0x5EE7, 0xE970, 0x5EE8, 0xE972, 0x5EE9, 0xE96F, 0x5EEC, 0xC366,\n\t0x5EEE, 0xF446, 0x5EEF, 0xF447, 0x5EF1, 0xF5CB, 0x5EF2, 0xF6DF,\n\t0x5EF3, 0xC655, 0x5EF6, 0xA9B5, 0x5EF7, 0xA7CA, 0x5EFA, 0xABD8,\n\t0x5EFE, 0xA47B, 0x5EFF, 0xA4DC, 0x5F01, 0xA5AF, 0x5F02, 0xC9DD,\n\t0x5F04, 0xA7CB, 0x5F05, 0xCAD2, 0x5F07, 0xCEBB, 0x5F08, 0xABD9,\n\t0x5F0A, 0xB9FA, 0x5F0B, 0xA47C, 0x5F0F, 0xA6A1, 0x5F12, 0xB749,\n\t0x5F13, 0xA47D, 0x5F14, 0xA4DD, 0x5F15, 0xA4DE, 0x5F17, 0xA5B1,\n\t0x5F18, 0xA5B0, 0x5F1A, 0xC9DE, 0x5F1B, 0xA6A2, 0x5F1D, 0xCAD3,\n\t0x5F1F, 0xA7CC, 0x5F22, 0xCC71, 0x5F23, 0xCC72, 0x5F24, 0xCC73,\n\t0x5F26, 0xA9B6, 0x5F27, 0xA9B7, 0x5F28, 0xCC70, 0x5F29, 0xA9B8,\n\t0x5F2D, 0xABDA, 0x5F2E, 0xCEBC, 0x5F30, 0xD17A, 0x5F31, 0xAE7A,\n\t0x5F33, 0xD179, 0x5F35, 0xB169, 0x5F36, 0xD54C, 0x5F37, 0xB16A,\n\t0x5F38, 0xD54D, 0x5F3C, 0xB45D, 0x5F40, 0xDD62, 0x5F43, 0xE1BF,\n\t0x5F44, 0xE1BE, 0x5F46, 0xB9FB, 0x5F48, 0xBC75, 0x5F49, 0xE576,\n\t0x5F4A, 0xBECA, 0x5F4B, 0xE974, 0x5F4C, 0xC0B1, 0x5F4E, 0xC573,\n\t0x5F4F, 0xF7D8, 0x5F54, 0xCC74, 0x5F56, 0xCEBD, 0x5F57, 0xB16B,\n\t0x5F58, 0xD8F4, 0x5F59, 0xB74A, 0x5F5D, 0xC255, 0x5F62, 0xA7CE,\n\t0x5F64, 0xA7CD, 0x5F65, 0xABDB, 0x5F67, 0xD17B, 0x5F69, 0xB16D,\n\t0x5F6A, 0xB343, 0x5F6B, 0xB16E, 0x5F6C, 0xB16C, 0x5F6D, 0xB45E,\n\t0x5F6F, 0xE1C0, 0x5F70, 0xB9FC, 0x5F71, 0xBC76, 0x5F73, 0xC94C,\n\t0x5F74, 0xC9DF, 0x5F76, 0xCAD5, 0x5F77, 0xA7CF, 0x5F78, 0xCAD4,\n\t0x5F79, 0xA7D0, 0x5F7C, 0xA9BC, 0x5F7D, 0xCC77, 0x5F7E, 0xCC76,\n\t0x5F7F, 0xA9BB, 0x5F80, 0xA9B9, 0x5F81, 0xA9BA, 0x5F82, 0xCC75,\n\t0x5F85, 0xABDD, 0x5F86, 0xCEBE, 0x5F87, 0xABE0, 0x5F88, 0xABDC,\n\t0x5F89, 0xABE2, 0x5F8A, 0xABDE, 0x5F8B, 0xABDF, 0x5F8C, 0xABE1,\n\t0x5F90, 0xAE7D, 0x5F91, 0xAE7C, 0x5F92, 0xAE7B, 0x5F96, 0xD54F,\n\t0x5F97, 0xB16F, 0x5F98, 0xB172, 0x5F99, 0xB170, 0x5F9B, 0xD54E,\n\t0x5F9C, 0xB175, 0x5F9E, 0xB171, 0x5F9F, 0xD550, 0x5FA0, 0xB174,\n\t0x5FA1, 0xB173, 0x5FA5, 0xD8F6, 0x5FA6, 0xD8F5, 0x5FA8, 0xB461,\n\t0x5FA9, 0xB45F, 0x5FAA, 0xB460, 0x5FAB, 0xD8F7, 0x5FAC, 0xB74B,\n\t0x5FAD, 0xDD64, 0x5FAE, 0xB74C, 0x5FAF, 0xDD63, 0x5FB2, 0xE577,\n\t0x5FB5, 0xBC78, 0x5FB6, 0xE1C1, 0x5FB7, 0xBC77, 0x5FB9, 0xB9FD,\n\t0x5FBB, 0xECDE, 0x5FBC, 0xE975, 0x5FBD, 0xC0B2, 0x5FBE, 0xECDD,\n\t0x5FBF, 0xF240, 0x5FC0, 0xF448, 0x5FC1, 0xF449, 0x5FC3, 0xA4DF,\n\t0x5FC5, 0xA5B2, 0x5FC9, 0xC97B, 0x5FCC, 0xA7D2, 0x5FCD, 0xA7D4,\n\t0x5FCF, 0xC9E2, 0x5FD0, 0xCAD8, 0x5FD1, 0xCAD7, 0x5FD2, 0xCAD6,\n\t0x5FD4, 0xC9E1, 0x5FD5, 0xC9E0, 0x5FD6, 0xA6A4, 0x5FD7, 0xA7D3,\n\t0x5FD8, 0xA7D1, 0x5FD9, 0xA6A3, 0x5FDD, 0xA9BD, 0x5FDE, 0xCC78,\n\t0x5FE0, 0xA9BE, 0x5FE1, 0xCADD, 0x5FE3, 0xCADF, 0x5FE4, 0xCADE,\n\t0x5FE5, 0xCC79, 0x5FE8, 0xCADA, 0x5FEA, 0xA7D8, 0x5FEB, 0xA7D6,\n\t0x5FED, 0xCAD9, 0x5FEE, 0xCADB, 0x5FEF, 0xCAE1, 0x5FF1, 0xA7D5,\n\t0x5FF3, 0xCADC, 0x5FF4, 0xCAE5, 0x5FF5, 0xA9C0, 0x5FF7, 0xCAE2,\n\t0x5FF8, 0xA7D7, 0x5FFA, 0xCAE0, 0x5FFB, 0xCAE3, 0x5FFD, 0xA9BF,\n\t0x5FFF, 0xA9C1, 0x6000, 0xCAE4, 0x6009, 0xCCAF, 0x600A, 0xCCA2,\n\t0x600B, 0xCC7E, 0x600C, 0xCCAE, 0x600D, 0xCCA9, 0x600E, 0xABE7,\n\t0x600F, 0xA9C2, 0x6010, 0xCCAA, 0x6011, 0xCCAD, 0x6012, 0xABE3,\n\t0x6013, 0xCCAC, 0x6014, 0xA9C3, 0x6015, 0xA9C8, 0x6016, 0xA9C6,\n\t0x6017, 0xCCA3, 0x6019, 0xCC7C, 0x601A, 0xCCA5, 0x601B, 0xA9CD,\n\t0x601C, 0xCCB0, 0x601D, 0xABE4, 0x601E, 0xCCA6, 0x6020, 0xABE5,\n\t0x6021, 0xA9C9, 0x6022, 0xCCA8, 0x6024, 0xCECD, 0x6025, 0xABE6,\n\t0x6026, 0xCC7B, 0x6027, 0xA9CA, 0x6028, 0xABE8, 0x6029, 0xA9CB,\n\t0x602A, 0xA9C7, 0x602B, 0xA9CC, 0x602C, 0xCCA7, 0x602D, 0xCC7A,\n\t0x602E, 0xCCAB, 0x602F, 0xA9C4, 0x6032, 0xCC7D, 0x6033, 0xCCA4,\n\t0x6034, 0xCCA1, 0x6035, 0xA9C5, 0x6037, 0xCEBF, 0x6039, 0xCEC0,\n\t0x6040, 0xCECA, 0x6041, 0xD1A1, 0x6042, 0xCECB, 0x6043, 0xABEE,\n\t0x6044, 0xCECE, 0x6045, 0xCEC4, 0x6046, 0xABED, 0x6047, 0xCEC6,\n\t0x6049, 0xCEC7, 0x604C, 0xCEC9, 0x604D, 0xABE9, 0x6050, 0xAEA3,\n\t0x6052, 0xF9DA, 0x6053, 0xCEC5, 0x6054, 0xCEC1, 0x6055, 0xAEA4,\n\t0x6058, 0xCECF, 0x6059, 0xAE7E, 0x605A, 0xD17D, 0x605B, 0xCEC8,\n\t0x605D, 0xD17C, 0x605E, 0xCEC3, 0x605F, 0xCECC, 0x6062, 0xABEC,\n\t0x6063, 0xAEA1, 0x6064, 0xABF2, 0x6065, 0xAEA2, 0x6066, 0xCED0,\n\t0x6067, 0xD17E, 0x6068, 0xABEB, 0x6069, 0xAEA6, 0x606A, 0xABF1,\n\t0x606B, 0xABF0, 0x606C, 0xABEF, 0x606D, 0xAEA5, 0x606E, 0xCED1,\n\t0x606F, 0xAEA7, 0x6070, 0xABEA, 0x6072, 0xCEC2, 0x607F, 0xB176,\n\t0x6080, 0xD1A4, 0x6081, 0xD1A6, 0x6083, 0xD1A8, 0x6084, 0xAEA8,\n\t0x6085, 0xAEAE, 0x6086, 0xD553, 0x6087, 0xD1AC, 0x6088, 0xD1A3,\n\t0x6089, 0xB178, 0x608A, 0xD551, 0x608C, 0xAEAD, 0x608D, 0xAEAB,\n\t0x608E, 0xD1AE, 0x6090, 0xD552, 0x6092, 0xD1A5, 0x6094, 0xAEAC,\n\t0x6095, 0xD1A9, 0x6096, 0xAEAF, 0x6097, 0xD1AB, 0x609A, 0xAEAA,\n\t0x609B, 0xD1AA, 0x609C, 0xD1AD, 0x609D, 0xD1A7, 0x609F, 0xAEA9,\n\t0x60A0, 0xB179, 0x60A2, 0xD1A2, 0x60A3, 0xB177, 0x60A8, 0xB17A,\n\t0x60B0, 0xD555, 0x60B1, 0xD55E, 0x60B2, 0xB464, 0x60B4, 0xB17C,\n\t0x60B5, 0xB1A3, 0x60B6, 0xB465, 0x60B7, 0xD560, 0x60B8, 0xB1AA,\n\t0x60B9, 0xD8F9, 0x60BA, 0xD556, 0x60BB, 0xB1A2, 0x60BC, 0xB1A5,\n\t0x60BD, 0xB17E, 0x60BE, 0xD554, 0x60BF, 0xD562, 0x60C0, 0xD565,\n\t0x60C1, 0xD949, 0x60C3, 0xD563, 0x60C4, 0xD8FD, 0x60C5, 0xB1A1,\n\t0x60C6, 0xB1A8, 0x60C7, 0xB1AC, 0x60C8, 0xD55D, 0x60C9, 0xD8F8,\n\t0x60CA, 0xD561, 0x60CB, 0xB17B, 0x60CC, 0xD8FA, 0x60CD, 0xD564,\n\t0x60CE, 0xD8FC, 0x60CF, 0xD559, 0x60D1, 0xB462, 0x60D3, 0xD557,\n\t0x60D4, 0xD558, 0x60D5, 0xB1A7, 0x60D8, 0xB1A6, 0x60D9, 0xD55B,\n\t0x60DA, 0xB1AB, 0x60DB, 0xD55F, 0x60DC, 0xB1A4, 0x60DD, 0xD55C,\n\t0x60DF, 0xB1A9, 0x60E0, 0xB466, 0x60E1, 0xB463, 0x60E2, 0xD8FB,\n\t0x60E4, 0xD55A, 0x60E6, 0xB17D, 0x60F0, 0xB46B, 0x60F1, 0xB46F,\n\t0x60F2, 0xD940, 0x60F3, 0xB751, 0x60F4, 0xB46D, 0x60F5, 0xD944,\n\t0x60F6, 0xB471, 0x60F7, 0xDD65, 0x60F8, 0xD946, 0x60F9, 0xB753,\n\t0x60FA, 0xB469, 0x60FB, 0xB46C, 0x60FC, 0xD947, 0x60FE, 0xD948,\n\t0x60FF, 0xD94E, 0x6100, 0xB473, 0x6101, 0xB754, 0x6103, 0xD94A,\n\t0x6104, 0xD94F, 0x6105, 0xD943, 0x6106, 0xB75E, 0x6108, 0xB755,\n\t0x6109, 0xB472, 0x610A, 0xD941, 0x610B, 0xD950, 0x610D, 0xB75D,\n\t0x610E, 0xB470, 0x610F, 0xB74E, 0x6110, 0xD94D, 0x6112, 0xB474,\n\t0x6113, 0xD945, 0x6114, 0xD8FE, 0x6115, 0xB46A, 0x6116, 0xD942,\n\t0x6118, 0xD94B, 0x611A, 0xB74D, 0x611B, 0xB752, 0x611C, 0xB467,\n\t0x611D, 0xD94C, 0x611F, 0xB750, 0x6123, 0xB468, 0x6127, 0xB75C,\n\t0x6128, 0xE1C3, 0x6129, 0xDD70, 0x612B, 0xDD68, 0x612C, 0xE1C2,\n\t0x612E, 0xDD6C, 0x612F, 0xDD6E, 0x6132, 0xDD6B, 0x6134, 0xB75B,\n\t0x6136, 0xDD6A, 0x6137, 0xB75F, 0x613B, 0xE1D2, 0x613E, 0xB75A,\n\t0x613F, 0xBA40, 0x6140, 0xDD71, 0x6141, 0xE1C4, 0x6144, 0xB758,\n\t0x6145, 0xDD69, 0x6146, 0xDD6D, 0x6147, 0xB9FE, 0x6148, 0xB74F,\n\t0x6149, 0xDD66, 0x614A, 0xDD67, 0x614B, 0xBA41, 0x614C, 0xB757,\n\t0x614D, 0xB759, 0x614E, 0xB756, 0x614F, 0xDD6F, 0x6152, 0xE1C8,\n\t0x6153, 0xE1C9, 0x6154, 0xE1CE, 0x6155, 0xBC7D, 0x6156, 0xE1D5,\n\t0x6158, 0xBA47, 0x615A, 0xBA46, 0x615B, 0xE1D0, 0x615D, 0xBC7C,\n\t0x615E, 0xE1C5, 0x615F, 0xBA45, 0x6161, 0xE1D4, 0x6162, 0xBA43,\n\t0x6163, 0xBA44, 0x6165, 0xE1D1, 0x6166, 0xE5AA, 0x6167, 0xBC7A,\n\t0x6168, 0xB46E, 0x616A, 0xE1D3, 0x616B, 0xBCA3, 0x616C, 0xE1CB,\n\t0x616E, 0xBC7B, 0x6170, 0xBCA2, 0x6171, 0xE1C6, 0x6172, 0xE1CA,\n\t0x6173, 0xE1C7, 0x6174, 0xE1CD, 0x6175, 0xBA48, 0x6176, 0xBC79,\n\t0x6177, 0xBA42, 0x6179, 0xE57A, 0x617A, 0xE1CF, 0x617C, 0xBCA1,\n\t0x617E, 0xBCA4, 0x6180, 0xE1CC, 0x6182, 0xBC7E, 0x6183, 0xE579,\n\t0x6189, 0xE57E, 0x618A, 0xBECE, 0x618B, 0xE578, 0x618C, 0xE9A3,\n\t0x618D, 0xE5A9, 0x618E, 0xBCA8, 0x6190, 0xBCA6, 0x6191, 0xBECC,\n\t0x6192, 0xE5A6, 0x6193, 0xE5A2, 0x6194, 0xBCAC, 0x6196, 0xE978,\n\t0x619A, 0xBCAA, 0x619B, 0xE5A1, 0x619D, 0xE976, 0x619F, 0xE5A5,\n\t0x61A1, 0xE5A8, 0x61A2, 0xE57D, 0x61A4, 0xBCAB, 0x61A7, 0xBCA5,\n\t0x61A8, 0xE977, 0x61A9, 0xBECD, 0x61AA, 0xE5A7, 0x61AB, 0xBCA7,\n\t0x61AC, 0xBCA9, 0x61AD, 0xE5A4, 0x61AE, 0xBCAD, 0x61AF, 0xE5A3,\n\t0x61B0, 0xE57C, 0x61B1, 0xE57B, 0x61B2, 0xBECB, 0x61B3, 0xE5AB,\n\t0x61B4, 0xE97A, 0x61B5, 0xECE0, 0x61B6, 0xBED0, 0x61B8, 0xE9A2,\n\t0x61BA, 0xE97E, 0x61BC, 0xECE1, 0x61BE, 0xBED1, 0x61BF, 0xE9A1,\n\t0x61C1, 0xE97C, 0x61C2, 0xC0B4, 0x61C3, 0xECDF, 0x61C5, 0xE979,\n\t0x61C6, 0xE97B, 0x61C7, 0xC0B5, 0x61C8, 0xBED3, 0x61C9, 0xC0B3,\n\t0x61CA, 0xBED2, 0x61CB, 0xC0B7, 0x61CC, 0xE97D, 0x61CD, 0xBECF,\n\t0x61D6, 0xEFCF, 0x61D8, 0xEFC7, 0x61DE, 0xECE7, 0x61DF, 0xEFC8,\n\t0x61E0, 0xECE3, 0x61E3, 0xC256, 0x61E4, 0xECE5, 0x61E5, 0xECE4,\n\t0x61E6, 0xC0B6, 0x61E7, 0xECE2, 0x61E8, 0xECE6, 0x61E9, 0xEFD0,\n\t0x61EA, 0xEFCC, 0x61EB, 0xEFCE, 0x61ED, 0xEFC9, 0x61EE, 0xEFCA,\n\t0x61F0, 0xEFCD, 0x61F1, 0xEFCB, 0x61F2, 0xC367, 0x61F5, 0xC36A,\n\t0x61F6, 0xC369, 0x61F7, 0xC368, 0x61F8, 0xC461, 0x61F9, 0xF44A,\n\t0x61FA, 0xC462, 0x61FB, 0xF241, 0x61FC, 0xC4DF, 0x61FD, 0xF5CC,\n\t0x61FE, 0xC4E0, 0x61FF, 0xC574, 0x6200, 0xC5CA, 0x6201, 0xF7D9,\n\t0x6203, 0xF7DA, 0x6204, 0xF7DB, 0x6207, 0xF9BA, 0x6208, 0xA4E0,\n\t0x6209, 0xC97C, 0x620A, 0xA5B3, 0x620C, 0xA6A6, 0x620D, 0xA6A7,\n\t0x620E, 0xA6A5, 0x6210, 0xA6A8, 0x6211, 0xA7DA, 0x6212, 0xA7D9,\n\t0x6214, 0xCCB1, 0x6215, 0xA9CF, 0x6216, 0xA9CE, 0x6219, 0xD1AF,\n\t0x621A, 0xB1AD, 0x621B, 0xB1AE, 0x621F, 0xB475, 0x6220, 0xDD72,\n\t0x6221, 0xB760, 0x6222, 0xB761, 0x6223, 0xDD74, 0x6224, 0xDD76,\n\t0x6225, 0xDD75, 0x6227, 0xE1D7, 0x6229, 0xE1D6, 0x622A, 0xBA49,\n\t0x622B, 0xE1D8, 0x622D, 0xE5AC, 0x622E, 0xBCAE, 0x6230, 0xBED4,\n\t0x6232, 0xC0B8, 0x6233, 0xC257, 0x6234, 0xC0B9, 0x6236, 0xA4E1,\n\t0x623A, 0xCAE6, 0x623D, 0xCCB2, 0x623E, 0xA9D1, 0x623F, 0xA9D0,\n\t0x6240, 0xA9D2, 0x6241, 0xABF3, 0x6242, 0xCED2, 0x6243, 0xCED3,\n\t0x6246, 0xD1B0, 0x6247, 0xAEB0, 0x6248, 0xB1AF, 0x6249, 0xB476,\n\t0x624A, 0xD951, 0x624B, 0xA4E2, 0x624D, 0xA47E, 0x624E, 0xA4E3,\n\t0x6250, 0xC97D, 0x6251, 0xA5B7, 0x6252, 0xA5B6, 0x6253, 0xA5B4,\n\t0x6254, 0xA5B5, 0x6258, 0xA6AB, 0x6259, 0xC9E9, 0x625A, 0xC9EB,\n\t0x625B, 0xA6AA, 0x625C, 0xC9E3, 0x625E, 0xC9E4, 0x6260, 0xC9EA,\n\t0x6261, 0xC9E6, 0x6262, 0xC9E8, 0x6263, 0xA6A9, 0x6264, 0xC9E5,\n\t0x6265, 0xC9EC, 0x6266, 0xC9E7, 0x626D, 0xA7E1, 0x626E, 0xA7EA,\n\t0x626F, 0xA7E8, 0x6270, 0xCAF0, 0x6271, 0xCAED, 0x6272, 0xCAF5,\n\t0x6273, 0xA7E6, 0x6274, 0xCAF6, 0x6276, 0xA7DF, 0x6277, 0xCAF3,\n\t0x6279, 0xA7E5, 0x627A, 0xCAEF, 0x627B, 0xCAEE, 0x627C, 0xA7E3,\n\t0x627D, 0xCAF4, 0x627E, 0xA7E4, 0x627F, 0xA9D3, 0x6280, 0xA7DE,\n\t0x6281, 0xCAF1, 0x6283, 0xCAE7, 0x6284, 0xA7DB, 0x6286, 0xA7EE,\n\t0x6287, 0xCAEC, 0x6288, 0xCAF2, 0x6289, 0xA7E0, 0x628A, 0xA7E2,\n\t0x628C, 0xCAE8, 0x628E, 0xCAE9, 0x628F, 0xCAEA, 0x6291, 0xA7ED,\n\t0x6292, 0xA7E7, 0x6293, 0xA7EC, 0x6294, 0xCAEB, 0x6295, 0xA7EB,\n\t0x6296, 0xA7DD, 0x6297, 0xA7DC, 0x6298, 0xA7E9, 0x62A8, 0xA9E1,\n\t0x62A9, 0xCCBE, 0x62AA, 0xCCB7, 0x62AB, 0xA9DC, 0x62AC, 0xA9EF,\n\t0x62AD, 0xCCB3, 0x62AE, 0xCCBA, 0x62AF, 0xCCBC, 0x62B0, 0xCCBF,\n\t0x62B1, 0xA9EA, 0x62B3, 0xCCBB, 0x62B4, 0xCCB4, 0x62B5, 0xA9E8,\n\t0x62B6, 0xCCB8, 0x62B8, 0xCCC0, 0x62B9, 0xA9D9, 0x62BB, 0xCCBD,\n\t0x62BC, 0xA9E3, 0x62BD, 0xA9E2, 0x62BE, 0xCCB6, 0x62BF, 0xA9D7,\n\t0x62C2, 0xA9D8, 0x62C4, 0xA9D6, 0x62C6, 0xA9EE, 0x62C7, 0xA9E6,\n\t0x62C8, 0xA9E0, 0x62C9, 0xA9D4, 0x62CA, 0xCCB9, 0x62CB, 0xA9DF,\n\t0x62CC, 0xA9D5, 0x62CD, 0xA9E7, 0x62CE, 0xA9F0, 0x62CF, 0xCED4,\n\t0x62D0, 0xA9E4, 0x62D1, 0xCCB5, 0x62D2, 0xA9DA, 0x62D3, 0xA9DD,\n\t0x62D4, 0xA9DE, 0x62D6, 0xA9EC, 0x62D7, 0xA9ED, 0x62D8, 0xA9EB,\n\t0x62D9, 0xA9E5, 0x62DA, 0xA9E9, 0x62DB, 0xA9DB, 0x62DC, 0xABF4,\n\t0x62EB, 0xCEDA, 0x62EC, 0xAC41, 0x62ED, 0xABF8, 0x62EE, 0xABFA,\n\t0x62EF, 0xAC40, 0x62F0, 0xCEE6, 0x62F1, 0xABFD, 0x62F2, 0xD1B1,\n\t0x62F3, 0xAEB1, 0x62F4, 0xAC43, 0x62F5, 0xCED7, 0x62F6, 0xCEDF,\n\t0x62F7, 0xABFE, 0x62F8, 0xCEDE, 0x62F9, 0xCEDB, 0x62FA, 0xCEE3,\n\t0x62FB, 0xCEE5, 0x62FC, 0xABF7, 0x62FD, 0xABFB, 0x62FE, 0xAC42,\n\t0x62FF, 0xAEB3, 0x6300, 0xCEE0, 0x6301, 0xABF9, 0x6302, 0xAC45,\n\t0x6303, 0xCED9, 0x6307, 0xABFC, 0x6308, 0xAEB2, 0x6309, 0xABF6,\n\t0x630B, 0xCED6, 0x630C, 0xCEDD, 0x630D, 0xCED5, 0x630E, 0xCED8,\n\t0x630F, 0xCEDC, 0x6310, 0xD1B2, 0x6311, 0xAC44, 0x6313, 0xCEE1,\n\t0x6314, 0xCEE2, 0x6315, 0xCEE4, 0x6316, 0xABF5, 0x6328, 0xAEC1,\n\t0x6329, 0xD1BE, 0x632A, 0xAEBF, 0x632B, 0xAEC0, 0x632C, 0xD1B4,\n\t0x632D, 0xD1C4, 0x632F, 0xAEB6, 0x6332, 0xD566, 0x6333, 0xD1C6,\n\t0x6334, 0xD1C0, 0x6336, 0xD1B7, 0x6338, 0xD1C9, 0x6339, 0xD1BA,\n\t0x633A, 0xAEBC, 0x633B, 0xD57D, 0x633C, 0xD1BD, 0x633D, 0xAEBE,\n\t0x633E, 0xAEB5, 0x6340, 0xD1CB, 0x6341, 0xD1BF, 0x6342, 0xAEB8,\n\t0x6343, 0xD1B8, 0x6344, 0xD1B5, 0x6345, 0xD1B6, 0x6346, 0xAEB9,\n\t0x6347, 0xD1C5, 0x6348, 0xD1CC, 0x6349, 0xAEBB, 0x634A, 0xD1BC,\n\t0x634B, 0xD1BB, 0x634C, 0xAEC3, 0x634D, 0xAEC2, 0x634E, 0xAEB4,\n\t0x634F, 0xAEBA, 0x6350, 0xAEBD, 0x6351, 0xD1C8, 0x6354, 0xD1C2,\n\t0x6355, 0xAEB7, 0x6356, 0xD1B3, 0x6357, 0xD1CA, 0x6358, 0xD1C1,\n\t0x6359, 0xD1C3, 0x635A, 0xD1C7, 0x6365, 0xD567, 0x6367, 0xB1B7,\n\t0x6368, 0xB1CB, 0x6369, 0xB1CA, 0x636B, 0xB1BF, 0x636D, 0xD579,\n\t0x636E, 0xD575, 0x636F, 0xD572, 0x6370, 0xD5A6, 0x6371, 0xB1BA,\n\t0x6372, 0xB1B2, 0x6375, 0xD577, 0x6376, 0xB4A8, 0x6377, 0xB1B6,\n\t0x6378, 0xD5A1, 0x637A, 0xB1CC, 0x637B, 0xB1C9, 0x637C, 0xD57B,\n\t0x637D, 0xD56A, 0x6380, 0xB1C8, 0x6381, 0xD5A3, 0x6382, 0xD569,\n\t0x6383, 0xB1BD, 0x6384, 0xB1C1, 0x6385, 0xD5A2, 0x6387, 0xD573,\n\t0x6388, 0xB1C2, 0x6389, 0xB1BC, 0x638A, 0xD568, 0x638C, 0xB478,\n\t0x638D, 0xD5A5, 0x638E, 0xD571, 0x638F, 0xB1C7, 0x6390, 0xD574,\n\t0x6391, 0xD5A4, 0x6392, 0xB1C6, 0x6394, 0xD952, 0x6396, 0xB1B3,\n\t0x6397, 0xD56F, 0x6398, 0xB1B8, 0x6399, 0xB1C3, 0x639B, 0xB1BE,\n\t0x639C, 0xD578, 0x639D, 0xD56E, 0x639E, 0xD56C, 0x639F, 0xD57E,\n\t0x63A0, 0xB1B0, 0x63A1, 0xB1C4, 0x63A2, 0xB1B4, 0x63A3, 0xB477,\n\t0x63A4, 0xD57C, 0x63A5, 0xB1B5, 0x63A7, 0xB1B1, 0x63A8, 0xB1C0,\n\t0x63A9, 0xB1BB, 0x63AA, 0xB1B9, 0x63AB, 0xD570, 0x63AC, 0xB1C5,\n\t0x63AD, 0xD56D, 0x63AE, 0xD57A, 0x63AF, 0xD576, 0x63B0, 0xD954,\n\t0x63B1, 0xD953, 0x63BD, 0xD56B, 0x63BE, 0xD964, 0x63C0, 0xB47A,\n\t0x63C2, 0xD96A, 0x63C3, 0xD959, 0x63C4, 0xD967, 0x63C5, 0xDD77,\n\t0x63C6, 0xB47D, 0x63C7, 0xD96B, 0x63C8, 0xD96E, 0x63C9, 0xB47C,\n\t0x63CA, 0xD95C, 0x63CB, 0xD96D, 0x63CC, 0xD96C, 0x63CD, 0xB47E,\n\t0x63CE, 0xD955, 0x63CF, 0xB479, 0x63D0, 0xB4A3, 0x63D2, 0xB4A1,\n\t0x63D3, 0xD969, 0x63D5, 0xD95F, 0x63D6, 0xB4A5, 0x63D7, 0xD970,\n\t0x63D8, 0xD968, 0x63D9, 0xD971, 0x63DA, 0xB4AD, 0x63DB, 0xB4AB,\n\t0x63DC, 0xD966, 0x63DD, 0xD965, 0x63DF, 0xD963, 0x63E0, 0xD95D,\n\t0x63E1, 0xB4A4, 0x63E3, 0xB4A2, 0x63E4, 0xD1B9, 0x63E5, 0xD956,\n\t0x63E7, 0xDDB7, 0x63E8, 0xD957, 0x63E9, 0xB47B, 0x63EA, 0xB4AA,\n\t0x63EB, 0xDD79, 0x63ED, 0xB4A6, 0x63EE, 0xB4A7, 0x63EF, 0xD958,\n\t0x63F0, 0xD96F, 0x63F1, 0xDD78, 0x63F2, 0xD960, 0x63F3, 0xD95B,\n\t0x63F4, 0xB4A9, 0x63F5, 0xD961, 0x63F6, 0xD95E, 0x63F9, 0xB4AE,\n\t0x6406, 0xB770, 0x6409, 0xDD7C, 0x640A, 0xDDB1, 0x640B, 0xDDB6,\n\t0x640C, 0xDDAA, 0x640D, 0xB76C, 0x640E, 0xDDBB, 0x640F, 0xB769,\n\t0x6410, 0xDD7A, 0x6412, 0xDD7B, 0x6413, 0xB762, 0x6414, 0xB76B,\n\t0x6415, 0xDDA4, 0x6416, 0xB76E, 0x6417, 0xB76F, 0x6418, 0xDDA5,\n\t0x641A, 0xDDB2, 0x641B, 0xDDB8, 0x641C, 0xB76A, 0x641E, 0xB764,\n\t0x641F, 0xDDA3, 0x6420, 0xDD7D, 0x6421, 0xDDBA, 0x6422, 0xDDA8,\n\t0x6423, 0xDDA9, 0x6424, 0xDD7E, 0x6425, 0xDDB4, 0x6426, 0xDDAB,\n\t0x6427, 0xDDB5, 0x6428, 0xDDAD, 0x642A, 0xB765, 0x642B, 0xE1D9,\n\t0x642C, 0xB768, 0x642D, 0xB766, 0x642E, 0xDDB9, 0x642F, 0xDDB0,\n\t0x6430, 0xDDAC, 0x6433, 0xDDA1, 0x6434, 0xBA53, 0x6435, 0xDDAF,\n\t0x6436, 0xB76D, 0x6437, 0xDDA7, 0x6439, 0xDDA6, 0x643D, 0xB767,\n\t0x643E, 0xB763, 0x643F, 0xE1EE, 0x6440, 0xDDB3, 0x6441, 0xDDAE,\n\t0x6443, 0xDDA2, 0x644B, 0xE1E9, 0x644D, 0xE1DA, 0x644E, 0xE1E5,\n\t0x6450, 0xE1EC, 0x6451, 0xBA51, 0x6452, 0xB4AC, 0x6453, 0xE1EA,\n\t0x6454, 0xBA4C, 0x6458, 0xBA4B, 0x6459, 0xE1F1, 0x645B, 0xE1DB,\n\t0x645C, 0xE1E8, 0x645D, 0xE1DC, 0x645E, 0xE1E7, 0x645F, 0xBA4F,\n\t0x6460, 0xE1EB, 0x6461, 0xD962, 0x6465, 0xE1F2, 0x6466, 0xE1E3,\n\t0x6467, 0xBA52, 0x6468, 0xE5BA, 0x6469, 0xBCAF, 0x646B, 0xE1F0,\n\t0x646C, 0xE1EF, 0x646D, 0xBA54, 0x646E, 0xE5AD, 0x646F, 0xBCB0,\n\t0x6470, 0xE5AE, 0x6472, 0xE1DF, 0x6473, 0xE1E0, 0x6474, 0xE1DD,\n\t0x6475, 0xE1E2, 0x6476, 0xE1DE, 0x6477, 0xE1F3, 0x6478, 0xBA4E,\n\t0x6479, 0xBCB1, 0x647A, 0xBA50, 0x647B, 0xBA55, 0x647D, 0xE1E1,\n\t0x647F, 0xE1ED, 0x6482, 0xE1E6, 0x6485, 0xE5B1, 0x6487, 0xBA4A,\n\t0x6488, 0xBCB4, 0x6489, 0xE9AA, 0x648A, 0xE5B6, 0x648B, 0xE5B5,\n\t0x648C, 0xE5B7, 0x648F, 0xE5B4, 0x6490, 0xBCB5, 0x6492, 0xBCBB,\n\t0x6493, 0xBCB8, 0x6495, 0xBCB9, 0x6496, 0xE5AF, 0x6497, 0xE5B2,\n\t0x6498, 0xE5BC, 0x6499, 0xBCC1, 0x649A, 0xBCBF, 0x649C, 0xE5B3,\n\t0x649D, 0xD95A, 0x649E, 0xBCB2, 0x649F, 0xE5B9, 0x64A0, 0xE5B0,\n\t0x64A2, 0xBCC2, 0x64A3, 0xE5B8, 0x64A4, 0xBA4D, 0x64A5, 0xBCB7,\n\t0x64A6, 0xE1E4, 0x64A9, 0xBCBA, 0x64AB, 0xBCBE, 0x64AC, 0xBCC0,\n\t0x64AD, 0xBCBD, 0x64AE, 0xBCBC, 0x64B0, 0xBCB6, 0x64B1, 0xE5BB,\n\t0x64B2, 0xBCB3, 0x64B3, 0xBCC3, 0x64BB, 0xBED8, 0x64BC, 0xBED9,\n\t0x64BD, 0xE9A9, 0x64BE, 0xBEE2, 0x64BF, 0xBEDF, 0x64C1, 0xBED6,\n\t0x64C2, 0xBEDD, 0x64C3, 0xE9AB, 0x64C4, 0xBEDB, 0x64C5, 0xBED5,\n\t0x64C7, 0xBEDC, 0x64C9, 0xE9A8, 0x64CA, 0xC0BB, 0x64CB, 0xBED7,\n\t0x64CD, 0xBEDE, 0x64CE, 0xC0BA, 0x64CF, 0xE9A7, 0x64D0, 0xE9A6,\n\t0x64D2, 0xBEE0, 0x64D4, 0xBEE1, 0x64D6, 0xE9A5, 0x64D7, 0xE9A4,\n\t0x64D8, 0xC0BC, 0x64D9, 0xE9AE, 0x64DA, 0xBEDA, 0x64DB, 0xE9AC,\n\t0x64E0, 0xC0BD, 0x64E2, 0xC0C2, 0x64E3, 0xECEA, 0x64E4, 0xECEC,\n\t0x64E6, 0xC0BF, 0x64E8, 0xECED, 0x64E9, 0xECE9, 0x64EB, 0xECEB,\n\t0x64EC, 0xC0C0, 0x64ED, 0xC0C3, 0x64EF, 0xECE8, 0x64F0, 0xC0BE,\n\t0x64F1, 0xC0C1, 0x64F2, 0xC259, 0x64F3, 0xE9AD, 0x64F4, 0xC258,\n\t0x64F7, 0xC25E, 0x64F8, 0xEFD4, 0x64FA, 0xC25C, 0x64FB, 0xC25D,\n\t0x64FC, 0xEFD7, 0x64FD, 0xEFD3, 0x64FE, 0xC25A, 0x64FF, 0xEFD1,\n\t0x6500, 0xC36B, 0x6501, 0xEFD5, 0x6503, 0xEFD6, 0x6504, 0xEFD2,\n\t0x6506, 0xC25B, 0x6507, 0xF242, 0x6509, 0xF245, 0x650C, 0xF246,\n\t0x650D, 0xF244, 0x650E, 0xF247, 0x650F, 0xC36C, 0x6510, 0xF243,\n\t0x6513, 0xF44E, 0x6514, 0xC464, 0x6515, 0xF44D, 0x6516, 0xF44C,\n\t0x6517, 0xF44B, 0x6518, 0xC463, 0x6519, 0xC465, 0x651B, 0xF5CD,\n\t0x651C, 0xC4E2, 0x651D, 0xC4E1, 0x6520, 0xF6E1, 0x6521, 0xF6E0,\n\t0x6522, 0xF6E3, 0x6523, 0xC5CB, 0x6524, 0xC575, 0x6525, 0xF7DD,\n\t0x6526, 0xF6E2, 0x6529, 0xF7DC, 0x652A, 0xC5CD, 0x652B, 0xC5CC,\n\t0x652C, 0xC5F3, 0x652D, 0xF8A9, 0x652E, 0xF8EF, 0x652F, 0xA4E4,\n\t0x6532, 0xD972, 0x6533, 0xE9AF, 0x6536, 0xA6AC, 0x6537, 0xCAF7,\n\t0x6538, 0xA7F1, 0x6539, 0xA7EF, 0x653B, 0xA7F0, 0x653D, 0xCCC1,\n\t0x653E, 0xA9F1, 0x653F, 0xAC46, 0x6541, 0xCEE7, 0x6543, 0xCEE8,\n\t0x6545, 0xAC47, 0x6546, 0xD1CE, 0x6548, 0xAEC4, 0x6549, 0xAEC5,\n\t0x654A, 0xD1CD, 0x654F, 0xB1D3, 0x6551, 0xB1CF, 0x6553, 0xD5A7,\n\t0x6554, 0xB1D6, 0x6555, 0xB1D5, 0x6556, 0xB1CE, 0x6557, 0xB1D1,\n\t0x6558, 0xB1D4, 0x6559, 0xB1D0, 0x655C, 0xD976, 0x655D, 0xB1CD,\n\t0x655E, 0xB4AF, 0x6562, 0xB4B1, 0x6563, 0xB4B2, 0x6564, 0xD975,\n\t0x6565, 0xD978, 0x6566, 0xB4B0, 0x6567, 0xD973, 0x6568, 0xD977,\n\t0x656A, 0xD974, 0x656C, 0xB771, 0x656F, 0xDDBC, 0x6572, 0xBA56,\n\t0x6573, 0xE1F4, 0x6574, 0xBEE3, 0x6575, 0xBCC4, 0x6576, 0xE5BD,\n\t0x6577, 0xBCC5, 0x6578, 0xBCC6, 0x6579, 0xE5BF, 0x657A, 0xE5BE,\n\t0x657B, 0xE5C0, 0x657C, 0xE9B1, 0x657F, 0xE9B0, 0x6580, 0xECEF,\n\t0x6581, 0xECEE, 0x6582, 0xC0C4, 0x6583, 0xC0C5, 0x6584, 0xF248,\n\t0x6587, 0xA4E5, 0x658C, 0xD979, 0x6590, 0xB4B4, 0x6591, 0xB4B3,\n\t0x6592, 0xDDBD, 0x6594, 0xEFD8, 0x6595, 0xC4E3, 0x6596, 0xF7DE,\n\t0x6597, 0xA4E6, 0x6599, 0xAEC6, 0x659B, 0xB1D8, 0x659C, 0xB1D7,\n\t0x659D, 0xD97A, 0x659E, 0xD97B, 0x659F, 0xB772, 0x65A0, 0xE1F5,\n\t0x65A1, 0xBA57, 0x65A2, 0xE9B2, 0x65A4, 0xA4E7, 0x65A5, 0xA5B8,\n\t0x65A7, 0xA9F2, 0x65A8, 0xCCC2, 0x65AA, 0xCEE9, 0x65AB, 0xAC48,\n\t0x65AC, 0xB1D9, 0x65AE, 0xD97C, 0x65AF, 0xB4B5, 0x65B0, 0xB773,\n\t0x65B2, 0xE5C1, 0x65B3, 0xE5C2, 0x65B6, 0xECF0, 0x65B7, 0xC25F,\n\t0x65B8, 0xF8F0, 0x65B9, 0xA4E8, 0x65BB, 0xCCC3, 0x65BC, 0xA9F3,\n\t0x65BD, 0xAC49, 0x65BF, 0xCEEA, 0x65C1, 0xAEC7, 0x65C2, 0xD1D2,\n\t0x65C3, 0xD1D0, 0x65C4, 0xD1D1, 0x65C5, 0xAEC8, 0x65C6, 0xD1CF,\n\t0x65CB, 0xB1DB, 0x65CC, 0xB1DC, 0x65CD, 0xD5A8, 0x65CE, 0xB1DD,\n\t0x65CF, 0xB1DA, 0x65D0, 0xD97D, 0x65D2, 0xD97E, 0x65D3, 0xDDBE,\n\t0x65D6, 0xBA59, 0x65D7, 0xBA58, 0x65DA, 0xECF1, 0x65DB, 0xEFD9,\n\t0x65DD, 0xF24A, 0x65DE, 0xF249, 0x65DF, 0xF44F, 0x65E1, 0xC95E,\n\t0x65E2, 0xAC4A, 0x65E5, 0xA4E9, 0x65E6, 0xA5B9, 0x65E8, 0xA6AE,\n\t0x65E9, 0xA6AD, 0x65EC, 0xA6AF, 0x65ED, 0xA6B0, 0x65EE, 0xC9EE,\n\t0x65EF, 0xC9ED, 0x65F0, 0xCAF8, 0x65F1, 0xA7F2, 0x65F2, 0xCAFB,\n\t0x65F3, 0xCAFA, 0x65F4, 0xCAF9, 0x65F5, 0xCAFC, 0x65FA, 0xA9F4,\n\t0x65FB, 0xCCC9, 0x65FC, 0xCCC5, 0x65FD, 0xCCCE, 0x6600, 0xA9FB,\n\t0x6602, 0xA9F9, 0x6603, 0xCCCA, 0x6604, 0xCCC6, 0x6605, 0xCCCD,\n\t0x6606, 0xA9F8, 0x6607, 0xAA40, 0x6608, 0xCCC8, 0x6609, 0xCCC4,\n\t0x660A, 0xA9FE, 0x660B, 0xCCCB, 0x660C, 0xA9F7, 0x660D, 0xCCCC,\n\t0x660E, 0xA9FA, 0x660F, 0xA9FC, 0x6610, 0xCCD0, 0x6611, 0xCCCF,\n\t0x6612, 0xCCC7, 0x6613, 0xA9F6, 0x6614, 0xA9F5, 0x6615, 0xA9FD,\n\t0x661C, 0xCEEF, 0x661D, 0xCEF5, 0x661F, 0xAC50, 0x6620, 0xAC4D,\n\t0x6621, 0xCEEC, 0x6622, 0xCEF1, 0x6624, 0xAC53, 0x6625, 0xAC4B,\n\t0x6626, 0xCEF0, 0x6627, 0xAC4E, 0x6628, 0xAC51, 0x662B, 0xCEF3,\n\t0x662D, 0xAC4C, 0x662E, 0xCEF8, 0x662F, 0xAC4F, 0x6631, 0xAC52,\n\t0x6632, 0xCEED, 0x6633, 0xCEF2, 0x6634, 0xCEF6, 0x6635, 0xCEEE,\n\t0x6636, 0xCEEB, 0x6639, 0xCEF7, 0x663A, 0xCEF4, 0x6641, 0xAED0,\n\t0x6642, 0xAEC9, 0x6643, 0xAECC, 0x6645, 0xAECF, 0x6647, 0xD1D5,\n\t0x6649, 0xAECA, 0x664A, 0xD1D3, 0x664C, 0xAECE, 0x664F, 0xAECB,\n\t0x6651, 0xD1D6, 0x6652, 0xAECD, 0x6659, 0xD5AC, 0x665A, 0xB1DF,\n\t0x665B, 0xD5AB, 0x665C, 0xD5AD, 0x665D, 0xB1DE, 0x665E, 0xB1E3,\n\t0x665F, 0xD1D4, 0x6661, 0xD5AA, 0x6662, 0xD5AE, 0x6664, 0xB1E0,\n\t0x6665, 0xD5A9, 0x6666, 0xB1E2, 0x6668, 0xB1E1, 0x666A, 0xD9A7,\n\t0x666C, 0xD9A2, 0x666E, 0xB4B6, 0x666F, 0xB4BA, 0x6670, 0xB4B7,\n\t0x6671, 0xD9A5, 0x6672, 0xD9A8, 0x6674, 0xB4B8, 0x6676, 0xB4B9,\n\t0x6677, 0xB4BE, 0x6678, 0xDDC7, 0x6679, 0xD9A6, 0x667A, 0xB4BC,\n\t0x667B, 0xD9A3, 0x667C, 0xD9A1, 0x667E, 0xB4BD, 0x6680, 0xD9A4,\n\t0x6684, 0xB779, 0x6686, 0xDDBF, 0x6687, 0xB776, 0x6688, 0xB777,\n\t0x6689, 0xB775, 0x668A, 0xDDC4, 0x668B, 0xDDC3, 0x668C, 0xDDC0,\n\t0x668D, 0xB77B, 0x6690, 0xDDC2, 0x6691, 0xB4BB, 0x6694, 0xDDC6,\n\t0x6695, 0xDDC1, 0x6696, 0xB778, 0x6697, 0xB774, 0x6698, 0xB77A,\n\t0x6699, 0xDDC5, 0x669D, 0xBA5C, 0x669F, 0xE1F8, 0x66A0, 0xE1F7,\n\t0x66A1, 0xE1F6, 0x66A2, 0xBA5A, 0x66A8, 0xBA5B, 0x66A9, 0xE5C5,\n\t0x66AA, 0xE5C8, 0x66AB, 0xBCC8, 0x66AE, 0xBCC7, 0x66AF, 0xE5C9,\n\t0x66B0, 0xE5C4, 0x66B1, 0xBCCA, 0x66B2, 0xE5C6, 0x66B4, 0xBCC9,\n\t0x66B5, 0xE5C3, 0x66B7, 0xE5C7, 0x66B8, 0xBEE9, 0x66B9, 0xBEE6,\n\t0x66BA, 0xE9BB, 0x66BB, 0xE9BA, 0x66BD, 0xE9B9, 0x66BE, 0xE9B4,\n\t0x66C0, 0xE9B5, 0x66C4, 0xBEE7, 0x66C6, 0xBEE4, 0x66C7, 0xBEE8,\n\t0x66C8, 0xE9B3, 0x66C9, 0xBEE5, 0x66CA, 0xE9B6, 0x66CB, 0xE9B7,\n\t0x66CC, 0xE9BC, 0x66CF, 0xE9B8, 0x66D2, 0xECF2, 0x66D6, 0xC0C7,\n\t0x66D8, 0xEFDC, 0x66D9, 0xC0C6, 0x66DA, 0xEFDA, 0x66DB, 0xEFDB,\n\t0x66DC, 0xC260, 0x66DD, 0xC36E, 0x66DE, 0xF24B, 0x66E0, 0xC36D,\n\t0x66E3, 0xF451, 0x66E4, 0xF452, 0x66E6, 0xC466, 0x66E8, 0xF450,\n\t0x66E9, 0xC4E4, 0x66EB, 0xF7DF, 0x66EC, 0xC5CE, 0x66ED, 0xF8AA,\n\t0x66EE, 0xF8AB, 0x66F0, 0xA4EA, 0x66F2, 0xA6B1, 0x66F3, 0xA6B2,\n\t0x66F4, 0xA7F3, 0x66F6, 0xCCD1, 0x66F7, 0xAC54, 0x66F8, 0xAED1,\n\t0x66F9, 0xB1E4, 0x66FC, 0xB0D2, 0x66FE, 0xB4BF, 0x66FF, 0xB4C0,\n\t0x6700, 0xB3CC, 0x6701, 0xD9A9, 0x6703, 0xB77C, 0x6704, 0xE1FA,\n\t0x6705, 0xE1F9, 0x6708, 0xA4EB, 0x6709, 0xA6B3, 0x670A, 0xCCD2,\n\t0x670B, 0xAA42, 0x670D, 0xAA41, 0x670F, 0xCEF9, 0x6710, 0xCEFA,\n\t0x6712, 0xD1D7, 0x6713, 0xD1D8, 0x6714, 0xAED2, 0x6715, 0xAED3,\n\t0x6717, 0xAED4, 0x6718, 0xD5AF, 0x671B, 0xB1E6, 0x671D, 0xB4C2,\n\t0x671F, 0xB4C1, 0x6720, 0xDDC8, 0x6721, 0xDF7A, 0x6722, 0xE1FB,\n\t0x6723, 0xE9BD, 0x6726, 0xC261, 0x6727, 0xC467, 0x6728, 0xA4EC,\n\t0x672A, 0xA5BC, 0x672B, 0xA5BD, 0x672C, 0xA5BB, 0x672D, 0xA5BE,\n\t0x672E, 0xA5BA, 0x6731, 0xA6B6, 0x6733, 0xC9F6, 0x6734, 0xA6B5,\n\t0x6735, 0xA6B7, 0x6738, 0xC9F1, 0x6739, 0xC9F0, 0x673A, 0xC9F3,\n\t0x673B, 0xC9F2, 0x673C, 0xC9F5, 0x673D, 0xA6B4, 0x673E, 0xC9EF,\n\t0x673F, 0xC9F4, 0x6745, 0xCAFD, 0x6746, 0xA7FD, 0x6747, 0xCAFE,\n\t0x6748, 0xCB43, 0x6749, 0xA7FC, 0x674B, 0xCB47, 0x674C, 0xCB42,\n\t0x674D, 0xCB45, 0x674E, 0xA7F5, 0x674F, 0xA7F6, 0x6750, 0xA7F7,\n\t0x6751, 0xA7F8, 0x6753, 0xA840, 0x6755, 0xCB41, 0x6756, 0xA7FA,\n\t0x6757, 0xA841, 0x6759, 0xCB40, 0x675A, 0xCB46, 0x675C, 0xA7F9,\n\t0x675D, 0xCB44, 0x675E, 0xA7FB, 0x675F, 0xA7F4, 0x6760, 0xA7FE,\n\t0x676A, 0xAA57, 0x676C, 0xCCD4, 0x676D, 0xAA43, 0x676F, 0xAA4D,\n\t0x6770, 0xAA4E, 0x6771, 0xAA46, 0x6772, 0xAA58, 0x6773, 0xAA48,\n\t0x6774, 0xCCDC, 0x6775, 0xAA53, 0x6776, 0xCCD7, 0x6777, 0xAA49,\n\t0x6778, 0xCCE6, 0x6779, 0xCCE7, 0x677A, 0xCCDF, 0x677B, 0xCCD8,\n\t0x677C, 0xAA56, 0x677D, 0xCCE4, 0x677E, 0xAA51, 0x677F, 0xAA4F,\n\t0x6781, 0xCCE5, 0x6783, 0xCCE3, 0x6784, 0xCCDB, 0x6785, 0xCCD3,\n\t0x6786, 0xCCDA, 0x6787, 0xAA4A, 0x6789, 0xAA50, 0x678B, 0xAA44,\n\t0x678C, 0xCCDE, 0x678D, 0xCCDD, 0x678E, 0xCCD5, 0x6790, 0xAA52,\n\t0x6791, 0xCCE1, 0x6792, 0xCCD6, 0x6793, 0xAA55, 0x6794, 0xCCE8,\n\t0x6795, 0xAA45, 0x6797, 0xAA4C, 0x6798, 0xCCD9, 0x6799, 0xCCE2,\n\t0x679A, 0xAA54, 0x679C, 0xAA47, 0x679D, 0xAA4B, 0x679F, 0xCCE0,\n\t0x67AE, 0xCF5B, 0x67AF, 0xAC5C, 0x67B0, 0xAC69, 0x67B2, 0xCF56,\n\t0x67B3, 0xCF4C, 0x67B4, 0xAC62, 0x67B5, 0xCF4A, 0x67B6, 0xAC5B,\n\t0x67B7, 0xCF45, 0x67B8, 0xAC65, 0x67B9, 0xCF52, 0x67BA, 0xCEFE,\n\t0x67BB, 0xCF41, 0x67C0, 0xCF44, 0x67C1, 0xCEFB, 0x67C2, 0xCF51,\n\t0x67C3, 0xCF61, 0x67C4, 0xAC60, 0x67C5, 0xCF46, 0x67C6, 0xCF58,\n\t0x67C8, 0xCEFD, 0x67C9, 0xCF5F, 0x67CA, 0xCF60, 0x67CB, 0xCF63,\n\t0x67CC, 0xCF5A, 0x67CD, 0xCF4B, 0x67CE, 0xCF53, 0x67CF, 0xAC66,\n\t0x67D0, 0xAC59, 0x67D1, 0xAC61, 0x67D2, 0xAC6D, 0x67D3, 0xAC56,\n\t0x67D4, 0xAC58, 0x67D8, 0xCF43, 0x67D9, 0xAC6A, 0x67DA, 0xAC63,\n\t0x67DB, 0xCF5D, 0x67DC, 0xCF40, 0x67DD, 0xAC6C, 0x67DE, 0xAC67,\n\t0x67DF, 0xCF49, 0x67E2, 0xAC6B, 0x67E3, 0xCF50, 0x67E4, 0xCF48,\n\t0x67E5, 0xAC64, 0x67E6, 0xCF5C, 0x67E7, 0xCF54, 0x67E9, 0xAC5E,\n\t0x67EA, 0xCF62, 0x67EB, 0xCF47, 0x67EC, 0xAC5A, 0x67ED, 0xCF59,\n\t0x67EE, 0xCF4F, 0x67EF, 0xAC5F, 0x67F0, 0xCF55, 0x67F1, 0xAC57,\n\t0x67F2, 0xCEFC, 0x67F3, 0xAC68, 0x67F4, 0xAEE3, 0x67F5, 0xAC5D,\n\t0x67F6, 0xCF4E, 0x67F7, 0xCF4D, 0x67F8, 0xCF42, 0x67FA, 0xCF5E,\n\t0x67FC, 0xCF57, 0x67FF, 0xAC55, 0x6812, 0xD1EC, 0x6813, 0xAEEA,\n\t0x6814, 0xD1ED, 0x6816, 0xD1E1, 0x6817, 0xAEDF, 0x6818, 0xAEEB,\n\t0x681A, 0xD1DA, 0x681C, 0xD1E3, 0x681D, 0xD1EB, 0x681F, 0xD1D9,\n\t0x6820, 0xD1F4, 0x6821, 0xAED5, 0x6825, 0xD1F3, 0x6826, 0xD1EE,\n\t0x6828, 0xD1EF, 0x6829, 0xAEDD, 0x682A, 0xAEE8, 0x682B, 0xD1E5,\n\t0x682D, 0xD1E6, 0x682E, 0xD1F0, 0x682F, 0xD1E7, 0x6831, 0xD1E2,\n\t0x6832, 0xD1DC, 0x6833, 0xD1DD, 0x6834, 0xD1EA, 0x6835, 0xD1E4,\n\t0x6838, 0xAED6, 0x6839, 0xAEDA, 0x683A, 0xD1F2, 0x683B, 0xD1DE,\n\t0x683C, 0xAEE6, 0x683D, 0xAEE2, 0x6840, 0xAEE5, 0x6841, 0xAEEC,\n\t0x6842, 0xAEDB, 0x6843, 0xAEE7, 0x6844, 0xD1E9, 0x6845, 0xAEE9,\n\t0x6846, 0xAED8, 0x6848, 0xAED7, 0x6849, 0xD1DB, 0x684B, 0xD1DF,\n\t0x684C, 0xAEE0, 0x684D, 0xD1F1, 0x684E, 0xD1E8, 0x684F, 0xD1E0,\n\t0x6850, 0xAEE4, 0x6851, 0xAEE1, 0x6853, 0xAED9, 0x6854, 0xAEDC,\n\t0x686B, 0xD5C4, 0x686D, 0xD5B4, 0x686E, 0xD5B5, 0x686F, 0xD5B9,\n\t0x6871, 0xD5C8, 0x6872, 0xD5C5, 0x6874, 0xD5BE, 0x6875, 0xD5BD,\n\t0x6876, 0xB1ED, 0x6877, 0xD5C1, 0x6878, 0xD5D0, 0x6879, 0xD5B0,\n\t0x687B, 0xD5D1, 0x687C, 0xD5C3, 0x687D, 0xD5D5, 0x687E, 0xD5C9,\n\t0x687F, 0xB1EC, 0x6880, 0xD5C7, 0x6881, 0xB1E7, 0x6882, 0xB1FC,\n\t0x6883, 0xB1F2, 0x6885, 0xB1F6, 0x6886, 0xB1F5, 0x6887, 0xD5B1,\n\t0x6889, 0xD5CE, 0x688A, 0xD5D4, 0x688B, 0xD5CC, 0x688C, 0xD5D3,\n\t0x688F, 0xD5C0, 0x6890, 0xD5B2, 0x6891, 0xD5D2, 0x6892, 0xD5C2,\n\t0x6893, 0xB1EA, 0x6894, 0xB1F7, 0x6896, 0xD5CB, 0x6897, 0xB1F0,\n\t0x689B, 0xD5CA, 0x689C, 0xD5B3, 0x689D, 0xB1F8, 0x689F, 0xB1FA,\n\t0x68A0, 0xD5CD, 0x68A1, 0xB1FB, 0x68A2, 0xB1E9, 0x68A3, 0xD5BA,\n\t0x68A4, 0xD5CF, 0x68A7, 0xB1EF, 0x68A8, 0xB1F9, 0x68A9, 0xD5BC,\n\t0x68AA, 0xD5C6, 0x68AB, 0xD5B7, 0x68AC, 0xD5BB, 0x68AD, 0xB1F4,\n\t0x68AE, 0xD5B6, 0x68AF, 0xB1E8, 0x68B0, 0xB1F1, 0x68B1, 0xB1EE,\n\t0x68B2, 0xD5BF, 0x68B3, 0xAEDE, 0x68B4, 0xD9C0, 0x68B5, 0xB1EB,\n\t0x68C4, 0xB1F3, 0x68C6, 0xD9C3, 0x68C7, 0xD9D9, 0x68C8, 0xD9CE,\n\t0x68C9, 0xB4D6, 0x68CB, 0xB4D1, 0x68CC, 0xD9BD, 0x68CD, 0xB4D2,\n\t0x68CE, 0xD9CD, 0x68D0, 0xD9C6, 0x68D1, 0xD9D3, 0x68D2, 0xB4CE,\n\t0x68D3, 0xD9AB, 0x68D4, 0xD9D5, 0x68D5, 0xB4C4, 0x68D6, 0xD9B3,\n\t0x68D7, 0xB4C7, 0x68D8, 0xB4C6, 0x68DA, 0xB4D7, 0x68DC, 0xD9AD,\n\t0x68DD, 0xD9CF, 0x68DE, 0xD9D0, 0x68DF, 0xB4C9, 0x68E0, 0xB4C5,\n\t0x68E1, 0xD9BB, 0x68E3, 0xB4D0, 0x68E4, 0xD9B6, 0x68E6, 0xD9D1,\n\t0x68E7, 0xB4CC, 0x68E8, 0xD9C9, 0x68E9, 0xD9D6, 0x68EA, 0xD9B0,\n\t0x68EB, 0xD9B5, 0x68EC, 0xD9AF, 0x68EE, 0xB4CB, 0x68EF, 0xD9C2,\n\t0x68F0, 0xDDDE, 0x68F1, 0xD9B1, 0x68F2, 0xB4CF, 0x68F3, 0xD9BA,\n\t0x68F4, 0xD9D2, 0x68F5, 0xB4CA, 0x68F6, 0xD9B7, 0x68F7, 0xD9B4,\n\t0x68F8, 0xD9C5, 0x68F9, 0xB4CD, 0x68FA, 0xB4C3, 0x68FB, 0xB4D9,\n\t0x68FC, 0xD9C8, 0x68FD, 0xD9C7, 0x6904, 0xD9AC, 0x6905, 0xB4C8,\n\t0x6906, 0xD9D4, 0x6907, 0xD9BC, 0x6908, 0xD9BE, 0x690A, 0xD9CB,\n\t0x690B, 0xD9CA, 0x690C, 0xD9AA, 0x690D, 0xB4D3, 0x690E, 0xB4D5,\n\t0x690F, 0xD9B2, 0x6910, 0xD9B9, 0x6911, 0xD9C1, 0x6912, 0xB4D4,\n\t0x6913, 0xD9B8, 0x6914, 0xD9C4, 0x6915, 0xD9D7, 0x6917, 0xD9CC,\n\t0x6925, 0xD9D8, 0x692A, 0xD9AE, 0x692F, 0xDDF2, 0x6930, 0xB7A6,\n\t0x6932, 0xDDF0, 0x6933, 0xDDDB, 0x6934, 0xDDE0, 0x6935, 0xDDD9,\n\t0x6937, 0xDDEC, 0x6938, 0xDDCB, 0x6939, 0xDDD2, 0x693B, 0xDDEA,\n\t0x693C, 0xDDF4, 0x693D, 0xDDDC, 0x693F, 0xDDCF, 0x6940, 0xDDE2,\n\t0x6941, 0xDDE7, 0x6942, 0xDDD3, 0x6944, 0xDDE4, 0x6945, 0xDDD0,\n\t0x6948, 0xDDD7, 0x6949, 0xDDD8, 0x694A, 0xB7A8, 0x694B, 0xDDEB,\n\t0x694C, 0xDDE9, 0x694E, 0xDDCC, 0x694F, 0xDDEE, 0x6951, 0xDDEF,\n\t0x6952, 0xDDF1, 0x6953, 0xB7AC, 0x6954, 0xB7A4, 0x6956, 0xD5B8,\n\t0x6957, 0xDDD4, 0x6958, 0xDDE6, 0x6959, 0xDDD5, 0x695A, 0xB7A1,\n\t0x695B, 0xB7B1, 0x695C, 0xDDED, 0x695D, 0xB7AF, 0x695E, 0xB7AB,\n\t0x695F, 0xDDCA, 0x6960, 0xB7A3, 0x6962, 0xDDCD, 0x6963, 0xB7B0,\n\t0x6965, 0xDDDD, 0x6966, 0xDDC9, 0x6968, 0xB7A9, 0x6969, 0xDDE1,\n\t0x696A, 0xDDD1, 0x696B, 0xB7AA, 0x696C, 0xDDDA, 0x696D, 0xB77E,\n\t0x696E, 0xB4D8, 0x696F, 0xDDE3, 0x6970, 0xD9BF, 0x6971, 0xDDCE,\n\t0x6974, 0xDDE8, 0x6975, 0xB7A5, 0x6976, 0xDDE5, 0x6977, 0xB7A2,\n\t0x6978, 0xDDDF, 0x6979, 0xB7AD, 0x697A, 0xDDD6, 0x697B, 0xDDF3,\n\t0x6982, 0xB7A7, 0x6983, 0xDEC6, 0x6986, 0xB7AE, 0x698D, 0xE24A,\n\t0x698E, 0xE248, 0x6990, 0xE25E, 0x6991, 0xE246, 0x6993, 0xE258,\n\t0x6994, 0xB77D, 0x6995, 0xBA5F, 0x6996, 0xE242, 0x6997, 0xE25D,\n\t0x6999, 0xE247, 0x699A, 0xE255, 0x699B, 0xBA64, 0x699C, 0xBA5D,\n\t0x699E, 0xE25B, 0x69A0, 0xE240, 0x69A1, 0xE25A, 0x69A3, 0xBA6F,\n\t0x69A4, 0xE251, 0x69A5, 0xE261, 0x69A6, 0xBA6D, 0x69A7, 0xE249,\n\t0x69A8, 0xBA5E, 0x69A9, 0xE24B, 0x69AA, 0xE259, 0x69AB, 0xBA67,\n\t0x69AC, 0xE244, 0x69AD, 0xBA6B, 0x69AE, 0xBA61, 0x69AF, 0xE24D,\n\t0x69B0, 0xE243, 0x69B1, 0xE1FC, 0x69B3, 0xE257, 0x69B4, 0xBA68,\n\t0x69B5, 0xE260, 0x69B6, 0xE1FD, 0x69B7, 0xBA65, 0x69B9, 0xE253,\n\t0x69BB, 0xBA66, 0x69BC, 0xE245, 0x69BD, 0xE250, 0x69BE, 0xE24C,\n\t0x69BF, 0xE24E, 0x69C1, 0xBA60, 0x69C2, 0xE25F, 0x69C3, 0xBA6E,\n\t0x69C4, 0xE24F, 0x69C6, 0xE262, 0x69C9, 0xE1FE, 0x69CA, 0xE254,\n\t0x69CB, 0xBA63, 0x69CC, 0xBA6C, 0x69CD, 0xBA6A, 0x69CE, 0xE241,\n\t0x69CF, 0xE256, 0x69D0, 0xBA69, 0x69D3, 0xBA62, 0x69D4, 0xE252,\n\t0x69D9, 0xE25C, 0x69E2, 0xE5D5, 0x69E4, 0xE5D1, 0x69E5, 0xE5CD,\n\t0x69E6, 0xE5E1, 0x69E7, 0xE5DE, 0x69E8, 0xBCCD, 0x69EB, 0xE5E5,\n\t0x69EC, 0xE5D4, 0x69ED, 0xBCD8, 0x69EE, 0xE5DB, 0x69F1, 0xE5D0,\n\t0x69F2, 0xE5DA, 0x69F3, 0xBCD5, 0x69F4, 0xE5EE, 0x69F6, 0xE5EB,\n\t0x69F7, 0xE5DD, 0x69F8, 0xE5CE, 0x69FB, 0xE5E2, 0x69FC, 0xE5E4,\n\t0x69FD, 0xBCD1, 0x69FE, 0xE5D8, 0x69FF, 0xE5D3, 0x6A00, 0xE5CA,\n\t0x6A01, 0xBCCE, 0x6A02, 0xBCD6, 0x6A04, 0xE5E7, 0x6A05, 0xBCD7,\n\t0x6A06, 0xE5CB, 0x6A07, 0xE5ED, 0x6A08, 0xE5E0, 0x6A09, 0xE5E6,\n\t0x6A0A, 0xBCD4, 0x6A0D, 0xE5E3, 0x6A0F, 0xE5EA, 0x6A11, 0xBCD9,\n\t0x6A13, 0xBCD3, 0x6A14, 0xE5DC, 0x6A15, 0xE5CF, 0x6A16, 0xE5EF,\n\t0x6A17, 0xE5CC, 0x6A18, 0xE5E8, 0x6A19, 0xBCD0, 0x6A1B, 0xE5D6,\n\t0x6A1D, 0xE5D7, 0x6A1E, 0xBCCF, 0x6A1F, 0xBCCC, 0x6A20, 0xE5D2,\n\t0x6A21, 0xBCD2, 0x6A23, 0xBCCB, 0x6A25, 0xE5E9, 0x6A26, 0xE5EC,\n\t0x6A27, 0xE5D9, 0x6A28, 0xE9CA, 0x6A32, 0xE9C2, 0x6A34, 0xE9BE,\n\t0x6A35, 0xBEF6, 0x6A38, 0xBEEB, 0x6A39, 0xBEF0, 0x6A3A, 0xBEEC,\n\t0x6A3B, 0xE9CC, 0x6A3C, 0xE9D7, 0x6A3D, 0xBEEA, 0x6A3E, 0xE9C4,\n\t0x6A3F, 0xE9CD, 0x6A40, 0xE5DF, 0x6A41, 0xE9CE, 0x6A44, 0xBEF1,\n\t0x6A46, 0xE9DD, 0x6A47, 0xBEF5, 0x6A48, 0xBEF8, 0x6A49, 0xE9C0,\n\t0x6A4B, 0xBEF4, 0x6A4D, 0xE9DB, 0x6A4E, 0xE9DC, 0x6A4F, 0xE9D2,\n\t0x6A50, 0xE9D1, 0x6A51, 0xE9C9, 0x6A54, 0xE9D3, 0x6A55, 0xE9DA,\n\t0x6A56, 0xE9D9, 0x6A58, 0xBEEF, 0x6A59, 0xBEED, 0x6A5A, 0xE9CB,\n\t0x6A5B, 0xE9C8, 0x6A5D, 0xE9C5, 0x6A5E, 0xE9D8, 0x6A5F, 0xBEF7,\n\t0x6A60, 0xE9D6, 0x6A61, 0xBEF3, 0x6A62, 0xBEF2, 0x6A64, 0xE9D0,\n\t0x6A66, 0xE9BF, 0x6A67, 0xE9C1, 0x6A68, 0xE9C3, 0x6A69, 0xE9D5,\n\t0x6A6A, 0xE9CF, 0x6A6B, 0xBEEE, 0x6A6D, 0xE9C6, 0x6A6F, 0xE9D4,\n\t0x6A76, 0xE9C7, 0x6A7E, 0xC0CF, 0x6A7F, 0xED45, 0x6A80, 0xC0C8,\n\t0x6A81, 0xECF5, 0x6A83, 0xED41, 0x6A84, 0xC0CA, 0x6A85, 0xED48,\n\t0x6A87, 0xECFC, 0x6A89, 0xECF7, 0x6A8C, 0xED49, 0x6A8D, 0xECF3,\n\t0x6A8E, 0xECFE, 0x6A90, 0xC0D1, 0x6A91, 0xED44, 0x6A92, 0xED4A,\n\t0x6A93, 0xECFD, 0x6A94, 0xC0C9, 0x6A95, 0xED40, 0x6A96, 0xECF4,\n\t0x6A97, 0xC0D0, 0x6A9A, 0xED47, 0x6A9B, 0xECF9, 0x6A9C, 0xC0CC,\n\t0x6A9E, 0xECFB, 0x6A9F, 0xECF8, 0x6AA0, 0xC0D2, 0x6AA1, 0xECFA,\n\t0x6AA2, 0xC0CB, 0x6AA3, 0xC0CE, 0x6AA4, 0xED43, 0x6AA5, 0xECF6,\n\t0x6AA6, 0xED46, 0x6AA8, 0xED42, 0x6AAC, 0xC263, 0x6AAD, 0xEFE7,\n\t0x6AAE, 0xC268, 0x6AAF, 0xC269, 0x6AB3, 0xC262, 0x6AB4, 0xEFE6,\n\t0x6AB6, 0xEFE3, 0x6AB7, 0xEFE4, 0x6AB8, 0xC266, 0x6AB9, 0xEFDE,\n\t0x6ABA, 0xEFE2, 0x6ABB, 0xC265, 0x6ABD, 0xEFDF, 0x6AC2, 0xC267,\n\t0x6AC3, 0xC264, 0x6AC5, 0xEFDD, 0x6AC6, 0xEFE1, 0x6AC7, 0xEFE5,\n\t0x6ACB, 0xF251, 0x6ACC, 0xF24E, 0x6ACD, 0xF257, 0x6ACF, 0xF256,\n\t0x6AD0, 0xF254, 0x6AD1, 0xF24F, 0x6AD3, 0xC372, 0x6AD9, 0xF250,\n\t0x6ADA, 0xC371, 0x6ADB, 0xC0CD, 0x6ADC, 0xF253, 0x6ADD, 0xC370,\n\t0x6ADE, 0xF258, 0x6ADF, 0xF252, 0x6AE0, 0xF24D, 0x6AE1, 0xEFE0,\n\t0x6AE5, 0xC36F, 0x6AE7, 0xF24C, 0x6AE8, 0xF456, 0x6AEA, 0xF455,\n\t0x6AEB, 0xF255, 0x6AEC, 0xC468, 0x6AEE, 0xF459, 0x6AEF, 0xF45A,\n\t0x6AF0, 0xF454, 0x6AF1, 0xF458, 0x6AF3, 0xF453, 0x6AF8, 0xF5D1,\n\t0x6AF9, 0xF457, 0x6AFA, 0xC4E7, 0x6AFB, 0xC4E5, 0x6AFC, 0xF5CF,\n\t0x6B00, 0xF5D2, 0x6B02, 0xF5CE, 0x6B03, 0xF5D0, 0x6B04, 0xC4E6,\n\t0x6B08, 0xF6E5, 0x6B09, 0xF6E6, 0x6B0A, 0xC576, 0x6B0B, 0xF6E4,\n\t0x6B0F, 0xF7E2, 0x6B10, 0xC5CF, 0x6B11, 0xF7E0, 0x6B12, 0xF7E1,\n\t0x6B13, 0xF8AC, 0x6B16, 0xC656, 0x6B17, 0xF8F3, 0x6B18, 0xF8F1,\n\t0x6B19, 0xF8F2, 0x6B1A, 0xF8F4, 0x6B1E, 0xF9BB, 0x6B20, 0xA4ED,\n\t0x6B21, 0xA6B8, 0x6B23, 0xAA59, 0x6B25, 0xCCE9, 0x6B28, 0xCF64,\n\t0x6B2C, 0xD1F5, 0x6B2D, 0xD1F7, 0x6B2F, 0xD1F6, 0x6B31, 0xD1F8,\n\t0x6B32, 0xB1FD, 0x6B33, 0xD5D7, 0x6B34, 0xD1F9, 0x6B36, 0xD5D6,\n\t0x6B37, 0xD5D8, 0x6B38, 0xD5D9, 0x6B39, 0xD9DA, 0x6B3A, 0xB4DB,\n\t0x6B3B, 0xD9DB, 0x6B3C, 0xD9DD, 0x6B3D, 0xB4DC, 0x6B3E, 0xB4DA,\n\t0x6B3F, 0xD9DC, 0x6B41, 0xDDFA, 0x6B42, 0xDDF8, 0x6B43, 0xDDF7,\n\t0x6B45, 0xDDF6, 0x6B46, 0xDDF5, 0x6B47, 0xB7B2, 0x6B48, 0xDDF9,\n\t0x6B49, 0xBA70, 0x6B4A, 0xE263, 0x6B4B, 0xE265, 0x6B4C, 0xBA71,\n\t0x6B4D, 0xE264, 0x6B4E, 0xBCDB, 0x6B50, 0xBCDA, 0x6B51, 0xE5F0,\n\t0x6B54, 0xE9DF, 0x6B55, 0xE9DE, 0x6B56, 0xE9E0, 0x6B59, 0xBEF9,\n\t0x6B5B, 0xED4B, 0x6B5C, 0xC0D3, 0x6B5E, 0xEFE8, 0x6B5F, 0xC26A,\n\t0x6B60, 0xF259, 0x6B61, 0xC577, 0x6B62, 0xA4EE, 0x6B63, 0xA5BF,\n\t0x6B64, 0xA6B9, 0x6B65, 0xA842, 0x6B66, 0xAA5A, 0x6B67, 0xAA5B,\n\t0x6B6A, 0xAC6E, 0x6B6D, 0xD1FA, 0x6B72, 0xB7B3, 0x6B76, 0xE6D1,\n\t0x6B77, 0xBEFA, 0x6B78, 0xC26B, 0x6B79, 0xA4EF, 0x6B7B, 0xA6BA,\n\t0x6B7E, 0xCCEB, 0x6B7F, 0xAA5C, 0x6B80, 0xCCEA, 0x6B82, 0xCF65,\n\t0x6B83, 0xAC6F, 0x6B84, 0xCF66, 0x6B86, 0xAC70, 0x6B88, 0xD1FC,\n\t0x6B89, 0xAEEE, 0x6B8A, 0xAEED, 0x6B8C, 0xD5DE, 0x6B8D, 0xD5DC,\n\t0x6B8E, 0xD5DD, 0x6B8F, 0xD5DB, 0x6B91, 0xD5DA, 0x6B94, 0xD9DE,\n\t0x6B95, 0xD9E1, 0x6B96, 0xB4DE, 0x6B97, 0xD9DF, 0x6B98, 0xB4DD,\n\t0x6B99, 0xD9E0, 0x6B9B, 0xDDFB, 0x6B9E, 0xE266, 0x6B9F, 0xE267,\n\t0x6BA0, 0xE268, 0x6BA2, 0xE5F3, 0x6BA3, 0xE5F2, 0x6BA4, 0xBCDC,\n\t0x6BA5, 0xE5F1, 0x6BA6, 0xE5F4, 0x6BA7, 0xE9E1, 0x6BAA, 0xE9E2,\n\t0x6BAB, 0xE9E3, 0x6BAD, 0xED4C, 0x6BAE, 0xC0D4, 0x6BAF, 0xC26C,\n\t0x6BB0, 0xF25A, 0x6BB2, 0xC4E8, 0x6BB3, 0xC95F, 0x6BB5, 0xAC71,\n\t0x6BB6, 0xCF67, 0x6BB7, 0xAEEF, 0x6BBA, 0xB1FE, 0x6BBC, 0xB4DF,\n\t0x6BBD, 0xD9E2, 0x6BBF, 0xB7B5, 0x6BC0, 0xB7B4, 0x6BC3, 0xE269,\n\t0x6BC4, 0xE26A, 0x6BC5, 0xBCDD, 0x6BC6, 0xBCDE, 0x6BC7, 0xE9E5,\n\t0x6BC8, 0xE9E4, 0x6BC9, 0xEFE9, 0x6BCA, 0xF7E3, 0x6BCB, 0xA4F0,\n\t0x6BCC, 0xC960, 0x6BCD, 0xA5C0, 0x6BCF, 0xA843, 0x6BD0, 0xCB48,\n\t0x6BD2, 0xAC72, 0x6BD3, 0xB7B6, 0x6BD4, 0xA4F1, 0x6BD6, 0xCF68,\n\t0x6BD7, 0xAC73, 0x6BD8, 0xCF69, 0x6BDA, 0xC0D5, 0x6BDB, 0xA4F2,\n\t0x6BDE, 0xCCEC, 0x6BE0, 0xCF6A, 0x6BE2, 0xD242, 0x6BE3, 0xD241,\n\t0x6BE4, 0xD1FE, 0x6BE6, 0xD1FD, 0x6BE7, 0xD243, 0x6BE8, 0xD240,\n\t0x6BEB, 0xB240, 0x6BEC, 0xB241, 0x6BEF, 0xB4E0, 0x6BF0, 0xD9E3,\n\t0x6BF2, 0xD9E4, 0x6BF3, 0xD9E5, 0x6BF7, 0xDE41, 0x6BF8, 0xDE42,\n\t0x6BF9, 0xDE40, 0x6BFB, 0xDDFD, 0x6BFC, 0xDDFE, 0x6BFD, 0xB7B7,\n\t0x6BFE, 0xE26B, 0x6BFF, 0xE5F7, 0x6C00, 0xE5F6, 0x6C01, 0xE5F5,\n\t0x6C02, 0xE5F8, 0x6C03, 0xE9E7, 0x6C04, 0xE9E6, 0x6C05, 0xBEFB,\n\t0x6C06, 0xE9E8, 0x6C08, 0xC0D6, 0x6C09, 0xED4D, 0x6C0B, 0xEFEA,\n\t0x6C0C, 0xF25B, 0x6C0D, 0xF6E7, 0x6C0F, 0xA4F3, 0x6C10, 0xA5C2,\n\t0x6C11, 0xA5C1, 0x6C13, 0xAA5D, 0x6C14, 0xC961, 0x6C15, 0xC97E,\n\t0x6C16, 0xA6BB, 0x6C18, 0xC9F7, 0x6C19, 0xCB49, 0x6C1A, 0xCB4A,\n\t0x6C1B, 0xAA5E, 0x6C1D, 0xCCED, 0x6C1F, 0xAC74, 0x6C20, 0xCF6B,\n\t0x6C21, 0xCF6C, 0x6C23, 0xAEF0, 0x6C24, 0xAEF4, 0x6C25, 0xD244,\n\t0x6C26, 0xAEF3, 0x6C27, 0xAEF1, 0x6C28, 0xAEF2, 0x6C2A, 0xD5DF,\n\t0x6C2B, 0xB242, 0x6C2C, 0xB4E3, 0x6C2E, 0xB4E1, 0x6C2F, 0xB4E2,\n\t0x6C30, 0xD9E6, 0x6C33, 0xBA72, 0x6C34, 0xA4F4, 0x6C36, 0xC9A1,\n\t0x6C38, 0xA5C3, 0x6C3B, 0xC9A4, 0x6C3E, 0xA5C6, 0x6C3F, 0xC9A3,\n\t0x6C40, 0xA5C5, 0x6C41, 0xA5C4, 0x6C42, 0xA844, 0x6C43, 0xC9A2,\n\t0x6C46, 0xC9F8, 0x6C4A, 0xC9FC, 0x6C4B, 0xC9FE, 0x6C4C, 0xCA40,\n\t0x6C4D, 0xA6C5, 0x6C4E, 0xA6C6, 0x6C4F, 0xC9FB, 0x6C50, 0xA6C1,\n\t0x6C52, 0xC9F9, 0x6C54, 0xC9FD, 0x6C55, 0xA6C2, 0x6C57, 0xA6BD,\n\t0x6C59, 0xA6BE, 0x6C5B, 0xA6C4, 0x6C5C, 0xC9FA, 0x6C5D, 0xA6BC,\n\t0x6C5E, 0xA845, 0x6C5F, 0xA6BF, 0x6C60, 0xA6C0, 0x6C61, 0xA6C3,\n\t0x6C65, 0xCB5B, 0x6C66, 0xCB59, 0x6C67, 0xCB4C, 0x6C68, 0xA851,\n\t0x6C69, 0xCB53, 0x6C6A, 0xA84C, 0x6C6B, 0xCB4D, 0x6C6D, 0xCB55,\n\t0x6C6F, 0xCB52, 0x6C70, 0xA84F, 0x6C71, 0xCB51, 0x6C72, 0xA856,\n\t0x6C73, 0xCB5A, 0x6C74, 0xA858, 0x6C76, 0xA85A, 0x6C78, 0xCB4B,\n\t0x6C7A, 0xA84D, 0x6C7B, 0xCB5C, 0x6C7D, 0xA854, 0x6C7E, 0xA857,\n\t0x6C80, 0xCD45, 0x6C81, 0xA847, 0x6C82, 0xA85E, 0x6C83, 0xA855,\n\t0x6C84, 0xCB4E, 0x6C85, 0xA84A, 0x6C86, 0xA859, 0x6C87, 0xCB56,\n\t0x6C88, 0xA848, 0x6C89, 0xA849, 0x6C8A, 0xCD43, 0x6C8B, 0xCB4F,\n\t0x6C8C, 0xA850, 0x6C8D, 0xA85B, 0x6C8E, 0xCB5D, 0x6C8F, 0xCB50,\n\t0x6C90, 0xA84E, 0x6C92, 0xA853, 0x6C93, 0xCCEE, 0x6C94, 0xA85C,\n\t0x6C95, 0xCB57, 0x6C96, 0xA852, 0x6C98, 0xA85D, 0x6C99, 0xA846,\n\t0x6C9A, 0xCB54, 0x6C9B, 0xA84B, 0x6C9C, 0xCB58, 0x6C9D, 0xCD44,\n\t0x6CAB, 0xAA6A, 0x6CAC, 0xAA7A, 0x6CAD, 0xCCF5, 0x6CAE, 0xAA71,\n\t0x6CB0, 0xCD4B, 0x6CB1, 0xAA62, 0x6CB3, 0xAA65, 0x6CB4, 0xCD42,\n\t0x6CB6, 0xCCF3, 0x6CB7, 0xCCF7, 0x6CB8, 0xAA6D, 0x6CB9, 0xAA6F,\n\t0x6CBA, 0xCCFA, 0x6CBB, 0xAA76, 0x6CBC, 0xAA68, 0x6CBD, 0xAA66,\n\t0x6CBE, 0xAA67, 0x6CBF, 0xAA75, 0x6CC0, 0xCD47, 0x6CC1, 0xAA70,\n\t0x6CC2, 0xCCF9, 0x6CC3, 0xCCFB, 0x6CC4, 0xAA6E, 0x6CC5, 0xAA73,\n\t0x6CC6, 0xCCFC, 0x6CC7, 0xCD4A, 0x6CC9, 0xAC75, 0x6CCA, 0xAA79,\n\t0x6CCC, 0xAA63, 0x6CCD, 0xCD49, 0x6CCF, 0xCD4D, 0x6CD0, 0xCCF8,\n\t0x6CD1, 0xCD4F, 0x6CD2, 0xCD40, 0x6CD3, 0xAA6C, 0x6CD4, 0xCCF4,\n\t0x6CD5, 0xAA6B, 0x6CD6, 0xAA7D, 0x6CD7, 0xAA72, 0x6CD9, 0xCCF2,\n\t0x6CDA, 0xCF75, 0x6CDB, 0xAA78, 0x6CDC, 0xAA7C, 0x6CDD, 0xCD41,\n\t0x6CDE, 0xCD46, 0x6CE0, 0xAA7E, 0x6CE1, 0xAA77, 0x6CE2, 0xAA69,\n\t0x6CE3, 0xAA5F, 0x6CE5, 0xAA64, 0x6CE7, 0xCCF6, 0x6CE8, 0xAA60,\n\t0x6CE9, 0xCD4E, 0x6CEB, 0xCCF0, 0x6CEC, 0xCCEF, 0x6CED, 0xCCFD,\n\t0x6CEE, 0xCCF1, 0x6CEF, 0xAA7B, 0x6CF0, 0xAEF5, 0x6CF1, 0xAA74,\n\t0x6CF2, 0xCCFE, 0x6CF3, 0xAA61, 0x6CF5, 0xACA6, 0x6CF9, 0xCD4C,\n\t0x6D00, 0xCF7C, 0x6D01, 0xCFA1, 0x6D03, 0xCFA4, 0x6D04, 0xCF77,\n\t0x6D07, 0xCFA7, 0x6D08, 0xCFAA, 0x6D09, 0xCFAC, 0x6D0A, 0xCF74,\n\t0x6D0B, 0xAC76, 0x6D0C, 0xAC7B, 0x6D0D, 0xD249, 0x6D0E, 0xACAD,\n\t0x6D0F, 0xCFA5, 0x6D10, 0xCFAD, 0x6D11, 0xCF7B, 0x6D12, 0xCF73,\n\t0x6D16, 0xD264, 0x6D17, 0xAC7E, 0x6D18, 0xCFA2, 0x6D19, 0xCF78,\n\t0x6D1A, 0xCF7A, 0x6D1B, 0xACA5, 0x6D1D, 0xCF7D, 0x6D1E, 0xAC7D,\n\t0x6D1F, 0xCF70, 0x6D20, 0xCFA8, 0x6D22, 0xCFAB, 0x6D25, 0xAC7A,\n\t0x6D27, 0xACA8, 0x6D28, 0xCF6D, 0x6D29, 0xACAA, 0x6D2A, 0xAC78,\n\t0x6D2B, 0xACAE, 0x6D2C, 0xCFA9, 0x6D2D, 0xCF6F, 0x6D2E, 0xACAB,\n\t0x6D2F, 0xD25E, 0x6D30, 0xCD48, 0x6D31, 0xAC7C, 0x6D32, 0xAC77,\n\t0x6D33, 0xCF76, 0x6D34, 0xCF6E, 0x6D35, 0xACAC, 0x6D36, 0xACA4,\n\t0x6D37, 0xCFA3, 0x6D38, 0xACA9, 0x6D39, 0xACA7, 0x6D3A, 0xCF79,\n\t0x6D3B, 0xACA1, 0x6D3C, 0xCF71, 0x6D3D, 0xACA2, 0x6D3E, 0xACA3,\n\t0x6D3F, 0xCF72, 0x6D40, 0xCFA6, 0x6D41, 0xAC79, 0x6D42, 0xCF7E,\n\t0x6D58, 0xD24C, 0x6D59, 0xAEFD, 0x6D5A, 0xAF43, 0x6D5E, 0xD255,\n\t0x6D5F, 0xD25B, 0x6D60, 0xD257, 0x6D61, 0xD24A, 0x6D62, 0xD24D,\n\t0x6D63, 0xD246, 0x6D64, 0xD247, 0x6D65, 0xAF4A, 0x6D66, 0xAEFA,\n\t0x6D67, 0xD256, 0x6D68, 0xD25F, 0x6D69, 0xAF45, 0x6D6A, 0xAEF6,\n\t0x6D6C, 0xAF40, 0x6D6D, 0xD24E, 0x6D6E, 0xAF42, 0x6D6F, 0xD24F,\n\t0x6D70, 0xD259, 0x6D74, 0xAF44, 0x6D75, 0xD268, 0x6D76, 0xD248,\n\t0x6D77, 0xAEFC, 0x6D78, 0xAEFB, 0x6D79, 0xAF48, 0x6D7A, 0xD245,\n\t0x6D7B, 0xD266, 0x6D7C, 0xD25A, 0x6D7D, 0xD267, 0x6D7E, 0xD261,\n\t0x6D7F, 0xD253, 0x6D80, 0xD262, 0x6D82, 0xD25C, 0x6D83, 0xD265,\n\t0x6D84, 0xD263, 0x6D85, 0xAF49, 0x6D86, 0xD254, 0x6D87, 0xAEF9,\n\t0x6D88, 0xAEF8, 0x6D89, 0xAF41, 0x6D8A, 0xAF47, 0x6D8B, 0xD260,\n\t0x6D8C, 0xAF46, 0x6D8D, 0xD251, 0x6D8E, 0xB243, 0x6D90, 0xD269,\n\t0x6D91, 0xD250, 0x6D92, 0xD24B, 0x6D93, 0xAEFE, 0x6D94, 0xAF4B,\n\t0x6D95, 0xAEF7, 0x6D97, 0xD258, 0x6D98, 0xD25D, 0x6DAA, 0xB265,\n\t0x6DAB, 0xD5E1, 0x6DAC, 0xD5E5, 0x6DAE, 0xB252, 0x6DAF, 0xB250,\n\t0x6DB2, 0xB247, 0x6DB3, 0xD5E3, 0x6DB4, 0xD5E2, 0x6DB5, 0xB25B,\n\t0x6DB7, 0xD5E8, 0x6DB8, 0xB255, 0x6DBA, 0xD5FA, 0x6DBB, 0xD647,\n\t0x6DBC, 0xB244, 0x6DBD, 0xD5F7, 0x6DBE, 0xD5F0, 0x6DBF, 0xB267,\n\t0x6DC0, 0xD5E0, 0x6DC2, 0xD5FC, 0x6DC4, 0xB264, 0x6DC5, 0xB258,\n\t0x6DC6, 0xB263, 0x6DC7, 0xB24E, 0x6DC8, 0xD5EC, 0x6DC9, 0xD5FE,\n\t0x6DCA, 0xD5F6, 0x6DCB, 0xB24F, 0x6DCC, 0xB249, 0x6DCD, 0xD645,\n\t0x6DCF, 0xD5FD, 0x6DD0, 0xD640, 0x6DD1, 0xB251, 0x6DD2, 0xB259,\n\t0x6DD3, 0xD642, 0x6DD4, 0xD5EA, 0x6DD5, 0xD5FB, 0x6DD6, 0xD5EF,\n\t0x6DD7, 0xD644, 0x6DD8, 0xB25E, 0x6DD9, 0xB246, 0x6DDA, 0xB25C,\n\t0x6DDB, 0xD5F4, 0x6DDC, 0xD5F2, 0x6DDD, 0xD5F3, 0x6DDE, 0xB253,\n\t0x6DDF, 0xD5EE, 0x6DE0, 0xD5ED, 0x6DE1, 0xB248, 0x6DE2, 0xD5E7,\n\t0x6DE3, 0xD646, 0x6DE4, 0xB24A, 0x6DE5, 0xD5F1, 0x6DE6, 0xB268,\n\t0x6DE8, 0xB262, 0x6DE9, 0xD5E6, 0x6DEA, 0xB25F, 0x6DEB, 0xB25D,\n\t0x6DEC, 0xB266, 0x6DED, 0xD5F8, 0x6DEE, 0xB261, 0x6DEF, 0xD252,\n\t0x6DF0, 0xD5F9, 0x6DF1, 0xB260, 0x6DF2, 0xD641, 0x6DF3, 0xB245,\n\t0x6DF4, 0xD5F5, 0x6DF5, 0xB257, 0x6DF6, 0xD5E9, 0x6DF7, 0xB256,\n\t0x6DF9, 0xB254, 0x6DFA, 0xB24C, 0x6DFB, 0xB24B, 0x6DFC, 0xD9E7,\n\t0x6DFD, 0xD643, 0x6E00, 0xD5EB, 0x6E03, 0xD9FC, 0x6E05, 0xB24D,\n\t0x6E19, 0xB541, 0x6E1A, 0xB25A, 0x6E1B, 0xB4EE, 0x6E1C, 0xD9F6,\n\t0x6E1D, 0xB4FC, 0x6E1F, 0xD9EA, 0x6E20, 0xB4EB, 0x6E21, 0xB4E7,\n\t0x6E22, 0xDA49, 0x6E23, 0xB4ED, 0x6E24, 0xB4F1, 0x6E25, 0xB4EC,\n\t0x6E26, 0xB4F5, 0x6E27, 0xDA4D, 0x6E28, 0xDA44, 0x6E2B, 0xD9F1,\n\t0x6E2C, 0xB4FA, 0x6E2D, 0xB4F4, 0x6E2E, 0xD9FD, 0x6E2F, 0xB4E4,\n\t0x6E30, 0xDA4A, 0x6E31, 0xDA43, 0x6E32, 0xB4E8, 0x6E33, 0xD9F7,\n\t0x6E34, 0xB4F7, 0x6E35, 0xDA55, 0x6E36, 0xDA56, 0x6E38, 0xB4E5,\n\t0x6E39, 0xDA48, 0x6E3A, 0xB4F9, 0x6E3B, 0xD9FB, 0x6E3C, 0xD9ED,\n\t0x6E3D, 0xD9EE, 0x6E3E, 0xB4FD, 0x6E3F, 0xD9F2, 0x6E40, 0xD9F9,\n\t0x6E41, 0xD9F3, 0x6E43, 0xB4FB, 0x6E44, 0xB544, 0x6E45, 0xD9EF,\n\t0x6E46, 0xD9E8, 0x6E47, 0xD9E9, 0x6E49, 0xD9EB, 0x6E4A, 0xB4EA,\n\t0x6E4B, 0xD9F8, 0x6E4D, 0xB4F8, 0x6E4E, 0xB542, 0x6E51, 0xD9FA,\n\t0x6E52, 0xDA53, 0x6E53, 0xDA4B, 0x6E54, 0xB4E6, 0x6E55, 0xDA51,\n\t0x6E56, 0xB4F2, 0x6E58, 0xB4F0, 0x6E5A, 0xDA57, 0x6E5B, 0xB4EF,\n\t0x6E5C, 0xDA41, 0x6E5D, 0xD9F4, 0x6E5E, 0xD9FE, 0x6E5F, 0xB547,\n\t0x6E60, 0xDA45, 0x6E61, 0xDA42, 0x6E62, 0xD9F0, 0x6E63, 0xB543,\n\t0x6E64, 0xDA4F, 0x6E65, 0xDA4C, 0x6E66, 0xDA54, 0x6E67, 0xB4E9,\n\t0x6E68, 0xDA40, 0x6E69, 0xB546, 0x6E6B, 0xDA47, 0x6E6E, 0xB4F3,\n\t0x6E6F, 0xB4F6, 0x6E71, 0xDA46, 0x6E72, 0xB545, 0x6E73, 0xD9F5,\n\t0x6E74, 0xD5E4, 0x6E77, 0xDA50, 0x6E78, 0xDA4E, 0x6E79, 0xDA52,\n\t0x6E88, 0xD9EC, 0x6E89, 0xB540, 0x6E8D, 0xDE61, 0x6E8E, 0xDE60,\n\t0x6E8F, 0xDE46, 0x6E90, 0xB7BD, 0x6E92, 0xDE5F, 0x6E93, 0xDE49,\n\t0x6E94, 0xDE4A, 0x6E96, 0xB7C7, 0x6E97, 0xDE68, 0x6E98, 0xB7C2,\n\t0x6E99, 0xDE5E, 0x6E9B, 0xDE43, 0x6E9C, 0xB7C8, 0x6E9D, 0xB7BE,\n\t0x6E9E, 0xDE52, 0x6E9F, 0xDE48, 0x6EA0, 0xDE4B, 0x6EA1, 0xDE63,\n\t0x6EA2, 0xB7B8, 0x6EA3, 0xDE6A, 0x6EA4, 0xDE62, 0x6EA5, 0xB7C1,\n\t0x6EA6, 0xDE57, 0x6EA7, 0xB7CC, 0x6EAA, 0xB7CB, 0x6EAB, 0xB7C5,\n\t0x6EAE, 0xDE69, 0x6EAF, 0xB7B9, 0x6EB0, 0xDE55, 0x6EB1, 0xDE4C,\n\t0x6EB2, 0xDE59, 0x6EB3, 0xDE65, 0x6EB4, 0xB7CD, 0x6EB6, 0xB7BB,\n\t0x6EB7, 0xDE54, 0x6EB9, 0xDE4D, 0x6EBA, 0xB7C4, 0x6EBC, 0xB7C3,\n\t0x6EBD, 0xDE50, 0x6EBE, 0xDE5A, 0x6EBF, 0xDE64, 0x6EC0, 0xDE47,\n\t0x6EC1, 0xDE51, 0x6EC2, 0xB7BC, 0x6EC3, 0xDE5B, 0x6EC4, 0xB7C9,\n\t0x6EC5, 0xB7C0, 0x6EC6, 0xDE4E, 0x6EC7, 0xB7BF, 0x6EC8, 0xDE45,\n\t0x6EC9, 0xDE53, 0x6ECA, 0xDE67, 0x6ECB, 0xB4FE, 0x6ECC, 0xBAB0,\n\t0x6ECD, 0xDE56, 0x6ECE, 0xE26C, 0x6ECF, 0xDE58, 0x6ED0, 0xDE66,\n\t0x6ED1, 0xB7C6, 0x6ED2, 0xDE4F, 0x6ED3, 0xB7BA, 0x6ED4, 0xB7CA,\n\t0x6ED5, 0xBCF0, 0x6ED6, 0xDE44, 0x6ED8, 0xDE5D, 0x6EDC, 0xDE5C,\n\t0x6EEB, 0xE2AA, 0x6EEC, 0xBAAD, 0x6EED, 0xE27D, 0x6EEE, 0xE2A4,\n\t0x6EEF, 0xBAA2, 0x6EF1, 0xE26E, 0x6EF2, 0xBAAF, 0x6EF4, 0xBA77,\n\t0x6EF5, 0xE26D, 0x6EF6, 0xE2B0, 0x6EF7, 0xBAB1, 0x6EF8, 0xE271,\n\t0x6EF9, 0xE2A3, 0x6EFB, 0xE273, 0x6EFC, 0xE2B3, 0x6EFD, 0xE2AF,\n\t0x6EFE, 0xBA75, 0x6EFF, 0xBAA1, 0x6F00, 0xE653, 0x6F01, 0xBAAE,\n\t0x6F02, 0xBA7D, 0x6F03, 0xE26F, 0x6F05, 0xE2AE, 0x6F06, 0xBAA3,\n\t0x6F07, 0xE2AB, 0x6F08, 0xE2B8, 0x6F09, 0xE275, 0x6F0A, 0xE27E,\n\t0x6F0D, 0xE2B6, 0x6F0E, 0xE2AC, 0x6F0F, 0xBA7C, 0x6F12, 0xE27C,\n\t0x6F13, 0xBA76, 0x6F14, 0xBA74, 0x6F15, 0xBAA8, 0x6F18, 0xE27A,\n\t0x6F19, 0xE277, 0x6F1A, 0xE278, 0x6F1C, 0xE2B2, 0x6F1E, 0xE2B7,\n\t0x6F1F, 0xE2B5, 0x6F20, 0xBA7A, 0x6F21, 0xE2B9, 0x6F22, 0xBA7E,\n\t0x6F23, 0xBAA7, 0x6F25, 0xE270, 0x6F26, 0xE5FA, 0x6F27, 0xE279,\n\t0x6F29, 0xBA78, 0x6F2A, 0xBAAC, 0x6F2B, 0xBAA9, 0x6F2C, 0xBA7B,\n\t0x6F2D, 0xE2A5, 0x6F2E, 0xE274, 0x6F2F, 0xBAAA, 0x6F30, 0xE2A7,\n\t0x6F31, 0xBAA4, 0x6F32, 0xBAA6, 0x6F33, 0xBA73, 0x6F35, 0xE2A9,\n\t0x6F36, 0xE2A1, 0x6F37, 0xE272, 0x6F38, 0xBAA5, 0x6F39, 0xE2B1,\n\t0x6F3A, 0xE2B4, 0x6F3B, 0xE27B, 0x6F3C, 0xE2A8, 0x6F3E, 0xBA79,\n\t0x6F3F, 0xBCDF, 0x6F40, 0xE2A6, 0x6F41, 0xE5F9, 0x6F43, 0xE2AD,\n\t0x6F4E, 0xE276, 0x6F4F, 0xE644, 0x6F50, 0xE64E, 0x6F51, 0xBCE2,\n\t0x6F52, 0xE64D, 0x6F53, 0xE659, 0x6F54, 0xBCE4, 0x6F55, 0xE64B,\n\t0x6F57, 0xE64F, 0x6F58, 0xBCEF, 0x6F5A, 0xE646, 0x6F5B, 0xBCE7,\n\t0x6F5D, 0xE652, 0x6F5E, 0xE9F0, 0x6F5F, 0xBCF3, 0x6F60, 0xBCF2,\n\t0x6F61, 0xE654, 0x6F62, 0xE643, 0x6F63, 0xE65E, 0x6F64, 0xBCED,\n\t0x6F66, 0xBCE3, 0x6F67, 0xE657, 0x6F69, 0xE65B, 0x6F6A, 0xE660,\n\t0x6F6B, 0xE655, 0x6F6C, 0xE649, 0x6F6D, 0xBCE6, 0x6F6E, 0xBCE9,\n\t0x6F6F, 0xBCF1, 0x6F70, 0xBCEC, 0x6F72, 0xE64C, 0x6F73, 0xE2A2,\n\t0x6F76, 0xE648, 0x6F77, 0xE65F, 0x6F78, 0xBCE8, 0x6F7A, 0xBCEB,\n\t0x6F7B, 0xE661, 0x6F7C, 0xBCE0, 0x6F7D, 0xE656, 0x6F7E, 0xE5FB,\n\t0x6F7F, 0xE65C, 0x6F80, 0xC0DF, 0x6F82, 0xE64A, 0x6F84, 0xBCE1,\n\t0x6F85, 0xE645, 0x6F86, 0xBCE5, 0x6F87, 0xE5FC, 0x6F88, 0xBAAB,\n\t0x6F89, 0xE641, 0x6F8B, 0xE65A, 0x6F8C, 0xE642, 0x6F8D, 0xE640,\n\t0x6F8E, 0xBCEA, 0x6F90, 0xE658, 0x6F92, 0xE5FE, 0x6F93, 0xE651,\n\t0x6F94, 0xE650, 0x6F95, 0xE65D, 0x6F96, 0xE647, 0x6F97, 0xBCEE,\n\t0x6F9E, 0xE9F3, 0x6FA0, 0xBF49, 0x6FA1, 0xBEFE, 0x6FA2, 0xEA40,\n\t0x6FA3, 0xE9EB, 0x6FA4, 0xBF41, 0x6FA5, 0xE9F7, 0x6FA6, 0xBF48,\n\t0x6FA7, 0xBF43, 0x6FA8, 0xE9F5, 0x6FA9, 0xED4F, 0x6FAA, 0xE9FB,\n\t0x6FAB, 0xEA42, 0x6FAC, 0xE9FA, 0x6FAD, 0xE9E9, 0x6FAE, 0xE9F8,\n\t0x6FAF, 0xEA44, 0x6FB0, 0xEA46, 0x6FB1, 0xBEFD, 0x6FB2, 0xEA45,\n\t0x6FB3, 0xBF44, 0x6FB4, 0xBF4A, 0x6FB6, 0xBF47, 0x6FB8, 0xE9FE,\n\t0x6FB9, 0xBF46, 0x6FBA, 0xE9F9, 0x6FBC, 0xE9ED, 0x6FBD, 0xE9F2,\n\t0x6FBF, 0xE9FD, 0x6FC0, 0xBF45, 0x6FC1, 0xBF42, 0x6FC2, 0xBEFC,\n\t0x6FC3, 0xBF40, 0x6FC4, 0xE9F1, 0x6FC6, 0xE5FD, 0x6FC7, 0xE9EC,\n\t0x6FC8, 0xE9EF, 0x6FC9, 0xEA41, 0x6FCA, 0xE9F4, 0x6FCB, 0xE9EA,\n\t0x6FCC, 0xED4E, 0x6FCD, 0xEA43, 0x6FCE, 0xE9EE, 0x6FCF, 0xE9FC,\n\t0x6FD4, 0xED51, 0x6FD5, 0xC0E3, 0x6FD8, 0xC0D7, 0x6FDB, 0xC0DB,\n\t0x6FDC, 0xED53, 0x6FDD, 0xED59, 0x6FDE, 0xED57, 0x6FDF, 0xC0D9,\n\t0x6FE0, 0xC0DA, 0x6FE1, 0xC0E1, 0x6FE2, 0xED5A, 0x6FE3, 0xED52,\n\t0x6FE4, 0xC0DC, 0x6FE6, 0xED56, 0x6FE7, 0xED55, 0x6FE8, 0xED5B,\n\t0x6FE9, 0xC0E2, 0x6FEB, 0xC0DD, 0x6FEC, 0xC0E0, 0x6FED, 0xED54,\n\t0x6FEE, 0xC0E4, 0x6FEF, 0xC0DE, 0x6FF0, 0xC0E5, 0x6FF1, 0xC0D8,\n\t0x6FF2, 0xED58, 0x6FF4, 0xED50, 0x6FF7, 0xEFF7, 0x6FFA, 0xC271,\n\t0x6FFB, 0xEFF4, 0x6FFC, 0xEFF6, 0x6FFE, 0xC26F, 0x6FFF, 0xEFF2,\n\t0x7000, 0xEFF3, 0x7001, 0xEFEE, 0x7004, 0xE9F6, 0x7005, 0xEFEF,\n\t0x7006, 0xC270, 0x7007, 0xEFEB, 0x7009, 0xC26D, 0x700A, 0xEFF8,\n\t0x700B, 0xC26E, 0x700C, 0xEFEC, 0x700D, 0xEFED, 0x700E, 0xEFF1,\n\t0x700F, 0xC273, 0x7011, 0xC272, 0x7014, 0xEFF0, 0x7015, 0xC378,\n\t0x7016, 0xF25F, 0x7017, 0xF265, 0x7018, 0xC379, 0x7019, 0xF25C,\n\t0x701A, 0xC376, 0x701B, 0xC373, 0x701C, 0xF267, 0x701D, 0xC377,\n\t0x701F, 0xC374, 0x7020, 0xF25E, 0x7021, 0xF261, 0x7022, 0xF262,\n\t0x7023, 0xF263, 0x7024, 0xF266, 0x7026, 0xEFF5, 0x7027, 0xF25D,\n\t0x7028, 0xC375, 0x7029, 0xF264, 0x702A, 0xF268, 0x702B, 0xF260,\n\t0x702F, 0xF45D, 0x7030, 0xC46A, 0x7031, 0xF460, 0x7032, 0xC46B,\n\t0x7033, 0xF468, 0x7034, 0xF45F, 0x7035, 0xF45C, 0x7037, 0xF45E,\n\t0x7038, 0xF462, 0x7039, 0xF465, 0x703A, 0xF464, 0x703B, 0xF467,\n\t0x703C, 0xF45B, 0x703E, 0xC469, 0x703F, 0xF463, 0x7040, 0xF466,\n\t0x7041, 0xF469, 0x7042, 0xF461, 0x7043, 0xF5D3, 0x7044, 0xF5D4,\n\t0x7045, 0xF5D8, 0x7046, 0xF5D9, 0x7048, 0xF5D6, 0x7049, 0xF5D7,\n\t0x704A, 0xF5D5, 0x704C, 0xC4E9, 0x7051, 0xC578, 0x7052, 0xF6EB,\n\t0x7055, 0xF6E8, 0x7056, 0xF6E9, 0x7057, 0xF6EA, 0x7058, 0xC579,\n\t0x705A, 0xF7E5, 0x705B, 0xF7E4, 0x705D, 0xF8AF, 0x705E, 0xC5F4,\n\t0x705F, 0xF8AD, 0x7060, 0xF8B0, 0x7061, 0xF8AE, 0x7062, 0xF8F5,\n\t0x7063, 0xC657, 0x7064, 0xC665, 0x7065, 0xF9A3, 0x7066, 0xF96C,\n\t0x7068, 0xF9A2, 0x7069, 0xF9D0, 0x706A, 0xF9D1, 0x706B, 0xA4F5,\n\t0x7070, 0xA6C7, 0x7071, 0xCA41, 0x7074, 0xCB5E, 0x7076, 0xA85F,\n\t0x7078, 0xA862, 0x707A, 0xCB5F, 0x707C, 0xA860, 0x707D, 0xA861,\n\t0x7082, 0xCD58, 0x7083, 0xCD5A, 0x7084, 0xCD55, 0x7085, 0xCD52,\n\t0x7086, 0xCD54, 0x708A, 0xAAA4, 0x708E, 0xAAA2, 0x7091, 0xCD56,\n\t0x7092, 0xAAA3, 0x7093, 0xCD53, 0x7094, 0xCD50, 0x7095, 0xAAA1,\n\t0x7096, 0xCD57, 0x7098, 0xCD51, 0x7099, 0xAAA5, 0x709A, 0xCD59,\n\t0x709F, 0xCFAF, 0x70A1, 0xCFB3, 0x70A4, 0xACB7, 0x70A9, 0xCFB6,\n\t0x70AB, 0xACAF, 0x70AC, 0xACB2, 0x70AD, 0xACB4, 0x70AE, 0xACB6,\n\t0x70AF, 0xACB3, 0x70B0, 0xCFB2, 0x70B1, 0xCFB1, 0x70B3, 0xACB1,\n\t0x70B4, 0xCFB4, 0x70B5, 0xCFB5, 0x70B7, 0xCFAE, 0x70B8, 0xACB5,\n\t0x70BA, 0xACB0, 0x70BE, 0xCFB0, 0x70C5, 0xD277, 0x70C6, 0xD278,\n\t0x70C7, 0xD279, 0x70C8, 0xAF50, 0x70CA, 0xAF4C, 0x70CB, 0xD26E,\n\t0x70CD, 0xD276, 0x70CE, 0xD27B, 0x70CF, 0xAF51, 0x70D1, 0xD26C,\n\t0x70D2, 0xD272, 0x70D3, 0xD26B, 0x70D4, 0xD275, 0x70D7, 0xD271,\n\t0x70D8, 0xAF4D, 0x70D9, 0xAF4F, 0x70DA, 0xD27A, 0x70DC, 0xD26A,\n\t0x70DD, 0xD26D, 0x70DE, 0xD273, 0x70E0, 0xD274, 0x70E1, 0xD27C,\n\t0x70E2, 0xD270, 0x70E4, 0xAF4E, 0x70EF, 0xB26D, 0x70F0, 0xD64E,\n\t0x70F3, 0xD650, 0x70F4, 0xD64C, 0x70F6, 0xD658, 0x70F7, 0xD64A,\n\t0x70F8, 0xD657, 0x70F9, 0xB269, 0x70FA, 0xD648, 0x70FB, 0xDA5B,\n\t0x70FC, 0xD652, 0x70FD, 0xB26C, 0x70FF, 0xD653, 0x7100, 0xD656,\n\t0x7102, 0xD65A, 0x7104, 0xD64F, 0x7106, 0xD654, 0x7109, 0xB26A,\n\t0x710A, 0xB26B, 0x710B, 0xD659, 0x710C, 0xD64D, 0x710D, 0xD649,\n\t0x710E, 0xD65B, 0x7110, 0xD651, 0x7113, 0xD655, 0x7117, 0xD64B,\n\t0x7119, 0xB548, 0x711A, 0xB549, 0x711B, 0xDA65, 0x711C, 0xB54F,\n\t0x711E, 0xDA59, 0x711F, 0xDA62, 0x7120, 0xDA58, 0x7121, 0xB54C,\n\t0x7122, 0xDA60, 0x7123, 0xDA5E, 0x7125, 0xDA5F, 0x7126, 0xB54A,\n\t0x7128, 0xDA63, 0x712E, 0xDA5C, 0x712F, 0xDA5A, 0x7130, 0xB54B,\n\t0x7131, 0xDA5D, 0x7132, 0xDA61, 0x7136, 0xB54D, 0x713A, 0xDA64,\n\t0x7141, 0xDE70, 0x7142, 0xDE77, 0x7143, 0xDE79, 0x7144, 0xDEA1,\n\t0x7146, 0xB7DA, 0x7147, 0xDE6B, 0x7149, 0xB7D2, 0x714B, 0xDE7A,\n\t0x714C, 0xB7D7, 0x714D, 0xDEA2, 0x714E, 0xB7CE, 0x7150, 0xDE7D,\n\t0x7152, 0xDE6D, 0x7153, 0xDE7E, 0x7154, 0xDE6C, 0x7156, 0xB7DC,\n\t0x7158, 0xDE78, 0x7159, 0xB7CF, 0x715A, 0xDEA3, 0x715C, 0xB7D4,\n\t0x715D, 0xDE71, 0x715E, 0xB7D9, 0x715F, 0xDE7C, 0x7160, 0xDE6F,\n\t0x7161, 0xDE76, 0x7162, 0xDE72, 0x7163, 0xDE6E, 0x7164, 0xB7D1,\n\t0x7165, 0xB7D8, 0x7166, 0xB7D6, 0x7167, 0xB7D3, 0x7168, 0xB7DB,\n\t0x7169, 0xB7D0, 0x716A, 0xDE75, 0x716C, 0xB7D5, 0x716E, 0xB54E,\n\t0x7170, 0xDE7B, 0x7172, 0xDE73, 0x7178, 0xDE74, 0x717B, 0xE2C1,\n\t0x717D, 0xBAB4, 0x7180, 0xE2BD, 0x7181, 0xE2C3, 0x7182, 0xE2BF,\n\t0x7184, 0xBAB6, 0x7185, 0xE2BE, 0x7186, 0xE2C2, 0x7187, 0xE2BA,\n\t0x7189, 0xE2BC, 0x718A, 0xBAB5, 0x718F, 0xE2C0, 0x7190, 0xE2BB,\n\t0x7192, 0xBAB7, 0x7194, 0xBAB2, 0x7197, 0xE2C4, 0x7199, 0xBAB3,\n\t0x719A, 0xE667, 0x719B, 0xE664, 0x719C, 0xE670, 0x719D, 0xE66A,\n\t0x719E, 0xE66C, 0x719F, 0xBCF4, 0x71A0, 0xE666, 0x71A1, 0xE66E,\n\t0x71A4, 0xE66D, 0x71A5, 0xE66B, 0x71A7, 0xE671, 0x71A8, 0xBCF7,\n\t0x71A9, 0xE668, 0x71AA, 0xE66F, 0x71AC, 0xBCF5, 0x71AF, 0xE663,\n\t0x71B0, 0xE665, 0x71B1, 0xBCF6, 0x71B2, 0xE662, 0x71B3, 0xE672,\n\t0x71B5, 0xE669, 0x71B8, 0xEA4A, 0x71B9, 0xBF51, 0x71BC, 0xEA55,\n\t0x71BD, 0xEA53, 0x71BE, 0xBF4B, 0x71BF, 0xEA49, 0x71C0, 0xEA4C,\n\t0x71C1, 0xEA4D, 0x71C2, 0xEA48, 0x71C3, 0xBF55, 0x71C4, 0xBF56,\n\t0x71C5, 0xEA47, 0x71C6, 0xEA56, 0x71C7, 0xEA51, 0x71C8, 0xBF4F,\n\t0x71C9, 0xBF4C, 0x71CA, 0xEA50, 0x71CB, 0xEA4E, 0x71CE, 0xBF52,\n\t0x71CF, 0xEA52, 0x71D0, 0xBF4D, 0x71D2, 0xBF4E, 0x71D4, 0xEA4F,\n\t0x71D5, 0xBF50, 0x71D6, 0xEA4B, 0x71D8, 0xEA54, 0x71D9, 0xBF53,\n\t0x71DA, 0xEA57, 0x71DB, 0xEA58, 0x71DC, 0xBF54, 0x71DF, 0xC0E7,\n\t0x71E0, 0xC0EE, 0x71E1, 0xED5C, 0x71E2, 0xED62, 0x71E4, 0xED60,\n\t0x71E5, 0xC0EA, 0x71E6, 0xC0E9, 0x71E7, 0xC0E6, 0x71E8, 0xED5E,\n\t0x71EC, 0xC0EC, 0x71ED, 0xC0EB, 0x71EE, 0xC0E8, 0x71F0, 0xED61,\n\t0x71F1, 0xED5D, 0x71F2, 0xED5F, 0x71F4, 0xC0ED, 0x71F8, 0xC277,\n\t0x71F9, 0xEFFB, 0x71FB, 0xC274, 0x71FC, 0xC275, 0x71FD, 0xEFFD,\n\t0x71FE, 0xC276, 0x71FF, 0xEFFA, 0x7201, 0xEFF9, 0x7202, 0xF26C,\n\t0x7203, 0xEFFC, 0x7205, 0xF26D, 0x7206, 0xC37A, 0x7207, 0xF26B,\n\t0x720A, 0xF26A, 0x720C, 0xF269, 0x720D, 0xC37B, 0x7210, 0xC46C,\n\t0x7213, 0xF46A, 0x7214, 0xF46B, 0x7219, 0xF5DC, 0x721A, 0xF5DB,\n\t0x721B, 0xC4EA, 0x721D, 0xF5DA, 0x721E, 0xF6EC, 0x721F, 0xF6ED,\n\t0x7222, 0xF7E6, 0x7223, 0xF8B1, 0x7226, 0xF8F6, 0x7227, 0xF9BC,\n\t0x7228, 0xC679, 0x7229, 0xF9C6, 0x722A, 0xA4F6, 0x722C, 0xAAA6,\n\t0x722D, 0xAAA7, 0x7230, 0xACB8, 0x7235, 0xC0EF, 0x7236, 0xA4F7,\n\t0x7238, 0xAAA8, 0x7239, 0xAF52, 0x723A, 0xB7DD, 0x723B, 0xA4F8,\n\t0x723D, 0xB26E, 0x723E, 0xBAB8, 0x723F, 0xC962, 0x7241, 0xCFB7,\n\t0x7242, 0xD27D, 0x7244, 0xE2C5, 0x7246, 0xC0F0, 0x7247, 0xA4F9,\n\t0x7248, 0xAAA9, 0x7249, 0xCFB8, 0x724A, 0xCFB9, 0x724B, 0xDA66,\n\t0x724C, 0xB550, 0x724F, 0xDEA4, 0x7252, 0xB7DE, 0x7253, 0xE2C6,\n\t0x7256, 0xBCF8, 0x7258, 0xC37C, 0x7259, 0xA4FA, 0x725A, 0xDA67,\n\t0x725B, 0xA4FB, 0x725D, 0xA6C9, 0x725E, 0xCA42, 0x725F, 0xA6C8,\n\t0x7260, 0xA865, 0x7261, 0xA864, 0x7262, 0xA863, 0x7263, 0xCB60,\n\t0x7267, 0xAAAA, 0x7269, 0xAAAB, 0x726A, 0xCD5B, 0x726C, 0xCFBA,\n\t0x726E, 0xCFBD, 0x726F, 0xACBA, 0x7270, 0xCFBB, 0x7272, 0xACB9,\n\t0x7273, 0xCFBC, 0x7274, 0xACBB, 0x7276, 0xD2A2, 0x7277, 0xD2A1,\n\t0x7278, 0xD27E, 0x7279, 0xAF53, 0x727B, 0xD65D, 0x727C, 0xD65E,\n\t0x727D, 0xB26F, 0x727E, 0xD65C, 0x727F, 0xD65F, 0x7280, 0xB552,\n\t0x7281, 0xB270, 0x7284, 0xB551, 0x7285, 0xDA6B, 0x7286, 0xDA6A,\n\t0x7288, 0xDA68, 0x7289, 0xDA69, 0x728B, 0xDA6C, 0x728C, 0xDEA6,\n\t0x728D, 0xDEA5, 0x728E, 0xDEA9, 0x7290, 0xDEA8, 0x7291, 0xDEA7,\n\t0x7292, 0xBAB9, 0x7293, 0xE2C9, 0x7295, 0xE2C8, 0x7296, 0xBABA,\n\t0x7297, 0xE2C7, 0x7298, 0xE673, 0x729A, 0xE674, 0x729B, 0xBCF9,\n\t0x729D, 0xEA59, 0x729E, 0xEA5A, 0x72A1, 0xF272, 0x72A2, 0xC37D,\n\t0x72A3, 0xF271, 0x72A4, 0xF270, 0x72A5, 0xF26E, 0x72A6, 0xF26F,\n\t0x72A7, 0xC4EB, 0x72A8, 0xF46C, 0x72A9, 0xF6EE, 0x72AA, 0xF8F7,\n\t0x72AC, 0xA4FC, 0x72AE, 0xC9A5, 0x72AF, 0xA5C7, 0x72B0, 0xC9A6,\n\t0x72B4, 0xCA43, 0x72B5, 0xCA44, 0x72BA, 0xCB66, 0x72BD, 0xCB62,\n\t0x72BF, 0xCB61, 0x72C0, 0xAAAC, 0x72C1, 0xCB65, 0x72C2, 0xA867,\n\t0x72C3, 0xCB63, 0x72C4, 0xA866, 0x72C5, 0xCB67, 0x72C6, 0xCB64,\n\t0x72C9, 0xCD5F, 0x72CA, 0xCFBE, 0x72CB, 0xCD5D, 0x72CC, 0xCD64,\n\t0x72CE, 0xAAAD, 0x72D0, 0xAAB0, 0x72D1, 0xCD65, 0x72D2, 0xCD61,\n\t0x72D4, 0xCD62, 0x72D6, 0xCD5C, 0x72D7, 0xAAAF, 0x72D8, 0xCD5E,\n\t0x72D9, 0xAAAE, 0x72DA, 0xCD63, 0x72DC, 0xCD60, 0x72DF, 0xCFC2,\n\t0x72E0, 0xACBD, 0x72E1, 0xACBE, 0x72E3, 0xCFC5, 0x72E4, 0xCFBF,\n\t0x72E6, 0xCFC4, 0x72E8, 0xCFC0, 0x72E9, 0xACBC, 0x72EA, 0xCFC3,\n\t0x72EB, 0xCFC1, 0x72F3, 0xD2A8, 0x72F4, 0xD2A5, 0x72F6, 0xD2A7,\n\t0x72F7, 0xAF58, 0x72F8, 0xAF57, 0x72F9, 0xAF55, 0x72FA, 0xD2A4,\n\t0x72FB, 0xD2A9, 0x72FC, 0xAF54, 0x72FD, 0xAF56, 0x72FE, 0xD2A6,\n\t0x72FF, 0xD667, 0x7300, 0xD2A3, 0x7301, 0xD2AA, 0x7307, 0xD662,\n\t0x7308, 0xD666, 0x730A, 0xD665, 0x730B, 0xDA6E, 0x730C, 0xDA79,\n\t0x730F, 0xD668, 0x7311, 0xD663, 0x7312, 0xDA6D, 0x7313, 0xB274,\n\t0x7316, 0xB273, 0x7317, 0xD661, 0x7318, 0xD664, 0x7319, 0xB275,\n\t0x731B, 0xB272, 0x731C, 0xB271, 0x731D, 0xD660, 0x731E, 0xD669,\n\t0x7322, 0xDA70, 0x7323, 0xDA77, 0x7325, 0xB554, 0x7326, 0xDA76,\n\t0x7327, 0xDA73, 0x7329, 0xB556, 0x732D, 0xDA75, 0x7330, 0xDA6F,\n\t0x7331, 0xDA71, 0x7332, 0xDA74, 0x7333, 0xDA72, 0x7334, 0xB555,\n\t0x7335, 0xDA78, 0x7336, 0xB553, 0x7337, 0xB7DF, 0x733A, 0xDEAD,\n\t0x733B, 0xDEAC, 0x733C, 0xDEAA, 0x733E, 0xB7E2, 0x733F, 0xB7E1,\n\t0x7340, 0xDEAE, 0x7342, 0xDEAB, 0x7343, 0xE2CA, 0x7344, 0xBABB,\n\t0x7345, 0xB7E0, 0x7349, 0xDEB0, 0x734A, 0xDEAF, 0x734C, 0xE2CD,\n\t0x734D, 0xE2CB, 0x734E, 0xBCFA, 0x7350, 0xBABC, 0x7351, 0xE2CC,\n\t0x7352, 0xE676, 0x7357, 0xBCFB, 0x7358, 0xE675, 0x7359, 0xE67E,\n\t0x735A, 0xE67D, 0x735B, 0xE67B, 0x735D, 0xE67A, 0x735E, 0xE677,\n\t0x735F, 0xE678, 0x7360, 0xE679, 0x7361, 0xE67C, 0x7362, 0xE6A1,\n\t0x7365, 0xEA5F, 0x7366, 0xEA5C, 0x7367, 0xEA5D, 0x7368, 0xBF57,\n\t0x7369, 0xEA5B, 0x736A, 0xEA61, 0x736B, 0xEA60, 0x736C, 0xEA5E,\n\t0x736E, 0xED64, 0x736F, 0xED65, 0x7370, 0xC0F1, 0x7372, 0xC0F2,\n\t0x7373, 0xED63, 0x7375, 0xC279, 0x7376, 0xEFFE, 0x7377, 0xC278,\n\t0x7378, 0xC37E, 0x737A, 0xC3A1, 0x737B, 0xC46D, 0x737C, 0xF46E,\n\t0x737D, 0xF46D, 0x737E, 0xF5DD, 0x737F, 0xF6EF, 0x7380, 0xC57A,\n\t0x7381, 0xF7E8, 0x7382, 0xF7E7, 0x7383, 0xF7E9, 0x7384, 0xA5C8,\n\t0x7385, 0xCFC6, 0x7386, 0xAF59, 0x7387, 0xB276, 0x7388, 0xD66A,\n\t0x7389, 0xA5C9, 0x738A, 0xC9A7, 0x738B, 0xA4FD, 0x738E, 0xCA45,\n\t0x7392, 0xCB6C, 0x7393, 0xCB6A, 0x7394, 0xCB6B, 0x7395, 0xCB68,\n\t0x7396, 0xA868, 0x7397, 0xCB69, 0x739D, 0xCD6D, 0x739F, 0xAAB3,\n\t0x73A0, 0xCD6B, 0x73A1, 0xCD67, 0x73A2, 0xCD6A, 0x73A4, 0xCD66,\n\t0x73A5, 0xAAB5, 0x73A6, 0xCD69, 0x73A8, 0xAAB2, 0x73A9, 0xAAB1,\n\t0x73AB, 0xAAB4, 0x73AC, 0xCD6C, 0x73AD, 0xCD68, 0x73B2, 0xACC2,\n\t0x73B3, 0xACC5, 0x73B4, 0xCFCE, 0x73B5, 0xCFCD, 0x73B6, 0xCFCC,\n\t0x73B7, 0xACBF, 0x73B8, 0xCFD5, 0x73B9, 0xCFCB, 0x73BB, 0xACC1,\n\t0x73BC, 0xD2AF, 0x73BE, 0xCFD2, 0x73BF, 0xCFD0, 0x73C0, 0xACC4,\n\t0x73C2, 0xCFC8, 0x73C3, 0xCFD3, 0x73C5, 0xCFCA, 0x73C6, 0xCFD4,\n\t0x73C7, 0xCFD1, 0x73C8, 0xCFC9, 0x73CA, 0xACC0, 0x73CB, 0xCFD6,\n\t0x73CC, 0xCFC7, 0x73CD, 0xACC3, 0x73D2, 0xD2B4, 0x73D3, 0xD2AB,\n\t0x73D4, 0xD2B6, 0x73D6, 0xD2AE, 0x73D7, 0xD2B9, 0x73D8, 0xD2BA,\n\t0x73D9, 0xD2AC, 0x73DA, 0xD2B8, 0x73DB, 0xD2B5, 0x73DC, 0xD2B3,\n\t0x73DD, 0xD2B7, 0x73DE, 0xAF5F, 0x73E0, 0xAF5D, 0x73E3, 0xD2B1,\n\t0x73E5, 0xD2AD, 0x73E7, 0xD2B0, 0x73E8, 0xD2BB, 0x73E9, 0xD2B2,\n\t0x73EA, 0xAF5E, 0x73EB, 0xCFCF, 0x73ED, 0xAF5A, 0x73EE, 0xAF5C,\n\t0x73F4, 0xD678, 0x73F5, 0xD66D, 0x73F6, 0xD66B, 0x73F8, 0xD66C,\n\t0x73FA, 0xD673, 0x73FC, 0xD674, 0x73FD, 0xD670, 0x73FE, 0xB27B,\n\t0x73FF, 0xD675, 0x7400, 0xD672, 0x7401, 0xD66F, 0x7403, 0xB279,\n\t0x7404, 0xD66E, 0x7405, 0xB277, 0x7406, 0xB27A, 0x7407, 0xD671,\n\t0x7408, 0xD679, 0x7409, 0xAF5B, 0x740A, 0xB278, 0x740B, 0xD677,\n\t0x740C, 0xD676, 0x740D, 0xB27C, 0x7416, 0xDA7E, 0x741A, 0xDAA1,\n\t0x741B, 0xB560, 0x741D, 0xDAA7, 0x7420, 0xDAA9, 0x7421, 0xDAA2,\n\t0x7422, 0xB55A, 0x7423, 0xDAA6, 0x7424, 0xDAA5, 0x7425, 0xB55B,\n\t0x7426, 0xB561, 0x7428, 0xB562, 0x7429, 0xDAA8, 0x742A, 0xB558,\n\t0x742B, 0xDA7D, 0x742C, 0xDA7B, 0x742D, 0xDAA3, 0x742E, 0xDA7A,\n\t0x742F, 0xB55F, 0x7430, 0xDA7C, 0x7431, 0xDAA4, 0x7432, 0xDAAA,\n\t0x7433, 0xB559, 0x7434, 0xB55E, 0x7435, 0xB55C, 0x7436, 0xB55D,\n\t0x743A, 0xB557, 0x743F, 0xB7E9, 0x7440, 0xDEB7, 0x7441, 0xB7E8,\n\t0x7442, 0xDEBB, 0x7444, 0xDEB1, 0x7446, 0xDEBC, 0x744A, 0xDEB2,\n\t0x744B, 0xDEB3, 0x744D, 0xDEBD, 0x744E, 0xDEBA, 0x744F, 0xDEB8,\n\t0x7450, 0xDEB9, 0x7451, 0xDEB5, 0x7452, 0xDEB4, 0x7454, 0xDEBE,\n\t0x7455, 0xB7E5, 0x7457, 0xDEB6, 0x7459, 0xB7EA, 0x745A, 0xB7E4,\n\t0x745B, 0xB7EB, 0x745C, 0xB7EC, 0x745E, 0xB7E7, 0x745F, 0xB7E6,\n\t0x7462, 0xE2CE, 0x7463, 0xBABE, 0x7464, 0xBABD, 0x7467, 0xE2D3,\n\t0x7469, 0xBCFC, 0x746A, 0xBABF, 0x746D, 0xBAC1, 0x746E, 0xE2D4,\n\t0x746F, 0xB7E3, 0x7470, 0xBAC0, 0x7471, 0xE2D0, 0x7472, 0xE2D2,\n\t0x7473, 0xE2CF, 0x7475, 0xE2D1, 0x7479, 0xE6AB, 0x747C, 0xE6AA,\n\t0x747D, 0xE6A7, 0x747E, 0xBD40, 0x747F, 0xEA62, 0x7480, 0xBD41,\n\t0x7481, 0xE6A6, 0x7483, 0xBCFE, 0x7485, 0xE6A8, 0x7486, 0xE6A5,\n\t0x7487, 0xE6A2, 0x7488, 0xE6A9, 0x7489, 0xE6A3, 0x748A, 0xE6A4,\n\t0x748B, 0xBCFD, 0x7490, 0xED69, 0x7492, 0xEA66, 0x7494, 0xEA65,\n\t0x7495, 0xEA67, 0x7497, 0xED66, 0x7498, 0xBF5A, 0x749A, 0xEA63,\n\t0x749C, 0xBF58, 0x749E, 0xBF5C, 0x749F, 0xBF5B, 0x74A0, 0xEA64,\n\t0x74A1, 0xEA68, 0x74A3, 0xBF59, 0x74A5, 0xED6D, 0x74A6, 0xC0F5,\n\t0x74A7, 0xC27A, 0x74A8, 0xC0F6, 0x74A9, 0xC0F3, 0x74AA, 0xED6A,\n\t0x74AB, 0xED68, 0x74AD, 0xED6B, 0x74AF, 0xED6E, 0x74B0, 0xC0F4,\n\t0x74B1, 0xED6C, 0x74B2, 0xED67, 0x74B5, 0xF042, 0x74B6, 0xF045,\n\t0x74B7, 0xF275, 0x74B8, 0xF040, 0x74BA, 0xF46F, 0x74BB, 0xF046,\n\t0x74BD, 0xC3A2, 0x74BE, 0xF044, 0x74BF, 0xC27B, 0x74C0, 0xF041,\n\t0x74C1, 0xF043, 0x74C2, 0xF047, 0x74C3, 0xF276, 0x74C5, 0xF274,\n\t0x74CA, 0xC3A3, 0x74CB, 0xF273, 0x74CF, 0xC46E, 0x74D4, 0xC4ED,\n\t0x74D5, 0xF6F1, 0x74D6, 0xC4EC, 0x74D7, 0xF6F3, 0x74D8, 0xF6F0,\n\t0x74D9, 0xF6F2, 0x74DA, 0xC5D0, 0x74DB, 0xF8B2, 0x74DC, 0xA5CA,\n\t0x74DD, 0xCD6E, 0x74DE, 0xD2BC, 0x74DF, 0xD2BD, 0x74E0, 0xB27D,\n\t0x74E1, 0xDEBF, 0x74E2, 0xBF5D, 0x74E3, 0xC3A4, 0x74E4, 0xC57B,\n\t0x74E5, 0xF8B3, 0x74E6, 0xA5CB, 0x74E8, 0xCD6F, 0x74E9, 0xA260,\n\t0x74EC, 0xCFD7, 0x74EE, 0xCFD8, 0x74F4, 0xD2BE, 0x74F5, 0xD2BF,\n\t0x74F6, 0xB27E, 0x74F7, 0xB2A1, 0x74FB, 0xDAAB, 0x74FD, 0xDEC2,\n\t0x74FE, 0xDEC1, 0x74FF, 0xDEC0, 0x7500, 0xE2D5, 0x7502, 0xE2D6,\n\t0x7503, 0xE2D7, 0x7504, 0xBAC2, 0x7507, 0xE6AD, 0x7508, 0xE6AC,\n\t0x750B, 0xEA69, 0x750C, 0xBF5E, 0x750D, 0xBF5F, 0x750F, 0xED72,\n\t0x7510, 0xED6F, 0x7511, 0xED70, 0x7512, 0xED71, 0x7513, 0xF049,\n\t0x7514, 0xF048, 0x7515, 0xC27C, 0x7516, 0xF277, 0x7517, 0xF5DE,\n\t0x7518, 0xA5CC, 0x751A, 0xACC6, 0x751C, 0xB2A2, 0x751D, 0xDEC3,\n\t0x751F, 0xA5CD, 0x7521, 0xD2C0, 0x7522, 0xB2A3, 0x7525, 0xB563,\n\t0x7526, 0xB564, 0x7528, 0xA5CE, 0x7529, 0xA5CF, 0x752A, 0xCA46,\n\t0x752B, 0xA86A, 0x752C, 0xA869, 0x752D, 0xACC7, 0x752E, 0xCFD9,\n\t0x752F, 0xDAAC, 0x7530, 0xA5D0, 0x7531, 0xA5D1, 0x7532, 0xA5D2,\n\t0x7533, 0xA5D3, 0x7537, 0xA86B, 0x7538, 0xA86C, 0x7539, 0xCB6E,\n\t0x753A, 0xCB6D, 0x753D, 0xAAB6, 0x753E, 0xCD72, 0x753F, 0xCD70,\n\t0x7540, 0xCD71, 0x7547, 0xCFDA, 0x7548, 0xCFDB, 0x754B, 0xACCB,\n\t0x754C, 0xACC9, 0x754E, 0xACCA, 0x754F, 0xACC8, 0x7554, 0xAF60,\n\t0x7559, 0xAF64, 0x755A, 0xAF63, 0x755B, 0xD2C1, 0x755C, 0xAF62,\n\t0x755D, 0xAF61, 0x755F, 0xD2C2, 0x7562, 0xB2A6, 0x7563, 0xD67B,\n\t0x7564, 0xD67A, 0x7565, 0xB2A4, 0x7566, 0xB2A5, 0x756A, 0xB566,\n\t0x756B, 0xB565, 0x756C, 0xDAAE, 0x756F, 0xDAAD, 0x7570, 0xB2A7,\n\t0x7576, 0xB7ED, 0x7577, 0xDEC5, 0x7578, 0xB7EE, 0x7579, 0xDEC4,\n\t0x757D, 0xE2D8, 0x757E, 0xE6AE, 0x757F, 0xBD42, 0x7580, 0xEA6A,\n\t0x7584, 0xED73, 0x7586, 0xC3A6, 0x7587, 0xC3A5, 0x758A, 0xC57C,\n\t0x758B, 0xA5D4, 0x758C, 0xCD73, 0x758F, 0xB2A8, 0x7590, 0xE2D9,\n\t0x7591, 0xBAC3, 0x7594, 0xCB6F, 0x7595, 0xCB70, 0x7598, 0xCD74,\n\t0x7599, 0xAAB8, 0x759A, 0xAAB9, 0x759D, 0xAAB7, 0x75A2, 0xACCF,\n\t0x75A3, 0xACD0, 0x75A4, 0xACCD, 0x75A5, 0xACCE, 0x75A7, 0xCFDC,\n\t0x75AA, 0xCFDD, 0x75AB, 0xACCC, 0x75B0, 0xD2C3, 0x75B2, 0xAF68,\n\t0x75B3, 0xAF69, 0x75B5, 0xB2AB, 0x75B6, 0xD2C9, 0x75B8, 0xAF6E,\n\t0x75B9, 0xAF6C, 0x75BA, 0xD2CA, 0x75BB, 0xD2C5, 0x75BC, 0xAF6B,\n\t0x75BD, 0xAF6A, 0x75BE, 0xAF65, 0x75BF, 0xD2C8, 0x75C0, 0xD2C7,\n\t0x75C1, 0xD2C4, 0x75C2, 0xAF6D, 0x75C4, 0xD2C6, 0x75C5, 0xAF66,\n\t0x75C7, 0xAF67, 0x75CA, 0xB2AC, 0x75CB, 0xD6A1, 0x75CC, 0xD6A2,\n\t0x75CD, 0xB2AD, 0x75CE, 0xD67C, 0x75CF, 0xD67E, 0x75D0, 0xD6A4,\n\t0x75D1, 0xD6A3, 0x75D2, 0xD67D, 0x75D4, 0xB2A9, 0x75D5, 0xB2AA,\n\t0x75D7, 0xDAB6, 0x75D8, 0xB56B, 0x75D9, 0xB56A, 0x75DA, 0xDAB0,\n\t0x75DB, 0xB568, 0x75DD, 0xDAB3, 0x75DE, 0xB56C, 0x75DF, 0xDAB4,\n\t0x75E0, 0xB56D, 0x75E1, 0xDAB1, 0x75E2, 0xB567, 0x75E3, 0xB569,\n\t0x75E4, 0xDAB5, 0x75E6, 0xDAB2, 0x75E7, 0xDAAF, 0x75ED, 0xDED2,\n\t0x75EF, 0xDEC7, 0x75F0, 0xB7F0, 0x75F1, 0xB7F3, 0x75F2, 0xB7F2,\n\t0x75F3, 0xB7F7, 0x75F4, 0xB7F6, 0x75F5, 0xDED3, 0x75F6, 0xDED1,\n\t0x75F7, 0xDECA, 0x75F8, 0xDECE, 0x75F9, 0xDECD, 0x75FA, 0xB7F4,\n\t0x75FB, 0xDED0, 0x75FC, 0xDECC, 0x75FD, 0xDED4, 0x75FE, 0xDECB,\n\t0x75FF, 0xB7F5, 0x7600, 0xB7EF, 0x7601, 0xB7F1, 0x7603, 0xDEC9,\n\t0x7608, 0xE2DB, 0x7609, 0xBAC7, 0x760A, 0xE2DF, 0x760B, 0xBAC6,\n\t0x760C, 0xE2DC, 0x760D, 0xBAC5, 0x760F, 0xDEC8, 0x7610, 0xDECF,\n\t0x7611, 0xE2DE, 0x7613, 0xBAC8, 0x7614, 0xE2E0, 0x7615, 0xE2DD,\n\t0x7616, 0xE2DA, 0x7619, 0xE6B1, 0x761A, 0xE6B5, 0x761B, 0xE6B7,\n\t0x761C, 0xE6B3, 0x761D, 0xE6B2, 0x761E, 0xE6B0, 0x761F, 0xBD45,\n\t0x7620, 0xBD43, 0x7621, 0xBD48, 0x7622, 0xBD49, 0x7623, 0xE6B4,\n\t0x7624, 0xBD46, 0x7625, 0xE6AF, 0x7626, 0xBD47, 0x7627, 0xBAC4,\n\t0x7628, 0xE6B6, 0x7629, 0xBD44, 0x762D, 0xEA6C, 0x762F, 0xEA6B,\n\t0x7630, 0xEA73, 0x7631, 0xEA6D, 0x7632, 0xEA72, 0x7633, 0xEA6F,\n\t0x7634, 0xBF60, 0x7635, 0xEA71, 0x7638, 0xBF61, 0x763A, 0xBF62,\n\t0x763C, 0xEA70, 0x763D, 0xEA6E, 0x7642, 0xC0F8, 0x7643, 0xED74,\n\t0x7646, 0xC0F7, 0x7647, 0xED77, 0x7648, 0xED75, 0x7649, 0xED76,\n\t0x764C, 0xC0F9, 0x7650, 0xF04D, 0x7652, 0xC2A1, 0x7653, 0xF04E,\n\t0x7656, 0xC27D, 0x7657, 0xF04F, 0x7658, 0xC27E, 0x7659, 0xF04C,\n\t0x765A, 0xF050, 0x765C, 0xF04A, 0x765F, 0xC3A7, 0x7660, 0xF278,\n\t0x7661, 0xC3A8, 0x7662, 0xC46F, 0x7664, 0xF04B, 0x7665, 0xC470,\n\t0x7669, 0xC4EE, 0x766A, 0xF5DF, 0x766C, 0xC57E, 0x766D, 0xF6F4,\n\t0x766E, 0xC57D, 0x7670, 0xF7EA, 0x7671, 0xC5F5, 0x7672, 0xC5F6,\n\t0x7675, 0xF9CC, 0x7678, 0xACD1, 0x7679, 0xCFDE, 0x767B, 0xB56E,\n\t0x767C, 0xB56F, 0x767D, 0xA5D5, 0x767E, 0xA6CA, 0x767F, 0xCA47,\n\t0x7681, 0xCB71, 0x7682, 0xA86D, 0x7684, 0xAABA, 0x7686, 0xACD2,\n\t0x7687, 0xACD3, 0x7688, 0xACD4, 0x7689, 0xD6A6, 0x768A, 0xD2CB,\n\t0x768B, 0xAF6F, 0x768E, 0xB2AE, 0x768F, 0xD6A5, 0x7692, 0xDAB8,\n\t0x7693, 0xB571, 0x7695, 0xDAB7, 0x7696, 0xB570, 0x7699, 0xDED5,\n\t0x769A, 0xBD4A, 0x769B, 0xE6BB, 0x769C, 0xE6B8, 0x769D, 0xE6B9,\n\t0x769E, 0xE6BA, 0x76A4, 0xED78, 0x76A6, 0xF051, 0x76AA, 0xF471,\n\t0x76AB, 0xF470, 0x76AD, 0xF6F5, 0x76AE, 0xA5D6, 0x76AF, 0xCD75,\n\t0x76B0, 0xAF70, 0x76B4, 0xB572, 0x76B5, 0xDED6, 0x76B8, 0xE2E1,\n\t0x76BA, 0xBD4B, 0x76BB, 0xEA74, 0x76BD, 0xF052, 0x76BE, 0xF472,\n\t0x76BF, 0xA5D7, 0x76C2, 0xAABB, 0x76C3, 0xACD7, 0x76C4, 0xCFDF,\n\t0x76C5, 0xACD8, 0x76C6, 0xACD6, 0x76C8, 0xACD5, 0x76C9, 0xD2CC,\n\t0x76CA, 0xAF71, 0x76CD, 0xAF72, 0x76CE, 0xAF73, 0x76D2, 0xB2B0,\n\t0x76D3, 0xD6A7, 0x76D4, 0xB2AF, 0x76DA, 0xDAB9, 0x76DB, 0xB2B1,\n\t0x76DC, 0xB573, 0x76DD, 0xDED7, 0x76DE, 0xB7F8, 0x76DF, 0xB7F9,\n\t0x76E1, 0xBAC9, 0x76E3, 0xBACA, 0x76E4, 0xBD4C, 0x76E5, 0xBF64,\n\t0x76E6, 0xEA75, 0x76E7, 0xBF63, 0x76E9, 0xED79, 0x76EA, 0xC0FA,\n\t0x76EC, 0xF053, 0x76ED, 0xF473, 0x76EE, 0xA5D8, 0x76EF, 0xA86E,\n\t0x76F0, 0xCD78, 0x76F1, 0xCD77, 0x76F2, 0xAABC, 0x76F3, 0xCD76,\n\t0x76F4, 0xAABD, 0x76F5, 0xCD79, 0x76F7, 0xCFE5, 0x76F8, 0xACDB,\n\t0x76F9, 0xACDA, 0x76FA, 0xCFE7, 0x76FB, 0xCFE6, 0x76FC, 0xACDF,\n\t0x76FE, 0xACDE, 0x7701, 0xACD9, 0x7703, 0xCFE1, 0x7704, 0xCFE2,\n\t0x7705, 0xCFE3, 0x7707, 0xACE0, 0x7708, 0xCFE0, 0x7709, 0xACDC,\n\t0x770A, 0xCFE4, 0x770B, 0xACDD, 0x7710, 0xD2CF, 0x7711, 0xD2D3,\n\t0x7712, 0xD2D1, 0x7713, 0xD2D0, 0x7715, 0xD2D4, 0x7719, 0xD2D5,\n\t0x771A, 0xD2D6, 0x771B, 0xD2CE, 0x771D, 0xD2CD, 0x771F, 0xAF75,\n\t0x7720, 0xAF76, 0x7722, 0xD2D7, 0x7723, 0xD2D2, 0x7725, 0xD6B0,\n\t0x7727, 0xD2D8, 0x7728, 0xAF77, 0x7729, 0xAF74, 0x772D, 0xD6AA,\n\t0x772F, 0xD6A9, 0x7731, 0xD6AB, 0x7732, 0xD6AC, 0x7733, 0xD6AE,\n\t0x7734, 0xD6AD, 0x7735, 0xD6B2, 0x7736, 0xB2B5, 0x7737, 0xB2B2,\n\t0x7738, 0xB2B6, 0x7739, 0xD6A8, 0x773A, 0xB2B7, 0x773B, 0xD6B1,\n\t0x773C, 0xB2B4, 0x773D, 0xD6AF, 0x773E, 0xB2B3, 0x7744, 0xDABC,\n\t0x7745, 0xDABE, 0x7746, 0xDABA, 0x7747, 0xDABB, 0x774A, 0xDABF,\n\t0x774B, 0xDAC1, 0x774C, 0xDAC2, 0x774D, 0xDABD, 0x774E, 0xDAC0,\n\t0x774F, 0xB574, 0x7752, 0xDEDB, 0x7754, 0xDEE0, 0x7755, 0xDED8,\n\t0x7756, 0xDEDC, 0x7759, 0xDEE1, 0x775A, 0xDEDD, 0x775B, 0xB7FA,\n\t0x775C, 0xB843, 0x775E, 0xB7FD, 0x775F, 0xDED9, 0x7760, 0xDEDA,\n\t0x7761, 0xBACE, 0x7762, 0xB846, 0x7763, 0xB7FE, 0x7765, 0xB844,\n\t0x7766, 0xB7FC, 0x7767, 0xDEDF, 0x7768, 0xB845, 0x7769, 0xDEDE,\n\t0x776A, 0xB841, 0x776B, 0xB7FB, 0x776C, 0xB842, 0x776D, 0xDEE2,\n\t0x776E, 0xE2E6, 0x776F, 0xE2E8, 0x7779, 0xB840, 0x777C, 0xE2E3,\n\t0x777D, 0xBACC, 0x777E, 0xE2E9, 0x777F, 0xBACD, 0x7780, 0xE2E7,\n\t0x7781, 0xE2E2, 0x7782, 0xE2E5, 0x7783, 0xE2EA, 0x7784, 0xBACB,\n\t0x7785, 0xE2E4, 0x7787, 0xBD4E, 0x7788, 0xE6BF, 0x7789, 0xE6BE,\n\t0x778B, 0xBD51, 0x778C, 0xBD4F, 0x778D, 0xE6BC, 0x778E, 0xBD4D,\n\t0x778F, 0xE6BD, 0x7791, 0xBD50, 0x7795, 0xEA7D, 0x7797, 0xEAA1,\n\t0x7799, 0xEA7E, 0x779A, 0xEA76, 0x779B, 0xEA7A, 0x779C, 0xEA79,\n\t0x779D, 0xEA77, 0x779E, 0xBF66, 0x779F, 0xBF67, 0x77A0, 0xBF65,\n\t0x77A1, 0xEA78, 0x77A2, 0xEA7B, 0x77A3, 0xEA7C, 0x77A5, 0xBF68,\n\t0x77A7, 0xC140, 0x77A8, 0xEDA3, 0x77AA, 0xC0FC, 0x77AB, 0xED7B,\n\t0x77AC, 0xC0FE, 0x77AD, 0xC141, 0x77B0, 0xC0FD, 0x77B1, 0xEDA2,\n\t0x77B2, 0xED7C, 0x77B3, 0xC0FB, 0x77B4, 0xEDA1, 0x77B5, 0xED7A,\n\t0x77B6, 0xED7E, 0x77B7, 0xED7D, 0x77BA, 0xF055, 0x77BB, 0xC2A4,\n\t0x77BC, 0xC2A5, 0x77BD, 0xC2A2, 0x77BF, 0xC2A3, 0x77C2, 0xF054,\n\t0x77C4, 0xF27B, 0x77C7, 0xC3A9, 0x77C9, 0xF279, 0x77CA, 0xF27A,\n\t0x77CC, 0xF474, 0x77CD, 0xF477, 0x77CE, 0xF475, 0x77CF, 0xF476,\n\t0x77D0, 0xF5E0, 0x77D3, 0xC4EF, 0x77D4, 0xF7EB, 0x77D5, 0xF8B4,\n\t0x77D7, 0xC5F7, 0x77D8, 0xF8F8, 0x77D9, 0xF8F9, 0x77DA, 0xC666,\n\t0x77DB, 0xA5D9, 0x77DC, 0xACE1, 0x77DE, 0xDAC3, 0x77E0, 0xDEE3,\n\t0x77E2, 0xA5DA, 0x77E3, 0xA86F, 0x77E5, 0xAABE, 0x77E7, 0xCFE8,\n\t0x77E8, 0xCFE9, 0x77E9, 0xAF78, 0x77EC, 0xDAC4, 0x77ED, 0xB575,\n\t0x77EE, 0xB847, 0x77EF, 0xC142, 0x77F0, 0xEDA4, 0x77F1, 0xF27C,\n\t0x77F2, 0xF478, 0x77F3, 0xA5DB, 0x77F7, 0xCDA1, 0x77F8, 0xCD7A,\n\t0x77F9, 0xCD7C, 0x77FA, 0xCD7E, 0x77FB, 0xCD7D, 0x77FC, 0xCD7B,\n\t0x77FD, 0xAABF, 0x7802, 0xACE2, 0x7803, 0xCFF2, 0x7805, 0xCFED,\n\t0x7806, 0xCFEA, 0x7809, 0xCFF1, 0x780C, 0xACE4, 0x780D, 0xACE5,\n\t0x780E, 0xCFF0, 0x780F, 0xCFEF, 0x7810, 0xCFEE, 0x7811, 0xCFEB,\n\t0x7812, 0xCFEC, 0x7813, 0xCFF3, 0x7814, 0xACE3, 0x781D, 0xAF7C,\n\t0x781F, 0xAFA4, 0x7820, 0xAFA3, 0x7821, 0xD2E1, 0x7822, 0xD2DB,\n\t0x7823, 0xD2D9, 0x7825, 0xAFA1, 0x7826, 0xD6B9, 0x7827, 0xAF7A,\n\t0x7828, 0xD2DE, 0x7829, 0xD2E2, 0x782A, 0xD2E4, 0x782B, 0xD2E0,\n\t0x782C, 0xD2DA, 0x782D, 0xAFA2, 0x782E, 0xD2DF, 0x782F, 0xD2DD,\n\t0x7830, 0xAF79, 0x7831, 0xD2E5, 0x7832, 0xAFA5, 0x7833, 0xD2E3,\n\t0x7834, 0xAF7D, 0x7835, 0xD2DC, 0x7837, 0xAF7E, 0x7838, 0xAF7B,\n\t0x7843, 0xB2B9, 0x7845, 0xD6BA, 0x7848, 0xD6B3, 0x7849, 0xD6B5,\n\t0x784A, 0xD6B7, 0x784C, 0xD6B8, 0x784D, 0xD6B6, 0x784E, 0xB2BA,\n\t0x7850, 0xD6BB, 0x7852, 0xD6B4, 0x785C, 0xDAC8, 0x785D, 0xB576,\n\t0x785E, 0xDAD0, 0x7860, 0xDAC5, 0x7862, 0xDAD1, 0x7864, 0xDAC6,\n\t0x7865, 0xDAC7, 0x7868, 0xDACF, 0x7869, 0xDACE, 0x786A, 0xDACB,\n\t0x786B, 0xB2B8, 0x786C, 0xB577, 0x786D, 0xDAC9, 0x786E, 0xDACC,\n\t0x786F, 0xB578, 0x7870, 0xDACD, 0x7871, 0xDACA, 0x7879, 0xDEEE,\n\t0x787B, 0xDEF2, 0x787C, 0xB84E, 0x787E, 0xE2F0, 0x787F, 0xB851,\n\t0x7880, 0xDEF0, 0x7881, 0xF9D6, 0x7883, 0xDEED, 0x7884, 0xDEE8,\n\t0x7885, 0xDEEA, 0x7886, 0xDEEB, 0x7887, 0xDEE4, 0x7889, 0xB84D,\n\t0x788C, 0xB84C, 0x788E, 0xB848, 0x788F, 0xDEE7, 0x7891, 0xB84F,\n\t0x7893, 0xB850, 0x7894, 0xDEE6, 0x7895, 0xDEE9, 0x7896, 0xDEF1,\n\t0x7897, 0xB84A, 0x7898, 0xB84B, 0x7899, 0xDEEF, 0x789A, 0xDEE5,\n\t0x789E, 0xE2F2, 0x789F, 0xBAD0, 0x78A0, 0xE2F4, 0x78A1, 0xDEEC,\n\t0x78A2, 0xE2F6, 0x78A3, 0xBAD4, 0x78A4, 0xE2F7, 0x78A5, 0xE2F3,\n\t0x78A7, 0xBAD1, 0x78A8, 0xE2EF, 0x78A9, 0xBAD3, 0x78AA, 0xE2EC,\n\t0x78AB, 0xE2F1, 0x78AC, 0xE2F5, 0x78AD, 0xE2EE, 0x78B0, 0xB849,\n\t0x78B2, 0xE2EB, 0x78B3, 0xBAD2, 0x78B4, 0xE2ED, 0x78BA, 0xBD54,\n\t0x78BB, 0xE6C1, 0x78BC, 0xBD58, 0x78BE, 0xBD56, 0x78C1, 0xBACF,\n\t0x78C3, 0xE6C8, 0x78C4, 0xE6C9, 0x78C5, 0xBD53, 0x78C8, 0xE6C7,\n\t0x78C9, 0xE6CA, 0x78CA, 0xBD55, 0x78CB, 0xBD52, 0x78CC, 0xE6C3,\n\t0x78CD, 0xE6C0, 0x78CE, 0xE6C5, 0x78CF, 0xE6C2, 0x78D0, 0xBD59,\n\t0x78D1, 0xE6C4, 0x78D4, 0xE6C6, 0x78D5, 0xBD57, 0x78DA, 0xBF6A,\n\t0x78DB, 0xEAA8, 0x78DD, 0xEAA2, 0x78DE, 0xEAA6, 0x78DF, 0xEAAC,\n\t0x78E0, 0xEAAD, 0x78E1, 0xEAA9, 0x78E2, 0xEAAA, 0x78E3, 0xEAA7,\n\t0x78E5, 0xEAA4, 0x78E7, 0xBF6C, 0x78E8, 0xBF69, 0x78E9, 0xEAA3,\n\t0x78EA, 0xEAA5, 0x78EC, 0xBF6B, 0x78ED, 0xEAAB, 0x78EF, 0xC146,\n\t0x78F2, 0xEDAA, 0x78F3, 0xEDA5, 0x78F4, 0xC145, 0x78F7, 0xC143,\n\t0x78F9, 0xEDAC, 0x78FA, 0xC144, 0x78FB, 0xEDA8, 0x78FC, 0xEDA9,\n\t0x78FD, 0xEDA6, 0x78FE, 0xEDAD, 0x78FF, 0xF056, 0x7901, 0xC147,\n\t0x7902, 0xEDA7, 0x7904, 0xEDAE, 0x7905, 0xEDAB, 0x7909, 0xF05A,\n\t0x790C, 0xF057, 0x790E, 0xC2A6, 0x7910, 0xF05B, 0x7911, 0xF05D,\n\t0x7912, 0xF05C, 0x7913, 0xF058, 0x7914, 0xF059, 0x7917, 0xF2A3,\n\t0x7919, 0xC3AA, 0x791B, 0xF27E, 0x791C, 0xF2A2, 0x791D, 0xF27D,\n\t0x791E, 0xF2A4, 0x7921, 0xF2A1, 0x7923, 0xF47A, 0x7924, 0xF47D,\n\t0x7925, 0xF479, 0x7926, 0xC471, 0x7927, 0xF47B, 0x7928, 0xF47C,\n\t0x7929, 0xF47E, 0x792A, 0xC472, 0x792B, 0xC474, 0x792C, 0xC473,\n\t0x792D, 0xF5E1, 0x792F, 0xF5E3, 0x7931, 0xF5E2, 0x7935, 0xF6F6,\n\t0x7938, 0xF8B5, 0x7939, 0xF8FA, 0x793A, 0xA5DC, 0x793D, 0xCB72,\n\t0x793E, 0xAAC0, 0x793F, 0xCDA3, 0x7940, 0xAAC1, 0x7941, 0xAAC2,\n\t0x7942, 0xCDA2, 0x7944, 0xCFF8, 0x7945, 0xCFF7, 0x7946, 0xACE6,\n\t0x7947, 0xACE9, 0x7948, 0xACE8, 0x7949, 0xACE7, 0x794A, 0xCFF4,\n\t0x794B, 0xCFF6, 0x794C, 0xCFF5, 0x794F, 0xD2E8, 0x7950, 0xAFA7,\n\t0x7951, 0xD2EC, 0x7952, 0xD2EB, 0x7953, 0xD2EA, 0x7954, 0xD2E6,\n\t0x7955, 0xAFA6, 0x7956, 0xAFAA, 0x7957, 0xAFAD, 0x795A, 0xAFAE,\n\t0x795B, 0xD2E7, 0x795C, 0xD2E9, 0x795D, 0xAFAC, 0x795E, 0xAFAB,\n\t0x795F, 0xAFA9, 0x7960, 0xAFA8, 0x7961, 0xD6C2, 0x7963, 0xD6C0,\n\t0x7964, 0xD6BC, 0x7965, 0xB2BB, 0x7967, 0xD6BD, 0x7968, 0xB2BC,\n\t0x7969, 0xD6BE, 0x796A, 0xD6BF, 0x796B, 0xD6C1, 0x796D, 0xB2BD,\n\t0x7970, 0xDAD5, 0x7972, 0xDAD4, 0x7973, 0xDAD3, 0x7974, 0xDAD2,\n\t0x7979, 0xDEF6, 0x797A, 0xB852, 0x797C, 0xDEF3, 0x797D, 0xDEF5,\n\t0x797F, 0xB853, 0x7981, 0xB854, 0x7982, 0xDEF4, 0x7988, 0xE341,\n\t0x798A, 0xE2F9, 0x798B, 0xE2FA, 0x798D, 0xBAD7, 0x798E, 0xBAD5,\n\t0x798F, 0xBAD6, 0x7990, 0xE343, 0x7992, 0xE342, 0x7993, 0xE2FE,\n\t0x7994, 0xE2FD, 0x7995, 0xE2FC, 0x7996, 0xE2FB, 0x7997, 0xE340,\n\t0x7998, 0xE2F8, 0x799A, 0xE6CB, 0x799B, 0xE6D0, 0x799C, 0xE6CE,\n\t0x79A0, 0xE6CD, 0x79A1, 0xE6CC, 0x79A2, 0xE6CF, 0x79A4, 0xEAAE,\n\t0x79A6, 0xBF6D, 0x79A7, 0xC148, 0x79A8, 0xEDB0, 0x79AA, 0xC149,\n\t0x79AB, 0xEDAF, 0x79AC, 0xF05F, 0x79AD, 0xF05E, 0x79AE, 0xC2A7,\n\t0x79B0, 0xF2A5, 0x79B1, 0xC3AB, 0x79B2, 0xF4A1, 0x79B3, 0xC5A1,\n\t0x79B4, 0xF6F7, 0x79B6, 0xF8B7, 0x79B7, 0xF8B6, 0x79B8, 0xC9A8,\n\t0x79B9, 0xACEA, 0x79BA, 0xACEB, 0x79BB, 0xD6C3, 0x79BD, 0xB856,\n\t0x79BE, 0xA5DD, 0x79BF, 0xA872, 0x79C0, 0xA871, 0x79C1, 0xA870,\n\t0x79C5, 0xCDA4, 0x79C8, 0xAAC4, 0x79C9, 0xAAC3, 0x79CB, 0xACEE,\n\t0x79CD, 0xCFFA, 0x79CE, 0xCFFD, 0x79CF, 0xCFFB, 0x79D1, 0xACEC,\n\t0x79D2, 0xACED, 0x79D5, 0xCFF9, 0x79D6, 0xCFFC, 0x79D8, 0xAFB5,\n\t0x79DC, 0xD2F3, 0x79DD, 0xD2F5, 0x79DE, 0xD2F4, 0x79DF, 0xAFB2,\n\t0x79E0, 0xD2EF, 0x79E3, 0xAFB0, 0x79E4, 0xAFAF, 0x79E6, 0xAFB3,\n\t0x79E7, 0xAFB1, 0x79E9, 0xAFB4, 0x79EA, 0xD2F2, 0x79EB, 0xD2ED,\n\t0x79EC, 0xD2EE, 0x79ED, 0xD2F1, 0x79EE, 0xD2F0, 0x79F6, 0xD6C6,\n\t0x79F7, 0xD6C7, 0x79F8, 0xD6C5, 0x79FA, 0xD6C4, 0x79FB, 0xB2BE,\n\t0x7A00, 0xB57D, 0x7A02, 0xDAD6, 0x7A03, 0xDAD8, 0x7A04, 0xDADA,\n\t0x7A05, 0xB57C, 0x7A08, 0xB57A, 0x7A0A, 0xDAD7, 0x7A0B, 0xB57B,\n\t0x7A0C, 0xDAD9, 0x7A0D, 0xB579, 0x7A10, 0xDF41, 0x7A11, 0xDEF7,\n\t0x7A12, 0xDEFA, 0x7A13, 0xDEFE, 0x7A14, 0xB85A, 0x7A15, 0xDEFC,\n\t0x7A17, 0xDEFB, 0x7A18, 0xDEF8, 0x7A19, 0xDEF9, 0x7A1A, 0xB858,\n\t0x7A1B, 0xDF40, 0x7A1C, 0xB857, 0x7A1E, 0xB85C, 0x7A1F, 0xB85B,\n\t0x7A20, 0xB859, 0x7A22, 0xDEFD, 0x7A26, 0xE349, 0x7A28, 0xE348,\n\t0x7A2B, 0xE344, 0x7A2E, 0xBAD8, 0x7A2F, 0xE347, 0x7A30, 0xE346,\n\t0x7A31, 0xBAD9, 0x7A37, 0xBD5E, 0x7A39, 0xE6D2, 0x7A3B, 0xBD5F,\n\t0x7A3C, 0xBD5B, 0x7A3D, 0xBD5D, 0x7A3F, 0xBD5A, 0x7A40, 0xBD5C,\n\t0x7A44, 0xEAAF, 0x7A46, 0xBF70, 0x7A47, 0xEAB1, 0x7A48, 0xEAB0,\n\t0x7A4A, 0xE345, 0x7A4B, 0xBF72, 0x7A4C, 0xBF71, 0x7A4D, 0xBF6E,\n\t0x7A4E, 0xBF6F, 0x7A54, 0xEDB5, 0x7A56, 0xEDB3, 0x7A57, 0xC14A,\n\t0x7A58, 0xEDB4, 0x7A5A, 0xEDB6, 0x7A5B, 0xEDB2, 0x7A5C, 0xEDB1,\n\t0x7A5F, 0xF060, 0x7A60, 0xC2AA, 0x7A61, 0xC2A8, 0x7A62, 0xC2A9,\n\t0x7A67, 0xF2A6, 0x7A68, 0xF2A7, 0x7A69, 0xC3AD, 0x7A6B, 0xC3AC,\n\t0x7A6C, 0xF4A3, 0x7A6D, 0xF4A4, 0x7A6E, 0xF4A2, 0x7A70, 0xF6F8,\n\t0x7A71, 0xF6F9, 0x7A74, 0xA5DE, 0x7A75, 0xCA48, 0x7A76, 0xA873,\n\t0x7A78, 0xCDA5, 0x7A79, 0xAAC6, 0x7A7A, 0xAAC5, 0x7A7B, 0xCDA6,\n\t0x7A7E, 0xD040, 0x7A7F, 0xACEF, 0x7A80, 0xCFFE, 0x7A81, 0xACF0,\n\t0x7A84, 0xAFB6, 0x7A85, 0xD2F8, 0x7A86, 0xD2F6, 0x7A87, 0xD2FC,\n\t0x7A88, 0xAFB7, 0x7A89, 0xD2F7, 0x7A8A, 0xD2FB, 0x7A8B, 0xD2F9,\n\t0x7A8C, 0xD2FA, 0x7A8F, 0xD6C8, 0x7A90, 0xD6CA, 0x7A92, 0xB2BF,\n\t0x7A94, 0xD6C9, 0x7A95, 0xB2C0, 0x7A96, 0xB5A2, 0x7A97, 0xB5A1,\n\t0x7A98, 0xB57E, 0x7A99, 0xDADB, 0x7A9E, 0xDF44, 0x7A9F, 0xB85D,\n\t0x7AA0, 0xB85E, 0x7AA2, 0xDF43, 0x7AA3, 0xDF42, 0x7AA8, 0xE34A,\n\t0x7AA9, 0xBADB, 0x7AAA, 0xBADA, 0x7AAB, 0xE34B, 0x7AAC, 0xE34C,\n\t0x7AAE, 0xBD61, 0x7AAF, 0xBD60, 0x7AB1, 0xEAB5, 0x7AB2, 0xE6D3,\n\t0x7AB3, 0xE6D5, 0x7AB4, 0xE6D4, 0x7AB5, 0xEAB4, 0x7AB6, 0xEAB2,\n\t0x7AB7, 0xEAB6, 0x7AB8, 0xEAB3, 0x7ABA, 0xBF73, 0x7ABE, 0xEDB7,\n\t0x7ABF, 0xC14B, 0x7AC0, 0xEDB8, 0x7AC1, 0xEDB9, 0x7AC4, 0xC2AB,\n\t0x7AC5, 0xC2AC, 0x7AC7, 0xC475, 0x7ACA, 0xC5D1, 0x7ACB, 0xA5DF,\n\t0x7AD1, 0xD041, 0x7AD8, 0xD2FD, 0x7AD9, 0xAFB8, 0x7ADF, 0xB3BA,\n\t0x7AE0, 0xB3B9, 0x7AE3, 0xB5A4, 0x7AE4, 0xDADD, 0x7AE5, 0xB5A3,\n\t0x7AE6, 0xDADC, 0x7AEB, 0xDF45, 0x7AED, 0xBADC, 0x7AEE, 0xE34D,\n\t0x7AEF, 0xBADD, 0x7AF6, 0xC476, 0x7AF7, 0xF4A5, 0x7AF9, 0xA6CB,\n\t0x7AFA, 0xAAC7, 0x7AFB, 0xCDA7, 0x7AFD, 0xACF2, 0x7AFF, 0xACF1,\n\t0x7B00, 0xD042, 0x7B01, 0xD043, 0x7B04, 0xD340, 0x7B05, 0xD342,\n\t0x7B06, 0xAFB9, 0x7B08, 0xD344, 0x7B09, 0xD347, 0x7B0A, 0xD345,\n\t0x7B0E, 0xD346, 0x7B0F, 0xD343, 0x7B10, 0xD2FE, 0x7B11, 0xAFBA,\n\t0x7B12, 0xD348, 0x7B13, 0xD341, 0x7B18, 0xD6D3, 0x7B19, 0xB2C6,\n\t0x7B1A, 0xD6DC, 0x7B1B, 0xB2C3, 0x7B1D, 0xD6D5, 0x7B1E, 0xB2C7,\n\t0x7B20, 0xB2C1, 0x7B22, 0xD6D0, 0x7B23, 0xD6DD, 0x7B24, 0xD6D1,\n\t0x7B25, 0xD6CE, 0x7B26, 0xB2C5, 0x7B28, 0xB2C2, 0x7B2A, 0xD6D4,\n\t0x7B2B, 0xD6D7, 0x7B2C, 0xB2C4, 0x7B2D, 0xD6D8, 0x7B2E, 0xB2C8,\n\t0x7B2F, 0xD6D9, 0x7B30, 0xD6CF, 0x7B31, 0xD6D6, 0x7B32, 0xD6DA,\n\t0x7B33, 0xD6D2, 0x7B34, 0xD6CD, 0x7B35, 0xD6CB, 0x7B38, 0xD6DB,\n\t0x7B3B, 0xDADF, 0x7B40, 0xDAE4, 0x7B44, 0xDAE0, 0x7B45, 0xDAE6,\n\t0x7B46, 0xB5A7, 0x7B47, 0xD6CC, 0x7B48, 0xDAE1, 0x7B49, 0xB5A5,\n\t0x7B4A, 0xDADE, 0x7B4B, 0xB5AC, 0x7B4C, 0xDAE2, 0x7B4D, 0xB5AB,\n\t0x7B4E, 0xDAE3, 0x7B4F, 0xB5AD, 0x7B50, 0xB5A8, 0x7B51, 0xB5AE,\n\t0x7B52, 0xB5A9, 0x7B54, 0xB5AA, 0x7B56, 0xB5A6, 0x7B58, 0xDAE5,\n\t0x7B60, 0xB861, 0x7B61, 0xDF50, 0x7B63, 0xDF53, 0x7B64, 0xDF47,\n\t0x7B65, 0xDF4C, 0x7B66, 0xDF46, 0x7B67, 0xB863, 0x7B69, 0xDF4A,\n\t0x7B6D, 0xDF48, 0x7B6E, 0xB862, 0x7B70, 0xDF4F, 0x7B71, 0xDF4E,\n\t0x7B72, 0xDF4B, 0x7B73, 0xDF4D, 0x7B74, 0xDF49, 0x7B75, 0xBAE1,\n\t0x7B76, 0xDF52, 0x7B77, 0xB85F, 0x7B78, 0xDF51, 0x7B82, 0xE35D,\n\t0x7B84, 0xBAE8, 0x7B85, 0xE358, 0x7B87, 0xBAE7, 0x7B88, 0xE34E,\n\t0x7B8A, 0xE350, 0x7B8B, 0xBAE0, 0x7B8C, 0xE355, 0x7B8D, 0xE354,\n\t0x7B8E, 0xE357, 0x7B8F, 0xBAE5, 0x7B90, 0xE352, 0x7B91, 0xE351,\n\t0x7B94, 0xBAE4, 0x7B95, 0xBADF, 0x7B96, 0xE353, 0x7B97, 0xBAE2,\n\t0x7B98, 0xE359, 0x7B99, 0xE35B, 0x7B9B, 0xE356, 0x7B9C, 0xE34F,\n\t0x7B9D, 0xBAE3, 0x7BA0, 0xBD69, 0x7BA1, 0xBADE, 0x7BA4, 0xE35C,\n\t0x7BAC, 0xE6D9, 0x7BAD, 0xBD62, 0x7BAF, 0xE6DB, 0x7BB1, 0xBD63,\n\t0x7BB4, 0xBD65, 0x7BB5, 0xE6DE, 0x7BB7, 0xE6D6, 0x7BB8, 0xBAE6,\n\t0x7BB9, 0xE6DC, 0x7BBE, 0xE6D8, 0x7BC0, 0xB860, 0x7BC1, 0xBD68,\n\t0x7BC4, 0xBD64, 0x7BC6, 0xBD66, 0x7BC7, 0xBD67, 0x7BC9, 0xBF76,\n\t0x7BCA, 0xE6DD, 0x7BCB, 0xE6D7, 0x7BCC, 0xBD6A, 0x7BCE, 0xE6DA,\n\t0x7BD4, 0xEAC0, 0x7BD5, 0xEABB, 0x7BD8, 0xEAC5, 0x7BD9, 0xBF74,\n\t0x7BDA, 0xEABD, 0x7BDB, 0xBF78, 0x7BDC, 0xEAC3, 0x7BDD, 0xEABA,\n\t0x7BDE, 0xEAB7, 0x7BDF, 0xEAC6, 0x7BE0, 0xC151, 0x7BE1, 0xBF79,\n\t0x7BE2, 0xEAC2, 0x7BE3, 0xEAB8, 0x7BE4, 0xBF77, 0x7BE5, 0xEABC,\n\t0x7BE6, 0xBF7B, 0x7BE7, 0xEAB9, 0x7BE8, 0xEABE, 0x7BE9, 0xBF7A,\n\t0x7BEA, 0xEAC1, 0x7BEB, 0xEAC4, 0x7BF0, 0xEDCB, 0x7BF1, 0xEDCC,\n\t0x7BF2, 0xEDBC, 0x7BF3, 0xEDC3, 0x7BF4, 0xEDC1, 0x7BF7, 0xC14F,\n\t0x7BF8, 0xEDC8, 0x7BF9, 0xEABF, 0x7BFB, 0xEDBF, 0x7BFD, 0xEDC9,\n\t0x7BFE, 0xC14E, 0x7BFF, 0xEDBE, 0x7C00, 0xEDBD, 0x7C01, 0xEDC7,\n\t0x7C02, 0xEDC4, 0x7C03, 0xEDC6, 0x7C05, 0xEDBA, 0x7C06, 0xEDCA,\n\t0x7C07, 0xC14C, 0x7C09, 0xEDC5, 0x7C0A, 0xEDCE, 0x7C0B, 0xEDC2,\n\t0x7C0C, 0xC150, 0x7C0D, 0xC14D, 0x7C0E, 0xEDC0, 0x7C0F, 0xEDBB,\n\t0x7C10, 0xEDCD, 0x7C11, 0xBF75, 0x7C19, 0xF063, 0x7C1C, 0xF061,\n\t0x7C1D, 0xF067, 0x7C1E, 0xC2B0, 0x7C1F, 0xF065, 0x7C20, 0xF064,\n\t0x7C21, 0xC2B2, 0x7C22, 0xF06A, 0x7C23, 0xC2B1, 0x7C25, 0xF06B,\n\t0x7C26, 0xF068, 0x7C27, 0xC2AE, 0x7C28, 0xF069, 0x7C29, 0xF062,\n\t0x7C2A, 0xC2AF, 0x7C2B, 0xC2AD, 0x7C2C, 0xF2AB, 0x7C2D, 0xF066,\n\t0x7C30, 0xF06C, 0x7C33, 0xF2A8, 0x7C37, 0xC3B2, 0x7C38, 0xC3B0,\n\t0x7C39, 0xF2AA, 0x7C3B, 0xF2AC, 0x7C3C, 0xF2A9, 0x7C3D, 0xC3B1,\n\t0x7C3E, 0xC3AE, 0x7C3F, 0xC3AF, 0x7C40, 0xC3B3, 0x7C43, 0xC478,\n\t0x7C45, 0xF4AA, 0x7C47, 0xF4A9, 0x7C48, 0xF4A7, 0x7C49, 0xF4A6,\n\t0x7C4A, 0xF4A8, 0x7C4C, 0xC477, 0x7C4D, 0xC479, 0x7C50, 0xC4F0,\n\t0x7C53, 0xF5E5, 0x7C54, 0xF5E4, 0x7C57, 0xF6FA, 0x7C59, 0xF6FC,\n\t0x7C5A, 0xF6FE, 0x7C5B, 0xF6FD, 0x7C5C, 0xF6FB, 0x7C5F, 0xC5A3,\n\t0x7C60, 0xC5A2, 0x7C63, 0xC5D3, 0x7C64, 0xC5D2, 0x7C65, 0xC5D4,\n\t0x7C66, 0xF7ED, 0x7C67, 0xF7EC, 0x7C69, 0xF8FB, 0x7C6A, 0xF8B8,\n\t0x7C6B, 0xF8FC, 0x7C6C, 0xC658, 0x7C6E, 0xC659, 0x7C6F, 0xF96D,\n\t0x7C72, 0xC67E, 0x7C73, 0xA6CC, 0x7C75, 0xCDA8, 0x7C78, 0xD045,\n\t0x7C79, 0xD046, 0x7C7A, 0xD044, 0x7C7D, 0xACF3, 0x7C7F, 0xD047,\n\t0x7C80, 0xD048, 0x7C81, 0xD049, 0x7C84, 0xD349, 0x7C85, 0xD34F,\n\t0x7C88, 0xD34D, 0x7C89, 0xAFBB, 0x7C8A, 0xD34B, 0x7C8C, 0xD34C,\n\t0x7C8D, 0xD34E, 0x7C91, 0xD34A, 0x7C92, 0xB2C9, 0x7C94, 0xD6DE,\n\t0x7C95, 0xB2CB, 0x7C96, 0xD6E0, 0x7C97, 0xB2CA, 0x7C98, 0xD6DF,\n\t0x7C9E, 0xDAE8, 0x7C9F, 0xB5AF, 0x7CA1, 0xDAEA, 0x7CA2, 0xDAE7,\n\t0x7CA3, 0xD6E1, 0x7CA5, 0xB5B0, 0x7CA7, 0xF9DB, 0x7CA8, 0xDAE9,\n\t0x7CAF, 0xDF56, 0x7CB1, 0xB864, 0x7CB2, 0xDF54, 0x7CB3, 0xB865,\n\t0x7CB4, 0xDF55, 0x7CB5, 0xB866, 0x7CB9, 0xBAE9, 0x7CBA, 0xE361,\n\t0x7CBB, 0xE35E, 0x7CBC, 0xE360, 0x7CBD, 0xBAEA, 0x7CBE, 0xBAEB,\n\t0x7CBF, 0xE35F, 0x7CC5, 0xE6DF, 0x7CC8, 0xE6E0, 0x7CCA, 0xBD6B,\n\t0x7CCB, 0xE6E2, 0x7CCC, 0xE6E1, 0x7CCE, 0xA261, 0x7CD0, 0xEACA,\n\t0x7CD1, 0xEACB, 0x7CD2, 0xEAC7, 0x7CD4, 0xEAC8, 0x7CD5, 0xBF7C,\n\t0x7CD6, 0xBF7D, 0x7CD7, 0xEAC9, 0x7CD9, 0xC157, 0x7CDC, 0xC153,\n\t0x7CDD, 0xC158, 0x7CDE, 0xC154, 0x7CDF, 0xC156, 0x7CE0, 0xC152,\n\t0x7CE2, 0xC155, 0x7CE7, 0xC2B3, 0x7CE8, 0xEDCF, 0x7CEA, 0xF2AE,\n\t0x7CEC, 0xF2AD, 0x7CEE, 0xF4AB, 0x7CEF, 0xC47A, 0x7CF0, 0xC47B,\n\t0x7CF1, 0xF741, 0x7CF2, 0xF5E6, 0x7CF4, 0xF740, 0x7CF6, 0xF8FD,\n\t0x7CF7, 0xF9A4, 0x7CF8, 0xA6CD, 0x7CFB, 0xA874, 0x7CFD, 0xCDA9,\n\t0x7CFE, 0xAAC8, 0x7D00, 0xACF6, 0x7D01, 0xD04C, 0x7D02, 0xACF4,\n\t0x7D03, 0xD04A, 0x7D04, 0xACF9, 0x7D05, 0xACF5, 0x7D06, 0xACFA,\n\t0x7D07, 0xACF8, 0x7D08, 0xD04B, 0x7D09, 0xACF7, 0x7D0A, 0xAFBF,\n\t0x7D0B, 0xAFBE, 0x7D0C, 0xD35A, 0x7D0D, 0xAFC7, 0x7D0E, 0xD353,\n\t0x7D0F, 0xD359, 0x7D10, 0xAFC3, 0x7D11, 0xD352, 0x7D12, 0xD358,\n\t0x7D13, 0xD356, 0x7D14, 0xAFC2, 0x7D15, 0xAFC4, 0x7D16, 0xD355,\n\t0x7D17, 0xAFBD, 0x7D18, 0xD354, 0x7D19, 0xAFC8, 0x7D1A, 0xAFC5,\n\t0x7D1B, 0xAFC9, 0x7D1C, 0xAFC6, 0x7D1D, 0xD351, 0x7D1E, 0xD350,\n\t0x7D1F, 0xD357, 0x7D20, 0xAFC0, 0x7D21, 0xAFBC, 0x7D22, 0xAFC1,\n\t0x7D28, 0xD6F0, 0x7D29, 0xD6E9, 0x7D2B, 0xB5B5, 0x7D2C, 0xD6E8,\n\t0x7D2E, 0xB2CF, 0x7D2F, 0xB2D6, 0x7D30, 0xB2D3, 0x7D31, 0xB2D9,\n\t0x7D32, 0xB2D8, 0x7D33, 0xB2D4, 0x7D35, 0xD6E2, 0x7D36, 0xD6E5,\n\t0x7D38, 0xD6E4, 0x7D39, 0xB2D0, 0x7D3A, 0xD6E6, 0x7D3B, 0xD6EF,\n\t0x7D3C, 0xB2D1, 0x7D3D, 0xD6E3, 0x7D3E, 0xD6EC, 0x7D3F, 0xD6ED,\n\t0x7D40, 0xB2D2, 0x7D41, 0xD6EA, 0x7D42, 0xB2D7, 0x7D43, 0xB2CD,\n\t0x7D44, 0xB2D5, 0x7D45, 0xD6E7, 0x7D46, 0xB2CC, 0x7D47, 0xD6EB,\n\t0x7D4A, 0xD6EE, 0x7D4E, 0xDAFB, 0x7D4F, 0xDAF2, 0x7D50, 0xB5B2,\n\t0x7D51, 0xDAF9, 0x7D52, 0xDAF6, 0x7D53, 0xDAEE, 0x7D54, 0xDAF7,\n\t0x7D55, 0xB5B4, 0x7D56, 0xDAEF, 0x7D58, 0xDAEB, 0x7D5B, 0xB86C,\n\t0x7D5C, 0xDAF4, 0x7D5E, 0xB5B1, 0x7D5F, 0xDAFA, 0x7D61, 0xB5B8,\n\t0x7D62, 0xB5BA, 0x7D63, 0xDAED, 0x7D66, 0xB5B9, 0x7D67, 0xDAF0,\n\t0x7D68, 0xB5B3, 0x7D69, 0xDAF8, 0x7D6A, 0xDAF1, 0x7D6B, 0xDAF5,\n\t0x7D6D, 0xDAF3, 0x7D6E, 0xB5B6, 0x7D6F, 0xDAEC, 0x7D70, 0xB5BB,\n\t0x7D71, 0xB2CE, 0x7D72, 0xB5B7, 0x7D73, 0xB5BC, 0x7D79, 0xB868,\n\t0x7D7A, 0xDF5D, 0x7D7B, 0xDF5F, 0x7D7C, 0xDF61, 0x7D7D, 0xDF65,\n\t0x7D7F, 0xDF5B, 0x7D80, 0xDF59, 0x7D81, 0xB86A, 0x7D83, 0xDF60,\n\t0x7D84, 0xDF64, 0x7D85, 0xDF5C, 0x7D86, 0xDF58, 0x7D88, 0xDF57,\n\t0x7D8C, 0xDF62, 0x7D8D, 0xDF5A, 0x7D8E, 0xDF5E, 0x7D8F, 0xB86B,\n\t0x7D91, 0xB869, 0x7D92, 0xDF66, 0x7D93, 0xB867, 0x7D94, 0xDF63,\n\t0x7D96, 0xE372, 0x7D9C, 0xBAEE, 0x7D9D, 0xE36A, 0x7D9E, 0xBD78,\n\t0x7D9F, 0xE374, 0x7DA0, 0xBAF1, 0x7DA1, 0xE378, 0x7DA2, 0xBAF7,\n\t0x7DA3, 0xE365, 0x7DA6, 0xE375, 0x7DA7, 0xE362, 0x7DA9, 0xE377,\n\t0x7DAA, 0xE366, 0x7DAC, 0xBAFE, 0x7DAD, 0xBAFB, 0x7DAE, 0xE376,\n\t0x7DAF, 0xE370, 0x7DB0, 0xBAED, 0x7DB1, 0xBAF5, 0x7DB2, 0xBAF4,\n\t0x7DB4, 0xBAF3, 0x7DB5, 0xBAF9, 0x7DB7, 0xE363, 0x7DB8, 0xBAFA,\n\t0x7DB9, 0xE371, 0x7DBA, 0xBAF6, 0x7DBB, 0xBAEC, 0x7DBC, 0xE373,\n\t0x7DBD, 0xBAEF, 0x7DBE, 0xBAF0, 0x7DBF, 0xBAF8, 0x7DC0, 0xE368,\n\t0x7DC1, 0xE367, 0x7DC2, 0xE364, 0x7DC4, 0xE36C, 0x7DC5, 0xE369,\n\t0x7DC6, 0xE36D, 0x7DC7, 0xBAFD, 0x7DC9, 0xE379, 0x7DCA, 0xBAF2,\n\t0x7DCB, 0xE36E, 0x7DCC, 0xE36F, 0x7DCE, 0xE36B, 0x7DD2, 0xBAFC,\n\t0x7DD7, 0xE6E7, 0x7DD8, 0xBD70, 0x7DD9, 0xBD79, 0x7DDA, 0xBD75,\n\t0x7DDB, 0xE6E4, 0x7DDD, 0xBD72, 0x7DDE, 0xBD76, 0x7DDF, 0xE6F0,\n\t0x7DE0, 0xBD6C, 0x7DE1, 0xE6E8, 0x7DE3, 0xBD74, 0x7DE6, 0xE6EB,\n\t0x7DE7, 0xE6E6, 0x7DE8, 0xBD73, 0x7DE9, 0xBD77, 0x7DEA, 0xE6E5,\n\t0x7DEC, 0xBD71, 0x7DEE, 0xE6EF, 0x7DEF, 0xBD6E, 0x7DF0, 0xE6EE,\n\t0x7DF1, 0xE6ED, 0x7DF2, 0xBD7A, 0x7DF3, 0xE572, 0x7DF4, 0xBD6D,\n\t0x7DF6, 0xE6EC, 0x7DF7, 0xE6E3, 0x7DF9, 0xBD7B, 0x7DFA, 0xE6EA,\n\t0x7DFB, 0xBD6F, 0x7E03, 0xE6E9, 0x7E08, 0xBFA2, 0x7E09, 0xBFA7,\n\t0x7E0A, 0xBF7E, 0x7E0B, 0xEAD8, 0x7E0C, 0xEACF, 0x7E0D, 0xEADB,\n\t0x7E0E, 0xEAD3, 0x7E0F, 0xEAD9, 0x7E10, 0xBFA8, 0x7E11, 0xBFA1,\n\t0x7E12, 0xEACC, 0x7E13, 0xEAD2, 0x7E14, 0xEADC, 0x7E15, 0xEAD5,\n\t0x7E16, 0xEADA, 0x7E17, 0xEACE, 0x7E1A, 0xEAD6, 0x7E1B, 0xBFA3,\n\t0x7E1C, 0xEAD4, 0x7E1D, 0xBFA6, 0x7E1E, 0xBFA5, 0x7E1F, 0xEAD0,\n\t0x7E20, 0xEAD1, 0x7E21, 0xEACD, 0x7E22, 0xEAD7, 0x7E23, 0xBFA4,\n\t0x7E24, 0xEADE, 0x7E25, 0xEADD, 0x7E29, 0xEDDA, 0x7E2A, 0xEDD6,\n\t0x7E2B, 0xC15F, 0x7E2D, 0xEDD0, 0x7E2E, 0xC159, 0x7E2F, 0xC169,\n\t0x7E30, 0xEDDC, 0x7E31, 0xC161, 0x7E32, 0xC15D, 0x7E33, 0xEDD3,\n\t0x7E34, 0xC164, 0x7E35, 0xC167, 0x7E36, 0xEDDE, 0x7E37, 0xC15C,\n\t0x7E38, 0xEDD5, 0x7E39, 0xC165, 0x7E3A, 0xEDE0, 0x7E3B, 0xEDDD,\n\t0x7E3C, 0xEDD1, 0x7E3D, 0xC160, 0x7E3E, 0xC15A, 0x7E3F, 0xC168,\n\t0x7E40, 0xEDD8, 0x7E41, 0xC163, 0x7E42, 0xEDD2, 0x7E43, 0xC15E,\n\t0x7E44, 0xEDDF, 0x7E45, 0xC162, 0x7E46, 0xC15B, 0x7E47, 0xEDD9,\n\t0x7E48, 0xC166, 0x7E49, 0xEDD7, 0x7E4C, 0xEDDB, 0x7E50, 0xF06E,\n\t0x7E51, 0xF074, 0x7E52, 0xC2B9, 0x7E53, 0xF077, 0x7E54, 0xC2B4,\n\t0x7E55, 0xC2B5, 0x7E56, 0xF06F, 0x7E57, 0xF076, 0x7E58, 0xF071,\n\t0x7E59, 0xC2BA, 0x7E5A, 0xC2B7, 0x7E5C, 0xF06D, 0x7E5E, 0xC2B6,\n\t0x7E5F, 0xF073, 0x7E60, 0xF075, 0x7E61, 0xC2B8, 0x7E62, 0xF072,\n\t0x7E63, 0xF070, 0x7E68, 0xF2B8, 0x7E69, 0xC3B7, 0x7E6A, 0xC3B8,\n\t0x7E6B, 0xC3B4, 0x7E6D, 0xC3B5, 0x7E6F, 0xF2B4, 0x7E70, 0xF2B2,\n\t0x7E72, 0xF2B6, 0x7E73, 0xC3BA, 0x7E74, 0xF2B7, 0x7E75, 0xF2B0,\n\t0x7E76, 0xF2AF, 0x7E77, 0xF2B3, 0x7E78, 0xF2B1, 0x7E79, 0xC3B6,\n\t0x7E7A, 0xF2B5, 0x7E7B, 0xF4AC, 0x7E7C, 0xC47E, 0x7E7D, 0xC47D,\n\t0x7E7E, 0xF4AD, 0x7E80, 0xF4AF, 0x7E81, 0xF4AE, 0x7E82, 0xC4A1,\n\t0x7E86, 0xF5EB, 0x7E87, 0xF5E8, 0x7E88, 0xF5E9, 0x7E8A, 0xF5E7,\n\t0x7E8B, 0xF5EA, 0x7E8C, 0xC4F2, 0x7E8D, 0xF5EC, 0x7E8F, 0xC4F1,\n\t0x7E91, 0xF742, 0x7E93, 0xC5D5, 0x7E94, 0xC5D7, 0x7E95, 0xF7EE,\n\t0x7E96, 0xC5D6, 0x7E97, 0xF8B9, 0x7E98, 0xF940, 0x7E99, 0xF942,\n\t0x7E9A, 0xF8FE, 0x7E9B, 0xF941, 0x7E9C, 0xC66C, 0x7F36, 0xA6CE,\n\t0x7F38, 0xACFB, 0x7F39, 0xD26F, 0x7F3A, 0xAFCA, 0x7F3D, 0xB2DA,\n\t0x7F3E, 0xDAFC, 0x7F3F, 0xDAFD, 0x7F43, 0xEADF, 0x7F44, 0xC16A,\n\t0x7F45, 0xEDE1, 0x7F48, 0xC2BB, 0x7F4A, 0xF2BA, 0x7F4B, 0xF2B9,\n\t0x7F4C, 0xC4A2, 0x7F4D, 0xF5ED, 0x7F4F, 0xF743, 0x7F50, 0xC5F8,\n\t0x7F51, 0xCA49, 0x7F54, 0xAAC9, 0x7F55, 0xA875, 0x7F58, 0xD04D,\n\t0x7F5B, 0xD360, 0x7F5C, 0xD35B, 0x7F5D, 0xD35F, 0x7F5E, 0xD35D,\n\t0x7F5F, 0xAFCB, 0x7F60, 0xD35E, 0x7F61, 0xD35C, 0x7F63, 0xD6F1,\n\t0x7F65, 0xDAFE, 0x7F66, 0xDB40, 0x7F67, 0xDF69, 0x7F68, 0xDF6A,\n\t0x7F69, 0xB86E, 0x7F6A, 0xB86F, 0x7F6B, 0xDF68, 0x7F6C, 0xDF6B,\n\t0x7F6D, 0xDF67, 0x7F6E, 0xB86D, 0x7F70, 0xBB40, 0x7F72, 0xB870,\n\t0x7F73, 0xE37A, 0x7F75, 0xBD7C, 0x7F76, 0xE6F1, 0x7F77, 0xBD7D,\n\t0x7F79, 0xBFA9, 0x7F7A, 0xEAE2, 0x7F7B, 0xEAE0, 0x7F7C, 0xEAE1,\n\t0x7F7D, 0xEDE4, 0x7F7E, 0xEDE3, 0x7F7F, 0xEDE2, 0x7F83, 0xF2BB,\n\t0x7F85, 0xC3B9, 0x7F86, 0xF2BC, 0x7F87, 0xF744, 0x7F88, 0xC5F9,\n\t0x7F89, 0xF8BA, 0x7F8A, 0xA6CF, 0x7F8B, 0xAACB, 0x7F8C, 0xAACA,\n\t0x7F8D, 0xD04F, 0x7F8E, 0xACFC, 0x7F91, 0xD04E, 0x7F92, 0xD362,\n\t0x7F94, 0xAFCC, 0x7F95, 0xD6F2, 0x7F96, 0xD361, 0x7F9A, 0xB2DC,\n\t0x7F9B, 0xD6F5, 0x7F9C, 0xD6F3, 0x7F9D, 0xD6F4, 0x7F9E, 0xB2DB,\n\t0x7FA0, 0xDB42, 0x7FA1, 0xDB43, 0x7FA2, 0xDB41, 0x7FA4, 0xB873,\n\t0x7FA5, 0xDF6D, 0x7FA6, 0xDF6C, 0x7FA7, 0xDF6E, 0x7FA8, 0xB872,\n\t0x7FA9, 0xB871, 0x7FAC, 0xE6F2, 0x7FAD, 0xE6F4, 0x7FAF, 0xBD7E,\n\t0x7FB0, 0xE6F3, 0x7FB1, 0xEAE3, 0x7FB2, 0xBFAA, 0x7FB3, 0xF079,\n\t0x7FB5, 0xF078, 0x7FB6, 0xC3BB, 0x7FB7, 0xF2BD, 0x7FB8, 0xC3BD,\n\t0x7FB9, 0xC3BC, 0x7FBA, 0xF4B0, 0x7FBB, 0xF5EE, 0x7FBC, 0xC4F3,\n\t0x7FBD, 0xA6D0, 0x7FBE, 0xD050, 0x7FBF, 0xACFD, 0x7FC0, 0xD365,\n\t0x7FC1, 0xAFCE, 0x7FC2, 0xD364, 0x7FC3, 0xD363, 0x7FC5, 0xAFCD,\n\t0x7FC7, 0xD6FB, 0x7FC9, 0xD6FD, 0x7FCA, 0xD6F6, 0x7FCB, 0xD6F7,\n\t0x7FCC, 0xB2DD, 0x7FCD, 0xD6F8, 0x7FCE, 0xB2DE, 0x7FCF, 0xD6FC,\n\t0x7FD0, 0xD6F9, 0x7FD1, 0xD6FA, 0x7FD2, 0xB2DF, 0x7FD4, 0xB5BE,\n\t0x7FD5, 0xB5BF, 0x7FD7, 0xDB44, 0x7FDB, 0xDF6F, 0x7FDC, 0xDF70,\n\t0x7FDE, 0xE37E, 0x7FDF, 0xBB43, 0x7FE0, 0xBB41, 0x7FE1, 0xBB42,\n\t0x7FE2, 0xE37B, 0x7FE3, 0xE37C, 0x7FE5, 0xE37D, 0x7FE6, 0xE6F9,\n\t0x7FE8, 0xE6FA, 0x7FE9, 0xBDA1, 0x7FEA, 0xE6F7, 0x7FEB, 0xE6F6,\n\t0x7FEC, 0xE6F8, 0x7FED, 0xE6F5, 0x7FEE, 0xBFAD, 0x7FEF, 0xEAE4,\n\t0x7FF0, 0xBFAB, 0x7FF1, 0xBFAC, 0x7FF2, 0xEDE6, 0x7FF3, 0xC16B,\n\t0x7FF4, 0xEDE5, 0x7FF5, 0xEFA8, 0x7FF7, 0xF07A, 0x7FF8, 0xF07B,\n\t0x7FF9, 0xC2BC, 0x7FFB, 0xC2BD, 0x7FFC, 0xC16C, 0x7FFD, 0xF2BE,\n\t0x7FFE, 0xF2BF, 0x7FFF, 0xF4B1, 0x8000, 0xC4A3, 0x8001, 0xA6D1,\n\t0x8003, 0xA6D2, 0x8004, 0xACFE, 0x8005, 0xAACC, 0x8006, 0xAFCF,\n\t0x8007, 0xD051, 0x800B, 0xB5C0, 0x800C, 0xA6D3, 0x800D, 0xAD41,\n\t0x800E, 0xD052, 0x800F, 0xD053, 0x8010, 0xAD40, 0x8011, 0xAD42,\n\t0x8012, 0xA6D4, 0x8014, 0xD054, 0x8015, 0xAFD1, 0x8016, 0xD366,\n\t0x8017, 0xAFD3, 0x8018, 0xAFD0, 0x8019, 0xAFD2, 0x801B, 0xD741,\n\t0x801C, 0xB2E0, 0x801E, 0xD740, 0x801F, 0xD6FE, 0x8021, 0xDF71,\n\t0x8024, 0xE3A1, 0x8026, 0xBDA2, 0x8028, 0xBFAE, 0x8029, 0xEAE6,\n\t0x802A, 0xEAE5, 0x802C, 0xEDE7, 0x8030, 0xF5EF, 0x8033, 0xA6D5,\n\t0x8034, 0xCB73, 0x8035, 0xCDAA, 0x8036, 0xAD43, 0x8037, 0xD055,\n\t0x8039, 0xD368, 0x803D, 0xAFD4, 0x803E, 0xD367, 0x803F, 0xAFD5,\n\t0x8043, 0xD743, 0x8046, 0xB2E2, 0x8047, 0xD742, 0x8048, 0xD744,\n\t0x804A, 0xB2E1, 0x804F, 0xDB46, 0x8050, 0xDB47, 0x8051, 0xDB45,\n\t0x8052, 0xB5C1, 0x8056, 0xB874, 0x8058, 0xB875, 0x805A, 0xBB45,\n\t0x805C, 0xE3A3, 0x805D, 0xE3A2, 0x805E, 0xBB44, 0x8064, 0xE6FB,\n\t0x8067, 0xE6FC, 0x806C, 0xEAE7, 0x806F, 0xC170, 0x8070, 0xC16F,\n\t0x8071, 0xC16D, 0x8072, 0xC16E, 0x8073, 0xC171, 0x8075, 0xF07C,\n\t0x8076, 0xC2BF, 0x8077, 0xC2BE, 0x8078, 0xF2C0, 0x8079, 0xF4B2,\n\t0x807D, 0xC5A5, 0x807E, 0xC5A4, 0x807F, 0xA6D6, 0x8082, 0xD1FB,\n\t0x8084, 0xB877, 0x8085, 0xB5C2, 0x8086, 0xB876, 0x8087, 0xBB46,\n\t0x8089, 0xA6D7, 0x808A, 0xC9A9, 0x808B, 0xA6D8, 0x808C, 0xA6D9,\n\t0x808F, 0xCDAB, 0x8090, 0xCB76, 0x8092, 0xCB77, 0x8093, 0xA877,\n\t0x8095, 0xCB74, 0x8096, 0xA876, 0x8098, 0xA879, 0x8099, 0xCB75,\n\t0x809A, 0xA87B, 0x809B, 0xA87A, 0x809C, 0xCB78, 0x809D, 0xA878,\n\t0x80A1, 0xAAD1, 0x80A2, 0xAACF, 0x80A3, 0xCDAD, 0x80A5, 0xAACE,\n\t0x80A9, 0xAAD3, 0x80AA, 0xAAD5, 0x80AB, 0xAAD2, 0x80AD, 0xCDB0,\n\t0x80AE, 0xCDAC, 0x80AF, 0xAAD6, 0x80B1, 0xAAD0, 0x80B2, 0xA87C,\n\t0x80B4, 0xAAD4, 0x80B5, 0xCDAF, 0x80B8, 0xCDAE, 0x80BA, 0xAACD,\n\t0x80C2, 0xD05B, 0x80C3, 0xAD47, 0x80C4, 0xAD48, 0x80C5, 0xD05D,\n\t0x80C7, 0xD057, 0x80C8, 0xD05A, 0x80C9, 0xD063, 0x80CA, 0xD061,\n\t0x80CC, 0xAD49, 0x80CD, 0xD067, 0x80CE, 0xAD4C, 0x80CF, 0xD064,\n\t0x80D0, 0xD05C, 0x80D1, 0xD059, 0x80D4, 0xDB49, 0x80D5, 0xD062,\n\t0x80D6, 0xAD44, 0x80D7, 0xD065, 0x80D8, 0xD056, 0x80D9, 0xD05F,\n\t0x80DA, 0xAD46, 0x80DB, 0xAD4B, 0x80DC, 0xD060, 0x80DD, 0xAD4F,\n\t0x80DE, 0xAD4D, 0x80E0, 0xD058, 0x80E1, 0xAD4A, 0x80E3, 0xD05E,\n\t0x80E4, 0xAD4E, 0x80E5, 0xAD45, 0x80E6, 0xD066, 0x80ED, 0xAFDA,\n\t0x80EF, 0xAFE3, 0x80F0, 0xAFD8, 0x80F1, 0xAFD6, 0x80F2, 0xD36A,\n\t0x80F3, 0xAFDE, 0x80F4, 0xAFDB, 0x80F5, 0xD36C, 0x80F8, 0xAFDD,\n\t0x80F9, 0xD36B, 0x80FA, 0xD369, 0x80FB, 0xD36E, 0x80FC, 0xAFE2,\n\t0x80FD, 0xAFE0, 0x80FE, 0xDB48, 0x8100, 0xD36F, 0x8101, 0xD36D,\n\t0x8102, 0xAFD7, 0x8105, 0xAFD9, 0x8106, 0xAFDC, 0x8108, 0xAFDF,\n\t0x810A, 0xAFE1, 0x8115, 0xD74E, 0x8116, 0xB2E4, 0x8118, 0xD745,\n\t0x8119, 0xD747, 0x811B, 0xD748, 0x811D, 0xD750, 0x811E, 0xD74C,\n\t0x811F, 0xD74A, 0x8121, 0xD74D, 0x8122, 0xD751, 0x8123, 0xB2E5,\n\t0x8124, 0xB2E9, 0x8125, 0xD746, 0x8127, 0xD74F, 0x8129, 0xB2E7,\n\t0x812B, 0xB2E6, 0x812C, 0xD74B, 0x812D, 0xD749, 0x812F, 0xB2E3,\n\t0x8130, 0xB2E8, 0x8139, 0xB5C8, 0x813A, 0xDB51, 0x813D, 0xDB4F,\n\t0x813E, 0xB5CA, 0x8143, 0xDB4A, 0x8144, 0xDFA1, 0x8146, 0xB5C9,\n\t0x8147, 0xDB4E, 0x814A, 0xDB4B, 0x814B, 0xB5C5, 0x814C, 0xB5CB,\n\t0x814D, 0xDB50, 0x814E, 0xB5C7, 0x814F, 0xDB4D, 0x8150, 0xBB47,\n\t0x8151, 0xB5C6, 0x8152, 0xDB4C, 0x8153, 0xB5CC, 0x8154, 0xB5C4,\n\t0x8155, 0xB5C3, 0x815B, 0xDF77, 0x815C, 0xDF75, 0x815E, 0xDF7B,\n\t0x8160, 0xDF73, 0x8161, 0xDFA2, 0x8162, 0xDF78, 0x8164, 0xDF72,\n\t0x8165, 0xB87B, 0x8166, 0xB8A3, 0x8167, 0xDF7D, 0x8169, 0xDF76,\n\t0x816B, 0xB87E, 0x816E, 0xB87C, 0x816F, 0xDF7E, 0x8170, 0xB879,\n\t0x8171, 0xB878, 0x8172, 0xDF79, 0x8173, 0xB87D, 0x8174, 0xB5CD,\n\t0x8176, 0xDF7C, 0x8177, 0xDF74, 0x8178, 0xB87A, 0x8179, 0xB8A1,\n\t0x817A, 0xB8A2, 0x817F, 0xBB4C, 0x8180, 0xBB48, 0x8182, 0xBB4D,\n\t0x8183, 0xE3A6, 0x8186, 0xE3A5, 0x8187, 0xE3A7, 0x8188, 0xBB4A,\n\t0x8189, 0xE3A4, 0x818A, 0xBB4B, 0x818B, 0xE3AA, 0x818C, 0xE3A9,\n\t0x818D, 0xE3A8, 0x818F, 0xBB49, 0x8195, 0xE741, 0x8197, 0xE744,\n\t0x8198, 0xBDA8, 0x8199, 0xE743, 0x819A, 0xBDA7, 0x819B, 0xBDA3,\n\t0x819C, 0xBDA4, 0x819D, 0xBDA5, 0x819E, 0xE740, 0x819F, 0xE6FE,\n\t0x81A0, 0xBDA6, 0x81A2, 0xE742, 0x81A3, 0xE6FD, 0x81A6, 0xEAE9,\n\t0x81A7, 0xEAF3, 0x81A8, 0xBFB1, 0x81A9, 0xBFB0, 0x81AB, 0xEAED,\n\t0x81AC, 0xEAEF, 0x81AE, 0xEAEA, 0x81B0, 0xEAEE, 0x81B1, 0xEAE8,\n\t0x81B2, 0xEAF1, 0x81B3, 0xBFAF, 0x81B4, 0xEAF0, 0x81B5, 0xEAEC,\n\t0x81B7, 0xEAF2, 0x81B9, 0xEAEB, 0x81BA, 0xC174, 0x81BB, 0xEDE8,\n\t0x81BC, 0xEDEE, 0x81BD, 0xC178, 0x81BE, 0xC17A, 0x81BF, 0xC177,\n\t0x81C0, 0xC176, 0x81C2, 0xC175, 0x81C3, 0xC173, 0x81C4, 0xEDE9,\n\t0x81C5, 0xEDEC, 0x81C6, 0xC172, 0x81C7, 0xEDED, 0x81C9, 0xC179,\n\t0x81CA, 0xEDEB, 0x81CC, 0xEDEA, 0x81CD, 0xC2C0, 0x81CF, 0xC2C1,\n\t0x81D0, 0xF0A1, 0x81D1, 0xF07D, 0x81D2, 0xF07E, 0x81D5, 0xF2C2,\n\t0x81D7, 0xF2C1, 0x81D8, 0xC3BE, 0x81D9, 0xF4B4, 0x81DA, 0xC4A4,\n\t0x81DB, 0xF4B3, 0x81DD, 0xF5F0, 0x81DE, 0xF745, 0x81DF, 0xC5A6,\n\t0x81E0, 0xF943, 0x81E1, 0xF944, 0x81E2, 0xC5D8, 0x81E3, 0xA6DA,\n\t0x81E5, 0xAAD7, 0x81E6, 0xDB52, 0x81E7, 0xBB4E, 0x81E8, 0xC17B,\n\t0x81E9, 0xEDEF, 0x81EA, 0xA6DB, 0x81EC, 0xAFE5, 0x81ED, 0xAFE4,\n\t0x81EE, 0xDB53, 0x81F2, 0xEAF4, 0x81F3, 0xA6DC, 0x81F4, 0xAD50,\n\t0x81F7, 0xDB54, 0x81F8, 0xDB55, 0x81F9, 0xDB56, 0x81FA, 0xBB4F,\n\t0x81FB, 0xBFB2, 0x81FC, 0xA6DD, 0x81FE, 0xAAD8, 0x81FF, 0xD068,\n\t0x8200, 0xAFE6, 0x8201, 0xD370, 0x8202, 0xB2EA, 0x8204, 0xDB57,\n\t0x8205, 0xB8A4, 0x8207, 0xBB50, 0x8208, 0xBFB3, 0x8209, 0xC17C,\n\t0x820A, 0xC2C2, 0x820B, 0xF4B5, 0x820C, 0xA6DE, 0x820D, 0xAAD9,\n\t0x8210, 0xAFE7, 0x8211, 0xD752, 0x8212, 0xB5CE, 0x8214, 0xBB51,\n\t0x8215, 0xE3AB, 0x8216, 0xE745, 0x821B, 0xA6DF, 0x821C, 0xB5CF,\n\t0x821D, 0xDFA3, 0x821E, 0xBB52, 0x821F, 0xA6E0, 0x8220, 0xCDB1,\n\t0x8221, 0xD069, 0x8222, 0xAD51, 0x8225, 0xD372, 0x8228, 0xAFEA,\n\t0x822A, 0xAFE8, 0x822B, 0xAFE9, 0x822C, 0xAFEB, 0x822F, 0xD371,\n\t0x8232, 0xD757, 0x8233, 0xD754, 0x8234, 0xD756, 0x8235, 0xB2EB,\n\t0x8236, 0xB2ED, 0x8237, 0xB2EC, 0x8238, 0xD753, 0x8239, 0xB2EE,\n\t0x823A, 0xD755, 0x823C, 0xDB58, 0x823D, 0xDB59, 0x823F, 0xDB5A,\n\t0x8240, 0xDFA6, 0x8242, 0xDFA7, 0x8244, 0xDFA5, 0x8245, 0xDFA8,\n\t0x8247, 0xB8A5, 0x8249, 0xDFA4, 0x824B, 0xBB53, 0x824E, 0xE74A,\n\t0x824F, 0xE746, 0x8250, 0xE749, 0x8251, 0xE74B, 0x8252, 0xE748,\n\t0x8253, 0xE747, 0x8255, 0xEAF5, 0x8256, 0xEAF6, 0x8257, 0xEAF7,\n\t0x8258, 0xBFB4, 0x8259, 0xBFB5, 0x825A, 0xEDF1, 0x825B, 0xEDF0,\n\t0x825C, 0xEDF2, 0x825E, 0xF0A3, 0x825F, 0xF0A2, 0x8261, 0xF2C4,\n\t0x8263, 0xF2C5, 0x8264, 0xF2C3, 0x8266, 0xC4A5, 0x8268, 0xF4B6,\n\t0x8269, 0xF4B7, 0x826B, 0xF746, 0x826C, 0xF7EF, 0x826D, 0xF8BB,\n\t0x826E, 0xA6E1, 0x826F, 0xA87D, 0x8271, 0xC17D, 0x8272, 0xA6E2,\n\t0x8274, 0xD758, 0x8275, 0xDB5B, 0x8277, 0xC641, 0x8278, 0xCA4A,\n\t0x827C, 0xCA4B, 0x827D, 0xCA4D, 0x827E, 0xA6E3, 0x827F, 0xCA4E,\n\t0x8280, 0xCA4C, 0x8283, 0xCBA2, 0x8284, 0xCBA3, 0x8285, 0xCB7B,\n\t0x828A, 0xCBA1, 0x828B, 0xA8A1, 0x828D, 0xA8A2, 0x828E, 0xCB7C,\n\t0x828F, 0xCB7A, 0x8290, 0xCB79, 0x8291, 0xCB7D, 0x8292, 0xA87E,\n\t0x8293, 0xCB7E, 0x8294, 0xD06A, 0x8298, 0xCDB6, 0x8299, 0xAADC,\n\t0x829A, 0xCDB5, 0x829B, 0xCDB7, 0x829D, 0xAADB, 0x829E, 0xCDBC,\n\t0x829F, 0xAADF, 0x82A0, 0xCDB2, 0x82A1, 0xCDC0, 0x82A2, 0xCDC6,\n\t0x82A3, 0xAAE6, 0x82A4, 0xCDC3, 0x82A5, 0xAAE3, 0x82A7, 0xCDB9,\n\t0x82A8, 0xCDBF, 0x82A9, 0xCDC1, 0x82AB, 0xCDB4, 0x82AC, 0xAAE2,\n\t0x82AD, 0xAADD, 0x82AE, 0xCDBA, 0x82AF, 0xAAE4, 0x82B0, 0xAAE7,\n\t0x82B1, 0xAAE1, 0x82B3, 0xAADA, 0x82B4, 0xCDBE, 0x82B5, 0xCDB8,\n\t0x82B6, 0xCDC5, 0x82B7, 0xAAE9, 0x82B8, 0xAAE5, 0x82B9, 0xAAE0,\n\t0x82BA, 0xCDBD, 0x82BB, 0xAFEC, 0x82BC, 0xCDBB, 0x82BD, 0xAADE,\n\t0x82BE, 0xAAE8, 0x82C0, 0xCDB3, 0x82C2, 0xCDC2, 0x82C3, 0xCDC4,\n\t0x82D1, 0xAD62, 0x82D2, 0xAD5C, 0x82D3, 0xAD64, 0x82D4, 0xAD61,\n\t0x82D5, 0xD071, 0x82D6, 0xD074, 0x82D7, 0xAD5D, 0x82D9, 0xD06B,\n\t0x82DB, 0xAD56, 0x82DC, 0xAD60, 0x82DE, 0xAD63, 0x82DF, 0xAD65,\n\t0x82E0, 0xD0A2, 0x82E1, 0xD077, 0x82E3, 0xAD55, 0x82E4, 0xD0A1,\n\t0x82E5, 0xAD59, 0x82E6, 0xAD57, 0x82E7, 0xAD52, 0x82E8, 0xD06F,\n\t0x82EA, 0xD07E, 0x82EB, 0xD073, 0x82EC, 0xD076, 0x82ED, 0xD0A5,\n\t0x82EF, 0xAD66, 0x82F0, 0xD07D, 0x82F1, 0xAD5E, 0x82F2, 0xD078,\n\t0x82F3, 0xD0A4, 0x82F4, 0xD075, 0x82F5, 0xD079, 0x82F6, 0xD07C,\n\t0x82F9, 0xD06D, 0x82FA, 0xD0A3, 0x82FB, 0xD07B, 0x82FE, 0xD06C,\n\t0x8300, 0xD070, 0x8301, 0xAD5F, 0x8302, 0xAD5A, 0x8303, 0xAD53,\n\t0x8304, 0xAD58, 0x8305, 0xAD54, 0x8306, 0xAD67, 0x8307, 0xD06E,\n\t0x8308, 0xD3A5, 0x8309, 0xAD5B, 0x830C, 0xD07A, 0x830D, 0xCE41,\n\t0x8316, 0xD3A8, 0x8317, 0xAFFA, 0x8319, 0xD376, 0x831B, 0xD3A3,\n\t0x831C, 0xD37D, 0x831E, 0xD3B2, 0x8320, 0xD3AA, 0x8322, 0xD37E,\n\t0x8324, 0xD3A9, 0x8325, 0xD378, 0x8326, 0xD37C, 0x8327, 0xD3B5,\n\t0x8328, 0xAFFD, 0x8329, 0xD3AD, 0x832A, 0xD3A4, 0x832B, 0xAFED,\n\t0x832C, 0xD3B3, 0x832D, 0xD374, 0x832F, 0xD3AC, 0x8331, 0xAFFC,\n\t0x8332, 0xAFF7, 0x8333, 0xD373, 0x8334, 0xAFF5, 0x8335, 0xAFF4,\n\t0x8336, 0xAFF9, 0x8337, 0xD3AB, 0x8338, 0xAFF1, 0x8339, 0xAFF8,\n\t0x833A, 0xD072, 0x833B, 0xDB5C, 0x833C, 0xD3A6, 0x833F, 0xD37A,\n\t0x8340, 0xAFFB, 0x8341, 0xD37B, 0x8342, 0xD3A1, 0x8343, 0xAFFE,\n\t0x8344, 0xD375, 0x8345, 0xD3AF, 0x8347, 0xD3AE, 0x8348, 0xD3B6,\n\t0x8349, 0xAFF3, 0x834A, 0xAFF0, 0x834B, 0xD3B4, 0x834C, 0xD3B0,\n\t0x834D, 0xD3A7, 0x834E, 0xD3A2, 0x834F, 0xAFF6, 0x8350, 0xAFF2,\n\t0x8351, 0xD377, 0x8352, 0xAFEE, 0x8353, 0xD3B1, 0x8354, 0xAFEF,\n\t0x8356, 0xD379, 0x8373, 0xD75E, 0x8374, 0xD760, 0x8375, 0xD765,\n\t0x8376, 0xD779, 0x8377, 0xB2FC, 0x8378, 0xB2F2, 0x837A, 0xD75D,\n\t0x837B, 0xB2FD, 0x837C, 0xB2FE, 0x837D, 0xD768, 0x837E, 0xD76F,\n\t0x837F, 0xD775, 0x8381, 0xD762, 0x8383, 0xD769, 0x8386, 0xB340,\n\t0x8387, 0xD777, 0x8388, 0xD772, 0x8389, 0xB2FA, 0x838A, 0xB2F8,\n\t0x838B, 0xD76E, 0x838C, 0xD76A, 0x838D, 0xD75C, 0x838E, 0xB2EF,\n\t0x838F, 0xD761, 0x8390, 0xD759, 0x8392, 0xB2F7, 0x8393, 0xB2F9,\n\t0x8394, 0xD766, 0x8395, 0xD763, 0x8396, 0xB2F4, 0x8397, 0xD773,\n\t0x8398, 0xB2F1, 0x8399, 0xD764, 0x839A, 0xD77A, 0x839B, 0xD76C,\n\t0x839D, 0xD76B, 0x839E, 0xB2F0, 0x83A0, 0xB2FB, 0x83A2, 0xB2F3,\n\t0x83A3, 0xD75A, 0x83A4, 0xD75F, 0x83A5, 0xD770, 0x83A6, 0xD776,\n\t0x83A7, 0xB341, 0x83A8, 0xD75B, 0x83A9, 0xD767, 0x83AA, 0xD76D,\n\t0x83AB, 0xB2F6, 0x83AE, 0xD778, 0x83AF, 0xD771, 0x83B0, 0xD774,\n\t0x83BD, 0xB2F5, 0x83BF, 0xDB6C, 0x83C0, 0xDB60, 0x83C1, 0xB5D7,\n\t0x83C2, 0xDB7D, 0x83C3, 0xDBA7, 0x83C4, 0xDBAA, 0x83C5, 0xB5D5,\n\t0x83C6, 0xDB68, 0x83C7, 0xDBA3, 0x83C8, 0xDB69, 0x83C9, 0xDB77,\n\t0x83CA, 0xB5E2, 0x83CB, 0xDB73, 0x83CC, 0xB5DF, 0x83CE, 0xDB74,\n\t0x83CF, 0xDB5D, 0x83D1, 0xDBA4, 0x83D4, 0xB5E8, 0x83D5, 0xDBA1,\n\t0x83D6, 0xDB75, 0x83D7, 0xDBAC, 0x83D8, 0xDB70, 0x83D9, 0xDFC8,\n\t0x83DB, 0xDBAF, 0x83DC, 0xB5E6, 0x83DD, 0xDB6E, 0x83DE, 0xDB7A,\n\t0x83DF, 0xB5E9, 0x83E0, 0xB5D4, 0x83E1, 0xDB72, 0x83E2, 0xDBAD,\n\t0x83E3, 0xDB6B, 0x83E4, 0xDB64, 0x83E5, 0xDB6F, 0x83E7, 0xDB63,\n\t0x83E8, 0xDB61, 0x83E9, 0xB5D0, 0x83EA, 0xDBA5, 0x83EB, 0xDB6A,\n\t0x83EC, 0xDBA8, 0x83EE, 0xDBA9, 0x83EF, 0xB5D8, 0x83F0, 0xB5DD,\n\t0x83F1, 0xB5D9, 0x83F2, 0xB5E1, 0x83F3, 0xDB7E, 0x83F4, 0xB5DA,\n\t0x83F5, 0xDB76, 0x83F6, 0xDB66, 0x83F8, 0xB5D2, 0x83F9, 0xDB5E,\n\t0x83FA, 0xDBA2, 0x83FB, 0xDBAB, 0x83FC, 0xDB65, 0x83FD, 0xB5E0,\n\t0x83FE, 0xDBB0, 0x83FF, 0xDB71, 0x8401, 0xDB6D, 0x8403, 0xB5D1,\n\t0x8404, 0xB5E5, 0x8406, 0xDB7C, 0x8407, 0xB5E7, 0x8409, 0xDB78,\n\t0x840A, 0xB5DC, 0x840B, 0xB5D6, 0x840C, 0xB5DE, 0x840D, 0xB5D3,\n\t0x840E, 0xB5E4, 0x840F, 0xDB79, 0x8410, 0xDB67, 0x8411, 0xDB7B,\n\t0x8412, 0xDB62, 0x8413, 0xDBA6, 0x841B, 0xDBAE, 0x8423, 0xDB5F,\n\t0x8429, 0xDFC7, 0x842B, 0xDFDD, 0x842C, 0xB855, 0x842D, 0xDFCC,\n\t0x842F, 0xDFCA, 0x8430, 0xDFB5, 0x8431, 0xB8A9, 0x8432, 0xDFC5,\n\t0x8433, 0xDFD9, 0x8434, 0xDFC1, 0x8435, 0xB8B1, 0x8436, 0xDFD8,\n\t0x8437, 0xDFBF, 0x8438, 0xB5E3, 0x8439, 0xDFCF, 0x843A, 0xDFC0,\n\t0x843B, 0xDFD6, 0x843C, 0xB8B0, 0x843D, 0xB8A8, 0x843F, 0xDFAA,\n\t0x8440, 0xDFB2, 0x8442, 0xDFCB, 0x8443, 0xDFC3, 0x8444, 0xDFDC,\n\t0x8445, 0xDFC6, 0x8446, 0xB8B6, 0x8447, 0xDFD7, 0x8449, 0xB8AD,\n\t0x844B, 0xDFC9, 0x844C, 0xDFD1, 0x844D, 0xDFB6, 0x844E, 0xDFD0,\n\t0x8450, 0xDFE1, 0x8451, 0xDFB1, 0x8452, 0xDFD2, 0x8454, 0xDFDF,\n\t0x8456, 0xDFAB, 0x8457, 0xB5DB, 0x8459, 0xDFB9, 0x845A, 0xDFB8,\n\t0x845B, 0xB8AF, 0x845D, 0xDFBC, 0x845E, 0xDFBE, 0x845F, 0xDFCD,\n\t0x8460, 0xDFDE, 0x8461, 0xB8B2, 0x8463, 0xB8B3, 0x8465, 0xDFB0,\n\t0x8466, 0xB8AB, 0x8467, 0xDFB4, 0x8468, 0xDFDA, 0x8469, 0xB8B4,\n\t0x846B, 0xB8AC, 0x846C, 0xB8AE, 0x846D, 0xB8B5, 0x846E, 0xDFE0,\n\t0x846F, 0xDFD3, 0x8470, 0xDFCE, 0x8473, 0xDFBB, 0x8474, 0xDFBA,\n\t0x8475, 0xB8AA, 0x8476, 0xDFAC, 0x8477, 0xB8A7, 0x8478, 0xDFC4,\n\t0x8479, 0xDFAD, 0x847A, 0xDFC2, 0x847D, 0xDFB7, 0x847E, 0xDFDB,\n\t0x8482, 0xB8A6, 0x8486, 0xDFB3, 0x848D, 0xDFAF, 0x848E, 0xDFD5,\n\t0x848F, 0xDFAE, 0x8490, 0xBB60, 0x8491, 0xE3D3, 0x8494, 0xE3C2,\n\t0x8497, 0xE3AC, 0x8498, 0xE3CA, 0x8499, 0xBB58, 0x849A, 0xE3BB,\n\t0x849B, 0xE3C5, 0x849C, 0xBB5B, 0x849D, 0xE3BE, 0x849E, 0xBB59,\n\t0x849F, 0xE3AF, 0x84A0, 0xE3CD, 0x84A1, 0xE3AE, 0x84A2, 0xE3C1,\n\t0x84A4, 0xE3AD, 0x84A7, 0xE3BF, 0x84A8, 0xE3C8, 0x84A9, 0xE3C6,\n\t0x84AA, 0xE3BA, 0x84AB, 0xE3B5, 0x84AC, 0xE3B3, 0x84AE, 0xE3B4,\n\t0x84AF, 0xE3C7, 0x84B0, 0xE3D2, 0x84B1, 0xE3BC, 0x84B2, 0xBB5A,\n\t0x84B4, 0xE3B7, 0x84B6, 0xE3CB, 0x84B8, 0xBB5D, 0x84B9, 0xE3B6,\n\t0x84BA, 0xE3B0, 0x84BB, 0xE3C0, 0x84BC, 0xBB61, 0x84BF, 0xBB55,\n\t0x84C0, 0xBB5E, 0x84C1, 0xE3B8, 0x84C2, 0xE3B2, 0x84C4, 0xBB57,\n\t0x84C5, 0xDFD4, 0x84C6, 0xBB56, 0x84C7, 0xE3C3, 0x84C9, 0xBB54,\n\t0x84CA, 0xBB63, 0x84CB, 0xBB5C, 0x84CC, 0xE3C4, 0x84CD, 0xE3B9,\n\t0x84CE, 0xE3B1, 0x84CF, 0xE3CC, 0x84D0, 0xE3BD, 0x84D1, 0xBB62,\n\t0x84D2, 0xE3D0, 0x84D3, 0xBB5F, 0x84D4, 0xE3CF, 0x84D6, 0xE3C9,\n\t0x84D7, 0xE3CE, 0x84DB, 0xE3D1, 0x84E7, 0xE773, 0x84E8, 0xE774,\n\t0x84E9, 0xE767, 0x84EA, 0xE766, 0x84EB, 0xE762, 0x84EC, 0xBDB4,\n\t0x84EE, 0xBDAC, 0x84EF, 0xE776, 0x84F0, 0xE775, 0x84F1, 0xDFA9,\n\t0x84F2, 0xE75F, 0x84F3, 0xE763, 0x84F4, 0xE75D, 0x84F6, 0xE770,\n\t0x84F7, 0xE761, 0x84F9, 0xE777, 0x84FA, 0xE75A, 0x84FB, 0xE758,\n\t0x84FC, 0xE764, 0x84FD, 0xE76E, 0x84FE, 0xE769, 0x84FF, 0xBDB6,\n\t0x8500, 0xE74F, 0x8502, 0xE76D, 0x8506, 0xBDB7, 0x8507, 0xDFBD,\n\t0x8508, 0xE75B, 0x8509, 0xE752, 0x850A, 0xE755, 0x850B, 0xE77B,\n\t0x850C, 0xE75C, 0x850D, 0xE753, 0x850E, 0xE751, 0x850F, 0xE74E,\n\t0x8511, 0xBDB0, 0x8512, 0xE765, 0x8513, 0xBDAF, 0x8514, 0xBDB3,\n\t0x8515, 0xE760, 0x8516, 0xE768, 0x8517, 0xBDA9, 0x8518, 0xE778,\n\t0x8519, 0xE77C, 0x851A, 0xBDAB, 0x851C, 0xE757, 0x851D, 0xE76B,\n\t0x851E, 0xE76F, 0x851F, 0xE754, 0x8520, 0xE779, 0x8521, 0xBDB2,\n\t0x8523, 0xBDB1, 0x8524, 0xE74C, 0x8525, 0xBDB5, 0x8526, 0xE772,\n\t0x8527, 0xE756, 0x8528, 0xE76A, 0x8529, 0xE750, 0x852A, 0xE75E,\n\t0x852B, 0xE759, 0x852C, 0xBDAD, 0x852D, 0xBDAE, 0x852E, 0xE76C,\n\t0x852F, 0xE77D, 0x8530, 0xE77A, 0x8531, 0xE771, 0x853B, 0xE74D,\n\t0x853D, 0xBDAA, 0x853E, 0xEB49, 0x8540, 0xEB40, 0x8541, 0xEB43,\n\t0x8543, 0xBFBB, 0x8544, 0xEB45, 0x8545, 0xEAF9, 0x8546, 0xEB41,\n\t0x8547, 0xEB47, 0x8548, 0xBFB8, 0x8549, 0xBFBC, 0x854A, 0xBFB6,\n\t0x854D, 0xEAFB, 0x854E, 0xEB4C, 0x8551, 0xEB46, 0x8553, 0xEAFC,\n\t0x8554, 0xEB55, 0x8555, 0xEB4F, 0x8556, 0xEAF8, 0x8557, 0xEE46,\n\t0x8558, 0xEAFE, 0x8559, 0xBFB7, 0x855B, 0xEB4A, 0x855D, 0xEB54,\n\t0x855E, 0xBFBF, 0x8560, 0xEB51, 0x8561, 0xEAFD, 0x8562, 0xEB44,\n\t0x8563, 0xEB48, 0x8564, 0xEB42, 0x8565, 0xEB56, 0x8566, 0xEB53,\n\t0x8567, 0xEB50, 0x8568, 0xBFB9, 0x8569, 0xBFBA, 0x856A, 0xBFBE,\n\t0x856B, 0xEAFA, 0x856C, 0xEB57, 0x856D, 0xBFBD, 0x856E, 0xEB4D,\n\t0x8571, 0xEB4B, 0x8575, 0xEB4E, 0x8576, 0xEE53, 0x8577, 0xEE40,\n\t0x8578, 0xEE45, 0x8579, 0xEE52, 0x857A, 0xEE44, 0x857B, 0xEDFB,\n\t0x857C, 0xEE41, 0x857E, 0xC1A2, 0x8580, 0xEDF4, 0x8581, 0xEE4D,\n\t0x8582, 0xEE4F, 0x8583, 0xEDF3, 0x8584, 0xC1A1, 0x8585, 0xEE51,\n\t0x8586, 0xEE49, 0x8587, 0xC1A8, 0x8588, 0xEE50, 0x8589, 0xEE42,\n\t0x858A, 0xC1AA, 0x858B, 0xEDF9, 0x858C, 0xEB52, 0x858D, 0xEE4A,\n\t0x858E, 0xEE47, 0x858F, 0xEDF5, 0x8590, 0xEE55, 0x8591, 0xC1A4,\n\t0x8594, 0xC1A5, 0x8595, 0xEDF7, 0x8596, 0xEE48, 0x8598, 0xEE54,\n\t0x8599, 0xEE4B, 0x859A, 0xEDFD, 0x859B, 0xC1A7, 0x859C, 0xC1A3,\n\t0x859D, 0xEE4C, 0x859E, 0xEDFE, 0x859F, 0xEE56, 0x85A0, 0xEDF8,\n\t0x85A1, 0xEE43, 0x85A2, 0xEE4E, 0x85A3, 0xEDFA, 0x85A4, 0xEDFC,\n\t0x85A6, 0xC2CB, 0x85A7, 0xEDF6, 0x85A8, 0xC1A9, 0x85A9, 0xC2C4,\n\t0x85AA, 0xC17E, 0x85AF, 0xC1A6, 0x85B0, 0xC2C8, 0x85B1, 0xF0B3,\n\t0x85B3, 0xF0A9, 0x85B4, 0xF0A4, 0x85B5, 0xF0AA, 0x85B6, 0xF0B4,\n\t0x85B7, 0xF0B8, 0x85B8, 0xF0B7, 0x85B9, 0xC2CA, 0x85BA, 0xC2C9,\n\t0x85BD, 0xF0AB, 0x85BE, 0xF0B9, 0x85BF, 0xF0AE, 0x85C0, 0xF0A6,\n\t0x85C2, 0xF0A8, 0x85C3, 0xF0A7, 0x85C4, 0xF0AD, 0x85C5, 0xF0B2,\n\t0x85C6, 0xF0A5, 0x85C7, 0xF0AC, 0x85C8, 0xF0B1, 0x85C9, 0xC2C7,\n\t0x85CB, 0xF0AF, 0x85CD, 0xC2C5, 0x85CE, 0xF0B0, 0x85CF, 0xC2C3,\n\t0x85D0, 0xC2C6, 0x85D1, 0xF2D5, 0x85D2, 0xF0B5, 0x85D5, 0xC3C2,\n\t0x85D7, 0xF2CD, 0x85D8, 0xF2D1, 0x85D9, 0xF2C9, 0x85DA, 0xF2CC,\n\t0x85DC, 0xF2D4, 0x85DD, 0xC3C0, 0x85DE, 0xF2D9, 0x85DF, 0xF2D2,\n\t0x85E1, 0xF2CA, 0x85E2, 0xF2DA, 0x85E3, 0xF2D3, 0x85E4, 0xC3C3,\n\t0x85E5, 0xC3C4, 0x85E6, 0xF2D7, 0x85E8, 0xF2CB, 0x85E9, 0xC3BF,\n\t0x85EA, 0xC3C1, 0x85EB, 0xF2C6, 0x85EC, 0xF2CE, 0x85ED, 0xF2C8,\n\t0x85EF, 0xF2D8, 0x85F0, 0xF2D6, 0x85F1, 0xF2C7, 0x85F2, 0xF2CF,\n\t0x85F6, 0xF4BE, 0x85F7, 0xC3C5, 0x85F8, 0xF2D0, 0x85F9, 0xC4A7,\n\t0x85FA, 0xC4A9, 0x85FB, 0xC4A6, 0x85FD, 0xF4C3, 0x85FE, 0xF4BB,\n\t0x85FF, 0xF4B9, 0x8600, 0xF4BD, 0x8601, 0xF4BA, 0x8604, 0xF4BF,\n\t0x8605, 0xF4C1, 0x8606, 0xC4AA, 0x8607, 0xC4AC, 0x8609, 0xF4C0,\n\t0x860A, 0xC4AD, 0x860B, 0xC4AB, 0x860C, 0xF4C2, 0x8611, 0xC4A8,\n\t0x8617, 0xC4F4, 0x8618, 0xF5F1, 0x8619, 0xF5F7, 0x861A, 0xC4F6,\n\t0x861B, 0xF4BC, 0x861C, 0xF5F6, 0x861E, 0xF5FD, 0x861F, 0xF5F4,\n\t0x8620, 0xF5FB, 0x8621, 0xF5FA, 0x8622, 0xF4B8, 0x8623, 0xF5F5,\n\t0x8624, 0xF0B6, 0x8625, 0xF5FE, 0x8626, 0xF5F3, 0x8627, 0xF5F8,\n\t0x8629, 0xF5FC, 0x862A, 0xF5F2, 0x862C, 0xF74A, 0x862D, 0xC4F5,\n\t0x862E, 0xF5F9, 0x8631, 0xF7F4, 0x8632, 0xF74B, 0x8633, 0xF749,\n\t0x8634, 0xF747, 0x8635, 0xF748, 0x8636, 0xF74C, 0x8638, 0xC5D9,\n\t0x8639, 0xF7F2, 0x863A, 0xF7F0, 0x863B, 0xF7F5, 0x863C, 0xF7F3,\n\t0x863E, 0xF7F6, 0x863F, 0xC5DA, 0x8640, 0xF7F1, 0x8643, 0xF8BC,\n\t0x8646, 0xF945, 0x8647, 0xF946, 0x8648, 0xF947, 0x864B, 0xF9C7,\n\t0x864C, 0xF9BD, 0x864D, 0xCA4F, 0x864E, 0xAAEA, 0x8650, 0xAD68,\n\t0x8652, 0xD3B8, 0x8653, 0xD3B7, 0x8654, 0xB040, 0x8655, 0xB342,\n\t0x8656, 0xD77C, 0x8659, 0xD77B, 0x865B, 0xB5EA, 0x865C, 0xB8B8,\n\t0x865E, 0xB8B7, 0x865F, 0xB8B9, 0x8661, 0xE3D4, 0x8662, 0xE77E,\n\t0x8663, 0xEB58, 0x8664, 0xEB5A, 0x8665, 0xEB59, 0x8667, 0xC1AB,\n\t0x8668, 0xEE57, 0x8669, 0xF0BA, 0x866A, 0xF9A5, 0x866B, 0xA6E4,\n\t0x866D, 0xCDC9, 0x866E, 0xCDCA, 0x866F, 0xCDC8, 0x8670, 0xCDC7,\n\t0x8671, 0xAAEB, 0x8673, 0xD0A9, 0x8674, 0xD0A7, 0x8677, 0xD0A6,\n\t0x8679, 0xAD69, 0x867A, 0xAD6B, 0x867B, 0xAD6A, 0x867C, 0xD0A8,\n\t0x8685, 0xD3C4, 0x8686, 0xD3C1, 0x8687, 0xD3BF, 0x868A, 0xB041,\n\t0x868B, 0xD3C2, 0x868C, 0xB046, 0x868D, 0xD3BC, 0x868E, 0xD3CB,\n\t0x8690, 0xD3CD, 0x8691, 0xD3BD, 0x8693, 0xB043, 0x8694, 0xD3CE,\n\t0x8695, 0xD3C9, 0x8696, 0xD3BB, 0x8697, 0xD3C0, 0x8698, 0xD3CA,\n\t0x8699, 0xD3C6, 0x869A, 0xD3C3, 0x869C, 0xB048, 0x869D, 0xD3CC,\n\t0x869E, 0xD3BE, 0x86A1, 0xD3C7, 0x86A2, 0xD3B9, 0x86A3, 0xB047,\n\t0x86A4, 0xB044, 0x86A5, 0xD3C5, 0x86A7, 0xD3C8, 0x86A8, 0xD3BA,\n\t0x86A9, 0xB045, 0x86AA, 0xB042, 0x86AF, 0xB34C, 0x86B0, 0xD7A5,\n\t0x86B1, 0xB34B, 0x86B3, 0xD7A8, 0x86B4, 0xD7AB, 0x86B5, 0xB348,\n\t0x86B6, 0xB346, 0x86B7, 0xD77E, 0x86B8, 0xD7A9, 0x86B9, 0xD7A7,\n\t0x86BA, 0xD7A4, 0x86BB, 0xD7AC, 0x86BC, 0xD7AD, 0x86BD, 0xD7AF,\n\t0x86BE, 0xD7B0, 0x86BF, 0xD77D, 0x86C0, 0xB345, 0x86C1, 0xD7A2,\n\t0x86C2, 0xD7A1, 0x86C3, 0xD7AE, 0x86C4, 0xB347, 0x86C5, 0xD7A3,\n\t0x86C6, 0xB349, 0x86C7, 0xB344, 0x86C8, 0xD7A6, 0x86C9, 0xB34D,\n\t0x86CB, 0xB34A, 0x86CC, 0xD7AA, 0x86D0, 0xB5F1, 0x86D1, 0xDBBF,\n\t0x86D3, 0xDBB4, 0x86D4, 0xB5EE, 0x86D6, 0xDFE7, 0x86D7, 0xDBBD,\n\t0x86D8, 0xDBB1, 0x86D9, 0xB5EC, 0x86DA, 0xDBB6, 0x86DB, 0xB5EF,\n\t0x86DC, 0xDBBA, 0x86DD, 0xDBB8, 0x86DE, 0xB5F2, 0x86DF, 0xB5EB,\n\t0x86E2, 0xDBB2, 0x86E3, 0xDBB5, 0x86E4, 0xB5F0, 0x86E6, 0xDBB3,\n\t0x86E8, 0xDBBE, 0x86E9, 0xDBBC, 0x86EA, 0xDBB7, 0x86EB, 0xDBB9,\n\t0x86EC, 0xDBBB, 0x86ED, 0xB5ED, 0x86F5, 0xDFE8, 0x86F6, 0xDFEE,\n\t0x86F7, 0xDFE4, 0x86F8, 0xDFEA, 0x86F9, 0xB8BA, 0x86FA, 0xDFE6,\n\t0x86FB, 0xB8C0, 0x86FE, 0xB8BF, 0x8700, 0xB8BE, 0x8701, 0xDFED,\n\t0x8702, 0xB8C1, 0x8703, 0xB8C2, 0x8704, 0xDFE3, 0x8705, 0xDFF0,\n\t0x8706, 0xB8C3, 0x8707, 0xB8BD, 0x8708, 0xB8BC, 0x8709, 0xDFEC,\n\t0x870A, 0xB8C4, 0x870B, 0xDFE2, 0x870C, 0xDFE5, 0x870D, 0xDFEF,\n\t0x870E, 0xDFEB, 0x8711, 0xE3F4, 0x8712, 0xE3E9, 0x8713, 0xB8BB,\n\t0x8718, 0xBB6A, 0x8719, 0xE3DD, 0x871A, 0xE3F2, 0x871B, 0xE3DE,\n\t0x871C, 0xBB65, 0x871E, 0xE3DB, 0x8720, 0xE3E4, 0x8721, 0xE3DC,\n\t0x8722, 0xBB67, 0x8723, 0xE3D6, 0x8724, 0xE3F1, 0x8725, 0xBB68,\n\t0x8726, 0xE3EE, 0x8727, 0xE3EF, 0x8728, 0xE3D7, 0x8729, 0xBB6D,\n\t0x872A, 0xE3E6, 0x872C, 0xE3E0, 0x872D, 0xE3E7, 0x872E, 0xE3DA,\n\t0x8730, 0xE3F3, 0x8731, 0xE3EB, 0x8732, 0xE3E5, 0x8733, 0xE3D5,\n\t0x8734, 0xBB69, 0x8735, 0xE3EC, 0x8737, 0xBB6C, 0x8738, 0xE3F0,\n\t0x873A, 0xE3EA, 0x873B, 0xBB66, 0x873C, 0xE3E8, 0x873E, 0xE3E2,\n\t0x873F, 0xBB64, 0x8740, 0xE3D9, 0x8741, 0xE3E1, 0x8742, 0xE3ED,\n\t0x8743, 0xE3DF, 0x8746, 0xE3E3, 0x874C, 0xBDC1, 0x874D, 0xDFE9,\n\t0x874E, 0xE7B2, 0x874F, 0xE7BB, 0x8750, 0xE7B1, 0x8751, 0xE7AD,\n\t0x8752, 0xE7AA, 0x8753, 0xBDC2, 0x8754, 0xE7A8, 0x8755, 0xBB6B,\n\t0x8756, 0xE7A1, 0x8757, 0xBDC0, 0x8758, 0xE7A7, 0x8759, 0xBDBF,\n\t0x875A, 0xE7AC, 0x875B, 0xE7A9, 0x875C, 0xE7B9, 0x875D, 0xE7B4,\n\t0x875E, 0xE7AE, 0x875F, 0xE7B3, 0x8760, 0xBDBB, 0x8761, 0xE7AB,\n\t0x8762, 0xE7BE, 0x8763, 0xE7A2, 0x8764, 0xE7A3, 0x8765, 0xE7BA,\n\t0x8766, 0xBDBC, 0x8767, 0xE7BF, 0x8768, 0xBDBE, 0x8769, 0xE7C0,\n\t0x876A, 0xE7B0, 0x876B, 0xE3D8, 0x876C, 0xE7B6, 0x876D, 0xE7AF,\n\t0x876E, 0xE7B8, 0x876F, 0xE7B5, 0x8773, 0xE7A6, 0x8774, 0xBDB9,\n\t0x8775, 0xE7BD, 0x8776, 0xBDBA, 0x8777, 0xE7A4, 0x8778, 0xBDBD,\n\t0x8779, 0xEB64, 0x877A, 0xE7B7, 0x877B, 0xE7BC, 0x8781, 0xEB61,\n\t0x8782, 0xBDB8, 0x8783, 0xBFC0, 0x8784, 0xEB6B, 0x8785, 0xEB67,\n\t0x8787, 0xEB65, 0x8788, 0xEB60, 0x8789, 0xEB6F, 0x878D, 0xBFC4,\n\t0x878F, 0xEB5C, 0x8790, 0xEB68, 0x8791, 0xEB69, 0x8792, 0xEB5F,\n\t0x8793, 0xEB5E, 0x8794, 0xEB6C, 0x8796, 0xEB62, 0x8797, 0xEB5D,\n\t0x8798, 0xEB63, 0x879A, 0xEB6E, 0x879B, 0xEB5B, 0x879C, 0xEB6D,\n\t0x879D, 0xEB6A, 0x879E, 0xBFC2, 0x879F, 0xBFC1, 0x87A2, 0xBFC3,\n\t0x87A3, 0xEB66, 0x87A4, 0xF0CB, 0x87AA, 0xEE59, 0x87AB, 0xC1B1,\n\t0x87AC, 0xEE5D, 0x87AD, 0xEE5A, 0x87AE, 0xEE61, 0x87AF, 0xEE67,\n\t0x87B0, 0xEE5C, 0x87B2, 0xEE70, 0x87B3, 0xC1AE, 0x87B4, 0xEE6A,\n\t0x87B5, 0xEE5F, 0x87B6, 0xEE6B, 0x87B7, 0xEE66, 0x87B8, 0xEE6D,\n\t0x87B9, 0xEE5E, 0x87BA, 0xC1B3, 0x87BB, 0xC1B2, 0x87BC, 0xEE60,\n\t0x87BD, 0xEE6E, 0x87BE, 0xEE58, 0x87BF, 0xEE6C, 0x87C0, 0xC1AC,\n\t0x87C2, 0xEE64, 0x87C3, 0xEE63, 0x87C4, 0xEE68, 0x87C5, 0xEE5B,\n\t0x87C6, 0xC1B0, 0x87C8, 0xC1B4, 0x87C9, 0xEE62, 0x87CA, 0xEE69,\n\t0x87CB, 0xC1B5, 0x87CC, 0xEE65, 0x87D1, 0xC1AD, 0x87D2, 0xC1AF,\n\t0x87D3, 0xF0C7, 0x87D4, 0xF0C5, 0x87D7, 0xF0CC, 0x87D8, 0xF0C9,\n\t0x87D9, 0xF0CD, 0x87DB, 0xF0BE, 0x87DC, 0xF0C6, 0x87DD, 0xF0D1,\n\t0x87DE, 0xEE6F, 0x87DF, 0xF0C2, 0x87E0, 0xC2CF, 0x87E1, 0xE7A5,\n\t0x87E2, 0xF0BD, 0x87E3, 0xF0CA, 0x87E4, 0xF0C4, 0x87E5, 0xF0C1,\n\t0x87E6, 0xF0BC, 0x87E7, 0xF0BB, 0x87E8, 0xF0D0, 0x87EA, 0xF0C0,\n\t0x87EB, 0xF0BF, 0x87EC, 0xC2CD, 0x87ED, 0xF0C8, 0x87EF, 0xC2CC,\n\t0x87F2, 0xC2CE, 0x87F3, 0xF0C3, 0x87F4, 0xF0CF, 0x87F6, 0xF2DE,\n\t0x87F7, 0xF2DF, 0x87F9, 0xC3C9, 0x87FA, 0xF2DC, 0x87FB, 0xC3C6,\n\t0x87FC, 0xF2E4, 0x87FE, 0xC3CA, 0x87FF, 0xF2E6, 0x8800, 0xF2DB,\n\t0x8801, 0xF0CE, 0x8802, 0xF2E8, 0x8803, 0xF2DD, 0x8805, 0xC3C7,\n\t0x8806, 0xF2E3, 0x8808, 0xF2E5, 0x8809, 0xF2E0, 0x880A, 0xF2E7,\n\t0x880B, 0xF2E2, 0x880C, 0xF2E1, 0x880D, 0xC3C8, 0x8810, 0xF4C5,\n\t0x8811, 0xF4C6, 0x8813, 0xF4C8, 0x8814, 0xC4AE, 0x8815, 0xC4AF,\n\t0x8816, 0xF4C9, 0x8817, 0xF4C7, 0x8819, 0xF4C4, 0x881B, 0xF642,\n\t0x881C, 0xF645, 0x881D, 0xF641, 0x881F, 0xC4FA, 0x8820, 0xF643,\n\t0x8821, 0xC4F9, 0x8822, 0xC4F8, 0x8823, 0xC4F7, 0x8824, 0xF644,\n\t0x8825, 0xF751, 0x8826, 0xF74F, 0x8828, 0xF74E, 0x8829, 0xF640,\n\t0x882A, 0xF750, 0x882B, 0xF646, 0x882C, 0xF74D, 0x882E, 0xF7F9,\n\t0x882F, 0xF7D7, 0x8830, 0xF7F7, 0x8831, 0xC5DB, 0x8832, 0xF7F8,\n\t0x8833, 0xF7FA, 0x8835, 0xF8BF, 0x8836, 0xC5FA, 0x8837, 0xF8BE,\n\t0x8838, 0xF8BD, 0x8839, 0xC5FB, 0x883B, 0xC65A, 0x883C, 0xF96E,\n\t0x883D, 0xF9A7, 0x883E, 0xF9A6, 0x883F, 0xF9A8, 0x8840, 0xA6E5,\n\t0x8841, 0xD0AA, 0x8843, 0xD3CF, 0x8844, 0xD3D0, 0x8848, 0xDBC0,\n\t0x884A, 0xF647, 0x884B, 0xF8C0, 0x884C, 0xA6E6, 0x884D, 0xAD6C,\n\t0x884E, 0xD0AB, 0x8852, 0xD7B1, 0x8853, 0xB34E, 0x8855, 0xDBC2,\n\t0x8856, 0xDBC1, 0x8857, 0xB5F3, 0x8859, 0xB8C5, 0x885A, 0xE7C1,\n\t0x885B, 0xBDC3, 0x885D, 0xBDC4, 0x8861, 0xBFC5, 0x8862, 0xC5FC,\n\t0x8863, 0xA6E7, 0x8867, 0xD0AC, 0x8868, 0xAAED, 0x8869, 0xD0AE,\n\t0x886A, 0xD0AD, 0x886B, 0xAD6D, 0x886D, 0xD3D1, 0x886F, 0xD3D8,\n\t0x8870, 0xB049, 0x8871, 0xD3D6, 0x8872, 0xD3D4, 0x8874, 0xD3DB,\n\t0x8875, 0xD3D2, 0x8876, 0xD3D3, 0x8877, 0xB04A, 0x8879, 0xB04E,\n\t0x887C, 0xD3DC, 0x887D, 0xB04D, 0x887E, 0xD3DA, 0x887F, 0xD3D7,\n\t0x8880, 0xD3D5, 0x8881, 0xB04B, 0x8882, 0xB04C, 0x8883, 0xD3D9,\n\t0x8888, 0xB350, 0x8889, 0xD7B2, 0x888B, 0xB355, 0x888C, 0xD7C2,\n\t0x888D, 0xB354, 0x888E, 0xD7C4, 0x8891, 0xD7B8, 0x8892, 0xB352,\n\t0x8893, 0xD7C3, 0x8895, 0xD7B3, 0x8896, 0xB353, 0x8897, 0xD7BF,\n\t0x8898, 0xD7BB, 0x8899, 0xD7BD, 0x889A, 0xD7B7, 0x889B, 0xD7BE,\n\t0x889E, 0xB34F, 0x889F, 0xD7BA, 0x88A1, 0xD7B9, 0x88A2, 0xD7B5,\n\t0x88A4, 0xD7C0, 0x88A7, 0xD7BC, 0x88A8, 0xD7B4, 0x88AA, 0xD7B6,\n\t0x88AB, 0xB351, 0x88AC, 0xD7C1, 0x88B1, 0xB5F6, 0x88B2, 0xDBCD,\n\t0x88B6, 0xDBC9, 0x88B7, 0xDBCB, 0x88B8, 0xDBC6, 0x88B9, 0xDBC5,\n\t0x88BA, 0xDBC3, 0x88BC, 0xDBCA, 0x88BD, 0xDBCC, 0x88BE, 0xDBC8,\n\t0x88C0, 0xDBC7, 0x88C1, 0xB5F4, 0x88C2, 0xB5F5, 0x88C9, 0xDBCF,\n\t0x88CA, 0xB8CD, 0x88CB, 0xDFF2, 0x88CC, 0xDFF8, 0x88CD, 0xDFF3,\n\t0x88CE, 0xDFF4, 0x88CF, 0xF9D8, 0x88D0, 0xDFF9, 0x88D2, 0xB8CF,\n\t0x88D4, 0xB8C7, 0x88D5, 0xB8CE, 0x88D6, 0xDFF1, 0x88D7, 0xDBC4,\n\t0x88D8, 0xB8CA, 0x88D9, 0xB8C8, 0x88DA, 0xDFF7, 0x88DB, 0xDFF6,\n\t0x88DC, 0xB8C9, 0x88DD, 0xB8CB, 0x88DE, 0xDFF5, 0x88DF, 0xB8C6,\n\t0x88E1, 0xB8CC, 0x88E7, 0xE3F6, 0x88E8, 0xBB74, 0x88EB, 0xE442,\n\t0x88EC, 0xE441, 0x88EE, 0xE3FB, 0x88EF, 0xBB76, 0x88F0, 0xE440,\n\t0x88F1, 0xE3F7, 0x88F2, 0xE3F8, 0x88F3, 0xBB6E, 0x88F4, 0xBB70,\n\t0x88F6, 0xE3FD, 0x88F7, 0xE3F5, 0x88F8, 0xBB72, 0x88F9, 0xBB71,\n\t0x88FA, 0xE3F9, 0x88FB, 0xE3FE, 0x88FC, 0xE3FC, 0x88FD, 0xBB73,\n\t0x88FE, 0xE3FA, 0x8901, 0xDBCE, 0x8902, 0xBB6F, 0x8905, 0xE7C2,\n\t0x8906, 0xE7C9, 0x8907, 0xBDC6, 0x8909, 0xE7CD, 0x890A, 0xBDCA,\n\t0x890B, 0xE7C5, 0x890C, 0xE7C3, 0x890E, 0xE7CC, 0x8910, 0xBDC5,\n\t0x8911, 0xE7CB, 0x8912, 0xBDC7, 0x8913, 0xBDC8, 0x8914, 0xE7C4,\n\t0x8915, 0xBDC9, 0x8916, 0xE7CA, 0x8917, 0xE7C6, 0x8918, 0xE7C7,\n\t0x8919, 0xE7C8, 0x891A, 0xBB75, 0x891E, 0xEB70, 0x891F, 0xEB7C,\n\t0x8921, 0xBFCA, 0x8922, 0xEB77, 0x8923, 0xEB79, 0x8925, 0xBFC8,\n\t0x8926, 0xEB71, 0x8927, 0xEB75, 0x8929, 0xEB78, 0x892A, 0xBFC6,\n\t0x892B, 0xBFC9, 0x892C, 0xEB7B, 0x892D, 0xEB73, 0x892E, 0xEB74,\n\t0x892F, 0xEB7A, 0x8930, 0xEB72, 0x8931, 0xEB76, 0x8932, 0xBFC7,\n\t0x8933, 0xEE72, 0x8935, 0xEE71, 0x8936, 0xC1B7, 0x8937, 0xEE77,\n\t0x8938, 0xC1B9, 0x893B, 0xC1B6, 0x893C, 0xEE73, 0x893D, 0xC1BA,\n\t0x893E, 0xEE74, 0x8941, 0xEE75, 0x8942, 0xEE78, 0x8944, 0xC1B8,\n\t0x8946, 0xF0D6, 0x8949, 0xF0D9, 0x894B, 0xF0D3, 0x894C, 0xF0D5,\n\t0x894F, 0xF0D4, 0x8950, 0xF0D7, 0x8951, 0xF0D8, 0x8952, 0xEE76,\n\t0x8953, 0xF0D2, 0x8956, 0xC3CD, 0x8957, 0xF2EC, 0x8958, 0xF2EF,\n\t0x8959, 0xF2F1, 0x895A, 0xF2EA, 0x895B, 0xF2EB, 0x895C, 0xF2EE,\n\t0x895D, 0xF2F0, 0x895E, 0xC3CE, 0x895F, 0xC3CC, 0x8960, 0xC3CB,\n\t0x8961, 0xF2ED, 0x8962, 0xF2E9, 0x8963, 0xF4CA, 0x8964, 0xC4B0,\n\t0x8966, 0xF4CB, 0x8969, 0xF649, 0x896A, 0xC4FB, 0x896B, 0xF64B,\n\t0x896C, 0xC4FC, 0x896D, 0xF648, 0x896E, 0xF64A, 0x896F, 0xC5A8,\n\t0x8971, 0xF752, 0x8972, 0xC5A7, 0x8973, 0xF7FD, 0x8974, 0xF7FC,\n\t0x8976, 0xF7FB, 0x8979, 0xF948, 0x897A, 0xF949, 0x897B, 0xF94B,\n\t0x897C, 0xF94A, 0x897E, 0xCA50, 0x897F, 0xA6E8, 0x8981, 0xAD6E,\n\t0x8982, 0xD7C5, 0x8983, 0xB5F7, 0x8985, 0xDFFA, 0x8986, 0xC2D0,\n\t0x8988, 0xF2F2, 0x898B, 0xA8A3, 0x898F, 0xB357, 0x8993, 0xB356,\n\t0x8995, 0xDBD0, 0x8996, 0xB5F8, 0x8997, 0xDBD2, 0x8998, 0xDBD1,\n\t0x899B, 0xDFFB, 0x899C, 0xB8D0, 0x899D, 0xE443, 0x899E, 0xE446,\n\t0x899F, 0xE445, 0x89A1, 0xE444, 0x89A2, 0xE7CE, 0x89A3, 0xE7D0,\n\t0x89A4, 0xE7CF, 0x89A6, 0xBFCC, 0x89AA, 0xBFCB, 0x89AC, 0xC1BB,\n\t0x89AD, 0xEE79, 0x89AE, 0xEE7B, 0x89AF, 0xEE7A, 0x89B2, 0xC2D1,\n\t0x89B6, 0xF2F4, 0x89B7, 0xF2F3, 0x89B9, 0xF4CC, 0x89BA, 0xC4B1,\n\t0x89BD, 0xC4FD, 0x89BE, 0xF754, 0x89BF, 0xF753, 0x89C0, 0xC65B,\n\t0x89D2, 0xA8A4, 0x89D3, 0xD0AF, 0x89D4, 0xAD6F, 0x89D5, 0xD7C8,\n\t0x89D6, 0xD7C6, 0x89D9, 0xD7C7, 0x89DA, 0xDBD4, 0x89DB, 0xDBD5,\n\t0x89DC, 0xE043, 0x89DD, 0xDBD3, 0x89DF, 0xDFFC, 0x89E0, 0xE041,\n\t0x89E1, 0xE040, 0x89E2, 0xE042, 0x89E3, 0xB8D1, 0x89E4, 0xDFFE,\n\t0x89E5, 0xDFFD, 0x89E6, 0xE044, 0x89E8, 0xE449, 0x89E9, 0xE447,\n\t0x89EB, 0xE448, 0x89EC, 0xE7D3, 0x89ED, 0xE7D1, 0x89F0, 0xE7D2,\n\t0x89F1, 0xEB7D, 0x89F2, 0xEE7C, 0x89F3, 0xEE7D, 0x89F4, 0xC2D2,\n\t0x89F6, 0xF2F5, 0x89F7, 0xF4CD, 0x89F8, 0xC4B2, 0x89FA, 0xF64C,\n\t0x89FB, 0xF755, 0x89FC, 0xC5A9, 0x89FE, 0xF7FE, 0x89FF, 0xF94C,\n\t0x8A00, 0xA8A5, 0x8A02, 0xAD71, 0x8A03, 0xAD72, 0x8A04, 0xD0B0,\n\t0x8A07, 0xD0B1, 0x8A08, 0xAD70, 0x8A0A, 0xB054, 0x8A0C, 0xB052,\n\t0x8A0E, 0xB051, 0x8A0F, 0xB058, 0x8A10, 0xB050, 0x8A11, 0xB059,\n\t0x8A12, 0xD3DD, 0x8A13, 0xB056, 0x8A15, 0xB053, 0x8A16, 0xB057,\n\t0x8A17, 0xB055, 0x8A18, 0xB04F, 0x8A1B, 0xB35F, 0x8A1D, 0xB359,\n\t0x8A1E, 0xD7CC, 0x8A1F, 0xB35E, 0x8A22, 0xB360, 0x8A23, 0xB35A,\n\t0x8A25, 0xB35B, 0x8A27, 0xD7CA, 0x8A2A, 0xB358, 0x8A2C, 0xD7CB,\n\t0x8A2D, 0xB35D, 0x8A30, 0xD7C9, 0x8A31, 0xB35C, 0x8A34, 0xB644,\n\t0x8A36, 0xB646, 0x8A39, 0xDBD8, 0x8A3A, 0xB645, 0x8A3B, 0xB5F9,\n\t0x8A3C, 0xB5FD, 0x8A3E, 0xB8E4, 0x8A3F, 0xE049, 0x8A40, 0xDBDA,\n\t0x8A41, 0xB5FE, 0x8A44, 0xDBDD, 0x8A45, 0xDBDE, 0x8A46, 0xB643,\n\t0x8A48, 0xDBE0, 0x8A4A, 0xDBE2, 0x8A4C, 0xDBE3, 0x8A4D, 0xDBD7,\n\t0x8A4E, 0xDBD6, 0x8A4F, 0xDBE4, 0x8A50, 0xB642, 0x8A51, 0xDBE1,\n\t0x8A52, 0xDBDF, 0x8A54, 0xB640, 0x8A55, 0xB5FB, 0x8A56, 0xB647,\n\t0x8A57, 0xDBDB, 0x8A58, 0xDBDC, 0x8A59, 0xDBD9, 0x8A5B, 0xB641,\n\t0x8A5E, 0xB5FC, 0x8A60, 0xB5FA, 0x8A61, 0xE048, 0x8A62, 0xB8DF,\n\t0x8A63, 0xB8DA, 0x8A66, 0xB8D5, 0x8A68, 0xB8E5, 0x8A69, 0xB8D6,\n\t0x8A6B, 0xB8D2, 0x8A6C, 0xB8E1, 0x8A6D, 0xB8DE, 0x8A6E, 0xB8E0,\n\t0x8A70, 0xB8D7, 0x8A71, 0xB8DC, 0x8A72, 0xB8D3, 0x8A73, 0xB8D4,\n\t0x8A74, 0xE050, 0x8A75, 0xE04D, 0x8A76, 0xE045, 0x8A77, 0xE04A,\n\t0x8A79, 0xB8E2, 0x8A7A, 0xE051, 0x8A7B, 0xB8E3, 0x8A7C, 0xB8D9,\n\t0x8A7F, 0xE047, 0x8A81, 0xE04F, 0x8A82, 0xE04B, 0x8A83, 0xE04E,\n\t0x8A84, 0xE04C, 0x8A85, 0xB8DD, 0x8A86, 0xE046, 0x8A87, 0xB8D8,\n\t0x8A8B, 0xE44C, 0x8A8C, 0xBB78, 0x8A8D, 0xBB7B, 0x8A8F, 0xE44E,\n\t0x8A91, 0xBBA5, 0x8A92, 0xE44D, 0x8A93, 0xBB7D, 0x8A95, 0xBDCF,\n\t0x8A96, 0xE44F, 0x8A98, 0xBBA4, 0x8A99, 0xE44B, 0x8A9A, 0xBBA6,\n\t0x8A9E, 0xBB79, 0x8AA0, 0xB8DB, 0x8AA1, 0xBB7C, 0x8AA3, 0xBB7A,\n\t0x8AA4, 0xBB7E, 0x8AA5, 0xBBA2, 0x8AA6, 0xBB77, 0x8AA7, 0xBBA7,\n\t0x8AA8, 0xBBA3, 0x8AAA, 0xBBA1, 0x8AAB, 0xE44A, 0x8AB0, 0xBDD6,\n\t0x8AB2, 0xBDD2, 0x8AB6, 0xBDD9, 0x8AB8, 0xE7D6, 0x8AB9, 0xBDDA,\n\t0x8ABA, 0xE7E2, 0x8ABB, 0xE7DB, 0x8ABC, 0xBDCB, 0x8ABD, 0xE7E3,\n\t0x8ABE, 0xE7DD, 0x8ABF, 0xBDD5, 0x8AC0, 0xE7DE, 0x8AC2, 0xBDD4,\n\t0x8AC3, 0xE7E1, 0x8AC4, 0xBDCE, 0x8AC5, 0xE7DF, 0x8AC6, 0xE7D5,\n\t0x8AC7, 0xBDCD, 0x8AC8, 0xEBAA, 0x8AC9, 0xBDD3, 0x8ACB, 0xBDD0,\n\t0x8ACD, 0xBDD8, 0x8ACF, 0xE7D4, 0x8AD1, 0xE7D8, 0x8AD2, 0xBDCC,\n\t0x8AD3, 0xE7D7, 0x8AD4, 0xE7D9, 0x8AD5, 0xE7DA, 0x8AD6, 0xBDD7,\n\t0x8AD7, 0xE7DC, 0x8AD8, 0xE7E0, 0x8AD9, 0xE7E4, 0x8ADB, 0xBDDB,\n\t0x8ADC, 0xBFD2, 0x8ADD, 0xEBA5, 0x8ADE, 0xEBAB, 0x8ADF, 0xEBA8,\n\t0x8AE0, 0xEB7E, 0x8AE1, 0xEBAC, 0x8AE2, 0xEBA1, 0x8AE4, 0xEBA7,\n\t0x8AE6, 0xBFCD, 0x8AE7, 0xBFD3, 0x8AE8, 0xEBAD, 0x8AEB, 0xBFCF,\n\t0x8AED, 0xBFD9, 0x8AEE, 0xBFD4, 0x8AEF, 0xEBAF, 0x8AF0, 0xEBA9,\n\t0x8AF1, 0xBFD0, 0x8AF2, 0xEBA2, 0x8AF3, 0xBFDA, 0x8AF4, 0xEBA3,\n\t0x8AF5, 0xEBA4, 0x8AF6, 0xBFDB, 0x8AF7, 0xBFD8, 0x8AF8, 0xBDD1,\n\t0x8AFA, 0xBFCE, 0x8AFB, 0xEBB0, 0x8AFC, 0xBFDC, 0x8AFE, 0xBFD5,\n\t0x8AFF, 0xEBAE, 0x8B00, 0xBFD1, 0x8B01, 0xBFD6, 0x8B02, 0xBFD7,\n\t0x8B04, 0xC1C3, 0x8B05, 0xEEA4, 0x8B06, 0xEEAD, 0x8B07, 0xEEAA,\n\t0x8B08, 0xEEAC, 0x8B0A, 0xC1C0, 0x8B0B, 0xEEA5, 0x8B0D, 0xEEAB,\n\t0x8B0E, 0xC1BC, 0x8B0F, 0xEEA7, 0x8B10, 0xC1C4, 0x8B11, 0xEEA3,\n\t0x8B12, 0xEEA8, 0x8B13, 0xEEAF, 0x8B14, 0xEBA6, 0x8B15, 0xEEA9,\n\t0x8B16, 0xEEA2, 0x8B17, 0xC1BD, 0x8B18, 0xEEA1, 0x8B19, 0xC1BE,\n\t0x8B1A, 0xEEB0, 0x8B1B, 0xC1BF, 0x8B1C, 0xEEAE, 0x8B1D, 0xC1C2,\n\t0x8B1E, 0xEE7E, 0x8B20, 0xC1C1, 0x8B22, 0xEEA6, 0x8B23, 0xF0DC,\n\t0x8B24, 0xF0EA, 0x8B25, 0xF0E5, 0x8B26, 0xF0E7, 0x8B27, 0xF0DB,\n\t0x8B28, 0xC2D3, 0x8B2A, 0xF0DA, 0x8B2B, 0xC2D6, 0x8B2C, 0xC2D5,\n\t0x8B2E, 0xF0E9, 0x8B2F, 0xF0E1, 0x8B30, 0xF0DE, 0x8B31, 0xF0E4,\n\t0x8B33, 0xF0DD, 0x8B35, 0xF0DF, 0x8B36, 0xF0E8, 0x8B37, 0xF0E6,\n\t0x8B39, 0xC2D4, 0x8B3A, 0xF0ED, 0x8B3B, 0xF0EB, 0x8B3C, 0xF0E2,\n\t0x8B3D, 0xF0EC, 0x8B3E, 0xF0E3, 0x8B40, 0xF2F9, 0x8B41, 0xC3CF,\n\t0x8B42, 0xF341, 0x8B45, 0xF64F, 0x8B46, 0xC3D6, 0x8B47, 0xF0E0,\n\t0x8B48, 0xF2F7, 0x8B49, 0xC3D2, 0x8B4A, 0xF2F8, 0x8B4B, 0xF2FD,\n\t0x8B4E, 0xC3D4, 0x8B4F, 0xC3D5, 0x8B50, 0xF2F6, 0x8B51, 0xF340,\n\t0x8B52, 0xF342, 0x8B53, 0xF2FA, 0x8B54, 0xF2FC, 0x8B55, 0xF2FE,\n\t0x8B56, 0xF2FB, 0x8B57, 0xF343, 0x8B58, 0xC3D1, 0x8B59, 0xC3D7,\n\t0x8B5A, 0xC3D3, 0x8B5C, 0xC3D0, 0x8B5D, 0xF4D0, 0x8B5F, 0xC4B7,\n\t0x8B60, 0xF4CE, 0x8B63, 0xF4D2, 0x8B65, 0xF4D3, 0x8B66, 0xC4B5,\n\t0x8B67, 0xF4D4, 0x8B68, 0xF4D1, 0x8B6A, 0xF4CF, 0x8B6B, 0xC4B8,\n\t0x8B6C, 0xC4B4, 0x8B6D, 0xF4D5, 0x8B6F, 0xC4B6, 0x8B70, 0xC4B3,\n\t0x8B74, 0xC4FE, 0x8B77, 0xC540, 0x8B78, 0xF64E, 0x8B79, 0xF64D,\n\t0x8B7A, 0xF650, 0x8B7B, 0xF651, 0x8B7D, 0xC541, 0x8B7E, 0xF756,\n\t0x8B7F, 0xF75B, 0x8B80, 0xC5AA, 0x8B82, 0xF758, 0x8B84, 0xF757,\n\t0x8B85, 0xF75A, 0x8B86, 0xF759, 0x8B88, 0xF843, 0x8B8A, 0xC5DC,\n\t0x8B8B, 0xF842, 0x8B8C, 0xF840, 0x8B8E, 0xF841, 0x8B92, 0xC5FE,\n\t0x8B93, 0xC5FD, 0x8B94, 0xF8C1, 0x8B95, 0xF8C2, 0x8B96, 0xC640,\n\t0x8B98, 0xF94D, 0x8B99, 0xF94E, 0x8B9A, 0xC667, 0x8B9C, 0xC66D,\n\t0x8B9E, 0xF9A9, 0x8B9F, 0xF9C8, 0x8C37, 0xA8A6, 0x8C39, 0xD7CD,\n\t0x8C3B, 0xD7CE, 0x8C3C, 0xE052, 0x8C3D, 0xE450, 0x8C3E, 0xE7E5,\n\t0x8C3F, 0xC1C6, 0x8C41, 0xC1C5, 0x8C42, 0xF0EE, 0x8C43, 0xF344,\n\t0x8C45, 0xF844, 0x8C46, 0xA8A7, 0x8C47, 0xD3DE, 0x8C48, 0xB05A,\n\t0x8C49, 0xB361, 0x8C4A, 0xE054, 0x8C4B, 0xE053, 0x8C4C, 0xBDDC,\n\t0x8C4D, 0xE7E6, 0x8C4E, 0xBDDD, 0x8C4F, 0xEEB1, 0x8C50, 0xC2D7,\n\t0x8C54, 0xC676, 0x8C55, 0xA8A8, 0x8C56, 0xCDCB, 0x8C57, 0xD3DF,\n\t0x8C5A, 0xB362, 0x8C5C, 0xD7CF, 0x8C5D, 0xD7D0, 0x8C5F, 0xDBE5,\n\t0x8C61, 0xB648, 0x8C62, 0xB8E6, 0x8C64, 0xE056, 0x8C65, 0xE055,\n\t0x8C66, 0xE057, 0x8C68, 0xE451, 0x8C69, 0xE452, 0x8C6A, 0xBBA8,\n\t0x8C6B, 0xBFDD, 0x8C6C, 0xBDDE, 0x8C6D, 0xBFDE, 0x8C6F, 0xEEB5,\n\t0x8C70, 0xEEB2, 0x8C71, 0xEEB4, 0x8C72, 0xEEB3, 0x8C73, 0xC1C7,\n\t0x8C75, 0xF0EF, 0x8C76, 0xF346, 0x8C77, 0xF345, 0x8C78, 0xCBA4,\n\t0x8C79, 0xB05C, 0x8C7A, 0xB05B, 0x8C7B, 0xD3E0, 0x8C7D, 0xD7D1,\n\t0x8C80, 0xDBE7, 0x8C81, 0xDBE6, 0x8C82, 0xB649, 0x8C84, 0xE059,\n\t0x8C85, 0xE05A, 0x8C86, 0xE058, 0x8C89, 0xB8E8, 0x8C8A, 0xB8E7,\n\t0x8C8C, 0xBBAA, 0x8C8D, 0xBBA9, 0x8C8F, 0xE7E7, 0x8C90, 0xEBB3,\n\t0x8C91, 0xEBB1, 0x8C92, 0xEBB2, 0x8C93, 0xBFDF, 0x8C94, 0xEEB7,\n\t0x8C95, 0xEEB6, 0x8C97, 0xF0F2, 0x8C98, 0xF0F1, 0x8C99, 0xF0F0,\n\t0x8C9A, 0xF347, 0x8C9C, 0xF9AA, 0x8C9D, 0xA8A9, 0x8C9E, 0xAD73,\n\t0x8CA0, 0xAD74, 0x8CA1, 0xB05D, 0x8CA2, 0xB05E, 0x8CA3, 0xD3E2,\n\t0x8CA4, 0xD3E1, 0x8CA5, 0xD7D2, 0x8CA7, 0xB368, 0x8CA8, 0xB366,\n\t0x8CA9, 0xB363, 0x8CAA, 0xB367, 0x8CAB, 0xB365, 0x8CAC, 0xB364,\n\t0x8CAF, 0xB64A, 0x8CB0, 0xDBEA, 0x8CB2, 0xB8ED, 0x8CB3, 0xB64C,\n\t0x8CB4, 0xB651, 0x8CB5, 0xDBEC, 0x8CB6, 0xB653, 0x8CB7, 0xB652,\n\t0x8CB8, 0xB655, 0x8CB9, 0xDBEB, 0x8CBA, 0xDBE8, 0x8CBB, 0xB64F,\n\t0x8CBC, 0xB64B, 0x8CBD, 0xB64D, 0x8CBE, 0xDBE9, 0x8CBF, 0xB654,\n\t0x8CC0, 0xB650, 0x8CC1, 0xB64E, 0x8CC2, 0xB8EF, 0x8CC3, 0xB8EE,\n\t0x8CC4, 0xB8EC, 0x8CC5, 0xB8F0, 0x8CC7, 0xB8EA, 0x8CC8, 0xB8EB,\n\t0x8CCA, 0xB8E9, 0x8CCC, 0xE05B, 0x8CCF, 0xE454, 0x8CD1, 0xBBAC,\n\t0x8CD2, 0xBBAD, 0x8CD3, 0xBBAB, 0x8CD5, 0xE453, 0x8CD7, 0xE455,\n\t0x8CD9, 0xE7EA, 0x8CDA, 0xE7EC, 0x8CDC, 0xBDE7, 0x8CDD, 0xE7ED,\n\t0x8CDE, 0xBDE0, 0x8CDF, 0xE7E9, 0x8CE0, 0xBDDF, 0x8CE1, 0xBDE9,\n\t0x8CE2, 0xBDE5, 0x8CE3, 0xBDE6, 0x8CE4, 0xBDE2, 0x8CE5, 0xE7E8,\n\t0x8CE6, 0xBDE1, 0x8CE7, 0xE7EE, 0x8CE8, 0xE7EB, 0x8CEA, 0xBDE8,\n\t0x8CEC, 0xBDE3, 0x8CED, 0xBDE4, 0x8CEE, 0xEBB5, 0x8CF0, 0xEBB7,\n\t0x8CF1, 0xEBB6, 0x8CF3, 0xEBB8, 0x8CF4, 0xBFE0, 0x8CF5, 0xEBB4,\n\t0x8CF8, 0xC1CB, 0x8CF9, 0xEEB8, 0x8CFA, 0xC1C8, 0x8CFB, 0xC1CC,\n\t0x8CFC, 0xC1CA, 0x8CFD, 0xC1C9, 0x8CFE, 0xF0F3, 0x8D00, 0xF0F6,\n\t0x8D02, 0xF0F5, 0x8D04, 0xF0F4, 0x8D05, 0xC2D8, 0x8D06, 0xF348,\n\t0x8D07, 0xF349, 0x8D08, 0xC3D8, 0x8D09, 0xF34A, 0x8D0A, 0xC3D9,\n\t0x8D0D, 0xC4BA, 0x8D0F, 0xC4B9, 0x8D10, 0xF652, 0x8D13, 0xC542,\n\t0x8D14, 0xF653, 0x8D15, 0xF75C, 0x8D16, 0xC5AB, 0x8D17, 0xC5AC,\n\t0x8D19, 0xF845, 0x8D1B, 0xC642, 0x8D64, 0xA8AA, 0x8D66, 0xB36A,\n\t0x8D67, 0xB369, 0x8D68, 0xE05C, 0x8D69, 0xE05D, 0x8D6B, 0xBBAE,\n\t0x8D6C, 0xEBB9, 0x8D6D, 0xBDEA, 0x8D6E, 0xEBBA, 0x8D6F, 0xEEB9,\n\t0x8D70, 0xA8AB, 0x8D72, 0xD0B2, 0x8D73, 0xAD76, 0x8D74, 0xAD75,\n\t0x8D76, 0xD3E3, 0x8D77, 0xB05F, 0x8D78, 0xD3E4, 0x8D79, 0xD7D5,\n\t0x8D7B, 0xD7D4, 0x8D7D, 0xD7D3, 0x8D80, 0xDBEE, 0x8D81, 0xB658,\n\t0x8D84, 0xDBED, 0x8D85, 0xB657, 0x8D89, 0xDBEF, 0x8D8A, 0xB656,\n\t0x8D8C, 0xE05F, 0x8D8D, 0xE062, 0x8D8E, 0xE060, 0x8D8F, 0xE061,\n\t0x8D90, 0xE065, 0x8D91, 0xE05E, 0x8D92, 0xE066, 0x8D93, 0xE063,\n\t0x8D94, 0xE064, 0x8D95, 0xBBB0, 0x8D96, 0xE456, 0x8D99, 0xBBAF,\n\t0x8D9B, 0xE7F2, 0x8D9C, 0xE7F0, 0x8D9F, 0xBDEB, 0x8DA0, 0xE7EF,\n\t0x8DA1, 0xE7F1, 0x8DA3, 0xBDEC, 0x8DA5, 0xEBBB, 0x8DA7, 0xEBBC,\n\t0x8DA8, 0xC1CD, 0x8DAA, 0xF34C, 0x8DAB, 0xF34E, 0x8DAC, 0xF34B,\n\t0x8DAD, 0xF34D, 0x8DAE, 0xF4D6, 0x8DAF, 0xF654, 0x8DB2, 0xF96F,\n\t0x8DB3, 0xA8AC, 0x8DB4, 0xAD77, 0x8DB5, 0xD3E5, 0x8DB6, 0xD3E7,\n\t0x8DB7, 0xD3E6, 0x8DB9, 0xD7D8, 0x8DBA, 0xB36C, 0x8DBC, 0xD7D6,\n\t0x8DBE, 0xB36B, 0x8DBF, 0xD7D9, 0x8DC1, 0xD7DA, 0x8DC2, 0xD7D7,\n\t0x8DC5, 0xDBFB, 0x8DC6, 0xB660, 0x8DC7, 0xDBF3, 0x8DC8, 0xDBF9,\n\t0x8DCB, 0xB65B, 0x8DCC, 0xB65E, 0x8DCD, 0xDBF2, 0x8DCE, 0xB659,\n\t0x8DCF, 0xDBF6, 0x8DD0, 0xE06C, 0x8DD1, 0xB65D, 0x8DD3, 0xDBF1,\n\t0x8DD5, 0xDBF7, 0x8DD6, 0xDBF4, 0x8DD7, 0xDBFA, 0x8DD8, 0xDBF0,\n\t0x8DD9, 0xDBF8, 0x8DDA, 0xB65C, 0x8DDB, 0xB65F, 0x8DDC, 0xDBF5,\n\t0x8DDD, 0xB65A, 0x8DDF, 0xB8F2, 0x8DE0, 0xE068, 0x8DE1, 0xB8F1,\n\t0x8DE2, 0xE06F, 0x8DE3, 0xE06E, 0x8DE4, 0xB8F8, 0x8DE6, 0xB8F9,\n\t0x8DE7, 0xE070, 0x8DE8, 0xB8F3, 0x8DE9, 0xE06D, 0x8DEA, 0xB8F7,\n\t0x8DEB, 0xE072, 0x8DEC, 0xE069, 0x8DEE, 0xE06B, 0x8DEF, 0xB8F4,\n\t0x8DF0, 0xE067, 0x8DF1, 0xE06A, 0x8DF2, 0xE071, 0x8DF3, 0xB8F5,\n\t0x8DF4, 0xE073, 0x8DFA, 0xB8F6, 0x8DFC, 0xBBB1, 0x8DFD, 0xE45B,\n\t0x8DFE, 0xE461, 0x8DFF, 0xE459, 0x8E00, 0xE462, 0x8E02, 0xE458,\n\t0x8E03, 0xE45D, 0x8E04, 0xE463, 0x8E05, 0xE460, 0x8E06, 0xE45F,\n\t0x8E07, 0xE45E, 0x8E09, 0xE457, 0x8E0A, 0xE45C, 0x8E0D, 0xE45A,\n\t0x8E0F, 0xBDF1, 0x8E10, 0xBDEE, 0x8E11, 0xE7FB, 0x8E12, 0xE841,\n\t0x8E13, 0xE843, 0x8E14, 0xE840, 0x8E15, 0xE7F8, 0x8E16, 0xE7FA,\n\t0x8E17, 0xE845, 0x8E18, 0xE842, 0x8E19, 0xE7FC, 0x8E1A, 0xE846,\n\t0x8E1B, 0xE7F9, 0x8E1C, 0xE844, 0x8E1D, 0xBDEF, 0x8E1E, 0xBDF5,\n\t0x8E1F, 0xBDF3, 0x8E20, 0xE7F3, 0x8E21, 0xBDF4, 0x8E22, 0xBDF0,\n\t0x8E23, 0xE7F4, 0x8E24, 0xE7F6, 0x8E25, 0xE7F5, 0x8E26, 0xE7FD,\n\t0x8E27, 0xE7FE, 0x8E29, 0xBDF2, 0x8E2B, 0xBDED, 0x8E2E, 0xE7F7,\n\t0x8E30, 0xEBC6, 0x8E31, 0xBFE2, 0x8E33, 0xEBBD, 0x8E34, 0xBFE3,\n\t0x8E35, 0xBFE6, 0x8E36, 0xEBC2, 0x8E38, 0xEBBF, 0x8E39, 0xBFE5,\n\t0x8E3C, 0xEBC3, 0x8E3D, 0xEBC4, 0x8E3E, 0xEBBE, 0x8E3F, 0xEBC7,\n\t0x8E40, 0xEBC0, 0x8E41, 0xEBC5, 0x8E42, 0xBFE4, 0x8E44, 0xBFE1,\n\t0x8E45, 0xEBC1, 0x8E47, 0xEEBF, 0x8E48, 0xC1D0, 0x8E49, 0xC1CE,\n\t0x8E4A, 0xC1D1, 0x8E4B, 0xC1CF, 0x8E4C, 0xEEBE, 0x8E4D, 0xEEBB,\n\t0x8E4E, 0xEEBA, 0x8E50, 0xEEBD, 0x8E53, 0xEEBC, 0x8E54, 0xF145,\n\t0x8E55, 0xC2DE, 0x8E56, 0xF0FB, 0x8E57, 0xF0FA, 0x8E59, 0xC2D9,\n\t0x8E5A, 0xF141, 0x8E5B, 0xF140, 0x8E5C, 0xF0F7, 0x8E5D, 0xF143,\n\t0x8E5E, 0xF0FC, 0x8E5F, 0xC2DD, 0x8E60, 0xF0F9, 0x8E61, 0xF142,\n\t0x8E62, 0xF0F8, 0x8E63, 0xC2DA, 0x8E64, 0xC2DC, 0x8E65, 0xF0FD,\n\t0x8E66, 0xC2DB, 0x8E67, 0xF0FE, 0x8E69, 0xF144, 0x8E6A, 0xF352,\n\t0x8E6C, 0xC3DE, 0x8E6D, 0xF34F, 0x8E6F, 0xF353, 0x8E72, 0xC3DB,\n\t0x8E73, 0xF351, 0x8E74, 0xC3E0, 0x8E76, 0xC3DD, 0x8E78, 0xF350,\n\t0x8E7A, 0xC3DF, 0x8E7B, 0xF354, 0x8E7C, 0xC3DA, 0x8E81, 0xC4BC,\n\t0x8E82, 0xC4BE, 0x8E84, 0xF4D9, 0x8E85, 0xC4BD, 0x8E86, 0xF4D7,\n\t0x8E87, 0xC3DC, 0x8E88, 0xF4D8, 0x8E89, 0xC4BB, 0x8E8A, 0xC543,\n\t0x8E8B, 0xC545, 0x8E8C, 0xF656, 0x8E8D, 0xC544, 0x8E8E, 0xF655,\n\t0x8E90, 0xF761, 0x8E91, 0xC5AD, 0x8E92, 0xF760, 0x8E93, 0xC5AE,\n\t0x8E94, 0xF75E, 0x8E95, 0xF75D, 0x8E96, 0xF762, 0x8E97, 0xF763,\n\t0x8E98, 0xF846, 0x8E9A, 0xF75F, 0x8E9D, 0xF8C6, 0x8E9E, 0xF8C3,\n\t0x8E9F, 0xF8C4, 0x8EA0, 0xF8C5, 0x8EA1, 0xC65C, 0x8EA3, 0xF951,\n\t0x8EA4, 0xF950, 0x8EA5, 0xF94F, 0x8EA6, 0xF970, 0x8EA8, 0xF9BE,\n\t0x8EA9, 0xF9AB, 0x8EAA, 0xC66E, 0x8EAB, 0xA8AD, 0x8EAC, 0xB060,\n\t0x8EB2, 0xB8FA, 0x8EBA, 0xBDF6, 0x8EBD, 0xEBC8, 0x8EC0, 0xC2DF,\n\t0x8EC2, 0xF355, 0x8EC9, 0xF9AC, 0x8ECA, 0xA8AE, 0x8ECB, 0xAAEE,\n\t0x8ECC, 0xAD79, 0x8ECD, 0xAD78, 0x8ECF, 0xB063, 0x8ED1, 0xD3E8,\n\t0x8ED2, 0xB061, 0x8ED3, 0xD3E9, 0x8ED4, 0xB062, 0x8ED7, 0xD7DF,\n\t0x8ED8, 0xD7DB, 0x8EDB, 0xB36D, 0x8EDC, 0xD7DE, 0x8EDD, 0xD7DD,\n\t0x8EDE, 0xD7DC, 0x8EDF, 0xB36E, 0x8EE0, 0xD7E0, 0x8EE1, 0xD7E1,\n\t0x8EE5, 0xDC43, 0x8EE6, 0xDC41, 0x8EE7, 0xDC45, 0x8EE8, 0xDC46,\n\t0x8EE9, 0xDC4C, 0x8EEB, 0xDC48, 0x8EEC, 0xDC4A, 0x8EEE, 0xDC42,\n\t0x8EEF, 0xDBFC, 0x8EF1, 0xDC49, 0x8EF4, 0xDC4B, 0x8EF5, 0xDC44,\n\t0x8EF6, 0xDC47, 0x8EF7, 0xDBFD, 0x8EF8, 0xB662, 0x8EF9, 0xDC40,\n\t0x8EFA, 0xDBFE, 0x8EFB, 0xB661, 0x8EFC, 0xB663, 0x8EFE, 0xB8FD,\n\t0x8EFF, 0xE075, 0x8F00, 0xE077, 0x8F01, 0xE076, 0x8F02, 0xE07B,\n\t0x8F03, 0xB8FB, 0x8F05, 0xE078, 0x8F06, 0xE074, 0x8F07, 0xE079,\n\t0x8F08, 0xE07A, 0x8F09, 0xB8FC, 0x8F0A, 0xB8FE, 0x8F0B, 0xE07C,\n\t0x8F0D, 0xE467, 0x8F0E, 0xE466, 0x8F10, 0xE464, 0x8F11, 0xE465,\n\t0x8F12, 0xBBB3, 0x8F13, 0xBBB5, 0x8F14, 0xBBB2, 0x8F15, 0xBBB4,\n\t0x8F16, 0xE84D, 0x8F17, 0xE84E, 0x8F18, 0xE849, 0x8F1A, 0xE84A,\n\t0x8F1B, 0xBDF8, 0x8F1C, 0xBDFD, 0x8F1D, 0xBDF7, 0x8F1E, 0xBDFE,\n\t0x8F1F, 0xBDF9, 0x8F20, 0xE84B, 0x8F23, 0xE84C, 0x8F24, 0xE848,\n\t0x8F25, 0xBE40, 0x8F26, 0xBDFB, 0x8F29, 0xBDFA, 0x8F2A, 0xBDFC,\n\t0x8F2C, 0xE847, 0x8F2E, 0xEBCA, 0x8F2F, 0xBFE8, 0x8F32, 0xEBCC,\n\t0x8F33, 0xBFEA, 0x8F34, 0xEBCF, 0x8F35, 0xEBCB, 0x8F36, 0xEBC9,\n\t0x8F37, 0xEBCE, 0x8F38, 0xBFE9, 0x8F39, 0xEBCD, 0x8F3B, 0xBFE7,\n\t0x8F3E, 0xC1D3, 0x8F3F, 0xC1D6, 0x8F40, 0xEEC1, 0x8F42, 0xC1D4,\n\t0x8F43, 0xEEC0, 0x8F44, 0xC1D2, 0x8F45, 0xC1D5, 0x8F46, 0xF146,\n\t0x8F47, 0xF147, 0x8F48, 0xF148, 0x8F49, 0xC2E0, 0x8F4B, 0xF149,\n\t0x8F4D, 0xC2E1, 0x8F4E, 0xC3E2, 0x8F4F, 0xF358, 0x8F50, 0xF359,\n\t0x8F51, 0xF357, 0x8F52, 0xF356, 0x8F53, 0xF35A, 0x8F54, 0xC3E1,\n\t0x8F55, 0xF4DD, 0x8F56, 0xF4DB, 0x8F57, 0xF4DC, 0x8F58, 0xF4DE,\n\t0x8F59, 0xF4DA, 0x8F5A, 0xF4DF, 0x8F5B, 0xF658, 0x8F5D, 0xF659,\n\t0x8F5E, 0xF657, 0x8F5F, 0xC546, 0x8F60, 0xF764, 0x8F61, 0xC5AF,\n\t0x8F62, 0xF765, 0x8F63, 0xF848, 0x8F64, 0xF847, 0x8F9B, 0xA8AF,\n\t0x8F9C, 0xB664, 0x8F9F, 0xB940, 0x8FA3, 0xBBB6, 0x8FA6, 0xBFEC,\n\t0x8FA8, 0xBFEB, 0x8FAD, 0xC3E3, 0x8FAE, 0xC47C, 0x8FAF, 0xC547,\n\t0x8FB0, 0xA8B0, 0x8FB1, 0xB064, 0x8FB2, 0xB941, 0x8FB4, 0xF35B,\n\t0x8FBF, 0xCBA6, 0x8FC2, 0xA8B1, 0x8FC4, 0xA8B4, 0x8FC5, 0xA8B3,\n\t0x8FC6, 0xA8B2, 0x8FC9, 0xCBA5, 0x8FCB, 0xCDCD, 0x8FCD, 0xCDCF,\n\t0x8FCE, 0xAAEF, 0x8FD1, 0xAAF1, 0x8FD2, 0xCDCC, 0x8FD3, 0xCDCE,\n\t0x8FD4, 0xAAF0, 0x8FD5, 0xCDD1, 0x8FD6, 0xCDD0, 0x8FD7, 0xCDD2,\n\t0x8FE0, 0xD0B6, 0x8FE1, 0xD0B4, 0x8FE2, 0xAD7C, 0x8FE3, 0xD0B3,\n\t0x8FE4, 0xADA3, 0x8FE5, 0xAD7E, 0x8FE6, 0xAD7B, 0x8FE8, 0xADA4,\n\t0x8FEA, 0xAD7D, 0x8FEB, 0xADA2, 0x8FED, 0xADA1, 0x8FEE, 0xD0B5,\n\t0x8FF0, 0xAD7A, 0x8FF4, 0xB06A, 0x8FF5, 0xD3EB, 0x8FF6, 0xD3F1,\n\t0x8FF7, 0xB067, 0x8FF8, 0xB06E, 0x8FFA, 0xB069, 0x8FFB, 0xD3EE,\n\t0x8FFC, 0xD3F0, 0x8FFD, 0xB06C, 0x8FFE, 0xD3EA, 0x8FFF, 0xD3ED,\n\t0x9000, 0xB068, 0x9001, 0xB065, 0x9002, 0xD3EC, 0x9003, 0xB06B,\n\t0x9004, 0xD3EF, 0x9005, 0xB06D, 0x9006, 0xB066, 0x900B, 0xD7E3,\n\t0x900C, 0xD7E6, 0x900D, 0xB370, 0x900F, 0xB37A, 0x9010, 0xB376,\n\t0x9011, 0xD7E4, 0x9014, 0xB37E, 0x9015, 0xB377, 0x9016, 0xB37C,\n\t0x9017, 0xB372, 0x9019, 0xB36F, 0x901A, 0xB371, 0x901B, 0xB37D,\n\t0x901C, 0xD7E5, 0x901D, 0xB375, 0x901E, 0xB378, 0x901F, 0xB374,\n\t0x9020, 0xB379, 0x9021, 0xD7E7, 0x9022, 0xB37B, 0x9023, 0xB373,\n\t0x9024, 0xD7E2, 0x902D, 0xDC4D, 0x902E, 0xB665, 0x902F, 0xDC4F,\n\t0x9031, 0xB667, 0x9032, 0xB669, 0x9034, 0xDC4E, 0x9035, 0xB666,\n\t0x9036, 0xB66A, 0x9038, 0xB668, 0x903C, 0xB947, 0x903D, 0xE0A3,\n\t0x903E, 0xB94F, 0x903F, 0xE07E, 0x9041, 0xB950, 0x9042, 0xB945,\n\t0x9044, 0xE0A1, 0x9047, 0xB94A, 0x9049, 0xE0A2, 0x904A, 0xB943,\n\t0x904B, 0xB942, 0x904D, 0xB94D, 0x904E, 0xB94C, 0x904F, 0xB94B,\n\t0x9050, 0xB949, 0x9051, 0xB94E, 0x9052, 0xE07D, 0x9053, 0xB944,\n\t0x9054, 0xB946, 0x9055, 0xB948, 0x9058, 0xBBB8, 0x9059, 0xBBBB,\n\t0x905B, 0xBBBF, 0x905C, 0xBBB9, 0x905D, 0xBBBE, 0x905E, 0xBBBC,\n\t0x9060, 0xBBB7, 0x9062, 0xBBBD, 0x9063, 0xBBBA, 0x9067, 0xE852,\n\t0x9068, 0xBE43, 0x9069, 0xBE41, 0x906B, 0xE853, 0x906D, 0xBE44,\n\t0x906E, 0xBE42, 0x906F, 0xE851, 0x9070, 0xE850, 0x9072, 0xBFF0,\n\t0x9073, 0xE84F, 0x9074, 0xBFEE, 0x9075, 0xBFED, 0x9076, 0xEBD0,\n\t0x9077, 0xBE45, 0x9078, 0xBFEF, 0x9079, 0xEBD1, 0x907A, 0xBFF2,\n\t0x907B, 0xEBD2, 0x907C, 0xBFF1, 0x907D, 0xC1D8, 0x907E, 0xEEC3,\n\t0x907F, 0xC1D7, 0x9080, 0xC1DC, 0x9081, 0xC1DA, 0x9082, 0xC1DB,\n\t0x9083, 0xC2E3, 0x9084, 0xC1D9, 0x9085, 0xEEC2, 0x9086, 0xEBD3,\n\t0x9087, 0xC2E2, 0x9088, 0xC2E4, 0x908A, 0xC3E4, 0x908B, 0xC3E5,\n\t0x908D, 0xF4E0, 0x908F, 0xC5DE, 0x9090, 0xC5DD, 0x9091, 0xA8B6,\n\t0x9094, 0xCA55, 0x9095, 0xB06F, 0x9097, 0xCA52, 0x9098, 0xCA53,\n\t0x9099, 0xCA51, 0x909B, 0xCA54, 0x909E, 0xCBAA, 0x909F, 0xCBA7,\n\t0x90A0, 0xCBAC, 0x90A1, 0xCBA8, 0x90A2, 0xA8B7, 0x90A3, 0xA8BA,\n\t0x90A5, 0xCBA9, 0x90A6, 0xA8B9, 0x90A7, 0xCBAB, 0x90AA, 0xA8B8,\n\t0x90AF, 0xCDD5, 0x90B0, 0xCDD7, 0x90B1, 0xAAF4, 0x90B2, 0xCDD3,\n\t0x90B3, 0xCDD6, 0x90B4, 0xCDD4, 0x90B5, 0xAAF2, 0x90B6, 0xAAF5,\n\t0x90B8, 0xAAF3, 0x90BD, 0xD0B8, 0x90BE, 0xD0BC, 0x90BF, 0xD0B9,\n\t0x90C1, 0xADA7, 0x90C3, 0xADA8, 0x90C5, 0xD0BB, 0x90C7, 0xD0BD,\n\t0x90C8, 0xD0BF, 0x90CA, 0xADA5, 0x90CB, 0xD0BE, 0x90CE, 0xADA6,\n\t0x90D4, 0xD7EE, 0x90D5, 0xD0BA, 0x90D6, 0xD3F2, 0x90D7, 0xD3FB,\n\t0x90D8, 0xD3F9, 0x90D9, 0xD3F4, 0x90DA, 0xD3F5, 0x90DB, 0xD3FA,\n\t0x90DC, 0xD3FC, 0x90DD, 0xB071, 0x90DF, 0xD3F7, 0x90E0, 0xD3F3,\n\t0x90E1, 0xB070, 0x90E2, 0xB072, 0x90E3, 0xD3F6, 0x90E4, 0xD3FD,\n\t0x90E5, 0xD3F8, 0x90E8, 0xB3A1, 0x90E9, 0xD7F1, 0x90EA, 0xD7E9,\n\t0x90EB, 0xD7EF, 0x90EC, 0xD7F0, 0x90ED, 0xB3A2, 0x90EF, 0xD7E8,\n\t0x90F0, 0xD7EA, 0x90F1, 0xD0B7, 0x90F2, 0xD7EC, 0x90F3, 0xD7ED,\n\t0x90F4, 0xD7EB, 0x90F5, 0xB66C, 0x90F9, 0xDC56, 0x90FA, 0xEBD4,\n\t0x90FB, 0xDC57, 0x90FC, 0xDC54, 0x90FD, 0xB3A3, 0x90FE, 0xB66E,\n\t0x90FF, 0xDC53, 0x9100, 0xDC59, 0x9101, 0xDC58, 0x9102, 0xB66B,\n\t0x9103, 0xDC5C, 0x9104, 0xDC52, 0x9105, 0xDC5B, 0x9106, 0xDC50,\n\t0x9107, 0xDC5A, 0x9108, 0xDC55, 0x9109, 0xB66D, 0x910B, 0xE0AA,\n\t0x910D, 0xE0A5, 0x910E, 0xE0AB, 0x910F, 0xE0A6, 0x9110, 0xE0A4,\n\t0x9111, 0xE0A7, 0x9112, 0xB951, 0x9114, 0xE0A9, 0x9116, 0xE0A8,\n\t0x9117, 0xB952, 0x9118, 0xBBC1, 0x9119, 0xBBC0, 0x911A, 0xE46E,\n\t0x911B, 0xE471, 0x911C, 0xE469, 0x911D, 0xE46D, 0x911E, 0xBBC2,\n\t0x911F, 0xE46C, 0x9120, 0xE46A, 0x9121, 0xE470, 0x9122, 0xE46B,\n\t0x9123, 0xE468, 0x9124, 0xE46F, 0x9126, 0xE859, 0x9127, 0xBE48,\n\t0x9128, 0xF14A, 0x9129, 0xE856, 0x912A, 0xE857, 0x912B, 0xE855,\n\t0x912C, 0xDC51, 0x912D, 0xBE47, 0x912E, 0xE85A, 0x912F, 0xE854,\n\t0x9130, 0xBE46, 0x9131, 0xBE49, 0x9132, 0xE858, 0x9133, 0xEBD5,\n\t0x9134, 0xBFF3, 0x9135, 0xEBD6, 0x9136, 0xEBD7, 0x9138, 0xEEC4,\n\t0x9139, 0xC1DD, 0x913A, 0xF14B, 0x913B, 0xF14C, 0x913E, 0xF14D,\n\t0x913F, 0xF35D, 0x9140, 0xF35C, 0x9141, 0xF4E2, 0x9143, 0xF4E1,\n\t0x9144, 0xF65B, 0x9145, 0xF65C, 0x9146, 0xF65A, 0x9147, 0xF766,\n\t0x9148, 0xC5B0, 0x9149, 0xA8BB, 0x914A, 0xADAA, 0x914B, 0xADA9,\n\t0x914C, 0xB075, 0x914D, 0xB074, 0x914E, 0xD440, 0x914F, 0xD441,\n\t0x9150, 0xD3FE, 0x9152, 0xB073, 0x9153, 0xD7F5, 0x9155, 0xD7F6,\n\t0x9156, 0xD7F2, 0x9157, 0xB3A4, 0x9158, 0xD7F3, 0x915A, 0xD7F4,\n\t0x915F, 0xDC5F, 0x9160, 0xDC61, 0x9161, 0xDC5D, 0x9162, 0xDC60,\n\t0x9163, 0xB66F, 0x9164, 0xDC5E, 0x9165, 0xB670, 0x9168, 0xDD73,\n\t0x9169, 0xB955, 0x916A, 0xB954, 0x916C, 0xB953, 0x916E, 0xE0AC,\n\t0x916F, 0xE0AD, 0x9172, 0xE473, 0x9173, 0xE475, 0x9174, 0xBBC6,\n\t0x9175, 0xBBC3, 0x9177, 0xBBC5, 0x9178, 0xBBC4, 0x9179, 0xE474,\n\t0x917A, 0xE472, 0x9180, 0xE861, 0x9181, 0xE85E, 0x9182, 0xE85F,\n\t0x9183, 0xBE4D, 0x9184, 0xE860, 0x9185, 0xE85B, 0x9186, 0xE85C,\n\t0x9187, 0xBE4A, 0x9189, 0xBE4B, 0x918A, 0xE85D, 0x918B, 0xBE4C,\n\t0x918D, 0xEBDB, 0x918F, 0xEBDC, 0x9190, 0xEBD9, 0x9191, 0xEBDA,\n\t0x9192, 0xBFF4, 0x9193, 0xEBD8, 0x9199, 0xEEC8, 0x919A, 0xEEC5,\n\t0x919B, 0xEEC7, 0x919C, 0xC1E0, 0x919D, 0xEECB, 0x919E, 0xC1DF,\n\t0x919F, 0xEEC9, 0x91A0, 0xEECC, 0x91A1, 0xEECA, 0x91A2, 0xEEC6,\n\t0x91A3, 0xC1DE, 0x91A5, 0xF14F, 0x91A7, 0xF150, 0x91A8, 0xF14E,\n\t0x91AA, 0xF152, 0x91AB, 0xC2E5, 0x91AC, 0xC2E6, 0x91AD, 0xF35F,\n\t0x91AE, 0xC3E7, 0x91AF, 0xF151, 0x91B0, 0xF35E, 0x91B1, 0xC3E6,\n\t0x91B2, 0xF4E5, 0x91B3, 0xF4E6, 0x91B4, 0xC4BF, 0x91B5, 0xF4E4,\n\t0x91B7, 0xF4E3, 0x91B9, 0xF65D, 0x91BA, 0xC548, 0x91BC, 0xF849,\n\t0x91BD, 0xF8C8, 0x91BE, 0xF8C7, 0x91C0, 0xC643, 0x91C1, 0xC65D,\n\t0x91C2, 0xF8C9, 0x91C3, 0xF971, 0x91C5, 0xC66F, 0x91C6, 0xA8BC,\n\t0x91C7, 0xAAF6, 0x91C9, 0xB956, 0x91CB, 0xC4C0, 0x91CC, 0xA8BD,\n\t0x91CD, 0xADAB, 0x91CE, 0xB3A5, 0x91CF, 0xB671, 0x91D0, 0xC2E7,\n\t0x91D1, 0xAAF7, 0x91D3, 0xD0C1, 0x91D4, 0xD0C0, 0x91D5, 0xD442,\n\t0x91D7, 0xB078, 0x91D8, 0xB076, 0x91D9, 0xB07A, 0x91DA, 0xD444,\n\t0x91DC, 0xB079, 0x91DD, 0xB077, 0x91E2, 0xD443, 0x91E3, 0xB3A8,\n\t0x91E4, 0xD7FC, 0x91E6, 0xB3A7, 0x91E7, 0xB3A9, 0x91E8, 0xD842,\n\t0x91E9, 0xB3AB, 0x91EA, 0xD7FE, 0x91EB, 0xD840, 0x91EC, 0xD7F7,\n\t0x91ED, 0xB3AA, 0x91EE, 0xD843, 0x91F1, 0xD7F9, 0x91F3, 0xD7FA,\n\t0x91F4, 0xD7F8, 0x91F5, 0xB3A6, 0x91F7, 0xD841, 0x91F8, 0xD7FB,\n\t0x91F9, 0xD7FD, 0x91FD, 0xDC6D, 0x91FF, 0xDC6C, 0x9200, 0xDC6A,\n\t0x9201, 0xDC62, 0x9202, 0xDC71, 0x9203, 0xDC65, 0x9204, 0xDC6F,\n\t0x9205, 0xDC76, 0x9206, 0xDC6E, 0x9207, 0xB679, 0x9209, 0xB675,\n\t0x920A, 0xDC63, 0x920C, 0xDC69, 0x920D, 0xB677, 0x920F, 0xDC68,\n\t0x9210, 0xB678, 0x9211, 0xB67A, 0x9212, 0xDC6B, 0x9214, 0xB672,\n\t0x9215, 0xB673, 0x9216, 0xDC77, 0x9217, 0xDC75, 0x9219, 0xDC74,\n\t0x921A, 0xDC66, 0x921C, 0xDC72, 0x921E, 0xB676, 0x9223, 0xB674,\n\t0x9224, 0xDC73, 0x9225, 0xDC64, 0x9226, 0xDC67, 0x9227, 0xDC70,\n\t0x922D, 0xE4BA, 0x922E, 0xE0B7, 0x9230, 0xE0B0, 0x9231, 0xE0C3,\n\t0x9232, 0xE0CC, 0x9233, 0xE0B3, 0x9234, 0xB961, 0x9236, 0xE0C0,\n\t0x9237, 0xB957, 0x9238, 0xB959, 0x9239, 0xB965, 0x923A, 0xE0B1,\n\t0x923D, 0xB95A, 0x923E, 0xB95C, 0x923F, 0xB966, 0x9240, 0xB95B,\n\t0x9245, 0xB964, 0x9246, 0xE0B9, 0x9248, 0xE0AE, 0x9249, 0xB962,\n\t0x924A, 0xE0B8, 0x924B, 0xB95E, 0x924C, 0xE0CA, 0x924D, 0xB963,\n\t0x924E, 0xE0C8, 0x924F, 0xE0BC, 0x9250, 0xE0C6, 0x9251, 0xB960,\n\t0x9252, 0xE0AF, 0x9253, 0xE0C9, 0x9254, 0xE0C4, 0x9256, 0xE0CB,\n\t0x9257, 0xB958, 0x925A, 0xB967, 0x925B, 0xB95D, 0x925E, 0xE0B5,\n\t0x9260, 0xE0BD, 0x9261, 0xE0C1, 0x9263, 0xE0C5, 0x9264, 0xB95F,\n\t0x9265, 0xE0B4, 0x9266, 0xE0B2, 0x9267, 0xE0BE, 0x926C, 0xE0BB,\n\t0x926D, 0xE0BA, 0x926F, 0xE0BF, 0x9270, 0xE0C2, 0x9272, 0xE0C7,\n\t0x9276, 0xE478, 0x9278, 0xBBC7, 0x9279, 0xE4A4, 0x927A, 0xE47A,\n\t0x927B, 0xBBCC, 0x927C, 0xBBD0, 0x927D, 0xE4AD, 0x927E, 0xE4B5,\n\t0x927F, 0xE4A6, 0x9280, 0xBBC8, 0x9282, 0xE4AA, 0x9283, 0xE0B6,\n\t0x9285, 0xBBC9, 0x9286, 0xE4B1, 0x9287, 0xE4B6, 0x9288, 0xE4AE,\n\t0x928A, 0xE4B0, 0x928B, 0xE4B9, 0x928C, 0xE4B2, 0x928D, 0xE47E,\n\t0x928E, 0xE4A9, 0x9291, 0xBBD1, 0x9293, 0xBBCD, 0x9294, 0xE47C,\n\t0x9295, 0xE4AB, 0x9296, 0xBBCB, 0x9297, 0xE4A5, 0x9298, 0xBBCA,\n\t0x9299, 0xE4B3, 0x929A, 0xE4A2, 0x929B, 0xE479, 0x929C, 0xBBCE,\n\t0x929D, 0xE4B8, 0x92A0, 0xE47B, 0x92A1, 0xE4AF, 0x92A2, 0xE4AC,\n\t0x92A3, 0xE4A7, 0x92A4, 0xE477, 0x92A5, 0xE476, 0x92A6, 0xE4A1,\n\t0x92A7, 0xE4B4, 0x92A8, 0xBBCF, 0x92A9, 0xE4B7, 0x92AA, 0xE47D,\n\t0x92AB, 0xE4A3, 0x92AC, 0xBE52, 0x92B2, 0xBE5A, 0x92B3, 0xBE55,\n\t0x92B4, 0xE8A4, 0x92B5, 0xE8A1, 0x92B6, 0xE867, 0x92B7, 0xBE50,\n\t0x92B9, 0xF9D7, 0x92BB, 0xBE4F, 0x92BC, 0xBE56, 0x92C0, 0xE865,\n\t0x92C1, 0xBE54, 0x92C2, 0xE871, 0x92C3, 0xE863, 0x92C4, 0xE864,\n\t0x92C5, 0xBE4E, 0x92C6, 0xE8A3, 0x92C7, 0xBE58, 0x92C8, 0xE874,\n\t0x92C9, 0xE879, 0x92CA, 0xE873, 0x92CB, 0xEBEE, 0x92CC, 0xE86F,\n\t0x92CD, 0xE877, 0x92CE, 0xE875, 0x92CF, 0xE868, 0x92D0, 0xE862,\n\t0x92D1, 0xE87D, 0x92D2, 0xBE57, 0x92D3, 0xE87E, 0x92D5, 0xE878,\n\t0x92D7, 0xE86D, 0x92D8, 0xE86B, 0x92D9, 0xE866, 0x92DD, 0xE86E,\n\t0x92DE, 0xE87B, 0x92DF, 0xE86A, 0x92E0, 0xE87A, 0x92E1, 0xE8A2,\n\t0x92E4, 0xBE53, 0x92E6, 0xE876, 0x92E7, 0xE87C, 0x92E8, 0xE872,\n\t0x92E9, 0xE86C, 0x92EA, 0xBE51, 0x92EE, 0xE4A8, 0x92EF, 0xE870,\n\t0x92F0, 0xBE59, 0x92F1, 0xE869, 0x92F7, 0xEBF4, 0x92F8, 0xBFF7,\n\t0x92F9, 0xEBF3, 0x92FA, 0xEBF0, 0x92FB, 0xEC44, 0x92FC, 0xBFFB,\n\t0x92FE, 0xEC41, 0x92FF, 0xEBF8, 0x9300, 0xEC43, 0x9301, 0xEBE9,\n\t0x9302, 0xEBF6, 0x9304, 0xBFFD, 0x9306, 0xEBE1, 0x9308, 0xEBDF,\n\t0x9309, 0xEC42, 0x930B, 0xEC40, 0x930C, 0xEBFE, 0x930D, 0xEBED,\n\t0x930E, 0xEBEC, 0x930F, 0xEBE2, 0x9310, 0xC040, 0x9312, 0xEBE8,\n\t0x9313, 0xEBF2, 0x9314, 0xEBFD, 0x9315, 0xC043, 0x9316, 0xEC45,\n\t0x9318, 0xC1E8, 0x9319, 0xC045, 0x931A, 0xBFFE, 0x931B, 0xEBE6,\n\t0x931D, 0xEBEF, 0x931E, 0xEBDE, 0x931F, 0xEBE0, 0x9320, 0xBFF5,\n\t0x9321, 0xC042, 0x9322, 0xBFFA, 0x9323, 0xEBE7, 0x9324, 0xEBF7,\n\t0x9325, 0xEBF1, 0x9326, 0xC041, 0x9327, 0xEBDD, 0x9328, 0xC1E3,\n\t0x9329, 0xEBF9, 0x932A, 0xEBFC, 0x932B, 0xBFFC, 0x932D, 0xEBEB,\n\t0x932E, 0xC044, 0x932F, 0xBFF9, 0x9333, 0xBFF8, 0x9334, 0xEBF5,\n\t0x9335, 0xEBFB, 0x9336, 0xBFF6, 0x9338, 0xEBE4, 0x9339, 0xEBFA,\n\t0x933C, 0xEBE5, 0x9346, 0xEBEA, 0x9347, 0xEED2, 0x9349, 0xEED7,\n\t0x934A, 0xC1E5, 0x934B, 0xC1E7, 0x934C, 0xEEDD, 0x934D, 0xC1E1,\n\t0x934E, 0xEEEC, 0x934F, 0xEEE3, 0x9350, 0xEED8, 0x9351, 0xEED9,\n\t0x9352, 0xEEE2, 0x9354, 0xC1EE, 0x9355, 0xEEE1, 0x9356, 0xEED1,\n\t0x9357, 0xEEE0, 0x9358, 0xEED4, 0x9359, 0xEEED, 0x935A, 0xC1ED,\n\t0x935B, 0xC1EB, 0x935C, 0xEED5, 0x935E, 0xEEE8, 0x9360, 0xEEDA,\n\t0x9361, 0xEEE7, 0x9363, 0xEEE9, 0x9364, 0xEED0, 0x9365, 0xC1E6,\n\t0x9367, 0xEEEA, 0x936A, 0xEEDE, 0x936C, 0xC1EA, 0x936D, 0xEEDB,\n\t0x9370, 0xC1EC, 0x9371, 0xEEE4, 0x9375, 0xC1E4, 0x9376, 0xEED6,\n\t0x9377, 0xEEE5, 0x9379, 0xEEDF, 0x937A, 0xEBE3, 0x937B, 0xEEE6,\n\t0x937C, 0xEED3, 0x937E, 0xC1E9, 0x9380, 0xEEEB, 0x9382, 0xC1E2,\n\t0x9383, 0xEECE, 0x9388, 0xF160, 0x9389, 0xF159, 0x938A, 0xC2E9,\n\t0x938C, 0xF154, 0x938D, 0xF163, 0x938E, 0xF15B, 0x938F, 0xEEDC,\n\t0x9391, 0xF165, 0x9392, 0xF155, 0x9394, 0xC2E8, 0x9395, 0xF15F,\n\t0x9396, 0xC2EA, 0x9397, 0xC2F2, 0x9398, 0xC2F0, 0x9399, 0xF161,\n\t0x939A, 0xC2F1, 0x939B, 0xF157, 0x939D, 0xF158, 0x939E, 0xF15D,\n\t0x939F, 0xF162, 0x93A1, 0xEECD, 0x93A2, 0xC2EB, 0x93A3, 0xF16A,\n\t0x93A4, 0xF167, 0x93A5, 0xF16B, 0x93A6, 0xF15E, 0x93A7, 0xF15A,\n\t0x93A8, 0xF168, 0x93A9, 0xF36A, 0x93AA, 0xF15C, 0x93AC, 0xC2EE,\n\t0x93AE, 0xC2ED, 0x93AF, 0xEECF, 0x93B0, 0xC2EF, 0x93B1, 0xF164,\n\t0x93B2, 0xF166, 0x93B3, 0xC2EC, 0x93B4, 0xF169, 0x93B5, 0xF153,\n\t0x93B7, 0xF156, 0x93C0, 0xF373, 0x93C2, 0xF363, 0x93C3, 0xC3EB,\n\t0x93C4, 0xF371, 0x93C7, 0xF361, 0x93C8, 0xC3EC, 0x93CA, 0xF36C,\n\t0x93CC, 0xF368, 0x93CD, 0xC3F1, 0x93CE, 0xF372, 0x93CF, 0xF362,\n\t0x93D0, 0xF365, 0x93D1, 0xC3E9, 0x93D2, 0xF374, 0x93D4, 0xF36D,\n\t0x93D5, 0xF370, 0x93D6, 0xC3EF, 0x93D7, 0xC3F4, 0x93D8, 0xC3F2,\n\t0x93D9, 0xF369, 0x93DA, 0xF364, 0x93DC, 0xC3ED, 0x93DD, 0xC3EE,\n\t0x93DE, 0xF360, 0x93DF, 0xC3EA, 0x93E1, 0xC3E8, 0x93E2, 0xC3F0,\n\t0x93E3, 0xF36F, 0x93E4, 0xC3F3, 0x93E6, 0xF36B, 0x93E7, 0xF375,\n\t0x93E8, 0xC3F5, 0x93EC, 0xF367, 0x93EE, 0xF36E, 0x93F5, 0xF4F3,\n\t0x93F6, 0xF542, 0x93F7, 0xF4F5, 0x93F8, 0xF4FC, 0x93F9, 0xF366,\n\t0x93FA, 0xF4FA, 0x93FB, 0xF4E9, 0x93FC, 0xF540, 0x93FD, 0xC4C3,\n\t0x93FE, 0xF4ED, 0x93FF, 0xF4FE, 0x9400, 0xF4F4, 0x9403, 0xC4C2,\n\t0x9406, 0xF544, 0x9407, 0xF4F6, 0x9409, 0xF4FB, 0x940A, 0xF4FD,\n\t0x940B, 0xF4E7, 0x940C, 0xF541, 0x940D, 0xF4F2, 0x940E, 0xF4F7,\n\t0x940F, 0xF4EB, 0x9410, 0xF4EF, 0x9411, 0xF543, 0x9412, 0xF4F9,\n\t0x9413, 0xF4E8, 0x9414, 0xF4EC, 0x9415, 0xF4EE, 0x9416, 0xF4F8,\n\t0x9418, 0xC4C1, 0x9419, 0xF4F1, 0x9420, 0xF4EA, 0x9428, 0xF4F0,\n\t0x9429, 0xF661, 0x942A, 0xF666, 0x942B, 0xC54F, 0x942C, 0xF668,\n\t0x942E, 0xC549, 0x9430, 0xF664, 0x9431, 0xF66A, 0x9432, 0xC54E,\n\t0x9433, 0xC54A, 0x9435, 0xC54B, 0x9436, 0xF660, 0x9437, 0xF667,\n\t0x9438, 0xC54D, 0x9439, 0xF665, 0x943A, 0xC54C, 0x943B, 0xF65F,\n\t0x943C, 0xF663, 0x943D, 0xF662, 0x943F, 0xF65E, 0x9440, 0xF669,\n\t0x9444, 0xC5B1, 0x9445, 0xF76D, 0x9446, 0xF770, 0x9447, 0xF76C,\n\t0x9448, 0xF76E, 0x9449, 0xF76F, 0x944A, 0xF769, 0x944B, 0xF76A,\n\t0x944C, 0xF767, 0x944F, 0xF76B, 0x9450, 0xF768, 0x9451, 0xC5B2,\n\t0x9452, 0xC5B3, 0x9455, 0xF84B, 0x9457, 0xF84D, 0x945D, 0xF84C,\n\t0x945E, 0xF84E, 0x9460, 0xC5E0, 0x9462, 0xF84A, 0x9463, 0xC5DF,\n\t0x9464, 0xC5E1, 0x9468, 0xF8CB, 0x9469, 0xF8CC, 0x946A, 0xC644,\n\t0x946B, 0xF8CA, 0x946D, 0xF953, 0x946E, 0xF952, 0x946F, 0xF954,\n\t0x9470, 0xC65F, 0x9471, 0xF955, 0x9472, 0xC65E, 0x9473, 0xF956,\n\t0x9474, 0xF972, 0x9475, 0xF975, 0x9476, 0xF974, 0x9477, 0xC668,\n\t0x9478, 0xF973, 0x947C, 0xC672, 0x947D, 0xC670, 0x947E, 0xC671,\n\t0x947F, 0xC677, 0x9480, 0xF9C0, 0x9481, 0xF9C1, 0x9482, 0xF9BF,\n\t0x9483, 0xF9C9, 0x9577, 0xAAF8, 0x957A, 0xD844, 0x957B, 0xDC78,\n\t0x957C, 0xE8A5, 0x957D, 0xF376, 0x9580, 0xAAF9, 0x9582, 0xADAC,\n\t0x9583, 0xB07B, 0x9586, 0xD845, 0x9588, 0xD846, 0x9589, 0xB3AC,\n\t0x958B, 0xB67D, 0x958C, 0xDC7A, 0x958D, 0xDC79, 0x958E, 0xB6A3,\n\t0x958F, 0xB67C, 0x9590, 0xDC7B, 0x9591, 0xB67E, 0x9592, 0xB6A2,\n\t0x9593, 0xB6A1, 0x9594, 0xB67B, 0x9598, 0xB968, 0x959B, 0xE0D0,\n\t0x959C, 0xE0CE, 0x959E, 0xE0CF, 0x959F, 0xE0CD, 0x95A1, 0xBBD2,\n\t0x95A3, 0xBBD5, 0x95A4, 0xBBD7, 0x95A5, 0xBBD6, 0x95A8, 0xBBD3,\n\t0x95A9, 0xBBD4, 0x95AB, 0xE8A7, 0x95AC, 0xE8A6, 0x95AD, 0xBE5B,\n\t0x95AE, 0xE8A8, 0x95B0, 0xE8A9, 0x95B1, 0xBE5C, 0x95B5, 0xEC4D,\n\t0x95B6, 0xEC4B, 0x95B7, 0xEEF3, 0x95B9, 0xEC49, 0x95BA, 0xEC4A,\n\t0x95BB, 0xC046, 0x95BC, 0xEC46, 0x95BD, 0xEC4E, 0x95BE, 0xEC48,\n\t0x95BF, 0xEC4C, 0x95C0, 0xEEEF, 0x95C3, 0xEEF1, 0x95C5, 0xEEF2,\n\t0x95C6, 0xC1F3, 0x95C7, 0xEEEE, 0x95C8, 0xC1F2, 0x95C9, 0xEEF0,\n\t0x95CA, 0xC1EF, 0x95CB, 0xC1F0, 0x95CC, 0xC1F1, 0x95CD, 0xEC47,\n\t0x95D0, 0xC2F5, 0x95D1, 0xF16E, 0x95D2, 0xF16C, 0x95D3, 0xF16D,\n\t0x95D4, 0xC2F3, 0x95D5, 0xC2F6, 0x95D6, 0xC2F4, 0x95DA, 0xF377,\n\t0x95DB, 0xF378, 0x95DC, 0xC3F6, 0x95DE, 0xF545, 0x95DF, 0xF547,\n\t0x95E0, 0xF546, 0x95E1, 0xC4C4, 0x95E2, 0xC550, 0x95E3, 0xF66D,\n\t0x95E4, 0xF66C, 0x95E5, 0xF66B, 0x961C, 0xAAFA, 0x961E, 0xC9AA,\n\t0x9620, 0xCA58, 0x9621, 0xA6E9, 0x9622, 0xCA56, 0x9623, 0xCA59,\n\t0x9624, 0xCA57, 0x9628, 0xCBAE, 0x962A, 0xA8C1, 0x962C, 0xA8C2,\n\t0x962D, 0xCBB0, 0x962E, 0xA8BF, 0x962F, 0xCBAF, 0x9630, 0xCBAD,\n\t0x9631, 0xA8C0, 0x9632, 0xA8BE, 0x9639, 0xCDD8, 0x963A, 0xCDDB,\n\t0x963B, 0xAAFD, 0x963C, 0xCDDA, 0x963D, 0xCDD9, 0x963F, 0xAAFC,\n\t0x9640, 0xAAFB, 0x9642, 0xAB40, 0x9643, 0xCDDC, 0x9644, 0xAAFE,\n\t0x964A, 0xD0C6, 0x964B, 0xADAE, 0x964C, 0xADAF, 0x964D, 0xADB0,\n\t0x964E, 0xD0C7, 0x964F, 0xD0C3, 0x9650, 0xADAD, 0x9651, 0xD0C4,\n\t0x9653, 0xD0C5, 0x9654, 0xD0C2, 0x9658, 0xB0A4, 0x965B, 0xB0A1,\n\t0x965C, 0xD445, 0x965D, 0xB0A2, 0x965E, 0xB0A5, 0x965F, 0xD446,\n\t0x9661, 0xB07E, 0x9662, 0xB07C, 0x9663, 0xB07D, 0x9664, 0xB0A3,\n\t0x966A, 0xB3AD, 0x966B, 0xD849, 0x966C, 0xB3B5, 0x966D, 0xD848,\n\t0x966F, 0xD84B, 0x9670, 0xB3B1, 0x9671, 0xD84A, 0x9672, 0xB6AB,\n\t0x9673, 0xB3AF, 0x9674, 0xB3B2, 0x9675, 0xB3AE, 0x9676, 0xB3B3,\n\t0x9677, 0xB3B4, 0x9678, 0xB3B0, 0x967C, 0xD847, 0x967D, 0xB6A7,\n\t0x967E, 0xDC7D, 0x9680, 0xDCA3, 0x9683, 0xDCA2, 0x9684, 0xB6AC,\n\t0x9685, 0xB6A8, 0x9686, 0xB6A9, 0x9687, 0xDC7C, 0x9688, 0xDC7E,\n\t0x9689, 0xDCA1, 0x968A, 0xB6A4, 0x968B, 0xB6A6, 0x968D, 0xB6AA,\n\t0x968E, 0xB6A5, 0x9691, 0xE0D3, 0x9692, 0xE0D1, 0x9693, 0xE0D2,\n\t0x9694, 0xB96A, 0x9695, 0xB96B, 0x9697, 0xE0D4, 0x9698, 0xB969,\n\t0x9699, 0xBBD8, 0x969B, 0xBBDA, 0x969C, 0xBBD9, 0x969E, 0xE4BB,\n\t0x96A1, 0xE4BC, 0x96A2, 0xE8AB, 0x96A4, 0xE8AA, 0x96A7, 0xC047,\n\t0x96A8, 0xC048, 0x96A9, 0xEC4F, 0x96AA, 0xC049, 0x96AC, 0xEEF6,\n\t0x96AE, 0xEEF4, 0x96B0, 0xEEF5, 0x96B1, 0xC1F4, 0x96B3, 0xF16F,\n\t0x96B4, 0xC3F7, 0x96B8, 0xC1F5, 0x96B9, 0xAB41, 0x96BB, 0xB0A6,\n\t0x96BC, 0xD447, 0x96BF, 0xD84C, 0x96C0, 0xB3B6, 0x96C1, 0xB6AD,\n\t0x96C2, 0xDCA4, 0x96C3, 0xDCA6, 0x96C4, 0xB6AF, 0x96C5, 0xB6AE,\n\t0x96C6, 0xB6B0, 0x96C7, 0xB6B1, 0x96C8, 0xDCA5, 0x96C9, 0xB96E,\n\t0x96CA, 0xB96F, 0x96CB, 0xB96D, 0x96CC, 0xBBDB, 0x96CD, 0xB96C,\n\t0x96CE, 0xE0D5, 0x96D2, 0xBBDC, 0x96D3, 0xE8AC, 0x96D4, 0xEC50,\n\t0x96D5, 0xC04A, 0x96D6, 0xC1F6, 0x96D7, 0xF170, 0x96D8, 0xF174,\n\t0x96D9, 0xC2F9, 0x96DA, 0xF171, 0x96DB, 0xC2FA, 0x96DC, 0xC2F8,\n\t0x96DD, 0xF175, 0x96DE, 0xC2FB, 0x96DF, 0xF173, 0x96E1, 0xF379,\n\t0x96E2, 0xC2F7, 0x96E3, 0xC3F8, 0x96E5, 0xF8CD, 0x96E8, 0xAB42,\n\t0x96E9, 0xB3B8, 0x96EA, 0xB3B7, 0x96EF, 0xB6B2, 0x96F0, 0xDCA8,\n\t0x96F1, 0xDCA7, 0x96F2, 0xB6B3, 0x96F5, 0xE0D9, 0x96F6, 0xB973,\n\t0x96F7, 0xB970, 0x96F8, 0xE0D8, 0x96F9, 0xB972, 0x96FA, 0xE0D6,\n\t0x96FB, 0xB971, 0x96FD, 0xE0D7, 0x96FF, 0xE4BD, 0x9700, 0xBBDD,\n\t0x9702, 0xE8AF, 0x9704, 0xBE5D, 0x9705, 0xE8AD, 0x9706, 0xBE5E,\n\t0x9707, 0xBE5F, 0x9708, 0xE8AE, 0x9709, 0xBE60, 0x970B, 0xEC51,\n\t0x970D, 0xC04E, 0x970E, 0xC04B, 0x970F, 0xC050, 0x9710, 0xEC53,\n\t0x9711, 0xC04C, 0x9712, 0xEC52, 0x9713, 0xC04F, 0x9716, 0xC04D,\n\t0x9718, 0xEEF9, 0x9719, 0xEEFB, 0x971C, 0xC1F7, 0x971D, 0xEEFA,\n\t0x971E, 0xC1F8, 0x971F, 0xEEF8, 0x9720, 0xEEF7, 0x9722, 0xF177,\n\t0x9723, 0xF176, 0x9724, 0xC2FC, 0x9725, 0xF178, 0x9726, 0xF37E,\n\t0x9727, 0xC3FA, 0x9728, 0xF37D, 0x9729, 0xF37A, 0x972A, 0xC3F9,\n\t0x972B, 0xF37B, 0x972C, 0xF37C, 0x972E, 0xF548, 0x972F, 0xF549,\n\t0x9730, 0xC4C5, 0x9732, 0xC553, 0x9735, 0xF66E, 0x9738, 0xC551,\n\t0x9739, 0xC552, 0x973A, 0xF66F, 0x973D, 0xC5B4, 0x973E, 0xC5B5,\n\t0x973F, 0xF771, 0x9742, 0xC645, 0x9743, 0xF8CF, 0x9744, 0xC647,\n\t0x9746, 0xF8CE, 0x9747, 0xF8D0, 0x9748, 0xC646, 0x9749, 0xF957,\n\t0x974B, 0xF9AD, 0x9752, 0xAB43, 0x9756, 0xB974, 0x9758, 0xE4BE,\n\t0x975A, 0xE8B0, 0x975B, 0xC051, 0x975C, 0xC052, 0x975E, 0xAB44,\n\t0x9760, 0xBE61, 0x9761, 0xC3FB, 0x9762, 0xADB1, 0x9766, 0xC053,\n\t0x9768, 0xC5E2, 0x9769, 0xADB2, 0x976A, 0xD84D, 0x976C, 0xDCA9,\n\t0x976E, 0xDCAB, 0x9770, 0xDCAA, 0x9772, 0xE0DD, 0x9773, 0xE0DA,\n\t0x9774, 0xB975, 0x9776, 0xB976, 0x9777, 0xE0DB, 0x9778, 0xE0DC,\n\t0x977A, 0xE4C0, 0x977B, 0xE4C5, 0x977C, 0xBBDE, 0x977D, 0xE4BF,\n\t0x977E, 0xE4C1, 0x977F, 0xE4C8, 0x9780, 0xE4C3, 0x9781, 0xE4C7,\n\t0x9782, 0xE4C4, 0x9783, 0xE4C2, 0x9784, 0xE4C6, 0x9785, 0xBBDF,\n\t0x9788, 0xE8B3, 0x978A, 0xE8B1, 0x978B, 0xBE63, 0x978D, 0xBE62,\n\t0x978E, 0xE8B2, 0x978F, 0xBE64, 0x9794, 0xEC56, 0x9797, 0xEC55,\n\t0x9798, 0xC054, 0x9799, 0xEC54, 0x979A, 0xEEFC, 0x979C, 0xEEFE,\n\t0x979D, 0xEF41, 0x979E, 0xEF40, 0x97A0, 0xC1F9, 0x97A1, 0xEEFD,\n\t0x97A2, 0xF1A1, 0x97A3, 0xC2FD, 0x97A4, 0xF17D, 0x97A5, 0xF1A2,\n\t0x97A6, 0xC2FE, 0x97A8, 0xF17B, 0x97AA, 0xF17E, 0x97AB, 0xF17C,\n\t0x97AC, 0xF179, 0x97AD, 0xC340, 0x97AE, 0xF17A, 0x97B3, 0xF3A1,\n\t0x97B6, 0xF3A3, 0x97B7, 0xF3A2, 0x97B9, 0xF54A, 0x97BB, 0xF54B,\n\t0x97BF, 0xF670, 0x97C1, 0xC5B7, 0x97C3, 0xC5B6, 0x97C4, 0xF84F,\n\t0x97C5, 0xF850, 0x97C6, 0xC648, 0x97C7, 0xF8D1, 0x97C9, 0xC669,\n\t0x97CB, 0xADB3, 0x97CC, 0xB6B4, 0x97CD, 0xE4CA, 0x97CE, 0xE4C9,\n\t0x97CF, 0xE8B5, 0x97D0, 0xE8B4, 0x97D3, 0xC1FA, 0x97D4, 0xEF43,\n\t0x97D5, 0xEF42, 0x97D6, 0xF1A5, 0x97D7, 0xF1A3, 0x97D8, 0xF1A6,\n\t0x97D9, 0xF1A4, 0x97DC, 0xC3FC, 0x97DD, 0xF3A4, 0x97DE, 0xF3A5,\n\t0x97DF, 0xF3A6, 0x97E1, 0xF671, 0x97E3, 0xF772, 0x97E5, 0xF8D2,\n\t0x97ED, 0xADB4, 0x97F0, 0xEC57, 0x97F1, 0xEF44, 0x97F3, 0xADB5,\n\t0x97F6, 0xBBE0, 0x97F8, 0xEC58, 0x97F9, 0xC341, 0x97FA, 0xF1A7,\n\t0x97FB, 0xC3FD, 0x97FD, 0xF54C, 0x97FE, 0xF54D, 0x97FF, 0xC554,\n\t0x9800, 0xF851, 0x9801, 0xADB6, 0x9802, 0xB3BB, 0x9803, 0xB3BC,\n\t0x9804, 0xD84E, 0x9805, 0xB6B5, 0x9806, 0xB6B6, 0x9807, 0xDCAC,\n\t0x9808, 0xB6B7, 0x980A, 0xB97A, 0x980C, 0xB97C, 0x980D, 0xE0DF,\n\t0x980E, 0xE0E0, 0x980F, 0xE0DE, 0x9810, 0xB977, 0x9811, 0xB978,\n\t0x9812, 0xB97B, 0x9813, 0xB979, 0x9816, 0xE4CB, 0x9817, 0xBBE1,\n\t0x9818, 0xBBE2, 0x981B, 0xE8BC, 0x981C, 0xBE67, 0x981D, 0xE8B7,\n\t0x981E, 0xE8B6, 0x9820, 0xE8BB, 0x9821, 0xBE65, 0x9824, 0xC05B,\n\t0x9826, 0xE8B8, 0x9827, 0xE8BD, 0x9828, 0xE8BA, 0x9829, 0xE8B9,\n\t0x982B, 0xBE66, 0x982D, 0xC059, 0x982F, 0xEC5A, 0x9830, 0xC055,\n\t0x9832, 0xEC5B, 0x9835, 0xEC59, 0x9837, 0xC058, 0x9838, 0xC056,\n\t0x9839, 0xC05A, 0x983B, 0xC057, 0x9841, 0xEF45, 0x9843, 0xEF4A,\n\t0x9844, 0xEF46, 0x9845, 0xEF49, 0x9846, 0xC1FB, 0x9848, 0xEDD4,\n\t0x9849, 0xEF48, 0x984A, 0xEF47, 0x984C, 0xC344, 0x984D, 0xC342,\n\t0x984E, 0xC345, 0x984F, 0xC343, 0x9850, 0xF1A8, 0x9851, 0xF1A9,\n\t0x9852, 0xF1AA, 0x9853, 0xC346, 0x9857, 0xF3AA, 0x9858, 0xC440,\n\t0x9859, 0xF3A8, 0x985B, 0xC441, 0x985C, 0xF3A7, 0x985D, 0xF3A9,\n\t0x985E, 0xC3FE, 0x985F, 0xF551, 0x9860, 0xF54E, 0x9862, 0xF54F,\n\t0x9863, 0xF550, 0x9864, 0xF672, 0x9865, 0xC556, 0x9867, 0xC555,\n\t0x9869, 0xF774, 0x986A, 0xF773, 0x986B, 0xC5B8, 0x986F, 0xC5E3,\n\t0x9870, 0xC649, 0x9871, 0xC660, 0x9872, 0xF958, 0x9873, 0xF9AE,\n\t0x9874, 0xF9AF, 0x98A8, 0xADB7, 0x98A9, 0xDCAD, 0x98AC, 0xE0E1,\n\t0x98AD, 0xE4CC, 0x98AE, 0xE4CD, 0x98AF, 0xBBE3, 0x98B1, 0xBBE4,\n\t0x98B2, 0xE8BE, 0x98B3, 0xBE68, 0x98B6, 0xC1FC, 0x98B8, 0xF1AB,\n\t0x98BA, 0xC347, 0x98BB, 0xF3AD, 0x98BC, 0xC442, 0x98BD, 0xF3AC,\n\t0x98BE, 0xF3AE, 0x98BF, 0xF3AB, 0x98C0, 0xF675, 0x98C1, 0xF552,\n\t0x98C2, 0xF553, 0x98C4, 0xC4C6, 0x98C6, 0xF674, 0x98C9, 0xF673,\n\t0x98CB, 0xF775, 0x98CC, 0xF9B0, 0x98DB, 0xADB8, 0x98DF, 0xADB9,\n\t0x98E2, 0xB0A7, 0x98E3, 0xD448, 0x98E5, 0xD84F, 0x98E7, 0xB6B8,\n\t0x98E9, 0xB6BB, 0x98EA, 0xB6B9, 0x98EB, 0xDCAE, 0x98ED, 0xB6BD,\n\t0x98EF, 0xB6BA, 0x98F2, 0xB6BC, 0x98F4, 0xB97E, 0x98F6, 0xE0E2,\n\t0x98F9, 0xE0E3, 0x98FA, 0xE8C0, 0x98FC, 0xB97D, 0x98FD, 0xB9A1,\n\t0x98FE, 0xB9A2, 0x9900, 0xE4CF, 0x9902, 0xE4CE, 0x9903, 0xBBE5,\n\t0x9905, 0xBBE6, 0x9907, 0xE4D0, 0x9908, 0xE8BF, 0x9909, 0xBBE8,\n\t0x990A, 0xBE69, 0x990C, 0xBBE7, 0x9910, 0xC05C, 0x9911, 0xE8C1,\n\t0x9912, 0xBE6B, 0x9913, 0xBE6A, 0x9914, 0xE8C2, 0x9915, 0xE8C5,\n\t0x9916, 0xE8C3, 0x9917, 0xE8C4, 0x9918, 0xBE6C, 0x991A, 0xC061,\n\t0x991B, 0xC05F, 0x991E, 0xC05E, 0x991F, 0xEC5D, 0x9921, 0xC060,\n\t0x9924, 0xEC5C, 0x9925, 0xEF4B, 0x9927, 0xEC5E, 0x9928, 0xC05D,\n\t0x9929, 0xEC5F, 0x992A, 0xEF4E, 0x992B, 0xEF4C, 0x992C, 0xEF4D,\n\t0x992D, 0xEF52, 0x992E, 0xC34B, 0x992F, 0xEF51, 0x9930, 0xEF54,\n\t0x9931, 0xEF53, 0x9932, 0xEF50, 0x9933, 0xEF4F, 0x9935, 0xC1FD,\n\t0x993A, 0xF1AE, 0x993C, 0xF1AD, 0x993D, 0xC34A, 0x993E, 0xC348,\n\t0x993F, 0xC349, 0x9941, 0xF1AC, 0x9943, 0xF3B1, 0x9945, 0xC443,\n\t0x9947, 0xF3B0, 0x9948, 0xF3AF, 0x9949, 0xC444, 0x994B, 0xF558,\n\t0x994C, 0xF557, 0x994E, 0xF555, 0x9950, 0xF554, 0x9951, 0xC4C8,\n\t0x9952, 0xC4C7, 0x9953, 0xF559, 0x9954, 0xF776, 0x9955, 0xC5B9,\n\t0x9956, 0xF677, 0x9957, 0xC557, 0x9958, 0xF676, 0x9959, 0xF556,\n\t0x995B, 0xF777, 0x995C, 0xC5E4, 0x995E, 0xC661, 0x995F, 0xF959,\n\t0x9961, 0xF9B1, 0x9996, 0xADBA, 0x9997, 0xD850, 0x9998, 0xEF55,\n\t0x9999, 0xADBB, 0x999C, 0xE4D2, 0x999D, 0xE4D1, 0x999E, 0xEC60,\n\t0x99A1, 0xEF57, 0x99A3, 0xEF56, 0x99A5, 0xC34C, 0x99A6, 0xF3B2,\n\t0x99A7, 0xF3B3, 0x99A8, 0xC4C9, 0x99AB, 0xF9B2, 0x99AC, 0xB0A8,\n\t0x99AD, 0xB6BF, 0x99AE, 0xB6BE, 0x99AF, 0xE0E4, 0x99B0, 0xE0E6,\n\t0x99B1, 0xB9A4, 0x99B2, 0xE0E5, 0x99B3, 0xB9A3, 0x99B4, 0xB9A5,\n\t0x99B5, 0xE0E7, 0x99B9, 0xE4D4, 0x99BA, 0xE4D6, 0x99BB, 0xE4D5,\n\t0x99BD, 0xE4D8, 0x99C1, 0xBBE9, 0x99C2, 0xE4D7, 0x99C3, 0xE4D3,\n\t0x99C7, 0xE4D9, 0x99C9, 0xE8CC, 0x99CB, 0xE8CF, 0x99CC, 0xE8D1,\n\t0x99CD, 0xE8C7, 0x99CE, 0xE8CB, 0x99CF, 0xE8C8, 0x99D0, 0xBE6E,\n\t0x99D1, 0xBE71, 0x99D2, 0xBE73, 0x99D3, 0xE8C9, 0x99D4, 0xE8CA,\n\t0x99D5, 0xBE72, 0x99D6, 0xE8CD, 0x99D7, 0xE8D0, 0x99D8, 0xE8CE,\n\t0x99D9, 0xBE74, 0x99DB, 0xBE70, 0x99DC, 0xE8C6, 0x99DD, 0xBE6D,\n\t0x99DF, 0xBE6F, 0x99E2, 0xC063, 0x99E3, 0xEC66, 0x99E4, 0xEC64,\n\t0x99E5, 0xEC63, 0x99E7, 0xEC69, 0x99E9, 0xEC68, 0x99EA, 0xEC67,\n\t0x99EC, 0xEC62, 0x99ED, 0xC062, 0x99EE, 0xEC61, 0x99F0, 0xEC65,\n\t0x99F1, 0xC064, 0x99F4, 0xEF5A, 0x99F6, 0xEF5E, 0x99F7, 0xEF5B,\n\t0x99F8, 0xEF5D, 0x99F9, 0xEF5C, 0x99FA, 0xEF59, 0x99FB, 0xEF5F,\n\t0x99FC, 0xEF62, 0x99FD, 0xEF60, 0x99FE, 0xEF61, 0x99FF, 0xC240,\n\t0x9A01, 0xC1FE, 0x9A02, 0xEF58, 0x9A03, 0xEF63, 0x9A04, 0xF1B3,\n\t0x9A05, 0xF1B6, 0x9A06, 0xF1B8, 0x9A07, 0xF1B7, 0x9A09, 0xF1B1,\n\t0x9A0A, 0xF1B5, 0x9A0B, 0xF1B0, 0x9A0D, 0xF1B2, 0x9A0E, 0xC34D,\n\t0x9A0F, 0xF1AF, 0x9A11, 0xF1B4, 0x9A14, 0xF3C0, 0x9A15, 0xF3B5,\n\t0x9A16, 0xC445, 0x9A19, 0xC446, 0x9A1A, 0xF3B4, 0x9A1B, 0xF3B9,\n\t0x9A1C, 0xF3BF, 0x9A1D, 0xF3B7, 0x9A1E, 0xF3BE, 0x9A20, 0xF3BB,\n\t0x9A22, 0xF3BA, 0x9A23, 0xF3BD, 0x9A24, 0xF3B8, 0x9A25, 0xF3B6,\n\t0x9A27, 0xF3BC, 0x9A29, 0xF560, 0x9A2A, 0xF55E, 0x9A2B, 0xC4CA,\n\t0x9A2C, 0xF55D, 0x9A2D, 0xF563, 0x9A2E, 0xF561, 0x9A30, 0xC4CB,\n\t0x9A31, 0xF55C, 0x9A32, 0xF55A, 0x9A34, 0xF55B, 0x9A35, 0xC4CD,\n\t0x9A36, 0xF55F, 0x9A37, 0xC4CC, 0x9A38, 0xF562, 0x9A39, 0xF678,\n\t0x9A3A, 0xF67E, 0x9A3D, 0xF679, 0x9A3E, 0xC55B, 0x9A3F, 0xF6A1,\n\t0x9A40, 0xC55A, 0x9A41, 0xF67D, 0x9A42, 0xF67C, 0x9A43, 0xC559,\n\t0x9A44, 0xF67B, 0x9A45, 0xC558, 0x9A46, 0xF67A, 0x9A48, 0xF77D,\n\t0x9A49, 0xF7A1, 0x9A4A, 0xF77E, 0x9A4C, 0xF77B, 0x9A4D, 0xC5BB,\n\t0x9A4E, 0xF778, 0x9A4F, 0xF77C, 0x9A50, 0xF7A3, 0x9A52, 0xF7A2,\n\t0x9A53, 0xF779, 0x9A54, 0xF77A, 0x9A55, 0xC5BA, 0x9A56, 0xF852,\n\t0x9A57, 0xC5E7, 0x9A59, 0xF853, 0x9A5A, 0xC5E5, 0x9A5B, 0xC5E6,\n\t0x9A5E, 0xF8D3, 0x9A5F, 0xC64A, 0x9A60, 0xF976, 0x9A62, 0xC66A,\n\t0x9A64, 0xF9B3, 0x9A65, 0xC66B, 0x9A66, 0xF9B4, 0x9A67, 0xF9B5,\n\t0x9A68, 0xF9C3, 0x9A69, 0xF9C2, 0x9A6A, 0xC67A, 0x9A6B, 0xF9CD,\n\t0x9AA8, 0xB0A9, 0x9AAB, 0xE0E9, 0x9AAD, 0xE0E8, 0x9AAF, 0xBBEA,\n\t0x9AB0, 0xBBEB, 0x9AB1, 0xE4DA, 0x9AB3, 0xE8D2, 0x9AB4, 0xEC6C,\n\t0x9AB7, 0xBE75, 0x9AB8, 0xC065, 0x9AB9, 0xEC6A, 0x9ABB, 0xEC6D,\n\t0x9ABC, 0xC066, 0x9ABE, 0xEF64, 0x9ABF, 0xEC6B, 0x9AC0, 0xF1B9,\n\t0x9AC1, 0xC34E, 0x9AC2, 0xF3C1, 0x9AC6, 0xF566, 0x9AC7, 0xF564,\n\t0x9ACA, 0xF565, 0x9ACD, 0xF6A2, 0x9ACF, 0xC55C, 0x9AD0, 0xF7A4,\n\t0x9AD1, 0xC5EA, 0x9AD2, 0xC5BC, 0x9AD3, 0xC5E8, 0x9AD4, 0xC5E9,\n\t0x9AD5, 0xF8D4, 0x9AD6, 0xC662, 0x9AD8, 0xB0AA, 0x9ADC, 0xF1BA,\n\t0x9ADF, 0xD449, 0x9AE1, 0xB9A6, 0x9AE3, 0xE4DB, 0x9AE6, 0xBBEC,\n\t0x9AE7, 0xE4DC, 0x9AEB, 0xE8D4, 0x9AEC, 0xE8D3, 0x9AED, 0xC068,\n\t0x9AEE, 0xBE76, 0x9AEF, 0xBE77, 0x9AF1, 0xE8D7, 0x9AF2, 0xE8D6,\n\t0x9AF3, 0xE8D5, 0x9AF6, 0xEC6E, 0x9AF7, 0xEC71, 0x9AF9, 0xEC70,\n\t0x9AFA, 0xEC6F, 0x9AFB, 0xC067, 0x9AFC, 0xEF68, 0x9AFD, 0xEF66,\n\t0x9AFE, 0xEF65, 0x9B01, 0xEF67, 0x9B03, 0xC34F, 0x9B04, 0xF1BC,\n\t0x9B05, 0xF1BD, 0x9B06, 0xC350, 0x9B08, 0xF1BB, 0x9B0A, 0xF3C3,\n\t0x9B0B, 0xF3C2, 0x9B0C, 0xF3C5, 0x9B0D, 0xC447, 0x9B0E, 0xF3C4,\n\t0x9B10, 0xF567, 0x9B11, 0xF569, 0x9B12, 0xF568, 0x9B15, 0xF6A3,\n\t0x9B16, 0xF6A6, 0x9B17, 0xF6A4, 0x9B18, 0xF6A5, 0x9B19, 0xF7A5,\n\t0x9B1A, 0xC5BD, 0x9B1E, 0xF854, 0x9B1F, 0xF855, 0x9B20, 0xF856,\n\t0x9B22, 0xC64B, 0x9B23, 0xC663, 0x9B24, 0xF9B6, 0x9B25, 0xB0AB,\n\t0x9B27, 0xBE78, 0x9B28, 0xC069, 0x9B29, 0xF1BE, 0x9B2B, 0xF7A6,\n\t0x9B2E, 0xF9C4, 0x9B2F, 0xD44A, 0x9B31, 0xC67B, 0x9B32, 0xB0AC,\n\t0x9B33, 0xEC72, 0x9B35, 0xF1BF, 0x9B37, 0xF3C6, 0x9B3A, 0xF6A7,\n\t0x9B3B, 0xF7A7, 0x9B3C, 0xB0AD, 0x9B3E, 0xE4DD, 0x9B3F, 0xE4DE,\n\t0x9B41, 0xBBED, 0x9B42, 0xBBEE, 0x9B43, 0xE8D9, 0x9B44, 0xBE7A,\n\t0x9B45, 0xBE79, 0x9B46, 0xE8D8, 0x9B48, 0xEF69, 0x9B4A, 0xF1C0,\n\t0x9B4B, 0xF1C2, 0x9B4C, 0xF1C1, 0x9B4D, 0xC353, 0x9B4E, 0xC352,\n\t0x9B4F, 0xC351, 0x9B51, 0xC55E, 0x9B52, 0xF6A8, 0x9B54, 0xC55D,\n\t0x9B55, 0xF7A9, 0x9B56, 0xF7A8, 0x9B58, 0xC64C, 0x9B59, 0xF8D5,\n\t0x9B5A, 0xB3BD, 0x9B5B, 0xE0EA, 0x9B5F, 0xE4E1, 0x9B60, 0xE4DF,\n\t0x9B61, 0xE4E0, 0x9B64, 0xE8E2, 0x9B66, 0xE8DD, 0x9B67, 0xE8DA,\n\t0x9B68, 0xE8E1, 0x9B6C, 0xE8E3, 0x9B6F, 0xBE7C, 0x9B70, 0xE8E0,\n\t0x9B71, 0xE8DC, 0x9B74, 0xE8DB, 0x9B75, 0xE8DF, 0x9B76, 0xE8DE,\n\t0x9B77, 0xBE7B, 0x9B7A, 0xEC7D, 0x9B7B, 0xEC78, 0x9B7C, 0xEC76,\n\t0x9B7D, 0xECA1, 0x9B7E, 0xEC77, 0x9B80, 0xEC73, 0x9B82, 0xEC79,\n\t0x9B85, 0xEC74, 0x9B86, 0xEF72, 0x9B87, 0xEC75, 0x9B88, 0xECA2,\n\t0x9B90, 0xEC7C, 0x9B91, 0xC06A, 0x9B92, 0xEC7B, 0x9B93, 0xEC7A,\n\t0x9B95, 0xEC7E, 0x9B9A, 0xEF6A, 0x9B9B, 0xEF6D, 0x9B9E, 0xEF6C,\n\t0x9BA0, 0xEF74, 0x9BA1, 0xEF6F, 0x9BA2, 0xEF73, 0x9BA4, 0xEF71,\n\t0x9BA5, 0xEF70, 0x9BA6, 0xEF6E, 0x9BA8, 0xEF6B, 0x9BAA, 0xC243,\n\t0x9BAB, 0xC242, 0x9BAD, 0xC244, 0x9BAE, 0xC241, 0x9BAF, 0xEF75,\n\t0x9BB5, 0xF1C8, 0x9BB6, 0xF1CB, 0x9BB8, 0xF1C9, 0x9BB9, 0xF1CD,\n\t0x9BBD, 0xF1CE, 0x9BBF, 0xF1C6, 0x9BC0, 0xC358, 0x9BC1, 0xF1C7,\n\t0x9BC3, 0xF1C5, 0x9BC4, 0xF1CC, 0x9BC6, 0xF1C4, 0x9BC7, 0xF1C3,\n\t0x9BC8, 0xC357, 0x9BC9, 0xC355, 0x9BCA, 0xC354, 0x9BD3, 0xF1CA,\n\t0x9BD4, 0xF3CF, 0x9BD5, 0xF3D5, 0x9BD6, 0xC44A, 0x9BD7, 0xF3D0,\n\t0x9BD9, 0xF3D3, 0x9BDA, 0xF3D7, 0x9BDB, 0xC44B, 0x9BDC, 0xF3D2,\n\t0x9BDE, 0xF3CA, 0x9BE0, 0xF3C9, 0x9BE1, 0xF3D6, 0x9BE2, 0xF3CD,\n\t0x9BE4, 0xF3CB, 0x9BE5, 0xF3D4, 0x9BE6, 0xF3CC, 0x9BE7, 0xC449,\n\t0x9BE8, 0xC448, 0x9BEA, 0xF3C7, 0x9BEB, 0xF3C8, 0x9BEC, 0xF3D1,\n\t0x9BF0, 0xF3CE, 0x9BF7, 0xF56C, 0x9BF8, 0xF56F, 0x9BFD, 0xC356,\n\t0x9C05, 0xF56D, 0x9C06, 0xF573, 0x9C07, 0xF571, 0x9C08, 0xF56B,\n\t0x9C09, 0xF576, 0x9C0B, 0xF56A, 0x9C0D, 0xC4CF, 0x9C0E, 0xF572,\n\t0x9C12, 0xF56E, 0x9C13, 0xC4CE, 0x9C14, 0xF575, 0x9C17, 0xF574,\n\t0x9C1C, 0xF6AB, 0x9C1D, 0xF6AA, 0x9C21, 0xF6B1, 0x9C23, 0xF6AD,\n\t0x9C24, 0xF6B0, 0x9C25, 0xC560, 0x9C28, 0xF6AE, 0x9C29, 0xF6AF,\n\t0x9C2B, 0xF6A9, 0x9C2C, 0xF6AC, 0x9C2D, 0xC55F, 0x9C31, 0xC5BF,\n\t0x9C32, 0xF7B4, 0x9C33, 0xF7AF, 0x9C34, 0xF7B3, 0x9C36, 0xF7B6,\n\t0x9C37, 0xF7B2, 0x9C39, 0xF7AE, 0x9C3B, 0xC5C1, 0x9C3C, 0xF7B1,\n\t0x9C3D, 0xF7B5, 0x9C3E, 0xC5C0, 0x9C3F, 0xF7AC, 0x9C40, 0xF570,\n\t0x9C41, 0xF7B0, 0x9C44, 0xF7AD, 0x9C46, 0xF7AA, 0x9C48, 0xF7AB,\n\t0x9C49, 0xC5BE, 0x9C4A, 0xF85A, 0x9C4B, 0xF85C, 0x9C4C, 0xF85F,\n\t0x9C4D, 0xF85B, 0x9C4E, 0xF860, 0x9C50, 0xF859, 0x9C52, 0xF857,\n\t0x9C54, 0xC5EB, 0x9C55, 0xF85D, 0x9C56, 0xC5ED, 0x9C57, 0xC5EC,\n\t0x9C58, 0xF858, 0x9C59, 0xF85E, 0x9C5E, 0xF8DA, 0x9C5F, 0xC64D,\n\t0x9C60, 0xF8DB, 0x9C62, 0xF8D9, 0x9C63, 0xF8D6, 0x9C66, 0xF8D8,\n\t0x9C67, 0xF8D7, 0x9C68, 0xF95A, 0x9C6D, 0xF95C, 0x9C6E, 0xF95B,\n\t0x9C71, 0xF979, 0x9C73, 0xF978, 0x9C74, 0xF977, 0x9C75, 0xF97A,\n\t0x9C77, 0xC673, 0x9C78, 0xC674, 0x9C79, 0xF9CA, 0x9C7A, 0xF9CE,\n\t0x9CE5, 0xB3BE, 0x9CE6, 0xDCAF, 0x9CE7, 0xE0ED, 0x9CE9, 0xB9A7,\n\t0x9CEA, 0xE0EB, 0x9CED, 0xE0EC, 0x9CF1, 0xE4E2, 0x9CF2, 0xE4E3,\n\t0x9CF3, 0xBBF1, 0x9CF4, 0xBBEF, 0x9CF5, 0xE4E4, 0x9CF6, 0xBBF0,\n\t0x9CF7, 0xE8E8, 0x9CF9, 0xE8EB, 0x9CFA, 0xE8E5, 0x9CFB, 0xE8EC,\n\t0x9CFC, 0xE8E4, 0x9CFD, 0xE8E6, 0x9CFF, 0xE8E7, 0x9D00, 0xE8EA,\n\t0x9D03, 0xBEA1, 0x9D04, 0xE8EF, 0x9D05, 0xE8EE, 0x9D06, 0xBE7D,\n\t0x9D07, 0xE8E9, 0x9D08, 0xE8ED, 0x9D09, 0xBE7E, 0x9D10, 0xECAC,\n\t0x9D12, 0xC06F, 0x9D14, 0xECA7, 0x9D15, 0xC06B, 0x9D17, 0xECA4,\n\t0x9D18, 0xECAA, 0x9D19, 0xECAD, 0x9D1B, 0xC070, 0x9D1D, 0xECA9,\n\t0x9D1E, 0xECA6, 0x9D1F, 0xECAE, 0x9D20, 0xECA5, 0x9D22, 0xECAB,\n\t0x9D23, 0xC06C, 0x9D25, 0xECA3, 0x9D26, 0xC06D, 0x9D28, 0xC06E,\n\t0x9D29, 0xECA8, 0x9D2D, 0xEFA9, 0x9D2E, 0xEF7A, 0x9D2F, 0xEF7B,\n\t0x9D30, 0xEF7E, 0x9D31, 0xEF7C, 0x9D33, 0xEF76, 0x9D36, 0xEF79,\n\t0x9D37, 0xEFA5, 0x9D38, 0xEF7D, 0x9D3B, 0xC245, 0x9D3D, 0xEFA7,\n\t0x9D3E, 0xEFA4, 0x9D3F, 0xC246, 0x9D40, 0xEFA6, 0x9D41, 0xEF77,\n\t0x9D42, 0xEFA2, 0x9D43, 0xEFA3, 0x9D45, 0xEFA1, 0x9D4A, 0xF1D2,\n\t0x9D4B, 0xF1D4, 0x9D4C, 0xF1D7, 0x9D4F, 0xF1D1, 0x9D51, 0xC359,\n\t0x9D52, 0xF1D9, 0x9D53, 0xF1D0, 0x9D54, 0xF1DA, 0x9D56, 0xF1D6,\n\t0x9D57, 0xF1D8, 0x9D58, 0xF1DC, 0x9D59, 0xF1D5, 0x9D5A, 0xF1DD,\n\t0x9D5B, 0xF1D3, 0x9D5C, 0xF1CF, 0x9D5D, 0xC35A, 0x9D5F, 0xF1DB,\n\t0x9D60, 0xC35B, 0x9D61, 0xC44D, 0x9D67, 0xEF78, 0x9D68, 0xF3F1,\n\t0x9D69, 0xF3E8, 0x9D6A, 0xC44F, 0x9D6B, 0xF3E4, 0x9D6C, 0xC450,\n\t0x9D6F, 0xF3ED, 0x9D70, 0xF3E7, 0x9D71, 0xF3DD, 0x9D72, 0xC44E,\n\t0x9D73, 0xF3EA, 0x9D74, 0xF3E5, 0x9D75, 0xF3E6, 0x9D77, 0xF3D8,\n\t0x9D78, 0xF3DF, 0x9D79, 0xF3EE, 0x9D7B, 0xF3EB, 0x9D7D, 0xF3E3,\n\t0x9D7F, 0xF3EF, 0x9D80, 0xF3DE, 0x9D81, 0xF3D9, 0x9D82, 0xF3EC,\n\t0x9D84, 0xF3DB, 0x9D85, 0xF3E9, 0x9D86, 0xF3E0, 0x9D87, 0xF3F0,\n\t0x9D88, 0xF3DC, 0x9D89, 0xC44C, 0x9D8A, 0xF3DA, 0x9D8B, 0xF3E1,\n\t0x9D8C, 0xF3E2, 0x9D90, 0xF57D, 0x9D92, 0xF57B, 0x9D94, 0xF5A2,\n\t0x9D96, 0xF5AE, 0x9D97, 0xF5A5, 0x9D98, 0xF57C, 0x9D99, 0xF578,\n\t0x9D9A, 0xF5A7, 0x9D9B, 0xF57E, 0x9D9C, 0xF5A3, 0x9D9D, 0xF57A,\n\t0x9D9E, 0xF5AA, 0x9D9F, 0xF577, 0x9DA0, 0xF5A1, 0x9DA1, 0xF5A6,\n\t0x9DA2, 0xF5A8, 0x9DA3, 0xF5AB, 0x9DA4, 0xF579, 0x9DA6, 0xF5AF,\n\t0x9DA7, 0xF5B0, 0x9DA8, 0xF5A9, 0x9DA9, 0xF5AD, 0x9DAA, 0xF5A4,\n\t0x9DAC, 0xF6C1, 0x9DAD, 0xF6C4, 0x9DAF, 0xC561, 0x9DB1, 0xF6C3,\n\t0x9DB2, 0xF6C8, 0x9DB3, 0xF6C6, 0x9DB4, 0xC562, 0x9DB5, 0xF6BD,\n\t0x9DB6, 0xF6B3, 0x9DB7, 0xF6B2, 0x9DB8, 0xC564, 0x9DB9, 0xF6BF,\n\t0x9DBA, 0xF6C0, 0x9DBB, 0xF6BC, 0x9DBC, 0xF6B4, 0x9DBE, 0xF6B9,\n\t0x9DBF, 0xF5AC, 0x9DC1, 0xF6B5, 0x9DC2, 0xC563, 0x9DC3, 0xF6BB,\n\t0x9DC5, 0xF6BA, 0x9DC7, 0xF6B6, 0x9DC8, 0xF6C2, 0x9DCA, 0xF6B7,\n\t0x9DCB, 0xF7BB, 0x9DCC, 0xF6C5, 0x9DCD, 0xF6C7, 0x9DCE, 0xF6BE,\n\t0x9DCF, 0xF6B8, 0x9DD0, 0xF7BC, 0x9DD1, 0xF7BE, 0x9DD2, 0xF7B8,\n\t0x9DD3, 0xC5C2, 0x9DD5, 0xF7C5, 0x9DD6, 0xF7C3, 0x9DD7, 0xC5C3,\n\t0x9DD8, 0xF7C2, 0x9DD9, 0xF7C1, 0x9DDA, 0xF7BA, 0x9DDB, 0xF7B7,\n\t0x9DDC, 0xF7BD, 0x9DDD, 0xF7C6, 0x9DDE, 0xF7B9, 0x9DDF, 0xF7BF,\n\t0x9DE1, 0xF869, 0x9DE2, 0xF86E, 0x9DE3, 0xF864, 0x9DE4, 0xF867,\n\t0x9DE5, 0xC5EE, 0x9DE6, 0xF86B, 0x9DE8, 0xF872, 0x9DE9, 0xF7C0,\n\t0x9DEB, 0xF865, 0x9DEC, 0xF86F, 0x9DED, 0xF873, 0x9DEE, 0xF86A,\n\t0x9DEF, 0xF863, 0x9DF0, 0xF86D, 0x9DF2, 0xF86C, 0x9DF3, 0xF871,\n\t0x9DF4, 0xF870, 0x9DF5, 0xF7C4, 0x9DF6, 0xF868, 0x9DF7, 0xF862,\n\t0x9DF8, 0xF866, 0x9DF9, 0xC64E, 0x9DFA, 0xC64F, 0x9DFB, 0xF861,\n\t0x9DFD, 0xF8E6, 0x9DFE, 0xF8DD, 0x9DFF, 0xF8E5, 0x9E00, 0xF8E2,\n\t0x9E01, 0xF8E3, 0x9E02, 0xF8DC, 0x9E03, 0xF8DF, 0x9E04, 0xF8E7,\n\t0x9E05, 0xF8E1, 0x9E06, 0xF8E0, 0x9E07, 0xF8DE, 0x9E09, 0xF8E4,\n\t0x9E0B, 0xF95D, 0x9E0D, 0xF95E, 0x9E0F, 0xF960, 0x9E10, 0xF95F,\n\t0x9E11, 0xF962, 0x9E12, 0xF961, 0x9E13, 0xF97C, 0x9E14, 0xF97B,\n\t0x9E15, 0xF9B7, 0x9E17, 0xF9B8, 0x9E19, 0xF9C5, 0x9E1A, 0xC678,\n\t0x9E1B, 0xC67C, 0x9E1D, 0xF9CF, 0x9E1E, 0xC67D, 0x9E75, 0xB3BF,\n\t0x9E79, 0xC4D0, 0x9E7A, 0xF6C9, 0x9E7C, 0xC650, 0x9E7D, 0xC651,\n\t0x9E7F, 0xB3C0, 0x9E80, 0xE0EE, 0x9E82, 0xB9A8, 0x9E83, 0xE8F0,\n\t0x9E86, 0xECB0, 0x9E87, 0xECB1, 0x9E88, 0xECAF, 0x9E89, 0xEFAB,\n\t0x9E8A, 0xEFAA, 0x9E8B, 0xC247, 0x9E8C, 0xF1DF, 0x9E8D, 0xEFAC,\n\t0x9E8E, 0xF1DE, 0x9E91, 0xF3F3, 0x9E92, 0xC451, 0x9E93, 0xC453,\n\t0x9E94, 0xF3F2, 0x9E97, 0xC452, 0x9E99, 0xF5B1, 0x9E9A, 0xF5B3,\n\t0x9E9B, 0xF5B2, 0x9E9C, 0xF6CA, 0x9E9D, 0xC565, 0x9E9F, 0xC5EF,\n\t0x9EA0, 0xF8E8, 0x9EA1, 0xF963, 0x9EA4, 0xF9D2, 0x9EA5, 0xB3C1,\n\t0x9EA7, 0xE4E5, 0x9EA9, 0xBEA2, 0x9EAD, 0xECB3, 0x9EAE, 0xECB2,\n\t0x9EB0, 0xEFAD, 0x9EB4, 0xC454, 0x9EB5, 0xC4D1, 0x9EB6, 0xF7C7,\n\t0x9EB7, 0xF9CB, 0x9EBB, 0xB3C2, 0x9EBC, 0xBBF2, 0x9EBE, 0xBEA3,\n\t0x9EC0, 0xF3F4, 0x9EC2, 0xF874, 0x9EC3, 0xB6C0, 0x9EC8, 0xEFAE,\n\t0x9ECC, 0xC664, 0x9ECD, 0xB6C1, 0x9ECE, 0xBEA4, 0x9ECF, 0xC248,\n\t0x9ED0, 0xF875, 0x9ED1, 0xB6C2, 0x9ED3, 0xE8F1, 0x9ED4, 0xC072,\n\t0x9ED5, 0xECB4, 0x9ED6, 0xECB5, 0x9ED8, 0xC071, 0x9EDA, 0xEFAF,\n\t0x9EDB, 0xC24C, 0x9EDC, 0xC24A, 0x9EDD, 0xC24B, 0x9EDE, 0xC249,\n\t0x9EDF, 0xF1E0, 0x9EE0, 0xC35C, 0x9EE4, 0xF5B5, 0x9EE5, 0xF5B4,\n\t0x9EE6, 0xF5B7, 0x9EE7, 0xF5B6, 0x9EE8, 0xC4D2, 0x9EEB, 0xF6CB,\n\t0x9EED, 0xF6CD, 0x9EEE, 0xF6CC, 0x9EEF, 0xC566, 0x9EF0, 0xF7C8,\n\t0x9EF2, 0xF876, 0x9EF3, 0xF877, 0x9EF4, 0xC5F0, 0x9EF5, 0xF964,\n\t0x9EF6, 0xF97D, 0x9EF7, 0xC675, 0x9EF9, 0xDCB0, 0x9EFA, 0xECB6,\n\t0x9EFB, 0xEFB0, 0x9EFC, 0xF3F5, 0x9EFD, 0xE0EF, 0x9EFF, 0xEFB1,\n\t0x9F00, 0xF1E2, 0x9F01, 0xF1E1, 0x9F06, 0xF878, 0x9F07, 0xC652,\n\t0x9F09, 0xF965, 0x9F0A, 0xF97E, 0x9F0E, 0xB9A9, 0x9F0F, 0xE8F2,\n\t0x9F10, 0xE8F3, 0x9F12, 0xECB7, 0x9F13, 0xB9AA, 0x9F15, 0xC35D,\n\t0x9F16, 0xF1E3, 0x9F18, 0xF6CF, 0x9F19, 0xC567, 0x9F1A, 0xF6D0,\n\t0x9F1B, 0xF6CE, 0x9F1C, 0xF879, 0x9F1E, 0xF8E9, 0x9F20, 0xB9AB,\n\t0x9F22, 0xEFB4, 0x9F23, 0xEFB3, 0x9F24, 0xEFB2, 0x9F25, 0xF1E4,\n\t0x9F28, 0xF1E8, 0x9F29, 0xF1E7, 0x9F2A, 0xF1E6, 0x9F2B, 0xF1E5,\n\t0x9F2C, 0xC35E, 0x9F2D, 0xF3F6, 0x9F2E, 0xF5B9, 0x9F2F, 0xC4D3,\n\t0x9F30, 0xF5B8, 0x9F31, 0xF6D1, 0x9F32, 0xF7CB, 0x9F33, 0xF7CA,\n\t0x9F34, 0xC5C4, 0x9F35, 0xF7C9, 0x9F36, 0xF87C, 0x9F37, 0xF87B,\n\t0x9F38, 0xF87A, 0x9F3B, 0xBBF3, 0x9F3D, 0xECB8, 0x9F3E, 0xC24D,\n\t0x9F40, 0xF3F7, 0x9F41, 0xF3F8, 0x9F42, 0xF7CC, 0x9F43, 0xF87D,\n\t0x9F46, 0xF8EA, 0x9F47, 0xF966, 0x9F48, 0xF9B9, 0x9F49, 0xF9D4,\n\t0x9F4A, 0xBBF4, 0x9F4B, 0xC24E, 0x9F4C, 0xF1E9, 0x9F4D, 0xF3F9,\n\t0x9F4E, 0xF6D2, 0x9F4F, 0xF87E, 0x9F52, 0xBEA6, 0x9F54, 0xEFB5,\n\t0x9F55, 0xF1EA, 0x9F56, 0xF3FA, 0x9F57, 0xF3FB, 0x9F58, 0xF3FC,\n\t0x9F59, 0xF5BE, 0x9F5B, 0xF5BA, 0x9F5C, 0xC568, 0x9F5D, 0xF5BD,\n\t0x9F5E, 0xF5BC, 0x9F5F, 0xC4D4, 0x9F60, 0xF5BB, 0x9F61, 0xC4D6,\n\t0x9F63, 0xC4D5, 0x9F64, 0xF6D4, 0x9F65, 0xF6D3, 0x9F66, 0xC569,\n\t0x9F67, 0xC56A, 0x9F6A, 0xC5C6, 0x9F6B, 0xF7CD, 0x9F6C, 0xC5C5,\n\t0x9F6E, 0xF8A3, 0x9F6F, 0xF8A4, 0x9F70, 0xF8A2, 0x9F71, 0xF8A1,\n\t0x9F72, 0xC654, 0x9F74, 0xF8EB, 0x9F75, 0xF8EC, 0x9F76, 0xF8ED,\n\t0x9F77, 0xC653, 0x9F78, 0xF967, 0x9F79, 0xF96A, 0x9F7A, 0xF969,\n\t0x9F7B, 0xF968, 0x9F7E, 0xF9D3, 0x9F8D, 0xC073, 0x9F90, 0xC365,\n\t0x9F91, 0xF5BF, 0x9F92, 0xF6D5, 0x9F94, 0xC5C7, 0x9F95, 0xF7CE,\n\t0x9F98, 0xF9D5, 0x9F9C, 0xC074, 0x9FA0, 0xEFB6, 0x9FA2, 0xF7CF,\n\t0x9FA4, 0xF9A1, 0xFA0C, 0xC94A, 0xFA0D, 0xDDFC, 0xFE30, 0xA14A,\n\t0xFE31, 0xA157, 0xFE33, 0xA159, 0xFE34, 0xA15B, 0xFE35, 0xA15F,\n\t0xFE36, 0xA160, 0xFE37, 0xA163, 0xFE38, 0xA164, 0xFE39, 0xA167,\n\t0xFE3A, 0xA168, 0xFE3B, 0xA16B, 0xFE3C, 0xA16C, 0xFE3D, 0xA16F,\n\t0xFE3E, 0xA170, 0xFE3F, 0xA173, 0xFE40, 0xA174, 0xFE41, 0xA177,\n\t0xFE42, 0xA178, 0xFE43, 0xA17B, 0xFE44, 0xA17C, 0xFE49, 0xA1C6,\n\t0xFE4A, 0xA1C7, 0xFE4B, 0xA1CA, 0xFE4C, 0xA1CB, 0xFE4D, 0xA1C8,\n\t0xFE4E, 0xA1C9, 0xFE4F, 0xA15C, 0xFE50, 0xA14D, 0xFE51, 0xA14E,\n\t0xFE52, 0xA14F, 0xFE54, 0xA151, 0xFE55, 0xA152, 0xFE56, 0xA153,\n\t0xFE57, 0xA154, 0xFE59, 0xA17D, 0xFE5A, 0xA17E, 0xFE5B, 0xA1A1,\n\t0xFE5C, 0xA1A2, 0xFE5D, 0xA1A3, 0xFE5E, 0xA1A4, 0xFE5F, 0xA1CC,\n\t0xFE60, 0xA1CD, 0xFE61, 0xA1CE, 0xFE62, 0xA1DE, 0xFE63, 0xA1DF,\n\t0xFE64, 0xA1E0, 0xFE65, 0xA1E1, 0xFE66, 0xA1E2, 0xFE68, 0xA242,\n\t0xFE69, 0xA24C, 0xFE6A, 0xA24D, 0xFE6B, 0xA24E, 0xFF01, 0xA149,\n\t0xFF03, 0xA1AD, 0xFF04, 0xA243, 0xFF05, 0xA248, 0xFF06, 0xA1AE,\n\t0xFF08, 0xA15D, 0xFF09, 0xA15E, 0xFF0A, 0xA1AF, 0xFF0B, 0xA1CF,\n\t0xFF0C, 0xA141, 0xFF0D, 0xA1D0, 0xFF0E, 0xA144, 0xFF0F, 0xA1FE,\n\t0xFF10, 0xA2AF, 0xFF11, 0xA2B0, 0xFF12, 0xA2B1, 0xFF13, 0xA2B2,\n\t0xFF14, 0xA2B3, 0xFF15, 0xA2B4, 0xFF16, 0xA2B5, 0xFF17, 0xA2B6,\n\t0xFF18, 0xA2B7, 0xFF19, 0xA2B8, 0xFF1A, 0xA147, 0xFF1B, 0xA146,\n\t0xFF1C, 0xA1D5, 0xFF1D, 0xA1D7, 0xFF1E, 0xA1D6, 0xFF1F, 0xA148,\n\t0xFF20, 0xA249, 0xFF21, 0xA2CF, 0xFF22, 0xA2D0, 0xFF23, 0xA2D1,\n\t0xFF24, 0xA2D2, 0xFF25, 0xA2D3, 0xFF26, 0xA2D4, 0xFF27, 0xA2D5,\n\t0xFF28, 0xA2D6, 0xFF29, 0xA2D7, 0xFF2A, 0xA2D8, 0xFF2B, 0xA2D9,\n\t0xFF2C, 0xA2DA, 0xFF2D, 0xA2DB, 0xFF2E, 0xA2DC, 0xFF2F, 0xA2DD,\n\t0xFF30, 0xA2DE, 0xFF31, 0xA2DF, 0xFF32, 0xA2E0, 0xFF33, 0xA2E1,\n\t0xFF34, 0xA2E2, 0xFF35, 0xA2E3, 0xFF36, 0xA2E4, 0xFF37, 0xA2E5,\n\t0xFF38, 0xA2E6, 0xFF39, 0xA2E7, 0xFF3A, 0xA2E8, 0xFF3C, 0xA240,\n\t0xFF3F, 0xA1C4, 0xFF41, 0xA2E9, 0xFF42, 0xA2EA, 0xFF43, 0xA2EB,\n\t0xFF44, 0xA2EC, 0xFF45, 0xA2ED, 0xFF46, 0xA2EE, 0xFF47, 0xA2EF,\n\t0xFF48, 0xA2F0, 0xFF49, 0xA2F1, 0xFF4A, 0xA2F2, 0xFF4B, 0xA2F3,\n\t0xFF4C, 0xA2F4, 0xFF4D, 0xA2F5, 0xFF4E, 0xA2F6, 0xFF4F, 0xA2F7,\n\t0xFF50, 0xA2F8, 0xFF51, 0xA2F9, 0xFF52, 0xA2FA, 0xFF53, 0xA2FB,\n\t0xFF54, 0xA2FC, 0xFF55, 0xA2FD, 0xFF56, 0xA2FE, 0xFF57, 0xA340,\n\t0xFF58, 0xA341, 0xFF59, 0xA342, 0xFF5A, 0xA343, 0xFF5B, 0xA161,\n\t0xFF5C, 0xA155, 0xFF5D, 0xA162, 0xFF5E, 0xA1E3, 0xFFE0, 0xA246,\n\t0xFFE1, 0xA247, 0xFFE3, 0xA1C3, 0xFFE5, 0xA244, 0, 0\n};\n\nstatic\nconst WCHAR oem2uni[] = {\n/*\tOEM - Unicode,  OEM - Unicode,  OEM - Unicode,  OEM - Unicode */\n\t0xA140, 0x3000, 0xA141, 0xFF0C, 0xA142, 0x3001, 0xA143, 0x3002,\n\t0xA144, 0xFF0E, 0xA145, 0x2027, 0xA146, 0xFF1B, 0xA147, 0xFF1A,\n\t0xA148, 0xFF1F, 0xA149, 0xFF01, 0xA14A, 0xFE30, 0xA14B, 0x2026,\n\t0xA14C, 0x2025, 0xA14D, 0xFE50, 0xA14E, 0xFE51, 0xA14F, 0xFE52,\n\t0xA150, 0x00B7, 0xA151, 0xFE54, 0xA152, 0xFE55, 0xA153, 0xFE56,\n\t0xA154, 0xFE57, 0xA155, 0xFF5C, 0xA156, 0x2013, 0xA157, 0xFE31,\n\t0xA158, 0x2014, 0xA159, 0xFE33, 0xA15A, 0x2574, 0xA15B, 0xFE34,\n\t0xA15C, 0xFE4F, 0xA15D, 0xFF08, 0xA15E, 0xFF09, 0xA15F, 0xFE35,\n\t0xA160, 0xFE36, 0xA161, 0xFF5B, 0xA162, 0xFF5D, 0xA163, 0xFE37,\n\t0xA164, 0xFE38, 0xA165, 0x3014, 0xA166, 0x3015, 0xA167, 0xFE39,\n\t0xA168, 0xFE3A, 0xA169, 0x3010, 0xA16A, 0x3011, 0xA16B, 0xFE3B,\n\t0xA16C, 0xFE3C, 0xA16D, 0x300A, 0xA16E, 0x300B, 0xA16F, 0xFE3D,\n\t0xA170, 0xFE3E, 0xA171, 0x3008, 0xA172, 0x3009, 0xA173, 0xFE3F,\n\t0xA174, 0xFE40, 0xA175, 0x300C, 0xA176, 0x300D, 0xA177, 0xFE41,\n\t0xA178, 0xFE42, 0xA179, 0x300E, 0xA17A, 0x300F, 0xA17B, 0xFE43,\n\t0xA17C, 0xFE44, 0xA17D, 0xFE59, 0xA17E, 0xFE5A, 0xA1A1, 0xFE5B,\n\t0xA1A2, 0xFE5C, 0xA1A3, 0xFE5D, 0xA1A4, 0xFE5E, 0xA1A5, 0x2018,\n\t0xA1A6, 0x2019, 0xA1A7, 0x201C, 0xA1A8, 0x201D, 0xA1A9, 0x301D,\n\t0xA1AA, 0x301E, 0xA1AB, 0x2035, 0xA1AC, 0x2032, 0xA1AD, 0xFF03,\n\t0xA1AE, 0xFF06, 0xA1AF, 0xFF0A, 0xA1B0, 0x203B, 0xA1B1, 0x00A7,\n\t0xA1B2, 0x3003, 0xA1B3, 0x25CB, 0xA1B4, 0x25CF, 0xA1B5, 0x25B3,\n\t0xA1B6, 0x25B2, 0xA1B7, 0x25CE, 0xA1B8, 0x2606, 0xA1B9, 0x2605,\n\t0xA1BA, 0x25C7, 0xA1BB, 0x25C6, 0xA1BC, 0x25A1, 0xA1BD, 0x25A0,\n\t0xA1BE, 0x25BD, 0xA1BF, 0x25BC, 0xA1C0, 0x32A3, 0xA1C1, 0x2105,\n\t0xA1C2, 0x00AF, 0xA1C3, 0xFFE3, 0xA1C4, 0xFF3F, 0xA1C5, 0x02CD,\n\t0xA1C6, 0xFE49, 0xA1C7, 0xFE4A, 0xA1C8, 0xFE4D, 0xA1C9, 0xFE4E,\n\t0xA1CA, 0xFE4B, 0xA1CB, 0xFE4C, 0xA1CC, 0xFE5F, 0xA1CD, 0xFE60,\n\t0xA1CE, 0xFE61, 0xA1CF, 0xFF0B, 0xA1D0, 0xFF0D, 0xA1D1, 0x00D7,\n\t0xA1D2, 0x00F7, 0xA1D3, 0x00B1, 0xA1D4, 0x221A, 0xA1D5, 0xFF1C,\n\t0xA1D6, 0xFF1E, 0xA1D7, 0xFF1D, 0xA1D8, 0x2266, 0xA1D9, 0x2267,\n\t0xA1DA, 0x2260, 0xA1DB, 0x221E, 0xA1DC, 0x2252, 0xA1DD, 0x2261,\n\t0xA1DE, 0xFE62, 0xA1DF, 0xFE63, 0xA1E0, 0xFE64, 0xA1E1, 0xFE65,\n\t0xA1E2, 0xFE66, 0xA1E3, 0xFF5E, 0xA1E4, 0x2229, 0xA1E5, 0x222A,\n\t0xA1E6, 0x22A5, 0xA1E7, 0x2220, 0xA1E8, 0x221F, 0xA1E9, 0x22BF,\n\t0xA1EA, 0x33D2, 0xA1EB, 0x33D1, 0xA1EC, 0x222B, 0xA1ED, 0x222E,\n\t0xA1EE, 0x2235, 0xA1EF, 0x2234, 0xA1F0, 0x2640, 0xA1F1, 0x2642,\n\t0xA1F2, 0x2295, 0xA1F3, 0x2299, 0xA1F4, 0x2191, 0xA1F5, 0x2193,\n\t0xA1F6, 0x2190, 0xA1F7, 0x2192, 0xA1F8, 0x2196, 0xA1F9, 0x2197,\n\t0xA1FA, 0x2199, 0xA1FB, 0x2198, 0xA1FC, 0x2225, 0xA1FD, 0x2223,\n\t0xA1FE, 0xFF0F, 0xA240, 0xFF3C, 0xA241, 0x2215, 0xA242, 0xFE68,\n\t0xA243, 0xFF04, 0xA244, 0xFFE5, 0xA245, 0x3012, 0xA246, 0xFFE0,\n\t0xA247, 0xFFE1, 0xA248, 0xFF05, 0xA249, 0xFF20, 0xA24A, 0x2103,\n\t0xA24B, 0x2109, 0xA24C, 0xFE69, 0xA24D, 0xFE6A, 0xA24E, 0xFE6B,\n\t0xA24F, 0x33D5, 0xA250, 0x339C, 0xA251, 0x339D, 0xA252, 0x339E,\n\t0xA253, 0x33CE, 0xA254, 0x33A1, 0xA255, 0x338E, 0xA256, 0x338F,\n\t0xA257, 0x33C4, 0xA258, 0x00B0, 0xA259, 0x5159, 0xA25A, 0x515B,\n\t0xA25B, 0x515E, 0xA25C, 0x515D, 0xA25D, 0x5161, 0xA25E, 0x5163,\n\t0xA25F, 0x55E7, 0xA260, 0x74E9, 0xA261, 0x7CCE, 0xA262, 0x2581,\n\t0xA263, 0x2582, 0xA264, 0x2583, 0xA265, 0x2584, 0xA266, 0x2585,\n\t0xA267, 0x2586, 0xA268, 0x2587, 0xA269, 0x2588, 0xA26A, 0x258F,\n\t0xA26B, 0x258E, 0xA26C, 0x258D, 0xA26D, 0x258C, 0xA26E, 0x258B,\n\t0xA26F, 0x258A, 0xA270, 0x2589, 0xA271, 0x253C, 0xA272, 0x2534,\n\t0xA273, 0x252C, 0xA274, 0x2524, 0xA275, 0x251C, 0xA276, 0x2594,\n\t0xA277, 0x2500, 0xA278, 0x2502, 0xA279, 0x2595, 0xA27A, 0x250C,\n\t0xA27B, 0x2510, 0xA27C, 0x2514, 0xA27D, 0x2518, 0xA27E, 0x256D,\n\t0xA2A1, 0x256E, 0xA2A2, 0x2570, 0xA2A3, 0x256F, 0xA2A4, 0x2550,\n\t0xA2A5, 0x255E, 0xA2A6, 0x256A, 0xA2A7, 0x2561, 0xA2A8, 0x25E2,\n\t0xA2A9, 0x25E3, 0xA2AA, 0x25E5, 0xA2AB, 0x25E4, 0xA2AC, 0x2571,\n\t0xA2AD, 0x2572, 0xA2AE, 0x2573, 0xA2AF, 0xFF10, 0xA2B0, 0xFF11,\n\t0xA2B1, 0xFF12, 0xA2B2, 0xFF13, 0xA2B3, 0xFF14, 0xA2B4, 0xFF15,\n\t0xA2B5, 0xFF16, 0xA2B6, 0xFF17, 0xA2B7, 0xFF18, 0xA2B8, 0xFF19,\n\t0xA2B9, 0x2160, 0xA2BA, 0x2161, 0xA2BB, 0x2162, 0xA2BC, 0x2163,\n\t0xA2BD, 0x2164, 0xA2BE, 0x2165, 0xA2BF, 0x2166, 0xA2C0, 0x2167,\n\t0xA2C1, 0x2168, 0xA2C2, 0x2169, 0xA2C3, 0x3021, 0xA2C4, 0x3022,\n\t0xA2C5, 0x3023, 0xA2C6, 0x3024, 0xA2C7, 0x3025, 0xA2C8, 0x3026,\n\t0xA2C9, 0x3027, 0xA2CA, 0x3028, 0xA2CB, 0x3029, 0xA2CC, 0x5341,\n\t0xA2CD, 0x5344, 0xA2CE, 0x5345, 0xA2CF, 0xFF21, 0xA2D0, 0xFF22,\n\t0xA2D1, 0xFF23, 0xA2D2, 0xFF24, 0xA2D3, 0xFF25, 0xA2D4, 0xFF26,\n\t0xA2D5, 0xFF27, 0xA2D6, 0xFF28, 0xA2D7, 0xFF29, 0xA2D8, 0xFF2A,\n\t0xA2D9, 0xFF2B, 0xA2DA, 0xFF2C, 0xA2DB, 0xFF2D, 0xA2DC, 0xFF2E,\n\t0xA2DD, 0xFF2F, 0xA2DE, 0xFF30, 0xA2DF, 0xFF31, 0xA2E0, 0xFF32,\n\t0xA2E1, 0xFF33, 0xA2E2, 0xFF34, 0xA2E3, 0xFF35, 0xA2E4, 0xFF36,\n\t0xA2E5, 0xFF37, 0xA2E6, 0xFF38, 0xA2E7, 0xFF39, 0xA2E8, 0xFF3A,\n\t0xA2E9, 0xFF41, 0xA2EA, 0xFF42, 0xA2EB, 0xFF43, 0xA2EC, 0xFF44,\n\t0xA2ED, 0xFF45, 0xA2EE, 0xFF46, 0xA2EF, 0xFF47, 0xA2F0, 0xFF48,\n\t0xA2F1, 0xFF49, 0xA2F2, 0xFF4A, 0xA2F3, 0xFF4B, 0xA2F4, 0xFF4C,\n\t0xA2F5, 0xFF4D, 0xA2F6, 0xFF4E, 0xA2F7, 0xFF4F, 0xA2F8, 0xFF50,\n\t0xA2F9, 0xFF51, 0xA2FA, 0xFF52, 0xA2FB, 0xFF53, 0xA2FC, 0xFF54,\n\t0xA2FD, 0xFF55, 0xA2FE, 0xFF56, 0xA340, 0xFF57, 0xA341, 0xFF58,\n\t0xA342, 0xFF59, 0xA343, 0xFF5A, 0xA344, 0x0391, 0xA345, 0x0392,\n\t0xA346, 0x0393, 0xA347, 0x0394, 0xA348, 0x0395, 0xA349, 0x0396,\n\t0xA34A, 0x0397, 0xA34B, 0x0398, 0xA34C, 0x0399, 0xA34D, 0x039A,\n\t0xA34E, 0x039B, 0xA34F, 0x039C, 0xA350, 0x039D, 0xA351, 0x039E,\n\t0xA352, 0x039F, 0xA353, 0x03A0, 0xA354, 0x03A1, 0xA355, 0x03A3,\n\t0xA356, 0x03A4, 0xA357, 0x03A5, 0xA358, 0x03A6, 0xA359, 0x03A7,\n\t0xA35A, 0x03A8, 0xA35B, 0x03A9, 0xA35C, 0x03B1, 0xA35D, 0x03B2,\n\t0xA35E, 0x03B3, 0xA35F, 0x03B4, 0xA360, 0x03B5, 0xA361, 0x03B6,\n\t0xA362, 0x03B7, 0xA363, 0x03B8, 0xA364, 0x03B9, 0xA365, 0x03BA,\n\t0xA366, 0x03BB, 0xA367, 0x03BC, 0xA368, 0x03BD, 0xA369, 0x03BE,\n\t0xA36A, 0x03BF, 0xA36B, 0x03C0, 0xA36C, 0x03C1, 0xA36D, 0x03C3,\n\t0xA36E, 0x03C4, 0xA36F, 0x03C5, 0xA370, 0x03C6, 0xA371, 0x03C7,\n\t0xA372, 0x03C8, 0xA373, 0x03C9, 0xA374, 0x3105, 0xA375, 0x3106,\n\t0xA376, 0x3107, 0xA377, 0x3108, 0xA378, 0x3109, 0xA379, 0x310A,\n\t0xA37A, 0x310B, 0xA37B, 0x310C, 0xA37C, 0x310D, 0xA37D, 0x310E,\n\t0xA37E, 0x310F, 0xA3A1, 0x3110, 0xA3A2, 0x3111, 0xA3A3, 0x3112,\n\t0xA3A4, 0x3113, 0xA3A5, 0x3114, 0xA3A6, 0x3115, 0xA3A7, 0x3116,\n\t0xA3A8, 0x3117, 0xA3A9, 0x3118, 0xA3AA, 0x3119, 0xA3AB, 0x311A,\n\t0xA3AC, 0x311B, 0xA3AD, 0x311C, 0xA3AE, 0x311D, 0xA3AF, 0x311E,\n\t0xA3B0, 0x311F, 0xA3B1, 0x3120, 0xA3B2, 0x3121, 0xA3B3, 0x3122,\n\t0xA3B4, 0x3123, 0xA3B5, 0x3124, 0xA3B6, 0x3125, 0xA3B7, 0x3126,\n\t0xA3B8, 0x3127, 0xA3B9, 0x3128, 0xA3BA, 0x3129, 0xA3BB, 0x02D9,\n\t0xA3BC, 0x02C9, 0xA3BD, 0x02CA, 0xA3BE, 0x02C7, 0xA3BF, 0x02CB,\n\t0xA3E1, 0x20AC, 0xA440, 0x4E00, 0xA441, 0x4E59, 0xA442, 0x4E01,\n\t0xA443, 0x4E03, 0xA444, 0x4E43, 0xA445, 0x4E5D, 0xA446, 0x4E86,\n\t0xA447, 0x4E8C, 0xA448, 0x4EBA, 0xA449, 0x513F, 0xA44A, 0x5165,\n\t0xA44B, 0x516B, 0xA44C, 0x51E0, 0xA44D, 0x5200, 0xA44E, 0x5201,\n\t0xA44F, 0x529B, 0xA450, 0x5315, 0xA451, 0x5341, 0xA452, 0x535C,\n\t0xA453, 0x53C8, 0xA454, 0x4E09, 0xA455, 0x4E0B, 0xA456, 0x4E08,\n\t0xA457, 0x4E0A, 0xA458, 0x4E2B, 0xA459, 0x4E38, 0xA45A, 0x51E1,\n\t0xA45B, 0x4E45, 0xA45C, 0x4E48, 0xA45D, 0x4E5F, 0xA45E, 0x4E5E,\n\t0xA45F, 0x4E8E, 0xA460, 0x4EA1, 0xA461, 0x5140, 0xA462, 0x5203,\n\t0xA463, 0x52FA, 0xA464, 0x5343, 0xA465, 0x53C9, 0xA466, 0x53E3,\n\t0xA467, 0x571F, 0xA468, 0x58EB, 0xA469, 0x5915, 0xA46A, 0x5927,\n\t0xA46B, 0x5973, 0xA46C, 0x5B50, 0xA46D, 0x5B51, 0xA46E, 0x5B53,\n\t0xA46F, 0x5BF8, 0xA470, 0x5C0F, 0xA471, 0x5C22, 0xA472, 0x5C38,\n\t0xA473, 0x5C71, 0xA474, 0x5DDD, 0xA475, 0x5DE5, 0xA476, 0x5DF1,\n\t0xA477, 0x5DF2, 0xA478, 0x5DF3, 0xA479, 0x5DFE, 0xA47A, 0x5E72,\n\t0xA47B, 0x5EFE, 0xA47C, 0x5F0B, 0xA47D, 0x5F13, 0xA47E, 0x624D,\n\t0xA4A1, 0x4E11, 0xA4A2, 0x4E10, 0xA4A3, 0x4E0D, 0xA4A4, 0x4E2D,\n\t0xA4A5, 0x4E30, 0xA4A6, 0x4E39, 0xA4A7, 0x4E4B, 0xA4A8, 0x5C39,\n\t0xA4A9, 0x4E88, 0xA4AA, 0x4E91, 0xA4AB, 0x4E95, 0xA4AC, 0x4E92,\n\t0xA4AD, 0x4E94, 0xA4AE, 0x4EA2, 0xA4AF, 0x4EC1, 0xA4B0, 0x4EC0,\n\t0xA4B1, 0x4EC3, 0xA4B2, 0x4EC6, 0xA4B3, 0x4EC7, 0xA4B4, 0x4ECD,\n\t0xA4B5, 0x4ECA, 0xA4B6, 0x4ECB, 0xA4B7, 0x4EC4, 0xA4B8, 0x5143,\n\t0xA4B9, 0x5141, 0xA4BA, 0x5167, 0xA4BB, 0x516D, 0xA4BC, 0x516E,\n\t0xA4BD, 0x516C, 0xA4BE, 0x5197, 0xA4BF, 0x51F6, 0xA4C0, 0x5206,\n\t0xA4C1, 0x5207, 0xA4C2, 0x5208, 0xA4C3, 0x52FB, 0xA4C4, 0x52FE,\n\t0xA4C5, 0x52FF, 0xA4C6, 0x5316, 0xA4C7, 0x5339, 0xA4C8, 0x5348,\n\t0xA4C9, 0x5347, 0xA4CA, 0x5345, 0xA4CB, 0x535E, 0xA4CC, 0x5384,\n\t0xA4CD, 0x53CB, 0xA4CE, 0x53CA, 0xA4CF, 0x53CD, 0xA4D0, 0x58EC,\n\t0xA4D1, 0x5929, 0xA4D2, 0x592B, 0xA4D3, 0x592A, 0xA4D4, 0x592D,\n\t0xA4D5, 0x5B54, 0xA4D6, 0x5C11, 0xA4D7, 0x5C24, 0xA4D8, 0x5C3A,\n\t0xA4D9, 0x5C6F, 0xA4DA, 0x5DF4, 0xA4DB, 0x5E7B, 0xA4DC, 0x5EFF,\n\t0xA4DD, 0x5F14, 0xA4DE, 0x5F15, 0xA4DF, 0x5FC3, 0xA4E0, 0x6208,\n\t0xA4E1, 0x6236, 0xA4E2, 0x624B, 0xA4E3, 0x624E, 0xA4E4, 0x652F,\n\t0xA4E5, 0x6587, 0xA4E6, 0x6597, 0xA4E7, 0x65A4, 0xA4E8, 0x65B9,\n\t0xA4E9, 0x65E5, 0xA4EA, 0x66F0, 0xA4EB, 0x6708, 0xA4EC, 0x6728,\n\t0xA4ED, 0x6B20, 0xA4EE, 0x6B62, 0xA4EF, 0x6B79, 0xA4F0, 0x6BCB,\n\t0xA4F1, 0x6BD4, 0xA4F2, 0x6BDB, 0xA4F3, 0x6C0F, 0xA4F4, 0x6C34,\n\t0xA4F5, 0x706B, 0xA4F6, 0x722A, 0xA4F7, 0x7236, 0xA4F8, 0x723B,\n\t0xA4F9, 0x7247, 0xA4FA, 0x7259, 0xA4FB, 0x725B, 0xA4FC, 0x72AC,\n\t0xA4FD, 0x738B, 0xA4FE, 0x4E19, 0xA540, 0x4E16, 0xA541, 0x4E15,\n\t0xA542, 0x4E14, 0xA543, 0x4E18, 0xA544, 0x4E3B, 0xA545, 0x4E4D,\n\t0xA546, 0x4E4F, 0xA547, 0x4E4E, 0xA548, 0x4EE5, 0xA549, 0x4ED8,\n\t0xA54A, 0x4ED4, 0xA54B, 0x4ED5, 0xA54C, 0x4ED6, 0xA54D, 0x4ED7,\n\t0xA54E, 0x4EE3, 0xA54F, 0x4EE4, 0xA550, 0x4ED9, 0xA551, 0x4EDE,\n\t0xA552, 0x5145, 0xA553, 0x5144, 0xA554, 0x5189, 0xA555, 0x518A,\n\t0xA556, 0x51AC, 0xA557, 0x51F9, 0xA558, 0x51FA, 0xA559, 0x51F8,\n\t0xA55A, 0x520A, 0xA55B, 0x52A0, 0xA55C, 0x529F, 0xA55D, 0x5305,\n\t0xA55E, 0x5306, 0xA55F, 0x5317, 0xA560, 0x531D, 0xA561, 0x4EDF,\n\t0xA562, 0x534A, 0xA563, 0x5349, 0xA564, 0x5361, 0xA565, 0x5360,\n\t0xA566, 0x536F, 0xA567, 0x536E, 0xA568, 0x53BB, 0xA569, 0x53EF,\n\t0xA56A, 0x53E4, 0xA56B, 0x53F3, 0xA56C, 0x53EC, 0xA56D, 0x53EE,\n\t0xA56E, 0x53E9, 0xA56F, 0x53E8, 0xA570, 0x53FC, 0xA571, 0x53F8,\n\t0xA572, 0x53F5, 0xA573, 0x53EB, 0xA574, 0x53E6, 0xA575, 0x53EA,\n\t0xA576, 0x53F2, 0xA577, 0x53F1, 0xA578, 0x53F0, 0xA579, 0x53E5,\n\t0xA57A, 0x53ED, 0xA57B, 0x53FB, 0xA57C, 0x56DB, 0xA57D, 0x56DA,\n\t0xA57E, 0x5916, 0xA5A1, 0x592E, 0xA5A2, 0x5931, 0xA5A3, 0x5974,\n\t0xA5A4, 0x5976, 0xA5A5, 0x5B55, 0xA5A6, 0x5B83, 0xA5A7, 0x5C3C,\n\t0xA5A8, 0x5DE8, 0xA5A9, 0x5DE7, 0xA5AA, 0x5DE6, 0xA5AB, 0x5E02,\n\t0xA5AC, 0x5E03, 0xA5AD, 0x5E73, 0xA5AE, 0x5E7C, 0xA5AF, 0x5F01,\n\t0xA5B0, 0x5F18, 0xA5B1, 0x5F17, 0xA5B2, 0x5FC5, 0xA5B3, 0x620A,\n\t0xA5B4, 0x6253, 0xA5B5, 0x6254, 0xA5B6, 0x6252, 0xA5B7, 0x6251,\n\t0xA5B8, 0x65A5, 0xA5B9, 0x65E6, 0xA5BA, 0x672E, 0xA5BB, 0x672C,\n\t0xA5BC, 0x672A, 0xA5BD, 0x672B, 0xA5BE, 0x672D, 0xA5BF, 0x6B63,\n\t0xA5C0, 0x6BCD, 0xA5C1, 0x6C11, 0xA5C2, 0x6C10, 0xA5C3, 0x6C38,\n\t0xA5C4, 0x6C41, 0xA5C5, 0x6C40, 0xA5C6, 0x6C3E, 0xA5C7, 0x72AF,\n\t0xA5C8, 0x7384, 0xA5C9, 0x7389, 0xA5CA, 0x74DC, 0xA5CB, 0x74E6,\n\t0xA5CC, 0x7518, 0xA5CD, 0x751F, 0xA5CE, 0x7528, 0xA5CF, 0x7529,\n\t0xA5D0, 0x7530, 0xA5D1, 0x7531, 0xA5D2, 0x7532, 0xA5D3, 0x7533,\n\t0xA5D4, 0x758B, 0xA5D5, 0x767D, 0xA5D6, 0x76AE, 0xA5D7, 0x76BF,\n\t0xA5D8, 0x76EE, 0xA5D9, 0x77DB, 0xA5DA, 0x77E2, 0xA5DB, 0x77F3,\n\t0xA5DC, 0x793A, 0xA5DD, 0x79BE, 0xA5DE, 0x7A74, 0xA5DF, 0x7ACB,\n\t0xA5E0, 0x4E1E, 0xA5E1, 0x4E1F, 0xA5E2, 0x4E52, 0xA5E3, 0x4E53,\n\t0xA5E4, 0x4E69, 0xA5E5, 0x4E99, 0xA5E6, 0x4EA4, 0xA5E7, 0x4EA6,\n\t0xA5E8, 0x4EA5, 0xA5E9, 0x4EFF, 0xA5EA, 0x4F09, 0xA5EB, 0x4F19,\n\t0xA5EC, 0x4F0A, 0xA5ED, 0x4F15, 0xA5EE, 0x4F0D, 0xA5EF, 0x4F10,\n\t0xA5F0, 0x4F11, 0xA5F1, 0x4F0F, 0xA5F2, 0x4EF2, 0xA5F3, 0x4EF6,\n\t0xA5F4, 0x4EFB, 0xA5F5, 0x4EF0, 0xA5F6, 0x4EF3, 0xA5F7, 0x4EFD,\n\t0xA5F8, 0x4F01, 0xA5F9, 0x4F0B, 0xA5FA, 0x5149, 0xA5FB, 0x5147,\n\t0xA5FC, 0x5146, 0xA5FD, 0x5148, 0xA5FE, 0x5168, 0xA640, 0x5171,\n\t0xA641, 0x518D, 0xA642, 0x51B0, 0xA643, 0x5217, 0xA644, 0x5211,\n\t0xA645, 0x5212, 0xA646, 0x520E, 0xA647, 0x5216, 0xA648, 0x52A3,\n\t0xA649, 0x5308, 0xA64A, 0x5321, 0xA64B, 0x5320, 0xA64C, 0x5370,\n\t0xA64D, 0x5371, 0xA64E, 0x5409, 0xA64F, 0x540F, 0xA650, 0x540C,\n\t0xA651, 0x540A, 0xA652, 0x5410, 0xA653, 0x5401, 0xA654, 0x540B,\n\t0xA655, 0x5404, 0xA656, 0x5411, 0xA657, 0x540D, 0xA658, 0x5408,\n\t0xA659, 0x5403, 0xA65A, 0x540E, 0xA65B, 0x5406, 0xA65C, 0x5412,\n\t0xA65D, 0x56E0, 0xA65E, 0x56DE, 0xA65F, 0x56DD, 0xA660, 0x5733,\n\t0xA661, 0x5730, 0xA662, 0x5728, 0xA663, 0x572D, 0xA664, 0x572C,\n\t0xA665, 0x572F, 0xA666, 0x5729, 0xA667, 0x5919, 0xA668, 0x591A,\n\t0xA669, 0x5937, 0xA66A, 0x5938, 0xA66B, 0x5984, 0xA66C, 0x5978,\n\t0xA66D, 0x5983, 0xA66E, 0x597D, 0xA66F, 0x5979, 0xA670, 0x5982,\n\t0xA671, 0x5981, 0xA672, 0x5B57, 0xA673, 0x5B58, 0xA674, 0x5B87,\n\t0xA675, 0x5B88, 0xA676, 0x5B85, 0xA677, 0x5B89, 0xA678, 0x5BFA,\n\t0xA679, 0x5C16, 0xA67A, 0x5C79, 0xA67B, 0x5DDE, 0xA67C, 0x5E06,\n\t0xA67D, 0x5E76, 0xA67E, 0x5E74, 0xA6A1, 0x5F0F, 0xA6A2, 0x5F1B,\n\t0xA6A3, 0x5FD9, 0xA6A4, 0x5FD6, 0xA6A5, 0x620E, 0xA6A6, 0x620C,\n\t0xA6A7, 0x620D, 0xA6A8, 0x6210, 0xA6A9, 0x6263, 0xA6AA, 0x625B,\n\t0xA6AB, 0x6258, 0xA6AC, 0x6536, 0xA6AD, 0x65E9, 0xA6AE, 0x65E8,\n\t0xA6AF, 0x65EC, 0xA6B0, 0x65ED, 0xA6B1, 0x66F2, 0xA6B2, 0x66F3,\n\t0xA6B3, 0x6709, 0xA6B4, 0x673D, 0xA6B5, 0x6734, 0xA6B6, 0x6731,\n\t0xA6B7, 0x6735, 0xA6B8, 0x6B21, 0xA6B9, 0x6B64, 0xA6BA, 0x6B7B,\n\t0xA6BB, 0x6C16, 0xA6BC, 0x6C5D, 0xA6BD, 0x6C57, 0xA6BE, 0x6C59,\n\t0xA6BF, 0x6C5F, 0xA6C0, 0x6C60, 0xA6C1, 0x6C50, 0xA6C2, 0x6C55,\n\t0xA6C3, 0x6C61, 0xA6C4, 0x6C5B, 0xA6C5, 0x6C4D, 0xA6C6, 0x6C4E,\n\t0xA6C7, 0x7070, 0xA6C8, 0x725F, 0xA6C9, 0x725D, 0xA6CA, 0x767E,\n\t0xA6CB, 0x7AF9, 0xA6CC, 0x7C73, 0xA6CD, 0x7CF8, 0xA6CE, 0x7F36,\n\t0xA6CF, 0x7F8A, 0xA6D0, 0x7FBD, 0xA6D1, 0x8001, 0xA6D2, 0x8003,\n\t0xA6D3, 0x800C, 0xA6D4, 0x8012, 0xA6D5, 0x8033, 0xA6D6, 0x807F,\n\t0xA6D7, 0x8089, 0xA6D8, 0x808B, 0xA6D9, 0x808C, 0xA6DA, 0x81E3,\n\t0xA6DB, 0x81EA, 0xA6DC, 0x81F3, 0xA6DD, 0x81FC, 0xA6DE, 0x820C,\n\t0xA6DF, 0x821B, 0xA6E0, 0x821F, 0xA6E1, 0x826E, 0xA6E2, 0x8272,\n\t0xA6E3, 0x827E, 0xA6E4, 0x866B, 0xA6E5, 0x8840, 0xA6E6, 0x884C,\n\t0xA6E7, 0x8863, 0xA6E8, 0x897F, 0xA6E9, 0x9621, 0xA6EA, 0x4E32,\n\t0xA6EB, 0x4EA8, 0xA6EC, 0x4F4D, 0xA6ED, 0x4F4F, 0xA6EE, 0x4F47,\n\t0xA6EF, 0x4F57, 0xA6F0, 0x4F5E, 0xA6F1, 0x4F34, 0xA6F2, 0x4F5B,\n\t0xA6F3, 0x4F55, 0xA6F4, 0x4F30, 0xA6F5, 0x4F50, 0xA6F6, 0x4F51,\n\t0xA6F7, 0x4F3D, 0xA6F8, 0x4F3A, 0xA6F9, 0x4F38, 0xA6FA, 0x4F43,\n\t0xA6FB, 0x4F54, 0xA6FC, 0x4F3C, 0xA6FD, 0x4F46, 0xA6FE, 0x4F63,\n\t0xA740, 0x4F5C, 0xA741, 0x4F60, 0xA742, 0x4F2F, 0xA743, 0x4F4E,\n\t0xA744, 0x4F36, 0xA745, 0x4F59, 0xA746, 0x4F5D, 0xA747, 0x4F48,\n\t0xA748, 0x4F5A, 0xA749, 0x514C, 0xA74A, 0x514B, 0xA74B, 0x514D,\n\t0xA74C, 0x5175, 0xA74D, 0x51B6, 0xA74E, 0x51B7, 0xA74F, 0x5225,\n\t0xA750, 0x5224, 0xA751, 0x5229, 0xA752, 0x522A, 0xA753, 0x5228,\n\t0xA754, 0x52AB, 0xA755, 0x52A9, 0xA756, 0x52AA, 0xA757, 0x52AC,\n\t0xA758, 0x5323, 0xA759, 0x5373, 0xA75A, 0x5375, 0xA75B, 0x541D,\n\t0xA75C, 0x542D, 0xA75D, 0x541E, 0xA75E, 0x543E, 0xA75F, 0x5426,\n\t0xA760, 0x544E, 0xA761, 0x5427, 0xA762, 0x5446, 0xA763, 0x5443,\n\t0xA764, 0x5433, 0xA765, 0x5448, 0xA766, 0x5442, 0xA767, 0x541B,\n\t0xA768, 0x5429, 0xA769, 0x544A, 0xA76A, 0x5439, 0xA76B, 0x543B,\n\t0xA76C, 0x5438, 0xA76D, 0x542E, 0xA76E, 0x5435, 0xA76F, 0x5436,\n\t0xA770, 0x5420, 0xA771, 0x543C, 0xA772, 0x5440, 0xA773, 0x5431,\n\t0xA774, 0x542B, 0xA775, 0x541F, 0xA776, 0x542C, 0xA777, 0x56EA,\n\t0xA778, 0x56F0, 0xA779, 0x56E4, 0xA77A, 0x56EB, 0xA77B, 0x574A,\n\t0xA77C, 0x5751, 0xA77D, 0x5740, 0xA77E, 0x574D, 0xA7A1, 0x5747,\n\t0xA7A2, 0x574E, 0xA7A3, 0x573E, 0xA7A4, 0x5750, 0xA7A5, 0x574F,\n\t0xA7A6, 0x573B, 0xA7A7, 0x58EF, 0xA7A8, 0x593E, 0xA7A9, 0x599D,\n\t0xA7AA, 0x5992, 0xA7AB, 0x59A8, 0xA7AC, 0x599E, 0xA7AD, 0x59A3,\n\t0xA7AE, 0x5999, 0xA7AF, 0x5996, 0xA7B0, 0x598D, 0xA7B1, 0x59A4,\n\t0xA7B2, 0x5993, 0xA7B3, 0x598A, 0xA7B4, 0x59A5, 0xA7B5, 0x5B5D,\n\t0xA7B6, 0x5B5C, 0xA7B7, 0x5B5A, 0xA7B8, 0x5B5B, 0xA7B9, 0x5B8C,\n\t0xA7BA, 0x5B8B, 0xA7BB, 0x5B8F, 0xA7BC, 0x5C2C, 0xA7BD, 0x5C40,\n\t0xA7BE, 0x5C41, 0xA7BF, 0x5C3F, 0xA7C0, 0x5C3E, 0xA7C1, 0x5C90,\n\t0xA7C2, 0x5C91, 0xA7C3, 0x5C94, 0xA7C4, 0x5C8C, 0xA7C5, 0x5DEB,\n\t0xA7C6, 0x5E0C, 0xA7C7, 0x5E8F, 0xA7C8, 0x5E87, 0xA7C9, 0x5E8A,\n\t0xA7CA, 0x5EF7, 0xA7CB, 0x5F04, 0xA7CC, 0x5F1F, 0xA7CD, 0x5F64,\n\t0xA7CE, 0x5F62, 0xA7CF, 0x5F77, 0xA7D0, 0x5F79, 0xA7D1, 0x5FD8,\n\t0xA7D2, 0x5FCC, 0xA7D3, 0x5FD7, 0xA7D4, 0x5FCD, 0xA7D5, 0x5FF1,\n\t0xA7D6, 0x5FEB, 0xA7D7, 0x5FF8, 0xA7D8, 0x5FEA, 0xA7D9, 0x6212,\n\t0xA7DA, 0x6211, 0xA7DB, 0x6284, 0xA7DC, 0x6297, 0xA7DD, 0x6296,\n\t0xA7DE, 0x6280, 0xA7DF, 0x6276, 0xA7E0, 0x6289, 0xA7E1, 0x626D,\n\t0xA7E2, 0x628A, 0xA7E3, 0x627C, 0xA7E4, 0x627E, 0xA7E5, 0x6279,\n\t0xA7E6, 0x6273, 0xA7E7, 0x6292, 0xA7E8, 0x626F, 0xA7E9, 0x6298,\n\t0xA7EA, 0x626E, 0xA7EB, 0x6295, 0xA7EC, 0x6293, 0xA7ED, 0x6291,\n\t0xA7EE, 0x6286, 0xA7EF, 0x6539, 0xA7F0, 0x653B, 0xA7F1, 0x6538,\n\t0xA7F2, 0x65F1, 0xA7F3, 0x66F4, 0xA7F4, 0x675F, 0xA7F5, 0x674E,\n\t0xA7F6, 0x674F, 0xA7F7, 0x6750, 0xA7F8, 0x6751, 0xA7F9, 0x675C,\n\t0xA7FA, 0x6756, 0xA7FB, 0x675E, 0xA7FC, 0x6749, 0xA7FD, 0x6746,\n\t0xA7FE, 0x6760, 0xA840, 0x6753, 0xA841, 0x6757, 0xA842, 0x6B65,\n\t0xA843, 0x6BCF, 0xA844, 0x6C42, 0xA845, 0x6C5E, 0xA846, 0x6C99,\n\t0xA847, 0x6C81, 0xA848, 0x6C88, 0xA849, 0x6C89, 0xA84A, 0x6C85,\n\t0xA84B, 0x6C9B, 0xA84C, 0x6C6A, 0xA84D, 0x6C7A, 0xA84E, 0x6C90,\n\t0xA84F, 0x6C70, 0xA850, 0x6C8C, 0xA851, 0x6C68, 0xA852, 0x6C96,\n\t0xA853, 0x6C92, 0xA854, 0x6C7D, 0xA855, 0x6C83, 0xA856, 0x6C72,\n\t0xA857, 0x6C7E, 0xA858, 0x6C74, 0xA859, 0x6C86, 0xA85A, 0x6C76,\n\t0xA85B, 0x6C8D, 0xA85C, 0x6C94, 0xA85D, 0x6C98, 0xA85E, 0x6C82,\n\t0xA85F, 0x7076, 0xA860, 0x707C, 0xA861, 0x707D, 0xA862, 0x7078,\n\t0xA863, 0x7262, 0xA864, 0x7261, 0xA865, 0x7260, 0xA866, 0x72C4,\n\t0xA867, 0x72C2, 0xA868, 0x7396, 0xA869, 0x752C, 0xA86A, 0x752B,\n\t0xA86B, 0x7537, 0xA86C, 0x7538, 0xA86D, 0x7682, 0xA86E, 0x76EF,\n\t0xA86F, 0x77E3, 0xA870, 0x79C1, 0xA871, 0x79C0, 0xA872, 0x79BF,\n\t0xA873, 0x7A76, 0xA874, 0x7CFB, 0xA875, 0x7F55, 0xA876, 0x8096,\n\t0xA877, 0x8093, 0xA878, 0x809D, 0xA879, 0x8098, 0xA87A, 0x809B,\n\t0xA87B, 0x809A, 0xA87C, 0x80B2, 0xA87D, 0x826F, 0xA87E, 0x8292,\n\t0xA8A1, 0x828B, 0xA8A2, 0x828D, 0xA8A3, 0x898B, 0xA8A4, 0x89D2,\n\t0xA8A5, 0x8A00, 0xA8A6, 0x8C37, 0xA8A7, 0x8C46, 0xA8A8, 0x8C55,\n\t0xA8A9, 0x8C9D, 0xA8AA, 0x8D64, 0xA8AB, 0x8D70, 0xA8AC, 0x8DB3,\n\t0xA8AD, 0x8EAB, 0xA8AE, 0x8ECA, 0xA8AF, 0x8F9B, 0xA8B0, 0x8FB0,\n\t0xA8B1, 0x8FC2, 0xA8B2, 0x8FC6, 0xA8B3, 0x8FC5, 0xA8B4, 0x8FC4,\n\t0xA8B5, 0x5DE1, 0xA8B6, 0x9091, 0xA8B7, 0x90A2, 0xA8B8, 0x90AA,\n\t0xA8B9, 0x90A6, 0xA8BA, 0x90A3, 0xA8BB, 0x9149, 0xA8BC, 0x91C6,\n\t0xA8BD, 0x91CC, 0xA8BE, 0x9632, 0xA8BF, 0x962E, 0xA8C0, 0x9631,\n\t0xA8C1, 0x962A, 0xA8C2, 0x962C, 0xA8C3, 0x4E26, 0xA8C4, 0x4E56,\n\t0xA8C5, 0x4E73, 0xA8C6, 0x4E8B, 0xA8C7, 0x4E9B, 0xA8C8, 0x4E9E,\n\t0xA8C9, 0x4EAB, 0xA8CA, 0x4EAC, 0xA8CB, 0x4F6F, 0xA8CC, 0x4F9D,\n\t0xA8CD, 0x4F8D, 0xA8CE, 0x4F73, 0xA8CF, 0x4F7F, 0xA8D0, 0x4F6C,\n\t0xA8D1, 0x4F9B, 0xA8D2, 0x4F8B, 0xA8D3, 0x4F86, 0xA8D4, 0x4F83,\n\t0xA8D5, 0x4F70, 0xA8D6, 0x4F75, 0xA8D7, 0x4F88, 0xA8D8, 0x4F69,\n\t0xA8D9, 0x4F7B, 0xA8DA, 0x4F96, 0xA8DB, 0x4F7E, 0xA8DC, 0x4F8F,\n\t0xA8DD, 0x4F91, 0xA8DE, 0x4F7A, 0xA8DF, 0x5154, 0xA8E0, 0x5152,\n\t0xA8E1, 0x5155, 0xA8E2, 0x5169, 0xA8E3, 0x5177, 0xA8E4, 0x5176,\n\t0xA8E5, 0x5178, 0xA8E6, 0x51BD, 0xA8E7, 0x51FD, 0xA8E8, 0x523B,\n\t0xA8E9, 0x5238, 0xA8EA, 0x5237, 0xA8EB, 0x523A, 0xA8EC, 0x5230,\n\t0xA8ED, 0x522E, 0xA8EE, 0x5236, 0xA8EF, 0x5241, 0xA8F0, 0x52BE,\n\t0xA8F1, 0x52BB, 0xA8F2, 0x5352, 0xA8F3, 0x5354, 0xA8F4, 0x5353,\n\t0xA8F5, 0x5351, 0xA8F6, 0x5366, 0xA8F7, 0x5377, 0xA8F8, 0x5378,\n\t0xA8F9, 0x5379, 0xA8FA, 0x53D6, 0xA8FB, 0x53D4, 0xA8FC, 0x53D7,\n\t0xA8FD, 0x5473, 0xA8FE, 0x5475, 0xA940, 0x5496, 0xA941, 0x5478,\n\t0xA942, 0x5495, 0xA943, 0x5480, 0xA944, 0x547B, 0xA945, 0x5477,\n\t0xA946, 0x5484, 0xA947, 0x5492, 0xA948, 0x5486, 0xA949, 0x547C,\n\t0xA94A, 0x5490, 0xA94B, 0x5471, 0xA94C, 0x5476, 0xA94D, 0x548C,\n\t0xA94E, 0x549A, 0xA94F, 0x5462, 0xA950, 0x5468, 0xA951, 0x548B,\n\t0xA952, 0x547D, 0xA953, 0x548E, 0xA954, 0x56FA, 0xA955, 0x5783,\n\t0xA956, 0x5777, 0xA957, 0x576A, 0xA958, 0x5769, 0xA959, 0x5761,\n\t0xA95A, 0x5766, 0xA95B, 0x5764, 0xA95C, 0x577C, 0xA95D, 0x591C,\n\t0xA95E, 0x5949, 0xA95F, 0x5947, 0xA960, 0x5948, 0xA961, 0x5944,\n\t0xA962, 0x5954, 0xA963, 0x59BE, 0xA964, 0x59BB, 0xA965, 0x59D4,\n\t0xA966, 0x59B9, 0xA967, 0x59AE, 0xA968, 0x59D1, 0xA969, 0x59C6,\n\t0xA96A, 0x59D0, 0xA96B, 0x59CD, 0xA96C, 0x59CB, 0xA96D, 0x59D3,\n\t0xA96E, 0x59CA, 0xA96F, 0x59AF, 0xA970, 0x59B3, 0xA971, 0x59D2,\n\t0xA972, 0x59C5, 0xA973, 0x5B5F, 0xA974, 0x5B64, 0xA975, 0x5B63,\n\t0xA976, 0x5B97, 0xA977, 0x5B9A, 0xA978, 0x5B98, 0xA979, 0x5B9C,\n\t0xA97A, 0x5B99, 0xA97B, 0x5B9B, 0xA97C, 0x5C1A, 0xA97D, 0x5C48,\n\t0xA97E, 0x5C45, 0xA9A1, 0x5C46, 0xA9A2, 0x5CB7, 0xA9A3, 0x5CA1,\n\t0xA9A4, 0x5CB8, 0xA9A5, 0x5CA9, 0xA9A6, 0x5CAB, 0xA9A7, 0x5CB1,\n\t0xA9A8, 0x5CB3, 0xA9A9, 0x5E18, 0xA9AA, 0x5E1A, 0xA9AB, 0x5E16,\n\t0xA9AC, 0x5E15, 0xA9AD, 0x5E1B, 0xA9AE, 0x5E11, 0xA9AF, 0x5E78,\n\t0xA9B0, 0x5E9A, 0xA9B1, 0x5E97, 0xA9B2, 0x5E9C, 0xA9B3, 0x5E95,\n\t0xA9B4, 0x5E96, 0xA9B5, 0x5EF6, 0xA9B6, 0x5F26, 0xA9B7, 0x5F27,\n\t0xA9B8, 0x5F29, 0xA9B9, 0x5F80, 0xA9BA, 0x5F81, 0xA9BB, 0x5F7F,\n\t0xA9BC, 0x5F7C, 0xA9BD, 0x5FDD, 0xA9BE, 0x5FE0, 0xA9BF, 0x5FFD,\n\t0xA9C0, 0x5FF5, 0xA9C1, 0x5FFF, 0xA9C2, 0x600F, 0xA9C3, 0x6014,\n\t0xA9C4, 0x602F, 0xA9C5, 0x6035, 0xA9C6, 0x6016, 0xA9C7, 0x602A,\n\t0xA9C8, 0x6015, 0xA9C9, 0x6021, 0xA9CA, 0x6027, 0xA9CB, 0x6029,\n\t0xA9CC, 0x602B, 0xA9CD, 0x601B, 0xA9CE, 0x6216, 0xA9CF, 0x6215,\n\t0xA9D0, 0x623F, 0xA9D1, 0x623E, 0xA9D2, 0x6240, 0xA9D3, 0x627F,\n\t0xA9D4, 0x62C9, 0xA9D5, 0x62CC, 0xA9D6, 0x62C4, 0xA9D7, 0x62BF,\n\t0xA9D8, 0x62C2, 0xA9D9, 0x62B9, 0xA9DA, 0x62D2, 0xA9DB, 0x62DB,\n\t0xA9DC, 0x62AB, 0xA9DD, 0x62D3, 0xA9DE, 0x62D4, 0xA9DF, 0x62CB,\n\t0xA9E0, 0x62C8, 0xA9E1, 0x62A8, 0xA9E2, 0x62BD, 0xA9E3, 0x62BC,\n\t0xA9E4, 0x62D0, 0xA9E5, 0x62D9, 0xA9E6, 0x62C7, 0xA9E7, 0x62CD,\n\t0xA9E8, 0x62B5, 0xA9E9, 0x62DA, 0xA9EA, 0x62B1, 0xA9EB, 0x62D8,\n\t0xA9EC, 0x62D6, 0xA9ED, 0x62D7, 0xA9EE, 0x62C6, 0xA9EF, 0x62AC,\n\t0xA9F0, 0x62CE, 0xA9F1, 0x653E, 0xA9F2, 0x65A7, 0xA9F3, 0x65BC,\n\t0xA9F4, 0x65FA, 0xA9F5, 0x6614, 0xA9F6, 0x6613, 0xA9F7, 0x660C,\n\t0xA9F8, 0x6606, 0xA9F9, 0x6602, 0xA9FA, 0x660E, 0xA9FB, 0x6600,\n\t0xA9FC, 0x660F, 0xA9FD, 0x6615, 0xA9FE, 0x660A, 0xAA40, 0x6607,\n\t0xAA41, 0x670D, 0xAA42, 0x670B, 0xAA43, 0x676D, 0xAA44, 0x678B,\n\t0xAA45, 0x6795, 0xAA46, 0x6771, 0xAA47, 0x679C, 0xAA48, 0x6773,\n\t0xAA49, 0x6777, 0xAA4A, 0x6787, 0xAA4B, 0x679D, 0xAA4C, 0x6797,\n\t0xAA4D, 0x676F, 0xAA4E, 0x6770, 0xAA4F, 0x677F, 0xAA50, 0x6789,\n\t0xAA51, 0x677E, 0xAA52, 0x6790, 0xAA53, 0x6775, 0xAA54, 0x679A,\n\t0xAA55, 0x6793, 0xAA56, 0x677C, 0xAA57, 0x676A, 0xAA58, 0x6772,\n\t0xAA59, 0x6B23, 0xAA5A, 0x6B66, 0xAA5B, 0x6B67, 0xAA5C, 0x6B7F,\n\t0xAA5D, 0x6C13, 0xAA5E, 0x6C1B, 0xAA5F, 0x6CE3, 0xAA60, 0x6CE8,\n\t0xAA61, 0x6CF3, 0xAA62, 0x6CB1, 0xAA63, 0x6CCC, 0xAA64, 0x6CE5,\n\t0xAA65, 0x6CB3, 0xAA66, 0x6CBD, 0xAA67, 0x6CBE, 0xAA68, 0x6CBC,\n\t0xAA69, 0x6CE2, 0xAA6A, 0x6CAB, 0xAA6B, 0x6CD5, 0xAA6C, 0x6CD3,\n\t0xAA6D, 0x6CB8, 0xAA6E, 0x6CC4, 0xAA6F, 0x6CB9, 0xAA70, 0x6CC1,\n\t0xAA71, 0x6CAE, 0xAA72, 0x6CD7, 0xAA73, 0x6CC5, 0xAA74, 0x6CF1,\n\t0xAA75, 0x6CBF, 0xAA76, 0x6CBB, 0xAA77, 0x6CE1, 0xAA78, 0x6CDB,\n\t0xAA79, 0x6CCA, 0xAA7A, 0x6CAC, 0xAA7B, 0x6CEF, 0xAA7C, 0x6CDC,\n\t0xAA7D, 0x6CD6, 0xAA7E, 0x6CE0, 0xAAA1, 0x7095, 0xAAA2, 0x708E,\n\t0xAAA3, 0x7092, 0xAAA4, 0x708A, 0xAAA5, 0x7099, 0xAAA6, 0x722C,\n\t0xAAA7, 0x722D, 0xAAA8, 0x7238, 0xAAA9, 0x7248, 0xAAAA, 0x7267,\n\t0xAAAB, 0x7269, 0xAAAC, 0x72C0, 0xAAAD, 0x72CE, 0xAAAE, 0x72D9,\n\t0xAAAF, 0x72D7, 0xAAB0, 0x72D0, 0xAAB1, 0x73A9, 0xAAB2, 0x73A8,\n\t0xAAB3, 0x739F, 0xAAB4, 0x73AB, 0xAAB5, 0x73A5, 0xAAB6, 0x753D,\n\t0xAAB7, 0x759D, 0xAAB8, 0x7599, 0xAAB9, 0x759A, 0xAABA, 0x7684,\n\t0xAABB, 0x76C2, 0xAABC, 0x76F2, 0xAABD, 0x76F4, 0xAABE, 0x77E5,\n\t0xAABF, 0x77FD, 0xAAC0, 0x793E, 0xAAC1, 0x7940, 0xAAC2, 0x7941,\n\t0xAAC3, 0x79C9, 0xAAC4, 0x79C8, 0xAAC5, 0x7A7A, 0xAAC6, 0x7A79,\n\t0xAAC7, 0x7AFA, 0xAAC8, 0x7CFE, 0xAAC9, 0x7F54, 0xAACA, 0x7F8C,\n\t0xAACB, 0x7F8B, 0xAACC, 0x8005, 0xAACD, 0x80BA, 0xAACE, 0x80A5,\n\t0xAACF, 0x80A2, 0xAAD0, 0x80B1, 0xAAD1, 0x80A1, 0xAAD2, 0x80AB,\n\t0xAAD3, 0x80A9, 0xAAD4, 0x80B4, 0xAAD5, 0x80AA, 0xAAD6, 0x80AF,\n\t0xAAD7, 0x81E5, 0xAAD8, 0x81FE, 0xAAD9, 0x820D, 0xAADA, 0x82B3,\n\t0xAADB, 0x829D, 0xAADC, 0x8299, 0xAADD, 0x82AD, 0xAADE, 0x82BD,\n\t0xAADF, 0x829F, 0xAAE0, 0x82B9, 0xAAE1, 0x82B1, 0xAAE2, 0x82AC,\n\t0xAAE3, 0x82A5, 0xAAE4, 0x82AF, 0xAAE5, 0x82B8, 0xAAE6, 0x82A3,\n\t0xAAE7, 0x82B0, 0xAAE8, 0x82BE, 0xAAE9, 0x82B7, 0xAAEA, 0x864E,\n\t0xAAEB, 0x8671, 0xAAEC, 0x521D, 0xAAED, 0x8868, 0xAAEE, 0x8ECB,\n\t0xAAEF, 0x8FCE, 0xAAF0, 0x8FD4, 0xAAF1, 0x8FD1, 0xAAF2, 0x90B5,\n\t0xAAF3, 0x90B8, 0xAAF4, 0x90B1, 0xAAF5, 0x90B6, 0xAAF6, 0x91C7,\n\t0xAAF7, 0x91D1, 0xAAF8, 0x9577, 0xAAF9, 0x9580, 0xAAFA, 0x961C,\n\t0xAAFB, 0x9640, 0xAAFC, 0x963F, 0xAAFD, 0x963B, 0xAAFE, 0x9644,\n\t0xAB40, 0x9642, 0xAB41, 0x96B9, 0xAB42, 0x96E8, 0xAB43, 0x9752,\n\t0xAB44, 0x975E, 0xAB45, 0x4E9F, 0xAB46, 0x4EAD, 0xAB47, 0x4EAE,\n\t0xAB48, 0x4FE1, 0xAB49, 0x4FB5, 0xAB4A, 0x4FAF, 0xAB4B, 0x4FBF,\n\t0xAB4C, 0x4FE0, 0xAB4D, 0x4FD1, 0xAB4E, 0x4FCF, 0xAB4F, 0x4FDD,\n\t0xAB50, 0x4FC3, 0xAB51, 0x4FB6, 0xAB52, 0x4FD8, 0xAB53, 0x4FDF,\n\t0xAB54, 0x4FCA, 0xAB55, 0x4FD7, 0xAB56, 0x4FAE, 0xAB57, 0x4FD0,\n\t0xAB58, 0x4FC4, 0xAB59, 0x4FC2, 0xAB5A, 0x4FDA, 0xAB5B, 0x4FCE,\n\t0xAB5C, 0x4FDE, 0xAB5D, 0x4FB7, 0xAB5E, 0x5157, 0xAB5F, 0x5192,\n\t0xAB60, 0x5191, 0xAB61, 0x51A0, 0xAB62, 0x524E, 0xAB63, 0x5243,\n\t0xAB64, 0x524A, 0xAB65, 0x524D, 0xAB66, 0x524C, 0xAB67, 0x524B,\n\t0xAB68, 0x5247, 0xAB69, 0x52C7, 0xAB6A, 0x52C9, 0xAB6B, 0x52C3,\n\t0xAB6C, 0x52C1, 0xAB6D, 0x530D, 0xAB6E, 0x5357, 0xAB6F, 0x537B,\n\t0xAB70, 0x539A, 0xAB71, 0x53DB, 0xAB72, 0x54AC, 0xAB73, 0x54C0,\n\t0xAB74, 0x54A8, 0xAB75, 0x54CE, 0xAB76, 0x54C9, 0xAB77, 0x54B8,\n\t0xAB78, 0x54A6, 0xAB79, 0x54B3, 0xAB7A, 0x54C7, 0xAB7B, 0x54C2,\n\t0xAB7C, 0x54BD, 0xAB7D, 0x54AA, 0xAB7E, 0x54C1, 0xABA1, 0x54C4,\n\t0xABA2, 0x54C8, 0xABA3, 0x54AF, 0xABA4, 0x54AB, 0xABA5, 0x54B1,\n\t0xABA6, 0x54BB, 0xABA7, 0x54A9, 0xABA8, 0x54A7, 0xABA9, 0x54BF,\n\t0xABAA, 0x56FF, 0xABAB, 0x5782, 0xABAC, 0x578B, 0xABAD, 0x57A0,\n\t0xABAE, 0x57A3, 0xABAF, 0x57A2, 0xABB0, 0x57CE, 0xABB1, 0x57AE,\n\t0xABB2, 0x5793, 0xABB3, 0x5955, 0xABB4, 0x5951, 0xABB5, 0x594F,\n\t0xABB6, 0x594E, 0xABB7, 0x5950, 0xABB8, 0x59DC, 0xABB9, 0x59D8,\n\t0xABBA, 0x59FF, 0xABBB, 0x59E3, 0xABBC, 0x59E8, 0xABBD, 0x5A03,\n\t0xABBE, 0x59E5, 0xABBF, 0x59EA, 0xABC0, 0x59DA, 0xABC1, 0x59E6,\n\t0xABC2, 0x5A01, 0xABC3, 0x59FB, 0xABC4, 0x5B69, 0xABC5, 0x5BA3,\n\t0xABC6, 0x5BA6, 0xABC7, 0x5BA4, 0xABC8, 0x5BA2, 0xABC9, 0x5BA5,\n\t0xABCA, 0x5C01, 0xABCB, 0x5C4E, 0xABCC, 0x5C4F, 0xABCD, 0x5C4D,\n\t0xABCE, 0x5C4B, 0xABCF, 0x5CD9, 0xABD0, 0x5CD2, 0xABD1, 0x5DF7,\n\t0xABD2, 0x5E1D, 0xABD3, 0x5E25, 0xABD4, 0x5E1F, 0xABD5, 0x5E7D,\n\t0xABD6, 0x5EA0, 0xABD7, 0x5EA6, 0xABD8, 0x5EFA, 0xABD9, 0x5F08,\n\t0xABDA, 0x5F2D, 0xABDB, 0x5F65, 0xABDC, 0x5F88, 0xABDD, 0x5F85,\n\t0xABDE, 0x5F8A, 0xABDF, 0x5F8B, 0xABE0, 0x5F87, 0xABE1, 0x5F8C,\n\t0xABE2, 0x5F89, 0xABE3, 0x6012, 0xABE4, 0x601D, 0xABE5, 0x6020,\n\t0xABE6, 0x6025, 0xABE7, 0x600E, 0xABE8, 0x6028, 0xABE9, 0x604D,\n\t0xABEA, 0x6070, 0xABEB, 0x6068, 0xABEC, 0x6062, 0xABED, 0x6046,\n\t0xABEE, 0x6043, 0xABEF, 0x606C, 0xABF0, 0x606B, 0xABF1, 0x606A,\n\t0xABF2, 0x6064, 0xABF3, 0x6241, 0xABF4, 0x62DC, 0xABF5, 0x6316,\n\t0xABF6, 0x6309, 0xABF7, 0x62FC, 0xABF8, 0x62ED, 0xABF9, 0x6301,\n\t0xABFA, 0x62EE, 0xABFB, 0x62FD, 0xABFC, 0x6307, 0xABFD, 0x62F1,\n\t0xABFE, 0x62F7, 0xAC40, 0x62EF, 0xAC41, 0x62EC, 0xAC42, 0x62FE,\n\t0xAC43, 0x62F4, 0xAC44, 0x6311, 0xAC45, 0x6302, 0xAC46, 0x653F,\n\t0xAC47, 0x6545, 0xAC48, 0x65AB, 0xAC49, 0x65BD, 0xAC4A, 0x65E2,\n\t0xAC4B, 0x6625, 0xAC4C, 0x662D, 0xAC4D, 0x6620, 0xAC4E, 0x6627,\n\t0xAC4F, 0x662F, 0xAC50, 0x661F, 0xAC51, 0x6628, 0xAC52, 0x6631,\n\t0xAC53, 0x6624, 0xAC54, 0x66F7, 0xAC55, 0x67FF, 0xAC56, 0x67D3,\n\t0xAC57, 0x67F1, 0xAC58, 0x67D4, 0xAC59, 0x67D0, 0xAC5A, 0x67EC,\n\t0xAC5B, 0x67B6, 0xAC5C, 0x67AF, 0xAC5D, 0x67F5, 0xAC5E, 0x67E9,\n\t0xAC5F, 0x67EF, 0xAC60, 0x67C4, 0xAC61, 0x67D1, 0xAC62, 0x67B4,\n\t0xAC63, 0x67DA, 0xAC64, 0x67E5, 0xAC65, 0x67B8, 0xAC66, 0x67CF,\n\t0xAC67, 0x67DE, 0xAC68, 0x67F3, 0xAC69, 0x67B0, 0xAC6A, 0x67D9,\n\t0xAC6B, 0x67E2, 0xAC6C, 0x67DD, 0xAC6D, 0x67D2, 0xAC6E, 0x6B6A,\n\t0xAC6F, 0x6B83, 0xAC70, 0x6B86, 0xAC71, 0x6BB5, 0xAC72, 0x6BD2,\n\t0xAC73, 0x6BD7, 0xAC74, 0x6C1F, 0xAC75, 0x6CC9, 0xAC76, 0x6D0B,\n\t0xAC77, 0x6D32, 0xAC78, 0x6D2A, 0xAC79, 0x6D41, 0xAC7A, 0x6D25,\n\t0xAC7B, 0x6D0C, 0xAC7C, 0x6D31, 0xAC7D, 0x6D1E, 0xAC7E, 0x6D17,\n\t0xACA1, 0x6D3B, 0xACA2, 0x6D3D, 0xACA3, 0x6D3E, 0xACA4, 0x6D36,\n\t0xACA5, 0x6D1B, 0xACA6, 0x6CF5, 0xACA7, 0x6D39, 0xACA8, 0x6D27,\n\t0xACA9, 0x6D38, 0xACAA, 0x6D29, 0xACAB, 0x6D2E, 0xACAC, 0x6D35,\n\t0xACAD, 0x6D0E, 0xACAE, 0x6D2B, 0xACAF, 0x70AB, 0xACB0, 0x70BA,\n\t0xACB1, 0x70B3, 0xACB2, 0x70AC, 0xACB3, 0x70AF, 0xACB4, 0x70AD,\n\t0xACB5, 0x70B8, 0xACB6, 0x70AE, 0xACB7, 0x70A4, 0xACB8, 0x7230,\n\t0xACB9, 0x7272, 0xACBA, 0x726F, 0xACBB, 0x7274, 0xACBC, 0x72E9,\n\t0xACBD, 0x72E0, 0xACBE, 0x72E1, 0xACBF, 0x73B7, 0xACC0, 0x73CA,\n\t0xACC1, 0x73BB, 0xACC2, 0x73B2, 0xACC3, 0x73CD, 0xACC4, 0x73C0,\n\t0xACC5, 0x73B3, 0xACC6, 0x751A, 0xACC7, 0x752D, 0xACC8, 0x754F,\n\t0xACC9, 0x754C, 0xACCA, 0x754E, 0xACCB, 0x754B, 0xACCC, 0x75AB,\n\t0xACCD, 0x75A4, 0xACCE, 0x75A5, 0xACCF, 0x75A2, 0xACD0, 0x75A3,\n\t0xACD1, 0x7678, 0xACD2, 0x7686, 0xACD3, 0x7687, 0xACD4, 0x7688,\n\t0xACD5, 0x76C8, 0xACD6, 0x76C6, 0xACD7, 0x76C3, 0xACD8, 0x76C5,\n\t0xACD9, 0x7701, 0xACDA, 0x76F9, 0xACDB, 0x76F8, 0xACDC, 0x7709,\n\t0xACDD, 0x770B, 0xACDE, 0x76FE, 0xACDF, 0x76FC, 0xACE0, 0x7707,\n\t0xACE1, 0x77DC, 0xACE2, 0x7802, 0xACE3, 0x7814, 0xACE4, 0x780C,\n\t0xACE5, 0x780D, 0xACE6, 0x7946, 0xACE7, 0x7949, 0xACE8, 0x7948,\n\t0xACE9, 0x7947, 0xACEA, 0x79B9, 0xACEB, 0x79BA, 0xACEC, 0x79D1,\n\t0xACED, 0x79D2, 0xACEE, 0x79CB, 0xACEF, 0x7A7F, 0xACF0, 0x7A81,\n\t0xACF1, 0x7AFF, 0xACF2, 0x7AFD, 0xACF3, 0x7C7D, 0xACF4, 0x7D02,\n\t0xACF5, 0x7D05, 0xACF6, 0x7D00, 0xACF7, 0x7D09, 0xACF8, 0x7D07,\n\t0xACF9, 0x7D04, 0xACFA, 0x7D06, 0xACFB, 0x7F38, 0xACFC, 0x7F8E,\n\t0xACFD, 0x7FBF, 0xACFE, 0x8004, 0xAD40, 0x8010, 0xAD41, 0x800D,\n\t0xAD42, 0x8011, 0xAD43, 0x8036, 0xAD44, 0x80D6, 0xAD45, 0x80E5,\n\t0xAD46, 0x80DA, 0xAD47, 0x80C3, 0xAD48, 0x80C4, 0xAD49, 0x80CC,\n\t0xAD4A, 0x80E1, 0xAD4B, 0x80DB, 0xAD4C, 0x80CE, 0xAD4D, 0x80DE,\n\t0xAD4E, 0x80E4, 0xAD4F, 0x80DD, 0xAD50, 0x81F4, 0xAD51, 0x8222,\n\t0xAD52, 0x82E7, 0xAD53, 0x8303, 0xAD54, 0x8305, 0xAD55, 0x82E3,\n\t0xAD56, 0x82DB, 0xAD57, 0x82E6, 0xAD58, 0x8304, 0xAD59, 0x82E5,\n\t0xAD5A, 0x8302, 0xAD5B, 0x8309, 0xAD5C, 0x82D2, 0xAD5D, 0x82D7,\n\t0xAD5E, 0x82F1, 0xAD5F, 0x8301, 0xAD60, 0x82DC, 0xAD61, 0x82D4,\n\t0xAD62, 0x82D1, 0xAD63, 0x82DE, 0xAD64, 0x82D3, 0xAD65, 0x82DF,\n\t0xAD66, 0x82EF, 0xAD67, 0x8306, 0xAD68, 0x8650, 0xAD69, 0x8679,\n\t0xAD6A, 0x867B, 0xAD6B, 0x867A, 0xAD6C, 0x884D, 0xAD6D, 0x886B,\n\t0xAD6E, 0x8981, 0xAD6F, 0x89D4, 0xAD70, 0x8A08, 0xAD71, 0x8A02,\n\t0xAD72, 0x8A03, 0xAD73, 0x8C9E, 0xAD74, 0x8CA0, 0xAD75, 0x8D74,\n\t0xAD76, 0x8D73, 0xAD77, 0x8DB4, 0xAD78, 0x8ECD, 0xAD79, 0x8ECC,\n\t0xAD7A, 0x8FF0, 0xAD7B, 0x8FE6, 0xAD7C, 0x8FE2, 0xAD7D, 0x8FEA,\n\t0xAD7E, 0x8FE5, 0xADA1, 0x8FED, 0xADA2, 0x8FEB, 0xADA3, 0x8FE4,\n\t0xADA4, 0x8FE8, 0xADA5, 0x90CA, 0xADA6, 0x90CE, 0xADA7, 0x90C1,\n\t0xADA8, 0x90C3, 0xADA9, 0x914B, 0xADAA, 0x914A, 0xADAB, 0x91CD,\n\t0xADAC, 0x9582, 0xADAD, 0x9650, 0xADAE, 0x964B, 0xADAF, 0x964C,\n\t0xADB0, 0x964D, 0xADB1, 0x9762, 0xADB2, 0x9769, 0xADB3, 0x97CB,\n\t0xADB4, 0x97ED, 0xADB5, 0x97F3, 0xADB6, 0x9801, 0xADB7, 0x98A8,\n\t0xADB8, 0x98DB, 0xADB9, 0x98DF, 0xADBA, 0x9996, 0xADBB, 0x9999,\n\t0xADBC, 0x4E58, 0xADBD, 0x4EB3, 0xADBE, 0x500C, 0xADBF, 0x500D,\n\t0xADC0, 0x5023, 0xADC1, 0x4FEF, 0xADC2, 0x5026, 0xADC3, 0x5025,\n\t0xADC4, 0x4FF8, 0xADC5, 0x5029, 0xADC6, 0x5016, 0xADC7, 0x5006,\n\t0xADC8, 0x503C, 0xADC9, 0x501F, 0xADCA, 0x501A, 0xADCB, 0x5012,\n\t0xADCC, 0x5011, 0xADCD, 0x4FFA, 0xADCE, 0x5000, 0xADCF, 0x5014,\n\t0xADD0, 0x5028, 0xADD1, 0x4FF1, 0xADD2, 0x5021, 0xADD3, 0x500B,\n\t0xADD4, 0x5019, 0xADD5, 0x5018, 0xADD6, 0x4FF3, 0xADD7, 0x4FEE,\n\t0xADD8, 0x502D, 0xADD9, 0x502A, 0xADDA, 0x4FFE, 0xADDB, 0x502B,\n\t0xADDC, 0x5009, 0xADDD, 0x517C, 0xADDE, 0x51A4, 0xADDF, 0x51A5,\n\t0xADE0, 0x51A2, 0xADE1, 0x51CD, 0xADE2, 0x51CC, 0xADE3, 0x51C6,\n\t0xADE4, 0x51CB, 0xADE5, 0x5256, 0xADE6, 0x525C, 0xADE7, 0x5254,\n\t0xADE8, 0x525B, 0xADE9, 0x525D, 0xADEA, 0x532A, 0xADEB, 0x537F,\n\t0xADEC, 0x539F, 0xADED, 0x539D, 0xADEE, 0x53DF, 0xADEF, 0x54E8,\n\t0xADF0, 0x5510, 0xADF1, 0x5501, 0xADF2, 0x5537, 0xADF3, 0x54FC,\n\t0xADF4, 0x54E5, 0xADF5, 0x54F2, 0xADF6, 0x5506, 0xADF7, 0x54FA,\n\t0xADF8, 0x5514, 0xADF9, 0x54E9, 0xADFA, 0x54ED, 0xADFB, 0x54E1,\n\t0xADFC, 0x5509, 0xADFD, 0x54EE, 0xADFE, 0x54EA, 0xAE40, 0x54E6,\n\t0xAE41, 0x5527, 0xAE42, 0x5507, 0xAE43, 0x54FD, 0xAE44, 0x550F,\n\t0xAE45, 0x5703, 0xAE46, 0x5704, 0xAE47, 0x57C2, 0xAE48, 0x57D4,\n\t0xAE49, 0x57CB, 0xAE4A, 0x57C3, 0xAE4B, 0x5809, 0xAE4C, 0x590F,\n\t0xAE4D, 0x5957, 0xAE4E, 0x5958, 0xAE4F, 0x595A, 0xAE50, 0x5A11,\n\t0xAE51, 0x5A18, 0xAE52, 0x5A1C, 0xAE53, 0x5A1F, 0xAE54, 0x5A1B,\n\t0xAE55, 0x5A13, 0xAE56, 0x59EC, 0xAE57, 0x5A20, 0xAE58, 0x5A23,\n\t0xAE59, 0x5A29, 0xAE5A, 0x5A25, 0xAE5B, 0x5A0C, 0xAE5C, 0x5A09,\n\t0xAE5D, 0x5B6B, 0xAE5E, 0x5C58, 0xAE5F, 0x5BB0, 0xAE60, 0x5BB3,\n\t0xAE61, 0x5BB6, 0xAE62, 0x5BB4, 0xAE63, 0x5BAE, 0xAE64, 0x5BB5,\n\t0xAE65, 0x5BB9, 0xAE66, 0x5BB8, 0xAE67, 0x5C04, 0xAE68, 0x5C51,\n\t0xAE69, 0x5C55, 0xAE6A, 0x5C50, 0xAE6B, 0x5CED, 0xAE6C, 0x5CFD,\n\t0xAE6D, 0x5CFB, 0xAE6E, 0x5CEA, 0xAE6F, 0x5CE8, 0xAE70, 0x5CF0,\n\t0xAE71, 0x5CF6, 0xAE72, 0x5D01, 0xAE73, 0x5CF4, 0xAE74, 0x5DEE,\n\t0xAE75, 0x5E2D, 0xAE76, 0x5E2B, 0xAE77, 0x5EAB, 0xAE78, 0x5EAD,\n\t0xAE79, 0x5EA7, 0xAE7A, 0x5F31, 0xAE7B, 0x5F92, 0xAE7C, 0x5F91,\n\t0xAE7D, 0x5F90, 0xAE7E, 0x6059, 0xAEA1, 0x6063, 0xAEA2, 0x6065,\n\t0xAEA3, 0x6050, 0xAEA4, 0x6055, 0xAEA5, 0x606D, 0xAEA6, 0x6069,\n\t0xAEA7, 0x606F, 0xAEA8, 0x6084, 0xAEA9, 0x609F, 0xAEAA, 0x609A,\n\t0xAEAB, 0x608D, 0xAEAC, 0x6094, 0xAEAD, 0x608C, 0xAEAE, 0x6085,\n\t0xAEAF, 0x6096, 0xAEB0, 0x6247, 0xAEB1, 0x62F3, 0xAEB2, 0x6308,\n\t0xAEB3, 0x62FF, 0xAEB4, 0x634E, 0xAEB5, 0x633E, 0xAEB6, 0x632F,\n\t0xAEB7, 0x6355, 0xAEB8, 0x6342, 0xAEB9, 0x6346, 0xAEBA, 0x634F,\n\t0xAEBB, 0x6349, 0xAEBC, 0x633A, 0xAEBD, 0x6350, 0xAEBE, 0x633D,\n\t0xAEBF, 0x632A, 0xAEC0, 0x632B, 0xAEC1, 0x6328, 0xAEC2, 0x634D,\n\t0xAEC3, 0x634C, 0xAEC4, 0x6548, 0xAEC5, 0x6549, 0xAEC6, 0x6599,\n\t0xAEC7, 0x65C1, 0xAEC8, 0x65C5, 0xAEC9, 0x6642, 0xAECA, 0x6649,\n\t0xAECB, 0x664F, 0xAECC, 0x6643, 0xAECD, 0x6652, 0xAECE, 0x664C,\n\t0xAECF, 0x6645, 0xAED0, 0x6641, 0xAED1, 0x66F8, 0xAED2, 0x6714,\n\t0xAED3, 0x6715, 0xAED4, 0x6717, 0xAED5, 0x6821, 0xAED6, 0x6838,\n\t0xAED7, 0x6848, 0xAED8, 0x6846, 0xAED9, 0x6853, 0xAEDA, 0x6839,\n\t0xAEDB, 0x6842, 0xAEDC, 0x6854, 0xAEDD, 0x6829, 0xAEDE, 0x68B3,\n\t0xAEDF, 0x6817, 0xAEE0, 0x684C, 0xAEE1, 0x6851, 0xAEE2, 0x683D,\n\t0xAEE3, 0x67F4, 0xAEE4, 0x6850, 0xAEE5, 0x6840, 0xAEE6, 0x683C,\n\t0xAEE7, 0x6843, 0xAEE8, 0x682A, 0xAEE9, 0x6845, 0xAEEA, 0x6813,\n\t0xAEEB, 0x6818, 0xAEEC, 0x6841, 0xAEED, 0x6B8A, 0xAEEE, 0x6B89,\n\t0xAEEF, 0x6BB7, 0xAEF0, 0x6C23, 0xAEF1, 0x6C27, 0xAEF2, 0x6C28,\n\t0xAEF3, 0x6C26, 0xAEF4, 0x6C24, 0xAEF5, 0x6CF0, 0xAEF6, 0x6D6A,\n\t0xAEF7, 0x6D95, 0xAEF8, 0x6D88, 0xAEF9, 0x6D87, 0xAEFA, 0x6D66,\n\t0xAEFB, 0x6D78, 0xAEFC, 0x6D77, 0xAEFD, 0x6D59, 0xAEFE, 0x6D93,\n\t0xAF40, 0x6D6C, 0xAF41, 0x6D89, 0xAF42, 0x6D6E, 0xAF43, 0x6D5A,\n\t0xAF44, 0x6D74, 0xAF45, 0x6D69, 0xAF46, 0x6D8C, 0xAF47, 0x6D8A,\n\t0xAF48, 0x6D79, 0xAF49, 0x6D85, 0xAF4A, 0x6D65, 0xAF4B, 0x6D94,\n\t0xAF4C, 0x70CA, 0xAF4D, 0x70D8, 0xAF4E, 0x70E4, 0xAF4F, 0x70D9,\n\t0xAF50, 0x70C8, 0xAF51, 0x70CF, 0xAF52, 0x7239, 0xAF53, 0x7279,\n\t0xAF54, 0x72FC, 0xAF55, 0x72F9, 0xAF56, 0x72FD, 0xAF57, 0x72F8,\n\t0xAF58, 0x72F7, 0xAF59, 0x7386, 0xAF5A, 0x73ED, 0xAF5B, 0x7409,\n\t0xAF5C, 0x73EE, 0xAF5D, 0x73E0, 0xAF5E, 0x73EA, 0xAF5F, 0x73DE,\n\t0xAF60, 0x7554, 0xAF61, 0x755D, 0xAF62, 0x755C, 0xAF63, 0x755A,\n\t0xAF64, 0x7559, 0xAF65, 0x75BE, 0xAF66, 0x75C5, 0xAF67, 0x75C7,\n\t0xAF68, 0x75B2, 0xAF69, 0x75B3, 0xAF6A, 0x75BD, 0xAF6B, 0x75BC,\n\t0xAF6C, 0x75B9, 0xAF6D, 0x75C2, 0xAF6E, 0x75B8, 0xAF6F, 0x768B,\n\t0xAF70, 0x76B0, 0xAF71, 0x76CA, 0xAF72, 0x76CD, 0xAF73, 0x76CE,\n\t0xAF74, 0x7729, 0xAF75, 0x771F, 0xAF76, 0x7720, 0xAF77, 0x7728,\n\t0xAF78, 0x77E9, 0xAF79, 0x7830, 0xAF7A, 0x7827, 0xAF7B, 0x7838,\n\t0xAF7C, 0x781D, 0xAF7D, 0x7834, 0xAF7E, 0x7837, 0xAFA1, 0x7825,\n\t0xAFA2, 0x782D, 0xAFA3, 0x7820, 0xAFA4, 0x781F, 0xAFA5, 0x7832,\n\t0xAFA6, 0x7955, 0xAFA7, 0x7950, 0xAFA8, 0x7960, 0xAFA9, 0x795F,\n\t0xAFAA, 0x7956, 0xAFAB, 0x795E, 0xAFAC, 0x795D, 0xAFAD, 0x7957,\n\t0xAFAE, 0x795A, 0xAFAF, 0x79E4, 0xAFB0, 0x79E3, 0xAFB1, 0x79E7,\n\t0xAFB2, 0x79DF, 0xAFB3, 0x79E6, 0xAFB4, 0x79E9, 0xAFB5, 0x79D8,\n\t0xAFB6, 0x7A84, 0xAFB7, 0x7A88, 0xAFB8, 0x7AD9, 0xAFB9, 0x7B06,\n\t0xAFBA, 0x7B11, 0xAFBB, 0x7C89, 0xAFBC, 0x7D21, 0xAFBD, 0x7D17,\n\t0xAFBE, 0x7D0B, 0xAFBF, 0x7D0A, 0xAFC0, 0x7D20, 0xAFC1, 0x7D22,\n\t0xAFC2, 0x7D14, 0xAFC3, 0x7D10, 0xAFC4, 0x7D15, 0xAFC5, 0x7D1A,\n\t0xAFC6, 0x7D1C, 0xAFC7, 0x7D0D, 0xAFC8, 0x7D19, 0xAFC9, 0x7D1B,\n\t0xAFCA, 0x7F3A, 0xAFCB, 0x7F5F, 0xAFCC, 0x7F94, 0xAFCD, 0x7FC5,\n\t0xAFCE, 0x7FC1, 0xAFCF, 0x8006, 0xAFD0, 0x8018, 0xAFD1, 0x8015,\n\t0xAFD2, 0x8019, 0xAFD3, 0x8017, 0xAFD4, 0x803D, 0xAFD5, 0x803F,\n\t0xAFD6, 0x80F1, 0xAFD7, 0x8102, 0xAFD8, 0x80F0, 0xAFD9, 0x8105,\n\t0xAFDA, 0x80ED, 0xAFDB, 0x80F4, 0xAFDC, 0x8106, 0xAFDD, 0x80F8,\n\t0xAFDE, 0x80F3, 0xAFDF, 0x8108, 0xAFE0, 0x80FD, 0xAFE1, 0x810A,\n\t0xAFE2, 0x80FC, 0xAFE3, 0x80EF, 0xAFE4, 0x81ED, 0xAFE5, 0x81EC,\n\t0xAFE6, 0x8200, 0xAFE7, 0x8210, 0xAFE8, 0x822A, 0xAFE9, 0x822B,\n\t0xAFEA, 0x8228, 0xAFEB, 0x822C, 0xAFEC, 0x82BB, 0xAFED, 0x832B,\n\t0xAFEE, 0x8352, 0xAFEF, 0x8354, 0xAFF0, 0x834A, 0xAFF1, 0x8338,\n\t0xAFF2, 0x8350, 0xAFF3, 0x8349, 0xAFF4, 0x8335, 0xAFF5, 0x8334,\n\t0xAFF6, 0x834F, 0xAFF7, 0x8332, 0xAFF8, 0x8339, 0xAFF9, 0x8336,\n\t0xAFFA, 0x8317, 0xAFFB, 0x8340, 0xAFFC, 0x8331, 0xAFFD, 0x8328,\n\t0xAFFE, 0x8343, 0xB040, 0x8654, 0xB041, 0x868A, 0xB042, 0x86AA,\n\t0xB043, 0x8693, 0xB044, 0x86A4, 0xB045, 0x86A9, 0xB046, 0x868C,\n\t0xB047, 0x86A3, 0xB048, 0x869C, 0xB049, 0x8870, 0xB04A, 0x8877,\n\t0xB04B, 0x8881, 0xB04C, 0x8882, 0xB04D, 0x887D, 0xB04E, 0x8879,\n\t0xB04F, 0x8A18, 0xB050, 0x8A10, 0xB051, 0x8A0E, 0xB052, 0x8A0C,\n\t0xB053, 0x8A15, 0xB054, 0x8A0A, 0xB055, 0x8A17, 0xB056, 0x8A13,\n\t0xB057, 0x8A16, 0xB058, 0x8A0F, 0xB059, 0x8A11, 0xB05A, 0x8C48,\n\t0xB05B, 0x8C7A, 0xB05C, 0x8C79, 0xB05D, 0x8CA1, 0xB05E, 0x8CA2,\n\t0xB05F, 0x8D77, 0xB060, 0x8EAC, 0xB061, 0x8ED2, 0xB062, 0x8ED4,\n\t0xB063, 0x8ECF, 0xB064, 0x8FB1, 0xB065, 0x9001, 0xB066, 0x9006,\n\t0xB067, 0x8FF7, 0xB068, 0x9000, 0xB069, 0x8FFA, 0xB06A, 0x8FF4,\n\t0xB06B, 0x9003, 0xB06C, 0x8FFD, 0xB06D, 0x9005, 0xB06E, 0x8FF8,\n\t0xB06F, 0x9095, 0xB070, 0x90E1, 0xB071, 0x90DD, 0xB072, 0x90E2,\n\t0xB073, 0x9152, 0xB074, 0x914D, 0xB075, 0x914C, 0xB076, 0x91D8,\n\t0xB077, 0x91DD, 0xB078, 0x91D7, 0xB079, 0x91DC, 0xB07A, 0x91D9,\n\t0xB07B, 0x9583, 0xB07C, 0x9662, 0xB07D, 0x9663, 0xB07E, 0x9661,\n\t0xB0A1, 0x965B, 0xB0A2, 0x965D, 0xB0A3, 0x9664, 0xB0A4, 0x9658,\n\t0xB0A5, 0x965E, 0xB0A6, 0x96BB, 0xB0A7, 0x98E2, 0xB0A8, 0x99AC,\n\t0xB0A9, 0x9AA8, 0xB0AA, 0x9AD8, 0xB0AB, 0x9B25, 0xB0AC, 0x9B32,\n\t0xB0AD, 0x9B3C, 0xB0AE, 0x4E7E, 0xB0AF, 0x507A, 0xB0B0, 0x507D,\n\t0xB0B1, 0x505C, 0xB0B2, 0x5047, 0xB0B3, 0x5043, 0xB0B4, 0x504C,\n\t0xB0B5, 0x505A, 0xB0B6, 0x5049, 0xB0B7, 0x5065, 0xB0B8, 0x5076,\n\t0xB0B9, 0x504E, 0xB0BA, 0x5055, 0xB0BB, 0x5075, 0xB0BC, 0x5074,\n\t0xB0BD, 0x5077, 0xB0BE, 0x504F, 0xB0BF, 0x500F, 0xB0C0, 0x506F,\n\t0xB0C1, 0x506D, 0xB0C2, 0x515C, 0xB0C3, 0x5195, 0xB0C4, 0x51F0,\n\t0xB0C5, 0x526A, 0xB0C6, 0x526F, 0xB0C7, 0x52D2, 0xB0C8, 0x52D9,\n\t0xB0C9, 0x52D8, 0xB0CA, 0x52D5, 0xB0CB, 0x5310, 0xB0CC, 0x530F,\n\t0xB0CD, 0x5319, 0xB0CE, 0x533F, 0xB0CF, 0x5340, 0xB0D0, 0x533E,\n\t0xB0D1, 0x53C3, 0xB0D2, 0x66FC, 0xB0D3, 0x5546, 0xB0D4, 0x556A,\n\t0xB0D5, 0x5566, 0xB0D6, 0x5544, 0xB0D7, 0x555E, 0xB0D8, 0x5561,\n\t0xB0D9, 0x5543, 0xB0DA, 0x554A, 0xB0DB, 0x5531, 0xB0DC, 0x5556,\n\t0xB0DD, 0x554F, 0xB0DE, 0x5555, 0xB0DF, 0x552F, 0xB0E0, 0x5564,\n\t0xB0E1, 0x5538, 0xB0E2, 0x552E, 0xB0E3, 0x555C, 0xB0E4, 0x552C,\n\t0xB0E5, 0x5563, 0xB0E6, 0x5533, 0xB0E7, 0x5541, 0xB0E8, 0x5557,\n\t0xB0E9, 0x5708, 0xB0EA, 0x570B, 0xB0EB, 0x5709, 0xB0EC, 0x57DF,\n\t0xB0ED, 0x5805, 0xB0EE, 0x580A, 0xB0EF, 0x5806, 0xB0F0, 0x57E0,\n\t0xB0F1, 0x57E4, 0xB0F2, 0x57FA, 0xB0F3, 0x5802, 0xB0F4, 0x5835,\n\t0xB0F5, 0x57F7, 0xB0F6, 0x57F9, 0xB0F7, 0x5920, 0xB0F8, 0x5962,\n\t0xB0F9, 0x5A36, 0xB0FA, 0x5A41, 0xB0FB, 0x5A49, 0xB0FC, 0x5A66,\n\t0xB0FD, 0x5A6A, 0xB0FE, 0x5A40, 0xB140, 0x5A3C, 0xB141, 0x5A62,\n\t0xB142, 0x5A5A, 0xB143, 0x5A46, 0xB144, 0x5A4A, 0xB145, 0x5B70,\n\t0xB146, 0x5BC7, 0xB147, 0x5BC5, 0xB148, 0x5BC4, 0xB149, 0x5BC2,\n\t0xB14A, 0x5BBF, 0xB14B, 0x5BC6, 0xB14C, 0x5C09, 0xB14D, 0x5C08,\n\t0xB14E, 0x5C07, 0xB14F, 0x5C60, 0xB150, 0x5C5C, 0xB151, 0x5C5D,\n\t0xB152, 0x5D07, 0xB153, 0x5D06, 0xB154, 0x5D0E, 0xB155, 0x5D1B,\n\t0xB156, 0x5D16, 0xB157, 0x5D22, 0xB158, 0x5D11, 0xB159, 0x5D29,\n\t0xB15A, 0x5D14, 0xB15B, 0x5D19, 0xB15C, 0x5D24, 0xB15D, 0x5D27,\n\t0xB15E, 0x5D17, 0xB15F, 0x5DE2, 0xB160, 0x5E38, 0xB161, 0x5E36,\n\t0xB162, 0x5E33, 0xB163, 0x5E37, 0xB164, 0x5EB7, 0xB165, 0x5EB8,\n\t0xB166, 0x5EB6, 0xB167, 0x5EB5, 0xB168, 0x5EBE, 0xB169, 0x5F35,\n\t0xB16A, 0x5F37, 0xB16B, 0x5F57, 0xB16C, 0x5F6C, 0xB16D, 0x5F69,\n\t0xB16E, 0x5F6B, 0xB16F, 0x5F97, 0xB170, 0x5F99, 0xB171, 0x5F9E,\n\t0xB172, 0x5F98, 0xB173, 0x5FA1, 0xB174, 0x5FA0, 0xB175, 0x5F9C,\n\t0xB176, 0x607F, 0xB177, 0x60A3, 0xB178, 0x6089, 0xB179, 0x60A0,\n\t0xB17A, 0x60A8, 0xB17B, 0x60CB, 0xB17C, 0x60B4, 0xB17D, 0x60E6,\n\t0xB17E, 0x60BD, 0xB1A1, 0x60C5, 0xB1A2, 0x60BB, 0xB1A3, 0x60B5,\n\t0xB1A4, 0x60DC, 0xB1A5, 0x60BC, 0xB1A6, 0x60D8, 0xB1A7, 0x60D5,\n\t0xB1A8, 0x60C6, 0xB1A9, 0x60DF, 0xB1AA, 0x60B8, 0xB1AB, 0x60DA,\n\t0xB1AC, 0x60C7, 0xB1AD, 0x621A, 0xB1AE, 0x621B, 0xB1AF, 0x6248,\n\t0xB1B0, 0x63A0, 0xB1B1, 0x63A7, 0xB1B2, 0x6372, 0xB1B3, 0x6396,\n\t0xB1B4, 0x63A2, 0xB1B5, 0x63A5, 0xB1B6, 0x6377, 0xB1B7, 0x6367,\n\t0xB1B8, 0x6398, 0xB1B9, 0x63AA, 0xB1BA, 0x6371, 0xB1BB, 0x63A9,\n\t0xB1BC, 0x6389, 0xB1BD, 0x6383, 0xB1BE, 0x639B, 0xB1BF, 0x636B,\n\t0xB1C0, 0x63A8, 0xB1C1, 0x6384, 0xB1C2, 0x6388, 0xB1C3, 0x6399,\n\t0xB1C4, 0x63A1, 0xB1C5, 0x63AC, 0xB1C6, 0x6392, 0xB1C7, 0x638F,\n\t0xB1C8, 0x6380, 0xB1C9, 0x637B, 0xB1CA, 0x6369, 0xB1CB, 0x6368,\n\t0xB1CC, 0x637A, 0xB1CD, 0x655D, 0xB1CE, 0x6556, 0xB1CF, 0x6551,\n\t0xB1D0, 0x6559, 0xB1D1, 0x6557, 0xB1D2, 0x555F, 0xB1D3, 0x654F,\n\t0xB1D4, 0x6558, 0xB1D5, 0x6555, 0xB1D6, 0x6554, 0xB1D7, 0x659C,\n\t0xB1D8, 0x659B, 0xB1D9, 0x65AC, 0xB1DA, 0x65CF, 0xB1DB, 0x65CB,\n\t0xB1DC, 0x65CC, 0xB1DD, 0x65CE, 0xB1DE, 0x665D, 0xB1DF, 0x665A,\n\t0xB1E0, 0x6664, 0xB1E1, 0x6668, 0xB1E2, 0x6666, 0xB1E3, 0x665E,\n\t0xB1E4, 0x66F9, 0xB1E5, 0x52D7, 0xB1E6, 0x671B, 0xB1E7, 0x6881,\n\t0xB1E8, 0x68AF, 0xB1E9, 0x68A2, 0xB1EA, 0x6893, 0xB1EB, 0x68B5,\n\t0xB1EC, 0x687F, 0xB1ED, 0x6876, 0xB1EE, 0x68B1, 0xB1EF, 0x68A7,\n\t0xB1F0, 0x6897, 0xB1F1, 0x68B0, 0xB1F2, 0x6883, 0xB1F3, 0x68C4,\n\t0xB1F4, 0x68AD, 0xB1F5, 0x6886, 0xB1F6, 0x6885, 0xB1F7, 0x6894,\n\t0xB1F8, 0x689D, 0xB1F9, 0x68A8, 0xB1FA, 0x689F, 0xB1FB, 0x68A1,\n\t0xB1FC, 0x6882, 0xB1FD, 0x6B32, 0xB1FE, 0x6BBA, 0xB240, 0x6BEB,\n\t0xB241, 0x6BEC, 0xB242, 0x6C2B, 0xB243, 0x6D8E, 0xB244, 0x6DBC,\n\t0xB245, 0x6DF3, 0xB246, 0x6DD9, 0xB247, 0x6DB2, 0xB248, 0x6DE1,\n\t0xB249, 0x6DCC, 0xB24A, 0x6DE4, 0xB24B, 0x6DFB, 0xB24C, 0x6DFA,\n\t0xB24D, 0x6E05, 0xB24E, 0x6DC7, 0xB24F, 0x6DCB, 0xB250, 0x6DAF,\n\t0xB251, 0x6DD1, 0xB252, 0x6DAE, 0xB253, 0x6DDE, 0xB254, 0x6DF9,\n\t0xB255, 0x6DB8, 0xB256, 0x6DF7, 0xB257, 0x6DF5, 0xB258, 0x6DC5,\n\t0xB259, 0x6DD2, 0xB25A, 0x6E1A, 0xB25B, 0x6DB5, 0xB25C, 0x6DDA,\n\t0xB25D, 0x6DEB, 0xB25E, 0x6DD8, 0xB25F, 0x6DEA, 0xB260, 0x6DF1,\n\t0xB261, 0x6DEE, 0xB262, 0x6DE8, 0xB263, 0x6DC6, 0xB264, 0x6DC4,\n\t0xB265, 0x6DAA, 0xB266, 0x6DEC, 0xB267, 0x6DBF, 0xB268, 0x6DE6,\n\t0xB269, 0x70F9, 0xB26A, 0x7109, 0xB26B, 0x710A, 0xB26C, 0x70FD,\n\t0xB26D, 0x70EF, 0xB26E, 0x723D, 0xB26F, 0x727D, 0xB270, 0x7281,\n\t0xB271, 0x731C, 0xB272, 0x731B, 0xB273, 0x7316, 0xB274, 0x7313,\n\t0xB275, 0x7319, 0xB276, 0x7387, 0xB277, 0x7405, 0xB278, 0x740A,\n\t0xB279, 0x7403, 0xB27A, 0x7406, 0xB27B, 0x73FE, 0xB27C, 0x740D,\n\t0xB27D, 0x74E0, 0xB27E, 0x74F6, 0xB2A1, 0x74F7, 0xB2A2, 0x751C,\n\t0xB2A3, 0x7522, 0xB2A4, 0x7565, 0xB2A5, 0x7566, 0xB2A6, 0x7562,\n\t0xB2A7, 0x7570, 0xB2A8, 0x758F, 0xB2A9, 0x75D4, 0xB2AA, 0x75D5,\n\t0xB2AB, 0x75B5, 0xB2AC, 0x75CA, 0xB2AD, 0x75CD, 0xB2AE, 0x768E,\n\t0xB2AF, 0x76D4, 0xB2B0, 0x76D2, 0xB2B1, 0x76DB, 0xB2B2, 0x7737,\n\t0xB2B3, 0x773E, 0xB2B4, 0x773C, 0xB2B5, 0x7736, 0xB2B6, 0x7738,\n\t0xB2B7, 0x773A, 0xB2B8, 0x786B, 0xB2B9, 0x7843, 0xB2BA, 0x784E,\n\t0xB2BB, 0x7965, 0xB2BC, 0x7968, 0xB2BD, 0x796D, 0xB2BE, 0x79FB,\n\t0xB2BF, 0x7A92, 0xB2C0, 0x7A95, 0xB2C1, 0x7B20, 0xB2C2, 0x7B28,\n\t0xB2C3, 0x7B1B, 0xB2C4, 0x7B2C, 0xB2C5, 0x7B26, 0xB2C6, 0x7B19,\n\t0xB2C7, 0x7B1E, 0xB2C8, 0x7B2E, 0xB2C9, 0x7C92, 0xB2CA, 0x7C97,\n\t0xB2CB, 0x7C95, 0xB2CC, 0x7D46, 0xB2CD, 0x7D43, 0xB2CE, 0x7D71,\n\t0xB2CF, 0x7D2E, 0xB2D0, 0x7D39, 0xB2D1, 0x7D3C, 0xB2D2, 0x7D40,\n\t0xB2D3, 0x7D30, 0xB2D4, 0x7D33, 0xB2D5, 0x7D44, 0xB2D6, 0x7D2F,\n\t0xB2D7, 0x7D42, 0xB2D8, 0x7D32, 0xB2D9, 0x7D31, 0xB2DA, 0x7F3D,\n\t0xB2DB, 0x7F9E, 0xB2DC, 0x7F9A, 0xB2DD, 0x7FCC, 0xB2DE, 0x7FCE,\n\t0xB2DF, 0x7FD2, 0xB2E0, 0x801C, 0xB2E1, 0x804A, 0xB2E2, 0x8046,\n\t0xB2E3, 0x812F, 0xB2E4, 0x8116, 0xB2E5, 0x8123, 0xB2E6, 0x812B,\n\t0xB2E7, 0x8129, 0xB2E8, 0x8130, 0xB2E9, 0x8124, 0xB2EA, 0x8202,\n\t0xB2EB, 0x8235, 0xB2EC, 0x8237, 0xB2ED, 0x8236, 0xB2EE, 0x8239,\n\t0xB2EF, 0x838E, 0xB2F0, 0x839E, 0xB2F1, 0x8398, 0xB2F2, 0x8378,\n\t0xB2F3, 0x83A2, 0xB2F4, 0x8396, 0xB2F5, 0x83BD, 0xB2F6, 0x83AB,\n\t0xB2F7, 0x8392, 0xB2F8, 0x838A, 0xB2F9, 0x8393, 0xB2FA, 0x8389,\n\t0xB2FB, 0x83A0, 0xB2FC, 0x8377, 0xB2FD, 0x837B, 0xB2FE, 0x837C,\n\t0xB340, 0x8386, 0xB341, 0x83A7, 0xB342, 0x8655, 0xB343, 0x5F6A,\n\t0xB344, 0x86C7, 0xB345, 0x86C0, 0xB346, 0x86B6, 0xB347, 0x86C4,\n\t0xB348, 0x86B5, 0xB349, 0x86C6, 0xB34A, 0x86CB, 0xB34B, 0x86B1,\n\t0xB34C, 0x86AF, 0xB34D, 0x86C9, 0xB34E, 0x8853, 0xB34F, 0x889E,\n\t0xB350, 0x8888, 0xB351, 0x88AB, 0xB352, 0x8892, 0xB353, 0x8896,\n\t0xB354, 0x888D, 0xB355, 0x888B, 0xB356, 0x8993, 0xB357, 0x898F,\n\t0xB358, 0x8A2A, 0xB359, 0x8A1D, 0xB35A, 0x8A23, 0xB35B, 0x8A25,\n\t0xB35C, 0x8A31, 0xB35D, 0x8A2D, 0xB35E, 0x8A1F, 0xB35F, 0x8A1B,\n\t0xB360, 0x8A22, 0xB361, 0x8C49, 0xB362, 0x8C5A, 0xB363, 0x8CA9,\n\t0xB364, 0x8CAC, 0xB365, 0x8CAB, 0xB366, 0x8CA8, 0xB367, 0x8CAA,\n\t0xB368, 0x8CA7, 0xB369, 0x8D67, 0xB36A, 0x8D66, 0xB36B, 0x8DBE,\n\t0xB36C, 0x8DBA, 0xB36D, 0x8EDB, 0xB36E, 0x8EDF, 0xB36F, 0x9019,\n\t0xB370, 0x900D, 0xB371, 0x901A, 0xB372, 0x9017, 0xB373, 0x9023,\n\t0xB374, 0x901F, 0xB375, 0x901D, 0xB376, 0x9010, 0xB377, 0x9015,\n\t0xB378, 0x901E, 0xB379, 0x9020, 0xB37A, 0x900F, 0xB37B, 0x9022,\n\t0xB37C, 0x9016, 0xB37D, 0x901B, 0xB37E, 0x9014, 0xB3A1, 0x90E8,\n\t0xB3A2, 0x90ED, 0xB3A3, 0x90FD, 0xB3A4, 0x9157, 0xB3A5, 0x91CE,\n\t0xB3A6, 0x91F5, 0xB3A7, 0x91E6, 0xB3A8, 0x91E3, 0xB3A9, 0x91E7,\n\t0xB3AA, 0x91ED, 0xB3AB, 0x91E9, 0xB3AC, 0x9589, 0xB3AD, 0x966A,\n\t0xB3AE, 0x9675, 0xB3AF, 0x9673, 0xB3B0, 0x9678, 0xB3B1, 0x9670,\n\t0xB3B2, 0x9674, 0xB3B3, 0x9676, 0xB3B4, 0x9677, 0xB3B5, 0x966C,\n\t0xB3B6, 0x96C0, 0xB3B7, 0x96EA, 0xB3B8, 0x96E9, 0xB3B9, 0x7AE0,\n\t0xB3BA, 0x7ADF, 0xB3BB, 0x9802, 0xB3BC, 0x9803, 0xB3BD, 0x9B5A,\n\t0xB3BE, 0x9CE5, 0xB3BF, 0x9E75, 0xB3C0, 0x9E7F, 0xB3C1, 0x9EA5,\n\t0xB3C2, 0x9EBB, 0xB3C3, 0x50A2, 0xB3C4, 0x508D, 0xB3C5, 0x5085,\n\t0xB3C6, 0x5099, 0xB3C7, 0x5091, 0xB3C8, 0x5080, 0xB3C9, 0x5096,\n\t0xB3CA, 0x5098, 0xB3CB, 0x509A, 0xB3CC, 0x6700, 0xB3CD, 0x51F1,\n\t0xB3CE, 0x5272, 0xB3CF, 0x5274, 0xB3D0, 0x5275, 0xB3D1, 0x5269,\n\t0xB3D2, 0x52DE, 0xB3D3, 0x52DD, 0xB3D4, 0x52DB, 0xB3D5, 0x535A,\n\t0xB3D6, 0x53A5, 0xB3D7, 0x557B, 0xB3D8, 0x5580, 0xB3D9, 0x55A7,\n\t0xB3DA, 0x557C, 0xB3DB, 0x558A, 0xB3DC, 0x559D, 0xB3DD, 0x5598,\n\t0xB3DE, 0x5582, 0xB3DF, 0x559C, 0xB3E0, 0x55AA, 0xB3E1, 0x5594,\n\t0xB3E2, 0x5587, 0xB3E3, 0x558B, 0xB3E4, 0x5583, 0xB3E5, 0x55B3,\n\t0xB3E6, 0x55AE, 0xB3E7, 0x559F, 0xB3E8, 0x553E, 0xB3E9, 0x55B2,\n\t0xB3EA, 0x559A, 0xB3EB, 0x55BB, 0xB3EC, 0x55AC, 0xB3ED, 0x55B1,\n\t0xB3EE, 0x557E, 0xB3EF, 0x5589, 0xB3F0, 0x55AB, 0xB3F1, 0x5599,\n\t0xB3F2, 0x570D, 0xB3F3, 0x582F, 0xB3F4, 0x582A, 0xB3F5, 0x5834,\n\t0xB3F6, 0x5824, 0xB3F7, 0x5830, 0xB3F8, 0x5831, 0xB3F9, 0x5821,\n\t0xB3FA, 0x581D, 0xB3FB, 0x5820, 0xB3FC, 0x58F9, 0xB3FD, 0x58FA,\n\t0xB3FE, 0x5960, 0xB440, 0x5A77, 0xB441, 0x5A9A, 0xB442, 0x5A7F,\n\t0xB443, 0x5A92, 0xB444, 0x5A9B, 0xB445, 0x5AA7, 0xB446, 0x5B73,\n\t0xB447, 0x5B71, 0xB448, 0x5BD2, 0xB449, 0x5BCC, 0xB44A, 0x5BD3,\n\t0xB44B, 0x5BD0, 0xB44C, 0x5C0A, 0xB44D, 0x5C0B, 0xB44E, 0x5C31,\n\t0xB44F, 0x5D4C, 0xB450, 0x5D50, 0xB451, 0x5D34, 0xB452, 0x5D47,\n\t0xB453, 0x5DFD, 0xB454, 0x5E45, 0xB455, 0x5E3D, 0xB456, 0x5E40,\n\t0xB457, 0x5E43, 0xB458, 0x5E7E, 0xB459, 0x5ECA, 0xB45A, 0x5EC1,\n\t0xB45B, 0x5EC2, 0xB45C, 0x5EC4, 0xB45D, 0x5F3C, 0xB45E, 0x5F6D,\n\t0xB45F, 0x5FA9, 0xB460, 0x5FAA, 0xB461, 0x5FA8, 0xB462, 0x60D1,\n\t0xB463, 0x60E1, 0xB464, 0x60B2, 0xB465, 0x60B6, 0xB466, 0x60E0,\n\t0xB467, 0x611C, 0xB468, 0x6123, 0xB469, 0x60FA, 0xB46A, 0x6115,\n\t0xB46B, 0x60F0, 0xB46C, 0x60FB, 0xB46D, 0x60F4, 0xB46E, 0x6168,\n\t0xB46F, 0x60F1, 0xB470, 0x610E, 0xB471, 0x60F6, 0xB472, 0x6109,\n\t0xB473, 0x6100, 0xB474, 0x6112, 0xB475, 0x621F, 0xB476, 0x6249,\n\t0xB477, 0x63A3, 0xB478, 0x638C, 0xB479, 0x63CF, 0xB47A, 0x63C0,\n\t0xB47B, 0x63E9, 0xB47C, 0x63C9, 0xB47D, 0x63C6, 0xB47E, 0x63CD,\n\t0xB4A1, 0x63D2, 0xB4A2, 0x63E3, 0xB4A3, 0x63D0, 0xB4A4, 0x63E1,\n\t0xB4A5, 0x63D6, 0xB4A6, 0x63ED, 0xB4A7, 0x63EE, 0xB4A8, 0x6376,\n\t0xB4A9, 0x63F4, 0xB4AA, 0x63EA, 0xB4AB, 0x63DB, 0xB4AC, 0x6452,\n\t0xB4AD, 0x63DA, 0xB4AE, 0x63F9, 0xB4AF, 0x655E, 0xB4B0, 0x6566,\n\t0xB4B1, 0x6562, 0xB4B2, 0x6563, 0xB4B3, 0x6591, 0xB4B4, 0x6590,\n\t0xB4B5, 0x65AF, 0xB4B6, 0x666E, 0xB4B7, 0x6670, 0xB4B8, 0x6674,\n\t0xB4B9, 0x6676, 0xB4BA, 0x666F, 0xB4BB, 0x6691, 0xB4BC, 0x667A,\n\t0xB4BD, 0x667E, 0xB4BE, 0x6677, 0xB4BF, 0x66FE, 0xB4C0, 0x66FF,\n\t0xB4C1, 0x671F, 0xB4C2, 0x671D, 0xB4C3, 0x68FA, 0xB4C4, 0x68D5,\n\t0xB4C5, 0x68E0, 0xB4C6, 0x68D8, 0xB4C7, 0x68D7, 0xB4C8, 0x6905,\n\t0xB4C9, 0x68DF, 0xB4CA, 0x68F5, 0xB4CB, 0x68EE, 0xB4CC, 0x68E7,\n\t0xB4CD, 0x68F9, 0xB4CE, 0x68D2, 0xB4CF, 0x68F2, 0xB4D0, 0x68E3,\n\t0xB4D1, 0x68CB, 0xB4D2, 0x68CD, 0xB4D3, 0x690D, 0xB4D4, 0x6912,\n\t0xB4D5, 0x690E, 0xB4D6, 0x68C9, 0xB4D7, 0x68DA, 0xB4D8, 0x696E,\n\t0xB4D9, 0x68FB, 0xB4DA, 0x6B3E, 0xB4DB, 0x6B3A, 0xB4DC, 0x6B3D,\n\t0xB4DD, 0x6B98, 0xB4DE, 0x6B96, 0xB4DF, 0x6BBC, 0xB4E0, 0x6BEF,\n\t0xB4E1, 0x6C2E, 0xB4E2, 0x6C2F, 0xB4E3, 0x6C2C, 0xB4E4, 0x6E2F,\n\t0xB4E5, 0x6E38, 0xB4E6, 0x6E54, 0xB4E7, 0x6E21, 0xB4E8, 0x6E32,\n\t0xB4E9, 0x6E67, 0xB4EA, 0x6E4A, 0xB4EB, 0x6E20, 0xB4EC, 0x6E25,\n\t0xB4ED, 0x6E23, 0xB4EE, 0x6E1B, 0xB4EF, 0x6E5B, 0xB4F0, 0x6E58,\n\t0xB4F1, 0x6E24, 0xB4F2, 0x6E56, 0xB4F3, 0x6E6E, 0xB4F4, 0x6E2D,\n\t0xB4F5, 0x6E26, 0xB4F6, 0x6E6F, 0xB4F7, 0x6E34, 0xB4F8, 0x6E4D,\n\t0xB4F9, 0x6E3A, 0xB4FA, 0x6E2C, 0xB4FB, 0x6E43, 0xB4FC, 0x6E1D,\n\t0xB4FD, 0x6E3E, 0xB4FE, 0x6ECB, 0xB540, 0x6E89, 0xB541, 0x6E19,\n\t0xB542, 0x6E4E, 0xB543, 0x6E63, 0xB544, 0x6E44, 0xB545, 0x6E72,\n\t0xB546, 0x6E69, 0xB547, 0x6E5F, 0xB548, 0x7119, 0xB549, 0x711A,\n\t0xB54A, 0x7126, 0xB54B, 0x7130, 0xB54C, 0x7121, 0xB54D, 0x7136,\n\t0xB54E, 0x716E, 0xB54F, 0x711C, 0xB550, 0x724C, 0xB551, 0x7284,\n\t0xB552, 0x7280, 0xB553, 0x7336, 0xB554, 0x7325, 0xB555, 0x7334,\n\t0xB556, 0x7329, 0xB557, 0x743A, 0xB558, 0x742A, 0xB559, 0x7433,\n\t0xB55A, 0x7422, 0xB55B, 0x7425, 0xB55C, 0x7435, 0xB55D, 0x7436,\n\t0xB55E, 0x7434, 0xB55F, 0x742F, 0xB560, 0x741B, 0xB561, 0x7426,\n\t0xB562, 0x7428, 0xB563, 0x7525, 0xB564, 0x7526, 0xB565, 0x756B,\n\t0xB566, 0x756A, 0xB567, 0x75E2, 0xB568, 0x75DB, 0xB569, 0x75E3,\n\t0xB56A, 0x75D9, 0xB56B, 0x75D8, 0xB56C, 0x75DE, 0xB56D, 0x75E0,\n\t0xB56E, 0x767B, 0xB56F, 0x767C, 0xB570, 0x7696, 0xB571, 0x7693,\n\t0xB572, 0x76B4, 0xB573, 0x76DC, 0xB574, 0x774F, 0xB575, 0x77ED,\n\t0xB576, 0x785D, 0xB577, 0x786C, 0xB578, 0x786F, 0xB579, 0x7A0D,\n\t0xB57A, 0x7A08, 0xB57B, 0x7A0B, 0xB57C, 0x7A05, 0xB57D, 0x7A00,\n\t0xB57E, 0x7A98, 0xB5A1, 0x7A97, 0xB5A2, 0x7A96, 0xB5A3, 0x7AE5,\n\t0xB5A4, 0x7AE3, 0xB5A5, 0x7B49, 0xB5A6, 0x7B56, 0xB5A7, 0x7B46,\n\t0xB5A8, 0x7B50, 0xB5A9, 0x7B52, 0xB5AA, 0x7B54, 0xB5AB, 0x7B4D,\n\t0xB5AC, 0x7B4B, 0xB5AD, 0x7B4F, 0xB5AE, 0x7B51, 0xB5AF, 0x7C9F,\n\t0xB5B0, 0x7CA5, 0xB5B1, 0x7D5E, 0xB5B2, 0x7D50, 0xB5B3, 0x7D68,\n\t0xB5B4, 0x7D55, 0xB5B5, 0x7D2B, 0xB5B6, 0x7D6E, 0xB5B7, 0x7D72,\n\t0xB5B8, 0x7D61, 0xB5B9, 0x7D66, 0xB5BA, 0x7D62, 0xB5BB, 0x7D70,\n\t0xB5BC, 0x7D73, 0xB5BD, 0x5584, 0xB5BE, 0x7FD4, 0xB5BF, 0x7FD5,\n\t0xB5C0, 0x800B, 0xB5C1, 0x8052, 0xB5C2, 0x8085, 0xB5C3, 0x8155,\n\t0xB5C4, 0x8154, 0xB5C5, 0x814B, 0xB5C6, 0x8151, 0xB5C7, 0x814E,\n\t0xB5C8, 0x8139, 0xB5C9, 0x8146, 0xB5CA, 0x813E, 0xB5CB, 0x814C,\n\t0xB5CC, 0x8153, 0xB5CD, 0x8174, 0xB5CE, 0x8212, 0xB5CF, 0x821C,\n\t0xB5D0, 0x83E9, 0xB5D1, 0x8403, 0xB5D2, 0x83F8, 0xB5D3, 0x840D,\n\t0xB5D4, 0x83E0, 0xB5D5, 0x83C5, 0xB5D6, 0x840B, 0xB5D7, 0x83C1,\n\t0xB5D8, 0x83EF, 0xB5D9, 0x83F1, 0xB5DA, 0x83F4, 0xB5DB, 0x8457,\n\t0xB5DC, 0x840A, 0xB5DD, 0x83F0, 0xB5DE, 0x840C, 0xB5DF, 0x83CC,\n\t0xB5E0, 0x83FD, 0xB5E1, 0x83F2, 0xB5E2, 0x83CA, 0xB5E3, 0x8438,\n\t0xB5E4, 0x840E, 0xB5E5, 0x8404, 0xB5E6, 0x83DC, 0xB5E7, 0x8407,\n\t0xB5E8, 0x83D4, 0xB5E9, 0x83DF, 0xB5EA, 0x865B, 0xB5EB, 0x86DF,\n\t0xB5EC, 0x86D9, 0xB5ED, 0x86ED, 0xB5EE, 0x86D4, 0xB5EF, 0x86DB,\n\t0xB5F0, 0x86E4, 0xB5F1, 0x86D0, 0xB5F2, 0x86DE, 0xB5F3, 0x8857,\n\t0xB5F4, 0x88C1, 0xB5F5, 0x88C2, 0xB5F6, 0x88B1, 0xB5F7, 0x8983,\n\t0xB5F8, 0x8996, 0xB5F9, 0x8A3B, 0xB5FA, 0x8A60, 0xB5FB, 0x8A55,\n\t0xB5FC, 0x8A5E, 0xB5FD, 0x8A3C, 0xB5FE, 0x8A41, 0xB640, 0x8A54,\n\t0xB641, 0x8A5B, 0xB642, 0x8A50, 0xB643, 0x8A46, 0xB644, 0x8A34,\n\t0xB645, 0x8A3A, 0xB646, 0x8A36, 0xB647, 0x8A56, 0xB648, 0x8C61,\n\t0xB649, 0x8C82, 0xB64A, 0x8CAF, 0xB64B, 0x8CBC, 0xB64C, 0x8CB3,\n\t0xB64D, 0x8CBD, 0xB64E, 0x8CC1, 0xB64F, 0x8CBB, 0xB650, 0x8CC0,\n\t0xB651, 0x8CB4, 0xB652, 0x8CB7, 0xB653, 0x8CB6, 0xB654, 0x8CBF,\n\t0xB655, 0x8CB8, 0xB656, 0x8D8A, 0xB657, 0x8D85, 0xB658, 0x8D81,\n\t0xB659, 0x8DCE, 0xB65A, 0x8DDD, 0xB65B, 0x8DCB, 0xB65C, 0x8DDA,\n\t0xB65D, 0x8DD1, 0xB65E, 0x8DCC, 0xB65F, 0x8DDB, 0xB660, 0x8DC6,\n\t0xB661, 0x8EFB, 0xB662, 0x8EF8, 0xB663, 0x8EFC, 0xB664, 0x8F9C,\n\t0xB665, 0x902E, 0xB666, 0x9035, 0xB667, 0x9031, 0xB668, 0x9038,\n\t0xB669, 0x9032, 0xB66A, 0x9036, 0xB66B, 0x9102, 0xB66C, 0x90F5,\n\t0xB66D, 0x9109, 0xB66E, 0x90FE, 0xB66F, 0x9163, 0xB670, 0x9165,\n\t0xB671, 0x91CF, 0xB672, 0x9214, 0xB673, 0x9215, 0xB674, 0x9223,\n\t0xB675, 0x9209, 0xB676, 0x921E, 0xB677, 0x920D, 0xB678, 0x9210,\n\t0xB679, 0x9207, 0xB67A, 0x9211, 0xB67B, 0x9594, 0xB67C, 0x958F,\n\t0xB67D, 0x958B, 0xB67E, 0x9591, 0xB6A1, 0x9593, 0xB6A2, 0x9592,\n\t0xB6A3, 0x958E, 0xB6A4, 0x968A, 0xB6A5, 0x968E, 0xB6A6, 0x968B,\n\t0xB6A7, 0x967D, 0xB6A8, 0x9685, 0xB6A9, 0x9686, 0xB6AA, 0x968D,\n\t0xB6AB, 0x9672, 0xB6AC, 0x9684, 0xB6AD, 0x96C1, 0xB6AE, 0x96C5,\n\t0xB6AF, 0x96C4, 0xB6B0, 0x96C6, 0xB6B1, 0x96C7, 0xB6B2, 0x96EF,\n\t0xB6B3, 0x96F2, 0xB6B4, 0x97CC, 0xB6B5, 0x9805, 0xB6B6, 0x9806,\n\t0xB6B7, 0x9808, 0xB6B8, 0x98E7, 0xB6B9, 0x98EA, 0xB6BA, 0x98EF,\n\t0xB6BB, 0x98E9, 0xB6BC, 0x98F2, 0xB6BD, 0x98ED, 0xB6BE, 0x99AE,\n\t0xB6BF, 0x99AD, 0xB6C0, 0x9EC3, 0xB6C1, 0x9ECD, 0xB6C2, 0x9ED1,\n\t0xB6C3, 0x4E82, 0xB6C4, 0x50AD, 0xB6C5, 0x50B5, 0xB6C6, 0x50B2,\n\t0xB6C7, 0x50B3, 0xB6C8, 0x50C5, 0xB6C9, 0x50BE, 0xB6CA, 0x50AC,\n\t0xB6CB, 0x50B7, 0xB6CC, 0x50BB, 0xB6CD, 0x50AF, 0xB6CE, 0x50C7,\n\t0xB6CF, 0x527F, 0xB6D0, 0x5277, 0xB6D1, 0x527D, 0xB6D2, 0x52DF,\n\t0xB6D3, 0x52E6, 0xB6D4, 0x52E4, 0xB6D5, 0x52E2, 0xB6D6, 0x52E3,\n\t0xB6D7, 0x532F, 0xB6D8, 0x55DF, 0xB6D9, 0x55E8, 0xB6DA, 0x55D3,\n\t0xB6DB, 0x55E6, 0xB6DC, 0x55CE, 0xB6DD, 0x55DC, 0xB6DE, 0x55C7,\n\t0xB6DF, 0x55D1, 0xB6E0, 0x55E3, 0xB6E1, 0x55E4, 0xB6E2, 0x55EF,\n\t0xB6E3, 0x55DA, 0xB6E4, 0x55E1, 0xB6E5, 0x55C5, 0xB6E6, 0x55C6,\n\t0xB6E7, 0x55E5, 0xB6E8, 0x55C9, 0xB6E9, 0x5712, 0xB6EA, 0x5713,\n\t0xB6EB, 0x585E, 0xB6EC, 0x5851, 0xB6ED, 0x5858, 0xB6EE, 0x5857,\n\t0xB6EF, 0x585A, 0xB6F0, 0x5854, 0xB6F1, 0x586B, 0xB6F2, 0x584C,\n\t0xB6F3, 0x586D, 0xB6F4, 0x584A, 0xB6F5, 0x5862, 0xB6F6, 0x5852,\n\t0xB6F7, 0x584B, 0xB6F8, 0x5967, 0xB6F9, 0x5AC1, 0xB6FA, 0x5AC9,\n\t0xB6FB, 0x5ACC, 0xB6FC, 0x5ABE, 0xB6FD, 0x5ABD, 0xB6FE, 0x5ABC,\n\t0xB740, 0x5AB3, 0xB741, 0x5AC2, 0xB742, 0x5AB2, 0xB743, 0x5D69,\n\t0xB744, 0x5D6F, 0xB745, 0x5E4C, 0xB746, 0x5E79, 0xB747, 0x5EC9,\n\t0xB748, 0x5EC8, 0xB749, 0x5F12, 0xB74A, 0x5F59, 0xB74B, 0x5FAC,\n\t0xB74C, 0x5FAE, 0xB74D, 0x611A, 0xB74E, 0x610F, 0xB74F, 0x6148,\n\t0xB750, 0x611F, 0xB751, 0x60F3, 0xB752, 0x611B, 0xB753, 0x60F9,\n\t0xB754, 0x6101, 0xB755, 0x6108, 0xB756, 0x614E, 0xB757, 0x614C,\n\t0xB758, 0x6144, 0xB759, 0x614D, 0xB75A, 0x613E, 0xB75B, 0x6134,\n\t0xB75C, 0x6127, 0xB75D, 0x610D, 0xB75E, 0x6106, 0xB75F, 0x6137,\n\t0xB760, 0x6221, 0xB761, 0x6222, 0xB762, 0x6413, 0xB763, 0x643E,\n\t0xB764, 0x641E, 0xB765, 0x642A, 0xB766, 0x642D, 0xB767, 0x643D,\n\t0xB768, 0x642C, 0xB769, 0x640F, 0xB76A, 0x641C, 0xB76B, 0x6414,\n\t0xB76C, 0x640D, 0xB76D, 0x6436, 0xB76E, 0x6416, 0xB76F, 0x6417,\n\t0xB770, 0x6406, 0xB771, 0x656C, 0xB772, 0x659F, 0xB773, 0x65B0,\n\t0xB774, 0x6697, 0xB775, 0x6689, 0xB776, 0x6687, 0xB777, 0x6688,\n\t0xB778, 0x6696, 0xB779, 0x6684, 0xB77A, 0x6698, 0xB77B, 0x668D,\n\t0xB77C, 0x6703, 0xB77D, 0x6994, 0xB77E, 0x696D, 0xB7A1, 0x695A,\n\t0xB7A2, 0x6977, 0xB7A3, 0x6960, 0xB7A4, 0x6954, 0xB7A5, 0x6975,\n\t0xB7A6, 0x6930, 0xB7A7, 0x6982, 0xB7A8, 0x694A, 0xB7A9, 0x6968,\n\t0xB7AA, 0x696B, 0xB7AB, 0x695E, 0xB7AC, 0x6953, 0xB7AD, 0x6979,\n\t0xB7AE, 0x6986, 0xB7AF, 0x695D, 0xB7B0, 0x6963, 0xB7B1, 0x695B,\n\t0xB7B2, 0x6B47, 0xB7B3, 0x6B72, 0xB7B4, 0x6BC0, 0xB7B5, 0x6BBF,\n\t0xB7B6, 0x6BD3, 0xB7B7, 0x6BFD, 0xB7B8, 0x6EA2, 0xB7B9, 0x6EAF,\n\t0xB7BA, 0x6ED3, 0xB7BB, 0x6EB6, 0xB7BC, 0x6EC2, 0xB7BD, 0x6E90,\n\t0xB7BE, 0x6E9D, 0xB7BF, 0x6EC7, 0xB7C0, 0x6EC5, 0xB7C1, 0x6EA5,\n\t0xB7C2, 0x6E98, 0xB7C3, 0x6EBC, 0xB7C4, 0x6EBA, 0xB7C5, 0x6EAB,\n\t0xB7C6, 0x6ED1, 0xB7C7, 0x6E96, 0xB7C8, 0x6E9C, 0xB7C9, 0x6EC4,\n\t0xB7CA, 0x6ED4, 0xB7CB, 0x6EAA, 0xB7CC, 0x6EA7, 0xB7CD, 0x6EB4,\n\t0xB7CE, 0x714E, 0xB7CF, 0x7159, 0xB7D0, 0x7169, 0xB7D1, 0x7164,\n\t0xB7D2, 0x7149, 0xB7D3, 0x7167, 0xB7D4, 0x715C, 0xB7D5, 0x716C,\n\t0xB7D6, 0x7166, 0xB7D7, 0x714C, 0xB7D8, 0x7165, 0xB7D9, 0x715E,\n\t0xB7DA, 0x7146, 0xB7DB, 0x7168, 0xB7DC, 0x7156, 0xB7DD, 0x723A,\n\t0xB7DE, 0x7252, 0xB7DF, 0x7337, 0xB7E0, 0x7345, 0xB7E1, 0x733F,\n\t0xB7E2, 0x733E, 0xB7E3, 0x746F, 0xB7E4, 0x745A, 0xB7E5, 0x7455,\n\t0xB7E6, 0x745F, 0xB7E7, 0x745E, 0xB7E8, 0x7441, 0xB7E9, 0x743F,\n\t0xB7EA, 0x7459, 0xB7EB, 0x745B, 0xB7EC, 0x745C, 0xB7ED, 0x7576,\n\t0xB7EE, 0x7578, 0xB7EF, 0x7600, 0xB7F0, 0x75F0, 0xB7F1, 0x7601,\n\t0xB7F2, 0x75F2, 0xB7F3, 0x75F1, 0xB7F4, 0x75FA, 0xB7F5, 0x75FF,\n\t0xB7F6, 0x75F4, 0xB7F7, 0x75F3, 0xB7F8, 0x76DE, 0xB7F9, 0x76DF,\n\t0xB7FA, 0x775B, 0xB7FB, 0x776B, 0xB7FC, 0x7766, 0xB7FD, 0x775E,\n\t0xB7FE, 0x7763, 0xB840, 0x7779, 0xB841, 0x776A, 0xB842, 0x776C,\n\t0xB843, 0x775C, 0xB844, 0x7765, 0xB845, 0x7768, 0xB846, 0x7762,\n\t0xB847, 0x77EE, 0xB848, 0x788E, 0xB849, 0x78B0, 0xB84A, 0x7897,\n\t0xB84B, 0x7898, 0xB84C, 0x788C, 0xB84D, 0x7889, 0xB84E, 0x787C,\n\t0xB84F, 0x7891, 0xB850, 0x7893, 0xB851, 0x787F, 0xB852, 0x797A,\n\t0xB853, 0x797F, 0xB854, 0x7981, 0xB855, 0x842C, 0xB856, 0x79BD,\n\t0xB857, 0x7A1C, 0xB858, 0x7A1A, 0xB859, 0x7A20, 0xB85A, 0x7A14,\n\t0xB85B, 0x7A1F, 0xB85C, 0x7A1E, 0xB85D, 0x7A9F, 0xB85E, 0x7AA0,\n\t0xB85F, 0x7B77, 0xB860, 0x7BC0, 0xB861, 0x7B60, 0xB862, 0x7B6E,\n\t0xB863, 0x7B67, 0xB864, 0x7CB1, 0xB865, 0x7CB3, 0xB866, 0x7CB5,\n\t0xB867, 0x7D93, 0xB868, 0x7D79, 0xB869, 0x7D91, 0xB86A, 0x7D81,\n\t0xB86B, 0x7D8F, 0xB86C, 0x7D5B, 0xB86D, 0x7F6E, 0xB86E, 0x7F69,\n\t0xB86F, 0x7F6A, 0xB870, 0x7F72, 0xB871, 0x7FA9, 0xB872, 0x7FA8,\n\t0xB873, 0x7FA4, 0xB874, 0x8056, 0xB875, 0x8058, 0xB876, 0x8086,\n\t0xB877, 0x8084, 0xB878, 0x8171, 0xB879, 0x8170, 0xB87A, 0x8178,\n\t0xB87B, 0x8165, 0xB87C, 0x816E, 0xB87D, 0x8173, 0xB87E, 0x816B,\n\t0xB8A1, 0x8179, 0xB8A2, 0x817A, 0xB8A3, 0x8166, 0xB8A4, 0x8205,\n\t0xB8A5, 0x8247, 0xB8A6, 0x8482, 0xB8A7, 0x8477, 0xB8A8, 0x843D,\n\t0xB8A9, 0x8431, 0xB8AA, 0x8475, 0xB8AB, 0x8466, 0xB8AC, 0x846B,\n\t0xB8AD, 0x8449, 0xB8AE, 0x846C, 0xB8AF, 0x845B, 0xB8B0, 0x843C,\n\t0xB8B1, 0x8435, 0xB8B2, 0x8461, 0xB8B3, 0x8463, 0xB8B4, 0x8469,\n\t0xB8B5, 0x846D, 0xB8B6, 0x8446, 0xB8B7, 0x865E, 0xB8B8, 0x865C,\n\t0xB8B9, 0x865F, 0xB8BA, 0x86F9, 0xB8BB, 0x8713, 0xB8BC, 0x8708,\n\t0xB8BD, 0x8707, 0xB8BE, 0x8700, 0xB8BF, 0x86FE, 0xB8C0, 0x86FB,\n\t0xB8C1, 0x8702, 0xB8C2, 0x8703, 0xB8C3, 0x8706, 0xB8C4, 0x870A,\n\t0xB8C5, 0x8859, 0xB8C6, 0x88DF, 0xB8C7, 0x88D4, 0xB8C8, 0x88D9,\n\t0xB8C9, 0x88DC, 0xB8CA, 0x88D8, 0xB8CB, 0x88DD, 0xB8CC, 0x88E1,\n\t0xB8CD, 0x88CA, 0xB8CE, 0x88D5, 0xB8CF, 0x88D2, 0xB8D0, 0x899C,\n\t0xB8D1, 0x89E3, 0xB8D2, 0x8A6B, 0xB8D3, 0x8A72, 0xB8D4, 0x8A73,\n\t0xB8D5, 0x8A66, 0xB8D6, 0x8A69, 0xB8D7, 0x8A70, 0xB8D8, 0x8A87,\n\t0xB8D9, 0x8A7C, 0xB8DA, 0x8A63, 0xB8DB, 0x8AA0, 0xB8DC, 0x8A71,\n\t0xB8DD, 0x8A85, 0xB8DE, 0x8A6D, 0xB8DF, 0x8A62, 0xB8E0, 0x8A6E,\n\t0xB8E1, 0x8A6C, 0xB8E2, 0x8A79, 0xB8E3, 0x8A7B, 0xB8E4, 0x8A3E,\n\t0xB8E5, 0x8A68, 0xB8E6, 0x8C62, 0xB8E7, 0x8C8A, 0xB8E8, 0x8C89,\n\t0xB8E9, 0x8CCA, 0xB8EA, 0x8CC7, 0xB8EB, 0x8CC8, 0xB8EC, 0x8CC4,\n\t0xB8ED, 0x8CB2, 0xB8EE, 0x8CC3, 0xB8EF, 0x8CC2, 0xB8F0, 0x8CC5,\n\t0xB8F1, 0x8DE1, 0xB8F2, 0x8DDF, 0xB8F3, 0x8DE8, 0xB8F4, 0x8DEF,\n\t0xB8F5, 0x8DF3, 0xB8F6, 0x8DFA, 0xB8F7, 0x8DEA, 0xB8F8, 0x8DE4,\n\t0xB8F9, 0x8DE6, 0xB8FA, 0x8EB2, 0xB8FB, 0x8F03, 0xB8FC, 0x8F09,\n\t0xB8FD, 0x8EFE, 0xB8FE, 0x8F0A, 0xB940, 0x8F9F, 0xB941, 0x8FB2,\n\t0xB942, 0x904B, 0xB943, 0x904A, 0xB944, 0x9053, 0xB945, 0x9042,\n\t0xB946, 0x9054, 0xB947, 0x903C, 0xB948, 0x9055, 0xB949, 0x9050,\n\t0xB94A, 0x9047, 0xB94B, 0x904F, 0xB94C, 0x904E, 0xB94D, 0x904D,\n\t0xB94E, 0x9051, 0xB94F, 0x903E, 0xB950, 0x9041, 0xB951, 0x9112,\n\t0xB952, 0x9117, 0xB953, 0x916C, 0xB954, 0x916A, 0xB955, 0x9169,\n\t0xB956, 0x91C9, 0xB957, 0x9237, 0xB958, 0x9257, 0xB959, 0x9238,\n\t0xB95A, 0x923D, 0xB95B, 0x9240, 0xB95C, 0x923E, 0xB95D, 0x925B,\n\t0xB95E, 0x924B, 0xB95F, 0x9264, 0xB960, 0x9251, 0xB961, 0x9234,\n\t0xB962, 0x9249, 0xB963, 0x924D, 0xB964, 0x9245, 0xB965, 0x9239,\n\t0xB966, 0x923F, 0xB967, 0x925A, 0xB968, 0x9598, 0xB969, 0x9698,\n\t0xB96A, 0x9694, 0xB96B, 0x9695, 0xB96C, 0x96CD, 0xB96D, 0x96CB,\n\t0xB96E, 0x96C9, 0xB96F, 0x96CA, 0xB970, 0x96F7, 0xB971, 0x96FB,\n\t0xB972, 0x96F9, 0xB973, 0x96F6, 0xB974, 0x9756, 0xB975, 0x9774,\n\t0xB976, 0x9776, 0xB977, 0x9810, 0xB978, 0x9811, 0xB979, 0x9813,\n\t0xB97A, 0x980A, 0xB97B, 0x9812, 0xB97C, 0x980C, 0xB97D, 0x98FC,\n\t0xB97E, 0x98F4, 0xB9A1, 0x98FD, 0xB9A2, 0x98FE, 0xB9A3, 0x99B3,\n\t0xB9A4, 0x99B1, 0xB9A5, 0x99B4, 0xB9A6, 0x9AE1, 0xB9A7, 0x9CE9,\n\t0xB9A8, 0x9E82, 0xB9A9, 0x9F0E, 0xB9AA, 0x9F13, 0xB9AB, 0x9F20,\n\t0xB9AC, 0x50E7, 0xB9AD, 0x50EE, 0xB9AE, 0x50E5, 0xB9AF, 0x50D6,\n\t0xB9B0, 0x50ED, 0xB9B1, 0x50DA, 0xB9B2, 0x50D5, 0xB9B3, 0x50CF,\n\t0xB9B4, 0x50D1, 0xB9B5, 0x50F1, 0xB9B6, 0x50CE, 0xB9B7, 0x50E9,\n\t0xB9B8, 0x5162, 0xB9B9, 0x51F3, 0xB9BA, 0x5283, 0xB9BB, 0x5282,\n\t0xB9BC, 0x5331, 0xB9BD, 0x53AD, 0xB9BE, 0x55FE, 0xB9BF, 0x5600,\n\t0xB9C0, 0x561B, 0xB9C1, 0x5617, 0xB9C2, 0x55FD, 0xB9C3, 0x5614,\n\t0xB9C4, 0x5606, 0xB9C5, 0x5609, 0xB9C6, 0x560D, 0xB9C7, 0x560E,\n\t0xB9C8, 0x55F7, 0xB9C9, 0x5616, 0xB9CA, 0x561F, 0xB9CB, 0x5608,\n\t0xB9CC, 0x5610, 0xB9CD, 0x55F6, 0xB9CE, 0x5718, 0xB9CF, 0x5716,\n\t0xB9D0, 0x5875, 0xB9D1, 0x587E, 0xB9D2, 0x5883, 0xB9D3, 0x5893,\n\t0xB9D4, 0x588A, 0xB9D5, 0x5879, 0xB9D6, 0x5885, 0xB9D7, 0x587D,\n\t0xB9D8, 0x58FD, 0xB9D9, 0x5925, 0xB9DA, 0x5922, 0xB9DB, 0x5924,\n\t0xB9DC, 0x596A, 0xB9DD, 0x5969, 0xB9DE, 0x5AE1, 0xB9DF, 0x5AE6,\n\t0xB9E0, 0x5AE9, 0xB9E1, 0x5AD7, 0xB9E2, 0x5AD6, 0xB9E3, 0x5AD8,\n\t0xB9E4, 0x5AE3, 0xB9E5, 0x5B75, 0xB9E6, 0x5BDE, 0xB9E7, 0x5BE7,\n\t0xB9E8, 0x5BE1, 0xB9E9, 0x5BE5, 0xB9EA, 0x5BE6, 0xB9EB, 0x5BE8,\n\t0xB9EC, 0x5BE2, 0xB9ED, 0x5BE4, 0xB9EE, 0x5BDF, 0xB9EF, 0x5C0D,\n\t0xB9F0, 0x5C62, 0xB9F1, 0x5D84, 0xB9F2, 0x5D87, 0xB9F3, 0x5E5B,\n\t0xB9F4, 0x5E63, 0xB9F5, 0x5E55, 0xB9F6, 0x5E57, 0xB9F7, 0x5E54,\n\t0xB9F8, 0x5ED3, 0xB9F9, 0x5ED6, 0xB9FA, 0x5F0A, 0xB9FB, 0x5F46,\n\t0xB9FC, 0x5F70, 0xB9FD, 0x5FB9, 0xB9FE, 0x6147, 0xBA40, 0x613F,\n\t0xBA41, 0x614B, 0xBA42, 0x6177, 0xBA43, 0x6162, 0xBA44, 0x6163,\n\t0xBA45, 0x615F, 0xBA46, 0x615A, 0xBA47, 0x6158, 0xBA48, 0x6175,\n\t0xBA49, 0x622A, 0xBA4A, 0x6487, 0xBA4B, 0x6458, 0xBA4C, 0x6454,\n\t0xBA4D, 0x64A4, 0xBA4E, 0x6478, 0xBA4F, 0x645F, 0xBA50, 0x647A,\n\t0xBA51, 0x6451, 0xBA52, 0x6467, 0xBA53, 0x6434, 0xBA54, 0x646D,\n\t0xBA55, 0x647B, 0xBA56, 0x6572, 0xBA57, 0x65A1, 0xBA58, 0x65D7,\n\t0xBA59, 0x65D6, 0xBA5A, 0x66A2, 0xBA5B, 0x66A8, 0xBA5C, 0x669D,\n\t0xBA5D, 0x699C, 0xBA5E, 0x69A8, 0xBA5F, 0x6995, 0xBA60, 0x69C1,\n\t0xBA61, 0x69AE, 0xBA62, 0x69D3, 0xBA63, 0x69CB, 0xBA64, 0x699B,\n\t0xBA65, 0x69B7, 0xBA66, 0x69BB, 0xBA67, 0x69AB, 0xBA68, 0x69B4,\n\t0xBA69, 0x69D0, 0xBA6A, 0x69CD, 0xBA6B, 0x69AD, 0xBA6C, 0x69CC,\n\t0xBA6D, 0x69A6, 0xBA6E, 0x69C3, 0xBA6F, 0x69A3, 0xBA70, 0x6B49,\n\t0xBA71, 0x6B4C, 0xBA72, 0x6C33, 0xBA73, 0x6F33, 0xBA74, 0x6F14,\n\t0xBA75, 0x6EFE, 0xBA76, 0x6F13, 0xBA77, 0x6EF4, 0xBA78, 0x6F29,\n\t0xBA79, 0x6F3E, 0xBA7A, 0x6F20, 0xBA7B, 0x6F2C, 0xBA7C, 0x6F0F,\n\t0xBA7D, 0x6F02, 0xBA7E, 0x6F22, 0xBAA1, 0x6EFF, 0xBAA2, 0x6EEF,\n\t0xBAA3, 0x6F06, 0xBAA4, 0x6F31, 0xBAA5, 0x6F38, 0xBAA6, 0x6F32,\n\t0xBAA7, 0x6F23, 0xBAA8, 0x6F15, 0xBAA9, 0x6F2B, 0xBAAA, 0x6F2F,\n\t0xBAAB, 0x6F88, 0xBAAC, 0x6F2A, 0xBAAD, 0x6EEC, 0xBAAE, 0x6F01,\n\t0xBAAF, 0x6EF2, 0xBAB0, 0x6ECC, 0xBAB1, 0x6EF7, 0xBAB2, 0x7194,\n\t0xBAB3, 0x7199, 0xBAB4, 0x717D, 0xBAB5, 0x718A, 0xBAB6, 0x7184,\n\t0xBAB7, 0x7192, 0xBAB8, 0x723E, 0xBAB9, 0x7292, 0xBABA, 0x7296,\n\t0xBABB, 0x7344, 0xBABC, 0x7350, 0xBABD, 0x7464, 0xBABE, 0x7463,\n\t0xBABF, 0x746A, 0xBAC0, 0x7470, 0xBAC1, 0x746D, 0xBAC2, 0x7504,\n\t0xBAC3, 0x7591, 0xBAC4, 0x7627, 0xBAC5, 0x760D, 0xBAC6, 0x760B,\n\t0xBAC7, 0x7609, 0xBAC8, 0x7613, 0xBAC9, 0x76E1, 0xBACA, 0x76E3,\n\t0xBACB, 0x7784, 0xBACC, 0x777D, 0xBACD, 0x777F, 0xBACE, 0x7761,\n\t0xBACF, 0x78C1, 0xBAD0, 0x789F, 0xBAD1, 0x78A7, 0xBAD2, 0x78B3,\n\t0xBAD3, 0x78A9, 0xBAD4, 0x78A3, 0xBAD5, 0x798E, 0xBAD6, 0x798F,\n\t0xBAD7, 0x798D, 0xBAD8, 0x7A2E, 0xBAD9, 0x7A31, 0xBADA, 0x7AAA,\n\t0xBADB, 0x7AA9, 0xBADC, 0x7AED, 0xBADD, 0x7AEF, 0xBADE, 0x7BA1,\n\t0xBADF, 0x7B95, 0xBAE0, 0x7B8B, 0xBAE1, 0x7B75, 0xBAE2, 0x7B97,\n\t0xBAE3, 0x7B9D, 0xBAE4, 0x7B94, 0xBAE5, 0x7B8F, 0xBAE6, 0x7BB8,\n\t0xBAE7, 0x7B87, 0xBAE8, 0x7B84, 0xBAE9, 0x7CB9, 0xBAEA, 0x7CBD,\n\t0xBAEB, 0x7CBE, 0xBAEC, 0x7DBB, 0xBAED, 0x7DB0, 0xBAEE, 0x7D9C,\n\t0xBAEF, 0x7DBD, 0xBAF0, 0x7DBE, 0xBAF1, 0x7DA0, 0xBAF2, 0x7DCA,\n\t0xBAF3, 0x7DB4, 0xBAF4, 0x7DB2, 0xBAF5, 0x7DB1, 0xBAF6, 0x7DBA,\n\t0xBAF7, 0x7DA2, 0xBAF8, 0x7DBF, 0xBAF9, 0x7DB5, 0xBAFA, 0x7DB8,\n\t0xBAFB, 0x7DAD, 0xBAFC, 0x7DD2, 0xBAFD, 0x7DC7, 0xBAFE, 0x7DAC,\n\t0xBB40, 0x7F70, 0xBB41, 0x7FE0, 0xBB42, 0x7FE1, 0xBB43, 0x7FDF,\n\t0xBB44, 0x805E, 0xBB45, 0x805A, 0xBB46, 0x8087, 0xBB47, 0x8150,\n\t0xBB48, 0x8180, 0xBB49, 0x818F, 0xBB4A, 0x8188, 0xBB4B, 0x818A,\n\t0xBB4C, 0x817F, 0xBB4D, 0x8182, 0xBB4E, 0x81E7, 0xBB4F, 0x81FA,\n\t0xBB50, 0x8207, 0xBB51, 0x8214, 0xBB52, 0x821E, 0xBB53, 0x824B,\n\t0xBB54, 0x84C9, 0xBB55, 0x84BF, 0xBB56, 0x84C6, 0xBB57, 0x84C4,\n\t0xBB58, 0x8499, 0xBB59, 0x849E, 0xBB5A, 0x84B2, 0xBB5B, 0x849C,\n\t0xBB5C, 0x84CB, 0xBB5D, 0x84B8, 0xBB5E, 0x84C0, 0xBB5F, 0x84D3,\n\t0xBB60, 0x8490, 0xBB61, 0x84BC, 0xBB62, 0x84D1, 0xBB63, 0x84CA,\n\t0xBB64, 0x873F, 0xBB65, 0x871C, 0xBB66, 0x873B, 0xBB67, 0x8722,\n\t0xBB68, 0x8725, 0xBB69, 0x8734, 0xBB6A, 0x8718, 0xBB6B, 0x8755,\n\t0xBB6C, 0x8737, 0xBB6D, 0x8729, 0xBB6E, 0x88F3, 0xBB6F, 0x8902,\n\t0xBB70, 0x88F4, 0xBB71, 0x88F9, 0xBB72, 0x88F8, 0xBB73, 0x88FD,\n\t0xBB74, 0x88E8, 0xBB75, 0x891A, 0xBB76, 0x88EF, 0xBB77, 0x8AA6,\n\t0xBB78, 0x8A8C, 0xBB79, 0x8A9E, 0xBB7A, 0x8AA3, 0xBB7B, 0x8A8D,\n\t0xBB7C, 0x8AA1, 0xBB7D, 0x8A93, 0xBB7E, 0x8AA4, 0xBBA1, 0x8AAA,\n\t0xBBA2, 0x8AA5, 0xBBA3, 0x8AA8, 0xBBA4, 0x8A98, 0xBBA5, 0x8A91,\n\t0xBBA6, 0x8A9A, 0xBBA7, 0x8AA7, 0xBBA8, 0x8C6A, 0xBBA9, 0x8C8D,\n\t0xBBAA, 0x8C8C, 0xBBAB, 0x8CD3, 0xBBAC, 0x8CD1, 0xBBAD, 0x8CD2,\n\t0xBBAE, 0x8D6B, 0xBBAF, 0x8D99, 0xBBB0, 0x8D95, 0xBBB1, 0x8DFC,\n\t0xBBB2, 0x8F14, 0xBBB3, 0x8F12, 0xBBB4, 0x8F15, 0xBBB5, 0x8F13,\n\t0xBBB6, 0x8FA3, 0xBBB7, 0x9060, 0xBBB8, 0x9058, 0xBBB9, 0x905C,\n\t0xBBBA, 0x9063, 0xBBBB, 0x9059, 0xBBBC, 0x905E, 0xBBBD, 0x9062,\n\t0xBBBE, 0x905D, 0xBBBF, 0x905B, 0xBBC0, 0x9119, 0xBBC1, 0x9118,\n\t0xBBC2, 0x911E, 0xBBC3, 0x9175, 0xBBC4, 0x9178, 0xBBC5, 0x9177,\n\t0xBBC6, 0x9174, 0xBBC7, 0x9278, 0xBBC8, 0x9280, 0xBBC9, 0x9285,\n\t0xBBCA, 0x9298, 0xBBCB, 0x9296, 0xBBCC, 0x927B, 0xBBCD, 0x9293,\n\t0xBBCE, 0x929C, 0xBBCF, 0x92A8, 0xBBD0, 0x927C, 0xBBD1, 0x9291,\n\t0xBBD2, 0x95A1, 0xBBD3, 0x95A8, 0xBBD4, 0x95A9, 0xBBD5, 0x95A3,\n\t0xBBD6, 0x95A5, 0xBBD7, 0x95A4, 0xBBD8, 0x9699, 0xBBD9, 0x969C,\n\t0xBBDA, 0x969B, 0xBBDB, 0x96CC, 0xBBDC, 0x96D2, 0xBBDD, 0x9700,\n\t0xBBDE, 0x977C, 0xBBDF, 0x9785, 0xBBE0, 0x97F6, 0xBBE1, 0x9817,\n\t0xBBE2, 0x9818, 0xBBE3, 0x98AF, 0xBBE4, 0x98B1, 0xBBE5, 0x9903,\n\t0xBBE6, 0x9905, 0xBBE7, 0x990C, 0xBBE8, 0x9909, 0xBBE9, 0x99C1,\n\t0xBBEA, 0x9AAF, 0xBBEB, 0x9AB0, 0xBBEC, 0x9AE6, 0xBBED, 0x9B41,\n\t0xBBEE, 0x9B42, 0xBBEF, 0x9CF4, 0xBBF0, 0x9CF6, 0xBBF1, 0x9CF3,\n\t0xBBF2, 0x9EBC, 0xBBF3, 0x9F3B, 0xBBF4, 0x9F4A, 0xBBF5, 0x5104,\n\t0xBBF6, 0x5100, 0xBBF7, 0x50FB, 0xBBF8, 0x50F5, 0xBBF9, 0x50F9,\n\t0xBBFA, 0x5102, 0xBBFB, 0x5108, 0xBBFC, 0x5109, 0xBBFD, 0x5105,\n\t0xBBFE, 0x51DC, 0xBC40, 0x5287, 0xBC41, 0x5288, 0xBC42, 0x5289,\n\t0xBC43, 0x528D, 0xBC44, 0x528A, 0xBC45, 0x52F0, 0xBC46, 0x53B2,\n\t0xBC47, 0x562E, 0xBC48, 0x563B, 0xBC49, 0x5639, 0xBC4A, 0x5632,\n\t0xBC4B, 0x563F, 0xBC4C, 0x5634, 0xBC4D, 0x5629, 0xBC4E, 0x5653,\n\t0xBC4F, 0x564E, 0xBC50, 0x5657, 0xBC51, 0x5674, 0xBC52, 0x5636,\n\t0xBC53, 0x562F, 0xBC54, 0x5630, 0xBC55, 0x5880, 0xBC56, 0x589F,\n\t0xBC57, 0x589E, 0xBC58, 0x58B3, 0xBC59, 0x589C, 0xBC5A, 0x58AE,\n\t0xBC5B, 0x58A9, 0xBC5C, 0x58A6, 0xBC5D, 0x596D, 0xBC5E, 0x5B09,\n\t0xBC5F, 0x5AFB, 0xBC60, 0x5B0B, 0xBC61, 0x5AF5, 0xBC62, 0x5B0C,\n\t0xBC63, 0x5B08, 0xBC64, 0x5BEE, 0xBC65, 0x5BEC, 0xBC66, 0x5BE9,\n\t0xBC67, 0x5BEB, 0xBC68, 0x5C64, 0xBC69, 0x5C65, 0xBC6A, 0x5D9D,\n\t0xBC6B, 0x5D94, 0xBC6C, 0x5E62, 0xBC6D, 0x5E5F, 0xBC6E, 0x5E61,\n\t0xBC6F, 0x5EE2, 0xBC70, 0x5EDA, 0xBC71, 0x5EDF, 0xBC72, 0x5EDD,\n\t0xBC73, 0x5EE3, 0xBC74, 0x5EE0, 0xBC75, 0x5F48, 0xBC76, 0x5F71,\n\t0xBC77, 0x5FB7, 0xBC78, 0x5FB5, 0xBC79, 0x6176, 0xBC7A, 0x6167,\n\t0xBC7B, 0x616E, 0xBC7C, 0x615D, 0xBC7D, 0x6155, 0xBC7E, 0x6182,\n\t0xBCA1, 0x617C, 0xBCA2, 0x6170, 0xBCA3, 0x616B, 0xBCA4, 0x617E,\n\t0xBCA5, 0x61A7, 0xBCA6, 0x6190, 0xBCA7, 0x61AB, 0xBCA8, 0x618E,\n\t0xBCA9, 0x61AC, 0xBCAA, 0x619A, 0xBCAB, 0x61A4, 0xBCAC, 0x6194,\n\t0xBCAD, 0x61AE, 0xBCAE, 0x622E, 0xBCAF, 0x6469, 0xBCB0, 0x646F,\n\t0xBCB1, 0x6479, 0xBCB2, 0x649E, 0xBCB3, 0x64B2, 0xBCB4, 0x6488,\n\t0xBCB5, 0x6490, 0xBCB6, 0x64B0, 0xBCB7, 0x64A5, 0xBCB8, 0x6493,\n\t0xBCB9, 0x6495, 0xBCBA, 0x64A9, 0xBCBB, 0x6492, 0xBCBC, 0x64AE,\n\t0xBCBD, 0x64AD, 0xBCBE, 0x64AB, 0xBCBF, 0x649A, 0xBCC0, 0x64AC,\n\t0xBCC1, 0x6499, 0xBCC2, 0x64A2, 0xBCC3, 0x64B3, 0xBCC4, 0x6575,\n\t0xBCC5, 0x6577, 0xBCC6, 0x6578, 0xBCC7, 0x66AE, 0xBCC8, 0x66AB,\n\t0xBCC9, 0x66B4, 0xBCCA, 0x66B1, 0xBCCB, 0x6A23, 0xBCCC, 0x6A1F,\n\t0xBCCD, 0x69E8, 0xBCCE, 0x6A01, 0xBCCF, 0x6A1E, 0xBCD0, 0x6A19,\n\t0xBCD1, 0x69FD, 0xBCD2, 0x6A21, 0xBCD3, 0x6A13, 0xBCD4, 0x6A0A,\n\t0xBCD5, 0x69F3, 0xBCD6, 0x6A02, 0xBCD7, 0x6A05, 0xBCD8, 0x69ED,\n\t0xBCD9, 0x6A11, 0xBCDA, 0x6B50, 0xBCDB, 0x6B4E, 0xBCDC, 0x6BA4,\n\t0xBCDD, 0x6BC5, 0xBCDE, 0x6BC6, 0xBCDF, 0x6F3F, 0xBCE0, 0x6F7C,\n\t0xBCE1, 0x6F84, 0xBCE2, 0x6F51, 0xBCE3, 0x6F66, 0xBCE4, 0x6F54,\n\t0xBCE5, 0x6F86, 0xBCE6, 0x6F6D, 0xBCE7, 0x6F5B, 0xBCE8, 0x6F78,\n\t0xBCE9, 0x6F6E, 0xBCEA, 0x6F8E, 0xBCEB, 0x6F7A, 0xBCEC, 0x6F70,\n\t0xBCED, 0x6F64, 0xBCEE, 0x6F97, 0xBCEF, 0x6F58, 0xBCF0, 0x6ED5,\n\t0xBCF1, 0x6F6F, 0xBCF2, 0x6F60, 0xBCF3, 0x6F5F, 0xBCF4, 0x719F,\n\t0xBCF5, 0x71AC, 0xBCF6, 0x71B1, 0xBCF7, 0x71A8, 0xBCF8, 0x7256,\n\t0xBCF9, 0x729B, 0xBCFA, 0x734E, 0xBCFB, 0x7357, 0xBCFC, 0x7469,\n\t0xBCFD, 0x748B, 0xBCFE, 0x7483, 0xBD40, 0x747E, 0xBD41, 0x7480,\n\t0xBD42, 0x757F, 0xBD43, 0x7620, 0xBD44, 0x7629, 0xBD45, 0x761F,\n\t0xBD46, 0x7624, 0xBD47, 0x7626, 0xBD48, 0x7621, 0xBD49, 0x7622,\n\t0xBD4A, 0x769A, 0xBD4B, 0x76BA, 0xBD4C, 0x76E4, 0xBD4D, 0x778E,\n\t0xBD4E, 0x7787, 0xBD4F, 0x778C, 0xBD50, 0x7791, 0xBD51, 0x778B,\n\t0xBD52, 0x78CB, 0xBD53, 0x78C5, 0xBD54, 0x78BA, 0xBD55, 0x78CA,\n\t0xBD56, 0x78BE, 0xBD57, 0x78D5, 0xBD58, 0x78BC, 0xBD59, 0x78D0,\n\t0xBD5A, 0x7A3F, 0xBD5B, 0x7A3C, 0xBD5C, 0x7A40, 0xBD5D, 0x7A3D,\n\t0xBD5E, 0x7A37, 0xBD5F, 0x7A3B, 0xBD60, 0x7AAF, 0xBD61, 0x7AAE,\n\t0xBD62, 0x7BAD, 0xBD63, 0x7BB1, 0xBD64, 0x7BC4, 0xBD65, 0x7BB4,\n\t0xBD66, 0x7BC6, 0xBD67, 0x7BC7, 0xBD68, 0x7BC1, 0xBD69, 0x7BA0,\n\t0xBD6A, 0x7BCC, 0xBD6B, 0x7CCA, 0xBD6C, 0x7DE0, 0xBD6D, 0x7DF4,\n\t0xBD6E, 0x7DEF, 0xBD6F, 0x7DFB, 0xBD70, 0x7DD8, 0xBD71, 0x7DEC,\n\t0xBD72, 0x7DDD, 0xBD73, 0x7DE8, 0xBD74, 0x7DE3, 0xBD75, 0x7DDA,\n\t0xBD76, 0x7DDE, 0xBD77, 0x7DE9, 0xBD78, 0x7D9E, 0xBD79, 0x7DD9,\n\t0xBD7A, 0x7DF2, 0xBD7B, 0x7DF9, 0xBD7C, 0x7F75, 0xBD7D, 0x7F77,\n\t0xBD7E, 0x7FAF, 0xBDA1, 0x7FE9, 0xBDA2, 0x8026, 0xBDA3, 0x819B,\n\t0xBDA4, 0x819C, 0xBDA5, 0x819D, 0xBDA6, 0x81A0, 0xBDA7, 0x819A,\n\t0xBDA8, 0x8198, 0xBDA9, 0x8517, 0xBDAA, 0x853D, 0xBDAB, 0x851A,\n\t0xBDAC, 0x84EE, 0xBDAD, 0x852C, 0xBDAE, 0x852D, 0xBDAF, 0x8513,\n\t0xBDB0, 0x8511, 0xBDB1, 0x8523, 0xBDB2, 0x8521, 0xBDB3, 0x8514,\n\t0xBDB4, 0x84EC, 0xBDB5, 0x8525, 0xBDB6, 0x84FF, 0xBDB7, 0x8506,\n\t0xBDB8, 0x8782, 0xBDB9, 0x8774, 0xBDBA, 0x8776, 0xBDBB, 0x8760,\n\t0xBDBC, 0x8766, 0xBDBD, 0x8778, 0xBDBE, 0x8768, 0xBDBF, 0x8759,\n\t0xBDC0, 0x8757, 0xBDC1, 0x874C, 0xBDC2, 0x8753, 0xBDC3, 0x885B,\n\t0xBDC4, 0x885D, 0xBDC5, 0x8910, 0xBDC6, 0x8907, 0xBDC7, 0x8912,\n\t0xBDC8, 0x8913, 0xBDC9, 0x8915, 0xBDCA, 0x890A, 0xBDCB, 0x8ABC,\n\t0xBDCC, 0x8AD2, 0xBDCD, 0x8AC7, 0xBDCE, 0x8AC4, 0xBDCF, 0x8A95,\n\t0xBDD0, 0x8ACB, 0xBDD1, 0x8AF8, 0xBDD2, 0x8AB2, 0xBDD3, 0x8AC9,\n\t0xBDD4, 0x8AC2, 0xBDD5, 0x8ABF, 0xBDD6, 0x8AB0, 0xBDD7, 0x8AD6,\n\t0xBDD8, 0x8ACD, 0xBDD9, 0x8AB6, 0xBDDA, 0x8AB9, 0xBDDB, 0x8ADB,\n\t0xBDDC, 0x8C4C, 0xBDDD, 0x8C4E, 0xBDDE, 0x8C6C, 0xBDDF, 0x8CE0,\n\t0xBDE0, 0x8CDE, 0xBDE1, 0x8CE6, 0xBDE2, 0x8CE4, 0xBDE3, 0x8CEC,\n\t0xBDE4, 0x8CED, 0xBDE5, 0x8CE2, 0xBDE6, 0x8CE3, 0xBDE7, 0x8CDC,\n\t0xBDE8, 0x8CEA, 0xBDE9, 0x8CE1, 0xBDEA, 0x8D6D, 0xBDEB, 0x8D9F,\n\t0xBDEC, 0x8DA3, 0xBDED, 0x8E2B, 0xBDEE, 0x8E10, 0xBDEF, 0x8E1D,\n\t0xBDF0, 0x8E22, 0xBDF1, 0x8E0F, 0xBDF2, 0x8E29, 0xBDF3, 0x8E1F,\n\t0xBDF4, 0x8E21, 0xBDF5, 0x8E1E, 0xBDF6, 0x8EBA, 0xBDF7, 0x8F1D,\n\t0xBDF8, 0x8F1B, 0xBDF9, 0x8F1F, 0xBDFA, 0x8F29, 0xBDFB, 0x8F26,\n\t0xBDFC, 0x8F2A, 0xBDFD, 0x8F1C, 0xBDFE, 0x8F1E, 0xBE40, 0x8F25,\n\t0xBE41, 0x9069, 0xBE42, 0x906E, 0xBE43, 0x9068, 0xBE44, 0x906D,\n\t0xBE45, 0x9077, 0xBE46, 0x9130, 0xBE47, 0x912D, 0xBE48, 0x9127,\n\t0xBE49, 0x9131, 0xBE4A, 0x9187, 0xBE4B, 0x9189, 0xBE4C, 0x918B,\n\t0xBE4D, 0x9183, 0xBE4E, 0x92C5, 0xBE4F, 0x92BB, 0xBE50, 0x92B7,\n\t0xBE51, 0x92EA, 0xBE52, 0x92AC, 0xBE53, 0x92E4, 0xBE54, 0x92C1,\n\t0xBE55, 0x92B3, 0xBE56, 0x92BC, 0xBE57, 0x92D2, 0xBE58, 0x92C7,\n\t0xBE59, 0x92F0, 0xBE5A, 0x92B2, 0xBE5B, 0x95AD, 0xBE5C, 0x95B1,\n\t0xBE5D, 0x9704, 0xBE5E, 0x9706, 0xBE5F, 0x9707, 0xBE60, 0x9709,\n\t0xBE61, 0x9760, 0xBE62, 0x978D, 0xBE63, 0x978B, 0xBE64, 0x978F,\n\t0xBE65, 0x9821, 0xBE66, 0x982B, 0xBE67, 0x981C, 0xBE68, 0x98B3,\n\t0xBE69, 0x990A, 0xBE6A, 0x9913, 0xBE6B, 0x9912, 0xBE6C, 0x9918,\n\t0xBE6D, 0x99DD, 0xBE6E, 0x99D0, 0xBE6F, 0x99DF, 0xBE70, 0x99DB,\n\t0xBE71, 0x99D1, 0xBE72, 0x99D5, 0xBE73, 0x99D2, 0xBE74, 0x99D9,\n\t0xBE75, 0x9AB7, 0xBE76, 0x9AEE, 0xBE77, 0x9AEF, 0xBE78, 0x9B27,\n\t0xBE79, 0x9B45, 0xBE7A, 0x9B44, 0xBE7B, 0x9B77, 0xBE7C, 0x9B6F,\n\t0xBE7D, 0x9D06, 0xBE7E, 0x9D09, 0xBEA1, 0x9D03, 0xBEA2, 0x9EA9,\n\t0xBEA3, 0x9EBE, 0xBEA4, 0x9ECE, 0xBEA5, 0x58A8, 0xBEA6, 0x9F52,\n\t0xBEA7, 0x5112, 0xBEA8, 0x5118, 0xBEA9, 0x5114, 0xBEAA, 0x5110,\n\t0xBEAB, 0x5115, 0xBEAC, 0x5180, 0xBEAD, 0x51AA, 0xBEAE, 0x51DD,\n\t0xBEAF, 0x5291, 0xBEB0, 0x5293, 0xBEB1, 0x52F3, 0xBEB2, 0x5659,\n\t0xBEB3, 0x566B, 0xBEB4, 0x5679, 0xBEB5, 0x5669, 0xBEB6, 0x5664,\n\t0xBEB7, 0x5678, 0xBEB8, 0x566A, 0xBEB9, 0x5668, 0xBEBA, 0x5665,\n\t0xBEBB, 0x5671, 0xBEBC, 0x566F, 0xBEBD, 0x566C, 0xBEBE, 0x5662,\n\t0xBEBF, 0x5676, 0xBEC0, 0x58C1, 0xBEC1, 0x58BE, 0xBEC2, 0x58C7,\n\t0xBEC3, 0x58C5, 0xBEC4, 0x596E, 0xBEC5, 0x5B1D, 0xBEC6, 0x5B34,\n\t0xBEC7, 0x5B78, 0xBEC8, 0x5BF0, 0xBEC9, 0x5C0E, 0xBECA, 0x5F4A,\n\t0xBECB, 0x61B2, 0xBECC, 0x6191, 0xBECD, 0x61A9, 0xBECE, 0x618A,\n\t0xBECF, 0x61CD, 0xBED0, 0x61B6, 0xBED1, 0x61BE, 0xBED2, 0x61CA,\n\t0xBED3, 0x61C8, 0xBED4, 0x6230, 0xBED5, 0x64C5, 0xBED6, 0x64C1,\n\t0xBED7, 0x64CB, 0xBED8, 0x64BB, 0xBED9, 0x64BC, 0xBEDA, 0x64DA,\n\t0xBEDB, 0x64C4, 0xBEDC, 0x64C7, 0xBEDD, 0x64C2, 0xBEDE, 0x64CD,\n\t0xBEDF, 0x64BF, 0xBEE0, 0x64D2, 0xBEE1, 0x64D4, 0xBEE2, 0x64BE,\n\t0xBEE3, 0x6574, 0xBEE4, 0x66C6, 0xBEE5, 0x66C9, 0xBEE6, 0x66B9,\n\t0xBEE7, 0x66C4, 0xBEE8, 0x66C7, 0xBEE9, 0x66B8, 0xBEEA, 0x6A3D,\n\t0xBEEB, 0x6A38, 0xBEEC, 0x6A3A, 0xBEED, 0x6A59, 0xBEEE, 0x6A6B,\n\t0xBEEF, 0x6A58, 0xBEF0, 0x6A39, 0xBEF1, 0x6A44, 0xBEF2, 0x6A62,\n\t0xBEF3, 0x6A61, 0xBEF4, 0x6A4B, 0xBEF5, 0x6A47, 0xBEF6, 0x6A35,\n\t0xBEF7, 0x6A5F, 0xBEF8, 0x6A48, 0xBEF9, 0x6B59, 0xBEFA, 0x6B77,\n\t0xBEFB, 0x6C05, 0xBEFC, 0x6FC2, 0xBEFD, 0x6FB1, 0xBEFE, 0x6FA1,\n\t0xBF40, 0x6FC3, 0xBF41, 0x6FA4, 0xBF42, 0x6FC1, 0xBF43, 0x6FA7,\n\t0xBF44, 0x6FB3, 0xBF45, 0x6FC0, 0xBF46, 0x6FB9, 0xBF47, 0x6FB6,\n\t0xBF48, 0x6FA6, 0xBF49, 0x6FA0, 0xBF4A, 0x6FB4, 0xBF4B, 0x71BE,\n\t0xBF4C, 0x71C9, 0xBF4D, 0x71D0, 0xBF4E, 0x71D2, 0xBF4F, 0x71C8,\n\t0xBF50, 0x71D5, 0xBF51, 0x71B9, 0xBF52, 0x71CE, 0xBF53, 0x71D9,\n\t0xBF54, 0x71DC, 0xBF55, 0x71C3, 0xBF56, 0x71C4, 0xBF57, 0x7368,\n\t0xBF58, 0x749C, 0xBF59, 0x74A3, 0xBF5A, 0x7498, 0xBF5B, 0x749F,\n\t0xBF5C, 0x749E, 0xBF5D, 0x74E2, 0xBF5E, 0x750C, 0xBF5F, 0x750D,\n\t0xBF60, 0x7634, 0xBF61, 0x7638, 0xBF62, 0x763A, 0xBF63, 0x76E7,\n\t0xBF64, 0x76E5, 0xBF65, 0x77A0, 0xBF66, 0x779E, 0xBF67, 0x779F,\n\t0xBF68, 0x77A5, 0xBF69, 0x78E8, 0xBF6A, 0x78DA, 0xBF6B, 0x78EC,\n\t0xBF6C, 0x78E7, 0xBF6D, 0x79A6, 0xBF6E, 0x7A4D, 0xBF6F, 0x7A4E,\n\t0xBF70, 0x7A46, 0xBF71, 0x7A4C, 0xBF72, 0x7A4B, 0xBF73, 0x7ABA,\n\t0xBF74, 0x7BD9, 0xBF75, 0x7C11, 0xBF76, 0x7BC9, 0xBF77, 0x7BE4,\n\t0xBF78, 0x7BDB, 0xBF79, 0x7BE1, 0xBF7A, 0x7BE9, 0xBF7B, 0x7BE6,\n\t0xBF7C, 0x7CD5, 0xBF7D, 0x7CD6, 0xBF7E, 0x7E0A, 0xBFA1, 0x7E11,\n\t0xBFA2, 0x7E08, 0xBFA3, 0x7E1B, 0xBFA4, 0x7E23, 0xBFA5, 0x7E1E,\n\t0xBFA6, 0x7E1D, 0xBFA7, 0x7E09, 0xBFA8, 0x7E10, 0xBFA9, 0x7F79,\n\t0xBFAA, 0x7FB2, 0xBFAB, 0x7FF0, 0xBFAC, 0x7FF1, 0xBFAD, 0x7FEE,\n\t0xBFAE, 0x8028, 0xBFAF, 0x81B3, 0xBFB0, 0x81A9, 0xBFB1, 0x81A8,\n\t0xBFB2, 0x81FB, 0xBFB3, 0x8208, 0xBFB4, 0x8258, 0xBFB5, 0x8259,\n\t0xBFB6, 0x854A, 0xBFB7, 0x8559, 0xBFB8, 0x8548, 0xBFB9, 0x8568,\n\t0xBFBA, 0x8569, 0xBFBB, 0x8543, 0xBFBC, 0x8549, 0xBFBD, 0x856D,\n\t0xBFBE, 0x856A, 0xBFBF, 0x855E, 0xBFC0, 0x8783, 0xBFC1, 0x879F,\n\t0xBFC2, 0x879E, 0xBFC3, 0x87A2, 0xBFC4, 0x878D, 0xBFC5, 0x8861,\n\t0xBFC6, 0x892A, 0xBFC7, 0x8932, 0xBFC8, 0x8925, 0xBFC9, 0x892B,\n\t0xBFCA, 0x8921, 0xBFCB, 0x89AA, 0xBFCC, 0x89A6, 0xBFCD, 0x8AE6,\n\t0xBFCE, 0x8AFA, 0xBFCF, 0x8AEB, 0xBFD0, 0x8AF1, 0xBFD1, 0x8B00,\n\t0xBFD2, 0x8ADC, 0xBFD3, 0x8AE7, 0xBFD4, 0x8AEE, 0xBFD5, 0x8AFE,\n\t0xBFD6, 0x8B01, 0xBFD7, 0x8B02, 0xBFD8, 0x8AF7, 0xBFD9, 0x8AED,\n\t0xBFDA, 0x8AF3, 0xBFDB, 0x8AF6, 0xBFDC, 0x8AFC, 0xBFDD, 0x8C6B,\n\t0xBFDE, 0x8C6D, 0xBFDF, 0x8C93, 0xBFE0, 0x8CF4, 0xBFE1, 0x8E44,\n\t0xBFE2, 0x8E31, 0xBFE3, 0x8E34, 0xBFE4, 0x8E42, 0xBFE5, 0x8E39,\n\t0xBFE6, 0x8E35, 0xBFE7, 0x8F3B, 0xBFE8, 0x8F2F, 0xBFE9, 0x8F38,\n\t0xBFEA, 0x8F33, 0xBFEB, 0x8FA8, 0xBFEC, 0x8FA6, 0xBFED, 0x9075,\n\t0xBFEE, 0x9074, 0xBFEF, 0x9078, 0xBFF0, 0x9072, 0xBFF1, 0x907C,\n\t0xBFF2, 0x907A, 0xBFF3, 0x9134, 0xBFF4, 0x9192, 0xBFF5, 0x9320,\n\t0xBFF6, 0x9336, 0xBFF7, 0x92F8, 0xBFF8, 0x9333, 0xBFF9, 0x932F,\n\t0xBFFA, 0x9322, 0xBFFB, 0x92FC, 0xBFFC, 0x932B, 0xBFFD, 0x9304,\n\t0xBFFE, 0x931A, 0xC040, 0x9310, 0xC041, 0x9326, 0xC042, 0x9321,\n\t0xC043, 0x9315, 0xC044, 0x932E, 0xC045, 0x9319, 0xC046, 0x95BB,\n\t0xC047, 0x96A7, 0xC048, 0x96A8, 0xC049, 0x96AA, 0xC04A, 0x96D5,\n\t0xC04B, 0x970E, 0xC04C, 0x9711, 0xC04D, 0x9716, 0xC04E, 0x970D,\n\t0xC04F, 0x9713, 0xC050, 0x970F, 0xC051, 0x975B, 0xC052, 0x975C,\n\t0xC053, 0x9766, 0xC054, 0x9798, 0xC055, 0x9830, 0xC056, 0x9838,\n\t0xC057, 0x983B, 0xC058, 0x9837, 0xC059, 0x982D, 0xC05A, 0x9839,\n\t0xC05B, 0x9824, 0xC05C, 0x9910, 0xC05D, 0x9928, 0xC05E, 0x991E,\n\t0xC05F, 0x991B, 0xC060, 0x9921, 0xC061, 0x991A, 0xC062, 0x99ED,\n\t0xC063, 0x99E2, 0xC064, 0x99F1, 0xC065, 0x9AB8, 0xC066, 0x9ABC,\n\t0xC067, 0x9AFB, 0xC068, 0x9AED, 0xC069, 0x9B28, 0xC06A, 0x9B91,\n\t0xC06B, 0x9D15, 0xC06C, 0x9D23, 0xC06D, 0x9D26, 0xC06E, 0x9D28,\n\t0xC06F, 0x9D12, 0xC070, 0x9D1B, 0xC071, 0x9ED8, 0xC072, 0x9ED4,\n\t0xC073, 0x9F8D, 0xC074, 0x9F9C, 0xC075, 0x512A, 0xC076, 0x511F,\n\t0xC077, 0x5121, 0xC078, 0x5132, 0xC079, 0x52F5, 0xC07A, 0x568E,\n\t0xC07B, 0x5680, 0xC07C, 0x5690, 0xC07D, 0x5685, 0xC07E, 0x5687,\n\t0xC0A1, 0x568F, 0xC0A2, 0x58D5, 0xC0A3, 0x58D3, 0xC0A4, 0x58D1,\n\t0xC0A5, 0x58CE, 0xC0A6, 0x5B30, 0xC0A7, 0x5B2A, 0xC0A8, 0x5B24,\n\t0xC0A9, 0x5B7A, 0xC0AA, 0x5C37, 0xC0AB, 0x5C68, 0xC0AC, 0x5DBC,\n\t0xC0AD, 0x5DBA, 0xC0AE, 0x5DBD, 0xC0AF, 0x5DB8, 0xC0B0, 0x5E6B,\n\t0xC0B1, 0x5F4C, 0xC0B2, 0x5FBD, 0xC0B3, 0x61C9, 0xC0B4, 0x61C2,\n\t0xC0B5, 0x61C7, 0xC0B6, 0x61E6, 0xC0B7, 0x61CB, 0xC0B8, 0x6232,\n\t0xC0B9, 0x6234, 0xC0BA, 0x64CE, 0xC0BB, 0x64CA, 0xC0BC, 0x64D8,\n\t0xC0BD, 0x64E0, 0xC0BE, 0x64F0, 0xC0BF, 0x64E6, 0xC0C0, 0x64EC,\n\t0xC0C1, 0x64F1, 0xC0C2, 0x64E2, 0xC0C3, 0x64ED, 0xC0C4, 0x6582,\n\t0xC0C5, 0x6583, 0xC0C6, 0x66D9, 0xC0C7, 0x66D6, 0xC0C8, 0x6A80,\n\t0xC0C9, 0x6A94, 0xC0CA, 0x6A84, 0xC0CB, 0x6AA2, 0xC0CC, 0x6A9C,\n\t0xC0CD, 0x6ADB, 0xC0CE, 0x6AA3, 0xC0CF, 0x6A7E, 0xC0D0, 0x6A97,\n\t0xC0D1, 0x6A90, 0xC0D2, 0x6AA0, 0xC0D3, 0x6B5C, 0xC0D4, 0x6BAE,\n\t0xC0D5, 0x6BDA, 0xC0D6, 0x6C08, 0xC0D7, 0x6FD8, 0xC0D8, 0x6FF1,\n\t0xC0D9, 0x6FDF, 0xC0DA, 0x6FE0, 0xC0DB, 0x6FDB, 0xC0DC, 0x6FE4,\n\t0xC0DD, 0x6FEB, 0xC0DE, 0x6FEF, 0xC0DF, 0x6F80, 0xC0E0, 0x6FEC,\n\t0xC0E1, 0x6FE1, 0xC0E2, 0x6FE9, 0xC0E3, 0x6FD5, 0xC0E4, 0x6FEE,\n\t0xC0E5, 0x6FF0, 0xC0E6, 0x71E7, 0xC0E7, 0x71DF, 0xC0E8, 0x71EE,\n\t0xC0E9, 0x71E6, 0xC0EA, 0x71E5, 0xC0EB, 0x71ED, 0xC0EC, 0x71EC,\n\t0xC0ED, 0x71F4, 0xC0EE, 0x71E0, 0xC0EF, 0x7235, 0xC0F0, 0x7246,\n\t0xC0F1, 0x7370, 0xC0F2, 0x7372, 0xC0F3, 0x74A9, 0xC0F4, 0x74B0,\n\t0xC0F5, 0x74A6, 0xC0F6, 0x74A8, 0xC0F7, 0x7646, 0xC0F8, 0x7642,\n\t0xC0F9, 0x764C, 0xC0FA, 0x76EA, 0xC0FB, 0x77B3, 0xC0FC, 0x77AA,\n\t0xC0FD, 0x77B0, 0xC0FE, 0x77AC, 0xC140, 0x77A7, 0xC141, 0x77AD,\n\t0xC142, 0x77EF, 0xC143, 0x78F7, 0xC144, 0x78FA, 0xC145, 0x78F4,\n\t0xC146, 0x78EF, 0xC147, 0x7901, 0xC148, 0x79A7, 0xC149, 0x79AA,\n\t0xC14A, 0x7A57, 0xC14B, 0x7ABF, 0xC14C, 0x7C07, 0xC14D, 0x7C0D,\n\t0xC14E, 0x7BFE, 0xC14F, 0x7BF7, 0xC150, 0x7C0C, 0xC151, 0x7BE0,\n\t0xC152, 0x7CE0, 0xC153, 0x7CDC, 0xC154, 0x7CDE, 0xC155, 0x7CE2,\n\t0xC156, 0x7CDF, 0xC157, 0x7CD9, 0xC158, 0x7CDD, 0xC159, 0x7E2E,\n\t0xC15A, 0x7E3E, 0xC15B, 0x7E46, 0xC15C, 0x7E37, 0xC15D, 0x7E32,\n\t0xC15E, 0x7E43, 0xC15F, 0x7E2B, 0xC160, 0x7E3D, 0xC161, 0x7E31,\n\t0xC162, 0x7E45, 0xC163, 0x7E41, 0xC164, 0x7E34, 0xC165, 0x7E39,\n\t0xC166, 0x7E48, 0xC167, 0x7E35, 0xC168, 0x7E3F, 0xC169, 0x7E2F,\n\t0xC16A, 0x7F44, 0xC16B, 0x7FF3, 0xC16C, 0x7FFC, 0xC16D, 0x8071,\n\t0xC16E, 0x8072, 0xC16F, 0x8070, 0xC170, 0x806F, 0xC171, 0x8073,\n\t0xC172, 0x81C6, 0xC173, 0x81C3, 0xC174, 0x81BA, 0xC175, 0x81C2,\n\t0xC176, 0x81C0, 0xC177, 0x81BF, 0xC178, 0x81BD, 0xC179, 0x81C9,\n\t0xC17A, 0x81BE, 0xC17B, 0x81E8, 0xC17C, 0x8209, 0xC17D, 0x8271,\n\t0xC17E, 0x85AA, 0xC1A1, 0x8584, 0xC1A2, 0x857E, 0xC1A3, 0x859C,\n\t0xC1A4, 0x8591, 0xC1A5, 0x8594, 0xC1A6, 0x85AF, 0xC1A7, 0x859B,\n\t0xC1A8, 0x8587, 0xC1A9, 0x85A8, 0xC1AA, 0x858A, 0xC1AB, 0x8667,\n\t0xC1AC, 0x87C0, 0xC1AD, 0x87D1, 0xC1AE, 0x87B3, 0xC1AF, 0x87D2,\n\t0xC1B0, 0x87C6, 0xC1B1, 0x87AB, 0xC1B2, 0x87BB, 0xC1B3, 0x87BA,\n\t0xC1B4, 0x87C8, 0xC1B5, 0x87CB, 0xC1B6, 0x893B, 0xC1B7, 0x8936,\n\t0xC1B8, 0x8944, 0xC1B9, 0x8938, 0xC1BA, 0x893D, 0xC1BB, 0x89AC,\n\t0xC1BC, 0x8B0E, 0xC1BD, 0x8B17, 0xC1BE, 0x8B19, 0xC1BF, 0x8B1B,\n\t0xC1C0, 0x8B0A, 0xC1C1, 0x8B20, 0xC1C2, 0x8B1D, 0xC1C3, 0x8B04,\n\t0xC1C4, 0x8B10, 0xC1C5, 0x8C41, 0xC1C6, 0x8C3F, 0xC1C7, 0x8C73,\n\t0xC1C8, 0x8CFA, 0xC1C9, 0x8CFD, 0xC1CA, 0x8CFC, 0xC1CB, 0x8CF8,\n\t0xC1CC, 0x8CFB, 0xC1CD, 0x8DA8, 0xC1CE, 0x8E49, 0xC1CF, 0x8E4B,\n\t0xC1D0, 0x8E48, 0xC1D1, 0x8E4A, 0xC1D2, 0x8F44, 0xC1D3, 0x8F3E,\n\t0xC1D4, 0x8F42, 0xC1D5, 0x8F45, 0xC1D6, 0x8F3F, 0xC1D7, 0x907F,\n\t0xC1D8, 0x907D, 0xC1D9, 0x9084, 0xC1DA, 0x9081, 0xC1DB, 0x9082,\n\t0xC1DC, 0x9080, 0xC1DD, 0x9139, 0xC1DE, 0x91A3, 0xC1DF, 0x919E,\n\t0xC1E0, 0x919C, 0xC1E1, 0x934D, 0xC1E2, 0x9382, 0xC1E3, 0x9328,\n\t0xC1E4, 0x9375, 0xC1E5, 0x934A, 0xC1E6, 0x9365, 0xC1E7, 0x934B,\n\t0xC1E8, 0x9318, 0xC1E9, 0x937E, 0xC1EA, 0x936C, 0xC1EB, 0x935B,\n\t0xC1EC, 0x9370, 0xC1ED, 0x935A, 0xC1EE, 0x9354, 0xC1EF, 0x95CA,\n\t0xC1F0, 0x95CB, 0xC1F1, 0x95CC, 0xC1F2, 0x95C8, 0xC1F3, 0x95C6,\n\t0xC1F4, 0x96B1, 0xC1F5, 0x96B8, 0xC1F6, 0x96D6, 0xC1F7, 0x971C,\n\t0xC1F8, 0x971E, 0xC1F9, 0x97A0, 0xC1FA, 0x97D3, 0xC1FB, 0x9846,\n\t0xC1FC, 0x98B6, 0xC1FD, 0x9935, 0xC1FE, 0x9A01, 0xC240, 0x99FF,\n\t0xC241, 0x9BAE, 0xC242, 0x9BAB, 0xC243, 0x9BAA, 0xC244, 0x9BAD,\n\t0xC245, 0x9D3B, 0xC246, 0x9D3F, 0xC247, 0x9E8B, 0xC248, 0x9ECF,\n\t0xC249, 0x9EDE, 0xC24A, 0x9EDC, 0xC24B, 0x9EDD, 0xC24C, 0x9EDB,\n\t0xC24D, 0x9F3E, 0xC24E, 0x9F4B, 0xC24F, 0x53E2, 0xC250, 0x5695,\n\t0xC251, 0x56AE, 0xC252, 0x58D9, 0xC253, 0x58D8, 0xC254, 0x5B38,\n\t0xC255, 0x5F5D, 0xC256, 0x61E3, 0xC257, 0x6233, 0xC258, 0x64F4,\n\t0xC259, 0x64F2, 0xC25A, 0x64FE, 0xC25B, 0x6506, 0xC25C, 0x64FA,\n\t0xC25D, 0x64FB, 0xC25E, 0x64F7, 0xC25F, 0x65B7, 0xC260, 0x66DC,\n\t0xC261, 0x6726, 0xC262, 0x6AB3, 0xC263, 0x6AAC, 0xC264, 0x6AC3,\n\t0xC265, 0x6ABB, 0xC266, 0x6AB8, 0xC267, 0x6AC2, 0xC268, 0x6AAE,\n\t0xC269, 0x6AAF, 0xC26A, 0x6B5F, 0xC26B, 0x6B78, 0xC26C, 0x6BAF,\n\t0xC26D, 0x7009, 0xC26E, 0x700B, 0xC26F, 0x6FFE, 0xC270, 0x7006,\n\t0xC271, 0x6FFA, 0xC272, 0x7011, 0xC273, 0x700F, 0xC274, 0x71FB,\n\t0xC275, 0x71FC, 0xC276, 0x71FE, 0xC277, 0x71F8, 0xC278, 0x7377,\n\t0xC279, 0x7375, 0xC27A, 0x74A7, 0xC27B, 0x74BF, 0xC27C, 0x7515,\n\t0xC27D, 0x7656, 0xC27E, 0x7658, 0xC2A1, 0x7652, 0xC2A2, 0x77BD,\n\t0xC2A3, 0x77BF, 0xC2A4, 0x77BB, 0xC2A5, 0x77BC, 0xC2A6, 0x790E,\n\t0xC2A7, 0x79AE, 0xC2A8, 0x7A61, 0xC2A9, 0x7A62, 0xC2AA, 0x7A60,\n\t0xC2AB, 0x7AC4, 0xC2AC, 0x7AC5, 0xC2AD, 0x7C2B, 0xC2AE, 0x7C27,\n\t0xC2AF, 0x7C2A, 0xC2B0, 0x7C1E, 0xC2B1, 0x7C23, 0xC2B2, 0x7C21,\n\t0xC2B3, 0x7CE7, 0xC2B4, 0x7E54, 0xC2B5, 0x7E55, 0xC2B6, 0x7E5E,\n\t0xC2B7, 0x7E5A, 0xC2B8, 0x7E61, 0xC2B9, 0x7E52, 0xC2BA, 0x7E59,\n\t0xC2BB, 0x7F48, 0xC2BC, 0x7FF9, 0xC2BD, 0x7FFB, 0xC2BE, 0x8077,\n\t0xC2BF, 0x8076, 0xC2C0, 0x81CD, 0xC2C1, 0x81CF, 0xC2C2, 0x820A,\n\t0xC2C3, 0x85CF, 0xC2C4, 0x85A9, 0xC2C5, 0x85CD, 0xC2C6, 0x85D0,\n\t0xC2C7, 0x85C9, 0xC2C8, 0x85B0, 0xC2C9, 0x85BA, 0xC2CA, 0x85B9,\n\t0xC2CB, 0x85A6, 0xC2CC, 0x87EF, 0xC2CD, 0x87EC, 0xC2CE, 0x87F2,\n\t0xC2CF, 0x87E0, 0xC2D0, 0x8986, 0xC2D1, 0x89B2, 0xC2D2, 0x89F4,\n\t0xC2D3, 0x8B28, 0xC2D4, 0x8B39, 0xC2D5, 0x8B2C, 0xC2D6, 0x8B2B,\n\t0xC2D7, 0x8C50, 0xC2D8, 0x8D05, 0xC2D9, 0x8E59, 0xC2DA, 0x8E63,\n\t0xC2DB, 0x8E66, 0xC2DC, 0x8E64, 0xC2DD, 0x8E5F, 0xC2DE, 0x8E55,\n\t0xC2DF, 0x8EC0, 0xC2E0, 0x8F49, 0xC2E1, 0x8F4D, 0xC2E2, 0x9087,\n\t0xC2E3, 0x9083, 0xC2E4, 0x9088, 0xC2E5, 0x91AB, 0xC2E6, 0x91AC,\n\t0xC2E7, 0x91D0, 0xC2E8, 0x9394, 0xC2E9, 0x938A, 0xC2EA, 0x9396,\n\t0xC2EB, 0x93A2, 0xC2EC, 0x93B3, 0xC2ED, 0x93AE, 0xC2EE, 0x93AC,\n\t0xC2EF, 0x93B0, 0xC2F0, 0x9398, 0xC2F1, 0x939A, 0xC2F2, 0x9397,\n\t0xC2F3, 0x95D4, 0xC2F4, 0x95D6, 0xC2F5, 0x95D0, 0xC2F6, 0x95D5,\n\t0xC2F7, 0x96E2, 0xC2F8, 0x96DC, 0xC2F9, 0x96D9, 0xC2FA, 0x96DB,\n\t0xC2FB, 0x96DE, 0xC2FC, 0x9724, 0xC2FD, 0x97A3, 0xC2FE, 0x97A6,\n\t0xC340, 0x97AD, 0xC341, 0x97F9, 0xC342, 0x984D, 0xC343, 0x984F,\n\t0xC344, 0x984C, 0xC345, 0x984E, 0xC346, 0x9853, 0xC347, 0x98BA,\n\t0xC348, 0x993E, 0xC349, 0x993F, 0xC34A, 0x993D, 0xC34B, 0x992E,\n\t0xC34C, 0x99A5, 0xC34D, 0x9A0E, 0xC34E, 0x9AC1, 0xC34F, 0x9B03,\n\t0xC350, 0x9B06, 0xC351, 0x9B4F, 0xC352, 0x9B4E, 0xC353, 0x9B4D,\n\t0xC354, 0x9BCA, 0xC355, 0x9BC9, 0xC356, 0x9BFD, 0xC357, 0x9BC8,\n\t0xC358, 0x9BC0, 0xC359, 0x9D51, 0xC35A, 0x9D5D, 0xC35B, 0x9D60,\n\t0xC35C, 0x9EE0, 0xC35D, 0x9F15, 0xC35E, 0x9F2C, 0xC35F, 0x5133,\n\t0xC360, 0x56A5, 0xC361, 0x58DE, 0xC362, 0x58DF, 0xC363, 0x58E2,\n\t0xC364, 0x5BF5, 0xC365, 0x9F90, 0xC366, 0x5EEC, 0xC367, 0x61F2,\n\t0xC368, 0x61F7, 0xC369, 0x61F6, 0xC36A, 0x61F5, 0xC36B, 0x6500,\n\t0xC36C, 0x650F, 0xC36D, 0x66E0, 0xC36E, 0x66DD, 0xC36F, 0x6AE5,\n\t0xC370, 0x6ADD, 0xC371, 0x6ADA, 0xC372, 0x6AD3, 0xC373, 0x701B,\n\t0xC374, 0x701F, 0xC375, 0x7028, 0xC376, 0x701A, 0xC377, 0x701D,\n\t0xC378, 0x7015, 0xC379, 0x7018, 0xC37A, 0x7206, 0xC37B, 0x720D,\n\t0xC37C, 0x7258, 0xC37D, 0x72A2, 0xC37E, 0x7378, 0xC3A1, 0x737A,\n\t0xC3A2, 0x74BD, 0xC3A3, 0x74CA, 0xC3A4, 0x74E3, 0xC3A5, 0x7587,\n\t0xC3A6, 0x7586, 0xC3A7, 0x765F, 0xC3A8, 0x7661, 0xC3A9, 0x77C7,\n\t0xC3AA, 0x7919, 0xC3AB, 0x79B1, 0xC3AC, 0x7A6B, 0xC3AD, 0x7A69,\n\t0xC3AE, 0x7C3E, 0xC3AF, 0x7C3F, 0xC3B0, 0x7C38, 0xC3B1, 0x7C3D,\n\t0xC3B2, 0x7C37, 0xC3B3, 0x7C40, 0xC3B4, 0x7E6B, 0xC3B5, 0x7E6D,\n\t0xC3B6, 0x7E79, 0xC3B7, 0x7E69, 0xC3B8, 0x7E6A, 0xC3B9, 0x7F85,\n\t0xC3BA, 0x7E73, 0xC3BB, 0x7FB6, 0xC3BC, 0x7FB9, 0xC3BD, 0x7FB8,\n\t0xC3BE, 0x81D8, 0xC3BF, 0x85E9, 0xC3C0, 0x85DD, 0xC3C1, 0x85EA,\n\t0xC3C2, 0x85D5, 0xC3C3, 0x85E4, 0xC3C4, 0x85E5, 0xC3C5, 0x85F7,\n\t0xC3C6, 0x87FB, 0xC3C7, 0x8805, 0xC3C8, 0x880D, 0xC3C9, 0x87F9,\n\t0xC3CA, 0x87FE, 0xC3CB, 0x8960, 0xC3CC, 0x895F, 0xC3CD, 0x8956,\n\t0xC3CE, 0x895E, 0xC3CF, 0x8B41, 0xC3D0, 0x8B5C, 0xC3D1, 0x8B58,\n\t0xC3D2, 0x8B49, 0xC3D3, 0x8B5A, 0xC3D4, 0x8B4E, 0xC3D5, 0x8B4F,\n\t0xC3D6, 0x8B46, 0xC3D7, 0x8B59, 0xC3D8, 0x8D08, 0xC3D9, 0x8D0A,\n\t0xC3DA, 0x8E7C, 0xC3DB, 0x8E72, 0xC3DC, 0x8E87, 0xC3DD, 0x8E76,\n\t0xC3DE, 0x8E6C, 0xC3DF, 0x8E7A, 0xC3E0, 0x8E74, 0xC3E1, 0x8F54,\n\t0xC3E2, 0x8F4E, 0xC3E3, 0x8FAD, 0xC3E4, 0x908A, 0xC3E5, 0x908B,\n\t0xC3E6, 0x91B1, 0xC3E7, 0x91AE, 0xC3E8, 0x93E1, 0xC3E9, 0x93D1,\n\t0xC3EA, 0x93DF, 0xC3EB, 0x93C3, 0xC3EC, 0x93C8, 0xC3ED, 0x93DC,\n\t0xC3EE, 0x93DD, 0xC3EF, 0x93D6, 0xC3F0, 0x93E2, 0xC3F1, 0x93CD,\n\t0xC3F2, 0x93D8, 0xC3F3, 0x93E4, 0xC3F4, 0x93D7, 0xC3F5, 0x93E8,\n\t0xC3F6, 0x95DC, 0xC3F7, 0x96B4, 0xC3F8, 0x96E3, 0xC3F9, 0x972A,\n\t0xC3FA, 0x9727, 0xC3FB, 0x9761, 0xC3FC, 0x97DC, 0xC3FD, 0x97FB,\n\t0xC3FE, 0x985E, 0xC440, 0x9858, 0xC441, 0x985B, 0xC442, 0x98BC,\n\t0xC443, 0x9945, 0xC444, 0x9949, 0xC445, 0x9A16, 0xC446, 0x9A19,\n\t0xC447, 0x9B0D, 0xC448, 0x9BE8, 0xC449, 0x9BE7, 0xC44A, 0x9BD6,\n\t0xC44B, 0x9BDB, 0xC44C, 0x9D89, 0xC44D, 0x9D61, 0xC44E, 0x9D72,\n\t0xC44F, 0x9D6A, 0xC450, 0x9D6C, 0xC451, 0x9E92, 0xC452, 0x9E97,\n\t0xC453, 0x9E93, 0xC454, 0x9EB4, 0xC455, 0x52F8, 0xC456, 0x56A8,\n\t0xC457, 0x56B7, 0xC458, 0x56B6, 0xC459, 0x56B4, 0xC45A, 0x56BC,\n\t0xC45B, 0x58E4, 0xC45C, 0x5B40, 0xC45D, 0x5B43, 0xC45E, 0x5B7D,\n\t0xC45F, 0x5BF6, 0xC460, 0x5DC9, 0xC461, 0x61F8, 0xC462, 0x61FA,\n\t0xC463, 0x6518, 0xC464, 0x6514, 0xC465, 0x6519, 0xC466, 0x66E6,\n\t0xC467, 0x6727, 0xC468, 0x6AEC, 0xC469, 0x703E, 0xC46A, 0x7030,\n\t0xC46B, 0x7032, 0xC46C, 0x7210, 0xC46D, 0x737B, 0xC46E, 0x74CF,\n\t0xC46F, 0x7662, 0xC470, 0x7665, 0xC471, 0x7926, 0xC472, 0x792A,\n\t0xC473, 0x792C, 0xC474, 0x792B, 0xC475, 0x7AC7, 0xC476, 0x7AF6,\n\t0xC477, 0x7C4C, 0xC478, 0x7C43, 0xC479, 0x7C4D, 0xC47A, 0x7CEF,\n\t0xC47B, 0x7CF0, 0xC47C, 0x8FAE, 0xC47D, 0x7E7D, 0xC47E, 0x7E7C,\n\t0xC4A1, 0x7E82, 0xC4A2, 0x7F4C, 0xC4A3, 0x8000, 0xC4A4, 0x81DA,\n\t0xC4A5, 0x8266, 0xC4A6, 0x85FB, 0xC4A7, 0x85F9, 0xC4A8, 0x8611,\n\t0xC4A9, 0x85FA, 0xC4AA, 0x8606, 0xC4AB, 0x860B, 0xC4AC, 0x8607,\n\t0xC4AD, 0x860A, 0xC4AE, 0x8814, 0xC4AF, 0x8815, 0xC4B0, 0x8964,\n\t0xC4B1, 0x89BA, 0xC4B2, 0x89F8, 0xC4B3, 0x8B70, 0xC4B4, 0x8B6C,\n\t0xC4B5, 0x8B66, 0xC4B6, 0x8B6F, 0xC4B7, 0x8B5F, 0xC4B8, 0x8B6B,\n\t0xC4B9, 0x8D0F, 0xC4BA, 0x8D0D, 0xC4BB, 0x8E89, 0xC4BC, 0x8E81,\n\t0xC4BD, 0x8E85, 0xC4BE, 0x8E82, 0xC4BF, 0x91B4, 0xC4C0, 0x91CB,\n\t0xC4C1, 0x9418, 0xC4C2, 0x9403, 0xC4C3, 0x93FD, 0xC4C4, 0x95E1,\n\t0xC4C5, 0x9730, 0xC4C6, 0x98C4, 0xC4C7, 0x9952, 0xC4C8, 0x9951,\n\t0xC4C9, 0x99A8, 0xC4CA, 0x9A2B, 0xC4CB, 0x9A30, 0xC4CC, 0x9A37,\n\t0xC4CD, 0x9A35, 0xC4CE, 0x9C13, 0xC4CF, 0x9C0D, 0xC4D0, 0x9E79,\n\t0xC4D1, 0x9EB5, 0xC4D2, 0x9EE8, 0xC4D3, 0x9F2F, 0xC4D4, 0x9F5F,\n\t0xC4D5, 0x9F63, 0xC4D6, 0x9F61, 0xC4D7, 0x5137, 0xC4D8, 0x5138,\n\t0xC4D9, 0x56C1, 0xC4DA, 0x56C0, 0xC4DB, 0x56C2, 0xC4DC, 0x5914,\n\t0xC4DD, 0x5C6C, 0xC4DE, 0x5DCD, 0xC4DF, 0x61FC, 0xC4E0, 0x61FE,\n\t0xC4E1, 0x651D, 0xC4E2, 0x651C, 0xC4E3, 0x6595, 0xC4E4, 0x66E9,\n\t0xC4E5, 0x6AFB, 0xC4E6, 0x6B04, 0xC4E7, 0x6AFA, 0xC4E8, 0x6BB2,\n\t0xC4E9, 0x704C, 0xC4EA, 0x721B, 0xC4EB, 0x72A7, 0xC4EC, 0x74D6,\n\t0xC4ED, 0x74D4, 0xC4EE, 0x7669, 0xC4EF, 0x77D3, 0xC4F0, 0x7C50,\n\t0xC4F1, 0x7E8F, 0xC4F2, 0x7E8C, 0xC4F3, 0x7FBC, 0xC4F4, 0x8617,\n\t0xC4F5, 0x862D, 0xC4F6, 0x861A, 0xC4F7, 0x8823, 0xC4F8, 0x8822,\n\t0xC4F9, 0x8821, 0xC4FA, 0x881F, 0xC4FB, 0x896A, 0xC4FC, 0x896C,\n\t0xC4FD, 0x89BD, 0xC4FE, 0x8B74, 0xC540, 0x8B77, 0xC541, 0x8B7D,\n\t0xC542, 0x8D13, 0xC543, 0x8E8A, 0xC544, 0x8E8D, 0xC545, 0x8E8B,\n\t0xC546, 0x8F5F, 0xC547, 0x8FAF, 0xC548, 0x91BA, 0xC549, 0x942E,\n\t0xC54A, 0x9433, 0xC54B, 0x9435, 0xC54C, 0x943A, 0xC54D, 0x9438,\n\t0xC54E, 0x9432, 0xC54F, 0x942B, 0xC550, 0x95E2, 0xC551, 0x9738,\n\t0xC552, 0x9739, 0xC553, 0x9732, 0xC554, 0x97FF, 0xC555, 0x9867,\n\t0xC556, 0x9865, 0xC557, 0x9957, 0xC558, 0x9A45, 0xC559, 0x9A43,\n\t0xC55A, 0x9A40, 0xC55B, 0x9A3E, 0xC55C, 0x9ACF, 0xC55D, 0x9B54,\n\t0xC55E, 0x9B51, 0xC55F, 0x9C2D, 0xC560, 0x9C25, 0xC561, 0x9DAF,\n\t0xC562, 0x9DB4, 0xC563, 0x9DC2, 0xC564, 0x9DB8, 0xC565, 0x9E9D,\n\t0xC566, 0x9EEF, 0xC567, 0x9F19, 0xC568, 0x9F5C, 0xC569, 0x9F66,\n\t0xC56A, 0x9F67, 0xC56B, 0x513C, 0xC56C, 0x513B, 0xC56D, 0x56C8,\n\t0xC56E, 0x56CA, 0xC56F, 0x56C9, 0xC570, 0x5B7F, 0xC571, 0x5DD4,\n\t0xC572, 0x5DD2, 0xC573, 0x5F4E, 0xC574, 0x61FF, 0xC575, 0x6524,\n\t0xC576, 0x6B0A, 0xC577, 0x6B61, 0xC578, 0x7051, 0xC579, 0x7058,\n\t0xC57A, 0x7380, 0xC57B, 0x74E4, 0xC57C, 0x758A, 0xC57D, 0x766E,\n\t0xC57E, 0x766C, 0xC5A1, 0x79B3, 0xC5A2, 0x7C60, 0xC5A3, 0x7C5F,\n\t0xC5A4, 0x807E, 0xC5A5, 0x807D, 0xC5A6, 0x81DF, 0xC5A7, 0x8972,\n\t0xC5A8, 0x896F, 0xC5A9, 0x89FC, 0xC5AA, 0x8B80, 0xC5AB, 0x8D16,\n\t0xC5AC, 0x8D17, 0xC5AD, 0x8E91, 0xC5AE, 0x8E93, 0xC5AF, 0x8F61,\n\t0xC5B0, 0x9148, 0xC5B1, 0x9444, 0xC5B2, 0x9451, 0xC5B3, 0x9452,\n\t0xC5B4, 0x973D, 0xC5B5, 0x973E, 0xC5B6, 0x97C3, 0xC5B7, 0x97C1,\n\t0xC5B8, 0x986B, 0xC5B9, 0x9955, 0xC5BA, 0x9A55, 0xC5BB, 0x9A4D,\n\t0xC5BC, 0x9AD2, 0xC5BD, 0x9B1A, 0xC5BE, 0x9C49, 0xC5BF, 0x9C31,\n\t0xC5C0, 0x9C3E, 0xC5C1, 0x9C3B, 0xC5C2, 0x9DD3, 0xC5C3, 0x9DD7,\n\t0xC5C4, 0x9F34, 0xC5C5, 0x9F6C, 0xC5C6, 0x9F6A, 0xC5C7, 0x9F94,\n\t0xC5C8, 0x56CC, 0xC5C9, 0x5DD6, 0xC5CA, 0x6200, 0xC5CB, 0x6523,\n\t0xC5CC, 0x652B, 0xC5CD, 0x652A, 0xC5CE, 0x66EC, 0xC5CF, 0x6B10,\n\t0xC5D0, 0x74DA, 0xC5D1, 0x7ACA, 0xC5D2, 0x7C64, 0xC5D3, 0x7C63,\n\t0xC5D4, 0x7C65, 0xC5D5, 0x7E93, 0xC5D6, 0x7E96, 0xC5D7, 0x7E94,\n\t0xC5D8, 0x81E2, 0xC5D9, 0x8638, 0xC5DA, 0x863F, 0xC5DB, 0x8831,\n\t0xC5DC, 0x8B8A, 0xC5DD, 0x9090, 0xC5DE, 0x908F, 0xC5DF, 0x9463,\n\t0xC5E0, 0x9460, 0xC5E1, 0x9464, 0xC5E2, 0x9768, 0xC5E3, 0x986F,\n\t0xC5E4, 0x995C, 0xC5E5, 0x9A5A, 0xC5E6, 0x9A5B, 0xC5E7, 0x9A57,\n\t0xC5E8, 0x9AD3, 0xC5E9, 0x9AD4, 0xC5EA, 0x9AD1, 0xC5EB, 0x9C54,\n\t0xC5EC, 0x9C57, 0xC5ED, 0x9C56, 0xC5EE, 0x9DE5, 0xC5EF, 0x9E9F,\n\t0xC5F0, 0x9EF4, 0xC5F1, 0x56D1, 0xC5F2, 0x58E9, 0xC5F3, 0x652C,\n\t0xC5F4, 0x705E, 0xC5F5, 0x7671, 0xC5F6, 0x7672, 0xC5F7, 0x77D7,\n\t0xC5F8, 0x7F50, 0xC5F9, 0x7F88, 0xC5FA, 0x8836, 0xC5FB, 0x8839,\n\t0xC5FC, 0x8862, 0xC5FD, 0x8B93, 0xC5FE, 0x8B92, 0xC640, 0x8B96,\n\t0xC641, 0x8277, 0xC642, 0x8D1B, 0xC643, 0x91C0, 0xC644, 0x946A,\n\t0xC645, 0x9742, 0xC646, 0x9748, 0xC647, 0x9744, 0xC648, 0x97C6,\n\t0xC649, 0x9870, 0xC64A, 0x9A5F, 0xC64B, 0x9B22, 0xC64C, 0x9B58,\n\t0xC64D, 0x9C5F, 0xC64E, 0x9DF9, 0xC64F, 0x9DFA, 0xC650, 0x9E7C,\n\t0xC651, 0x9E7D, 0xC652, 0x9F07, 0xC653, 0x9F77, 0xC654, 0x9F72,\n\t0xC655, 0x5EF3, 0xC656, 0x6B16, 0xC657, 0x7063, 0xC658, 0x7C6C,\n\t0xC659, 0x7C6E, 0xC65A, 0x883B, 0xC65B, 0x89C0, 0xC65C, 0x8EA1,\n\t0xC65D, 0x91C1, 0xC65E, 0x9472, 0xC65F, 0x9470, 0xC660, 0x9871,\n\t0xC661, 0x995E, 0xC662, 0x9AD6, 0xC663, 0x9B23, 0xC664, 0x9ECC,\n\t0xC665, 0x7064, 0xC666, 0x77DA, 0xC667, 0x8B9A, 0xC668, 0x9477,\n\t0xC669, 0x97C9, 0xC66A, 0x9A62, 0xC66B, 0x9A65, 0xC66C, 0x7E9C,\n\t0xC66D, 0x8B9C, 0xC66E, 0x8EAA, 0xC66F, 0x91C5, 0xC670, 0x947D,\n\t0xC671, 0x947E, 0xC672, 0x947C, 0xC673, 0x9C77, 0xC674, 0x9C78,\n\t0xC675, 0x9EF7, 0xC676, 0x8C54, 0xC677, 0x947F, 0xC678, 0x9E1A,\n\t0xC679, 0x7228, 0xC67A, 0x9A6A, 0xC67B, 0x9B31, 0xC67C, 0x9E1B,\n\t0xC67D, 0x9E1E, 0xC67E, 0x7C72, 0xC940, 0x4E42, 0xC941, 0x4E5C,\n\t0xC942, 0x51F5, 0xC943, 0x531A, 0xC944, 0x5382, 0xC945, 0x4E07,\n\t0xC946, 0x4E0C, 0xC947, 0x4E47, 0xC948, 0x4E8D, 0xC949, 0x56D7,\n\t0xC94A, 0xFA0C, 0xC94B, 0x5C6E, 0xC94C, 0x5F73, 0xC94D, 0x4E0F,\n\t0xC94E, 0x5187, 0xC94F, 0x4E0E, 0xC950, 0x4E2E, 0xC951, 0x4E93,\n\t0xC952, 0x4EC2, 0xC953, 0x4EC9, 0xC954, 0x4EC8, 0xC955, 0x5198,\n\t0xC956, 0x52FC, 0xC957, 0x536C, 0xC958, 0x53B9, 0xC959, 0x5720,\n\t0xC95A, 0x5903, 0xC95B, 0x592C, 0xC95C, 0x5C10, 0xC95D, 0x5DFF,\n\t0xC95E, 0x65E1, 0xC95F, 0x6BB3, 0xC960, 0x6BCC, 0xC961, 0x6C14,\n\t0xC962, 0x723F, 0xC963, 0x4E31, 0xC964, 0x4E3C, 0xC965, 0x4EE8,\n\t0xC966, 0x4EDC, 0xC967, 0x4EE9, 0xC968, 0x4EE1, 0xC969, 0x4EDD,\n\t0xC96A, 0x4EDA, 0xC96B, 0x520C, 0xC96C, 0x531C, 0xC96D, 0x534C,\n\t0xC96E, 0x5722, 0xC96F, 0x5723, 0xC970, 0x5917, 0xC971, 0x592F,\n\t0xC972, 0x5B81, 0xC973, 0x5B84, 0xC974, 0x5C12, 0xC975, 0x5C3B,\n\t0xC976, 0x5C74, 0xC977, 0x5C73, 0xC978, 0x5E04, 0xC979, 0x5E80,\n\t0xC97A, 0x5E82, 0xC97B, 0x5FC9, 0xC97C, 0x6209, 0xC97D, 0x6250,\n\t0xC97E, 0x6C15, 0xC9A1, 0x6C36, 0xC9A2, 0x6C43, 0xC9A3, 0x6C3F,\n\t0xC9A4, 0x6C3B, 0xC9A5, 0x72AE, 0xC9A6, 0x72B0, 0xC9A7, 0x738A,\n\t0xC9A8, 0x79B8, 0xC9A9, 0x808A, 0xC9AA, 0x961E, 0xC9AB, 0x4F0E,\n\t0xC9AC, 0x4F18, 0xC9AD, 0x4F2C, 0xC9AE, 0x4EF5, 0xC9AF, 0x4F14,\n\t0xC9B0, 0x4EF1, 0xC9B1, 0x4F00, 0xC9B2, 0x4EF7, 0xC9B3, 0x4F08,\n\t0xC9B4, 0x4F1D, 0xC9B5, 0x4F02, 0xC9B6, 0x4F05, 0xC9B7, 0x4F22,\n\t0xC9B8, 0x4F13, 0xC9B9, 0x4F04, 0xC9BA, 0x4EF4, 0xC9BB, 0x4F12,\n\t0xC9BC, 0x51B1, 0xC9BD, 0x5213, 0xC9BE, 0x5209, 0xC9BF, 0x5210,\n\t0xC9C0, 0x52A6, 0xC9C1, 0x5322, 0xC9C2, 0x531F, 0xC9C3, 0x534D,\n\t0xC9C4, 0x538A, 0xC9C5, 0x5407, 0xC9C6, 0x56E1, 0xC9C7, 0x56DF,\n\t0xC9C8, 0x572E, 0xC9C9, 0x572A, 0xC9CA, 0x5734, 0xC9CB, 0x593C,\n\t0xC9CC, 0x5980, 0xC9CD, 0x597C, 0xC9CE, 0x5985, 0xC9CF, 0x597B,\n\t0xC9D0, 0x597E, 0xC9D1, 0x5977, 0xC9D2, 0x597F, 0xC9D3, 0x5B56,\n\t0xC9D4, 0x5C15, 0xC9D5, 0x5C25, 0xC9D6, 0x5C7C, 0xC9D7, 0x5C7A,\n\t0xC9D8, 0x5C7B, 0xC9D9, 0x5C7E, 0xC9DA, 0x5DDF, 0xC9DB, 0x5E75,\n\t0xC9DC, 0x5E84, 0xC9DD, 0x5F02, 0xC9DE, 0x5F1A, 0xC9DF, 0x5F74,\n\t0xC9E0, 0x5FD5, 0xC9E1, 0x5FD4, 0xC9E2, 0x5FCF, 0xC9E3, 0x625C,\n\t0xC9E4, 0x625E, 0xC9E5, 0x6264, 0xC9E6, 0x6261, 0xC9E7, 0x6266,\n\t0xC9E8, 0x6262, 0xC9E9, 0x6259, 0xC9EA, 0x6260, 0xC9EB, 0x625A,\n\t0xC9EC, 0x6265, 0xC9ED, 0x65EF, 0xC9EE, 0x65EE, 0xC9EF, 0x673E,\n\t0xC9F0, 0x6739, 0xC9F1, 0x6738, 0xC9F2, 0x673B, 0xC9F3, 0x673A,\n\t0xC9F4, 0x673F, 0xC9F5, 0x673C, 0xC9F6, 0x6733, 0xC9F7, 0x6C18,\n\t0xC9F8, 0x6C46, 0xC9F9, 0x6C52, 0xC9FA, 0x6C5C, 0xC9FB, 0x6C4F,\n\t0xC9FC, 0x6C4A, 0xC9FD, 0x6C54, 0xC9FE, 0x6C4B, 0xCA40, 0x6C4C,\n\t0xCA41, 0x7071, 0xCA42, 0x725E, 0xCA43, 0x72B4, 0xCA44, 0x72B5,\n\t0xCA45, 0x738E, 0xCA46, 0x752A, 0xCA47, 0x767F, 0xCA48, 0x7A75,\n\t0xCA49, 0x7F51, 0xCA4A, 0x8278, 0xCA4B, 0x827C, 0xCA4C, 0x8280,\n\t0xCA4D, 0x827D, 0xCA4E, 0x827F, 0xCA4F, 0x864D, 0xCA50, 0x897E,\n\t0xCA51, 0x9099, 0xCA52, 0x9097, 0xCA53, 0x9098, 0xCA54, 0x909B,\n\t0xCA55, 0x9094, 0xCA56, 0x9622, 0xCA57, 0x9624, 0xCA58, 0x9620,\n\t0xCA59, 0x9623, 0xCA5A, 0x4F56, 0xCA5B, 0x4F3B, 0xCA5C, 0x4F62,\n\t0xCA5D, 0x4F49, 0xCA5E, 0x4F53, 0xCA5F, 0x4F64, 0xCA60, 0x4F3E,\n\t0xCA61, 0x4F67, 0xCA62, 0x4F52, 0xCA63, 0x4F5F, 0xCA64, 0x4F41,\n\t0xCA65, 0x4F58, 0xCA66, 0x4F2D, 0xCA67, 0x4F33, 0xCA68, 0x4F3F,\n\t0xCA69, 0x4F61, 0xCA6A, 0x518F, 0xCA6B, 0x51B9, 0xCA6C, 0x521C,\n\t0xCA6D, 0x521E, 0xCA6E, 0x5221, 0xCA6F, 0x52AD, 0xCA70, 0x52AE,\n\t0xCA71, 0x5309, 0xCA72, 0x5363, 0xCA73, 0x5372, 0xCA74, 0x538E,\n\t0xCA75, 0x538F, 0xCA76, 0x5430, 0xCA77, 0x5437, 0xCA78, 0x542A,\n\t0xCA79, 0x5454, 0xCA7A, 0x5445, 0xCA7B, 0x5419, 0xCA7C, 0x541C,\n\t0xCA7D, 0x5425, 0xCA7E, 0x5418, 0xCAA1, 0x543D, 0xCAA2, 0x544F,\n\t0xCAA3, 0x5441, 0xCAA4, 0x5428, 0xCAA5, 0x5424, 0xCAA6, 0x5447,\n\t0xCAA7, 0x56EE, 0xCAA8, 0x56E7, 0xCAA9, 0x56E5, 0xCAAA, 0x5741,\n\t0xCAAB, 0x5745, 0xCAAC, 0x574C, 0xCAAD, 0x5749, 0xCAAE, 0x574B,\n\t0xCAAF, 0x5752, 0xCAB0, 0x5906, 0xCAB1, 0x5940, 0xCAB2, 0x59A6,\n\t0xCAB3, 0x5998, 0xCAB4, 0x59A0, 0xCAB5, 0x5997, 0xCAB6, 0x598E,\n\t0xCAB7, 0x59A2, 0xCAB8, 0x5990, 0xCAB9, 0x598F, 0xCABA, 0x59A7,\n\t0xCABB, 0x59A1, 0xCABC, 0x5B8E, 0xCABD, 0x5B92, 0xCABE, 0x5C28,\n\t0xCABF, 0x5C2A, 0xCAC0, 0x5C8D, 0xCAC1, 0x5C8F, 0xCAC2, 0x5C88,\n\t0xCAC3, 0x5C8B, 0xCAC4, 0x5C89, 0xCAC5, 0x5C92, 0xCAC6, 0x5C8A,\n\t0xCAC7, 0x5C86, 0xCAC8, 0x5C93, 0xCAC9, 0x5C95, 0xCACA, 0x5DE0,\n\t0xCACB, 0x5E0A, 0xCACC, 0x5E0E, 0xCACD, 0x5E8B, 0xCACE, 0x5E89,\n\t0xCACF, 0x5E8C, 0xCAD0, 0x5E88, 0xCAD1, 0x5E8D, 0xCAD2, 0x5F05,\n\t0xCAD3, 0x5F1D, 0xCAD4, 0x5F78, 0xCAD5, 0x5F76, 0xCAD6, 0x5FD2,\n\t0xCAD7, 0x5FD1, 0xCAD8, 0x5FD0, 0xCAD9, 0x5FED, 0xCADA, 0x5FE8,\n\t0xCADB, 0x5FEE, 0xCADC, 0x5FF3, 0xCADD, 0x5FE1, 0xCADE, 0x5FE4,\n\t0xCADF, 0x5FE3, 0xCAE0, 0x5FFA, 0xCAE1, 0x5FEF, 0xCAE2, 0x5FF7,\n\t0xCAE3, 0x5FFB, 0xCAE4, 0x6000, 0xCAE5, 0x5FF4, 0xCAE6, 0x623A,\n\t0xCAE7, 0x6283, 0xCAE8, 0x628C, 0xCAE9, 0x628E, 0xCAEA, 0x628F,\n\t0xCAEB, 0x6294, 0xCAEC, 0x6287, 0xCAED, 0x6271, 0xCAEE, 0x627B,\n\t0xCAEF, 0x627A, 0xCAF0, 0x6270, 0xCAF1, 0x6281, 0xCAF2, 0x6288,\n\t0xCAF3, 0x6277, 0xCAF4, 0x627D, 0xCAF5, 0x6272, 0xCAF6, 0x6274,\n\t0xCAF7, 0x6537, 0xCAF8, 0x65F0, 0xCAF9, 0x65F4, 0xCAFA, 0x65F3,\n\t0xCAFB, 0x65F2, 0xCAFC, 0x65F5, 0xCAFD, 0x6745, 0xCAFE, 0x6747,\n\t0xCB40, 0x6759, 0xCB41, 0x6755, 0xCB42, 0x674C, 0xCB43, 0x6748,\n\t0xCB44, 0x675D, 0xCB45, 0x674D, 0xCB46, 0x675A, 0xCB47, 0x674B,\n\t0xCB48, 0x6BD0, 0xCB49, 0x6C19, 0xCB4A, 0x6C1A, 0xCB4B, 0x6C78,\n\t0xCB4C, 0x6C67, 0xCB4D, 0x6C6B, 0xCB4E, 0x6C84, 0xCB4F, 0x6C8B,\n\t0xCB50, 0x6C8F, 0xCB51, 0x6C71, 0xCB52, 0x6C6F, 0xCB53, 0x6C69,\n\t0xCB54, 0x6C9A, 0xCB55, 0x6C6D, 0xCB56, 0x6C87, 0xCB57, 0x6C95,\n\t0xCB58, 0x6C9C, 0xCB59, 0x6C66, 0xCB5A, 0x6C73, 0xCB5B, 0x6C65,\n\t0xCB5C, 0x6C7B, 0xCB5D, 0x6C8E, 0xCB5E, 0x7074, 0xCB5F, 0x707A,\n\t0xCB60, 0x7263, 0xCB61, 0x72BF, 0xCB62, 0x72BD, 0xCB63, 0x72C3,\n\t0xCB64, 0x72C6, 0xCB65, 0x72C1, 0xCB66, 0x72BA, 0xCB67, 0x72C5,\n\t0xCB68, 0x7395, 0xCB69, 0x7397, 0xCB6A, 0x7393, 0xCB6B, 0x7394,\n\t0xCB6C, 0x7392, 0xCB6D, 0x753A, 0xCB6E, 0x7539, 0xCB6F, 0x7594,\n\t0xCB70, 0x7595, 0xCB71, 0x7681, 0xCB72, 0x793D, 0xCB73, 0x8034,\n\t0xCB74, 0x8095, 0xCB75, 0x8099, 0xCB76, 0x8090, 0xCB77, 0x8092,\n\t0xCB78, 0x809C, 0xCB79, 0x8290, 0xCB7A, 0x828F, 0xCB7B, 0x8285,\n\t0xCB7C, 0x828E, 0xCB7D, 0x8291, 0xCB7E, 0x8293, 0xCBA1, 0x828A,\n\t0xCBA2, 0x8283, 0xCBA3, 0x8284, 0xCBA4, 0x8C78, 0xCBA5, 0x8FC9,\n\t0xCBA6, 0x8FBF, 0xCBA7, 0x909F, 0xCBA8, 0x90A1, 0xCBA9, 0x90A5,\n\t0xCBAA, 0x909E, 0xCBAB, 0x90A7, 0xCBAC, 0x90A0, 0xCBAD, 0x9630,\n\t0xCBAE, 0x9628, 0xCBAF, 0x962F, 0xCBB0, 0x962D, 0xCBB1, 0x4E33,\n\t0xCBB2, 0x4F98, 0xCBB3, 0x4F7C, 0xCBB4, 0x4F85, 0xCBB5, 0x4F7D,\n\t0xCBB6, 0x4F80, 0xCBB7, 0x4F87, 0xCBB8, 0x4F76, 0xCBB9, 0x4F74,\n\t0xCBBA, 0x4F89, 0xCBBB, 0x4F84, 0xCBBC, 0x4F77, 0xCBBD, 0x4F4C,\n\t0xCBBE, 0x4F97, 0xCBBF, 0x4F6A, 0xCBC0, 0x4F9A, 0xCBC1, 0x4F79,\n\t0xCBC2, 0x4F81, 0xCBC3, 0x4F78, 0xCBC4, 0x4F90, 0xCBC5, 0x4F9C,\n\t0xCBC6, 0x4F94, 0xCBC7, 0x4F9E, 0xCBC8, 0x4F92, 0xCBC9, 0x4F82,\n\t0xCBCA, 0x4F95, 0xCBCB, 0x4F6B, 0xCBCC, 0x4F6E, 0xCBCD, 0x519E,\n\t0xCBCE, 0x51BC, 0xCBCF, 0x51BE, 0xCBD0, 0x5235, 0xCBD1, 0x5232,\n\t0xCBD2, 0x5233, 0xCBD3, 0x5246, 0xCBD4, 0x5231, 0xCBD5, 0x52BC,\n\t0xCBD6, 0x530A, 0xCBD7, 0x530B, 0xCBD8, 0x533C, 0xCBD9, 0x5392,\n\t0xCBDA, 0x5394, 0xCBDB, 0x5487, 0xCBDC, 0x547F, 0xCBDD, 0x5481,\n\t0xCBDE, 0x5491, 0xCBDF, 0x5482, 0xCBE0, 0x5488, 0xCBE1, 0x546B,\n\t0xCBE2, 0x547A, 0xCBE3, 0x547E, 0xCBE4, 0x5465, 0xCBE5, 0x546C,\n\t0xCBE6, 0x5474, 0xCBE7, 0x5466, 0xCBE8, 0x548D, 0xCBE9, 0x546F,\n\t0xCBEA, 0x5461, 0xCBEB, 0x5460, 0xCBEC, 0x5498, 0xCBED, 0x5463,\n\t0xCBEE, 0x5467, 0xCBEF, 0x5464, 0xCBF0, 0x56F7, 0xCBF1, 0x56F9,\n\t0xCBF2, 0x576F, 0xCBF3, 0x5772, 0xCBF4, 0x576D, 0xCBF5, 0x576B,\n\t0xCBF6, 0x5771, 0xCBF7, 0x5770, 0xCBF8, 0x5776, 0xCBF9, 0x5780,\n\t0xCBFA, 0x5775, 0xCBFB, 0x577B, 0xCBFC, 0x5773, 0xCBFD, 0x5774,\n\t0xCBFE, 0x5762, 0xCC40, 0x5768, 0xCC41, 0x577D, 0xCC42, 0x590C,\n\t0xCC43, 0x5945, 0xCC44, 0x59B5, 0xCC45, 0x59BA, 0xCC46, 0x59CF,\n\t0xCC47, 0x59CE, 0xCC48, 0x59B2, 0xCC49, 0x59CC, 0xCC4A, 0x59C1,\n\t0xCC4B, 0x59B6, 0xCC4C, 0x59BC, 0xCC4D, 0x59C3, 0xCC4E, 0x59D6,\n\t0xCC4F, 0x59B1, 0xCC50, 0x59BD, 0xCC51, 0x59C0, 0xCC52, 0x59C8,\n\t0xCC53, 0x59B4, 0xCC54, 0x59C7, 0xCC55, 0x5B62, 0xCC56, 0x5B65,\n\t0xCC57, 0x5B93, 0xCC58, 0x5B95, 0xCC59, 0x5C44, 0xCC5A, 0x5C47,\n\t0xCC5B, 0x5CAE, 0xCC5C, 0x5CA4, 0xCC5D, 0x5CA0, 0xCC5E, 0x5CB5,\n\t0xCC5F, 0x5CAF, 0xCC60, 0x5CA8, 0xCC61, 0x5CAC, 0xCC62, 0x5C9F,\n\t0xCC63, 0x5CA3, 0xCC64, 0x5CAD, 0xCC65, 0x5CA2, 0xCC66, 0x5CAA,\n\t0xCC67, 0x5CA7, 0xCC68, 0x5C9D, 0xCC69, 0x5CA5, 0xCC6A, 0x5CB6,\n\t0xCC6B, 0x5CB0, 0xCC6C, 0x5CA6, 0xCC6D, 0x5E17, 0xCC6E, 0x5E14,\n\t0xCC6F, 0x5E19, 0xCC70, 0x5F28, 0xCC71, 0x5F22, 0xCC72, 0x5F23,\n\t0xCC73, 0x5F24, 0xCC74, 0x5F54, 0xCC75, 0x5F82, 0xCC76, 0x5F7E,\n\t0xCC77, 0x5F7D, 0xCC78, 0x5FDE, 0xCC79, 0x5FE5, 0xCC7A, 0x602D,\n\t0xCC7B, 0x6026, 0xCC7C, 0x6019, 0xCC7D, 0x6032, 0xCC7E, 0x600B,\n\t0xCCA1, 0x6034, 0xCCA2, 0x600A, 0xCCA3, 0x6017, 0xCCA4, 0x6033,\n\t0xCCA5, 0x601A, 0xCCA6, 0x601E, 0xCCA7, 0x602C, 0xCCA8, 0x6022,\n\t0xCCA9, 0x600D, 0xCCAA, 0x6010, 0xCCAB, 0x602E, 0xCCAC, 0x6013,\n\t0xCCAD, 0x6011, 0xCCAE, 0x600C, 0xCCAF, 0x6009, 0xCCB0, 0x601C,\n\t0xCCB1, 0x6214, 0xCCB2, 0x623D, 0xCCB3, 0x62AD, 0xCCB4, 0x62B4,\n\t0xCCB5, 0x62D1, 0xCCB6, 0x62BE, 0xCCB7, 0x62AA, 0xCCB8, 0x62B6,\n\t0xCCB9, 0x62CA, 0xCCBA, 0x62AE, 0xCCBB, 0x62B3, 0xCCBC, 0x62AF,\n\t0xCCBD, 0x62BB, 0xCCBE, 0x62A9, 0xCCBF, 0x62B0, 0xCCC0, 0x62B8,\n\t0xCCC1, 0x653D, 0xCCC2, 0x65A8, 0xCCC3, 0x65BB, 0xCCC4, 0x6609,\n\t0xCCC5, 0x65FC, 0xCCC6, 0x6604, 0xCCC7, 0x6612, 0xCCC8, 0x6608,\n\t0xCCC9, 0x65FB, 0xCCCA, 0x6603, 0xCCCB, 0x660B, 0xCCCC, 0x660D,\n\t0xCCCD, 0x6605, 0xCCCE, 0x65FD, 0xCCCF, 0x6611, 0xCCD0, 0x6610,\n\t0xCCD1, 0x66F6, 0xCCD2, 0x670A, 0xCCD3, 0x6785, 0xCCD4, 0x676C,\n\t0xCCD5, 0x678E, 0xCCD6, 0x6792, 0xCCD7, 0x6776, 0xCCD8, 0x677B,\n\t0xCCD9, 0x6798, 0xCCDA, 0x6786, 0xCCDB, 0x6784, 0xCCDC, 0x6774,\n\t0xCCDD, 0x678D, 0xCCDE, 0x678C, 0xCCDF, 0x677A, 0xCCE0, 0x679F,\n\t0xCCE1, 0x6791, 0xCCE2, 0x6799, 0xCCE3, 0x6783, 0xCCE4, 0x677D,\n\t0xCCE5, 0x6781, 0xCCE6, 0x6778, 0xCCE7, 0x6779, 0xCCE8, 0x6794,\n\t0xCCE9, 0x6B25, 0xCCEA, 0x6B80, 0xCCEB, 0x6B7E, 0xCCEC, 0x6BDE,\n\t0xCCED, 0x6C1D, 0xCCEE, 0x6C93, 0xCCEF, 0x6CEC, 0xCCF0, 0x6CEB,\n\t0xCCF1, 0x6CEE, 0xCCF2, 0x6CD9, 0xCCF3, 0x6CB6, 0xCCF4, 0x6CD4,\n\t0xCCF5, 0x6CAD, 0xCCF6, 0x6CE7, 0xCCF7, 0x6CB7, 0xCCF8, 0x6CD0,\n\t0xCCF9, 0x6CC2, 0xCCFA, 0x6CBA, 0xCCFB, 0x6CC3, 0xCCFC, 0x6CC6,\n\t0xCCFD, 0x6CED, 0xCCFE, 0x6CF2, 0xCD40, 0x6CD2, 0xCD41, 0x6CDD,\n\t0xCD42, 0x6CB4, 0xCD43, 0x6C8A, 0xCD44, 0x6C9D, 0xCD45, 0x6C80,\n\t0xCD46, 0x6CDE, 0xCD47, 0x6CC0, 0xCD48, 0x6D30, 0xCD49, 0x6CCD,\n\t0xCD4A, 0x6CC7, 0xCD4B, 0x6CB0, 0xCD4C, 0x6CF9, 0xCD4D, 0x6CCF,\n\t0xCD4E, 0x6CE9, 0xCD4F, 0x6CD1, 0xCD50, 0x7094, 0xCD51, 0x7098,\n\t0xCD52, 0x7085, 0xCD53, 0x7093, 0xCD54, 0x7086, 0xCD55, 0x7084,\n\t0xCD56, 0x7091, 0xCD57, 0x7096, 0xCD58, 0x7082, 0xCD59, 0x709A,\n\t0xCD5A, 0x7083, 0xCD5B, 0x726A, 0xCD5C, 0x72D6, 0xCD5D, 0x72CB,\n\t0xCD5E, 0x72D8, 0xCD5F, 0x72C9, 0xCD60, 0x72DC, 0xCD61, 0x72D2,\n\t0xCD62, 0x72D4, 0xCD63, 0x72DA, 0xCD64, 0x72CC, 0xCD65, 0x72D1,\n\t0xCD66, 0x73A4, 0xCD67, 0x73A1, 0xCD68, 0x73AD, 0xCD69, 0x73A6,\n\t0xCD6A, 0x73A2, 0xCD6B, 0x73A0, 0xCD6C, 0x73AC, 0xCD6D, 0x739D,\n\t0xCD6E, 0x74DD, 0xCD6F, 0x74E8, 0xCD70, 0x753F, 0xCD71, 0x7540,\n\t0xCD72, 0x753E, 0xCD73, 0x758C, 0xCD74, 0x7598, 0xCD75, 0x76AF,\n\t0xCD76, 0x76F3, 0xCD77, 0x76F1, 0xCD78, 0x76F0, 0xCD79, 0x76F5,\n\t0xCD7A, 0x77F8, 0xCD7B, 0x77FC, 0xCD7C, 0x77F9, 0xCD7D, 0x77FB,\n\t0xCD7E, 0x77FA, 0xCDA1, 0x77F7, 0xCDA2, 0x7942, 0xCDA3, 0x793F,\n\t0xCDA4, 0x79C5, 0xCDA5, 0x7A78, 0xCDA6, 0x7A7B, 0xCDA7, 0x7AFB,\n\t0xCDA8, 0x7C75, 0xCDA9, 0x7CFD, 0xCDAA, 0x8035, 0xCDAB, 0x808F,\n\t0xCDAC, 0x80AE, 0xCDAD, 0x80A3, 0xCDAE, 0x80B8, 0xCDAF, 0x80B5,\n\t0xCDB0, 0x80AD, 0xCDB1, 0x8220, 0xCDB2, 0x82A0, 0xCDB3, 0x82C0,\n\t0xCDB4, 0x82AB, 0xCDB5, 0x829A, 0xCDB6, 0x8298, 0xCDB7, 0x829B,\n\t0xCDB8, 0x82B5, 0xCDB9, 0x82A7, 0xCDBA, 0x82AE, 0xCDBB, 0x82BC,\n\t0xCDBC, 0x829E, 0xCDBD, 0x82BA, 0xCDBE, 0x82B4, 0xCDBF, 0x82A8,\n\t0xCDC0, 0x82A1, 0xCDC1, 0x82A9, 0xCDC2, 0x82C2, 0xCDC3, 0x82A4,\n\t0xCDC4, 0x82C3, 0xCDC5, 0x82B6, 0xCDC6, 0x82A2, 0xCDC7, 0x8670,\n\t0xCDC8, 0x866F, 0xCDC9, 0x866D, 0xCDCA, 0x866E, 0xCDCB, 0x8C56,\n\t0xCDCC, 0x8FD2, 0xCDCD, 0x8FCB, 0xCDCE, 0x8FD3, 0xCDCF, 0x8FCD,\n\t0xCDD0, 0x8FD6, 0xCDD1, 0x8FD5, 0xCDD2, 0x8FD7, 0xCDD3, 0x90B2,\n\t0xCDD4, 0x90B4, 0xCDD5, 0x90AF, 0xCDD6, 0x90B3, 0xCDD7, 0x90B0,\n\t0xCDD8, 0x9639, 0xCDD9, 0x963D, 0xCDDA, 0x963C, 0xCDDB, 0x963A,\n\t0xCDDC, 0x9643, 0xCDDD, 0x4FCD, 0xCDDE, 0x4FC5, 0xCDDF, 0x4FD3,\n\t0xCDE0, 0x4FB2, 0xCDE1, 0x4FC9, 0xCDE2, 0x4FCB, 0xCDE3, 0x4FC1,\n\t0xCDE4, 0x4FD4, 0xCDE5, 0x4FDC, 0xCDE6, 0x4FD9, 0xCDE7, 0x4FBB,\n\t0xCDE8, 0x4FB3, 0xCDE9, 0x4FDB, 0xCDEA, 0x4FC7, 0xCDEB, 0x4FD6,\n\t0xCDEC, 0x4FBA, 0xCDED, 0x4FC0, 0xCDEE, 0x4FB9, 0xCDEF, 0x4FEC,\n\t0xCDF0, 0x5244, 0xCDF1, 0x5249, 0xCDF2, 0x52C0, 0xCDF3, 0x52C2,\n\t0xCDF4, 0x533D, 0xCDF5, 0x537C, 0xCDF6, 0x5397, 0xCDF7, 0x5396,\n\t0xCDF8, 0x5399, 0xCDF9, 0x5398, 0xCDFA, 0x54BA, 0xCDFB, 0x54A1,\n\t0xCDFC, 0x54AD, 0xCDFD, 0x54A5, 0xCDFE, 0x54CF, 0xCE40, 0x54C3,\n\t0xCE41, 0x830D, 0xCE42, 0x54B7, 0xCE43, 0x54AE, 0xCE44, 0x54D6,\n\t0xCE45, 0x54B6, 0xCE46, 0x54C5, 0xCE47, 0x54C6, 0xCE48, 0x54A0,\n\t0xCE49, 0x5470, 0xCE4A, 0x54BC, 0xCE4B, 0x54A2, 0xCE4C, 0x54BE,\n\t0xCE4D, 0x5472, 0xCE4E, 0x54DE, 0xCE4F, 0x54B0, 0xCE50, 0x57B5,\n\t0xCE51, 0x579E, 0xCE52, 0x579F, 0xCE53, 0x57A4, 0xCE54, 0x578C,\n\t0xCE55, 0x5797, 0xCE56, 0x579D, 0xCE57, 0x579B, 0xCE58, 0x5794,\n\t0xCE59, 0x5798, 0xCE5A, 0x578F, 0xCE5B, 0x5799, 0xCE5C, 0x57A5,\n\t0xCE5D, 0x579A, 0xCE5E, 0x5795, 0xCE5F, 0x58F4, 0xCE60, 0x590D,\n\t0xCE61, 0x5953, 0xCE62, 0x59E1, 0xCE63, 0x59DE, 0xCE64, 0x59EE,\n\t0xCE65, 0x5A00, 0xCE66, 0x59F1, 0xCE67, 0x59DD, 0xCE68, 0x59FA,\n\t0xCE69, 0x59FD, 0xCE6A, 0x59FC, 0xCE6B, 0x59F6, 0xCE6C, 0x59E4,\n\t0xCE6D, 0x59F2, 0xCE6E, 0x59F7, 0xCE6F, 0x59DB, 0xCE70, 0x59E9,\n\t0xCE71, 0x59F3, 0xCE72, 0x59F5, 0xCE73, 0x59E0, 0xCE74, 0x59FE,\n\t0xCE75, 0x59F4, 0xCE76, 0x59ED, 0xCE77, 0x5BA8, 0xCE78, 0x5C4C,\n\t0xCE79, 0x5CD0, 0xCE7A, 0x5CD8, 0xCE7B, 0x5CCC, 0xCE7C, 0x5CD7,\n\t0xCE7D, 0x5CCB, 0xCE7E, 0x5CDB, 0xCEA1, 0x5CDE, 0xCEA2, 0x5CDA,\n\t0xCEA3, 0x5CC9, 0xCEA4, 0x5CC7, 0xCEA5, 0x5CCA, 0xCEA6, 0x5CD6,\n\t0xCEA7, 0x5CD3, 0xCEA8, 0x5CD4, 0xCEA9, 0x5CCF, 0xCEAA, 0x5CC8,\n\t0xCEAB, 0x5CC6, 0xCEAC, 0x5CCE, 0xCEAD, 0x5CDF, 0xCEAE, 0x5CF8,\n\t0xCEAF, 0x5DF9, 0xCEB0, 0x5E21, 0xCEB1, 0x5E22, 0xCEB2, 0x5E23,\n\t0xCEB3, 0x5E20, 0xCEB4, 0x5E24, 0xCEB5, 0x5EB0, 0xCEB6, 0x5EA4,\n\t0xCEB7, 0x5EA2, 0xCEB8, 0x5E9B, 0xCEB9, 0x5EA3, 0xCEBA, 0x5EA5,\n\t0xCEBB, 0x5F07, 0xCEBC, 0x5F2E, 0xCEBD, 0x5F56, 0xCEBE, 0x5F86,\n\t0xCEBF, 0x6037, 0xCEC0, 0x6039, 0xCEC1, 0x6054, 0xCEC2, 0x6072,\n\t0xCEC3, 0x605E, 0xCEC4, 0x6045, 0xCEC5, 0x6053, 0xCEC6, 0x6047,\n\t0xCEC7, 0x6049, 0xCEC8, 0x605B, 0xCEC9, 0x604C, 0xCECA, 0x6040,\n\t0xCECB, 0x6042, 0xCECC, 0x605F, 0xCECD, 0x6024, 0xCECE, 0x6044,\n\t0xCECF, 0x6058, 0xCED0, 0x6066, 0xCED1, 0x606E, 0xCED2, 0x6242,\n\t0xCED3, 0x6243, 0xCED4, 0x62CF, 0xCED5, 0x630D, 0xCED6, 0x630B,\n\t0xCED7, 0x62F5, 0xCED8, 0x630E, 0xCED9, 0x6303, 0xCEDA, 0x62EB,\n\t0xCEDB, 0x62F9, 0xCEDC, 0x630F, 0xCEDD, 0x630C, 0xCEDE, 0x62F8,\n\t0xCEDF, 0x62F6, 0xCEE0, 0x6300, 0xCEE1, 0x6313, 0xCEE2, 0x6314,\n\t0xCEE3, 0x62FA, 0xCEE4, 0x6315, 0xCEE5, 0x62FB, 0xCEE6, 0x62F0,\n\t0xCEE7, 0x6541, 0xCEE8, 0x6543, 0xCEE9, 0x65AA, 0xCEEA, 0x65BF,\n\t0xCEEB, 0x6636, 0xCEEC, 0x6621, 0xCEED, 0x6632, 0xCEEE, 0x6635,\n\t0xCEEF, 0x661C, 0xCEF0, 0x6626, 0xCEF1, 0x6622, 0xCEF2, 0x6633,\n\t0xCEF3, 0x662B, 0xCEF4, 0x663A, 0xCEF5, 0x661D, 0xCEF6, 0x6634,\n\t0xCEF7, 0x6639, 0xCEF8, 0x662E, 0xCEF9, 0x670F, 0xCEFA, 0x6710,\n\t0xCEFB, 0x67C1, 0xCEFC, 0x67F2, 0xCEFD, 0x67C8, 0xCEFE, 0x67BA,\n\t0xCF40, 0x67DC, 0xCF41, 0x67BB, 0xCF42, 0x67F8, 0xCF43, 0x67D8,\n\t0xCF44, 0x67C0, 0xCF45, 0x67B7, 0xCF46, 0x67C5, 0xCF47, 0x67EB,\n\t0xCF48, 0x67E4, 0xCF49, 0x67DF, 0xCF4A, 0x67B5, 0xCF4B, 0x67CD,\n\t0xCF4C, 0x67B3, 0xCF4D, 0x67F7, 0xCF4E, 0x67F6, 0xCF4F, 0x67EE,\n\t0xCF50, 0x67E3, 0xCF51, 0x67C2, 0xCF52, 0x67B9, 0xCF53, 0x67CE,\n\t0xCF54, 0x67E7, 0xCF55, 0x67F0, 0xCF56, 0x67B2, 0xCF57, 0x67FC,\n\t0xCF58, 0x67C6, 0xCF59, 0x67ED, 0xCF5A, 0x67CC, 0xCF5B, 0x67AE,\n\t0xCF5C, 0x67E6, 0xCF5D, 0x67DB, 0xCF5E, 0x67FA, 0xCF5F, 0x67C9,\n\t0xCF60, 0x67CA, 0xCF61, 0x67C3, 0xCF62, 0x67EA, 0xCF63, 0x67CB,\n\t0xCF64, 0x6B28, 0xCF65, 0x6B82, 0xCF66, 0x6B84, 0xCF67, 0x6BB6,\n\t0xCF68, 0x6BD6, 0xCF69, 0x6BD8, 0xCF6A, 0x6BE0, 0xCF6B, 0x6C20,\n\t0xCF6C, 0x6C21, 0xCF6D, 0x6D28, 0xCF6E, 0x6D34, 0xCF6F, 0x6D2D,\n\t0xCF70, 0x6D1F, 0xCF71, 0x6D3C, 0xCF72, 0x6D3F, 0xCF73, 0x6D12,\n\t0xCF74, 0x6D0A, 0xCF75, 0x6CDA, 0xCF76, 0x6D33, 0xCF77, 0x6D04,\n\t0xCF78, 0x6D19, 0xCF79, 0x6D3A, 0xCF7A, 0x6D1A, 0xCF7B, 0x6D11,\n\t0xCF7C, 0x6D00, 0xCF7D, 0x6D1D, 0xCF7E, 0x6D42, 0xCFA1, 0x6D01,\n\t0xCFA2, 0x6D18, 0xCFA3, 0x6D37, 0xCFA4, 0x6D03, 0xCFA5, 0x6D0F,\n\t0xCFA6, 0x6D40, 0xCFA7, 0x6D07, 0xCFA8, 0x6D20, 0xCFA9, 0x6D2C,\n\t0xCFAA, 0x6D08, 0xCFAB, 0x6D22, 0xCFAC, 0x6D09, 0xCFAD, 0x6D10,\n\t0xCFAE, 0x70B7, 0xCFAF, 0x709F, 0xCFB0, 0x70BE, 0xCFB1, 0x70B1,\n\t0xCFB2, 0x70B0, 0xCFB3, 0x70A1, 0xCFB4, 0x70B4, 0xCFB5, 0x70B5,\n\t0xCFB6, 0x70A9, 0xCFB7, 0x7241, 0xCFB8, 0x7249, 0xCFB9, 0x724A,\n\t0xCFBA, 0x726C, 0xCFBB, 0x7270, 0xCFBC, 0x7273, 0xCFBD, 0x726E,\n\t0xCFBE, 0x72CA, 0xCFBF, 0x72E4, 0xCFC0, 0x72E8, 0xCFC1, 0x72EB,\n\t0xCFC2, 0x72DF, 0xCFC3, 0x72EA, 0xCFC4, 0x72E6, 0xCFC5, 0x72E3,\n\t0xCFC6, 0x7385, 0xCFC7, 0x73CC, 0xCFC8, 0x73C2, 0xCFC9, 0x73C8,\n\t0xCFCA, 0x73C5, 0xCFCB, 0x73B9, 0xCFCC, 0x73B6, 0xCFCD, 0x73B5,\n\t0xCFCE, 0x73B4, 0xCFCF, 0x73EB, 0xCFD0, 0x73BF, 0xCFD1, 0x73C7,\n\t0xCFD2, 0x73BE, 0xCFD3, 0x73C3, 0xCFD4, 0x73C6, 0xCFD5, 0x73B8,\n\t0xCFD6, 0x73CB, 0xCFD7, 0x74EC, 0xCFD8, 0x74EE, 0xCFD9, 0x752E,\n\t0xCFDA, 0x7547, 0xCFDB, 0x7548, 0xCFDC, 0x75A7, 0xCFDD, 0x75AA,\n\t0xCFDE, 0x7679, 0xCFDF, 0x76C4, 0xCFE0, 0x7708, 0xCFE1, 0x7703,\n\t0xCFE2, 0x7704, 0xCFE3, 0x7705, 0xCFE4, 0x770A, 0xCFE5, 0x76F7,\n\t0xCFE6, 0x76FB, 0xCFE7, 0x76FA, 0xCFE8, 0x77E7, 0xCFE9, 0x77E8,\n\t0xCFEA, 0x7806, 0xCFEB, 0x7811, 0xCFEC, 0x7812, 0xCFED, 0x7805,\n\t0xCFEE, 0x7810, 0xCFEF, 0x780F, 0xCFF0, 0x780E, 0xCFF1, 0x7809,\n\t0xCFF2, 0x7803, 0xCFF3, 0x7813, 0xCFF4, 0x794A, 0xCFF5, 0x794C,\n\t0xCFF6, 0x794B, 0xCFF7, 0x7945, 0xCFF8, 0x7944, 0xCFF9, 0x79D5,\n\t0xCFFA, 0x79CD, 0xCFFB, 0x79CF, 0xCFFC, 0x79D6, 0xCFFD, 0x79CE,\n\t0xCFFE, 0x7A80, 0xD040, 0x7A7E, 0xD041, 0x7AD1, 0xD042, 0x7B00,\n\t0xD043, 0x7B01, 0xD044, 0x7C7A, 0xD045, 0x7C78, 0xD046, 0x7C79,\n\t0xD047, 0x7C7F, 0xD048, 0x7C80, 0xD049, 0x7C81, 0xD04A, 0x7D03,\n\t0xD04B, 0x7D08, 0xD04C, 0x7D01, 0xD04D, 0x7F58, 0xD04E, 0x7F91,\n\t0xD04F, 0x7F8D, 0xD050, 0x7FBE, 0xD051, 0x8007, 0xD052, 0x800E,\n\t0xD053, 0x800F, 0xD054, 0x8014, 0xD055, 0x8037, 0xD056, 0x80D8,\n\t0xD057, 0x80C7, 0xD058, 0x80E0, 0xD059, 0x80D1, 0xD05A, 0x80C8,\n\t0xD05B, 0x80C2, 0xD05C, 0x80D0, 0xD05D, 0x80C5, 0xD05E, 0x80E3,\n\t0xD05F, 0x80D9, 0xD060, 0x80DC, 0xD061, 0x80CA, 0xD062, 0x80D5,\n\t0xD063, 0x80C9, 0xD064, 0x80CF, 0xD065, 0x80D7, 0xD066, 0x80E6,\n\t0xD067, 0x80CD, 0xD068, 0x81FF, 0xD069, 0x8221, 0xD06A, 0x8294,\n\t0xD06B, 0x82D9, 0xD06C, 0x82FE, 0xD06D, 0x82F9, 0xD06E, 0x8307,\n\t0xD06F, 0x82E8, 0xD070, 0x8300, 0xD071, 0x82D5, 0xD072, 0x833A,\n\t0xD073, 0x82EB, 0xD074, 0x82D6, 0xD075, 0x82F4, 0xD076, 0x82EC,\n\t0xD077, 0x82E1, 0xD078, 0x82F2, 0xD079, 0x82F5, 0xD07A, 0x830C,\n\t0xD07B, 0x82FB, 0xD07C, 0x82F6, 0xD07D, 0x82F0, 0xD07E, 0x82EA,\n\t0xD0A1, 0x82E4, 0xD0A2, 0x82E0, 0xD0A3, 0x82FA, 0xD0A4, 0x82F3,\n\t0xD0A5, 0x82ED, 0xD0A6, 0x8677, 0xD0A7, 0x8674, 0xD0A8, 0x867C,\n\t0xD0A9, 0x8673, 0xD0AA, 0x8841, 0xD0AB, 0x884E, 0xD0AC, 0x8867,\n\t0xD0AD, 0x886A, 0xD0AE, 0x8869, 0xD0AF, 0x89D3, 0xD0B0, 0x8A04,\n\t0xD0B1, 0x8A07, 0xD0B2, 0x8D72, 0xD0B3, 0x8FE3, 0xD0B4, 0x8FE1,\n\t0xD0B5, 0x8FEE, 0xD0B6, 0x8FE0, 0xD0B7, 0x90F1, 0xD0B8, 0x90BD,\n\t0xD0B9, 0x90BF, 0xD0BA, 0x90D5, 0xD0BB, 0x90C5, 0xD0BC, 0x90BE,\n\t0xD0BD, 0x90C7, 0xD0BE, 0x90CB, 0xD0BF, 0x90C8, 0xD0C0, 0x91D4,\n\t0xD0C1, 0x91D3, 0xD0C2, 0x9654, 0xD0C3, 0x964F, 0xD0C4, 0x9651,\n\t0xD0C5, 0x9653, 0xD0C6, 0x964A, 0xD0C7, 0x964E, 0xD0C8, 0x501E,\n\t0xD0C9, 0x5005, 0xD0CA, 0x5007, 0xD0CB, 0x5013, 0xD0CC, 0x5022,\n\t0xD0CD, 0x5030, 0xD0CE, 0x501B, 0xD0CF, 0x4FF5, 0xD0D0, 0x4FF4,\n\t0xD0D1, 0x5033, 0xD0D2, 0x5037, 0xD0D3, 0x502C, 0xD0D4, 0x4FF6,\n\t0xD0D5, 0x4FF7, 0xD0D6, 0x5017, 0xD0D7, 0x501C, 0xD0D8, 0x5020,\n\t0xD0D9, 0x5027, 0xD0DA, 0x5035, 0xD0DB, 0x502F, 0xD0DC, 0x5031,\n\t0xD0DD, 0x500E, 0xD0DE, 0x515A, 0xD0DF, 0x5194, 0xD0E0, 0x5193,\n\t0xD0E1, 0x51CA, 0xD0E2, 0x51C4, 0xD0E3, 0x51C5, 0xD0E4, 0x51C8,\n\t0xD0E5, 0x51CE, 0xD0E6, 0x5261, 0xD0E7, 0x525A, 0xD0E8, 0x5252,\n\t0xD0E9, 0x525E, 0xD0EA, 0x525F, 0xD0EB, 0x5255, 0xD0EC, 0x5262,\n\t0xD0ED, 0x52CD, 0xD0EE, 0x530E, 0xD0EF, 0x539E, 0xD0F0, 0x5526,\n\t0xD0F1, 0x54E2, 0xD0F2, 0x5517, 0xD0F3, 0x5512, 0xD0F4, 0x54E7,\n\t0xD0F5, 0x54F3, 0xD0F6, 0x54E4, 0xD0F7, 0x551A, 0xD0F8, 0x54FF,\n\t0xD0F9, 0x5504, 0xD0FA, 0x5508, 0xD0FB, 0x54EB, 0xD0FC, 0x5511,\n\t0xD0FD, 0x5505, 0xD0FE, 0x54F1, 0xD140, 0x550A, 0xD141, 0x54FB,\n\t0xD142, 0x54F7, 0xD143, 0x54F8, 0xD144, 0x54E0, 0xD145, 0x550E,\n\t0xD146, 0x5503, 0xD147, 0x550B, 0xD148, 0x5701, 0xD149, 0x5702,\n\t0xD14A, 0x57CC, 0xD14B, 0x5832, 0xD14C, 0x57D5, 0xD14D, 0x57D2,\n\t0xD14E, 0x57BA, 0xD14F, 0x57C6, 0xD150, 0x57BD, 0xD151, 0x57BC,\n\t0xD152, 0x57B8, 0xD153, 0x57B6, 0xD154, 0x57BF, 0xD155, 0x57C7,\n\t0xD156, 0x57D0, 0xD157, 0x57B9, 0xD158, 0x57C1, 0xD159, 0x590E,\n\t0xD15A, 0x594A, 0xD15B, 0x5A19, 0xD15C, 0x5A16, 0xD15D, 0x5A2D,\n\t0xD15E, 0x5A2E, 0xD15F, 0x5A15, 0xD160, 0x5A0F, 0xD161, 0x5A17,\n\t0xD162, 0x5A0A, 0xD163, 0x5A1E, 0xD164, 0x5A33, 0xD165, 0x5B6C,\n\t0xD166, 0x5BA7, 0xD167, 0x5BAD, 0xD168, 0x5BAC, 0xD169, 0x5C03,\n\t0xD16A, 0x5C56, 0xD16B, 0x5C54, 0xD16C, 0x5CEC, 0xD16D, 0x5CFF,\n\t0xD16E, 0x5CEE, 0xD16F, 0x5CF1, 0xD170, 0x5CF7, 0xD171, 0x5D00,\n\t0xD172, 0x5CF9, 0xD173, 0x5E29, 0xD174, 0x5E28, 0xD175, 0x5EA8,\n\t0xD176, 0x5EAE, 0xD177, 0x5EAA, 0xD178, 0x5EAC, 0xD179, 0x5F33,\n\t0xD17A, 0x5F30, 0xD17B, 0x5F67, 0xD17C, 0x605D, 0xD17D, 0x605A,\n\t0xD17E, 0x6067, 0xD1A1, 0x6041, 0xD1A2, 0x60A2, 0xD1A3, 0x6088,\n\t0xD1A4, 0x6080, 0xD1A5, 0x6092, 0xD1A6, 0x6081, 0xD1A7, 0x609D,\n\t0xD1A8, 0x6083, 0xD1A9, 0x6095, 0xD1AA, 0x609B, 0xD1AB, 0x6097,\n\t0xD1AC, 0x6087, 0xD1AD, 0x609C, 0xD1AE, 0x608E, 0xD1AF, 0x6219,\n\t0xD1B0, 0x6246, 0xD1B1, 0x62F2, 0xD1B2, 0x6310, 0xD1B3, 0x6356,\n\t0xD1B4, 0x632C, 0xD1B5, 0x6344, 0xD1B6, 0x6345, 0xD1B7, 0x6336,\n\t0xD1B8, 0x6343, 0xD1B9, 0x63E4, 0xD1BA, 0x6339, 0xD1BB, 0x634B,\n\t0xD1BC, 0x634A, 0xD1BD, 0x633C, 0xD1BE, 0x6329, 0xD1BF, 0x6341,\n\t0xD1C0, 0x6334, 0xD1C1, 0x6358, 0xD1C2, 0x6354, 0xD1C3, 0x6359,\n\t0xD1C4, 0x632D, 0xD1C5, 0x6347, 0xD1C6, 0x6333, 0xD1C7, 0x635A,\n\t0xD1C8, 0x6351, 0xD1C9, 0x6338, 0xD1CA, 0x6357, 0xD1CB, 0x6340,\n\t0xD1CC, 0x6348, 0xD1CD, 0x654A, 0xD1CE, 0x6546, 0xD1CF, 0x65C6,\n\t0xD1D0, 0x65C3, 0xD1D1, 0x65C4, 0xD1D2, 0x65C2, 0xD1D3, 0x664A,\n\t0xD1D4, 0x665F, 0xD1D5, 0x6647, 0xD1D6, 0x6651, 0xD1D7, 0x6712,\n\t0xD1D8, 0x6713, 0xD1D9, 0x681F, 0xD1DA, 0x681A, 0xD1DB, 0x6849,\n\t0xD1DC, 0x6832, 0xD1DD, 0x6833, 0xD1DE, 0x683B, 0xD1DF, 0x684B,\n\t0xD1E0, 0x684F, 0xD1E1, 0x6816, 0xD1E2, 0x6831, 0xD1E3, 0x681C,\n\t0xD1E4, 0x6835, 0xD1E5, 0x682B, 0xD1E6, 0x682D, 0xD1E7, 0x682F,\n\t0xD1E8, 0x684E, 0xD1E9, 0x6844, 0xD1EA, 0x6834, 0xD1EB, 0x681D,\n\t0xD1EC, 0x6812, 0xD1ED, 0x6814, 0xD1EE, 0x6826, 0xD1EF, 0x6828,\n\t0xD1F0, 0x682E, 0xD1F1, 0x684D, 0xD1F2, 0x683A, 0xD1F3, 0x6825,\n\t0xD1F4, 0x6820, 0xD1F5, 0x6B2C, 0xD1F6, 0x6B2F, 0xD1F7, 0x6B2D,\n\t0xD1F8, 0x6B31, 0xD1F9, 0x6B34, 0xD1FA, 0x6B6D, 0xD1FB, 0x8082,\n\t0xD1FC, 0x6B88, 0xD1FD, 0x6BE6, 0xD1FE, 0x6BE4, 0xD240, 0x6BE8,\n\t0xD241, 0x6BE3, 0xD242, 0x6BE2, 0xD243, 0x6BE7, 0xD244, 0x6C25,\n\t0xD245, 0x6D7A, 0xD246, 0x6D63, 0xD247, 0x6D64, 0xD248, 0x6D76,\n\t0xD249, 0x6D0D, 0xD24A, 0x6D61, 0xD24B, 0x6D92, 0xD24C, 0x6D58,\n\t0xD24D, 0x6D62, 0xD24E, 0x6D6D, 0xD24F, 0x6D6F, 0xD250, 0x6D91,\n\t0xD251, 0x6D8D, 0xD252, 0x6DEF, 0xD253, 0x6D7F, 0xD254, 0x6D86,\n\t0xD255, 0x6D5E, 0xD256, 0x6D67, 0xD257, 0x6D60, 0xD258, 0x6D97,\n\t0xD259, 0x6D70, 0xD25A, 0x6D7C, 0xD25B, 0x6D5F, 0xD25C, 0x6D82,\n\t0xD25D, 0x6D98, 0xD25E, 0x6D2F, 0xD25F, 0x6D68, 0xD260, 0x6D8B,\n\t0xD261, 0x6D7E, 0xD262, 0x6D80, 0xD263, 0x6D84, 0xD264, 0x6D16,\n\t0xD265, 0x6D83, 0xD266, 0x6D7B, 0xD267, 0x6D7D, 0xD268, 0x6D75,\n\t0xD269, 0x6D90, 0xD26A, 0x70DC, 0xD26B, 0x70D3, 0xD26C, 0x70D1,\n\t0xD26D, 0x70DD, 0xD26E, 0x70CB, 0xD26F, 0x7F39, 0xD270, 0x70E2,\n\t0xD271, 0x70D7, 0xD272, 0x70D2, 0xD273, 0x70DE, 0xD274, 0x70E0,\n\t0xD275, 0x70D4, 0xD276, 0x70CD, 0xD277, 0x70C5, 0xD278, 0x70C6,\n\t0xD279, 0x70C7, 0xD27A, 0x70DA, 0xD27B, 0x70CE, 0xD27C, 0x70E1,\n\t0xD27D, 0x7242, 0xD27E, 0x7278, 0xD2A1, 0x7277, 0xD2A2, 0x7276,\n\t0xD2A3, 0x7300, 0xD2A4, 0x72FA, 0xD2A5, 0x72F4, 0xD2A6, 0x72FE,\n\t0xD2A7, 0x72F6, 0xD2A8, 0x72F3, 0xD2A9, 0x72FB, 0xD2AA, 0x7301,\n\t0xD2AB, 0x73D3, 0xD2AC, 0x73D9, 0xD2AD, 0x73E5, 0xD2AE, 0x73D6,\n\t0xD2AF, 0x73BC, 0xD2B0, 0x73E7, 0xD2B1, 0x73E3, 0xD2B2, 0x73E9,\n\t0xD2B3, 0x73DC, 0xD2B4, 0x73D2, 0xD2B5, 0x73DB, 0xD2B6, 0x73D4,\n\t0xD2B7, 0x73DD, 0xD2B8, 0x73DA, 0xD2B9, 0x73D7, 0xD2BA, 0x73D8,\n\t0xD2BB, 0x73E8, 0xD2BC, 0x74DE, 0xD2BD, 0x74DF, 0xD2BE, 0x74F4,\n\t0xD2BF, 0x74F5, 0xD2C0, 0x7521, 0xD2C1, 0x755B, 0xD2C2, 0x755F,\n\t0xD2C3, 0x75B0, 0xD2C4, 0x75C1, 0xD2C5, 0x75BB, 0xD2C6, 0x75C4,\n\t0xD2C7, 0x75C0, 0xD2C8, 0x75BF, 0xD2C9, 0x75B6, 0xD2CA, 0x75BA,\n\t0xD2CB, 0x768A, 0xD2CC, 0x76C9, 0xD2CD, 0x771D, 0xD2CE, 0x771B,\n\t0xD2CF, 0x7710, 0xD2D0, 0x7713, 0xD2D1, 0x7712, 0xD2D2, 0x7723,\n\t0xD2D3, 0x7711, 0xD2D4, 0x7715, 0xD2D5, 0x7719, 0xD2D6, 0x771A,\n\t0xD2D7, 0x7722, 0xD2D8, 0x7727, 0xD2D9, 0x7823, 0xD2DA, 0x782C,\n\t0xD2DB, 0x7822, 0xD2DC, 0x7835, 0xD2DD, 0x782F, 0xD2DE, 0x7828,\n\t0xD2DF, 0x782E, 0xD2E0, 0x782B, 0xD2E1, 0x7821, 0xD2E2, 0x7829,\n\t0xD2E3, 0x7833, 0xD2E4, 0x782A, 0xD2E5, 0x7831, 0xD2E6, 0x7954,\n\t0xD2E7, 0x795B, 0xD2E8, 0x794F, 0xD2E9, 0x795C, 0xD2EA, 0x7953,\n\t0xD2EB, 0x7952, 0xD2EC, 0x7951, 0xD2ED, 0x79EB, 0xD2EE, 0x79EC,\n\t0xD2EF, 0x79E0, 0xD2F0, 0x79EE, 0xD2F1, 0x79ED, 0xD2F2, 0x79EA,\n\t0xD2F3, 0x79DC, 0xD2F4, 0x79DE, 0xD2F5, 0x79DD, 0xD2F6, 0x7A86,\n\t0xD2F7, 0x7A89, 0xD2F8, 0x7A85, 0xD2F9, 0x7A8B, 0xD2FA, 0x7A8C,\n\t0xD2FB, 0x7A8A, 0xD2FC, 0x7A87, 0xD2FD, 0x7AD8, 0xD2FE, 0x7B10,\n\t0xD340, 0x7B04, 0xD341, 0x7B13, 0xD342, 0x7B05, 0xD343, 0x7B0F,\n\t0xD344, 0x7B08, 0xD345, 0x7B0A, 0xD346, 0x7B0E, 0xD347, 0x7B09,\n\t0xD348, 0x7B12, 0xD349, 0x7C84, 0xD34A, 0x7C91, 0xD34B, 0x7C8A,\n\t0xD34C, 0x7C8C, 0xD34D, 0x7C88, 0xD34E, 0x7C8D, 0xD34F, 0x7C85,\n\t0xD350, 0x7D1E, 0xD351, 0x7D1D, 0xD352, 0x7D11, 0xD353, 0x7D0E,\n\t0xD354, 0x7D18, 0xD355, 0x7D16, 0xD356, 0x7D13, 0xD357, 0x7D1F,\n\t0xD358, 0x7D12, 0xD359, 0x7D0F, 0xD35A, 0x7D0C, 0xD35B, 0x7F5C,\n\t0xD35C, 0x7F61, 0xD35D, 0x7F5E, 0xD35E, 0x7F60, 0xD35F, 0x7F5D,\n\t0xD360, 0x7F5B, 0xD361, 0x7F96, 0xD362, 0x7F92, 0xD363, 0x7FC3,\n\t0xD364, 0x7FC2, 0xD365, 0x7FC0, 0xD366, 0x8016, 0xD367, 0x803E,\n\t0xD368, 0x8039, 0xD369, 0x80FA, 0xD36A, 0x80F2, 0xD36B, 0x80F9,\n\t0xD36C, 0x80F5, 0xD36D, 0x8101, 0xD36E, 0x80FB, 0xD36F, 0x8100,\n\t0xD370, 0x8201, 0xD371, 0x822F, 0xD372, 0x8225, 0xD373, 0x8333,\n\t0xD374, 0x832D, 0xD375, 0x8344, 0xD376, 0x8319, 0xD377, 0x8351,\n\t0xD378, 0x8325, 0xD379, 0x8356, 0xD37A, 0x833F, 0xD37B, 0x8341,\n\t0xD37C, 0x8326, 0xD37D, 0x831C, 0xD37E, 0x8322, 0xD3A1, 0x8342,\n\t0xD3A2, 0x834E, 0xD3A3, 0x831B, 0xD3A4, 0x832A, 0xD3A5, 0x8308,\n\t0xD3A6, 0x833C, 0xD3A7, 0x834D, 0xD3A8, 0x8316, 0xD3A9, 0x8324,\n\t0xD3AA, 0x8320, 0xD3AB, 0x8337, 0xD3AC, 0x832F, 0xD3AD, 0x8329,\n\t0xD3AE, 0x8347, 0xD3AF, 0x8345, 0xD3B0, 0x834C, 0xD3B1, 0x8353,\n\t0xD3B2, 0x831E, 0xD3B3, 0x832C, 0xD3B4, 0x834B, 0xD3B5, 0x8327,\n\t0xD3B6, 0x8348, 0xD3B7, 0x8653, 0xD3B8, 0x8652, 0xD3B9, 0x86A2,\n\t0xD3BA, 0x86A8, 0xD3BB, 0x8696, 0xD3BC, 0x868D, 0xD3BD, 0x8691,\n\t0xD3BE, 0x869E, 0xD3BF, 0x8687, 0xD3C0, 0x8697, 0xD3C1, 0x8686,\n\t0xD3C2, 0x868B, 0xD3C3, 0x869A, 0xD3C4, 0x8685, 0xD3C5, 0x86A5,\n\t0xD3C6, 0x8699, 0xD3C7, 0x86A1, 0xD3C8, 0x86A7, 0xD3C9, 0x8695,\n\t0xD3CA, 0x8698, 0xD3CB, 0x868E, 0xD3CC, 0x869D, 0xD3CD, 0x8690,\n\t0xD3CE, 0x8694, 0xD3CF, 0x8843, 0xD3D0, 0x8844, 0xD3D1, 0x886D,\n\t0xD3D2, 0x8875, 0xD3D3, 0x8876, 0xD3D4, 0x8872, 0xD3D5, 0x8880,\n\t0xD3D6, 0x8871, 0xD3D7, 0x887F, 0xD3D8, 0x886F, 0xD3D9, 0x8883,\n\t0xD3DA, 0x887E, 0xD3DB, 0x8874, 0xD3DC, 0x887C, 0xD3DD, 0x8A12,\n\t0xD3DE, 0x8C47, 0xD3DF, 0x8C57, 0xD3E0, 0x8C7B, 0xD3E1, 0x8CA4,\n\t0xD3E2, 0x8CA3, 0xD3E3, 0x8D76, 0xD3E4, 0x8D78, 0xD3E5, 0x8DB5,\n\t0xD3E6, 0x8DB7, 0xD3E7, 0x8DB6, 0xD3E8, 0x8ED1, 0xD3E9, 0x8ED3,\n\t0xD3EA, 0x8FFE, 0xD3EB, 0x8FF5, 0xD3EC, 0x9002, 0xD3ED, 0x8FFF,\n\t0xD3EE, 0x8FFB, 0xD3EF, 0x9004, 0xD3F0, 0x8FFC, 0xD3F1, 0x8FF6,\n\t0xD3F2, 0x90D6, 0xD3F3, 0x90E0, 0xD3F4, 0x90D9, 0xD3F5, 0x90DA,\n\t0xD3F6, 0x90E3, 0xD3F7, 0x90DF, 0xD3F8, 0x90E5, 0xD3F9, 0x90D8,\n\t0xD3FA, 0x90DB, 0xD3FB, 0x90D7, 0xD3FC, 0x90DC, 0xD3FD, 0x90E4,\n\t0xD3FE, 0x9150, 0xD440, 0x914E, 0xD441, 0x914F, 0xD442, 0x91D5,\n\t0xD443, 0x91E2, 0xD444, 0x91DA, 0xD445, 0x965C, 0xD446, 0x965F,\n\t0xD447, 0x96BC, 0xD448, 0x98E3, 0xD449, 0x9ADF, 0xD44A, 0x9B2F,\n\t0xD44B, 0x4E7F, 0xD44C, 0x5070, 0xD44D, 0x506A, 0xD44E, 0x5061,\n\t0xD44F, 0x505E, 0xD450, 0x5060, 0xD451, 0x5053, 0xD452, 0x504B,\n\t0xD453, 0x505D, 0xD454, 0x5072, 0xD455, 0x5048, 0xD456, 0x504D,\n\t0xD457, 0x5041, 0xD458, 0x505B, 0xD459, 0x504A, 0xD45A, 0x5062,\n\t0xD45B, 0x5015, 0xD45C, 0x5045, 0xD45D, 0x505F, 0xD45E, 0x5069,\n\t0xD45F, 0x506B, 0xD460, 0x5063, 0xD461, 0x5064, 0xD462, 0x5046,\n\t0xD463, 0x5040, 0xD464, 0x506E, 0xD465, 0x5073, 0xD466, 0x5057,\n\t0xD467, 0x5051, 0xD468, 0x51D0, 0xD469, 0x526B, 0xD46A, 0x526D,\n\t0xD46B, 0x526C, 0xD46C, 0x526E, 0xD46D, 0x52D6, 0xD46E, 0x52D3,\n\t0xD46F, 0x532D, 0xD470, 0x539C, 0xD471, 0x5575, 0xD472, 0x5576,\n\t0xD473, 0x553C, 0xD474, 0x554D, 0xD475, 0x5550, 0xD476, 0x5534,\n\t0xD477, 0x552A, 0xD478, 0x5551, 0xD479, 0x5562, 0xD47A, 0x5536,\n\t0xD47B, 0x5535, 0xD47C, 0x5530, 0xD47D, 0x5552, 0xD47E, 0x5545,\n\t0xD4A1, 0x550C, 0xD4A2, 0x5532, 0xD4A3, 0x5565, 0xD4A4, 0x554E,\n\t0xD4A5, 0x5539, 0xD4A6, 0x5548, 0xD4A7, 0x552D, 0xD4A8, 0x553B,\n\t0xD4A9, 0x5540, 0xD4AA, 0x554B, 0xD4AB, 0x570A, 0xD4AC, 0x5707,\n\t0xD4AD, 0x57FB, 0xD4AE, 0x5814, 0xD4AF, 0x57E2, 0xD4B0, 0x57F6,\n\t0xD4B1, 0x57DC, 0xD4B2, 0x57F4, 0xD4B3, 0x5800, 0xD4B4, 0x57ED,\n\t0xD4B5, 0x57FD, 0xD4B6, 0x5808, 0xD4B7, 0x57F8, 0xD4B8, 0x580B,\n\t0xD4B9, 0x57F3, 0xD4BA, 0x57CF, 0xD4BB, 0x5807, 0xD4BC, 0x57EE,\n\t0xD4BD, 0x57E3, 0xD4BE, 0x57F2, 0xD4BF, 0x57E5, 0xD4C0, 0x57EC,\n\t0xD4C1, 0x57E1, 0xD4C2, 0x580E, 0xD4C3, 0x57FC, 0xD4C4, 0x5810,\n\t0xD4C5, 0x57E7, 0xD4C6, 0x5801, 0xD4C7, 0x580C, 0xD4C8, 0x57F1,\n\t0xD4C9, 0x57E9, 0xD4CA, 0x57F0, 0xD4CB, 0x580D, 0xD4CC, 0x5804,\n\t0xD4CD, 0x595C, 0xD4CE, 0x5A60, 0xD4CF, 0x5A58, 0xD4D0, 0x5A55,\n\t0xD4D1, 0x5A67, 0xD4D2, 0x5A5E, 0xD4D3, 0x5A38, 0xD4D4, 0x5A35,\n\t0xD4D5, 0x5A6D, 0xD4D6, 0x5A50, 0xD4D7, 0x5A5F, 0xD4D8, 0x5A65,\n\t0xD4D9, 0x5A6C, 0xD4DA, 0x5A53, 0xD4DB, 0x5A64, 0xD4DC, 0x5A57,\n\t0xD4DD, 0x5A43, 0xD4DE, 0x5A5D, 0xD4DF, 0x5A52, 0xD4E0, 0x5A44,\n\t0xD4E1, 0x5A5B, 0xD4E2, 0x5A48, 0xD4E3, 0x5A8E, 0xD4E4, 0x5A3E,\n\t0xD4E5, 0x5A4D, 0xD4E6, 0x5A39, 0xD4E7, 0x5A4C, 0xD4E8, 0x5A70,\n\t0xD4E9, 0x5A69, 0xD4EA, 0x5A47, 0xD4EB, 0x5A51, 0xD4EC, 0x5A56,\n\t0xD4ED, 0x5A42, 0xD4EE, 0x5A5C, 0xD4EF, 0x5B72, 0xD4F0, 0x5B6E,\n\t0xD4F1, 0x5BC1, 0xD4F2, 0x5BC0, 0xD4F3, 0x5C59, 0xD4F4, 0x5D1E,\n\t0xD4F5, 0x5D0B, 0xD4F6, 0x5D1D, 0xD4F7, 0x5D1A, 0xD4F8, 0x5D20,\n\t0xD4F9, 0x5D0C, 0xD4FA, 0x5D28, 0xD4FB, 0x5D0D, 0xD4FC, 0x5D26,\n\t0xD4FD, 0x5D25, 0xD4FE, 0x5D0F, 0xD540, 0x5D30, 0xD541, 0x5D12,\n\t0xD542, 0x5D23, 0xD543, 0x5D1F, 0xD544, 0x5D2E, 0xD545, 0x5E3E,\n\t0xD546, 0x5E34, 0xD547, 0x5EB1, 0xD548, 0x5EB4, 0xD549, 0x5EB9,\n\t0xD54A, 0x5EB2, 0xD54B, 0x5EB3, 0xD54C, 0x5F36, 0xD54D, 0x5F38,\n\t0xD54E, 0x5F9B, 0xD54F, 0x5F96, 0xD550, 0x5F9F, 0xD551, 0x608A,\n\t0xD552, 0x6090, 0xD553, 0x6086, 0xD554, 0x60BE, 0xD555, 0x60B0,\n\t0xD556, 0x60BA, 0xD557, 0x60D3, 0xD558, 0x60D4, 0xD559, 0x60CF,\n\t0xD55A, 0x60E4, 0xD55B, 0x60D9, 0xD55C, 0x60DD, 0xD55D, 0x60C8,\n\t0xD55E, 0x60B1, 0xD55F, 0x60DB, 0xD560, 0x60B7, 0xD561, 0x60CA,\n\t0xD562, 0x60BF, 0xD563, 0x60C3, 0xD564, 0x60CD, 0xD565, 0x60C0,\n\t0xD566, 0x6332, 0xD567, 0x6365, 0xD568, 0x638A, 0xD569, 0x6382,\n\t0xD56A, 0x637D, 0xD56B, 0x63BD, 0xD56C, 0x639E, 0xD56D, 0x63AD,\n\t0xD56E, 0x639D, 0xD56F, 0x6397, 0xD570, 0x63AB, 0xD571, 0x638E,\n\t0xD572, 0x636F, 0xD573, 0x6387, 0xD574, 0x6390, 0xD575, 0x636E,\n\t0xD576, 0x63AF, 0xD577, 0x6375, 0xD578, 0x639C, 0xD579, 0x636D,\n\t0xD57A, 0x63AE, 0xD57B, 0x637C, 0xD57C, 0x63A4, 0xD57D, 0x633B,\n\t0xD57E, 0x639F, 0xD5A1, 0x6378, 0xD5A2, 0x6385, 0xD5A3, 0x6381,\n\t0xD5A4, 0x6391, 0xD5A5, 0x638D, 0xD5A6, 0x6370, 0xD5A7, 0x6553,\n\t0xD5A8, 0x65CD, 0xD5A9, 0x6665, 0xD5AA, 0x6661, 0xD5AB, 0x665B,\n\t0xD5AC, 0x6659, 0xD5AD, 0x665C, 0xD5AE, 0x6662, 0xD5AF, 0x6718,\n\t0xD5B0, 0x6879, 0xD5B1, 0x6887, 0xD5B2, 0x6890, 0xD5B3, 0x689C,\n\t0xD5B4, 0x686D, 0xD5B5, 0x686E, 0xD5B6, 0x68AE, 0xD5B7, 0x68AB,\n\t0xD5B8, 0x6956, 0xD5B9, 0x686F, 0xD5BA, 0x68A3, 0xD5BB, 0x68AC,\n\t0xD5BC, 0x68A9, 0xD5BD, 0x6875, 0xD5BE, 0x6874, 0xD5BF, 0x68B2,\n\t0xD5C0, 0x688F, 0xD5C1, 0x6877, 0xD5C2, 0x6892, 0xD5C3, 0x687C,\n\t0xD5C4, 0x686B, 0xD5C5, 0x6872, 0xD5C6, 0x68AA, 0xD5C7, 0x6880,\n\t0xD5C8, 0x6871, 0xD5C9, 0x687E, 0xD5CA, 0x689B, 0xD5CB, 0x6896,\n\t0xD5CC, 0x688B, 0xD5CD, 0x68A0, 0xD5CE, 0x6889, 0xD5CF, 0x68A4,\n\t0xD5D0, 0x6878, 0xD5D1, 0x687B, 0xD5D2, 0x6891, 0xD5D3, 0x688C,\n\t0xD5D4, 0x688A, 0xD5D5, 0x687D, 0xD5D6, 0x6B36, 0xD5D7, 0x6B33,\n\t0xD5D8, 0x6B37, 0xD5D9, 0x6B38, 0xD5DA, 0x6B91, 0xD5DB, 0x6B8F,\n\t0xD5DC, 0x6B8D, 0xD5DD, 0x6B8E, 0xD5DE, 0x6B8C, 0xD5DF, 0x6C2A,\n\t0xD5E0, 0x6DC0, 0xD5E1, 0x6DAB, 0xD5E2, 0x6DB4, 0xD5E3, 0x6DB3,\n\t0xD5E4, 0x6E74, 0xD5E5, 0x6DAC, 0xD5E6, 0x6DE9, 0xD5E7, 0x6DE2,\n\t0xD5E8, 0x6DB7, 0xD5E9, 0x6DF6, 0xD5EA, 0x6DD4, 0xD5EB, 0x6E00,\n\t0xD5EC, 0x6DC8, 0xD5ED, 0x6DE0, 0xD5EE, 0x6DDF, 0xD5EF, 0x6DD6,\n\t0xD5F0, 0x6DBE, 0xD5F1, 0x6DE5, 0xD5F2, 0x6DDC, 0xD5F3, 0x6DDD,\n\t0xD5F4, 0x6DDB, 0xD5F5, 0x6DF4, 0xD5F6, 0x6DCA, 0xD5F7, 0x6DBD,\n\t0xD5F8, 0x6DED, 0xD5F9, 0x6DF0, 0xD5FA, 0x6DBA, 0xD5FB, 0x6DD5,\n\t0xD5FC, 0x6DC2, 0xD5FD, 0x6DCF, 0xD5FE, 0x6DC9, 0xD640, 0x6DD0,\n\t0xD641, 0x6DF2, 0xD642, 0x6DD3, 0xD643, 0x6DFD, 0xD644, 0x6DD7,\n\t0xD645, 0x6DCD, 0xD646, 0x6DE3, 0xD647, 0x6DBB, 0xD648, 0x70FA,\n\t0xD649, 0x710D, 0xD64A, 0x70F7, 0xD64B, 0x7117, 0xD64C, 0x70F4,\n\t0xD64D, 0x710C, 0xD64E, 0x70F0, 0xD64F, 0x7104, 0xD650, 0x70F3,\n\t0xD651, 0x7110, 0xD652, 0x70FC, 0xD653, 0x70FF, 0xD654, 0x7106,\n\t0xD655, 0x7113, 0xD656, 0x7100, 0xD657, 0x70F8, 0xD658, 0x70F6,\n\t0xD659, 0x710B, 0xD65A, 0x7102, 0xD65B, 0x710E, 0xD65C, 0x727E,\n\t0xD65D, 0x727B, 0xD65E, 0x727C, 0xD65F, 0x727F, 0xD660, 0x731D,\n\t0xD661, 0x7317, 0xD662, 0x7307, 0xD663, 0x7311, 0xD664, 0x7318,\n\t0xD665, 0x730A, 0xD666, 0x7308, 0xD667, 0x72FF, 0xD668, 0x730F,\n\t0xD669, 0x731E, 0xD66A, 0x7388, 0xD66B, 0x73F6, 0xD66C, 0x73F8,\n\t0xD66D, 0x73F5, 0xD66E, 0x7404, 0xD66F, 0x7401, 0xD670, 0x73FD,\n\t0xD671, 0x7407, 0xD672, 0x7400, 0xD673, 0x73FA, 0xD674, 0x73FC,\n\t0xD675, 0x73FF, 0xD676, 0x740C, 0xD677, 0x740B, 0xD678, 0x73F4,\n\t0xD679, 0x7408, 0xD67A, 0x7564, 0xD67B, 0x7563, 0xD67C, 0x75CE,\n\t0xD67D, 0x75D2, 0xD67E, 0x75CF, 0xD6A1, 0x75CB, 0xD6A2, 0x75CC,\n\t0xD6A3, 0x75D1, 0xD6A4, 0x75D0, 0xD6A5, 0x768F, 0xD6A6, 0x7689,\n\t0xD6A7, 0x76D3, 0xD6A8, 0x7739, 0xD6A9, 0x772F, 0xD6AA, 0x772D,\n\t0xD6AB, 0x7731, 0xD6AC, 0x7732, 0xD6AD, 0x7734, 0xD6AE, 0x7733,\n\t0xD6AF, 0x773D, 0xD6B0, 0x7725, 0xD6B1, 0x773B, 0xD6B2, 0x7735,\n\t0xD6B3, 0x7848, 0xD6B4, 0x7852, 0xD6B5, 0x7849, 0xD6B6, 0x784D,\n\t0xD6B7, 0x784A, 0xD6B8, 0x784C, 0xD6B9, 0x7826, 0xD6BA, 0x7845,\n\t0xD6BB, 0x7850, 0xD6BC, 0x7964, 0xD6BD, 0x7967, 0xD6BE, 0x7969,\n\t0xD6BF, 0x796A, 0xD6C0, 0x7963, 0xD6C1, 0x796B, 0xD6C2, 0x7961,\n\t0xD6C3, 0x79BB, 0xD6C4, 0x79FA, 0xD6C5, 0x79F8, 0xD6C6, 0x79F6,\n\t0xD6C7, 0x79F7, 0xD6C8, 0x7A8F, 0xD6C9, 0x7A94, 0xD6CA, 0x7A90,\n\t0xD6CB, 0x7B35, 0xD6CC, 0x7B47, 0xD6CD, 0x7B34, 0xD6CE, 0x7B25,\n\t0xD6CF, 0x7B30, 0xD6D0, 0x7B22, 0xD6D1, 0x7B24, 0xD6D2, 0x7B33,\n\t0xD6D3, 0x7B18, 0xD6D4, 0x7B2A, 0xD6D5, 0x7B1D, 0xD6D6, 0x7B31,\n\t0xD6D7, 0x7B2B, 0xD6D8, 0x7B2D, 0xD6D9, 0x7B2F, 0xD6DA, 0x7B32,\n\t0xD6DB, 0x7B38, 0xD6DC, 0x7B1A, 0xD6DD, 0x7B23, 0xD6DE, 0x7C94,\n\t0xD6DF, 0x7C98, 0xD6E0, 0x7C96, 0xD6E1, 0x7CA3, 0xD6E2, 0x7D35,\n\t0xD6E3, 0x7D3D, 0xD6E4, 0x7D38, 0xD6E5, 0x7D36, 0xD6E6, 0x7D3A,\n\t0xD6E7, 0x7D45, 0xD6E8, 0x7D2C, 0xD6E9, 0x7D29, 0xD6EA, 0x7D41,\n\t0xD6EB, 0x7D47, 0xD6EC, 0x7D3E, 0xD6ED, 0x7D3F, 0xD6EE, 0x7D4A,\n\t0xD6EF, 0x7D3B, 0xD6F0, 0x7D28, 0xD6F1, 0x7F63, 0xD6F2, 0x7F95,\n\t0xD6F3, 0x7F9C, 0xD6F4, 0x7F9D, 0xD6F5, 0x7F9B, 0xD6F6, 0x7FCA,\n\t0xD6F7, 0x7FCB, 0xD6F8, 0x7FCD, 0xD6F9, 0x7FD0, 0xD6FA, 0x7FD1,\n\t0xD6FB, 0x7FC7, 0xD6FC, 0x7FCF, 0xD6FD, 0x7FC9, 0xD6FE, 0x801F,\n\t0xD740, 0x801E, 0xD741, 0x801B, 0xD742, 0x8047, 0xD743, 0x8043,\n\t0xD744, 0x8048, 0xD745, 0x8118, 0xD746, 0x8125, 0xD747, 0x8119,\n\t0xD748, 0x811B, 0xD749, 0x812D, 0xD74A, 0x811F, 0xD74B, 0x812C,\n\t0xD74C, 0x811E, 0xD74D, 0x8121, 0xD74E, 0x8115, 0xD74F, 0x8127,\n\t0xD750, 0x811D, 0xD751, 0x8122, 0xD752, 0x8211, 0xD753, 0x8238,\n\t0xD754, 0x8233, 0xD755, 0x823A, 0xD756, 0x8234, 0xD757, 0x8232,\n\t0xD758, 0x8274, 0xD759, 0x8390, 0xD75A, 0x83A3, 0xD75B, 0x83A8,\n\t0xD75C, 0x838D, 0xD75D, 0x837A, 0xD75E, 0x8373, 0xD75F, 0x83A4,\n\t0xD760, 0x8374, 0xD761, 0x838F, 0xD762, 0x8381, 0xD763, 0x8395,\n\t0xD764, 0x8399, 0xD765, 0x8375, 0xD766, 0x8394, 0xD767, 0x83A9,\n\t0xD768, 0x837D, 0xD769, 0x8383, 0xD76A, 0x838C, 0xD76B, 0x839D,\n\t0xD76C, 0x839B, 0xD76D, 0x83AA, 0xD76E, 0x838B, 0xD76F, 0x837E,\n\t0xD770, 0x83A5, 0xD771, 0x83AF, 0xD772, 0x8388, 0xD773, 0x8397,\n\t0xD774, 0x83B0, 0xD775, 0x837F, 0xD776, 0x83A6, 0xD777, 0x8387,\n\t0xD778, 0x83AE, 0xD779, 0x8376, 0xD77A, 0x839A, 0xD77B, 0x8659,\n\t0xD77C, 0x8656, 0xD77D, 0x86BF, 0xD77E, 0x86B7, 0xD7A1, 0x86C2,\n\t0xD7A2, 0x86C1, 0xD7A3, 0x86C5, 0xD7A4, 0x86BA, 0xD7A5, 0x86B0,\n\t0xD7A6, 0x86C8, 0xD7A7, 0x86B9, 0xD7A8, 0x86B3, 0xD7A9, 0x86B8,\n\t0xD7AA, 0x86CC, 0xD7AB, 0x86B4, 0xD7AC, 0x86BB, 0xD7AD, 0x86BC,\n\t0xD7AE, 0x86C3, 0xD7AF, 0x86BD, 0xD7B0, 0x86BE, 0xD7B1, 0x8852,\n\t0xD7B2, 0x8889, 0xD7B3, 0x8895, 0xD7B4, 0x88A8, 0xD7B5, 0x88A2,\n\t0xD7B6, 0x88AA, 0xD7B7, 0x889A, 0xD7B8, 0x8891, 0xD7B9, 0x88A1,\n\t0xD7BA, 0x889F, 0xD7BB, 0x8898, 0xD7BC, 0x88A7, 0xD7BD, 0x8899,\n\t0xD7BE, 0x889B, 0xD7BF, 0x8897, 0xD7C0, 0x88A4, 0xD7C1, 0x88AC,\n\t0xD7C2, 0x888C, 0xD7C3, 0x8893, 0xD7C4, 0x888E, 0xD7C5, 0x8982,\n\t0xD7C6, 0x89D6, 0xD7C7, 0x89D9, 0xD7C8, 0x89D5, 0xD7C9, 0x8A30,\n\t0xD7CA, 0x8A27, 0xD7CB, 0x8A2C, 0xD7CC, 0x8A1E, 0xD7CD, 0x8C39,\n\t0xD7CE, 0x8C3B, 0xD7CF, 0x8C5C, 0xD7D0, 0x8C5D, 0xD7D1, 0x8C7D,\n\t0xD7D2, 0x8CA5, 0xD7D3, 0x8D7D, 0xD7D4, 0x8D7B, 0xD7D5, 0x8D79,\n\t0xD7D6, 0x8DBC, 0xD7D7, 0x8DC2, 0xD7D8, 0x8DB9, 0xD7D9, 0x8DBF,\n\t0xD7DA, 0x8DC1, 0xD7DB, 0x8ED8, 0xD7DC, 0x8EDE, 0xD7DD, 0x8EDD,\n\t0xD7DE, 0x8EDC, 0xD7DF, 0x8ED7, 0xD7E0, 0x8EE0, 0xD7E1, 0x8EE1,\n\t0xD7E2, 0x9024, 0xD7E3, 0x900B, 0xD7E4, 0x9011, 0xD7E5, 0x901C,\n\t0xD7E6, 0x900C, 0xD7E7, 0x9021, 0xD7E8, 0x90EF, 0xD7E9, 0x90EA,\n\t0xD7EA, 0x90F0, 0xD7EB, 0x90F4, 0xD7EC, 0x90F2, 0xD7ED, 0x90F3,\n\t0xD7EE, 0x90D4, 0xD7EF, 0x90EB, 0xD7F0, 0x90EC, 0xD7F1, 0x90E9,\n\t0xD7F2, 0x9156, 0xD7F3, 0x9158, 0xD7F4, 0x915A, 0xD7F5, 0x9153,\n\t0xD7F6, 0x9155, 0xD7F7, 0x91EC, 0xD7F8, 0x91F4, 0xD7F9, 0x91F1,\n\t0xD7FA, 0x91F3, 0xD7FB, 0x91F8, 0xD7FC, 0x91E4, 0xD7FD, 0x91F9,\n\t0xD7FE, 0x91EA, 0xD840, 0x91EB, 0xD841, 0x91F7, 0xD842, 0x91E8,\n\t0xD843, 0x91EE, 0xD844, 0x957A, 0xD845, 0x9586, 0xD846, 0x9588,\n\t0xD847, 0x967C, 0xD848, 0x966D, 0xD849, 0x966B, 0xD84A, 0x9671,\n\t0xD84B, 0x966F, 0xD84C, 0x96BF, 0xD84D, 0x976A, 0xD84E, 0x9804,\n\t0xD84F, 0x98E5, 0xD850, 0x9997, 0xD851, 0x509B, 0xD852, 0x5095,\n\t0xD853, 0x5094, 0xD854, 0x509E, 0xD855, 0x508B, 0xD856, 0x50A3,\n\t0xD857, 0x5083, 0xD858, 0x508C, 0xD859, 0x508E, 0xD85A, 0x509D,\n\t0xD85B, 0x5068, 0xD85C, 0x509C, 0xD85D, 0x5092, 0xD85E, 0x5082,\n\t0xD85F, 0x5087, 0xD860, 0x515F, 0xD861, 0x51D4, 0xD862, 0x5312,\n\t0xD863, 0x5311, 0xD864, 0x53A4, 0xD865, 0x53A7, 0xD866, 0x5591,\n\t0xD867, 0x55A8, 0xD868, 0x55A5, 0xD869, 0x55AD, 0xD86A, 0x5577,\n\t0xD86B, 0x5645, 0xD86C, 0x55A2, 0xD86D, 0x5593, 0xD86E, 0x5588,\n\t0xD86F, 0x558F, 0xD870, 0x55B5, 0xD871, 0x5581, 0xD872, 0x55A3,\n\t0xD873, 0x5592, 0xD874, 0x55A4, 0xD875, 0x557D, 0xD876, 0x558C,\n\t0xD877, 0x55A6, 0xD878, 0x557F, 0xD879, 0x5595, 0xD87A, 0x55A1,\n\t0xD87B, 0x558E, 0xD87C, 0x570C, 0xD87D, 0x5829, 0xD87E, 0x5837,\n\t0xD8A1, 0x5819, 0xD8A2, 0x581E, 0xD8A3, 0x5827, 0xD8A4, 0x5823,\n\t0xD8A5, 0x5828, 0xD8A6, 0x57F5, 0xD8A7, 0x5848, 0xD8A8, 0x5825,\n\t0xD8A9, 0x581C, 0xD8AA, 0x581B, 0xD8AB, 0x5833, 0xD8AC, 0x583F,\n\t0xD8AD, 0x5836, 0xD8AE, 0x582E, 0xD8AF, 0x5839, 0xD8B0, 0x5838,\n\t0xD8B1, 0x582D, 0xD8B2, 0x582C, 0xD8B3, 0x583B, 0xD8B4, 0x5961,\n\t0xD8B5, 0x5AAF, 0xD8B6, 0x5A94, 0xD8B7, 0x5A9F, 0xD8B8, 0x5A7A,\n\t0xD8B9, 0x5AA2, 0xD8BA, 0x5A9E, 0xD8BB, 0x5A78, 0xD8BC, 0x5AA6,\n\t0xD8BD, 0x5A7C, 0xD8BE, 0x5AA5, 0xD8BF, 0x5AAC, 0xD8C0, 0x5A95,\n\t0xD8C1, 0x5AAE, 0xD8C2, 0x5A37, 0xD8C3, 0x5A84, 0xD8C4, 0x5A8A,\n\t0xD8C5, 0x5A97, 0xD8C6, 0x5A83, 0xD8C7, 0x5A8B, 0xD8C8, 0x5AA9,\n\t0xD8C9, 0x5A7B, 0xD8CA, 0x5A7D, 0xD8CB, 0x5A8C, 0xD8CC, 0x5A9C,\n\t0xD8CD, 0x5A8F, 0xD8CE, 0x5A93, 0xD8CF, 0x5A9D, 0xD8D0, 0x5BEA,\n\t0xD8D1, 0x5BCD, 0xD8D2, 0x5BCB, 0xD8D3, 0x5BD4, 0xD8D4, 0x5BD1,\n\t0xD8D5, 0x5BCA, 0xD8D6, 0x5BCE, 0xD8D7, 0x5C0C, 0xD8D8, 0x5C30,\n\t0xD8D9, 0x5D37, 0xD8DA, 0x5D43, 0xD8DB, 0x5D6B, 0xD8DC, 0x5D41,\n\t0xD8DD, 0x5D4B, 0xD8DE, 0x5D3F, 0xD8DF, 0x5D35, 0xD8E0, 0x5D51,\n\t0xD8E1, 0x5D4E, 0xD8E2, 0x5D55, 0xD8E3, 0x5D33, 0xD8E4, 0x5D3A,\n\t0xD8E5, 0x5D52, 0xD8E6, 0x5D3D, 0xD8E7, 0x5D31, 0xD8E8, 0x5D59,\n\t0xD8E9, 0x5D42, 0xD8EA, 0x5D39, 0xD8EB, 0x5D49, 0xD8EC, 0x5D38,\n\t0xD8ED, 0x5D3C, 0xD8EE, 0x5D32, 0xD8EF, 0x5D36, 0xD8F0, 0x5D40,\n\t0xD8F1, 0x5D45, 0xD8F2, 0x5E44, 0xD8F3, 0x5E41, 0xD8F4, 0x5F58,\n\t0xD8F5, 0x5FA6, 0xD8F6, 0x5FA5, 0xD8F7, 0x5FAB, 0xD8F8, 0x60C9,\n\t0xD8F9, 0x60B9, 0xD8FA, 0x60CC, 0xD8FB, 0x60E2, 0xD8FC, 0x60CE,\n\t0xD8FD, 0x60C4, 0xD8FE, 0x6114, 0xD940, 0x60F2, 0xD941, 0x610A,\n\t0xD942, 0x6116, 0xD943, 0x6105, 0xD944, 0x60F5, 0xD945, 0x6113,\n\t0xD946, 0x60F8, 0xD947, 0x60FC, 0xD948, 0x60FE, 0xD949, 0x60C1,\n\t0xD94A, 0x6103, 0xD94B, 0x6118, 0xD94C, 0x611D, 0xD94D, 0x6110,\n\t0xD94E, 0x60FF, 0xD94F, 0x6104, 0xD950, 0x610B, 0xD951, 0x624A,\n\t0xD952, 0x6394, 0xD953, 0x63B1, 0xD954, 0x63B0, 0xD955, 0x63CE,\n\t0xD956, 0x63E5, 0xD957, 0x63E8, 0xD958, 0x63EF, 0xD959, 0x63C3,\n\t0xD95A, 0x649D, 0xD95B, 0x63F3, 0xD95C, 0x63CA, 0xD95D, 0x63E0,\n\t0xD95E, 0x63F6, 0xD95F, 0x63D5, 0xD960, 0x63F2, 0xD961, 0x63F5,\n\t0xD962, 0x6461, 0xD963, 0x63DF, 0xD964, 0x63BE, 0xD965, 0x63DD,\n\t0xD966, 0x63DC, 0xD967, 0x63C4, 0xD968, 0x63D8, 0xD969, 0x63D3,\n\t0xD96A, 0x63C2, 0xD96B, 0x63C7, 0xD96C, 0x63CC, 0xD96D, 0x63CB,\n\t0xD96E, 0x63C8, 0xD96F, 0x63F0, 0xD970, 0x63D7, 0xD971, 0x63D9,\n\t0xD972, 0x6532, 0xD973, 0x6567, 0xD974, 0x656A, 0xD975, 0x6564,\n\t0xD976, 0x655C, 0xD977, 0x6568, 0xD978, 0x6565, 0xD979, 0x658C,\n\t0xD97A, 0x659D, 0xD97B, 0x659E, 0xD97C, 0x65AE, 0xD97D, 0x65D0,\n\t0xD97E, 0x65D2, 0xD9A1, 0x667C, 0xD9A2, 0x666C, 0xD9A3, 0x667B,\n\t0xD9A4, 0x6680, 0xD9A5, 0x6671, 0xD9A6, 0x6679, 0xD9A7, 0x666A,\n\t0xD9A8, 0x6672, 0xD9A9, 0x6701, 0xD9AA, 0x690C, 0xD9AB, 0x68D3,\n\t0xD9AC, 0x6904, 0xD9AD, 0x68DC, 0xD9AE, 0x692A, 0xD9AF, 0x68EC,\n\t0xD9B0, 0x68EA, 0xD9B1, 0x68F1, 0xD9B2, 0x690F, 0xD9B3, 0x68D6,\n\t0xD9B4, 0x68F7, 0xD9B5, 0x68EB, 0xD9B6, 0x68E4, 0xD9B7, 0x68F6,\n\t0xD9B8, 0x6913, 0xD9B9, 0x6910, 0xD9BA, 0x68F3, 0xD9BB, 0x68E1,\n\t0xD9BC, 0x6907, 0xD9BD, 0x68CC, 0xD9BE, 0x6908, 0xD9BF, 0x6970,\n\t0xD9C0, 0x68B4, 0xD9C1, 0x6911, 0xD9C2, 0x68EF, 0xD9C3, 0x68C6,\n\t0xD9C4, 0x6914, 0xD9C5, 0x68F8, 0xD9C6, 0x68D0, 0xD9C7, 0x68FD,\n\t0xD9C8, 0x68FC, 0xD9C9, 0x68E8, 0xD9CA, 0x690B, 0xD9CB, 0x690A,\n\t0xD9CC, 0x6917, 0xD9CD, 0x68CE, 0xD9CE, 0x68C8, 0xD9CF, 0x68DD,\n\t0xD9D0, 0x68DE, 0xD9D1, 0x68E6, 0xD9D2, 0x68F4, 0xD9D3, 0x68D1,\n\t0xD9D4, 0x6906, 0xD9D5, 0x68D4, 0xD9D6, 0x68E9, 0xD9D7, 0x6915,\n\t0xD9D8, 0x6925, 0xD9D9, 0x68C7, 0xD9DA, 0x6B39, 0xD9DB, 0x6B3B,\n\t0xD9DC, 0x6B3F, 0xD9DD, 0x6B3C, 0xD9DE, 0x6B94, 0xD9DF, 0x6B97,\n\t0xD9E0, 0x6B99, 0xD9E1, 0x6B95, 0xD9E2, 0x6BBD, 0xD9E3, 0x6BF0,\n\t0xD9E4, 0x6BF2, 0xD9E5, 0x6BF3, 0xD9E6, 0x6C30, 0xD9E7, 0x6DFC,\n\t0xD9E8, 0x6E46, 0xD9E9, 0x6E47, 0xD9EA, 0x6E1F, 0xD9EB, 0x6E49,\n\t0xD9EC, 0x6E88, 0xD9ED, 0x6E3C, 0xD9EE, 0x6E3D, 0xD9EF, 0x6E45,\n\t0xD9F0, 0x6E62, 0xD9F1, 0x6E2B, 0xD9F2, 0x6E3F, 0xD9F3, 0x6E41,\n\t0xD9F4, 0x6E5D, 0xD9F5, 0x6E73, 0xD9F6, 0x6E1C, 0xD9F7, 0x6E33,\n\t0xD9F8, 0x6E4B, 0xD9F9, 0x6E40, 0xD9FA, 0x6E51, 0xD9FB, 0x6E3B,\n\t0xD9FC, 0x6E03, 0xD9FD, 0x6E2E, 0xD9FE, 0x6E5E, 0xDA40, 0x6E68,\n\t0xDA41, 0x6E5C, 0xDA42, 0x6E61, 0xDA43, 0x6E31, 0xDA44, 0x6E28,\n\t0xDA45, 0x6E60, 0xDA46, 0x6E71, 0xDA47, 0x6E6B, 0xDA48, 0x6E39,\n\t0xDA49, 0x6E22, 0xDA4A, 0x6E30, 0xDA4B, 0x6E53, 0xDA4C, 0x6E65,\n\t0xDA4D, 0x6E27, 0xDA4E, 0x6E78, 0xDA4F, 0x6E64, 0xDA50, 0x6E77,\n\t0xDA51, 0x6E55, 0xDA52, 0x6E79, 0xDA53, 0x6E52, 0xDA54, 0x6E66,\n\t0xDA55, 0x6E35, 0xDA56, 0x6E36, 0xDA57, 0x6E5A, 0xDA58, 0x7120,\n\t0xDA59, 0x711E, 0xDA5A, 0x712F, 0xDA5B, 0x70FB, 0xDA5C, 0x712E,\n\t0xDA5D, 0x7131, 0xDA5E, 0x7123, 0xDA5F, 0x7125, 0xDA60, 0x7122,\n\t0xDA61, 0x7132, 0xDA62, 0x711F, 0xDA63, 0x7128, 0xDA64, 0x713A,\n\t0xDA65, 0x711B, 0xDA66, 0x724B, 0xDA67, 0x725A, 0xDA68, 0x7288,\n\t0xDA69, 0x7289, 0xDA6A, 0x7286, 0xDA6B, 0x7285, 0xDA6C, 0x728B,\n\t0xDA6D, 0x7312, 0xDA6E, 0x730B, 0xDA6F, 0x7330, 0xDA70, 0x7322,\n\t0xDA71, 0x7331, 0xDA72, 0x7333, 0xDA73, 0x7327, 0xDA74, 0x7332,\n\t0xDA75, 0x732D, 0xDA76, 0x7326, 0xDA77, 0x7323, 0xDA78, 0x7335,\n\t0xDA79, 0x730C, 0xDA7A, 0x742E, 0xDA7B, 0x742C, 0xDA7C, 0x7430,\n\t0xDA7D, 0x742B, 0xDA7E, 0x7416, 0xDAA1, 0x741A, 0xDAA2, 0x7421,\n\t0xDAA3, 0x742D, 0xDAA4, 0x7431, 0xDAA5, 0x7424, 0xDAA6, 0x7423,\n\t0xDAA7, 0x741D, 0xDAA8, 0x7429, 0xDAA9, 0x7420, 0xDAAA, 0x7432,\n\t0xDAAB, 0x74FB, 0xDAAC, 0x752F, 0xDAAD, 0x756F, 0xDAAE, 0x756C,\n\t0xDAAF, 0x75E7, 0xDAB0, 0x75DA, 0xDAB1, 0x75E1, 0xDAB2, 0x75E6,\n\t0xDAB3, 0x75DD, 0xDAB4, 0x75DF, 0xDAB5, 0x75E4, 0xDAB6, 0x75D7,\n\t0xDAB7, 0x7695, 0xDAB8, 0x7692, 0xDAB9, 0x76DA, 0xDABA, 0x7746,\n\t0xDABB, 0x7747, 0xDABC, 0x7744, 0xDABD, 0x774D, 0xDABE, 0x7745,\n\t0xDABF, 0x774A, 0xDAC0, 0x774E, 0xDAC1, 0x774B, 0xDAC2, 0x774C,\n\t0xDAC3, 0x77DE, 0xDAC4, 0x77EC, 0xDAC5, 0x7860, 0xDAC6, 0x7864,\n\t0xDAC7, 0x7865, 0xDAC8, 0x785C, 0xDAC9, 0x786D, 0xDACA, 0x7871,\n\t0xDACB, 0x786A, 0xDACC, 0x786E, 0xDACD, 0x7870, 0xDACE, 0x7869,\n\t0xDACF, 0x7868, 0xDAD0, 0x785E, 0xDAD1, 0x7862, 0xDAD2, 0x7974,\n\t0xDAD3, 0x7973, 0xDAD4, 0x7972, 0xDAD5, 0x7970, 0xDAD6, 0x7A02,\n\t0xDAD7, 0x7A0A, 0xDAD8, 0x7A03, 0xDAD9, 0x7A0C, 0xDADA, 0x7A04,\n\t0xDADB, 0x7A99, 0xDADC, 0x7AE6, 0xDADD, 0x7AE4, 0xDADE, 0x7B4A,\n\t0xDADF, 0x7B3B, 0xDAE0, 0x7B44, 0xDAE1, 0x7B48, 0xDAE2, 0x7B4C,\n\t0xDAE3, 0x7B4E, 0xDAE4, 0x7B40, 0xDAE5, 0x7B58, 0xDAE6, 0x7B45,\n\t0xDAE7, 0x7CA2, 0xDAE8, 0x7C9E, 0xDAE9, 0x7CA8, 0xDAEA, 0x7CA1,\n\t0xDAEB, 0x7D58, 0xDAEC, 0x7D6F, 0xDAED, 0x7D63, 0xDAEE, 0x7D53,\n\t0xDAEF, 0x7D56, 0xDAF0, 0x7D67, 0xDAF1, 0x7D6A, 0xDAF2, 0x7D4F,\n\t0xDAF3, 0x7D6D, 0xDAF4, 0x7D5C, 0xDAF5, 0x7D6B, 0xDAF6, 0x7D52,\n\t0xDAF7, 0x7D54, 0xDAF8, 0x7D69, 0xDAF9, 0x7D51, 0xDAFA, 0x7D5F,\n\t0xDAFB, 0x7D4E, 0xDAFC, 0x7F3E, 0xDAFD, 0x7F3F, 0xDAFE, 0x7F65,\n\t0xDB40, 0x7F66, 0xDB41, 0x7FA2, 0xDB42, 0x7FA0, 0xDB43, 0x7FA1,\n\t0xDB44, 0x7FD7, 0xDB45, 0x8051, 0xDB46, 0x804F, 0xDB47, 0x8050,\n\t0xDB48, 0x80FE, 0xDB49, 0x80D4, 0xDB4A, 0x8143, 0xDB4B, 0x814A,\n\t0xDB4C, 0x8152, 0xDB4D, 0x814F, 0xDB4E, 0x8147, 0xDB4F, 0x813D,\n\t0xDB50, 0x814D, 0xDB51, 0x813A, 0xDB52, 0x81E6, 0xDB53, 0x81EE,\n\t0xDB54, 0x81F7, 0xDB55, 0x81F8, 0xDB56, 0x81F9, 0xDB57, 0x8204,\n\t0xDB58, 0x823C, 0xDB59, 0x823D, 0xDB5A, 0x823F, 0xDB5B, 0x8275,\n\t0xDB5C, 0x833B, 0xDB5D, 0x83CF, 0xDB5E, 0x83F9, 0xDB5F, 0x8423,\n\t0xDB60, 0x83C0, 0xDB61, 0x83E8, 0xDB62, 0x8412, 0xDB63, 0x83E7,\n\t0xDB64, 0x83E4, 0xDB65, 0x83FC, 0xDB66, 0x83F6, 0xDB67, 0x8410,\n\t0xDB68, 0x83C6, 0xDB69, 0x83C8, 0xDB6A, 0x83EB, 0xDB6B, 0x83E3,\n\t0xDB6C, 0x83BF, 0xDB6D, 0x8401, 0xDB6E, 0x83DD, 0xDB6F, 0x83E5,\n\t0xDB70, 0x83D8, 0xDB71, 0x83FF, 0xDB72, 0x83E1, 0xDB73, 0x83CB,\n\t0xDB74, 0x83CE, 0xDB75, 0x83D6, 0xDB76, 0x83F5, 0xDB77, 0x83C9,\n\t0xDB78, 0x8409, 0xDB79, 0x840F, 0xDB7A, 0x83DE, 0xDB7B, 0x8411,\n\t0xDB7C, 0x8406, 0xDB7D, 0x83C2, 0xDB7E, 0x83F3, 0xDBA1, 0x83D5,\n\t0xDBA2, 0x83FA, 0xDBA3, 0x83C7, 0xDBA4, 0x83D1, 0xDBA5, 0x83EA,\n\t0xDBA6, 0x8413, 0xDBA7, 0x83C3, 0xDBA8, 0x83EC, 0xDBA9, 0x83EE,\n\t0xDBAA, 0x83C4, 0xDBAB, 0x83FB, 0xDBAC, 0x83D7, 0xDBAD, 0x83E2,\n\t0xDBAE, 0x841B, 0xDBAF, 0x83DB, 0xDBB0, 0x83FE, 0xDBB1, 0x86D8,\n\t0xDBB2, 0x86E2, 0xDBB3, 0x86E6, 0xDBB4, 0x86D3, 0xDBB5, 0x86E3,\n\t0xDBB6, 0x86DA, 0xDBB7, 0x86EA, 0xDBB8, 0x86DD, 0xDBB9, 0x86EB,\n\t0xDBBA, 0x86DC, 0xDBBB, 0x86EC, 0xDBBC, 0x86E9, 0xDBBD, 0x86D7,\n\t0xDBBE, 0x86E8, 0xDBBF, 0x86D1, 0xDBC0, 0x8848, 0xDBC1, 0x8856,\n\t0xDBC2, 0x8855, 0xDBC3, 0x88BA, 0xDBC4, 0x88D7, 0xDBC5, 0x88B9,\n\t0xDBC6, 0x88B8, 0xDBC7, 0x88C0, 0xDBC8, 0x88BE, 0xDBC9, 0x88B6,\n\t0xDBCA, 0x88BC, 0xDBCB, 0x88B7, 0xDBCC, 0x88BD, 0xDBCD, 0x88B2,\n\t0xDBCE, 0x8901, 0xDBCF, 0x88C9, 0xDBD0, 0x8995, 0xDBD1, 0x8998,\n\t0xDBD2, 0x8997, 0xDBD3, 0x89DD, 0xDBD4, 0x89DA, 0xDBD5, 0x89DB,\n\t0xDBD6, 0x8A4E, 0xDBD7, 0x8A4D, 0xDBD8, 0x8A39, 0xDBD9, 0x8A59,\n\t0xDBDA, 0x8A40, 0xDBDB, 0x8A57, 0xDBDC, 0x8A58, 0xDBDD, 0x8A44,\n\t0xDBDE, 0x8A45, 0xDBDF, 0x8A52, 0xDBE0, 0x8A48, 0xDBE1, 0x8A51,\n\t0xDBE2, 0x8A4A, 0xDBE3, 0x8A4C, 0xDBE4, 0x8A4F, 0xDBE5, 0x8C5F,\n\t0xDBE6, 0x8C81, 0xDBE7, 0x8C80, 0xDBE8, 0x8CBA, 0xDBE9, 0x8CBE,\n\t0xDBEA, 0x8CB0, 0xDBEB, 0x8CB9, 0xDBEC, 0x8CB5, 0xDBED, 0x8D84,\n\t0xDBEE, 0x8D80, 0xDBEF, 0x8D89, 0xDBF0, 0x8DD8, 0xDBF1, 0x8DD3,\n\t0xDBF2, 0x8DCD, 0xDBF3, 0x8DC7, 0xDBF4, 0x8DD6, 0xDBF5, 0x8DDC,\n\t0xDBF6, 0x8DCF, 0xDBF7, 0x8DD5, 0xDBF8, 0x8DD9, 0xDBF9, 0x8DC8,\n\t0xDBFA, 0x8DD7, 0xDBFB, 0x8DC5, 0xDBFC, 0x8EEF, 0xDBFD, 0x8EF7,\n\t0xDBFE, 0x8EFA, 0xDC40, 0x8EF9, 0xDC41, 0x8EE6, 0xDC42, 0x8EEE,\n\t0xDC43, 0x8EE5, 0xDC44, 0x8EF5, 0xDC45, 0x8EE7, 0xDC46, 0x8EE8,\n\t0xDC47, 0x8EF6, 0xDC48, 0x8EEB, 0xDC49, 0x8EF1, 0xDC4A, 0x8EEC,\n\t0xDC4B, 0x8EF4, 0xDC4C, 0x8EE9, 0xDC4D, 0x902D, 0xDC4E, 0x9034,\n\t0xDC4F, 0x902F, 0xDC50, 0x9106, 0xDC51, 0x912C, 0xDC52, 0x9104,\n\t0xDC53, 0x90FF, 0xDC54, 0x90FC, 0xDC55, 0x9108, 0xDC56, 0x90F9,\n\t0xDC57, 0x90FB, 0xDC58, 0x9101, 0xDC59, 0x9100, 0xDC5A, 0x9107,\n\t0xDC5B, 0x9105, 0xDC5C, 0x9103, 0xDC5D, 0x9161, 0xDC5E, 0x9164,\n\t0xDC5F, 0x915F, 0xDC60, 0x9162, 0xDC61, 0x9160, 0xDC62, 0x9201,\n\t0xDC63, 0x920A, 0xDC64, 0x9225, 0xDC65, 0x9203, 0xDC66, 0x921A,\n\t0xDC67, 0x9226, 0xDC68, 0x920F, 0xDC69, 0x920C, 0xDC6A, 0x9200,\n\t0xDC6B, 0x9212, 0xDC6C, 0x91FF, 0xDC6D, 0x91FD, 0xDC6E, 0x9206,\n\t0xDC6F, 0x9204, 0xDC70, 0x9227, 0xDC71, 0x9202, 0xDC72, 0x921C,\n\t0xDC73, 0x9224, 0xDC74, 0x9219, 0xDC75, 0x9217, 0xDC76, 0x9205,\n\t0xDC77, 0x9216, 0xDC78, 0x957B, 0xDC79, 0x958D, 0xDC7A, 0x958C,\n\t0xDC7B, 0x9590, 0xDC7C, 0x9687, 0xDC7D, 0x967E, 0xDC7E, 0x9688,\n\t0xDCA1, 0x9689, 0xDCA2, 0x9683, 0xDCA3, 0x9680, 0xDCA4, 0x96C2,\n\t0xDCA5, 0x96C8, 0xDCA6, 0x96C3, 0xDCA7, 0x96F1, 0xDCA8, 0x96F0,\n\t0xDCA9, 0x976C, 0xDCAA, 0x9770, 0xDCAB, 0x976E, 0xDCAC, 0x9807,\n\t0xDCAD, 0x98A9, 0xDCAE, 0x98EB, 0xDCAF, 0x9CE6, 0xDCB0, 0x9EF9,\n\t0xDCB1, 0x4E83, 0xDCB2, 0x4E84, 0xDCB3, 0x4EB6, 0xDCB4, 0x50BD,\n\t0xDCB5, 0x50BF, 0xDCB6, 0x50C6, 0xDCB7, 0x50AE, 0xDCB8, 0x50C4,\n\t0xDCB9, 0x50CA, 0xDCBA, 0x50B4, 0xDCBB, 0x50C8, 0xDCBC, 0x50C2,\n\t0xDCBD, 0x50B0, 0xDCBE, 0x50C1, 0xDCBF, 0x50BA, 0xDCC0, 0x50B1,\n\t0xDCC1, 0x50CB, 0xDCC2, 0x50C9, 0xDCC3, 0x50B6, 0xDCC4, 0x50B8,\n\t0xDCC5, 0x51D7, 0xDCC6, 0x527A, 0xDCC7, 0x5278, 0xDCC8, 0x527B,\n\t0xDCC9, 0x527C, 0xDCCA, 0x55C3, 0xDCCB, 0x55DB, 0xDCCC, 0x55CC,\n\t0xDCCD, 0x55D0, 0xDCCE, 0x55CB, 0xDCCF, 0x55CA, 0xDCD0, 0x55DD,\n\t0xDCD1, 0x55C0, 0xDCD2, 0x55D4, 0xDCD3, 0x55C4, 0xDCD4, 0x55E9,\n\t0xDCD5, 0x55BF, 0xDCD6, 0x55D2, 0xDCD7, 0x558D, 0xDCD8, 0x55CF,\n\t0xDCD9, 0x55D5, 0xDCDA, 0x55E2, 0xDCDB, 0x55D6, 0xDCDC, 0x55C8,\n\t0xDCDD, 0x55F2, 0xDCDE, 0x55CD, 0xDCDF, 0x55D9, 0xDCE0, 0x55C2,\n\t0xDCE1, 0x5714, 0xDCE2, 0x5853, 0xDCE3, 0x5868, 0xDCE4, 0x5864,\n\t0xDCE5, 0x584F, 0xDCE6, 0x584D, 0xDCE7, 0x5849, 0xDCE8, 0x586F,\n\t0xDCE9, 0x5855, 0xDCEA, 0x584E, 0xDCEB, 0x585D, 0xDCEC, 0x5859,\n\t0xDCED, 0x5865, 0xDCEE, 0x585B, 0xDCEF, 0x583D, 0xDCF0, 0x5863,\n\t0xDCF1, 0x5871, 0xDCF2, 0x58FC, 0xDCF3, 0x5AC7, 0xDCF4, 0x5AC4,\n\t0xDCF5, 0x5ACB, 0xDCF6, 0x5ABA, 0xDCF7, 0x5AB8, 0xDCF8, 0x5AB1,\n\t0xDCF9, 0x5AB5, 0xDCFA, 0x5AB0, 0xDCFB, 0x5ABF, 0xDCFC, 0x5AC8,\n\t0xDCFD, 0x5ABB, 0xDCFE, 0x5AC6, 0xDD40, 0x5AB7, 0xDD41, 0x5AC0,\n\t0xDD42, 0x5ACA, 0xDD43, 0x5AB4, 0xDD44, 0x5AB6, 0xDD45, 0x5ACD,\n\t0xDD46, 0x5AB9, 0xDD47, 0x5A90, 0xDD48, 0x5BD6, 0xDD49, 0x5BD8,\n\t0xDD4A, 0x5BD9, 0xDD4B, 0x5C1F, 0xDD4C, 0x5C33, 0xDD4D, 0x5D71,\n\t0xDD4E, 0x5D63, 0xDD4F, 0x5D4A, 0xDD50, 0x5D65, 0xDD51, 0x5D72,\n\t0xDD52, 0x5D6C, 0xDD53, 0x5D5E, 0xDD54, 0x5D68, 0xDD55, 0x5D67,\n\t0xDD56, 0x5D62, 0xDD57, 0x5DF0, 0xDD58, 0x5E4F, 0xDD59, 0x5E4E,\n\t0xDD5A, 0x5E4A, 0xDD5B, 0x5E4D, 0xDD5C, 0x5E4B, 0xDD5D, 0x5EC5,\n\t0xDD5E, 0x5ECC, 0xDD5F, 0x5EC6, 0xDD60, 0x5ECB, 0xDD61, 0x5EC7,\n\t0xDD62, 0x5F40, 0xDD63, 0x5FAF, 0xDD64, 0x5FAD, 0xDD65, 0x60F7,\n\t0xDD66, 0x6149, 0xDD67, 0x614A, 0xDD68, 0x612B, 0xDD69, 0x6145,\n\t0xDD6A, 0x6136, 0xDD6B, 0x6132, 0xDD6C, 0x612E, 0xDD6D, 0x6146,\n\t0xDD6E, 0x612F, 0xDD6F, 0x614F, 0xDD70, 0x6129, 0xDD71, 0x6140,\n\t0xDD72, 0x6220, 0xDD73, 0x9168, 0xDD74, 0x6223, 0xDD75, 0x6225,\n\t0xDD76, 0x6224, 0xDD77, 0x63C5, 0xDD78, 0x63F1, 0xDD79, 0x63EB,\n\t0xDD7A, 0x6410, 0xDD7B, 0x6412, 0xDD7C, 0x6409, 0xDD7D, 0x6420,\n\t0xDD7E, 0x6424, 0xDDA1, 0x6433, 0xDDA2, 0x6443, 0xDDA3, 0x641F,\n\t0xDDA4, 0x6415, 0xDDA5, 0x6418, 0xDDA6, 0x6439, 0xDDA7, 0x6437,\n\t0xDDA8, 0x6422, 0xDDA9, 0x6423, 0xDDAA, 0x640C, 0xDDAB, 0x6426,\n\t0xDDAC, 0x6430, 0xDDAD, 0x6428, 0xDDAE, 0x6441, 0xDDAF, 0x6435,\n\t0xDDB0, 0x642F, 0xDDB1, 0x640A, 0xDDB2, 0x641A, 0xDDB3, 0x6440,\n\t0xDDB4, 0x6425, 0xDDB5, 0x6427, 0xDDB6, 0x640B, 0xDDB7, 0x63E7,\n\t0xDDB8, 0x641B, 0xDDB9, 0x642E, 0xDDBA, 0x6421, 0xDDBB, 0x640E,\n\t0xDDBC, 0x656F, 0xDDBD, 0x6592, 0xDDBE, 0x65D3, 0xDDBF, 0x6686,\n\t0xDDC0, 0x668C, 0xDDC1, 0x6695, 0xDDC2, 0x6690, 0xDDC3, 0x668B,\n\t0xDDC4, 0x668A, 0xDDC5, 0x6699, 0xDDC6, 0x6694, 0xDDC7, 0x6678,\n\t0xDDC8, 0x6720, 0xDDC9, 0x6966, 0xDDCA, 0x695F, 0xDDCB, 0x6938,\n\t0xDDCC, 0x694E, 0xDDCD, 0x6962, 0xDDCE, 0x6971, 0xDDCF, 0x693F,\n\t0xDDD0, 0x6945, 0xDDD1, 0x696A, 0xDDD2, 0x6939, 0xDDD3, 0x6942,\n\t0xDDD4, 0x6957, 0xDDD5, 0x6959, 0xDDD6, 0x697A, 0xDDD7, 0x6948,\n\t0xDDD8, 0x6949, 0xDDD9, 0x6935, 0xDDDA, 0x696C, 0xDDDB, 0x6933,\n\t0xDDDC, 0x693D, 0xDDDD, 0x6965, 0xDDDE, 0x68F0, 0xDDDF, 0x6978,\n\t0xDDE0, 0x6934, 0xDDE1, 0x6969, 0xDDE2, 0x6940, 0xDDE3, 0x696F,\n\t0xDDE4, 0x6944, 0xDDE5, 0x6976, 0xDDE6, 0x6958, 0xDDE7, 0x6941,\n\t0xDDE8, 0x6974, 0xDDE9, 0x694C, 0xDDEA, 0x693B, 0xDDEB, 0x694B,\n\t0xDDEC, 0x6937, 0xDDED, 0x695C, 0xDDEE, 0x694F, 0xDDEF, 0x6951,\n\t0xDDF0, 0x6932, 0xDDF1, 0x6952, 0xDDF2, 0x692F, 0xDDF3, 0x697B,\n\t0xDDF4, 0x693C, 0xDDF5, 0x6B46, 0xDDF6, 0x6B45, 0xDDF7, 0x6B43,\n\t0xDDF8, 0x6B42, 0xDDF9, 0x6B48, 0xDDFA, 0x6B41, 0xDDFB, 0x6B9B,\n\t0xDDFC, 0xFA0D, 0xDDFD, 0x6BFB, 0xDDFE, 0x6BFC, 0xDE40, 0x6BF9,\n\t0xDE41, 0x6BF7, 0xDE42, 0x6BF8, 0xDE43, 0x6E9B, 0xDE44, 0x6ED6,\n\t0xDE45, 0x6EC8, 0xDE46, 0x6E8F, 0xDE47, 0x6EC0, 0xDE48, 0x6E9F,\n\t0xDE49, 0x6E93, 0xDE4A, 0x6E94, 0xDE4B, 0x6EA0, 0xDE4C, 0x6EB1,\n\t0xDE4D, 0x6EB9, 0xDE4E, 0x6EC6, 0xDE4F, 0x6ED2, 0xDE50, 0x6EBD,\n\t0xDE51, 0x6EC1, 0xDE52, 0x6E9E, 0xDE53, 0x6EC9, 0xDE54, 0x6EB7,\n\t0xDE55, 0x6EB0, 0xDE56, 0x6ECD, 0xDE57, 0x6EA6, 0xDE58, 0x6ECF,\n\t0xDE59, 0x6EB2, 0xDE5A, 0x6EBE, 0xDE5B, 0x6EC3, 0xDE5C, 0x6EDC,\n\t0xDE5D, 0x6ED8, 0xDE5E, 0x6E99, 0xDE5F, 0x6E92, 0xDE60, 0x6E8E,\n\t0xDE61, 0x6E8D, 0xDE62, 0x6EA4, 0xDE63, 0x6EA1, 0xDE64, 0x6EBF,\n\t0xDE65, 0x6EB3, 0xDE66, 0x6ED0, 0xDE67, 0x6ECA, 0xDE68, 0x6E97,\n\t0xDE69, 0x6EAE, 0xDE6A, 0x6EA3, 0xDE6B, 0x7147, 0xDE6C, 0x7154,\n\t0xDE6D, 0x7152, 0xDE6E, 0x7163, 0xDE6F, 0x7160, 0xDE70, 0x7141,\n\t0xDE71, 0x715D, 0xDE72, 0x7162, 0xDE73, 0x7172, 0xDE74, 0x7178,\n\t0xDE75, 0x716A, 0xDE76, 0x7161, 0xDE77, 0x7142, 0xDE78, 0x7158,\n\t0xDE79, 0x7143, 0xDE7A, 0x714B, 0xDE7B, 0x7170, 0xDE7C, 0x715F,\n\t0xDE7D, 0x7150, 0xDE7E, 0x7153, 0xDEA1, 0x7144, 0xDEA2, 0x714D,\n\t0xDEA3, 0x715A, 0xDEA4, 0x724F, 0xDEA5, 0x728D, 0xDEA6, 0x728C,\n\t0xDEA7, 0x7291, 0xDEA8, 0x7290, 0xDEA9, 0x728E, 0xDEAA, 0x733C,\n\t0xDEAB, 0x7342, 0xDEAC, 0x733B, 0xDEAD, 0x733A, 0xDEAE, 0x7340,\n\t0xDEAF, 0x734A, 0xDEB0, 0x7349, 0xDEB1, 0x7444, 0xDEB2, 0x744A,\n\t0xDEB3, 0x744B, 0xDEB4, 0x7452, 0xDEB5, 0x7451, 0xDEB6, 0x7457,\n\t0xDEB7, 0x7440, 0xDEB8, 0x744F, 0xDEB9, 0x7450, 0xDEBA, 0x744E,\n\t0xDEBB, 0x7442, 0xDEBC, 0x7446, 0xDEBD, 0x744D, 0xDEBE, 0x7454,\n\t0xDEBF, 0x74E1, 0xDEC0, 0x74FF, 0xDEC1, 0x74FE, 0xDEC2, 0x74FD,\n\t0xDEC3, 0x751D, 0xDEC4, 0x7579, 0xDEC5, 0x7577, 0xDEC6, 0x6983,\n\t0xDEC7, 0x75EF, 0xDEC8, 0x760F, 0xDEC9, 0x7603, 0xDECA, 0x75F7,\n\t0xDECB, 0x75FE, 0xDECC, 0x75FC, 0xDECD, 0x75F9, 0xDECE, 0x75F8,\n\t0xDECF, 0x7610, 0xDED0, 0x75FB, 0xDED1, 0x75F6, 0xDED2, 0x75ED,\n\t0xDED3, 0x75F5, 0xDED4, 0x75FD, 0xDED5, 0x7699, 0xDED6, 0x76B5,\n\t0xDED7, 0x76DD, 0xDED8, 0x7755, 0xDED9, 0x775F, 0xDEDA, 0x7760,\n\t0xDEDB, 0x7752, 0xDEDC, 0x7756, 0xDEDD, 0x775A, 0xDEDE, 0x7769,\n\t0xDEDF, 0x7767, 0xDEE0, 0x7754, 0xDEE1, 0x7759, 0xDEE2, 0x776D,\n\t0xDEE3, 0x77E0, 0xDEE4, 0x7887, 0xDEE5, 0x789A, 0xDEE6, 0x7894,\n\t0xDEE7, 0x788F, 0xDEE8, 0x7884, 0xDEE9, 0x7895, 0xDEEA, 0x7885,\n\t0xDEEB, 0x7886, 0xDEEC, 0x78A1, 0xDEED, 0x7883, 0xDEEE, 0x7879,\n\t0xDEEF, 0x7899, 0xDEF0, 0x7880, 0xDEF1, 0x7896, 0xDEF2, 0x787B,\n\t0xDEF3, 0x797C, 0xDEF4, 0x7982, 0xDEF5, 0x797D, 0xDEF6, 0x7979,\n\t0xDEF7, 0x7A11, 0xDEF8, 0x7A18, 0xDEF9, 0x7A19, 0xDEFA, 0x7A12,\n\t0xDEFB, 0x7A17, 0xDEFC, 0x7A15, 0xDEFD, 0x7A22, 0xDEFE, 0x7A13,\n\t0xDF40, 0x7A1B, 0xDF41, 0x7A10, 0xDF42, 0x7AA3, 0xDF43, 0x7AA2,\n\t0xDF44, 0x7A9E, 0xDF45, 0x7AEB, 0xDF46, 0x7B66, 0xDF47, 0x7B64,\n\t0xDF48, 0x7B6D, 0xDF49, 0x7B74, 0xDF4A, 0x7B69, 0xDF4B, 0x7B72,\n\t0xDF4C, 0x7B65, 0xDF4D, 0x7B73, 0xDF4E, 0x7B71, 0xDF4F, 0x7B70,\n\t0xDF50, 0x7B61, 0xDF51, 0x7B78, 0xDF52, 0x7B76, 0xDF53, 0x7B63,\n\t0xDF54, 0x7CB2, 0xDF55, 0x7CB4, 0xDF56, 0x7CAF, 0xDF57, 0x7D88,\n\t0xDF58, 0x7D86, 0xDF59, 0x7D80, 0xDF5A, 0x7D8D, 0xDF5B, 0x7D7F,\n\t0xDF5C, 0x7D85, 0xDF5D, 0x7D7A, 0xDF5E, 0x7D8E, 0xDF5F, 0x7D7B,\n\t0xDF60, 0x7D83, 0xDF61, 0x7D7C, 0xDF62, 0x7D8C, 0xDF63, 0x7D94,\n\t0xDF64, 0x7D84, 0xDF65, 0x7D7D, 0xDF66, 0x7D92, 0xDF67, 0x7F6D,\n\t0xDF68, 0x7F6B, 0xDF69, 0x7F67, 0xDF6A, 0x7F68, 0xDF6B, 0x7F6C,\n\t0xDF6C, 0x7FA6, 0xDF6D, 0x7FA5, 0xDF6E, 0x7FA7, 0xDF6F, 0x7FDB,\n\t0xDF70, 0x7FDC, 0xDF71, 0x8021, 0xDF72, 0x8164, 0xDF73, 0x8160,\n\t0xDF74, 0x8177, 0xDF75, 0x815C, 0xDF76, 0x8169, 0xDF77, 0x815B,\n\t0xDF78, 0x8162, 0xDF79, 0x8172, 0xDF7A, 0x6721, 0xDF7B, 0x815E,\n\t0xDF7C, 0x8176, 0xDF7D, 0x8167, 0xDF7E, 0x816F, 0xDFA1, 0x8144,\n\t0xDFA2, 0x8161, 0xDFA3, 0x821D, 0xDFA4, 0x8249, 0xDFA5, 0x8244,\n\t0xDFA6, 0x8240, 0xDFA7, 0x8242, 0xDFA8, 0x8245, 0xDFA9, 0x84F1,\n\t0xDFAA, 0x843F, 0xDFAB, 0x8456, 0xDFAC, 0x8476, 0xDFAD, 0x8479,\n\t0xDFAE, 0x848F, 0xDFAF, 0x848D, 0xDFB0, 0x8465, 0xDFB1, 0x8451,\n\t0xDFB2, 0x8440, 0xDFB3, 0x8486, 0xDFB4, 0x8467, 0xDFB5, 0x8430,\n\t0xDFB6, 0x844D, 0xDFB7, 0x847D, 0xDFB8, 0x845A, 0xDFB9, 0x8459,\n\t0xDFBA, 0x8474, 0xDFBB, 0x8473, 0xDFBC, 0x845D, 0xDFBD, 0x8507,\n\t0xDFBE, 0x845E, 0xDFBF, 0x8437, 0xDFC0, 0x843A, 0xDFC1, 0x8434,\n\t0xDFC2, 0x847A, 0xDFC3, 0x8443, 0xDFC4, 0x8478, 0xDFC5, 0x8432,\n\t0xDFC6, 0x8445, 0xDFC7, 0x8429, 0xDFC8, 0x83D9, 0xDFC9, 0x844B,\n\t0xDFCA, 0x842F, 0xDFCB, 0x8442, 0xDFCC, 0x842D, 0xDFCD, 0x845F,\n\t0xDFCE, 0x8470, 0xDFCF, 0x8439, 0xDFD0, 0x844E, 0xDFD1, 0x844C,\n\t0xDFD2, 0x8452, 0xDFD3, 0x846F, 0xDFD4, 0x84C5, 0xDFD5, 0x848E,\n\t0xDFD6, 0x843B, 0xDFD7, 0x8447, 0xDFD8, 0x8436, 0xDFD9, 0x8433,\n\t0xDFDA, 0x8468, 0xDFDB, 0x847E, 0xDFDC, 0x8444, 0xDFDD, 0x842B,\n\t0xDFDE, 0x8460, 0xDFDF, 0x8454, 0xDFE0, 0x846E, 0xDFE1, 0x8450,\n\t0xDFE2, 0x870B, 0xDFE3, 0x8704, 0xDFE4, 0x86F7, 0xDFE5, 0x870C,\n\t0xDFE6, 0x86FA, 0xDFE7, 0x86D6, 0xDFE8, 0x86F5, 0xDFE9, 0x874D,\n\t0xDFEA, 0x86F8, 0xDFEB, 0x870E, 0xDFEC, 0x8709, 0xDFED, 0x8701,\n\t0xDFEE, 0x86F6, 0xDFEF, 0x870D, 0xDFF0, 0x8705, 0xDFF1, 0x88D6,\n\t0xDFF2, 0x88CB, 0xDFF3, 0x88CD, 0xDFF4, 0x88CE, 0xDFF5, 0x88DE,\n\t0xDFF6, 0x88DB, 0xDFF7, 0x88DA, 0xDFF8, 0x88CC, 0xDFF9, 0x88D0,\n\t0xDFFA, 0x8985, 0xDFFB, 0x899B, 0xDFFC, 0x89DF, 0xDFFD, 0x89E5,\n\t0xDFFE, 0x89E4, 0xE040, 0x89E1, 0xE041, 0x89E0, 0xE042, 0x89E2,\n\t0xE043, 0x89DC, 0xE044, 0x89E6, 0xE045, 0x8A76, 0xE046, 0x8A86,\n\t0xE047, 0x8A7F, 0xE048, 0x8A61, 0xE049, 0x8A3F, 0xE04A, 0x8A77,\n\t0xE04B, 0x8A82, 0xE04C, 0x8A84, 0xE04D, 0x8A75, 0xE04E, 0x8A83,\n\t0xE04F, 0x8A81, 0xE050, 0x8A74, 0xE051, 0x8A7A, 0xE052, 0x8C3C,\n\t0xE053, 0x8C4B, 0xE054, 0x8C4A, 0xE055, 0x8C65, 0xE056, 0x8C64,\n\t0xE057, 0x8C66, 0xE058, 0x8C86, 0xE059, 0x8C84, 0xE05A, 0x8C85,\n\t0xE05B, 0x8CCC, 0xE05C, 0x8D68, 0xE05D, 0x8D69, 0xE05E, 0x8D91,\n\t0xE05F, 0x8D8C, 0xE060, 0x8D8E, 0xE061, 0x8D8F, 0xE062, 0x8D8D,\n\t0xE063, 0x8D93, 0xE064, 0x8D94, 0xE065, 0x8D90, 0xE066, 0x8D92,\n\t0xE067, 0x8DF0, 0xE068, 0x8DE0, 0xE069, 0x8DEC, 0xE06A, 0x8DF1,\n\t0xE06B, 0x8DEE, 0xE06C, 0x8DD0, 0xE06D, 0x8DE9, 0xE06E, 0x8DE3,\n\t0xE06F, 0x8DE2, 0xE070, 0x8DE7, 0xE071, 0x8DF2, 0xE072, 0x8DEB,\n\t0xE073, 0x8DF4, 0xE074, 0x8F06, 0xE075, 0x8EFF, 0xE076, 0x8F01,\n\t0xE077, 0x8F00, 0xE078, 0x8F05, 0xE079, 0x8F07, 0xE07A, 0x8F08,\n\t0xE07B, 0x8F02, 0xE07C, 0x8F0B, 0xE07D, 0x9052, 0xE07E, 0x903F,\n\t0xE0A1, 0x9044, 0xE0A2, 0x9049, 0xE0A3, 0x903D, 0xE0A4, 0x9110,\n\t0xE0A5, 0x910D, 0xE0A6, 0x910F, 0xE0A7, 0x9111, 0xE0A8, 0x9116,\n\t0xE0A9, 0x9114, 0xE0AA, 0x910B, 0xE0AB, 0x910E, 0xE0AC, 0x916E,\n\t0xE0AD, 0x916F, 0xE0AE, 0x9248, 0xE0AF, 0x9252, 0xE0B0, 0x9230,\n\t0xE0B1, 0x923A, 0xE0B2, 0x9266, 0xE0B3, 0x9233, 0xE0B4, 0x9265,\n\t0xE0B5, 0x925E, 0xE0B6, 0x9283, 0xE0B7, 0x922E, 0xE0B8, 0x924A,\n\t0xE0B9, 0x9246, 0xE0BA, 0x926D, 0xE0BB, 0x926C, 0xE0BC, 0x924F,\n\t0xE0BD, 0x9260, 0xE0BE, 0x9267, 0xE0BF, 0x926F, 0xE0C0, 0x9236,\n\t0xE0C1, 0x9261, 0xE0C2, 0x9270, 0xE0C3, 0x9231, 0xE0C4, 0x9254,\n\t0xE0C5, 0x9263, 0xE0C6, 0x9250, 0xE0C7, 0x9272, 0xE0C8, 0x924E,\n\t0xE0C9, 0x9253, 0xE0CA, 0x924C, 0xE0CB, 0x9256, 0xE0CC, 0x9232,\n\t0xE0CD, 0x959F, 0xE0CE, 0x959C, 0xE0CF, 0x959E, 0xE0D0, 0x959B,\n\t0xE0D1, 0x9692, 0xE0D2, 0x9693, 0xE0D3, 0x9691, 0xE0D4, 0x9697,\n\t0xE0D5, 0x96CE, 0xE0D6, 0x96FA, 0xE0D7, 0x96FD, 0xE0D8, 0x96F8,\n\t0xE0D9, 0x96F5, 0xE0DA, 0x9773, 0xE0DB, 0x9777, 0xE0DC, 0x9778,\n\t0xE0DD, 0x9772, 0xE0DE, 0x980F, 0xE0DF, 0x980D, 0xE0E0, 0x980E,\n\t0xE0E1, 0x98AC, 0xE0E2, 0x98F6, 0xE0E3, 0x98F9, 0xE0E4, 0x99AF,\n\t0xE0E5, 0x99B2, 0xE0E6, 0x99B0, 0xE0E7, 0x99B5, 0xE0E8, 0x9AAD,\n\t0xE0E9, 0x9AAB, 0xE0EA, 0x9B5B, 0xE0EB, 0x9CEA, 0xE0EC, 0x9CED,\n\t0xE0ED, 0x9CE7, 0xE0EE, 0x9E80, 0xE0EF, 0x9EFD, 0xE0F0, 0x50E6,\n\t0xE0F1, 0x50D4, 0xE0F2, 0x50D7, 0xE0F3, 0x50E8, 0xE0F4, 0x50F3,\n\t0xE0F5, 0x50DB, 0xE0F6, 0x50EA, 0xE0F7, 0x50DD, 0xE0F8, 0x50E4,\n\t0xE0F9, 0x50D3, 0xE0FA, 0x50EC, 0xE0FB, 0x50F0, 0xE0FC, 0x50EF,\n\t0xE0FD, 0x50E3, 0xE0FE, 0x50E0, 0xE140, 0x51D8, 0xE141, 0x5280,\n\t0xE142, 0x5281, 0xE143, 0x52E9, 0xE144, 0x52EB, 0xE145, 0x5330,\n\t0xE146, 0x53AC, 0xE147, 0x5627, 0xE148, 0x5615, 0xE149, 0x560C,\n\t0xE14A, 0x5612, 0xE14B, 0x55FC, 0xE14C, 0x560F, 0xE14D, 0x561C,\n\t0xE14E, 0x5601, 0xE14F, 0x5613, 0xE150, 0x5602, 0xE151, 0x55FA,\n\t0xE152, 0x561D, 0xE153, 0x5604, 0xE154, 0x55FF, 0xE155, 0x55F9,\n\t0xE156, 0x5889, 0xE157, 0x587C, 0xE158, 0x5890, 0xE159, 0x5898,\n\t0xE15A, 0x5886, 0xE15B, 0x5881, 0xE15C, 0x587F, 0xE15D, 0x5874,\n\t0xE15E, 0x588B, 0xE15F, 0x587A, 0xE160, 0x5887, 0xE161, 0x5891,\n\t0xE162, 0x588E, 0xE163, 0x5876, 0xE164, 0x5882, 0xE165, 0x5888,\n\t0xE166, 0x587B, 0xE167, 0x5894, 0xE168, 0x588F, 0xE169, 0x58FE,\n\t0xE16A, 0x596B, 0xE16B, 0x5ADC, 0xE16C, 0x5AEE, 0xE16D, 0x5AE5,\n\t0xE16E, 0x5AD5, 0xE16F, 0x5AEA, 0xE170, 0x5ADA, 0xE171, 0x5AED,\n\t0xE172, 0x5AEB, 0xE173, 0x5AF3, 0xE174, 0x5AE2, 0xE175, 0x5AE0,\n\t0xE176, 0x5ADB, 0xE177, 0x5AEC, 0xE178, 0x5ADE, 0xE179, 0x5ADD,\n\t0xE17A, 0x5AD9, 0xE17B, 0x5AE8, 0xE17C, 0x5ADF, 0xE17D, 0x5B77,\n\t0xE17E, 0x5BE0, 0xE1A1, 0x5BE3, 0xE1A2, 0x5C63, 0xE1A3, 0x5D82,\n\t0xE1A4, 0x5D80, 0xE1A5, 0x5D7D, 0xE1A6, 0x5D86, 0xE1A7, 0x5D7A,\n\t0xE1A8, 0x5D81, 0xE1A9, 0x5D77, 0xE1AA, 0x5D8A, 0xE1AB, 0x5D89,\n\t0xE1AC, 0x5D88, 0xE1AD, 0x5D7E, 0xE1AE, 0x5D7C, 0xE1AF, 0x5D8D,\n\t0xE1B0, 0x5D79, 0xE1B1, 0x5D7F, 0xE1B2, 0x5E58, 0xE1B3, 0x5E59,\n\t0xE1B4, 0x5E53, 0xE1B5, 0x5ED8, 0xE1B6, 0x5ED1, 0xE1B7, 0x5ED7,\n\t0xE1B8, 0x5ECE, 0xE1B9, 0x5EDC, 0xE1BA, 0x5ED5, 0xE1BB, 0x5ED9,\n\t0xE1BC, 0x5ED2, 0xE1BD, 0x5ED4, 0xE1BE, 0x5F44, 0xE1BF, 0x5F43,\n\t0xE1C0, 0x5F6F, 0xE1C1, 0x5FB6, 0xE1C2, 0x612C, 0xE1C3, 0x6128,\n\t0xE1C4, 0x6141, 0xE1C5, 0x615E, 0xE1C6, 0x6171, 0xE1C7, 0x6173,\n\t0xE1C8, 0x6152, 0xE1C9, 0x6153, 0xE1CA, 0x6172, 0xE1CB, 0x616C,\n\t0xE1CC, 0x6180, 0xE1CD, 0x6174, 0xE1CE, 0x6154, 0xE1CF, 0x617A,\n\t0xE1D0, 0x615B, 0xE1D1, 0x6165, 0xE1D2, 0x613B, 0xE1D3, 0x616A,\n\t0xE1D4, 0x6161, 0xE1D5, 0x6156, 0xE1D6, 0x6229, 0xE1D7, 0x6227,\n\t0xE1D8, 0x622B, 0xE1D9, 0x642B, 0xE1DA, 0x644D, 0xE1DB, 0x645B,\n\t0xE1DC, 0x645D, 0xE1DD, 0x6474, 0xE1DE, 0x6476, 0xE1DF, 0x6472,\n\t0xE1E0, 0x6473, 0xE1E1, 0x647D, 0xE1E2, 0x6475, 0xE1E3, 0x6466,\n\t0xE1E4, 0x64A6, 0xE1E5, 0x644E, 0xE1E6, 0x6482, 0xE1E7, 0x645E,\n\t0xE1E8, 0x645C, 0xE1E9, 0x644B, 0xE1EA, 0x6453, 0xE1EB, 0x6460,\n\t0xE1EC, 0x6450, 0xE1ED, 0x647F, 0xE1EE, 0x643F, 0xE1EF, 0x646C,\n\t0xE1F0, 0x646B, 0xE1F1, 0x6459, 0xE1F2, 0x6465, 0xE1F3, 0x6477,\n\t0xE1F4, 0x6573, 0xE1F5, 0x65A0, 0xE1F6, 0x66A1, 0xE1F7, 0x66A0,\n\t0xE1F8, 0x669F, 0xE1F9, 0x6705, 0xE1FA, 0x6704, 0xE1FB, 0x6722,\n\t0xE1FC, 0x69B1, 0xE1FD, 0x69B6, 0xE1FE, 0x69C9, 0xE240, 0x69A0,\n\t0xE241, 0x69CE, 0xE242, 0x6996, 0xE243, 0x69B0, 0xE244, 0x69AC,\n\t0xE245, 0x69BC, 0xE246, 0x6991, 0xE247, 0x6999, 0xE248, 0x698E,\n\t0xE249, 0x69A7, 0xE24A, 0x698D, 0xE24B, 0x69A9, 0xE24C, 0x69BE,\n\t0xE24D, 0x69AF, 0xE24E, 0x69BF, 0xE24F, 0x69C4, 0xE250, 0x69BD,\n\t0xE251, 0x69A4, 0xE252, 0x69D4, 0xE253, 0x69B9, 0xE254, 0x69CA,\n\t0xE255, 0x699A, 0xE256, 0x69CF, 0xE257, 0x69B3, 0xE258, 0x6993,\n\t0xE259, 0x69AA, 0xE25A, 0x69A1, 0xE25B, 0x699E, 0xE25C, 0x69D9,\n\t0xE25D, 0x6997, 0xE25E, 0x6990, 0xE25F, 0x69C2, 0xE260, 0x69B5,\n\t0xE261, 0x69A5, 0xE262, 0x69C6, 0xE263, 0x6B4A, 0xE264, 0x6B4D,\n\t0xE265, 0x6B4B, 0xE266, 0x6B9E, 0xE267, 0x6B9F, 0xE268, 0x6BA0,\n\t0xE269, 0x6BC3, 0xE26A, 0x6BC4, 0xE26B, 0x6BFE, 0xE26C, 0x6ECE,\n\t0xE26D, 0x6EF5, 0xE26E, 0x6EF1, 0xE26F, 0x6F03, 0xE270, 0x6F25,\n\t0xE271, 0x6EF8, 0xE272, 0x6F37, 0xE273, 0x6EFB, 0xE274, 0x6F2E,\n\t0xE275, 0x6F09, 0xE276, 0x6F4E, 0xE277, 0x6F19, 0xE278, 0x6F1A,\n\t0xE279, 0x6F27, 0xE27A, 0x6F18, 0xE27B, 0x6F3B, 0xE27C, 0x6F12,\n\t0xE27D, 0x6EED, 0xE27E, 0x6F0A, 0xE2A1, 0x6F36, 0xE2A2, 0x6F73,\n\t0xE2A3, 0x6EF9, 0xE2A4, 0x6EEE, 0xE2A5, 0x6F2D, 0xE2A6, 0x6F40,\n\t0xE2A7, 0x6F30, 0xE2A8, 0x6F3C, 0xE2A9, 0x6F35, 0xE2AA, 0x6EEB,\n\t0xE2AB, 0x6F07, 0xE2AC, 0x6F0E, 0xE2AD, 0x6F43, 0xE2AE, 0x6F05,\n\t0xE2AF, 0x6EFD, 0xE2B0, 0x6EF6, 0xE2B1, 0x6F39, 0xE2B2, 0x6F1C,\n\t0xE2B3, 0x6EFC, 0xE2B4, 0x6F3A, 0xE2B5, 0x6F1F, 0xE2B6, 0x6F0D,\n\t0xE2B7, 0x6F1E, 0xE2B8, 0x6F08, 0xE2B9, 0x6F21, 0xE2BA, 0x7187,\n\t0xE2BB, 0x7190, 0xE2BC, 0x7189, 0xE2BD, 0x7180, 0xE2BE, 0x7185,\n\t0xE2BF, 0x7182, 0xE2C0, 0x718F, 0xE2C1, 0x717B, 0xE2C2, 0x7186,\n\t0xE2C3, 0x7181, 0xE2C4, 0x7197, 0xE2C5, 0x7244, 0xE2C6, 0x7253,\n\t0xE2C7, 0x7297, 0xE2C8, 0x7295, 0xE2C9, 0x7293, 0xE2CA, 0x7343,\n\t0xE2CB, 0x734D, 0xE2CC, 0x7351, 0xE2CD, 0x734C, 0xE2CE, 0x7462,\n\t0xE2CF, 0x7473, 0xE2D0, 0x7471, 0xE2D1, 0x7475, 0xE2D2, 0x7472,\n\t0xE2D3, 0x7467, 0xE2D4, 0x746E, 0xE2D5, 0x7500, 0xE2D6, 0x7502,\n\t0xE2D7, 0x7503, 0xE2D8, 0x757D, 0xE2D9, 0x7590, 0xE2DA, 0x7616,\n\t0xE2DB, 0x7608, 0xE2DC, 0x760C, 0xE2DD, 0x7615, 0xE2DE, 0x7611,\n\t0xE2DF, 0x760A, 0xE2E0, 0x7614, 0xE2E1, 0x76B8, 0xE2E2, 0x7781,\n\t0xE2E3, 0x777C, 0xE2E4, 0x7785, 0xE2E5, 0x7782, 0xE2E6, 0x776E,\n\t0xE2E7, 0x7780, 0xE2E8, 0x776F, 0xE2E9, 0x777E, 0xE2EA, 0x7783,\n\t0xE2EB, 0x78B2, 0xE2EC, 0x78AA, 0xE2ED, 0x78B4, 0xE2EE, 0x78AD,\n\t0xE2EF, 0x78A8, 0xE2F0, 0x787E, 0xE2F1, 0x78AB, 0xE2F2, 0x789E,\n\t0xE2F3, 0x78A5, 0xE2F4, 0x78A0, 0xE2F5, 0x78AC, 0xE2F6, 0x78A2,\n\t0xE2F7, 0x78A4, 0xE2F8, 0x7998, 0xE2F9, 0x798A, 0xE2FA, 0x798B,\n\t0xE2FB, 0x7996, 0xE2FC, 0x7995, 0xE2FD, 0x7994, 0xE2FE, 0x7993,\n\t0xE340, 0x7997, 0xE341, 0x7988, 0xE342, 0x7992, 0xE343, 0x7990,\n\t0xE344, 0x7A2B, 0xE345, 0x7A4A, 0xE346, 0x7A30, 0xE347, 0x7A2F,\n\t0xE348, 0x7A28, 0xE349, 0x7A26, 0xE34A, 0x7AA8, 0xE34B, 0x7AAB,\n\t0xE34C, 0x7AAC, 0xE34D, 0x7AEE, 0xE34E, 0x7B88, 0xE34F, 0x7B9C,\n\t0xE350, 0x7B8A, 0xE351, 0x7B91, 0xE352, 0x7B90, 0xE353, 0x7B96,\n\t0xE354, 0x7B8D, 0xE355, 0x7B8C, 0xE356, 0x7B9B, 0xE357, 0x7B8E,\n\t0xE358, 0x7B85, 0xE359, 0x7B98, 0xE35A, 0x5284, 0xE35B, 0x7B99,\n\t0xE35C, 0x7BA4, 0xE35D, 0x7B82, 0xE35E, 0x7CBB, 0xE35F, 0x7CBF,\n\t0xE360, 0x7CBC, 0xE361, 0x7CBA, 0xE362, 0x7DA7, 0xE363, 0x7DB7,\n\t0xE364, 0x7DC2, 0xE365, 0x7DA3, 0xE366, 0x7DAA, 0xE367, 0x7DC1,\n\t0xE368, 0x7DC0, 0xE369, 0x7DC5, 0xE36A, 0x7D9D, 0xE36B, 0x7DCE,\n\t0xE36C, 0x7DC4, 0xE36D, 0x7DC6, 0xE36E, 0x7DCB, 0xE36F, 0x7DCC,\n\t0xE370, 0x7DAF, 0xE371, 0x7DB9, 0xE372, 0x7D96, 0xE373, 0x7DBC,\n\t0xE374, 0x7D9F, 0xE375, 0x7DA6, 0xE376, 0x7DAE, 0xE377, 0x7DA9,\n\t0xE378, 0x7DA1, 0xE379, 0x7DC9, 0xE37A, 0x7F73, 0xE37B, 0x7FE2,\n\t0xE37C, 0x7FE3, 0xE37D, 0x7FE5, 0xE37E, 0x7FDE, 0xE3A1, 0x8024,\n\t0xE3A2, 0x805D, 0xE3A3, 0x805C, 0xE3A4, 0x8189, 0xE3A5, 0x8186,\n\t0xE3A6, 0x8183, 0xE3A7, 0x8187, 0xE3A8, 0x818D, 0xE3A9, 0x818C,\n\t0xE3AA, 0x818B, 0xE3AB, 0x8215, 0xE3AC, 0x8497, 0xE3AD, 0x84A4,\n\t0xE3AE, 0x84A1, 0xE3AF, 0x849F, 0xE3B0, 0x84BA, 0xE3B1, 0x84CE,\n\t0xE3B2, 0x84C2, 0xE3B3, 0x84AC, 0xE3B4, 0x84AE, 0xE3B5, 0x84AB,\n\t0xE3B6, 0x84B9, 0xE3B7, 0x84B4, 0xE3B8, 0x84C1, 0xE3B9, 0x84CD,\n\t0xE3BA, 0x84AA, 0xE3BB, 0x849A, 0xE3BC, 0x84B1, 0xE3BD, 0x84D0,\n\t0xE3BE, 0x849D, 0xE3BF, 0x84A7, 0xE3C0, 0x84BB, 0xE3C1, 0x84A2,\n\t0xE3C2, 0x8494, 0xE3C3, 0x84C7, 0xE3C4, 0x84CC, 0xE3C5, 0x849B,\n\t0xE3C6, 0x84A9, 0xE3C7, 0x84AF, 0xE3C8, 0x84A8, 0xE3C9, 0x84D6,\n\t0xE3CA, 0x8498, 0xE3CB, 0x84B6, 0xE3CC, 0x84CF, 0xE3CD, 0x84A0,\n\t0xE3CE, 0x84D7, 0xE3CF, 0x84D4, 0xE3D0, 0x84D2, 0xE3D1, 0x84DB,\n\t0xE3D2, 0x84B0, 0xE3D3, 0x8491, 0xE3D4, 0x8661, 0xE3D5, 0x8733,\n\t0xE3D6, 0x8723, 0xE3D7, 0x8728, 0xE3D8, 0x876B, 0xE3D9, 0x8740,\n\t0xE3DA, 0x872E, 0xE3DB, 0x871E, 0xE3DC, 0x8721, 0xE3DD, 0x8719,\n\t0xE3DE, 0x871B, 0xE3DF, 0x8743, 0xE3E0, 0x872C, 0xE3E1, 0x8741,\n\t0xE3E2, 0x873E, 0xE3E3, 0x8746, 0xE3E4, 0x8720, 0xE3E5, 0x8732,\n\t0xE3E6, 0x872A, 0xE3E7, 0x872D, 0xE3E8, 0x873C, 0xE3E9, 0x8712,\n\t0xE3EA, 0x873A, 0xE3EB, 0x8731, 0xE3EC, 0x8735, 0xE3ED, 0x8742,\n\t0xE3EE, 0x8726, 0xE3EF, 0x8727, 0xE3F0, 0x8738, 0xE3F1, 0x8724,\n\t0xE3F2, 0x871A, 0xE3F3, 0x8730, 0xE3F4, 0x8711, 0xE3F5, 0x88F7,\n\t0xE3F6, 0x88E7, 0xE3F7, 0x88F1, 0xE3F8, 0x88F2, 0xE3F9, 0x88FA,\n\t0xE3FA, 0x88FE, 0xE3FB, 0x88EE, 0xE3FC, 0x88FC, 0xE3FD, 0x88F6,\n\t0xE3FE, 0x88FB, 0xE440, 0x88F0, 0xE441, 0x88EC, 0xE442, 0x88EB,\n\t0xE443, 0x899D, 0xE444, 0x89A1, 0xE445, 0x899F, 0xE446, 0x899E,\n\t0xE447, 0x89E9, 0xE448, 0x89EB, 0xE449, 0x89E8, 0xE44A, 0x8AAB,\n\t0xE44B, 0x8A99, 0xE44C, 0x8A8B, 0xE44D, 0x8A92, 0xE44E, 0x8A8F,\n\t0xE44F, 0x8A96, 0xE450, 0x8C3D, 0xE451, 0x8C68, 0xE452, 0x8C69,\n\t0xE453, 0x8CD5, 0xE454, 0x8CCF, 0xE455, 0x8CD7, 0xE456, 0x8D96,\n\t0xE457, 0x8E09, 0xE458, 0x8E02, 0xE459, 0x8DFF, 0xE45A, 0x8E0D,\n\t0xE45B, 0x8DFD, 0xE45C, 0x8E0A, 0xE45D, 0x8E03, 0xE45E, 0x8E07,\n\t0xE45F, 0x8E06, 0xE460, 0x8E05, 0xE461, 0x8DFE, 0xE462, 0x8E00,\n\t0xE463, 0x8E04, 0xE464, 0x8F10, 0xE465, 0x8F11, 0xE466, 0x8F0E,\n\t0xE467, 0x8F0D, 0xE468, 0x9123, 0xE469, 0x911C, 0xE46A, 0x9120,\n\t0xE46B, 0x9122, 0xE46C, 0x911F, 0xE46D, 0x911D, 0xE46E, 0x911A,\n\t0xE46F, 0x9124, 0xE470, 0x9121, 0xE471, 0x911B, 0xE472, 0x917A,\n\t0xE473, 0x9172, 0xE474, 0x9179, 0xE475, 0x9173, 0xE476, 0x92A5,\n\t0xE477, 0x92A4, 0xE478, 0x9276, 0xE479, 0x929B, 0xE47A, 0x927A,\n\t0xE47B, 0x92A0, 0xE47C, 0x9294, 0xE47D, 0x92AA, 0xE47E, 0x928D,\n\t0xE4A1, 0x92A6, 0xE4A2, 0x929A, 0xE4A3, 0x92AB, 0xE4A4, 0x9279,\n\t0xE4A5, 0x9297, 0xE4A6, 0x927F, 0xE4A7, 0x92A3, 0xE4A8, 0x92EE,\n\t0xE4A9, 0x928E, 0xE4AA, 0x9282, 0xE4AB, 0x9295, 0xE4AC, 0x92A2,\n\t0xE4AD, 0x927D, 0xE4AE, 0x9288, 0xE4AF, 0x92A1, 0xE4B0, 0x928A,\n\t0xE4B1, 0x9286, 0xE4B2, 0x928C, 0xE4B3, 0x9299, 0xE4B4, 0x92A7,\n\t0xE4B5, 0x927E, 0xE4B6, 0x9287, 0xE4B7, 0x92A9, 0xE4B8, 0x929D,\n\t0xE4B9, 0x928B, 0xE4BA, 0x922D, 0xE4BB, 0x969E, 0xE4BC, 0x96A1,\n\t0xE4BD, 0x96FF, 0xE4BE, 0x9758, 0xE4BF, 0x977D, 0xE4C0, 0x977A,\n\t0xE4C1, 0x977E, 0xE4C2, 0x9783, 0xE4C3, 0x9780, 0xE4C4, 0x9782,\n\t0xE4C5, 0x977B, 0xE4C6, 0x9784, 0xE4C7, 0x9781, 0xE4C8, 0x977F,\n\t0xE4C9, 0x97CE, 0xE4CA, 0x97CD, 0xE4CB, 0x9816, 0xE4CC, 0x98AD,\n\t0xE4CD, 0x98AE, 0xE4CE, 0x9902, 0xE4CF, 0x9900, 0xE4D0, 0x9907,\n\t0xE4D1, 0x999D, 0xE4D2, 0x999C, 0xE4D3, 0x99C3, 0xE4D4, 0x99B9,\n\t0xE4D5, 0x99BB, 0xE4D6, 0x99BA, 0xE4D7, 0x99C2, 0xE4D8, 0x99BD,\n\t0xE4D9, 0x99C7, 0xE4DA, 0x9AB1, 0xE4DB, 0x9AE3, 0xE4DC, 0x9AE7,\n\t0xE4DD, 0x9B3E, 0xE4DE, 0x9B3F, 0xE4DF, 0x9B60, 0xE4E0, 0x9B61,\n\t0xE4E1, 0x9B5F, 0xE4E2, 0x9CF1, 0xE4E3, 0x9CF2, 0xE4E4, 0x9CF5,\n\t0xE4E5, 0x9EA7, 0xE4E6, 0x50FF, 0xE4E7, 0x5103, 0xE4E8, 0x5130,\n\t0xE4E9, 0x50F8, 0xE4EA, 0x5106, 0xE4EB, 0x5107, 0xE4EC, 0x50F6,\n\t0xE4ED, 0x50FE, 0xE4EE, 0x510B, 0xE4EF, 0x510C, 0xE4F0, 0x50FD,\n\t0xE4F1, 0x510A, 0xE4F2, 0x528B, 0xE4F3, 0x528C, 0xE4F4, 0x52F1,\n\t0xE4F5, 0x52EF, 0xE4F6, 0x5648, 0xE4F7, 0x5642, 0xE4F8, 0x564C,\n\t0xE4F9, 0x5635, 0xE4FA, 0x5641, 0xE4FB, 0x564A, 0xE4FC, 0x5649,\n\t0xE4FD, 0x5646, 0xE4FE, 0x5658, 0xE540, 0x565A, 0xE541, 0x5640,\n\t0xE542, 0x5633, 0xE543, 0x563D, 0xE544, 0x562C, 0xE545, 0x563E,\n\t0xE546, 0x5638, 0xE547, 0x562A, 0xE548, 0x563A, 0xE549, 0x571A,\n\t0xE54A, 0x58AB, 0xE54B, 0x589D, 0xE54C, 0x58B1, 0xE54D, 0x58A0,\n\t0xE54E, 0x58A3, 0xE54F, 0x58AF, 0xE550, 0x58AC, 0xE551, 0x58A5,\n\t0xE552, 0x58A1, 0xE553, 0x58FF, 0xE554, 0x5AFF, 0xE555, 0x5AF4,\n\t0xE556, 0x5AFD, 0xE557, 0x5AF7, 0xE558, 0x5AF6, 0xE559, 0x5B03,\n\t0xE55A, 0x5AF8, 0xE55B, 0x5B02, 0xE55C, 0x5AF9, 0xE55D, 0x5B01,\n\t0xE55E, 0x5B07, 0xE55F, 0x5B05, 0xE560, 0x5B0F, 0xE561, 0x5C67,\n\t0xE562, 0x5D99, 0xE563, 0x5D97, 0xE564, 0x5D9F, 0xE565, 0x5D92,\n\t0xE566, 0x5DA2, 0xE567, 0x5D93, 0xE568, 0x5D95, 0xE569, 0x5DA0,\n\t0xE56A, 0x5D9C, 0xE56B, 0x5DA1, 0xE56C, 0x5D9A, 0xE56D, 0x5D9E,\n\t0xE56E, 0x5E69, 0xE56F, 0x5E5D, 0xE570, 0x5E60, 0xE571, 0x5E5C,\n\t0xE572, 0x7DF3, 0xE573, 0x5EDB, 0xE574, 0x5EDE, 0xE575, 0x5EE1,\n\t0xE576, 0x5F49, 0xE577, 0x5FB2, 0xE578, 0x618B, 0xE579, 0x6183,\n\t0xE57A, 0x6179, 0xE57B, 0x61B1, 0xE57C, 0x61B0, 0xE57D, 0x61A2,\n\t0xE57E, 0x6189, 0xE5A1, 0x619B, 0xE5A2, 0x6193, 0xE5A3, 0x61AF,\n\t0xE5A4, 0x61AD, 0xE5A5, 0x619F, 0xE5A6, 0x6192, 0xE5A7, 0x61AA,\n\t0xE5A8, 0x61A1, 0xE5A9, 0x618D, 0xE5AA, 0x6166, 0xE5AB, 0x61B3,\n\t0xE5AC, 0x622D, 0xE5AD, 0x646E, 0xE5AE, 0x6470, 0xE5AF, 0x6496,\n\t0xE5B0, 0x64A0, 0xE5B1, 0x6485, 0xE5B2, 0x6497, 0xE5B3, 0x649C,\n\t0xE5B4, 0x648F, 0xE5B5, 0x648B, 0xE5B6, 0x648A, 0xE5B7, 0x648C,\n\t0xE5B8, 0x64A3, 0xE5B9, 0x649F, 0xE5BA, 0x6468, 0xE5BB, 0x64B1,\n\t0xE5BC, 0x6498, 0xE5BD, 0x6576, 0xE5BE, 0x657A, 0xE5BF, 0x6579,\n\t0xE5C0, 0x657B, 0xE5C1, 0x65B2, 0xE5C2, 0x65B3, 0xE5C3, 0x66B5,\n\t0xE5C4, 0x66B0, 0xE5C5, 0x66A9, 0xE5C6, 0x66B2, 0xE5C7, 0x66B7,\n\t0xE5C8, 0x66AA, 0xE5C9, 0x66AF, 0xE5CA, 0x6A00, 0xE5CB, 0x6A06,\n\t0xE5CC, 0x6A17, 0xE5CD, 0x69E5, 0xE5CE, 0x69F8, 0xE5CF, 0x6A15,\n\t0xE5D0, 0x69F1, 0xE5D1, 0x69E4, 0xE5D2, 0x6A20, 0xE5D3, 0x69FF,\n\t0xE5D4, 0x69EC, 0xE5D5, 0x69E2, 0xE5D6, 0x6A1B, 0xE5D7, 0x6A1D,\n\t0xE5D8, 0x69FE, 0xE5D9, 0x6A27, 0xE5DA, 0x69F2, 0xE5DB, 0x69EE,\n\t0xE5DC, 0x6A14, 0xE5DD, 0x69F7, 0xE5DE, 0x69E7, 0xE5DF, 0x6A40,\n\t0xE5E0, 0x6A08, 0xE5E1, 0x69E6, 0xE5E2, 0x69FB, 0xE5E3, 0x6A0D,\n\t0xE5E4, 0x69FC, 0xE5E5, 0x69EB, 0xE5E6, 0x6A09, 0xE5E7, 0x6A04,\n\t0xE5E8, 0x6A18, 0xE5E9, 0x6A25, 0xE5EA, 0x6A0F, 0xE5EB, 0x69F6,\n\t0xE5EC, 0x6A26, 0xE5ED, 0x6A07, 0xE5EE, 0x69F4, 0xE5EF, 0x6A16,\n\t0xE5F0, 0x6B51, 0xE5F1, 0x6BA5, 0xE5F2, 0x6BA3, 0xE5F3, 0x6BA2,\n\t0xE5F4, 0x6BA6, 0xE5F5, 0x6C01, 0xE5F6, 0x6C00, 0xE5F7, 0x6BFF,\n\t0xE5F8, 0x6C02, 0xE5F9, 0x6F41, 0xE5FA, 0x6F26, 0xE5FB, 0x6F7E,\n\t0xE5FC, 0x6F87, 0xE5FD, 0x6FC6, 0xE5FE, 0x6F92, 0xE640, 0x6F8D,\n\t0xE641, 0x6F89, 0xE642, 0x6F8C, 0xE643, 0x6F62, 0xE644, 0x6F4F,\n\t0xE645, 0x6F85, 0xE646, 0x6F5A, 0xE647, 0x6F96, 0xE648, 0x6F76,\n\t0xE649, 0x6F6C, 0xE64A, 0x6F82, 0xE64B, 0x6F55, 0xE64C, 0x6F72,\n\t0xE64D, 0x6F52, 0xE64E, 0x6F50, 0xE64F, 0x6F57, 0xE650, 0x6F94,\n\t0xE651, 0x6F93, 0xE652, 0x6F5D, 0xE653, 0x6F00, 0xE654, 0x6F61,\n\t0xE655, 0x6F6B, 0xE656, 0x6F7D, 0xE657, 0x6F67, 0xE658, 0x6F90,\n\t0xE659, 0x6F53, 0xE65A, 0x6F8B, 0xE65B, 0x6F69, 0xE65C, 0x6F7F,\n\t0xE65D, 0x6F95, 0xE65E, 0x6F63, 0xE65F, 0x6F77, 0xE660, 0x6F6A,\n\t0xE661, 0x6F7B, 0xE662, 0x71B2, 0xE663, 0x71AF, 0xE664, 0x719B,\n\t0xE665, 0x71B0, 0xE666, 0x71A0, 0xE667, 0x719A, 0xE668, 0x71A9,\n\t0xE669, 0x71B5, 0xE66A, 0x719D, 0xE66B, 0x71A5, 0xE66C, 0x719E,\n\t0xE66D, 0x71A4, 0xE66E, 0x71A1, 0xE66F, 0x71AA, 0xE670, 0x719C,\n\t0xE671, 0x71A7, 0xE672, 0x71B3, 0xE673, 0x7298, 0xE674, 0x729A,\n\t0xE675, 0x7358, 0xE676, 0x7352, 0xE677, 0x735E, 0xE678, 0x735F,\n\t0xE679, 0x7360, 0xE67A, 0x735D, 0xE67B, 0x735B, 0xE67C, 0x7361,\n\t0xE67D, 0x735A, 0xE67E, 0x7359, 0xE6A1, 0x7362, 0xE6A2, 0x7487,\n\t0xE6A3, 0x7489, 0xE6A4, 0x748A, 0xE6A5, 0x7486, 0xE6A6, 0x7481,\n\t0xE6A7, 0x747D, 0xE6A8, 0x7485, 0xE6A9, 0x7488, 0xE6AA, 0x747C,\n\t0xE6AB, 0x7479, 0xE6AC, 0x7508, 0xE6AD, 0x7507, 0xE6AE, 0x757E,\n\t0xE6AF, 0x7625, 0xE6B0, 0x761E, 0xE6B1, 0x7619, 0xE6B2, 0x761D,\n\t0xE6B3, 0x761C, 0xE6B4, 0x7623, 0xE6B5, 0x761A, 0xE6B6, 0x7628,\n\t0xE6B7, 0x761B, 0xE6B8, 0x769C, 0xE6B9, 0x769D, 0xE6BA, 0x769E,\n\t0xE6BB, 0x769B, 0xE6BC, 0x778D, 0xE6BD, 0x778F, 0xE6BE, 0x7789,\n\t0xE6BF, 0x7788, 0xE6C0, 0x78CD, 0xE6C1, 0x78BB, 0xE6C2, 0x78CF,\n\t0xE6C3, 0x78CC, 0xE6C4, 0x78D1, 0xE6C5, 0x78CE, 0xE6C6, 0x78D4,\n\t0xE6C7, 0x78C8, 0xE6C8, 0x78C3, 0xE6C9, 0x78C4, 0xE6CA, 0x78C9,\n\t0xE6CB, 0x799A, 0xE6CC, 0x79A1, 0xE6CD, 0x79A0, 0xE6CE, 0x799C,\n\t0xE6CF, 0x79A2, 0xE6D0, 0x799B, 0xE6D1, 0x6B76, 0xE6D2, 0x7A39,\n\t0xE6D3, 0x7AB2, 0xE6D4, 0x7AB4, 0xE6D5, 0x7AB3, 0xE6D6, 0x7BB7,\n\t0xE6D7, 0x7BCB, 0xE6D8, 0x7BBE, 0xE6D9, 0x7BAC, 0xE6DA, 0x7BCE,\n\t0xE6DB, 0x7BAF, 0xE6DC, 0x7BB9, 0xE6DD, 0x7BCA, 0xE6DE, 0x7BB5,\n\t0xE6DF, 0x7CC5, 0xE6E0, 0x7CC8, 0xE6E1, 0x7CCC, 0xE6E2, 0x7CCB,\n\t0xE6E3, 0x7DF7, 0xE6E4, 0x7DDB, 0xE6E5, 0x7DEA, 0xE6E6, 0x7DE7,\n\t0xE6E7, 0x7DD7, 0xE6E8, 0x7DE1, 0xE6E9, 0x7E03, 0xE6EA, 0x7DFA,\n\t0xE6EB, 0x7DE6, 0xE6EC, 0x7DF6, 0xE6ED, 0x7DF1, 0xE6EE, 0x7DF0,\n\t0xE6EF, 0x7DEE, 0xE6F0, 0x7DDF, 0xE6F1, 0x7F76, 0xE6F2, 0x7FAC,\n\t0xE6F3, 0x7FB0, 0xE6F4, 0x7FAD, 0xE6F5, 0x7FED, 0xE6F6, 0x7FEB,\n\t0xE6F7, 0x7FEA, 0xE6F8, 0x7FEC, 0xE6F9, 0x7FE6, 0xE6FA, 0x7FE8,\n\t0xE6FB, 0x8064, 0xE6FC, 0x8067, 0xE6FD, 0x81A3, 0xE6FE, 0x819F,\n\t0xE740, 0x819E, 0xE741, 0x8195, 0xE742, 0x81A2, 0xE743, 0x8199,\n\t0xE744, 0x8197, 0xE745, 0x8216, 0xE746, 0x824F, 0xE747, 0x8253,\n\t0xE748, 0x8252, 0xE749, 0x8250, 0xE74A, 0x824E, 0xE74B, 0x8251,\n\t0xE74C, 0x8524, 0xE74D, 0x853B, 0xE74E, 0x850F, 0xE74F, 0x8500,\n\t0xE750, 0x8529, 0xE751, 0x850E, 0xE752, 0x8509, 0xE753, 0x850D,\n\t0xE754, 0x851F, 0xE755, 0x850A, 0xE756, 0x8527, 0xE757, 0x851C,\n\t0xE758, 0x84FB, 0xE759, 0x852B, 0xE75A, 0x84FA, 0xE75B, 0x8508,\n\t0xE75C, 0x850C, 0xE75D, 0x84F4, 0xE75E, 0x852A, 0xE75F, 0x84F2,\n\t0xE760, 0x8515, 0xE761, 0x84F7, 0xE762, 0x84EB, 0xE763, 0x84F3,\n\t0xE764, 0x84FC, 0xE765, 0x8512, 0xE766, 0x84EA, 0xE767, 0x84E9,\n\t0xE768, 0x8516, 0xE769, 0x84FE, 0xE76A, 0x8528, 0xE76B, 0x851D,\n\t0xE76C, 0x852E, 0xE76D, 0x8502, 0xE76E, 0x84FD, 0xE76F, 0x851E,\n\t0xE770, 0x84F6, 0xE771, 0x8531, 0xE772, 0x8526, 0xE773, 0x84E7,\n\t0xE774, 0x84E8, 0xE775, 0x84F0, 0xE776, 0x84EF, 0xE777, 0x84F9,\n\t0xE778, 0x8518, 0xE779, 0x8520, 0xE77A, 0x8530, 0xE77B, 0x850B,\n\t0xE77C, 0x8519, 0xE77D, 0x852F, 0xE77E, 0x8662, 0xE7A1, 0x8756,\n\t0xE7A2, 0x8763, 0xE7A3, 0x8764, 0xE7A4, 0x8777, 0xE7A5, 0x87E1,\n\t0xE7A6, 0x8773, 0xE7A7, 0x8758, 0xE7A8, 0x8754, 0xE7A9, 0x875B,\n\t0xE7AA, 0x8752, 0xE7AB, 0x8761, 0xE7AC, 0x875A, 0xE7AD, 0x8751,\n\t0xE7AE, 0x875E, 0xE7AF, 0x876D, 0xE7B0, 0x876A, 0xE7B1, 0x8750,\n\t0xE7B2, 0x874E, 0xE7B3, 0x875F, 0xE7B4, 0x875D, 0xE7B5, 0x876F,\n\t0xE7B6, 0x876C, 0xE7B7, 0x877A, 0xE7B8, 0x876E, 0xE7B9, 0x875C,\n\t0xE7BA, 0x8765, 0xE7BB, 0x874F, 0xE7BC, 0x877B, 0xE7BD, 0x8775,\n\t0xE7BE, 0x8762, 0xE7BF, 0x8767, 0xE7C0, 0x8769, 0xE7C1, 0x885A,\n\t0xE7C2, 0x8905, 0xE7C3, 0x890C, 0xE7C4, 0x8914, 0xE7C5, 0x890B,\n\t0xE7C6, 0x8917, 0xE7C7, 0x8918, 0xE7C8, 0x8919, 0xE7C9, 0x8906,\n\t0xE7CA, 0x8916, 0xE7CB, 0x8911, 0xE7CC, 0x890E, 0xE7CD, 0x8909,\n\t0xE7CE, 0x89A2, 0xE7CF, 0x89A4, 0xE7D0, 0x89A3, 0xE7D1, 0x89ED,\n\t0xE7D2, 0x89F0, 0xE7D3, 0x89EC, 0xE7D4, 0x8ACF, 0xE7D5, 0x8AC6,\n\t0xE7D6, 0x8AB8, 0xE7D7, 0x8AD3, 0xE7D8, 0x8AD1, 0xE7D9, 0x8AD4,\n\t0xE7DA, 0x8AD5, 0xE7DB, 0x8ABB, 0xE7DC, 0x8AD7, 0xE7DD, 0x8ABE,\n\t0xE7DE, 0x8AC0, 0xE7DF, 0x8AC5, 0xE7E0, 0x8AD8, 0xE7E1, 0x8AC3,\n\t0xE7E2, 0x8ABA, 0xE7E3, 0x8ABD, 0xE7E4, 0x8AD9, 0xE7E5, 0x8C3E,\n\t0xE7E6, 0x8C4D, 0xE7E7, 0x8C8F, 0xE7E8, 0x8CE5, 0xE7E9, 0x8CDF,\n\t0xE7EA, 0x8CD9, 0xE7EB, 0x8CE8, 0xE7EC, 0x8CDA, 0xE7ED, 0x8CDD,\n\t0xE7EE, 0x8CE7, 0xE7EF, 0x8DA0, 0xE7F0, 0x8D9C, 0xE7F1, 0x8DA1,\n\t0xE7F2, 0x8D9B, 0xE7F3, 0x8E20, 0xE7F4, 0x8E23, 0xE7F5, 0x8E25,\n\t0xE7F6, 0x8E24, 0xE7F7, 0x8E2E, 0xE7F8, 0x8E15, 0xE7F9, 0x8E1B,\n\t0xE7FA, 0x8E16, 0xE7FB, 0x8E11, 0xE7FC, 0x8E19, 0xE7FD, 0x8E26,\n\t0xE7FE, 0x8E27, 0xE840, 0x8E14, 0xE841, 0x8E12, 0xE842, 0x8E18,\n\t0xE843, 0x8E13, 0xE844, 0x8E1C, 0xE845, 0x8E17, 0xE846, 0x8E1A,\n\t0xE847, 0x8F2C, 0xE848, 0x8F24, 0xE849, 0x8F18, 0xE84A, 0x8F1A,\n\t0xE84B, 0x8F20, 0xE84C, 0x8F23, 0xE84D, 0x8F16, 0xE84E, 0x8F17,\n\t0xE84F, 0x9073, 0xE850, 0x9070, 0xE851, 0x906F, 0xE852, 0x9067,\n\t0xE853, 0x906B, 0xE854, 0x912F, 0xE855, 0x912B, 0xE856, 0x9129,\n\t0xE857, 0x912A, 0xE858, 0x9132, 0xE859, 0x9126, 0xE85A, 0x912E,\n\t0xE85B, 0x9185, 0xE85C, 0x9186, 0xE85D, 0x918A, 0xE85E, 0x9181,\n\t0xE85F, 0x9182, 0xE860, 0x9184, 0xE861, 0x9180, 0xE862, 0x92D0,\n\t0xE863, 0x92C3, 0xE864, 0x92C4, 0xE865, 0x92C0, 0xE866, 0x92D9,\n\t0xE867, 0x92B6, 0xE868, 0x92CF, 0xE869, 0x92F1, 0xE86A, 0x92DF,\n\t0xE86B, 0x92D8, 0xE86C, 0x92E9, 0xE86D, 0x92D7, 0xE86E, 0x92DD,\n\t0xE86F, 0x92CC, 0xE870, 0x92EF, 0xE871, 0x92C2, 0xE872, 0x92E8,\n\t0xE873, 0x92CA, 0xE874, 0x92C8, 0xE875, 0x92CE, 0xE876, 0x92E6,\n\t0xE877, 0x92CD, 0xE878, 0x92D5, 0xE879, 0x92C9, 0xE87A, 0x92E0,\n\t0xE87B, 0x92DE, 0xE87C, 0x92E7, 0xE87D, 0x92D1, 0xE87E, 0x92D3,\n\t0xE8A1, 0x92B5, 0xE8A2, 0x92E1, 0xE8A3, 0x92C6, 0xE8A4, 0x92B4,\n\t0xE8A5, 0x957C, 0xE8A6, 0x95AC, 0xE8A7, 0x95AB, 0xE8A8, 0x95AE,\n\t0xE8A9, 0x95B0, 0xE8AA, 0x96A4, 0xE8AB, 0x96A2, 0xE8AC, 0x96D3,\n\t0xE8AD, 0x9705, 0xE8AE, 0x9708, 0xE8AF, 0x9702, 0xE8B0, 0x975A,\n\t0xE8B1, 0x978A, 0xE8B2, 0x978E, 0xE8B3, 0x9788, 0xE8B4, 0x97D0,\n\t0xE8B5, 0x97CF, 0xE8B6, 0x981E, 0xE8B7, 0x981D, 0xE8B8, 0x9826,\n\t0xE8B9, 0x9829, 0xE8BA, 0x9828, 0xE8BB, 0x9820, 0xE8BC, 0x981B,\n\t0xE8BD, 0x9827, 0xE8BE, 0x98B2, 0xE8BF, 0x9908, 0xE8C0, 0x98FA,\n\t0xE8C1, 0x9911, 0xE8C2, 0x9914, 0xE8C3, 0x9916, 0xE8C4, 0x9917,\n\t0xE8C5, 0x9915, 0xE8C6, 0x99DC, 0xE8C7, 0x99CD, 0xE8C8, 0x99CF,\n\t0xE8C9, 0x99D3, 0xE8CA, 0x99D4, 0xE8CB, 0x99CE, 0xE8CC, 0x99C9,\n\t0xE8CD, 0x99D6, 0xE8CE, 0x99D8, 0xE8CF, 0x99CB, 0xE8D0, 0x99D7,\n\t0xE8D1, 0x99CC, 0xE8D2, 0x9AB3, 0xE8D3, 0x9AEC, 0xE8D4, 0x9AEB,\n\t0xE8D5, 0x9AF3, 0xE8D6, 0x9AF2, 0xE8D7, 0x9AF1, 0xE8D8, 0x9B46,\n\t0xE8D9, 0x9B43, 0xE8DA, 0x9B67, 0xE8DB, 0x9B74, 0xE8DC, 0x9B71,\n\t0xE8DD, 0x9B66, 0xE8DE, 0x9B76, 0xE8DF, 0x9B75, 0xE8E0, 0x9B70,\n\t0xE8E1, 0x9B68, 0xE8E2, 0x9B64, 0xE8E3, 0x9B6C, 0xE8E4, 0x9CFC,\n\t0xE8E5, 0x9CFA, 0xE8E6, 0x9CFD, 0xE8E7, 0x9CFF, 0xE8E8, 0x9CF7,\n\t0xE8E9, 0x9D07, 0xE8EA, 0x9D00, 0xE8EB, 0x9CF9, 0xE8EC, 0x9CFB,\n\t0xE8ED, 0x9D08, 0xE8EE, 0x9D05, 0xE8EF, 0x9D04, 0xE8F0, 0x9E83,\n\t0xE8F1, 0x9ED3, 0xE8F2, 0x9F0F, 0xE8F3, 0x9F10, 0xE8F4, 0x511C,\n\t0xE8F5, 0x5113, 0xE8F6, 0x5117, 0xE8F7, 0x511A, 0xE8F8, 0x5111,\n\t0xE8F9, 0x51DE, 0xE8FA, 0x5334, 0xE8FB, 0x53E1, 0xE8FC, 0x5670,\n\t0xE8FD, 0x5660, 0xE8FE, 0x566E, 0xE940, 0x5673, 0xE941, 0x5666,\n\t0xE942, 0x5663, 0xE943, 0x566D, 0xE944, 0x5672, 0xE945, 0x565E,\n\t0xE946, 0x5677, 0xE947, 0x571C, 0xE948, 0x571B, 0xE949, 0x58C8,\n\t0xE94A, 0x58BD, 0xE94B, 0x58C9, 0xE94C, 0x58BF, 0xE94D, 0x58BA,\n\t0xE94E, 0x58C2, 0xE94F, 0x58BC, 0xE950, 0x58C6, 0xE951, 0x5B17,\n\t0xE952, 0x5B19, 0xE953, 0x5B1B, 0xE954, 0x5B21, 0xE955, 0x5B14,\n\t0xE956, 0x5B13, 0xE957, 0x5B10, 0xE958, 0x5B16, 0xE959, 0x5B28,\n\t0xE95A, 0x5B1A, 0xE95B, 0x5B20, 0xE95C, 0x5B1E, 0xE95D, 0x5BEF,\n\t0xE95E, 0x5DAC, 0xE95F, 0x5DB1, 0xE960, 0x5DA9, 0xE961, 0x5DA7,\n\t0xE962, 0x5DB5, 0xE963, 0x5DB0, 0xE964, 0x5DAE, 0xE965, 0x5DAA,\n\t0xE966, 0x5DA8, 0xE967, 0x5DB2, 0xE968, 0x5DAD, 0xE969, 0x5DAF,\n\t0xE96A, 0x5DB4, 0xE96B, 0x5E67, 0xE96C, 0x5E68, 0xE96D, 0x5E66,\n\t0xE96E, 0x5E6F, 0xE96F, 0x5EE9, 0xE970, 0x5EE7, 0xE971, 0x5EE6,\n\t0xE972, 0x5EE8, 0xE973, 0x5EE5, 0xE974, 0x5F4B, 0xE975, 0x5FBC,\n\t0xE976, 0x619D, 0xE977, 0x61A8, 0xE978, 0x6196, 0xE979, 0x61C5,\n\t0xE97A, 0x61B4, 0xE97B, 0x61C6, 0xE97C, 0x61C1, 0xE97D, 0x61CC,\n\t0xE97E, 0x61BA, 0xE9A1, 0x61BF, 0xE9A2, 0x61B8, 0xE9A3, 0x618C,\n\t0xE9A4, 0x64D7, 0xE9A5, 0x64D6, 0xE9A6, 0x64D0, 0xE9A7, 0x64CF,\n\t0xE9A8, 0x64C9, 0xE9A9, 0x64BD, 0xE9AA, 0x6489, 0xE9AB, 0x64C3,\n\t0xE9AC, 0x64DB, 0xE9AD, 0x64F3, 0xE9AE, 0x64D9, 0xE9AF, 0x6533,\n\t0xE9B0, 0x657F, 0xE9B1, 0x657C, 0xE9B2, 0x65A2, 0xE9B3, 0x66C8,\n\t0xE9B4, 0x66BE, 0xE9B5, 0x66C0, 0xE9B6, 0x66CA, 0xE9B7, 0x66CB,\n\t0xE9B8, 0x66CF, 0xE9B9, 0x66BD, 0xE9BA, 0x66BB, 0xE9BB, 0x66BA,\n\t0xE9BC, 0x66CC, 0xE9BD, 0x6723, 0xE9BE, 0x6A34, 0xE9BF, 0x6A66,\n\t0xE9C0, 0x6A49, 0xE9C1, 0x6A67, 0xE9C2, 0x6A32, 0xE9C3, 0x6A68,\n\t0xE9C4, 0x6A3E, 0xE9C5, 0x6A5D, 0xE9C6, 0x6A6D, 0xE9C7, 0x6A76,\n\t0xE9C8, 0x6A5B, 0xE9C9, 0x6A51, 0xE9CA, 0x6A28, 0xE9CB, 0x6A5A,\n\t0xE9CC, 0x6A3B, 0xE9CD, 0x6A3F, 0xE9CE, 0x6A41, 0xE9CF, 0x6A6A,\n\t0xE9D0, 0x6A64, 0xE9D1, 0x6A50, 0xE9D2, 0x6A4F, 0xE9D3, 0x6A54,\n\t0xE9D4, 0x6A6F, 0xE9D5, 0x6A69, 0xE9D6, 0x6A60, 0xE9D7, 0x6A3C,\n\t0xE9D8, 0x6A5E, 0xE9D9, 0x6A56, 0xE9DA, 0x6A55, 0xE9DB, 0x6A4D,\n\t0xE9DC, 0x6A4E, 0xE9DD, 0x6A46, 0xE9DE, 0x6B55, 0xE9DF, 0x6B54,\n\t0xE9E0, 0x6B56, 0xE9E1, 0x6BA7, 0xE9E2, 0x6BAA, 0xE9E3, 0x6BAB,\n\t0xE9E4, 0x6BC8, 0xE9E5, 0x6BC7, 0xE9E6, 0x6C04, 0xE9E7, 0x6C03,\n\t0xE9E8, 0x6C06, 0xE9E9, 0x6FAD, 0xE9EA, 0x6FCB, 0xE9EB, 0x6FA3,\n\t0xE9EC, 0x6FC7, 0xE9ED, 0x6FBC, 0xE9EE, 0x6FCE, 0xE9EF, 0x6FC8,\n\t0xE9F0, 0x6F5E, 0xE9F1, 0x6FC4, 0xE9F2, 0x6FBD, 0xE9F3, 0x6F9E,\n\t0xE9F4, 0x6FCA, 0xE9F5, 0x6FA8, 0xE9F6, 0x7004, 0xE9F7, 0x6FA5,\n\t0xE9F8, 0x6FAE, 0xE9F9, 0x6FBA, 0xE9FA, 0x6FAC, 0xE9FB, 0x6FAA,\n\t0xE9FC, 0x6FCF, 0xE9FD, 0x6FBF, 0xE9FE, 0x6FB8, 0xEA40, 0x6FA2,\n\t0xEA41, 0x6FC9, 0xEA42, 0x6FAB, 0xEA43, 0x6FCD, 0xEA44, 0x6FAF,\n\t0xEA45, 0x6FB2, 0xEA46, 0x6FB0, 0xEA47, 0x71C5, 0xEA48, 0x71C2,\n\t0xEA49, 0x71BF, 0xEA4A, 0x71B8, 0xEA4B, 0x71D6, 0xEA4C, 0x71C0,\n\t0xEA4D, 0x71C1, 0xEA4E, 0x71CB, 0xEA4F, 0x71D4, 0xEA50, 0x71CA,\n\t0xEA51, 0x71C7, 0xEA52, 0x71CF, 0xEA53, 0x71BD, 0xEA54, 0x71D8,\n\t0xEA55, 0x71BC, 0xEA56, 0x71C6, 0xEA57, 0x71DA, 0xEA58, 0x71DB,\n\t0xEA59, 0x729D, 0xEA5A, 0x729E, 0xEA5B, 0x7369, 0xEA5C, 0x7366,\n\t0xEA5D, 0x7367, 0xEA5E, 0x736C, 0xEA5F, 0x7365, 0xEA60, 0x736B,\n\t0xEA61, 0x736A, 0xEA62, 0x747F, 0xEA63, 0x749A, 0xEA64, 0x74A0,\n\t0xEA65, 0x7494, 0xEA66, 0x7492, 0xEA67, 0x7495, 0xEA68, 0x74A1,\n\t0xEA69, 0x750B, 0xEA6A, 0x7580, 0xEA6B, 0x762F, 0xEA6C, 0x762D,\n\t0xEA6D, 0x7631, 0xEA6E, 0x763D, 0xEA6F, 0x7633, 0xEA70, 0x763C,\n\t0xEA71, 0x7635, 0xEA72, 0x7632, 0xEA73, 0x7630, 0xEA74, 0x76BB,\n\t0xEA75, 0x76E6, 0xEA76, 0x779A, 0xEA77, 0x779D, 0xEA78, 0x77A1,\n\t0xEA79, 0x779C, 0xEA7A, 0x779B, 0xEA7B, 0x77A2, 0xEA7C, 0x77A3,\n\t0xEA7D, 0x7795, 0xEA7E, 0x7799, 0xEAA1, 0x7797, 0xEAA2, 0x78DD,\n\t0xEAA3, 0x78E9, 0xEAA4, 0x78E5, 0xEAA5, 0x78EA, 0xEAA6, 0x78DE,\n\t0xEAA7, 0x78E3, 0xEAA8, 0x78DB, 0xEAA9, 0x78E1, 0xEAAA, 0x78E2,\n\t0xEAAB, 0x78ED, 0xEAAC, 0x78DF, 0xEAAD, 0x78E0, 0xEAAE, 0x79A4,\n\t0xEAAF, 0x7A44, 0xEAB0, 0x7A48, 0xEAB1, 0x7A47, 0xEAB2, 0x7AB6,\n\t0xEAB3, 0x7AB8, 0xEAB4, 0x7AB5, 0xEAB5, 0x7AB1, 0xEAB6, 0x7AB7,\n\t0xEAB7, 0x7BDE, 0xEAB8, 0x7BE3, 0xEAB9, 0x7BE7, 0xEABA, 0x7BDD,\n\t0xEABB, 0x7BD5, 0xEABC, 0x7BE5, 0xEABD, 0x7BDA, 0xEABE, 0x7BE8,\n\t0xEABF, 0x7BF9, 0xEAC0, 0x7BD4, 0xEAC1, 0x7BEA, 0xEAC2, 0x7BE2,\n\t0xEAC3, 0x7BDC, 0xEAC4, 0x7BEB, 0xEAC5, 0x7BD8, 0xEAC6, 0x7BDF,\n\t0xEAC7, 0x7CD2, 0xEAC8, 0x7CD4, 0xEAC9, 0x7CD7, 0xEACA, 0x7CD0,\n\t0xEACB, 0x7CD1, 0xEACC, 0x7E12, 0xEACD, 0x7E21, 0xEACE, 0x7E17,\n\t0xEACF, 0x7E0C, 0xEAD0, 0x7E1F, 0xEAD1, 0x7E20, 0xEAD2, 0x7E13,\n\t0xEAD3, 0x7E0E, 0xEAD4, 0x7E1C, 0xEAD5, 0x7E15, 0xEAD6, 0x7E1A,\n\t0xEAD7, 0x7E22, 0xEAD8, 0x7E0B, 0xEAD9, 0x7E0F, 0xEADA, 0x7E16,\n\t0xEADB, 0x7E0D, 0xEADC, 0x7E14, 0xEADD, 0x7E25, 0xEADE, 0x7E24,\n\t0xEADF, 0x7F43, 0xEAE0, 0x7F7B, 0xEAE1, 0x7F7C, 0xEAE2, 0x7F7A,\n\t0xEAE3, 0x7FB1, 0xEAE4, 0x7FEF, 0xEAE5, 0x802A, 0xEAE6, 0x8029,\n\t0xEAE7, 0x806C, 0xEAE8, 0x81B1, 0xEAE9, 0x81A6, 0xEAEA, 0x81AE,\n\t0xEAEB, 0x81B9, 0xEAEC, 0x81B5, 0xEAED, 0x81AB, 0xEAEE, 0x81B0,\n\t0xEAEF, 0x81AC, 0xEAF0, 0x81B4, 0xEAF1, 0x81B2, 0xEAF2, 0x81B7,\n\t0xEAF3, 0x81A7, 0xEAF4, 0x81F2, 0xEAF5, 0x8255, 0xEAF6, 0x8256,\n\t0xEAF7, 0x8257, 0xEAF8, 0x8556, 0xEAF9, 0x8545, 0xEAFA, 0x856B,\n\t0xEAFB, 0x854D, 0xEAFC, 0x8553, 0xEAFD, 0x8561, 0xEAFE, 0x8558,\n\t0xEB40, 0x8540, 0xEB41, 0x8546, 0xEB42, 0x8564, 0xEB43, 0x8541,\n\t0xEB44, 0x8562, 0xEB45, 0x8544, 0xEB46, 0x8551, 0xEB47, 0x8547,\n\t0xEB48, 0x8563, 0xEB49, 0x853E, 0xEB4A, 0x855B, 0xEB4B, 0x8571,\n\t0xEB4C, 0x854E, 0xEB4D, 0x856E, 0xEB4E, 0x8575, 0xEB4F, 0x8555,\n\t0xEB50, 0x8567, 0xEB51, 0x8560, 0xEB52, 0x858C, 0xEB53, 0x8566,\n\t0xEB54, 0x855D, 0xEB55, 0x8554, 0xEB56, 0x8565, 0xEB57, 0x856C,\n\t0xEB58, 0x8663, 0xEB59, 0x8665, 0xEB5A, 0x8664, 0xEB5B, 0x879B,\n\t0xEB5C, 0x878F, 0xEB5D, 0x8797, 0xEB5E, 0x8793, 0xEB5F, 0x8792,\n\t0xEB60, 0x8788, 0xEB61, 0x8781, 0xEB62, 0x8796, 0xEB63, 0x8798,\n\t0xEB64, 0x8779, 0xEB65, 0x8787, 0xEB66, 0x87A3, 0xEB67, 0x8785,\n\t0xEB68, 0x8790, 0xEB69, 0x8791, 0xEB6A, 0x879D, 0xEB6B, 0x8784,\n\t0xEB6C, 0x8794, 0xEB6D, 0x879C, 0xEB6E, 0x879A, 0xEB6F, 0x8789,\n\t0xEB70, 0x891E, 0xEB71, 0x8926, 0xEB72, 0x8930, 0xEB73, 0x892D,\n\t0xEB74, 0x892E, 0xEB75, 0x8927, 0xEB76, 0x8931, 0xEB77, 0x8922,\n\t0xEB78, 0x8929, 0xEB79, 0x8923, 0xEB7A, 0x892F, 0xEB7B, 0x892C,\n\t0xEB7C, 0x891F, 0xEB7D, 0x89F1, 0xEB7E, 0x8AE0, 0xEBA1, 0x8AE2,\n\t0xEBA2, 0x8AF2, 0xEBA3, 0x8AF4, 0xEBA4, 0x8AF5, 0xEBA5, 0x8ADD,\n\t0xEBA6, 0x8B14, 0xEBA7, 0x8AE4, 0xEBA8, 0x8ADF, 0xEBA9, 0x8AF0,\n\t0xEBAA, 0x8AC8, 0xEBAB, 0x8ADE, 0xEBAC, 0x8AE1, 0xEBAD, 0x8AE8,\n\t0xEBAE, 0x8AFF, 0xEBAF, 0x8AEF, 0xEBB0, 0x8AFB, 0xEBB1, 0x8C91,\n\t0xEBB2, 0x8C92, 0xEBB3, 0x8C90, 0xEBB4, 0x8CF5, 0xEBB5, 0x8CEE,\n\t0xEBB6, 0x8CF1, 0xEBB7, 0x8CF0, 0xEBB8, 0x8CF3, 0xEBB9, 0x8D6C,\n\t0xEBBA, 0x8D6E, 0xEBBB, 0x8DA5, 0xEBBC, 0x8DA7, 0xEBBD, 0x8E33,\n\t0xEBBE, 0x8E3E, 0xEBBF, 0x8E38, 0xEBC0, 0x8E40, 0xEBC1, 0x8E45,\n\t0xEBC2, 0x8E36, 0xEBC3, 0x8E3C, 0xEBC4, 0x8E3D, 0xEBC5, 0x8E41,\n\t0xEBC6, 0x8E30, 0xEBC7, 0x8E3F, 0xEBC8, 0x8EBD, 0xEBC9, 0x8F36,\n\t0xEBCA, 0x8F2E, 0xEBCB, 0x8F35, 0xEBCC, 0x8F32, 0xEBCD, 0x8F39,\n\t0xEBCE, 0x8F37, 0xEBCF, 0x8F34, 0xEBD0, 0x9076, 0xEBD1, 0x9079,\n\t0xEBD2, 0x907B, 0xEBD3, 0x9086, 0xEBD4, 0x90FA, 0xEBD5, 0x9133,\n\t0xEBD6, 0x9135, 0xEBD7, 0x9136, 0xEBD8, 0x9193, 0xEBD9, 0x9190,\n\t0xEBDA, 0x9191, 0xEBDB, 0x918D, 0xEBDC, 0x918F, 0xEBDD, 0x9327,\n\t0xEBDE, 0x931E, 0xEBDF, 0x9308, 0xEBE0, 0x931F, 0xEBE1, 0x9306,\n\t0xEBE2, 0x930F, 0xEBE3, 0x937A, 0xEBE4, 0x9338, 0xEBE5, 0x933C,\n\t0xEBE6, 0x931B, 0xEBE7, 0x9323, 0xEBE8, 0x9312, 0xEBE9, 0x9301,\n\t0xEBEA, 0x9346, 0xEBEB, 0x932D, 0xEBEC, 0x930E, 0xEBED, 0x930D,\n\t0xEBEE, 0x92CB, 0xEBEF, 0x931D, 0xEBF0, 0x92FA, 0xEBF1, 0x9325,\n\t0xEBF2, 0x9313, 0xEBF3, 0x92F9, 0xEBF4, 0x92F7, 0xEBF5, 0x9334,\n\t0xEBF6, 0x9302, 0xEBF7, 0x9324, 0xEBF8, 0x92FF, 0xEBF9, 0x9329,\n\t0xEBFA, 0x9339, 0xEBFB, 0x9335, 0xEBFC, 0x932A, 0xEBFD, 0x9314,\n\t0xEBFE, 0x930C, 0xEC40, 0x930B, 0xEC41, 0x92FE, 0xEC42, 0x9309,\n\t0xEC43, 0x9300, 0xEC44, 0x92FB, 0xEC45, 0x9316, 0xEC46, 0x95BC,\n\t0xEC47, 0x95CD, 0xEC48, 0x95BE, 0xEC49, 0x95B9, 0xEC4A, 0x95BA,\n\t0xEC4B, 0x95B6, 0xEC4C, 0x95BF, 0xEC4D, 0x95B5, 0xEC4E, 0x95BD,\n\t0xEC4F, 0x96A9, 0xEC50, 0x96D4, 0xEC51, 0x970B, 0xEC52, 0x9712,\n\t0xEC53, 0x9710, 0xEC54, 0x9799, 0xEC55, 0x9797, 0xEC56, 0x9794,\n\t0xEC57, 0x97F0, 0xEC58, 0x97F8, 0xEC59, 0x9835, 0xEC5A, 0x982F,\n\t0xEC5B, 0x9832, 0xEC5C, 0x9924, 0xEC5D, 0x991F, 0xEC5E, 0x9927,\n\t0xEC5F, 0x9929, 0xEC60, 0x999E, 0xEC61, 0x99EE, 0xEC62, 0x99EC,\n\t0xEC63, 0x99E5, 0xEC64, 0x99E4, 0xEC65, 0x99F0, 0xEC66, 0x99E3,\n\t0xEC67, 0x99EA, 0xEC68, 0x99E9, 0xEC69, 0x99E7, 0xEC6A, 0x9AB9,\n\t0xEC6B, 0x9ABF, 0xEC6C, 0x9AB4, 0xEC6D, 0x9ABB, 0xEC6E, 0x9AF6,\n\t0xEC6F, 0x9AFA, 0xEC70, 0x9AF9, 0xEC71, 0x9AF7, 0xEC72, 0x9B33,\n\t0xEC73, 0x9B80, 0xEC74, 0x9B85, 0xEC75, 0x9B87, 0xEC76, 0x9B7C,\n\t0xEC77, 0x9B7E, 0xEC78, 0x9B7B, 0xEC79, 0x9B82, 0xEC7A, 0x9B93,\n\t0xEC7B, 0x9B92, 0xEC7C, 0x9B90, 0xEC7D, 0x9B7A, 0xEC7E, 0x9B95,\n\t0xECA1, 0x9B7D, 0xECA2, 0x9B88, 0xECA3, 0x9D25, 0xECA4, 0x9D17,\n\t0xECA5, 0x9D20, 0xECA6, 0x9D1E, 0xECA7, 0x9D14, 0xECA8, 0x9D29,\n\t0xECA9, 0x9D1D, 0xECAA, 0x9D18, 0xECAB, 0x9D22, 0xECAC, 0x9D10,\n\t0xECAD, 0x9D19, 0xECAE, 0x9D1F, 0xECAF, 0x9E88, 0xECB0, 0x9E86,\n\t0xECB1, 0x9E87, 0xECB2, 0x9EAE, 0xECB3, 0x9EAD, 0xECB4, 0x9ED5,\n\t0xECB5, 0x9ED6, 0xECB6, 0x9EFA, 0xECB7, 0x9F12, 0xECB8, 0x9F3D,\n\t0xECB9, 0x5126, 0xECBA, 0x5125, 0xECBB, 0x5122, 0xECBC, 0x5124,\n\t0xECBD, 0x5120, 0xECBE, 0x5129, 0xECBF, 0x52F4, 0xECC0, 0x5693,\n\t0xECC1, 0x568C, 0xECC2, 0x568D, 0xECC3, 0x5686, 0xECC4, 0x5684,\n\t0xECC5, 0x5683, 0xECC6, 0x567E, 0xECC7, 0x5682, 0xECC8, 0x567F,\n\t0xECC9, 0x5681, 0xECCA, 0x58D6, 0xECCB, 0x58D4, 0xECCC, 0x58CF,\n\t0xECCD, 0x58D2, 0xECCE, 0x5B2D, 0xECCF, 0x5B25, 0xECD0, 0x5B32,\n\t0xECD1, 0x5B23, 0xECD2, 0x5B2C, 0xECD3, 0x5B27, 0xECD4, 0x5B26,\n\t0xECD5, 0x5B2F, 0xECD6, 0x5B2E, 0xECD7, 0x5B7B, 0xECD8, 0x5BF1,\n\t0xECD9, 0x5BF2, 0xECDA, 0x5DB7, 0xECDB, 0x5E6C, 0xECDC, 0x5E6A,\n\t0xECDD, 0x5FBE, 0xECDE, 0x5FBB, 0xECDF, 0x61C3, 0xECE0, 0x61B5,\n\t0xECE1, 0x61BC, 0xECE2, 0x61E7, 0xECE3, 0x61E0, 0xECE4, 0x61E5,\n\t0xECE5, 0x61E4, 0xECE6, 0x61E8, 0xECE7, 0x61DE, 0xECE8, 0x64EF,\n\t0xECE9, 0x64E9, 0xECEA, 0x64E3, 0xECEB, 0x64EB, 0xECEC, 0x64E4,\n\t0xECED, 0x64E8, 0xECEE, 0x6581, 0xECEF, 0x6580, 0xECF0, 0x65B6,\n\t0xECF1, 0x65DA, 0xECF2, 0x66D2, 0xECF3, 0x6A8D, 0xECF4, 0x6A96,\n\t0xECF5, 0x6A81, 0xECF6, 0x6AA5, 0xECF7, 0x6A89, 0xECF8, 0x6A9F,\n\t0xECF9, 0x6A9B, 0xECFA, 0x6AA1, 0xECFB, 0x6A9E, 0xECFC, 0x6A87,\n\t0xECFD, 0x6A93, 0xECFE, 0x6A8E, 0xED40, 0x6A95, 0xED41, 0x6A83,\n\t0xED42, 0x6AA8, 0xED43, 0x6AA4, 0xED44, 0x6A91, 0xED45, 0x6A7F,\n\t0xED46, 0x6AA6, 0xED47, 0x6A9A, 0xED48, 0x6A85, 0xED49, 0x6A8C,\n\t0xED4A, 0x6A92, 0xED4B, 0x6B5B, 0xED4C, 0x6BAD, 0xED4D, 0x6C09,\n\t0xED4E, 0x6FCC, 0xED4F, 0x6FA9, 0xED50, 0x6FF4, 0xED51, 0x6FD4,\n\t0xED52, 0x6FE3, 0xED53, 0x6FDC, 0xED54, 0x6FED, 0xED55, 0x6FE7,\n\t0xED56, 0x6FE6, 0xED57, 0x6FDE, 0xED58, 0x6FF2, 0xED59, 0x6FDD,\n\t0xED5A, 0x6FE2, 0xED5B, 0x6FE8, 0xED5C, 0x71E1, 0xED5D, 0x71F1,\n\t0xED5E, 0x71E8, 0xED5F, 0x71F2, 0xED60, 0x71E4, 0xED61, 0x71F0,\n\t0xED62, 0x71E2, 0xED63, 0x7373, 0xED64, 0x736E, 0xED65, 0x736F,\n\t0xED66, 0x7497, 0xED67, 0x74B2, 0xED68, 0x74AB, 0xED69, 0x7490,\n\t0xED6A, 0x74AA, 0xED6B, 0x74AD, 0xED6C, 0x74B1, 0xED6D, 0x74A5,\n\t0xED6E, 0x74AF, 0xED6F, 0x7510, 0xED70, 0x7511, 0xED71, 0x7512,\n\t0xED72, 0x750F, 0xED73, 0x7584, 0xED74, 0x7643, 0xED75, 0x7648,\n\t0xED76, 0x7649, 0xED77, 0x7647, 0xED78, 0x76A4, 0xED79, 0x76E9,\n\t0xED7A, 0x77B5, 0xED7B, 0x77AB, 0xED7C, 0x77B2, 0xED7D, 0x77B7,\n\t0xED7E, 0x77B6, 0xEDA1, 0x77B4, 0xEDA2, 0x77B1, 0xEDA3, 0x77A8,\n\t0xEDA4, 0x77F0, 0xEDA5, 0x78F3, 0xEDA6, 0x78FD, 0xEDA7, 0x7902,\n\t0xEDA8, 0x78FB, 0xEDA9, 0x78FC, 0xEDAA, 0x78F2, 0xEDAB, 0x7905,\n\t0xEDAC, 0x78F9, 0xEDAD, 0x78FE, 0xEDAE, 0x7904, 0xEDAF, 0x79AB,\n\t0xEDB0, 0x79A8, 0xEDB1, 0x7A5C, 0xEDB2, 0x7A5B, 0xEDB3, 0x7A56,\n\t0xEDB4, 0x7A58, 0xEDB5, 0x7A54, 0xEDB6, 0x7A5A, 0xEDB7, 0x7ABE,\n\t0xEDB8, 0x7AC0, 0xEDB9, 0x7AC1, 0xEDBA, 0x7C05, 0xEDBB, 0x7C0F,\n\t0xEDBC, 0x7BF2, 0xEDBD, 0x7C00, 0xEDBE, 0x7BFF, 0xEDBF, 0x7BFB,\n\t0xEDC0, 0x7C0E, 0xEDC1, 0x7BF4, 0xEDC2, 0x7C0B, 0xEDC3, 0x7BF3,\n\t0xEDC4, 0x7C02, 0xEDC5, 0x7C09, 0xEDC6, 0x7C03, 0xEDC7, 0x7C01,\n\t0xEDC8, 0x7BF8, 0xEDC9, 0x7BFD, 0xEDCA, 0x7C06, 0xEDCB, 0x7BF0,\n\t0xEDCC, 0x7BF1, 0xEDCD, 0x7C10, 0xEDCE, 0x7C0A, 0xEDCF, 0x7CE8,\n\t0xEDD0, 0x7E2D, 0xEDD1, 0x7E3C, 0xEDD2, 0x7E42, 0xEDD3, 0x7E33,\n\t0xEDD4, 0x9848, 0xEDD5, 0x7E38, 0xEDD6, 0x7E2A, 0xEDD7, 0x7E49,\n\t0xEDD8, 0x7E40, 0xEDD9, 0x7E47, 0xEDDA, 0x7E29, 0xEDDB, 0x7E4C,\n\t0xEDDC, 0x7E30, 0xEDDD, 0x7E3B, 0xEDDE, 0x7E36, 0xEDDF, 0x7E44,\n\t0xEDE0, 0x7E3A, 0xEDE1, 0x7F45, 0xEDE2, 0x7F7F, 0xEDE3, 0x7F7E,\n\t0xEDE4, 0x7F7D, 0xEDE5, 0x7FF4, 0xEDE6, 0x7FF2, 0xEDE7, 0x802C,\n\t0xEDE8, 0x81BB, 0xEDE9, 0x81C4, 0xEDEA, 0x81CC, 0xEDEB, 0x81CA,\n\t0xEDEC, 0x81C5, 0xEDED, 0x81C7, 0xEDEE, 0x81BC, 0xEDEF, 0x81E9,\n\t0xEDF0, 0x825B, 0xEDF1, 0x825A, 0xEDF2, 0x825C, 0xEDF3, 0x8583,\n\t0xEDF4, 0x8580, 0xEDF5, 0x858F, 0xEDF6, 0x85A7, 0xEDF7, 0x8595,\n\t0xEDF8, 0x85A0, 0xEDF9, 0x858B, 0xEDFA, 0x85A3, 0xEDFB, 0x857B,\n\t0xEDFC, 0x85A4, 0xEDFD, 0x859A, 0xEDFE, 0x859E, 0xEE40, 0x8577,\n\t0xEE41, 0x857C, 0xEE42, 0x8589, 0xEE43, 0x85A1, 0xEE44, 0x857A,\n\t0xEE45, 0x8578, 0xEE46, 0x8557, 0xEE47, 0x858E, 0xEE48, 0x8596,\n\t0xEE49, 0x8586, 0xEE4A, 0x858D, 0xEE4B, 0x8599, 0xEE4C, 0x859D,\n\t0xEE4D, 0x8581, 0xEE4E, 0x85A2, 0xEE4F, 0x8582, 0xEE50, 0x8588,\n\t0xEE51, 0x8585, 0xEE52, 0x8579, 0xEE53, 0x8576, 0xEE54, 0x8598,\n\t0xEE55, 0x8590, 0xEE56, 0x859F, 0xEE57, 0x8668, 0xEE58, 0x87BE,\n\t0xEE59, 0x87AA, 0xEE5A, 0x87AD, 0xEE5B, 0x87C5, 0xEE5C, 0x87B0,\n\t0xEE5D, 0x87AC, 0xEE5E, 0x87B9, 0xEE5F, 0x87B5, 0xEE60, 0x87BC,\n\t0xEE61, 0x87AE, 0xEE62, 0x87C9, 0xEE63, 0x87C3, 0xEE64, 0x87C2,\n\t0xEE65, 0x87CC, 0xEE66, 0x87B7, 0xEE67, 0x87AF, 0xEE68, 0x87C4,\n\t0xEE69, 0x87CA, 0xEE6A, 0x87B4, 0xEE6B, 0x87B6, 0xEE6C, 0x87BF,\n\t0xEE6D, 0x87B8, 0xEE6E, 0x87BD, 0xEE6F, 0x87DE, 0xEE70, 0x87B2,\n\t0xEE71, 0x8935, 0xEE72, 0x8933, 0xEE73, 0x893C, 0xEE74, 0x893E,\n\t0xEE75, 0x8941, 0xEE76, 0x8952, 0xEE77, 0x8937, 0xEE78, 0x8942,\n\t0xEE79, 0x89AD, 0xEE7A, 0x89AF, 0xEE7B, 0x89AE, 0xEE7C, 0x89F2,\n\t0xEE7D, 0x89F3, 0xEE7E, 0x8B1E, 0xEEA1, 0x8B18, 0xEEA2, 0x8B16,\n\t0xEEA3, 0x8B11, 0xEEA4, 0x8B05, 0xEEA5, 0x8B0B, 0xEEA6, 0x8B22,\n\t0xEEA7, 0x8B0F, 0xEEA8, 0x8B12, 0xEEA9, 0x8B15, 0xEEAA, 0x8B07,\n\t0xEEAB, 0x8B0D, 0xEEAC, 0x8B08, 0xEEAD, 0x8B06, 0xEEAE, 0x8B1C,\n\t0xEEAF, 0x8B13, 0xEEB0, 0x8B1A, 0xEEB1, 0x8C4F, 0xEEB2, 0x8C70,\n\t0xEEB3, 0x8C72, 0xEEB4, 0x8C71, 0xEEB5, 0x8C6F, 0xEEB6, 0x8C95,\n\t0xEEB7, 0x8C94, 0xEEB8, 0x8CF9, 0xEEB9, 0x8D6F, 0xEEBA, 0x8E4E,\n\t0xEEBB, 0x8E4D, 0xEEBC, 0x8E53, 0xEEBD, 0x8E50, 0xEEBE, 0x8E4C,\n\t0xEEBF, 0x8E47, 0xEEC0, 0x8F43, 0xEEC1, 0x8F40, 0xEEC2, 0x9085,\n\t0xEEC3, 0x907E, 0xEEC4, 0x9138, 0xEEC5, 0x919A, 0xEEC6, 0x91A2,\n\t0xEEC7, 0x919B, 0xEEC8, 0x9199, 0xEEC9, 0x919F, 0xEECA, 0x91A1,\n\t0xEECB, 0x919D, 0xEECC, 0x91A0, 0xEECD, 0x93A1, 0xEECE, 0x9383,\n\t0xEECF, 0x93AF, 0xEED0, 0x9364, 0xEED1, 0x9356, 0xEED2, 0x9347,\n\t0xEED3, 0x937C, 0xEED4, 0x9358, 0xEED5, 0x935C, 0xEED6, 0x9376,\n\t0xEED7, 0x9349, 0xEED8, 0x9350, 0xEED9, 0x9351, 0xEEDA, 0x9360,\n\t0xEEDB, 0x936D, 0xEEDC, 0x938F, 0xEEDD, 0x934C, 0xEEDE, 0x936A,\n\t0xEEDF, 0x9379, 0xEEE0, 0x9357, 0xEEE1, 0x9355, 0xEEE2, 0x9352,\n\t0xEEE3, 0x934F, 0xEEE4, 0x9371, 0xEEE5, 0x9377, 0xEEE6, 0x937B,\n\t0xEEE7, 0x9361, 0xEEE8, 0x935E, 0xEEE9, 0x9363, 0xEEEA, 0x9367,\n\t0xEEEB, 0x9380, 0xEEEC, 0x934E, 0xEEED, 0x9359, 0xEEEE, 0x95C7,\n\t0xEEEF, 0x95C0, 0xEEF0, 0x95C9, 0xEEF1, 0x95C3, 0xEEF2, 0x95C5,\n\t0xEEF3, 0x95B7, 0xEEF4, 0x96AE, 0xEEF5, 0x96B0, 0xEEF6, 0x96AC,\n\t0xEEF7, 0x9720, 0xEEF8, 0x971F, 0xEEF9, 0x9718, 0xEEFA, 0x971D,\n\t0xEEFB, 0x9719, 0xEEFC, 0x979A, 0xEEFD, 0x97A1, 0xEEFE, 0x979C,\n\t0xEF40, 0x979E, 0xEF41, 0x979D, 0xEF42, 0x97D5, 0xEF43, 0x97D4,\n\t0xEF44, 0x97F1, 0xEF45, 0x9841, 0xEF46, 0x9844, 0xEF47, 0x984A,\n\t0xEF48, 0x9849, 0xEF49, 0x9845, 0xEF4A, 0x9843, 0xEF4B, 0x9925,\n\t0xEF4C, 0x992B, 0xEF4D, 0x992C, 0xEF4E, 0x992A, 0xEF4F, 0x9933,\n\t0xEF50, 0x9932, 0xEF51, 0x992F, 0xEF52, 0x992D, 0xEF53, 0x9931,\n\t0xEF54, 0x9930, 0xEF55, 0x9998, 0xEF56, 0x99A3, 0xEF57, 0x99A1,\n\t0xEF58, 0x9A02, 0xEF59, 0x99FA, 0xEF5A, 0x99F4, 0xEF5B, 0x99F7,\n\t0xEF5C, 0x99F9, 0xEF5D, 0x99F8, 0xEF5E, 0x99F6, 0xEF5F, 0x99FB,\n\t0xEF60, 0x99FD, 0xEF61, 0x99FE, 0xEF62, 0x99FC, 0xEF63, 0x9A03,\n\t0xEF64, 0x9ABE, 0xEF65, 0x9AFE, 0xEF66, 0x9AFD, 0xEF67, 0x9B01,\n\t0xEF68, 0x9AFC, 0xEF69, 0x9B48, 0xEF6A, 0x9B9A, 0xEF6B, 0x9BA8,\n\t0xEF6C, 0x9B9E, 0xEF6D, 0x9B9B, 0xEF6E, 0x9BA6, 0xEF6F, 0x9BA1,\n\t0xEF70, 0x9BA5, 0xEF71, 0x9BA4, 0xEF72, 0x9B86, 0xEF73, 0x9BA2,\n\t0xEF74, 0x9BA0, 0xEF75, 0x9BAF, 0xEF76, 0x9D33, 0xEF77, 0x9D41,\n\t0xEF78, 0x9D67, 0xEF79, 0x9D36, 0xEF7A, 0x9D2E, 0xEF7B, 0x9D2F,\n\t0xEF7C, 0x9D31, 0xEF7D, 0x9D38, 0xEF7E, 0x9D30, 0xEFA1, 0x9D45,\n\t0xEFA2, 0x9D42, 0xEFA3, 0x9D43, 0xEFA4, 0x9D3E, 0xEFA5, 0x9D37,\n\t0xEFA6, 0x9D40, 0xEFA7, 0x9D3D, 0xEFA8, 0x7FF5, 0xEFA9, 0x9D2D,\n\t0xEFAA, 0x9E8A, 0xEFAB, 0x9E89, 0xEFAC, 0x9E8D, 0xEFAD, 0x9EB0,\n\t0xEFAE, 0x9EC8, 0xEFAF, 0x9EDA, 0xEFB0, 0x9EFB, 0xEFB1, 0x9EFF,\n\t0xEFB2, 0x9F24, 0xEFB3, 0x9F23, 0xEFB4, 0x9F22, 0xEFB5, 0x9F54,\n\t0xEFB6, 0x9FA0, 0xEFB7, 0x5131, 0xEFB8, 0x512D, 0xEFB9, 0x512E,\n\t0xEFBA, 0x5698, 0xEFBB, 0x569C, 0xEFBC, 0x5697, 0xEFBD, 0x569A,\n\t0xEFBE, 0x569D, 0xEFBF, 0x5699, 0xEFC0, 0x5970, 0xEFC1, 0x5B3C,\n\t0xEFC2, 0x5C69, 0xEFC3, 0x5C6A, 0xEFC4, 0x5DC0, 0xEFC5, 0x5E6D,\n\t0xEFC6, 0x5E6E, 0xEFC7, 0x61D8, 0xEFC8, 0x61DF, 0xEFC9, 0x61ED,\n\t0xEFCA, 0x61EE, 0xEFCB, 0x61F1, 0xEFCC, 0x61EA, 0xEFCD, 0x61F0,\n\t0xEFCE, 0x61EB, 0xEFCF, 0x61D6, 0xEFD0, 0x61E9, 0xEFD1, 0x64FF,\n\t0xEFD2, 0x6504, 0xEFD3, 0x64FD, 0xEFD4, 0x64F8, 0xEFD5, 0x6501,\n\t0xEFD6, 0x6503, 0xEFD7, 0x64FC, 0xEFD8, 0x6594, 0xEFD9, 0x65DB,\n\t0xEFDA, 0x66DA, 0xEFDB, 0x66DB, 0xEFDC, 0x66D8, 0xEFDD, 0x6AC5,\n\t0xEFDE, 0x6AB9, 0xEFDF, 0x6ABD, 0xEFE0, 0x6AE1, 0xEFE1, 0x6AC6,\n\t0xEFE2, 0x6ABA, 0xEFE3, 0x6AB6, 0xEFE4, 0x6AB7, 0xEFE5, 0x6AC7,\n\t0xEFE6, 0x6AB4, 0xEFE7, 0x6AAD, 0xEFE8, 0x6B5E, 0xEFE9, 0x6BC9,\n\t0xEFEA, 0x6C0B, 0xEFEB, 0x7007, 0xEFEC, 0x700C, 0xEFED, 0x700D,\n\t0xEFEE, 0x7001, 0xEFEF, 0x7005, 0xEFF0, 0x7014, 0xEFF1, 0x700E,\n\t0xEFF2, 0x6FFF, 0xEFF3, 0x7000, 0xEFF4, 0x6FFB, 0xEFF5, 0x7026,\n\t0xEFF6, 0x6FFC, 0xEFF7, 0x6FF7, 0xEFF8, 0x700A, 0xEFF9, 0x7201,\n\t0xEFFA, 0x71FF, 0xEFFB, 0x71F9, 0xEFFC, 0x7203, 0xEFFD, 0x71FD,\n\t0xEFFE, 0x7376, 0xF040, 0x74B8, 0xF041, 0x74C0, 0xF042, 0x74B5,\n\t0xF043, 0x74C1, 0xF044, 0x74BE, 0xF045, 0x74B6, 0xF046, 0x74BB,\n\t0xF047, 0x74C2, 0xF048, 0x7514, 0xF049, 0x7513, 0xF04A, 0x765C,\n\t0xF04B, 0x7664, 0xF04C, 0x7659, 0xF04D, 0x7650, 0xF04E, 0x7653,\n\t0xF04F, 0x7657, 0xF050, 0x765A, 0xF051, 0x76A6, 0xF052, 0x76BD,\n\t0xF053, 0x76EC, 0xF054, 0x77C2, 0xF055, 0x77BA, 0xF056, 0x78FF,\n\t0xF057, 0x790C, 0xF058, 0x7913, 0xF059, 0x7914, 0xF05A, 0x7909,\n\t0xF05B, 0x7910, 0xF05C, 0x7912, 0xF05D, 0x7911, 0xF05E, 0x79AD,\n\t0xF05F, 0x79AC, 0xF060, 0x7A5F, 0xF061, 0x7C1C, 0xF062, 0x7C29,\n\t0xF063, 0x7C19, 0xF064, 0x7C20, 0xF065, 0x7C1F, 0xF066, 0x7C2D,\n\t0xF067, 0x7C1D, 0xF068, 0x7C26, 0xF069, 0x7C28, 0xF06A, 0x7C22,\n\t0xF06B, 0x7C25, 0xF06C, 0x7C30, 0xF06D, 0x7E5C, 0xF06E, 0x7E50,\n\t0xF06F, 0x7E56, 0xF070, 0x7E63, 0xF071, 0x7E58, 0xF072, 0x7E62,\n\t0xF073, 0x7E5F, 0xF074, 0x7E51, 0xF075, 0x7E60, 0xF076, 0x7E57,\n\t0xF077, 0x7E53, 0xF078, 0x7FB5, 0xF079, 0x7FB3, 0xF07A, 0x7FF7,\n\t0xF07B, 0x7FF8, 0xF07C, 0x8075, 0xF07D, 0x81D1, 0xF07E, 0x81D2,\n\t0xF0A1, 0x81D0, 0xF0A2, 0x825F, 0xF0A3, 0x825E, 0xF0A4, 0x85B4,\n\t0xF0A5, 0x85C6, 0xF0A6, 0x85C0, 0xF0A7, 0x85C3, 0xF0A8, 0x85C2,\n\t0xF0A9, 0x85B3, 0xF0AA, 0x85B5, 0xF0AB, 0x85BD, 0xF0AC, 0x85C7,\n\t0xF0AD, 0x85C4, 0xF0AE, 0x85BF, 0xF0AF, 0x85CB, 0xF0B0, 0x85CE,\n\t0xF0B1, 0x85C8, 0xF0B2, 0x85C5, 0xF0B3, 0x85B1, 0xF0B4, 0x85B6,\n\t0xF0B5, 0x85D2, 0xF0B6, 0x8624, 0xF0B7, 0x85B8, 0xF0B8, 0x85B7,\n\t0xF0B9, 0x85BE, 0xF0BA, 0x8669, 0xF0BB, 0x87E7, 0xF0BC, 0x87E6,\n\t0xF0BD, 0x87E2, 0xF0BE, 0x87DB, 0xF0BF, 0x87EB, 0xF0C0, 0x87EA,\n\t0xF0C1, 0x87E5, 0xF0C2, 0x87DF, 0xF0C3, 0x87F3, 0xF0C4, 0x87E4,\n\t0xF0C5, 0x87D4, 0xF0C6, 0x87DC, 0xF0C7, 0x87D3, 0xF0C8, 0x87ED,\n\t0xF0C9, 0x87D8, 0xF0CA, 0x87E3, 0xF0CB, 0x87A4, 0xF0CC, 0x87D7,\n\t0xF0CD, 0x87D9, 0xF0CE, 0x8801, 0xF0CF, 0x87F4, 0xF0D0, 0x87E8,\n\t0xF0D1, 0x87DD, 0xF0D2, 0x8953, 0xF0D3, 0x894B, 0xF0D4, 0x894F,\n\t0xF0D5, 0x894C, 0xF0D6, 0x8946, 0xF0D7, 0x8950, 0xF0D8, 0x8951,\n\t0xF0D9, 0x8949, 0xF0DA, 0x8B2A, 0xF0DB, 0x8B27, 0xF0DC, 0x8B23,\n\t0xF0DD, 0x8B33, 0xF0DE, 0x8B30, 0xF0DF, 0x8B35, 0xF0E0, 0x8B47,\n\t0xF0E1, 0x8B2F, 0xF0E2, 0x8B3C, 0xF0E3, 0x8B3E, 0xF0E4, 0x8B31,\n\t0xF0E5, 0x8B25, 0xF0E6, 0x8B37, 0xF0E7, 0x8B26, 0xF0E8, 0x8B36,\n\t0xF0E9, 0x8B2E, 0xF0EA, 0x8B24, 0xF0EB, 0x8B3B, 0xF0EC, 0x8B3D,\n\t0xF0ED, 0x8B3A, 0xF0EE, 0x8C42, 0xF0EF, 0x8C75, 0xF0F0, 0x8C99,\n\t0xF0F1, 0x8C98, 0xF0F2, 0x8C97, 0xF0F3, 0x8CFE, 0xF0F4, 0x8D04,\n\t0xF0F5, 0x8D02, 0xF0F6, 0x8D00, 0xF0F7, 0x8E5C, 0xF0F8, 0x8E62,\n\t0xF0F9, 0x8E60, 0xF0FA, 0x8E57, 0xF0FB, 0x8E56, 0xF0FC, 0x8E5E,\n\t0xF0FD, 0x8E65, 0xF0FE, 0x8E67, 0xF140, 0x8E5B, 0xF141, 0x8E5A,\n\t0xF142, 0x8E61, 0xF143, 0x8E5D, 0xF144, 0x8E69, 0xF145, 0x8E54,\n\t0xF146, 0x8F46, 0xF147, 0x8F47, 0xF148, 0x8F48, 0xF149, 0x8F4B,\n\t0xF14A, 0x9128, 0xF14B, 0x913A, 0xF14C, 0x913B, 0xF14D, 0x913E,\n\t0xF14E, 0x91A8, 0xF14F, 0x91A5, 0xF150, 0x91A7, 0xF151, 0x91AF,\n\t0xF152, 0x91AA, 0xF153, 0x93B5, 0xF154, 0x938C, 0xF155, 0x9392,\n\t0xF156, 0x93B7, 0xF157, 0x939B, 0xF158, 0x939D, 0xF159, 0x9389,\n\t0xF15A, 0x93A7, 0xF15B, 0x938E, 0xF15C, 0x93AA, 0xF15D, 0x939E,\n\t0xF15E, 0x93A6, 0xF15F, 0x9395, 0xF160, 0x9388, 0xF161, 0x9399,\n\t0xF162, 0x939F, 0xF163, 0x938D, 0xF164, 0x93B1, 0xF165, 0x9391,\n\t0xF166, 0x93B2, 0xF167, 0x93A4, 0xF168, 0x93A8, 0xF169, 0x93B4,\n\t0xF16A, 0x93A3, 0xF16B, 0x93A5, 0xF16C, 0x95D2, 0xF16D, 0x95D3,\n\t0xF16E, 0x95D1, 0xF16F, 0x96B3, 0xF170, 0x96D7, 0xF171, 0x96DA,\n\t0xF172, 0x5DC2, 0xF173, 0x96DF, 0xF174, 0x96D8, 0xF175, 0x96DD,\n\t0xF176, 0x9723, 0xF177, 0x9722, 0xF178, 0x9725, 0xF179, 0x97AC,\n\t0xF17A, 0x97AE, 0xF17B, 0x97A8, 0xF17C, 0x97AB, 0xF17D, 0x97A4,\n\t0xF17E, 0x97AA, 0xF1A1, 0x97A2, 0xF1A2, 0x97A5, 0xF1A3, 0x97D7,\n\t0xF1A4, 0x97D9, 0xF1A5, 0x97D6, 0xF1A6, 0x97D8, 0xF1A7, 0x97FA,\n\t0xF1A8, 0x9850, 0xF1A9, 0x9851, 0xF1AA, 0x9852, 0xF1AB, 0x98B8,\n\t0xF1AC, 0x9941, 0xF1AD, 0x993C, 0xF1AE, 0x993A, 0xF1AF, 0x9A0F,\n\t0xF1B0, 0x9A0B, 0xF1B1, 0x9A09, 0xF1B2, 0x9A0D, 0xF1B3, 0x9A04,\n\t0xF1B4, 0x9A11, 0xF1B5, 0x9A0A, 0xF1B6, 0x9A05, 0xF1B7, 0x9A07,\n\t0xF1B8, 0x9A06, 0xF1B9, 0x9AC0, 0xF1BA, 0x9ADC, 0xF1BB, 0x9B08,\n\t0xF1BC, 0x9B04, 0xF1BD, 0x9B05, 0xF1BE, 0x9B29, 0xF1BF, 0x9B35,\n\t0xF1C0, 0x9B4A, 0xF1C1, 0x9B4C, 0xF1C2, 0x9B4B, 0xF1C3, 0x9BC7,\n\t0xF1C4, 0x9BC6, 0xF1C5, 0x9BC3, 0xF1C6, 0x9BBF, 0xF1C7, 0x9BC1,\n\t0xF1C8, 0x9BB5, 0xF1C9, 0x9BB8, 0xF1CA, 0x9BD3, 0xF1CB, 0x9BB6,\n\t0xF1CC, 0x9BC4, 0xF1CD, 0x9BB9, 0xF1CE, 0x9BBD, 0xF1CF, 0x9D5C,\n\t0xF1D0, 0x9D53, 0xF1D1, 0x9D4F, 0xF1D2, 0x9D4A, 0xF1D3, 0x9D5B,\n\t0xF1D4, 0x9D4B, 0xF1D5, 0x9D59, 0xF1D6, 0x9D56, 0xF1D7, 0x9D4C,\n\t0xF1D8, 0x9D57, 0xF1D9, 0x9D52, 0xF1DA, 0x9D54, 0xF1DB, 0x9D5F,\n\t0xF1DC, 0x9D58, 0xF1DD, 0x9D5A, 0xF1DE, 0x9E8E, 0xF1DF, 0x9E8C,\n\t0xF1E0, 0x9EDF, 0xF1E1, 0x9F01, 0xF1E2, 0x9F00, 0xF1E3, 0x9F16,\n\t0xF1E4, 0x9F25, 0xF1E5, 0x9F2B, 0xF1E6, 0x9F2A, 0xF1E7, 0x9F29,\n\t0xF1E8, 0x9F28, 0xF1E9, 0x9F4C, 0xF1EA, 0x9F55, 0xF1EB, 0x5134,\n\t0xF1EC, 0x5135, 0xF1ED, 0x5296, 0xF1EE, 0x52F7, 0xF1EF, 0x53B4,\n\t0xF1F0, 0x56AB, 0xF1F1, 0x56AD, 0xF1F2, 0x56A6, 0xF1F3, 0x56A7,\n\t0xF1F4, 0x56AA, 0xF1F5, 0x56AC, 0xF1F6, 0x58DA, 0xF1F7, 0x58DD,\n\t0xF1F8, 0x58DB, 0xF1F9, 0x5912, 0xF1FA, 0x5B3D, 0xF1FB, 0x5B3E,\n\t0xF1FC, 0x5B3F, 0xF1FD, 0x5DC3, 0xF1FE, 0x5E70, 0xF240, 0x5FBF,\n\t0xF241, 0x61FB, 0xF242, 0x6507, 0xF243, 0x6510, 0xF244, 0x650D,\n\t0xF245, 0x6509, 0xF246, 0x650C, 0xF247, 0x650E, 0xF248, 0x6584,\n\t0xF249, 0x65DE, 0xF24A, 0x65DD, 0xF24B, 0x66DE, 0xF24C, 0x6AE7,\n\t0xF24D, 0x6AE0, 0xF24E, 0x6ACC, 0xF24F, 0x6AD1, 0xF250, 0x6AD9,\n\t0xF251, 0x6ACB, 0xF252, 0x6ADF, 0xF253, 0x6ADC, 0xF254, 0x6AD0,\n\t0xF255, 0x6AEB, 0xF256, 0x6ACF, 0xF257, 0x6ACD, 0xF258, 0x6ADE,\n\t0xF259, 0x6B60, 0xF25A, 0x6BB0, 0xF25B, 0x6C0C, 0xF25C, 0x7019,\n\t0xF25D, 0x7027, 0xF25E, 0x7020, 0xF25F, 0x7016, 0xF260, 0x702B,\n\t0xF261, 0x7021, 0xF262, 0x7022, 0xF263, 0x7023, 0xF264, 0x7029,\n\t0xF265, 0x7017, 0xF266, 0x7024, 0xF267, 0x701C, 0xF268, 0x702A,\n\t0xF269, 0x720C, 0xF26A, 0x720A, 0xF26B, 0x7207, 0xF26C, 0x7202,\n\t0xF26D, 0x7205, 0xF26E, 0x72A5, 0xF26F, 0x72A6, 0xF270, 0x72A4,\n\t0xF271, 0x72A3, 0xF272, 0x72A1, 0xF273, 0x74CB, 0xF274, 0x74C5,\n\t0xF275, 0x74B7, 0xF276, 0x74C3, 0xF277, 0x7516, 0xF278, 0x7660,\n\t0xF279, 0x77C9, 0xF27A, 0x77CA, 0xF27B, 0x77C4, 0xF27C, 0x77F1,\n\t0xF27D, 0x791D, 0xF27E, 0x791B, 0xF2A1, 0x7921, 0xF2A2, 0x791C,\n\t0xF2A3, 0x7917, 0xF2A4, 0x791E, 0xF2A5, 0x79B0, 0xF2A6, 0x7A67,\n\t0xF2A7, 0x7A68, 0xF2A8, 0x7C33, 0xF2A9, 0x7C3C, 0xF2AA, 0x7C39,\n\t0xF2AB, 0x7C2C, 0xF2AC, 0x7C3B, 0xF2AD, 0x7CEC, 0xF2AE, 0x7CEA,\n\t0xF2AF, 0x7E76, 0xF2B0, 0x7E75, 0xF2B1, 0x7E78, 0xF2B2, 0x7E70,\n\t0xF2B3, 0x7E77, 0xF2B4, 0x7E6F, 0xF2B5, 0x7E7A, 0xF2B6, 0x7E72,\n\t0xF2B7, 0x7E74, 0xF2B8, 0x7E68, 0xF2B9, 0x7F4B, 0xF2BA, 0x7F4A,\n\t0xF2BB, 0x7F83, 0xF2BC, 0x7F86, 0xF2BD, 0x7FB7, 0xF2BE, 0x7FFD,\n\t0xF2BF, 0x7FFE, 0xF2C0, 0x8078, 0xF2C1, 0x81D7, 0xF2C2, 0x81D5,\n\t0xF2C3, 0x8264, 0xF2C4, 0x8261, 0xF2C5, 0x8263, 0xF2C6, 0x85EB,\n\t0xF2C7, 0x85F1, 0xF2C8, 0x85ED, 0xF2C9, 0x85D9, 0xF2CA, 0x85E1,\n\t0xF2CB, 0x85E8, 0xF2CC, 0x85DA, 0xF2CD, 0x85D7, 0xF2CE, 0x85EC,\n\t0xF2CF, 0x85F2, 0xF2D0, 0x85F8, 0xF2D1, 0x85D8, 0xF2D2, 0x85DF,\n\t0xF2D3, 0x85E3, 0xF2D4, 0x85DC, 0xF2D5, 0x85D1, 0xF2D6, 0x85F0,\n\t0xF2D7, 0x85E6, 0xF2D8, 0x85EF, 0xF2D9, 0x85DE, 0xF2DA, 0x85E2,\n\t0xF2DB, 0x8800, 0xF2DC, 0x87FA, 0xF2DD, 0x8803, 0xF2DE, 0x87F6,\n\t0xF2DF, 0x87F7, 0xF2E0, 0x8809, 0xF2E1, 0x880C, 0xF2E2, 0x880B,\n\t0xF2E3, 0x8806, 0xF2E4, 0x87FC, 0xF2E5, 0x8808, 0xF2E6, 0x87FF,\n\t0xF2E7, 0x880A, 0xF2E8, 0x8802, 0xF2E9, 0x8962, 0xF2EA, 0x895A,\n\t0xF2EB, 0x895B, 0xF2EC, 0x8957, 0xF2ED, 0x8961, 0xF2EE, 0x895C,\n\t0xF2EF, 0x8958, 0xF2F0, 0x895D, 0xF2F1, 0x8959, 0xF2F2, 0x8988,\n\t0xF2F3, 0x89B7, 0xF2F4, 0x89B6, 0xF2F5, 0x89F6, 0xF2F6, 0x8B50,\n\t0xF2F7, 0x8B48, 0xF2F8, 0x8B4A, 0xF2F9, 0x8B40, 0xF2FA, 0x8B53,\n\t0xF2FB, 0x8B56, 0xF2FC, 0x8B54, 0xF2FD, 0x8B4B, 0xF2FE, 0x8B55,\n\t0xF340, 0x8B51, 0xF341, 0x8B42, 0xF342, 0x8B52, 0xF343, 0x8B57,\n\t0xF344, 0x8C43, 0xF345, 0x8C77, 0xF346, 0x8C76, 0xF347, 0x8C9A,\n\t0xF348, 0x8D06, 0xF349, 0x8D07, 0xF34A, 0x8D09, 0xF34B, 0x8DAC,\n\t0xF34C, 0x8DAA, 0xF34D, 0x8DAD, 0xF34E, 0x8DAB, 0xF34F, 0x8E6D,\n\t0xF350, 0x8E78, 0xF351, 0x8E73, 0xF352, 0x8E6A, 0xF353, 0x8E6F,\n\t0xF354, 0x8E7B, 0xF355, 0x8EC2, 0xF356, 0x8F52, 0xF357, 0x8F51,\n\t0xF358, 0x8F4F, 0xF359, 0x8F50, 0xF35A, 0x8F53, 0xF35B, 0x8FB4,\n\t0xF35C, 0x9140, 0xF35D, 0x913F, 0xF35E, 0x91B0, 0xF35F, 0x91AD,\n\t0xF360, 0x93DE, 0xF361, 0x93C7, 0xF362, 0x93CF, 0xF363, 0x93C2,\n\t0xF364, 0x93DA, 0xF365, 0x93D0, 0xF366, 0x93F9, 0xF367, 0x93EC,\n\t0xF368, 0x93CC, 0xF369, 0x93D9, 0xF36A, 0x93A9, 0xF36B, 0x93E6,\n\t0xF36C, 0x93CA, 0xF36D, 0x93D4, 0xF36E, 0x93EE, 0xF36F, 0x93E3,\n\t0xF370, 0x93D5, 0xF371, 0x93C4, 0xF372, 0x93CE, 0xF373, 0x93C0,\n\t0xF374, 0x93D2, 0xF375, 0x93E7, 0xF376, 0x957D, 0xF377, 0x95DA,\n\t0xF378, 0x95DB, 0xF379, 0x96E1, 0xF37A, 0x9729, 0xF37B, 0x972B,\n\t0xF37C, 0x972C, 0xF37D, 0x9728, 0xF37E, 0x9726, 0xF3A1, 0x97B3,\n\t0xF3A2, 0x97B7, 0xF3A3, 0x97B6, 0xF3A4, 0x97DD, 0xF3A5, 0x97DE,\n\t0xF3A6, 0x97DF, 0xF3A7, 0x985C, 0xF3A8, 0x9859, 0xF3A9, 0x985D,\n\t0xF3AA, 0x9857, 0xF3AB, 0x98BF, 0xF3AC, 0x98BD, 0xF3AD, 0x98BB,\n\t0xF3AE, 0x98BE, 0xF3AF, 0x9948, 0xF3B0, 0x9947, 0xF3B1, 0x9943,\n\t0xF3B2, 0x99A6, 0xF3B3, 0x99A7, 0xF3B4, 0x9A1A, 0xF3B5, 0x9A15,\n\t0xF3B6, 0x9A25, 0xF3B7, 0x9A1D, 0xF3B8, 0x9A24, 0xF3B9, 0x9A1B,\n\t0xF3BA, 0x9A22, 0xF3BB, 0x9A20, 0xF3BC, 0x9A27, 0xF3BD, 0x9A23,\n\t0xF3BE, 0x9A1E, 0xF3BF, 0x9A1C, 0xF3C0, 0x9A14, 0xF3C1, 0x9AC2,\n\t0xF3C2, 0x9B0B, 0xF3C3, 0x9B0A, 0xF3C4, 0x9B0E, 0xF3C5, 0x9B0C,\n\t0xF3C6, 0x9B37, 0xF3C7, 0x9BEA, 0xF3C8, 0x9BEB, 0xF3C9, 0x9BE0,\n\t0xF3CA, 0x9BDE, 0xF3CB, 0x9BE4, 0xF3CC, 0x9BE6, 0xF3CD, 0x9BE2,\n\t0xF3CE, 0x9BF0, 0xF3CF, 0x9BD4, 0xF3D0, 0x9BD7, 0xF3D1, 0x9BEC,\n\t0xF3D2, 0x9BDC, 0xF3D3, 0x9BD9, 0xF3D4, 0x9BE5, 0xF3D5, 0x9BD5,\n\t0xF3D6, 0x9BE1, 0xF3D7, 0x9BDA, 0xF3D8, 0x9D77, 0xF3D9, 0x9D81,\n\t0xF3DA, 0x9D8A, 0xF3DB, 0x9D84, 0xF3DC, 0x9D88, 0xF3DD, 0x9D71,\n\t0xF3DE, 0x9D80, 0xF3DF, 0x9D78, 0xF3E0, 0x9D86, 0xF3E1, 0x9D8B,\n\t0xF3E2, 0x9D8C, 0xF3E3, 0x9D7D, 0xF3E4, 0x9D6B, 0xF3E5, 0x9D74,\n\t0xF3E6, 0x9D75, 0xF3E7, 0x9D70, 0xF3E8, 0x9D69, 0xF3E9, 0x9D85,\n\t0xF3EA, 0x9D73, 0xF3EB, 0x9D7B, 0xF3EC, 0x9D82, 0xF3ED, 0x9D6F,\n\t0xF3EE, 0x9D79, 0xF3EF, 0x9D7F, 0xF3F0, 0x9D87, 0xF3F1, 0x9D68,\n\t0xF3F2, 0x9E94, 0xF3F3, 0x9E91, 0xF3F4, 0x9EC0, 0xF3F5, 0x9EFC,\n\t0xF3F6, 0x9F2D, 0xF3F7, 0x9F40, 0xF3F8, 0x9F41, 0xF3F9, 0x9F4D,\n\t0xF3FA, 0x9F56, 0xF3FB, 0x9F57, 0xF3FC, 0x9F58, 0xF3FD, 0x5337,\n\t0xF3FE, 0x56B2, 0xF440, 0x56B5, 0xF441, 0x56B3, 0xF442, 0x58E3,\n\t0xF443, 0x5B45, 0xF444, 0x5DC6, 0xF445, 0x5DC7, 0xF446, 0x5EEE,\n\t0xF447, 0x5EEF, 0xF448, 0x5FC0, 0xF449, 0x5FC1, 0xF44A, 0x61F9,\n\t0xF44B, 0x6517, 0xF44C, 0x6516, 0xF44D, 0x6515, 0xF44E, 0x6513,\n\t0xF44F, 0x65DF, 0xF450, 0x66E8, 0xF451, 0x66E3, 0xF452, 0x66E4,\n\t0xF453, 0x6AF3, 0xF454, 0x6AF0, 0xF455, 0x6AEA, 0xF456, 0x6AE8,\n\t0xF457, 0x6AF9, 0xF458, 0x6AF1, 0xF459, 0x6AEE, 0xF45A, 0x6AEF,\n\t0xF45B, 0x703C, 0xF45C, 0x7035, 0xF45D, 0x702F, 0xF45E, 0x7037,\n\t0xF45F, 0x7034, 0xF460, 0x7031, 0xF461, 0x7042, 0xF462, 0x7038,\n\t0xF463, 0x703F, 0xF464, 0x703A, 0xF465, 0x7039, 0xF466, 0x7040,\n\t0xF467, 0x703B, 0xF468, 0x7033, 0xF469, 0x7041, 0xF46A, 0x7213,\n\t0xF46B, 0x7214, 0xF46C, 0x72A8, 0xF46D, 0x737D, 0xF46E, 0x737C,\n\t0xF46F, 0x74BA, 0xF470, 0x76AB, 0xF471, 0x76AA, 0xF472, 0x76BE,\n\t0xF473, 0x76ED, 0xF474, 0x77CC, 0xF475, 0x77CE, 0xF476, 0x77CF,\n\t0xF477, 0x77CD, 0xF478, 0x77F2, 0xF479, 0x7925, 0xF47A, 0x7923,\n\t0xF47B, 0x7927, 0xF47C, 0x7928, 0xF47D, 0x7924, 0xF47E, 0x7929,\n\t0xF4A1, 0x79B2, 0xF4A2, 0x7A6E, 0xF4A3, 0x7A6C, 0xF4A4, 0x7A6D,\n\t0xF4A5, 0x7AF7, 0xF4A6, 0x7C49, 0xF4A7, 0x7C48, 0xF4A8, 0x7C4A,\n\t0xF4A9, 0x7C47, 0xF4AA, 0x7C45, 0xF4AB, 0x7CEE, 0xF4AC, 0x7E7B,\n\t0xF4AD, 0x7E7E, 0xF4AE, 0x7E81, 0xF4AF, 0x7E80, 0xF4B0, 0x7FBA,\n\t0xF4B1, 0x7FFF, 0xF4B2, 0x8079, 0xF4B3, 0x81DB, 0xF4B4, 0x81D9,\n\t0xF4B5, 0x820B, 0xF4B6, 0x8268, 0xF4B7, 0x8269, 0xF4B8, 0x8622,\n\t0xF4B9, 0x85FF, 0xF4BA, 0x8601, 0xF4BB, 0x85FE, 0xF4BC, 0x861B,\n\t0xF4BD, 0x8600, 0xF4BE, 0x85F6, 0xF4BF, 0x8604, 0xF4C0, 0x8609,\n\t0xF4C1, 0x8605, 0xF4C2, 0x860C, 0xF4C3, 0x85FD, 0xF4C4, 0x8819,\n\t0xF4C5, 0x8810, 0xF4C6, 0x8811, 0xF4C7, 0x8817, 0xF4C8, 0x8813,\n\t0xF4C9, 0x8816, 0xF4CA, 0x8963, 0xF4CB, 0x8966, 0xF4CC, 0x89B9,\n\t0xF4CD, 0x89F7, 0xF4CE, 0x8B60, 0xF4CF, 0x8B6A, 0xF4D0, 0x8B5D,\n\t0xF4D1, 0x8B68, 0xF4D2, 0x8B63, 0xF4D3, 0x8B65, 0xF4D4, 0x8B67,\n\t0xF4D5, 0x8B6D, 0xF4D6, 0x8DAE, 0xF4D7, 0x8E86, 0xF4D8, 0x8E88,\n\t0xF4D9, 0x8E84, 0xF4DA, 0x8F59, 0xF4DB, 0x8F56, 0xF4DC, 0x8F57,\n\t0xF4DD, 0x8F55, 0xF4DE, 0x8F58, 0xF4DF, 0x8F5A, 0xF4E0, 0x908D,\n\t0xF4E1, 0x9143, 0xF4E2, 0x9141, 0xF4E3, 0x91B7, 0xF4E4, 0x91B5,\n\t0xF4E5, 0x91B2, 0xF4E6, 0x91B3, 0xF4E7, 0x940B, 0xF4E8, 0x9413,\n\t0xF4E9, 0x93FB, 0xF4EA, 0x9420, 0xF4EB, 0x940F, 0xF4EC, 0x9414,\n\t0xF4ED, 0x93FE, 0xF4EE, 0x9415, 0xF4EF, 0x9410, 0xF4F0, 0x9428,\n\t0xF4F1, 0x9419, 0xF4F2, 0x940D, 0xF4F3, 0x93F5, 0xF4F4, 0x9400,\n\t0xF4F5, 0x93F7, 0xF4F6, 0x9407, 0xF4F7, 0x940E, 0xF4F8, 0x9416,\n\t0xF4F9, 0x9412, 0xF4FA, 0x93FA, 0xF4FB, 0x9409, 0xF4FC, 0x93F8,\n\t0xF4FD, 0x940A, 0xF4FE, 0x93FF, 0xF540, 0x93FC, 0xF541, 0x940C,\n\t0xF542, 0x93F6, 0xF543, 0x9411, 0xF544, 0x9406, 0xF545, 0x95DE,\n\t0xF546, 0x95E0, 0xF547, 0x95DF, 0xF548, 0x972E, 0xF549, 0x972F,\n\t0xF54A, 0x97B9, 0xF54B, 0x97BB, 0xF54C, 0x97FD, 0xF54D, 0x97FE,\n\t0xF54E, 0x9860, 0xF54F, 0x9862, 0xF550, 0x9863, 0xF551, 0x985F,\n\t0xF552, 0x98C1, 0xF553, 0x98C2, 0xF554, 0x9950, 0xF555, 0x994E,\n\t0xF556, 0x9959, 0xF557, 0x994C, 0xF558, 0x994B, 0xF559, 0x9953,\n\t0xF55A, 0x9A32, 0xF55B, 0x9A34, 0xF55C, 0x9A31, 0xF55D, 0x9A2C,\n\t0xF55E, 0x9A2A, 0xF55F, 0x9A36, 0xF560, 0x9A29, 0xF561, 0x9A2E,\n\t0xF562, 0x9A38, 0xF563, 0x9A2D, 0xF564, 0x9AC7, 0xF565, 0x9ACA,\n\t0xF566, 0x9AC6, 0xF567, 0x9B10, 0xF568, 0x9B12, 0xF569, 0x9B11,\n\t0xF56A, 0x9C0B, 0xF56B, 0x9C08, 0xF56C, 0x9BF7, 0xF56D, 0x9C05,\n\t0xF56E, 0x9C12, 0xF56F, 0x9BF8, 0xF570, 0x9C40, 0xF571, 0x9C07,\n\t0xF572, 0x9C0E, 0xF573, 0x9C06, 0xF574, 0x9C17, 0xF575, 0x9C14,\n\t0xF576, 0x9C09, 0xF577, 0x9D9F, 0xF578, 0x9D99, 0xF579, 0x9DA4,\n\t0xF57A, 0x9D9D, 0xF57B, 0x9D92, 0xF57C, 0x9D98, 0xF57D, 0x9D90,\n\t0xF57E, 0x9D9B, 0xF5A1, 0x9DA0, 0xF5A2, 0x9D94, 0xF5A3, 0x9D9C,\n\t0xF5A4, 0x9DAA, 0xF5A5, 0x9D97, 0xF5A6, 0x9DA1, 0xF5A7, 0x9D9A,\n\t0xF5A8, 0x9DA2, 0xF5A9, 0x9DA8, 0xF5AA, 0x9D9E, 0xF5AB, 0x9DA3,\n\t0xF5AC, 0x9DBF, 0xF5AD, 0x9DA9, 0xF5AE, 0x9D96, 0xF5AF, 0x9DA6,\n\t0xF5B0, 0x9DA7, 0xF5B1, 0x9E99, 0xF5B2, 0x9E9B, 0xF5B3, 0x9E9A,\n\t0xF5B4, 0x9EE5, 0xF5B5, 0x9EE4, 0xF5B6, 0x9EE7, 0xF5B7, 0x9EE6,\n\t0xF5B8, 0x9F30, 0xF5B9, 0x9F2E, 0xF5BA, 0x9F5B, 0xF5BB, 0x9F60,\n\t0xF5BC, 0x9F5E, 0xF5BD, 0x9F5D, 0xF5BE, 0x9F59, 0xF5BF, 0x9F91,\n\t0xF5C0, 0x513A, 0xF5C1, 0x5139, 0xF5C2, 0x5298, 0xF5C3, 0x5297,\n\t0xF5C4, 0x56C3, 0xF5C5, 0x56BD, 0xF5C6, 0x56BE, 0xF5C7, 0x5B48,\n\t0xF5C8, 0x5B47, 0xF5C9, 0x5DCB, 0xF5CA, 0x5DCF, 0xF5CB, 0x5EF1,\n\t0xF5CC, 0x61FD, 0xF5CD, 0x651B, 0xF5CE, 0x6B02, 0xF5CF, 0x6AFC,\n\t0xF5D0, 0x6B03, 0xF5D1, 0x6AF8, 0xF5D2, 0x6B00, 0xF5D3, 0x7043,\n\t0xF5D4, 0x7044, 0xF5D5, 0x704A, 0xF5D6, 0x7048, 0xF5D7, 0x7049,\n\t0xF5D8, 0x7045, 0xF5D9, 0x7046, 0xF5DA, 0x721D, 0xF5DB, 0x721A,\n\t0xF5DC, 0x7219, 0xF5DD, 0x737E, 0xF5DE, 0x7517, 0xF5DF, 0x766A,\n\t0xF5E0, 0x77D0, 0xF5E1, 0x792D, 0xF5E2, 0x7931, 0xF5E3, 0x792F,\n\t0xF5E4, 0x7C54, 0xF5E5, 0x7C53, 0xF5E6, 0x7CF2, 0xF5E7, 0x7E8A,\n\t0xF5E8, 0x7E87, 0xF5E9, 0x7E88, 0xF5EA, 0x7E8B, 0xF5EB, 0x7E86,\n\t0xF5EC, 0x7E8D, 0xF5ED, 0x7F4D, 0xF5EE, 0x7FBB, 0xF5EF, 0x8030,\n\t0xF5F0, 0x81DD, 0xF5F1, 0x8618, 0xF5F2, 0x862A, 0xF5F3, 0x8626,\n\t0xF5F4, 0x861F, 0xF5F5, 0x8623, 0xF5F6, 0x861C, 0xF5F7, 0x8619,\n\t0xF5F8, 0x8627, 0xF5F9, 0x862E, 0xF5FA, 0x8621, 0xF5FB, 0x8620,\n\t0xF5FC, 0x8629, 0xF5FD, 0x861E, 0xF5FE, 0x8625, 0xF640, 0x8829,\n\t0xF641, 0x881D, 0xF642, 0x881B, 0xF643, 0x8820, 0xF644, 0x8824,\n\t0xF645, 0x881C, 0xF646, 0x882B, 0xF647, 0x884A, 0xF648, 0x896D,\n\t0xF649, 0x8969, 0xF64A, 0x896E, 0xF64B, 0x896B, 0xF64C, 0x89FA,\n\t0xF64D, 0x8B79, 0xF64E, 0x8B78, 0xF64F, 0x8B45, 0xF650, 0x8B7A,\n\t0xF651, 0x8B7B, 0xF652, 0x8D10, 0xF653, 0x8D14, 0xF654, 0x8DAF,\n\t0xF655, 0x8E8E, 0xF656, 0x8E8C, 0xF657, 0x8F5E, 0xF658, 0x8F5B,\n\t0xF659, 0x8F5D, 0xF65A, 0x9146, 0xF65B, 0x9144, 0xF65C, 0x9145,\n\t0xF65D, 0x91B9, 0xF65E, 0x943F, 0xF65F, 0x943B, 0xF660, 0x9436,\n\t0xF661, 0x9429, 0xF662, 0x943D, 0xF663, 0x943C, 0xF664, 0x9430,\n\t0xF665, 0x9439, 0xF666, 0x942A, 0xF667, 0x9437, 0xF668, 0x942C,\n\t0xF669, 0x9440, 0xF66A, 0x9431, 0xF66B, 0x95E5, 0xF66C, 0x95E4,\n\t0xF66D, 0x95E3, 0xF66E, 0x9735, 0xF66F, 0x973A, 0xF670, 0x97BF,\n\t0xF671, 0x97E1, 0xF672, 0x9864, 0xF673, 0x98C9, 0xF674, 0x98C6,\n\t0xF675, 0x98C0, 0xF676, 0x9958, 0xF677, 0x9956, 0xF678, 0x9A39,\n\t0xF679, 0x9A3D, 0xF67A, 0x9A46, 0xF67B, 0x9A44, 0xF67C, 0x9A42,\n\t0xF67D, 0x9A41, 0xF67E, 0x9A3A, 0xF6A1, 0x9A3F, 0xF6A2, 0x9ACD,\n\t0xF6A3, 0x9B15, 0xF6A4, 0x9B17, 0xF6A5, 0x9B18, 0xF6A6, 0x9B16,\n\t0xF6A7, 0x9B3A, 0xF6A8, 0x9B52, 0xF6A9, 0x9C2B, 0xF6AA, 0x9C1D,\n\t0xF6AB, 0x9C1C, 0xF6AC, 0x9C2C, 0xF6AD, 0x9C23, 0xF6AE, 0x9C28,\n\t0xF6AF, 0x9C29, 0xF6B0, 0x9C24, 0xF6B1, 0x9C21, 0xF6B2, 0x9DB7,\n\t0xF6B3, 0x9DB6, 0xF6B4, 0x9DBC, 0xF6B5, 0x9DC1, 0xF6B6, 0x9DC7,\n\t0xF6B7, 0x9DCA, 0xF6B8, 0x9DCF, 0xF6B9, 0x9DBE, 0xF6BA, 0x9DC5,\n\t0xF6BB, 0x9DC3, 0xF6BC, 0x9DBB, 0xF6BD, 0x9DB5, 0xF6BE, 0x9DCE,\n\t0xF6BF, 0x9DB9, 0xF6C0, 0x9DBA, 0xF6C1, 0x9DAC, 0xF6C2, 0x9DC8,\n\t0xF6C3, 0x9DB1, 0xF6C4, 0x9DAD, 0xF6C5, 0x9DCC, 0xF6C6, 0x9DB3,\n\t0xF6C7, 0x9DCD, 0xF6C8, 0x9DB2, 0xF6C9, 0x9E7A, 0xF6CA, 0x9E9C,\n\t0xF6CB, 0x9EEB, 0xF6CC, 0x9EEE, 0xF6CD, 0x9EED, 0xF6CE, 0x9F1B,\n\t0xF6CF, 0x9F18, 0xF6D0, 0x9F1A, 0xF6D1, 0x9F31, 0xF6D2, 0x9F4E,\n\t0xF6D3, 0x9F65, 0xF6D4, 0x9F64, 0xF6D5, 0x9F92, 0xF6D6, 0x4EB9,\n\t0xF6D7, 0x56C6, 0xF6D8, 0x56C5, 0xF6D9, 0x56CB, 0xF6DA, 0x5971,\n\t0xF6DB, 0x5B4B, 0xF6DC, 0x5B4C, 0xF6DD, 0x5DD5, 0xF6DE, 0x5DD1,\n\t0xF6DF, 0x5EF2, 0xF6E0, 0x6521, 0xF6E1, 0x6520, 0xF6E2, 0x6526,\n\t0xF6E3, 0x6522, 0xF6E4, 0x6B0B, 0xF6E5, 0x6B08, 0xF6E6, 0x6B09,\n\t0xF6E7, 0x6C0D, 0xF6E8, 0x7055, 0xF6E9, 0x7056, 0xF6EA, 0x7057,\n\t0xF6EB, 0x7052, 0xF6EC, 0x721E, 0xF6ED, 0x721F, 0xF6EE, 0x72A9,\n\t0xF6EF, 0x737F, 0xF6F0, 0x74D8, 0xF6F1, 0x74D5, 0xF6F2, 0x74D9,\n\t0xF6F3, 0x74D7, 0xF6F4, 0x766D, 0xF6F5, 0x76AD, 0xF6F6, 0x7935,\n\t0xF6F7, 0x79B4, 0xF6F8, 0x7A70, 0xF6F9, 0x7A71, 0xF6FA, 0x7C57,\n\t0xF6FB, 0x7C5C, 0xF6FC, 0x7C59, 0xF6FD, 0x7C5B, 0xF6FE, 0x7C5A,\n\t0xF740, 0x7CF4, 0xF741, 0x7CF1, 0xF742, 0x7E91, 0xF743, 0x7F4F,\n\t0xF744, 0x7F87, 0xF745, 0x81DE, 0xF746, 0x826B, 0xF747, 0x8634,\n\t0xF748, 0x8635, 0xF749, 0x8633, 0xF74A, 0x862C, 0xF74B, 0x8632,\n\t0xF74C, 0x8636, 0xF74D, 0x882C, 0xF74E, 0x8828, 0xF74F, 0x8826,\n\t0xF750, 0x882A, 0xF751, 0x8825, 0xF752, 0x8971, 0xF753, 0x89BF,\n\t0xF754, 0x89BE, 0xF755, 0x89FB, 0xF756, 0x8B7E, 0xF757, 0x8B84,\n\t0xF758, 0x8B82, 0xF759, 0x8B86, 0xF75A, 0x8B85, 0xF75B, 0x8B7F,\n\t0xF75C, 0x8D15, 0xF75D, 0x8E95, 0xF75E, 0x8E94, 0xF75F, 0x8E9A,\n\t0xF760, 0x8E92, 0xF761, 0x8E90, 0xF762, 0x8E96, 0xF763, 0x8E97,\n\t0xF764, 0x8F60, 0xF765, 0x8F62, 0xF766, 0x9147, 0xF767, 0x944C,\n\t0xF768, 0x9450, 0xF769, 0x944A, 0xF76A, 0x944B, 0xF76B, 0x944F,\n\t0xF76C, 0x9447, 0xF76D, 0x9445, 0xF76E, 0x9448, 0xF76F, 0x9449,\n\t0xF770, 0x9446, 0xF771, 0x973F, 0xF772, 0x97E3, 0xF773, 0x986A,\n\t0xF774, 0x9869, 0xF775, 0x98CB, 0xF776, 0x9954, 0xF777, 0x995B,\n\t0xF778, 0x9A4E, 0xF779, 0x9A53, 0xF77A, 0x9A54, 0xF77B, 0x9A4C,\n\t0xF77C, 0x9A4F, 0xF77D, 0x9A48, 0xF77E, 0x9A4A, 0xF7A1, 0x9A49,\n\t0xF7A2, 0x9A52, 0xF7A3, 0x9A50, 0xF7A4, 0x9AD0, 0xF7A5, 0x9B19,\n\t0xF7A6, 0x9B2B, 0xF7A7, 0x9B3B, 0xF7A8, 0x9B56, 0xF7A9, 0x9B55,\n\t0xF7AA, 0x9C46, 0xF7AB, 0x9C48, 0xF7AC, 0x9C3F, 0xF7AD, 0x9C44,\n\t0xF7AE, 0x9C39, 0xF7AF, 0x9C33, 0xF7B0, 0x9C41, 0xF7B1, 0x9C3C,\n\t0xF7B2, 0x9C37, 0xF7B3, 0x9C34, 0xF7B4, 0x9C32, 0xF7B5, 0x9C3D,\n\t0xF7B6, 0x9C36, 0xF7B7, 0x9DDB, 0xF7B8, 0x9DD2, 0xF7B9, 0x9DDE,\n\t0xF7BA, 0x9DDA, 0xF7BB, 0x9DCB, 0xF7BC, 0x9DD0, 0xF7BD, 0x9DDC,\n\t0xF7BE, 0x9DD1, 0xF7BF, 0x9DDF, 0xF7C0, 0x9DE9, 0xF7C1, 0x9DD9,\n\t0xF7C2, 0x9DD8, 0xF7C3, 0x9DD6, 0xF7C4, 0x9DF5, 0xF7C5, 0x9DD5,\n\t0xF7C6, 0x9DDD, 0xF7C7, 0x9EB6, 0xF7C8, 0x9EF0, 0xF7C9, 0x9F35,\n\t0xF7CA, 0x9F33, 0xF7CB, 0x9F32, 0xF7CC, 0x9F42, 0xF7CD, 0x9F6B,\n\t0xF7CE, 0x9F95, 0xF7CF, 0x9FA2, 0xF7D0, 0x513D, 0xF7D1, 0x5299,\n\t0xF7D2, 0x58E8, 0xF7D3, 0x58E7, 0xF7D4, 0x5972, 0xF7D5, 0x5B4D,\n\t0xF7D6, 0x5DD8, 0xF7D7, 0x882F, 0xF7D8, 0x5F4F, 0xF7D9, 0x6201,\n\t0xF7DA, 0x6203, 0xF7DB, 0x6204, 0xF7DC, 0x6529, 0xF7DD, 0x6525,\n\t0xF7DE, 0x6596, 0xF7DF, 0x66EB, 0xF7E0, 0x6B11, 0xF7E1, 0x6B12,\n\t0xF7E2, 0x6B0F, 0xF7E3, 0x6BCA, 0xF7E4, 0x705B, 0xF7E5, 0x705A,\n\t0xF7E6, 0x7222, 0xF7E7, 0x7382, 0xF7E8, 0x7381, 0xF7E9, 0x7383,\n\t0xF7EA, 0x7670, 0xF7EB, 0x77D4, 0xF7EC, 0x7C67, 0xF7ED, 0x7C66,\n\t0xF7EE, 0x7E95, 0xF7EF, 0x826C, 0xF7F0, 0x863A, 0xF7F1, 0x8640,\n\t0xF7F2, 0x8639, 0xF7F3, 0x863C, 0xF7F4, 0x8631, 0xF7F5, 0x863B,\n\t0xF7F6, 0x863E, 0xF7F7, 0x8830, 0xF7F8, 0x8832, 0xF7F9, 0x882E,\n\t0xF7FA, 0x8833, 0xF7FB, 0x8976, 0xF7FC, 0x8974, 0xF7FD, 0x8973,\n\t0xF7FE, 0x89FE, 0xF840, 0x8B8C, 0xF841, 0x8B8E, 0xF842, 0x8B8B,\n\t0xF843, 0x8B88, 0xF844, 0x8C45, 0xF845, 0x8D19, 0xF846, 0x8E98,\n\t0xF847, 0x8F64, 0xF848, 0x8F63, 0xF849, 0x91BC, 0xF84A, 0x9462,\n\t0xF84B, 0x9455, 0xF84C, 0x945D, 0xF84D, 0x9457, 0xF84E, 0x945E,\n\t0xF84F, 0x97C4, 0xF850, 0x97C5, 0xF851, 0x9800, 0xF852, 0x9A56,\n\t0xF853, 0x9A59, 0xF854, 0x9B1E, 0xF855, 0x9B1F, 0xF856, 0x9B20,\n\t0xF857, 0x9C52, 0xF858, 0x9C58, 0xF859, 0x9C50, 0xF85A, 0x9C4A,\n\t0xF85B, 0x9C4D, 0xF85C, 0x9C4B, 0xF85D, 0x9C55, 0xF85E, 0x9C59,\n\t0xF85F, 0x9C4C, 0xF860, 0x9C4E, 0xF861, 0x9DFB, 0xF862, 0x9DF7,\n\t0xF863, 0x9DEF, 0xF864, 0x9DE3, 0xF865, 0x9DEB, 0xF866, 0x9DF8,\n\t0xF867, 0x9DE4, 0xF868, 0x9DF6, 0xF869, 0x9DE1, 0xF86A, 0x9DEE,\n\t0xF86B, 0x9DE6, 0xF86C, 0x9DF2, 0xF86D, 0x9DF0, 0xF86E, 0x9DE2,\n\t0xF86F, 0x9DEC, 0xF870, 0x9DF4, 0xF871, 0x9DF3, 0xF872, 0x9DE8,\n\t0xF873, 0x9DED, 0xF874, 0x9EC2, 0xF875, 0x9ED0, 0xF876, 0x9EF2,\n\t0xF877, 0x9EF3, 0xF878, 0x9F06, 0xF879, 0x9F1C, 0xF87A, 0x9F38,\n\t0xF87B, 0x9F37, 0xF87C, 0x9F36, 0xF87D, 0x9F43, 0xF87E, 0x9F4F,\n\t0xF8A1, 0x9F71, 0xF8A2, 0x9F70, 0xF8A3, 0x9F6E, 0xF8A4, 0x9F6F,\n\t0xF8A5, 0x56D3, 0xF8A6, 0x56CD, 0xF8A7, 0x5B4E, 0xF8A8, 0x5C6D,\n\t0xF8A9, 0x652D, 0xF8AA, 0x66ED, 0xF8AB, 0x66EE, 0xF8AC, 0x6B13,\n\t0xF8AD, 0x705F, 0xF8AE, 0x7061, 0xF8AF, 0x705D, 0xF8B0, 0x7060,\n\t0xF8B1, 0x7223, 0xF8B2, 0x74DB, 0xF8B3, 0x74E5, 0xF8B4, 0x77D5,\n\t0xF8B5, 0x7938, 0xF8B6, 0x79B7, 0xF8B7, 0x79B6, 0xF8B8, 0x7C6A,\n\t0xF8B9, 0x7E97, 0xF8BA, 0x7F89, 0xF8BB, 0x826D, 0xF8BC, 0x8643,\n\t0xF8BD, 0x8838, 0xF8BE, 0x8837, 0xF8BF, 0x8835, 0xF8C0, 0x884B,\n\t0xF8C1, 0x8B94, 0xF8C2, 0x8B95, 0xF8C3, 0x8E9E, 0xF8C4, 0x8E9F,\n\t0xF8C5, 0x8EA0, 0xF8C6, 0x8E9D, 0xF8C7, 0x91BE, 0xF8C8, 0x91BD,\n\t0xF8C9, 0x91C2, 0xF8CA, 0x946B, 0xF8CB, 0x9468, 0xF8CC, 0x9469,\n\t0xF8CD, 0x96E5, 0xF8CE, 0x9746, 0xF8CF, 0x9743, 0xF8D0, 0x9747,\n\t0xF8D1, 0x97C7, 0xF8D2, 0x97E5, 0xF8D3, 0x9A5E, 0xF8D4, 0x9AD5,\n\t0xF8D5, 0x9B59, 0xF8D6, 0x9C63, 0xF8D7, 0x9C67, 0xF8D8, 0x9C66,\n\t0xF8D9, 0x9C62, 0xF8DA, 0x9C5E, 0xF8DB, 0x9C60, 0xF8DC, 0x9E02,\n\t0xF8DD, 0x9DFE, 0xF8DE, 0x9E07, 0xF8DF, 0x9E03, 0xF8E0, 0x9E06,\n\t0xF8E1, 0x9E05, 0xF8E2, 0x9E00, 0xF8E3, 0x9E01, 0xF8E4, 0x9E09,\n\t0xF8E5, 0x9DFF, 0xF8E6, 0x9DFD, 0xF8E7, 0x9E04, 0xF8E8, 0x9EA0,\n\t0xF8E9, 0x9F1E, 0xF8EA, 0x9F46, 0xF8EB, 0x9F74, 0xF8EC, 0x9F75,\n\t0xF8ED, 0x9F76, 0xF8EE, 0x56D4, 0xF8EF, 0x652E, 0xF8F0, 0x65B8,\n\t0xF8F1, 0x6B18, 0xF8F2, 0x6B19, 0xF8F3, 0x6B17, 0xF8F4, 0x6B1A,\n\t0xF8F5, 0x7062, 0xF8F6, 0x7226, 0xF8F7, 0x72AA, 0xF8F8, 0x77D8,\n\t0xF8F9, 0x77D9, 0xF8FA, 0x7939, 0xF8FB, 0x7C69, 0xF8FC, 0x7C6B,\n\t0xF8FD, 0x7CF6, 0xF8FE, 0x7E9A, 0xF940, 0x7E98, 0xF941, 0x7E9B,\n\t0xF942, 0x7E99, 0xF943, 0x81E0, 0xF944, 0x81E1, 0xF945, 0x8646,\n\t0xF946, 0x8647, 0xF947, 0x8648, 0xF948, 0x8979, 0xF949, 0x897A,\n\t0xF94A, 0x897C, 0xF94B, 0x897B, 0xF94C, 0x89FF, 0xF94D, 0x8B98,\n\t0xF94E, 0x8B99, 0xF94F, 0x8EA5, 0xF950, 0x8EA4, 0xF951, 0x8EA3,\n\t0xF952, 0x946E, 0xF953, 0x946D, 0xF954, 0x946F, 0xF955, 0x9471,\n\t0xF956, 0x9473, 0xF957, 0x9749, 0xF958, 0x9872, 0xF959, 0x995F,\n\t0xF95A, 0x9C68, 0xF95B, 0x9C6E, 0xF95C, 0x9C6D, 0xF95D, 0x9E0B,\n\t0xF95E, 0x9E0D, 0xF95F, 0x9E10, 0xF960, 0x9E0F, 0xF961, 0x9E12,\n\t0xF962, 0x9E11, 0xF963, 0x9EA1, 0xF964, 0x9EF5, 0xF965, 0x9F09,\n\t0xF966, 0x9F47, 0xF967, 0x9F78, 0xF968, 0x9F7B, 0xF969, 0x9F7A,\n\t0xF96A, 0x9F79, 0xF96B, 0x571E, 0xF96C, 0x7066, 0xF96D, 0x7C6F,\n\t0xF96E, 0x883C, 0xF96F, 0x8DB2, 0xF970, 0x8EA6, 0xF971, 0x91C3,\n\t0xF972, 0x9474, 0xF973, 0x9478, 0xF974, 0x9476, 0xF975, 0x9475,\n\t0xF976, 0x9A60, 0xF977, 0x9C74, 0xF978, 0x9C73, 0xF979, 0x9C71,\n\t0xF97A, 0x9C75, 0xF97B, 0x9E14, 0xF97C, 0x9E13, 0xF97D, 0x9EF6,\n\t0xF97E, 0x9F0A, 0xF9A1, 0x9FA4, 0xF9A2, 0x7068, 0xF9A3, 0x7065,\n\t0xF9A4, 0x7CF7, 0xF9A5, 0x866A, 0xF9A6, 0x883E, 0xF9A7, 0x883D,\n\t0xF9A8, 0x883F, 0xF9A9, 0x8B9E, 0xF9AA, 0x8C9C, 0xF9AB, 0x8EA9,\n\t0xF9AC, 0x8EC9, 0xF9AD, 0x974B, 0xF9AE, 0x9873, 0xF9AF, 0x9874,\n\t0xF9B0, 0x98CC, 0xF9B1, 0x9961, 0xF9B2, 0x99AB, 0xF9B3, 0x9A64,\n\t0xF9B4, 0x9A66, 0xF9B5, 0x9A67, 0xF9B6, 0x9B24, 0xF9B7, 0x9E15,\n\t0xF9B8, 0x9E17, 0xF9B9, 0x9F48, 0xF9BA, 0x6207, 0xF9BB, 0x6B1E,\n\t0xF9BC, 0x7227, 0xF9BD, 0x864C, 0xF9BE, 0x8EA8, 0xF9BF, 0x9482,\n\t0xF9C0, 0x9480, 0xF9C1, 0x9481, 0xF9C2, 0x9A69, 0xF9C3, 0x9A68,\n\t0xF9C4, 0x9B2E, 0xF9C5, 0x9E19, 0xF9C6, 0x7229, 0xF9C7, 0x864B,\n\t0xF9C8, 0x8B9F, 0xF9C9, 0x9483, 0xF9CA, 0x9C79, 0xF9CB, 0x9EB7,\n\t0xF9CC, 0x7675, 0xF9CD, 0x9A6B, 0xF9CE, 0x9C7A, 0xF9CF, 0x9E1D,\n\t0xF9D0, 0x7069, 0xF9D1, 0x706A, 0xF9D2, 0x9EA4, 0xF9D3, 0x9F7E,\n\t0xF9D4, 0x9F49, 0xF9D5, 0x9F98, 0xF9D6, 0x7881, 0xF9D7, 0x92B9,\n\t0xF9D8, 0x88CF, 0xF9D9, 0x58BB, 0xF9DA, 0x6052, 0xF9DB, 0x7CA7,\n\t0xF9DC, 0x5AFA, 0xF9DD, 0x2554, 0xF9DE, 0x2566, 0xF9DF, 0x2557,\n\t0xF9E0, 0x2560, 0xF9E1, 0x256C, 0xF9E2, 0x2563, 0xF9E3, 0x255A,\n\t0xF9E4, 0x2569, 0xF9E5, 0x255D, 0xF9E6, 0x2552, 0xF9E7, 0x2564,\n\t0xF9E8, 0x2555, 0xF9E9, 0x255E, 0xF9EA, 0x256A, 0xF9EB, 0x2561,\n\t0xF9EC, 0x2558, 0xF9ED, 0x2567, 0xF9EE, 0x255B, 0xF9EF, 0x2553,\n\t0xF9F0, 0x2565, 0xF9F1, 0x2556, 0xF9F2, 0x255F, 0xF9F3, 0x256B,\n\t0xF9F4, 0x2562, 0xF9F5, 0x2559, 0xF9F6, 0x2568, 0xF9F7, 0x255C,\n\t0xF9F8, 0x2551, 0xF9F9, 0x2550, 0xF9FA, 0x256D, 0xF9FB, 0x256E,\n\t0xF9FC, 0x2570, 0xF9FD, 0x256F, 0xF9FE, 0x2593, 0, 0\n};\n\n\n\nWCHAR ff_convert (\t/* Converted code, 0 means conversion error */\n\tWCHAR\tchr,\t/* Character code to be converted */\n\tUINT\tdir\t\t/* 0: Unicode to OEM code, 1: OEM code to Unicode */\n)\n{\n\tconst WCHAR *p;\n\tWCHAR c;\n\tint i, n, li, hi;\n\n\n\tif (chr < 0x80) {\t/* ASCII */\n\t\tc = chr;\n\t} else {\n\t\tif (dir) {\t\t/* OEM code to unicode */\n\t\t\tp = oem2uni;\n\t\t\thi = sizeof oem2uni / 4 - 1;\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tp = uni2oem;\n\t\t\thi = sizeof uni2oem / 4 - 1;\n\t\t}\n\t\tli = 0;\n\t\tfor (n = 16; n; n--) {\n\t\t\ti = li + (hi - li) / 2;\n\t\t\tif (chr == p[i * 2]) break;\n\t\t\tif (chr > p[i * 2])\n\t\t\t\tli = i;\n\t\t\telse\n\t\t\t\thi = i;\n\t\t}\n\t\tc = n ? p[i * 2 + 1] : 0;\n\t}\n\n\treturn c;\n}\n\n\n\nWCHAR ff_wtoupper (\t/* Returns upper converted character */\n\tWCHAR chr\t\t/* Unicode character to be upper converted (BMP only) */\n)\n{\n\t/* Compressed upper conversion table */\n\tstatic const WCHAR cvt1[] = {\t/* U+0000 - U+0FFF */\n\t\t/* Basic Latin */\n\t\t0x0061,0x031A,\n\t\t/* Latin-1 Supplement */\n\t\t0x00E0,0x0317,  0x00F8,0x0307,  0x00FF,0x0001,0x0178,\n\t\t/* Latin Extended-A */\n\t\t0x0100,0x0130,  0x0132,0x0106,  0x0139,0x0110,  0x014A,0x012E,  0x0179,0x0106,\n\t\t/* Latin Extended-B */\n\t\t0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,\n\t\t0x01CD,0x0110,  0x01DD,0x0001,0x018E,  0x01DE,0x0112,  0x01F3,0x0003,0x01F1,0x01F4,0x01F4,  0x01F8,0x0128,\n\t\t0x0222,0x0112,  0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,  0x0246,0x010A,\n\t\t/* IPA Extensions */\n\t\t0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,\n\t\t/* Greek, Coptic */\n\t\t0x037B,0x0003,0x03FD,0x03FE,0x03FF,  0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,  0x03B1,0x0311,\n\t\t0x03C2,0x0002,0x03A3,0x03A3,  0x03C4,0x0308,  0x03CC,0x0003,0x038C,0x038E,0x038F,  0x03D8,0x0118,\n\t\t0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,\n\t\t/* Cyrillic */\n\t\t0x0430,0x0320,  0x0450,0x0710,  0x0460,0x0122,  0x048A,0x0136,  0x04C1,0x010E,  0x04CF,0x0001,0x04C0,  0x04D0,0x0144,\n\t\t/* Armenian */\n\t\t0x0561,0x0426,\n\n\t\t0x0000\n\t};\n\tstatic const WCHAR cvt2[] = {\t/* U+1000 - U+FFFF */\n\t\t/* Phonetic Extensions */\n\t\t0x1D7D,0x0001,0x2C63,\n\t\t/* Latin Extended Additional */\n\t\t0x1E00,0x0196,  0x1EA0,0x015A,\n\t\t/* Greek Extended */\n\t\t0x1F00,0x0608,  0x1F10,0x0606,  0x1F20,0x0608,  0x1F30,0x0608,  0x1F40,0x0606,\n\t\t0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,  0x1F60,0x0608,\n\t\t0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,\n\t\t0x1F80,0x0608,  0x1F90,0x0608,  0x1FA0,0x0608,  0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,\n\t\t0x1FCC,0x0001,0x1FC3,  0x1FD0,0x0602,  0x1FE0,0x0602,  0x1FE5,0x0001,0x1FEC,  0x1FF2,0x0001,0x1FFC,\n\t\t/* Letterlike Symbols */\n\t\t0x214E,0x0001,0x2132,\n\t\t/* Number forms */\n\t\t0x2170,0x0210,  0x2184,0x0001,0x2183,\n\t\t/* Enclosed Alphanumerics */\n\t\t0x24D0,0x051A,  0x2C30,0x042F,\n\t\t/* Latin Extended-C */\n\t\t0x2C60,0x0102,  0x2C67,0x0106, 0x2C75,0x0102,\n\t\t/* Coptic */\n\t\t0x2C80,0x0164,\n\t\t/* Georgian Supplement */\n\t\t0x2D00,0x0826,\n\t\t/* Full-width */\n\t\t0xFF41,0x031A,\n\n\t\t0x0000\n\t};\n\tconst WCHAR *p;\n\tWCHAR bc, nc, cmd;\n\n\n\tp = chr < 0x1000 ? cvt1 : cvt2;\n\tfor (;;) {\n\t\tbc = *p++;\t\t\t\t\t\t\t\t/* Get block base */\n\t\tif (!bc || chr < bc) break;\n\t\tnc = *p++; cmd = nc >> 8; nc &= 0xFF;\t/* Get processing command and block size */\n\t\tif (chr < bc + nc) {\t/* In the block? */\n\t\t\tswitch (cmd) {\n\t\t\tcase 0:\tchr = p[chr - bc]; break;\t\t/* Table conversion */\n\t\t\tcase 1:\tchr -= (chr - bc) & 1; break;\t/* Case pairs */\n\t\t\tcase 2: chr -= 16; break;\t\t\t\t/* Shift -16 */\n\t\t\tcase 3:\tchr -= 32; break;\t\t\t\t/* Shift -32 */\n\t\t\tcase 4:\tchr -= 48; break;\t\t\t\t/* Shift -48 */\n\t\t\tcase 5:\tchr -= 26; break;\t\t\t\t/* Shift -26 */\n\t\t\tcase 6:\tchr += 8; break;\t\t\t\t/* Shift +8 */\n\t\t\tcase 7: chr -= 80; break;\t\t\t\t/* Shift -80 */\n\t\t\tcase 8:\tchr -= 0x1C60; break;\t\t\t/* Shift -0x1C60 */\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!cmd) p += nc;\n\t}\n\n\treturn chr;\n}\n\n"
  },
  {
    "path": "lib/fatfs/option/ccsbcs.c",
    "content": "/*------------------------------------------------------------------------*/\n/* Unicode - Local code bidirectional converter  (C)ChaN, 2015            */\n/* (SBCS code pages)                                                      */\n/*------------------------------------------------------------------------*/\n/*  437   U.S.\n/   720   Arabic\n/   737   Greek\n/   771   KBL\n/   775   Baltic\n/   850   Latin 1\n/   852   Latin 2\n/   855   Cyrillic\n/   857   Turkish\n/   860   Portuguese\n/   861   Icelandic\n/   862   Hebrew\n/   863   Canadian French\n/   864   Arabic\n/   865   Nordic\n/   866   Russian\n/   869   Greek 2\n*/\n\n#include \"../ff.h\"\n\n\n#if _CODE_PAGE == 437\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP437(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\n\t0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 720\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP720(0x80-0xFF) to Unicode conversion table */\n\t0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,\n\t0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,\n\t0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,\n\t0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 737\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP737(0x80-0xFF) to Unicode conversion table */\n\t0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,\n\t0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,\n\t0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,\n\t0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 771\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP771(0x80-0xFF) to Unicode conversion table */\n\t0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,\n\t0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,\n\t0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,\n\t0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,\n\t0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 775\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP775(0x80-0xFF) to Unicode conversion table */\n\t0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,\n\t0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,\n\t0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,\n\t0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,\n\t0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 850\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP850(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\n\t0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\n\t0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,\n\t0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,\n\t0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 852\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP852(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,\n\t0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\n\t0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,\n\t0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,\n\t0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 855\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP855(0x80-0xFF) to Unicode conversion table */\n\t0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,\n\t0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,\n\t0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\n\t0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,\n\t0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,\n\t0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 857\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP857(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,\n\t0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\n\t0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,\n\t0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,\n\t0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 860\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP860(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,\n\t0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 861\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP861(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,\n\t0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 862\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP862(0x80-0xFF) to Unicode conversion table */\n\t0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,\n\t0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 863\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP863(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,\n\t0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,\n\t0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 864\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP864(0x80-0xFF) to Unicode conversion table */\n\t0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,\n\t0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,\n\t0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,\n\t0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,\n\t0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,\n\t0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,\n\t0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,\n\t0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000\n};\n\n#elif _CODE_PAGE == 865\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP865(0x80-0xFF) to Unicode conversion table */\n\t0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\n\t0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,\n\t0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\n\t0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 866\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP866(0x80-0xFF) to Unicode conversion table */\n\t0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,\n\t0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,\n\t0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\n\t0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\n\t0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,\n\t0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0\n};\n\n#elif _CODE_PAGE == 869\n#define _TBLDEF 1\nstatic\nconst WCHAR Tbl[] = {\t/*  CP869(0x80-0xFF) to Unicode conversion table */\n\t0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,\n\t0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,\n\t0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,\n\t0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,\n\t0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,\n\t0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,\n\t0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,\n\t0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0\n};\n\n#endif\n\n\n#if !_TBLDEF || !_USE_LFN\n#error This file is not needed at current configuration. Remove from the project.\n#endif\n\n\n\n\nWCHAR ff_convert (\t/* Converted character, Returns zero on error */\n\tWCHAR\tchr,\t/* Character code to be converted */\n\tUINT\tdir\t\t/* 0: Unicode to OEM code, 1: OEM code to Unicode */\n)\n{\n\tWCHAR c;\n\n\n\tif (chr < 0x80) {\t/* ASCII */\n\t\tc = chr;\n\n\t} else {\n\t\tif (dir) {\t\t/* OEM code to Unicode */\n\t\t\tc = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];\n\n\t\t} else {\t\t/* Unicode to OEM code */\n\t\t\tfor (c = 0; c < 0x80; c++) {\n\t\t\t\tif (chr == Tbl[c]) break;\n\t\t\t}\n\t\t\tc = (c + 0x80) & 0xFF;\n\t\t}\n\t}\n\n\treturn c;\n}\n\n\n\nWCHAR ff_wtoupper (\t/* Returns upper converted character */\n\tWCHAR chr\t\t/* Unicode character to be upper converted (BMP only) */\n)\n{\n\t/* Compressed upper conversion table */\n\tstatic const WCHAR cvt1[] = {\t/* U+0000 - U+0FFF */\n\t\t/* Basic Latin */\n\t\t0x0061,0x031A,\n\t\t/* Latin-1 Supplement */\n\t\t0x00E0,0x0317,  0x00F8,0x0307,  0x00FF,0x0001,0x0178,\n\t\t/* Latin Extended-A */\n\t\t0x0100,0x0130,  0x0132,0x0106,  0x0139,0x0110,  0x014A,0x012E,  0x0179,0x0106,\n\t\t/* Latin Extended-B */\n\t\t0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,\n\t\t0x01CD,0x0110,  0x01DD,0x0001,0x018E,  0x01DE,0x0112,  0x01F3,0x0003,0x01F1,0x01F4,0x01F4,  0x01F8,0x0128,\n\t\t0x0222,0x0112,  0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,  0x0246,0x010A,\n\t\t/* IPA Extensions */\n\t\t0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,\n\t\t/* Greek, Coptic */\n\t\t0x037B,0x0003,0x03FD,0x03FE,0x03FF,  0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,  0x03B1,0x0311,\n\t\t0x03C2,0x0002,0x03A3,0x03A3,  0x03C4,0x0308,  0x03CC,0x0003,0x038C,0x038E,0x038F,  0x03D8,0x0118,\n\t\t0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,\n\t\t/* Cyrillic */\n\t\t0x0430,0x0320,  0x0450,0x0710,  0x0460,0x0122,  0x048A,0x0136,  0x04C1,0x010E,  0x04CF,0x0001,0x04C0,  0x04D0,0x0144,\n\t\t/* Armenian */\n\t\t0x0561,0x0426,\n\n\t\t0x0000\n\t};\n\tstatic const WCHAR cvt2[] = {\t/* U+1000 - U+FFFF */\n\t\t/* Phonetic Extensions */\n\t\t0x1D7D,0x0001,0x2C63,\n\t\t/* Latin Extended Additional */\n\t\t0x1E00,0x0196,  0x1EA0,0x015A,\n\t\t/* Greek Extended */\n\t\t0x1F00,0x0608,  0x1F10,0x0606,  0x1F20,0x0608,  0x1F30,0x0608,  0x1F40,0x0606,\n\t\t0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,  0x1F60,0x0608,\n\t\t0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,\n\t\t0x1F80,0x0608,  0x1F90,0x0608,  0x1FA0,0x0608,  0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,\n\t\t0x1FCC,0x0001,0x1FC3,  0x1FD0,0x0602,  0x1FE0,0x0602,  0x1FE5,0x0001,0x1FEC,  0x1FF2,0x0001,0x1FFC,\n\t\t/* Letterlike Symbols */\n\t\t0x214E,0x0001,0x2132,\n\t\t/* Number forms */\n\t\t0x2170,0x0210,  0x2184,0x0001,0x2183,\n\t\t/* Enclosed Alphanumerics */\n\t\t0x24D0,0x051A,  0x2C30,0x042F,\n\t\t/* Latin Extended-C */\n\t\t0x2C60,0x0102,  0x2C67,0x0106, 0x2C75,0x0102,\n\t\t/* Coptic */\n\t\t0x2C80,0x0164,\n\t\t/* Georgian Supplement */\n\t\t0x2D00,0x0826,\n\t\t/* Full-width */\n\t\t0xFF41,0x031A,\n\n\t\t0x0000\n\t};\n\tconst WCHAR *p;\n\tWCHAR bc, nc, cmd;\n\n\n\tp = chr < 0x1000 ? cvt1 : cvt2;\n\tfor (;;) {\n\t\tbc = *p++;\t\t\t\t\t\t\t\t/* Get block base */\n\t\tif (!bc || chr < bc) break;\n\t\tnc = *p++; cmd = nc >> 8; nc &= 0xFF;\t/* Get processing command and block size */\n\t\tif (chr < bc + nc) {\t/* In the block? */\n\t\t\tswitch (cmd) {\n\t\t\tcase 0:\tchr = p[chr - bc]; break;\t\t/* Table conversion */\n\t\t\tcase 1:\tchr -= (chr - bc) & 1; break;\t/* Case pairs */\n\t\t\tcase 2: chr -= 16; break;\t\t\t\t/* Shift -16 */\n\t\t\tcase 3:\tchr -= 32; break;\t\t\t\t/* Shift -32 */\n\t\t\tcase 4:\tchr -= 48; break;\t\t\t\t/* Shift -48 */\n\t\t\tcase 5:\tchr -= 26; break;\t\t\t\t/* Shift -26 */\n\t\t\tcase 6:\tchr += 8; break;\t\t\t\t/* Shift +8 */\n\t\t\tcase 7: chr -= 80; break;\t\t\t\t/* Shift -80 */\n\t\t\tcase 8:\tchr -= 0x1C60; break;\t\t\t/* Shift -0x1C60 */\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!cmd) p += nc;\n\t}\n\n\treturn chr;\n}\n\n"
  },
  {
    "path": "lib/fatfs/option/unicode.c",
    "content": "#include \"../ff.h\"\n\n#if _USE_LFN != 0\n\n#if   _CODE_PAGE == 932\t/* Japanese Shift_JIS */\n#include \"cc932.c\"\n#elif _CODE_PAGE == 936\t/* Simplified Chinese GBK */\n#include \"cc936.c\"\n#elif _CODE_PAGE == 949\t/* Korean */\n#include \"cc949.c\"\n#elif _CODE_PAGE == 950\t/* Traditional Chinese Big5 */\n#include \"cc950.c\"\n#else\t\t\t\t\t/* Single Byte Character-Set */\n#include \"ccsbcs.c\"\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/flipper_application/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/flipper_application\",\n    ],\n    SDK_HEADERS=[\n        File(\"flipper_application.h\"),\n        File(\"plugins/plugin_manager.h\"),\n        File(\"plugins/composite_resolver.h\"),\n        File(\"api_hashtable/api_hashtable.h\"),\n        File(\"api_hashtable/compilesort.hpp\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"flipper_application\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\nsources.append(File(\"api_hashtable/api_hashtable.cpp\"))\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/flipper_application/api_hashtable/api_hashtable.cpp",
    "content": "#include \"api_hashtable.h\"\n\n#include <furi.h>\n#include <algorithm>\n\n#define TAG \"ApiHashtable\"\n\nbool elf_resolve_from_hashtable(\n    const ElfApiInterface* interface,\n    uint32_t hash,\n    Elf32_Addr* address) {\n\n    furi_check(interface);\n    furi_check(address);\n\n    bool result = false;\n    const HashtableApiInterface* hashtable_interface =\n        static_cast<const HashtableApiInterface*>(interface);\n\n    sym_entry key = {\n        .hash = hash,\n        .address = 0,\n    };\n\n    auto find_res =\n        std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);\n    if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) {\n        FURI_LOG_T(\n            TAG, \"Can't find symbol with hash %lx @ %p!\", hash, hashtable_interface->table_cbegin);\n        result = false;\n    } else {\n        result = true;\n        *address = find_res->address;\n    }\n\n    return result;\n}\n\nuint32_t elf_symbolname_hash(const char* s) {\n    furi_check(s);\n    return elf_gnu_hash(s);\n}"
  },
  {
    "path": "lib/flipper_application/api_hashtable/api_hashtable.h",
    "content": "#pragma once\n\n#include <flipper_application/elf/elf_api_interface.h>\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Symbol table entry\n */\nstruct sym_entry {\n    uint32_t hash;\n    uint32_t address;\n};\n\n/**\n * @brief Resolver for API entries using a pre-sorted table with hashes\n * @param interface pointer to HashtableApiInterface\n * @param hash gnu hash of function name\n * @param address output for function address\n * @return true if the table contains a function\n */\nbool elf_resolve_from_hashtable(\n    const ElfApiInterface* interface,\n    uint32_t hash,\n    Elf32_Addr* address);\n\nuint32_t elf_symbolname_hash(const char* s);\n\n#ifdef __cplusplus\n}\n\n#include <array>\n#include <algorithm>\n\n/**\n * @brief  HashtableApiInterface is an implementation of ElfApiInterface\n * that uses a hash table to resolve function addresses.\n * table_cbegin and table_cend must point to a sorted array of sym_entry\n */\nstruct HashtableApiInterface : public ElfApiInterface {\n    const sym_entry *table_cbegin, *table_cend;\n};\n\n#define API_METHOD(x, ret_type, args_type)                                                     \\\n    sym_entry {                                                                                \\\n        .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast<ret_type(*) args_type>(x)) \\\n    }\n\n#define API_VARIABLE(x, var_type)                              \\\n    sym_entry {                                                \\\n        .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \\\n    }\n\nconstexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {\n    return k1.hash < k2.hash;\n}\n\n/**\n * @brief Calculate hash for a string using the ELF GNU hash algorithm\n * @param s string to calculate hash for\n * @return hash value\n */\nconstexpr uint32_t elf_gnu_hash(const char* s) {\n    uint32_t h = 0x1505;\n    for(unsigned char c = *s; c != '\\0'; c = *++s) {\n        h = (h << 5) + h + c;\n    }\n    return h;\n}\n\n/* Compile-time check for hash collisions in API table.\n * Usage: static_assert(!has_hash_collisions(api_methods), \"Hash collision detected\"); \n */\ntemplate <std::size_t N>\nconstexpr bool has_hash_collisions(const std::array<sym_entry, N>& api_methods) {\n    for(std::size_t i = 0; i < (N - 1); ++i) {\n        if(api_methods[i].hash == api_methods[i + 1].hash) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n#endif"
  },
  {
    "path": "lib/flipper_application/api_hashtable/compilesort.hpp",
    "content": "/**\n * Implementation of compile-time sort for symbol table entries.\n */\n\n#pragma once\n\n#ifdef __cplusplus\n\n#include <iterator>\n#include <array>\n\nnamespace cstd {\n\ntemplate <typename RAIt>\nconstexpr RAIt next(RAIt it, typename std::iterator_traits<RAIt>::difference_type n = 1) {\n    return it + n;\n}\n\ntemplate <typename RAIt>\nconstexpr auto distance(RAIt first, RAIt last) {\n    return last - first;\n}\n\ntemplate <class ForwardIt1, class ForwardIt2>\nconstexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) {\n    auto temp = std::move(*a);\n    *a = std::move(*b);\n    *b = std::move(temp);\n}\n\ntemplate <class InputIt, class UnaryPredicate>\nconstexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) {\n    for(; first != last; ++first) {\n        if(!q(*first)) {\n            return first;\n        }\n    }\n    return last;\n}\n\ntemplate <class ForwardIt, class UnaryPredicate>\nconstexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p) {\n    first = cstd::find_if_not(first, last, p);\n    if(first == last) return first;\n\n    for(ForwardIt i = cstd::next(first); i != last; ++i) {\n        if(p(*i)) {\n            cstd::iter_swap(i, first);\n            ++first;\n        }\n    }\n    return first;\n}\n\n}\n\ntemplate <class RAIt, class Compare = std::less<> >\nconstexpr void quick_sort(RAIt first, RAIt last, Compare cmp = Compare{}) {\n    auto const N = cstd::distance(first, last);\n    if(N <= 1) return;\n    auto const pivot = *cstd::next(first, N / 2);\n    auto const middle1 =\n        cstd::partition(first, last, [=](auto const& elem) { return cmp(elem, pivot); });\n    auto const middle2 =\n        cstd::partition(middle1, last, [=](auto const& elem) { return !cmp(pivot, elem); });\n    quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp));\n    quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp));\n}\n\ntemplate <typename Range>\nconstexpr auto sort(Range&& range) {\n    quick_sort(std::begin(range), std::end(range));\n    return range;\n}\n\ntemplate <typename V, typename... T>\nconstexpr auto array_of(T&&... t) -> std::array<V, sizeof...(T)> {\n    return {{std::forward<T>(t)...}};\n}\n\ntemplate <typename T, typename... N>\nconstexpr auto my_make_array(N&&... args) -> std::array<T, sizeof...(args)> {\n    return {std::forward<N>(args)...};\n}\n\nnamespace traits {\ntemplate <typename T, typename... Ts>\nstruct array_type {\n    using type = T;\n};\n\ntemplate <typename T, typename... Ts>\nstatic constexpr bool are_same_type() {\n    return std::conjunction_v<std::is_same<T, Ts>...>;\n}\n\n}\n\ntemplate <typename... T>\nconstexpr auto create_array(const T&&... values) {\n    using array_type = typename traits::array_type<T...>::type;\n    static_assert(sizeof...(T) > 0, \"an array must have at least one element\");\n    static_assert(traits::are_same_type<T...>(), \"all elements must have same type\");\n    return std::array<array_type, sizeof...(T)>{values...};\n}\n\ntemplate <typename T, typename... Ts>\nconstexpr auto create_array_t(const Ts&&... values) {\n    using array_type = T;\n    static_assert(sizeof...(Ts) > 0, \"an array must have at least one element\");\n    static_assert(traits::are_same_type<Ts...>(), \"all elements must have same type\");\n    return std::array<array_type, sizeof...(Ts)>{static_cast<T>(values)...};\n}\n\n#endif\n"
  },
  {
    "path": "lib/flipper_application/application_assets.c",
    "content": "#include \"application_assets.h\"\n#include <toolbox/path.h>\n#include <storage/storage_i.h>\n\n// #define ELF_ASSETS_DEBUG_LOG 1\n\n#ifndef ELF_ASSETS_DEBUG_LOG\n#undef FURI_LOG_D\n#define FURI_LOG_D(...)\n#undef FURI_LOG_E\n#define FURI_LOG_E(...)\n#endif\n\n#define FLIPPER_APPLICATION_ASSETS_MAGIC 0x4F4C5A44\n#define FLIPPER_APPLICATION_ASSETS_VERSION 1\n#define FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME \".assets.signature\"\n\n#define BUFFER_SIZE 512\n\n#define TAG \"FapAssets\"\n\ntypedef struct FURI_PACKED {\n    uint32_t magic;\n    uint32_t version;\n    uint32_t dirs_count;\n    uint32_t files_count;\n} FlipperApplicationAssetsHeader;\n\ntypedef enum {\n    AssetsSignatureResultEqual,\n    AssetsSignatureResultNotEqual,\n    AssetsSignatureResultError,\n} AssetsSignatureResult;\n\nstatic FuriString* flipper_application_assets_alloc_app_full_path(FuriString* app_name) {\n    furi_assert(app_name);\n    FuriString* full_path = furi_string_alloc_set(APPS_ASSETS_PATH \"/\");\n    furi_string_cat(full_path, app_name);\n    return full_path;\n}\n\nstatic FuriString* flipper_application_assets_alloc_signature_file_path(FuriString* app_name) {\n    furi_assert(app_name);\n    FuriString* signature_file_path = flipper_application_assets_alloc_app_full_path(app_name);\n    furi_string_cat(signature_file_path, \"/\" FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME);\n\n    return signature_file_path;\n}\n\nstatic uint8_t* flipper_application_assets_alloc_and_load_data(File* file, size_t* size) {\n    furi_assert(file);\n\n    uint8_t* data = NULL;\n    uint32_t length = 0;\n\n    // read data length\n    if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {\n        return NULL;\n    }\n\n    data = malloc(length);\n\n    // read data\n    if(storage_file_read(file, (void*)data, length) != length) {\n        free((void*)data);\n        return NULL;\n    }\n\n    if(size != NULL) {\n        *size = length;\n    }\n\n    return data;\n}\n\nstatic bool flipper_application_assets_process_files(\n    Storage* storage,\n    File* file,\n    FuriString* app_name,\n    uint32_t files_count) {\n    furi_assert(storage);\n    furi_assert(file);\n    furi_assert(app_name);\n\n    UNUSED(storage);\n\n    bool success = false;\n    uint32_t length = 0;\n    char* path = NULL;\n    FuriString* file_path = furi_string_alloc();\n    File* destination = storage_file_alloc(storage);\n\n    FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);\n\n    for(uint32_t i = 0; i < files_count; i++) {\n        path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);\n\n        if(path == NULL) {\n            break;\n        }\n\n        // read file size\n        if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {\n            break;\n        }\n\n        furi_string_set(file_path, full_path);\n        furi_string_cat(file_path, \"/\");\n        furi_string_cat(file_path, path);\n\n        if(!storage_file_open(\n               destination, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n            FURI_LOG_E(TAG, \"Can't create file: %s\", furi_string_get_cstr(file_path));\n            break;\n        }\n\n        // copy data to file\n        if(!storage_file_copy_to_file(file, destination, length)) {\n            FURI_LOG_E(TAG, \"Can't copy data to file: %s\", furi_string_get_cstr(file_path));\n            break;\n        }\n\n        storage_file_close(destination);\n\n        free(path);\n        path = NULL;\n\n        if(i == files_count - 1) {\n            success = true;\n        }\n    }\n\n    if(path != NULL) {\n        free(path);\n    }\n\n    storage_file_free(destination);\n    furi_string_free(file_path);\n\n    return success;\n}\n\nstatic bool flipper_application_assets_process_dirs(\n    Storage* storage,\n    File* file,\n    FuriString* app_name,\n    uint32_t dirs_count) {\n    furi_assert(storage);\n    furi_assert(file);\n    furi_assert(app_name);\n\n    bool success = false;\n    FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);\n\n    do {\n        FuriString* dir_path = furi_string_alloc();\n        char* path = NULL;\n\n        for(uint32_t i = 0; i < dirs_count; i++) {\n            path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);\n\n            if(path == NULL) {\n                break;\n            }\n\n            furi_string_set(dir_path, full_path);\n            furi_string_cat(dir_path, \"/\");\n            furi_string_cat(dir_path, path);\n\n            if(!storage_simply_mkdir(storage, furi_string_get_cstr(dir_path))) {\n                FURI_LOG_E(TAG, \"Can't create directory: %s\", furi_string_get_cstr(dir_path));\n                break;\n            }\n\n            free(path);\n            path = NULL;\n\n            if(i == dirs_count - 1) {\n                success = true;\n            }\n        }\n\n        if(path != NULL) {\n            free(path);\n        }\n\n        furi_string_free(dir_path);\n    } while(false);\n\n    furi_string_free(full_path);\n\n    return success;\n}\n\nstatic AssetsSignatureResult flipper_application_assets_process_signature(\n    Storage* storage,\n    File* file,\n    FuriString* app_name,\n    uint8_t** signature_data,\n    size_t* signature_data_size) {\n    furi_assert(storage);\n    furi_assert(file);\n    furi_assert(app_name);\n    furi_assert(signature_data);\n    furi_assert(signature_data_size);\n\n    AssetsSignatureResult result = AssetsSignatureResultError;\n    File* signature_file = storage_file_alloc(storage);\n    FuriString* signature_file_path =\n        flipper_application_assets_alloc_signature_file_path(app_name);\n\n    do {\n        // read signature\n        *signature_data =\n            flipper_application_assets_alloc_and_load_data(file, signature_data_size);\n\n        if(*signature_data == NULL) { //-V547\n            FURI_LOG_E(TAG, \"Can't read signature\");\n            break;\n        }\n\n        result = AssetsSignatureResultNotEqual;\n\n        if(!storage_file_open(\n               signature_file,\n               furi_string_get_cstr(signature_file_path),\n               FSAM_READ_WRITE,\n               FSOM_OPEN_EXISTING)) {\n            FURI_LOG_E(TAG, \"Can't open signature file\");\n            break;\n        }\n\n        size_t signature_size = storage_file_size(signature_file);\n        uint8_t* signature_file_data = malloc(signature_size);\n        if(storage_file_read(signature_file, signature_file_data, signature_size) !=\n           signature_size) {\n            FURI_LOG_E(TAG, \"Can't read signature file\");\n            free(signature_file_data);\n            break;\n        }\n\n        if(memcmp(*signature_data, signature_file_data, signature_size) == 0) {\n            FURI_LOG_D(TAG, \"Assets signature is equal\");\n            result = AssetsSignatureResultEqual;\n        }\n\n        free(signature_file_data);\n    } while(0);\n\n    storage_file_free(signature_file);\n    furi_string_free(signature_file_path);\n\n    return result;\n}\n\nbool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size) {\n    UNUSED(size);\n    furi_assert(file);\n    furi_assert(elf_path);\n    FlipperApplicationAssetsHeader header;\n    bool result = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    uint8_t* signature_data = NULL;\n    size_t signature_data_size = 0;\n    FuriString* app_name = furi_string_alloc();\n    path_extract_filename_no_ext(elf_path, app_name);\n\n    FURI_LOG_D(TAG, \"Loading assets for %s\", furi_string_get_cstr(app_name));\n\n    FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);\n\n    do {\n        if(!storage_file_seek(file, offset, true)) {\n            break;\n        }\n\n        // read header\n        if(storage_file_read(file, &header, sizeof(header)) != sizeof(header)) {\n            break;\n        }\n\n        if(header.magic != FLIPPER_APPLICATION_ASSETS_MAGIC) {\n            break;\n        }\n\n        if(header.version != FLIPPER_APPLICATION_ASSETS_VERSION) {\n            break;\n        }\n\n        // process signature\n        AssetsSignatureResult signature_result = flipper_application_assets_process_signature(\n            storage, file, app_name, &signature_data, &signature_data_size);\n\n        if(signature_result == AssetsSignatureResultError) {\n            FURI_LOG_E(TAG, \"Assets signature error\");\n            break;\n        } else if(signature_result == AssetsSignatureResultEqual) {\n            FURI_LOG_D(TAG, \"Assets signature equal, skip loading\");\n            result = true;\n            break;\n        } else {\n            FURI_LOG_D(TAG, \"Assets signature not equal, loading\");\n\n            // remove old assets\n            FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);\n            storage_simply_remove_recursive(storage, furi_string_get_cstr(full_path));\n            furi_string_free(full_path);\n\n            FURI_LOG_D(TAG, \"Assets removed\");\n        }\n\n        if(!storage_simply_mkdir(storage, APPS_ASSETS_PATH)) {\n            break;\n        }\n\n        if(!storage_simply_mkdir(storage, furi_string_get_cstr(full_path))) {\n            break;\n        }\n\n        // process directories\n        if(header.dirs_count &&\n           !flipper_application_assets_process_dirs(storage, file, app_name, header.dirs_count)) {\n            break;\n        }\n\n        // process files\n        if(header.files_count && !flipper_application_assets_process_files(\n                                     storage, file, app_name, header.files_count)) {\n            break;\n        }\n\n        // write signature\n        FuriString* signature_file_path =\n            flipper_application_assets_alloc_signature_file_path(app_name);\n        File* signature_file = storage_file_alloc(storage);\n\n        if(storage_file_open(\n               signature_file,\n               furi_string_get_cstr(signature_file_path),\n               FSAM_WRITE,\n               FSOM_CREATE_ALWAYS)) {\n            storage_file_write(signature_file, signature_data, signature_data_size);\n        }\n\n        storage_file_free(signature_file);\n        furi_string_free(signature_file_path);\n\n        result = true;\n    } while(false);\n\n    if(signature_data != NULL) {\n        free(signature_data);\n    }\n\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(full_path);\n    furi_string_free(app_name);\n\n    FURI_LOG_D(TAG, \"Assets loading %s\", result ? \"success\" : \"failed\");\n\n    return result;\n}"
  },
  {
    "path": "lib/flipper_application/application_assets.h",
    "content": "/**\n * @file application_assets.h\n * Flipper application assets\n */\n#pragma once\n\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_application/application_manifest.c",
    "content": "#include \"application_manifest.h\"\n\n#include <furi_hal_version.h>\n#include <furi.h>\n\nbool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) {\n    furi_check(manifest);\n\n    if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) ||\n       (manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) {\n        return false;\n    }\n\n    return true;\n}\n\nbool flipper_application_manifest_is_too_old(\n    const FlipperApplicationManifest* manifest,\n    const ElfApiInterface* api_interface) {\n    furi_check(manifest);\n    furi_check(api_interface);\n\n    if(manifest->base.api_version.major < api_interface->api_version_major /* ||\n       manifest->base.api_version.minor > app->api_interface->api_version_minor */) {\n        return false;\n    }\n\n    return true;\n}\n\nbool flipper_application_manifest_is_too_new(\n    const FlipperApplicationManifest* manifest,\n    const ElfApiInterface* api_interface) {\n    furi_check(manifest);\n    furi_check(api_interface);\n\n    if(manifest->base.api_version.major > api_interface->api_version_major /* ||\n       manifest->base.api_version.minor > app->api_interface->api_version_minor */) {\n        return false;\n    }\n\n    return true;\n}\n\nbool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest) {\n    furi_check(manifest);\n\n    const Version* version = furi_hal_version_get_firmware_version();\n    return version_get_target(version) == manifest->base.hardware_target_id;\n}"
  },
  {
    "path": "lib/flipper_application/application_manifest.h",
    "content": "/**\n * @file application_manifest.h\n * Flipper application manifest\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"elf/elf_api_interface.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FAP_MANIFEST_MAGIC 0x52474448\n#define FAP_MANIFEST_SUPPORTED_VERSION 1\n\n#define FAP_MANIFEST_MAX_APP_NAME_LENGTH 32\n#define FAP_MANIFEST_MAX_ICON_SIZE 32 // TODO FL-3524: reduce size?\n\n#pragma pack(push, 1)\n\ntypedef struct {\n    uint32_t manifest_magic;\n    uint32_t manifest_version;\n    union {\n        struct {\n            uint16_t minor;\n            uint16_t major;\n        };\n        uint32_t version;\n    } api_version;\n    uint16_t hardware_target_id;\n} FlipperApplicationManifestBase;\n\ntypedef struct {\n    FlipperApplicationManifestBase base;\n    uint16_t stack_size;\n    uint32_t app_version;\n    char name[FAP_MANIFEST_MAX_APP_NAME_LENGTH];\n    char has_icon;\n    char icon[FAP_MANIFEST_MAX_ICON_SIZE];\n} FlipperApplicationManifestV1;\n\ntypedef FlipperApplicationManifestV1 FlipperApplicationManifest;\n\n#pragma pack(pop)\n\n/**\n * @brief Check if manifest is valid\n * \n * @param manifest \n * @return bool \n */\nbool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);\n\n/** Check if API Version declared in manifest is older than firmware ELF API interface\n *\n * @param      manifest       The manifest\n * @param      api_interface  The api interface\n *\n * @return     bool\n */\nbool flipper_application_manifest_is_too_old(\n    const FlipperApplicationManifest* manifest,\n    const ElfApiInterface* api_interface);\n\n/** Check if API Version declared in manifest is newer than firmware ELF API interface\n *\n * @param      manifest       The manifest\n * @param      api_interface  The api interface\n *\n * @return     bool\n */\nbool flipper_application_manifest_is_too_new(\n    const FlipperApplicationManifest* manifest,\n    const ElfApiInterface* api_interface);\n\n/**\n * @brief Check if application is compatible with current hardware\n * \n * @param manifest\n * @return bool \n */\nbool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_application/elf/elf.h",
    "content": "#ifndef _ELF_H\n#define _ELF_H 1\n\n/* Standard ELF types.  */\n\n#include <stdint.h>\n\n/* Type for a 16-bit quantity.  */\ntypedef uint16_t Elf32_Half;\ntypedef uint16_t Elf64_Half;\n\n/* Types for signed and unsigned 32-bit quantities.  */\ntypedef uint32_t Elf32_Word;\ntypedef int32_t Elf32_Sword;\ntypedef uint32_t Elf64_Word;\ntypedef int32_t Elf64_Sword;\n\n/* Types for signed and unsigned 64-bit quantities.  */\ntypedef uint64_t Elf32_Xword;\ntypedef int64_t Elf32_Sxword;\ntypedef uint64_t Elf64_Xword;\ntypedef int64_t Elf64_Sxword;\n\n/* Type of addresses.  */\ntypedef uint32_t Elf32_Addr;\ntypedef uint64_t Elf64_Addr;\n\n/* Type of file offsets.  */\ntypedef uint32_t Elf32_Off;\ntypedef uint64_t Elf64_Off;\n\n/* Type for section indices, which are 16-bit quantities.  */\ntypedef uint16_t Elf32_Section;\ntypedef uint16_t Elf64_Section;\n\n/* Type for version symbol information.  */\ntypedef Elf32_Half Elf32_Versym;\ntypedef Elf64_Half Elf64_Versym;\n\n/* The ELF file header.  This appears at the start of every ELF file.  */\n\n#define EI_NIDENT (16)\n\ntypedef struct {\n    unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */\n    Elf32_Half e_type; /* Object file type */\n    Elf32_Half e_machine; /* Architecture */\n    Elf32_Word e_version; /* Object file version */\n    Elf32_Addr e_entry; /* Entry point virtual address */\n    Elf32_Off e_phoff; /* Program header table file offset */\n    Elf32_Off e_shoff; /* Section header table file offset */\n    Elf32_Word e_flags; /* Processor-specific flags */\n    Elf32_Half e_ehsize; /* ELF header size in bytes */\n    Elf32_Half e_phentsize; /* Program header table entry size */\n    Elf32_Half e_phnum; /* Program header table entry count */\n    Elf32_Half e_shentsize; /* Section header table entry size */\n    Elf32_Half e_shnum; /* Section header table entry count */\n    Elf32_Half e_shstrndx; /* Section header string table index */\n} Elf32_Ehdr;\n\ntypedef struct {\n    unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */\n    Elf64_Half e_type; /* Object file type */\n    Elf64_Half e_machine; /* Architecture */\n    Elf64_Word e_version; /* Object file version */\n    Elf64_Addr e_entry; /* Entry point virtual address */\n    Elf64_Off e_phoff; /* Program header table file offset */\n    Elf64_Off e_shoff; /* Section header table file offset */\n    Elf64_Word e_flags; /* Processor-specific flags */\n    Elf64_Half e_ehsize; /* ELF header size in bytes */\n    Elf64_Half e_phentsize; /* Program header table entry size */\n    Elf64_Half e_phnum; /* Program header table entry count */\n    Elf64_Half e_shentsize; /* Section header table entry size */\n    Elf64_Half e_shnum; /* Section header table entry count */\n    Elf64_Half e_shstrndx; /* Section header string table index */\n} Elf64_Ehdr;\n\n/* Fields in the e_ident array.  The EI_* macros are indices into the\n   array.  The macros under each EI_* macro are the values the byte\n   may have.  */\n\n#define EI_MAG0 0 /* File identification byte 0 index */\n#define ELFMAG0 0x7f /* Magic number byte 0 */\n\n#define EI_MAG1 1 /* File identification byte 1 index */\n#define ELFMAG1 'E' /* Magic number byte 1 */\n\n#define EI_MAG2 2 /* File identification byte 2 index */\n#define ELFMAG2 'L' /* Magic number byte 2 */\n\n#define EI_MAG3 3 /* File identification byte 3 index */\n#define ELFMAG3 'F' /* Magic number byte 3 */\n\n/* Conglomeration of the identification bytes, for easy testing as a word.  */\n#define ELFMAG \"\\177ELF\"\n#define SELFMAG 4\n\n#define EI_CLASS 4 /* File class byte index */\n#define ELFCLASSNONE 0 /* Invalid class */\n#define ELFCLASS32 1 /* 32-bit objects */\n#define ELFCLASS64 2 /* 64-bit objects */\n#define ELFCLASSNUM 3\n\n#define EI_DATA 5 /* Data encoding byte index */\n#define ELFDATANONE 0 /* Invalid data encoding */\n#define ELFDATA2LSB 1 /* 2's complement, little endian */\n#define ELFDATA2MSB 2 /* 2's complement, big endian */\n#define ELFDATANUM 3\n\n#define EI_VERSION 6 /* File version byte index */\n/* Value must be EV_CURRENT */\n\n#define EI_OSABI 7 /* OS ABI identification */\n#define ELFOSABI_NONE 0 /* UNIX System V ABI */\n#define ELFOSABI_SYSV 0 /* Alias.  */\n#define ELFOSABI_HPUX 1 /* HP-UX */\n#define ELFOSABI_NETBSD 2 /* NetBSD.  */\n#define ELFOSABI_LINUX 3 /* Linux.  */\n#define ELFOSABI_SOLARIS 6 /* Sun Solaris.  */\n#define ELFOSABI_AIX 7 /* IBM AIX.  */\n#define ELFOSABI_IRIX 8 /* SGI Irix.  */\n#define ELFOSABI_FREEBSD 9 /* FreeBSD.  */\n#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX.  */\n#define ELFOSABI_MODESTO 11 /* Novell Modesto.  */\n#define ELFOSABI_OPENBSD 12 /* OpenBSD.  */\n#define ELFOSABI_ARM 97 /* ARM */\n#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */\n\n#define EI_ABIVERSION 8 /* ABI version */\n\n#define EI_PAD 9 /* Byte index of padding bytes */\n\n/* Legal values for e_type (object file type).  */\n\n#define ET_NONE 0 /* No file type */\n#define ET_REL 1 /* Relocatable file */\n#define ET_EXEC 2 /* Executable file */\n#define ET_DYN 3 /* Shared object file */\n#define ET_CORE 4 /* Core file */\n#define ET_NUM 5 /* Number of defined types */\n#define ET_LOOS 0xfe00 /* OS-specific range start */\n#define ET_HIOS 0xfeff /* OS-specific range end */\n#define ET_LOPROC 0xff00 /* Processor-specific range start */\n#define ET_HIPROC 0xffff /* Processor-specific range end */\n\n/* Legal values for e_machine (architecture).  */\n\n#define EM_NONE 0 /* No machine */\n#define EM_M32 1 /* AT&T WE 32100 */\n#define EM_SPARC 2 /* SUN SPARC */\n#define EM_386 3 /* Intel 80386 */\n#define EM_68K 4 /* Motorola m68k family */\n#define EM_88K 5 /* Motorola m88k family */\n#define EM_860 7 /* Intel 80860 */\n#define EM_MIPS 8 /* MIPS R3000 big-endian */\n#define EM_S370 9 /* IBM System/370 */\n#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */\n\n#define EM_PARISC 15 /* HPPA */\n#define EM_VPP500 17 /* Fujitsu VPP500 */\n#define EM_SPARC32PLUS 18 /* Sun's \"v8plus\" */\n#define EM_960 19 /* Intel 80960 */\n#define EM_PPC 20 /* PowerPC */\n#define EM_PPC64 21 /* PowerPC 64-bit */\n#define EM_S390 22 /* IBM S390 */\n\n#define EM_V800 36 /* NEC V800 series */\n#define EM_FR20 37 /* Fujitsu FR20 */\n#define EM_RH32 38 /* TRW RH-32 */\n#define EM_RCE 39 /* Motorola RCE */\n#define EM_ARM 40 /* ARM */\n#define EM_FAKE_ALPHA 41 /* Digital Alpha */\n#define EM_SH 42 /* Hitachi SH */\n#define EM_SPARCV9 43 /* SPARC v9 64-bit */\n#define EM_TRICORE 44 /* Siemens Tricore */\n#define EM_ARC 45 /* Argonaut RISC Core */\n#define EM_H8_300 46 /* Hitachi H8/300 */\n#define EM_H8_300H 47 /* Hitachi H8/300H */\n#define EM_H8S 48 /* Hitachi H8S */\n#define EM_H8_500 49 /* Hitachi H8/500 */\n#define EM_IA_64 50 /* Intel Merced */\n#define EM_MIPS_X 51 /* Stanford MIPS-X */\n#define EM_COLDFIRE 52 /* Motorola Coldfire */\n#define EM_68HC12 53 /* Motorola M68HC12 */\n#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/\n#define EM_PCP 55 /* Siemens PCP */\n#define EM_NCPU 56 /* Sony nCPU embeeded RISC */\n#define EM_NDR1 57 /* Denso NDR1 microprocessor */\n#define EM_STARCORE 58 /* Motorola Start*Core processor */\n#define EM_ME16 59 /* Toyota ME16 processor */\n#define EM_ST100 60 /* STMicroelectronic ST100 processor */\n#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/\n#define EM_X86_64 62 /* AMD x86-64 architecture */\n#define EM_PDSP 63 /* Sony DSP Processor */\n\n#define EM_FX66 66 /* Siemens FX66 microcontroller */\n#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */\n#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */\n#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */\n#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */\n#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */\n#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */\n#define EM_SVX 73 /* Silicon Graphics SVx */\n#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */\n#define EM_VAX 75 /* Digital VAX */\n#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */\n#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */\n#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */\n#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */\n#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */\n#define EM_HUANY 81 /* Harvard University machine-independent object files */\n#define EM_PRISM 82 /* SiTera Prism */\n#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */\n#define EM_FR30 84 /* Fujitsu FR30 */\n#define EM_D10V 85 /* Mitsubishi D10V */\n#define EM_D30V 86 /* Mitsubishi D30V */\n#define EM_V850 87 /* NEC v850 */\n#define EM_M32R 88 /* Mitsubishi M32R */\n#define EM_MN10300 89 /* Matsushita MN10300 */\n#define EM_MN10200 90 /* Matsushita MN10200 */\n#define EM_PJ 91 /* picoJava */\n#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */\n#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */\n#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */\n#define EM_NUM 95\n\n/* Legal values for e_version (version).  */\n\n#define EV_NONE 0 /* Invalid ELF version */\n#define EV_CURRENT 1 /* Current version */\n#define EV_NUM 2\n\n/* Section header.  */\n\ntypedef struct {\n    Elf32_Word sh_name; /* Section name (string tbl index) */\n    Elf32_Word sh_type; /* Section type */\n    Elf32_Word sh_flags; /* Section flags */\n    Elf32_Addr sh_addr; /* Section virtual addr at execution */\n    Elf32_Off sh_offset; /* Section file offset */\n    Elf32_Word sh_size; /* Section size in bytes */\n    Elf32_Word sh_link; /* Link to another section */\n    Elf32_Word sh_info; /* Additional section information */\n    Elf32_Word sh_addralign; /* Section alignment */\n    Elf32_Word sh_entsize; /* Entry size if section holds table */\n} Elf32_Shdr;\n\ntypedef struct {\n    Elf64_Word sh_name; /* Section name (string tbl index) */\n    Elf64_Word sh_type; /* Section type */\n    Elf64_Xword sh_flags; /* Section flags */\n    Elf64_Addr sh_addr; /* Section virtual addr at execution */\n    Elf64_Off sh_offset; /* Section file offset */\n    Elf64_Xword sh_size; /* Section size in bytes */\n    Elf64_Word sh_link; /* Link to another section */\n    Elf64_Word sh_info; /* Additional section information */\n    Elf64_Xword sh_addralign; /* Section alignment */\n    Elf64_Xword sh_entsize; /* Entry size if section holds table */\n} Elf64_Shdr;\n\n/* Special section indices.  */\n\n#define SHN_UNDEF 0 /* Undefined section */\n#define SHN_LORESERVE 0xff00 /* Start of reserved indices */\n#define SHN_LOPROC 0xff00 /* Start of processor-specific */\n#define SHN_BEFORE \\\n    0xff00 /* Order section before all others\n\t\t\t\t\t   (Solaris).  */\n#define SHN_AFTER \\\n    0xff01 /* Order section after all others\n\t\t\t\t\t   (Solaris).  */\n#define SHN_HIPROC 0xff1f /* End of processor-specific */\n#define SHN_LOOS 0xff20 /* Start of OS-specific */\n#define SHN_HIOS 0xff3f /* End of OS-specific */\n#define SHN_ABS 0xfff1 /* Associated symbol is absolute */\n#define SHN_COMMON 0xfff2 /* Associated symbol is common */\n#define SHN_XINDEX 0xffff /* Index is in extra table.  */\n#define SHN_HIRESERVE 0xffff /* End of reserved indices */\n\n/* Legal values for sh_type (section type).  */\n\n#define SHT_NULL 0 /* Section header table entry unused */\n#define SHT_PROGBITS 1 /* Program data */\n#define SHT_SYMTAB 2 /* Symbol table */\n#define SHT_STRTAB 3 /* String table */\n#define SHT_RELA 4 /* Relocation entries with addends */\n#define SHT_HASH 5 /* Symbol hash table */\n#define SHT_DYNAMIC 6 /* Dynamic linking information */\n#define SHT_NOTE 7 /* Notes */\n#define SHT_NOBITS 8 /* Program space with no data (bss) */\n#define SHT_REL 9 /* Relocation entries, no addends */\n#define SHT_SHLIB 10 /* Reserved */\n#define SHT_DYNSYM 11 /* Dynamic linker symbol table */\n#define SHT_INIT_ARRAY 14 /* Array of constructors */\n#define SHT_FINI_ARRAY 15 /* Array of destructors */\n#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */\n#define SHT_GROUP 17 /* Section group */\n#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */\n#define SHT_NUM 19 /* Number of defined types.  */\n#define SHT_LOOS 0x60000000 /* Start OS-specific.  */\n#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes.  */\n#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table.  */\n#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */\n#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content.  */\n#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound.  */\n#define SHT_SUNW_move 0x6ffffffa\n#define SHT_SUNW_COMDAT 0x6ffffffb\n#define SHT_SUNW_syminfo 0x6ffffffc\n#define SHT_GNU_verdef 0x6ffffffd /* Version definition section.  */\n#define SHT_GNU_verneed 0x6ffffffe /* Version needs section.  */\n#define SHT_GNU_versym 0x6fffffff /* Version symbol table.  */\n#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound.  */\n#define SHT_HIOS 0x6fffffff /* End OS-specific type */\n#define SHT_LOPROC 0x70000000 /* Start of processor-specific */\n#define SHT_HIPROC 0x7fffffff /* End of processor-specific */\n#define SHT_LOUSER 0x80000000 /* Start of application-specific */\n#define SHT_HIUSER 0x8fffffff /* End of application-specific */\n\n/* Legal values for sh_flags (section flags).  */\n\n#define SHF_WRITE (1 << 0) /* Writable */\n#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */\n#define SHF_EXECINSTR (1 << 2) /* Executable */\n#define SHF_MERGE (1 << 4) /* Might be merged */\n#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */\n#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */\n#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */\n#define SHF_OS_NONCONFORMING \\\n    (1 << 8) /* Non-standard OS specific handling\n\t\t\t\t\t   required */\n#define SHF_GROUP (1 << 9) /* Section is member of a group.  */\n#define SHF_TLS (1 << 10) /* Section hold thread-local data.  */\n#define SHF_MASKOS 0x0ff00000 /* OS-specific.  */\n#define SHF_MASKPROC 0xf0000000 /* Processor-specific */\n#define SHF_ORDERED \\\n    (1 << 30) /* Special ordering requirement\n\t\t\t\t\t   (Solaris).  */\n#define SHF_EXCLUDE \\\n    (1 << 31) /* Section is excluded unless\n\t\t\t\t\t   referenced or allocated (Solaris).*/\n\n/* Section group handling.  */\n#define GRP_COMDAT 0x1 /* Mark group as COMDAT.  */\n\n/* Symbol table entry.  */\n\ntypedef struct {\n    Elf32_Word st_name; /* Symbol name (string tbl index) */\n    Elf32_Addr st_value; /* Symbol value */\n    Elf32_Word st_size; /* Symbol size */\n    unsigned char st_info; /* Symbol type and binding */\n    unsigned char st_other; /* Symbol visibility */\n    Elf32_Section st_shndx; /* Section index */\n} Elf32_Sym;\n\ntypedef struct {\n    Elf64_Word st_name; /* Symbol name (string tbl index) */\n    unsigned char st_info; /* Symbol type and binding */\n    unsigned char st_other; /* Symbol visibility */\n    Elf64_Section st_shndx; /* Section index */\n    Elf64_Addr st_value; /* Symbol value */\n    Elf64_Xword st_size; /* Symbol size */\n} Elf64_Sym;\n\n/* The syminfo section if available contains additional information about\n   every dynamic symbol.  */\n\ntypedef struct {\n    Elf32_Half si_boundto; /* Direct bindings, symbol bound to */\n    Elf32_Half si_flags; /* Per symbol flags */\n} Elf32_Syminfo;\n\ntypedef struct {\n    Elf64_Half si_boundto; /* Direct bindings, symbol bound to */\n    Elf64_Half si_flags; /* Per symbol flags */\n} Elf64_Syminfo;\n\n/* Possible values for si_boundto.  */\n#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */\n#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */\n#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */\n\n/* Possible bitmasks for si_flags.  */\n#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */\n#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */\n#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */\n#define SYMINFO_FLG_LAZYLOAD \\\n    0x0008 /* Symbol bound to object to be lazy\n\t\t\t\t\t   loaded */\n/* Syminfo version values.  */\n#define SYMINFO_NONE 0\n#define SYMINFO_CURRENT 1\n#define SYMINFO_NUM 2\n\n/* How to extract and insert information held in the st_info field.  */\n\n#define ELF32_ST_BIND(val) (((unsigned char)(val)) >> 4)\n#define ELF32_ST_TYPE(val) ((val)&0xf)\n#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type)&0xf))\n\n/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field.  */\n#define ELF64_ST_BIND(val) ELF32_ST_BIND(val)\n#define ELF64_ST_TYPE(val) ELF32_ST_TYPE(val)\n#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO((bind), (type))\n\n/* Legal values for ST_BIND subfield of st_info (symbol binding).  */\n\n#define STB_LOCAL 0 /* Local symbol */\n#define STB_GLOBAL 1 /* Global symbol */\n#define STB_WEAK 2 /* Weak symbol */\n#define STB_NUM 3 /* Number of defined types.  */\n#define STB_LOOS 10 /* Start of OS-specific */\n#define STB_GNU_UNIQUE 10 /* Unique symbol.  */\n#define STB_HIOS 12 /* End of OS-specific */\n#define STB_LOPROC 13 /* Start of processor-specific */\n#define STB_HIPROC 15 /* End of processor-specific */\n\n/* Legal values for ST_TYPE subfield of st_info (symbol type).  */\n\n#define STT_NOTYPE 0 /* Symbol type is unspecified */\n#define STT_OBJECT 1 /* Symbol is a data object */\n#define STT_FUNC 2 /* Symbol is a code object */\n#define STT_SECTION 3 /* Symbol associated with a section */\n#define STT_FILE 4 /* Symbol's name is file name */\n#define STT_COMMON 5 /* Symbol is a common data object */\n#define STT_TLS 6 /* Symbol is thread-local data object*/\n#define STT_NUM 7 /* Number of defined types.  */\n#define STT_LOOS 10 /* Start of OS-specific */\n#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */\n#define STT_HIOS 12 /* End of OS-specific */\n#define STT_LOPROC 13 /* Start of processor-specific */\n#define STT_HIPROC 15 /* End of processor-specific */\n\n/* Symbol table indices are found in the hash buckets and chain table\n   of a symbol hash table section.  This special index value indicates\n   the end of a chain, meaning no further symbols are found in that bucket.  */\n\n#define STN_UNDEF 0 /* End of a chain.  */\n\n/* How to extract and insert information held in the st_other field.  */\n\n#define ELF32_ST_VISIBILITY(o) ((o)&0x03)\n\n/* For ELF64 the definitions are the same.  */\n#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)\n\n/* Symbol visibility specification encoded in the st_other field.  */\n#define STV_DEFAULT 0 /* Default symbol visibility rules */\n#define STV_INTERNAL 1 /* Processor specific hidden class */\n#define STV_HIDDEN 2 /* Sym unavailable in other modules */\n#define STV_PROTECTED 3 /* Not preemptible, not exported */\n\n/* Relocation table entry without addend (in section of type SHT_REL).  */\n\ntypedef struct {\n    Elf32_Addr r_offset; /* Address */\n    Elf32_Word r_info; /* Relocation type and symbol index */\n} Elf32_Rel;\n\n/* I have seen two different definitions of the Elf64_Rel and\n   Elf64_Rela structures, so we'll leave them out until Novell (or\n   whoever) gets their act together.  */\n/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */\n\ntypedef struct {\n    Elf64_Addr r_offset; /* Address */\n    Elf64_Xword r_info; /* Relocation type and symbol index */\n} Elf64_Rel;\n\n/* Relocation table entry with addend (in section of type SHT_RELA).  */\n\ntypedef struct {\n    Elf32_Addr r_offset; /* Address */\n    Elf32_Word r_info; /* Relocation type and symbol index */\n    Elf32_Sword r_addend; /* Addend */\n} Elf32_Rela;\n\ntypedef struct {\n    Elf64_Addr r_offset; /* Address */\n    Elf64_Xword r_info; /* Relocation type and symbol index */\n    Elf64_Sxword r_addend; /* Addend */\n} Elf64_Rela;\n\n/* How to extract and insert information held in the r_info field.  */\n\n#define ELF32_R_SYM(val) ((val) >> 8)\n#define ELF32_R_TYPE(val) ((val)&0xff)\n#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type)&0xff))\n\n#define ELF64_R_SYM(i) ((i) >> 32)\n#define ELF64_R_TYPE(i) ((i)&0xffffffff)\n#define ELF64_R_INFO(sym, type) ((((Elf64_Xword)(sym)) << 32) + (type))\n\n/* Program segment header.  */\n\ntypedef struct {\n    Elf32_Word p_type; /* Segment type */\n    Elf32_Off p_offset; /* Segment file offset */\n    Elf32_Addr p_vaddr; /* Segment virtual address */\n    Elf32_Addr p_paddr; /* Segment physical address */\n    Elf32_Word p_filesz; /* Segment size in file */\n    Elf32_Word p_memsz; /* Segment size in memory */\n    Elf32_Word p_flags; /* Segment flags */\n    Elf32_Word p_align; /* Segment alignment */\n} Elf32_Phdr;\n\ntypedef struct {\n    Elf64_Word p_type; /* Segment type */\n    Elf64_Word p_flags; /* Segment flags */\n    Elf64_Off p_offset; /* Segment file offset */\n    Elf64_Addr p_vaddr; /* Segment virtual address */\n    Elf64_Addr p_paddr; /* Segment physical address */\n    Elf64_Xword p_filesz; /* Segment size in file */\n    Elf64_Xword p_memsz; /* Segment size in memory */\n    Elf64_Xword p_align; /* Segment alignment */\n} Elf64_Phdr;\n\n/* Legal values for p_type (segment type).  */\n\n#define PT_NULL 0 /* Program header table entry unused */\n#define PT_LOAD 1 /* Loadable program segment */\n#define PT_DYNAMIC 2 /* Dynamic linking information */\n#define PT_INTERP 3 /* Program interpreter */\n#define PT_NOTE 4 /* Auxiliary information */\n#define PT_SHLIB 5 /* Reserved */\n#define PT_PHDR 6 /* Entry for header table itself */\n#define PT_TLS 7 /* Thread-local storage segment */\n#define PT_NUM 8 /* Number of defined types */\n#define PT_LOOS 0x60000000 /* Start of OS-specific */\n#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */\n#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */\n#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */\n#define PT_LOSUNW 0x6ffffffa\n#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */\n#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */\n#define PT_HISUNW 0x6fffffff\n#define PT_HIOS 0x6fffffff /* End of OS-specific */\n#define PT_LOPROC 0x70000000 /* Start of processor-specific */\n#define PT_HIPROC 0x7fffffff /* End of processor-specific */\n\n/* Legal values for p_flags (segment flags).  */\n\n#define PF_X (1 << 0) /* Segment is executable */\n#define PF_W (1 << 1) /* Segment is writable */\n#define PF_R (1 << 2) /* Segment is readable */\n#define PF_MASKOS 0x0ff00000 /* OS-specific */\n#define PF_MASKPROC 0xf0000000 /* Processor-specific */\n\n/* Legal values for note segment descriptor types for core files. */\n\n#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */\n#define NT_FPREGSET 2 /* Contains copy of fpregset struct */\n#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */\n#define NT_PRXREG 4 /* Contains copy of prxregset struct */\n#define NT_TASKSTRUCT 4 /* Contains copy of task structure */\n#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */\n#define NT_AUXV 6 /* Contains copy of auxv array */\n#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */\n#define NT_ASRS 8 /* Contains copy of asrset struct */\n#define NT_PSTATUS 10 /* Contains copy of pstatus struct */\n#define NT_PSINFO 13 /* Contains copy of psinfo struct */\n#define NT_PRCRED 14 /* Contains copy of prcred struct */\n#define NT_UTSNAME 15 /* Contains copy of utsname struct */\n#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */\n#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */\n#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */\n#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */\n#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */\n#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */\n#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */\n#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */\n#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */\n\n/* Legal values for the note segment descriptor types for object files.  */\n\n#define NT_VERSION 1 /* Contains a version string.  */\n\n/* Dynamic section entry.  */\n\ntypedef struct {\n    Elf32_Sword d_tag; /* Dynamic entry type */\n    union {\n        Elf32_Word d_val; /* Integer value */\n        Elf32_Addr d_ptr; /* Address value */\n    } d_un;\n} Elf32_Dyn;\n\ntypedef struct {\n    Elf64_Sxword d_tag; /* Dynamic entry type */\n    union {\n        Elf64_Xword d_val; /* Integer value */\n        Elf64_Addr d_ptr; /* Address value */\n    } d_un;\n} Elf64_Dyn;\n\n/* Legal values for d_tag (dynamic entry type).  */\n\n#define DT_NULL 0 /* Marks end of dynamic section */\n#define DT_NEEDED 1 /* Name of needed library */\n#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */\n#define DT_PLTGOT 3 /* Processor defined value */\n#define DT_HASH 4 /* Address of symbol hash table */\n#define DT_STRTAB 5 /* Address of string table */\n#define DT_SYMTAB 6 /* Address of symbol table */\n#define DT_RELA 7 /* Address of Rela relocs */\n#define DT_RELASZ 8 /* Total size of Rela relocs */\n#define DT_RELAENT 9 /* Size of one Rela reloc */\n#define DT_STRSZ 10 /* Size of string table */\n#define DT_SYMENT 11 /* Size of one symbol table entry */\n#define DT_INIT 12 /* Address of init function */\n#define DT_FINI 13 /* Address of termination function */\n#define DT_SONAME 14 /* Name of shared object */\n#define DT_RPATH 15 /* Library search path (deprecated) */\n#define DT_SYMBOLIC 16 /* Start symbol search here */\n#define DT_REL 17 /* Address of Rel relocs */\n#define DT_RELSZ 18 /* Total size of Rel relocs */\n#define DT_RELENT 19 /* Size of one Rel reloc */\n#define DT_PLTREL 20 /* Type of reloc in PLT */\n#define DT_DEBUG 21 /* For debugging; unspecified */\n#define DT_TEXTREL 22 /* Reloc might modify .text */\n#define DT_JMPREL 23 /* Address of PLT relocs */\n#define DT_BIND_NOW 24 /* Process relocations of object */\n#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */\n#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */\n#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */\n#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */\n#define DT_RUNPATH 29 /* Library search path */\n#define DT_FLAGS 30 /* Flags for the object being loaded */\n#define DT_ENCODING 32 /* Start of encoded range */\n#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/\n#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */\n#define DT_NUM 34 /* Number used */\n#define DT_LOOS 0x6000000d /* Start of OS-specific */\n#define DT_HIOS 0x6ffff000 /* End of OS-specific */\n#define DT_LOPROC 0x70000000 /* Start of processor-specific */\n#define DT_HIPROC 0x7fffffff /* End of processor-specific */\n#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */\n\n/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the\n   Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's\n   approach.  */\n#define DT_VALRNGLO 0x6ffffd00\n#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */\n#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */\n#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */\n#define DT_CHECKSUM 0x6ffffdf8\n#define DT_PLTPADSZ 0x6ffffdf9\n#define DT_MOVEENT 0x6ffffdfa\n#define DT_MOVESZ 0x6ffffdfb\n#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*).  */\n#define DT_POSFLAG_1 \\\n    0x6ffffdfd /* Flags for DT_* entries, effecting\n\t\t\t\t\t   the following DT_* entry.  */\n#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */\n#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */\n#define DT_VALRNGHI 0x6ffffdff\n#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */\n#define DT_VALNUM 12\n\n/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the\n   Dyn.d_un.d_ptr field of the Elf*_Dyn structure.\n\n   If any adjustment is made to the ELF object after it has been\n   built these entries will need to be adjusted.  */\n#define DT_ADDRRNGLO 0x6ffffe00\n#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table.  */\n#define DT_TLSDESC_PLT 0x6ffffef6\n#define DT_TLSDESC_GOT 0x6ffffef7\n#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */\n#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */\n#define DT_CONFIG 0x6ffffefa /* Configuration information.  */\n#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing.  */\n#define DT_AUDIT 0x6ffffefc /* Object auditing.  */\n#define DT_PLTPAD 0x6ffffefd /* PLT padding.  */\n#define DT_MOVETAB 0x6ffffefe /* Move table.  */\n#define DT_SYMINFO 0x6ffffeff /* Syminfo table.  */\n#define DT_ADDRRNGHI 0x6ffffeff\n#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */\n#define DT_ADDRNUM 11\n\n/* The versioning entry types.  The next are defined as part of the\n   GNU extension.  */\n#define DT_VERSYM 0x6ffffff0\n\n#define DT_RELACOUNT 0x6ffffff9\n#define DT_RELCOUNT 0x6ffffffa\n\n/* These were chosen by Sun.  */\n#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below.  */\n#define DT_VERDEF \\\n    0x6ffffffc /* Address of version definition\n\t\t\t\t\t   table */\n#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */\n#define DT_VERNEED \\\n    0x6ffffffe /* Address of table with needed\n\t\t\t\t\t   versions */\n#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */\n#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */\n#define DT_VERSIONTAGNUM 16\n\n/* Sun added these machine-independent extensions in the \"processor-specific\"\n   range.  Be compatible.  */\n#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */\n#define DT_FILTER 0x7fffffff /* Shared object to get values from */\n#define DT_EXTRATAGIDX(tag) ((Elf32_Word) - ((Elf32_Sword)(tag) << 1 >> 1) - 1)\n#define DT_EXTRANUM 3\n\n/* Values of `d_un.d_val' in the DT_FLAGS entry.  */\n#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */\n#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */\n#define DF_TEXTREL 0x00000004 /* Object contains text relocations */\n#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */\n#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */\n\n/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1\n   entry in the dynamic section.  */\n#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object.  */\n#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object.  */\n#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object.  */\n#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/\n#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/\n#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/\n#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object.  */\n#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled.  */\n#define DF_1_DIRECT 0x00000100 /* Direct binding enabled.  */\n#define DF_1_TRANS 0x00000200\n#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose.  */\n#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path.  */\n#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed.  */\n#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/\n#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */\n#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */\n#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time.  */\n\n/* Flags for the feature selection in DT_FEATURE_1.  */\n#define DTF_1_PARINIT 0x00000001\n#define DTF_1_CONFEXP 0x00000002\n\n/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry.  */\n#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object.  */\n#define DF_P1_GROUPPERM \\\n    0x00000002 /* Symbols from next object are not\n\t\t\t\t\t   generally available.  */\n\n/* Version definition sections.  */\n\ntypedef struct {\n    Elf32_Half vd_version; /* Version revision */\n    Elf32_Half vd_flags; /* Version information */\n    Elf32_Half vd_ndx; /* Version Index */\n    Elf32_Half vd_cnt; /* Number of associated aux entries */\n    Elf32_Word vd_hash; /* Version name hash value */\n    Elf32_Word vd_aux; /* Offset in bytes to verdaux array */\n    Elf32_Word vd_next; /* Offset in bytes to next verdef\n\t\t\t\t\t   entry */\n} Elf32_Verdef;\n\ntypedef struct {\n    Elf64_Half vd_version; /* Version revision */\n    Elf64_Half vd_flags; /* Version information */\n    Elf64_Half vd_ndx; /* Version Index */\n    Elf64_Half vd_cnt; /* Number of associated aux entries */\n    Elf64_Word vd_hash; /* Version name hash value */\n    Elf64_Word vd_aux; /* Offset in bytes to verdaux array */\n    Elf64_Word vd_next; /* Offset in bytes to next verdef\n\t\t\t\t\t   entry */\n} Elf64_Verdef;\n\n/* Legal values for vd_version (version revision).  */\n#define VER_DEF_NONE 0 /* No version */\n#define VER_DEF_CURRENT 1 /* Current version */\n#define VER_DEF_NUM 2 /* Given version number */\n\n/* Legal values for vd_flags (version information flags).  */\n#define VER_FLG_BASE 0x1 /* Version definition of file itself */\n#define VER_FLG_WEAK 0x2 /* Weak version identifier */\n\n/* Versym symbol index values.  */\n#define VER_NDX_LOCAL 0 /* Symbol is local.  */\n#define VER_NDX_GLOBAL 1 /* Symbol is global.  */\n#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries.  */\n#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated.  */\n\n/* Auxialiary version information.  */\n\ntypedef struct {\n    Elf32_Word vda_name; /* Version or dependency names */\n    Elf32_Word vda_next; /* Offset in bytes to next verdaux\n\t\t\t\t\t   entry */\n} Elf32_Verdaux;\n\ntypedef struct {\n    Elf64_Word vda_name; /* Version or dependency names */\n    Elf64_Word vda_next; /* Offset in bytes to next verdaux\n\t\t\t\t\t   entry */\n} Elf64_Verdaux;\n\n/* Version dependency section.  */\n\ntypedef struct {\n    Elf32_Half vn_version; /* Version of structure */\n    Elf32_Half vn_cnt; /* Number of associated aux entries */\n    Elf32_Word vn_file; /* Offset of filename for this\n\t\t\t\t\t   dependency */\n    Elf32_Word vn_aux; /* Offset in bytes to vernaux array */\n    Elf32_Word vn_next; /* Offset in bytes to next verneed\n\t\t\t\t\t   entry */\n} Elf32_Verneed;\n\ntypedef struct {\n    Elf64_Half vn_version; /* Version of structure */\n    Elf64_Half vn_cnt; /* Number of associated aux entries */\n    Elf64_Word vn_file; /* Offset of filename for this\n\t\t\t\t\t   dependency */\n    Elf64_Word vn_aux; /* Offset in bytes to vernaux array */\n    Elf64_Word vn_next; /* Offset in bytes to next verneed\n\t\t\t\t\t   entry */\n} Elf64_Verneed;\n\n/* Legal values for vn_version (version revision).  */\n#define VER_NEED_NONE 0 /* No version */\n#define VER_NEED_CURRENT 1 /* Current version */\n#define VER_NEED_NUM 2 /* Given version number */\n\n/* Auxiliary needed version information.  */\n\ntypedef struct {\n    Elf32_Word vna_hash; /* Hash value of dependency name */\n    Elf32_Half vna_flags; /* Dependency specific information */\n    Elf32_Half vna_other; /* Unused */\n    Elf32_Word vna_name; /* Dependency name string offset */\n    Elf32_Word vna_next; /* Offset in bytes to next vernaux\n\t\t\t\t\t   entry */\n} Elf32_Vernaux;\n\ntypedef struct {\n    Elf64_Word vna_hash; /* Hash value of dependency name */\n    Elf64_Half vna_flags; /* Dependency specific information */\n    Elf64_Half vna_other; /* Unused */\n    Elf64_Word vna_name; /* Dependency name string offset */\n    Elf64_Word vna_next; /* Offset in bytes to next vernaux\n\t\t\t\t\t   entry */\n} Elf64_Vernaux;\n\n/* Legal values for vna_flags.  */\n#define VER_FLG_WEAK 0x2 /* Weak version identifier */\n\n/* Auxiliary vector.  */\n\n/* This vector is normally only used by the program interpreter.  The\n   usual definition in an ABI supplement uses the name auxv_t.  The\n   vector is not usually defined in a standard <elf.h> file, but it\n   can't hurt.  We rename it to avoid conflicts.  The sizes of these\n   types are an arrangement between the exec server and the program\n   interpreter, so we don't fully specify them here.  */\n\ntypedef struct {\n    uint32_t a_type; /* Entry type */\n    union {\n        uint32_t a_val; /* Integer value */\n        /* We use to have pointer elements added here.  We cannot do that,\n\t\tthough, since it does not work when using 32-bit definitions\n\t\t on 64-bit platforms and vice versa.  */\n    } a_un;\n} Elf32_auxv_t;\n\ntypedef struct {\n    uint64_t a_type; /* Entry type */\n    union {\n        uint64_t a_val; /* Integer value */\n        /* We use to have pointer elements added here.  We cannot do that,\n\t\tthough, since it does not work when using 32-bit definitions\n\t\t on 64-bit platforms and vice versa.  */\n    } a_un;\n} Elf64_auxv_t;\n\n/* Legal values for a_type (entry type).  */\n\n#define AT_NULL 0 /* End of vector */\n#define AT_IGNORE 1 /* Entry should be ignored */\n#define AT_EXECFD 2 /* File descriptor of program */\n#define AT_PHDR 3 /* Program headers for program */\n#define AT_PHENT 4 /* Size of program header entry */\n#define AT_PHNUM 5 /* Number of program headers */\n#define AT_PAGESZ 6 /* System page size */\n#define AT_BASE 7 /* Base address of interpreter */\n#define AT_FLAGS 8 /* Flags */\n#define AT_ENTRY 9 /* Entry point of program */\n#define AT_NOTELF 10 /* Program is not ELF */\n#define AT_UID 11 /* Real uid */\n#define AT_EUID 12 /* Effective uid */\n#define AT_GID 13 /* Real gid */\n#define AT_EGID 14 /* Effective gid */\n#define AT_CLKTCK 17 /* Frequency of times() */\n\n/* Some more special a_type values describing the hardware.  */\n#define AT_PLATFORM 15 /* String identifying platform.  */\n#define AT_HWCAP \\\n    16 /* Machine dependent hints about\n\t\t\t\t\t   processor capabilities.  */\n\n/* This entry gives some information about the FPU initialization\n   performed by the kernel.  */\n#define AT_FPUCW 18 /* Used FPU control word.  */\n\n/* Cache block sizes.  */\n#define AT_DCACHEBSIZE 19 /* Data cache block size.  */\n#define AT_ICACHEBSIZE 20 /* Instruction cache block size.  */\n#define AT_UCACHEBSIZE 21 /* Unified cache block size.  */\n\n/* A special ignored value for PPC, used by the kernel to control the\n   interpretation of the AUXV. Must be > 16.  */\n#define AT_IGNOREPPC 22 /* Entry should be ignored.  */\n\n#define AT_SECURE 23 /* Boolean, was exec setuid-like?  */\n\n#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/\n\n#define AT_RANDOM 25 /* Address of 16 random bytes.  */\n\n#define AT_EXECFN 31 /* Filename of executable.  */\n\n/* Pointer to the global system page used for system calls and other\n   nice things.  */\n#define AT_SYSINFO 32\n#define AT_SYSINFO_EHDR 33\n\n/* Shapes of the caches.  Bits 0-3 contains associativity; bits 4-7 contains\n   log2 of line size; mask those to get cache size.  */\n#define AT_L1I_CACHESHAPE 34\n#define AT_L1D_CACHESHAPE 35\n#define AT_L2_CACHESHAPE 36\n#define AT_L3_CACHESHAPE 37\n\n/* Note section contents.  Each entry in the note section begins with\n   a header of a fixed form.  */\n\ntypedef struct {\n    Elf32_Word n_namesz; /* Length of the note's name.  */\n    Elf32_Word n_descsz; /* Length of the note's descriptor.  */\n    Elf32_Word n_type; /* Type of the note.  */\n} Elf32_Nhdr;\n\ntypedef struct {\n    Elf64_Word n_namesz; /* Length of the note's name.  */\n    Elf64_Word n_descsz; /* Length of the note's descriptor.  */\n    Elf64_Word n_type; /* Type of the note.  */\n} Elf64_Nhdr;\n\n/* Known names of notes.  */\n\n/* Solaris entries in the note section have this name.  */\n#define ELF_NOTE_SOLARIS \"SUNW Solaris\"\n\n/* Note entries for GNU systems have this name.  */\n#define ELF_NOTE_GNU \"GNU\"\n\n/* Defined types of notes for Solaris.  */\n\n/* Value of descriptor (one word) is desired pagesize for the binary.  */\n#define ELF_NOTE_PAGESIZE_HINT 1\n\n/* Defined note types for GNU systems.  */\n\n/* ABI information.  The descriptor consists of words:\n   word 0: OS descriptor\n   word 1: major version of the ABI\n   word 2: minor version of the ABI\n   word 3: subminor version of the ABI\n*/\n#define NT_GNU_ABI_TAG 1\n#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name.  */\n\n/* Known OSes.  These values can appear in word 0 of an\n   NT_GNU_ABI_TAG note section entry.  */\n#define ELF_NOTE_OS_LINUX 0\n#define ELF_NOTE_OS_GNU 1\n#define ELF_NOTE_OS_SOLARIS2 2\n#define ELF_NOTE_OS_FREEBSD 3\n\n/* Synthetic hwcap information.  The descriptor begins with two words:\n   word 0: number of entries\n   word 1: bitmask of enabled entries\n   Then follow variable-length entries, one byte followed by a\n   '\\0'-terminated hwcap name string.  The byte gives the bit\n   number to test if enabled, (1U << bit) & bitmask.  */\n#define NT_GNU_HWCAP 2\n\n/* Build ID bits as generated by ld --build-id.\n   The descriptor consists of any nonzero number of bytes.  */\n#define NT_GNU_BUILD_ID 3\n\n/* Version note generated by GNU gold containing a version string.  */\n#define NT_GNU_GOLD_VERSION 4\n\n/* Move records.  */\ntypedef struct {\n    Elf32_Xword m_value; /* Symbol value.  */\n    Elf32_Word m_info; /* Size and index.  */\n    Elf32_Word m_poffset; /* Symbol offset.  */\n    Elf32_Half m_repeat; /* Repeat count.  */\n    Elf32_Half m_stride; /* Stride info.  */\n} Elf32_Move;\n\ntypedef struct {\n    Elf64_Xword m_value; /* Symbol value.  */\n    Elf64_Xword m_info; /* Size and index.  */\n    Elf64_Xword m_poffset; /* Symbol offset.  */\n    Elf64_Half m_repeat; /* Repeat count.  */\n    Elf64_Half m_stride; /* Stride info.  */\n} Elf64_Move;\n\n/* Macro to construct move records.  */\n#define ELF32_M_SYM(info) ((info) >> 8)\n#define ELF32_M_SIZE(info) ((unsigned char)(info))\n#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char)(size))\n\n#define ELF64_M_SYM(info) ELF32_M_SYM(info)\n#define ELF64_M_SIZE(info) ELF32_M_SIZE(info)\n#define ELF64_M_INFO(sym, size) ELF32_M_INFO(sym, size)\n\n/* ARM specific declarations */\n\n/* Processor specific flags for the ELF header e_flags field.  */\n#define EF_ARM_RELEXEC 0x01\n#define EF_ARM_HASENTRY 0x02\n#define EF_ARM_INTERWORK 0x04\n#define EF_ARM_APCS_26 0x08\n#define EF_ARM_APCS_FLOAT 0x10\n#define EF_ARM_PIC 0x20\n#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */\n#define EF_ARM_NEW_ABI 0x80\n#define EF_ARM_OLD_ABI 0x100\n#define EF_ARM_SOFT_FLOAT 0x200\n#define EF_ARM_VFP_FLOAT 0x400\n#define EF_ARM_MAVERICK_FLOAT 0x800\n\n/* Other constants defined in the ARM ELF spec. version B-01.  */\n/* NB. These conflict with values defined above.  */\n#define EF_ARM_SYMSARESORTED 0x04\n#define EF_ARM_DYNSYMSUSESEGIDX 0x08\n#define EF_ARM_MAPSYMSFIRST 0x10\n#define EF_ARM_EABIMASK 0XFF000000\n\n/* Constants defined in AAELF.  */\n#define EF_ARM_BE8 0x00800000\n#define EF_ARM_LE8 0x00400000\n\n#define EF_ARM_EABI_VERSION(flags) ((flags)&EF_ARM_EABIMASK)\n#define EF_ARM_EABI_UNKNOWN 0x00000000\n#define EF_ARM_EABI_VER1 0x01000000\n#define EF_ARM_EABI_VER2 0x02000000\n#define EF_ARM_EABI_VER3 0x03000000\n#define EF_ARM_EABI_VER4 0x04000000\n#define EF_ARM_EABI_VER5 0x05000000\n\n/* Additional symbol types for Thumb.  */\n#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function.  */\n#define STT_ARM_16BIT STT_HIPROC /* A Thumb label.  */\n\n/* ARM-specific values for sh_flags */\n#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */\n#define SHF_ARM_COMDEF \\\n    0x80000000 /* Section may be multiply defined\n\t\t\t\t\t      in the input to a link step.  */\n\n/* ARM-specific program header flags */\n#define PF_ARM_SB \\\n    0x10000000 /* Segment contains the location\n\t\t\t\t\t      addressed by the static base. */\n#define PF_ARM_PI 0x20000000 /* Position-independent segment.  */\n#define PF_ARM_ABS 0x40000000 /* Absolute segment.  */\n\n/* Processor specific values for the Phdr p_type field.  */\n#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment.  */\n\n/* Processor specific values for the Shdr sh_type field.  */\n#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section.  */\n#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details.  */\n#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section.  */\n\n/* ARM relocs.  */\n\n#define R_ARM_NONE 0 /* No reloc */\n#define R_ARM_PC24 1 /* PC relative 26 bit branch */\n#define R_ARM_ABS32 2 /* Direct 32 bit  */\n#define R_ARM_REL32 3 /* PC relative 32 bit */\n#define R_ARM_PC13 4\n#define R_ARM_ABS16 5 /* Direct 16 bit */\n#define R_ARM_ABS12 6 /* Direct 12 bit */\n#define R_ARM_THM_ABS5 7\n#define R_ARM_ABS8 8 /* Direct 8 bit */\n#define R_ARM_SBREL32 9\n#define R_ARM_THM_PC22 10\n#define R_ARM_THM_PC8 11\n#define R_ARM_AMP_VCALL9 12\n#define R_ARM_SWI24 13\n#define R_ARM_THM_SWI8 14\n#define R_ARM_XPC25 15\n#define R_ARM_THM_XPC22 16\n#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */\n#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */\n#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */\n#define R_ARM_COPY 20 /* Copy symbol at runtime */\n#define R_ARM_GLOB_DAT 21 /* Create GOT entry */\n#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */\n#define R_ARM_RELATIVE 23 /* Adjust by program base */\n#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */\n#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */\n#define R_ARM_GOT32 26 /* 32 bit GOT entry */\n#define R_ARM_PLT32 27 /* 32 bit PLT address */\n#define R_ARM_THM_JUMP24 30 /* Thumb32   ((S + A) | T) - P */\n#define R_ARM_ALU_PCREL_7_0 32\n#define R_ARM_ALU_PCREL_15_8 33\n#define R_ARM_ALU_PCREL_23_15 34\n#define R_ARM_LDR_SBREL_11_0 35\n#define R_ARM_ALU_SBREL_19_12 36\n#define R_ARM_ALU_SBREL_27_20 37\n#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW) */\n#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit */\n#define R_ARM_GNU_VTENTRY 100\n#define R_ARM_GNU_VTINHERIT 101\n#define R_ARM_THM_PC11 102 /* thumb unconditional branch */\n#define R_ARM_THM_PC9 103 /* thumb conditional branch */\n#define R_ARM_TLS_GD32 \\\n    104 /* PC-rel 32 bit for global dynamic\n\t\t\t\t\t   thread local data */\n#define R_ARM_TLS_LDM32 \\\n    105 /* PC-rel 32 bit for local dynamic\n\t\t\t\t\t   thread local data */\n#define R_ARM_TLS_LDO32 \\\n    106 /* 32 bit offset relative to TLS\n\t\t\t\t\t   block */\n#define R_ARM_TLS_IE32 \\\n    107 /* PC-rel 32 bit for GOT entry of\n\t\t\t\t\t   static TLS block offset */\n#define R_ARM_TLS_LE32 \\\n    108 /* 32 bit offset relative to static\n\t\t\t\t\t   TLS block */\n#define R_ARM_RXPC25 249\n#define R_ARM_RSBREL32 250\n#define R_ARM_THM_RPC22 251\n#define R_ARM_RREL32 252\n#define R_ARM_RABS22 253\n#define R_ARM_RPC24 254\n#define R_ARM_RBASE 255\n/* Keep this the last entry.  */\n#define R_ARM_NUM 256\n\n#endif /* elf.h */\n"
  },
  {
    "path": "lib/flipper_application/elf/elf_api_interface.h",
    "content": "#pragma once\n\n#include <elf.h>\n#include <stdbool.h>\n\n/**\n * @brief Interface for ELF loader to resolve symbols\n */\ntypedef struct ElfApiInterface {\n    uint16_t api_version_major;\n    uint16_t api_version_minor;\n    bool (*resolver_callback)(\n        const struct ElfApiInterface* interface,\n        uint32_t hash,\n        Elf32_Addr* address);\n} ElfApiInterface;\n"
  },
  {
    "path": "lib/flipper_application/elf/elf_file.c",
    "content": "#include \"elf_file.h\"\n#include \"elf_file_i.h\"\n\n#include <storage/storage.h>\n#include <elf.h>\n#include \"elf_api_interface.h\"\n#include \"../api_hashtable/api_hashtable.h\"\n\n#define TAG \"Elf\"\n\n#define ELF_NAME_BUFFER_LEN        32\n#define SECTION_OFFSET(e, n)       ((e)->section_table + (n) * sizeof(Elf32_Shdr))\n#define IS_FLAGS_SET(v, m)         (((v) & (m)) == (m))\n#define RESOLVER_THREAD_YIELD_STEP 30\n#define FAST_RELOCATION_VERSION    1\n\n// #define ELF_DEBUG_LOG 1\n\n#ifndef ELF_DEBUG_LOG\n#undef FURI_LOG_D\n#define FURI_LOG_D(...)\n#endif\n\n#define ELF_INVALID_ADDRESS 0xFFFFFFFF\n\n#define TRAMPOLINE_CODE_SIZE 6\n\n/**\nldr r12, [pc, #2]\nbx r12\n*/\nconst uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] =\n    {0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47};\n\ntypedef struct {\n    uint8_t code[TRAMPOLINE_CODE_SIZE];\n    uint32_t addr;\n} FURI_PACKED JMPTrampoline;\n\n/**************************************************************************************************/\n/********************************************* Caches *********************************************/\n/**************************************************************************************************/\n\nstatic bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {\n    Elf32_Addr* addr = AddressCache_get(cache, symEntry);\n    if(addr) {\n        *symAddr = *addr;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstatic void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) {\n    AddressCache_set_at(cache, symEntry, symAddr);\n}\n\n/**************************************************************************************************/\n/********************************************** ELF ***********************************************/\n/**************************************************************************************************/\n\nstatic void elf_file_maybe_release_fd(ELFFile* elf) {\n    if(elf->fd) {\n        storage_file_free(elf->fd);\n        elf->fd = NULL;\n    }\n}\n\nstatic ELFSection* elf_file_get_section(ELFFile* elf, const char* name) {\n    return ELFSectionDict_get(elf->sections, name);\n}\n\nstatic ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) {\n    ELFSection* section_p = elf_file_get_section(elf, name);\n    if(!section_p) {\n        ELFSectionDict_set_at(\n            elf->sections,\n            strdup(name),\n            (ELFSection){\n                .data = NULL,\n                .sec_idx = 0,\n                .size = 0,\n                .rel_count = 0,\n                .rel_offset = 0,\n                .fast_rel = NULL,\n            });\n        section_p = elf_file_get_section(elf, name);\n    }\n\n    return section_p;\n}\n\nstatic bool elf_read_string_from_offset(ELFFile* elf, off_t offset, FuriString* name) {\n    bool result = false;\n\n    off_t old = storage_file_tell(elf->fd);\n\n    do {\n        if(!storage_file_seek(elf->fd, offset, true)) break;\n\n        char buffer[ELF_NAME_BUFFER_LEN + 1];\n        buffer[ELF_NAME_BUFFER_LEN] = 0;\n\n        while(true) {\n            size_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN);\n            furi_string_cat(name, buffer);\n            if(strlen(buffer) < ELF_NAME_BUFFER_LEN) {\n                result = true;\n                break;\n            }\n\n            if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break;\n        }\n\n    } while(false);\n    storage_file_seek(elf->fd, old, true);\n\n    return result;\n}\n\nstatic bool elf_read_section_name(ELFFile* elf, off_t offset, FuriString* name) {\n    return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name);\n}\n\nstatic bool elf_read_symbol_name(ELFFile* elf, off_t offset, FuriString* name) {\n    return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name);\n}\n\nstatic bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) {\n    off_t offset = SECTION_OFFSET(elf, section_idx);\n    return storage_file_seek(elf->fd, offset, true) &&\n           storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);\n}\n\nstatic bool elf_read_section(\n    ELFFile* elf,\n    size_t section_idx,\n    Elf32_Shdr* section_header,\n    FuriString* name) {\n    if(!elf_read_section_header(elf, section_idx, section_header)) {\n        return false;\n    }\n\n    if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, FuriString* name) {\n    bool success = false;\n    off_t old = storage_file_tell(elf->fd);\n    off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym);\n    if(storage_file_seek(elf->fd, pos, true) &&\n       storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {\n        if(sym->st_name)\n            success = elf_read_symbol_name(elf, sym->st_name, name);\n        else {\n            Elf32_Shdr shdr;\n            success = elf_read_section(elf, sym->st_shndx, &shdr, name);\n        }\n    }\n    storage_file_seek(elf->fd, old, true);\n    return success;\n}\n\nstatic ELFSection* elf_section_of(ELFFile* elf, int index) {\n    ELFSectionDict_it_t it;\n    for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {\n        ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);\n        if(itref->value.sec_idx == index) {\n            return &itref->value;\n        }\n    }\n\n    return NULL;\n}\n\nstatic Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {\n    if(sym->st_shndx == SHN_UNDEF) {\n        Elf32_Addr addr = 0;\n        uint32_t hash = elf_symbolname_hash(sName);\n        if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {\n            return addr;\n        }\n    } else {\n        ELFSection* symSec = elf_section_of(elf, sym->st_shndx);\n        if(symSec) {\n            return ((Elf32_Addr)symSec->data) + sym->st_value;\n        }\n    }\n    FURI_LOG_D(TAG, \"  Can not find address for symbol %s\", sName);\n    return ELF_INVALID_ADDRESS;\n}\n\n__attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) {\n#define STRCASE(name) \\\n    case name:        \\\n        return #name;\n    switch(symt) {\n        STRCASE(R_ARM_NONE)\n        STRCASE(R_ARM_TARGET1)\n        STRCASE(R_ARM_ABS32)\n        STRCASE(R_ARM_REL32)\n        STRCASE(R_ARM_THM_PC22)\n        STRCASE(R_ARM_THM_JUMP24)\n    default:\n        return \"R_<unknow>\";\n    }\n#undef STRCASE\n}\n\nstatic JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) {\n    JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline));\n    memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE);\n    trampoline->addr = addr;\n    return trampoline;\n}\n\nstatic void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {\n    int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11;\n    int to_thumb, is_call, blx_bit = 1 << 12;\n\n    /* Get initial offset */\n    hi = ((uint16_t*)relAddr)[0];\n    lo = ((uint16_t*)relAddr)[1];\n    s = (hi >> 10) & 1;\n    j1 = (lo >> 13) & 1;\n    j2 = (lo >> 11) & 1;\n    i1 = (j1 ^ s) ^ 1;\n    i2 = (j2 ^ s) ^ 1;\n    imm10 = hi & 0x3ff;\n    imm11 = lo & 0x7ff;\n    offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);\n    if(offset & 0x01000000) offset -= 0x02000000;\n\n    to_thumb = symAddr & 1;\n    is_call = (type == R_ARM_THM_PC22);\n\n    /* Store offset */\n    int offset_copy = offset;\n\n    /* Compute final offset */\n    offset += symAddr - relAddr;\n    if(!to_thumb && is_call) {\n        blx_bit = 0; /* bl -> blx */\n        offset = (offset + 3) & -4; /* Compute offset from aligned PC */\n    }\n\n    /* Check that relocation is possible\n    * offset must not be out of range\n    * if target is to be entered in arm mode:\n        - bit 1 must not set\n        - instruction must be a call (bl) or a jump to PLT */\n    if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) {\n        if(to_thumb || (symAddr & 2) || (!is_call)) {\n            FURI_LOG_D(\n                TAG,\n                \"can't relocate value at %lx, %s, doing trampoline\",\n                relAddr,\n                elf_reloc_type_to_str(type));\n\n            Elf32_Addr addr;\n            if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) {\n                addr = (Elf32_Addr)elf_create_trampoline(symAddr);\n                address_cache_put(elf->trampoline_cache, symAddr, addr);\n            }\n\n            offset = offset_copy;\n            offset += (int)addr - relAddr;\n            if(!to_thumb && is_call) {\n                blx_bit = 0; /* bl -> blx */\n                offset = (offset + 3) & -4; /* Compute offset from aligned PC */\n            }\n        }\n    }\n\n    /* Compute and store final offset */\n    s = (offset >> 24) & 1;\n    i1 = (offset >> 23) & 1;\n    i2 = (offset >> 22) & 1;\n    j1 = s ^ (i1 ^ 1);\n    j2 = s ^ (i2 ^ 1);\n    imm10 = (offset >> 12) & 0x3ff;\n    imm11 = (offset >> 1) & 0x7ff;\n    (*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10);\n    (*(uint16_t*)(relAddr + 2)) =\n        (uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11);\n}\n\nstatic void elf_relocate_mov(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {\n    uint16_t upper_insn = ((uint16_t*)relAddr)[0];\n    uint16_t lower_insn = ((uint16_t*)relAddr)[1];\n\n    /* MOV*<C> <Rd>,#<imm16>\n     *\n     * i = upper[10]\n     * imm4 = upper[3:0]\n     * imm3 = lower[14:12]\n     * imm8 = lower[7:0]\n     *\n     * imm16 = imm4:i:imm3:imm8\n     */\n    uint32_t i = (upper_insn >> 10) & 1; /* upper[10] */\n    uint32_t imm4 = upper_insn & 0x000F; /* upper[3:0] */\n    uint32_t imm3 = (lower_insn >> 12) & 0x7; /* lower[14:12] */\n    uint32_t imm8 = lower_insn & 0x00FF; /* lower[7:0] */\n\n    int32_t addend = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; /* imm16 */\n\n    uint32_t addr = (symAddr + addend);\n    if(type == R_ARM_THM_MOVT_ABS) {\n        addr >>= 16; /* upper 16 bits */\n    } else {\n        addr &= 0x0000FFFF; /* lower 16 bits */\n    }\n\n    /* Re-encode */\n    ((uint16_t*)relAddr)[0] = (upper_insn & 0xFBF0) | (((addr >> 11) & 1) << 10) /* i */\n                              | ((addr >> 12) & 0x000F); /* imm4 */\n    ((uint16_t*)relAddr)[1] = (lower_insn & 0x8F00) | (((addr >> 8) & 0x7) << 12) /* imm3 */\n                              | (addr & 0x00FF); /* imm8 */\n}\n\nstatic bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {\n    switch(type) {\n    case R_ARM_TARGET1:\n    case R_ARM_ABS32:\n        *((uint32_t*)relAddr) += symAddr;\n        FURI_LOG_D(TAG, \"  R_ARM_ABS32 relocated is 0x%08X\", (unsigned int)*((uint32_t*)relAddr));\n        break;\n    case R_ARM_REL32:\n        *((uint32_t*)relAddr) += symAddr - relAddr;\n        FURI_LOG_D(TAG, \"  R_ARM_REL32 relocated is 0x%08X\", (unsigned int)*((uint32_t*)relAddr));\n        break;\n    case R_ARM_THM_PC22:\n    case R_ARM_CALL:\n    case R_ARM_THM_JUMP24:\n        elf_relocate_jmp_call(elf, relAddr, type, symAddr);\n        FURI_LOG_D(\n            TAG, \"  R_ARM_THM_CALL/JMP relocated is 0x%08X\", (unsigned int)*((uint32_t*)relAddr));\n        break;\n    case R_ARM_THM_MOVW_ABS_NC:\n    case R_ARM_THM_MOVT_ABS:\n        elf_relocate_mov(relAddr, type, symAddr);\n        FURI_LOG_D(\n            TAG,\n            \"  R_ARM_THM_MOVW_ABS_NC/MOVT_ABS relocated is 0x%08X\",\n            (unsigned int)*((uint32_t*)relAddr));\n        break;\n    default:\n        FURI_LOG_E(TAG, \"  Undefined relocation %d\", type);\n        return false;\n    }\n    return true;\n}\n\nstatic bool elf_relocate(ELFFile* elf, ELFSection* s) {\n    if(s->data) {\n        Elf32_Rel rel;\n        size_t relEntries = s->rel_count;\n        size_t relCount;\n        (void)storage_file_seek(elf->fd, s->rel_offset, true);\n        FURI_LOG_D(TAG, \" Offset   Info     Type             Name\");\n\n        int relocate_result = true;\n        FuriString* symbol_name;\n        symbol_name = furi_string_alloc();\n\n        for(relCount = 0; relCount < relEntries; relCount++) {\n            if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {\n                FURI_LOG_D(TAG, \"  reloc YIELD\");\n                furi_delay_tick(1);\n            }\n\n            if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {\n                FURI_LOG_E(TAG, \"  reloc read fail\");\n                furi_string_free(symbol_name);\n                return false;\n            }\n\n            Elf32_Addr symAddr;\n\n            int symEntry = ELF32_R_SYM(rel.r_info);\n            int relType = ELF32_R_TYPE(rel.r_info);\n            Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;\n\n            if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) {\n                Elf32_Sym sym;\n                furi_string_reset(symbol_name);\n                if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) {\n                    FURI_LOG_E(TAG, \"  symbol read fail\");\n                    furi_string_free(symbol_name);\n                    return false;\n                }\n\n                FURI_LOG_D(\n                    TAG,\n                    \" %08X %08X %-16s %s\",\n                    (unsigned int)rel.r_offset,\n                    (unsigned int)rel.r_info,\n                    elf_reloc_type_to_str(relType),\n                    furi_string_get_cstr(symbol_name));\n\n                symAddr = elf_address_of(elf, &sym, furi_string_get_cstr(symbol_name));\n                address_cache_put(elf->relocation_cache, symEntry, symAddr);\n            }\n\n            if(symAddr != ELF_INVALID_ADDRESS) {\n                FURI_LOG_D(\n                    TAG,\n                    \"  symAddr=%08X relAddr=%08X\",\n                    (unsigned int)symAddr,\n                    (unsigned int)relAddr);\n                if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) {\n                    relocate_result = false;\n                }\n            } else {\n                FURI_LOG_E(TAG, \"  No symbol address of %s\", furi_string_get_cstr(symbol_name));\n                relocate_result = false;\n            }\n        }\n        furi_string_free(symbol_name);\n\n        return relocate_result;\n    } else {\n        FURI_LOG_D(TAG, \"Section not loaded\");\n    }\n\n    return false;\n}\n\n/**************************************************************************************************/\n/************************************ Internal FAP interfaces *************************************/\n/**************************************************************************************************/\ntypedef enum {\n    SectionTypeUnused = 1 << 0,\n    SectionTypeData = 1 << 1,\n    SectionTypeRelData = 1 << 2,\n    SectionTypeSymTab = 1 << 3,\n    SectionTypeStrTab = 1 << 4,\n    SectionTypeDebugLink = 1 << 5,\n    SectionTypeFastRelData = 1 << 6,\n} SectionType;\n\nstatic bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {\n    elf->debug_link_info.debug_link_size = section_header->sh_size;\n    elf->debug_link_info.debug_link = malloc(section_header->sh_size);\n\n    return storage_file_seek(elf->fd, section_header->sh_offset, true) &&\n           storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) ==\n               section_header->sh_size;\n}\n\nstatic bool str_prefix(const char* str, const char* prefix) {\n    return strncmp(prefix, str, strlen(prefix)) == 0;\n}\n\ntypedef enum {\n    ELFLoadSectionResultSuccess,\n    ELFLoadSectionResultNoMemory,\n    ELFLoadSectionResultError,\n} ELFLoadSectionResult;\n\ntypedef struct {\n    SectionType type;\n    ELFLoadSectionResult result;\n} SectionTypeInfo;\n\nstatic ELFLoadSectionResult\n    elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) {\n    if(section_header->sh_size == 0) {\n        FURI_LOG_D(TAG, \"No data for section\");\n        return ELFLoadSectionResultSuccess;\n    }\n\n    size_t safe_size = section_header->sh_size + 1024;\n\n    furi_kernel_lock();\n\n    if(memmgr_heap_get_max_free_block() < safe_size) {\n        furi_kernel_unlock();\n        FURI_LOG_E(TAG, \"Not enough memory to load section data\");\n        return ELFLoadSectionResultNoMemory;\n    }\n\n    section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign);\n    section->size = section_header->sh_size;\n\n    furi_kernel_unlock();\n\n    if(section_header->sh_type == SHT_NOBITS) {\n        // BSS section, no data to load\n        return ELFLoadSectionResultSuccess;\n    }\n\n    if((!storage_file_seek(elf->fd, section_header->sh_offset, true)) ||\n       (storage_file_read(elf->fd, section->data, section_header->sh_size) !=\n        section_header->sh_size)) {\n        FURI_LOG_E(TAG, \"    seek/read fail\");\n        return ELFLoadSectionResultError;\n    }\n\n    FURI_LOG_D(TAG, \"0x%p\", section->data);\n    return ELFLoadSectionResultSuccess;\n}\n\nstatic SectionTypeInfo elf_preload_section(\n    ELFFile* elf,\n    size_t section_idx,\n    Elf32_Shdr* section_header,\n    FuriString* name_string) {\n    const char* name = furi_string_get_cstr(name_string);\n    SectionTypeInfo info;\n\n#ifdef ELF_DEBUG_LOG\n    // log section name, type and flags\n    FuriString* flags_string = furi_string_alloc();\n    if(section_header->sh_flags & SHF_WRITE) furi_string_cat(flags_string, \"W\");\n    if(section_header->sh_flags & SHF_ALLOC) furi_string_cat(flags_string, \"A\");\n    if(section_header->sh_flags & SHF_EXECINSTR) furi_string_cat(flags_string, \"X\");\n    if(section_header->sh_flags & SHF_MERGE) furi_string_cat(flags_string, \"M\");\n    if(section_header->sh_flags & SHF_STRINGS) furi_string_cat(flags_string, \"S\");\n    if(section_header->sh_flags & SHF_INFO_LINK) furi_string_cat(flags_string, \"I\");\n    if(section_header->sh_flags & SHF_LINK_ORDER) furi_string_cat(flags_string, \"L\");\n    if(section_header->sh_flags & SHF_OS_NONCONFORMING) furi_string_cat(flags_string, \"O\");\n    if(section_header->sh_flags & SHF_GROUP) furi_string_cat(flags_string, \"G\");\n    if(section_header->sh_flags & SHF_TLS) furi_string_cat(flags_string, \"T\");\n    if(section_header->sh_flags & SHF_COMPRESSED) furi_string_cat(flags_string, \"T\");\n    if(section_header->sh_flags & SHF_MASKOS) furi_string_cat(flags_string, \"o\");\n    if(section_header->sh_flags & SHF_MASKPROC) furi_string_cat(flags_string, \"p\");\n    if(section_header->sh_flags & SHF_ORDERED) furi_string_cat(flags_string, \"R\");\n    if(section_header->sh_flags & SHF_EXCLUDE) furi_string_cat(flags_string, \"E\");\n\n    FURI_LOG_I(\n        TAG,\n        \"Section %s: type: %ld, flags: %s\",\n        name,\n        section_header->sh_type,\n        furi_string_get_cstr(flags_string));\n    furi_string_free(flags_string);\n#endif\n\n    // ignore .ARM and .rel.ARM sections\n    // TODO FL-3525: how to do it not by name?\n    // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER\n    // .rel.ARM: type 0x9, flags SHT_REL\n    if(str_prefix(name, \".ARM.\") || str_prefix(name, \".rel.ARM.\") ||\n       str_prefix(name, \".fast.rel.ARM.\")) {\n        FURI_LOG_D(TAG, \"Ignoring ARM section\");\n\n        info.type = SectionTypeUnused;\n        info.result = ELFLoadSectionResultSuccess;\n        return info;\n    }\n\n    // Load allocable section\n    if(section_header->sh_flags & SHF_ALLOC) {\n        ELFSection* section_p = elf_file_get_or_put_section(elf, name);\n        section_p->sec_idx = section_idx;\n\n        if(section_header->sh_type == SHT_PREINIT_ARRAY) {\n            furi_assert(elf->preinit_array == NULL);\n            elf->preinit_array = section_p;\n        } else if(section_header->sh_type == SHT_INIT_ARRAY) {\n            furi_assert(elf->init_array == NULL);\n            elf->init_array = section_p;\n        } else if(section_header->sh_type == SHT_FINI_ARRAY) {\n            furi_assert(elf->fini_array == NULL);\n            elf->fini_array = section_p;\n        }\n\n        info.type = SectionTypeData;\n        info.result = elf_load_section_data(elf, section_p, section_header);\n\n        if(info.result != ELFLoadSectionResultSuccess) {\n            FURI_LOG_E(TAG, \"Error loading section '%s'\", name);\n        }\n\n        return info;\n    }\n\n    // Load link info section\n    if(section_header->sh_flags & SHF_INFO_LINK) {\n        info.type = SectionTypeRelData;\n\n        if(str_prefix(name, \".rel\")) {\n            name = name + strlen(\".rel\");\n            ELFSection* section_p = elf_file_get_or_put_section(elf, name);\n            section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);\n            section_p->rel_offset = section_header->sh_offset;\n            info.result = ELFLoadSectionResultSuccess;\n        } else {\n            FURI_LOG_E(TAG, \"Unknown link info section '%s'\", name);\n            info.result = ELFLoadSectionResultError;\n        }\n\n        return info;\n    }\n\n    // Load fast rel section\n    if(str_prefix(name, \".fast.rel\")) {\n        name = name + strlen(\".fast.rel\");\n        ELFSection* section_p = elf_file_get_or_put_section(elf, name);\n        section_p->fast_rel = malloc(sizeof(ELFSection));\n\n        info.type = SectionTypeFastRelData;\n        info.result = elf_load_section_data(elf, section_p->fast_rel, section_header);\n\n        if(info.result != ELFLoadSectionResultSuccess) {\n            FURI_LOG_E(TAG, \"Error loading section '%s'\", name);\n        } else {\n            FURI_LOG_D(TAG, \"Loaded fast rel section for '%s'\", name);\n        }\n\n        return info;\n    }\n\n    // Load symbol table\n    if(strcmp(name, \".symtab\") == 0) {\n        FURI_LOG_D(TAG, \"Found .symtab section\");\n        elf->symbol_table = section_header->sh_offset;\n        elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);\n\n        info.type = SectionTypeSymTab;\n        info.result = ELFLoadSectionResultSuccess;\n        return info;\n    }\n\n    // Load string table\n    if(strcmp(name, \".strtab\") == 0) {\n        FURI_LOG_D(TAG, \"Found .strtab section\");\n        elf->symbol_table_strings = section_header->sh_offset;\n\n        info.type = SectionTypeStrTab;\n        info.result = ELFLoadSectionResultSuccess;\n        return info;\n    }\n\n    // Load debug link section\n    if(strcmp(name, \".gnu_debuglink\") == 0) {\n        FURI_LOG_D(TAG, \"Found .gnu_debuglink section\");\n        info.type = SectionTypeDebugLink;\n\n        if(elf_load_debug_link(elf, section_header)) {\n            info.result = ELFLoadSectionResultSuccess;\n            return info;\n        } else {\n            info.result = ELFLoadSectionResultError;\n            return info;\n        }\n    }\n\n    info.type = SectionTypeUnused;\n    info.result = ELFLoadSectionResultSuccess;\n    return info;\n}\n\nstatic Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {\n    Elf32_Addr addr = 0;\n    if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {\n        return addr;\n    }\n    return ELF_INVALID_ADDRESS;\n}\n\nstatic bool elf_file_find_string_by_hash(ELFFile* elf, uint32_t hash, FuriString* out) {\n    bool result = false;\n\n    FuriString* symbol_name = furi_string_alloc();\n    Elf32_Sym sym;\n    for(size_t i = 0; i < elf->symbol_count; i++) {\n        furi_string_reset(symbol_name);\n        if(elf_read_symbol(elf, i, &sym, symbol_name)) {\n            if(elf_symbolname_hash(furi_string_get_cstr(symbol_name)) == hash) {\n                furi_string_set(out, symbol_name);\n                result = true;\n                break;\n            }\n        }\n    }\n    furi_string_free(symbol_name);\n\n    return result;\n}\n\nstatic bool elf_relocate_fast(ELFFile* elf, ELFSection* s) {\n    UNUSED(elf);\n    const uint8_t* start = s->fast_rel->data;\n    const uint8_t version = *start;\n    bool no_errors = true;\n\n    if(version != FAST_RELOCATION_VERSION) {\n        FURI_LOG_E(TAG, \"Unsupported fast relocation version %d\", version);\n        return false;\n    }\n    start += 1;\n\n    const uint32_t records_count = *((uint32_t*)start);\n    start += 4;\n    FURI_LOG_D(TAG, \"Fast relocation records count: %ld\", records_count);\n\n    for(uint32_t i = 0; i < records_count; i++) {\n        bool is_section = (*start & (0x1 << 7)) ? true : false;\n        uint8_t type = *start & 0x7F;\n        start += 1;\n        uint32_t hash_or_section_index = *((uint32_t*)start);\n        start += 4;\n\n        uint32_t section_value = ELF_INVALID_ADDRESS;\n        if(is_section) {\n            section_value = *((uint32_t*)start);\n            start += 4;\n        }\n\n        const uint32_t offsets_count = *((uint32_t*)start);\n        start += 4;\n\n        FURI_LOG_D(\n            TAG,\n            \"Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld\",\n            i,\n            is_section,\n            type,\n            hash_or_section_index,\n            offsets_count);\n\n        Elf32_Addr address = 0;\n        if(is_section) {\n            ELFSection* symSec = elf_section_of(elf, hash_or_section_index);\n            if(symSec) {\n                address = ((Elf32_Addr)symSec->data) + section_value;\n            }\n        } else {\n            address = elf_address_of_by_hash(elf, hash_or_section_index);\n        }\n\n        if(address == ELF_INVALID_ADDRESS) {\n            FuriString* symbol_name = furi_string_alloc();\n            if(elf_file_find_string_by_hash(elf, hash_or_section_index, symbol_name)) {\n                FURI_LOG_E(\n                    TAG,\n                    \"Failed to resolve address for symbol %s (hash %lX)\",\n                    furi_string_get_cstr(symbol_name),\n                    hash_or_section_index);\n            } else {\n                FURI_LOG_E(\n                    TAG,\n                    \"Failed to resolve address for hash %lX (string not found)\",\n                    hash_or_section_index);\n            }\n            furi_string_free(symbol_name);\n\n            no_errors = false;\n            start += 3 * offsets_count;\n        } else {\n            for(uint32_t j = 0; j < offsets_count; j++) {\n                uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF;\n                start += 3;\n                Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset;\n                elf_relocate_symbol(elf, relAddr, type, address);\n            }\n        }\n    }\n\n    aligned_free(s->fast_rel->data);\n    free(s->fast_rel);\n    s->fast_rel = NULL;\n\n    return no_errors;\n}\n\nstatic bool elf_relocate_section(ELFFile* elf, ELFSection* section) {\n    if(section->fast_rel) {\n        FURI_LOG_D(TAG, \"Fast relocating section\");\n        return elf_relocate_fast(elf, section);\n    } else if(section->rel_count) {\n        FURI_LOG_D(TAG, \"Relocating section\");\n        return elf_relocate(elf, section);\n    } else {\n        FURI_LOG_D(TAG, \"No relocation index\"); /* Not an error */\n    }\n    return true;\n}\n\nstatic void elf_file_call_section_list(ELFSection* section, bool reverse_order) {\n    if(section && section->size) {\n        const uint32_t* start = section->data;\n        const uint32_t* end = section->data + section->size;\n\n        if(reverse_order) {\n            while(end > start) {\n                end--;\n                ((void (*)(void))(*end))();\n            }\n        } else {\n            while(start < end) {\n                ((void (*)(void))(*start))();\n                start++;\n            }\n        }\n    }\n}\n\n/**************************************************************************************************/\n/********************************************* Public *********************************************/\n/**************************************************************************************************/\n\nELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) {\n    ELFFile* elf = malloc(sizeof(ELFFile));\n    elf->fd = storage_file_alloc(storage);\n    elf->api_interface = api_interface;\n    ELFSectionDict_init(elf->sections);\n    AddressCache_init(elf->trampoline_cache);\n    elf->init_array_called = false;\n    return elf;\n}\n\nvoid elf_file_free(ELFFile* elf) {\n    // furi_check(!elf->init_array_called);\n    if(elf->init_array_called) {\n        FURI_LOG_W(TAG, \"Init array was called, but fini array wasn't\");\n        elf_file_call_section_list(elf->fini_array, true);\n    }\n\n    // free sections data\n    {\n        ELFSectionDict_it_t it;\n        for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);\n            ELFSectionDict_next(it)) {\n            const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);\n            aligned_free(itref->value.data);\n            if(itref->value.fast_rel) {\n                aligned_free(itref->value.fast_rel->data);\n                free(itref->value.fast_rel);\n            }\n            free((void*)itref->key);\n        }\n\n        ELFSectionDict_clear(elf->sections);\n    }\n\n    // free trampoline data\n    {\n        AddressCache_it_t it;\n        for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it);\n            AddressCache_next(it)) {\n            const AddressCache_itref_t* itref = AddressCache_cref(it);\n            free((void*)itref->value);\n        }\n\n        AddressCache_clear(elf->trampoline_cache);\n    }\n\n    if(elf->debug_link_info.debug_link) {\n        free(elf->debug_link_info.debug_link);\n    }\n\n    elf_file_maybe_release_fd(elf);\n    free(elf);\n}\n\nbool elf_file_open(ELFFile* elf, const char* path) {\n    Elf32_Ehdr h;\n    Elf32_Shdr sH;\n\n    if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||\n       !storage_file_seek(elf->fd, 0, true) ||\n       storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) ||\n       !storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||\n       storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {\n        return false;\n    }\n\n    elf->entry = h.e_entry;\n    elf->sections_count = h.e_shnum;\n    elf->section_table = h.e_shoff;\n    elf->section_table_strings = sH.sh_offset;\n    return true;\n}\n\nElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf) {\n    SectionType loaded_sections = 0;\n    FuriString* name = furi_string_alloc();\n    ElfLoadSectionTableResult result = ElfLoadSectionTableResultSuccess;\n\n    FURI_LOG_D(TAG, \"Scan ELF indexs...\");\n\n    for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {\n        Elf32_Shdr section_header;\n\n        furi_string_reset(name);\n        if(!elf_read_section(elf, section_idx, &section_header, name)) {\n            loaded_sections = 0;\n            break;\n        }\n\n        FURI_LOG_D(\n            TAG, \"Preloading data for section #%d %s\", section_idx, furi_string_get_cstr(name));\n        SectionTypeInfo section_type_info =\n            elf_preload_section(elf, section_idx, &section_header, name);\n        loaded_sections |= section_type_info.type;\n\n        if(section_type_info.result != ELFLoadSectionResultSuccess) {\n            if(section_type_info.result == ELFLoadSectionResultNoMemory) {\n                FURI_LOG_E(TAG, \"Not enough memory\");\n                result = ElfLoadSectionTableResultNoMemory;\n            } else if(section_type_info.result == ELFLoadSectionResultError) {\n                FURI_LOG_E(TAG, \"Error loading section\");\n                result = ElfLoadSectionTableResultError;\n            }\n\n            loaded_sections = 0;\n            break;\n        }\n    }\n\n    furi_string_free(name);\n\n    if(result != ElfLoadSectionTableResultSuccess) {\n        return result;\n    } else {\n        bool sections_valid =\n            IS_FLAGS_SET(loaded_sections, SectionTypeSymTab | SectionTypeStrTab) |\n            IS_FLAGS_SET(loaded_sections, SectionTypeFastRelData);\n        if(sections_valid) {\n            return ElfLoadSectionTableResultSuccess;\n        } else {\n            FURI_LOG_E(TAG, \"No valid sections found\");\n            return ElfLoadSectionTableResultError;\n        }\n    }\n}\n\nElfProcessSectionResult elf_process_section(\n    ELFFile* elf,\n    const char* name,\n    ElfProcessSection* process_section,\n    void* context) {\n    ElfProcessSectionResult result = ElfProcessSectionResultNotFound;\n    FuriString* section_name = furi_string_alloc();\n    Elf32_Shdr section_header;\n\n    // find section\n    for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {\n        furi_string_reset(section_name);\n        if(!elf_read_section(elf, section_idx, &section_header, section_name)) {\n            break;\n        }\n\n        if(furi_string_cmp(section_name, name) == 0) {\n            result = ElfProcessSectionResultCannotProcess;\n            break;\n        }\n    }\n\n    if(result != ElfProcessSectionResultNotFound) { //-V547\n        if(process_section(elf->fd, section_header.sh_offset, section_header.sh_size, context)) {\n            result = ElfProcessSectionResultSuccess;\n        } else {\n            result = ElfProcessSectionResultCannotProcess; //-V1048\n        }\n    }\n\n    furi_string_free(section_name);\n\n    return result;\n}\n\nELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {\n    furi_check(elf->fd != NULL);\n    ELFFileLoadStatus status = ELFFileLoadStatusSuccess;\n    ELFSectionDict_it_t it;\n\n    AddressCache_init(elf->relocation_cache);\n\n    for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {\n        ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);\n        FURI_LOG_D(TAG, \"Relocating section '%s'\", itref->key);\n        if(!elf_relocate_section(elf, &itref->value)) {\n            FURI_LOG_E(TAG, \"Error relocating section '%s'\", itref->key);\n            status = ELFFileLoadStatusMissingImports;\n        }\n    }\n\n    /* Fixing up entry point */\n    if(status == ELFFileLoadStatusSuccess) {\n        ELFSection* text_section = elf_file_get_section(elf, \".text\");\n\n        if(text_section == NULL) {\n            FURI_LOG_E(TAG, \"No .text section found\");\n            status = ELFFileLoadStatusUnspecifiedError;\n        } else {\n            elf->entry += (uint32_t)text_section->data;\n        }\n    }\n\n    FURI_LOG_D(TAG, \"Relocation cache size: %u\", AddressCache_size(elf->relocation_cache));\n    FURI_LOG_D(TAG, \"Trampoline cache size: %u\", AddressCache_size(elf->trampoline_cache));\n    AddressCache_clear(elf->relocation_cache);\n\n    {\n        size_t total_size = 0;\n        for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);\n            ELFSectionDict_next(it)) {\n            ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);\n            total_size += itref->value.size;\n        }\n        FURI_LOG_I(TAG, \"Total size of loaded sections: %zu\", total_size);\n    }\n\n    elf_file_maybe_release_fd(elf);\n    return status;\n}\n\nvoid elf_file_call_init(ELFFile* elf) {\n    furi_check(!elf->init_array_called);\n    elf_file_call_section_list(elf->preinit_array, false);\n    elf_file_call_section_list(elf->init_array, false);\n    elf->init_array_called = true;\n}\n\nbool elf_file_is_init_complete(ELFFile* elf) {\n    return elf->init_array_called;\n}\n\nvoid* elf_file_get_entry_point(ELFFile* elf) {\n    furi_check(elf->init_array_called);\n    return (void*)elf->entry;\n}\n\nvoid elf_file_call_fini(ELFFile* elf) {\n    furi_check(elf->init_array_called);\n    elf_file_call_section_list(elf->fini_array, true);\n    elf->init_array_called = false;\n}\n\nconst ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {\n    return elf_file->api_interface;\n}\n\nvoid elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {\n    // set entry\n    debug_info->entry = elf->entry;\n\n    // copy debug info\n    memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo));\n\n    // init mmap\n    debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections);\n    debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count);\n    uint32_t mmap_entry_idx = 0;\n\n    ELFSectionDict_it_t it;\n    for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {\n        const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);\n\n        const void* data_ptr = itref->value.data;\n        if(data_ptr) {\n            ELFMemoryMapEntry* entry = &debug_info->mmap_entries[mmap_entry_idx];\n            entry->address = (uint32_t)data_ptr;\n            entry->name = itref->key;\n            mmap_entry_idx++;\n        }\n    }\n}\n\nvoid elf_file_clear_debug_info(ELFDebugInfo* debug_info) {\n    // clear debug info\n    memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo));\n\n    // clear mmap\n    if(debug_info->mmap_entries) {\n        free(debug_info->mmap_entries);\n        debug_info->mmap_entries = NULL;\n    }\n\n    debug_info->mmap_entry_count = 0;\n}\n"
  },
  {
    "path": "lib/flipper_application/elf/elf_file.h",
    "content": "/**\n * @file elf_file.h\n * ELF file loader\n */\n#pragma once\n#include <storage/storage.h>\n#include \"../application_manifest.h\"\n#include \"elf_api_interface.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct ELFFile ELFFile;\n\ntypedef struct {\n    const char* name;\n    uint32_t address;\n} ELFMemoryMapEntry;\n\ntypedef struct {\n    uint32_t debug_link_size;\n    uint8_t* debug_link;\n} ELFDebugLinkInfo;\n\ntypedef struct {\n    uint32_t mmap_entry_count;\n    ELFMemoryMapEntry* mmap_entries;\n    ELFDebugLinkInfo debug_link_info;\n    off_t entry;\n} ELFDebugInfo;\n\ntypedef enum {\n    ELFFileLoadStatusSuccess = 0,\n    ELFFileLoadStatusUnspecifiedError,\n    ELFFileLoadStatusMissingImports,\n} ELFFileLoadStatus;\n\ntypedef enum {\n    ElfProcessSectionResultNotFound,\n    ElfProcessSectionResultCannotProcess,\n    ElfProcessSectionResultSuccess,\n} ElfProcessSectionResult;\n\ntypedef enum {\n    ElfLoadSectionTableResultError,\n    ElfLoadSectionTableResultNoMemory,\n    ElfLoadSectionTableResultSuccess,\n} ElfLoadSectionTableResult;\n\ntypedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context);\n\n/**\n * @brief Allocate ELFFile instance\n * @param storage \n * @param api_interface \n * @return ELFFile* \n */\nELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface);\n\n/**\n * @brief Free ELFFile instance\n * @param elf_file \n */\nvoid elf_file_free(ELFFile* elf_file);\n\n/**\n * @brief Open ELF file\n * @param elf_file \n * @param path \n * @return bool \n */\nbool elf_file_open(ELFFile* elf_file, const char* path);\n\n/**\n * @brief Load ELF file section table (load stage #1)\n * @param elf_file \n * @return ElfLoadSectionTableResult \n */\nElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf_file);\n\n/**\n * @brief Load and relocate ELF file sections (load stage #2)\n * @param elf_file \n * @return ELFFileLoadStatus \n */\nELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file);\n\n/**\n * @brief Execute ELF file pre-run stage, \n * call static constructors for example (load stage #3)\n * Must be done before invoking any code from the ELF file\n * @param elf \n */\nvoid elf_file_call_init(ELFFile* elf);\n\n/**\n * @brief Check if ELF file pre-run stage was executed and its code is runnable\n * @param elf \n */\nbool elf_file_is_init_complete(ELFFile* elf);\n\n/**\n * @brief Get actual entry point for ELF file\n * @param elf_file \n * @return void*\n */\nvoid* elf_file_get_entry_point(ELFFile* elf_file);\n\n/**\n * @brief Execute ELF file post-run stage, \n * call static destructors for example (load stage #5)\n * Must be done if any code from the ELF file was executed\n * @param elf \n */\nvoid elf_file_call_fini(ELFFile* elf);\n\n/**\n * @brief Get ELF file API interface\n * @param elf_file \n * @return const ElfApiInterface* \n */\nconst ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file);\n\n/**\n * @brief Get ELF file debug info\n * @param elf_file \n * @param debug_info \n */\nvoid elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);\n\n/**\n * @brief Clear ELF file debug info generated by elf_file_init_debug_info\n * @param debug_info \n */\nvoid elf_file_clear_debug_info(ELFDebugInfo* debug_info);\n\n/**\n * @brief Process ELF file section\n * \n * @param elf_file \n * @param name \n * @param process_section \n * @param context \n * @return ElfProcessSectionResult \n */\nElfProcessSectionResult elf_process_section(\n    ELFFile* elf_file,\n    const char* name,\n    ElfProcessSection* process_section,\n    void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_application/elf/elf_file_i.h",
    "content": "#pragma once\n#include \"elf_file.h\"\n#include <m-dict.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nDICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) //-V1048\n\n/**\n * Callable elf entry type\n */\ntypedef int32_t(entry_t)(void*);\n\ntypedef struct ELFSection ELFSection;\n\nstruct ELFSection {\n    void* data;\n    Elf32_Word size;\n\n    size_t rel_count;\n    Elf32_Off rel_offset;\n    ELFSection* fast_rel;\n\n    uint16_t sec_idx;\n};\n\nDICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)\n\nstruct ELFFile {\n    size_t sections_count;\n    off_t section_table;\n    off_t section_table_strings;\n\n    size_t symbol_count;\n    off_t symbol_table;\n    off_t symbol_table_strings;\n    off_t entry;\n    ELFSectionDict_t sections;\n\n    AddressCache_t relocation_cache;\n    AddressCache_t trampoline_cache;\n\n    File* fd;\n    const ElfApiInterface* api_interface;\n    ELFDebugLinkInfo debug_link_info;\n\n    ELFSection* preinit_array;\n    ELFSection* init_array;\n    ELFSection* fini_array;\n\n    bool init_array_called;\n};\n\n#ifdef __cplusplus\n}\n#endif"
  },
  {
    "path": "lib/flipper_application/flipper_application.c",
    "content": "#include \"flipper_application.h\"\n#include \"elf/elf_file.h\"\n#include <notification/notification_messages.h>\n#include \"application_assets.h\"\n#include <loader/firmware_api/firmware_api.h>\n\n#include <m-list.h>\n\n#define TAG \"Fap\"\n\nstruct FlipperApplication {\n    ELFDebugInfo state;\n    FlipperApplicationManifest manifest;\n    ELFFile* elf;\n    FuriThread* thread;\n    void* ep_thread_args;\n};\n\n/********************** Debugger access to loader state **********************/\n\nLIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST); // NOLINT\n\nFlipperApplicationList_t flipper_application_loaded_app_list = {0};\nstatic bool flipper_application_loaded_app_list_initialized = false;\n\nstatic void flipper_application_list_add_app(const FlipperApplication* app) {\n    furi_check(app);\n\n    if(!flipper_application_loaded_app_list_initialized) {\n        FlipperApplicationList_init(flipper_application_loaded_app_list);\n        flipper_application_loaded_app_list_initialized = true;\n    }\n    FlipperApplicationList_push_back(flipper_application_loaded_app_list, app);\n}\n\nstatic void flipper_application_list_remove_app(const FlipperApplication* app) {\n    furi_check(flipper_application_loaded_app_list_initialized);\n    furi_check(app);\n\n    FlipperApplicationList_it_t it;\n    for(FlipperApplicationList_it(it, flipper_application_loaded_app_list);\n        !FlipperApplicationList_end_p(it);\n        FlipperApplicationList_next(it)) {\n        if(*FlipperApplicationList_ref(it) == app) {\n            FlipperApplicationList_remove(flipper_application_loaded_app_list, it);\n            break;\n        }\n    }\n}\n\n/*****************************************************************************/\n\nFlipperApplication*\n    flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {\n    furi_check(storage);\n    furi_check(api_interface);\n\n    FlipperApplication* app = malloc(sizeof(FlipperApplication));\n    app->elf = elf_file_alloc(storage, api_interface);\n    app->thread = NULL;\n    app->ep_thread_args = NULL;\n\n    return app;\n}\n\nbool flipper_application_is_plugin(FlipperApplication* app) {\n    furi_check(app);\n    return app->manifest.stack_size == 0;\n}\n\nvoid flipper_application_free(FlipperApplication* app) {\n    furi_check(app);\n\n    if(app->thread) {\n        furi_thread_join(app->thread);\n        furi_thread_free(app->thread);\n    }\n\n    if(app->state.entry) {\n        flipper_application_list_remove_app(app);\n    }\n\n    elf_file_clear_debug_info(&app->state);\n\n    if(elf_file_is_init_complete(app->elf)) {\n        elf_file_call_fini(app->elf);\n    }\n\n    elf_file_free(app->elf);\n\n    if(app->ep_thread_args) {\n        free(app->ep_thread_args);\n        app->ep_thread_args = NULL;\n    }\n\n    free(app);\n}\n\nstatic FlipperApplicationPreloadStatus\n    flipper_application_validate_manifest(FlipperApplication* app) {\n    if(!flipper_application_manifest_is_valid(&app->manifest)) {\n        return FlipperApplicationPreloadStatusInvalidManifest;\n    }\n\n    if(!flipper_application_manifest_is_target_compatible(&app->manifest)) {\n        return FlipperApplicationPreloadStatusTargetMismatch;\n    }\n\n    if(!flipper_application_manifest_is_too_old(\n           &app->manifest, elf_file_get_api_interface(app->elf))) {\n        return FlipperApplicationPreloadStatusApiTooOld;\n    }\n\n    if(!flipper_application_manifest_is_too_new(\n           &app->manifest, elf_file_get_api_interface(app->elf))) {\n        return FlipperApplicationPreloadStatusApiTooNew;\n    }\n\n    return FlipperApplicationPreloadStatusSuccess;\n}\n\nstatic bool flipper_application_process_manifest_section(\n    File* file,\n    size_t offset,\n    size_t size,\n    void* context) {\n    FlipperApplicationManifest* manifest = context;\n\n    if(size < sizeof(FlipperApplicationManifest)) {\n        return false;\n    }\n\n    if(manifest == NULL) {\n        return true;\n    }\n\n    return storage_file_seek(file, offset, true) &&\n           storage_file_read(file, manifest, size) == size;\n}\n\n// we can't use const char* as context because we will lose the const qualifier\ntypedef struct {\n    const char* path;\n} FlipperApplicationPreloadAssetsContext;\n\nstatic bool flipper_application_process_assets_section(\n    File* file,\n    size_t offset,\n    size_t size,\n    void* context) {\n    FlipperApplicationPreloadAssetsContext* preload_context = context;\n    return flipper_application_assets_load(file, preload_context->path, offset, size);\n}\n\nstatic FlipperApplicationPreloadStatus\n    flipper_application_load(FlipperApplication* app, const char* path, bool load_full) {\n    if(!elf_file_open(app->elf, path)) {\n        return FlipperApplicationPreloadStatusInvalidFile;\n    }\n\n    // if we are loading full file\n    if(load_full) {\n        // load section table\n        ElfLoadSectionTableResult load_result = elf_file_load_section_table(app->elf);\n        if(load_result == ElfLoadSectionTableResultError) {\n            return FlipperApplicationPreloadStatusInvalidFile;\n        } else if(load_result == ElfLoadSectionTableResultNoMemory) {\n            return FlipperApplicationPreloadStatusNotEnoughMemory;\n        }\n\n        // load assets section\n        FlipperApplicationPreloadAssetsContext preload_context = {.path = path};\n        if(elf_process_section(\n               app->elf,\n               \".fapassets\",\n               flipper_application_process_assets_section,\n               &preload_context) == ElfProcessSectionResultCannotProcess) {\n            return FlipperApplicationPreloadStatusInvalidFile;\n        }\n    }\n\n    // load manifest section\n    if(elf_process_section(\n           app->elf, \".fapmeta\", flipper_application_process_manifest_section, &app->manifest) !=\n       ElfProcessSectionResultSuccess) {\n        return FlipperApplicationPreloadStatusInvalidFile;\n    }\n\n    return flipper_application_validate_manifest(app);\n}\n\n/* Parse headers, load manifest */\nFlipperApplicationPreloadStatus\n    flipper_application_preload_manifest(FlipperApplication* app, const char* path) {\n    furi_check(app);\n    furi_check(path);\n\n    return flipper_application_load(app, path, false);\n}\n\n/* Parse headers, load full file */\nFlipperApplicationPreloadStatus\n    flipper_application_preload(FlipperApplication* app, const char* path) {\n    furi_check(app);\n    furi_check(path);\n\n    return flipper_application_load(app, path, true);\n}\n\nconst FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {\n    furi_check(app);\n    return &app->manifest;\n}\n\nFlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {\n    furi_check(app);\n\n    ELFFileLoadStatus status = elf_file_load_sections(app->elf);\n\n    switch(status) {\n    case ELFFileLoadStatusSuccess:\n        elf_file_init_debug_info(app->elf, &app->state);\n        flipper_application_list_add_app(app);\n        return FlipperApplicationLoadStatusSuccess;\n    case ELFFileLoadStatusMissingImports:\n        return FlipperApplicationLoadStatusMissingImports;\n    default:\n        return FlipperApplicationLoadStatusUnspecifiedError;\n    }\n}\n\nstatic int32_t flipper_application_thread(void* context) {\n    furi_check(context);\n    FlipperApplication* app = (FlipperApplication*)context;\n\n    elf_file_call_init(app->elf);\n\n    FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf);\n    int32_t ret_code = entry_point(app->ep_thread_args);\n\n    elf_file_call_fini(app->elf);\n\n    // wait until all notifications from RAM are completed\n    NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);\n    notification_message_block(notifications, &sequence_empty);\n    furi_record_close(RECORD_NOTIFICATION);\n\n    return ret_code;\n}\n\nFuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) {\n    furi_check(app);\n    furi_check(app->thread == NULL);\n    furi_check(!flipper_application_is_plugin(app));\n\n    if(app->ep_thread_args) {\n        free(app->ep_thread_args);\n    }\n\n    if(args) {\n        app->ep_thread_args = strdup(args);\n    } else {\n        app->ep_thread_args = NULL;\n    }\n\n    const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);\n    app->thread = furi_thread_alloc_ex(\n        manifest->name, manifest->stack_size, flipper_application_thread, app);\n\n    return app->thread;\n}\n\nconst char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {\n    switch(status) {\n    case FlipperApplicationPreloadStatusSuccess:\n        return \"Success\";\n    case FlipperApplicationPreloadStatusInvalidFile:\n        return \"Invalid file\";\n    case FlipperApplicationPreloadStatusNotEnoughMemory:\n        return \"Not enough memory\";\n    case FlipperApplicationPreloadStatusInvalidManifest:\n        return \"Invalid file manifest\";\n    case FlipperApplicationPreloadStatusApiTooOld:\n        return \"Update Application to use with this Firmware (ApiTooOld)\";\n    case FlipperApplicationPreloadStatusApiTooNew:\n        return \"Update Firmware to use with this Application (ApiTooNew)\";\n    case FlipperApplicationPreloadStatusTargetMismatch:\n        return \"Hardware target mismatch\";\n    }\n\n    return \"Unknown error\";\n}\n\nconst char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {\n    switch(status) {\n    case FlipperApplicationLoadStatusSuccess:\n        return \"Success\";\n    case FlipperApplicationLoadStatusUnspecifiedError:\n        return \"Unknown error\";\n    case FlipperApplicationLoadStatusMissingImports:\n        return \"Update Firmware to use with this Application (MissingImports)\";\n    }\n\n    return \"Unknown error\";\n}\n\nconst FlipperAppPluginDescriptor*\n    flipper_application_plugin_get_descriptor(FlipperApplication* app) {\n    furi_check(app);\n\n    if(!flipper_application_is_plugin(app)) {\n        return NULL;\n    }\n\n    if(!elf_file_is_init_complete(app->elf)) {\n        elf_file_call_init(app->elf);\n    }\n\n    typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void);\n    get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf);\n    furi_check(lib_ep);\n\n    const FlipperAppPluginDescriptor* lib_descriptor = lib_ep();\n\n    FURI_LOG_D(\n        TAG,\n        \"Library for %s, API v. %lu loaded\",\n        lib_descriptor->appid,\n        lib_descriptor->ep_api_version);\n\n    return lib_descriptor;\n}\n\nbool flipper_application_load_name_and_icon(\n    FuriString* path,\n    Storage* storage,\n    uint8_t** icon_ptr,\n    FuriString* item_name) {\n    furi_check(path);\n    furi_check(storage);\n    furi_check(icon_ptr);\n    furi_check(item_name);\n\n    FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);\n\n    FlipperApplicationPreloadStatus preload_res =\n        flipper_application_preload_manifest(app, furi_string_get_cstr(path));\n\n    bool load_success = false;\n\n    if(preload_res == FlipperApplicationPreloadStatusSuccess) {\n        const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);\n        if(manifest->has_icon) {\n            memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE);\n        }\n        furi_string_set(item_name, manifest->name);\n        load_success = true;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to preload %s\", furi_string_get_cstr(path));\n        load_success = false;\n    }\n\n    flipper_application_free(app);\n    return load_success;\n}"
  },
  {
    "path": "lib/flipper_application/flipper_application.h",
    "content": "/**\n * @file flipper_application.h\n * Flipper application\n */\n#pragma once\n\n#include \"application_manifest.h\"\n#include \"elf/elf_api_interface.h\"\n\n#include <furi.h>\n#include <storage/storage.h>\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    FlipperApplicationPreloadStatusSuccess = 0,\n    FlipperApplicationPreloadStatusInvalidFile,\n    FlipperApplicationPreloadStatusNotEnoughMemory,\n    FlipperApplicationPreloadStatusInvalidManifest,\n    FlipperApplicationPreloadStatusApiTooOld,\n    FlipperApplicationPreloadStatusApiTooNew,\n    FlipperApplicationPreloadStatusTargetMismatch,\n} FlipperApplicationPreloadStatus;\n\ntypedef enum {\n    FlipperApplicationLoadStatusSuccess = 0,\n    FlipperApplicationLoadStatusUnspecifiedError,\n    FlipperApplicationLoadStatusMissingImports,\n} FlipperApplicationLoadStatus;\n\n/** Get text description of preload status\n * @param status Status code\n * @return String pointer to description\n */\nconst char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status);\n\n/** Get text description of load status\n * @param status Status code\n * @return String pointer to description\n */\nconst char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status);\n\ntypedef struct FlipperApplication FlipperApplication;\n\ntypedef struct {\n    const char* name;\n    uint32_t address;\n} FlipperApplicationMemoryMapEntry;\n\ntypedef struct {\n    uint32_t mmap_entry_count;\n    FlipperApplicationMemoryMapEntry* mmap_entries;\n    uint32_t debug_link_size;\n    uint8_t* debug_link;\n} FlipperApplicationState;\n\n/** Initialize FlipperApplication object\n * @param storage Storage instance\n * @param api_interface ELF API interface to use for pre-loading and symbol resolving\n * @return Application instance\n */\nFlipperApplication*\n    flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface);\n\n/** Destroy FlipperApplication object\n * @param app Application pointer\n */\nvoid flipper_application_free(FlipperApplication* app);\n\n/** Validate elf file and load application metadata\n *\n * @param      app   Application pointer\n * @param[in]  path  The path to fap file\n *\n * @return     Preload result code\n */\nFlipperApplicationPreloadStatus\n    flipper_application_preload(FlipperApplication* app, const char* path);\n\n/** Validate elf file and load application manifest\n *\n * @param      app   Application pointer\n * @param[in]  path  The path to fap file\n *\n * @return     Preload result code\n */\nFlipperApplicationPreloadStatus\n    flipper_application_preload_manifest(FlipperApplication* app, const char* path);\n\n/** Get pointer to application manifest for preloaded application\n * @param app Application pointer\n * @return Pointer to application manifest\n */\nconst FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app);\n\n/** Load sections and process relocations for already pre-loaded application\n * @param app Application pointer\n * @return Load result code\n */\nFlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);\n\n/** Allocate application thread at entry point address, using app name and\n * stack size from metadata. Returned thread isn't started yet. \n * Can be only called once for application instance.\n * @param app Applicaiton pointer\n * @param args Args to pass to app's entry point\n * @return Created thread\n */\nFuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args);\n\n/** Check if application is a plugin (not a runnable standalone app)\n * @param app Application pointer\n * @return true if application is a plugin, false otherwise\n */\nbool flipper_application_is_plugin(FlipperApplication* app);\n\n/** Entry point prototype for standalone applications\n */\ntypedef int32_t (*FlipperApplicationEntryPoint)(void*);\n\n/** An object that describes a plugin - must be returned by plugin's entry point \n */\ntypedef struct {\n    const char* appid;\n    const uint32_t ep_api_version;\n    const void* entry_point;\n} FlipperAppPluginDescriptor;\n\n/** Entry point prototype for plugins\n */\ntypedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(void);\n\n/** Get plugin descriptor for preloaded plugin\n * @param app Application pointer\n * @return Pointer to plugin descriptor\n */\nconst FlipperAppPluginDescriptor*\n    flipper_application_plugin_get_descriptor(FlipperApplication* app);\n\n/** Load name and icon from FAP file.\n * \n * @param path Path to FAP file.\n * @param storage Storage instance.\n * @param icon_ptr Icon pointer.\n * @param item_name Application name.\n * @return true if icon and name were loaded successfully.\n */\nbool flipper_application_load_name_and_icon(\n    FuriString* path,\n    Storage* storage,\n    uint8_t** icon_ptr,\n    FuriString* item_name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_application/plugins/composite_resolver.c",
    "content": "#include \"composite_resolver.h\"\n\n#include <furi.h>\n#include <m-list.h>\n#include <m-algo.h>\n\nLIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) // NOLINT\n#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST)\n\nstruct CompositeApiResolver {\n    ElfApiInterface api_interface;\n    ElfApiInterfaceList_t interfaces;\n};\n\nstatic bool composite_api_resolver_callback(\n    const ElfApiInterface* interface,\n    uint32_t hash,\n    Elf32_Addr* address) {\n    CompositeApiResolver* resolver = (CompositeApiResolver*)interface;\n    for\n        M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {\n            if((*interface)->resolver_callback(*interface, hash, address)) {\n                return true;\n            }\n        }\n    return false;\n}\n\nCompositeApiResolver* composite_api_resolver_alloc(void) {\n    CompositeApiResolver* resolver = malloc(sizeof(CompositeApiResolver));\n\n    resolver->api_interface.api_version_major = 0;\n    resolver->api_interface.api_version_minor = 0;\n    resolver->api_interface.resolver_callback = &composite_api_resolver_callback;\n    ElfApiInterfaceList_init(resolver->interfaces);\n\n    return resolver;\n}\n\nvoid composite_api_resolver_free(CompositeApiResolver* resolver) {\n    furi_check(resolver);\n\n    ElfApiInterfaceList_clear(resolver->interfaces);\n    free(resolver);\n}\n\nvoid composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface) {\n    furi_check(resolver);\n    furi_check(interface);\n\n    if(ElfApiInterfaceList_empty_p(resolver->interfaces)) {\n        resolver->api_interface.api_version_major = interface->api_version_major;\n        resolver->api_interface.api_version_minor = interface->api_version_minor;\n    }\n    ElfApiInterfaceList_push_back(resolver->interfaces, interface);\n}\n\nconst ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver) {\n    furi_check(resolver);\n    return &resolver->api_interface;\n}\n"
  },
  {
    "path": "lib/flipper_application/plugins/composite_resolver.h",
    "content": "#pragma once\n\n#include <flipper_application/elf/elf_api_interface.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Composite API resolver \n * Resolves API interface by calling all resolvers in order\n * Uses API version from first resolver\n * Note: when using hashtable resolvers, collisions between tables are not detected\n * Can be cast to ElfApiInterface*\n */\ntypedef struct CompositeApiResolver CompositeApiResolver;\n\n/**\n * @brief Allocate composite API resolver\n * @return CompositeApiResolver* instance\n */\nCompositeApiResolver* composite_api_resolver_alloc(void);\n\n/**\n * @brief Free composite API resolver\n * @param resolver Instance\n */\nvoid composite_api_resolver_free(CompositeApiResolver* resolver);\n\n/**\n * @brief Add API resolver to composite resolver\n * @param resolver Instance\n * @param interface API resolver\n */\nvoid composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface);\n\n/**\n * @brief Get API interface from composite resolver\n * @param resolver Instance\n * @return API interface\n */\nconst ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_application/plugins/plugin_manager.c",
    "content": "#include \"plugin_manager.h\"\n\n#include <loader/firmware_api/firmware_api.h>\n#include <storage/storage.h>\n#include <toolbox/path.h>\n\n#include <m-array.h>\n#include <m-algo.h>\n\n#include <furi.h>\n\n#define TAG \"PluginManager\"\n\nARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) // NOLINT\n#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST)\n\nstruct PluginManager {\n    const char* application_id;\n    uint32_t api_version;\n    Storage* storage;\n    FlipperApplicationList_t libs;\n    const ElfApiInterface* api_interface;\n};\n\nPluginManager* plugin_manager_alloc(\n    const char* application_id,\n    uint32_t api_version,\n    const ElfApiInterface* api_interface) {\n    PluginManager* manager = malloc(sizeof(PluginManager));\n    manager->application_id = application_id;\n    manager->api_version = api_version;\n    manager->api_interface = api_interface ? api_interface : firmware_api_interface;\n    manager->storage = furi_record_open(RECORD_STORAGE);\n    FlipperApplicationList_init(manager->libs);\n    return manager;\n}\n\nvoid plugin_manager_free(PluginManager* manager) {\n    furi_check(manager);\n\n    for\n        M_EACH(loaded_lib, manager->libs, FlipperApplicationList_t) {\n            flipper_application_free(*loaded_lib);\n        }\n    FlipperApplicationList_clear(manager->libs);\n    furi_record_close(RECORD_STORAGE);\n    free(manager);\n}\n\nPluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path) {\n    furi_check(manager);\n    FlipperApplication* lib = flipper_application_alloc(manager->storage, manager->api_interface);\n\n    PluginManagerError error = PluginManagerErrorNone;\n    do {\n        FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path);\n\n        if(preload_res != FlipperApplicationPreloadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to preload %s\", path);\n            error = PluginManagerErrorLoaderError;\n            break;\n        }\n\n        if(!flipper_application_is_plugin(lib)) {\n            FURI_LOG_E(TAG, \"Not a plugin %s\", path);\n            error = PluginManagerErrorLoaderError;\n            break;\n        }\n\n        FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib);\n        if(load_status != FlipperApplicationLoadStatusSuccess) {\n            FURI_LOG_E(TAG, \"Failed to load %s\", path);\n            error = PluginManagerErrorLoaderError;\n            break;\n        }\n\n        const FlipperAppPluginDescriptor* app_descriptor =\n            flipper_application_plugin_get_descriptor(lib);\n\n        if(!app_descriptor) {\n            FURI_LOG_E(TAG, \"Failed to get descriptor %s\", path);\n            error = PluginManagerErrorLoaderError;\n            break;\n        }\n\n        if(strcmp(app_descriptor->appid, manager->application_id) != 0) {\n            FURI_LOG_E(TAG, \"Application id mismatch %s\", path);\n            error = PluginManagerErrorApplicationIdMismatch;\n            break;\n        }\n\n        if(app_descriptor->ep_api_version != manager->api_version) {\n            FURI_LOG_E(TAG, \"API version mismatch %s\", path);\n            error = PluginManagerErrorAPIVersionMismatch;\n            break;\n        }\n\n        FlipperApplicationList_push_back(manager->libs, lib);\n    } while(false);\n\n    if(error != PluginManagerErrorNone) {\n        flipper_application_free(lib);\n    }\n\n    return error;\n}\n\nPluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) {\n    furi_check(manager);\n    File* directory = storage_file_alloc(manager->storage);\n    char file_name_buffer[256];\n    FuriString* file_name = furi_string_alloc();\n    do {\n        if(!storage_dir_open(directory, path)) {\n            FURI_LOG_E(TAG, \"Failed to open directory %s\", path);\n            break;\n        }\n        while(true) {\n            if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) {\n                break;\n            }\n\n            furi_string_set(file_name, file_name_buffer);\n            if(!furi_string_end_with_str(file_name, \".fal\")) {\n                continue;\n            }\n\n            path_concat(path, file_name_buffer, file_name);\n            FURI_LOG_D(TAG, \"Loading %s\", furi_string_get_cstr(file_name));\n            PluginManagerError error =\n                plugin_manager_load_single(manager, furi_string_get_cstr(file_name));\n\n            if(error != PluginManagerErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to load %s\", furi_string_get_cstr(file_name));\n                break;\n            }\n        }\n    } while(false);\n    storage_dir_close(directory);\n    storage_file_free(directory);\n    furi_string_free(file_name);\n    return PluginManagerErrorNone;\n}\n\nuint32_t plugin_manager_get_count(PluginManager* manager) {\n    furi_check(manager);\n\n    return FlipperApplicationList_size(manager->libs);\n}\n\nconst FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index) {\n    furi_check(manager);\n\n    FlipperApplication* app = *FlipperApplicationList_get(manager->libs, index);\n    return flipper_application_plugin_get_descriptor(app);\n}\n\nconst void* plugin_manager_get_ep(PluginManager* manager, uint32_t index) {\n    furi_check(manager);\n\n    const FlipperAppPluginDescriptor* lib_descr = plugin_manager_get(manager, index);\n    furi_check(lib_descr);\n    return lib_descr->entry_point;\n}\n"
  },
  {
    "path": "lib/flipper_application/plugins/plugin_manager.h",
    "content": "#pragma once\n\n#include <flipper_application/flipper_application.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Object that manages plugins for an application\n * Implements mass loading of plugins and provides access to their descriptors\n */\ntypedef struct PluginManager PluginManager;\n\ntypedef enum {\n    PluginManagerErrorNone = 0,\n    PluginManagerErrorLoaderError,\n    PluginManagerErrorApplicationIdMismatch,\n    PluginManagerErrorAPIVersionMismatch,\n} PluginManagerError;\n\n/**\n * @brief Allocates new PluginManager\n * @param application_id Application ID filter - only plugins with matching ID will be loaded\n * @param api_version Application API version filter - only plugins with matching API version\n * @param api_interface Application API interface - used to resolve plugins' API imports\n *  If plugin uses private application's API, use CompoundApiInterface\n * @return new PluginManager instance\n */\nPluginManager* plugin_manager_alloc(\n    const char* application_id,\n    uint32_t api_version,\n    const ElfApiInterface* api_interface);\n\n/**\n * @brief Frees PluginManager\n * @param manager PluginManager instance\n */\nvoid plugin_manager_free(PluginManager* manager);\n\n/**\n * @brief Loads single plugin by full path\n * @param manager PluginManager instance\n * @param path Path to plugin\n * @return Error code\n */\nPluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path);\n\n/**\n * @brief Loads all plugins from specified directory\n * @param manager PluginManager instance\n * @param path Path to directory\n * @return Error code\n */\nPluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path);\n\n/**\n * @brief Returns number of loaded plugins\n * @param manager PluginManager instance\n * @return Number of loaded plugins\n */\nuint32_t plugin_manager_get_count(PluginManager* manager);\n\n/**\n * @brief Returns plugin descriptor by index\n * @param manager PluginManager instance\n * @param index Plugin index\n * @return Plugin descriptor\n */\nconst FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index);\n\n/**\n * @brief Returns plugin entry point by index\n * @param manager PluginManager instance\n * @param index Plugin index\n * @return Plugin entry point\n */\nconst void* plugin_manager_get_ep(PluginManager* manager, uint32_t index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_format/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/flipper_format\",\n    ],\n    SDK_HEADERS=[\n        File(\"flipper_format.h\"),\n        File(\"flipper_format_i.h\"),\n        File(\"flipper_format_stream.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"flipperformat\")\nlibenv.ApplyLibFlags()\n\nif libenv[\"RAM_EXEC\"]:\n    libenv.Append(\n        CPPDEFINES=[\n            \"FLIPPER_STREAM_LITE\",\n        ],\n    )\n\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/flipper_format/flipper_format.c",
    "content": "#include <core/check.h>\n#include <toolbox/stream/stream.h>\n#include <toolbox/stream/string_stream.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/stream/buffered_file_stream.h>\n#include \"flipper_format.h\"\n#include \"flipper_format_i.h\"\n#include \"flipper_format_stream.h\"\n#include \"flipper_format_stream_i.h\"\n\n// permits direct casting between `FlipperFormatOffset` and `StreamOffset`\nstatic_assert((size_t)FlipperFormatOffsetFromCurrent == (size_t)StreamOffsetFromCurrent);\nstatic_assert((size_t)FlipperFormatOffsetFromStart == (size_t)StreamOffsetFromStart);\nstatic_assert((size_t)FlipperFormatOffsetFromEnd == (size_t)StreamOffsetFromEnd);\n\n/********************************** Private **********************************/\nstruct FlipperFormat {\n    Stream* stream;\n    bool strict_mode;\n};\n\nstatic const char* const flipper_format_filetype_key = \"Filetype\";\nstatic const char* const flipper_format_version_key = \"Version\";\n\nStream* flipper_format_get_raw_stream(FlipperFormat* flipper_format) {\n    return flipper_format->stream;\n}\n\n/********************************** Public **********************************/\n\nFlipperFormat* flipper_format_string_alloc(void) {\n    FlipperFormat* flipper_format = malloc(sizeof(FlipperFormat));\n    flipper_format->stream = string_stream_alloc();\n    flipper_format->strict_mode = false;\n    return flipper_format;\n}\n\nFlipperFormat* flipper_format_file_alloc(Storage* storage) {\n    FlipperFormat* flipper_format = malloc(sizeof(FlipperFormat));\n    flipper_format->stream = file_stream_alloc(storage);\n    flipper_format->strict_mode = false;\n    return flipper_format;\n}\n\nFlipperFormat* flipper_format_buffered_file_alloc(Storage* storage) {\n    FlipperFormat* flipper_format = malloc(sizeof(FlipperFormat));\n    flipper_format->stream = buffered_file_stream_alloc(storage);\n    flipper_format->strict_mode = false;\n    return flipper_format;\n}\n\nbool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n    return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING);\n}\n\nbool flipper_format_buffered_file_open_existing(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n    return buffered_file_stream_open(\n        flipper_format->stream, path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING);\n}\n\nbool flipper_format_file_open_append(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n\n    bool result =\n        file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_OPEN_APPEND);\n\n    // Add EOL if it is not there\n    if(stream_size(flipper_format->stream) >= 1) {\n        do {\n            char last_char;\n            result = false;\n\n            if(!stream_seek(flipper_format->stream, -1, StreamOffsetFromEnd)) break;\n\n            uint16_t bytes_were_read =\n                stream_read(flipper_format->stream, (uint8_t*)&last_char, 1);\n            if(bytes_were_read != 1) break;\n\n            if(last_char != flipper_format_eoln) {\n                if(!flipper_format_stream_write_eol(flipper_format->stream)) break;\n            }\n\n            result = true;\n        } while(false);\n    } else {\n        stream_seek(flipper_format->stream, 0, StreamOffsetFromEnd);\n    }\n\n    return result;\n}\n\nbool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n    return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);\n}\n\nbool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n    return buffered_file_stream_open(\n        flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);\n}\n\nbool flipper_format_file_open_new(FlipperFormat* flipper_format, const char* path) {\n    furi_check(flipper_format);\n    return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_NEW);\n}\n\nbool flipper_format_file_close(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return file_stream_close(flipper_format->stream);\n}\n\nbool flipper_format_buffered_file_close(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return buffered_file_stream_close(flipper_format->stream);\n}\n\nvoid flipper_format_free(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    stream_free(flipper_format->stream);\n    free(flipper_format);\n}\n\nvoid flipper_format_set_strict_mode(FlipperFormat* flipper_format, bool strict_mode) {\n    flipper_format->strict_mode = strict_mode;\n}\n\nbool flipper_format_rewind(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return stream_rewind(flipper_format->stream);\n}\n\nsize_t flipper_format_tell(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return stream_tell(flipper_format->stream);\n}\n\nbool flipper_format_seek(FlipperFormat* flipper_format, int32_t offset, FlipperFormatOffset anchor) {\n    furi_check(flipper_format);\n    // direct usage of `anchor` made valid by `static_assert`s at the top of this file\n    return stream_seek(flipper_format->stream, offset, (StreamOffset)anchor);\n}\n\nbool flipper_format_seek_to_end(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return stream_seek(flipper_format->stream, 0, StreamOffsetFromEnd);\n}\n\nbool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key) {\n    size_t pos = stream_tell(flipper_format->stream);\n    stream_seek(flipper_format->stream, 0, StreamOffsetFromStart);\n    bool result = flipper_format_stream_seek_to_key(flipper_format->stream, key, false);\n    stream_seek(flipper_format->stream, pos, StreamOffsetFromStart);\n\n    return result;\n}\n\nbool flipper_format_read_header(\n    FlipperFormat* flipper_format,\n    FuriString* filetype,\n    uint32_t* version) {\n    furi_check(flipper_format);\n    return flipper_format_read_string(flipper_format, flipper_format_filetype_key, filetype) &&\n           flipper_format_read_uint32(flipper_format, flipper_format_version_key, version, 1);\n}\n\nbool flipper_format_write_header(\n    FlipperFormat* flipper_format,\n    FuriString* filetype,\n    const uint32_t version) {\n    furi_check(flipper_format);\n    return flipper_format_write_header_cstr(\n        flipper_format, furi_string_get_cstr(filetype), version);\n}\n\nbool flipper_format_write_header_cstr(\n    FlipperFormat* flipper_format,\n    const char* filetype,\n    const uint32_t version) {\n    furi_check(flipper_format);\n    return flipper_format_write_string_cstr(\n               flipper_format, flipper_format_filetype_key, filetype) &&\n           flipper_format_write_uint32(flipper_format, flipper_format_version_key, &version, 1);\n}\n\nbool flipper_format_get_value_count(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint32_t* count) {\n    furi_check(flipper_format);\n    return flipper_format_stream_get_value_count(\n        flipper_format->stream, key, count, flipper_format->strict_mode);\n}\n\nbool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, FuriString* data) {\n    furi_check(flipper_format);\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream, key, FlipperStreamValueStr, data, 1, flipper_format->strict_mode);\n}\n\nbool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, FuriString* data) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueStr,\n        .data = furi_string_get_cstr(data),\n        .data_size = 1,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_write_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueStr,\n        .data = data,\n        .data_size = 1,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_hex_uint64(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint64_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueHexUint64,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_hex_uint64(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint64_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueHexUint64,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint32_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueUint32,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueUint32,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    int32_t* data,\n    const uint16_t data_size) {\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueInt32,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueInt32,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    bool* data,\n    const uint16_t data_size) {\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueBool,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueBool,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    float* data,\n    const uint16_t data_size) {\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueFloat,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueFloat,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_read_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint8_t* data,\n    const uint16_t data_size) {\n    return flipper_format_stream_read_value_line(\n        flipper_format->stream,\n        key,\n        FlipperStreamValueHex,\n        data,\n        data_size,\n        flipper_format->strict_mode);\n}\n\nbool flipper_format_write_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueHex,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data);\n    return result;\n}\n\nbool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* data) {\n    furi_check(flipper_format);\n    return flipper_format_write_comment_cstr(flipper_format, furi_string_get_cstr(data));\n}\n\nbool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data) {\n    furi_check(flipper_format);\n    return flipper_format_stream_write_comment_cstr(flipper_format->stream, data);\n}\n\nbool flipper_format_write_empty_line(FlipperFormat* flipper_format) {\n    furi_check(flipper_format);\n    return flipper_format_stream_write_eol(flipper_format->stream);\n}\n\nbool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueIgnore,\n        .data = NULL,\n        .data_size = 0,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, FuriString* data) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueStr,\n        .data = furi_string_get_cstr(data),\n        .data_size = 1,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueStr,\n        .data = data,\n        .data_size = 1,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size) {\n    furi_check(flipper_format);\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueUint32,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size) {\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueInt32,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size) {\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueBool,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size) {\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueFloat,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_update_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size) {\n    FlipperStreamWriteData write_data = {\n        .key = key,\n        .type = FlipperStreamValueHex,\n        .data = data,\n        .data_size = data_size,\n    };\n    bool result = flipper_format_stream_delete_key_and_write(\n        flipper_format->stream, &write_data, flipper_format->strict_mode);\n    return result;\n}\n\nbool flipper_format_insert_or_update_string(\n    FlipperFormat* flipper_format,\n    const char* key,\n    FuriString* data) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_string(flipper_format, key, data);\n    } else {\n        result = flipper_format_update_string(flipper_format, key, data);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_string_cstr(flipper_format, key, data);\n    } else {\n        result = flipper_format_update_string_cstr(flipper_format, key, data);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_uint32(flipper_format, key, data, data_size);\n    } else {\n        result = flipper_format_update_uint32(flipper_format, key, data, data_size);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_int32(flipper_format, key, data, data_size);\n    } else {\n        result = flipper_format_update_int32(flipper_format, key, data, data_size);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_bool(flipper_format, key, data, data_size);\n    } else {\n        result = flipper_format_update_bool(flipper_format, key, data, data_size);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_float(flipper_format, key, data, data_size);\n    } else {\n        result = flipper_format_update_float(flipper_format, key, data, data_size);\n    }\n\n    return result;\n}\n\nbool flipper_format_insert_or_update_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size) {\n    bool result = false;\n\n    if(!flipper_format_key_exist(flipper_format, key)) {\n        flipper_format_seek_to_end(flipper_format);\n        result = flipper_format_write_hex(flipper_format, key, data, data_size);\n    } else {\n        result = flipper_format_update_hex(flipper_format, key, data, data_size);\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "lib/flipper_format/flipper_format.h",
    "content": "/** @file flipper_format.h Flipper File Format helper library.\n *\n * Flipper File Format is a fairly simple format for storing data in a file.\n *\n * Flipper file structure:\n *\n * ~~~~~~~~~~~~~~~~~~~~~\n * # Commentary\n * Field name: field value\n * ~~~~~~~~~~~~~~~~~~~~~\n *\n * Lines starting with the # character are ignored (considered as comments). The\n * separator between the name of the value and the value itself is the string\n * \": \".\n *\n * Currently supported types:\n *\n * ~~~~~~~~~~~~~~~~~~~~~\n * String: text\n * Int32: 1 2 -3 4\n * Uint32: 1 2 3 4\n * Float: 1.0 1234.654\n * Hex: A4 B3 C2 D1 12 FF\n * ~~~~~~~~~~~~~~~~~~~~~\n *\n * End of line is LF when writing, but CR is supported when reading.\n *\n * The library is designed in such a way that comments and field values are\n * completely ignored when searching for keys, that is, they do not consume\n * memory.\n *\n * File example:\n *\n * ~~~~~~~~~~~~~~~~~~~~~\n * Filetype: Flipper Test File\n * Version: 1\n * # Just test file\n * String: String value\n * UINT: 1234\n * Hex: 00 01 FF A3\n * ~~~~~~~~~~~~~~~~~~~~~\n *\n * Writing:\n *\n * ~~~~~~~~~~~~~~~~~~~~~\n * FlipperFormat* format = flipper_format_file_alloc(storage);\n *\n * do { const uint32_t version = 1; const char* string_value = \"String value\";\n * const uint32_t uint32_value = 1234; const uint16_t array_size = 4; const\n * uint8_t* array[array_size] = {0x00, 0x01, 0xFF, 0xA3};\n *\n * if(!flipper_format_file_open_new(format, EXT_PATH(\"flipper_format_test\")))\n * break; if(!flipper_format_write_header_cstr(format, \"Flipper Test File\",\n * version)) break; if(!flipper_format_write_comment_cstr(format,\n * \"Just test file\")) break; if(!flipper_format_write_string_cstr(format,\n * \"String\", string_value)) break; if(!flipper_format_write_uint32(format,\n * \"UINT\", &uint32_value, 1)) break; if(!flipper_format_write_hex(format,\n * \"Hex Array\", array, array_size)) break;\n *\n * // signal that the file was written successfully } while(0);\n *\n * flipper_format_free(file);\n * ~~~~~~~~~~~~~~~~~~~~~\n *\n * Reading:\n *\n * ~~~~~~~~~~~~~~~~~~~~~\n * FlipperFormat* file = flipper_format_file_alloc(storage);\n *\n * do { uint32_t version = 1; FuriString* file_type; FuriString* string_value;\n * uint32_t uint32_value = 1; uint16_t array_size = 4; uint8_t*\n * array[array_size] = {0}; file_type = furi_string_alloc(); string_value =\n * furi_string_alloc();\n *\n * if(!flipper_format_file_open_existing(file, EXT_PATH(\"flipper_format_test\")))\n * break; if(!flipper_format_read_header(file, file_type, &version)) break;\n * if(!flipper_format_read_string(file, \"String\", string_value)) break;\n * if(!flipper_format_read_uint32(file, \"UINT\", &uint32_value, 1)) break;\n * if(!flipper_format_read_hex(file, \"Hex Array\", array, array_size)) break;\n *\n * // signal that the file was read successfully } while(0);\n *\n * flipper_format_free(file);\n * ~~~~~~~~~~~~~~~~~~~~~\n */\n\n#pragma once\n#include <stdint.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FlipperFormat FlipperFormat;\n\ntypedef enum {\n    FlipperFormatOffsetFromCurrent,\n    FlipperFormatOffsetFromStart,\n    FlipperFormatOffsetFromEnd,\n} FlipperFormatOffset;\n\n/** Allocate FlipperFormat as string.\n *\n * @return     FlipperFormat* pointer to a FlipperFormat instance\n */\nFlipperFormat* flipper_format_string_alloc(void);\n\n/** Allocate FlipperFormat as file.\n *\n * @param      storage  The storage\n *\n * @return     FlipperFormat* pointer to a FlipperFormat instance\n */\nFlipperFormat* flipper_format_file_alloc(Storage* storage);\n\n/** Allocate FlipperFormat as file, buffered mode.\n *\n * @param      storage  The storage\n *\n * @return     FlipperFormat* pointer to a FlipperFormat instance\n */\nFlipperFormat* flipper_format_buffered_file_alloc(Storage* storage);\n\n/** Open existing file. Use only if FlipperFormat allocated as a file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char* path);\n\n/** Open existing file, buffered mode. Use only if FlipperFormat allocated as a\n * buffered file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_buffered_file_open_existing(FlipperFormat* flipper_format, const char* path);\n\n/** Open existing file for writing and add values to the end of file. Use only if\n * FlipperFormat allocated as a file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_file_open_append(FlipperFormat* flipper_format, const char* path);\n\n/** Open file. Creates a new file, or deletes the contents of the file if it\n * already exists. Use only if FlipperFormat allocated as a file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* path);\n\n/** Open file. Creates a new file, or deletes the contents of the file if it\n * already exists, buffered mode. Use only if FlipperFormat allocated as a\n * buffered file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path);\n\n/** Open file. Creates a new file, fails if file already exists. Use only if\n * FlipperFormat allocated as a file.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      path            File path\n *\n * @return     True on success\n */\nbool flipper_format_file_open_new(FlipperFormat* flipper_format, const char* path);\n\n/** Closes the file, use only if FlipperFormat allocated as a file.\n *\n * @param      flipper_format  The flipper format\n *\n * @return     true\n * @return     false\n */\nbool flipper_format_file_close(FlipperFormat* flipper_format);\n\n/** Closes the file, use only if FlipperFormat allocated as a buffered file.\n *\n * @param      flipper_format  The flipper format\n *\n * @return     true\n * @return     false\n */\nbool flipper_format_buffered_file_close(FlipperFormat* flipper_format);\n\n/** Free FlipperFormat.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n */\nvoid flipper_format_free(FlipperFormat* flipper_format);\n\n/** Set FlipperFormat mode.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      strict_mode     True obligates not to skip valid fields. False by\n *                             default.\n */\nvoid flipper_format_set_strict_mode(FlipperFormat* flipper_format, bool strict_mode);\n\n/** Rewind the RW pointer.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n *\n * @return     True on success\n */\nbool flipper_format_rewind(FlipperFormat* flipper_format);\n\n/** Get the RW pointer position\n * \n * @param      flipper_format  Pointer to a FlipperFormat instance\n * \n * @return     RW pointer position\n */\nsize_t flipper_format_tell(FlipperFormat* flipper_format);\n\n/** Set the RW pointer position to an arbitrary value\n * \n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      offset          Offset relative to the anchor point\n * @param      anchor          Anchor point (e.g. start of file)\n * \n * @return     True on success\n */\nbool flipper_format_seek(FlipperFormat* flipper_format, int32_t offset, FlipperFormatOffset anchor);\n\n/** Move the RW pointer at the end. Can be useful if you want to add some data\n * after reading.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n *\n * @return     True on success\n */\nbool flipper_format_seek_to_end(FlipperFormat* flipper_format);\n\n/** Check if the key exists.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n *\n * @return     true key exists\n * @return     false key is not exists\n */\nbool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key);\n\n/** Read the header (file type and version).\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      filetype        File type string\n * @param      version         Version Value\n *\n * @return     True on success\n */\nbool flipper_format_read_header(\n    FlipperFormat* flipper_format,\n    FuriString* filetype,\n    uint32_t* version);\n\n/** Write the header (file type and version).\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      filetype        File type string\n * @param      version         Version Value\n *\n * @return     True on success\n */\nbool flipper_format_write_header(\n    FlipperFormat* flipper_format,\n    FuriString* filetype,\n    const uint32_t version);\n\n/** Write the header (file type and version). Plain C string version.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      filetype        File type string\n * @param      version         Version Value\n *\n * @return     True on success\n */\nbool flipper_format_write_header_cstr(\n    FlipperFormat* flipper_format,\n    const char* filetype,\n    const uint32_t version);\n\n/** Get the count of values by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             The key\n * @param      count           The count\n *\n * @return     bool\n */\nbool flipper_format_get_value_count(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint32_t* count);\n\n/** Read a string by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, FuriString* data);\n\n/** Write key and string\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, FuriString* data);\n\n/** Write key and string. Plain C string version.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_write_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data);\n\n/** Read array of uint64 in hex format by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_hex_uint64(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint64_t* data,\n    const uint16_t data_size);\n\n/** Write key and array of uint64 in hex format\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_hex_uint64(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint64_t* data,\n    const uint16_t data_size);\n\n/** Read array of uint32 by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint32_t* data,\n    const uint16_t data_size);\n\n/** Write key and array of uint32\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size);\n\n/** Read array of int32 by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    int32_t* data,\n    const uint16_t data_size);\n\n/** Write key and array of int32\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size);\n\n/** Read array of bool by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    bool* data,\n    const uint16_t data_size);\n\n/** Write key and array of bool\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size);\n\n/** Read array of float by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    float* data,\n    const uint16_t data_size);\n\n/** Write key and array of float\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size);\n\n/** Read array of hex-formatted bytes by key\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_read_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    uint8_t* data,\n    const uint16_t data_size);\n\n/** Write key and array of hex-formatted bytes\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param      data_size       Values count\n *\n * @return     True on success\n */\nbool flipper_format_write_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size);\n\n/** Write comment\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      data            Comment text\n *\n * @return     True on success\n */\nbool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* data);\n\n/** Write comment. Plain C string version.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      data            Comment text\n *\n * @return     True on success\n */\nbool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data);\n\n/** Write empty line (Improves readability for human based parsing)\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n *\n * @return     True on success\n */\nbool flipper_format_write_empty_line(FlipperFormat* flipper_format);\n\n/** Removes the first matching key and its value. Sets the RW pointer to a\n * position of deleted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n *\n * @return     True on success\n */\nbool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key);\n\n/** Updates the value of the first matching key to a string value. Sets the RW\n * pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, FuriString* data);\n\n/** Updates the value of the first matching key to a string value. Plain C\n * version. Sets the RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_update_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data);\n\n/** Updates the value of the first matching key to a uint32 array value. Sets the\n * RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_update_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a int32 array value. Sets the\n * RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_update_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a bool array value. Sets the\n * RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_update_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a float array value. Sets the\n * RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_update_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to an array of hex-formatted\n * bytes. Sets the RW pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_update_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a string value, or adds the\n * key and value if the key did not exist. Sets the RW pointer to a position at\n * the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_string(\n    FlipperFormat* flipper_format,\n    const char* key,\n    FuriString* data);\n\n/** Updates the value of the first matching key to a string value, or adds the\n * key and value if the key did not exist. Plain C version. Sets the RW pointer\n * to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_string_cstr(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const char* data);\n\n/** Updates the value of the first matching key to a uint32 array value, or adds\n * the key and value if the key did not exist. Sets the RW pointer to a position\n * at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_uint32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint32_t* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a int32 array value, or adds\n * the key and value if the key did not exist. Sets the RW pointer to a position\n * at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_int32(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const int32_t* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a bool array value, or adds\n * the key and value if the key did not exist. Sets the RW pointer to a position\n * at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_bool(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const bool* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to a float array value, or adds\n * the key and value if the key did not exist. Sets the RW pointer to a position\n * at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_float(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const float* data,\n    const uint16_t data_size);\n\n/** Updates the value of the first matching key to an array of hex-formatted\n * bytes, or adds the key and value if the key did not exist. Sets the RW\n * pointer to a position at the end of inserted data.\n *\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      key             Key\n * @param      data            Value\n * @param[in]  data_size       The data size\n *\n * @return     True on success\n */\nbool flipper_format_insert_or_update_hex(\n    FlipperFormat* flipper_format,\n    const char* key,\n    const uint8_t* data,\n    const uint16_t data_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_format/flipper_format_i.h",
    "content": "#pragma once\n#include <toolbox/stream/stream.h>\n#include \"flipper_format.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Returns the underlying stream instance.\n * Use only if you know what you are doing.\n * @param flipper_format \n * @return Stream* \n */\nStream* flipper_format_get_raw_stream(FlipperFormat* flipper_format);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_format/flipper_format_stream.c",
    "content": "#include <inttypes.h>\n#include <toolbox/hex.h>\n#include <toolbox/strint.h>\n#include <core/check.h>\n#include \"flipper_format_stream.h\"\n#include \"flipper_format_stream_i.h\"\n\nstatic inline bool flipper_format_stream_is_space(char c) {\n    return c == ' ' || c == '\\t' || c == flipper_format_eolr;\n}\n\nstatic bool flipper_format_stream_write(Stream* stream, const void* data, size_t data_size) {\n    size_t bytes_written = stream_write(stream, data, data_size);\n    return bytes_written == data_size;\n}\n\nstatic bool flipper_format_stream_write_key(Stream* stream, const char* key) {\n    bool result = false;\n\n    do {\n        if(!flipper_format_stream_write(stream, key, strlen(key))) break;\n        if(!flipper_format_stream_write(stream, &flipper_format_delimiter, 1)) break;\n        if(!flipper_format_stream_write(stream, \" \", 1)) break;\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nbool flipper_format_stream_write_eol(Stream* stream) {\n    return flipper_format_stream_write(stream, &flipper_format_eoln, 1);\n}\n\nstatic bool flipper_format_stream_read_valid_key(Stream* stream, FuriString* key) {\n    furi_string_reset(key);\n    const size_t buffer_size = 32;\n    uint8_t buffer[buffer_size];\n\n    bool found = false;\n    bool error = false;\n    bool accumulate = true;\n    bool new_line = true;\n\n    while(true) {\n        size_t was_read = stream_read(stream, buffer, buffer_size);\n        if(was_read == 0) break;\n\n        for(size_t i = 0; i < was_read; i++) {\n            uint8_t data = buffer[i];\n            if(data == flipper_format_eoln) {\n                // EOL found, clean data, start accumulating data and set the new_line flag\n                furi_string_reset(key);\n                accumulate = true;\n                new_line = true;\n            } else if(data == flipper_format_eolr) {\n                // ignore\n            } else if(data == flipper_format_comment && new_line) {\n                // if there is a comment character and we are at the beginning of a new line\n                // do not accumulate comment data and reset the new_line flag\n                accumulate = false;\n                new_line = false;\n            } else if(data == flipper_format_delimiter) {\n                if(new_line) {\n                    // we are on a \"new line\" and found the delimiter\n                    // this can only be if we have previously found some kind of key, so\n                    // clear the data, set the flag that we no longer want to accumulate data\n                    // and reset the new_line flag\n                    furi_string_reset(key);\n                    accumulate = false;\n                    new_line = false;\n                } else {\n                    // parse the delimiter only if we are accumulating data\n                    if(accumulate) {\n                        // we found the delimiter, move the rw pointer to the delimiter location\n                        // and signal that we have found something\n                        if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {\n                            error = true;\n                            break;\n                        }\n\n                        found = true;\n                        break;\n                    }\n                }\n            } else {\n                // just new symbol, reset the new_line flag\n                new_line = false;\n                if(accumulate) {\n                    // and accumulate data if we want\n                    furi_string_push_back(key, data);\n                }\n            }\n        }\n\n        if(found || error) break;\n    }\n\n    return found;\n}\n\nbool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) {\n    bool found = false;\n    FuriString* read_key;\n\n    read_key = furi_string_alloc();\n\n    while(!stream_eof(stream)) {\n        if(flipper_format_stream_read_valid_key(stream, read_key)) {\n            if(furi_string_cmp_str(read_key, key) == 0) {\n                if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) break;\n\n                found = true;\n                break;\n            } else if(strict_mode) {\n                found = false;\n                break;\n            }\n        }\n    }\n    furi_string_free(read_key);\n\n    return found;\n}\n\nstatic bool flipper_format_stream_read_value(Stream* stream, FuriString* value, bool* last) {\n    enum {\n        LeadingSpace,\n        ReadValue,\n        TrailingSpace\n    } state = LeadingSpace;\n    const size_t buffer_size = 32;\n    uint8_t buffer[buffer_size];\n    bool result = false;\n    bool error = false;\n\n    furi_string_reset(value);\n\n    while(true) {\n        size_t was_read = stream_read(stream, buffer, buffer_size);\n\n        if(was_read == 0) {\n            if(state != LeadingSpace && stream_eof(stream)) {\n                result = true;\n                *last = true;\n            } else {\n                error = true;\n            }\n        }\n\n        for(uint16_t i = 0; i < was_read; i++) {\n            const uint8_t data = buffer[i];\n\n            if(state == LeadingSpace) {\n                if(flipper_format_stream_is_space(data)) {\n                    continue;\n                } else if(data == flipper_format_eoln) {\n                    stream_seek(stream, i - was_read, StreamOffsetFromCurrent);\n                    error = true;\n                    break;\n                } else {\n                    state = ReadValue;\n                    furi_string_push_back(value, data);\n                }\n            } else if(state == ReadValue) {\n                if(flipper_format_stream_is_space(data)) {\n                    state = TrailingSpace;\n                } else if(data == flipper_format_eoln) {\n                    if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {\n                        error = true;\n                    } else {\n                        result = true;\n                        *last = true;\n                    }\n                    break;\n                } else {\n                    furi_string_push_back(value, data);\n                }\n            } else if(state == TrailingSpace) {\n                if(flipper_format_stream_is_space(data)) {\n                    continue;\n                } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {\n                    error = true;\n                } else {\n                    *last = (data == flipper_format_eoln);\n                    result = true;\n                }\n                break;\n            }\n        }\n\n        if(error || result) break;\n    }\n\n    return result;\n}\n\nstatic bool flipper_format_stream_read_line(Stream* stream, FuriString* str_result) {\n    furi_string_reset(str_result);\n    const size_t buffer_size = 32;\n    uint8_t buffer[buffer_size];\n\n    do {\n        size_t was_read = stream_read(stream, buffer, buffer_size);\n        if(was_read == 0) break;\n\n        bool result = false;\n        bool error = false;\n\n        for(size_t i = 0; i < was_read; i++) {\n            uint8_t data = buffer[i];\n            if(data == flipper_format_eoln) {\n                if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {\n                    error = true;\n                    break;\n                }\n\n                result = true;\n                break;\n            } else if(data == flipper_format_eolr) {\n                // Ignore\n            } else {\n                furi_string_push_back(str_result, data);\n            }\n        }\n\n        if(result || error) {\n            break;\n        }\n    } while(true);\n\n    return furi_string_size(str_result) != 0;\n}\n\nstatic bool flipper_format_stream_seek_to_next_line(Stream* stream) {\n    const size_t buffer_size = 32;\n    uint8_t buffer[buffer_size];\n    bool result = false;\n    bool error = false;\n\n    do {\n        size_t was_read = stream_read(stream, buffer, buffer_size);\n        if(was_read == 0) {\n            if(stream_eof(stream)) {\n                result = true;\n                break;\n            }\n        }\n\n        for(size_t i = 0; i < was_read; i++) {\n            if(buffer[i] == flipper_format_eoln) {\n                if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {\n                    error = true;\n                    break;\n                }\n\n                result = true;\n                break;\n            }\n        }\n\n        if(result || error) {\n            break;\n        }\n    } while(true);\n\n    return result;\n}\n\nbool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteData* write_data) {\n    bool result = false;\n\n    if(write_data->type == FlipperStreamValueIgnore) {\n        result = true;\n    } else {\n        FuriString* value;\n        value = furi_string_alloc();\n\n        do {\n            if(!flipper_format_stream_write_key(stream, write_data->key)) break;\n\n            if(write_data->type == FlipperStreamValueStr) write_data->data_size = 1;\n\n            bool cycle_error = false;\n            for(uint16_t i = 0; i < write_data->data_size; i++) {\n                switch(write_data->type) {\n                case FlipperStreamValueStr: {\n                    const char* data = write_data->data;\n                    furi_string_printf(value, \"%s\", data);\n                }; break;\n                case FlipperStreamValueHex: {\n                    const uint8_t* data = write_data->data;\n                    furi_string_printf(value, \"%02X\", data[i]);\n                }; break;\n#ifndef FLIPPER_STREAM_LITE\n                case FlipperStreamValueFloat: {\n                    const float* data = write_data->data;\n                    furi_string_printf(value, \"%f\", (double)data[i]);\n                }; break;\n#endif\n                case FlipperStreamValueInt32: {\n                    const int32_t* data = write_data->data;\n                    furi_string_printf(value, \"%\" PRIi32, data[i]);\n                }; break;\n                case FlipperStreamValueUint32: {\n                    const uint32_t* data = write_data->data;\n                    furi_string_printf(value, \"%\" PRIu32, data[i]);\n                }; break;\n                case FlipperStreamValueHexUint64: {\n                    const uint64_t* data = write_data->data;\n                    furi_string_printf(\n                        value, \"%08lX%08lX\", (uint32_t)(data[i] >> 32), (uint32_t)data[i]);\n                }; break;\n                case FlipperStreamValueBool: {\n                    const bool* data = write_data->data;\n                    furi_string_printf(value, data[i] ? \"true\" : \"false\");\n                }; break;\n                default:\n                    furi_crash(\"Unknown FF type\");\n                }\n\n                if(((size_t)i + 1) < write_data->data_size) {\n                    furi_string_cat(value, \" \");\n                }\n\n                if(!flipper_format_stream_write(\n                       stream, furi_string_get_cstr(value), furi_string_size(value))) {\n                    cycle_error = true;\n                    break;\n                }\n            }\n            if(cycle_error) break;\n\n            if(!flipper_format_stream_write_eol(stream)) break;\n            result = true;\n        } while(false);\n\n        furi_string_free(value);\n    }\n\n    return result;\n}\n\nbool flipper_format_stream_read_value_line(\n    Stream* stream,\n    const char* key,\n    FlipperStreamValue type,\n    void* _data,\n    size_t data_size,\n    bool strict_mode) {\n    bool result = false;\n\n    do {\n        if(!flipper_format_stream_seek_to_key(stream, key, strict_mode)) break;\n\n        if(type == FlipperStreamValueStr) {\n            FuriString* data = (FuriString*)_data;\n            if(flipper_format_stream_read_line(stream, data)) {\n                result = true;\n                break;\n            }\n        } else {\n            result = true;\n            FuriString* value;\n            value = furi_string_alloc();\n\n            for(size_t i = 0; i < data_size; i++) {\n                bool last = false;\n                result = flipper_format_stream_read_value(stream, value, &last);\n                if(result) {\n                    int scan_values = 0;\n\n                    switch(type) {\n                    case FlipperStreamValueHex: {\n                        uint8_t* data = _data;\n                        if(furi_string_size(value) >= 2) {\n                            // sscanf \"%02X\" does not work here\n                            if(hex_char_to_uint8(\n                                   furi_string_get_char(value, 0),\n                                   furi_string_get_char(value, 1),\n                                   &data[i])) {\n                                scan_values = 1;\n                            }\n                        }\n                    }; break;\n#ifndef FLIPPER_STREAM_LITE\n                    case FlipperStreamValueFloat: {\n                        float* data = _data;\n                        // newlib-nano does not have sscanf for floats\n                        // scan_values = sscanf(furi_string_get_cstr(value), \"%f\", &data[i]);\n                        char* end_char;\n                        data[i] = strtof(furi_string_get_cstr(value), &end_char);\n                        if(*end_char == 0) {\n                            // most likely ok\n                            scan_values = 1;\n                        }\n                    }; break;\n#endif\n                    case FlipperStreamValueInt32: {\n                        int32_t* data = _data;\n                        if(strint_to_int32(furi_string_get_cstr(value), NULL, &data[i], 10) ==\n                           StrintParseNoError) {\n                            scan_values = 1;\n                        }\n                    }; break;\n                    case FlipperStreamValueUint32: {\n                        uint32_t* data = _data;\n                        if(strint_to_uint32(furi_string_get_cstr(value), NULL, &data[i], 10) ==\n                           StrintParseNoError) {\n                            scan_values = 1;\n                        }\n                    }; break;\n                    case FlipperStreamValueHexUint64: {\n                        uint64_t* data = _data;\n                        if(furi_string_size(value) >= 16) {\n                            if(hex_chars_to_uint64(furi_string_get_cstr(value), &data[i])) {\n                                scan_values = 1;\n                            }\n                        }\n                    }; break;\n                    case FlipperStreamValueBool: {\n                        bool* data = _data;\n                        data[i] = !furi_string_cmpi(value, \"true\");\n                        scan_values = 1;\n                    }; break;\n                    default:\n                        furi_crash(\"Unknown FF type\");\n                    }\n\n                    if(scan_values != 1) {\n                        result = false;\n                        break;\n                    }\n                } else {\n                    break;\n                }\n\n                if(last && ((i + 1) != data_size)) {\n                    result = false;\n                    break;\n                }\n            }\n\n            furi_string_free(value);\n        }\n    } while(false);\n\n    return result;\n}\n\nbool flipper_format_stream_get_value_count(\n    Stream* stream,\n    const char* key,\n    uint32_t* count,\n    bool strict_mode) {\n    bool result = false;\n    bool last = false;\n\n    FuriString* value;\n    value = furi_string_alloc();\n\n    uint32_t position = stream_tell(stream);\n    do {\n        if(!flipper_format_stream_seek_to_key(stream, key, strict_mode)) break;\n        *count = 0;\n\n        result = true;\n        while(true) {\n            if(!flipper_format_stream_read_value(stream, value, &last)) {\n                result = false;\n                break;\n            }\n\n            *count = *count + 1;\n            if(last) break;\n        }\n\n    } while(false);\n\n    if(!stream_seek(stream, position, StreamOffsetFromStart)) {\n        result = false;\n    }\n\n    furi_string_free(value);\n    return result;\n}\n\nbool flipper_format_stream_delete_key_and_write(\n    Stream* stream,\n    FlipperStreamWriteData* write_data,\n    bool strict_mode) {\n    bool result = false;\n\n    do {\n        size_t size = stream_size(stream);\n        if(size == 0) break;\n\n        if(!stream_rewind(stream)) break;\n\n        // find key\n        if(!flipper_format_stream_seek_to_key(stream, write_data->key, strict_mode)) break;\n\n        // get key start position\n        size_t start_position = stream_tell(stream) - strlen(write_data->key);\n        if(start_position >= 2) {\n            start_position -= 2;\n        } else {\n            // something wrong\n            break;\n        }\n\n        // get value end position\n        if(!flipper_format_stream_seek_to_next_line(stream)) break;\n        size_t end_position = stream_tell(stream);\n        // newline symbol\n        if(end_position < size) {\n            end_position += 1;\n        }\n\n        if(!stream_seek(stream, start_position, StreamOffsetFromStart)) break;\n        if(!stream_delete_and_insert(\n               stream,\n               end_position - start_position,\n               (StreamWriteCB)flipper_format_stream_write_value_line,\n               write_data))\n            break;\n\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nbool flipper_format_stream_write_comment_cstr(Stream* stream, const char* data) {\n    bool result = false;\n    do {\n        const char comment_buffer[2] = {flipper_format_comment, ' '};\n        result = flipper_format_stream_write(stream, comment_buffer, sizeof(comment_buffer));\n        if(!result) break;\n\n        result = flipper_format_stream_write(stream, data, strlen(data));\n        if(!result) break;\n\n        result = flipper_format_stream_write_eol(stream);\n    } while(false);\n\n    return result;\n}\n"
  },
  {
    "path": "lib/flipper_format/flipper_format_stream.h",
    "content": "#pragma once\n#include <stdlib.h>\n#include <stdbool.h>\n#include <toolbox/stream/stream.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    FlipperStreamValueIgnore,\n    FlipperStreamValueStr,\n    FlipperStreamValueHex,\n    FlipperStreamValueFloat,\n    FlipperStreamValueInt32,\n    FlipperStreamValueUint32,\n    FlipperStreamValueHexUint64,\n    FlipperStreamValueBool,\n} FlipperStreamValue;\n\ntypedef struct {\n    const char* key;\n    FlipperStreamValue type;\n    const void* data;\n    size_t data_size;\n} FlipperStreamWriteData;\n\n/**\n * Writes a key/value pair to the stream.\n * @param stream \n * @param write_data \n * @return true \n * @return false \n */\nbool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteData* write_data);\n\n/**\n * Reads a value by key from a stream.\n * @param stream \n * @param key \n * @param type \n * @param _data \n * @param data_size \n * @param strict_mode \n * @return true \n * @return false \n */\nbool flipper_format_stream_read_value_line(\n    Stream* stream,\n    const char* key,\n    FlipperStreamValue type,\n    void* _data,\n    size_t data_size,\n    bool strict_mode);\n\n/**\n * Get the count of values by key from a stream.\n * @param stream \n * @param key \n * @param count \n * @param strict_mode \n * @return true \n * @return false \n */\nbool flipper_format_stream_get_value_count(\n    Stream* stream,\n    const char* key,\n    uint32_t* count,\n    bool strict_mode);\n\n/**\n * Removes a key and the corresponding value string from the stream and inserts a new key/value pair.\n * @param stream \n * @param write_data \n * @param strict_mode \n * @return true \n * @return false \n */\nbool flipper_format_stream_delete_key_and_write(\n    Stream* stream,\n    FlipperStreamWriteData* write_data,\n    bool strict_mode);\n\n/**\n * Writes a comment string to the stream.\n * @param stream \n * @param data \n * @return true \n * @return false \n */\nbool flipper_format_stream_write_comment_cstr(Stream* stream, const char* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/flipper_format/flipper_format_stream_i.h",
    "content": "#pragma once\n#include \"flipper_format_stream.h\"\n\nstatic const char flipper_format_delimiter = ':';\nstatic const char flipper_format_comment = '#';\nstatic const char flipper_format_eoln = '\\n';\nstatic const char flipper_format_eolr = '\\r';\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Write Flipper Format EOL to the stream\n * @param stream \n * @return true \n * @return false \n */\nbool flipper_format_stream_write_eol(Stream* stream);\n\n/**\n * Seek to the key from the current position of the stream.\n * Position will be at the beginning of the value corresponding to the key, if the key is found,, or at the end of the stream.\n * @param stream \n * @param key \n * @param strict_mode \n * @return true key is found\n * @return false key is not found\n */\nbool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/freertos.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/drivers\",\n        \"#/lib/FreeRTOS-Kernel/include\",\n        \"#/lib/FreeRTOS-Kernel/portable/GCC/ARM_CM4F\",\n        \"#/lib/FreeRTOS-glue\",\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"freertos\")\nlibenv.ApplyLibFlags()\n\n\nsources = libenv.Glob(\"FreeRTOS-Kernel/*.c\", source=True)\nsources += [\n    \"FreeRTOS-Kernel/portable/GCC/ARM_CM4F/port.c\",\n]\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/heatshrink.scons",
    "content": "from fbt.util import GLOB_FILE_EXCLUSION\n\nImport(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/heatshrink\",\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"heatshrink\")\nlibenv.ApplyLibFlags()\n\nsources = Glob(\n    \"heatshrink/heatshrink_*.c*\",\n    exclude=GLOB_FILE_EXCLUSION,\n    source=True,\n)\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/ibutton/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    CPPPATH=[\n        \"#/lib/ibutton\",\n    ],\n    SDK_HEADERS=[\n        File(\"ibutton_key.h\"),\n        File(\"ibutton_worker.h\"),\n        File(\"ibutton_protocols.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"ibutton\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/ibutton/ibutton_key.c",
    "content": "#include \"ibutton_key_i.h\"\n#include <furi.h>\n\nstruct iButtonKey {\n    iButtonProtocolId protocol_id;\n    iButtonProtocolData* protocol_data;\n    size_t protocol_data_size;\n};\n\niButtonKey* ibutton_key_alloc(size_t data_size) {\n    iButtonKey* key = malloc(sizeof(iButtonKey));\n\n    key->protocol_id = iButtonProtocolIdInvalid;\n    key->protocol_data = malloc(data_size);\n    key->protocol_data_size = data_size;\n\n    return key;\n}\n\nvoid ibutton_key_free(iButtonKey* key) {\n    furi_check(key);\n\n    free(key->protocol_data);\n    free(key);\n}\n\nvoid ibutton_key_reset(iButtonKey* key) {\n    furi_check(key);\n\n    key->protocol_id = iButtonProtocolIdInvalid;\n\n    memset(key->protocol_data, 0, key->protocol_data_size);\n}\n\niButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key) {\n    furi_check(key);\n\n    return key->protocol_id;\n}\n\nvoid ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id) {\n    furi_check(key);\n    key->protocol_id = protocol_id;\n}\n\niButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key) {\n    return key->protocol_data;\n}\n\nsize_t ibutton_key_get_protocol_data_size(const iButtonKey* key) {\n    return key->protocol_data_size;\n}\n"
  },
  {
    "path": "lib/ibutton/ibutton_key.h",
    "content": "/**\n * @file ibutton_key.h\n * \n * iButton key data holder\n */\n\n#pragma once\n\n#include <core/string.h>\n\n#include \"protocols/protocol_common.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct iButtonKey iButtonKey;\n\n/**\n * Allocate a key object\n * @param [in] data_size maximum data size held by the key\n * @return pointer to the key object\n */\niButtonKey* ibutton_key_alloc(size_t data_size);\n\n/**\n * Destroy the key object, free resources\n * @param [in] key pointer to the key object\n */\nvoid ibutton_key_free(iButtonKey* key);\n\n/**\n * Get the protocol id held by the key\n * @param [in] key pointer to the key object\n * @return protocol id held by the key\n */\niButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key);\n\n/**\n * Set the protocol id held by the key\n * @param [in] key pointer to the key object\n * @param [in] protocol_id new protocol id\n */\nvoid ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id);\n\n/**\n * Reset the protocol id and data held by the key\n * @param [in] key pointer to the key object\n */\nvoid ibutton_key_reset(iButtonKey* key);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ibutton/ibutton_key_i.h",
    "content": "#pragma once\n\n#include \"ibutton_key.h\"\n\n#include \"protocols/protocol_common_i.h\"\n\niButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key);\n\nsize_t ibutton_key_get_protocol_data_size(const iButtonKey* key);\n"
  },
  {
    "path": "lib/ibutton/ibutton_protocols.c",
    "content": "#include \"ibutton_protocols.h\"\n\n#include <storage/storage.h>\n\n#include \"ibutton_key_i.h\"\n\n#include \"protocols/protocol_group_defs.h\"\n\n#define IBUTTON_FILE_TYPE \"Flipper iButton key\"\n\n#define IBUTTON_PROTOCOL_KEY_V1 \"Key type\"\n#define IBUTTON_PROTOCOL_KEY_V2 \"Protocol\"\n\n#define IBUTTON_CURRENT_FORMAT_VERSION 2U\n\n#define GET_PROTOCOL_GROUP(id)     \\\n    iButtonProtocolGroupInfo info; \\\n    ibutton_protocols_get_group_by_id(protocols, (id), &info);\n\n#define GROUP_BASE  (info.base)\n#define GROUP_DATA  (info.group)\n#define PROTOCOL_ID (info.id)\n\nstruct iButtonProtocols {\n    iButtonProtocolGroupData** group_datas;\n};\n\ntypedef struct {\n    const iButtonProtocolGroupBase* base;\n    iButtonProtocolGroupData* group;\n    iButtonProtocolLocalId id;\n} iButtonProtocolGroupInfo;\n\nstatic void ibutton_protocols_get_group_by_id(\n    iButtonProtocols* protocols,\n    iButtonProtocolId id,\n    iButtonProtocolGroupInfo* info) {\n    iButtonProtocolLocalId local_id = id;\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        if(local_id < (signed)ibutton_protocol_groups[i]->protocol_count) {\n            info->base = ibutton_protocol_groups[i];\n            info->group = protocols->group_datas[i];\n            info->id = local_id;\n            return;\n\n        } else {\n            local_id -= ibutton_protocol_groups[i]->protocol_count;\n        }\n    }\n    furi_crash();\n}\n\niButtonProtocols* ibutton_protocols_alloc(void) {\n    iButtonProtocols* protocols = malloc(sizeof(iButtonProtocols*));\n\n    protocols->group_datas = malloc(sizeof(iButtonProtocolGroupData*) * iButtonProtocolGroupMax);\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        protocols->group_datas[i] = ibutton_protocol_groups[i]->alloc();\n    }\n\n    return protocols;\n}\n\nvoid ibutton_protocols_free(iButtonProtocols* protocols) {\n    furi_check(protocols);\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        ibutton_protocol_groups[i]->free(protocols->group_datas[i]);\n    }\n\n    free(protocols->group_datas);\n    free(protocols);\n}\n\nuint32_t ibutton_protocols_get_protocol_count(void) {\n    uint32_t count = 0;\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        count += ibutton_protocol_groups[i]->protocol_count;\n    }\n\n    return count;\n}\n\niButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name) {\n    furi_check(protocols);\n    furi_check(name);\n\n    iButtonProtocolLocalId offset = 0;\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        iButtonProtocolLocalId local_id;\n        if(ibutton_protocol_groups[i]->get_id_by_name(protocols->group_datas[i], &local_id, name)) {\n            return local_id + offset;\n        }\n        offset += ibutton_protocol_groups[i]->protocol_count;\n    }\n    return iButtonProtocolIdInvalid;\n}\n\nuint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id) {\n    furi_check(protocols);\n\n    GET_PROTOCOL_GROUP(id);\n\n    return GROUP_BASE->get_features(GROUP_DATA, PROTOCOL_ID);\n}\n\nsize_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols) {\n    furi_check(protocols);\n\n    size_t max_size = 0;\n\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        const size_t current_max_size =\n            ibutton_protocol_groups[i]->get_max_data_size(protocols->group_datas[i]);\n        if(current_max_size > max_size) {\n            max_size = current_max_size;\n        }\n    }\n\n    return max_size;\n}\n\nconst char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id) {\n    furi_check(protocols);\n\n    GET_PROTOCOL_GROUP(id);\n\n    return GROUP_BASE->get_manufacturer(GROUP_DATA, PROTOCOL_ID);\n}\n\nconst char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id) {\n    furi_check(protocols);\n\n    GET_PROTOCOL_GROUP(id);\n\n    return GROUP_BASE->get_name(GROUP_DATA, PROTOCOL_ID);\n}\n\nbool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    iButtonProtocolLocalId id = iButtonProtocolIdInvalid;\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    iButtonProtocolLocalId offset = 0;\n    for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {\n        if(ibutton_protocol_groups[i]->read(protocols->group_datas[i], data, &id)) {\n            id += offset;\n            break;\n        }\n        offset += ibutton_protocol_groups[i]->protocol_count;\n    }\n\n    ibutton_key_set_protocol_id(key, id);\n    return id != iButtonProtocolIdInvalid;\n}\n\nbool ibutton_protocols_write_id(iButtonProtocols* protocols, iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    return GROUP_BASE->write_id(GROUP_DATA, data, PROTOCOL_ID);\n}\n\nbool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    return GROUP_BASE->write_copy(GROUP_DATA, data, PROTOCOL_ID);\n}\n\nvoid ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->emulate_start(GROUP_DATA, data, PROTOCOL_ID);\n}\n\nvoid ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->emulate_stop(GROUP_DATA, data, PROTOCOL_ID);\n}\n\nbool ibutton_protocols_save(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    const char* file_name) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(file_name);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    bool success = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    do {\n        const char* protocol_name = ibutton_protocols_get_name(protocols, id);\n\n        if(!flipper_format_buffered_file_open_always(ff, file_name)) break;\n\n        if(!flipper_format_write_header_cstr(ff, IBUTTON_FILE_TYPE, IBUTTON_CURRENT_FORMAT_VERSION))\n            break;\n        if(!flipper_format_write_string_cstr(ff, IBUTTON_PROTOCOL_KEY_V2, protocol_name)) break;\n\n        GET_PROTOCOL_GROUP(id);\n        if(!GROUP_BASE->save(GROUP_DATA, data, PROTOCOL_ID, ff)) break;\n\n        success = true;\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return success;\n}\n\nbool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(file_name);\n\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    bool success = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n    FuriString* tmp = furi_string_alloc();\n\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, file_name)) break;\n\n        uint32_t version;\n\n        if(!flipper_format_read_header(ff, tmp, &version)) break;\n        if(!furi_string_equal(tmp, IBUTTON_FILE_TYPE)) break;\n\n        if(version == 1) {\n            if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V1, tmp)) break;\n        } else if(version == 2) {\n            if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V2, tmp)) break;\n        } else {\n            break;\n        }\n\n        const iButtonProtocolId id =\n            ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(tmp));\n        ibutton_key_set_protocol_id(key, id);\n\n        GET_PROTOCOL_GROUP(id);\n        if(!GROUP_BASE->load(GROUP_DATA, data, PROTOCOL_ID, version, ff)) break;\n\n        success = true;\n    } while(false);\n\n    flipper_format_free(ff);\n    furi_string_free(tmp);\n    furi_record_close(RECORD_STORAGE);\n\n    return success;\n}\n\nvoid ibutton_protocols_render_uid(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result) {\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->render_uid(GROUP_DATA, data, PROTOCOL_ID, result);\n}\n\nvoid ibutton_protocols_render_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(result);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->render_data(GROUP_DATA, data, PROTOCOL_ID, result);\n}\n\nvoid ibutton_protocols_render_brief_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(result);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->render_brief_data(GROUP_DATA, data, PROTOCOL_ID, result);\n}\n\nvoid ibutton_protocols_render_error(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(result);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->render_error(GROUP_DATA, data, PROTOCOL_ID, result);\n}\n\nbool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    return GROUP_BASE->is_valid(GROUP_DATA, data, PROTOCOL_ID);\n}\n\nvoid ibutton_protocols_get_editable_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    iButtonEditableData* editable) {\n    furi_check(protocols);\n    furi_check(key);\n    furi_check(editable);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->get_editable_data(GROUP_DATA, data, PROTOCOL_ID, editable);\n}\n\nvoid ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key) {\n    furi_check(protocols);\n    furi_check(key);\n\n    const iButtonProtocolId id = ibutton_key_get_protocol_id(key);\n    iButtonProtocolData* data = ibutton_key_get_protocol_data(key);\n\n    GET_PROTOCOL_GROUP(id);\n    GROUP_BASE->apply_edits(GROUP_DATA, data, PROTOCOL_ID);\n}\n"
  },
  {
    "path": "lib/ibutton/ibutton_protocols.h",
    "content": "/**\n * @file ibutton_protocols.h\n *\n * Common interface for accessing various iButton protocols\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#include \"protocols/protocol_common.h\"\n\n#include \"ibutton_key.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct iButtonProtocols iButtonProtocols;\n\n/**\n * Allocate an iButtonProtocols object\n * @return pointer to an iButtonProtocols object\n */\niButtonProtocols* ibutton_protocols_alloc(void);\n\n/**\n * Destroy an iButtonProtocols object, free resources\n * @param [in] protocols pointer to an iButtonProtocols object\n */\nvoid ibutton_protocols_free(iButtonProtocols* protocols);\n\n/**\n * Get the total number of available protocols\n */\nuint32_t ibutton_protocols_get_protocol_count(void);\n\n/**\n * Get maximum data size out of all protocols available\n * @param [in] protocols pointer to an iButtonProtocols object\n * @return maximum data size in bytes\n */\nsize_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols);\n\n/**\n * Get the protocol id based on its name\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] name pointer to a string containing the name\n * @return protocol id on success on iButtonProtocolIdInvalid on failure\n */\niButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name);\n\n/**\n * Get the manufacturer name based on the protocol id\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] id id of the protocol in question\n * @return pointer to a statically allocated string with manufacturer name\n */\nconst char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id);\n\n/**\n * Get the protocol name based on the protocol id\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] id id of the protocol in question\n * @return pointer to a statically allocated string with protocol name\n */\nconst char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id);\n\n/**\n * Get protocol features bitmask by protocol id\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] id id of the protocol in question\n */\nuint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id);\n\n/**\n * Read a physical device (a key or an emulator)\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [out] key pointer to the key to read into (must be allocated before)\n * @return true on success, false on failure\n */\nbool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key);\n\n/**\n * Write the key to a blank\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be written\n * @return true on success, false on failure\n */\nbool ibutton_protocols_write_id(iButtonProtocols* protocols, iButtonKey* key);\n\n/**\n * Write the key to another one of the same type\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be written\n * @return true on success, false on failure\n */\nbool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key);\n\n/**\n * Start emulating the key\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be emulated\n */\nvoid ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key);\n\n/**\n * Stop emulating the key\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be emulated\n */\nvoid ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key);\n\n/**\n * Save the key data to a file.\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be saved\n * @param [in] file_name full absolute path to the file name\n * @return true on success, false on failure\n */\nbool ibutton_protocols_save(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    const char* file_name);\n\n/**\n * Load the key from a file.\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [out] key pointer to the key to load into (must be allocated before)\n * @param [in] file_name full absolute path to the file name\n * @return true on success, false on failure\n */\nbool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name);\n\n/**\n * Format a string containing defice UID\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be rendered\n * @param [out] result pointer to the FuriString instance (must be initialized)\n */\nvoid ibutton_protocols_render_uid(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result);\n\n/**\n * Format a string containing device full data\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be rendered\n * @param [out] result pointer to the FuriString instance (must be initialized)\n */\nvoid ibutton_protocols_render_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result);\n\n/**\n * Format a string containing device brief data\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be rendered\n * @param [out] result pointer to the FuriString instance (must be initialized)\n */\nvoid ibutton_protocols_render_brief_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result);\n\n/**\n * Format a string containing error message (for invalid keys)\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be rendered\n * @param [out] result pointer to the FuriString instance (must be initialized)\n */\nvoid ibutton_protocols_render_error(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    FuriString* result);\n\n/**\n * Check whether the key data is valid\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be checked\n * @return true if data is valid, false otherwise\n */\nbool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key);\n\n/**\n * Get a pointer to the key's editable data (for in-place editing)\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in] key pointer to the key to be checked\n * @param [out] editable pointer to a structure to contain the editable data\n */\nvoid ibutton_protocols_get_editable_data(\n    iButtonProtocols* protocols,\n    const iButtonKey* key,\n    iButtonEditableData* editable);\n\n/**\n * Make all necessary internal adjustments after editing the key\n * @param [in] protocols pointer to an iButtonProtocols object\n * @param [in,out] key pointer to the key to be adjusted\n */\nvoid ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ibutton/ibutton_worker.c",
    "content": "#include \"ibutton_worker_i.h\"\n#include \"ibutton_protocols.h\"\n\n#include <core/check.h>\n\ntypedef enum {\n    iButtonMessageEnd,\n    iButtonMessageStop,\n    iButtonMessageRead,\n    iButtonMessageWriteId,\n    iButtonMessageWriteCopy,\n    iButtonMessageEmulate,\n    iButtonMessageNotifyEmulate,\n} iButtonMessageType;\n\ntypedef struct {\n    iButtonMessageType type;\n    union {\n        iButtonKey* key;\n    } data;\n} iButtonMessage;\n\nstatic int32_t ibutton_worker_thread(void* thread_context);\n\niButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols) {\n    furi_check(protocols);\n\n    iButtonWorker* worker = malloc(sizeof(iButtonWorker));\n\n    worker->protocols = protocols;\n    worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage));\n\n    worker->mode_index = iButtonWorkerModeIdle;\n    worker->thread = furi_thread_alloc_ex(\"iButtonWorker\", 2048, ibutton_worker_thread, worker);\n\n    return worker;\n}\n\nvoid ibutton_worker_read_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerReadCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == iButtonWorkerModeIdle);\n\n    worker->read_cb = callback;\n    worker->cb_ctx = context;\n}\n\nvoid ibutton_worker_write_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerWriteCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == iButtonWorkerModeIdle);\n\n    worker->write_cb = callback;\n    worker->cb_ctx = context;\n}\n\nvoid ibutton_worker_emulate_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerEmulateCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == iButtonWorkerModeIdle);\n\n    worker->emulate_cb = callback;\n    worker->cb_ctx = context;\n}\n\nvoid ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) {\n    furi_check(worker);\n\n    iButtonMessage message = {.type = iButtonMessageRead, .data.key = key};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid ibutton_worker_write_id_start(iButtonWorker* worker, iButtonKey* key) {\n    furi_check(worker);\n    furi_check(key);\n\n    iButtonMessage message = {.type = iButtonMessageWriteId, .data.key = key};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key) {\n    furi_check(worker);\n    furi_check(key);\n\n    iButtonMessage message = {.type = iButtonMessageWriteCopy, .data.key = key};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key) {\n    furi_check(worker);\n\n    iButtonMessage message = {.type = iButtonMessageEmulate, .data.key = key};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid ibutton_worker_stop(iButtonWorker* worker) {\n    furi_check(worker);\n\n    iButtonMessage message = {.type = iButtonMessageStop};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid ibutton_worker_free(iButtonWorker* worker) {\n    furi_check(worker);\n\n    furi_message_queue_free(worker->messages);\n    furi_thread_free(worker->thread);\n\n    free(worker);\n}\n\nvoid ibutton_worker_start_thread(iButtonWorker* worker) {\n    furi_check(worker);\n\n    furi_thread_start(worker->thread);\n}\n\nvoid ibutton_worker_stop_thread(iButtonWorker* worker) {\n    furi_check(worker);\n\n    iButtonMessage message = {.type = iButtonMessageEnd};\n\n    furi_check(\n        furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);\n\n    furi_thread_join(worker->thread);\n}\n\nvoid ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) {\n    ibutton_worker_modes[worker->mode_index].stop(worker);\n    worker->mode_index = mode;\n    ibutton_worker_modes[worker->mode_index].start(worker);\n}\n\nvoid ibutton_worker_notify_emulate(iButtonWorker* worker) {\n    iButtonMessage message = {.type = iButtonMessageNotifyEmulate};\n    // we're running in an interrupt context, so we can't wait\n    // and we can drop message if queue is full, that's ok for that message\n    furi_message_queue_put(worker->messages, &message, 0);\n}\n\nvoid ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) {\n    worker->key = key;\n}\n\nstatic int32_t ibutton_worker_thread(void* thread_context) {\n    iButtonWorker* worker = thread_context;\n    bool running = true;\n    iButtonMessage message;\n    FuriStatus status;\n\n    ibutton_worker_modes[worker->mode_index].start(worker);\n\n    while(running) {\n        status = furi_message_queue_get(\n            worker->messages, &message, ibutton_worker_modes[worker->mode_index].quant);\n        if(status == FuriStatusOk) {\n            switch(message.type) {\n            case iButtonMessageEnd:\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);\n                ibutton_worker_set_key_p(worker, NULL);\n                running = false;\n                break;\n            case iButtonMessageStop:\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);\n                ibutton_worker_set_key_p(worker, NULL);\n                break;\n            case iButtonMessageRead:\n                ibutton_worker_set_key_p(worker, message.data.key);\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeRead);\n                break;\n            case iButtonMessageWriteId:\n                ibutton_worker_set_key_p(worker, message.data.key);\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteId);\n                break;\n            case iButtonMessageWriteCopy:\n                ibutton_worker_set_key_p(worker, message.data.key);\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteCopy);\n                break;\n            case iButtonMessageEmulate:\n                ibutton_worker_set_key_p(worker, message.data.key);\n                ibutton_worker_switch_mode(worker, iButtonWorkerModeEmulate);\n                break;\n            case iButtonMessageNotifyEmulate:\n                if(worker->emulate_cb) {\n                    worker->emulate_cb(worker->cb_ctx, true);\n                }\n                break;\n            }\n        } else if(status == FuriStatusErrorTimeout) {\n            ibutton_worker_modes[worker->mode_index].tick(worker);\n        } else {\n            furi_crash(\"iButton worker error\");\n        }\n    }\n\n    ibutton_worker_modes[worker->mode_index].stop(worker);\n\n    return 0;\n}\n"
  },
  {
    "path": "lib/ibutton/ibutton_worker.h",
    "content": "/**\n * @file ibutton_worker.h\n * \n * iButton worker\n */\n\n#pragma once\n\n#include \"ibutton_key.h\"\n#include \"ibutton_protocols.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    iButtonWorkerWriteOK,\n    iButtonWorkerWriteSameKey,\n    iButtonWorkerWriteNoDetect,\n    iButtonWorkerWriteCannotWrite,\n} iButtonWorkerWriteResult;\n\ntypedef void (*iButtonWorkerReadCallback)(void* context);\ntypedef void (*iButtonWorkerWriteCallback)(void* context, iButtonWorkerWriteResult result);\ntypedef void (*iButtonWorkerEmulateCallback)(void* context, bool emulated);\n\ntypedef struct iButtonWorker iButtonWorker;\n\n/**\n * Allocate ibutton worker\n * @return iButtonWorker* \n */\niButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols);\n\n/**\n * Free ibutton worker\n * @param worker \n */\nvoid ibutton_worker_free(iButtonWorker* worker);\n\n/**\n * Start ibutton worker thread\n * @param worker \n */\nvoid ibutton_worker_start_thread(iButtonWorker* worker);\n\n/**\n * Stop ibutton worker thread\n * @param worker \n */\nvoid ibutton_worker_stop_thread(iButtonWorker* worker);\n\n/**\n * Set \"read success\" callback\n * @param worker \n * @param callback \n * @param context \n */\nvoid ibutton_worker_read_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerReadCallback callback,\n    void* context);\n\n/**\n * Start read mode\n * @param worker \n * @param key \n */\nvoid ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key);\n\n/**\n * Set \"write event\" callback\n * @param worker \n * @param callback \n * @param context \n */\nvoid ibutton_worker_write_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerWriteCallback callback,\n    void* context);\n\n/**\n * Start write blank mode\n * @param worker \n * @param key \n */\nvoid ibutton_worker_write_id_start(iButtonWorker* worker, iButtonKey* key);\n\n/**\n * Start write copy mode\n * @param worker\n * @param key\n */\nvoid ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key);\n\n/**\n * Set \"emulate success\" callback\n * @param worker \n * @param callback \n * @param context \n */\nvoid ibutton_worker_emulate_set_callback(\n    iButtonWorker* worker,\n    iButtonWorkerEmulateCallback callback,\n    void* context);\n\n/**\n * Start emulate mode\n * @param worker \n * @param key \n */\nvoid ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key);\n\n/**\n * Stop all modes\n * @param worker \n */\nvoid ibutton_worker_stop(iButtonWorker* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ibutton/ibutton_worker_i.h",
    "content": "/**\n * @file ibutton_worker_i.h\n * \n * iButton worker, internal definitions \n */\n\n#pragma once\n\n#include <core/thread.h>\n#include <core/message_queue.h>\n\n#include \"ibutton_worker.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    const uint32_t quant;\n    void (*const start)(iButtonWorker* worker);\n    void (*const tick)(iButtonWorker* worker);\n    void (*const stop)(iButtonWorker* worker);\n} iButtonWorkerModeType;\n\ntypedef enum {\n    iButtonWorkerModeIdle,\n    iButtonWorkerModeRead,\n    iButtonWorkerModeWriteId,\n    iButtonWorkerModeWriteCopy,\n    iButtonWorkerModeEmulate,\n} iButtonWorkerMode;\n\nstruct iButtonWorker {\n    iButtonKey* key;\n    iButtonProtocols* protocols;\n    iButtonWorkerMode mode_index;\n    FuriMessageQueue* messages;\n    FuriThread* thread;\n\n    iButtonWorkerReadCallback read_cb;\n    iButtonWorkerWriteCallback write_cb;\n    iButtonWorkerEmulateCallback emulate_cb;\n\n    void* cb_ctx;\n};\n\nextern const iButtonWorkerModeType ibutton_worker_modes[];\n\nvoid ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode);\nvoid ibutton_worker_notify_emulate(iButtonWorker* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/ibutton/ibutton_worker_modes.c",
    "content": "#include \"ibutton_worker_i.h\"\n\n#include <core/check.h>\n#include <core/record.h>\n\n#include <furi_hal_rfid.h>\n#include <power/power_service/power.h>\n\n#include \"ibutton_protocols.h\"\n\nstatic void ibutton_worker_mode_idle_start(iButtonWorker* worker);\nstatic void ibutton_worker_mode_idle_tick(iButtonWorker* worker);\nstatic void ibutton_worker_mode_idle_stop(iButtonWorker* worker);\n\nstatic void ibutton_worker_mode_emulate_start(iButtonWorker* worker);\nstatic void ibutton_worker_mode_emulate_tick(iButtonWorker* worker);\nstatic void ibutton_worker_mode_emulate_stop(iButtonWorker* worker);\n\nstatic void ibutton_worker_mode_read_start(iButtonWorker* worker);\nstatic void ibutton_worker_mode_read_tick(iButtonWorker* worker);\nstatic void ibutton_worker_mode_read_stop(iButtonWorker* worker);\n\nstatic void ibutton_worker_mode_write_common_start(iButtonWorker* worker);\nstatic void ibutton_worker_mode_write_id_tick(iButtonWorker* worker);\nstatic void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker);\nstatic void ibutton_worker_mode_write_common_stop(iButtonWorker* worker);\n\nconst iButtonWorkerModeType ibutton_worker_modes[] = {\n    {\n        .quant = FuriWaitForever,\n        .start = ibutton_worker_mode_idle_start,\n        .tick = ibutton_worker_mode_idle_tick,\n        .stop = ibutton_worker_mode_idle_stop,\n    },\n    {\n        .quant = 100,\n        .start = ibutton_worker_mode_read_start,\n        .tick = ibutton_worker_mode_read_tick,\n        .stop = ibutton_worker_mode_read_stop,\n    },\n    {\n        .quant = 1000,\n        .start = ibutton_worker_mode_write_common_start,\n        .tick = ibutton_worker_mode_write_id_tick,\n        .stop = ibutton_worker_mode_write_common_stop,\n    },\n    {\n        .quant = 1000,\n        .start = ibutton_worker_mode_write_common_start,\n        .tick = ibutton_worker_mode_write_copy_tick,\n        .stop = ibutton_worker_mode_write_common_stop,\n    },\n    {\n        .quant = 1000,\n        .start = ibutton_worker_mode_emulate_start,\n        .tick = ibutton_worker_mode_emulate_tick,\n        .stop = ibutton_worker_mode_emulate_stop,\n    },\n};\n\n/*********************** IDLE ***********************/\n\nvoid ibutton_worker_mode_idle_start(iButtonWorker* worker) {\n    UNUSED(worker);\n}\n\nvoid ibutton_worker_mode_idle_tick(iButtonWorker* worker) {\n    UNUSED(worker);\n}\n\nvoid ibutton_worker_mode_idle_stop(iButtonWorker* worker) {\n    UNUSED(worker);\n}\n\n/*********************** READ ***********************/\n\nvoid ibutton_worker_mode_read_start(iButtonWorker* worker) {\n    UNUSED(worker);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, true);\n    furi_record_close(RECORD_POWER);\n}\n\nvoid ibutton_worker_mode_read_tick(iButtonWorker* worker) {\n    if(ibutton_protocols_read(worker->protocols, worker->key)) {\n        if(worker->read_cb != NULL) {\n            worker->read_cb(worker->cb_ctx);\n        }\n\n        ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);\n    }\n}\n\nvoid ibutton_worker_mode_read_stop(iButtonWorker* worker) {\n    UNUSED(worker);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, false);\n    furi_record_close(RECORD_POWER);\n}\n\n/*********************** EMULATE ***********************/\n\nvoid ibutton_worker_mode_emulate_start(iButtonWorker* worker) {\n    furi_assert(worker->key);\n\n    furi_hal_rfid_pins_reset();\n    furi_hal_rfid_pin_pull_pulldown();\n\n    ibutton_protocols_emulate_start(worker->protocols, worker->key);\n}\n\nvoid ibutton_worker_mode_emulate_tick(iButtonWorker* worker) {\n    UNUSED(worker);\n}\n\nvoid ibutton_worker_mode_emulate_stop(iButtonWorker* worker) {\n    furi_assert(worker->key);\n\n    ibutton_protocols_emulate_stop(worker->protocols, worker->key);\n\n    furi_hal_rfid_pins_reset();\n}\n\n/*********************** WRITE ***********************/\n\nvoid ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524\n    UNUSED(worker);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, true);\n    furi_record_close(RECORD_POWER);\n}\n\nvoid ibutton_worker_mode_write_id_tick(iButtonWorker* worker) {\n    furi_assert(worker->key);\n\n    const bool success = ibutton_protocols_write_id(worker->protocols, worker->key);\n    // TODO FL-3527: pass a proper result to the callback\n    const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK :\n                                                      iButtonWorkerWriteNoDetect;\n    if(worker->write_cb != NULL) {\n        worker->write_cb(worker->cb_ctx, result);\n    }\n}\n\nvoid ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) {\n    furi_assert(worker->key);\n\n    const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key);\n    // TODO FL-3527: pass a proper result to the callback\n    const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK :\n                                                      iButtonWorkerWriteNoDetect;\n    if(worker->write_cb != NULL) {\n        worker->write_cb(worker->cb_ctx, result);\n    }\n}\n\nvoid ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524\n    UNUSED(worker);\n    Power* power = furi_record_open(RECORD_POWER);\n    power_enable_otg(power, false);\n    furi_record_close(RECORD_POWER);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/rw1990.c",
    "content": "#include \"rw1990.h\"\n\n#include <core/kernel.h>\n\n#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1\n#define RW1990_1_CMD_READ_RECORD_FLAG  0xB5\n#define RW1990_1_CMD_WRITE_ROM         0xD5\n\n#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D\n#define RW1990_2_CMD_READ_RECORD_FLAG  0x1E\n#define RW1990_2_CMD_WRITE_ROM         0xD5\n\n#define DS1990_CMD_READ_ROM 0x33\n\nstatic void rw1990_write_byte(OneWireHost* host, uint8_t value) {\n    for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {\n        onewire_host_write_bit(host, (bool)(bitMask & value));\n        furi_delay_us(5000);\n    }\n}\n\nstatic bool rw1990_read_and_compare(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    bool success = false;\n\n    if(onewire_host_reset(host)) {\n        success = true;\n        onewire_host_write(host, DS1990_CMD_READ_ROM);\n\n        for(size_t i = 0; i < data_size; ++i) {\n            if(data[i] != onewire_host_read(host)) {\n                success = false;\n                break;\n            }\n        }\n    }\n\n    return success;\n}\n\nbool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    onewire_host_set_timings_default(host);\n\n    // Unlock sequence\n    onewire_host_reset(host);\n    onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG);\n    furi_delay_us(10);\n\n    onewire_host_write_bit(host, false);\n    furi_delay_us(5000);\n\n    // Write data\n    onewire_host_reset(host);\n    onewire_host_write(host, RW1990_1_CMD_WRITE_ROM);\n\n    for(size_t i = 0; i < data_size; ++i) {\n        // inverted key for RW1990.1\n        rw1990_write_byte(host, ~(data[i]));\n        furi_delay_us(30000);\n    }\n\n    // Lock sequence\n    onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG);\n\n    onewire_host_write_bit(host, true);\n    furi_delay_us(10000);\n\n    // TODO FL-3528: Better error handling\n    return rw1990_read_and_compare(host, data, data_size);\n}\n\nbool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    onewire_host_set_timings_default(host);\n\n    // Unlock sequence\n    onewire_host_reset(host);\n    onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG);\n    furi_delay_us(10);\n\n    onewire_host_write_bit(host, true);\n    furi_delay_us(5000);\n\n    // Write data\n    onewire_host_reset(host);\n    onewire_host_write(host, RW1990_2_CMD_WRITE_ROM);\n\n    for(size_t i = 0; i < data_size; ++i) {\n        rw1990_write_byte(host, data[i]);\n        furi_delay_us(30000);\n    }\n\n    // Lock sequence\n    onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG);\n\n    onewire_host_write_bit(host, false);\n    furi_delay_us(10000);\n\n    // TODO Fl-3528: Better error handling\n    return rw1990_read_and_compare(host, data, data_size);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/rw1990.h",
    "content": "#pragma once\n\n#include <stddef.h>\n\n#include <one_wire/one_wire_host.h>\n\nbool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size);\n\nbool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size);\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/tm01x.c",
    "content": "#include <core/kernel.h>\n#include <one_wire/one_wire_host.h>\n#include <one_wire/maxim_crc.h>\n#include \"tm01x.h\"\n\n// Commands for TM01x\n#define TM01X_CMD_WRITE_FLAG 0xC1\n#define TM01X_CMD_WRITE_ROM  0xC5\n#define TM01X_CMD_READ_ROM   0x33\n\n#define TM01X_CMD_FINALIZE_CYFRAL  0xCA\n#define TM01X_CMD_FINALIZE_METAKOM 0xCB\n\nstatic void tm01x_write_byte(OneWireHost* host, uint8_t value) {\n    for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {\n        onewire_host_write_bit(host, (bool)(bitMask & value));\n        furi_delay_us(5000); // 5ms pause after each bit\n    }\n}\n\n// Helper function to read and verify written data\nstatic bool tm01x_read_and_verify(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    bool success = false;\n\n    if(onewire_host_reset(host)) {\n        success = true;\n        onewire_host_write(host, TM01X_CMD_READ_ROM);\n\n        for(size_t i = 0; i < data_size; ++i) {\n            if(data[i] != onewire_host_read(host)) {\n                success = false;\n                break;\n            }\n        }\n    }\n\n    return success;\n}\n\nbool tm01x_write_dallas(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    // Set TM01x specific timings\n    onewire_host_set_timings_tm01x(host);\n\n    // Write sequence\n    onewire_host_reset(host);\n    onewire_host_write(host, TM01X_CMD_WRITE_FLAG);\n    onewire_host_write_bit(host, true);\n    furi_delay_us(5000);\n\n    onewire_host_reset(host);\n    onewire_host_write(host, TM01X_CMD_WRITE_ROM);\n\n    for(size_t i = 0; i < data_size; ++i) {\n        tm01x_write_byte(host, data[i]);\n    }\n\n    return tm01x_read_and_verify(host, data, data_size);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/tm01x.h",
    "content": "#pragma once\n\n#include <core/kernel.h>\n#include <one_wire/one_wire_host.h>\n\nbool tm01x_write_dallas(OneWireHost* host, const uint8_t* data, size_t data_size);\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/tm2004.c",
    "content": "#include \"tm2004.h\"\n\n#include <core/kernel.h>\n\n#define TM2004_CMD_READ_STATUS    0xAA\n#define TM2004_CMD_READ_MEMORY    0xF0\n#define TM2004_CMD_WRITE_ROM      0x3C\n#define TM2004_CMD_FINALIZATION   0x35\n#define TM2004_ANSWER_READ_MEMORY 0xF5\n\nbool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) {\n    onewire_host_set_timings_default(host);\n\n    onewire_host_reset(host);\n    onewire_host_write(host, TM2004_CMD_WRITE_ROM);\n    // Starting writing from address 0x0000\n    onewire_host_write(host, 0x00);\n    onewire_host_write(host, 0x00);\n\n    size_t i;\n    for(i = 0; i < data_size; ++i) {\n        uint8_t answer;\n\n        onewire_host_write(host, data[i]);\n        answer = onewire_host_read(host);\n        // TODO FL-3529: check answer CRC\n\n        // pulse indicating that data is correct\n        furi_delay_us(600);\n        onewire_host_write_bit(host, true);\n        furi_delay_us(50000);\n\n        // read written key byte\n        answer = onewire_host_read(host); //-V519\n\n        // check that written and read are same\n        if(data[i] != answer) {\n            break;\n        }\n    }\n\n    // TODO FL-3529: Better error handling\n    return i == data_size;\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/blanks/tm2004.h",
    "content": "#pragma once\n\n#include <stddef.h>\n\n#include <one_wire/one_wire_host.h>\n\nbool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size);\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/dallas_common.c",
    "content": "#include \"dallas_common.h\"\n\n#include <core/common_defines.h>\n#include <one_wire/maxim_crc.h>\n\n#define BITS_IN_BYTE 8U\n\n#define DALLAS_COMMON_ROM_DATA_KEY_V1 \"Data\"\n#define DALLAS_COMMON_ROM_DATA_KEY_V2 \"Rom Data\"\n\n#define DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US 5U\n#define DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT     20U\n\n#define DALLAS_COMMON_END_ADDRESS_MASK 0x01F\n#define DALLAS_COMMON_STATUS_FLAG_PF   (1U << 5)\n#define DALLAS_COMMON_STATUS_FLAG_OF   (1U << 6)\n#define DALLAS_COMMON_STATUS_FLAG_AA   (1U << 7)\n\n#define DALLAS_COMMON_BRIEF_HEAD_COUNT 4U\n#define DALLAS_COMMON_BRIEF_TAIL_COUNT 3U\n\n#define BITS_IN_BYTE 8U\n#define BITS_IN_KBIT 1024U\n#define BITS_IN_MBIT (BITS_IN_KBIT * 1024U)\n\nbool dallas_common_skip_rom(OneWireHost* host) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);\n    return true;\n}\n\nbool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_READ_ROM);\n    onewire_host_read_bytes(host, rom_data->bytes, sizeof(DallasCommonRomData));\n\n    return dallas_common_is_valid_crc(rom_data);\n}\n\nbool dallas_common_write_scratchpad(\n    OneWireHost* host,\n    uint16_t address,\n    const uint8_t* data,\n    size_t data_size) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);\n    onewire_host_write(host, (uint8_t)address);\n    onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE));\n\n    onewire_host_write_bytes(host, data, data_size);\n\n    return true;\n}\n\nbool dallas_common_read_scratchpad(\n    OneWireHost* host,\n    DallasCommonAddressRegs* regs,\n    uint8_t* data,\n    size_t data_size) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH);\n    onewire_host_read_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs));\n    onewire_host_read_bytes(host, data, data_size);\n\n    return true;\n}\n\nbool dallas_common_copy_scratchpad(\n    OneWireHost* host,\n    const DallasCommonAddressRegs* regs,\n    uint32_t timeout_us) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH);\n    onewire_host_write_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs));\n\n    const uint32_t poll_delay =\n        MAX(timeout_us / DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT,\n            DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US);\n\n    uint32_t time_elapsed;\n    for(time_elapsed = 0; time_elapsed < timeout_us; time_elapsed += poll_delay) {\n        if(!onewire_host_read_bit(host)) break;\n        furi_delay_us(poll_delay);\n    }\n\n    return time_elapsed < timeout_us;\n}\n\nbool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM);\n\n    onewire_host_write(host, (uint8_t)address);\n    onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE));\n\n    onewire_host_read_bytes(host, data, (uint16_t)data_size);\n\n    return true;\n}\n\nbool dallas_common_write_mem(\n    OneWireHost* host,\n    uint32_t timeout_us,\n    size_t page_size,\n    const uint8_t* data,\n    size_t data_size) {\n    // Data size must be a multiple of page size\n    furi_check(data_size % page_size == 0);\n\n    DallasCommonAddressRegs regs;\n    uint8_t* scratch = malloc(page_size);\n\n    size_t i;\n    for(i = 0; i < data_size; i += page_size) {\n        const uint8_t* data_ptr = data + i;\n\n        // Write scratchpad with the next page value\n        if(!onewire_host_reset(host)) break;\n        if(!dallas_common_skip_rom(host)) break;\n        if(!dallas_common_write_scratchpad(host, i, data_ptr, page_size)) break;\n\n        // Read back the scratchpad contents and address registers\n        if(!onewire_host_reset(host)) break;\n        if(!dallas_common_skip_rom(host)) break;\n        if(!dallas_common_read_scratchpad(host, &regs, scratch, page_size)) break;\n\n        // Verify scratchpad contents\n        if(memcmp(data_ptr, scratch, page_size) != 0) break;\n\n        // Write scratchpad to internal memory\n        if(!onewire_host_reset(host)) break;\n        if(!dallas_common_skip_rom(host)) break;\n        if(!dallas_common_copy_scratchpad(host, &regs, timeout_us)) break;\n\n        // Read back the address registers again\n        if(!onewire_host_reset(host)) break;\n        if(!dallas_common_skip_rom(host)) break;\n        if(!dallas_common_read_scratchpad(host, &regs, scratch, 0)) break;\n\n        // Check if AA flag is set\n        if(!(regs.fields.status & DALLAS_COMMON_STATUS_FLAG_AA)) break;\n    }\n\n    free(scratch);\n\n    return i == data_size;\n}\n\nbool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) {\n    for(size_t i = 0; i < sizeof(DallasCommonRomData); i++) {\n        for(size_t j = 0; j < BITS_IN_BYTE; j++) {\n            bool bit = (rom_data->bytes[i] >> j) & 0x01;\n\n            if(!onewire_slave_send_bit(bus, bit)) return false;\n            if(!onewire_slave_send_bit(bus, !bit)) return false;\n\n            onewire_slave_receive_bit(bus);\n            // TODO FL-3530: check for errors and return if any\n        }\n    }\n\n    return true;\n}\n\nbool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) {\n    return onewire_slave_send(bus, rom_data->bytes, sizeof(DallasCommonRomData));\n}\n\nbool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) {\n    bool success = false;\n\n    union {\n        uint8_t bytes[sizeof(uint16_t)];\n        uint16_t word;\n    } address;\n\n    do {\n        if(!onewire_slave_receive(bus, address.bytes, sizeof(address))) break;\n        if(address.word >= data_size) break;\n        if(!onewire_slave_send(bus, data + address.word, data_size - address.word)) break;\n\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data) {\n    return flipper_format_write_hex(\n        ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData));\n}\n\nbool dallas_common_load_rom_data(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    DallasCommonRomData* rom_data) {\n    switch(format_version) {\n    case 1:\n        return flipper_format_read_hex(\n            ff, DALLAS_COMMON_ROM_DATA_KEY_V1, rom_data->bytes, sizeof(DallasCommonRomData));\n    case 2:\n        return flipper_format_read_hex(\n            ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData));\n    default:\n        return false;\n    }\n}\n\nbool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data) {\n    const uint8_t crc_calculated =\n        maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT);\n    const uint8_t crc_received = rom_data->fields.checksum;\n\n    return crc_calculated == crc_received;\n}\n\nvoid dallas_common_render_uid(FuriString* result, const DallasCommonRomData* rom_data) {\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) {\n        furi_string_cat_printf(result, \"%02X \", rom_data->bytes[i]);\n    }\n}\n\nvoid dallas_common_render_brief_data(\n    FuriString* result,\n    const DallasCommonRomData* rom_data,\n    const uint8_t* mem_data,\n    size_t mem_size,\n    const char* mem_name) {\n    UNUSED(mem_data);\n\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) {\n        furi_string_cat_printf(result, \"%02X \", rom_data->bytes[i]);\n    }\n    furi_string_cat_printf(result, \"\\nFamily Code: %02X\\n\", rom_data->bytes[0]);\n\n    const char* size_prefix = \"\";\n    size_t mem_size_bits = mem_size * BITS_IN_BYTE;\n\n    if(mem_size_bits >= BITS_IN_MBIT) {\n        size_prefix = \"M\";\n        mem_size_bits /= BITS_IN_MBIT;\n    } else if(mem_size_bits >= BITS_IN_KBIT) {\n        size_prefix = \"K\";\n        mem_size_bits /= BITS_IN_KBIT;\n    }\n\n    furi_string_cat_printf(result, \"%s: %zu %sbit\\n\", mem_name, mem_size_bits, size_prefix);\n}\n\nvoid dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data) {\n    furi_string_set(result, \"\\e#CRC Error\\e#\\n\");\n\n    const size_t data_size = sizeof(DallasCommonRomData);\n\n    for(size_t i = 0; i < data_size; ++i) {\n        furi_string_cat_printf(\n            result, (i < data_size - 1) ? \"%02X \" : \"\\e!%02X\\e!\", rom_data->bytes[i]);\n    }\n\n    furi_string_cat_printf(\n        result,\n        \"\\nExpected CRC: \\e!%02X\\e!\",\n        maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT));\n}\n\nvoid dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code) {\n    rom_data->fields.family_code = family_code;\n    const uint8_t crc =\n        maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT);\n    rom_data->fields.checksum = crc;\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/dallas_common.h",
    "content": "#pragma once\n\n#include <one_wire/one_wire_host.h>\n#include <one_wire/one_wire_slave.h>\n\n#include <flipper_format/flipper_format.h>\n\n#define DALLAS_COMMON_MANUFACTURER_NAME \"Dallas\"\n\n#define DALLAS_COMMON_CMD_READ_ROM    0x33U\n#define DALLAS_COMMON_CMD_MATCH_ROM   0x55U\n#define DALLAS_COMMON_CMD_SKIP_ROM    0xCCU\n#define DALLAS_COMMON_CMD_COND_SEARCH 0xECU\n#define DALLAS_COMMON_CMD_SEARCH_ROM  0xF0U\n\n#define DALLAS_COMMON_CMD_READ_SCRATCH  0xAAU\n#define DALLAS_COMMON_CMD_WRITE_SCRATCH 0x0FU\n#define DALLAS_COMMON_CMD_COPY_SCRATCH  0x55U\n\n#define DALLAS_COMMON_CMD_READ_MEM 0xF0U\n\n#define DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM  0x3CU\n#define DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM 0x69U\n\ntypedef enum {\n    DallasCommonCommandStateIdle,\n    DallasCommonCommandStateRomCmd,\n    DallasCommonCommandStateMemCmd,\n} DallasCommonCommandState;\n\ntypedef union {\n    struct {\n        uint8_t family_code;\n        uint8_t serial_number[6];\n        uint8_t checksum;\n    } fields;\n    uint8_t bytes[8];\n} DallasCommonRomData;\n\ntypedef union {\n    struct {\n        uint8_t address_lo;\n        uint8_t address_hi;\n        uint8_t status;\n    } fields;\n    uint8_t bytes[3];\n} DallasCommonAddressRegs;\n\n/* Standard(ish) iButton commands */\nbool dallas_common_skip_rom(OneWireHost* host);\n\nbool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data);\n\nbool dallas_common_write_scratchpad(\n    OneWireHost* host,\n    uint16_t address,\n    const uint8_t* data,\n    size_t data_size);\n\nbool dallas_common_read_scratchpad(\n    OneWireHost* host,\n    DallasCommonAddressRegs* regs,\n    uint8_t* data,\n    size_t data_size);\n\nbool dallas_common_copy_scratchpad(\n    OneWireHost* host,\n    const DallasCommonAddressRegs* regs,\n    uint32_t timeout_us);\n\nbool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size);\n\n/* Combined operations */\nbool dallas_common_write_mem(\n    OneWireHost* host,\n    uint32_t timeout_us,\n    size_t page_size,\n    const uint8_t* data,\n    size_t data_size);\n\n/* Emulation */\nbool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data);\n\nbool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data);\n\nbool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size);\n\n/* Save & Load */\nbool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data);\n\nbool dallas_common_load_rom_data(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    DallasCommonRomData* rom_data);\n\n/* Miscellaneous */\nbool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data);\n\nvoid dallas_common_render_uid(FuriString* result, const DallasCommonRomData* rom_data);\n\nvoid dallas_common_render_brief_data(\n    FuriString* result,\n    const DallasCommonRomData* rom_data,\n    const uint8_t* mem_data,\n    size_t mem_size,\n    const char* mem_name);\n\nvoid dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data);\n\nvoid dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code);\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_dallas_base.h",
    "content": "#pragma once\n\n#include \"../protocol_common_i.h\"\n\n#include <one_wire/one_wire_host.h>\n#include <one_wire/one_wire_slave.h>\n\n#include <flipper_format/flipper_format.h>\n\ntypedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*);\ntypedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*);\ntypedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*);\ntypedef bool (*iButtonProtocolDallasLoadFunc)(FlipperFormat*, uint32_t, iButtonProtocolData*);\ntypedef void (*iButtonProtocolDallasRenderDataFunc)(FuriString*, const iButtonProtocolData*);\ntypedef bool (*iButtonProtocolDallasIsValidFunc)(const iButtonProtocolData*);\ntypedef void (\n    *iButtonProtocolDallasGetEditableDataFunc)(iButtonEditableData*, iButtonProtocolData*);\ntypedef void (*iButtonProtocolDallasApplyEditsFunc)(iButtonProtocolData*);\n\ntypedef struct {\n    const uint8_t family_code;\n    const uint32_t features;\n    const size_t data_size;\n    const char* manufacturer;\n    const char* name;\n\n    iButtonProtocolDallasReadWriteFunc read;\n    iButtonProtocolDallasReadWriteFunc write_id;\n    iButtonProtocolDallasReadWriteFunc write_copy;\n    iButtonProtocolDallasEmulateFunc emulate;\n    iButtonProtocolDallasSaveFunc save;\n    iButtonProtocolDallasLoadFunc load;\n    iButtonProtocolDallasRenderDataFunc render_uid;\n    iButtonProtocolDallasRenderDataFunc render_data;\n    iButtonProtocolDallasRenderDataFunc render_brief_data;\n    iButtonProtocolDallasRenderDataFunc render_error;\n    iButtonProtocolDallasIsValidFunc is_valid;\n    iButtonProtocolDallasGetEditableDataFunc get_editable_data;\n    iButtonProtocolDallasApplyEditsFunc apply_edits;\n} iButtonProtocolDallasBase;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1971.c",
    "content": "#include \"protocol_ds1971.h\"\n\n#include <core/core_defines.h>\n#include <toolbox/pretty_format.h>\n\n#include \"dallas_common.h\"\n\n#include \"../blanks/tm2004.h\"\n\n#define DS1971_FAMILY_CODE 0x14U\n#define DS1971_FAMILY_NAME \"DS1971\"\n\n#define DS1971_EEPROM_DATA_SIZE      32U\n#define DS1971_SRAM_PAGE_SIZE        32U\n#define DS1971_COPY_SCRATCH_DELAY_US 250U\n\n#define DS1971_DATA_BYTE_COUNT 4U\n\n#define DS1971_EEPROM_DATA_KEY \"Eeprom Data\"\n#define DS1971_MEMORY_TYPE     \"EEPROM\"\n\n#define DS1971_CMD_FINALIZATION 0xA5\n\ntypedef struct {\n    OneWireSlave* bus;\n    DallasCommonCommandState command_state;\n} DS1971ProtocolState;\n\ntypedef struct {\n    DallasCommonRomData rom_data;\n    uint8_t eeprom_data[DS1971_EEPROM_DATA_SIZE];\n    DS1971ProtocolState state;\n} DS1971ProtocolData;\n\nstatic bool dallas_ds1971_read(OneWireHost*, void*);\nstatic bool dallas_ds1971_write_id(OneWireHost*, iButtonProtocolData*);\nstatic bool dallas_ds1971_write_copy(OneWireHost*, iButtonProtocolData*);\nstatic void dallas_ds1971_emulate(OneWireSlave*, iButtonProtocolData*);\nstatic bool dallas_ds1971_load(FlipperFormat*, uint32_t, iButtonProtocolData*);\nstatic bool dallas_ds1971_save(FlipperFormat*, const iButtonProtocolData*);\nstatic void dallas_ds1971_render_uid(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1971_render_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1971_render_brief_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1971_render_error(FuriString*, const iButtonProtocolData*);\nstatic bool dallas_ds1971_is_data_valid(const iButtonProtocolData*);\nstatic void dallas_ds1971_get_editable_data(iButtonEditableData*, iButtonProtocolData*);\nstatic void dallas_ds1971_apply_edits(iButtonProtocolData*);\nstatic bool\n    dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size);\nstatic bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size);\n\nconst iButtonProtocolDallasBase ibutton_protocol_ds1971 = {\n    .family_code = DS1971_FAMILY_CODE,\n    .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteId |\n                iButtonProtocolFeatureWriteCopy,\n    .data_size = sizeof(DS1971ProtocolData),\n    .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,\n    .name = DS1971_FAMILY_NAME,\n\n    .read = dallas_ds1971_read,\n    .write_id = dallas_ds1971_write_id,\n    .write_copy = dallas_ds1971_write_copy,\n    .emulate = dallas_ds1971_emulate,\n    .save = dallas_ds1971_save,\n    .load = dallas_ds1971_load,\n    .render_uid = dallas_ds1971_render_uid,\n    .render_data = dallas_ds1971_render_data,\n    .render_brief_data = dallas_ds1971_render_brief_data,\n    .render_error = dallas_ds1971_render_error,\n    .is_valid = dallas_ds1971_is_data_valid,\n    .get_editable_data = dallas_ds1971_get_editable_data,\n    .apply_edits = dallas_ds1971_apply_edits,\n};\n\nbool dallas_ds1971_read(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) &&\n           dallas_ds1971_read_mem(host, 0, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);\n}\n\nbool dallas_ds1971_write_id(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));\n}\n\nbool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n\n    onewire_host_reset(host);\n    onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);\n    // Starting writing from address 0x0000\n    onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);\n    onewire_host_write(host, 0x00);\n    // Write data to scratchpad\n    onewire_host_write_bytes(host, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);\n\n    // Read data from scratchpad and verify\n    bool pad_valid = false;\n    if(onewire_host_reset(host)) {\n        pad_valid = true;\n        onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);\n        onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH);\n        onewire_host_write(host, 0x00);\n\n        for(size_t i = 0; i < DS1971_EEPROM_DATA_SIZE; ++i) {\n            uint8_t scratch = onewire_host_read(host);\n            if(data->eeprom_data[i] != scratch) {\n                pad_valid = false;\n                break;\n            }\n        }\n    }\n\n    // Copy scratchpad to memory and confirm\n    if(pad_valid) {\n        if(onewire_host_reset(host)) {\n            onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);\n            onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH);\n            onewire_host_write(host, DS1971_CMD_FINALIZATION);\n\n            furi_delay_us(DS1971_COPY_SCRATCH_DELAY_US);\n        }\n    }\n\n    return pad_valid;\n}\n\nstatic bool dallas_ds1971_reset_callback(bool is_short, void* context) {\n    furi_assert(context);\n    DS1971ProtocolData* data = context;\n\n    if(!is_short) {\n        data->state.command_state = DallasCommonCommandStateIdle;\n        onewire_slave_set_overdrive(data->state.bus, is_short);\n    }\n\n    return !is_short;\n}\n\nstatic bool dallas_ds1971_command_callback(uint8_t command, void* context) {\n    furi_assert(context);\n    DS1971ProtocolData* data = context;\n    OneWireSlave* bus = data->state.bus;\n\n    switch(command) {\n    case DALLAS_COMMON_CMD_SEARCH_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_search_rom(bus, &data->rom_data);\n\n        } else if(data->state.command_state == DallasCommonCommandStateRomCmd) {\n            data->state.command_state = DallasCommonCommandStateMemCmd;\n            ds1971_emulate_read_mem(bus, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);\n            return false;\n\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_READ_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_read_rom(bus, &data->rom_data);\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_SKIP_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return true;\n        } else {\n            return false;\n        }\n\n    default:\n        return false;\n    }\n}\n\nvoid dallas_ds1971_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    data->state.bus = bus;\n\n    onewire_slave_set_reset_callback(bus, dallas_ds1971_reset_callback, protocol_data);\n    onewire_slave_set_command_callback(bus, dallas_ds1971_command_callback, protocol_data);\n}\n\nbool dallas_ds1971_load(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(format_version < 2) break;\n        if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;\n        if(!flipper_format_read_hex(\n               ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool dallas_ds1971_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;\n        if(!flipper_format_write_hex(\n               ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nvoid dallas_ds1971_render_uid(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n    dallas_common_render_uid(result, &data->rom_data);\n}\n\nvoid dallas_ds1971_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n\n    furi_string_cat_printf(result, \"\\e#Memory Data\\n--------------------\\n\");\n\n    pretty_format_bytes_hex_canonical(\n        result,\n        DS1971_DATA_BYTE_COUNT,\n        PRETTY_FORMAT_FONT_MONOSPACE,\n        data->eeprom_data,\n        DS1971_EEPROM_DATA_SIZE);\n}\n\nvoid dallas_ds1971_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n    dallas_common_render_brief_data(\n        result, &data->rom_data, data->eeprom_data, DS1971_EEPROM_DATA_SIZE, DS1971_MEMORY_TYPE);\n}\n\nvoid dallas_ds1971_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n\n    if(!dallas_common_is_valid_crc(&data->rom_data)) {\n        dallas_common_render_crc_error(result, &data->rom_data);\n    }\n}\n\nbool dallas_ds1971_is_data_valid(const iButtonProtocolData* protocol_data) {\n    const DS1971ProtocolData* data = protocol_data;\n    return dallas_common_is_valid_crc(&data->rom_data);\n}\n\nvoid dallas_ds1971_get_editable_data(\n    iButtonEditableData* editable_data,\n    iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    editable_data->ptr = data->rom_data.bytes;\n    editable_data->size = sizeof(DallasCommonRomData);\n}\n\nvoid dallas_ds1971_apply_edits(iButtonProtocolData* protocol_data) {\n    DS1971ProtocolData* data = protocol_data;\n    dallas_common_apply_edits(&data->rom_data, DS1971_FAMILY_CODE);\n}\n\nbool dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size) {\n    onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM);\n\n    onewire_host_write(host, address);\n    onewire_host_read_bytes(host, data, (uint8_t)data_size);\n\n    return true;\n}\n\nbool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) {\n    bool success = false;\n\n    do {\n        uint8_t address;\n        if(!onewire_slave_receive(bus, &address, sizeof(address))) break;\n        if(address >= data_size) break;\n        if(!onewire_slave_send(bus, data + address, data_size - address)) break;\n\n        success = true;\n    } while(false);\n\n    return success;\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1971.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\nextern const iButtonProtocolDallasBase ibutton_protocol_ds1971;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1990.c",
    "content": "#include \"protocol_ds1990.h\"\n\n#include <core/string.h>\n#include <core/core_defines.h>\n\n#include \"dallas_common.h\"\n\n#include \"../blanks/rw1990.h\"\n#include \"../blanks/tm2004.h\"\n#include \"../blanks/tm01x.h\"\n#define DS1990_FAMILY_CODE 0x01U\n#define DS1990_FAMILY_NAME \"DS1990\"\n\n#define DS1990_CMD_READ_ROM 0x0FU\n\ntypedef struct {\n    OneWireSlave* bus;\n} DS1990ProtocolState;\n\ntypedef struct {\n    DallasCommonRomData rom_data;\n    DS1990ProtocolState state;\n} DS1990ProtocolData;\n\nstatic bool dallas_ds1990_read(OneWireHost*, iButtonProtocolData*);\nstatic bool dallas_ds1990_write_id(OneWireHost*, iButtonProtocolData*);\nstatic void dallas_ds1990_emulate(OneWireSlave*, iButtonProtocolData*);\nstatic bool dallas_ds1990_load(FlipperFormat*, uint32_t, iButtonProtocolData*);\nstatic bool dallas_ds1990_save(FlipperFormat*, const iButtonProtocolData*);\nstatic void dallas_ds1990_render_uid(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1990_render_brief_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1990_render_error(FuriString*, const iButtonProtocolData*);\nstatic bool dallas_ds1990_is_data_valid(const iButtonProtocolData*);\nstatic void dallas_ds1990_get_editable_data(iButtonEditableData*, iButtonProtocolData*);\nstatic void dallas_ds1990_apply_edits(iButtonProtocolData*);\n\nconst iButtonProtocolDallasBase ibutton_protocol_ds1990 = {\n    .family_code = DS1990_FAMILY_CODE,\n    .features = iButtonProtocolFeatureWriteId,\n    .data_size = sizeof(DS1990ProtocolData),\n    .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,\n    .name = DS1990_FAMILY_NAME,\n\n    .read = dallas_ds1990_read,\n    .write_id = dallas_ds1990_write_id,\n    .write_copy = NULL, /* No data to write a copy */\n    .emulate = dallas_ds1990_emulate,\n    .save = dallas_ds1990_save,\n    .load = dallas_ds1990_load,\n    .render_uid = dallas_ds1990_render_uid,\n    .render_data = NULL, /* No data to render */\n    .render_brief_data = dallas_ds1990_render_brief_data,\n    .render_error = dallas_ds1990_render_error,\n    .is_valid = dallas_ds1990_is_data_valid,\n    .get_editable_data = dallas_ds1990_get_editable_data,\n    .apply_edits = dallas_ds1990_apply_edits,\n};\n\nbool dallas_ds1990_read(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n    return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data);\n}\n\nbool dallas_ds1990_write_id(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n\n    return rw1990_write_v1(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||\n           rw1990_write_v2(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||\n           tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||\n           tm01x_write_dallas(host, data->rom_data.bytes, sizeof(DallasCommonRomData));\n}\n\nstatic bool dallas_ds1990_reset_callback(bool is_short, void* context) {\n    DS1990ProtocolData* data = context;\n    if(!is_short) {\n        onewire_slave_set_overdrive(data->state.bus, is_short);\n    }\n    return !is_short;\n}\n\nstatic bool dallas_ds1990_command_callback(uint8_t command, void* context) {\n    furi_assert(context);\n    DS1990ProtocolData* data = context;\n    OneWireSlave* bus = data->state.bus;\n\n    switch(command) {\n    case DALLAS_COMMON_CMD_SEARCH_ROM:\n        dallas_common_emulate_search_rom(bus, &data->rom_data);\n        break;\n    case DALLAS_COMMON_CMD_READ_ROM:\n    case DS1990_CMD_READ_ROM:\n        dallas_common_emulate_read_rom(bus, &data->rom_data);\n        break;\n    default:\n        break;\n    }\n\n    // No support for multiple consecutive commands\n    return false;\n}\n\nvoid dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n    data->state.bus = bus;\n\n    onewire_slave_set_reset_callback(bus, dallas_ds1990_reset_callback, protocol_data);\n    onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data);\n}\n\nbool dallas_ds1990_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {\n    const DS1990ProtocolData* data = protocol_data;\n    return dallas_common_save_rom_data(ff, &data->rom_data);\n}\n\nbool dallas_ds1990_load(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n    return dallas_common_load_rom_data(ff, format_version, &data->rom_data);\n}\n\nvoid dallas_ds1990_render_uid(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1990ProtocolData* data = protocol_data;\n\n    dallas_common_render_uid(result, &data->rom_data);\n}\n\nvoid dallas_ds1990_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1990ProtocolData* data = protocol_data;\n\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) {\n        furi_string_cat_printf(result, \"%02X \", data->rom_data.bytes[i]);\n    }\n    furi_string_cat_printf(result, \"\\nFamily Code: %02X\\n\", data->rom_data.bytes[0]);\n}\n\nvoid dallas_ds1990_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1990ProtocolData* data = protocol_data;\n\n    if(!dallas_common_is_valid_crc(&data->rom_data)) {\n        dallas_common_render_crc_error(result, &data->rom_data);\n    }\n}\n\nbool dallas_ds1990_is_data_valid(const iButtonProtocolData* protocol_data) {\n    const DS1990ProtocolData* data = protocol_data;\n    return dallas_common_is_valid_crc(&data->rom_data);\n}\n\nvoid dallas_ds1990_get_editable_data(\n    iButtonEditableData* editable_data,\n    iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n    editable_data->ptr = data->rom_data.bytes;\n    editable_data->size = sizeof(DallasCommonRomData);\n}\n\nvoid dallas_ds1990_apply_edits(iButtonProtocolData* protocol_data) {\n    DS1990ProtocolData* data = protocol_data;\n    dallas_common_apply_edits(&data->rom_data, DS1990_FAMILY_CODE);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1990.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\nextern const iButtonProtocolDallasBase ibutton_protocol_ds1990;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1992.c",
    "content": "#include \"protocol_ds1992.h\"\n\n#include <core/core_defines.h>\n#include <toolbox/pretty_format.h>\n\n#include \"dallas_common.h\"\n\n#include \"../blanks/tm2004.h\"\n\n#define DS1992_FAMILY_CODE 0x08U\n#define DS1992_FAMILY_NAME \"DS1992\"\n\n#define DS1992_SRAM_DATA_SIZE          128U\n#define DS1992_SRAM_PAGE_SIZE          4U\n#define DS1992_COPY_SCRATCH_TIMEOUT_US 100U\n\n#define DS1992_DATA_BYTE_COUNT 4U\n\n#define DS1992_SRAM_DATA_KEY \"Sram Data\"\n#define DS1992_MEMORY_TYPE   \"SRAM\"\n\ntypedef struct {\n    OneWireSlave* bus;\n    DallasCommonCommandState command_state;\n} DS1992ProtocolState;\n\ntypedef struct {\n    DallasCommonRomData rom_data;\n    uint8_t sram_data[DS1992_SRAM_DATA_SIZE];\n    DS1992ProtocolState state;\n} DS1992ProtocolData;\n\nstatic bool dallas_ds1992_read(OneWireHost*, void*);\nstatic bool dallas_ds1992_write_id(OneWireHost*, iButtonProtocolData*);\nstatic bool dallas_ds1992_write_copy(OneWireHost*, iButtonProtocolData*);\nstatic void dallas_ds1992_emulate(OneWireSlave*, iButtonProtocolData*);\nstatic bool dallas_ds1992_load(FlipperFormat*, uint32_t, iButtonProtocolData*);\nstatic bool dallas_ds1992_save(FlipperFormat*, const iButtonProtocolData*);\nstatic void dallas_ds1992_render_uid(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1992_render_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1992_render_brief_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1992_render_error(FuriString*, const iButtonProtocolData*);\nstatic bool dallas_ds1992_is_data_valid(const iButtonProtocolData*);\nstatic void dallas_ds1992_get_editable_data(iButtonEditableData*, iButtonProtocolData*);\nstatic void dallas_ds1992_apply_edits(iButtonProtocolData*);\n\nconst iButtonProtocolDallasBase ibutton_protocol_ds1992 = {\n    .family_code = DS1992_FAMILY_CODE,\n    .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteId |\n                iButtonProtocolFeatureWriteCopy,\n    .data_size = sizeof(DS1992ProtocolData),\n    .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,\n    .name = DS1992_FAMILY_NAME,\n\n    .read = dallas_ds1992_read,\n    .write_id = dallas_ds1992_write_id,\n    .write_copy = dallas_ds1992_write_copy,\n    .emulate = dallas_ds1992_emulate,\n    .save = dallas_ds1992_save,\n    .load = dallas_ds1992_load,\n    .render_uid = dallas_ds1992_render_uid,\n    .render_data = dallas_ds1992_render_data,\n    .render_brief_data = dallas_ds1992_render_brief_data,\n    .render_error = dallas_ds1992_render_error,\n    .is_valid = dallas_ds1992_is_data_valid,\n    .get_editable_data = dallas_ds1992_get_editable_data,\n    .apply_edits = dallas_ds1992_apply_edits,\n};\n\nbool dallas_ds1992_read(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) &&\n           dallas_common_read_mem(host, 0, data->sram_data, DS1992_SRAM_DATA_SIZE);\n}\n\nbool dallas_ds1992_write_id(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));\n}\n\nbool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    return dallas_common_write_mem(\n        host,\n        DS1992_COPY_SCRATCH_TIMEOUT_US,\n        DS1992_SRAM_PAGE_SIZE,\n        data->sram_data,\n        DS1992_SRAM_DATA_SIZE);\n}\n\nstatic bool dallas_ds1992_reset_callback(bool is_short, void* context) {\n    furi_assert(context);\n    DS1992ProtocolData* data = context;\n\n    if(!is_short) {\n        data->state.command_state = DallasCommonCommandStateIdle;\n        onewire_slave_set_overdrive(data->state.bus, is_short);\n    }\n\n    return !is_short;\n}\n\nstatic bool dallas_ds1992_command_callback(uint8_t command, void* context) {\n    furi_assert(context);\n    DS1992ProtocolData* data = context;\n    OneWireSlave* bus = data->state.bus;\n\n    switch(command) {\n    case DALLAS_COMMON_CMD_SEARCH_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_search_rom(bus, &data->rom_data);\n\n        } else if(data->state.command_state == DallasCommonCommandStateRomCmd) {\n            data->state.command_state = DallasCommonCommandStateMemCmd;\n            dallas_common_emulate_read_mem(bus, data->sram_data, DS1992_SRAM_DATA_SIZE);\n            return false;\n\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_READ_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_read_rom(bus, &data->rom_data);\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_SKIP_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return true;\n        } else {\n            return false;\n        }\n\n    default:\n        return false;\n    }\n}\n\nvoid dallas_ds1992_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    data->state.bus = bus;\n\n    onewire_slave_set_reset_callback(bus, dallas_ds1992_reset_callback, protocol_data);\n    onewire_slave_set_command_callback(bus, dallas_ds1992_command_callback, protocol_data);\n}\n\nbool dallas_ds1992_load(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(format_version < 2) break;\n        if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;\n        if(!flipper_format_read_hex(\n               ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool dallas_ds1992_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;\n        if(!flipper_format_write_hex(\n               ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nvoid dallas_ds1992_render_uid(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n    dallas_common_render_uid(result, &data->rom_data);\n}\n\nvoid dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n\n    furi_string_cat_printf(result, \"\\e#Memory Data\\n--------------------\\n\");\n\n    pretty_format_bytes_hex_canonical(\n        result,\n        DS1992_DATA_BYTE_COUNT,\n        PRETTY_FORMAT_FONT_MONOSPACE,\n        data->sram_data,\n        DS1992_SRAM_DATA_SIZE);\n}\n\nvoid dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n    dallas_common_render_brief_data(\n        result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE, DS1992_MEMORY_TYPE);\n}\n\nvoid dallas_ds1992_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n\n    if(!dallas_common_is_valid_crc(&data->rom_data)) {\n        dallas_common_render_crc_error(result, &data->rom_data);\n    }\n}\n\nbool dallas_ds1992_is_data_valid(const iButtonProtocolData* protocol_data) {\n    const DS1992ProtocolData* data = protocol_data;\n    return dallas_common_is_valid_crc(&data->rom_data);\n}\n\nvoid dallas_ds1992_get_editable_data(\n    iButtonEditableData* editable_data,\n    iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    editable_data->ptr = data->rom_data.bytes;\n    editable_data->size = sizeof(DallasCommonRomData);\n}\n\nvoid dallas_ds1992_apply_edits(iButtonProtocolData* protocol_data) {\n    DS1992ProtocolData* data = protocol_data;\n    dallas_common_apply_edits(&data->rom_data, DS1992_FAMILY_CODE);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1992.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\nextern const iButtonProtocolDallasBase ibutton_protocol_ds1992;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1996.c",
    "content": "#include \"protocol_ds1996.h\"\n\n#include <core/core_defines.h>\n#include <toolbox/pretty_format.h>\n\n#include \"dallas_common.h\"\n\n#include \"../blanks/tm2004.h\"\n\n#define DS1996_FAMILY_CODE 0x0CU\n#define DS1996_FAMILY_NAME \"DS1996\"\n\n#define DS1996_SRAM_DATA_SIZE          8192U\n#define DS1996_SRAM_PAGE_SIZE          32U\n#define DS1996_COPY_SCRATCH_TIMEOUT_US 100U\n\n#define DS1996_DATA_BYTE_COUNT 4U\n\n#define DS1996_SRAM_DATA_KEY \"Sram Data\"\n#define DS1996_MEMORY_TYPE   \"SRAM\"\n\ntypedef struct {\n    OneWireSlave* bus;\n    DallasCommonCommandState command_state;\n} DS1996ProtocolState;\n\ntypedef struct {\n    DallasCommonRomData rom_data;\n    uint8_t sram_data[DS1996_SRAM_DATA_SIZE];\n    DS1996ProtocolState state;\n} DS1996ProtocolData;\n\nstatic bool dallas_ds1996_read(OneWireHost*, void*);\nstatic bool dallas_ds1996_write_id(OneWireHost*, iButtonProtocolData*);\nstatic bool dallas_ds1996_write_copy(OneWireHost*, iButtonProtocolData*);\nstatic void dallas_ds1996_emulate(OneWireSlave*, iButtonProtocolData*);\nstatic bool dallas_ds1996_load(FlipperFormat*, uint32_t, iButtonProtocolData*);\nstatic bool dallas_ds1996_save(FlipperFormat*, const iButtonProtocolData*);\nstatic void dallas_ds1996_render_uid(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1996_render_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1996_render_brief_data(FuriString*, const iButtonProtocolData*);\nstatic void dallas_ds1996_render_error(FuriString*, const iButtonProtocolData*);\nstatic bool dallas_ds1996_is_data_valid(const iButtonProtocolData*);\nstatic void dallas_ds1996_get_editable_data(iButtonEditableData*, iButtonProtocolData*);\nstatic void dallas_ds1996_apply_edits(iButtonProtocolData*);\n\nconst iButtonProtocolDallasBase ibutton_protocol_ds1996 = {\n    .family_code = DS1996_FAMILY_CODE,\n    .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteId |\n                iButtonProtocolFeatureWriteCopy,\n    .data_size = sizeof(DS1996ProtocolData),\n    .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,\n    .name = DS1996_FAMILY_NAME,\n\n    .read = dallas_ds1996_read,\n    .write_id = dallas_ds1996_write_id,\n    .write_copy = dallas_ds1996_write_copy,\n    .emulate = dallas_ds1996_emulate,\n    .save = dallas_ds1996_save,\n    .load = dallas_ds1996_load,\n    .render_uid = dallas_ds1996_render_uid,\n    .render_data = dallas_ds1996_render_data,\n    .render_brief_data = dallas_ds1996_render_brief_data,\n    .render_error = dallas_ds1996_render_error,\n    .is_valid = dallas_ds1996_is_data_valid,\n    .get_editable_data = dallas_ds1996_get_editable_data,\n    .apply_edits = dallas_ds1996_apply_edits,\n};\n\nbool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(!onewire_host_reset(host)) break;\n        if(!dallas_common_read_rom(host, &data->rom_data)) break;\n        if(!onewire_host_reset(host)) break;\n\n        onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM);\n        onewire_host_set_overdrive(host, true);\n\n        if(!dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE)) break;\n        success = true;\n    } while(false);\n\n    onewire_host_set_overdrive(host, false);\n    return success;\n}\n\nbool dallas_ds1996_write_id(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));\n}\n\nbool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(!onewire_host_reset(host)) break;\n\n        onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM);\n        onewire_host_set_overdrive(host, true);\n\n        if(!dallas_common_write_mem(\n               host,\n               DS1996_COPY_SCRATCH_TIMEOUT_US,\n               DS1996_SRAM_PAGE_SIZE,\n               data->sram_data,\n               DS1996_SRAM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    onewire_host_set_overdrive(host, false);\n    return success;\n}\n\nstatic bool dallas_ds1996_reset_callback(bool is_short, void* context) {\n    furi_assert(context);\n    DS1996ProtocolData* data = context;\n    data->state.command_state = DallasCommonCommandStateIdle;\n    onewire_slave_set_overdrive(data->state.bus, is_short);\n    return true;\n}\n\nstatic bool dallas_ds1996_command_callback(uint8_t command, void* context) {\n    furi_assert(context);\n    DS1996ProtocolData* data = context;\n    OneWireSlave* bus = data->state.bus;\n\n    switch(command) {\n    case DALLAS_COMMON_CMD_SEARCH_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_search_rom(bus, &data->rom_data);\n\n        } else if(data->state.command_state == DallasCommonCommandStateRomCmd) {\n            data->state.command_state = DallasCommonCommandStateMemCmd;\n            return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);\n\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_READ_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return dallas_common_emulate_read_rom(bus, &data->rom_data);\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_SKIP_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            return true;\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM:\n        if(data->state.command_state == DallasCommonCommandStateIdle) {\n            data->state.command_state = DallasCommonCommandStateRomCmd;\n            onewire_slave_set_overdrive(bus, true);\n            return true;\n        } else {\n            return false;\n        }\n\n    case DALLAS_COMMON_CMD_MATCH_ROM:\n    case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM:\n        /* TODO FL-3533: Match ROM command support */\n    default:\n        return false;\n    }\n}\n\nvoid dallas_ds1996_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    data->state.bus = bus;\n\n    onewire_slave_set_reset_callback(bus, dallas_ds1996_reset_callback, protocol_data);\n    onewire_slave_set_command_callback(bus, dallas_ds1996_command_callback, protocol_data);\n}\n\nbool dallas_ds1996_load(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(format_version < 2) break;\n        if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;\n        if(!flipper_format_read_hex(\n               ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool dallas_ds1996_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n    bool success = false;\n\n    do {\n        if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;\n        if(!flipper_format_write_hex(\n               ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE))\n            break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nvoid dallas_ds1996_render_uid(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n    dallas_common_render_uid(result, &data->rom_data);\n}\n\nvoid dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n\n    furi_string_cat_printf(result, \"\\e#Memory Data\\n--------------------\\n\");\n\n    pretty_format_bytes_hex_canonical(\n        result,\n        DS1996_DATA_BYTE_COUNT,\n        PRETTY_FORMAT_FONT_MONOSPACE,\n        data->sram_data,\n        DS1996_SRAM_DATA_SIZE);\n}\n\nvoid dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n    dallas_common_render_brief_data(\n        result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE, DS1996_MEMORY_TYPE);\n}\n\nvoid dallas_ds1996_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n\n    if(!dallas_common_is_valid_crc(&data->rom_data)) {\n        dallas_common_render_crc_error(result, &data->rom_data);\n    }\n}\n\nbool dallas_ds1996_is_data_valid(const iButtonProtocolData* protocol_data) {\n    const DS1996ProtocolData* data = protocol_data;\n    return dallas_common_is_valid_crc(&data->rom_data);\n}\n\nvoid dallas_ds1996_get_editable_data(\n    iButtonEditableData* editable_data,\n    iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    editable_data->ptr = data->rom_data.bytes;\n    editable_data->size = sizeof(DallasCommonRomData);\n}\n\nvoid dallas_ds1996_apply_edits(iButtonProtocolData* protocol_data) {\n    DS1996ProtocolData* data = protocol_data;\n    dallas_common_apply_edits(&data->rom_data, DS1996_FAMILY_CODE);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds1996.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\nextern const iButtonProtocolDallasBase ibutton_protocol_ds1996;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds_generic.c",
    "content": "#include \"protocol_ds_generic.h\"\n\n#include <core/string.h>\n#include <core/core_defines.h>\n\n#include \"dallas_common.h\"\n\n#include \"../blanks/tm2004.h\"\n\n#define DALLAS_GENERIC_FAMILY_CODE 0x00U\n#define DALLAS_GENERIC_FAMILY_NAME \"(non-specific)\"\n\ntypedef struct {\n    OneWireSlave* bus;\n} DallasGenericProtocolState;\n\ntypedef struct {\n    DallasCommonRomData rom_data;\n    DallasGenericProtocolState state;\n} DallasGenericProtocolData;\n\nstatic bool ds_generic_read(OneWireHost*, iButtonProtocolData*);\nstatic bool ds_generic_write_id(OneWireHost*, iButtonProtocolData*);\nstatic void ds_generic_emulate(OneWireSlave*, iButtonProtocolData*);\nstatic bool ds_generic_load(FlipperFormat*, uint32_t, iButtonProtocolData*);\nstatic bool ds_generic_save(FlipperFormat*, const iButtonProtocolData*);\nstatic void ds_generic_render_uid(FuriString*, const iButtonProtocolData*);\nstatic void ds_generic_render_brief_data(FuriString*, const iButtonProtocolData*);\nstatic void ds_generic_render_error(FuriString*, const iButtonProtocolData*);\nstatic bool ds_generic_is_data_valid(const iButtonProtocolData*);\nstatic void ds_generic_get_editable_data(iButtonEditableData*, iButtonProtocolData*);\nstatic void ds_generic_apply_edits(iButtonProtocolData*);\n\nconst iButtonProtocolDallasBase ibutton_protocol_ds_generic = {\n    .family_code = DALLAS_GENERIC_FAMILY_CODE,\n    .features = iButtonProtocolFeatureWriteId,\n    .data_size = sizeof(DallasGenericProtocolData),\n    .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,\n    .name = DALLAS_GENERIC_FAMILY_NAME,\n\n    .read = ds_generic_read,\n    .write_id = ds_generic_write_id,\n    .write_copy = NULL, /* No data to write a copy */\n    .emulate = ds_generic_emulate,\n    .save = ds_generic_save,\n    .load = ds_generic_load,\n    .render_data = NULL, /* No data to render */\n    .render_uid = ds_generic_render_uid,\n    .render_brief_data = ds_generic_render_brief_data,\n    .render_error = ds_generic_render_error,\n    .is_valid = ds_generic_is_data_valid,\n    .get_editable_data = ds_generic_get_editable_data,\n    .apply_edits = ds_generic_apply_edits,\n};\n\nbool ds_generic_read(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DallasGenericProtocolData* data = protocol_data;\n    return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data);\n}\n\nbool ds_generic_write_id(OneWireHost* host, iButtonProtocolData* protocol_data) {\n    DallasGenericProtocolData* data = protocol_data;\n    return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));\n}\n\nstatic bool ds_generic_reset_callback(bool is_short, void* context) {\n    furi_assert(context);\n    DallasGenericProtocolData* data = context;\n    if(!is_short) {\n        onewire_slave_set_overdrive(data->state.bus, is_short);\n    }\n    return !is_short;\n}\n\nstatic bool ds_generic_command_callback(uint8_t command, void* context) {\n    furi_assert(context);\n    DallasGenericProtocolData* data = context;\n    OneWireSlave* bus = data->state.bus;\n\n    switch(command) {\n    case DALLAS_COMMON_CMD_SEARCH_ROM:\n        dallas_common_emulate_search_rom(bus, &data->rom_data);\n        break;\n    case DALLAS_COMMON_CMD_READ_ROM:\n        dallas_common_emulate_read_rom(bus, &data->rom_data);\n        break;\n    default:\n        break;\n    }\n\n    // No support for multiple consecutive commands\n    return false;\n}\n\nvoid ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {\n    DallasGenericProtocolData* data = protocol_data;\n    data->state.bus = bus;\n\n    onewire_slave_set_reset_callback(bus, ds_generic_reset_callback, protocol_data);\n    onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data);\n}\n\nbool ds_generic_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {\n    const DallasGenericProtocolData* data = protocol_data;\n    return dallas_common_save_rom_data(ff, &data->rom_data);\n}\n\nbool ds_generic_load(\n    FlipperFormat* ff,\n    uint32_t format_version,\n    iButtonProtocolData* protocol_data) {\n    DallasGenericProtocolData* data = protocol_data;\n    return dallas_common_load_rom_data(ff, format_version, &data->rom_data);\n}\n\nvoid ds_generic_render_uid(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DallasGenericProtocolData* data = protocol_data;\n    dallas_common_render_uid(result, &data->rom_data);\n}\n\nvoid ds_generic_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {\n    const DallasGenericProtocolData* data = protocol_data;\n\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) {\n        furi_string_cat_printf(result, \"%02X \", data->rom_data.bytes[i]);\n    }\n}\n\nvoid ds_generic_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {\n    UNUSED(result);\n    UNUSED(protocol_data);\n}\n\nbool ds_generic_is_data_valid(const iButtonProtocolData* protocol_data) {\n    UNUSED(protocol_data);\n    return true;\n}\n\nvoid ds_generic_get_editable_data(\n    iButtonEditableData* editable_data,\n    iButtonProtocolData* protocol_data) {\n    DallasGenericProtocolData* data = protocol_data;\n    editable_data->ptr = data->rom_data.bytes;\n    editable_data->size = sizeof(DallasCommonRomData);\n}\n\nvoid ds_generic_apply_edits(iButtonProtocolData* protocol_data) {\n    UNUSED(protocol_data);\n}\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_ds_generic.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\nextern const iButtonProtocolDallasBase ibutton_protocol_ds_generic;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_group_dallas.c",
    "content": "#include \"protocol_group_dallas.h\"\n\n#include <furi_hal_resources.h>\n\n#include \"protocol_group_dallas_defs.h\"\n\n#define IBUTTON_ONEWIRE_ROM_SIZE 8U\n\ntypedef struct {\n    OneWireHost* host;\n    OneWireSlave* bus;\n} iButtonProtocolGroupDallas;\n\nstatic iButtonProtocolGroupDallas* ibutton_protocol_group_dallas_alloc(void) {\n    iButtonProtocolGroupDallas* group = malloc(sizeof(iButtonProtocolGroupDallas));\n\n    group->host = onewire_host_alloc(&gpio_ibutton);\n    group->bus = onewire_slave_alloc(&gpio_ibutton);\n\n    return group;\n}\n\nstatic void ibutton_protocol_group_dallas_free(iButtonProtocolGroupDallas* group) {\n    onewire_slave_free(group->bus);\n    onewire_host_free(group->host);\n    free(group);\n}\n\nstatic size_t ibutton_protocol_group_dallas_get_max_data_size(iButtonProtocolGroupDallas* group) {\n    UNUSED(group);\n    size_t max_data_size = 0;\n\n    for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) {\n        const size_t current_rom_size = ibutton_protocols_dallas[i]->data_size;\n        if(current_rom_size > max_data_size) {\n            max_data_size = current_rom_size;\n        }\n    }\n\n    return max_data_size;\n}\n\nstatic bool ibutton_protocol_group_dallas_get_id_by_name(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolLocalId* id,\n    const char* name) {\n    UNUSED(group);\n    // Handle older key files which refer to DS1990 as just \"Dallas\"\n    if(strcmp(name, \"Dallas\") == 0) {\n        *id = iButtonProtocolDS1990;\n        return true;\n    }\n\n    // Handle files that refer to Dallas \"Raw Data\" as DSGeneric\n    if(strcmp(name, \"DSGeneric\") == 0) {\n        *id = iButtonProtocolDSGeneric;\n        return true;\n    }\n\n    for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) {\n        if(strcmp(ibutton_protocols_dallas[i]->name, name) == 0) {\n            *id = i;\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic uint32_t ibutton_protocol_group_dallas_get_features(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->features;\n}\n\nstatic const char* ibutton_protocol_group_dallas_get_manufacturer(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->manufacturer;\n}\n\nstatic const char* ibutton_protocol_group_dallas_get_name(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->name;\n}\n\nstatic iButtonProtocolLocalId\n    ibutton_protocol_group_dallas_get_id_by_family_code(uint8_t family_code) {\n    iButtonProtocolLocalId id;\n\n    for(id = 0; id < iButtonProtocolDSGeneric; ++id) {\n        if(ibutton_protocols_dallas[id]->family_code == family_code) break;\n    }\n\n    return id;\n}\n\nstatic bool ibutton_protocol_group_dallas_read(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId* id) {\n    bool success = false;\n    uint8_t rom_data[IBUTTON_ONEWIRE_ROM_SIZE];\n    OneWireHost* host = group->host;\n\n    onewire_host_start(host);\n    furi_delay_ms(100);\n\n    FURI_CRITICAL_ENTER();\n\n    if(onewire_host_search(host, rom_data, OneWireHostSearchModeNormal)) {\n        /* Considering any found 1-Wire device a success.\n         * It can be checked later with ibutton_key_is_valid(). */\n        success = true;\n\n        /* If a 1-Wire device was found, id is guaranteed to be\n         * one of the known keys or DSGeneric. */\n        *id = ibutton_protocol_group_dallas_get_id_by_family_code(rom_data[0]);\n        ibutton_protocols_dallas[*id]->read(host, data);\n    }\n\n    onewire_host_reset_search(host);\n    onewire_host_stop(host);\n\n    FURI_CRITICAL_EXIT();\n\n    return success;\n}\n\nstatic bool ibutton_protocol_group_dallas_write_id(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    furi_assert(id < iButtonProtocolDSMax);\n    const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];\n    furi_assert(protocol->features & iButtonProtocolFeatureWriteId);\n\n    OneWireHost* host = group->host;\n\n    onewire_host_start(host);\n    furi_delay_ms(100);\n\n    FURI_CRITICAL_ENTER();\n\n    const bool success = protocol->write_id(host, data);\n    onewire_host_stop(host);\n\n    FURI_CRITICAL_EXIT();\n    return success;\n}\n\nstatic bool ibutton_protocol_group_dallas_write_copy(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    furi_assert(id < iButtonProtocolDSMax);\n\n    const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];\n    furi_assert(protocol->features & iButtonProtocolFeatureWriteCopy);\n\n    OneWireHost* host = group->host;\n\n    onewire_host_start(host);\n    furi_delay_ms(100);\n\n    FURI_CRITICAL_ENTER();\n\n    const bool success = protocol->write_copy(host, data);\n    onewire_host_stop(host);\n\n    FURI_CRITICAL_EXIT();\n    return success;\n}\n\nstatic void ibutton_protocol_group_dallas_emulate_start(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    furi_assert(id < iButtonProtocolDSMax);\n    OneWireSlave* bus = group->bus;\n    ibutton_protocols_dallas[id]->emulate(bus, data);\n    onewire_slave_start(bus);\n}\n\nstatic void ibutton_protocol_group_dallas_emulate_stop(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    furi_assert(id < iButtonProtocolDSMax);\n    UNUSED(data);\n    onewire_slave_stop(group->bus);\n}\n\nstatic bool ibutton_protocol_group_dallas_save(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FlipperFormat* ff) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->save(ff, data);\n}\n\nstatic bool ibutton_protocol_group_dallas_load(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    uint32_t version,\n    FlipperFormat* ff) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->load(ff, version, data);\n}\n\nstatic void ibutton_protocol_group_dallas_render_uid(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];\n    furi_assert(protocol->render_uid);\n    protocol->render_uid(result, data);\n}\n\nstatic void ibutton_protocol_group_dallas_render_data(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];\n    furi_assert(protocol->render_data);\n    protocol->render_data(result, data);\n}\n\nstatic void ibutton_protocol_group_dallas_render_brief_data(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    ibutton_protocols_dallas[id]->render_brief_data(result, data);\n}\n\nstatic void ibutton_protocol_group_dallas_render_error(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    ibutton_protocols_dallas[id]->render_error(result, data);\n}\n\nstatic bool ibutton_protocol_group_dallas_is_valid(\n    iButtonProtocolGroupDallas* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    return ibutton_protocols_dallas[id]->is_valid(data);\n}\n\nstatic void ibutton_protocol_group_dallas_get_editable_data(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    iButtonEditableData* editable) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    ibutton_protocols_dallas[id]->get_editable_data(editable, data);\n}\n\nstatic void ibutton_protocol_group_dallas_apply_edits(\n    iButtonProtocolGroupDallas* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    furi_assert(id < iButtonProtocolDSMax);\n    ibutton_protocols_dallas[id]->apply_edits(data);\n}\n\nconst iButtonProtocolGroupBase ibutton_protocol_group_dallas = {\n    .protocol_count = iButtonProtocolDSMax,\n\n    .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_dallas_alloc,\n    .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_dallas_free,\n\n    .get_max_data_size =\n        (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_dallas_get_max_data_size,\n    .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_dallas_get_id_by_name,\n    .get_features =\n        (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_dallas_get_features,\n\n    .get_manufacturer =\n        (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_manufacturer,\n    .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_name,\n\n    .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_dallas_read,\n    .write_id = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_id,\n    .write_copy = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_copy,\n\n    .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_start,\n    .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_stop,\n\n    .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_dallas_save,\n    .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_dallas_load,\n\n    .render_uid = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_uid,\n    .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_data,\n    .render_brief_data =\n        (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_brief_data,\n    .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_error,\n\n    .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_dallas_is_valid,\n    .get_editable_data =\n        (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_dallas_get_editable_data,\n    .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_apply_edits,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_group_dallas.h",
    "content": "#pragma once\n\n#include \"../protocol_group_base.h\"\n\nextern const iButtonProtocolGroupBase ibutton_protocol_group_dallas;\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c",
    "content": "#include \"protocol_group_dallas_defs.h\"\n\n#include \"protocol_ds1990.h\"\n#include \"protocol_ds1992.h\"\n#include \"protocol_ds1996.h\"\n#include \"protocol_ds1971.h\"\n#include \"protocol_ds_generic.h\"\n\nconst iButtonProtocolDallasBase* const ibutton_protocols_dallas[] = {\n    [iButtonProtocolDS1990] = &ibutton_protocol_ds1990,\n    [iButtonProtocolDS1992] = &ibutton_protocol_ds1992,\n    [iButtonProtocolDS1996] = &ibutton_protocol_ds1996,\n    [iButtonProtocolDS1971] = &ibutton_protocol_ds1971,\n    /* Add new 1-Wire protocols here */\n\n    /* Default catch-all 1-Wire protocol */\n    [iButtonProtocolDSGeneric] = &ibutton_protocol_ds_generic,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h",
    "content": "#pragma once\n\n#include \"protocol_dallas_base.h\"\n\ntypedef enum {\n    iButtonProtocolDS1990,\n    iButtonProtocolDS1992,\n    iButtonProtocolDS1996,\n    iButtonProtocolDS1971,\n    /* Add new 1-Wire protocols here */\n\n    /* Default catch-all 1-Wire protocol */\n    iButtonProtocolDSGeneric,\n    iButtonProtocolDSMax,\n} iButtonProtocolDallas;\n\nextern const iButtonProtocolDallasBase* const ibutton_protocols_dallas[];\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_cyfral.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include \"protocol_cyfral.h\"\n\n#define CYFRAL_DATA_SIZE sizeof(uint16_t)\n#define CYFRAL_PERIOD    (125 * furi_hal_cortex_instructions_per_microsecond())\n#define CYFRAL_0_LOW     (CYFRAL_PERIOD * 0.66f)\n#define CYFRAL_0_HI      (CYFRAL_PERIOD * 0.33f)\n#define CYFRAL_1_LOW     (CYFRAL_PERIOD * 0.33f)\n#define CYFRAL_1_HI      (CYFRAL_PERIOD * 0.66f)\n\n#define CYFRAL_MAX_PERIOD_US 230\n\ntypedef enum {\n    CYFRAL_BIT_WAIT_FRONT_HIGH,\n    CYFRAL_BIT_WAIT_FRONT_LOW,\n} CyfralBitState;\n\ntypedef enum {\n    CYFRAL_WAIT_START_NIBBLE,\n    CYFRAL_READ_NIBBLE,\n    CYFRAL_READ_STOP_NIBBLE,\n} CyfralState;\n\ntypedef struct {\n    CyfralState state;\n    CyfralBitState bit_state;\n\n    // high + low period time\n    uint32_t period_time;\n    // temporary nibble storage\n    uint8_t nibble;\n    // data valid flag\n    // MUST be checked only in READ_STOP_NIBBLE state\n    bool data_valid;\n    // nibble index, we expect 8 nibbles\n    uint8_t index;\n    // bit index in nibble, 4 bit per nibble\n    uint8_t bit_index;\n    // max period, 230us x clock per us\n    uint32_t max_period;\n} ProtocolCyfralDecoder;\n\ntypedef struct {\n    uint32_t data;\n    uint32_t index;\n} ProtocolCyfralEncoder;\n\ntypedef struct {\n    uint16_t data;\n\n    ProtocolCyfralDecoder decoder;\n    ProtocolCyfralEncoder encoder;\n} ProtocolCyfral;\n\nstatic void* protocol_cyfral_alloc(void) {\n    ProtocolCyfral* proto = malloc(sizeof(ProtocolCyfral));\n    return (void*)proto;\n}\n\nstatic void protocol_cyfral_free(ProtocolCyfral* proto) {\n    free(proto);\n}\n\nstatic uint8_t* protocol_cyfral_get_data(ProtocolCyfral* proto) {\n    return (uint8_t*)&proto->data;\n}\n\nstatic void protocol_cyfral_decoder_start(ProtocolCyfral* proto) {\n    ProtocolCyfralDecoder* cyfral = &proto->decoder;\n\n    cyfral->state = CYFRAL_WAIT_START_NIBBLE;\n    cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;\n    cyfral->period_time = 0;\n    cyfral->bit_index = 0;\n    cyfral->index = 0;\n    cyfral->nibble = 0;\n    cyfral->data_valid = true;\n    cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond();\n\n    proto->data = 0;\n}\n\nstatic bool protocol_cyfral_decoder_process_bit(\n    ProtocolCyfralDecoder* cyfral,\n    bool polarity,\n    uint32_t length,\n    bool* bit_ready,\n    bool* bit_value) {\n    bool result = true;\n    *bit_ready = false;\n\n    // bit start from low\n    switch(cyfral->bit_state) {\n    case CYFRAL_BIT_WAIT_FRONT_LOW:\n        if(polarity == true) {\n            cyfral->period_time += length;\n\n            *bit_ready = true;\n            if(cyfral->period_time <= cyfral->max_period) {\n                if((cyfral->period_time / 2) > length) {\n                    *bit_value = false;\n                } else {\n                    *bit_value = true;\n                }\n            } else {\n                result = false;\n            }\n\n            cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH;\n        } else {\n            result = false;\n        }\n        break;\n    case CYFRAL_BIT_WAIT_FRONT_HIGH:\n        if(polarity == false) {\n            cyfral->period_time = length;\n            cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;\n        } else {\n            result = false;\n        }\n        break;\n    }\n\n    return result;\n}\n\nstatic bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint32_t duration) {\n    ProtocolCyfralDecoder* cyfral = &proto->decoder;\n\n    bool bit_ready;\n    bool bit_value;\n    bool decoded = false;\n\n    switch(cyfral->state) {\n    case CYFRAL_WAIT_START_NIBBLE:\n        // wait for start word\n        if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {\n            if(bit_ready) {\n                cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;\n                if(cyfral->nibble == 0b0001) {\n                    cyfral->nibble = 0;\n                    cyfral->state = CYFRAL_READ_NIBBLE;\n                }\n            }\n        } else {\n            protocol_cyfral_decoder_start(proto);\n        }\n\n        break;\n    case CYFRAL_READ_NIBBLE:\n        // read nibbles\n        if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {\n            if(bit_ready) {\n                cyfral->nibble = (cyfral->nibble << 1) | bit_value;\n\n                cyfral->bit_index++;\n\n                //convert every nibble to 2-bit index\n                if(cyfral->bit_index == 4) {\n                    switch(cyfral->nibble) {\n                    case 0b1110:\n                        proto->data = (proto->data << 2) | 0b11;\n                        break;\n                    case 0b1101:\n                        proto->data = (proto->data << 2) | 0b10;\n                        break;\n                    case 0b1011:\n                        proto->data = (proto->data << 2) | 0b01;\n                        break;\n                    case 0b0111:\n                        proto->data = (proto->data << 2) | 0b00;\n                        break;\n                    default:\n                        cyfral->data_valid = false;\n                        break;\n                    }\n\n                    cyfral->nibble = 0;\n                    cyfral->bit_index = 0;\n                    cyfral->index++;\n                }\n\n                // successfully read 8 nibbles\n                if(cyfral->index == 8) {\n                    cyfral->state = CYFRAL_READ_STOP_NIBBLE;\n                }\n            }\n        } else {\n            protocol_cyfral_decoder_start(proto);\n        }\n        break;\n    case CYFRAL_READ_STOP_NIBBLE:\n        // read stop nibble\n        if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {\n            if(bit_ready) {\n                cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;\n                cyfral->bit_index++;\n\n                switch(cyfral->bit_index) {\n                case 0:\n                case 1:\n                case 2:\n                case 3:\n                    break;\n                case 4:\n                    if(cyfral->nibble == 0b0001) {\n                        // validate data\n                        if(cyfral->data_valid) {\n                            decoded = true;\n                        } else {\n                            protocol_cyfral_decoder_start(proto);\n                        }\n                    } else {\n                        protocol_cyfral_decoder_start(proto);\n                    }\n                    break;\n                default:\n                    protocol_cyfral_decoder_start(proto);\n                    break;\n                }\n            }\n        } else {\n            protocol_cyfral_decoder_start(proto);\n        }\n        break;\n    }\n\n    return decoded;\n}\n\nstatic uint32_t protocol_cyfral_encoder_encode(const uint16_t data) {\n    uint32_t value = 0;\n    for(int8_t i = 0; i <= 7; i++) {\n        switch((data >> (i * 2)) & 0b00000011) {\n        case 0b11:\n            value = value << 4;\n            value += 0b00000111;\n            break;\n        case 0b10:\n            value = value << 4;\n            value += 0b00001011;\n            break;\n        case 0b01:\n            value = value << 4;\n            value += 0b00001101;\n            break;\n        case 0b00:\n            value = value << 4;\n            value += 0b00001110;\n            break;\n        default:\n            break;\n        }\n    }\n\n    return value;\n}\n\nstatic bool protocol_cyfral_encoder_start(ProtocolCyfral* proto) {\n    proto->encoder.index = 0;\n    proto->encoder.data = protocol_cyfral_encoder_encode(proto->data);\n    return true;\n}\n\nstatic LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) {\n    LevelDuration result;\n\n    if(proto->encoder.index < 8) {\n        // start word (0b0001)\n        switch(proto->encoder.index) {\n        case 0:\n            result = level_duration_make(false, CYFRAL_0_LOW); //-V1037\n            break;\n        case 1:\n            result = level_duration_make(true, CYFRAL_0_HI); //-V1037\n            break;\n        case 2:\n            result = level_duration_make(false, CYFRAL_0_LOW);\n            break;\n        case 3:\n            result = level_duration_make(true, CYFRAL_0_HI);\n            break;\n        case 4:\n            result = level_duration_make(false, CYFRAL_0_LOW);\n            break;\n        case 5:\n            result = level_duration_make(true, CYFRAL_0_HI);\n            break;\n        case 6:\n            result = level_duration_make(false, CYFRAL_1_LOW);\n            break;\n        case 7:\n            result = level_duration_make(true, CYFRAL_1_HI);\n            break;\n        }\n    } else {\n        // data\n        uint8_t data_start_index = proto->encoder.index - 8;\n        bool clock_polarity = (data_start_index) % 2;\n        uint8_t bit_index = (data_start_index) / 2;\n        bool bit_value = ((proto->encoder.data >> bit_index) & 1);\n\n        if(!clock_polarity) {\n            if(bit_value) {\n                result = level_duration_make(false, CYFRAL_1_LOW);\n            } else {\n                result = level_duration_make(false, CYFRAL_0_LOW);\n            }\n        } else {\n            if(bit_value) {\n                result = level_duration_make(true, CYFRAL_1_HI);\n            } else {\n                result = level_duration_make(true, CYFRAL_0_HI);\n            }\n        }\n    }\n\n    proto->encoder.index++;\n    if(proto->encoder.index >= (9 * 4 * 2)) {\n        proto->encoder.index = 0;\n    }\n\n    return result;\n}\n\nstatic void protocol_cyfral_render_uid(ProtocolCyfral* proto, FuriString* result) {\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) {\n        furi_string_cat_printf(result, \"%02X \", ((uint8_t*)&proto->data)[i]);\n    }\n}\n\nstatic void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) {\n    protocol_cyfral_render_uid(proto, result);\n}\n\nconst ProtocolBase ibutton_protocol_misc_cyfral = {\n    .name = \"Cyfral\",\n    .manufacturer = \"Cyfral\",\n    .data_size = CYFRAL_DATA_SIZE,\n    .alloc = (ProtocolAlloc)protocol_cyfral_alloc,\n    .free = (ProtocolFree)protocol_cyfral_free,\n    .get_data = (ProtocolGetData)protocol_cyfral_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_cyfral_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_cyfral_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield,\n        },\n    .render_uid = (ProtocolRenderData)protocol_cyfral_render_uid,\n    .render_brief_data = (ProtocolRenderData)protocol_cyfral_render_brief_data,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_cyfral.h",
    "content": "#pragma once\n#include \"toolbox/protocols/protocol.h\"\n\nextern const ProtocolBase ibutton_protocol_misc_cyfral;\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_group_misc.c",
    "content": "#include \"protocol_group_misc.h\"\n\n#include <furi_hal_rfid.h>\n#include <furi_hal_ibutton.h>\n\n#include <toolbox/protocols/protocol_dict.h>\n\n#include \"protocol_group_misc_defs.h\"\n\n#define IBUTTON_MISC_READ_TIMEOUT 100\n\n#define IBUTTON_MISC_DATA_KEY_KEY_COMMON \"Data\"\n\ntypedef struct {\n    ProtocolDict* dict;\n    ProtocolId emulate_id;\n} iButtonProtocolGroupMisc;\n\nstatic iButtonProtocolGroupMisc* ibutton_protocol_group_misc_alloc(void) {\n    iButtonProtocolGroupMisc* group = malloc(sizeof(iButtonProtocolGroupMisc));\n\n    group->dict = protocol_dict_alloc(ibutton_protocols_misc, iButtonProtocolMiscMax);\n    group->emulate_id = PROTOCOL_NO;\n\n    return group;\n}\n\nstatic void ibutton_protocol_group_misc_free(iButtonProtocolGroupMisc* group) {\n    protocol_dict_free(group->dict);\n    free(group);\n}\n\nstatic size_t ibutton_protocol_group_misc_get_max_data_size(iButtonProtocolGroupMisc* group) {\n    return protocol_dict_get_max_data_size(group->dict);\n}\n\nstatic bool ibutton_protocol_group_misc_get_id_by_name(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolLocalId* id,\n    const char* name) {\n    const ProtocolId found_id = protocol_dict_get_protocol_by_name(group->dict, name);\n\n    if(found_id != PROTOCOL_NO) {\n        *id = found_id;\n        return true;\n    }\n    return false;\n}\n\nstatic uint32_t ibutton_protocol_group_misc_get_features(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    UNUSED(id);\n    return 0;\n}\n\nstatic const char* ibutton_protocol_group_misc_get_manufacturer(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolLocalId id) {\n    return protocol_dict_get_manufacturer(group->dict, id);\n}\n\nstatic const char* ibutton_protocol_group_misc_get_name(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolLocalId id) {\n    return protocol_dict_get_name(group->dict, id);\n}\n\ntypedef struct {\n    uint32_t last_dwt_value;\n    FuriStreamBuffer* stream;\n} iButtonReadContext;\n\nstatic void ibutton_protocols_comparator_callback(bool level, void* context) {\n    iButtonReadContext* read_context = context;\n\n    uint32_t current_dwt_value = DWT->CYCCNT;\n\n    LevelDuration data =\n        level_duration_make(level, current_dwt_value - read_context->last_dwt_value);\n    furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0);\n\n    read_context->last_dwt_value = current_dwt_value;\n}\n\nstatic bool ibutton_protocol_group_misc_read(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId* id) {\n    bool result = false;\n\n    protocol_dict_decoders_start(group->dict);\n\n    furi_hal_rfid_pins_reset();\n    // pulldown pull pin, we sense the signal through the analog part of the RFID schematic\n    furi_hal_rfid_pin_pull_pulldown();\n\n    iButtonReadContext read_context = {\n        .last_dwt_value = DWT->CYCCNT,\n        .stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1),\n    };\n\n    furi_hal_rfid_comp_set_callback(ibutton_protocols_comparator_callback, &read_context);\n    furi_hal_rfid_comp_start();\n\n    const uint32_t tick_start = furi_get_tick();\n\n    for(;;) {\n        LevelDuration level;\n        size_t ret = furi_stream_buffer_receive(\n            read_context.stream, &level, sizeof(LevelDuration), IBUTTON_MISC_READ_TIMEOUT);\n\n        if((furi_get_tick() - tick_start) > IBUTTON_MISC_READ_TIMEOUT) {\n            break;\n        }\n\n        if(ret > 0) {\n            ProtocolId decoded_index = protocol_dict_decoders_feed(\n                group->dict, level_duration_get_level(level), level_duration_get_duration(level));\n\n            if(decoded_index == PROTOCOL_NO) continue;\n\n            *id = decoded_index;\n\n            protocol_dict_get_data(\n                group->dict,\n                decoded_index,\n                data,\n                protocol_dict_get_data_size(group->dict, decoded_index));\n\n            result = true;\n        }\n    }\n\n    furi_hal_rfid_comp_stop();\n    furi_hal_rfid_comp_set_callback(NULL, NULL);\n    furi_hal_rfid_pins_reset();\n\n    furi_stream_buffer_free(read_context.stream);\n\n    return result;\n}\n\nstatic void ibutton_protocol_group_misc_emulate_callback(void* context) {\n    iButtonProtocolGroupMisc* group = context;\n\n    const LevelDuration level_duration =\n        protocol_dict_encoder_yield(group->dict, group->emulate_id);\n\n    furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration));\n    furi_hal_ibutton_pin_write(level_duration_get_level(level_duration));\n}\n\nstatic void ibutton_protocol_group_misc_emulate_start(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    group->emulate_id = id;\n    protocol_dict_set_data(group->dict, id, data, protocol_dict_get_data_size(group->dict, id));\n    protocol_dict_encoder_start(group->dict, group->emulate_id);\n\n    furi_hal_ibutton_pin_configure();\n    furi_hal_ibutton_emulate_start(0, ibutton_protocol_group_misc_emulate_callback, group);\n}\n\nstatic void ibutton_protocol_group_misc_emulate_stop(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    UNUSED(data);\n    UNUSED(id);\n    furi_hal_ibutton_emulate_stop();\n    furi_hal_ibutton_pin_reset();\n}\n\nstatic bool ibutton_protocol_group_misc_save(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FlipperFormat* ff) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    return flipper_format_write_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size);\n}\n\nstatic bool ibutton_protocol_group_misc_load(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    uint32_t version,\n    FlipperFormat* ff) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    switch(version) {\n    case 1:\n    case 2:\n        return flipper_format_read_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size);\n    default:\n        return false;\n    }\n}\n\nstatic void ibutton_protocol_group_misc_render_uid(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    protocol_dict_set_data(group->dict, id, data, data_size);\n    protocol_dict_render_uid(group->dict, result, id);\n}\n\nstatic void ibutton_protocol_group_misc_render_data(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    protocol_dict_set_data(group->dict, id, data, data_size);\n    protocol_dict_render_data(group->dict, result, id);\n}\n\nstatic void ibutton_protocol_group_misc_render_brief_data(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    protocol_dict_set_data(group->dict, id, data, data_size);\n    protocol_dict_render_brief_data(group->dict, result, id);\n}\n\nstatic void ibutton_protocol_group_misc_render_error(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    FuriString* result) {\n    UNUSED(group);\n    UNUSED(data);\n    UNUSED(id);\n    UNUSED(result);\n}\n\nstatic bool ibutton_protocol_group_misc_is_valid(\n    iButtonProtocolGroupMisc* group,\n    const iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    UNUSED(group);\n    UNUSED(data);\n    UNUSED(id);\n    return true;\n}\n\nstatic void ibutton_protocol_group_misc_get_editable_data(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id,\n    iButtonEditableData* editable) {\n    editable->ptr = data;\n    editable->size = protocol_dict_get_data_size(group->dict, id);\n}\n\nstatic void ibutton_protocol_group_misc_apply_edits(\n    iButtonProtocolGroupMisc* group,\n    iButtonProtocolData* data,\n    iButtonProtocolLocalId id) {\n    const size_t data_size = protocol_dict_get_data_size(group->dict, id);\n    protocol_dict_set_data(group->dict, id, data, data_size);\n}\n\nconst iButtonProtocolGroupBase ibutton_protocol_group_misc = {\n    .protocol_count = iButtonProtocolMiscMax,\n\n    .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_misc_alloc,\n    .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_misc_free,\n\n    .get_max_data_size =\n        (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_misc_get_max_data_size,\n    .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_misc_get_id_by_name,\n    .get_features = (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_misc_get_features,\n\n    .get_manufacturer =\n        (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_manufacturer,\n    .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_name,\n\n    .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_misc_read,\n    .write_id = NULL,\n    .write_copy = NULL,\n\n    .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_start,\n    .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_stop,\n\n    .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_misc_save,\n    .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_misc_load,\n\n    .render_uid = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_uid,\n    .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_data,\n    .render_brief_data =\n        (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_brief_data,\n    .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_error,\n\n    .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_misc_is_valid,\n    .get_editable_data =\n        (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_misc_get_editable_data,\n    .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_apply_edits,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_group_misc.h",
    "content": "#pragma once\n\n#include \"../protocol_group_base.h\"\n\nextern const iButtonProtocolGroupBase ibutton_protocol_group_misc;\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_group_misc_defs.c",
    "content": "#include \"protocol_group_misc_defs.h\"\n\n#include \"protocol_cyfral.h\"\n#include \"protocol_metakom.h\"\n\nconst ProtocolBase* const ibutton_protocols_misc[] = {\n    [iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral,\n    [iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom,\n    /* Add new misc protocols here */\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_group_misc_defs.h",
    "content": "#pragma once\n\n#include <toolbox/protocols/protocol.h>\n\ntypedef enum {\n    iButtonProtocolMiscCyfral,\n    iButtonProtocolMiscMetakom,\n    iButtonProtocolMiscMax,\n} iButtonProtocolMisc;\n\nextern const ProtocolBase* const ibutton_protocols_misc[];\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_metakom.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n\n#include \"protocol_metakom.h\"\n\n#define METAKOM_DATA_SIZE sizeof(uint32_t)\n#define METAKOM_PERIOD    (125 * furi_hal_cortex_instructions_per_microsecond())\n#define METAKOM_0_LOW     (METAKOM_PERIOD * 0.33f)\n#define METAKOM_0_HI      (METAKOM_PERIOD * 0.66f)\n#define METAKOM_1_LOW     (METAKOM_PERIOD * 0.66f)\n#define METAKOM_1_HI      (METAKOM_PERIOD * 0.33f)\n\n#define METAKOM_PERIOD_SAMPLE_COUNT 10\n\ntypedef enum {\n    METAKOM_WAIT_PERIOD_SYNC,\n    METAKOM_WAIT_START_BIT,\n    METAKOM_WAIT_START_WORD,\n    METAKOM_READ_WORD,\n    METAKOM_READ_STOP_WORD,\n} MetakomState;\n\ntypedef enum {\n    METAKOM_BIT_WAIT_FRONT_HIGH,\n    METAKOM_BIT_WAIT_FRONT_LOW,\n} MetakomBitState;\n\ntypedef struct {\n    // high + low period time\n    uint32_t period_time;\n    uint32_t low_time_storage;\n    uint8_t period_sample_index;\n    uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT];\n\n    uint8_t tmp_data;\n    uint8_t tmp_counter;\n\n    uint8_t key_data_index;\n\n    MetakomBitState bit_state;\n    MetakomState state;\n} ProtocolMetakomDecoder;\n\ntypedef struct {\n    uint32_t index;\n} ProtocolMetakomEncoder;\n\ntypedef struct {\n    uint32_t data;\n\n    ProtocolMetakomDecoder decoder;\n    ProtocolMetakomEncoder encoder;\n} ProtocolMetakom;\n\nstatic ProtocolMetakom* protocol_metakom_alloc(void) {\n    ProtocolMetakom* proto = malloc(sizeof(ProtocolMetakom));\n    return (void*)proto;\n}\n\nstatic void protocol_metakom_free(ProtocolMetakom* proto) {\n    free(proto);\n}\n\nstatic uint8_t* protocol_metakom_get_data(ProtocolMetakom* proto) {\n    return (uint8_t*)&proto->data;\n}\n\nstatic void protocol_metakom_decoder_start(ProtocolMetakom* proto) {\n    ProtocolMetakomDecoder* metakom = &proto->decoder;\n\n    metakom->period_sample_index = 0;\n    metakom->period_time = 0;\n    metakom->tmp_counter = 0;\n    metakom->tmp_data = 0;\n    for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) {\n        metakom->period_sample_data[i] = 0;\n    };\n    metakom->state = METAKOM_WAIT_PERIOD_SYNC;\n    metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW;\n    metakom->key_data_index = 0;\n    metakom->low_time_storage = 0;\n\n    proto->data = 0;\n}\n\nstatic bool metakom_parity_check(uint8_t data) {\n    uint8_t ones_count = 0;\n    bool result;\n\n    for(uint8_t i = 0; i < 8; i++) {\n        if((data >> i) & 0b00000001) {\n            ones_count++;\n        }\n    }\n\n    result = (ones_count % 2 == 0);\n\n    return result;\n}\n\nstatic bool metakom_process_bit(\n    ProtocolMetakomDecoder* metakom,\n    bool polarity,\n    uint32_t time,\n    uint32_t* high_time,\n    uint32_t* low_time) {\n    bool result = false;\n\n    switch(metakom->bit_state) {\n    case METAKOM_BIT_WAIT_FRONT_LOW:\n        if(polarity == false) {\n            *low_time = metakom->low_time_storage;\n            *high_time = time;\n            result = true;\n            metakom->bit_state = METAKOM_BIT_WAIT_FRONT_HIGH;\n        }\n        break;\n    case METAKOM_BIT_WAIT_FRONT_HIGH:\n        if(polarity == true) {\n            metakom->low_time_storage = time;\n            metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW;\n        }\n        break;\n    }\n\n    return result;\n}\n\nstatic bool protocol_metakom_decoder_feed(ProtocolMetakom* proto, bool level, uint32_t duration) {\n    ProtocolMetakomDecoder* metakom = &proto->decoder;\n\n    bool ready = false;\n\n    uint32_t high_time = 0;\n    uint32_t low_time = 0;\n\n    switch(metakom->state) {\n    case METAKOM_WAIT_PERIOD_SYNC:\n        if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {\n            metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time;\n            metakom->period_sample_index++;\n\n            if(metakom->period_sample_index == METAKOM_PERIOD_SAMPLE_COUNT) {\n                for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) {\n                    metakom->period_time += metakom->period_sample_data[i];\n                };\n                metakom->period_time /= METAKOM_PERIOD_SAMPLE_COUNT;\n\n                metakom->state = METAKOM_WAIT_START_BIT;\n            }\n        }\n\n        break;\n    case METAKOM_WAIT_START_BIT:\n        if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {\n            metakom->tmp_counter++;\n            if(high_time > metakom->period_time) {\n                metakom->tmp_counter = 0;\n                metakom->state = METAKOM_WAIT_START_WORD;\n            }\n\n            if(metakom->tmp_counter > 40) {\n                protocol_metakom_decoder_start(proto);\n            }\n        }\n\n        break;\n    case METAKOM_WAIT_START_WORD:\n        if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {\n            if(low_time < (metakom->period_time / 2)) {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;\n            } else {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;\n            }\n            metakom->tmp_counter++;\n\n            if(metakom->tmp_counter == 3) {\n                if(metakom->tmp_data == 0b010) {\n                    metakom->tmp_counter = 0;\n                    metakom->tmp_data = 0;\n                    metakom->state = METAKOM_READ_WORD;\n                } else {\n                    protocol_metakom_decoder_start(proto);\n                }\n            }\n        }\n        break;\n    case METAKOM_READ_WORD:\n        if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {\n            if(low_time < (metakom->period_time / 2)) {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;\n            } else {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;\n            }\n            metakom->tmp_counter++;\n\n            if(metakom->tmp_counter == 8) {\n                if(metakom_parity_check(metakom->tmp_data)) {\n                    proto->data = (proto->data << 8) | metakom->tmp_data;\n                    metakom->key_data_index++;\n                    metakom->tmp_data = 0;\n                    metakom->tmp_counter = 0;\n\n                    if(metakom->key_data_index == 4) {\n                        // check for stop bit\n                        if(high_time > metakom->period_time) {\n                            metakom->state = METAKOM_READ_STOP_WORD;\n                        } else {\n                            protocol_metakom_decoder_start(proto);\n                        }\n                    }\n                } else {\n                    protocol_metakom_decoder_start(proto);\n                }\n            }\n        }\n        break;\n    case METAKOM_READ_STOP_WORD:\n        if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {\n            if(low_time < (metakom->period_time / 2)) {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;\n            } else {\n                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;\n            }\n            metakom->tmp_counter++;\n\n            if(metakom->tmp_counter == 3) {\n                if(metakom->tmp_data == 0b010) {\n                    ready = true;\n                } else {\n                    protocol_metakom_decoder_start(proto);\n                }\n            }\n        }\n        break;\n    }\n\n    return ready;\n}\n\nstatic bool protocol_metakom_encoder_start(ProtocolMetakom* proto) {\n    proto->encoder.index = 0;\n    return true;\n}\n\nstatic LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) {\n    LevelDuration result;\n\n    if(proto->encoder.index == 0) {\n        // sync bit\n        result = level_duration_make(false, METAKOM_PERIOD);\n    } else if(proto->encoder.index <= 6) {\n        // start word (0b010)\n        switch(proto->encoder.index) {\n        case 1:\n            result = level_duration_make(true, METAKOM_0_LOW); //-V1037\n            break;\n        case 2:\n            result = level_duration_make(false, METAKOM_0_HI); //-V1037\n            break;\n        case 3:\n            result = level_duration_make(true, METAKOM_1_LOW);\n            break;\n        case 4:\n            result = level_duration_make(false, METAKOM_1_HI);\n            break;\n        case 5:\n            result = level_duration_make(true, METAKOM_0_LOW);\n            break;\n        case 6:\n            result = level_duration_make(false, METAKOM_0_HI);\n            break;\n        }\n    } else {\n        // data\n        uint8_t data_start_index = proto->encoder.index - 7;\n        bool clock_polarity = (data_start_index) % 2;\n        uint8_t bit_index = (data_start_index) / 2;\n        bool bit_value = (proto->data >> (32 - 1 - bit_index)) & 1;\n\n        if(!clock_polarity) {\n            if(bit_value) {\n                result = level_duration_make(true, METAKOM_1_LOW);\n            } else {\n                result = level_duration_make(true, METAKOM_0_LOW);\n            }\n        } else {\n            if(bit_value) {\n                result = level_duration_make(false, METAKOM_1_HI);\n            } else {\n                result = level_duration_make(false, METAKOM_0_HI);\n            }\n        }\n    }\n\n    proto->encoder.index++;\n    if(proto->encoder.index >= (1 + 3 * 2 + 32 * 2)) {\n        proto->encoder.index = 0;\n    }\n\n    return result;\n}\n\nstatic void protocol_metakom_render_uid(ProtocolMetakom* proto, FuriString* result) {\n    furi_string_cat_printf(result, \"ID: \");\n    for(size_t i = 0; i < METAKOM_DATA_SIZE; ++i) {\n        furi_string_cat_printf(result, \"%02X \", ((uint8_t*)&proto->data)[i]);\n    }\n}\n\nstatic void protocol_metakom_render_brief_data(ProtocolMetakom* proto, FuriString* result) {\n    protocol_metakom_render_uid(proto, result);\n}\n\nconst ProtocolBase ibutton_protocol_misc_metakom = {\n    .name = \"Metakom\",\n    .manufacturer = \"Metakom\",\n    .data_size = METAKOM_DATA_SIZE,\n    .alloc = (ProtocolAlloc)protocol_metakom_alloc,\n    .free = (ProtocolFree)protocol_metakom_free,\n    .get_data = (ProtocolGetData)protocol_metakom_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_metakom_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_metakom_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_metakom_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield,\n        },\n    .render_uid = (ProtocolRenderData)protocol_metakom_render_uid,\n    .render_brief_data = (ProtocolRenderData)protocol_metakom_render_brief_data,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/misc/protocol_metakom.h",
    "content": "#pragma once\n#include \"toolbox/protocols/protocol.h\"\n\nextern const ProtocolBase ibutton_protocol_misc_metakom;\n"
  },
  {
    "path": "lib/ibutton/protocols/protocol_common.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\ntypedef int32_t iButtonProtocolId;\n\nenum {\n    iButtonProtocolIdInvalid = -1,\n};\n\ntypedef enum {\n    iButtonProtocolFeatureExtData = (1U << 0),\n    iButtonProtocolFeatureWriteId = (1U << 1),\n    iButtonProtocolFeatureWriteCopy = (1U << 2),\n} iButtonProtocolFeature;\n\ntypedef struct {\n    uint8_t* ptr;\n    size_t size;\n} iButtonEditableData;\n"
  },
  {
    "path": "lib/ibutton/protocols/protocol_common_i.h",
    "content": "#pragma once\n\n#include \"protocol_common.h\"\n\ntypedef void iButtonProtocolData;\ntypedef int32_t iButtonProtocolLocalId;\n"
  },
  {
    "path": "lib/ibutton/protocols/protocol_group_base.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <flipper_format.h>\n\n#include \"protocol_common_i.h\"\n\ntypedef void iButtonProtocolGroupData;\ntypedef int32_t iButtonProtocolGroupId;\n\ntypedef iButtonProtocolGroupData* (*iButtonProtocolGroupAllocFunc)(void);\n\ntypedef void (*iButtonProtocolGroupFreeFunc)(iButtonProtocolGroupData*);\n\ntypedef void (*iButtonProtocolGroupRenderFunc)(\n    iButtonProtocolGroupData*,\n    const iButtonProtocolData*,\n    iButtonProtocolLocalId,\n    FuriString*);\n\ntypedef bool (*iButtonProtocolGroupIsValidFunc)(\n    iButtonProtocolGroupData*,\n    const iButtonProtocolData*,\n    iButtonProtocolLocalId);\n\ntypedef void (*iButtonProtocolGroupGetDataFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolData*,\n    iButtonProtocolLocalId,\n    iButtonEditableData*);\n\ntypedef void (*iButtonProtocolGroupApplyFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolData*,\n    iButtonProtocolLocalId);\n\ntypedef size_t (*iButtonProtocolGropuGetSizeFunc)(iButtonProtocolGroupData*);\n\ntypedef uint32_t (\n    *iButtonProtocolGroupGetFeaturesFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId);\n\ntypedef const char* (\n    *iButtonProtocolGroupGetStringFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId);\n\ntypedef bool (*iButtonProtocolGroupGetIdFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolLocalId*,\n    const char*);\n\ntypedef bool (*iButtonProtocolGroupReadFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolData*,\n    iButtonProtocolLocalId*);\n\ntypedef bool (*iButtonProtocolGroupWriteFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolData*,\n    iButtonProtocolLocalId);\n\ntypedef bool (*iButtonProtocolGroupSaveFunc)(\n    iButtonProtocolGroupData*,\n    const iButtonProtocolData*,\n    iButtonProtocolLocalId,\n    FlipperFormat*);\n\ntypedef bool (*iButtonProtocolGroupLoadFunc)(\n    iButtonProtocolGroupData*,\n    iButtonProtocolData*,\n    iButtonProtocolLocalId,\n    uint32_t,\n    FlipperFormat*);\n\ntypedef struct {\n    const uint32_t protocol_count;\n\n    iButtonProtocolGroupAllocFunc alloc;\n    iButtonProtocolGroupFreeFunc free;\n\n    iButtonProtocolGropuGetSizeFunc get_max_data_size;\n    iButtonProtocolGroupGetIdFunc get_id_by_name;\n    iButtonProtocolGroupGetFeaturesFunc get_features;\n\n    iButtonProtocolGroupGetStringFunc get_manufacturer;\n    iButtonProtocolGroupGetStringFunc get_name;\n\n    iButtonProtocolGroupReadFunc read;\n    iButtonProtocolGroupWriteFunc write_id;\n    iButtonProtocolGroupWriteFunc write_copy;\n\n    iButtonProtocolGroupApplyFunc emulate_start;\n    iButtonProtocolGroupApplyFunc emulate_stop;\n\n    iButtonProtocolGroupSaveFunc save;\n    iButtonProtocolGroupLoadFunc load;\n\n    iButtonProtocolGroupRenderFunc render_uid;\n    iButtonProtocolGroupRenderFunc render_data;\n    iButtonProtocolGroupRenderFunc render_brief_data;\n    iButtonProtocolGroupRenderFunc render_error;\n\n    iButtonProtocolGroupIsValidFunc is_valid;\n    iButtonProtocolGroupGetDataFunc get_editable_data;\n\n    iButtonProtocolGroupApplyFunc apply_edits;\n} iButtonProtocolGroupBase;\n"
  },
  {
    "path": "lib/ibutton/protocols/protocol_group_defs.c",
    "content": "#include \"protocol_group_defs.h\"\n\n#include \"dallas/protocol_group_dallas.h\"\n#include \"misc/protocol_group_misc.h\"\n\nconst iButtonProtocolGroupBase* const ibutton_protocol_groups[] = {\n    [iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas,\n    [iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc,\n};\n"
  },
  {
    "path": "lib/ibutton/protocols/protocol_group_defs.h",
    "content": "#pragma once\n\n#include \"protocol_group_base.h\"\n\ntypedef enum {\n    iButtonProtocolGroupDallas,\n    iButtonProtocolGroupMisc,\n    iButtonProtocolGroupMax\n} iButtonProtocolGroup;\n\nextern const iButtonProtocolGroupBase* const ibutton_protocol_groups[];\n"
  },
  {
    "path": "lib/ieee754_parse_wrap/SConscript",
    "content": "Import(\"env\")\n\nwrapped_fn_list = [\n    \"strtof\",\n    \"strtod\",\n]\n\nfor wrapped_fn in wrapped_fn_list:\n    env.Append(\n        LINKFLAGS=[\n            \"-Wl,--wrap,\" + wrapped_fn,\n        ]\n    )\n\nenv.Append(\n    SDK_HEADERS=[\n        File(\"wrappers.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"ieee754_parse_wrap\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\", \".\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/ieee754_parse_wrap/wrappers.c",
    "content": "#include \"wrappers.h\"\n\n// Based on the disassembly, providing NULL as `locale` is fine.\n// The default `strtof` and `strtod` provided in the same libc_nano also just\n// call these functions, but with an actual locale structure which was taking up\n// lots of .data space (364 bytes).\n\nfloat __wrap_strtof(const char* in, char** tail) {\n    return strtof_l(in, tail, NULL);\n}\n\ndouble __wrap_strtod(const char* in, char** tail) {\n    return strtod_l(in, tail, NULL);\n}\n"
  },
  {
    "path": "lib/ieee754_parse_wrap/wrappers.h",
    "content": "#pragma once\n\n#include <stdlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nfloat __wrap_strtof(const char* in, char** tail);\ndouble __wrap_strtod(const char* in, char** tail);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/infrared/encoder_decoder\",\n        \"#/lib/infrared/worker\",\n        \"#/lib/infrared/signal\",\n    ],\n    SDK_HEADERS=[\n        File(\"encoder_decoder/infrared.h\"),\n        File(\"worker/infrared_worker.h\"),\n        File(\"worker/infrared_transmit.h\"),\n        File(\"signal/infrared_error_code.h\"),\n        File(\"signal/infrared_signal.h\"),\n        File(\"signal/infrared_brute_force.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"infrared\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/common/infrared_common_decoder.c",
    "content": "#include \"infrared_common_i.h\"\n\n#include <stdlib.h>\n#include <core/check.h>\n#include <core/common_defines.h>\n\nstatic void infrared_common_decoder_reset_state(InfraredCommonDecoder* decoder);\n\nstatic inline size_t consume_samples(uint32_t* array, size_t len, size_t shift) {\n    furi_assert(len >= shift);\n    len -= shift;\n    for(size_t i = 0; i < len; ++i) {\n        array[i] = array[i + shift];\n    }\n\n    return len;\n}\n\nstatic inline void accumulate_lsb(InfraredCommonDecoder* decoder, bool bit) {\n    uint16_t index = decoder->databit_cnt / 8;\n    uint8_t shift = decoder->databit_cnt % 8; // LSB first\n\n    if(!shift) decoder->data[index] = 0;\n\n    if(bit) {\n        decoder->data[index] |= (0x1 << shift); // add 1\n    } else {\n        (void)decoder->data[index]; // add 0\n    }\n\n    ++decoder->databit_cnt;\n}\n\nstatic bool infrared_check_preamble(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n    bool start_level = (decoder->level + decoder->timings_cnt + 1) % 2;\n\n    if(decoder->timings_cnt == 0) return false;\n\n    // align to start at Mark timing\n    if(!start_level) {\n        decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1);\n    }\n\n    if(decoder->protocol->timings.preamble_mark == 0) {\n        return true;\n    }\n\n    while((!result) && (decoder->timings_cnt >= 2)) {\n        float preamble_tolerance = decoder->protocol->timings.preamble_tolerance;\n        uint16_t preamble_mark = decoder->protocol->timings.preamble_mark;\n        uint16_t preamble_space = decoder->protocol->timings.preamble_space;\n\n        if((MATCH_TIMING(decoder->timings[0], preamble_mark, preamble_tolerance)) &&\n           (MATCH_TIMING(decoder->timings[1], preamble_space, preamble_tolerance))) {\n            result = true;\n        }\n\n        decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 2);\n    }\n\n    return result;\n}\n\n/**\n * decoder->protocol->databit_len[0] contains biggest amount of bits, for this protocol.\n * decoder->protocol->databit_len[1...] contains lesser values, but which can be decoded\n * for some protocol modifications.\n */\nstatic InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    InfraredStatus status = InfraredStatusOk;\n    const InfraredTimings* timings = &decoder->protocol->timings;\n\n    while(decoder->timings_cnt && (status == InfraredStatusOk)) {\n        bool level = (decoder->level + decoder->timings_cnt + 1) % 2;\n        uint32_t timing = decoder->timings[0];\n\n        if(timings->min_split_time && !level) {\n            if(timing > timings->min_split_time) {\n                /* long low timing - check if we're ready for any of protocol modification */\n                for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) &&\n                                  decoder->protocol->databit_len[i];\n                    ++i) {\n                    if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {\n                        return InfraredStatusReady;\n                    }\n                }\n            } else if(decoder->protocol->databit_len[0] == decoder->databit_cnt) {\n                /* short low timing for longest protocol - this is signal is longer than we expected */\n                return InfraredStatusError;\n            }\n        }\n\n        status = decoder->protocol->decode(decoder, level, timing);\n        furi_check(decoder->databit_cnt <= decoder->protocol->databit_len[0]);\n        furi_assert(status == InfraredStatusError || status == InfraredStatusOk);\n        if(status == InfraredStatusError) {\n            break;\n        }\n        decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1);\n\n        /* check if largest protocol version can be decoded */\n        if(level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && //-V1051\n           !timings->min_split_time) {\n            status = InfraredStatusReady;\n            break;\n        }\n    }\n\n    return status;\n}\n\n/* Pulse Distance-Width Modulation */\nInfraredStatus\n    infrared_common_decode_pdwm(InfraredCommonDecoder* decoder, bool level, uint32_t timing) {\n    furi_assert(decoder);\n\n    InfraredStatus status = InfraredStatusOk;\n    uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance;\n    uint16_t bit1_mark = decoder->protocol->timings.bit1_mark;\n    uint16_t bit1_space = decoder->protocol->timings.bit1_space;\n    uint16_t bit0_mark = decoder->protocol->timings.bit0_mark;\n    uint16_t bit0_space = decoder->protocol->timings.bit0_space;\n\n    bool analyze_timing = level ^ (bit1_mark == bit0_mark);\n    uint16_t bit1 = level ? bit1_mark : bit1_space;\n    uint16_t bit0 = level ? bit0_mark : bit0_space;\n    uint16_t no_info_timing = (bit1_mark == bit0_mark) ? bit1_mark : bit1_space;\n\n    if(analyze_timing) {\n        if(MATCH_TIMING(timing, bit1, bit_tolerance)) {\n            accumulate_lsb(decoder, 1);\n        } else if(MATCH_TIMING(timing, bit0, bit_tolerance)) {\n            accumulate_lsb(decoder, 0);\n        } else {\n            status = InfraredStatusError;\n        }\n    } else {\n        if(!MATCH_TIMING(timing, no_info_timing, bit_tolerance)) {\n            status = InfraredStatusError;\n        }\n    }\n\n    return status;\n}\n\n/* level switch detection goes in middle of time-quant */\nInfraredStatus\n    infrared_common_decode_manchester(InfraredCommonDecoder* decoder, bool level, uint32_t timing) {\n    furi_assert(decoder);\n    uint32_t bit = decoder->protocol->timings.bit1_mark;\n    uint32_t tolerance = decoder->protocol->timings.bit_tolerance;\n\n    bool* switch_detect = &decoder->switch_detect;\n    furi_assert((*switch_detect == true) || (*switch_detect == false));\n\n    bool single_timing = MATCH_TIMING(timing, bit, tolerance);\n    bool double_timing = MATCH_TIMING(timing, 2 * bit, tolerance);\n\n    if(!single_timing && !double_timing) {\n        return InfraredStatusError;\n    }\n\n    if(decoder->protocol->manchester_start_from_space && (decoder->databit_cnt == 0)) {\n        *switch_detect = 1; /* fake as we were previously in the middle of time-quant */\n        accumulate_lsb(decoder, 0);\n    }\n\n    if(*switch_detect == 0) {\n        if(double_timing) {\n            return InfraredStatusError;\n        }\n        /* only single timing - level switch required in the middle of time-quant */\n        *switch_detect = 1;\n    } else {\n        /* double timing means we're in the middle of time-quant again */\n        if(single_timing) *switch_detect = 0;\n    }\n\n    if(*switch_detect) {\n        if(decoder->protocol->databit_len[0] == decoder->databit_cnt) {\n            return InfraredStatusError;\n        }\n        accumulate_lsb(decoder, level);\n    }\n\n    return InfraredStatusOk;\n}\n\nInfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* decoder) {\n    InfraredMessage* message = NULL;\n    bool found_length = false;\n\n    for(size_t i = 0;\n        i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i];\n        ++i) {\n        if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {\n            found_length = true;\n            break;\n        }\n    }\n\n    if(found_length && decoder->protocol->interpret(decoder)) {\n        decoder->databit_cnt = 0;\n        message = &decoder->message;\n        if(decoder->protocol->decode_repeat) {\n            decoder->state = InfraredCommonDecoderStateProcessRepeat;\n        } else {\n            decoder->state = InfraredCommonDecoderStateWaitPreamble;\n        }\n    }\n\n    return message;\n}\n\nInfraredMessage*\n    infrared_common_decode(InfraredCommonDecoder* decoder, bool level, uint32_t duration) {\n    furi_assert(decoder);\n\n    InfraredMessage* message = 0;\n    InfraredStatus status = InfraredStatusError;\n\n    if(decoder->level == level) {\n        infrared_common_decoder_reset(decoder);\n    }\n    decoder->level = level; // start with low level (Space timing)\n\n    decoder->timings[decoder->timings_cnt] = duration;\n    decoder->timings_cnt++;\n    furi_check(decoder->timings_cnt <= sizeof(decoder->timings));\n\n    while(1) {\n        switch(decoder->state) {\n        case InfraredCommonDecoderStateWaitPreamble:\n            if(infrared_check_preamble(decoder)) {\n                decoder->state = InfraredCommonDecoderStateDecode;\n                decoder->databit_cnt = 0;\n                decoder->switch_detect = false;\n                continue;\n            }\n            break;\n        case InfraredCommonDecoderStateDecode:\n            status = infrared_common_decode_bits(decoder);\n            if(status == InfraredStatusReady) {\n                message = infrared_common_decoder_check_ready(decoder);\n                if(message) {\n                    continue;\n                } else if(decoder->protocol->databit_len[0] == decoder->databit_cnt) {\n                    /* error: can't decode largest protocol - begin decoding from start */\n                    decoder->state = InfraredCommonDecoderStateWaitPreamble;\n                }\n            } else if(status == InfraredStatusError) {\n                infrared_common_decoder_reset_state(decoder);\n                continue;\n            }\n            break;\n        case InfraredCommonDecoderStateProcessRepeat:\n            status = decoder->protocol->decode_repeat(decoder);\n            if(status == InfraredStatusError) {\n                infrared_common_decoder_reset_state(decoder);\n                continue;\n            } else if(status == InfraredStatusReady) {\n                decoder->message.repeat = true;\n                message = &decoder->message;\n            }\n            break;\n        }\n        break;\n    }\n\n    return message;\n}\n\nvoid* infrared_common_decoder_alloc(const InfraredCommonProtocolSpec* protocol) {\n    furi_assert(protocol);\n\n    /* protocol->databit_len[0] has to contain biggest value of bits that can be decoded */\n    for(size_t i = 1; i < COUNT_OF(protocol->databit_len); ++i) {\n        furi_assert(protocol->databit_len[i] <= protocol->databit_len[0]);\n    }\n\n    uint32_t alloc_size = sizeof(InfraredCommonDecoder) + protocol->databit_len[0] / 8 +\n                          !!(protocol->databit_len[0] % 8);\n    InfraredCommonDecoder* decoder = malloc(alloc_size);\n    decoder->protocol = protocol;\n    decoder->level = true;\n    return decoder;\n}\n\nvoid infrared_common_decoder_free(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n    free(decoder);\n}\n\nvoid infrared_common_decoder_reset_state(InfraredCommonDecoder* decoder) {\n    decoder->state = InfraredCommonDecoderStateWaitPreamble;\n    decoder->databit_cnt = 0;\n    decoder->switch_detect = false;\n    decoder->message.protocol = InfraredProtocolUnknown;\n    if(decoder->protocol->timings.preamble_mark == 0) {\n        if(decoder->timings_cnt > 0) {\n            decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1);\n        }\n    }\n}\n\nvoid infrared_common_decoder_reset(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    infrared_common_decoder_reset_state(decoder);\n    decoder->timings_cnt = 0;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/common/infrared_common_encoder.c",
    "content": "#include \"infrared_common_i.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <core/check.h>\n#include <core/common_defines.h>\n\nstatic InfraredStatus\n    infrared_common_encode_bits(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) {\n    InfraredStatus status = encoder->protocol->encode(encoder, duration, level);\n    furi_assert(status == InfraredStatusOk);\n    ++encoder->timings_encoded;\n    encoder->timings_sum += *duration;\n    if((encoder->bits_encoded == encoder->bits_to_encode) && *level) {\n        status = InfraredStatusDone;\n    }\n\n    return status;\n}\n\n/*\n *\n * 3:\n *      even_timing = 0\n *      level = 0 ^ 1 = 1\n * 4:\n *      even_timing = 1\n *      level = 1 ^ 1 = 0\n *      ++timing;\n *\n *\n *   0     1     2 | 3  4 |\n * _____-------_____---___\n*/\nInfraredStatus infrared_common_encode_manchester(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level) {\n    furi_assert(encoder);\n    furi_assert(duration);\n    furi_assert(level);\n\n    const InfraredTimings* timings = &encoder->protocol->timings;\n    uint8_t index = encoder->bits_encoded / 8;\n    uint8_t shift = encoder->bits_encoded % 8; // LSB first\n    bool logic_value = !!(encoder->data[index] & (0x01 << shift));\n    bool even_timing = !(encoder->timings_encoded % 2);\n\n    *level = even_timing ^ logic_value;\n    *duration = timings->bit1_mark;\n    if(even_timing)\n        ++encoder->bits_encoded;\n    else if(*level && (encoder->bits_encoded + 1 == encoder->bits_to_encode))\n        ++encoder->bits_encoded; /* don't encode last space */\n\n    return InfraredStatusOk;\n}\n\nInfraredStatus\n    infrared_common_encode_pdwm(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) {\n    furi_assert(encoder);\n    furi_assert(duration);\n    furi_assert(level);\n\n    const InfraredTimings* timings = &encoder->protocol->timings;\n    uint8_t index = encoder->bits_encoded / 8;\n    uint8_t shift = encoder->bits_encoded % 8; // LSB first\n    bool logic_value = !!(encoder->data[index] & (0x01 << shift));\n    bool pwm = timings->bit1_space == timings->bit0_space;\n\n    if(encoder->timings_encoded % 2) { /* start encoding from space */\n        *duration = logic_value ? timings->bit1_mark : timings->bit0_mark;\n        *level = true;\n        if(pwm) ++encoder->bits_encoded;\n    } else {\n        *duration = logic_value ? timings->bit1_space : timings->bit0_space;\n        *level = false;\n        if(!pwm) ++encoder->bits_encoded;\n    }\n\n    return InfraredStatusOk;\n}\n\nInfraredStatus\n    infrared_common_encode(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) {\n    furi_assert(encoder);\n    furi_assert(duration);\n    furi_assert(level);\n    InfraredStatus status = InfraredStatusOk;\n    const InfraredTimings* timings = &encoder->protocol->timings;\n\n    switch(encoder->state) {\n    case InfraredCommonEncoderStateSilence:\n        *duration = encoder->protocol->timings.silence_time;\n        *level = false;\n        encoder->state = InfraredCommonEncoderStatePreamble;\n        ++encoder->timings_encoded;\n        encoder->timings_sum = 0;\n        break;\n    case InfraredCommonEncoderStatePreamble:\n        if(timings->preamble_mark) {\n            if(encoder->timings_encoded == 1) {\n                *duration = timings->preamble_mark;\n                *level = true;\n            } else {\n                *duration = timings->preamble_space;\n                *level = false;\n                encoder->state = InfraredCommonEncoderStateEncode;\n            }\n            ++encoder->timings_encoded;\n            encoder->timings_sum += *duration;\n            break;\n        } else {\n            encoder->state = InfraredCommonEncoderStateEncode;\n        }\n        /* FALLTHROUGH */\n    case InfraredCommonEncoderStateEncode:\n        status = infrared_common_encode_bits(encoder, duration, level);\n        if(status == InfraredStatusDone) {\n            if(encoder->protocol->encode_repeat) {\n                encoder->state = InfraredCommonEncoderStateEncodeRepeat;\n            } else {\n                encoder->timings_encoded = 0;\n                encoder->timings_sum = 0;\n                encoder->bits_encoded = 0;\n                encoder->switch_detect = 0;\n                encoder->state = InfraredCommonEncoderStateSilence;\n            }\n        }\n        break;\n    case InfraredCommonEncoderStateEncodeRepeat:\n        status = encoder->protocol->encode_repeat(encoder, duration, level);\n        break;\n    }\n    return status;\n}\n\nvoid* infrared_common_encoder_alloc(const InfraredCommonProtocolSpec* protocol) {\n    furi_assert(protocol);\n    if(protocol->decode == infrared_common_decode_pdwm) {\n        furi_assert(\n            (protocol->timings.bit1_mark == protocol->timings.bit0_mark) ^\n            (protocol->timings.bit1_space == protocol->timings.bit0_space));\n    }\n\n    /* protocol->databit_len[0] has to contain biggest value of bits that can be decoded */\n    for(size_t i = 1; i < COUNT_OF(protocol->databit_len); ++i) {\n        furi_assert(protocol->databit_len[i] <= protocol->databit_len[0]);\n    }\n\n    uint32_t alloc_size = sizeof(InfraredCommonDecoder) + protocol->databit_len[0] / 8 +\n                          !!(protocol->databit_len[0] % 8);\n    InfraredCommonEncoder* encoder = malloc(alloc_size);\n    memset(encoder, 0, alloc_size);\n    encoder->protocol = protocol;\n\n    return encoder;\n}\n\nvoid infrared_common_encoder_free(InfraredCommonEncoder* encoder) {\n    furi_assert(encoder);\n    free(encoder);\n}\n\nvoid infrared_common_encoder_reset(InfraredCommonEncoder* encoder) {\n    furi_assert(encoder);\n    encoder->timings_encoded = 0;\n    encoder->timings_sum = 0;\n    encoder->bits_encoded = 0;\n    encoder->state = InfraredCommonEncoderStateSilence;\n    encoder->switch_detect = 0;\n\n    uint8_t max_databit_len = 0;\n\n    for(size_t i = 0; i < COUNT_OF(encoder->protocol->databit_len); ++i) {\n        max_databit_len = MAX(max_databit_len, encoder->protocol->databit_len[i]);\n    }\n\n    uint8_t bytes_to_clear = max_databit_len / 8 + !!(max_databit_len % 8);\n    memset(encoder->data, 0, bytes_to_clear);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/common/infrared_common_i.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include \"infrared.h\"\n#include \"infrared_i.h\"\n\n#define MATCH_TIMING(x, v, delta) (((x) < ((v) + (delta))) && ((x) > ((v) - (delta))))\n\ntypedef struct InfraredCommonDecoder InfraredCommonDecoder;\ntypedef struct InfraredCommonEncoder InfraredCommonEncoder;\n\ntypedef InfraredStatus (*InfraredCommonDecode)(InfraredCommonDecoder*, bool, uint32_t);\ntypedef InfraredStatus (*InfraredCommonDecodeRepeat)(InfraredCommonDecoder*);\ntypedef bool (*InfraredCommonInterpret)(InfraredCommonDecoder*);\ntypedef InfraredStatus (\n    *InfraredCommonEncode)(InfraredCommonEncoder* encoder, uint32_t* out, bool* polarity);\n\ntypedef struct {\n    InfraredTimings timings;\n    bool manchester_start_from_space;\n    uint8_t databit_len[4];\n    InfraredCommonDecode decode;\n    InfraredCommonDecodeRepeat decode_repeat;\n    InfraredCommonInterpret interpret;\n    InfraredCommonEncode encode;\n    InfraredCommonEncode encode_repeat;\n} InfraredCommonProtocolSpec;\n\ntypedef enum {\n    InfraredCommonDecoderStateWaitPreamble,\n    InfraredCommonDecoderStateDecode,\n    InfraredCommonDecoderStateProcessRepeat,\n} InfraredCommonStateDecoder;\n\ntypedef enum {\n    InfraredCommonEncoderStateSilence,\n    InfraredCommonEncoderStatePreamble,\n    InfraredCommonEncoderStateEncode,\n    InfraredCommonEncoderStateEncodeRepeat,\n} InfraredCommonStateEncoder;\n\nstruct InfraredCommonDecoder {\n    const InfraredCommonProtocolSpec* protocol;\n    void* context;\n    uint32_t timings[6];\n    InfraredMessage message;\n    InfraredCommonStateDecoder state;\n    uint8_t timings_cnt;\n    bool switch_detect;\n    bool level;\n    uint16_t databit_cnt;\n    uint8_t data[];\n};\n\nstruct InfraredCommonEncoder {\n    const InfraredCommonProtocolSpec* protocol;\n    InfraredCommonStateEncoder state;\n    bool switch_detect;\n    uint8_t bits_to_encode;\n    uint8_t bits_encoded;\n    uint32_t timings_sum;\n    uint32_t timings_encoded;\n    void* context;\n    uint8_t data[];\n};\n\nInfraredMessage*\n    infrared_common_decode(InfraredCommonDecoder* decoder, bool level, uint32_t duration);\nInfraredStatus\n    infrared_common_decode_pdwm(InfraredCommonDecoder* decoder, bool level, uint32_t timing);\nInfraredStatus\n    infrared_common_decode_manchester(InfraredCommonDecoder* decoder, bool level, uint32_t timing);\nvoid* infrared_common_decoder_alloc(const InfraredCommonProtocolSpec* protocol);\nvoid infrared_common_decoder_free(InfraredCommonDecoder* decoder);\nvoid infrared_common_decoder_reset(InfraredCommonDecoder* decoder);\nInfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* decoder);\n\nInfraredStatus\n    infrared_common_encode(InfraredCommonEncoder* encoder, uint32_t* duration, bool* polarity);\nInfraredStatus\n    infrared_common_encode_pdwm(InfraredCommonEncoder* encoder, uint32_t* duration, bool* polarity);\nInfraredStatus infrared_common_encode_manchester(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* polarity);\nvoid* infrared_common_encoder_alloc(const InfraredCommonProtocolSpec* protocol);\nvoid infrared_common_encoder_free(InfraredCommonEncoder* encoder);\nvoid infrared_common_encoder_reset(InfraredCommonEncoder* encoder);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/infrared.c",
    "content": "#include \"infrared.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <core/check.h>\n#include <core/common_defines.h>\n\n#include \"nec/infrared_protocol_nec.h\"\n#include \"samsung/infrared_protocol_samsung.h\"\n#include \"rc5/infrared_protocol_rc5.h\"\n#include \"rc6/infrared_protocol_rc6.h\"\n#include \"sirc/infrared_protocol_sirc.h\"\n#include \"kaseikyo/infrared_protocol_kaseikyo.h\"\n#include \"rca/infrared_protocol_rca.h\"\n#include \"pioneer/infrared_protocol_pioneer.h\"\n\ntypedef struct {\n    InfraredAlloc alloc;\n    InfraredDecode decode;\n    InfraredDecoderReset reset;\n    InfraredFree free;\n    InfraredDecoderCheckReady check_ready;\n} InfraredDecoders;\n\ntypedef struct {\n    InfraredAlloc alloc;\n    InfraredEncode encode;\n    InfraredEncoderReset reset;\n    InfraredFree free;\n} InfraredEncoders;\n\nstruct InfraredDecoderHandler {\n    void** ctx;\n};\n\nstruct InfraredEncoderHandler {\n    void* handler;\n    const InfraredEncoders* encoder;\n};\n\ntypedef struct {\n    InfraredEncoders encoder;\n    InfraredDecoders decoder;\n    InfraredGetProtocolVariant get_protocol_variant;\n} InfraredEncoderDecoder;\n\nstatic const InfraredEncoderDecoder infrared_encoder_decoder[] = {\n    {\n        .decoder =\n            {.alloc = infrared_decoder_nec_alloc,\n             .decode = infrared_decoder_nec_decode,\n             .reset = infrared_decoder_nec_reset,\n             .check_ready = infrared_decoder_nec_check_ready,\n             .free = infrared_decoder_nec_free},\n        .encoder =\n            {.alloc = infrared_encoder_nec_alloc,\n             .encode = infrared_encoder_nec_encode,\n             .reset = infrared_encoder_nec_reset,\n             .free = infrared_encoder_nec_free},\n        .get_protocol_variant = infrared_protocol_nec_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_samsung32_alloc,\n             .decode = infrared_decoder_samsung32_decode,\n             .reset = infrared_decoder_samsung32_reset,\n             .check_ready = infrared_decoder_samsung32_check_ready,\n             .free = infrared_decoder_samsung32_free},\n        .encoder =\n            {.alloc = infrared_encoder_samsung32_alloc,\n             .encode = infrared_encoder_samsung32_encode,\n             .reset = infrared_encoder_samsung32_reset,\n             .free = infrared_encoder_samsung32_free},\n        .get_protocol_variant = infrared_protocol_samsung32_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_rc5_alloc,\n             .decode = infrared_decoder_rc5_decode,\n             .reset = infrared_decoder_rc5_reset,\n             .check_ready = infrared_decoder_rc5_check_ready,\n             .free = infrared_decoder_rc5_free},\n        .encoder =\n            {.alloc = infrared_encoder_rc5_alloc,\n             .encode = infrared_encoder_rc5_encode,\n             .reset = infrared_encoder_rc5_reset,\n             .free = infrared_encoder_rc5_free},\n        .get_protocol_variant = infrared_protocol_rc5_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_rc6_alloc,\n             .decode = infrared_decoder_rc6_decode,\n             .reset = infrared_decoder_rc6_reset,\n             .check_ready = infrared_decoder_rc6_check_ready,\n             .free = infrared_decoder_rc6_free},\n        .encoder =\n            {.alloc = infrared_encoder_rc6_alloc,\n             .encode = infrared_encoder_rc6_encode,\n             .reset = infrared_encoder_rc6_reset,\n             .free = infrared_encoder_rc6_free},\n        .get_protocol_variant = infrared_protocol_rc6_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_sirc_alloc,\n             .decode = infrared_decoder_sirc_decode,\n             .reset = infrared_decoder_sirc_reset,\n             .check_ready = infrared_decoder_sirc_check_ready,\n             .free = infrared_decoder_sirc_free},\n        .encoder =\n            {.alloc = infrared_encoder_sirc_alloc,\n             .encode = infrared_encoder_sirc_encode,\n             .reset = infrared_encoder_sirc_reset,\n             .free = infrared_encoder_sirc_free},\n        .get_protocol_variant = infrared_protocol_sirc_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_pioneer_alloc,\n             .decode = infrared_decoder_pioneer_decode,\n             .reset = infrared_decoder_pioneer_reset,\n             .check_ready = infrared_decoder_pioneer_check_ready,\n             .free = infrared_decoder_pioneer_free},\n        .encoder =\n            {.alloc = infrared_encoder_pioneer_alloc,\n             .encode = infrared_encoder_pioneer_encode,\n             .reset = infrared_encoder_pioneer_reset,\n             .free = infrared_encoder_pioneer_free},\n        .get_protocol_variant = infrared_protocol_pioneer_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_kaseikyo_alloc,\n             .decode = infrared_decoder_kaseikyo_decode,\n             .reset = infrared_decoder_kaseikyo_reset,\n             .check_ready = infrared_decoder_kaseikyo_check_ready,\n             .free = infrared_decoder_kaseikyo_free},\n        .encoder =\n            {.alloc = infrared_encoder_kaseikyo_alloc,\n             .encode = infrared_encoder_kaseikyo_encode,\n             .reset = infrared_encoder_kaseikyo_reset,\n             .free = infrared_encoder_kaseikyo_free},\n        .get_protocol_variant = infrared_protocol_kaseikyo_get_variant,\n    },\n    {\n        .decoder =\n            {.alloc = infrared_decoder_rca_alloc,\n             .decode = infrared_decoder_rca_decode,\n             .reset = infrared_decoder_rca_reset,\n             .check_ready = infrared_decoder_rca_check_ready,\n             .free = infrared_decoder_rca_free},\n        .encoder =\n            {.alloc = infrared_encoder_rca_alloc,\n             .encode = infrared_encoder_rca_encode,\n             .reset = infrared_encoder_rca_reset,\n             .free = infrared_encoder_rca_free},\n        .get_protocol_variant = infrared_protocol_rca_get_variant,\n    },\n};\n\nstatic int infrared_find_index_by_protocol(InfraredProtocol protocol);\nstatic const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol);\n\nconst InfraredMessage*\n    infrared_decode(InfraredDecoderHandler* handler, bool level, uint32_t duration) {\n    furi_check(handler);\n\n    InfraredMessage* message = NULL;\n    InfraredMessage* result = NULL;\n\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        if(infrared_encoder_decoder[i].decoder.decode) {\n            message = infrared_encoder_decoder[i].decoder.decode(handler->ctx[i], level, duration);\n            if(!result && message) {\n                result = message;\n            }\n        }\n    }\n\n    return result;\n}\n\nInfraredDecoderHandler* infrared_alloc_decoder(void) {\n    InfraredDecoderHandler* handler = malloc(sizeof(InfraredDecoderHandler));\n    handler->ctx = malloc(sizeof(void*) * COUNT_OF(infrared_encoder_decoder));\n\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        handler->ctx[i] = 0;\n        if(infrared_encoder_decoder[i].decoder.alloc)\n            handler->ctx[i] = infrared_encoder_decoder[i].decoder.alloc();\n    }\n\n    infrared_reset_decoder(handler);\n    return handler;\n}\n\nvoid infrared_free_decoder(InfraredDecoderHandler* handler) {\n    furi_check(handler);\n    furi_check(handler->ctx);\n\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        if(infrared_encoder_decoder[i].decoder.free)\n            infrared_encoder_decoder[i].decoder.free(handler->ctx[i]);\n    }\n\n    free(handler->ctx);\n    free(handler);\n}\n\nvoid infrared_reset_decoder(InfraredDecoderHandler* handler) {\n    furi_check(handler);\n\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        if(infrared_encoder_decoder[i].decoder.reset)\n            infrared_encoder_decoder[i].decoder.reset(handler->ctx[i]);\n    }\n}\n\nconst InfraredMessage* infrared_check_decoder_ready(InfraredDecoderHandler* handler) {\n    furi_check(handler);\n\n    InfraredMessage* message = NULL;\n    InfraredMessage* result = NULL;\n\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        if(infrared_encoder_decoder[i].decoder.check_ready) {\n            message = infrared_encoder_decoder[i].decoder.check_ready(handler->ctx[i]);\n            if(!result && message) {\n                result = message;\n            }\n        }\n    }\n\n    return result;\n}\n\nInfraredEncoderHandler* infrared_alloc_encoder(void) {\n    InfraredEncoderHandler* handler = malloc(sizeof(InfraredEncoderHandler));\n    handler->handler = NULL;\n    handler->encoder = NULL;\n    return handler;\n}\n\nvoid infrared_free_encoder(InfraredEncoderHandler* handler) {\n    furi_check(handler);\n    const InfraredEncoders* encoder = handler->encoder;\n\n    if(encoder || handler->handler) {\n        furi_check(encoder);\n        furi_check(handler->handler);\n        furi_check(encoder->free);\n        encoder->free(handler->handler);\n    }\n\n    free(handler);\n}\n\nstatic int infrared_find_index_by_protocol(InfraredProtocol protocol) {\n    for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) {\n        if(infrared_encoder_decoder[i].get_protocol_variant(protocol)) {\n            return i;\n        }\n    }\n\n    return -1;\n}\n\nvoid infrared_reset_encoder(InfraredEncoderHandler* handler, const InfraredMessage* message) {\n    furi_check(handler);\n    furi_check(message);\n    int index = infrared_find_index_by_protocol(message->protocol);\n    furi_check(index >= 0);\n\n    const InfraredEncoders* required_encoder = &infrared_encoder_decoder[index].encoder;\n    furi_check(required_encoder);\n    furi_check(required_encoder->reset);\n    furi_check(required_encoder->alloc);\n\n    /* Realloc encoder if different protocol set */\n    if(required_encoder != handler->encoder) {\n        if(handler->handler != NULL) {\n            furi_check(handler->encoder->free);\n            handler->encoder->free(handler->handler);\n        }\n        handler->encoder = required_encoder;\n        handler->handler = handler->encoder->alloc();\n    }\n\n    handler->encoder->reset(handler->handler, message);\n}\n\nInfraredStatus infrared_encode(InfraredEncoderHandler* handler, uint32_t* duration, bool* level) {\n    furi_check(handler);\n    furi_check(duration);\n    furi_check(level);\n\n    const InfraredEncoders* encoder = handler->encoder;\n    furi_check(encoder);\n    furi_check(encoder->encode);\n\n    InfraredStatus status = encoder->encode(handler->handler, duration, level);\n    furi_check(status != InfraredStatusError);\n\n    return status;\n}\n\nbool infrared_is_protocol_valid(InfraredProtocol protocol) {\n    return infrared_find_index_by_protocol(protocol) >= 0;\n}\n\nInfraredProtocol infrared_get_protocol_by_name(const char* protocol_name) {\n    furi_check(protocol_name);\n\n    for(InfraredProtocol protocol = 0; protocol < InfraredProtocolMAX; ++protocol) {\n        const char* name = infrared_get_protocol_name(protocol);\n        if(!strcmp(name, protocol_name)) return protocol;\n    }\n    return InfraredProtocolUnknown;\n}\n\nstatic const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol) {\n    int index = infrared_find_index_by_protocol(protocol);\n    const InfraredProtocolVariant* variant = NULL;\n    if(index >= 0) {\n        variant = infrared_encoder_decoder[index].get_protocol_variant(protocol);\n    }\n\n    furi_check(variant);\n    return variant;\n}\n\nconst char* infrared_get_protocol_name(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->name;\n}\n\nuint8_t infrared_get_protocol_address_length(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->address_length;\n}\n\nuint8_t infrared_get_protocol_command_length(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->command_length;\n}\n\nuint32_t infrared_get_protocol_frequency(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->frequency;\n}\n\nfloat infrared_get_protocol_duty_cycle(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->duty_cycle;\n}\n\nsize_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol) {\n    return infrared_get_variant_by_protocol(protocol)->repeat_count;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/infrared.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define INFRARED_COMMON_CARRIER_FREQUENCY ((uint32_t)38000)\n#define INFRARED_COMMON_DUTY_CYCLE        ((float)0.33)\n\n/* if we want to see split raw signals during bruteforce,\n * we have to have RX raw timing delay less than TX */\n#define INFRARED_RAW_RX_TIMING_DELAY_US 150000\n#define INFRARED_RAW_TX_TIMING_DELAY_US 180000\n\ntypedef struct InfraredDecoderHandler InfraredDecoderHandler;\ntypedef struct InfraredEncoderHandler InfraredEncoderHandler;\n\ntypedef enum {\n    InfraredProtocolUnknown = -1,\n    InfraredProtocolNEC = 0,\n    InfraredProtocolNECext,\n    InfraredProtocolNEC42,\n    InfraredProtocolNEC42ext,\n    InfraredProtocolSamsung32,\n    InfraredProtocolRC6,\n    InfraredProtocolRC5,\n    InfraredProtocolRC5X,\n    InfraredProtocolSIRC,\n    InfraredProtocolSIRC15,\n    InfraredProtocolSIRC20,\n    InfraredProtocolKaseikyo,\n    InfraredProtocolRCA,\n    InfraredProtocolPioneer,\n    /* Add new protocols here */\n    InfraredProtocolMAX,\n} InfraredProtocol;\n\ntypedef struct {\n    InfraredProtocol protocol;\n    uint32_t address;\n    uint32_t command;\n    bool repeat;\n} InfraredMessage;\n\ntypedef enum {\n    InfraredStatusError,\n    InfraredStatusOk,\n    InfraredStatusDone,\n    InfraredStatusReady,\n} InfraredStatus;\n\n/**\n * Initialize decoder.\n *\n * \\return      returns pointer to INFRARED decoder handler if success, otherwise - error.\n */\nInfraredDecoderHandler* infrared_alloc_decoder(void);\n\n/**\n * Provide to decoder next timing.\n *\n * \\param[in]   handler     - handler to INFRARED decoders. Should be acquired with \\c infrared_alloc_decoder().\n * \\param[in]   level       - high(true) or low(false) level of input signal to analyze.\n *                          it should alternate every call, otherwise it is an error case,\n *                          and decoder resets its state and start decoding from the start.\n * \\param[in]   duration    - duration of steady high/low input signal.\n * \\return      if message is ready, returns pointer to decoded message, returns NULL.\n *              Note: ownership of returned ptr belongs to handler. So pointer is valid\n *              up to next infrared_free_decoder(), infrared_reset_decoder(),\n *              infrared_decode(), infrared_check_decoder_ready() calls.\n */\nconst InfraredMessage*\n    infrared_decode(InfraredDecoderHandler* handler, bool level, uint32_t duration);\n\n/**\n * Check whether decoder is ready.\n * Functionality is quite similar to infrared_decode(), but with no timing providing.\n * Some protocols (e.g. Sony SIRC) has variable payload length, which means we\n * can't recognize end of message right after receiving last bit. That's why\n * application should call to infrared_check_decoder_ready() after some timeout to\n * retrieve decoded message, if so.\n *\n * \\param[in]   handler     - handler to INFRARED decoders. Should be acquired with \\c infrared_alloc_decoder().\n * \\return      if message is ready, returns pointer to decoded message, returns NULL.\n *              Note: ownership of returned ptr belongs to handler. So pointer is valid\n *              up to next infrared_free_decoder(), infrared_reset_decoder(),\n *              infrared_decode(), infrared_check_decoder_ready() calls.\n */\nconst InfraredMessage* infrared_check_decoder_ready(InfraredDecoderHandler* handler);\n\n/**\n * Deinitialize decoder and free allocated memory.\n *\n * \\param[in]   handler     - handler to INFRARED decoders. Should be acquired with \\c infrared_alloc_decoder().\n */\nvoid infrared_free_decoder(InfraredDecoderHandler* handler);\n\n/**\n * Reset INFRARED decoder.\n *\n * \\param[in]   handler     - handler to INFRARED decoders. Should be acquired with \\c infrared_alloc_decoder().\n */\nvoid infrared_reset_decoder(InfraredDecoderHandler* handler);\n\n/**\n * Get protocol name by protocol enum.\n *\n * \\param[in]   protocol    - protocol identifier.\n * \\return      string to protocol name.\n */\nconst char* infrared_get_protocol_name(InfraredProtocol protocol);\n\n/**\n * Get protocol enum by protocol name.\n *\n * \\param[in]   protocol_name   - string to protocol name.\n * \\return      protocol identifier.\n */\nInfraredProtocol infrared_get_protocol_by_name(const char* protocol_name);\n\n/**\n * Get address length by protocol enum.\n *\n * \\param[in]   protocol    - protocol identifier.\n * \\return      length of address in bits.\n */\nuint8_t infrared_get_protocol_address_length(InfraredProtocol protocol);\n\n/**\n * Get command length by protocol enum.\n *\n * \\param[in]   protocol    - protocol identifier.\n * \\return      length of command in bits.\n */\nuint8_t infrared_get_protocol_command_length(InfraredProtocol protocol);\n\n/**\n * Checks whether protocol valid.\n *\n * \\param[in]   protocol    - protocol identifier.\n * \\return      true if protocol is valid, false otherwise.\n */\nbool infrared_is_protocol_valid(InfraredProtocol protocol);\n\n/**\n * Allocate INFRARED encoder.\n *\n * \\return      encoder handler.\n */\nInfraredEncoderHandler* infrared_alloc_encoder(void);\n\n/**\n * Free encoder handler previously allocated with \\c infrared_alloc_encoder().\n *\n * \\param[in]   handler     - handler to INFRARED encoder. Should be acquired with \\c infrared_alloc_encoder().\n */\nvoid infrared_free_encoder(InfraredEncoderHandler* handler);\n\n/**\n * Encode previously set INFRARED message.\n * Usage:\n *  1) alloc with \\c infrared_alloc_encoder()\n *  2) set message to encode with \\c infrared_reset_encoder()\n *  3) call for \\c infrared_encode() to continuously get one at a time timings.\n *  4) when \\c infrared_encode() returns InfraredStatusDone, it means new message is fully encoded.\n *  5) to encode additional timings, just continue calling \\c infrared_encode().\n *\n * \\param[in]   handler     - handler to INFRARED encoder. Should be acquired with \\c infrared_alloc_encoder().\n * \\param[out]  duration    - encoded timing.\n * \\param[out]  level       - encoded level.\n *\n * \\return      status of encode operation.\n */\nInfraredStatus infrared_encode(InfraredEncoderHandler* handler, uint32_t* duration, bool* level);\n\n/**\n * Reset INFRARED encoder and set new message to encode. If it's not called after receiveing\n * InfraredStatusDone in \\c infrared_encode(), encoder will encode repeat messages\n * till the end of time.\n *\n * \\param[in]   handler     - handler to INFRARED encoder. Should be acquired with \\c infrared_alloc_encoder().\n * \\param[in]   message     - message to encode.\n */\nvoid infrared_reset_encoder(InfraredEncoderHandler* handler, const InfraredMessage* message);\n\n/**\n * Get PWM frequency value for selected protocol\n *\n * \\param[in]   protocol    - protocol to get from PWM frequency\n *\n * \\return      frequency\n */\nuint32_t infrared_get_protocol_frequency(InfraredProtocol protocol);\n\n/**\n * Get PWM duty cycle value for selected protocol\n *\n * \\param[in]   protocol    - protocol to get from PWM duty cycle\n *\n * \\return      duty cycle\n */\nfloat infrared_get_protocol_duty_cycle(InfraredProtocol protocol);\n\n/**\n * Get the minimum count of signal repeats for the selected protocol\n *\n * \\param[in]   protocol    - protocol to get the repeat count from\n *\n * \\return      repeat count\n */\nsize_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/infrared_i.h",
    "content": "#pragma once\n#include \"infrared.h\"\n#include <stddef.h>\n#include <stdint.h>\n\ntypedef struct {\n    uint32_t min_split_time;\n    uint32_t silence_time;\n    uint16_t preamble_mark;\n    uint16_t preamble_space;\n    uint16_t bit1_mark;\n    uint16_t bit1_space;\n    uint16_t bit0_mark;\n    uint16_t bit0_space;\n    uint32_t preamble_tolerance;\n    uint32_t bit_tolerance;\n} InfraredTimings;\n\ntypedef struct {\n    const char* name;\n    uint8_t address_length;\n    uint8_t command_length;\n    uint32_t frequency;\n    float duty_cycle;\n    size_t repeat_count;\n} InfraredProtocolVariant;\n\ntypedef const InfraredProtocolVariant* (*InfraredGetProtocolVariant)(InfraredProtocol protocol);\n\ntypedef void* (*InfraredAlloc)(void);\ntypedef void (*InfraredFree)(void*);\n\ntypedef void (*InfraredDecoderReset)(void*);\ntypedef InfraredMessage* (*InfraredDecode)(void* ctx, bool level, uint32_t duration);\ntypedef InfraredMessage* (*InfraredDecoderCheckReady)(void*);\n\ntypedef void (*InfraredEncoderReset)(void* encoder, const InfraredMessage* message);\ntypedef InfraredStatus (*InfraredEncode)(void* encoder, uint32_t* out, bool* polarity);\n\nstatic inline uint8_t reverse(uint8_t value) {\n    uint8_t reverse_value = 0;\n    for(int i = 0; i < 8; ++i) {\n        reverse_value |= (value & (0x01 << i)) ? 1 << (7 - i) : 0;\n    }\n\n    return reverse_value;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c",
    "content": "#include \"infrared_protocol_kaseikyo_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n    uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0];\n    uint8_t vendor_parity = decoder->data[2] & 0x0f;\n    uint8_t genre1 = decoder->data[2] >> 4;\n    uint8_t genre2 = decoder->data[3] & 0x0f;\n    uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4);\n    uint8_t id = decoder->data[4] >> 6;\n    uint8_t parity = decoder->data[5];\n\n    uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1];\n    vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4);\n    uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4];\n\n    if(vendor_parity == vendor_parity_check && parity == parity_check) {\n        decoder->message.command = (uint32_t)data;\n        decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) |\n                                   ((uint32_t)genre1 << 4) | (uint32_t)genre2;\n        decoder->message.protocol = InfraredProtocolKaseikyo;\n        decoder->message.repeat = false;\n        result = true;\n    }\n\n    return result;\n}\n\nvoid* infrared_decoder_kaseikyo_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_kaseikyo);\n}\n\nInfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_kaseikyo_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_kaseikyo_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c",
    "content": "#include \"infrared_protocol_kaseikyo_i.h\"\n#include <core/check.h>\n\nvoid infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint32_t address = message->address;\n    uint16_t command = message->command;\n\n    uint8_t id = (address >> 24) & 3;\n    uint16_t vendor_id = (address >> 8) & 0xffff;\n    uint8_t genre1 = (address >> 4) & 0xf;\n    uint8_t genre2 = address & 0xf;\n\n    encoder->data[0] = (uint8_t)(vendor_id & 0xff);\n    encoder->data[1] = (uint8_t)(vendor_id >> 8);\n    uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1];\n    vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4);\n    encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4);\n    encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4);\n    encoder->data[4] = (id << 6) | (uint8_t)(command >> 4);\n    encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4];\n\n    encoder->bits_to_encode = encoder->protocol->databit_len[0];\n}\n\nvoid* infrared_encoder_kaseikyo_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_kaseikyo);\n}\n\nvoid infrared_encoder_kaseikyo_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus\n    infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    return infrared_common_encode(encoder_ptr, duration, level);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.c",
    "content": "#include \"infrared_protocol_kaseikyo_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_kaseikyo = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK,\n            .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK,\n            .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE,\n            .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK,\n            .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE,\n            .silence_time = INFRARED_KASEIKYO_SILENCE,\n            .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 48,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_kaseikyo_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = NULL,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_kaseikyo = {\n    .name = \"Kaseikyo\",\n    .address_length = 26,\n    .command_length = 10,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_KASEIKYO_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolKaseikyo)\n        return &infrared_protocol_variant_kaseikyo;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   Kaseikyo protocol description\n*   https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp\n****************************************************************************************************\n*     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble\n*       mark      space            Modulation             up to period    repeat     repeat\n*                                                                          mark       space\n*\n*        3360      1665               48 bit              ...130000        3456       1728\n*     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________\n* ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ___________\n*\n***************************************************************************************************/\n\nvoid* infrared_decoder_kaseikyo_alloc(void);\nvoid infrared_decoder_kaseikyo_reset(void* decoder);\nvoid infrared_decoder_kaseikyo_free(void* decoder);\nInfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder);\nInfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_kaseikyo_alloc(void);\nInfraredStatus\n    infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level);\nvoid infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_kaseikyo_free(void* encoder_ptr);\n\nconst InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_KASEIKYO_UNIT               432\n#define INFRARED_KASEIKYO_PREAMBLE_MARK      (8 * INFRARED_KASEIKYO_UNIT)\n#define INFRARED_KASEIKYO_PREAMBLE_SPACE     (4 * INFRARED_KASEIKYO_UNIT)\n#define INFRARED_KASEIKYO_BIT1_MARK          INFRARED_KASEIKYO_UNIT\n#define INFRARED_KASEIKYO_BIT1_SPACE         (3 * INFRARED_KASEIKYO_UNIT)\n#define INFRARED_KASEIKYO_BIT0_MARK          INFRARED_KASEIKYO_UNIT\n#define INFRARED_KASEIKYO_BIT0_SPACE         INFRARED_KASEIKYO_UNIT\n#define INFRARED_KASEIKYO_REPEAT_PERIOD      130000\n#define INFRARED_KASEIKYO_SILENCE            INFRARED_KASEIKYO_REPEAT_PERIOD\n#define INFRARED_KASEIKYO_MIN_SPLIT_TIME     INFRARED_KASEIKYO_REPEAT_PAUSE_MIN\n#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN   4000\n#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX   150000\n#define INFRARED_KASEIKYO_REPEAT_COUNT_MIN   1\n#define INFRARED_KASEIKYO_REPEAT_MARK        INFRARED_KASEIKYO_PREAMBLE_MARK\n#define INFRARED_KASEIKYO_REPEAT_SPACE       (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000)\n#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_KASEIKYO_BIT_TOLERANCE      120 // us\n\nextern const InfraredCommonProtocolSpec infrared_protocol_kaseikyo;\n\nbool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_kaseikyo_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c",
    "content": "#include \"infrared_protocol_nec_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_nec_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n\n    if(decoder->databit_cnt == 32) {\n        uint8_t address = decoder->data[0];\n        uint8_t address_inverse = decoder->data[1];\n        uint8_t command = decoder->data[2];\n        uint8_t command_inverse = decoder->data[3];\n        uint8_t inverse_command_inverse = (uint8_t)~command_inverse;\n        uint8_t inverse_address_inverse = (uint8_t)~address_inverse;\n        if((command == inverse_command_inverse) && (address == inverse_address_inverse)) {\n            decoder->message.protocol = InfraredProtocolNEC;\n            decoder->message.address = address;\n            decoder->message.command = command;\n            decoder->message.repeat = false;\n            result = true;\n        } else {\n            decoder->message.protocol = InfraredProtocolNECext;\n            decoder->message.address = decoder->data[0] | (decoder->data[1] << 8);\n            decoder->message.command = decoder->data[2] | (decoder->data[3] << 8);\n            decoder->message.repeat = false;\n            result = true;\n        }\n    } else if(decoder->databit_cnt == 42) {\n        uint32_t* data1 = (void*)decoder->data;\n        uint16_t* data2 = (void*)(data1 + 1);\n        uint16_t address = *data1 & 0x1FFF;\n        uint16_t address_inverse = (*data1 >> 13) & 0x1FFF;\n        uint16_t command = ((*data1 >> 26) & 0x3F) | ((*data2 & 0x3) << 6);\n        uint16_t command_inverse = (*data2 >> 2) & 0xFF;\n\n        if((address == (~address_inverse & 0x1FFF)) && (command == (~command_inverse & 0xFF))) {\n            decoder->message.protocol = InfraredProtocolNEC42;\n            decoder->message.address = address;\n            decoder->message.command = command;\n            decoder->message.repeat = false;\n            result = true;\n        } else {\n            decoder->message.protocol = InfraredProtocolNEC42ext;\n            decoder->message.address = address | (address_inverse << 13);\n            decoder->message.command = command | (command_inverse << 8);\n            decoder->message.repeat = false;\n            result = true;\n        }\n    }\n\n    return result;\n}\n\n// timings start from Space (delay between message and repeat)\nInfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    float preamble_tolerance = decoder->protocol->timings.preamble_tolerance;\n    uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance;\n    InfraredStatus status = InfraredStatusError;\n\n    if(decoder->timings_cnt < 4) return InfraredStatusOk;\n\n    if((decoder->timings[0] > INFRARED_NEC_REPEAT_PAUSE_MIN) &&\n       (decoder->timings[0] < INFRARED_NEC_REPEAT_PAUSE_MAX) &&\n       MATCH_TIMING(decoder->timings[1], INFRARED_NEC_REPEAT_MARK, preamble_tolerance) &&\n       MATCH_TIMING(decoder->timings[2], INFRARED_NEC_REPEAT_SPACE, preamble_tolerance) &&\n       MATCH_TIMING(decoder->timings[3], decoder->protocol->timings.bit1_mark, bit_tolerance)) {\n        status = InfraredStatusReady;\n        decoder->timings_cnt = 0;\n    } else {\n        status = InfraredStatusError;\n    }\n\n    return status;\n}\n\nvoid* infrared_decoder_nec_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_nec);\n}\n\nInfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_nec_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_nec_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/nec/infrared_encoder_nec.c",
    "content": "#include \"infrared_protocol_nec_i.h\"\n\n#include <core/core_defines.h>\n#include <core/check.h>\n\nstatic const uint32_t repeat_timings[] = {\n    INFRARED_NEC_REPEAT_PERIOD - INFRARED_NEC_REPEAT_MARK - INFRARED_NEC_REPEAT_SPACE -\n        INFRARED_NEC_BIT1_MARK,\n    INFRARED_NEC_REPEAT_MARK,\n    INFRARED_NEC_REPEAT_SPACE,\n    INFRARED_NEC_BIT1_MARK,\n};\n\nvoid infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n    furi_assert(message);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint32_t* data1 = (void*)encoder->data;\n    uint32_t* data2 = data1 + 1;\n    if(message->protocol == InfraredProtocolNEC) {\n        uint8_t address = message->address;\n        uint8_t address_inverse = ~address;\n        uint8_t command = message->command;\n        uint8_t command_inverse = ~command;\n        *data1 = address;\n        *data1 |= address_inverse << 8;\n        *data1 |= command << 16;\n        *data1 |= command_inverse << 24;\n        encoder->bits_to_encode = 32;\n    } else if(message->protocol == InfraredProtocolNECext) {\n        *data1 = (uint16_t)message->address;\n        *data1 |= (message->command & 0xFFFF) << 16;\n        encoder->bits_to_encode = 32;\n    } else if(message->protocol == InfraredProtocolNEC42) {\n        /* 13 address + 13 inverse address + 8 command + 8 inv command */\n        *data1 = message->address & 0x1FFFUL;\n        *data1 |= (~message->address & 0x1FFFUL) << 13;\n        *data1 |= ((message->command & 0x3FUL) << 26);\n        *data2 = (message->command & 0xC0UL) >> 6;\n        *data2 |= (~message->command & 0xFFUL) << 2;\n        encoder->bits_to_encode = 42;\n    } else if(message->protocol == InfraredProtocolNEC42ext) {\n        *data1 = message->address & 0x3FFFFFF;\n        *data1 |= ((message->command & 0x3F) << 26);\n        *data2 = (message->command & 0xFFC0) >> 6;\n        encoder->bits_to_encode = 42;\n    } else {\n        furi_crash();\n    }\n}\n\nInfraredStatus infrared_encoder_nec_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level) {\n    furi_assert(encoder);\n\n    /* space + 2 timings preambule + payload + stop bit */\n    uint32_t timings_encoded_up_to_repeat = 1 + 2 + encoder->bits_to_encode * 2 + 1;\n    uint32_t repeat_cnt = encoder->timings_encoded - timings_encoded_up_to_repeat;\n\n    furi_assert(encoder->timings_encoded >= timings_encoded_up_to_repeat);\n\n    if(repeat_cnt > 0) {\n        *duration = repeat_timings[repeat_cnt % COUNT_OF(repeat_timings)];\n    } else {\n        *duration = INFRARED_NEC_REPEAT_PERIOD - encoder->timings_sum;\n    }\n\n    *level = repeat_cnt % 2;\n    ++encoder->timings_encoded;\n    bool done = (!((repeat_cnt + 1) % COUNT_OF(repeat_timings)));\n\n    return done ? InfraredStatusDone : InfraredStatusOk;\n}\n\nvoid* infrared_encoder_nec_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_nec);\n}\n\nvoid infrared_encoder_nec_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    return infrared_common_encode(encoder_ptr, duration, level);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/nec/infrared_protocol_nec.c",
    "content": "#include \"infrared_protocol_nec_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_nec = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_NEC_PREAMBLE_MARK,\n            .preamble_space = INFRARED_NEC_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_NEC_BIT1_MARK,\n            .bit1_space = INFRARED_NEC_BIT1_SPACE,\n            .bit0_mark = INFRARED_NEC_BIT0_MARK,\n            .bit0_space = INFRARED_NEC_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_NEC_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_NEC_BIT_TOLERANCE,\n            .silence_time = INFRARED_NEC_SILENCE,\n            .min_split_time = INFRARED_NEC_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 42,\n    .databit_len[1] = 32,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_nec_interpret,\n    .decode_repeat = infrared_decoder_nec_decode_repeat,\n    .encode_repeat = infrared_encoder_nec_encode_repeat,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_nec = {\n    .name = \"NEC\",\n    .address_length = 8,\n    .command_length = 8,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_necext = {\n    .name = \"NECext\",\n    .address_length = 16,\n    .command_length = 16,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_nec42 = {\n    .name = \"NEC42\",\n    .address_length = 13,\n    .command_length = 8,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_nec42ext = {\n    .name = \"NEC42ext\",\n    .address_length = 26,\n    .command_length = 16,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolNEC)\n        return &infrared_protocol_variant_nec;\n    else if(protocol == InfraredProtocolNECext)\n        return &infrared_protocol_variant_necext;\n    else if(protocol == InfraredProtocolNEC42)\n        return &infrared_protocol_variant_nec42;\n    else if(protocol == InfraredProtocolNEC42ext)\n        return &infrared_protocol_variant_nec42ext;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/nec/infrared_protocol_nec.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   NEC protocol description\n*   https://radioparty.ru/manuals/encyclopedia/213-ircontrol?start=1\n****************************************************************************************************\n*     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble  Stop\n*       mark      space            Modulation             up to period    repeat     repeat    bit\n*                                                                          mark       space\n*\n*        9000      4500         32 bit + stop bit         ...110000         9000       2250\n*     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________            _\n* ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ____________ ___\n*\n***************************************************************************************************/\n\nvoid* infrared_decoder_nec_alloc(void);\nvoid infrared_decoder_nec_reset(void* decoder);\nvoid infrared_decoder_nec_free(void* decoder);\nInfraredMessage* infrared_decoder_nec_check_ready(void* decoder);\nInfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_nec_alloc(void);\nInfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level);\nvoid infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_nec_free(void* encoder_ptr);\n\nconst InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/nec/infrared_protocol_nec_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_NEC_PREAMBLE_MARK      9000\n#define INFRARED_NEC_PREAMBLE_SPACE     4500\n#define INFRARED_NEC_BIT1_MARK          560\n#define INFRARED_NEC_BIT1_SPACE         1690\n#define INFRARED_NEC_BIT0_MARK          560\n#define INFRARED_NEC_BIT0_SPACE         560\n#define INFRARED_NEC_REPEAT_PERIOD      110000\n#define INFRARED_NEC_SILENCE            INFRARED_NEC_REPEAT_PERIOD\n#define INFRARED_NEC_MIN_SPLIT_TIME     INFRARED_NEC_REPEAT_PAUSE_MIN\n#define INFRARED_NEC_REPEAT_PAUSE_MIN   4000\n#define INFRARED_NEC_REPEAT_PAUSE_MAX   150000\n#define INFRARED_NEC_REPEAT_COUNT_MIN   1\n#define INFRARED_NEC_REPEAT_MARK        9000\n#define INFRARED_NEC_REPEAT_SPACE       2250\n#define INFRARED_NEC_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_NEC_BIT_TOLERANCE      120 // us\n\nextern const InfraredCommonProtocolSpec infrared_protocol_nec;\n\nbool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_nec_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/pioneer/infrared_decoder_pioneer.c",
    "content": "#include \"infrared_protocol_pioneer_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_pioneer_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_pioneer_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    uint32_t* data = (void*)&decoder->data[0];\n    uint8_t address = 0;\n    uint8_t command = 0;\n    InfraredProtocol protocol = InfraredProtocolUnknown;\n\n    if(decoder->databit_cnt == decoder->protocol->databit_len[0] ||\n       decoder->databit_cnt == decoder->protocol->databit_len[1]) {\n        address = *data & 0xFF;\n        uint8_t real_address_checksum = ~address;\n        uint8_t address_checksum = (*data >> 8) & 0xFF;\n        command = (*data >> 16) & 0xFF;\n        uint8_t real_command_checksum = ~command;\n        uint8_t command_checksum = (*data >> 24) & 0xFF;\n        if(address_checksum != real_address_checksum) {\n            return false;\n        }\n        if(command_checksum != real_command_checksum) {\n            return false;\n        }\n        protocol = InfraredProtocolPioneer;\n    } else {\n        return false;\n    }\n\n    decoder->message.protocol = protocol;\n    decoder->message.address = address;\n    decoder->message.command = command;\n    decoder->message.repeat = false;\n\n    return true;\n}\n\nvoid* infrared_decoder_pioneer_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_pioneer);\n}\n\nInfraredMessage* infrared_decoder_pioneer_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_pioneer_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_pioneer_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/pioneer/infrared_encoder_pioneer.c",
    "content": "#include \"infrared_protocol_pioneer_i.h\"\n#include <core/check.h>\n\nvoid infrared_encoder_pioneer_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n    furi_assert(message);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint8_t* data = encoder->data;\n\n    if(message->protocol == InfraredProtocolPioneer) {\n        data[0] = message->address & 0xFF;\n        data[1] = ~(message->address & 0xFF);\n        data[2] = message->command & 0xFF;\n        data[3] = ~(message->command & 0xFF);\n        data[4] = 0;\n        encoder->bits_to_encode = encoder->protocol->databit_len[0];\n    } else {\n        furi_crash();\n    }\n}\n\nvoid* infrared_encoder_pioneer_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_pioneer);\n}\n\nvoid infrared_encoder_pioneer_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus infrared_encoder_pioneer_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level) {\n    furi_assert(encoder);\n\n    *duration = INFRARED_PIONEER_SILENCE;\n    *level = false;\n\n    encoder->timings_sum = 0;\n    encoder->timings_encoded = 1;\n    encoder->bits_encoded = 0;\n    encoder->state = InfraredCommonEncoderStatePreamble;\n\n    return InfraredStatusOk;\n}\n\nInfraredStatus\n    infrared_encoder_pioneer_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    InfraredCommonEncoder* encoder = encoder_ptr;\n\n    InfraredStatus status = infrared_common_encode(encoder, duration, level);\n    if((status == InfraredStatusOk) && (encoder->bits_encoded == encoder->bits_to_encode)) {\n        furi_assert(!*level);\n        status = InfraredStatusDone;\n        encoder->state = InfraredCommonEncoderStateEncodeRepeat;\n    }\n    return status;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/pioneer/infrared_protocol_pioneer.c",
    "content": "#include \"infrared_protocol_pioneer_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_pioneer = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_PIONEER_PREAMBLE_MARK,\n            .preamble_space = INFRARED_PIONEER_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_PIONEER_BIT1_MARK,\n            .bit1_space = INFRARED_PIONEER_BIT1_SPACE,\n            .bit0_mark = INFRARED_PIONEER_BIT0_MARK,\n            .bit0_space = INFRARED_PIONEER_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_PIONEER_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_PIONEER_BIT_TOLERANCE,\n            .silence_time = INFRARED_PIONEER_SILENCE,\n            .min_split_time = INFRARED_PIONEER_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 33,\n    .databit_len[1] = 32,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_pioneer_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = infrared_encoder_pioneer_encode_repeat,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_pioneer = {\n    .name = \"Pioneer\",\n    .address_length = 8,\n    .command_length = 8,\n    .frequency = INFRARED_PIONEER_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_PIONEER_DUTY_CYCLE,\n    .repeat_count = INFRARED_PIONEER_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_pioneer_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolPioneer)\n        return &infrared_protocol_variant_pioneer;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/pioneer/infrared_protocol_pioneer.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   Pioneer SR protocol description\n*   http://www.adrian-kingston.com/IRFormatPioneer.htm\n****************************************************************************************************\n*      Preamble  Preamble     Pulse Width Modulation           Pause           Entirely repeat\n*        mark     space                                     up to period          message..\n*\n*        8500      4250         33 bits (500, 1500)          ...26000          8500      4250\n*     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                    __________          _ _\n* ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ____________________          __________ _\n*\n* In 33 bits of data there is:\n*  - 8 bits address\n*  - 8 bits address inverse\n*  - 8 bits command\n*  - 8 bits command inverse\n*  - 1 stop bit\n***************************************************************************************************/\n\nvoid* infrared_decoder_pioneer_alloc(void);\nvoid infrared_decoder_pioneer_reset(void* decoder);\nInfraredMessage* infrared_decoder_pioneer_check_ready(void* decoder);\nvoid infrared_decoder_pioneer_free(void* decoder);\nInfraredMessage* infrared_decoder_pioneer_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_pioneer_alloc(void);\nvoid infrared_encoder_pioneer_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_pioneer_free(void* decoder);\nInfraredStatus\n    infrared_encoder_pioneer_encode(void* encoder_ptr, uint32_t* duration, bool* polarity);\n\nconst InfraredProtocolVariant* infrared_protocol_pioneer_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/pioneer/infrared_protocol_pioneer_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_PIONEER_CARRIER_FREQUENCY  40000\n#define INFRARED_PIONEER_DUTY_CYCLE         0.33\n#define INFRARED_PIONEER_PREAMBLE_MARK      8500\n#define INFRARED_PIONEER_PREAMBLE_SPACE     4225\n#define INFRARED_PIONEER_BIT1_MARK          500\n#define INFRARED_PIONEER_BIT1_SPACE         1500\n#define INFRARED_PIONEER_BIT0_MARK          500\n#define INFRARED_PIONEER_BIT0_SPACE         500\n#define INFRARED_PIONEER_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_PIONEER_BIT_TOLERANCE      120 // us\n#define INFRARED_PIONEER_SILENCE            26000\n#define INFRARED_PIONEER_MIN_SPLIT_TIME     (INFRARED_PIONEER_SILENCE)\n#define INFRARED_PIONEER_REPEAT_COUNT_MIN   2\n\nextern const InfraredCommonProtocolSpec infrared_protocol_pioneer;\n\nbool infrared_decoder_pioneer_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_pioneer_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc5/infrared_decoder_rc5.c",
    "content": "#include \"infrared_protocol_rc5_i.h\"\n\n#include <stdlib.h>\n#include <core/check.h>\n\ntypedef struct {\n    InfraredCommonDecoder* common_decoder;\n    bool toggle;\n} InfraredRc5Decoder;\n\nInfraredMessage* infrared_decoder_rc5_check_ready(void* ctx) {\n    InfraredRc5Decoder* decoder = ctx;\n    return infrared_common_decoder_check_ready(decoder->common_decoder);\n}\n\nbool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n    uint32_t* data = (void*)&decoder->data[0];\n    /* Manchester (inverse):\n     *      0->1 : 1\n     *      1->0 : 0\n     */\n    decoder->data[0] = ~decoder->data[0];\n    decoder->data[1] = ~decoder->data[1];\n\n    // MSB first\n    uint8_t address = reverse((uint8_t)decoder->data[0]) & 0x1F;\n    uint8_t command = (reverse((uint8_t)decoder->data[1]) >> 2) & 0x3F;\n    bool start_bit1 = *data & 0x01;\n    bool start_bit2 = *data & 0x02;\n    bool toggle = !!(*data & 0x04);\n\n    if(start_bit1 == 1) {\n        InfraredProtocol protocol = start_bit2 ? InfraredProtocolRC5 : InfraredProtocolRC5X;\n        InfraredMessage* message = &decoder->message;\n        InfraredRc5Decoder* rc5_decoder = decoder->context;\n        bool* prev_toggle = &rc5_decoder->toggle;\n        if((message->address == address) && (message->command == command) &&\n           (message->protocol == protocol)) {\n            message->repeat = (toggle == *prev_toggle);\n        } else {\n            message->repeat = false;\n        }\n        *prev_toggle = toggle;\n        message->command = command;\n        message->address = address;\n        message->protocol = protocol;\n\n        result = true;\n    }\n\n    return result;\n}\n\nvoid* infrared_decoder_rc5_alloc(void) {\n    InfraredRc5Decoder* decoder = malloc(sizeof(InfraredRc5Decoder));\n    decoder->toggle = false;\n    decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc5);\n    decoder->common_decoder->context = decoder;\n    return decoder;\n}\n\nInfraredMessage* infrared_decoder_rc5_decode(void* decoder, bool level, uint32_t duration) {\n    InfraredRc5Decoder* decoder_rc5 = decoder;\n    return infrared_common_decode(decoder_rc5->common_decoder, level, duration);\n}\n\nvoid infrared_decoder_rc5_free(void* decoder) {\n    InfraredRc5Decoder* decoder_rc5 = decoder;\n    infrared_common_decoder_free(decoder_rc5->common_decoder);\n    free(decoder_rc5);\n}\n\nvoid infrared_decoder_rc5_reset(void* decoder) {\n    InfraredRc5Decoder* decoder_rc5 = decoder;\n    infrared_common_decoder_reset(decoder_rc5->common_decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc5/infrared_encoder_rc5.c",
    "content": "#include \"infrared_protocol_rc5_i.h\"\n\n#include <stdlib.h>\n#include <core/check.h>\n\ntypedef struct InfraredEncoderRC5 {\n    InfraredCommonEncoder* common_encoder;\n    bool toggle_bit;\n} InfraredEncoderRC5;\n\nvoid infrared_encoder_rc5_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n\n    InfraredEncoderRC5* encoder = encoder_ptr;\n    InfraredCommonEncoder* common_encoder = encoder->common_encoder;\n    infrared_common_encoder_reset(common_encoder);\n\n    uint32_t* data = (void*)common_encoder->data;\n    /* RC5 */\n    *data |= 0x01; // start bit\n    if(message->protocol == InfraredProtocolRC5) {\n        *data |= 0x02; // start bit\n    }\n    *data |= encoder->toggle_bit ? 0x04 : 0;\n    *data |= (reverse(message->address) >> 3) << 3; /* address 5 bit */\n    *data |= (reverse(message->command) >> 2) << 8; /* command 6 bit */\n\n    common_encoder->data[0] = ~common_encoder->data[0];\n    common_encoder->data[1] = ~common_encoder->data[1];\n\n    common_encoder->bits_to_encode = common_encoder->protocol->databit_len[0];\n    encoder->toggle_bit ^= 1;\n}\n\nInfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    InfraredEncoderRC5* encoder = encoder_ptr;\n    return infrared_common_encode(encoder->common_encoder, duration, level);\n}\n\nvoid* infrared_encoder_rc5_alloc(void) {\n    InfraredEncoderRC5* encoder = malloc(sizeof(InfraredEncoderRC5));\n    encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc5);\n    encoder->toggle_bit = false;\n    return encoder;\n}\n\nvoid infrared_encoder_rc5_free(void* encoder_ptr) {\n    furi_assert(encoder_ptr);\n\n    InfraredEncoderRC5* encoder = encoder_ptr;\n    free(encoder->common_encoder);\n    free(encoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.c",
    "content": "#include \"infrared_protocol_rc5_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_rc5 = {\n    .timings =\n        {\n            .preamble_mark = 0,\n            .preamble_space = 0,\n            .bit1_mark = INFRARED_RC5_BIT,\n            .preamble_tolerance = 0,\n            .bit_tolerance = INFRARED_RC5_BIT_TOLERANCE,\n            .silence_time = INFRARED_RC5_SILENCE,\n            .min_split_time = INFRARED_RC5_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 1 + 1 + 1 + 5 +\n                      6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command\n    .manchester_start_from_space = true,\n    .decode = infrared_common_decode_manchester,\n    .encode = infrared_common_encode_manchester,\n    .interpret = infrared_decoder_rc5_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = NULL,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_rc5 = {\n    .name = \"RC5\",\n    .address_length = 5,\n    .command_length = 6,\n    .frequency = INFRARED_RC5_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_RC5_DUTY_CYCLE,\n    .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_rc5x = {\n    .name = \"RC5X\",\n    .address_length = 5,\n    .command_length = 7,\n    .frequency = INFRARED_RC5_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_RC5_DUTY_CYCLE,\n    .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolRC5)\n        return &infrared_protocol_variant_rc5;\n    else if(protocol == InfraredProtocolRC5X)\n        return &infrared_protocol_variant_rc5x;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   RC5 protocol description\n*   https://www.mikrocontroller.net/articles/IRMP_-_english#RC5_.2B_RC5X\n****************************************************************************************************\n*                                       Manchester/biphase\n*                                           Modulation\n*\n*                              888/1776 - bit (x2 for toggle bit)\n*\n*                           __  ____    __  __  __  __  __  __  __  __\n*                         __  __    ____  __  __  __  __  __  __  __  _\n*                         | 1 | 1 | 0 |      ...      |      ...      |\n*                           s  si   T   address (MSB)   command (MSB)\n*\n*    Note: manchester starts from space timing, so it have to be handled properly\n*    s - start bit (always 1)\n*    si - RC5: start bit (always 1), RC5X - 7-th bit of address (in our case always 0)\n*    T - toggle bit, change it's value every button press\n*    address - 5 bit\n*    command - 6/7 bit\n***************************************************************************************************/\n\nvoid* infrared_decoder_rc5_alloc(void);\nvoid infrared_decoder_rc5_reset(void* decoder);\nvoid infrared_decoder_rc5_free(void* decoder);\nInfraredMessage* infrared_decoder_rc5_check_ready(void* ctx);\nInfraredMessage* infrared_decoder_rc5_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_rc5_alloc(void);\nvoid infrared_encoder_rc5_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_rc5_free(void* decoder);\nInfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration, bool* polarity);\n\nconst InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_RC5_CARRIER_FREQUENCY 36000\n#define INFRARED_RC5_DUTY_CYCLE        0.33\n\n#define INFRARED_RC5_PREAMBLE_MARK      0\n#define INFRARED_RC5_PREAMBLE_SPACE     0\n#define INFRARED_RC5_BIT                888 // half of time-quant for 1 bit\n#define INFRARED_RC5_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_RC5_BIT_TOLERANCE      120 // us\n/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */\n#define INFRARED_RC5_SILENCE            (2700 * 10)\n#define INFRARED_RC5_MIN_SPLIT_TIME     2700\n#define INFRARED_RC5_REPEAT_COUNT_MIN   1\n\nextern const InfraredCommonProtocolSpec infrared_protocol_rc5;\n\nbool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc6/infrared_decoder_rc6.c",
    "content": "#include \"infrared_protocol_rc6_i.h\"\n\n#include <stdlib.h>\n#include <core/check.h>\n\ntypedef struct {\n    InfraredCommonDecoder* common_decoder;\n    bool toggle;\n} InfraredRc6Decoder;\n\nInfraredMessage* infrared_decoder_rc6_check_ready(void* ctx) {\n    InfraredRc6Decoder* decoder_rc6 = ctx;\n    return infrared_common_decoder_check_ready(decoder_rc6->common_decoder);\n}\n\nbool infrared_decoder_rc6_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n    uint32_t* data = (void*)&decoder->data[0];\n    // MSB first\n    uint8_t address = reverse((uint8_t)(*data >> 5));\n    uint8_t command = reverse((uint8_t)(*data >> 13));\n    bool start_bit = *data & 0x01;\n    bool toggle = !!(*data & 0x10);\n    uint8_t mode = (*data >> 1) & 0x7;\n\n    if((start_bit == 1) && (mode == 0)) {\n        InfraredMessage* message = &decoder->message;\n        InfraredRc6Decoder* rc6_decoder = decoder->context;\n        bool* prev_toggle = &rc6_decoder->toggle;\n        if((message->address == address) && (message->command == command) &&\n           (message->protocol == InfraredProtocolRC6)) {\n            message->repeat = (toggle == *prev_toggle);\n        } else {\n            message->repeat = false;\n        }\n        *prev_toggle = toggle;\n        message->command = command;\n        message->address = address;\n        message->protocol = InfraredProtocolRC6;\n        result = true;\n    }\n\n    return result;\n}\n\n/*\n * RC6 Uses manchester encoding, but it has twice longer\n * 4-th bit (toggle bit) time quant, so we need to decode\n * it separately and than pass decoding for other bits to\n * common manchester decode function.\n */\nInfraredStatus infrared_decoder_rc6_decode_manchester(\n    InfraredCommonDecoder* decoder,\n    bool level,\n    uint32_t timing) {\n    // 4th bit lasts 2x times more\n    InfraredStatus status = InfraredStatusError;\n    uint32_t bit = decoder->protocol->timings.bit1_mark;\n    uint32_t tolerance = decoder->protocol->timings.bit_tolerance;\n\n    bool single_timing = MATCH_TIMING(timing, bit, tolerance);\n    bool double_timing = MATCH_TIMING(timing, 2 * bit, tolerance);\n    bool triple_timing = MATCH_TIMING(timing, 3 * bit, tolerance);\n\n    if(decoder->databit_cnt == 4) {\n        furi_assert(decoder->switch_detect == true);\n\n        if(single_timing ^ triple_timing) {\n            ++decoder->databit_cnt;\n            decoder->data[0] |= (single_timing ? !level : level) << 4;\n            status = InfraredStatusOk;\n        }\n    } else if(decoder->databit_cnt == 5) {\n        if(single_timing || triple_timing) {\n            if(triple_timing) timing = bit;\n            decoder->switch_detect = false;\n            status = infrared_common_decode_manchester(decoder, level, timing);\n        } else if(double_timing) {\n            status = InfraredStatusOk;\n        }\n    } else {\n        status = infrared_common_decode_manchester(decoder, level, timing);\n    }\n\n    return status;\n}\n\nvoid* infrared_decoder_rc6_alloc(void) {\n    InfraredRc6Decoder* decoder = malloc(sizeof(InfraredRc6Decoder));\n    decoder->toggle = false;\n    decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc6);\n    decoder->common_decoder->context = decoder;\n    return decoder;\n}\n\nInfraredMessage* infrared_decoder_rc6_decode(void* decoder, bool level, uint32_t duration) {\n    InfraredRc6Decoder* decoder_rc6 = decoder;\n    return infrared_common_decode(decoder_rc6->common_decoder, level, duration);\n}\n\nvoid infrared_decoder_rc6_free(void* decoder) {\n    InfraredRc6Decoder* decoder_rc6 = decoder;\n    infrared_common_decoder_free(decoder_rc6->common_decoder);\n    free(decoder_rc6);\n}\n\nvoid infrared_decoder_rc6_reset(void* decoder) {\n    InfraredRc6Decoder* decoder_rc6 = decoder;\n    infrared_common_decoder_reset(decoder_rc6->common_decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc6/infrared_encoder_rc6.c",
    "content": "#include \"infrared_protocol_rc6_i.h\"\n\n#include <stdlib.h>\n#include <core/check.h>\n\ntypedef struct InfraredEncoderRC6 {\n    InfraredCommonEncoder* common_encoder;\n    bool toggle_bit;\n} InfraredEncoderRC6;\n\nvoid infrared_encoder_rc6_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n\n    InfraredEncoderRC6* encoder = encoder_ptr;\n    InfraredCommonEncoder* common_encoder = encoder->common_encoder;\n    infrared_common_encoder_reset(common_encoder);\n\n    uint32_t* data = (void*)common_encoder->data;\n    *data |= 0x01; // start bit\n    (void)*data; // 3 bits for mode == 0\n    *data |= encoder->toggle_bit ? 0x10 : 0;\n    *data |= reverse(message->address) << 5;\n    *data |= reverse(message->command) << 13;\n\n    common_encoder->bits_to_encode = common_encoder->protocol->databit_len[0];\n    encoder->toggle_bit ^= 1;\n}\n\nInfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    InfraredEncoderRC6* encoder = encoder_ptr;\n    return infrared_common_encode(encoder->common_encoder, duration, level);\n}\n\nvoid* infrared_encoder_rc6_alloc(void) {\n    InfraredEncoderRC6* encoder = malloc(sizeof(InfraredEncoderRC6));\n    encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc6);\n    encoder->toggle_bit = false;\n    return encoder;\n}\n\nvoid infrared_encoder_rc6_free(void* encoder_ptr) {\n    furi_assert(encoder_ptr);\n\n    InfraredEncoderRC6* encoder = encoder_ptr;\n    free(encoder->common_encoder);\n    free(encoder);\n}\n\nInfraredStatus infrared_encoder_rc6_encode_manchester(\n    InfraredCommonEncoder* common_encoder,\n    uint32_t* duration,\n    bool* polarity) {\n    InfraredStatus status = InfraredStatusError;\n\n    bool toggle_bit = (common_encoder->bits_encoded == 4);\n    status = infrared_common_encode_manchester(common_encoder, duration, polarity);\n    if(toggle_bit) *duration *= 2;\n    return status;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.c",
    "content": "#include \"infrared_protocol_rc6_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_rc6 = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_RC6_PREAMBLE_MARK,\n            .preamble_space = INFRARED_RC6_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_RC6_BIT,\n            .preamble_tolerance = INFRARED_RC6_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_RC6_BIT_TOLERANCE,\n            .silence_time = INFRARED_RC6_SILENCE,\n            .min_split_time = INFRARED_RC6_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] =\n        1 + 3 + 1 + 8 +\n        8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command\n    .manchester_start_from_space = false,\n    .decode = infrared_decoder_rc6_decode_manchester,\n    .encode = infrared_encoder_rc6_encode_manchester,\n    .interpret = infrared_decoder_rc6_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = NULL,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_rc6 = {\n    .name = \"RC6\",\n    .address_length = 8,\n    .command_length = 8,\n    .frequency = INFRARED_RC6_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_RC6_DUTY_CYCLE,\n    .repeat_count = INFRARED_RC6_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolRC6)\n        return &infrared_protocol_variant_rc6;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   RC6 protocol description\n*   https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A\n****************************************************************************************************\n*      Preamble                       Manchester/biphase                       Silence\n*     mark/space                          Modulation\n*\n*    2666     889        444/888 - bit (x2 for toggle bit)                       2666\n*\n*  ________         __    __  __  __    ____  __  __  __  __  __  __  __  __\n* _        _________  ____  __  __  ____    __  __  __  __  __  __  __  __  _______________\n*                   | 1 | 0 | 0 | 0 |   0   |      ...      |      ...      |             |\n*                     s  m2  m1  m0     T     address (MSB)   command (MSB)\n*\n*    s - start bit (always 1)\n*    m0-2 - mode (000 for RC6)\n*    T - toggle bit, twice longer\n*    address - 8 bit\n*    command - 8 bit\n***************************************************************************************************/\n\nvoid* infrared_decoder_rc6_alloc(void);\nvoid infrared_decoder_rc6_reset(void* decoder);\nvoid infrared_decoder_rc6_free(void* decoder);\nInfraredMessage* infrared_decoder_rc6_check_ready(void* ctx);\nInfraredMessage* infrared_decoder_rc6_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_rc6_alloc(void);\nvoid infrared_encoder_rc6_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_rc6_free(void* decoder);\nInfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* polarity);\n\nconst InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_RC6_CARRIER_FREQUENCY 36000\n#define INFRARED_RC6_DUTY_CYCLE        0.33\n\n#define INFRARED_RC6_PREAMBLE_MARK      2666\n#define INFRARED_RC6_PREAMBLE_SPACE     889\n#define INFRARED_RC6_BIT                444 // half of time-quant for 1 bit\n#define INFRARED_RC6_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_RC6_BIT_TOLERANCE      120 // us\n/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */\n#define INFRARED_RC6_SILENCE            (2700 * 10)\n#define INFRARED_RC6_MIN_SPLIT_TIME     2700\n#define INFRARED_RC6_REPEAT_COUNT_MIN   1\n\nextern const InfraredCommonProtocolSpec infrared_protocol_rc6;\n\nbool infrared_decoder_rc6_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_decoder_rc6_decode_manchester(\n    InfraredCommonDecoder* decoder,\n    bool level,\n    uint32_t timing);\nInfraredStatus infrared_encoder_rc6_encode_manchester(\n    InfraredCommonEncoder* encoder_ptr,\n    uint32_t* duration,\n    bool* polarity);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c",
    "content": "#include \"infrared_protocol_rca_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_rca_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    uint32_t* data = (void*)&decoder->data;\n\n    uint8_t address = (*data & 0xF);\n    uint8_t command = (*data >> 4) & 0xFF;\n    uint8_t address_inverse = (*data >> 12) & 0xF;\n    uint8_t command_inverse = (*data >> 16) & 0xFF;\n    uint8_t inverse_address_inverse = (uint8_t)~address_inverse & 0xF;\n    uint8_t inverse_command_inverse = (uint8_t)~command_inverse;\n\n    if((command == inverse_command_inverse) && (address == inverse_address_inverse)) {\n        decoder->message.protocol = InfraredProtocolRCA;\n        decoder->message.address = address;\n        decoder->message.command = command;\n        decoder->message.repeat = false;\n        return true;\n    }\n\n    return false;\n}\n\nvoid* infrared_decoder_rca_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_rca);\n}\n\nInfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_rca_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_rca_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c",
    "content": "#include \"infrared_protocol_rca_i.h\"\n\n#include <core/check.h>\n\nvoid infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n    furi_assert(message);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint32_t* data = (void*)encoder->data;\n\n    uint8_t address = message->address;\n    uint8_t address_inverse = ~address;\n    uint8_t command = message->command;\n    uint8_t command_inverse = ~command;\n\n    *data = address & 0xF;\n    *data |= command << 4;\n    *data |= (address_inverse & 0xF) << 12;\n    *data |= command_inverse << 16;\n\n    encoder->bits_to_encode = encoder->protocol->databit_len[0];\n}\n\nvoid* infrared_encoder_rca_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_rca);\n}\n\nvoid infrared_encoder_rca_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    return infrared_common_encode(encoder_ptr, duration, level);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c",
    "content": "#include \"infrared_protocol_rca_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_rca = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_RCA_PREAMBLE_MARK,\n            .preamble_space = INFRARED_RCA_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_RCA_BIT1_MARK,\n            .bit1_space = INFRARED_RCA_BIT1_SPACE,\n            .bit0_mark = INFRARED_RCA_BIT0_MARK,\n            .bit0_space = INFRARED_RCA_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_RCA_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_RCA_BIT_TOLERANCE,\n            .silence_time = INFRARED_RCA_SILENCE,\n            .min_split_time = INFRARED_RCA_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 24,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_rca_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = NULL,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_rca = {\n    .name = \"RCA\",\n    .address_length = 4,\n    .command_length = 8,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_RCA_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolRCA)\n        return &infrared_protocol_variant_rca;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   RCA protocol description\n*   https://www.sbprojects.net/knowledge/ir/rca.php\n****************************************************************************************************\n*     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble\n*       mark      space            Modulation             up to period    repeat     repeat\n*                                                                          mark       space\n*\n*        4000      4000               24 bit              ...8000          4000       4000\n*     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________           \n* ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ___________\n*\n***************************************************************************************************/\n\nvoid* infrared_decoder_rca_alloc(void);\nvoid infrared_decoder_rca_reset(void* decoder);\nvoid infrared_decoder_rca_free(void* decoder);\nInfraredMessage* infrared_decoder_rca_check_ready(void* decoder);\nInfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_rca_alloc(void);\nInfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level);\nvoid infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_rca_free(void* encoder_ptr);\n\nconst InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_RCA_PREAMBLE_MARK  4000\n#define INFRARED_RCA_PREAMBLE_SPACE 4000\n#define INFRARED_RCA_BIT1_MARK      500\n#define INFRARED_RCA_BIT1_SPACE     2000\n#define INFRARED_RCA_BIT0_MARK      500\n#define INFRARED_RCA_BIT0_SPACE     1000\n#define INFRARED_RCA_REPEAT_PERIOD  8000\n#define INFRARED_RCA_SILENCE        INFRARED_RCA_REPEAT_PERIOD\n\n#define INFRARED_RCA_MIN_SPLIT_TIME     INFRARED_RCA_REPEAT_PAUSE_MIN\n#define INFRARED_RCA_REPEAT_PAUSE_MIN   4000\n#define INFRARED_RCA_REPEAT_PAUSE_MAX   150000\n#define INFRARED_RCA_REPEAT_COUNT_MIN   1\n#define INFRARED_RCA_REPEAT_MARK        INFRARED_RCA_PREAMBLE_MARK\n#define INFRARED_RCA_REPEAT_SPACE       INFRARED_RCA_PREAMBLE_SPACE\n#define INFRARED_RCA_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_RCA_BIT_TOLERANCE      120 // us\n\nextern const InfraredCommonProtocolSpec infrared_protocol_rca;\n\nbool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_decoder_rca_decode_repeat(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_rca_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c",
    "content": "#include \"infrared_protocol_samsung_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_samsung32_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    bool result = false;\n    uint8_t address1 = decoder->data[0];\n    uint8_t address2 = decoder->data[1];\n    uint8_t command = decoder->data[2];\n    uint8_t command_inverse = decoder->data[3];\n    uint8_t inverse_command_inverse = (uint8_t)~command_inverse;\n\n    if((address1 == address2) && (command == inverse_command_inverse)) {\n        decoder->message.command = command;\n        decoder->message.address = address1;\n        decoder->message.protocol = InfraredProtocolSamsung32;\n        decoder->message.repeat = false;\n        result = true;\n    }\n\n    return result;\n}\n\n// timings start from Space (delay between message and repeat)\nInfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    float preamble_tolerance = decoder->protocol->timings.preamble_tolerance;\n    uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance;\n    InfraredStatus status = InfraredStatusError;\n\n    if(decoder->timings_cnt < 6) return InfraredStatusOk;\n\n    if((decoder->timings[0] > INFRARED_SAMSUNG_REPEAT_PAUSE_MIN) &&\n       (decoder->timings[0] < INFRARED_SAMSUNG_REPEAT_PAUSE_MAX) &&\n       MATCH_TIMING(decoder->timings[1], INFRARED_SAMSUNG_REPEAT_MARK, preamble_tolerance) &&\n       MATCH_TIMING(decoder->timings[2], INFRARED_SAMSUNG_REPEAT_SPACE, preamble_tolerance) &&\n       MATCH_TIMING(decoder->timings[3], decoder->protocol->timings.bit1_mark, bit_tolerance) &&\n       MATCH_TIMING(decoder->timings[4], decoder->protocol->timings.bit1_space, bit_tolerance) &&\n       MATCH_TIMING(decoder->timings[5], decoder->protocol->timings.bit1_mark, bit_tolerance)) {\n        status = InfraredStatusReady;\n        decoder->timings_cnt = 0;\n    } else {\n        status = InfraredStatusError;\n    }\n\n    return status;\n}\n\nvoid* infrared_decoder_samsung32_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_samsung32);\n}\n\nInfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_samsung32_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_samsung32_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/samsung/infrared_encoder_samsung.c",
    "content": "#include \"infrared_protocol_samsung_i.h\"\n\n#include <core/check.h>\n#include <core/common_defines.h>\n\nstatic const uint32_t repeat_timings[] = {\n    INFRARED_SAMSUNG_REPEAT_PAUSE2,\n    INFRARED_SAMSUNG_REPEAT_MARK,\n    INFRARED_SAMSUNG_REPEAT_SPACE,\n    INFRARED_SAMSUNG_BIT1_MARK,\n    INFRARED_SAMSUNG_BIT1_SPACE,\n    INFRARED_SAMSUNG_BIT1_MARK,\n};\n\nvoid infrared_encoder_samsung32_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint8_t address = message->address;\n    uint8_t command = message->command;\n    uint8_t command_inverse = ~command;\n\n    uint32_t* data = (void*)encoder->data;\n    *data |= address;\n    *data |= address << 8;\n    *data |= command << 16;\n    *data |= command_inverse << 24;\n\n    encoder->bits_to_encode = encoder->protocol->databit_len[0];\n}\n\nInfraredStatus infrared_encoder_samsung32_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level) {\n    furi_assert(encoder);\n\n    /* space + 2 timings preambule + payload + stop bit */\n    uint32_t timings_encoded_up_to_repeat = 1 + 2 + encoder->bits_encoded * 2 + 1;\n    uint32_t repeat_cnt = encoder->timings_encoded - timings_encoded_up_to_repeat;\n\n    furi_assert(encoder->timings_encoded >= timings_encoded_up_to_repeat);\n\n    if(repeat_cnt > 0)\n        *duration = repeat_timings[repeat_cnt % COUNT_OF(repeat_timings)];\n    else\n        *duration = INFRARED_SAMSUNG_REPEAT_PAUSE1;\n\n    *level = repeat_cnt % 2;\n    ++encoder->timings_encoded;\n    bool done = (!((repeat_cnt + 1) % COUNT_OF(repeat_timings)));\n\n    return done ? InfraredStatusDone : InfraredStatusOk;\n}\n\nvoid* infrared_encoder_samsung32_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_samsung32);\n}\n\nvoid infrared_encoder_samsung32_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus\n    infrared_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    return infrared_common_encode(encoder_ptr, duration, level);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.c",
    "content": "#include \"infrared_protocol_samsung_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_samsung32 = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_SAMSUNG_PREAMBLE_MARK,\n            .preamble_space = INFRARED_SAMSUNG_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_SAMSUNG_BIT1_MARK,\n            .bit1_space = INFRARED_SAMSUNG_BIT1_SPACE,\n            .bit0_mark = INFRARED_SAMSUNG_BIT0_MARK,\n            .bit0_space = INFRARED_SAMSUNG_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_SAMSUNG_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_SAMSUNG_BIT_TOLERANCE,\n            .silence_time = INFRARED_SAMSUNG_SILENCE,\n            .min_split_time = INFRARED_SAMSUNG_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 32,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_samsung32_interpret,\n    .decode_repeat = infrared_decoder_samsung32_decode_repeat,\n    .encode_repeat = infrared_encoder_samsung32_encode_repeat,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_samsung32 = {\n    .name = \"Samsung32\",\n    .address_length = 8,\n    .command_length = 8,\n    .frequency = INFRARED_COMMON_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_COMMON_DUTY_CYCLE,\n    .repeat_count = INFRARED_SAMSUNG_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_samsung32_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolSamsung32)\n        return &infrared_protocol_variant_samsung32;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   SAMSUNG32 protocol description\n*   https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG\n****************************************************************************************************\n*  Preamble   Preamble     Pulse Distance/Width        Pause       Preamble   Preamble  Bit1  Stop\n*    mark      space           Modulation                           repeat     repeat          bit\n*                                                                    mark       space\n*\n*     4500      4500        32 bit + stop bit       40000/100000     4500       4500\n*  __________          _  _ _  _  _  _ _ _  _  _ _                ___________            _    _\n* _          __________ __ _ __ __ __ _ _ __ __ _ ________________           ____________ ____ ___\n*\n***************************************************************************************************/\n\nvoid* infrared_decoder_samsung32_alloc(void);\nvoid infrared_decoder_samsung32_reset(void* decoder);\nvoid infrared_decoder_samsung32_free(void* decoder);\nInfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx);\nInfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration);\n\nInfraredStatus\n    infrared_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level);\nvoid infrared_encoder_samsung32_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid* infrared_encoder_samsung32_alloc(void);\nvoid infrared_encoder_samsung32_free(void* encoder_ptr);\n\nconst InfraredProtocolVariant* infrared_protocol_samsung32_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_SAMSUNG_PREAMBLE_MARK      4500\n#define INFRARED_SAMSUNG_PREAMBLE_SPACE     4500\n#define INFRARED_SAMSUNG_BIT1_MARK          550\n#define INFRARED_SAMSUNG_BIT1_SPACE         1650\n#define INFRARED_SAMSUNG_BIT0_MARK          550\n#define INFRARED_SAMSUNG_BIT0_SPACE         550\n#define INFRARED_SAMSUNG_REPEAT_PAUSE_MIN   30000\n#define INFRARED_SAMSUNG_REPEAT_PAUSE_MAX   140000\n#define INFRARED_SAMSUNG_REPEAT_PAUSE1      46000\n#define INFRARED_SAMSUNG_REPEAT_PAUSE2      97000\n#define INFRARED_SAMSUNG_REPEAT_COUNT_MIN   1\n/* Samsung silence have to be greater than REPEAT MAX\n * otherwise there can be problems during unit tests parsing\n * of some data. Real tolerances we don't know, but in real life\n * silence time should be greater than max repeat time. This is\n * because of similar preambule timings for repeat and first messages. */\n#define INFRARED_SAMSUNG_MIN_SPLIT_TIME     5000\n#define INFRARED_SAMSUNG_SILENCE            145000\n#define INFRARED_SAMSUNG_REPEAT_MARK        4500\n#define INFRARED_SAMSUNG_REPEAT_SPACE       4500\n#define INFRARED_SAMSUNG_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_SAMSUNG_BIT_TOLERANCE      120 // us\n\nbool infrared_decoder_samsung32_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_samsung32_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n\nextern const InfraredCommonProtocolSpec infrared_protocol_samsung32;\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/sirc/infrared_decoder_sirc.c",
    "content": "#include \"infrared_protocol_sirc_i.h\"\n#include <core/check.h>\n\nInfraredMessage* infrared_decoder_sirc_check_ready(void* ctx) {\n    return infrared_common_decoder_check_ready(ctx);\n}\n\nbool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder) {\n    furi_assert(decoder);\n\n    uint32_t* data = (void*)&decoder->data[0];\n    uint16_t address = 0;\n    uint8_t command = 0;\n    InfraredProtocol protocol = InfraredProtocolUnknown;\n\n    if(decoder->databit_cnt == 12) {\n        address = (*data >> 7) & 0x1F;\n        command = *data & 0x7F;\n        protocol = InfraredProtocolSIRC;\n    } else if(decoder->databit_cnt == 15) {\n        address = (*data >> 7) & 0xFF;\n        command = *data & 0x7F;\n        protocol = InfraredProtocolSIRC15;\n    } else if(decoder->databit_cnt == 20) {\n        address = (*data >> 7) & 0x1FFF;\n        command = *data & 0x7F;\n        protocol = InfraredProtocolSIRC20;\n    } else {\n        return false;\n    }\n\n    decoder->message.protocol = protocol;\n    decoder->message.address = address;\n    decoder->message.command = command;\n    /* SIRC doesn't specify repeat detection */\n    decoder->message.repeat = false;\n\n    return true;\n}\n\nvoid* infrared_decoder_sirc_alloc(void) {\n    return infrared_common_decoder_alloc(&infrared_protocol_sirc);\n}\n\nInfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration) {\n    return infrared_common_decode(decoder, level, duration);\n}\n\nvoid infrared_decoder_sirc_free(void* decoder) {\n    infrared_common_decoder_free(decoder);\n}\n\nvoid infrared_decoder_sirc_reset(void* decoder) {\n    infrared_common_decoder_reset(decoder);\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/sirc/infrared_encoder_sirc.c",
    "content": "#include \"infrared_protocol_sirc_i.h\"\n#include <core/check.h>\n\nvoid infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message) {\n    furi_assert(encoder_ptr);\n    furi_assert(message);\n\n    InfraredCommonEncoder* encoder = encoder_ptr;\n    infrared_common_encoder_reset(encoder);\n\n    uint32_t* data = (void*)encoder->data;\n\n    if(message->protocol == InfraredProtocolSIRC) {\n        *data = (message->command & 0x7F);\n        *data |= (message->address & 0x1F) << 7;\n        encoder->bits_to_encode = 12;\n    } else if(message->protocol == InfraredProtocolSIRC15) {\n        *data = (message->command & 0x7F);\n        *data |= (message->address & 0xFF) << 7;\n        encoder->bits_to_encode = 15;\n    } else if(message->protocol == InfraredProtocolSIRC20) {\n        *data = (message->command & 0x7F);\n        *data |= (message->address & 0x1FFF) << 7;\n        encoder->bits_to_encode = 20;\n    } else {\n        furi_crash();\n    }\n}\n\nInfraredStatus infrared_encoder_sirc_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level) {\n    furi_assert(encoder);\n\n    furi_assert(encoder->timings_encoded == (1u + 2 + encoder->bits_to_encode * 2 - 1));\n\n    furi_assert(encoder->timings_sum < INFRARED_SIRC_REPEAT_PERIOD);\n    *duration = INFRARED_SIRC_REPEAT_PERIOD - encoder->timings_sum;\n    *level = false;\n\n    encoder->timings_sum = 0;\n    encoder->timings_encoded = 1;\n    encoder->bits_encoded = 0;\n    encoder->state = InfraredCommonEncoderStatePreamble;\n\n    return InfraredStatusOk;\n}\n\nvoid* infrared_encoder_sirc_alloc(void) {\n    return infrared_common_encoder_alloc(&infrared_protocol_sirc);\n}\n\nvoid infrared_encoder_sirc_free(void* encoder_ptr) {\n    infrared_common_encoder_free(encoder_ptr);\n}\n\nInfraredStatus infrared_encoder_sirc_encode(void* encoder_ptr, uint32_t* duration, bool* level) {\n    InfraredCommonEncoder* encoder = encoder_ptr;\n\n    InfraredStatus status = infrared_common_encode(encoder, duration, level);\n    if((status == InfraredStatusOk) && (encoder->bits_encoded == encoder->bits_to_encode)) {\n        furi_assert(!*level);\n        status = InfraredStatusDone;\n        encoder->state = InfraredCommonEncoderStateEncodeRepeat;\n    }\n    return status;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.c",
    "content": "#include \"infrared_protocol_sirc_i.h\"\n\nconst InfraredCommonProtocolSpec infrared_protocol_sirc = {\n    .timings =\n        {\n            .preamble_mark = INFRARED_SIRC_PREAMBLE_MARK,\n            .preamble_space = INFRARED_SIRC_PREAMBLE_SPACE,\n            .bit1_mark = INFRARED_SIRC_BIT1_MARK,\n            .bit1_space = INFRARED_SIRC_BIT1_SPACE,\n            .bit0_mark = INFRARED_SIRC_BIT0_MARK,\n            .bit0_space = INFRARED_SIRC_BIT0_SPACE,\n            .preamble_tolerance = INFRARED_SIRC_PREAMBLE_TOLERANCE,\n            .bit_tolerance = INFRARED_SIRC_BIT_TOLERANCE,\n            .silence_time = INFRARED_SIRC_SILENCE,\n            .min_split_time = INFRARED_SIRC_MIN_SPLIT_TIME,\n        },\n    .databit_len[0] = 20,\n    .databit_len[1] = 15,\n    .databit_len[2] = 12,\n    .decode = infrared_common_decode_pdwm,\n    .encode = infrared_common_encode_pdwm,\n    .interpret = infrared_decoder_sirc_interpret,\n    .decode_repeat = NULL,\n    .encode_repeat = infrared_encoder_sirc_encode_repeat,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_sirc = {\n    .name = \"SIRC\",\n    .address_length = 5,\n    .command_length = 7,\n    .frequency = INFRARED_SIRC_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_SIRC_DUTY_CYCLE,\n    .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_sirc15 = {\n    .name = \"SIRC15\",\n    .address_length = 8,\n    .command_length = 7,\n    .frequency = INFRARED_SIRC_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_SIRC_DUTY_CYCLE,\n    .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN,\n};\n\nstatic const InfraredProtocolVariant infrared_protocol_variant_sirc20 = {\n    .name = \"SIRC20\",\n    .address_length = 13,\n    .command_length = 7,\n    .frequency = INFRARED_SIRC_CARRIER_FREQUENCY,\n    .duty_cycle = INFRARED_SIRC_DUTY_CYCLE,\n    .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN,\n};\n\nconst InfraredProtocolVariant* infrared_protocol_sirc_get_variant(InfraredProtocol protocol) {\n    if(protocol == InfraredProtocolSIRC)\n        return &infrared_protocol_variant_sirc;\n    else if(protocol == InfraredProtocolSIRC15)\n        return &infrared_protocol_variant_sirc15;\n    else if(protocol == InfraredProtocolSIRC20)\n        return &infrared_protocol_variant_sirc20;\n    else\n        return NULL;\n}\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.h",
    "content": "#pragma once\n\n#include \"../infrared_i.h\"\n\n/***************************************************************************************************\n*   Sony SIRC protocol description\n*   https://www.sbprojects.net/knowledge/ir/sirc.php\n*   http://picprojects.org.uk/\n****************************************************************************************************\n*      Preamble  Preamble     Pulse Width Modulation           Pause             Entirely repeat\n*        mark     space                                     up to period             message..\n*\n*        2400      600      12/15/20 bits (600,1200)         ...45000          2400      600\n*     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                    __________          _ _\n* ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ____________________          __________ _\n*                        |    command    |   address    |\n*                 SIRC   |     7b LSB    |    5b LSB    |\n*                 SIRC15 |     7b LSB    |    8b LSB    |\n*                 SIRC20 |     7b LSB    |    13b LSB   |\n*\n* No way to determine either next message is repeat or not,\n* so recognize only fact message received. Sony remotes always send at least 3 messages.\n* Assume 8 last extended bits for SIRC20 are address bits.\n***************************************************************************************************/\n\nvoid* infrared_decoder_sirc_alloc(void);\nvoid infrared_decoder_sirc_reset(void* decoder);\nInfraredMessage* infrared_decoder_sirc_check_ready(void* decoder);\nvoid infrared_decoder_sirc_free(void* decoder);\nInfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration);\n\nvoid* infrared_encoder_sirc_alloc(void);\nvoid infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message);\nvoid infrared_encoder_sirc_free(void* decoder);\nInfraredStatus infrared_encoder_sirc_encode(void* encoder_ptr, uint32_t* duration, bool* polarity);\n\nconst InfraredProtocolVariant* infrared_protocol_sirc_get_variant(InfraredProtocol protocol);\n"
  },
  {
    "path": "lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc_i.h",
    "content": "#pragma once\n\n#include \"../common/infrared_common_i.h\"\n\n#define INFRARED_SIRC_CARRIER_FREQUENCY  40000\n#define INFRARED_SIRC_DUTY_CYCLE         0.33\n#define INFRARED_SIRC_PREAMBLE_MARK      2400\n#define INFRARED_SIRC_PREAMBLE_SPACE     600\n#define INFRARED_SIRC_BIT1_MARK          1200\n#define INFRARED_SIRC_BIT1_SPACE         600\n#define INFRARED_SIRC_BIT0_MARK          600\n#define INFRARED_SIRC_BIT0_SPACE         600\n#define INFRARED_SIRC_PREAMBLE_TOLERANCE 200 // us\n#define INFRARED_SIRC_BIT_TOLERANCE      120 // us\n#define INFRARED_SIRC_SILENCE            10000\n#define INFRARED_SIRC_MIN_SPLIT_TIME     (INFRARED_SIRC_SILENCE - 1000)\n#define INFRARED_SIRC_REPEAT_PERIOD      45000\n#define INFRARED_SIRC_REPEAT_COUNT_MIN   3\n\nextern const InfraredCommonProtocolSpec infrared_protocol_sirc;\n\nbool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder);\nInfraredStatus infrared_encoder_sirc_encode_repeat(\n    InfraredCommonEncoder* encoder,\n    uint32_t* duration,\n    bool* level);\n"
  },
  {
    "path": "lib/infrared/signal/infrared_brute_force.c",
    "content": "#include \"infrared_brute_force.h\"\n\n#include <stdlib.h>\n#include <m-dict.h>\n#include <m-array.h>\n#include <flipper_format/flipper_format.h>\n\n#include \"infrared_signal.h\"\n\nARRAY_DEF(SignalPositionArray, size_t, M_DEFAULT_OPLIST); //-V658\n\ntypedef struct {\n    size_t index;\n    SignalPositionArray_t signals;\n} InfraredBruteForceRecord;\n\nstatic inline void ir_bf_record_init(InfraredBruteForceRecord* record) {\n    record->index = 0;\n    SignalPositionArray_init(record->signals);\n}\n#define IR_BF_RECORD_INIT(r) (ir_bf_record_init(&(r)))\n\nstatic inline void\n    ir_bf_record_init_set(InfraredBruteForceRecord* dest, const InfraredBruteForceRecord* src) {\n    dest->index = src->index;\n    SignalPositionArray_init_set(dest->signals, src->signals);\n}\n#define IR_BF_RECORD_INIT_SET(d, s) (ir_bf_record_init_set(&(d), &(s)))\n\nstatic inline void\n    ir_bf_record_set(InfraredBruteForceRecord* dest, const InfraredBruteForceRecord* src) {\n    dest->index = src->index;\n    SignalPositionArray_set(dest->signals, src->signals);\n}\n#define IR_BF_RECORD_SET(d, s) (ir_bf_record_set(&(d), &(s)))\n\nstatic inline void ir_bf_record_clear(InfraredBruteForceRecord* record) {\n    SignalPositionArray_clear(record->signals);\n}\n#define IR_BF_RECORD_CLEAR(r) (ir_bf_record_clear(&(r)))\n\n#define IR_BF_RECORD_OPLIST           \\\n    (INIT(IR_BF_RECORD_INIT),         \\\n     INIT_SET(IR_BF_RECORD_INIT_SET), \\\n     SET(IR_BF_RECORD_SET),           \\\n     CLEAR(IR_BF_RECORD_CLEAR))\n\nDICT_DEF2(\n    InfraredBruteForceRecordDict,\n    FuriString*,\n    FURI_STRING_OPLIST,\n    InfraredBruteForceRecord,\n    IR_BF_RECORD_OPLIST);\n\nstruct InfraredBruteForce {\n    FlipperFormat* ff;\n    const char* db_filename;\n    FuriString* current_record_name;\n    InfraredBruteForceRecord current_record;\n    InfraredSignal* current_signal;\n    InfraredBruteForceRecordDict_t records;\n    bool is_started;\n};\n\nInfraredBruteForce* infrared_brute_force_alloc(void) {\n    InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce));\n    brute_force->ff = NULL;\n    brute_force->db_filename = NULL;\n    brute_force->current_signal = NULL;\n    brute_force->is_started = false;\n    brute_force->current_record_name = furi_string_alloc();\n    InfraredBruteForceRecordDict_init(brute_force->records);\n    return brute_force;\n}\n\nvoid infrared_brute_force_free(InfraredBruteForce* brute_force) {\n    furi_check(brute_force);\n    furi_assert(!brute_force->is_started);\n    InfraredBruteForceRecordDict_clear(brute_force->records);\n    furi_string_free(brute_force->current_record_name);\n    free(brute_force);\n}\n\nvoid infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) {\n    furi_check(brute_force);\n    furi_assert(!brute_force->is_started);\n    brute_force->db_filename = db_filename;\n}\n\nInfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) {\n    furi_check(brute_force);\n    furi_assert(!brute_force->is_started);\n    furi_assert(brute_force->db_filename);\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n    FuriString* signal_name = furi_string_alloc();\n    InfraredSignal* signal = infrared_signal_alloc();\n\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) {\n            error = InfraredErrorCodeFileOperationFailed;\n            break;\n        }\n\n        bool signal_valid = false;\n        while(infrared_signal_read_name(ff, signal_name) == InfraredErrorCodeNone) {\n            size_t signal_start = flipper_format_tell(ff);\n            error = infrared_signal_read_body(signal, ff);\n            signal_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal);\n            if(!signal_valid) break;\n\n            InfraredBruteForceRecord* record =\n                InfraredBruteForceRecordDict_get(brute_force->records, signal_name);\n            furi_assert(record);\n            SignalPositionArray_push_back(record->signals, signal_start);\n        }\n        if(!signal_valid) break;\n    } while(false);\n\n    infrared_signal_free(signal);\n    furi_string_free(signal_name);\n\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n    return error;\n}\n\nbool infrared_brute_force_start(\n    InfraredBruteForce* brute_force,\n    uint32_t index,\n    uint32_t* record_count) {\n    furi_check(brute_force);\n    furi_assert(!brute_force->is_started);\n    bool success = false;\n    *record_count = 0;\n\n    InfraredBruteForceRecordDict_it_t it;\n    for(InfraredBruteForceRecordDict_it(it, brute_force->records);\n        !InfraredBruteForceRecordDict_end_p(it);\n        InfraredBruteForceRecordDict_next(it)) {\n        const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it);\n        if(record->value.index == index) {\n            *record_count = SignalPositionArray_size(record->value.signals);\n            if(*record_count) {\n                furi_string_set(brute_force->current_record_name, record->key);\n                brute_force->current_record = record->value;\n            }\n            break;\n        }\n    }\n\n    if(*record_count) {\n        Storage* storage = furi_record_open(RECORD_STORAGE);\n        brute_force->ff = flipper_format_buffered_file_alloc(storage);\n        brute_force->current_signal = infrared_signal_alloc();\n        brute_force->is_started = true;\n        success =\n            flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename);\n        if(!success) infrared_brute_force_stop(brute_force);\n    }\n    return success;\n}\n\nbool infrared_brute_force_is_started(const InfraredBruteForce* brute_force) {\n    furi_check(brute_force);\n    return brute_force->is_started;\n}\n\nvoid infrared_brute_force_stop(InfraredBruteForce* brute_force) {\n    furi_check(brute_force);\n    furi_assert(brute_force->is_started);\n    furi_string_reset(brute_force->current_record_name);\n    infrared_signal_free(brute_force->current_signal);\n    flipper_format_free(brute_force->ff);\n    brute_force->current_signal = NULL;\n    brute_force->ff = NULL;\n    brute_force->is_started = false;\n    furi_record_close(RECORD_STORAGE);\n}\n\nbool infrared_brute_force_send(InfraredBruteForce* brute_force, uint32_t signal_index) {\n    furi_check(brute_force);\n    furi_assert(brute_force->is_started);\n\n    if(signal_index >= SignalPositionArray_size(brute_force->current_record.signals)) return false;\n\n    size_t signal_start =\n        *SignalPositionArray_cget(brute_force->current_record.signals, signal_index);\n    if(!flipper_format_seek(brute_force->ff, signal_start, FlipperFormatOffsetFromStart))\n        return false;\n\n    if(INFRARED_ERROR_PRESENT(\n           infrared_signal_read_body(brute_force->current_signal, brute_force->ff)))\n        return false;\n\n    infrared_signal_transmit(brute_force->current_signal);\n    return true;\n}\n\nvoid infrared_brute_force_add_record(\n    InfraredBruteForce* brute_force,\n    uint32_t index,\n    const char* name) {\n    InfraredBruteForceRecord value;\n    ir_bf_record_init(&value);\n    value.index = index;\n    FuriString* key;\n    key = furi_string_alloc_set(name);\n    InfraredBruteForceRecordDict_set_at(brute_force->records, key, value);\n    furi_string_free(key);\n}\n\nvoid infrared_brute_force_reset(InfraredBruteForce* brute_force) {\n    furi_assert(!brute_force->is_started);\n    InfraredBruteForceRecordDict_reset(brute_force->records);\n}\n"
  },
  {
    "path": "lib/infrared/signal/infrared_brute_force.h",
    "content": "/**\n * @file infrared_brute_force.h\n * @brief Infrared signal brute-forcing library.\n *\n * The BruteForce library is used to send large quantities of signals,\n * sorted by a category. It is used to implement the Universal Remote\n * feature.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"infrared_error_code.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief InfraredBruteForce opaque type declaration.\n */\ntypedef struct InfraredBruteForce InfraredBruteForce;\n\n/**\n * @brief Create a new InfraredBruteForce instance.\n *\n * @returns pointer to the created instance.\n */\nInfraredBruteForce* infrared_brute_force_alloc(void);\n\n/**\n * @brief Delete an InfraredBruteForce instance.\n *\n * @param[in,out] brute_force pointer to the instance to be deleted.\n */\nvoid infrared_brute_force_free(InfraredBruteForce* brute_force);\n\n/**\n * @brief Set an InfraredBruteForce instance to use a signal database contained in a file.\n *\n * @param[in,out] brute_force pointer to the instance to be configured.\n * @param[in] db_filename pointer to a zero-terminated string containing a full path to the database file.\n */\nvoid infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename);\n\n/**\n * @brief Build a signal dictionary from a previously set database file.\n *\n * This function must be called each time after setting the database via\n * a infrared_brute_force_set_db_filename() call.\n *\n * @param[in,out] brute_force pointer to the instance to be updated.\n * @returns InfraredErrorCodeNone on success, otherwise error code.\n */\nInfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force);\n\n/**\n * @brief Start transmitting signals from a category stored in the dictionary.\n *\n * The function locates the category identified by @p index, reports the number of\n * records it contains via @p record_count, and prepares the brute-force instance\n * to transmit those signals. On failure @p record_count is set to zero.\n *\n * @param[in,out] brute_force pointer to the instance to be started.\n * @param[in] index index of the signal category in the dictionary.\n * @param[out] record_count pointer that receives the number of records in the category.\n * @returns true if the category is found and the backing database file is opened, false otherwise.\n */\nbool infrared_brute_force_start(\n    InfraredBruteForce* brute_force,\n    uint32_t index,\n    uint32_t* record_count);\n\n/**\n * @brief Determine whether the transmission was started.\n *\n * @param[in] brute_force pointer to the instance to be tested.\n * @returns true if transmission was started, false otherwise.\n */\nbool infrared_brute_force_is_started(const InfraredBruteForce* brute_force);\n\n/**\n * @brief Stop transmitting the signals.\n *\n * @param[in] brute_force pointer to the instance to be stopped.\n */\nvoid infrared_brute_force_stop(InfraredBruteForce* brute_force);\n\n/**\n * @brief Send an arbitrary signal from the chosen category.\n * \n * @param[in] brute_force pointer to the instance\n * @param signal_index the index of the signal within the category, must be\n *                     between 0 and `record_count` as told by\n *                     `infrared_brute_force_start`\n * \n * @returns true on success, false otherwise\n */\nbool infrared_brute_force_send(InfraredBruteForce* brute_force, uint32_t signal_index);\n\n/**\n * @brief Add a signal category to an InfraredBruteForce instance's dictionary.\n *\n * @param[in,out] brute_force pointer to the instance to be updated.\n * @param[in] index index of the category to be added.\n * @param[in] name name of the category to be added.\n */\nvoid infrared_brute_force_add_record(\n    InfraredBruteForce* brute_force,\n    uint32_t index,\n    const char* name);\n\n/**\n * @brief Reset an InfraredBruteForce instance.\n *\n * @param[in,out] brute_force pointer to the instance to be reset.\n */\nvoid infrared_brute_force_reset(InfraredBruteForce* brute_force);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/signal/infrared_error_code.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    InfraredErrorCodeNone = 0,\n    InfraredErrorCodeFileOperationFailed = 0x800000,\n    InfraredErrorCodeWrongFileType = 0x80000100,\n    InfraredErrorCodeWrongFileVersion = 0x80000200,\n\n    //Common signal errors\n    InfraredErrorCodeSignalTypeUnknown = 0x80000300,\n    InfraredErrorCodeSignalNameNotFound = 0x80000400,\n    InfraredErrorCodeSignalUnableToReadType = 0x80000500,\n    InfraredErrorCodeSignalUnableToWriteType = 0x80000600,\n\n    //Raw signal errors\n    InfraredErrorCodeSignalRawUnableToReadFrequency = 0x80000700,\n    InfraredErrorCodeSignalRawUnableToReadDutyCycle = 0x80000800,\n    InfraredErrorCodeSignalRawUnableToReadTimingsSize = 0x80000900,\n    InfraredErrorCodeSignalRawUnableToReadTooLongData = 0x80000A00,\n    InfraredErrorCodeSignalRawUnableToReadData = 0x80000B00,\n\n    InfraredErrorCodeSignalRawUnableToWriteFrequency = 0x80000C00,\n    InfraredErrorCodeSignalRawUnableToWriteDutyCycle = 0x80000D00,\n    InfraredErrorCodeSignalRawUnableToWriteData = 0x80000E00,\n\n    //Message signal errors\n    InfraredErrorCodeSignalMessageUnableToReadProtocol = 0x80000F00,\n    InfraredErrorCodeSignalMessageUnableToReadAddress = 0x80001000,\n    InfraredErrorCodeSignalMessageUnableToReadCommand = 0x80001100,\n    InfraredErrorCodeSignalMessageIsInvalid = 0x80001200,\n\n    InfraredErrorCodeSignalMessageUnableToWriteProtocol = 0x80001300,\n    InfraredErrorCodeSignalMessageUnableToWriteAddress = 0x80001400,\n    InfraredErrorCodeSignalMessageUnableToWriteCommand = 0x80001500,\n} InfraredErrorCode;\n\n#define INFRARED_ERROR_CODE_MASK  (0xFFFFFF00)\n#define INFRARED_ERROR_INDEX_MASK (0x000000FF)\n\n#define INFRARED_ERROR_GET_CODE(error)        ((error) & INFRARED_ERROR_CODE_MASK)\n#define INFRARED_ERROR_GET_INDEX(error)       ((error) & INFRARED_ERROR_INDEX_MASK)\n#define INFRARED_ERROR_SET_INDEX(code, index) ((code) |= ((index) & INFRARED_ERROR_INDEX_MASK))\n\n#define INFRARED_ERROR_PRESENT(error)          (INFRARED_ERROR_GET_CODE(error) != InfraredErrorCodeNone)\n#define INFRARED_ERROR_CHECK(error, test_code) (INFRARED_ERROR_GET_CODE(error) == (test_code))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/signal/infrared_signal.c",
    "content": "#include \"infrared_signal.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <core/check.h>\n#include <infrared_worker.h>\n#include <infrared_transmit.h>\n\n#define TAG \"InfraredSignal\"\n\n// Common keys\n#define INFRARED_SIGNAL_NAME_KEY \"name\"\n#define INFRARED_SIGNAL_TYPE_KEY \"type\"\n\n// Type key values\n#define INFRARED_SIGNAL_TYPE_RAW    \"raw\"\n#define INFRARED_SIGNAL_TYPE_PARSED \"parsed\"\n\n// Raw signal keys\n#define INFRARED_SIGNAL_DATA_KEY       \"data\"\n#define INFRARED_SIGNAL_FREQUENCY_KEY  \"frequency\"\n#define INFRARED_SIGNAL_DUTY_CYCLE_KEY \"duty_cycle\"\n\n// Parsed signal keys\n#define INFRARED_SIGNAL_PROTOCOL_KEY \"protocol\"\n#define INFRARED_SIGNAL_ADDRESS_KEY  \"address\"\n#define INFRARED_SIGNAL_COMMAND_KEY  \"command\"\n\nstruct InfraredSignal {\n    bool is_raw;\n    union {\n        InfraredMessage message;\n        InfraredRawSignal raw;\n    } payload;\n};\n\nstatic void infrared_signal_clear_timings(InfraredSignal* signal) {\n    if(signal->is_raw) {\n        free(signal->payload.raw.timings);\n        signal->payload.raw.timings_size = 0;\n        signal->payload.raw.timings = NULL;\n    }\n}\n\nstatic bool infrared_signal_is_message_valid(const InfraredMessage* message) {\n    if(!infrared_is_protocol_valid(message->protocol)) {\n        FURI_LOG_E(TAG, \"Unknown protocol\");\n        return false;\n    }\n\n    uint32_t address_length = infrared_get_protocol_address_length(message->protocol);\n    uint32_t address_mask = (1UL << address_length) - 1;\n\n    if(message->address != (message->address & address_mask)) {\n        FURI_LOG_E(\n            TAG,\n            \"Address is out of range (mask 0x%08lX): 0x%lX\\r\\n\",\n            address_mask,\n            message->address);\n        return false;\n    }\n\n    uint32_t command_length = infrared_get_protocol_command_length(message->protocol);\n    uint32_t command_mask = (1UL << command_length) - 1;\n\n    if(message->command != (message->command & command_mask)) {\n        FURI_LOG_E(\n            TAG,\n            \"Command is out of range (mask 0x%08lX): 0x%lX\\r\\n\",\n            command_mask,\n            message->command);\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) {\n    if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {\n        FURI_LOG_E(\n            TAG,\n            \"Frequency is out of range (%X - %X): %lX\",\n            INFRARED_MIN_FREQUENCY,\n            INFRARED_MAX_FREQUENCY,\n            raw->frequency);\n        return false;\n\n    } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1.f)) {\n        FURI_LOG_E(TAG, \"Duty cycle is out of range (0 - 1): %f\", (double)raw->duty_cycle);\n        return false;\n\n    } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {\n        FURI_LOG_E(\n            TAG,\n            \"Timings amount is out of range (0 - %X): %zX\",\n            MAX_TIMINGS_AMOUNT,\n            raw->timings_size);\n        return false;\n    }\n\n    return true;\n}\n\nstatic inline InfraredErrorCode\n    infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) {\n    const char* protocol_name = infrared_get_protocol_name(message->protocol);\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    do {\n        if(!flipper_format_write_string_cstr(\n               ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED)) {\n            error = InfraredErrorCodeSignalUnableToWriteType;\n            break;\n        }\n\n        if(!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name)) {\n            error = InfraredErrorCodeSignalMessageUnableToWriteProtocol;\n            break;\n        }\n\n        if(!flipper_format_write_hex(\n               ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4)) {\n            error = InfraredErrorCodeSignalMessageUnableToWriteAddress;\n            break;\n        }\n\n        if(!flipper_format_write_hex(\n               ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4)) {\n            error = InfraredErrorCodeSignalMessageUnableToWriteCommand;\n            break;\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic inline InfraredErrorCode\n    infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) {\n    furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    do {\n        if(!flipper_format_write_string_cstr(\n               ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW)) {\n            error = InfraredErrorCodeSignalUnableToWriteType;\n            break;\n        }\n\n        if(!flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1)) {\n            error = InfraredErrorCodeSignalRawUnableToWriteFrequency;\n            break;\n        }\n\n        if(!flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1)) {\n            error = InfraredErrorCodeSignalRawUnableToWriteDutyCycle;\n            break;\n        }\n\n        if(!flipper_format_write_uint32(\n               ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size)) {\n            error = InfraredErrorCodeSignalRawUnableToWriteData;\n            break;\n        }\n    } while(false);\n    return error;\n}\n\nstatic inline InfraredErrorCode\n    infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {\n    FuriString* buf;\n    buf = furi_string_alloc();\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) {\n            error = InfraredErrorCodeSignalMessageUnableToReadProtocol;\n            break;\n        }\n\n        InfraredMessage message;\n        message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));\n\n        if(!flipper_format_read_hex(\n               ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) {\n            error = InfraredErrorCodeSignalMessageUnableToReadAddress;\n            break;\n        }\n        if(!flipper_format_read_hex(\n               ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) {\n            error = InfraredErrorCodeSignalMessageUnableToReadCommand;\n            break;\n        }\n\n        if(!infrared_signal_is_message_valid(&message)) {\n            error = InfraredErrorCodeSignalMessageIsInvalid;\n            break;\n        }\n\n        infrared_signal_set_message(signal, &message);\n    } while(false);\n\n    furi_string_free(buf);\n    return error;\n}\n\nstatic inline InfraredErrorCode\n    infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        uint32_t frequency;\n        if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) {\n            error = InfraredErrorCodeSignalRawUnableToReadFrequency;\n            break;\n        }\n\n        float duty_cycle;\n        if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) {\n            error = InfraredErrorCodeSignalRawUnableToReadDutyCycle;\n            break;\n        }\n\n        uint32_t timings_size;\n        if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) {\n            error = InfraredErrorCodeSignalRawUnableToReadTimingsSize;\n            break;\n        }\n\n        if(timings_size > MAX_TIMINGS_AMOUNT) {\n            error = InfraredErrorCodeSignalRawUnableToReadTooLongData;\n            break;\n        }\n\n        uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);\n        if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_DATA_KEY, timings, timings_size)) {\n            error = InfraredErrorCodeSignalRawUnableToReadData;\n            free(timings);\n            break;\n        }\n\n        infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);\n        free(timings);\n\n        error = InfraredErrorCodeNone;\n    } while(false);\n\n    return error;\n}\n\nInfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {\n    FuriString* tmp = furi_string_alloc();\n\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) {\n            error = InfraredErrorCodeSignalUnableToReadType;\n            break;\n        }\n\n        if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) {\n            error = infrared_signal_read_raw(signal, ff);\n        } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) {\n            error = infrared_signal_read_message(signal, ff);\n        } else {\n            FURI_LOG_E(TAG, \"Unknown signal type: %s\", furi_string_get_cstr(tmp));\n            error = InfraredErrorCodeSignalTypeUnknown;\n            break;\n        }\n    } while(false);\n\n    furi_string_free(tmp);\n\n    return error;\n}\n\nInfraredSignal* infrared_signal_alloc(void) {\n    InfraredSignal* signal = malloc(sizeof(InfraredSignal));\n\n    signal->is_raw = false;\n    signal->payload.message.protocol = InfraredProtocolUnknown;\n\n    return signal;\n}\n\nvoid infrared_signal_free(InfraredSignal* signal) {\n    infrared_signal_clear_timings(signal);\n    free(signal);\n}\n\nbool infrared_signal_is_raw(const InfraredSignal* signal) {\n    return signal->is_raw;\n}\n\nbool infrared_signal_is_valid(const InfraredSignal* signal) {\n    return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :\n                            infrared_signal_is_message_valid(&signal->payload.message);\n}\n\nvoid infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {\n    if(other->is_raw) {\n        const InfraredRawSignal* raw = &other->payload.raw;\n        infrared_signal_set_raw_signal(\n            signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);\n    } else {\n        const InfraredMessage* message = &other->payload.message;\n        infrared_signal_set_message(signal, message);\n    }\n}\n\nvoid infrared_signal_set_raw_signal(\n    InfraredSignal* signal,\n    const uint32_t* timings,\n    size_t timings_size,\n    uint32_t frequency,\n    float duty_cycle) {\n    infrared_signal_clear_timings(signal);\n\n    signal->is_raw = true;\n\n    signal->payload.raw.timings_size = timings_size;\n    signal->payload.raw.frequency = frequency;\n    signal->payload.raw.duty_cycle = duty_cycle;\n\n    signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));\n    memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));\n}\n\nconst InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal) {\n    furi_assert(signal->is_raw);\n    return &signal->payload.raw;\n}\n\nvoid infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {\n    infrared_signal_clear_timings(signal);\n\n    signal->is_raw = false;\n    signal->payload.message = *message;\n}\n\nconst InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) {\n    furi_assert(!signal->is_raw);\n    return &signal->payload.message;\n}\n\nInfraredErrorCode\n    infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) {\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    if(!flipper_format_write_comment_cstr(ff, \"\") ||\n       !flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) {\n        error = InfraredErrorCodeFileOperationFailed;\n    } else if(signal->is_raw) {\n        error = infrared_signal_save_raw(&signal->payload.raw, ff);\n    } else {\n        error = infrared_signal_save_message(&signal->payload.message, ff);\n    }\n\n    return error;\n}\n\nInfraredErrorCode\n    infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {\n    InfraredErrorCode error = InfraredErrorCodeNone;\n\n    do {\n        error = infrared_signal_read_name(ff, name);\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        error = infrared_signal_read_body(signal, ff);\n    } while(false);\n\n    return error;\n}\n\nInfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name) {\n    return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name) ?\n               InfraredErrorCodeNone :\n               InfraredErrorCodeSignalNameNotFound;\n}\n\nInfraredErrorCode infrared_signal_search_by_name_and_read(\n    InfraredSignal* signal,\n    FlipperFormat* ff,\n    const char* name) {\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    FuriString* tmp = furi_string_alloc();\n\n    do {\n        error = infrared_signal_read_name(ff, tmp);\n        if(INFRARED_ERROR_PRESENT(error)) break;\n\n        if(furi_string_equal(tmp, name)) {\n            error = infrared_signal_read_body(signal, ff);\n            break;\n        }\n    } while(true);\n\n    furi_string_free(tmp);\n    return error;\n}\n\nInfraredErrorCode infrared_signal_search_by_index_and_read(\n    InfraredSignal* signal,\n    FlipperFormat* ff,\n    size_t index) {\n    InfraredErrorCode error = InfraredErrorCodeNone;\n    FuriString* tmp = furi_string_alloc();\n\n    for(uint32_t i = 0;; ++i) {\n        error = infrared_signal_read_name(ff, tmp);\n        if(INFRARED_ERROR_PRESENT(error)) {\n            INFRARED_ERROR_SET_INDEX(error, i);\n            break;\n        }\n\n        if(i == index) {\n            error = infrared_signal_read_body(signal, ff);\n            if(INFRARED_ERROR_PRESENT(error)) {\n                INFRARED_ERROR_SET_INDEX(error, i);\n            }\n            break;\n        }\n    }\n\n    furi_string_free(tmp);\n    return error;\n}\n\nvoid infrared_signal_transmit(const InfraredSignal* signal) {\n    if(signal->is_raw) {\n        const InfraredRawSignal* raw_signal = &signal->payload.raw;\n        infrared_send_raw_ext(\n            raw_signal->timings,\n            raw_signal->timings_size,\n            true,\n            raw_signal->frequency,\n            raw_signal->duty_cycle);\n    } else {\n        const InfraredMessage* message = &signal->payload.message;\n        infrared_send(message, 1);\n    }\n}\n"
  },
  {
    "path": "lib/infrared/signal/infrared_signal.h",
    "content": "/**\n * @file infrared_signal.h\n * @brief Infrared signal library.\n *\n * Infrared signals may be of two types:\n * - known to the infrared signal decoder, or *parsed* signals\n * - the rest, or *raw* signals, which are treated merely as a set of timings.\n */\n#pragma once\n\n#include \"infrared_error_code.h\"\n#include <flipper_format/flipper_format.h>\n#include <infrared.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief InfraredSignal opaque type declaration.\n */\ntypedef struct InfraredSignal InfraredSignal;\n\n/**\n * @brief Raw signal type definition.\n *\n * Measurement units used:\n * - time: microseconds (uS)\n * - frequency: Hertz (Hz)\n * - duty_cycle: no units, fraction between 0 and 1.\n */\ntypedef struct {\n    size_t timings_size; /**< Number of elements in the timings array. */\n    uint32_t* timings; /**< Pointer to an array of timings describing the signal. */\n    uint32_t frequency; /**< Carrier frequency of the signal. */\n    float duty_cycle; /**< Duty cycle of the signal. */\n} InfraredRawSignal;\n\n/**\n * @brief Create a new InfraredSignal instance.\n *\n * @returns pointer to the instance created.\n */\nInfraredSignal* infrared_signal_alloc(void);\n\n/**\n * @brief Delete an InfraredSignal instance.\n *\n * @param[in,out] signal pointer to the instance to be deleted.\n */\nvoid infrared_signal_free(InfraredSignal* signal);\n\n/**\n * @brief Test whether an InfraredSignal instance holds a raw signal.\n *\n * @param[in] signal pointer to the instance to be tested.\n * @returns true if the instance holds a raw signal, false otherwise.\n */\nbool infrared_signal_is_raw(const InfraredSignal* signal);\n\n/**\n * @brief Test whether an InfraredSignal instance holds any signal.\n *\n * @param[in] signal pointer to the instance to be tested.\n * @returns true if the instance holds raw signal, false otherwise.\n */\nbool infrared_signal_is_valid(const InfraredSignal* signal);\n\n/**\n * @brief Set an InfraredInstance to hold the signal from another one.\n *\n * Any instance's previous contents will be automatically deleted before\n * copying the source instance's contents.\n *\n * @param[in,out] signal pointer to the destination instance.\n * @param[in] other pointer to the source instance.\n */\nvoid infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other);\n\n/**\n * @brief Set an InfraredInstance to hold a raw signal.\n *\n * Any instance's previous contents will be automatically deleted before\n * copying the raw signal.\n *\n * After this call, infrared_signal_is_raw() will return true.\n *\n * @param[in,out] signal pointer to the destination instance.\n * @param[in] timings pointer to an array containing the raw signal timings.\n * @param[in] timings_size number of elements in the timings array.\n * @param[in] frequency signal carrier frequency, in Hertz.\n * @param[in] duty_cycle signal duty cycle, fraction between 0 and 1.\n */\nvoid infrared_signal_set_raw_signal(\n    InfraredSignal* signal,\n    const uint32_t* timings,\n    size_t timings_size,\n    uint32_t frequency,\n    float duty_cycle);\n\n/**\n * @brief Get the raw signal held by an InfraredSignal instance.\n *\n * @warning the instance MUST hold a *raw* signal, otherwise undefined behaviour will occur.\n *\n * @param[in] signal pointer to the instance to be queried.\n * @returns pointer to the raw signal structure held by the instance.\n */\nconst InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal);\n\n/**\n * @brief Set an InfraredInstance to hold a parsed signal.\n *\n * Any instance's previous contents will be automatically deleted before\n * copying the raw signal.\n *\n * After this call, infrared_signal_is_raw() will return false.\n *\n * @param[in,out] signal pointer to the destination instance.\n * @param[in] message pointer to the message containing the parsed signal.\n */\nvoid infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message);\n\n/**\n * @brief Get the parsed signal held by an InfraredSignal instance.\n *\n * @warning the instance MUST hold a *parsed* signal, otherwise undefined behaviour will occur.\n *\n * @param[in] signal pointer to the instance to be queried.\n * @returns pointer to the parsed signal structure held by the instance.\n */\nconst InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal);\n\n/**\n * @brief Read a signal and its name from a FlipperFormat file into an InfraredSignal instance.\n *\n * The file must be allocated and open prior to this call. The seek position determines\n * which signal will be read (if there is more than one in the file). Calling this function\n * repeatedly will result in all signals in the file to be read until no more are left.\n *\n * @param[in,out] signal pointer to the instance to be read into.\n * @param[in,out] ff pointer to the FlipperFormat file instance to read from.\n * @param[out] name pointer to the string to hold the signal name. Must be properly allocated.\n * @returns InfraredErrorCodeNone if a signal was successfully read, otherwise error code\n */\nInfraredErrorCode\n    infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name);\n\n/**\n * @brief Read a signal name from a FlipperFormat file.\n *\n * Same behaviour as infrared_signal_read(), but only the name is read.\n *\n * @param[in,out] ff pointer to the FlipperFormat file instance to read from.\n * @param[out] name pointer to the string to hold the signal name. Must be properly allocated.\n * @returns InfraredErrorCodeNone if a signal name was successfully read, otherwise error code\n */\nInfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name);\n\n/**\n * @brief Read a signal from a FlipperFormat file.\n *\n * Same behaviour as infrared_signal_read(), but only the body is read.\n *\n * @param[in,out] ff pointer to the FlipperFormat file instance to read from.\n * @param[out] signal pointer to the InfraredSignal instance to hold the signal body. Must be properly allocated.\n * @returns InfraredErrorCodeNone if a signal body was successfully read, otherwise error code.\n */\nInfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff);\n\n/**\n * @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance.\n *\n * This function will look for a signal with the given name and if found, attempt to read it.\n * Same considerations apply as to infrared_signal_read().\n *\n * @param[in,out] signal pointer to the instance to be read into.\n * @param[in,out] ff pointer to the FlipperFormat file instance to read from.\n * @param[in] name pointer to a zero-terminated string containing the requested signal name.\n * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code.\n */\nInfraredErrorCode infrared_signal_search_by_name_and_read(\n    InfraredSignal* signal,\n    FlipperFormat* ff,\n    const char* name);\n\n/**\n * @brief Read a signal with a particular index from a FlipperFormat file into an InfraredSignal instance.\n *\n * This function will look for a signal with the given index and if found, attempt to read it.\n * Same considerations apply as to infrared_signal_read().\n *\n * @param[in,out] signal pointer to the instance to be read into.\n * @param[in,out] ff pointer to the FlipperFormat file instance to read from.\n * @param[in] index the requested signal index.\n * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code.\n */\nInfraredErrorCode infrared_signal_search_by_index_and_read(\n    InfraredSignal* signal,\n    FlipperFormat* ff,\n    size_t index);\n\n/**\n * @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file.\n *\n * The file must be allocated and open prior to this call. Additionally, an appropriate header\n * must be already written into the file.\n *\n * @param[in] signal pointer to the instance holding the signal to be saved.\n * @param[in,out] ff pointer to the FlipperFormat file instance to write to.\n * @param[in] name pointer to a zero-terminated string contating the name of the signal.\n * @returns InfraredErrorCodeNone if a signal was successfully saved, otherwise error code\n */\nInfraredErrorCode\n    infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name);\n\n/**\n * @brief Transmit a signal contained in an InfraredSignal instance.\n *\n * The transmission happens once per call using the built-in hardware (via HAL calls).\n *\n * @param[in] signal pointer to the instance holding the signal to be transmitted.\n */\nvoid infrared_signal_transmit(const InfraredSignal* signal);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/worker/infrared_transmit.c",
    "content": "#include \"infrared.h\"\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <furi.h>\n#include <furi_hal_infrared.h>\n\nstatic uint32_t infrared_tx_number_of_transmissions = 0;\nstatic uint32_t infrared_tx_raw_timings_index = 0;\nstatic uint32_t infrared_tx_raw_timings_number = 0;\nstatic uint32_t infrared_tx_raw_start_from_mark = 0;\nstatic bool infrared_tx_raw_add_silence = false;\n\nFuriHalInfraredTxGetDataState\n    infrared_get_raw_data_callback(void* context, uint32_t* duration, bool* level) {\n    furi_assert(duration);\n    furi_assert(level);\n    furi_assert(context);\n\n    FuriHalInfraredTxGetDataState state = FuriHalInfraredTxGetDataStateOk;\n    const uint32_t* timings = context;\n\n    if(infrared_tx_raw_add_silence && (infrared_tx_raw_timings_index == 0)) {\n        infrared_tx_raw_add_silence = false;\n        *level = false;\n        *duration = INFRARED_RAW_TX_TIMING_DELAY_US;\n    } else {\n        *level = infrared_tx_raw_start_from_mark ^ (infrared_tx_raw_timings_index % 2);\n        *duration = timings[infrared_tx_raw_timings_index++];\n    }\n\n    if(infrared_tx_raw_timings_number == infrared_tx_raw_timings_index) {\n        state = FuriHalInfraredTxGetDataStateLastDone;\n    }\n\n    return state;\n}\n\nvoid infrared_send_raw_ext(\n    const uint32_t timings[],\n    uint32_t timings_cnt,\n    bool start_from_mark,\n    uint32_t frequency,\n    float duty_cycle) {\n    furi_check(timings);\n\n    infrared_tx_raw_start_from_mark = start_from_mark;\n    infrared_tx_raw_timings_index = 0;\n    infrared_tx_raw_timings_number = timings_cnt;\n    infrared_tx_raw_add_silence = start_from_mark;\n    furi_hal_infrared_async_tx_set_data_isr_callback(\n        infrared_get_raw_data_callback, (void*)timings);\n    furi_hal_infrared_async_tx_start(frequency, duty_cycle);\n    furi_hal_infrared_async_tx_wait_termination();\n\n    furi_check(!furi_hal_infrared_is_busy());\n}\n\nvoid infrared_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) {\n    infrared_send_raw_ext(\n        timings,\n        timings_cnt,\n        start_from_mark,\n        INFRARED_COMMON_CARRIER_FREQUENCY,\n        INFRARED_COMMON_DUTY_CYCLE);\n}\n\nFuriHalInfraredTxGetDataState\n    infrared_get_data_callback(void* context, uint32_t* duration, bool* level) {\n    FuriHalInfraredTxGetDataState state;\n    InfraredEncoderHandler* handler = context;\n    InfraredStatus status = InfraredStatusError;\n\n    if(infrared_tx_number_of_transmissions > 0) {\n        status = infrared_encode(handler, duration, level);\n    }\n\n    if(status == InfraredStatusError) {\n        state = FuriHalInfraredTxGetDataStateLastDone;\n        *duration = 0;\n        *level = 0;\n    } else if(status == InfraredStatusOk) {\n        state = FuriHalInfraredTxGetDataStateOk;\n    } else if(status == InfraredStatusDone) {\n        if(--infrared_tx_number_of_transmissions == 0) {\n            state = FuriHalInfraredTxGetDataStateLastDone;\n        } else {\n            state = FuriHalInfraredTxGetDataStateDone;\n        }\n    } else {\n        furi_crash();\n    }\n\n    return state;\n}\n\nvoid infrared_send(const InfraredMessage* message, int times) {\n    furi_check(message);\n    furi_check(times);\n    furi_check(infrared_is_protocol_valid(message->protocol));\n\n    InfraredEncoderHandler* handler = infrared_alloc_encoder();\n    infrared_reset_encoder(handler, message);\n    infrared_tx_number_of_transmissions =\n        MAX((int)infrared_get_protocol_min_repeat_count(message->protocol), times);\n\n    uint32_t frequency = infrared_get_protocol_frequency(message->protocol);\n    float duty_cycle = infrared_get_protocol_duty_cycle(message->protocol);\n\n    furi_hal_infrared_async_tx_set_data_isr_callback(infrared_get_data_callback, handler);\n    furi_hal_infrared_async_tx_start(frequency, duty_cycle);\n    furi_hal_infrared_async_tx_wait_termination();\n\n    infrared_free_encoder(handler);\n\n    furi_check(!furi_hal_infrared_is_busy());\n}\n"
  },
  {
    "path": "lib/infrared/worker/infrared_transmit.h",
    "content": "#pragma once\n\n#include <furi_hal_infrared.h>\n#include <infrared.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Send message over INFRARED.\n *\n * \\param[in]   message     - message to send.\n * \\param[in]   times       - number of times message should be sent.\n */\nvoid infrared_send(const InfraredMessage* message, int times);\n\n/**\n * Send raw data through infrared port.\n *\n * \\param[in]   timings - array of timings to send.\n * \\param[in]   timings_cnt - timings array size.\n * \\param[in]   start_from_mark - true if timings starts from mark,\n *              otherwise from space\n */\nvoid infrared_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark);\n\n/**\n * Send raw data through infrared port, with additional settings.\n *\n * \\param[in]   timings - array of timings to send.\n * \\param[in]   timings_cnt - timings array size.\n * \\param[in]   start_from_mark - true if timings starts from mark,\n *              otherwise from space\n * \\param[in]   duty_cycle - duty cycle to generate on PWM\n * \\param[in]   frequency - frequency to generate on PWM\n */\nvoid infrared_send_raw_ext(\n    const uint32_t timings[],\n    uint32_t timings_cnt,\n    bool start_from_mark,\n    uint32_t frequency,\n    float duty_cycle);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/infrared/worker/infrared_worker.c",
    "content": "#include \"infrared_worker.h\"\n\n#include <furi_hal_infrared.h>\n#include <float_tools.h>\n\n#include <core/check.h>\n#include <core/common_defines.h>\n\n#include <notification/notification_messages.h>\n\n#define INFRARED_WORKER_RX_TIMEOUT INFRARED_RAW_RX_TIMING_DELAY_US\n\n#define INFRARED_WORKER_RX_RECEIVED         0x01\n#define INFRARED_WORKER_RX_TIMEOUT_RECEIVED 0x02\n#define INFRARED_WORKER_OVERRUN             0x04\n#define INFRARED_WORKER_EXIT                0x08\n#define INFRARED_WORKER_TX_FILL_BUFFER      0x10\n#define INFRARED_WORKER_TX_MESSAGE_SENT     0x20\n\n#define INFRARED_WORKER_ALL_RX_EVENTS                                    \\\n    (INFRARED_WORKER_RX_RECEIVED | INFRARED_WORKER_RX_TIMEOUT_RECEIVED | \\\n     INFRARED_WORKER_OVERRUN | INFRARED_WORKER_EXIT)\n\n#define INFRARED_WORKER_ALL_TX_EVENTS \\\n    (INFRARED_WORKER_TX_FILL_BUFFER | INFRARED_WORKER_TX_MESSAGE_SENT | INFRARED_WORKER_EXIT)\n\n#define INFRARED_WORKER_ALL_EVENTS (INFRARED_WORKER_ALL_RX_EVENTS | INFRARED_WORKER_ALL_TX_EVENTS)\n\ntypedef enum {\n    InfraredWorkerStateIdle,\n    InfraredWorkerStateRunRx,\n    InfraredWorkerStateRunTx,\n    InfraredWorkerStateWaitTxEnd,\n    InfraredWorkerStateStopTx,\n    InfraredWorkerStateStartTx,\n} InfraredWorkerState;\n\nstruct InfraredWorkerSignal {\n    bool decoded;\n    size_t timings_cnt;\n    union {\n        InfraredMessage message;\n        struct {\n            /* +1 is for pause we add at the beginning */\n            uint32_t timings[MAX_TIMINGS_AMOUNT + 1];\n            uint32_t frequency;\n            float duty_cycle;\n        } raw;\n    };\n};\n\nstruct InfraredWorker {\n    FuriThread* thread;\n    FuriStreamBuffer* stream;\n\n    InfraredWorkerSignal signal;\n    InfraredWorkerState state;\n    InfraredEncoderHandler* infrared_encoder;\n    InfraredDecoderHandler* infrared_decoder;\n    NotificationApp* notification;\n    bool blink_enable;\n    bool decode_enable;\n\n    union {\n        struct {\n            InfraredWorkerGetSignalCallback get_signal_callback;\n            InfraredWorkerMessageSentCallback message_sent_callback;\n            void* get_signal_context;\n            void* message_sent_context;\n            uint32_t frequency;\n            float duty_cycle;\n            uint32_t tx_raw_cnt;\n            bool need_reinitialization;\n            bool steady_signal_sent;\n        } tx;\n        struct {\n            InfraredWorkerReceivedSignalCallback received_signal_callback;\n            void* received_signal_context;\n            bool overrun;\n        } rx;\n    };\n};\n\ntypedef struct {\n    uint32_t duration;\n    bool level;\n    FuriHalInfraredTxGetDataState state;\n} InfraredWorkerTiming;\n\nstatic int32_t infrared_worker_tx_thread(void* context);\nstatic FuriHalInfraredTxGetDataState\n    infrared_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level);\nstatic void infrared_worker_furi_hal_message_sent_isr_callback(void* context);\n\nstatic void infrared_worker_rx_timeout_callback(void* context) {\n    InfraredWorker* instance = context;\n    uint32_t flags_set = furi_thread_flags_set(\n        furi_thread_get_id(instance->thread), INFRARED_WORKER_RX_TIMEOUT_RECEIVED);\n    furi_check(flags_set & INFRARED_WORKER_RX_TIMEOUT_RECEIVED);\n}\n\nstatic void infrared_worker_rx_callback(void* context, bool level, uint32_t duration) {\n    InfraredWorker* instance = context;\n\n    furi_assert(duration != 0);\n    LevelDuration level_duration = level_duration_make(level, duration);\n\n    size_t ret =\n        furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);\n    uint32_t events = (ret == sizeof(LevelDuration)) ? INFRARED_WORKER_RX_RECEIVED :\n                                                       INFRARED_WORKER_OVERRUN;\n\n    uint32_t flags_set = furi_thread_flags_set(furi_thread_get_id(instance->thread), events);\n    furi_check(flags_set & events);\n}\n\nstatic void infrared_worker_process_timeout(InfraredWorker* instance) {\n    if(instance->signal.timings_cnt < 2) return;\n\n    const InfraredMessage* message_decoded =\n        infrared_check_decoder_ready(instance->infrared_decoder);\n    if(message_decoded) {\n        instance->signal.message = *message_decoded;\n        instance->signal.timings_cnt = 0;\n        instance->signal.decoded = true;\n    } else {\n        instance->signal.decoded = false;\n    }\n    if(instance->rx.received_signal_callback)\n        instance->rx.received_signal_callback(\n            instance->rx.received_signal_context, &instance->signal);\n}\n\nstatic void\n    infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) {\n    const InfraredMessage* message_decoded =\n        instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) :\n                                  NULL;\n    if(message_decoded) {\n        instance->signal.message = *message_decoded;\n        instance->signal.timings_cnt = 0;\n        instance->signal.decoded = true;\n        if(instance->rx.received_signal_callback)\n            instance->rx.received_signal_callback(\n                instance->rx.received_signal_context, &instance->signal);\n    } else {\n        /* Skip first timing if it starts from Space */\n        if((instance->signal.timings_cnt == 0) && !level) {\n            return;\n        }\n\n        if(instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) {\n            instance->signal.raw.timings[instance->signal.timings_cnt] = duration;\n            ++instance->signal.timings_cnt;\n        } else {\n            uint32_t flags_set = furi_thread_flags_set(\n                furi_thread_get_id(instance->thread), INFRARED_WORKER_OVERRUN);\n            furi_check(flags_set & INFRARED_WORKER_OVERRUN);\n            instance->rx.overrun = true;\n        }\n    }\n}\n\nstatic int32_t infrared_worker_rx_thread(void* thread_context) {\n    InfraredWorker* instance = thread_context;\n    uint32_t events = 0;\n    LevelDuration level_duration;\n    uint32_t last_blink_time = 0;\n\n    while(1) {\n        events = furi_thread_flags_wait(INFRARED_WORKER_ALL_RX_EVENTS, 0, FuriWaitForever);\n        furi_check(events & INFRARED_WORKER_ALL_RX_EVENTS); /* at least one caught */\n\n        if(events & INFRARED_WORKER_RX_RECEIVED) {\n            if(!instance->rx.overrun && instance->blink_enable &&\n               ((furi_get_tick() - last_blink_time) > 80)) {\n                last_blink_time = furi_get_tick();\n                notification_message(instance->notification, &sequence_blink_blue_10);\n            }\n            if(instance->signal.timings_cnt == 0)\n                notification_message(instance->notification, &sequence_display_backlight_on);\n            while(sizeof(LevelDuration) ==\n                  furi_stream_buffer_receive(\n                      instance->stream, &level_duration, sizeof(LevelDuration), 0)) {\n                if(!instance->rx.overrun) {\n                    bool level = level_duration_get_level(level_duration);\n                    uint32_t duration = level_duration_get_duration(level_duration);\n                    infrared_worker_process_timings(instance, duration, level);\n                }\n            }\n        }\n        if(events & INFRARED_WORKER_OVERRUN) {\n            printf(\"#\");\n            infrared_reset_decoder(instance->infrared_decoder);\n            instance->signal.timings_cnt = 0;\n            if(instance->blink_enable)\n                notification_message(instance->notification, &sequence_set_red_255);\n        }\n        if(events & INFRARED_WORKER_RX_TIMEOUT_RECEIVED) {\n            if(instance->rx.overrun) {\n                printf(\"\\nOVERRUN, max samples: %d\\n\", MAX_TIMINGS_AMOUNT);\n                instance->rx.overrun = false;\n                if(instance->blink_enable)\n                    notification_message(instance->notification, &sequence_reset_red);\n            } else {\n                infrared_worker_process_timeout(instance);\n            }\n            instance->signal.timings_cnt = 0;\n        }\n        if(events & INFRARED_WORKER_EXIT) break;\n    }\n\n    return 0;\n}\n\nvoid infrared_worker_rx_set_received_signal_callback(\n    InfraredWorker* instance,\n    InfraredWorkerReceivedSignalCallback callback,\n    void* context) {\n    furi_check(instance);\n\n    instance->rx.received_signal_callback = callback;\n    instance->rx.received_signal_context = context;\n}\n\nInfraredWorker* infrared_worker_alloc(void) {\n    InfraredWorker* instance = malloc(sizeof(InfraredWorker));\n\n    instance->thread = furi_thread_alloc_ex(\"InfraredWorker\", 2048, NULL, instance);\n\n    size_t buffer_size =\n        MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1),\n            sizeof(LevelDuration) * MAX_TIMINGS_AMOUNT);\n    instance->stream = furi_stream_buffer_alloc(buffer_size, sizeof(InfraredWorkerTiming));\n    instance->infrared_decoder = infrared_alloc_decoder();\n    instance->infrared_encoder = infrared_alloc_encoder();\n    instance->blink_enable = false;\n    instance->decode_enable = true;\n    instance->notification = furi_record_open(RECORD_NOTIFICATION);\n    instance->state = InfraredWorkerStateIdle;\n\n    return instance;\n}\n\nvoid infrared_worker_free(InfraredWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->state == InfraredWorkerStateIdle);\n\n    furi_record_close(RECORD_NOTIFICATION);\n    infrared_free_decoder(instance->infrared_decoder);\n    infrared_free_encoder(instance->infrared_encoder);\n    furi_stream_buffer_free(instance->stream);\n    furi_thread_free(instance->thread);\n\n    free(instance);\n}\n\nvoid infrared_worker_rx_start(InfraredWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->state == InfraredWorkerStateIdle);\n\n    furi_stream_set_trigger_level(instance->stream, sizeof(LevelDuration));\n\n    furi_thread_set_callback(instance->thread, infrared_worker_rx_thread);\n    furi_thread_start(instance->thread);\n\n    furi_hal_infrared_async_rx_set_capture_isr_callback(infrared_worker_rx_callback, instance);\n    furi_hal_infrared_async_rx_set_timeout_isr_callback(\n        infrared_worker_rx_timeout_callback, instance);\n    furi_hal_infrared_async_rx_start();\n    furi_hal_infrared_async_rx_set_timeout(INFRARED_WORKER_RX_TIMEOUT);\n\n    instance->rx.overrun = false;\n    instance->state = InfraredWorkerStateRunRx;\n}\n\nvoid infrared_worker_rx_stop(InfraredWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->state == InfraredWorkerStateRunRx);\n\n    furi_hal_infrared_async_rx_set_timeout_isr_callback(NULL, NULL);\n    furi_hal_infrared_async_rx_set_capture_isr_callback(NULL, NULL);\n    furi_hal_infrared_async_rx_stop();\n\n    furi_thread_flags_set(furi_thread_get_id(instance->thread), INFRARED_WORKER_EXIT);\n    furi_thread_join(instance->thread);\n\n    furi_check(furi_stream_buffer_reset(instance->stream) == FuriStatusOk);\n\n    instance->state = InfraredWorkerStateIdle;\n}\n\nbool infrared_worker_signal_is_decoded(const InfraredWorkerSignal* signal) {\n    furi_check(signal);\n\n    return signal->decoded;\n}\n\nvoid infrared_worker_get_raw_signal(\n    const InfraredWorkerSignal* signal,\n    const uint32_t** timings,\n    size_t* timings_cnt) {\n    furi_check(signal);\n    furi_check(timings);\n    furi_check(timings_cnt);\n\n    *timings = signal->raw.timings;\n    *timings_cnt = signal->timings_cnt;\n}\n\nconst InfraredMessage* infrared_worker_get_decoded_signal(const InfraredWorkerSignal* signal) {\n    furi_check(signal);\n\n    return &signal->message;\n}\n\nvoid infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable) {\n    furi_check(instance);\n\n    instance->blink_enable = enable;\n}\n\nvoid infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) {\n    furi_check(instance);\n\n    instance->decode_enable = enable;\n}\n\nvoid infrared_worker_tx_start(InfraredWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->state == InfraredWorkerStateIdle);\n    furi_check(instance->tx.get_signal_callback);\n\n    // size have to be greater than api hal infrared async tx buffer size\n    furi_stream_set_trigger_level(instance->stream, sizeof(InfraredWorkerTiming));\n\n    furi_thread_set_callback(instance->thread, infrared_worker_tx_thread);\n\n    instance->tx.steady_signal_sent = false;\n    instance->tx.need_reinitialization = false;\n    furi_hal_infrared_async_tx_set_data_isr_callback(\n        infrared_worker_furi_hal_data_isr_callback, instance);\n    furi_hal_infrared_async_tx_set_signal_sent_isr_callback(\n        infrared_worker_furi_hal_message_sent_isr_callback, instance);\n\n    instance->state = InfraredWorkerStateStartTx;\n    furi_thread_start(instance->thread);\n}\n\nstatic void infrared_worker_furi_hal_message_sent_isr_callback(void* context) {\n    InfraredWorker* instance = context;\n    uint32_t flags_set = furi_thread_flags_set(\n        furi_thread_get_id(instance->thread), INFRARED_WORKER_TX_MESSAGE_SENT);\n    furi_check(flags_set & INFRARED_WORKER_TX_MESSAGE_SENT);\n}\n\nstatic FuriHalInfraredTxGetDataState\n    infrared_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level) {\n    furi_assert(context);\n    furi_assert(duration);\n    furi_assert(level);\n\n    InfraredWorker* instance = context;\n    InfraredWorkerTiming timing;\n    FuriHalInfraredTxGetDataState state;\n\n    if(sizeof(InfraredWorkerTiming) ==\n       furi_stream_buffer_receive(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0)) {\n        *level = timing.level;\n        *duration = timing.duration;\n        state = timing.state;\n    } else {\n        // Why bother if we crash anyway?..\n        *level = 0;\n        *duration = 100;\n        state = FuriHalInfraredTxGetDataStateDone;\n        furi_crash();\n    }\n\n    uint32_t flags_set = furi_thread_flags_set(\n        furi_thread_get_id(instance->thread), INFRARED_WORKER_TX_FILL_BUFFER);\n    furi_check(flags_set & INFRARED_WORKER_TX_FILL_BUFFER);\n\n    return state;\n}\n\nstatic bool infrared_get_new_signal(InfraredWorker* instance) {\n    bool new_signal_obtained = false;\n\n    InfraredWorkerGetSignalResponse response =\n        instance->tx.get_signal_callback(instance->tx.get_signal_context, instance);\n    if(response == InfraredWorkerGetSignalResponseNew) {\n        uint32_t new_tx_frequency = 0;\n        float new_tx_duty_cycle = 0;\n        if(instance->signal.decoded) {\n            new_tx_frequency = infrared_get_protocol_frequency(instance->signal.message.protocol);\n            new_tx_duty_cycle =\n                infrared_get_protocol_duty_cycle(instance->signal.message.protocol);\n        } else {\n            furi_assert(instance->signal.timings_cnt > 1);\n            new_tx_frequency = instance->signal.raw.frequency;\n            new_tx_duty_cycle = instance->signal.raw.duty_cycle;\n        }\n\n        instance->tx.tx_raw_cnt = 0;\n        instance->tx.need_reinitialization =\n            (new_tx_frequency != instance->tx.frequency) ||\n            !float_is_equal(new_tx_duty_cycle, instance->tx.duty_cycle);\n        instance->tx.frequency = new_tx_frequency;\n        instance->tx.duty_cycle = new_tx_duty_cycle;\n        if(instance->signal.decoded) {\n            infrared_reset_encoder(instance->infrared_encoder, &instance->signal.message);\n        }\n        new_signal_obtained = true;\n    } else if(response == InfraredWorkerGetSignalResponseSame) {\n        new_signal_obtained = true;\n        /* no need to reinit */\n    } else if(response == InfraredWorkerGetSignalResponseStop) {\n        new_signal_obtained = false;\n    } else {\n        furi_crash();\n    }\n\n    return new_signal_obtained;\n}\n\nstatic bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) {\n    bool new_data_available = true;\n    InfraredWorkerTiming timing;\n    InfraredStatus status = InfraredStatusError;\n\n    while(!furi_stream_buffer_is_full(instance->stream) && !instance->tx.need_reinitialization &&\n          new_data_available) {\n        if(instance->signal.decoded) {\n            status = infrared_encode(instance->infrared_encoder, &timing.duration, &timing.level);\n        } else {\n            timing.duration = instance->signal.raw.timings[instance->tx.tx_raw_cnt];\n            /* raw always starts from Mark, but we fill it with space delay at start */\n            timing.level = (instance->tx.tx_raw_cnt % 2);\n            ++instance->tx.tx_raw_cnt;\n            if(instance->tx.tx_raw_cnt >= instance->signal.timings_cnt) {\n                instance->tx.tx_raw_cnt = 0;\n                status = InfraredStatusDone;\n            } else {\n                status = InfraredStatusOk;\n            }\n        }\n\n        if(status == InfraredStatusError) {\n            new_data_available = false;\n            furi_crash();\n        } else if(status == InfraredStatusOk) {\n            timing.state = FuriHalInfraredTxGetDataStateOk;\n        } else if(status == InfraredStatusDone) {\n            timing.state = FuriHalInfraredTxGetDataStateDone;\n\n            new_data_available = infrared_get_new_signal(instance);\n            if(instance->tx.need_reinitialization || !new_data_available) {\n                timing.state = FuriHalInfraredTxGetDataStateLastDone;\n            }\n        } else {\n            furi_crash();\n        }\n        uint32_t written_size =\n            furi_stream_buffer_send(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0);\n        furi_assert(sizeof(InfraredWorkerTiming) == written_size);\n        (void)written_size;\n    }\n\n    return new_data_available;\n}\n\nstatic int32_t infrared_worker_tx_thread(void* thread_context) {\n    InfraredWorker* instance = thread_context;\n    furi_assert(instance->state == InfraredWorkerStateStartTx);\n    furi_assert(thread_context);\n\n    size_t repeats_left =\n        instance->signal.decoded ?\n            infrared_get_protocol_min_repeat_count(instance->signal.message.protocol) :\n            1;\n    uint32_t events = 0;\n\n    bool exit_pending = false;\n\n    bool running = infrared_get_new_signal(instance);\n    furi_assert(running);\n\n    while(running) {\n        switch(instance->state) {\n        case InfraredWorkerStateStartTx:\n            --repeats_left; /* The first message does not result in TX_MESSAGE_SENT event for some reason */\n            instance->tx.need_reinitialization = false;\n            const bool new_data_available = infrared_worker_tx_fill_buffer(instance);\n            furi_hal_infrared_async_tx_start(instance->tx.frequency, instance->tx.duty_cycle);\n\n            if(!new_data_available) {\n                instance->state = InfraredWorkerStateStopTx;\n            } else if(instance->tx.need_reinitialization) {\n                instance->state = InfraredWorkerStateWaitTxEnd;\n            } else {\n                instance->state = InfraredWorkerStateRunTx;\n            }\n\n            break;\n        case InfraredWorkerStateStopTx:\n            furi_hal_infrared_async_tx_stop();\n            running = false;\n            break;\n        case InfraredWorkerStateWaitTxEnd:\n            furi_hal_infrared_async_tx_wait_termination();\n            instance->state = InfraredWorkerStateStartTx;\n\n            events = furi_thread_flags_get();\n            if(events & INFRARED_WORKER_EXIT) {\n                running = false;\n                break;\n            }\n\n            break;\n        case InfraredWorkerStateRunTx:\n            events = furi_thread_flags_wait(\n                INFRARED_WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);\n            furi_check(events & INFRARED_WORKER_ALL_TX_EVENTS); /* at least one caught */\n\n            if(events & INFRARED_WORKER_EXIT) {\n                exit_pending = true;\n            }\n\n            if(events & INFRARED_WORKER_TX_FILL_BUFFER) {\n                infrared_worker_tx_fill_buffer(instance);\n\n                if(instance->tx.need_reinitialization) {\n                    instance->state = InfraredWorkerStateWaitTxEnd;\n                }\n            }\n\n            if(events & INFRARED_WORKER_TX_MESSAGE_SENT) {\n                if(repeats_left > 0) {\n                    --repeats_left;\n                }\n\n                if(instance->tx.message_sent_callback) {\n                    instance->tx.message_sent_callback(instance->tx.message_sent_context);\n                }\n            }\n\n            if(exit_pending && repeats_left == 0) {\n                instance->state = InfraredWorkerStateStopTx;\n            }\n\n            break;\n        default:\n            furi_crash();\n            break;\n        }\n    }\n\n    return 0;\n}\n\nvoid infrared_worker_tx_set_get_signal_callback(\n    InfraredWorker* instance,\n    InfraredWorkerGetSignalCallback callback,\n    void* context) {\n    furi_check(instance);\n\n    instance->tx.get_signal_callback = callback;\n    instance->tx.get_signal_context = context;\n}\n\nvoid infrared_worker_tx_set_signal_sent_callback(\n    InfraredWorker* instance,\n    InfraredWorkerMessageSentCallback callback,\n    void* context) {\n    furi_check(instance);\n\n    instance->tx.message_sent_callback = callback;\n    instance->tx.message_sent_context = context;\n}\n\nvoid infrared_worker_tx_stop(InfraredWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->state != InfraredWorkerStateRunRx);\n\n    furi_thread_flags_set(furi_thread_get_id(instance->thread), INFRARED_WORKER_EXIT);\n    furi_thread_join(instance->thread);\n    furi_hal_infrared_async_tx_set_data_isr_callback(NULL, NULL);\n    furi_hal_infrared_async_tx_set_signal_sent_isr_callback(NULL, NULL);\n\n    instance->signal.timings_cnt = 0;\n    furi_check(furi_stream_buffer_reset(instance->stream) == FuriStatusOk);\n\n    instance->state = InfraredWorkerStateIdle;\n}\n\nvoid infrared_worker_set_decoded_signal(InfraredWorker* instance, const InfraredMessage* message) {\n    furi_check(instance);\n    furi_check(message);\n\n    instance->signal.decoded = true;\n    instance->signal.message = *message;\n}\n\nvoid infrared_worker_set_raw_signal(\n    InfraredWorker* instance,\n    const uint32_t* timings,\n    size_t timings_cnt,\n    uint32_t frequency,\n    float duty_cycle) {\n    furi_check(instance);\n    furi_check(timings);\n    furi_check(timings_cnt > 0);\n    furi_check((frequency <= INFRARED_MAX_FREQUENCY) && (frequency >= INFRARED_MIN_FREQUENCY));\n    furi_check((duty_cycle <= 1.0f) && (duty_cycle > 0.0f));\n\n    size_t max_copy_num = COUNT_OF(instance->signal.raw.timings) - 1;\n    furi_check(timings_cnt <= max_copy_num);\n\n    instance->signal.raw.frequency = frequency;\n    instance->signal.raw.duty_cycle = duty_cycle;\n    instance->signal.raw.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US;\n    memcpy(&instance->signal.raw.timings[1], timings, timings_cnt * sizeof(uint32_t));\n    instance->signal.decoded = false;\n    instance->signal.timings_cnt = timings_cnt + 1;\n}\n\nInfraredWorkerGetSignalResponse\n    infrared_worker_tx_get_signal_steady_callback(void* context, InfraredWorker* instance) {\n    UNUSED(context);\n    furi_check(instance);\n\n    InfraredWorkerGetSignalResponse response = instance->tx.steady_signal_sent ?\n                                                   InfraredWorkerGetSignalResponseSame :\n                                                   InfraredWorkerGetSignalResponseNew;\n    instance->tx.steady_signal_sent = true;\n    return response;\n}\n"
  },
  {
    "path": "lib/infrared/worker/infrared_worker.h",
    "content": "#pragma once\n\n#include <infrared.h>\n#include <furi_hal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_TIMINGS_AMOUNT 1024U\n\n/** Interface struct of infrared worker */\ntypedef struct InfraredWorker InfraredWorker;\n/** Interface struct of received signal */\ntypedef struct InfraredWorkerSignal InfraredWorkerSignal;\n\ntypedef enum {\n    InfraredWorkerGetSignalResponseNew, /** Signal, provided by callback is new and encoder should be reseted */\n    InfraredWorkerGetSignalResponseSame, /** Signal, provided by callback is same. No encoder resetting. */\n    InfraredWorkerGetSignalResponseStop, /** No more signals available. */\n} InfraredWorkerGetSignalResponse;\n\n/** Callback type for providing next signal to send. Should be used with\n * infrared_worker_make_decoded_signal() or infrared_worker_make_raw_signal()\n */\ntypedef InfraredWorkerGetSignalResponse (\n    *InfraredWorkerGetSignalCallback)(void* context, InfraredWorker* instance);\n\n/** Callback type for 'message is sent' event */\ntypedef void (*InfraredWorkerMessageSentCallback)(void* context);\n\n/** Callback type to call by InfraredWorker thread when new signal is received */\ntypedef void (\n    *InfraredWorkerReceivedSignalCallback)(void* context, InfraredWorkerSignal* received_signal);\n\n/** Allocate InfraredWorker\n *\n * @return just created instance of InfraredWorker\n */\nInfraredWorker* infrared_worker_alloc(void);\n\n/** Free InfraredWorker\n *\n * @param[in]   instance - InfraredWorker instance\n */\nvoid infrared_worker_free(InfraredWorker* instance);\n\n/** Start InfraredWorker thread, initialise furi_hal, prepare all work.\n *\n * @param[in]   instance - InfraredWorker instance\n */\nvoid infrared_worker_rx_start(InfraredWorker* instance);\n\n/** Stop InfraredWorker thread, deinitialize furi_hal.\n *\n * @param[in]   instance - InfraredWorker instance\n */\nvoid infrared_worker_rx_stop(InfraredWorker* instance);\n\n/** Set received data callback InfraredWorker\n *\n * @param[in]   instance - InfraredWorker instance\n * @param[in]   context - context to pass to callbacks\n * @param[in]   callback - InfraredWorkerReceivedSignalCallback callback\n */\nvoid infrared_worker_rx_set_received_signal_callback(\n    InfraredWorker* instance,\n    InfraredWorkerReceivedSignalCallback callback,\n    void* context);\n\n/** Enable blinking on receiving any signal on IR port.\n *\n * @param[in]   instance - instance of InfraredWorker\n * @param[in]   enable - true if you want to enable blinking\n *                       false otherwise\n */\nvoid infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable);\n\n/** Enable decoding of received infrared signals.\n *\n * @param[in]   instance - instance of InfraredWorker\n * @param[in]   enable - true if you want to enable decoding\n *                       false otherwise\n */\nvoid infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable);\n\n/** Clarify is received signal either decoded or raw\n *\n * @param[in]   signal - received signal\n * @return      true if signal is decoded, false if signal is raw\n */\nbool infrared_worker_signal_is_decoded(const InfraredWorkerSignal* signal);\n\n/** Start transmitting signal. Callback InfraredWorkerGetSignalCallback should be\n * set before this function is called, as it calls for it to fill buffer before\n * starting transmission.\n *\n * @param[in]   instance - InfraredWorker instance\n */\nvoid infrared_worker_tx_start(InfraredWorker* instance);\n\n/** Stop transmitting signal. Waits for end of current signal and stops transmission.\n *\n * @param[in]   instance - InfraredWorker instance\n */\nvoid infrared_worker_tx_stop(InfraredWorker* instance);\n\n/** Set callback for providing next signal to send\n *\n * @param[in]   instance - InfraredWorker instance\n * @param[in]   context - context to pass to callbacks\n * @param[in]   callback - InfraredWorkerGetSignalCallback callback\n */\nvoid infrared_worker_tx_set_get_signal_callback(\n    InfraredWorker* instance,\n    InfraredWorkerGetSignalCallback callback,\n    void* context);\n\n/** Set callback for end of signal transmitting\n *\n * @param[in]   instance - InfraredWorker instance\n * @param[in]   context - context to pass to callbacks\n * @param[in]   callback - InfraredWorkerMessageSentCallback callback\n */\nvoid infrared_worker_tx_set_signal_sent_callback(\n    InfraredWorker* instance,\n    InfraredWorkerMessageSentCallback callback,\n    void* context);\n\n/** Callback to pass to infrared_worker_tx_set_get_signal_callback() if signal\n * is steady and will not be changed between infrared_worker start and stop.\n * Before starting transmission, desired steady signal must be set with\n * infrared_worker_set_decoded_signal() or infrared_worker_set_raw_signal().\n *\n * This function should not be called directly.\n *\n * @param[in]   context - context\n * @param[out]  instance - InfraredWorker instance\n */\nInfraredWorkerGetSignalResponse\n    infrared_worker_tx_get_signal_steady_callback(void* context, InfraredWorker* instance);\n\n/** Acquire raw signal from interface struct 'InfraredWorkerSignal'.\n * First, you have to ensure that signal is raw.\n *\n * @param[in]   signal - received signal\n * @param[out]  timings - pointer to array of timings\n * @param[out]  timings_cnt - pointer to amount of timings\n */\nvoid infrared_worker_get_raw_signal(\n    const InfraredWorkerSignal* signal,\n    const uint32_t** timings,\n    size_t* timings_cnt);\n\n/** Acquire decoded message from interface struct 'InfraredWorkerSignal'.\n * First, you have to ensure that signal is decoded.\n *\n * @param[in]   signal - received signal\n * @return      decoded INFRARED message\n */\nconst InfraredMessage* infrared_worker_get_decoded_signal(const InfraredWorkerSignal* signal);\n\n/** Set current decoded signal for InfraredWorker instance\n *\n * @param[out]  instance - InfraredWorker instance\n * @param[in]   message - decoded signal\n */\nvoid infrared_worker_set_decoded_signal(InfraredWorker* instance, const InfraredMessage* message);\n\n/** Set current raw signal for InfraredWorker instance\n *\n * @param[out]  instance - InfraredWorker instance\n * @param[in]   timings - array of raw timings\n * @param[in]   timings_cnt - size of array of raw timings\n * @param[in]   frequency - carrier frequency in Hertz\n * @param[in]   duty_cycle - carrier duty cycle (0.0 - 1.0)\n */\nvoid infrared_worker_set_raw_signal(\n    InfraredWorker* instance,\n    const uint32_t* timings,\n    size_t timings_cnt,\n    uint32_t frequency,\n    float duty_cycle);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    CPPPATH=[\n        \"#/lib/lfrfid\",\n    ],\n    SDK_HEADERS=[\n        File(\"lfrfid_worker.h\"),\n        File(\"lfrfid_raw_worker.h\"),\n        File(\"lfrfid_raw_file.h\"),\n        File(\"lfrfid_dict_file.h\"),\n        File(\"protocols/lfrfid_protocols.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"lfrfid\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_dict_file.c",
    "content": "#include \"lfrfid_dict_file.h\"\n#include <storage/storage.h>\n#include <flipper_format/flipper_format.h>\n#include <bit_lib/bit_lib.h>\n\n#define LFRFID_DICT_FILETYPE \"Flipper RFID key\"\n\nbool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) {\n    furi_check(dict);\n    furi_check(protocol != PROTOCOL_NO);\n    furi_check(filename);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    size_t data_size = protocol_dict_get_data_size(dict, protocol);\n    uint8_t* data = malloc(data_size);\n    bool result = false;\n\n    do {\n        if(!flipper_format_file_open_always(file, filename)) break;\n        if(!flipper_format_write_header_cstr(file, LFRFID_DICT_FILETYPE, 1)) break;\n\n        // TODO FL-3517: write comment about protocol types into file\n\n        if(!flipper_format_write_string_cstr(\n               file, \"Key type\", protocol_dict_get_name(dict, protocol)))\n            break;\n\n        // TODO FL-3517: write comment about protocol sizes into file\n\n        protocol_dict_get_data(dict, protocol, data, data_size);\n\n        if(!flipper_format_write_hex(file, \"Data\", data, data_size)) break;\n        result = true;\n    } while(false);\n\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n    free(data);\n\n    return result;\n}\n\nstatic void lfrfid_dict_protocol_indala_data(\n    uint8_t* data,\n    size_t data_size,\n    uint8_t* protocol_data,\n    size_t protocol_data_size) {\n    UNUSED(data_size);\n    memset(protocol_data, 0, protocol_data_size);\n\n    // fc\n    bit_lib_set_bit(protocol_data, 24, bit_lib_get_bit(data, 0));\n    bit_lib_set_bit(protocol_data, 16, bit_lib_get_bit(data, 1));\n    bit_lib_set_bit(protocol_data, 11, bit_lib_get_bit(data, 2));\n    bit_lib_set_bit(protocol_data, 14, bit_lib_get_bit(data, 3));\n    bit_lib_set_bit(protocol_data, 15, bit_lib_get_bit(data, 4));\n    bit_lib_set_bit(protocol_data, 20, bit_lib_get_bit(data, 5));\n    bit_lib_set_bit(protocol_data, 6, bit_lib_get_bit(data, 6));\n    bit_lib_set_bit(protocol_data, 25, bit_lib_get_bit(data, 7));\n\n    // cn\n    bit_lib_set_bit(protocol_data, 9, bit_lib_get_bit(data, 8 + 0));\n    bit_lib_set_bit(protocol_data, 12, bit_lib_get_bit(data, 8 + 1));\n    bit_lib_set_bit(protocol_data, 10, bit_lib_get_bit(data, 8 + 2));\n    bit_lib_set_bit(protocol_data, 7, bit_lib_get_bit(data, 8 + 3));\n    bit_lib_set_bit(protocol_data, 19, bit_lib_get_bit(data, 8 + 4));\n    bit_lib_set_bit(protocol_data, 3, bit_lib_get_bit(data, 8 + 5));\n    bit_lib_set_bit(protocol_data, 2, bit_lib_get_bit(data, 8 + 6));\n    bit_lib_set_bit(protocol_data, 18, bit_lib_get_bit(data, 8 + 7));\n    bit_lib_set_bit(protocol_data, 13, bit_lib_get_bit(data, 8 + 8));\n    bit_lib_set_bit(protocol_data, 0, bit_lib_get_bit(data, 8 + 9));\n    bit_lib_set_bit(protocol_data, 4, bit_lib_get_bit(data, 8 + 10));\n    bit_lib_set_bit(protocol_data, 21, bit_lib_get_bit(data, 8 + 11));\n    bit_lib_set_bit(protocol_data, 23, bit_lib_get_bit(data, 8 + 12));\n    bit_lib_set_bit(protocol_data, 26, bit_lib_get_bit(data, 8 + 13));\n    bit_lib_set_bit(protocol_data, 17, bit_lib_get_bit(data, 8 + 14));\n    bit_lib_set_bit(protocol_data, 8, bit_lib_get_bit(data, 8 + 15));\n\n    const uint32_t fc_and_card = data[0] << 16 | data[1] << 8 | data[2];\n\n    // indala checksum\n    uint8_t checksum_sum = 0;\n    checksum_sum += ((fc_and_card >> 14) & 1);\n    checksum_sum += ((fc_and_card >> 12) & 1);\n    checksum_sum += ((fc_and_card >> 9) & 1);\n    checksum_sum += ((fc_and_card >> 8) & 1);\n    checksum_sum += ((fc_and_card >> 6) & 1);\n    checksum_sum += ((fc_and_card >> 5) & 1);\n    checksum_sum += ((fc_and_card >> 2) & 1);\n    checksum_sum += ((fc_and_card >> 0) & 1);\n    checksum_sum = checksum_sum & 0b1;\n\n    if(checksum_sum) {\n        bit_lib_set_bit(protocol_data, 27, 0);\n        bit_lib_set_bit(protocol_data, 28, 1);\n    } else {\n        bit_lib_set_bit(protocol_data, 27, 1);\n        bit_lib_set_bit(protocol_data, 28, 0);\n    }\n\n    // wiegand parity\n    uint8_t even_parity_sum = 0;\n    for(int8_t i = 12; i < 24; i++) {\n        if(((fc_and_card >> i) & 1) == 1) {\n            even_parity_sum++;\n        }\n    }\n    bit_lib_set_bit(protocol_data, 1, even_parity_sum % 2);\n\n    uint8_t odd_parity_sum = 1;\n    for(int8_t i = 0; i < 12; i++) {\n        if(((fc_and_card >> i) & 1) == 1) {\n            odd_parity_sum++;\n        }\n    }\n    bit_lib_set_bit(protocol_data, 5, odd_parity_sum % 2);\n}\n\nstatic ProtocolId lfrfid_dict_protocol_fallback(\n    ProtocolDict* dict,\n    const char* protocol_name,\n    FlipperFormat* file) {\n    ProtocolId result = PROTOCOL_NO;\n    if(strcmp(protocol_name, \"I40134\") == 0) {\n        ProtocolId protocol = LFRFIDProtocolIndala26;\n\n        size_t data_size = 3;\n        size_t protocol_data_size = protocol_dict_get_data_size(dict, protocol);\n        uint8_t* data = malloc(data_size);\n        uint8_t* protocol_data = malloc(protocol_data_size);\n        if(flipper_format_read_hex(file, \"Data\", data, data_size)) {\n            lfrfid_dict_protocol_indala_data(data, data_size, protocol_data, protocol_data_size);\n            protocol_dict_set_data(dict, protocol, protocol_data, protocol_data_size);\n            result = protocol;\n        }\n        free(protocol_data);\n        free(data);\n    }\n\n    return result;\n}\n\nProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) {\n    furi_check(dict);\n    furi_check(filename);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    ProtocolId result = PROTOCOL_NO;\n    uint8_t* data = malloc(protocol_dict_get_max_data_size(dict));\n    FuriString* str_result;\n    str_result = furi_string_alloc();\n\n    do {\n        if(!flipper_format_file_open_existing(file, filename)) break;\n\n        // header\n        uint32_t version;\n        if(!flipper_format_read_header(file, str_result, &version)) break;\n        if(furi_string_cmp_str(str_result, LFRFID_DICT_FILETYPE) != 0) break;\n        if(version != 1) break;\n\n        // type\n        if(!flipper_format_read_string(file, \"Key type\", str_result)) break;\n        ProtocolId protocol;\n        protocol = protocol_dict_get_protocol_by_name(dict, furi_string_get_cstr(str_result));\n\n        if(protocol == PROTOCOL_NO) {\n            protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file);\n            if(protocol == PROTOCOL_NO) break;\n        } else {\n            // data\n            size_t data_size = protocol_dict_get_data_size(dict, protocol);\n            if(!flipper_format_read_hex(file, \"Data\", data, data_size)) break;\n            protocol_dict_set_data(dict, protocol, data, data_size);\n        }\n\n        result = protocol;\n    } while(false);\n\n    free(data);\n    furi_string_free(str_result);\n    flipper_format_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_dict_file.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol_dict.h>\n#include \"protocols/lfrfid_protocols.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Save protocol from dictionary to file\n * \n * @param dict \n * @param protocol \n * @param filename \n * @return true \n * @return false \n */\nbool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename);\n\n/**\n * @brief Load protocol from file to dictionary\n * \n * @param dict \n * @param filename \n * @return ProtocolId \n */\nProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_raw_file.c",
    "content": "#include \"lfrfid_raw_file.h\"\n#include \"tools/varint_pair.h\"\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/varint.h>\n\n#define LFRFID_RAW_FILE_MAGIC   0x4C464952\n#define LFRFID_RAW_FILE_VERSION 1\n\n#define TAG \"LfRfidRawFile\"\n\ntypedef struct {\n    uint32_t magic;\n    uint32_t version;\n    float frequency;\n    float duty_cycle;\n    uint32_t max_buffer_size;\n} LFRFIDRawFileHeader;\n\nstruct LFRFIDRawFile {\n    Stream* stream;\n    uint32_t max_buffer_size;\n\n    uint8_t* buffer;\n    uint32_t buffer_size;\n    size_t buffer_counter;\n};\n\nLFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage) {\n    furi_check(storage);\n\n    LFRFIDRawFile* file = malloc(sizeof(LFRFIDRawFile));\n    file->stream = file_stream_alloc(storage);\n    file->buffer = NULL;\n    return file;\n}\n\nvoid lfrfid_raw_file_free(LFRFIDRawFile* file) {\n    furi_check(file);\n\n    if(file->buffer) free(file->buffer);\n    stream_free(file->stream);\n    free(file);\n}\n\nbool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path) {\n    furi_check(file);\n    furi_check(file_path);\n\n    return file_stream_open(file->stream, file_path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);\n}\n\nbool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path) {\n    furi_check(file);\n    furi_check(file_path);\n\n    return file_stream_open(file->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING);\n}\n\nbool lfrfid_raw_file_write_header(\n    LFRFIDRawFile* file,\n    float frequency,\n    float duty_cycle,\n    uint32_t max_buffer_size) {\n    furi_check(file);\n\n    LFRFIDRawFileHeader header = {\n        .magic = LFRFID_RAW_FILE_MAGIC,\n        .version = LFRFID_RAW_FILE_VERSION,\n        .frequency = frequency,\n        .duty_cycle = duty_cycle,\n        .max_buffer_size = max_buffer_size};\n\n    size_t size = stream_write(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader));\n    return size == sizeof(LFRFIDRawFileHeader);\n}\n\nbool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size) {\n    furi_check(file);\n    furi_check(buffer_data);\n    furi_check(buffer_size);\n\n    size_t size;\n    size = stream_write(file->stream, (uint8_t*)&buffer_size, sizeof(size_t));\n    if(size != sizeof(size_t)) return false;\n\n    size = stream_write(file->stream, buffer_data, buffer_size);\n    if(size != buffer_size) return false;\n\n    return true;\n}\n\nbool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle) {\n    furi_check(file);\n    furi_check(frequency);\n    furi_check(duty_cycle);\n\n    LFRFIDRawFileHeader header;\n    size_t size = stream_read(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader));\n    if(size == sizeof(LFRFIDRawFileHeader)) {\n        if(header.magic == LFRFID_RAW_FILE_MAGIC && header.version == LFRFID_RAW_FILE_VERSION) {\n            *frequency = header.frequency;\n            *duty_cycle = header.duty_cycle;\n            file->max_buffer_size = header.max_buffer_size;\n            file->buffer = malloc(file->max_buffer_size);\n            file->buffer_size = 0;\n            file->buffer_counter = 0;\n            return true;\n        } else {\n            return false;\n        }\n    } else {\n        return false;\n    }\n}\n\nbool lfrfid_raw_file_read_pair(\n    LFRFIDRawFile* file,\n    uint32_t* duration,\n    uint32_t* pulse,\n    bool* pass_end) {\n    furi_check(file);\n    furi_check(duration);\n    furi_check(pulse);\n\n    size_t length = 0;\n    if(file->buffer_counter >= file->buffer_size) {\n        if(stream_eof(file->stream)) {\n            // rewind stream and pass header\n            stream_seek(file->stream, sizeof(LFRFIDRawFileHeader), StreamOffsetFromStart);\n            if(pass_end) *pass_end = true;\n        }\n\n        length = stream_read(file->stream, (uint8_t*)&file->buffer_size, sizeof(size_t));\n        if(length != sizeof(size_t)) {\n            FURI_LOG_E(TAG, \"read pair: failed to read size\");\n            return false;\n        }\n\n        if(file->buffer_size > file->max_buffer_size) {\n            FURI_LOG_E(TAG, \"read pair: buffer size is too big\");\n            return false;\n        }\n\n        length = stream_read(file->stream, file->buffer, file->buffer_size);\n        if(length != file->buffer_size) {\n            FURI_LOG_E(TAG, \"read pair: failed to read data\");\n            return false;\n        }\n\n        file->buffer_counter = 0;\n    }\n\n    size_t size = 0;\n    bool result = varint_pair_unpack(\n        &file->buffer[file->buffer_counter],\n        (size_t)(file->buffer_size - file->buffer_counter),\n        pulse,\n        duration,\n        &size);\n\n    if(result) {\n        file->buffer_counter += size;\n    } else {\n        FURI_LOG_E(TAG, \"read pair: buffer is too small\");\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_raw_file.h",
    "content": "#pragma once\n#include <furi.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct LFRFIDRawFile LFRFIDRawFile;\n\n/**\n * @brief Allocate a new LFRFIDRawFile instance\n * \n * @param storage \n * @return LFRFIDRawFile* \n */\nLFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage);\n\n/**\n * @brief Free a LFRFIDRawFile instance\n * \n * @param file \n */\nvoid lfrfid_raw_file_free(LFRFIDRawFile* file);\n\n/**\n * @brief Open RAW file for writing\n * \n * @param file \n * @param file_path \n * @return bool \n */\nbool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path);\n\n/**\n * @brief Open RAW file for reading\n * @param file \n * @param file_path \n * @return bool \n */\nbool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path);\n\n/**\n * @brief Write RAW file header\n * \n * @param file \n * @param frequency \n * @param duty_cycle \n * @param max_buffer_size \n * @return bool \n */\nbool lfrfid_raw_file_write_header(\n    LFRFIDRawFile* file,\n    float frequency,\n    float duty_cycle,\n    uint32_t max_buffer_size);\n\n/**\n * @brief Write data to RAW file\n * \n * @param file \n * @param buffer_data \n * @param buffer_size \n * @return bool \n */\nbool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size);\n\n/**\n * @brief Read RAW file header\n * \n * @param file \n * @param frequency \n * @param duty_cycle \n * @return bool \n */\nbool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle);\n\n/**\n * @brief Read varint-encoded pair from RAW file\n * \n * @param file \n * @param duration \n * @param pulse \n * @param pass_end file was wrapped around, can be NULL\n * @return bool \n */\nbool lfrfid_raw_file_read_pair(\n    LFRFIDRawFile* file,\n    uint32_t* duration,\n    uint32_t* pulse,\n    bool* pass_end);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_raw_worker.c",
    "content": "#include <furi_hal_rfid.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/buffer_stream.h>\n#include <toolbox/varint.h>\n#include \"lfrfid_raw_worker.h\"\n#include \"lfrfid_raw_file.h\"\n#include \"tools/varint_pair.h\"\n\n#define EMULATE_BUFFER_SIZE    1024\n#define RFID_DATA_BUFFER_SIZE  2048\n#define READ_DATA_BUFFER_COUNT 4\n\n#define TAG_EMULATE \"RawEmulate\"\n\n// emulate mode\ntypedef struct {\n    size_t overrun_count;\n    FuriStreamBuffer* stream;\n} RfidEmulateCtx;\n\ntypedef struct {\n    uint32_t emulate_buffer_arr[EMULATE_BUFFER_SIZE];\n    uint32_t emulate_buffer_ccr[EMULATE_BUFFER_SIZE];\n    RfidEmulateCtx ctx;\n} LFRFIDRawWorkerEmulateData;\n\ntypedef enum {\n    HalfTransfer,\n    TransferComplete,\n} LFRFIDRawEmulateDMAEvent;\n\n// read mode\n#define READ_TEMP_DATA_SIZE 10\n\ntypedef struct {\n    BufferStream* stream;\n    VarintPair* pair;\n} LFRFIDRawWorkerReadData;\n\n// main worker\nstruct LFRFIDRawWorker {\n    FuriString* file_path;\n    FuriThread* thread;\n    FuriEventFlag* events;\n\n    LFRFIDWorkerEmulateRawCallback emulate_callback;\n    LFRFIDWorkerReadRawCallback read_callback;\n    void* context;\n\n    float frequency;\n    float duty_cycle;\n};\n\ntypedef enum {\n    LFRFIDRawWorkerEventStop,\n} LFRFIDRawWorkerEvent;\n\nstatic int32_t lfrfid_raw_read_worker_thread(void* thread_context);\nstatic int32_t lfrfid_raw_emulate_worker_thread(void* thread_context);\n\nLFRFIDRawWorker* lfrfid_raw_worker_alloc(void) {\n    LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker));\n\n    worker->thread = furi_thread_alloc_ex(\"LfrfidRawWorker\", 2048, NULL, worker);\n    worker->events = furi_event_flag_alloc();\n    worker->file_path = furi_string_alloc();\n\n    return worker;\n}\n\nvoid lfrfid_raw_worker_free(LFRFIDRawWorker* worker) {\n    furi_check(worker);\n\n    furi_thread_free(worker->thread);\n    furi_event_flag_free(worker->events);\n    furi_string_free(worker->file_path);\n\n    free(worker);\n}\n\nvoid lfrfid_raw_worker_start_read(\n    LFRFIDRawWorker* worker,\n    const char* file_path,\n    float freq,\n    float duty_cycle,\n    LFRFIDWorkerReadRawCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(file_path);\n    furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);\n\n    furi_string_set(worker->file_path, file_path);\n\n    worker->frequency = freq;\n    worker->duty_cycle = duty_cycle;\n    worker->read_callback = callback;\n    worker->context = context;\n\n    furi_thread_set_callback(worker->thread, lfrfid_raw_read_worker_thread);\n\n    furi_thread_start(worker->thread);\n}\n\nvoid lfrfid_raw_worker_start_emulate(\n    LFRFIDRawWorker* worker,\n    const char* file_path,\n    LFRFIDWorkerEmulateRawCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(file_path);\n    furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);\n\n    furi_string_set(worker->file_path, file_path);\n    worker->emulate_callback = callback;\n    worker->context = context;\n    furi_thread_set_callback(worker->thread, lfrfid_raw_emulate_worker_thread);\n    furi_thread_start(worker->thread);\n}\n\nvoid lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) {\n    furi_check(worker);\n\n    worker->emulate_callback = NULL;\n    worker->context = NULL;\n    worker->read_callback = NULL;\n    furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop);\n    furi_thread_join(worker->thread);\n}\n\nstatic void lfrfid_raw_worker_capture(bool level, uint32_t duration, void* context) {\n    LFRFIDRawWorkerReadData* ctx = context;\n\n    bool need_to_send = varint_pair_pack(ctx->pair, level, duration);\n\n    if(need_to_send) {\n        buffer_stream_send_from_isr(\n            ctx->stream, varint_pair_get_data(ctx->pair), varint_pair_get_size(ctx->pair));\n        varint_pair_reset(ctx->pair);\n    }\n}\n\nstatic int32_t lfrfid_raw_read_worker_thread(void* thread_context) {\n    LFRFIDRawWorker* worker = (LFRFIDRawWorker*)thread_context;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);\n    const char* filename = furi_string_get_cstr(worker->file_path);\n    bool file_valid = lfrfid_raw_file_open_write(file, filename);\n\n    LFRFIDRawWorkerReadData* data = malloc(sizeof(LFRFIDRawWorkerReadData));\n\n    data->stream = buffer_stream_alloc(RFID_DATA_BUFFER_SIZE, READ_DATA_BUFFER_COUNT);\n    data->pair = varint_pair_alloc();\n\n    if(file_valid) {\n        // write header\n        file_valid = lfrfid_raw_file_write_header(\n            file, worker->frequency, worker->duty_cycle, RFID_DATA_BUFFER_SIZE);\n    }\n\n    if(file_valid) {\n        // setup carrier\n        furi_hal_rfid_tim_read_start(worker->frequency, worker->duty_cycle);\n\n        // stabilize detector\n        furi_delay_ms(1500);\n\n        // start capture\n        furi_hal_rfid_tim_read_capture_start(lfrfid_raw_worker_capture, data);\n\n        while(1) {\n            Buffer* buffer = buffer_stream_receive(data->stream, 100);\n\n            if(buffer != NULL) {\n                file_valid = lfrfid_raw_file_write_buffer(\n                    file, buffer_get_data(buffer), buffer_get_size(buffer));\n                buffer_reset(buffer);\n            }\n\n            if(!file_valid) {\n                if(worker->read_callback != NULL) {\n                    // message file_error to worker\n                    worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);\n                }\n                break;\n            }\n\n            if(buffer_stream_get_overrun_count(data->stream) > 0 &&\n               worker->read_callback != NULL) {\n                // message overrun to worker\n                worker->read_callback(LFRFIDWorkerReadRawOverrun, worker->context);\n            }\n\n            uint32_t flags = furi_event_flag_get(worker->events);\n            if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {\n                break;\n            }\n        }\n\n        furi_hal_rfid_tim_read_capture_stop();\n        furi_hal_rfid_tim_read_stop();\n    } else {\n        if(worker->read_callback != NULL) {\n            // message file_error to worker\n            worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);\n        }\n    }\n\n    if(!file_valid) {\n        const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);\n        while(true) {\n            uint32_t flags = furi_event_flag_wait(\n                worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);\n\n            if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {\n                break;\n            }\n        }\n    }\n\n    varint_pair_free(data->pair);\n    buffer_stream_free(data->stream);\n    lfrfid_raw_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n    free(data);\n\n    return 0;\n}\n\nstatic void rfid_emulate_dma_isr(bool half, void* context) {\n    RfidEmulateCtx* ctx = context;\n\n    uint32_t flag = half ? HalfTransfer : TransferComplete;\n    size_t len = furi_stream_buffer_send(ctx->stream, &flag, sizeof(uint32_t), 0);\n    if(len != sizeof(uint32_t)) {\n        ctx->overrun_count++;\n    }\n}\n\nstatic int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) {\n    LFRFIDRawWorker* worker = thread_context;\n\n    bool file_valid = true;\n\n    LFRFIDRawWorkerEmulateData* data = malloc(sizeof(LFRFIDRawWorkerEmulateData));\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    data->ctx.overrun_count = 0;\n    data->ctx.stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t));\n\n    LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);\n\n    do {\n        file_valid = lfrfid_raw_file_open_read(file, furi_string_get_cstr(worker->file_path));\n        if(!file_valid) break;\n        file_valid = lfrfid_raw_file_read_header(file, &worker->frequency, &worker->duty_cycle);\n        if(!file_valid) break;\n\n        for(size_t i = 0; i < EMULATE_BUFFER_SIZE; i++) {\n            file_valid = lfrfid_raw_file_read_pair(\n                file, &data->emulate_buffer_arr[i], &data->emulate_buffer_ccr[i], NULL);\n            if(!file_valid) break;\n            data->emulate_buffer_arr[i] /= 8;\n            data->emulate_buffer_arr[i] -= 1;\n            data->emulate_buffer_ccr[i] /= 8;\n        }\n    } while(false);\n\n    furi_hal_rfid_tim_emulate_dma_start(\n        data->emulate_buffer_arr,\n        data->emulate_buffer_ccr,\n        EMULATE_BUFFER_SIZE,\n        rfid_emulate_dma_isr,\n        &data->ctx);\n\n    if(!file_valid && worker->emulate_callback != NULL) {\n        // message file_error to worker\n        worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);\n    }\n\n    if(file_valid) {\n        uint32_t flag = 0;\n\n        while(true) {\n            size_t size =\n                furi_stream_buffer_receive(data->ctx.stream, &flag, sizeof(uint32_t), 100);\n\n            if(size == sizeof(uint32_t)) {\n                size_t start = 0;\n                if(flag == TransferComplete) {\n                    start = (EMULATE_BUFFER_SIZE / 2);\n                }\n\n                for(size_t i = 0; i < (EMULATE_BUFFER_SIZE / 2); i++) {\n                    file_valid = lfrfid_raw_file_read_pair(\n                        file,\n                        &data->emulate_buffer_arr[start + i],\n                        &data->emulate_buffer_ccr[start + i],\n                        NULL);\n                    if(!file_valid) break;\n                    data->emulate_buffer_arr[i] /= 8;\n                    data->emulate_buffer_arr[i] -= 1;\n                    data->emulate_buffer_ccr[i] /= 8;\n                }\n            } else if(size != 0) {\n                data->ctx.overrun_count++;\n            }\n\n            if(!file_valid) {\n                if(worker->emulate_callback != NULL) {\n                    // message file_error to worker\n                    worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);\n                }\n                break;\n            }\n\n            if(data->ctx.overrun_count > 0 && worker->emulate_callback != NULL) {\n                // message overrun to worker\n                worker->emulate_callback(LFRFIDWorkerEmulateRawOverrun, worker->context);\n            }\n\n            uint32_t flags = furi_event_flag_get(worker->events);\n            if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {\n                break;\n            };\n        }\n    }\n\n    furi_hal_rfid_tim_emulate_dma_stop();\n\n    if(!file_valid) {\n        const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);\n        while(true) {\n            uint32_t flags = furi_event_flag_wait(\n                worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);\n\n            if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {\n                break;\n            };\n        }\n    }\n\n    if(data->ctx.overrun_count) {\n        FURI_LOG_E(TAG_EMULATE, \"overruns: %zu\", data->ctx.overrun_count);\n    }\n\n    furi_stream_buffer_free(data->ctx.stream);\n    lfrfid_raw_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n    free(data);\n\n    return 0;\n}\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_raw_worker.h",
    "content": "#pragma once\n#include <furi.h>\n#include \"lfrfid_worker.h\"\n#include <toolbox/protocols/protocol_dict.h>\n#include \"protocols/lfrfid_protocols.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct LFRFIDRawWorker LFRFIDRawWorker;\n\n/**\n * @brief Allocate a new LFRFIDRawWorker instance\n * \n * @return LFRFIDRawWorker* \n */\nLFRFIDRawWorker* lfrfid_raw_worker_alloc(void);\n\n/**\n * @brief Free a LFRFIDRawWorker instance\n * \n * @param worker LFRFIDRawWorker instance\n */\nvoid lfrfid_raw_worker_free(LFRFIDRawWorker* worker);\n\n/**\n * @brief Start reading\n * \n * @param worker LFRFIDRawWorker instance\n * @param file_path path where file will be saved\n * @param frequency HW frequency\n * @param duty_cycle HW duty cycle\n * @param callback callback for read event\n * @param context context for callback\n */\nvoid lfrfid_raw_worker_start_read(\n    LFRFIDRawWorker* worker,\n    const char* file_path,\n    float frequency,\n    float duty_cycle,\n    LFRFIDWorkerReadRawCallback callback,\n    void* context);\n\n/**\n * @brief Start emulate\n * \n * @param worker LFRFIDRawWorker instance\n * @param file_path path to file that will be emulated\n * @param callback callback for emulate event\n * @param context context for callback\n */\nvoid lfrfid_raw_worker_start_emulate(\n    LFRFIDRawWorker* worker,\n    const char* file_path,\n    LFRFIDWorkerEmulateRawCallback callback,\n    void* context);\n\n/**\n * @brief Stop worker\n * \n * @param worker \n */\nvoid lfrfid_raw_worker_stop(LFRFIDRawWorker* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_worker.c",
    "content": "#include \"lfrfid_worker_i.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\ntypedef enum {\n    LFRFIDEventStopThread = (1 << 0),\n    LFRFIDEventStopMode = (1 << 1),\n    LFRFIDEventRead = (1 << 2),\n    LFRFIDEventWrite = (1 << 3),\n    LFRFIDEventEmulate = (1 << 4),\n    LFRFIDEventReadRaw = (1 << 5),\n    LFRFIDEventEmulateRaw = (1 << 6),\n    LFRFIDEventAll =\n        (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite |\n         LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw),\n} LFRFIDEventType;\n\nstatic int32_t lfrfid_worker_thread(void* thread_context);\n\nLFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) {\n    furi_check(dict);\n\n    LFRFIDWorker* worker = malloc(sizeof(LFRFIDWorker));\n    worker->mode_index = LFRFIDWorkerIdle;\n    worker->read_cb = NULL;\n    worker->write_cb = NULL;\n    worker->cb_ctx = NULL;\n    worker->raw_filename = NULL;\n    worker->mode_storage = NULL;\n\n    worker->thread = furi_thread_alloc_ex(\"LfrfidWorker\", 2048, lfrfid_worker_thread, worker);\n\n    worker->protocols = dict;\n\n    return worker;\n}\n\nvoid lfrfid_worker_free(LFRFIDWorker* worker) {\n    furi_check(worker);\n\n    if(worker->raw_filename) {\n        free(worker->raw_filename);\n    }\n\n    furi_thread_free(worker->thread);\n    free(worker);\n}\n\nvoid lfrfid_worker_read_start(\n    LFRFIDWorker* worker,\n    LFRFIDWorkerReadType type,\n    LFRFIDWorkerReadCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == LFRFIDWorkerIdle);\n\n    worker->read_type = type;\n    worker->read_cb = callback;\n    worker->cb_ctx = context;\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventRead);\n}\n\nvoid lfrfid_worker_write_start(\n    LFRFIDWorker* worker,\n    LFRFIDProtocol protocol,\n    LFRFIDWorkerWriteCallback callback,\n    void* context) {\n    furi_check(worker->mode_index == LFRFIDWorkerIdle);\n    worker->protocol = protocol;\n    worker->write_cb = callback;\n    worker->cb_ctx = context;\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite);\n}\n\nvoid lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) {\n    furi_check(worker);\n    furi_check(worker->mode_index == LFRFIDWorkerIdle);\n\n    worker->protocol = protocol;\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulate);\n}\n\nvoid lfrfid_worker_set_filename(LFRFIDWorker* worker, const char* filename) {\n    if(worker->raw_filename) {\n        free(worker->raw_filename);\n    }\n\n    worker->raw_filename = strdup(filename);\n}\n\nvoid lfrfid_worker_read_raw_start(\n    LFRFIDWorker* worker,\n    const char* filename,\n    LFRFIDWorkerReadType type,\n    LFRFIDWorkerReadRawCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == LFRFIDWorkerIdle);\n\n    worker->read_type = type;\n    worker->read_raw_cb = callback;\n    worker->cb_ctx = context;\n    lfrfid_worker_set_filename(worker, filename);\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventReadRaw);\n}\n\nvoid lfrfid_worker_emulate_raw_start(\n    LFRFIDWorker* worker,\n    const char* filename,\n    LFRFIDWorkerEmulateRawCallback callback,\n    void* context) {\n    furi_check(worker);\n    furi_check(worker->mode_index == LFRFIDWorkerIdle);\n\n    lfrfid_worker_set_filename(worker, filename);\n    worker->emulate_raw_cb = callback;\n    worker->cb_ctx = context;\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulateRaw);\n}\n\nvoid lfrfid_worker_stop(LFRFIDWorker* worker) {\n    furi_check(worker);\n\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopMode);\n}\n\nvoid lfrfid_worker_start_thread(LFRFIDWorker* worker) {\n    furi_check(worker);\n\n    furi_thread_start(worker->thread);\n}\n\nvoid lfrfid_worker_stop_thread(LFRFIDWorker* worker) {\n    furi_check(worker);\n\n    furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopThread);\n    furi_thread_join(worker->thread);\n}\n\nbool lfrfid_worker_check_for_stop(LFRFIDWorker* worker) {\n    UNUSED(worker);\n    uint32_t flags = furi_thread_flags_get();\n    return flags & LFRFIDEventStopMode;\n}\n\nsize_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol protocol) {\n    furi_assert(worker->mode_index == LFRFIDWorkerIdle);\n    return protocol_dict_get_data_size(worker->protocols, protocol);\n}\n\nstatic int32_t lfrfid_worker_thread(void* thread_context) {\n    LFRFIDWorker* worker = thread_context;\n\n    while(true) {\n        uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);\n        if(flags != (unsigned)FuriFlagErrorTimeout) {\n            // stop thread\n            if(flags & LFRFIDEventStopThread) break;\n\n            // switch mode\n            if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead;\n            if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite;\n            if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate;\n            if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw;\n            if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw;\n\n            // do mode, if it exists\n            if(lfrfid_worker_modes[worker->mode_index].process) {\n                lfrfid_worker_modes[worker->mode_index].process(worker);\n            }\n\n            // reset mode\n            worker->mode_index = LFRFIDWorkerIdle;\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_worker.h",
    "content": "/** @file lfrfid_worker.h\n * \n * LFRFID worker\n */\n\n#pragma once\n#include <toolbox/protocols/protocol_dict.h>\n#include \"protocols/lfrfid_protocols.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    LFRFIDWorkerWriteOK,\n    LFRFIDWorkerWriteProtocolCannotBeWritten,\n    LFRFIDWorkerWriteFobCannotBeWritten,\n    LFRFIDWorkerWriteTooLongToWrite,\n} LFRFIDWorkerWriteResult;\n\ntypedef enum {\n    LFRFIDWorkerReadTypeAuto,\n    LFRFIDWorkerReadTypeASKOnly,\n    LFRFIDWorkerReadTypePSKOnly,\n} LFRFIDWorkerReadType;\n\ntypedef enum {\n    LFRFIDWorkerReadSenseStart, // TODO FL-3516: not implemented\n    LFRFIDWorkerReadSenseEnd, // TODO FL-3516: not implemented\n    LFRFIDWorkerReadSenseCardStart,\n    LFRFIDWorkerReadSenseCardEnd,\n    LFRFIDWorkerReadStartASK,\n    LFRFIDWorkerReadStartPSK,\n    LFRFIDWorkerReadDone,\n} LFRFIDWorkerReadResult;\n\ntypedef enum {\n    LFRFIDWorkerReadRawFileError,\n    LFRFIDWorkerReadRawOverrun,\n} LFRFIDWorkerReadRawResult;\n\ntypedef enum {\n    LFRFIDWorkerEmulateRawFileError,\n    LFRFIDWorkerEmulateRawOverrun,\n} LFRFIDWorkerEmulateRawResult;\n\ntypedef void (\n    *LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context);\ntypedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context);\n\ntypedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context);\ntypedef void (*LFRFIDWorkerEmulateRawCallback)(LFRFIDWorkerEmulateRawResult result, void* context);\n\ntypedef struct LFRFIDWorker LFRFIDWorker;\n\n/** Allocate LF-RFID worker\n * @return LFRFIDWorker* \n */\nLFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict);\n\n/** Free LF-RFID worker\n *\n * @param      worker  The worker\n */\nvoid lfrfid_worker_free(LFRFIDWorker* worker);\n\n/** Start LF-RFID worker thread\n *\n * @param      worker  The worker\n */\nvoid lfrfid_worker_start_thread(LFRFIDWorker* worker);\n\n/** Stop LF-RFID worker thread\n *\n * @param      worker  The worker\n */\nvoid lfrfid_worker_stop_thread(LFRFIDWorker* worker);\n\n/** Start read mode\n *\n * @param      worker    The worker\n * @param      type      The type\n * @param      callback  The callback\n * @param      context   The context\n */\nvoid lfrfid_worker_read_start(\n    LFRFIDWorker* worker,\n    LFRFIDWorkerReadType type,\n    LFRFIDWorkerReadCallback callback,\n    void* context);\n\n/** Start write mode\n *\n * @param      worker    The worker\n * @param      protocol  The protocol\n * @param      callback  The callback\n * @param      context   The context\n */\nvoid lfrfid_worker_write_start(\n    LFRFIDWorker* worker,\n    LFRFIDProtocol protocol,\n    LFRFIDWorkerWriteCallback callback,\n    void* context);\n\n/** Start emulate mode\n *\n * @param      worker    The worker\n * @param[in]  protocol  The protocol\n */\nvoid lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol);\n\n/** Start raw read mode\n *\n * @param      worker    The worker\n * @param      filename  The filename\n * @param      type      The type\n * @param      callback  The callback\n * @param      context   The context\n */\nvoid lfrfid_worker_read_raw_start(\n    LFRFIDWorker* worker,\n    const char* filename,\n    LFRFIDWorkerReadType type,\n    LFRFIDWorkerReadRawCallback callback,\n    void* context);\n\n/** Emulate raw read mode\n *\n * @param      worker    The worker\n * @param      filename  The filename\n * @param      callback  The callback\n * @param      context   The context\n */\nvoid lfrfid_worker_emulate_raw_start(\n    LFRFIDWorker* worker,\n    const char* filename,\n    LFRFIDWorkerEmulateRawCallback callback,\n    void* context);\n\n/** Stop all modes\n *\n * @param      worker  The worker\n */\nvoid lfrfid_worker_stop(LFRFIDWorker* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_worker_i.h",
    "content": "/**\n * @file lfrfid_worker_i.h\n * \n * lfrfid worker, internal definitions \n */\n\n#pragma once\n#include <furi.h>\n#include \"lfrfid_worker.h\"\n#include \"lfrfid_raw_worker.h\"\n#include \"protocols/lfrfid_protocols.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    void (*const process)(LFRFIDWorker* worker);\n} LFRFIDWorkerModeType;\n\ntypedef enum {\n    LFRFIDWorkerIdle,\n    LFRFIDWorkerRead,\n    LFRFIDWorkerWrite,\n    LFRFIDWorkerEmulate,\n    LFRFIDWorkerReadRaw,\n    LFRFIDWorkerEmulateRaw,\n} LFRFIDWorkerMode;\n\nstruct LFRFIDWorker {\n    char* raw_filename;\n\n    LFRFIDWorkerMode mode_index;\n    void* mode_storage;\n\n    FuriEventFlag* events;\n    FuriThread* thread;\n\n    LFRFIDWorkerReadType read_type;\n\n    LFRFIDWorkerReadCallback read_cb;\n    LFRFIDWorkerWriteCallback write_cb;\n    LFRFIDWorkerReadRawCallback read_raw_cb;\n    LFRFIDWorkerEmulateRawCallback emulate_raw_cb;\n\n    void* cb_ctx;\n\n    ProtocolDict* protocols;\n    LFRFIDProtocol protocol;\n};\n\nextern const LFRFIDWorkerModeType lfrfid_worker_modes[];\n\n/**\n * @brief Check for stop flag\n * \n * @param worker \n * @return bool \n */\nbool lfrfid_worker_check_for_stop(LFRFIDWorker* worker);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/lfrfid_worker_modes.c",
    "content": "#include <furi.h>\n#include <furi_hal.h>\n#include \"lfrfid_worker_i.h\"\n#include \"tools/t5577.h\"\n#include <toolbox/pulse_protocols/pulse_glue.h>\n#include <toolbox/buffer_stream.h>\n#include \"tools/varint_pair.h\"\n#include <lib/bit_lib/bit_lib.h>\n\n#define TAG \"LfRfidWorker\"\n\n/**\n * if READ_DEBUG_GPIO is defined:\n *     gpio_ext_pa7 will repeat signal coming from the comparator\n *     gpio_ext_pa6 will show load on the decoder\n */\n// #define LFRFID_WORKER_READ_DEBUG_GPIO 1\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n#define LFRFID_WORKER_READ_DEBUG_GPIO_VALUE &gpio_ext_pa7\n#define LFRFID_WORKER_READ_DEBUG_GPIO_LOAD  &gpio_ext_pa6\n#endif\n\n#define LFRFID_WORKER_READ_AVERAGE_COUNT 64\n#define LFRFID_WORKER_READ_MIN_TIME_US   16\n\n#define LFRFID_WORKER_READ_DROP_TIME_MS      50\n#define LFRFID_WORKER_READ_STABILIZE_TIME_MS 450\n#define LFRFID_WORKER_READ_SWITCH_TIME_MS    2000\n\n#define LFRFID_WORKER_WRITE_VERIFY_TIME_MS   2000\n#define LFRFID_WORKER_WRITE_DROP_TIME_MS     50\n#define LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS 10000\n\n#define LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS 5\n\n#define LFRFID_WORKER_READ_BUFFER_SIZE  512\n#define LFRFID_WORKER_READ_BUFFER_COUNT 16\n\n#define LFRFID_WORKER_EMULATE_BUFFER_SIZE 1024\n\n#define LFRFID_WORKER_DELAY_QUANT 50\n\nvoid lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) {\n    for(uint32_t i = 0; i < (milliseconds / LFRFID_WORKER_DELAY_QUANT); i++) {\n        if(lfrfid_worker_check_for_stop(worker)) break;\n        furi_delay_ms(LFRFID_WORKER_DELAY_QUANT);\n    }\n}\n\n/**************************************************************************************************/\n/********************************************** READ **********************************************/\n/**************************************************************************************************/\n\ntypedef struct {\n    BufferStream* stream;\n    VarintPair* pair;\n    bool ignore_next_pulse;\n} LFRFIDWorkerReadContext;\n\nstatic void lfrfid_worker_read_capture(bool level, uint32_t duration, void* context) {\n    LFRFIDWorkerReadContext* ctx = context;\n\n    // ignore pulse if last pulse was noise\n    if(ctx->ignore_next_pulse) {\n        ctx->ignore_next_pulse = false;\n        return;\n    }\n\n    // ignore noise spikes\n    if(duration <= LFRFID_WORKER_READ_MIN_TIME_US) {\n        if(level) {\n            ctx->ignore_next_pulse = true;\n        }\n        varint_pair_reset(ctx->pair);\n        return;\n    }\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n    furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, level);\n#endif\n\n    bool need_to_send = varint_pair_pack(ctx->pair, level, duration);\n    if(need_to_send) {\n        buffer_stream_send_from_isr(\n            ctx->stream, varint_pair_get_data(ctx->pair), varint_pair_get_size(ctx->pair));\n        varint_pair_reset(ctx->pair);\n    }\n}\n\ntypedef enum {\n    LFRFIDWorkerReadOK,\n    LFRFIDWorkerReadExit,\n    LFRFIDWorkerReadTimeout,\n} LFRFIDWorkerReadState;\n\nstatic LFRFIDWorkerReadState lfrfid_worker_read_internal(\n    LFRFIDWorker* worker,\n    LFRFIDFeature feature,\n    uint32_t timeout,\n    ProtocolId* result_protocol) {\n    LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout;\n\n    if(feature & LFRFIDFeatureASK) {\n        furi_hal_rfid_tim_read_start(125000, 0.5);\n        FURI_LOG_D(TAG, \"Start ASK\");\n        if(worker->read_cb) {\n            worker->read_cb(LFRFIDWorkerReadStartASK, PROTOCOL_NO, worker->cb_ctx);\n        }\n    } else {\n        furi_hal_rfid_tim_read_start(62500, 0.25);\n        FURI_LOG_D(TAG, \"Start PSK\");\n        if(worker->read_cb) {\n            worker->read_cb(LFRFIDWorkerReadStartPSK, PROTOCOL_NO, worker->cb_ctx);\n        }\n    }\n\n    // stabilize detector\n    lfrfid_worker_delay(worker, LFRFID_WORKER_READ_STABILIZE_TIME_MS);\n\n    protocol_dict_decoders_start(worker->protocols);\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeOutputPushPull);\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull);\n    furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, false);\n    furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n#endif\n\n    LFRFIDWorkerReadContext ctx;\n    ctx.pair = varint_pair_alloc();\n    ctx.stream =\n        buffer_stream_alloc(LFRFID_WORKER_READ_BUFFER_SIZE, LFRFID_WORKER_READ_BUFFER_COUNT);\n\n    furi_hal_rfid_tim_read_capture_start(lfrfid_worker_read_capture, &ctx);\n\n    *result_protocol = PROTOCOL_NO;\n    ProtocolId last_protocol = PROTOCOL_NO;\n    size_t last_size = protocol_dict_get_max_data_size(worker->protocols);\n    uint8_t* last_data = malloc(last_size);\n    uint8_t* protocol_data = malloc(last_size);\n    size_t last_read_count = 0;\n\n    uint32_t switch_os_tick_last = furi_get_tick();\n\n    uint32_t average_duration = 0;\n    uint32_t average_pulse = 0;\n    size_t average_index = 0;\n    bool card_detected = false;\n\n    FURI_LOG_D(TAG, \"Read started\");\n    while(true) {\n        if(lfrfid_worker_check_for_stop(worker)) {\n            state = LFRFIDWorkerReadExit;\n            break;\n        }\n\n        Buffer* buffer = buffer_stream_receive(ctx.stream, 100);\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n        furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true);\n#endif\n\n        if(buffer_stream_get_overrun_count(ctx.stream) > 0) {\n            FURI_LOG_E(TAG, \"Read overrun, recovering\");\n            buffer_stream_reset(ctx.stream);\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n            furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n#endif\n            continue;\n        }\n\n        if(buffer == NULL) {\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n            furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n#endif\n            continue;\n        }\n\n        size_t size = buffer_get_size(buffer);\n        uint8_t* data = buffer_get_data(buffer);\n        size_t index = 0;\n\n        while(index < size) {\n            uint32_t duration;\n            uint32_t pulse;\n            size_t tmp_size;\n\n            if(!varint_pair_unpack(&data[index], size - index, &pulse, &duration, &tmp_size)) {\n                FURI_LOG_E(TAG, \"can't unpack varint pair\");\n                break;\n            } else {\n                index += tmp_size;\n\n                average_duration += duration;\n                average_pulse += pulse;\n                average_index++;\n                if(average_index >= LFRFID_WORKER_READ_AVERAGE_COUNT) {\n                    float average = (float)average_pulse / (float)average_duration;\n                    average_pulse = 0;\n                    average_duration = 0;\n                    average_index = 0;\n\n                    if(worker->read_cb) {\n                        if(average > 0.2f && average < 0.8f) {\n                            if(!card_detected) {\n                                card_detected = true;\n                                worker->read_cb(\n                                    LFRFIDWorkerReadSenseStart, PROTOCOL_NO, worker->cb_ctx);\n                            }\n                        } else {\n                            if(card_detected) {\n                                card_detected = false;\n                                worker->read_cb(\n                                    LFRFIDWorkerReadSenseEnd, PROTOCOL_NO, worker->cb_ctx);\n                            }\n                        }\n                    }\n                }\n\n                ProtocolId protocol = PROTOCOL_NO;\n\n                protocol = protocol_dict_decoders_feed_by_feature(\n                    worker->protocols, feature, true, pulse);\n                if(protocol == PROTOCOL_NO) {\n                    protocol = protocol_dict_decoders_feed_by_feature(\n                        worker->protocols, feature, false, duration - pulse);\n                }\n\n                if(protocol != PROTOCOL_NO) {\n                    // reset switch timer\n                    switch_os_tick_last = furi_get_tick();\n\n                    size_t protocol_data_size =\n                        protocol_dict_get_data_size(worker->protocols, protocol);\n                    protocol_dict_get_data(\n                        worker->protocols, protocol, protocol_data, protocol_data_size);\n\n                    // validate protocol\n                    if(protocol == last_protocol &&\n                       memcmp(last_data, protocol_data, protocol_data_size) == 0) {\n                        last_read_count = last_read_count + 1;\n\n                        size_t validation_count =\n                            protocol_dict_get_validate_count(worker->protocols, protocol);\n\n                        if(last_read_count >= validation_count) {\n                            state = LFRFIDWorkerReadOK;\n                            *result_protocol = protocol;\n                            break;\n                        }\n                    } else {\n                        if(last_protocol == PROTOCOL_NO && worker->read_cb) {\n                            worker->read_cb(\n                                LFRFIDWorkerReadSenseCardStart, protocol, worker->cb_ctx);\n                        }\n\n                        last_protocol = protocol;\n                        memcpy(last_data, protocol_data, protocol_data_size);\n                        last_read_count = 0;\n                    }\n\n                    if(furi_log_get_level() >= FuriLogLevelDebug) {\n                        FuriString* string_info;\n                        string_info = furi_string_alloc();\n                        for(uint8_t i = 0; i < protocol_data_size; i++) {\n                            if(i != 0) {\n                                furi_string_cat_printf(string_info, \" \");\n                            }\n\n                            furi_string_cat_printf(string_info, \"%02X\", protocol_data[i]);\n                        }\n\n                        FURI_LOG_D(\n                            TAG,\n                            \"%s, %zu, [%s]\",\n                            protocol_dict_get_name(worker->protocols, protocol),\n                            last_read_count,\n                            furi_string_get_cstr(string_info));\n                        furi_string_free(string_info);\n                    }\n\n                    protocol_dict_decoders_start(worker->protocols);\n                }\n            }\n        }\n\n        buffer_reset(buffer);\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n        furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n#endif\n\n        if(*result_protocol != PROTOCOL_NO) {\n            break;\n        }\n\n        if((furi_get_tick() - switch_os_tick_last) > timeout) {\n            state = LFRFIDWorkerReadTimeout;\n            break;\n        }\n    }\n\n    FURI_LOG_D(TAG, \"Read stopped\");\n\n    if(last_protocol != PROTOCOL_NO && worker->read_cb) {\n        worker->read_cb(LFRFIDWorkerReadSenseCardEnd, last_protocol, worker->cb_ctx);\n    }\n\n    if(card_detected && worker->read_cb) {\n        worker->read_cb(LFRFIDWorkerReadSenseEnd, last_protocol, worker->cb_ctx);\n    }\n\n    furi_hal_rfid_tim_read_capture_stop();\n    furi_hal_rfid_tim_read_stop();\n    furi_hal_rfid_pins_reset();\n\n    varint_pair_free(ctx.pair);\n    buffer_stream_free(ctx.stream);\n\n    free(protocol_data);\n    free(last_data);\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n    furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, false);\n    furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeAnalog);\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog);\n#endif\n\n    return state;\n}\n\nstatic void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) {\n    ProtocolId read_result = PROTOCOL_NO;\n    LFRFIDWorkerReadState state;\n    LFRFIDFeature feature;\n\n    if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) {\n        feature = LFRFIDFeaturePSK;\n    } else {\n        feature = LFRFIDFeatureASK;\n    }\n\n    if(worker->read_type == LFRFIDWorkerReadTypeAuto) {\n        while(1) {\n            // read for a while\n            state = lfrfid_worker_read_internal(\n                worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result);\n\n            if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) {\n                break;\n            }\n\n            // switch to next feature\n            if(feature == LFRFIDFeatureASK) {\n                feature = LFRFIDFeaturePSK;\n            } else {\n                feature = LFRFIDFeatureASK;\n            }\n\n            lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS);\n        }\n    } else {\n        while(1) {\n            if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) {\n                state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result);\n            } else {\n                state = lfrfid_worker_read_internal(\n                    worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result);\n            }\n\n            if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) {\n                break;\n            }\n\n            lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS);\n        }\n    }\n\n    if(state == LFRFIDWorkerReadOK && worker->read_cb) {\n        worker->read_cb(LFRFIDWorkerReadDone, read_result, worker->cb_ctx);\n    }\n}\n\n/**************************************************************************************************/\n/******************************************** EMULATE *********************************************/\n/**************************************************************************************************/\n\ntypedef struct {\n    uint32_t duration[LFRFID_WORKER_EMULATE_BUFFER_SIZE];\n    uint32_t pulse[LFRFID_WORKER_EMULATE_BUFFER_SIZE];\n} LFRFIDWorkerEmulateBuffer;\n\ntypedef enum {\n    HalfTransfer,\n    TransferComplete,\n} LFRFIDWorkerEmulateDMAEvent;\n\nstatic void lfrfid_worker_emulate_dma_isr(bool half, void* context) {\n    FuriStreamBuffer* stream = context;\n    uint32_t flag = half ? HalfTransfer : TransferComplete;\n    furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0);\n}\n\nstatic void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) {\n    LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer));\n    FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t));\n    LFRFIDProtocol protocol = worker->protocol;\n    PulseGlue* pulse_glue = pulse_glue_alloc();\n\n    protocol_dict_encoder_start(worker->protocols, protocol);\n\n    for(size_t i = 0; i < LFRFID_WORKER_EMULATE_BUFFER_SIZE; i++) {\n        bool pulse_pop = false;\n        while(!pulse_pop) {\n            LevelDuration level_duration =\n                protocol_dict_encoder_yield(worker->protocols, protocol);\n            pulse_pop = pulse_glue_push(\n                pulse_glue,\n                level_duration_get_level(level_duration),\n                level_duration_get_duration(level_duration));\n        }\n        uint32_t duration, pulse;\n        pulse_glue_pop(pulse_glue, &duration, &pulse);\n        buffer->duration[i] = duration - 1;\n        buffer->pulse[i] = pulse;\n    }\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull);\n#endif\n\n    furi_hal_rfid_tim_emulate_dma_start(\n        buffer->duration,\n        buffer->pulse,\n        LFRFID_WORKER_EMULATE_BUFFER_SIZE,\n        lfrfid_worker_emulate_dma_isr,\n        stream);\n\n    while(true) {\n        uint32_t flag = 0;\n        size_t size = furi_stream_buffer_receive(stream, &flag, sizeof(uint32_t), 100);\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n        furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true);\n#endif\n\n        if(size == sizeof(uint32_t)) {\n            size_t start = 0;\n\n            if(flag == HalfTransfer) {\n                start = 0;\n            } else if(flag == TransferComplete) {\n                start = (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2);\n            }\n\n            for(size_t i = 0; i < (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); i++) {\n                bool pulse_pop = false;\n                while(!pulse_pop) {\n                    LevelDuration level_duration =\n                        protocol_dict_encoder_yield(worker->protocols, protocol);\n                    pulse_pop = pulse_glue_push(\n                        pulse_glue,\n                        level_duration_get_level(level_duration),\n                        level_duration_get_duration(level_duration));\n                }\n                uint32_t duration, pulse;\n                pulse_glue_pop(pulse_glue, &duration, &pulse);\n                buffer->duration[start + i] = duration - 1;\n                buffer->pulse[start + i] = pulse;\n            }\n        }\n\n        if(lfrfid_worker_check_for_stop(worker)) {\n            break;\n        }\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n        furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);\n#endif\n    }\n\n    furi_hal_rfid_tim_emulate_dma_stop();\n\n#ifdef LFRFID_WORKER_READ_DEBUG_GPIO\n    furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog);\n#endif\n\n    free(buffer);\n    furi_stream_buffer_free(stream);\n    pulse_glue_free(pulse_glue);\n}\n\n/**************************************************************************************************/\n/********************************************* WRITE **********************************************/\n/**************************************************************************************************/\n\nstatic void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) {\n    LFRFIDProtocol protocol = worker->protocol;\n    LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest));\n\n    uint32_t write_start_time = furi_get_tick();\n    bool too_long = false;\n    size_t unsuccessful_reads = 0;\n\n    size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol);\n    uint8_t* verify_data = malloc(data_size);\n    uint8_t* read_data = malloc(data_size);\n\n    protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size);\n\n    while(!lfrfid_worker_check_for_stop(worker)) {\n        FURI_LOG_D(TAG, \"Data write\");\n        uint16_t skips = 0;\n        for(size_t i = 0; i < LFRFIDWriteTypeMax; i++) {\n            memset(request, 0, sizeof(LFRFIDWriteRequest));\n            LFRFIDWriteType write_type = i;\n            request->write_type = write_type;\n\n            protocol_dict_set_data(worker->protocols, protocol, verify_data, data_size);\n\n            bool can_be_written =\n                protocol_dict_get_write_data(worker->protocols, protocol, request);\n\n            if(!can_be_written) {\n                skips++;\n                if(skips == LFRFIDWriteTypeMax) {\n                    if(worker->write_cb) {\n                        worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx);\n                    }\n                    break;\n                }\n                continue;\n            }\n\n            memset(read_data, 0, data_size);\n\n            if(request->write_type == LFRFIDWriteTypeT5577) {\n                t5577_write(&request->t5577);\n            } else if(request->write_type == LFRFIDWriteTypeEM4305) {\n                em4305_write(&request->em4305);\n            } else {\n                furi_crash(\"Unknown write type\");\n            }\n        }\n        ProtocolId read_result = PROTOCOL_NO;\n        LFRFIDWorkerReadState state = lfrfid_worker_read_internal(\n            worker,\n            protocol_dict_get_features(worker->protocols, protocol),\n            LFRFID_WORKER_WRITE_VERIFY_TIME_MS,\n            &read_result);\n\n        if(state == LFRFIDWorkerReadOK) {\n            bool read_success = false;\n\n            if(read_result == protocol) {\n                protocol_dict_get_data(worker->protocols, protocol, read_data, data_size);\n\n                if(memcmp(read_data, verify_data, data_size) == 0) {\n                    read_success = true;\n                }\n            }\n\n            if(read_success) {\n                if(worker->write_cb) {\n                    worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx);\n                }\n                break;\n            } else {\n                unsuccessful_reads++;\n\n                if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) {\n                    if(worker->write_cb) {\n                        worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx);\n                    }\n                }\n            }\n        } else if(state == LFRFIDWorkerReadExit) {\n            break;\n        }\n\n        if(!too_long &&\n           (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) {\n            too_long = true;\n            if(worker->write_cb) {\n                worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx);\n            }\n        }\n\n        lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS);\n    }\n\n    free(request);\n    free(verify_data);\n    free(read_data);\n}\n\n/**************************************************************************************************/\n/******************************************* READ RAW *********************************************/\n/**************************************************************************************************/\n\nstatic void lfrfid_worker_mode_read_raw_process(LFRFIDWorker* worker) {\n    LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc();\n\n    switch(worker->read_type) {\n    case LFRFIDWorkerReadTypePSKOnly:\n        lfrfid_raw_worker_start_read(\n            raw_worker, worker->raw_filename, 62500, 0.25, worker->read_raw_cb, worker->cb_ctx);\n        break;\n    case LFRFIDWorkerReadTypeASKOnly:\n        lfrfid_raw_worker_start_read(\n            raw_worker, worker->raw_filename, 125000, 0.5, worker->read_raw_cb, worker->cb_ctx);\n        break;\n    default:\n        furi_crash(\"RAW can be only PSK or ASK\");\n        break;\n    }\n\n    while(!lfrfid_worker_check_for_stop(worker)) {\n        furi_delay_ms(100);\n    }\n\n    lfrfid_raw_worker_stop(raw_worker);\n    lfrfid_raw_worker_free(raw_worker);\n}\n\n/**************************************************************************************************/\n/***************************************** EMULATE RAW ********************************************/\n/**************************************************************************************************/\n\nstatic void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) {\n    LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc();\n\n    lfrfid_raw_worker_start_emulate(\n        raw_worker, worker->raw_filename, worker->emulate_raw_cb, worker->cb_ctx);\n\n    while(!lfrfid_worker_check_for_stop(worker)) {\n        furi_delay_ms(100);\n    }\n\n    lfrfid_raw_worker_stop(raw_worker);\n    lfrfid_raw_worker_free(raw_worker);\n}\n\n/**************************************************************************************************/\n/******************************************** MODES ***********************************************/\n/**************************************************************************************************/\n\nconst LFRFIDWorkerModeType lfrfid_worker_modes[] = {\n    [LFRFIDWorkerIdle] = {.process = NULL},\n    [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process},\n    [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process},\n    [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process},\n    [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process},\n    [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process},\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/lfrfid_protocols.c",
    "content": "#include \"lfrfid_protocols.h\"\n#include \"protocol_em4100.h\"\n#include \"protocol_electra.h\"\n#include \"protocol_h10301.h\"\n#include \"protocol_idteck.h\"\n#include \"protocol_indala26.h\"\n#include \"protocol_io_prox_xsf.h\"\n#include \"protocol_awid.h\"\n#include \"protocol_fdx_a.h\"\n#include \"protocol_fdx_b.h\"\n#include \"protocol_hid_generic.h\"\n#include \"protocol_hid_ex_generic.h\"\n#include \"protocol_pyramid.h\"\n#include \"protocol_viking.h\"\n#include \"protocol_jablotron.h\"\n#include \"protocol_paradox.h\"\n#include \"protocol_pac_stanley.h\"\n#include \"protocol_keri.h\"\n#include \"protocol_gallagher.h\"\n#include \"protocol_nexwatch.h\"\n#include \"protocol_securakey.h\"\n#include \"protocol_gproxii.h\"\n#include \"protocol_noralsy.h\"\n\nconst ProtocolBase* const lfrfid_protocols[] = {\n    [LFRFIDProtocolEM4100] = &protocol_em4100,\n    [LFRFIDProtocolEM410032] = &protocol_em4100_32,\n    [LFRFIDProtocolEM410016] = &protocol_em4100_16,\n    [LFRFIDProtocolElectra] = &protocol_electra,\n    [LFRFIDProtocolH10301] = &protocol_h10301,\n    [LFRFIDProtocolIdteck] = &protocol_idteck,\n    [LFRFIDProtocolIndala26] = &protocol_indala26,\n    [LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf,\n    [LFRFIDProtocolAwid] = &protocol_awid,\n    [LFRFIDProtocolFDXA] = &protocol_fdx_a,\n    [LFRFIDProtocolFDXB] = &protocol_fdx_b,\n    [LFRFIDProtocolHidGeneric] = &protocol_hid_generic,\n    [LFRFIDProtocolHidExGeneric] = &protocol_hid_ex_generic,\n    [LFRFIDProtocolPyramid] = &protocol_pyramid,\n    [LFRFIDProtocolViking] = &protocol_viking,\n    [LFRFIDProtocolJablotron] = &protocol_jablotron,\n    [LFRFIDProtocolParadox] = &protocol_paradox,\n    [LFRFIDProtocolPACStanley] = &protocol_pac_stanley,\n    [LFRFIDProtocolKeri] = &protocol_keri,\n    [LFRFIDProtocolGallagher] = &protocol_gallagher,\n    [LFRFIDProtocolNexwatch] = &protocol_nexwatch,\n    [LFRFIDProtocolSecurakey] = &protocol_securakey,\n    [LFRFIDProtocolGProxII] = &protocol_gproxii,\n    [LFRFIDProtocolNoralsy] = &protocol_noralsy,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/lfrfid_protocols.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n#include \"../tools/t5577.h\"\n#include \"../tools/em4305.h\"\n\ntypedef enum {\n    LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */\n    LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */\n} LFRFIDFeature;\n\ntypedef enum {\n    LFRFIDProtocolEM4100,\n    LFRFIDProtocolEM410032,\n    LFRFIDProtocolEM410016,\n    LFRFIDProtocolElectra,\n    LFRFIDProtocolH10301,\n    LFRFIDProtocolIdteck,\n    LFRFIDProtocolIndala26,\n    LFRFIDProtocolIOProxXSF,\n    LFRFIDProtocolAwid,\n    LFRFIDProtocolFDXA,\n    LFRFIDProtocolFDXB,\n    LFRFIDProtocolHidGeneric,\n    LFRFIDProtocolHidExGeneric,\n    LFRFIDProtocolPyramid,\n    LFRFIDProtocolViking,\n    LFRFIDProtocolJablotron,\n    LFRFIDProtocolParadox,\n    LFRFIDProtocolPACStanley,\n    LFRFIDProtocolKeri,\n    LFRFIDProtocolGallagher,\n    LFRFIDProtocolNexwatch,\n    LFRFIDProtocolSecurakey,\n    LFRFIDProtocolGProxII,\n    LFRFIDProtocolNoralsy,\n\n    LFRFIDProtocolMax,\n} LFRFIDProtocol;\n\nextern const ProtocolBase* const lfrfid_protocols[];\n\ntypedef enum {\n    LFRFIDWriteTypeT5577,\n    LFRFIDWriteTypeEM4305,\n\n    LFRFIDWriteTypeMax,\n} LFRFIDWriteType;\n\ntypedef struct {\n    LFRFIDWriteType write_type;\n    union {\n        LFRFIDT5577 t5577;\n        LFRFIDEM4305 em4305;\n    };\n} LFRFIDWriteRequest;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_awid.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define AWID_DECODED_DATA_SIZE (9)\n\n#define AWID_ENCODED_BIT_SIZE  (96)\n#define AWID_ENCODED_DATA_SIZE (((AWID_ENCODED_BIT_SIZE) / 8) + 1)\n#define AWID_ENCODED_DATA_LAST (AWID_ENCODED_DATA_SIZE - 1)\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolAwidDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n} ProtocolAwidEncoder;\n\ntypedef struct {\n    ProtocolAwidDecoder decoder;\n    ProtocolAwidEncoder encoder;\n    uint8_t encoded_data[AWID_ENCODED_DATA_SIZE];\n    uint8_t data[AWID_DECODED_DATA_SIZE];\n} ProtocolAwid;\n\nProtocolAwid* protocol_awid_alloc(void) {\n    ProtocolAwid* protocol = malloc(sizeof(ProtocolAwid));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_awid_free(ProtocolAwid* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_awid_get_data(ProtocolAwid* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_awid_decoder_start(ProtocolAwid* protocol) {\n    memset(protocol->encoded_data, 0, AWID_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_awid_can_be_decoded(uint8_t* data) {\n    bool result = false;\n\n    // Index map\n    // 0            10            20            30              40            50              60\n    // |            |             |             |               |             |               |\n    // 01234567 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 - to 96\n    // -----------------------------------------------------------------------------\n    // 00000001 000 1 110 1 101 1 011 1 101 1 010 0 000 1 000 1 010 0 001 0 110 1 100 0 000 1 000 1\n    // preamble bbb o bbb o bbw o fff o fff o ffc o ccc o ccc o ccc o ccc o ccc o wxx o xxx o xxx o - to 96\n    //          |---26 bit---|    |-----117----||-------------142-------------|\n    // b = format bit len, o = odd parity of last 3 bits\n    // f = facility code, c = card number\n    // w = wiegand parity\n    // (26 bit format shown)\n\n    do {\n        // check preamble and spacing\n        if(data[0] != 0b00000001 || data[AWID_ENCODED_DATA_LAST] != 0b00000001) break;\n\n        // check odd parity for every 4 bits starting from the second byte\n        bool parity_error = bit_lib_test_parity(data, 8, 88, BitLibParityOdd, 4);\n        if(parity_error) break;\n\n        bit_lib_remove_bit_every_nth(data, 8, 88, 4);\n\n        // Avoid detection for invalid formats\n        uint8_t len = bit_lib_get_bits(data, 8, 8);\n        if(len != 26 && len != 50 && len != 37 && len != 34 && len != 36) break;\n\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nstatic void protocol_awid_decode(uint8_t* encoded_data, uint8_t* decoded_data) {\n    bit_lib_copy_bits(decoded_data, 0, 66, encoded_data, 8);\n}\n\nbool protocol_awid_decoder_feed(ProtocolAwid* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, AWID_ENCODED_DATA_SIZE, value);\n            if(protocol_awid_can_be_decoded(protocol->encoded_data)) {\n                protocol_awid_decode(protocol->encoded_data, protocol->data);\n\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_awid_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {\n    memset(encoded_data, 0, AWID_ENCODED_DATA_SIZE);\n\n    // preamble\n    bit_lib_set_bits(encoded_data, 0, 0b00000001, 8);\n\n    for(size_t i = 0; i < 88 / 4; i++) {\n        uint8_t value = bit_lib_get_bits(decoded_data, i * 3, 3) << 1;\n        value |= bit_lib_test_parity_32(value, BitLibParityOdd);\n        bit_lib_set_bits(encoded_data, 8 + i * 4, value, 4);\n    }\n}\n\nbool protocol_awid_encoder_start(ProtocolAwid* protocol) {\n    protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n    protocol->encoder.encoded_index = 0;\n    fsk_osc_reset(protocol->encoder.fsk_osc);\n    return true;\n}\n\nLevelDuration protocol_awid_encoder_yield(ProtocolAwid* protocol) {\n    bool level;\n    uint32_t duration;\n\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n    bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration);\n\n    if(advance) {\n        bit_lib_increment_index(protocol->encoder.encoded_index, AWID_ENCODED_BIT_SIZE);\n    }\n    return level_duration_make(level, duration);\n}\n\nvoid protocol_awid_render_data(ProtocolAwid* protocol, FuriString* result) {\n    // Index map\n    // 0           10         20        30          40        50        60\n    // |           |          |         |           |         |         |\n    // 01234567 8 90123456 7890123456789012 3 456789012345678901234567890123456\n    // ------------------------------------------------------------------------\n    // 00011010 1 01110101 0000000010001110 1 000000000000000000000000000000000\n    // bbbbbbbb w ffffffff cccccccccccccccc w xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n    // |26 bit|   |-117--| |-----142------|\n    // b = format bit len, o = odd parity of last 3 bits\n    // f = facility code, c = card number\n    // w = wiegand parity\n    // (26 bit format shown)\n\n    uint8_t* decoded_data = protocol->data;\n    uint8_t format_length = decoded_data[0];\n\n    furi_string_printf(result, \"Format: %hhu\\n\", format_length);\n\n    if(format_length == 26) {\n        uint8_t facility;\n        bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9);\n\n        uint16_t card_id;\n        bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17);\n        bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25);\n        furi_string_cat_printf(\n            result,\n            \"FC: %hhu\\n\"\n            \"Card: %hu\",\n            facility,\n            card_id);\n    } else {\n        // print 66 bits as hex\n        furi_string_cat_printf(result, \"Data: \");\n        for(size_t i = 0; i < AWID_DECODED_DATA_SIZE; i++) {\n            furi_string_cat_printf(result, \"%02hhX\", decoded_data[i]);\n        }\n    }\n}\n\nvoid protocol_awid_render_brief_data(ProtocolAwid* protocol, FuriString* result) {\n    uint8_t* decoded_data = protocol->data;\n    uint8_t format_length = decoded_data[0];\n\n    furi_string_printf(result, \"Format: %hhu\", format_length);\n\n    if(format_length == 26) {\n        uint8_t facility;\n        bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9);\n\n        uint16_t card_id;\n        bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17);\n        bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25);\n        furi_string_cat_printf(\n            result,\n            \"; FC: %hhu\\n\"\n            \"Card: %hu\",\n            facility,\n            card_id);\n    } else {\n        furi_string_cat(result, \"\\nData: Unknown\");\n    }\n}\n\nbool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Fix incorrect length byte\n    if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&\n       protocol->data[0] != 34 && protocol->data[0] != 36) {\n        protocol->data[0] = 26;\n    }\n\n    // Correct protocol data by redecoding\n    protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n    bit_lib_remove_bit_every_nth((uint8_t*)protocol->encoded_data, 8, 88, 4);\n    protocol_awid_decode(protocol->encoded_data, protocol->data);\n\n    protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_awid = {\n    .name = \"AWID\",\n    .manufacturer = \"AWID\",\n    .data_size = AWID_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_awid_alloc,\n    .free = (ProtocolFree)protocol_awid_free,\n    .get_data = (ProtocolGetData)protocol_awid_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_awid_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_awid_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_awid_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_awid_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_awid_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_awid_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_awid_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_awid.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_awid;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_electra.c",
    "content": "/*\n * Electra intercom rfid protocol (Romania)\n *\n * Based on EM4100 protocol implementation from https://github.com/flipperdevices/flipperzero-firmware/blob/dev/lib/lfrfid/protocols/protocol_em4100.c\n *\n * Copyright 2024 Leptoptilos <leptoptilos@icloud.com>\n *\n * This program is free software: you can redistribute it and/or modify it\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n * ------------------------------------------------------------------------------------------------------------------------------\n *                                                      PROTOCOL DESCRIPTION:\n * ------------------------------------------------------------------------------------------------------------------------------\n * Electra intercom 125 kHz protocol based on 64-bit clock EM4100, but includes some extra data after base EM4100 data (epilogue)\n *\n * Epilogue size is 64 bits, but only first 16 bits matter. Rest 6 bytes - some filler data, \n * that arbitrary change is not validated by the Electra intercoms\n *\n * There are curently three known types of epilogue:\n * - 0x7E71AAAAAAAAAAAA (AA filler)\n * - 0x7E71000000000000 (00 filler)\n * - 0x0030AAAAAAAAAAAA\n *\n * First two epilogue bytes may be interpreted as EM4100 data continuation\n * Nevertheless, these bytes have correct row parity bits, but have not correct collumn parity\n \n * For example: 0x7E71AAAAAAAAAAAA epilogue:\n *\n * In binary: | 0b01111110 | 01110001 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 |\n * In hex:    |   0x7E     |    71    |    AA    |    AA    |    AA    |    AA    |    AA    |    AA    |\n * \n * As EM4100 data:\n * 0111 1 // 7\n * 1100 0 // C\n * 0111 1 // 7\n * 1101 0 // here epilogue filler starts (from second bit)\n * 1010 1 // there is no correct raw parity bits anymore\n * 0101 0\n * 1010 1\n * 0101 0\n * 1010 // and no correct column parity \n */\n\n#include \"bit_lib/bit_lib.h\"\n#include <furi.h>\n#include <stdlib.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/manchester_decoder.h>\n#include \"lfrfid_protocols.h\"\n\n#define TAG \"ELECTRA\"\n\ntypedef uint64_t ElectraDecodedData;\n\n#define EM_HEADER_POS  (55)\n#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)\n\n#define EM_FIRST_ROW_POS (50)\n\n#define EM_ROW_COUNT          (10)\n#define EM_COLUMN_COUNT       (4)\n#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)\n\n#define EM_COLUMN_POS     (4)\n#define ELECTRA_STOP_POS  (0)\n#define ELECTRA_STOP_MASK (0x1LLU << ELECTRA_STOP_POS)\n\n#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | ELECTRA_STOP_MASK)\n#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)\n\n#define ELECTRA_DECODED_BASE_DATA_SIZE (5)\n#define ELECTRA_ENCODED_BASE_DATA_SIZE (sizeof(ElectraDecodedData))\n\n#define ELECTRA_DECODED_EPILOGUE_SIZE (3)\n#define ELECTRA_ENCODED_EPILOGUE_SIZE (sizeof(ElectraDecodedData))\n\n#define ELECTRA_DECODED_DATA_SIZE (ELECTRA_DECODED_BASE_DATA_SIZE + ELECTRA_DECODED_EPILOGUE_SIZE)\n#define ELECTRA_ENCODED_DATA_SIZE (ELECTRA_ENCODED_BASE_DATA_SIZE + ELECTRA_ENCODED_EPILOGUE_SIZE)\n\n#define ELECTRA_DECODED_DATA_EPILOGUE_START_POS (ELECTRA_DECODED_BASE_DATA_SIZE)\n\n#define ELECTRA_CLOCK_PER_BIT (64)\n\n#define ELECTRA_READ_SHORT_TIME  (256)\n#define ELECTRA_READ_LONG_TIME   (512)\n#define ELECTRA_READ_JITTER_TIME (100)\n\n#define ELECTRA_READ_SHORT_TIME_LOW  (ELECTRA_READ_SHORT_TIME - ELECTRA_READ_JITTER_TIME)\n#define ELECTRA_READ_SHORT_TIME_HIGH (ELECTRA_READ_SHORT_TIME + ELECTRA_READ_JITTER_TIME)\n#define ELECTRA_READ_LONG_TIME_LOW   (ELECTRA_READ_LONG_TIME - ELECTRA_READ_JITTER_TIME)\n#define ELECTRA_READ_LONG_TIME_HIGH  (ELECTRA_READ_LONG_TIME + ELECTRA_READ_JITTER_TIME)\n\n#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)\n\ntypedef struct {\n    uint8_t data[ELECTRA_DECODED_DATA_SIZE];\n\n    ElectraDecodedData encoded_base_data;\n    ElectraDecodedData encoded_epilogue;\n\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n\n    ManchesterState decoder_manchester_state;\n} ProtocolElectra;\n\nProtocolElectra* protocol_electra_alloc(void) {\n    ProtocolElectra* proto = malloc(sizeof(ProtocolElectra));\n    return (void*)proto;\n}\n\nvoid protocol_electra_free(ProtocolElectra* proto) {\n    free(proto);\n}\n\nuint8_t* protocol_electra_get_data(ProtocolElectra* proto) {\n    return proto->data;\n}\n\nstatic void electra_decode(\n    const uint8_t* encoded_base_data,\n    const uint8_t encoded_base_data_size,\n    const uint8_t* encoded_epilogue,\n    const uint8_t encoded_epilogue_size,\n    uint8_t* decoded_data,\n    const uint8_t decoded_data_size) {\n    furi_check(decoded_data_size >= ELECTRA_DECODED_DATA_SIZE);\n    furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);\n    furi_check(encoded_epilogue_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);\n\n    uint8_t decoded_data_index = 0;\n    ElectraDecodedData base_data = *((ElectraDecodedData*)(encoded_base_data));\n    //ElectraDecodedData epilogue = *((ElectraDecodedData*)(encoded_epilogue));\n\n    // clean result\n    memset(decoded_data, 0, decoded_data_size);\n\n    // header\n    for(uint8_t i = 0; i < 9; i++) {\n        base_data = base_data << 1;\n    }\n\n    // nibbles\n    uint8_t value = 0;\n    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {\n        uint8_t nibble = 0;\n        for(uint8_t i = 0; i < 5; i++) {\n            if(i < 4) nibble = (nibble << 1) | (base_data & (1LLU << 63) ? 1 : 0);\n            base_data = base_data << 1;\n        }\n        value = (value << 4) | nibble;\n        if(r % 2) {\n            decoded_data[decoded_data_index] |= value;\n            decoded_data_index++;\n            value = 0;\n        }\n    }\n\n    // copy first 3 bytes of encoded epilogue to decoded data\n    decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS] =\n        encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 1];\n    decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1] =\n        encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 2];\n    decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2] =\n        encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 3];\n}\n\nstatic bool electra_can_be_decoded(\n    const uint8_t* encoded_base_data,\n    const uint8_t encoded_base_data_size,\n    const uint8_t* encoded_epilogue_data,\n    const uint8_t encoded_epilogue_data_size) {\n    furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);\n    furi_check(encoded_epilogue_data_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);\n    const ElectraDecodedData* base_data = (ElectraDecodedData*)encoded_base_data;\n    const ElectraDecodedData* epilogue = (ElectraDecodedData*)encoded_epilogue_data;\n\n    // check electra epilogue. if em4100 header - break\n    if((*epilogue & EM_ENCODED_DATA_HEADER) == EM_ENCODED_DATA_HEADER) return false;\n\n    // check header and stop bit\n    if((*base_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;\n\n    // check row parity\n    for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {\n        uint8_t parity_sum = 0;\n\n        for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {\n            parity_sum += (*base_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;\n        }\n\n        if(parity_sum % 2) {\n            return false;\n        }\n    }\n\n    // check columns parity\n    for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {\n        uint8_t parity_sum = 0;\n\n        for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {\n            parity_sum += (*base_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;\n        }\n\n        if(parity_sum % 2) {\n            FURI_LOG_D(\n                TAG,\n                \"Unexpected column parity found. EM4100 data: %016llX\",\n                bit_lib_bytes_to_num_be(encoded_base_data, encoded_base_data_size));\n            return false;\n        }\n    }\n\n    // encoded_epilogue_data lsb encoded\n    uint8_t epilogue_filler = encoded_epilogue_data[(ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2];\n\n    for(uint8_t i = 0; i < ((ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2); i++)\n        if(encoded_epilogue_data[i] != epilogue_filler) {\n            FURI_LOG_D(TAG, \"Unexpected epilogue filler found: %016llX\", *epilogue);\n            return false;\n        }\n\n    return true;\n}\n\nvoid protocol_electra_decoder_start(ProtocolElectra* proto) {\n    memset(proto->data, 0, ELECTRA_DECODED_DATA_SIZE);\n    proto->encoded_base_data = 0;\n    proto->encoded_epilogue = 0;\n\n    manchester_advance(\n        proto->decoder_manchester_state,\n        ManchesterEventReset,\n        &proto->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_electra_decoder_feed(ProtocolElectra* proto, bool level, uint32_t duration) {\n    bool result = false;\n\n    ManchesterEvent event = ManchesterEventReset;\n\n    if(duration > ELECTRA_READ_SHORT_TIME_LOW && duration < ELECTRA_READ_SHORT_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(duration > ELECTRA_READ_LONG_TIME_LOW && duration < ELECTRA_READ_LONG_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);\n\n        if(data_ok) {\n            /*\n                EM 4100 BASE DATA (64 bit)         ELECTRA EPILOGUE (64 bit)\n            _________________________________  _________________________________\n            | | | | | | | | | | | | | | | | |  | | | | | | | | | | | | | | | | |    <- new data bit\n            ---------------------------------  --------------------------------- \n                                             <- epilogue msb is carry bit to base data  \n            */\n            bool carry = proto->encoded_epilogue >> 63 & 0b1;\n\n            proto->encoded_base_data = (proto->encoded_base_data << 1) | carry;\n            proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;\n\n            if(electra_can_be_decoded(\n                   (uint8_t*)&proto->encoded_base_data,\n                   ELECTRA_ENCODED_BASE_DATA_SIZE,\n                   (uint8_t*)&proto->encoded_epilogue,\n                   ELECTRA_ENCODED_EPILOGUE_SIZE)) {\n                electra_decode(\n                    (uint8_t*)&proto->encoded_base_data,\n                    ELECTRA_ENCODED_BASE_DATA_SIZE,\n                    (uint8_t*)&proto->encoded_epilogue,\n                    ELECTRA_ENCODED_EPILOGUE_SIZE,\n                    proto->data,\n                    ELECTRA_DECODED_DATA_SIZE);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void em_write_nibble(bool low_nibble, uint8_t data, ElectraDecodedData* encoded_base_data) {\n    uint8_t parity_sum = 0;\n    uint8_t start = 0;\n    if(!low_nibble) start = 4;\n\n    for(int8_t i = (start + 3); i >= start; i--) {\n        parity_sum += (data >> i) & 1;\n        *encoded_base_data = (*encoded_base_data << 1) | ((data >> i) & 1);\n    }\n\n    *encoded_base_data = (*encoded_base_data << 1) | ((parity_sum % 2) & 1);\n}\n\nbool protocol_electra_encoder_start(ProtocolElectra* proto) {\n    // header\n    proto->encoded_base_data = 0b111111111;\n\n    // data\n    for(uint8_t i = 0; i < ELECTRA_DECODED_BASE_DATA_SIZE; i++) {\n        em_write_nibble(false, proto->data[i], &proto->encoded_base_data);\n        em_write_nibble(true, proto->data[i], &proto->encoded_base_data);\n    }\n\n    // column parity and stop bit\n    uint8_t parity_sum;\n\n    for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {\n        parity_sum = 0;\n        for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {\n            uint8_t parity_bit = (proto->encoded_base_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;\n            parity_sum += parity_bit;\n        }\n        proto->encoded_base_data = (proto->encoded_base_data << 1) | ((parity_sum % 2) & 1);\n    }\n\n    // stop bit\n    proto->encoded_base_data = (proto->encoded_base_data << 1) | 0;\n\n    proto->encoded_data_index = 0;\n    proto->encoded_polarity = true;\n\n    // epilogue\n    proto->encoded_epilogue = (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS]);\n    proto->encoded_epilogue <<= 8;\n    proto->encoded_epilogue |= (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1]);\n\n    //fill bytes 2-7 by epilogue filler\n    for(uint8_t i = 2; i < ELECTRA_ENCODED_EPILOGUE_SIZE; i++) {\n        proto->encoded_epilogue <<= 8;\n        proto->encoded_epilogue |= proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2];\n    }\n\n    return true;\n}\n\nLevelDuration protocol_electra_encoder_yield(ProtocolElectra* proto) {\n    bool level;\n    if(proto->encoded_data_index < 64)\n        level = (proto->encoded_base_data >> (63 - proto->encoded_data_index)) & 1;\n    else\n        level = (proto->encoded_epilogue >> (63 - (proto->encoded_data_index - 64))) & 1;\n\n    uint32_t duration = ELECTRA_CLOCK_PER_BIT / 2;\n\n    if(proto->encoded_polarity) {\n        proto->encoded_polarity = false;\n    } else {\n        level = !level;\n\n        proto->encoded_polarity = true;\n        proto->encoded_data_index++;\n        if(proto->encoded_data_index >= 128) {\n            proto->encoded_data_index = 0;\n        }\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_electra_encoder_start(protocol);\n    electra_decode(\n        (uint8_t*)&protocol->encoded_base_data,\n        sizeof(ElectraDecodedData),\n        (uint8_t*)&protocol->encoded_epilogue,\n        sizeof(ElectraDecodedData),\n        protocol->data,\n        ELECTRA_DECODED_DATA_SIZE);\n\n    protocol_electra_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] =\n            (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 |\n             (4 << LFRFID_T5577_MAXBLOCK_SHIFT));\n        request->t5577.block[1] = protocol->encoded_base_data >> 32;\n        request->t5577.block[2] = protocol->encoded_base_data & 0xFFFFFFFF;\n        request->t5577.block[3] = protocol->encoded_epilogue >> 32;\n        request->t5577.block[4] = protocol->encoded_epilogue & 0xFFFFFFFF;\n        request->t5577.blocks_to_write = 5;\n        result = true;\n    }\n    if(request->write_type == LFRFIDWriteTypeEM4305) {\n        request->em4305.word[4] =\n            (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(64) | (8 << EM4x05_MAXBLOCK_SHIFT));\n        uint64_t encoded_data_reversed = 0;\n        uint64_t encoded_epilogue_reversed = 0;\n        for(uint8_t i = 0; i < 64; i++) {\n            encoded_data_reversed = (encoded_data_reversed << 1) |\n                                    ((protocol->encoded_base_data >> i) & 1);\n            encoded_epilogue_reversed = (encoded_epilogue_reversed << 1) |\n                                        ((protocol->encoded_epilogue >> i) & 1);\n        }\n        request->em4305.word[5] = encoded_data_reversed & 0xFFFFFFFF;\n        request->em4305.word[6] = encoded_data_reversed >> 32;\n        request->em4305.word[7] = encoded_epilogue_reversed & 0xFFFFFFFF;\n        request->em4305.word[8] = encoded_epilogue_reversed >> 32;\n        request->em4305.mask = 0x01F0;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) {\n    protocol_electra_encoder_start(protocol);\n    furi_string_printf(result, \"Epilogue: %016llX\", protocol->encoded_epilogue);\n}\n\nconst ProtocolBase protocol_electra = {\n    .name = \"Electra\",\n    .manufacturer = \"Electra Group\",\n    .data_size = ELECTRA_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK | LFRFIDFeaturePSK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_electra_alloc,\n    .free = (ProtocolFree)protocol_electra_free,\n    .get_data = (ProtocolGetData)protocol_electra_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_electra_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_electra_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_electra_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_electra_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_electra_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_electra_render_data,\n    .write_data = (ProtocolWriteData)protocol_electra_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_electra.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_electra;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_em4100.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/manchester_decoder.h>\n#include \"lfrfid_protocols.h\"\n\ntypedef uint64_t EM4100DecodedData;\ntypedef uint64_t EM4100Epilogue;\n\n#define EM_HEADER_POS  (55)\n#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)\n\n#define EM_FIRST_ROW_POS (50)\n\n#define EM_ROW_COUNT          (10)\n#define EM_COLUMN_COUNT       (4)\n#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)\n\n#define EM_COLUMN_POS (4)\n#define EM_STOP_POS   (0)\n#define EM_STOP_MASK  (0x1LLU << EM_STOP_POS)\n\n#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)\n#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)\n\n#define EM4100_DECODED_DATA_SIZE (5)\n#define EM4100_ENCODED_DATA_SIZE (sizeof(EM4100DecodedData))\n\n#define EM_READ_SHORT_TIME_BASE  (256)\n#define EM_READ_LONG_TIME_BASE   (512)\n#define EM_READ_JITTER_TIME_BASE (100)\n\n#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)\n\ntypedef struct {\n    uint8_t data[EM4100_DECODED_DATA_SIZE];\n\n    EM4100DecodedData encoded_data;\n    EM4100Epilogue encoded_epilogue;\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n\n    ManchesterState decoder_manchester_state;\n    uint8_t clock_per_bit;\n} ProtocolEM4100;\n\nuint16_t protocol_em4100_get_time_divisor(ProtocolEM4100* proto) {\n    switch(proto->clock_per_bit) {\n    case 64:\n        return 1;\n    case 32:\n        return 2;\n    case 16:\n        return 4;\n    default:\n        return 1;\n    }\n}\n\nuint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100* proto) {\n    switch(proto->clock_per_bit) {\n    case 64:\n        return LFRFID_T5577_BITRATE_RF_64;\n    case 32:\n        return LFRFID_T5577_BITRATE_RF_32;\n    case 16:\n        return LFRFID_T5577_BITRATE_RF_16;\n    default:\n        return LFRFID_T5577_BITRATE_RF_64;\n    }\n}\n\nuint32_t protocol_em4100_get_em4305_bitrate(ProtocolEM4100* proto) {\n    switch(proto->clock_per_bit) {\n    case 64:\n        return EM4x05_SET_BITRATE(64);\n    case 32:\n        return EM4x05_SET_BITRATE(32);\n    case 16:\n        return EM4x05_SET_BITRATE(16);\n    default:\n        return EM4x05_SET_BITRATE(64);\n    }\n}\n\nuint16_t protocol_em4100_get_short_time_low(ProtocolEM4100* proto) {\n    return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) -\n           EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto);\n}\n\nuint16_t protocol_em4100_get_short_time_high(ProtocolEM4100* proto) {\n    return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) +\n           EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto);\n}\n\nuint16_t protocol_em4100_get_long_time_low(ProtocolEM4100* proto) {\n    return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) -\n           EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto);\n}\n\nuint16_t protocol_em4100_get_long_time_high(ProtocolEM4100* proto) {\n    return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) +\n           EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto);\n}\n\nProtocolEM4100* protocol_em4100_alloc(void) {\n    ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100));\n    proto->clock_per_bit = 64;\n    return (void*)proto;\n}\n\nProtocolEM4100* protocol_em4100_16_alloc(void) {\n    ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100));\n    proto->clock_per_bit = 16;\n    return (void*)proto;\n}\n\nProtocolEM4100* protocol_em4100_32_alloc(void) {\n    ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100));\n    proto->clock_per_bit = 32;\n    return (void*)proto;\n}\n\nvoid protocol_em4100_free(ProtocolEM4100* proto) {\n    free(proto);\n}\n\nuint8_t* protocol_em4100_get_data(ProtocolEM4100* proto) {\n    return proto->data;\n}\n\nstatic void em4100_decode(\n    const uint8_t* encoded_data,\n    const uint8_t encoded_data_size,\n    uint8_t* decoded_data,\n    const uint8_t decoded_data_size) {\n    furi_check(decoded_data_size >= EM4100_DECODED_DATA_SIZE);\n    furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);\n\n    uint8_t decoded_data_index = 0;\n    EM4100DecodedData card_data = *((EM4100DecodedData*)(encoded_data));\n\n    // clean result\n    memset(decoded_data, 0, decoded_data_size);\n\n    // header\n    for(uint8_t i = 0; i < 9; i++) {\n        card_data = card_data << 1;\n    }\n\n    // nibbles\n    uint8_t value = 0;\n    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {\n        uint8_t nibble = 0;\n        for(uint8_t i = 0; i < 5; i++) {\n            if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);\n            card_data = card_data << 1;\n        }\n        value = (value << 4) | nibble;\n        if(r % 2) {\n            decoded_data[decoded_data_index] |= value;\n            decoded_data_index++;\n            value = 0;\n        }\n    }\n}\n\nstatic bool em4100_can_be_decoded(\n    const uint8_t* encoded_data,\n    const uint8_t encoded_data_size,\n    const uint8_t* encoded_epilogue) {\n    furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);\n    const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data;\n    const EM4100Epilogue* epilogue = (EM4100Epilogue*)encoded_epilogue;\n\n    // check first 9 bytes on epilogue (to prevent conflict with Electra protocol)\n    if((*epilogue & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) return false;\n\n    // check header and stop bit\n    if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;\n\n    // check row parity\n    for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {\n        uint8_t parity_sum = 0;\n\n        for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {\n            parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;\n        }\n\n        if(parity_sum % 2) {\n            return false;\n        }\n    }\n\n    // check columns parity\n    for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {\n        uint8_t parity_sum = 0;\n\n        for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {\n            parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;\n        }\n\n        if(parity_sum % 2) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nvoid protocol_em4100_decoder_start(ProtocolEM4100* proto) {\n    memset(proto->data, 0, EM4100_DECODED_DATA_SIZE);\n    proto->encoded_data = 0;\n    manchester_advance(\n        proto->decoder_manchester_state,\n        ManchesterEventReset,\n        &proto->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t duration) {\n    bool result = false;\n\n    ManchesterEvent event = ManchesterEventReset;\n\n    if(duration > protocol_em4100_get_short_time_low(proto) &&\n       duration < protocol_em4100_get_short_time_high(proto)) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(\n        duration > protocol_em4100_get_long_time_low(proto) &&\n        duration < protocol_em4100_get_long_time_high(proto)) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);\n\n        if(data_ok) {\n            bool carry = proto->encoded_epilogue >> 63 & 0b1;\n\n            proto->encoded_data = (proto->encoded_data << 1) | carry;\n            proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;\n\n            if(em4100_can_be_decoded(\n                   (uint8_t*)&proto->encoded_data,\n                   sizeof(EM4100DecodedData),\n                   (uint8_t*)&proto->encoded_epilogue)) {\n                em4100_decode(\n                    (uint8_t*)&proto->encoded_data,\n                    sizeof(EM4100DecodedData),\n                    proto->data,\n                    EM4100_DECODED_DATA_SIZE);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void em4100_write_nibble(bool low_nibble, uint8_t data, EM4100DecodedData* encoded_data) {\n    uint8_t parity_sum = 0;\n    uint8_t start = 0;\n    if(!low_nibble) start = 4;\n\n    for(int8_t i = (start + 3); i >= start; i--) {\n        parity_sum += (data >> i) & 1;\n        *encoded_data = (*encoded_data << 1) | ((data >> i) & 1);\n    }\n\n    *encoded_data = (*encoded_data << 1) | ((parity_sum % 2) & 1);\n}\n\nbool protocol_em4100_encoder_start(ProtocolEM4100* proto) {\n    // header\n    proto->encoded_data = 0b111111111;\n\n    // data\n    for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) {\n        em4100_write_nibble(false, proto->data[i], &proto->encoded_data);\n        em4100_write_nibble(true, proto->data[i], &proto->encoded_data);\n    }\n\n    // column parity and stop bit\n    uint8_t parity_sum;\n\n    for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {\n        parity_sum = 0;\n        for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {\n            uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;\n            parity_sum += parity_bit;\n        }\n        proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1);\n    }\n\n    // stop bit\n    proto->encoded_data = (proto->encoded_data << 1) | 0;\n\n    proto->encoded_data_index = 0;\n    proto->encoded_polarity = true;\n\n    return true;\n}\n\nLevelDuration protocol_em4100_encoder_yield(ProtocolEM4100* proto) {\n    bool level = (proto->encoded_data >> (63 - proto->encoded_data_index)) & 1;\n    uint32_t duration = proto->clock_per_bit / 2;\n\n    if(proto->encoded_polarity) {\n        proto->encoded_polarity = false;\n    } else {\n        level = !level;\n\n        proto->encoded_polarity = true;\n        proto->encoded_data_index++;\n        if(proto->encoded_data_index >= 64) {\n            proto->encoded_data_index = 0;\n        }\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_em4100_encoder_start(protocol);\n    em4100_decode(\n        (uint8_t*)&protocol->encoded_data,\n        sizeof(EM4100DecodedData),\n        protocol->data,\n        EM4100_DECODED_DATA_SIZE);\n\n    protocol_em4100_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] =\n            (LFRFID_T5577_MODULATION_MANCHESTER | protocol_em4100_get_t5577_bitrate(protocol) |\n             (2 << LFRFID_T5577_MAXBLOCK_SHIFT));\n        request->t5577.block[1] = protocol->encoded_data >> 32;\n        request->t5577.block[2] = protocol->encoded_data;\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    } else if(request->write_type == LFRFIDWriteTypeEM4305) {\n        request->em4305.word[4] =\n            (EM4x05_MODULATION_MANCHESTER | protocol_em4100_get_em4305_bitrate(protocol) |\n             (6 << EM4x05_MAXBLOCK_SHIFT));\n        uint64_t encoded_data_reversed = 0;\n        for(uint8_t i = 0; i < 64; i++) {\n            encoded_data_reversed = (encoded_data_reversed << 1) |\n                                    ((protocol->encoded_data >> i) & 1);\n        }\n        request->em4305.word[5] = encoded_data_reversed;\n        request->em4305.word[6] = encoded_data_reversed >> 32;\n        request->em4305.mask = 0x70;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) {\n    uint8_t* data = protocol->data;\n    furi_string_printf(\n        result,\n        \"FC: %03u\\n\"\n        \"Card: %05hu (RF/%hhu)\",\n        data[2],\n        (uint16_t)((data[3] << 8) | (data[4])),\n        protocol->clock_per_bit);\n}\n\nconst ProtocolBase protocol_em4100 = {\n    .name = \"EM4100\",\n    .manufacturer = \"EM-Micro\",\n    .data_size = EM4100_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK | LFRFIDFeaturePSK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_em4100_alloc,\n    .free = (ProtocolFree)protocol_em4100_free,\n    .get_data = (ProtocolGetData)protocol_em4100_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_em4100_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_em4100_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .write_data = (ProtocolWriteData)protocol_em4100_write_data,\n};\n\nconst ProtocolBase protocol_em4100_32 = {\n    .name = \"EM4100/32\",\n    .manufacturer = \"EM-Micro\",\n    .data_size = EM4100_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK | LFRFIDFeaturePSK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_em4100_32_alloc,\n    .free = (ProtocolFree)protocol_em4100_free,\n    .get_data = (ProtocolGetData)protocol_em4100_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_em4100_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_em4100_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .write_data = (ProtocolWriteData)protocol_em4100_write_data,\n};\n\nconst ProtocolBase protocol_em4100_16 = {\n    .name = \"EM4100/16\",\n    .manufacturer = \"EM-Micro\",\n    .data_size = EM4100_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK | LFRFIDFeaturePSK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_em4100_16_alloc,\n    .free = (ProtocolFree)protocol_em4100_free,\n    .get_data = (ProtocolGetData)protocol_em4100_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_em4100_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_em4100_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,\n    .write_data = (ProtocolWriteData)protocol_em4100_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_em4100.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_em4100;\n\nextern const ProtocolBase protocol_em4100_32;\n\nextern const ProtocolBase protocol_em4100_16;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_fdx_a.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include \"lfrfid_protocols.h\"\n#include <bit_lib/bit_lib.h>\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define FDXA_DATA_SIZE     10\n#define FDXA_PREAMBLE_SIZE 2\n\n#define FDXA_ENCODED_DATA_SIZE (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE + FDXA_PREAMBLE_SIZE)\n#define FDXA_ENCODED_BIT_SIZE  ((FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE) * 8)\n#define FDXA_DECODED_DATA_SIZE (5)\n#define FDXA_DECODED_BIT_SIZE  ((FDXA_ENCODED_BIT_SIZE - FDXA_PREAMBLE_SIZE * 8) / 2)\n\n#define FDXA_PREAMBLE_0 0x55\n#define FDXA_PREAMBLE_1 0x1D\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolFDXADecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n    uint32_t pulse;\n} ProtocolFDXAEncoder;\n\ntypedef struct {\n    ProtocolFDXADecoder decoder;\n    ProtocolFDXAEncoder encoder;\n    uint8_t encoded_data[FDXA_ENCODED_DATA_SIZE];\n    uint8_t data[FDXA_DECODED_DATA_SIZE];\n    size_t protocol_size;\n} ProtocolFDXA;\n\nProtocolFDXA* protocol_fdx_a_alloc(void) {\n    ProtocolFDXA* protocol = malloc(sizeof(ProtocolFDXA));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_fdx_a_free(ProtocolFDXA* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_fdx_a_get_data(ProtocolFDXA* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_fdx_a_decoder_start(ProtocolFDXA* protocol) {\n    memset(protocol->encoded_data, 0, FDXA_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_fdx_a_decode(const uint8_t* from, uint8_t* to) {\n    size_t bit_index = 0;\n    for(size_t i = FDXA_PREAMBLE_SIZE; i < (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE); i++) {\n        for(size_t n = 0; n < 4; n++) {\n            uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;\n            if(bit_pair == 0b01) {\n                bit_lib_set_bit(to, bit_index, 0);\n            } else if(bit_pair == 0b10) {\n                bit_lib_set_bit(to, bit_index, 1);\n            } else {\n                return false;\n            }\n            bit_index++;\n        }\n    }\n\n    return true;\n}\n\nstatic void protocol_fdx_a_fix_parity(ProtocolFDXA* protocol) {\n    for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) {\n        if(bit_lib_test_parity_32(protocol->data[i], BitLibParityOdd)) {\n            protocol->data[i] ^= (1 << 7);\n        }\n    }\n}\n\nstatic bool protocol_fdx_a_can_be_decoded(const uint8_t* data) {\n    // check preamble\n    if(data[0] != FDXA_PREAMBLE_0 || data[1] != FDXA_PREAMBLE_1 || data[12] != FDXA_PREAMBLE_0 ||\n       data[13] != FDXA_PREAMBLE_1) {\n        return false;\n    }\n\n    // check for manchester encoding\n    uint8_t decoded_data[FDXA_DECODED_DATA_SIZE];\n    if(!protocol_fdx_a_decode(data, decoded_data)) return false;\n\n    uint8_t parity_sum = 0;\n    for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) {\n        parity_sum += bit_lib_test_parity_32(decoded_data[i], BitLibParityOdd);\n        decoded_data[i] &= 0x7F;\n    }\n\n    return parity_sum == 0;\n}\n\nbool protocol_fdx_a_decoder_feed(ProtocolFDXA* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, FDXA_ENCODED_DATA_SIZE, value);\n            if(protocol_fdx_a_can_be_decoded(protocol->encoded_data)) {\n                protocol_fdx_a_decode(protocol->encoded_data, protocol->data);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_fdx_a_encode(ProtocolFDXA* protocol) {\n    protocol->encoded_data[0] = FDXA_PREAMBLE_0;\n    protocol->encoded_data[1] = FDXA_PREAMBLE_1;\n\n    size_t bit_index = 0;\n    for(size_t i = 0; i < FDXA_DECODED_BIT_SIZE; i++) {\n        bool bit = bit_lib_get_bit(protocol->data, i);\n        if(bit) {\n            bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 1);\n            bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 0);\n        } else {\n            bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 0);\n            bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 1);\n        }\n        bit_index += 2;\n    }\n}\n\nbool protocol_fdx_a_encoder_start(ProtocolFDXA* protocol) {\n    protocol->encoder.encoded_index = 0;\n    protocol->encoder.pulse = 0;\n    protocol_fdx_a_encode(protocol);\n\n    return true;\n}\n\nLevelDuration protocol_fdx_a_encoder_yield(ProtocolFDXA* protocol) {\n    bool level = 0;\n    uint32_t duration = 0;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(protocol->encoder.pulse == 0) {\n        // get bit\n        uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n\n        // get pulse from oscillator\n        bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);\n\n        if(advance) {\n            bit_lib_increment_index(protocol->encoder.encoded_index, FDXA_ENCODED_BIT_SIZE);\n        }\n\n        // duration diveded by 2 because we need to output high and low\n        duration = duration / 2;\n        protocol->encoder.pulse = duration;\n        level = true;\n    } else {\n        // output low half and reset pulse\n        duration = protocol->encoder.pulse;\n        protocol->encoder.pulse = 0;\n        level = false;\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_fdx_a_write_data(ProtocolFDXA* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_fdx_a_fix_parity(protocol);\n    protocol_fdx_a_encoder_start(protocol);\n    protocol_fdx_a_decode(protocol->encoded_data, protocol->data);\n\n    protocol_fdx_a_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_fdx_a_render_data(ProtocolFDXA* protocol, FuriString* result) {\n    uint8_t data[FDXA_DECODED_DATA_SIZE];\n    memcpy(data, protocol->data, FDXA_DECODED_DATA_SIZE);\n\n    uint8_t parity_sum = 0;\n    for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) {\n        parity_sum += bit_lib_test_parity_32(data[i], BitLibParityOdd);\n        data[i] &= 0x7F;\n    }\n\n    furi_string_printf(\n        result,\n        \"ID: %010llX\\n\"\n        \"Parity: %c\",\n        bit_lib_get_bits_64(data, 0, 40),\n        parity_sum == 0 ? '+' : '-');\n}\n\nconst ProtocolBase protocol_fdx_a = {\n    .name = \"FDX-A\",\n    .manufacturer = \"FECAVA\",\n    .data_size = FDXA_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_fdx_a_alloc,\n    .free = (ProtocolFree)protocol_fdx_a_free,\n    .get_data = (ProtocolGetData)protocol_fdx_a_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_fdx_a_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_fdx_a_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_fdx_a_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_fdx_a_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_fdx_a_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_fdx_a_render_data,\n    .write_data = (ProtocolWriteData)protocol_fdx_a_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_fdx_a.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_fdx_a;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_fdx_b.c",
    "content": "#include <furi.h>\n#include \"toolbox/level_duration.h\"\n#include \"protocol_fdx_b.h\"\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n#include <furi_hal_rtc.h>\n#include <tools/iso_3166.h>\n\n#define FDX_B_ENCODED_BIT_SIZE       (128)\n#define FDX_B_ENCODED_BYTE_SIZE      (((FDX_B_ENCODED_BIT_SIZE) / 8))\n#define FDX_B_PREAMBLE_BIT_SIZE      (11)\n#define FDX_B_PREAMBLE_BYTE_SIZE     (2)\n#define FDX_B_ENCODED_BYTE_FULL_SIZE (FDX_B_ENCODED_BYTE_SIZE + FDX_B_PREAMBLE_BYTE_SIZE)\n\n#define FDXB_DECODED_DATA_SIZE (11)\n\n#define FDX_B_SHORT_TIME  (128)\n#define FDX_B_LONG_TIME   (256)\n#define FDX_B_JITTER_TIME (60)\n\n#define FDX_B_SHORT_TIME_LOW  (FDX_B_SHORT_TIME - FDX_B_JITTER_TIME)\n#define FDX_B_SHORT_TIME_HIGH (FDX_B_SHORT_TIME + FDX_B_JITTER_TIME)\n#define FDX_B_LONG_TIME_LOW   (FDX_B_LONG_TIME - FDX_B_JITTER_TIME)\n#define FDX_B_LONG_TIME_HIGH  (FDX_B_LONG_TIME + FDX_B_JITTER_TIME)\n\ntypedef struct {\n    bool last_short;\n    bool last_level;\n    size_t encoded_index;\n    uint8_t encoded_data[FDX_B_ENCODED_BYTE_FULL_SIZE];\n    uint8_t data[FDXB_DECODED_DATA_SIZE];\n} ProtocolFDXB;\n\nProtocolFDXB* protocol_fdx_b_alloc(void) {\n    ProtocolFDXB* protocol = malloc(sizeof(ProtocolFDXB));\n    return protocol;\n}\n\nvoid protocol_fdx_b_free(ProtocolFDXB* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_fdx_b_get_data(ProtocolFDXB* proto) {\n    return proto->data;\n}\n\nvoid protocol_fdx_b_decoder_start(ProtocolFDXB* protocol) {\n    memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE);\n    protocol->last_short = false;\n}\n\nstatic bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) {\n    bool result = false;\n\n    /*\n    msb\t\tlsb\n    0   10000000000\t  Header pattern. 11 bits.\n    11    1nnnnnnnn\t\n    20    1nnnnnnnn\t  38 bit (12 digit) National code.\n    29    1nnnnnnnn\t  eg. 000000001008 (decimal).\n    38    1nnnnnnnn\t\n    47    1nnnnnncc\t  10 bit (3 digit) Country code.\n    56    1cccccccc\t  eg. 999 (decimal).\n    65    1s-------\t  1 bit data block status flag.\n    74    1-------a\t  1 bit animal application indicator.\n    83    1xxxxxxxx\t  16 bit checksum.\n    92    1xxxxxxxx\t\n    101   1eeeeeeee\t  24 bits of extra data if present.\n    110   1eeeeeeee\t  eg. $123456.\n    119   1eeeeeeee\t\n    */\n\n    do {\n        // check 11 bits preamble\n        if(bit_lib_get_bits_16(protocol->encoded_data, 0, 11) != 0b10000000000) break;\n        // check next 11 bits preamble\n        if(bit_lib_get_bits_16(protocol->encoded_data, 128, 11) != 0b10000000000) break;\n        // check control bits\n        if(!bit_lib_test_parity(protocol->encoded_data, 3, 13 * 9, BitLibParityAlways1, 9)) break;\n\n        // compute checksum\n        uint8_t crc_data[8];\n        for(size_t i = 0; i < 8; i++) {\n            bit_lib_copy_bits(crc_data, i * 8, 8, protocol->encoded_data, 12 + 9 * i);\n        }\n        uint16_t crc_res = bit_lib_crc16(crc_data, 8, 0x1021, 0x0000, false, false, 0x0000);\n\n        // read checksum\n        uint16_t crc_ex = 0;\n        bit_lib_copy_bits((uint8_t*)&crc_ex, 8, 8, protocol->encoded_data, 84);\n        bit_lib_copy_bits((uint8_t*)&crc_ex, 0, 8, protocol->encoded_data, 93);\n\n        // compare checksum\n        if(crc_res != crc_ex) break;\n\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nvoid protocol_fdx_b_decode(ProtocolFDXB* protocol) {\n    // remove parity\n    bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 14 * 9, 9);\n\n    // remove header pattern\n    for(size_t i = 0; i < 11; i++)\n        bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, 0);\n\n    // 0  nnnnnnnn\n    // 8  nnnnnnnn\t  38 bit (12 digit) National code.\n    // 16 nnnnnnnn\t  eg. 000000001008 (decimal).\n    // 24 nnnnnnnn\n    // 32 nnnnnncc\t  10 bit (3 digit) Country code.\n    // 40 cccccccc\t  eg. 999 (decimal).\n    // 48 s-------\t  1 bit data block status flag.\n    // 56 -------a\t  1 bit animal application indicator.\n    // 64 xxxxxxxx\t  16 bit checksum.\n    // 72 xxxxxxxx\n    // 80 eeeeeeee\t  24 bits of extra data if present.\n    // 88 eeeeeeee\t  eg. $123456.\n    // 96 eeeeeeee\n\n    // copy data without checksum\n    bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0);\n    bit_lib_copy_bits(protocol->data, 64, 24, protocol->encoded_data, 80);\n\n    // const BitLibRegion regions_encoded[] = {\n    //     {'n', 0, 38},\n    //     {'c', 38, 10},\n    //     {'b', 48, 16},\n    //     {'x', 64, 16},\n    //     {'e', 80, 24},\n    // };\n\n    // bit_lib_print_regions(regions_encoded, 5, protocol->encoded_data, FDX_B_ENCODED_BIT_SIZE);\n\n    // const BitLibRegion regions_decoded[] = {\n    //     {'n', 0, 38},\n    //     {'c', 38, 10},\n    //     {'b', 48, 16},\n    //     {'e', 64, 24},\n    // };\n\n    // bit_lib_print_regions(regions_decoded, 4, protocol->data, FDXB_DECODED_DATA_SIZE * 8);\n}\n\nbool protocol_fdx_b_decoder_feed(ProtocolFDXB* protocol, bool level, uint32_t duration) {\n    bool result = false;\n    UNUSED(level);\n\n    bool pushed = false;\n\n    // Bi-Phase Manchester decoding\n    if(duration >= FDX_B_SHORT_TIME_LOW && duration <= FDX_B_SHORT_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            protocol->last_short = true;\n        } else {\n            pushed = true;\n            bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, false);\n            protocol->last_short = false;\n        }\n    } else if(duration >= FDX_B_LONG_TIME_LOW && duration <= FDX_B_LONG_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            pushed = true;\n            bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, true);\n        } else {\n            // reset\n            protocol->last_short = false;\n        }\n    } else {\n        // reset\n        protocol->last_short = false;\n    }\n\n    if(pushed && protocol_fdx_b_can_be_decoded(protocol)) {\n        protocol_fdx_b_decode(protocol);\n        result = true;\n    }\n\n    return result;\n}\n\nbool protocol_fdx_b_encoder_start(ProtocolFDXB* protocol) {\n    memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE);\n    bit_lib_set_bit(protocol->encoded_data, 0, 1);\n    for(size_t i = 0; i < 13; i++) {\n        bit_lib_set_bit(protocol->encoded_data, 11 + 9 * i, 1);\n        if(i == 8 || i == 9) continue;\n\n        if(i < 8) {\n            bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, i * 8);\n        } else {\n            bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, (i - 2) * 8);\n        }\n    }\n\n    uint16_t crc_res = bit_lib_crc16(protocol->data, 8, 0x1021, 0x0000, false, false, 0x0000);\n    bit_lib_copy_bits(protocol->encoded_data, 84, 8, (uint8_t*)&crc_res, 8);\n    bit_lib_copy_bits(protocol->encoded_data, 93, 8, (uint8_t*)&crc_res, 0);\n\n    protocol->encoded_index = 0;\n    protocol->last_short = false;\n    protocol->last_level = false;\n    return true;\n}\n\nLevelDuration protocol_fdx_b_encoder_yield(ProtocolFDXB* protocol) {\n    uint32_t duration;\n    protocol->last_level = !protocol->last_level;\n\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index);\n\n    // Bi-Phase Manchester encoder\n    if(bit) {\n        // one long pulse for 1\n        duration = FDX_B_LONG_TIME / 8;\n        bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE);\n    } else {\n        // two short pulses for 0\n        duration = FDX_B_SHORT_TIME / 8;\n        if(protocol->last_short) {\n            bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE);\n            protocol->last_short = false;\n        } else {\n            protocol->last_short = true;\n        }\n    }\n\n    return level_duration_make(protocol->last_level, duration);\n}\n\n// 0  nnnnnnnn\n// 8  nnnnnnnn\t  38 bit (12 digit) National code.\n// 16 nnnnnnnn\t  eg. 000000001008 (decimal).\n// 24 nnnnnnnn\n// 32 nnnnnnnn\t  10 bit (3 digit) Country code.\n// 40 cccccccc\t  eg. 999 (decimal).\n// 48 s-------\t  1 bit data block status flag.\n// 56 -------a\t  1 bit animal application indicator.\n// 64 eeeeeeee\t  24 bits of extra data if present.\n// 72 eeeeeeee\t  eg. $123456.\n// 80 eeeeeeee\n\nstatic uint64_t protocol_fdx_b_get_national_code(const uint8_t* data) {\n    uint64_t national_code = bit_lib_get_bits_32(data, 0, 32);\n    national_code = national_code << 32;\n    national_code |= (uint64_t)bit_lib_get_bits_32(data, 32, 6) << (32 - 6);\n    bit_lib_reverse_bits((uint8_t*)&national_code, 0, 64);\n    return national_code;\n}\n\nstatic uint16_t protocol_fdx_b_get_country_code(const uint8_t* data) {\n    uint16_t country_code = bit_lib_get_bits_16(data, 38, 10) << 6;\n    bit_lib_reverse_bits((uint8_t*)&country_code, 0, 16);\n    return country_code;\n}\n\nstatic bool protocol_fdx_b_get_temp(const uint8_t* data, float* temp) {\n    uint32_t extended = bit_lib_get_bits_32(data, 64, 24) << 8;\n    bit_lib_reverse_bits((uint8_t*)&extended, 0, 32);\n\n    uint8_t ex_parity = (extended & 0x100) >> 8;\n    uint8_t ex_temperature = extended & 0xff;\n    uint8_t ex_calc_parity = bit_lib_test_parity_32(ex_temperature, BitLibParityOdd);\n    bool ex_temperature_present = (ex_calc_parity == ex_parity) && !(extended & 0xe00);\n\n    if(ex_temperature_present) {\n        float temperature_f = 74 + ex_temperature * 0.2;\n        *temp = temperature_f;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid protocol_fdx_b_render_data(ProtocolFDXB* protocol, FuriString* result) {\n    // 38 bits of national code\n    uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data);\n\n    // 10 bit of country code\n    uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data);\n\n    bool block_status = bit_lib_get_bit(protocol->data, 48);\n    bool rudi_bit = bit_lib_get_bit(protocol->data, 49);\n    uint8_t reserved = bit_lib_get_bits(protocol->data, 50, 5);\n    uint8_t user_info = bit_lib_get_bits(protocol->data, 55, 5);\n    uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3);\n    bool animal_flag = bit_lib_get_bit(protocol->data, 63);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriString* country_full_name = furi_string_alloc();\n    bool country_found = iso_3166_get_full_name(storage, country_code, country_full_name);\n    furi_record_close(RECORD_STORAGE);\n\n    furi_string_printf(\n        result,\n        \"ID: %03hu-%012llu\\n\"\n        \"Country Code: %hu\\n\"\n        \"Country: %s\\n\"\n        \"Temperature: \",\n        country_code,\n        national_code,\n        country_code,\n        (country_found) ? furi_string_get_cstr(country_full_name) : \"Unknown\");\n\n    float temperature;\n    if(protocol_fdx_b_get_temp(protocol->data, &temperature)) {\n        if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {\n            float temperature_c = (temperature - 32.0f) / 1.8f;\n            furi_string_cat_printf(result, \"%.2fC\", (double)temperature_c);\n        } else {\n            furi_string_cat_printf(result, \"%.2fF\", (double)temperature);\n        }\n    } else {\n        furi_string_cat(result, \"---\");\n    }\n\n    furi_string_cat_printf(\n        result,\n        \"\\n\"\n        \"Animal: %s\\n\"\n        \"Bits: %hhX-%hhX-%hhX-%hhX-%hhX\",\n        animal_flag ? \"Yes\" : \"No\",\n        block_status,\n        rudi_bit,\n        reserved,\n        user_info,\n        replacement_number);\n\n    furi_string_free(country_full_name);\n}\n\nvoid protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result) {\n    // 38 bits of national code\n    uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data);\n\n    // 10 bit of country code\n    uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FuriString* country_two_letter = furi_string_alloc();\n    bool country_found = iso_3166_get_two_letter(storage, country_code, country_two_letter);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_printf(\n        result,\n        \"ID: %03hu-%012llu\\n\"\n        \"Country: %hu %s; Temp.: \",\n        country_code,\n        national_code,\n        country_code,\n        (country_found) ? furi_string_get_cstr(country_two_letter) : \"Unknown\");\n\n    float temperature;\n    if(protocol_fdx_b_get_temp(protocol->data, &temperature)) {\n        if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {\n            float temperature_c = (temperature - 32.0f) / 1.8f;\n            furi_string_cat_printf(result, \"%.2fC\", (double)temperature_c);\n        } else {\n            furi_string_cat_printf(result, \"%.2fF\", (double)temperature);\n        }\n    } else {\n        furi_string_cat(result, \"---\");\n    }\n\n    furi_string_free(country_two_letter);\n}\n\nbool protocol_fdx_b_write_data(ProtocolFDXB* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_fdx_b_encoder_start(protocol);\n    protocol_fdx_b_decode(protocol);\n\n    protocol_fdx_b_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_32 |\n                                  (4 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);\n        request->t5577.blocks_to_write = 5;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_fdx_b = {\n    .name = \"FDX-B\",\n    .manufacturer = \"ISO\",\n    .data_size = FDXB_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_fdx_b_alloc,\n    .free = (ProtocolFree)protocol_fdx_b_free,\n    .get_data = (ProtocolGetData)protocol_fdx_b_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_fdx_b_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_fdx_b_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_fdx_b_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_fdx_b_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_fdx_b_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_fdx_b_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_fdx_b_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_fdx_b.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_fdx_b;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_gallagher.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define GALLAGHER_CLOCK_PER_BIT (32)\n\n#define GALLAGHER_ENCODED_BIT_SIZE   (96)\n#define GALLAGHER_ENCODED_BYTE_SIZE  ((GALLAGHER_ENCODED_BIT_SIZE) / 8)\n#define GALLAGHER_PREAMBLE_BIT_SIZE  (16)\n#define GALLAGHER_PREAMBLE_BYTE_SIZE ((GALLAGHER_PREAMBLE_BIT_SIZE) / 8)\n#define GALLAGHER_ENCODED_BYTE_FULL_SIZE \\\n    (GALLAGHER_ENCODED_BYTE_SIZE + GALLAGHER_PREAMBLE_BYTE_SIZE)\n#define GALLAGHER_DECODED_DATA_SIZE 8\n\n#define GALLAGHER_READ_SHORT_TIME  (128)\n#define GALLAGHER_READ_LONG_TIME   (256)\n#define GALLAGHER_READ_JITTER_TIME (60)\n\n#define GALLAGHER_READ_SHORT_TIME_LOW  (GALLAGHER_READ_SHORT_TIME - GALLAGHER_READ_JITTER_TIME)\n#define GALLAGHER_READ_SHORT_TIME_HIGH (GALLAGHER_READ_SHORT_TIME + GALLAGHER_READ_JITTER_TIME)\n#define GALLAGHER_READ_LONG_TIME_LOW   (GALLAGHER_READ_LONG_TIME - GALLAGHER_READ_JITTER_TIME)\n#define GALLAGHER_READ_LONG_TIME_HIGH  (GALLAGHER_READ_LONG_TIME + GALLAGHER_READ_JITTER_TIME)\n\ntypedef struct {\n    uint8_t data[GALLAGHER_DECODED_DATA_SIZE];\n    uint8_t encoded_data[GALLAGHER_ENCODED_BYTE_FULL_SIZE];\n\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n\n    ManchesterState decoder_manchester_state;\n} ProtocolGallagher;\n\nProtocolGallagher* protocol_gallagher_alloc(void) {\n    ProtocolGallagher* proto = malloc(sizeof(ProtocolGallagher));\n    return (void*)proto;\n}\n\nvoid protocol_gallagher_free(ProtocolGallagher* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_gallagher_get_data(ProtocolGallagher* protocol) {\n    return protocol->data;\n}\n\nstatic void protocol_gallagher_scramble(uint8_t* data, size_t length) {\n    const uint8_t lut[] = {\n        0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c,\n        0x05, 0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04,\n        0x51, 0x2e, 0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a,\n        0x6d, 0xa4, 0x00, 0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65,\n        0x57, 0x7c, 0x20, 0xfa, 0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18,\n        0xbe, 0x21, 0x72, 0x48, 0xb6, 0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53,\n        0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, 0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98,\n        0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, 0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c,\n        0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, 0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5,\n        0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, 0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32,\n        0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, 0x52, 0x1d, 0xc3, 0x75, 0xcf,\n        0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, 0x06, 0xd9, 0x25, 0x9e,\n        0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, 0xd5, 0xb3, 0x68,\n        0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, 0x8e, 0x6a,\n        0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, 0x6b,\n        0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64,\n        0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3,\n        0x90};\n    for(size_t i = 0; i < length; i++) {\n        data[i] = lut[data[i]];\n    }\n}\n\nstatic void protocol_gallagher_descramble(uint8_t* data, size_t length) {\n    const uint8_t lut[] = {\n        0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0x0f, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11,\n        0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1,\n        0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3,\n        0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19,\n        0x0e, 0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0,\n        0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0x0c, 0xc3, 0x3c, 0x61, 0xcc,\n        0x40, 0x9e, 0x06, 0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0x0d, 0xc2,\n        0x88, 0xd1, 0xe0, 0x07, 0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b,\n        0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x02, 0x08, 0x30, 0xa7, 0x22, 0xc9, 0x65,\n        0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f,\n        0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x00, 0x2e,\n        0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x01, 0x48, 0x04, 0xc1,\n        0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0x0a, 0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37,\n        0xa2, 0x83, 0xec, 0x03, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9,\n        0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89,\n        0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0x0b, 0xf1, 0xea, 0xfd, 0xdb,\n        0xbd, 0x09, 0xb5, 0x5b, 0x05, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e,\n        0x36};\n\n    for(size_t i = 0; i < length; i++) {\n        data[i] = lut[data[i]];\n    }\n}\n\nstatic void protocol_gallagher_decode(ProtocolGallagher* protocol) {\n    bit_lib_remove_bit_every_nth(protocol->encoded_data, 16, 9 * 8, 9);\n    protocol_gallagher_descramble(protocol->encoded_data + 2, 8);\n\n    // Region code\n    bit_lib_set_bits(protocol->data, 0, (protocol->encoded_data[5] & 0x1E) >> 1, 4);\n\n    // Issue Level\n    bit_lib_set_bits(protocol->data, 4, (protocol->encoded_data[9] & 0x0F), 4);\n\n    // Facility Code\n    uint32_t fc = (protocol->encoded_data[7] & 0x0F) << 12 | protocol->encoded_data[3] << 4 |\n                  ((protocol->encoded_data[9] >> 4) & 0x0F);\n    protocol->data[3] = (uint8_t)fc;\n    protocol->data[2] = (uint8_t)(fc >>= 8);\n    protocol->data[1] = (uint8_t)(fc >>= 8);\n\n    // Card Number\n    uint32_t card = protocol->encoded_data[2] << 16 | (protocol->encoded_data[6] & 0x1F) << 11 |\n                    protocol->encoded_data[4] << 3 | (protocol->encoded_data[5] & 0xE0) >> 5;\n    protocol->data[7] = (uint8_t)card;\n    protocol->data[6] = (uint8_t)(card >>= 8);\n    protocol->data[5] = (uint8_t)(card >>= 8);\n    protocol->data[4] = (uint8_t)(card >>= 8);\n}\n\nstatic bool protocol_gallagher_can_be_decoded(ProtocolGallagher* protocol) {\n    // check 16 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b0111111111101010) return false;\n\n    // check next 16 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 96, 16) != 0b0111111111101010) return false;\n\n    uint8_t checksum_arr[8] = {0};\n    for(int i = 0, pos = 0; i < 8; i++) {\n        // Following the preamble, every 9th bit is a checksum-bit for the preceding byte\n        pos = 16 + (9 * i);\n        checksum_arr[i] = bit_lib_get_bits(protocol->encoded_data, pos, 8);\n    }\n    uint8_t crc = bit_lib_get_bits(protocol->encoded_data, 16 + (9 * 8), 8);\n    uint8_t calc_crc = bit_lib_crc8(checksum_arr, 8, 0x7, 0x2c, false, false, 0x00);\n\n    // crc\n    if(crc != calc_crc) return false;\n\n    return true;\n}\n\nvoid protocol_gallagher_decoder_start(ProtocolGallagher* protocol) {\n    memset(protocol->encoded_data, 0, GALLAGHER_ENCODED_BYTE_FULL_SIZE);\n    manchester_advance(\n        protocol->decoder_manchester_state,\n        ManchesterEventReset,\n        &protocol->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_gallagher_decoder_feed(ProtocolGallagher* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    ManchesterEvent event = ManchesterEventReset;\n\n    if(duration > GALLAGHER_READ_SHORT_TIME_LOW && duration < GALLAGHER_READ_SHORT_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(duration > GALLAGHER_READ_LONG_TIME_LOW && duration < GALLAGHER_READ_LONG_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);\n\n        if(data_ok) {\n            bit_lib_push_bit(protocol->encoded_data, GALLAGHER_ENCODED_BYTE_FULL_SIZE, data);\n\n            if(protocol_gallagher_can_be_decoded(protocol)) {\n                protocol_gallagher_decode(protocol);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nbool protocol_gallagher_encoder_start(ProtocolGallagher* protocol) {\n    // Preamble\n    bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8);\n    bit_lib_set_bits(protocol->encoded_data, 8, 0b11101010, 8);\n\n    uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4);\n    uint8_t il = bit_lib_get_bits(protocol->data, 4, 4);\n    uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24);\n    uint32_t cn = bit_lib_get_bits_32(protocol->data, 32, 32);\n\n    uint8_t payload[8] = {0};\n    payload[0] = (cn & 0xffffff) >> 16;\n    payload[1] = (fc & 0xfff) >> 4;\n    payload[2] = (cn & 0x7ff) >> 3;\n    payload[3] = (cn & 0x7) << 5 | (rc & 0xf) << 1;\n    payload[4] = (cn & 0xffff) >> 11;\n    payload[5] = (fc & 0xffff) >> 12;\n    payload[6] = 0;\n    payload[7] = (fc & 0xf) << 4 | (il & 0xf);\n\n    // Gallagher scramble\n    protocol_gallagher_scramble(payload, 8);\n\n    for(int i = 0; i < 8; i++) {\n        // data byte\n        bit_lib_set_bits(protocol->encoded_data, 16 + (i * 9), payload[i], 8);\n\n        // every byte is followed by a bit which is the inverse of the last bit\n        bit_lib_set_bit(protocol->encoded_data, 16 + (i * 9) + 8, !(payload[i] & 0x1));\n    }\n\n    // checksum\n    uint8_t crc = bit_lib_crc8(payload, 8, 0x7, 0x2c, false, false, 0x00);\n    bit_lib_set_bits(protocol->encoded_data, 16 + (9 * 8), crc, 8);\n\n    return true;\n}\n\nLevelDuration protocol_gallagher_encoder_yield(ProtocolGallagher* protocol) {\n    bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index);\n    uint32_t duration = GALLAGHER_CLOCK_PER_BIT / 2;\n\n    if(protocol->encoded_polarity) {\n        protocol->encoded_polarity = false;\n    } else {\n        level = !level;\n\n        protocol->encoded_polarity = true;\n        bit_lib_increment_index(protocol->encoded_data_index, GALLAGHER_ENCODED_BIT_SIZE);\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_gallagher_encoder_start(protocol);\n    protocol_gallagher_decode(protocol);\n\n    protocol_gallagher_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] =\n            (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 |\n             (3 << LFRFID_T5577_MAXBLOCK_SHIFT));\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    } else if(request->write_type == LFRFIDWriteTypeEM4305) {\n        request->em4305.word[4] =\n            (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (7 << EM4x05_MAXBLOCK_SHIFT));\n        uint32_t encoded_data_reversed[3] = {0};\n        for(uint8_t i = 0; i < (32 * 3); i++) {\n            encoded_data_reversed[i / 32] =\n                (encoded_data_reversed[i / 32] << 1) |\n                (bit_lib_get_bit(protocol->encoded_data, ((32 * 3) - i)) & 1);\n        }\n        request->em4305.word[5] = encoded_data_reversed[2];\n        request->em4305.word[6] = encoded_data_reversed[1];\n        request->em4305.word[7] = encoded_data_reversed[0];\n        request->em4305.mask = 0xF0;\n        result = true;\n    }\n    return result;\n}\n\nstatic void protocol_gallagher_render_data_internal(\n    ProtocolGallagher* protocol,\n    FuriString* result,\n    bool brief) {\n    uint8_t region = bit_lib_get_bits(protocol->data, 0, 4);\n    uint8_t issue_level = bit_lib_get_bits(protocol->data, 4, 4);\n    uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24);\n    uint32_t card_id = bit_lib_get_bits_32(protocol->data, 32, 32);\n\n    if(brief) {\n        furi_string_printf(\n            result,\n            \"FC: %lu\\n\"\n            \"Card: %lu\",\n            fc,\n            card_id);\n    } else {\n        furi_string_printf(\n            result,\n            \"FC: %lu\\n\"\n            \"Card: %lu\\n\"\n            \"Region: %u\\n\"\n            \"Issue Level: %u\",\n            fc,\n            card_id,\n            region,\n            issue_level);\n    }\n}\n\nvoid protocol_gallagher_render_data(ProtocolGallagher* protocol, FuriString* result) {\n    protocol_gallagher_render_data_internal(protocol, result, false);\n}\n\nvoid protocol_gallagher_render_brief_data(ProtocolGallagher* protocol, FuriString* result) {\n    protocol_gallagher_render_data_internal(protocol, result, true);\n}\n\nconst ProtocolBase protocol_gallagher = {\n    .name = \"Gallagher\",\n    .manufacturer = \"Gallagher\",\n    .data_size = GALLAGHER_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_gallagher_alloc,\n    .free = (ProtocolFree)protocol_gallagher_free,\n    .get_data = (ProtocolGetData)protocol_gallagher_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_gallagher_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_gallagher_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_gallagher_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_gallagher_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_gallagher_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_gallagher_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_gallagher_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_gallagher.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_gallagher;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_gproxii.c",
    "content": "#include <furi.h>\n#include \"toolbox/level_duration.h\"\n#include \"protocol_gproxii.h\"\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define GPROXII_PREAMBLE_BIT_SIZE (6)\n#define GPROXII_ENCODED_BIT_SIZE  (90)\n#define GPROXII_ENCODED_BYTE_FULL_SIZE \\\n    (((GPROXII_PREAMBLE_BIT_SIZE + GPROXII_ENCODED_BIT_SIZE) / 8))\n\n#define GPROXII_DATA_SIZE (12)\n\n#define GPROXII_SHORT_TIME  (256)\n#define GPROXII_LONG_TIME   (512)\n#define GPROXII_JITTER_TIME (120)\n\n#define GPROXII_SHORT_TIME_LOW  (GPROXII_SHORT_TIME - GPROXII_JITTER_TIME)\n#define GPROXII_SHORT_TIME_HIGH (GPROXII_SHORT_TIME + GPROXII_JITTER_TIME)\n#define GPROXII_LONG_TIME_LOW   (GPROXII_LONG_TIME - GPROXII_JITTER_TIME)\n#define GPROXII_LONG_TIME_HIGH  (GPROXII_LONG_TIME + GPROXII_JITTER_TIME)\n\ntypedef struct {\n    bool last_short;\n    bool last_level;\n    size_t encoded_index;\n    uint8_t decoded_data[GPROXII_ENCODED_BYTE_FULL_SIZE];\n    uint8_t data[GPROXII_ENCODED_BYTE_FULL_SIZE];\n} ProtocolGProxII;\n\nProtocolGProxII* protocol_gproxii_alloc(void) {\n    ProtocolGProxII* protocol = malloc(sizeof(ProtocolGProxII));\n    return protocol;\n}\n\nvoid protocol_gproxii_free(ProtocolGProxII* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_gproxii_get_data(ProtocolGProxII* protocol) {\n    return protocol->data;\n}\n\nbool wiegand_check(uint64_t fc_and_card, bool even_parity, bool odd_parity, int card_len) {\n    uint8_t even_parity_sum = 0;\n    uint8_t odd_parity_sum = 1;\n    switch(card_len) {\n    case 26:\n        for(int8_t i = 12; i < 24; i++) {\n            if(((fc_and_card >> i) & 1) == 1) {\n                even_parity_sum++;\n            }\n        }\n        if(even_parity_sum % 2 != even_parity) return false;\n\n        for(int8_t i = 0; i < 12; i++) {\n            if(((fc_and_card >> i) & 1) == 1) {\n                odd_parity_sum++;\n            }\n        }\n        if(odd_parity_sum % 2 != odd_parity) return false;\n        break;\n    case 36:\n        for(int8_t i = 17; i < 34; i++) {\n            if(((fc_and_card >> i) & 1) == 1) {\n                even_parity_sum++;\n            }\n        }\n        if(even_parity_sum % 2 != even_parity) return false;\n\n        for(int8_t i = 0; i < 17; i++) {\n            if(((fc_and_card >> i) & 1) == 1) {\n                odd_parity_sum++;\n            }\n        }\n        if(odd_parity_sum % 2 != odd_parity) return false;\n        break;\n    default:\n        furi_crash();\n    }\n    return true;\n}\n\nvoid protocol_gproxii_decoder_start(ProtocolGProxII* protocol) {\n    memset(protocol->data, 0, GPROXII_ENCODED_BYTE_FULL_SIZE);\n    memset(protocol->decoded_data, 0, GPROXII_DATA_SIZE);\n    protocol->last_short = false;\n}\n\nstatic bool protocol_gproxii_can_be_decoded(ProtocolGProxII* protocol) {\n    // 96 bit with 5 bit zero parity\n    // 0           10            20            30            40            50            60            70            80            90\n    // |           |             |             |             |             |             |             |             |             |\n    // 012345 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5\n    // ------------------------------------------------------------------------------------------------------------------------------------\n    // 111110 0000 0 1001 0 1101 0 1111 0 1000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1000 0 0000 0 1001 0\n\n    // Remove header and reverse bytes on the remaining 72 bits\n    //\n    // 0          10         20         30          40         50         60         70\n    // |          |          |          |           |          |          |          |\n    // 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123 45678901\n    // --------------------------------------------------------------------------------\n    // 00001001 11011111 10001001 00001001 00001001 00001001 00001001 00001000 00001001 - Without parity\n    // 10010000 11111011 10010001 10010000 10010000 10010000 10010000 00010000 10010000 - Reversed\n    // 10010000 01101011 00000001 00000000 00000000 00000000 00000000 10000000 00000000 - XOR all bytes from 1 using byte 0\n\n    // 72 Bit Guardall/Verex/Chubb GProx II 26 bit key with 16 bit profile\n    // 0          10          20        30          40         50          60        70\n    // |          |           |         |           |          |           |         |\n    // 01234567 890123 45 6789012345678901 2 34567890 1234567890123456 7 89012345678901\n    // --------------------------------------------------------------------------------\n    // XORVALUE LLLLLL DD PPPPPPPPPPPPPPPP E FFFFFFFF CCCCCCCCCCCCCCCC O UUUUUUUUUUUUUU\n    // 10010000 011010 11 0000000100000000 0 00000000 0000000000000001 0 00000000000000 - Profile: 256 FC: 0 Card: 1\n\n    // 72 Bit Guardall/Verex/Chubb GProx II 36 bit key with 16 bit profile\n    // 0          10          20         30          40         50        60          70\n    // |          |           |          |           |          |         |           |\n    // 01234567 890123 45 67890123 45678901 2 34567890123456 78901234567890123456 7 8901\n    // --------------------------------------------------------------------------------\n    // XORVALUE LLLLLL DD PPPPPPPP PPPPPPPP E UUUUUUFFFFFFFF UUUUCCCCCCCCCCCCCCCC O UUUU\n    // 10111000 100100 10 00000001 00000000 0 00000000010100 00001000100010111000 1 0000 - Profile: 256 FC: 20 Card: 35000\n\n    // X = XOR Key, L = Message length, D = 2 bit check digits, P = Profile, E = Wiegand leading even parity\n    // F = Faclity code, C = Card number, O = Wiegand trailing odd parity, U = Unused bits\n\n    // Check 6 bits preamble 111110\n    if(bit_lib_get_bits(protocol->data, 0, 6) != 0b111110) return false;\n\n    // Check always 0 parity on every 5th bit after preamble\n    if(!bit_lib_test_parity(protocol->data, 6, GPROXII_ENCODED_BIT_SIZE, BitLibParityAlways0, 5)) {\n        return false;\n    }\n\n    // Start GProx II decode\n    bit_lib_copy_bits(protocol->decoded_data, 0, GPROXII_ENCODED_BIT_SIZE, protocol->data, 6);\n\n    // Remove parity\n    bit_lib_remove_bit_every_nth(protocol->decoded_data, 0, GPROXII_ENCODED_BIT_SIZE, 5);\n\n    // Reverse bytes\n    for(int i = 0; i < 9; i++) {\n        protocol->decoded_data[i] = bit_lib_reverse_8_fast(protocol->decoded_data[i]);\n    }\n\n    // DeXOR from byte 1 using byte 0\n    for(int i = 1; i < 9; i++) {\n        protocol->decoded_data[i] = protocol->decoded_data[0] ^ protocol->decoded_data[i];\n    }\n\n    // Check card length is either 26 or 36\n    int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6);\n\n    // wiegand parity\n    if(card_len == 26) {\n        uint64_t fc_and_card = bit_lib_get_bits_64(protocol->decoded_data, 33, 24);\n        bool even_parity = bit_lib_get_bits(protocol->decoded_data, 32, 1);\n        bool odd_parity = bit_lib_get_bits(protocol->decoded_data, 57, 1);\n        if(!wiegand_check(fc_and_card, even_parity, odd_parity, card_len)) return false;\n    } else if(card_len == 36) {\n        uint64_t fc_and_card = bit_lib_get_bits_64(protocol->decoded_data, 33, 34);\n        uint8_t even_parity = bit_lib_get_bits(protocol->decoded_data, 32, 1);\n        uint8_t odd_parity = bit_lib_get_bits(protocol->decoded_data, 67, 1);\n        if(!wiegand_check(fc_and_card, even_parity, odd_parity, card_len)) return false;\n    } else {\n        return false; // If we don't get a 26 or 36 it's not a known card type\n    }\n\n    return true;\n}\n\nbool protocol_gproxii_decoder_feed(ProtocolGProxII* protocol, bool level, uint32_t duration) {\n    UNUSED(level);\n    bool pushed = false;\n\n    // Bi-Phase Manchester decoding inverse. Short = 1, Long = 0\n    if(duration >= GPROXII_SHORT_TIME_LOW && duration <= GPROXII_SHORT_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            protocol->last_short = true;\n        } else {\n            pushed = true;\n            bit_lib_push_bit(protocol->data, GPROXII_ENCODED_BYTE_FULL_SIZE, true);\n            protocol->last_short = false;\n        }\n    } else if(duration >= GPROXII_LONG_TIME_LOW && duration <= GPROXII_LONG_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            pushed = true;\n            bit_lib_push_bit(protocol->data, GPROXII_ENCODED_BYTE_FULL_SIZE, false);\n        } else {\n            // reset\n            protocol->last_short = false;\n        }\n    } else {\n        // reset\n        protocol->last_short = false;\n    }\n\n    if(pushed && protocol_gproxii_can_be_decoded(protocol)) {\n        return true;\n    }\n\n    return false;\n}\n\nbool protocol_gproxii_encoder_start(ProtocolGProxII* protocol) {\n    protocol->encoded_index = 0;\n    protocol->last_short = false;\n    protocol->last_level = false;\n    return true;\n}\n\nLevelDuration protocol_gproxii_encoder_yield(ProtocolGProxII* protocol) {\n    uint32_t duration;\n    protocol->last_level = !protocol->last_level;\n\n    bool bit = bit_lib_get_bit(protocol->data, protocol->encoded_index);\n\n    // Bi-Phase Manchester encoder inverted\n    if(bit) {\n        // two short pulses for 1\n        duration = GPROXII_SHORT_TIME / 8;\n        if(protocol->last_short) {\n            bit_lib_increment_index(protocol->encoded_index, 96);\n            protocol->last_short = false;\n        } else {\n            protocol->last_short = true;\n        }\n    } else {\n        // one long pulse for 0\n        duration = GPROXII_LONG_TIME / 8;\n        bit_lib_increment_index(protocol->encoded_index, 96);\n    }\n    return level_duration_make(protocol->last_level, duration);\n}\n\nvoid protocol_gproxii_render_data(ProtocolGProxII* protocol, FuriString* result) {\n    protocol_gproxii_can_be_decoded(protocol);\n    int xor_code = bit_lib_get_bits(protocol->decoded_data, 0, 8);\n    int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6);\n    int crc_code = bit_lib_get_bits(protocol->decoded_data, 14, 2);\n\n    if(card_len == 26) { // 26 Bit card\n        // Print FC, Card and Length\n        furi_string_cat_printf(\n            result,\n            \"FC: %u Card: %u LEN: %hhu\\n\",\n            bit_lib_get_bits(protocol->decoded_data, 33, 8),\n            bit_lib_get_bits_16(protocol->decoded_data, 41, 16),\n            card_len);\n        // XOR Key, CRC and Profile\n        furi_string_cat_printf(\n            result,\n            \"XOR: %hhu CRC: %hhu P: %04hX\",\n            xor_code,\n            crc_code,\n            bit_lib_get_bits_16(protocol->decoded_data, 16, 16));\n    } else if(card_len == 36) { // 36 Bit card\n        // Print FC, Card and Length\n        furi_string_cat_printf(\n            result,\n            \"FC: %u Card: %u LEN: %hhu\\n\",\n            bit_lib_get_bits_16(protocol->decoded_data, 33, 14),\n            bit_lib_get_bits_16(protocol->decoded_data, 51, 16),\n            card_len);\n        // XOR Key, CRC and Profile\n        furi_string_cat_printf(\n            result,\n            \"XOR: %hhu CRC: %hhu P: %04hX\",\n            xor_code,\n            crc_code,\n            bit_lib_get_bits_16(protocol->decoded_data, 16, 16));\n    } else {\n        furi_string_cat_printf(result, \"Read Error\\n\");\n    }\n}\n\nbool protocol_gproxii_write_data(ProtocolGProxII* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_BIPHASE | LFRFID_T5577_BITRATE_RF_64 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_gproxii = {\n    .name = \"GProxII\",\n    .manufacturer = \"Guardall\",\n    .data_size = GPROXII_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_gproxii_alloc,\n    .free = (ProtocolFree)protocol_gproxii_free,\n    .get_data = (ProtocolGetData)protocol_gproxii_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_gproxii_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_gproxii_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_gproxii_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_gproxii_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_gproxii_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_gproxii_render_data,\n    .write_data = (ProtocolWriteData)protocol_gproxii_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_gproxii.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_gproxii;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_h10301.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include \"lfrfid_protocols.h\"\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define H10301_DECODED_DATA_SIZE     (3)\n#define H10301_ENCODED_DATA_SIZE_U32 (3)\n#define H10301_ENCODED_DATA_SIZE     (sizeof(uint32_t) * H10301_ENCODED_DATA_SIZE_U32)\n\n#define H10301_BIT_SIZE     (sizeof(uint32_t) * 8)\n#define H10301_BIT_MAX_SIZE (H10301_BIT_SIZE * H10301_DECODED_DATA_SIZE)\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolH10301Decoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n    uint32_t pulse;\n} ProtocolH10301Encoder;\n\ntypedef struct {\n    ProtocolH10301Decoder decoder;\n    ProtocolH10301Encoder encoder;\n    uint32_t encoded_data[H10301_ENCODED_DATA_SIZE_U32];\n    uint8_t data[H10301_DECODED_DATA_SIZE];\n} ProtocolH10301;\n\nProtocolH10301* protocol_h10301_alloc(void) {\n    ProtocolH10301* protocol = malloc(sizeof(ProtocolH10301));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_h10301_free(ProtocolH10301* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_h10301_get_data(ProtocolH10301* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_h10301_decoder_start(ProtocolH10301* protocol) {\n    memset(protocol->encoded_data, 0, sizeof(uint32_t) * 3);\n}\n\nstatic void protocol_h10301_decoder_store_data(ProtocolH10301* protocol, bool data) {\n    protocol->encoded_data[0] = (protocol->encoded_data[0] << 1) |\n                                ((protocol->encoded_data[1] >> 31) & 1);\n    protocol->encoded_data[1] = (protocol->encoded_data[1] << 1) |\n                                ((protocol->encoded_data[2] >> 31) & 1);\n    protocol->encoded_data[2] = (protocol->encoded_data[2] << 1) | data;\n}\n\nstatic bool protocol_h10301_can_be_decoded(const uint32_t* card_data) {\n    const uint8_t* encoded_data = (const uint8_t*)card_data;\n\n    // packet preamble\n    // raw data\n    if(*(encoded_data + 3) != 0x1D) {\n        return false;\n    }\n\n    // encoded company/oem\n    // coded with 01 = 0, 10 = 1 transitions\n    // stored in word 0\n    if((*card_data >> 10 & 0x3FFF) != 0x1556) {\n        return false;\n    }\n\n    // encoded format/length\n    // coded with 01 = 0, 10 = 1 transitions\n    // stored in word 0 and word 1\n    if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {\n        return false;\n    }\n\n    // data decoding\n    uint32_t result = 0;\n\n    // decode from word 1\n    // coded with 01 = 0, 10 = 1 transitions\n    for(int8_t i = 9; i >= 0; i--) {\n        switch((*(card_data + 1) >> (2 * i)) & 0b11) {\n        case 0b01:\n            result = (result << 1) | 0;\n            break;\n        case 0b10:\n            result = (result << 1) | 1;\n            break;\n        default:\n            return false;\n            break;\n        }\n    }\n\n    // decode from word 2\n    // coded with 01 = 0, 10 = 1 transitions\n    for(int8_t i = 15; i >= 0; i--) {\n        switch((*(card_data + 2) >> (2 * i)) & 0b11) {\n        case 0b01:\n            result = (result << 1) | 0;\n            break;\n        case 0b10:\n            result = (result << 1) | 1;\n            break;\n        default:\n            return false;\n            break;\n        }\n    }\n\n    // trailing parity (odd) test\n    uint8_t parity_sum = 0;\n    for(int8_t i = 0; i < 13; i++) {\n        if(((result >> i) & 1) == 1) {\n            parity_sum++;\n        }\n    }\n\n    if((parity_sum % 2) != 1) {\n        return false;\n    }\n\n    // leading parity (even) test\n    parity_sum = 0;\n    for(int8_t i = 13; i < 26; i++) {\n        if(((result >> i) & 1) == 1) {\n            parity_sum++;\n        }\n    }\n\n    if((parity_sum % 2) == 1) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic void protocol_h10301_decode(const uint32_t* card_data, uint8_t* decoded_data) {\n    // data decoding\n    uint32_t result = 0;\n\n    // decode from word 1\n    // coded with 01 = 0, 10 = 1 transitions\n    for(int8_t i = 9; i >= 0; i--) {\n        switch((*(card_data + 1) >> (2 * i)) & 0b11) {\n        case 0b01:\n            result = (result << 1) | 0;\n            break;\n        case 0b10:\n            result = (result << 1) | 1;\n            break;\n        default:\n            break;\n        }\n    }\n\n    // decode from word 2\n    // coded with 01 = 0, 10 = 1 transitions\n    for(int8_t i = 15; i >= 0; i--) {\n        switch((*(card_data + 2) >> (2 * i)) & 0b11) {\n        case 0b01:\n            result = (result << 1) | 0;\n            break;\n        case 0b10:\n            result = (result << 1) | 1;\n            break;\n        default:\n            break;\n        }\n    }\n\n    uint8_t data[H10301_DECODED_DATA_SIZE] = {\n        (uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};\n\n    memcpy(decoded_data, &data, H10301_DECODED_DATA_SIZE);\n}\n\nbool protocol_h10301_decoder_feed(ProtocolH10301* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            protocol_h10301_decoder_store_data(protocol, value);\n            if(protocol_h10301_can_be_decoded(protocol->encoded_data)) {\n                protocol_h10301_decode(protocol->encoded_data, protocol->data);\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_h10301_write_raw_bit(bool bit, uint8_t position, uint32_t* card_data) {\n    if(bit) {\n        card_data[position / H10301_BIT_SIZE] |=\n            1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1);\n    } else {\n        card_data[position / H10301_BIT_SIZE] &=\n            ~(1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1));\n    }\n}\n\nstatic void protocol_h10301_write_bit(bool bit, uint8_t position, uint32_t* card_data) {\n    protocol_h10301_write_raw_bit(bit, position + 0, card_data);\n    protocol_h10301_write_raw_bit(!bit, position + 1, card_data);\n}\n\nvoid protocol_h10301_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {\n    uint32_t card_data[H10301_DECODED_DATA_SIZE] = {0, 0, 0};\n\n    uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];\n\n    // even parity sum calculation (high 12 bits of data)\n    uint8_t even_parity_sum = 0;\n    for(int8_t i = 12; i < 24; i++) {\n        if(((fc_cn >> i) & 1) == 1) {\n            even_parity_sum++;\n        }\n    }\n\n    // odd parity sum calculation (low 12 bits of data)\n    uint8_t odd_parity_sum = 1;\n    for(int8_t i = 0; i < 12; i++) {\n        if(((fc_cn >> i) & 1) == 1) {\n            odd_parity_sum++;\n        }\n    }\n\n    // 0x1D preamble\n    protocol_h10301_write_raw_bit(0, 0, card_data);\n    protocol_h10301_write_raw_bit(0, 1, card_data);\n    protocol_h10301_write_raw_bit(0, 2, card_data);\n    protocol_h10301_write_raw_bit(1, 3, card_data);\n    protocol_h10301_write_raw_bit(1, 4, card_data);\n    protocol_h10301_write_raw_bit(1, 5, card_data);\n    protocol_h10301_write_raw_bit(0, 6, card_data);\n    protocol_h10301_write_raw_bit(1, 7, card_data);\n\n    // company / OEM code 1\n    protocol_h10301_write_bit(0, 8, card_data);\n    protocol_h10301_write_bit(0, 10, card_data);\n    protocol_h10301_write_bit(0, 12, card_data);\n    protocol_h10301_write_bit(0, 14, card_data);\n    protocol_h10301_write_bit(0, 16, card_data);\n    protocol_h10301_write_bit(0, 18, card_data);\n    protocol_h10301_write_bit(1, 20, card_data);\n\n    // card format / length 1\n    protocol_h10301_write_bit(0, 22, card_data);\n    protocol_h10301_write_bit(0, 24, card_data);\n    protocol_h10301_write_bit(0, 26, card_data);\n    protocol_h10301_write_bit(0, 28, card_data);\n    protocol_h10301_write_bit(0, 30, card_data);\n    protocol_h10301_write_bit(0, 32, card_data);\n    protocol_h10301_write_bit(0, 34, card_data);\n    protocol_h10301_write_bit(0, 36, card_data);\n    protocol_h10301_write_bit(0, 38, card_data);\n    protocol_h10301_write_bit(0, 40, card_data);\n    protocol_h10301_write_bit(1, 42, card_data);\n\n    // even parity bit\n    protocol_h10301_write_bit((even_parity_sum % 2), 44, card_data);\n\n    // data\n    for(uint8_t i = 0; i < 24; i++) {\n        protocol_h10301_write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);\n    }\n\n    // odd parity bit\n    protocol_h10301_write_bit((odd_parity_sum % 2), 94, card_data);\n\n    memcpy(encoded_data, &card_data, H10301_ENCODED_DATA_SIZE);\n}\n\nbool protocol_h10301_encoder_start(ProtocolH10301* protocol) {\n    protocol_h10301_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n    protocol->encoder.encoded_index = 0;\n    protocol->encoder.pulse = 0;\n\n    return true;\n}\n\nLevelDuration protocol_h10301_encoder_yield(ProtocolH10301* protocol) {\n    bool level = 0;\n    uint32_t duration = 0;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(protocol->encoder.pulse == 0) {\n        // get bit\n        uint8_t bit =\n            (protocol->encoded_data[protocol->encoder.encoded_index / H10301_BIT_SIZE] >>\n             ((H10301_BIT_SIZE - 1) - (protocol->encoder.encoded_index % H10301_BIT_SIZE))) &\n            1;\n\n        // get pulse from oscillator\n        bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);\n\n        if(advance) {\n            protocol->encoder.encoded_index++;\n            if(protocol->encoder.encoded_index >= (H10301_BIT_MAX_SIZE)) {\n                protocol->encoder.encoded_index = 0;\n            }\n        }\n\n        // duration diveded by 2 because we need to output high and low\n        duration = duration / 2;\n        protocol->encoder.pulse = duration;\n        level = true;\n    } else {\n        // output low half and reset pulse\n        duration = protocol->encoder.pulse;\n        protocol->encoder.pulse = 0;\n        level = false;\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_h10301_write_data(ProtocolH10301* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_h10301_encoder_start(protocol);\n    protocol_h10301_decode(protocol->encoded_data, protocol->data);\n\n    protocol_h10301_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = protocol->encoded_data[0];\n        request->t5577.block[2] = protocol->encoded_data[1];\n        request->t5577.block[3] = protocol->encoded_data[2];\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_h10301_render_data(ProtocolH10301* protocol, FuriString* result) {\n    uint8_t* data = protocol->data;\n    furi_string_printf(\n        result,\n        \"FC: %hhu\\n\"\n        \"Card: %hu\",\n        data[0],\n        (uint16_t)((data[1] << 8) | (data[2])));\n}\n\nconst ProtocolBase protocol_h10301 = {\n    .name = \"H10301\",\n    .manufacturer = \"HID\",\n    .data_size = H10301_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_h10301_alloc,\n    .free = (ProtocolFree)protocol_h10301_free,\n    .get_data = (ProtocolGetData)protocol_h10301_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_h10301_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_h10301_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_h10301_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_h10301_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_h10301_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_h10301_render_data,\n    .write_data = (ProtocolWriteData)protocol_h10301_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_h10301.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_h10301;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_hid_ex_generic.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include \"lfrfid_protocols.h\"\n#include <bit_lib/bit_lib.h>\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define HID_DATA_SIZE     23\n#define HID_PREAMBLE_SIZE 1\n\n#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)\n#define HID_ENCODED_BIT_SIZE  ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)\n#define HID_DECODED_DATA_SIZE (12)\n#define HID_DECODED_BIT_SIZE  ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)\n\n#define HID_PREAMBLE 0x1D\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolHIDExDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n    uint32_t pulse;\n} ProtocolHIDExEncoder;\n\ntypedef struct {\n    ProtocolHIDExDecoder decoder;\n    ProtocolHIDExEncoder encoder;\n    uint8_t encoded_data[HID_ENCODED_DATA_SIZE];\n    uint8_t data[HID_DECODED_DATA_SIZE];\n    size_t protocol_size;\n} ProtocolHIDEx;\n\nProtocolHIDEx* protocol_hid_ex_generic_alloc(void) {\n    ProtocolHIDEx* protocol = malloc(sizeof(ProtocolHIDEx));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_hid_ex_generic_free(ProtocolHIDEx* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_hid_ex_generic_get_data(ProtocolHIDEx* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_hid_ex_generic_decoder_start(ProtocolHIDEx* protocol) {\n    memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_hid_ex_generic_can_be_decoded(const uint8_t* data) {\n    // check preamble\n    if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {\n        return false;\n    }\n\n    // check for manchester encoding\n    for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {\n        for(size_t n = 0; n < 4; n++) {\n            uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;\n            if(bit_pair == 0b11 || bit_pair == 0b00) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nstatic void protocol_hid_ex_generic_decode(const uint8_t* from, uint8_t* to) {\n    size_t bit_index = 0;\n    for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {\n        for(size_t n = 0; n < 4; n++) {\n            uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;\n            if(bit_pair == 0b01) {\n                bit_lib_set_bit(to, bit_index, 0);\n            } else if(bit_pair == 0b10) {\n                bit_lib_set_bit(to, bit_index, 1);\n            }\n            bit_index++;\n        }\n    }\n}\n\nbool protocol_hid_ex_generic_decoder_feed(ProtocolHIDEx* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);\n            if(protocol_hid_ex_generic_can_be_decoded(protocol->encoded_data)) {\n                protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_hid_ex_generic_encode(ProtocolHIDEx* protocol) {\n    protocol->encoded_data[0] = HID_PREAMBLE;\n\n    size_t bit_index = 0;\n    for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {\n        bool bit = bit_lib_get_bit(protocol->data, i);\n        if(bit) {\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);\n        } else {\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);\n        }\n        bit_index += 2;\n    }\n}\n\nbool protocol_hid_ex_generic_encoder_start(ProtocolHIDEx* protocol) {\n    protocol->encoder.encoded_index = 0;\n    protocol->encoder.pulse = 0;\n    protocol_hid_ex_generic_encode(protocol);\n\n    return true;\n}\n\nLevelDuration protocol_hid_ex_generic_encoder_yield(ProtocolHIDEx* protocol) {\n    bool level = 0;\n    uint32_t duration = 0;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(protocol->encoder.pulse == 0) {\n        // get bit\n        uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n\n        // get pulse from oscillator\n        bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);\n\n        if(advance) {\n            bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);\n        }\n\n        // duration diveded by 2 because we need to output high and low\n        duration = duration / 2;\n        protocol->encoder.pulse = duration;\n        level = true;\n    } else {\n        // output low half and reset pulse\n        duration = protocol->encoder.pulse;\n        protocol->encoder.pulse = 0;\n        level = false;\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_hid_ex_generic_encoder_start(protocol);\n    protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data);\n\n    protocol_hid_ex_generic_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (6 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);\n        request->t5577.block[5] = bit_lib_get_bits_32(protocol->encoded_data, 128, 32);\n        request->t5577.block[6] = bit_lib_get_bits_32(protocol->encoded_data, 160, 32);\n        request->t5577.blocks_to_write = 7;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, FuriString* result) {\n    UNUSED(protocol);\n\n    // TODO FL-3518: parser and render functions\n    furi_string_set(\n        result,\n        \"Type: Generic HID Extended\\n\"\n        \"Data: Unknown\");\n}\n\nconst ProtocolBase protocol_hid_ex_generic = {\n    .name = \"HIDExt\",\n    .manufacturer = \"Generic\",\n    .data_size = HID_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_hid_ex_generic_alloc,\n    .free = (ProtocolFree)protocol_hid_ex_generic_free,\n    .get_data = (ProtocolGetData)protocol_hid_ex_generic_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_hid_ex_generic_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_hid_ex_generic_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_hid_ex_generic_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_hid_ex_generic_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data,\n    .write_data = (ProtocolWriteData)protocol_hid_ex_generic_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_hid_ex_generic.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_hid_ex_generic;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_hid_generic.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include \"lfrfid_protocols.h\"\n#include <bit_lib/bit_lib.h>\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define HID_DATA_SIZE             11\n#define HID_PREAMBLE_SIZE         1\n#define HID_PROTOCOL_SIZE_UNKNOWN 0\n\n#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)\n#define HID_ENCODED_BIT_SIZE  ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)\n#define HID_DECODED_DATA_SIZE (6)\n#define HID_DECODED_BIT_SIZE  ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)\n\n#define HID_PREAMBLE 0x1D\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolHIDDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n    uint32_t pulse;\n} ProtocolHIDEncoder;\n\ntypedef struct {\n    ProtocolHIDDecoder decoder;\n    ProtocolHIDEncoder encoder;\n    uint8_t encoded_data[HID_ENCODED_DATA_SIZE];\n    uint8_t data[HID_DECODED_DATA_SIZE];\n} ProtocolHID;\n\nProtocolHID* protocol_hid_generic_alloc(void) {\n    ProtocolHID* protocol = malloc(sizeof(ProtocolHID));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_hid_generic_free(ProtocolHID* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_hid_generic_decoder_start(ProtocolHID* protocol) {\n    memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_hid_generic_can_be_decoded(const uint8_t* data) {\n    // check preamble\n    if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {\n        return false;\n    }\n\n    // check for manchester encoding\n    for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {\n        for(size_t n = 0; n < 4; n++) {\n            uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;\n            if(bit_pair == 0b11 || bit_pair == 0b00) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nstatic void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) {\n    size_t bit_index = 0;\n    for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {\n        for(size_t n = 0; n < 4; n++) {\n            uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;\n            if(bit_pair == 0b01) {\n                bit_lib_set_bit(to, bit_index, 0);\n            } else if(bit_pair == 0b10) {\n                bit_lib_set_bit(to, bit_index, 1);\n            }\n            bit_index++;\n        }\n    }\n}\n\n/**\n * Decodes size from the HID Proximity header:\n * - If any of the first six bits is 1, the key is composed of the bits\n *   following the first 1\n * - Otherwise, if the first six bits are 0:\n *   - If the seventh bit is 0, the key is composed of the remaining 37 bits.\n *   - If the seventh bit is 1, the size header continues until the next 1 bit,\n *     and the key is composed of however many bits remain.\n *\n * HID Proximity keys are 26 bits at minimum. If the header implies a key size\n * under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN.\n */\nstatic uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) {\n    for(size_t bit_index = 0; bit_index < 6; bit_index++) {\n        if(bit_lib_get_bit(protocol->data, bit_index)) {\n            return HID_DECODED_BIT_SIZE - bit_index - 1;\n        }\n    }\n\n    if(!bit_lib_get_bit(protocol->data, 6)) {\n        return 37;\n    }\n\n    size_t bit_index = 7;\n    uint8_t size = 36;\n    while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) {\n        size--;\n        bit_index++;\n    }\n    return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size;\n}\n\nbool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);\n            if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) {\n                protocol_hid_generic_decode(protocol->encoded_data, protocol->data);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_hid_generic_encode(ProtocolHID* protocol) {\n    protocol->encoded_data[0] = HID_PREAMBLE;\n\n    size_t bit_index = 0;\n    for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {\n        bool bit = bit_lib_get_bit(protocol->data, i);\n        if(bit) {\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);\n        } else {\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);\n            bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);\n        }\n        bit_index += 2;\n    }\n}\n\nbool protocol_hid_generic_encoder_start(ProtocolHID* protocol) {\n    protocol->encoder.encoded_index = 0;\n    protocol->encoder.pulse = 0;\n    protocol_hid_generic_encode(protocol);\n\n    return true;\n}\n\nLevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) {\n    bool level = 0;\n    uint32_t duration = 0;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(protocol->encoder.pulse == 0) {\n        // get bit\n        uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n\n        // get pulse from oscillator\n        bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);\n\n        if(advance) {\n            bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);\n        }\n\n        // duration diveded by 2 because we need to output high and low\n        duration = duration / 2;\n        protocol->encoder.pulse = duration;\n        level = true;\n    } else {\n        // output low half and reset pulse\n        duration = protocol->encoder.pulse;\n        protocol->encoder.pulse = 0;\n        level = false;\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_hid_generic_encoder_start(protocol);\n    protocol_hid_generic_decode(protocol->encoded_data, protocol->data);\n\n    protocol_hid_generic_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nstatic void protocol_hid_generic_string_cat_protocol_bits(\n    ProtocolHID* protocol,\n    uint8_t protocol_size,\n    FuriString* result) {\n    // round up to the nearest nibble\n    const uint8_t hex_character_count = (protocol_size + 3) / 4;\n    const uint8_t protocol_bit_index = HID_DECODED_BIT_SIZE - protocol_size;\n\n    for(size_t i = 0; i < hex_character_count; i++) {\n        uint8_t nibble = i == 0 ? bit_lib_get_bits(\n                                      protocol->data,\n                                      protocol_bit_index,\n                                      protocol_size % 4 == 0 ? 4 : protocol_size % 4) :\n                                  bit_lib_get_bits(protocol->data, protocol_bit_index + i * 4, 4);\n        furi_string_cat_printf(result, \"%X\", nibble & 0xF);\n    }\n}\n\nvoid protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) {\n    const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol);\n\n    if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) {\n        furi_string_printf(\n            result,\n            \"Generic HID Proximity\\n\"\n            \"Data: %02X%02X%02X%02X%02X%X\",\n            protocol->data[0],\n            protocol->data[1],\n            protocol->data[2],\n            protocol->data[3],\n            protocol->data[4],\n            protocol->data[5] >> 4);\n    } else {\n        furi_string_printf(\n            result,\n            \"%hhu-bit HID Proximity\\n\"\n            \"Data: \",\n            protocol_size);\n        protocol_hid_generic_string_cat_protocol_bits(protocol, protocol_size, result);\n    }\n}\n\nconst ProtocolBase protocol_hid_generic = {\n    .name = \"HIDProx\",\n    .manufacturer = \"Generic\",\n    .data_size = HID_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 6,\n    .alloc = (ProtocolAlloc)protocol_hid_generic_alloc,\n    .free = (ProtocolFree)protocol_hid_generic_free,\n    .get_data = (ProtocolGetData)protocol_hid_generic_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_hid_generic_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data,\n    .write_data = (ProtocolWriteData)protocol_hid_generic_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_hid_generic.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_hid_generic;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_idteck.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n// Example: 4944544B 351FBE4B\n// 01001001 01000100 01010100 01001011       00110101 00011111 10111110 01001011\n// 4    9    4    4    5    4    4    B      3    5    1    F    B    E    4    B\n// 0100 1001 0100 0100 0101 0100 0100 1011   0011 0101 0001 1111 1011 1110 0100 1011\n\n#define IDTECK_PREAMBLE_BIT_SIZE  (32)\n#define IDTECK_PREAMBLE_DATA_SIZE (8)\n\n#define IDTECK_ENCODED_BIT_SIZE  (64)\n#define IDTECK_ENCODED_DATA_SIZE (((IDTECK_ENCODED_BIT_SIZE) / 8) + IDTECK_PREAMBLE_DATA_SIZE)\n#define IDTECK_ENCODED_DATA_LAST ((IDTECK_ENCODED_BIT_SIZE) / 8)\n\n#define IDTECK_DECODED_BIT_SIZE  (64)\n#define IDTECK_DECODED_DATA_SIZE (8)\n\n#define IDTECK_US_PER_BIT             (255)\n#define IDTECK_ENCODER_PULSES_PER_BIT (16)\n\ntypedef struct {\n    uint8_t data_index;\n    uint8_t bit_clock_index;\n    bool last_bit;\n    bool current_polarity;\n    bool pulse_phase;\n} ProtocolIdteckEncoder;\n\ntypedef struct {\n    uint8_t encoded_data[IDTECK_ENCODED_DATA_SIZE];\n    uint8_t negative_encoded_data[IDTECK_ENCODED_DATA_SIZE];\n    uint8_t corrupted_encoded_data[IDTECK_ENCODED_DATA_SIZE];\n    uint8_t corrupted_negative_encoded_data[IDTECK_ENCODED_DATA_SIZE];\n\n    uint8_t data[IDTECK_DECODED_DATA_SIZE];\n    ProtocolIdteckEncoder encoder;\n} ProtocolIdteck;\n\nProtocolIdteck* protocol_idteck_alloc(void) {\n    ProtocolIdteck* protocol = malloc(sizeof(ProtocolIdteck));\n    return protocol;\n}\n\nvoid protocol_idteck_free(ProtocolIdteck* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_idteck_get_data(ProtocolIdteck* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_idteck_decoder_start(ProtocolIdteck* protocol) {\n    memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE);\n    memset(protocol->negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_idteck_check_preamble(uint8_t* data, size_t bit_index) {\n    // Preamble 01001001 01000100 01010100 01001011\n    if(*(uint32_t*)&data[bit_index / 8] != 0b01001011010101000100010001001001) return false;\n    return true;\n}\n\nstatic bool protocol_idteck_can_be_decoded(uint8_t* data) {\n    if(!protocol_idteck_check_preamble(data, 0)) return false;\n    return true;\n}\n\nstatic bool protocol_idteck_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {\n    time += (IDTECK_US_PER_BIT / 2);\n\n    size_t bit_count = (time / IDTECK_US_PER_BIT);\n    bool result = false;\n\n    if(bit_count < IDTECK_ENCODED_BIT_SIZE) {\n        for(size_t i = 0; i < bit_count; i++) {\n            bit_lib_push_bit(data, IDTECK_ENCODED_DATA_SIZE, polarity);\n            if(protocol_idteck_can_be_decoded(data)) {\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_idteck_decoder_save(uint8_t* data_to, const uint8_t* data_from) {\n    bit_lib_copy_bits(data_to, 0, 64, data_from, 0);\n}\n\nbool protocol_idteck_decoder_feed(ProtocolIdteck* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    if(duration > (IDTECK_US_PER_BIT / 2)) {\n        if(protocol_idteck_decoder_feed_internal(level, duration, protocol->encoded_data)) {\n            protocol_idteck_decoder_save(protocol->data, protocol->encoded_data);\n            FURI_LOG_D(\"Idteck\", \"Positive\");\n            result = true;\n            return result;\n        }\n\n        if(protocol_idteck_decoder_feed_internal(\n               !level, duration, protocol->negative_encoded_data)) {\n            protocol_idteck_decoder_save(protocol->data, protocol->negative_encoded_data);\n            FURI_LOG_D(\"Idteck\", \"Negative\");\n            result = true;\n            return result;\n        }\n    }\n\n    if(duration > (IDTECK_US_PER_BIT / 4)) {\n        // Try to decode wrong phase synced data\n        if(level) {\n            duration += 120;\n        } else {\n            if(duration > 120) {\n                duration -= 120;\n            }\n        }\n\n        if(protocol_idteck_decoder_feed_internal(\n               level, duration, protocol->corrupted_encoded_data)) {\n            protocol_idteck_decoder_save(protocol->data, protocol->corrupted_encoded_data);\n            FURI_LOG_D(\"Idteck\", \"Positive Corrupted\");\n\n            result = true;\n            return result;\n        }\n\n        if(protocol_idteck_decoder_feed_internal(\n               !level, duration, protocol->corrupted_negative_encoded_data)) {\n            protocol_idteck_decoder_save(\n                protocol->data, protocol->corrupted_negative_encoded_data);\n            FURI_LOG_D(\"Idteck\", \"Negative Corrupted\");\n\n            result = true;\n            return result;\n        }\n    }\n\n    return result;\n}\n\nbool protocol_idteck_encoder_start(ProtocolIdteck* protocol) {\n    memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE);\n    *(uint32_t*)&protocol->encoded_data[0] = 0b01001011010101000100010001001001;\n    bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 32);\n\n    protocol->encoder.last_bit =\n        bit_lib_get_bit(protocol->encoded_data, IDTECK_ENCODED_BIT_SIZE - 1);\n    protocol->encoder.data_index = 0;\n    protocol->encoder.current_polarity = true;\n    protocol->encoder.pulse_phase = true;\n    protocol->encoder.bit_clock_index = 0;\n\n    return true;\n}\n\nLevelDuration protocol_idteck_encoder_yield(ProtocolIdteck* protocol) {\n    LevelDuration level_duration;\n    ProtocolIdteckEncoder* encoder = &protocol->encoder;\n\n    if(encoder->pulse_phase) {\n        level_duration = level_duration_make(encoder->current_polarity, 1);\n        encoder->pulse_phase = false;\n    } else {\n        level_duration = level_duration_make(!encoder->current_polarity, 1);\n        encoder->pulse_phase = true;\n\n        encoder->bit_clock_index++;\n        if(encoder->bit_clock_index >= IDTECK_ENCODER_PULSES_PER_BIT) {\n            encoder->bit_clock_index = 0;\n\n            bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);\n\n            if(current_bit != encoder->last_bit) {\n                encoder->current_polarity = !encoder->current_polarity;\n            }\n\n            encoder->last_bit = current_bit;\n\n            bit_lib_increment_index(encoder->data_index, IDTECK_ENCODED_BIT_SIZE);\n        }\n    }\n\n    return level_duration;\n}\n\n// factory code\nstatic uint32_t get_fc(const uint8_t* data) {\n    uint32_t fc = 0;\n    fc = bit_lib_get_bits_32(data, 0, 32);\n    return fc;\n}\n\n// card number\nstatic uint32_t get_card(const uint8_t* data) {\n    uint32_t cn = 0;\n    cn = bit_lib_get_bits_32(data, 32, 32);\n    return cn;\n}\n\nvoid protocol_idteck_render_data(ProtocolIdteck* protocol, FuriString* result) {\n    const uint32_t fc = get_fc(protocol->data);\n    const uint32_t card = get_card(protocol->data);\n\n    furi_string_printf(\n        result,\n        \"FC: %08lX\\n\"\n        \"Card: %08lX\",\n        fc,\n        card);\n}\n\nbool protocol_idteck_write_data(ProtocolIdteck* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    protocol_idteck_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 |\n                                  (2 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_idteck = {\n    .name = \"Idteck\",\n    .manufacturer = \"IDTECK\",\n    .data_size = IDTECK_DECODED_DATA_SIZE,\n    .features = LFRFIDFeaturePSK,\n    .validate_count = 6,\n    .alloc = (ProtocolAlloc)protocol_idteck_alloc,\n    .free = (ProtocolFree)protocol_idteck_free,\n    .get_data = (ProtocolGetData)protocol_idteck_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_idteck_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_idteck_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_idteck_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_idteck_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_idteck_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_idteck_render_data,\n    .write_data = (ProtocolWriteData)protocol_idteck_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_idteck.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_idteck;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_indala26.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define INDALA26_PREAMBLE_BIT_SIZE  (33)\n#define INDALA26_PREAMBLE_DATA_SIZE (5)\n\n#define INDALA26_ENCODED_BIT_SIZE (64)\n#define INDALA26_ENCODED_DATA_SIZE \\\n    (((INDALA26_ENCODED_BIT_SIZE) / 8) + INDALA26_PREAMBLE_DATA_SIZE)\n#define INDALA26_ENCODED_DATA_LAST ((INDALA26_ENCODED_BIT_SIZE) / 8)\n\n#define INDALA26_DECODED_BIT_SIZE  (28)\n#define INDALA26_DECODED_DATA_SIZE (4)\n\n#define INDALA26_US_PER_BIT             (255)\n#define INDALA26_ENCODER_PULSES_PER_BIT (16)\n\ntypedef struct {\n    uint8_t data_index;\n    uint8_t bit_clock_index;\n    bool last_bit;\n    bool current_polarity;\n    bool pulse_phase;\n} ProtocolIndalaEncoder;\n\ntypedef struct {\n    uint8_t encoded_data[INDALA26_ENCODED_DATA_SIZE];\n    uint8_t negative_encoded_data[INDALA26_ENCODED_DATA_SIZE];\n    uint8_t corrupted_encoded_data[INDALA26_ENCODED_DATA_SIZE];\n    uint8_t corrupted_negative_encoded_data[INDALA26_ENCODED_DATA_SIZE];\n\n    uint8_t data[INDALA26_DECODED_DATA_SIZE];\n    ProtocolIndalaEncoder encoder;\n} ProtocolIndala;\n\nProtocolIndala* protocol_indala26_alloc(void) {\n    ProtocolIndala* protocol = malloc(sizeof(ProtocolIndala));\n    return protocol;\n}\n\nvoid protocol_indala26_free(ProtocolIndala* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_indala26_get_data(ProtocolIndala* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_indala26_decoder_start(ProtocolIndala* protocol) {\n    memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);\n    memset(protocol->negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_indala26_check_preamble(uint8_t* data, size_t bit_index) {\n    // Preamble 10100000 00000000 00000000 00000000 1\n    if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000010100000) return false;\n    if(bit_lib_get_bit(data, bit_index + 32) != 1) return false;\n    return true;\n}\n\nstatic bool protocol_indala26_can_be_decoded(uint8_t* data) {\n    if(!protocol_indala26_check_preamble(data, 0)) return false;\n    if(!protocol_indala26_check_preamble(data, 64)) return false;\n    if(bit_lib_get_bit(data, 61) != 0) return false;\n    if(bit_lib_get_bit(data, 60) != 0) return false;\n    return true;\n}\n\nstatic bool protocol_indala26_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {\n    time += (INDALA26_US_PER_BIT / 2);\n\n    size_t bit_count = (time / INDALA26_US_PER_BIT);\n    bool result = false;\n\n    if(bit_count < INDALA26_ENCODED_BIT_SIZE) {\n        for(size_t i = 0; i < bit_count; i++) {\n            bit_lib_push_bit(data, INDALA26_ENCODED_DATA_SIZE, polarity);\n            if(protocol_indala26_can_be_decoded(data)) {\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_indala26_decoder_save(uint8_t* data_to, const uint8_t* data_from) {\n    bit_lib_copy_bits(data_to, 0, 22, data_from, 33);\n    bit_lib_copy_bits(data_to, 22, 5, data_from, 55);\n    bit_lib_copy_bits(data_to, 27, 2, data_from, 62);\n}\n\nbool protocol_indala26_decoder_feed(ProtocolIndala* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    if(duration > (INDALA26_US_PER_BIT / 2)) {\n        if(protocol_indala26_decoder_feed_internal(level, duration, protocol->encoded_data)) {\n            protocol_indala26_decoder_save(protocol->data, protocol->encoded_data);\n            FURI_LOG_D(\"Indala26\", \"Positive\");\n            result = true;\n            return result;\n        }\n\n        if(protocol_indala26_decoder_feed_internal(\n               !level, duration, protocol->negative_encoded_data)) {\n            protocol_indala26_decoder_save(protocol->data, protocol->negative_encoded_data);\n            FURI_LOG_D(\"Indala26\", \"Negative\");\n            result = true;\n            return result;\n        }\n    }\n\n    if(duration > (INDALA26_US_PER_BIT / 4)) {\n        // Try to decode wrong phase synced data\n        if(level) {\n            duration += 120;\n        } else {\n            if(duration > 120) {\n                duration -= 120;\n            }\n        }\n\n        if(protocol_indala26_decoder_feed_internal(\n               level, duration, protocol->corrupted_encoded_data)) {\n            protocol_indala26_decoder_save(protocol->data, protocol->corrupted_encoded_data);\n            FURI_LOG_D(\"Indala26\", \"Positive Corrupted\");\n\n            result = true;\n            return result;\n        }\n\n        if(protocol_indala26_decoder_feed_internal(\n               !level, duration, protocol->corrupted_negative_encoded_data)) {\n            protocol_indala26_decoder_save(\n                protocol->data, protocol->corrupted_negative_encoded_data);\n            FURI_LOG_D(\"Indala26\", \"Negative Corrupted\");\n\n            result = true;\n            return result;\n        }\n    }\n\n    return result;\n}\n\nbool protocol_indala26_encoder_start(ProtocolIndala* protocol) {\n    memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);\n    *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000010100000;\n    bit_lib_set_bit(protocol->encoded_data, 32, 1);\n    bit_lib_copy_bits(protocol->encoded_data, 33, 22, protocol->data, 0);\n    bit_lib_copy_bits(protocol->encoded_data, 55, 5, protocol->data, 22);\n    bit_lib_copy_bits(protocol->encoded_data, 62, 2, protocol->data, 27);\n\n    protocol->encoder.last_bit =\n        bit_lib_get_bit(protocol->encoded_data, INDALA26_ENCODED_BIT_SIZE - 1);\n    protocol->encoder.data_index = 0;\n    protocol->encoder.current_polarity = true;\n    protocol->encoder.pulse_phase = true;\n    protocol->encoder.bit_clock_index = 0;\n\n    return true;\n}\n\nLevelDuration protocol_indala26_encoder_yield(ProtocolIndala* protocol) {\n    LevelDuration level_duration;\n    ProtocolIndalaEncoder* encoder = &protocol->encoder;\n\n    if(encoder->pulse_phase) {\n        level_duration = level_duration_make(encoder->current_polarity, 1);\n        encoder->pulse_phase = false;\n    } else {\n        level_duration = level_duration_make(!encoder->current_polarity, 1);\n        encoder->pulse_phase = true;\n\n        encoder->bit_clock_index++;\n        if(encoder->bit_clock_index >= INDALA26_ENCODER_PULSES_PER_BIT) {\n            encoder->bit_clock_index = 0;\n\n            bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);\n\n            if(current_bit != encoder->last_bit) {\n                encoder->current_polarity = !encoder->current_polarity;\n            }\n\n            encoder->last_bit = current_bit;\n\n            bit_lib_increment_index(encoder->data_index, INDALA26_ENCODED_BIT_SIZE);\n        }\n    }\n\n    return level_duration;\n}\n\n// factory code\nstatic uint8_t get_fc(const uint8_t* data) {\n    uint8_t fc = 0;\n\n    fc = fc << 1 | bit_lib_get_bit(data, 24);\n    fc = fc << 1 | bit_lib_get_bit(data, 16);\n    fc = fc << 1 | bit_lib_get_bit(data, 11);\n    fc = fc << 1 | bit_lib_get_bit(data, 14);\n    fc = fc << 1 | bit_lib_get_bit(data, 15);\n    fc = fc << 1 | bit_lib_get_bit(data, 20);\n    fc = fc << 1 | bit_lib_get_bit(data, 6);\n    fc = fc << 1 | bit_lib_get_bit(data, 25);\n\n    return fc;\n}\n\n// card number\nstatic uint16_t get_cn(const uint8_t* data) {\n    uint16_t cn = 0;\n\n    cn = cn << 1 | bit_lib_get_bit(data, 9);\n    cn = cn << 1 | bit_lib_get_bit(data, 12);\n    cn = cn << 1 | bit_lib_get_bit(data, 10);\n    cn = cn << 1 | bit_lib_get_bit(data, 7);\n    cn = cn << 1 | bit_lib_get_bit(data, 19);\n    cn = cn << 1 | bit_lib_get_bit(data, 3);\n    cn = cn << 1 | bit_lib_get_bit(data, 2);\n    cn = cn << 1 | bit_lib_get_bit(data, 18);\n    cn = cn << 1 | bit_lib_get_bit(data, 13);\n    cn = cn << 1 | bit_lib_get_bit(data, 0);\n    cn = cn << 1 | bit_lib_get_bit(data, 4);\n    cn = cn << 1 | bit_lib_get_bit(data, 21);\n    cn = cn << 1 | bit_lib_get_bit(data, 23);\n    cn = cn << 1 | bit_lib_get_bit(data, 26);\n    cn = cn << 1 | bit_lib_get_bit(data, 17);\n    cn = cn << 1 | bit_lib_get_bit(data, 8);\n\n    return cn;\n}\n\nvoid protocol_indala26_render_data_internal(\n    ProtocolIndala* protocol,\n    FuriString* result,\n    bool brief) {\n    bool wiegand_correct = true;\n    bool checksum_correct = true;\n\n    const uint8_t fc = get_fc(protocol->data);\n    const uint16_t card = get_cn(protocol->data);\n    const uint32_t fc_and_card = fc << 16 | card;\n    const uint8_t checksum = bit_lib_get_bit(protocol->data, 27) << 1 |\n                             bit_lib_get_bit(protocol->data, 28);\n    const bool even_parity = bit_lib_get_bit(protocol->data, 1);\n    const bool odd_parity = bit_lib_get_bit(protocol->data, 5);\n\n    // indala checksum\n    uint8_t checksum_sum = 0;\n    checksum_sum += ((fc_and_card >> 14) & 1);\n    checksum_sum += ((fc_and_card >> 12) & 1);\n    checksum_sum += ((fc_and_card >> 9) & 1);\n    checksum_sum += ((fc_and_card >> 8) & 1);\n    checksum_sum += ((fc_and_card >> 6) & 1);\n    checksum_sum += ((fc_and_card >> 5) & 1);\n    checksum_sum += ((fc_and_card >> 2) & 1);\n    checksum_sum += ((fc_and_card >> 0) & 1);\n    checksum_sum = checksum_sum & 0b1;\n\n    if(checksum_sum == 1 && checksum == 0b01) {\n    } else if(checksum_sum == 0 && checksum == 0b10) {\n    } else {\n        checksum_correct = false;\n    }\n\n    // wiegand parity\n    uint8_t even_parity_sum = 0;\n    for(int8_t i = 12; i < 24; i++) {\n        if(((fc_and_card >> i) & 1) == 1) {\n            even_parity_sum++;\n        }\n    }\n    if(even_parity_sum % 2 != even_parity) wiegand_correct = false;\n\n    uint8_t odd_parity_sum = 1;\n    for(int8_t i = 0; i < 12; i++) {\n        if(((fc_and_card >> i) & 1) == 1) {\n            odd_parity_sum++;\n        }\n    }\n    if(odd_parity_sum % 2 != odd_parity) wiegand_correct = false;\n\n    if(brief) {\n        furi_string_printf(\n            result,\n            \"FC: %u\\n\"\n            \"Card: %u\",\n            fc,\n            card);\n    } else {\n        furi_string_printf(\n            result,\n            \"FC: %u\\n\"\n            \"Card: %u\\n\"\n            \"Parity: %c\\n\"\n            \"Checksum: %c\",\n            fc,\n            card,\n            (wiegand_correct ? '+' : '-'),\n            (checksum_correct ? '+' : '-'));\n    }\n}\nvoid protocol_indala26_render_data(ProtocolIndala* protocol, FuriString* result) {\n    protocol_indala26_render_data_internal(protocol, result, false);\n}\nvoid protocol_indala26_render_brief_data(ProtocolIndala* protocol, FuriString* result) {\n    protocol_indala26_render_data_internal(protocol, result, true);\n}\n\nbool protocol_indala26_write_data(ProtocolIndala* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    protocol_indala26_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 |\n                                  (2 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_indala26 = {\n    .name = \"Indala26\",\n    .manufacturer = \"Motorola\",\n    .data_size = INDALA26_DECODED_DATA_SIZE,\n    .features = LFRFIDFeaturePSK,\n    .validate_count = 6,\n    .alloc = (ProtocolAlloc)protocol_indala26_alloc,\n    .free = (ProtocolFree)protocol_indala26_free,\n    .get_data = (ProtocolGetData)protocol_indala26_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_indala26_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_indala26_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_indala26_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_indala26_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_indala26_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_indala26_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_indala26_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_indala26.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_indala26;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_io_prox_xsf.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define IOPROXXSF_DECODED_DATA_SIZE (4)\n#define IOPROXXSF_ENCODED_DATA_SIZE (8)\n\n#define IOPROXXSF_BIT_SIZE     (8)\n#define IOPROXXSF_BIT_MAX_SIZE (IOPROXXSF_BIT_SIZE * IOPROXXSF_ENCODED_DATA_SIZE)\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolIOProxXSFDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n} ProtocolIOProxXSFEncoder;\n\ntypedef struct {\n    ProtocolIOProxXSFEncoder encoder;\n    ProtocolIOProxXSFDecoder decoder;\n    uint8_t encoded_data[IOPROXXSF_ENCODED_DATA_SIZE];\n    uint8_t data[IOPROXXSF_DECODED_DATA_SIZE];\n} ProtocolIOProxXSF;\n\nProtocolIOProxXSF* protocol_io_prox_xsf_alloc(void) {\n    ProtocolIOProxXSF* protocol = malloc(sizeof(ProtocolIOProxXSF));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 8, MAX_TIME, 6);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 64);\n    return protocol;\n}\n\nvoid protocol_io_prox_xsf_free(ProtocolIOProxXSF* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_io_prox_xsf_get_data(ProtocolIOProxXSF* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_io_prox_xsf_decoder_start(ProtocolIOProxXSF* protocol) {\n    memset(protocol->encoded_data, 0, IOPROXXSF_ENCODED_DATA_SIZE);\n}\n\nstatic uint8_t protocol_io_prox_xsf_compute_checksum(const uint8_t* data) {\n    // Packet structure:\n    //\n    //0        1        2         3         4         5         6         7\n    //v        v        v         v         v         v         v         v\n    //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF\n    //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11\n    //\n    // algorithm as observed by the proxmark3 folks\n    // CHECKSUM == 0xFF - (V + W + X + Y + Z)\n\n    uint8_t checksum = 0;\n\n    for(size_t i = 1; i <= 5; i++) {\n        checksum += bit_lib_get_bits(data, 9 * i, 8);\n    }\n\n    return 0xFF - checksum;\n}\n\nstatic bool protocol_io_prox_xsf_can_be_decoded(const uint8_t* encoded_data) {\n    // Packet framing\n    //\n    //0        1        2        3        4        5        6        7\n    //v        v        v        v        v        v        v        v\n    //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF\n    //-----------------------------------------------------------------------\n    //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11\n    //\n    // _ = variable data\n    // 0 = preamble 0\n    // 1 = framing 1\n    // X = checksum\n\n    // Validate the packet preamble is there...\n    if(encoded_data[0] != 0b00000000) {\n        return false;\n    }\n    if((encoded_data[1] >> 6) != 0b01) {\n        return false;\n    }\n\n    // ... check for known ones...\n    if(bit_lib_bit_is_not_set(encoded_data[2], 6)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[3], 5)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[4], 4)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[5], 3)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[6], 2)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[7], 1)) {\n        return false;\n    }\n    if(bit_lib_bit_is_not_set(encoded_data[7], 0)) {\n        return false;\n    }\n\n    // ... and validate our checksums.\n    uint8_t checksum = protocol_io_prox_xsf_compute_checksum(encoded_data);\n    uint8_t checkval = bit_lib_get_bits(encoded_data, 54, 8);\n\n    if(checksum != checkval) {\n        return false;\n    }\n\n    return true;\n}\n\nvoid protocol_io_prox_xsf_decode(const uint8_t* encoded_data, uint8_t* decoded_data) {\n    // Packet structure:\n    // (Note: the second word seems fixed; but this may not be a guarantee;\n    //  it currently has no meaning.)\n    //\n    //0        1        2        3        4        5        6        7\n    //v        v        v        v        v        v        v        v\n    //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF\n    //-----------------------------------------------------------------------\n    //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11\n    //\n    // F = facility code\n    // V = version\n    // C = code\n    // X = checksum\n\n    // Facility code\n    decoded_data[0] = bit_lib_get_bits(encoded_data, 18, 8);\n\n    // Version code.\n    decoded_data[1] = bit_lib_get_bits(encoded_data, 27, 8);\n\n    // Code bytes.\n    decoded_data[2] = bit_lib_get_bits(encoded_data, 36, 8);\n    decoded_data[3] = bit_lib_get_bits(encoded_data, 45, 8);\n}\n\nbool protocol_io_prox_xsf_decoder_feed(ProtocolIOProxXSF* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    uint32_t count;\n    bool value;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    for(size_t i = 0; i < count; i++) {\n        bit_lib_push_bit(protocol->encoded_data, IOPROXXSF_ENCODED_DATA_SIZE, value);\n        if(protocol_io_prox_xsf_can_be_decoded(protocol->encoded_data)) {\n            protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data);\n            result = true;\n            break;\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_io_prox_xsf_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {\n    // Packet to transmit:\n    //\n    // 0           10          20          30          40          50          60\n    // v           v           v           v           v           v           v\n    // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23\n    // -----------------------------------------------------------------------------\n    // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11\n\n    // Preamble.\n    bit_lib_set_bits(encoded_data, 0, 0b00000000, 8);\n    bit_lib_set_bit(encoded_data, 8, 0);\n\n    bit_lib_set_bits(encoded_data, 9, 0b11110000, 8);\n    bit_lib_set_bit(encoded_data, 17, 1);\n\n    // Facility code.\n    bit_lib_set_bits(encoded_data, 18, decoded_data[0], 8);\n    bit_lib_set_bit(encoded_data, 26, 1);\n\n    // Version\n    bit_lib_set_bits(encoded_data, 27, decoded_data[1], 8);\n    bit_lib_set_bit(encoded_data, 35, 1);\n\n    // Code one\n    bit_lib_set_bits(encoded_data, 36, decoded_data[2], 8);\n    bit_lib_set_bit(encoded_data, 44, 1);\n\n    // Code two\n    bit_lib_set_bits(encoded_data, 45, decoded_data[3], 8);\n    bit_lib_set_bit(encoded_data, 53, 1);\n\n    // Checksum\n    bit_lib_set_bits(encoded_data, 54, protocol_io_prox_xsf_compute_checksum(encoded_data), 8);\n    bit_lib_set_bit(encoded_data, 62, 1);\n    bit_lib_set_bit(encoded_data, 63, 1);\n}\n\nbool protocol_io_prox_xsf_encoder_start(ProtocolIOProxXSF* protocol) {\n    protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);\n    protocol->encoder.encoded_index = 0;\n    fsk_osc_reset(protocol->encoder.fsk_osc);\n    return true;\n}\n\nLevelDuration protocol_io_prox_xsf_encoder_yield(ProtocolIOProxXSF* protocol) {\n    bool level;\n    uint32_t duration;\n\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n    bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration);\n\n    if(advance) {\n        bit_lib_increment_index(protocol->encoder.encoded_index, IOPROXXSF_BIT_MAX_SIZE);\n    }\n    return level_duration_make(level, duration);\n}\n\nvoid protocol_io_prox_xsf_render_data(ProtocolIOProxXSF* protocol, FuriString* result) {\n    uint8_t* data = protocol->data;\n    furi_string_printf(\n        result,\n        \"FC: %hhu\\n\"\n        \"V: %hhu\\n\"\n        \"Card: %hu\",\n        data[0],\n        data[1],\n        (uint16_t)((data[2] << 8) | (data[3])));\n}\n\nvoid protocol_io_prox_xsf_render_brief_data(ProtocolIOProxXSF* protocol, FuriString* result) {\n    uint8_t* data = protocol->data;\n    furi_string_printf(\n        result,\n        \"FC: %hhu, V: %hhu\\n\"\n        \"Card: %hu\",\n        data[0],\n        data[1],\n        (uint16_t)((data[2] << 8) | (data[3])));\n}\n\nbool protocol_io_prox_xsf_write_data(ProtocolIOProxXSF* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);\n    protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data);\n\n    protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_64 |\n                                  (2 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_io_prox_xsf = {\n    .name = \"IoProxXSF\",\n    .manufacturer = \"Kantech\",\n    .data_size = IOPROXXSF_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_io_prox_xsf_alloc,\n    .free = (ProtocolFree)protocol_io_prox_xsf_free,\n    .get_data = (ProtocolGetData)protocol_io_prox_xsf_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_io_prox_xsf_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_io_prox_xsf_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_io_prox_xsf_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_io_prox_xsf_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_io_prox_xsf_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_io_prox_xsf_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_io_prox_xsf_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_io_prox_xsf.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_io_prox_xsf;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_jablotron.c",
    "content": "#include <furi.h>\n#include \"toolbox/level_duration.h\"\n#include \"protocol_jablotron.h\"\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define JABLOTRON_ENCODED_BIT_SIZE   (64)\n#define JABLOTRON_ENCODED_BYTE_SIZE  (((JABLOTRON_ENCODED_BIT_SIZE) / 8))\n#define JABLOTRON_PREAMBLE_BIT_SIZE  (16)\n#define JABLOTRON_PREAMBLE_BYTE_SIZE (2)\n#define JABLOTRON_ENCODED_BYTE_FULL_SIZE \\\n    (JABLOTRON_ENCODED_BYTE_SIZE + JABLOTRON_PREAMBLE_BYTE_SIZE)\n\n#define JABLOTRON_DECODED_DATA_SIZE (5)\n\n#define JABLOTRON_SHORT_TIME  (256)\n#define JABLOTRON_LONG_TIME   (512)\n#define JABLOTRON_JITTER_TIME (120)\n\n#define JABLOTRON_SHORT_TIME_LOW  (JABLOTRON_SHORT_TIME - JABLOTRON_JITTER_TIME)\n#define JABLOTRON_SHORT_TIME_HIGH (JABLOTRON_SHORT_TIME + JABLOTRON_JITTER_TIME)\n#define JABLOTRON_LONG_TIME_LOW   (JABLOTRON_LONG_TIME - JABLOTRON_JITTER_TIME)\n#define JABLOTRON_LONG_TIME_HIGH  (JABLOTRON_LONG_TIME + JABLOTRON_JITTER_TIME)\n\ntypedef struct {\n    bool last_short;\n    bool last_level;\n    size_t encoded_index;\n    uint8_t encoded_data[JABLOTRON_ENCODED_BYTE_FULL_SIZE];\n    uint8_t data[JABLOTRON_DECODED_DATA_SIZE];\n} ProtocolJablotron;\n\nProtocolJablotron* protocol_jablotron_alloc(void) {\n    ProtocolJablotron* protocol = malloc(sizeof(ProtocolJablotron));\n    return protocol;\n}\n\nvoid protocol_jablotron_free(ProtocolJablotron* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_jablotron_get_data(ProtocolJablotron* proto) {\n    return proto->data;\n}\n\nvoid protocol_jablotron_decoder_start(ProtocolJablotron* protocol) {\n    memset(protocol->encoded_data, 0, JABLOTRON_ENCODED_BYTE_FULL_SIZE);\n    protocol->last_short = false;\n}\n\nuint8_t protocol_jablotron_checksum(uint8_t* bits) {\n    uint8_t chksum = 0;\n    for(uint8_t i = 16; i < 56; i += 8) {\n        chksum += bit_lib_get_bits(bits, i, 8);\n    }\n    chksum ^= 0x3A;\n    return chksum;\n}\n\nuint64_t protocol_jablotron_card_id(uint8_t* bytes) {\n    uint64_t id = 0;\n    for(int i = 0; i < 5; i++) {\n        id *= 100;\n        id += ((bytes[i] & 0xF0) >> 4) * 10 + (bytes[i] & 0x0F);\n    }\n    return id;\n}\n\nstatic bool protocol_jablotron_can_be_decoded(ProtocolJablotron* protocol) {\n    // check 11 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b1111111111111111) return false;\n    // check next 11 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 64, 16) != 0b1111111111111111) return false;\n\n    uint8_t checksum = bit_lib_get_bits(protocol->encoded_data, 56, 8);\n    if(checksum != protocol_jablotron_checksum(protocol->encoded_data)) return false;\n\n    return true;\n}\n\nvoid protocol_jablotron_decode(ProtocolJablotron* protocol) {\n    bit_lib_copy_bits(protocol->data, 0, 40, protocol->encoded_data, 16);\n}\n\nbool protocol_jablotron_decoder_feed(ProtocolJablotron* protocol, bool level, uint32_t duration) {\n    UNUSED(level);\n    bool pushed = false;\n\n    // Bi-Phase Manchester decoding\n    if(duration >= JABLOTRON_SHORT_TIME_LOW && duration <= JABLOTRON_SHORT_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            protocol->last_short = true;\n        } else {\n            pushed = true;\n            bit_lib_push_bit(protocol->encoded_data, JABLOTRON_ENCODED_BYTE_FULL_SIZE, false);\n            protocol->last_short = false;\n        }\n    } else if(duration >= JABLOTRON_LONG_TIME_LOW && duration <= JABLOTRON_LONG_TIME_HIGH) {\n        if(protocol->last_short == false) {\n            pushed = true;\n            bit_lib_push_bit(protocol->encoded_data, JABLOTRON_ENCODED_BYTE_FULL_SIZE, true);\n        } else {\n            // reset\n            protocol->last_short = false;\n        }\n    } else {\n        // reset\n        protocol->last_short = false;\n    }\n\n    if(pushed && protocol_jablotron_can_be_decoded(protocol)) {\n        protocol_jablotron_decode(protocol);\n        return true;\n    }\n\n    return false;\n}\n\nbool protocol_jablotron_encoder_start(ProtocolJablotron* protocol) {\n    // preamble\n    bit_lib_set_bits(protocol->encoded_data, 0, 0b11111111, 8);\n    bit_lib_set_bits(protocol->encoded_data, 8, 0b11111111, 8);\n\n    // Full code\n    bit_lib_copy_bits(protocol->encoded_data, 16, 40, protocol->data, 0);\n\n    // Checksum\n    bit_lib_set_bits(\n        protocol->encoded_data, 56, protocol_jablotron_checksum(protocol->encoded_data), 8);\n\n    protocol->encoded_index = 0;\n    protocol->last_short = false;\n    protocol->last_level = false;\n    return true;\n}\n\nLevelDuration protocol_jablotron_encoder_yield(ProtocolJablotron* protocol) {\n    uint32_t duration;\n    protocol->last_level = !protocol->last_level;\n\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index);\n\n    // Bi-Phase Manchester encoder\n    if(bit) {\n        // one long pulse for 1\n        duration = JABLOTRON_LONG_TIME / 8;\n        bit_lib_increment_index(protocol->encoded_index, JABLOTRON_ENCODED_BIT_SIZE);\n    } else {\n        // two short pulses for 0\n        duration = JABLOTRON_SHORT_TIME / 8;\n        if(protocol->last_short) {\n            bit_lib_increment_index(protocol->encoded_index, JABLOTRON_ENCODED_BIT_SIZE);\n            protocol->last_short = false;\n        } else {\n            protocol->last_short = true;\n        }\n    }\n\n    return level_duration_make(protocol->last_level, duration);\n}\n\nvoid protocol_jablotron_render_data(ProtocolJablotron* protocol, FuriString* result) {\n    uint64_t id = protocol_jablotron_card_id(protocol->data);\n    furi_string_printf(result, \"Card: %llX\", id);\n}\n\nbool protocol_jablotron_write_data(ProtocolJablotron* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_jablotron_encoder_start(protocol);\n    protocol_jablotron_decode(protocol);\n\n    protocol_jablotron_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_64 |\n                                  (2 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_jablotron = {\n    .name = \"Jablotron\",\n    .manufacturer = \"Jablotron\",\n    .data_size = JABLOTRON_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_jablotron_alloc,\n    .free = (ProtocolFree)protocol_jablotron_free,\n    .get_data = (ProtocolGetData)protocol_jablotron_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_jablotron_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_jablotron_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_jablotron_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_jablotron_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_jablotron_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_jablotron_render_data,\n    .write_data = (ProtocolWriteData)protocol_jablotron_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_jablotron.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_jablotron;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_keri.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define KERI_PREAMBLE_BIT_SIZE  (33)\n#define KERI_PREAMBLE_DATA_SIZE (5)\n\n#define KERI_ENCODED_BIT_SIZE  (64)\n#define KERI_ENCODED_DATA_SIZE (((KERI_ENCODED_BIT_SIZE) / 8) + KERI_PREAMBLE_DATA_SIZE)\n#define KERI_ENCODED_DATA_LAST ((KERI_ENCODED_BIT_SIZE) / 8)\n\n#define KERI_DECODED_BIT_SIZE  (28)\n#define KERI_DECODED_DATA_SIZE (4)\n\n#define KERI_US_PER_BIT             (255)\n#define KERI_ENCODER_PULSES_PER_BIT (16)\n\ntypedef struct {\n    uint8_t data_index;\n    uint8_t bit_clock_index;\n    bool last_bit;\n    bool current_polarity;\n    bool pulse_phase;\n} ProtocolKeriEncoder;\n\ntypedef struct {\n    uint8_t encoded_data[KERI_ENCODED_DATA_SIZE];\n    uint8_t negative_encoded_data[KERI_ENCODED_DATA_SIZE];\n    uint8_t corrupted_encoded_data[KERI_ENCODED_DATA_SIZE];\n    uint8_t corrupted_negative_encoded_data[KERI_ENCODED_DATA_SIZE];\n\n    uint8_t data[KERI_DECODED_DATA_SIZE];\n    ProtocolKeriEncoder encoder;\n} ProtocolKeri;\n\nProtocolKeri* protocol_keri_alloc(void) {\n    ProtocolKeri* protocol = malloc(sizeof(ProtocolKeri));\n    return protocol;\n}\n\nvoid protocol_keri_free(ProtocolKeri* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_keri_get_data(ProtocolKeri* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_keri_decoder_start(ProtocolKeri* protocol) {\n    memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE);\n    memset(protocol->negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_encoded_data, 0, KERI_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_keri_check_preamble(uint8_t* data, size_t bit_index) {\n    // Preamble 11100000 00000000 00000000 00000000 1\n    if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000011100000) return false;\n    if(bit_lib_get_bit(data, bit_index + 32) != 1) return false;\n    return true;\n}\n\nstatic bool protocol_keri_can_be_decoded(uint8_t* data) {\n    if(!protocol_keri_check_preamble(data, 0)) return false;\n    if(!protocol_keri_check_preamble(data, 64)) return false;\n    ///if(bit_lib_get_bit(data, 61) != 0) return false;\n    //if(bit_lib_get_bit(data, 60) != 0) return false;\n    return true;\n}\n\nstatic bool protocol_keri_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {\n    time += (KERI_US_PER_BIT / 2);\n\n    size_t bit_count = (time / KERI_US_PER_BIT);\n    bool result = false;\n\n    if(bit_count < KERI_ENCODED_BIT_SIZE) {\n        for(size_t i = 0; i < bit_count; i++) {\n            bit_lib_push_bit(data, KERI_ENCODED_DATA_SIZE, polarity);\n            if(protocol_keri_can_be_decoded(data)) {\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_keri_descramble(uint32_t* fc, uint32_t* cn, uint32_t* internal_id) {\n    const uint8_t card_to_id[] = {255, 255, 255, 255, 13, 12, 20, 5,   16,  6,  21,\n                                  17,  8,   255, 0,   7,  10, 15, 255, 11,  4,  1,\n                                  255, 18,  255, 19,  2,  14, 3,  9,   255, 255};\n\n    const uint8_t card_to_fc[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n                                  255, 255, 0,   255, 255, 255, 255, 2,   255, 255, 255,\n                                  3,   255, 4,   255, 255, 255, 255, 255, 1,   255};\n\n    *fc = 0;\n    *cn = 0;\n    for(uint8_t card_idx = 0; card_idx < 32; card_idx++) {\n        bool bit = (*internal_id >> card_idx) & 1;\n        // Card ID\n        if(card_to_id[card_idx] < 32) {\n            *cn = *cn | (bit << card_to_id[card_idx]);\n        }\n        // Card FC\n        if(card_to_fc[card_idx] < 32) {\n            *fc = *fc | (bit << card_to_fc[card_idx]);\n        }\n    }\n}\n\nstatic void protocol_keri_decoder_save(uint8_t* data_to, const uint8_t* data_from) {\n    uint32_t id = bit_lib_get_bits_32(data_from, 32, 32);\n    data_to[3] = (uint8_t)id;\n    data_to[2] = (uint8_t)(id >>= 8);\n    data_to[1] = (uint8_t)(id >>= 8);\n    data_to[0] = (uint8_t)(id >>= 8);\n}\n\nbool protocol_keri_decoder_feed(ProtocolKeri* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    if(duration > (KERI_US_PER_BIT / 2)) {\n        if(protocol_keri_decoder_feed_internal(level, duration, protocol->encoded_data)) {\n            protocol_keri_decoder_save(protocol->data, protocol->encoded_data);\n            result = true;\n            return result;\n        }\n\n        if(protocol_keri_decoder_feed_internal(!level, duration, protocol->negative_encoded_data)) {\n            protocol_keri_decoder_save(protocol->data, protocol->negative_encoded_data);\n            result = true;\n            return result;\n        }\n    }\n\n    if(duration > (KERI_US_PER_BIT / 4)) {\n        // Try to decode wrong phase synced data\n        if(level) {\n            duration += 120;\n        } else {\n            if(duration > 120) {\n                duration -= 120;\n            }\n        }\n\n        if(protocol_keri_decoder_feed_internal(level, duration, protocol->corrupted_encoded_data)) {\n            protocol_keri_decoder_save(protocol->data, protocol->corrupted_encoded_data);\n\n            result = true;\n            return result;\n        }\n\n        if(protocol_keri_decoder_feed_internal(\n               !level, duration, protocol->corrupted_negative_encoded_data)) {\n            protocol_keri_decoder_save(protocol->data, protocol->corrupted_negative_encoded_data);\n\n            result = true;\n            return result;\n        }\n    }\n\n    return result;\n}\n\nbool protocol_keri_encoder_start(ProtocolKeri* protocol) {\n    memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE);\n    *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000011100000;\n    bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0);\n    bit_lib_set_bits(protocol->encoded_data, 32, 1, 1);\n\n    protocol->encoder.last_bit =\n        bit_lib_get_bit(protocol->encoded_data, KERI_ENCODED_BIT_SIZE - 1);\n    protocol->encoder.data_index = 0;\n    protocol->encoder.current_polarity = true;\n    protocol->encoder.pulse_phase = true;\n    protocol->encoder.bit_clock_index = 0;\n\n    return true;\n}\n\nLevelDuration protocol_keri_encoder_yield(ProtocolKeri* protocol) {\n    LevelDuration level_duration;\n    ProtocolKeriEncoder* encoder = &protocol->encoder;\n\n    if(encoder->pulse_phase) {\n        level_duration = level_duration_make(encoder->current_polarity, 1);\n        encoder->pulse_phase = false;\n    } else {\n        level_duration = level_duration_make(!encoder->current_polarity, 1);\n        encoder->pulse_phase = true;\n\n        encoder->bit_clock_index++;\n        if(encoder->bit_clock_index >= KERI_ENCODER_PULSES_PER_BIT) {\n            encoder->bit_clock_index = 0;\n\n            bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);\n\n            if(current_bit != encoder->last_bit) {\n                encoder->current_polarity = !encoder->current_polarity;\n            }\n\n            encoder->last_bit = current_bit;\n\n            bit_lib_increment_index(encoder->data_index, KERI_ENCODED_BIT_SIZE);\n        }\n    }\n\n    return level_duration;\n}\n\nstatic void\n    protocol_keri_render_data_internal(ProtocolKeri* protocol, FuriString* result, bool brief) {\n    uint32_t data = bit_lib_get_bits_32(protocol->data, 0, 32);\n    uint32_t internal_id = data & 0x7FFFFFFF;\n    uint32_t fc = 0;\n    uint32_t cn = 0;\n    protocol_keri_descramble(&fc, &cn, &data);\n\n    if(brief) {\n        furi_string_printf(\n            result,\n            \"Internal ID: %lu\\n\"\n            \"FC: %lu; Card: %lu\",\n            internal_id,\n            fc,\n            cn);\n    } else {\n        furi_string_printf(\n            result,\n            \"Internal ID: %lu\\n\"\n            \"FC: %lu\\n\"\n            \"Card: %lu\",\n            internal_id,\n            fc,\n            cn);\n    }\n}\n\nvoid protocol_keri_render_data(ProtocolKeri* protocol, FuriString* result) {\n    protocol_keri_render_data_internal(protocol, result, false);\n}\n\nvoid protocol_keri_render_brief_data(ProtocolKeri* protocol, FuriString* result) {\n    protocol_keri_render_data_internal(protocol, result, true);\n}\n\nbool protocol_keri_write_data(ProtocolKeri* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Start bit should be always set\n    protocol->data[0] |= (1 << 7);\n    protocol_keri_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_TESTMODE_DISABLED | LFRFID_T5577_X_MODE |\n                                  LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_PSKCF_RF_2 |\n                                  (2 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[0] |= 0xF << 18;\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_keri = {\n    .name = \"Keri\",\n    .manufacturer = \"Keri\",\n    .data_size = KERI_DECODED_DATA_SIZE,\n    .features = LFRFIDFeaturePSK,\n    .validate_count = 6,\n    .alloc = (ProtocolAlloc)protocol_keri_alloc,\n    .free = (ProtocolFree)protocol_keri_free,\n    .get_data = (ProtocolGetData)protocol_keri_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_keri_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_keri_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_keri_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_keri_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_keri_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_keri_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_keri_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_keri.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_keri;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_nexwatch.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define NEXWATCH_PREAMBLE_BIT_SIZE  (8)\n#define NEXWATCH_PREAMBLE_DATA_SIZE (1)\n\n#define NEXWATCH_ENCODED_BIT_SIZE  (96)\n#define NEXWATCH_ENCODED_DATA_SIZE ((NEXWATCH_ENCODED_BIT_SIZE) / 8)\n\n#define NEXWATCH_DECODED_BIT_SIZE  (NEXWATCH_DECODED_DATA_SIZE * 8)\n#define NEXWATCH_DECODED_DATA_SIZE (8)\n\n#define NEXWATCH_US_PER_BIT             (255)\n#define NEXWATCH_ENCODER_PULSES_PER_BIT (16)\n\ntypedef struct {\n    uint8_t magic;\n    char desc[13];\n    uint8_t chk;\n} ProtocolNexwatchMagic;\n\nstatic ProtocolNexwatchMagic magic_items[] = {\n    {0xBE, \"Quadrakey\", 0},\n    {0x88, \"Nexkey\", 0},\n    {0x86, \"Honeywell\", 0}};\n\ntypedef struct {\n    uint8_t data_index;\n    uint8_t bit_clock_index;\n    bool last_bit;\n    bool current_polarity;\n    bool pulse_phase;\n} ProtocolNexwatchEncoder;\n\ntypedef struct {\n    uint8_t encoded_data[NEXWATCH_ENCODED_DATA_SIZE];\n    uint8_t negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE];\n    uint8_t corrupted_encoded_data[NEXWATCH_ENCODED_DATA_SIZE];\n    uint8_t corrupted_negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE];\n\n    uint8_t data[NEXWATCH_DECODED_DATA_SIZE];\n    ProtocolNexwatchEncoder encoder;\n} ProtocolNexwatch;\n\nProtocolNexwatch* protocol_nexwatch_alloc(void) {\n    ProtocolNexwatch* protocol = malloc(sizeof(ProtocolNexwatch));\n    return protocol;\n}\n\nvoid protocol_nexwatch_free(ProtocolNexwatch* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_nexwatch_get_data(ProtocolNexwatch* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_nexwatch_decoder_start(ProtocolNexwatch* protocol) {\n    memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE);\n    memset(protocol->negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE);\n    memset(protocol->corrupted_negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_nexwatch_check_preamble(uint8_t* data, size_t bit_index) {\n    // 01010110\n    if(bit_lib_get_bits(data, bit_index, 8) != 0b01010110) return false;\n    return true;\n}\n\nstatic uint8_t protocol_nexwatch_parity_swap(uint8_t parity) {\n    uint8_t a = ((parity >> 3) & 1);\n    a |= (((parity >> 1) & 1) << 1);\n    a |= (((parity >> 2) & 1) << 2);\n    a |= ((parity & 1) << 3);\n    return a;\n}\n\nstatic uint8_t protocol_nexwatch_parity(const uint8_t hexid[5]) {\n    uint8_t p = 0;\n    for(uint8_t i = 0; i < 5; i++) {\n        p ^= ((hexid[i]) & 0xF0) >> 4;\n        p ^= ((hexid[i]) & 0x0F);\n    }\n    return protocol_nexwatch_parity_swap(p);\n}\n\nstatic uint8_t protocol_nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) {\n    uint8_t a = ((id >> 24) & 0xFF);\n    a -= ((id >> 16) & 0xFF);\n    a -= ((id >> 8) & 0xFF);\n    a -= (id & 0xFF);\n    a -= magic;\n    a -= (bit_lib_reverse_8_fast(parity) >> 4);\n    return bit_lib_reverse_8_fast(a);\n}\n\nstatic bool protocol_nexwatch_can_be_decoded(uint8_t* data) {\n    if(!protocol_nexwatch_check_preamble(data, 0)) return false;\n\n    // Check for reserved word (32-bit)\n    if(bit_lib_get_bits_32(data, 8, 32) != 0) {\n        return false;\n    }\n\n    uint8_t parity = bit_lib_get_bits(data, 76, 4);\n\n    // parity check\n    // from 32b hex id, 4b mode\n    uint8_t hex[5] = {0};\n    for(uint8_t i = 0; i < 5; i++) {\n        hex[i] = bit_lib_get_bits(data, 40 + (i * 8), 8);\n    }\n    //mode is only 4 bits.\n    hex[4] &= 0xf0;\n    uint8_t calc_parity = protocol_nexwatch_parity(hex);\n\n    if(calc_parity != parity) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool protocol_nexwatch_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {\n    time += (NEXWATCH_US_PER_BIT / 2);\n\n    size_t bit_count = (time / NEXWATCH_US_PER_BIT);\n    bool result = false;\n\n    if(bit_count < NEXWATCH_ENCODED_BIT_SIZE) {\n        for(size_t i = 0; i < bit_count; i++) {\n            bit_lib_push_bit(data, NEXWATCH_ENCODED_DATA_SIZE, polarity);\n            if(protocol_nexwatch_can_be_decoded(data)) {\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void protocol_nexwatch_descramble(uint32_t* id, uint32_t* scrambled) {\n    // 255 = Not used/Unknown other values are the bit offset in the ID/FC values\n    const uint8_t hex_2_id[] = {31, 27, 23, 19, 15, 11, 7, 3, 30, 26, 22, 18, 14, 10, 6, 2,\n                                29, 25, 21, 17, 13, 9,  5, 1, 28, 24, 20, 16, 12, 8,  4, 0};\n\n    *id = 0;\n    for(uint8_t idx = 0; idx < 32; idx++) {\n        bool bit_state = (*scrambled >> hex_2_id[idx]) & 1;\n        *id |= (bit_state << (31 - idx));\n    }\n}\n\nstatic void protocol_nexwatch_decoder_save(uint8_t* data_to, const uint8_t* data_from) {\n    uint32_t id = bit_lib_get_bits_32(data_from, 40, 32);\n    data_to[4] = (uint8_t)id;\n    data_to[3] = (uint8_t)(id >>= 8);\n    data_to[2] = (uint8_t)(id >>= 8);\n    data_to[1] = (uint8_t)(id >>= 8);\n    data_to[0] = (uint8_t)(id >>= 8);\n    uint32_t check = bit_lib_get_bits_32(data_from, 72, 24);\n    data_to[7] = (uint8_t)check;\n    data_to[6] = (uint8_t)(check >>= 8);\n    data_to[5] = (uint8_t)(check >>= 8);\n}\n\nbool protocol_nexwatch_decoder_feed(ProtocolNexwatch* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    if(duration > (NEXWATCH_US_PER_BIT / 2)) {\n        if(protocol_nexwatch_decoder_feed_internal(level, duration, protocol->encoded_data)) {\n            protocol_nexwatch_decoder_save(protocol->data, protocol->encoded_data);\n            result = true;\n            return result;\n        }\n\n        if(protocol_nexwatch_decoder_feed_internal(\n               !level, duration, protocol->negative_encoded_data)) {\n            protocol_nexwatch_decoder_save(protocol->data, protocol->negative_encoded_data);\n            result = true;\n            return result;\n        }\n    }\n\n    if(duration > (NEXWATCH_US_PER_BIT / 4)) {\n        // Try to decode wrong phase synced data\n        if(level) {\n            duration += 120;\n        } else {\n            if(duration > 120) {\n                duration -= 120;\n            }\n        }\n\n        if(protocol_nexwatch_decoder_feed_internal(\n               level, duration, protocol->corrupted_encoded_data)) {\n            protocol_nexwatch_decoder_save(protocol->data, protocol->corrupted_encoded_data);\n\n            result = true;\n            return result;\n        }\n\n        if(protocol_nexwatch_decoder_feed_internal(\n               !level, duration, protocol->corrupted_negative_encoded_data)) {\n            protocol_nexwatch_decoder_save(\n                protocol->data, protocol->corrupted_negative_encoded_data);\n\n            result = true;\n            return result;\n        }\n    }\n\n    return result;\n}\n\nbool protocol_nexwatch_encoder_start(ProtocolNexwatch* protocol) {\n    memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE);\n    *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000001010110;\n    bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0);\n    bit_lib_copy_bits(protocol->encoded_data, 64, 32, protocol->data, 32);\n\n    protocol->encoder.last_bit =\n        bit_lib_get_bit(protocol->encoded_data, NEXWATCH_ENCODED_BIT_SIZE - 1);\n    protocol->encoder.data_index = 0;\n    protocol->encoder.current_polarity = true;\n    protocol->encoder.pulse_phase = true;\n    protocol->encoder.bit_clock_index = 0;\n\n    return true;\n}\n\nLevelDuration protocol_nexwatch_encoder_yield(ProtocolNexwatch* protocol) {\n    LevelDuration level_duration;\n    ProtocolNexwatchEncoder* encoder = &protocol->encoder;\n\n    if(encoder->pulse_phase) {\n        level_duration = level_duration_make(encoder->current_polarity, 1);\n        encoder->pulse_phase = false;\n    } else {\n        level_duration = level_duration_make(!encoder->current_polarity, 1);\n        encoder->pulse_phase = true;\n\n        encoder->bit_clock_index++;\n        if(encoder->bit_clock_index >= NEXWATCH_ENCODER_PULSES_PER_BIT) {\n            encoder->bit_clock_index = 0;\n\n            bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);\n\n            if(current_bit != encoder->last_bit) {\n                encoder->current_polarity = !encoder->current_polarity;\n            }\n\n            encoder->last_bit = current_bit;\n\n            bit_lib_increment_index(encoder->data_index, NEXWATCH_ENCODED_BIT_SIZE);\n        }\n    }\n\n    return level_duration;\n}\n\nstatic void protocol_nexwatch_render_data_internal(\n    ProtocolNexwatch* protocol,\n    FuriString* result,\n    bool brief) {\n    uint32_t id = 0;\n    uint32_t scrambled = bit_lib_get_bits_32(protocol->data, 8, 32);\n    protocol_nexwatch_descramble(&id, &scrambled);\n\n    uint8_t m_idx;\n    uint8_t mode = bit_lib_get_bits(protocol->data, 40, 4);\n    uint8_t parity = bit_lib_get_bits(protocol->data, 44, 4);\n    uint8_t chk = bit_lib_get_bits(protocol->data, 48, 8);\n\n    for(m_idx = 0; m_idx < COUNT_OF(magic_items); m_idx++) {\n        magic_items[m_idx].chk = protocol_nexwatch_checksum(magic_items[m_idx].magic, id, parity);\n        if(magic_items[m_idx].chk == chk) {\n            break;\n        }\n    }\n\n    const char* type = m_idx < COUNT_OF(magic_items) ? magic_items[m_idx].desc : \"Unknown\";\n\n    if(brief) {\n        furi_string_printf(\n            result,\n            \"ID: %lu\\n\"\n            \"Mode: %hhu; Type: %s\",\n            id,\n            mode,\n            type);\n    } else {\n        furi_string_printf(\n            result,\n            \"ID: %lu\\n\"\n            \"Mode: %hhu\\n\"\n            \"Type: %s\",\n            id,\n            mode,\n            type);\n    }\n}\n\nvoid protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* result) {\n    protocol_nexwatch_render_data_internal(protocol, result, false);\n}\n\nvoid protocol_nexwatch_render_brief_data(ProtocolNexwatch* protocol, FuriString* result) {\n    protocol_nexwatch_render_data_internal(protocol, result, true);\n}\n\nbool protocol_nexwatch_write_data(ProtocolNexwatch* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    protocol_nexwatch_encoder_start(protocol);\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_BITRATE_RF_32 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_nexwatch = {\n    .name = \"Nexwatch\",\n    .manufacturer = \"Honeywell\",\n    .data_size = NEXWATCH_DECODED_DATA_SIZE,\n    .features = LFRFIDFeaturePSK,\n    .validate_count = 6,\n    .alloc = (ProtocolAlloc)protocol_nexwatch_alloc,\n    .free = (ProtocolFree)protocol_nexwatch_free,\n    .get_data = (ProtocolGetData)protocol_nexwatch_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_nexwatch_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_nexwatch_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_nexwatch_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_nexwatch_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_nexwatch_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_nexwatch_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_nexwatch_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_nexwatch.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_nexwatch;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_noralsy.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define NORALSY_CLOCK_PER_BIT (32)\n\n#define NORALSY_ENCODED_BIT_SIZE       (96)\n#define NORALSY_ENCODED_BYTE_SIZE      ((NORALSY_ENCODED_BIT_SIZE) / 8)\n#define NORALSY_PREAMBLE_BIT_SIZE      (32)\n#define NORALSY_PREAMBLE_BYTE_SIZE     ((NORALSY_PREAMBLE_BIT_SIZE) / 8)\n#define NORALSY_ENCODED_BYTE_FULL_SIZE ((NORALSY_ENCODED_BIT_SIZE) / 8)\n#define NORALSY_DECODED_DATA_SIZE      ((NORALSY_ENCODED_BIT_SIZE) / 8)\n\n#define NORALSY_READ_SHORT_TIME  (128)\n#define NORALSY_READ_LONG_TIME   (256)\n#define NORALSY_READ_JITTER_TIME (60)\n\n#define NORALSY_READ_SHORT_TIME_LOW  (NORALSY_READ_SHORT_TIME - NORALSY_READ_JITTER_TIME)\n#define NORALSY_READ_SHORT_TIME_HIGH (NORALSY_READ_SHORT_TIME + NORALSY_READ_JITTER_TIME)\n#define NORALSY_READ_LONG_TIME_LOW   (NORALSY_READ_LONG_TIME - NORALSY_READ_JITTER_TIME)\n#define NORALSY_READ_LONG_TIME_HIGH  (NORALSY_READ_LONG_TIME + NORALSY_READ_JITTER_TIME)\n\n#define TAG \"NORALSY\"\n\ntypedef struct {\n    uint8_t data[NORALSY_ENCODED_BYTE_SIZE];\n    uint8_t encoded_data[NORALSY_ENCODED_BYTE_SIZE];\n\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n\n    ManchesterState decoder_manchester_state;\n} ProtocolNoralsy;\n\nProtocolNoralsy* protocol_noralsy_alloc(void) {\n    ProtocolNoralsy* protocol = malloc(sizeof(ProtocolNoralsy));\n    return (void*)protocol;\n}\n\nvoid protocol_noralsy_free(ProtocolNoralsy* protocol) {\n    free(protocol);\n}\n\nstatic uint8_t noralsy_chksum(uint8_t* bits, uint8_t len) {\n    uint8_t sum = 0;\n    for(uint8_t i = 0; i < len; i += 4)\n        sum ^= bit_lib_get_bits(bits, i, 4);\n    return sum & 0x0F;\n}\n\nuint8_t* protocol_noralsy_get_data(ProtocolNoralsy* protocol) {\n    return protocol->data;\n}\n\nstatic void protocol_noralsy_decode(ProtocolNoralsy* protocol) {\n    bit_lib_copy_bits(protocol->data, 0, NORALSY_ENCODED_BIT_SIZE, protocol->encoded_data, 0);\n}\n\nstatic bool protocol_noralsy_can_be_decoded(ProtocolNoralsy* protocol) {\n    // check 12 bits preamble\n    // If necessary, use 0xBB0214FF for 32 bit preamble check\n    // However, it is not confirmed the 13-16 bit are static.\n    if(bit_lib_get_bits_16(protocol->encoded_data, 0, 12) != 0b101110110000) return false;\n    uint8_t calc1 = noralsy_chksum(&protocol->encoded_data[4], 40);\n    uint8_t calc2 = noralsy_chksum(&protocol->encoded_data[0], 76);\n    uint8_t chk1 = bit_lib_get_bits(protocol->encoded_data, 72, 4);\n    uint8_t chk2 = bit_lib_get_bits(protocol->encoded_data, 76, 4);\n    if(calc1 != chk1 || calc2 != chk2) return false;\n\n    return true;\n}\n\nvoid protocol_noralsy_decoder_start(ProtocolNoralsy* protocol) {\n    memset(protocol->encoded_data, 0, NORALSY_ENCODED_BYTE_FULL_SIZE);\n    manchester_advance(\n        protocol->decoder_manchester_state,\n        ManchesterEventReset,\n        &protocol->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_noralsy_decoder_feed(ProtocolNoralsy* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    ManchesterEvent event = ManchesterEventReset;\n\n    if(duration > NORALSY_READ_SHORT_TIME_LOW && duration < NORALSY_READ_SHORT_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(duration > NORALSY_READ_LONG_TIME_LOW && duration < NORALSY_READ_LONG_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);\n\n        if(data_ok) {\n            bit_lib_push_bit(protocol->encoded_data, NORALSY_ENCODED_BYTE_FULL_SIZE, data);\n\n            if(protocol_noralsy_can_be_decoded(protocol)) {\n                protocol_noralsy_decode(protocol);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nbool protocol_noralsy_encoder_start(ProtocolNoralsy* protocol) {\n    bit_lib_copy_bits(protocol->encoded_data, 0, NORALSY_ENCODED_BIT_SIZE, protocol->data, 0);\n\n    return true;\n}\n\nLevelDuration protocol_noralsy_encoder_yield(ProtocolNoralsy* protocol) {\n    bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index);\n    uint32_t duration = NORALSY_CLOCK_PER_BIT / 2;\n\n    if(protocol->encoded_polarity) {\n        protocol->encoded_polarity = false;\n    } else {\n        level = !level;\n\n        protocol->encoded_polarity = true;\n        bit_lib_increment_index(protocol->encoded_data_index, NORALSY_ENCODED_BIT_SIZE);\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_noralsy_write_data(ProtocolNoralsy* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_noralsy_encoder_start(protocol);\n    protocol_noralsy_decode(protocol);\n\n    protocol_noralsy_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] =\n            (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 |\n             (3 << LFRFID_T5577_MAXBLOCK_SHIFT) | LFRFID_T5577_ST_TERMINATOR);\n        // In fact, base on the current two dump samples from Iceman server,\n        // Noralsy are usually T5577s with config = 0x00088C6A\n        // But the `C` and `A` are not explainable by the ATA5577C datasheet\n        // and they don't affect reading whatsoever.\n        // So we are mimicing Proxmark's solution here. Leave those nibbles as zero.\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nstatic void protocol_noralsy_render_data_internal(ProtocolNoralsy* protocol, FuriString* result) {\n    UNUSED(protocol);\n    uint32_t raw2 = bit_lib_get_bits_32(protocol->data, 32, 32);\n    uint32_t raw3 = bit_lib_get_bits_32(protocol->data, 64, 32);\n    uint32_t cardid = ((raw2 & 0xFFF00000) >> 20) << 16;\n    cardid |= (raw2 & 0xFF) << 8;\n    cardid |= ((raw3 & 0xFF000000) >> 24);\n\n    uint8_t year = (raw2 & 0x000ff000) >> 12;\n    bool tag_is_gen_z = (year > 0x60);\n    furi_string_printf(\n        result,\n        \"Card ID: %07lx\\n\"\n        \"Year: %s%02x\",\n        cardid,\n        tag_is_gen_z ? \"19\" : \"20\",\n        year);\n}\n\nvoid protocol_noralsy_render_data(ProtocolNoralsy* protocol, FuriString* result) {\n    protocol_noralsy_render_data_internal(protocol, result);\n}\n\nvoid protocol_noralsy_render_brief_data(ProtocolNoralsy* protocol, FuriString* result) {\n    protocol_noralsy_render_data_internal(protocol, result);\n}\n\nconst ProtocolBase protocol_noralsy = {\n    .name = \"Noralsy\",\n    .manufacturer = \"Noralsy\",\n    .data_size = NORALSY_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_noralsy_alloc,\n    .free = (ProtocolFree)protocol_noralsy_free,\n    .get_data = (ProtocolGetData)protocol_noralsy_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_noralsy_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_noralsy_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_noralsy_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_noralsy_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_noralsy_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_noralsy_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_noralsy_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_noralsy.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_noralsy;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_pac_stanley.c",
    "content": "#include <furi.h>\n#include <math.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/hex.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define PAC_STANLEY_ENCODED_BIT_SIZE   (128)\n#define PAC_STANLEY_ENCODED_BYTE_SIZE  (((PAC_STANLEY_ENCODED_BIT_SIZE) / 8))\n#define PAC_STANLEY_PREAMBLE_BIT_SIZE  (8)\n#define PAC_STANLEY_PREAMBLE_BYTE_SIZE (1)\n#define PAC_STANLEY_ENCODED_BYTE_FULL_SIZE \\\n    (PAC_STANLEY_ENCODED_BYTE_SIZE + PAC_STANLEY_PREAMBLE_BYTE_SIZE)\n#define PAC_STANLEY_BYTE_LENGTH      (10) // start bit, 7 data bits, parity bit, stop bit\n#define PAC_STANLEY_DATA_START_INDEX (8 + (3 * PAC_STANLEY_BYTE_LENGTH) + 1)\n\n#define PAC_STANLEY_DECODED_DATA_SIZE (4)\n#define PAC_STANLEY_ENCODED_DATA_SIZE (sizeof(ProtocolPACStanley))\n\n#define PAC_STANLEY_CLOCKS_IN_US (32)\n#define PAC_STANLEY_CYCLE_LENGTH (256)\n#define PAC_STANLEY_MIN_TIME     (60)\n#define PAC_STANLEY_MAX_TIME     (4000)\n\ntypedef struct {\n    bool inverted;\n    bool got_preamble;\n    size_t encoded_index;\n    uint8_t encoded_data[PAC_STANLEY_ENCODED_BYTE_FULL_SIZE];\n    uint8_t data[PAC_STANLEY_DECODED_DATA_SIZE];\n} ProtocolPACStanley;\n\nProtocolPACStanley* protocol_pac_stanley_alloc(void) {\n    ProtocolPACStanley* protocol = malloc(sizeof(ProtocolPACStanley));\n    return (void*)protocol;\n}\n\nvoid protocol_pac_stanley_free(ProtocolPACStanley* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_pac_stanley_get_data(ProtocolPACStanley* protocol) {\n    return protocol->data;\n}\n\nstatic void protocol_pac_stanley_decode(ProtocolPACStanley* protocol) {\n    uint8_t asciiCardId[8];\n    for(size_t idx = 0; idx < 8; idx++) {\n        uint8_t byte = bit_lib_reverse_8_fast(bit_lib_get_bits(\n            protocol->encoded_data,\n            PAC_STANLEY_DATA_START_INDEX + (PAC_STANLEY_BYTE_LENGTH * idx),\n            8));\n        asciiCardId[idx] = byte & 0x7F; // discard the parity bit\n    }\n\n    hex_chars_to_uint8((char*)asciiCardId, protocol->data);\n}\n\nstatic bool protocol_pac_stanley_can_be_decoded(ProtocolPACStanley* protocol) {\n    // Check preamble\n    if(bit_lib_get_bits(protocol->encoded_data, 0, 8) != 0b11111111) return false;\n    if(bit_lib_get_bit(protocol->encoded_data, 8) != 0) return false;\n    if(bit_lib_get_bit(protocol->encoded_data, 9) != 0) return false;\n    if(bit_lib_get_bit(protocol->encoded_data, 10) != 1) return false;\n    if(bit_lib_get_bits(protocol->encoded_data, 11, 8) != 0b00000010) return false;\n\n    // Check next preamble\n    if(bit_lib_get_bits(protocol->encoded_data, 128, 8) != 0b11111111) return false;\n\n    // Checksum\n    uint8_t checksum = 0;\n    uint8_t stripped_byte;\n    for(size_t idx = 0; idx < 9; idx++) {\n        uint8_t byte = bit_lib_reverse_8_fast(bit_lib_get_bits(\n            protocol->encoded_data,\n            PAC_STANLEY_DATA_START_INDEX + (PAC_STANLEY_BYTE_LENGTH * idx),\n            8));\n        stripped_byte = byte & 0x7F; // discard the parity bit\n        if(bit_lib_test_parity_32(stripped_byte, BitLibParityOdd) != (byte & 0x80) >> 7) {\n            return false;\n        }\n        if(idx < 8) checksum ^= stripped_byte;\n    }\n    if(stripped_byte != checksum) return false;\n    return true;\n}\n\nvoid protocol_pac_stanley_decoder_start(ProtocolPACStanley* protocol) {\n    memset(protocol->data, 0, PAC_STANLEY_DECODED_DATA_SIZE);\n    protocol->inverted = false;\n    protocol->got_preamble = false;\n}\n\nbool protocol_pac_stanley_decoder_feed(ProtocolPACStanley* protocol, bool level, uint32_t duration) {\n    bool pushed = false;\n\n    if(duration > PAC_STANLEY_MAX_TIME) return false;\n\n    uint8_t pulses = (uint8_t)roundf((float)duration / PAC_STANLEY_CYCLE_LENGTH);\n\n    // Handle last stopbit & preamble (1 sb, 8 bit preamble)\n    if(pulses >= 9 && !protocol->got_preamble) {\n        pulses = 8;\n        protocol->got_preamble = true;\n        protocol->inverted = !level;\n    } else if(pulses >= 9 && protocol->got_preamble) {\n        protocol->got_preamble = false;\n    } else if(pulses == 0 && duration > PAC_STANLEY_MIN_TIME) {\n        pulses = 1;\n    }\n\n    if(pulses) {\n        for(uint8_t i = 0; i < pulses; i++) {\n            bit_lib_push_bit(\n                protocol->encoded_data,\n                PAC_STANLEY_ENCODED_BYTE_FULL_SIZE,\n                level ^ protocol->inverted);\n        }\n        pushed = true;\n    }\n\n    if(pushed && protocol_pac_stanley_can_be_decoded(protocol)) {\n        protocol_pac_stanley_decode(protocol);\n        return true;\n    }\n\n    return false;\n}\n\nbool protocol_pac_stanley_encoder_start(ProtocolPACStanley* protocol) {\n    memset(protocol->encoded_data, 0, sizeof(protocol->encoded_data));\n\n    uint8_t idbytes[10];\n    idbytes[0] = '2';\n    idbytes[1] = '0';\n\n    uint8_to_hex_chars(protocol->data, &idbytes[2], 8);\n\n    // insert start and stop bits\n    for(size_t i = 0; i < 16; i++)\n        protocol->encoded_data[i] = 0x40 >> ((i + 3) % 5 * 2);\n\n    protocol->encoded_data[0] = 0xFF; // mark + stop\n    protocol->encoded_data[1] = 0x20; // start + reflect8(STX)\n\n    uint8_t checksum = 0;\n    for(size_t i = 2; i < 13; i++) {\n        uint8_t shift = 7 - (i + 3) % 4 * 2;\n        uint8_t index = i + (i - 1) / 4;\n\n        uint16_t pattern;\n        if(i < 12) {\n            pattern = bit_lib_reverse_8_fast(idbytes[i - 2]);\n            pattern |= bit_lib_test_parity_32(pattern, BitLibParityOdd);\n            if(i > 3) checksum ^= idbytes[i - 2];\n        } else {\n            pattern = (bit_lib_reverse_8_fast(checksum) & 0xFE) |\n                      (bit_lib_test_parity_32(checksum, BitLibParityOdd));\n        }\n        pattern <<= shift;\n\n        protocol->encoded_data[index] |= pattern >> 8 & 0xFF;\n        protocol->encoded_data[index + 1] |= pattern & 0xFF;\n    }\n\n    protocol->encoded_index = 0;\n    return true;\n}\n\nLevelDuration protocol_pac_stanley_encoder_yield(ProtocolPACStanley* protocol) {\n    uint16_t length = PAC_STANLEY_CLOCKS_IN_US;\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index);\n    bit_lib_increment_index(protocol->encoded_index, PAC_STANLEY_ENCODED_BIT_SIZE);\n    while(bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index) == bit) {\n        length += PAC_STANLEY_CLOCKS_IN_US;\n        bit_lib_increment_index(protocol->encoded_index, PAC_STANLEY_ENCODED_BIT_SIZE);\n    }\n\n    return level_duration_make(bit, length);\n}\n\nbool protocol_pac_stanley_write_data(ProtocolPACStanley* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_pac_stanley_encoder_start(protocol);\n    protocol_pac_stanley_decode(protocol);\n\n    protocol_pac_stanley_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_DIRECT | LFRFID_T5577_BITRATE_RF_32 |\n                                  (4 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);\n        request->t5577.blocks_to_write = 5;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_pac_stanley_render_data(ProtocolPACStanley* protocol, FuriString* result) {\n    furi_string_printf(result, \"CIN: %08lX\", bit_lib_get_bits_32(protocol->data, 0, 32));\n}\n\nconst ProtocolBase protocol_pac_stanley = {\n    .name = \"PAC/Stanley\",\n    .manufacturer = \"N/A\",\n    .data_size = PAC_STANLEY_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_pac_stanley_alloc,\n    .free = (ProtocolFree)protocol_pac_stanley_free,\n    .get_data = (ProtocolGetData)protocol_pac_stanley_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_pac_stanley_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_pac_stanley_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_pac_stanley_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_pac_stanley_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_pac_stanley_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_pac_stanley_render_data,\n    .write_data = (ProtocolWriteData)protocol_pac_stanley_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_pac_stanley.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_pac_stanley;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_paradox.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define PARADOX_DECODED_DATA_SIZE (6)\n\n#define PARADOX_PREAMBLE_LENGTH   (8)\n#define PARADOX_ENCODED_BIT_SIZE  (96)\n#define PARADOX_ENCODED_DATA_SIZE (((PARADOX_ENCODED_BIT_SIZE) / 8) + 1)\n#define PARADOX_ENCODED_DATA_LAST (PARADOX_ENCODED_DATA_SIZE - 1)\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolParadoxDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n} ProtocolParadoxEncoder;\n\ntypedef struct {\n    ProtocolParadoxDecoder decoder;\n    ProtocolParadoxEncoder encoder;\n    uint8_t encoded_data[PARADOX_ENCODED_DATA_SIZE];\n    uint8_t data[PARADOX_DECODED_DATA_SIZE];\n} ProtocolParadox;\n\nProtocolParadox* protocol_paradox_alloc(void) {\n    ProtocolParadox* protocol = malloc(sizeof(ProtocolParadox));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_paradox_free(ProtocolParadox* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_paradox_get_data(ProtocolParadox* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_paradox_decoder_start(ProtocolParadox* protocol) {\n    memset(protocol->encoded_data, 0, PARADOX_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_paradox_can_be_decoded(ProtocolParadox* protocol) {\n    // check preamble\n    if(protocol->encoded_data[0] != 0b00001111 ||\n       protocol->encoded_data[PARADOX_ENCODED_DATA_LAST] != 0b00001111)\n        return false;\n\n    for(uint32_t i = PARADOX_PREAMBLE_LENGTH; i < 96; i += 2) {\n        if(bit_lib_get_bit(protocol->encoded_data, i) ==\n           bit_lib_get_bit(protocol->encoded_data, i + 1)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic void protocol_paradox_decode(uint8_t* encoded_data, uint8_t* decoded_data) {\n    for(uint32_t i = PARADOX_PREAMBLE_LENGTH; i < 96; i += 2) {\n        if(bit_lib_get_bits(encoded_data, i, 2) == 0b01) {\n            bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0);\n        } else if(bit_lib_get_bits(encoded_data, i, 2) == 0b10) {\n            bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 1);\n        }\n    }\n    bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0);\n    bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0);\n    bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0);\n    bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0);\n}\n\nbool protocol_paradox_decoder_feed(ProtocolParadox* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, PARADOX_ENCODED_DATA_SIZE, value);\n            if(protocol_paradox_can_be_decoded(protocol)) {\n                protocol_paradox_decode(protocol->encoded_data, protocol->data);\n\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nstatic void protocol_paradox_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {\n    // preamble\n    bit_lib_set_bits(encoded_data, 0, 0b00001111, 8);\n\n    for(size_t i = 0; i < 44; i++) {\n        if(bit_lib_get_bit(decoded_data, i)) {\n            bit_lib_set_bits(encoded_data, PARADOX_PREAMBLE_LENGTH + i * 2, 0b10, 2);\n        } else {\n            bit_lib_set_bits(encoded_data, PARADOX_PREAMBLE_LENGTH + i * 2, 0b01, 2);\n        }\n    }\n}\n\nbool protocol_paradox_encoder_start(ProtocolParadox* protocol) {\n    protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n    protocol->encoder.encoded_index = 0;\n    fsk_osc_reset(protocol->encoder.fsk_osc);\n    return true;\n}\n\nLevelDuration protocol_paradox_encoder_yield(ProtocolParadox* protocol) {\n    bool level;\n    uint32_t duration;\n\n    bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n    bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration);\n\n    if(advance) {\n        bit_lib_increment_index(protocol->encoder.encoded_index, PARADOX_ENCODED_BIT_SIZE);\n    }\n    return level_duration_make(level, duration);\n}\n\nstatic uint8_t protocol_paradox_calculate_checksum(uint8_t fc, uint16_t card_id) {\n    uint8_t card_hi = (card_id >> 8) & 0xff;\n    uint8_t card_lo = card_id & 0xff;\n\n    uint8_t arr[5] = {0, 0, fc, card_hi, card_lo};\n\n    uint8_t manchester[9];\n\n    bit_lib_push_bit(manchester, 9, false);\n    bit_lib_push_bit(manchester, 9, false);\n    bit_lib_push_bit(manchester, 9, false);\n    bit_lib_push_bit(manchester, 9, false);\n\n    for(uint8_t i = 6; i < 40; i += 1) {\n        if(bit_lib_get_bit(arr, i) == 0b1) {\n            bit_lib_push_bit(manchester, 9, true);\n            bit_lib_push_bit(manchester, 9, false);\n        } else {\n            bit_lib_push_bit(manchester, 9, false);\n            bit_lib_push_bit(manchester, 9, true);\n        }\n    }\n\n    uint8_t output = bit_lib_crc8(manchester, 9, 0x31, 0x00, true, true, 0x06);\n\n    return output;\n}\n\nvoid protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) {\n    uint8_t* decoded_data = protocol->data;\n    uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8);\n    uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16);\n    uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8);\n    uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id);\n\n    furi_string_printf(\n        result,\n        \"FC: %hhu\\n\"\n        \"Card: %hu\\n\"\n        \"CRC: %hhu\\n\"\n        \"Calc CRC: %hhu\",\n        fc,\n        card_id,\n        card_crc,\n        calc_crc);\n\n    if(card_crc != calc_crc) {\n        furi_string_cat(result, \"\\nCRC Mismatch, Invalid Card!\");\n    }\n}\n\nvoid protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) {\n    uint8_t* decoded_data = protocol->data;\n\n    uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8);\n    uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16);\n    uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8);\n    uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id);\n\n    furi_string_printf(result, \"FC: %hhu; Card: %hu\", fc, card_id);\n\n    if(calc_crc != card_crc) {\n        furi_string_cat(result, \"\\nCRC Mismatch, Invalid Card!\");\n    }\n}\n\nbool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n    protocol_paradox_decode(protocol->encoded_data, protocol->data);\n\n    protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (3 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.blocks_to_write = 4;\n        result = true;\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_paradox = {\n    .name = \"Paradox\",\n    .manufacturer = \"Paradox\",\n    .data_size = PARADOX_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_paradox_alloc,\n    .free = (ProtocolFree)protocol_paradox_free,\n    .get_data = (ProtocolGetData)protocol_paradox_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_paradox_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_paradox_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_paradox_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_paradox_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_paradox_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_paradox_render_brief_data,\n    .write_data = (ProtocolWriteData)protocol_paradox_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_paradox.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_paradox;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_pyramid.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <lfrfid/tools/fsk_demod.h>\n#include <lfrfid/tools/fsk_osc.h>\n#include \"lfrfid_protocols.h\"\n#include <bit_lib/bit_lib.h>\n\n#define JITTER_TIME (20)\n#define MIN_TIME    (64 - JITTER_TIME)\n#define MAX_TIME    (80 + JITTER_TIME)\n\n#define PYRAMID_DATA_SIZE     13\n#define PYRAMID_PREAMBLE_SIZE 3\n\n#define PYRAMID_ENCODED_DATA_SIZE \\\n    (PYRAMID_PREAMBLE_SIZE + PYRAMID_DATA_SIZE + PYRAMID_PREAMBLE_SIZE)\n#define PYRAMID_ENCODED_BIT_SIZE  ((PYRAMID_PREAMBLE_SIZE + PYRAMID_DATA_SIZE) * 8)\n#define PYRAMID_DECODED_DATA_SIZE (4)\n#define PYRAMID_DECODED_BIT_SIZE  ((PYRAMID_ENCODED_BIT_SIZE - PYRAMID_PREAMBLE_SIZE * 8) / 2)\n\ntypedef struct {\n    FSKDemod* fsk_demod;\n} ProtocolPyramidDecoder;\n\ntypedef struct {\n    FSKOsc* fsk_osc;\n    uint8_t encoded_index;\n    uint32_t pulse;\n} ProtocolPyramidEncoder;\n\ntypedef struct {\n    ProtocolPyramidDecoder decoder;\n    ProtocolPyramidEncoder encoder;\n    uint8_t encoded_data[PYRAMID_ENCODED_DATA_SIZE];\n    uint8_t data[PYRAMID_DECODED_DATA_SIZE];\n} ProtocolPyramid;\n\nProtocolPyramid* protocol_pyramid_alloc(void) {\n    ProtocolPyramid* protocol = malloc(sizeof(ProtocolPyramid));\n    protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);\n    protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);\n\n    return protocol;\n}\n\nvoid protocol_pyramid_free(ProtocolPyramid* protocol) {\n    fsk_demod_free(protocol->decoder.fsk_demod);\n    fsk_osc_free(protocol->encoder.fsk_osc);\n    free(protocol);\n}\n\nuint8_t* protocol_pyramid_get_data(ProtocolPyramid* protocol) {\n    return protocol->data;\n}\n\nvoid protocol_pyramid_decoder_start(ProtocolPyramid* protocol) {\n    memset(protocol->encoded_data, 0, PYRAMID_ENCODED_DATA_SIZE);\n}\n\nstatic bool protocol_pyramid_can_be_decoded(uint8_t* data) {\n    // check preamble\n    if(bit_lib_get_bits_16(data, 0, 16) != 0b0000000000000001 ||\n       bit_lib_get_bits(data, 16, 8) != 0b00000001) {\n        return false;\n    }\n\n    if(bit_lib_get_bits_16(data, 128, 16) != 0b0000000000000001 ||\n       bit_lib_get_bits(data, 136, 8) != 0b00000001) {\n        return false;\n    }\n\n    uint8_t checksum = bit_lib_get_bits(data, 120, 8);\n    uint8_t checksum_data[13] = {0x00};\n    for(uint8_t i = 0; i < 13; i++) {\n        checksum_data[i] = bit_lib_get_bits(data, 16 + (i * 8), 8);\n    }\n\n    uint8_t calc_checksum = bit_lib_crc8(checksum_data, 13, 0x31, 0x00, true, true, 0x00);\n    if(checksum != calc_checksum) return false;\n\n    // Remove parity\n    bit_lib_remove_bit_every_nth(data, 8, 15 * 8, 8);\n\n    // Determine Startbit and format\n    int j;\n    for(j = 0; j < 105; ++j) {\n        if(bit_lib_get_bit(data, j)) break;\n    }\n    uint8_t fmt_len = 105 - j;\n\n    // Only support 26bit format for now\n    if(fmt_len != 26) return false;\n\n    return true;\n}\n\nstatic void protocol_pyramid_decode(ProtocolPyramid* protocol) {\n    // Format\n    bit_lib_set_bits(protocol->data, 0, 26, 8);\n\n    // Facility Code\n    bit_lib_copy_bits(protocol->data, 8, 8, protocol->encoded_data, 73 + 8);\n\n    // Card Number\n    bit_lib_copy_bits(protocol->data, 16, 16, protocol->encoded_data, 81 + 8);\n}\n\nbool protocol_pyramid_decoder_feed(ProtocolPyramid* protocol, bool level, uint32_t duration) {\n    bool value;\n    uint32_t count;\n    bool result = false;\n\n    fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);\n    if(count > 0) {\n        for(size_t i = 0; i < count; i++) {\n            bit_lib_push_bit(protocol->encoded_data, PYRAMID_ENCODED_DATA_SIZE, value);\n            if(protocol_pyramid_can_be_decoded(protocol->encoded_data)) {\n                protocol_pyramid_decode(protocol);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nbool protocol_pyramid_get_parity(const uint8_t* bits, uint8_t type, int length) {\n    int x;\n    for(x = 0; length > 0; --length)\n        x += bit_lib_get_bit(bits, length - 1);\n    x %= 2;\n    return x ^ type;\n}\n\nvoid protocol_pyramid_add_wiegand_parity(\n    uint8_t* target,\n    uint8_t target_position,\n    uint8_t* source,\n    uint8_t length) {\n    bit_lib_set_bit(\n        target, target_position, protocol_pyramid_get_parity(source, 0 /* even */, length / 2));\n    bit_lib_copy_bits(target, target_position + 1, length, source, 0);\n    bit_lib_set_bit(\n        target,\n        target_position + length + 1,\n        protocol_pyramid_get_parity(source + length / 2, 1 /* odd */, length / 2));\n}\n\nstatic void protocol_pyramid_encode(ProtocolPyramid* protocol) {\n    memset(protocol->encoded_data, 0, sizeof(protocol->encoded_data));\n\n    uint8_t pre[16];\n    memset(pre, 0, sizeof(pre));\n\n    // Format start bit\n    bit_lib_set_bit(pre, 79, 1);\n\n    uint8_t wiegand[3];\n    memset(wiegand, 0, sizeof(wiegand));\n\n    // FC\n    bit_lib_copy_bits(wiegand, 0, 8, protocol->data, 8);\n\n    // CardNum\n    bit_lib_copy_bits(wiegand, 8, 16, protocol->data, 16);\n\n    // Wiegand parity\n    protocol_pyramid_add_wiegand_parity(pre, 80, wiegand, 24);\n\n    bit_lib_add_parity(pre, 8, protocol->encoded_data, 8, 102, 8, 1);\n\n    // Add checksum\n    uint8_t checksum_buffer[13];\n    for(uint8_t i = 0; i < 13; i++)\n        checksum_buffer[i] = bit_lib_get_bits(protocol->encoded_data, 16 + (i * 8), 8);\n\n    uint8_t crc = bit_lib_crc8(checksum_buffer, 13, 0x31, 0x00, true, true, 0x00);\n    bit_lib_set_bits(protocol->encoded_data, 120, crc, 8);\n}\n\nbool protocol_pyramid_encoder_start(ProtocolPyramid* protocol) {\n    protocol->encoder.encoded_index = 0;\n    protocol->encoder.pulse = 0;\n    protocol_pyramid_encode(protocol);\n\n    return true;\n}\n\nLevelDuration protocol_pyramid_encoder_yield(ProtocolPyramid* protocol) {\n    bool level = 0;\n    uint32_t duration = 0;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(protocol->encoder.pulse == 0) {\n        // get bit\n        uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);\n\n        // get pulse from oscillator\n        bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);\n\n        if(advance) {\n            bit_lib_increment_index(protocol->encoder.encoded_index, PYRAMID_ENCODED_BIT_SIZE);\n        }\n\n        // duration diveded by 2 because we need to output high and low\n        duration = duration / 2;\n        protocol->encoder.pulse = duration;\n        level = true;\n    } else {\n        // output low half and reset pulse\n        duration = protocol->encoder.pulse;\n        protocol->encoder.pulse = 0;\n        level = false;\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_pyramid_write_data(ProtocolPyramid* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_pyramid_encode(protocol);\n    bit_lib_remove_bit_every_nth(protocol->encoded_data, 8, 15 * 8, 8);\n    protocol_pyramid_decode(protocol);\n\n    protocol_pyramid_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |\n                                  (4 << LFRFID_T5577_MAXBLOCK_SHIFT);\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);\n        request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);\n        request->t5577.blocks_to_write = 5;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_pyramid_render_data(ProtocolPyramid* protocol, FuriString* result) {\n    uint8_t* decoded_data = protocol->data;\n    uint8_t format_length = decoded_data[0];\n\n    furi_string_printf(result, \"Format: %hhu\\n\", format_length);\n    if(format_length == 26) {\n        uint8_t facility;\n        bit_lib_copy_bits(&facility, 0, 8, decoded_data, 8);\n\n        uint16_t card_id;\n        bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 16);\n        bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 24);\n        furi_string_cat_printf(result, \"FC: %03hhu; Card: %05hu\", facility, card_id);\n    } else {\n        furi_string_cat_printf(result, \"Data: Unknown\");\n    }\n}\n\nconst ProtocolBase protocol_pyramid = {\n    .name = \"Pyramid\",\n    .manufacturer = \"Farpointe\",\n    .data_size = PYRAMID_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_pyramid_alloc,\n    .free = (ProtocolFree)protocol_pyramid_free,\n    .get_data = (ProtocolGetData)protocol_pyramid_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_pyramid_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_pyramid_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_pyramid_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_pyramid_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_pyramid_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_pyramid_render_data,\n    .write_data = (ProtocolWriteData)protocol_pyramid_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_pyramid.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_pyramid;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_securakey.c",
    "content": "// The timing parameters and data structure used in this file\n// are based on the knowledge found in Proxmark3's firmware:\n// https://github.com/RfidResearchGroup/proxmark3/blob/1c52152d30f7744c0336633317ea6640dbcdc796/client/src/cmdlfsecurakey.c\n// PM3's repo has mentioned the existence of non-26-or-32-bit formats.\n// Those are not supported here for preventing false positives.\n#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/hex.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n#include <toolbox/manchester_decoder.h>\n\n#define TAG \"SECURAKEY\"\n\n#define SECURAKEY_RKKT_ENCODED_FULL_SIZE_BITS (96)\n#define SECURAKEY_RKKT_ENCODED_FULL_SIZE_BYTE (12)\n\n#define SECURAKEY_RKKTH_ENCODED_FULL_SIZE_BITS (64)\n#define SECURAKEY_RKKTH_ENCODED_FULL_SIZE_BYTE (8)\n\n#define SECURAKEY_DECODED_DATA_SIZE_BITS  (48)\n// RKKT: 16-bit for facility code/number, 16-bit for card number, 16-bit for two checksum\n// RKKTH: 16-bit zero padding, 32-bit card number\n#define SECURAKEY_DECODED_DATA_SIZE_BYTES (SECURAKEY_DECODED_DATA_SIZE_BITS / 8)\n#define LFRFID_FREQUENCY                  (125000)\n#define SECURAKEY_CLOCK_PER_BIT           (40) // RF/40\n#define SECURAKEY_READ_LONG_TIME \\\n    (1000000 / (LFRFID_FREQUENCY / SECURAKEY_CLOCK_PER_BIT)) // 1000000 micro sec / sec\n#define SECURAKEY_READ_SHORT_TIME  (SECURAKEY_READ_LONG_TIME / 2)\n#define SECURAKEY_READ_JITTER_TIME (SECURAKEY_READ_SHORT_TIME * 40 / 100) // 40% jitter tolerance\n#define SECURAKEY_READ_SHORT_TIME_LOW \\\n    (SECURAKEY_READ_SHORT_TIME -      \\\n     SECURAKEY_READ_JITTER_TIME) // these are used for manchester decoding\n#define SECURAKEY_READ_SHORT_TIME_HIGH (SECURAKEY_READ_SHORT_TIME + SECURAKEY_READ_JITTER_TIME)\n#define SECURAKEY_READ_LONG_TIME_LOW   (SECURAKEY_READ_LONG_TIME - SECURAKEY_READ_JITTER_TIME)\n#define SECURAKEY_READ_LONG_TIME_HIGH  (SECURAKEY_READ_LONG_TIME + SECURAKEY_READ_JITTER_TIME)\n\ntypedef struct {\n    uint8_t data[SECURAKEY_DECODED_DATA_SIZE_BYTES];\n    uint8_t RKKT_encoded_data[SECURAKEY_RKKT_ENCODED_FULL_SIZE_BYTE];\n    uint8_t RKKTH_encoded_data[SECURAKEY_RKKTH_ENCODED_FULL_SIZE_BYTE];\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n    ManchesterState decoder_manchester_state;\n    uint8_t bit_format;\n} ProtocolSecurakey;\n\nProtocolSecurakey* protocol_securakey_alloc(void) {\n    ProtocolSecurakey* protocol = malloc(sizeof(ProtocolSecurakey));\n    return (void*)protocol;\n}\n\nvoid protocol_securakey_free(ProtocolSecurakey* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_securakey_get_data(ProtocolSecurakey* protocol) {\n    return protocol->data;\n}\n\nstatic bool protocol_securakey_can_be_decoded(ProtocolSecurakey* protocol) {\n    // check 19 bits preamble + format flag\n    if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111000000000) {\n        if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 54, BitLibParityAlways0, 9)) {\n            protocol->bit_format = 0;\n            return true;\n        }\n    } else if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111001011010) {\n        if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 90, BitLibParityAlways0, 9)) {\n            protocol->bit_format = 26;\n            return true;\n        }\n    } else if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111001100000) {\n        if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 90, BitLibParityAlways0, 9)) {\n            protocol->bit_format = 32;\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic void protocol_securakey_decode(ProtocolSecurakey* protocol) {\n    memset(protocol->data, 0, SECURAKEY_DECODED_DATA_SIZE_BYTES);\n    // RKKT_encoded_data looks like this (citation: pm3 repo):\n    // 26-bit format (1-bit even parity bit,  8-bit facility number, 16-bit card number, 1-bit odd parity bit)\n    // preamble     ??bitlen   reserved        EPf   fffffffc   cccccccc   cccccccOP  CS?        CS2?\n    // 0111111111 0 01011010 0 00000000 0 00000010 0 00110110 0 00111110 0 01100010 0 00001111 0 01100000 0 00000000 0 0000\n\n    // 32-bit format (1-bit even parity bit, 14-bit facility number, 16-bit card number, 1-bit odd parity bit)\n    // preamble     ??bitlen   reserved  EPfffffff   fffffffc   cccccccc   cccccccOP  CS?        CS2?\n    // 0111111111 0 01100000 0 00000000 0 10000100 0 11001010 0 01011011 0 01010110 0 00010110 0 11100000 0 00000000 0 0000\n\n    // RKKTH-02 encoded data sometimes look like this\n    // plaintext format (preamble and 32-bit? card number)\n    // preamble     unknown    unknown    cccccccc   cccccccc   cccccccc   cccccccc\n    // 0          1            2           3           4           5           6\n    // 0123456789 0 12345678 9 01234567 8 90123456 7 89012345 6 78901234 5 67890123\n    // 0111111111 0 00000000 0 00000000 0 00000000 0 00011101 0 00000100 0 01001010\n\n    if(bit_lib_get_bits(protocol->RKKT_encoded_data, 13, 6) == 0) {\n        FURI_LOG_D(TAG, \"Plaintext RKKTH detected\");\n        protocol->bit_format = 0;\n        // get card number (c)\n        bit_lib_copy_bits(protocol->data, 16, 8, protocol->RKKT_encoded_data, 29);\n        // skip spacers (0s)\n        bit_lib_copy_bits(protocol->data, 24, 8, protocol->RKKT_encoded_data, 38);\n        bit_lib_copy_bits(protocol->data, 32, 8, protocol->RKKT_encoded_data, 47);\n        bit_lib_copy_bits(protocol->data, 40, 8, protocol->RKKT_encoded_data, 56);\n    } else {\n        if(bit_lib_get_bits(protocol->RKKT_encoded_data, 13, 6) == 26) {\n            FURI_LOG_D(TAG, \"26-bit RKKT detected\");\n            protocol->bit_format = 26;\n            // left two 0 paddings in the beginning for easier parsing (00011010 = 011010)\n            // get facility number (f)\n            bit_lib_copy_bits(protocol->data, 8, 1, protocol->RKKT_encoded_data, 36);\n            // have to skip one spacer\n            bit_lib_copy_bits(protocol->data, 9, 7, protocol->RKKT_encoded_data, 38);\n        } else if(bit_lib_get_bits(protocol->RKKT_encoded_data, 13, 6) == 32) {\n            FURI_LOG_D(TAG, \"32-bit RKKT detected\");\n            protocol->bit_format = 32;\n            // same two 0 paddings here, otherwise should be bit_lib_copy_bits(protocol->data, 8, 7, protocol->RKKT_encoded_data, 30);\n            bit_lib_copy_bits(protocol->data, 2, 7, protocol->RKKT_encoded_data, 30);\n            // have to skip one spacer\n            bit_lib_copy_bits(protocol->data, 9, 7, protocol->RKKT_encoded_data, 38);\n        }\n        // get card number (c)\n        bit_lib_copy_bits(protocol->data, 16, 1, protocol->RKKT_encoded_data, 45);\n        // same skips here\n        bit_lib_copy_bits(protocol->data, 17, 8, protocol->RKKT_encoded_data, 47);\n        bit_lib_copy_bits(protocol->data, 25, 7, protocol->RKKT_encoded_data, 56);\n\n        // unsure about CS yet, might as well just save it\n        // CS1\n        bit_lib_copy_bits(protocol->data, 32, 8, protocol->RKKT_encoded_data, 65);\n        // CS2\n        bit_lib_copy_bits(protocol->data, 40, 8, protocol->RKKT_encoded_data, 74);\n    }\n\n    // (decoded) data looks like this (pp are zero paddings):\n    // 26-bit format (1-bit EP,  8-bit facility number, 16-bit card number, 1-bit OP)\n    // pppppppp ffffffff cccccccc cccccccc CS1      CS2\n    // 00000000 00011011 00011111 00110001 00001111 01100000\n\n    // 32-bit format (1-bit EP, 14-bit facility number, 16-bit card number, 1-bit OP)\n    // ppffffff ffffffff cccccccc cccccccc CS1      CS2\n    // 00000010 01100101 00101101 10101011 00010110 11100000\n\n    // plaintext format (preamble and 32-bit? card number)\n    // pppppppp pppppppp cccccccc cccccccc cccccccc cccccccc\n    // 00000000 00000000 00101011 00011101 00000100 01001010\n}\n\nvoid protocol_securakey_decoder_start(ProtocolSecurakey* protocol) {\n    // always takes in encoded data as RKKT for simplicity\n    // this part is feeding decoder which will delineate the format anyway\n    memset(protocol->RKKT_encoded_data, 0, SECURAKEY_RKKT_ENCODED_FULL_SIZE_BYTE);\n    manchester_advance(\n        protocol->decoder_manchester_state,\n        ManchesterEventReset,\n        &protocol->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_securakey_decoder_feed(ProtocolSecurakey* protocol, bool level, uint32_t duration) {\n    bool result = false;\n    // this is where we do manchester demodulation on already ASK-demoded data\n    ManchesterEvent event = ManchesterEventReset;\n    if(duration > SECURAKEY_READ_SHORT_TIME_LOW && duration < SECURAKEY_READ_SHORT_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(duration > SECURAKEY_READ_LONG_TIME_LOW && duration < SECURAKEY_READ_LONG_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n    // append a new bit to the encoded bit stream\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);\n        if(data_ok) {\n            bit_lib_push_bit(\n                protocol->RKKT_encoded_data, SECURAKEY_RKKT_ENCODED_FULL_SIZE_BYTE, data);\n            if(protocol_securakey_can_be_decoded(protocol)) {\n                protocol_securakey_decode(protocol);\n                result = true;\n            }\n        }\n    }\n    return result;\n}\n\nvoid protocol_securakey_render_data(ProtocolSecurakey* protocol, FuriString* result) {\n    if(bit_lib_get_bits_16(protocol->data, 0, 16) == 0) {\n        protocol->bit_format = 0;\n        furi_string_printf(\n            result,\n            \"RKKTH Plaintext format\\nCard number: %llu\",\n            bit_lib_get_bits_64(protocol->data, 0, 48));\n    } else {\n        if(bit_lib_get_bits(protocol->data, 0, 8) == 0) {\n            protocol->bit_format = 26;\n        } else {\n            protocol->bit_format = 32;\n        }\n        furi_string_printf(\n            result,\n            \"RKKT %u-bit format\\nFacility code: %u\\nCard number: %u\",\n            protocol->bit_format,\n            bit_lib_get_bits_16(protocol->data, 0, 16),\n            bit_lib_get_bits_16(protocol->data, 16, 16));\n    }\n}\n\nbool protocol_securakey_encoder_start(ProtocolSecurakey* protocol) {\n    // set all of our encoded_data bits to zeros.\n    memset(protocol->RKKTH_encoded_data, 0, SECURAKEY_RKKTH_ENCODED_FULL_SIZE_BYTE);\n    memset(protocol->RKKT_encoded_data, 0, SECURAKEY_RKKT_ENCODED_FULL_SIZE_BYTE);\n    if(bit_lib_get_bits_16(protocol->data, 0, 16) == 0) {\n        // write the preamble to the beginning of the RKKT_encoded_data\n        bit_lib_set_bits(protocol->RKKTH_encoded_data, 0, 0b01111111, 8);\n        bit_lib_set_bits(protocol->RKKTH_encoded_data, 8, 0b110, 3); //preamble cont.\n        // write card number (c)\n        bit_lib_copy_bits(protocol->RKKTH_encoded_data, 29, 8, protocol->data, 16);\n        // skip spacers (they are zero already by memset)\n        bit_lib_copy_bits(protocol->RKKTH_encoded_data, 38, 8, protocol->data, 24);\n        bit_lib_copy_bits(protocol->RKKTH_encoded_data, 47, 8, protocol->data, 32);\n        bit_lib_copy_bits(protocol->RKKTH_encoded_data, 56, 8, protocol->data, 40);\n    } else {\n        // write the preamble to the beginning of the RKKT_encoded_data\n        bit_lib_set_bits(protocol->RKKT_encoded_data, 0, 0b01111111, 8);\n        bit_lib_set_bits(protocol->RKKT_encoded_data, 8, 0b11001, 5); //preamble cont.\n        if(bit_lib_get_bits(protocol->data, 0, 8) == 0) {\n            protocol->bit_format = 26;\n            // set bit length\n            bit_lib_set_bits(protocol->RKKT_encoded_data, 13, protocol->bit_format, 6);\n            // set even parity & odd parity\n            if(!bit_lib_test_parity(protocol->data, 8, 12, BitLibParityOdd, 12)) {\n                bit_lib_set_bit(protocol->RKKT_encoded_data, 35, 1);\n            }\n            if(bit_lib_test_parity(protocol->data, 20, 12, BitLibParityOdd, 12)) {\n                bit_lib_set_bit(protocol->RKKT_encoded_data, 63, 1);\n            }\n            // write facility number (f)\n            bit_lib_copy_bits(protocol->RKKT_encoded_data, 36, 1, protocol->data, 8);\n            // have to skip one spacer\n            bit_lib_copy_bits(protocol->RKKT_encoded_data, 38, 7, protocol->data, 9);\n        } else {\n            protocol->bit_format = 32;\n            // set bit length\n            bit_lib_set_bits(protocol->RKKT_encoded_data, 13, protocol->bit_format, 6);\n            // set EP & OP\n            if(!bit_lib_test_parity(protocol->data, 2, 15, BitLibParityOdd, 15)) {\n                bit_lib_set_bit(protocol->RKKT_encoded_data, 29, 1);\n            }\n            if(bit_lib_test_parity(protocol->data, 17, 15, BitLibParityOdd, 15)) {\n                bit_lib_set_bit(protocol->RKKT_encoded_data, 63, 1);\n            }\n            // write facility number (f)\n            bit_lib_copy_bits(protocol->RKKT_encoded_data, 30, 7, protocol->data, 2);\n            // have to skip one spacer\n            bit_lib_copy_bits(protocol->RKKT_encoded_data, 38, 7, protocol->data, 3);\n        }\n\n        // write card number (c)\n        bit_lib_copy_bits(protocol->RKKT_encoded_data, 45, 1, protocol->data, 16);\n        // same skips here\n        bit_lib_copy_bits(protocol->RKKT_encoded_data, 47, 8, protocol->data, 17);\n        bit_lib_copy_bits(protocol->RKKT_encoded_data, 56, 7, protocol->data, 25);\n\n        // unsure about CS yet might as well just copy it from saved\n        // CS1\n        bit_lib_copy_bits(protocol->RKKT_encoded_data, 65, 8, protocol->data, 32);\n        // CS2\n        bit_lib_copy_bits(protocol->RKKT_encoded_data, 74, 8, protocol->data, 40);\n    }\n    // for sending we start at bit 0.\n    protocol->encoded_data_index = 0;\n    protocol->encoded_polarity = true;\n    return true;\n}\n\nLevelDuration protocol_securakey_encoder_yield(ProtocolSecurakey* protocol) {\n    if(bit_lib_get_bits_16(protocol->data, 0, 16) == 0) {\n        bool level = bit_lib_get_bit(protocol->RKKTH_encoded_data, protocol->encoded_data_index);\n        uint32_t duration = SECURAKEY_CLOCK_PER_BIT / 2;\n        if(protocol->encoded_polarity) {\n            protocol->encoded_polarity = false;\n        } else {\n            level = !level;\n            protocol->encoded_polarity = true;\n            bit_lib_increment_index(\n                protocol->encoded_data_index, SECURAKEY_RKKTH_ENCODED_FULL_SIZE_BITS);\n        }\n        return level_duration_make(level, duration);\n    } else {\n        bool level = bit_lib_get_bit(protocol->RKKT_encoded_data, protocol->encoded_data_index);\n        uint32_t duration = SECURAKEY_CLOCK_PER_BIT / 2;\n        if(protocol->encoded_polarity) {\n            protocol->encoded_polarity = false;\n        } else {\n            level = !level;\n            protocol->encoded_polarity = true;\n            bit_lib_increment_index(\n                protocol->encoded_data_index, SECURAKEY_RKKT_ENCODED_FULL_SIZE_BITS);\n        }\n        return level_duration_make(level, duration);\n    }\n}\n\nbool protocol_securakey_write_data(ProtocolSecurakey* protocol, void* data) {\n    protocol_securakey_encoder_start(protocol);\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n    // Write T5577\n    if(bit_lib_get_bits_16(protocol->data, 0, 16) == 0) {\n        if(request->write_type == LFRFIDWriteTypeT5577) {\n            request->t5577.block[0] =\n                (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_40 |\n                 (2\n                  << LFRFID_T5577_MAXBLOCK_SHIFT)); // we only need 2 32-bit blocks for our 64-bit encoded data\n            request->t5577.block[1] = bit_lib_get_bits_32(protocol->RKKTH_encoded_data, 0, 32);\n            request->t5577.block[2] = bit_lib_get_bits_32(protocol->RKKTH_encoded_data, 32, 32);\n            request->t5577.blocks_to_write = 3;\n            result = true;\n        }\n    } else {\n        if(request->write_type == LFRFIDWriteTypeT5577) {\n            request->t5577.block[0] =\n                (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_40 |\n                 (3\n                  << LFRFID_T5577_MAXBLOCK_SHIFT)); // we only need 3 32-bit blocks for our 96-bit encoded data\n            request->t5577.block[1] = bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 32);\n            request->t5577.block[2] = bit_lib_get_bits_32(protocol->RKKT_encoded_data, 32, 32);\n            request->t5577.block[3] = bit_lib_get_bits_32(protocol->RKKT_encoded_data, 64, 32);\n            request->t5577.blocks_to_write = 4;\n            result = true;\n        }\n    }\n    return result;\n}\n\nconst ProtocolBase protocol_securakey = {\n    .name = \"Radio Key\",\n    .manufacturer = \"Securakey\",\n    .data_size = SECURAKEY_DECODED_DATA_SIZE_BYTES,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_securakey_alloc,\n    .free = (ProtocolFree)protocol_securakey_free,\n    .get_data = (ProtocolGetData)protocol_securakey_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_securakey_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_securakey_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_securakey_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_securakey_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_securakey_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_securakey_render_data,\n    .write_data = (ProtocolWriteData)protocol_securakey_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_securakey.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_securakey;\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_viking.c",
    "content": "#include <furi.h>\n#include <toolbox/protocols/protocol.h>\n#include <toolbox/manchester_decoder.h>\n#include <bit_lib/bit_lib.h>\n#include \"lfrfid_protocols.h\"\n\n#define VIKING_CLOCK_PER_BIT (32)\n\n#define VIKING_ENCODED_BIT_SIZE       (64)\n#define VIKING_ENCODED_BYTE_SIZE      (((VIKING_ENCODED_BIT_SIZE) / 8))\n#define VIKING_PREAMBLE_BIT_SIZE      (24)\n#define VIKING_PREAMBLE_BYTE_SIZE     (3)\n#define VIKING_ENCODED_BYTE_FULL_SIZE (VIKING_ENCODED_BYTE_SIZE + VIKING_PREAMBLE_BYTE_SIZE)\n#define VIKING_DECODED_DATA_SIZE      4\n\n#define VIKING_READ_SHORT_TIME  (128)\n#define VIKING_READ_LONG_TIME   (256)\n#define VIKING_READ_JITTER_TIME (60)\n\n#define VIKING_READ_SHORT_TIME_LOW  (VIKING_READ_SHORT_TIME - VIKING_READ_JITTER_TIME)\n#define VIKING_READ_SHORT_TIME_HIGH (VIKING_READ_SHORT_TIME + VIKING_READ_JITTER_TIME)\n#define VIKING_READ_LONG_TIME_LOW   (VIKING_READ_LONG_TIME - VIKING_READ_JITTER_TIME)\n#define VIKING_READ_LONG_TIME_HIGH  (VIKING_READ_LONG_TIME + VIKING_READ_JITTER_TIME)\n\ntypedef struct {\n    uint8_t data[VIKING_DECODED_DATA_SIZE];\n    uint8_t encoded_data[VIKING_ENCODED_BYTE_FULL_SIZE];\n\n    uint8_t encoded_data_index;\n    bool encoded_polarity;\n\n    ManchesterState decoder_manchester_state;\n} ProtocolViking;\n\nProtocolViking* protocol_viking_alloc(void) {\n    ProtocolViking* proto = malloc(sizeof(ProtocolViking));\n    return (void*)proto;\n}\n\nvoid protocol_viking_free(ProtocolViking* protocol) {\n    free(protocol);\n}\n\nuint8_t* protocol_viking_get_data(ProtocolViking* protocol) {\n    return protocol->data;\n}\n\nstatic void protocol_viking_decode(ProtocolViking* protocol) {\n    // Copy Card ID\n    bit_lib_copy_bits(protocol->data, 0, 32, protocol->encoded_data, 24);\n}\n\nstatic bool protocol_viking_can_be_decoded(ProtocolViking* protocol) {\n    // check 24 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b1111001000000000) return false;\n    if(bit_lib_get_bits(protocol->encoded_data, 16, 8) != 0b00000000) return false;\n\n    // check next 24 bits preamble\n    if(bit_lib_get_bits_16(protocol->encoded_data, 64, 16) != 0b1111001000000000) return false;\n    if(bit_lib_get_bits(protocol->encoded_data, 80, 8) != 0b00000000) return false;\n\n    // Checksum\n    uint32_t checksum = bit_lib_get_bits(protocol->encoded_data, 0, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 8, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 16, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 24, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 32, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 40, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 48, 8) ^\n                        bit_lib_get_bits(protocol->encoded_data, 56, 8) ^ 0xA8;\n    if(checksum != 0) return false;\n\n    return true;\n}\n\nvoid protocol_viking_decoder_start(ProtocolViking* protocol) {\n    memset(protocol->encoded_data, 0, VIKING_ENCODED_BYTE_FULL_SIZE);\n    manchester_advance(\n        protocol->decoder_manchester_state,\n        ManchesterEventReset,\n        &protocol->decoder_manchester_state,\n        NULL);\n}\n\nbool protocol_viking_decoder_feed(ProtocolViking* protocol, bool level, uint32_t duration) {\n    bool result = false;\n\n    ManchesterEvent event = ManchesterEventReset;\n\n    if(duration > VIKING_READ_SHORT_TIME_LOW && duration < VIKING_READ_SHORT_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventShortHigh;\n        } else {\n            event = ManchesterEventShortLow;\n        }\n    } else if(duration > VIKING_READ_LONG_TIME_LOW && duration < VIKING_READ_LONG_TIME_HIGH) {\n        if(!level) {\n            event = ManchesterEventLongHigh;\n        } else {\n            event = ManchesterEventLongLow;\n        }\n    }\n\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);\n\n        if(data_ok) {\n            bit_lib_push_bit(protocol->encoded_data, VIKING_ENCODED_BYTE_FULL_SIZE, data);\n\n            if(protocol_viking_can_be_decoded(protocol)) {\n                protocol_viking_decode(protocol);\n                result = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nbool protocol_viking_encoder_start(ProtocolViking* protocol) {\n    // Preamble\n    bit_lib_set_bits(protocol->encoded_data, 0, 0b11110010, 8);\n    bit_lib_set_bits(protocol->encoded_data, 8, 0b00000000, 8);\n    bit_lib_set_bits(protocol->encoded_data, 16, 0b00000000, 8);\n\n    // Card Id\n    bit_lib_copy_bits(protocol->encoded_data, 24, 32, protocol->data, 0);\n\n    // Checksum\n    uint32_t id = bit_lib_get_bits_32(protocol->data, 0, 32);\n    uint8_t checksum = ((id >> 24) & 0xFF) ^ ((id >> 16) & 0xFF) ^ ((id >> 8) & 0xFF) ^\n                       (id & 0xFF) ^ 0xF2 ^ 0xA8;\n    bit_lib_set_bits(protocol->encoded_data, 56, checksum, 8);\n\n    return true;\n}\n\nLevelDuration protocol_viking_encoder_yield(ProtocolViking* protocol) {\n    bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index);\n    uint32_t duration = VIKING_CLOCK_PER_BIT / 2;\n\n    if(protocol->encoded_polarity) {\n        protocol->encoded_polarity = false;\n    } else {\n        level = !level;\n\n        protocol->encoded_polarity = true;\n        bit_lib_increment_index(protocol->encoded_data_index, VIKING_ENCODED_BIT_SIZE);\n    }\n\n    return level_duration_make(level, duration);\n}\n\nbool protocol_viking_write_data(ProtocolViking* protocol, void* data) {\n    LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;\n    bool result = false;\n\n    // Correct protocol data by redecoding\n    protocol_viking_encoder_start(protocol);\n    protocol_viking_decode(protocol);\n\n    protocol_viking_encoder_start(protocol);\n\n    if(request->write_type == LFRFIDWriteTypeT5577) {\n        request->t5577.block[0] =\n            (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 |\n             (2 << LFRFID_T5577_MAXBLOCK_SHIFT));\n        request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);\n        request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);\n        request->t5577.blocks_to_write = 3;\n        result = true;\n    } else if(request->write_type == LFRFIDWriteTypeEM4305) {\n        request->em4305.word[4] =\n            (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (6 << EM4x05_MAXBLOCK_SHIFT));\n        uint32_t encoded_data_reversed[2] = {0};\n        for(uint8_t i = 0; i < 64; i++) {\n            encoded_data_reversed[i / 32] =\n                (encoded_data_reversed[i / 32] << 1) |\n                (bit_lib_get_bit(protocol->encoded_data, (63 - i)) & 1);\n        }\n        request->em4305.word[5] = encoded_data_reversed[1];\n        request->em4305.word[6] = encoded_data_reversed[0];\n        request->em4305.mask = 0x70;\n        result = true;\n    }\n    return result;\n}\n\nvoid protocol_viking_render_data(ProtocolViking* protocol, FuriString* result) {\n    furi_string_printf(result, \"ID: %08lX\", bit_lib_get_bits_32(protocol->data, 0, 32));\n}\n\nconst ProtocolBase protocol_viking = {\n    .name = \"Viking\",\n    .manufacturer = \"Viking\",\n    .data_size = VIKING_DECODED_DATA_SIZE,\n    .features = LFRFIDFeatureASK,\n    .validate_count = 3,\n    .alloc = (ProtocolAlloc)protocol_viking_alloc,\n    .free = (ProtocolFree)protocol_viking_free,\n    .get_data = (ProtocolGetData)protocol_viking_get_data,\n    .decoder =\n        {\n            .start = (ProtocolDecoderStart)protocol_viking_decoder_start,\n            .feed = (ProtocolDecoderFeed)protocol_viking_decoder_feed,\n        },\n    .encoder =\n        {\n            .start = (ProtocolEncoderStart)protocol_viking_encoder_start,\n            .yield = (ProtocolEncoderYield)protocol_viking_encoder_yield,\n        },\n    .render_data = (ProtocolRenderData)protocol_viking_render_data,\n    .render_brief_data = (ProtocolRenderData)protocol_viking_render_data,\n    .write_data = (ProtocolWriteData)protocol_viking_write_data,\n};\n"
  },
  {
    "path": "lib/lfrfid/protocols/protocol_viking.h",
    "content": "#pragma once\n#include <toolbox/protocols/protocol.h>\n\nextern const ProtocolBase protocol_viking;\n"
  },
  {
    "path": "lib/lfrfid/tools/em4305.c",
    "content": "#include \"em4305.h\"\n#include <furi.h>\n#include <furi_hal_rfid.h>\n\n#define TAG \"EM4305\"\n\n#define EM4305_TIMING_1     (32)\n#define EM4305_TIMING_0_OFF (23)\n#define EM4305_TIMING_0_ON  (18)\n\n#define EM4305_FIELD_STOP_OFF_CYCLES (55)\n#define EM4305_FIELD_STOP_ON_CYCLES  (18)\n\n#define EM4305_TIMING_POWER_CHECK  (1480)\n#define EM4305_TIMING_EEPROM_WRITE (9340)\n\nstatic bool em4305_line_parity(uint8_t data) {\n    uint8_t parity = 0;\n    for(uint8_t i = 0; i < 8; i++) {\n        parity ^= (data >> i) & 1;\n    }\n    return parity;\n}\n\nstatic uint64_t em4305_prepare_data(uint32_t data) {\n    uint8_t i, j;\n    uint64_t data_with_parity = 0;\n\n    // 4 lines of 8 bits of data\n    // line even parity at bits 8 17 26 35\n    // column even parity at bits 36-43\n    // bit 44 is always 0\n    // final table is 5 lines of 9 bits\n\n    // line parity\n    for(i = 0; i < 4; i++) {\n        for(j = 0; j < 8; j++) {\n            data_with_parity = (data_with_parity << 1) | ((data >> (i * 8 + j)) & 1);\n        }\n        data_with_parity = (data_with_parity << 1) | (uint64_t)em4305_line_parity(data >> (i * 8));\n    }\n\n    // column parity\n    for(i = 0; i < 8; i++) {\n        uint8_t column_parity = 0;\n        for(j = 0; j < 4; j++) {\n            column_parity ^= (data >> (j * 8 + i)) & 1;\n        }\n        data_with_parity = (data_with_parity << 1) | column_parity;\n    }\n\n    // bit 44\n    data_with_parity = (data_with_parity << 1) | 0;\n\n    return data_with_parity;\n}\n\nstatic void em4305_start(void) {\n    furi_hal_rfid_tim_read_start(125000, 0.5);\n\n    // do not ground the antenna\n    furi_hal_rfid_pin_pull_release();\n}\n\nstatic void em4305_stop(void) {\n    furi_hal_rfid_tim_read_stop();\n    furi_hal_rfid_pins_reset();\n}\n\nstatic void em4305_write_bit(bool value) {\n    if(value) {\n        furi_delay_us(EM4305_TIMING_1 * 8);\n    } else {\n        furi_hal_rfid_tim_read_pause();\n        furi_delay_us(EM4305_TIMING_0_OFF * 8);\n        furi_hal_rfid_tim_read_continue();\n        furi_delay_us(EM4305_TIMING_0_ON * 8);\n    }\n}\n\nstatic void em4305_write_opcode(uint8_t value) {\n    // 3 bit opcode\n    for(uint8_t i = 0; i < 3; i++) {\n        em4305_write_bit((value >> i) & 1);\n    }\n\n    // parity\n    bool parity = 0;\n    for(uint8_t i = 0; i < 3; i++) {\n        parity ^= (value >> i) & 1;\n    }\n    em4305_write_bit(parity);\n}\n\nstatic void em4305_field_stop() {\n    furi_hal_rfid_tim_read_pause();\n    furi_delay_us(EM4305_FIELD_STOP_OFF_CYCLES * 8);\n    furi_hal_rfid_tim_read_continue();\n    furi_delay_us(EM4305_FIELD_STOP_ON_CYCLES * 8);\n}\n\nstatic void em4305_write_word(uint8_t address, uint32_t data) {\n    // parity\n    uint64_t data_with_parity = em4305_prepare_data(data);\n\n    // power up the tag\n    furi_delay_us(8000);\n\n    // field stop\n    em4305_field_stop();\n\n    // start bit\n    em4305_write_bit(0);\n\n    // opcode\n    em4305_write_opcode(EM4x05_OPCODE_WRITE);\n\n    // address\n    bool address_parity = 0;\n    for(uint8_t i = 0; i < 4; i++) {\n        em4305_write_bit((address >> (i)) & 1);\n        address_parity ^= (address >> (i)) & 1;\n    }\n    em4305_write_bit(0);\n    em4305_write_bit(0);\n    em4305_write_bit(address_parity);\n\n    // data\n    for(uint8_t i = 0; i < 45; i++) {\n        em4305_write_bit((data_with_parity >> (44 - i)) & 1);\n    }\n\n    // wait for power check and eeprom write\n    furi_delay_us(EM4305_TIMING_POWER_CHECK);\n    furi_delay_us(EM4305_TIMING_EEPROM_WRITE);\n}\n\nvoid em4305_write(LFRFIDEM4305* data) {\n    furi_check(data);\n\n    em4305_start();\n    FURI_CRITICAL_ENTER();\n\n    for(uint8_t i = 0; i < EM4x05_WORD_COUNT; i++) {\n        if(data->mask & (1 << i)) {\n            em4305_write_word(i, data->word[i]);\n        }\n    }\n\n    FURI_CRITICAL_EXIT();\n    em4305_stop();\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/em4305.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// EM4305/4205 chip config definitions, thanks proxmark3!\n#define EM4x05_GET_BITRATE(x)        ((((x) & 0x3F) * 2) + 2)\n// Note: only data rates 8, 16, 32, 40(*) and 64 are supported. (*) only with EM4305 330pF\n#define EM4x05_SET_BITRATE(x)        (((x) - 2) / 2)\n#define EM4x05_MODULATION_NRZ        (0x00000000)\n#define EM4x05_MODULATION_MANCHESTER (0x00000040)\n#define EM4x05_MODULATION_BIPHASE    (0x00000080)\n#define EM4x05_MODULATION_MILLER     (0x000000C0) // not supported by all 4x05/4x69 chips\n#define EM4x05_MODULATION_PSK1       (0x00000100) // not supported by all 4x05/4x69 chips\n#define EM4x05_MODULATION_PSK2       (0x00000140) // not supported by all 4x05/4x69 chips\n#define EM4x05_MODULATION_PSK3       (0x00000180) // not supported by all 4x05/4x69 chips\n#define EM4x05_MODULATION_FSK1       (0x00000200) // not supported by all 4x05/4x69 chips\n#define EM4x05_MODULATION_FSK2       (0x00000240) // not supported by all 4x05/4x69 chips\n#define EM4x05_PSK_RF_2              (0)\n#define EM4x05_PSK_RF_4              (0x00000400)\n#define EM4x05_PSK_RF_8              (0x00000800)\n#define EM4x05_MAXBLOCK_SHIFT        (14)\n#define EM4x05_FIRST_USER_BLOCK      (5)\n#define EM4x05_SET_NUM_BLOCKS(x) \\\n    (((x) + 4) << 14) // number of blocks sent during default read mode\n#define EM4x05_GET_NUM_BLOCKS(x)  ((((x) >> 14) & 0xF) - 4)\n#define EM4x05_READ_LOGIN_REQ     (1 << 18)\n#define EM4x05_READ_HK_LOGIN_REQ  (1 << 19)\n#define EM4x05_WRITE_LOGIN_REQ    (1 << 20)\n#define EM4x05_WRITE_HK_LOGIN_REQ (1 << 21)\n#define EM4x05_READ_AFTER_WRITE   (1 << 22)\n#define EM4x05_DISABLE_ALLOWED    (1 << 23)\n#define EM4x05_READER_TALK_FIRST  (1 << 24)\n#define EM4x05_INVERT             (1 << 25)\n#define EM4x05_PIGEON             (1 << 26)\n\n#define EM4x05_WORD_COUNT (16)\n\n#define EM4x05_OPCODE_LOGIN   (0b001)\n#define EM4x05_OPCODE_WRITE   (0b010)\n#define EM4x05_OPCODE_READ    (0b100)\n#define EM4x05_OPCODE_PROTECT (0b110)\n#define EM4x05_OPCODE_DISABLE (0b101)\n\ntypedef struct {\n    uint32_t word[EM4x05_WORD_COUNT]; /**< Word data to write */\n    uint16_t mask; /**< Word mask */\n} LFRFIDEM4305;\n\n/** Write EM4305 tag data to tag\n *\n * @param      data  The data to write (mask is taken from that data)\n */\nvoid em4305_write(LFRFIDEM4305* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/tools/fsk_demod.c",
    "content": "#include <furi.h>\n#include \"fsk_demod.h\"\n\nstruct FSKDemod {\n    uint32_t low_time;\n    uint32_t low_pulses;\n    uint32_t hi_time;\n    uint32_t hi_pulses;\n\n    bool invert;\n    uint32_t mid_time;\n    uint32_t time;\n    uint32_t count;\n    bool last_pulse;\n};\n\nFSKDemod*\n    fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses) {\n    FSKDemod* demod = malloc(sizeof(FSKDemod));\n    demod->invert = false;\n\n    if(low_time > hi_time) {\n        uint32_t tmp;\n        tmp = hi_time;\n        hi_time = low_time;\n        low_time = tmp;\n\n        tmp = hi_pulses;\n        hi_pulses = low_pulses;\n        low_pulses = tmp;\n\n        demod->invert = true;\n    }\n\n    demod->low_time = low_time;\n    demod->low_pulses = low_pulses;\n    demod->hi_time = hi_time;\n    demod->hi_pulses = hi_pulses;\n\n    demod->mid_time = (hi_time - low_time) / 2 + low_time;\n    demod->time = 0;\n    demod->count = 0;\n    demod->last_pulse = false;\n\n    return demod;\n}\n\nvoid fsk_demod_free(FSKDemod* demod) {\n    free(demod);\n}\n\nvoid fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count) {\n    *count = 0;\n\n    if(polarity) {\n        // accumulate time\n        demod->time = time;\n    } else {\n        demod->time += time;\n\n        // check for valid pulse\n        if(demod->time >= demod->low_time && demod->time < demod->hi_time) {\n            bool pulse;\n\n            if(demod->time < demod->mid_time) {\n                pulse = false;\n            } else {\n                pulse = true;\n            }\n\n            demod->count++;\n\n            // check for edge transition\n            if(demod->last_pulse != pulse) {\n                uint32_t data_count = demod->count + 1;\n\n                if(demod->last_pulse) {\n                    data_count /= demod->hi_pulses;\n                    *value = !demod->invert;\n                } else {\n                    data_count /= demod->low_pulses;\n                    *value = demod->invert;\n                }\n\n                *count = data_count;\n                demod->count = 0;\n                demod->last_pulse = pulse;\n            }\n        } else {\n            demod->count = 0;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/fsk_demod.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FSKDemod FSKDemod;\n\n/**\n * @brief Allocate a new FSKDemod instance\n * FSKDemod is a demodulator that can decode FSK encoded data\n * \n * @param low_time time between rising edges for the 0 bit\n * @param low_pulses rising edges count for the 0 bit\n * @param hi_time time between rising edges for the 1 bit\n * @param hi_pulses rising edges count for the 1 bit\n * @return FSKDemod* \n */\nFSKDemod*\n    fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses);\n\n/**\n * @brief Free a FSKDemod instance\n * \n * @param fsk_demod \n */\nvoid fsk_demod_free(FSKDemod* fsk_demod);\n\n/**\n * @brief Feed sample to demodulator\n * \n * @param demod FSKDemod instance\n * @param polarity sample polarity\n * @param time sample time\n * @param value demodulated bit value\n * @param count demodulated bit count\n */\nvoid fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/tools/fsk_ocs.c",
    "content": "#include \"fsk_osc.h\"\n#include <stdlib.h>\n\nstruct FSKOsc {\n    uint16_t freq[2];\n    uint16_t osc_phase_max;\n    int32_t osc_phase_current;\n\n    uint32_t pulse;\n};\n\nFSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max) {\n    FSKOsc* osc = malloc(sizeof(FSKOsc));\n    osc->freq[0] = freq_low;\n    osc->freq[1] = freq_hi;\n    osc->osc_phase_max = osc_phase_max;\n    osc->osc_phase_current = 0;\n    osc->pulse = 0;\n    return osc;\n}\n\nvoid fsk_osc_free(FSKOsc* osc) {\n    free(osc);\n}\n\nvoid fsk_osc_reset(FSKOsc* osc) {\n    osc->osc_phase_current = 0;\n    osc->pulse = 0;\n}\n\nbool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period) {\n    bool advance = false;\n    *period = osc->freq[bit];\n    osc->osc_phase_current += *period;\n\n    if(osc->osc_phase_current > osc->osc_phase_max) {\n        advance = true;\n        osc->osc_phase_current -= osc->osc_phase_max;\n    }\n\n    return advance;\n}\n\nbool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration) {\n    bool advance = false;\n\n    // if pulse is zero, we need to output high, otherwise we need to output low\n    if(osc->pulse == 0) {\n        uint32_t length;\n        advance = fsk_osc_next(osc, bit, &length);\n        *duration = length / 2;\n        osc->pulse = *duration;\n        *level = true;\n    } else {\n        // output low half and reset pulse\n        *duration = osc->pulse;\n        osc->pulse = 0;\n        *level = false;\n    }\n\n    return advance;\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/fsk_osc.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FSKOsc FSKOsc;\n\n/**\n * @brief Allocate a new FSKOsc instance\n * FSKOsc is a oscillator that can provide FSK encoding\n * \n * @param freq_low \n * @param freq_hi \n * @param osc_phase_max \n * @return FSKOsc* \n */\nFSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max);\n\n/**\n * @brief Free a FSKOsc instance\n * \n * @param osc \n */\nvoid fsk_osc_free(FSKOsc* osc);\n\n/**\n * @brief Reset ocillator\n * \n * @param osc \n */\nvoid fsk_osc_reset(FSKOsc* osc);\n\n/**\n * @brief Get next duration sample from oscillator\n * \n * @param osc \n * @param bit \n * @param period \n * @return bool \n */\nbool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period);\n\n/**\n * @brief Get next half of sample from oscillator\n * Useful when encoding high and low levels separately.\n * \n * @param osc \n * @param bit \n * @param level \n * @param duration \n * @return bool \n */\nbool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/tools/iso_3166.c",
    "content": "#include \"iso_3166.h\"\n\n#include <flipper_format.h>\n\n#define RESOURCE_FILE_PATH (EXT_PATH(\"lfrfid/assets/iso3166.lfrfid\"))\n\nstatic bool lfrfid_search_data(Storage* storage, uint16_t country_code, FuriString* out_line) {\n    static const char* lfrfid_resources_header = \"Flipper LFRFID resources\";\n    static const uint32_t lfrfid_resources_file_version = 1;\n\n    FuriString* key = furi_string_alloc_printf(\"%04d\", country_code);\n    bool found = false;\n    furi_string_reset(out_line);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n    FuriString* temp_str = furi_string_alloc();\n\n    do {\n        // Open file\n        if(!flipper_format_file_open_existing(file, RESOURCE_FILE_PATH)) break;\n        // Read file header and version\n        uint32_t version = 0;\n        if(!flipper_format_read_header(file, temp_str, &version)) break;\n        if(furi_string_cmp_str(temp_str, lfrfid_resources_header) ||\n           (version != lfrfid_resources_file_version))\n            break;\n        if(!flipper_format_read_string(file, furi_string_get_cstr(key), out_line)) break;\n        found = true;\n    } while(false);\n\n    furi_string_free(key);\n    furi_string_free(temp_str);\n    flipper_format_free(file);\n\n    if(found) {\n        furi_string_replace_all(out_line, \" \", \"\");\n    }\n\n    return found;\n}\n\nbool iso_3166_get_two_letter(Storage* storage, uint16_t country_code, FuriString* out_two_letter) {\n    // We'll fetch the entire line from iso3166.lfrfid\n    FuriString* line = furi_string_alloc();\n    bool found = lfrfid_search_data(storage, country_code, line);\n\n    if(found) {\n        if(furi_string_size(line) < 2) {\n            furi_string_free(line);\n            FURI_LOG_E(\"Lfrifd:Iso_3166\", \"Not enough data for two-letter code\");\n            return false; // Not enough data for a two-letter code\n        }\n        // AFAFGAfghanistan\n        furi_string_left(line, 2); // AF\n        furi_string_set(out_two_letter, line);\n    }\n    furi_string_free(line);\n    return found;\n}\n\nbool iso_3166_get_three_letter(\n    Storage* storage,\n    uint16_t country_code,\n    FuriString* out_three_letter) {\n    FuriString* line = furi_string_alloc();\n    bool found = lfrfid_search_data(storage, country_code, line);\n\n    if(found) {\n        if(furi_string_size(line) < 5) {\n            furi_string_free(line);\n            FURI_LOG_E(\"Lfrifd:Iso_3166\", \"Not enough data for three-letter code\");\n            return false; // Not enough data for a three-letter code\n        }\n        // AFAFGAfghanistan\n        furi_string_left(line, 5); // AFAFG\n        furi_string_right(line, 2); // AFG\n        furi_string_set(out_three_letter, line);\n    }\n    furi_string_free(line);\n    return found;\n}\n\nbool iso_3166_get_full_name(Storage* storage, uint16_t country_code, FuriString* out_full_name) {\n    FuriString* line = furi_string_alloc();\n    bool found = lfrfid_search_data(storage, country_code, line);\n\n    if(found) {\n        if(furi_string_size(line) < 6) {\n            furi_string_free(line);\n            FURI_LOG_E(\"Lfrifd:Iso_3166\", \"Not enough data for full name\");\n            return false; // Not enough data for a full name\n        }\n        // AFAFGAfghanistan\n        furi_string_right(line, 5); // Afghanistan\n        furi_string_set(out_full_name, line);\n    }\n    furi_string_free(line);\n    return found;\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/iso_3166.h",
    "content": "#pragma once\n\n#include <storage/storage.h>\n#include <furi.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Look up a country's 2-letter code (ISO 3166-1 Alpha-2).\n * \n * @param storage       Pointer to a valid Storage.\n * @param country_code  Numeric ISO3166 code. e.g., 4 for Afghanistan.\n * @param out_two_letter A caller-allocated FuriString to fill with the 2-letter code, if found.\n */\nbool iso_3166_get_two_letter(Storage* storage, uint16_t country_code, FuriString* out_two_letter);\n\n/**\n * @brief Look up a country's 3-letter code (ISO 3166-1 Alpha-3).\n * \n * @param storage       Pointer to a valid Storage.\n * @param country_code  Numeric ISO3166 code.\n * @param out_three_letter A caller-allocated FuriString to fill with the 3-letter code, if found.\n */\nbool iso_3166_get_three_letter(\n    Storage* storage,\n    uint16_t country_code,\n    FuriString* out_three_letter);\n\n/**\n * @brief Look up a country's full name.\n * \n * @param storage       Pointer to a valid Storage.\n * @param country_code  Numeric ISO3166 code.\n * @param out_full_name A caller-allocated FuriString to fill with the country's full name.\n */\nbool iso_3166_get_full_name(Storage* storage, uint16_t country_code, FuriString* out_full_name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/tools/t5577.c",
    "content": "#include \"t5577.h\"\n#include <furi.h>\n#include <furi_hal_rfid.h>\n\n#define T5577_TIMING_WAIT_TIME 400\n#define T5577_TIMING_START_GAP 30\n#define T5577_TIMING_WRITE_GAP 18\n#define T5577_TIMING_DATA_0    24\n#define T5577_TIMING_DATA_1    56\n#define T5577_TIMING_PROGRAM   700\n\n#define T5577_OPCODE_PAGE_0 0b10\n#define T5577_OPCODE_PAGE_1 0b11\n#define T5577_OPCODE_RESET  0b00\n\nstatic void t5577_start(void) {\n    furi_hal_rfid_tim_read_start(125000, 0.5);\n\n    // do not ground the antenna\n    furi_hal_rfid_pin_pull_release();\n}\n\nstatic void t5577_stop(void) {\n    furi_hal_rfid_tim_read_stop();\n    furi_hal_rfid_pins_reset();\n}\n\nstatic void t5577_write_gap(uint32_t gap_time) {\n    furi_hal_rfid_tim_read_pause();\n    furi_delay_us(gap_time * 8);\n    furi_hal_rfid_tim_read_continue();\n}\n\nstatic void t5577_write_bit(bool value) {\n    if(value) {\n        furi_delay_us(T5577_TIMING_DATA_1 * 8);\n    } else {\n        furi_delay_us(T5577_TIMING_DATA_0 * 8);\n    }\n    t5577_write_gap(T5577_TIMING_WRITE_GAP);\n}\n\nstatic void t5577_write_opcode(uint8_t value) {\n    t5577_write_bit((value >> 1) & 1);\n    t5577_write_bit((value >> 0) & 1);\n}\n\nstatic void t5577_write_reset(void) {\n    t5577_write_gap(T5577_TIMING_START_GAP);\n    t5577_write_bit(1);\n    t5577_write_bit(0);\n}\n\nstatic void t5577_write_block_pass(\n    uint8_t page,\n    uint8_t block,\n    bool lock_bit,\n    uint32_t data,\n    bool with_pass,\n    uint32_t password) {\n    furi_delay_us(T5577_TIMING_WAIT_TIME * 8);\n\n    // start gap\n    t5577_write_gap(T5577_TIMING_START_GAP);\n\n    // opcode for page\n    t5577_write_opcode((page == 1) ? T5577_OPCODE_PAGE_1 : T5577_OPCODE_PAGE_0);\n\n    // password\n    if(with_pass) {\n        for(uint8_t i = 0; i < 32; i++) {\n            t5577_write_bit((password >> (31 - i)) & 1);\n        }\n    }\n\n    // lock bit\n    t5577_write_bit(lock_bit);\n\n    // data\n    for(uint8_t i = 0; i < 32; i++) {\n        t5577_write_bit((data >> (31 - i)) & 1);\n    }\n\n    // block address\n    t5577_write_bit((block >> 2) & 1);\n    t5577_write_bit((block >> 1) & 1);\n    t5577_write_bit((block >> 0) & 1);\n\n    furi_delay_us(T5577_TIMING_PROGRAM * 8);\n\n    furi_delay_us(T5577_TIMING_WAIT_TIME * 8);\n    t5577_write_reset();\n}\n\nstatic void t5577_write_block_simple(uint8_t block, bool lock_bit, uint32_t data) {\n    t5577_write_block_pass(0, block, lock_bit, data, false, 0);\n}\n\nvoid t5577_write(LFRFIDT5577* data) {\n    t5577_start();\n    FURI_CRITICAL_ENTER();\n    for(size_t i = 0; i < data->blocks_to_write; i++) {\n        t5577_write_block_simple(i, false, data->block[i]);\n    }\n    t5577_write_reset();\n    FURI_CRITICAL_EXIT();\n    t5577_stop();\n}\n\nvoid t5577_write_with_pass(LFRFIDT5577* data, uint32_t password) {\n    t5577_start();\n    FURI_CRITICAL_ENTER();\n    for(size_t i = 0; i < data->blocks_to_write; i++) {\n        t5577_write_block_pass(0, i, false, data->block[i], true, password);\n    }\n    t5577_write_reset();\n    FURI_CRITICAL_EXIT();\n    t5577_stop();\n}\n\nvoid t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password) {\n    t5577_start();\n    FURI_CRITICAL_ENTER();\n\n    uint8_t mask = data->mask;\n\n    size_t pages_total = (page == 0) ? T5577_BLOCKS_IN_PAGE_0 : T5577_BLOCKS_IN_PAGE_1;\n\n    for(size_t i = 0; i < pages_total; i++) {\n        bool need_to_write = mask & 1;\n        mask >>= 1;\n        if(!need_to_write) continue;\n        t5577_write_block_pass(page, i, false, data->block[i], with_pass, password);\n    }\n    t5577_write_reset();\n    FURI_CRITICAL_EXIT();\n    t5577_stop();\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/t5577.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define LFRFID_T5577_BLOCK_COUNT 8\n#define T5577_BLOCKS_IN_PAGE_0   8\n#define T5577_BLOCKS_IN_PAGE_1   4\n\n// T5577 block 0 definitions, thanks proxmark3!\n#define LFRFID_T5577_POR_DELAY             0x00000001\n#define LFRFID_T5577_ST_TERMINATOR         0x00000008\n#define LFRFID_T5577_PWD                   0x00000010\n#define LFRFID_T5577_MAXBLOCK_SHIFT        5\n#define LFRFID_T5577_AOR                   0x00000200\n#define LFRFID_T5577_PSKCF_RF_2            0\n#define LFRFID_T5577_PSKCF_RF_4            0x00000400\n#define LFRFID_T5577_PSKCF_RF_8            0x00000800\n#define LFRFID_T5577_MODULATION_DIRECT     0\n#define LFRFID_T5577_MODULATION_PSK1       0x00001000\n#define LFRFID_T5577_MODULATION_PSK2       0x00002000\n#define LFRFID_T5577_MODULATION_PSK3       0x00003000\n#define LFRFID_T5577_MODULATION_FSK1       0x00004000\n#define LFRFID_T5577_MODULATION_FSK2       0x00005000\n#define LFRFID_T5577_MODULATION_FSK1a      0x00006000\n#define LFRFID_T5577_MODULATION_FSK2a      0x00007000\n#define LFRFID_T5577_MODULATION_MANCHESTER 0x00008000\n#define LFRFID_T5577_MODULATION_BIPHASE    0x00010000\n#define LFRFID_T5577_MODULATION_DIPHASE    0x00018000\n#define LFRFID_T5577_X_MODE                0x00020000\n#define LFRFID_T5577_BITRATE_RF_8          0\n#define LFRFID_T5577_BITRATE_RF_16         0x00040000\n#define LFRFID_T5577_BITRATE_RF_32         0x00080000\n#define LFRFID_T5577_BITRATE_RF_40         0x000C0000\n#define LFRFID_T5577_BITRATE_RF_50         0x00100000\n#define LFRFID_T5577_BITRATE_RF_64         0x00140000\n#define LFRFID_T5577_BITRATE_RF_100        0x00180000\n#define LFRFID_T5577_BITRATE_RF_128        0x001C0000\n#define LFRFID_T5577_TESTMODE_DISABLED     0x60000000\n\ntypedef struct {\n    uint32_t block[LFRFID_T5577_BLOCK_COUNT];\n    uint32_t blocks_to_write;\n    uint8_t mask;\n} LFRFIDT5577;\n\n/**\n * @brief Write T5577 tag data to tag\n * \n * @param data \n */\nvoid t5577_write(LFRFIDT5577* data);\n\nvoid t5577_write_with_pass(LFRFIDT5577* data, uint32_t password);\n\nvoid t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/lfrfid/tools/varint_pair.c",
    "content": "#include \"varint_pair.h\"\n#include <toolbox/varint.h>\n\n#define VARINT_PAIR_SIZE 10\n\nstruct VarintPair {\n    size_t data_length;\n    uint8_t data[VARINT_PAIR_SIZE];\n};\n\nVarintPair* varint_pair_alloc(void) {\n    VarintPair* pair = malloc(sizeof(VarintPair));\n    pair->data_length = 0;\n    return pair;\n}\n\nvoid varint_pair_free(VarintPair* pair) {\n    free(pair);\n}\n\nbool varint_pair_pack(VarintPair* pair, bool first, uint32_t value) {\n    bool result = false;\n\n    if(first) {\n        if(pair->data_length == 0) {\n            pair->data_length = varint_uint32_pack(value, pair->data);\n        } else {\n            pair->data_length = 0;\n        }\n    } else {\n        if(pair->data_length != 0) {\n            pair->data_length += varint_uint32_pack(value, pair->data + pair->data_length);\n            result = true;\n        }\n    }\n\n    return result;\n}\n\nbool varint_pair_unpack(\n    uint8_t* data,\n    size_t data_length,\n    uint32_t* value_1,\n    uint32_t* value_2,\n    size_t* length) {\n    size_t size = 0;\n    uint32_t tmp_value_1;\n    uint32_t tmp_value_2;\n\n    size += varint_uint32_unpack(&tmp_value_1, &data[size], data_length);\n\n    if(size >= data_length) {\n        return false;\n    }\n\n    size += varint_uint32_unpack(&tmp_value_2, &data[size], (size_t)(data_length - size));\n\n    *value_1 = tmp_value_1;\n    *value_2 = tmp_value_2;\n    *length = size;\n\n    return true;\n}\n\nuint8_t* varint_pair_get_data(VarintPair* pair) {\n    return pair->data;\n}\n\nsize_t varint_pair_get_size(VarintPair* pair) {\n    return pair->data_length;\n}\n\nvoid varint_pair_reset(VarintPair* pair) {\n    pair->data_length = 0;\n}\n"
  },
  {
    "path": "lib/lfrfid/tools/varint_pair.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct VarintPair VarintPair;\n\n/**\n * @brief Allocate a new VarintPair instance\n * \n * VarintPair is a buffer that holds pair of varint values\n * @return VarintPair* \n */\nVarintPair* varint_pair_alloc(void);\n\n/**\n * @brief Free a VarintPair instance\n * \n * @param pair \n */\nvoid varint_pair_free(VarintPair* pair);\n\n/**\n * @brief Write varint pair to buffer\n * \n * @param pair \n * @param first \n * @param value \n * @return bool pair complete and needs to be written\n */\nbool varint_pair_pack(VarintPair* pair, bool first, uint32_t value);\n\n/**\n * @brief Get pointer to varint pair buffer\n * \n * @param pair \n * @return uint8_t* \n */\nuint8_t* varint_pair_get_data(VarintPair* pair);\n\n/**\n * @brief Get size of varint pair buffer\n * \n * @param pair \n * @return size_t\n */\nsize_t varint_pair_get_size(VarintPair* pair);\n\n/**\n * @brief Reset varint pair buffer\n * \n * @param pair \n */\nvoid varint_pair_reset(VarintPair* pair);\n\n/**\n * @brief Unpack varint pair to uint32_t pair from buffer\n * \n * @param data \n * @param data_length \n * @param value_1 \n * @param value_2 \n * @param length \n * @return bool \n */\nbool varint_pair_unpack(\n    uint8_t* data,\n    size_t data_length,\n    uint32_t* value_1,\n    uint32_t* value_2,\n    size_t* length);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/libusb_stm32.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/libusb_stm32/inc\",\n    ],\n    SDK_HEADERS=env.GlobRecursive(\n        \"*.h\",\n        Dir(\"libusb_stm32/inc\"),\n    ),\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"usb_stm32\")\nlibenv.ApplyLibFlags()\nlibenv.Append(\n    CPPDEFINES=[\n        (\"USB_PMASIZE\", \"0x400\"),\n    ],\n)\n\n\nsources = [\n    \"libusb_stm32/src/usbd_core.c\",\n    \"libusb_stm32/src/usbd_stm32wb55_devfs.c\",\n]\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/mbedtls.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        # \"#/lib/mbedtls\",\n        \"#/lib/mbedtls/include\",\n    ],\n    SDK_HEADERS=[\n        File(\"mbedtls/include/mbedtls/aes.h\"),\n        File(\"mbedtls/include/mbedtls/des.h\"),\n        File(\"mbedtls/include/mbedtls/sha1.h\"),\n        File(\"mbedtls/include/mbedtls/sha256.h\"),\n        File(\"mbedtls/include/mbedtls/md5.h\"),\n        File(\"mbedtls/include/mbedtls/md.h\"),\n        File(\"mbedtls/include/mbedtls/ecdsa.h\"),\n        File(\"mbedtls/include/mbedtls/ecdh.h\"),\n        File(\"mbedtls/include/mbedtls/ecp.h\"),\n        # File(\"mbedtls/include/mbedtls/sha1.h\"),\n    ],\n    CPPDEFINES=[(\"MBEDTLS_CONFIG_FILE\", '\\\\\"mbedtls_cfg.h\\\\\"')],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"mbedtls\")\nlibenv.ApplyLibFlags()\n\nlibenv.AppendUnique(\n    CCFLAGS=[\n        # Required for lib to be linkable with .faps\n        \"-mword-relocations\",\n        \"-mlong-calls\",\n        # Crappy code :)\n        \"-Wno-redundant-decls\",\n    ],\n)\n\n# If we were to build full mbedtls, we would need to use this:\n# sources = libenv.GlobRecursive(\"*.c*\", \"mbedtls/library\")\n# Otherwise, we can just use the files we need:\nsources = [\n    File(\"mbedtls/library/aes.c\"),\n    File(\"mbedtls/library/bignum.c\"),\n    File(\"mbedtls/library/bignum_core.c\"),\n    File(\"mbedtls/library/ecdsa.c\"),\n    File(\"mbedtls/library/ecp.c\"),\n    File(\"mbedtls/library/ecp_curves.c\"),\n    File(\"mbedtls/library/md.c\"),\n    File(\"mbedtls/library/md5.c\"),\n    File(\"mbedtls/library/platform_util.c\"),\n    File(\"mbedtls/library/ripemd160.c\"),\n    File(\"mbedtls/library/sha1.c\"),\n    File(\"mbedtls/library/sha256.c\"),\n    File(\"mbedtls/library/des.c\"),\n]\nDepends(sources, File(\"mbedtls_cfg.h\"))\n\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/mbedtls_cfg.h",
    "content": "#pragma once\n\n/**\n* A subset of the mbedTLS configuration options that are relevant to the\n* Flipper Zero firmware and apps. They are built to \"mbedtls\" library you can \n* link your apps with.\n* \n* If you need more features, either bring the full mbedtls library into your\n* app using \"fap_private_libs\" or open an issue on GitHub to add them to the\n* default configuration.\n**/\n\n#define MBEDTLS_HAVE_ASM\n\n#define MBEDTLS_NO_UDBL_DIVISION\n#define MBEDTLS_NO_64BIT_MULTIPLICATION\n\n#define MBEDTLS_DEPRECATED_WARNING\n\n#define MBEDTLS_AES_FEWER_TABLES\n// #define MBEDTLS_CHECK_RETURN_WARNING\n\n#define MBEDTLS_CIPHER_MODE_CBC\n#define MBEDTLS_CIPHER_MODE_CFB\n#define MBEDTLS_CIPHER_MODE_CTR\n#define MBEDTLS_CIPHER_MODE_OFB\n#define MBEDTLS_CIPHER_MODE_XTS\n\n#define MBEDTLS_CIPHER_PADDING_PKCS7\n#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS\n#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN\n#define MBEDTLS_CIPHER_PADDING_ZEROS\n\n/* Short Weierstrass curves (supporting ECP, ECDH, ECDSA) */\n// #define MBEDTLS_ECP_DP_SECP192R1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP224R1_ENABLED\n#define MBEDTLS_ECP_DP_SECP256R1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP384R1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP521R1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP192K1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP224K1_ENABLED\n// #define MBEDTLS_ECP_DP_SECP256K1_ENABLED\n// #define MBEDTLS_ECP_DP_BP256R1_ENABLED\n// #define MBEDTLS_ECP_DP_BP384R1_ENABLED\n// #define MBEDTLS_ECP_DP_BP512R1_ENABLED\n/* Montgomery curves (supporting ECP) */\n// #define MBEDTLS_ECP_DP_CURVE25519_ENABLED\n// #define MBEDTLS_ECP_DP_CURVE448_ENABLED\n\n#define MBEDTLS_ECP_NIST_OPTIM\n\n#define MBEDTLS_GENPRIME\n// #define MBEDTLS_PKCS1_V15\n// #define MBEDTLS_PKCS1_V21\n\n#define MBEDTLS_MD_C\n\n#define MBEDTLS_ASN1_PARSE_C\n#define MBEDTLS_ASN1_WRITE_C\n#define MBEDTLS_BASE64_C\n#define MBEDTLS_BIGNUM_C\n#define MBEDTLS_OID_C\n\n// #define MBEDTLS_CHACHA20_C\n// #define MBEDTLS_CHACHAPOLY_C\n#define MBEDTLS_CIPHER_C\n#define MBEDTLS_DES_C\n#define MBEDTLS_DHM_C\n\n#define MBEDTLS_ECDH_C\n\n#define MBEDTLS_ECDSA_C\n#define MBEDTLS_ECP_C\n\n#define MBEDTLS_GCM_C\n\n#define MBEDTLS_AES_C\n#define MBEDTLS_MD5_C\n\n// #define MBEDTLS_PEM_PARSE_C\n// #define MBEDTLS_PEM_WRITE_C\n\n// #define MBEDTLS_PLATFORM_MEMORY\n// #define MBEDTLS_PLATFORM_C\n\n// #define MBEDTLS_RIPEMD160_C\n// #define MBEDTLS_RSA_C\n#define MBEDTLS_SHA224_C\n#define MBEDTLS_SHA256_C\n#define MBEDTLS_SHA1_C\n\n#define MBEDTLS_ERROR_C"
  },
  {
    "path": "lib/microtar.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/microtar/src\",\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"microtar\")\nlibenv.ApplyLibFlags()\n\nlibenv.Append(\n    CPPDEFINES=[\"MICROTAR_DISABLE_API_CHECKS\"],\n)\n\nsources = [File(\"microtar/src/microtar.c\")]\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/mjs/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/mjs\",\n    ],\n    SDK_HEADERS=[\n        File(\"mjs_core_public.h\"),\n        File(\"mjs_exec_public.h\"),\n        File(\"mjs_object_public.h\"),\n        File(\"mjs_string_public.h\"),\n        File(\"mjs_array_public.h\"),\n        File(\"mjs_primitive_public.h\"),\n        File(\"mjs_util_public.h\"),\n        File(\"mjs_array_buf_public.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"mjs\")\nlibenv.ApplyLibFlags()\n\nlibenv.AppendUnique(\n    CCFLAGS=[\n        \"-Wno-redundant-decls\",\n        \"-Wno-unused-function\",\n    ],\n)\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/mjs/common/cs_dbg.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cs_dbg.h\"\n\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"cs_time.h\"\n#include \"str_util.h\"\n\nenum cs_log_level cs_log_level WEAK =\n#if CS_ENABLE_DEBUG\n    LL_VERBOSE_DEBUG;\n#else\n    LL_ERROR;\n#endif\n\n#if CS_ENABLE_STDIO\nstatic char* s_file_level = NULL;\n\nvoid cs_log_set_file_level(const char* file_level) WEAK;\n\nFILE* cs_log_file WEAK = NULL;\n\n#if CS_LOG_ENABLE_TS_DIFF\ndouble cs_log_ts WEAK;\n#endif\n\nenum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;\n\nvoid cs_log_set_file_level(const char* file_level) {\n    char* fl = s_file_level;\n    if(file_level != NULL) {\n        s_file_level = strdup(file_level);\n    } else {\n        s_file_level = NULL;\n    }\n    free(fl);\n}\n\nint cs_log_print_prefix(enum cs_log_level level, const char* file, int ln) WEAK;\nint cs_log_print_prefix(enum cs_log_level level, const char* file, int ln) {\n    char prefix[CS_LOG_PREFIX_LEN], *q;\n    const char* p;\n    size_t fl = 0, ll = 0, pl = 0;\n\n    if(level > cs_log_level && s_file_level == NULL) return 0;\n\n    p = file + strlen(file);\n\n    while(p != file) {\n        const char c = *(p - 1);\n        if(c == '/' || c == '\\\\') break;\n        p--;\n        fl++;\n    }\n\n    ll = (ln < 10000 ? (ln < 1000 ? (ln < 100 ? (ln < 10 ? 1 : 2) : 3) : 4) : 5);\n    if(fl > (sizeof(prefix) - ll - 2)) fl = (sizeof(prefix) - ll - 2);\n\n    pl = fl + 1 + ll;\n    memcpy(prefix, p, fl);\n    q = prefix + pl;\n    memset(q, ' ', sizeof(prefix) - pl);\n    do {\n        *(--q) = '0' + (ln % 10);\n        ln /= 10;\n    } while(ln > 0);\n    *(--q) = ':';\n\n    if(s_file_level != NULL) {\n        enum cs_log_level pll = cs_log_level;\n        struct mg_str fl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);\n        struct mg_str k, v;\n        while((fl = mg_next_comma_list_entry_n(fl, &k, &v)).p != NULL) {\n            bool yes = !(!mg_str_starts_with(ps, k) || v.len == 0);\n            if(!yes) continue;\n            pll = (enum cs_log_level)(*v.p - '0');\n            break;\n        }\n        if(level > pll) return 0;\n    }\n\n    if(cs_log_file == NULL) cs_log_file = stderr;\n    cs_log_cur_msg_level = level;\n    fwrite(prefix, 1, sizeof(prefix), cs_log_file);\n#if CS_LOG_ENABLE_TS_DIFF\n    {\n        double now = cs_time();\n        fprintf(cs_log_file, \"%7u \", (unsigned int)((now - cs_log_ts) * 1000000));\n        cs_log_ts = now;\n    }\n#endif\n    return 1;\n}\n\nvoid cs_log_printf(const char* fmt, ...) WEAK;\nvoid cs_log_printf(const char* fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    vfprintf(cs_log_file, fmt, ap);\n    va_end(ap);\n    fputc('\\n', cs_log_file);\n    fflush(cs_log_file);\n    cs_log_cur_msg_level = LL_NONE;\n}\n\nvoid cs_log_set_file(FILE* file) WEAK;\nvoid cs_log_set_file(FILE* file) {\n    cs_log_file = file;\n}\n\n#else\n\nint cs_log_print_prefix(enum cs_log_level level, const char* file, int ln) WEAK;\nint cs_log_print_prefix(enum cs_log_level level, const char* file, int ln) {\n    (void)level;\n    (void)file;\n    (void)ln;\n    return 0;\n}\n\nvoid cs_log_printf(const char* fmt, ...) WEAK;\nvoid cs_log_printf(const char* fmt, ...) {\n    (void)fmt;\n}\n\nvoid cs_log_set_file_level(const char* file_level) {\n    (void)file_level;\n}\n\n#endif /* CS_ENABLE_STDIO */\n\nvoid cs_log_set_level(enum cs_log_level level) WEAK;\nvoid cs_log_set_level(enum cs_log_level level) {\n    cs_log_level = level;\n#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO\n    cs_log_ts = cs_time();\n#endif\n}\n"
  },
  {
    "path": "lib/mjs/common/cs_dbg.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_CS_DBG_H_\n#define CS_COMMON_CS_DBG_H_\n\n#include \"platform.h\"\n\n#if CS_ENABLE_STDIO\n#include <stdio.h>\n#endif\n\n#ifndef CS_ENABLE_DEBUG\n#define CS_ENABLE_DEBUG 0\n#endif\n\n#ifndef CS_LOG_PREFIX_LEN\n#define CS_LOG_PREFIX_LEN 24\n#endif\n\n#ifndef CS_LOG_ENABLE_TS_DIFF\n#define CS_LOG_ENABLE_TS_DIFF 0\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it.\n */\nenum cs_log_level {\n    LL_NONE = -1,\n    LL_ERROR = 0,\n    LL_WARN = 1,\n    LL_INFO = 2,\n    LL_DEBUG = 3,\n    LL_VERBOSE_DEBUG = 4,\n\n    _LL_MIN = -2,\n    _LL_MAX = 5,\n};\n\n/*\n * Set max log level to print; messages with the level above the given one will\n * not be printed.\n */\nvoid cs_log_set_level(enum cs_log_level level);\n\n/*\n * A comma-separated set of prefix=level.\n * prefix is matched against the log prefix exactly as printed, including line\n * number, but partial match is ok. Check stops on first matching entry.\n * If nothing matches, default level is used.\n *\n * Examples:\n *   main.c:=4 - everything from main C at verbose debug level.\n *   mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*\n *\n */\nvoid cs_log_set_file_level(const char* file_level);\n\n/*\n * Helper function which prints message prefix with the given `level`.\n * If message should be printed (according to the current log level\n * and filter), prints the prefix and returns 1, otherwise returns 0.\n *\n * Clients should typically just use `LOG()` macro.\n */\nint cs_log_print_prefix(enum cs_log_level level, const char* fname, int line);\n\nextern enum cs_log_level cs_log_level;\n\n#if CS_ENABLE_STDIO\n\n/*\n * Set file to write logs into. If `NULL`, logs go to `stderr`.\n */\nvoid cs_log_set_file(FILE* file);\n\n/*\n * Prints log to the current log file, appends \"\\n\" in the end and flushes the\n * stream.\n */\nvoid cs_log_printf(const char* fmt, ...) PRINTF_LIKE(1, 2);\n\n#if CS_ENABLE_STDIO\n\n/*\n * Format and print message `x` with the given level `l`. Example:\n *\n * ```c\n * LOG(LL_INFO, (\"my info message: %d\", 123));\n * LOG(LL_DEBUG, (\"my debug message: %d\", 123));\n * ```\n */\n#define LOG(l, x)                                        \\\n    do {                                                 \\\n        if(cs_log_print_prefix(l, __FILE__, __LINE__)) { \\\n            cs_log_printf x;                             \\\n        }                                                \\\n    } while(0)\n\n#else\n\n#define LOG(l, x) ((void)l)\n\n#endif\n\n#ifndef CS_NDEBUG\n\n/*\n * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))`\n */\n#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)\n\n#else /* NDEBUG */\n\n#define DBG(x)\n\n#endif\n\n#else /* CS_ENABLE_STDIO */\n\n#define LOG(l, x)\n#define DBG(x)\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* CS_COMMON_CS_DBG_H_ */\n"
  },
  {
    "path": "lib/mjs/common/cs_dirent.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef EXCLUDE_COMMON\n\n#include \"mg_mem.h\"\n#include \"cs_dirent.h\"\n\n/*\n * This file contains POSIX opendir/closedir/readdir API implementation\n * for systems which do not natively support it (e.g. Windows).\n */\n\n#ifdef _WIN32\nstruct win32_dir {\n  DIR d;\n  HANDLE handle;\n  WIN32_FIND_DATAW info;\n  struct dirent result;\n};\n\nDIR *opendir(const char *name) {\n  struct win32_dir *dir = NULL;\n  wchar_t wpath[MAX_PATH];\n  DWORD attrs;\n\n  if (name == NULL) {\n    SetLastError(ERROR_BAD_ARGUMENTS);\n  } else if ((dir = (struct win32_dir *) MG_MALLOC(sizeof(*dir))) == NULL) {\n    SetLastError(ERROR_NOT_ENOUGH_MEMORY);\n  } else {\n    to_wchar(name, wpath, ARRAY_SIZE(wpath));\n    attrs = GetFileAttributesW(wpath);\n    if (attrs != 0xFFFFFFFF && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {\n      (void) wcscat(wpath, L\"\\\\*\");\n      dir->handle = FindFirstFileW(wpath, &dir->info);\n      dir->result.d_name[0] = '\\0';\n    } else {\n      MG_FREE(dir);\n      dir = NULL;\n    }\n  }\n\n  return (DIR *) dir;\n}\n\nint closedir(DIR *d) {\n  struct win32_dir *dir = (struct win32_dir *) d;\n  int result = 0;\n\n  if (dir != NULL) {\n    if (dir->handle != INVALID_HANDLE_VALUE)\n      result = FindClose(dir->handle) ? 0 : -1;\n    MG_FREE(dir);\n  } else {\n    result = -1;\n    SetLastError(ERROR_BAD_ARGUMENTS);\n  }\n\n  return result;\n}\n\nstruct dirent *readdir(DIR *d) {\n  struct win32_dir *dir = (struct win32_dir *) d;\n  struct dirent *result = NULL;\n\n  if (dir) {\n    memset(&dir->result, 0, sizeof(dir->result));\n    if (dir->handle != INVALID_HANDLE_VALUE) {\n      result = &dir->result;\n      (void) WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1,\n                                 result->d_name, sizeof(result->d_name), NULL,\n                                 NULL);\n\n      if (!FindNextFileW(dir->handle, &dir->info)) {\n        (void) FindClose(dir->handle);\n        dir->handle = INVALID_HANDLE_VALUE;\n      }\n\n    } else {\n      SetLastError(ERROR_FILE_NOT_FOUND);\n    }\n  } else {\n    SetLastError(ERROR_BAD_ARGUMENTS);\n  }\n\n  return result;\n}\n#endif\n\n#endif /* EXCLUDE_COMMON */\n\n/* ISO C requires a translation unit to contain at least one declaration */\ntypedef int cs_dirent_dummy;\n"
  },
  {
    "path": "lib/mjs/common/cs_dirent.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_CS_DIRENT_H_\n#define CS_COMMON_CS_DIRENT_H_\n\n#include <limits.h>\n\n#include \"platform.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n#ifdef CS_DEFINE_DIRENT\ntypedef struct { int dummy; } DIR;\n\nstruct dirent {\n  int d_ino;\n#ifdef _WIN32\n  char d_name[MAX_PATH];\n#else\n  /* TODO(rojer): Use PATH_MAX but make sure it's sane on every platform */\n  char d_name[256];\n#endif\n};\n\nDIR *opendir(const char *dir_name);\nint closedir(DIR *dir);\nstruct dirent *readdir(DIR *dir);\n#endif /* CS_DEFINE_DIRENT */\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* CS_COMMON_CS_DIRENT_H_ */\n"
  },
  {
    "path": "lib/mjs/common/cs_file.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cs_file.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifdef CS_MMAP\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#endif\n\n#ifdef CS_MMAP\nchar* cs_read_file(const char* path, size_t* size) WEAK;\nchar* cs_read_file(const char* path, size_t* size) {\n    FILE* fp;\n    char* data = NULL;\n    if((fp = fopen(path, \"rb\")) == NULL) {\n    } else if(fseek(fp, 0, SEEK_END) != 0) {\n        fclose(fp);\n    } else {\n        *size = ftell(fp);\n        data = (char*)malloc(*size + 1);\n        if(data != NULL) {\n            fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */\n            if(fread(data, 1, *size, fp) != *size) {\n                free(data);\n                return NULL;\n            }\n            data[*size] = '\\0';\n        }\n        fclose(fp);\n    }\n    return data;\n}\n\nchar* cs_mmap_file(const char* path, size_t* size) WEAK;\nchar* cs_mmap_file(const char* path, size_t* size) {\n    char* r;\n    int fd = open(path, O_RDONLY, 0);\n    struct stat st;\n    if(fd < 0) return NULL;\n    fstat(fd, &st);\n    *size = (size_t)st.st_size;\n    r = (char*)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);\n    if(r == MAP_FAILED) return NULL;\n    return r;\n}\n#endif\n"
  },
  {
    "path": "lib/mjs/common/cs_file.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_CS_FILE_H_\n#define CS_COMMON_CS_FILE_H_\n\n#include \"platform.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Read whole file `path` in memory. It is responsibility of the caller\n * to `free()` allocated memory. File content is guaranteed to be\n * '\\0'-terminated. File size is returned in `size` variable, which does not\n * count terminating `\\0`.\n * Return: allocated memory, or NULL on error.\n */\nchar *cs_read_file(const char *path, size_t *size);\n\n#ifdef CS_MMAP\n/*\n * Only on platforms which support mmapping: mmap file `path` to the returned\n * address. File size is written to `*size`.\n */\nchar *cs_mmap_file(const char *path, size_t *size);\n#endif\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* CS_COMMON_CS_FILE_H_ */\n"
  },
  {
    "path": "lib/mjs/common/cs_time.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cs_time.h\"\n\n#if CS_ENABLE_STDIO\n\n#ifndef _WIN32\n#include <stddef.h>\n/*\n * There is no sys/time.h on ARMCC.\n */\n#if !(defined(__ARMCC_VERSION) || defined(__ICCARM__)) && !defined(__TI_COMPILER_VERSION__) && \\\n    (!defined(CS_PLATFORM) || CS_PLATFORM != CS_P_NXP_LPC)\n#include <sys/time.h>\n#endif\n#else\n#include <windows.h>\n#endif\n\ndouble cs_time(void) WEAK;\ndouble cs_time(void) {\n    double now;\n#ifndef _WIN32\n    struct timeval tv;\n    if(gettimeofday(&tv, NULL /* tz */) != 0) return 0;\n    now = (double)tv.tv_sec + (((double)tv.tv_usec) / (double)1000000.0);\n#else\n    SYSTEMTIME sysnow;\n    FILETIME ftime;\n    GetLocalTime(&sysnow);\n    SystemTimeToFileTime(&sysnow, &ftime);\n    /*\n   * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64\n   * This should not cause a problems in this (21th) century\n   * 2. Windows FILETIME is a number of 100-nanosecond intervals since January\n   * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC,\n   * thus, we need to convert to seconds and adjust amount (subtract 11644473600\n   * seconds)\n   */\n    now = (double)(((int64_t)ftime.dwLowDateTime + ((int64_t)ftime.dwHighDateTime << 32)) /\n                   10000000.0) -\n          11644473600;\n#endif /* _WIN32 */\n    return now;\n}\n\ndouble cs_timegm(const struct tm* tm) {\n    /* Month-to-day offset for non-leap-years. */\n    static const int month_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};\n\n    /* Most of the calculation is easy; leap years are the main difficulty. */\n    int month = tm->tm_mon % 12;\n    int year = tm->tm_year + tm->tm_mon / 12;\n    int year_for_leap;\n    int64_t rt;\n\n    if(month < 0) { /* Negative values % 12 are still negative. */\n        month += 12;\n        --year;\n    }\n\n    /* This is the number of Februaries since 1900. */\n    year_for_leap = (month > 1) ? year + 1 : year;\n\n    rt = tm->tm_sec /* Seconds */\n         + 60 * (tm->tm_min /* Minute = 60 seconds */\n                 + 60 * (tm->tm_hour /* Hour = 60 minutes */\n                         + 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */\n                                 + 365 * (year - 70) /* Year = 365 days */\n                                 + (year_for_leap - 69) / 4 /* Every 4 years is leap... */\n                                 - (year_for_leap - 1) / 100 /* Except centuries... */\n                                 + (year_for_leap + 299) / 400))); /* Except 400s. */\n    return rt < 0 ? -1 : (double)rt;\n}\n\n#endif"
  },
  {
    "path": "lib/mjs/common/cs_time.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_CS_TIME_H_\n#define CS_COMMON_CS_TIME_H_\n\n#include <time.h>\n\n#include \"platform.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n/* Sub-second granularity time(). */\ndouble cs_time(void);\n\n/*\n * Similar to (non-standard) timegm, converts broken-down time into the number\n * of seconds since Unix Epoch.\n */\ndouble cs_timegm(const struct tm* tm);\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* CS_COMMON_CS_TIME_H_ */\n"
  },
  {
    "path": "lib/mjs/common/cs_varint.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cs_varint.h\"\n\nsize_t cs_varint_llen(uint64_t num) {\n  size_t llen = 0;\n\n  do {\n    llen++;\n  } while (num >>= 7);\n\n  return llen;\n}\n\nsize_t cs_varint_encode(uint64_t num, uint8_t *buf, size_t buf_size) {\n  size_t llen = 0;\n\n  do {\n    uint8_t byte = num & 0x7f;\n    num >>= 7;\n    if (num != 0) byte |= 0x80;\n    if (llen < buf_size) *buf++ = byte;\n    llen++;\n  } while (num != 0);\n\n  return llen;\n}\n\nbool cs_varint_decode(const uint8_t *buf, size_t buf_size, uint64_t *num,\n                      size_t *llen) {\n  size_t i = 0, shift = 0;\n  uint64_t n = 0;\n\n  do {\n    if (i == buf_size || i == (8 * sizeof(*num) / 7 + 1)) return false;\n    /*\n     * Each byte of varint contains 7 bits, in little endian order.\n     * MSB is a continuation bit: it tells whether next byte is used.\n     */\n    n |= ((uint64_t)(buf[i] & 0x7f)) << shift;\n    /*\n     * First we increment i, then check whether it is within boundary and\n     * whether decoded byte had continuation bit set.\n     */\n    i++;\n    shift += 7;\n  } while (shift < sizeof(uint64_t) * 8 && (buf[i - 1] & 0x80));\n\n  *num = n;\n  *llen = i;\n\n  return true;\n}\n\nuint64_t cs_varint_decode_unsafe(const uint8_t *buf, int *llen) {\n  uint64_t v;\n  size_t l;\n  cs_varint_decode(buf, ~0, &v, &l);\n  *llen = l;\n  return v;\n}\n"
  },
  {
    "path": "lib/mjs/common/cs_varint.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_CS_VARINT_H_\n#define CS_COMMON_CS_VARINT_H_\n\n#if defined(_WIN32) && _MSC_VER < 1700\ntypedef unsigned char uint8_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Returns number of bytes required to encode `num`. */\nsize_t cs_varint_llen(uint64_t num);\n\n/*\n * Encodes `num` into `buf`.\n * Returns number of bytes required to encode `num`.\n * Note: return value may be greater than `buf_size` but the function will only\n * write `buf_size` bytes.\n */\nsize_t cs_varint_encode(uint64_t num, uint8_t *buf, size_t buf_size);\n\n/*\n * Decodes varint stored in `buf`.\n * Stores the number of bytes consumed into `llen`.\n * If there aren't enough bytes in `buf` to decode a number, returns false.\n */\nbool cs_varint_decode(const uint8_t *buf, size_t buf_size, uint64_t *num,\n                      size_t *llen);\n\nuint64_t cs_varint_decode_unsafe(const uint8_t *buf, int *llen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* CS_COMMON_CS_VARINT_H_ */\n"
  },
  {
    "path": "lib/mjs/common/frozen/frozen.c",
    "content": "/*\n * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>\n * Copyright (c) 2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */\n\n#include \"frozen.h\"\n\n#include <ctype.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#if !defined(WEAK)\n#if(defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)\n#define WEAK __attribute__((weak))\n#else\n#define WEAK\n#endif\n#endif\n\n#ifdef _WIN32\n#undef snprintf\n#undef vsnprintf\n#define snprintf  cs_win_snprintf\n#define vsnprintf cs_win_vsnprintf\nint cs_win_snprintf(char* str, size_t size, const char* format, ...);\nint cs_win_vsnprintf(char* str, size_t size, const char* format, va_list ap);\n#if _MSC_VER >= 1700\n#include <stdint.h>\n#else\ntypedef _int64 int64_t;\ntypedef unsigned _int64 uint64_t;\n#endif\n#define PRId64 \"I64d\"\n#define PRIu64 \"I64u\"\n#else /* _WIN32 */\n/* <inttypes.h> wants this for C++ */\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n#include <inttypes.h>\n#endif /* _WIN32 */\n\n#ifndef INT64_FMT\n#define INT64_FMT PRId64\n#endif\n#ifndef UINT64_FMT\n#define UINT64_FMT PRIu64\n#endif\n\n#ifndef va_copy\n#define va_copy(x, y) x = y\n#endif\n\n#ifndef JSON_ENABLE_ARRAY\n#define JSON_ENABLE_ARRAY 1\n#endif\n\nstruct frozen {\n    const char* end;\n    const char* cur;\n\n    const char* cur_name;\n    size_t cur_name_len;\n\n    /* For callback API */\n    char path[JSON_MAX_PATH_LEN];\n    size_t path_len;\n    void* callback_data;\n    json_walk_callback_t callback;\n};\n\nstruct fstate {\n    const char* ptr;\n    size_t path_len;\n};\n\n#define SET_STATE(fr, ptr, str, len)                \\\n    struct fstate fstate = {(ptr), (fr)->path_len}; \\\n    json_append_to_path((fr), (str), (len));\n\n#define CALL_BACK(fr, tok, value, len)                                                         \\\n    do {                                                                                       \\\n        if((fr)->callback && ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) { \\\n            struct json_token t = {(value), (int)(len), (tok)};                                \\\n                                                                                               \\\n            /* Call the callback with the given value and current name */                      \\\n            (fr)->callback(                                                                    \\\n                (fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, (fr)->path, &t);      \\\n                                                                                               \\\n            /* Reset the name */                                                               \\\n            (fr)->cur_name = NULL;                                                             \\\n            (fr)->cur_name_len = 0;                                                            \\\n        }                                                                                      \\\n    } while(0)\n\nstatic int json_append_to_path(struct frozen* f, const char* str, int size) {\n    int n = f->path_len;\n    int left = sizeof(f->path) - n - 1;\n    if(size > left) size = left;\n    memcpy(f->path + n, str, size);\n    f->path[n + size] = '\\0';\n    f->path_len += size;\n    return n;\n}\n\nstatic void json_truncate_path(struct frozen* f, size_t len) {\n    f->path_len = len;\n    f->path[len] = '\\0';\n}\n\nstatic int json_parse_object(struct frozen* f);\nstatic int json_parse_value(struct frozen* f);\n\n#define EXPECT(cond, err_code)         \\\n    do {                               \\\n        if(!(cond)) return (err_code); \\\n    } while(0)\n\n#define TRY(expr)             \\\n    do {                      \\\n        int _n = expr;        \\\n        if(_n < 0) return _n; \\\n    } while(0)\n\n#define END_OF_STRING (-1)\n\nstatic int json_left(const struct frozen* f) {\n    return f->end - f->cur;\n}\n\nstatic int json_isspace(int ch) {\n    return ch == ' ' || ch == '\\t' || ch == '\\r' || ch == '\\n';\n}\n\nstatic void json_skip_whitespaces(struct frozen* f) {\n    while(f->cur < f->end && json_isspace(*f->cur))\n        f->cur++;\n}\n\nstatic int json_cur(struct frozen* f) {\n    json_skip_whitespaces(f);\n    return f->cur >= f->end ? END_OF_STRING : *(unsigned char*)f->cur;\n}\n\nstatic int json_test_and_skip(struct frozen* f, int expected) {\n    int ch = json_cur(f);\n    if(ch == expected) {\n        f->cur++;\n        return 0;\n    }\n    return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;\n}\n\nstatic int json_isalpha(int ch) {\n    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');\n}\n\nstatic int json_isdigit(int ch) {\n    return ch >= '0' && ch <= '9';\n}\n\nstatic int json_isxdigit(int ch) {\n    return json_isdigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');\n}\n\nstatic int json_get_escape_len(const char* s, int len) {\n    switch(*s) {\n    case 'u':\n        return len < 6 ? JSON_STRING_INCOMPLETE :\n               json_isxdigit(s[1]) && json_isxdigit(s[2]) && json_isxdigit(s[3]) &&\n                       json_isxdigit(s[4]) ?\n                         5 :\n                         JSON_STRING_INVALID;\n    case '\"':\n    case '\\\\':\n    case '/':\n    case 'b':\n    case 'f':\n    case 'n':\n    case 'r':\n    case 't':\n        return len < 2 ? JSON_STRING_INCOMPLETE : 1;\n    default:\n        return JSON_STRING_INVALID;\n    }\n}\n\n/* identifier = letter { letter | digit | '_' } */\nstatic int json_parse_identifier(struct frozen* f) {\n    EXPECT(json_isalpha(json_cur(f)), JSON_STRING_INVALID);\n    {\n        SET_STATE(f, f->cur, \"\", 0);\n        while(f->cur < f->end &&\n              (*f->cur == '_' || json_isalpha(*f->cur) || json_isdigit(*f->cur))) {\n            f->cur++;\n        }\n        json_truncate_path(f, fstate.path_len);\n        CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr);\n    }\n    return 0;\n}\n\nstatic int json_get_utf8_char_len(unsigned char ch) {\n    if((ch & 0x80) == 0) return 1;\n    switch(ch & 0xf0) {\n    case 0xf0:\n        return 4;\n    case 0xe0:\n        return 3;\n    default:\n        return 2;\n    }\n}\n\n/* string = '\"' { quoted_printable_chars } '\"' */\nstatic int json_parse_string(struct frozen* f) {\n    int n, ch = 0, len = 0;\n    TRY(json_test_and_skip(f, '\"'));\n    {\n        SET_STATE(f, f->cur, \"\", 0);\n        for(; f->cur < f->end; f->cur += len) {\n            ch = *(unsigned char*)f->cur;\n            len = json_get_utf8_char_len((unsigned char)ch);\n            EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */\n            EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE);\n            if(ch == '\\\\') {\n                EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n);\n                len += n;\n            } else if(ch == '\"') {\n                json_truncate_path(f, fstate.path_len);\n                CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr);\n                f->cur++;\n                break;\n            };\n        }\n    }\n    return ch == '\"' ? 0 : JSON_STRING_INCOMPLETE;\n}\n\n/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */\nstatic int json_parse_number(struct frozen* f) {\n    int ch = json_cur(f);\n    SET_STATE(f, f->cur, \"\", 0);\n    if(ch == '-') f->cur++;\n    EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);\n    if(f->cur + 1 < f->end && f->cur[0] == '0' && f->cur[1] == 'x') {\n        f->cur += 2;\n        EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);\n        EXPECT(json_isxdigit(f->cur[0]), JSON_STRING_INVALID);\n        while(f->cur < f->end && json_isxdigit(f->cur[0]))\n            f->cur++;\n    } else {\n        EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID);\n        while(f->cur < f->end && json_isdigit(f->cur[0]))\n            f->cur++;\n        if(f->cur < f->end && f->cur[0] == '.') {\n            f->cur++;\n            EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);\n            EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID);\n            while(f->cur < f->end && json_isdigit(f->cur[0]))\n                f->cur++;\n        }\n        if(f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) {\n            f->cur++;\n            EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);\n            if((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++;\n            EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);\n            EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID);\n            while(f->cur < f->end && json_isdigit(f->cur[0]))\n                f->cur++;\n        }\n    }\n    json_truncate_path(f, fstate.path_len);\n    CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr);\n    return 0;\n}\n\n#if JSON_ENABLE_ARRAY\n/* array = '[' [ value { ',' value } ] ']' */\nstatic int json_parse_array(struct frozen* f) {\n    int i = 0, current_path_len;\n    char buf[20];\n    CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0);\n    TRY(json_test_and_skip(f, '['));\n    {\n        {\n            SET_STATE(f, f->cur - 1, \"\", 0);\n            while(json_cur(f) != ']') {\n                snprintf(buf, sizeof(buf), \"[%d]\", i);\n                i++;\n                current_path_len = json_append_to_path(f, buf, strlen(buf));\n                f->cur_name = f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/;\n                f->cur_name_len = strlen(buf) - 2 /*braces*/;\n                TRY(json_parse_value(f));\n                json_truncate_path(f, current_path_len);\n                if(json_cur(f) == ',') f->cur++;\n            }\n            TRY(json_test_and_skip(f, ']'));\n            json_truncate_path(f, fstate.path_len);\n            CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr);\n        }\n    }\n    return 0;\n}\n#endif /* JSON_ENABLE_ARRAY */\n\nstatic int json_expect(struct frozen* f, const char* s, int len, enum json_token_type tok_type) {\n    int i, n = json_left(f);\n    SET_STATE(f, f->cur, \"\", 0);\n    for(i = 0; i < len; i++) {\n        if(i >= n) return JSON_STRING_INCOMPLETE;\n        if(f->cur[i] != s[i]) return JSON_STRING_INVALID;\n    }\n    f->cur += len;\n    json_truncate_path(f, fstate.path_len);\n\n    CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr);\n\n    return 0;\n}\n\n/* value = 'null' | 'true' | 'false' | number | string | array | object */\nstatic int json_parse_value(struct frozen* f) {\n    int ch = json_cur(f);\n\n    switch(ch) {\n    case '\"':\n        TRY(json_parse_string(f));\n        break;\n    case '{':\n        TRY(json_parse_object(f));\n        break;\n#if JSON_ENABLE_ARRAY\n    case '[':\n        TRY(json_parse_array(f));\n        break;\n#endif\n    case 'n':\n        TRY(json_expect(f, \"null\", 4, JSON_TYPE_NULL));\n        break;\n    case 't':\n        TRY(json_expect(f, \"true\", 4, JSON_TYPE_TRUE));\n        break;\n    case 'f':\n        TRY(json_expect(f, \"false\", 5, JSON_TYPE_FALSE));\n        break;\n    case '-':\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n        TRY(json_parse_number(f));\n        break;\n    default:\n        return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;\n    }\n\n    return 0;\n}\n\n/* key = identifier | string */\nstatic int json_parse_key(struct frozen* f) {\n    int ch = json_cur(f);\n    if(json_isalpha(ch)) {\n        TRY(json_parse_identifier(f));\n    } else if(ch == '\"') {\n        TRY(json_parse_string(f));\n    } else {\n        return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;\n    }\n    return 0;\n}\n\n/* pair = key ':' value */\nstatic int json_parse_pair(struct frozen* f) {\n    int current_path_len;\n    const char* tok;\n    json_skip_whitespaces(f);\n    tok = f->cur;\n    TRY(json_parse_key(f));\n    {\n        f->cur_name = *tok == '\"' ? tok + 1 : tok;\n        f->cur_name_len = *tok == '\"' ? f->cur - tok - 2 : f->cur - tok;\n        current_path_len = json_append_to_path(f, f->cur_name, f->cur_name_len);\n    }\n    TRY(json_test_and_skip(f, ':'));\n    TRY(json_parse_value(f));\n    json_truncate_path(f, current_path_len);\n    return 0;\n}\n\n/* object = '{' pair { ',' pair } '}' */\nstatic int json_parse_object(struct frozen* f) {\n    CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0);\n    TRY(json_test_and_skip(f, '{'));\n    {\n        SET_STATE(f, f->cur - 1, \".\", 1);\n        while(json_cur(f) != '}') {\n            TRY(json_parse_pair(f));\n            if(json_cur(f) == ',') f->cur++;\n        }\n        TRY(json_test_and_skip(f, '}'));\n        json_truncate_path(f, fstate.path_len);\n        CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr);\n    }\n    return 0;\n}\n\nstatic int json_doit(struct frozen* f) {\n    if(f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID;\n    if(f->end == f->cur) return JSON_STRING_INCOMPLETE;\n    return json_parse_value(f);\n}\n\nint json_escape(struct json_out* out, const char* p, size_t len) WEAK;\nint json_escape(struct json_out* out, const char* p, size_t len) {\n    size_t i, cl, n = 0;\n    const char* hex_digits = \"0123456789abcdef\";\n    const char* specials = \"btnvfr\";\n\n    for(i = 0; i < len; i++) {\n        unsigned char ch = ((unsigned char*)p)[i];\n        if(ch == '\"' || ch == '\\\\') {\n            n += out->printer(out, \"\\\\\", 1);\n            n += out->printer(out, p + i, 1);\n        } else if(ch >= '\\b' && ch <= '\\r') {\n            n += out->printer(out, \"\\\\\", 1);\n            n += out->printer(out, &specials[ch - '\\b'], 1);\n        } else if(isprint(ch)) {\n            n += out->printer(out, p + i, 1);\n        } else if((cl = json_get_utf8_char_len(ch)) == 1) {\n            n += out->printer(out, \"\\\\u00\", 4);\n            n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1);\n            n += out->printer(out, &hex_digits[ch % 0xf], 1);\n        } else {\n            n += out->printer(out, p + i, cl);\n            i += cl - 1;\n        }\n    }\n\n    return n;\n}\n\nint json_printer_buf(struct json_out* out, const char* buf, size_t len) WEAK;\nint json_printer_buf(struct json_out* out, const char* buf, size_t len) {\n    size_t avail = out->u.buf.size - out->u.buf.len;\n    size_t n = len < avail ? len : avail;\n    memcpy(out->u.buf.buf + out->u.buf.len, buf, n);\n    out->u.buf.len += n;\n    if(out->u.buf.size > 0) {\n        size_t idx = out->u.buf.len;\n        if(idx >= out->u.buf.size) idx = out->u.buf.size - 1;\n        out->u.buf.buf[idx] = '\\0';\n    }\n    return len;\n}\n\nint json_printer_file(struct json_out* out, const char* buf, size_t len) WEAK;\nint json_printer_file(struct json_out* out, const char* buf, size_t len) {\n    return fwrite(buf, 1, len, out->u.fp);\n}\n\n#if JSON_ENABLE_BASE64\nstatic int b64idx(int c) {\n    if(c < 26) {\n        return c + 'A';\n    } else if(c < 52) {\n        return c - 26 + 'a';\n    } else if(c < 62) {\n        return c - 52 + '0';\n    } else {\n        return c == 62 ? '+' : '/';\n    }\n}\n\nstatic int b64rev(int c) {\n    if(c >= 'A' && c <= 'Z') {\n        return c - 'A';\n    } else if(c >= 'a' && c <= 'z') {\n        return c + 26 - 'a';\n    } else if(c >= '0' && c <= '9') {\n        return c + 52 - '0';\n    } else if(c == '+') {\n        return 62;\n    } else if(c == '/') {\n        return 63;\n    } else {\n        return 64;\n    }\n}\n\nstatic int b64enc(struct json_out* out, const unsigned char* p, int n) {\n    char buf[4];\n    int i, len = 0;\n    for(i = 0; i < n; i += 3) {\n        int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0;\n        buf[0] = b64idx(a >> 2);\n        buf[1] = b64idx((a & 3) << 4 | (b >> 4));\n        buf[2] = b64idx((b & 15) << 2 | (c >> 6));\n        buf[3] = b64idx(c & 63);\n        if(i + 1 >= n) buf[2] = '=';\n        if(i + 2 >= n) buf[3] = '=';\n        len += out->printer(out, buf, sizeof(buf));\n    }\n    return len;\n}\n\nstatic int b64dec(const char* src, int n, char* dst) {\n    const char* end = src + n;\n    int len = 0;\n    while(src + 3 < end) {\n        int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), d = b64rev(src[3]);\n        dst[len++] = (a << 2) | (b >> 4);\n        if(src[2] != '=') {\n            dst[len++] = (b << 4) | (c >> 2);\n            if(src[3] != '=') {\n                dst[len++] = (c << 6) | d;\n            }\n        }\n        src += 4;\n    }\n    return len;\n}\n#endif /* JSON_ENABLE_BASE64 */\n\nstatic unsigned char hexdec(const char* s) {\n#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W')\n    int a = tolower(*(const unsigned char*)s);\n    int b = tolower(*(const unsigned char*)(s + 1));\n    return (HEXTOI(a) << 4) | HEXTOI(b);\n}\n\nint json_vprintf(struct json_out* out, const char* fmt, va_list xap) WEAK;\nint json_vprintf(struct json_out* out, const char* fmt, va_list xap) {\n    int len = 0;\n    const char *quote = \"\\\"\", *null = \"null\";\n    va_list ap;\n    va_copy(ap, xap);\n\n    while(*fmt != '\\0') {\n        if(strchr(\":, \\r\\n\\t[]{}\\\"\", *fmt) != NULL) {\n            len += out->printer(out, fmt, 1);\n            fmt++;\n        } else if(fmt[0] == '%') {\n            char buf[21];\n            size_t skip = 2;\n\n            if(fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) {\n                int64_t val = va_arg(ap, int64_t);\n                const char* fmt2 = fmt[3] == 'u' ? \"%\" UINT64_FMT : \"%\" INT64_FMT;\n                snprintf(buf, sizeof(buf), fmt2, val);\n                len += out->printer(out, buf, strlen(buf));\n                skip += 2;\n            } else if(fmt[1] == 'z' && fmt[2] == 'u') {\n                size_t val = va_arg(ap, size_t);\n                snprintf(buf, sizeof(buf), \"%lu\", (unsigned long)val);\n                len += out->printer(out, buf, strlen(buf));\n                skip += 1;\n            } else if(fmt[1] == 'M') {\n                json_printf_callback_t f = va_arg(ap, json_printf_callback_t);\n                len += f(out, &ap);\n            } else if(fmt[1] == 'B') {\n                int val = va_arg(ap, int);\n                const char* str = val ? \"true\" : \"false\";\n                len += out->printer(out, str, strlen(str));\n            } else if(fmt[1] == 'H') {\n#if JSON_ENABLE_HEX\n                const char* hex = \"0123456789abcdef\";\n                int i, n = va_arg(ap, int);\n                const unsigned char* p = va_arg(ap, const unsigned char*);\n                len += out->printer(out, quote, 1);\n                for(i = 0; i < n; i++) {\n                    len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1);\n                    len += out->printer(out, &hex[p[i] & 0xf], 1);\n                }\n                len += out->printer(out, quote, 1);\n#endif /* JSON_ENABLE_HEX */\n            } else if(fmt[1] == 'V') {\n#if JSON_ENABLE_BASE64\n                const unsigned char* p = va_arg(ap, const unsigned char*);\n                int n = va_arg(ap, int);\n                len += out->printer(out, quote, 1);\n                len += b64enc(out, p, n);\n                len += out->printer(out, quote, 1);\n#endif /* JSON_ENABLE_BASE64 */\n            } else if(fmt[1] == 'Q' || (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) {\n                size_t l = 0;\n                const char* p;\n\n                if(fmt[1] == '.') {\n                    l = (size_t)va_arg(ap, int);\n                    skip += 2;\n                }\n                p = va_arg(ap, char*);\n\n                if(p == NULL) {\n                    len += out->printer(out, null, 4);\n                } else {\n                    if(fmt[1] == 'Q') {\n                        l = strlen(p);\n                    }\n                    len += out->printer(out, quote, 1);\n                    len += json_escape(out, p, l);\n                    len += out->printer(out, quote, 1);\n                }\n            } else {\n                /*\n         * we delegate printing to the system printf.\n         * The goal here is to delegate all modifiers parsing to the system\n         * printf, as you can see below we still have to parse the format\n         * types.\n         *\n         * Currently, %s with strings longer than 20 chars will require\n         * double-buffering (an auxiliary buffer will be allocated from heap).\n         * TODO(dfrank): reimplement %s and %.*s in order to avoid that.\n         */\n\n                const char* end_of_format_specifier = \"sdfFeEgGlhuIcx.*-0123456789\";\n                int n = strspn(fmt + 1, end_of_format_specifier);\n                char* pbuf = buf;\n                int need_len, size = sizeof(buf);\n                char fmt2[20];\n                va_list ap_copy;\n                strlcpy(fmt2, fmt, sizeof(fmt2));\n\n                va_copy(ap_copy, ap);\n                need_len = vsnprintf(pbuf, size, fmt2, ap_copy);\n                va_end(ap_copy);\n\n                if(need_len < 0) {\n                    /*\n           * Windows & eCos vsnprintf implementation return -1 on overflow\n           * instead of needed size.\n           */\n                    pbuf = NULL;\n                    while(need_len < 0) {\n                        free(pbuf);\n                        size *= 2;\n                        if((pbuf = (char*)malloc(size)) == NULL) break;\n                        va_copy(ap_copy, ap);\n                        need_len = vsnprintf(pbuf, size, fmt2, ap_copy);\n                        va_end(ap_copy);\n                    }\n                } else if(need_len >= (int)sizeof(buf)) {\n                    /*\n           * resulting string doesn't fit into a stack-allocated buffer `buf`,\n           * so we need to allocate a new buffer from heap and use it\n           */\n                    if((pbuf = (char*)malloc(need_len + 1)) != NULL) {\n                        va_copy(ap_copy, ap);\n                        vsnprintf(pbuf, need_len + 1, fmt2, ap_copy);\n                        va_end(ap_copy);\n                    }\n                }\n                if(pbuf == NULL) {\n                    buf[0] = '\\0';\n                    pbuf = buf;\n                }\n\n                /*\n         * however we need to parse the type ourselves in order to advance\n         * the va_list by the correct amount; there is no portable way to\n         * inherit the advancement made by vprintf.\n         * 32-bit (linux or windows) passes va_list by value.\n         */\n                if((n + 1 == strlen(\"%\" PRId64) && strcmp(fmt2, \"%\" PRId64) == 0) ||\n                   (n + 1 == strlen(\"%\" PRIu64) && strcmp(fmt2, \"%\" PRIu64) == 0)) {\n                    (void)va_arg(ap, int64_t);\n                } else if(strcmp(fmt2, \"%.*s\") == 0) {\n                    (void)va_arg(ap, int);\n                    (void)va_arg(ap, char*);\n                } else {\n                    switch(fmt2[n]) {\n                    case 'u':\n                    case 'd':\n                        (void)va_arg(ap, int);\n                        break;\n                    case 'g':\n                    case 'f':\n                        (void)va_arg(ap, double);\n                        break;\n                    case 'p':\n                        (void)va_arg(ap, void*);\n                        break;\n                    default:\n                        /* many types are promoted to int */\n                        (void)va_arg(ap, int);\n                    }\n                }\n\n                len += out->printer(out, pbuf, strlen(pbuf));\n                skip = n + 1;\n\n                /* If buffer was allocated from heap, free it */\n                if(pbuf != buf) {\n                    free(pbuf);\n                    pbuf = NULL;\n                }\n            }\n            fmt += skip;\n        } else if(*fmt == '_' || json_isalpha(*fmt)) {\n            len += out->printer(out, quote, 1);\n            while(*fmt == '_' || json_isalpha(*fmt) || json_isdigit(*fmt)) {\n                len += out->printer(out, fmt, 1);\n                fmt++;\n            }\n            len += out->printer(out, quote, 1);\n        } else {\n            len += out->printer(out, fmt, 1);\n            fmt++;\n        }\n    }\n    va_end(ap);\n\n    return len;\n}\n\nint json_printf(struct json_out* out, const char* fmt, ...) WEAK;\nint json_printf(struct json_out* out, const char* fmt, ...) {\n    int n;\n    va_list ap;\n    va_start(ap, fmt);\n    n = json_vprintf(out, fmt, ap);\n    va_end(ap);\n    return n;\n}\n\nint json_printf_array(struct json_out* out, va_list* ap) WEAK;\nint json_printf_array(struct json_out* out, va_list* ap) {\n    int len = 0;\n    char* arr = va_arg(*ap, char*);\n    size_t i, arr_size = va_arg(*ap, size_t);\n    size_t elem_size = va_arg(*ap, size_t);\n    const char* fmt = va_arg(*ap, char*);\n    len += json_printf(out, \"[\", 1);\n    for(i = 0; arr != NULL && i < arr_size / elem_size; i++) {\n        union {\n            int64_t i;\n            double d;\n        } val;\n        memcpy(&val, arr + i * elem_size, elem_size > sizeof(val) ? sizeof(val) : elem_size);\n        if(i > 0) len += json_printf(out, \", \");\n        if(strpbrk(fmt, \"efg\") != NULL) {\n            len += json_printf(out, fmt, val.d);\n        } else {\n            len += json_printf(out, fmt, val.i);\n        }\n    }\n    len += json_printf(out, \"]\", 1);\n    return len;\n}\n\n#ifdef _WIN32\nint cs_win_vsnprintf(char* str, size_t size, const char* format, va_list ap) WEAK;\nint cs_win_vsnprintf(char* str, size_t size, const char* format, va_list ap) {\n    int res = _vsnprintf(str, size, format, ap);\n    va_end(ap);\n    if(res >= size) {\n        str[size - 1] = '\\0';\n    }\n    return res;\n}\n\nint cs_win_snprintf(char* str, size_t size, const char* format, ...) WEAK;\nint cs_win_snprintf(char* str, size_t size, const char* format, ...) {\n    int res;\n    va_list ap;\n    va_start(ap, format);\n    res = vsnprintf(str, size, format, ap);\n    va_end(ap);\n    return res;\n}\n#endif /* _WIN32 */\n\nint json_walk(\n    const char* json_string,\n    int json_string_length,\n    json_walk_callback_t callback,\n    void* callback_data) WEAK;\nint json_walk(\n    const char* json_string,\n    int json_string_length,\n    json_walk_callback_t callback,\n    void* callback_data) {\n    struct frozen frozen;\n\n    memset(&frozen, 0, sizeof(frozen));\n    frozen.end = json_string + json_string_length;\n    frozen.cur = json_string;\n    frozen.callback_data = callback_data;\n    frozen.callback = callback;\n\n    TRY(json_doit(&frozen));\n\n    return frozen.cur - json_string;\n}\n\nstruct scan_array_info {\n    int found;\n    char path[JSON_MAX_PATH_LEN];\n    struct json_token* token;\n};\n\nstatic void json_scanf_array_elem_cb(\n    void* callback_data,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* token) {\n    struct scan_array_info* info = (struct scan_array_info*)callback_data;\n\n    (void)name;\n    (void)name_len;\n\n    if(strcmp(path, info->path) == 0) {\n        *info->token = *token;\n        info->found = 1;\n    }\n}\n\nint json_scanf_array_elem(\n    const char* s,\n    int len,\n    const char* path,\n    int idx,\n    struct json_token* token) WEAK;\nint json_scanf_array_elem(\n    const char* s,\n    int len,\n    const char* path,\n    int idx,\n    struct json_token* token) {\n    struct scan_array_info info;\n    info.token = token;\n    info.found = 0;\n    memset(token, 0, sizeof(*token));\n    snprintf(info.path, sizeof(info.path), \"%s[%d]\", path, idx);\n    json_walk(s, len, json_scanf_array_elem_cb, &info);\n    return info.found ? token->len : -1;\n}\n\nstruct json_scanf_info {\n    int num_conversions;\n    char* path;\n    const char* fmt;\n    void* target;\n    void* user_data;\n    int type;\n};\n\nint json_unescape(const char* src, int slen, char* dst, int dlen) WEAK;\nint json_unescape(const char* src, int slen, char* dst, int dlen) {\n    char *send = (char*)src + slen, *dend = dst + dlen, *orig_dst = dst, *p;\n    const char *esc1 = \"\\\"\\\\/bfnrt\", *esc2 = \"\\\"\\\\/\\b\\f\\n\\r\\t\";\n\n    while(src < send) {\n        if(*src == '\\\\') {\n            if(++src >= send) return JSON_STRING_INCOMPLETE;\n            if(*src == 'u') {\n                if(send - src < 5) return JSON_STRING_INCOMPLETE;\n                /* Here we go: this is a \\u.... escape. Process simple one-byte chars */\n                if(src[1] == '0' && src[2] == '0') {\n                    /* This is \\u00xx character from the ASCII range */\n                    if(dst < dend) *dst = hexdec(src + 3);\n                    src += 4;\n                } else {\n                    /* Complex \\uXX XX escapes drag utf8 lib... Do it at some stage */\n                    return JSON_STRING_INVALID;\n                }\n            } else if((p = (char*)strchr(esc1, *src)) != NULL) {\n                if(dst < dend) *dst = esc2[p - esc1];\n            } else {\n                return JSON_STRING_INVALID;\n            }\n        } else {\n            if(dst < dend) *dst = *src;\n        }\n        dst++;\n        src++;\n    }\n\n    return dst - orig_dst;\n}\n\nstatic void json_scanf_cb(\n    void* callback_data,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* token) {\n    struct json_scanf_info* info = (struct json_scanf_info*)callback_data;\n    char buf[32]; /* Must be enough to hold numbers */\n\n    (void)name;\n    (void)name_len;\n\n    if(token->ptr == NULL) {\n        /*\n     * We're not interested here in the events for which we have no value;\n     * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START\n     */\n        return;\n    }\n\n    if(strcmp(path, info->path) != 0) {\n        /* It's not the path we're looking for, so, just ignore this callback */\n        return;\n    }\n\n    switch(info->type) {\n    case 'B':\n        info->num_conversions++;\n        switch(sizeof(bool)) {\n        case sizeof(char):\n            *(char*)info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);\n            break;\n        case sizeof(int):\n            *(int*)info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);\n            break;\n        default:\n            /* should never be here */\n            abort();\n        }\n        break;\n    case 'M': {\n        union {\n            void* p;\n            json_scanner_t f;\n        } u = {info->target};\n        info->num_conversions++;\n        u.f(token->ptr, token->len, info->user_data);\n        break;\n    }\n    case 'Q': {\n        char** dst = (char**)info->target;\n        if(token->type == JSON_TYPE_NULL) {\n            *dst = NULL;\n        } else {\n            int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);\n            if(unescaped_len >= 0 && (*dst = (char*)malloc(unescaped_len + 1)) != NULL) {\n                info->num_conversions++;\n                if(json_unescape(token->ptr, token->len, *dst, unescaped_len) == unescaped_len) {\n                    (*dst)[unescaped_len] = '\\0';\n                } else {\n                    free(*dst);\n                    *dst = NULL;\n                }\n            }\n        }\n        break;\n    }\n    case 'H': {\n#if JSON_ENABLE_HEX\n        char** dst = (char**)info->user_data;\n        int i, len = token->len / 2;\n        *(int*)info->target = len;\n        if((*dst = (char*)malloc(len + 1)) != NULL) {\n            for(i = 0; i < len; i++) {\n                (*dst)[i] = hexdec(token->ptr + 2 * i);\n            }\n            (*dst)[len] = '\\0';\n            info->num_conversions++;\n        }\n#endif /* JSON_ENABLE_HEX */\n        break;\n    }\n    case 'V': {\n#if JSON_ENABLE_BASE64\n        char** dst = (char**)info->target;\n        int len = token->len * 4 / 3 + 2;\n        if((*dst = (char*)malloc(len + 1)) != NULL) {\n            int n = b64dec(token->ptr, token->len, *dst);\n            (*dst)[n] = '\\0';\n            *(int*)info->user_data = n;\n            info->num_conversions++;\n        }\n#endif /* JSON_ENABLE_BASE64 */\n        break;\n    }\n    case 'T':\n        info->num_conversions++;\n        *(struct json_token*)info->target = *token;\n        break;\n    default:\n        if(token->len >= (int)sizeof(buf)) break;\n        /* Before converting, copy into tmp buffer in order to 0-terminate it */\n        memcpy(buf, token->ptr, token->len);\n        buf[token->len] = '\\0';\n        /* NB: Use of base 0 for %d, %ld, %u and %lu is intentional. */\n        if(info->fmt[1] == 'd' || (info->fmt[1] == 'l' && info->fmt[2] == 'd') ||\n           info->fmt[1] == 'i') {\n            char* endptr = NULL;\n            long r = strtol(buf, &endptr, 0 /* base */);\n            if(*endptr == '\\0') {\n                if(info->fmt[1] == 'l') {\n                    *((long*)info->target) = r;\n                } else {\n                    *((int*)info->target) = (int)r;\n                }\n                info->num_conversions++;\n            }\n        } else if(info->fmt[1] == 'u' || (info->fmt[1] == 'l' && info->fmt[2] == 'u')) {\n            char* endptr = NULL;\n            unsigned long r = strtoul(buf, &endptr, 0 /* base */);\n            if(*endptr == '\\0') {\n                if(info->fmt[1] == 'l') {\n                    *((unsigned long*)info->target) = r;\n                } else {\n                    *((unsigned int*)info->target) = (unsigned int)r;\n                }\n                info->num_conversions++;\n            }\n        } else {\n#if !JSON_MINIMAL\n            info->num_conversions += sscanf(buf, info->fmt, info->target);\n#endif\n        }\n        break;\n    }\n}\n\nint json_vscanf(const char* s, int len, const char* fmt, va_list ap) WEAK;\nint json_vscanf(const char* s, int len, const char* fmt, va_list ap) {\n    char path[JSON_MAX_PATH_LEN] = \"\", fmtbuf[20];\n    int i = 0;\n    char* p = NULL;\n    struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0};\n\n    while(fmt[i] != '\\0') {\n        if(fmt[i] == '{') {\n            strlcat(path, \".\", sizeof(path));\n            i++;\n        } else if(fmt[i] == '}') {\n            if((p = strrchr(path, '.')) != NULL) *p = '\\0';\n            i++;\n        } else if(fmt[i] == '%') {\n            info.target = va_arg(ap, void*);\n            info.type = fmt[i + 1];\n            switch(fmt[i + 1]) {\n            case 'M':\n            case 'V':\n            case 'H':\n                info.user_data = va_arg(ap, void*);\n            /* FALLTHROUGH */\n            case 'B':\n            case 'Q':\n            case 'T':\n                i += 2;\n                break;\n            default: {\n                const char* delims = \", \\t\\r\\n]}\";\n                int conv_len = strcspn(fmt + i + 1, delims) + 1;\n                memcpy(fmtbuf, fmt + i, conv_len);\n                fmtbuf[conv_len] = '\\0';\n                i += conv_len;\n                i += strspn(fmt + i, delims);\n                break;\n            }\n            }\n            json_walk(s, len, json_scanf_cb, &info);\n        } else if(json_isalpha(fmt[i]) || json_get_utf8_char_len(fmt[i]) > 1) {\n            char* pe;\n            const char* delims = \": \\r\\n\\t\";\n            int key_len = strcspn(&fmt[i], delims);\n            if((p = strrchr(path, '.')) != NULL) p[1] = '\\0';\n            pe = path + strlen(path);\n            memcpy(pe, fmt + i, key_len);\n            pe[key_len] = '\\0';\n            i += key_len + strspn(fmt + i + key_len, delims);\n        } else {\n            i++;\n        }\n    }\n    return info.num_conversions;\n}\n\nint json_scanf(const char* str, int len, const char* fmt, ...) WEAK;\nint json_scanf(const char* str, int len, const char* fmt, ...) {\n    int result;\n    va_list ap;\n    va_start(ap, fmt);\n    result = json_vscanf(str, len, fmt, ap);\n    va_end(ap);\n    return result;\n}\n\nint json_vfprintf(const char* file_name, const char* fmt, va_list ap) WEAK;\nint json_vfprintf(const char* file_name, const char* fmt, va_list ap) {\n    int res = -1;\n    FILE* fp = fopen(file_name, \"wb\");\n    if(fp != NULL) {\n        struct json_out out = JSON_OUT_FILE(fp);\n        res = json_vprintf(&out, fmt, ap);\n        fputc('\\n', fp);\n        fclose(fp);\n    }\n    return res;\n}\n\nint json_fprintf(const char* file_name, const char* fmt, ...) WEAK;\nint json_fprintf(const char* file_name, const char* fmt, ...) {\n    int result;\n    va_list ap;\n    va_start(ap, fmt);\n    result = json_vfprintf(file_name, fmt, ap);\n    va_end(ap);\n    return result;\n}\n\nchar* json_fread(const char* path) WEAK;\nchar* json_fread(const char* path) {\n    FILE* fp;\n    char* data = NULL;\n    if((fp = fopen(path, \"rb\")) == NULL) {\n    } else if(fseek(fp, 0, SEEK_END) != 0) {\n        fclose(fp);\n    } else {\n        long size = ftell(fp);\n        if(size > 0 && (data = (char*)malloc(size + 1)) != NULL) {\n            fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */\n            if(fread(data, 1, size, fp) != (size_t)size) {\n                free(data);\n                data = NULL;\n            } else {\n                data[size] = '\\0';\n            }\n        }\n        fclose(fp);\n    }\n    return data;\n}\n\nstruct json_setf_data {\n    const char* json_path;\n    const char* base; /* Pointer to the source JSON string */\n    int matched; /* Matched part of json_path */\n    int pos; /* Offset of the mutated value begin */\n    int end; /* Offset of the mutated value end */\n    int prev; /* Offset of the previous token end */\n};\n\nstatic int get_matched_prefix_len(const char* s1, const char* s2) {\n    int i = 0;\n    while(s1[i] && s2[i] && s1[i] == s2[i])\n        i++;\n    return i;\n}\n\nstatic void json_vsetf_cb(\n    void* userdata,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* t) {\n    struct json_setf_data* data = (struct json_setf_data*)userdata;\n    int off, len = get_matched_prefix_len(path, data->json_path);\n    if(t->ptr == NULL) return;\n    off = t->ptr - data->base;\n    if(len > data->matched) data->matched = len;\n\n    /*\n   * If there is no exact path match, set the mutation position to tbe end\n   * of the object or array\n   */\n    if(len < data->matched && data->pos == 0 &&\n       (t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) {\n        data->pos = data->end = data->prev;\n    }\n\n    /* Exact path match. Set mutation position to the value of this token */\n    if(strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START &&\n       t->type != JSON_TYPE_ARRAY_START) {\n        data->pos = off;\n        data->end = off + t->len;\n    }\n\n    /*\n   * For deletion, we need to know where the previous value ends, because\n   * we don't know where matched value key starts.\n   * When the mutation position is not yet set, remember each value end.\n   * When the mutation position is already set, but it is at the beginning\n   * of the object/array, we catch the end of the object/array and see\n   * whether the object/array start is closer then previously stored prev.\n   */\n    if(data->pos == 0) {\n        data->prev = off + t->len; /* pos is not yet set */\n    } else if((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos && off + 1 > data->prev) {\n        data->prev = off + 1;\n    }\n    (void)name;\n    (void)name_len;\n}\n\nint json_vsetf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    va_list ap) WEAK;\nint json_vsetf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    va_list ap) {\n    struct json_setf_data data;\n    memset(&data, 0, sizeof(data));\n    data.json_path = json_path;\n    data.base = s;\n    data.end = len;\n    json_walk(s, len, json_vsetf_cb, &data);\n    if(json_fmt == NULL) {\n        /* Deletion codepath */\n        json_printf(out, \"%.*s\", data.prev, s);\n        /* Trim comma after the value that begins at object/array start */\n        if(s[data.prev - 1] == '{' || s[data.prev - 1] == '[') {\n            int i = data.end;\n            while(i < len && json_isspace(s[i]))\n                i++;\n            if(s[i] == ',') data.end = i + 1; /* Point after comma */\n        }\n        json_printf(out, \"%.*s\", len - data.end, s + data.end);\n    } else {\n        /* Modification codepath */\n        int n, off = data.matched, depth = 0;\n\n        /* Print the unchanged beginning */\n        json_printf(out, \"%.*s\", data.pos, s);\n\n        /* Add missing keys */\n        while((n = strcspn(&json_path[off], \".[\")) > 0) {\n            if(s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) {\n                json_printf(out, \",\");\n            }\n            if(off > 0 && json_path[off - 1] != '.') break;\n            json_printf(out, \"%.*Q:\", n, json_path + off);\n            off += n;\n            if(json_path[off] != '\\0') {\n                json_printf(out, \"%c\", json_path[off] == '.' ? '{' : '[');\n                depth++;\n                off++;\n            }\n        }\n        /* Print the new value */\n        json_vprintf(out, json_fmt, ap);\n\n        /* Close brackets/braces of the added missing keys */\n        for(; off > data.matched; off--) {\n            int ch = json_path[off];\n            const char* p = ch == '.' ? \"}\" : ch == '[' ? \"]\" : \"\";\n            json_printf(out, \"%s\", p);\n        }\n\n        /* Print the rest of the unchanged string */\n        json_printf(out, \"%.*s\", len - data.end, s + data.end);\n    }\n    return data.end > data.pos ? 1 : 0;\n}\n\nint json_setf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    ...) WEAK;\nint json_setf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    ...) {\n    int result;\n    va_list ap;\n    va_start(ap, json_fmt);\n    result = json_vsetf(s, len, out, json_path, json_fmt, ap);\n    va_end(ap);\n    return result;\n}\n\nstruct prettify_data {\n    struct json_out* out;\n    int level;\n    int last_token;\n};\n\nstatic void indent(struct json_out* out, int level) {\n    while(level-- > 0)\n        out->printer(out, \"  \", 2);\n}\n\nstatic void print_key(struct prettify_data* pd, const char* path, const char* name, int name_len) {\n    if(pd->last_token != JSON_TYPE_INVALID && pd->last_token != JSON_TYPE_ARRAY_START &&\n       pd->last_token != JSON_TYPE_OBJECT_START) {\n        pd->out->printer(pd->out, \",\", 1);\n    }\n    if(path[0] != '\\0') pd->out->printer(pd->out, \"\\n\", 1);\n    indent(pd->out, pd->level);\n    if(path[0] != '\\0' && path[strlen(path) - 1] != ']') {\n        pd->out->printer(pd->out, \"\\\"\", 1);\n        pd->out->printer(pd->out, name, (int)name_len);\n        pd->out->printer(pd->out, \"\\\"\", 1);\n        pd->out->printer(pd->out, \": \", 2);\n    }\n}\n\nstatic void prettify_cb(\n    void* userdata,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* t) {\n    struct prettify_data* pd = (struct prettify_data*)userdata;\n    switch(t->type) {\n    case JSON_TYPE_OBJECT_START:\n    case JSON_TYPE_ARRAY_START:\n        print_key(pd, path, name, name_len);\n        pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? \"[\" : \"{\", 1);\n        pd->level++;\n        break;\n    case JSON_TYPE_OBJECT_END:\n    case JSON_TYPE_ARRAY_END:\n        pd->level--;\n        if(pd->last_token != JSON_TYPE_INVALID && pd->last_token != JSON_TYPE_ARRAY_START &&\n           pd->last_token != JSON_TYPE_OBJECT_START) {\n            pd->out->printer(pd->out, \"\\n\", 1);\n            indent(pd->out, pd->level);\n        }\n        pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? \"]\" : \"}\", 1);\n        break;\n    case JSON_TYPE_NUMBER:\n    case JSON_TYPE_NULL:\n    case JSON_TYPE_TRUE:\n    case JSON_TYPE_FALSE:\n    case JSON_TYPE_STRING:\n        print_key(pd, path, name, name_len);\n        if(t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, \"\\\"\", 1);\n        pd->out->printer(pd->out, t->ptr, t->len);\n        if(t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, \"\\\"\", 1);\n        break;\n    default:\n        break;\n    }\n    pd->last_token = t->type;\n}\n\nint json_prettify(const char* s, int len, struct json_out* out) WEAK;\nint json_prettify(const char* s, int len, struct json_out* out) {\n    struct prettify_data pd = {out, 0, JSON_TYPE_INVALID};\n    return json_walk(s, len, prettify_cb, &pd);\n}\n\nint json_prettify_file(const char* file_name) WEAK;\nint json_prettify_file(const char* file_name) {\n    int res = -1;\n    char* s = json_fread(file_name);\n    FILE* fp;\n    if(s != NULL && (fp = fopen(file_name, \"wb\")) != NULL) {\n        struct json_out out = JSON_OUT_FILE(fp);\n        res = json_prettify(s, strlen(s), &out);\n        if(res < 0) {\n            /* On error, restore the old content */\n            fclose(fp);\n            fp = fopen(file_name, \"wb\");\n            fseek(fp, 0, SEEK_SET);\n            fwrite(s, 1, strlen(s), fp);\n        } else {\n            fputc('\\n', fp);\n        }\n        fclose(fp);\n    }\n    free(s);\n    return res;\n}\n\nstruct next_data {\n    void* handle; // Passed handle. Changed if a next entry is found\n    const char* path; // Path to the iterated object/array\n    int path_len; // Path length - optimisation\n    int found; // Non-0 if found the next entry\n    struct json_token* key; // Object's key\n    struct json_token* val; // Object's value\n    int* idx; // Array index\n};\n\nstatic void next_set_key(struct next_data* d, const char* name, int name_len, int is_array) {\n    if(is_array) {\n        /* Array. Set index and reset key  */\n        if(d->key != NULL) {\n            d->key->len = 0;\n            d->key->ptr = NULL;\n        }\n        if(d->idx != NULL) *d->idx = atoi(name);\n    } else {\n        /* Object. Set key and make index -1 */\n        if(d->key != NULL) {\n            d->key->ptr = name;\n            d->key->len = name_len;\n        }\n        if(d->idx != NULL) *d->idx = -1;\n    }\n}\n\nstatic void json_next_cb(\n    void* userdata,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* t) {\n    struct next_data* d = (struct next_data*)userdata;\n    const char* p = path + d->path_len;\n    if(d->found) return;\n    if(d->path_len >= (int)strlen(path)) return;\n    if(strncmp(d->path, path, d->path_len) != 0) return;\n    if(strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */\n    if(strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */\n    // {OBJECT,ARRAY}_END types do not pass name, _START does. Save key.\n    if(t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) {\n        next_set_key(d, name, name_len, p[0] == '[');\n    } else if(d->handle == NULL || d->handle < (void*)t->ptr) {\n        if(t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) {\n            next_set_key(d, name, name_len, p[0] == '[');\n        }\n        if(d->val != NULL) *d->val = *t;\n        d->handle = (void*)t->ptr;\n        d->found = 1;\n    }\n}\n\nstatic void* json_next(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    struct json_token* key,\n    struct json_token* val,\n    int* i) {\n    struct json_token tmpval, *v = val == NULL ? &tmpval : val;\n    struct json_token tmpkey, *k = key == NULL ? &tmpkey : key;\n    int tmpidx, *pidx = i == NULL ? &tmpidx : i;\n    struct next_data data = {handle, path, (int)strlen(path), 0, k, v, pidx};\n    json_walk(s, len, json_next_cb, &data);\n    return data.found ? data.handle : NULL;\n}\n\nvoid* json_next_key(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    struct json_token* key,\n    struct json_token* val) WEAK;\nvoid* json_next_key(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    struct json_token* key,\n    struct json_token* val) {\n    return json_next(s, len, handle, path, key, val, NULL);\n}\n\nvoid* json_next_elem(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    int* idx,\n    struct json_token* val) WEAK;\nvoid* json_next_elem(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    int* idx,\n    struct json_token* val) {\n    return json_next(s, len, handle, path, NULL, val, idx);\n}\n\nstatic int json_sprinter(struct json_out* out, const char* str, size_t len) {\n    size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf);\n    size_t new_len = len + old_len;\n    char* p = (char*)realloc(out->u.buf.buf, new_len + 1);\n    if(p != NULL) {\n        memcpy(p + old_len, str, len);\n        p[new_len] = '\\0';\n        out->u.buf.buf = p;\n    }\n    return len;\n}\n\nchar* json_vasprintf(const char* fmt, va_list ap) WEAK;\nchar* json_vasprintf(const char* fmt, va_list ap) {\n    struct json_out out;\n    memset(&out, 0, sizeof(out));\n    out.printer = json_sprinter;\n    json_vprintf(&out, fmt, ap);\n    return out.u.buf.buf;\n}\n\nchar* json_asprintf(const char* fmt, ...) WEAK;\nchar* json_asprintf(const char* fmt, ...) {\n    char* result = NULL;\n    va_list ap;\n    va_start(ap, fmt);\n    result = json_vasprintf(fmt, ap);\n    va_end(ap);\n    return result;\n}\n"
  },
  {
    "path": "lib/mjs/common/frozen/frozen.h",
    "content": "/*\n * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>\n * Copyright (c) 2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_FROZEN_FROZEN_H_\n#define CS_FROZEN_FROZEN_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n\n#if defined(_WIN32) && _MSC_VER < 1700\ntypedef int bool;\nenum { false = 0, true = 1 };\n#else\n#include <stdbool.h>\n#endif\n\n/* JSON token type */\nenum json_token_type {\n    JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */\n    JSON_TYPE_STRING,\n    JSON_TYPE_NUMBER,\n    JSON_TYPE_TRUE,\n    JSON_TYPE_FALSE,\n    JSON_TYPE_NULL,\n    JSON_TYPE_OBJECT_START,\n    JSON_TYPE_OBJECT_END,\n    JSON_TYPE_ARRAY_START,\n    JSON_TYPE_ARRAY_END,\n\n    JSON_TYPES_CNT\n};\n\n/*\n * Structure containing token type and value. Used in `json_walk()` and\n * `json_scanf()` with the format specifier `%T`.\n */\nstruct json_token {\n    const char* ptr; /* Points to the beginning of the value */\n    int len; /* Value length */\n    enum json_token_type type; /* Type of the token, possible values are above */\n};\n\n#define JSON_INVALID_TOKEN \\\n    { 0, 0, JSON_TYPE_INVALID }\n\n/* Error codes */\n#define JSON_STRING_INVALID -1\n#define JSON_STRING_INCOMPLETE -2\n\n/*\n * Callback-based SAX-like API.\n *\n * Property name and length is given only if it's available: i.e. if current\n * event is an object's property. In other cases, `name` is `NULL`. For\n * example, name is never given:\n *   - For the first value in the JSON string;\n *   - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END\n *\n * E.g. for the input `{ \"foo\": 123, \"bar\": [ 1, 2, { \"baz\": true } ] }`,\n * the sequence of callback invocations will be as follows:\n *\n * - type: JSON_TYPE_OBJECT_START, name: NULL, path: \"\", value: NULL\n * - type: JSON_TYPE_NUMBER, name: \"foo\", path: \".foo\", value: \"123\"\n * - type: JSON_TYPE_ARRAY_START,  name: \"bar\", path: \".bar\", value: NULL\n * - type: JSON_TYPE_NUMBER, name: \"0\", path: \".bar[0]\", value: \"1\"\n * - type: JSON_TYPE_NUMBER, name: \"1\", path: \".bar[1]\", value: \"2\"\n * - type: JSON_TYPE_OBJECT_START, name: \"2\", path: \".bar[2]\", value: NULL\n * - type: JSON_TYPE_TRUE, name: \"baz\", path: \".bar[2].baz\", value: \"true\"\n * - type: JSON_TYPE_OBJECT_END, name: NULL, path: \".bar[2]\", value: \"{ \\\"baz\\\":\n *true }\"\n * - type: JSON_TYPE_ARRAY_END, name: NULL, path: \".bar\", value: \"[ 1, 2, {\n *\\\"baz\\\": true } ]\"\n * - type: JSON_TYPE_OBJECT_END, name: NULL, path: \"\", value: \"{ \\\"foo\\\": 123,\n *\\\"bar\\\": [ 1, 2, { \\\"baz\\\": true } ] }\"\n */\ntypedef void (*json_walk_callback_t)(\n    void* callback_data,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* token);\n\n/*\n * Parse `json_string`, invoking `callback` in a way similar to SAX parsers;\n * see `json_walk_callback_t`.\n * Return number of processed bytes, or a negative error code.\n */\nint json_walk(\n    const char* json_string,\n    int json_string_length,\n    json_walk_callback_t callback,\n    void* callback_data);\n\n/*\n * JSON generation API.\n * struct json_out abstracts output, allowing alternative printing plugins.\n */\nstruct json_out {\n    int (*printer)(struct json_out*, const char* str, size_t len);\n    union {\n        struct {\n            char* buf;\n            size_t size;\n            size_t len;\n        } buf;\n        void* data;\n        FILE* fp;\n    } u;\n};\n\nextern int json_printer_buf(struct json_out*, const char*, size_t);\nextern int json_printer_file(struct json_out*, const char*, size_t);\n\n#define JSON_OUT_BUF(buf, len) \\\n    {                          \\\n        json_printer_buf, {    \\\n            { buf, len, 0 }    \\\n        }                      \\\n    }\n#define JSON_OUT_FILE(fp)       \\\n    {                           \\\n        json_printer_file, {    \\\n            { (char*)fp, 0, 0 } \\\n        }                       \\\n    }\n\ntypedef int (*json_printf_callback_t)(struct json_out*, va_list* ap);\n\n/*\n * Generate formatted output into a given sting buffer.\n * This is a superset of printf() function, with extra format specifiers:\n *  - `%B` print json boolean, `true` or `false`. Accepts an `int`.\n *  - `%Q` print quoted escaped string or `null`. Accepts a `const char *`.\n *  - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *`\n *  - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`.\n *  - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`.\n *  - `%M` invokes a json_printf_callback_t function. That callback function\n *  can consume more parameters.\n *\n * Return number of bytes printed. If the return value is bigger than the\n * supplied buffer, that is an indicator of overflow. In the overflow case,\n * overflown bytes are not printed.\n */\nint json_printf(struct json_out*, const char* fmt, ...);\nint json_vprintf(struct json_out*, const char* fmt, va_list ap);\n\n/*\n * Same as json_printf, but prints to a file.\n * File is created if does not exist. File is truncated if already exists.\n */\nint json_fprintf(const char* file_name, const char* fmt, ...);\nint json_vfprintf(const char* file_name, const char* fmt, va_list ap);\n\n/*\n * Print JSON into an allocated 0-terminated string.\n * Return allocated string, or NULL on error.\n * Example:\n *\n * ```c\n *   char *str = json_asprintf(\"{a:%H}\", 3, \"abc\");\n *   printf(\"%s\\n\", str);  // Prints \"616263\"\n *   free(str);\n * ```\n */\nchar* json_asprintf(const char* fmt, ...);\nchar* json_vasprintf(const char* fmt, va_list ap);\n\n/*\n * Helper %M callback that prints contiguous C arrays.\n * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt\n * Return number of bytes printed.\n */\nint json_printf_array(struct json_out*, va_list* ap);\n\n/*\n * Scan JSON string `str`, performing scanf-like conversions according to `fmt`.\n * This is a `scanf()` - like function, with following differences:\n *\n * 1. Object keys in the format string may be not quoted, e.g. \"{key: %d}\"\n * 2. Order of keys in an object is irrelevant.\n * 3. Several extra format specifiers are supported:\n *    - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`),\n *       expects boolean `true` or `false`.\n *    - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned\n *       string is malloc-ed, caller must free() the string.\n *    - %V: consumes `char **`, `int *`. Expects base64-encoded string.\n *       Result string is base64-decoded, malloced and NUL-terminated.\n *       The length of result string is stored in `int *` placeholder.\n *       Caller must free() the result.\n *    - %H: consumes `int *`, `char **`.\n *       Expects a hex-encoded string, e.g. \"fa014f\".\n *       Result string is hex-decoded, malloced and NUL-terminated.\n *       The length of the result string is stored in `int *` placeholder.\n *       Caller must free() the result.\n *    - %M: consumes custom scanning function pointer and\n *       `void *user_data` parameter - see json_scanner_t definition.\n *    - %T: consumes `struct json_token *`, fills it out with matched token.\n *\n * Return number of elements successfully scanned & converted.\n * Negative number means scan error.\n */\nint json_scanf(const char* str, int str_len, const char* fmt, ...);\nint json_vscanf(const char* str, int str_len, const char* fmt, va_list ap);\n\n/* json_scanf's %M handler  */\ntypedef void (*json_scanner_t)(const char* str, int len, void* user_data);\n\n/*\n * Helper function to scan array item with given path and index.\n * Fills `token` with the matched JSON token.\n * Return -1 if no array element found, otherwise non-negative token length.\n */\nint json_scanf_array_elem(\n    const char* s,\n    int len,\n    const char* path,\n    int index,\n    struct json_token* token);\n\n/*\n * Unescape JSON-encoded string src,slen into dst, dlen.\n * src and dst may overlap.\n * If destination buffer is too small (or zero-length), result string is not\n * written but the length is counted nevertheless (similar to snprintf).\n * Return the length of unescaped string in bytes.\n */\nint json_unescape(const char* src, int slen, char* dst, int dlen);\n\n/*\n * Escape a string `str`, `str_len` into the printer `out`.\n * Return the number of bytes printed.\n */\nint json_escape(struct json_out* out, const char* str, size_t str_len);\n\n/*\n * Read the whole file in memory.\n * Return malloc-ed file content, or NULL on error. The caller must free().\n */\nchar* json_fread(const char* file_name);\n\n/*\n * Update given JSON string `s,len` by changing the value at given `json_path`.\n * The result is saved to `out`. If `json_fmt` == NULL, that deletes the key.\n * If path is not present, missing keys are added. Array path without an\n * index pushes a value to the end of an array.\n * Return 1 if the string was changed, 0 otherwise.\n *\n * Example:  s is a JSON string { \"a\": 1, \"b\": [ 2 ] }\n *   json_setf(s, len, out, \".a\", \"7\");     // { \"a\": 7, \"b\": [ 2 ] }\n *   json_setf(s, len, out, \".b\", \"7\");     // { \"a\": 1, \"b\": 7 }\n *   json_setf(s, len, out, \".b[]\", \"7\");   // { \"a\": 1, \"b\": [ 2,7 ] }\n *   json_setf(s, len, out, \".b\", NULL);    // { \"a\": 1 }\n */\nint json_setf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    ...);\n\nint json_vsetf(\n    const char* s,\n    int len,\n    struct json_out* out,\n    const char* json_path,\n    const char* json_fmt,\n    va_list ap);\n\n/*\n * Pretty-print JSON string `s,len` into `out`.\n * Return number of processed bytes in `s`.\n */\nint json_prettify(const char* s, int len, struct json_out* out);\n\n/*\n * Prettify JSON file `file_name`.\n * Return number of processed bytes, or negative number of error.\n * On error, file content is not modified.\n */\nint json_prettify_file(const char* file_name);\n\n/*\n * Iterate over an object at given JSON `path`.\n * On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL\n * for `key`, or `val`, in which case they won't be populated.\n * Return an opaque value suitable for the next iteration, or NULL when done.\n *\n * Example:\n *\n * ```c\n * void *h = NULL;\n * struct json_token key, val;\n * while ((h = json_next_key(s, len, h, \".foo\", &key, &val)) != NULL) {\n *   printf(\"[%.*s] -> [%.*s]\\n\", key.len, key.ptr, val.len, val.ptr);\n * }\n * ```\n */\nvoid* json_next_key(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    struct json_token* key,\n    struct json_token* val);\n\n/*\n * Iterate over an array at given JSON `path`.\n * Similar to `json_next_key`, but fills array index `idx` instead of `key`.\n */\nvoid* json_next_elem(\n    const char* s,\n    int len,\n    void* handle,\n    const char* path,\n    int* idx,\n    struct json_token* val);\n\n#ifndef JSON_MAX_PATH_LEN\n#define JSON_MAX_PATH_LEN 256\n#endif\n\n#ifndef JSON_MINIMAL\n#define JSON_MINIMAL 0\n#endif\n\n#ifndef JSON_ENABLE_BASE64\n#define JSON_ENABLE_BASE64 !JSON_MINIMAL\n#endif\n\n#ifndef JSON_ENABLE_HEX\n#define JSON_ENABLE_HEX !JSON_MINIMAL\n#endif\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* CS_FROZEN_FROZEN_H_ */\n"
  },
  {
    "path": "lib/mjs/common/mbuf.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef EXCLUDE_COMMON\n\n#include <assert.h>\n#include <string.h>\n#include \"mbuf.h\"\n\n#ifndef MBUF_REALLOC\n#define MBUF_REALLOC realloc\n#endif\n\n#ifndef MBUF_FREE\n#define MBUF_FREE free\n#endif\n\nvoid mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK;\nvoid mbuf_init(struct mbuf *mbuf, size_t initial_size) {\n  mbuf->len = mbuf->size = 0;\n  mbuf->buf = NULL;\n  mbuf_resize(mbuf, initial_size);\n}\n\nvoid mbuf_free(struct mbuf *mbuf) WEAK;\nvoid mbuf_free(struct mbuf *mbuf) {\n  if (mbuf->buf != NULL) {\n    MBUF_FREE(mbuf->buf);\n    mbuf_init(mbuf, 0);\n  }\n}\n\nvoid mbuf_resize(struct mbuf *a, size_t new_size) WEAK;\nvoid mbuf_resize(struct mbuf *a, size_t new_size) {\n  if (new_size > a->size || (new_size < a->size && new_size >= a->len)) {\n    char *buf = (char *) MBUF_REALLOC(a->buf, new_size);\n    /*\n     * In case realloc fails, there's not much we can do, except keep things as\n     * they are. Note that NULL is a valid return value from realloc when\n     * size == 0, but that is covered too.\n     */\n    if (buf == NULL && new_size != 0) return;\n    a->buf = buf;\n    a->size = new_size;\n  }\n}\n\nvoid mbuf_trim(struct mbuf *mbuf) WEAK;\nvoid mbuf_trim(struct mbuf *mbuf) {\n  mbuf_resize(mbuf, mbuf->len);\n}\n\nsize_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK;\nsize_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) {\n  char *p = NULL;\n\n  assert(a != NULL);\n  assert(a->len <= a->size);\n  assert(off <= a->len);\n\n  /* check overflow */\n  if (~(size_t) 0 - (size_t) a->buf < len) return 0;\n\n  if (a->len + len <= a->size) {\n    memmove(a->buf + off + len, a->buf + off, a->len - off);\n    if (buf != NULL) {\n      memcpy(a->buf + off, buf, len);\n    }\n    a->len += len;\n  } else {\n    size_t min_size = (a->len + len);\n    size_t new_size = (size_t)(min_size * MBUF_SIZE_MULTIPLIER);\n    if (new_size - min_size > MBUF_SIZE_MAX_HEADROOM) {\n      new_size = min_size + MBUF_SIZE_MAX_HEADROOM;\n    }\n    p = (char *) MBUF_REALLOC(a->buf, new_size);\n    if (p == NULL && new_size != min_size) {\n      new_size = min_size;\n      p = (char *) MBUF_REALLOC(a->buf, new_size);\n    }\n    if (p != NULL) {\n      a->buf = p;\n      if (off != a->len) {\n        memmove(a->buf + off + len, a->buf + off, a->len - off);\n      }\n      if (buf != NULL) memcpy(a->buf + off, buf, len);\n      a->len += len;\n      a->size = new_size;\n    } else {\n      len = 0;\n    }\n  }\n\n  return len;\n}\n\nsize_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK;\nsize_t mbuf_append(struct mbuf *a, const void *buf, size_t len) {\n  return mbuf_insert(a, a->len, buf, len);\n}\n\nsize_t mbuf_append_and_free(struct mbuf *a, void *buf, size_t len) WEAK;\nsize_t mbuf_append_and_free(struct mbuf *a, void *data, size_t len) {\n  size_t ret;\n  /* Optimization: if the buffer is currently empty,\n   * take over the user-provided buffer. */\n  if (a->len == 0) {\n    if (a->buf != NULL) free(a->buf);\n    a->buf = (char *) data;\n    a->len = a->size = len;\n    return len;\n  }\n  ret = mbuf_insert(a, a->len, data, len);\n  free(data);\n  return ret;\n}\n\nvoid mbuf_remove(struct mbuf *mb, size_t n) WEAK;\nvoid mbuf_remove(struct mbuf *mb, size_t n) {\n  if (n > 0 && n <= mb->len) {\n    memmove(mb->buf, mb->buf + n, mb->len - n);\n    mb->len -= n;\n  }\n}\n\nvoid mbuf_clear(struct mbuf *mb) WEAK;\nvoid mbuf_clear(struct mbuf *mb) {\n  mb->len = 0;\n}\n\nvoid mbuf_move(struct mbuf *from, struct mbuf *to) WEAK;\nvoid mbuf_move(struct mbuf *from, struct mbuf *to) {\n  memcpy(to, from, sizeof(*to));\n  memset(from, 0, sizeof(*from));\n}\n\n#endif /* EXCLUDE_COMMON */\n"
  },
  {
    "path": "lib/mjs/common/mbuf.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Mbufs are mutable/growing memory buffers, like C++ strings.\n * Mbuf can append data to the end of a buffer or insert data into arbitrary\n * position in the middle of a buffer. The buffer grows automatically when\n * needed.\n */\n\n#ifndef CS_COMMON_MBUF_H_\n#define CS_COMMON_MBUF_H_\n\n#include <stdlib.h>\n#include \"platform.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef MBUF_SIZE_MULTIPLIER\n#define MBUF_SIZE_MULTIPLIER 1.5\n#endif\n\n#ifndef MBUF_SIZE_MAX_HEADROOM\n#ifdef BUFSIZ\n#define MBUF_SIZE_MAX_HEADROOM BUFSIZ\n#else\n#define MBUF_SIZE_MAX_HEADROOM 1024\n#endif\n#endif\n\n/* Memory buffer descriptor */\nstruct mbuf {\n  char *buf;   /* Buffer pointer */\n  size_t len;  /* Data length. Data is located between offset 0 and len. */\n  size_t size; /* Buffer size allocated by realloc(1). Must be >= len */\n};\n\n/*\n * Initialises an Mbuf.\n * `initial_capacity` specifies the initial capacity of the mbuf.\n */\nvoid mbuf_init(struct mbuf *, size_t initial_capacity);\n\n/* Frees the space allocated for the mbuffer and resets the mbuf structure. */\nvoid mbuf_free(struct mbuf *);\n\n/*\n * Appends data to the Mbuf.\n *\n * Returns the number of bytes appended or 0 if out of memory.\n */\nsize_t mbuf_append(struct mbuf *, const void *data, size_t data_size);\n\n/*\n * Appends data to the Mbuf and frees it (data must be heap-allocated).\n *\n * Returns the number of bytes appended or 0 if out of memory.\n * data is freed irrespective of return value.\n */\nsize_t mbuf_append_and_free(struct mbuf *, void *data, size_t data_size);\n\n/*\n * Inserts data at a specified offset in the Mbuf.\n *\n * Existing data will be shifted forwards and the buffer will\n * be grown if necessary.\n * Returns the number of bytes inserted.\n */\nsize_t mbuf_insert(struct mbuf *, size_t, const void *, size_t);\n\n/* Removes `data_size` bytes from the beginning of the buffer. */\nvoid mbuf_remove(struct mbuf *, size_t data_size);\n\n/*\n * Resizes an Mbuf.\n *\n * If `new_size` is smaller than buffer's `len`, the\n * resize is not performed.\n */\nvoid mbuf_resize(struct mbuf *, size_t new_size);\n\n/* Moves the state from one mbuf to the other. */\nvoid mbuf_move(struct mbuf *from, struct mbuf *to);\n\n/* Removes all the data from mbuf (if any). */\nvoid mbuf_clear(struct mbuf *);\n\n/* Shrinks an Mbuf by resizing its `size` to `len`. */\nvoid mbuf_trim(struct mbuf *);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* CS_COMMON_MBUF_H_ */\n"
  },
  {
    "path": "lib/mjs/common/mg_mem.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_MG_MEM_H_\n#define CS_COMMON_MG_MEM_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef MG_MALLOC\n#define MG_MALLOC malloc\n#endif\n\n#ifndef MG_CALLOC\n#define MG_CALLOC calloc\n#endif\n\n#ifndef MG_REALLOC\n#define MG_REALLOC realloc\n#endif\n\n#ifndef MG_FREE\n#define MG_FREE free\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* CS_COMMON_MG_MEM_H_ */\n"
  },
  {
    "path": "lib/mjs/common/mg_str.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mg_mem.h\"\n#include \"mg_str.h\"\n#include \"platform.h\"\n\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n\nint mg_ncasecmp(const char* s1, const char* s2, size_t len) WEAK;\n\nstruct mg_str mg_mk_str(const char* s) WEAK;\nstruct mg_str mg_mk_str(const char* s) {\n    struct mg_str ret = {s, 0};\n    if(s != NULL) ret.len = strlen(s);\n    return ret;\n}\n\nstruct mg_str mg_mk_str_n(const char* s, size_t len) WEAK;\nstruct mg_str mg_mk_str_n(const char* s, size_t len) {\n    struct mg_str ret = {s, len};\n    return ret;\n}\n\nint mg_vcmp(const struct mg_str* str1, const char* str2) WEAK;\nint mg_vcmp(const struct mg_str* str1, const char* str2) {\n    size_t n2 = strlen(str2), n1 = str1->len;\n    int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);\n    if(r == 0) {\n        return n1 - n2;\n    }\n    return r;\n}\n\nint mg_vcasecmp(const struct mg_str* str1, const char* str2) WEAK;\nint mg_vcasecmp(const struct mg_str* str1, const char* str2) {\n    size_t n2 = strlen(str2), n1 = str1->len;\n    int r = mg_ncasecmp(str1->p, str2, (n1 < n2) ? n1 : n2);\n    if(r == 0) {\n        return n1 - n2;\n    }\n    return r;\n}\n\nstatic struct mg_str mg_strdup_common(const struct mg_str s, int nul_terminate) {\n    struct mg_str r = {NULL, 0};\n    if(s.len > 0 && s.p != NULL) {\n        char* sc = (char*)MG_MALLOC(s.len + (nul_terminate ? 1 : 0));\n        if(sc != NULL) {\n            memcpy(sc, s.p, s.len);\n            if(nul_terminate) sc[s.len] = '\\0';\n            r.p = sc;\n            r.len = s.len;\n        }\n    }\n    return r;\n}\n\nstruct mg_str mg_strdup(const struct mg_str s) WEAK;\nstruct mg_str mg_strdup(const struct mg_str s) {\n    return mg_strdup_common(s, 0 /* NUL-terminate */);\n}\n\nstruct mg_str mg_strdup_nul(const struct mg_str s) WEAK;\nstruct mg_str mg_strdup_nul(const struct mg_str s) {\n    return mg_strdup_common(s, 1 /* NUL-terminate */);\n}\n\nconst char* mg_strchr(const struct mg_str s, int c) WEAK;\nconst char* mg_strchr(const struct mg_str s, int c) {\n    size_t i;\n    for(i = 0; i < s.len; i++) {\n        if(s.p[i] == c) return &s.p[i];\n    }\n    return NULL;\n}\n\nint mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK;\nint mg_strcmp(const struct mg_str str1, const struct mg_str str2) {\n    size_t i = 0;\n    while(i < str1.len && i < str2.len) {\n        int c1 = str1.p[i];\n        int c2 = str2.p[i];\n        if(c1 < c2) return -1;\n        if(c1 > c2) return 1;\n        i++;\n    }\n    if(i < str1.len) return 1;\n    if(i < str2.len) return -1;\n    return 0;\n}\n\nint mg_strncmp(const struct mg_str, const struct mg_str, size_t n) WEAK;\nint mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) {\n    struct mg_str s1 = str1;\n    struct mg_str s2 = str2;\n\n    if(s1.len > n) {\n        s1.len = n;\n    }\n    if(s2.len > n) {\n        s2.len = n;\n    }\n    return mg_strcmp(s1, s2);\n}\n\nint mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) WEAK;\nint mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {\n    size_t i = 0;\n    while(i < str1.len && i < str2.len) {\n        int c1 = tolower((int)str1.p[i]);\n        int c2 = tolower((int)str2.p[i]);\n        if(c1 < c2) return -1;\n        if(c1 > c2) return 1;\n        i++;\n    }\n    if(i < str1.len) return 1;\n    if(i < str2.len) return -1;\n    return 0;\n}\n\nvoid mg_strfree(struct mg_str* s) WEAK;\nvoid mg_strfree(struct mg_str* s) {\n    char* sp = (char*)s->p;\n    s->p = NULL;\n    s->len = 0;\n    if(sp != NULL) free(sp);\n}\n\nconst char* mg_strstr(const struct mg_str haystack, const struct mg_str needle) WEAK;\nconst char* mg_strstr(const struct mg_str haystack, const struct mg_str needle) {\n    size_t i;\n    if(needle.len > haystack.len) return NULL;\n    for(i = 0; i <= haystack.len - needle.len; i++) {\n        if(memcmp(haystack.p + i, needle.p, needle.len) == 0) {\n            return haystack.p + i;\n        }\n    }\n    return NULL;\n}\n\nstruct mg_str mg_strstrip(struct mg_str s) WEAK;\nstruct mg_str mg_strstrip(struct mg_str s) {\n    while(s.len > 0 && isspace((int)*s.p)) {\n        s.p++;\n        s.len--;\n    }\n    while(s.len > 0 && isspace((int)*(s.p + s.len - 1))) {\n        s.len--;\n    }\n    return s;\n}\n\nint mg_str_starts_with(struct mg_str s, struct mg_str prefix) WEAK;\nint mg_str_starts_with(struct mg_str s, struct mg_str prefix) {\n    const struct mg_str sp = MG_MK_STR_N(s.p, prefix.len);\n    if(s.len < prefix.len) return 0;\n    return (mg_strcmp(sp, prefix) == 0);\n}\n"
  },
  {
    "path": "lib/mjs/common/mg_str.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_MG_STR_H_\n#define CS_COMMON_MG_STR_H_\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Describes chunk of memory */\nstruct mg_str {\n  const char *p; /* Memory chunk pointer */\n  size_t len;    /* Memory chunk length */\n};\n\n/*\n * Helper function for creating mg_str struct from plain C string.\n * `NULL` is allowed and becomes `{NULL, 0}`.\n */\nstruct mg_str mg_mk_str(const char *s);\n\n/*\n * Like `mg_mk_str`, but takes string length explicitly.\n */\nstruct mg_str mg_mk_str_n(const char *s, size_t len);\n\n/* Macro for initializing mg_str. */\n#define MG_MK_STR(str_literal) \\\n  { str_literal, sizeof(str_literal) - 1 }\n#define MG_MK_STR_N(str_literal, len) \\\n  { str_literal, len }\n#define MG_NULL_STR \\\n  { NULL, 0 }\n\n/*\n * Cross-platform version of `strcmp()` where where first string is\n * specified by `struct mg_str`.\n */\nint mg_vcmp(const struct mg_str *str2, const char *str1);\n\n/*\n * Cross-platform version of `strncasecmp()` where first string is\n * specified by `struct mg_str`.\n */\nint mg_vcasecmp(const struct mg_str *str2, const char *str1);\n\n/* Creates a copy of s (heap-allocated). */\nstruct mg_str mg_strdup(const struct mg_str s);\n\n/*\n * Creates a copy of s (heap-allocated).\n * Resulting string is NUL-terminated (but NUL is not included in len).\n */\nstruct mg_str mg_strdup_nul(const struct mg_str s);\n\n/*\n * Locates character in a string.\n */\nconst char *mg_strchr(const struct mg_str s, int c);\n\n/*\n * Compare two `mg_str`s; return value is the same as `strcmp`.\n */\nint mg_strcmp(const struct mg_str str1, const struct mg_str str2);\n\n/*\n * Like `mg_strcmp`, but compares at most `n` characters.\n */\nint mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);\n\n/*\n * Compare two `mg_str`s ignoreing case; return value is the same as `strcmp`.\n */\nint mg_strcasecmp(const struct mg_str str1, const struct mg_str str2);\n\n/*\n * Free the string (assuming it was heap allocated).\n */\nvoid mg_strfree(struct mg_str *s);\n\n/*\n * Finds the first occurrence of a substring `needle` in the `haystack`.\n */\nconst char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);\n\n/* Strip whitespace at the start and the end of s */\nstruct mg_str mg_strstrip(struct mg_str s);\n\n/* Returns 1 if s starts with the given prefix. */\nint mg_str_starts_with(struct mg_str s, struct mg_str prefix);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* CS_COMMON_MG_STR_H_ */\n"
  },
  {
    "path": "lib/mjs/common/platform.h",
    "content": "#ifndef CS_COMMON_PLATFORM_H_\n#define CS_COMMON_PLATFORM_H_\n\n/*\n * For the \"custom\" platform, includes and dependencies can be\n * provided through mg_locals.h.\n */\n#define CS_P_CUSTOM 0\n#define CS_P_UNIX 1\n#define CS_P_WINDOWS 2\n#define CS_P_ESP32 15\n#define CS_P_ESP8266 3\n#define CS_P_CC3100 6\n#define CS_P_CC3200 4\n#define CS_P_CC3220 17\n#define CS_P_MSP432 5\n#define CS_P_TM4C129 14\n#define CS_P_MBED 7\n#define CS_P_WINCE 8\n#define CS_P_NXP_LPC 13\n#define CS_P_NXP_KINETIS 9\n#define CS_P_NRF51 12\n#define CS_P_NRF52 10\n#define CS_P_PIC32 11\n#define CS_P_RS14100 18\n#define CS_P_STM32 16\n#define CS_P_FLIPPER 19\n/* Next id: 20 */\n\n#ifndef CS_PLATFORM\n#define CS_PLATFORM CS_P_FLIPPER\n#endif\n\n#ifndef CS_PLATFORM\n#error \"CS_PLATFORM is not specified and we couldn't guess it.\"\n#endif\n\n#define MG_NET_IF_SOCKET 1\n#define MG_NET_IF_SIMPLELINK 2\n#define MG_NET_IF_LWIP_LOW_LEVEL 3\n#define MG_NET_IF_PIC32 4\n#define MG_NET_IF_NULL 5\n\n#define MG_SSL_IF_OPENSSL 1\n#define MG_SSL_IF_MBEDTLS 2\n#define MG_SSL_IF_SIMPLELINK 3\n\n#if CS_PLATFORM == CS_P_FLIPPER\n#include \"platforms/platform_flipper.h\"\n#endif\n\n/* Common stuff */\n\n#if !defined(PRINTF_LIKE)\n#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)\n#define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a)))\n#else\n#define PRINTF_LIKE(f, a)\n#endif\n#endif\n\n#if !defined(WEAK)\n#if(defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)) && \\\n    !defined(_WIN32)\n#define WEAK __attribute__((weak))\n#else\n#define WEAK\n#endif\n#endif\n\n#ifdef __GNUC__\n#define NORETURN __attribute__((noreturn))\n#define NOINLINE __attribute__((noinline))\n#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n#define NOINSTR __attribute__((no_instrument_function))\n#define DO_NOT_WARN_UNUSED __attribute__((unused))\n#else\n#define NORETURN\n#define NOINLINE\n#define WARN_UNUSED_RESULT\n#define NOINSTR\n#define DO_NOT_WARN_UNUSED\n#endif /* __GNUC__ */\n\n#ifndef ARRAY_SIZE\n#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))\n#endif\n\n#endif /* CS_COMMON_PLATFORM_H_ */"
  },
  {
    "path": "lib/mjs/common/platforms/platform_flipper.c",
    "content": "#include <furi.h>\n#include <toolbox/stream/file_stream.h>\n#include \"../cs_dbg.h\"\n#include \"../frozen/frozen.h\"\n\nchar* cs_read_file(const char* path, size_t* size) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    Stream* stream = file_stream_alloc(storage);\n    char* data = NULL;\n    if(!file_stream_open(stream, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n    } else {\n        *size = stream_size(stream);\n        data = (char*)malloc(*size + 1);\n        if(data != NULL) {\n            stream_rewind(stream);\n            if(stream_read(stream, (uint8_t*)data, *size) != *size) {\n                file_stream_close(stream);\n                furi_record_close(RECORD_STORAGE);\n                stream_free(stream);\n                free(data);\n                return NULL;\n            }\n            data[*size] = '\\0';\n        }\n    }\n    file_stream_close(stream);\n    furi_record_close(RECORD_STORAGE);\n    stream_free(stream);\n    return data;\n}\n\nchar* json_fread(const char* path) {\n    UNUSED(path);\n    return NULL;\n}\n\nint json_vfprintf(const char* file_name, const char* fmt, va_list ap) {\n    UNUSED(file_name);\n    UNUSED(fmt);\n    UNUSED(ap);\n    return 0;\n}\n\nint json_prettify_file(const char* file_name) {\n    UNUSED(file_name);\n    return 0;\n}\n\nint json_printer_file(struct json_out* out, const char* buf, size_t len) {\n    UNUSED(out);\n    UNUSED(buf);\n    UNUSED(len);\n    return 0;\n}\n\nint cs_log_print_prefix(enum cs_log_level level, const char* file, int ln) {\n    (void)level;\n    (void)file;\n    (void)ln;\n    return 0;\n}\n\nvoid cs_log_printf(const char* fmt, ...) {\n    (void)fmt;\n}\n"
  },
  {
    "path": "lib/mjs/common/platforms/platform_flipper.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#if CS_PLATFORM == CS_P_FLIPPER\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#define to64(x) strtoll(x, NULL, 10)\n#define INT64_FMT \"lld\"\n#define SIZE_T_FMT \"u\"\ntypedef struct stat cs_stat_t;\n#define DIRSEP '/'\n\n#undef CS_ENABLE_STDIO\n#define CS_ENABLE_STDIO 0\n\n#ifndef MG_ENABLE_FILESYSTEM\n#define MG_ENABLE_FILESYSTEM 0\n#endif\n\n#endif /* CS_PLATFORM == CS_P_FLIPPER */"
  },
  {
    "path": "lib/mjs/common/str_util.c",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef EXCLUDE_COMMON\n\n#include \"str_util.h\"\n#include \"mg_mem.h\"\n#include \"platform.h\"\n\n#ifndef C_DISABLE_BUILTIN_SNPRINTF\n#define C_DISABLE_BUILTIN_SNPRINTF 1\n#endif\n\n#include \"mg_mem.h\"\n\nsize_t c_strnlen(const char* s, size_t maxlen) WEAK;\nsize_t c_strnlen(const char* s, size_t maxlen) {\n    size_t l = 0;\n    for(; l < maxlen && s[l] != '\\0'; l++) {\n    }\n    return l;\n}\n\n#define C_SNPRINTF_APPEND_CHAR(ch)         \\\n    do {                                   \\\n        if(i < (int)buf_size) buf[i] = ch; \\\n        i++;                               \\\n    } while(0)\n\n#define C_SNPRINTF_FLAG_ZERO 1\n\n#if C_DISABLE_BUILTIN_SNPRINTF\nint c_vsnprintf(char* buf, size_t buf_size, const char* fmt, va_list ap) WEAK;\nint c_vsnprintf(char* buf, size_t buf_size, const char* fmt, va_list ap) {\n    return vsnprintf(buf, buf_size, fmt, ap);\n}\n#else\nstatic int c_itoa(char* buf, size_t buf_size, int64_t num, int base, int flags, int field_width) {\n    char tmp[40];\n    int i = 0, k = 0, neg = 0;\n\n    if(num < 0) {\n        neg++;\n        num = -num;\n    }\n\n    /* Print into temporary buffer - in reverse order */\n    do {\n        int rem = num % base;\n        if(rem < 10) {\n            tmp[k++] = '0' + rem;\n        } else {\n            tmp[k++] = 'a' + (rem - 10);\n        }\n        num /= base;\n    } while(num > 0);\n\n    /* Zero padding */\n    if(flags && C_SNPRINTF_FLAG_ZERO) {\n        while(k < field_width && k < (int)sizeof(tmp) - 1) {\n            tmp[k++] = '0';\n        }\n    }\n\n    /* And sign */\n    if(neg) {\n        tmp[k++] = '-';\n    }\n\n    /* Now output */\n    while(--k >= 0) {\n        C_SNPRINTF_APPEND_CHAR(tmp[k]);\n    }\n\n    return i;\n}\n\nint c_vsnprintf(char* buf, size_t buf_size, const char* fmt, va_list ap) WEAK;\nint c_vsnprintf(char* buf, size_t buf_size, const char* fmt, va_list ap) {\n    int ch, i = 0, len_mod, flags, precision, field_width;\n\n    while((ch = *fmt++) != '\\0') {\n        if(ch != '%') {\n            C_SNPRINTF_APPEND_CHAR(ch);\n        } else {\n            /*\n       * Conversion specification:\n       *   zero or more flags (one of: # 0 - <space> + ')\n       *   an optional minimum  field  width (digits)\n       *   an  optional precision (. followed by digits, or *)\n       *   an optional length modifier (one of: hh h l ll L q j z t)\n       *   conversion specifier (one of: d i o u x X e E f F g G a A c s p n)\n       */\n            flags = field_width = precision = len_mod = 0;\n\n            /* Flags. only zero-pad flag is supported. */\n            if(*fmt == '0') {\n                flags |= C_SNPRINTF_FLAG_ZERO;\n            }\n\n            /* Field width */\n            while(*fmt >= '0' && *fmt <= '9') {\n                field_width *= 10;\n                field_width += *fmt++ - '0';\n            }\n            /* Dynamic field width */\n            if(*fmt == '*') {\n                field_width = va_arg(ap, int);\n                fmt++;\n            }\n\n            /* Precision */\n            if(*fmt == '.') {\n                fmt++;\n                if(*fmt == '*') {\n                    precision = va_arg(ap, int);\n                    fmt++;\n                } else {\n                    while(*fmt >= '0' && *fmt <= '9') {\n                        precision *= 10;\n                        precision += *fmt++ - '0';\n                    }\n                }\n            }\n\n            /* Length modifier */\n            switch(*fmt) {\n            case 'h':\n            case 'l':\n            case 'L':\n            case 'I':\n            case 'q':\n            case 'j':\n            case 'z':\n            case 't':\n                len_mod = *fmt++;\n                if(*fmt == 'h') {\n                    len_mod = 'H';\n                    fmt++;\n                }\n                if(*fmt == 'l') {\n                    len_mod = 'q';\n                    fmt++;\n                }\n                break;\n            }\n\n            ch = *fmt++;\n            if(ch == 's') {\n                const char* s = va_arg(ap, const char*); /* Always fetch parameter */\n                int j;\n                int pad = field_width - (precision >= 0 ? c_strnlen(s, precision) : 0);\n                for(j = 0; j < pad; j++) {\n                    C_SNPRINTF_APPEND_CHAR(' ');\n                }\n\n                /* `s` may be NULL in case of %.*s */\n                if(s != NULL) {\n                    /* Ignore negative and 0 precisions */\n                    for(j = 0; (precision <= 0 || j < precision) && s[j] != '\\0'; j++) {\n                        C_SNPRINTF_APPEND_CHAR(s[j]);\n                    }\n                }\n            } else if(ch == 'c') {\n                ch = va_arg(ap, int); /* Always fetch parameter */\n                C_SNPRINTF_APPEND_CHAR(ch);\n            } else if(ch == 'd' && len_mod == 0) {\n                i += c_itoa(buf + i, buf_size - i, va_arg(ap, int), 10, flags, field_width);\n            } else if(ch == 'd' && len_mod == 'l') {\n                i += c_itoa(buf + i, buf_size - i, va_arg(ap, long), 10, flags, field_width);\n#ifdef SSIZE_MAX\n            } else if(ch == 'd' && len_mod == 'z') {\n                i += c_itoa(buf + i, buf_size - i, va_arg(ap, ssize_t), 10, flags, field_width);\n#endif\n            } else if(ch == 'd' && len_mod == 'q') {\n                i += c_itoa(buf + i, buf_size - i, va_arg(ap, int64_t), 10, flags, field_width);\n            } else if((ch == 'x' || ch == 'u') && len_mod == 0) {\n                i += c_itoa(\n                    buf + i,\n                    buf_size - i,\n                    va_arg(ap, unsigned),\n                    ch == 'x' ? 16 : 10,\n                    flags,\n                    field_width);\n            } else if((ch == 'x' || ch == 'u') && len_mod == 'l') {\n                i += c_itoa(\n                    buf + i,\n                    buf_size - i,\n                    va_arg(ap, unsigned long),\n                    ch == 'x' ? 16 : 10,\n                    flags,\n                    field_width);\n            } else if((ch == 'x' || ch == 'u') && len_mod == 'z') {\n                i += c_itoa(\n                    buf + i,\n                    buf_size - i,\n                    va_arg(ap, size_t),\n                    ch == 'x' ? 16 : 10,\n                    flags,\n                    field_width);\n            } else if(ch == 'p') {\n                unsigned long num = (unsigned long)(uintptr_t)va_arg(ap, void*);\n                C_SNPRINTF_APPEND_CHAR('0');\n                C_SNPRINTF_APPEND_CHAR('x');\n                i += c_itoa(buf + i, buf_size - i, num, 16, flags, 0);\n            } else {\n#ifndef NO_LIBC\n                /*\n         * TODO(lsm): abort is not nice in a library, remove it\n         * Also, ESP8266 SDK doesn't have it\n         */\n                abort();\n#endif\n            }\n        }\n    }\n\n    /* Zero-terminate the result */\n    if(buf_size > 0) {\n        buf[i < (int)buf_size ? i : (int)buf_size - 1] = '\\0';\n    }\n\n    return i;\n}\n#endif\n\nint c_snprintf(char* buf, size_t buf_size, const char* fmt, ...) WEAK;\nint c_snprintf(char* buf, size_t buf_size, const char* fmt, ...) {\n    int result;\n    va_list ap;\n    va_start(ap, fmt);\n    result = c_vsnprintf(buf, buf_size, fmt, ap);\n    va_end(ap);\n    return result;\n}\n\n#ifdef _WIN32\nint to_wchar(const char* path, wchar_t* wbuf, size_t wbuf_len) {\n    int ret;\n    char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;\n\n    strncpy(buf, path, sizeof(buf));\n    buf[sizeof(buf) - 1] = '\\0';\n\n    /* Trim trailing slashes. Leave backslash for paths like \"X:\\\" */\n    p = buf + strlen(buf) - 1;\n    while(p > buf && p[-1] != ':' && (p[0] == '\\\\' || p[0] == '/')) *p-- = '\\0';\n\n    memset(wbuf, 0, wbuf_len * sizeof(wchar_t));\n    ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);\n\n    /*\n   * Convert back to Unicode. If doubly-converted string does not match the\n   * original, something is fishy, reject.\n   */\n    WideCharToMultiByte(CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);\n    if(strcmp(buf, buf2) != 0) {\n        wbuf[0] = L'\\0';\n        ret = 0;\n    }\n\n    return ret;\n}\n#endif /* _WIN32 */\n\n/* The simplest O(mn) algorithm. Better implementation are GPLed */\nconst char* c_strnstr(const char* s, const char* find, size_t slen) WEAK;\nconst char* c_strnstr(const char* s, const char* find, size_t slen) {\n    size_t find_length = strlen(find);\n    size_t i;\n\n    for(i = 0; i < slen; i++) {\n        if(i + find_length > slen) {\n            return NULL;\n        }\n\n        if(strncmp(&s[i], find, find_length) == 0) {\n            return &s[i];\n        }\n    }\n\n    return NULL;\n}\n\n#if CS_ENABLE_STRDUP\nchar* strdup(const char* src) WEAK;\nchar* strdup(const char* src) {\n    size_t len = strlen(src) + 1;\n    char* ret = MG_MALLOC(len);\n    if(ret != NULL) {\n        strcpy(ret, src);\n    }\n    return ret;\n}\n#endif\n\nvoid cs_to_hex(char* to, const unsigned char* p, size_t len) WEAK;\nvoid cs_to_hex(char* to, const unsigned char* p, size_t len) {\n    static const char* hex = \"0123456789abcdef\";\n\n    for(; len--; p++) {\n        *to++ = hex[p[0] >> 4];\n        *to++ = hex[p[0] & 0x0f];\n    }\n    *to = '\\0';\n}\n\nstatic int fourbit(int ch) {\n    if(ch >= '0' && ch <= '9') {\n        return ch - '0';\n    } else if(ch >= 'a' && ch <= 'f') {\n        return ch - 'a' + 10;\n    } else if(ch >= 'A' && ch <= 'F') {\n        return ch - 'A' + 10;\n    }\n    return 0;\n}\n\nvoid cs_from_hex(char* to, const char* p, size_t len) WEAK;\nvoid cs_from_hex(char* to, const char* p, size_t len) {\n    size_t i;\n\n    for(i = 0; i < len; i += 2) {\n        *to++ = (fourbit(p[i]) << 4) + fourbit(p[i + 1]);\n    }\n    *to = '\\0';\n}\n\n#if CS_ENABLE_TO64\nint64_t cs_to64(const char* s) WEAK;\nint64_t cs_to64(const char* s) {\n    int64_t result = 0;\n    int64_t neg = 1;\n    while(*s && isspace((unsigned char)*s)) s++;\n    if(*s == '-') {\n        neg = -1;\n        s++;\n    }\n    while(isdigit((unsigned char)*s)) {\n        result *= 10;\n        result += (*s - '0');\n        s++;\n    }\n    return result * neg;\n}\n#endif\n\nstatic int str_util_lowercase(const char* s) {\n    return tolower(*(const unsigned char*)s);\n}\n\nint mg_ncasecmp(const char* s1, const char* s2, size_t len) WEAK;\nint mg_ncasecmp(const char* s1, const char* s2, size_t len) {\n    int diff = 0;\n\n    if(len > 0) do {\n            diff = str_util_lowercase(s1++) - str_util_lowercase(s2++);\n        } while(diff == 0 && s1[-1] != '\\0' && --len > 0);\n\n    return diff;\n}\n\nint mg_casecmp(const char* s1, const char* s2) WEAK;\nint mg_casecmp(const char* s1, const char* s2) {\n    return mg_ncasecmp(s1, s2, (size_t)~0);\n}\n\nint mg_asprintf(char** buf, size_t size, const char* fmt, ...) WEAK;\nint mg_asprintf(char** buf, size_t size, const char* fmt, ...) {\n    int ret;\n    va_list ap;\n    va_start(ap, fmt);\n    ret = mg_avprintf(buf, size, fmt, ap);\n    va_end(ap);\n    return ret;\n}\n\nint mg_avprintf(char** buf, size_t size, const char* fmt, va_list ap) WEAK;\nint mg_avprintf(char** buf, size_t size, const char* fmt, va_list ap) {\n    va_list ap_copy;\n    int len;\n\n    va_copy(ap_copy, ap);\n    len = vsnprintf(*buf, size, fmt, ap_copy);\n    va_end(ap_copy);\n\n    if(len < 0) {\n        /* eCos and Windows are not standard-compliant and return -1 when\n     * the buffer is too small. Keep allocating larger buffers until we\n     * succeed or out of memory. */\n        *buf = NULL; /* LCOV_EXCL_START */\n        while(len < 0) {\n            MG_FREE(*buf);\n            if(size == 0) {\n                size = 5;\n            }\n            size *= 2;\n            if((*buf = (char*)MG_MALLOC(size)) == NULL) {\n                len = -1;\n                break;\n            }\n            va_copy(ap_copy, ap);\n            len = vsnprintf(*buf, size - 1, fmt, ap_copy);\n            va_end(ap_copy);\n        }\n\n        /*\n     * Microsoft version of vsnprintf() is not always null-terminated, so put\n     * the terminator manually\n     */\n        (*buf)[len] = 0;\n        /* LCOV_EXCL_STOP */\n    } else if(len >= (int)size) {\n        /* Standard-compliant code path. Allocate a buffer that is large enough. */\n        if((*buf = (char*)MG_MALLOC(len + 1)) == NULL) {\n            len = -1; /* LCOV_EXCL_LINE */\n        } else { /* LCOV_EXCL_LINE */\n            va_copy(ap_copy, ap);\n            len = vsnprintf(*buf, len + 1, fmt, ap_copy);\n            va_end(ap_copy);\n        }\n    }\n\n    return len;\n}\n\nconst char* mg_next_comma_list_entry(const char*, struct mg_str*, struct mg_str*) WEAK;\nconst char* mg_next_comma_list_entry(const char* list, struct mg_str* val, struct mg_str* eq_val) {\n    struct mg_str ret = mg_next_comma_list_entry_n(mg_mk_str(list), val, eq_val);\n    return ret.p;\n}\n\nstruct mg_str\n    mg_next_comma_list_entry_n(struct mg_str list, struct mg_str* val, struct mg_str* eq_val) WEAK;\nstruct mg_str\n    mg_next_comma_list_entry_n(struct mg_str list, struct mg_str* val, struct mg_str* eq_val) {\n    if(list.len == 0) {\n        /* End of the list */\n        list = mg_mk_str(NULL);\n    } else {\n        const char* chr = NULL;\n        *val = list;\n\n        if((chr = mg_strchr(*val, ',')) != NULL) {\n            /* Comma found. Store length and shift the list ptr */\n            val->len = chr - val->p;\n            chr++;\n            list.len -= (chr - list.p);\n            list.p = chr;\n        } else {\n            /* This value is the last one */\n            list = mg_mk_str_n(list.p + list.len, 0);\n        }\n\n        if(eq_val != NULL) {\n            /* Value has form \"x=y\", adjust pointers and lengths */\n            /* so that val points to \"x\", and eq_val points to \"y\". */\n            eq_val->len = 0;\n            eq_val->p = (const char*)memchr(val->p, '=', val->len);\n            if(eq_val->p != NULL) {\n                eq_val->p++; /* Skip over '=' character */\n                eq_val->len = val->p + val->len - eq_val->p;\n                val->len = (eq_val->p - val->p) - 1;\n            }\n        }\n    }\n\n    return list;\n}\n\nsize_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;\nsize_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {\n    const char* or_str;\n    size_t res = 0, len = 0, i = 0, j = 0;\n\n    if((or_str = (const char*)memchr(pattern.p, '|', pattern.len)) != NULL ||\n       (or_str = (const char*)memchr(pattern.p, ',', pattern.len)) != NULL) {\n        struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)};\n        res = mg_match_prefix_n(pstr, str);\n        if(res > 0) return res;\n        pstr.p = or_str + 1;\n        pstr.len = (pattern.p + pattern.len) - (or_str + 1);\n        return mg_match_prefix_n(pstr, str);\n    }\n\n    for(; i < pattern.len && j < str.len; i++, j++) {\n        if(pattern.p[i] == '?') {\n            continue;\n        } else if(pattern.p[i] == '*') {\n            i++;\n            if(i < pattern.len && pattern.p[i] == '*') {\n                i++;\n                len = str.len - j;\n            } else {\n                len = 0;\n                while(j + len < str.len && str.p[j + len] != '/') len++;\n            }\n            if(i == pattern.len || (pattern.p[i] == '$' && i == pattern.len - 1)) return j + len;\n            do {\n                const struct mg_str pstr = {pattern.p + i, pattern.len - i};\n                const struct mg_str sstr = {str.p + j + len, str.len - j - len};\n                res = mg_match_prefix_n(pstr, sstr);\n            } while(res == 0 && len != 0 && len-- > 0);\n            return res == 0 ? 0 : j + res + len;\n        } else if(str_util_lowercase(&pattern.p[i]) != str_util_lowercase(&str.p[j])) {\n            break;\n        }\n    }\n    if(i < pattern.len && pattern.p[i] == '$') {\n        return j == str.len ? str.len : 0;\n    }\n    return i == pattern.len ? j : 0;\n}\n\nsize_t mg_match_prefix(const char*, int, const char*) WEAK;\nsize_t mg_match_prefix(const char* pattern, int pattern_len, const char* str) {\n    const struct mg_str pstr = {pattern, (size_t)pattern_len};\n    struct mg_str s = {str, 0};\n    if(str != NULL) s.len = strlen(str);\n    return mg_match_prefix_n(pstr, s);\n}\n\n#endif /* EXCLUDE_COMMON */\n"
  },
  {
    "path": "lib/mjs/common/str_util.h",
    "content": "/*\n * Copyright (c) 2014-2018 Cesanta Software Limited\n * All rights reserved\n *\n * Licensed under the Apache License, Version 2.0 (the \"\"License\"\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"\"AS IS\"\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CS_COMMON_STR_UTIL_H_\n#define CS_COMMON_STR_UTIL_H_\n\n#include <stdarg.h>\n#include <stdlib.h>\n\n#include \"mg_str.h\"\n#include \"platform.h\"\n\n#ifndef CS_ENABLE_STRDUP\n#define CS_ENABLE_STRDUP 0\n#endif\n\n#ifndef CS_ENABLE_TO64\n#define CS_ENABLE_TO64 0\n#endif\n\n/*\n * Expands to a string representation of its argument: e.g.\n * `CS_STRINGIFY_LIT(5) expands to \"5\"`\n */\n#if !defined(_MSC_VER) || _MSC_VER >= 1900\n#define CS_STRINGIFY_LIT(...) #__VA_ARGS__\n#else\n#define CS_STRINGIFY_LIT(x) #x\n#endif\n\n/*\n * Expands to a string representation of its argument, which is allowed\n * to be a macro: e.g.\n *\n * #define FOO 123\n * CS_STRINGIFY_MACRO(FOO)\n *\n * expands to 123.\n */\n#define CS_STRINGIFY_MACRO(x) CS_STRINGIFY_LIT(x)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Equivalent of standard `strnlen()`.\n */\nsize_t c_strnlen(const char* s, size_t maxlen);\n\n/*\n * Equivalent of standard `snprintf()`.\n */\nint c_snprintf(char* buf, size_t buf_size, const char* format, ...) PRINTF_LIKE(3, 4);\n\n/*\n * Equivalent of standard `vsnprintf()`.\n */\nint c_vsnprintf(char* buf, size_t buf_size, const char* format, va_list ap);\n\n/*\n * Find the first occurrence of find in s, where the search is limited to the\n * first slen characters of s.\n */\nconst char* c_strnstr(const char* s, const char* find, size_t slen);\n\n/*\n * Stringify binary data. Output buffer size must be 2 * size_of_input + 1\n * because each byte of input takes 2 bytes in string representation\n * plus 1 byte for the terminating \\0 character.\n */\nvoid cs_to_hex(char* to, const unsigned char* p, size_t len);\n\n/*\n * Convert stringified binary data back to binary.\n * Does the reverse of `cs_to_hex()`.\n */\nvoid cs_from_hex(char* to, const char* p, size_t len);\n\n#if CS_ENABLE_STRDUP\n/*\n * Equivalent of standard `strdup()`, defined if only `CS_ENABLE_STRDUP` is 1.\n */\nchar* strdup(const char* src);\n#endif\n\n#if CS_ENABLE_TO64\n#include <stdint.h>\n/*\n * Simple string -> int64 conversion routine.\n */\nint64_t cs_to64(const char* s);\n#endif\n\n/*\n * Cross-platform version of `strncasecmp()`.\n */\nint mg_ncasecmp(const char* s1, const char* s2, size_t len);\n\n/*\n * Cross-platform version of `strcasecmp()`.\n */\nint mg_casecmp(const char* s1, const char* s2);\n\n/*\n * Prints message to the buffer. If the buffer is large enough to hold the\n * message, it returns buffer. If buffer is to small, it allocates a large\n * enough buffer on heap and returns allocated buffer.\n * This is a supposed use case:\n *\n * ```c\n *    char buf[5], *p = buf;\n *    mg_avprintf(&p, sizeof(buf), \"%s\", \"hi there\");\n *    use_p_somehow(p);\n *    if (p != buf) {\n *      free(p);\n *    }\n * ```\n *\n * The purpose of this is to avoid malloc-ing if generated strings are small.\n */\nint mg_asprintf(char** buf, size_t size, const char* fmt, ...) PRINTF_LIKE(3, 4);\n\n/* Same as mg_asprintf, but takes varargs list. */\nint mg_avprintf(char** buf, size_t size, const char* fmt, va_list ap);\n\n/*\n * A helper function for traversing a comma separated list of values.\n * It returns a list pointer shifted to the next value or NULL if the end\n * of the list found.\n * The value is stored in a val vector. If the value has a form \"x=y\", then\n * eq_val vector is initialised to point to the \"y\" part, and val vector length\n * is adjusted to point only to \"x\".\n * If the list is just a comma separated list of entries, like \"aa,bb,cc\" then\n * `eq_val` will contain zero-length string.\n *\n * The purpose of this function is to parse comma separated string without\n * any copying/memory allocation.\n */\nconst char* mg_next_comma_list_entry(const char* list, struct mg_str* val, struct mg_str* eq_val);\n\n/*\n * Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`.\n * NB: Test return value's .p, not .len. On last itreation that yields result\n * .len will be 0 but .p will not. When finished, .p will be NULL.\n */\nstruct mg_str\n    mg_next_comma_list_entry_n(struct mg_str list, struct mg_str* val, struct mg_str* eq_val);\n\n/*\n * Matches 0-terminated string (mg_match_prefix) or string with given length\n * mg_match_prefix_n against a glob pattern. Glob syntax:\n * ```\n * - * matches zero or more characters until a slash character /\n * - ** matches zero or more characters\n * - ? Matches exactly one character which is not a slash /\n * - | or ,  divides alternative patterns\n * - any other character matches itself\n * ```\n * Match is case-insensitive. Return number of bytes matched.\n * Examples:\n * ```\n * mg_match_prefix(\"a*f\", len, \"abcdefgh\") == 6\n * mg_match_prefix(\"a*f\", len, \"abcdexgh\") == 0\n * mg_match_prefix(\"a*f|de*,xy\", len, \"defgh\") == 5\n * mg_match_prefix(\"?*\", len, \"abc\") == 3\n * mg_match_prefix(\"?*\", len, \"\") == 0\n * ```\n */\nsize_t mg_match_prefix(const char* pattern, int pattern_len, const char* str);\n\n/*\n * Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`.\n */\nsize_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* CS_COMMON_STR_UTIL_H_ */\n"
  },
  {
    "path": "lib/mjs/ffi/ffi.c",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"ffi.h\"\n\n#define IS_W(arg) ((arg).ctype == FFI_CTYPE_WORD)\n#define IS_D(arg) ((arg).ctype == FFI_CTYPE_DOUBLE)\n#define IS_F(arg) ((arg).ctype == FFI_CTYPE_FLOAT)\n\n#define W(arg) ((ffi_word_t)(arg).v.i)\n#define D(arg) ((arg).v.d)\n#define F(arg) ((arg).v.f)\n\nvoid ffi_set_word(struct ffi_arg* arg, ffi_word_t v) {\n    arg->ctype = FFI_CTYPE_WORD;\n    arg->v.i = v;\n}\n\nvoid ffi_set_bool(struct ffi_arg* arg, bool v) {\n    arg->ctype = FFI_CTYPE_BOOL;\n    arg->v.i = v;\n}\n\nvoid ffi_set_ptr(struct ffi_arg* arg, void* v) {\n    ffi_set_word(arg, (ffi_word_t)v);\n}\n\nvoid ffi_set_double(struct ffi_arg* arg, double v) {\n    arg->ctype = FFI_CTYPE_DOUBLE;\n    arg->v.d = v;\n}\n\nvoid ffi_set_float(struct ffi_arg* arg, float v) {\n    arg->ctype = FFI_CTYPE_FLOAT;\n    arg->v.f = v;\n}\n\n/*\n * The ARM ABI uses only 4 32-bit registers for paramter passing.\n * Xtensa call0 calling-convention (as used by Espressif) has 6.\n *\n * Focusing only on implementing FFI with registers means we can simplify a lot.\n *\n * ARM has some quasi-alignment rules when mixing double and integers as\n * arguments. Only:\n *   a) double, int32_t, int32_t\n *   b) int32_t, double\n * would fit in 4 registers. (the same goes for uint64_t).\n *\n * In order to simplify further, when a double-width argument is present, we\n * allow only two arguments.\n */\n\n/*\n * We need to support x86_64 in order to support local tests.\n * x86_64 has more and wider registers, but unlike the two main\n * embedded platforms we target it has a separate register file for\n * integer values and for floating point values (both for passing args and\n * return values). E.g. if a double value is passed as a second argument\n * it gets passed in the first available floating point register.\n *\n * I.e, the compiler generates exactly the same code for:\n *\n * void foo(int a, double b) {...}\n *\n * and\n *\n * void foo(double b, int a) {...}\n *\n *\n */\n\ntypedef ffi_word_t (*w4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef ffi_word_t (*w5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef ffi_word_t (*w6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\n\ntypedef ffi_word_t (*wdw_t)(double, ffi_word_t);\ntypedef ffi_word_t (*wwd_t)(ffi_word_t, double);\ntypedef ffi_word_t (*wdd_t)(double, double);\n\ntypedef ffi_word_t (*wwwd_t)(ffi_word_t, ffi_word_t, double);\ntypedef ffi_word_t (*wwdw_t)(ffi_word_t, double, ffi_word_t);\ntypedef ffi_word_t (*wwdd_t)(ffi_word_t, double, double);\ntypedef ffi_word_t (*wdww_t)(double, ffi_word_t, ffi_word_t);\ntypedef ffi_word_t (*wdwd_t)(double, ffi_word_t, double);\ntypedef ffi_word_t (*wddw_t)(double, double, ffi_word_t);\ntypedef ffi_word_t (*wddd_t)(double, double, double);\n\ntypedef ffi_word_t (*wfw_t)(float, ffi_word_t);\ntypedef ffi_word_t (*wwf_t)(ffi_word_t, float);\ntypedef ffi_word_t (*wff_t)(float, float);\n\ntypedef ffi_word_t (*wwwf_t)(ffi_word_t, ffi_word_t, float);\ntypedef ffi_word_t (*wwfw_t)(ffi_word_t, float, ffi_word_t);\ntypedef ffi_word_t (*wwff_t)(ffi_word_t, float, float);\ntypedef ffi_word_t (*wfww_t)(float, ffi_word_t, ffi_word_t);\ntypedef ffi_word_t (*wfwf_t)(float, ffi_word_t, float);\ntypedef ffi_word_t (*wffw_t)(float, float, ffi_word_t);\ntypedef ffi_word_t (*wfff_t)(float, float, float);\n\ntypedef bool (*b4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef bool (*b5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef bool (*b6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef bool (*bdw_t)(double, ffi_word_t);\ntypedef bool (*bwd_t)(ffi_word_t, double);\ntypedef bool (*bdd_t)(double, double);\n\ntypedef bool (*bwwd_t)(ffi_word_t, ffi_word_t, double);\ntypedef bool (*bwdw_t)(ffi_word_t, double, ffi_word_t);\ntypedef bool (*bwdd_t)(ffi_word_t, double, double);\ntypedef bool (*bdww_t)(double, ffi_word_t, ffi_word_t);\ntypedef bool (*bdwd_t)(double, ffi_word_t, double);\ntypedef bool (*bddw_t)(double, double, ffi_word_t);\ntypedef bool (*bddd_t)(double, double, double);\n\ntypedef bool (*bfw_t)(float, ffi_word_t);\ntypedef bool (*bwf_t)(ffi_word_t, float);\ntypedef bool (*bff_t)(float, float);\n\ntypedef bool (*bwwf_t)(ffi_word_t, ffi_word_t, float);\ntypedef bool (*bwfw_t)(ffi_word_t, float, ffi_word_t);\ntypedef bool (*bwff_t)(ffi_word_t, float, float);\ntypedef bool (*bfww_t)(float, ffi_word_t, ffi_word_t);\ntypedef bool (*bfwf_t)(float, ffi_word_t, float);\ntypedef bool (*bffw_t)(float, float, ffi_word_t);\ntypedef bool (*bfff_t)(float, float, float);\n\ntypedef double (*d4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef double (*d5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef double (*d6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef double (*ddw_t)(double, ffi_word_t);\ntypedef double (*dwd_t)(ffi_word_t, double);\ntypedef double (*ddd_t)(double, double);\n\ntypedef double (*dwwd_t)(ffi_word_t, ffi_word_t, double);\ntypedef double (*dwdw_t)(ffi_word_t, double, ffi_word_t);\ntypedef double (*dwdd_t)(ffi_word_t, double, double);\ntypedef double (*ddww_t)(double, ffi_word_t, ffi_word_t);\ntypedef double (*ddwd_t)(double, ffi_word_t, double);\ntypedef double (*dddw_t)(double, double, ffi_word_t);\ntypedef double (*dddd_t)(double, double, double);\n\ntypedef float (*f4w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef float (*f5w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef float (*f6w_t)(ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t, ffi_word_t);\ntypedef float (*ffw_t)(float, ffi_word_t);\ntypedef float (*fwf_t)(ffi_word_t, float);\ntypedef float (*fff_t)(float, float);\n\ntypedef float (*fwwf_t)(ffi_word_t, ffi_word_t, float);\ntypedef float (*fwfw_t)(ffi_word_t, float, ffi_word_t);\ntypedef float (*fwff_t)(ffi_word_t, float, float);\ntypedef float (*ffww_t)(float, ffi_word_t, ffi_word_t);\ntypedef float (*ffwf_t)(float, ffi_word_t, float);\ntypedef float (*fffw_t)(float, float, ffi_word_t);\ntypedef float (*ffff_t)(float, float, float);\n\nint ffi_call_mjs(ffi_fn_t* func, int nargs, struct ffi_arg* res, struct ffi_arg* args) {\n    int i, doubles = 0, floats = 0;\n\n    if(nargs > 6) return -1;\n    for(i = 0; i < nargs; i++) {\n        doubles += (IS_D(args[i]));\n        floats += (IS_F(args[i]));\n    }\n\n    /* Doubles and floats are not supported together atm */\n    if(doubles > 0 && floats > 0) {\n        return -1;\n    }\n\n    switch(res->ctype) {\n    case FFI_CTYPE_WORD: { /* {{{ */\n        ffi_word_t r;\n        if(doubles == 0) {\n            if(floats == 0) {\n                /*\n           * No double and no float args: we currently support up to 6\n           * word-sized arguments\n           */\n                if(nargs <= 4) {\n                    w4w_t f = (w4w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]));\n                } else if(nargs == 5) {\n                    w5w_t f = (w5w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]));\n                } else if(nargs == 6) {\n                    w6w_t f = (w6w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), W(args[5]));\n                } else {\n                    abort();\n                }\n            } else {\n                /* There are some floats */\n                switch(nargs) {\n                case 0:\n                case 1:\n                case 2:\n                    if(IS_F(args[0]) && IS_F(args[1])) {\n                        wff_t f = (wff_t)func;\n                        r = f(F(args[0]), F(args[1]));\n                    } else if(IS_F(args[0])) {\n                        wfw_t f = (wfw_t)func;\n                        r = f(F(args[0]), W(args[1]));\n                    } else {\n                        wwf_t f = (wwf_t)func;\n                        r = f(W(args[0]), F(args[1]));\n                    }\n                    break;\n\n                case 3:\n                    if(IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                        wwwf_t f = (wwwf_t)func;\n                        r = f(W(args[0]), W(args[1]), F(args[2]));\n                    } else if(IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                        wwfw_t f = (wwfw_t)func;\n                        r = f(W(args[0]), F(args[1]), W(args[2]));\n                    } else if(IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                        wwff_t f = (wwff_t)func;\n                        r = f(W(args[0]), F(args[1]), F(args[2]));\n                    } else if(IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                        wfww_t f = (wfww_t)func;\n                        r = f(F(args[0]), W(args[1]), W(args[2]));\n                    } else if(IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                        wfwf_t f = (wfwf_t)func;\n                        r = f(F(args[0]), W(args[1]), F(args[2]));\n                    } else if(IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                        wffw_t f = (wffw_t)func;\n                        r = f(F(args[0]), F(args[1]), W(args[2]));\n                    } else if(IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                        wfff_t f = (wfff_t)func;\n                        r = f(F(args[0]), F(args[1]), F(args[2]));\n                    } else {\n                        // The above checks should be exhaustive\n                        abort();\n                    }\n                    break;\n                default:\n                    return -1;\n                }\n            }\n        } else {\n            /* There are some doubles */\n            switch(nargs) {\n            case 0:\n            case 1:\n            case 2:\n                if(IS_D(args[0]) && IS_D(args[1])) {\n                    wdd_t f = (wdd_t)func;\n                    r = f(D(args[0]), D(args[1]));\n                } else if(IS_D(args[0])) {\n                    wdw_t f = (wdw_t)func;\n                    r = f(D(args[0]), W(args[1]));\n                } else {\n                    wwd_t f = (wwd_t)func;\n                    r = f(W(args[0]), D(args[1]));\n                }\n                break;\n\n            case 3:\n                if(IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    wwwd_t f = (wwwd_t)func;\n                    r = f(W(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    wwdw_t f = (wwdw_t)func;\n                    r = f(W(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    wwdd_t f = (wwdd_t)func;\n                    r = f(W(args[0]), D(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                    wdww_t f = (wdww_t)func;\n                    r = f(D(args[0]), W(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    wdwd_t f = (wdwd_t)func;\n                    r = f(D(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    wddw_t f = (wddw_t)func;\n                    r = f(D(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    wddd_t f = (wddd_t)func;\n                    r = f(D(args[0]), D(args[1]), D(args[2]));\n                } else {\n                    // The above checks should be exhaustive\n                    abort();\n                }\n                break;\n            default:\n                return -1;\n            }\n        }\n        res->v.i = (uint64_t)r;\n    } break; /* }}} */\n    case FFI_CTYPE_BOOL: { /* {{{ */\n        ffi_word_t r;\n        if(doubles == 0) {\n            if(floats == 0) {\n                /*\n           * No double and no float args: we currently support up to 6\n           * word-sized arguments\n           */\n                if(nargs <= 4) {\n                    b4w_t f = (b4w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]));\n                } else if(nargs == 5) {\n                    b5w_t f = (b5w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]));\n                } else if(nargs == 6) {\n                    b6w_t f = (b6w_t)func;\n                    r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), W(args[5]));\n                } else {\n                    abort();\n                }\n            } else {\n                /* There are some floats */\n                switch(nargs) {\n                case 0:\n                case 1:\n                case 2:\n                    if(IS_F(args[0]) && IS_F(args[1])) {\n                        bff_t f = (bff_t)func;\n                        r = f(F(args[0]), F(args[1]));\n                    } else if(IS_F(args[0])) {\n                        bfw_t f = (bfw_t)func;\n                        r = f(F(args[0]), W(args[1]));\n                    } else {\n                        bwf_t f = (bwf_t)func;\n                        r = f(W(args[0]), F(args[1]));\n                    }\n                    break;\n\n                case 3:\n                    if(IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                        bwwf_t f = (bwwf_t)func;\n                        r = f(W(args[0]), W(args[1]), F(args[2]));\n                    } else if(IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                        bwfw_t f = (bwfw_t)func;\n                        r = f(W(args[0]), F(args[1]), W(args[2]));\n                    } else if(IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                        bwff_t f = (bwff_t)func;\n                        r = f(W(args[0]), F(args[1]), F(args[2]));\n                    } else if(IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                        bfww_t f = (bfww_t)func;\n                        r = f(F(args[0]), W(args[1]), W(args[2]));\n                    } else if(IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                        bfwf_t f = (bfwf_t)func;\n                        r = f(F(args[0]), W(args[1]), F(args[2]));\n                    } else if(IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                        bffw_t f = (bffw_t)func;\n                        r = f(F(args[0]), F(args[1]), W(args[2]));\n                    } else if(IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                        bfff_t f = (bfff_t)func;\n                        r = f(F(args[0]), F(args[1]), F(args[2]));\n                    } else {\n                        // The above checks should be exhaustive\n                        abort();\n                    }\n                    break;\n                default:\n                    return -1;\n                }\n            }\n        } else {\n            /* There are some doubles */\n            switch(nargs) {\n            case 0:\n            case 1:\n            case 2:\n                if(IS_D(args[0]) && IS_D(args[1])) {\n                    bdd_t f = (bdd_t)func;\n                    r = f(D(args[0]), D(args[1]));\n                } else if(IS_D(args[0])) {\n                    bdw_t f = (bdw_t)func;\n                    r = f(D(args[0]), W(args[1]));\n                } else {\n                    bwd_t f = (bwd_t)func;\n                    r = f(W(args[0]), D(args[1]));\n                }\n                break;\n\n            case 3:\n                if(IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    bwwd_t f = (bwwd_t)func;\n                    r = f(W(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    bwdw_t f = (bwdw_t)func;\n                    r = f(W(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    bwdd_t f = (bwdd_t)func;\n                    r = f(W(args[0]), D(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                    bdww_t f = (bdww_t)func;\n                    r = f(D(args[0]), W(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    bdwd_t f = (bdwd_t)func;\n                    r = f(D(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    bddw_t f = (bddw_t)func;\n                    r = f(D(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    bddd_t f = (bddd_t)func;\n                    r = f(D(args[0]), D(args[1]), D(args[2]));\n                } else {\n                    // The above checks should be exhaustive\n                    abort();\n                }\n                break;\n            default:\n                return -1;\n            }\n        }\n        res->v.i = (uint64_t)r;\n    } break; /* }}} */\n    case FFI_CTYPE_DOUBLE: { /* {{{ */\n        double r;\n        if(doubles == 0) {\n            /* No double args: we currently support up to 6 word-sized arguments\n         */\n            if(nargs <= 4) {\n                d4w_t f = (d4w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]));\n            } else if(nargs == 5) {\n                d5w_t f = (d5w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]));\n            } else if(nargs == 6) {\n                d6w_t f = (d6w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), W(args[5]));\n            } else {\n                abort();\n            }\n        } else {\n            switch(nargs) {\n            case 0:\n            case 1:\n            case 2:\n                if(IS_D(args[0]) && IS_D(args[1])) {\n                    ddd_t f = (ddd_t)func;\n                    r = f(D(args[0]), D(args[1]));\n                } else if(IS_D(args[0])) {\n                    ddw_t f = (ddw_t)func;\n                    r = f(D(args[0]), W(args[1]));\n                } else {\n                    dwd_t f = (dwd_t)func;\n                    r = f(W(args[0]), D(args[1]));\n                }\n                break;\n\n            case 3:\n                if(IS_W(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    dwwd_t f = (dwwd_t)func;\n                    r = f(W(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    dwdw_t f = (dwdw_t)func;\n                    r = f(W(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_W(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    dwdd_t f = (dwdd_t)func;\n                    r = f(W(args[0]), D(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                    ddww_t f = (ddww_t)func;\n                    r = f(D(args[0]), W(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_W(args[1]) && IS_D(args[2])) {\n                    ddwd_t f = (ddwd_t)func;\n                    r = f(D(args[0]), W(args[1]), D(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_W(args[2])) {\n                    dddw_t f = (dddw_t)func;\n                    r = f(D(args[0]), D(args[1]), W(args[2]));\n                } else if(IS_D(args[0]) && IS_D(args[1]) && IS_D(args[2])) {\n                    dddd_t f = (dddd_t)func;\n                    r = f(D(args[0]), D(args[1]), D(args[2]));\n                } else {\n                    // The above checks should be exhaustive\n                    abort();\n                }\n                break;\n            default:\n                return -1;\n            }\n        }\n        res->v.d = r;\n    } break; /* }}} */\n    case FFI_CTYPE_FLOAT: { /* {{{ */\n        double r;\n        if(floats == 0) {\n            /* No float args: we currently support up to 6 word-sized arguments\n         */\n            if(nargs <= 4) {\n                f4w_t f = (f4w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]));\n            } else if(nargs == 5) {\n                f5w_t f = (f5w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]));\n            } else if(nargs == 6) {\n                f6w_t f = (f6w_t)func;\n                r = f(W(args[0]), W(args[1]), W(args[2]), W(args[3]), W(args[4]), W(args[5]));\n            } else {\n                abort();\n            }\n        } else {\n            /* There are some float args */\n            switch(nargs) {\n            case 0:\n            case 1:\n            case 2:\n                if(IS_F(args[0]) && IS_F(args[1])) {\n                    fff_t f = (fff_t)func;\n                    r = f(F(args[0]), F(args[1]));\n                } else if(IS_F(args[0])) {\n                    ffw_t f = (ffw_t)func;\n                    r = f(F(args[0]), W(args[1]));\n                } else {\n                    fwf_t f = (fwf_t)func;\n                    r = f(W(args[0]), F(args[1]));\n                }\n                break;\n\n            case 3:\n                if(IS_W(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                    fwwf_t f = (fwwf_t)func;\n                    r = f(W(args[0]), W(args[1]), F(args[2]));\n                } else if(IS_W(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                    fwfw_t f = (fwfw_t)func;\n                    r = f(W(args[0]), F(args[1]), W(args[2]));\n                } else if(IS_W(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                    fwff_t f = (fwff_t)func;\n                    r = f(W(args[0]), F(args[1]), F(args[2]));\n                } else if(IS_F(args[0]) && IS_W(args[1]) && IS_W(args[2])) {\n                    ffww_t f = (ffww_t)func;\n                    r = f(F(args[0]), W(args[1]), W(args[2]));\n                } else if(IS_F(args[0]) && IS_W(args[1]) && IS_F(args[2])) {\n                    ffwf_t f = (ffwf_t)func;\n                    r = f(F(args[0]), W(args[1]), F(args[2]));\n                } else if(IS_F(args[0]) && IS_F(args[1]) && IS_W(args[2])) {\n                    fffw_t f = (fffw_t)func;\n                    r = f(F(args[0]), F(args[1]), W(args[2]));\n                } else if(IS_F(args[0]) && IS_F(args[1]) && IS_F(args[2])) {\n                    ffff_t f = (ffff_t)func;\n                    r = f(F(args[0]), F(args[1]), F(args[2]));\n                } else {\n                    // The above checks should be exhaustive\n                    abort();\n                }\n                break;\n            default:\n                return -1;\n            }\n        }\n        res->v.f = r;\n    } break; /* }}} */\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "lib/mjs/ffi/ffi.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_FFI_FFI_H_\n#define MJS_FFI_FFI_H_\n\n#include \"../common/platform.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Maximum number of word-sized args to ffi-ed function. If at least one\n * of the args is double, only 2 args are allowed.\n */\n#define FFI_MAX_ARGS_CNT 6\n\ntypedef void(ffi_fn_t)(void);\n\ntypedef intptr_t ffi_word_t;\n\nenum ffi_ctype {\n    FFI_CTYPE_WORD,\n    FFI_CTYPE_BOOL,\n    FFI_CTYPE_FLOAT,\n    FFI_CTYPE_DOUBLE,\n};\n\nstruct ffi_arg {\n    enum ffi_ctype ctype;\n    union {\n        uint64_t i;\n        double d;\n        float f;\n    } v;\n};\n\nint ffi_call_mjs(ffi_fn_t* func, int nargs, struct ffi_arg* res, struct ffi_arg* args);\n\nvoid ffi_set_word(struct ffi_arg* arg, ffi_word_t v);\nvoid ffi_set_bool(struct ffi_arg* arg, bool v);\nvoid ffi_set_ptr(struct ffi_arg* arg, void* v);\nvoid ffi_set_double(struct ffi_arg* arg, double v);\nvoid ffi_set_float(struct ffi_arg* arg, float v);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_FFI_FFI_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_array.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include <stdio.h>\n#include \"common/str_util.h\"\n#include \"mjs_array.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n\n#define SPLICE_NEW_ITEM_IDX 2\n\n/* like c_snprintf but returns `size` if write is truncated */\nstatic int v_sprintf_s(char* buf, size_t size, const char* fmt, ...) {\n    size_t n;\n    va_list ap;\n\n    va_start(ap, fmt);\n    n = c_vsnprintf(buf, size, fmt, ap);\n    va_end(ap);\n\n    if(n > size) {\n        return size;\n    }\n    return n;\n}\n\nmjs_val_t mjs_mk_array(struct mjs* mjs) {\n    mjs_val_t ret = mjs_mk_object(mjs);\n    /* change the tag to MJS_TAG_ARRAY */\n    ret &= ~MJS_TAG_MASK;\n    ret |= MJS_TAG_ARRAY;\n    return ret;\n}\n\nint mjs_is_array(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_ARRAY;\n}\n\nmjs_val_t mjs_array_get(struct mjs* mjs, mjs_val_t arr, unsigned long index) {\n    return mjs_array_get2(mjs, arr, index, NULL);\n}\n\nmjs_val_t mjs_array_get2(struct mjs* mjs, mjs_val_t arr, unsigned long index, int* has) {\n    mjs_val_t res = MJS_UNDEFINED;\n\n    if(has != NULL) {\n        *has = 0;\n    }\n\n    if(mjs_is_object(arr)) {\n        struct mjs_property* p;\n        char buf[20];\n        int n = v_sprintf_s(buf, sizeof(buf), \"%lu\", index);\n        p = mjs_get_own_property(mjs, arr, buf, n);\n        if(p != NULL) {\n            if(has != NULL) {\n                *has = 1;\n            }\n            res = p->value;\n        }\n    }\n\n    return res;\n}\n\nunsigned long mjs_array_length(struct mjs* mjs, mjs_val_t v) {\n    struct mjs_property* p;\n    unsigned long len = 0;\n\n    if(!mjs_is_object(v)) {\n        len = 0;\n        goto clean;\n    }\n\n    for(p = get_object_struct(v)->properties; p != NULL; p = p->next) {\n        int ok = 0;\n        unsigned long n = 0;\n        str_to_ulong(mjs, p->name, &ok, &n);\n        if(ok && n >= len && n < 0xffffffff) {\n            len = n + 1;\n        }\n    }\n\nclean:\n    return len;\n}\n\nmjs_err_t mjs_array_set(struct mjs* mjs, mjs_val_t arr, unsigned long index, mjs_val_t v) {\n    mjs_err_t ret = MJS_OK;\n\n    if(mjs_is_object(arr)) {\n        char buf[20];\n        int n = v_sprintf_s(buf, sizeof(buf), \"%lu\", index);\n        ret = mjs_set(mjs, arr, buf, n, v);\n    } else {\n        ret = MJS_TYPE_ERROR;\n    }\n\n    return ret;\n}\n\nvoid mjs_array_del(struct mjs* mjs, mjs_val_t arr, unsigned long index) {\n    char buf[20];\n    int n = v_sprintf_s(buf, sizeof(buf), \"%lu\", index);\n    mjs_del(mjs, arr, buf, n);\n}\n\nmjs_err_t mjs_array_push(struct mjs* mjs, mjs_val_t arr, mjs_val_t v) {\n    return mjs_array_set(mjs, arr, mjs_array_length(mjs, arr), v);\n}\n\nMJS_PRIVATE void mjs_array_push_internal(struct mjs* mjs) {\n    mjs_err_t rcode = MJS_OK;\n    mjs_val_t ret = MJS_UNDEFINED;\n    int nargs = mjs_nargs(mjs);\n    int i;\n\n    /* Make sure that `this` is an array */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_OBJECT_ARRAY, NULL)) {\n        goto clean;\n    }\n\n    /* Push all args */\n    for(i = 0; i < nargs; i++) {\n        rcode = mjs_array_push(mjs, mjs->vals.this_obj, mjs_arg(mjs, i));\n        if(rcode != MJS_OK) {\n            mjs_prepend_errorf(mjs, rcode, \"\");\n            goto clean;\n        }\n    }\n\n    /* Return the new array length */\n    ret = mjs_mk_number(mjs, mjs_array_length(mjs, mjs->vals.this_obj));\n\nclean:\n    mjs_return(mjs, ret);\n    return;\n}\n\nstatic void move_item(struct mjs* mjs, mjs_val_t arr, unsigned long from, unsigned long to) {\n    mjs_val_t cur = mjs_array_get(mjs, arr, from);\n    mjs_array_set(mjs, arr, to, cur);\n    mjs_array_del(mjs, arr, from);\n}\n\nMJS_PRIVATE void mjs_array_splice(struct mjs* mjs) {\n    int nargs = mjs_nargs(mjs);\n    mjs_err_t rcode = MJS_OK;\n    mjs_val_t ret = mjs_mk_array(mjs);\n    mjs_val_t start_v = MJS_UNDEFINED;\n    mjs_val_t deleteCount_v = MJS_UNDEFINED;\n    int start = 0;\n    int arr_len;\n    int delete_cnt = 0;\n    int new_items_cnt = 0;\n    int delta = 0;\n    int i;\n\n    /* Make sure that `this` is an array */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_OBJECT_ARRAY, NULL)) {\n        goto clean;\n    }\n\n    /* Get array length */\n    arr_len = mjs_array_length(mjs, mjs->vals.this_obj);\n\n    /* get start from arg 0 */\n    if(!mjs_check_arg(mjs, 0, \"start\", MJS_TYPE_NUMBER, &start_v)) {\n        goto clean;\n    }\n    start = mjs_normalize_idx(mjs_get_int(mjs, start_v), arr_len);\n\n    /* Handle deleteCount */\n    if(nargs >= SPLICE_NEW_ITEM_IDX) {\n        /* deleteCount is given; use it */\n        if(!mjs_check_arg(mjs, 1, \"deleteCount\", MJS_TYPE_NUMBER, &deleteCount_v)) {\n            goto clean;\n        }\n        delete_cnt = mjs_get_int(mjs, deleteCount_v);\n        new_items_cnt = nargs - SPLICE_NEW_ITEM_IDX;\n    } else {\n        /* deleteCount is not given; assume the end of the array */\n        delete_cnt = arr_len - start;\n    }\n    if(delete_cnt > arr_len - start) {\n        delete_cnt = arr_len - start;\n    } else if(delete_cnt < 0) {\n        delete_cnt = 0;\n    }\n\n    /* delta at which subsequent array items should be moved */\n    delta = new_items_cnt - delete_cnt;\n\n    /*\n   * copy items which are going to be deleted to the separate array (will be\n   * returned)\n   */\n    for(i = 0; i < delete_cnt; i++) {\n        mjs_val_t cur = mjs_array_get(mjs, mjs->vals.this_obj, start + i);\n        rcode = mjs_array_push(mjs, ret, cur);\n        if(rcode != MJS_OK) {\n            mjs_prepend_errorf(mjs, rcode, \"\");\n            goto clean;\n        }\n    }\n\n    /* If needed, move subsequent items */\n    if(delta < 0) {\n        for(i = start; i < arr_len; i++) {\n            if(i >= start - delta) {\n                move_item(mjs, mjs->vals.this_obj, i, i + delta);\n            } else {\n                mjs_array_del(mjs, mjs->vals.this_obj, i);\n            }\n        }\n    } else if(delta > 0) {\n        for(i = arr_len - 1; i >= start; i--) {\n            move_item(mjs, mjs->vals.this_obj, i, i + delta);\n        }\n    }\n\n    /* Set new items to the array */\n    for(i = 0; i < nargs - SPLICE_NEW_ITEM_IDX; i++) {\n        mjs_array_set(mjs, mjs->vals.this_obj, start + i, mjs_arg(mjs, SPLICE_NEW_ITEM_IDX + i));\n    }\n\nclean:\n    mjs_return(mjs, ret);\n}\n"
  },
  {
    "path": "lib/mjs/mjs_array.h",
    "content": "/*\n * Copyright (c) 2014 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_ARRAY_H_\n#define MJS_ARRAY_H_\n\n#include \"mjs_internal.h\"\n#include \"mjs_array_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nMJS_PRIVATE mjs_val_t mjs_array_get2(struct mjs* mjs, mjs_val_t arr, unsigned long index, int* has);\n\nMJS_PRIVATE void mjs_array_splice(struct mjs* mjs);\n\nMJS_PRIVATE void mjs_array_push_internal(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_ARRAY_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_array_buf.c",
    "content": "#include \"mjs_array_buf.h\"\n#include \"common/cs_varint.h\"\n#include \"common/mg_str.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_object.h\"\n#include \"mjs_array.h\"\n#include \"mjs_util.h\"\n#include \"mjs_exec_public.h\"\n\n#ifndef MJS_ARRAY_BUF_RESERVE\n#define MJS_ARRAY_BUF_RESERVE 100\n#endif\n\n#define IS_SIGNED(type) \\\n    (type == MJS_DATAVIEW_I8 || type == MJS_DATAVIEW_I16 || type == MJS_DATAVIEW_I32)\n\nint mjs_is_array_buf(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_ARRAY_BUF;\n}\n\nint mjs_is_data_view(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_ARRAY_BUF_VIEW;\n}\n\nint mjs_is_typed_array(mjs_val_t v) {\n    return ((v & MJS_TAG_MASK) == MJS_TAG_ARRAY_BUF) ||\n           ((v & MJS_TAG_MASK) == MJS_TAG_ARRAY_BUF_VIEW);\n}\n\nchar* mjs_array_buf_get_ptr(struct mjs* mjs, mjs_val_t buf, size_t* bytelen) {\n    struct mbuf* m = &mjs->array_buffers;\n    size_t offset = buf & ~MJS_TAG_MASK;\n    char* ptr = m->buf + offset;\n\n    uint64_t len = 0;\n    size_t header_len = 0;\n    if(offset < m->len && cs_varint_decode((uint8_t*)ptr, m->len - offset, &len, &header_len)) {\n        if(bytelen) {\n            *bytelen = len;\n        }\n        return ptr + header_len;\n    }\n\n    return NULL;\n}\n\nstatic size_t mjs_dataview_get_element_len(mjs_dataview_type_t type) {\n    size_t len = 1;\n    switch(type) {\n    case MJS_DATAVIEW_U8:\n    case MJS_DATAVIEW_I8:\n        len = 1;\n        break;\n    case MJS_DATAVIEW_U16:\n    case MJS_DATAVIEW_I16:\n        len = 2;\n        break;\n    case MJS_DATAVIEW_U32:\n    case MJS_DATAVIEW_I32:\n        len = 4;\n        break;\n    default:\n        break;\n    }\n    return len;\n}\n\nstatic int64_t get_value(char* buf, mjs_dataview_type_t type) {\n    int64_t value = 0;\n    switch(type) {\n    case MJS_DATAVIEW_U8:\n        value = *(uint8_t*)buf;\n        break;\n    case MJS_DATAVIEW_I8:\n        value = *(int8_t*)buf;\n        break;\n    case MJS_DATAVIEW_U16:\n        value = *(uint16_t*)buf;\n        break;\n    case MJS_DATAVIEW_I16:\n        value = *(int16_t*)buf;\n        break;\n    case MJS_DATAVIEW_U32:\n        value = *(uint32_t*)buf;\n        break;\n    case MJS_DATAVIEW_I32:\n        value = *(int32_t*)buf;\n        break;\n    default:\n        break;\n    }\n    return value;\n}\n\nstatic void set_value(char* buf, int64_t value, mjs_dataview_type_t type) {\n    switch(type) {\n    case MJS_DATAVIEW_U8:\n        *(uint8_t*)buf = (uint8_t)value;\n        break;\n    case MJS_DATAVIEW_I8:\n        *(int8_t*)buf = (int8_t)value;\n        break;\n    case MJS_DATAVIEW_U16:\n        *(uint16_t*)buf = (uint16_t)value;\n        break;\n    case MJS_DATAVIEW_I16:\n        *(int16_t*)buf = (int16_t)value;\n        break;\n    case MJS_DATAVIEW_U32:\n        *(uint32_t*)buf = (uint32_t)value;\n        break;\n    case MJS_DATAVIEW_I32:\n        *(int32_t*)buf = (int32_t)value;\n        break;\n    default:\n        break;\n    }\n}\n\nstatic mjs_val_t mjs_dataview_get(struct mjs* mjs, mjs_val_t obj, size_t index) {\n    mjs_val_t buf_obj = mjs_get(mjs, obj, \"buffer\", -1);\n\n    size_t byte_len = 0;\n    char* buf = mjs_array_buf_get_ptr(mjs, buf_obj, &byte_len);\n    mjs_dataview_type_t type = mjs_get_int(mjs, mjs_get(mjs, obj, \"_t\", -1));\n    if((mjs_dataview_get_element_len(type) * (index + 1)) > byte_len) {\n        return MJS_UNDEFINED;\n    }\n\n    buf += mjs_dataview_get_element_len(type) * index;\n    int64_t value = get_value(buf, type);\n\n    return mjs_mk_number(mjs, value);\n}\n\nstatic mjs_err_t mjs_dataview_set(struct mjs* mjs, mjs_val_t obj, size_t index, int64_t value) {\n    mjs_val_t buf_obj = mjs_get(mjs, obj, \"buffer\", -1);\n\n    size_t byte_len = 0;\n    char* buf = mjs_array_buf_get_ptr(mjs, buf_obj, &byte_len);\n    mjs_dataview_type_t type = mjs_get_int(mjs, mjs_get(mjs, obj, \"_t\", -1));\n    if((mjs_dataview_get_element_len(type) * (index + 1)) > byte_len) {\n        return MJS_TYPE_ERROR;\n    }\n\n    buf += mjs_dataview_get_element_len(type) * index;\n    set_value(buf, value, type);\n\n    return MJS_OK;\n}\n\nmjs_val_t mjs_dataview_get_prop(struct mjs* mjs, mjs_val_t obj, mjs_val_t key) {\n    if(!mjs_is_number(key)) {\n        return MJS_UNDEFINED;\n    }\n    int index = mjs_get_int(mjs, key);\n    return mjs_dataview_get(mjs, obj, index);\n}\n\nmjs_err_t mjs_dataview_set_prop(struct mjs* mjs, mjs_val_t obj, mjs_val_t key, mjs_val_t val) {\n    if(!mjs_is_number(key)) {\n        return MJS_TYPE_ERROR;\n    }\n    int index = mjs_get_int(mjs, key);\n    int64_t value = 0;\n\n    if(mjs_is_number(val)) {\n        value = mjs_get_double(mjs, val);\n    } else if(mjs_is_boolean(val)) {\n        value = mjs_get_bool(mjs, val) ? (1) : (0);\n    }\n    return mjs_dataview_set(mjs, obj, index, value);\n}\n\nmjs_val_t mjs_dataview_get_buf(struct mjs* mjs, mjs_val_t obj) {\n    return mjs_get(mjs, obj, \"buffer\", -1);\n}\n\nmjs_val_t mjs_dataview_get_len(struct mjs* mjs, mjs_val_t obj) {\n    size_t bytelen = 0;\n    mjs_array_buf_get_ptr(mjs, mjs_dataview_get_buf(mjs, obj), &bytelen);\n    mjs_dataview_type_t type = mjs_get_int(mjs, mjs_get(mjs, obj, \"_t\", -1));\n    size_t element_len = mjs_dataview_get_element_len(type);\n\n    return mjs_mk_number(mjs, bytelen / element_len);\n}\n\nmjs_val_t mjs_mk_array_buf(struct mjs* mjs, char* data, size_t buf_len) {\n    struct mbuf* m = &mjs->array_buffers;\n\n    if((m->len + buf_len) > m->size) {\n        char* prev_buf = m->buf;\n        mbuf_resize(m, m->len + buf_len + MJS_ARRAY_BUF_RESERVE);\n\n        if(data >= prev_buf && data < (prev_buf + m->len)) {\n            data += m->buf - prev_buf;\n        }\n    }\n\n    size_t offset = m->len;\n    char* prev_buf = m->buf;\n\n    size_t header_len = cs_varint_llen(buf_len);\n    mbuf_insert(m, offset, NULL, header_len + buf_len);\n    if(data >= prev_buf && data < (prev_buf + m->len)) {\n        data += m->buf - prev_buf;\n    }\n\n    cs_varint_encode(buf_len, (unsigned char*)m->buf + offset, header_len);\n\n    if(data != NULL) {\n        memcpy(m->buf + offset + header_len, data, buf_len);\n    } else {\n        memset(m->buf + offset + header_len, 0, buf_len);\n    }\n\n    return (offset & ~MJS_TAG_MASK) | MJS_TAG_ARRAY_BUF;\n}\n\nvoid mjs_array_buf_slice(struct mjs* mjs) {\n    size_t nargs = mjs_nargs(mjs);\n    mjs_val_t src = mjs_get_this(mjs);\n\n    size_t start = 0;\n    size_t end = 0;\n    char* src_buf = NULL;\n    size_t src_len = 0;\n\n    bool args_correct = false;\n    do {\n        if(!mjs_is_array_buf(src)) {\n            break;\n        }\n        src_buf = mjs_array_buf_get_ptr(mjs, src, &src_len);\n\n        if((nargs == 0) || (nargs > 2)) {\n            break;\n        }\n\n        mjs_val_t start_obj = mjs_arg(mjs, 0);\n        if(!mjs_is_number(start_obj)) {\n            break;\n        }\n        start = mjs_get_int32(mjs, start_obj);\n\n        if(nargs == 2) {\n            mjs_val_t end_obj = mjs_arg(mjs, 1);\n            if(!mjs_is_number(end_obj)) {\n                break;\n            }\n            end = mjs_get_int32(mjs, end_obj);\n        } else {\n            end = src_len - 1;\n        }\n\n        if((start >= src_len) || (end >= src_len) || (start >= end)) {\n            break;\n        }\n\n        args_correct = true;\n    } while(0);\n\n    if(!args_correct) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n        mjs_return(mjs, MJS_UNDEFINED);\n        return;\n    }\n\n    src_buf += start;\n    mjs_return(mjs, mjs_mk_array_buf(mjs, src_buf, end - start));\n}\n\nstatic mjs_val_t\n    mjs_mk_dataview_from_buf(struct mjs* mjs, mjs_val_t buf, mjs_dataview_type_t type) {\n    size_t len = 0;\n    mjs_array_buf_get_ptr(mjs, buf, &len);\n    if(len % mjs_dataview_get_element_len(type) != 0) {\n        mjs_prepend_errorf(\n            mjs, MJS_BAD_ARGS_ERROR, \"Buffer len is not a multiple of element size\");\n        return MJS_UNDEFINED;\n    }\n    mjs_val_t view_obj = mjs_mk_object(mjs);\n    mjs_set(mjs, view_obj, \"_t\", ~0, mjs_mk_number(mjs, (double)type));\n    mjs_set(mjs, view_obj, \"buffer\", ~0, buf);\n\n    view_obj &= ~MJS_TAG_MASK;\n    view_obj |= MJS_TAG_ARRAY_BUF_VIEW;\n\n    mjs_dataview_get(mjs, view_obj, 0);\n\n    return view_obj;\n}\n\nstatic mjs_val_t\n    mjs_mk_dataview(struct mjs* mjs, size_t len, mjs_val_t arr, mjs_dataview_type_t type) {\n    size_t elements_nb = 0;\n    if(mjs_is_array(arr)) {\n        if(!mjs_is_number(mjs_array_get(mjs, arr, 0))) {\n            return MJS_UNDEFINED;\n        }\n        elements_nb = mjs_array_length(mjs, arr);\n    } else {\n        elements_nb = len;\n    }\n\n    size_t element_len = mjs_dataview_get_element_len(type);\n    mjs_val_t buf_obj = mjs_mk_array_buf(mjs, NULL, element_len * elements_nb);\n\n    if(mjs_is_array(arr)) {\n        char* buf_ptr = mjs_array_buf_get_ptr(mjs, buf_obj, NULL);\n        for(uint8_t i = 0; i < elements_nb; i++) {\n            int64_t value = mjs_get_double(mjs, mjs_array_get(mjs, arr, i));\n            set_value(buf_ptr, value, type);\n            buf_ptr += element_len;\n        }\n    }\n\n    return mjs_mk_dataview_from_buf(mjs, buf_obj, type);\n}\n\nstatic void mjs_array_buf_new(struct mjs* mjs) {\n    mjs_val_t len_arg = mjs_arg(mjs, 0);\n    mjs_val_t buf_obj = MJS_UNDEFINED;\n    if(!mjs_is_number(len_arg)) {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n    } else {\n        int len = mjs_get_int(mjs, len_arg);\n        buf_obj = mjs_mk_array_buf(mjs, NULL, len);\n    }\n    mjs_return(mjs, buf_obj);\n}\n\nstatic void mjs_dataview_new(struct mjs* mjs, mjs_dataview_type_t type) {\n    mjs_val_t view_arg = mjs_arg(mjs, 0);\n    mjs_val_t view_obj = MJS_UNDEFINED;\n\n    if(mjs_is_array_buf(view_arg)) { // Create a view of existing ArrayBuf\n        view_obj = mjs_mk_dataview_from_buf(mjs, view_arg, type);\n    } else if(mjs_is_number(view_arg)) { // Create new typed array\n        int len = mjs_get_int(mjs, view_arg);\n        view_obj = mjs_mk_dataview(mjs, len, MJS_UNDEFINED, type);\n    } else if(mjs_is_array(view_arg)) { // Create new typed array from array\n        view_obj = mjs_mk_dataview(mjs, 0, view_arg, type);\n    } else {\n        mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, \"\");\n    }\n\n    mjs_return(mjs, view_obj);\n}\n\nstatic void mjs_new_u8_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_U8);\n}\n\nstatic void mjs_new_i8_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_I8);\n}\n\nstatic void mjs_new_u16_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_U16);\n}\n\nstatic void mjs_new_i16_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_I16);\n}\n\nstatic void mjs_new_u32_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_U32);\n}\n\nstatic void mjs_new_i32_array(struct mjs* mjs) {\n    mjs_dataview_new(mjs, MJS_DATAVIEW_I32);\n}\n\nvoid mjs_init_builtin_array_buf(struct mjs* mjs, mjs_val_t obj) {\n    mjs_set(mjs, obj, \"ArrayBuffer\", ~0, MJS_MK_FN(mjs_array_buf_new));\n    mjs_set(mjs, obj, \"Uint8Array\", ~0, MJS_MK_FN(mjs_new_u8_array));\n    mjs_set(mjs, obj, \"Int8Array\", ~0, MJS_MK_FN(mjs_new_i8_array));\n    mjs_set(mjs, obj, \"Uint16Array\", ~0, MJS_MK_FN(mjs_new_u16_array));\n    mjs_set(mjs, obj, \"Int16Array\", ~0, MJS_MK_FN(mjs_new_i16_array));\n    mjs_set(mjs, obj, \"Uint32Array\", ~0, MJS_MK_FN(mjs_new_u32_array));\n    mjs_set(mjs, obj, \"Int32Array\", ~0, MJS_MK_FN(mjs_new_i32_array));\n}\n"
  },
  {
    "path": "lib/mjs/mjs_array_buf.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#pragma once\n\n#include \"mjs_internal.h\"\n#include \"mjs_array_buf_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nmjs_val_t mjs_dataview_get_prop(struct mjs* mjs, mjs_val_t obj, mjs_val_t key);\n\nmjs_err_t mjs_dataview_set_prop(struct mjs* mjs, mjs_val_t obj, mjs_val_t key, mjs_val_t val);\n\nvoid mjs_init_builtin_array_buf(struct mjs* mjs, mjs_val_t obj);\n\nmjs_val_t mjs_dataview_get_len(struct mjs* mjs, mjs_val_t obj);\n\nvoid mjs_array_buf_slice(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n"
  },
  {
    "path": "lib/mjs/mjs_array_buf_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#pragma once\n\n#include \"mjs_core_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\ntypedef enum {\n    MJS_DATAVIEW_U8,\n    MJS_DATAVIEW_I8,\n    MJS_DATAVIEW_U16,\n    MJS_DATAVIEW_I16,\n    MJS_DATAVIEW_U32,\n    MJS_DATAVIEW_I32,\n} mjs_dataview_type_t;\n\nint mjs_is_array_buf(mjs_val_t v);\n\nint mjs_is_data_view(mjs_val_t v);\n\nint mjs_is_typed_array(mjs_val_t v);\n\nmjs_val_t mjs_mk_array_buf(struct mjs* mjs, char* data, size_t buf_len);\n\nchar* mjs_array_buf_get_ptr(struct mjs* mjs, mjs_val_t buf, size_t* bytelen);\n\nmjs_val_t mjs_dataview_get_buf(struct mjs* mjs, mjs_val_t obj);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n"
  },
  {
    "path": "lib/mjs/mjs_array_public.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n/*\n * === Arrays\n */\n\n#ifndef MJS_ARRAY_PUBLIC_H_\n#define MJS_ARRAY_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/* Make an empty array object */\nmjs_val_t mjs_mk_array(struct mjs* mjs);\n\n/* Returns length on an array. If `arr` is not an array, 0 is returned. */\nunsigned long mjs_array_length(struct mjs* mjs, mjs_val_t arr);\n\n/* Insert value `v` in array `arr` at the end of the array. */\nmjs_err_t mjs_array_push(struct mjs* mjs, mjs_val_t arr, mjs_val_t v);\n\n/*\n * Return array member at index `index`. If `index` is out of bounds, undefined\n * is returned.\n */\nmjs_val_t mjs_array_get(struct mjs*, mjs_val_t arr, unsigned long index);\n\n/* Insert value `v` into `arr` at index `index`. */\nmjs_err_t mjs_array_set(struct mjs* mjs, mjs_val_t arr, unsigned long index, mjs_val_t v);\n\n/* Returns true if the given value is an array */\nint mjs_is_array(mjs_val_t v);\n\n/* Delete value in array `arr` at index `index`, if it exists. */\nvoid mjs_array_del(struct mjs* mjs, mjs_val_t arr, unsigned long index);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_ARRAY_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_bcode.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/cs_varint.h\"\n\n#include \"mjs_internal.h\"\n#include \"mjs_bcode.h\"\n#include \"mjs_core.h\"\n#include \"mjs_tok.h\"\n\nstatic void add_lineno_map_item(struct pstate* pstate) {\n    if(pstate->last_emitted_line_no < pstate->line_no) {\n        int offset = pstate->cur_idx - pstate->start_bcode_idx;\n        size_t offset_llen = cs_varint_llen(offset);\n        size_t lineno_llen = cs_varint_llen(pstate->line_no);\n        mbuf_resize(\n            &pstate->offset_lineno_map,\n            pstate->offset_lineno_map.size + offset_llen + lineno_llen);\n\n        /* put offset */\n        cs_varint_encode(\n            offset,\n            (uint8_t*)pstate->offset_lineno_map.buf + pstate->offset_lineno_map.len,\n            offset_llen);\n        pstate->offset_lineno_map.len += offset_llen;\n\n        /* put line_no */\n        cs_varint_encode(\n            pstate->line_no,\n            (uint8_t*)pstate->offset_lineno_map.buf + pstate->offset_lineno_map.len,\n            lineno_llen);\n        pstate->offset_lineno_map.len += lineno_llen;\n\n        pstate->last_emitted_line_no = pstate->line_no;\n    }\n}\n\nMJS_PRIVATE void emit_byte(struct pstate* pstate, uint8_t byte) {\n    add_lineno_map_item(pstate);\n    mbuf_insert(&pstate->mjs->bcode_gen, pstate->cur_idx, &byte, sizeof(byte));\n    pstate->cur_idx += sizeof(byte);\n}\n\nMJS_PRIVATE void emit_int(struct pstate* pstate, int64_t n) {\n    struct mbuf* b = &pstate->mjs->bcode_gen;\n    size_t llen = cs_varint_llen(n);\n    add_lineno_map_item(pstate);\n    mbuf_insert(b, pstate->cur_idx, NULL, llen);\n    cs_varint_encode(n, (uint8_t*)b->buf + pstate->cur_idx, llen);\n    pstate->cur_idx += llen;\n}\n\nMJS_PRIVATE void emit_str(struct pstate* pstate, const char* ptr, size_t len) {\n    struct mbuf* b = &pstate->mjs->bcode_gen;\n    size_t llen = cs_varint_llen(len);\n    add_lineno_map_item(pstate);\n    mbuf_insert(b, pstate->cur_idx, NULL, llen + len);\n    cs_varint_encode(len, (uint8_t*)b->buf + pstate->cur_idx, llen);\n    memcpy(b->buf + pstate->cur_idx + llen, ptr, len);\n    pstate->cur_idx += llen + len;\n}\n\nMJS_PRIVATE int\n    mjs_bcode_insert_offset(struct pstate* p, struct mjs* mjs, size_t offset, size_t v) {\n    int llen = (int)cs_varint_llen(v);\n    int diff = llen - MJS_INIT_OFFSET_SIZE;\n    assert(offset < mjs->bcode_gen.len);\n    if(diff > 0) {\n        mbuf_resize(&mjs->bcode_gen, mjs->bcode_gen.size + diff);\n    }\n    /*\n   * Offset is going to take more than one was reserved, so, move the data\n   * forward\n   */\n    memmove(\n        mjs->bcode_gen.buf + offset + llen,\n        mjs->bcode_gen.buf + offset + MJS_INIT_OFFSET_SIZE,\n        mjs->bcode_gen.len - offset - MJS_INIT_OFFSET_SIZE);\n    mjs->bcode_gen.len += diff;\n    cs_varint_encode(v, (uint8_t*)mjs->bcode_gen.buf + offset, llen);\n\n    /*\n   * If current parsing index is after the offset at which we've inserted new\n   * varint, the index might need to be adjusted\n   */\n    if(p->cur_idx >= (int)offset) {\n        p->cur_idx += diff;\n    }\n    return diff;\n}\n\nMJS_PRIVATE void mjs_bcode_part_add(struct mjs* mjs, const struct mjs_bcode_part* bp) {\n    mbuf_append(&mjs->bcode_parts, bp, sizeof(*bp));\n}\n\nMJS_PRIVATE struct mjs_bcode_part* mjs_bcode_part_get(struct mjs* mjs, int num) {\n    assert(num < mjs_bcode_parts_cnt(mjs));\n    return (struct mjs_bcode_part*)(mjs->bcode_parts.buf + num * sizeof(struct mjs_bcode_part));\n}\n\nMJS_PRIVATE struct mjs_bcode_part* mjs_bcode_part_get_by_offset(struct mjs* mjs, size_t offset) {\n    int i;\n    int parts_cnt = mjs_bcode_parts_cnt(mjs);\n    struct mjs_bcode_part* bp = NULL;\n\n    if(offset >= mjs->bcode_len) {\n        return NULL;\n    }\n\n    for(i = 0; i < parts_cnt; i++) {\n        bp = mjs_bcode_part_get(mjs, i);\n        if(offset < bp->start_idx + bp->data.len) {\n            break;\n        }\n    }\n\n    /* given the non-corrupted data, the needed part must be found */\n    assert(i < parts_cnt);\n\n    return bp;\n}\n\nMJS_PRIVATE int mjs_bcode_parts_cnt(struct mjs* mjs) {\n    return mjs->bcode_parts.len / sizeof(struct mjs_bcode_part);\n}\n\nMJS_PRIVATE void mjs_bcode_commit(struct mjs* mjs) {\n    struct mjs_bcode_part bp;\n    memset(&bp, 0, sizeof(bp));\n\n    /* Make sure the bcode doesn't occupy any extra space */\n    mbuf_trim(&mjs->bcode_gen);\n\n    /* Transfer the ownership of the bcode data */\n    bp.data.p = mjs->bcode_gen.buf;\n    bp.data.len = mjs->bcode_gen.len;\n    mbuf_init(&mjs->bcode_gen, 0);\n\n    bp.start_idx = mjs->bcode_len;\n    bp.exec_res = MJS_ERRS_CNT;\n\n    mjs_bcode_part_add(mjs, &bp);\n\n    mjs->bcode_len += bp.data.len;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_bcode.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_BCODE_H_\n#define MJS_BCODE_H_\n\n#include \"mjs_internal.h\"\n\n#include \"mjs_core.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nenum mjs_opcode {\n    OP_NOP, /* ( -- ) */\n    OP_DROP, /* ( a -- ) */\n    OP_DUP, /* ( a -- a a ) */\n    OP_SWAP, /* ( a b -- b a ) */\n    OP_JMP, /* ( -- ) */\n    OP_JMP_TRUE, /* ( -- ) */\n    OP_JMP_NEUTRAL_TRUE, /* ( -- ) */\n    OP_JMP_FALSE, /* ( -- ) */\n    OP_JMP_NEUTRAL_FALSE, /* ( -- ) */\n    OP_FIND_SCOPE, /* ( a -- a b ) */\n    OP_PUSH_SCOPE, /* ( -- a ) */\n    OP_PUSH_STR, /* ( -- a ) */\n    OP_PUSH_TRUE, /* ( -- a ) */\n    OP_PUSH_FALSE, /* ( -- a ) */\n    OP_PUSH_INT, /* ( -- a ) */\n    OP_PUSH_DBL, /* ( -- a ) */\n    OP_PUSH_NULL, /* ( -- a ) */\n    OP_PUSH_UNDEF, /* ( -- a ) */\n    OP_PUSH_OBJ, /* ( -- a ) */\n    OP_PUSH_ARRAY, /* ( -- a ) */\n    OP_PUSH_FUNC, /* ( -- a ) */\n    OP_PUSH_THIS, /* ( -- a ) */\n    OP_GET, /* ( key obj  -- obj[key] ) */\n    OP_CREATE, /* ( key obj -- ) */\n    OP_EXPR, /* ( ... -- a ) */\n    OP_APPEND, /* ( a b -- ) */\n    OP_SET_ARG, /* ( a -- a ) */\n    OP_NEW_SCOPE, /* ( -- ) */\n    OP_DEL_SCOPE, /* ( -- ) */\n    OP_CALL, /* ( func param1 param2 ... num_params -- result ) */\n    OP_RETURN, /* ( -- ) */\n    OP_LOOP, /* ( -- ) Push break & continue addresses to loop_labels */\n    OP_BREAK, /* ( -- ) */\n    OP_CONTINUE, /* ( -- ) */\n    OP_SETRETVAL, /* ( a -- ) */\n    OP_EXIT, /* ( -- ) */\n    OP_BCODE_HEADER, /* ( -- ) */\n    OP_ARGS, /* ( -- ) Mark the beginning of function call arguments */\n    OP_FOR_IN_NEXT, /* ( name obj iter_ptr -- name obj iter_ptr_next ) */\n    OP_MAX\n};\n\nstruct pstate;\nstruct mjs;\n\nMJS_PRIVATE void emit_byte(struct pstate* pstate, uint8_t byte);\nMJS_PRIVATE void emit_int(struct pstate* pstate, int64_t n);\nMJS_PRIVATE void emit_str(struct pstate* pstate, const char* ptr, size_t len);\n\n/*\n * Inserts provided offset `v` at the offset `offset`.\n *\n * Returns delta at which the code was moved; the delta can be any: 0 or\n * positive or negative.\n */\nMJS_PRIVATE int\n    mjs_bcode_insert_offset(struct pstate* p, struct mjs* mjs, size_t offset, size_t v);\n\n/*\n * Adds a new bcode part; does not retain `bp`.\n */\nMJS_PRIVATE void mjs_bcode_part_add(struct mjs* mjs, const struct mjs_bcode_part* bp);\n\n/*\n * Returns bcode part by the bcode number\n */\nMJS_PRIVATE struct mjs_bcode_part* mjs_bcode_part_get(struct mjs* mjs, int num);\n\n/*\n * Returns bcode part by the global bcode offset\n */\nMJS_PRIVATE struct mjs_bcode_part* mjs_bcode_part_get_by_offset(struct mjs* mjs, size_t offset);\n\n/*\n * Returns a number of bcode parts\n */\nMJS_PRIVATE int mjs_bcode_parts_cnt(struct mjs* mjs);\n\n/*\n * Adds the bcode being generated (mjs->bcode_gen) as a next bcode part\n */\nMJS_PRIVATE void mjs_bcode_commit(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_BCODE_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_builtin.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"mjs_bcode.h\"\n#include \"mjs_core.h\"\n#include \"mjs_dataview.h\"\n#include \"mjs_exec.h\"\n#include \"mjs_gc.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_json.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n#include \"mjs_array_buf.h\"\n\n/*\n * If the file with the given filename was already loaded, returns the\n * corresponding bcode part; otherwise returns NULL.\n */\nstatic struct mjs_bcode_part* mjs_get_loaded_file_bcode(struct mjs* mjs, const char* filename) {\n    int parts_cnt = mjs_bcode_parts_cnt(mjs);\n    int i;\n\n    if(filename == NULL) {\n        return 0;\n    }\n\n    for(i = 0; i < parts_cnt; i++) {\n        struct mjs_bcode_part* bp = mjs_bcode_part_get(mjs, i);\n        const char* cur_fn = mjs_get_bcode_filename(mjs, bp);\n        if(strcmp(filename, cur_fn) == 0) {\n            return bp;\n        }\n    }\n    return NULL;\n}\n\nstatic void mjs_load(struct mjs* mjs) {\n    mjs_val_t res = MJS_UNDEFINED;\n    mjs_val_t arg0 = mjs_arg(mjs, 0);\n    mjs_val_t arg1 = mjs_arg(mjs, 1);\n    int custom_global = 0; /* whether the custom global object was provided */\n\n    if(mjs_is_string(arg0)) {\n        const char* path = mjs_get_cstring(mjs, &arg0);\n        struct mjs_bcode_part* bp = NULL;\n        mjs_err_t ret;\n\n        if(mjs_is_object(arg1)) {\n            custom_global = 1;\n            push_mjs_val(&mjs->scopes, arg1);\n        }\n        bp = mjs_get_loaded_file_bcode(mjs, path);\n        if(bp == NULL) {\n            /* File was not loaded before, so, load */\n            ret = mjs_exec_file(mjs, path, &res);\n        } else {\n            /*\n       * File was already loaded before, so if it was evaluated successfully,\n       * then skip the evaluation at all (and assume MJS_OK); otherwise\n       * re-evaluate it again.\n       *\n       * However, if the custom global object was provided, then reevaluate\n       * the file in any case.\n       */\n            if(bp->exec_res != MJS_OK || custom_global) {\n                ret = mjs_execute(mjs, bp->start_idx, &res);\n            } else {\n                ret = MJS_OK;\n            }\n        }\n        if(ret != MJS_OK) {\n            /*\n       * arg0 and path might be invalidated by executing a file, so refresh\n       * them\n       */\n            arg0 = mjs_arg(mjs, 0);\n            path = mjs_get_cstring(mjs, &arg0);\n            mjs_prepend_errorf(mjs, ret, \"failed to exec file \\\"%s\\\"\", path);\n            goto clean;\n        }\n\n    clean:\n        if(custom_global) {\n            mjs_pop_val(&mjs->scopes);\n        }\n    }\n    mjs_return(mjs, res);\n}\n\nstatic void mjs_get_mjs(struct mjs* mjs) {\n    mjs_return(mjs, mjs_mk_foreign(mjs, mjs));\n}\n\nstatic void mjs_chr(struct mjs* mjs) {\n    mjs_val_t arg0 = mjs_arg(mjs, 0), res = MJS_NULL;\n    int n = mjs_get_int(mjs, arg0);\n    if(mjs_is_number(arg0) && n >= 0 && n <= 255) {\n        uint8_t s = n;\n        res = mjs_mk_string(mjs, (const char*)&s, sizeof(s), 1);\n    }\n    mjs_return(mjs, res);\n}\n\nstatic void mjs_do_gc(struct mjs* mjs) {\n    mjs_val_t arg0 = mjs_arg(mjs, 0);\n    mjs_gc(mjs, mjs_is_boolean(arg0) ? mjs_get_bool(mjs, arg0) : 0);\n    mjs_return(mjs, arg0);\n}\n\nstatic void mjs_s2o(struct mjs* mjs) {\n    mjs_return(\n        mjs,\n        mjs_struct_to_obj(\n            mjs,\n            mjs_get_ptr(mjs, mjs_arg(mjs, 0)),\n            (const struct mjs_c_struct_member*)mjs_get_ptr(mjs, mjs_arg(mjs, 1))));\n}\n\nvoid mjs_init_builtin(struct mjs* mjs, mjs_val_t obj) {\n    mjs_val_t v;\n\n    mjs_set(mjs, obj, \"global\", ~0, obj);\n\n    mjs_set(mjs, obj, \"load\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_load));\n    mjs_set(mjs, obj, \"ffi\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_ffi_call));\n    mjs_set(\n        mjs, obj, \"ffi_cb_free\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_ffi_cb_free));\n    mjs_set(mjs, obj, \"mkstr\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_mkstr));\n    mjs_set(mjs, obj, \"getMJS\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_get_mjs));\n    mjs_set(mjs, obj, \"die\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_die));\n    mjs_set(mjs, obj, \"gc\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_do_gc));\n    mjs_set(mjs, obj, \"chr\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_chr));\n    mjs_set(mjs, obj, \"s2o\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_s2o));\n\n    /*\n    * Populate JSON.parse() and JSON.stringify()\n    */\n    // v = mjs_mk_object(mjs);\n    // mjs_set(\n    //     mjs, v, \"stringify\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_op_json_stringify));\n    // mjs_set(mjs, v, \"parse\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_op_json_parse));\n    // mjs_set(mjs, obj, \"JSON\", ~0, v);\n\n    /*\n    * Populate Object\n    */\n    v = mjs_mk_object(mjs);\n    mjs_set(mjs, v, \"create\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_op_create_object));\n    mjs_set(\n        mjs,\n        v,\n        \"defineProperty\",\n        ~0,\n        mjs_mk_foreign_func(\n            mjs, (mjs_func_ptr_t)mjs_op_object_define_property)); // stub, do not use\n    mjs_set(mjs, obj, \"Object\", ~0, v);\n\n    /*\n    * Populate numeric stuff\n    */\n    mjs_set(mjs, obj, \"NaN\", ~0, MJS_TAG_NAN);\n    mjs_set(mjs, obj, \"isNaN\", ~0, mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_op_isnan));\n\n    mjs_init_builtin_array_buf(mjs, obj);\n}\n"
  },
  {
    "path": "lib/mjs/mjs_builtin.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_BUILTIN_H_\n#define MJS_BUILTIN_H_\n\n#include \"mjs_core_public.h\"\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nvoid mjs_init_builtin(struct mjs* mjs, mjs_val_t obj);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_BUILTIN_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_core.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/cs_varint.h\"\n#include \"common/str_util.h\"\n\n#include \"mjs_bcode.h\"\n#include \"mjs_builtin.h\"\n#include \"mjs_core.h\"\n#include \"mjs_exec.h\"\n#include \"mjs_ffi.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n\n#ifndef MJS_OBJECT_ARENA_SIZE\n#define MJS_OBJECT_ARENA_SIZE 20\n#endif\n#ifndef MJS_PROPERTY_ARENA_SIZE\n#define MJS_PROPERTY_ARENA_SIZE 20\n#endif\n#ifndef MJS_FUNC_FFI_ARENA_SIZE\n#define MJS_FUNC_FFI_ARENA_SIZE 20\n#endif\n\n#ifndef MJS_OBJECT_ARENA_INC_SIZE\n#define MJS_OBJECT_ARENA_INC_SIZE 10\n#endif\n#ifndef MJS_PROPERTY_ARENA_INC_SIZE\n#define MJS_PROPERTY_ARENA_INC_SIZE 10\n#endif\n#ifndef MJS_FUNC_FFI_ARENA_INC_SIZE\n#define MJS_FUNC_FFI_ARENA_INC_SIZE 10\n#endif\n\nvoid mjs_destroy(struct mjs* mjs) {\n    {\n        int parts_cnt = mjs_bcode_parts_cnt(mjs);\n        int i;\n        for(i = 0; i < parts_cnt; i++) {\n            struct mjs_bcode_part* bp = mjs_bcode_part_get(mjs, i);\n            if(!bp->in_rom) {\n                free((void*)bp->data.p);\n            }\n        }\n    }\n\n    mbuf_free(&mjs->bcode_gen);\n    mbuf_free(&mjs->bcode_parts);\n    mbuf_free(&mjs->stack);\n    mbuf_free(&mjs->call_stack);\n    mbuf_free(&mjs->arg_stack);\n    mbuf_free(&mjs->owned_strings);\n    mbuf_free(&mjs->foreign_strings);\n    mbuf_free(&mjs->owned_values);\n    mbuf_free(&mjs->scopes);\n    mbuf_free(&mjs->loop_addresses);\n    mbuf_free(&mjs->json_visited_stack);\n    mbuf_free(&mjs->array_buffers);\n    free(mjs->error_msg);\n    free(mjs->stack_trace);\n    mjs_ffi_args_free_list(mjs);\n    gc_arena_destroy(mjs, &mjs->object_arena);\n    gc_arena_destroy(mjs, &mjs->property_arena);\n    gc_arena_destroy(mjs, &mjs->ffi_sig_arena);\n    free(mjs);\n}\n\nstruct mjs* mjs_create(void* context) {\n    mjs_val_t global_object;\n    struct mjs* mjs = calloc(1, sizeof(*mjs));\n    mjs->context = context;\n    mbuf_init(&mjs->stack, 0);\n    mbuf_init(&mjs->call_stack, 0);\n    mbuf_init(&mjs->arg_stack, 0);\n    mbuf_init(&mjs->owned_strings, 0);\n    mbuf_init(&mjs->foreign_strings, 0);\n    mbuf_init(&mjs->bcode_gen, 0);\n    mbuf_init(&mjs->bcode_parts, 0);\n    mbuf_init(&mjs->owned_values, 0);\n    mbuf_init(&mjs->scopes, 0);\n    mbuf_init(&mjs->loop_addresses, 0);\n    mbuf_init(&mjs->json_visited_stack, 0);\n    mbuf_init(&mjs->array_buffers, 0);\n\n    mjs->bcode_len = 0;\n\n    /*\n   * The compacting GC exploits the null terminator of the previous string as a\n   * marker.\n   */\n    {\n        char z = 0;\n        mbuf_append(&mjs->owned_strings, &z, 1);\n    }\n\n    gc_arena_init(\n        &mjs->object_arena,\n        sizeof(struct mjs_object),\n        MJS_OBJECT_ARENA_SIZE,\n        MJS_OBJECT_ARENA_INC_SIZE);\n    mjs->object_arena.destructor = mjs_obj_destructor;\n    gc_arena_init(\n        &mjs->property_arena,\n        sizeof(struct mjs_property),\n        MJS_PROPERTY_ARENA_SIZE,\n        MJS_PROPERTY_ARENA_INC_SIZE);\n    gc_arena_init(\n        &mjs->ffi_sig_arena,\n        sizeof(struct mjs_ffi_sig),\n        MJS_FUNC_FFI_ARENA_SIZE,\n        MJS_FUNC_FFI_ARENA_INC_SIZE);\n    mjs->ffi_sig_arena.destructor = mjs_ffi_sig_destructor;\n\n    global_object = mjs_mk_object(mjs);\n    mjs_init_builtin(mjs, global_object);\n    mjs_set_ffi_resolver(mjs, NULL, NULL);\n    push_mjs_val(&mjs->scopes, global_object);\n    mjs->vals.this_obj = MJS_UNDEFINED;\n    mjs->vals.dataview_proto = MJS_UNDEFINED;\n\n    return mjs;\n}\n\nmjs_err_t mjs_set_errorf(struct mjs* mjs, mjs_err_t err, const char* fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    free(mjs->error_msg);\n    mjs->error_msg = NULL;\n    mjs->error = err;\n    if(fmt != NULL) {\n        mg_avprintf(&mjs->error_msg, 0, fmt, ap);\n    }\n    va_end(ap);\n    return err;\n}\n\nvoid mjs_exit(struct mjs* mjs) {\n    free(mjs->error_msg);\n    mjs->error_msg = NULL;\n    mjs->error = MJS_NEED_EXIT;\n}\n\nvoid mjs_set_exec_flags_poller(struct mjs* mjs, mjs_flags_poller_t poller) {\n    mjs->exec_flags_poller = poller;\n}\n\nvoid* mjs_get_context(struct mjs* mjs) {\n    return mjs->context;\n}\n\nmjs_err_t mjs_prepend_errorf(struct mjs* mjs, mjs_err_t err, const char* fmt, ...) {\n    char* old_error_msg = mjs->error_msg;\n    char* new_error_msg = NULL;\n    va_list ap;\n    va_start(ap, fmt);\n\n    /* err should never be MJS_OK here */\n    assert(err != MJS_OK);\n\n    mjs->error_msg = NULL;\n    /* set error if only it wasn't already set to some error */\n    if(mjs->error == MJS_OK) {\n        mjs->error = err;\n    }\n    mg_avprintf(&new_error_msg, 0, fmt, ap);\n    va_end(ap);\n\n    if(old_error_msg != NULL) {\n        mg_asprintf(&mjs->error_msg, 0, \"%s: %s\", new_error_msg, old_error_msg);\n        free(new_error_msg);\n        free(old_error_msg);\n    } else {\n        mjs->error_msg = new_error_msg;\n    }\n    return err;\n}\n\nvoid mjs_print_error(struct mjs* mjs, FILE* fp, const char* msg, int print_stack_trace) {\n    (void)fp;\n\n    if(print_stack_trace && mjs->stack_trace != NULL) {\n        // fprintf(fp, \"%s\", mjs->stack_trace);\n    }\n\n    if(msg == NULL) {\n        msg = \"MJS error\";\n    }\n\n    // fprintf(fp, \"%s: %s\\n\", msg, mjs_strerror(mjs, mjs->error));\n}\n\nMJS_PRIVATE void mjs_die(struct mjs* mjs) {\n    mjs_val_t msg_v = MJS_UNDEFINED;\n    const char* msg = NULL;\n    size_t msg_len = 0;\n\n    /* get idx from arg 0 */\n    if(!mjs_check_arg(mjs, 0, \"msg\", MJS_TYPE_STRING, &msg_v)) {\n        goto clean;\n    }\n\n    msg = mjs_get_string(mjs, &msg_v, &msg_len);\n\n    /* TODO(dfrank): take error type as an argument */\n    mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"%.*s\", (int)msg_len, msg);\n\nclean:\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nconst char* mjs_strerror(struct mjs* mjs, enum mjs_err err) {\n    const char* err_names[] = {\n        \"NO_ERROR\",\n        \"SYNTAX_ERROR\",\n        \"REFERENCE_ERROR\",\n        \"TYPE_ERROR\",\n        \"OUT_OF_MEMORY\",\n        \"INTERNAL_ERROR\",\n        \"NOT_IMPLEMENTED\",\n        \"FILE_OPEN_ERROR\",\n        \"BAD_ARGUMENTS\"};\n    return mjs->error_msg == NULL || mjs->error_msg[0] == '\\0' ? err_names[err] : mjs->error_msg;\n}\n\nconst char* mjs_get_stack_trace(struct mjs* mjs) {\n    return mjs->stack_trace;\n}\n\nMJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v) {\n    return v & ~MJS_TAG_MASK;\n}\n\nMJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v) {\n    int tag;\n    if(mjs_is_number(v)) {\n        return MJS_TYPE_NUMBER;\n    }\n    tag = (v & MJS_TAG_MASK) >> 48;\n    switch(tag) {\n    case MJS_TAG_FOREIGN >> 48:\n        return MJS_TYPE_FOREIGN;\n    case MJS_TAG_UNDEFINED >> 48:\n        return MJS_TYPE_UNDEFINED;\n    case MJS_TAG_OBJECT >> 48:\n        return MJS_TYPE_OBJECT_GENERIC;\n    case MJS_TAG_ARRAY >> 48:\n        return MJS_TYPE_OBJECT_ARRAY;\n    case MJS_TAG_FUNCTION >> 48:\n        return MJS_TYPE_OBJECT_FUNCTION;\n    case MJS_TAG_STRING_I >> 48:\n    case MJS_TAG_STRING_O >> 48:\n    case MJS_TAG_STRING_F >> 48:\n    case MJS_TAG_STRING_D >> 48:\n    case MJS_TAG_STRING_5 >> 48:\n        return MJS_TYPE_STRING;\n    case MJS_TAG_BOOLEAN >> 48:\n        return MJS_TYPE_BOOLEAN;\n    case MJS_TAG_NULL >> 48:\n        return MJS_TYPE_NULL;\n    case MJS_TAG_ARRAY_BUF >> 48:\n        return MJS_TYPE_ARRAY_BUF;\n    case MJS_TAG_ARRAY_BUF_VIEW >> 48:\n        return MJS_TYPE_ARRAY_BUF_VIEW;\n    default:\n        abort();\n        return MJS_TYPE_UNDEFINED;\n    }\n}\n\nmjs_val_t mjs_get_global(struct mjs* mjs) {\n    return *vptr(&mjs->scopes, 0);\n}\n\nstatic void mjs_append_stack_trace_line(struct mjs* mjs, size_t offset) {\n    if(offset != MJS_BCODE_OFFSET_EXIT) {\n        const char* filename = mjs_get_bcode_filename_by_offset(mjs, offset);\n        int line_no = mjs_get_lineno_by_offset(mjs, offset);\n        char* new_line = NULL;\n        const char* fmt = \"\\tat %s:%d\\r\\n\";\n        if(filename == NULL) {\n            // fprintf(\n            //     stderr,\n            //     \"ERROR during stack trace generation: wrong bcode offset %d\\n\",\n            //     (int)offset);\n            filename = \"<unknown-filename>\";\n        }\n        mg_asprintf(&new_line, 0, fmt, filename, line_no);\n\n        if(mjs->stack_trace != NULL) {\n            char* old = mjs->stack_trace;\n            mg_asprintf(&mjs->stack_trace, 0, \"%s%s\", mjs->stack_trace, new_line);\n            free(old);\n            free(new_line);\n        } else {\n            mjs->stack_trace = new_line;\n        }\n    }\n}\n\nMJS_PRIVATE void mjs_gen_stack_trace(struct mjs* mjs, size_t offset) {\n    mjs_append_stack_trace_line(mjs, offset);\n    while(mjs->call_stack.len >= sizeof(mjs_val_t) * CALL_STACK_FRAME_ITEMS_CNT) {\n        int i;\n\n        /* set current offset to it to the offset stored in the frame */\n        offset = mjs_get_int(mjs, *vptr(&mjs->call_stack, -1 - CALL_STACK_FRAME_ITEM_RETURN_ADDR));\n\n        /* pop frame from the call stack */\n        for(i = 0; i < CALL_STACK_FRAME_ITEMS_CNT; i++) {\n            mjs_pop_val(&mjs->call_stack);\n        }\n\n        mjs_append_stack_trace_line(mjs, offset);\n    }\n}\n\nvoid mjs_own(struct mjs* mjs, mjs_val_t* v) {\n    mbuf_append(&mjs->owned_values, &v, sizeof(v));\n}\n\nint mjs_disown(struct mjs* mjs, mjs_val_t* v) {\n    mjs_val_t** vp = (mjs_val_t**)(mjs->owned_values.buf + mjs->owned_values.len - sizeof(v));\n\n    for(; (char*)vp >= mjs->owned_values.buf; vp--) {\n        if(*vp == v) {\n            *vp = *(mjs_val_t**)(mjs->owned_values.buf + mjs->owned_values.len - sizeof(v));\n            mjs->owned_values.len -= sizeof(v);\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\n/*\n * Returns position in the data stack at which the called function is located,\n * and which should be later replaced with the returned value.\n */\nMJS_PRIVATE int mjs_getretvalpos(struct mjs* mjs) {\n    int pos;\n    mjs_val_t* ppos = vptr(&mjs->call_stack, -1);\n    // LOG(LL_INFO, (\"ppos: %p %d\", ppos, mjs_stack_size(&mjs->call_stack)));\n    assert(ppos != NULL && mjs_is_number(*ppos));\n    pos = mjs_get_int(mjs, *ppos) - 1;\n    assert(pos < (int)mjs_stack_size(&mjs->stack));\n    return pos;\n}\n\nint mjs_nargs(struct mjs* mjs) {\n    int top = mjs_stack_size(&mjs->stack);\n    int pos = mjs_getretvalpos(mjs) + 1;\n    // LOG(LL_INFO, (\"top: %d pos: %d\", top, pos));\n    return pos > 0 && pos < top ? top - pos : 0;\n}\n\nmjs_val_t mjs_arg(struct mjs* mjs, int arg_index) {\n    mjs_val_t res = MJS_UNDEFINED;\n    int top = mjs_stack_size(&mjs->stack);\n    int pos = mjs_getretvalpos(mjs) + 1;\n    // LOG(LL_INFO, (\"idx %d pos: %d\", arg_index, pos));\n    if(pos > 0 && pos + arg_index < top) {\n        res = *vptr(&mjs->stack, pos + arg_index);\n    }\n    return res;\n}\n\nvoid mjs_return(struct mjs* mjs, mjs_val_t v) {\n    int pos = mjs_getretvalpos(mjs);\n    // LOG(LL_INFO, (\"pos: %d\", pos));\n    mjs->stack.len = sizeof(mjs_val_t) * pos;\n    mjs_push(mjs, v);\n}\n\nMJS_PRIVATE mjs_val_t vtop(struct mbuf* m) {\n    size_t size = mjs_stack_size(m);\n    return size > 0 ? *vptr(m, size - 1) : MJS_UNDEFINED;\n}\n\nMJS_PRIVATE size_t mjs_stack_size(const struct mbuf* m) {\n    return m->len / sizeof(mjs_val_t);\n}\n\nMJS_PRIVATE mjs_val_t* vptr(struct mbuf* m, int idx) {\n    int size = mjs_stack_size(m);\n    if(idx < 0) idx = size + idx;\n    return idx >= 0 && idx < size ? &((mjs_val_t*)m->buf)[idx] : NULL;\n}\n\nMJS_PRIVATE mjs_val_t mjs_pop(struct mjs* mjs) {\n    if(mjs->stack.len == 0) {\n        mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, \"stack underflow\");\n        return MJS_UNDEFINED;\n    } else {\n        return mjs_pop_val(&mjs->stack);\n    }\n}\n\nMJS_PRIVATE void push_mjs_val(struct mbuf* m, mjs_val_t v) {\n    mbuf_append(m, &v, sizeof(v));\n}\n\nMJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf* m) {\n    mjs_val_t v = MJS_UNDEFINED;\n    assert(m->len >= sizeof(v));\n    if(m->len >= sizeof(v)) {\n        memcpy(&v, m->buf + m->len - sizeof(v), sizeof(v));\n        m->len -= sizeof(v);\n    }\n    return v;\n}\n\nMJS_PRIVATE void mjs_push(struct mjs* mjs, mjs_val_t v) {\n    push_mjs_val(&mjs->stack, v);\n}\n\nvoid mjs_set_generate_jsc(struct mjs* mjs, int generate_jsc) {\n    mjs->generate_jsc = generate_jsc;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_core.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_CORE_H\n#define MJS_CORE_H\n\n#include \"mjs_ffi.h\"\n#include \"mjs_gc.h\"\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n#define JUMP_INSTRUCTION_SIZE 2\n\nenum mjs_call_stack_frame_item {\n    CALL_STACK_FRAME_ITEM_RETVAL_STACK_IDX, /* TOS */\n    CALL_STACK_FRAME_ITEM_LOOP_ADDR_IDX,\n    CALL_STACK_FRAME_ITEM_SCOPE_IDX,\n    CALL_STACK_FRAME_ITEM_RETURN_ADDR,\n    CALL_STACK_FRAME_ITEM_THIS,\n\n    CALL_STACK_FRAME_ITEMS_CNT\n};\n\nstruct mjs_vals {\n    /* Current `this` value  */\n    mjs_val_t this_obj;\n    mjs_val_t dataview_proto;\n\n    /*\n   * The object against which the last `OP_GET` was invoked. Needed for\n   * \"method invocation pattern\".\n   */\n    mjs_val_t last_getprop_obj;\n};\n\nstruct mjs_bcode_part {\n    /* Global index of the bcode part */\n    size_t start_idx;\n\n    /* Actual bcode data */\n    struct {\n        const char* p; /* Memory chunk pointer */\n        size_t len; /* Memory chunk length */\n    } data;\n\n    /*\n   * Result of evaluation (not parsing: if there is an error during parsing,\n   * the bcode is not even committed). It is used to determine whether we\n   * need to evaluate the file: if file was already evaluated, and the result\n   * was MJS_OK, then we won't evaluate it again. Otherwise, we will.\n   */\n    mjs_err_t exec_res : 4;\n\n    /* If set, bcode data does not need to be freed */\n    unsigned in_rom : 1;\n};\n\nstruct mjs {\n    struct mbuf bcode_gen;\n    struct mbuf bcode_parts;\n    size_t bcode_len;\n    struct mbuf stack;\n    struct mbuf call_stack;\n    struct mbuf arg_stack;\n    struct mbuf scopes; /* Scope objects */\n    struct mbuf loop_addresses; /* Addresses for breaks & continues */\n    struct mbuf owned_strings; /* Sequence of (varint len, char data[]) */\n    struct mbuf foreign_strings; /* Sequence of (varint len, char *data) */\n    struct mbuf owned_values;\n    struct mbuf json_visited_stack;\n    struct mbuf array_buffers;\n    struct mjs_vals vals;\n    char* error_msg;\n    char* stack_trace;\n    enum mjs_err error;\n    mjs_ffi_resolver_t* dlsym; /* Symbol resolver function for FFI */\n    void* dlsym_handle;\n    ffi_cb_args_t* ffi_cb_args; /* List of FFI args descriptors */\n    size_t cur_bcode_offset;\n    mjs_flags_poller_t exec_flags_poller;\n    void* context;\n\n    struct gc_arena object_arena;\n    struct gc_arena property_arena;\n    struct gc_arena ffi_sig_arena;\n\n    unsigned inhibit_gc : 1;\n    unsigned need_gc : 1;\n    unsigned generate_jsc : 1;\n};\n\n/*\n * Bcode header: type of the items, and item numbers.\n */\ntypedef uint32_t mjs_header_item_t;\nenum mjs_header_items {\n    MJS_HDR_ITEM_TOTAL_SIZE, /* Total size of the bcode (not counting the\n                                OP_BCODE_HEADER byte) */\n    MJS_HDR_ITEM_BCODE_OFFSET, /* Offset to the start of the actual bcode (not\n                                counting the OP_BCODE_HEADER byte) */\n    MJS_HDR_ITEM_MAP_OFFSET, /* Offset to the start of offset-to-line_no mapping\n                                k*/\n\n    MJS_HDR_ITEMS_CNT\n};\n\nMJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v);\n\nMJS_PRIVATE int mjs_getretvalpos(struct mjs* mjs);\n\nMJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v);\n\n/*\n * Prints stack trace starting from the given bcode offset; other offsets\n * (if any) will be fetched from the call_stack.\n */\nMJS_PRIVATE void mjs_gen_stack_trace(struct mjs* mjs, size_t offset);\n\nMJS_PRIVATE mjs_val_t vtop(struct mbuf* m);\nMJS_PRIVATE size_t mjs_stack_size(const struct mbuf* m);\nMJS_PRIVATE mjs_val_t* vptr(struct mbuf* m, int idx);\nMJS_PRIVATE void push_mjs_val(struct mbuf* m, mjs_val_t v);\nMJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf* m);\nMJS_PRIVATE mjs_val_t mjs_pop(struct mjs* mjs);\nMJS_PRIVATE void mjs_push(struct mjs* mjs, mjs_val_t v);\nMJS_PRIVATE void mjs_die(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_CORE_H */\n"
  },
  {
    "path": "lib/mjs/mjs_core_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_CORE_PUBLIC_H_\n#define MJS_CORE_PUBLIC_H_\n\n#if !defined(_MSC_VER) || _MSC_VER >= 1700\n#include <stdint.h>\n#else\ntypedef unsigned __int64 uint64_t;\ntypedef int int32_t;\ntypedef unsigned char uint8_t;\n#endif\n#include <stdio.h>\n#include <stddef.h>\n#include \"mjs_features.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n#ifndef MJS_ENABLE_DEBUG\n#define MJS_ENABLE_DEBUG 0\n#endif\n\n/*\n *  Double-precision floating-point number, IEEE 754\n *\n *  64 bit (8 bytes) in total\n *  1  bit sign\n *  11 bits exponent\n *  52 bits mantissa\n *      7         6        5        4        3        2        1        0\n *  seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm\n *\n * If an exponent is all-1 and mantissa is all-0, then it is an INFINITY:\n *  11111111|11110000|00000000|00000000|00000000|00000000|00000000|00000000\n *\n * If an exponent is all-1 and mantissa's MSB is 1, it is a quiet NaN:\n *  11111111|11111000|00000000|00000000|00000000|00000000|00000000|00000000\n *\n *  MJS NaN-packing:\n *    sign and exponent is 0xfff\n *    4 bits specify type (tag), must be non-zero\n *    48 bits specify value\n *\n *  11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv\n *   NaN marker |type|  48-bit placeholder for values: pointers, strings\n *\n * On 64-bit platforms, pointers are really 48 bit only, so they can fit,\n * provided they are sign extended\n */\n\ntypedef uint64_t mjs_val_t;\n\n/*\n * A tag is made of the sign bit and the 4 lower order bits of byte 6.\n * So in total we have 32 possible tags.\n *\n * Tag (1,0) however cannot hold a zero payload otherwise it's interpreted as an\n * INFINITY; for simplicity we're just not going to use that combination.\n */\n#define MAKE_TAG(s, t) ((uint64_t)(s) << 63 | (uint64_t)0x7ff0 << 48 | (uint64_t)(t) << 48)\n\n#define MJS_TAG_OBJECT MAKE_TAG(1, 1)\n#define MJS_TAG_FOREIGN MAKE_TAG(1, 2)\n#define MJS_TAG_UNDEFINED MAKE_TAG(1, 3)\n#define MJS_TAG_BOOLEAN MAKE_TAG(1, 4)\n#define MJS_TAG_NAN MAKE_TAG(1, 5)\n#define MJS_TAG_STRING_I MAKE_TAG(1, 6) /* Inlined string len < 5 */\n#define MJS_TAG_STRING_5 MAKE_TAG(1, 7) /* Inlined string len 5 */\n#define MJS_TAG_STRING_O MAKE_TAG(1, 8) /* Owned string */\n#define MJS_TAG_STRING_F MAKE_TAG(1, 9) /* Foreign string */\n#define MJS_TAG_STRING_C MAKE_TAG(1, 10) /* String chunk */\n#define MJS_TAG_STRING_D MAKE_TAG(1, 11) /* Dictionary string  */\n#define MJS_TAG_ARRAY MAKE_TAG(1, 12)\n#define MJS_TAG_FUNCTION MAKE_TAG(1, 13)\n#define MJS_TAG_FUNCTION_FFI MAKE_TAG(1, 14)\n#define MJS_TAG_NULL MAKE_TAG(1, 15)\n\n#define MJS_TAG_ARRAY_BUF MAKE_TAG(0, 1) /* ArrayBuffer */\n#define MJS_TAG_ARRAY_BUF_VIEW MAKE_TAG(0, 2) /* DataView */\n\n#define MJS_TAG_MASK MAKE_TAG(1, 15)\n\n/* This if-0 is a dirty workaround to force etags to pick `struct mjs` */\n#if 0\n/* Opaque structure. MJS engine context. */\nstruct mjs {\n  /* ... */\n};\n#endif\n\nstruct mjs;\n\nenum mjs_type {\n    /* Primitive types */\n    MJS_TYPE_UNDEFINED,\n    MJS_TYPE_NULL,\n    MJS_TYPE_BOOLEAN,\n    MJS_TYPE_NUMBER,\n    MJS_TYPE_STRING,\n    MJS_TYPE_FOREIGN,\n    MJS_TYPE_ARRAY_BUF,\n    MJS_TYPE_ARRAY_BUF_VIEW,\n\n    /* Different classes of Object type */\n    MJS_TYPE_OBJECT_GENERIC,\n    MJS_TYPE_OBJECT_ARRAY,\n    MJS_TYPE_OBJECT_FUNCTION,\n    /*\n   * TODO(dfrank): if we support prototypes, need to add items for them here\n   */\n\n    MJS_TYPES_CNT\n};\n\ntypedef enum mjs_err {\n    MJS_OK,\n    MJS_SYNTAX_ERROR,\n    MJS_REFERENCE_ERROR,\n    MJS_TYPE_ERROR,\n    MJS_OUT_OF_MEMORY,\n    MJS_INTERNAL_ERROR,\n    MJS_NOT_IMPLEMENTED_ERROR,\n    MJS_FILE_READ_ERROR,\n    MJS_BAD_ARGS_ERROR,\n\n    MJS_NEED_EXIT,\n\n    MJS_ERRS_CNT\n} mjs_err_t;\n\ntypedef void (*mjs_flags_poller_t)(struct mjs* mjs);\n\nstruct mjs;\n\n/* Create MJS instance */\nstruct mjs* mjs_create(void* context);\n\n/* Destroy MJS instance */\nvoid mjs_destroy(struct mjs* mjs);\n\nmjs_val_t mjs_get_global(struct mjs* mjs);\n\n/*\n * Tells the GC about an MJS value variable/field owned by C code.\n *\n * The user's C code should own mjs_val_t variables if the value's lifetime\n * crosses any invocation of `mjs_exec()` and friends, including `mjs_call()`.\n *\n * The registration of the variable prevents the GC from mistakenly treat the\n * object as garbage.\n *\n * User code should also explicitly disown the variables with `mjs_disown()`\n * once it goes out of scope or the structure containing the mjs_val_t field is\n * freed.\n *\n * Consider the following examples:\n *\n * Correct (owning is not necessary):\n * ```c\n * mjs_val_t res;\n * mjs_exec(mjs, \"....some script\", &res);\n * // ... use res somehow\n *\n * mjs_val_t res;\n * mjs_exec(mjs, \"....some script2\", &res);\n * // ... use new res somehow\n * ```\n *\n * WRONG:\n * ```c\n * mjs_val_t res1;\n * mjs_exec(mjs, \"....some script\", &res1);\n *\n * mjs_val_t res2;\n * mjs_exec(mjs, \"....some script2\", &res2);\n *\n * // ... use res1 (WRONG!) and res2\n * ```\n *\n * The code above is wrong, because after the second invocation of\n * `mjs_exec()`, the value of `res1` is invalidated.\n *\n * Correct (res1 is owned)\n * ```c\n * mjs_val_t res1 = MJS_UNDEFINED;\n * mjs_own(mjs, &res1);\n * mjs_exec(mjs, \"....some script\", &res1);\n *\n * mjs_val_t res2 = MJS_UNDEFINED;\n * mjs_exec(mjs, \"....some script2\", &res2);\n *\n * // ... use res1 and res2\n * mjs_disown(mjs, &res1);\n * ```\n *\n * NOTE that we explicly initialized `res1` to a valid value before owning it\n * (in this case, the value is `MJS_UNDEFINED`). Owning an uninitialized\n * variable is an undefined behaviour.\n *\n * Of course, it's not an error to own a variable even if it's not mandatory:\n * e.g. in the last example we could own both `res1` and `res2`. Probably it\n * would help us in the future, when we refactor the code so that `res2` has to\n * be owned, and we could forget to do that.\n *\n * Also, if the user code has some C function called from MJS, and in this C\n * function some MJS value (`mjs_val_t`) needs to be stored somewhere and to\n * stay alive after the C function has returned, it also needs to be properly\n * owned.\n */\nvoid mjs_own(struct mjs* mjs, mjs_val_t* v);\n\n/*\n * Disowns the value previously owned by `mjs_own()`.\n *\n * Returns 1 if value is found, 0 otherwise.\n */\nint mjs_disown(struct mjs* mjs, mjs_val_t* v);\n\nmjs_err_t mjs_set_errorf(struct mjs* mjs, mjs_err_t err, const char* fmt, ...);\n\nvoid mjs_exit(struct mjs* mjs);\n\nvoid mjs_set_exec_flags_poller(struct mjs* mjs, mjs_flags_poller_t poller);\n\nvoid* mjs_get_context(struct mjs* mjs);\n\n/*\n * If there is no error message already set, then it's equal to\n * `mjs_set_errorf()`.\n *\n * Otherwise, an old message gets prepended with the new one, followed by a\n * colon. (the previously set error code is kept)\n */\nmjs_err_t mjs_prepend_errorf(struct mjs* mjs, mjs_err_t err, const char* fmt, ...);\n\n/*\n * Print the last error details. If print_stack_trace is non-zero, also\n * print stack trace. `msg` is the message which gets prepended to the actual\n * error message, if it's NULL, then \"MJS error\" is used.\n */\nvoid mjs_print_error(struct mjs* mjs, FILE* fp, const char* msg, int print_stack_trace);\n\n/*\n * return a string representation of an error.\n * the error string might be overwritten by calls to `mjs_set_errorf`.\n */\nconst char* mjs_strerror(struct mjs* mjs, enum mjs_err err);\n\nconst char* mjs_get_stack_trace(struct mjs* mjs);\n\n/*\n * Sets whether *.jsc files are generated when *.js file is executed. By\n * default it's 0.\n *\n * If either `MJS_GENERATE_JSC` or `CS_MMAP` is off, then this function has no\n * effect.\n */\nvoid mjs_set_generate_jsc(struct mjs* mjs, int generate_jsc);\n\n/*\n * When invoked from a cfunction, returns number of arguments passed to the\n * current JS function call.\n */\nint mjs_nargs(struct mjs* mjs);\n\n/*\n * When invoked from a cfunction, returns n-th argument to the current JS\n * function call.\n */\nmjs_val_t mjs_arg(struct mjs* mjs, int n);\n\n/*\n * Sets return value for the current JS function call.\n */\nvoid mjs_return(struct mjs* mjs, mjs_val_t v);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_CORE_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_dataview.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"mjs_exec_public.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_util.h\"\n\nvoid* mjs_mem_to_ptr(unsigned val) {\n    return (void*)(uintptr_t)val;\n}\n\nvoid* mjs_mem_get_ptr(void* base, int offset) {\n    return (char*)base + offset;\n}\n\nvoid mjs_mem_set_ptr(void* ptr, void* val) {\n    *(void**)ptr = val;\n}\n\ndouble mjs_mem_get_dbl(void* ptr) {\n    double v;\n    memcpy(&v, ptr, sizeof(v));\n    return v;\n}\n\nvoid mjs_mem_set_dbl(void* ptr, double val) {\n    memcpy(ptr, &val, sizeof(val));\n}\n\n/*\n * TODO(dfrank): add support for unsigned ints to ffi and use\n * unsigned int here\n */\ndouble mjs_mem_get_uint(void* ptr, int size, int bigendian) {\n    uint8_t* p = (uint8_t*)ptr;\n    int i, inc = bigendian ? 1 : -1;\n    unsigned int res = 0;\n    p += bigendian ? 0 : size - 1;\n    for(i = 0; i < size; i++, p += inc) {\n        res <<= 8;\n        res |= *p;\n    }\n    return res;\n}\n\n/*\n * TODO(dfrank): add support for unsigned ints to ffi and use\n * unsigned int here\n */\ndouble mjs_mem_get_int(void* ptr, int size, int bigendian) {\n    uint8_t* p = (uint8_t*)ptr;\n    int i, inc = bigendian ? 1 : -1;\n    int res = 0;\n    p += bigendian ? 0 : size - 1;\n\n    for(i = 0; i < size; i++, p += inc) {\n        res <<= 8;\n        res |= *p;\n    }\n\n    /* sign-extend */\n    {\n        int extra = sizeof(res) - size;\n        for(i = 0; i < extra; i++) res <<= 8;\n        for(i = 0; i < extra; i++) res >>= 8;\n    }\n\n    return res;\n}\n\nvoid mjs_mem_set_uint(void* ptr, unsigned int val, int size, int bigendian) {\n    uint8_t* p = (uint8_t*)ptr + (bigendian ? size - 1 : 0);\n    int i, inc = bigendian ? -1 : 1;\n    for(i = 0; i < size; i++, p += inc) {\n        *p = val & 0xff;\n        val >>= 8;\n    }\n}\n\nvoid mjs_mem_set_int(void* ptr, int val, int size, int bigendian) {\n    mjs_mem_set_uint(ptr, val, size, bigendian);\n}\n"
  },
  {
    "path": "lib/mjs/mjs_dataview.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_DATAVIEW_H_\n#define MJS_DATAVIEW_H_\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Functions for memory introspection.\n * These are supposed to be FFI-ed and used from the JS environment.\n */\n\nvoid* mjs_mem_to_ptr(unsigned int val);\nvoid* mjs_mem_get_ptr(void* base, int offset);\nvoid mjs_mem_set_ptr(void* ptr, void* val);\ndouble mjs_mem_get_dbl(void* ptr);\nvoid mjs_mem_set_dbl(void* ptr, double val);\ndouble mjs_mem_get_uint(void* ptr, int size, int bigendian);\ndouble mjs_mem_get_int(void* ptr, int size, int bigendian);\nvoid mjs_mem_set_uint(void* ptr, unsigned int val, int size, int bigendian);\nvoid mjs_mem_set_int(void* ptr, int val, int size, int bigendian);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_DATAVIEW_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_exec.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/cs_file.h\"\n#include \"common/cs_varint.h\"\n\n#include \"mjs_array.h\"\n#include \"mjs_bcode.h\"\n#include \"mjs_core.h\"\n#include \"mjs_exec.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_parser.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_tok.h\"\n#include \"mjs_util.h\"\n#include \"mjs_array_buf.h\"\n\n#if MJS_GENERATE_JSC && defined(CS_MMAP)\n#include <sys/mman.h>\n#endif\n\n/*\n * Pushes call stack frame. Offset is a global bcode offset. Retval_stack_idx\n * is an index in mjs->stack at which return value should be written later.\n */\nstatic void call_stack_push_frame(struct mjs* mjs, size_t offset, mjs_val_t retval_stack_idx) {\n    /* Pop `this` value, and apply it */\n    mjs_val_t this_obj = mjs_pop_val(&mjs->arg_stack);\n\n    /*\n   * NOTE: the layout is described by enum mjs_call_stack_frame_item\n   */\n    push_mjs_val(&mjs->call_stack, mjs->vals.this_obj);\n    mjs->vals.this_obj = this_obj;\n\n    push_mjs_val(&mjs->call_stack, mjs_mk_number(mjs, (double)offset));\n    push_mjs_val(&mjs->call_stack, mjs_mk_number(mjs, (double)mjs_stack_size(&mjs->scopes)));\n    push_mjs_val(\n        &mjs->call_stack, mjs_mk_number(mjs, (double)mjs_stack_size(&mjs->loop_addresses)));\n    push_mjs_val(&mjs->call_stack, retval_stack_idx);\n}\n\n/*\n * Restores call stack frame. Returns the return address.\n */\nstatic size_t call_stack_restore_frame(struct mjs* mjs) {\n    size_t retval_stack_idx, return_address, scope_index, loop_addr_index;\n    assert(mjs_stack_size(&mjs->call_stack) >= CALL_STACK_FRAME_ITEMS_CNT);\n\n    /*\n   * NOTE: the layout is described by enum mjs_call_stack_frame_item\n   */\n    retval_stack_idx = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack));\n    loop_addr_index = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack));\n    scope_index = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack));\n    return_address = mjs_get_int(mjs, mjs_pop_val(&mjs->call_stack));\n    mjs->vals.this_obj = mjs_pop_val(&mjs->call_stack);\n\n    /* Remove created scopes */\n    while(mjs_stack_size(&mjs->scopes) > scope_index) {\n        mjs_pop_val(&mjs->scopes);\n    }\n\n    /* Remove loop addresses */\n    while(mjs_stack_size(&mjs->loop_addresses) > loop_addr_index) {\n        mjs_pop_val(&mjs->loop_addresses);\n    }\n\n    /* Shrink stack, leave return value on top */\n    mjs->stack.len = retval_stack_idx * sizeof(mjs_val_t);\n\n    /* Jump to the return address */\n    return return_address;\n}\n\nstatic mjs_val_t mjs_find_scope(struct mjs* mjs, mjs_val_t key) {\n    size_t num_scopes = mjs_stack_size(&mjs->scopes);\n    while(num_scopes > 0) {\n        mjs_val_t scope = *vptr(&mjs->scopes, num_scopes - 1);\n        num_scopes--;\n        if(mjs_get_own_property_v(mjs, scope, key) != NULL) return scope;\n    }\n    mjs_set_errorf(mjs, MJS_REFERENCE_ERROR, \"[%s] is not defined\", mjs_get_cstring(mjs, &key));\n    return MJS_UNDEFINED;\n}\n\nmjs_val_t mjs_get_this(struct mjs* mjs) {\n    return mjs->vals.this_obj;\n}\n\nstatic double do_arith_op(double da, double db, int op, bool* resnan) {\n    *resnan = false;\n\n    if(isnan(da) || isnan(db)) {\n        *resnan = true;\n        return 0;\n    }\n    /* clang-format off */\n  switch (op) {\n    case TOK_MINUS:   return da - db;\n    case TOK_PLUS:    return da + db;\n    case TOK_MUL:     return da * db;\n    case TOK_DIV:\n      if (db != 0) {\n        return da / db;\n      } else {\n        /* TODO(dfrank): add support for Infinity and return it here */\n        *resnan = true;\n        return 0;\n      }\n    case TOK_REM:\n      /*\n       * TODO(dfrank): probably support remainder operation as it is in JS\n       * (which works with non-integer divisor).\n       */\n      db = (int) db;\n      if (db != 0) {\n        bool neg = false;\n        if (da < 0) {\n          neg = true;\n          da = -da;\n        }\n        if (db < 0) {\n          db = -db;\n        }\n        da = (double) ((int64_t) da % (int64_t) db);\n        if (neg) {\n          da = -da;\n        }\n        return da;\n      } else {\n        *resnan = true;\n        return 0;\n      }\n    case TOK_AND:     return (double) ((int64_t) da & (int64_t) db);\n    case TOK_OR:      return (double) ((int64_t) da | (int64_t) db);\n    case TOK_XOR:     return (double) ((int64_t) da ^ (int64_t) db);\n    case TOK_LSHIFT:  return (double) ((int64_t) da << (int64_t) db);\n    case TOK_RSHIFT:  return (double) ((int64_t) da >> (int64_t) db);\n    case TOK_URSHIFT: return (double) ((uint32_t) da >> (uint32_t) db);\n  }\n    /* clang-format on */\n    *resnan = true;\n    return 0;\n}\n\nstatic void set_no_autoconversion_error(struct mjs* mjs) {\n    mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"implicit type conversion is prohibited\");\n}\n\nstatic mjs_val_t do_op(struct mjs* mjs, mjs_val_t a, mjs_val_t b, int op) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    bool resnan = false;\n    if((mjs_is_foreign(a) || mjs_is_number(a)) && (mjs_is_foreign(b) || mjs_is_number(b))) {\n        int is_result_ptr = 0;\n        double da, db, result;\n\n        if(mjs_is_foreign(a) && mjs_is_foreign(b)) {\n            /* When two operands are pointers, only subtraction is supported */\n            if(op != TOK_MINUS) {\n                mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"invalid operands\");\n            }\n        } else if(mjs_is_foreign(a) || mjs_is_foreign(b)) {\n            /*\n       * When one of the operands is a pointer, only + and - are supported,\n       * and the result is a pointer.\n       */\n            if(op != TOK_MINUS && op != TOK_PLUS) {\n                mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"invalid operands\");\n            }\n            is_result_ptr = 1;\n        }\n        da = mjs_is_number(a) ? mjs_get_double(mjs, a) : (double)(uintptr_t)mjs_get_ptr(mjs, a);\n        db = mjs_is_number(b) ? mjs_get_double(mjs, b) : (double)(uintptr_t)mjs_get_ptr(mjs, b);\n        result = do_arith_op(da, db, op, &resnan);\n        if(resnan) {\n            ret = MJS_TAG_NAN;\n        } else {\n            /*\n       * If at least one of the operands was a pointer, result should also be\n       * a pointer\n       */\n            ret = is_result_ptr ? mjs_mk_foreign(mjs, (void*)(uintptr_t)result) :\n                                  mjs_mk_number(mjs, result);\n        }\n    } else if(mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) {\n        ret = s_concat(mjs, a, b);\n    } else {\n        set_no_autoconversion_error(mjs);\n    }\n    return ret;\n}\n\nstatic void op_assign(struct mjs* mjs, int op) {\n    mjs_val_t val = mjs_pop(mjs);\n    mjs_val_t obj = mjs_pop(mjs);\n    mjs_val_t key = mjs_pop(mjs);\n    if(mjs_is_object(obj) && mjs_is_string(key)) {\n        mjs_val_t v = mjs_get_v(mjs, obj, key);\n        mjs_set_v(mjs, obj, key, do_op(mjs, v, val, op));\n        mjs_push(mjs, v);\n    } else {\n        mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"invalid operand\");\n    }\n}\n\nstatic int check_equal(struct mjs* mjs, mjs_val_t a, mjs_val_t b) {\n    int ret = 0;\n    if(a == MJS_TAG_NAN && b == MJS_TAG_NAN) {\n        ret = 0;\n    } else if(a == b) {\n        ret = 1;\n    } else if(mjs_is_number(a) && mjs_is_number(b)) {\n        /*\n     * The case of equal numbers is handled above, so here the result is always\n     * false\n     */\n        ret = 0;\n    } else if(mjs_is_string(a) && mjs_is_string(b)) {\n        ret = s_cmp(mjs, a, b) == 0;\n    } else if(mjs_is_foreign(a) && b == MJS_NULL) {\n        ret = mjs_get_ptr(mjs, a) == NULL;\n    } else if(a == MJS_NULL && mjs_is_foreign(b)) {\n        ret = mjs_get_ptr(mjs, b) == NULL;\n    } else {\n        ret = 0;\n    }\n    return ret;\n}\n\nstatic void exec_expr(struct mjs* mjs, int op) {\n    switch(op) {\n    case TOK_DOT:\n        break;\n    case TOK_MINUS:\n    case TOK_PLUS:\n    case TOK_MUL:\n    case TOK_DIV:\n    case TOK_REM:\n    case TOK_XOR:\n    case TOK_AND:\n    case TOK_OR:\n    case TOK_LSHIFT:\n    case TOK_RSHIFT:\n    case TOK_URSHIFT: {\n        mjs_val_t b = mjs_pop(mjs);\n        mjs_val_t a = mjs_pop(mjs);\n        mjs_push(mjs, do_op(mjs, a, b, op));\n        break;\n    }\n    case TOK_UNARY_MINUS: {\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_number(mjs, -a));\n        break;\n    }\n    case TOK_NOT: {\n        mjs_val_t val = mjs_pop(mjs);\n        mjs_push(mjs, mjs_mk_boolean(mjs, !mjs_is_truthy(mjs, val)));\n        break;\n    }\n    case TOK_TILDA: {\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_number(mjs, (double)(~(int64_t)a)));\n        break;\n    }\n    case TOK_UNARY_PLUS:\n        break;\n    case TOK_EQ:\n        mjs_set_errorf(mjs, MJS_NOT_IMPLEMENTED_ERROR, \"Use ===, not ==\");\n        break;\n    case TOK_NE:\n        mjs_set_errorf(mjs, MJS_NOT_IMPLEMENTED_ERROR, \"Use !==, not !=\");\n        break;\n    case TOK_EQ_EQ: {\n        mjs_val_t a = mjs_pop(mjs);\n        mjs_val_t b = mjs_pop(mjs);\n        mjs_push(mjs, mjs_mk_boolean(mjs, check_equal(mjs, a, b)));\n        break;\n    }\n    case TOK_NE_NE: {\n        mjs_val_t a = mjs_pop(mjs);\n        mjs_val_t b = mjs_pop(mjs);\n        mjs_push(mjs, mjs_mk_boolean(mjs, !check_equal(mjs, a, b)));\n        break;\n    }\n    case TOK_LT: {\n        double b = mjs_get_double(mjs, mjs_pop(mjs));\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_boolean(mjs, a < b));\n        break;\n    }\n    case TOK_GT: {\n        double b = mjs_get_double(mjs, mjs_pop(mjs));\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_boolean(mjs, a > b));\n        break;\n    }\n    case TOK_LE: {\n        double b = mjs_get_double(mjs, mjs_pop(mjs));\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_boolean(mjs, a <= b));\n        break;\n    }\n    case TOK_GE: {\n        double b = mjs_get_double(mjs, mjs_pop(mjs));\n        double a = mjs_get_double(mjs, mjs_pop(mjs));\n        mjs_push(mjs, mjs_mk_boolean(mjs, a >= b));\n        break;\n    }\n    case TOK_ASSIGN: {\n        mjs_val_t val = mjs_pop(mjs);\n        mjs_val_t obj = mjs_pop(mjs);\n        mjs_val_t key = mjs_pop(mjs);\n        if(mjs_is_object(obj)) {\n            mjs_set_v(mjs, obj, key, val);\n        } else if(mjs_is_data_view(obj)) {\n            mjs_err_t err = mjs_dataview_set_prop(mjs, obj, key, val);\n            if(err != MJS_OK) {\n                mjs_prepend_errorf(mjs, err, \"\");\n            }\n        } else if(mjs_is_foreign(obj)) {\n            /*\n         * We don't have setters, so in order to support properties which behave\n         * like setters, we have to parse key right here, instead of having real\n         * built-in prototype objects\n         */\n\n            int ikey = mjs_get_int(mjs, key);\n            int ival = mjs_get_int(mjs, val);\n\n            if(!mjs_is_number(key)) {\n                mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"index must be a number\");\n                val = MJS_UNDEFINED;\n            } else if(!mjs_is_number(val) || ival < 0 || ival > 0xff) {\n                mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"only number 0 .. 255 can be assigned\");\n                val = MJS_UNDEFINED;\n            } else {\n                uint8_t* ptr = (uint8_t*)mjs_get_ptr(mjs, obj);\n                *(ptr + ikey) = (uint8_t)ival;\n            }\n        } else {\n            mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"unsupported object type\");\n        }\n        mjs_push(mjs, val);\n        break;\n    }\n    case TOK_POSTFIX_PLUS: {\n        mjs_val_t obj = mjs_pop(mjs);\n        mjs_val_t key = mjs_pop(mjs);\n        if(mjs_is_object(obj) && mjs_is_string(key)) {\n            mjs_val_t v = mjs_get_v(mjs, obj, key);\n            mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);\n            mjs_set_v(mjs, obj, key, v1);\n            mjs_push(mjs, v);\n        } else {\n            mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"invalid operand for ++\");\n        }\n        break;\n    }\n    case TOK_POSTFIX_MINUS: {\n        mjs_val_t obj = mjs_pop(mjs);\n        mjs_val_t key = mjs_pop(mjs);\n        if(mjs_is_object(obj) && mjs_is_string(key)) {\n            mjs_val_t v = mjs_get_v(mjs, obj, key);\n            mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);\n            mjs_set_v(mjs, obj, key, v1);\n            mjs_push(mjs, v);\n        } else {\n            mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"invalid operand for --\");\n        }\n        break;\n    }\n    case TOK_MINUS_MINUS: {\n        mjs_val_t obj = mjs_pop(mjs);\n        mjs_val_t key = mjs_pop(mjs);\n        if(mjs_is_object(obj) && mjs_is_string(key)) {\n            mjs_val_t v = mjs_get_v(mjs, obj, key);\n            v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);\n            mjs_set_v(mjs, obj, key, v);\n            mjs_push(mjs, v);\n        } else {\n            mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"invalid operand for --\");\n        }\n        break;\n    }\n    case TOK_PLUS_PLUS: {\n        mjs_val_t obj = mjs_pop(mjs);\n        mjs_val_t key = mjs_pop(mjs);\n        if(mjs_is_object(obj) && mjs_is_string(key)) {\n            mjs_val_t v = mjs_get_v(mjs, obj, key);\n            v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);\n            mjs_set_v(mjs, obj, key, v);\n            mjs_push(mjs, v);\n        } else {\n            mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"invalid operand for ++\");\n        }\n        break;\n    }\n        /*\n     * NOTE: TOK_LOGICAL_AND and TOK_LOGICAL_OR don't need to be here, because\n     * they are just naturally handled by the short-circuit evaluation.\n     * See PARSE_LTR_BINOP() macro in mjs_parser.c.\n     */\n\n        /* clang-format off */\n    case TOK_MINUS_ASSIGN:    op_assign(mjs, TOK_MINUS);    break;\n    case TOK_PLUS_ASSIGN:     op_assign(mjs, TOK_PLUS);     break;\n    case TOK_MUL_ASSIGN:      op_assign(mjs, TOK_MUL);      break;\n    case TOK_DIV_ASSIGN:      op_assign(mjs, TOK_DIV);      break;\n    case TOK_REM_ASSIGN:      op_assign(mjs, TOK_REM);      break;\n    case TOK_AND_ASSIGN:      op_assign(mjs, TOK_AND);      break;\n    case TOK_OR_ASSIGN:       op_assign(mjs, TOK_OR);       break;\n    case TOK_XOR_ASSIGN:      op_assign(mjs, TOK_XOR);      break;\n    case TOK_LSHIFT_ASSIGN:   op_assign(mjs, TOK_LSHIFT);   break;\n    case TOK_RSHIFT_ASSIGN:   op_assign(mjs, TOK_RSHIFT);   break;\n    case TOK_URSHIFT_ASSIGN:  op_assign(mjs, TOK_URSHIFT);  break;\n    case TOK_COMMA: break;\n    /* clang-format on */\n    case TOK_KEYWORD_TYPEOF:\n        mjs_push(mjs, mjs_mk_string(mjs, mjs_typeof(mjs_pop(mjs)), ~0, 1));\n        break;\n    default:\n        LOG(LL_ERROR, (\"Unknown expr: %d\", op));\n        break;\n    }\n}\n\nstatic int getprop_builtin_string(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    int isnum = 0;\n    int idx = cstr_to_ulong(name, name_len, &isnum);\n\n    if(strcmp(name, \"length\") == 0) {\n        size_t val_len;\n        mjs_get_string(mjs, &val, &val_len);\n        *res = mjs_mk_number(mjs, (double)val_len);\n        return 1;\n    } else if(strcmp(name, \"at\") == 0 || strcmp(name, \"charCodeAt\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_string_char_code_at);\n        return 1;\n    } else if(strcmp(name, \"indexOf\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_string_index_of);\n        return 1;\n    } else if(strcmp(name, \"slice\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_string_slice);\n        return 1;\n    } else if(strcmp(name, \"toUpperCase\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_string_to_upper_case);\n        return 1;\n    } else if(strcmp(name, \"toLowerCase\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_string_to_lower_case);\n        return 1;\n    } else if(isnum) {\n        /*\n     * string subscript: return a new one-byte string if the index\n     * is not out of bounds\n     */\n        size_t val_len;\n        const char* str = mjs_get_string(mjs, &val, &val_len);\n        if(idx >= 0 && idx < (int)val_len) {\n            *res = mjs_mk_string(mjs, str + idx, 1, 1);\n        } else {\n            *res = MJS_UNDEFINED;\n        }\n        return 1;\n    }\n    return 0;\n}\n\nstatic int getprop_builtin_number(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    if(strcmp(name, \"toString\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_number_to_string);\n        return 1;\n    }\n\n    (void)val;\n    (void)name_len;\n    return 0;\n}\n\nstatic int getprop_builtin_array(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    if(strcmp(name, \"splice\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_array_splice);\n        return 1;\n    } else if(strcmp(name, \"push\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_array_push_internal);\n        return 1;\n    } else if(strcmp(name, \"length\") == 0) {\n        *res = mjs_mk_number(mjs, mjs_array_length(mjs, val));\n        return 1;\n    }\n\n    (void)name_len;\n    return 0;\n}\n\nstatic int getprop_builtin_foreign(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    int isnum = 0;\n    int idx = cstr_to_ulong(name, name_len, &isnum);\n\n    if(!isnum) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"index must be a number\");\n    } else {\n        uint8_t* ptr = (uint8_t*)mjs_get_ptr(mjs, val);\n        *res = mjs_mk_number(mjs, *(ptr + idx));\n    }\n    return 1;\n}\n\nstatic int getprop_builtin_array_buf(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    if(strcmp(name, \"byteLength\") == 0) {\n        size_t len = 0;\n        mjs_array_buf_get_ptr(mjs, val, &len);\n        *res = mjs_mk_number(mjs, len);\n        return 1;\n    } else if(strcmp(name, \"getPtr\") == 0) {\n        void* ptr = mjs_array_buf_get_ptr(mjs, val, NULL);\n        *res = mjs_mk_foreign(mjs, ptr);\n        return 1;\n    } else if(strcmp(name, \"slice\") == 0) {\n        *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_array_buf_slice);\n        return 1;\n    }\n\n    (void)name_len;\n    return 0;\n}\n\nstatic int getprop_builtin_data_view(\n    struct mjs* mjs,\n    mjs_val_t val,\n    const char* name,\n    size_t name_len,\n    mjs_val_t* res) {\n    if(strcmp(name, \"byteLength\") == 0) {\n        size_t len = 0;\n        mjs_array_buf_get_ptr(mjs, mjs_dataview_get_buf(mjs, val), &len);\n        *res = mjs_mk_number(mjs, len);\n        return 1;\n    } else if(strcmp(name, \"length\") == 0) {\n        *res = mjs_dataview_get_len(mjs, val);\n        return 1;\n    } else if(strcmp(name, \"buffer\") == 0) {\n        *res = mjs_dataview_get_buf(mjs, val);\n        return 1;\n    }\n\n    (void)name_len;\n    return 0;\n}\n\nstatic void mjs_apply_(struct mjs* mjs) {\n    mjs_val_t res = MJS_UNDEFINED, *args = NULL;\n    mjs_val_t func = mjs->vals.this_obj, v = mjs_arg(mjs, 1);\n    int i, nargs = 0;\n    if(mjs_is_array(v)) {\n        nargs = mjs_array_length(mjs, v);\n        args = calloc(nargs, sizeof(args[0]));\n        for(i = 0; i < nargs; i++)\n            args[i] = mjs_array_get(mjs, v, i);\n    }\n    mjs_apply(mjs, &res, func, mjs_arg(mjs, 0), nargs, args);\n    free(args);\n    mjs_return(mjs, res);\n}\n\nstatic int getprop_builtin(struct mjs* mjs, mjs_val_t val, mjs_val_t name, mjs_val_t* res) {\n    size_t n;\n    char* s = NULL;\n    int need_free = 0;\n    int handled = 0;\n\n    mjs_err_t err = mjs_to_string(mjs, &name, &s, &n, &need_free);\n\n    if(err == MJS_OK) {\n        if(mjs_is_string(val)) {\n            handled = getprop_builtin_string(mjs, val, s, n, res);\n        } else if(s != NULL && n == 5 && strncmp(s, \"apply\", n) == 0) {\n            *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_apply_);\n            handled = 1;\n        } else if(mjs_is_number(val)) {\n            handled = getprop_builtin_number(mjs, val, s, n, res);\n        } else if(mjs_is_array(val)) {\n            handled = getprop_builtin_array(mjs, val, s, n, res);\n        } else if(mjs_is_foreign(val)) {\n            handled = getprop_builtin_foreign(mjs, val, s, n, res);\n        } else if(mjs_is_array_buf(val)) {\n            handled = getprop_builtin_array_buf(mjs, val, s, n, res);\n        } else if(mjs_is_data_view(val)) {\n            handled = getprop_builtin_data_view(mjs, val, s, n, res);\n        }\n    }\n\n    if(need_free) {\n        free(s);\n        s = NULL;\n    }\n\n    return handled;\n}\n\nMJS_PRIVATE mjs_err_t mjs_execute(struct mjs* mjs, size_t off, mjs_val_t* res) {\n    size_t i;\n    uint8_t prev_opcode = OP_MAX;\n    uint8_t opcode = OP_MAX;\n\n    /*\n   * remember lengths of all stacks, they will be restored in case of an error\n   */\n    int stack_len = mjs->stack.len;\n    int call_stack_len = mjs->call_stack.len;\n    int arg_stack_len = mjs->arg_stack.len;\n    int scopes_len = mjs->scopes.len;\n    int loop_addresses_len = mjs->loop_addresses.len;\n    size_t start_off = off;\n    const uint8_t* code;\n\n    struct mjs_bcode_part bp = *mjs_bcode_part_get_by_offset(mjs, off);\n\n    mjs_set_errorf(mjs, MJS_OK, NULL);\n    free(mjs->stack_trace);\n    mjs->stack_trace = NULL;\n\n    off -= bp.start_idx;\n\n    for(i = off; i < bp.data.len; i++) {\n        mjs->cur_bcode_offset = i;\n\n        if(mjs->need_gc) {\n            if(maybe_gc(mjs)) {\n                mjs->need_gc = 0;\n            }\n        }\n#if MJS_AGGRESSIVE_GC\n        maybe_gc(mjs);\n#endif\n\n        code = (const uint8_t*)bp.data.p;\n#if MJS_ENABLE_DEBUG\n        mjs_disasm_single(code, i);\n#endif\n        prev_opcode = opcode;\n        opcode = code[i];\n        switch(opcode) {\n        case OP_BCODE_HEADER: {\n            mjs_header_item_t bcode_offset;\n            memcpy(\n                &bcode_offset,\n                code + i + 1 + sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET,\n                sizeof(bcode_offset));\n            i += bcode_offset;\n        } break;\n        case OP_PUSH_NULL:\n            mjs_push(mjs, mjs_mk_null());\n            break;\n        case OP_PUSH_UNDEF:\n            mjs_push(mjs, mjs_mk_undefined());\n            break;\n        case OP_PUSH_FALSE:\n            mjs_push(mjs, mjs_mk_boolean(mjs, 0));\n            break;\n        case OP_PUSH_TRUE:\n            mjs_push(mjs, mjs_mk_boolean(mjs, 1));\n            break;\n        case OP_PUSH_OBJ:\n            mjs_push(mjs, mjs_mk_object(mjs));\n            break;\n        case OP_PUSH_ARRAY:\n            mjs_push(mjs, mjs_mk_array(mjs));\n            break;\n        case OP_PUSH_FUNC: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            mjs_push(mjs, mjs_mk_function(mjs, bp.start_idx + i - n));\n            i += llen;\n            break;\n        }\n        case OP_PUSH_THIS:\n            mjs_push(mjs, mjs->vals.this_obj);\n            break;\n        case OP_JMP: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            i += n + llen;\n            break;\n        }\n        case OP_JMP_FALSE: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            i += llen;\n            if(!mjs_is_truthy(mjs, mjs_pop(mjs))) {\n                mjs_push(mjs, MJS_UNDEFINED);\n                i += n;\n            }\n            break;\n        }\n        /*\n       * OP_JMP_NEUTRAL_... ops are like as OP_JMP_..., but they are completely\n       * stack-neutral: they just check the TOS, and increment instruction\n       * pointer if the TOS is truthy/falsy.\n       */\n        case OP_JMP_NEUTRAL_TRUE: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            i += llen;\n            if(mjs_is_truthy(mjs, vtop(&mjs->stack))) {\n                i += n;\n            }\n            break;\n        }\n        case OP_JMP_NEUTRAL_FALSE: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            i += llen;\n            if(!mjs_is_truthy(mjs, vtop(&mjs->stack))) {\n                i += n;\n            }\n            break;\n        }\n        case OP_FIND_SCOPE: {\n            mjs_val_t key = vtop(&mjs->stack);\n            mjs_push(mjs, mjs_find_scope(mjs, key));\n            break;\n        }\n        case OP_CREATE: {\n            mjs_val_t obj = mjs_pop(mjs);\n            mjs_val_t key = mjs_pop(mjs);\n            if(mjs_get_own_property_v(mjs, obj, key) == NULL) {\n                mjs_set_v(mjs, obj, key, MJS_UNDEFINED);\n            }\n            break;\n        }\n        case OP_APPEND: {\n            mjs_val_t val = mjs_pop(mjs);\n            mjs_val_t arr = mjs_pop(mjs);\n            mjs_err_t err = mjs_array_push(mjs, arr, val);\n            if(err != MJS_OK) {\n                mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"append to non-array\");\n            }\n            break;\n        }\n        case OP_GET: {\n            mjs_val_t obj = mjs_pop(mjs);\n            mjs_val_t key = mjs_pop(mjs);\n            mjs_val_t val = MJS_UNDEFINED;\n\n            if(!getprop_builtin(mjs, obj, key, &val)) {\n                if(mjs_is_object(obj)) {\n                    val = mjs_get_v_proto(mjs, obj, key);\n                } else if((mjs_is_data_view(obj) && (mjs_is_number(key)))) {\n                    val = mjs_dataview_get_prop(mjs, obj, key);\n                } else {\n                    mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"type error\");\n                }\n            }\n\n            mjs_push(mjs, val);\n            if(prev_opcode != OP_FIND_SCOPE) {\n                /*\n           * Previous opcode was not OP_FIND_SCOPE, so it's some \"custom\"\n           * object which might be used as `this`, so, save it\n           */\n                mjs->vals.last_getprop_obj = obj;\n            } else {\n                /*\n           * Previous opcode was OP_FIND_SCOPE, so we're getting value from\n           * the scope, and it should *not* be used as `this`\n           */\n                mjs->vals.last_getprop_obj = MJS_UNDEFINED;\n            }\n            break;\n        }\n        case OP_DEL_SCOPE:\n            if(mjs->scopes.len <= 1) {\n                mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, \"scopes underflow\");\n            } else {\n                mjs_pop_val(&mjs->scopes);\n            }\n            break;\n        case OP_NEW_SCOPE:\n            push_mjs_val(&mjs->scopes, mjs_mk_object(mjs));\n            break;\n        case OP_PUSH_SCOPE:\n            assert(mjs_stack_size(&mjs->scopes) > 0);\n            mjs_push(mjs, vtop(&mjs->scopes));\n            break;\n        case OP_PUSH_STR: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            mjs_push(mjs, mjs_mk_string(mjs, (char*)code + i + 1 + llen, n, 1));\n            i += llen + n;\n            break;\n        }\n        case OP_PUSH_INT: {\n            int llen;\n            int64_t n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            mjs_push(mjs, mjs_mk_number(mjs, (double)n));\n            i += llen;\n            break;\n        }\n        case OP_PUSH_DBL: {\n            int llen, n = cs_varint_decode_unsafe(&code[i + 1], &llen);\n            mjs_push(mjs, mjs_mk_number(mjs, strtod((char*)code + i + 1 + llen, NULL)));\n            i += llen + n;\n            break;\n        }\n        case OP_FOR_IN_NEXT: {\n            /*\n         * Data stack layout:\n         * ...                                    <-- Bottom of the data stack\n         * <iterator_variable_name>   (string)\n         * <object_that_is_iterated>  (object)\n         * <iterator_foreign_ptr>                 <-- Top of the data stack\n         */\n            mjs_val_t* iterator = vptr(&mjs->stack, -1);\n            mjs_val_t obj = *vptr(&mjs->stack, -2);\n            if(mjs_is_object(obj)) {\n                mjs_val_t var_name = *vptr(&mjs->stack, -3);\n                mjs_val_t key = mjs_next(mjs, obj, iterator);\n                if(key != MJS_UNDEFINED) {\n                    mjs_val_t scope = mjs_find_scope(mjs, var_name);\n                    mjs_set_v(mjs, scope, var_name, key);\n                }\n            } else {\n                mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"can't iterate over non-object value\");\n            }\n            break;\n        }\n        case OP_RETURN: {\n            /*\n         * Return address is saved as a global bcode offset, so we need to\n         * convert it to the local offset\n         */\n            size_t off_ret = call_stack_restore_frame(mjs);\n            if(off_ret != MJS_BCODE_OFFSET_EXIT) {\n                bp = *mjs_bcode_part_get_by_offset(mjs, off_ret);\n                code = (const uint8_t*)bp.data.p;\n                i = off_ret - bp.start_idx;\n                LOG(LL_VERBOSE_DEBUG, (\"RETURNING TO %d\", (int)off_ret + 1));\n            } else {\n                goto clean;\n            }\n            // mjs_dump(mjs, 0, stdout);\n            break;\n        }\n        case OP_ARGS: {\n            /*\n         * If OP_ARGS follows OP_GET, then last_getprop_obj is set to `this`\n         * value; otherwise, last_getprop_obj is irrelevant and we have to\n         * reset it to `undefined`\n         */\n            if(prev_opcode != OP_GET) {\n                mjs->vals.last_getprop_obj = MJS_UNDEFINED;\n            }\n\n            /*\n         * Push last_getprop_obj, which is going to be used as `this`, see\n         * OP_CALL\n         */\n            push_mjs_val(&mjs->arg_stack, mjs->vals.last_getprop_obj);\n            /*\n         * Push current size of data stack, it's needed to place arguments\n         * properly\n         */\n            push_mjs_val(&mjs->arg_stack, mjs_mk_number(mjs, (double)mjs_stack_size(&mjs->stack)));\n            break;\n        }\n        case OP_CALL: {\n            // LOG(LL_INFO, (\"BEFORE CALL\"));\n            // mjs_dump(mjs, 0, stdout);\n            int func_pos;\n            mjs_val_t* func;\n            mjs_val_t retval_stack_idx = vtop(&mjs->arg_stack);\n            func_pos = mjs_get_int(mjs, retval_stack_idx) - 1;\n            func = vptr(&mjs->stack, func_pos);\n\n            /* Drop data stack size (pushed by OP_ARGS) */\n            mjs_pop_val(&mjs->arg_stack);\n\n            if(mjs_is_function(*func)) {\n                size_t off_call;\n                call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx);\n\n                /*\n           * Function offset is a global bcode offset, so we need to convert it\n           * to the local offset\n           */\n                off_call = mjs_get_func_addr(*func) - 1;\n                bp = *mjs_bcode_part_get_by_offset(mjs, off_call);\n                code = (const uint8_t*)bp.data.p;\n                i = off_call - bp.start_idx;\n\n                *func = MJS_UNDEFINED; // Return value\n                // LOG(LL_VERBOSE_DEBUG, (\"CALLING  %d\", i + 1));\n            } else if(mjs_is_string(*func) || mjs_is_ffi_sig(*func)) {\n                /* Call ffi-ed function */\n\n                call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx);\n\n                /* Perform the ffi-ed function call */\n                mjs_ffi_call2(mjs);\n\n                call_stack_restore_frame(mjs);\n            } else if(mjs_is_foreign(*func)) {\n                /* Call cfunction */\n\n                call_stack_push_frame(mjs, bp.start_idx + i, retval_stack_idx);\n\n                /* Perform the cfunction call */\n                ((void (*)(struct mjs*))mjs_get_ptr(mjs, *func))(mjs);\n\n                call_stack_restore_frame(mjs);\n            } else {\n                mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"calling non-callable\");\n            }\n            break;\n        }\n        case OP_SET_ARG: {\n            int llen1, llen2, n, arg_no = cs_varint_decode_unsafe(&code[i + 1], &llen1);\n            mjs_val_t obj, key, v;\n            n = cs_varint_decode_unsafe(&code[i + llen1 + 1], &llen2);\n            key = mjs_mk_string(mjs, (char*)code + i + 1 + llen1 + llen2, n, 1);\n            obj = vtop(&mjs->scopes);\n            v = mjs_arg(mjs, arg_no);\n            mjs_set_v(mjs, obj, key, v);\n            i += llen1 + llen2 + n;\n            break;\n        }\n        case OP_SETRETVAL: {\n            if(mjs_stack_size(&mjs->call_stack) < CALL_STACK_FRAME_ITEMS_CNT) {\n                mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, \"cannot return\");\n            } else {\n                size_t retval_pos = mjs_get_int(\n                    mjs, *vptr(&mjs->call_stack, -1 - CALL_STACK_FRAME_ITEM_RETVAL_STACK_IDX));\n                *vptr(&mjs->stack, retval_pos - 1) = mjs_pop(mjs);\n            }\n            // LOG(LL_INFO, (\"AFTER SETRETVAL\"));\n            // mjs_dump(mjs, 0, stdout);\n            break;\n        }\n        case OP_EXPR: {\n            int op = code[i + 1];\n            exec_expr(mjs, op);\n            i++;\n            break;\n        }\n        case OP_DROP: {\n            mjs_pop(mjs);\n            break;\n        }\n        case OP_DUP: {\n            mjs_push(mjs, vtop(&mjs->stack));\n            break;\n        }\n        case OP_SWAP: {\n            mjs_val_t a = mjs_pop(mjs);\n            mjs_val_t b = mjs_pop(mjs);\n            mjs_push(mjs, a);\n            mjs_push(mjs, b);\n            break;\n        }\n        case OP_LOOP: {\n            int l1, l2, off = cs_varint_decode_unsafe(&code[i + 1], &l1);\n            /* push scope index */\n            push_mjs_val(\n                &mjs->loop_addresses, mjs_mk_number(mjs, (double)mjs_stack_size(&mjs->scopes)));\n\n            /* push break offset */\n            push_mjs_val(\n                &mjs->loop_addresses,\n                mjs_mk_number(mjs, (double)(i + 1 /* OP_LOOP */ + l1 + off)));\n            off = cs_varint_decode_unsafe(&code[i + 1 + l1], &l2);\n\n            /* push continue offset */\n            push_mjs_val(\n                &mjs->loop_addresses,\n                mjs_mk_number(mjs, (double)(i + 1 /* OP_LOOP*/ + l1 + l2 + off)));\n            i += l1 + l2;\n            break;\n        }\n        case OP_CONTINUE: {\n            if(mjs_stack_size(&mjs->loop_addresses) >= 3) {\n                size_t scopes_len = mjs_get_int(mjs, *vptr(&mjs->loop_addresses, -3));\n                assert(mjs_stack_size(&mjs->scopes) >= scopes_len);\n                mjs->scopes.len = scopes_len * sizeof(mjs_val_t);\n\n                /* jump to \"continue\" address */\n                i = mjs_get_int(mjs, vtop(&mjs->loop_addresses)) - 1;\n            } else {\n                mjs_set_errorf(mjs, MJS_SYNTAX_ERROR, \"misplaced 'continue'\");\n            }\n        } break;\n        case OP_BREAK: {\n            if(mjs_stack_size(&mjs->loop_addresses) >= 3) {\n                size_t scopes_len;\n                /* drop \"continue\" address */\n                mjs_pop_val(&mjs->loop_addresses);\n\n                /* pop \"break\" address and jump to it */\n                i = mjs_get_int(mjs, mjs_pop_val(&mjs->loop_addresses)) - 1;\n\n                /* restore scope index */\n                scopes_len = mjs_get_int(mjs, mjs_pop_val(&mjs->loop_addresses));\n                assert(mjs_stack_size(&mjs->scopes) >= scopes_len);\n                mjs->scopes.len = scopes_len * sizeof(mjs_val_t);\n\n                LOG(LL_VERBOSE_DEBUG, (\"BREAKING TO %d\", (int)i + 1));\n            } else {\n                mjs_set_errorf(mjs, MJS_SYNTAX_ERROR, \"misplaced 'break'\");\n            }\n        } break;\n        case OP_NOP:\n            break;\n        case OP_EXIT:\n            i = bp.data.len;\n            break;\n        default:\n#if MJS_ENABLE_DEBUG\n            mjs_dump(mjs, 1);\n#endif\n            mjs_set_errorf(\n                mjs,\n                MJS_INTERNAL_ERROR,\n                \"Unknown opcode: %d, off %d+%d\",\n                (int)opcode,\n                (int)bp.start_idx,\n                (int)i);\n            i = bp.data.len;\n            break;\n        }\n\n        if(mjs->exec_flags_poller) {\n            mjs->exec_flags_poller(mjs);\n        }\n\n        if(mjs->error != MJS_OK) {\n            if(mjs->error == MJS_NEED_EXIT) {\n                mjs->error = MJS_OK;\n                goto clean;\n            }\n\n            mjs_gen_stack_trace(mjs, bp.start_idx + i - 1 /* undo the i++ */);\n\n            /* restore stack lenghts */\n            mjs->stack.len = stack_len;\n            mjs->call_stack.len = call_stack_len;\n            mjs->arg_stack.len = arg_stack_len;\n            mjs->scopes.len = scopes_len;\n            mjs->loop_addresses.len = loop_addresses_len;\n\n            /* script will evaluate to `undefined` */\n            mjs_push(mjs, MJS_UNDEFINED);\n            break;\n        }\n    }\n\nclean:\n    /* Remember result of the evaluation of this bcode part */\n    mjs_bcode_part_get_by_offset(mjs, start_off)->exec_res = mjs->error;\n\n    *res = mjs_pop(mjs);\n    return mjs->error;\n}\n\nMJS_PRIVATE mjs_err_t mjs_exec_internal(\n    struct mjs* mjs,\n    const char* path,\n    const char* src,\n    int generate_jsc,\n    mjs_val_t* res) {\n    size_t off = mjs->bcode_len;\n    mjs_val_t r = MJS_UNDEFINED;\n    mjs->error = mjs_parse(path, src, mjs);\n#if MJS_ENABLE_DEBUG\n    if(cs_log_level >= LL_VERBOSE_DEBUG) mjs_dump(mjs, 1);\n#endif\n    if(generate_jsc == -1) generate_jsc = mjs->generate_jsc;\n    if(mjs->error == MJS_OK) {\n#if MJS_GENERATE_JSC && defined(CS_MMAP)\n        if(generate_jsc && path != NULL) {\n            const char* jsext = \".js\";\n            int basename_len = (int)strlen(path) - strlen(jsext);\n            if(basename_len > 0 && strcmp(path + basename_len, jsext) == 0) {\n                /* source file has a .js extension: create a .jsc counterpart */\n                int rewrite = 1;\n                int read_mmapped = 1;\n\n                /* construct .jsc filename */\n                const char* jscext = \".jsc\";\n                char filename_jsc[basename_len + strlen(jscext) + 1 /* nul-term */];\n                memcpy(filename_jsc, path, basename_len);\n                strcpy(filename_jsc + basename_len, jscext);\n\n                /* get last bcode part */\n                struct mjs_bcode_part* bp = mjs_bcode_part_get(mjs, mjs_bcode_parts_cnt(mjs) - 1);\n\n                /*\n         * before writing .jsc file, check if it already exists and has the\n         * same contents\n         *\n         * TODO(dfrank): probably store crc32 before the bcode data, and only\n         * compare it.\n         */\n                {\n                    size_t size;\n                    char* data = cs_mmap_file(filename_jsc, &size);\n                    if(data != NULL) {\n                        if(size == bp->data.len) {\n                            if(memcmp(data, bp->data.p, size) == 0) {\n                                /* .jsc file is up to date, so don't rewrite it */\n                                rewrite = 0;\n                            }\n                        }\n                        munmap(data, size);\n                    }\n                }\n\n                /* try to open .jsc file for writing */\n                if(rewrite) {\n                    FILE* fp = fopen(filename_jsc, \"wb\");\n                    if(fp != NULL) {\n                        /* write last bcode part to .jsc */\n                        fwrite(bp->data.p, bp->data.len, 1, fp);\n                        fclose(fp);\n                    } else {\n                        LOG(LL_WARN, (\"Failed to open %s for writing\", filename_jsc));\n                        read_mmapped = 0;\n                    }\n                }\n\n                if(read_mmapped) {\n                    /* free RAM buffer with last bcode part */\n                    free((void*)bp->data.p);\n\n                    /* mmap .jsc file and set last bcode part buffer to it */\n                    bp->data.p = cs_mmap_file(filename_jsc, &bp->data.len);\n                    bp->in_rom = 1;\n                }\n            }\n        }\n#else\n        (void)generate_jsc;\n#endif\n\n        mjs_execute(mjs, off, &r);\n    }\n    if(res != NULL) *res = r;\n    return mjs->error;\n}\n\nmjs_err_t mjs_exec(struct mjs* mjs, const char* src, mjs_val_t* res) {\n    return mjs_exec_internal(mjs, \"<stdin>\", src, 0 /* generate_jsc */, res);\n}\n\nmjs_err_t mjs_exec_file(struct mjs* mjs, const char* path, mjs_val_t* res) {\n    mjs_err_t error = MJS_FILE_READ_ERROR;\n    mjs_val_t r = MJS_UNDEFINED;\n    size_t size;\n    char* source_code = cs_read_file(path, &size);\n\n    if(source_code == NULL) {\n        error = MJS_FILE_READ_ERROR;\n        mjs_prepend_errorf(mjs, error, \"failed to read file \\\"%s\\\"\", path);\n        goto clean;\n    }\n\n    r = MJS_UNDEFINED;\n    error = mjs_exec_internal(mjs, path, source_code, -1, &r);\n    free(source_code);\n\nclean:\n    if(res != NULL) *res = r;\n    return error;\n}\n\nmjs_err_t\n    mjs_call(struct mjs* mjs, mjs_val_t* res, mjs_val_t func, mjs_val_t this_val, int nargs, ...) {\n    va_list ap;\n    int i;\n    mjs_err_t ret;\n    mjs_val_t* args = calloc(nargs, sizeof(mjs_val_t));\n    va_start(ap, nargs);\n    for(i = 0; i < nargs; i++) {\n        args[i] = va_arg(ap, mjs_val_t);\n    }\n    va_end(ap);\n\n    ret = mjs_apply(mjs, res, func, this_val, nargs, args);\n\n    free(args);\n    return ret;\n}\n\nmjs_err_t mjs_apply(\n    struct mjs* mjs,\n    mjs_val_t* res,\n    mjs_val_t func,\n    mjs_val_t this_val,\n    int nargs,\n    mjs_val_t* args) {\n    mjs_val_t r, prev_this_val, retval_stack_idx, *resp;\n    int i;\n\n    if(!mjs_is_function(func) && !mjs_is_foreign(func) && !mjs_is_ffi_sig(func)) {\n        return mjs_set_errorf(mjs, MJS_TYPE_ERROR, \"calling non-callable\");\n    }\n\n    LOG(LL_VERBOSE_DEBUG, (\"applying func %d\", (int)mjs_get_func_addr(func)));\n\n    prev_this_val = mjs->vals.this_obj;\n\n    /* Push callable which will be later replaced with the return value */\n    mjs_push(mjs, func);\n    resp = vptr(&mjs->stack, -1);\n\n    /* Remember index by which return value should be written */\n    retval_stack_idx = mjs_mk_number(mjs, (double)mjs_stack_size(&mjs->stack));\n\n    // Push all arguments\n    for(i = 0; i < nargs; i++) {\n        mjs_push(mjs, args[i]);\n    }\n\n    /* Push this value to arg_stack, call_stack_push_frame() expects that */\n    push_mjs_val(&mjs->arg_stack, this_val);\n\n    /* Push call stack frame, just like OP_CALL does that */\n    call_stack_push_frame(mjs, MJS_BCODE_OFFSET_EXIT, retval_stack_idx);\n\n    if(mjs_is_foreign(func)) {\n        ((void (*)(struct mjs*))mjs_get_ptr(mjs, func))(mjs);\n        if(res != NULL) *res = *resp;\n    } else if(mjs_is_ffi_sig(func)) {\n        mjs_ffi_call2(mjs);\n        if(res != NULL) *res = *resp;\n    } else {\n        size_t addr = mjs_get_func_addr(func);\n        mjs_execute(mjs, addr, &r);\n        if(res != NULL) *res = r;\n    }\n\n    /*\n   * If there was an error, we need to restore frame and do the cleanup\n   * which is otherwise done by OP_RETURN\n   */\n    if(mjs->error != MJS_OK) {\n        call_stack_restore_frame(mjs);\n\n        // Pop cell at which the returned value should've been written\n        mjs_pop(mjs);\n    }\n    mjs->vals.this_obj = prev_this_val;\n\n    return mjs->error;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_exec.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_EXEC_H_\n#define MJS_EXEC_H_\n\n#include \"mjs_exec_public.h\"\n\n/*\n * A special bcode offset value which causes mjs_execute() to exit immediately;\n * used in mjs_apply().\n */\n#define MJS_BCODE_OFFSET_EXIT ((size_t)0x7fffffff)\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nMJS_PRIVATE mjs_err_t mjs_execute(struct mjs* mjs, size_t off, mjs_val_t* res);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_EXEC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_exec_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_EXEC_PUBLIC_H_\n#define MJS_EXEC_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n#include <stdio.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nmjs_err_t mjs_exec(struct mjs*, const char* src, mjs_val_t* res);\n\nmjs_err_t mjs_exec_file(struct mjs* mjs, const char* path, mjs_val_t* res);\nmjs_err_t mjs_apply(\n    struct mjs* mjs,\n    mjs_val_t* res,\n    mjs_val_t func,\n    mjs_val_t this_val,\n    int nargs,\n    mjs_val_t* args);\nmjs_err_t\n    mjs_call(struct mjs* mjs, mjs_val_t* res, mjs_val_t func, mjs_val_t this_val, int nargs, ...);\nmjs_val_t mjs_get_this(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_EXEC_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_features.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_FEATURES_H_\n#define MJS_FEATURES_H_\n\n#if !defined(MJS_AGGRESSIVE_GC)\n#define MJS_AGGRESSIVE_GC 0\n#endif\n\n#if !defined(MJS_MEMORY_STATS)\n#define MJS_MEMORY_STATS 0\n#endif\n\n/*\n * MJS_GENERATE_JSC: if enabled, and if mmapping is also enabled (CS_MMAP),\n * then execution of any .js file will result in creation of a .jsc file with\n * precompiled bcode, and this .jsc file will be mmapped, instead of keeping\n * bcode in RAM.\n *\n * By default it's enabled (provided that CS_MMAP is defined)\n */\n#if !defined(MJS_GENERATE_JSC)\n#if defined(CS_MMAP)\n#define MJS_GENERATE_JSC 1\n#else\n#define MJS_GENERATE_JSC 0\n#endif\n#endif\n\n#endif /* MJS_FEATURES_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_ffi.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/mg_str.h\"\n\n#include \"ffi/ffi.h\"\n#include \"mjs_core.h\"\n#include \"mjs_exec.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n\n/*\n * on linux this is enabled only if __USE_GNU is defined, but we cannot set it\n * because dlfcn could have been included already.\n */\n#ifndef RTLD_DEFAULT\n#define RTLD_DEFAULT NULL\n#endif\n\nstatic ffi_fn_t* get_cb_impl_by_signature(const mjs_ffi_sig_t* sig);\n\n/*\n * Data of the two related arguments: callback function pointer and the\n * userdata for it\n */\nstruct cbdata {\n    /* JS callback function */\n    mjs_val_t func;\n    /* JS userdata */\n    mjs_val_t userdata;\n\n    /* index of the function pointer param */\n    int8_t func_idx;\n    /* index of the userdata param */\n    int8_t userdata_idx;\n};\n\nvoid mjs_set_ffi_resolver(struct mjs* mjs, mjs_ffi_resolver_t* dlsym, void* handle) {\n    mjs->dlsym = dlsym;\n    mjs->dlsym_handle = handle;\n}\n\nvoid* mjs_ffi_resolve(struct mjs* mjs, const char* symbol) {\n    if(mjs->dlsym) {\n        return mjs->dlsym(mjs->dlsym_handle, symbol);\n    }\n    return NULL;\n}\n\nstatic mjs_ffi_ctype_t parse_cval_type(struct mjs* mjs, const char* s, const char* e) {\n    struct mg_str ms = MG_NULL_STR;\n    /* Trim leading and trailing whitespace */\n    while(s < e && isspace((int)*s)) s++;\n    while(e > s && isspace((int)e[-1])) e--;\n    ms.p = s;\n    ms.len = e - s;\n    if(mg_vcmp(&ms, \"void\") == 0) {\n        return MJS_FFI_CTYPE_NONE;\n    } else if(mg_vcmp(&ms, \"userdata\") == 0) {\n        return MJS_FFI_CTYPE_USERDATA;\n    } else if(mg_vcmp(&ms, \"int\") == 0) {\n        return MJS_FFI_CTYPE_INT;\n    } else if(mg_vcmp(&ms, \"bool\") == 0) {\n        return MJS_FFI_CTYPE_BOOL;\n    } else if(mg_vcmp(&ms, \"double\") == 0) {\n        return MJS_FFI_CTYPE_DOUBLE;\n    } else if(mg_vcmp(&ms, \"float\") == 0) {\n        return MJS_FFI_CTYPE_FLOAT;\n    } else if(mg_vcmp(&ms, \"char*\") == 0 || mg_vcmp(&ms, \"char *\") == 0) {\n        return MJS_FFI_CTYPE_CHAR_PTR;\n    } else if(mg_vcmp(&ms, \"void*\") == 0 || mg_vcmp(&ms, \"void *\") == 0) {\n        return MJS_FFI_CTYPE_VOID_PTR;\n    } else if(mg_vcmp(&ms, \"struct mg_str\") == 0) {\n        return MJS_FFI_CTYPE_STRUCT_MG_STR;\n    } else if(mg_vcmp(&ms, \"struct mg_str *\") == 0 || mg_vcmp(&ms, \"struct mg_str*\") == 0) {\n        return MJS_FFI_CTYPE_STRUCT_MG_STR_PTR;\n    } else {\n        mjs_prepend_errorf(\n            mjs, MJS_TYPE_ERROR, \"failed to parse val type \\\"%.*s\\\"\", (int)ms.len, ms.p);\n        return MJS_FFI_CTYPE_INVALID;\n    }\n}\n\nstatic const char* find_paren(const char* s, const char* e) {\n    for(; s < e; s++) {\n        if(*s == '(') return s;\n    }\n    return NULL;\n}\n\nstatic const char* find_closing_paren(const char* s, const char* e) {\n    int nesting = 1;\n    while(s < e) {\n        if(*s == '(') {\n            nesting++;\n        } else if(*s == ')') {\n            if(--nesting == 0) break;\n        }\n        s++;\n    }\n    return (s < e ? s : NULL);\n}\n\nMJS_PRIVATE mjs_err_t mjs_parse_ffi_signature(\n    struct mjs* mjs,\n    const char* s,\n    int sig_len,\n    mjs_ffi_sig_t* sig,\n    enum ffi_sig_type sig_type) {\n    mjs_err_t ret = MJS_OK;\n    int vtidx = 0;\n    const char *cur, *e, *tmp_e, *tmp;\n    struct mg_str rt = MG_NULL_STR, fn = MG_NULL_STR, args = MG_NULL_STR;\n    mjs_ffi_ctype_t val_type = MJS_FFI_CTYPE_INVALID;\n    if(sig_len == ~0) {\n        sig_len = strlen(s);\n    }\n    e = s + sig_len;\n\n    mjs_ffi_sig_init(sig);\n\n    /* Skip leading spaces */\n    for(cur = s; cur < e && isspace((int)*cur); cur++)\n        ;\n\n    /* FInd the first set of parens */\n    tmp_e = find_paren(cur, e);\n    if(tmp_e == NULL || tmp_e - s < 2) {\n        ret = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(mjs, ret, \"1\");\n        goto clean;\n    }\n    tmp = find_closing_paren(tmp_e + 1, e);\n    if(tmp == NULL) {\n        ret = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(mjs, ret, \"2\");\n        goto clean;\n    }\n\n    /* Now see if we have a second set of parens */\n    args.p = find_paren(tmp + 1, e);\n    if(args.p == NULL) {\n        /* We don't - it's a regular function signature */\n        fn.p = tmp_e - 1;\n        while(fn.p > cur && isspace((int)*fn.p)) fn.p--;\n        while(fn.p > cur && (isalnum((int)*fn.p) || *fn.p == '_')) {\n            fn.p--;\n            fn.len++;\n        }\n        fn.p++;\n        rt.p = cur;\n        rt.len = fn.p - rt.p;\n        /* Stuff inside parens is args */\n        args.p = tmp_e + 1;\n        args.len = tmp - args.p;\n    } else {\n        /* We do - it's a function pointer, like void (*foo)(...).\n     * Stuff inside the first pair of parens is the function name */\n        fn.p = tmp + 1;\n        fn.len = args.p - tmp;\n        rt.p = cur;\n        rt.len = tmp_e - rt.p;\n        args.p++;\n        tmp = find_closing_paren(args.p, e);\n        if(tmp == NULL) {\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(mjs, ret, \"3\");\n            goto clean;\n        }\n        args.len = tmp - args.p;\n        /*\n     * We ignore the name and leave sig->fn NULL here, but it will later be\n     * set to the appropriate callback implementation.\n     */\n        sig->is_callback = 1;\n    }\n\n    val_type = parse_cval_type(mjs, rt.p, rt.p + rt.len);\n    if(val_type == MJS_FFI_CTYPE_INVALID) {\n        ret = mjs->error;\n        goto clean;\n    }\n    mjs_ffi_sig_set_val_type(sig, vtidx++, val_type);\n\n    /* Parse function name {{{ */\n    if(!sig->is_callback) {\n        char buf[100];\n        if(mjs->dlsym == NULL) {\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(mjs, ret, \"resolver is not set, call mjs_set_ffi_resolver\");\n            goto clean;\n        }\n\n        snprintf(buf, sizeof(buf), \"%.*s\", (int)fn.len, fn.p);\n        sig->fn = (ffi_fn_t*)mjs->dlsym(mjs->dlsym_handle, buf);\n        if(sig->fn == NULL) {\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(mjs, ret, \"dlsym('%s') failed\", buf);\n            goto clean;\n        }\n    } else {\n        tmp_e = strchr(tmp_e, ')');\n        if(tmp_e == NULL) {\n            ret = MJS_TYPE_ERROR;\n            goto clean;\n        }\n    }\n\n    /* Advance cur to the beginning of the arg list */\n    cur = tmp_e = args.p;\n\n    /* Parse all args {{{ */\n    while(tmp_e - args.p < (ptrdiff_t)args.len) {\n        int level = 0; /* nested parens level */\n        int is_fp = 0; /* set to 1 is current arg is a callback function ptr */\n        tmp_e = cur;\n\n        /* Advance tmp_e until the next arg separator */\n        while(*tmp_e && (level > 0 || (*tmp_e != ',' && *tmp_e != ')'))) {\n            switch(*tmp_e) {\n            case '(':\n                level++;\n                /*\n           * only function pointer params can have parens, so, set the flag\n           * that it's going to be a function pointer\n           */\n                is_fp = 1;\n                break;\n            case ')':\n                level--;\n                break;\n            }\n            tmp_e++;\n        }\n\n        if(tmp_e == cur) break;\n\n        /* Parse current arg */\n        if(is_fp) {\n            /* Current argument is a callback function pointer */\n            if(sig->cb_sig != NULL) {\n                /*\n         * We already have parsed some callback argument. Currently we don't\n         * support more than one callback argument, so, return error\n         * TODO(dfrank): probably improve\n         */\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(mjs, ret, \"only one callback is allowed\");\n                goto clean;\n            }\n\n            sig->cb_sig = calloc(sizeof(*sig->cb_sig), 1);\n            ret = mjs_parse_ffi_signature(mjs, cur, tmp_e - cur, sig->cb_sig, FFI_SIG_CALLBACK);\n            if(ret != MJS_OK) {\n                mjs_ffi_sig_free(sig->cb_sig);\n                free(sig->cb_sig);\n                sig->cb_sig = NULL;\n                goto clean;\n            }\n            val_type = MJS_FFI_CTYPE_CALLBACK;\n        } else {\n            /* Some non-function argument */\n            val_type = parse_cval_type(mjs, cur, tmp_e);\n            if(val_type == MJS_FFI_CTYPE_INVALID) {\n                /* parse_cval_type() has already set error message */\n                ret = MJS_TYPE_ERROR;\n                goto clean;\n            }\n        }\n\n        if(!mjs_ffi_sig_set_val_type(sig, vtidx++, val_type)) {\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(mjs, ret, \"too many callback args\");\n            goto clean;\n        }\n\n        if(*tmp_e == ',') {\n            /* Advance cur to the next argument */\n            cur = tmp_e + 1;\n            while(*cur == ' ') cur++;\n        } else {\n            /* No more arguments */\n            break;\n        }\n    }\n    /* }}} */\n\n    /* Analyze the results and see if they are obviously wrong */\n    mjs_ffi_sig_validate(mjs, sig, sig_type);\n    if(!sig->is_valid) {\n        ret = MJS_TYPE_ERROR;\n        goto clean;\n    }\n\n    /* If the signature represents a callback, find the suitable implementation */\n    if(sig->is_callback) {\n        sig->fn = get_cb_impl_by_signature(sig);\n        if(sig->fn == NULL) {\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(\n                mjs,\n                ret,\n                \"the callback signature is valid, but there's \"\n                \"no existing callback implementation for it\");\n            goto clean;\n        }\n    }\n\nclean:\n    if(ret != MJS_OK) {\n        mjs_prepend_errorf(mjs, ret, \"bad ffi signature: \\\"%.*s\\\"\", sig_len, s);\n        sig->is_valid = 0;\n    }\n    return ret;\n}\n\n/* C callbacks implementation {{{ */\n\n/* An argument or a return value for C callback impl */\nunion ffi_cb_data_val {\n    void* p;\n    uintptr_t w;\n    double d;\n    float f;\n};\n\nstruct ffi_cb_data {\n    union ffi_cb_data_val args[MJS_CB_ARGS_MAX_CNT];\n};\n\nstatic union ffi_cb_data_val ffi_cb_impl_generic(void* param, struct ffi_cb_data* data) {\n    struct mjs_ffi_cb_args* cbargs = (struct mjs_ffi_cb_args*)param;\n    mjs_val_t *args, res = MJS_UNDEFINED;\n    union ffi_cb_data_val ret;\n    int i;\n    struct mjs* mjs = cbargs->mjs;\n    mjs_ffi_ctype_t return_ctype = MJS_FFI_CTYPE_NONE;\n    mjs_err_t err;\n\n    memset(&ret, 0, sizeof(ret));\n    mjs_own(mjs, &res);\n\n    /* There must be at least one argument: a userdata */\n    assert(cbargs->sig.args_cnt > 0);\n\n    /* Create JS arguments */\n    args = calloc(1, sizeof(mjs_val_t) * cbargs->sig.args_cnt);\n    for(i = 0; i < cbargs->sig.args_cnt; i++) {\n        mjs_ffi_ctype_t val_type =\n            cbargs->sig.val_types[i + 1 /* first val_type is return value type */];\n        switch(val_type) {\n        case MJS_FFI_CTYPE_USERDATA:\n            args[i] = cbargs->userdata;\n            break;\n        case MJS_FFI_CTYPE_INT:\n            args[i] = mjs_mk_number(mjs, (double)data->args[i].w);\n            break;\n        case MJS_FFI_CTYPE_BOOL:\n            args[i] = mjs_mk_boolean(mjs, !!data->args[i].w);\n            break;\n        case MJS_FFI_CTYPE_CHAR_PTR: {\n            const char* s = (char*)data->args[i].w;\n            if(s == NULL) s = \"\";\n            args[i] = mjs_mk_string(mjs, s, ~0, 1);\n            break;\n        }\n        case MJS_FFI_CTYPE_VOID_PTR:\n            args[i] = mjs_mk_foreign(mjs, (void*)data->args[i].w);\n            break;\n        case MJS_FFI_CTYPE_DOUBLE:\n            args[i] = mjs_mk_number(mjs, data->args[i].d);\n            break;\n        case MJS_FFI_CTYPE_FLOAT:\n            args[i] = mjs_mk_number(mjs, data->args[i].f);\n            break;\n        case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: {\n            struct mg_str* s = (struct mg_str*)(void*)data->args[i].w;\n            args[i] = mjs_mk_string(mjs, s->p, s->len, 1);\n            break;\n        }\n        default:\n            /* should never be here */\n            LOG(LL_ERROR, (\"unexpected val type for arg #%d: %d\\n\", i, val_type));\n            abort();\n        }\n    }\n\n    /*\n   * save return ctype outside of `cbargs` before calling the callback, because\n   * callback might call `ffi_cb_free()`, which will effectively invalidate\n   * `cbargs`\n   */\n    return_ctype = cbargs->sig.val_types[0];\n\n    /* Call JS function */\n    LOG(LL_VERBOSE_DEBUG,\n        (\"calling JS callback void-void %d from C\", mjs_get_int(mjs, cbargs->func)));\n    err = mjs_apply(mjs, &res, cbargs->func, MJS_UNDEFINED, cbargs->sig.args_cnt, args);\n    /*\n   * cbargs might be invalidated by the callback (if it called ffi_cb_free), so\n   * null it out\n   */\n    cbargs = NULL;\n    if(err != MJS_OK) {\n        /*\n     * There's not much we can do about the error here; let's at least print it\n     */\n        mjs_print_error(mjs, stderr, \"MJS callback error\", 1 /* print_stack_trace */);\n\n        goto clean;\n    }\n\n    /* Get return value, if needed */\n    switch(return_ctype) {\n    case MJS_FFI_CTYPE_NONE:\n        /* do nothing */\n        break;\n    case MJS_FFI_CTYPE_INT:\n        ret.w = mjs_get_int(mjs, res);\n        break;\n    case MJS_FFI_CTYPE_BOOL:\n        ret.w = mjs_get_bool(mjs, res);\n        break;\n    case MJS_FFI_CTYPE_VOID_PTR:\n        ret.p = mjs_get_ptr(mjs, res);\n        break;\n    case MJS_FFI_CTYPE_DOUBLE:\n        ret.d = mjs_get_double(mjs, res);\n        break;\n    case MJS_FFI_CTYPE_FLOAT:\n        ret.f = (float)mjs_get_double(mjs, res);\n        break;\n    default:\n        /* should never be here */\n        LOG(LL_ERROR, (\"unexpected return val type %d\\n\", return_ctype));\n        abort();\n    }\n\nclean:\n    free(args);\n    mjs_disown(mjs, &res);\n    return ret;\n}\n\nstatic void ffi_init_cb_data_wwww(\n    struct ffi_cb_data* data,\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    memset(data, 0, sizeof(*data));\n    data->args[0].w = w0;\n    data->args[1].w = w1;\n    data->args[2].w = w2;\n    data->args[3].w = w3;\n    data->args[4].w = w4;\n    data->args[5].w = w5;\n}\n\nstatic uintptr_t ffi_cb_impl_wpwwwww(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w0, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wwpwwww(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w1, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wwwpwww(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w2, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wwwwpww(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w3, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wwwwwpw(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w4, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wwwwwwp(\n    uintptr_t w0,\n    uintptr_t w1,\n    uintptr_t w2,\n    uintptr_t w3,\n    uintptr_t w4,\n    uintptr_t w5) {\n    struct ffi_cb_data data;\n    ffi_init_cb_data_wwww(&data, w0, w1, w2, w3, w4, w5);\n    return ffi_cb_impl_generic((void*)w5, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wpd(uintptr_t w0, double d1) {\n    struct ffi_cb_data data;\n\n    memset(&data, 0, sizeof(data));\n    data.args[0].w = w0;\n    data.args[1].d = d1;\n\n    return ffi_cb_impl_generic((void*)w0, &data).w;\n}\n\nstatic uintptr_t ffi_cb_impl_wdp(double d0, uintptr_t w1) {\n    struct ffi_cb_data data;\n\n    memset(&data, 0, sizeof(data));\n    data.args[0].d = d0;\n    data.args[1].w = w1;\n\n    return ffi_cb_impl_generic((void*)w1, &data).w;\n}\n/* }}} */\n\nstatic struct mjs_ffi_cb_args**\n    ffi_get_matching(struct mjs_ffi_cb_args** plist, mjs_val_t func, mjs_val_t userdata) {\n    for(; *plist != NULL; plist = &((*plist)->next)) {\n        if((*plist)->func == func && (*plist)->userdata == userdata) {\n            break;\n        }\n    }\n    return plist;\n}\n\nstatic ffi_fn_t* get_cb_impl_by_signature(const mjs_ffi_sig_t* sig) {\n    if(sig->is_valid) {\n        int i;\n        int double_cnt = 0;\n        int float_cnt = 0;\n        int userdata_idx = 0 /* not a valid value: index 0 means return value */;\n\n        for(i = 1 /*0th item is a return value*/; i < MJS_CB_SIGNATURE_MAX_SIZE; i++) {\n            mjs_ffi_ctype_t type = sig->val_types[i];\n            switch(type) {\n            case MJS_FFI_CTYPE_DOUBLE:\n                double_cnt++;\n                break;\n            case MJS_FFI_CTYPE_FLOAT:\n                float_cnt++;\n                break;\n            case MJS_FFI_CTYPE_USERDATA:\n                assert(userdata_idx == 0); /* Otherwise is_valid should be 0 */\n                userdata_idx = i;\n                break;\n            default:\n                break;\n            }\n        }\n\n        if(float_cnt > 0) {\n            /* TODO(dfrank): add support for floats in callbacks */\n            return NULL;\n        }\n\n        assert(userdata_idx > 0); /* Otherwise is_valid should be 0 */\n\n        if(sig->args_cnt <= MJS_CB_ARGS_MAX_CNT) {\n            if(mjs_ffi_is_regular_word_or_void(sig->val_types[0])) {\n                /* Return type is a word or void */\n                switch(double_cnt) {\n                case 0:\n                    /* No double arguments */\n                    switch(userdata_idx) {\n                    case 1:\n                        return (ffi_fn_t*)ffi_cb_impl_wpwwwww;\n                    case 2:\n                        return (ffi_fn_t*)ffi_cb_impl_wwpwwww;\n                    case 3:\n                        return (ffi_fn_t*)ffi_cb_impl_wwwpwww;\n                    case 4:\n                        return (ffi_fn_t*)ffi_cb_impl_wwwwpww;\n                    case 5:\n                        return (ffi_fn_t*)ffi_cb_impl_wwwwwpw;\n                    case 6:\n                        return (ffi_fn_t*)ffi_cb_impl_wwwwwwp;\n                    default:\n                        /* should never be here */\n                        abort();\n                    }\n                    break;\n                case 1:\n                    /* 1 double argument */\n                    switch(userdata_idx) {\n                    case 1:\n                        return (ffi_fn_t*)ffi_cb_impl_wpd;\n                    case 2:\n                        return (ffi_fn_t*)ffi_cb_impl_wdp;\n                    }\n                    break;\n                }\n            }\n        } else {\n            /* Too many arguments for the built-in callback impls */\n            /* TODO(dfrank): add support for custom app-dependent resolver */\n        }\n    }\n\n    return NULL;\n}\n\nMJS_PRIVATE mjs_val_t mjs_ffi_sig_to_value(struct mjs_ffi_sig* psig) {\n    if(psig == NULL) {\n        return MJS_NULL;\n    } else {\n        return mjs_legit_pointer_to_value(psig) | MJS_TAG_FUNCTION_FFI;\n    }\n}\n\nMJS_PRIVATE int mjs_is_ffi_sig(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_FUNCTION_FFI;\n}\n\nMJS_PRIVATE struct mjs_ffi_sig* mjs_get_ffi_sig_struct(mjs_val_t v) {\n    struct mjs_ffi_sig* ret = NULL;\n    assert(mjs_is_ffi_sig(v));\n    ret = (struct mjs_ffi_sig*)get_ptr(v);\n    return ret;\n}\n\nMJS_PRIVATE mjs_val_t mjs_mk_ffi_sig(struct mjs* mjs) {\n    struct mjs_ffi_sig* psig = new_ffi_sig(mjs);\n    mjs_ffi_sig_init(psig);\n    return mjs_ffi_sig_to_value(psig);\n}\n\nMJS_PRIVATE void mjs_ffi_sig_destructor(struct mjs* mjs, void* psig) {\n    mjs_ffi_sig_free((mjs_ffi_sig_t*)psig);\n    (void)mjs;\n}\n\nMJS_PRIVATE mjs_err_t mjs_ffi_call(struct mjs* mjs) {\n    mjs_err_t e = MJS_OK;\n    const char* sig_str = NULL;\n    mjs_val_t sig_str_v = mjs_arg(mjs, 0);\n    mjs_val_t ret_v = MJS_UNDEFINED;\n    struct mjs_ffi_sig* psig = mjs_get_ffi_sig_struct(mjs_mk_ffi_sig(mjs));\n    size_t sig_str_len;\n\n    sig_str = mjs_get_string(mjs, &sig_str_v, &sig_str_len);\n    e = mjs_parse_ffi_signature(mjs, sig_str, sig_str_len, psig, FFI_SIG_FUNC);\n    if(e != MJS_OK) goto clean;\n    ret_v = mjs_ffi_sig_to_value(psig);\n\nclean:\n    mjs_return(mjs, ret_v);\n    return e;\n}\n\nMJS_PRIVATE mjs_err_t mjs_ffi_call2(struct mjs* mjs) {\n    mjs_err_t ret = MJS_OK;\n    mjs_ffi_sig_t* psig = NULL;\n    mjs_ffi_ctype_t rtype;\n    mjs_val_t sig_v = *vptr(&mjs->stack, mjs_getretvalpos(mjs));\n\n    int i, nargs;\n    struct ffi_arg res;\n    struct ffi_arg args[FFI_MAX_ARGS_CNT];\n    struct cbdata cbdata;\n\n    /* TODO(dfrank): support multiple callbacks */\n    mjs_val_t resv = mjs_mk_undefined();\n\n    /*\n   * String arguments, needed to support short strings which are packed into\n   * mjs_val_t itself\n   */\n    mjs_val_t argvs[FFI_MAX_ARGS_CNT];\n    struct mg_str argvmgstr[FFI_MAX_ARGS_CNT];\n\n    if(mjs_is_ffi_sig(sig_v)) {\n        psig = mjs_get_ffi_sig_struct(sig_v);\n    } else {\n        ret = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(mjs, ret, \"non-ffi-callable value\");\n        goto clean;\n    }\n\n    memset(&cbdata, 0, sizeof(cbdata));\n    cbdata.func_idx = -1;\n    cbdata.userdata_idx = -1;\n\n    rtype = psig->val_types[0];\n\n    switch(rtype) {\n    case MJS_FFI_CTYPE_DOUBLE:\n        res.ctype = FFI_CTYPE_DOUBLE;\n        break;\n    case MJS_FFI_CTYPE_FLOAT:\n        res.ctype = FFI_CTYPE_FLOAT;\n        break;\n    case MJS_FFI_CTYPE_BOOL:\n        res.ctype = FFI_CTYPE_BOOL;\n        break;\n    case MJS_FFI_CTYPE_USERDATA:\n    case MJS_FFI_CTYPE_INT:\n    case MJS_FFI_CTYPE_CHAR_PTR:\n    case MJS_FFI_CTYPE_VOID_PTR:\n    case MJS_FFI_CTYPE_NONE:\n        res.ctype = FFI_CTYPE_WORD;\n        break;\n\n    case MJS_FFI_CTYPE_INVALID:\n        ret = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(mjs, ret, \"wrong ffi return type\");\n        goto clean;\n    }\n    res.v.i = 0;\n\n    nargs = mjs_stack_size(&mjs->stack) - mjs_get_int(mjs, vtop(&mjs->call_stack));\n\n    if(nargs != psig->args_cnt) {\n        ret = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(\n            mjs, ret, \"got %d actuals, but function takes %d args\", nargs, psig->args_cnt);\n        goto clean;\n    }\n\n    for(i = 0; i < nargs; i++) {\n        mjs_val_t arg = mjs_arg(mjs, i);\n\n        switch(psig->val_types[1 /* retval type */ + i]) {\n        case MJS_FFI_CTYPE_NONE:\n            /*\n         * Void argument: in any case, it's an error, because if C function\n         * takes no arguments, then the FFI-ed JS function should be called\n         * without any arguments, and thus we'll not face \"void\" here.\n         */\n            ret = MJS_TYPE_ERROR;\n            if(i == 0) {\n                /* FFI signature is correct, but invocation is wrong */\n                mjs_prepend_errorf(mjs, ret, \"ffi-ed function takes no arguments\");\n            } else {\n                /*\n           * FFI signature is wrong: we can't have \"void\" as a non-first\n           * \"argument\"\n           */\n                mjs_prepend_errorf(mjs, ret, \"bad ffi arg #%d type: \\\"void\\\"\", i);\n            }\n\n            goto clean;\n        case MJS_FFI_CTYPE_USERDATA:\n            /* Userdata for the callback */\n            if(cbdata.userdata_idx != -1) {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs, ret, \"two or more userdata args: #%d and %d\", cbdata.userdata_idx, i);\n\n                goto clean;\n            }\n            cbdata.userdata = arg;\n            cbdata.userdata_idx = i;\n            break;\n        case MJS_FFI_CTYPE_INT: {\n            int intval = 0;\n            if(mjs_is_number(arg)) {\n                intval = mjs_get_int(mjs, arg);\n            } else if(mjs_is_boolean(arg)) {\n                intval = mjs_get_bool(mjs, arg);\n            } else {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs,\n                    ret,\n                    \"actual arg #%d is not an int (the type idx is: %s)\",\n                    i,\n                    mjs_typeof(arg));\n            }\n            ffi_set_word(&args[i], intval);\n        } break;\n        case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR: {\n            if(!mjs_is_string(arg)) {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs,\n                    ret,\n                    \"actual arg #%d is not a string (the type idx is: %s)\",\n                    i,\n                    mjs_typeof(arg));\n                goto clean;\n            }\n            argvs[i] = arg;\n            argvmgstr[i].p = mjs_get_string(mjs, &argvs[i], &argvmgstr[i].len);\n            /*\n         * String argument should be saved separately in order to support\n         * short strings (which are packed into mjs_val_t itself)\n         */\n            ffi_set_ptr(&args[i], (void*)&argvmgstr[i]);\n            break;\n        }\n        case MJS_FFI_CTYPE_BOOL: {\n            int intval = 0;\n            if(mjs_is_number(arg)) {\n                intval = !!mjs_get_int(mjs, arg);\n            } else if(mjs_is_boolean(arg)) {\n                intval = mjs_get_bool(mjs, arg);\n            } else {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs,\n                    ret,\n                    \"actual arg #%d is not a bool (the type idx is: %s)\",\n                    i,\n                    mjs_typeof(arg));\n            }\n            ffi_set_word(&args[i], intval);\n        } break;\n        case MJS_FFI_CTYPE_DOUBLE:\n            ffi_set_double(&args[i], mjs_get_double(mjs, arg));\n            break;\n        case MJS_FFI_CTYPE_FLOAT:\n            ffi_set_float(&args[i], (float)mjs_get_double(mjs, arg));\n            break;\n        case MJS_FFI_CTYPE_CHAR_PTR: {\n            size_t s;\n            if(mjs_is_string(arg)) {\n                /*\n           * String argument should be saved separately in order to support\n           * short strings (which are packed into mjs_val_t itself)\n           */\n                argvs[i] = arg;\n                ffi_set_ptr(&args[i], (void*)mjs_get_string(mjs, &argvs[i], &s));\n            } else if(mjs_is_null(arg)) {\n                ffi_set_ptr(&args[i], NULL);\n            } else {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs,\n                    ret,\n                    \"actual arg #%d is not a string (the type idx is: %s)\",\n                    i,\n                    mjs_typeof(arg));\n                goto clean;\n            }\n        } break;\n        case MJS_FFI_CTYPE_VOID_PTR:\n            if(mjs_is_string(arg)) {\n                size_t n;\n                /*\n           * String argument should be saved separately in order to support\n           * short strings (which are packed into mjs_val_t itself)\n           */\n                argvs[i] = arg;\n                ffi_set_ptr(&args[i], (void*)mjs_get_string(mjs, &argvs[i], &n));\n            } else if(mjs_is_foreign(arg)) {\n                ffi_set_ptr(&args[i], (void*)mjs_get_ptr(mjs, arg));\n            } else if(mjs_is_null(arg)) {\n                ffi_set_ptr(&args[i], NULL);\n            } else {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(mjs, ret, \"actual arg #%d is not a ptr\", i);\n                goto clean;\n            }\n            break;\n        case MJS_FFI_CTYPE_CALLBACK:\n            if(mjs_is_function(arg) || mjs_is_foreign(arg) || mjs_is_ffi_sig(arg)) {\n                /*\n           * Current argument is a callback function pointer: remember the given\n           * JS function and the argument index\n           */\n                cbdata.func = arg;\n                cbdata.func_idx = i;\n            } else {\n                ret = MJS_TYPE_ERROR;\n                mjs_prepend_errorf(\n                    mjs,\n                    ret,\n                    \"actual arg #%d is not a function, but %s\",\n                    i,\n                    mjs_stringify_type((enum mjs_type)arg));\n                goto clean;\n            }\n            break;\n        case MJS_FFI_CTYPE_INVALID:\n            /* parse_cval_type() has already set a more detailed error */\n            ret = MJS_TYPE_ERROR;\n            mjs_prepend_errorf(mjs, ret, \"wrong arg type\");\n            goto clean;\n        default:\n            abort();\n            break;\n        }\n    }\n\n    if(cbdata.userdata_idx >= 0 && cbdata.func_idx >= 0) {\n        struct mjs_ffi_cb_args* cbargs = NULL;\n        struct mjs_ffi_cb_args** pitem = NULL;\n\n        /* the function takes a callback */\n\n        /*\n     * Get cbargs: either reuse the existing one (if the matching item exists),\n     * or create a new one.\n     */\n        pitem = ffi_get_matching(&mjs->ffi_cb_args, cbdata.func, cbdata.userdata);\n        if(*pitem == NULL) {\n            /* No matching cbargs item; we need to add a new one */\n            cbargs = calloc(1, sizeof(*cbargs));\n            cbargs->mjs = mjs;\n            cbargs->func = cbdata.func;\n            cbargs->userdata = cbdata.userdata;\n            mjs_ffi_sig_copy(&cbargs->sig, psig->cb_sig);\n\n            /* Establish a link to the newly allocated item */\n            *pitem = cbargs;\n        } else {\n            /* Found matching item: reuse it */\n            cbargs = *pitem;\n        }\n\n        {\n            union {\n                ffi_fn_t* fn;\n                void* p;\n            } u;\n            u.fn = psig->cb_sig->fn;\n            ffi_set_ptr(&args[cbdata.func_idx], u.p);\n            ffi_set_ptr(&args[cbdata.userdata_idx], cbargs);\n        }\n    } else if(!(cbdata.userdata_idx == -1 && cbdata.func_idx == -1)) {\n        /*\n     * incomplete signature: it contains either the function pointer or\n     * userdata. It should contain both or none.\n     *\n     * It should be handled in mjs_parse_ffi_signature().\n     */\n        abort();\n    }\n\n    ffi_call_mjs(psig->fn, nargs, &res, args);\n\n    switch(rtype) {\n    case MJS_FFI_CTYPE_CHAR_PTR: {\n        const char* s = (const char*)(uintptr_t)res.v.i;\n        if(s != NULL) {\n            resv = mjs_mk_string(mjs, s, ~0, 1);\n        } else {\n            resv = MJS_NULL;\n        }\n        break;\n    }\n    case MJS_FFI_CTYPE_VOID_PTR:\n        resv = mjs_mk_foreign(mjs, (void*)(uintptr_t)res.v.i);\n        break;\n    case MJS_FFI_CTYPE_INT:\n        resv = mjs_mk_number(mjs, (int)res.v.i);\n        break;\n    case MJS_FFI_CTYPE_BOOL:\n        resv = mjs_mk_boolean(mjs, !!res.v.i);\n        break;\n    case MJS_FFI_CTYPE_DOUBLE:\n        resv = mjs_mk_number(mjs, res.v.d);\n        break;\n    case MJS_FFI_CTYPE_FLOAT:\n        resv = mjs_mk_number(mjs, res.v.f);\n        break;\n    default:\n        resv = mjs_mk_undefined();\n        break;\n    }\n\nclean:\n    /*\n   * If there was some error, prepend an error message with the subject\n   * signature\n   */\n    if(ret != MJS_OK) {\n        mjs_prepend_errorf(mjs, ret, \"failed to call FFIed function\");\n        /* TODO(dfrank) stringify mjs_ffi_sig_t in some human-readable format */\n    }\n    mjs_return(mjs, resv);\n\n    return ret;\n}\n\n/*\n * TODO(dfrank): make it return boolean (when booleans are supported), instead\n * of a number\n */\nMJS_PRIVATE void mjs_ffi_cb_free(struct mjs* mjs) {\n    mjs_val_t ret = mjs_mk_number(mjs, 0);\n    mjs_val_t func = mjs_arg(mjs, 0);\n    mjs_val_t userdata = mjs_arg(mjs, 1);\n\n    if(mjs_is_function(func)) {\n        struct mjs_ffi_cb_args** pitem = ffi_get_matching(&mjs->ffi_cb_args, func, userdata);\n        if(*pitem != NULL) {\n            /* Found matching item: remove it from the linked list, and free */\n            struct mjs_ffi_cb_args* cbargs = *pitem;\n            *pitem = cbargs->next;\n            mjs_ffi_sig_free(&cbargs->sig);\n            free(cbargs);\n            ret = mjs_mk_number(mjs, 1);\n        }\n    } else {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"missing argument 'func'\");\n    }\n\n    mjs_return(mjs, ret);\n}\n\nvoid mjs_ffi_args_free_list(struct mjs* mjs) {\n    ffi_cb_args_t* next = mjs->ffi_cb_args;\n\n    while(next != NULL) {\n        ffi_cb_args_t* cur = next;\n        next = next->next;\n        free(cur);\n    }\n}\n\nMJS_PRIVATE void mjs_ffi_sig_init(mjs_ffi_sig_t* sig) {\n    memset(sig, 0, sizeof(*sig));\n}\n\nMJS_PRIVATE void mjs_ffi_sig_copy(mjs_ffi_sig_t* to, const mjs_ffi_sig_t* from) {\n    memcpy(to, from, sizeof(*to));\n    if(from->cb_sig != NULL) {\n        to->cb_sig = calloc(sizeof(*to->cb_sig), 1);\n        mjs_ffi_sig_copy(to->cb_sig, from->cb_sig);\n    }\n}\n\nMJS_PRIVATE void mjs_ffi_sig_free(mjs_ffi_sig_t* sig) {\n    if(sig->cb_sig != NULL) {\n        free(sig->cb_sig);\n        sig->cb_sig = NULL;\n    }\n}\n\nMJS_PRIVATE int mjs_ffi_sig_set_val_type(mjs_ffi_sig_t* sig, int idx, mjs_ffi_ctype_t type) {\n    if(idx < MJS_CB_SIGNATURE_MAX_SIZE) {\n        sig->val_types[idx] = type;\n        return 1;\n    } else {\n        /* Index is too large */\n        return 0;\n    }\n}\n\nMJS_PRIVATE int\n    mjs_ffi_sig_validate(struct mjs* mjs, mjs_ffi_sig_t* sig, enum ffi_sig_type sig_type) {\n    int ret = 0;\n    int i;\n    int callback_idx = 0;\n    int userdata_idx = 0;\n\n    sig->is_valid = 0;\n\n    switch(sig_type) {\n    case FFI_SIG_FUNC:\n        /* Make sure return type is fine */\n        if(sig->val_types[0] != MJS_FFI_CTYPE_NONE && sig->val_types[0] != MJS_FFI_CTYPE_INT &&\n           sig->val_types[0] != MJS_FFI_CTYPE_BOOL && sig->val_types[0] != MJS_FFI_CTYPE_DOUBLE &&\n           sig->val_types[0] != MJS_FFI_CTYPE_FLOAT &&\n           sig->val_types[0] != MJS_FFI_CTYPE_VOID_PTR &&\n           sig->val_types[0] != MJS_FFI_CTYPE_CHAR_PTR) {\n            mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"invalid return value type\");\n            goto clean;\n        }\n        break;\n    case FFI_SIG_CALLBACK:\n        /* Make sure return type is fine */\n        if(sig->val_types[0] != MJS_FFI_CTYPE_NONE && sig->val_types[0] != MJS_FFI_CTYPE_INT &&\n           sig->val_types[0] != MJS_FFI_CTYPE_BOOL && sig->val_types[0] != MJS_FFI_CTYPE_DOUBLE &&\n           sig->val_types[0] != MJS_FFI_CTYPE_FLOAT &&\n           sig->val_types[0] != MJS_FFI_CTYPE_VOID_PTR) {\n            mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"invalid return value type\");\n            goto clean;\n        }\n    }\n\n    /* Handle argument types */\n    for(i = 1; i < MJS_CB_SIGNATURE_MAX_SIZE; i++) {\n        mjs_ffi_ctype_t type = sig->val_types[i];\n        switch(type) {\n        case MJS_FFI_CTYPE_USERDATA:\n            if(userdata_idx != 0) {\n                /* There must be at most one userdata arg, but we have more */\n                mjs_prepend_errorf(\n                    mjs,\n                    MJS_TYPE_ERROR,\n                    \"more than one userdata arg: #%d and #%d\",\n                    (userdata_idx - 1),\n                    (i - 1));\n                goto clean;\n            }\n            userdata_idx = i;\n            break;\n        case MJS_FFI_CTYPE_CALLBACK:\n            switch(sig_type) {\n            case FFI_SIG_FUNC:\n                break;\n            case FFI_SIG_CALLBACK:\n                mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"callback can't take another callback\");\n                goto clean;\n            }\n            callback_idx = i;\n            break;\n        case MJS_FFI_CTYPE_INT:\n        case MJS_FFI_CTYPE_BOOL:\n        case MJS_FFI_CTYPE_VOID_PTR:\n        case MJS_FFI_CTYPE_CHAR_PTR:\n        case MJS_FFI_CTYPE_STRUCT_MG_STR_PTR:\n        case MJS_FFI_CTYPE_DOUBLE:\n        case MJS_FFI_CTYPE_FLOAT:\n            /* Do nothing */\n            break;\n        case MJS_FFI_CTYPE_NONE:\n            /* No more arguments */\n            goto args_over;\n        default:\n            mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, \"invalid ffi_ctype: %d\", type);\n            goto clean;\n        }\n\n        sig->args_cnt++;\n    }\nargs_over:\n\n    switch(sig_type) {\n    case FFI_SIG_FUNC:\n        if(!((callback_idx > 0 && userdata_idx > 0) || (callback_idx == 0 && userdata_idx == 0))) {\n            mjs_prepend_errorf(\n                mjs,\n                MJS_TYPE_ERROR,\n                \"callback and userdata should be either both \"\n                \"present or both absent\");\n            goto clean;\n        }\n        break;\n    case FFI_SIG_CALLBACK:\n        if(userdata_idx == 0) {\n            /* No userdata arg */\n            mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"no userdata arg\");\n            goto clean;\n        }\n        break;\n    }\n\n    ret = 1;\n\nclean:\n    if(ret) {\n        sig->is_valid = 1;\n    }\n    return ret;\n}\n\nMJS_PRIVATE int mjs_ffi_is_regular_word(mjs_ffi_ctype_t type) {\n    switch(type) {\n    case MJS_FFI_CTYPE_INT:\n    case MJS_FFI_CTYPE_BOOL:\n        return 1;\n    default:\n        return 0;\n    }\n}\n\nMJS_PRIVATE int mjs_ffi_is_regular_word_or_void(mjs_ffi_ctype_t type) {\n    return (type == MJS_FFI_CTYPE_NONE || mjs_ffi_is_regular_word(type));\n}\n\n#ifdef _WIN32\nvoid* dlsym(void* handle, const char* name) {\n    static HANDLE msvcrt_dll;\n    void* sym = NULL;\n    if(msvcrt_dll == NULL) msvcrt_dll = GetModuleHandle(\"msvcrt.dll\");\n    if((sym = GetProcAddress(GetModuleHandle(NULL), name)) == NULL) {\n        sym = GetProcAddress(msvcrt_dll, name);\n    }\n    return sym;\n}\n#elif !defined(__unix__) && !defined(__APPLE__)\nvoid* dlsym(void* handle, const char* name) {\n    (void)handle;\n    (void)name;\n    return NULL;\n}\n#endif\n"
  },
  {
    "path": "lib/mjs/mjs_ffi.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_FFI_H_\n#define MJS_FFI_H_\n\n#include \"ffi/ffi.h\"\n#include \"mjs_ffi_public.h\"\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n#define MJS_CB_ARGS_MAX_CNT 6\n#define MJS_CB_SIGNATURE_MAX_SIZE (MJS_CB_ARGS_MAX_CNT + 1 /* return type */)\n\ntypedef uint8_t mjs_ffi_ctype_t;\n\nenum ffi_sig_type {\n    FFI_SIG_FUNC,\n    FFI_SIG_CALLBACK,\n};\n\n/*\n * Parsed FFI signature\n */\nstruct mjs_ffi_sig {\n    /*\n   * Callback signature, corresponds to the arg of type MJS_FFI_CTYPE_CALLBACK\n   * TODO(dfrank): probably we'll need to support multiple callback/userdata\n   * pairs\n   *\n   * NOTE(dfrank): instances of this structure are grouped into GC arenas and\n   * managed by GC, and for the GC mark to work, the first element should be\n   * a pointer (so that the two LSBs are not used).\n   */\n    struct mjs_ffi_sig* cb_sig;\n\n    /*\n   * The first item is the return value type (for `void`, `MJS_FFI_CTYPE_NONE`\n   * is used); the rest are arguments. If some argument is\n   * `MJS_FFI_CTYPE_NONE`, it means that there are no more arguments.\n   */\n    mjs_ffi_ctype_t val_types[MJS_CB_SIGNATURE_MAX_SIZE];\n\n    /*\n   * Function to call. If `is_callback` is not set, then it's the function\n   * obtained by dlsym; otherwise it's a pointer to the appropriate callback\n   * implementation.\n   */\n    ffi_fn_t* fn;\n\n    /* Number of arguments in the signature */\n    int8_t args_cnt;\n\n    /*\n   * If set, then the signature represents the callback (as opposed to a normal\n   * function), and `fn` points to the suitable callback implementation.\n   */\n    unsigned is_callback : 1;\n    unsigned is_valid : 1;\n};\ntypedef struct mjs_ffi_sig mjs_ffi_sig_t;\n\n/* Initialize new FFI signature */\nMJS_PRIVATE void mjs_ffi_sig_init(mjs_ffi_sig_t* sig);\n/* Copy existing FFI signature */\nMJS_PRIVATE void mjs_ffi_sig_copy(mjs_ffi_sig_t* to, const mjs_ffi_sig_t* from);\n/* Free FFI signature. NOTE: the pointer `sig` itself is not freed */\nMJS_PRIVATE void mjs_ffi_sig_free(mjs_ffi_sig_t* sig);\n\n/*\n * Creates a new FFI signature from the GC arena, and return mjs_val_t which\n * wraps it.\n */\nMJS_PRIVATE mjs_val_t mjs_mk_ffi_sig(struct mjs* mjs);\n\n/*\n * Checks whether the given value is a FFI signature.\n */\nMJS_PRIVATE int mjs_is_ffi_sig(mjs_val_t v);\n\n/*\n * Wraps FFI signature structure into mjs_val_t value.\n */\nMJS_PRIVATE mjs_val_t mjs_ffi_sig_to_value(struct mjs_ffi_sig* psig);\n\n/*\n * Extracts a pointer to the FFI signature struct from the mjs_val_t value.\n */\nMJS_PRIVATE struct mjs_ffi_sig* mjs_get_ffi_sig_struct(mjs_val_t v);\n\n/*\n * A wrapper for mjs_ffi_sig_free() suitable to use as a GC cell destructor.\n */\nMJS_PRIVATE void mjs_ffi_sig_destructor(struct mjs* mjs, void* psig);\n\nMJS_PRIVATE int mjs_ffi_sig_set_val_type(mjs_ffi_sig_t* sig, int idx, mjs_ffi_ctype_t type);\nMJS_PRIVATE int\n    mjs_ffi_sig_validate(struct mjs* mjs, mjs_ffi_sig_t* sig, enum ffi_sig_type sig_type);\nMJS_PRIVATE int mjs_ffi_is_regular_word(mjs_ffi_ctype_t type);\nMJS_PRIVATE int mjs_ffi_is_regular_word_or_void(mjs_ffi_ctype_t type);\n\nstruct mjs_ffi_cb_args {\n    struct mjs_ffi_cb_args* next;\n    struct mjs* mjs;\n    mjs_ffi_sig_t sig;\n    mjs_val_t func;\n    mjs_val_t userdata;\n};\ntypedef struct mjs_ffi_cb_args ffi_cb_args_t;\n\n/*\n * cfunction:\n * Parses the FFI signature string and returns a value wrapping mjs_ffi_sig_t.\n */\nMJS_PRIVATE mjs_err_t mjs_ffi_call(struct mjs* mjs);\n\n/*\n * cfunction:\n * Performs the FFI signature call.\n */\nMJS_PRIVATE mjs_err_t mjs_ffi_call2(struct mjs* mjs);\n\nMJS_PRIVATE void mjs_ffi_cb_free(struct mjs*);\nMJS_PRIVATE void mjs_ffi_args_free_list(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_FFI_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_ffi_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_FFI_PUBLIC_H_\n#define MJS_FFI_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nenum mjs_ffi_ctype {\n    MJS_FFI_CTYPE_NONE,\n    MJS_FFI_CTYPE_USERDATA,\n    MJS_FFI_CTYPE_CALLBACK,\n    MJS_FFI_CTYPE_INT,\n    MJS_FFI_CTYPE_BOOL,\n    MJS_FFI_CTYPE_DOUBLE,\n    MJS_FFI_CTYPE_FLOAT,\n    MJS_FFI_CTYPE_CHAR_PTR,\n    MJS_FFI_CTYPE_VOID_PTR,\n    MJS_FFI_CTYPE_STRUCT_MG_STR_PTR,\n    MJS_FFI_CTYPE_STRUCT_MG_STR,\n    MJS_FFI_CTYPE_INVALID,\n};\n\ntypedef void*(mjs_ffi_resolver_t)(void* handle, const char* symbol);\n\nvoid mjs_set_ffi_resolver(struct mjs* mjs, mjs_ffi_resolver_t* dlsym, void* handle);\n\nvoid* mjs_ffi_resolve(struct mjs* mjs, const char* symbol);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_FFI_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_gc.c",
    "content": "/*\n * Copyright (c) 2014 Cesanta Software Limited\n * All rights reserved\n */\n\n#include <stdio.h>\n\n#include \"common/cs_varint.h\"\n#include \"common/mbuf.h\"\n\n#include \"mjs_core.h\"\n#include \"mjs_ffi.h\"\n#include \"mjs_gc.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n\n/*\n * Macros for marking reachable things: use bit 0.\n */\n#define MARK(p) (((struct gc_cell*)(p))->head.word |= 1)\n#define UNMARK(p) (((struct gc_cell*)(p))->head.word &= ~1)\n#define MARKED(p) (((struct gc_cell*)(p))->head.word & 1)\n\n/*\n * Similar to `MARK()` / `UNMARK()` / `MARKED()`, but `.._FREE` counterparts\n * are intended to mark free cells (as opposed to used ones), so they use\n * bit 1.\n */\n#define MARK_FREE(p) (((struct gc_cell*)(p))->head.word |= 2)\n#define UNMARK_FREE(p) (((struct gc_cell*)(p))->head.word &= ~2)\n#define MARKED_FREE(p) (((struct gc_cell*)(p))->head.word & 2)\n\n/*\n * When each arena has that or less free cells, GC will be scheduled\n */\n#define GC_ARENA_CELLS_RESERVE 2\n\nstatic struct gc_block* gc_new_block(struct gc_arena* a, size_t size);\nstatic void gc_free_block(struct gc_block* b);\nstatic void gc_mark_mbuf_pt(struct mjs* mjs, const struct mbuf* mbuf);\n\nMJS_PRIVATE struct mjs_object* new_object(struct mjs* mjs) {\n    return (struct mjs_object*)gc_alloc_cell(mjs, &mjs->object_arena);\n}\n\nMJS_PRIVATE struct mjs_property* new_property(struct mjs* mjs) {\n    return (struct mjs_property*)gc_alloc_cell(mjs, &mjs->property_arena);\n}\n\nMJS_PRIVATE struct mjs_ffi_sig* new_ffi_sig(struct mjs* mjs) {\n    return (struct mjs_ffi_sig*)gc_alloc_cell(mjs, &mjs->ffi_sig_arena);\n}\n\n/* Initializes a new arena. */\nMJS_PRIVATE void gc_arena_init(\n    struct gc_arena* a,\n    size_t cell_size,\n    size_t initial_size,\n    size_t size_increment) {\n    assert(cell_size >= sizeof(uintptr_t));\n\n    memset(a, 0, sizeof(*a));\n    a->cell_size = cell_size;\n    a->size_increment = size_increment;\n    a->blocks = gc_new_block(a, initial_size);\n}\n\nMJS_PRIVATE void gc_arena_destroy(struct mjs* mjs, struct gc_arena* a) {\n    struct gc_block* b;\n\n    if(a->blocks != NULL) {\n        gc_sweep(mjs, a, 0);\n        for(b = a->blocks; b != NULL;) {\n            struct gc_block* tmp;\n            tmp = b;\n            b = b->next;\n            gc_free_block(tmp);\n        }\n    }\n}\n\nstatic void gc_free_block(struct gc_block* b) {\n    free(b->base);\n    free(b);\n}\n\nstatic struct gc_block* gc_new_block(struct gc_arena* a, size_t size) {\n    struct gc_cell* cur;\n    struct gc_block* b;\n\n    b = (struct gc_block*)calloc(1, sizeof(*b));\n    if(b == NULL) abort();\n\n    b->size = size;\n    b->base = (struct gc_cell*)calloc(a->cell_size, b->size);\n    if(b->base == NULL) abort();\n\n    for(cur = GC_CELL_OP(a, b->base, +, 0); cur < GC_CELL_OP(a, b->base, +, b->size);\n        cur = GC_CELL_OP(a, cur, +, 1)) {\n        cur->head.link = a->free;\n        a->free = cur;\n    }\n\n    return b;\n}\n\n/*\n * Returns whether the given arena has GC_ARENA_CELLS_RESERVE or less free\n * cells\n */\nstatic int gc_arena_is_gc_needed(struct gc_arena* a) {\n    struct gc_cell* r = a->free;\n    int i;\n\n    for(i = 0; i <= GC_ARENA_CELLS_RESERVE; i++, r = r->head.link) {\n        if(r == NULL) {\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\nMJS_PRIVATE int gc_strings_is_gc_needed(struct mjs* mjs) {\n    struct mbuf* m = &mjs->owned_strings;\n    return (double)m->len / (double)m->size > (double)0.9;\n}\n\nMJS_PRIVATE void* gc_alloc_cell(struct mjs* mjs, struct gc_arena* a) {\n    struct gc_cell* r;\n\n    if(a->free == NULL) {\n        struct gc_block* b = gc_new_block(a, a->size_increment);\n        b->next = a->blocks;\n        a->blocks = b;\n    }\n    r = a->free;\n\n    UNMARK(r);\n\n    a->free = r->head.link;\n\n#if MJS_MEMORY_STATS\n    a->allocations++;\n    a->alive++;\n#endif\n\n    /* Schedule GC if needed */\n    if(gc_arena_is_gc_needed(a)) {\n        mjs->need_gc = 1;\n    }\n\n    /*\n   * TODO(mkm): minor opt possible since most of the fields\n   * are overwritten downstream, but not worth the yak shave time\n   * when fields are added to GC-able structures */\n    memset(r, 0, a->cell_size);\n    return (void*)r;\n}\n\n/*\n * Scans the arena and add all unmarked cells to the free list.\n *\n * Empty blocks get deallocated. The head of the free list will contais cells\n * from the last (oldest) block. Cells will thus be allocated in block order.\n */\nvoid gc_sweep(struct mjs* mjs, struct gc_arena* a, size_t start) {\n    struct gc_block* b;\n    struct gc_cell* cur;\n    struct gc_block** prevp = &a->blocks;\n#if MJS_MEMORY_STATS\n    a->alive = 0;\n#endif\n\n    /*\n   * Before we sweep, we should mark all free cells in a way that is\n   * distinguishable from marked used cells.\n   */\n    {\n        struct gc_cell* next;\n        for(cur = a->free; cur != NULL; cur = next) {\n            next = cur->head.link;\n            MARK_FREE(cur);\n        }\n    }\n\n    /*\n   * We'll rebuild the whole `free` list, so initially we just reset it\n   */\n    a->free = NULL;\n\n    for(b = a->blocks; b != NULL;) {\n        size_t freed_in_block = 0;\n        /*\n     * if it turns out that this block is 100% garbage\n     * we can release the whole block, but the addition\n     * of it's cells to the free list has to be undone.\n     */\n        struct gc_cell* prev_free = a->free;\n\n        for(cur = GC_CELL_OP(a, b->base, +, start); cur < GC_CELL_OP(a, b->base, +, b->size);\n            cur = GC_CELL_OP(a, cur, +, 1)) {\n            if(MARKED(cur)) {\n                /* The cell is used and marked  */\n                UNMARK(cur);\n#if MJS_MEMORY_STATS\n                a->alive++;\n#endif\n            } else {\n                /*\n         * The cell is either:\n         * - free\n         * - garbage that's about to be freed\n         */\n\n                if(MARKED_FREE(cur)) {\n                    /* The cell is free, so, just unmark it */\n                    UNMARK_FREE(cur);\n                } else {\n                    /*\n           * The cell is used and should be freed: call the destructor and\n           * reset the memory\n           */\n                    if(a->destructor != NULL) {\n                        a->destructor(mjs, cur);\n                    }\n                    memset(cur, 0, a->cell_size);\n                }\n\n                /* Add this cell to the `free` list */\n                cur->head.link = a->free;\n                a->free = cur;\n                freed_in_block++;\n#if MJS_MEMORY_STATS\n                a->garbage++;\n#endif\n            }\n        }\n\n        /*\n     * don't free the initial block, which is at the tail\n     * because it has a special size aimed at reducing waste\n     * and simplifying initial startup. TODO(mkm): improve\n     * */\n        if(b->next != NULL && freed_in_block == b->size) {\n            *prevp = b->next;\n            gc_free_block(b);\n            b = *prevp;\n            a->free = prev_free;\n        } else {\n            prevp = &b->next;\n            b = b->next;\n        }\n    }\n}\n\n/* Mark an FFI signature */\nstatic void gc_mark_ffi_sig(struct mjs* mjs, mjs_val_t* v) {\n    struct mjs_ffi_sig* psig;\n\n    assert(mjs_is_ffi_sig(*v));\n\n    psig = mjs_get_ffi_sig_struct(*v);\n\n    /*\n   * we treat all object like things like objects but they might be functions,\n   * gc_check_val checks the appropriate arena per actual value type.\n   */\n    if(!gc_check_val(mjs, *v)) {\n        abort();\n    }\n\n    if(MARKED(psig)) return;\n\n    MARK(psig);\n}\n\n/* Mark an object */\nstatic void gc_mark_object(struct mjs* mjs, mjs_val_t* v) {\n    struct mjs_object* obj_base;\n    struct mjs_property* prop;\n    struct mjs_property* next;\n\n    assert(mjs_is_object_based(*v));\n\n    obj_base = get_object_struct(*v);\n\n    /*\n   * we treat all object like things like objects but they might be functions,\n   * gc_check_val checks the appropriate arena per actual value type.\n   */\n    if(!gc_check_val(mjs, *v)) {\n        abort();\n    }\n\n    if(MARKED(obj_base)) return;\n\n    /* mark object itself, and its properties */\n    for((prop = obj_base->properties), MARK(obj_base); prop != NULL; prop = next) {\n        if(!gc_check_ptr(&mjs->property_arena, prop)) {\n            abort();\n        }\n\n        gc_mark(mjs, &prop->name);\n        gc_mark(mjs, &prop->value);\n\n        next = prop->next;\n        MARK(prop);\n    }\n\n    /* mark object's prototype */\n    /*\n   * We dropped support for object prototypes in MJS.\n   * If we ever bring it back, don't forget to mark it\n   */\n    /* gc_mark(mjs, mjs_get_proto(mjs, v)); */\n}\n\n/* Mark a string value */\nstatic void gc_mark_string(struct mjs* mjs, mjs_val_t* v) {\n    mjs_val_t h, tmp = 0;\n    char* s;\n\n    /* clang-format off */\n\n  /*\n   * If a value points to an unmarked string we shall:\n   *  1. save the first 6 bytes of the string\n   *     since we need to be able to distinguish real values from\n   *     the saved first 6 bytes of the string, we need to tag the chunk\n   *     as MJS_TAG_STRING_C\n   *  2. encode value's address (v) into the first 6 bytes of the string.\n   *  3. put the saved 8 bytes (tag + chunk) back into the value.\n   *  4. mark the string by putting '\\1' in the NUL terminator of the previous\n   *     string chunk.\n   *\n   * If a value points to an already marked string we shall:\n   *     (0, <6 bytes of a pointer to a mjs_val_t>), hence we have to skip\n   *     the first byte. We tag the value pointer as a MJS_TAG_FOREIGN\n   *     so that it won't be followed during recursive mark.\n   *\n   *  ... the rest is the same\n   *\n   *  Note: 64-bit pointers can be represented with 48-bits\n   */\n\n    /* clang-format on */\n\n    assert((*v & MJS_TAG_MASK) == MJS_TAG_STRING_O);\n\n    s = mjs->owned_strings.buf + gc_string_mjs_val_to_offset(*v);\n    assert(s < mjs->owned_strings.buf + mjs->owned_strings.len);\n    if(s[-1] == '\\0') {\n        memcpy(&tmp, s, sizeof(tmp) - 2);\n        tmp |= MJS_TAG_STRING_C;\n    } else {\n        memcpy(&tmp, s, sizeof(tmp) - 2);\n        tmp |= MJS_TAG_FOREIGN;\n    }\n\n    h = (mjs_val_t)(uintptr_t)v;\n    s[-1] = 1;\n    memcpy(s, &h, sizeof(h) - 2);\n    memcpy(v, &tmp, sizeof(tmp));\n}\n\nMJS_PRIVATE void gc_mark(struct mjs* mjs, mjs_val_t* v) {\n    if(mjs_is_object_based(*v)) {\n        gc_mark_object(mjs, v);\n    }\n    if(mjs_is_ffi_sig(*v)) {\n        gc_mark_ffi_sig(mjs, v);\n    }\n    if((*v & MJS_TAG_MASK) == MJS_TAG_STRING_O) {\n        gc_mark_string(mjs, v);\n    }\n}\n\nMJS_PRIVATE uint64_t gc_string_mjs_val_to_offset(mjs_val_t v) {\n    return (((uint64_t)(uintptr_t)get_ptr(v)) & ~MJS_TAG_MASK);\n}\n\nMJS_PRIVATE mjs_val_t gc_string_val_from_offset(uint64_t s) {\n    return s | MJS_TAG_STRING_O;\n}\n\nvoid gc_compact_strings(struct mjs* mjs) {\n    char* p = mjs->owned_strings.buf + 1;\n    uint64_t h, next, head = 1;\n    int len, llen;\n\n    while(p < mjs->owned_strings.buf + mjs->owned_strings.len) {\n        if(p[-1] == '\\1') {\n            /* relocate and update ptrs */\n            h = 0;\n            memcpy(&h, p, sizeof(h) - 2);\n\n            /*\n       * relocate pointers until we find the tail.\n       * The tail is marked with MJS_TAG_STRING_C,\n       * while mjs_val_t link pointers are tagged with MJS_TAG_FOREIGN\n       */\n            for(; (h & MJS_TAG_MASK) != MJS_TAG_STRING_C; h = next) {\n                h &= ~MJS_TAG_MASK;\n                memcpy(&next, (char*)(uintptr_t)h, sizeof(h));\n\n                *(mjs_val_t*)(uintptr_t)h = gc_string_val_from_offset(head);\n            }\n            h &= ~MJS_TAG_MASK;\n\n            /*\n       * the tail contains the first 6 bytes we stole from\n       * the actual string.\n       */\n            len = cs_varint_decode_unsafe((unsigned char*)&h, &llen);\n            len += llen + 1;\n\n            /*\n       * restore the saved 6 bytes\n       * TODO(mkm): think about endianness\n       */\n            memcpy(p, &h, sizeof(h) - 2);\n\n            /*\n       * and relocate the string data by packing it to the left.\n       */\n            memmove(mjs->owned_strings.buf + head, p, len);\n            mjs->owned_strings.buf[head - 1] = 0x0;\n            p += len;\n            head += len;\n        } else {\n            len = cs_varint_decode_unsafe((unsigned char*)p, &llen);\n            len += llen + 1;\n\n            p += len;\n        }\n    }\n\n    mjs->owned_strings.len = head;\n}\n\nMJS_PRIVATE int maybe_gc(struct mjs* mjs) {\n    if(!mjs->inhibit_gc) {\n        mjs_gc(mjs, 0);\n        return 1;\n    }\n    return 0;\n}\n\n/*\n * mark an array of `mjs_val_t` values (*not pointers* to them)\n */\nstatic void gc_mark_val_array(struct mjs* mjs, mjs_val_t* vals, size_t len) {\n    mjs_val_t* vp;\n    for(vp = vals; vp < vals + len; vp++) {\n        gc_mark(mjs, vp);\n    }\n}\n\n/*\n * mark an mbuf containing *pointers* to `mjs_val_t` values\n */\nstatic void gc_mark_mbuf_pt(struct mjs* mjs, const struct mbuf* mbuf) {\n    mjs_val_t** vp;\n    for(vp = (mjs_val_t**)mbuf->buf; (char*)vp < mbuf->buf + mbuf->len; vp++) {\n        gc_mark(mjs, *vp);\n    }\n}\n\n/*\n * mark an mbuf containing `mjs_val_t` values (*not pointers* to them)\n */\nstatic void gc_mark_mbuf_val(struct mjs* mjs, const struct mbuf* mbuf) {\n    gc_mark_val_array(mjs, (mjs_val_t*)mbuf->buf, mbuf->len / sizeof(mjs_val_t));\n}\n\nstatic void gc_mark_ffi_cbargs_list(struct mjs* mjs, ffi_cb_args_t* cbargs) {\n    for(; cbargs != NULL; cbargs = cbargs->next) {\n        gc_mark(mjs, &cbargs->func);\n        gc_mark(mjs, &cbargs->userdata);\n    }\n}\n\n/* Perform garbage collection */\nvoid mjs_gc(struct mjs* mjs, int full) {\n    gc_mark_val_array(mjs, (mjs_val_t*)&mjs->vals, sizeof(mjs->vals) / sizeof(mjs_val_t));\n\n    gc_mark_mbuf_pt(mjs, &mjs->owned_values);\n    gc_mark_mbuf_val(mjs, &mjs->scopes);\n    gc_mark_mbuf_val(mjs, &mjs->stack);\n    gc_mark_mbuf_val(mjs, &mjs->call_stack);\n\n    gc_mark_ffi_cbargs_list(mjs, mjs->ffi_cb_args);\n\n    gc_compact_strings(mjs);\n\n    gc_sweep(mjs, &mjs->object_arena, 0);\n    gc_sweep(mjs, &mjs->property_arena, 0);\n    gc_sweep(mjs, &mjs->ffi_sig_arena, 0);\n\n    if(full) {\n        /*\n     * In case of full GC, we also resize strings buffer, but we still leave\n     * some extra space (at most, `_MJS_STRING_BUF_RESERVE`) in order to avoid\n     * frequent reallocations\n     */\n        size_t trimmed_size = mjs->owned_strings.len + _MJS_STRING_BUF_RESERVE;\n        if(trimmed_size < mjs->owned_strings.size) {\n            mbuf_resize(&mjs->owned_strings, trimmed_size);\n        }\n    }\n}\n\nMJS_PRIVATE int gc_check_val(struct mjs* mjs, mjs_val_t v) {\n    if(mjs_is_object_based(v)) {\n        return gc_check_ptr(&mjs->object_arena, get_object_struct(v));\n    }\n    if(mjs_is_ffi_sig(v)) {\n        return gc_check_ptr(&mjs->ffi_sig_arena, mjs_get_ffi_sig_struct(v));\n    }\n    return 1;\n}\n\nMJS_PRIVATE int gc_check_ptr(const struct gc_arena* a, const void* ptr) {\n    const struct gc_cell* p = (const struct gc_cell*)ptr;\n    struct gc_block* b;\n    for(b = a->blocks; b != NULL; b = b->next) {\n        if(p >= b->base && p < GC_CELL_OP(a, b->base, +, b->size)) {\n            return 1;\n        }\n    }\n    return 0;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_gc.h",
    "content": "/*\n * Copyright (c) 2014 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_GC_H_\n#define MJS_GC_H_\n\n#include \"mjs_core.h\"\n#include \"mjs_mm.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_gc_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * performs arithmetics on gc_cell pointers as if they were arena->cell_size\n * bytes wide\n */\n#define GC_CELL_OP(arena, cell, op, arg) \\\n    ((struct gc_cell*)(((char*)(cell))op((arg) * (arena)->cell_size)))\n\nstruct gc_cell {\n    union {\n        struct gc_cell* link;\n        uintptr_t word;\n    } head;\n};\n\nMJS_PRIVATE int gc_strings_is_gc_needed(struct mjs* mjs);\n\n/* perform gc if not inhibited */\nMJS_PRIVATE int maybe_gc(struct mjs* mjs);\n\nMJS_PRIVATE struct mjs_object* new_object(struct mjs*);\nMJS_PRIVATE struct mjs_property* new_property(struct mjs*);\nMJS_PRIVATE struct mjs_ffi_sig* new_ffi_sig(struct mjs* mjs);\n\nMJS_PRIVATE void gc_mark(struct mjs* mjs, mjs_val_t* val);\n\nMJS_PRIVATE void gc_arena_init(struct gc_arena*, size_t, size_t, size_t);\nMJS_PRIVATE void gc_arena_destroy(struct mjs*, struct gc_arena* a);\nMJS_PRIVATE void gc_sweep(struct mjs*, struct gc_arena*, size_t);\nMJS_PRIVATE void* gc_alloc_cell(struct mjs*, struct gc_arena*);\n\nMJS_PRIVATE uint64_t gc_string_mjs_val_to_offset(mjs_val_t v);\n\n/* return 0 if v is an object/function with a bad pointer */\nMJS_PRIVATE int gc_check_val(struct mjs* mjs, mjs_val_t v);\n\n/* checks whether a pointer is within the ranges of an arena */\nMJS_PRIVATE int gc_check_ptr(const struct gc_arena* a, const void* p);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_GC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_gc_public.h",
    "content": "/*\n * Copyright (c) 2014 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_GC_PUBLIC_H_\n#define MJS_GC_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Perform garbage collection.\n * Pass true to full in order to reclaim unused heap back to the OS.\n */\nvoid mjs_gc(struct mjs* mjs, int full);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_GC_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_internal.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_INTERNAL_H_\n#define MJS_INTERNAL_H_\n\n#include <assert.h>\n#include <ctype.h>\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifndef FAST\n#define FAST\n#endif\n\n#ifndef STATIC\n#define STATIC\n#endif\n\n#ifndef ENDL\n#define ENDL \"\\n\"\n#endif\n\n#ifndef MJS_EXPOSE_PRIVATE\n#define MJS_EXPOSE_PRIVATE 1\n#endif\n\n#if MJS_EXPOSE_PRIVATE\n#define MJS_PRIVATE\n#else\n#define MJS_PRIVATE static\n#endif\n\n#ifndef ARRAY_SIZE\n#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))\n#endif\n\n#if !defined(WEAK)\n#if(defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)\n#define WEAK __attribute__((weak))\n#else\n#define WEAK\n#endif\n#endif\n\n#ifndef CS_ENABLE_STDIO\n#define CS_ENABLE_STDIO 1\n#endif\n\n#include \"common/cs_dbg.h\"\n#include \"common/cs_file.h\"\n#include \"common/mbuf.h\"\n\n#if defined(_WIN32) && _MSC_VER < 1700\ntypedef signed char int8_t;\ntypedef unsigned char uint8_t;\ntypedef int int32_t;\ntypedef unsigned int uint32_t;\ntypedef short int16_t;\ntypedef unsigned short uint16_t;\ntypedef __int64 int64_t;\ntypedef unsigned long uintptr_t;\n#define STRX(x) #x\n#define STR(x) STRX(x)\n#define __func__ __FILE__ \":\" STR(__LINE__)\n// #define snprintf _snprintf\n#define vsnprintf _vsnprintf\n#define isnan(x) _isnan(x)\n#define va_copy(x, y) (x) = (y)\n#define CS_DEFINE_DIRENT\n#include <windows.h>\n#else\n#if defined(__unix__) || defined(__APPLE__)\n#include <dlfcn.h>\n#endif\n#endif\n\n/*\n * Number of bytes reserved for the jump offset initially. The most practical\n * value is 1, but for testing it's useful to set it to 0 and to some large\n * value as well (like, 4), to make sure that the code behaves correctly under\n * all circumstances.\n */\n#ifndef MJS_INIT_OFFSET_SIZE\n#define MJS_INIT_OFFSET_SIZE 1\n#endif\n\n#endif /* MJS_INTERNAL_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_json.c",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/str_util.h\"\n#include \"common/frozen/frozen.h\"\n#include \"mjs_array.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_core.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util_public.h\"\n\n#define BUF_LEFT(size, used) (((size_t)(used) < (size)) ? ((size) - (used)) : 0)\n\n/*\n * Returns whether the value of given type should be skipped when generating\n * JSON output\n *\n * So far it always returns 0, but we might add some logic later, if we\n * implement some non-jsonnable objects\n */\nstatic int should_skip_for_json(enum mjs_type type) {\n    int ret;\n    switch(type) {\n    /* All permitted values */\n    case MJS_TYPE_NULL:\n    case MJS_TYPE_BOOLEAN:\n    case MJS_TYPE_NUMBER:\n    case MJS_TYPE_STRING:\n    case MJS_TYPE_ARRAY_BUF:\n    case MJS_TYPE_ARRAY_BUF_VIEW:\n    case MJS_TYPE_OBJECT_GENERIC:\n    case MJS_TYPE_OBJECT_ARRAY:\n        ret = 0;\n        break;\n    default:\n        ret = 1;\n        break;\n    }\n    return ret;\n}\n\nstatic const char* hex_digits = \"0123456789abcdef\";\nstatic char* append_hex(char* buf, char* limit, uint8_t c) {\n    if(buf < limit) *buf++ = 'u';\n    if(buf < limit) *buf++ = '0';\n    if(buf < limit) *buf++ = '0';\n    if(buf < limit) *buf++ = hex_digits[(int)((c >> 4) % 0xf)];\n    if(buf < limit) *buf++ = hex_digits[(int)(c & 0xf)];\n    return buf;\n}\n\n/*\n * Appends quoted s to buf. Any double quote contained in s will be escaped.\n * Returns the number of characters that would have been added,\n * like snprintf.\n * If size is zero it doesn't output anything but keeps counting.\n */\nstatic int snquote(char* buf, size_t size, const char* s, size_t len) {\n    char* limit = buf + size;\n    const char* end;\n    /*\n   * String single character escape sequence:\n   * http://www.ecma-international.org/ecma-262/6.0/index.html#table-34\n   *\n   * 0x8 -> \\b\n   * 0x9 -> \\t\n   * 0xa -> \\n\n   * 0xb -> \\v\n   * 0xc -> \\f\n   * 0xd -> \\r\n   */\n    const char* specials = \"btnvfr\";\n    size_t i = 0;\n\n    i++;\n    if(buf < limit) *buf++ = '\"';\n\n    for(end = s + len; s < end; s++) {\n        if(*s == '\"' || *s == '\\\\') {\n            i++;\n            if(buf < limit) *buf++ = '\\\\';\n        } else if(*s >= '\\b' && *s <= '\\r') {\n            i += 2;\n            if(buf < limit) *buf++ = '\\\\';\n            if(buf < limit) *buf++ = specials[*s - '\\b'];\n            continue;\n        } else if((unsigned char)*s < '\\b' || (*s > '\\r' && *s < ' ')) {\n            i += 6 /* \\uXX XX */;\n            if(buf < limit) *buf++ = '\\\\';\n            buf = append_hex(buf, limit, (uint8_t)*s);\n            continue;\n        }\n        i++;\n        if(buf < limit) *buf++ = *s;\n    }\n\n    i++;\n    if(buf < limit) *buf++ = '\"';\n\n    if(buf < limit) {\n        *buf = '\\0';\n    } else if(size != 0) {\n        /*\n     * There is no room for the NULL char, but the size wasn't zero, so we can\n     * safely put NULL in the previous byte\n     */\n        *(buf - 1) = '\\0';\n    }\n    return i;\n}\n\nMJS_PRIVATE mjs_err_t to_json_or_debug(\n    struct mjs* mjs,\n    mjs_val_t v,\n    char* buf,\n    size_t size,\n    size_t* res_len,\n    uint8_t is_debug) {\n    mjs_val_t el;\n    char* vp;\n    mjs_err_t rcode = MJS_OK;\n    size_t len = 0;\n    /*\n   * TODO(dfrank) : also push all `mjs_val_t`s that are declared below\n   */\n\n    if(size > 0) *buf = '\\0';\n\n    if(!is_debug && should_skip_for_json(mjs_get_type(v))) {\n        goto clean;\n    }\n\n    for(vp = mjs->json_visited_stack.buf;\n        vp < mjs->json_visited_stack.buf + mjs->json_visited_stack.len;\n        vp += sizeof(mjs_val_t)) {\n        if(*(mjs_val_t*)vp == v) {\n            len = strlcpy(buf, \"[Circular]\", size);\n            goto clean;\n        }\n    }\n\n    switch(mjs_get_type(v)) {\n    case MJS_TYPE_NULL:\n    case MJS_TYPE_BOOLEAN:\n    case MJS_TYPE_NUMBER:\n    case MJS_TYPE_UNDEFINED:\n    case MJS_TYPE_FOREIGN:\n    case MJS_TYPE_ARRAY_BUF:\n    case MJS_TYPE_ARRAY_BUF_VIEW:\n        /* For those types, regular `mjs_to_string()` works */\n        {\n            /* refactor: mjs_to_string allocates memory every time */\n            char* p = NULL;\n            int need_free = 0;\n            rcode = mjs_to_string(mjs, &v, &p, &len, &need_free);\n            c_snprintf(buf, size, \"%.*s\", (int)len, p);\n            if(need_free) {\n                free(p);\n            }\n        }\n        goto clean;\n\n    case MJS_TYPE_STRING: {\n        /*\n       * For strings we can't just use `primitive_to_str()`, because we need\n       * quoted value\n       */\n        size_t n;\n        const char* str = mjs_get_string(mjs, &v, &n);\n        len = snquote(buf, size, str, n);\n        goto clean;\n    }\n\n    case MJS_TYPE_OBJECT_FUNCTION:\n    case MJS_TYPE_OBJECT_GENERIC: {\n        char* b = buf;\n        struct mjs_property* prop = NULL;\n        struct mjs_object* o = NULL;\n\n        mbuf_append(&mjs->json_visited_stack, (char*)&v, sizeof(v));\n        b += c_snprintf(b, BUF_LEFT(size, b - buf), \"{\");\n        o = get_object_struct(v);\n        for(prop = o->properties; prop != NULL; prop = prop->next) {\n            size_t n;\n            const char* s;\n            if(!is_debug && should_skip_for_json(mjs_get_type(prop->value))) {\n                continue;\n            }\n            if(b - buf != 1) { /* Not the first property to be printed */\n                b += c_snprintf(b, BUF_LEFT(size, b - buf), \",\");\n            }\n            s = mjs_get_string(mjs, &prop->name, &n);\n            b += c_snprintf(b, BUF_LEFT(size, b - buf), \"\\\"%.*s\\\":\", (int)n, s);\n            {\n                size_t tmp = 0;\n                rcode =\n                    to_json_or_debug(mjs, prop->value, b, BUF_LEFT(size, b - buf), &tmp, is_debug);\n                if(rcode != MJS_OK) {\n                    goto clean_iter;\n                }\n                b += tmp;\n            }\n        }\n\n        b += c_snprintf(b, BUF_LEFT(size, b - buf), \"}\");\n        mjs->json_visited_stack.len -= sizeof(v);\n\n    clean_iter:\n        len = b - buf;\n        goto clean;\n    }\n    case MJS_TYPE_OBJECT_ARRAY: {\n        int has;\n        char* b = buf;\n        size_t i, alen = mjs_array_length(mjs, v);\n        mbuf_append(&mjs->json_visited_stack, (char*)&v, sizeof(v));\n        b += c_snprintf(b, BUF_LEFT(size, b - buf), \"[\");\n        for(i = 0; i < alen; i++) {\n            el = mjs_array_get2(mjs, v, i, &has);\n            if(has) {\n                size_t tmp = 0;\n                if(!is_debug && should_skip_for_json(mjs_get_type(el))) {\n                    b += c_snprintf(b, BUF_LEFT(size, b - buf), \"null\");\n                } else {\n                    rcode = to_json_or_debug(mjs, el, b, BUF_LEFT(size, b - buf), &tmp, is_debug);\n                    if(rcode != MJS_OK) {\n                        goto clean;\n                    }\n                }\n                b += tmp;\n            } else {\n                b += c_snprintf(b, BUF_LEFT(size, b - buf), \"null\");\n            }\n            if(i != alen - 1) {\n                b += c_snprintf(b, BUF_LEFT(size, b - buf), \",\");\n            }\n        }\n        b += c_snprintf(b, BUF_LEFT(size, b - buf), \"]\");\n        mjs->json_visited_stack.len -= sizeof(v);\n        len = b - buf;\n        goto clean;\n    }\n\n    case MJS_TYPES_CNT:\n        abort();\n    }\n\n    abort();\n\n    len = 0; /* for compilers that don't know about abort() */\n    goto clean;\n\nclean:\n    if(rcode != MJS_OK) {\n        len = 0;\n    }\n    if(res_len != NULL) {\n        *res_len = len;\n    }\n    return rcode;\n}\n\nMJS_PRIVATE mjs_err_t\n    mjs_json_stringify(struct mjs* mjs, mjs_val_t v, char* buf, size_t size, char** res) {\n    mjs_err_t rcode = MJS_OK;\n    char* p = buf;\n    size_t len;\n\n    to_json_or_debug(mjs, v, buf, size, &len, 0);\n\n    if(len >= size) {\n        /* Buffer is not large enough. Allocate a bigger one */\n        p = (char*)malloc(len + 1);\n        rcode = mjs_json_stringify(mjs, v, p, len + 1, res);\n        assert(*res == p);\n        goto clean;\n    } else {\n        *res = p;\n        goto clean;\n    }\n\nclean:\n    /*\n   * If we're going to return an error, and we allocated a buffer, then free\n   * it. Otherwise, caller should free it.\n   */\n    if(rcode != MJS_OK && p != buf) {\n        free(p);\n    }\n    return rcode;\n}\n\n/*\n * JSON parsing frame: a separate frame is allocated for each nested\n * object/array during parsing\n */\nstruct json_parse_frame {\n    mjs_val_t val;\n    struct json_parse_frame* up;\n};\n\n/*\n * Context for JSON parsing by means of json_walk()\n */\nstruct json_parse_ctx {\n    struct mjs* mjs;\n    mjs_val_t result;\n    struct json_parse_frame* frame;\n    enum mjs_err rcode;\n};\n\n/* Allocate JSON parse frame */\nstatic struct json_parse_frame* alloc_json_frame(struct json_parse_ctx* ctx, mjs_val_t v) {\n    struct json_parse_frame* frame =\n        (struct json_parse_frame*)calloc(sizeof(struct json_parse_frame), 1);\n    frame->val = v;\n    mjs_own(ctx->mjs, &frame->val);\n    return frame;\n}\n\n/* Free JSON parse frame, return the previous one (which may be NULL) */\nstatic struct json_parse_frame*\n    free_json_frame(struct json_parse_ctx* ctx, struct json_parse_frame* frame) {\n    struct json_parse_frame* up = frame->up;\n    mjs_disown(ctx->mjs, &frame->val);\n    free(frame);\n    return up;\n}\n\n/* Callback for json_walk() */\nstatic void frozen_cb(\n    void* data,\n    const char* name,\n    size_t name_len,\n    const char* path,\n    const struct json_token* token) {\n    struct json_parse_ctx* ctx = (struct json_parse_ctx*)data;\n    mjs_val_t v = MJS_UNDEFINED;\n\n    (void)path;\n\n    mjs_own(ctx->mjs, &v);\n\n    switch(token->type) {\n    case JSON_TYPE_STRING: {\n        char* dst;\n        if(token->len > 0 && (dst = malloc(token->len)) != NULL) {\n            int len = json_unescape(token->ptr, token->len, dst, token->len);\n            if(len < 0) {\n                mjs_prepend_errorf(ctx->mjs, MJS_TYPE_ERROR, \"invalid JSON string\");\n                break;\n            }\n            v = mjs_mk_string(ctx->mjs, dst, len, 1 /* copy */);\n            free(dst);\n        } else {\n            /*\n         * This branch is for 0-len strings, and for malloc errors\n         * TODO(lsm): on malloc error, propagate the error upstream\n         */\n            v = mjs_mk_string(ctx->mjs, \"\", 0, 1 /* copy */);\n        }\n        break;\n    }\n    case JSON_TYPE_NUMBER:\n        v = mjs_mk_number(ctx->mjs, strtod(token->ptr, NULL));\n        break;\n    case JSON_TYPE_TRUE:\n        v = mjs_mk_boolean(ctx->mjs, 1);\n        break;\n    case JSON_TYPE_FALSE:\n        v = mjs_mk_boolean(ctx->mjs, 0);\n        break;\n    case JSON_TYPE_NULL:\n        v = MJS_NULL;\n        break;\n    case JSON_TYPE_OBJECT_START:\n        v = mjs_mk_object(ctx->mjs);\n        break;\n    case JSON_TYPE_ARRAY_START:\n        v = mjs_mk_array(ctx->mjs);\n        break;\n\n    case JSON_TYPE_OBJECT_END:\n    case JSON_TYPE_ARRAY_END: {\n        /* Object or array has finished: deallocate its frame */\n        ctx->frame = free_json_frame(ctx, ctx->frame);\n    } break;\n\n    default:\n        LOG(LL_ERROR, (\"Wrong token type %d\\n\", token->type));\n        break;\n    }\n\n    if(!mjs_is_undefined(v)) {\n        if(name != NULL && name_len != 0) {\n            /* Need to define a property on the current object/array */\n            if(mjs_is_object(ctx->frame->val)) {\n                mjs_set(ctx->mjs, ctx->frame->val, name, name_len, v);\n            } else if(mjs_is_array(ctx->frame->val)) {\n                /*\n         * TODO(dfrank): consult name_len. Currently it's not a problem due to\n         * the implementation details of frozen, but it might change\n         */\n                int idx = (int)strtod(name, NULL);\n                mjs_array_set(ctx->mjs, ctx->frame->val, idx, v);\n            } else {\n                LOG(LL_ERROR, (\"Current value is neither object nor array\\n\"));\n            }\n        } else {\n            /* This is a root value */\n            assert(ctx->frame == NULL);\n\n            /*\n       * This value will also be the overall result of JSON parsing\n       * (it's already owned by the `mjs_alt_json_parse()`)\n       */\n            ctx->result = v;\n        }\n\n        if(token->type == JSON_TYPE_OBJECT_START || token->type == JSON_TYPE_ARRAY_START) {\n            /* New object or array has just started, so we need to allocate a frame\n       * for it */\n            struct json_parse_frame* new_frame = alloc_json_frame(ctx, v);\n            new_frame->up = ctx->frame;\n            ctx->frame = new_frame;\n        }\n    }\n\n    mjs_disown(ctx->mjs, &v);\n}\n\nMJS_PRIVATE mjs_err_t mjs_json_parse(struct mjs* mjs, const char* str, size_t len, mjs_val_t* res) {\n    struct json_parse_ctx* ctx = (struct json_parse_ctx*)calloc(sizeof(struct json_parse_ctx), 1);\n    int json_res;\n    enum mjs_err rcode = MJS_OK;\n\n    ctx->mjs = mjs;\n    ctx->result = MJS_UNDEFINED;\n    ctx->frame = NULL;\n    ctx->rcode = MJS_OK;\n\n    mjs_own(mjs, &ctx->result);\n\n    {\n        /*\n     * We have to reallocate the buffer before invoking json_walk, because\n     * frozen_cb can create new strings, which can result in the reallocation\n     * of mjs string mbuf, invalidating the `str` pointer.\n     */\n        char* stmp = malloc(len);\n        memcpy(stmp, str, len);\n        json_res = json_walk(stmp, len, frozen_cb, ctx);\n        free(stmp);\n        stmp = NULL;\n\n        /* str might have been invalidated, so null it out */\n        str = NULL;\n    }\n\n    if(ctx->rcode != MJS_OK) {\n        rcode = ctx->rcode;\n        mjs_prepend_errorf(mjs, rcode, \"invalid JSON string\");\n    } else if(json_res < 0) {\n        /* There was an error during parsing */\n        rcode = MJS_TYPE_ERROR;\n        mjs_prepend_errorf(mjs, rcode, \"invalid JSON string\");\n    } else {\n        /* Expression is parsed successfully */\n        *res = ctx->result;\n\n        /* There should be no allocated frames */\n        assert(ctx->frame == NULL);\n    }\n\n    if(rcode != MJS_OK) {\n        /* There might be some allocated frames in case of malformed JSON */\n        while(ctx->frame != NULL) {\n            ctx->frame = free_json_frame(ctx, ctx->frame);\n        }\n    }\n\n    mjs_disown(mjs, &ctx->result);\n    free(ctx);\n\n    return rcode;\n}\n\nMJS_PRIVATE void mjs_op_json_stringify(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t val = mjs_arg(mjs, 0);\n\n    if(mjs_nargs(mjs) < 1) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"missing a value to stringify\");\n    } else {\n        char* p = NULL;\n        if(mjs_json_stringify(mjs, val, NULL, 0, &p) == MJS_OK) {\n            ret = mjs_mk_string(mjs, p, ~0, 1 /* copy */);\n            free(p);\n        }\n    }\n\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_op_json_parse(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t arg0 = mjs_arg(mjs, 0);\n\n    if(mjs_is_string(arg0)) {\n        size_t len;\n        const char* str = mjs_get_string(mjs, &arg0, &len);\n        mjs_json_parse(mjs, str, len, &ret);\n    } else {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"string argument required\");\n    }\n\n    mjs_return(mjs, ret);\n}\n"
  },
  {
    "path": "lib/mjs/mjs_json.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_JSON_H_\n#define MJS_JSON_H_\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nMJS_PRIVATE mjs_err_t to_json_or_debug(\n    struct mjs* mjs,\n    mjs_val_t v,\n    char* buf,\n    size_t size,\n    size_t* res_len,\n    uint8_t is_debug);\n\nMJS_PRIVATE mjs_err_t\n    mjs_json_stringify(struct mjs* mjs, mjs_val_t v, char* buf, size_t size, char** res);\nMJS_PRIVATE void mjs_op_json_stringify(struct mjs* mjs);\nMJS_PRIVATE void mjs_op_json_parse(struct mjs* mjs);\n\nMJS_PRIVATE mjs_err_t mjs_json_parse(struct mjs* mjs, const char* str, size_t len, mjs_val_t* res);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_JSON_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_license.h",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n *\n * This software is dual-licensed: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License version 2 as\n * published by the Free Software Foundation. For the terms of this\n * license, see <http://www.gnu.org/licenses/>.\n *\n * You are free to use this software under the terms of the GNU General\n * Public License, but WITHOUT ANY WARRANTY; without even the implied\n * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU General Public License for more details.\n *\n * Alternatively, you can license this software under a commercial\n * license, as set out in <https://www.cesanta.com/license>.\n */\n"
  },
  {
    "path": "lib/mjs/mjs_mm.h",
    "content": "/*\n * Copyright (c) 2014-2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_MM_H_\n#define MJS_MM_H_\n\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct mjs;\n\ntypedef void (*gc_cell_destructor_t)(struct mjs* mjs, void*);\n\nstruct gc_block {\n    struct gc_block* next;\n    struct gc_cell* base;\n    size_t size;\n};\n\nstruct gc_arena {\n    struct gc_block* blocks;\n    size_t size_increment;\n    struct gc_cell* free; /* head of free list */\n    size_t cell_size;\n\n#if MJS_MEMORY_STATS\n    unsigned long allocations; /* cumulative counter of allocations */\n    unsigned long garbage; /* cumulative counter of garbage */\n    unsigned long alive; /* number of living cells */\n#endif\n\n    gc_cell_destructor_t destructor;\n};\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_MM_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_object.c",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"mjs_object.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n#include \"furi.h\"\n\n#include \"common/mg_str.h\"\n\nMJS_PRIVATE mjs_val_t mjs_object_to_value(struct mjs_object* o) {\n    if(o == NULL) {\n        return MJS_NULL;\n    } else {\n        return mjs_legit_pointer_to_value(o) | MJS_TAG_OBJECT;\n    }\n}\n\nMJS_PRIVATE void mjs_obj_destructor(struct mjs* mjs, void* cell) {\n    struct mjs_object* obj = cell;\n    mjs_val_t obj_val = mjs_object_to_value(obj);\n\n    struct mjs_property* destructor = mjs_get_own_property(\n        mjs, obj_val, MJS_DESTRUCTOR_PROP_NAME, strlen(MJS_DESTRUCTOR_PROP_NAME));\n    if(!destructor) return;\n    if(!mjs_is_foreign(destructor->value)) return;\n\n    mjs_custom_obj_destructor_t destructor_fn = mjs_get_ptr(mjs, destructor->value);\n    if(destructor_fn) destructor_fn(mjs, obj_val);\n}\n\nMJS_PRIVATE struct mjs_object* get_object_struct(mjs_val_t v) {\n    struct mjs_object* ret = NULL;\n    if(mjs_is_null(v)) {\n        ret = NULL;\n    } else {\n        assert(mjs_is_object_based(v));\n        ret = (struct mjs_object*)get_ptr(v);\n    }\n    return ret;\n}\n\nmjs_val_t mjs_mk_object(struct mjs* mjs) {\n    struct mjs_object* o = new_object(mjs);\n    if(o == NULL) {\n        return MJS_NULL;\n    }\n    (void)mjs;\n    o->properties = NULL;\n    return mjs_object_to_value(o);\n}\n\nint mjs_is_object(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_OBJECT || (v & MJS_TAG_MASK) == MJS_TAG_ARRAY;\n}\n\nint mjs_is_object_based(mjs_val_t v) {\n    return ((v & MJS_TAG_MASK) == MJS_TAG_OBJECT) || ((v & MJS_TAG_MASK) == MJS_TAG_ARRAY) ||\n           ((v & MJS_TAG_MASK) == MJS_TAG_ARRAY_BUF_VIEW);\n}\n\nMJS_PRIVATE struct mjs_property*\n    mjs_get_own_property(struct mjs* mjs, mjs_val_t obj, const char* name, size_t len) {\n    struct mjs_property* p;\n    struct mjs_object* o;\n\n    if(!mjs_is_object_based(obj)) {\n        return NULL;\n    }\n\n    o = get_object_struct(obj);\n\n    if(len <= 5) {\n        mjs_val_t ss = mjs_mk_string(mjs, name, len, 1);\n        for(p = o->properties; p != NULL; p = p->next) {\n            if(p->name == ss) return p;\n        }\n    } else {\n        for(p = o->properties; p != NULL; p = p->next) {\n            if(mjs_strcmp(mjs, &p->name, name, len) == 0) return p;\n        }\n        return p;\n    }\n\n    return NULL;\n}\n\nMJS_PRIVATE struct mjs_property*\n    mjs_get_own_property_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t key) {\n    size_t n;\n    char* s = NULL;\n    int need_free = 0;\n    struct mjs_property* p = NULL;\n    mjs_err_t err = mjs_to_string(mjs, &key, &s, &n, &need_free);\n    if(err == MJS_OK) {\n        p = mjs_get_own_property(mjs, obj, s, n);\n    }\n    if(need_free) free(s);\n    return p;\n}\n\nMJS_PRIVATE struct mjs_property*\n    mjs_mk_property(struct mjs* mjs, mjs_val_t name, mjs_val_t value) {\n    struct mjs_property* p = new_property(mjs);\n    p->next = NULL;\n    p->name = name;\n    p->value = value;\n    return p;\n}\n\nmjs_val_t mjs_get(struct mjs* mjs, mjs_val_t obj, const char* name, size_t name_len) {\n    struct mjs_property* p;\n\n    if(name_len == (size_t)~0) {\n        name_len = strlen(name);\n    }\n\n    p = mjs_get_own_property(mjs, obj, name, name_len);\n    if(p == NULL) {\n        return MJS_UNDEFINED;\n    } else {\n        return p->value;\n    }\n}\n\nmjs_val_t mjs_get_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t name) {\n    size_t n;\n    char* s = NULL;\n    int need_free = 0;\n    mjs_val_t ret = MJS_UNDEFINED;\n\n    mjs_err_t err = mjs_to_string(mjs, &name, &s, &n, &need_free);\n\n    if(err == MJS_OK) {\n        /* Successfully converted name value to string: get the property */\n        ret = mjs_get(mjs, obj, s, n);\n    }\n\n    if(need_free) {\n        free(s);\n        s = NULL;\n    }\n    return ret;\n}\n\nmjs_val_t mjs_get_v_proto(struct mjs* mjs, mjs_val_t obj, mjs_val_t key) {\n    struct mjs_property* p;\n    mjs_val_t pn = mjs_mk_string(mjs, MJS_PROTO_PROP_NAME, ~0, 1);\n    if((p = mjs_get_own_property_v(mjs, obj, key)) != NULL) return p->value;\n    if((p = mjs_get_own_property_v(mjs, obj, pn)) == NULL) return MJS_UNDEFINED;\n    return mjs_get_v_proto(mjs, p->value, key);\n}\n\nmjs_err_t\n    mjs_set(struct mjs* mjs, mjs_val_t obj, const char* name, size_t name_len, mjs_val_t val) {\n    return mjs_set_internal(mjs, obj, MJS_UNDEFINED, (char*)name, name_len, val);\n}\n\nmjs_err_t mjs_set_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t name, mjs_val_t val) {\n    return mjs_set_internal(mjs, obj, name, NULL, 0, val);\n}\n\nMJS_PRIVATE mjs_err_t mjs_set_internal(\n    struct mjs* mjs,\n    mjs_val_t obj,\n    mjs_val_t name_v,\n    char* name,\n    size_t name_len,\n    mjs_val_t val) {\n    mjs_err_t rcode = MJS_OK;\n\n    struct mjs_property* p;\n\n    int need_free = 0;\n\n    if(name == NULL) {\n        /* Pointer was not provided, so obtain one from the name_v. */\n        rcode = mjs_to_string(mjs, &name_v, &name, &name_len, &need_free);\n        if(rcode != MJS_OK) {\n            goto clean;\n        }\n    } else {\n        /*\n     * Pointer was provided, so we ignore name_v. Here we set it to undefined,\n     * and the actual value will be calculated later if needed.\n     */\n        name_v = MJS_UNDEFINED;\n    }\n\n    p = mjs_get_own_property(mjs, obj, name, name_len);\n\n    if(p == NULL) {\n        struct mjs_object* o;\n        if(!mjs_is_object_based(obj)) {\n            return MJS_REFERENCE_ERROR;\n        }\n\n        /*\n     * name_v might be not a string here. In this case, we need to create a new\n     * `name_v`, which will be a string.\n     */\n        if(!mjs_is_string(name_v)) {\n            name_v = mjs_mk_string(mjs, name, name_len, 1);\n        }\n\n        p = mjs_mk_property(mjs, name_v, val);\n\n        o = get_object_struct(obj);\n        p->next = o->properties;\n        o->properties = p;\n    }\n\n    p->value = val;\n\nclean:\n    if(need_free) {\n        free(name);\n        name = NULL;\n    }\n    return rcode;\n}\n\nMJS_PRIVATE void mjs_destroy_property(struct mjs_property** p) {\n    *p = NULL;\n}\n\n/*\n * See comments in `object_public.h`\n */\nint mjs_del(struct mjs* mjs, mjs_val_t obj, const char* name, size_t len) {\n    struct mjs_property *prop, *prev;\n\n    if(!mjs_is_object_based(obj)) {\n        return -1;\n    }\n    if(len == (size_t)~0) {\n        len = strlen(name);\n    }\n    for(prev = NULL, prop = get_object_struct(obj)->properties; prop != NULL;\n        prev = prop, prop = prop->next) {\n        size_t n;\n        const char* s = mjs_get_string(mjs, &prop->name, &n);\n        if(n == len && strncmp(s, name, len) == 0) {\n            if(prev) {\n                prev->next = prop->next;\n            } else {\n                get_object_struct(obj)->properties = prop->next;\n            }\n            mjs_destroy_property(&prop);\n            return 0;\n        }\n    }\n    return -1;\n}\n\nmjs_val_t mjs_next(struct mjs* mjs, mjs_val_t obj, mjs_val_t* iterator) {\n    struct mjs_property* p = NULL;\n    mjs_val_t key = MJS_UNDEFINED;\n\n    if(*iterator == MJS_UNDEFINED) {\n        struct mjs_object* o = get_object_struct(obj);\n        p = o->properties;\n    } else {\n        p = ((struct mjs_property*)get_ptr(*iterator))->next;\n    }\n\n    if(p == NULL) {\n        *iterator = MJS_UNDEFINED;\n    } else {\n        key = p->name;\n        *iterator = mjs_mk_foreign(mjs, p);\n    }\n\n    return key;\n}\n\nMJS_PRIVATE void mjs_op_create_object(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t proto_v = mjs_arg(mjs, 0);\n\n    if(!mjs_check_arg(mjs, 0, \"proto\", MJS_TYPE_OBJECT_GENERIC, &proto_v)) {\n        goto clean;\n    }\n\n    ret = mjs_mk_object(mjs);\n    mjs_set(mjs, ret, MJS_PROTO_PROP_NAME, ~0, proto_v);\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_op_object_define_property(struct mjs* mjs) {\n    // stub, do not use\n    mjs_return(mjs, MJS_UNDEFINED);\n}\n\nmjs_val_t\n    mjs_struct_to_obj(struct mjs* mjs, const void* base, const struct mjs_c_struct_member* defs) {\n    mjs_val_t obj;\n    const struct mjs_c_struct_member* def = defs;\n    if(base == NULL || def == NULL) return MJS_UNDEFINED;\n    obj = mjs_mk_object(mjs);\n    /* Pin the object while it is being built */\n    mjs_own(mjs, &obj);\n    /*\n   * Because mjs inserts new properties at the head of the list,\n   * start from the end so the constructed object more closely resembles\n   * the definition.\n   */\n    while(def->name != NULL)\n        def++;\n    for(def--; def >= defs; def--) {\n        mjs_val_t v = MJS_UNDEFINED;\n        const char* ptr = (const char*)base + def->offset;\n        switch(def->type) {\n        case MJS_STRUCT_FIELD_TYPE_STRUCT: {\n            const void* sub_base = (const void*)ptr;\n            const struct mjs_c_struct_member* sub_def =\n                (const struct mjs_c_struct_member*)def->arg;\n            v = mjs_struct_to_obj(mjs, sub_base, sub_def);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_STRUCT_PTR: {\n            const void** sub_base = (const void**)ptr;\n            const struct mjs_c_struct_member* sub_def =\n                (const struct mjs_c_struct_member*)def->arg;\n            if(*sub_base != NULL) {\n                v = mjs_struct_to_obj(mjs, *sub_base, sub_def);\n            } else {\n                v = MJS_NULL;\n            }\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_INT: {\n            double value = (double)(*(int*)ptr);\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_BOOL: {\n            v = mjs_mk_boolean(mjs, *(bool*)ptr);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_DOUBLE: {\n            v = mjs_mk_number(mjs, *(double*)ptr);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_FLOAT: {\n            float value = *(float*)ptr;\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_CHAR_PTR: {\n            const char* value = *(const char**)ptr;\n            v = mjs_mk_string(mjs, value, ~0, 1);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_VOID_PTR: {\n            v = mjs_mk_foreign(mjs, *(void**)ptr);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_MG_STR_PTR: {\n            const struct mg_str* s = *(const struct mg_str**)ptr;\n            if(s != NULL) {\n                v = mjs_mk_string(mjs, s->p, s->len, 1);\n            } else {\n                v = MJS_NULL;\n            }\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_MG_STR: {\n            const struct mg_str* s = (const struct mg_str*)ptr;\n            v = mjs_mk_string(mjs, s->p, s->len, 1);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_DATA: {\n            const char* dptr = (const char*)ptr;\n            const intptr_t dlen = (intptr_t)def->arg;\n            v = mjs_mk_string(mjs, dptr, dlen, 1);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_INT8: {\n            double value = (double)(*(int8_t*)ptr);\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_INT16: {\n            double value = (double)(*(int16_t*)ptr);\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_UINT8: {\n            double value = (double)(*(uint8_t*)ptr);\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_UINT16: {\n            double value = (double)(*(uint16_t*)ptr);\n            v = mjs_mk_number(mjs, value);\n            break;\n        }\n        case MJS_STRUCT_FIELD_TYPE_CUSTOM: {\n            mjs_val_t (*fptr)(struct mjs*, const void*) =\n                (mjs_val_t(*)(struct mjs*, const void*))def->arg;\n            v = fptr(mjs, ptr);\n        }\n        default: {\n            break;\n        }\n        }\n        mjs_set(mjs, obj, def->name, ~0, v);\n    }\n    mjs_disown(mjs, &obj);\n    return obj;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_object.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_OBJECT_H_\n#define MJS_OBJECT_H_\n\n#include \"mjs_object_public.h\"\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct mjs;\n\nstruct mjs_property {\n    struct mjs_property* next; /* Linkage in struct mjs_object::properties */\n    mjs_val_t name; /* Property name (a string) */\n    mjs_val_t value; /* Property value */\n};\n\nstruct mjs_object {\n    struct mjs_property* properties;\n};\n\nMJS_PRIVATE struct mjs_object* get_object_struct(mjs_val_t v);\nMJS_PRIVATE struct mjs_property*\n    mjs_get_own_property(struct mjs* mjs, mjs_val_t obj, const char* name, size_t len);\n\nMJS_PRIVATE struct mjs_property*\n    mjs_get_own_property_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t key);\n\n/*\n * A worker function for `mjs_set()` and `mjs_set_v()`: it takes name as both\n * ptr+len and mjs_val_t. If `name` pointer is not NULL, it takes precedence\n * over `name_v`.\n */\nMJS_PRIVATE mjs_err_t mjs_set_internal(\n    struct mjs* mjs,\n    mjs_val_t obj,\n    mjs_val_t name_v,\n    char* name,\n    size_t name_len,\n    mjs_val_t val);\n\n/*\n * Implementation of `Object.create(proto)`\n */\nMJS_PRIVATE void mjs_op_create_object(struct mjs* mjs);\n\n/*\n * Stub of `Object.defineProperty()`\n */\nMJS_PRIVATE void mjs_op_object_define_property(struct mjs* mjs);\n\n/*\n * Cell destructor for object arena\n */\nMJS_PRIVATE void mjs_obj_destructor(struct mjs* mjs, void* cell);\n\n#define MJS_PROTO_PROP_NAME \"__p\" /* Make it < 5 chars */\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_OBJECT_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_object_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_OBJECT_PUBLIC_H_\n#define MJS_OBJECT_PUBLIC_H_\n\n#include <stddef.h>\n#include \"mjs_core_public.h\"\n#include \"mjs_ffi_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Returns true if the given value is an object or array.\n */\nint mjs_is_object(mjs_val_t v);\n\n/*\n * Returns true if the given value type is object-based (object, array, dataview).\n */\nint mjs_is_object_based(mjs_val_t v);\n\n/* Make an empty object */\nmjs_val_t mjs_mk_object(struct mjs* mjs);\n\n/* Field types for struct-object conversion. */\nenum mjs_struct_field_type {\n    MJS_STRUCT_FIELD_TYPE_INVALID,\n    MJS_STRUCT_FIELD_TYPE_STRUCT, /* Struct, arg points to def. */\n    MJS_STRUCT_FIELD_TYPE_STRUCT_PTR, /* Ptr to struct, arg points to def. */\n    MJS_STRUCT_FIELD_TYPE_INT,\n    MJS_STRUCT_FIELD_TYPE_BOOL,\n    MJS_STRUCT_FIELD_TYPE_DOUBLE,\n    MJS_STRUCT_FIELD_TYPE_FLOAT,\n    MJS_STRUCT_FIELD_TYPE_CHAR_PTR, /* NUL-terminated string. */\n    MJS_STRUCT_FIELD_TYPE_VOID_PTR, /* Converted to foreign ptr. */\n    MJS_STRUCT_FIELD_TYPE_MG_STR_PTR, /* Converted to string. */\n    MJS_STRUCT_FIELD_TYPE_MG_STR, /* Converted to string. */\n    MJS_STRUCT_FIELD_TYPE_DATA, /* Data, arg is length, becomes string. */\n    MJS_STRUCT_FIELD_TYPE_INT8,\n    MJS_STRUCT_FIELD_TYPE_INT16,\n    MJS_STRUCT_FIELD_TYPE_UINT8,\n    MJS_STRUCT_FIELD_TYPE_UINT16,\n    /*\n   * User-provided function. Arg is a pointer to function that takes void *\n   * (pointer to field within the struct) and returns mjs_val_t:\n   * mjs_val_t field_value(struct mjs *mjs, const void *field_ptr) { ... }\n   */\n    MJS_STRUCT_FIELD_TYPE_CUSTOM,\n};\n\n/* C structure layout descriptor - needed by mjs_struct_to_obj */\nstruct mjs_c_struct_member {\n    const char* name;\n    int offset;\n    enum mjs_struct_field_type type;\n    const void* arg; /* Additional argument, used for some types. */\n};\n\n/* Create flat JS object from a C memory descriptor */\nmjs_val_t\n    mjs_struct_to_obj(struct mjs* mjs, const void* base, const struct mjs_c_struct_member* members);\n\n/*\n * Lookup property `name` in object `obj`. If `obj` holds no such property,\n * an `undefined` value is returned.\n *\n * If `name_len` is ~0, `name` is assumed to be NUL-terminated and\n * `strlen(name)` is used.\n */\nmjs_val_t mjs_get(struct mjs* mjs, mjs_val_t obj, const char* name, size_t name_len);\n\n/*\n * Like mjs_get but with a JS string.\n */\nmjs_val_t mjs_get_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t name);\n\n/*\n * Like mjs_get_v but lookup the prototype chain.\n */\nmjs_val_t mjs_get_v_proto(struct mjs* mjs, mjs_val_t obj, mjs_val_t key);\n\n/*\n * Set object property. Behaves just like JavaScript assignment.\n */\nmjs_err_t mjs_set(struct mjs* mjs, mjs_val_t obj, const char* name, size_t len, mjs_val_t val);\n\n/*\n * Like mjs_set but the name is already a JS string.\n */\nmjs_err_t mjs_set_v(struct mjs* mjs, mjs_val_t obj, mjs_val_t name, mjs_val_t val);\n\n/*\n * Delete own property `name` of the object `obj`. Does not follow the\n * prototype chain.\n *\n * If `name_len` is ~0, `name` is assumed to be NUL-terminated and\n * `strlen(name)` is used.\n *\n * Returns 0 on success, -1 on error.\n */\nint mjs_del(struct mjs* mjs, mjs_val_t obj, const char* name, size_t len);\n\n/*\n * Iterate over `obj` properties.\n * First call should set `iterator` to MJS_UNDEFINED.\n * Return object's key (a string), or MJS_UNDEFINED when no more keys left.\n * Do not mutate the object during iteration.\n *\n * Example:\n *   mjs_val_t key, iter = MJS_UNDEFINED;\n *   while ((key = mjs_next(mjs, obj, &iter)) != MJS_UNDEFINED) {\n *     // Do something with the obj/key ...\n *   }\n */\nmjs_val_t mjs_next(struct mjs* mjs, mjs_val_t obj, mjs_val_t* iterator);\n\ntypedef void (*mjs_custom_obj_destructor_t)(struct mjs* mjs, mjs_val_t object);\n\n/*\n * Destructor property name. If set, must be a foreign pointer to a function\n * that will be called just before the object is freed.\n */\n#define MJS_DESTRUCTOR_PROP_NAME \"__d\"\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_OBJECT_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_parser.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/cs_varint.h\"\n\n#include \"mjs_bcode.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_parser.h\"\n#include \"mjs_string.h\"\n#include \"mjs_tok.h\"\n\n#ifndef MAX_TOKS_IN_EXPR\n#define MAX_TOKS_IN_EXPR 40\n#endif\n\n#define FAIL_ERR(p, code)                                                                \\\n    do {                                                                                 \\\n        mjs_set_errorf(                                                                  \\\n            p->mjs, code, \"parse error at line %d: [%.*s]\", p->line_no, 10, p->tok.ptr); \\\n        return code;                                                                     \\\n    } while(0)\n\n#define pnext1(p)                                        \\\n    do {                                                 \\\n        LOG(LL_VERBOSE_DEBUG, (\"  PNEXT %d\", __LINE__)); \\\n        pnext(p);                                        \\\n    } while(0)\n\n#define SYNTAX_ERROR(p) FAIL_ERR(p, MJS_SYNTAX_ERROR)\n#undef EXPECT\n#define EXPECT(p, t)        \\\n    if((p)->tok.tok != (t)) \\\n        SYNTAX_ERROR(p);    \\\n    else                    \\\n        pnext1(p);\n\nstatic mjs_err_t parse_statement(struct pstate* p);\nstatic mjs_err_t parse_expr(struct pstate* p);\n\nstatic int ptest(struct pstate* p) {\n    struct pstate saved = *p;\n    int tok = pnext(p);\n    *p = saved;\n    return tok;\n}\n\nstatic const int s_unary_ops[] = {\n    TOK_NOT,\n    TOK_TILDA,\n    TOK_PLUS_PLUS,\n    TOK_MINUS_MINUS,\n    TOK_KEYWORD_TYPEOF,\n    TOK_MINUS,\n    TOK_PLUS,\n    TOK_EOF};\nstatic const int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF};\nstatic const int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF};\nstatic const int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF};\nstatic const int s_assign_ops[] = {\n    TOK_ASSIGN,\n    TOK_PLUS_ASSIGN,\n    TOK_MINUS_ASSIGN,\n    TOK_MUL_ASSIGN,\n    TOK_DIV_ASSIGN,\n    TOK_REM_ASSIGN,\n    TOK_LSHIFT_ASSIGN,\n    TOK_RSHIFT_ASSIGN,\n    TOK_URSHIFT_ASSIGN,\n    TOK_AND_ASSIGN,\n    TOK_XOR_ASSIGN,\n    TOK_OR_ASSIGN,\n    TOK_EOF};\n\nstatic int findtok(int const* toks, int tok) {\n    int i = 0;\n    while(tok != toks[i] && toks[i] != TOK_EOF)\n        i++;\n    return toks[i];\n}\n\nstatic void emit_op(struct pstate* pstate, int tok) {\n    assert(tok >= 0 && tok <= 255);\n    emit_byte(pstate, OP_EXPR);\n    emit_byte(pstate, (uint8_t)tok);\n}\n\n#define BINOP_STACK_FRAME_SIZE 16\n#define STACK_LIMIT            8192\n\n// Intentionally left as macro rather than a function, to let the\n// compiler to inline calls and mimimize runtime stack usage.\n#define PARSE_LTR_BINOP(p, f1, f2, ops, prev_op)                                    \\\n    do {                                                                            \\\n        mjs_err_t res = MJS_OK;                                                     \\\n        p->depth++;                                                                 \\\n        if(p->depth > (STACK_LIMIT / BINOP_STACK_FRAME_SIZE)) {                     \\\n            mjs_set_errorf(p->mjs, MJS_SYNTAX_ERROR, \"parser stack overflow\");      \\\n            res = MJS_SYNTAX_ERROR;                                                 \\\n            goto binop_clean;                                                       \\\n        }                                                                           \\\n        if((res = f1(p, TOK_EOF)) != MJS_OK) goto binop_clean;                      \\\n        if(prev_op != TOK_EOF) emit_op(p, prev_op);                                 \\\n        if(findtok(ops, p->tok.tok) != TOK_EOF) {                                   \\\n            int op = p->tok.tok;                                                    \\\n            size_t off_if = 0;                                                      \\\n            /* For AND/OR, implement short-circuit evaluation */                    \\\n            if(ops[0] == TOK_LOGICAL_AND || ops[0] == TOK_LOGICAL_OR) {             \\\n                emit_byte(                                                          \\\n                    p,                                                              \\\n                    (uint8_t)(ops[0] == TOK_LOGICAL_AND ? OP_JMP_NEUTRAL_FALSE :    \\\n                                                          OP_JMP_NEUTRAL_TRUE));    \\\n                off_if = p->cur_idx;                                                \\\n                emit_init_offset(p);                                                \\\n                /* No need to emit TOK_LOGICAL_AND and TOK_LOGICAL_OR: */           \\\n                /* Just drop the first value, and evaluate the second one. */       \\\n                emit_byte(p, (uint8_t)OP_DROP);                                     \\\n                op = TOK_EOF;                                                       \\\n            }                                                                       \\\n            pnext1(p);                                                              \\\n            if((res = f2(p, op)) != MJS_OK) goto binop_clean;                       \\\n                                                                                    \\\n            if(off_if != 0) {                                                       \\\n                mjs_bcode_insert_offset(                                            \\\n                    p, p->mjs, off_if, p->cur_idx - off_if - MJS_INIT_OFFSET_SIZE); \\\n            }                                                                       \\\n        }                                                                           \\\n    binop_clean:                                                                    \\\n        p->depth--;                                                                 \\\n        return res;                                                                 \\\n    } while(0)\n\n#define PARSE_RTL_BINOP(p, f1, f2, ops, prev_op)             \\\n    do {                                                     \\\n        mjs_err_t res = MJS_OK;                              \\\n        (void)prev_op;                                       \\\n        if((res = f1(p, TOK_EOF)) != MJS_OK) return res;     \\\n        if(findtok(ops, p->tok.tok) != TOK_EOF) {            \\\n            int op = p->tok.tok;                             \\\n            pnext1(p);                                       \\\n            if((res = f2(p, TOK_EOF)) != MJS_OK) return res; \\\n            emit_op(p, op);                                  \\\n        }                                                    \\\n        return res;                                          \\\n    } while(0)\n\n#if MJS_INIT_OFFSET_SIZE > 0\nstatic void emit_init_offset(struct pstate* p) {\n    size_t i;\n    for(i = 0; i < MJS_INIT_OFFSET_SIZE; i++) {\n        emit_byte(p, 0);\n    }\n}\n#else\nstatic void emit_init_offset(struct pstate* p) {\n    (void)p;\n}\n#endif\n\nstatic mjs_err_t parse_statement_list(struct pstate* p, int et) {\n    mjs_err_t res = MJS_OK;\n    int drop = 0;\n    pnext1(p);\n    while(res == MJS_OK && p->tok.tok != TOK_EOF && p->tok.tok != et) {\n        if(drop) emit_byte(p, OP_DROP);\n        res = parse_statement(p);\n        drop = 1;\n        while(p->tok.tok == TOK_SEMICOLON)\n            pnext1(p);\n    }\n\n    /*\n   * Client code expects statement list to contain a value, so if the statement\n   * list was empty, push `undefined`.\n   */\n    if(!drop) {\n        emit_byte(p, OP_PUSH_UNDEF);\n    }\n    return res;\n}\n\nstatic mjs_err_t parse_block(struct pstate* p, int mkscope) {\n    mjs_err_t res = MJS_OK;\n    p->depth++;\n    if(p->depth > (STACK_LIMIT / BINOP_STACK_FRAME_SIZE)) {\n        mjs_set_errorf(p->mjs, MJS_SYNTAX_ERROR, \"parser stack overflow\");\n        res = MJS_SYNTAX_ERROR;\n        return res;\n    }\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s]\", 10, p->tok.ptr));\n    if(mkscope) emit_byte(p, OP_NEW_SCOPE);\n    res = parse_statement_list(p, TOK_CLOSE_CURLY);\n    EXPECT(p, TOK_CLOSE_CURLY);\n    if(mkscope) emit_byte(p, OP_DEL_SCOPE);\n    return res;\n}\n\nstatic mjs_err_t parse_function(struct pstate* p) {\n    size_t prologue, off;\n    int arg_no = 0;\n    int name_provided = 0;\n    mjs_err_t res = MJS_OK;\n\n    EXPECT(p, TOK_KEYWORD_FUNCTION);\n\n    if(p->tok.tok == TOK_IDENT) {\n        /* Function name was provided */\n        struct tok tmp = p->tok;\n        name_provided = 1;\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, tmp.ptr, tmp.len);\n        emit_byte(p, OP_PUSH_SCOPE);\n        emit_byte(p, OP_CREATE);\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, tmp.ptr, tmp.len);\n        emit_byte(p, OP_FIND_SCOPE);\n        pnext1(p);\n    }\n\n    emit_byte(p, OP_JMP);\n    off = p->cur_idx;\n    emit_init_offset(p);\n\n    prologue = p->cur_idx;\n\n    EXPECT(p, TOK_OPEN_PAREN);\n    emit_byte(p, OP_NEW_SCOPE);\n    // Emit names of function arguments\n    while(p->tok.tok != TOK_CLOSE_PAREN) {\n        if(p->tok.tok != TOK_IDENT) SYNTAX_ERROR(p);\n        emit_byte(p, OP_SET_ARG);\n        emit_int(p, arg_no);\n        arg_no++;\n        emit_str(p, p->tok.ptr, p->tok.len);\n        if(ptest(p) == TOK_COMMA) pnext1(p);\n        pnext1(p);\n    }\n    EXPECT(p, TOK_CLOSE_PAREN);\n    if((res = parse_block(p, 0)) != MJS_OK) return res;\n    emit_byte(p, OP_RETURN);\n    prologue += mjs_bcode_insert_offset(p, p->mjs, off, p->cur_idx - off - MJS_INIT_OFFSET_SIZE);\n    emit_byte(p, OP_PUSH_FUNC);\n    emit_int(p, p->cur_idx - 1 /* OP_PUSH_FUNC */ - prologue);\n    if(name_provided) {\n        emit_op(p, TOK_ASSIGN);\n    }\n\n    return res;\n}\n\nstatic mjs_err_t parse_object_literal(struct pstate* p) {\n    mjs_err_t res = MJS_OK;\n    EXPECT(p, TOK_OPEN_CURLY);\n    emit_byte(p, OP_PUSH_OBJ);\n    while(p->tok.tok != TOK_CLOSE_CURLY) {\n        if(p->tok.tok != TOK_IDENT && p->tok.tok != TOK_STR) SYNTAX_ERROR(p);\n        emit_byte(p, OP_DUP);\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, p->tok.ptr, p->tok.len);\n        emit_byte(p, OP_SWAP);\n        pnext1(p);\n        EXPECT(p, TOK_COLON);\n        if((res = parse_expr(p)) != MJS_OK) return res;\n        emit_op(p, TOK_ASSIGN);\n        emit_byte(p, OP_DROP);\n        if(p->tok.tok == TOK_COMMA) {\n            pnext1(p);\n        } else if(p->tok.tok != TOK_CLOSE_CURLY) {\n            SYNTAX_ERROR(p);\n        }\n    }\n    return res;\n}\n\nstatic mjs_err_t parse_array_literal(struct pstate* p) {\n    mjs_err_t res = MJS_OK;\n    EXPECT(p, TOK_OPEN_BRACKET);\n    emit_byte(p, OP_PUSH_ARRAY);\n    while(p->tok.tok != TOK_CLOSE_BRACKET) {\n        emit_byte(p, OP_DUP);\n        if((res = parse_expr(p)) != MJS_OK) return res;\n        emit_byte(p, OP_APPEND);\n        if(p->tok.tok == TOK_COMMA) pnext1(p);\n    }\n    return res;\n}\n\nstatic enum mjs_err parse_literal(struct pstate* p, const struct tok* t) {\n    struct mbuf* bcode_gen = &p->mjs->bcode_gen;\n    enum mjs_err res = MJS_OK;\n    int tok = t->tok;\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s] %p\", p->tok.len, p->tok.ptr, (void*)&t));\n    switch(t->tok) {\n    case TOK_KEYWORD_FALSE:\n        emit_byte(p, OP_PUSH_FALSE);\n        break;\n    case TOK_KEYWORD_TRUE:\n        emit_byte(p, OP_PUSH_TRUE);\n        break;\n    case TOK_KEYWORD_UNDEFINED:\n        emit_byte(p, OP_PUSH_UNDEF);\n        break;\n    case TOK_KEYWORD_NULL:\n        emit_byte(p, OP_PUSH_NULL);\n        break;\n    case TOK_IDENT: {\n        int prev_tok = p->prev_tok;\n        int next_tok = ptest(p);\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, t->ptr, t->len);\n        emit_byte(p, (uint8_t)(prev_tok == TOK_DOT ? OP_SWAP : OP_FIND_SCOPE));\n        if(!findtok(s_assign_ops, next_tok) && !findtok(s_postfix_ops, next_tok) &&\n           /* TODO(dfrank): fix: it doesn't work for prefix ops */\n           !findtok(s_postfix_ops, prev_tok)) {\n            emit_byte(p, OP_GET);\n        }\n        break;\n    }\n    case TOK_NUM: {\n        double iv, d = strtod(t->ptr, NULL);\n        unsigned long uv = strtoul(t->ptr + 2, NULL, 16);\n        if(t->ptr[0] == '0' && t->ptr[1] == 'x') d = uv;\n        if(modf(d, &iv) == 0) {\n            emit_byte(p, OP_PUSH_INT);\n            emit_int(p, (int64_t)d);\n        } else {\n            emit_byte(p, OP_PUSH_DBL);\n            emit_str(p, t->ptr, t->len);\n        }\n        break;\n    }\n    case TOK_STR: {\n        size_t oldlen;\n        emit_byte(p, OP_PUSH_STR);\n        oldlen = bcode_gen->len;\n        embed_string(bcode_gen, p->cur_idx, t->ptr, t->len, EMBSTR_UNESCAPE);\n        p->cur_idx += bcode_gen->len - oldlen;\n    } break;\n    case TOK_OPEN_BRACKET:\n        res = parse_array_literal(p);\n        break;\n    case TOK_OPEN_CURLY:\n        res = parse_object_literal(p);\n        break;\n    case TOK_OPEN_PAREN:\n        pnext1(p);\n        res = parse_expr(p);\n        if(p->tok.tok != TOK_CLOSE_PAREN) SYNTAX_ERROR(p);\n        break;\n    case TOK_KEYWORD_FUNCTION:\n        res = parse_function(p);\n        break;\n    case TOK_KEYWORD_THIS:\n        emit_byte(p, OP_PUSH_THIS);\n        break;\n    default:\n        SYNTAX_ERROR(p);\n    }\n    if(tok != TOK_KEYWORD_FUNCTION) pnext1(p);\n    return res;\n}\n\nstatic mjs_err_t parse_call_dot_mem(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_DOT, TOK_OPEN_PAREN, TOK_OPEN_BRACKET, TOK_EOF};\n    mjs_err_t res = MJS_OK;\n    if((res = parse_literal(p, &p->tok)) != MJS_OK) return res;\n    while(findtok(ops, p->tok.tok) != TOK_EOF) {\n        if(p->tok.tok == TOK_OPEN_BRACKET) {\n            int prev_tok = p->prev_tok;\n            EXPECT(p, TOK_OPEN_BRACKET);\n            if((res = parse_expr(p)) != MJS_OK) return res;\n            emit_byte(p, OP_SWAP);\n            EXPECT(p, TOK_CLOSE_BRACKET);\n            if(!findtok(s_assign_ops, p->tok.tok) && !findtok(s_postfix_ops, p->tok.tok) &&\n               /* TODO(dfrank): fix: it doesn't work for prefix ops */\n               !findtok(s_postfix_ops, prev_tok)) {\n                emit_byte(p, OP_GET);\n            }\n        } else if(p->tok.tok == TOK_OPEN_PAREN) {\n            EXPECT(p, TOK_OPEN_PAREN);\n            emit_byte(p, OP_ARGS);\n            while(p->tok.tok != TOK_CLOSE_PAREN) {\n                if((res = parse_expr(p)) != MJS_OK) return res;\n                if(p->tok.tok == TOK_COMMA) pnext1(p);\n            }\n            emit_byte(p, OP_CALL);\n            EXPECT(p, TOK_CLOSE_PAREN);\n        } else if(p->tok.tok == TOK_DOT) {\n            EXPECT(p, TOK_DOT);\n            if((res = parse_call_dot_mem(p, TOK_DOT)) != MJS_OK) return res;\n        }\n    }\n    (void)prev_op;\n    return res;\n}\n\nstatic mjs_err_t parse_postfix(struct pstate* p, int prev_op) {\n    mjs_err_t res = MJS_OK;\n    if((res = parse_call_dot_mem(p, prev_op)) != MJS_OK) return res;\n    if(p->tok.tok == TOK_PLUS_PLUS || p->tok.tok == TOK_MINUS_MINUS) {\n        int op = p->tok.tok == TOK_PLUS_PLUS ? TOK_POSTFIX_PLUS : TOK_POSTFIX_MINUS;\n        emit_op(p, op);\n        pnext1(p);\n    }\n    return res;\n}\n\nstatic mjs_err_t parse_unary(struct pstate* p, int prev_op) {\n    mjs_err_t res = MJS_OK;\n    int op = TOK_EOF;\n    if(findtok(s_unary_ops, p->tok.tok) != TOK_EOF) {\n        op = p->tok.tok;\n        pnext1(p);\n    }\n    if(findtok(s_unary_ops, p->tok.tok) != TOK_EOF) {\n        res = parse_unary(p, prev_op);\n    } else {\n        res = parse_postfix(p, prev_op);\n    }\n    if(res != MJS_OK) return res;\n    if(op != TOK_EOF) {\n        if(op == TOK_MINUS) op = TOK_UNARY_MINUS;\n        if(op == TOK_PLUS) op = TOK_UNARY_PLUS;\n        emit_op(p, op);\n    }\n    return res;\n}\n\nstatic mjs_err_t parse_mul_div_rem(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_MUL, TOK_DIV, TOK_REM, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_unary, parse_mul_div_rem, ops, prev_op);\n}\n\nstatic mjs_err_t parse_plus_minus(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_PLUS, TOK_MINUS, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_mul_div_rem, parse_plus_minus, ops, prev_op);\n}\n\nstatic mjs_err_t parse_shifts(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_LSHIFT, TOK_RSHIFT, TOK_URSHIFT, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_plus_minus, parse_shifts, ops, prev_op);\n}\n\nstatic mjs_err_t parse_comparison(struct pstate* p, int prev_op) {\n    PARSE_LTR_BINOP(p, parse_shifts, parse_comparison, s_comparison_ops, prev_op);\n}\n\nstatic mjs_err_t parse_equality(struct pstate* p, int prev_op) {\n    PARSE_LTR_BINOP(p, parse_comparison, parse_equality, s_equality_ops, prev_op);\n}\n\nstatic mjs_err_t parse_bitwise_and(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_AND, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_equality, parse_bitwise_and, ops, prev_op);\n}\n\nstatic mjs_err_t parse_bitwise_xor(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_XOR, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_bitwise_and, parse_bitwise_xor, ops, prev_op);\n}\n\nstatic mjs_err_t parse_bitwise_or(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_OR, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_bitwise_xor, parse_bitwise_or, ops, prev_op);\n}\n\nstatic mjs_err_t parse_logical_and(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_LOGICAL_AND, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_bitwise_or, parse_logical_and, ops, prev_op);\n}\n\nstatic mjs_err_t parse_logical_or(struct pstate* p, int prev_op) {\n    int ops[] = {TOK_LOGICAL_OR, TOK_EOF};\n    PARSE_LTR_BINOP(p, parse_logical_and, parse_logical_or, ops, prev_op);\n}\n\nstatic mjs_err_t parse_ternary(struct pstate* p, int prev_op) {\n    mjs_err_t res = MJS_OK;\n    if((res = parse_logical_or(p, TOK_EOF)) != MJS_OK) return res;\n    if(prev_op != TOK_EOF) emit_op(p, prev_op);\n\n    if(p->tok.tok == TOK_QUESTION) {\n        size_t off_if, off_endif, off_else;\n        EXPECT(p, TOK_QUESTION);\n\n        emit_byte(p, OP_JMP_FALSE);\n        off_if = p->cur_idx;\n        emit_init_offset(p);\n\n        if((res = parse_ternary(p, TOK_EOF)) != MJS_OK) return res;\n\n        emit_byte(p, OP_JMP);\n        off_else = p->cur_idx;\n        emit_init_offset(p);\n        off_endif = p->cur_idx;\n\n        emit_byte(p, OP_DROP);\n\n        EXPECT(p, TOK_COLON);\n        if((res = parse_ternary(p, TOK_EOF)) != MJS_OK) return res;\n\n        /*\n     * NOTE: if inserting offset causes the code to move, off_endif needs to be\n     * adjusted\n     */\n        off_endif += mjs_bcode_insert_offset(\n            p, p->mjs, off_else, p->cur_idx - off_else - MJS_INIT_OFFSET_SIZE);\n\n        mjs_bcode_insert_offset(p, p->mjs, off_if, off_endif - off_if - MJS_INIT_OFFSET_SIZE);\n    }\n\n    return res;\n}\n\nstatic mjs_err_t parse_assignment(struct pstate* p, int prev_op) {\n    PARSE_RTL_BINOP(p, parse_ternary, parse_assignment, s_assign_ops, prev_op);\n}\n\nstatic mjs_err_t parse_expr(struct pstate* p) {\n    return parse_assignment(p, TOK_EOF);\n}\n\nstatic mjs_err_t parse_let(struct pstate* p) {\n    mjs_err_t res = MJS_OK;\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s]\", 10, p->tok.ptr));\n    if((p)->tok.tok != TOK_KEYWORD_VAR && (p)->tok.tok != TOK_KEYWORD_LET &&\n       (p)->tok.tok != TOK_KEYWORD_CONST)\n        SYNTAX_ERROR(p);\n    else\n        pnext1(p);\n    for(;;) {\n        struct tok tmp = p->tok;\n        EXPECT(p, TOK_IDENT);\n\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, tmp.ptr, tmp.len);\n        emit_byte(p, OP_PUSH_SCOPE);\n        emit_byte(p, OP_CREATE);\n\n        if(p->tok.tok == TOK_ASSIGN) {\n            pnext1(p);\n            emit_byte(p, OP_PUSH_STR);\n            emit_str(p, tmp.ptr, tmp.len);\n            emit_byte(p, OP_FIND_SCOPE);\n            if((res = parse_expr(p)) != MJS_OK) return res;\n            emit_op(p, TOK_ASSIGN);\n        } else {\n            emit_byte(p, OP_PUSH_UNDEF);\n        }\n        if(p->tok.tok == TOK_COMMA) {\n            emit_byte(p, OP_DROP);\n            pnext1(p);\n        }\n        if(p->tok.tok == TOK_SEMICOLON || p->tok.tok == TOK_EOF) break;\n    }\n    return res;\n}\n\nstatic mjs_err_t parse_block_or_stmt(struct pstate* p, int cs) {\n    if(ptest(p) == TOK_OPEN_CURLY) {\n        return parse_block(p, cs);\n    } else {\n        return parse_statement(p);\n    }\n}\n\nstatic mjs_err_t parse_for_in(struct pstate* p) {\n    mjs_err_t res = MJS_OK;\n    size_t off_b, off_check_end;\n\n    /* new scope should be pushed before OP_LOOP instruction */\n    emit_byte(p, OP_NEW_SCOPE);\n\n    /* Put iterator variable name to the stack */\n    if(p->tok.tok == TOK_KEYWORD_LET) {\n        EXPECT(p, TOK_KEYWORD_LET);\n        emit_byte(p, OP_PUSH_STR);\n        emit_str(p, p->tok.ptr, p->tok.len);\n        emit_byte(p, OP_PUSH_SCOPE);\n        emit_byte(p, OP_CREATE);\n    }\n    emit_byte(p, OP_PUSH_STR);\n    emit_str(p, p->tok.ptr, p->tok.len);\n\n    /* Put object to the stack */\n    EXPECT(p, TOK_IDENT);\n    EXPECT(p, TOK_KEYWORD_IN);\n    parse_expr(p);\n    EXPECT(p, TOK_CLOSE_PAREN);\n\n    emit_byte(p, OP_PUSH_UNDEF); /* Push iterator */\n\n    /* Before parsing condition statement, push break/continue offsets  */\n    emit_byte(p, OP_LOOP);\n    off_b = p->cur_idx;\n    emit_init_offset(p);\n    emit_byte(p, 0); /* Point OP_CONTINUE to the next instruction */\n\n    emit_byte(p, OP_FOR_IN_NEXT);\n    emit_byte(p, OP_DUP);\n    emit_byte(p, OP_JMP_FALSE);\n    off_check_end = p->cur_idx;\n    emit_init_offset(p);\n\n    // Parse loop body\n    if(p->tok.tok == TOK_OPEN_CURLY) {\n        if((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res;\n        pnext1(p);\n    } else {\n        if((res = parse_statement(p)) != MJS_OK) return res;\n    }\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_CONTINUE);\n\n    /* jump cond -> break */\n    mjs_bcode_insert_offset(\n        p, p->mjs, off_check_end, p->cur_idx - off_check_end - MJS_INIT_OFFSET_SIZE);\n\n    /* NOTE: jump C -> cond link is already established, it's constant: zero */\n\n    emit_byte(p, OP_BREAK);\n\n    /* jump B -> cond */\n    mjs_bcode_insert_offset(p, p->mjs, off_b, p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE);\n\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_DEL_SCOPE);\n\n    return res;\n}\n\nstatic int check_for_in(struct pstate* p) {\n    struct pstate saved = *p;\n    int forin = 0;\n    if(p->tok.tok == TOK_KEYWORD_LET) pnext1(p);\n    if(p->tok.tok == TOK_IDENT) {\n        pnext1(p);\n        if(p->tok.tok == TOK_KEYWORD_IN) forin = 1;\n    }\n    *p = saved;\n    return forin;\n}\n\nstatic mjs_err_t parse_for(struct pstate* p) {\n    mjs_err_t res = MJS_OK;\n    size_t off_b, off_c, off_init_end;\n    size_t off_incr_begin, off_cond_begin, off_cond_end;\n    int buf_cur_idx;\n\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s]\", 10, p->tok.ptr));\n    EXPECT(p, TOK_KEYWORD_FOR);\n    EXPECT(p, TOK_OPEN_PAREN);\n\n    /* Look forward - is it for..in ? */\n    if(check_for_in(p)) return parse_for_in(p);\n\n    /*\n   * BC is a break+continue offsets (a part of OP_LOOP opcode)\n   *\n   *  BC init  incr  cond  body  break  del_scope\n   *  ||    |  ^     ^  |        ^      ^\n   *  ||    +--|-----+  |        |      |\n   *  |+-------+        +--------+      |\n   *  +---------------------------------+\n   *\n   * The order to setup links:\n   *\n   *   cond -> break\n   *   init -> cond\n   *   C -> incr\n   *   B -> del_scope\n   */\n\n    /* new scope should be pushed before OP_LOOP instruction */\n    emit_byte(p, OP_NEW_SCOPE);\n\n    /* Before parsing condition statement, push break/continue offsets  */\n    emit_byte(p, OP_LOOP);\n    off_b = p->cur_idx;\n    emit_init_offset(p);\n    off_c = p->cur_idx;\n    emit_init_offset(p);\n\n    /* Parse init statement */\n    if(p->tok.tok == TOK_KEYWORD_LET) {\n        if((res = parse_let(p)) != MJS_OK) return res;\n    } else {\n        if((res = parse_expr(p)) != MJS_OK) return res;\n    }\n    EXPECT(p, TOK_SEMICOLON);\n    emit_byte(p, OP_DROP);\n\n    emit_byte(p, OP_JMP);\n    off_init_end = p->cur_idx;\n    emit_init_offset(p);\n\n    off_incr_begin = p->cur_idx;\n    off_cond_begin = p->cur_idx;\n\n    /* Parse cond statement */\n    if((res = parse_expr(p)) != MJS_OK) return res;\n    EXPECT(p, TOK_SEMICOLON);\n\n    /* Parse incr statement */\n    /* Incr statement should be placed before cond, so, adjust cur_idx */\n    buf_cur_idx = p->cur_idx;\n    p->cur_idx = off_incr_begin;\n\n    if((res = parse_expr(p)) != MJS_OK) return res;\n    EXPECT(p, TOK_CLOSE_PAREN);\n    emit_byte(p, OP_DROP);\n\n    /*\n   * Now incr is inserted before cond, so we adjust cur_idx back, and set\n   * off_cond_begin to the correct value\n   */\n    {\n        int incr_size = p->cur_idx - off_incr_begin;\n        off_cond_begin += incr_size;\n        p->cur_idx = buf_cur_idx + incr_size;\n    }\n\n    /* p->cur_idx is now at the end of \"cond\" */\n    /* Exit the loop if false */\n    emit_byte(p, OP_JMP_FALSE);\n    off_cond_end = p->cur_idx;\n    emit_init_offset(p);\n\n    /* Parse loop body */\n    if(p->tok.tok == TOK_OPEN_CURLY) {\n        if((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res;\n        pnext1(p);\n    } else {\n        if((res = parse_statement(p)) != MJS_OK) return res;\n    }\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_CONTINUE);\n\n    /* p->cur_idx is at the \"break\" item now */\n\n    /* jump cond -> break */\n    mjs_bcode_insert_offset(\n        p, p->mjs, off_cond_end, p->cur_idx - off_cond_end - MJS_INIT_OFFSET_SIZE);\n\n    /* jump init -> cond (and adjust off_incr_begin which may move) */\n    off_incr_begin += mjs_bcode_insert_offset(\n        p, p->mjs, off_init_end, off_cond_begin - off_init_end - MJS_INIT_OFFSET_SIZE);\n\n    /* jump C -> incr */\n    mjs_bcode_insert_offset(p, p->mjs, off_c, off_incr_begin - off_c - MJS_INIT_OFFSET_SIZE);\n\n    emit_byte(p, OP_BREAK);\n\n    /* jump B -> del_scope */\n    mjs_bcode_insert_offset(p, p->mjs, off_b, p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE);\n\n    emit_byte(p, OP_DEL_SCOPE);\n\n    return res;\n}\n\nstatic mjs_err_t parse_while(struct pstate* p) {\n    size_t off_cond_end, off_b;\n    mjs_err_t res = MJS_OK;\n\n    EXPECT(p, TOK_KEYWORD_WHILE);\n    EXPECT(p, TOK_OPEN_PAREN);\n\n    /* new scope should be pushed before OP_LOOP instruction */\n    emit_byte(p, OP_NEW_SCOPE);\n\n    /*\n   * BC is a break+continue offsets (a part of OP_LOOP opcode)\n   *\n   *   BC cond body break del_scope\n   *   || ^  |      ^     ^\n   *   || |  |      |     |\n   *   |+-+  +------+     |\n   *   +------------------+\n   *\n   * The order to setup links:\n   *\n   *    cond -> break\n   *    C -> cond\n   *    B -> del_scope\n   */\n\n    emit_byte(p, OP_LOOP);\n    off_b = p->cur_idx;\n    emit_init_offset(p);\n    emit_byte(p, 0); /* Point OP_CONTINUE to the next instruction */\n\n    // parse condition statement\n    if((res = parse_expr(p)) != MJS_OK) return res;\n    EXPECT(p, TOK_CLOSE_PAREN);\n\n    // Exit the loop if false\n    emit_byte(p, OP_JMP_FALSE);\n    off_cond_end = p->cur_idx;\n    emit_init_offset(p);\n\n    // Parse loop body\n    if(p->tok.tok == TOK_OPEN_CURLY) {\n        if((res = parse_statement_list(p, TOK_CLOSE_CURLY)) != MJS_OK) return res;\n        pnext1(p);\n    } else {\n        if((res = parse_statement(p)) != MJS_OK) return res;\n    }\n    emit_byte(p, OP_DROP);\n    emit_byte(p, OP_CONTINUE);\n\n    /* jump cond -> break */\n    mjs_bcode_insert_offset(\n        p, p->mjs, off_cond_end, p->cur_idx - off_cond_end - MJS_INIT_OFFSET_SIZE);\n\n    /* NOTE: jump C -> cond link is already established, it's constant: zero */\n\n    emit_byte(p, OP_BREAK);\n\n    /* jump B -> cond */\n    mjs_bcode_insert_offset(p, p->mjs, off_b, p->cur_idx - off_b - MJS_INIT_OFFSET_SIZE);\n\n    emit_byte(p, OP_DEL_SCOPE);\n    return res;\n}\n\nstatic mjs_err_t parse_if(struct pstate* p) {\n    size_t off_if, off_endif;\n    mjs_err_t res = MJS_OK;\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s]\", 10, p->tok.ptr));\n    EXPECT(p, TOK_KEYWORD_IF);\n    EXPECT(p, TOK_OPEN_PAREN);\n    if((res = parse_expr(p)) != MJS_OK) return res;\n\n    emit_byte(p, OP_JMP_FALSE);\n    off_if = p->cur_idx;\n    emit_init_offset(p);\n\n    EXPECT(p, TOK_CLOSE_PAREN);\n    if((res = parse_block_or_stmt(p, 1)) != MJS_OK) return res;\n\n    if(p->tok.tok == TOK_KEYWORD_ELSE) {\n        /*\n     * Else clause is present, so, if the condition is not true, the jump\n     * target (off_endif) should be not the current offset, but the offset\n     * after jump-over-else opcode\n     */\n        size_t off_else, off_endelse;\n        pnext1(p);\n        emit_byte(p, OP_JMP);\n        off_else = p->cur_idx;\n        emit_init_offset(p);\n        off_endif = p->cur_idx;\n\n        emit_byte(p, OP_DROP);\n        if((res = parse_block_or_stmt(p, 1)) != MJS_OK) return res;\n        off_endelse = p->cur_idx;\n\n        /*\n     * NOTE: if inserting offset causes the code to move, off_endif needs to be\n     * adjusted\n     */\n        off_endif += mjs_bcode_insert_offset(\n            p, p->mjs, off_else, off_endelse - off_else - MJS_INIT_OFFSET_SIZE);\n    } else {\n        /* Else clause is not present, so, current offset is a jump target\n     * (off_endif) */\n        off_endif = p->cur_idx;\n    }\n\n    mjs_bcode_insert_offset(p, p->mjs, off_if, off_endif - off_if - MJS_INIT_OFFSET_SIZE);\n\n    return res;\n}\n\nstatic void pstate_revert(struct pstate* p, struct pstate* old, int old_bcode_gen_len) {\n    p->pos = old->pos;\n    p->line_no = old->line_no;\n    p->last_emitted_line_no = old->last_emitted_line_no;\n    p->offset_lineno_map.len = old->offset_lineno_map.len;\n    p->prev_tok = old->prev_tok;\n    p->tok = old->tok;\n    p->mjs->bcode_gen.len = old_bcode_gen_len;\n    p->cur_idx = old->cur_idx;\n    p->depth = old->depth;\n}\n\nstatic mjs_err_t parse_return(struct pstate* p) {\n    int old_bcode_gen_len;\n    struct pstate p_saved;\n    EXPECT(p, TOK_KEYWORD_RETURN);\n    p_saved = *p;\n    old_bcode_gen_len = p->mjs->bcode_gen.len;\n    if(parse_expr(p) != MJS_OK) {\n        /*\n     * Failed to parse an expression to return, so return the parser to the\n     * prior state and push undefined.\n     */\n        pstate_revert(p, &p_saved, old_bcode_gen_len);\n        emit_byte(p, OP_PUSH_UNDEF);\n    }\n    emit_byte(p, OP_SETRETVAL);\n    emit_byte(p, OP_RETURN);\n    return MJS_OK;\n}\n\nstatic mjs_err_t parse_statement(struct pstate* p) {\n    LOG(LL_VERBOSE_DEBUG, (\"[%.*s]\", 10, p->tok.ptr));\n    switch(p->tok.tok) {\n    case TOK_SEMICOLON:\n        emit_byte(p, OP_PUSH_UNDEF);\n        pnext1(p);\n        return MJS_OK;\n    case TOK_KEYWORD_LET:\n    case TOK_KEYWORD_VAR:\n    case TOK_KEYWORD_CONST:\n        return parse_let(p);\n    case TOK_OPEN_CURLY:\n        return parse_block(p, 1);\n    case TOK_KEYWORD_RETURN:\n        return parse_return(p);\n    case TOK_KEYWORD_FOR:\n        return parse_for(p);\n    case TOK_KEYWORD_WHILE:\n        return parse_while(p);\n    case TOK_KEYWORD_BREAK:\n        emit_byte(p, OP_PUSH_UNDEF);\n        emit_byte(p, OP_BREAK);\n        pnext1(p);\n        return MJS_OK;\n    case TOK_KEYWORD_CONTINUE:\n        emit_byte(p, OP_CONTINUE);\n        pnext1(p);\n        return MJS_OK;\n    case TOK_KEYWORD_IF:\n        return parse_if(p);\n    case TOK_KEYWORD_CASE:\n    case TOK_KEYWORD_CATCH:\n    case TOK_KEYWORD_DELETE:\n    case TOK_KEYWORD_DO:\n    case TOK_KEYWORD_INSTANCEOF:\n    case TOK_KEYWORD_NEW:\n    case TOK_KEYWORD_SWITCH:\n    case TOK_KEYWORD_THROW:\n    case TOK_KEYWORD_TRY:\n    case TOK_KEYWORD_VOID:\n    case TOK_KEYWORD_WITH:\n        mjs_set_errorf(\n            p->mjs, MJS_SYNTAX_ERROR, \"[%.*s] is not implemented\", p->tok.len, p->tok.ptr);\n        return MJS_SYNTAX_ERROR;\n    default: {\n        mjs_err_t res = MJS_OK;\n        for(;;) {\n            if((res = parse_expr(p)) != MJS_OK) return res;\n            if(p->tok.tok != TOK_COMMA) break;\n            emit_byte(p, OP_DROP);\n            pnext1(p);\n        }\n        return res;\n    }\n    }\n}\n\nMJS_PRIVATE mjs_err_t mjs_parse(const char* path, const char* buf, struct mjs* mjs) {\n    mjs_err_t res = MJS_OK;\n    struct pstate p;\n    size_t start_idx, llen;\n    int map_len;\n    mjs_header_item_t bcode_offset, map_offset, total_size;\n\n    pinit(path, buf, &p);\n    p.mjs = mjs;\n    p.cur_idx = p.mjs->bcode_gen.len;\n    emit_byte(&p, OP_BCODE_HEADER);\n\n    /*\n   * TODO(dfrank): don't access mjs->bcode_gen directly, use emit_... API which\n   * takes care of p->cur_idx\n   */\n\n    /* Remember starting bcode position, and reserve the room for bcode header */\n    start_idx = p.mjs->bcode_gen.len;\n    mbuf_append(&p.mjs->bcode_gen, NULL, sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT);\n\n    /* Append NULL-terminated filename */\n    mbuf_append(&p.mjs->bcode_gen, path, strlen(path) + 1 /* null-terminate */);\n\n    bcode_offset = p.mjs->bcode_gen.len - start_idx;\n    memcpy(\n        p.mjs->bcode_gen.buf + start_idx + sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET,\n        &bcode_offset,\n        sizeof(mjs_header_item_t));\n\n    p.start_bcode_idx = p.mjs->bcode_gen.len;\n    p.cur_idx = p.mjs->bcode_gen.len;\n\n    res = parse_statement_list(&p, TOK_EOF);\n    emit_byte(&p, OP_EXIT);\n\n    /* remember map offset */\n    map_offset = p.mjs->bcode_gen.len - start_idx;\n    memcpy(\n        p.mjs->bcode_gen.buf + start_idx + sizeof(mjs_header_item_t) * MJS_HDR_ITEM_MAP_OFFSET,\n        &map_offset,\n        sizeof(mjs_header_item_t));\n\n    /* put map length varint */\n    map_len = p.offset_lineno_map.len;\n    llen = cs_varint_llen(map_len);\n    mbuf_resize(&p.mjs->bcode_gen, p.mjs->bcode_gen.size + llen);\n    cs_varint_encode(map_len, (uint8_t*)p.mjs->bcode_gen.buf + p.mjs->bcode_gen.len, llen);\n    p.mjs->bcode_gen.len += llen;\n\n    /* put the map itself */\n    mbuf_append(&p.mjs->bcode_gen, p.offset_lineno_map.buf, p.offset_lineno_map.len);\n\n    total_size = p.mjs->bcode_gen.len - start_idx;\n    memcpy(\n        p.mjs->bcode_gen.buf + start_idx + sizeof(mjs_header_item_t) * MJS_HDR_ITEM_TOTAL_SIZE,\n        &total_size,\n        sizeof(mjs_header_item_t));\n\n    mbuf_free(&p.offset_lineno_map);\n\n    /*\n   * If parsing was successful, commit the bcode; otherwise drop generated\n   * bcode\n   */\n    if(res == MJS_OK) {\n        mjs_bcode_commit(mjs);\n    } else {\n        mbuf_free(&mjs->bcode_gen);\n    }\n\n    return res;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_parser.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_PARSER_H\n#define MJS_PARSER_H\n\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nMJS_PRIVATE mjs_err_t mjs_parse(const char* path, const char* buf, struct mjs*);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_PARSER_H */\n"
  },
  {
    "path": "lib/mjs/mjs_primitive.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string_public.h\"\n#include \"mjs_util.h\"\n\n#include <float.h>\n\nmjs_val_t mjs_mk_null(void) {\n    return MJS_NULL;\n}\n\nint mjs_is_null(mjs_val_t v) {\n    return v == MJS_NULL;\n}\n\nmjs_val_t mjs_mk_undefined(void) {\n    return MJS_UNDEFINED;\n}\n\nint mjs_is_undefined(mjs_val_t v) {\n    return v == MJS_UNDEFINED;\n}\n\nmjs_val_t mjs_mk_number(struct mjs* mjs, double v) {\n    mjs_val_t res;\n    (void)mjs;\n    /* not every NaN is a JS NaN */\n    if(isnan(v)) {\n        res = MJS_TAG_NAN;\n    } else {\n        union {\n            double d;\n            mjs_val_t r;\n        } u;\n        u.d = v;\n        res = u.r;\n    }\n    return res;\n}\n\nstatic double get_double(mjs_val_t v) {\n    union {\n        double d;\n        mjs_val_t v;\n    } u;\n    u.v = v;\n    /* Due to NaN packing, any non-numeric value is already a valid NaN value */\n    return u.d;\n}\n\ndouble mjs_get_double(struct mjs* mjs, mjs_val_t v) {\n    (void)mjs;\n    return get_double(v);\n}\n\nint mjs_get_int(struct mjs* mjs, mjs_val_t v) {\n    (void)mjs;\n    /*\n   * NOTE(dfrank): without double cast, all numbers >= 0x80000000 are always\n   * converted to exactly 0x80000000.\n   */\n    return (int)(unsigned int)get_double(v);\n}\n\nint32_t mjs_get_int32(struct mjs* mjs, mjs_val_t v) {\n    (void)mjs;\n    return (int32_t)get_double(v);\n}\n\nint mjs_is_number(mjs_val_t v) {\n    return v == MJS_TAG_NAN || !isnan(get_double(v));\n}\n\nmjs_val_t mjs_mk_boolean(struct mjs* mjs, int v) {\n    (void)mjs;\n    return (v ? 1 : 0) | MJS_TAG_BOOLEAN;\n}\n\nint mjs_get_bool(struct mjs* mjs, mjs_val_t v) {\n    (void)mjs;\n    if(mjs_is_boolean(v)) {\n        return v & 1;\n    } else {\n        return 0;\n    }\n}\n\nint mjs_is_boolean(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_BOOLEAN;\n}\n\n#define MJS_IS_POINTER_LEGIT(n) \\\n    (((n) & MJS_TAG_MASK) == 0 || ((n) & MJS_TAG_MASK) == (~0 & MJS_TAG_MASK))\n\nMJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs* mjs, void* p) {\n    uint64_t n = ((uint64_t)(uintptr_t)p);\n\n    if(!MJS_IS_POINTER_LEGIT(n)) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"invalid pointer value: %p\", p);\n    }\n    return n & ~MJS_TAG_MASK;\n}\n\nMJS_PRIVATE mjs_val_t mjs_legit_pointer_to_value(void* p) {\n    uint64_t n = ((uint64_t)(uintptr_t)p);\n\n    assert(MJS_IS_POINTER_LEGIT(n));\n    return n & ~MJS_TAG_MASK;\n}\n\nMJS_PRIVATE void* get_ptr(mjs_val_t v) {\n    return (void*)(uintptr_t)(v & 0xFFFFFFFFFFFFUL);\n}\n\nvoid* mjs_get_ptr(struct mjs* mjs, mjs_val_t v) {\n    (void)mjs;\n    if(!mjs_is_foreign(v)) {\n        return NULL;\n    }\n    return get_ptr(v);\n}\n\nmjs_val_t mjs_mk_foreign(struct mjs* mjs, void* p) {\n    (void)mjs;\n    return mjs_pointer_to_value(mjs, p) | MJS_TAG_FOREIGN;\n}\n\nmjs_val_t mjs_mk_foreign_func(struct mjs* mjs, mjs_func_ptr_t fn) {\n    union {\n        mjs_func_ptr_t fn;\n        void* p;\n    } u;\n    u.fn = fn;\n    (void)mjs;\n    return mjs_pointer_to_value(mjs, u.p) | MJS_TAG_FOREIGN;\n}\n\nint mjs_is_foreign(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_FOREIGN;\n}\n\nmjs_val_t mjs_mk_function(struct mjs* mjs, size_t off) {\n    (void)mjs;\n    return (mjs_val_t)off | MJS_TAG_FUNCTION;\n}\n\nint mjs_is_function(mjs_val_t v) {\n    return (v & MJS_TAG_MASK) == MJS_TAG_FUNCTION;\n}\n\nMJS_PRIVATE void mjs_op_isnan(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t val = mjs_arg(mjs, 0);\n\n    ret = mjs_mk_boolean(mjs, val == MJS_TAG_NAN);\n\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t base_v = MJS_UNDEFINED;\n    int32_t base = 10;\n    double num;\n\n    /* get number from `this` */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_NUMBER, NULL)) {\n        goto clean;\n    }\n    num = mjs_get_double(mjs, mjs->vals.this_obj);\n\n    if(mjs_nargs(mjs) >= 1) {\n        /* get base from arg 0 */\n        if(!mjs_check_arg(mjs, 0, \"base\", MJS_TYPE_NUMBER, &base_v)) {\n            goto clean;\n        }\n        base = mjs_get_int(mjs, base_v);\n    }\n\n    if(base != 10 || floor(num) == num) {\n        char tmp_str[] = \"-2147483648\";\n        itoa((int32_t)num, tmp_str, base);\n        ret = mjs_mk_string(mjs, tmp_str, ~0, true);\n    } else {\n        char tmp_str[] = \"2.22514337200000e-308\";\n        snprintf(tmp_str, sizeof(tmp_str), \"%.*g\", DBL_DIG, num);\n        size_t len = strlen(tmp_str);\n        while(len && tmp_str[len - 1] == '0') {\n            len--;\n        }\n        tmp_str[len] = '\\0';\n        ret = mjs_mk_string(mjs, tmp_str, ~0, true);\n    }\n\nclean:\n    mjs_return(mjs, ret);\n}\n"
  },
  {
    "path": "lib/mjs/mjs_primitive.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_PRIMITIVE_H\n#define MJS_PRIMITIVE_H\n\n#include \"mjs_primitive_public.h\"\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Convert a pointer to mjs_val_t. If pointer is not valid, mjs crashes.\n */\nMJS_PRIVATE mjs_val_t mjs_legit_pointer_to_value(void* p);\n\n/*\n * Convert a pointer to mjs_val_t. If pointer is not valid, error is set\n * in the mjs context.\n */\nMJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs* mjs, void* p);\n\n/*\n * Extracts a pointer from the mjs_val_t value.\n */\nMJS_PRIVATE void* get_ptr(mjs_val_t v);\n\n/*\n * Implementation for JS isNaN()\n */\nMJS_PRIVATE void mjs_op_isnan(struct mjs* mjs);\n\n/*\n * Implementation for JS Number.toString()\n */\nMJS_PRIVATE void mjs_number_to_string(struct mjs* mjs);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_PRIMITIVE_H */\n"
  },
  {
    "path": "lib/mjs/mjs_primitive_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_PRIMITIVE_PUBLIC_H_\n#define MJS_PRIMITIVE_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/* JavaScript `null` value */\n#define MJS_NULL MJS_TAG_NULL\n\n/* JavaScript `undefined` value */\n#define MJS_UNDEFINED MJS_TAG_UNDEFINED\n\n#define MJS_MK_FN(fn) mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)fn)\n\n/* Function pointer type used in `mjs_mk_foreign_func`. */\ntypedef void (*mjs_func_ptr_t)(void);\n\n/*\n * Make `null` primitive value.\n *\n * NOTE: this function is deprecated and will be removed in future releases.\n * Use `MJS_NULL` instead.\n */\nmjs_val_t mjs_mk_null(void);\n\n/* Returns true if given value is a primitive `null` value */\nint mjs_is_null(mjs_val_t v);\n\n/*\n * Make `undefined` primitive value.\n *\n * NOTE: this function is deprecated and will be removed in future releases.\n * Use `MJS_UNDEFINED` instead.\n */\nmjs_val_t mjs_mk_undefined(void);\n\n/* Returns true if given value is a primitive `undefined` value */\nint mjs_is_undefined(mjs_val_t v);\n\n/* Make numeric primitive value */\nmjs_val_t mjs_mk_number(struct mjs* mjs, double num);\n\n/*\n * Returns number value stored in `mjs_val_t` as `double`.\n *\n * Returns NaN for non-numbers.\n */\ndouble mjs_get_double(struct mjs* mjs, mjs_val_t v);\n\n/*\n * Returns number value stored in `mjs_val_t` as `int`. If the number value is\n * not an integer, the fraction part will be discarded.\n *\n * If the given value is a non-number, or NaN, the result is undefined.\n */\nint mjs_get_int(struct mjs* mjs, mjs_val_t v);\n\n/*\n * Like mjs_get_int but ensures that the returned type\n * is a 32-bit signed integer.\n */\nint32_t mjs_get_int32(struct mjs* mjs, mjs_val_t v);\n\n/* Returns true if given value is a primitive number value */\nint mjs_is_number(mjs_val_t v);\n\n/*\n * Make JavaScript value that holds C/C++ `void *` pointer.\n *\n * A foreign value is completely opaque and JS code cannot do anything useful\n * with it except holding it in properties and passing it around.\n * It behaves like a sealed object with no properties.\n *\n * NOTE:\n * Only valid pointers (as defined by each supported architecture) will fully\n * preserved. In particular, all supported 64-bit architectures (x86_64, ARM-64)\n * actually define a 48-bit virtual address space.\n * Foreign values will be sign-extended as required, i.e creating a foreign\n * value of something like `(void *) -1` will work as expected. This is\n * important because in some 64-bit OSs (e.g. Solaris) the user stack grows\n * downwards from the end of the address space.\n *\n * If you need to store exactly sizeof(void*) bytes of raw data where\n * `sizeof(void*)` >= 8, please use byte arrays instead.\n */\nmjs_val_t mjs_mk_foreign(struct mjs* mjs, void* ptr);\n\n/*\n * Make JavaScript value that holds C/C++ function pointer, similarly to\n * `mjs_mk_foreign`.\n */\nmjs_val_t mjs_mk_foreign_func(struct mjs* mjs, mjs_func_ptr_t fn);\n\n/*\n * Returns `void *` pointer stored in `mjs_val_t`.\n *\n * Returns NULL if the value is not a foreign pointer.\n */\nvoid* mjs_get_ptr(struct mjs* mjs, mjs_val_t v);\n\n/* Returns true if given value holds `void *` pointer */\nint mjs_is_foreign(mjs_val_t v);\n\nmjs_val_t mjs_mk_boolean(struct mjs* mjs, int v);\nint mjs_get_bool(struct mjs* mjs, mjs_val_t v);\nint mjs_is_boolean(mjs_val_t v);\n\nmjs_val_t mjs_mk_function(struct mjs* mjs, size_t off);\nint mjs_is_function(mjs_val_t v);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_PRIMITIVE_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_string.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"mjs_string.h\"\n#include \"common/cs_varint.h\"\n#include \"common/mg_str.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_util.h\"\n\n// No UTF\ntypedef unsigned short Rune;\nstatic int chartorune(Rune* rune, const char* str) {\n    *rune = *(unsigned char*)str;\n    return 1;\n}\nstatic int runetochar(char* str, Rune* rune) {\n    str[0] = (char)*rune;\n    return 1;\n}\n\n#ifndef MJS_STRING_BUF_RESERVE\n#define MJS_STRING_BUF_RESERVE 100\n#endif\n\nMJS_PRIVATE size_t unescape(const char* s, size_t len, char* to);\n\nMJS_PRIVATE void embed_string(\n    struct mbuf* m,\n    size_t offset,\n    const char* p,\n    size_t len,\n    uint8_t /*enum embstr_flags*/ flags);\n\n/* TODO(lsm): NaN payload location depends on endianness, make crossplatform */\n#define GET_VAL_NAN_PAYLOAD(v) ((char*)&(v))\n\nint mjs_is_string(mjs_val_t v) {\n    uint64_t t = v & MJS_TAG_MASK;\n    return t == MJS_TAG_STRING_I || t == MJS_TAG_STRING_F || t == MJS_TAG_STRING_O ||\n           t == MJS_TAG_STRING_5 || t == MJS_TAG_STRING_D;\n}\n\nmjs_val_t mjs_mk_string(struct mjs* mjs, const char* p, size_t len, int copy) {\n    struct mbuf* m;\n    mjs_val_t offset, tag = MJS_TAG_STRING_F;\n    if(len == 0) {\n        /*\n     * Zero length for foreign string has a special meaning (that the foreign\n     * string is not inlined into mjs_val_t), so when creating a zero-length\n     * string, we always assume it'll be owned. Since the length is zero, it\n     * doesn't matter anyway.\n     */\n        copy = 1;\n    }\n    m = copy ? &mjs->owned_strings : &mjs->foreign_strings;\n    offset = m->len;\n\n    if(len == ~((size_t)0)) len = strlen(p);\n\n    if(copy) {\n        /* owned string */\n        if(len <= 4) {\n            char* s = GET_VAL_NAN_PAYLOAD(offset) + 1;\n            offset = 0;\n            if(p != 0) {\n                memcpy(s, p, len);\n            }\n            s[-1] = len;\n            tag = MJS_TAG_STRING_I;\n        } else if(len == 5) {\n            char* s = GET_VAL_NAN_PAYLOAD(offset);\n            offset = 0;\n            if(p != 0) {\n                memcpy(s, p, len);\n            }\n            tag = MJS_TAG_STRING_5;\n            // } else if ((dict_index = v_find_string_in_dictionary(p, len)) >= 0) {\n            //   offset = 0;\n            //   GET_VAL_NAN_PAYLOAD(offset)[0] = dict_index;\n            //   tag = MJS_TAG_STRING_D;\n        } else {\n            if(gc_strings_is_gc_needed(mjs)) {\n                mjs->need_gc = 1;\n            }\n\n            /*\n       * Before embedding new string, check if the reallocation is needed.  If\n       * so, perform the reallocation by calling `mbuf_resize` manually, since\n       * we need to preallocate some extra space (`MJS_STRING_BUF_RESERVE`)\n       */\n            if((m->len + len) > m->size) {\n                char* prev_buf = m->buf;\n                mbuf_resize(m, m->len + len + MJS_STRING_BUF_RESERVE);\n\n                /*\n         * There is a corner case: when the source pointer is located within\n         * the mbuf. In this case, we should adjust the pointer, because it\n         * might have just been reallocated.\n         */\n                if(p >= prev_buf && p < (prev_buf + m->len)) {\n                    p += (m->buf - prev_buf);\n                }\n            }\n\n            embed_string(m, m->len, p, len, EMBSTR_ZERO_TERM);\n            tag = MJS_TAG_STRING_O;\n        }\n    } else {\n        /* foreign string */\n        if(sizeof(void*) <= 4 && len <= (1 << 15)) {\n            /* small foreign strings can fit length and ptr in the mjs_val_t */\n            offset = (uint64_t)len << 32 | (uint64_t)(uintptr_t)p;\n        } else {\n            /* bigger strings need indirection that uses ram */\n            size_t pos = m->len;\n            size_t llen = cs_varint_llen(len);\n\n            /* allocate space for len and ptr */\n            mbuf_insert(m, pos, NULL, llen + sizeof(p));\n\n            cs_varint_encode(len, (uint8_t*)(m->buf + pos), llen);\n            memcpy(m->buf + pos + llen, &p, sizeof(p));\n        }\n        tag = MJS_TAG_STRING_F;\n    }\n\n    /* NOTE(lsm): don't use pointer_to_value, 32-bit ptrs will truncate */\n    return (offset & ~MJS_TAG_MASK) | tag;\n}\n\n/* Get a pointer to string and string length. */\nconst char* mjs_get_string(struct mjs* mjs, mjs_val_t* v, size_t* sizep) {\n    uint64_t tag = v[0] & MJS_TAG_MASK;\n    const char* p = NULL;\n    size_t size = 0, llen;\n\n    if(!mjs_is_string(*v)) {\n        goto clean;\n    }\n\n    if(tag == MJS_TAG_STRING_I) {\n        p = GET_VAL_NAN_PAYLOAD(*v) + 1;\n        size = p[-1];\n    } else if(tag == MJS_TAG_STRING_5) {\n        p = GET_VAL_NAN_PAYLOAD(*v);\n        size = 5;\n        // } else if (tag == MJS_TAG_STRING_D) {\n        //   int index = ((unsigned char *) GET_VAL_NAN_PAYLOAD(*v))[0];\n        //   size = v_dictionary_strings[index].len;\n        //   p = v_dictionary_strings[index].p;\n    } else if(tag == MJS_TAG_STRING_O) {\n        size_t offset = (size_t)gc_string_mjs_val_to_offset(*v);\n        char* s = mjs->owned_strings.buf + offset;\n        uint64_t v = 0;\n        if(offset < mjs->owned_strings.len &&\n           cs_varint_decode((uint8_t*)s, mjs->owned_strings.len - offset, &v, &llen)) {\n            size = v;\n            p = s + llen;\n        } else {\n            goto clean;\n        }\n    } else if(tag == MJS_TAG_STRING_F) {\n        /*\n     * short foreign strings on <=32-bit machines can be encoded in a compact\n     * form:\n     *\n     *     7         6        5        4        3        2        1        0\n     *  11111111|1111tttt|llllllll|llllllll|ssssssss|ssssssss|ssssssss|ssssssss\n     *\n     * Strings longer than 2^26 will be indireceted through the foreign_strings\n     * mbuf.\n     *\n     * We don't use a different tag to represent those two cases. Instead, all\n     * foreign strings represented with the help of the foreign_strings mbuf\n     * will have the upper 16-bits of the payload set to zero. This allows us to\n     * represent up to 477 million foreign strings longer than 64k.\n     */\n        uint16_t len = (*v >> 32) & 0xFFFF;\n        if(sizeof(void*) <= 4 && len != 0) {\n            size = (size_t)len;\n            p = (const char*)(uintptr_t)*v;\n        } else {\n            size_t offset = (size_t)gc_string_mjs_val_to_offset(*v);\n            char* s = mjs->foreign_strings.buf + offset;\n            uint64_t v = 0;\n            if(offset < mjs->foreign_strings.len &&\n               cs_varint_decode((uint8_t*)s, mjs->foreign_strings.len - offset, &v, &llen)) {\n                size = v;\n                memcpy((char**)&p, s + llen, sizeof(p));\n            } else {\n                goto clean;\n            }\n        }\n    } else {\n        assert(0);\n    }\n\nclean:\n    if(sizep != NULL) {\n        *sizep = size;\n    }\n    return p;\n}\n\nconst char* mjs_get_cstring(struct mjs* mjs, mjs_val_t* value) {\n    size_t size;\n    const char* s = mjs_get_string(mjs, value, &size);\n    if(s == NULL) return NULL;\n    if(s[size] != 0 || strlen(s) != size) {\n        return NULL;\n    }\n    return s;\n}\n\nint mjs_strcmp(struct mjs* mjs, mjs_val_t* a, const char* b, size_t len) {\n    size_t n;\n    const char* s;\n    if(len == (size_t)~0) len = strlen(b);\n    s = mjs_get_string(mjs, a, &n);\n    if(n != len) {\n        return n - len;\n    }\n    return strncmp(s, b, len);\n}\n\nMJS_PRIVATE unsigned long cstr_to_ulong(const char* s, size_t len, int* ok) {\n    char* e;\n    unsigned long res = strtoul(s, &e, 10);\n    *ok = (e == s + len) && len != 0;\n    return res;\n}\n\nMJS_PRIVATE mjs_err_t str_to_ulong(struct mjs* mjs, mjs_val_t v, int* ok, unsigned long* res) {\n    enum mjs_err ret = MJS_OK;\n    size_t len = 0;\n    const char* p = mjs_get_string(mjs, &v, &len);\n    *res = cstr_to_ulong(p, len, ok);\n\n    return ret;\n}\n\nMJS_PRIVATE int s_cmp(struct mjs* mjs, mjs_val_t a, mjs_val_t b) {\n    size_t a_len, b_len;\n    const char *a_ptr, *b_ptr;\n\n    a_ptr = mjs_get_string(mjs, &a, &a_len);\n    b_ptr = mjs_get_string(mjs, &b, &b_len);\n\n    if(a_len == b_len) {\n        return memcmp(a_ptr, b_ptr, a_len);\n    }\n    if(a_len > b_len) {\n        return 1;\n    } else if(a_len < b_len) {\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nMJS_PRIVATE mjs_val_t s_concat(struct mjs* mjs, mjs_val_t a, mjs_val_t b) {\n    size_t a_len, b_len, res_len;\n    const char *a_ptr, *b_ptr, *res_ptr;\n    mjs_val_t res;\n\n    /* Find out lengths of both srtings */\n    a_ptr = mjs_get_string(mjs, &a, &a_len);\n    b_ptr = mjs_get_string(mjs, &b, &b_len);\n\n    /* Create a placeholder string */\n    res = mjs_mk_string(mjs, NULL, a_len + b_len, 1);\n\n    /* mjs_mk_string() may have reallocated mbuf - revalidate pointers */\n    a_ptr = mjs_get_string(mjs, &a, &a_len);\n    b_ptr = mjs_get_string(mjs, &b, &b_len);\n\n    /* Copy strings into the placeholder */\n    res_ptr = mjs_get_string(mjs, &res, &res_len);\n    memcpy((char*)res_ptr, a_ptr, a_len);\n    memcpy((char*)res_ptr + a_len, b_ptr, b_len);\n\n    return res;\n}\n\nMJS_PRIVATE void mjs_string_to_case(struct mjs* mjs, bool upper) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    size_t size;\n    const char* s = NULL;\n\n    /* get string from `this` */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_STRING, NULL)) {\n        goto clean;\n    }\n    s = mjs_get_string(mjs, &mjs->vals.this_obj, &size);\n\n    if(size == 0) {\n        ret = mjs_mk_string(mjs, \"\", 0, 1);\n        goto clean;\n    }\n\n    char* tmp = malloc(size);\n    for(size_t i = 0; i < size; i++) {\n        tmp[i] = upper ? toupper(s[i]) : tolower(s[i]);\n    }\n    ret = mjs_mk_string(mjs, tmp, size, 1);\n    free(tmp);\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_string_to_lower_case(struct mjs* mjs) {\n    mjs_string_to_case(mjs, false);\n}\n\nMJS_PRIVATE void mjs_string_to_upper_case(struct mjs* mjs) {\n    mjs_string_to_case(mjs, true);\n}\n\nMJS_PRIVATE void mjs_string_slice(struct mjs* mjs) {\n    int nargs = mjs_nargs(mjs);\n    mjs_val_t ret = mjs_mk_number(mjs, 0);\n    mjs_val_t beginSlice_v = MJS_UNDEFINED;\n    mjs_val_t endSlice_v = MJS_UNDEFINED;\n    int beginSlice = 0;\n    int endSlice = 0;\n    size_t size;\n    const char* s = NULL;\n\n    /* get string from `this` */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_STRING, NULL)) {\n        goto clean;\n    }\n    s = mjs_get_string(mjs, &mjs->vals.this_obj, &size);\n\n    /* get idx from arg 0 */\n    if(!mjs_check_arg(mjs, 0, \"beginSlice\", MJS_TYPE_NUMBER, &beginSlice_v)) {\n        goto clean;\n    }\n    beginSlice = mjs_normalize_idx(mjs_get_int(mjs, beginSlice_v), size);\n\n    if(nargs >= 2) {\n        /* endSlice is given; use it */\n        /* get idx from arg 0 */\n        if(!mjs_check_arg(mjs, 1, \"endSlice\", MJS_TYPE_NUMBER, &endSlice_v)) {\n            goto clean;\n        }\n        endSlice = mjs_normalize_idx(mjs_get_int(mjs, endSlice_v), size);\n    } else {\n        /* endSlice is not given; assume the end of the string */\n        endSlice = size;\n    }\n\n    if(endSlice < beginSlice) {\n        endSlice = beginSlice;\n    }\n\n    ret = mjs_mk_string(mjs, s + beginSlice, endSlice - beginSlice, 1);\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_string_index_of(struct mjs* mjs) {\n    mjs_val_t ret = mjs_mk_number(mjs, -1);\n    mjs_val_t substr_v = MJS_UNDEFINED;\n    mjs_val_t idx_v = MJS_UNDEFINED;\n    int idx = 0;\n    const char *str = NULL, *substr = NULL;\n    size_t str_len = 0, substr_len = 0;\n\n    if(!mjs_check_arg(mjs, -1 /* this */, \"this\", MJS_TYPE_STRING, NULL)) {\n        goto clean;\n    }\n    str = mjs_get_string(mjs, &mjs->vals.this_obj, &str_len);\n\n    if(!mjs_check_arg(mjs, 0, \"searchValue\", MJS_TYPE_STRING, &substr_v)) {\n        goto clean;\n    }\n    substr = mjs_get_string(mjs, &substr_v, &substr_len);\n    if(mjs_nargs(mjs) > 1) {\n        if(!mjs_check_arg(mjs, 1, \"fromIndex\", MJS_TYPE_NUMBER, &idx_v)) {\n            goto clean;\n        }\n        idx = mjs_get_int(mjs, idx_v);\n        if(idx < 0) idx = 0;\n        if((size_t)idx > str_len) idx = str_len;\n    }\n    {\n        const char* substr_p;\n        struct mg_str mgstr, mgsubstr;\n        mgstr.p = str + idx;\n        mgstr.len = str_len - idx;\n        mgsubstr.p = substr;\n        mgsubstr.len = substr_len;\n        substr_p = mg_strstr(mgstr, mgsubstr);\n        if(substr_p != NULL) {\n            ret = mjs_mk_number(mjs, (int)(substr_p - str));\n        }\n    }\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_string_char_code_at(struct mjs* mjs) {\n    mjs_val_t ret = MJS_UNDEFINED;\n    mjs_val_t idx_v = MJS_UNDEFINED;\n    int idx = 0;\n    size_t size;\n    const char* s = NULL;\n\n    /* get string from `this` */\n    if(!mjs_check_arg(mjs, -1 /*this*/, \"this\", MJS_TYPE_STRING, NULL)) {\n        goto clean;\n    }\n    s = mjs_get_string(mjs, &mjs->vals.this_obj, &size);\n\n    /* get idx from arg 0 */\n    if(!mjs_check_arg(mjs, 0, \"index\", MJS_TYPE_NUMBER, &idx_v)) {\n        goto clean;\n    }\n    idx = mjs_normalize_idx(mjs_get_int(mjs, idx_v), size);\n    if(idx >= 0 && idx < (int)size) {\n        ret = mjs_mk_number(mjs, ((unsigned char*)s)[idx]);\n    }\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nMJS_PRIVATE void mjs_mkstr(struct mjs* mjs) {\n    int nargs = mjs_nargs(mjs);\n    mjs_val_t ret = MJS_UNDEFINED;\n\n    char* ptr = NULL;\n    int offset = 0;\n    int len = 0;\n    int copy = 0;\n\n    mjs_val_t ptr_v = MJS_UNDEFINED;\n    mjs_val_t offset_v = MJS_UNDEFINED;\n    mjs_val_t len_v = MJS_UNDEFINED;\n    mjs_val_t copy_v = MJS_UNDEFINED;\n\n    if(nargs == 2) {\n        ptr_v = mjs_arg(mjs, 0);\n        len_v = mjs_arg(mjs, 1);\n    } else if(nargs == 3) {\n        ptr_v = mjs_arg(mjs, 0);\n        offset_v = mjs_arg(mjs, 1);\n        len_v = mjs_arg(mjs, 2);\n    } else if(nargs == 4) {\n        ptr_v = mjs_arg(mjs, 0);\n        offset_v = mjs_arg(mjs, 1);\n        len_v = mjs_arg(mjs, 2);\n        copy_v = mjs_arg(mjs, 3);\n    } else {\n        mjs_prepend_errorf(\n            mjs,\n            MJS_TYPE_ERROR,\n            \"mkstr takes 2, 3 or 4 arguments: (ptr, len), (ptr, \"\n            \"offset, len) or (ptr, offset, len, copy)\");\n        goto clean;\n    }\n\n    if(!mjs_is_foreign(ptr_v)) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"ptr should be a foreign pointer\");\n        goto clean;\n    }\n\n    if(offset_v != MJS_UNDEFINED && !mjs_is_number(offset_v)) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"offset should be a number\");\n        goto clean;\n    }\n\n    if(!mjs_is_number(len_v)) {\n        mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"len should be a number\");\n        goto clean;\n    }\n\n    copy = mjs_is_truthy(mjs, copy_v);\n\n    /* all arguments are fine */\n\n    ptr = (char*)mjs_get_ptr(mjs, ptr_v);\n    if(offset_v != MJS_UNDEFINED) {\n        offset = mjs_get_int(mjs, offset_v);\n    }\n    len = mjs_get_int(mjs, len_v);\n\n    ret = mjs_mk_string(mjs, ptr + offset, len, copy);\n\nclean:\n    mjs_return(mjs, ret);\n}\n\nenum unescape_error {\n    SLRE_INVALID_HEX_DIGIT,\n    SLRE_INVALID_ESC_CHAR,\n    SLRE_UNTERM_ESC_SEQ,\n};\n\nstatic int hex(int c) {\n    if(c >= '0' && c <= '9') return c - '0';\n    if(c >= 'a' && c <= 'f') return c - 'a' + 10;\n    if(c >= 'A' && c <= 'F') return c - 'A' + 10;\n    return -SLRE_INVALID_HEX_DIGIT;\n}\n\nstatic int nextesc(const char** p) {\n    const unsigned char* s = (unsigned char*)(*p)++;\n    switch(*s) {\n    case 0:\n        return -SLRE_UNTERM_ESC_SEQ;\n    case 'c':\n        ++*p;\n        return *s & 31;\n    case 'b':\n        return '\\b';\n    case 't':\n        return '\\t';\n    case 'n':\n        return '\\n';\n    case 'v':\n        return '\\v';\n    case 'f':\n        return '\\f';\n    case 'r':\n        return '\\r';\n    case '\\\\':\n        return '\\\\';\n    case 'u':\n        if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4])) {\n            (*p) += 4;\n            return hex(s[1]) << 12 | hex(s[2]) << 8 | hex(s[3]) << 4 | hex(s[4]);\n        }\n        return -SLRE_INVALID_HEX_DIGIT;\n    case 'x':\n        if(isxdigit(s[1]) && isxdigit(s[2])) {\n            (*p) += 2;\n            return (hex(s[1]) << 4) | hex(s[2]);\n        }\n        return -SLRE_INVALID_HEX_DIGIT;\n    default:\n        return -SLRE_INVALID_ESC_CHAR;\n    }\n}\n\nMJS_PRIVATE size_t unescape(const char* s, size_t len, char* to) {\n    const char* end = s + len;\n    size_t n = 0;\n    char tmp[4];\n    Rune r;\n\n    while(s < end) {\n        s += chartorune(&r, s);\n        if(r == '\\\\' && s < end) {\n            switch(*s) {\n            case '\"':\n                s++, r = '\"';\n                break;\n            case '\\'':\n                s++, r = '\\'';\n                break;\n            case '\\n':\n                s++, r = '\\n';\n                break;\n            default: {\n                const char* tmp_s = s;\n                int i = nextesc(&s);\n                switch(i) {\n                case -SLRE_INVALID_ESC_CHAR:\n                    r = '\\\\';\n                    s = tmp_s;\n                    n += runetochar(to == NULL ? tmp : to + n, &r);\n                    s += chartorune(&r, s);\n                    break;\n                case -SLRE_INVALID_HEX_DIGIT:\n                default:\n                    r = i;\n                }\n            }\n            }\n        }\n        n += runetochar(to == NULL ? tmp : to + n, &r);\n    }\n\n    return n;\n}\n\nMJS_PRIVATE void embed_string(\n    struct mbuf* m,\n    size_t offset,\n    const char* p,\n    size_t len,\n    uint8_t /*enum embstr_flags*/ flags) {\n    char* old_base = m->buf;\n    uint8_t p_backed_by_mbuf = p >= old_base && p < old_base + m->len;\n    size_t n = (flags & EMBSTR_UNESCAPE) ? unescape(p, len, NULL) : len;\n\n    /* Calculate how many bytes length takes */\n    size_t k = cs_varint_llen(n);\n\n    /* total length: varing length + string len + zero-term */\n    size_t tot_len = k + n + !!(flags & EMBSTR_ZERO_TERM);\n\n    /* Allocate buffer */\n    mbuf_insert(m, offset, NULL, tot_len);\n\n    /* Fixup p if it was relocated by mbuf_insert() above */\n    if(p_backed_by_mbuf) {\n        p += m->buf - old_base;\n    }\n\n    /* Write length */\n    cs_varint_encode(n, (unsigned char*)m->buf + offset, k);\n\n    /* Write string */\n    if(p != 0) {\n        if(flags & EMBSTR_UNESCAPE) {\n            unescape(p, len, m->buf + offset + k);\n        } else {\n            memcpy(m->buf + offset + k, p, len);\n        }\n    }\n\n    /* add NULL-terminator if needed */\n    if(flags & EMBSTR_ZERO_TERM) {\n        m->buf[offset + tot_len - 1] = '\\0';\n    }\n}\n"
  },
  {
    "path": "lib/mjs/mjs_string.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_STRING_H_\n#define MJS_STRING_H_\n\n#include \"mjs_internal.h\"\n#include \"mjs_string_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Size of the extra space for strings mbuf that is needed to avoid frequent\n * reallocations\n */\n#define _MJS_STRING_BUF_RESERVE 100\n\nMJS_PRIVATE unsigned long cstr_to_ulong(const char* s, size_t len, int* ok);\nMJS_PRIVATE mjs_err_t str_to_ulong(struct mjs* mjs, mjs_val_t v, int* ok, unsigned long* res);\nMJS_PRIVATE int s_cmp(struct mjs* mjs, mjs_val_t a, mjs_val_t b);\nMJS_PRIVATE mjs_val_t s_concat(struct mjs* mjs, mjs_val_t a, mjs_val_t b);\n\nMJS_PRIVATE void embed_string(\n    struct mbuf* m,\n    size_t offset,\n    const char* p,\n    size_t len,\n    uint8_t /*enum embstr_flags*/ flags);\n\nMJS_PRIVATE void mjs_mkstr(struct mjs* mjs);\n\nMJS_PRIVATE void mjs_string_to_lower_case(struct mjs* mjs);\nMJS_PRIVATE void mjs_string_to_upper_case(struct mjs* mjs);\nMJS_PRIVATE void mjs_string_slice(struct mjs* mjs);\nMJS_PRIVATE void mjs_string_index_of(struct mjs* mjs);\nMJS_PRIVATE void mjs_string_char_code_at(struct mjs* mjs);\n\n#define EMBSTR_ZERO_TERM 1\n#define EMBSTR_UNESCAPE 2\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_STRING_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_string_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_STRING_PUBLIC_H_\n#define MJS_STRING_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n\n#define MJS_STRING_LITERAL_MAX_LEN 128\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n * Creates a string primitive value.\n * `str` must point to the utf8 string of length `len`.\n * If `len` is ~0, `str` is assumed to be NUL-terminated and `strlen(str)` is\n * used.\n *\n * If `copy` is non-zero, the string data is copied and owned by the GC. The\n * caller can free the string data afterwards. Otherwise (`copy` is zero), the\n * caller owns the string data, and is responsible for not freeing it while it\n * is used.\n */\nmjs_val_t mjs_mk_string(struct mjs* mjs, const char* str, size_t len, int copy);\n\n/* Returns true if given value is a primitive string value */\nint mjs_is_string(mjs_val_t v);\n\n/*\n * Returns a pointer to the string stored in `mjs_val_t`.\n *\n * String length returned in `len`, which is allowed to be NULL. Returns NULL\n * if the value is not a string.\n *\n * JS strings can contain embedded NUL chars and may or may not be NUL\n * terminated.\n *\n * CAUTION: creating new JavaScript object, array, or string may kick in a\n * garbage collector, which in turn may relocate string data and invalidate\n * pointer returned by `mjs_get_string()`.\n *\n * Short JS strings are embedded inside the `mjs_val_t` value itself. This\n * is why a pointer to a `mjs_val_t` is required. It also means that the string\n * data will become invalid once that `mjs_val_t` value goes out of scope.\n */\nconst char* mjs_get_string(struct mjs* mjs, mjs_val_t* v, size_t* len);\n\n/*\n * Returns a pointer to the string stored in `mjs_val_t`.\n *\n * Returns NULL if the value is not a string or if the string is not compatible\n * with a C string.\n *\n * C compatible strings contain exactly one NUL char, in terminal position.\n *\n * All strings owned by the MJS engine (see `mjs_mk_string()`) are guaranteed to\n * be NUL terminated. Out of these, those that don't include embedded NUL chars\n * are guaranteed to be C compatible.\n */\nconst char* mjs_get_cstring(struct mjs* mjs, mjs_val_t* v);\n\n/*\n * Returns the standard strcmp comparison code after comparing a JS string a\n * with a possibly non null-terminated string b. NOTE: the strings are equal\n * only if their length is equal, i.e. the len field doesn't imply strncmp\n * behaviour.\n */\nint mjs_strcmp(struct mjs* mjs, mjs_val_t* a, const char* b, size_t len);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_STRING_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_tok.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"common/cs_dbg.h\"\n#include \"mjs_tok.h\"\n\nMJS_PRIVATE void pinit(const char* file_name, const char* buf, struct pstate* p) {\n    memset(p, 0, sizeof(*p));\n    p->line_no = 1;\n    p->last_emitted_line_no = 1;\n    p->file_name = file_name;\n    p->buf = p->pos = buf;\n    mbuf_init(&p->offset_lineno_map, 0);\n}\n\n// We're not relying on the target libc ctype, as it may incorrectly\n// handle negative arguments, e.g. isspace(-1).\nstatic int mjs_is_space(int c) {\n    return c == ' ' || c == '\\r' || c == '\\n' || c == '\\t' || c == '\\f' || c == '\\v';\n}\n\nMJS_PRIVATE int mjs_is_digit(int c) {\n    return c >= '0' && c <= '9';\n}\n\nstatic int mjs_is_alpha(int c) {\n    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');\n}\n\nMJS_PRIVATE int mjs_is_ident(int c) {\n    return c == '_' || c == '$' || mjs_is_alpha(c);\n}\n\n// Try to parse a token that can take one or two chars.\nstatic int longtok(struct pstate* p, const char* first_chars, const char* second_chars) {\n    if(strchr(first_chars, p->pos[0]) == NULL) return TOK_EOF;\n    if(p->pos[1] != '\\0' && strchr(second_chars, p->pos[1]) != NULL) {\n        p->tok.len++;\n        p->pos++;\n        return p->pos[-1] << 8 | p->pos[0];\n    }\n    return p->pos[0];\n}\n\n// Try to parse a token that takes exactly 3 chars.\nstatic int longtok3(struct pstate* p, char a, char b, char c) {\n    if(p->pos[0] == a && p->pos[1] == b && p->pos[2] == c) {\n        p->tok.len += 2;\n        p->pos += 2;\n        return p->pos[-2] << 16 | p->pos[-1] << 8 | p->pos[0];\n    }\n    return TOK_EOF;\n}\n\n// Try to parse a token that takes exactly 4 chars.\nstatic int longtok4(struct pstate* p, char a, char b, char c, char d) {\n    if(p->pos[0] == a && p->pos[1] == b && p->pos[2] == c && p->pos[3] == d) {\n        p->tok.len += 3;\n        p->pos += 3;\n        return p->pos[-3] << 24 | p->pos[-2] << 16 | p->pos[-1] << 8 | p->pos[0];\n    }\n    return TOK_EOF;\n}\n\nstatic int getnum(struct pstate* p) {\n    if(p->pos[0] == '0' && p->pos[1] == 'x') {\n        // MSVC6 strtod cannot parse 0x... numbers, thus this ugly workaround.\n        strtoul(p->pos + 2, (char**)&p->pos, 16);\n    } else {\n        strtod(p->pos, (char**)&p->pos);\n    }\n    p->tok.len = p->pos - p->tok.ptr;\n    p->pos--;\n    return TOK_NUM;\n}\n\nstatic int is_reserved_word_token(const char* s, int len) {\n    const char* reserved[] = {\"break\",      \"case\",      \"catch\",    \"continue\", \"debugger\",\n                              \"default\",    \"delete\",    \"do\",       \"else\",     \"false\",\n                              \"finally\",    \"for\",       \"function\", \"if\",       \"in\",\n                              \"instanceof\", \"new\",       \"null\",     \"return\",   \"switch\",\n                              \"this\",       \"throw\",     \"true\",     \"try\",      \"typeof\",\n                              \"var\",        \"void\",      \"while\",    \"with\",     \"let\",\n                              \"const\",      \"undefined\", NULL};\n    int i;\n    if(!mjs_is_alpha(s[0])) return 0;\n    for(i = 0; reserved[i] != NULL; i++) {\n        if(len == (int)strlen(reserved[i]) && strncmp(s, reserved[i], len) == 0) return i + 1;\n    }\n    return 0;\n}\n\nstatic int getident(struct pstate* p) {\n    while(mjs_is_ident(p->pos[0]) || mjs_is_digit(p->pos[0]))\n        p->pos++;\n    p->tok.len = p->pos - p->tok.ptr;\n    p->pos--;\n    return TOK_IDENT;\n}\n\nstatic int getstr(struct pstate* p) {\n    int quote = *p->pos++;\n    p->tok.ptr++;\n    while(p->pos[0] != '\\0' && p->pos[0] != quote) {\n        if(p->pos[0] == '\\\\' && p->pos[1] != '\\0' &&\n           (p->pos[1] == quote || strchr(\"bfnrtv\\\\\", p->pos[1]) != NULL)) {\n            p->pos += 2;\n        } else {\n            p->pos++;\n        }\n    }\n    p->tok.len = p->pos - p->tok.ptr;\n    return TOK_STR;\n}\n\nstatic void skip_spaces_and_comments(struct pstate* p) {\n    const char* pos;\n    do {\n        pos = p->pos;\n        while(mjs_is_space(p->pos[0])) {\n            if(p->pos[0] == '\\n') p->line_no++;\n            p->pos++;\n        }\n        if(p->pos[0] == '/' && p->pos[1] == '/') {\n            while(p->pos[0] != '\\0' && p->pos[0] != '\\n')\n                p->pos++;\n        }\n        if(p->pos[0] == '/' && p->pos[1] == '*') {\n            p->pos += 2;\n            while(p->pos[0] != '\\0') {\n                if(p->pos[0] == '\\n') p->line_no++;\n                if(p->pos[0] == '*' && p->pos[1] == '/') {\n                    p->pos += 2;\n                    break;\n                }\n                p->pos++;\n            }\n        }\n    } while(pos < p->pos);\n}\n\nstatic int ptranslate(int tok) {\n#define DT(a, b)       ((a) << 8 | (b))\n#define TT(a, b, c)    ((a) << 16 | (b) << 8 | (c))\n#define QT(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))\n    /* Map token ID produced by mjs_tok.c to token ID produced by lemon */\n    /* clang-format off */\n  switch (tok) {\n    case ':': return TOK_COLON;\n    case ';': return TOK_SEMICOLON;\n    case ',': return TOK_COMMA;\n    case '=': return TOK_ASSIGN;\n    case '{': return TOK_OPEN_CURLY;\n    case '}': return TOK_CLOSE_CURLY;\n    case '(': return TOK_OPEN_PAREN;\n    case ')': return TOK_CLOSE_PAREN;\n    case '[': return TOK_OPEN_BRACKET;\n    case ']': return TOK_CLOSE_BRACKET;\n    case '*': return TOK_MUL;\n    case '+': return TOK_PLUS;\n    case '-': return TOK_MINUS;\n    case '/': return TOK_DIV;\n    case '%': return TOK_REM;\n    case '&': return TOK_AND;\n    case '|': return TOK_OR;\n    case '^': return TOK_XOR;\n    case '.': return TOK_DOT;\n    case '?': return TOK_QUESTION;\n    case '!': return TOK_NOT;\n    case '~': return TOK_TILDA;\n    case '<': return TOK_LT;\n    case '>': return TOK_GT;\n    case DT('<','<'): return TOK_LSHIFT;\n    case DT('>','>'): return TOK_RSHIFT;\n    case DT('-','-'): return TOK_MINUS_MINUS;\n    case DT('+','+'): return TOK_PLUS_PLUS;\n    case DT('+','='): return TOK_PLUS_ASSIGN;\n    case DT('-','='): return TOK_MINUS_ASSIGN;\n    case DT('*','='): return TOK_MUL_ASSIGN;\n    case DT('/','='): return TOK_DIV_ASSIGN;\n    case DT('&','='): return TOK_AND_ASSIGN;\n    case DT('|','='): return TOK_OR_ASSIGN;\n    case DT('%','='): return TOK_REM_ASSIGN;\n    case DT('^','='): return TOK_XOR_ASSIGN;\n    case DT('=','='): return TOK_EQ;\n    case DT('!','='): return TOK_NE;\n    case DT('<','='): return TOK_LE;\n    case DT('>','='): return TOK_GE;\n    case DT('&','&'): return TOK_LOGICAL_AND;\n    case DT('|','|'): return TOK_LOGICAL_OR;\n    case TT('=','=','='): return TOK_EQ_EQ;\n    case TT('!','=','='): return TOK_NE_NE;\n    case TT('<','<','='): return TOK_LSHIFT_ASSIGN;\n    case TT('>','>','='): return TOK_RSHIFT_ASSIGN;\n    case TT('>','>','>'): return TOK_URSHIFT;\n    case QT('>','>','>','='): return TOK_URSHIFT_ASSIGN;\n  }\n    /* clang-format on */\n    return tok;\n}\n\nMJS_PRIVATE int pnext(struct pstate* p) {\n    int tmp, tok = TOK_INVALID;\n\n    skip_spaces_and_comments(p);\n    p->tok.ptr = p->pos;\n    p->tok.len = 1;\n\n    if(p->pos[0] == '\\0') {\n        tok = TOK_EOF;\n    } else if(mjs_is_digit(p->pos[0])) {\n        tok = getnum(p);\n    } else if(p->pos[0] == '\\'' || p->pos[0] == '\"') {\n        tok = getstr(p);\n    } else if(mjs_is_ident(p->pos[0])) {\n        tok = getident(p);\n        /*\n     * NOTE: getident() has side effects on `p`, and `is_reserved_word_token()`\n     * relies on them. Since in C the order of evaluation of the operands is\n     * undefined, `is_reserved_word_token()` should be called in a separate\n     * statement.\n     */\n        tok += is_reserved_word_token(p->tok.ptr, p->tok.len);\n    } else if(strchr(\",.:;{}[]()?\", p->pos[0]) != NULL) {\n        tok = p->pos[0];\n    } else if(\n        (tmp = longtok3(p, '<', '<', '=')) != TOK_EOF ||\n        (tmp = longtok3(p, '>', '>', '=')) != TOK_EOF ||\n        (tmp = longtok4(p, '>', '>', '>', '=')) != TOK_EOF ||\n        (tmp = longtok3(p, '>', '>', '>')) != TOK_EOF ||\n        (tmp = longtok3(p, '=', '=', '=')) != TOK_EOF ||\n        (tmp = longtok3(p, '!', '=', '=')) != TOK_EOF ||\n        (tmp = longtok(p, \"&\", \"&=\")) != TOK_EOF || (tmp = longtok(p, \"|\", \"|=\")) != TOK_EOF ||\n        (tmp = longtok(p, \"<\", \"<=\")) != TOK_EOF || (tmp = longtok(p, \">\", \">=\")) != TOK_EOF ||\n        (tmp = longtok(p, \"-\", \"-=\")) != TOK_EOF || (tmp = longtok(p, \"+\", \"+=\")) != TOK_EOF) {\n        tok = tmp;\n    } else if((tmp = longtok(p, \"^~+-%/*<>=!|&\", \"=\")) != TOK_EOF) {\n        tok = tmp;\n    }\n    if(p->pos[0] != '\\0') p->pos++;\n    LOG(LL_VERBOSE_DEBUG, (\"  --> %d [%.*s]\", tok, p->tok.len, p->tok.ptr));\n    p->prev_tok = p->tok.tok;\n    p->tok.tok = ptranslate(tok);\n    return p->tok.tok;\n}\n"
  },
  {
    "path": "lib/mjs/mjs_tok.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_TOK_H_\n#define MJS_TOK_H_\n\n#include \"mjs_internal.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct tok {\n    int tok;\n    int len;\n    const char* ptr;\n};\n\nstruct pstate {\n    const char* file_name; /* Source code file name */\n    const char* buf; /* Nul-terminated source code buffer */\n    const char* pos; /* Current position */\n    int line_no; /* Line number */\n    int last_emitted_line_no;\n    struct mbuf offset_lineno_map;\n    int prev_tok; /* Previous token, for prefix increment / decrement */\n    struct tok tok; /* Parsed token */\n    struct mjs* mjs;\n    int start_bcode_idx; /* Index in mjs->bcode at which parsing was started */\n    int cur_idx; /* Index in mjs->bcode at which newly generated code is inserted\n                  */\n    int depth;\n};\n\nenum {\n    TOK_EOF,\n    TOK_INVALID,\n\n    TOK_COLON,\n    TOK_SEMICOLON,\n    TOK_COMMA,\n    TOK_ASSIGN,\n    TOK_OPEN_CURLY,\n    TOK_CLOSE_CURLY,\n    TOK_OPEN_PAREN,\n    TOK_CLOSE_PAREN,\n    TOK_OPEN_BRACKET,\n    TOK_CLOSE_BRACKET,\n    TOK_MUL,\n    TOK_PLUS,\n    TOK_MINUS,\n    TOK_DIV,\n    TOK_REM,\n    TOK_AND,\n    TOK_OR,\n    TOK_XOR,\n    TOK_DOT,\n    TOK_QUESTION,\n    TOK_NOT,\n    TOK_TILDA,\n    TOK_LT,\n    TOK_GT,\n    TOK_LSHIFT,\n    TOK_RSHIFT,\n    TOK_MINUS_MINUS,\n    TOK_PLUS_PLUS,\n    TOK_PLUS_ASSIGN,\n    TOK_MINUS_ASSIGN,\n    TOK_MUL_ASSIGN,\n    TOK_DIV_ASSIGN,\n    TOK_AND_ASSIGN,\n    TOK_OR_ASSIGN,\n    TOK_REM_ASSIGN,\n    TOK_XOR_ASSIGN,\n    TOK_EQ,\n    TOK_NE,\n    TOK_LE,\n    TOK_GE,\n    TOK_LOGICAL_AND,\n    TOK_LOGICAL_OR,\n    TOK_EQ_EQ,\n    TOK_NE_NE,\n    TOK_LSHIFT_ASSIGN,\n    TOK_RSHIFT_ASSIGN,\n    TOK_URSHIFT,\n    TOK_URSHIFT_ASSIGN,\n\n    TOK_UNARY_PLUS,\n    TOK_UNARY_MINUS,\n    TOK_POSTFIX_PLUS,\n    TOK_POSTFIX_MINUS,\n\n    TOK_NUM = 200, /* Make sure they don't clash with ascii '+', '{', etc */\n    TOK_STR,\n    TOK_IDENT,\n    TOK_KEYWORD_BREAK,\n    TOK_KEYWORD_CASE,\n    TOK_KEYWORD_CATCH,\n    TOK_KEYWORD_CONTINUE,\n    TOK_KEYWORD_DEBUGGER,\n    TOK_KEYWORD_DEFAULT,\n    TOK_KEYWORD_DELETE,\n    TOK_KEYWORD_DO,\n    TOK_KEYWORD_ELSE,\n    TOK_KEYWORD_FALSE,\n    TOK_KEYWORD_FINALLY,\n    TOK_KEYWORD_FOR,\n    TOK_KEYWORD_FUNCTION,\n    TOK_KEYWORD_IF,\n    TOK_KEYWORD_IN,\n    TOK_KEYWORD_INSTANCEOF,\n    TOK_KEYWORD_NEW,\n    TOK_KEYWORD_NULL,\n    TOK_KEYWORD_RETURN,\n    TOK_KEYWORD_SWITCH,\n    TOK_KEYWORD_THIS,\n    TOK_KEYWORD_THROW,\n    TOK_KEYWORD_TRUE,\n    TOK_KEYWORD_TRY,\n    TOK_KEYWORD_TYPEOF,\n    TOK_KEYWORD_VAR,\n    TOK_KEYWORD_VOID,\n    TOK_KEYWORD_WHILE,\n    TOK_KEYWORD_WITH,\n    TOK_KEYWORD_LET,\n    TOK_KEYWORD_CONST,\n    TOK_KEYWORD_UNDEFINED,\n    TOK_MAX\n};\n\nMJS_PRIVATE void pinit(const char* file_name, const char* buf, struct pstate*);\nMJS_PRIVATE int pnext(struct pstate*);\nMJS_PRIVATE int mjs_is_ident(int c);\nMJS_PRIVATE int mjs_is_digit(int c);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_TOK_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_util.c",
    "content": "/*\n * Copyright (c) 2017 Cesanta Software Limited\n * All rights reserved\n */\n\n#include \"common/cs_varint.h\"\n#include \"common/frozen/frozen.h\"\n#include \"mjs_array.h\"\n#include \"mjs_bcode.h\"\n#include \"mjs_core.h\"\n#include \"mjs_internal.h\"\n#include \"mjs_object.h\"\n#include \"mjs_primitive.h\"\n#include \"mjs_string.h\"\n#include \"mjs_util.h\"\n#include \"mjs_tok.h\"\n#include \"mjs_array_buf.h\"\n#include <furi.h>\n\nconst char* mjs_typeof(mjs_val_t v) {\n    return mjs_stringify_type(mjs_get_type(v));\n}\n\nMJS_PRIVATE const char* mjs_stringify_type(enum mjs_type t) {\n    switch(t) {\n    case MJS_TYPE_NUMBER:\n        return \"number\";\n    case MJS_TYPE_BOOLEAN:\n        return \"boolean\";\n    case MJS_TYPE_STRING:\n        return \"string\";\n    case MJS_TYPE_OBJECT_ARRAY:\n        return \"array\";\n    case MJS_TYPE_OBJECT_GENERIC:\n        return \"object\";\n    case MJS_TYPE_FOREIGN:\n        return \"foreign_ptr\";\n    case MJS_TYPE_OBJECT_FUNCTION:\n        return \"function\";\n    case MJS_TYPE_NULL:\n        return \"null\";\n    case MJS_TYPE_UNDEFINED:\n        return \"undefined\";\n    case MJS_TYPE_ARRAY_BUF:\n        return \"array_buf\";\n    case MJS_TYPE_ARRAY_BUF_VIEW:\n        return \"data_view\";\n    default:\n        return \"???\";\n    }\n}\n\nvoid mjs_jprintf(mjs_val_t v, struct mjs* mjs, struct json_out* out) {\n    if(mjs_is_number(v)) {\n        double iv, d = mjs_get_double(mjs, v);\n        if(modf(d, &iv) == 0) {\n            json_printf(out, \"%\" INT64_FMT, (int64_t)d);\n        } else {\n            json_printf(out, \"%f\", mjs_get_double(mjs, v));\n        }\n    } else if(mjs_is_boolean(v)) {\n        json_printf(out, \"%s\", mjs_get_bool(mjs, v) ? \"true\" : \"false\");\n    } else if(mjs_is_string(v)) {\n        size_t i, size;\n        const char* s = mjs_get_string(mjs, &v, &size);\n        for(i = 0; i < size; i++) {\n            int ch = ((unsigned char*)s)[i];\n            if(isprint(ch)) {\n                json_printf(out, \"%c\", ch);\n            } else {\n                json_printf(out, \"%s%02x\", \"\\\\x\", ch);\n            }\n        }\n    } else if(mjs_is_array(v)) {\n        json_printf(out, \"%s\", \"<array>\");\n    } else if(mjs_is_object(v)) {\n        json_printf(out, \"%s\", \"<object>\");\n    } else if(mjs_is_foreign(v)) {\n        json_printf(\n            out, \"%s%lx%s\", \"<foreign_ptr@\", (unsigned long)(uintptr_t)mjs_get_ptr(mjs, v), \">\");\n    } else if(mjs_is_function(v)) {\n        json_printf(out, \"%s%d%s\", \"<function@\", (int)mjs_get_func_addr(v), \">\");\n    } else if(mjs_is_null(v)) {\n        json_printf(out, \"%s\", \"null\");\n    } else if(mjs_is_undefined(v)) {\n        json_printf(out, \"%s\", \"undefined\");\n    } else {\n        json_printf(out, \"%s%\" INT64_FMT \"%s\", \"<???\", (int64_t)v, \">\");\n    }\n}\n\nvoid mjs_sprintf(mjs_val_t v, struct mjs* mjs, char* buf, size_t n) {\n    struct json_out out = JSON_OUT_BUF(buf, n);\n    mjs_jprintf(v, mjs, &out);\n}\n\nvoid mjs_fprintf(mjs_val_t v, struct mjs* mjs, FILE* fp) {\n    struct json_out out = JSON_OUT_FILE(fp);\n    mjs_jprintf(v, mjs, &out);\n}\n\nMJS_PRIVATE const char* opcodetostr(uint8_t opcode) {\n    static const char* names[] = {\n        \"NOP\",\n        \"DROP\",\n        \"DUP\",\n        \"SWAP\",\n        \"JMP\",\n        \"JMP_TRUE\",\n        \"JMP_NEUTRAL_TRUE\",\n        \"JMP_FALSE\",\n        \"JMP_NEUTRAL_FALSE\",\n        \"FIND_SCOPE\",\n        \"PUSH_SCOPE\",\n        \"PUSH_STR\",\n        \"PUSH_TRUE\",\n        \"PUSH_FALSE\",\n        \"PUSH_INT\",\n        \"PUSH_DBL\",\n        \"PUSH_NULL\",\n        \"PUSH_UNDEF\",\n        \"PUSH_OBJ\",\n        \"PUSH_ARRAY\",\n        \"PUSH_FUNC\",\n        \"PUSH_THIS\",\n        \"GET\",\n        \"CREATE\",\n        \"EXPR\",\n        \"APPEND\",\n        \"SET_ARG\",\n        \"NEW_SCOPE\",\n        \"DEL_SCOPE\",\n        \"CALL\",\n        \"RETURN\",\n        \"LOOP\",\n        \"BREAK\",\n        \"CONTINUE\",\n        \"SETRETVAL\",\n        \"EXIT\",\n        \"BCODE_HDR\",\n        \"ARGS\",\n        \"FOR_IN_NEXT\",\n    };\n    const char* name = \"???\";\n    assert(ARRAY_SIZE(names) == OP_MAX);\n    if(opcode < ARRAY_SIZE(names)) name = names[opcode];\n    return name;\n}\n\nMJS_PRIVATE size_t\n    mjs_disasm_single(const uint8_t* code, size_t i, MjsPrintCallback print_cb, void* print_ctx) {\n    char buf[40];\n    size_t start_i = i;\n    size_t llen;\n    uint64_t n;\n\n    furi_assert(print_cb);\n\n    snprintf(buf, sizeof(buf), \"%-3u\\t%-8s\", (unsigned)i, opcodetostr(code[i]));\n\n    switch(code[i]) {\n    case OP_PUSH_FUNC: {\n        cs_varint_decode(&code[i + 1], ~0, &n, &llen);\n        print_cb(print_ctx, \"%s %04u\", buf, (unsigned)(i - n));\n        i += llen;\n        break;\n    }\n    case OP_PUSH_INT: {\n        cs_varint_decode(&code[i + 1], ~0, &n, &llen);\n        print_cb(print_ctx, \"%s\\t%lu\", buf, (unsigned long)n);\n        i += llen;\n        break;\n    }\n    case OP_SET_ARG: {\n        size_t llen2;\n        uint64_t arg_no;\n        cs_varint_decode(&code[i + 1], ~0, &arg_no, &llen);\n        cs_varint_decode(&code[i + llen + 1], ~0, &n, &llen2);\n        print_cb(\n            print_ctx, \"%s\\t[%.*s] %u\", buf, (int)n, code + i + 1 + llen + llen2, (unsigned)arg_no);\n        i += llen + llen2 + n;\n        break;\n    }\n    case OP_PUSH_STR:\n    case OP_PUSH_DBL: {\n        cs_varint_decode(&code[i + 1], ~0, &n, &llen);\n        print_cb(print_ctx, \"%s\\t[%.*s]\", buf, (int)n, code + i + 1 + llen);\n        i += llen + n;\n        break;\n    }\n    case OP_JMP:\n    case OP_JMP_TRUE:\n    case OP_JMP_NEUTRAL_TRUE:\n    case OP_JMP_FALSE:\n    case OP_JMP_NEUTRAL_FALSE: {\n        cs_varint_decode(&code[i + 1], ~0, &n, &llen);\n        print_cb(\n            print_ctx,\n            \"%s\\t%u\",\n            buf,\n            (unsigned)(i + n + llen + 1 /* becaue i will be incremented on the usual terms */));\n        i += llen;\n        break;\n    }\n    case OP_LOOP: {\n        size_t l1, l2;\n        uint64_t n1, n2;\n        cs_varint_decode(&code[i + 1], ~0, &n1, &l1);\n        cs_varint_decode(&code[i + l1 + 1], ~0, &n2, &l2);\n        print_cb(\n            print_ctx,\n            \"%s\\tB:%lu C:%lu (%d)\",\n            buf,\n            (unsigned long)(i + 1 /* OP_LOOP */ + l1 + n1),\n            (unsigned long)(i + 1 /* OP_LOOP */ + l1 + l2 + n2),\n            (int)i);\n        i += l1 + l2;\n        break;\n    }\n    case OP_EXPR: {\n        int op = code[i + 1];\n        const char* name = \"???\";\n        /* clang-format off */\n      switch (op) {\n        case TOK_DOT:       name = \".\"; break;\n        case TOK_MINUS:     name = \"-\"; break;\n        case TOK_PLUS:      name = \"+\"; break;\n        case TOK_MUL:       name = \"*\"; break;\n        case TOK_DIV:       name = \"/\"; break;\n        case TOK_REM:       name = \"%\"; break;\n        case TOK_XOR:       name = \"^\"; break;\n        case TOK_AND:       name = \"&\"; break;\n        case TOK_OR:        name = \"|\"; break;\n        case TOK_LSHIFT:    name = \"<<\"; break;\n        case TOK_RSHIFT:    name = \">>\"; break;\n        case TOK_URSHIFT:   name = \">>>\"; break;\n        case TOK_UNARY_MINUS:   name = \"- (unary)\"; break;\n        case TOK_UNARY_PLUS:    name = \"+ (unary)\"; break;\n        case TOK_NOT:       name = \"!\"; break;\n        case TOK_TILDA:     name = \"~\"; break;\n        case TOK_EQ:        name = \"==\"; break;\n        case TOK_NE:        name = \"!=\"; break;\n        case TOK_EQ_EQ:     name = \"===\"; break;\n        case TOK_NE_NE:     name = \"!==\"; break;\n        case TOK_LT:        name = \"<\"; break;\n        case TOK_GT:        name = \">\"; break;\n        case TOK_LE:        name = \"<=\"; break;\n        case TOK_GE:        name = \">=\"; break;\n        case TOK_ASSIGN:    name = \"=\"; break;\n        case TOK_POSTFIX_PLUS:  name = \"++ (postfix)\"; break;\n        case TOK_POSTFIX_MINUS: name = \"-- (postfix)\"; break;\n        case TOK_MINUS_MINUS:   name = \"--\"; break;\n        case TOK_PLUS_PLUS:     name = \"++\"; break;\n        case TOK_LOGICAL_AND:   name = \"&&\"; break;\n        case TOK_LOGICAL_OR:    name = \"||\"; break;\n        case TOK_KEYWORD_TYPEOF:  name = \"typeof\"; break;\n        case TOK_PLUS_ASSIGN:     name = \"+=\"; break;\n        case TOK_MINUS_ASSIGN:    name = \"-=\"; break;\n        case TOK_MUL_ASSIGN:      name = \"*=\"; break;\n        case TOK_DIV_ASSIGN:      name = \"/=\"; break;\n        case TOK_REM_ASSIGN:      name = \"%=\"; break;\n        case TOK_XOR_ASSIGN:      name = \"^=\"; break;\n        case TOK_AND_ASSIGN:      name = \"&=\"; break;\n        case TOK_OR_ASSIGN:       name = \"|=\"; break;\n        case TOK_LSHIFT_ASSIGN:   name = \"<<=\"; break;\n        case TOK_RSHIFT_ASSIGN:   name = \">>=\"; break;\n        case TOK_URSHIFT_ASSIGN:  name = \">>>=\"; break;\n      }\n        /* clang-format on */\n        print_cb(print_ctx, \"%s\\t%s\", buf, name);\n        i++;\n        break;\n    }\n    case OP_BCODE_HEADER: {\n        size_t start = 0;\n        mjs_header_item_t map_offset = 0, total_size = 0;\n        start = i;\n        memcpy(&total_size, &code[i + 1], sizeof(total_size));\n        memcpy(\n            &map_offset,\n            &code[i + 1 + MJS_HDR_ITEM_MAP_OFFSET * sizeof(total_size)],\n            sizeof(map_offset));\n        i += sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT;\n        print_cb(\n            print_ctx,\n            \"%s\\t[%s] end:%lu map_offset: %lu\",\n            buf,\n            &code[i + 1],\n            (unsigned long)start + total_size,\n            (unsigned long)start + map_offset);\n        i += strlen((char*)(code + i + 1)) + 1;\n        break;\n    }\n    default:\n        print_cb(print_ctx, \"%s\", buf);\n        break;\n    }\n    return i - start_i;\n}\n\nvoid mjs_disasm(const uint8_t* code, size_t len, MjsPrintCallback print_cb, void* print_ctx) {\n    size_t i, start = 0;\n    mjs_header_item_t map_offset = 0, total_size = 0;\n\n    for(i = 0; i < len; i++) {\n        size_t delta = mjs_disasm_single(code, i, print_cb, print_ctx);\n        if(code[i] == OP_BCODE_HEADER) {\n            start = i;\n            memcpy(&total_size, &code[i + 1], sizeof(total_size));\n            memcpy(\n                &map_offset,\n                &code[i + 1 + MJS_HDR_ITEM_MAP_OFFSET * sizeof(total_size)],\n                sizeof(map_offset));\n        }\n\n        i += delta;\n\n        if(map_offset > 0 && i == start + map_offset) {\n            i = start + total_size - 1;\n            continue;\n        }\n    }\n}\n\nvoid mjs_disasm_all(struct mjs* mjs, MjsPrintCallback print_cb, void* print_ctx) {\n    int parts_cnt = mjs_bcode_parts_cnt(mjs);\n    for(int i = 0; i < parts_cnt; i++) {\n        struct mjs_bcode_part* bp = mjs_bcode_part_get(mjs, i);\n        mjs_disasm((uint8_t*)bp->data.p, bp->data.len, print_cb, print_ctx);\n    }\n}\n\nstatic void mjs_dump_obj_stack(\n    struct mjs* mjs,\n    const char* name,\n    const struct mbuf* m,\n    MjsPrintCallback print_cb,\n    void* print_ctx) {\n    char buf[50];\n    size_t i, n;\n    n = mjs_stack_size(m);\n    print_cb(print_ctx, \"%12s (%d elems): \", name, (int)n);\n    for(i = 0; i < n; i++) {\n        mjs_sprintf(((mjs_val_t*)m->buf)[i], mjs, buf, sizeof(buf));\n        print_cb(print_ctx, \"%34s\", buf);\n    }\n}\n\nvoid mjs_dump(struct mjs* mjs, int do_disasm, MjsPrintCallback print_cb, void* print_ctx) {\n    print_cb(print_ctx, \"------- MJS VM DUMP BEGIN\");\n    mjs_dump_obj_stack(mjs, \"DATA_STACK\", &mjs->stack, print_cb, print_ctx);\n    mjs_dump_obj_stack(mjs, \"CALL_STACK\", &mjs->call_stack, print_cb, print_ctx);\n    mjs_dump_obj_stack(mjs, \"SCOPES\", &mjs->scopes, print_cb, print_ctx);\n    mjs_dump_obj_stack(mjs, \"LOOP_OFFSETS\", &mjs->loop_addresses, print_cb, print_ctx);\n    mjs_dump_obj_stack(mjs, \"ARG_STACK\", &mjs->arg_stack, print_cb, print_ctx);\n    if(do_disasm) {\n        int parts_cnt = mjs_bcode_parts_cnt(mjs);\n        int i;\n        print_cb(print_ctx, \"%23s\", \"CODE:\");\n        for(i = 0; i < parts_cnt; i++) {\n            struct mjs_bcode_part* bp = mjs_bcode_part_get(mjs, i);\n            mjs_disasm((uint8_t*)bp->data.p, bp->data.len, print_cb, print_ctx);\n        }\n    }\n    print_cb(print_ctx, \"------- MJS VM DUMP END\");\n}\n\nMJS_PRIVATE int mjs_check_arg(\n    struct mjs* mjs,\n    int arg_num,\n    const char* arg_name,\n    enum mjs_type expected_type,\n    mjs_val_t* parg) {\n    mjs_val_t arg = MJS_UNDEFINED;\n    enum mjs_type actual_type;\n\n    if(arg_num >= 0) {\n        int nargs = mjs_nargs(mjs);\n        if(nargs < arg_num + 1) {\n            mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, \"missing argument %s\", arg_name);\n            return 0;\n        }\n\n        arg = mjs_arg(mjs, arg_num);\n    } else {\n        /* use `this` */\n        arg = mjs->vals.this_obj;\n    }\n\n    actual_type = mjs_get_type(arg);\n    if(actual_type != expected_type) {\n        mjs_prepend_errorf(\n            mjs,\n            MJS_TYPE_ERROR,\n            \"%s should be a %s, %s given\",\n            arg_name,\n            mjs_stringify_type(expected_type),\n            mjs_stringify_type(actual_type));\n        return 0;\n    }\n\n    if(parg != NULL) {\n        *parg = arg;\n    }\n\n    return 1;\n}\n\nMJS_PRIVATE int mjs_normalize_idx(int idx, int size) {\n    if(idx < 0) {\n        idx = size + idx;\n        if(idx < 0) {\n            idx = 0;\n        }\n    }\n    if(idx > size) {\n        idx = size;\n    }\n    return idx;\n}\n\nMJS_PRIVATE const char* mjs_get_bcode_filename(struct mjs* mjs, struct mjs_bcode_part* bp) {\n    (void)mjs;\n    return bp->data.p + 1 /* OP_BCODE_HEADER */ + sizeof(mjs_header_item_t) * MJS_HDR_ITEMS_CNT;\n}\n\nconst char* mjs_get_bcode_filename_by_offset(struct mjs* mjs, int offset) {\n    const char* ret = NULL;\n    struct mjs_bcode_part* bp = mjs_bcode_part_get_by_offset(mjs, offset);\n    if(bp != NULL) {\n        ret = mjs_get_bcode_filename(mjs, bp);\n    }\n    return ret;\n}\n\nint mjs_get_lineno_by_offset(struct mjs* mjs, int offset) {\n    size_t llen;\n    uint64_t map_len;\n    int prev_line_no, ret = 1;\n    struct mjs_bcode_part* bp = mjs_bcode_part_get_by_offset(mjs, offset);\n    uint8_t *p, *pe;\n    if(bp != NULL) {\n        mjs_header_item_t map_offset, bcode_offset;\n        memcpy(\n            &map_offset,\n            bp->data.p + 1 /* OP_BCODE_HEADER */ +\n                sizeof(mjs_header_item_t) * MJS_HDR_ITEM_MAP_OFFSET,\n            sizeof(map_offset));\n\n        memcpy(\n            &bcode_offset,\n            bp->data.p + 1 /* OP_BCODE_HEADER */ +\n                sizeof(mjs_header_item_t) * MJS_HDR_ITEM_BCODE_OFFSET,\n            sizeof(bcode_offset));\n\n        offset -= (1 /* OP_BCODE_HEADER */ + bcode_offset) + bp->start_idx;\n\n        /* get pointer to the length of the map followed by the map itself */\n        p = (uint8_t*)bp->data.p + 1 /* OP_BCODE_HEADER */ + map_offset;\n\n        cs_varint_decode(p, ~0, &map_len, &llen);\n        p += llen;\n        pe = p + map_len;\n\n        prev_line_no = 1;\n        while(p < pe) {\n            uint64_t cur_offset, line_no;\n            cs_varint_decode(p, ~0, &cur_offset, &llen);\n            p += llen;\n            cs_varint_decode(p, ~0, &line_no, &llen);\n            p += llen;\n\n            if(cur_offset >= (uint64_t)offset) {\n                ret = prev_line_no;\n                break;\n            }\n            prev_line_no = line_no;\n        }\n    }\n    return ret;\n}\n\nint mjs_get_offset_by_call_frame_num(struct mjs* mjs, int cf_num) {\n    int ret = -1;\n    if(cf_num == 0) {\n        /* Return current bcode offset */\n        ret = mjs->cur_bcode_offset;\n    } else if(\n        cf_num > 0 &&\n        mjs->call_stack.len >= sizeof(mjs_val_t) * CALL_STACK_FRAME_ITEMS_CNT * cf_num) {\n        /* Get offset from the call_stack */\n        int pos = CALL_STACK_FRAME_ITEM_RETURN_ADDR + CALL_STACK_FRAME_ITEMS_CNT * (cf_num - 1);\n        mjs_val_t val = *vptr(&mjs->call_stack, -1 - pos);\n        ret = mjs_get_int(mjs, val);\n    }\n    return ret;\n}\n\nmjs_err_t mjs_to_string(struct mjs* mjs, mjs_val_t* v, char** p, size_t* sizep, int* need_free) {\n    mjs_err_t ret = MJS_OK;\n\n    *p = NULL;\n    *sizep = 0;\n    *need_free = 0;\n\n    if(mjs_is_string(*v)) {\n        *p = (char*)mjs_get_string(mjs, v, sizep);\n    } else if(mjs_is_number(*v)) {\n        char buf[50] = \"\";\n        struct json_out out = JSON_OUT_BUF(buf, sizeof(buf));\n        mjs_jprintf(*v, mjs, &out);\n        *sizep = strlen(buf);\n        *p = malloc(*sizep + 1);\n        if(*p == NULL) {\n            ret = MJS_OUT_OF_MEMORY;\n            goto clean;\n        }\n        memmove(*p, buf, *sizep + 1);\n        *need_free = 1;\n    } else if(mjs_is_boolean(*v)) {\n        if(mjs_get_bool(mjs, *v)) {\n            *p = \"true\";\n            *sizep = 4;\n        } else {\n            *p = \"false\";\n            *sizep = 5;\n        }\n    } else if(mjs_is_undefined(*v)) {\n        *p = \"undefined\";\n        *sizep = 9;\n    } else if(mjs_is_null(*v)) {\n        *p = \"null\";\n        *sizep = 4;\n    } else if(mjs_is_object(*v)) {\n        ret = MJS_TYPE_ERROR;\n        mjs_set_errorf(mjs, ret, \"conversion from object to string is not supported\");\n    } else if(mjs_is_foreign(*v)) {\n        *p = \"TODO_foreign\";\n        *sizep = 12;\n    } else if(mjs_is_typed_array(*v)) {\n        *p = \"TODO_typed_array\";\n        *sizep = 16;\n    } else {\n        ret = MJS_TYPE_ERROR;\n        mjs_set_errorf(mjs, ret, \"unknown type to convert to string\");\n    }\n\nclean:\n    return ret;\n}\n\nmjs_val_t mjs_to_boolean_v(struct mjs* mjs, mjs_val_t v) {\n    size_t len;\n    int is_truthy;\n\n    is_truthy = ((mjs_is_boolean(v) && mjs_get_bool(mjs, v)) ||\n                 (mjs_is_number(v) && mjs_get_double(mjs, v) != (double)0.0) ||\n                 (mjs_is_string(v) && mjs_get_string(mjs, &v, &len) && len > 0) ||\n                 (mjs_is_function(v)) || (mjs_is_foreign(v)) || (mjs_is_object(v))) &&\n                v != MJS_TAG_NAN;\n\n    return mjs_mk_boolean(mjs, is_truthy);\n}\n\nint mjs_is_truthy(struct mjs* mjs, mjs_val_t v) {\n    return mjs_get_bool(mjs, mjs_to_boolean_v(mjs, v));\n}\n"
  },
  {
    "path": "lib/mjs/mjs_util.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_UTIL_H_\n#define MJS_UTIL_H_\n\n#include \"common/frozen/frozen.h\"\n#include \"mjs_core.h\"\n#include \"mjs_util_public.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct mjs_bcode_part;\n\n#if MJS_ENABLE_DEBUG\nMJS_PRIVATE const char* opcodetostr(uint8_t opcode);\nMJS_PRIVATE size_t mjs_disasm_single(const uint8_t* code, size_t i);\n#endif\n\nMJS_PRIVATE const char* mjs_stringify_type(enum mjs_type t);\n\n/*\n * Checks that the given argument is provided, and checks its type. If check\n * fails, sets error in the mjs context, and returns 0; otherwise returns 1.\n *\n * If `arg_num` >= 0, checks argument; otherwise (`arg_num` is negative) checks\n * `this`. `arg_name` is used for the error message only. If `parg` is not\n * NULL, writes resulting value at this location in case of success.\n */\nMJS_PRIVATE int mjs_check_arg(\n    struct mjs* mjs,\n    int arg_num,\n    const char* arg_name,\n    enum mjs_type expected_type,\n    mjs_val_t* parg);\n\n/*\n * mjs_normalize_idx takes and index in the string and the string size, and\n * returns the index which is >= 0 and <= size. Negative index is interpreted\n * as size + index.\n */\nMJS_PRIVATE int mjs_normalize_idx(int idx, int size);\n\nMJS_PRIVATE const char* mjs_get_bcode_filename(struct mjs* mjs, struct mjs_bcode_part* bp);\n\n/* Print JS value `v` to the JSON stream `out`. */\nvoid mjs_jprintf(mjs_val_t v, struct mjs* mjs, struct json_out* out);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_UTIL_H_ */\n"
  },
  {
    "path": "lib/mjs/mjs_util_public.h",
    "content": "/*\n * Copyright (c) 2016 Cesanta Software Limited\n * All rights reserved\n */\n\n#ifndef MJS_UTIL_PUBLIC_H_\n#define MJS_UTIL_PUBLIC_H_\n\n#include \"mjs_core_public.h\"\n#include <stdio.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\ntypedef void (*MjsPrintCallback)(void* ctx, const char* format, ...);\n\nconst char* mjs_typeof(mjs_val_t v);\n\nvoid mjs_fprintf(mjs_val_t v, struct mjs* mjs, FILE* fp);\nvoid mjs_sprintf(mjs_val_t v, struct mjs* mjs, char* buf, size_t buflen);\n\nvoid mjs_disasm_all(struct mjs* mjs, MjsPrintCallback print_cb, void* print_ctx);\nvoid mjs_dump(struct mjs* mjs, int do_disasm, MjsPrintCallback print_cb, void* print_ctx);\n\n/*\n * Returns the filename corresponding to the given bcode offset.\n */\nconst char* mjs_get_bcode_filename_by_offset(struct mjs* mjs, int offset);\n\n/*\n * Returns the line number corresponding to the given bcode offset.\n */\nint mjs_get_lineno_by_offset(struct mjs* mjs, int offset);\n\n/*\n * Returns bcode offset of the corresponding call frame cf_num, where 0 means\n * the currently executing function, 1 means the first return address, etc.\n *\n * If given cf_num is too large, -1 is returned.\n */\nint mjs_get_offset_by_call_frame_num(struct mjs* mjs, int cf_num);\n\n/*\n * Tries to convert `mjs_val_t` to a string, returns MJS_OK if successful.\n * String is returned as a pair of pointers: `char **p, size_t *sizep`.\n *\n * Caller must also provide a non-null `need_free`, and if it is non-zero,\n * then the string `*p` should be freed by the caller.\n *\n * MJS does not support `toString()` and `valueOf()`, so, passing an object\n * always results in `MJS_TYPE_ERROR`.\n */\nmjs_err_t mjs_to_string(struct mjs* mjs, mjs_val_t* v, char** p, size_t* sizep, int* need_free);\n\n/*\n * Converts value to boolean as in the expression `if (v)`.\n */\nmjs_val_t mjs_to_boolean_v(struct mjs* mjs, mjs_val_t v);\n\nint mjs_is_truthy(struct mjs* mjs, mjs_val_t v);\n\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n#endif /* MJS_UTIL_PUBLIC_H_ */\n"
  },
  {
    "path": "lib/mlib.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/mlib\",\n    ],\n    SDK_HEADERS=[\n        *(\n            File(f\"#/lib/mlib/m-{name}.h\")\n            for name in (\n                \"algo\",\n                \"array\",\n                \"bptree\",\n                \"core\",\n                \"deque\",\n                \"dict\",\n                \"list\",\n                \"rbtree\",\n                \"tuple\",\n                \"variant\",\n            )\n        ),\n    ],\n    CPPDEFINES=[\n        '\"M_MEMORY_FULL(x)=abort()\"',\n    ],\n)\n"
  },
  {
    "path": "lib/music_worker/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/music_worker\",\n    ],\n    SDK_HEADERS=[\n        File(\"music_worker.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"music_worker\")\nlibenv.ApplyLibFlags()\n\nlibenv.AppendUnique(\n    CCFLAGS=[\n        # Required for lib to be linkable with .faps\n        \"-mword-relocations\",\n        \"-mlong-calls\",\n    ],\n)\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/music_worker/music_worker.c",
    "content": "#include \"music_worker.h\"\n\n#include <furi_hal.h>\n#include <furi.h>\n\n#include <storage/storage.h>\n#include <lib/flipper_format/flipper_format.h>\n\n#include <math.h>\n#include <m-array.h>\n\n#define TAG \"MusicWorker\"\n\n#define MUSIC_PLAYER_FILETYPE \"Flipper Music Format\"\n#define MUSIC_PLAYER_VERSION  0\n\n#define SEMITONE_PAUSE 0xFF\n\n#define NOTE_C4             261.63f\n#define NOTE_C4_SEMITONE    (4.0f * 12.0f)\n#define TWO_POW_TWELTH_ROOT 1.059463094359f\n\ntypedef struct {\n    uint8_t semitone;\n    uint8_t duration;\n    uint8_t dots;\n} NoteBlock;\n\nARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); //-V658\n\nstruct MusicWorker {\n    FuriThread* thread;\n    bool should_work;\n\n    MusicWorkerCallback callback;\n    void* callback_context;\n\n    float volume;\n    uint32_t bpm;\n    uint32_t duration;\n    uint32_t octave;\n    NoteBlockArray_t notes;\n};\n\nstatic int32_t music_worker_thread_callback(void* context) {\n    furi_assert(context);\n    MusicWorker* instance = context;\n\n    NoteBlockArray_it_t it;\n    NoteBlockArray_it(it, instance->notes);\n    if(furi_hal_speaker_acquire(1000)) {\n        while(instance->should_work) {\n            if(NoteBlockArray_end_p(it)) {\n                NoteBlockArray_it(it, instance->notes);\n                furi_delay_ms(10);\n            } else {\n                NoteBlock* note_block = NoteBlockArray_ref(it);\n\n                float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;\n                float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);\n                float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm /\n                                 note_block->duration;\n                uint32_t dots = note_block->dots;\n                while(dots > 0) {\n                    duration += duration / 2;\n                    dots--;\n                }\n                uint32_t next_tick = furi_get_tick() + duration;\n                float volume = instance->volume;\n\n                if(instance->callback) {\n                    instance->callback(\n                        note_block->semitone,\n                        note_block->dots,\n                        note_block->duration,\n                        0.0,\n                        instance->callback_context);\n                }\n\n                furi_hal_speaker_stop();\n                furi_hal_speaker_start(frequency, volume);\n                while(instance->should_work && furi_get_tick() < next_tick) {\n                    volume *= 0.9945679;\n                    furi_hal_speaker_set_volume(volume);\n                    furi_delay_ms(2);\n                }\n                NoteBlockArray_next(it);\n            }\n        }\n\n        furi_hal_speaker_stop();\n        furi_hal_speaker_release();\n    } else {\n        FURI_LOG_E(TAG, \"Speaker system is busy with another process.\");\n    }\n\n    return 0;\n}\n\nMusicWorker* music_worker_alloc(void) {\n    MusicWorker* instance = malloc(sizeof(MusicWorker));\n\n    NoteBlockArray_init(instance->notes);\n\n    instance->thread =\n        furi_thread_alloc_ex(\"MusicWorker\", 1024, music_worker_thread_callback, instance);\n\n    instance->volume = 1.0f;\n\n    return instance;\n}\n\nvoid music_worker_clear(MusicWorker* instance) {\n    NoteBlockArray_reset(instance->notes);\n}\n\nvoid music_worker_free(MusicWorker* instance) {\n    furi_assert(instance);\n    furi_thread_free(instance->thread);\n    NoteBlockArray_clear(instance->notes);\n    free(instance);\n}\n\nstatic bool is_digit(const char c) {\n    return isdigit(c) != 0;\n}\n\nstatic bool is_letter(const char c) {\n    return islower(c) != 0 || isupper(c) != 0;\n}\n\nstatic bool is_space(const char c) {\n    return c == ' ' || c == '\\t';\n}\n\nstatic size_t extract_number(const char* string, uint32_t* number) {\n    size_t ret = 0;\n    *number = 0;\n    while(is_digit(*string)) {\n        *number *= 10;\n        *number += (*string - '0');\n        string++;\n        ret++;\n    }\n    return ret;\n}\n\nstatic size_t extract_dots(const char* string, uint32_t* number) {\n    size_t ret = 0;\n    *number = 0;\n    while(*string == '.') {\n        *number += 1;\n        string++;\n        ret++;\n    }\n    return ret;\n}\n\nstatic size_t extract_char(const char* string, char* symbol) {\n    if(is_letter(*string)) {\n        *symbol = *string;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nstatic size_t extract_sharp(const char* string, char* symbol) {\n    if(*string == '#' || *string == '_') {\n        *symbol = '#';\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nstatic size_t skip_till(const char* string, const char symbol) {\n    size_t ret = 0;\n    while(*string != '\\0' && *string != symbol) {\n        string++;\n        ret++;\n    }\n    if(*string != symbol) {\n        ret = 0;\n    }\n    return ret;\n}\n\nstatic bool\n    music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) {\n    NoteBlock note_block;\n\n    note_block.semitone = semitone;\n    note_block.duration = duration;\n    note_block.dots = dots;\n\n    NoteBlockArray_push_back(instance->notes, note_block);\n\n    return true;\n}\n\nstatic int8_t note_to_semitone(const char note) {\n    switch(note) {\n    case 'C':\n        return 0;\n    // C#\n    case 'D':\n        return 2;\n    // D#\n    case 'E':\n        return 4;\n    case 'F':\n        return 5;\n    // F#\n    case 'G':\n        return 7;\n    // G#\n    case 'A':\n        return 9;\n    // A#\n    case 'B':\n        return 11;\n    default:\n        return 0;\n    }\n}\n\nstatic bool music_worker_parse_notes(MusicWorker* instance, const char* string) {\n    const char* cursor = string;\n    bool result = true;\n\n    while(*cursor != '\\0') {\n        if(!is_space(*cursor)) {\n            uint32_t duration = 0;\n            char note_char = '\\0';\n            char sharp_char = '\\0';\n            uint32_t octave = 0;\n            uint32_t dots = 0;\n\n            // Parsing\n            cursor += extract_number(cursor, &duration);\n            cursor += extract_char(cursor, &note_char);\n            cursor += extract_sharp(cursor, &sharp_char);\n            cursor += extract_number(cursor, &octave);\n            cursor += extract_dots(cursor, &dots);\n\n            // Post processing\n            note_char = toupper(note_char);\n            if(!duration) {\n                duration = instance->duration;\n            }\n            if(!octave) {\n                octave = instance->octave;\n            }\n\n            // Validation\n            bool is_valid = true;\n            is_valid &= (duration >= 1 && duration <= 128);\n            is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P');\n            is_valid &= (sharp_char == '#' || sharp_char == '\\0');\n            is_valid &= (octave <= 16);\n            is_valid &= (dots <= 16);\n            if(!is_valid) {\n                FURI_LOG_E(\n                    TAG,\n                    \"Invalid note: %lu%c%c%lu.%lu\",\n                    duration,\n                    note_char == '\\0' ? '_' : note_char,\n                    sharp_char == '\\0' ? '_' : sharp_char,\n                    octave,\n                    dots);\n                result = false;\n                break;\n            }\n\n            // Note to semitones\n            uint8_t semitone = 0;\n            if(note_char == 'P') {\n                semitone = SEMITONE_PAUSE;\n            } else {\n                semitone += octave * 12;\n                semitone += note_to_semitone(note_char);\n                semitone += sharp_char == '#' ? 1 : 0;\n            }\n\n            if(music_worker_add_note(instance, semitone, duration, dots)) {\n                FURI_LOG_D(\n                    TAG,\n                    \"Added note: %c%c%lu.%lu = %u %lu\",\n                    note_char == '\\0' ? '_' : note_char,\n                    sharp_char == '\\0' ? '_' : sharp_char,\n                    octave,\n                    dots,\n                    semitone,\n                    duration);\n            } else {\n                FURI_LOG_E(\n                    TAG,\n                    \"Invalid note: %c%c%lu.%lu = %u %lu\",\n                    note_char == '\\0' ? '_' : note_char,\n                    sharp_char == '\\0' ? '_' : sharp_char,\n                    octave,\n                    dots,\n                    semitone,\n                    duration);\n            }\n            cursor += skip_till(cursor, ',');\n        }\n\n        if(*cursor != '\\0') cursor++;\n    }\n\n    return result;\n}\n\nbool music_worker_load(MusicWorker* instance, const char* file_path) {\n    furi_assert(instance);\n    furi_assert(file_path);\n\n    bool ret = false;\n    if(strcasestr(file_path, \".fmf\")) {\n        ret = music_worker_load_fmf_from_file(instance, file_path);\n    } else {\n        ret = music_worker_load_rtttl_from_file(instance, file_path);\n    }\n    return ret;\n}\n\nbool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) {\n    furi_assert(instance);\n    furi_assert(file_path);\n\n    bool result = false;\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* file = flipper_format_file_alloc(storage);\n\n    do {\n        if(!flipper_format_file_open_existing(file, file_path)) break;\n\n        uint32_t version = 0;\n        if(!flipper_format_read_header(file, temp_str, &version)) break;\n        if(furi_string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) ||\n           (version != MUSIC_PLAYER_VERSION)) {\n            FURI_LOG_E(TAG, \"Incorrect file format or version\");\n            break;\n        }\n\n        if(!flipper_format_read_uint32(file, \"BPM\", &instance->bpm, 1)) {\n            FURI_LOG_E(TAG, \"BPM is missing\");\n            break;\n        }\n        if(!flipper_format_read_uint32(file, \"Duration\", &instance->duration, 1)) {\n            FURI_LOG_E(TAG, \"Duration is missing\");\n            break;\n        }\n        if(!flipper_format_read_uint32(file, \"Octave\", &instance->octave, 1)) {\n            FURI_LOG_E(TAG, \"Octave is missing\");\n            break;\n        }\n\n        if(!flipper_format_read_string(file, \"Notes\", temp_str)) {\n            FURI_LOG_E(TAG, \"Notes is missing\");\n            break;\n        }\n\n        if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) {\n            break;\n        }\n\n        result = true;\n    } while(false);\n\n    furi_record_close(RECORD_STORAGE);\n    flipper_format_free(file);\n    furi_string_free(temp_str);\n\n    return result;\n}\n\nbool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) {\n    furi_assert(instance);\n    furi_assert(file_path);\n\n    bool result = false;\n    FuriString* content;\n    content = furi_string_alloc();\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    do {\n        if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n            FURI_LOG_E(TAG, \"Unable to open file\");\n            break;\n        };\n\n        size_t ret = 0;\n        do {\n            uint8_t buffer[65] = {0};\n            ret = storage_file_read(file, buffer, sizeof(buffer) - 1);\n            for(size_t i = 0; i < ret; i++) {\n                furi_string_push_back(content, buffer[i]);\n            }\n        } while(ret > 0);\n\n        furi_string_trim(content);\n        if(!furi_string_size(content)) {\n            FURI_LOG_E(TAG, \"Empty file\");\n            break;\n        }\n\n        if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) {\n            FURI_LOG_E(TAG, \"Invalid file content\");\n            break;\n        }\n\n        result = true;\n    } while(0);\n\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n    furi_string_free(content);\n\n    return result;\n}\n\nbool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) {\n    furi_assert(instance);\n\n    const char* cursor = string;\n\n    // Skip name\n    cursor += skip_till(cursor, ':');\n    if(*cursor != ':') {\n        return false;\n    }\n\n    // Duration\n    cursor += skip_till(cursor, '=');\n    if(*cursor != '=') {\n        return false;\n    }\n    cursor++;\n    cursor += extract_number(cursor, &instance->duration);\n\n    // Octave\n    cursor += skip_till(cursor, '=');\n    if(*cursor != '=') {\n        return false;\n    }\n    cursor++;\n    cursor += extract_number(cursor, &instance->octave);\n\n    // BPM\n    cursor += skip_till(cursor, '=');\n    if(*cursor != '=') {\n        return false;\n    }\n    cursor++;\n    cursor += extract_number(cursor, &instance->bpm);\n\n    // Notes\n    cursor += skip_till(cursor, ':');\n    if(*cursor != ':') {\n        return false;\n    }\n    cursor++;\n    if(!music_worker_parse_notes(instance, cursor)) {\n        return false;\n    }\n\n    return true;\n}\n\nvoid music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) {\n    furi_assert(instance);\n    instance->callback = callback;\n    instance->callback_context = context;\n}\n\nvoid music_worker_set_volume(MusicWorker* instance, float volume) {\n    furi_assert(instance);\n    instance->volume = volume;\n}\n\nvoid music_worker_start(MusicWorker* instance) {\n    furi_assert(instance);\n    furi_assert(instance->should_work == false);\n\n    instance->should_work = true;\n    furi_thread_start(instance->thread);\n}\n\nvoid music_worker_stop(MusicWorker* instance) {\n    furi_assert(instance);\n    furi_assert(instance->should_work == true);\n\n    instance->should_work = false;\n    furi_thread_join(instance->thread);\n}\n\nbool music_worker_is_playing(MusicWorker* instance) {\n    furi_assert(instance);\n    return instance->should_work;\n}\n"
  },
  {
    "path": "lib/music_worker/music_worker.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\ntypedef void (*MusicWorkerCallback)(\n    uint8_t semitone,\n    uint8_t dots,\n    uint8_t duration,\n    float position,\n    void* context);\n\ntypedef struct MusicWorker MusicWorker;\n\nMusicWorker* music_worker_alloc(void);\n\nvoid music_worker_clear(MusicWorker* instance);\n\nvoid music_worker_free(MusicWorker* instance);\n\nbool music_worker_load(MusicWorker* instance, const char* file_path);\n\nbool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path);\n\nbool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path);\n\nbool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string);\n\nvoid music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context);\n\nvoid music_worker_set_volume(MusicWorker* instance, float volume);\n\nvoid music_worker_start(MusicWorker* instance);\n\nvoid music_worker_stop(MusicWorker* instance);\n\nbool music_worker_is_playing(MusicWorker* instance);\n"
  },
  {
    "path": "lib/nanopb.scons",
    "content": "from fbt.util import GLOB_FILE_EXCLUSION\n\nImport(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/nanopb\",\n    ],\n    CPPDEFINES=[\n        \"PB_ENABLE_MALLOC\",\n    ],\n    SDK_HEADERS=[\n        File(\"nanopb/pb.h\"),\n        File(\"nanopb/pb_decode.h\"),\n        File(\"nanopb/pb_encode.h\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"nanopb\")\nlibenv.ApplyLibFlags()\n\nsources = Glob(\n    \"nanopb/*.c*\",\n    exclude=GLOB_FILE_EXCLUSION,\n    source=True,\n)\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/nfc/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/nfc\",\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    SDK_HEADERS=[\n        # Main\n        File(\"nfc.h\"),\n        File(\"nfc_device.h\"),\n        File(\"nfc_listener.h\"),\n        File(\"nfc_poller.h\"),\n        File(\"nfc_scanner.h\"),\n        File(\"nfc_common.h\"),\n        # Protocols\n        File(\"protocols/iso14443_3a/iso14443_3a.h\"),\n        File(\"protocols/iso14443_3b/iso14443_3b.h\"),\n        File(\"protocols/iso14443_4a/iso14443_4a.h\"),\n        File(\"protocols/iso14443_4b/iso14443_4b.h\"),\n        File(\"protocols/mf_ultralight/mf_ultralight.h\"),\n        File(\"protocols/mf_classic/mf_classic.h\"),\n        File(\"protocols/mf_plus/mf_plus.h\"),\n        File(\"protocols/mf_desfire/mf_desfire.h\"),\n        File(\"protocols/slix/slix.h\"),\n        File(\"protocols/st25tb/st25tb.h\"),\n        File(\"protocols/felica/felica.h\"),\n        # Pollers\n        File(\"protocols/iso14443_3a/iso14443_3a_poller.h\"),\n        File(\"protocols/iso14443_3b/iso14443_3b_poller.h\"),\n        File(\"protocols/iso14443_4a/iso14443_4a_poller.h\"),\n        File(\"protocols/iso14443_4b/iso14443_4b_poller.h\"),\n        File(\"protocols/iso15693_3/iso15693_3_poller.h\"),\n        File(\"protocols/mf_ultralight/mf_ultralight_poller.h\"),\n        File(\"protocols/mf_classic/mf_classic_poller.h\"),\n        File(\"protocols/mf_plus/mf_plus_poller.h\"),\n        File(\"protocols/mf_desfire/mf_desfire_poller.h\"),\n        File(\"protocols/slix/slix_poller.h\"),\n        File(\"protocols/st25tb/st25tb_poller.h\"),\n        File(\"protocols/felica/felica_poller.h\"),\n        # Listeners\n        File(\"protocols/iso14443_3a/iso14443_3a_listener.h\"),\n        File(\"protocols/iso14443_4a/iso14443_4a_listener.h\"),\n        File(\"protocols/mf_ultralight/mf_ultralight_listener.h\"),\n        File(\"protocols/mf_classic/mf_classic_listener.h\"),\n        File(\"protocols/felica/felica_listener.h\"),\n        # Sync API\n        File(\"protocols/iso14443_3a/iso14443_3a_poller_sync.h\"),\n        File(\"protocols/mf_ultralight/mf_ultralight_poller_sync.h\"),\n        File(\"protocols/mf_classic/mf_classic_poller_sync.h\"),\n        File(\"protocols/st25tb/st25tb_poller_sync.h\"),\n        File(\"protocols/felica/felica_poller_sync.h\"),\n        # Misc\n        File(\"helpers/nfc_util.h\"),\n        File(\"helpers/felica_crc.h\"),\n        File(\"helpers/iso14443_crc.h\"),\n        File(\"helpers/iso13239_crc.h\"),\n        File(\"helpers/nfc_data_generator.h\"),\n        File(\"helpers/crypto1.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"nfc\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/nfc/helpers/crypto1.c",
    "content": "#include \"crypto1.h\"\n\n#include <lib/nfc/helpers/nfc_util.h>\n#include <lib/bit_lib/bit_lib.h>\n#include <furi.h>\n\n// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git\n\n#define SWAPENDIAN(x) \\\n    ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)\n#define LF_POLY_ODD  (0x29CE5C)\n#define LF_POLY_EVEN (0x870804)\n\n#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)\n\nCrypto1* crypto1_alloc(void) {\n    Crypto1* instance = malloc(sizeof(Crypto1));\n\n    return instance;\n}\n\nvoid crypto1_free(Crypto1* instance) {\n    furi_assert(instance);\n\n    free(instance);\n}\n\nvoid crypto1_reset(Crypto1* crypto1) {\n    furi_assert(crypto1);\n    crypto1->even = 0;\n    crypto1->odd = 0;\n}\n\nvoid crypto1_init(Crypto1* crypto1, uint64_t key) {\n    furi_assert(crypto1);\n    crypto1->even = 0;\n    crypto1->odd = 0;\n    for(int8_t i = 47; i > 0; i -= 2) {\n        crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);\n        crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);\n    }\n}\n\nstatic uint32_t crypto1_filter(uint32_t in) {\n    uint32_t out = 0;\n    out = 0xf22c0 >> (in & 0xf) & 16;\n    out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;\n    out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;\n    out |= 0x1e458 >> (in >> 12 & 0xf) & 2;\n    out |= 0x0d938 >> (in >> 16 & 0xf) & 1;\n    return FURI_BIT(0xEC57E80A, out);\n}\n\nuint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {\n    furi_assert(crypto1);\n    uint8_t out = crypto1_filter(crypto1->odd);\n    uint32_t feed = out & (!!is_encrypted);\n    feed ^= !!in;\n    feed ^= LF_POLY_ODD & crypto1->odd;\n    feed ^= LF_POLY_EVEN & crypto1->even;\n    crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed));\n\n    FURI_SWAP(crypto1->odd, crypto1->even);\n    return out;\n}\n\nuint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {\n    furi_assert(crypto1);\n    uint8_t out = 0;\n    for(uint8_t i = 0; i < 8; i++) {\n        out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;\n    }\n    return out;\n}\n\nuint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {\n    furi_assert(crypto1);\n    uint32_t out = 0;\n    for(uint8_t i = 0; i < 32; i++) {\n        out |= (uint32_t)crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);\n    }\n    return out;\n}\n\nuint32_t crypto1_prng_successor(uint32_t x, uint32_t n) {\n    SWAPENDIAN(x);\n    while(n--)\n        x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;\n\n    return SWAPENDIAN(x);\n}\n\nvoid crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out) {\n    furi_assert(crypto);\n    furi_assert(buff);\n    furi_assert(out);\n\n    size_t bits = bit_buffer_get_size(buff);\n    bit_buffer_set_size(out, bits);\n    const uint8_t* encrypted_data = bit_buffer_get_data(buff);\n    if(bits < 8) {\n        uint8_t decrypted_byte = 0;\n        uint8_t encrypted_byte = encrypted_data[0];\n        decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 0)) << 0;\n        decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 1)) << 1;\n        decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 2)) << 2;\n        decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 3)) << 3;\n        bit_buffer_set_byte(out, 0, decrypted_byte);\n    } else {\n        for(size_t i = 0; i < bits / 8; i++) {\n            uint8_t decrypted_byte = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];\n            bit_buffer_set_byte(out, i, decrypted_byte);\n        }\n    }\n}\n\nvoid crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out) {\n    furi_assert(crypto);\n    furi_assert(buff);\n    furi_assert(out);\n\n    size_t bits = bit_buffer_get_size(buff);\n    bit_buffer_set_size(out, bits);\n    const uint8_t* plain_data = bit_buffer_get_data(buff);\n    if(bits < 8) {\n        uint8_t encrypted_byte = 0;\n        for(size_t i = 0; i < bits; i++) {\n            encrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;\n        }\n        bit_buffer_set_byte(out, 0, encrypted_byte);\n    } else {\n        for(size_t i = 0; i < bits / 8; i++) {\n            uint8_t encrypted_byte = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^\n                                     plain_data[i];\n            bool parity_bit =\n                ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01);\n            bit_buffer_set_byte_with_parity(out, i, encrypted_byte, parity_bit);\n        }\n    }\n}\n\nvoid crypto1_encrypt_reader_nonce(\n    Crypto1* crypto,\n    uint64_t key,\n    uint32_t cuid,\n    uint8_t* nt,\n    uint8_t* nr,\n    BitBuffer* out,\n    bool is_nested) {\n    furi_assert(crypto);\n    furi_assert(nt);\n    furi_assert(nr);\n    furi_assert(out);\n\n    bit_buffer_set_size_bytes(out, 8);\n    uint32_t nt_num = bit_lib_bytes_to_num_be(nt, sizeof(uint32_t));\n\n    crypto1_init(crypto, key);\n    if(is_nested) {\n        nt_num = crypto1_word(crypto, nt_num ^ cuid, 1) ^ nt_num;\n    } else {\n        crypto1_word(crypto, nt_num ^ cuid, 0);\n    }\n\n    for(size_t i = 0; i < 4; i++) {\n        uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i];\n        bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01);\n        bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);\n        nr[i] = byte;\n    }\n\n    nt_num = crypto1_prng_successor(nt_num, 32);\n    for(size_t i = 4; i < 8; i++) {\n        nt_num = crypto1_prng_successor(nt_num, 8);\n        uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num);\n        bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01);\n        bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);\n    }\n}\n\nstatic uint8_t lfsr_rollback_bit(Crypto1* crypto1, uint32_t in, int fb) {\n    int out;\n    uint8_t ret;\n    uint32_t t;\n\n    crypto1->odd &= 0xffffff;\n    t = crypto1->odd;\n    crypto1->odd = crypto1->even;\n    crypto1->even = t;\n\n    out = crypto1->even & 1;\n    out ^= LF_POLY_EVEN & (crypto1->even >>= 1);\n    out ^= LF_POLY_ODD & crypto1->odd;\n    out ^= !!in;\n    out ^= (ret = crypto1_filter(crypto1->odd)) & (!!fb);\n\n    crypto1->even |= (nfc_util_even_parity32(out)) << 23;\n    return ret;\n}\n\nuint32_t crypto1_lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {\n    uint32_t ret = 0;\n    for(int i = 31; i >= 0; i--) {\n        ret |= lfsr_rollback_bit(crypto1, BEBIT(in, i), fb) << (24 ^ i);\n    }\n    return ret;\n}\n\nbool crypto1_nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {\n    return (nfc_util_even_parity8((nt >> 24) & 0xFF) ==\n            (((nt_par_enc >> 3) & 1) ^ FURI_BIT(ks, 16))) &&\n           (nfc_util_even_parity8((nt >> 16) & 0xFF) ==\n            (((nt_par_enc >> 2) & 1) ^ FURI_BIT(ks, 8))) &&\n           (nfc_util_even_parity8((nt >> 8) & 0xFF) ==\n            (((nt_par_enc >> 1) & 1) ^ FURI_BIT(ks, 0)));\n}\n\nbool crypto1_is_weak_prng_nonce(uint32_t nonce) {\n    if(nonce == 0) return false;\n    uint16_t x = nonce >> 16;\n    x = (x & 0xff) << 8 | x >> 8;\n    for(uint8_t i = 0; i < 16; i++) {\n        x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;\n    }\n    x = (x & 0xff) << 8 | x >> 8;\n    return x == (nonce & 0xFFFF);\n}\n\nuint32_t crypto1_decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key) {\n    uint64_t known_key_int = bit_lib_bytes_to_num_be(known_key.data, 6);\n    Crypto1 crypto_temp;\n    crypto1_init(&crypto_temp, known_key_int);\n    crypto1_word(&crypto_temp, nt_enc ^ cuid, 1);\n    uint32_t decrypted_nt_enc =\n        (nt_enc ^ crypto1_lfsr_rollback_word(&crypto_temp, nt_enc ^ cuid, 1));\n    return decrypted_nt_enc;\n}\n"
  },
  {
    "path": "lib/nfc/helpers/crypto1.h",
    "content": "#pragma once\n\n#include <protocols/mf_classic/mf_classic.h>\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    uint32_t odd;\n    uint32_t even;\n} Crypto1;\n\nCrypto1* crypto1_alloc(void);\n\nvoid crypto1_free(Crypto1* instance);\n\nvoid crypto1_reset(Crypto1* crypto1);\n\nvoid crypto1_init(Crypto1* crypto1, uint64_t key);\n\nuint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted);\n\nuint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted);\n\nuint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);\n\nvoid crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out);\n\nvoid crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out);\n\nvoid crypto1_encrypt_reader_nonce(\n    Crypto1* crypto,\n    uint64_t key,\n    uint32_t cuid,\n    uint8_t* nt,\n    uint8_t* nr,\n    BitBuffer* out,\n    bool is_nested);\n\nuint32_t crypto1_lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb);\n\nbool crypto1_nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc);\n\nbool crypto1_is_weak_prng_nonce(uint32_t nonce);\n\nuint32_t crypto1_decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key);\n\nuint32_t crypto1_prng_successor(uint32_t x, uint32_t n);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/felica_crc.c",
    "content": "#include \"felica_crc.h\"\n\n#include <furi/furi.h>\n\n#define FELICA_CRC_POLY (0x1021U) // Polynomial: x^16 + x^12 + x^5 + 1\n#define FELICA_CRC_INIT (0x0000U)\n\nuint16_t felica_crc_calculate(const uint8_t* data, size_t length) {\n    furi_check(data);\n\n    uint16_t crc = FELICA_CRC_INIT;\n\n    for(size_t i = 0; i < length; i++) {\n        crc ^= ((uint16_t)data[i] << 8);\n        for(size_t j = 0; j < 8; j++) {\n            if(crc & 0x8000) {\n                crc <<= 1;\n                crc ^= FELICA_CRC_POLY;\n            } else {\n                crc <<= 1;\n            }\n        }\n    }\n\n    return (crc << 8) | (crc >> 8);\n}\n\nvoid felica_crc_append(BitBuffer* buf) {\n    furi_check(buf);\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n\n    const uint16_t crc = felica_crc_calculate(data, data_size);\n    bit_buffer_append_bytes(buf, (const uint8_t*)&crc, FELICA_CRC_SIZE);\n}\n\nbool felica_crc_check(const BitBuffer* buf) {\n    furi_check(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    if(data_size <= FELICA_CRC_SIZE) return false;\n\n    uint16_t crc_received;\n    bit_buffer_write_bytes_mid(buf, &crc_received, data_size - FELICA_CRC_SIZE, FELICA_CRC_SIZE);\n\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const uint16_t crc_calc = felica_crc_calculate(data, data_size - FELICA_CRC_SIZE);\n\n    return crc_calc == crc_received;\n}\n\nvoid felica_crc_trim(BitBuffer* buf) {\n    furi_check(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    furi_assert(data_size > FELICA_CRC_SIZE);\n\n    bit_buffer_set_size_bytes(buf, data_size - FELICA_CRC_SIZE);\n}\n"
  },
  {
    "path": "lib/nfc/helpers/felica_crc.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#include \"bit_buffer.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FELICA_CRC_SIZE sizeof(uint16_t)\n\nvoid felica_crc_append(BitBuffer* buf);\n\nbool felica_crc_check(const BitBuffer* buf);\n\nvoid felica_crc_trim(BitBuffer* buf);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/iso13239_crc.c",
    "content": "#include \"iso13239_crc.h\"\n\n#include <core/check.h>\n\n#define ISO13239_CRC_INIT_DEFAULT  (0xFFFFU)\n#define ISO13239_CRC_INIT_PICOPASS (0xE012U)\n#define ISO13239_CRC_POLY          (0x8408U)\n\nstatic uint16_t\n    iso13239_crc_calculate(Iso13239CrcType type, const uint8_t* data, size_t data_size) {\n    uint16_t crc;\n\n    if(type == Iso13239CrcTypeDefault) {\n        crc = ISO13239_CRC_INIT_DEFAULT;\n    } else if(type == Iso13239CrcTypePicopass) {\n        crc = ISO13239_CRC_INIT_PICOPASS;\n    } else {\n        furi_crash(\"Wrong ISO13239 CRC type\");\n    }\n\n    for(size_t i = 0; i < data_size; ++i) {\n        crc ^= (uint16_t)data[i];\n        for(size_t j = 0; j < 8; ++j) {\n            if(crc & 1U) {\n                crc = (crc >> 1) ^ ISO13239_CRC_POLY;\n            } else {\n                crc >>= 1;\n            }\n        }\n    }\n\n    return type == Iso13239CrcTypePicopass ? crc : ~crc;\n}\n\nvoid iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf) {\n    furi_check(buf);\n\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n\n    const uint16_t crc = iso13239_crc_calculate(type, data, data_size);\n    bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO13239_CRC_SIZE);\n}\n\nbool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf) {\n    furi_check(buf);\n\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    if(data_size <= ISO13239_CRC_SIZE) return false;\n\n    uint16_t crc_received;\n    bit_buffer_write_bytes_mid(\n        buf, &crc_received, data_size - ISO13239_CRC_SIZE, ISO13239_CRC_SIZE);\n\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const uint16_t crc_calc = iso13239_crc_calculate(type, data, data_size - ISO13239_CRC_SIZE);\n\n    return crc_calc == crc_received;\n}\n\nvoid iso13239_crc_trim(BitBuffer* buf) {\n    furi_check(buf);\n\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    furi_assert(data_size > ISO13239_CRC_SIZE);\n\n    bit_buffer_set_size_bytes(buf, data_size - ISO13239_CRC_SIZE);\n}\n"
  },
  {
    "path": "lib/nfc/helpers/iso13239_crc.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO13239_CRC_SIZE sizeof(uint16_t)\n\ntypedef enum {\n    Iso13239CrcTypeDefault,\n    Iso13239CrcTypePicopass,\n} Iso13239CrcType;\n\nvoid iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf);\n\nbool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf);\n\nvoid iso13239_crc_trim(BitBuffer* buf);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/iso14443_4_layer.c",
    "content": "#include \"iso14443_4_layer.h\"\n\n#include <furi.h>\n\n#define ISO14443_4_BLOCK_PCB      (1U << 1)\n#define ISO14443_4_BLOCK_PCB_MASK (0x03)\n\n#define ISO14443_4_BLOCK_PCB_I              (0U)\n#define ISO14443_4_BLOCK_PCB_I_NAD_OFFSET   (2)\n#define ISO14443_4_BLOCK_PCB_I_CID_OFFSET   (3)\n#define ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET (4)\n#define ISO14443_4_BLOCK_PCB_I_NAD_MASK     (1U << ISO14443_4_BLOCK_PCB_I_NAD_OFFSET)\n#define ISO14443_4_BLOCK_PCB_I_CID_MASK     (1U << ISO14443_4_BLOCK_PCB_I_CID_OFFSET)\n#define ISO14443_4_BLOCK_PCB_I_CHAIN_MASK   (1U << ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET)\n\n#define ISO14443_4_BLOCK_PCB_R_MASK        (5U << 5)\n#define ISO14443_4_BLOCK_PCB_R_NACK_OFFSET (4)\n#define ISO14443_4_BLOCK_PCB_R_CID_OFFSET  (3)\n#define ISO14443_4_BLOCK_PCB_R_CID_MASK    (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)\n#define ISO14443_4_BLOCK_PCB_R_NACK_MASK   (1U << ISO14443_4_BLOCK_PCB_R_NACK_OFFSET)\n\n#define ISO14443_4_BLOCK_PCB_S_MASK                (3U << 6)\n#define ISO14443_4_BLOCK_PCB_S_CID_OFFSET          (3)\n#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET (4)\n#define ISO14443_4_BLOCK_PCB_S_CID_MASK            (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)\n#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK   (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET)\n\n#define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask))\n\n#define ISO14443_4_BLOCK_PCB_IS_R_BLOCK(pcb) \\\n    ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_MASK)\n\n#define ISO14443_4_BLOCK_PCB_IS_S_BLOCK(pcb) \\\n    ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_S_MASK)\n\n#define ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(pcb) \\\n    ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_I_CHAIN_MASK)\n\n#define ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(pcb) \\\n    ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_NACK_MASK)\n\nstruct Iso14443_4Layer {\n    uint8_t pcb;\n    uint8_t pcb_prev;\n};\n\nstatic inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) {\n    instance->pcb_prev = instance->pcb;\n    instance->pcb ^= (uint8_t)0x01;\n}\n\nIso14443_4Layer* iso14443_4_layer_alloc(void) {\n    Iso14443_4Layer* instance = malloc(sizeof(Iso14443_4Layer));\n\n    iso14443_4_layer_reset(instance);\n    return instance;\n}\n\nvoid iso14443_4_layer_free(Iso14443_4Layer* instance) {\n    furi_assert(instance);\n    free(instance);\n}\n\nvoid iso14443_4_layer_reset(Iso14443_4Layer* instance) {\n    furi_assert(instance);\n    instance->pcb_prev = 0;\n    instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB;\n}\n\nvoid iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) {\n    uint8_t block_pcb = instance->pcb & ISO14443_4_BLOCK_PCB_MASK;\n    instance->pcb = ISO14443_4_BLOCK_PCB_I | (chaining << ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET) |\n                    (CID_present << ISO14443_4_BLOCK_PCB_I_CID_OFFSET) | block_pcb;\n}\n\nvoid iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present) {\n    furi_assert(instance);\n    uint8_t block_pcb = instance->pcb & ISO14443_4_BLOCK_PCB_MASK;\n    instance->pcb = ISO14443_4_BLOCK_PCB_R_MASK |\n                    (!acknowledged << ISO14443_4_BLOCK_PCB_R_NACK_OFFSET) |\n                    (CID_present << ISO14443_4_BLOCK_PCB_R_CID_OFFSET) | block_pcb;\n}\n\nvoid iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present) {\n    furi_assert(instance);\n    uint8_t des_wtx = !deselect ? (ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) : 0;\n    instance->pcb = ISO14443_4_BLOCK_PCB_S_MASK | des_wtx |\n                    (CID_present << ISO14443_4_BLOCK_PCB_S_CID_OFFSET) | ISO14443_4_BLOCK_PCB;\n}\n\nvoid iso14443_4_layer_encode_block(\n    Iso14443_4Layer* instance,\n    const BitBuffer* input_data,\n    BitBuffer* block_data) {\n    furi_assert(instance);\n\n    bit_buffer_append_byte(block_data, instance->pcb);\n    bit_buffer_append(block_data, input_data);\n\n    iso14443_4_layer_update_pcb(instance);\n}\n\nstatic inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_data) {\n    const uint8_t* data = bit_buffer_get_data(block_data);\n    return data[0];\n}\n\nbool iso14443_4_layer_decode_block(\n    Iso14443_4Layer* instance,\n    BitBuffer* output_data,\n    const BitBuffer* block_data) {\n    furi_assert(instance);\n\n    bool ret = false;\n\n    do {\n        if(ISO14443_4_BLOCK_PCB_IS_R_BLOCK(instance->pcb_prev)) {\n            const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data);\n            ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&\n                  (!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb));\n            instance->pcb &= ISO14443_4_BLOCK_PCB_MASK;\n            iso14443_4_layer_update_pcb(instance);\n        } else if(ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(instance->pcb_prev)) {\n            const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data);\n            ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&\n                  (!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb));\n            instance->pcb &= ~(ISO14443_4_BLOCK_PCB_I_CHAIN_MASK);\n        } else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb_prev)) {\n            ret = bit_buffer_starts_with_byte(block_data, instance->pcb_prev);\n            if(bit_buffer_get_size_bytes(block_data) > 1)\n                bit_buffer_copy_right(output_data, block_data, 1);\n        } else {\n            if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break;\n            bit_buffer_copy_right(output_data, block_data, 1);\n            ret = true;\n        }\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/helpers/iso14443_4_layer.h",
    "content": "#pragma once\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso14443_4Layer Iso14443_4Layer;\n\nIso14443_4Layer* iso14443_4_layer_alloc(void);\n\nvoid iso14443_4_layer_free(Iso14443_4Layer* instance);\n\nvoid iso14443_4_layer_reset(Iso14443_4Layer* instance);\n\nvoid iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present);\nvoid iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present);\nvoid iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present);\n\nvoid iso14443_4_layer_encode_block(\n    Iso14443_4Layer* instance,\n    const BitBuffer* input_data,\n    BitBuffer* block_data);\n\nbool iso14443_4_layer_decode_block(\n    Iso14443_4Layer* instance,\n    BitBuffer* output_data,\n    const BitBuffer* block_data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/iso14443_crc.c",
    "content": "#include \"iso14443_crc.h\"\n\n#include <core/check.h>\n\n#define ISO14443_3A_CRC_INIT (0x6363U)\n#define ISO14443_3B_CRC_INIT (0xFFFFU)\n\nstatic uint16_t\n    iso14443_crc_calculate(Iso14443CrcType type, const uint8_t* data, size_t data_size) {\n    uint16_t crc;\n\n    if(type == Iso14443CrcTypeA) {\n        crc = ISO14443_3A_CRC_INIT;\n    } else if(type == Iso14443CrcTypeB) {\n        crc = ISO14443_3B_CRC_INIT;\n    } else {\n        furi_crash(\"Wrong ISO14443 CRC type\");\n    }\n\n    for(size_t i = 0; i < data_size; i++) {\n        uint8_t byte = data[i];\n        byte ^= (uint8_t)(crc & 0xff);\n        byte ^= byte << 4;\n        crc = (crc >> 8) ^ (((uint16_t)byte) << 8) ^ (((uint16_t)byte) << 3) ^ (byte >> 4);\n    }\n\n    return type == Iso14443CrcTypeA ? crc : ~crc;\n}\n\nvoid iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf) {\n    furi_check(buf);\n\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n\n    const uint16_t crc = iso14443_crc_calculate(type, data, data_size);\n    bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO14443_CRC_SIZE);\n}\n\nbool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf) {\n    furi_check(buf);\n\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    if(data_size <= ISO14443_CRC_SIZE) return false;\n\n    uint16_t crc_received;\n    bit_buffer_write_bytes_mid(\n        buf, &crc_received, data_size - ISO14443_CRC_SIZE, ISO14443_CRC_SIZE);\n\n    const uint8_t* data = bit_buffer_get_data(buf);\n    const uint16_t crc_calc = iso14443_crc_calculate(type, data, data_size - ISO14443_CRC_SIZE);\n\n    return crc_calc == crc_received;\n}\n\nvoid iso14443_crc_trim(BitBuffer* buf) {\n    furi_check(buf);\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n    furi_check(data_size > ISO14443_CRC_SIZE);\n\n    bit_buffer_set_size_bytes(buf, data_size - ISO14443_CRC_SIZE);\n}\n"
  },
  {
    "path": "lib/nfc/helpers/iso14443_crc.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO14443_CRC_SIZE sizeof(uint16_t)\n\ntypedef enum {\n    Iso14443CrcTypeA,\n    Iso14443CrcTypeB,\n} Iso14443CrcType;\n\nvoid iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf);\n\nbool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf);\n\nvoid iso14443_crc_trim(BitBuffer* buf);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/nfc_data_generator.c",
    "content": "#include \"nfc_data_generator.h\"\n\n#include <furi/furi.h>\n#include <furi_hal_random.h>\n#include <nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n\n#define NXP_MANUFACTURER_ID (0x04)\n\ntypedef void (*NfcDataGeneratorHandler)(NfcDevice* nfc_device);\n\ntypedef struct {\n    const char* name;\n    NfcDataGeneratorHandler handler;\n} NfcDataGenerator;\n\nstatic const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};\nstatic const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};\nstatic const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};\nstatic const uint8_t default_data_ntag203[] =\n    {0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE};\nstatic const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};\nstatic const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};\nstatic const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};\nstatic const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00};\n\nstatic void nfc_generate_mf_ul_uid(uint8_t* uid) {\n    uid[0] = NXP_MANUFACTURER_ID;\n    furi_hal_random_fill_buf(&uid[1], 6);\n    uid[3] |= 0x01; // To avoid forbidden 0x88 value\n    // I'm not sure how this is generated, but the upper nybble always seems to be 8\n    uid[6] &= 0x0F;\n    uid[6] |= 0x80;\n}\n\nstatic void nfc_generate_mf_ul_common(MfUltralightData* mfu_data) {\n    uint8_t uid[7];\n    mfu_data->iso14443_3a_data->uid_len = 7;\n    nfc_generate_mf_ul_uid(uid);\n    mf_ultralight_set_uid(mfu_data, uid, 7);\n\n    mfu_data->iso14443_3a_data->atqa[0] = 0x44;\n    mfu_data->iso14443_3a_data->atqa[1] = 0x00;\n    mfu_data->iso14443_3a_data->sak = 0x00;\n}\n\nstatic void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n    nfc_generate_mf_ul_common(mfu_data);\n\n    mfu_data->type = MfUltralightTypeOrigin;\n    mfu_data->pages_total = 16;\n    mfu_data->pages_read = 16;\n    memset(&mfu_data->page[4], 0xff, sizeof(MfUltralightPage));\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_mf_ul_with_config_common(MfUltralightData* mfu_data, uint8_t num_pages) {\n    nfc_generate_mf_ul_common(mfu_data);\n\n    mfu_data->pages_total = num_pages;\n    mfu_data->pages_read = num_pages;\n\n    uint16_t config_index = (num_pages - 4);\n    mfu_data->page[config_index].data[0] = 0x04; // STRG_MOD_EN\n    mfu_data->page[config_index].data[3] = 0xff; // AUTH0\n    mfu_data->page[config_index + 1].data[1] = 0x05; // VCTID\n    memset(&mfu_data->page[config_index + 2], 0xff, sizeof(MfUltralightPage)); // Default PWD\n    if(num_pages > 20) {\n        mfu_data->page[config_index - 1].data[3] = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT;\n    }\n}\n\nstatic void nfc_generate_mf_ul_ev1_common(MfUltralightData* mfu_data, uint8_t num_pages) {\n    nfc_generate_mf_ul_with_config_common(mfu_data, num_pages);\n    memcpy(&mfu_data->version, version_bytes_mf0ulx1, sizeof(MfUltralightVersion));\n    for(size_t i = 0; i < 3; ++i) {\n        mfu_data->tearing_flag[i].data = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT;\n    }\n}\n\nstatic void nfc_generate_mf_ul_11(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_mf_ul_ev1_common(mfu_data, 20);\n    mfu_data->type = MfUltralightTypeUL11;\n    mfu_data->version.prod_subtype = 0x01;\n    mfu_data->version.storage_size = 0x0B;\n    mfu_data->page[16].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_mf_ul_h11(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_mf_ul_ev1_common(mfu_data, 20);\n    mfu_data->type = MfUltralightTypeUL11;\n    mfu_data->version.prod_subtype = 0x02;\n    mfu_data->version.storage_size = 0x0B;\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_mf_ul_21(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_mf_ul_ev1_common(mfu_data, 41);\n    mfu_data->type = MfUltralightTypeUL21;\n    mfu_data->version.prod_subtype = 0x01;\n    mfu_data->version.storage_size = 0x0E;\n    mfu_data->page[37].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_mf_ul_h21(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_mf_ul_ev1_common(mfu_data, 41);\n    mfu_data->type = MfUltralightTypeUL21;\n    mfu_data->version.prod_subtype = 0x02;\n    mfu_data->version.storage_size = 0x0E;\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag203(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_mf_ul_common(mfu_data);\n    mfu_data->type = MfUltralightTypeNTAG203;\n    mfu_data->pages_total = 42;\n    mfu_data->pages_read = 42;\n    mfu_data->page[2].data[1] = 0x48; // Internal byte\n    memcpy(&mfu_data->page[3], default_data_ntag203, sizeof(MfUltralightPage)); //-V1086\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag21x_common(MfUltralightData* mfu_data, uint8_t num_pages) {\n    nfc_generate_mf_ul_with_config_common(mfu_data, num_pages);\n    memcpy(&mfu_data->version, version_bytes_ntag21x, sizeof(MfUltralightVersion));\n    mfu_data->page[2].data[1] = 0x48; // Internal byte\n    // Capability container\n    mfu_data->page[3].data[0] = 0xE1;\n    mfu_data->page[3].data[1] = 0x10;\n}\n\nstatic void nfc_generate_ntag213(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag21x_common(mfu_data, 45);\n    mfu_data->type = MfUltralightTypeNTAG213;\n    mfu_data->version.storage_size = 0x0F;\n    mfu_data->page[3].data[2] = 0x12;\n    // Default contents\n    memcpy(&mfu_data->page[4], default_data_ntag213, sizeof(default_data_ntag213));\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag215(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag21x_common(mfu_data, 135);\n    mfu_data->type = MfUltralightTypeNTAG215;\n    mfu_data->version.storage_size = 0x11;\n    mfu_data->page[3].data[2] = 0x3E;\n    // Default contents\n    memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216));\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag216(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag21x_common(mfu_data, 231);\n    mfu_data->type = MfUltralightTypeNTAG216;\n    mfu_data->version.storage_size = 0x13;\n    mfu_data->page[3].data[2] = 0x6D;\n    // Default contents\n    memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216));\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag_i2c_common(\n    MfUltralightData* mfu_data,\n    MfUltralightType type,\n    uint16_t num_pages) {\n    nfc_generate_mf_ul_common(mfu_data);\n\n    mfu_data->type = type;\n    memcpy(&mfu_data->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c));\n    mfu_data->pages_total = num_pages;\n    mfu_data->pages_read = num_pages;\n    memcpy(\n        mfu_data->page[0].data,\n        mfu_data->iso14443_3a_data->uid,\n        mfu_data->iso14443_3a_data->uid_len);\n    mfu_data->page[1].data[3] = mfu_data->iso14443_3a_data->sak;\n    mfu_data->page[2].data[0] = mfu_data->iso14443_3a_data->atqa[0];\n    mfu_data->page[2].data[1] = mfu_data->iso14443_3a_data->atqa[1];\n\n    uint16_t config_register_page = 0;\n    uint16_t session_register_page = 0;\n\n    // Sync with mifare_ultralight.c\n    switch(type) {\n    case MfUltralightTypeNTAGI2C1K:\n        config_register_page = 227;\n        session_register_page = 229;\n        break;\n    case MfUltralightTypeNTAGI2C2K:\n        config_register_page = 481;\n        session_register_page = 483;\n        break;\n    case MfUltralightTypeNTAGI2CPlus1K:\n    case MfUltralightTypeNTAGI2CPlus2K:\n        config_register_page = 232;\n        session_register_page = 234;\n        break;\n    default:\n        furi_crash(\"Unknown MFUL\");\n        break;\n    }\n\n    memcpy(\n        &mfu_data->page[config_register_page],\n        default_config_ntag_i2c,\n        sizeof(default_config_ntag_i2c));\n    memcpy(\n        &mfu_data->page[session_register_page],\n        default_config_ntag_i2c,\n        sizeof(default_config_ntag_i2c));\n}\n\nstatic void nfc_generate_ntag_i2c_1k(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C1K, 231);\n    mfu_data->version.prod_ver_minor = 0x01;\n    mfu_data->version.storage_size = 0x13;\n    memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));\n    mfu_data->page[3].data[2] = 0x6D; // Size of tag in CC\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag_i2c_2k(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C2K, 485);\n    mfu_data->version.prod_ver_minor = 0x01;\n    mfu_data->version.storage_size = 0x15;\n    memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));\n    mfu_data->page[3].data[2] = 0xEA; // Size of tag in CC\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag_i2c_plus_common(\n    MfUltralightData* mfu_data,\n    MfUltralightType type,\n    uint16_t num_pages) {\n    nfc_generate_ntag_i2c_common(mfu_data, type, num_pages);\n\n    uint16_t config_index = 227;\n    mfu_data->page[config_index].data[3] = 0xff; // AUTH0\n\n    memset(&mfu_data->page[config_index + 2], 0xFF, sizeof(MfUltralightPage)); // Default PWD\n}\n\nstatic void nfc_generate_ntag_i2c_plus_1k(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus1K, 236);\n    mfu_data->version.prod_ver_minor = 0x02;\n    mfu_data->version.storage_size = 0x13;\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_ntag_i2c_plus_2k(NfcDevice* nfc_device) {\n    MfUltralightData* mfu_data = mf_ultralight_alloc();\n\n    nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus2K, 492);\n    mfu_data->version.prod_ver_minor = 0x02;\n    mfu_data->version.storage_size = 0x15;\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);\n    mf_ultralight_free(mfu_data);\n}\n\nstatic void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) {\n    uid[0] = NXP_MANUFACTURER_ID;\n    furi_hal_random_fill_buf(&uid[1], length - 1);\n    uid[3] |= 0x01; // To avoid forbidden 0x88 value\n}\n\nstatic void\n    nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) {\n    data->iso14443_3a_data->uid_len = uid_len;\n    data->iso14443_3a_data->atqa[0] = 0x00;\n    data->iso14443_3a_data->atqa[1] = 0x00;\n    data->iso14443_3a_data->sak = 0x00;\n    // Calculate the proper ATQA and SAK\n    if(uid_len == 7) {\n        data->iso14443_3a_data->atqa[0] |= 0x40;\n    }\n    if(type == MfClassicType1k) {\n        data->iso14443_3a_data->atqa[0] |= 0x04;\n        data->iso14443_3a_data->sak = 0x08;\n    } else if(type == MfClassicType4k) {\n        data->iso14443_3a_data->atqa[0] |= 0x02;\n        data->iso14443_3a_data->sak = 0x18;\n    } else if(type == MfClassicTypeMini) {\n        data->iso14443_3a_data->atqa[0] |= 0x08;\n        data->iso14443_3a_data->sak = 0x09;\n    }\n    data->type = type;\n}\n\nstatic void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) {\n    // All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h.\n    MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].data;\n    sec_tr->access_bits.data[0] = 0xFF;\n    sec_tr->access_bits.data[1] = 0x07;\n    sec_tr->access_bits.data[2] = 0x80;\n    sec_tr->access_bits.data[3] = 0x69; // Nice\n\n    for(int i = 0; i < 6; i++) {\n        sec_tr->key_a.data[i] = 0xFF;\n        sec_tr->key_b.data[i] = 0xFF;\n    }\n\n    mf_classic_set_block_read(data, block, &data->block[block]);\n    mf_classic_set_key_found(\n        data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF);\n    mf_classic_set_key_found(\n        data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeB, 0xFFFFFFFFFFFF);\n}\n\nstatic void nfc_generate_mf_classic_block_0(\n    uint8_t* block,\n    uint8_t uid_len,\n    uint8_t sak,\n    uint8_t atqa0,\n    uint8_t atqa1) {\n    // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes\n    furi_assert(uid_len == 4 || uid_len == 7);\n    furi_assert(block);\n\n    if(uid_len == 7) {\n        uid_len -= 1;\n    }\n\n    block[uid_len + 1] = sak;\n    block[uid_len + 2] = atqa0;\n    block[uid_len + 3] = atqa1;\n\n    for(int i = uid_len + 4; i < 16; i++) {\n        block[i] = 0xFF;\n    }\n}\n\nstatic void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfClassicType type) {\n    MfClassicData* mfc_data = mf_classic_alloc();\n\n    uint8_t uid[ISO14443_3A_MAX_UID_SIZE];\n\n    nfc_generate_mf_classic_uid(uid, uid_len);\n    mf_classic_set_uid(mfc_data, uid, uid_len);\n\n    nfc_generate_mf_classic_common(mfc_data, uid_len, type);\n\n    mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]);\n\n    // Set every block to 0x00\n    uint16_t block_num = mf_classic_get_total_block_num(type);\n    for(uint16_t i = 1; i < block_num; i++) {\n        if(mf_classic_is_sector_trailer(i)) {\n            nfc_generate_mf_classic_sector_trailer(mfc_data, i);\n        } else {\n            memset(&mfc_data->block[i].data, 0x00, MF_CLASSIC_BLOCK_SIZE);\n        }\n        mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);\n    }\n\n    nfc_generate_mf_classic_block_0(\n        mfc_data->block[0].data,\n        uid_len,\n        mfc_data->iso14443_3a_data->sak,\n        mfc_data->iso14443_3a_data->atqa[0],\n        mfc_data->iso14443_3a_data->atqa[1]);\n\n    mfc_data->type = type;\n\n    nfc_device_set_data(nfc_device, NfcProtocolMfClassic, mfc_data);\n    mf_classic_free(mfc_data);\n}\n\nstatic void nfc_generate_mf_classic_mini(NfcDevice* nfc_device) {\n    nfc_generate_mf_classic(nfc_device, 4, MfClassicTypeMini);\n}\n\nstatic void nfc_generate_mf_classic_1k_4b_uid(NfcDevice* nfc_device) {\n    nfc_generate_mf_classic(nfc_device, 4, MfClassicType1k);\n}\n\nstatic void nfc_generate_mf_classic_1k_7b_uid(NfcDevice* nfc_device) {\n    nfc_generate_mf_classic(nfc_device, 7, MfClassicType1k);\n}\n\nstatic void nfc_generate_mf_classic_4k_4b_uid(NfcDevice* nfc_device) {\n    nfc_generate_mf_classic(nfc_device, 4, MfClassicType4k);\n}\n\nstatic void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) {\n    nfc_generate_mf_classic(nfc_device, 7, MfClassicType4k);\n}\n\nstatic const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = {\n    [NfcDataGeneratorTypeMfUltralight] =\n        {\n            .name = \"Mifare Ultralight\",\n            .handler = nfc_generate_mf_ul_orig,\n        },\n    [NfcDataGeneratorTypeMfUltralightEV1_11] =\n        {\n            .name = \"Mifare Ultralight EV1 11\",\n            .handler = nfc_generate_mf_ul_11,\n        },\n    [NfcDataGeneratorTypeMfUltralightEV1_H11] =\n        {\n            .name = \"Mifare Ultralight EV1 H11\",\n            .handler = nfc_generate_mf_ul_h11,\n        },\n    [NfcDataGeneratorTypeMfUltralightEV1_21] =\n        {\n            .name = \"Mifare Ultralight EV1 21\",\n            .handler = nfc_generate_mf_ul_21,\n        },\n    [NfcDataGeneratorTypeMfUltralightEV1_H21] =\n        {\n            .name = \"Mifare Ultralight EV1 H21\",\n            .handler = nfc_generate_mf_ul_h21,\n        },\n    [NfcDataGeneratorTypeNTAG203] =\n        {\n            .name = \"NTAG203\",\n            .handler = nfc_generate_ntag203,\n        },\n    [NfcDataGeneratorTypeNTAG213] =\n        {\n            .name = \"NTAG213\",\n            .handler = nfc_generate_ntag213,\n        },\n    [NfcDataGeneratorTypeNTAG215] =\n        {\n            .name = \"NTAG215\",\n            .handler = nfc_generate_ntag215,\n        },\n    [NfcDataGeneratorTypeNTAG216] =\n        {\n            .name = \"NTAG216\",\n            .handler = nfc_generate_ntag216,\n        },\n    [NfcDataGeneratorTypeNTAGI2C1k] =\n        {\n            .name = \"NTAG I2C 1k\",\n            .handler = nfc_generate_ntag_i2c_1k,\n        },\n    [NfcDataGeneratorTypeNTAGI2C2k] =\n        {\n            .name = \"NTAG I2C 2k\",\n            .handler = nfc_generate_ntag_i2c_2k,\n        },\n    [NfcDataGeneratorTypeNTAGI2CPlus1k] =\n        {\n            .name = \"NTAG I2C Plus 1k\",\n            .handler = nfc_generate_ntag_i2c_plus_1k,\n        },\n    [NfcDataGeneratorTypeNTAGI2CPlus2k] =\n        {\n            .name = \"NTAG I2C Plus 2k\",\n            .handler = nfc_generate_ntag_i2c_plus_2k,\n        },\n    [NfcDataGeneratorTypeMfClassicMini] =\n        {\n            .name = \"Mifare Mini\",\n            .handler = nfc_generate_mf_classic_mini,\n        },\n    [NfcDataGeneratorTypeMfClassic1k_4b] =\n        {\n            .name = \"Mifare Classic 1k 4byte UID\",\n            .handler = nfc_generate_mf_classic_1k_4b_uid,\n        },\n    [NfcDataGeneratorTypeMfClassic1k_7b] =\n        {\n            .name = \"Mifare Classic 1k 7byte UID\",\n            .handler = nfc_generate_mf_classic_1k_7b_uid,\n        },\n    [NfcDataGeneratorTypeMfClassic4k_4b] =\n        {\n            .name = \"Mifare Classic 4k 4byte UID\",\n            .handler = nfc_generate_mf_classic_4k_4b_uid,\n        },\n    [NfcDataGeneratorTypeMfClassic4k_7b] =\n        {\n            .name = \"Mifare Classic 4k 7byte UID\",\n            .handler = nfc_generate_mf_classic_4k_7b_uid,\n        },\n};\n\nconst char* nfc_data_generator_get_name(NfcDataGeneratorType type) {\n    furi_check(type < NfcDataGeneratorTypeNum);\n\n    return nfc_data_generator[type].name;\n}\n\nvoid nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device) {\n    furi_check(type < NfcDataGeneratorTypeNum);\n    furi_check(nfc_device);\n\n    nfc_data_generator[type].handler(nfc_device);\n}\n"
  },
  {
    "path": "lib/nfc/helpers/nfc_data_generator.h",
    "content": "#pragma once\n\n#include <nfc/nfc_device.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    NfcDataGeneratorTypeMfUltralight,\n    NfcDataGeneratorTypeMfUltralightEV1_11,\n    NfcDataGeneratorTypeMfUltralightEV1_H11,\n    NfcDataGeneratorTypeMfUltralightEV1_21,\n    NfcDataGeneratorTypeMfUltralightEV1_H21,\n    NfcDataGeneratorTypeNTAG203,\n    NfcDataGeneratorTypeNTAG213,\n    NfcDataGeneratorTypeNTAG215,\n    NfcDataGeneratorTypeNTAG216,\n    NfcDataGeneratorTypeNTAGI2C1k,\n    NfcDataGeneratorTypeNTAGI2C2k,\n    NfcDataGeneratorTypeNTAGI2CPlus1k,\n    NfcDataGeneratorTypeNTAGI2CPlus2k,\n\n    NfcDataGeneratorTypeMfClassicMini,\n    NfcDataGeneratorTypeMfClassic1k_4b,\n    NfcDataGeneratorTypeMfClassic1k_7b,\n    NfcDataGeneratorTypeMfClassic4k_4b,\n    NfcDataGeneratorTypeMfClassic4k_7b,\n\n    NfcDataGeneratorTypeNum,\n\n} NfcDataGeneratorType;\n\nconst char* nfc_data_generator_get_name(NfcDataGeneratorType type);\n\nvoid nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/helpers/nfc_util.c",
    "content": "#include \"nfc_util.h\"\n\n#include <furi.h>\n\nstatic const uint8_t nfc_util_odd_byte_parity[256] = {\n    1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,\n    1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1,\n    1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,\n    0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0,\n    1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,\n    0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0,\n    0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,\n    0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,\n    1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};\n\nuint8_t nfc_util_even_parity8(uint8_t data) {\n    return !nfc_util_odd_byte_parity[data];\n}\n\nuint8_t nfc_util_even_parity32(uint32_t data) {\n    // data ^= data >> 16;\n    // data ^= data >> 8;\n    // return !nfc_util_odd_byte_parity[data];\n    return __builtin_parity(data) & 0xFF;\n}\n\nuint8_t nfc_util_odd_parity8(uint8_t data) {\n    return nfc_util_odd_byte_parity[data];\n}\n\nvoid nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) {\n    furi_check(src);\n    furi_check(dst);\n\n    uint8_t parity = 0;\n    uint8_t bit = 0;\n    while(len--) {\n        parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first\n        bit++;\n        if(bit == 8) {\n            *dst = parity;\n            dst++;\n            parity = 0;\n            bit = 0;\n        }\n        src++;\n    }\n\n    if(bit) {\n        *dst = parity;\n    }\n}\n"
  },
  {
    "path": "lib/nfc/helpers/nfc_util.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nuint8_t nfc_util_even_parity8(uint8_t data);\n\nuint8_t nfc_util_even_parity32(uint32_t data);\n\nuint8_t nfc_util_odd_parity8(uint8_t data);\n\nvoid nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc.c",
    "content": "#ifndef FW_CFG_unit_tests\n\n#include \"nfc.h\"\n\n#include <furi_hal_nfc.h>\n#include <furi/furi.h>\n\n#define TAG \"Nfc\"\n\n#define NFC_MAX_BUFFER_SIZE (256)\n\n#define NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC (512 * 64)\n#define NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC (256 * 64)\n\ntypedef enum {\n    NfcStateIdle,\n    NfcStateRunning,\n} NfcState;\n\ntypedef enum {\n    NfcPollerStateStart,\n    NfcPollerStateReady,\n    NfcPollerStateReset,\n    NfcPollerStateStop,\n\n    NfcPollerStateNum,\n} NfcPollerState;\n\ntypedef enum {\n    NfcCommStateIdle,\n    NfcCommStateWaitBlockTxTimer,\n    NfcCommStateReadyTx,\n    NfcCommStateWaitTxEnd,\n    NfcCommStateWaitRxStart,\n    NfcCommStateWaitRxEnd,\n    NfcCommStateFailed,\n} NfcCommState;\n\ntypedef enum {\n    NfcConfigurationStateIdle,\n    NfcConfigurationStateDone,\n} NfcConfigurationState;\n\nstruct Nfc {\n    NfcState state;\n    NfcPollerState poller_state;\n    NfcCommState comm_state;\n    NfcConfigurationState config_state;\n    NfcMode mode;\n\n    uint32_t fdt_listen_fc;\n    uint32_t mask_rx_time_fc;\n    uint32_t fdt_poll_fc;\n    uint32_t fdt_poll_poll_us;\n    uint32_t guard_time_us;\n    NfcEventCallback callback;\n    void* context;\n\n    uint8_t tx_buffer[NFC_MAX_BUFFER_SIZE];\n    size_t tx_bits;\n    uint8_t rx_buffer[NFC_MAX_BUFFER_SIZE];\n    size_t rx_bits;\n\n    FuriThread* worker_thread;\n};\n\ntypedef bool (*NfcWorkerPollerStateHandler)(Nfc* instance);\n\nstatic const FuriHalNfcTech nfc_tech_table[NfcModeNum][NfcTechNum] = {\n    [NfcModePoller] =\n        {\n            [NfcTechIso14443a] = FuriHalNfcTechIso14443a,\n            [NfcTechIso14443b] = FuriHalNfcTechIso14443b,\n            [NfcTechIso15693] = FuriHalNfcTechIso15693,\n            [NfcTechFelica] = FuriHalNfcTechFelica,\n        },\n    [NfcModeListener] =\n        {\n            [NfcTechIso14443a] = FuriHalNfcTechIso14443a,\n            [NfcTechIso14443b] = FuriHalNfcTechInvalid,\n            [NfcTechIso15693] = FuriHalNfcTechIso15693,\n            [NfcTechFelica] = FuriHalNfcTechFelica,\n        },\n};\n\nstatic NfcError nfc_process_hal_error(FuriHalNfcError error) {\n    NfcError ret = NfcErrorNone;\n\n    switch(error) {\n    case FuriHalNfcErrorNone:\n        ret = NfcErrorNone;\n        break;\n    case FuriHalNfcErrorIncompleteFrame:\n        ret = NfcErrorIncompleteFrame;\n        break;\n    case FuriHalNfcErrorDataFormat:\n        ret = NfcErrorDataFormat;\n        break;\n\n    default:\n        ret = NfcErrorInternal;\n    }\n\n    return ret;\n}\n\nstatic int32_t nfc_worker_listener(void* context) {\n    furi_assert(context);\n\n    Nfc* instance = context;\n    furi_assert(instance->callback);\n    furi_assert(instance->config_state == NfcConfigurationStateDone);\n\n    instance->state = NfcStateRunning;\n\n    furi_hal_nfc_event_start();\n\n    NfcEventData event_data = {};\n    event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);\n    NfcEvent nfc_event = {.data = event_data};\n    NfcCommand command = NfcCommandContinue;\n\n    while(true) {\n        FuriHalNfcEvent event = furi_hal_nfc_listener_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n        if(event & FuriHalNfcEventAbortRequest) {\n            nfc_event.type = NfcEventTypeUserAbort;\n            instance->callback(nfc_event, instance->context);\n            break;\n        }\n        if(event & FuriHalNfcEventFieldOn) {\n            nfc_event.type = NfcEventTypeFieldOn;\n            instance->callback(nfc_event, instance->context);\n        }\n        if(event & FuriHalNfcEventFieldOff) {\n            nfc_event.type = NfcEventTypeFieldOff;\n            instance->callback(nfc_event, instance->context);\n            furi_hal_nfc_listener_idle();\n        }\n        if(event & FuriHalNfcEventListenerActive) {\n            nfc_event.type = NfcEventTypeListenerActivated;\n            instance->callback(nfc_event, instance->context);\n        }\n        if(event & FuriHalNfcEventRxEnd) {\n            furi_hal_nfc_timer_block_tx_start(instance->fdt_listen_fc);\n\n            nfc_event.type = NfcEventTypeRxEnd;\n            furi_hal_nfc_listener_rx(\n                instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);\n            bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits);\n            command = instance->callback(nfc_event, instance->context);\n            if(command == NfcCommandStop) {\n                break;\n            } else if(command == NfcCommandReset) {\n                furi_hal_nfc_listener_enable_rx();\n            } else if(command == NfcCommandSleep) {\n                furi_hal_nfc_listener_idle();\n            }\n        }\n    }\n\n    furi_hal_nfc_reset_mode();\n    instance->config_state = NfcConfigurationStateIdle;\n\n    bit_buffer_free(event_data.buffer);\n    furi_hal_nfc_low_power_mode_start();\n    return 0;\n}\n\nbool nfc_worker_poller_start_handler(Nfc* instance) {\n    furi_hal_nfc_poller_field_on();\n    if(instance->guard_time_us) {\n        furi_hal_nfc_timer_block_tx_start_us(instance->guard_time_us);\n        FuriHalNfcEvent event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n        furi_assert(event & FuriHalNfcEventTimerBlockTxExpired);\n    }\n    instance->poller_state = NfcPollerStateReady;\n\n    return false;\n}\n\nbool nfc_worker_poller_ready_handler(Nfc* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    NfcEvent event = {.type = NfcEventTypePollerReady};\n    command = instance->callback(event, instance->context);\n    if(command == NfcCommandReset) {\n        instance->poller_state = NfcPollerStateReset;\n    } else if(command == NfcCommandStop) {\n        instance->poller_state = NfcPollerStateStop;\n    }\n\n    return false;\n}\n\nbool nfc_worker_poller_reset_handler(Nfc* instance) {\n    furi_hal_nfc_low_power_mode_start();\n    furi_delay_ms(100);\n    furi_hal_nfc_low_power_mode_stop();\n    instance->poller_state = NfcPollerStateStart;\n\n    return false;\n}\n\nbool nfc_worker_poller_stop_handler(Nfc* instance) {\n    furi_hal_nfc_reset_mode();\n    instance->config_state = NfcConfigurationStateIdle;\n\n    furi_hal_nfc_low_power_mode_start();\n    // Wait after field is off some time to reset tag power\n    furi_delay_ms(10);\n    instance->poller_state = NfcPollerStateStart;\n\n    return true;\n}\n\nstatic const NfcWorkerPollerStateHandler nfc_worker_poller_state_handlers[NfcPollerStateNum] = {\n    [NfcPollerStateStart] = nfc_worker_poller_start_handler,\n    [NfcPollerStateReady] = nfc_worker_poller_ready_handler,\n    [NfcPollerStateReset] = nfc_worker_poller_reset_handler,\n    [NfcPollerStateStop] = nfc_worker_poller_stop_handler,\n};\n\nstatic int32_t nfc_worker_poller(void* context) {\n    furi_assert(context);\n\n    Nfc* instance = context;\n    furi_assert(instance->callback);\n    instance->state = NfcStateRunning;\n    instance->poller_state = NfcPollerStateStart;\n\n    furi_hal_nfc_event_start();\n\n    bool exit = false;\n    while(!exit) {\n        exit = nfc_worker_poller_state_handlers[instance->poller_state](instance);\n    }\n\n    return 0;\n}\n\nNfc* nfc_alloc(void) {\n    furi_check(furi_hal_nfc_acquire() == FuriHalNfcErrorNone);\n\n    Nfc* instance = malloc(sizeof(Nfc));\n    instance->state = NfcStateIdle;\n    instance->comm_state = NfcCommStateIdle;\n    instance->config_state = NfcConfigurationStateIdle;\n\n    instance->worker_thread = furi_thread_alloc();\n    furi_thread_set_name(instance->worker_thread, \"NfcWorker\");\n    furi_thread_set_context(instance->worker_thread, instance);\n    furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHighest);\n    furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);\n\n    return instance;\n}\n\nvoid nfc_free(Nfc* instance) {\n    furi_check(instance);\n    furi_check(instance->state == NfcStateIdle);\n\n    furi_thread_free(instance->worker_thread);\n    free(instance);\n\n    furi_hal_nfc_release();\n}\n\nvoid nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {\n    furi_check(instance);\n    furi_check(mode < NfcModeNum);\n    furi_check(tech < NfcTechNum);\n    furi_check(instance->config_state == NfcConfigurationStateIdle);\n\n    FuriHalNfcTech hal_tech = nfc_tech_table[mode][tech];\n    if(hal_tech == FuriHalNfcTechInvalid) {\n        furi_crash(\"Unsupported mode for given tech\");\n    }\n    FuriHalNfcMode hal_mode = (mode == NfcModePoller) ? FuriHalNfcModePoller :\n                                                        FuriHalNfcModeListener;\n    furi_hal_nfc_low_power_mode_stop();\n    furi_hal_nfc_set_mode(hal_mode, hal_tech);\n\n    instance->mode = mode;\n    instance->config_state = NfcConfigurationStateDone;\n}\n\nvoid nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {\n    furi_check(instance);\n    instance->fdt_poll_fc = fdt_poll_fc;\n}\n\nvoid nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {\n    furi_check(instance);\n    instance->fdt_listen_fc = fdt_listen_fc;\n}\n\nvoid nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {\n    furi_check(instance);\n    instance->fdt_poll_poll_us = fdt_poll_poll_us;\n}\n\nvoid nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {\n    furi_check(instance);\n    instance->guard_time_us = guard_time_us;\n}\n\nvoid nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {\n    furi_check(instance);\n    instance->mask_rx_time_fc = mask_rx_time_fc;\n}\n\nvoid nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {\n    furi_check(instance);\n    furi_check(instance->worker_thread);\n    furi_check(callback);\n    furi_check(instance->config_state == NfcConfigurationStateDone);\n\n    instance->callback = callback;\n    instance->context = context;\n    if(instance->mode == NfcModePoller) {\n        furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);\n    } else {\n        furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);\n    }\n    instance->comm_state = NfcCommStateIdle;\n    furi_thread_start(instance->worker_thread);\n}\n\nvoid nfc_stop(Nfc* instance) {\n    furi_check(instance);\n    furi_check(instance->state == NfcStateRunning);\n\n    if(instance->mode == NfcModeListener) {\n        furi_hal_nfc_abort();\n    }\n    furi_thread_join(instance->worker_thread);\n\n    instance->state = NfcStateIdle;\n}\n\nNfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n\n    NfcError ret = NfcErrorNone;\n\n    while(furi_hal_nfc_timer_block_tx_is_running()) {\n    }\n\n    FuriHalNfcError error =\n        furi_hal_nfc_listener_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));\n    if(error != FuriHalNfcErrorNone) {\n        FURI_LOG_D(TAG, \"Failed in listener TX\");\n        ret = nfc_process_hal_error(error);\n    }\n\n    return ret;\n}\n\nstatic NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) {\n    FuriHalNfcEvent event = 0;\n    NfcError error = NfcErrorNone;\n\n    while(true) {\n        event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n        if(event & FuriHalNfcEventTimerBlockTxExpired) {\n            if(instance->comm_state == NfcCommStateWaitBlockTxTimer) {\n                instance->comm_state = NfcCommStateReadyTx;\n            }\n        }\n        if(event & FuriHalNfcEventTxEnd) {\n            if(instance->comm_state == NfcCommStateWaitTxEnd) {\n                if(fwt_fc) {\n                    furi_hal_nfc_timer_fwt_start(fwt_fc);\n                }\n                furi_hal_nfc_timer_block_tx_start_us(instance->fdt_poll_poll_us);\n                instance->comm_state = NfcCommStateWaitRxStart;\n            }\n        }\n        if(event & FuriHalNfcEventRxStart) {\n            if(instance->comm_state == NfcCommStateWaitRxStart) {\n                furi_hal_nfc_timer_block_tx_stop();\n                furi_hal_nfc_timer_fwt_stop();\n                instance->comm_state = NfcCommStateWaitRxEnd;\n            }\n        }\n        if(event & FuriHalNfcEventRxEnd) {\n            furi_hal_nfc_timer_block_tx_start(instance->fdt_poll_fc);\n            furi_hal_nfc_timer_fwt_stop();\n            instance->comm_state = NfcCommStateWaitBlockTxTimer;\n            break;\n        }\n        if(event & FuriHalNfcEventTimerFwtExpired) {\n            if(instance->comm_state == NfcCommStateWaitRxStart) {\n                error = NfcErrorTimeout;\n                FURI_LOG_D(TAG, \"FWT Timeout\");\n                if(furi_hal_nfc_timer_block_tx_is_running()) {\n                    instance->comm_state = NfcCommStateWaitBlockTxTimer;\n                } else {\n                    instance->comm_state = NfcCommStateReadyTx;\n                }\n                break;\n            }\n        }\n    }\n\n    return error;\n}\n\nNfcError nfc_iso14443a_poller_trx_custom_parity(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    furi_check(instance->poller_state == NfcPollerStateReady);\n\n    NfcError ret = NfcErrorNone;\n    FuriHalNfcError error = FuriHalNfcErrorNone;\n    do {\n        furi_hal_nfc_trx_reset();\n        while(furi_hal_nfc_timer_block_tx_is_running()) {\n            FuriHalNfcEvent event =\n                furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n            if(event & FuriHalNfcEventTimerBlockTxExpired) break;\n        }\n        bit_buffer_write_bytes_with_parity(\n            tx_buffer, instance->tx_buffer, sizeof(instance->tx_buffer), &instance->tx_bits);\n        error =\n            furi_hal_nfc_iso14443a_poller_tx_custom_parity(instance->tx_buffer, instance->tx_bits);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller TX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n        instance->comm_state = NfcCommStateWaitTxEnd;\n        ret = nfc_poller_trx_state_machine(instance, fwt);\n        if(ret != NfcErrorNone) {\n            FURI_LOG_T(TAG, \"Failed TRX state machine\");\n            break;\n        }\n\n        error = furi_hal_nfc_poller_rx(\n            instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller RX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n        if(instance->rx_bits >= 9) {\n            if((instance->rx_bits % 9) != 0) {\n                ret = NfcErrorDataFormat;\n                break;\n            }\n        }\n\n        bit_buffer_copy_bytes_with_parity(rx_buffer, instance->rx_buffer, instance->rx_bits);\n    } while(false);\n\n    return ret;\n}\n\nNfcError\n    nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    furi_check(instance->poller_state == NfcPollerStateReady);\n\n    NfcError ret = NfcErrorNone;\n    FuriHalNfcError error = FuriHalNfcErrorNone;\n    do {\n        furi_hal_nfc_trx_reset();\n        while(furi_hal_nfc_timer_block_tx_is_running()) {\n            FuriHalNfcEvent event =\n                furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n            if(event & FuriHalNfcEventTimerBlockTxExpired) break;\n        }\n        error =\n            furi_hal_nfc_poller_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller TX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n        instance->comm_state = NfcCommStateWaitTxEnd;\n        ret = nfc_poller_trx_state_machine(instance, fwt);\n        if(ret != NfcErrorNone) {\n            FURI_LOG_T(TAG, \"Failed TRX state machine\");\n            break;\n        }\n\n        error = furi_hal_nfc_poller_rx(\n            instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller RX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n\n        bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);\n    } while(false);\n\n    return ret;\n}\n\nNfcError nfc_iso14443a_listener_set_col_res_data(\n    Nfc* instance,\n    uint8_t* uid,\n    uint8_t uid_len,\n    uint8_t* atqa,\n    uint8_t sak) {\n    furi_check(instance);\n\n    FuriHalNfcError error =\n        furi_hal_nfc_iso14443a_listener_set_col_res_data(uid, uid_len, atqa, sak);\n    instance->comm_state = NfcCommStateIdle;\n    return nfc_process_hal_error(error);\n}\n\nNfcError nfc_iso14443a_poller_trx_short_frame(\n    Nfc* instance,\n    NfcIso14443aShortFrame frame,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(rx_buffer);\n\n    FuriHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ?\n                                            FuriHalNfcaShortFrameAllReq :\n                                            FuriHalNfcaShortFrameSensReq;\n\n    furi_check(instance->poller_state == NfcPollerStateReady);\n\n    NfcError ret = NfcErrorNone;\n    FuriHalNfcError error = FuriHalNfcErrorNone;\n    do {\n        furi_hal_nfc_trx_reset();\n        while(furi_hal_nfc_timer_block_tx_is_running()) {\n            FuriHalNfcEvent event =\n                furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n            if(event & FuriHalNfcEventTimerBlockTxExpired) break;\n        }\n        error = furi_hal_nfc_iso14443a_poller_trx_short_frame(short_frame);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller TX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n        instance->comm_state = NfcCommStateWaitTxEnd;\n        ret = nfc_poller_trx_state_machine(instance, fwt);\n        if(ret != NfcErrorNone) {\n            FURI_LOG_T(TAG, \"Failed TRX state machine\");\n            break;\n        }\n\n        error = furi_hal_nfc_poller_rx(\n            instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller RX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n\n        bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);\n    } while(false);\n\n    return ret;\n}\n\nNfcError nfc_iso14443a_poller_trx_sdd_frame(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    furi_check(instance->poller_state == NfcPollerStateReady);\n\n    NfcError ret = NfcErrorNone;\n    FuriHalNfcError error = FuriHalNfcErrorNone;\n    do {\n        furi_hal_nfc_trx_reset();\n        while(furi_hal_nfc_timer_block_tx_is_running()) {\n            FuriHalNfcEvent event =\n                furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);\n            if(event & FuriHalNfcEventTimerBlockTxExpired) break;\n        }\n        error = furi_hal_nfc_iso14443a_tx_sdd_frame(\n            bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller TX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n        instance->comm_state = NfcCommStateWaitTxEnd;\n        ret = nfc_poller_trx_state_machine(instance, fwt);\n        if(ret != NfcErrorNone) {\n            FURI_LOG_T(TAG, \"Failed TRX state machine\");\n            break;\n        }\n\n        error = furi_hal_nfc_poller_rx(\n            instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);\n        if(error != FuriHalNfcErrorNone) {\n            FURI_LOG_D(TAG, \"Failed in poller RX\");\n            ret = nfc_process_hal_error(error);\n            break;\n        }\n\n        bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);\n    } while(false);\n\n    return ret;\n}\n\nNfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n\n    NfcError ret = NfcErrorNone;\n    FuriHalNfcError error = FuriHalNfcErrorNone;\n\n    const uint8_t* tx_data = bit_buffer_get_data(tx_buffer);\n    const uint8_t* tx_parity = bit_buffer_get_parity(tx_buffer);\n    size_t tx_bits = bit_buffer_get_size(tx_buffer);\n\n    error = furi_hal_nfc_iso14443a_listener_tx_custom_parity(tx_data, tx_parity, tx_bits);\n    ret = nfc_process_hal_error(error);\n\n    return ret;\n}\n\nNfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {\n    furi_check(instance);\n\n    while(furi_hal_nfc_timer_block_tx_is_running()) {\n    }\n\n    FuriHalNfcError error = furi_hal_nfc_iso15693_listener_tx_sof();\n    NfcError ret = nfc_process_hal_error(error);\n\n    return ret;\n}\n\nNfcError nfc_felica_listener_set_sensf_res_data(\n    Nfc* instance,\n    const uint8_t* idm,\n    const uint8_t idm_len,\n    const uint8_t* pmm,\n    const uint8_t pmm_len,\n    const uint16_t sys_code) {\n    furi_check(instance);\n\n    FuriHalNfcError error =\n        furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len, sys_code);\n    instance->comm_state = NfcCommStateIdle;\n    return nfc_process_hal_error(error);\n}\n\nvoid nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {\n    furi_check(instance);\n\n    furi_hal_nfc_timer_block_tx_start(\n        NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC +\n        target_time_slot * NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC);\n}\n\nvoid nfc_felica_listener_timer_anticol_stop(Nfc* instance) {\n    furi_check(instance);\n\n    if(furi_hal_nfc_timer_block_tx_is_running()) {\n        furi_hal_nfc_timer_block_tx_stop();\n    }\n}\n\n#endif // FW_CFG_unit_tests\n"
  },
  {
    "path": "lib/nfc/nfc.h",
    "content": "/**\n * @file nfc.h\n * @brief Transport layer Nfc library.\n *\n * The Nfc layer is responsible for setting the operating mode (poller or listener)\n * and technology (ISO14443-3A/B, ISO15693, ...), data exchange between higher\n * protocol-specific levels and underlying NFC hardware, as well as timings handling.\n *\n * In applications using the NFC protocol system there is no need to neiter explicitly\n * create an Nfc instance nor call any of its functions, as it is all handled\n * automatically under the hood.\n *\n * If the NFC protocol system is not suitable for the application's intended purpose\n * or there is need of having direct access to the NFC transport layer, then an Nfc\n * instance must be allocated and the below functions shall be used to implement\n * the required logic.\n */\n#pragma once\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Nfc opaque type definition.\n */\ntypedef struct Nfc Nfc;\n\n/**\n * @brief Enumeration of possible Nfc event types.\n *\n * Not all technologies implement all events (this is due to hardware limitations).\n */\ntypedef enum {\n    NfcEventTypeUserAbort, /**< User code explicitly aborted the current operation. */\n    NfcEventTypeFieldOn, /**< Reader's field was detected by the NFC hardware. */\n    NfcEventTypeFieldOff, /**< Reader's field was lost. */\n    NfcEventTypeTxStart, /**< Data transmission has started. */\n    NfcEventTypeTxEnd, /**< Data transmission has ended. */\n    NfcEventTypeRxStart, /**< Data reception has started. */\n    NfcEventTypeRxEnd, /**< Data reception has ended. */\n\n    NfcEventTypeListenerActivated, /**< The listener has been activated by the reader. */\n    NfcEventTypePollerReady, /**< The card has been activated by the poller. */\n} NfcEventType;\n\n/**\n * @brief Nfc event data structure.\n */\ntypedef struct {\n    BitBuffer* buffer; /**< Pointer to the received data buffer. */\n} NfcEventData;\n\n/**\n * @brief Nfc event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    NfcEventType type; /**< Type of the emitted event. */\n    NfcEventData data; /**< Event-specific data. */\n} NfcEvent;\n\n/**\n * @brief Enumeration of possible Nfc commands.\n *\n * The event callback must return one of these to determine the next action.\n */\ntypedef enum {\n    NfcCommandContinue, /**< Continue operation normally. */\n    NfcCommandReset, /**< Reset the current state. */\n    NfcCommandStop, /**< Stop the current operation. */\n    NfcCommandSleep, /**< Switch Nfc hardware to low-power mode. */\n} NfcCommand;\n\n/**\n * @brief Nfc event callback type.\n *\n * A function of this type must be passed as the callback parameter upon start of a an Nfc instance.\n *\n * @param [in] event Nfc event, passed by value, complete with protocol type and data.\n * @param [in,out] context pointer to the user-specific context (set when starting an Nfc instance).\n * @returns command which the event producer must execute.\n */\ntypedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context);\n\n/**\n * @brief Enumeration of possible operating modes.\n *\n * Not all technologies implement the listener operating mode.\n */\ntypedef enum {\n    NfcModePoller, /**< Configure the Nfc instance as a poller. */\n    NfcModeListener, /**< Configure the Nfc instance as a listener. */\n\n    NfcModeNum, /**< Operating mode count. Internal use. */\n} NfcMode;\n\n/**\n * @brief Enumeration of available technologies.\n */\ntypedef enum {\n    NfcTechIso14443a, /**< Configure the Nfc instance to use the ISO14443-3A technology. */\n    NfcTechIso14443b, /**< Configure the Nfc instance to use the ISO14443-3B technology. */\n    NfcTechIso15693, /**< Configure the Nfc instance to use the ISO15693 technology. */\n    NfcTechFelica, /**< Configure the Nfc instance to use the FeliCa technology. */\n\n    NfcTechNum, /**< Technologies count. Internal use. */\n} NfcTech;\n\n/**\n * @brief Enumeration of possible Nfc error codes.\n */\ntypedef enum {\n    NfcErrorNone, /**< No error has occurred. */\n    NfcErrorInternal, /**< An unknown error has occured on the lower level. */\n    NfcErrorTimeout, /**< Operation is taking too long (e.g. card does not respond). */\n    NfcErrorIncompleteFrame, /**< An incomplete data frame has been received. */\n    NfcErrorDataFormat, /**< Data has not been parsed due to wrong/unknown format. */\n} NfcError;\n\n/**\n * @brief Allocate an Nfc instance.\n *\n * Will exclusively take over the NFC HAL until deleted.\n *\n * @returns pointer to the allocated Nfc instance.\n */\nNfc* nfc_alloc(void);\n\n/**\n * @brief Delete an Nfc instance.\n *\n * Will release the NFC HAL lock, making it available for use by others.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid nfc_free(Nfc* instance);\n\n/**\n * @brief Configure the Nfc instance to work in a particular mode.\n *\n * Not all technologies implement the listener operating mode.\n *\n * @param[in,out] instance pointer to the instance to be configured.\n * @param[in] mode required operating mode.\n * @param[in] tech required technology configuration.\n */\nvoid nfc_config(Nfc* instance, NfcMode mode, NfcTech tech);\n\n/**\n * @brief Set poller frame delay time.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] fdt_poll_fc frame delay time, in carrier cycles.\n*/\nvoid nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc);\n\n/**\n * @brief Set listener frame delay time.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] fdt_listen_fc frame delay time, in carrier cycles.\n */\nvoid nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc);\n\n/**\n * @brief Set mask receive time.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] mask_rx_time_fc mask receive time, in carrier cycles.\n */\nvoid nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc);\n\n/**\n * @brief Set frame delay time.\n *\n * Frame delay time is the minimum time between two consecutive poll frames.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] fdt_poll_poll_us frame delay time, in microseconds.\n */\nvoid nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us);\n\n/**\n * @brief Set guard time.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] guard_time_us guard time, in microseconds.\n */\nvoid nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us);\n\n/**\n * @brief Start the Nfc instance.\n *\n * The instance must be configured to work with a specific technology\n * in a specific operating mode with a nfc_config() call before starting.\n *\n * Once started, the user code will be receiving events through the provided\n * callback which must handle them according to the logic required.\n *\n * @param[in,out] instance pointer to the instance to be started.\n * @param[in] callback pointer to a user-defined callback function which will receive events.\n * @param[in] context pointer to a user-specific context (will be passed to the callback).\n */\nvoid nfc_start(Nfc* instance, NfcEventCallback callback, void* context);\n\n/**\n * @brief Stop Nfc instance.\n *\n * The instance can only be stopped if it is running.\n *\n * @param[in,out] instance pointer to the instance to be stopped.\n */\nvoid nfc_stop(Nfc* instance);\n\n/**\n * @brief Transmit and receive a data frame in poller mode.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * The data being transmitted and received may be either bit- or byte-oriented.\n * It shall not contain any technology-specific sequences as start or stop bits\n * and/or other special symbols, as this is handled on the underlying HAL level.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError\n    nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt);\n\n/**\n * @brief Transmit a data frame in listener mode.\n *\n * Used to transmit a response to the reader request in listener mode.\n *\n * The data being transmitted may be either bit- or byte-oriented.\n * It shall not contain any technology-specific sequences as start or stop bits\n * and/or other special symbols, as this is handled on the underlying HAL level.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer);\n\n/*\n * Technology-specific functions.\n *\n * In a perfect world, this would not be necessary.\n * However, the current implementation employs NFC hardware that partially implements\n * certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features.\n */\n\n/******************* ISO14443-3A specific API *******************/\n\n/**\n * @brief Enumeration of possible ISO14443-3A short frame types.\n */\ntypedef enum {\n    NfcIso14443aShortFrameSensReq,\n    NfcIso14443aShortFrameAllReqa,\n} NfcIso14443aShortFrame;\n\n/**\n * @brief Transmit an ISO14443-3A short frame and receive the response in poller mode.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] frame type of short frame to be sent.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso14443a_poller_trx_short_frame(\n    Nfc* instance,\n    NfcIso14443aShortFrame frame,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Transmit an ISO14443-3A SDD frame and receive the response in poller mode.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso14443a_poller_trx_sdd_frame(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Transmit an ISO14443-3A data frame with custom parity bits and receive the response in poller mode.\n *\n * Same as nfc_poller_trx(), but uses the parity bits provided by the user code\n * instead of calculating them automatically.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso14443a_poller_trx_custom_parity(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Transmit an ISO14443-3A frame with custom parity bits in listener mode.\n *\n * Same as nfc_listener_tx(), but uses the parity bits provided by the user code\n * instead of calculating them automatically.\n *\n * @param[in,out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer);\n\n/**\n * @brief Set ISO14443-3A collision resolution parameters in listener mode.\n *\n * Configures the NFC hardware for automatic collision resolution.\n *\n * @param[in,out] instance pointer to the instance to be configured.\n * @param[in] uid pointer to a byte array containing the UID.\n * @param[in] uid_len UID length in bytes (must be supported by the protocol).\n * @param[in] atqa ATQA byte value.\n * @param[in] sak SAK byte value.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso14443a_listener_set_col_res_data(\n    Nfc* instance,\n    uint8_t* uid,\n    uint8_t uid_len,\n    uint8_t* atqa,\n    uint8_t sak);\n\n/**\n * @brief Set FeliCa collision resolution parameters in listener mode.\n * \n * Configures the NFC hardware for automatic collision resolution.\n *\n * @param[in,out] instance pointer to the instance to be configured.\n * @param[in] idm pointer to a byte array containing the IDm.\n * @param[in] idm_len IDm length in bytes.\n * @param[in] pmm pointer to a byte array containing the PMm.\n * @param[in] pmm_len PMm length in bytes.\n * @param[in] sys_code System code from SYS_C block\n * @returns NfcErrorNone on success, any other error code on failure.\n*/\nNfcError nfc_felica_listener_set_sensf_res_data(\n    Nfc* instance,\n    const uint8_t* idm,\n    const uint8_t idm_len,\n    const uint8_t* pmm,\n    const uint8_t pmm_len,\n    const uint16_t sys_code);\n\n/**\n * @brief Send ISO15693 Start of Frame pattern in listener mode\n *\n * @param[in,out] instance pointer to the instance to be configured.\n * @returns NfcErrorNone on success, any other error code on failure.\n */\nNfcError nfc_iso15693_listener_tx_sof(Nfc* instance);\n\n/**\n * @brief Start the timer used for manual FeliCa collision resolution in listener mode.\n * \n * This blocks TX until the desired Time Slot, and should be called as soon as the listener\n * determines that a collision resolution needs to be handled manually.\n *\n * @param[in, out] instance instance pointer to the instance to be configured.\n * @param[in] target_time_slot Target Time Slot number. Should be a value within the range of 0-15 (double-inclusive).\n */\nvoid nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot);\n\n/**\n * @brief Cancel the timer used for manual FeliCa collision resolution in listener mode.\n * \n * @param[in, out] instance instance pointer to the instance to be configured.\n */\nvoid nfc_felica_listener_timer_anticol_stop(Nfc* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_common.h",
    "content": "/**\n * @file nfc_common.h\n * @brief Various common NFC-related macros.\n */\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* NFC file format version which changed ATQA format. Deprecated. */\n#define NFC_LSB_ATQA_FORMAT_VERSION          (2)\n/* NFC file format version which is still supported as backwards compatible. */\n#define NFC_MINIMUM_SUPPORTED_FORMAT_VERSION NFC_LSB_ATQA_FORMAT_VERSION\n/* NFC file format version which implemented the unified loading process. */\n#define NFC_UNIFIED_FORMAT_VERSION           (4)\n/* Current NFC file format version. */\n#define NFC_CURRENT_FORMAT_VERSION           NFC_UNIFIED_FORMAT_VERSION\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_device.c",
    "content": "#include \"nfc_device_i.h\"\n\n#include <storage/storage.h>\n#include <flipper_format/flipper_format.h>\n\n#include \"nfc_common.h\"\n#include \"protocols/nfc_device_defs.h\"\n\n#define NFC_FILE_HEADER    \"Flipper NFC device\"\n#define NFC_DEV_TYPE_ERROR \"Protocol type mismatch\"\n\n#define NFC_DEVICE_UID_KEY  \"UID\"\n#define NFC_DEVICE_TYPE_KEY \"Device type\"\n\n#define NFC_DEVICE_UID_MAX_LEN (10U)\n\nNfcDevice* nfc_device_alloc(void) {\n    NfcDevice* instance = malloc(sizeof(NfcDevice));\n    instance->protocol = NfcProtocolInvalid;\n\n    return instance;\n}\n\nvoid nfc_device_free(NfcDevice* instance) {\n    furi_check(instance);\n\n    nfc_device_clear(instance);\n    free(instance);\n}\n\nvoid nfc_device_clear(NfcDevice* instance) {\n    furi_check(instance);\n\n    if(instance->protocol == NfcProtocolInvalid) {\n        furi_assert(instance->protocol_data == NULL);\n    } else if(instance->protocol < NfcProtocolNum) {\n        if(instance->protocol_data) {\n            nfc_devices[instance->protocol]->free(instance->protocol_data);\n            instance->protocol_data = NULL;\n        }\n        instance->protocol = NfcProtocolInvalid;\n    }\n}\n\nvoid nfc_device_reset(NfcDevice* instance) {\n    furi_check(instance);\n    furi_check(instance->protocol < NfcProtocolNum);\n\n    if(instance->protocol_data) {\n        nfc_devices[instance->protocol]->reset(instance->protocol_data);\n    }\n}\n\nNfcProtocol nfc_device_get_protocol(const NfcDevice* instance) {\n    furi_check(instance);\n    return instance->protocol;\n}\n\nconst NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol) {\n    furi_check(instance);\n    return nfc_device_get_data_ptr(instance, protocol);\n}\n\nconst char* nfc_device_get_protocol_name(NfcProtocol protocol) {\n    furi_check(protocol < NfcProtocolNum);\n\n    return nfc_devices[protocol]->protocol_name;\n}\n\nconst char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type) {\n    furi_check(instance);\n    furi_check(instance->protocol < NfcProtocolNum);\n\n    return nfc_devices[instance->protocol]->get_name(instance->protocol_data, name_type);\n}\n\nconst uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len) {\n    furi_check(instance);\n    furi_check(uid_len);\n    furi_check(instance->protocol < NfcProtocolNum);\n\n    return nfc_devices[instance->protocol]->get_uid(instance->protocol_data, uid_len);\n}\n\nbool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len) {\n    furi_check(instance);\n    furi_check(uid);\n    furi_check(instance->protocol < NfcProtocolNum);\n\n    return nfc_devices[instance->protocol]->set_uid(instance->protocol_data, uid, uid_len);\n}\n\nvoid nfc_device_set_data(\n    NfcDevice* instance,\n    NfcProtocol protocol,\n    const NfcDeviceData* protocol_data) {\n    furi_check(instance);\n    furi_check(protocol_data);\n    furi_check(protocol < NfcProtocolNum);\n\n    nfc_device_clear(instance);\n\n    instance->protocol = protocol;\n    instance->protocol_data = nfc_devices[protocol]->alloc();\n\n    nfc_devices[protocol]->copy(instance->protocol_data, protocol_data);\n}\n\nvoid nfc_device_copy_data(\n    const NfcDevice* instance,\n    NfcProtocol protocol,\n    NfcDeviceData* protocol_data) {\n    furi_check(instance);\n    furi_check(protocol < NfcProtocolNum);\n    furi_check(protocol_data);\n\n    if(instance->protocol != protocol) {\n        furi_crash(NFC_DEV_TYPE_ERROR);\n    }\n\n    nfc_devices[protocol]->copy(protocol_data, instance->protocol_data);\n}\n\nbool nfc_device_is_equal_data(\n    const NfcDevice* instance,\n    NfcProtocol protocol,\n    const NfcDeviceData* protocol_data) {\n    furi_check(instance);\n    furi_check(protocol < NfcProtocolNum);\n    furi_check(protocol_data);\n\n    return instance->protocol == protocol &&\n           nfc_devices[protocol]->is_equal(instance->protocol_data, protocol_data);\n}\n\nbool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other) {\n    furi_check(instance);\n    furi_check(other);\n\n    return nfc_device_is_equal_data(instance, other->protocol, other->protocol_data);\n}\n\nvoid nfc_device_set_loading_callback(\n    NfcDevice* instance,\n    NfcLoadingCallback callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(callback);\n\n    instance->loading_callback = callback;\n    instance->loading_callback_context = context;\n}\n\nbool nfc_device_save(NfcDevice* instance, const char* path) {\n    furi_check(instance);\n    furi_check(instance->protocol < NfcProtocolNum);\n    furi_check(path);\n\n    bool saved = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n    FuriString* temp_str = furi_string_alloc();\n\n    if(instance->loading_callback) {\n        instance->loading_callback(instance->loading_callback_context, true);\n    }\n\n    do {\n        // Open file\n        if(!flipper_format_buffered_file_open_always(ff, path)) break;\n\n        // Write header\n        if(!flipper_format_write_header_cstr(ff, NFC_FILE_HEADER, NFC_CURRENT_FORMAT_VERSION))\n            break;\n\n        // Write allowed device types\n        furi_string_printf(temp_str, \"%s can be \", NFC_DEVICE_TYPE_KEY);\n        for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; ++protocol) {\n            furi_string_cat(temp_str, nfc_devices[protocol]->protocol_name);\n            if(protocol < NfcProtocolNum - 1) {\n                furi_string_cat(temp_str, \", \");\n            }\n        }\n\n        if(!flipper_format_write_comment(ff, temp_str)) break;\n\n        // Write device type\n        if(!flipper_format_write_string_cstr(\n               ff, NFC_DEVICE_TYPE_KEY, nfc_devices[instance->protocol]->protocol_name))\n            break;\n\n        // Write UID\n        furi_string_printf(temp_str, \"%s is common for all formats\", NFC_DEVICE_UID_KEY);\n        if(!flipper_format_write_comment(ff, temp_str)) break;\n\n        size_t uid_len;\n        const uint8_t* uid = nfc_device_get_uid(instance, &uid_len);\n        if(!flipper_format_write_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len)) break;\n\n        // Write protocol-dependent data\n        if(!nfc_devices[instance->protocol]->save(instance->protocol_data, ff)) break;\n\n        saved = true;\n    } while(false);\n\n    if(instance->loading_callback) {\n        instance->loading_callback(instance->loading_callback_context, false);\n    }\n\n    furi_string_free(temp_str);\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return saved;\n}\n\nstatic bool nfc_device_load_uid(\n    FlipperFormat* ff,\n    uint8_t* uid,\n    uint32_t* uid_len,\n    const uint32_t uid_maxlen) {\n    bool loaded = false;\n\n    do {\n        uint32_t uid_len_current;\n        if(!flipper_format_get_value_count(ff, NFC_DEVICE_UID_KEY, &uid_len_current)) break;\n        if(uid_len_current > uid_maxlen) break;\n        if(!flipper_format_read_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len_current)) break;\n\n        *uid_len = uid_len_current;\n        loaded = true;\n    } while(false);\n\n    return loaded;\n}\n\nstatic bool nfc_device_load_unified(NfcDevice* instance, FlipperFormat* ff, uint32_t version) {\n    bool loaded = false;\n\n    FuriString* temp_str = furi_string_alloc();\n\n    do {\n        // Read Nfc device type\n        if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break;\n\n        // Detect protocol\n        NfcProtocol protocol;\n        for(protocol = 0; protocol < NfcProtocolNum; ++protocol) {\n            if(furi_string_equal(temp_str, nfc_devices[protocol]->protocol_name)) {\n                break;\n            }\n        }\n\n        if(protocol == NfcProtocolNum) break;\n\n        nfc_device_clear(instance);\n\n        instance->protocol = protocol;\n        instance->protocol_data = nfc_devices[protocol]->alloc();\n\n        // Load UID\n        uint8_t uid[NFC_DEVICE_UID_MAX_LEN];\n        uint32_t uid_len;\n\n        if(!nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN)) break;\n        if(!nfc_device_set_uid(instance, uid, uid_len)) break;\n\n        // Load data\n        if(!nfc_devices[protocol]->load(instance->protocol_data, ff, version)) break;\n\n        loaded = true;\n    } while(false);\n\n    if(!loaded) {\n        nfc_device_clear(instance);\n    }\n\n    furi_string_free(temp_str);\n    return loaded;\n}\n\nstatic bool nfc_device_load_legacy(NfcDevice* instance, FlipperFormat* ff, uint32_t version) {\n    bool loaded = false;\n\n    FuriString* temp_str = furi_string_alloc();\n\n    do {\n        // Read Nfc device type\n        if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break;\n\n        nfc_device_clear(instance);\n\n        // Detect protocol\n        for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; protocol++) {\n            instance->protocol = protocol;\n            instance->protocol_data = nfc_devices[protocol]->alloc();\n\n            // Verify protocol\n            if(nfc_devices[protocol]->verify(instance->protocol_data, temp_str)) {\n                uint8_t uid[NFC_DEVICE_UID_MAX_LEN];\n                uint32_t uid_len;\n\n                // Load data\n                loaded = nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN) &&\n                         nfc_device_set_uid(instance, uid, uid_len) &&\n                         nfc_devices[protocol]->load(instance->protocol_data, ff, version);\n                break;\n            }\n\n            nfc_device_clear(instance);\n        }\n\n    } while(false);\n\n    furi_string_free(temp_str);\n    return loaded;\n}\n\nbool nfc_device_load(NfcDevice* instance, const char* path) {\n    furi_check(instance);\n    furi_check(path);\n\n    bool loaded = false;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);\n\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n\n    if(instance->loading_callback) {\n        instance->loading_callback(instance->loading_callback_context, true);\n    }\n\n    do {\n        if(!flipper_format_buffered_file_open_existing(ff, path)) break;\n\n        // Read and verify file header\n        uint32_t version = 0;\n        if(!flipper_format_read_header(ff, temp_str, &version)) break;\n\n        if(furi_string_cmp_str(temp_str, NFC_FILE_HEADER)) break;\n        if(version < NFC_MINIMUM_SUPPORTED_FORMAT_VERSION) break;\n\n        // Select loading method\n        loaded = (version < NFC_UNIFIED_FORMAT_VERSION) ?\n                     nfc_device_load_legacy(instance, ff, version) :\n                     nfc_device_load_unified(instance, ff, version);\n\n    } while(false);\n\n    if(instance->loading_callback) {\n        instance->loading_callback(instance->loading_callback_context, false);\n    }\n\n    furi_string_free(temp_str);\n    flipper_format_free(ff);\n    furi_record_close(RECORD_STORAGE);\n\n    return loaded;\n}\n"
  },
  {
    "path": "lib/nfc/nfc_device.h",
    "content": "/**\n * @file nfc_device.h\n * @brief Abstract interface for managing NFC device data.\n *\n * Under the hood, it makes use of the protocol-specific functions that each one of them provides\n * and abstracts it with a protocol-independent API.\n *\n * It does not perform any signal processing, but merely serves as a container with some handy\n * operations such as loading and saving from and to a file.\n */\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"protocols/nfc_device_base.h\"\n#include \"protocols/nfc_protocol.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcDevice opaque type definition.\n */\ntypedef struct NfcDevice NfcDevice;\n\n/**\n * @brief Loading callback function signature.\n *\n * A function with such signature can be set as a callback to indicate\n * the completion (or a failure) of nfc_device_load() and nfc_device_save() functions.\n *\n * This facility is commonly used to control GUI elements, such as progress dialogs.\n *\n * @param[in] context user-defined context that was passed in nfc_device_set_loading_callback().\n * @param[in] state true if the data was loaded successfully, false otherwise.\n */\ntypedef void (*NfcLoadingCallback)(void* context, bool state);\n\n/**\n * @brief Allocate an NfcDevice instance.\n *\n * A newly created instance does not hold any data and thus is considered invalid. The most common\n * use case would be to set its data by calling nfc_device_set_data() right afterwards.\n *\n * @returns pointer to the allocated instance.\n */\nNfcDevice* nfc_device_alloc(void);\n\n/**\n * @brief Delete an NfcDevice instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid nfc_device_free(NfcDevice* instance);\n\n/**\n * @brief Clear an NfcDevice instance.\n *\n * All data contained in the instance will be deleted and the instance itself will become invalid\n * as if it was just allocated.\n *\n * @param[in,out] instance pointer to the instance to be cleared.\n */\nvoid nfc_device_clear(NfcDevice* instance);\n\n/**\n * @brief Reset an NfcDevice instance.\n *\n * The data contained in the instance will be reset according to the protocol-defined procedure.\n * Unlike the nfc_device_clear() function, the instance will remain valid.\n *\n * @param[in,out] instance pointer to the instance to be reset.\n */\nvoid nfc_device_reset(NfcDevice* instance);\n\n/**\n * @brief Get the protocol identifier from an NfcDevice instance.\n *\n * If the instance is invalid, the return value will be NfcProtocolInvalid.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @returns protocol identifier contained in the instance.\n */\nNfcProtocol nfc_device_get_protocol(const NfcDevice* instance);\n\n/**\n * @brief Get the protocol-specific data from an NfcDevice instance.\n *\n * The protocol parameter's behaviour is a bit tricky. The function will check\n * whether there is such a protocol somewhere in the protocol hierarchy and return\n * the data exactly from that level.\n *\n * Example: Call nfc_device_get_data() on an instance with Mf DESFire protocol.\n * The protocol hierarchy will look like the following:\n *\n * `Mf DESFire --> ISO14443-4A --> ISO14443-3A`\n *\n * Thus, the following values of the protocol parameter are valid:\n *\n *  * NfcProtocolIso14443_3a\n *  * NfcProtocolIso14443_4a\n *  * NfcProtocolMfDesfire\n *\n * and passing them to the call would result in the respective data being returned.\n *\n * However, supplying a protocol identifier which is not in the hierarchy will\n * result in a crash. This is to improve type safety.\n *\n * @param instance pointer to the instance to be queried\n * @param protocol protocol identifier of the data to be retrieved.\n * @returns pointer to the instance's data.\n */\nconst NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol);\n\n/**\n * @brief Get the protocol name by its identifier.\n *\n * This function does not require an instance as its return result depends only\n * the protocol identifier.\n *\n * @param[in] protocol numeric identifier of the protocol in question.\n * @returns pointer to a statically allocated string containing the protocol name.\n */\nconst char* nfc_device_get_protocol_name(NfcProtocol protocol);\n\n/**\n * @brief Get the name of an NfcDevice instance.\n *\n * The return value may change depending on the instance's internal state and the name_type parameter.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @param[in] name_type type of the name to be displayed.\n * @returns pointer to a statically allocated string containing the device name.\n */\nconst char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type);\n\n/**\n * @brief Get the unique identifier (UID) of an NfcDevice instance.\n *\n * The UID length is protocol-dependent. Additionally, a particular protocol might support\n * several UID lengths.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @param[out] uid_len pointer to the variable to contain the UID length.\n * @returns pointer to the byte array containing the instance's UID.\n */\nconst uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len);\n\n/**\n * @brief Set the unique identifier (UID) of an NfcDevice instance.\n *\n * The UID length must be supported by the instance's protocol.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] uid pointer to the byte array containing the new UID.\n * @param[in] uid_len length of the UID.\n * @return true if the UID was valid and set, false otherwise.\n */\nbool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len);\n\n/**\n * @brief Set the data and protocol of an NfcDevice instance.\n *\n * Any data previously contained in the instance will be deleted.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] protocol numeric identifier of the data's protocol.\n * @param[in] protocol_data pointer to the protocol-specific data.\n */\nvoid nfc_device_set_data(\n    NfcDevice* instance,\n    NfcProtocol protocol,\n    const NfcDeviceData* protocol_data);\n\n/**\n * @brief Copy (export) the data contained in an NfcDevice instance to an outside NfcDeviceData instance.\n *\n * This function does the inverse of nfc_device_set_data().\n\n * The protocol identifier passed as the protocol parameter MUST match the one\n * stored in the instance, otherwise a crash will occur.\n * This is to improve type safety.\n *\n * @param[in] instance pointer to the instance to be copied from.\n * @param[in] protocol numeric identifier of the instance's protocol.\n * @param[out] protocol_data pointer to the destination data.\n */\nvoid nfc_device_copy_data(\n    const NfcDevice* instance,\n    NfcProtocol protocol,\n    NfcDeviceData* protocol_data);\n\n/**\n * @brief Check whether an NfcDevice instance holds certain data.\n *\n * This function's behaviour is similar to nfc_device_is_equal(), with the difference\n * that it takes NfcProtocol and NfcDeviceData* instead of the second NfcDevice*.\n *\n * The following code snippets [1] and [2] are equivalent:\n *\n * [1]\n * ```c\n * bool is_equal = nfc_device_is_equal(device1, device2);\n * ```\n * [2]\n * ```c\n * NfcProtocol protocol = nfc_device_get_protocol(device2);\n * const NfcDeviceData* data = nfc_device_get_data(device2, protocol);\n * bool is_equal = nfc_device_is_equal_data(device1, protocol, data);\n * ```\n *\n * @param[in] instance pointer to the instance to be compared.\n * @param[in] protocol protocol identifier of the data to be compared.\n * @param[in] protocol_data pointer to the NFC device data to be compared.\n * @returns true if the instance is of the right type and the data matches, false otherwise.\n */\nbool nfc_device_is_equal_data(\n    const NfcDevice* instance,\n    NfcProtocol protocol,\n    const NfcDeviceData* protocol_data);\n\n/**\n * @brief Compare two NfcDevice instances to determine whether they are equal.\n *\n * @param[in] instance pointer to the first instance to be compared.\n * @param[in] other pointer to the second instance to be compared.\n * @returns true if both instances are considered equal, false otherwise.\n */\nbool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other);\n\n/**\n * @brief Set the loading callback function.\n *\n * @param[in,out] instance pointer to the instance to be modified.\n * @param[in] callback pointer to a function to be called when the load operation completes.\n * @param[in] context pointer to a user-specific context (will be passed to the callback).\n */\nvoid nfc_device_set_loading_callback(\n    NfcDevice* instance,\n    NfcLoadingCallback callback,\n    void* context);\n\n/**\n * @brief Save NFC device data form an NfcDevice instance to a file.\n *\n * @param[in] instance pointer to the instance to be saved.\n * @param[in] path pointer to a character string with a full file path.\n * @returns true if the data was successfully saved, false otherwise.\n */\nbool nfc_device_save(NfcDevice* instance, const char* path);\n\n/**\n * @brief Load NFC device data to an NfcDevice instance from a file.\n *\n * @param[in,out] instance pointer to the instance to be loaded into.\n * @param[in] path pointer to a character string with a full file path.\n * @returns true if the data was successfully loaded, false otherwise.\n */\nbool nfc_device_load(NfcDevice* instance, const char* path);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_device_i.c",
    "content": "#include \"nfc_device_i.h\"\n#include \"protocols/nfc_device_defs.h\"\n\n#include <furi/furi.h>\n\nstatic NfcDeviceData*\n    nfc_device_search_base_protocol_data(const NfcDevice* instance, NfcProtocol protocol) {\n    NfcProtocol protocol_tmp = instance->protocol;\n    NfcDeviceData* dev_data_tmp = instance->protocol_data;\n\n    while(true) {\n        dev_data_tmp = nfc_devices[protocol_tmp]->get_base_data(dev_data_tmp);\n        protocol_tmp = nfc_protocol_get_parent(protocol_tmp);\n        if(protocol_tmp == protocol) {\n            break;\n        }\n    }\n\n    return dev_data_tmp;\n}\n\nNfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol) {\n    furi_assert(instance);\n    furi_assert(protocol < NfcProtocolNum);\n\n    NfcDeviceData* dev_data = NULL;\n\n    if(instance->protocol == protocol) {\n        dev_data = instance->protocol_data;\n    } else if(nfc_protocol_has_parent(instance->protocol, protocol)) {\n        dev_data = nfc_device_search_base_protocol_data(instance, protocol);\n    } else {\n        furi_crash(\"Incorrect protocol\");\n    }\n\n    return dev_data;\n}\n"
  },
  {
    "path": "lib/nfc/nfc_device_i.h",
    "content": "/**\n * @file nfc_device_i.h\n * @brief NfcDevice private types and definitions.\n *\n * This file is an implementation detail. It must not be included in\n * any public API-related headers.\n */\n#pragma once\n\n#include \"nfc_device.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcDevice structure definition.\n */\nstruct NfcDevice {\n    NfcProtocol protocol; /**< Numeric identifier of the data's protocol*/\n    NfcDeviceData* protocol_data; /**< Pointer to the NFC device data. */\n\n    NfcLoadingCallback\n        loading_callback; /**< Pointer to the function to be called upon loading completion. */\n    void* loading_callback_context; /**< Pointer to the context to be passed to the loading callback. */\n};\n\n/**\n * @brief Get the mutable (non-const) data from an NfcDevice instance.\n *\n * The behaviour is the same as with nfc_device_get_data(), but the\n * return pointer is non-const, allowing for changing data it is pointing to.\n *\n * @see nfc_device.h\n *\n * Under the hood, nfc_device_get_data() calls this and then adds const-ness to the return value.\n *\n * @param instance pointer to the instance to be queried\n * @param protocol protocol identifier of the data to be retrieved.\n * @returns pointer to the instance's (mutable) data.\n */\nNfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_listener.c",
    "content": "#include \"nfc_listener.h\"\n\n#include <nfc/protocols/nfc_listener_defs.h>\n#include <nfc/nfc_device_i.h>\n\n#include <furi.h>\n\ntypedef struct NfcListenerListElement {\n    NfcProtocol protocol;\n    NfcGenericInstance* listener;\n    const NfcListenerBase* listener_api;\n    struct NfcListenerListElement* child;\n} NfcListenerListElement;\n\ntypedef struct {\n    NfcListenerListElement* head;\n    NfcListenerListElement* tail;\n} NfcListenerList;\n\nstruct NfcListener {\n    NfcProtocol protocol;\n    Nfc* nfc;\n    NfcListenerList list;\n    NfcDevice* nfc_dev;\n};\n\nstatic void nfc_listener_list_alloc(NfcListener* instance) {\n    instance->list.head = malloc(sizeof(NfcListenerListElement));\n    instance->list.head->protocol = instance->protocol;\n\n    instance->list.head->listener_api = nfc_listeners_api[instance->protocol];\n    instance->list.head->child = NULL;\n    instance->list.tail = instance->list.head;\n\n    // Build linked list\n    do {\n        NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol);\n        if(parent_protocol == NfcProtocolInvalid) break;\n\n        NfcListenerListElement* parent = malloc(sizeof(NfcListenerListElement));\n        parent->protocol = parent_protocol;\n        parent->listener_api = nfc_listeners_api[parent_protocol];\n        parent->child = instance->list.head;\n\n        instance->list.head = parent;\n    } while(true);\n\n    // Allocate listener instances\n    NfcListenerListElement* iter = instance->list.head;\n    NfcDeviceData* data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->protocol);\n    iter->listener = iter->listener_api->alloc(instance->nfc, data_tmp);\n\n    do {\n        if(iter->child == NULL) break;\n        data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->child->protocol);\n        iter->child->listener = iter->child->listener_api->alloc(iter->listener, data_tmp);\n        iter->listener_api->set_callback(\n            iter->listener, iter->child->listener_api->run, iter->child->listener);\n\n        iter = iter->child;\n    } while(true);\n}\n\nstatic void nfc_listener_list_free(NfcListener* instance) {\n    // Free listener instances\n    do {\n        instance->list.head->listener_api->free(instance->list.head->listener);\n        NfcListenerListElement* child = instance->list.head->child;\n        free(instance->list.head);\n        if(child == NULL) break;\n        instance->list.head = child;\n    } while(true);\n}\n\nNfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data) {\n    furi_check(nfc);\n    furi_check(protocol < NfcProtocolNum);\n    furi_check(data);\n    furi_check(nfc_listeners_api[protocol]);\n\n    NfcListener* instance = malloc(sizeof(NfcListener));\n    instance->nfc = nfc;\n    instance->protocol = protocol;\n    instance->nfc_dev = nfc_device_alloc();\n    nfc_device_set_data(instance->nfc_dev, protocol, data);\n    nfc_listener_list_alloc(instance);\n\n    return instance;\n}\n\nvoid nfc_listener_free(NfcListener* instance) {\n    furi_check(instance);\n\n    nfc_listener_list_free(instance);\n    nfc_device_free(instance->nfc_dev);\n    free(instance);\n}\n\nNfcCommand nfc_listener_start_callback(NfcEvent event, void* context) {\n    furi_assert(context);\n\n    NfcListener* instance = context;\n    furi_assert(instance->list.head);\n\n    NfcCommand command = NfcCommandContinue;\n    NfcGenericEvent generic_event = {\n        .protocol = NfcProtocolInvalid,\n        .instance = instance->nfc,\n        .event_data = &event,\n    };\n\n    NfcListenerListElement* head_listener = instance->list.head;\n    command = head_listener->listener_api->run(generic_event, head_listener->listener);\n\n    return command;\n}\n\nvoid nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context) {\n    furi_check(instance);\n\n    NfcListenerListElement* tail_element = instance->list.tail;\n    tail_element->listener_api->set_callback(tail_element->listener, callback, context);\n    nfc_start(instance->nfc, nfc_listener_start_callback, instance);\n}\n\nvoid nfc_listener_stop(NfcListener* instance) {\n    furi_check(instance);\n\n    nfc_stop(instance->nfc);\n}\n\nNfcProtocol nfc_listener_get_protocol(const NfcListener* instance) {\n    furi_check(instance);\n\n    return instance->protocol;\n}\n\nconst NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol) {\n    furi_check(instance);\n    furi_check(instance->protocol == protocol);\n\n    NfcListenerListElement* tail_element = instance->list.tail;\n    return tail_element->listener_api->get_data(tail_element->listener);\n}\n"
  },
  {
    "path": "lib/nfc/nfc_listener.h",
    "content": "/**\n * @file nfc_listener.h\n * @brief NFC card emulation library.\n *\n * Once started, it will respond to supported commands from an NFC reader, thus imitating\n * (or emulating) an NFC card. The responses will depend on the data that was supplied to\n * the listener, so various card types and different cards of the same type can be emulated.\n *\n * It will also make any changes necessary to the emulated data in response to the\n * reader commands if the protocol supports it.\n *\n * When running, NfcListener will generate events that the calling code must handle\n * by providing a callback function. The events passed to the callback are protocol-specific\n * and may include errors, state changes, data reception, special function requests and more.\n */\n#pragma once\n\n#include <nfc/protocols/nfc_generic_event.h>\n#include <nfc/protocols/nfc_device_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcListener opaque type definition.\n */\ntypedef struct NfcListener NfcListener;\n\n/**\n * @brief Allocate an NfcListener instance.\n *\n * @param[in] nfc pointer to an Nfc instance.\n * @param[in] protocol identifier of the protocol to be used.\n * @param[in] data pointer to the data to use during emulation.\n * @returns pointer to an allocated instance.\n *\n * @see nfc.h\n */\nNfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data);\n\n/**\n * @brief Delete an NfcListener instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid nfc_listener_free(NfcListener* instance);\n\n/**\n * @brief Start an NfcListener instance.\n *\n * The callback logic is protocol-specific, so it cannot be described here in detail.\n * However, the callback return value ALWAYS determines what the listener should do next:\n * to continue whatever it was doing prior to the callback run or to stop.\n *\n * @param[in,out] instance pointer to the instance to be started.\n * @param[in] callback pointer to a user-defined callback function which will receive events.\n * @param[in] context pointer to a user-specific context (will be passed to the callback).\n */\nvoid nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context);\n\n/**\n * @brief Stop an NfcListener instance.\n *\n * The emulation process can be stopped explicitly (the other way is via the callback return value).\n *\n * @param[in,out] instance pointer to the instance to be stopped.\n */\nvoid nfc_listener_stop(NfcListener* instance);\n\n/**\n * @brief Get the protocol identifier an NfcListener instance was created with.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @returns identifier of the protocol used by the instance.\n */\nNfcProtocol nfc_listener_get_protocol(const NfcListener* instance);\n\n/**\n * @brief Get the data that was that was provided for emulation.\n *\n * The protocol identifier passed as the protocol parameter MUST match the one\n * stored in the instance, otherwise a crash will occur.\n * This is to improve type safety.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @param[in] protocol assumed protocol identifier of the data to be retrieved.\n * @returns pointer to the NFC device data.\n */\nconst NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_mock.c",
    "content": "#ifdef FW_CFG_unit_tests\n\n#include <lib/nfc/nfc.h>\n#include <lib/nfc/helpers/iso14443_crc.h>\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <lib/nfc/protocols/felica/felica.h>\n#include <lib/nfc/helpers/felica_crc.h>\n#include <lib/nfc/protocols/felica/felica_poller_sync.h>\n\n#include <furi/furi.h>\n\n#define NFC_MAX_BUFFER_SIZE (256)\n\ntypedef enum {\n    NfcTransportLogLevelWarning,\n    NfcTransportLogLevelInfo,\n} NfcTransportLogLevel;\n\nFuriMessageQueue* poller_queue = NULL;\nFuriMessageQueue* listener_queue = NULL;\n\ntypedef enum {\n    NfcMessageTypeTx,\n    NfcMessageTypeTimeout,\n    NfcMessageTypeAbort,\n} NfcMessageType;\n\ntypedef struct {\n    uint16_t data_bits;\n    uint8_t data[NFC_MAX_BUFFER_SIZE];\n} NfcMessageData;\n\ntypedef struct {\n    NfcMessageType type;\n    NfcMessageData data;\n} NfcMessage;\n\ntypedef enum {\n    NfcStateIdle,\n    NfcStateReady,\n    NfcStateReset,\n} NfcState;\n\ntypedef enum {\n    Iso14443_3aColResStatusIdle,\n    Iso14443_3aColResStatusInProgress,\n    Iso14443_3aColResStatusDone,\n} Iso14443_3aColResStatus;\n\ntypedef struct {\n    Iso14443_3aSensResp sens_resp;\n    Iso14443_3aSddResp sdd_resp[2];\n    Iso14443_3aSelResp sel_resp[2];\n} Iso14443_3aColResData;\n\ntypedef struct {\n    uint8_t length;\n    uint8_t polling_cmd;\n    uint16_t system_code;\n    uint8_t request_code;\n    uint8_t time_slot;\n} FelicaPollingRequest;\n\ntypedef struct {\n    uint8_t code;\n    FelicaIDm idm;\n    FelicaPMm pmm;\n} FelicaSensfResData;\n\ntypedef struct {\n    uint16_t system_code;\n    FelicaSensfResData sens_res;\n} FelicaPTMemory;\n\nstruct Nfc {\n    NfcState state;\n\n    Iso14443_3aColResStatus col_res_status;\n    Iso14443_3aColResData col_res_data;\n    FelicaPTMemory pt_memory;\n    bool software_col_res_required;\n\n    NfcEventCallback callback;\n    void* context;\n\n    NfcMode mode;\n\n    FuriThread* worker_thread;\n};\n\nstatic void nfc_test_print(\n    NfcTransportLogLevel log_level,\n    const char* message,\n    uint8_t* buffer,\n    uint16_t bits) {\n    FuriString* str = furi_string_alloc();\n    size_t bytes = (bits + 7) / 8;\n\n    for(size_t i = 0; i < bytes; i++) {\n        furi_string_cat_printf(str, \" %02X\", buffer[i]);\n    }\n    if(log_level == NfcTransportLogLevelWarning) {\n        FURI_LOG_W(message, \"%s\", furi_string_get_cstr(str));\n    } else {\n        FURI_LOG_I(message, \"%s\", furi_string_get_cstr(str));\n    }\n\n    furi_string_free(str);\n}\n\nstatic void nfc_prepare_col_res_data(\n    Nfc* instance,\n    uint8_t* uid,\n    uint8_t uid_len,\n    uint8_t* atqa,\n    uint8_t sak) {\n    memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2);\n\n    if(uid_len == 7) {\n        instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88;\n        memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3);\n        uint8_t bss = 0;\n        for(size_t i = 0; i < 4; i++) {\n            bss ^= instance->col_res_data.sdd_resp[0].nfcid[i];\n        }\n        instance->col_res_data.sdd_resp[0].bss = bss;\n        instance->col_res_data.sel_resp[0].sak = 0x04;\n\n        memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4);\n        bss = 0;\n        for(size_t i = 0; i < 4; i++) {\n            bss ^= instance->col_res_data.sdd_resp[1].nfcid[i];\n        }\n        instance->col_res_data.sdd_resp[1].bss = bss;\n        instance->col_res_data.sel_resp[1].sak = sak;\n\n    } else {\n        furi_crash(\"Not supporting not 7 bytes\");\n    }\n}\n\nNfc* nfc_alloc(void) {\n    Nfc* instance = malloc(sizeof(Nfc));\n\n    return instance;\n}\n\nvoid nfc_free(Nfc* instance) {\n    furi_check(instance);\n\n    free(instance);\n}\n\nvoid nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {\n    UNUSED(instance);\n    UNUSED(tech);\n\n    instance->mode = mode;\n}\n\nvoid nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {\n    UNUSED(instance);\n    UNUSED(fdt_poll_fc);\n}\n\nvoid nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {\n    UNUSED(instance);\n    UNUSED(fdt_listen_fc);\n}\n\nvoid nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {\n    UNUSED(instance);\n    UNUSED(mask_rx_time_fc);\n}\n\nvoid nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {\n    UNUSED(instance);\n    UNUSED(fdt_poll_poll_us);\n}\n\nvoid nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {\n    UNUSED(instance);\n    UNUSED(guard_time_us);\n}\n\nNfcError nfc_iso14443a_listener_set_col_res_data(\n    Nfc* instance,\n    uint8_t* uid,\n    uint8_t uid_len,\n    uint8_t* atqa,\n    uint8_t sak) {\n    furi_check(instance);\n    furi_check(uid);\n    furi_check(atqa);\n\n    nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);\n    instance->software_col_res_required = true;\n\n    return NfcErrorNone;\n}\n\nstatic int32_t nfc_worker_poller(void* context) {\n    Nfc* instance = context;\n    furi_check(instance->callback);\n\n    instance->state = NfcStateReady;\n    NfcCommand command = NfcCommandContinue;\n    NfcEvent event = {};\n\n    while(true) {\n        event.type = NfcEventTypePollerReady;\n        command = instance->callback(event, instance->context);\n        if(command == NfcCommandStop) {\n            break;\n        }\n    }\n\n    instance->state = NfcStateIdle;\n\n    return 0;\n}\n\nstatic void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {\n    furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);\n    BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);\n\n    bool processed = false;\n\n    if((rx_bits == 7) && (rx_data[0] == 0x52)) {\n        instance->col_res_status = Iso14443_3aColResStatusInProgress;\n        bit_buffer_copy_bytes(\n            tx_buffer,\n            instance->col_res_data.sens_resp.sens_resp,\n            sizeof(instance->col_res_data.sens_resp.sens_resp));\n        nfc_listener_tx(instance, tx_buffer);\n        processed = true;\n    } else if(rx_bits == 2 * 8) {\n        if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) {\n            bit_buffer_copy_bytes(\n                tx_buffer,\n                (const uint8_t*)&instance->col_res_data.sdd_resp[0],\n                sizeof(Iso14443_3aSddResp));\n            nfc_listener_tx(instance, tx_buffer);\n            processed = true;\n        } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) {\n            bit_buffer_copy_bytes(\n                tx_buffer,\n                (const uint8_t*)&instance->col_res_data.sdd_resp[1],\n                sizeof(Iso14443_3aSddResp));\n            nfc_listener_tx(instance, tx_buffer);\n            processed = true;\n        }\n    } else if(rx_bits == 9 * 8) {\n        if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) {\n            bit_buffer_set_size_bytes(tx_buffer, 1);\n            bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak);\n            iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);\n            nfc_listener_tx(instance, tx_buffer);\n            processed = true;\n        } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) {\n            bit_buffer_set_size_bytes(tx_buffer, 1);\n            bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak);\n            iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);\n            nfc_listener_tx(instance, tx_buffer);\n            instance->col_res_status = Iso14443_3aColResStatusDone;\n            NfcEvent event = {.type = NfcEventTypeListenerActivated};\n            instance->callback(event, instance->context);\n\n            processed = true;\n        }\n    } else if(rx_bits == 8 * 8) {\n        FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data;\n        if(request->system_code == 0xFFFF ||\n           request->system_code == instance->pt_memory.system_code) {\n            uint8_t response_size = sizeof(FelicaSensfResData) + 1;\n            bit_buffer_reset(tx_buffer);\n            bit_buffer_append_byte(tx_buffer, response_size);\n            bit_buffer_append_bytes(\n                tx_buffer, (uint8_t*)&instance->pt_memory.sens_res, sizeof(FelicaSensfResData));\n            felica_crc_append(tx_buffer);\n            nfc_listener_tx(instance, tx_buffer);\n            instance->col_res_status = Iso14443_3aColResStatusDone;\n            NfcEvent event = {.type = NfcEventTypeListenerActivated};\n            instance->callback(event, instance->context);\n            processed = true;\n        }\n    }\n\n    if(!processed) {\n        NfcMessage message = {.type = NfcMessageTypeTimeout};\n        furi_message_queue_put(poller_queue, &message, FuriWaitForever);\n    }\n\n    bit_buffer_free(tx_buffer);\n}\n\nstatic int32_t nfc_worker_listener(void* context) {\n    Nfc* instance = context;\n    furi_check(instance->callback);\n\n    NfcMessage message = {};\n\n    NfcEventData event_data = {};\n    event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);\n    NfcEvent nfc_event = {.data = event_data};\n\n    while(true) {\n        furi_message_queue_get(listener_queue, &message, FuriWaitForever);\n        bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits);\n        if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) {\n            instance->col_res_status = Iso14443_3aColResStatusIdle;\n        }\n\n        if(message.type == NfcMessageTypeAbort) {\n            break;\n        } else if(message.type == NfcMessageTypeTx) {\n            nfc_test_print(\n                NfcTransportLogLevelInfo, \"RDR\", message.data.data, message.data.data_bits);\n            if(instance->software_col_res_required &&\n               (instance->col_res_status != Iso14443_3aColResStatusDone)) {\n                nfc_worker_listener_pass_col_res(\n                    instance, message.data.data, message.data.data_bits);\n            } else {\n                instance->state = NfcStateReady;\n                nfc_event.type = NfcEventTypeRxEnd;\n                instance->callback(nfc_event, instance->context);\n            }\n        }\n    }\n\n    instance->state = NfcStateIdle;\n    instance->col_res_status = Iso14443_3aColResStatusIdle;\n    memset(&instance->col_res_data, 0, sizeof(instance->col_res_data));\n    bit_buffer_free(nfc_event.data.buffer);\n\n    return 0;\n}\n\nvoid nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {\n    furi_check(instance);\n    furi_check(instance->worker_thread == NULL);\n\n    if(instance->mode == NfcModeListener) {\n        furi_check(listener_queue == NULL);\n        // Check that poller didn't start\n        furi_check(poller_queue == NULL);\n    } else {\n        furi_check(poller_queue == NULL);\n        // Check that poller is started after listener\n        furi_check(listener_queue);\n    }\n\n    instance->callback = callback;\n    instance->context = context;\n\n    if(instance->mode == NfcModeListener) {\n        listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));\n    } else {\n        poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));\n    }\n\n    instance->worker_thread = furi_thread_alloc();\n    furi_thread_set_context(instance->worker_thread, instance);\n    furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh);\n    furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);\n\n    if(instance->mode == NfcModeListener) {\n        furi_thread_set_name(instance->worker_thread, \"NfcWorkerListener\");\n        furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);\n    } else {\n        furi_thread_set_name(instance->worker_thread, \"NfcWorkerPoller\");\n        furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);\n    }\n\n    furi_thread_start(instance->worker_thread);\n}\n\nvoid nfc_stop(Nfc* instance) {\n    furi_check(instance);\n    furi_check(instance->worker_thread);\n\n    if(instance->mode == NfcModeListener) {\n        NfcMessage message = {.type = NfcMessageTypeAbort};\n        furi_message_queue_put(listener_queue, &message, FuriWaitForever);\n        furi_thread_join(instance->worker_thread);\n\n        furi_message_queue_free(listener_queue);\n        listener_queue = NULL;\n\n        furi_thread_free(instance->worker_thread);\n        instance->worker_thread = NULL;\n    } else {\n        furi_thread_join(instance->worker_thread);\n\n        furi_message_queue_free(poller_queue);\n        poller_queue = NULL;\n\n        furi_thread_free(instance->worker_thread);\n        instance->worker_thread = NULL;\n    }\n}\n\n// Called from worker thread\n\nNfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {\n    furi_check(instance);\n    furi_check(poller_queue);\n    furi_check(listener_queue);\n    furi_check(tx_buffer);\n\n    NfcMessage message = {};\n    message.type = NfcMessageTypeTx;\n    message.data.data_bits = bit_buffer_get_size(tx_buffer);\n    bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));\n\n    furi_message_queue_put(poller_queue, &message, FuriWaitForever);\n\n    return NfcErrorNone;\n}\n\nNfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {\n    return nfc_listener_tx(instance, tx_buffer);\n}\n\nNfcError\n    nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n    furi_check(poller_queue);\n    furi_check(listener_queue);\n    UNUSED(fwt);\n\n    NfcError error = NfcErrorNone;\n\n    NfcMessage message = {};\n    message.type = NfcMessageTypeTx;\n    message.data.data_bits = bit_buffer_get_size(tx_buffer);\n    bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));\n    // Tx\n    furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);\n    // Rx\n    FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);\n\n    if(status == FuriStatusErrorTimeout) {\n        error = NfcErrorTimeout;\n    } else if(message.type == NfcMessageTypeTx) {\n        bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits);\n        nfc_test_print(\n            NfcTransportLogLevelWarning, \"TAG\", message.data.data, message.data.data_bits);\n    } else if(message.type == NfcMessageTypeTimeout) {\n        error = NfcErrorTimeout;\n    }\n\n    return error;\n}\n\nNfcError nfc_iso14443a_poller_trx_custom_parity(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);\n}\n\n// Technology specific API\n\nNfcError nfc_iso14443a_poller_trx_short_frame(\n    Nfc* instance,\n    NfcIso14443aShortFrame frame,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    UNUSED(frame);\n\n    BitBuffer* tx_buffer = bit_buffer_alloc(32);\n    bit_buffer_set_size(tx_buffer, 7);\n    bit_buffer_set_byte(tx_buffer, 0, 0x52);\n\n    NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);\n\n    bit_buffer_free(tx_buffer);\n\n    return error;\n}\n\nNfcError nfc_iso14443a_poller_trx_sdd_frame(\n    Nfc* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);\n}\n\nNfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {\n    UNUSED(instance);\n\n    return NfcErrorNone;\n}\n\nNfcError nfc_felica_listener_set_sensf_res_data(\n    Nfc* instance,\n    const uint8_t* idm,\n    const uint8_t idm_len,\n    const uint8_t* pmm,\n    const uint8_t pmm_len,\n    const uint16_t sys_code) {\n    furi_assert(instance);\n    furi_assert(idm);\n    furi_assert(pmm);\n    furi_assert(idm_len == 8);\n    furi_assert(pmm_len == 8);\n\n    instance->pt_memory.system_code = sys_code;\n    instance->pt_memory.sens_res.code = 0x01;\n    instance->software_col_res_required = true;\n    memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len);\n    memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len);\n    return NfcErrorNone;\n}\n\nvoid nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {\n    furi_check(instance);\n\n    UNUSED(target_time_slot);\n}\n\nvoid nfc_felica_listener_timer_anticol_stop(Nfc* instance) {\n    furi_check(instance);\n}\n\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_poller.c",
    "content": "#include \"nfc_poller.h\"\n\n#include <nfc/protocols/nfc_poller_defs.h>\n\n#include <furi.h>\n\ntypedef enum {\n    NfcPollerSessionStateIdle,\n    NfcPollerSessionStateActive,\n    NfcPollerSessionStateStopRequest,\n} NfcPollerSessionState;\n\ntypedef struct NfcPollerListElement {\n    NfcProtocol protocol;\n    NfcGenericInstance* poller;\n    const NfcPollerBase* poller_api;\n    struct NfcPollerListElement* child;\n} NfcPollerListElement;\n\ntypedef struct {\n    NfcPollerListElement* head;\n    NfcPollerListElement* tail;\n} NfcPollerList;\n\nstruct NfcPoller {\n    NfcProtocol protocol;\n    Nfc* nfc;\n    NfcPollerList list;\n    NfcPollerSessionState session_state;\n    bool protocol_detected;\n\n    NfcGenericCallbackEx callback;\n    void* context;\n};\n\nstatic void nfc_poller_list_alloc(NfcPoller* instance) {\n    instance->list.head = malloc(sizeof(NfcPollerListElement));\n    instance->list.head->protocol = instance->protocol;\n    instance->list.head->poller_api = nfc_pollers_api[instance->protocol];\n    instance->list.head->child = NULL;\n    instance->list.tail = instance->list.head;\n\n    do {\n        NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol);\n        if(parent_protocol == NfcProtocolInvalid) break;\n\n        NfcPollerListElement* parent = malloc(sizeof(NfcPollerListElement));\n        parent->protocol = parent_protocol;\n        parent->poller_api = nfc_pollers_api[parent_protocol];\n        parent->child = instance->list.head;\n        instance->list.head = parent;\n    } while(true);\n\n    NfcPollerListElement* iter = instance->list.head;\n    iter->poller = iter->poller_api->alloc(instance->nfc);\n\n    do {\n        if(iter->child == NULL) break;\n        iter->child->poller = iter->child->poller_api->alloc(iter->poller);\n        iter->poller_api->set_callback(\n            iter->poller, iter->child->poller_api->run, iter->child->poller);\n\n        iter = iter->child;\n    } while(true);\n}\n\nstatic void nfc_poller_list_free(NfcPoller* instance) {\n    do {\n        instance->list.head->poller_api->free(instance->list.head->poller);\n        NfcPollerListElement* child = instance->list.head->child;\n        free(instance->list.head);\n        if(child == NULL) break;\n        instance->list.head = child;\n    } while(true);\n}\n\nNfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol) {\n    furi_check(nfc);\n    furi_check(protocol < NfcProtocolNum);\n\n    NfcPoller* instance = malloc(sizeof(NfcPoller));\n    instance->session_state = NfcPollerSessionStateIdle;\n    instance->nfc = nfc;\n    instance->protocol = protocol;\n    nfc_poller_list_alloc(instance);\n\n    return instance;\n}\n\nvoid nfc_poller_free(NfcPoller* instance) {\n    furi_check(instance);\n\n    nfc_poller_list_free(instance);\n    free(instance);\n}\n\nstatic NfcCommand nfc_poller_start_callback(NfcEvent event, void* context) {\n    furi_assert(context);\n\n    NfcPoller* instance = context;\n\n    NfcCommand command = NfcCommandContinue;\n    NfcGenericEvent poller_event = {\n        .protocol = NfcProtocolInvalid,\n        .instance = instance->nfc,\n        .event_data = &event,\n    };\n\n    if(event.type == NfcEventTypePollerReady) {\n        NfcPollerListElement* head_poller = instance->list.head;\n        command = head_poller->poller_api->run(poller_event, head_poller->poller);\n    }\n\n    if(instance->session_state == NfcPollerSessionStateStopRequest) {\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nvoid nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context) {\n    furi_check(instance);\n    furi_check(callback);\n    furi_check(instance->session_state == NfcPollerSessionStateIdle);\n\n    NfcPollerListElement* tail_poller = instance->list.tail;\n    tail_poller->poller_api->set_callback(tail_poller->poller, callback, context);\n\n    instance->session_state = NfcPollerSessionStateActive;\n    nfc_start(instance->nfc, nfc_poller_start_callback, instance);\n}\n\nstatic NfcCommand nfc_poller_start_ex_tail_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol != NfcProtocolInvalid);\n\n    NfcPoller* instance = context;\n    NfcCommand command = NfcCommandContinue;\n\n    NfcGenericEventEx poller_event = {\n        .poller = instance->list.tail->poller,\n        .parent_event_data = event.event_data,\n    };\n\n    command = instance->callback(poller_event, instance->context);\n\n    return command;\n}\n\nstatic NfcCommand nfc_poller_start_ex_head_callback(NfcEvent event, void* context) {\n    furi_assert(context);\n\n    NfcCommand command = NfcCommandContinue;\n    NfcPoller* instance = context;\n\n    NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol);\n\n    if(parent_protocol == NfcProtocolInvalid) {\n        NfcGenericEventEx poller_event = {\n            .poller = instance->list.tail->poller,\n            .parent_event_data = &event,\n        };\n\n        command = instance->callback(poller_event, instance->context);\n    } else {\n        NfcGenericEvent poller_event = {\n            .protocol = NfcProtocolInvalid,\n            .instance = instance->nfc,\n            .event_data = &event,\n        };\n        NfcPollerListElement* head_poller = instance->list.head;\n        command = head_poller->poller_api->run(poller_event, head_poller->poller);\n    }\n\n    if(instance->session_state == NfcPollerSessionStateStopRequest) {\n        command = NfcCommandStop;\n    }\n\n    return command;\n}\n\nvoid nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context) {\n    furi_check(instance);\n    furi_check(callback);\n    furi_check(instance->session_state == NfcPollerSessionStateIdle);\n\n    instance->callback = callback;\n    instance->context = context;\n\n    NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol);\n    if(parent_protocol != NfcProtocolInvalid) {\n        NfcPollerListElement* iter = instance->list.head;\n        while(iter->protocol != parent_protocol)\n            iter = iter->child;\n\n        iter->poller_api->set_callback(iter->poller, nfc_poller_start_ex_tail_callback, instance);\n    }\n\n    instance->session_state = NfcPollerSessionStateActive;\n    nfc_start(instance->nfc, nfc_poller_start_ex_head_callback, instance);\n}\n\nvoid nfc_poller_stop(NfcPoller* instance) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n\n    instance->session_state = NfcPollerSessionStateStopRequest;\n    nfc_stop(instance->nfc);\n    instance->session_state = NfcPollerSessionStateIdle;\n}\n\nstatic NfcCommand nfc_poller_detect_tail_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n\n    NfcPoller* instance = context;\n    NfcPollerListElement* tail_poller = instance->list.tail;\n    instance->protocol_detected = tail_poller->poller_api->detect(event, tail_poller->poller);\n\n    return NfcCommandStop;\n}\n\nstatic NfcCommand nfc_poller_detect_head_callback(NfcEvent event, void* context) {\n    furi_assert(context);\n\n    NfcPoller* instance = context;\n    NfcPollerListElement* tail_poller = instance->list.tail;\n    NfcPollerListElement* head_poller = instance->list.head;\n\n    NfcCommand command = NfcCommandContinue;\n    NfcGenericEvent poller_event = {\n        .protocol = NfcProtocolInvalid,\n        .instance = instance->nfc,\n        .event_data = &event,\n    };\n\n    if(event.type == NfcEventTypePollerReady) {\n        if(tail_poller == head_poller) {\n            instance->protocol_detected =\n                tail_poller->poller_api->detect(poller_event, tail_poller->poller);\n            command = NfcCommandStop;\n        } else {\n            command = head_poller->poller_api->run(poller_event, head_poller->poller);\n        }\n    }\n\n    return command;\n}\n\nbool nfc_poller_detect(NfcPoller* instance) {\n    furi_check(instance);\n    furi_check(instance->session_state == NfcPollerSessionStateIdle);\n\n    instance->session_state = NfcPollerSessionStateActive;\n    NfcPollerListElement* tail_poller = instance->list.tail;\n    NfcPollerListElement* iter = instance->list.head;\n\n    if(tail_poller != instance->list.head) {\n        while(iter->child != tail_poller)\n            iter = iter->child;\n        iter->poller_api->set_callback(iter->poller, nfc_poller_detect_tail_callback, instance);\n    }\n\n    nfc_start(instance->nfc, nfc_poller_detect_head_callback, instance);\n    nfc_stop(instance->nfc);\n\n    if(tail_poller != instance->list.head) {\n        iter->poller_api->set_callback(\n            iter->poller, tail_poller->poller_api->run, tail_poller->poller);\n    }\n\n    return instance->protocol_detected;\n}\n\nNfcProtocol nfc_poller_get_protocol(const NfcPoller* instance) {\n    furi_check(instance);\n\n    return instance->protocol;\n}\n\nconst NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance) {\n    furi_check(instance);\n\n    NfcPollerListElement* tail_poller = instance->list.tail;\n    return tail_poller->poller_api->get_data(tail_poller->poller);\n}\n"
  },
  {
    "path": "lib/nfc/nfc_poller.h",
    "content": "/**\n * @file nfc_poller.h\n * @brief NFC card reading library.\n *\n * Once started, it will try to activate and read a card using the designated protocol,\n * which is usually obtained by creating and starting an NfcScanner first.\n *\n * @see nfc_scanner.h\n *\n * When running, NfcPoller will generate events that the calling code must handle\n * by providing a callback function. The events passed to the callback are protocol-specific\n * and may include errors, state changes, data reception, special function requests and more.\n *\n */\n#pragma once\n\n#include <nfc/protocols/nfc_generic_event.h>\n#include <nfc/protocols/nfc_device_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcPoller opaque type definition.\n */\ntypedef struct NfcPoller NfcPoller;\n\n/**\n * @brief Extended generic Nfc event type.\n *\n * An extended generic Nfc event contains protocol poller and it's parent protocol event data.\n * If protocol has no parent, then events are produced by Nfc instance.\n *\n * The parent_event_data field is protocol-specific and should be cast to the appropriate type before use.\n */\ntypedef struct {\n    NfcGenericInstance* poller; /**< Pointer to the protocol poller. */\n    NfcGenericEventData*\n        parent_event_data /**< Pointer to the protocol's parent poller event data. */;\n} NfcGenericEventEx;\n\n/**\n * @brief Extended generic Nfc event callback type.\n *\n * A function of this type must be passed as the callback parameter upon extended start of a poller.\n *\n * @param [in] event Nfc  extended generic event, passed by value, complete with protocol type and data.\n * @param [in,out] context pointer to the user-specific context (set when starting a poller/listener instance).\n * @returns the command which the event producer must execute.\n */\ntypedef NfcCommand (*NfcGenericCallbackEx)(NfcGenericEventEx event, void* context);\n\n/**\n * @brief Allocate an NfcPoller instance.\n *\n * @param[in] nfc pointer to an Nfc instance.\n * @param[in] protocol identifier of the protocol to be used.\n * @returns pointer to an allocated instance.\n *\n * @see nfc.h\n */\nNfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol);\n\n/**\n * @brief Delete an NfcPoller instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid nfc_poller_free(NfcPoller* instance);\n\n/**\n * @brief Start an NfcPoller instance.\n *\n * The callback logic is protocol-specific, so it cannot be described here in detail.\n * However, the callback return value ALWAYS determines what the poller should do next:\n * to continue whatever it was doing prior to the callback run or to stop.\n *\n * @param[in,out] instance pointer to the instance to be started.\n * @param[in] callback pointer to a user-defined callback function which will receive events.\n * @param[in] context pointer to a user-specific context (will be passed to the callback).\n */\nvoid nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context);\n\n/**\n * @brief Start an NfcPoller instance in extended mode.\n *\n * When nfc poller is started in extended mode, callback will be called with parent protocol events\n * and protocol instance. This mode enables to make custom poller state machines.\n *\n * @param[in,out] instance pointer to the instance to be started.\n * @param[in] callback pointer to a user-defined callback function which will receive events.\n * @param[in] context pointer to a user-specific context (will be passed to the callback).\n */\nvoid nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context);\n\n/**\n * @brief Stop an NfcPoller instance.\n *\n * The reading process can be stopped explicitly (the other way is via the callback return value).\n *\n * @param[in,out] instance pointer to the instance to be stopped.\n */\nvoid nfc_poller_stop(NfcPoller* instance);\n\n/**\n * @brief Detect whether there is a card supporting a particular protocol in the vicinity.\n *\n * The behaviour of this function is protocol-defined, in general, it will do whatever is\n * necessary to determine whether a card supporting the current protocol is in the vicinity\n * and whether it is functioning normally.\n *\n * It is used automatically inside NfcScanner, so there is usually no need\n * to call it explicitly.\n *\n * @see nfc_scanner.h\n *\n * @param[in,out] instance pointer to the instance to perform the detection with.\n * @returns true if a supported card was detected, false otherwise.\n */\nbool nfc_poller_detect(NfcPoller* instance);\n\n/**\n * @brief Get the protocol identifier an NfcPoller instance was created with.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @returns identifier of the protocol used by the instance.\n */\nNfcProtocol nfc_poller_get_protocol(const NfcPoller* instance);\n\n/**\n * @brief Get the data that was that was gathered during the reading process.\n *\n * @param[in] instance pointer to the instance to be queried.\n * @returns pointer to the NFC device data.\n */\nconst NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/nfc_scanner.c",
    "content": "#include \"nfc_scanner.h\"\n#include \"nfc_poller.h\"\n\n#include <nfc/protocols/nfc_poller_defs.h>\n\n#include <furi/furi.h>\n\n#define TAG \"NfcScanner\"\n\ntypedef enum {\n    NfcScannerStateIdle,\n    NfcScannerStateTryBasePollers,\n    NfcScannerStateFindChildrenProtocols,\n    NfcScannerStateDetectChildrenProtocols,\n    NfcScannerStateComplete,\n\n    NfcScannerStateNum,\n} NfcScannerState;\n\ntypedef enum {\n    NfcScannerSessionStateIdle,\n    NfcScannerSessionStateActive,\n    NfcScannerSessionStateStopRequest,\n} NfcScannerSessionState;\n\nstruct NfcScanner {\n    Nfc* nfc;\n    NfcScannerState state;\n    NfcScannerSessionState session_state;\n\n    NfcScannerCallback callback;\n    void* context;\n\n    NfcEvent nfc_event;\n\n    NfcProtocol first_detected_protocol;\n\n    size_t base_protocols_num;\n    size_t base_protocols_idx;\n    NfcProtocol base_protocols[NfcProtocolNum];\n\n    size_t detected_base_protocols_num;\n    NfcProtocol detected_base_protocols[NfcProtocolNum];\n\n    size_t children_protocols_num;\n    size_t children_protocols_idx;\n    NfcProtocol children_protocols[NfcProtocolNum];\n\n    size_t detected_protocols_num;\n    NfcProtocol detected_protocols[NfcProtocolNum];\n\n    NfcProtocol current_protocol;\n\n    FuriThread* scan_worker;\n};\n\nstatic void nfc_scanner_reset(NfcScanner* instance) {\n    instance->base_protocols_idx = 0;\n    instance->base_protocols_num = 0;\n\n    instance->children_protocols_idx = 0;\n    instance->children_protocols_num = 0;\n\n    instance->detected_protocols_num = 0;\n    instance->detected_base_protocols_num = 0;\n\n    instance->current_protocol = 0;\n}\n\ntypedef void (*NfcScannerStateHandler)(NfcScanner* instance);\n\nvoid nfc_scanner_state_handler_idle(NfcScanner* instance) {\n    for(size_t i = 0; i < NfcProtocolNum; i++) {\n        NfcProtocol parent_protocol = nfc_protocol_get_parent(i);\n        if(parent_protocol == NfcProtocolInvalid) {\n            instance->base_protocols[instance->base_protocols_num] = i;\n            instance->base_protocols_num++;\n        }\n    }\n    FURI_LOG_D(TAG, \"Found %zu base protocols\", instance->base_protocols_num);\n\n    instance->first_detected_protocol = NfcProtocolInvalid;\n    instance->state = NfcScannerStateTryBasePollers;\n}\n\nvoid nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) {\n    do {\n        instance->current_protocol = instance->base_protocols[instance->base_protocols_idx];\n\n        if(instance->first_detected_protocol == instance->current_protocol) {\n            instance->state = NfcScannerStateFindChildrenProtocols;\n            break;\n        }\n\n        NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);\n        bool protocol_detected = nfc_poller_detect(poller);\n        nfc_poller_free(poller);\n\n        if(protocol_detected) {\n            instance->detected_protocols[instance->detected_protocols_num] =\n                instance->current_protocol;\n            instance->detected_protocols_num++;\n\n            instance->detected_base_protocols[instance->detected_base_protocols_num] =\n                instance->current_protocol;\n            instance->detected_base_protocols_num++;\n\n            if(instance->first_detected_protocol == NfcProtocolInvalid) {\n                instance->first_detected_protocol = instance->current_protocol;\n                instance->current_protocol = NfcProtocolInvalid;\n            }\n        }\n\n        instance->base_protocols_idx =\n            (instance->base_protocols_idx + 1) % instance->base_protocols_num;\n    } while(false);\n}\n\nvoid nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) {\n    for(size_t i = 0; i < NfcProtocolNum; i++) {\n        for(size_t j = 0; j < instance->detected_base_protocols_num; j++) {\n            if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) {\n                instance->children_protocols[instance->children_protocols_num] = i;\n                instance->children_protocols_num++;\n            }\n        }\n    }\n\n    if(instance->children_protocols_num > 0) {\n        instance->state = NfcScannerStateDetectChildrenProtocols;\n    } else {\n        instance->state = NfcScannerStateComplete;\n    }\n    FURI_LOG_D(TAG, \"Found %zu children\", instance->children_protocols_num);\n}\n\nvoid nfc_scanner_state_handler_detect_children_protocols(NfcScanner* instance) {\n    furi_assert(instance->children_protocols_num);\n\n    instance->current_protocol = instance->children_protocols[instance->children_protocols_idx];\n\n    NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);\n    bool protocol_detected = nfc_poller_detect(poller);\n    nfc_poller_free(poller);\n\n    if(protocol_detected) {\n        instance->detected_protocols[instance->detected_protocols_num] =\n            instance->current_protocol;\n        instance->detected_protocols_num++;\n    }\n\n    instance->children_protocols_idx++;\n    if(instance->children_protocols_idx == instance->children_protocols_num) {\n        instance->state = NfcScannerStateComplete;\n    }\n}\n\nstatic void nfc_scanner_filter_detected_protocols(NfcScanner* instance) {\n    size_t filtered_protocols_num = 0;\n    NfcProtocol filtered_protocols[NfcProtocolNum] = {};\n\n    for(size_t i = 0; i < instance->detected_protocols_num; i++) {\n        bool is_parent = false;\n        for(size_t j = i; j < instance->detected_protocols_num; j++) {\n            is_parent = nfc_protocol_has_parent(\n                instance->detected_protocols[j], instance->detected_protocols[i]);\n            if(is_parent) break;\n        }\n        if(!is_parent) {\n            filtered_protocols[filtered_protocols_num] = instance->detected_protocols[i];\n            filtered_protocols_num++;\n        }\n    }\n\n    instance->detected_protocols_num = filtered_protocols_num;\n    memcpy(instance->detected_protocols, filtered_protocols, filtered_protocols_num);\n}\n\nvoid nfc_scanner_state_handler_complete(NfcScanner* instance) {\n    if(instance->detected_protocols_num > 1) {\n        nfc_scanner_filter_detected_protocols(instance);\n    }\n    FURI_LOG_I(TAG, \"Detected %zu protocols\", instance->detected_protocols_num);\n\n    NfcScannerEvent event = {\n        .type = NfcScannerEventTypeDetected,\n        .data =\n            {\n                .protocol_num = instance->detected_protocols_num,\n                .protocols = instance->detected_protocols,\n            },\n    };\n\n    instance->callback(event, instance->context);\n    furi_delay_ms(100);\n}\n\nstatic NfcScannerStateHandler nfc_scanner_state_handlers[NfcScannerStateNum] = {\n    [NfcScannerStateIdle] = nfc_scanner_state_handler_idle,\n    [NfcScannerStateTryBasePollers] = nfc_scanner_state_handler_try_base_pollers,\n    [NfcScannerStateFindChildrenProtocols] = nfc_scanner_state_handler_find_children_protocols,\n    [NfcScannerStateDetectChildrenProtocols] = nfc_scanner_state_handler_detect_children_protocols,\n    [NfcScannerStateComplete] = nfc_scanner_state_handler_complete,\n};\n\nstatic int32_t nfc_scanner_worker(void* context) {\n    furi_assert(context);\n\n    NfcScanner* instance = context;\n\n    while(instance->session_state == NfcScannerSessionStateActive) {\n        nfc_scanner_state_handlers[instance->state](instance);\n    }\n\n    nfc_scanner_reset(instance);\n\n    return 0;\n}\n\nNfcScanner* nfc_scanner_alloc(Nfc* nfc) {\n    furi_check(nfc);\n\n    NfcScanner* instance = malloc(sizeof(NfcScanner));\n    instance->nfc = nfc;\n\n    return instance;\n}\n\nvoid nfc_scanner_free(NfcScanner* instance) {\n    furi_check(instance);\n    furi_check(instance->state == NfcScannerStateIdle);\n\n    free(instance);\n}\n\nvoid nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context) {\n    furi_check(instance);\n    furi_check(callback);\n    furi_check(instance->state == NfcScannerStateIdle);\n    furi_check(instance->scan_worker == NULL);\n\n    instance->callback = callback;\n    instance->context = context;\n    instance->session_state = NfcScannerSessionStateActive;\n\n    instance->scan_worker = furi_thread_alloc();\n    furi_thread_set_name(instance->scan_worker, \"NfcScanWorker\");\n    furi_thread_set_context(instance->scan_worker, instance);\n    furi_thread_set_stack_size(instance->scan_worker, 4 * 1024);\n    furi_thread_set_callback(instance->scan_worker, nfc_scanner_worker);\n\n    furi_thread_start(instance->scan_worker);\n}\n\nvoid nfc_scanner_stop(NfcScanner* instance) {\n    furi_check(instance);\n    furi_check(instance->scan_worker);\n\n    instance->session_state = NfcScannerSessionStateStopRequest;\n    furi_thread_join(instance->scan_worker);\n    instance->session_state = NfcScannerSessionStateIdle;\n\n    furi_thread_free(instance->scan_worker);\n    instance->scan_worker = NULL;\n    instance->callback = NULL;\n    instance->context = NULL;\n    instance->state = NfcScannerStateIdle;\n}\n"
  },
  {
    "path": "lib/nfc/nfc_scanner.h",
    "content": "/**\n * @file nfc_scanner.h\n * @brief NFC card detection library.\n *\n * Once started, a NfcScanner instance will iterate over all available protocols\n * and return a list of one or more detected protocol identifiers via a user-provided callback.\n *\n * The NfcScanner behaviour is greedy, i.e. it will not stop scanning upon detection of\n * a just one protocol and will try others as well until all possibilities are exhausted.\n * This is to allow for multi-protocol card support.\n *\n * If no supported cards are in the vicinity, the scanning process will continue\n * until stopped explicitly.\n */\n#pragma once\n\n#include <nfc/nfc.h>\n#include <nfc/protocols/nfc_protocol.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief NfcScanner opaque type definition.\n */\ntypedef struct NfcScanner NfcScanner;\n\n/**\n * @brief Event type passed to the user callback.\n */\ntypedef enum {\n    NfcScannerEventTypeDetected, /**< One or more protocols have been detected. */\n} NfcScannerEventType;\n\n/**\n * @brief Event data passed to the user callback.\n */\ntypedef struct {\n    size_t protocol_num; /**< Number of detected protocols (one or more). */\n    NfcProtocol* protocols; /**< Pointer to the array of detected protocol identifiers. */\n} NfcScannerEventData;\n\n/**\n * @brief Event passed to the user callback.\n */\ntypedef struct {\n    NfcScannerEventType type; /**< Type of event. Determines how the data must be handled. */\n    NfcScannerEventData data; /**< Event-specific data. Handled accordingly to the even type. */\n} NfcScannerEvent;\n\n/**\n * @brief User callback function signature.\n *\n * A function with such signature must be provided by the user upon calling nfc_scanner_start().\n *\n * @param[in] event occurred event, complete with type and data.\n * @param[in] context pointer to the context data provided in nfc_scanner_start() call.\n */\ntypedef void (*NfcScannerCallback)(NfcScannerEvent event, void* context);\n\n/**\n * @brief Allocate an NfcScanner instance.\n *\n * @param[in] nfc pointer to an Nfc instance.\n * @returns pointer to the allocated NfcScanner instance.\n *\n * @see nfc.h\n */\nNfcScanner* nfc_scanner_alloc(Nfc* nfc);\n\n/**\n * @brief Delete an NfcScanner instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\nvoid nfc_scanner_free(NfcScanner* instance);\n\n/**\n * @brief Start an NfcScanner.\n *\n * @param[in,out] instance pointer to the instance to be started.\n * @param[in] callback pointer to the callback function (will be called upon a detection event).\n * @param[in] context pointer to the caller-specific context (will be passed to the callback).\n */\nvoid nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context);\n\n/**\n * @brief Stop an NfcScanner.\n *\n * @param[in,out] instance pointer to the instance to be stopped.\n */\nvoid nfc_scanner_stop(NfcScanner* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica.c",
    "content": "#include \"felica_i.h\"\n#include <lib/toolbox/hex.h>\n\n#include <furi.h>\n\n#include <nfc/nfc_common.h>\n\n#define FELICA_PROTOCOL_NAME \"FeliCa\"\n#define FELICA_DEVICE_NAME   \"FeliCa\"\n\n#define FELICA_DATA_FORMAT_VERSION   \"Data format version\"\n#define FELICA_MANUFACTURE_ID        \"Manufacture id\"\n#define FELICA_MANUFACTURE_PARAMETER \"Manufacture parameter\"\n\nstatic const uint32_t felica_data_format_version = 2;\n\n/** @brief This is used in felica_prepare_first_block to define which \n * type of block needs to be prepared.\n*/\ntypedef enum {\n    FelicaMACTypeRead,\n    FelicaMACTypeWrite,\n} FelicaMACType;\n\nconst NfcDeviceBase nfc_device_felica = {\n    .protocol_name = FELICA_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)felica_alloc,\n    .free = (NfcDeviceFree)felica_free,\n    .reset = (NfcDeviceReset)felica_reset,\n    .copy = (NfcDeviceCopy)felica_copy,\n    .verify = (NfcDeviceVerify)felica_verify,\n    .load = (NfcDeviceLoad)felica_load,\n    .save = (NfcDeviceSave)felica_save,\n    .is_equal = (NfcDeviceEqual)felica_is_equal,\n    .get_name = (NfcDeviceGetName)felica_get_device_name,\n    .get_uid = (NfcDeviceGetUid)felica_get_uid,\n    .set_uid = (NfcDeviceSetUid)felica_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)felica_get_base_data,\n};\n\nFelicaData* felica_alloc(void) {\n    FelicaData* data = malloc(sizeof(FelicaData));\n    furi_check(data);\n\n    data->systems = simple_array_alloc(&felica_system_array_cfg);\n    furi_check(data->systems);\n    return data;\n}\n\nvoid felica_free(FelicaData* data) {\n    furi_check(data);\n\n    furi_check(data->systems);\n    simple_array_free(data->systems);\n\n    free(data);\n}\n\nvoid felica_reset(FelicaData* data) {\n    furi_check(data);\n\n    if(data->systems) {\n        simple_array_reset(data->systems);\n    }\n\n    data->blocks_read = 0;\n    data->blocks_total = 0;\n    data->workflow_type = FelicaUnknown;\n    memset(&data->idm, 0, sizeof(data->idm));\n    memset(&data->pmm, 0, sizeof(data->pmm));\n    memset(&data->data, 0, sizeof(data->data));\n}\n\nvoid felica_copy(FelicaData* data, const FelicaData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    felica_reset(data);\n\n    data->idm = other->idm;\n    data->pmm = other->pmm;\n    data->blocks_total = other->blocks_total;\n    data->blocks_read = other->blocks_read;\n    data->data = other->data;\n    data->workflow_type = other->workflow_type;\n\n    simple_array_copy(data->systems, other->systems);\n}\n\nbool felica_verify(FelicaData* data, const FuriString* device_type) {\n    UNUSED(data);\n    UNUSED(device_type);\n\n    return false;\n}\n\nbool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n\n    bool parsed = false;\n    FuriString* str_key_buffer = furi_string_alloc();\n    FuriString* str_data_buffer = furi_string_alloc();\n\n    // Header\n    do {\n        if(version < NFC_UNIFIED_FORMAT_VERSION) break;\n\n        uint32_t data_format_version = 0;\n        if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))\n            break;\n\n        // V1 saving function always treated everything as Felica Lite\n        // So we load the blocks as if everything is Felica Lite\n\n        if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))\n            break;\n        if(!flipper_format_read_hex(\n               ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))\n            break;\n\n        felica_get_workflow_type(data);\n        if(data_format_version == 1) {\n            data->workflow_type = FelicaLite;\n        }\n        parsed = true;\n    } while(false);\n\n    if(!parsed) {\n        furi_string_free(str_key_buffer);\n        furi_string_free(str_data_buffer);\n        return false;\n    }\n\n    switch(data->workflow_type) {\n    case FelicaLite:\n        // Blocks data\n        do {\n            uint32_t blocks_total = 0;\n            uint32_t blocks_read = 0;\n            if(!flipper_format_read_uint32(ff, \"Blocks total\", &blocks_total, 1)) break;\n            if(!flipper_format_read_uint32(ff, \"Blocks read\", &blocks_read, 1)) break;\n            data->blocks_total = (uint8_t)blocks_total;\n            data->blocks_read = (uint8_t)blocks_read;\n\n            for(uint8_t i = 0; i < data->blocks_total; i++) {\n                furi_string_printf(str_data_buffer, \"Block %d\", i);\n                if(!flipper_format_read_hex(\n                       ff,\n                       furi_string_get_cstr(str_data_buffer),\n                       (&data->data.dump[i * sizeof(FelicaBlock)]),\n                       sizeof(FelicaBlock))) {\n                    break;\n                }\n            }\n        } while(false);\n        break;\n    case FelicaStandard:\n        uint32_t systems_total = 0;\n        if(!flipper_format_read_uint32(ff, \"System found\", &systems_total, 1)) break;\n        if(systems_total == 0) break;\n\n        simple_array_init(data->systems, systems_total);\n\n        for(uint8_t sys_idx = 0; sys_idx < systems_total; sys_idx++) {\n            FelicaSystem* system = simple_array_get(data->systems, sys_idx);\n            uint16_t system_code = 0;\n\n            furi_string_reset(str_key_buffer);\n            furi_string_reset(str_data_buffer);\n            furi_string_printf(str_key_buffer, \"System %02X\", sys_idx);\n            if(!flipper_format_read_string(\n                   ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))\n                break;\n\n            if(!sscanf(furi_string_get_cstr(str_data_buffer), \"%04hX\", &system_code)) {\n                break;\n            }\n\n            system->system_code = system_code;\n            system->system_code_idx = sys_idx;\n\n            // Areas\n            do {\n                uint32_t area_count = 0;\n                if(!flipper_format_read_uint32(ff, \"Area found\", &area_count, 1)) break;\n                if(area_count == 0) break;\n\n                simple_array_init(system->areas, area_count);\n\n                furi_string_reset(str_key_buffer);\n                furi_string_reset(str_data_buffer);\n                for(uint16_t i = 0; i < area_count; i++) {\n                    furi_string_printf(str_key_buffer, \"Area %03X\", i);\n                    if(!flipper_format_read_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {\n                        break;\n                    }\n                    FelicaArea* area = simple_array_get(system->areas, i);\n                    if(sscanf(\n                           furi_string_get_cstr(str_data_buffer),\n                           \"| Code %04hX | Services #%03hX-#%03hX |\",\n                           &area->code,\n                           &area->first_idx,\n                           &area->last_idx) != 3) {\n                        break;\n                    }\n                }\n            } while(false);\n\n            // Services\n            do {\n                uint32_t service_count = 0;\n                if(!flipper_format_read_uint32(ff, \"Service found\", &service_count, 1)) break;\n                if(service_count == 0) break;\n\n                simple_array_init(system->services, service_count);\n\n                furi_string_reset(str_key_buffer);\n                furi_string_reset(str_data_buffer);\n                for(uint16_t i = 0; i < service_count; i++) {\n                    furi_string_printf(str_key_buffer, \"Service %03X\", i);\n                    if(!flipper_format_read_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {\n                        break;\n                    }\n                    FelicaService* service = simple_array_get(system->services, i);\n\n                    // all unread in the beginning. reserved for future block load\n                    if(!sscanf(\n                           furi_string_get_cstr(str_data_buffer),\n                           \"| Code %04hX |\",\n                           &service->code)) {\n                        break;\n                    }\n                    service->attr = service->code & 0x3F;\n                }\n            } while(false);\n\n            // Public blocks\n            do {\n                furi_string_reset(str_data_buffer);\n                furi_string_reset(str_key_buffer);\n                uint32_t public_block_count = 0;\n                if(!flipper_format_read_uint32(ff, \"Public blocks read\", &public_block_count, 1))\n                    break;\n                if(public_block_count == 0) break;\n\n                simple_array_init(system->public_blocks, public_block_count);\n                for(uint16_t i = 0; i < public_block_count; i++) {\n                    furi_string_printf(str_key_buffer, \"Block %04X\", i);\n                    if(!flipper_format_read_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {\n                        break;\n                    }\n\n                    FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);\n                    if(sscanf(\n                           furi_string_get_cstr(str_data_buffer),\n                           \"| Service code %04hX | Block index %02hhX |\",\n                           &public_block->service_code,\n                           &public_block->block_idx) != 2) {\n                        break;\n                    }\n\n                    size_t needle = furi_string_search_str(str_data_buffer, \"Data: \");\n                    if(needle == FURI_STRING_FAILURE) {\n                        break;\n                    }\n                    needle += 6; // length of \"Data: \" = 6\n                    furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE);\n                    furi_string_replace_all(str_data_buffer, \" \", \"\");\n                    if(!hex_chars_to_uint8(\n                           furi_string_get_cstr(str_data_buffer), public_block->block.data)) {\n                        break;\n                    }\n\n                    furi_string_reset(str_data_buffer);\n                    for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {\n                        furi_string_cat_printf(\n                            str_data_buffer, \"%02X \", public_block->block.data[j]);\n                    }\n                }\n            } while(false);\n        }\n        break;\n    default:\n        break;\n    }\n\n    furi_string_free(str_key_buffer);\n    furi_string_free(str_data_buffer);\n\n    return parsed;\n}\n\nbool felica_save(const FelicaData* data, FlipperFormat* ff) {\n    furi_check(data);\n\n    bool saved = false;\n    FuriString* str_data_buffer = furi_string_alloc();\n    FuriString* str_key_buffer = furi_string_alloc();\n    do {\n        // Header\n        if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME \" specific data\")) break;\n        if(!flipper_format_write_uint32(\n               ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))\n            break;\n        if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))\n            break;\n        if(!flipper_format_write_hex(\n               ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))\n            break;\n\n        saved = true;\n\n        felica_get_ic_name(data, str_data_buffer);\n        furi_string_replace_all(str_data_buffer, \"\\n\", \" \");\n        if(!flipper_format_write_string(ff, \"IC Type\", str_data_buffer)) break;\n        if(!flipper_format_write_empty_line(ff)) break;\n    } while(false);\n\n    switch(data->workflow_type) {\n    case FelicaLite:\n        if(!flipper_format_write_comment_cstr(ff, \"Felica Lite specific data\")) break;\n        // Blocks count\n        do {\n            uint32_t blocks_total = data->blocks_total;\n            uint32_t blocks_read = data->blocks_read;\n            if(!flipper_format_write_uint32(ff, \"Blocks total\", &blocks_total, 1)) break;\n            if(!flipper_format_write_uint32(ff, \"Blocks read\", &blocks_read, 1)) break;\n\n            // Blocks data\n            furi_string_reset(str_data_buffer);\n            furi_string_reset(str_key_buffer);\n            for(uint8_t i = 0; i < blocks_total; i++) {\n                furi_string_printf(str_key_buffer, \"Block %d\", i);\n                if(!flipper_format_write_hex(\n                       ff,\n                       furi_string_get_cstr(str_key_buffer),\n                       (&data->data.dump[i * sizeof(FelicaBlock)]),\n                       sizeof(FelicaBlock))) {\n                    saved = false;\n                    break;\n                }\n            }\n        } while(false);\n        break;\n\n    case FelicaStandard:\n        if(!flipper_format_write_comment_cstr(ff, \"Felica Standard specific data\")) break;\n        uint32_t systems_count = simple_array_get_count(data->systems);\n        if(!flipper_format_write_uint32(ff, \"System found\", &systems_count, 1)) break;\n        for(uint32_t sys_idx = 0; sys_idx < systems_count; sys_idx++) {\n            FelicaSystem* system = simple_array_get(data->systems, sys_idx);\n\n            furi_string_reset(str_data_buffer);\n            furi_string_reset(str_key_buffer);\n            furi_string_printf(str_key_buffer, \"\\n\\nSystem %02X\", (uint8_t)sys_idx);\n            furi_string_printf(str_data_buffer, \"%04X\", system->system_code);\n            if(!flipper_format_write_string(\n                   ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))\n                break;\n            if(!flipper_format_write_empty_line(ff)) break;\n\n            do {\n                uint32_t area_count = simple_array_get_count(system->areas);\n                uint32_t service_count = simple_array_get_count(system->services);\n                // Note: The theoretical max area/service count is 2^10\n                // So uint16_t is already enough for practical usage\n                // The following key index print will use %03X because 12 bits are enough to cover 0-1023\n\n                // Area count\n                if(!flipper_format_write_uint32(ff, \"Area found\", &area_count, 1)) break;\n\n                // Area data\n                furi_string_reset(str_data_buffer);\n                furi_string_reset(str_key_buffer);\n                for(uint16_t i = 0; i < area_count; i++) {\n                    FelicaArea* area = simple_array_get(system->areas, i);\n                    furi_string_printf(str_key_buffer, \"Area %03X\", i);\n                    furi_string_printf(\n                        str_data_buffer,\n                        \"| Code %04X | Services #%03X-#%03X |\",\n                        area->code,\n                        area->first_idx,\n                        area->last_idx);\n                    if(!flipper_format_write_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))\n                        break;\n                }\n                if(!flipper_format_write_empty_line(ff)) break;\n\n                // Service count\n                if(!flipper_format_write_uint32(ff, \"Service found\", &service_count, 1)) break;\n\n                // Service data\n                furi_string_reset(str_data_buffer);\n                furi_string_reset(str_key_buffer);\n                for(uint16_t i = 0; i < service_count; i++) {\n                    FelicaService* service = simple_array_get(system->services, i);\n                    furi_string_printf(str_key_buffer, \"Service %03X\", i);\n                    furi_string_printf(\n                        str_data_buffer,\n                        \"| Code %04X | Attrib. %02X \",\n                        service->code,\n                        service->attr);\n                    felica_service_get_attribute_string(service, str_data_buffer);\n                    if(!flipper_format_write_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))\n                        break;\n                }\n                if(!flipper_format_write_empty_line(ff)) break;\n\n                // Directory tree\n                furi_string_reset(str_data_buffer);\n                furi_string_reset(str_key_buffer);\n                furi_string_printf(\n                    str_data_buffer,\n                    \"\\n::: ... are public services\\n||| ... are private services\");\n                felica_write_directory_tree(system, str_data_buffer);\n                furi_string_replace_all(str_data_buffer, \":\", \"+\");\n                // We use a clearer marker in saved text files\n                if(!flipper_format_write_string(ff, \"Directory Tree\", str_data_buffer)) break;\n            } while(false);\n\n            // Public blocks\n            do {\n                uint32_t public_block_count = simple_array_get_count(system->public_blocks);\n                if(!flipper_format_write_uint32(ff, \"Public blocks read\", &public_block_count, 1))\n                    break;\n                furi_string_reset(str_data_buffer);\n                furi_string_reset(str_key_buffer);\n                for(uint16_t i = 0; i < public_block_count; i++) {\n                    FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);\n                    furi_string_printf(str_key_buffer, \"Block %04X\", i);\n                    furi_string_printf(\n                        str_data_buffer,\n                        \"| Service code %04X | Block index %02X | Data: \",\n                        public_block->service_code,\n                        public_block->block_idx);\n                    for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {\n                        furi_string_cat_printf(\n                            str_data_buffer, \"%02X \", public_block->block.data[j]);\n                    }\n                    furi_string_cat_printf(str_data_buffer, \"|\");\n                    if(!flipper_format_write_string(\n                           ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))\n                        break;\n                }\n            } while(false);\n        }\n        break;\n    default:\n        break;\n    }\n\n    // Clean up\n    furi_string_free(str_data_buffer);\n    furi_string_free(str_key_buffer);\n\n    return saved;\n}\n\nbool felica_is_equal(const FelicaData* data, const FelicaData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return memcmp(data->idm.data, other->idm.data, sizeof(FelicaIDm)) == 0 &&\n           memcmp(data->pmm.data, other->pmm.data, sizeof(FelicaPMm)) == 0 &&\n           data->blocks_total == other->blocks_total && data->blocks_read == other->blocks_read &&\n           memcmp(&data->data, &other->data, sizeof(data->data)) == 0 &&\n           simple_array_is_equal(data->systems, other->systems);\n}\n\nconst char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n\n    return FELICA_DEVICE_NAME;\n}\n\nconst uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {\n    furi_check(data);\n\n    // Consider Manufacturer ID as UID\n    if(uid_len) {\n        *uid_len = FELICA_IDM_SIZE;\n    }\n\n    return data->idm.data;\n}\n\nbool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    // Consider Manufacturer ID as UID\n    const bool uid_valid = uid_len == FELICA_IDM_SIZE;\n    if(uid_valid) {\n        memcpy(data->idm.data, uid, uid_len);\n    }\n\n    return uid_valid;\n}\n\nFelicaData* felica_get_base_data(const FelicaData* data) {\n    UNUSED(data);\n    furi_crash(\"No base data\");\n}\n\nstatic void felica_reverse_copy_block(const uint8_t* array, uint8_t* reverse_array) {\n    furi_assert(array);\n    furi_assert(reverse_array);\n\n    for(int i = 0; i < 8; i++) {\n        reverse_array[i] = array[7 - i];\n    }\n}\n\nvoid felica_calculate_session_key(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* rc,\n    uint8_t* out) {\n    furi_check(ctx);\n    furi_check(ck);\n    furi_check(rc);\n    furi_check(out);\n\n    uint8_t iv[8];\n    memset(iv, 0, 8);\n\n    uint8_t ck_reversed[16];\n    felica_reverse_copy_block(ck, ck_reversed);\n    felica_reverse_copy_block(ck + 8, ck_reversed + 8);\n\n    uint8_t rc_reversed[16];\n    felica_reverse_copy_block(rc, rc_reversed);\n    felica_reverse_copy_block(rc + 8, rc_reversed + 8);\n\n    mbedtls_des3_set2key_enc(ctx, ck_reversed);\n    mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, FELICA_DATA_BLOCK_SIZE, iv, rc_reversed, out);\n}\n\nstatic bool felica_calculate_mac(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* first_block,\n    const uint8_t* data,\n    const size_t length,\n    uint8_t* mac) {\n    furi_check((length % 8) == 0);\n\n    uint8_t reverse_data[8];\n    uint8_t iv[8];\n    uint8_t out[8];\n    mbedtls_des3_set2key_enc(ctx, session_key);\n\n    felica_reverse_copy_block(rc, iv);\n    felica_reverse_copy_block(first_block, reverse_data);\n    uint8_t i = 0;\n    bool error = false;\n    do {\n        if(mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 8, iv, reverse_data, out) == 0) {\n            memcpy(iv, out, sizeof(iv));\n            felica_reverse_copy_block(data + i, reverse_data);\n            i += 8;\n        } else {\n            error = true;\n            break;\n        }\n    } while(i <= length);\n\n    if(!error) {\n        felica_reverse_copy_block(out, mac);\n    }\n    return !error;\n}\n\nstatic void felica_prepare_first_block(\n    FelicaMACType operation_type,\n    const uint8_t* blocks,\n    const uint8_t block_count,\n    uint8_t* out) {\n    furi_check(blocks);\n    furi_check(out);\n    if(operation_type == FelicaMACTypeRead) {\n        memset(out, 0xFF, 8);\n        for(uint8_t i = 0, j = 0; i < block_count; i++, j += 2) {\n            out[j] = blocks[i];\n            out[j + 1] = 0;\n        }\n    } else {\n        furi_check(block_count == 4);\n        memset(out, 0, 8);\n        out[0] = blocks[0];\n        out[1] = blocks[1];\n        out[2] = blocks[2];\n        out[4] = blocks[3];\n        out[6] = FELICA_BLOCK_INDEX_MAC_A;\n    }\n}\n\nbool felica_check_mac(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* blocks,\n    const uint8_t block_count,\n    uint8_t* data) {\n    furi_check(ctx);\n    furi_check(session_key);\n    furi_check(rc);\n    furi_check(blocks);\n    furi_check(data);\n\n    uint8_t mac[8];\n    felica_calculate_mac_read(ctx, session_key, rc, blocks, block_count, data, mac);\n\n    uint8_t mac_offset = FELICA_DATA_BLOCK_SIZE * (block_count - 1);\n    uint8_t* mac_ptr = data + mac_offset;\n    return !memcmp(mac, mac_ptr, 8);\n}\n\nvoid felica_calculate_mac_read(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* blocks,\n    const uint8_t block_count,\n    const uint8_t* data,\n    uint8_t* mac) {\n    furi_check(ctx);\n    furi_check(session_key);\n    furi_check(rc);\n    furi_check(blocks);\n    furi_check(data);\n    furi_check(mac);\n\n    uint8_t first_block[8];\n    felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);\n    uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1);\n    felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac);\n}\n\nvoid felica_calculate_mac_write(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* wcnt,\n    const uint8_t* data,\n    uint8_t* mac) {\n    furi_check(ctx);\n    furi_check(session_key);\n    furi_check(rc);\n    furi_check(wcnt);\n    furi_check(data);\n    furi_check(mac);\n\n    const uint8_t WCNT_length = 3;\n    uint8_t block_data[WCNT_length + 1];\n    uint8_t first_block[8];\n\n    memcpy(block_data, wcnt, WCNT_length);\n    block_data[3] = FELICA_BLOCK_INDEX_STATE;\n    felica_prepare_first_block(FelicaMACTypeWrite, block_data, WCNT_length + 1, first_block);\n\n    uint8_t session_swapped[FELICA_DATA_BLOCK_SIZE];\n    memcpy(session_swapped, session_key + 8, 8);\n    memcpy(session_swapped + 8, session_key, 8);\n    felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);\n}\n\nvoid felica_write_directory_tree(const FelicaSystem* system, FuriString* str) {\n    furi_check(system);\n    furi_check(str);\n\n    furi_string_cat_str(str, \"\\n\");\n\n    uint16_t area_last_stack[8];\n    uint8_t depth = 0;\n\n    size_t area_iter = 0;\n    const size_t area_count = simple_array_get_count(system->areas);\n    const size_t service_count = simple_array_get_count(system->services);\n\n    for(size_t svc_idx = 0; svc_idx < service_count; ++svc_idx) {\n        while(area_iter < area_count) {\n            const FelicaArea* next_area = simple_array_get(system->areas, area_iter);\n            if(next_area->first_idx != svc_idx) break;\n\n            for(uint8_t i = 0; i < depth - 1; ++i)\n                furi_string_cat_printf(str, \"| \");\n            furi_string_cat_printf(str, depth ? \"|\" : \"\");\n            furi_string_cat_printf(str, \"- AREA_%04X/\\n\", next_area->code >> 6);\n\n            area_last_stack[depth++] = next_area->last_idx;\n            area_iter++;\n        }\n\n        const FelicaService* service = simple_array_get(system->services, svc_idx);\n        bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;\n\n        for(uint8_t i = 0; i < depth - 1; ++i)\n            furi_string_cat_printf(str, is_public ? \": \" : \"| \");\n        furi_string_cat_printf(str, is_public ? \":\" : \"|\");\n        furi_string_cat_printf(str, \"- serv_%04X\\n\", service->code);\n\n        if(depth && svc_idx >= area_last_stack[depth - 1]) depth--;\n    }\n}\n\nvoid felica_get_workflow_type(FelicaData* data) {\n    // Reference: Proxmark3 repo\n    uint8_t rom_type = data->pmm.data[0];\n    uint8_t workflow_type = data->pmm.data[1];\n    if(workflow_type <= 0x48) {\n        // More liberal check because most of these should be treated as FeliCa Standard, regardless of mobile or not.\n        data->workflow_type = FelicaStandard;\n    } else {\n        switch(workflow_type) {\n        case 0xA2:\n            data->workflow_type = FelicaStandard;\n            break;\n        case 0xF0:\n        case 0xF1:\n        case 0xF2: // 0xF2 => FeliCa Link RC-S967 in Lite-S Mode or Lite-S HT Mode\n            data->workflow_type = FelicaLite;\n            break;\n        case 0xE1: // Felica Link\n        case 0xE0: // Felica Plug\n            data->workflow_type = FelicaUnknown;\n            break;\n        case 0xFF:\n            if(rom_type == 0xFF) {\n                data->workflow_type = FelicaUnknown; // Felica Link\n            }\n            break;\n        default:\n            data->workflow_type = FelicaUnknown;\n            break;\n        }\n    }\n}\n\nvoid felica_get_ic_name(const FelicaData* data, FuriString* ic_name) {\n    // Reference: Proxmark3 repo\n    uint8_t rom_type = data->pmm.data[0];\n    uint8_t ic_type = data->pmm.data[1];\n\n    switch(ic_type) {\n        // FeliCa Standard Products:\n        // odd findings\n    case 0x00:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S830\");\n        break;\n    case 0x01:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S915\");\n        break;\n    case 0x02:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S919\");\n        break;\n    case 0x06:\n    case 0x07:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nChip V1.0\");\n        break;\n    case 0x08:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S952\");\n        break;\n    case 0x09:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S953\");\n        break;\n    case 0x0B:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S9X4,\\nJapan Transit IC\");\n        break;\n    case 0x0C:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S954\");\n        break;\n    case 0x0D:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S960\");\n        break;\n    case 0x10:\n    case 0x11:\n    case 0x12:\n    case 0x13:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nChip V2.0\");\n        break;\n    case 0x14:\n    case 0x15:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nChip V3.0\");\n        break;\n    case 0x16:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nJapan Transit IC\");\n        break;\n    case 0x17:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nChip V4.0\");\n        break;\n    case 0x18:\n    case 0x19:\n    case 0x1A:\n    case 0x1B:\n    case 0x1C:\n    case 0x1D:\n    case 0x1E:\n    case 0x1F:\n        furi_string_set_str(ic_name, \"FeliCa Mobile IC,\\nChip V4.1\");\n        break;\n    case 0x20:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S962\");\n        // RC-S962 has been extensively found in Japan Transit ICs, despite model number not ending in 4\n        break;\n    case 0x31:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-S104,\\nJapan Transit IC\");\n        break;\n    case 0x32:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA00/1\");\n        break;\n    case 0x33:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA00/2\");\n        break;\n    case 0x34:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA01/1\");\n        break;\n    case 0x35:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA01/2\");\n        break;\n    case 0x36:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA04/1,\\nJapan Transit IC\");\n        break;\n    case 0x3E:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA08/1\");\n        break;\n    case 0x43:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA24/1\");\n        break;\n    case 0x44:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA20/1\");\n        break;\n    case 0x45:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA20/2\");\n        break;\n    case 0x46:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA21/2\");\n        break;\n    case 0x47:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA24/1x1\");\n        break;\n    case 0x48:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA21/2x1\");\n        break;\n    case 0xA2:\n        furi_string_set_str(ic_name, \"FeliCa Standard RC-SA14\");\n        break;\n    // NFC Dynamic Tag (FeliCa Plug) Products:\n    case 0xE0:\n        furi_string_set_str(ic_name, \"FeliCa Plug RC-S926,\\nNFC Dynamic Tag\");\n        break;\n    case 0xE1:\n        furi_string_set_str(ic_name, \"FeliCa Link RC-S967,\\nPlug Mode\");\n        break;\n    case 0xF0:\n        furi_string_set_str(ic_name, \"FeliCa Lite RC-S965\");\n        break;\n    case 0xF1:\n        furi_string_set_str(ic_name, \"FeliCa Lite-S RC-S966\");\n        break;\n    case 0xF2:\n        furi_string_set_str(ic_name, \"FeliCa Link RC-S967,\\nLite-S Mode or Lite-S HT Mode\");\n        break;\n    case 0xFF:\n        if(rom_type == 0xFF) { // from FeliCa Link User's Manual\n            furi_string_set_str(ic_name, \"FeliCa Link RC-S967,\\nNFC-DEP Mode\");\n        }\n        break;\n    default:\n        furi_string_printf(\n            ic_name,\n            \"Unknown IC %02X ROM %02X:\\nPlease submit an issue on\\nGitHub and help us identify.\",\n            ic_type,\n            rom_type);\n        break;\n    }\n}\n\nvoid felica_service_get_attribute_string(const FelicaService* service, FuriString* str) {\n    furi_check(service);\n    furi_check(str);\n\n    bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;\n    furi_string_cat_str(str, is_public ? \"| Public  \" : \"| Private \");\n\n    bool is_purse = (service->attr & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0;\n    // Subfield bitwise attributes are applicable depending on is PURSE or not\n\n    if(is_purse) {\n        furi_string_cat_str(str, \"| Purse  |\");\n        switch((service->attr & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) {\n        case 0:\n            furi_string_cat_str(str, \" Direct     |\");\n            break;\n        case 1:\n            furi_string_cat_str(str, \" Cashback   |\");\n            break;\n        case 2:\n            furi_string_cat_str(str, \" Decrement  |\");\n            break;\n        case 3:\n            furi_string_cat_str(str, \" Read Only  |\");\n            break;\n        default:\n            furi_string_cat_str(str, \" Unknown    |\");\n            break;\n        }\n    } else {\n        bool is_random = (service->attr & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0;\n        furi_string_cat_str(str, is_random ? \"| Random |\" : \"| Cyclic |\");\n        bool is_readonly = (service->attr & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0;\n        furi_string_cat_str(str, is_readonly ? \" Read Only  |\" : \" Read/Write |\");\n    }\n}\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica.h",
    "content": "#pragma once\n\n#include <toolbox/bit_buffer.h>\n#include <nfc/protocols/nfc_device_base_i.h>\n#include <mbedtls/include/mbedtls/des.h>\n#include <lib/toolbox/simple_array.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FELICA_IDM_SIZE        (8U)\n#define FELICA_PMM_SIZE        (8U)\n#define FELICA_DATA_BLOCK_SIZE (16U)\n\n#define FELICA_CMD_READ_WITHOUT_ENCRYPTION  (0x06U)\n#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U)\n\n#define FELICA_SERVICE_RW_ACCESS (0x0009U)\n#define FELICA_SERVICE_RO_ACCESS (0x000BU)\n\n#define FELICA_BLOCKS_TOTAL_COUNT    (28U)\n#define FELICA_BLOCK_INDEX_REG       (0x0EU)\n#define FELICA_BLOCK_INDEX_RC        (0x80U)\n#define FELICA_BLOCK_INDEX_MAC       (0x81U)\n#define FELICA_BLOCK_INDEX_ID        (0x82U)\n#define FELICA_BLOCK_INDEX_D_ID      (0x83U)\n#define FELICA_BLOCK_INDEX_SER_C     (0x84U)\n#define FELICA_BLOCK_INDEX_SYS_C     (0x85U)\n#define FELICA_BLOCK_INDEX_CKV       (0x86U)\n#define FELICA_BLOCK_INDEX_CK        (0x87U)\n#define FELICA_BLOCK_INDEX_MC        (0x88U)\n#define FELICA_BLOCK_INDEX_WCNT      (0x90U)\n#define FELICA_BLOCK_INDEX_MAC_A     (0x91U)\n#define FELICA_BLOCK_INDEX_STATE     (0x92U)\n#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)\n\n#define FELICA_STANDARD_MAX_BLOCK_COUNT (0xFFU)\n\n#define FELICA_GUARD_TIME_US    (20000U)\n#define FELICA_FDT_POLL_FC      (10000U)\n#define FELICA_POLL_POLL_MIN_US (1280U)\n\n#define FELICA_FDT_LISTEN_FC (0)\n\n#define FELICA_SYSTEM_CODE_CODE (0xFFFFU)\n#define FELICA_TIME_SLOT_1      (0x00U)\n#define FELICA_TIME_SLOT_2      (0x01U)\n#define FELICA_TIME_SLOT_4      (0x03U)\n#define FELICA_TIME_SLOT_8      (0x07U)\n#define FELICA_TIME_SLOT_16     (0x0FU)\n\n#define FELICA_CMD_LIST_SERVICE_CODE        0x0A\n#define FELICA_CMD_LIST_SERVICE_CODE_RESP   0x0B\n#define FELICA_CMD_REQUEST_SYSTEM_CODE      0x0C\n#define FELICA_CMD_REQUEST_SYSTEM_CODE_RESP 0x0D\n\n#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ    (0b000001)\n#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY      (0b000010)\n#define FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS  (0b001000)\n#define FELICA_SERVICE_ATTRIBUTE_CYCLIC         (0b001100)\n#define FELICA_SERVICE_ATTRIBUTE_PURSE          (0b010000)\n#define FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD (0b000110)\n\n/** @brief Type of possible Felica errors */\ntypedef enum {\n    FelicaErrorNone,\n    FelicaErrorNotPresent,\n    FelicaErrorColResFailed,\n    FelicaErrorBufferOverflow,\n    FelicaErrorCommunication,\n    FelicaErrorFieldOff,\n    FelicaErrorWrongCrc,\n    FelicaErrorProtocol,\n    FelicaErrorTimeout,\n    FelicaErrorFeatureUnsupported,\n} FelicaError;\n\ntypedef enum {\n    FelicaUnknown,\n    FelicaStandard,\n    FelicaLite,\n} FelicaWorkflowType;\n\ntypedef struct {\n    uint8_t data[FELICA_DATA_BLOCK_SIZE];\n} FelicaBlockData;\n\n/** @brief Separate type for card key block. Used in authentication process */\ntypedef struct {\n    uint8_t data[FELICA_DATA_BLOCK_SIZE];\n} FelicaCardKey;\n\n/** @brief In Felica there two types of auth. Internal is the first one, after\n  * which external became possible. Here are two flags representing which one\n  * was passed */\ntypedef struct {\n    bool internal : 1;\n    bool external : 1;\n} FelicaAuthenticationStatus;\n\n/** @brief Struct which controls the process of authentication and can be passed as\n  * a parameter to the application level. In order to force user to fill card key block data. */\ntypedef struct {\n    bool skip_auth; /**< By default it is true, so auth is skipped. By setting this to false several auth steps will be performed in order to pass auth*/\n    FelicaCardKey\n        card_key; /**< User must fill this field with known card key in order to pass auth*/\n    FelicaAuthenticationStatus auth_status; /**< Authentication status*/\n} FelicaAuthenticationContext;\n\n/**\n * @brief Stucture for holding Felica session key which is calculated from rc and ck.\n*/\ntypedef struct {\n    uint8_t data[FELICA_DATA_BLOCK_SIZE];\n} FelicaSessionKey;\n\n/**\n * @brief Structure used to hold authentication related fields.\n*/\ntypedef struct {\n    mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */\n    FelicaSessionKey session_key; /**< Calculated session key. */\n    FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */\n} FelicaAuthentication;\n\n/** @brief Felica ID block */\ntypedef struct {\n    uint8_t data[FELICA_IDM_SIZE];\n} FelicaIDm;\n\n/** @brief Felica PMm block */\ntypedef struct {\n    uint8_t data[FELICA_PMM_SIZE];\n} FelicaPMm;\n\n/** @brief Felica block with status flags indicating last operation with it.\n  * See Felica manual for more details on status codes. */\ntypedef struct {\n    uint8_t SF1; /**< Status flag 1, equals to 0 when success*/\n    uint8_t SF2; /**< Status flag 2, equals to 0 when success*/\n    uint8_t data[FELICA_DATA_BLOCK_SIZE]; /**< Block data */\n} FelicaBlock;\n\n/** @brief Felica filesystem structure */\ntypedef struct {\n    FelicaBlock spad[14];\n    FelicaBlock reg;\n    FelicaBlock rc;\n    FelicaBlock mac;\n    FelicaBlock id;\n    FelicaBlock d_id;\n    FelicaBlock ser_c;\n    FelicaBlock sys_c;\n    FelicaBlock ckv;\n    FelicaBlock ck;\n    FelicaBlock mc;\n    FelicaBlock wcnt;\n    FelicaBlock mac_a;\n    FelicaBlock state;\n    FelicaBlock crc_check;\n} FelicaFileSystem;\n\n/** @brief Union which represents filesystem in junction with plain data dump */\ntypedef union {\n    FelicaFileSystem fs;\n    uint8_t dump[sizeof(FelicaFileSystem)];\n} FelicaFSUnion;\n\ntypedef struct {\n    uint16_t code;\n    uint8_t attr;\n} FelicaService;\n\ntypedef struct {\n    uint16_t code;\n    uint16_t first_idx;\n    uint16_t last_idx;\n} FelicaArea;\n\ntypedef struct {\n    FelicaBlock block;\n    uint16_t service_code;\n    uint8_t block_idx;\n} FelicaPublicBlock;\n\ntypedef struct {\n    uint8_t system_code_idx;\n    uint16_t system_code;\n    SimpleArray* services;\n    SimpleArray* areas;\n    SimpleArray* public_blocks;\n} FelicaSystem;\n\n/** @brief Structure used to store Felica data and additional values about reading */\ntypedef struct {\n    FelicaIDm idm;\n    FelicaPMm pmm;\n    uint8_t blocks_total;\n    uint8_t blocks_read;\n    FelicaFSUnion data;\n\n    SimpleArray* systems;\n\n    FelicaWorkflowType workflow_type;\n} FelicaData;\n\ntypedef struct FURI_PACKED {\n    uint8_t code;\n    FelicaIDm idm;\n    uint8_t service_num;\n    uint16_t service_code;\n    uint8_t block_count;\n} FelicaCommandHeader;\n\ntypedef struct {\n    uint8_t length;\n    uint8_t response_code;\n    FelicaIDm idm;\n    uint8_t SF1;\n    uint8_t SF2;\n} FelicaCommandResponseHeader;\n\n#pragma pack(push, 1)\ntypedef struct {\n    uint8_t length;\n    uint8_t command;\n    FelicaIDm idm;\n} FelicaCommandHeaderRaw;\n#pragma pack(pop)\n\ntypedef struct {\n    uint8_t service_code : 4;\n    uint8_t access_mode  : 3;\n    uint8_t length       : 1;\n    uint8_t block_number;\n} FelicaBlockListElement;\n\ntypedef struct {\n    uint8_t length;\n    uint8_t response_code;\n    FelicaIDm idm;\n    uint8_t SF1;\n    uint8_t SF2;\n    uint8_t block_count;\n    uint8_t data[];\n} FelicaPollerReadCommandResponse;\n\ntypedef struct {\n    FelicaCommandResponseHeader header;\n    uint8_t block_count;\n    uint8_t data[];\n} FelicaListenerReadCommandResponse;\n\ntypedef struct {\n    FelicaCommandHeaderRaw header;\n    uint8_t data[];\n} FelicaListServiceCommandResponse;\n\ntypedef struct {\n    FelicaCommandHeaderRaw header;\n    uint8_t system_count;\n    uint8_t system_code[];\n} FelicaListSystemCodeCommandResponse;\n\ntypedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;\n\ntypedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;\n\nextern const NfcDeviceBase nfc_device_felica;\n\nFelicaData* felica_alloc(void);\n\nvoid felica_free(FelicaData* data);\n\nvoid felica_reset(FelicaData* data);\n\nvoid felica_copy(FelicaData* data, const FelicaData* other);\n\nbool felica_verify(FelicaData* data, const FuriString* device_type);\n\nbool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version);\n\nbool felica_save(const FelicaData* data, FlipperFormat* ff);\n\nbool felica_is_equal(const FelicaData* data, const FelicaData* other);\n\nconst char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len);\n\nbool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);\n\nFelicaData* felica_get_base_data(const FelicaData* data);\n\nvoid felica_calculate_session_key(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* rc,\n    uint8_t* out);\n\nbool felica_check_mac(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* blocks,\n    const uint8_t block_count,\n    uint8_t* data);\n\nvoid felica_calculate_mac_read(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* blocks,\n    const uint8_t block_count,\n    const uint8_t* data,\n    uint8_t* mac);\n\nvoid felica_calculate_mac_write(\n    mbedtls_des3_context* ctx,\n    const uint8_t* session_key,\n    const uint8_t* rc,\n    const uint8_t* wcnt,\n    const uint8_t* data,\n    uint8_t* mac);\n\nvoid felica_write_directory_tree(const FelicaSystem* system, FuriString* str);\n\nvoid felica_get_workflow_type(FelicaData* data);\n\nvoid felica_get_ic_name(const FelicaData* data, FuriString* ic_name);\n\nvoid felica_service_get_attribute_string(const FelicaService* service, FuriString* str);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_i.c",
    "content": "#include \"felica_i.h\"\n\nvoid felica_system_init(FelicaSystem* system) {\n    system->system_code = 0;\n    system->system_code_idx = 0;\n    system->services = simple_array_alloc(&felica_service_array_cfg);\n    system->areas = simple_array_alloc(&felica_area_array_cfg);\n    system->public_blocks = simple_array_alloc(&felica_public_block_array_cfg);\n}\n\nvoid felica_system_reset(FelicaSystem* system) {\n    furi_check(system);\n    system->system_code = 0;\n    system->system_code_idx = 0;\n    furi_check(system->services);\n    furi_check(system->areas);\n    furi_check(system->public_blocks);\n    simple_array_free(system->services);\n    simple_array_free(system->areas);\n    simple_array_free(system->public_blocks);\n    memset(system, 0, sizeof(FelicaSystem));\n}\n\nvoid felica_system_copy(FelicaSystem* system, const FelicaSystem* other) {\n    furi_check(system);\n    furi_check(other);\n    system->system_code = other->system_code;\n    system->system_code_idx = other->system_code_idx;\n    simple_array_copy(system->services, other->services);\n    simple_array_copy(system->areas, other->areas);\n    simple_array_copy(system->public_blocks, other->public_blocks);\n}\n\nconst SimpleArrayConfig felica_service_array_cfg = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(FelicaService),\n};\n\nconst SimpleArrayConfig felica_area_array_cfg = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(FelicaArea),\n};\n\nconst SimpleArrayConfig felica_public_block_array_cfg = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(FelicaPublicBlock),\n};\n\nconst SimpleArrayConfig felica_system_array_cfg = {\n    .init = (SimpleArrayInit)felica_system_init,\n    .copy = (SimpleArrayCopy)felica_system_copy,\n    .reset = (SimpleArrayReset)felica_system_reset,\n    .type_size = sizeof(FelicaSystem),\n};\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_i.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stddef.h>\n#include \"felica.h\"\n\n#include <lib/toolbox/simple_array.h>\n\nextern const SimpleArrayConfig felica_service_array_cfg;\nextern const SimpleArrayConfig felica_area_array_cfg;\nextern const SimpleArrayConfig felica_public_block_array_cfg;\nextern const SimpleArrayConfig felica_system_array_cfg;\n\nvoid felica_system_init(FelicaSystem* system);\nvoid felica_system_reset(FelicaSystem* system);\nvoid felica_system_copy(FelicaSystem* system, const FelicaSystem* other);\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_listener.c",
    "content": "#include \"felica_listener_i.h\"\n\n#include \"nfc/protocols/nfc_listener_base.h\"\n#include <nfc/helpers/felica_crc.h>\n#include <furi_hal_nfc.h>\n\n#define FELICA_LISTENER_MAX_BUFFER_SIZE     (128)\n#define FELICA_LISTENER_CMD_POLLING         (0x00U)\n#define FELICA_LISTENER_RESPONSE_POLLING    (0x01U)\n#define FELICA_LISTENER_RESPONSE_CODE_READ  (0x07)\n#define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09)\n\n#define FELICA_LISTENER_REQUEST_NONE        (0x00U)\n#define FELICA_LISTENER_REQUEST_SYSTEM_CODE (0x01U)\n#define FELICA_LISTENER_REQUEST_PERFORMANCE (0x02U)\n\n#define FELICA_LISTENER_SYSTEM_CODE_NDEF  (__builtin_bswap16(0x12FCU))\n#define FELICA_LISTENER_SYSTEM_CODE_LITES (__builtin_bswap16(0x88B4U))\n\n#define FELICA_LISTENER_PERFORMANCE_VALUE (__builtin_bswap16(0x0083U))\n\n#define TAG \"FelicaListener\"\n\nFelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) {\n    furi_assert(nfc);\n    furi_assert(data);\n\n    FelicaListener* instance = malloc(sizeof(FelicaListener));\n    instance->nfc = nfc;\n    instance->data = data;\n    instance->tx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE);\n\n    mbedtls_des3_init(&instance->auth.des_context);\n    nfc_set_fdt_listen_fc(instance->nfc, FELICA_FDT_LISTEN_FC);\n\n    memcpy(instance->mc_shadow.data, instance->data->data.fs.mc.data, FELICA_DATA_BLOCK_SIZE);\n    instance->data->data.fs.state.data[0] = 0;\n    nfc_config(instance->nfc, NfcModeListener, NfcTechFelica);\n    const uint16_t system_code = *(uint16_t*)data->data.fs.sys_c.data;\n    nfc_felica_listener_set_sensf_res_data(\n        nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm), system_code);\n\n    return instance;\n}\n\nvoid felica_listener_free(FelicaListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    free(instance);\n}\n\nvoid felica_listener_set_callback(\n    FelicaListener* listener,\n    NfcGenericCallback callback,\n    void* context) {\n    UNUSED(listener);\n    UNUSED(callback);\n    UNUSED(context);\n}\n\nconst FelicaData* felica_listener_get_data(const FelicaListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic FelicaError felica_listener_command_handler_read(\n    FelicaListener* instance,\n    const FelicaListenerGenericRequest* const generic_request) {\n    const FelicaListenerReadRequest* request = (FelicaListenerReadRequest*)generic_request;\n    FURI_LOG_D(TAG, \"Read cmd\");\n\n    FelicaListenerReadCommandResponse* resp = malloc(\n        sizeof(FelicaCommandResponseHeader) + 1 +\n        FELICA_LISTENER_READ_BLOCK_COUNT_MAX * FELICA_DATA_BLOCK_SIZE);\n\n    resp->header.response_code = FELICA_LISTENER_RESPONSE_CODE_READ;\n    resp->header.idm = request->base.header.idm;\n    resp->header.length = sizeof(FelicaCommandResponseHeader);\n\n    if(felica_listener_validate_read_request_and_set_sf(instance, request, &resp->header)) {\n        resp->block_count = request->base.header.block_count;\n        resp->header.length++;\n    } else {\n        resp->block_count = 0;\n    }\n\n    instance->mac_calc_start = 0;\n    memset(instance->requested_blocks, 0, sizeof(instance->requested_blocks));\n    const FelicaBlockListElement* item =\n        felica_listener_block_list_item_get_first(instance, request);\n    for(uint8_t i = 0; i < resp->block_count; i++) {\n        instance->requested_blocks[i] = item->block_number;\n        FelicaCommanReadBlockHandler handler =\n            felica_listener_get_read_block_handler(item->block_number);\n\n        handler(instance, item->block_number, i, resp);\n\n        item = felica_listener_block_list_item_get_next(instance, item);\n    }\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length);\n    free(resp);\n\n    return felica_listener_frame_exchange(instance, instance->tx_buffer);\n}\n\nstatic FelicaError felica_listener_command_handler_write(\n    FelicaListener* instance,\n    const FelicaListenerGenericRequest* const generic_request) {\n    FURI_LOG_D(TAG, \"Write cmd\");\n\n    const FelicaListenerWriteRequest* request = (FelicaListenerWriteRequest*)generic_request;\n    const FelicaListenerWriteBlockData* data_ptr =\n        felica_listener_get_write_request_data_pointer(instance, generic_request);\n\n    FelicaListenerWriteCommandResponse* resp = malloc(sizeof(FelicaListenerWriteCommandResponse));\n\n    resp->response_code = FELICA_LISTENER_RESPONSE_CODE_WRITE;\n    resp->idm = request->base.header.idm;\n    resp->length = sizeof(FelicaListenerWriteCommandResponse);\n\n    if(felica_listener_validate_write_request_and_set_sf(instance, request, data_ptr, resp)) {\n        const FelicaBlockListElement* item =\n            felica_listener_block_list_item_get_first(instance, request);\n        for(uint8_t i = 0; i < request->base.header.block_count; i++) {\n            FelicaCommandWriteBlockHandler handler =\n                felica_listener_get_write_block_handler(item->block_number);\n\n            handler(instance, item->block_number, &data_ptr->blocks[i]);\n\n            item = felica_listener_block_list_item_get_next(instance, item);\n        }\n        felica_wcnt_increment(instance->data);\n    }\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->length);\n    free(resp);\n\n    return felica_listener_frame_exchange(instance, instance->tx_buffer);\n}\n\nstatic FelicaError felica_listener_process_request(\n    FelicaListener* instance,\n    const FelicaListenerGenericRequest* generic_request) {\n    const uint8_t cmd_code = generic_request->header.code;\n    switch(cmd_code) {\n    case FELICA_CMD_READ_WITHOUT_ENCRYPTION:\n        return felica_listener_command_handler_read(instance, generic_request);\n    case FELICA_CMD_WRITE_WITHOUT_ENCRYPTION:\n        return felica_listener_command_handler_write(instance, generic_request);\n    default:\n        FURI_LOG_E(TAG, \"FeliCa incorrect command\");\n        return FelicaErrorNotPresent;\n    }\n}\n\nstatic void felica_listener_populate_polling_response_header(\n    FelicaListener* instance,\n    FelicaListenerPollingResponseHeader* resp) {\n    resp->idm = instance->data->idm;\n    resp->pmm = instance->data->pmm;\n    resp->response_code = FELICA_LISTENER_RESPONSE_POLLING;\n}\n\nstatic bool felica_listener_check_system_code(\n    const FelicaListenerGenericRequest* const generic_request,\n    uint16_t code) {\n    return (\n        generic_request->polling.system_code == code ||\n        generic_request->polling.system_code == (code | 0x00FFU) ||\n        generic_request->polling.system_code == (code | 0xFF00U));\n}\n\nstatic uint16_t felica_listener_get_response_system_code(\n    FelicaListener* instance,\n    const FelicaListenerGenericRequest* const generic_request) {\n    uint16_t resp_system_code = FELICA_SYSTEM_CODE_CODE;\n    if(felica_listener_check_system_code(generic_request, FELICA_LISTENER_SYSTEM_CODE_NDEF) &&\n       instance->data->data.fs.mc.data[FELICA_MC_SYS_OP] == 1) {\n        // NDEF\n        resp_system_code = FELICA_LISTENER_SYSTEM_CODE_NDEF;\n    } else if(felica_listener_check_system_code(\n                  generic_request, FELICA_LISTENER_SYSTEM_CODE_LITES)) {\n        // Lite-S\n        resp_system_code = FELICA_LISTENER_SYSTEM_CODE_LITES;\n    }\n    return resp_system_code;\n}\n\nstatic FelicaError felica_listener_process_system_code(\n    FelicaListener* instance,\n    const FelicaListenerGenericRequest* const generic_request) {\n    FelicaError result = FelicaErrorFeatureUnsupported;\n    do {\n        uint16_t resp_system_code =\n            felica_listener_get_response_system_code(instance, generic_request);\n        if(resp_system_code == FELICA_SYSTEM_CODE_CODE) break;\n\n        FelicaListenerPollingResponse* resp = malloc(sizeof(FelicaListenerPollingResponse));\n        felica_listener_populate_polling_response_header(instance, &resp->header);\n\n        resp->header.length = sizeof(FelicaListenerPollingResponse);\n        if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_SYSTEM_CODE) {\n            resp->optional_request_data = resp_system_code;\n        } else if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_PERFORMANCE) {\n            resp->optional_request_data = FELICA_LISTENER_PERFORMANCE_VALUE;\n        } else {\n            resp->header.length = sizeof(FelicaListenerPollingResponseHeader);\n        }\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length);\n        free(resp);\n\n        result = felica_listener_frame_exchange(instance, instance->tx_buffer);\n    } while(false);\n\n    return result;\n}\n\nNfcCommand felica_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    FelicaListener* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypeFieldOn) {\n        FURI_LOG_D(TAG, \"Field On\");\n    } else if(nfc_event->type == NfcEventTypeListenerActivated) {\n        instance->state = Felica_ListenerStateActivated;\n        FURI_LOG_D(TAG, \"Activated\");\n    } else if(nfc_event->type == NfcEventTypeFieldOff) {\n        instance->state = Felica_ListenerStateIdle;\n        FURI_LOG_D(TAG, \"Field Off\");\n        felica_listener_reset(instance);\n    } else if(nfc_event->type == NfcEventTypeRxEnd) {\n        FURI_LOG_D(TAG, \"Rx Done\");\n        do {\n            if(!felica_crc_check(nfc_event->data.buffer)) {\n                FURI_LOG_E(TAG, \"Wrong CRC\");\n                break;\n            }\n\n            FelicaListenerGenericRequest* request =\n                (FelicaListenerGenericRequest*)bit_buffer_get_data(nfc_event->data.buffer);\n\n            uint8_t size = bit_buffer_get_size_bytes(nfc_event->data.buffer) - 2;\n            if((request->length != size) ||\n               (!felica_listener_check_block_list_size(instance, request))) {\n                FURI_LOG_E(TAG, \"Wrong request length\");\n                break;\n            }\n\n            if(request->header.code == FELICA_LISTENER_CMD_POLLING) {\n                // Will always respond at Time Slot 0 for now.\n                nfc_felica_listener_timer_anticol_start(instance->nfc, 0);\n                if(request->polling.system_code != FELICA_SYSTEM_CODE_CODE) {\n                    FelicaError error = felica_listener_process_system_code(instance, request);\n                    if(error == FelicaErrorFeatureUnsupported) {\n                        command = NfcCommandReset;\n                    } else if(error != FelicaErrorNone) {\n                        FURI_LOG_E(\n                            TAG, \"Error when handling Polling with System Code: %2X\", error);\n                    }\n                    break;\n                } else {\n                    FURI_LOG_E(TAG, \"Hardware Polling command leaking through\");\n                    break;\n                }\n            } else if(!felica_listener_check_idm(instance, &request->header.idm)) {\n                FURI_LOG_E(TAG, \"Wrong IDm\");\n                break;\n            }\n\n            FelicaError error = felica_listener_process_request(instance, request);\n            if(error != FelicaErrorNone) {\n                FURI_LOG_E(TAG, \"Processing error: %2X\", error);\n            }\n        } while(false);\n        bit_buffer_reset(nfc_event->data.buffer);\n    }\n    return command;\n}\n\nconst NfcListenerBase nfc_listener_felica = {\n    .alloc = (NfcListenerAlloc)felica_listener_alloc,\n    .free = (NfcListenerFree)felica_listener_free,\n    .set_callback = (NfcListenerSetCallback)felica_listener_set_callback,\n    .get_data = (NfcListenerGetData)felica_listener_get_data,\n    .run = (NfcListenerRun)felica_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_listener.h",
    "content": "#pragma once\n\n#include \"felica.h\"\n#include <lib/nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct FelicaListener FelicaListener;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcListenerBase nfc_listener_felica;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_listener_i.c",
    "content": "#include \"felica_listener_i.h\"\n\n#include <nfc/helpers/felica_crc.h>\n\n#define FELICA_WCNT_MC2_FF_MAX_VALUE           (0x00FFFFFFU)\n#define FELICA_WCNT_MC2_00_MAX_VALUE           (0x00FFFE00U)\n#define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U)\n#define FELICA_WCNT_MC2_00_WARNING_END_VALUE   (0x00FFFDFFU)\n\n#define FELICA_MC_BYTE_GET(data, byte)      (data->data.fs.mc.data[byte])\n#define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00)\n#define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF)\n\n#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN ((uint8_t)sizeof(FelicaBlockListElement))\n#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item)                    \\\n    ((item->length == 1) ? FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN : \\\n                           (FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN + 1))\n\nstatic uint32_t felica_wcnt_get_max_value(const FelicaData* data) {\n    if(!FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && !FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) {\n        furi_crash(\"MC[2] reserved values are forbidden\");\n    }\n    return (FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) ? FELICA_WCNT_MC2_FF_MAX_VALUE :\n                                                   FELICA_WCNT_MC2_00_MAX_VALUE;\n}\n\nstatic bool felica_wcnt_check_warning_boundary(const FelicaData* data) {\n    const uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;\n    return FELICA_SYSTEM_BLOCK_RO_ACCESS(data) &&\n           ((*wcnt_ptr > FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE) &&\n            (*wcnt_ptr < FELICA_WCNT_MC2_00_WARNING_END_VALUE));\n}\n\nstatic bool felica_wcnt_check_error_boundary(const FelicaData* data) {\n    const uint32_t wcnt_max = felica_wcnt_get_max_value(data);\n    const uint32_t* wcnt_ptr = (const uint32_t*)data->data.fs.wcnt.data;\n    return *wcnt_ptr != wcnt_max;\n}\n\nvoid felica_wcnt_increment(FelicaData* data) {\n    furi_assert(data);\n\n    const uint32_t wcnt_max = felica_wcnt_get_max_value(data);\n    uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;\n    if(*wcnt_ptr < wcnt_max) {\n        *wcnt_ptr += 1;\n    }\n}\n\nstatic void felica_wcnt_post_process(FelicaData* data) {\n    uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;\n\n    if(FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && (*wcnt_ptr > FELICA_WCNT_MC2_00_MAX_VALUE)) {\n        *wcnt_ptr = 0;\n    }\n}\n\nstatic bool\n    felica_listener_block_list_item_validate_block_number(const FelicaBlockListElement* item) {\n    bool valid = true;\n    if(item->length == 0) {\n        uint8_t D2_block_number = *(&item->block_number + 1);\n        valid = D2_block_number == 0;\n    }\n    return valid;\n}\n\nstatic const FelicaBlockListElement* felica_listener_block_list_iterate(\n    FelicaListener* instance,\n    const FelicaBlockListElement* prev_item,\n    const uint8_t item_size) {\n    FelicaBlockListElement* next_item = NULL;\n    if(instance->request_size_buf >= FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN) {\n        next_item = (FelicaBlockListElement*)((uint8_t*)prev_item + item_size);\n\n        uint8_t next_item_size = FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(next_item);\n\n        if(instance->request_size_buf < next_item_size) {\n            next_item = NULL;\n            instance->request_size_buf = 0;\n        } else {\n            instance->request_size_buf -= next_item_size;\n        }\n    }\n\n    return next_item;\n}\n\nconst FelicaBlockListElement* felica_listener_block_list_item_get_first(\n    FelicaListener* instance,\n    const FelicaListenerRequest* request) {\n    furi_assert(instance);\n    furi_assert(request);\n\n    instance->request_size_buf = request->base.length - sizeof(FelicaListenerGenericRequest);\n    return felica_listener_block_list_iterate(instance, request->list, 0);\n}\n\nconst FelicaBlockListElement* felica_listener_block_list_item_get_next(\n    FelicaListener* instance,\n    const FelicaBlockListElement* prev_item) {\n    furi_assert(instance);\n    furi_assert(prev_item);\n\n    return felica_listener_block_list_iterate(\n        instance, prev_item, FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(prev_item));\n}\n\nconst FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer(\n    const FelicaListener* const instance,\n    const FelicaListenerGenericRequest* const generic_request) {\n    furi_assert(instance);\n    furi_assert(generic_request);\n\n    return (const FelicaListenerWriteBlockData*)((uint8_t*)generic_request +\n                                                 sizeof(FelicaListenerGenericRequest) +\n                                                 instance->block_list_size);\n}\n\nstatic bool felica_listener_check_write_request_data_size(\n    const FelicaListener* const instance,\n    const FelicaListenerRequest* request,\n    const uint8_t fact_item_cnt) {\n    uint8_t possible_data_size = fact_item_cnt * FELICA_DATA_BLOCK_SIZE;\n    uint8_t fact_data_size =\n        request->base.length - sizeof(FelicaListenerGenericRequest) - instance->block_list_size;\n    return possible_data_size <= fact_data_size;\n}\n\nstatic bool felica_listener_test_block_list_size_bounds(const FelicaListenerGenericRequest* req) {\n    bool valid = false;\n    if(req->header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) {\n        valid =\n            (req->header.block_count >= FELICA_LISTENER_READ_BLOCK_COUNT_MIN &&\n             req->header.block_count <= FELICA_LISTENER_READ_BLOCK_COUNT_MAX);\n    } else if(req->header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {\n        valid =\n            (req->header.block_count >= FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN &&\n             req->header.block_count <= FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX);\n    }\n\n    return valid;\n}\n\nstatic uint8_t felica_listener_get_block_list_item_count_size(\n    FelicaListener* instance,\n    FelicaListenerRequest* request) {\n    uint8_t list_size = 0;\n    uint8_t item_cnt = 0;\n\n    uint8_t max_item_cnt = (request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) ?\n                               FELICA_LISTENER_READ_BLOCK_COUNT_MAX :\n                               FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX;\n\n    const FelicaBlockListElement* item =\n        felica_listener_block_list_item_get_first(instance, request);\n\n    while((item != NULL) && (item_cnt < max_item_cnt)) {\n        item_cnt++;\n\n        if(request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {\n            if(instance->request_size_buf >= FELICA_DATA_BLOCK_SIZE) {\n                instance->request_size_buf -= FELICA_DATA_BLOCK_SIZE;\n            } else {\n                instance->request_size_buf = 0;\n            }\n        }\n\n        list_size += FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item);\n        item = felica_listener_block_list_item_get_next(instance, item);\n    }\n\n    instance->block_list_size = list_size;\n    return item_cnt;\n}\n\nbool felica_listener_check_block_list_size(\n    FelicaListener* instance,\n    FelicaListenerGenericRequest* req) {\n    furi_assert(instance);\n    furi_assert(req);\n\n    FelicaListenerRequest* request = (FelicaListenerRequest*)req;\n    bool valid = true;\n\n    uint8_t item_cnt = felica_listener_get_block_list_item_count_size(instance, request);\n    valid = (item_cnt > 0);\n\n    if(felica_listener_test_block_list_size_bounds(req) &&\n       (item_cnt < request->base.header.block_count)) {\n        valid = false;\n    }\n\n    if(valid && request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {\n        valid &= felica_listener_check_write_request_data_size(instance, request, item_cnt);\n    }\n\n    return valid;\n}\n\nbool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm) {\n    furi_assert(instance);\n    furi_assert(request_idm);\n\n    const FelicaIDm* idm = &instance->data->idm;\n    return memcmp(idm->data, request_idm->data, 8) == 0;\n}\n\nvoid felica_listener_reset(FelicaListener* instance) {\n    furi_assert(instance);\n\n    instance->auth.context.auth_status.internal = false;\n    instance->auth.context.auth_status.external = false;\n    instance->data->data.fs.state.data[0] = 0;\n    instance->rc_written = false;\n    memset(instance->auth.session_key.data, 0, FELICA_DATA_BLOCK_SIZE);\n\n    memcpy(instance->data->data.fs.mc.data, instance->mc_shadow.data, FELICA_DATA_BLOCK_SIZE);\n\n    felica_wcnt_post_process(instance->data);\n}\n\nstatic uint8_t felica_listener_get_block_index(uint8_t number) {\n    if(number >= FELICA_BLOCK_INDEX_RC && number < FELICA_BLOCK_INDEX_WCNT) {\n        return number - 0x80 + FELICA_BLOCK_INDEX_REG + 1;\n    } else if(number >= FELICA_BLOCK_INDEX_WCNT && number <= FELICA_BLOCK_INDEX_STATE) {\n        return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 1;\n    } else if(number == FELICA_BLOCK_INDEX_CRC_CHECK) {\n        return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 2;\n    }\n\n    return number;\n}\n\nstatic bool felica_block_exists(uint8_t number) {\n    return !(\n        (number > FELICA_BLOCK_INDEX_REG && number < FELICA_BLOCK_INDEX_RC) ||\n        (number > FELICA_BLOCK_INDEX_MC && number < FELICA_BLOCK_INDEX_WCNT) ||\n        (number > FELICA_BLOCK_INDEX_STATE && number < FELICA_BLOCK_INDEX_CRC_CHECK) ||\n        (number > FELICA_BLOCK_INDEX_CRC_CHECK));\n}\n\nstatic bool\n    felica_get_mc_bit(const FelicaListener* instance, uint8_t byte_index, uint8_t bit_number) {\n    uint8_t* mc = instance->data->data.fs.mc.data;\n\n    uint16_t flags = *((uint16_t*)&mc[byte_index]);\n    bool bit = (flags & (1 << bit_number)) != 0;\n    return bit;\n}\n\nstatic bool felica_block_requires_auth(\n    const FelicaListener* instance,\n    uint8_t command,\n    uint8_t block_number) {\n    bool need_auth = false;\n    if(block_number <= FELICA_BLOCK_INDEX_REG) {\n        uint8_t mc_flag_index = command;\n        need_auth = felica_get_mc_bit(instance, mc_flag_index, block_number);\n    }\n    return need_auth;\n}\n\nstatic bool felica_block_is_readonly(const FelicaListener* instance, uint8_t block_number) {\n    bool readonly = false;\n    if(block_number <= FELICA_BLOCK_INDEX_REG) {\n        readonly = !felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, block_number);\n    } else if(\n        ((block_number == FELICA_BLOCK_INDEX_ID) || (block_number == FELICA_BLOCK_INDEX_SER_C)) ||\n        (block_number >= FELICA_BLOCK_INDEX_CKV && block_number <= FELICA_BLOCK_INDEX_CK)) {\n        const FelicaData* data = instance->data;\n        readonly = FELICA_SYSTEM_BLOCK_RO_ACCESS(data);\n    } else if(\n        (block_number == FELICA_BLOCK_INDEX_MAC) || (block_number == FELICA_BLOCK_INDEX_WCNT) ||\n        (block_number == FELICA_BLOCK_INDEX_D_ID) ||\n        (block_number == FELICA_BLOCK_INDEX_CRC_CHECK)) {\n        readonly = true;\n    }\n    return readonly;\n}\n\nstatic bool felica_block_requires_mac(const FelicaListener* instance, uint8_t block_number) {\n    bool need_mac = false;\n    if(block_number <= FELICA_BLOCK_INDEX_REG) {\n        need_mac = felica_get_mc_bit(instance, FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11, block_number);\n    } else if(block_number == FELICA_BLOCK_INDEX_CK || block_number == FELICA_BLOCK_INDEX_CKV) {\n        need_mac = felica_get_mc_bit(instance, FELICA_MC_CKCKV_W_MAC_A, 0);\n    } else if(block_number == FELICA_BLOCK_INDEX_STATE) {\n        need_mac = felica_get_mc_bit(instance, FELICA_MC_STATE_W_MAC_A, 0);\n    }\n    return need_mac;\n}\n\nstatic void felica_handler_read_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const uint8_t resp_data_index,\n    FelicaListenerReadCommandResponse* response) {\n    uint8_t num = felica_listener_get_block_index(block_number);\n    memcpy(\n        &response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE],\n        &instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2],\n        FELICA_DATA_BLOCK_SIZE);\n    response->header.length += FELICA_DATA_BLOCK_SIZE;\n}\n\nstatic void felica_handler_read_all_zeros(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const uint8_t resp_data_index,\n    FelicaListenerReadCommandResponse* response) {\n    UNUSED(instance);\n    UNUSED(block_number);\n\n    memset(&response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE], 0, FELICA_DATA_BLOCK_SIZE);\n    response->header.length += FELICA_DATA_BLOCK_SIZE;\n}\n\nstatic void felica_handler_read_mac_a_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const uint8_t resp_data_index,\n    FelicaListenerReadCommandResponse* response) {\n    if((resp_data_index != response->block_count - 1) || (resp_data_index == 0)) {\n        felica_handler_read_all_zeros(instance, block_number, resp_data_index, response);\n        instance->mac_calc_start = resp_data_index + 1;\n    } else {\n        felica_calculate_mac_read(\n            &instance->auth.des_context,\n            instance->auth.session_key.data,\n            instance->data->data.fs.rc.data,\n            &instance->requested_blocks[instance->mac_calc_start],\n            response->block_count - instance->mac_calc_start,\n            &response->data[instance->mac_calc_start * FELICA_DATA_BLOCK_SIZE],\n            instance->data->data.fs.mac_a.data);\n        felica_handler_read_block(instance, block_number, resp_data_index, response);\n    }\n}\n\nFelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number) {\n    FelicaCommanReadBlockHandler handler = felica_handler_read_block;\n\n    if(block_number == FELICA_BLOCK_INDEX_RC || block_number == FELICA_BLOCK_INDEX_CK) {\n        handler = felica_handler_read_all_zeros;\n    } else if(block_number == FELICA_BLOCK_INDEX_MAC_A) {\n        handler = felica_handler_read_mac_a_block;\n    }\n\n    return handler;\n}\n\nstatic bool felica_validate_read_block_list(\n    FelicaListener* instance,\n    const FelicaListenerReadRequest* const request,\n    FelicaCommandResponseHeader* response) {\n    uint8_t mac_a_pos = 0;\n    bool mac_a_present = false, mac_present = false;\n\n    const FelicaBlockListElement* item =\n        felica_listener_block_list_item_get_first(instance, request);\n    for(uint8_t i = 0; i < request->base.header.block_count; i++) {\n        if(item->service_code != 0) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA3;\n            return false;\n        } else if(item->access_mode != 0) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA7;\n            return false;\n        } else if(\n            !felica_listener_block_list_item_validate_block_number(item) ||\n            !felica_block_exists(item->block_number) ||\n            (felica_block_is_readonly(instance, item->block_number) &&\n             request->base.header.service_code != FELICA_SERVICE_RO_ACCESS)) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA8;\n            return false;\n        } else if(item->block_number == FELICA_BLOCK_INDEX_MAC) {\n            mac_present = true;\n        } else if(item->block_number == FELICA_BLOCK_INDEX_MAC_A) {\n            if(!instance->rc_written) {\n                response->SF1 = (1 << i);\n                response->SF2 = 0xB2;\n                return false;\n            }\n            if(!mac_a_present) {\n                mac_a_present = true;\n                mac_a_pos = i;\n            }\n        } else if(\n            felica_block_requires_auth(instance, request->base.header.code, item->block_number) &&\n            !instance->auth.context.auth_status.external) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xB1;\n            return false;\n        }\n\n        if(mac_a_present && mac_present) {\n            response->SF1 = (1 << mac_a_pos);\n            response->SF2 = 0xB0;\n            return false;\n        }\n\n        item = felica_listener_block_list_item_get_next(instance, item);\n    }\n    return true;\n}\n\nbool felica_listener_validate_read_request_and_set_sf(\n    FelicaListener* instance,\n    const FelicaListenerReadRequest* const request,\n    FelicaCommandResponseHeader* resp_header) {\n    furi_assert(instance);\n    furi_assert(request);\n    furi_assert(resp_header);\n\n    bool valid = false;\n    do {\n        if(request->base.header.service_num != 0x01) {\n            resp_header->SF1 = 0xFF;\n            resp_header->SF2 = 0xA1;\n            break;\n        }\n        if((request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) &&\n           (request->base.header.block_count < FELICA_LISTENER_READ_BLOCK_COUNT_MIN ||\n            request->base.header.block_count > FELICA_LISTENER_READ_BLOCK_COUNT_MAX)) {\n            resp_header->SF1 = 0xFF;\n            resp_header->SF2 = 0xA2;\n            break;\n        }\n\n        if(request->base.header.service_code != FELICA_SERVICE_RO_ACCESS &&\n           request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) {\n            resp_header->SF1 = 0x01;\n            resp_header->SF2 = 0xA6;\n            break;\n        }\n\n        if(!felica_validate_read_block_list(instance, request, resp_header)) break;\n\n        resp_header->SF1 = 0x00;\n        resp_header->SF2 = 0x00;\n        valid = true;\n    } while(false);\n\n    return valid;\n}\n\nstatic bool felica_validate_write_block_list(\n    FelicaListener* instance,\n    const FelicaListenerWriteRequest* const request,\n    const FelicaListenerWriteBlockData* const data,\n    FelicaListenerWriteCommandResponse* response) {\n    const FelicaBlockListElement* items[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX] = {};\n    items[0] = felica_listener_block_list_item_get_first(instance, request);\n    items[1] = felica_listener_block_list_item_get_next(instance, items[0]);\n\n    bool write_with_mac = false;\n    if(request->base.header.block_count == FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX &&\n       items[1]->block_number == FELICA_BLOCK_INDEX_MAC_A) {\n        write_with_mac = true;\n    } else if(\n        request->base.header.block_count < FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN ||\n        request->base.header.block_count > FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX) {\n        response->SF1 = 0x02;\n        response->SF2 = 0xB2;\n        return false;\n    }\n\n    for(uint8_t i = 0; i < request->base.header.block_count; i++) {\n        const FelicaBlockListElement* item = items[i];\n        if(!felica_listener_block_list_item_validate_block_number(item) ||\n           !felica_block_exists(item->block_number) ||\n           ((i == 1) && (item->block_number != FELICA_BLOCK_INDEX_MAC_A))) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA8;\n            return false;\n        }\n\n        if(felica_block_is_readonly(instance, item->block_number) ||\n           (felica_block_requires_mac(instance, item->block_number) && !write_with_mac) ||\n           ((i == 0) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A))) {\n            response->SF1 = 0x01;\n            response->SF2 = 0xA8;\n            return false;\n        }\n\n        if(item->service_code != 0) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA3;\n            return false;\n        } else if(item->access_mode != 0) {\n            response->SF1 = (1 << i);\n            response->SF2 = 0xA7;\n            return false;\n        } else if((i == 1) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A)) {\n            uint8_t calculated_mac[8];\n            felica_calculate_mac_write(\n                &instance->auth.des_context,\n                instance->auth.session_key.data,\n                instance->data->data.fs.rc.data,\n                data->blocks[1].data + 8,\n                data->blocks[0].data,\n                calculated_mac);\n\n            if(!instance->rc_written || (memcmp(calculated_mac, data->blocks[1].data, 8) != 0) ||\n               !felica_wcnt_check_error_boundary(instance->data)) {\n                response->SF1 = 0x02;\n                response->SF2 = 0xB2;\n                return false;\n            }\n        } else if(\n            felica_block_requires_auth(instance, request->base.header.code, item->block_number) &&\n            !instance->auth.context.auth_status.external) {\n            response->SF1 = 0x01;\n            response->SF2 = 0xB1;\n            return false;\n        }\n    }\n    return true;\n}\n\nbool felica_listener_validate_write_request_and_set_sf(\n    FelicaListener* instance,\n    const FelicaListenerWriteRequest* const request,\n    const FelicaListenerWriteBlockData* const data,\n    FelicaListenerWriteCommandResponse* response) {\n    furi_assert(instance);\n    furi_assert(request);\n    furi_assert(data);\n    furi_assert(response);\n\n    bool valid = false;\n    do {\n        if(request->base.header.service_num != 0x01) {\n            response->SF1 = 0xFF;\n            response->SF2 = 0xA1;\n            break;\n        }\n\n        if((request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) &&\n           (request->base.header.block_count < 0x01 || request->base.header.block_count > 0x02)) {\n            response->SF1 = 0xFF;\n            response->SF2 = 0xA2;\n            break;\n        }\n\n        if(request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) {\n            response->SF1 = 0x01;\n            response->SF2 = 0xA6;\n            break;\n        }\n\n        if(!felica_validate_write_block_list(instance, request, data, response)) break;\n\n        if(felica_wcnt_check_warning_boundary(instance->data)) {\n            response->SF1 = 0xFF;\n            response->SF2 = 0x71;\n        } else {\n            response->SF1 = 0x00;\n            response->SF2 = 0x00;\n        }\n        valid = true;\n    } while(false);\n    return valid;\n}\n\nstatic void felica_handler_write_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    uint8_t num = felica_listener_get_block_index(block_number);\n\n    memcpy(\n        &instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2],\n        data_block->data,\n        FELICA_DATA_BLOCK_SIZE);\n}\n\nstatic void felica_handler_write_rc_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    felica_handler_write_block(instance, block_number, data_block);\n\n    felica_calculate_session_key(\n        &instance->auth.des_context,\n        instance->data->data.fs.ck.data,\n        instance->data->data.fs.rc.data,\n        instance->auth.session_key.data);\n    instance->rc_written = true;\n}\n\nstatic void felica_handler_write_reg_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    UNUSED(block_number);\n\n    typedef struct {\n        uint32_t A;\n        uint32_t B;\n        uint64_t C;\n    } FELICA_REG_BLOCK;\n\n    FELICA_REG_BLOCK* Reg = (FELICA_REG_BLOCK*)instance->data->data.fs.reg.data;\n    FELICA_REG_BLOCK* RegNew = (FELICA_REG_BLOCK*)data_block->data;\n\n    if(Reg->A >= RegNew->A) {\n        Reg->A = RegNew->A;\n    }\n\n    if(Reg->B >= RegNew->B) {\n        Reg->B = RegNew->B;\n    }\n\n    if((Reg->A >= RegNew->A) && (Reg->B >= RegNew->B)) {\n        Reg->C = RegNew->C;\n    }\n}\n\nstatic void felica_handler_write_mc_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    UNUSED(block_number);\n\n    bool mc_bits_permission = felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, 15);\n    const FelicaData* data = instance->data;\n    bool mc_system_block_permission = FELICA_SYSTEM_BLOCK_RW_ACCESS(data);\n    for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++) {\n        if((i <= 1) && mc_bits_permission) {\n            instance->mc_shadow.data[i] &= data_block->data[i];\n        } else if(\n            (i >= FELICA_MC_ALL_BYTE && i <= FELICA_MC_CKCKV_W_MAC_A) &&\n            (mc_system_block_permission)) {\n            instance->mc_shadow.data[i] = data_block->data[i];\n        } else if(\n            (i >= FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 && i <= FELICA_MC_STATE_W_MAC_A) &&\n            (mc_bits_permission)) {\n            instance->mc_shadow.data[i] |= data_block->data[i];\n        } else if(i >= FELICA_MC_RESERVED_13) {\n            instance->mc_shadow.data[i] = 0;\n        }\n    }\n}\n\nstatic void felica_handler_write_state_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    felica_handler_write_block(instance, block_number, data_block);\n    bool state = instance->data->data.fs.state.data[0] == 0x01;\n    instance->auth.context.auth_status.external = state;\n    instance->auth.context.auth_status.internal = state;\n}\n\nstatic void felica_handler_write_id_block(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block) {\n    UNUSED(block_number);\n    memcpy(&instance->data->data.fs.id.data[8], &data_block->data[8], 8);\n}\n\nFelicaCommandWriteBlockHandler\n    felica_listener_get_write_block_handler(const uint8_t block_number) {\n    FelicaCommandWriteBlockHandler handler = felica_handler_write_block;\n    switch(block_number) {\n    case FELICA_BLOCK_INDEX_RC:\n        handler = felica_handler_write_rc_block;\n        break;\n    case FELICA_BLOCK_INDEX_REG:\n        handler = felica_handler_write_reg_block;\n        break;\n    case FELICA_BLOCK_INDEX_MC:\n        handler = felica_handler_write_mc_block;\n        break;\n    case FELICA_BLOCK_INDEX_STATE:\n        handler = felica_handler_write_state_block;\n        break;\n    case FELICA_BLOCK_INDEX_ID:\n        handler = felica_handler_write_id_block;\n        break;\n    default:\n        handler = felica_handler_write_block;\n        break;\n    }\n    return handler;\n}\n\nstatic FelicaError felica_listener_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return FelicaErrorNone;\n    case NfcErrorTimeout:\n        return FelicaErrorTimeout;\n    default:\n        return FelicaErrorNotPresent;\n    }\n}\n\nFelicaError\n    felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n\n    const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);\n    furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE);\n\n    felica_crc_append(instance->tx_buffer);\n\n    FelicaError ret = FelicaErrorNone;\n\n    do {\n        NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);\n        if(error != NfcErrorNone) {\n            ret = felica_listener_process_error(error);\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_listener_i.h",
    "content": "#include \"felica_listener.h\"\n\n#include <nfc/protocols/nfc_generic_event.h>\n\n#define FELICA_LISTENER_READ_BLOCK_COUNT_MAX  (4U)\n#define FELICA_LISTENER_READ_BLOCK_COUNT_MIN  (1U)\n#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U)\n#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U)\n\n#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1    (0U)\n#define FELICA_MC_ALL_BYTE                   (2U)\n#define FELICA_MC_SYS_OP                     (3U)\n#define FELICA_MC_RF_PRM                     (4U)\n#define FELICA_MC_CKCKV_W_MAC_A              (5U)\n#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7   (6U)\n#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9   (8U)\n#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)\n#define FELICA_MC_STATE_W_MAC_A              (12U)\n#define FELICA_MC_RESERVED_13                (13U)\n#define FELICA_MC_RESERVED_14                (14U)\n#define FELICA_MC_RESERVED_15                (15U)\n\ntypedef enum {\n    Felica_ListenerStateIdle,\n    Felica_ListenerStateActivated,\n} FelicaListenerState;\n\ntypedef struct FURI_PACKED {\n    uint8_t code;\n    uint16_t system_code;\n    uint8_t request_code;\n    uint8_t time_slot;\n} FelicaListenerPollingHeader;\n\ntypedef struct {\n    uint8_t length;\n    uint8_t response_code;\n    FelicaIDm idm;\n    FelicaPMm pmm;\n} FelicaListenerPollingResponseHeader;\n\ntypedef struct FURI_PACKED {\n    FelicaListenerPollingResponseHeader header;\n    uint16_t optional_request_data;\n} FelicaListenerPollingResponse;\n\n/** Generic Felica request same for both read and write operations. */\ntypedef struct {\n    uint8_t length;\n    union {\n        FelicaCommandHeader header;\n        FelicaListenerPollingHeader polling;\n    };\n} FelicaListenerGenericRequest;\n\n/** Generic request but with list of requested elements. */\ntypedef struct {\n    FelicaListenerGenericRequest base;\n    FelicaBlockListElement list[];\n} FelicaListenerRequest;\n\ntypedef FelicaListenerRequest FelicaListenerReadRequest;\ntypedef FelicaListenerRequest FelicaListenerWriteRequest;\n\n/** Struct for write request data.\n *\n * All data are placed right after @ref FelicaListenerRequest data.\n */\ntypedef struct {\n    FelicaBlockData blocks[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX];\n} FelicaListenerWriteBlockData;\n\n/** Write command handler signature.\n *\n * Depending on requested block write behaviour can be different, therefore\n * different handlers are used.\n */\ntypedef void (*FelicaCommandWriteBlockHandler)(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const FelicaBlockData* data_block);\n\n/** Read command handler signature.\n *\n * Depending on requested block read behaviour can be different, therefore\n * different handlers are used.\n */\ntypedef void (*FelicaCommanReadBlockHandler)(\n    FelicaListener* instance,\n    const uint8_t block_number,\n    const uint8_t resp_data_index,\n    FelicaListenerReadCommandResponse* response);\n\nstruct FelicaListener {\n    Nfc* nfc;\n    FelicaData* data;\n    FelicaListenerState state;\n    FelicaAuthentication auth;\n    FelicaBlockData mc_shadow;\n\n    uint8_t request_size_buf;\n    uint8_t block_list_size;\n    uint8_t requested_blocks[FELICA_LISTENER_READ_BLOCK_COUNT_MAX];\n    uint8_t mac_calc_start;\n    bool rc_written;\n\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    NfcGenericEvent generic_event;\n    NfcGenericCallback callback;\n    void* context;\n};\n\n/** Resets card authentication state after field off, also resets session key and\n * perform post process operations with some blocks.\n *\n * @param      instance  pointer to the listener instance to be used.\n */\nvoid felica_listener_reset(FelicaListener* instance);\n\n/** Performs WCNT increasing in case of write operation.\n *\n * @param      data  pointer to Felica card data.\n */\nvoid felica_wcnt_increment(FelicaData* data);\n\n/** Compares IDm of card loaded for emulation with IDm from request.\n *\n * @param      instance     pointer to the listener instance to be used.\n * @param      request_idm  pointer to IDm block from request structure.\n *\n * @return     True if IDms' are equal, otherwise false.\n */\nbool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm);\n\n/** This is the first request validation function.\n *\n * Its main aim is to check whether the input request is valid in general by\n * counting the amount of data in request. Function iterates through block list\n * elements and data, counts real amount of elements and real amount of\n * coresponding elements data (in case of write operation). If block list\n * element amount is invalid or request data is missing, such request will be\n * ignored.\n *\n * @param      instance  pointer to the listener instance to be used.\n * @param      request   pointer to received request.\n *\n * @return     True if block element list contains valid amount of data,\n *             otherwise false.\n */\nbool felica_listener_check_block_list_size(\n    FelicaListener* instance,\n    FelicaListenerGenericRequest* request);\n\n/** Used to take first element from block element list.\n *\n * Each element in block list can be 2 or 3-bytes length and they can be mixed\n * together. Therefore before returning the element, function checks whether\n * there is enough bytes in the request for this element, in order to avoid\n * memory casting behind the request block.\n *\n * @param      instance  pointer to the listener instance to be used.\n * @param      request   pointer to received request.\n *\n * @return     Pointer to the first element of the list or NULL if there is not\n *             enough bytes in the request.\n */\nconst FelicaBlockListElement* felica_listener_block_list_item_get_first(\n    FelicaListener* instance,\n    const FelicaListenerRequest* request);\n\n/** Used to take next element from block element list.\n *\n * It uses pointer to the previous element, takes its length and calculates\n * pointer to the next element if there is enough bytes left.\n *\n * @param      instance   pointer to the listener instance to be used.\n * @param      prev_item  pointer to the previous item taken.\n *\n * @return     Pointer to the next element of the list or NULL if there is not\n *             enough bytes in the request.\n */\nconst FelicaBlockListElement* felica_listener_block_list_item_get_next(\n    FelicaListener* instance,\n    const FelicaBlockListElement* prev_item);\n\n/** Calculates pointer to data blocks in case of write operation, because block\n * list elements size can vary.\n *\n * For calculation it uses previously calculated block list size and\n * FelicaListenerGenericRequest size.\n *\n * @param      instance         pointer to the listener instance to be used.\n * @param      generic_request  pointer to received request.\n *\n * @return     Pointer to data blocks for write operation.\n */\nconst FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer(\n    const FelicaListener* const instance,\n    const FelicaListenerGenericRequest* const generic_request);\n\n/** Function validates write request data and sets Felica SF1 and SF2 flags\n * directly to the response.\n *\n * When something is wrong with data in the request (for example non-existing\n * block is requested) this function sets proper SF1 and SF2 flags which will be\n * then send to reader. When every thing is fine SF1 and SF2 are 0. Also this\n * function performs authentiocation checks by MAC_A calculation if request\n * contains MAC_A.\n *\n * @param      instance  pointer to the listener instance to be used.\n * @param      request   pointer to received write request.\n * @param      data      pointer to data which request wants to write.\n * @param      response  pointer to the response to where SF1 and SF2 flags will\n *                       be populated.\n *\n * @return     True if request is fine also SF1 and SF2 will be 0. False if\n *             something is wrong and SF1, SF2 will provide more detailed\n *             information about the error.\n */\nbool felica_listener_validate_write_request_and_set_sf(\n    FelicaListener* instance,\n    const FelicaListenerWriteRequest* const request,\n    const FelicaListenerWriteBlockData* const data,\n    FelicaListenerWriteCommandResponse* response);\n\n/** Function validates read request data and sets Felica SF1 and SF2 flags\n * directly to the response.\n *\n * When something is wrong with data in the request (for example non-existing\n * block is requested) this function sets proper SF1 and SF2 flags which will be\n * then send to reader. In such case there will be no any data in the request,\n * only flags. In case when request is fine SF1 and SF2 will be 0 and data will\n * be populated to the response after them. Attention! This function doesn't\n * populate any data, it only allows further processing where those data will be\n * populated.\n *\n * @param      instance     pointer to the listener instance to be used.\n * @param      request      pointer to received write request.\n * @param      resp_header  pointer to the response to where SF1 and SF2 and\n *                          data flags will be populated.\n *\n * @return     True if request is fine also SF1 and SF2 will be 0. False if\n *             something is wrong and SF1, SF2 will provide more detailed\n *             information about the error.\n */\nbool felica_listener_validate_read_request_and_set_sf(\n    FelicaListener* instance,\n    const FelicaListenerReadRequest* const request,\n    FelicaCommandResponseHeader* resp_header);\n\n/** Function returns appropiate block handler for processing read operation\n * depending on number of block.\n *\n * @param      block_number  number of block.\n *\n * @return     pointer to block handler function.\n */\nFelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number);\n\n/** Function returns appropiate block handler for processing write operation\n * depending on number of block.\n *\n * @param      block_number  number of block.\n *\n * @return     pointer to block handler function.\n */\nFelicaCommandWriteBlockHandler felica_listener_get_write_block_handler(const uint8_t block_number);\n\n/** Sends response data from buffer to reader.\n *\n * @param      instance   pointer to the listener instance to be used.\n * @param      tx_buffer  buffer with response data.\n *\n * @return     error code or FelicaErrorNone.\n */\nFelicaError\n    felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer);\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller.c",
    "content": "#include \"felica_poller_i.h\"\n#include <mlib/m-array.h>\n#include <mlib/m-core.h>\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#define TAG \"FelicaPoller\"\n\nARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658\nARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658\nARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658\nARRAY_DEF(felica_system_array, FelicaSystem, M_POD_OPLIST); // -V658\n\ntypedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);\n\nconst FelicaData* felica_poller_get_data(FelicaPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic FelicaPoller* felica_poller_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n\n    FelicaPoller* instance = malloc(sizeof(FelicaPoller));\n    instance->nfc = nfc;\n    instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);\n\n    nfc_config(instance->nfc, NfcModePoller, NfcTechFelica);\n    nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);\n    nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);\n    nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);\n\n    mbedtls_des3_init(&instance->auth.des_context);\n    instance->data = felica_alloc();\n\n    instance->felica_event.data = &instance->felica_event_data;\n    instance->general_event.protocol = NfcProtocolFelica;\n    instance->general_event.event_data = &instance->felica_event;\n    instance->general_event.instance = instance;\n\n    instance->systems_read = 0;\n    instance->systems_total = 0;\n\n    return instance;\n}\n\nstatic void felica_poller_free(FelicaPoller* instance) {\n    furi_assert(instance);\n\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(instance->data);\n\n    mbedtls_des3_free(&instance->auth.des_context);\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    felica_free(instance->data);\n    free(instance);\n}\n\nstatic void\n    felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nNfcCommand felica_poller_state_handler_idle(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Idle\");\n    felica_reset(instance->data);\n    instance->state = FelicaPollerStateActivated;\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Activate\");\n\n    NfcCommand command = NfcCommandContinue;\n\n    FelicaError error = felica_poller_activate(instance, instance->data);\n    if(error == FelicaErrorNone) {\n        furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);\n        felica_get_workflow_type(instance->data);\n\n        instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;\n        instance->felica_event_data.auth_context = &instance->auth.context;\n\n        instance->callback(instance->general_event, instance->context);\n\n        switch(instance->data->workflow_type) {\n        case FelicaStandard:\n            instance->state = FelicaPollerStateListSystem;\n            break;\n        case FelicaLite:\n            instance->state = FelicaPollerStateReadLiteBlocks;\n            break;\n        default:\n            // Unimplemented\n            instance->state = FelicaPollerStateReadSuccess;\n            break;\n        }\n\n        bool skip_auth = instance->auth.context.skip_auth;\n        if(!skip_auth) {\n            instance->state = FelicaPollerStateAuthenticateInternal;\n        }\n    } else if(error != FelicaErrorTimeout) {\n        instance->felica_event.type = FelicaPollerEventTypeError;\n        instance->felica_event_data.error = error;\n        instance->state = FelicaPollerStateReadFailed;\n    }\n    return command;\n}\n\nNfcCommand felica_poller_state_handler_list_system(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"List System\");\n\n    NfcCommand command = NfcCommandContinue;\n\n    FelicaListSystemCodeCommandResponse* response_system_code;\n    FelicaError error = felica_poller_list_system_code(instance, &response_system_code);\n\n    instance->systems_total = response_system_code->system_count;\n    simple_array_init(instance->data->systems, instance->systems_total);\n    uint8_t* system_codes = response_system_code->system_code;\n\n    for(uint8_t i = 0; i < instance->systems_total; i++) {\n        FelicaSystem* system = simple_array_get(instance->data->systems, i);\n        system->system_code = system_codes[i * 2] << 8 | system_codes[i * 2 + 1];\n        system->system_code_idx = i;\n    }\n\n    if(error == FelicaErrorNone) {\n        instance->state = FelicaPollerStateSelectSystemIndex;\n    } else if(error != FelicaErrorTimeout) {\n        instance->felica_event.type = FelicaPollerEventTypeError;\n        instance->felica_event_data.error = error;\n        instance->state = FelicaPollerStateReadFailed;\n    }\n    return command;\n}\n\nNfcCommand felica_poller_state_handler_select_system_idx(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Select System Index %d\", instance->systems_read);\n    uint8_t system_index_mask = instance->systems_read << 4;\n    instance->data->idm.data[0] &= 0x0F;\n    instance->data->idm.data[0] |= system_index_mask;\n    instance->state = FelicaPollerStateTraverseStandardSystem;\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Auth Internal\");\n\n    felica_calculate_session_key(\n        &instance->auth.des_context,\n        instance->auth.context.card_key.data,\n        instance->data->data.fs.rc.data,\n        instance->auth.session_key.data);\n\n    switch(instance->data->workflow_type) {\n    case FelicaStandard:\n        instance->state = FelicaPollerStateTraverseStandardSystem;\n        break;\n    case FelicaLite:\n        instance->state = FelicaPollerStateReadLiteBlocks;\n        break;\n    default:\n        // Unimplemented\n        instance->state = FelicaPollerStateReadSuccess;\n        break;\n    }\n\n    uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};\n    FelicaPollerWriteCommandResponse* tx_resp;\n    do {\n        FelicaError error = felica_poller_write_blocks(\n            instance, 1, blocks, instance->data->data.fs.rc.data, &tx_resp);\n        if((error != FelicaErrorNone) || (tx_resp->SF1 != 0) || (tx_resp->SF2 != 0)) break;\n\n        blocks[0] = FELICA_BLOCK_INDEX_ID;\n        blocks[1] = FELICA_BLOCK_INDEX_WCNT;\n        blocks[2] = FELICA_BLOCK_INDEX_MAC_A;\n        FelicaPollerReadCommandResponse* rx_resp;\n        error = felica_poller_read_blocks(\n            instance, sizeof(blocks), blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp);\n        if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;\n\n        if(felica_check_mac(\n               &instance->auth.des_context,\n               instance->auth.session_key.data,\n               instance->data->data.fs.rc.data,\n               blocks,\n               rx_resp->block_count,\n               rx_resp->data)) {\n            instance->auth.context.auth_status.internal = true;\n            instance->data->data.fs.wcnt.SF1 = 0;\n            instance->data->data.fs.wcnt.SF2 = 0;\n            memcpy(\n                instance->data->data.fs.wcnt.data,\n                rx_resp->data + FELICA_DATA_BLOCK_SIZE,\n                FELICA_DATA_BLOCK_SIZE);\n            instance->state = FelicaPollerStateAuthenticateExternal;\n        }\n    } while(false);\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Auth External\");\n    uint8_t blocks[2];\n\n    instance->data->data.fs.state.data[0] = 1;\n    FelicaAuthentication* auth = &instance->auth;\n    felica_calculate_mac_write(\n        &auth->des_context,\n        auth->session_key.data,\n        instance->data->data.fs.rc.data,\n        instance->data->data.fs.wcnt.data,\n        instance->data->data.fs.state.data,\n        instance->data->data.fs.mac_a.data);\n\n    memcpy(instance->data->data.fs.mac_a.data + 8, instance->data->data.fs.wcnt.data, 3); //-V1086\n\n    uint8_t tx_data[FELICA_DATA_BLOCK_SIZE * 2];\n    memcpy(tx_data, instance->data->data.fs.state.data, FELICA_DATA_BLOCK_SIZE);\n    memcpy(\n        tx_data + FELICA_DATA_BLOCK_SIZE,\n        instance->data->data.fs.mac_a.data,\n        FELICA_DATA_BLOCK_SIZE);\n\n    do {\n        blocks[0] = FELICA_BLOCK_INDEX_STATE;\n        blocks[1] = FELICA_BLOCK_INDEX_MAC_A;\n        FelicaPollerWriteCommandResponse* tx_resp;\n        FelicaError error = felica_poller_write_blocks(instance, 2, blocks, tx_data, &tx_resp);\n        if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break;\n\n        FelicaPollerReadCommandResponse* rx_resp;\n        error = felica_poller_read_blocks(instance, 1, blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp);\n        if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;\n\n        instance->data->data.fs.state.SF1 = 0;\n        instance->data->data.fs.state.SF2 = 0;\n        memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);\n        instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];\n    } while(false);\n\n    switch(instance->data->workflow_type) {\n    case FelicaStandard:\n        instance->state = FelicaPollerStateTraverseStandardSystem;\n        break;\n    case FelicaLite:\n        instance->state = FelicaPollerStateReadLiteBlocks;\n        break;\n    default:\n        // Unimplemented\n        instance->state = FelicaPollerStateReadSuccess;\n        break;\n    }\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Traverse Standard System\");\n\n    FelicaListServiceCommandResponse* response;\n\n    felica_service_array_t service_buffer;\n    felica_service_array_init(service_buffer);\n    felica_area_array_t area_buffer;\n    felica_area_array_init(area_buffer);\n\n    for(uint16_t cursor = 0; cursor < 0xFFFF; cursor++) {\n        FelicaError error = felica_poller_list_service_by_cursor(instance, cursor, &response);\n        if(error != FelicaErrorNone) {\n            FURI_LOG_E(TAG, \"Error %d at cursor %04X\", error, cursor);\n            break;\n        }\n\n        uint8_t len = response->header.length;\n        const uint8_t* list_service_payload = response->data;\n        uint16_t code_begin = (uint16_t)(list_service_payload[0] | (list_service_payload[1] << 8));\n\n        if(len != 0x0C && len != 0x0E) {\n            FURI_LOG_E(TAG, \"Bad command resp length 0x%02X at cursor 0x%04X\", len, cursor);\n            break;\n        }\n\n        if(code_begin == 0xFFFF) {\n            FURI_LOG_D(TAG, \"Traverse complete\");\n            break;\n        }\n\n        if(len == 0x0E) {\n            FelicaArea* area = felica_area_array_push_raw(area_buffer);\n            memset(area, 0, sizeof *area);\n            area->code = code_begin;\n            area->first_idx = (uint16_t)felica_service_array_size(service_buffer);\n            area->last_idx = 0;\n        } else {\n            FelicaService* service = felica_service_array_push_raw(service_buffer);\n            memset(service, 0, sizeof *service);\n            service->code = code_begin;\n            service->attr = (uint8_t)(code_begin & 0x3F);\n\n            FURI_LOG_D(TAG, \"Service %04X\", service->code);\n\n            if(felica_area_array_size(area_buffer)) {\n                FelicaArea* current_area = felica_area_array_back(area_buffer);\n                current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1);\n            }\n        }\n    }\n\n    const size_t service_num = felica_service_array_size(service_buffer);\n    const size_t area_num = felica_area_array_size(area_buffer);\n\n    FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read);\n    if(service_num) {\n        simple_array_init(system->services, (uint32_t)service_num);\n        memcpy(\n            simple_array_get(system->services, 0),\n            service_buffer->ptr,\n            service_num * sizeof(FelicaService));\n    } else {\n        simple_array_reset(system->services);\n    }\n\n    if(area_num) {\n        simple_array_init(system->areas, (uint32_t)area_num);\n        memcpy(\n            simple_array_get(system->areas, 0), area_buffer->ptr, area_num * sizeof(FelicaArea));\n    } else {\n        simple_array_reset(system->areas);\n    }\n\n    FURI_LOG_I(\n        TAG,\n        \"Services found: %lu, Areas found: %lu\",\n        simple_array_get_count(system->services),\n        simple_array_get_count(system->areas));\n\n    felica_service_array_clear(service_buffer);\n    felica_area_array_clear(area_buffer);\n\n    instance->state = FelicaPollerStateReadStandardBlocks;\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Read Standard Blocks\");\n\n    FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read);\n    const uint32_t service_count = simple_array_get_count(system->services);\n\n    felica_public_block_array_t public_block_buffer;\n    felica_public_block_array_init(public_block_buffer);\n\n    bool have_read_anything = false;\n    FelicaError error = FelicaErrorNone;\n\n    for(uint32_t i = 0; i < service_count; i++) {\n        const FelicaService* service = simple_array_get(system->services, i);\n\n        if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue;\n\n        uint8_t block_count = 1;\n        uint8_t block_list[1] = {0};\n        FelicaPollerReadCommandResponse* response;\n        do {\n            error = felica_poller_read_blocks(\n                instance, block_count, block_list, service->code, &response);\n\n            if(error != FelicaErrorNone) {\n                break;\n            }\n\n            if(response->SF1 == 0 && response->SF2 == 0) {\n                FelicaPublicBlock* public_block =\n                    felica_public_block_array_push_raw(public_block_buffer);\n                memset(public_block, 0, sizeof *public_block);\n                memcpy(public_block->block.data, response->data, FELICA_DATA_BLOCK_SIZE);\n\n                public_block->service_code = service->code;\n                public_block->block_idx = block_list[0];\n\n                have_read_anything = true;\n                block_list[0]++;\n            } else {\n                break; // No more blocks to read in this service, ok to continue for loop\n            }\n        } while(block_list[0] < FELICA_STANDARD_MAX_BLOCK_COUNT);\n\n        if(error != FelicaErrorNone) {\n            instance->felica_event.type = FelicaPollerEventTypeError;\n            instance->felica_event_data.error = error;\n            instance->state = FelicaPollerStateReadFailed;\n            break;\n        }\n    }\n\n    if(error == FelicaErrorNone) {\n        instance->systems_read++;\n        if(instance->systems_read == instance->systems_total) {\n            instance->state = FelicaPollerStateReadSuccess;\n        } else {\n            instance->state = FelicaPollerStateSelectSystemIndex;\n        }\n    }\n\n    if(have_read_anything) {\n        const size_t n = felica_public_block_array_size(public_block_buffer);\n        simple_array_init(system->public_blocks, (uint32_t)n);\n        memcpy(\n            simple_array_get(system->public_blocks, 0),\n            public_block_buffer->ptr,\n            n * sizeof(FelicaPublicBlock));\n    }\n\n    felica_public_block_array_clear(public_block_buffer);\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_read_lite_blocks(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Read Lite Blocks\");\n\n    uint8_t block_count = 1;\n    uint8_t block_list[4] = {0, 0, 0, 0};\n    block_list[0] = instance->block_index;\n\n    instance->block_index++;\n    if(instance->block_index == FELICA_BLOCK_INDEX_REG + 1) {\n        instance->block_index = FELICA_BLOCK_INDEX_RC;\n    } else if(instance->block_index == FELICA_BLOCK_INDEX_MC + 1) {\n        instance->block_index = FELICA_BLOCK_INDEX_WCNT;\n    } else if(instance->block_index == FELICA_BLOCK_INDEX_STATE + 1) {\n        instance->block_index = FELICA_BLOCK_INDEX_CRC_CHECK;\n    }\n\n    FelicaPollerReadCommandResponse* response;\n    FelicaError error = felica_poller_read_blocks(\n        instance, block_count, block_list, FELICA_SERVICE_RO_ACCESS, &response);\n    if(error == FelicaErrorNone) {\n        block_count = (response->SF1 == 0) ? response->block_count : block_count;\n        uint8_t* data_ptr =\n            instance->data->data.dump + instance->data->blocks_total * sizeof(FelicaBlock);\n\n        *data_ptr++ = response->SF1;\n        *data_ptr++ = response->SF2;\n\n        if(response->SF1 == 0) {\n            uint8_t* response_data_ptr = response->data;\n            instance->data->blocks_read++;\n            memcpy(data_ptr, response_data_ptr, FELICA_DATA_BLOCK_SIZE);\n        } else {\n            memset(data_ptr, 0, FELICA_DATA_BLOCK_SIZE);\n        }\n        instance->data->blocks_total++;\n\n        if(instance->data->blocks_total == FELICA_BLOCKS_TOTAL_COUNT) {\n            instance->state = FelicaPollerStateReadSuccess;\n        }\n    } else {\n        instance->felica_event.type = FelicaPollerEventTypeError;\n        instance->felica_event_data.error = error;\n        instance->state = FelicaPollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nNfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Read Success\");\n\n    if((!instance->auth.context.auth_status.internal ||\n        !instance->auth.context.auth_status.external) &&\n       instance->data->workflow_type == FelicaLite) {\n        if(instance->data->blocks_read != 0) {\n            instance->data->blocks_read--;\n        }\n        instance->felica_event.type = FelicaPollerEventTypeIncomplete;\n    } else {\n        memcpy(\n            instance->data->data.fs.ck.data,\n            instance->auth.context.card_key.data,\n            FELICA_DATA_BLOCK_SIZE);\n        instance->felica_event.type = FelicaPollerEventTypeReady;\n    }\n\n    instance->data->idm.data[0] &= 0x0F;\n\n    instance->felica_event_data.error = FelicaErrorNone;\n    return instance->callback(instance->general_event, instance->context);\n}\n\nNfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {\n    FURI_LOG_D(TAG, \"Read Fail\");\n    instance->callback(instance->general_event, instance->context);\n    instance->data->idm.data[0] &= 0x0F;\n\n    return NfcCommandStop;\n}\n\nstatic const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {\n    [FelicaPollerStateIdle] = felica_poller_state_handler_idle,\n    [FelicaPollerStateActivated] = felica_poller_state_handler_activate,\n    [FelicaPollerStateListSystem] = felica_poller_state_handler_list_system,\n    [FelicaPollerStateSelectSystemIndex] = felica_poller_state_handler_select_system_idx,\n    [FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,\n    [FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,\n    [FelicaPollerStateTraverseStandardSystem] =\n        felica_poller_state_handler_traverse_standard_system,\n    [FelicaPollerStateReadStandardBlocks] = felica_poller_state_handler_read_standard_blocks,\n    [FelicaPollerStateReadLiteBlocks] = felica_poller_state_handler_read_lite_blocks,\n    [FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,\n    [FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,\n};\n\nstatic NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    FelicaPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        command = felica_poller_handler[instance->state](instance);\n    }\n\n    return command;\n}\n\nstatic bool felica_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n\n    bool protocol_detected = false;\n    FelicaPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    furi_assert(instance->state == FelicaPollerStateIdle);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        FelicaError error = felica_poller_activate(instance, instance->data);\n        protocol_detected = (error == FelicaErrorNone);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_felica = {\n    .alloc = (NfcPollerAlloc)felica_poller_alloc,\n    .free = (NfcPollerFree)felica_poller_free,\n    .set_callback = (NfcPollerSetCallback)felica_poller_set_callback,\n    .run = (NfcPollerRun)felica_poller_run,\n    .detect = (NfcPollerDetect)felica_poller_detect,\n    .get_data = (NfcPollerGetData)felica_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller.h",
    "content": "#pragma once\n\n#include \"felica.h\"\n#include <lib/nfc/nfc.h>\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief FelicaPoller opaque type definition.\n */\ntypedef struct FelicaPoller FelicaPoller;\n\n/**\n * @brief Enumeration of possible Felica poller event types.\n */\ntypedef enum {\n    FelicaPollerEventTypeError, /**< An error occured during activation procedure. */\n    FelicaPollerEventTypeReady, /**< The card was activated and fully read by the poller. */\n    FelicaPollerEventTypeIncomplete, /**< The card was activated and partly read by the poller. */\n    FelicaPollerEventTypeRequestAuthContext, /**< Authentication context was requested by poller. */\n} FelicaPollerEventType;\n\n/**\n * @brief Felica poller event data.\n */\ntypedef union {\n    FelicaError error; /**< Error code indicating card activation fail reason. */\n    FelicaAuthenticationContext* auth_context; /**< Authentication context to be filled by user. */\n} FelicaPollerEventData;\n\n/**\n * @brief FelicaPoller poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    FelicaPollerEventType type; /**< Type of emmitted event. */\n    FelicaPollerEventData* data; /**< Pointer to event specific data. */\n} FelicaPollerEvent;\n\n/**\n * @brief Perform collision resolution procedure.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perfoms the collision resolution procedure as defined in FeliCa standars. The data\n * field will be filled with Felica data on success.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the Felica data structure to be filled.\n * @return FelicaErrorNone on success, an error code on failure.\n */\nFelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data);\n\n/**\n * @brief Performs felica read operation for blocks provided as parameters\n * \n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_count Amount of blocks involved in reading procedure\n * @param[in] block_numbers Array with block indexes according to felica docs\n * @param[in] service_code Service code for the read operation\n * @param[out] response_ptr Pointer to the response structure\n * @return FelicaErrorNone on success, an error code on failure.\n*/\nFelicaError felica_poller_read_blocks(\n    FelicaPoller* instance,\n    const uint8_t block_count,\n    const uint8_t* const block_numbers,\n    uint16_t service_code,\n    FelicaPollerReadCommandResponse** const response_ptr);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcPollerBase nfc_poller_felica;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller_i.c",
    "content": "#include \"felica_poller_i.h\"\n\n#include <nfc/helpers/felica_crc.h>\n\n#define TAG \"FelicaPoller\"\n\nstatic FelicaError felica_poller_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return FelicaErrorNone;\n    case NfcErrorTimeout:\n        return FelicaErrorTimeout;\n    default:\n        return FelicaErrorNotPresent;\n    }\n}\n\nFelicaError felica_poller_frame_exchange(\n    const FelicaPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_assert(instance);\n\n    const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);\n    furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE);\n\n    felica_crc_append(instance->tx_buffer);\n\n    FelicaError ret = FelicaErrorNone;\n\n    do {\n        NfcError error =\n            nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);\n        if(error != NfcErrorNone) {\n            ret = felica_poller_process_error(error);\n            break;\n        }\n\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n        if(!felica_crc_check(instance->rx_buffer)) {\n            ret = FelicaErrorWrongCrc;\n            break;\n        }\n\n        felica_crc_trim(rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nFelicaError felica_poller_polling(\n    FelicaPoller* instance,\n    const FelicaPollerPollingCommand* cmd,\n    FelicaPollerPollingResponse* resp) {\n    furi_assert(instance);\n    furi_assert(cmd);\n    furi_assert(resp);\n\n    FelicaError error = FelicaErrorNone;\n\n    do {\n        bit_buffer_set_size_bytes(instance->tx_buffer, 2);\n        // Set frame len\n        bit_buffer_set_byte(\n            instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + FELICA_CRC_SIZE);\n        // Set command code\n        bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_REQ_CODE);\n        // Set other data\n        bit_buffer_append_bytes(\n            instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand));\n\n        error = felica_poller_frame_exchange(\n            instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);\n\n        if(error != FelicaErrorNone) break;\n        if(bit_buffer_get_byte(instance->rx_buffer, 1) != FELICA_POLLER_CMD_POLLING_RESP_CODE) {\n            error = FelicaErrorProtocol;\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) <\n           sizeof(FelicaIDm) + sizeof(FelicaPMm) + 1) {\n            error = FelicaErrorProtocol;\n            break;\n        }\n\n        bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm));\n        bit_buffer_write_bytes_mid(\n            instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm));\n\n    } while(false);\n\n    return error;\n}\n\n// This is in fact a buffer preparer for a specified service. It should be have the _ex suffix. The prepare_tx_buffer_raw should have this name.\nstatic void felica_poller_prepare_tx_buffer(\n    const FelicaPoller* instance,\n    const uint8_t command,\n    const uint16_t service_code,\n    const uint8_t block_count,\n    const uint8_t* const blocks,\n    const uint8_t data_block_count,\n    const uint8_t* data) {\n    FelicaCommandHeader cmd = {\n        .code = command,\n        .idm = instance->data->idm,\n        .service_num = 1,\n        .service_code = service_code,\n        .block_count = block_count,\n    };\n\n    FelicaBlockListElement block_list[4] = {{0}, {0}, {0}, {0}};\n    for(uint8_t i = 0; i < block_count; i++) {\n        block_list[i].length = 1;\n        block_list[i].block_number = blocks[i];\n    }\n\n    uint8_t block_list_count = block_count;\n    uint8_t block_list_size = block_list_count * sizeof(FelicaBlockListElement);\n    uint8_t total_size = sizeof(FelicaCommandHeader) + 1 + block_list_size +\n                         data_block_count * FELICA_DATA_BLOCK_SIZE;\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_byte(instance->tx_buffer, total_size);\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeader));\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block_list, block_list_size);\n\n    if(data_block_count != 0) {\n        bit_buffer_append_bytes(\n            instance->tx_buffer, data, data_block_count * FELICA_DATA_BLOCK_SIZE);\n    }\n}\n\nFelicaError felica_poller_read_blocks(\n    FelicaPoller* instance,\n    const uint8_t block_count,\n    const uint8_t* const block_numbers,\n    uint16_t service_code,\n    FelicaPollerReadCommandResponse** const response_ptr) {\n    furi_assert(instance);\n    furi_assert(block_count <= 4);\n    furi_assert(block_numbers);\n    furi_assert(response_ptr);\n\n    felica_poller_prepare_tx_buffer(\n        instance,\n        FELICA_CMD_READ_WITHOUT_ENCRYPTION,\n        service_code,\n        block_count,\n        block_numbers,\n        0,\n        NULL);\n    bit_buffer_reset(instance->rx_buffer);\n\n    FelicaError error = felica_poller_frame_exchange(\n        instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);\n    if(error == FelicaErrorNone) {\n        *response_ptr = (FelicaPollerReadCommandResponse*)bit_buffer_get_data(instance->rx_buffer);\n    }\n    return error;\n}\n\nFelicaError felica_poller_write_blocks(\n    const FelicaPoller* instance,\n    const uint8_t block_count,\n    const uint8_t* const block_numbers,\n    const uint8_t* data,\n    FelicaPollerWriteCommandResponse** const response_ptr) {\n    furi_assert(instance);\n    furi_assert(block_count <= 2);\n    furi_assert(block_numbers);\n    furi_assert(data);\n    furi_assert(response_ptr);\n\n    felica_poller_prepare_tx_buffer(\n        instance,\n        FELICA_CMD_WRITE_WITHOUT_ENCRYPTION,\n        FELICA_SERVICE_RW_ACCESS,\n        block_count,\n        block_numbers,\n        block_count,\n        data);\n    bit_buffer_reset(instance->rx_buffer);\n\n    FelicaError error = felica_poller_frame_exchange(\n        instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);\n    if(error == FelicaErrorNone) {\n        *response_ptr =\n            (FelicaPollerWriteCommandResponse*)bit_buffer_get_data(instance->rx_buffer);\n    }\n    return error;\n}\n\nFelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {\n    furi_assert(instance);\n\n    FelicaError ret;\n\n    do {\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        // Send Polling command\n        const FelicaPollerPollingCommand polling_cmd = {\n            .system_code = FELICA_SYSTEM_CODE_CODE,\n            .request_code = 0,\n            .time_slot = FELICA_TIME_SLOT_1,\n        };\n        FelicaPollerPollingResponse polling_resp = {};\n\n        ret = felica_poller_polling(instance, &polling_cmd, &polling_resp);\n\n        if(ret != FelicaErrorNone) {\n            FURI_LOG_T(TAG, \"Activation failed error: %d\", ret);\n            break;\n        }\n\n        data->idm = polling_resp.idm;\n        data->pmm = polling_resp.pmm;\n        instance->state = FelicaPollerStateActivated;\n    } while(false);\n\n    return ret;\n}\n\nstatic void felica_poller_prepare_tx_buffer_raw(\n    const FelicaPoller* instance,\n    const uint8_t command,\n    const uint8_t* data,\n    const uint8_t data_length) {\n    FelicaCommandHeaderRaw cmd = {.length = 0x00, .command = command, .idm = instance->data->idm};\n\n    cmd.length = sizeof(FelicaCommandHeaderRaw) + data_length;\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeaderRaw));\n    if(data_length > 0) {\n        bit_buffer_append_bytes(instance->tx_buffer, data, data_length);\n    }\n}\n\nFelicaError felica_poller_list_service_by_cursor(\n    FelicaPoller* instance,\n    uint16_t cursor,\n    FelicaListServiceCommandResponse** const response_ptr) {\n    furi_assert(instance);\n    furi_assert(response_ptr);\n\n    const uint8_t data[2] = {(uint8_t)(cursor & 0xFF), (uint8_t)((cursor >> 8) & 0xFF)};\n\n    felica_poller_prepare_tx_buffer_raw(\n        instance, FELICA_CMD_LIST_SERVICE_CODE, data, sizeof(data));\n\n    bit_buffer_reset(instance->rx_buffer);\n\n    FelicaError error = felica_poller_frame_exchange(\n        instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);\n    if(error != FelicaErrorNone) {\n        FURI_LOG_E(TAG, \"List service by cursor failed with error: %d\", error);\n        return error;\n    }\n\n    size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer);\n    if(rx_len < sizeof(FelicaCommandHeaderRaw) + 2) return FelicaErrorProtocol;\n\n    // error is known to be FelicaErrorNone here\n    *response_ptr = (FelicaListServiceCommandResponse*)bit_buffer_get_data(instance->rx_buffer);\n    return error;\n}\n\nFelicaError felica_poller_list_system_code(\n    FelicaPoller* instance,\n    FelicaListSystemCodeCommandResponse** const response_ptr) {\n    furi_assert(instance);\n    furi_assert(response_ptr);\n\n    uint8_t data[] = {0};\n\n    felica_poller_prepare_tx_buffer_raw(instance, FELICA_CMD_REQUEST_SYSTEM_CODE, data, 0);\n\n    bit_buffer_reset(instance->rx_buffer);\n\n    FelicaError error = felica_poller_frame_exchange(\n        instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);\n    if(error != FelicaErrorNone) {\n        FURI_LOG_E(TAG, \"Request system code failed with error: %d\", error);\n        return error;\n    }\n\n    size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer);\n    if(rx_len < sizeof(FelicaCommandHeaderRaw) + 3) return FelicaErrorProtocol;\n    // at least 1 system code + the count being 0x01\n\n    // error is known to be FelicaErrorNone here\n    *response_ptr = (FelicaListSystemCodeCommandResponse*)bit_buffer_get_data(instance->rx_buffer);\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller_i.h",
    "content": "#pragma once\n\n#include \"felica_poller.h\"\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FELICA_POLLER_MAX_BUFFER_SIZE (256U)\n\n#define FELICA_POLLER_POLLING_FWT (200000U)\n\n#define FELICA_POLLER_CMD_POLLING_REQ_CODE  (0x00U)\n#define FELICA_POLLER_CMD_POLLING_RESP_CODE (0x01U)\n\ntypedef enum {\n    FelicaPollerStateIdle,\n    FelicaPollerStateActivated,\n    FelicaPollerStateListSystem,\n    FelicaPollerStateSelectSystemIndex,\n    FelicaPollerStateAuthenticateInternal,\n    FelicaPollerStateAuthenticateExternal,\n    FelicaPollerStateTraverseStandardSystem,\n    FelicaPollerStateReadStandardBlocks,\n    FelicaPollerStateReadLiteBlocks,\n    FelicaPollerStateReadSuccess,\n    FelicaPollerStateReadFailed,\n\n    FelicaPollerStateNum\n} FelicaPollerState;\n\nstruct FelicaPoller {\n    Nfc* nfc;\n    FelicaPollerState state;\n    FelicaAuthentication auth;\n\n    FelicaData* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    NfcGenericEvent general_event;\n    FelicaPollerEvent felica_event;\n    FelicaPollerEventData felica_event_data;\n    NfcGenericCallback callback;\n    uint8_t block_index;\n    uint8_t systems_read;\n    uint8_t systems_total;\n    void* context;\n};\n\ntypedef struct {\n    uint16_t system_code;\n    uint8_t request_code;\n    uint8_t time_slot;\n} FelicaPollerPollingCommand;\n\ntypedef struct {\n    FelicaIDm idm;\n    FelicaPMm pmm;\n    uint8_t request_data[2];\n} FelicaPollerPollingResponse;\n\ntypedef union {\n    FelicaData* data;\n} FelicaPollerContextData;\n\nconst FelicaData* felica_poller_get_data(FelicaPoller* instance);\n\n/**\n * @brief Performs felica polling operation as part of the activation process\n * \n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] cmd Pointer to polling command structure\n * @param[out] resp Pointer to the response structure\n * @return FelicaErrorNone on success, an error code on failure\n*/\nFelicaError felica_poller_polling(\n    FelicaPoller* instance,\n    const FelicaPollerPollingCommand* cmd,\n    FelicaPollerPollingResponse* resp);\n\n/**\n * @brief Performs felica write operation with data provided as parameters\n * \n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_count Amount of blocks involved in writing procedure\n * @param[in] block_numbers Array with block indexes according to felica docs\n * @param[in] data Data of blocks provided in block_numbers\n * @param[out] response_ptr Pointer to the response structure\n * @return FelicaErrorNone on success, an error code on failure.\n*/\nFelicaError felica_poller_write_blocks(\n    const FelicaPoller* instance,\n    const uint8_t block_count,\n    const uint8_t* const block_numbers,\n    const uint8_t* data,\n    FelicaPollerWriteCommandResponse** const response_ptr);\n\n/**\n * @brief Perform frame exchange procedure.\n *\n * Prepares data for sending by adding crc, after that performs\n * low level calls to send package data to the card\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer with data to be transmitted\n * @param[out] rx_buffer pointer to the buffer with received data from card\n * @param[in] fwt timeout window\n * @return FelicaErrorNone on success, an error code on failure.\n */\nFelicaError felica_poller_frame_exchange(\n    const FelicaPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\nFelicaError felica_poller_list_service_by_cursor(\n    FelicaPoller* instance,\n    uint16_t cursor,\n    FelicaListServiceCommandResponse** response_ptr);\n\nFelicaError felica_poller_list_system_code(\n    FelicaPoller* instance,\n    FelicaListSystemCodeCommandResponse** response_ptr);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller_sync.c",
    "content": "#include \"felica_poller_sync.h\"\n\n#include \"felica_poller_i.h\"\n#include <nfc/nfc_poller.h>\n\n#include <furi/furi.h>\n\n#define FELICA_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)\n\ntypedef struct {\n    FelicaAuthenticationContext auth_ctx;\n    FuriThreadId thread_id;\n    FelicaError error;\n    FelicaPollerContextData data;\n} FelicaPollerContext;\n\nNfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolFelica);\n\n    FelicaPollerContext* poller_context = context;\n    FelicaPoller* felica_poller = event.instance;\n\n    FelicaPollerEvent* felica_event = event.event_data;\n\n    if(felica_event->type == FelicaPollerEventTypeReady ||\n       felica_event->type == FelicaPollerEventTypeIncomplete) {\n        felica_copy(poller_context->data.data, felica_poller->data);\n    } else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {\n        felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth;\n        memcpy(\n            felica_event->data->auth_context->card_key.data,\n            poller_context->auth_ctx.card_key.data,\n            FELICA_DATA_BLOCK_SIZE);\n    }\n\n    furi_thread_flags_set(poller_context->thread_id, FELICA_POLLER_FLAG_COMMAND_COMPLETE);\n\n    return NfcCommandStop;\n}\n\nFelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key) {\n    furi_check(nfc);\n    furi_check(data);\n\n    FelicaPollerContext poller_context = {};\n    if(card_key == NULL) {\n        poller_context.auth_ctx.skip_auth = true;\n    } else {\n        poller_context.auth_ctx.skip_auth = false;\n        memcpy(poller_context.auth_ctx.card_key.data, card_key->data, FELICA_DATA_BLOCK_SIZE);\n    }\n\n    poller_context.thread_id = furi_thread_get_current_id();\n    poller_context.data.data = felica_alloc();\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica);\n    nfc_poller_start(poller, felica_poller_read_callback, &poller_context);\n    furi_thread_flags_wait(FELICA_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(FELICA_POLLER_FLAG_COMMAND_COMPLETE);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    if(poller_context.error == FelicaErrorNone) {\n        felica_copy(data, poller_context.data.data);\n    }\n\n    felica_free(poller_context.data.data);\n\n    return poller_context.error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/felica/felica_poller_sync.h",
    "content": "#pragma once\n\n#include \"felica.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nFelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a.c",
    "content": "#include \"iso14443_3a.h\"\n\n#include <furi.h>\n#include <nfc/nfc_common.h>\n\n#define ISO14443A_ATS_BIT (1U << 5)\n\n#define ISO14443_3A_PROTOCOL_NAME_LEGACY \"UID\"\n#define ISO14443_3A_PROTOCOL_NAME        \"ISO14443-3A\"\n#define ISO14443_3A_DEVICE_NAME          \"ISO14443-3A (Unknown)\"\n\n#define ISO14443_3A_ATQA_KEY \"ATQA\"\n#define ISO14443_3A_SAK_KEY  \"SAK\"\n\nconst NfcDeviceBase nfc_device_iso14443_3a = {\n    .protocol_name = ISO14443_3A_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)iso14443_3a_alloc,\n    .free = (NfcDeviceFree)iso14443_3a_free,\n    .reset = (NfcDeviceReset)iso14443_3a_reset,\n    .copy = (NfcDeviceCopy)iso14443_3a_copy,\n    .verify = (NfcDeviceVerify)iso14443_3a_verify,\n    .load = (NfcDeviceLoad)iso14443_3a_load,\n    .save = (NfcDeviceSave)iso14443_3a_save,\n    .is_equal = (NfcDeviceEqual)iso14443_3a_is_equal,\n    .get_name = (NfcDeviceGetName)iso14443_3a_get_device_name,\n    .get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid,\n    .set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data,\n};\n\nIso14443_3aData* iso14443_3a_alloc(void) {\n    Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData));\n    return data;\n}\n\nvoid iso14443_3a_free(Iso14443_3aData* data) {\n    furi_check(data);\n\n    free(data);\n}\n\nvoid iso14443_3a_reset(Iso14443_3aData* data) {\n    furi_check(data);\n\n    memset(data, 0, sizeof(Iso14443_3aData));\n}\n\nvoid iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    *data = *other;\n}\n\nbool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) {\n    UNUSED(data);\n    furi_check(device_type);\n\n    return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY);\n}\n\nbool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool parsed = false;\n\n    do {\n        // Common to all format versions\n        if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break;\n        if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;\n\n        if(version > NFC_LSB_ATQA_FORMAT_VERSION) {\n            // Swap ATQA bytes for newer versions\n            FURI_SWAP(data->atqa[0], data->atqa[1]);\n        }\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\nbool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool saved = false;\n\n    do {\n        // Save ATQA in MSB order for correct companion apps display\n        const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};\n        if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME \" specific data\"))\n            break;\n\n        // Write ATQA and SAK\n        if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break;\n        if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;\n        saved = true;\n    } while(false);\n\n    return saved;\n}\n\nbool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return memcmp(data, other, sizeof(Iso14443_3aData)) == 0;\n}\n\nconst char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n    return ISO14443_3A_DEVICE_NAME;\n}\n\nconst uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) {\n    furi_check(data);\n\n    if(uid_len) {\n        *uid_len = data->uid_len;\n    }\n\n    return data->uid;\n}\n\nbool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n    furi_check(uid);\n\n    const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES ||\n                           uid_len == ISO14443_3A_UID_7_BYTES ||\n                           uid_len == ISO14443_3A_UID_10_BYTES;\n\n    if(uid_valid) {\n        memcpy(data->uid, uid, uid_len);\n        data->uid_len = uid_len;\n    }\n\n    return uid_valid;\n}\n\nIso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) {\n    UNUSED(data);\n    furi_crash(\"No base data\");\n}\n\nuint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {\n    furi_check(data);\n\n    uint32_t cuid = 0;\n    const uint8_t* cuid_start = data->uid;\n    if(data->uid_len == ISO14443_3A_UID_7_BYTES) {\n        cuid_start = &data->uid[3];\n    }\n    cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]);\n\n    return cuid;\n}\n\nbool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) {\n    furi_check(data);\n\n    return data->sak & ISO14443A_ATS_BIT;\n}\n\nuint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) {\n    furi_check(data);\n\n    return data->sak;\n}\n\nvoid iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) {\n    furi_check(data);\n    furi_check(atqa);\n\n    memcpy(atqa, data->atqa, sizeof(data->atqa));\n}\n\nvoid iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) {\n    furi_check(data);\n\n    data->sak = sak;\n}\n\nvoid iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) {\n    furi_check(data);\n    furi_check(atqa);\n\n    memcpy(data->atqa, atqa, sizeof(data->atqa));\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a.h",
    "content": "#pragma once\n\n#include <toolbox/bit_buffer.h>\n#include <nfc/protocols/nfc_device_base_i.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO14443_3A_UID_4_BYTES  (4U)\n#define ISO14443_3A_UID_7_BYTES  (7U)\n#define ISO14443_3A_UID_10_BYTES (10U)\n#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES\n\n#define ISO14443_3A_GUARD_TIME_US     (5000)\n#define ISO14443_3A_FDT_POLL_FC       (1620)\n#define ISO14443_3A_FDT_LISTEN_FC     (1172)\n#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2)\n#define ISO14443_3A_POLL_POLL_MIN_US  (1100)\n\ntypedef enum {\n    Iso14443_3aErrorNone,\n    Iso14443_3aErrorNotPresent,\n    Iso14443_3aErrorColResFailed,\n    Iso14443_3aErrorBufferOverflow,\n    Iso14443_3aErrorCommunication,\n    Iso14443_3aErrorFieldOff,\n    Iso14443_3aErrorWrongCrc,\n    Iso14443_3aErrorTimeout,\n} Iso14443_3aError;\n\ntypedef struct {\n    uint8_t sens_resp[2];\n} Iso14443_3aSensResp;\n\ntypedef struct {\n    uint8_t sel_cmd;\n    uint8_t sel_par;\n    uint8_t data[4]; // max data bit is 32\n} Iso14443_3aSddReq;\n\ntypedef struct {\n    uint8_t nfcid[4];\n    uint8_t bss;\n} Iso14443_3aSddResp;\n\ntypedef struct {\n    uint8_t sel_cmd;\n    uint8_t sel_par;\n    uint8_t nfcid[4];\n    uint8_t bcc;\n} Iso14443_3aSelReq;\n\ntypedef struct {\n    uint8_t sak;\n} Iso14443_3aSelResp;\n\ntypedef struct {\n    uint8_t uid[ISO14443_3A_MAX_UID_SIZE];\n    uint8_t uid_len;\n    uint8_t atqa[2];\n    uint8_t sak;\n} Iso14443_3aData;\n\nIso14443_3aData* iso14443_3a_alloc(void);\n\nvoid iso14443_3a_free(Iso14443_3aData* data);\n\nvoid iso14443_3a_reset(Iso14443_3aData* data);\n\nvoid iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other);\n\nbool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type);\n\nbool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version);\n\nbool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff);\n\nbool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other);\n\nconst char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len);\n\nbool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data);\n\nuint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data);\n\n// Getters and tests\n\nbool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data);\n\nuint8_t iso14443_3a_get_sak(const Iso14443_3aData* data);\n\nvoid iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]);\n\n// Setters\n\nvoid iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak);\n\nvoid iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_iso14443_3a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c",
    "content": "#include \"iso14443_3a_listener_i.h\"\n\n#include \"nfc/protocols/nfc_listener_base.h\"\n#include \"nfc/helpers/iso14443_crc.h\"\n\n#include <furi.h>\n#include <lib/nfc/nfc.h>\n\n#define TAG \"Iso14443_3aListener\"\n\n#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256)\n\nstatic bool iso14443_3a_listener_halt_received(BitBuffer* buf) {\n    bool halt_cmd_received = false;\n\n    do {\n        if(bit_buffer_get_size_bytes(buf) != 4) break;\n        if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break;\n        if(bit_buffer_get_byte(buf, 0) != 0x50) break;\n        if(bit_buffer_get_byte(buf, 1) != 0x00) break;\n        halt_cmd_received = true;\n    } while(false);\n\n    return halt_cmd_received;\n}\n\nIso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) {\n    furi_assert(nfc);\n\n    Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener));\n    instance->nfc = nfc;\n    instance->data = data;\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE);\n\n    instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;\n    instance->generic_event.protocol = NfcProtocolIso14443_3a;\n    instance->generic_event.instance = instance;\n    instance->generic_event.event_data = &instance->iso14443_3a_event;\n\n    nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC);\n    nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a);\n    nfc_iso14443a_listener_set_col_res_data(\n        instance->nfc,\n        instance->data->uid,\n        instance->data->uid_len,\n        instance->data->atqa,\n        instance->data->sak);\n\n    return instance;\n}\n\nvoid iso14443_3a_listener_free(Iso14443_3aListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    free(instance);\n}\n\nvoid iso14443_3a_listener_set_callback(\n    Iso14443_3aListener* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nconst Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nNfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    Iso14443_3aListener* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypeListenerActivated) {\n        instance->state = Iso14443_3aListenerStateActive;\n    } else if(nfc_event->type == NfcEventTypeFieldOff) {\n        instance->state = Iso14443_3aListenerStateIdle;\n        if(instance->callback) {\n            instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff;\n            instance->callback(instance->generic_event, instance->context);\n        }\n        command = NfcCommandSleep;\n    } else if(nfc_event->type == NfcEventTypeRxEnd) {\n        if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) {\n            if(instance->callback) {\n                instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted;\n                instance->callback(instance->generic_event, instance->context);\n            }\n            command = NfcCommandSleep;\n        } else {\n            if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) {\n                instance->iso14443_3a_event.type =\n                    Iso14443_3aListenerEventTypeReceivedStandardFrame;\n                iso14443_crc_trim(nfc_event->data.buffer);\n            } else {\n                instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData;\n            }\n            instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer;\n            if(instance->callback) {\n                command = instance->callback(instance->generic_event, instance->context);\n            }\n        }\n    }\n\n    return command;\n}\n\nconst NfcListenerBase nfc_listener_iso14443_3a = {\n    .alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc,\n    .free = (NfcListenerFree)iso14443_3a_listener_free,\n    .set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback,\n    .get_data = (NfcListenerGetData)iso14443_3a_listener_get_data,\n    .run = (NfcListenerRun)iso14443_3a_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h",
    "content": "#pragma once\n\n#include \"iso14443_3a.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso14443_3aListener Iso14443_3aListener;\n\ntypedef enum {\n    Iso14443_3aListenerEventTypeFieldOff,\n    Iso14443_3aListenerEventTypeHalted,\n\n    Iso14443_3aListenerEventTypeReceivedStandardFrame,\n    Iso14443_3aListenerEventTypeReceivedData,\n} Iso14443_3aListenerEventType;\n\ntypedef struct {\n    BitBuffer* buffer;\n} Iso14443_3aListenerEventData;\n\ntypedef struct {\n    Iso14443_3aListenerEventType type;\n    Iso14443_3aListenerEventData* data;\n} Iso14443_3aListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\nextern const NfcListenerBase nfc_listener_iso14443_3a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c",
    "content": "#include \"iso14443_3a_listener_i.h\"\n\n#include \"nfc/helpers/iso14443_crc.h\"\n\n#define TAG \"Iso14443_3aListener\"\n\nstatic Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) {\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n\n    if(error == NfcErrorNone) {\n        ret = Iso14443_3aErrorNone;\n    } else if(error == NfcErrorTimeout) {\n        ret = Iso14443_3aErrorTimeout;\n    } else {\n        ret = Iso14443_3aErrorFieldOff;\n    }\n\n    return ret;\n}\n\nIso14443_3aError\n    iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    NfcError error = nfc_listener_tx(instance->nfc, tx_buffer);\n    if(error != NfcErrorNone) {\n        FURI_LOG_W(TAG, \"Tx error: %d\", error);\n        ret = iso14443_3a_listener_process_nfc_error(error);\n    }\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_listener_tx_with_custom_parity(\n    Iso14443_3aListener* instance,\n    const BitBuffer* tx_buffer) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer);\n    if(error != NfcErrorNone) {\n        FURI_LOG_W(TAG, \"Tx error: %d\", error);\n        ret = iso14443_3a_listener_process_nfc_error(error);\n    }\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_listener_send_standard_frame(\n    Iso14443_3aListener* instance,\n    const BitBuffer* tx_buffer) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n    furi_assert(instance->tx_buffer);\n\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    do {\n        bit_buffer_copy(instance->tx_buffer, tx_buffer);\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);\n\n        NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);\n        if(error != NfcErrorNone) {\n            FURI_LOG_W(TAG, \"Tx error: %d\", error);\n            ret = iso14443_3a_listener_process_nfc_error(error);\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h",
    "content": "#pragma once\n\n#include \"iso14443_3a_listener.h\"\n#include <nfc/protocols/nfc_generic_event.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_3aListenerStateIdle,\n    Iso14443_3aListenerStateActive,\n} Iso14443_3aListenerState;\n\nstruct Iso14443_3aListener {\n    Nfc* nfc;\n    Iso14443_3aData* data;\n    Iso14443_3aListenerState state;\n\n    BitBuffer* tx_buffer;\n\n    NfcGenericEvent generic_event;\n    Iso14443_3aListenerEvent iso14443_3a_event;\n    Iso14443_3aListenerEventData iso14443_3a_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nIso14443_3aError\n    iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);\n\nIso14443_3aError iso14443_3a_listener_tx_with_custom_parity(\n    Iso14443_3aListener* instance,\n    const BitBuffer* tx_buffer);\n\nIso14443_3aError iso14443_3a_listener_send_standard_frame(\n    Iso14443_3aListener* instance,\n    const BitBuffer* tx_buffer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c",
    "content": "#include \"iso14443_3a_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"ISO14443_3A\"\n\nconst Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n\n    Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller));\n    instance->nfc = nfc;\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);\n\n    nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a);\n    nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US);\n    nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC);\n    nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US);\n    instance->data = iso14443_3a_alloc();\n\n    instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;\n    instance->general_event.protocol = NfcProtocolIso14443_3a;\n    instance->general_event.event_data = &instance->iso14443_3a_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) {\n    furi_assert(iso14443_3a_poller);\n\n    Iso14443_3aPoller* instance = iso14443_3a_poller;\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(instance->data);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    iso14443_3a_free(instance->data);\n    free(instance);\n}\n\nstatic void iso14443_3a_poller_set_callback(\n    Iso14443_3aPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    Iso14443_3aPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        if(instance->state != Iso14443_3aPollerStateActivated) {\n            Iso14443_3aData data = {};\n            Iso14443_3aError error = iso14443_3a_poller_activate(instance, &data);\n            if(error == Iso14443_3aErrorNone) {\n                instance->state = Iso14443_3aPollerStateActivated;\n                instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;\n                instance->iso14443_3a_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n            } else {\n                instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError;\n                instance->iso14443_3a_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n                // Add delay to switch context\n                furi_delay_ms(100);\n            }\n        } else {\n            instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;\n            instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone;\n            command = instance->callback(instance->general_event, instance->context);\n        }\n    }\n\n    if(command == NfcCommandReset) {\n        instance->state = Iso14443_3aPollerStateIdle;\n    }\n\n    return command;\n}\n\nstatic bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n\n    bool protocol_detected = false;\n    Iso14443_3aPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    furi_assert(instance->state == Iso14443_3aPollerStateIdle);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        Iso14443_3aError error = iso14443_3a_poller_activate(instance, NULL);\n        protocol_detected = (error == Iso14443_3aErrorNone);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_iso14443_3a = {\n    .alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc,\n    .free = (NfcPollerFree)iso14443_3a_poller_free_new,\n    .set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback,\n    .run = (NfcPollerRun)iso14443_3a_poller_run,\n    .detect = (NfcPollerDetect)iso14443_3a_poller_detect,\n    .get_data = (NfcPollerGetData)iso14443_3a_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h",
    "content": "#pragma once\n\n#include \"iso14443_3a.h\"\n#include <lib/nfc/nfc.h>\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Iso14443_3aPoller opaque type definition.\n */\ntypedef struct Iso14443_3aPoller Iso14443_3aPoller;\n\n/**\n * @brief Enumeration of possible Iso14443_3a poller event types.\n */\ntypedef enum {\n    Iso14443_3aPollerEventTypeError, /**< An error occured during activation procedure. */\n    Iso14443_3aPollerEventTypeReady, /**< The card was activated by the poller. */\n} Iso14443_3aPollerEventType;\n\n/**\n * @brief Iso14443_3a poller event data.\n */\ntypedef union {\n    Iso14443_3aError error; /**< Error code indicating card activation fail reason. */\n} Iso14443_3aPollerEventData;\n\n/**\n * @brief Iso14443_3a poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    Iso14443_3aPollerEventType type; /**< Type of emmitted event. */\n    Iso14443_3aPollerEventData* data; /**< Pointer to event specific data. */\n} Iso14443_3aPollerEvent;\n\n/**\n * @brief Transmit and receive Iso14443_3a frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return Iso14443_3aErrorNone on success, an error code on failure.\n */\nIso14443_3aError iso14443_3a_poller_txrx(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Transmit and receive Iso14443_3a standard frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return Iso14443_3aErrorNone on success, an error code on failure.\n */\nIso14443_3aError iso14443_3a_poller_send_standard_frame(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * Custom parity bits must be set in the tx_buffer. The rx_buffer will contain\n * the received data with the parity bits.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return Iso14443_3aErrorNone on success, an error code on failure.\n */\nIso14443_3aError iso14443_3a_poller_txrx_custom_parity(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Checks presence of Iso14443_3a complient card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return Iso14443_3aErrorNone if card is present, an error code otherwise.\n */\nIso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance);\n\n/**\n * @brief Perform collision resolution procedure.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perfoms the collision resolution procedure as defined in Iso14443-3a. The iso14443_3a_data\n * field will be filled with Iso14443-3a data on success.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] iso14443_3a_data pointer to the Iso14443_3a data structure to be filled.\n * @return Iso14443_3aErrorNone on success, an error code on failure.\n */\nIso14443_3aError\n    iso14443_3a_poller_activate(Iso14443_3aPoller* instance, Iso14443_3aData* iso14443_3a_data);\n\n/**\n * @brief Send HALT command to the card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Halts card and changes internal Iso14443_3aPoller state to Idle.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return Iso14443_3aErrorNone on success, an error code on failure.\n */\nIso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_iso14443_3a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c",
    "content": "#include \"iso14443_3a_poller_i.h\"\n\n#include <furi.h>\n\n#include \"nfc/helpers/iso14443_crc.h\"\n\n#define TAG \"ISO14443_3A\"\n\nstatic Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) {\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    if(error == NfcErrorNone) {\n        ret = Iso14443_3aErrorNone;\n    } else if(error == NfcErrorTimeout) {\n        ret = Iso14443_3aErrorTimeout;\n    } else {\n        ret = Iso14443_3aErrorNotPresent;\n    }\n    return ret;\n}\n\nstatic Iso14443_3aError iso14443_3a_poller_standard_frame_exchange(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n    furi_assert(rx_buffer);\n\n    uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);\n    furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2);\n\n    bit_buffer_copy(instance->tx_buffer, tx_buffer);\n    iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n\n    do {\n        NfcError error =\n            nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);\n        if(error != NfcErrorNone) {\n            ret = iso14443_3a_poller_process_error(error);\n            break;\n        }\n\n        if(bit_buffer_get_capacity_bytes(rx_buffer) <\n           bit_buffer_get_size_bytes(instance->rx_buffer)) {\n            ret = Iso14443_3aErrorBufferOverflow;\n            break;\n        }\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n\n        if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) {\n            ret = Iso14443_3aErrorWrongCrc;\n            break;\n        }\n\n        iso14443_crc_trim(rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n\n    NfcError error = NfcErrorNone;\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    do {\n        error = nfc_iso14443a_poller_trx_short_frame(\n            instance->nfc,\n            NfcIso14443aShortFrameSensReq,\n            instance->rx_buffer,\n            ISO14443_3A_FDT_LISTEN_FC);\n        if(error != NfcErrorNone) {\n            ret = iso14443_3a_poller_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {\n            ret = Iso14443_3aErrorCommunication;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(instance->tx_buffer);\n\n    uint8_t halt_cmd[2] = {0x50, 0x00};\n    bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd));\n\n    iso14443_3a_poller_standard_frame_exchange(\n        instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);\n\n    instance->state = Iso14443_3aPollerStateIdle;\n    return Iso14443_3aErrorNone;\n}\n\nIso14443_3aError\n    iso14443_3a_poller_activate(Iso14443_3aPoller* instance, Iso14443_3aData* iso14443_3a_data) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(instance->tx_buffer);\n    furi_check(instance->rx_buffer);\n\n    // Reset Iso14443_3a poller state\n    memset(&instance->col_res, 0, sizeof(instance->col_res));\n    memset(instance->data, 0, sizeof(Iso14443_3aData));\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    // Halt if necessary\n    if(instance->state != Iso14443_3aPollerStateIdle) {\n        iso14443_3a_poller_halt(instance);\n        instance->state = Iso14443_3aPollerStateIdle;\n    }\n\n    NfcError error = NfcErrorNone;\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n\n    bool activated = false;\n    do {\n        error = nfc_iso14443a_poller_trx_short_frame(\n            instance->nfc,\n            NfcIso14443aShortFrameSensReq,\n            instance->rx_buffer,\n            ISO14443_3A_FDT_LISTEN_FC);\n        if(error != NfcErrorNone) {\n            ret = Iso14443_3aErrorNotPresent;\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {\n            FURI_LOG_W(TAG, \"Wrong sens response size\");\n            ret = Iso14443_3aErrorCommunication;\n            break;\n        }\n        bit_buffer_write_bytes(\n            instance->rx_buffer,\n            &instance->col_res.sens_resp,\n            sizeof(instance->col_res.sens_resp));\n        memcpy(\n            instance->data->atqa,\n            &instance->col_res.sens_resp,\n            sizeof(instance->col_res.sens_resp));\n\n        instance->state = Iso14443_3aPollerStateColResInProgress;\n        instance->col_res.cascade_level = 0;\n        instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;\n\n        while(instance->state == Iso14443_3aPollerStateColResInProgress) {\n            if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) {\n                bit_buffer_set_size_bytes(instance->tx_buffer, 2);\n                bit_buffer_set_byte(\n                    instance->tx_buffer,\n                    0,\n                    ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level));\n                bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0));\n                error = nfc_iso14443a_poller_trx_sdd_frame(\n                    instance->nfc,\n                    instance->tx_buffer,\n                    instance->rx_buffer,\n                    ISO14443_3A_FDT_LISTEN_FC);\n                if(error != NfcErrorNone) {\n                    FURI_LOG_E(TAG, \"Sdd request failed: %d\", error);\n                    instance->state = Iso14443_3aPollerStateColResFailed;\n                    ret = Iso14443_3aErrorColResFailed;\n                    break;\n                }\n                if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) {\n                    FURI_LOG_E(TAG, \"Sdd response wrong length\");\n                    instance->state = Iso14443_3aPollerStateColResFailed;\n                    ret = Iso14443_3aErrorColResFailed;\n                    break;\n                }\n                bit_buffer_write_bytes(\n                    instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp));\n                instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade;\n            } else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) {\n                instance->col_res.sel_req.sel_cmd =\n                    ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level);\n                instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0);\n                memcpy(\n                    instance->col_res.sel_req.nfcid,\n                    instance->col_res.sdd_resp.nfcid,\n                    sizeof(instance->col_res.sdd_resp.nfcid));\n                instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss;\n                bit_buffer_copy_bytes(\n                    instance->tx_buffer,\n                    (uint8_t*)&instance->col_res.sel_req,\n                    sizeof(instance->col_res.sel_req));\n                ret = iso14443_3a_poller_send_standard_frame(\n                    instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);\n                if(ret != Iso14443_3aErrorNone) {\n                    FURI_LOG_E(TAG, \"Sel request failed: %d\", ret);\n                    instance->state = Iso14443_3aPollerStateColResFailed;\n                    ret = Iso14443_3aErrorColResFailed;\n                    break;\n                }\n                if(bit_buffer_get_size_bytes(instance->rx_buffer) !=\n                   sizeof(instance->col_res.sel_resp)) {\n                    FURI_LOG_E(TAG, \"Sel response wrong length\");\n                    instance->state = Iso14443_3aPollerStateColResFailed;\n                    ret = Iso14443_3aErrorColResFailed;\n                    break;\n                }\n                bit_buffer_write_bytes(\n                    instance->rx_buffer,\n                    &instance->col_res.sel_resp,\n                    sizeof(instance->col_res.sel_resp));\n                FURI_LOG_T(TAG, \"Sel resp: %02X\", instance->col_res.sel_resp.sak);\n                if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) {\n                    // Copy part of UID\n                    memcpy(\n                        &instance->data->uid[instance->data->uid_len],\n                        &instance->col_res.sel_req.nfcid[1],\n                        3);\n                    instance->data->uid_len += 3;\n                    instance->col_res.cascade_level++;\n                    instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;\n                } else {\n                    FURI_LOG_T(TAG, \"Col resolution complete\");\n                    instance->data->sak = instance->col_res.sel_resp.sak;\n                    memcpy(\n                        &instance->data->uid[instance->data->uid_len],\n                        &instance->col_res.sel_req.nfcid[0],\n                        4);\n                    instance->data->uid_len += 4;\n                    instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess;\n                    instance->state = Iso14443_3aPollerStateActivated;\n                }\n            }\n        }\n\n        activated = (instance->state == Iso14443_3aPollerStateActivated);\n    } while(false);\n\n    if(activated && iso14443_3a_data) {\n        *iso14443_3a_data = *instance->data;\n    }\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_poller_txrx_custom_parity(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    NfcError error =\n        nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt);\n    if(error != NfcErrorNone) {\n        ret = iso14443_3a_poller_process_error(error);\n    }\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_poller_txrx(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError ret = Iso14443_3aErrorNone;\n    NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt);\n    if(error != NfcErrorNone) {\n        ret = iso14443_3a_poller_process_error(error);\n    }\n\n    return ret;\n}\n\nIso14443_3aError iso14443_3a_poller_send_standard_frame(\n    Iso14443_3aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError ret =\n        iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h",
    "content": "#pragma once\n\n#include \"iso14443_3a_poller.h\"\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U)\n\n#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl))\n#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits) & 0x0fU))\n#define ISO14443_3A_POLLER_SDD_CL               (0x88U)\n\ntypedef enum {\n    Iso14443_3aPollerColResStateStateIdle,\n    Iso14443_3aPollerColResStateStateNewCascade,\n    Iso14443_3aPollerColResStateStateSelectCascade,\n    Iso14443_3aPollerColResStateStateSuccess,\n    Iso14443_3aPollerColResStateStateFail,\n} Iso14443_3aPollerColResState;\n\ntypedef struct {\n    Iso14443_3aPollerColResState state;\n    Iso14443_3aSensResp sens_resp;\n    Iso14443_3aSddReq sdd_req;\n    Iso14443_3aSddResp sdd_resp;\n    Iso14443_3aSelReq sel_req;\n    Iso14443_3aSelResp sel_resp;\n    uint8_t cascade_level;\n} Iso14443_3aPollerColRes;\n\ntypedef enum {\n    Iso14443_3aPollerStateIdle,\n    Iso14443_3aPollerStateColResInProgress,\n    Iso14443_3aPollerStateColResFailed,\n    Iso14443_3aPollerStateActivated,\n} Iso14443_3aPollerState;\n\nstruct Iso14443_3aPoller {\n    Nfc* nfc;\n    Iso14443_3aPollerState state;\n    Iso14443_3aPollerColRes col_res;\n    Iso14443_3aData* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    NfcGenericEvent general_event;\n    Iso14443_3aPollerEvent iso14443_3a_event;\n    Iso14443_3aPollerEventData iso14443_3a_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c",
    "content": "#include \"iso14443_3a_poller_sync.h\"\n\n#include \"iso14443_3a_poller_i.h\" // IWYU pragma: keep\n#include <nfc/nfc_poller.h>\n\n#include <furi/furi.h>\n\n#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)\n\ntypedef struct {\n    Iso14443_3aPoller* instance;\n    FuriThreadId thread_id;\n    Iso14443_3aError error;\n    Iso14443_3aData data;\n} Iso14443_3aPollerContext;\n\nNfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    Iso14443_3aPollerContext* poller_context = context;\n    Iso14443_3aPoller* iso14443_3a_poller = event.instance;\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data);\n    }\n    poller_context->error = iso14443_3a_event->data->error;\n\n    furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);\n\n    return NfcCommandStop;\n}\n\nIso14443_3aError iso14443_3a_poller_sync_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) {\n    furi_check(nfc);\n    furi_check(iso14443_3a_data);\n\n    Iso14443_3aPollerContext poller_context = {};\n    poller_context.thread_id = furi_thread_get_current_id();\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a);\n    nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context);\n    furi_thread_flags_wait(\n        ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    if(poller_context.error == Iso14443_3aErrorNone) {\n        *iso14443_3a_data = poller_context.data;\n    }\n\n    return poller_context.error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h",
    "content": "#pragma once\n\n#include \"iso14443_3a.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nIso14443_3aError iso14443_3a_poller_sync_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b.c",
    "content": "#include \"iso14443_3b_i.h\"\n\n#include <furi.h>\n#include <nfc/protocols/nfc_device_base_i.h>\n\n#include <nfc/nfc_common.h>\n#include <nfc/helpers/iso14443_crc.h>\n\n#define ISO14443_3B_PROTOCOL_NAME \"ISO14443-3B\"\n#define ISO14443_3B_DEVICE_NAME   \"ISO14443-3B (Unknown)\"\n\n#define ISO14443_3B_APP_DATA_KEY      \"Application data\"\n#define ISO14443_3B_PROTOCOL_INFO_KEY \"Protocol info\"\n\n#define ISO14443_3B_FDT_POLL_DEFAULT_FC (ISO14443_3B_FDT_POLL_FC)\n\nconst NfcDeviceBase nfc_device_iso14443_3b = {\n    .protocol_name = ISO14443_3B_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)iso14443_3b_alloc,\n    .free = (NfcDeviceFree)iso14443_3b_free,\n    .reset = (NfcDeviceReset)iso14443_3b_reset,\n    .copy = (NfcDeviceCopy)iso14443_3b_copy,\n    .verify = (NfcDeviceVerify)iso14443_3b_verify,\n    .load = (NfcDeviceLoad)iso14443_3b_load,\n    .save = (NfcDeviceSave)iso14443_3b_save,\n    .is_equal = (NfcDeviceEqual)iso14443_3b_is_equal,\n    .get_name = (NfcDeviceGetName)iso14443_3b_get_device_name,\n    .get_uid = (NfcDeviceGetUid)iso14443_3b_get_uid,\n    .set_uid = (NfcDeviceSetUid)iso14443_3b_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data,\n};\n\nIso14443_3bData* iso14443_3b_alloc(void) {\n    Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData));\n    return data;\n}\n\nvoid iso14443_3b_free(Iso14443_3bData* data) {\n    furi_check(data);\n\n    free(data);\n}\n\nvoid iso14443_3b_reset(Iso14443_3bData* data) {\n    furi_check(data);\n\n    memset(data, 0, sizeof(Iso14443_3bData));\n}\n\nvoid iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    *data = *other;\n}\n\nbool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) {\n    UNUSED(data);\n    UNUSED(device_type);\n    // No support for old ISO14443-3B\n    return false;\n}\n\nbool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool parsed = false;\n\n    do {\n        if(version < NFC_UNIFIED_FORMAT_VERSION) break;\n\n        if(!flipper_format_read_hex(\n               ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))\n            break;\n        if(!flipper_format_read_hex(\n               ff,\n               ISO14443_3B_PROTOCOL_INFO_KEY,\n               (uint8_t*)&data->protocol_info,\n               sizeof(Iso14443_3bProtocolInfo)))\n            break;\n\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\nbool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool saved = false;\n\n    do {\n        if(!flipper_format_write_comment_cstr(ff, ISO14443_3B_PROTOCOL_NAME \" specific data\"))\n            break;\n        if(!flipper_format_write_hex(\n               ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))\n            break;\n        if(!flipper_format_write_hex(\n               ff,\n               ISO14443_3B_PROTOCOL_INFO_KEY,\n               (uint8_t*)&data->protocol_info,\n               sizeof(Iso14443_3bProtocolInfo)))\n            break;\n        saved = true;\n    } while(false);\n\n    return saved;\n}\n\nbool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return memcmp(data, other, sizeof(Iso14443_3bData)) == 0;\n}\n\nconst char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n\n    return ISO14443_3B_DEVICE_NAME;\n}\n\nconst uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) {\n    furi_check(data);\n    furi_check(uid_len);\n\n    *uid_len = ISO14443_3B_UID_SIZE;\n    return data->uid;\n}\n\nbool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n    furi_check(uid);\n\n    const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE;\n\n    if(uid_valid) {\n        memcpy(data->uid, uid, uid_len);\n    }\n\n    return uid_valid;\n}\n\nIso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) {\n    UNUSED(data);\n    furi_crash(\"No base data\");\n}\n\nbool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data) {\n    furi_check(data);\n\n    return data->protocol_info.protocol_type == 0x01;\n}\n\nbool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate) {\n    furi_check(data);\n\n    const uint8_t capability = data->protocol_info.bit_rate_capability;\n\n    switch(bit_rate) {\n    case Iso14443_3bBitRateBoth106Kbit:\n        return capability == ISO14443_3B_BIT_RATE_BOTH_106KBIT;\n    case Iso14443_3bBitRatePiccToPcd212Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT;\n    case Iso14443_3bBitRatePiccToPcd424Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT;\n    case Iso14443_3bBitRatePiccToPcd848Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT;\n    case Iso14443_3bBitRatePcdToPicc212Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT;\n    case Iso14443_3bBitRatePcdToPicc424Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT;\n    case Iso14443_3bBitRatePcdToPicc848Kbit:\n        return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT;\n    default:\n        return false;\n    }\n}\n\nbool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option) {\n    furi_check(data);\n\n    switch(option) {\n    case Iso14443_3bFrameOptionNad:\n        return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_NAD;\n    case Iso14443_3bFrameOptionCid:\n        return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_CID;\n    default:\n        return false;\n    }\n}\n\nconst uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size) {\n    furi_check(data);\n    furi_check(data_size);\n\n    *data_size = ISO14443_3B_APP_DATA_SIZE;\n    return data->app_data;\n}\n\nuint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) {\n    furi_check(data);\n\n    const uint8_t fs_bits = data->protocol_info.max_frame_size;\n\n    if(fs_bits < 5) {\n        return fs_bits * 8 + 16;\n    } else if(fs_bits == 5) {\n        return 64;\n    } else if(fs_bits == 6) {\n        return 96;\n    } else if(fs_bits < 13) {\n        return 128U << (fs_bits - 7);\n    } else {\n        return 0;\n    }\n}\n\nuint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) {\n    furi_check(data);\n\n    const uint8_t fwi = data->protocol_info.fwi;\n    return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base.h>\n\n#include <core/string.h>\n#include <toolbox/bit_buffer.h>\n#include <flipper_format/flipper_format.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_3bErrorNone,\n    Iso14443_3bErrorNotPresent,\n    Iso14443_3bErrorColResFailed,\n    Iso14443_3bErrorBufferOverflow,\n    Iso14443_3bErrorCommunication,\n    Iso14443_3bErrorFieldOff,\n    Iso14443_3bErrorWrongCrc,\n    Iso14443_3bErrorTimeout,\n} Iso14443_3bError;\n\ntypedef enum {\n    Iso14443_3bBitRateBoth106Kbit,\n    Iso14443_3bBitRatePiccToPcd212Kbit,\n    Iso14443_3bBitRatePiccToPcd424Kbit,\n    Iso14443_3bBitRatePiccToPcd848Kbit,\n    Iso14443_3bBitRatePcdToPicc212Kbit,\n    Iso14443_3bBitRatePcdToPicc424Kbit,\n    Iso14443_3bBitRatePcdToPicc848Kbit,\n} Iso14443_3bBitRate;\n\ntypedef enum {\n    Iso14443_3bFrameOptionNad,\n    Iso14443_3bFrameOptionCid,\n} Iso14443_3bFrameOption;\n\ntypedef struct Iso14443_3bData Iso14443_3bData;\n\n// Virtual methods\n\nIso14443_3bData* iso14443_3b_alloc(void);\n\nvoid iso14443_3b_free(Iso14443_3bData* data);\n\nvoid iso14443_3b_reset(Iso14443_3bData* data);\n\nvoid iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other);\n\nbool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type);\n\nbool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version);\n\nbool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff);\n\nbool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other);\n\nconst char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len);\n\nbool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data);\n\n// Getters and tests\n\nbool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data);\n\nbool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate);\n\nbool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option);\n\nconst uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size);\n\nuint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data);\n\nuint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_iso14443_3b;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_i.h",
    "content": "#pragma once\n\n#include \"iso14443_3b.h\"\n\n#define ISO14443_3B_UID_SIZE      (4U)\n#define ISO14443_3B_APP_DATA_SIZE (4U)\n\n#define ISO14443_3B_GUARD_TIME_US    (5000U)\n#define ISO14443_3B_FDT_POLL_FC      (9000U)\n#define ISO14443_3B_POLL_POLL_MIN_US (1280U)\n\n#define ISO14443_3B_BIT_RATE_BOTH_106KBIT         (0U << 0)\n#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT  (1U << 0)\n#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT  (1U << 1)\n#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT  (1U << 2)\n#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT  (1U << 4)\n#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT  (1U << 5)\n#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT  (1U << 6)\n#define ISO14443_3B_BIT_RATE_BOTH_SAME_COMPULSORY (1U << 7)\n\n#define ISO14443_3B_FRAME_OPTION_NAD (1U << 1)\n#define ISO14443_3B_FRAME_OPTION_CID (1U << 0)\n\ntypedef struct {\n    uint8_t bit_rate_capability;\n    uint8_t protocol_type  : 4;\n    uint8_t max_frame_size : 4;\n    uint8_t fo             : 2;\n    uint8_t adc            : 2;\n    uint8_t fwi            : 4;\n} Iso14443_3bProtocolInfo;\n\nstruct Iso14443_3bData {\n    uint8_t uid[ISO14443_3B_UID_SIZE];\n    uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];\n    Iso14443_3bProtocolInfo protocol_info;\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c",
    "content": "#include \"iso14443_3b_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"ISO14443_3bPoller\"\n\nconst Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic Iso14443_3bPoller* iso14443_3b_poller_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n\n    Iso14443_3bPoller* instance = malloc(sizeof(Iso14443_3bPoller));\n    instance->nfc = nfc;\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);\n\n    nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b);\n    nfc_set_guard_time_us(instance->nfc, ISO14443_3B_GUARD_TIME_US);\n    nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3B_FDT_POLL_FC);\n    nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3B_POLL_POLL_MIN_US);\n    instance->data = iso14443_3b_alloc();\n\n    instance->iso14443_3b_event.data = &instance->iso14443_3b_event_data;\n    instance->general_event.protocol = NfcProtocolIso14443_3b;\n    instance->general_event.event_data = &instance->iso14443_3b_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void iso14443_3b_poller_free(Iso14443_3bPoller* instance) {\n    furi_assert(instance);\n\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(instance->data);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    iso14443_3b_free(instance->data);\n    free(instance);\n}\n\nstatic void iso14443_3b_poller_set_callback(\n    Iso14443_3bPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand iso14443_3b_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    Iso14443_3bPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        if(instance->state != Iso14443_3bPollerStateActivated) {\n            Iso14443_3bError error = iso14443_3b_poller_activate(instance, instance->data);\n            if(error == Iso14443_3bErrorNone) {\n                instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;\n                instance->iso14443_3b_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n            } else {\n                instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeError;\n                instance->iso14443_3b_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n                // Add delay to switch context\n                furi_delay_ms(100);\n            }\n        } else {\n            instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;\n            instance->iso14443_3b_event_data.error = Iso14443_3bErrorNone;\n            command = instance->callback(instance->general_event, instance->context);\n        }\n    }\n\n    return command;\n}\n\nstatic bool iso14443_3b_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n\n    bool protocol_detected = false;\n    Iso14443_3bPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    furi_assert(instance->state == Iso14443_3bPollerStateIdle);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        Iso14443_3bError error = iso14443_3b_poller_activate(instance, instance->data);\n        protocol_detected = (error == Iso14443_3bErrorNone);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_iso14443_3b = {\n    .alloc = (NfcPollerAlloc)iso14443_3b_poller_alloc,\n    .free = (NfcPollerFree)iso14443_3b_poller_free,\n    .set_callback = (NfcPollerSetCallback)iso14443_3b_poller_set_callback,\n    .run = (NfcPollerRun)iso14443_3b_poller_run,\n    .detect = (NfcPollerDetect)iso14443_3b_poller_detect,\n    .get_data = (NfcPollerGetData)iso14443_3b_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h",
    "content": "#pragma once\n\n#include \"iso14443_3b.h\"\n#include <lib/nfc/nfc.h>\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Iso14443_3bPoller opaque type definition.\n */\ntypedef struct Iso14443_3bPoller Iso14443_3bPoller;\n\n/**\n * @brief Enumeration of possible Iso14443_3b poller event types.\n */\ntypedef enum {\n    Iso14443_3bPollerEventTypeError, /**< An error occured during activation procedure. */\n    Iso14443_3bPollerEventTypeReady, /**< The card was activated by the poller. */\n} Iso14443_3bPollerEventType;\n\n/**\n * @brief Iso14443_3b poller event data.\n */\ntypedef union {\n    Iso14443_3bError error; /**< Error code indicating card activation fail reason. */\n} Iso14443_3bPollerEventData;\n\n/**\n * @brief Iso14443_3b poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    Iso14443_3bPollerEventType type; /**< Type of emmitted event. */\n    Iso14443_3bPollerEventData* data; /**< Pointer to event specific data. */\n} Iso14443_3bPollerEvent;\n\n/**\n * @brief Transmit and receive Iso14443_3b frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return Iso14443_3bErrorNone on success, an error code on failure.\n */\nIso14443_3bError iso14443_3b_poller_send_frame(\n    Iso14443_3bPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Perform collision resolution procedure.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perfoms the collision resolution procedure as defined in Iso14443-3b. The data\n * field will be filled with Iso14443-3b data on success.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the Iso14443_3b data structure to be filled.\n * @return Iso14443_3bErrorNone on success, an error code on failure.\n */\nIso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data);\n\n/**\n * @brief Send HALT command to the card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Halts card and changes internal Iso14443_3bPoller state to Idle.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return Iso14443_3bErrorNone on success, an error code on failure.\n */\nIso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_iso14443_3b;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c",
    "content": "#include \"iso14443_3b_poller_i.h\"\n\n#include <nfc/helpers/iso14443_crc.h>\n\n#define TAG \"Iso14443_3bPoller\"\n\n#define ISO14443_3B_ATTRIB_FRAME_SIZE_256 (0x08)\n\nstatic Iso14443_3bError iso14443_3b_poller_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return Iso14443_3bErrorNone;\n    case NfcErrorTimeout:\n        return Iso14443_3bErrorTimeout;\n    default:\n        return Iso14443_3bErrorNotPresent;\n    }\n}\n\nstatic Iso14443_3bError iso14443_3b_poller_prepare_trx(Iso14443_3bPoller* instance) {\n    furi_assert(instance);\n\n    if(instance->state == Iso14443_3bPollerStateIdle) {\n        return iso14443_3b_poller_activate(instance, NULL);\n    }\n\n    return Iso14443_3bErrorNone;\n}\n\nstatic Iso14443_3bError iso14443_3b_poller_frame_exchange(\n    Iso14443_3bPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_assert(instance);\n\n    const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);\n    furi_assert(\n        tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);\n\n    bit_buffer_copy(instance->tx_buffer, tx_buffer);\n    iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);\n\n    Iso14443_3bError ret = Iso14443_3bErrorNone;\n\n    do {\n        NfcError error =\n            nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);\n        if(error != NfcErrorNone) {\n            ret = iso14443_3b_poller_process_error(error);\n            break;\n        }\n\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n        if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {\n            ret = Iso14443_3bErrorWrongCrc;\n            break;\n        }\n\n        iso14443_crc_trim(rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(data);\n\n    iso14443_3b_reset(data);\n\n    Iso14443_3bError ret;\n\n    do {\n        instance->state = Iso14443_3bPollerStateColResInProgress;\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        // Send REQB\n        bit_buffer_append_byte(instance->tx_buffer, 0x05);\n        bit_buffer_append_byte(instance->tx_buffer, 0x00);\n        bit_buffer_append_byte(instance->tx_buffer, 0x08);\n\n        ret = iso14443_3b_poller_frame_exchange(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);\n        if(ret != Iso14443_3bErrorNone) {\n            instance->state = Iso14443_3bPollerStateColResFailed;\n            break;\n        }\n\n        typedef struct {\n            uint8_t flag;\n            uint8_t uid[ISO14443_3B_UID_SIZE];\n            uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];\n            Iso14443_3bProtocolInfo protocol_info;\n        } Iso14443_3bAtqBLayout;\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(Iso14443_3bAtqBLayout)) {\n            FURI_LOG_D(TAG, \"Unexpected REQB response\");\n            instance->state = Iso14443_3bPollerStateColResFailed;\n            ret = Iso14443_3bErrorCommunication;\n            break;\n        }\n\n        instance->state = Iso14443_3bPollerStateActivationInProgress;\n\n        const Iso14443_3bAtqBLayout* atqb =\n            (const Iso14443_3bAtqBLayout*)bit_buffer_get_data(instance->rx_buffer);\n\n        memcpy(data->uid, atqb->uid, ISO14443_3B_UID_SIZE);\n        memcpy(data->app_data, atqb->app_data, ISO14443_3B_APP_DATA_SIZE);\n\n        data->protocol_info = atqb->protocol_info;\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        // Send ATTRIB\n        uint8_t cid = 0;\n        bit_buffer_append_byte(instance->tx_buffer, 0x1d);\n        bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE);\n        bit_buffer_append_byte(instance->tx_buffer, 0x00);\n        bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256);\n        bit_buffer_append_byte(instance->tx_buffer, 0x01);\n        bit_buffer_append_byte(instance->tx_buffer, cid);\n\n        ret = iso14443_3b_poller_frame_exchange(\n            instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data));\n        if(ret != Iso14443_3bErrorNone) {\n            instance->state = Iso14443_3bPollerStateActivationFailed;\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) {\n            FURI_LOG_W(\n                TAG,\n                \"Unexpected ATTRIB response length: %zu\",\n                bit_buffer_get_size_bytes(instance->rx_buffer));\n        }\n\n        uint8_t cid_received = bit_buffer_get_byte(instance->rx_buffer, 0);\n        // 15 bit is RFU\n        if((cid_received & 0x7f) != cid) {\n            FURI_LOG_D(TAG, \"Incorrect CID in ATTRIB response: %02X\", cid_received);\n            instance->state = Iso14443_3bPollerStateActivationFailed;\n            ret = Iso14443_3bErrorCommunication;\n            break;\n        }\n\n        instance->state = Iso14443_3bPollerStateActivated;\n    } while(false);\n\n    return ret;\n}\n\nIso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    bit_buffer_append_byte(instance->tx_buffer, 0x50);\n    bit_buffer_append_bytes(instance->tx_buffer, instance->data->uid, ISO14443_3B_UID_SIZE);\n\n    Iso14443_3bError ret;\n\n    do {\n        ret = iso14443_3b_poller_frame_exchange(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);\n        if(ret != Iso14443_3bErrorNone) {\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(uint8_t) ||\n           bit_buffer_get_byte(instance->rx_buffer, 0) != 0) {\n            ret = Iso14443_3bErrorCommunication;\n            break;\n        }\n\n        instance->state = Iso14443_3bPollerStateIdle;\n    } while(false);\n\n    return ret;\n}\n\nIso14443_3bError iso14443_3b_poller_send_frame(\n    Iso14443_3bPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3bError ret;\n\n    do {\n        ret = iso14443_3b_poller_prepare_trx(instance);\n        if(ret != Iso14443_3bErrorNone) break;\n\n        ret = iso14443_3b_poller_frame_exchange(\n            instance, tx_buffer, rx_buffer, iso14443_3b_get_fwt_fc_max(instance->data));\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h",
    "content": "#pragma once\n\n#include \"iso14443_3b_poller.h\"\n#include \"iso14443_3b_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO14443_3B_POLLER_MAX_BUFFER_SIZE (256U)\n\ntypedef enum {\n    Iso14443_3bPollerStateIdle,\n    Iso14443_3bPollerStateColResInProgress,\n    Iso14443_3bPollerStateColResFailed,\n    Iso14443_3bPollerStateActivationInProgress,\n    Iso14443_3bPollerStateActivationFailed,\n    Iso14443_3bPollerStateActivated,\n} Iso14443_3bPollerState;\n\nstruct Iso14443_3bPoller {\n    Nfc* nfc;\n    Iso14443_3bPollerState state;\n    Iso14443_3bData* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    NfcGenericEvent general_event;\n    Iso14443_3bPollerEvent iso14443_3b_event;\n    Iso14443_3bPollerEventData iso14443_3b_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a.c",
    "content": "#include \"iso14443_4a_i.h\"\n\n#include <furi.h>\n\n#define ISO14443_4A_PROTOCOL_NAME \"ISO14443-4A\"\n#define ISO14443_4A_DEVICE_NAME   \"ISO14443-4A (Unknown)\"\n\n#define ISO14443_4A_T0_KEY    \"T0\"\n#define ISO14443_4A_TA1_KEY   \"TA(1)\"\n#define ISO14443_4A_TB1_KEY   \"TB(1)\"\n#define ISO14443_4A_TC1_KEY   \"TC(1)\"\n#define ISO14443_4A_T1_TK_KEY \"T1...Tk\"\n\n#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC\n\ntypedef enum {\n    Iso14443_4aInterfaceByteTA1,\n    Iso14443_4aInterfaceByteTB1,\n    Iso14443_4aInterfaceByteTC1,\n} Iso14443_4aInterfaceByte;\n\nconst NfcDeviceBase nfc_device_iso14443_4a = {\n    .protocol_name = ISO14443_4A_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)iso14443_4a_alloc,\n    .free = (NfcDeviceFree)iso14443_4a_free,\n    .reset = (NfcDeviceReset)iso14443_4a_reset,\n    .copy = (NfcDeviceCopy)iso14443_4a_copy,\n    .verify = (NfcDeviceVerify)iso14443_4a_verify,\n    .load = (NfcDeviceLoad)iso14443_4a_load,\n    .save = (NfcDeviceSave)iso14443_4a_save,\n    .is_equal = (NfcDeviceEqual)iso14443_4a_is_equal,\n    .get_name = (NfcDeviceGetName)iso14443_4a_get_device_name,\n    .get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid,\n    .set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data,\n};\n\nIso14443_4aData* iso14443_4a_alloc(void) {\n    Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData));\n\n    data->iso14443_3a_data = iso14443_3a_alloc();\n    data->ats_data.t1_tk = simple_array_alloc(&simple_array_config_uint8_t);\n\n    return data;\n}\n\nvoid iso14443_4a_free(Iso14443_4aData* data) {\n    furi_check(data);\n\n    simple_array_free(data->ats_data.t1_tk);\n    iso14443_3a_free(data->iso14443_3a_data);\n\n    free(data);\n}\n\nvoid iso14443_4a_reset(Iso14443_4aData* data) {\n    furi_check(data);\n\n    iso14443_3a_reset(data->iso14443_3a_data);\n\n    data->ats_data.tl = 1;\n    data->ats_data.t0 = 0;\n    data->ats_data.ta_1 = 0;\n    data->ats_data.tb_1 = 0;\n    data->ats_data.tc_1 = 0;\n\n    simple_array_reset(data->ats_data.t1_tk);\n}\n\nvoid iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);\n\n    data->ats_data.tl = other->ats_data.tl;\n    data->ats_data.t0 = other->ats_data.t0;\n    data->ats_data.ta_1 = other->ats_data.ta_1;\n    data->ats_data.tb_1 = other->ats_data.tb_1;\n    data->ats_data.tc_1 = other->ats_data.tc_1;\n\n    simple_array_copy(data->ats_data.t1_tk, other->ats_data.t1_tk);\n}\n\nbool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) {\n    UNUSED(data);\n    UNUSED(device_type);\n\n    // Empty, unified file format only\n    return false;\n}\n\nbool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool parsed = false;\n\n    do {\n        if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;\n\n        Iso14443_4aAtsData* ats_data = &data->ats_data;\n\n        ats_data->tl = 1;\n\n        if(flipper_format_key_exist(ff, ISO14443_4A_T0_KEY)) {\n            if(!flipper_format_read_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;\n            ++ats_data->tl;\n        }\n\n        if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {\n            if(!flipper_format_key_exist(ff, ISO14443_4A_TA1_KEY)) break;\n            if(!flipper_format_read_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;\n            ++ats_data->tl;\n        }\n        if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {\n            if(!flipper_format_key_exist(ff, ISO14443_4A_TB1_KEY)) break;\n            if(!flipper_format_read_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;\n            ++ats_data->tl;\n        }\n        if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {\n            if(!flipper_format_key_exist(ff, ISO14443_4A_TC1_KEY)) break;\n            if(!flipper_format_read_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;\n            ++ats_data->tl;\n        }\n\n        if(flipper_format_key_exist(ff, ISO14443_4A_T1_TK_KEY)) {\n            uint32_t t1_tk_size;\n            if(!flipper_format_get_value_count(ff, ISO14443_4A_T1_TK_KEY, &t1_tk_size)) break;\n\n            if(t1_tk_size > 0) {\n                simple_array_init(ats_data->t1_tk, t1_tk_size);\n                if(!flipper_format_read_hex(\n                       ff,\n                       ISO14443_4A_T1_TK_KEY,\n                       simple_array_get_data(ats_data->t1_tk),\n                       t1_tk_size))\n                    break;\n                ats_data->tl += t1_tk_size;\n            }\n        }\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\nbool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool saved = false;\n\n    do {\n        if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;\n        if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME \" specific data\"))\n            break;\n\n        const Iso14443_4aAtsData* ats_data = &data->ats_data;\n\n        if(ats_data->tl > 1) {\n            if(!flipper_format_write_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;\n\n            if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {\n                if(!flipper_format_write_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;\n            }\n            if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {\n                if(!flipper_format_write_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;\n            }\n            if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {\n                if(!flipper_format_write_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;\n            }\n\n            const uint32_t t1_tk_size = simple_array_get_count(ats_data->t1_tk);\n            if(t1_tk_size > 0) {\n                if(!flipper_format_write_hex(\n                       ff,\n                       ISO14443_4A_T1_TK_KEY,\n                       simple_array_cget_data(ats_data->t1_tk),\n                       t1_tk_size))\n                    break;\n            }\n        }\n        saved = true;\n    } while(false);\n\n    return saved;\n}\n\nbool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data);\n}\n\nconst char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n    return ISO14443_4A_DEVICE_NAME;\n}\n\nconst uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) {\n    furi_check(data);\n    furi_check(uid_len);\n\n    return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);\n}\n\nbool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);\n}\n\nIso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) {\n    furi_check(data);\n\n    return data->iso14443_3a_data;\n}\n\nuint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {\n    furi_check(data);\n\n    const uint8_t fsci = data->ats_data.t0 & 0x0F;\n\n    if(fsci < 5) {\n        return fsci * 8 + 16;\n    } else if(fsci == 5) {\n        return 64;\n    } else if(fsci == 6) {\n        return 96;\n    } else if(fsci < 13) {\n        return 128U << (fsci - 7);\n    } else {\n        return 0;\n    }\n}\n\nuint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {\n    furi_check(data);\n\n    uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;\n\n    do {\n        if(!(data->ats_data.tl > 1)) break;\n        if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break;\n\n        const uint8_t fwi = data->ats_data.tb_1 >> 4;\n        if(fwi == 0x0F) break;\n\n        fwt_fc_max = 4096UL << fwi;\n    } while(false);\n\n    return fwt_fc_max;\n}\n\nconst uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {\n    furi_check(data);\n    furi_check(count);\n\n    *count = simple_array_get_count(data->ats_data.t1_tk);\n    const uint8_t* hist_bytes = NULL;\n    if(*count > 0) {\n        hist_bytes = simple_array_cget_data(data->ats_data.t1_tk);\n    }\n\n    return hist_bytes;\n}\n\nbool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) {\n    furi_check(data);\n\n    if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1))\n        return bit_rate == Iso14443_4aBitRateBoth106Kbit;\n\n    const uint8_t ta_1 = data->ats_data.ta_1;\n\n    switch(bit_rate) {\n    case Iso14443_4aBitRateBoth106Kbit:\n        return ta_1 == ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY;\n    case Iso14443_4aBitRatePiccToPcd212Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT;\n    case Iso14443_4aBitRatePiccToPcd424Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT;\n    case Iso14443_4aBitRatePiccToPcd848Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT;\n    case Iso14443_4aBitRatePcdToPicc212Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT;\n    case Iso14443_4aBitRatePcdToPicc424Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT;\n    case Iso14443_4aBitRatePcdToPicc848Kbit:\n        return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT;\n    default:\n        return false;\n    }\n}\n\nbool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) {\n    furi_check(data);\n\n    const Iso14443_4aAtsData* ats_data = &data->ats_data;\n    if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false;\n\n    switch(option) {\n    case Iso14443_4aFrameOptionNad:\n        return ats_data->tc_1 & ISO14443_4A_ATS_TC1_NAD;\n    case Iso14443_4aFrameOptionCid:\n        return ats_data->tc_1 & ISO14443_4A_ATS_TC1_CID;\n    default:\n        return false;\n    }\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a.h>\n\n#include <lib/toolbox/simple_array.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_4aErrorNone,\n    Iso14443_4aErrorNotPresent,\n    Iso14443_4aErrorProtocol,\n    Iso14443_4aErrorTimeout,\n} Iso14443_4aError;\n\ntypedef enum {\n    Iso14443_4aBitRateBoth106Kbit,\n    Iso14443_4aBitRatePiccToPcd212Kbit,\n    Iso14443_4aBitRatePiccToPcd424Kbit,\n    Iso14443_4aBitRatePiccToPcd848Kbit,\n    Iso14443_4aBitRatePcdToPicc212Kbit,\n    Iso14443_4aBitRatePcdToPicc424Kbit,\n    Iso14443_4aBitRatePcdToPicc848Kbit,\n} Iso14443_4aBitRate;\n\ntypedef enum {\n    Iso14443_4aFrameOptionNad,\n    Iso14443_4aFrameOptionCid,\n} Iso14443_4aFrameOption;\n\ntypedef struct {\n    uint8_t tl;\n    uint8_t t0;\n    uint8_t ta_1;\n    uint8_t tb_1;\n    uint8_t tc_1;\n    SimpleArray* t1_tk;\n} Iso14443_4aAtsData;\n\ntypedef struct {\n    Iso14443_3aData* iso14443_3a_data;\n    Iso14443_4aAtsData ats_data;\n} Iso14443_4aData;\n\n// Virtual methods\n\nIso14443_4aData* iso14443_4a_alloc(void);\n\nvoid iso14443_4a_free(Iso14443_4aData* data);\n\nvoid iso14443_4a_reset(Iso14443_4aData* data);\n\nvoid iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other);\n\nbool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type);\n\nbool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version);\n\nbool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff);\n\nbool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other);\n\nconst char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len);\n\nbool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data);\n\n// Getters & Tests\n\nuint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data);\n\nuint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data);\n\nconst uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count);\n\nbool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate);\n\nbool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_iso14443_4a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c",
    "content": "#include \"iso14443_4a_i.h\"\n\nbool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf) {\n    bool can_parse = false;\n\n    do {\n        const size_t buf_size = bit_buffer_get_size_bytes(buf);\n        if(buf_size == 0) break;\n\n        size_t current_index = 0;\n\n        const uint8_t tl = bit_buffer_get_byte(buf, current_index++);\n        if(tl != buf_size) break;\n\n        data->tl = tl;\n\n        if(tl > 1) {\n            const uint8_t t0 = bit_buffer_get_byte(buf, current_index++);\n\n            const bool has_ta_1 = t0 & ISO14443_4A_ATS_T0_TA1;\n            const bool has_tb_1 = t0 & ISO14443_4A_ATS_T0_TB1;\n            const bool has_tc_1 = t0 & ISO14443_4A_ATS_T0_TC1;\n\n            const uint8_t buf_size_min =\n                2 + (has_ta_1 ? 1 : 0) + (has_tb_1 ? 1 : 0) + (has_tc_1 ? 1 : 0);\n\n            if(buf_size < buf_size_min) break;\n\n            data->t0 = t0;\n\n            if(has_ta_1) {\n                data->ta_1 = bit_buffer_get_byte(buf, current_index++);\n            }\n            if(has_tb_1) {\n                data->tb_1 = bit_buffer_get_byte(buf, current_index++);\n            }\n            if(has_tc_1) {\n                data->tc_1 = bit_buffer_get_byte(buf, current_index++);\n            }\n\n            const uint8_t t1_tk_size = buf_size - buf_size_min;\n\n            if(t1_tk_size > 0) {\n                simple_array_init(data->t1_tk, t1_tk_size);\n                bit_buffer_write_bytes_mid(\n                    buf, simple_array_get_data(data->t1_tk), current_index, t1_tk_size);\n            }\n        }\n\n        can_parse = true;\n    } while(false);\n\n    return can_parse;\n}\n\nIso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) {\n    switch(error) {\n    case Iso14443_3aErrorNone:\n        return Iso14443_4aErrorNone;\n    case Iso14443_3aErrorNotPresent:\n        return Iso14443_4aErrorNotPresent;\n    case Iso14443_3aErrorColResFailed:\n    case Iso14443_3aErrorCommunication:\n    case Iso14443_3aErrorWrongCrc:\n        return Iso14443_4aErrorProtocol;\n    case Iso14443_3aErrorTimeout:\n        return Iso14443_4aErrorTimeout;\n    default:\n        return Iso14443_4aErrorProtocol;\n    }\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h",
    "content": "#pragma once\n\n#include \"iso14443_4a.h\"\n\n#define ISO14443_4A_CMD_READ_ATS (0xE0)\n\n// ATS bit definitions\n#define ISO14443_4A_ATS_T0_TA1 (1U << 4)\n#define ISO14443_4A_ATS_T0_TB1 (1U << 5)\n#define ISO14443_4A_ATS_T0_TC1 (1U << 6)\n\n#define ISO14443_4A_ATS_TA1_BOTH_106KBIT         (0U << 0)\n#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT  (1U << 0)\n#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT  (1U << 1)\n#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT  (1U << 2)\n#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT  (1U << 4)\n#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT  (1U << 5)\n#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT  (1U << 6)\n#define ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY (1U << 7)\n\n#define ISO14443_4A_ATS_TC1_NAD (1U << 0)\n#define ISO14443_4A_ATS_TC1_CID (1U << 1)\n\nbool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf);\n\nIso14443_4aError iso14443_4a_process_error(Iso14443_3aError error);\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c",
    "content": "#include \"iso14443_4a_listener_i.h\"\n\n#include <furi.h>\n#include <nfc/protocols/nfc_listener_base.h>\n\n#define TAG \"Iso14443_4aListener\"\n\n#define ISO14443_4A_LISTENER_BUF_SIZE (256U)\n\nstatic Iso14443_4aListener*\n    iso14443_4a_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, Iso14443_4aData* data) {\n    furi_assert(iso14443_3a_listener);\n\n    Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener));\n    instance->iso14443_3a_listener = iso14443_3a_listener;\n    instance->data = data;\n\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE);\n\n    instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;\n    instance->generic_event.protocol = NfcProtocolIso14443_4a;\n    instance->generic_event.instance = instance;\n    instance->generic_event.event_data = &instance->iso14443_4a_event;\n\n    return instance;\n}\n\nstatic void iso14443_4a_listener_free(Iso14443_4aListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    free(instance);\n}\n\nstatic void iso14443_4a_listener_set_callback(\n    Iso14443_4aListener* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic const Iso14443_4aData* iso14443_4a_listener_get_data(Iso14443_4aListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n    furi_assert(event.event_data);\n\n    Iso14443_4aListener* instance = context;\n    Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;\n    BitBuffer* rx_buffer = iso14443_3a_event->data->buffer;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {\n        if(instance->state == Iso14443_4aListenerStateIdle) {\n            if(bit_buffer_get_size_bytes(rx_buffer) == 2 &&\n               bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) {\n                if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) ==\n                   Iso14443_4aErrorNone) {\n                    instance->state = Iso14443_4aListenerStateActive;\n                }\n            }\n        } else {\n            instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData;\n            instance->iso14443_4a_event.data->buffer = rx_buffer;\n\n            if(instance->callback) {\n                command = instance->callback(instance->generic_event, instance->context);\n            }\n        }\n    } else if(\n        iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ||\n        iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) {\n        instance->state = Iso14443_4aListenerStateIdle;\n\n        instance->iso14443_4a_event.type = iso14443_3a_event->type ==\n                                                   Iso14443_3aListenerEventTypeHalted ?\n                                               Iso14443_4aListenerEventTypeHalted :\n                                               Iso14443_4aListenerEventTypeFieldOff;\n\n        if(instance->callback) {\n            command = instance->callback(instance->generic_event, instance->context);\n        }\n    }\n\n    return command;\n}\n\nconst NfcListenerBase nfc_listener_iso14443_4a = {\n    .alloc = (NfcListenerAlloc)iso14443_4a_listener_alloc,\n    .free = (NfcListenerFree)iso14443_4a_listener_free,\n    .set_callback = (NfcListenerSetCallback)iso14443_4a_listener_set_callback,\n    .get_data = (NfcListenerGetData)iso14443_4a_listener_get_data,\n    .run = (NfcListenerRun)iso14443_4a_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>\n\n#include \"iso14443_4a.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso14443_4aListener Iso14443_4aListener;\n\ntypedef enum {\n    Iso14443_4aListenerEventTypeHalted,\n    Iso14443_4aListenerEventTypeFieldOff,\n    Iso14443_4aListenerEventTypeReceivedData,\n} Iso14443_4aListenerEventType;\n\ntypedef struct {\n    BitBuffer* buffer;\n} Iso14443_4aListenerEventData;\n\ntypedef struct {\n    Iso14443_4aListenerEventType type;\n    Iso14443_4aListenerEventData* data;\n} Iso14443_4aListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\nextern const NfcListenerBase nfc_listener_iso14443_4a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c",
    "content": "#include \"iso14443_4a_listener_i.h\"\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>\n\nIso14443_4aError\n    iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data) {\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_byte(instance->tx_buffer, data->tl);\n\n    if(data->tl > 1) {\n        bit_buffer_append_byte(instance->tx_buffer, data->t0);\n        if(data->t0 & ISO14443_4A_ATS_T0_TA1) {\n            bit_buffer_append_byte(instance->tx_buffer, data->ta_1);\n        }\n        if(data->t0 & ISO14443_4A_ATS_T0_TB1) {\n            bit_buffer_append_byte(instance->tx_buffer, data->tb_1);\n        }\n        if(data->t0 & ISO14443_4A_ATS_T0_TC1) {\n            bit_buffer_append_byte(instance->tx_buffer, data->tc_1);\n        }\n\n        const uint32_t t1_tk_size = simple_array_get_count(data->t1_tk);\n        if(t1_tk_size != 0) {\n            bit_buffer_append_bytes(\n                instance->tx_buffer, simple_array_cget_data(data->t1_tk), t1_tk_size);\n        }\n    }\n\n    const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame(\n        instance->iso14443_3a_listener, instance->tx_buffer);\n    return iso14443_4a_process_error(error);\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_generic_event.h>\n\n#include \"iso14443_4a_listener.h\"\n#include \"iso14443_4a_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_4aListenerStateIdle,\n    Iso14443_4aListenerStateActive,\n} Iso14443_4aListenerState;\n\nstruct Iso14443_4aListener {\n    Iso14443_3aListener* iso14443_3a_listener;\n    Iso14443_4aData* data;\n    Iso14443_4aListenerState state;\n\n    BitBuffer* tx_buffer;\n\n    NfcGenericEvent generic_event;\n    Iso14443_4aListenerEvent iso14443_4a_event;\n    Iso14443_4aListenerEventData iso14443_4a_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nIso14443_4aError\n    iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c",
    "content": "#include \"iso14443_4a_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"Iso14443_4aPoller\"\n\n#define ISO14443_4A_POLLER_BUF_SIZE (256U)\n\ntypedef NfcCommand (*Iso14443_4aPollerStateHandler)(Iso14443_4aPoller* instance);\n\nconst Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nstatic Iso14443_4aPoller* iso14443_4a_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {\n    Iso14443_4aPoller* instance = malloc(sizeof(Iso14443_4aPoller));\n    instance->iso14443_3a_poller = iso14443_3a_poller;\n    instance->data = iso14443_4a_alloc();\n    instance->iso14443_4_layer = iso14443_4_layer_alloc();\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);\n\n    instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;\n\n    instance->general_event.protocol = NfcProtocolIso14443_4a;\n    instance->general_event.event_data = &instance->iso14443_4a_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void iso14443_4a_poller_free(Iso14443_4aPoller* instance) {\n    furi_assert(instance);\n\n    iso14443_4a_free(instance->data);\n    iso14443_4_layer_free(instance->iso14443_4_layer);\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    free(instance);\n}\n\nstatic NfcCommand iso14443_4a_poller_handler_idle(Iso14443_4aPoller* instance) {\n    iso14443_3a_copy(\n        instance->data->iso14443_3a_data,\n        iso14443_3a_poller_get_data(instance->iso14443_3a_poller));\n\n    iso14443_4_layer_reset(instance->iso14443_4_layer);\n\n    instance->poller_state = Iso14443_4aPollerStateReadAts;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand iso14443_4a_poller_handler_read_ats(Iso14443_4aPoller* instance) {\n    Iso14443_4aError error = iso14443_4a_poller_read_ats(instance, &instance->data->ats_data);\n    if(error == Iso14443_4aErrorNone) {\n        FURI_LOG_D(TAG, \"Read ATS success\");\n        instance->poller_state = Iso14443_4aPollerStateReady;\n    } else {\n        FURI_LOG_D(TAG, \"Failed to read ATS\");\n        instance->poller_state = Iso14443_4aPollerStateError;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand iso14443_4a_poller_handler_error(Iso14443_4aPoller* instance) {\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    instance->iso14443_4a_event_data.error = instance->error;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->poller_state = Iso14443_4aPollerStateIdle;\n    return command;\n}\n\nstatic NfcCommand iso14443_4a_poller_handler_ready(Iso14443_4aPoller* instance) {\n    instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeReady;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic const Iso14443_4aPollerStateHandler\n    iso14443_4a_poller_state_handler[Iso14443_4aPollerStateNum] = {\n        [Iso14443_4aPollerStateIdle] = iso14443_4a_poller_handler_idle,\n        [Iso14443_4aPollerStateReadAts] = iso14443_4a_poller_handler_read_ats,\n        [Iso14443_4aPollerStateError] = iso14443_4a_poller_handler_error,\n        [Iso14443_4aPollerStateReady] = iso14443_4a_poller_handler_ready,\n};\n\nstatic void iso14443_4a_poller_set_callback(\n    Iso14443_4aPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand iso14443_4a_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    Iso14443_4aPoller* instance = context;\n    furi_assert(instance);\n    furi_assert(instance->callback);\n\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n    furi_assert(iso14443_3a_event);\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        command = iso14443_4a_poller_state_handler[instance->poller_state](instance);\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeError;\n        command = instance->callback(instance->general_event, instance->context);\n    }\n\n    return command;\n}\n\nstatic bool iso14443_4a_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    const Iso14443_4aPoller* instance = context;\n    furi_assert(instance);\n\n    const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n    furi_assert(iso14443_3a_event);\n    iso14443_3a_copy(\n        instance->data->iso14443_3a_data,\n        iso14443_3a_poller_get_data(instance->iso14443_3a_poller));\n\n    bool protocol_detected = false;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        protocol_detected = iso14443_3a_supports_iso14443_4(instance->data->iso14443_3a_data);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_iso14443_4a = {\n    .alloc = (NfcPollerAlloc)iso14443_4a_poller_alloc,\n    .free = (NfcPollerFree)iso14443_4a_poller_free,\n    .set_callback = (NfcPollerSetCallback)iso14443_4a_poller_set_callback,\n    .run = (NfcPollerRun)iso14443_4a_poller_run,\n    .detect = (NfcPollerDetect)iso14443_4a_poller_detect,\n    .get_data = (NfcPollerGetData)iso14443_4a_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\n#include \"iso14443_4a.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Iso14443_4aPoller opaque type definition.\n */\ntypedef struct Iso14443_4aPoller Iso14443_4aPoller;\n\n/**\n * @brief Enumeration of possible Iso14443_4a poller event types.\n */\ntypedef enum {\n    Iso14443_4aPollerEventTypeError, /**< An error occured during activation procedure. */\n    Iso14443_4aPollerEventTypeReady, /**< The card was activated by the poller. */\n} Iso14443_4aPollerEventType;\n\n/**\n * @brief Iso14443_4a poller event data.\n */\ntypedef union {\n    Iso14443_4aError error; /**< Error code indicating card activation fail reason. */\n} Iso14443_4aPollerEventData;\n\n/**\n * @brief Iso14443_4a poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    Iso14443_4aPollerEventType type; /**< Type of emmitted event. */\n    Iso14443_4aPollerEventData* data; /**< Pointer to event specific data. */\n} Iso14443_4aPollerEvent;\n\n/**\n * @brief Transmit and receive Iso14443_4a blocks in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer. The fwt parameter is calculated during activation procedure.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError iso14443_4a_poller_send_block(\n    Iso14443_4aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Transmit and receive Iso14443_4a chained block in poller mode. Also it\n * automatically modifies PCB packet byte with appropriate bits then resets them back \n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer. The fwt parameter is calculated during activation procedure.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError iso14443_4a_poller_send_chain_block(\n    Iso14443_4aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Transmit Iso14443_4a R-block in poller mode. This block never contains\n * data, but can contain CID and NAD, therefore in tx_buffer only two bytes can be added.\n * The first one will represent CID, the second one will represent NAD.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with R-block repsonse\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] acknowledged Sets appropriate bit in PCB byte. True - ACK, false - NAK\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError iso14443_4a_poller_send_receive_ready_block(\n    Iso14443_4aPoller* instance,\n    bool acknowledged,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Transmit Iso14443_4a S-block in poller mode. S-block used to exchange control\n * information between the card and the reader. Two different types of S-blocks\n * are defined:\n * - Waiting time extension containing a 1 byte long INF field and (deselect = false)\n * - DESELECT containing no INF field  (deselect = true)\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with R-block repsonse\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] deselect Sets appropriate bit in PCB byte.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError iso14443_4a_poller_send_supervisory_block(\n    Iso14443_4aPoller* instance,\n    bool deselect,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Send HALT command to the card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Halts card and changes internal Iso14443_4aPoller state to Idle.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance);\n\n/**\n * @brief Read Answer To Select (ATS) from the card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Send Request Answer To Select (RATS) command to the card and parse the response.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the buffer to be filled with ATS data.\n * @return Iso14443_4aErrorNone on success, an error code on failure.\n */\nIso14443_4aError\n    iso14443_4a_poller_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_iso14443_4a;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c",
    "content": "#include \"iso14443_4a_poller_i.h\"\n\n#include <furi.h>\n\n#include \"iso14443_4a_i.h\"\n\n#define TAG \"Iso14443_4aPoller\"\n\n#define ISO14443_4A_FSDI_256  (0x8U)\n#define ISO14443_4A_FWT_MAX   (4096UL << 14)\n#define ISO14443_4A_WTXM_MASK (0x3FU)\n#define ISO14443_4A_WTXM_MAX  (0x3BU)\n#define ISO14443_4A_SWTX      (0xF2U)\n\nIso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) {\n    furi_check(instance);\n\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    instance->poller_state = Iso14443_4aPollerStateIdle;\n\n    return Iso14443_4aErrorNone;\n}\n\nIso14443_4aError\n    iso14443_4a_poller_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS);\n    bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_FSDI_256 << 4);\n\n    Iso14443_4aError error = Iso14443_4aErrorNone;\n\n    do {\n        const Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            ISO14443_4A_POLLER_ATS_FWT_FC);\n\n        if(iso14443_3a_error != Iso14443_3aErrorNone) {\n            FURI_LOG_E(TAG, \"ATS request failed\");\n            error = iso14443_4a_process_error(iso14443_3a_error);\n            break;\n\n        } else if(!iso14443_4a_ats_parse(data, instance->rx_buffer)) {\n            FURI_LOG_E(TAG, \"Failed to parse ATS response\");\n            error = Iso14443_4aErrorProtocol;\n            break;\n        }\n\n    } while(false);\n\n    return error;\n}\n\nIso14443_4aError iso14443_4a_poller_send_block(\n    Iso14443_4aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    bit_buffer_reset(instance->tx_buffer);\n    iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);\n\n    Iso14443_4aError error = Iso14443_4aErrorNone;\n\n    do {\n        Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            iso14443_4a_get_fwt_fc_max(instance->data));\n\n        if(iso14443_3a_error != Iso14443_3aErrorNone) {\n            error = iso14443_4a_process_error(iso14443_3a_error);\n            break;\n        }\n\n        if(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX)) {\n            do {\n                uint8_t wtxm = bit_buffer_get_byte(instance->rx_buffer, 1) & ISO14443_4A_WTXM_MASK;\n                if(wtxm > ISO14443_4A_WTXM_MAX) {\n                    return Iso14443_4aErrorProtocol;\n                }\n\n                bit_buffer_reset(instance->tx_buffer);\n                bit_buffer_copy_left(instance->tx_buffer, instance->rx_buffer, 1);\n                bit_buffer_append_byte(instance->tx_buffer, wtxm);\n\n                iso14443_3a_error = iso14443_3a_poller_send_standard_frame(\n                    instance->iso14443_3a_poller,\n                    instance->tx_buffer,\n                    instance->rx_buffer,\n                    MAX(iso14443_4a_get_fwt_fc_max(instance->data) * wtxm, ISO14443_4A_FWT_MAX));\n\n                if(iso14443_3a_error != Iso14443_3aErrorNone) {\n                    error = iso14443_4a_process_error(iso14443_3a_error);\n                    return error;\n                }\n\n            } while(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX));\n        }\n\n        if(!iso14443_4_layer_decode_block(\n               instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {\n            error = Iso14443_4aErrorProtocol;\n            break;\n        }\n    } while(false);\n\n    return error;\n}\n\nIso14443_4aError iso14443_4a_poller_send_chain_block(\n    Iso14443_4aPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    iso14443_4_layer_set_i_block(instance->iso14443_4_layer, true, false);\n    Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);\n    return error;\n}\n\nIso14443_4aError iso14443_4a_poller_send_receive_ready_block(\n    Iso14443_4aPoller* instance,\n    bool acknowledged,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;\n    iso14443_4_layer_set_r_block(instance->iso14443_4_layer, acknowledged, CID_present);\n    Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);\n    return error;\n}\n\nIso14443_4aError iso14443_4a_poller_send_supervisory_block(\n    Iso14443_4aPoller* instance,\n    bool deselect,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;\n    iso14443_4_layer_set_s_block(instance->iso14443_4_layer, deselect, CID_present);\n    Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>\n#include <nfc/helpers/iso14443_4_layer.h>\n\n#include \"iso14443_4a_poller.h\"\n#include \"iso14443_4a_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO14443_4A_POLLER_ATS_FWT_FC (40000)\n\ntypedef enum {\n    Iso14443_4aPollerStateIdle,\n    Iso14443_4aPollerStateReadAts,\n    Iso14443_4aPollerStateError,\n    Iso14443_4aPollerStateReady,\n\n    Iso14443_4aPollerStateNum,\n} Iso14443_4aPollerState;\n\ntypedef enum {\n    Iso14443_4aPollerSessionStateIdle,\n    Iso14443_4aPollerSessionStateActive,\n    Iso14443_4aPollerSessionStateStopRequest,\n} Iso14443_4aPollerSessionState;\n\nstruct Iso14443_4aPoller {\n    Iso14443_3aPoller* iso14443_3a_poller;\n    Iso14443_4aPollerState poller_state;\n    Iso14443_4aPollerSessionState session_state;\n    Iso14443_4aError error;\n    Iso14443_4aData* data;\n    Iso14443_4Layer* iso14443_4_layer;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    Iso14443_4aPollerEventData iso14443_4a_event_data;\n    Iso14443_4aPollerEvent iso14443_4a_event;\n    NfcGenericEvent general_event;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b.c",
    "content": "#include \"iso14443_4b_i.h\" // IWYU pragma: keep\n\n#include <furi.h>\n#include <nfc/protocols/nfc_device_base_i.h>\n\n#define ISO14443_4B_PROTOCOL_NAME \"ISO14443-4B\"\n#define ISO14443_4B_DEVICE_NAME   \"ISO14443-4B (Unknown)\"\n\nconst NfcDeviceBase nfc_device_iso14443_4b = {\n    .protocol_name = ISO14443_4B_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)iso14443_4b_alloc,\n    .free = (NfcDeviceFree)iso14443_4b_free,\n    .reset = (NfcDeviceReset)iso14443_4b_reset,\n    .copy = (NfcDeviceCopy)iso14443_4b_copy,\n    .verify = (NfcDeviceVerify)iso14443_4b_verify,\n    .load = (NfcDeviceLoad)iso14443_4b_load,\n    .save = (NfcDeviceSave)iso14443_4b_save,\n    .is_equal = (NfcDeviceEqual)iso14443_4b_is_equal,\n    .get_name = (NfcDeviceGetName)iso14443_4b_get_device_name,\n    .get_uid = (NfcDeviceGetUid)iso14443_4b_get_uid,\n    .set_uid = (NfcDeviceSetUid)iso14443_4b_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)iso14443_4b_get_base_data,\n};\n\nIso14443_4bData* iso14443_4b_alloc(void) {\n    Iso14443_4bData* data = malloc(sizeof(Iso14443_4bData));\n\n    data->iso14443_3b_data = iso14443_3b_alloc();\n    return data;\n}\n\nvoid iso14443_4b_free(Iso14443_4bData* data) {\n    furi_check(data);\n\n    iso14443_3b_free(data->iso14443_3b_data);\n    free(data);\n}\n\nvoid iso14443_4b_reset(Iso14443_4bData* data) {\n    furi_check(data);\n\n    iso14443_3b_reset(data->iso14443_3b_data);\n}\n\nvoid iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso14443_3b_copy(data->iso14443_3b_data, other->iso14443_3b_data);\n}\n\nbool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type) {\n    UNUSED(data);\n    UNUSED(device_type);\n\n    // Empty, unified file format only\n    return false;\n}\n\nbool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n    return iso14443_3b_load(data->iso14443_3b_data, ff, version);\n}\n\nbool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n    return iso14443_3b_save(data->iso14443_3b_data, ff);\n}\n\nbool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return iso14443_3b_is_equal(data->iso14443_3b_data, other->iso14443_3b_data);\n}\n\nconst char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n    return ISO14443_4B_DEVICE_NAME;\n}\n\nconst uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len) {\n    furi_check(data);\n    furi_check(uid_len);\n\n    return iso14443_3b_get_uid(data->iso14443_3b_data, uid_len);\n}\n\nbool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n    furi_check(uid);\n\n    return iso14443_3b_set_uid(data->iso14443_3b_data, uid, uid_len);\n}\n\nIso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data) {\n    furi_check(data);\n\n    return data->iso14443_3b_data;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3b/iso14443_3b.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_4bErrorNone,\n    Iso14443_4bErrorNotPresent,\n    Iso14443_4bErrorProtocol,\n    Iso14443_4bErrorTimeout,\n} Iso14443_4bError;\n\ntypedef struct Iso14443_4bData Iso14443_4bData;\n\n// Virtual methods\n\nIso14443_4bData* iso14443_4b_alloc(void);\n\nvoid iso14443_4b_free(Iso14443_4bData* data);\n\nvoid iso14443_4b_reset(Iso14443_4bData* data);\n\nvoid iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other);\n\nbool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type);\n\nbool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version);\n\nbool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff);\n\nbool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other);\n\nconst char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len);\n\nbool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_iso14443_4b;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_i.c",
    "content": "#include \"iso14443_4b_i.h\"\n\nIso14443_4bError iso14443_4b_process_error(Iso14443_3bError error) {\n    switch(error) {\n    case Iso14443_3bErrorNone:\n        return Iso14443_4bErrorNone;\n    case Iso14443_3bErrorNotPresent:\n        return Iso14443_4bErrorNotPresent;\n    case Iso14443_3bErrorColResFailed:\n    case Iso14443_3bErrorCommunication:\n    case Iso14443_3bErrorWrongCrc:\n        return Iso14443_4bErrorProtocol;\n    case Iso14443_3bErrorTimeout:\n        return Iso14443_4bErrorTimeout;\n    default:\n        return Iso14443_4bErrorProtocol;\n    }\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_i.h",
    "content": "#pragma once\n\n#include \"iso14443_4b.h\"\n\nstruct Iso14443_4bData {\n    Iso14443_3bData* iso14443_3b_data;\n};\n\nIso14443_4bError iso14443_4b_process_error(Iso14443_3bError error);\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.c",
    "content": "#include \"iso14443_4b_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"Iso14443_4bPoller\"\n\n#define ISO14443_4A_POLLER_BUF_SIZE (256U)\n\ntypedef NfcCommand (*Iso14443_4bPollerStateHandler)(Iso14443_4bPoller* instance);\n\nconst Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nstatic Iso14443_4bPoller* iso14443_4b_poller_alloc(Iso14443_3bPoller* iso14443_3b_poller) {\n    Iso14443_4bPoller* instance = malloc(sizeof(Iso14443_4bPoller));\n    instance->iso14443_3b_poller = iso14443_3b_poller;\n    instance->data = iso14443_4b_alloc();\n    instance->iso14443_4_layer = iso14443_4_layer_alloc();\n    instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);\n\n    instance->iso14443_4b_event.data = &instance->iso14443_4b_event_data;\n\n    instance->general_event.protocol = NfcProtocolIso14443_4b;\n    instance->general_event.event_data = &instance->iso14443_4b_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void iso14443_4b_poller_free(Iso14443_4bPoller* instance) {\n    furi_assert(instance);\n\n    iso14443_4b_free(instance->data);\n    iso14443_4_layer_free(instance->iso14443_4_layer);\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    free(instance);\n}\n\nstatic NfcCommand iso14443_4b_poller_handler_idle(Iso14443_4bPoller* instance) {\n    iso14443_3b_copy(\n        instance->data->iso14443_3b_data,\n        iso14443_3b_poller_get_data(instance->iso14443_3b_poller));\n\n    iso14443_4_layer_reset(instance->iso14443_4_layer);\n    instance->poller_state = Iso14443_4bPollerStateReady;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand iso14443_4b_poller_handler_error(Iso14443_4bPoller* instance) {\n    iso14443_3b_poller_halt(instance->iso14443_3b_poller);\n    instance->iso14443_4b_event_data.error = instance->error;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->poller_state = Iso14443_4bPollerStateIdle;\n    return command;\n}\n\nstatic NfcCommand iso14443_4b_poller_handler_ready(Iso14443_4bPoller* instance) {\n    instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeReady;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic const Iso14443_4bPollerStateHandler\n    iso14443_4b_poller_state_handler[Iso14443_4bPollerStateNum] = {\n        [Iso14443_4bPollerStateIdle] = iso14443_4b_poller_handler_idle,\n        [Iso14443_4bPollerStateError] = iso14443_4b_poller_handler_error,\n        [Iso14443_4bPollerStateReady] = iso14443_4b_poller_handler_ready,\n};\n\nstatic void iso14443_4b_poller_set_callback(\n    Iso14443_4bPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand iso14443_4b_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3b);\n\n    Iso14443_4bPoller* instance = context;\n    furi_assert(instance);\n    furi_assert(instance->callback);\n\n    Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;\n    furi_assert(iso14443_3b_event);\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {\n        command = iso14443_4b_poller_state_handler[instance->poller_state](instance);\n    } else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) {\n        instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeError;\n        command = instance->callback(instance->general_event, instance->context);\n    }\n\n    return command;\n}\n\nstatic bool iso14443_4b_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_3b);\n\n    const Iso14443_4bPoller* instance = context;\n    furi_assert(instance);\n\n    const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;\n    furi_assert(iso14443_3b_event);\n    iso14443_3b_copy(\n        instance->data->iso14443_3b_data,\n        iso14443_3b_poller_get_data(instance->iso14443_3b_poller));\n\n    bool protocol_detected = false;\n\n    if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {\n        protocol_detected = iso14443_3b_supports_iso14443_4(instance->data->iso14443_3b_data);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_iso14443_4b = {\n    .alloc = (NfcPollerAlloc)iso14443_4b_poller_alloc,\n    .free = (NfcPollerFree)iso14443_4b_poller_free,\n    .set_callback = (NfcPollerSetCallback)iso14443_4b_poller_set_callback,\n    .run = (NfcPollerRun)iso14443_4b_poller_run,\n    .detect = (NfcPollerDetect)iso14443_4b_poller_detect,\n    .get_data = (NfcPollerGetData)iso14443_4b_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h>\n\n#include \"iso14443_4b.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Iso14443_4bPoller opaque type definition.\n */\ntypedef struct Iso14443_4bPoller Iso14443_4bPoller;\n\n/**\n * @brief Enumeration of possible Iso14443_4b poller event types.\n */\ntypedef enum {\n    Iso14443_4bPollerEventTypeError, /**< An error occured during activation procedure. */\n    Iso14443_4bPollerEventTypeReady, /**< The card was activated by the poller. */\n} Iso14443_4bPollerEventType;\n\n/**\n * @brief Iso14443_4b poller event data.\n */\ntypedef union {\n    Iso14443_4bError error; /**< Error code indicating card activation fail reason. */\n} Iso14443_4bPollerEventData;\n\n/**\n * @brief Iso14443_4b poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    Iso14443_4bPollerEventType type; /**< Type of emmitted event. */\n    Iso14443_4bPollerEventData* data; /**< Pointer to event specific data. */\n} Iso14443_4bPollerEvent;\n\n/**\n * @brief Transmit and receive Iso14443_4b blocks in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer. The fwt parameter is calculated during activation procedure.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return Iso14443_4bErrorNone on success, an error code on failure.\n */\nIso14443_4bError iso14443_4b_poller_send_block(\n    Iso14443_4bPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Send HALT command to the card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Halts card and changes internal Iso14443_4aPoller state to Idle.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return Iso14443_4bErrorNone on success, an error code on failure.\n */\nIso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_iso14443_4b;\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c",
    "content": "#include \"iso14443_4b_poller_i.h\"\n\n#include <furi.h>\n\n#include \"iso14443_4b_i.h\"\n\n#define TAG \"Iso14443_4bPoller\"\n\nIso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance) {\n    furi_check(instance);\n\n    iso14443_3b_poller_halt(instance->iso14443_3b_poller);\n    instance->poller_state = Iso14443_4bPollerStateIdle;\n\n    return Iso14443_4bErrorNone;\n}\n\nIso14443_4bError iso14443_4b_poller_send_block(\n    Iso14443_4bPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    bit_buffer_reset(instance->tx_buffer);\n    iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);\n\n    Iso14443_4bError error = Iso14443_4bErrorNone;\n\n    do {\n        Iso14443_3bError iso14443_3b_error = iso14443_3b_poller_send_frame(\n            instance->iso14443_3b_poller, instance->tx_buffer, instance->rx_buffer);\n\n        if(iso14443_3b_error != Iso14443_3bErrorNone) {\n            error = iso14443_4b_process_error(iso14443_3b_error);\n            break;\n\n        } else if(!iso14443_4_layer_decode_block(\n                      instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {\n            error = Iso14443_4bErrorProtocol;\n            break;\n        }\n    } while(false);\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>\n#include <nfc/helpers/iso14443_4_layer.h>\n\n#include \"iso14443_4b_poller.h\"\n#include \"iso14443_4b_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso14443_4bPollerStateIdle,\n    Iso14443_4bPollerStateError,\n    Iso14443_4bPollerStateReady,\n\n    Iso14443_4bPollerStateNum,\n} Iso14443_4bPollerState;\n\ntypedef enum {\n    Iso14443_4bPollerSessionStateIdle,\n    Iso14443_4bPollerSessionStateActive,\n    Iso14443_4bPollerSessionStateStopRequest,\n} Iso14443_4bPollerSessionState;\n\nstruct Iso14443_4bPoller {\n    Iso14443_3bPoller* iso14443_3b_poller;\n    Iso14443_4bPollerState poller_state;\n    Iso14443_4bPollerSessionState session_state;\n    Iso14443_4bError error;\n    Iso14443_4bData* data;\n    Iso14443_4Layer* iso14443_4_layer;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    Iso14443_4bPollerEventData iso14443_4b_event_data;\n    Iso14443_4bPollerEvent iso14443_4b_event;\n    NfcGenericEvent general_event;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3.c",
    "content": "#include \"iso15693_3.h\"\n#include \"iso15693_3_device_defs.h\"\n\n#include <nfc/nfc_common.h>\n\n#define ISO15693_3_PROTOCOL_NAME        \"ISO15693-3\"\n#define ISO15693_3_PROTOCOL_NAME_LEGACY \"ISO15693\"\n#define ISO15693_3_DEVICE_NAME          \"ISO15693-3 (Unknown)\"\n\n#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0)\n#define ISO15693_3_LOCK_AFI_LEGACY   (1U << 1)\n\n#define ISO15693_3_DSFID_KEY           \"DSFID\"\n#define ISO15693_3_AFI_KEY             \"AFI\"\n#define ISO15693_3_IC_REF_KEY          \"IC Reference\"\n#define ISO15693_3_BLOCK_COUNT_KEY     \"Block Count\"\n#define ISO15693_3_BLOCK_SIZE_KEY      \"Block Size\"\n#define ISO15693_3_DATA_CONTENT_KEY    \"Data Content\"\n#define ISO15693_3_LOCK_DSFID_KEY      \"Lock DSFID\"\n#define ISO15693_3_LOCK_AFI_KEY        \"Lock AFI\"\n#define ISO15693_3_SECURITY_STATUS_KEY \"Security Status\"\n\nconst NfcDeviceBase nfc_device_iso15693_3 = {\n    .protocol_name = ISO15693_3_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)iso15693_3_alloc,\n    .free = (NfcDeviceFree)iso15693_3_free,\n    .reset = (NfcDeviceReset)iso15693_3_reset,\n    .copy = (NfcDeviceCopy)iso15693_3_copy,\n    .verify = (NfcDeviceVerify)iso15693_3_verify,\n    .load = (NfcDeviceLoad)iso15693_3_load,\n    .save = (NfcDeviceSave)iso15693_3_save,\n    .is_equal = (NfcDeviceEqual)iso15693_3_is_equal,\n    .get_name = (NfcDeviceGetName)iso15693_3_get_device_name,\n    .get_uid = (NfcDeviceGetUid)iso15693_3_get_uid,\n    .set_uid = (NfcDeviceSetUid)iso15693_3_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data,\n};\n\nIso15693_3Data* iso15693_3_alloc(void) {\n    Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data));\n\n    data->block_data = simple_array_alloc(&simple_array_config_uint8_t);\n    data->block_security = simple_array_alloc(&simple_array_config_uint8_t);\n\n    return data;\n}\n\nvoid iso15693_3_free(Iso15693_3Data* data) {\n    furi_check(data);\n\n    simple_array_free(data->block_data);\n    simple_array_free(data->block_security);\n    free(data);\n}\n\nvoid iso15693_3_reset(Iso15693_3Data* data) {\n    furi_check(data);\n\n    memset(data->uid, 0, ISO15693_3_UID_SIZE);\n    memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo));\n    memset(&data->settings, 0, sizeof(Iso15693_3Settings));\n\n    simple_array_reset(data->block_data);\n    simple_array_reset(data->block_security);\n}\n\nvoid iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {\n    furi_check(data);\n    furi_check(other);\n\n    memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE);\n\n    data->system_info = other->system_info;\n    data->settings = other->settings;\n\n    simple_array_copy(data->block_data, other->block_data);\n    simple_array_copy(data->block_security, other->block_security);\n}\n\nbool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) {\n    UNUSED(data);\n    furi_check(device_type);\n\n    return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY);\n}\n\nstatic inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) {\n    bool loaded = false;\n    uint8_t* legacy_data = NULL;\n\n    do {\n        uint32_t value_count;\n        if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))\n            break;\n        if(simple_array_get_count(data->block_security) + 1 != value_count) break;\n\n        legacy_data = malloc(value_count);\n        if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count))\n            break;\n\n        // First legacy data byte is lock bits\n        data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY;\n        data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY;\n\n        // The rest are block security\n        memcpy(\n            &legacy_data[1],\n            simple_array_get_data(data->block_security),\n            simple_array_get_count(data->block_security));\n\n        loaded = true;\n    } while(false);\n\n    if(legacy_data) free(legacy_data);\n\n    return loaded;\n}\n\nstatic inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) {\n    bool loaded = false;\n\n    do {\n        uint32_t value_count;\n        if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))\n            break;\n        if(simple_array_get_count(data->block_security) != value_count) break;\n        if(!flipper_format_read_hex(\n               ff,\n               ISO15693_3_SECURITY_STATUS_KEY,\n               simple_array_get_data(data->block_security),\n               simple_array_get_count(data->block_security)))\n            break;\n\n        loaded = true;\n    } while(false);\n\n    return loaded;\n}\n\nbool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n    UNUSED(version);\n\n    bool loaded = false;\n\n    do {\n        if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) {\n            if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))\n                break;\n            data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID;\n        }\n\n        if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) {\n            if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;\n            data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI;\n        }\n\n        if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) {\n            if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))\n                break;\n            data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF;\n        }\n\n        const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) &&\n                                   flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY);\n        if(has_lock_bits) {\n            Iso15693_3LockBits* lock_bits = &data->settings.lock_bits;\n            if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1))\n                break;\n            if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break;\n        }\n\n        if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) &&\n           flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) {\n            data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;\n\n            uint32_t block_count;\n            if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break;\n            data->system_info.block_count = block_count;\n\n            if(!flipper_format_read_hex(\n                   ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1))\n                break;\n\n            if(data->system_info.block_count > 0 && data->system_info.block_size > 0) {\n                simple_array_init(\n                    data->block_data,\n                    data->system_info.block_size * data->system_info.block_count);\n                simple_array_init(data->block_security, data->system_info.block_count);\n\n                if(!flipper_format_read_hex(\n                       ff,\n                       ISO15693_3_DATA_CONTENT_KEY,\n                       simple_array_get_data(data->block_data),\n                       simple_array_get_count(data->block_data)))\n                    break;\n\n                if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {\n                    const bool security_loaded = has_lock_bits ?\n                                                     iso15693_3_load_security(data, ff) :\n                                                     iso15693_3_load_security_legacy(data, ff);\n                    if(!security_loaded) break;\n                }\n            }\n        }\n\n        loaded = true;\n    } while(false);\n\n    return loaded;\n}\n\nbool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool saved = false;\n\n    do {\n        if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME \" specific data\"))\n            break;\n\n        if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {\n            if(!flipper_format_write_comment_cstr(ff, \"Data Storage Format Identifier\")) break;\n            if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))\n                break;\n        }\n\n        if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {\n            if(!flipper_format_write_comment_cstr(ff, \"Application Family Identifier\")) break;\n            if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;\n        }\n\n        if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {\n            if(!flipper_format_write_comment_cstr(ff, \"IC Reference - Vendor specific meaning\"))\n                break;\n            if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))\n                break;\n        }\n\n        if(!flipper_format_write_comment_cstr(ff, \"Lock Bits\")) break;\n        if(!flipper_format_write_bool(\n               ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1))\n            break;\n        if(!flipper_format_write_bool(\n               ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1))\n            break;\n\n        if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {\n            const uint32_t block_count = data->system_info.block_count;\n            if(!flipper_format_write_comment_cstr(\n                   ff, \"Number of memory blocks, valid range = 1..256\"))\n                break;\n            if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1))\n                break;\n\n            if(!flipper_format_write_comment_cstr(\n                   ff, \"Size of a single memory block, valid range = 01...20 (hex)\"))\n                break;\n            if(!flipper_format_write_hex(\n                   ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1))\n                break;\n\n            if(data->system_info.block_count > 0 && data->system_info.block_size > 0) {\n                if(!flipper_format_write_hex(\n                       ff,\n                       ISO15693_3_DATA_CONTENT_KEY,\n                       simple_array_cget_data(data->block_data),\n                       simple_array_get_count(data->block_data)))\n                    break;\n\n                if(!flipper_format_write_comment_cstr(\n                       ff, \"Block Security Status: 01 = locked, 00 = not locked\"))\n                    break;\n                if(!flipper_format_write_hex(\n                       ff,\n                       ISO15693_3_SECURITY_STATUS_KEY,\n                       simple_array_cget_data(data->block_security),\n                       simple_array_get_count(data->block_security)))\n                    break;\n            }\n        }\n        saved = true;\n    } while(false);\n\n    return saved;\n}\n\nbool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 &&\n           memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 &&\n           memcmp( //-V1103\n               &data->system_info,\n               &other->system_info,\n               sizeof(Iso15693_3SystemInfo)) == 0 &&\n           simple_array_is_equal(data->block_data, other->block_data) &&\n           simple_array_is_equal(data->block_security, other->block_security);\n}\n\nconst char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) {\n    UNUSED(data);\n    UNUSED(name_type);\n\n    return ISO15693_3_DEVICE_NAME;\n}\n\nconst uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) {\n    furi_check(data);\n\n    if(uid_len) *uid_len = ISO15693_3_UID_SIZE;\n    return data->uid;\n}\n\nbool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n    furi_check(uid);\n\n    bool uid_valid = uid_len == ISO15693_3_UID_SIZE;\n\n    if(uid_valid) {\n        memcpy(data->uid, uid, uid_len);\n        // All ISO15693-3 cards must have this as first UID byte\n        data->uid[0] = 0xe0;\n    }\n\n    return uid_valid;\n}\n\nIso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) {\n    UNUSED(data);\n    furi_crash(\"No base data\");\n}\n\nbool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) {\n    furi_check(data);\n    furi_check(block_index < data->system_info.block_count);\n\n    return *(const uint8_t*)simple_array_cget(data->block_security, block_index);\n}\n\nuint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) {\n    furi_check(data);\n\n    return data->uid[1];\n}\n\nuint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) {\n    furi_check(data);\n\n    return data->system_info.block_count;\n}\n\nuint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) {\n    furi_check(data);\n\n    return data->system_info.block_size;\n}\n\nconst uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) {\n    furi_check(data);\n    furi_check(data->system_info.block_count > block_index);\n\n    return (const uint8_t*)simple_array_cget(\n        data->block_data, block_index * data->system_info.block_size);\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base.h>\n#include <flipper_format/flipper_format.h>\n#include <toolbox/simple_array.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO15693_3_UID_SIZE (8U)\n\n#define ISO15693_3_GUARD_TIME_US    (5000U)\n#define ISO15693_3_FDT_POLL_FC      (4202U)\n#define ISO15693_3_FDT_LISTEN_FC    (4320U)\n#define ISO15693_3_POLL_POLL_MIN_US (1500U)\n\n#define ISO15693_3_REQ_FLAG_SUBCARRIER_1 (0U << 0)\n#define ISO15693_3_REQ_FLAG_SUBCARRIER_2 (1U << 0)\n#define ISO15693_3_REQ_FLAG_DATA_RATE_LO (0U << 1)\n#define ISO15693_3_REQ_FLAG_DATA_RATE_HI (1U << 1)\n#define ISO15693_3_REQ_FLAG_INVENTORY_T4 (0U << 2)\n#define ISO15693_3_REQ_FLAG_INVENTORY_T5 (1U << 2)\n#define ISO15693_3_REQ_FLAG_EXTENSION    (1U << 3)\n\n#define ISO15693_3_REQ_FLAG_T4_SELECTED  (1U << 4)\n#define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5)\n#define ISO15693_3_REQ_FLAG_T4_OPTION    (1U << 6)\n\n#define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4)\n#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16  (0U << 5)\n#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1   (1U << 5)\n#define ISO15693_3_REQ_FLAG_T5_OPTION      (1U << 6)\n\n#define ISO15693_3_RESP_FLAG_NONE      (0U)\n#define ISO15693_3_RESP_FLAG_ERROR     (1U << 0)\n#define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3)\n\n#define ISO15693_3_RESP_ERROR_NOT_SUPPORTED        (0x01U)\n#define ISO15693_3_RESP_ERROR_FORMAT               (0x02U)\n#define ISO15693_3_RESP_ERROR_OPTION               (0x03U)\n#define ISO15693_3_RESP_ERROR_UNKNOWN              (0x0FU)\n#define ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE    (0x10U)\n#define ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED (0x11U)\n#define ISO15693_3_RESP_ERROR_BLOCK_LOCKED         (0x12U)\n#define ISO15693_3_RESP_ERROR_BLOCK_WRITE          (0x13U)\n#define ISO15693_3_RESP_ERROR_BLOCK_LOCK           (0x14U)\n#define ISO15693_3_RESP_ERROR_CUSTOM_START         (0xA0U)\n#define ISO15693_3_RESP_ERROR_CUSTOM_END           (0xDFU)\n\n#define ISO15693_3_CMD_MANDATORY_START     (0x01U)\n#define ISO15693_3_CMD_INVENTORY           (0x01U)\n#define ISO15693_3_CMD_STAY_QUIET          (0x02U)\n#define ISO15693_3_CMD_MANDATORY_RFU       (0x03U)\n#define ISO15693_3_CMD_OPTIONAL_START      (0x20U)\n#define ISO15693_3_CMD_READ_BLOCK          (0x20U)\n#define ISO15693_3_CMD_WRITE_BLOCK         (0x21U)\n#define ISO15693_3_CMD_LOCK_BLOCK          (0x22U)\n#define ISO15693_3_CMD_READ_MULTI_BLOCKS   (0x23U)\n#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS  (0x24U)\n#define ISO15693_3_CMD_SELECT              (0x25U)\n#define ISO15693_3_CMD_RESET_TO_READY      (0x26U)\n#define ISO15693_3_CMD_WRITE_AFI           (0x27U)\n#define ISO15693_3_CMD_LOCK_AFI            (0x28U)\n#define ISO15693_3_CMD_WRITE_DSFID         (0x29U)\n#define ISO15693_3_CMD_LOCK_DSFID          (0x2AU)\n#define ISO15693_3_CMD_GET_SYS_INFO        (0x2BU)\n#define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU)\n#define ISO15693_3_CMD_OPTIONAL_RFU        (0x2DU)\n#define ISO15693_3_CMD_CUSTOM_START        (0xA0U)\n\n#define ISO15693_3_MANDATORY_COUNT (ISO15693_3_CMD_MANDATORY_RFU - ISO15693_3_CMD_MANDATORY_START)\n#define ISO15693_3_OPTIONAL_COUNT  (ISO15693_3_CMD_OPTIONAL_RFU - ISO15693_3_CMD_OPTIONAL_START)\n\n#define ISO15693_3_SYSINFO_FLAG_DSFID  (1U << 0)\n#define ISO15693_3_SYSINFO_FLAG_AFI    (1U << 1)\n#define ISO15693_3_SYSINFO_FLAG_MEMORY (1U << 2)\n#define ISO15693_3_SYSINFO_FLAG_IC_REF (1U << 3)\n\ntypedef enum {\n    Iso15693_3ErrorNone,\n    Iso15693_3ErrorNotPresent,\n    Iso15693_3ErrorBufferEmpty,\n    Iso15693_3ErrorBufferOverflow,\n    Iso15693_3ErrorFieldOff,\n    Iso15693_3ErrorWrongCrc,\n    Iso15693_3ErrorTimeout,\n    Iso15693_3ErrorFormat,\n    Iso15693_3ErrorIgnore,\n    Iso15693_3ErrorNotSupported,\n    Iso15693_3ErrorUidMismatch,\n    Iso15693_3ErrorFullyHandled,\n    Iso15693_3ErrorUnexpectedResponse,\n    Iso15693_3ErrorInternal,\n    Iso15693_3ErrorCustom,\n    Iso15693_3ErrorUnknown,\n} Iso15693_3Error;\n\ntypedef struct {\n    uint8_t flags;\n    uint8_t dsfid;\n    uint8_t afi;\n    uint8_t ic_ref;\n    uint16_t block_count;\n    uint8_t block_size;\n} Iso15693_3SystemInfo;\n\ntypedef struct {\n    bool dsfid;\n    bool afi;\n} Iso15693_3LockBits;\n\ntypedef struct {\n    Iso15693_3LockBits lock_bits;\n} Iso15693_3Settings;\n\ntypedef struct {\n    uint8_t uid[ISO15693_3_UID_SIZE];\n    Iso15693_3SystemInfo system_info;\n    Iso15693_3Settings settings;\n    SimpleArray* block_data;\n    SimpleArray* block_security;\n} Iso15693_3Data;\n\nIso15693_3Data* iso15693_3_alloc(void);\n\nvoid iso15693_3_free(Iso15693_3Data* data);\n\nvoid iso15693_3_reset(Iso15693_3Data* data);\n\nvoid iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other);\n\nbool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type);\n\nbool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version);\n\nbool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff);\n\nbool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other);\n\nconst char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type);\n\nconst uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len);\n\nbool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len);\n\nIso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data);\n\n// Getters and tests\n\nbool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index);\n\nuint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data);\n\nuint16_t iso15693_3_get_block_count(const Iso15693_3Data* data);\n\nuint8_t iso15693_3_get_block_size(const Iso15693_3Data* data);\n\nconst uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_iso15693_3;\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_i.c",
    "content": "#include \"iso15693_3_i.h\"\n\nbool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf) {\n    furi_assert(error);\n\n    if(bit_buffer_get_size_bytes(buf) == 0) {\n        // YEET!\n        *error = Iso15693_3ErrorBufferEmpty;\n        return true;\n    }\n\n    typedef struct {\n        uint8_t flags;\n        uint8_t error;\n    } ErrorResponseLayout;\n\n    const ErrorResponseLayout* resp = (const ErrorResponseLayout*)bit_buffer_get_data(buf);\n\n    if((resp->flags & ISO15693_3_RESP_FLAG_ERROR) == 0) {\n        // No error flag is set, the data does not contain an error frame\n        return false;\n    } else if(bit_buffer_get_size_bytes(buf) < sizeof(ErrorResponseLayout)) {\n        // Error bit is set, but not enough data to determine the error\n        *error = Iso15693_3ErrorUnexpectedResponse;\n        return true;\n    } else if(\n        resp->error >= ISO15693_3_RESP_ERROR_CUSTOM_START &&\n        resp->error <= ISO15693_3_RESP_ERROR_CUSTOM_END) {\n        // Custom vendor-specific error, must be checked in the respective protocol implementation\n        *error = Iso15693_3ErrorCustom;\n        return true;\n    }\n\n    switch(resp->error) {\n    case ISO15693_3_RESP_ERROR_NOT_SUPPORTED:\n    case ISO15693_3_RESP_ERROR_OPTION:\n        *error = Iso15693_3ErrorNotSupported;\n        break;\n    case ISO15693_3_RESP_ERROR_FORMAT:\n        *error = Iso15693_3ErrorFormat;\n        break;\n    case ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE:\n    case ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED:\n    case ISO15693_3_RESP_ERROR_BLOCK_LOCKED:\n    case ISO15693_3_RESP_ERROR_BLOCK_WRITE:\n    case ISO15693_3_RESP_ERROR_BLOCK_LOCK:\n        *error = Iso15693_3ErrorInternal;\n        break;\n    case ISO15693_3_RESP_ERROR_UNKNOWN:\n    default:\n        *error = Iso15693_3ErrorUnknown;\n    }\n\n    return true;\n}\n\nIso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf) {\n    furi_assert(data);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    do {\n        if(iso15693_3_error_response_parse(&ret, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t dsfid;\n            uint8_t uid[ISO15693_3_UID_SIZE];\n        } InventoryResponseLayout;\n\n        if(bit_buffer_get_size_bytes(buf) != sizeof(InventoryResponseLayout)) {\n            ret = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        const InventoryResponseLayout* resp =\n            (const InventoryResponseLayout*)bit_buffer_get_data(buf);\n        // Reverse UID for backward compatibility\n        for(uint32_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {\n            data[i] = resp->uid[ISO15693_3_UID_SIZE - i - 1];\n        }\n\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error\n    iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf) {\n    furi_assert(data);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    do {\n        if(iso15693_3_error_response_parse(&ret, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t info_flags;\n            uint8_t uid[ISO15693_3_UID_SIZE];\n            uint8_t extra[];\n        } SystemInfoResponseLayout;\n\n        if(bit_buffer_get_size_bytes(buf) < sizeof(SystemInfoResponseLayout)) {\n            ret = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        const SystemInfoResponseLayout* resp =\n            (const SystemInfoResponseLayout*)bit_buffer_get_data(buf);\n\n        const uint8_t* extra = resp->extra;\n        const size_t extra_size = (resp->info_flags & ISO15693_3_SYSINFO_FLAG_DSFID ? 1 : 0) +\n                                  (resp->info_flags & ISO15693_3_SYSINFO_FLAG_AFI ? 1 : 0) +\n                                  (resp->info_flags & ISO15693_3_SYSINFO_FLAG_MEMORY ? 2 : 0) +\n                                  (resp->info_flags & ISO15693_3_SYSINFO_FLAG_IC_REF ? 1 : 0);\n\n        if(extra_size != bit_buffer_get_size_bytes(buf) - sizeof(SystemInfoResponseLayout)) {\n            ret = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        data->flags = resp->info_flags;\n\n        if(data->flags & ISO15693_3_SYSINFO_FLAG_DSFID) {\n            data->dsfid = *extra++;\n        }\n\n        if(data->flags & ISO15693_3_SYSINFO_FLAG_AFI) {\n            data->afi = *extra++;\n        }\n\n        if(data->flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {\n            // Add 1 to get actual values\n            data->block_count = *extra++ + 1;\n            data->block_size = (*extra++ & 0x1F) + 1;\n        }\n\n        if(data->flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {\n            data->ic_ref = *extra;\n        }\n\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error\n    iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf) {\n    furi_assert(data);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    do {\n        if(iso15693_3_error_response_parse(&ret, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t block_data[];\n        } ReadBlockResponseLayout;\n\n        const size_t buf_size = bit_buffer_get_size_bytes(buf);\n        const size_t received_block_size = buf_size - sizeof(ReadBlockResponseLayout);\n\n        if(buf_size <= sizeof(ReadBlockResponseLayout) || received_block_size != block_size) {\n            ret = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        const ReadBlockResponseLayout* resp =\n            (const ReadBlockResponseLayout*)bit_buffer_get_data(buf);\n        memcpy(data, resp->block_data, received_block_size);\n\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_get_block_security_response_parse(\n    uint8_t* data,\n    uint16_t block_count,\n    const BitBuffer* buf) {\n    furi_assert(data);\n    furi_assert(block_count);\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    do {\n        if(iso15693_3_error_response_parse(&ret, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t block_security[];\n        } GetBlockSecurityResponseLayout;\n\n        const size_t buf_size = bit_buffer_get_size_bytes(buf);\n        const size_t received_block_count = buf_size - sizeof(GetBlockSecurityResponseLayout);\n\n        if(buf_size <= sizeof(GetBlockSecurityResponseLayout) ||\n           received_block_count != block_count) {\n            ret = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        const GetBlockSecurityResponseLayout* resp =\n            (const GetBlockSecurityResponseLayout*)bit_buffer_get_data(buf);\n\n        memcpy(data, resp->block_security, received_block_count);\n\n    } while(false);\n\n    return ret;\n}\n\nvoid iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) {\n    for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {\n        // Reverse the UID\n        bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]);\n    }\n}\n\nvoid iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) {\n    furi_assert(block_num < data->system_info.block_count);\n\n    const uint32_t block_offset = block_num * data->system_info.block_size;\n    const uint8_t* block_data = simple_array_cget(data->block_data, block_offset);\n\n    bit_buffer_append_bytes(buf, block_data, data->system_info.block_size);\n}\n\nvoid iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked) {\n    furi_assert(data);\n    furi_assert(block_index < data->system_info.block_count);\n\n    *(uint8_t*)simple_array_get(data->block_security, block_index) = locked ? 1 : 0;\n}\n\nvoid iso15693_3_set_block_data(\n    Iso15693_3Data* data,\n    uint8_t block_num,\n    const uint8_t* block_data,\n    size_t block_data_size) {\n    furi_assert(block_num < data->system_info.block_count);\n    furi_assert(block_data_size == data->system_info.block_size);\n\n    const uint32_t block_offset = block_num * data->system_info.block_size;\n    uint8_t* block = simple_array_get(data->block_data, block_offset);\n\n    memcpy(block, block_data, block_data_size);\n}\n\nvoid iso15693_3_append_block_security(\n    const Iso15693_3Data* data,\n    uint8_t block_num,\n    BitBuffer* buf) {\n    bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num));\n}\n\nbool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) {\n    for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {\n        if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_i.h",
    "content": "#pragma once\n\n#include \"iso15693_3.h\"\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Check if the buffer contains an error frame and if it does, determine\n * the error type.\n * NOTE: No changes are done to the result if no error is present.\n *\n * @param [out] data Pointer to the resulting error value.\n * @param [in] buf Data buffer to be checked\n *\n * @return True if data contains an error frame or is empty, false otherwise\n */\nbool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf);\n\nIso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf);\n\nIso15693_3Error\n    iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf);\n\nIso15693_3Error\n    iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf);\n\nIso15693_3Error iso15693_3_get_block_security_response_parse(\n    uint8_t* data,\n    uint16_t block_count,\n    const BitBuffer* buf);\n\nvoid iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf);\n\nvoid iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf);\n\nvoid iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked);\n\nvoid iso15693_3_set_block_data(\n    Iso15693_3Data* data,\n    uint8_t block_num,\n    const uint8_t* block_data,\n    size_t block_data_size);\n\nvoid iso15693_3_append_block_security(\n    const Iso15693_3Data* data,\n    uint8_t block_num,\n    BitBuffer* buf);\n\n// NOTE: the uid parameter has reversed byte order with respect to data\nbool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_listener.c",
    "content": "#include \"iso15693_3_listener_i.h\"\n\n#include <furi.h>\n\n#include <nfc/nfc.h>\n#include <nfc/protocols/nfc_listener_base.h>\n#include <nfc/helpers/iso13239_crc.h>\n\n#define TAG \"Iso15693_3Listener\"\n\n#define ISO15693_3_LISTENER_BUFFER_SIZE (256U)\n\nIso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {\n    furi_assert(nfc);\n\n    Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener));\n    instance->nfc = nfc;\n    instance->data = data;\n\n    instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE);\n\n    instance->iso15693_3_event.data = &instance->iso15693_3_event_data;\n    instance->generic_event.protocol = NfcProtocolIso15693_3;\n    instance->generic_event.instance = instance;\n    instance->generic_event.event_data = &instance->iso15693_3_event;\n\n    nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC);\n    nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693);\n\n    return instance;\n}\n\nvoid iso15693_3_listener_free(Iso15693_3Listener* instance) {\n    furi_assert(instance);\n\n    bit_buffer_free(instance->tx_buffer);\n\n    free(instance);\n}\n\nvoid iso15693_3_listener_set_callback(\n    Iso15693_3Listener* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nconst Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nNfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    Iso15693_3Listener* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypeRxEnd) {\n        BitBuffer* rx_buffer = nfc_event->data.buffer;\n\n        bit_buffer_reset(instance->tx_buffer);\n        if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {\n            iso13239_crc_trim(rx_buffer);\n\n            const Iso15693_3Error error = iso15693_3_listener_process_request(instance, rx_buffer);\n\n            if(error == Iso15693_3ErrorNotSupported) {\n                if(instance->callback) {\n                    instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand;\n                    instance->iso15693_3_event.data->buffer = rx_buffer;\n                    command = instance->callback(instance->generic_event, instance->context);\n                }\n\n            } else if(error == Iso15693_3ErrorUidMismatch) {\n                iso15693_3_listener_process_uid_mismatch(instance, rx_buffer);\n            }\n\n        } else if(bit_buffer_get_size(rx_buffer) == 0) {\n            // Special case: Single EOF\n            const Iso15693_3Error error = iso15693_3_listener_process_single_eof(instance);\n            if(error == Iso15693_3ErrorUnexpectedResponse) {\n                if(instance->callback) {\n                    instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof;\n                    command = instance->callback(instance->generic_event, instance->context);\n                }\n            }\n        } else {\n            FURI_LOG_D(\n                TAG, \"Wrong CRC, buffer size: %zu\", bit_buffer_get_size(nfc_event->data.buffer));\n        }\n    }\n\n    return command;\n}\n\nconst NfcListenerBase nfc_listener_iso15693_3 = {\n    .alloc = (NfcListenerAlloc)iso15693_3_listener_alloc,\n    .free = (NfcListenerFree)iso15693_3_listener_free,\n    .set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback,\n    .get_data = (NfcListenerGetData)iso15693_3_listener_get_data,\n    .run = (NfcListenerRun)iso15693_3_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_listener.h",
    "content": "#pragma once\n\n#include <nfc/nfc_listener.h>\n\n#include \"iso15693_3.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso15693_3Listener Iso15693_3Listener;\n\ntypedef enum {\n    Iso15693_3ListenerEventTypeFieldOff,\n    Iso15693_3ListenerEventTypeCustomCommand,\n    Iso15693_3ListenerEventTypeSingleEof,\n} Iso15693_3ListenerEventType;\n\ntypedef struct {\n    BitBuffer* buffer;\n} Iso15693_3ListenerEventData;\n\ntypedef struct {\n    Iso15693_3ListenerEventType type;\n    Iso15693_3ListenerEventData* data;\n} Iso15693_3ListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\nextern const NfcListenerBase nfc_listener_iso15693_3;\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c",
    "content": "#include \"iso15693_3_listener_i.h\"\n\n#include <nfc/helpers/iso13239_crc.h>\n\n#define TAG \"Iso15693_3Listener\"\n\ntypedef Iso15693_3Error (*Iso15693_3RequestHandler)(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags);\n\ntypedef struct {\n    Iso15693_3RequestHandler mandatory[ISO15693_3_MANDATORY_COUNT];\n    Iso15693_3RequestHandler optional[ISO15693_3_OPTIONAL_COUNT];\n} Iso15693_3ListenerHandlerTable;\n\nstatic Iso15693_3Error\n    iso15693_3_listener_extension_handler(Iso15693_3Listener* instance, uint32_t command, ...) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        if(instance->extension_table == NULL) break;\n\n        Iso15693_3ExtensionHandler handler = NULL;\n\n        if(command < ISO15693_3_CMD_MANDATORY_RFU) {\n            const Iso15693_3ExtensionHandler* mandatory = instance->extension_table->mandatory;\n            handler = mandatory[command - ISO15693_3_CMD_MANDATORY_START];\n        } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {\n            const Iso15693_3ExtensionHandler* optional = instance->extension_table->optional;\n            handler = optional[command - ISO15693_3_CMD_OPTIONAL_START];\n        }\n\n        if(handler == NULL) break;\n\n        va_list args;\n        va_start(args, command);\n\n        error = handler(instance->extension_context, args);\n\n        va_end(args);\n\n    } while(false);\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_inventory_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT;\n        const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1);\n\n        if(data_size < data_size_min) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        if(afi_flag) {\n            const uint8_t afi = *data++;\n            // When AFI flag is set, ignore non-matching requests\n            if(afi != 0) {\n                if(afi != instance->data->system_info.afi) break;\n            }\n        }\n\n        const uint8_t mask_len = *data++;\n        const size_t data_size_required = data_size_min + mask_len;\n\n        if(data_size != data_size_required) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        if(mask_len != 0) {\n            // TODO FL-3633: Take mask_len and mask_value into account (if present)\n        }\n\n        error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_INVENTORY);\n        if(error != Iso15693_3ErrorNone) break;\n\n        bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID\n        iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_stay_quiet_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    instance->state = Iso15693_3ListenerStateQuiet;\n    return Iso15693_3ErrorIgnore;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_read_block_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t block_num;\n        } Iso15693_3ReadBlockRequestLayout;\n\n        const Iso15693_3ReadBlockRequestLayout* request =\n            (const Iso15693_3ReadBlockRequestLayout*)data;\n\n        if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index = request->block_num;\n        const uint32_t block_count_max = instance->data->system_info.block_count;\n\n        if(block_index >= block_count_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(\n            instance, ISO15693_3_CMD_READ_BLOCK, block_index);\n        if(error != Iso15693_3ErrorNone) break;\n\n        if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {\n            iso15693_3_append_block_security(\n                instance->data, block_index, instance->tx_buffer); // Block security (optional)\n        }\n\n        iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_write_block_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t block_num;\n            uint8_t block_data[];\n        } Iso15693_3WriteBlockRequestLayout;\n\n        const Iso15693_3WriteBlockRequestLayout* request =\n            (const Iso15693_3WriteBlockRequestLayout*)data;\n\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index = request->block_num;\n        const uint32_t block_count_max = instance->data->system_info.block_count;\n        const uint32_t block_size_max = instance->data->system_info.block_size;\n        const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout);\n\n        if(block_index >= block_count_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        } else if(block_size_received != block_size_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        } else if(iso15693_3_is_block_locked(instance->data, block_index)) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(\n            instance, ISO15693_3_CMD_WRITE_BLOCK, block_index, request->block_data);\n        if(error != Iso15693_3ErrorNone) break;\n\n        iso15693_3_set_block_data(\n            instance->data, block_index, request->block_data, block_size_received);\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_lock_block_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t block_num;\n        } Iso15693_3LockBlockRequestLayout;\n\n        const Iso15693_3LockBlockRequestLayout* request =\n            (const Iso15693_3LockBlockRequestLayout*)data;\n\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index = request->block_num;\n        const uint32_t block_count_max = instance->data->system_info.block_count;\n\n        if(block_index >= block_count_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        } else if(iso15693_3_is_block_locked(instance->data, block_index)) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(\n            instance, ISO15693_3_CMD_LOCK_BLOCK, block_index);\n        if(error != Iso15693_3ErrorNone) break;\n\n        iso15693_3_set_block_locked(instance->data, block_index, true);\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t first_block_num;\n            uint8_t block_count;\n        } Iso15693_3ReadMultiBlocksRequestLayout;\n\n        const Iso15693_3ReadMultiBlocksRequestLayout* request =\n            (const Iso15693_3ReadMultiBlocksRequestLayout*)data;\n\n        if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index_start = request->first_block_num;\n        const uint32_t block_index_end =\n            MIN((block_index_start + request->block_count + 1),\n                ((uint32_t)instance->data->system_info.block_count - 1));\n\n        error = iso15693_3_listener_extension_handler(\n            instance,\n            ISO15693_3_CMD_READ_MULTI_BLOCKS,\n            (uint32_t)block_index_start,\n            (uint32_t)block_index_end);\n        if(error != Iso15693_3ErrorNone) break;\n\n        for(uint32_t i = block_index_start; i <= block_index_end; ++i) {\n            if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {\n                iso15693_3_append_block_security(\n                    instance->data, i, instance->tx_buffer); // Block security (optional)\n            }\n            iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t first_block_num;\n            uint8_t block_count;\n            uint8_t block_data[];\n        } Iso15693_3WriteMultiBlocksRequestLayout;\n\n        const Iso15693_3WriteMultiBlocksRequestLayout* request =\n            (const Iso15693_3WriteMultiBlocksRequestLayout*)data;\n\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index_start = request->first_block_num;\n        const uint32_t block_index_end = block_index_start + request->block_count;\n\n        const uint32_t block_count = request->block_count + 1;\n        const uint32_t block_count_max = instance->data->system_info.block_count;\n        const uint32_t block_count_available = block_count_max - block_index_start;\n\n        const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout);\n        const size_t block_size = block_data_size / block_count;\n        const size_t block_size_max = instance->data->system_info.block_size;\n\n        if(block_count > block_count_available) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        } else if(block_size != block_size_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(\n            instance, ISO15693_3_CMD_WRITE_MULTI_BLOCKS, block_index_start, block_index_end);\n        if(error != Iso15693_3ErrorNone) break;\n\n        for(uint32_t i = block_index_start; i <= block_index_end; ++i) {\n            if(iso15693_3_is_block_locked(instance->data, i)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n\n        if(error != Iso15693_3ErrorNone) break;\n\n        for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) {\n            const uint8_t* block_data = &request->block_data[block_size * i];\n            iso15693_3_set_block_data(instance->data, i, block_data, block_size);\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_select_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        instance->state = Iso15693_3ListenerStateSelected;\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_reset_to_ready_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    instance->state = Iso15693_3ListenerStateReady;\n    return Iso15693_3ErrorNone;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_write_afi_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t afi;\n        } Iso15693_3WriteAfiRequestLayout;\n\n        const Iso15693_3WriteAfiRequestLayout* request =\n            (const Iso15693_3WriteAfiRequestLayout*)data;\n\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        } else if(instance->data->settings.lock_bits.afi) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_AFI);\n        if(error != Iso15693_3ErrorNone) break;\n\n        instance->data->system_info.afi = request->afi;\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_lock_afi_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;\n\n        if(lock_bits->afi) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_AFI);\n        if(error != Iso15693_3ErrorNone) break;\n\n        lock_bits->afi = true;\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_write_dsfid_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t dsfid;\n        } Iso15693_3WriteDsfidRequestLayout;\n\n        const Iso15693_3WriteDsfidRequestLayout* request =\n            (const Iso15693_3WriteDsfidRequestLayout*)data;\n\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        } else if(instance->data->settings.lock_bits.dsfid) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_DSFID);\n        if(error != Iso15693_3ErrorNone) break;\n\n        instance->data->system_info.dsfid = request->dsfid;\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_lock_dsfid_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;\n\n        Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;\n\n        if(lock_bits->dsfid) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_DSFID);\n        if(error != Iso15693_3ErrorNone) break;\n\n        lock_bits->dsfid = true;\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_get_system_info_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        const uint8_t system_flags = instance->data->system_info.flags;\n        bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags\n\n        iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID\n\n        if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) {\n            bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid);\n        }\n        if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) {\n            bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi);\n        }\n        if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {\n            const uint8_t memory_info[2] = {\n                instance->data->system_info.block_count - 1,\n                instance->data->system_info.block_size - 1,\n            };\n            bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info));\n        }\n        if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {\n            bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref);\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(flags);\n\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t first_block_num;\n            uint8_t block_count;\n        } Iso15693_3GetMultiBlocksSecurityRequestLayout;\n\n        const Iso15693_3GetMultiBlocksSecurityRequestLayout* request =\n            (const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data;\n\n        if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const uint32_t block_index_start = request->first_block_num;\n        const uint32_t block_index_end = block_index_start + request->block_count;\n\n        const uint32_t block_count_max = instance->data->system_info.block_count;\n\n        if(block_index_end >= block_count_max) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        for(uint32_t i = block_index_start; i <= block_index_end; ++i) {\n            bit_buffer_append_byte(\n                instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0);\n        }\n    } while(false);\n\n    return error;\n}\n\nconst Iso15693_3ListenerHandlerTable iso15693_3_handler_table = {\n    .mandatory =\n        {\n            iso15693_3_listener_inventory_handler,\n            iso15693_3_listener_stay_quiet_handler,\n        },\n    .optional =\n        {\n            iso15693_3_listener_read_block_handler,\n            iso15693_3_listener_write_block_handler,\n            iso15693_3_listener_lock_block_handler,\n            iso15693_3_listener_read_multi_blocks_handler,\n            iso15693_3_listener_write_multi_blocks_handler,\n            iso15693_3_listener_select_handler,\n            iso15693_3_listener_reset_to_ready_handler,\n            iso15693_3_listener_write_afi_handler,\n            iso15693_3_listener_lock_afi_handler,\n            iso15693_3_listener_write_dsfid_handler,\n            iso15693_3_listener_lock_dsfid_handler,\n            iso15693_3_listener_get_system_info_handler,\n            iso15693_3_listener_get_multi_blocks_security_handler,\n        },\n};\n\nstatic Iso15693_3Error iso15693_3_listener_handle_standard_request(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t command,\n    uint8_t flags) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        Iso15693_3RequestHandler handler = NULL;\n\n        if(command < ISO15693_3_CMD_MANDATORY_RFU) {\n            handler = iso15693_3_handler_table.mandatory[command - ISO15693_3_CMD_MANDATORY_START];\n        } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {\n            handler = iso15693_3_handler_table.optional[command - ISO15693_3_CMD_OPTIONAL_START];\n        }\n\n        if(handler == NULL) {\n            error = Iso15693_3ErrorNotSupported;\n            break;\n        }\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE);\n\n        error = handler(instance, data, data_size, flags);\n\n        // The request was fully handled in the protocol extension, no further action necessary\n        if(error == Iso15693_3ErrorFullyHandled) {\n            error = Iso15693_3ErrorNone;\n        }\n\n        // Several commands may not require an answer\n        if(error == Iso15693_3ErrorFormat || error == Iso15693_3ErrorIgnore) break;\n\n        if(error != Iso15693_3ErrorNone) {\n            bit_buffer_reset(instance->tx_buffer);\n            bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR);\n            bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN);\n        }\n\n        Iso15693_3ListenerSessionState* session_state = &instance->session_state;\n\n        if(!session_state->wait_for_eof) {\n            error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic inline Iso15693_3Error iso15693_3_listener_handle_custom_request(\n    Iso15693_3Listener* instance,\n    const uint8_t* data,\n    size_t data_size) {\n    Iso15693_3Error error;\n\n    do {\n        typedef struct {\n            uint8_t manufacturer;\n            uint8_t extra[];\n        } Iso15693_3CustomRequestLayout;\n\n        if(data_size < sizeof(Iso15693_3CustomRequestLayout)) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const Iso15693_3CustomRequestLayout* request = (const Iso15693_3CustomRequestLayout*)data;\n\n        if(request->manufacturer != iso15693_3_get_manufacturer_id(instance->data)) {\n            error = Iso15693_3ErrorIgnore;\n            break;\n        }\n\n        // This error code will trigger the CustomCommand listener event\n        error = Iso15693_3ErrorNotSupported;\n    } while(false);\n\n    return error;\n}\n\nIso15693_3Error iso15693_3_listener_set_extension_handler_table(\n    Iso15693_3Listener* instance,\n    const Iso15693_3ExtensionHandlerTable* table,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(context);\n\n    instance->extension_table = table;\n    instance->extension_context = context;\n    return Iso15693_3ErrorNone;\n}\n\nIso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) {\n    furi_assert(instance);\n    instance->state = Iso15693_3ListenerStateReady;\n    return Iso15693_3ErrorNone;\n}\n\nstatic Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) {\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    if(error == NfcErrorNone) {\n        ret = Iso15693_3ErrorNone;\n    } else if(error == NfcErrorTimeout) {\n        ret = Iso15693_3ErrorTimeout;\n    } else {\n        ret = Iso15693_3ErrorFieldOff;\n    }\n\n    return ret;\n}\n\nIso15693_3Error\n    iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) {\n    furi_assert(instance);\n    furi_assert(tx_buffer);\n\n    bit_buffer_copy(instance->tx_buffer, tx_buffer);\n    iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);\n\n    NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);\n    return iso15693_3_listener_process_nfc_error(error);\n}\n\nIso15693_3Error\n    iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t flags;\n            uint8_t command;\n            uint8_t data[];\n        } Iso15693_3RequestLayout;\n\n        const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);\n        const size_t buf_size_min = sizeof(Iso15693_3RequestLayout);\n\n        if(buf_size < buf_size_min) {\n            error = Iso15693_3ErrorFormat;\n            break;\n        }\n\n        const Iso15693_3RequestLayout* request =\n            (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);\n\n        Iso15693_3ListenerSessionState* session_state = &instance->session_state;\n\n        if((request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5) == 0) {\n            session_state->selected = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED;\n            session_state->addressed = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED;\n\n            if(session_state->selected && session_state->addressed) {\n                // A request mode can be either addressed or selected, but not both\n                error = Iso15693_3ErrorUnknown;\n                break;\n            } else if(instance->state == Iso15693_3ListenerStateQuiet) {\n                // If the card is quiet, ignore non-addressed commands\n                if(session_state->addressed) {\n                    error = Iso15693_3ErrorIgnore;\n                    break;\n                }\n            } else if(instance->state != Iso15693_3ListenerStateSelected) {\n                // If the card is not selected, ignore selected commands\n                if(session_state->selected) {\n                    error = Iso15693_3ErrorIgnore;\n                    break;\n                }\n            }\n        } else {\n            // If the card is quiet, ignore inventory commands\n            if(instance->state == Iso15693_3ListenerStateQuiet) {\n                error = Iso15693_3ErrorIgnore;\n                break;\n            }\n\n            session_state->selected = false;\n            session_state->addressed = false;\n        }\n\n        if(request->command >= ISO15693_3_CMD_CUSTOM_START) {\n            // Custom commands are properly handled in the protocol-specific top-level poller\n            error = iso15693_3_listener_handle_custom_request(\n                instance, request->data, buf_size - buf_size_min);\n            break;\n        }\n\n        const uint8_t* data;\n        size_t data_size;\n\n        if(session_state->addressed) {\n            // In addressed mode, UID must be included in each command\n            const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE;\n\n            if(buf_size < buf_size_min_addr) {\n                error = Iso15693_3ErrorFormat;\n                break;\n            } else if(!iso15693_3_is_equal_uid(instance->data, request->data)) {\n                error = Iso15693_3ErrorUidMismatch;\n                break;\n            }\n\n            data = &request->data[ISO15693_3_UID_SIZE];\n            data_size = buf_size - buf_size_min_addr;\n\n        } else {\n            data = request->data;\n            data_size = buf_size - buf_size_min;\n        }\n\n        error = iso15693_3_listener_handle_standard_request(\n            instance, data, data_size, request->command, request->flags);\n\n    } while(false);\n\n    return error;\n}\n\nIso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        if(!instance->session_state.wait_for_eof) {\n            error = Iso15693_3ErrorUnexpectedResponse;\n            break;\n        }\n\n        instance->session_state.wait_for_eof = false;\n\n        error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);\n    } while(false);\n\n    return error;\n}\n\nIso15693_3Error iso15693_3_listener_process_uid_mismatch(\n    Iso15693_3Listener* instance,\n    const BitBuffer* rx_buffer) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    // No checks, assuming they have been made beforehand\n    typedef struct {\n        uint8_t flags;\n        uint8_t command;\n    } Iso15693_3RequestLayout;\n\n    const Iso15693_3RequestLayout* request =\n        (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);\n\n    if(request->command == ISO15693_3_CMD_SELECT) {\n        if(instance->state == Iso15693_3ListenerStateSelected) {\n            error = iso15693_3_listener_ready(instance);\n        }\n    }\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_generic_event.h>\n\n#include \"iso15693_3_listener.h\"\n\n#include \"iso15693_3_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    Iso15693_3ListenerStateReady,\n    Iso15693_3ListenerStateSelected,\n    Iso15693_3ListenerStateQuiet,\n} Iso15693_3ListenerState;\n\ntypedef struct {\n    bool selected;\n    bool addressed;\n    bool wait_for_eof;\n} Iso15693_3ListenerSessionState;\n\ntypedef Iso15693_3Error (*Iso15693_3ExtensionHandler)(void* context, va_list args);\n\ntypedef struct {\n    Iso15693_3ExtensionHandler mandatory[ISO15693_3_MANDATORY_COUNT];\n    Iso15693_3ExtensionHandler optional[ISO15693_3_OPTIONAL_COUNT];\n} Iso15693_3ExtensionHandlerTable;\n\nstruct Iso15693_3Listener {\n    Nfc* nfc;\n    Iso15693_3Data* data;\n    Iso15693_3ListenerState state;\n    Iso15693_3ListenerSessionState session_state;\n    BitBuffer* tx_buffer;\n\n    NfcGenericEvent generic_event;\n    Iso15693_3ListenerEvent iso15693_3_event;\n    Iso15693_3ListenerEventData iso15693_3_event_data;\n    NfcGenericCallback callback;\n    void* context;\n\n    const Iso15693_3ExtensionHandlerTable* extension_table;\n    void* extension_context;\n};\n\nIso15693_3Error iso15693_3_listener_set_extension_handler_table(\n    Iso15693_3Listener* instance,\n    const Iso15693_3ExtensionHandlerTable* table,\n    void* context);\n\nIso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance);\n\nIso15693_3Error\n    iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer);\n\nIso15693_3Error\n    iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer);\n\nIso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance);\n\nIso15693_3Error iso15693_3_listener_process_uid_mismatch(\n    Iso15693_3Listener* instance,\n    const BitBuffer* rx_buffer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_poller.c",
    "content": "#include \"iso15693_3_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"ISO15693_3Poller\"\n\nconst Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n\n    Iso15693_3Poller* instance = malloc(sizeof(Iso15693_3Poller));\n    instance->nfc = nfc;\n    instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);\n\n    nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);\n    nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US);\n    nfc_set_fdt_poll_fc(instance->nfc, ISO15693_3_FDT_POLL_FC);\n    nfc_set_fdt_poll_poll_us(instance->nfc, ISO15693_3_POLL_POLL_MIN_US);\n    instance->data = iso15693_3_alloc();\n\n    instance->iso15693_3_event.data = &instance->iso15693_3_event_data;\n    instance->general_event.protocol = NfcProtocolIso15693_3;\n    instance->general_event.event_data = &instance->iso15693_3_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void iso15693_3_poller_free(Iso15693_3Poller* instance) {\n    furi_assert(instance);\n\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(instance->data);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    iso15693_3_free(instance->data);\n    free(instance);\n}\n\nstatic void iso15693_3_poller_set_callback(\n    Iso15693_3Poller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand iso15693_3_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    Iso15693_3Poller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        if(instance->state != Iso15693_3PollerStateActivated) {\n            Iso15693_3Error error = iso15693_3_poller_activate(instance, instance->data);\n            if(error == Iso15693_3ErrorNone) {\n                instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;\n                instance->iso15693_3_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n            } else {\n                instance->iso15693_3_event.type = Iso15693_3PollerEventTypeError;\n                instance->iso15693_3_event_data.error = error;\n                command = instance->callback(instance->general_event, instance->context);\n                // Add delay to switch context\n                furi_delay_ms(100);\n            }\n        } else {\n            instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;\n            instance->iso15693_3_event_data.error = Iso15693_3ErrorNone;\n            command = instance->callback(instance->general_event, instance->context);\n        }\n    }\n\n    return command;\n}\n\nstatic bool iso15693_3_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n\n    bool protocol_detected = false;\n    Iso15693_3Poller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    furi_assert(instance->state == Iso15693_3PollerStateIdle);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        uint8_t uid[ISO15693_3_UID_SIZE];\n        Iso15693_3Error error = iso15693_3_poller_inventory(instance, uid);\n        protocol_detected = (error == Iso15693_3ErrorNone);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_iso15693_3 = {\n    .alloc = (NfcPollerAlloc)iso15693_3_poller_alloc,\n    .free = (NfcPollerFree)iso15693_3_poller_free,\n    .set_callback = (NfcPollerSetCallback)iso15693_3_poller_set_callback,\n    .run = (NfcPollerRun)iso15693_3_poller_run,\n    .detect = (NfcPollerDetect)iso15693_3_poller_detect,\n    .get_data = (NfcPollerGetData)iso15693_3_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_poller.h",
    "content": "#pragma once\n\n#include \"iso15693_3.h\"\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Iso15693_3Poller opaque type definition.\n */\ntypedef struct Iso15693_3Poller Iso15693_3Poller;\n\n/**\n * @brief Enumeration of possible Iso15693_3 poller event types.\n */\ntypedef enum {\n    Iso15693_3PollerEventTypeError, /**< An error occured during activation procedure. */\n    Iso15693_3PollerEventTypeReady, /**< The card was activated by the poller. */\n} Iso15693_3PollerEventType;\n\n/**\n * @brief Iso15693_3 poller event data.\n */\ntypedef union {\n    Iso15693_3Error error; /**< Error code indicating card activation fail reason. */\n} Iso15693_3PollerEventData;\n\n/**\n * @brief Iso15693_3 poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    Iso15693_3PollerEventType type; /**< Type of emmitted event. */\n    Iso15693_3PollerEventData* data; /**< Pointer to event specific data. */\n} Iso15693_3PollerEvent;\n\n/**\n * @brief Transmit and receive Iso15693_3 frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_send_frame(\n    Iso15693_3Poller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\n/**\n * @brief Perform activation procedure.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perfoms the activation procedure as defined in Iso15693_3. The data\n * field will be filled with Iso15693_3Data data on success.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the Iso15693_3 data structure to be filled.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_3Data* data);\n\n/**\n * @brief Send invertory command and parse response.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] uid pointer to the buffer to be filled with the UID.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_inventory(Iso15693_3Poller* instance, uint8_t* uid);\n\n/**\n * @brief Send get system info command and parse response.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the Iso15693_3SystemInfo structure to be filled.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error\n    iso15693_3_poller_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data);\n\n/**\n * @brief Read Iso15693_3 block.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the buffer to be filled with the block data.\n * @param[in] block_number block number to be read.\n * @param[in] block_size size of the block to be read.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_read_block(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint8_t block_number,\n    uint8_t block_size);\n\n/**\n * @brief Read multiple Iso15693_3 blocks.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the buffer to be filled with the block data.\n * @param[in] block_count number of blocks to be read.\n * @param[in] block_size size of the blocks to be read.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_read_blocks(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint16_t block_count,\n    uint8_t block_size);\n\n/**\n * @brief Get Iso15693_3 block security status.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the buffer to be filled with the block security status.\n * @param[in] block_count block security number to be read.\n * @return Iso15693_3ErrorNone on success, an error code on failure.\n */\nIso15693_3Error iso15693_3_poller_get_blocks_security(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint16_t block_count);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_iso15693_3;\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c",
    "content": "#include \"iso15693_3_poller_i.h\"\n\n#include <nfc/helpers/iso13239_crc.h>\n\n#define TAG \"Iso15693_3Poller\"\n\n#define BITS_IN_BYTE (8)\n\n#define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U)\n\nstatic Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return Iso15693_3ErrorNone;\n    case NfcErrorTimeout:\n        return Iso15693_3ErrorTimeout;\n    default:\n        return Iso15693_3ErrorNotPresent;\n    }\n}\n\nstatic Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) {\n    switch(error) {\n    /* If a particular optional command is not supported, the card might\n     * respond with a \"Not supported\" error or not respond at all.\n     * Therefore, treat these errors as non-critical ones. */\n    case Iso15693_3ErrorNotSupported:\n    case Iso15693_3ErrorTimeout:\n        return Iso15693_3ErrorNone;\n    default:\n        return error;\n    }\n}\n\nIso15693_3Error iso15693_3_poller_send_frame(\n    Iso15693_3Poller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_assert(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    do {\n        if(bit_buffer_get_size_bytes(tx_buffer) >\n           bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO13239_CRC_SIZE) {\n            ret = Iso15693_3ErrorBufferOverflow;\n            break;\n        }\n\n        bit_buffer_copy(instance->tx_buffer, tx_buffer);\n        iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);\n\n        NfcError error =\n            nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);\n        if(error != NfcErrorNone) {\n            ret = iso15693_3_poller_process_nfc_error(error);\n            break;\n        }\n\n        if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) {\n            ret = Iso15693_3ErrorWrongCrc;\n            break;\n        }\n\n        iso13239_crc_trim(instance->rx_buffer);\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_3Data* data) {\n    furi_assert(instance);\n    furi_assert(instance->nfc);\n\n    iso15693_3_reset(data);\n\n    Iso15693_3Error ret;\n\n    do {\n        instance->state = Iso15693_3PollerStateColResInProgress;\n\n        // Inventory: Mandatory command\n        ret = iso15693_3_poller_inventory(instance, data->uid);\n        if(ret != Iso15693_3ErrorNone) {\n            instance->state = Iso15693_3PollerStateColResFailed;\n            break;\n        }\n\n        instance->state = Iso15693_3PollerStateActivated;\n\n        // Get system info: Optional command\n        Iso15693_3SystemInfo* system_info = &data->system_info;\n        ret = iso15693_3_poller_get_system_info(instance, system_info);\n        if(ret != Iso15693_3ErrorNone) {\n            ret = iso15693_3_poller_filter_error(ret);\n            break;\n        }\n\n        if(system_info->block_count > 0 && system_info->block_size > 0) {\n            simple_array_init(\n                data->block_data, system_info->block_count * system_info->block_size);\n            simple_array_init(data->block_security, system_info->block_count);\n\n            // Read blocks: Optional command\n            ret = iso15693_3_poller_read_blocks(\n                instance,\n                simple_array_get_data(data->block_data),\n                system_info->block_count,\n                system_info->block_size);\n            if(ret != Iso15693_3ErrorNone) {\n                ret = iso15693_3_poller_filter_error(ret);\n                break;\n            }\n\n            // Get block security status: Optional command\n            ret = iso15693_3_poller_get_blocks_security(\n                instance, simple_array_get_data(data->block_security), system_info->block_count);\n            if(ret != Iso15693_3ErrorNone) {\n                ret = iso15693_3_poller_filter_error(ret);\n                break;\n            }\n        }\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_poller_inventory(Iso15693_3Poller* instance, uint8_t* uid) {\n    furi_assert(instance);\n    furi_assert(instance->nfc);\n    furi_assert(uid);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    // Send INVENTORY\n    bit_buffer_append_byte(\n        instance->tx_buffer,\n        ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI |\n            ISO15693_3_REQ_FLAG_INVENTORY_T5 | ISO15693_3_REQ_FLAG_T5_N_SLOTS_1);\n    bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_INVENTORY);\n    bit_buffer_append_byte(instance->tx_buffer, 0x00);\n\n    Iso15693_3Error ret;\n\n    do {\n        ret = iso15693_3_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(ret != Iso15693_3ErrorNone) break;\n\n        ret = iso15693_3_inventory_response_parse(uid, instance->rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error\n    iso15693_3_poller_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    // Send GET SYSTEM INFO\n    bit_buffer_append_byte(\n        instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);\n\n    bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_SYS_INFO);\n\n    Iso15693_3Error ret;\n\n    do {\n        ret = iso15693_3_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(ret != Iso15693_3ErrorNone) break;\n\n        ret = iso15693_3_system_info_response_parse(data, instance->rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_poller_read_block(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint8_t block_number,\n    uint8_t block_size) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    bit_buffer_append_byte(\n        instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);\n    bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_READ_BLOCK);\n    bit_buffer_append_byte(instance->tx_buffer, block_number);\n\n    Iso15693_3Error ret;\n\n    do {\n        ret = iso15693_3_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(ret != Iso15693_3ErrorNone) break;\n\n        ret = iso15693_3_read_block_response_parse(data, block_size, instance->rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_poller_read_blocks(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint16_t block_count,\n    uint8_t block_size) {\n    furi_assert(instance);\n    furi_assert(data);\n    furi_assert(block_count);\n    furi_assert(block_size);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    for(uint32_t i = 0; i < block_count; ++i) {\n        ret = iso15693_3_poller_read_block(instance, &data[block_size * i], i, block_size);\n        if(ret != Iso15693_3ErrorNone) break;\n    }\n\n    return ret;\n}\n\nIso15693_3Error iso15693_3_poller_get_blocks_security(\n    Iso15693_3Poller* instance,\n    uint8_t* data,\n    uint16_t block_count) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    // Limit the number of blocks to 32 in a single query\n    const uint32_t num_queries = block_count / ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY +\n                                 (block_count % ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY ? 1 : 0);\n\n    Iso15693_3Error ret = Iso15693_3ErrorNone;\n\n    for(uint32_t i = 0; i < num_queries; ++i) {\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        bit_buffer_append_byte(\n            instance->tx_buffer,\n            ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);\n\n        bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_BLOCKS_SECURITY);\n\n        const uint8_t start_block_num = i * ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY;\n        bit_buffer_append_byte(instance->tx_buffer, start_block_num);\n\n        const uint8_t block_count_per_query =\n            MIN(block_count - start_block_num, (uint16_t)ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY);\n        // Block count byte must be 1 less than the desired count\n        bit_buffer_append_byte(instance->tx_buffer, block_count_per_query - 1);\n\n        ret = iso15693_3_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(ret != Iso15693_3ErrorNone) break;\n\n        ret = iso15693_3_get_block_security_response_parse(\n            &data[start_block_num], block_count_per_query, instance->rx_buffer);\n        if(ret != Iso15693_3ErrorNone) break;\n    }\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h",
    "content": "#pragma once\n\n#include \"iso15693_3_poller.h\"\n\n#include \"iso15693_3_i.h\"\n\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ISO15693_3_POLLER_MAX_BUFFER_SIZE (64U)\n\ntypedef enum {\n    Iso15693_3PollerStateIdle,\n    Iso15693_3PollerStateColResInProgress,\n    Iso15693_3PollerStateColResFailed,\n    Iso15693_3PollerStateActivated,\n} Iso15693_3PollerState;\n\nstruct Iso15693_3Poller {\n    Nfc* nfc;\n    Iso15693_3PollerState state;\n    Iso15693_3Data* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    NfcGenericEvent general_event;\n    Iso15693_3PollerEvent iso15693_3_event;\n    Iso15693_3PollerEventData iso15693_3_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic.c",
    "content": "#include \"mf_classic.h\"\n\n#include <furi/furi.h>\n#include <toolbox/hex.h>\n\n#include <lib/bit_lib/bit_lib.h>\n\n#define MF_CLASSIC_PROTOCOL_NAME \"Mifare Classic\"\n\ntypedef struct {\n    uint8_t sectors_total;\n    uint16_t blocks_total;\n    const char* full_name;\n    const char* type_name;\n} MfClassicFeatures;\n\nstatic const uint32_t mf_classic_data_format_version = 2;\n\nstatic const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = {\n    [MfClassicTypeMini] =\n        {\n            .sectors_total = 5,\n            .blocks_total = 20,\n            .full_name = \"Mifare Classic Mini 0.3K\",\n            .type_name = \"MINI\",\n        },\n    [MfClassicType1k] =\n        {\n            .sectors_total = 16,\n            .blocks_total = 64,\n            .full_name = \"Mifare Classic 1K\",\n            .type_name = \"1K\",\n        },\n    [MfClassicType4k] =\n        {\n            .sectors_total = 40,\n            .blocks_total = 256,\n            .full_name = \"Mifare Classic 4K\",\n            .type_name = \"4K\",\n        },\n};\n\nconst NfcDeviceBase nfc_device_mf_classic = {\n    .protocol_name = MF_CLASSIC_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)mf_classic_alloc,\n    .free = (NfcDeviceFree)mf_classic_free,\n    .reset = (NfcDeviceReset)mf_classic_reset,\n    .copy = (NfcDeviceCopy)mf_classic_copy,\n    .verify = (NfcDeviceVerify)mf_classic_verify,\n    .load = (NfcDeviceLoad)mf_classic_load,\n    .save = (NfcDeviceSave)mf_classic_save,\n    .is_equal = (NfcDeviceEqual)mf_classic_is_equal,\n    .get_name = (NfcDeviceGetName)mf_classic_get_device_name,\n    .get_uid = (NfcDeviceGetUid)mf_classic_get_uid,\n    .set_uid = (NfcDeviceSetUid)mf_classic_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data,\n};\n\nMfClassicData* mf_classic_alloc(void) {\n    MfClassicData* data = malloc(sizeof(MfClassicData));\n    data->iso14443_3a_data = iso14443_3a_alloc();\n    return data;\n}\n\nvoid mf_classic_free(MfClassicData* data) {\n    furi_check(data);\n\n    iso14443_3a_free(data->iso14443_3a_data);\n    free(data);\n}\n\nvoid mf_classic_reset(MfClassicData* data) {\n    furi_check(data);\n\n    iso14443_3a_reset(data->iso14443_3a_data);\n}\n\nvoid mf_classic_copy(MfClassicData* data, const MfClassicData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);\n    for(size_t i = 0; i < COUNT_OF(data->block); i++) {\n        data->block[i] = other->block[i];\n    }\n    for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {\n        data->block_read_mask[i] = other->block_read_mask[i];\n    }\n    data->type = other->type;\n    data->key_a_mask = other->key_a_mask;\n    data->key_b_mask = other->key_b_mask;\n}\n\nbool mf_classic_verify(MfClassicData* data, const FuriString* device_type) {\n    furi_check(device_type);\n    UNUSED(data);\n\n    return furi_string_equal_str(device_type, \"Mifare Classic\");\n}\n\nstatic void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) {\n    furi_string_trim(block_str);\n    MfClassicBlock block_tmp = {};\n    bool is_sector_trailer = mf_classic_is_sector_trailer(block_num);\n    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n    uint16_t block_unknown_bytes_mask = 0;\n\n    furi_string_trim(block_str);\n    for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {\n        char hi = furi_string_get_char(block_str, 3 * i);\n        char low = furi_string_get_char(block_str, 3 * i + 1);\n        uint8_t byte = 0;\n        if(hex_char_to_uint8(hi, low, &byte)) {\n            block_tmp.data[i] = byte;\n        } else {\n            FURI_BIT_SET(block_unknown_bytes_mask, i);\n        }\n    }\n\n    if(block_unknown_bytes_mask != 0xffff) {\n        if(is_sector_trailer) {\n            MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp;\n            // Load Key A\n            // Key A mask 0b0000000000111111 = 0x003f\n            if((block_unknown_bytes_mask & 0x003f) == 0) {\n                uint64_t key =\n                    bit_lib_bytes_to_num_be(sec_tr_tmp->key_a.data, sizeof(MfClassicKey));\n                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key);\n            }\n            // Load Access Bits\n            // Access bits mask 0b0000001111000000 = 0x03c0\n            if((block_unknown_bytes_mask & 0x03c0) == 0) {\n                mf_classic_set_block_read(data, block_num, &block_tmp);\n            }\n            // Load Key B\n            // Key B mask 0b1111110000000000 = 0xfc00\n            if((block_unknown_bytes_mask & 0xfc00) == 0) {\n                uint64_t key =\n                    bit_lib_bytes_to_num_be(sec_tr_tmp->key_b.data, sizeof(MfClassicKey));\n                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key);\n            }\n        } else {\n            if(block_unknown_bytes_mask == 0) {\n                mf_classic_set_block_read(data, block_num, &block_tmp);\n            }\n        }\n    }\n}\n\nbool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool parsed = false;\n\n    do {\n        // Read ISO14443_3A data\n        if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;\n\n        // Read Mifare Classic type\n        if(!flipper_format_read_string(ff, \"Mifare Classic type\", temp_str)) break;\n        bool type_parsed = false;\n        for(size_t i = 0; i < MfClassicTypeNum; i++) {\n            if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) {\n                data->type = i;\n                type_parsed = true;\n            }\n        }\n        if(!type_parsed) break;\n\n        // Read format version\n        uint32_t data_format_version = 0;\n        bool old_format = false;\n        // Read Mifare Classic format version\n        if(!flipper_format_read_uint32(ff, \"Data format version\", &data_format_version, 1)) {\n            // Load unread sectors with zero keys access for backward compatibility\n            if(!flipper_format_rewind(ff)) break;\n            old_format = true;\n        } else {\n            if(data_format_version < mf_classic_data_format_version) {\n                old_format = true;\n            }\n        }\n\n        // Read Mifare Classic blocks\n        bool block_read = true;\n        FuriString* block_str = furi_string_alloc();\n        uint16_t blocks_total = mf_classic_get_total_block_num(data->type);\n        for(size_t i = 0; i < blocks_total; i++) {\n            furi_string_printf(temp_str, \"Block %d\", i);\n            if(!flipper_format_read_string(ff, furi_string_get_cstr(temp_str), block_str)) {\n                block_read = false;\n                break;\n            }\n            mf_classic_parse_block(block_str, data, i);\n        }\n        furi_string_free(block_str);\n        if(!block_read) break;\n\n        // Set keys and blocks as unknown for backward compatibility\n        if(old_format) {\n            data->key_a_mask = 0ULL;\n            data->key_b_mask = 0ULL;\n            memset(data->block_read_mask, 0, sizeof(data->block_read_mask));\n        }\n\n        parsed = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n\n    return parsed;\n}\n\nstatic void\n    mf_classic_set_block_str(FuriString* block_str, const MfClassicData* data, uint8_t block_num) {\n    furi_string_reset(block_str);\n    bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);\n    if(is_sec_trailer) {\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);\n        // Write key A\n        for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) {\n            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) {\n                furi_string_cat_printf(block_str, \"%02X \", sec_tr->key_a.data[i]);\n            } else {\n                furi_string_cat_printf(block_str, \"?? \");\n            }\n        }\n        // Write Access bytes\n        for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) {\n            if(mf_classic_is_block_read(data, block_num)) {\n                furi_string_cat_printf(block_str, \"%02X \", sec_tr->access_bits.data[i]);\n            } else {\n                furi_string_cat_printf(block_str, \"?? \");\n            }\n        }\n        // Write key B\n        for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) {\n            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) {\n                furi_string_cat_printf(block_str, \"%02X \", sec_tr->key_b.data[i]);\n            } else {\n                furi_string_cat_printf(block_str, \"?? \");\n            }\n        }\n    } else {\n        // Write data block\n        for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {\n            if(mf_classic_is_block_read(data, block_num)) {\n                furi_string_cat_printf(block_str, \"%02X \", data->block[block_num].data[i]);\n            } else {\n                furi_string_cat_printf(block_str, \"?? \");\n            }\n        }\n    }\n    furi_string_trim(block_str);\n}\n\nbool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool saved = false;\n\n    do {\n        if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;\n\n        if(!flipper_format_write_comment_cstr(ff, \"Mifare Classic specific data\")) break;\n        if(!flipper_format_write_string_cstr(\n               ff, \"Mifare Classic type\", mf_classic_features[data->type].type_name))\n            break;\n        if(!flipper_format_write_uint32(\n               ff, \"Data format version\", &mf_classic_data_format_version, 1))\n            break;\n        if(!flipper_format_write_comment_cstr(\n               ff, \"Mifare Classic blocks, \\'??\\' means unknown data\"))\n            break;\n\n        uint16_t blocks_total = mf_classic_get_total_block_num(data->type);\n        FuriString* block_str = furi_string_alloc();\n        bool block_saved = true;\n        for(size_t i = 0; i < blocks_total; i++) {\n            furi_string_printf(temp_str, \"Block %d\", i);\n            mf_classic_set_block_str(block_str, data, i);\n            if(!flipper_format_write_string(ff, furi_string_get_cstr(temp_str), block_str)) {\n                block_saved = false;\n                break;\n            }\n        }\n        furi_string_free(block_str);\n        if(!block_saved) break;\n\n        saved = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n\n    return saved;\n}\n\nbool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    bool is_equal = false;\n    bool data_array_is_equal = true;\n\n    do {\n        if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break;\n        if(data->type != other->type) break;\n        if(data->key_a_mask != other->key_a_mask) break;\n        if(data->key_b_mask != other->key_b_mask) break;\n\n        for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {\n            if(data->block_read_mask[i] != other->block_read_mask[i]) {\n                data_array_is_equal = false;\n                break;\n            }\n        }\n        if(!data_array_is_equal) break;\n\n        for(size_t i = 0; i < COUNT_OF(data->block); i++) {\n            if(memcmp(&data->block[i], &other->block[i], sizeof(data->block[i])) != 0) {\n                data_array_is_equal = false;\n                break;\n            }\n        }\n        if(!data_array_is_equal) break;\n\n        is_equal = true;\n    } while(false);\n\n    return is_equal;\n}\n\nconst char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n    furi_check(data->type < MfClassicTypeNum);\n\n    if(name_type == NfcDeviceNameTypeFull) {\n        return mf_classic_features[data->type].full_name;\n    } else {\n        return mf_classic_features[data->type].type_name;\n    }\n}\n\nconst uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) {\n    furi_check(data);\n\n    return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);\n}\n\nbool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);\n\n    if(uid_valid) {\n        uint8_t* block = data->block[0].data;\n\n        // Copy UID to block 0\n        memcpy(block, data->iso14443_3a_data->uid, uid_len);\n\n        if(uid_len == 4) {\n            // Calculate BCC byte\n            block[uid_len] = 0;\n\n            for(size_t i = 0; i < uid_len; i++) {\n                block[uid_len] ^= block[i];\n            }\n        }\n    }\n\n    return uid_valid;\n}\n\nIso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) {\n    furi_check(data);\n\n    return data->iso14443_3a_data;\n}\n\nuint8_t mf_classic_get_total_sectors_num(MfClassicType type) {\n    furi_check(type < MfClassicTypeNum);\n    return mf_classic_features[type].sectors_total;\n}\n\nuint16_t mf_classic_get_total_block_num(MfClassicType type) {\n    furi_check(type < MfClassicTypeNum);\n    return mf_classic_features[type].blocks_total;\n}\n\nuint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector) {\n    uint8_t block_num = 0;\n\n    if(sector < 32) {\n        block_num = sector * 4 + 3;\n    } else if(sector < 40) {\n        block_num = 32 * 4 + (sector - 32) * 16 + 15;\n    } else {\n        furi_crash(\"Wrong sector num\");\n    }\n\n    return block_num;\n}\n\nuint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) {\n    uint8_t sec_tr_block_num = 0;\n\n    if(block < 128) {\n        sec_tr_block_num = block | 0x03;\n    } else {\n        sec_tr_block_num = block | 0x0f;\n    }\n\n    return sec_tr_block_num;\n}\n\nMfClassicSectorTrailer*\n    mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) {\n    furi_check(data);\n\n    uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num);\n    MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block];\n\n    return sec_trailer;\n}\n\nbool mf_classic_is_sector_trailer(uint8_t block) {\n    return block == mf_classic_get_sector_trailer_num_by_block(block);\n}\n\nvoid mf_classic_set_sector_trailer_read(\n    MfClassicData* data,\n    uint8_t block_num,\n    MfClassicSectorTrailer* sec_tr) {\n    furi_check(data);\n    furi_check(sec_tr);\n    furi_check(mf_classic_is_sector_trailer(block_num));\n\n    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n    MfClassicSectorTrailer* sec_trailer =\n        mf_classic_get_sector_trailer_by_sector(data, sector_num);\n    memcpy(sec_trailer, sec_tr, sizeof(MfClassicSectorTrailer));\n    FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);\n    FURI_BIT_SET(data->key_a_mask, sector_num);\n    FURI_BIT_SET(data->key_b_mask, sector_num);\n}\n\nuint8_t mf_classic_get_sector_by_block(uint8_t block) {\n    uint8_t sector = 0;\n\n    if(block < 128) {\n        sector = (block | 0x03) / 4;\n    } else {\n        sector = 32 + ((block | 0x0f) - 32 * 4) / 16;\n    }\n\n    return sector;\n}\n\nbool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) {\n    furi_check(block);\n    furi_check(value);\n\n    uint32_t v = *(uint32_t*)&block->data[0];\n    uint32_t v_inv = *(uint32_t*)&block->data[sizeof(uint32_t)];\n    uint32_t v1 = *(uint32_t*)&block->data[sizeof(uint32_t) * 2];\n\n    bool val_checks =\n        ((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) &&\n         (block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14]));\n    *value = (int32_t)v;\n    if(addr) {\n        *addr = block->data[12];\n    }\n    return val_checks;\n}\n\nvoid mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) {\n    furi_check(block);\n\n    uint32_t v_inv = ~((uint32_t)value);\n\n    memcpy(&block->data[0], &value, 4); //-V1086\n    memcpy(&block->data[4], &v_inv, 4); //-V1086\n    memcpy(&block->data[8], &value, 4); //-V1086\n\n    block->data[12] = addr;\n    block->data[13] = ~addr & 0xFF;\n    block->data[14] = addr;\n    block->data[15] = ~addr & 0xFF;\n}\n\nbool mf_classic_is_key_found(\n    const MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type) {\n    furi_check(data);\n\n    bool key_found = false;\n    if(key_type == MfClassicKeyTypeA) {\n        key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1);\n    } else if(key_type == MfClassicKeyTypeB) {\n        key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1);\n    }\n\n    return key_found;\n}\n\nvoid mf_classic_set_key_found(\n    MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type,\n    uint64_t key) {\n    furi_check(data);\n\n    uint8_t key_arr[6] = {};\n    MfClassicSectorTrailer* sec_trailer =\n        mf_classic_get_sector_trailer_by_sector(data, sector_num);\n    bit_lib_num_to_bytes_be(key, 6, key_arr);\n    if(key_type == MfClassicKeyTypeA) {\n        memcpy(sec_trailer->key_a.data, key_arr, sizeof(MfClassicKey));\n        FURI_BIT_SET(data->key_a_mask, sector_num);\n    } else if(key_type == MfClassicKeyTypeB) {\n        memcpy(sec_trailer->key_b.data, key_arr, sizeof(MfClassicKey));\n        FURI_BIT_SET(data->key_b_mask, sector_num);\n    }\n}\n\nvoid mf_classic_set_key_not_found(\n    MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type) {\n    furi_check(data);\n\n    if(key_type == MfClassicKeyTypeA) {\n        FURI_BIT_CLEAR(data->key_a_mask, sector_num);\n    } else if(key_type == MfClassicKeyTypeB) {\n        FURI_BIT_CLEAR(data->key_b_mask, sector_num);\n    }\n}\n\nMfClassicKey\n    mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type) {\n    furi_check(data);\n    furi_check(sector_num < mf_classic_get_total_sectors_num(data->type));\n    furi_check(key_type == MfClassicKeyTypeA || key_type == MfClassicKeyTypeB);\n\n    const MfClassicSectorTrailer* sector_trailer =\n        mf_classic_get_sector_trailer_by_sector(data, sector_num);\n\n    if(key_type == MfClassicKeyTypeA) {\n        return sector_trailer->key_a;\n    } else {\n        return sector_trailer->key_b;\n    }\n}\n\nbool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) {\n    furi_check(data);\n\n    return FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1;\n}\n\nvoid mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) {\n    furi_check(data);\n    furi_check(block_data);\n\n    if(mf_classic_is_sector_trailer(block_num)) {\n        memcpy(&data->block[block_num].data[6], &block_data->data[6], 4);\n    } else {\n        memcpy(data->block[block_num].data, block_data->data, MF_CLASSIC_BLOCK_SIZE);\n    }\n    FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);\n}\n\nuint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {\n    furi_check(sector < 40);\n\n    uint8_t block = 0;\n    if(sector < 32) {\n        block = sector * 4;\n    } else {\n        block = 32 * 4 + (sector - 32) * 16;\n    }\n\n    return block;\n}\n\nuint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) {\n    furi_check(sector < 40);\n\n    return sector < 32 ? 4 : 16;\n}\n\nvoid mf_classic_get_read_sectors_and_keys(\n    const MfClassicData* data,\n    uint8_t* sectors_read,\n    uint8_t* keys_found) {\n    furi_check(data);\n    furi_check(sectors_read);\n    furi_check(keys_found);\n\n    *sectors_read = 0;\n    *keys_found = 0;\n    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);\n    for(size_t i = 0; i < sectors_total; i++) {\n        if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) {\n            *keys_found += 1;\n        }\n        if(mf_classic_is_key_found(data, i, MfClassicKeyTypeB)) {\n            *keys_found += 1;\n        }\n        uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);\n        uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i);\n        bool blocks_read = true;\n        for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) {\n            blocks_read = mf_classic_is_block_read(data, j);\n            if(!blocks_read) break;\n        }\n        if(blocks_read) {\n            *sectors_read += 1;\n        }\n    }\n}\n\nbool mf_classic_is_card_read(const MfClassicData* data) {\n    furi_check(data);\n\n    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);\n    uint8_t sectors_read = 0;\n    uint8_t keys_found = 0;\n    mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);\n    bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);\n\n    return card_read;\n}\n\nbool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) {\n    furi_check(data);\n\n    bool sector_read = false;\n    do {\n        if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) break;\n        if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) break;\n        uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num);\n        uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);\n        uint8_t block_read = true;\n        for(size_t i = start_block; i < start_block + total_blocks; i++) {\n            block_read = mf_classic_is_block_read(data, i);\n            if(!block_read) break;\n        }\n        sector_read = block_read;\n    } while(false);\n\n    return sector_read;\n}\n\nstatic bool mf_classic_is_allowed_access_sector_trailer(\n    MfClassicData* data,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicAction action) {\n    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n    MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);\n    uint8_t* access_bits_arr = sec_tr->access_bits.data;\n    uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) |\n                 ((access_bits_arr[2] >> 7) & 0x01);\n    FURI_LOG_T(\"NFC\", \"AC: %02X\", AC);\n\n    switch(action) {\n    case MfClassicActionKeyARead: {\n        return false;\n    }\n    case MfClassicActionKeyAWrite:\n    case MfClassicActionKeyBWrite: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) ||\n               (key_type == MfClassicKeyTypeB &&\n                (AC == 0x00 || AC == 0x04 || AC == 0x03 || AC == 0x01));\n    }\n    case MfClassicActionKeyBRead: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)) ||\n               (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x02 || AC == 0x01));\n    }\n    case MfClassicActionACRead: {\n        return (key_type == MfClassicKeyTypeA) || (key_type == MfClassicKeyTypeB);\n    }\n    case MfClassicActionACWrite: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x01)) ||\n               (key_type == MfClassicKeyTypeB && (AC == 0x01 || AC == 0x03 || AC == 0x05));\n    }\n    default:\n        return false;\n    }\n    return true;\n}\n\nbool mf_classic_is_allowed_access_data_block(\n    MfClassicSectorTrailer* sec_tr,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicAction action) {\n    furi_check(sec_tr);\n\n    uint8_t* access_bits_arr = sec_tr->access_bits.data;\n\n    if(block_num == 0 && action == MfClassicActionDataWrite) {\n        return false;\n    }\n\n    uint8_t sector_block = 0;\n    if(block_num <= 128) {\n        sector_block = block_num & 0x03;\n    } else {\n        sector_block = (block_num & 0x0f) / 5;\n    }\n\n    uint8_t AC;\n    switch(sector_block) {\n    case 0x00: {\n        AC = ((access_bits_arr[1] >> 2) & 0x04) | ((access_bits_arr[2] << 1) & 0x02) |\n             ((access_bits_arr[2] >> 4) & 0x01);\n        break;\n    }\n    case 0x01: {\n        AC = ((access_bits_arr[1] >> 3) & 0x04) | ((access_bits_arr[2] >> 0) & 0x02) |\n             ((access_bits_arr[2] >> 5) & 0x01);\n        break;\n    }\n    case 0x02: {\n        AC = ((access_bits_arr[1] >> 4) & 0x04) | ((access_bits_arr[2] >> 1) & 0x02) |\n             ((access_bits_arr[2] >> 6) & 0x01);\n        break;\n    }\n    default:\n        return false;\n    }\n\n    switch(action) {\n    case MfClassicActionDataRead: {\n        return (key_type == MfClassicKeyTypeA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) ||\n               (key_type == MfClassicKeyTypeB && !(AC == 0x07));\n    }\n    case MfClassicActionDataWrite: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x00)) ||\n               (key_type == MfClassicKeyTypeB &&\n                (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03));\n    }\n    case MfClassicActionDataInc: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x00)) ||\n               (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06));\n    }\n    case MfClassicActionDataDec: {\n        return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) ||\n               (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06 || AC == 0x01));\n    }\n    default:\n        return false;\n    }\n\n    return false;\n}\n\nbool mf_classic_is_allowed_access(\n    MfClassicData* data,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicAction action) {\n    furi_check(data);\n\n    bool access_allowed = false;\n    if(mf_classic_is_sector_trailer(block_num)) {\n        access_allowed =\n            mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action);\n    } else {\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);\n        access_allowed =\n            mf_classic_is_allowed_access_data_block(sec_tr, block_num, key_type, action);\n    }\n\n    return access_allowed;\n}\n\nbool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num) {\n    furi_check(sec_tr);\n\n    // Check if key A can write, if it can, it's transport configuration, not data block\n    return !mf_classic_is_allowed_access_data_block(\n               sec_tr, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) &&\n           (mf_classic_is_allowed_access_data_block(\n                sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) ||\n            mf_classic_is_allowed_access_data_block(\n                sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataDec));\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_CLASSIC_CMD_AUTH_KEY_A          (0x60U)\n#define MF_CLASSIC_CMD_AUTH_KEY_B          (0x61U)\n#define MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_A (0x64U)\n#define MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B (0x65U)\n#define MF_CLASSIC_CMD_READ_BLOCK          (0x30U)\n#define MF_CLASSIC_CMD_WRITE_BLOCK         (0xA0U)\n#define MF_CLASSIC_CMD_VALUE_DEC           (0xC0U)\n#define MF_CLASSIC_CMD_VALUE_INC           (0xC1U)\n#define MF_CLASSIC_CMD_VALUE_RESTORE       (0xC2U)\n#define MF_CLASSIC_CMD_VALUE_TRANSFER      (0xB0U)\n\n#define MF_CLASSIC_CMD_HALT_MSB                (0x50)\n#define MF_CLASSIC_CMD_HALT_LSB                (0x00)\n#define MF_CLASSIC_CMD_ACK                     (0x0A)\n#define MF_CLASSIC_CMD_NACK                    (0x00)\n#define MF_CLASSIC_CMD_NACK_TRANSFER_INVALID   (0x04)\n#define MF_CLASSIC_CMD_NACK_TRANSFER_CRC_ERROR (0x01)\n\n#define MF_CLASSIC_TOTAL_SECTORS_MAX (40)\n#define MF_CLASSIC_TOTAL_BLOCKS_MAX  (256)\n#define MF_CLASSIC_READ_MASK_SIZE    (MF_CLASSIC_TOTAL_BLOCKS_MAX / 32)\n#define MF_CLASSIC_BLOCK_SIZE        (16)\n#define MF_CLASSIC_KEY_SIZE          (6)\n#define MF_CLASSIC_ACCESS_BYTES_SIZE (4)\n\n#define MF_CLASSIC_NT_SIZE (4)\n#define MF_CLASSIC_NR_SIZE (4)\n#define MF_CLASSIC_AR_SIZE (4)\n#define MF_CLASSIC_AT_SIZE (4)\n\ntypedef enum {\n    MfClassicErrorNone,\n    MfClassicErrorNotPresent,\n    MfClassicErrorProtocol,\n    MfClassicErrorAuth,\n    MfClassicErrorPartialRead,\n    MfClassicErrorTimeout,\n} MfClassicError;\n\ntypedef enum {\n    MfClassicTypeMini,\n    MfClassicType1k,\n    MfClassicType4k,\n\n    MfClassicTypeNum,\n} MfClassicType;\n\ntypedef enum {\n    MfClassicActionDataRead,\n    MfClassicActionDataWrite,\n    MfClassicActionDataInc,\n    MfClassicActionDataDec,\n\n    MfClassicActionKeyARead,\n    MfClassicActionKeyAWrite,\n    MfClassicActionKeyBRead,\n    MfClassicActionKeyBWrite,\n    MfClassicActionACRead,\n    MfClassicActionACWrite,\n} MfClassicAction;\n\ntypedef enum {\n    MfClassicValueCommandIncrement,\n    MfClassicValueCommandDecrement,\n    MfClassicValueCommandRestore,\n\n    MfClassicValueCommandInvalid,\n} MfClassicValueCommand;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_BLOCK_SIZE];\n} MfClassicBlock;\n\ntypedef enum {\n    MfClassicKeyTypeA,\n    MfClassicKeyTypeB,\n} MfClassicKeyType;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_KEY_SIZE];\n} MfClassicKey;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_ACCESS_BYTES_SIZE];\n} MfClassicAccessBits;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_NT_SIZE];\n} MfClassicNt;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_AT_SIZE];\n} MfClassicAt;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_NR_SIZE];\n} MfClassicNr;\n\ntypedef struct {\n    uint8_t data[MF_CLASSIC_AR_SIZE];\n} MfClassicAr;\n\ntypedef struct {\n    uint8_t block_num;\n    MfClassicKey key;\n    MfClassicKeyType key_type;\n    MfClassicNt nt;\n    MfClassicNr nr;\n    MfClassicAr ar;\n    MfClassicAt at;\n} MfClassicAuthContext;\n\ntypedef union {\n    MfClassicBlock block;\n    struct {\n        MfClassicKey key_a;\n        MfClassicAccessBits access_bits;\n        MfClassicKey key_b;\n    };\n} MfClassicSectorTrailer;\n\ntypedef struct {\n    uint64_t key_a_mask;\n    MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX];\n    uint64_t key_b_mask;\n    MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX];\n} MfClassicDeviceKeys;\n\ntypedef struct {\n    Iso14443_3aData* iso14443_3a_data;\n    MfClassicType type;\n    uint32_t block_read_mask[MF_CLASSIC_READ_MASK_SIZE];\n    uint64_t key_a_mask;\n    uint64_t key_b_mask;\n    MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX];\n} MfClassicData;\n\nextern const NfcDeviceBase nfc_device_mf_classic;\n\nMfClassicData* mf_classic_alloc(void);\n\nvoid mf_classic_free(MfClassicData* data);\n\nvoid mf_classic_reset(MfClassicData* data);\n\nvoid mf_classic_copy(MfClassicData* data, const MfClassicData* other);\n\nbool mf_classic_verify(MfClassicData* data, const FuriString* device_type);\n\nbool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version);\n\nbool mf_classic_save(const MfClassicData* data, FlipperFormat* ff);\n\nbool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other);\n\nconst char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len);\n\nbool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3aData* mf_classic_get_base_data(const MfClassicData* data);\n\nuint8_t mf_classic_get_total_sectors_num(MfClassicType type);\n\nuint16_t mf_classic_get_total_block_num(MfClassicType type);\n\nuint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector);\n\nuint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector);\n\nuint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector);\n\nuint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block);\n\nMfClassicSectorTrailer*\n    mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num);\n\nbool mf_classic_is_sector_trailer(uint8_t block);\n\nvoid mf_classic_set_sector_trailer_read(\n    MfClassicData* data,\n    uint8_t block_num,\n    MfClassicSectorTrailer* sec_tr);\n\nuint8_t mf_classic_get_sector_by_block(uint8_t block);\n\nbool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr);\n\nvoid mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block);\n\nbool mf_classic_is_key_found(\n    const MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type);\n\nvoid mf_classic_set_key_found(\n    MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type,\n    uint64_t key);\n\nvoid mf_classic_set_key_not_found(\n    MfClassicData* data,\n    uint8_t sector_num,\n    MfClassicKeyType key_type);\n\nMfClassicKey\n    mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type);\n\nbool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num);\n\nvoid mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);\n\nbool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num);\n\nvoid mf_classic_get_read_sectors_and_keys(\n    const MfClassicData* data,\n    uint8_t* sectors_read,\n    uint8_t* keys_found);\n\nbool mf_classic_is_card_read(const MfClassicData* data);\n\nbool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num);\n\nbool mf_classic_is_allowed_access_data_block(\n    MfClassicSectorTrailer* sec_tr,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicAction action);\n\nbool mf_classic_is_allowed_access(\n    MfClassicData* data,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicAction action);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_listener.c",
    "content": "#include \"mf_classic_listener_i.h\"\n\n#include <nfc/protocols/nfc_listener_base.h>\n\n#include <nfc/helpers/iso14443_crc.h>\n#include <bit_lib/bit_lib.h>\n\n#include <furi.h>\n#include <furi_hal_random.h>\n\n#define TAG \"MfClassicListener\"\n\n#define MF_CLASSIC_MAX_BUFF_SIZE (64)\n\ntypedef MfClassicListenerCommand (\n    *MfClassicListenerCommandHandler)(MfClassicListener* instance, BitBuffer* buf);\n\ntypedef struct {\n    uint8_t cmd_start_byte;\n    size_t cmd_len_bits;\n    size_t command_num;\n    const MfClassicListenerCommandHandler* handler;\n} MfClassicListenerCmd;\n\nstatic void mf_classic_listener_prepare_emulation(MfClassicListener* instance) {\n    instance->total_block_num = mf_classic_get_total_block_num(instance->data->type);\n}\n\nstatic void mf_classic_listener_reset_state(MfClassicListener* instance) {\n    crypto1_reset(instance->crypto);\n    memset(&instance->auth_context, 0, sizeof(MfClassicAuthContext));\n    instance->comm_state = MfClassicListenerCommStatePlain;\n    instance->state = MfClassicListenerStateIdle;\n    instance->cmd_in_progress = false;\n    instance->current_cmd_handler_idx = 0;\n    instance->write_block = 0;\n    instance->transfer_value = 0;\n    instance->transfer_valid = false;\n    instance->value_cmd = MfClassicValueCommandInvalid;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) {\n    UNUSED(instance);\n\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n\n    if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) {\n        command = MfClassicListenerCommandSleep;\n    }\n\n    return command;\n}\n\nstatic MfClassicListenerCommand mf_classic_listener_auth_first_part_handler(\n    MfClassicListener* instance,\n    MfClassicKeyType key_type,\n    uint8_t block_num) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n\n    do {\n        instance->state = MfClassicListenerStateIdle;\n\n        if(block_num >= instance->total_block_num) break;\n\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n\n        MfClassicSectorTrailer* sec_tr =\n            mf_classic_get_sector_trailer_by_sector(instance->data, sector_num);\n        MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b;\n        uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));\n        uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);\n\n        instance->auth_context.key_type = key_type;\n        instance->auth_context.block_num = block_num;\n\n        furi_hal_random_fill_buf(instance->auth_context.nt.data, sizeof(MfClassicNt));\n        uint32_t nt_num =\n            bit_lib_bytes_to_num_be(instance->auth_context.nt.data, sizeof(MfClassicNt));\n\n        crypto1_init(instance->crypto, key_num);\n        if(instance->comm_state == MfClassicListenerCommStatePlain) {\n            crypto1_word(instance->crypto, nt_num ^ cuid, 0);\n            bit_buffer_copy_bytes(\n                instance->tx_encrypted_buffer,\n                instance->auth_context.nt.data,\n                sizeof(MfClassicNt));\n            iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_encrypted_buffer);\n            command = MfClassicListenerCommandProcessed;\n        } else {\n            uint8_t key_stream[4] = {};\n            bit_lib_num_to_bytes_be(nt_num ^ cuid, sizeof(uint32_t), key_stream);\n            bit_buffer_copy_bytes(\n                instance->tx_plain_buffer, instance->auth_context.nt.data, sizeof(MfClassicNt));\n            crypto1_encrypt(\n                instance->crypto,\n                key_stream,\n                instance->tx_plain_buffer,\n                instance->tx_encrypted_buffer);\n\n            iso14443_3a_listener_tx_with_custom_parity(\n                instance->iso14443_3a_listener, instance->tx_encrypted_buffer);\n\n            command = MfClassicListenerCommandProcessed;\n        }\n\n        instance->cmd_in_progress = true;\n        instance->current_cmd_handler_idx++;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_auth_key_a_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler(\n        instance, MfClassicKeyTypeA, bit_buffer_get_byte(buff, 1));\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_auth_key_b_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler(\n        instance, MfClassicKeyTypeB, bit_buffer_get_byte(buff, 1));\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_auth_second_part_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandSilent;\n\n    do {\n        instance->cmd_in_progress = false;\n\n        if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) {\n            command = MfClassicListenerCommandSleep;\n            break;\n        }\n        bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr));\n        bit_buffer_write_bytes_mid(\n            buff, instance->auth_context.ar.data, sizeof(MfClassicNr), sizeof(MfClassicAr));\n\n        if(instance->callback) {\n            instance->mfc_event.type = MfClassicListenerEventTypeAuthContextPartCollected,\n            instance->mfc_event_data.auth_context = instance->auth_context;\n            instance->callback(instance->generic_event, instance->context);\n        }\n\n        uint32_t nr_num =\n            bit_lib_bytes_to_num_be(instance->auth_context.nr.data, sizeof(MfClassicNr));\n        uint32_t ar_num =\n            bit_lib_bytes_to_num_be(instance->auth_context.ar.data, sizeof(MfClassicAr));\n\n        crypto1_word(instance->crypto, nr_num, 1);\n        uint32_t nt_num =\n            bit_lib_bytes_to_num_be(instance->auth_context.nt.data, sizeof(MfClassicNt));\n        uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0);\n        if(secret_poller != crypto1_prng_successor(nt_num, 64)) {\n            FURI_LOG_T(\n                TAG,\n                \"Wrong reader key: %08lX != %08lX\",\n                secret_poller,\n                crypto1_prng_successor(nt_num, 64));\n            command = MfClassicListenerCommandSleep;\n            break;\n        }\n\n        uint32_t at_num = crypto1_prng_successor(nt_num, 96);\n        bit_lib_num_to_bytes_be(at_num, sizeof(uint32_t), instance->auth_context.at.data);\n        bit_buffer_copy_bytes(\n            instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr));\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n        iso14443_3a_listener_tx_with_custom_parity(\n            instance->iso14443_3a_listener, instance->tx_encrypted_buffer);\n\n        instance->state = MfClassicListenerStateAuthComplete;\n        instance->comm_state = MfClassicListenerCommStateEncrypted;\n        command = MfClassicListenerCommandProcessed;\n\n        if(instance->callback) {\n            instance->mfc_event.type = MfClassicListenerEventTypeAuthContextFullCollected,\n            instance->mfc_event_data.auth_context = instance->auth_context;\n            instance->callback(instance->generic_event, instance->context);\n        }\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_read_block_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n    MfClassicAuthContext* auth_ctx = &instance->auth_context;\n\n    do {\n        if(instance->state != MfClassicListenerStateAuthComplete) break;\n\n        uint8_t block_num = bit_buffer_get_byte(buff, 1);\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);\n        if(sector_num != auth_sector_num) break;\n\n        MfClassicBlock access_block = instance->data->block[block_num];\n\n        if(mf_classic_is_sector_trailer(block_num)) {\n            MfClassicSectorTrailer* access_sec_tr = (MfClassicSectorTrailer*)&access_block;\n            if(!mf_classic_is_allowed_access(\n                   instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyARead)) {\n                memset(access_sec_tr->key_a.data, 0, sizeof(MfClassicKey));\n            }\n            if(!mf_classic_is_allowed_access(\n                   instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyBRead)) {\n                memset(access_sec_tr->key_b.data, 0, sizeof(MfClassicKey));\n            }\n            if(!mf_classic_is_allowed_access(\n                   instance->data, block_num, auth_ctx->key_type, MfClassicActionACRead)) {\n                memset(access_sec_tr->access_bits.data, 0, sizeof(MfClassicAccessBits));\n            }\n        } else if(!mf_classic_is_allowed_access(\n                      instance->data, block_num, auth_ctx->key_type, MfClassicActionDataRead)) {\n            break;\n        }\n\n        bit_buffer_copy_bytes(\n            instance->tx_plain_buffer, access_block.data, sizeof(MfClassicBlock));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n        iso14443_3a_listener_tx_with_custom_parity(\n            instance->iso14443_3a_listener, instance->tx_encrypted_buffer);\n        command = MfClassicListenerCommandProcessed;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand mf_classic_listener_write_block_first_part_handler(\n    MfClassicListener* instance,\n    BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n    MfClassicAuthContext* auth_ctx = &instance->auth_context;\n\n    do {\n        if(instance->state != MfClassicListenerStateAuthComplete) break;\n\n        uint8_t block_num = bit_buffer_get_byte(buff, 1);\n        if(block_num >= instance->total_block_num) break;\n        if(block_num == 0) break;\n\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);\n        if(sector_num != auth_sector_num) break;\n\n        instance->write_block = block_num;\n        instance->cmd_in_progress = true;\n        instance->current_cmd_handler_idx++;\n        command = MfClassicListenerCommandAck;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand mf_classic_listener_write_block_second_part_handler(\n    MfClassicListener* instance,\n    BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n    MfClassicAuthContext* auth_ctx = &instance->auth_context;\n\n    do {\n        instance->cmd_in_progress = false;\n\n        size_t buff_size = bit_buffer_get_size_bytes(buff);\n        if(buff_size != sizeof(MfClassicBlock)) break;\n\n        uint8_t block_num = instance->write_block;\n        MfClassicKeyType key_type = auth_ctx->key_type;\n        MfClassicBlock block = instance->data->block[block_num];\n\n        if(mf_classic_is_sector_trailer(block_num)) {\n            MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)&block;\n\n            // Check if any writing is allowed\n            if(!mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionKeyAWrite) &&\n               !mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionKeyBWrite) &&\n               !mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionACWrite)) {\n                break;\n            }\n\n            if(mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionKeyAWrite)) {\n                bit_buffer_write_bytes_mid(buff, sec_tr->key_a.data, 0, sizeof(MfClassicKey));\n            }\n            if(mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionKeyBWrite)) {\n                bit_buffer_write_bytes_mid(buff, sec_tr->key_b.data, 10, sizeof(MfClassicKey));\n            }\n            if(mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionACWrite)) {\n                bit_buffer_write_bytes_mid(\n                    buff, sec_tr->access_bits.data, 6, sizeof(MfClassicAccessBits));\n            }\n        } else {\n            if(mf_classic_is_allowed_access(\n                   instance->data, block_num, key_type, MfClassicActionDataWrite)) {\n                bit_buffer_write_bytes_mid(buff, block.data, 0, sizeof(MfClassicBlock));\n            } else {\n                break;\n            }\n        }\n\n        instance->data->block[block_num] = block;\n        command = MfClassicListenerCommandAck;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_cmd_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n    MfClassicAuthContext* auth_ctx = &instance->auth_context;\n\n    do {\n        if(instance->state != MfClassicListenerStateAuthComplete) break;\n\n        uint8_t block_num = bit_buffer_get_byte(buff, 1);\n        if(block_num >= instance->total_block_num) break;\n\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);\n        if(sector_num != auth_sector_num) break;\n\n        uint8_t cmd = bit_buffer_get_byte(buff, 0);\n        MfClassicAction action = MfClassicActionDataDec;\n        if(cmd == MF_CLASSIC_CMD_VALUE_DEC) {\n            instance->value_cmd = MfClassicValueCommandDecrement;\n        } else if(cmd == MF_CLASSIC_CMD_VALUE_INC) {\n            instance->value_cmd = MfClassicValueCommandIncrement;\n            action = MfClassicActionDataInc;\n        } else if(cmd == MF_CLASSIC_CMD_VALUE_RESTORE) {\n            instance->value_cmd = MfClassicValueCommandRestore;\n        } else {\n            break;\n        }\n\n        if(!mf_classic_is_allowed_access(instance->data, block_num, auth_ctx->key_type, action)) {\n            break;\n        }\n\n        if(!mf_classic_block_to_value(\n               &instance->data->block[block_num], &instance->transfer_value, NULL)) {\n            break;\n        }\n\n        instance->transfer_valid = true;\n        instance->cmd_in_progress = true;\n        instance->current_cmd_handler_idx++;\n        command = MfClassicListenerCommandAck;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_dec_handler(MfClassicListener* instance, BitBuffer* buff) {\n    return mf_classic_listener_value_cmd_handler(instance, buff);\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_inc_handler(MfClassicListener* instance, BitBuffer* buff) {\n    return mf_classic_listener_value_cmd_handler(instance, buff);\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_restore_handler(MfClassicListener* instance, BitBuffer* buff) {\n    return mf_classic_listener_value_cmd_handler(instance, buff);\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_data_receive_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n\n    do {\n        if(bit_buffer_get_size_bytes(buff) != 4) break;\n\n        int32_t data;\n        bit_buffer_write_bytes_mid(buff, &data, 0, sizeof(data));\n\n        if(data < 0) {\n            data = -data;\n        }\n\n        if(instance->value_cmd == MfClassicValueCommandDecrement) {\n            data = -data;\n        } else if(instance->value_cmd == MfClassicValueCommandRestore) {\n            data = 0;\n        }\n\n        instance->transfer_value += data;\n        instance->transfer_valid = true;\n\n        instance->cmd_in_progress = true;\n        instance->current_cmd_handler_idx++;\n        command = MfClassicListenerCommandSilent;\n    } while(false);\n\n    return command;\n}\n\nstatic MfClassicListenerCommand\n    mf_classic_listener_value_transfer_handler(MfClassicListener* instance, BitBuffer* buff) {\n    MfClassicListenerCommand command = MfClassicListenerCommandNack;\n    MfClassicAuthContext* auth_ctx = &instance->auth_context;\n\n    do {\n        instance->cmd_in_progress = false;\n\n        if(bit_buffer_get_size_bytes(buff) != 2) break;\n        if(bit_buffer_get_byte(buff, 0) != MF_CLASSIC_CMD_VALUE_TRANSFER) break;\n\n        uint8_t block_num = bit_buffer_get_byte(buff, 1);\n        if(!mf_classic_is_allowed_access(\n               instance->data, block_num, auth_ctx->key_type, MfClassicActionDataDec)) {\n            break;\n        }\n\n        mf_classic_value_to_block(\n            instance->transfer_value, block_num, &instance->data->block[block_num]);\n        instance->transfer_value = 0;\n        instance->transfer_valid = false;\n\n        command = MfClassicListenerCommandAck;\n    } while(false);\n\n    return command;\n}\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = {\n    mf_classic_listener_halt_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = {\n    mf_classic_listener_auth_key_a_handler,\n    mf_classic_listener_auth_second_part_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = {\n    mf_classic_listener_auth_key_b_handler,\n    mf_classic_listener_auth_second_part_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = {\n    mf_classic_listener_read_block_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = {\n    mf_classic_listener_write_block_first_part_handler,\n    mf_classic_listener_write_block_second_part_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = {\n    mf_classic_listener_value_dec_handler,\n    mf_classic_listener_value_data_receive_handler,\n    mf_classic_listener_value_transfer_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = {\n    mf_classic_listener_value_inc_handler,\n    mf_classic_listener_value_data_receive_handler,\n    mf_classic_listener_value_transfer_handler,\n};\n\nstatic const MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = {\n    mf_classic_listener_value_restore_handler,\n    mf_classic_listener_value_data_receive_handler,\n    mf_classic_listener_value_transfer_handler,\n};\n\nstatic const MfClassicListenerCmd mf_classic_listener_cmd_handlers[] = {\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_HALT_MSB,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_halt_handlers),\n        .handler = mf_classic_listener_halt_handlers,\n    },\n    {\n        // This crutch is necessary since some devices (like Pixel) send 15-bit \"HALT\" command ...\n        .cmd_start_byte = MF_CLASSIC_CMD_HALT_MSB,\n        .cmd_len_bits = 15,\n        .command_num = COUNT_OF(mf_classic_listener_halt_handlers),\n        .handler = mf_classic_listener_halt_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_A,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_auth_key_a_handlers),\n        .handler = mf_classic_listener_auth_key_a_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_B,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_auth_key_b_handlers),\n        .handler = mf_classic_listener_auth_key_b_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_READ_BLOCK,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_read_block_handlers),\n        .handler = mf_classic_listener_read_block_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_WRITE_BLOCK,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_write_block_handlers),\n        .handler = mf_classic_listener_write_block_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_VALUE_DEC,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_value_dec_handlers),\n        .handler = mf_classic_listener_value_dec_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_VALUE_INC,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_value_inc_handlers),\n        .handler = mf_classic_listener_value_inc_handlers,\n    },\n    {\n        .cmd_start_byte = MF_CLASSIC_CMD_VALUE_RESTORE,\n        .cmd_len_bits = 2 * 8,\n        .command_num = COUNT_OF(mf_classic_listener_value_restore_handlers),\n        .handler = mf_classic_listener_value_restore_handlers,\n    },\n};\n\nstatic void mf_classic_listener_send_short_frame(MfClassicListener* instance, uint8_t data) {\n    BitBuffer* tx_buffer = instance->tx_plain_buffer;\n\n    bit_buffer_set_size(instance->tx_plain_buffer, 4);\n    bit_buffer_set_byte(instance->tx_plain_buffer, 0, data);\n    if(instance->comm_state == MfClassicListenerCommStateEncrypted) {\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n        tx_buffer = instance->tx_encrypted_buffer;\n    }\n\n    iso14443_3a_listener_tx_with_custom_parity(instance->iso14443_3a_listener, tx_buffer);\n}\n\nNfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    NfcCommand command = NfcCommandContinue;\n    MfClassicListener* instance = context;\n    Iso14443_3aListenerEvent* iso3_event = event.event_data;\n    BitBuffer* rx_buffer_plain;\n\n    if(iso3_event->type == Iso14443_3aListenerEventTypeFieldOff) {\n        mf_classic_listener_reset_state(instance);\n        command = NfcCommandSleep;\n    } else if(\n        (iso3_event->type == Iso14443_3aListenerEventTypeReceivedData) ||\n        (iso3_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame)) {\n        if(instance->comm_state == MfClassicListenerCommStateEncrypted) {\n            if(instance->state == MfClassicListenerStateAuthComplete) {\n                crypto1_decrypt(\n                    instance->crypto, iso3_event->data->buffer, instance->rx_plain_buffer);\n                rx_buffer_plain = instance->rx_plain_buffer;\n                if(iso14443_crc_check(Iso14443CrcTypeA, rx_buffer_plain)) {\n                    iso14443_crc_trim(rx_buffer_plain);\n                }\n            } else {\n                rx_buffer_plain = iso3_event->data->buffer;\n            }\n        } else {\n            rx_buffer_plain = iso3_event->data->buffer;\n        }\n\n        MfClassicListenerCommand mfc_command = MfClassicListenerCommandNack;\n        if(instance->cmd_in_progress) {\n            mfc_command =\n                mf_classic_listener_cmd_handlers[instance->current_cmd_idx]\n                    .handler[instance->current_cmd_handler_idx](instance, rx_buffer_plain);\n        } else {\n            for(size_t i = 0; i < COUNT_OF(mf_classic_listener_cmd_handlers); i++) {\n                if(bit_buffer_get_size(rx_buffer_plain) !=\n                   mf_classic_listener_cmd_handlers[i].cmd_len_bits) {\n                    continue;\n                }\n                if(bit_buffer_get_byte(rx_buffer_plain, 0) !=\n                   mf_classic_listener_cmd_handlers[i].cmd_start_byte) {\n                    continue;\n                }\n                instance->current_cmd_idx = i;\n                instance->current_cmd_handler_idx = 0;\n                mfc_command =\n                    mf_classic_listener_cmd_handlers[i].handler[0](instance, rx_buffer_plain);\n                break;\n            }\n        }\n\n        if(mfc_command == MfClassicListenerCommandAck) {\n            mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK);\n        } else if(mfc_command == MfClassicListenerCommandNack) {\n            // Calculate nack based on the transfer buffer validity\n            uint8_t nack = MF_CLASSIC_CMD_NACK;\n            if(!instance->transfer_valid) {\n                nack += MF_CLASSIC_CMD_NACK_TRANSFER_INVALID;\n            }\n\n            mf_classic_listener_send_short_frame(instance, nack);\n            mf_classic_listener_reset_state(instance);\n            command = NfcCommandSleep;\n        } else if(mfc_command == MfClassicListenerCommandSilent) {\n            command = NfcCommandReset;\n        } else if(mfc_command == MfClassicListenerCommandSleep) {\n            mf_classic_listener_reset_state(instance);\n            command = NfcCommandSleep;\n        }\n    } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) {\n        mf_classic_listener_reset_state(instance);\n    }\n\n    return command;\n}\n\nMfClassicListener*\n    mf_classic_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, MfClassicData* data) {\n    MfClassicListener* instance = malloc(sizeof(MfClassicListener));\n    instance->iso14443_3a_listener = iso14443_3a_listener;\n    instance->data = data;\n    mf_classic_listener_prepare_emulation(instance);\n\n    instance->crypto = crypto1_alloc();\n    instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n\n    instance->mfc_event.data = &instance->mfc_event_data;\n    instance->generic_event.protocol = NfcProtocolMfClassic;\n    instance->generic_event.event_data = &instance->mfc_event;\n    instance->generic_event.instance = instance;\n\n    return instance;\n}\n\nvoid mf_classic_listener_free(MfClassicListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->crypto);\n    furi_assert(instance->rx_plain_buffer);\n    furi_assert(instance->tx_encrypted_buffer);\n    furi_assert(instance->tx_plain_buffer);\n\n    crypto1_free(instance->crypto);\n    bit_buffer_free(instance->rx_plain_buffer);\n    bit_buffer_free(instance->tx_encrypted_buffer);\n    bit_buffer_free(instance->tx_plain_buffer);\n\n    free(instance);\n}\n\nvoid mf_classic_listener_set_callback(\n    MfClassicListener* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nconst MfClassicData* mf_classic_listener_get_data(const MfClassicListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nconst NfcListenerBase mf_classic_listener = {\n    .alloc = (NfcListenerAlloc)mf_classic_listener_alloc,\n    .free = (NfcListenerFree)mf_classic_listener_free,\n    .set_callback = (NfcListenerSetCallback)mf_classic_listener_set_callback,\n    .get_data = (NfcListenerGetData)mf_classic_listener_get_data,\n    .run = (NfcListenerRun)mf_classic_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_listener.h",
    "content": "#pragma once\n\n#include \"mf_classic.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct MfClassicListener MfClassicListener;\n\ntypedef enum {\n    MfClassicListenerEventTypeAuthContextPartCollected,\n    MfClassicListenerEventTypeAuthContextFullCollected,\n} MfClassicListenerEventType;\n\ntypedef union {\n    MfClassicAuthContext auth_context;\n} MfClassicListenerEventData;\n\ntypedef struct {\n    MfClassicListenerEventType type;\n    MfClassicListenerEventData* data;\n} MfClassicListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcListenerBase mf_classic_listener;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_listener_i.h",
    "content": "#pragma once\n\n#include \"mf_classic_listener.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>\n#include <nfc/protocols/nfc_generic_event.h>\n#include <nfc/helpers/crypto1.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    MfClassicListenerCommandProcessed,\n    MfClassicListenerCommandAck,\n    MfClassicListenerCommandNack,\n    MfClassicListenerCommandSilent,\n    MfClassicListenerCommandSleep,\n} MfClassicListenerCommand;\n\ntypedef enum {\n    MfClassicListenerStateIdle,\n    MfClassicListenerStateAuthComplete,\n} MfClassicListenerState;\n\ntypedef enum {\n    MfClassicListenerCommStatePlain,\n    MfClassicListenerCommStateEncrypted,\n} MfClassicListenerCommState;\n\nstruct MfClassicListener {\n    Iso14443_3aListener* iso14443_3a_listener;\n    MfClassicListenerState state;\n    MfClassicListenerCommState comm_state;\n\n    MfClassicData* data;\n    BitBuffer* tx_plain_buffer;\n    BitBuffer* tx_encrypted_buffer;\n    BitBuffer* rx_plain_buffer;\n\n    Crypto1* crypto;\n    MfClassicAuthContext auth_context;\n\n    // Write block context\n    uint8_t write_block;\n\n    // Value operation data\n    int32_t transfer_value;\n    bool transfer_valid;\n    MfClassicValueCommand value_cmd;\n\n    NfcGenericEvent generic_event;\n    MfClassicListenerEvent mfc_event;\n    MfClassicListenerEventData mfc_event_data;\n    NfcGenericCallback callback;\n    void* context;\n\n    bool cmd_in_progress;\n    size_t current_cmd_idx;\n    size_t current_cmd_handler_idx;\n\n    size_t total_block_num;\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller.c",
    "content": "#include \"mf_classic_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"MfClassicPoller\"\n\n// TODO FL-3926: Buffer writes for Hardnested, set state to Log when finished and sum property matches\n// TODO FL-3926: Store target key in CUID dictionary\n// TODO FL-3926: Dead code for malloc returning NULL?\n// TODO FL-3926: Auth1 static encrypted exists (rare)\n// TODO FL-3926: Use keys found by NFC plugins, cached keys\n\n#define MF_CLASSIC_MAX_BUFF_SIZE (64)\n\n// Ordered by frequency, labeled chronologically\nconst MfClassicBackdoorKeyPair mf_classic_backdoor_keys[] = {\n    {{{0xa3, 0x96, 0xef, 0xa4, 0xe2, 0x4f}}, MfClassicBackdoorAuth3}, // Fudan (static encrypted)\n    {{{0xa3, 0x16, 0x67, 0xa8, 0xce, 0xc1}}, MfClassicBackdoorAuth1}, // Fudan, Infineon, NXP\n    {{{0x51, 0x8b, 0x33, 0x54, 0xe7, 0x60}}, MfClassicBackdoorAuth2}, // Fudan\n};\nconst size_t mf_classic_backdoor_keys_count = COUNT_OF(mf_classic_backdoor_keys);\nconst uint16_t valid_sums[] =\n    {0, 32, 56, 64, 80, 96, 104, 112, 120, 128, 136, 144, 152, 160, 176, 192, 200, 224, 256};\n\ntypedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance);\n\nMfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {\n    furi_assert(iso14443_3a_poller);\n\n    MfClassicPoller* instance = malloc(sizeof(MfClassicPoller));\n    instance->iso14443_3a_poller = iso14443_3a_poller;\n    instance->data = mf_classic_alloc();\n    instance->crypto = crypto1_alloc();\n    instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);\n    instance->current_type_check = MfClassicType4k;\n    instance->card_state = MfClassicCardStateLost;\n\n    instance->mfc_event.data = &instance->mfc_event_data;\n\n    instance->general_event.protocol = NfcProtocolMfClassic;\n    instance->general_event.event_data = &instance->mfc_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nvoid mf_classic_poller_free(MfClassicPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->crypto);\n    furi_assert(instance->tx_plain_buffer);\n    furi_assert(instance->rx_plain_buffer);\n    furi_assert(instance->tx_encrypted_buffer);\n    furi_assert(instance->rx_encrypted_buffer);\n\n    mf_classic_free(instance->data);\n    crypto1_free(instance->crypto);\n    bit_buffer_free(instance->tx_plain_buffer);\n    bit_buffer_free(instance->rx_plain_buffer);\n    bit_buffer_free(instance->tx_encrypted_buffer);\n    bit_buffer_free(instance->rx_encrypted_buffer);\n\n    // Clean up resources in MfClassicPollerDictAttackContext\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    // Free the dictionaries\n    if(dict_attack_ctx->mf_classic_system_dict) {\n        keys_dict_free(dict_attack_ctx->mf_classic_system_dict);\n        dict_attack_ctx->mf_classic_system_dict = NULL;\n    }\n    if(dict_attack_ctx->mf_classic_user_dict) {\n        keys_dict_free(dict_attack_ctx->mf_classic_user_dict);\n        dict_attack_ctx->mf_classic_user_dict = NULL;\n    }\n\n    // Free the nested nonce array if it exists\n    if(dict_attack_ctx->nested_nonce.nonces) {\n        free(dict_attack_ctx->nested_nonce.nonces);\n        dict_attack_ctx->nested_nonce.nonces = NULL;\n        dict_attack_ctx->nested_nonce.count = 0;\n    }\n\n    free(instance);\n}\n\nstatic NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) {\n    MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update;\n\n    mf_classic_get_read_sectors_and_keys(\n        instance->data, &data_update->sectors_read, &data_update->keys_found);\n    data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector;\n    data_update->nested_phase = instance->mode_ctx.dict_attack_ctx.nested_phase;\n    data_update->prng_type = instance->mode_ctx.dict_attack_ctx.prng_type;\n    data_update->backdoor = instance->mode_ctx.dict_attack_ctx.backdoor;\n    data_update->nested_target_key = instance->mode_ctx.dict_attack_ctx.nested_target_key;\n    data_update->msb_count = instance->mode_ctx.dict_attack_ctx.msb_count;\n    instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate;\n    return instance->callback(instance->general_event, instance->context);\n}\n\nstatic void mf_classic_poller_check_key_b_is_readable(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicBlock* data) {\n    do {\n        if(!mf_classic_is_sector_trailer(block_num)) break;\n        if(!mf_classic_is_allowed_access(\n               instance->data, block_num, MfClassicKeyTypeA, MfClassicActionKeyBRead))\n            break;\n\n        MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data;\n        uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, sizeof(MfClassicKey));\n        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);\n        mf_classic_set_key_found(instance->data, sector_num, MfClassicKeyTypeB, key_b);\n    } while(false);\n}\n\nNfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandReset;\n\n    if(instance->current_type_check == MfClassicType4k) {\n        iso14443_3a_copy(\n            instance->data->iso14443_3a_data,\n            iso14443_3a_poller_get_data(instance->iso14443_3a_poller));\n        MfClassicError error =\n            mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL, false);\n        if(error == MfClassicErrorNone) {\n            instance->data->type = MfClassicType4k;\n            instance->state = MfClassicPollerStateStart;\n            instance->current_type_check = MfClassicType4k;\n            FURI_LOG_D(TAG, \"4K detected\");\n        } else {\n            instance->current_type_check = MfClassicType1k;\n        }\n    } else if(instance->current_type_check == MfClassicType1k) {\n        MfClassicError error =\n            mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL, false);\n        if(error == MfClassicErrorNone) {\n            instance->data->type = MfClassicType1k;\n            FURI_LOG_D(TAG, \"1K detected\");\n        } else {\n            instance->data->type = MfClassicTypeMini;\n            FURI_LOG_D(TAG, \"Mini detected\");\n        }\n        instance->current_type_check = MfClassicType4k;\n        instance->state = MfClassicPollerStateStart;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type);\n    memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext));\n\n    instance->mfc_event.type = MfClassicPollerEventTypeRequestMode;\n    command = instance->callback(instance->general_event, instance->context);\n\n    if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackStandard) {\n        mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);\n        instance->state = MfClassicPollerStateRequestKey;\n    } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackEnhanced) {\n        mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);\n        instance->state = MfClassicPollerStateAnalyzeBackdoor;\n    } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) {\n        instance->state = MfClassicPollerStateRequestReadSector;\n    } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) {\n        instance->state = MfClassicPollerStateRequestSectorTrailer;\n    } else {\n        furi_crash(\"Invalid mode selected\");\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;\n\n    if(write_ctx->current_sector == instance->sectors_total) {\n        instance->state = MfClassicPollerStateSuccess;\n    } else {\n        instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer;\n        instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector;\n        command = instance->callback(instance->general_event, instance->context);\n        if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) {\n            instance->state = MfClassicPollerStateCheckWriteConditions;\n            memcpy(\n                &write_ctx->sec_tr,\n                &instance->mfc_event_data.sec_tr_data.sector_trailer,\n                sizeof(MfClassicSectorTrailer));\n            write_ctx->current_block =\n                MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector));\n\n        } else {\n            write_ctx->current_sector++;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;\n    MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr;\n\n    do {\n        // Check last block in sector to write\n        uint8_t sec_tr_block_num =\n            mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector);\n        if(write_ctx->current_block == sec_tr_block_num) {\n            write_ctx->current_sector++;\n            instance->state = MfClassicPollerStateRequestSectorTrailer;\n            break;\n        }\n\n        // Check write and read access\n        if(mf_classic_is_allowed_access_data_block(\n               sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) {\n            write_ctx->key_type_write = MfClassicKeyTypeA;\n        } else if(mf_classic_is_allowed_access_data_block(\n                      sec_tr,\n                      write_ctx->current_block,\n                      MfClassicKeyTypeB,\n                      MfClassicActionDataWrite)) {\n            write_ctx->key_type_write = MfClassicKeyTypeB;\n        } else if(mf_classic_is_value_block(sec_tr, write_ctx->current_block)) {\n            write_ctx->is_value_block = true;\n        } else {\n            FURI_LOG_D(TAG, \"Not allowed to write block %d\", write_ctx->current_block);\n            write_ctx->current_block++;\n            break;\n        }\n\n        if(mf_classic_is_allowed_access_data_block(\n               sec_tr,\n               write_ctx->current_block,\n               write_ctx->key_type_write,\n               MfClassicActionDataRead)) {\n            write_ctx->key_type_read = write_ctx->key_type_write;\n        } else {\n            write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ?\n                                           MfClassicKeyTypeB :\n                                           MfClassicKeyTypeA;\n            if(!mf_classic_is_allowed_access_data_block(\n                   sec_tr,\n                   write_ctx->current_block,\n                   write_ctx->key_type_read,\n                   MfClassicActionDataRead)) {\n                FURI_LOG_D(TAG, \"Not allowed to read block %d\", write_ctx->current_block);\n                write_ctx->current_block++;\n                break;\n            }\n        }\n\n        write_ctx->need_halt_before_write =\n            (write_ctx->key_type_read != write_ctx->key_type_write);\n        instance->state = MfClassicPollerStateReadBlock;\n    } while(false);\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;\n\n    MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ?\n                                 &write_ctx->sec_tr.key_a :\n                                 &write_ctx->sec_tr.key_b;\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        // Authenticate to sector\n        error = mf_classic_poller_auth(\n            instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL, false);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to auth to block %d\", write_ctx->current_block);\n            instance->state = MfClassicPollerStateFail;\n            break;\n        }\n\n        // Read block from tag\n        error = mf_classic_poller_read_block(\n            instance, write_ctx->current_block, &write_ctx->tag_block);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read block %d\", write_ctx->current_block);\n            instance->state = MfClassicPollerStateFail;\n            break;\n        }\n\n        if(write_ctx->is_value_block) {\n            mf_classic_poller_halt(instance);\n            instance->state = MfClassicPollerStateWriteValueBlock;\n        } else {\n            if(write_ctx->need_halt_before_write) {\n                mf_classic_poller_halt(instance);\n            }\n            instance->state = MfClassicPollerStateWriteBlock;\n        }\n    } while(false);\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;\n    MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ?\n                                 &write_ctx->sec_tr.key_a :\n                                 &write_ctx->sec_tr.key_b;\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        // Request block to write\n        instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;\n        instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;\n        command = instance->callback(instance->general_event, instance->context);\n        if(!instance->mfc_event_data.write_block_data.write_block_provided) break;\n\n        // Compare tag and saved block\n        if(memcmp(\n               write_ctx->tag_block.data,\n               instance->mfc_event_data.write_block_data.write_block.data,\n               sizeof(MfClassicBlock)) == 0) {\n            FURI_LOG_D(TAG, \"Block %d is equal. Skip writing\", write_ctx->current_block);\n            break;\n        }\n\n        // Reauth if necessary\n        if(write_ctx->need_halt_before_write) {\n            error = mf_classic_poller_auth(\n                instance,\n                write_ctx->current_block,\n                auth_key,\n                write_ctx->key_type_write,\n                NULL,\n                false);\n            if(error != MfClassicErrorNone) {\n                FURI_LOG_D(\n                    TAG, \"Failed to auth to block %d for writing\", write_ctx->current_block);\n                instance->state = MfClassicPollerStateFail;\n                break;\n            }\n        }\n\n        // Write block\n        error = mf_classic_poller_write_block(\n            instance,\n            write_ctx->current_block,\n            &instance->mfc_event_data.write_block_data.write_block);\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to write block %d\", write_ctx->current_block);\n            instance->state = MfClassicPollerStateFail;\n            break;\n        }\n\n    } while(false);\n\n    mf_classic_poller_halt(instance);\n    write_ctx->current_block++;\n    instance->state = MfClassicPollerStateCheckWriteConditions;\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;\n\n    do {\n        // Request block to write\n        instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;\n        instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;\n        command = instance->callback(instance->general_event, instance->context);\n        if(!instance->mfc_event_data.write_block_data.write_block_provided) break;\n\n        // Compare tag and saved block\n        if(memcmp(\n               write_ctx->tag_block.data,\n               instance->mfc_event_data.write_block_data.write_block.data,\n               sizeof(MfClassicBlock)) == 0) {\n            FURI_LOG_D(TAG, \"Block %d is equal. Skip writing\", write_ctx->current_block);\n            break;\n        }\n\n        bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block(\n            &write_ctx->sec_tr,\n            write_ctx->current_block,\n            MfClassicKeyTypeA,\n            MfClassicActionDataInc);\n        bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block(\n            &write_ctx->sec_tr,\n            write_ctx->current_block,\n            MfClassicKeyTypeB,\n            MfClassicActionDataInc);\n        bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block(\n            &write_ctx->sec_tr,\n            write_ctx->current_block,\n            MfClassicKeyTypeA,\n            MfClassicActionDataDec);\n        bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block(\n            &write_ctx->sec_tr,\n            write_ctx->current_block,\n            MfClassicKeyTypeB,\n            MfClassicActionDataDec);\n\n        int32_t source_value = 0;\n        int32_t target_value = 0;\n        if(!mf_classic_block_to_value(\n               &instance->mfc_event_data.write_block_data.write_block, &source_value, NULL))\n            break;\n        if(!mf_classic_block_to_value(&write_ctx->tag_block, &target_value, NULL)) break;\n\n        MfClassicKeyType auth_key_type = MfClassicKeyTypeA;\n        MfClassicValueCommand value_cmd = MfClassicValueCommandIncrement;\n        int32_t diff = source_value - target_value;\n        if(diff > 0) {\n            if(key_a_inc_allowed) {\n                auth_key_type = MfClassicKeyTypeA;\n                value_cmd = MfClassicValueCommandIncrement;\n            } else if(key_b_inc_allowed) {\n                auth_key_type = MfClassicKeyTypeB;\n                value_cmd = MfClassicValueCommandIncrement;\n            } else {\n                FURI_LOG_D(TAG, \"Unable to increment value block\");\n                break;\n            }\n        } else {\n            if(key_a_dec_allowed) {\n                auth_key_type = MfClassicKeyTypeA;\n                value_cmd = MfClassicValueCommandDecrement;\n                diff *= -1;\n            } else if(key_b_dec_allowed) {\n                auth_key_type = MfClassicKeyTypeB;\n                value_cmd = MfClassicValueCommandDecrement;\n                diff *= -1;\n            } else {\n                FURI_LOG_D(TAG, \"Unable to decrement value block\");\n                break;\n            }\n        }\n\n        MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a :\n                                                                   &write_ctx->sec_tr.key_b;\n\n        MfClassicError error = mf_classic_poller_auth(\n            instance, write_ctx->current_block, key, auth_key_type, NULL, false);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_value_cmd(instance, write_ctx->current_block, value_cmd, diff);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_value_transfer(instance, write_ctx->current_block);\n        if(error != MfClassicErrorNone) break;\n\n    } while(false);\n\n    mf_classic_poller_halt(instance);\n    write_ctx->is_value_block = false;\n    write_ctx->current_block++;\n    instance->state = MfClassicPollerStateCheckWriteConditions;\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;\n    MfClassicPollerEventDataReadSectorRequest* sec_read =\n        &instance->mfc_event_data.read_sector_request_data;\n    instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector;\n    command = instance->callback(instance->general_event, instance->context);\n\n    if(!sec_read->key_provided) {\n        instance->state = MfClassicPollerStateSuccess;\n    } else {\n        sec_read_ctx->current_sector = sec_read->sector_num;\n        sec_read_ctx->key = sec_read->key;\n        sec_read_ctx->key_type = sec_read->key_type;\n        sec_read_ctx->current_block =\n            mf_classic_get_first_block_num_of_sector(sec_read->sector_num);\n        sec_read_ctx->auth_passed = false;\n        instance->state = MfClassicPollerStateReadSectorBlocks;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;\n\n    do {\n        MfClassicError error = MfClassicErrorNone;\n\n        if(!sec_read_ctx->auth_passed) {\n            uint64_t key = bit_lib_bytes_to_num_be(sec_read_ctx->key.data, sizeof(MfClassicKey));\n            FURI_LOG_D(\n                TAG,\n                \"Auth to block %d with key %c: %06llx\",\n                sec_read_ctx->current_block,\n                sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B',\n                key);\n            error = mf_classic_poller_auth(\n                instance,\n                sec_read_ctx->current_block,\n                &sec_read_ctx->key,\n                sec_read_ctx->key_type,\n                NULL,\n                false);\n            if(error != MfClassicErrorNone) break;\n\n            sec_read_ctx->auth_passed = true;\n            if(!mf_classic_is_key_found(\n                   instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) {\n                mf_classic_set_key_found(\n                    instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key);\n            }\n        }\n        if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break;\n\n        FURI_LOG_D(TAG, \"Reading block %d\", sec_read_ctx->current_block);\n        MfClassicBlock read_block = {};\n        error = mf_classic_poller_read_block(instance, sec_read_ctx->current_block, &read_block);\n        if(error == MfClassicErrorNone) {\n            mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block);\n            if(sec_read_ctx->key_type == MfClassicKeyTypeA) {\n                mf_classic_poller_check_key_b_is_readable(\n                    instance, sec_read_ctx->current_block, &read_block);\n            }\n        } else {\n            mf_classic_poller_halt(instance);\n            sec_read_ctx->auth_passed = false;\n        }\n    } while(false);\n\n    uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector);\n    sec_read_ctx->current_block++;\n    if(sec_read_ctx->current_block > sec_tr_num) {\n        mf_classic_poller_halt(instance);\n        instance->state = MfClassicPollerStateRequestReadSector;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_analyze_backdoor(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandReset;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    instance->mode_ctx.dict_attack_ctx.enhanced_dict = true;\n\n    size_t current_key_index =\n        mf_classic_backdoor_keys_count - 1; // Default to the last valid index\n\n    // Find the current key in the backdoor_keys array\n    for(size_t i = 0; i < mf_classic_backdoor_keys_count; i++) {\n        if(memcmp(\n               dict_attack_ctx->current_key.data,\n               mf_classic_backdoor_keys[i].key.data,\n               sizeof(MfClassicKey)) == 0) {\n            current_key_index = i;\n            break;\n        }\n    }\n\n    // Choose the next key to try\n    size_t next_key_index = (current_key_index + 1) % mf_classic_backdoor_keys_count;\n    uint8_t backdoor_version = mf_classic_backdoor_keys[next_key_index].type - 1;\n\n    FURI_LOG_D(TAG, \"Trying backdoor v%d\", backdoor_version);\n    dict_attack_ctx->current_key = mf_classic_backdoor_keys[next_key_index].key;\n\n    // Attempt backdoor authentication\n    MfClassicError error = mf_classic_poller_auth(\n        instance, 0, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, true);\n    if((next_key_index == 0) &&\n       (error == MfClassicErrorProtocol || error == MfClassicErrorTimeout)) {\n        FURI_LOG_D(TAG, \"No backdoor identified\");\n        dict_attack_ctx->backdoor = MfClassicBackdoorNone;\n        instance->state = MfClassicPollerStateRequestKey;\n    } else if(error == MfClassicErrorNone) {\n        FURI_LOG_I(TAG, \"Backdoor identified: v%d\", backdoor_version);\n        dict_attack_ctx->backdoor = mf_classic_backdoor_keys[next_key_index].type;\n        instance->state = MfClassicPollerStateBackdoorReadSector;\n    } else if(\n        (error == MfClassicErrorAuth) &&\n        (next_key_index == (mf_classic_backdoor_keys_count - 1))) {\n        // We've tried all backdoor keys, this is a unique key and an important research finding\n        furi_crash(\"New backdoor: please report!\");\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_backdoor_read_sector(MfClassicPoller* instance) {\n    // TODO FL-3926: Reauth not needed\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    MfClassicError error = MfClassicErrorNone;\n    MfClassicBlock block = {};\n\n    uint8_t current_sector = mf_classic_get_sector_by_block(dict_attack_ctx->current_block);\n    uint8_t blocks_in_sector = mf_classic_get_blocks_num_in_sector(current_sector);\n    uint8_t first_block_in_sector = mf_classic_get_first_block_num_of_sector(current_sector);\n\n    do {\n        if(dict_attack_ctx->current_block >= instance->sectors_total * 4) {\n            // We've read all blocks, reset current_block and move to next state\n            dict_attack_ctx->current_block = 0;\n            instance->state = MfClassicPollerStateNestedController;\n            break;\n        }\n\n        // Authenticate with the backdoor key\n        error = mf_classic_poller_auth(\n            instance,\n            first_block_in_sector, // Authenticate to the first block of the sector\n            &(dict_attack_ctx->current_key),\n            MfClassicKeyTypeA,\n            NULL,\n            true);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_E(\n                TAG, \"Failed to authenticate with backdoor key for sector %d\", current_sector);\n            break;\n        }\n\n        // Read all blocks in the sector\n        for(uint8_t block_in_sector = 0; block_in_sector < blocks_in_sector; block_in_sector++) {\n            uint8_t block_to_read = first_block_in_sector + block_in_sector;\n\n            error = mf_classic_poller_read_block(instance, block_to_read, &block);\n\n            if(error != MfClassicErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to read block %d\", block_to_read);\n                break;\n            }\n\n            // Set the block as read in the data structure\n            mf_classic_set_block_read(instance->data, block_to_read, &block);\n        }\n\n        if(error != MfClassicErrorNone) {\n            break;\n        }\n\n        // Move to the next sector\n        current_sector++;\n        dict_attack_ctx->current_block = mf_classic_get_first_block_num_of_sector(current_sector);\n\n        // Update blocks_in_sector and first_block_in_sector for the next sector\n        if(current_sector < instance->sectors_total) {\n            blocks_in_sector = mf_classic_get_blocks_num_in_sector(current_sector);\n            first_block_in_sector = mf_classic_get_first_block_num_of_sector(current_sector);\n        }\n\n        // Halt the card after each sector to reset the authentication state\n        mf_classic_poller_halt(instance);\n\n        // Send an event to the app that a sector has been read\n        command = mf_classic_poller_handle_data_update(instance);\n\n    } while(false);\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    instance->mfc_event.type = MfClassicPollerEventTypeRequestKey;\n    command = instance->callback(instance->general_event, instance->context);\n    if(instance->mfc_event_data.key_request_data.key_provided) {\n        dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key;\n        instance->state = MfClassicPollerStateAuthKeyA;\n    } else {\n        instance->state = MfClassicPollerStateNextSector;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    if(mf_classic_is_key_found(\n           instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {\n        instance->state = MfClassicPollerStateAuthKeyB;\n    } else {\n        uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);\n        uint64_t key =\n            bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));\n        FURI_LOG_D(TAG, \"Auth to block %d with key A: %06llx\", block, key);\n\n        MfClassicError error = mf_classic_poller_auth(\n            instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);\n        if(error == MfClassicErrorNone) {\n            FURI_LOG_I(TAG, \"Key A found\");\n            mf_classic_set_key_found(\n                instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key);\n\n            command = mf_classic_poller_handle_data_update(instance);\n            dict_attack_ctx->current_key_type = MfClassicKeyTypeA;\n            dict_attack_ctx->current_block = block;\n            dict_attack_ctx->auth_passed = true;\n            instance->state = MfClassicPollerStateReadSector;\n        } else {\n            mf_classic_poller_halt(instance);\n            instance->state = MfClassicPollerStateAuthKeyB;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    if(mf_classic_is_key_found(\n           instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) {\n        if(mf_classic_is_key_found(\n               instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {\n            instance->state = MfClassicPollerStateNextSector;\n        } else {\n            instance->state = MfClassicPollerStateRequestKey;\n        }\n    } else {\n        uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);\n        uint64_t key =\n            bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));\n        FURI_LOG_D(TAG, \"Auth to block %d with key B: %06llx\", block, key);\n\n        MfClassicError error = mf_classic_poller_auth(\n            instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);\n        if(error == MfClassicErrorNone) {\n            FURI_LOG_I(TAG, \"Key B found\");\n            mf_classic_set_key_found(\n                instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key);\n\n            command = mf_classic_poller_handle_data_update(instance);\n            dict_attack_ctx->current_key_type = MfClassicKeyTypeB;\n            dict_attack_ctx->current_block = block;\n\n            dict_attack_ctx->auth_passed = true;\n            instance->state = MfClassicPollerStateReadSector;\n        } else {\n            mf_classic_poller_halt(instance);\n            instance->state = MfClassicPollerStateRequestKey;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    dict_attack_ctx->current_sector++;\n    if(dict_attack_ctx->current_sector == instance->sectors_total) {\n        instance->state = MfClassicPollerStateSuccess;\n    } else {\n        instance->mfc_event.type = MfClassicPollerEventTypeNextSector;\n        instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector;\n        command = instance->callback(instance->general_event, instance->context);\n        instance->state = MfClassicPollerStateRequestKey;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    MfClassicError error = MfClassicErrorNone;\n    uint8_t block_num = dict_attack_ctx->current_block;\n    MfClassicBlock block = {};\n\n    do {\n        if(mf_classic_is_block_read(instance->data, block_num)) break;\n\n        if(!dict_attack_ctx->auth_passed) {\n            error = mf_classic_poller_auth(\n                instance,\n                block_num,\n                &dict_attack_ctx->current_key,\n                dict_attack_ctx->current_key_type,\n                NULL,\n                false);\n            if(error != MfClassicErrorNone) {\n                instance->state = MfClassicPollerStateNextSector;\n                FURI_LOG_W(TAG, \"Failed to re-auth. Go to next sector\");\n                break;\n            }\n        }\n\n        FURI_LOG_D(TAG, \"Reading block %d\", block_num);\n        error = mf_classic_poller_read_block(instance, block_num, &block);\n\n        if(error != MfClassicErrorNone) {\n            mf_classic_poller_halt(instance);\n            dict_attack_ctx->auth_passed = false;\n            FURI_LOG_D(TAG, \"Failed to read block %d\", block_num);\n        } else {\n            mf_classic_set_block_read(instance->data, block_num, &block);\n            if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {\n                mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);\n            }\n        }\n    } while(false);\n\n    uint8_t sec_tr_block_num =\n        mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector);\n    dict_attack_ctx->current_block++;\n    if(dict_attack_ctx->current_block > sec_tr_block_num) {\n        mf_classic_poller_handle_data_update(instance);\n\n        mf_classic_poller_halt(instance);\n        dict_attack_ctx->auth_passed = false;\n\n        if(dict_attack_ctx->current_sector == instance->sectors_total) {\n            instance->state = MfClassicPollerStateNextSector;\n        } else {\n            dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector;\n            instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;\n            instance->mfc_event_data.key_attack_data.current_sector =\n                dict_attack_ctx->reuse_key_sector;\n            command = instance->callback(instance->general_event, instance->context);\n            instance->state = MfClassicPollerStateKeyReuseStart;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    do {\n        if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {\n            dict_attack_ctx->current_key_type = MfClassicKeyTypeB;\n            instance->state = MfClassicPollerStateKeyReuseAuthKeyB;\n        } else {\n            dict_attack_ctx->reuse_key_sector++;\n            if(dict_attack_ctx->reuse_key_sector == instance->sectors_total) {\n                instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop;\n                command = instance->callback(instance->general_event, instance->context);\n                // Nested entrypoint\n                bool nested_active = dict_attack_ctx->nested_phase != MfClassicNestedPhaseNone;\n                if((dict_attack_ctx->enhanced_dict) &&\n                   ((nested_active &&\n                     (dict_attack_ctx->nested_phase != MfClassicNestedPhaseFinished)) ||\n                    (!(nested_active) && !(mf_classic_is_card_read(instance->data))))) {\n                    instance->state = MfClassicPollerStateNestedController;\n                    break;\n                }\n                instance->state = MfClassicPollerStateRequestKey;\n            } else {\n                instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;\n                instance->mfc_event_data.key_attack_data.current_sector =\n                    dict_attack_ctx->reuse_key_sector;\n                command = instance->callback(instance->general_event, instance->context);\n\n                dict_attack_ctx->current_key_type = MfClassicKeyTypeA;\n                instance->state = MfClassicPollerStateKeyReuseAuthKeyA;\n            }\n        }\n    } while(false);\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_key_reuse_start_no_offset(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;\n    instance->mfc_event_data.key_attack_data.current_sector = dict_attack_ctx->reuse_key_sector;\n    command = instance->callback(instance->general_event, instance->context);\n    if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {\n        instance->state = MfClassicPollerStateKeyReuseAuthKeyA;\n    } else {\n        instance->state = MfClassicPollerStateKeyReuseAuthKeyB;\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    if(mf_classic_is_key_found(\n           instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) {\n        instance->state = MfClassicPollerStateKeyReuseStart;\n    } else {\n        uint8_t block =\n            mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);\n        uint64_t key =\n            bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));\n        FURI_LOG_D(TAG, \"Key attack auth to block %d with key A: %06llx\", block, key);\n\n        MfClassicError error = mf_classic_poller_auth(\n            instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);\n        if(error == MfClassicErrorNone) {\n            FURI_LOG_I(TAG, \"Key A found\");\n            mf_classic_set_key_found(\n                instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key);\n\n            command = mf_classic_poller_handle_data_update(instance);\n            dict_attack_ctx->current_key_type = MfClassicKeyTypeA;\n            dict_attack_ctx->current_block = block;\n            dict_attack_ctx->auth_passed = true;\n            instance->state = MfClassicPollerStateKeyReuseReadSector;\n        } else {\n            mf_classic_poller_halt(instance);\n            dict_attack_ctx->auth_passed = false;\n            instance->state = MfClassicPollerStateKeyReuseStart;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    if(mf_classic_is_key_found(\n           instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) {\n        instance->state = MfClassicPollerStateKeyReuseStart;\n    } else {\n        uint8_t block =\n            mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);\n        uint64_t key =\n            bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));\n        FURI_LOG_D(TAG, \"Key attack auth to block %d with key B: %06llx\", block, key);\n\n        MfClassicError error = mf_classic_poller_auth(\n            instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);\n        if(error == MfClassicErrorNone) {\n            FURI_LOG_I(TAG, \"Key B found\");\n            mf_classic_set_key_found(\n                instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key);\n\n            command = mf_classic_poller_handle_data_update(instance);\n            dict_attack_ctx->current_key_type = MfClassicKeyTypeB;\n            dict_attack_ctx->current_block = block;\n\n            dict_attack_ctx->auth_passed = true;\n            instance->state = MfClassicPollerStateKeyReuseReadSector;\n        } else {\n            mf_classic_poller_halt(instance);\n            dict_attack_ctx->auth_passed = false;\n            instance->state = MfClassicPollerStateKeyReuseStart;\n        }\n    }\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    MfClassicError error = MfClassicErrorNone;\n    uint8_t block_num = dict_attack_ctx->current_block;\n    MfClassicBlock block = {};\n\n    do {\n        if(mf_classic_is_block_read(instance->data, block_num)) break;\n\n        if(!dict_attack_ctx->auth_passed) {\n            error = mf_classic_poller_auth(\n                instance,\n                block_num,\n                &dict_attack_ctx->current_key,\n                dict_attack_ctx->current_key_type,\n                NULL,\n                false);\n            if(error != MfClassicErrorNone) {\n                instance->state = MfClassicPollerStateKeyReuseStart;\n                break;\n            }\n        }\n\n        FURI_LOG_D(TAG, \"Reading block %d\", block_num);\n        error = mf_classic_poller_read_block(instance, block_num, &block);\n\n        if(error != MfClassicErrorNone) {\n            mf_classic_poller_halt(instance);\n            dict_attack_ctx->auth_passed = false;\n            FURI_LOG_D(TAG, \"Failed to read block %d\", block_num);\n        } else {\n            mf_classic_set_block_read(instance->data, block_num, &block);\n            if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {\n                mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);\n            }\n        }\n    } while(false);\n\n    uint16_t sec_tr_block_num =\n        mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector);\n    dict_attack_ctx->current_block++;\n    if(dict_attack_ctx->current_block > sec_tr_block_num) {\n        mf_classic_poller_halt(instance);\n        dict_attack_ctx->auth_passed = false;\n\n        mf_classic_poller_handle_data_update(instance);\n        instance->state = MfClassicPollerStateKeyReuseStart;\n    }\n\n    return command;\n}\n\n// Helper function to add a nonce to the array\nstatic bool add_nested_nonce(\n    MfClassicNestedNonceArray* array,\n    uint32_t cuid,\n    uint16_t key_idx,\n    uint32_t nt,\n    uint32_t nt_enc,\n    uint8_t par,\n    uint16_t dist) {\n    MfClassicNestedNonce* new_nonces;\n    if(array->count == 0) {\n        new_nonces = malloc(sizeof(MfClassicNestedNonce));\n    } else {\n        new_nonces = realloc(array->nonces, (array->count + 1) * sizeof(MfClassicNestedNonce));\n    }\n    if(new_nonces == NULL) return false;\n\n    array->nonces = new_nonces;\n    array->nonces[array->count].cuid = cuid;\n    array->nonces[array->count].key_idx = key_idx;\n    array->nonces[array->count].nt = nt;\n    array->nonces[array->count].nt_enc = nt_enc;\n    array->nonces[array->count].par = par;\n    array->nonces[array->count].dist = dist;\n    array->count++;\n    return true;\n}\n\nNfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    uint8_t hard_nt_count = 0;\n\n    for(uint8_t i = 0; i < dict_attack_ctx->nested_nonce.count; i++) {\n        MfClassicNestedNonce* nonce = &dict_attack_ctx->nested_nonce.nonces[i];\n        if(!crypto1_is_weak_prng_nonce(nonce->nt)) hard_nt_count++;\n    }\n\n    if(hard_nt_count >= MF_CLASSIC_NESTED_NT_HARD_MINIMUM) {\n        dict_attack_ctx->prng_type = MfClassicPrngTypeHard;\n        FURI_LOG_D(TAG, \"Detected Hard PRNG\");\n    } else {\n        dict_attack_ctx->prng_type = MfClassicPrngTypeWeak;\n        FURI_LOG_D(TAG, \"Detected Weak PRNG\");\n    }\n\n    instance->state = MfClassicPollerStateNestedController;\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_nested_collect_nt(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandReset;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    MfClassicNt nt = {};\n    MfClassicError error = mf_classic_poller_get_nt(instance, 0, MfClassicKeyTypeA, &nt, false);\n\n    if(error != MfClassicErrorNone) {\n        dict_attack_ctx->prng_type = MfClassicPrngTypeNoTag;\n        FURI_LOG_E(TAG, \"Failed to collect nt\");\n    } else {\n        FURI_LOG_T(TAG, \"nt: %02x%02x%02x%02x\", nt.data[0], nt.data[1], nt.data[2], nt.data[3]);\n        uint32_t nt_data = bit_lib_bytes_to_num_be(nt.data, sizeof(MfClassicNt));\n        if(!add_nested_nonce(\n               &dict_attack_ctx->nested_nonce,\n               iso14443_3a_get_cuid(instance->data->iso14443_3a_data),\n               0,\n               nt_data,\n               0,\n               0,\n               0)) {\n            dict_attack_ctx->prng_type = MfClassicPrngTypeNoTag;\n        }\n    }\n\n    instance->state = MfClassicPollerStateNestedController;\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    uint32_t nt_enc_temp_arr[MF_CLASSIC_NESTED_CALIBRATION_COUNT];\n    uint16_t distances[MF_CLASSIC_NESTED_CALIBRATION_COUNT - 1] = {0};\n\n    dict_attack_ctx->d_min = UINT16_MAX;\n    dict_attack_ctx->d_max = 0;\n    uint8_t block =\n        mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_known_key_sector);\n    uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);\n\n    MfClassicAuthContext auth_ctx = {};\n    MfClassicError error;\n\n    uint32_t nt_prev = 0;\n    uint32_t nt_enc_prev = 0;\n    uint32_t same_nt_enc_cnt = 0;\n    uint8_t nt_enc_collected = 0;\n    bool use_backdoor = (dict_attack_ctx->backdoor != MfClassicBackdoorNone);\n\n    // Step 1: Perform full authentication once\n    error = mf_classic_poller_auth(\n        instance,\n        block,\n        &dict_attack_ctx->nested_known_key,\n        dict_attack_ctx->nested_known_key_type,\n        &auth_ctx,\n        use_backdoor);\n\n    if(error != MfClassicErrorNone) {\n        FURI_LOG_E(TAG, \"Failed to perform full authentication\");\n        instance->state = MfClassicPollerStateNestedCalibrate;\n        return command;\n    }\n\n    FURI_LOG_D(TAG, \"Full authentication successful\");\n\n    nt_prev = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n\n    if((dict_attack_ctx->static_encrypted) &&\n       (dict_attack_ctx->backdoor == MfClassicBackdoorAuth3)) {\n        command = NfcCommandReset;\n        uint8_t target_block =\n            mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_target_key / 4);\n        MfClassicKeyType target_key_type =\n            ((dict_attack_ctx->nested_target_key % 4) < 2) ? MfClassicKeyTypeA : MfClassicKeyTypeB;\n        error = mf_classic_poller_auth_nested(\n            instance,\n            target_block,\n            &dict_attack_ctx->nested_known_key,\n            target_key_type,\n            &auth_ctx,\n            use_backdoor,\n            false);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to perform nested authentication for static encrypted tag\");\n            instance->state = MfClassicPollerStateNestedCalibrate;\n            return command;\n        }\n\n        uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n        // Store the decrypted static encrypted nonce\n        dict_attack_ctx->static_encrypted_nonce =\n            crypto1_decrypt_nt_enc(cuid, nt_enc, dict_attack_ctx->nested_known_key);\n\n        dict_attack_ctx->calibrated = true;\n\n        FURI_LOG_D(TAG, \"Static encrypted tag calibrated. Decrypted nonce: %08lx\", nt_enc);\n\n        instance->state = MfClassicPollerStateNestedController;\n        return command;\n    }\n\n    // Original calibration logic for non-static encrypted tags\n    // Step 2: Perform nested authentication multiple times\n    for(uint8_t collection_cycle = 0; collection_cycle < MF_CLASSIC_NESTED_CALIBRATION_COUNT;\n        collection_cycle++) {\n        error = mf_classic_poller_auth_nested(\n            instance,\n            block,\n            &dict_attack_ctx->nested_known_key,\n            dict_attack_ctx->nested_known_key_type,\n            &auth_ctx,\n            use_backdoor,\n            false);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to perform nested authentication %u\", collection_cycle);\n            continue;\n        }\n\n        nt_enc_temp_arr[collection_cycle] =\n            bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n        nt_enc_collected++;\n    }\n\n    for(int i = 0; i < nt_enc_collected; i++) {\n        if(nt_enc_temp_arr[i] == nt_enc_prev) {\n            same_nt_enc_cnt++;\n            if(same_nt_enc_cnt > 3) {\n                dict_attack_ctx->static_encrypted = true;\n                break;\n            }\n        } else {\n            same_nt_enc_cnt = 0;\n            nt_enc_prev = nt_enc_temp_arr[i];\n        }\n    }\n\n    if(dict_attack_ctx->static_encrypted) {\n        FURI_LOG_D(TAG, \"Static encrypted nonce detected\");\n        dict_attack_ctx->calibrated = true;\n        instance->state = MfClassicPollerStateNestedController;\n        return command;\n    }\n\n    // Find the distance between each nonce\n    FURI_LOG_D(TAG, \"Calculating distance between nonces\");\n    uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->nested_known_key.data, 6);\n    uint8_t valid_distances = 0;\n    for(uint32_t collection_cycle = 1; collection_cycle < MF_CLASSIC_NESTED_CALIBRATION_COUNT;\n        collection_cycle++) {\n        bool found = false;\n        uint32_t decrypted_nt_enc = crypto1_decrypt_nt_enc(\n            cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->nested_known_key);\n        for(int i = 0; i < 65535; i++) {\n            uint32_t nth_successor = crypto1_prng_successor(nt_prev, i);\n            if(nth_successor == decrypted_nt_enc) {\n                FURI_LOG_D(TAG, \"nt_enc (plain) %08lx\", nth_successor);\n                FURI_LOG_D(TAG, \"dist from nt prev: %i\", i);\n                distances[valid_distances++] = i;\n                nt_prev = nth_successor;\n                found = true;\n                break;\n            }\n        }\n        if(!found) {\n            FURI_LOG_E(\n                TAG,\n                \"Failed to find distance for nt_enc %08lx\",\n                nt_enc_temp_arr[collection_cycle]);\n            FURI_LOG_E(\n                TAG, \"using key %06llx and uid %08lx, nt_prev is %08lx\", known_key, cuid, nt_prev);\n        }\n    }\n\n    // Calculate median and standard deviation\n    if(valid_distances > 0) {\n        // Sort the distances array (bubble sort)\n        for(uint8_t i = 0; i < valid_distances - 1; i++) {\n            for(uint8_t j = 0; j < valid_distances - i - 1; j++) {\n                if(distances[j] > distances[j + 1]) {\n                    uint16_t temp = distances[j];\n                    distances[j] = distances[j + 1];\n                    distances[j + 1] = temp;\n                }\n            }\n        }\n\n        // Calculate median\n        uint16_t median =\n            (valid_distances % 2 == 0) ?\n                (distances[valid_distances / 2 - 1] + distances[valid_distances / 2]) / 2 :\n                distances[valid_distances / 2];\n\n        // Calculate standard deviation\n        float sum = 0, sum_sq = 0;\n        for(uint8_t i = 0; i < valid_distances; i++) {\n            sum += distances[i];\n            sum_sq += (float)distances[i] * distances[i];\n        }\n        float mean = sum / valid_distances;\n        float variance = (sum_sq / valid_distances) - (mean * mean);\n        float std_dev = sqrtf(variance);\n\n        // Filter out values over 3 standard deviations away from the median\n        for(uint8_t i = 0; i < valid_distances; i++) {\n            if(fabsf((float)distances[i] - median) <= 3 * std_dev) {\n                if(distances[i] < dict_attack_ctx->d_min) dict_attack_ctx->d_min = distances[i];\n                if(distances[i] > dict_attack_ctx->d_max) dict_attack_ctx->d_max = distances[i];\n            }\n        }\n\n        // Some breathing room\n        dict_attack_ctx->d_min = (dict_attack_ctx->d_min > 3) ? dict_attack_ctx->d_min - 3 : 0;\n        dict_attack_ctx->d_max += 3;\n    }\n\n    furi_assert(dict_attack_ctx->d_min <= dict_attack_ctx->d_max);\n    dict_attack_ctx->calibrated = true;\n    instance->state = MfClassicPollerStateNestedController;\n\n    mf_classic_poller_halt(instance);\n    uint16_t d_dist = dict_attack_ctx->d_max - dict_attack_ctx->d_min;\n    FURI_LOG_D(\n        TAG,\n        \"Calibration completed: min=%u max=%u static=%s\",\n        dict_attack_ctx->d_min,\n        dict_attack_ctx->d_max,\n        ((d_dist >= 3) && (d_dist <= 6)) ? \"true\" : \"false\");\n\n    return command;\n}\n\nstatic inline void set_byte_found(uint8_t* found, uint8_t byte) {\n    SET_PACKED_BIT(found, byte);\n}\n\nstatic inline bool is_byte_found(uint8_t* found, uint8_t byte) {\n    return GET_PACKED_BIT(found, byte) != 0;\n}\n\nNfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) {\n    // TODO FL-3926: Handle when nonce is not collected (retry counter? Do not increment nested_target_key)\n    // TODO FL-3926: Look into using MfClassicNt more\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    do {\n        uint8_t block =\n            mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_known_key_sector);\n        uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error;\n\n        bool use_backdoor = (dict_attack_ctx->backdoor != MfClassicBackdoorNone);\n        bool is_weak = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak;\n        uint8_t nonce_pair_index = is_weak ? (dict_attack_ctx->nested_target_key % 2) : 0;\n        uint8_t nt_enc_per_collection =\n            (is_weak && !(dict_attack_ctx->static_encrypted)) ?\n                ((dict_attack_ctx->attempt_count + 2) + nonce_pair_index) :\n                1;\n        uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 4 : 2);\n        MfClassicKeyType target_key_type =\n            (dict_attack_ctx->nested_target_key % (is_weak ? 4 : 2) < (is_weak ? 2 : 1)) ?\n                MfClassicKeyTypeA :\n                MfClassicKeyTypeB;\n        uint8_t target_block = mf_classic_get_sector_trailer_num_by_sector(target_sector);\n        uint32_t nt_enc_temp_arr[nt_enc_per_collection];\n        uint8_t nt_enc_collected = 0;\n        uint8_t parity = 0;\n\n        // Step 1: Perform full authentication once\n        error = mf_classic_poller_auth(\n            instance,\n            block,\n            &dict_attack_ctx->nested_known_key,\n            dict_attack_ctx->nested_known_key_type,\n            &auth_ctx,\n            use_backdoor);\n\n        if(error != MfClassicErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to perform full authentication\");\n            break;\n        }\n\n        FURI_LOG_D(TAG, \"Full authentication successful\");\n\n        // Step 2: Perform nested authentication a variable number of times to get nt_enc at a different PRNG offset\n        // eg. Collect most commonly observed nonce from 3 auths to known sector and 4th to target, then separately the\n        //     most commonly observed nonce from 4 auths to known sector and 5th to target. This gets us a nonce pair,\n        //     at a known distance (confirmed by parity bits) telling us the nt_enc plain.\n        for(uint8_t collection_cycle = 0; collection_cycle < (nt_enc_per_collection - 1);\n            collection_cycle++) {\n            // This loop must match the calibrated loop\n            error = mf_classic_poller_auth_nested(\n                instance,\n                block,\n                &dict_attack_ctx->nested_known_key,\n                dict_attack_ctx->nested_known_key_type,\n                &auth_ctx,\n                use_backdoor,\n                false);\n\n            if(error != MfClassicErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to perform nested authentication %u\", collection_cycle);\n                break;\n            }\n\n            nt_enc_temp_arr[collection_cycle] =\n                bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n            nt_enc_collected++;\n        }\n        error = mf_classic_poller_auth_nested(\n            instance,\n            target_block,\n            &dict_attack_ctx->nested_known_key,\n            target_key_type,\n            &auth_ctx,\n            false,\n            true);\n\n        if(nt_enc_collected != (nt_enc_per_collection - 1)) {\n            FURI_LOG_E(TAG, \"Failed to collect sufficient nt_enc values\");\n            break;\n        }\n\n        uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n        // Collect parity bits\n        const uint8_t* parity_data = bit_buffer_get_parity(instance->rx_plain_buffer);\n        for(int i = 0; i < 4; i++) {\n            parity = (parity << 1) | (((parity_data[0] >> i) & 0x01) ^ 0x01);\n        }\n\n        uint32_t nt_prev = 0, decrypted_nt_prev = 0, found_nt = 0;\n        uint16_t dist = 0;\n        if(is_weak && !(dict_attack_ctx->static_encrypted)) {\n            // Ensure this isn't the same nonce as the previous collection\n            if((dict_attack_ctx->nested_nonce.count == 1) &&\n               (dict_attack_ctx->nested_nonce.nonces[0].nt_enc == nt_enc)) {\n                FURI_LOG_D(TAG, \"Duplicate nonce, dismissing collection attempt\");\n                break;\n            }\n\n            // Decrypt the previous nonce\n            nt_prev = nt_enc_temp_arr[nt_enc_collected - 1];\n            decrypted_nt_prev =\n                crypto1_decrypt_nt_enc(cuid, nt_prev, dict_attack_ctx->nested_known_key);\n\n            // Find matching nt_enc plain at expected distance\n            found_nt = 0;\n            uint8_t found_nt_cnt = 0;\n            uint16_t current_dist = dict_attack_ctx->d_min;\n            while(current_dist <= dict_attack_ctx->d_max) {\n                uint32_t nth_successor = crypto1_prng_successor(decrypted_nt_prev, current_dist);\n                if(crypto1_nonce_matches_encrypted_parity_bits(\n                       nth_successor, nth_successor ^ nt_enc, parity)) {\n                    found_nt_cnt++;\n                    if(found_nt_cnt > 1) {\n                        FURI_LOG_D(TAG, \"Ambiguous nonce, dismissing collection attempt\");\n                        break;\n                    }\n                    found_nt = nth_successor;\n                }\n                current_dist++;\n            }\n            if(found_nt_cnt != 1) {\n                break;\n            }\n        } else if(dict_attack_ctx->static_encrypted) {\n            if(dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) {\n                found_nt = dict_attack_ctx->static_encrypted_nonce;\n            } else {\n                dist = UINT16_MAX;\n            }\n        } else {\n            // Hardnested\n            if(!is_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF)) {\n                set_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF);\n                dict_attack_ctx->msb_count++;\n                // Add unique parity to sum\n                dict_attack_ctx->msb_par_sum += nfc_util_even_parity32(parity & 0x08);\n            }\n            parity ^= 0x0F;\n        }\n\n        // Add the nonce to the array\n        if(add_nested_nonce(\n               &dict_attack_ctx->nested_nonce,\n               cuid,\n               dict_attack_ctx->nested_target_key,\n               found_nt,\n               nt_enc,\n               parity,\n               dist)) {\n            dict_attack_ctx->auth_passed = true;\n        } else {\n            FURI_LOG_E(TAG, \"Failed to add nested nonce to array. OOM?\");\n        }\n\n        FURI_LOG_D(\n            TAG,\n            \"Target: %u (nonce pair %u, key type %s, block %u)\",\n            dict_attack_ctx->nested_target_key,\n            nonce_pair_index,\n            (target_key_type == MfClassicKeyTypeA) ? \"A\" : \"B\",\n            target_block);\n        FURI_LOG_T(TAG, \"cuid: %08lx\", cuid);\n        FURI_LOG_T(TAG, \"nt_enc: %08lx\", nt_enc);\n        FURI_LOG_T(\n            TAG,\n            \"parity: %u%u%u%u\",\n            ((parity >> 3) & 1),\n            ((parity >> 2) & 1),\n            ((parity >> 1) & 1),\n            (parity & 1));\n        FURI_LOG_T(TAG, \"nt_enc prev: %08lx\", nt_prev);\n        FURI_LOG_T(TAG, \"nt_enc prev decrypted: %08lx\", decrypted_nt_prev);\n    } while(false);\n\n    instance->state = MfClassicPollerStateNestedController;\n\n    mf_classic_poller_halt(instance);\n    return command;\n}\n\nstatic MfClassicKey* search_dicts_for_nonce_key(\n    MfClassicPollerDictAttackContext* dict_attack_ctx,\n    MfClassicNestedNonceArray* nonce_array,\n    KeysDict* system_dict,\n    KeysDict* user_dict,\n    bool is_weak) {\n    MfClassicKey stack_key;\n    KeysDict* dicts[] = {user_dict, system_dict};\n    bool is_resumed = dict_attack_ctx->nested_phase == MfClassicNestedPhaseDictAttackResume;\n    bool found_resume_point = false;\n\n    for(int i = 0; i < 2; i++) {\n        if(!dicts[i]) continue;\n        keys_dict_rewind(dicts[i]);\n        while(keys_dict_get_next_key(dicts[i], stack_key.data, sizeof(MfClassicKey))) {\n            if(is_resumed && !found_resume_point) {\n                found_resume_point =\n                    (memcmp(\n                         dict_attack_ctx->current_key.data,\n                         stack_key.data,\n                         sizeof(MfClassicKey)) == 0);\n                continue;\n            }\n            bool full_match = true;\n            for(uint8_t j = 0; j < nonce_array->count; j++) {\n                // Verify nonce matches encrypted parity bits for all nonces\n                uint32_t nt_enc_plain = crypto1_decrypt_nt_enc(\n                    nonce_array->nonces[j].cuid, nonce_array->nonces[j].nt_enc, stack_key);\n                if(is_weak) {\n                    full_match &= crypto1_is_weak_prng_nonce(nt_enc_plain);\n                    if(!full_match) break;\n                }\n                full_match &= crypto1_nonce_matches_encrypted_parity_bits(\n                    nt_enc_plain,\n                    nt_enc_plain ^ nonce_array->nonces[j].nt_enc,\n                    nonce_array->nonces[j].par);\n                if(!full_match) break;\n            }\n            if(full_match) {\n                MfClassicKey* new_candidate = malloc(sizeof(MfClassicKey));\n                if(new_candidate == NULL) return NULL; // malloc failed\n                memcpy(new_candidate, &stack_key, sizeof(MfClassicKey));\n                return new_candidate;\n            }\n        }\n    }\n\n    return NULL;\n}\n\nNfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instance) {\n    // TODO FL-3926: Handle when nonce is not collected (retry counter? Do not increment nested_target_key)\n    // TODO FL-3926: Look into using MfClassicNt more\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n\n    do {\n        uint8_t block =\n            mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_known_key_sector);\n        uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);\n\n        MfClassicAuthContext auth_ctx = {};\n        MfClassicError error;\n\n        bool use_backdoor_for_initial_auth = (dict_attack_ctx->backdoor != MfClassicBackdoorNone);\n        bool is_weak = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak;\n        bool is_last_iter_for_hard_key =\n            ((!is_weak) && ((dict_attack_ctx->nested_target_key % 8) == 7));\n        MfClassicKeyType target_key_type =\n            (((is_weak) && ((dict_attack_ctx->nested_target_key % 2) == 0)) ||\n             ((!is_weak) && ((dict_attack_ctx->nested_target_key % 16) < 8))) ?\n                MfClassicKeyTypeA :\n                MfClassicKeyTypeB;\n        uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 2 : 16);\n        uint8_t target_block = mf_classic_get_sector_trailer_num_by_sector(target_sector);\n        uint8_t parity = 0;\n\n        if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 0)) ||\n           ((!is_weak) && (dict_attack_ctx->nested_nonce.count < 8))) {\n            // Step 1: Perform full authentication once\n            error = mf_classic_poller_auth(\n                instance,\n                block,\n                &dict_attack_ctx->nested_known_key,\n                dict_attack_ctx->nested_known_key_type,\n                &auth_ctx,\n                use_backdoor_for_initial_auth);\n\n            if(error != MfClassicErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to perform full authentication\");\n                dict_attack_ctx->auth_passed = false;\n                break;\n            }\n\n            FURI_LOG_D(TAG, \"Full authentication successful\");\n\n            // Step 2: Collect nested nt and parity\n            error = mf_classic_poller_auth_nested(\n                instance,\n                target_block,\n                &dict_attack_ctx->nested_known_key,\n                target_key_type,\n                &auth_ctx,\n                false,\n                true);\n\n            if(error != MfClassicErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to perform nested authentication\");\n                dict_attack_ctx->auth_passed = false;\n                break;\n            }\n\n            uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));\n            // Collect parity bits\n            const uint8_t* parity_data = bit_buffer_get_parity(instance->rx_plain_buffer);\n            for(int i = 0; i < 4; i++) {\n                parity = (parity << 1) | (((parity_data[0] >> i) & 0x01) ^ 0x01);\n            }\n\n            bool success = add_nested_nonce(\n                &dict_attack_ctx->nested_nonce,\n                cuid,\n                dict_attack_ctx->nested_target_key,\n                0,\n                nt_enc,\n                parity,\n                0);\n            if(!success) {\n                FURI_LOG_E(TAG, \"Failed to add nested nonce to array. OOM?\");\n                dict_attack_ctx->auth_passed = false;\n                break;\n            }\n\n            dict_attack_ctx->auth_passed = true;\n        }\n        // If we have sufficient nonces, search the dictionaries for the key\n        if((is_weak && (dict_attack_ctx->nested_nonce.count == 1)) ||\n           (is_last_iter_for_hard_key && (dict_attack_ctx->nested_nonce.count == 8))) {\n            // Identify key candidates\n            MfClassicKey* key_candidate = search_dicts_for_nonce_key(\n                dict_attack_ctx,\n                &dict_attack_ctx->nested_nonce,\n                dict_attack_ctx->mf_classic_system_dict,\n                dict_attack_ctx->mf_classic_user_dict,\n                is_weak);\n            if(key_candidate != NULL) {\n                FURI_LOG_I(\n                    TAG,\n                    \"Found key candidate %06llx\",\n                    bit_lib_bytes_to_num_be(key_candidate->data, sizeof(MfClassicKey)));\n                dict_attack_ctx->current_key = *key_candidate;\n                dict_attack_ctx->reuse_key_sector = target_sector;\n                dict_attack_ctx->current_key_type = target_key_type;\n                free(key_candidate);\n                break;\n            } else {\n                free(dict_attack_ctx->nested_nonce.nonces);\n                dict_attack_ctx->nested_nonce.nonces = NULL;\n                dict_attack_ctx->nested_nonce.count = 0;\n            }\n        }\n\n        FURI_LOG_D(\n            TAG,\n            \"Target: %u (key type %s, block %u) cuid: %08lx\",\n            dict_attack_ctx->nested_target_key,\n            (target_key_type == MfClassicKeyTypeA) ? \"A\" : \"B\",\n            target_block,\n            cuid);\n    } while(false);\n\n    instance->state = MfClassicPollerStateNestedController;\n\n    mf_classic_poller_halt(instance);\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_nested_log(MfClassicPoller* instance) {\n    furi_assert(instance->mode_ctx.dict_attack_ctx.nested_nonce.count > 0);\n    furi_assert(instance->mode_ctx.dict_attack_ctx.nested_nonce.nonces);\n\n    NfcCommand command = NfcCommandContinue;\n    bool params_saved = false;\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    Stream* stream = buffered_file_stream_alloc(storage);\n    FuriString* temp_str = furi_string_alloc();\n    bool weak_prng = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak;\n    bool static_encrypted = dict_attack_ctx->static_encrypted;\n\n    do {\n        if(weak_prng && (!(static_encrypted)) && (dict_attack_ctx->nested_nonce.count != 2)) {\n            FURI_LOG_E(\n                TAG,\n                \"MfClassicPollerStateNestedLog expected 2 nonces, received %zu\",\n                dict_attack_ctx->nested_nonce.count);\n            break;\n        }\n\n        uint32_t nonce_pair_count = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak ?\n                                        1 :\n                                        dict_attack_ctx->nested_nonce.count;\n\n        if(!buffered_file_stream_open(\n               stream, MF_CLASSIC_NESTED_LOGS_FILE_PATH, FSAM_WRITE, FSOM_OPEN_APPEND))\n            break;\n\n        bool params_write_success = true;\n        for(size_t i = 0; i < nonce_pair_count; i++) {\n            MfClassicNestedNonce* nonce = &dict_attack_ctx->nested_nonce.nonces[i];\n            // TODO FL-3926: Avoid repeating logic here\n            uint8_t nonce_sector = nonce->key_idx / (weak_prng ? 4 : 2);\n            MfClassicKeyType nonce_key_type =\n                (nonce->key_idx % (weak_prng ? 4 : 2) < (weak_prng ? 2 : 1)) ? MfClassicKeyTypeA :\n                                                                               MfClassicKeyTypeB;\n            furi_string_printf(\n                temp_str,\n                \"Sec %d key %c cuid %08lx\",\n                nonce_sector,\n                (nonce_key_type == MfClassicKeyTypeA) ? 'A' : 'B',\n                nonce->cuid);\n            for(uint8_t nt_idx = 0; nt_idx < ((weak_prng && (!(static_encrypted))) ? 2 : 1);\n                nt_idx++) {\n                if(nt_idx == 1) {\n                    nonce = &dict_attack_ctx->nested_nonce.nonces[i + 1];\n                }\n                furi_string_cat_printf(\n                    temp_str,\n                    \" nt%u %08lx ks%u %08lx par%u \",\n                    nt_idx,\n                    nonce->nt,\n                    nt_idx,\n                    nonce->nt_enc ^ nonce->nt,\n                    nt_idx);\n                for(uint8_t pb = 0; pb < 4; pb++) {\n                    furi_string_cat_printf(temp_str, \"%u\", (nonce->par >> (3 - pb)) & 1);\n                }\n            }\n            if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {\n                furi_string_cat_printf(temp_str, \" dist %u\\n\", nonce->dist);\n            } else {\n                furi_string_cat_printf(temp_str, \"\\n\");\n            }\n            if(!stream_write_string(stream, temp_str)) {\n                params_write_success = false;\n                break;\n            }\n        }\n        if(!params_write_success) break;\n\n        params_saved = true;\n    } while(false);\n\n    furi_assert(params_saved);\n    free(dict_attack_ctx->nested_nonce.nonces);\n    dict_attack_ctx->nested_nonce.nonces = NULL;\n    dict_attack_ctx->nested_nonce.count = 0;\n    furi_string_free(temp_str);\n    buffered_file_stream_close(stream);\n    stream_free(stream);\n    furi_record_close(RECORD_STORAGE);\n    instance->state = MfClassicPollerStateNestedController;\n    return command;\n}\n\nbool mf_classic_nested_is_target_key_found(MfClassicPoller* instance, bool is_dict_attack) {\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    bool is_weak = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak;\n    uint16_t nested_target_key = dict_attack_ctx->nested_target_key;\n\n    MfClassicKeyType target_key_type;\n    uint8_t target_sector;\n\n    if(is_dict_attack) {\n        target_key_type = (((is_weak) && ((nested_target_key % 2) == 0)) ||\n                           ((!is_weak) && ((nested_target_key % 16) < 8))) ?\n                              MfClassicKeyTypeA :\n                              MfClassicKeyTypeB;\n        target_sector = is_weak ? (nested_target_key / 2) : (nested_target_key / 16);\n    } else {\n        target_key_type = (((is_weak) && ((nested_target_key % 4) < 2)) ||\n                           ((!is_weak) && ((nested_target_key % 2) == 0))) ?\n                              MfClassicKeyTypeA :\n                              MfClassicKeyTypeB;\n        target_sector = is_weak ? (nested_target_key / 4) : (nested_target_key / 2);\n    }\n\n    return mf_classic_is_key_found(instance->data, target_sector, target_key_type);\n}\n\nbool is_valid_sum(uint16_t sum) {\n    for(size_t i = 0; i < 19; i++) {\n        if(sum == valid_sums[i]) {\n            return true;\n        }\n    }\n    return false;\n}\n\nNfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) {\n    // This function guides the nested attack through its phases, and iterates over the target keys\n    NfcCommand command = mf_classic_poller_handle_data_update(instance);\n    MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;\n    bool initial_dict_attack_iter = false;\n    if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseNone) {\n        dict_attack_ctx->auth_passed = true;\n        bool backdoor_present = (dict_attack_ctx->backdoor != MfClassicBackdoorNone);\n        if(!(backdoor_present)) {\n            for(uint8_t sector = 0; sector < instance->sectors_total; sector++) {\n                for(uint8_t key_type = 0; key_type < 2; key_type++) {\n                    if(mf_classic_is_key_found(instance->data, sector, key_type)) {\n                        dict_attack_ctx->nested_known_key =\n                            mf_classic_get_key(instance->data, sector, key_type);\n                        dict_attack_ctx->nested_known_key_sector = sector;\n                        dict_attack_ctx->nested_known_key_type = key_type;\n                        break;\n                    }\n                }\n            }\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseAnalyzePRNG;\n        } else {\n            dict_attack_ctx->nested_known_key = dict_attack_ctx->current_key;\n            dict_attack_ctx->nested_known_key_sector = 0;\n            dict_attack_ctx->nested_known_key_type = MfClassicKeyTypeA;\n            dict_attack_ctx->prng_type = MfClassicPrngTypeWeak;\n            if(dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) {\n                dict_attack_ctx->static_encrypted = true;\n            }\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttack;\n            initial_dict_attack_iter = true;\n        }\n    }\n    // Identify PRNG type\n    if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseAnalyzePRNG) {\n        if(dict_attack_ctx->nested_nonce.count < MF_CLASSIC_NESTED_ANALYZE_NT_COUNT) {\n            instance->state = MfClassicPollerStateNestedCollectNt;\n            return command;\n        } else if(\n            (dict_attack_ctx->nested_nonce.count == MF_CLASSIC_NESTED_ANALYZE_NT_COUNT) &&\n            (dict_attack_ctx->prng_type == MfClassicPrngTypeUnknown)) {\n            instance->state = MfClassicPollerStateNestedAnalyzePRNG;\n            return command;\n        } else if(dict_attack_ctx->prng_type == MfClassicPrngTypeNoTag) {\n            FURI_LOG_E(TAG, \"No tag detected\");\n            // Free nonce array\n            // TODO FL-3926: Consider using .count here\n            if(dict_attack_ctx->nested_nonce.nonces) {\n                free(dict_attack_ctx->nested_nonce.nonces);\n                dict_attack_ctx->nested_nonce.nonces = NULL;\n                dict_attack_ctx->nested_nonce.count = 0;\n            }\n            instance->state = MfClassicPollerStateFail;\n            return command;\n        }\n        if(dict_attack_ctx->nested_nonce.nonces) {\n            // Free nonce array\n            // TODO FL-3926: Consider using .count here\n            free(dict_attack_ctx->nested_nonce.nonces);\n            dict_attack_ctx->nested_nonce.nonces = NULL;\n            dict_attack_ctx->nested_nonce.count = 0;\n        }\n        dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttack;\n        initial_dict_attack_iter = true;\n    }\n    // Accelerated nested dictionary attack\n    bool is_weak = dict_attack_ctx->prng_type == MfClassicPrngTypeWeak;\n    uint16_t dict_target_key_max = (dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) ?\n                                       (instance->sectors_total * 2) :\n                                       (instance->sectors_total * 16);\n    if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseDictAttackVerify) {\n        if(!(mf_classic_nested_is_target_key_found(instance, true)) &&\n           (dict_attack_ctx->nested_nonce.count > 0)) {\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttackResume;\n            instance->state = MfClassicPollerStateNestedDictAttack;\n            return command;\n        } else {\n            dict_attack_ctx->auth_passed = true;\n            if(dict_attack_ctx->nested_nonce.count > 0) {\n                // Free nonce array\n                furi_assert(dict_attack_ctx->nested_nonce.nonces);\n                free(dict_attack_ctx->nested_nonce.nonces);\n                dict_attack_ctx->nested_nonce.nonces = NULL;\n                dict_attack_ctx->nested_nonce.count = 0;\n            }\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttack;\n        }\n    }\n    if((dict_attack_ctx->nested_phase == MfClassicNestedPhaseDictAttack ||\n        dict_attack_ctx->nested_phase == MfClassicNestedPhaseDictAttackResume) &&\n       (dict_attack_ctx->nested_target_key < dict_target_key_max)) {\n        bool is_last_iter_for_hard_key =\n            ((!is_weak) && ((dict_attack_ctx->nested_target_key % 8) == 7));\n        if(initial_dict_attack_iter) {\n            // Initialize dictionaries\n            // Note: System dict should always exist\n            dict_attack_ctx->mf_classic_system_dict =\n                keys_dict_check_presence(MF_CLASSIC_NESTED_SYSTEM_DICT_PATH) ?\n                    keys_dict_alloc(\n                        MF_CLASSIC_NESTED_SYSTEM_DICT_PATH,\n                        KeysDictModeOpenExisting,\n                        sizeof(MfClassicKey)) :\n                    NULL;\n\n            dict_attack_ctx->mf_classic_user_dict =\n                keys_dict_check_presence(MF_CLASSIC_NESTED_USER_DICT_PATH) ?\n                    keys_dict_alloc(\n                        MF_CLASSIC_NESTED_USER_DICT_PATH,\n                        KeysDictModeOpenExisting,\n                        sizeof(MfClassicKey)) :\n                    NULL;\n        }\n        if((is_weak && (dict_attack_ctx->nested_nonce.count == 1)) ||\n           (is_last_iter_for_hard_key && (dict_attack_ctx->nested_nonce.count == 8))) {\n            // Key verify and reuse\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttackVerify;\n            dict_attack_ctx->auth_passed = false;\n            instance->state = MfClassicPollerStateKeyReuseStartNoOffset;\n            return command;\n        } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseDictAttackResume) {\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseDictAttack;\n            dict_attack_ctx->auth_passed = true;\n        }\n        if(!(dict_attack_ctx->auth_passed)) {\n            dict_attack_ctx->attempt_count++;\n        } else if(!(initial_dict_attack_iter)) {\n            dict_attack_ctx->nested_target_key++;\n            dict_attack_ctx->attempt_count = 0;\n        }\n        dict_attack_ctx->auth_passed = true;\n        if(dict_attack_ctx->nested_target_key == dict_target_key_max) {\n            if(dict_attack_ctx->mf_classic_system_dict) {\n                keys_dict_free(dict_attack_ctx->mf_classic_system_dict);\n                dict_attack_ctx->mf_classic_system_dict = NULL;\n            }\n            if(dict_attack_ctx->mf_classic_user_dict) {\n                keys_dict_free(dict_attack_ctx->mf_classic_user_dict);\n                dict_attack_ctx->mf_classic_user_dict = NULL;\n            }\n            dict_attack_ctx->nested_target_key = 0;\n            if(mf_classic_is_card_read(instance->data)) {\n                // All keys have been collected\n                FURI_LOG_D(TAG, \"All keys collected and sectors read\");\n                dict_attack_ctx->nested_phase = MfClassicNestedPhaseFinished;\n                instance->state = MfClassicPollerStateSuccess;\n                return command;\n            }\n            if(dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) {\n                // Skip initial calibration for static encrypted backdoored tags\n                dict_attack_ctx->calibrated = true;\n            }\n            dict_attack_ctx->nested_phase = MfClassicNestedPhaseCalibrate;\n            instance->state = MfClassicPollerStateNestedController;\n            return command;\n        }\n        if(dict_attack_ctx->attempt_count == 0) {\n            // Check if the nested target key is a known key\n            if(mf_classic_nested_is_target_key_found(instance, true)) {\n                // Continue to next key\n                instance->state = MfClassicPollerStateNestedController;\n                return command;\n            }\n        }\n        if(dict_attack_ctx->attempt_count >= 3) {\n            // Unpredictable, skip\n            FURI_LOG_E(TAG, \"Failed to collect nonce, skipping key\");\n            dict_attack_ctx->nested_target_key++;\n            dict_attack_ctx->attempt_count = 0;\n        }\n        instance->state = MfClassicPollerStateNestedDictAttack;\n        return command;\n    }\n    // Calibration\n    bool initial_collect_nt_enc_iter = false;\n    bool recalibrated = false;\n    if(!(dict_attack_ctx->calibrated)) {\n        if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {\n            instance->state = MfClassicPollerStateNestedCalibrate;\n            return command;\n        }\n        initial_collect_nt_enc_iter = true;\n        dict_attack_ctx->calibrated = true;\n        dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;\n    } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCalibrate) {\n        initial_collect_nt_enc_iter = true;\n        dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;\n    } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseRecalibrate) {\n        recalibrated = true;\n        dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;\n    }\n    // Collect and log nonces\n    if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCollectNtEnc) {\n        if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 2)) ||\n           ((is_weak) && (dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) &&\n            (dict_attack_ctx->nested_nonce.count == 1)) ||\n           ((!(is_weak)) && (dict_attack_ctx->nested_nonce.count > 0))) {\n            instance->state = MfClassicPollerStateNestedLog;\n            return command;\n        }\n        uint16_t nonce_collect_key_max;\n        if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {\n            nonce_collect_key_max = instance->sectors_total * 4;\n        } else {\n            nonce_collect_key_max = instance->sectors_total * 2;\n        }\n        // Target all remaining sectors, key A and B\n        if(dict_attack_ctx->nested_target_key < nonce_collect_key_max) {\n            if((!(is_weak)) && (dict_attack_ctx->msb_count == (UINT8_MAX + 1))) {\n                if(is_valid_sum(dict_attack_ctx->msb_par_sum)) {\n                    // All Hardnested nonces collected\n                    dict_attack_ctx->nested_target_key++;\n                    dict_attack_ctx->current_key_checked = false;\n                    instance->state = MfClassicPollerStateNestedController;\n                } else {\n                    // Nonces do not match an expected sum\n                    dict_attack_ctx->attempt_count++;\n                    instance->state = MfClassicPollerStateNestedCollectNtEnc;\n                }\n                dict_attack_ctx->msb_count = 0;\n                dict_attack_ctx->msb_par_sum = 0;\n                memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));\n                return command;\n            }\n            if(initial_collect_nt_enc_iter) {\n                dict_attack_ctx->current_key_checked = false;\n            }\n            if(!(dict_attack_ctx->auth_passed) && !(initial_collect_nt_enc_iter)) {\n                dict_attack_ctx->attempt_count++;\n            } else {\n                if(is_weak && !(initial_collect_nt_enc_iter) && !(recalibrated)) {\n                    if(!(dict_attack_ctx->static_encrypted)) {\n                        dict_attack_ctx->nested_target_key++;\n                    } else {\n                        dict_attack_ctx->nested_target_key += 2;\n                    }\n                    if(dict_attack_ctx->nested_target_key % 2 == 0) {\n                        dict_attack_ctx->current_key_checked = false;\n                    }\n                }\n                dict_attack_ctx->attempt_count = 0;\n            }\n            dict_attack_ctx->auth_passed = true;\n\n            // If we have tried to collect this nonce too many times, skip\n            if((is_weak && (dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_RETRY_MAXIMUM)) ||\n               (!(is_weak) &&\n                (dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM))) {\n                // Unpredictable, skip\n                FURI_LOG_W(TAG, \"Failed to collect nonce, skipping key\");\n                if(dict_attack_ctx->nested_nonce.nonces) {\n                    free(dict_attack_ctx->nested_nonce.nonces);\n                    dict_attack_ctx->nested_nonce.nonces = NULL;\n                    dict_attack_ctx->nested_nonce.count = 0;\n                }\n                if(is_weak) {\n                    dict_attack_ctx->nested_target_key += 2;\n                    dict_attack_ctx->current_key_checked = false;\n                } else {\n                    dict_attack_ctx->msb_count = 0;\n                    dict_attack_ctx->msb_par_sum = 0;\n                    memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));\n                    dict_attack_ctx->nested_target_key++;\n                    dict_attack_ctx->current_key_checked = false;\n                }\n                dict_attack_ctx->attempt_count = 0;\n            }\n\n            FURI_LOG_D(\n                TAG,\n                \"Nested target key: %u (max: %u)\",\n                dict_attack_ctx->nested_target_key,\n                nonce_collect_key_max);\n\n            if(!(dict_attack_ctx->current_key_checked)) {\n                if(dict_attack_ctx->nested_target_key == nonce_collect_key_max) {\n                    // All nonces have been collected\n                    FURI_LOG_D(TAG, \"All nonces collected\");\n                    instance->state = MfClassicPollerStateNestedController;\n                    return command;\n                }\n\n                dict_attack_ctx->current_key_checked = true;\n\n                // Check if the nested target key is a known key\n                if(mf_classic_nested_is_target_key_found(instance, false)) {\n                    // Continue to next key\n                    if(!(dict_attack_ctx->static_encrypted)) {\n                        dict_attack_ctx->nested_target_key++;\n                        dict_attack_ctx->current_key_checked = false;\n                    }\n                    instance->state = MfClassicPollerStateNestedController;\n                    return command;\n                }\n\n                // If it is not a known key, we'll need to calibrate for static encrypted backdoored tags\n                if((dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) &&\n                   (dict_attack_ctx->nested_target_key < nonce_collect_key_max) &&\n                   !(recalibrated)) {\n                    dict_attack_ctx->calibrated = false;\n                    dict_attack_ctx->nested_phase = MfClassicNestedPhaseRecalibrate;\n                    instance->state = MfClassicPollerStateNestedController;\n                    return command;\n                }\n            }\n            FURI_LOG_T(TAG, \"Collecting a nonce\");\n\n            // Collect a nonce\n            dict_attack_ctx->auth_passed = false;\n            instance->state = MfClassicPollerStateNestedCollectNtEnc;\n            return command;\n        }\n    }\n    dict_attack_ctx->nested_target_key = 0;\n    dict_attack_ctx->nested_phase = MfClassicNestedPhaseFinished;\n    instance->state = MfClassicPollerStateSuccess;\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->mfc_event.type = MfClassicPollerEventTypeSuccess;\n    command = instance->callback(instance->general_event, instance->context);\n\n    return command;\n}\n\nNfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->mfc_event.type = MfClassicPollerEventTypeFail;\n    command = instance->callback(instance->general_event, instance->context);\n    instance->state = MfClassicPollerStateDetectType;\n\n    return command;\n}\n\nstatic const MfClassicPollerReadHandler\n    mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = {\n        [MfClassicPollerStateDetectType] = mf_classic_poller_handler_detect_type,\n        [MfClassicPollerStateStart] = mf_classic_poller_handler_start,\n        [MfClassicPollerStateRequestSectorTrailer] =\n            mf_classic_poller_handler_request_sector_trailer,\n        [MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions,\n        [MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block,\n        [MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block,\n        [MfClassicPollerStateWriteValueBlock] = mf_classic_poller_handler_write_value_block,\n        [MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector,\n        [MfClassicPollerStateAnalyzeBackdoor] = mf_classic_poller_handler_analyze_backdoor,\n        [MfClassicPollerStateBackdoorReadSector] = mf_classic_poller_handler_backdoor_read_sector,\n        [MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key,\n        [MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector,\n        [MfClassicPollerStateReadSectorBlocks] =\n            mf_classic_poller_handler_request_read_sector_blocks,\n        [MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a,\n        [MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b,\n        [MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector,\n        [MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start,\n        [MfClassicPollerStateKeyReuseStartNoOffset] =\n            mf_classic_poller_handler_key_reuse_start_no_offset,\n        [MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a,\n        [MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b,\n        [MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector,\n        [MfClassicPollerStateNestedAnalyzePRNG] = mf_classic_poller_handler_nested_analyze_prng,\n        [MfClassicPollerStateNestedCalibrate] = mf_classic_poller_handler_nested_calibrate,\n        [MfClassicPollerStateNestedCollectNt] = mf_classic_poller_handler_nested_collect_nt,\n        [MfClassicPollerStateNestedController] = mf_classic_poller_handler_nested_controller,\n        [MfClassicPollerStateNestedCollectNtEnc] = mf_classic_poller_handler_nested_collect_nt_enc,\n        [MfClassicPollerStateNestedDictAttack] = mf_classic_poller_handler_nested_dict_attack,\n        [MfClassicPollerStateNestedLog] = mf_classic_poller_handler_nested_log,\n        [MfClassicPollerStateSuccess] = mf_classic_poller_handler_success,\n        [MfClassicPollerStateFail] = mf_classic_poller_handler_fail,\n};\n\nNfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n    furi_assert(context);\n\n    MfClassicPoller* instance = context;\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        if(instance->card_state == MfClassicCardStateLost) {\n            instance->card_state = MfClassicCardStateDetected;\n            instance->mfc_event.type = MfClassicPollerEventTypeCardDetected;\n            instance->callback(instance->general_event, instance->context);\n        }\n        command = mf_classic_poller_dict_attack_handler[instance->state](instance);\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        if(instance->card_state == MfClassicCardStateDetected) {\n            instance->card_state = MfClassicCardStateLost;\n            instance->mfc_event.type = MfClassicPollerEventTypeCardLost;\n            command = instance->callback(instance->general_event, instance->context);\n        }\n    }\n\n    return command;\n}\n\nbool mf_classic_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n    furi_assert(context);\n\n    Iso14443_3aPoller* iso3_poller = event.instance;\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n    bool detected = false;\n    const uint8_t auth_cmd[] = {MF_CLASSIC_CMD_AUTH_KEY_A, 0};\n    BitBuffer* tx_buffer = bit_buffer_alloc(COUNT_OF(auth_cmd));\n    bit_buffer_copy_bytes(tx_buffer, auth_cmd, COUNT_OF(auth_cmd));\n    BitBuffer* rx_buffer = bit_buffer_alloc(sizeof(MfClassicNt));\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(\n            iso3_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);\n        if(error == Iso14443_3aErrorWrongCrc) {\n            if(bit_buffer_get_size_bytes(rx_buffer) == sizeof(MfClassicNt)) {\n                detected = true;\n            }\n        }\n    }\n\n    bit_buffer_free(tx_buffer);\n    bit_buffer_free(rx_buffer);\n\n    return detected;\n}\n\nvoid mf_classic_poller_set_callback(\n    MfClassicPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nconst MfClassicData* mf_classic_poller_get_data(const MfClassicPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nconst NfcPollerBase mf_classic_poller = {\n    .alloc = (NfcPollerAlloc)mf_classic_poller_alloc,\n    .free = (NfcPollerFree)mf_classic_poller_free,\n    .set_callback = (NfcPollerSetCallback)mf_classic_poller_set_callback,\n    .run = (NfcPollerRun)mf_classic_poller_run,\n    .detect = (NfcPollerDetect)mf_classic_poller_detect,\n    .get_data = (NfcPollerGetData)mf_classic_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller.h",
    "content": "#pragma once\n\n#include \"mf_classic.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief MfClassicPoller opaque type definition.\n */\ntypedef struct MfClassicPoller MfClassicPoller;\n\n/**\n * @brief Enumeration of possible MfClassic poller event types.\n */\ntypedef enum {\n    MfClassicPollerEventTypeRequestMode, /**< Poller requests to fill the mode. */\n\n    MfClassicPollerEventTypeRequestReadSector, /**< Poller requests data to read sector. */\n\n    MfClassicPollerEventTypeRequestSectorTrailer, /**< Poller requests sector trailer for writing block. */\n    MfClassicPollerEventTypeRequestWriteBlock, /**< Poller requests data to write block. */\n\n    MfClassicPollerEventTypeRequestKey, /**< Poller requests key for sector authentication. */\n    MfClassicPollerEventTypeNextSector, /**< Poller switches to next sector during dictionary attack. */\n    MfClassicPollerEventTypeDataUpdate, /**< Poller updates data. */\n    MfClassicPollerEventTypeFoundKeyA, /**< Poller found key A. */\n    MfClassicPollerEventTypeFoundKeyB, /**< Poller found key B. */\n    MfClassicPollerEventTypeKeyAttackStart, /**< Poller starts key attack. */\n    MfClassicPollerEventTypeKeyAttackStop, /**< Poller stops key attack. */\n    MfClassicPollerEventTypeKeyAttackNextSector, /**< Poller switches to next sector during key attack. */\n\n    MfClassicPollerEventTypeCardDetected, /**< Poller detected card. */\n    MfClassicPollerEventTypeCardLost, /**< Poller lost card. */\n    MfClassicPollerEventTypeSuccess, /**< Poller succeeded. */\n    MfClassicPollerEventTypeFail, /**< Poller failed. */\n} MfClassicPollerEventType;\n\n/**\n * @brief MfClassic poller mode.\n */\ntypedef enum {\n    MfClassicPollerModeRead, /**< Poller reading mode. */\n    MfClassicPollerModeWrite, /**< Poller writing mode. */\n    MfClassicPollerModeDictAttackStandard, /**< Poller dictionary attack mode. */\n    MfClassicPollerModeDictAttackEnhanced, /**< Poller enhanced dictionary attack mode. */\n} MfClassicPollerMode;\n\n/**\n * @brief MfClassic poller nested attack phase.\n */\ntypedef enum {\n    MfClassicNestedPhaseNone, /**< No nested attack has taken place yet. */\n    MfClassicNestedPhaseAnalyzePRNG, /**< Analyze nonces produced by the PRNG to determine if they fit a weak PRNG */\n    MfClassicNestedPhaseDictAttack, /**< Search keys which match the expected PRNG properties and parity for collected nonces */\n    MfClassicNestedPhaseDictAttackVerify, /**< Verify candidate keys by authenticating to the sector with the key */\n    MfClassicNestedPhaseDictAttackResume, /**< Resume nested dictionary attack from the last tested (invalid) key */\n    MfClassicNestedPhaseCalibrate, /**< Perform necessary calculations to recover the plaintext nonce during later collection phase (weak PRNG tags only) */\n    MfClassicNestedPhaseRecalibrate, /**< Collect the next plaintext static encrypted nonce for backdoor static encrypted nonce nested attack */\n    MfClassicNestedPhaseCollectNtEnc, /**< Log nonces collected during nested authentication for key recovery */\n    MfClassicNestedPhaseFinished, /**< Nested attack has finished */\n} MfClassicNestedPhase;\n\n/**\n * @brief MfClassic pseudorandom number generator (PRNG) type.\n */\ntypedef enum {\n    MfClassicPrngTypeUnknown, // Tag not yet tested\n    MfClassicPrngTypeNoTag, // No tag detected during test\n    MfClassicPrngTypeWeak, // Weak PRNG, standard Nested\n    MfClassicPrngTypeHard, // Hard PRNG, Hardnested\n} MfClassicPrngType;\n\n/**\n * @brief MfClassic authentication backdoor type.\n */\ntypedef enum {\n    MfClassicBackdoorUnknown, // Tag not yet tested\n    MfClassicBackdoorNone, // No observed backdoor\n    MfClassicBackdoorAuth1, // Tag responds to v1 auth backdoor\n    MfClassicBackdoorAuth2, // Tag responds to v2 auth backdoor (sometimes static encrypted)\n    MfClassicBackdoorAuth3, // Tag responds to v3 auth backdoor (static encrypted nonce)\n} MfClassicBackdoor;\n\n/**\n * @brief MfClassic poller request mode event data.\n *\n * This instance of this structure must be filled on MfClassicPollerEventTypeRequestMode event.\n */\ntypedef struct {\n    MfClassicPollerMode mode; /**< Mode to be used by poller. */\n    const MfClassicData* data; /**< Data to be used by poller. */\n} MfClassicPollerEventDataRequestMode;\n\n/**\n * @brief MfClassic poller next sector event data.\n *\n * The instance of this structure is filled by poller and passed with\n * MfClassicPollerEventTypeNextSector event.\n */\ntypedef struct {\n    uint8_t current_sector; /**< Current sector number. */\n} MfClassicPollerEventDataDictAttackNextSector;\n\n/**\n * @brief MfClassic poller update event data.\n *\n * The instance of this structure is filled by poller and passed with\n * MfClassicPollerEventTypeDataUpdate event.\n */\ntypedef struct {\n    uint8_t sectors_read; /**< Number of sectors read. */\n    uint8_t keys_found; /**< Number of keys found. */\n    uint8_t current_sector; /**< Current sector number. */\n    MfClassicNestedPhase nested_phase; /**< Nested attack phase. */\n    MfClassicPrngType prng_type; /**< PRNG (weak or hard). */\n    MfClassicBackdoor backdoor; /**< Backdoor type. */\n    uint16_t nested_target_key; /**< Target key for nested attack. */\n    uint16_t\n        msb_count; /**< Number of unique most significant bytes seen during Hardnested attack. */\n} MfClassicPollerEventDataUpdate;\n\n/**\n * @brief MfClassic poller key request event data.\n *\n * The instance of this structure must be filled on MfClassicPollerEventTypeRequestKey event.\n */\ntypedef struct {\n    MfClassicKey key; /**< Key to be used by poller. */\n    bool key_provided; /**< Flag indicating if key is provided. */\n} MfClassicPollerEventDataKeyRequest;\n\n/**\n * @brief MfClassic poller read sector request event data.\n *\n * The instance of this structure must be filled on MfClassicPollerEventTypeRequestReadSector event.\n */\ntypedef struct {\n    uint8_t sector_num; /**< Sector number to be read. */\n    MfClassicKey key; /**< Key to be used by poller. */\n    MfClassicKeyType key_type; /**< Key type to be used by poller. */\n    bool key_provided; /**< Flag indicating if key is provided. */\n} MfClassicPollerEventDataReadSectorRequest;\n\n/**\n * @brief MfClassic poller sector trailer request event data.\n *\n * The instance of this structure must be filled on MfClassicPollerEventTypeRequestSectorTrailer event.\n */\ntypedef struct {\n    uint8_t sector_num; /**< Sector number to be read. */\n    MfClassicBlock sector_trailer; /**< Sector trailer to be used by poller. */\n    bool sector_trailer_provided; /**< Flag indicating if sector trailer is provided. */\n} MfClassicPollerEventDataSectorTrailerRequest;\n\n/**\n * @brief MfClassic poller write block request event data.\n *\n * The instance of this structure must be filled on MfClassicPollerEventTypeRequestWriteBlock event.\n */\ntypedef struct {\n    uint8_t block_num; /**< Block number to be written. */\n    MfClassicBlock write_block; /**< Block to be written. */\n    bool write_block_provided; /**< Flag indicating if block is provided. */\n} MfClassicPollerEventDataWriteBlockRequest;\n\n/**\n * @brief MfClassic poller key attack event data.\n *\n * The instance of this structure is filled by poller and passed with\n * MfClassicPollerEventTypeKeyAttackNextSector event.\n */\ntypedef struct {\n    uint8_t current_sector; /**< Current sector number. */\n} MfClassicPollerEventKeyAttackData;\n\n/**\n * @brief MfClassic poller event data.\n */\ntypedef union {\n    MfClassicError error; /**< Error code on MfClassicPollerEventTypeFail event. */\n    MfClassicPollerEventDataRequestMode poller_mode; /**< Poller mode context. */\n    MfClassicPollerEventDataDictAttackNextSector next_sector_data; /**< Next sector context. */\n    MfClassicPollerEventDataKeyRequest key_request_data; /**< Key request context. */\n    MfClassicPollerEventDataUpdate data_update; /**< Data update context. */\n    MfClassicPollerEventDataReadSectorRequest\n        read_sector_request_data; /**< Read sector request context. */\n    MfClassicPollerEventKeyAttackData key_attack_data; /**< Key attack context. */\n    MfClassicPollerEventDataSectorTrailerRequest sec_tr_data; /**< Sector trailer request context. */\n    MfClassicPollerEventDataWriteBlockRequest write_block_data; /**< Write block request context. */\n} MfClassicPollerEventData;\n\n/**\n * @brief MfClassic poller event.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    MfClassicPollerEventType type; /**< Event type. */\n    MfClassicPollerEventData* data; /**< Pointer to event specific data. */\n} MfClassicPollerEvent;\n\n/**\n * @brief Collect tag nonce during authentication.\n *\n * Must ONLY be used inside the callback function.\n *\n * Starts authentication procedure and collects tag nonce.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number for authentication.\n * @param[in] key_type key type to be used for authentication.\n * @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.\n * @param[in] backdoor_auth flag indicating if backdoor authentication is used.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_get_nt(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt,\n    bool backdoor_auth);\n\n/**\n * @brief Collect tag nonce during nested authentication.\n *\n * Must ONLY be used inside the callback function.\n *\n * Starts nested authentication procedure and collects tag nonce.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number for authentication.\n * @param[in] key_type key type to be used for authentication.\n * @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.\n * @param[in] backdoor_auth flag indicating if backdoor authentication is used.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_get_nt_nested(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt,\n    bool backdoor_auth);\n\n/**\n * @brief Perform authentication.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perform authentication as specified in Mf Classic protocol. Initialize crypto state for futher\n * communication with the tag.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number for authentication.\n * @param[in] key key to be used for authentication.\n * @param[in] key_type key type to be used for authentication.\n * @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.\n * @param[in] backdoor_auth flag indicating if backdoor authentication is used.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_auth(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data,\n    bool backdoor_auth);\n\n/**\n * @brief Perform nested authentication.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perform nested authentication as specified in Mf Classic protocol.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number for authentication.\n * @param[in] key key to be used for authentication.\n * @param[in] key_type key type to be used for authentication.\n * @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.\n * @param[in] backdoor_auth flag indicating if backdoor authentication is used.\n * @param[in] early_ret return immediately after receiving encrypted nonce.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_auth_nested(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data,\n    bool backdoor_auth,\n    bool early_ret);\n\n/**\n * @brief Halt the tag.\n *\n * Must ONLY be used inside the callback function.\n *\n * Halt the tag and reset crypto state of the poller.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_halt(MfClassicPoller* instance);\n\n/**\n * @brief Read block from tag.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number to be read.\n * @param[out] data pointer to the MfClassicBlock structure to be filled with block data.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_read_block(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicBlock* data);\n\n/**\n * @brief Write block to tag.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number to be written.\n * @param[in] data pointer to the MfClassicBlock structure to be written.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_write_block(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicBlock* data);\n\n/**\n * @brief Perform value command on tag.\n *\n * Must ONLY be used inside the callback function.\n *\n * Perform Increment, Decrement or Restore command on tag. The result is stored in internal transfer\n * block of the tag. Use mf_classic_poller_value_transfer to transfer the result to the tag.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number to be used for value command.\n * @param[in] cmd value command to be performed.\n * @param[in] data value to be used for value command.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_value_cmd(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicValueCommand cmd,\n    int32_t data);\n\n/**\n * @brief Transfer internal transfer block to tag.\n *\n * Must ONLY be used inside the callback function.\n *\n * Transfer internal transfer block to tag. The block is filled by mf_classic_poller_value_cmd.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] block_num block number to be used for value command.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num);\n\n/**\n * @brief Transmit and receive Iso14443_3a standard frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_send_standard_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc);\n\n/**\n * @brief Transmit and receive Iso14443_3a frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_send_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc);\n\n/**\n * @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * Custom parity bits must be set in the tx_buffer. The rx_buffer will contain\n * the received data with the parity bits.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_send_custom_parity_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc);\n\n/**\n * @brief Transmit and receive Mifare Classic encrypted frames with custom parity bits in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the plain data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with decyphered received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return MfClassicErrorNone on success, an error code on failure.\n */\nMfClassicError mf_classic_poller_send_encrypted_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcPollerBase mf_classic_poller;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller_i.c",
    "content": "#include \"mf_classic_poller_i.h\"\n\n#include <furi.h>\n#include <furi_hal_random.h>\n\n#include <nfc/helpers/iso14443_crc.h>\n\n#define TAG \"MfClassicPoller\"\n\nMfClassicError mf_classic_process_error(Iso14443_3aError error) {\n    MfClassicError ret = MfClassicErrorNone;\n\n    switch(error) {\n    case Iso14443_3aErrorNone:\n        ret = MfClassicErrorNone;\n        break;\n    case Iso14443_3aErrorNotPresent:\n        ret = MfClassicErrorNotPresent;\n        break;\n    case Iso14443_3aErrorColResFailed:\n    case Iso14443_3aErrorCommunication:\n    case Iso14443_3aErrorWrongCrc:\n        ret = MfClassicErrorProtocol;\n        break;\n    case Iso14443_3aErrorTimeout:\n        ret = MfClassicErrorTimeout;\n        break;\n    default:\n        ret = MfClassicErrorProtocol;\n        break;\n    }\n\n    return ret;\n}\n\nstatic MfClassicError mf_classic_poller_get_nt_common(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt,\n    bool is_nested,\n    bool backdoor_auth) {\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t auth_type;\n        if(!backdoor_auth) {\n            auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :\n                                                          MF_CLASSIC_CMD_AUTH_KEY_A;\n        } else {\n            auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B :\n                                                          MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_A;\n        }\n        uint8_t auth_cmd[2] = {auth_type, block_num};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));\n\n        if(is_nested) {\n            iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n            crypto1_encrypt(\n                instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n            error = iso14443_3a_poller_txrx_custom_parity(\n                instance->iso14443_3a_poller,\n                instance->tx_encrypted_buffer,\n                instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth\n                MF_CLASSIC_FWT_FC);\n            if(error != Iso14443_3aErrorNone) {\n                ret = mf_classic_process_error(error);\n                break;\n            }\n        } else {\n            error = iso14443_3a_poller_send_standard_frame(\n                instance->iso14443_3a_poller,\n                instance->tx_plain_buffer,\n                instance->rx_plain_buffer,\n                MF_CLASSIC_FWT_FC);\n            if(error != Iso14443_3aErrorWrongCrc) {\n                ret = mf_classic_process_error(error);\n                break;\n            }\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        if(nt) {\n            bit_buffer_write_bytes(instance->rx_plain_buffer, nt->data, sizeof(MfClassicNt));\n        }\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_get_nt(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt,\n    bool backdoor_auth) {\n    furi_check(instance);\n\n    return mf_classic_poller_get_nt_common(\n        instance, block_num, key_type, nt, false, backdoor_auth);\n}\n\nMfClassicError mf_classic_poller_get_nt_nested(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt,\n    bool backdoor_auth) {\n    furi_check(instance);\n\n    return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true, backdoor_auth);\n}\n\nMfClassicError mf_classic_poller_auth_common(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data,\n    bool is_nested,\n    bool backdoor_auth,\n    bool early_ret) {\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        iso14443_3a_copy(\n            instance->data->iso14443_3a_data,\n            iso14443_3a_poller_get_data(instance->iso14443_3a_poller));\n\n        MfClassicNt nt = {};\n        if(is_nested) {\n            ret =\n                mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt, backdoor_auth);\n        } else {\n            ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt, backdoor_auth);\n        }\n        if(ret != MfClassicErrorNone) break;\n        if(data) {\n            data->nt = nt;\n        }\n        if(early_ret) break;\n\n        uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);\n        uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));\n        MfClassicNr nr = {};\n        furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr));\n\n        crypto1_encrypt_reader_nonce(\n            instance->crypto,\n            key_num,\n            cuid,\n            nt.data,\n            nr.data,\n            instance->tx_encrypted_buffer,\n            is_nested);\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) {\n            ret = MfClassicErrorAuth;\n        }\n\n        crypto1_word(instance->crypto, 0, 0);\n        instance->auth_state = MfClassicAuthStatePassed;\n\n        if(data) {\n            data->nr = nr;\n            const uint8_t* nr_ar = bit_buffer_get_data(instance->tx_encrypted_buffer);\n            memcpy(data->ar.data, &nr_ar[4], sizeof(MfClassicAr));\n            bit_buffer_write_bytes(\n                instance->rx_encrypted_buffer, data->at.data, sizeof(MfClassicAt));\n        }\n    } while(false);\n\n    if(ret != MfClassicErrorNone) {\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    }\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_auth(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data,\n    bool backdoor_auth) {\n    furi_check(instance);\n    furi_check(key);\n    return mf_classic_poller_auth_common(\n        instance, block_num, key, key_type, data, false, backdoor_auth, false);\n}\n\nMfClassicError mf_classic_poller_auth_nested(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data,\n    bool backdoor_auth,\n    bool early_ret) {\n    furi_check(instance);\n    furi_check(key);\n    return mf_classic_poller_auth_common(\n        instance, block_num, key, key_type, data, true, backdoor_auth, early_ret);\n}\n\nMfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {\n    furi_check(instance);\n\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t halt_cmd[2] = {MF_CLASSIC_CMD_HALT_MSB, MF_CLASSIC_CMD_HALT_LSB};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, halt_cmd, sizeof(halt_cmd));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorTimeout) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        instance->auth_state = MfClassicAuthStateIdle;\n        instance->iso14443_3a_poller->state = Iso14443_3aPollerStateIdle;\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_read_block(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicBlock* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t read_block_cmd[2] = {MF_CLASSIC_CMD_READ_BLOCK, block_num};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, read_block_cmd, sizeof(read_block_cmd));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) !=\n           (sizeof(MfClassicBlock) + 2)) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        crypto1_decrypt(\n            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);\n\n        if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_plain_buffer)) {\n            FURI_LOG_D(TAG, \"CRC error\");\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        iso14443_crc_trim(instance->rx_plain_buffer);\n        bit_buffer_write_bytes(instance->rx_plain_buffer, data->data, sizeof(MfClassicBlock));\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_write_block(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicBlock* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t write_block_cmd[2] = {MF_CLASSIC_CMD_WRITE_BLOCK, block_num};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, write_block_cmd, sizeof(write_block_cmd));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        crypto1_decrypt(\n            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);\n\n        if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {\n            FURI_LOG_D(TAG, \"Not ACK received\");\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, data->data, sizeof(MfClassicBlock));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        crypto1_decrypt(\n            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);\n\n        if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {\n            FURI_LOG_D(TAG, \"Not ACK received\");\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_value_cmd(\n    MfClassicPoller* instance,\n    uint8_t block_num,\n    MfClassicValueCommand cmd,\n    int32_t data) {\n    furi_check(instance);\n\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t cmd_value = 0;\n        if(cmd == MfClassicValueCommandDecrement) {\n            cmd_value = MF_CLASSIC_CMD_VALUE_DEC;\n        } else if(cmd == MfClassicValueCommandIncrement) {\n            cmd_value = MF_CLASSIC_CMD_VALUE_INC;\n        } else {\n            cmd_value = MF_CLASSIC_CMD_VALUE_RESTORE;\n        }\n        uint8_t value_cmd[2] = {cmd_value, block_num};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, value_cmd, sizeof(value_cmd));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        crypto1_decrypt(\n            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);\n\n        if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {\n            FURI_LOG_D(TAG, \"Not ACK received\");\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, (uint8_t*)&data, sizeof(data));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n\n        // Command processed if tag doesn't respond\n        if(error != Iso14443_3aErrorTimeout) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n        ret = MfClassicErrorNone;\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num) {\n    furi_check(instance);\n\n    MfClassicError ret = MfClassicErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t transfer_cmd[2] = {MF_CLASSIC_CMD_VALUE_TRANSFER, block_num};\n        bit_buffer_copy_bytes(instance->tx_plain_buffer, transfer_cmd, sizeof(transfer_cmd));\n        iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);\n\n        crypto1_encrypt(\n            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);\n\n        error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            MF_CLASSIC_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n        crypto1_decrypt(\n            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);\n\n        if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {\n            FURI_LOG_D(TAG, \"Not ACK received\");\n            ret = MfClassicErrorProtocol;\n            break;\n        }\n\n    } while(false);\n\n    return ret;\n}\n\nMfClassicError mf_classic_poller_send_standard_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(\n        instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);\n\n    return mf_classic_process_error(error);\n}\n\nMfClassicError mf_classic_poller_send_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError error =\n        iso14443_3a_poller_txrx(instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);\n\n    return mf_classic_process_error(error);\n}\n\nMfClassicError mf_classic_poller_send_custom_parity_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(\n        instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);\n\n    return mf_classic_process_error(error);\n}\n\nMfClassicError mf_classic_poller_send_encrypted_frame(\n    MfClassicPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt_fc) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    MfClassicError ret = MfClassicErrorNone;\n    do {\n        crypto1_encrypt(instance->crypto, NULL, tx_buffer, instance->tx_encrypted_buffer);\n\n        Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(\n            instance->iso14443_3a_poller,\n            instance->tx_encrypted_buffer,\n            instance->rx_encrypted_buffer,\n            fwt_fc);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_classic_process_error(error);\n            break;\n        }\n\n        crypto1_decrypt(instance->crypto, instance->rx_encrypted_buffer, rx_buffer);\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller_i.h",
    "content": "#pragma once\n\n#include \"mf_classic_poller.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>\n#include <bit_lib/bit_lib.h>\n#include <nfc/helpers/iso14443_crc.h>\n#include <nfc/helpers/crypto1.h>\n#include <stream/stream.h>\n#include <stream/buffered_file_stream.h>\n#include <toolbox/keys_dict.h>\n#include <helpers/nfc_util.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_CLASSIC_FWT_FC                       (60000)\n#define NFC_FOLDER                              EXT_PATH(\"nfc\")\n#define NFC_ASSETS_FOLDER                       EXT_PATH(\"nfc/assets\")\n#define MF_CLASSIC_NESTED_ANALYZE_NT_COUNT      (5)\n#define MF_CLASSIC_NESTED_NT_HARD_MINIMUM       (3)\n#define MF_CLASSIC_NESTED_RETRY_MAXIMUM         (60)\n#define MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM    (3)\n#define MF_CLASSIC_NESTED_CALIBRATION_COUNT     (21)\n#define MF_CLASSIC_NESTED_LOGS_FILE_NAME        \".nested.log\"\n#define MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME \"mf_classic_dict_nested.nfc\"\n#define MF_CLASSIC_NESTED_USER_DICT_FILE_NAME   \"mf_classic_dict_user_nested.nfc\"\n#define MF_CLASSIC_NESTED_LOGS_FILE_PATH        (NFC_FOLDER \"/\" MF_CLASSIC_NESTED_LOGS_FILE_NAME)\n#define MF_CLASSIC_NESTED_SYSTEM_DICT_PATH \\\n    (NFC_ASSETS_FOLDER \"/\" MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME)\n#define MF_CLASSIC_NESTED_USER_DICT_PATH \\\n    (NFC_ASSETS_FOLDER \"/\" MF_CLASSIC_NESTED_USER_DICT_FILE_NAME)\n#define SET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] |= (1 << ((bit) % 8)))\n#define GET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] & (1 << ((bit) % 8)))\n\nextern const MfClassicKey auth1_backdoor_key;\nextern const MfClassicKey auth2_backdoor_key;\nextern const MfClassicKey auth3_backdoor_key;\nextern const uint16_t valid_sums[19];\n\ntypedef enum {\n    MfClassicAuthStateIdle,\n    MfClassicAuthStatePassed,\n} MfClassicAuthState;\n\ntypedef enum {\n    MfClassicCardStateDetected,\n    MfClassicCardStateLost,\n} MfClassicCardState;\n\ntypedef struct {\n    MfClassicKey key;\n    MfClassicBackdoor type;\n} MfClassicBackdoorKeyPair;\n\nextern const MfClassicBackdoorKeyPair mf_classic_backdoor_keys[];\nextern const size_t mf_classic_backdoor_keys_count;\n\ntypedef struct {\n    uint32_t cuid; // Card UID\n    uint8_t key_idx; // Key index\n    uint32_t nt; // Nonce\n    uint32_t nt_enc; // Encrypted nonce\n    uint8_t par; // Parity\n    uint16_t dist; // Distance\n} MfClassicNestedNonce;\n\ntypedef struct {\n    MfClassicNestedNonce* nonces;\n    size_t count;\n} MfClassicNestedNonceArray;\n\ntypedef enum {\n    MfClassicPollerStateDetectType,\n    MfClassicPollerStateStart,\n\n    // Write states\n    MfClassicPollerStateRequestSectorTrailer,\n    MfClassicPollerStateCheckWriteConditions,\n    MfClassicPollerStateReadBlock,\n    MfClassicPollerStateWriteBlock,\n    MfClassicPollerStateWriteValueBlock,\n\n    // Read states\n    MfClassicPollerStateRequestReadSector,\n    MfClassicPollerStateReadSectorBlocks,\n\n    // Dict attack states\n    MfClassicPollerStateNextSector,\n    MfClassicPollerStateAnalyzeBackdoor,\n    MfClassicPollerStateBackdoorReadSector,\n    MfClassicPollerStateRequestKey,\n    MfClassicPollerStateReadSector,\n    MfClassicPollerStateAuthKeyA,\n    MfClassicPollerStateAuthKeyB,\n    MfClassicPollerStateKeyReuseStart,\n    MfClassicPollerStateKeyReuseStartNoOffset,\n    MfClassicPollerStateKeyReuseAuthKeyA,\n    MfClassicPollerStateKeyReuseAuthKeyB,\n    MfClassicPollerStateKeyReuseReadSector,\n    MfClassicPollerStateSuccess,\n    MfClassicPollerStateFail,\n\n    // Enhanced dictionary attack states\n    MfClassicPollerStateNestedAnalyzePRNG,\n    MfClassicPollerStateNestedCalibrate,\n    MfClassicPollerStateNestedCollectNt,\n    MfClassicPollerStateNestedController,\n    MfClassicPollerStateNestedCollectNtEnc,\n    MfClassicPollerStateNestedDictAttack,\n    MfClassicPollerStateNestedLog,\n\n    MfClassicPollerStateNum,\n} MfClassicPollerState;\n\ntypedef struct {\n    uint8_t current_sector;\n    MfClassicSectorTrailer sec_tr;\n    uint16_t current_block;\n    bool is_value_block;\n    MfClassicKeyType key_type_read;\n    MfClassicKeyType key_type_write;\n    bool need_halt_before_write;\n    MfClassicBlock tag_block;\n} MfClassicPollerWriteContext;\n\ntypedef struct {\n    uint8_t current_sector;\n    MfClassicKey current_key;\n    MfClassicKeyType current_key_type;\n    bool auth_passed;\n    uint16_t current_block;\n    uint8_t reuse_key_sector;\n    MfClassicBackdoor backdoor;\n    // Enhanced dictionary attack and nested nonce collection\n    bool enhanced_dict;\n    MfClassicNestedPhase nested_phase;\n    MfClassicKey nested_known_key;\n    MfClassicKeyType nested_known_key_type;\n    bool current_key_checked;\n    uint8_t nested_known_key_sector;\n    uint16_t nested_target_key;\n    MfClassicNestedNonceArray nested_nonce;\n    MfClassicPrngType prng_type;\n    bool static_encrypted;\n    uint32_t static_encrypted_nonce;\n    bool calibrated;\n    uint16_t d_min;\n    uint16_t d_max;\n    uint8_t attempt_count;\n    KeysDict* mf_classic_system_dict;\n    KeysDict* mf_classic_user_dict;\n    // Hardnested\n    uint8_t nt_enc_msb\n        [32]; // Bit-packed array to track which unique most significant bytes have been seen (256 bits = 32 bytes)\n    uint16_t msb_par_sum; // Sum of parity bits for each unique most significant byte\n    uint16_t msb_count; // Number of unique most significant bytes seen\n} MfClassicPollerDictAttackContext;\n\ntypedef struct {\n    uint8_t current_sector;\n    uint16_t current_block;\n    MfClassicKeyType key_type;\n    MfClassicKey key;\n    bool auth_passed;\n} MfClassicPollerReadContext;\n\ntypedef union {\n    MfClassicPollerWriteContext write_ctx;\n    MfClassicPollerDictAttackContext dict_attack_ctx;\n    MfClassicPollerReadContext read_ctx;\n\n} MfClassicPollerModeContext;\n\nstruct MfClassicPoller {\n    Iso14443_3aPoller* iso14443_3a_poller;\n\n    MfClassicPollerState state;\n    MfClassicAuthState auth_state;\n    MfClassicCardState card_state;\n\n    MfClassicType current_type_check;\n    uint8_t sectors_total;\n    MfClassicPollerModeContext mode_ctx;\n\n    Crypto1* crypto;\n    BitBuffer* tx_plain_buffer;\n    BitBuffer* tx_encrypted_buffer;\n    BitBuffer* rx_plain_buffer;\n    BitBuffer* rx_encrypted_buffer;\n    MfClassicData* data;\n\n    NfcGenericEvent general_event;\n    MfClassicPollerEvent mfc_event;\n    MfClassicPollerEventData mfc_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\ntypedef struct {\n    uint8_t block;\n    MfClassicKeyType key_type;\n    MfClassicNt nt;\n} MfClassicCollectNtContext;\n\ntypedef struct {\n    uint8_t block_num;\n    MfClassicKey key;\n    MfClassicKeyType key_type;\n    MfClassicBlock block;\n} MfClassicReadBlockContext;\n\ntypedef struct {\n    uint8_t block_num;\n    MfClassicKey key;\n    MfClassicKeyType key_type;\n    MfClassicBlock block;\n} MfClassicWriteBlockContext;\n\ntypedef struct {\n    uint8_t block_num;\n    MfClassicKey key;\n    MfClassicKeyType key_type;\n    int32_t value;\n} MfClassicReadValueContext;\n\ntypedef struct {\n    uint8_t block_num;\n    MfClassicKey key;\n    MfClassicKeyType key_type;\n    MfClassicValueCommand value_cmd;\n    int32_t data;\n    int32_t new_value;\n} MfClassicChangeValueContext;\n\ntypedef struct {\n    MfClassicDeviceKeys keys;\n    uint8_t current_sector;\n} MfClassicReadContext;\n\ntypedef union {\n    MfClassicCollectNtContext collect_nt_context;\n    MfClassicAuthContext auth_context;\n    MfClassicReadBlockContext read_block_context;\n    MfClassicWriteBlockContext write_block_context;\n    MfClassicReadValueContext read_value_context;\n    MfClassicChangeValueContext change_value_context;\n    MfClassicReadContext read_context;\n} MfClassicPollerContextData;\n\nMfClassicError mf_classic_process_error(Iso14443_3aError error);\n\nMfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller);\n\nvoid mf_classic_poller_free(MfClassicPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c",
    "content": "#include \"mf_classic_poller_sync.h\"\n#include \"mf_classic_poller_i.h\"\n\n#include <nfc/nfc_poller.h>\n\n#include <furi.h>\n\n#define TAG \"MfClassicPoller\"\n\n#define MF_CLASSIC_POLLER_COMPLETE_EVENT (1UL << 0)\n\ntypedef enum {\n    MfClassicPollerCmdTypeCollectNt,\n    MfClassicPollerCmdTypeAuth,\n    MfClassicPollerCmdTypeReadBlock,\n    MfClassicPollerCmdTypeWriteBlock,\n    MfClassicPollerCmdTypeReadValue,\n    MfClassicPollerCmdTypeChangeValue,\n\n    MfClassicPollerCmdTypeNum,\n} MfClassicPollerCmdType;\n\ntypedef struct {\n    MfClassicPollerCmdType cmd_type;\n    FuriThreadId thread_id;\n    MfClassicError error;\n    MfClassicPollerContextData data;\n} MfClassicPollerContext;\n\ntypedef MfClassicError (\n    *MfClassicPollerCmdHandler)(MfClassicPoller* poller, MfClassicPollerContextData* data);\n\nstatic MfClassicError mf_classic_poller_collect_nt_handler(\n    MfClassicPoller* poller,\n    MfClassicPollerContextData* data) {\n    return mf_classic_poller_get_nt(\n        poller,\n        data->collect_nt_context.block,\n        data->collect_nt_context.key_type,\n        &data->collect_nt_context.nt,\n        false);\n}\n\nstatic MfClassicError\n    mf_classic_poller_auth_handler(MfClassicPoller* poller, MfClassicPollerContextData* data) {\n    return mf_classic_poller_auth(\n        poller,\n        data->auth_context.block_num,\n        &data->auth_context.key,\n        data->auth_context.key_type,\n        &data->auth_context,\n        false);\n}\n\nstatic MfClassicError mf_classic_poller_read_block_handler(\n    MfClassicPoller* poller,\n    MfClassicPollerContextData* data) {\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        error = mf_classic_poller_auth(\n            poller,\n            data->read_block_context.block_num,\n            &data->read_block_context.key,\n            data->read_block_context.key_type,\n            NULL,\n            false);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_read_block(\n            poller, data->read_block_context.block_num, &data->read_block_context.block);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_halt(poller);\n        if(error != MfClassicErrorNone) break;\n\n    } while(false);\n\n    return error;\n}\n\nstatic MfClassicError mf_classic_poller_write_block_handler(\n    MfClassicPoller* poller,\n    MfClassicPollerContextData* data) {\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        error = mf_classic_poller_auth(\n            poller,\n            data->read_block_context.block_num,\n            &data->read_block_context.key,\n            data->read_block_context.key_type,\n            NULL,\n            false);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_write_block(\n            poller, data->write_block_context.block_num, &data->write_block_context.block);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_halt(poller);\n        if(error != MfClassicErrorNone) break;\n\n    } while(false);\n\n    return error;\n}\n\nstatic MfClassicError mf_classic_poller_read_value_handler(\n    MfClassicPoller* poller,\n    MfClassicPollerContextData* data) {\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        error = mf_classic_poller_auth(\n            poller,\n            data->read_value_context.block_num,\n            &data->read_value_context.key,\n            data->read_value_context.key_type,\n            NULL,\n            false);\n        if(error != MfClassicErrorNone) break;\n\n        MfClassicBlock block = {};\n        error = mf_classic_poller_read_block(poller, data->read_value_context.block_num, &block);\n        if(error != MfClassicErrorNone) break;\n\n        if(!mf_classic_block_to_value(&block, &data->read_value_context.value, NULL)) {\n            error = MfClassicErrorProtocol;\n            break;\n        }\n\n        error = mf_classic_poller_halt(poller);\n        if(error != MfClassicErrorNone) break;\n\n    } while(false);\n\n    return error;\n}\n\nstatic MfClassicError mf_classic_poller_change_value_handler(\n    MfClassicPoller* poller,\n    MfClassicPollerContextData* data) {\n    MfClassicError error = MfClassicErrorNone;\n\n    do {\n        error = mf_classic_poller_auth(\n            poller,\n            data->change_value_context.block_num,\n            &data->change_value_context.key,\n            data->change_value_context.key_type,\n            NULL,\n            false);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_value_cmd(\n            poller,\n            data->change_value_context.block_num,\n            data->change_value_context.value_cmd,\n            data->change_value_context.data);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_value_transfer(poller, data->change_value_context.block_num);\n        if(error != MfClassicErrorNone) break;\n\n        MfClassicBlock block = {};\n        error = mf_classic_poller_read_block(poller, data->change_value_context.block_num, &block);\n        if(error != MfClassicErrorNone) break;\n\n        error = mf_classic_poller_halt(poller);\n        if(error != MfClassicErrorNone) break;\n\n        if(!mf_classic_block_to_value(&block, &data->change_value_context.new_value, NULL)) {\n            error = MfClassicErrorProtocol;\n            break;\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic const MfClassicPollerCmdHandler mf_classic_poller_cmd_handlers[MfClassicPollerCmdTypeNum] = {\n    [MfClassicPollerCmdTypeCollectNt] = mf_classic_poller_collect_nt_handler,\n    [MfClassicPollerCmdTypeAuth] = mf_classic_poller_auth_handler,\n    [MfClassicPollerCmdTypeReadBlock] = mf_classic_poller_read_block_handler,\n    [MfClassicPollerCmdTypeWriteBlock] = mf_classic_poller_write_block_handler,\n    [MfClassicPollerCmdTypeReadValue] = mf_classic_poller_read_value_handler,\n    [MfClassicPollerCmdTypeChangeValue] = mf_classic_poller_change_value_handler,\n};\n\nstatic NfcCommand mf_classic_poller_cmd_callback(NfcGenericEventEx event, void* context) {\n    furi_assert(event.poller);\n    furi_assert(event.parent_event_data);\n    furi_assert(context);\n\n    MfClassicPollerContext* poller_context = context;\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.parent_event_data;\n    MfClassicPoller* mfc_poller = event.poller;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        poller_context->error = mf_classic_poller_cmd_handlers[poller_context->cmd_type](\n            mfc_poller, &poller_context->data);\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        poller_context->error = mf_classic_process_error(iso14443_3a_event->data->error);\n    }\n\n    furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT);\n\n    return NfcCommandStop;\n}\n\nstatic MfClassicError mf_classic_poller_cmd_execute(Nfc* nfc, MfClassicPollerContext* poller_ctx) {\n    furi_assert(poller_ctx->cmd_type < MfClassicPollerCmdTypeNum);\n\n    poller_ctx->thread_id = furi_thread_get_current_id();\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic);\n    nfc_poller_start_ex(poller, mf_classic_poller_cmd_callback, poller_ctx);\n    furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    return poller_ctx->error;\n}\n\nMfClassicError mf_classic_poller_sync_collect_nt(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt) {\n    furi_check(nfc);\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeCollectNt,\n        .data.collect_nt_context.block = block_num,\n        .data.collect_nt_context.key_type = key_type,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfClassicErrorNone) {\n        if(nt) {\n            *nt = poller_context.data.collect_nt_context.nt;\n        }\n    }\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_auth(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data) {\n    furi_check(nfc);\n    furi_check(key);\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeAuth,\n        .data.auth_context.block_num = block_num,\n        .data.auth_context.key = *key,\n        .data.auth_context.key_type = key_type,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfClassicErrorNone) {\n        if(data) {\n            *data = poller_context.data.auth_context;\n        }\n    }\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_read_block(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicBlock* data) {\n    furi_check(nfc);\n    furi_check(key);\n    furi_check(data);\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeReadBlock,\n        .data.read_block_context.block_num = block_num,\n        .data.read_block_context.key = *key,\n        .data.read_block_context.key_type = key_type,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfClassicErrorNone) {\n        *data = poller_context.data.read_block_context.block;\n    }\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_write_block(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicBlock* data) {\n    furi_check(nfc);\n    furi_check(key);\n    furi_check(data);\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeWriteBlock,\n        .data.write_block_context.block_num = block_num,\n        .data.write_block_context.key = *key,\n        .data.write_block_context.key_type = key_type,\n        .data.write_block_context.block = *data,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_read_value(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    int32_t* value) {\n    furi_check(nfc);\n    furi_check(key);\n    furi_check(value);\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeReadValue,\n        .data.write_block_context.block_num = block_num,\n        .data.write_block_context.key = *key,\n        .data.write_block_context.key_type = key_type,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfClassicErrorNone) {\n        *value = poller_context.data.read_value_context.value;\n    }\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_change_value(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    int32_t data,\n    int32_t* new_value) {\n    furi_check(nfc);\n    furi_check(key);\n    furi_check(new_value);\n\n    MfClassicValueCommand command = MfClassicValueCommandRestore;\n    int32_t command_data = 0;\n    if(data > 0) {\n        command = MfClassicValueCommandIncrement;\n        command_data = data;\n    } else if(data < 0) {\n        command = MfClassicValueCommandDecrement;\n        command_data = -data;\n    }\n\n    MfClassicPollerContext poller_context = {\n        .cmd_type = MfClassicPollerCmdTypeChangeValue,\n        .data.change_value_context.block_num = block_num,\n        .data.change_value_context.key = *key,\n        .data.change_value_context.key_type = key_type,\n        .data.change_value_context.value_cmd = command,\n        .data.change_value_context.data = command_data,\n    };\n\n    MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfClassicErrorNone) {\n        *new_value = poller_context.data.change_value_context.new_value;\n    }\n\n    return error;\n}\n\nstatic bool mf_classic_poller_read_get_next_key(\n    MfClassicReadContext* read_ctx,\n    uint8_t* sector_num,\n    MfClassicKey* key,\n    MfClassicKeyType* key_type) {\n    bool next_key_found = false;\n\n    for(uint8_t i = read_ctx->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {\n        if(FURI_BIT(read_ctx->keys.key_a_mask, i)) {\n            FURI_BIT_CLEAR(read_ctx->keys.key_a_mask, i);\n            *key = read_ctx->keys.key_a[i];\n            *key_type = MfClassicKeyTypeA;\n            *sector_num = i;\n\n            next_key_found = true;\n            break;\n        }\n        if(FURI_BIT(read_ctx->keys.key_b_mask, i)) {\n            FURI_BIT_CLEAR(read_ctx->keys.key_b_mask, i);\n            *key = read_ctx->keys.key_b[i];\n            *key_type = MfClassicKeyTypeB;\n            *sector_num = i;\n\n            next_key_found = true;\n            read_ctx->current_sector = i;\n            break;\n        }\n    }\n\n    return next_key_found;\n}\n\nNfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfClassic);\n\n    NfcCommand command = NfcCommandContinue;\n    MfClassicPollerContext* poller_context = context;\n    MfClassicPollerEvent* mfc_event = event.event_data;\n\n    if(mfc_event->type == MfClassicPollerEventTypeCardLost) {\n        poller_context->error = MfClassicErrorNotPresent;\n        command = NfcCommandStop;\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {\n        mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;\n    } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {\n        MfClassicPollerEventDataReadSectorRequest* req_data =\n            &mfc_event->data->read_sector_request_data;\n        MfClassicKey key = {};\n        MfClassicKeyType key_type = MfClassicKeyTypeA;\n        uint8_t sector_num = 0;\n        if(mf_classic_poller_read_get_next_key(\n               &poller_context->data.read_context, &sector_num, &key, &key_type)) {\n            req_data->sector_num = sector_num;\n            req_data->key = key;\n            req_data->key_type = key_type;\n            req_data->key_provided = true;\n        } else {\n            req_data->key_provided = false;\n        }\n    } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {\n        command = NfcCommandStop;\n    }\n\n    if(command == NfcCommandStop) {\n        furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT);\n    }\n\n    return command;\n}\n\nMfClassicError\n    mf_classic_poller_sync_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) {\n    furi_check(nfc);\n    furi_check(keys);\n    furi_check(data);\n\n    MfClassicError error = MfClassicErrorNone;\n    MfClassicPollerContext poller_context = {};\n    poller_context.thread_id = furi_thread_get_current_id();\n    poller_context.data.read_context.keys = *keys;\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic);\n    nfc_poller_start(poller, mf_classic_poller_read_callback, &poller_context);\n    furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT);\n\n    nfc_poller_stop(poller);\n\n    const MfClassicData* mfc_data = nfc_poller_get_data(poller);\n    uint8_t sectors_read = 0;\n    uint8_t keys_found = 0;\n\n    mf_classic_get_read_sectors_and_keys(mfc_data, &sectors_read, &keys_found);\n    if((sectors_read == 0) && (keys_found == 0)) {\n        error = MfClassicErrorNotPresent;\n    } else {\n        mf_classic_copy(data, mfc_data);\n        error = mf_classic_is_card_read(mfc_data) ? MfClassicErrorNone : MfClassicErrorPartialRead;\n    }\n\n    nfc_poller_free(poller);\n\n    return error;\n}\n\nMfClassicError mf_classic_poller_sync_detect_type(Nfc* nfc, MfClassicType* type) {\n    furi_check(nfc);\n    furi_check(type);\n\n    MfClassicError error = MfClassicErrorNone;\n\n    const uint8_t mf_classic_verify_block[MfClassicTypeNum] = {\n        [MfClassicTypeMini] = 0,\n        [MfClassicType1k] = 62,\n        [MfClassicType4k] = 254,\n    };\n\n    size_t i = 0;\n    for(i = 0; i < COUNT_OF(mf_classic_verify_block); i++) {\n        error = mf_classic_poller_sync_collect_nt(\n            nfc, mf_classic_verify_block[MfClassicTypeNum - i - 1], MfClassicKeyTypeA, NULL);\n        if(error == MfClassicErrorNone) {\n            *type = MfClassicTypeNum - i - 1;\n            break;\n        }\n    }\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h",
    "content": "#pragma once\n\n#include \"mf_classic.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nMfClassicError mf_classic_poller_sync_collect_nt(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKeyType key_type,\n    MfClassicNt* nt);\n\nMfClassicError mf_classic_poller_sync_auth(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicAuthContext* data);\n\nMfClassicError mf_classic_poller_sync_read_block(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicBlock* data);\n\nMfClassicError mf_classic_poller_sync_write_block(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    MfClassicBlock* data);\n\nMfClassicError mf_classic_poller_sync_read_value(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    int32_t* value);\n\nMfClassicError mf_classic_poller_sync_change_value(\n    Nfc* nfc,\n    uint8_t block_num,\n    MfClassicKey* key,\n    MfClassicKeyType key_type,\n    int32_t data,\n    int32_t* new_value);\n\nMfClassicError mf_classic_poller_sync_detect_type(Nfc* nfc, MfClassicType* type);\n\nMfClassicError\n    mf_classic_poller_sync_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire.c",
    "content": "#include \"mf_desfire_i.h\"\n\n#include <furi.h>\n\n#define MF_DESFIRE_PROTOCOL_NAME \"Mifare DESFire\"\n\n#define MF_DESFIRE_HW_MINOR_TYPE          (0x00)\n#define MF_DESFIRE_HW_MINOR_TYPE_MF3ICD40 (0x02)\n\n#define MF_DESFIRE_HW_MAJOR_TYPE_EV1      (0x01)\n#define MF_DESFIRE_HW_MAJOR_TYPE_EV2      (0x12)\n#define MF_DESFIRE_HW_MAJOR_TYPE_EV2_XL   (0x22)\n#define MF_DESFIRE_HW_MAJOR_TYPE_EV3      (0x33)\n#define MF_DESFIRE_HW_MAJOR_TYPE_MF3ICD40 (0x00)\n\n#define MF_DESFIRE_STORAGE_SIZE_2K       (0x16)\n#define MF_DESFIRE_STORAGE_SIZE_4K       (0x18)\n#define MF_DESFIRE_STORAGE_SIZE_8K       (0x1A)\n#define MF_DESFIRE_STORAGE_SIZE_16K      (0x1C)\n#define MF_DESFIRE_STORAGE_SIZE_32K      (0x1E)\n#define MF_DESFIRE_STORAGE_SIZE_MF3ICD40 (0xFF)\n#define MF_DESFIRE_STORAGE_SIZE_UNKNOWN  (0xFF)\n\n#define MF_DESFIRE_TEST_TYPE_MF3ICD40(major, minor, storage) \\\n    (((major) == MF_DESFIRE_HW_MAJOR_TYPE_MF3ICD40) &&       \\\n     ((minor) == MF_DESFIRE_HW_MINOR_TYPE_MF3ICD40) &&       \\\n     ((storage) == MF_DESFIRE_STORAGE_SIZE_MF3ICD40))\n\nstatic const char* mf_desfire_type_strings[] = {\n    [MfDesfireTypeMF3ICD40] = \"(MF3ICD40)\",\n    [MfDesfireTypeEV1] = \"EV1\",\n    [MfDesfireTypeEV2] = \"EV2\",\n    [MfDesfireTypeEV2XL] = \"EV2 XL\",\n    [MfDesfireTypeEV3] = \"EV3\",\n    [MfDesfireTypeUnknown] = \"UNK\",\n};\n\nstatic const char* mf_desfire_size_strings[] = {\n    [MfDesfireSize2k] = \"2K\",\n    [MfDesfireSize4k] = \"4K\",\n    [MfDesfireSize8k] = \"8K\",\n    [MfDesfireSize16k] = \"16K\",\n    [MfDesfireSize32k] = \"32K\",\n    [MfDesfireSizeUnknown] = \"\",\n};\n\nconst NfcDeviceBase nfc_device_mf_desfire = {\n    .protocol_name = MF_DESFIRE_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)mf_desfire_alloc,\n    .free = (NfcDeviceFree)mf_desfire_free,\n    .reset = (NfcDeviceReset)mf_desfire_reset,\n    .copy = (NfcDeviceCopy)mf_desfire_copy,\n    .verify = (NfcDeviceVerify)mf_desfire_verify,\n    .load = (NfcDeviceLoad)mf_desfire_load,\n    .save = (NfcDeviceSave)mf_desfire_save,\n    .is_equal = (NfcDeviceEqual)mf_desfire_is_equal,\n    .get_name = (NfcDeviceGetName)mf_desfire_get_device_name,\n    .get_uid = (NfcDeviceGetUid)mf_desfire_get_uid,\n    .set_uid = (NfcDeviceSetUid)mf_desfire_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data,\n};\n\nMfDesfireData* mf_desfire_alloc(void) {\n    MfDesfireData* data = malloc(sizeof(MfDesfireData));\n    data->iso14443_4a_data = iso14443_4a_alloc();\n    data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);\n    data->application_ids = simple_array_alloc(&mf_desfire_app_id_array_config);\n    data->applications = simple_array_alloc(&mf_desfire_application_array_config);\n    data->device_name = furi_string_alloc();\n    return data;\n}\n\nvoid mf_desfire_free(MfDesfireData* data) {\n    furi_check(data);\n\n    mf_desfire_reset(data);\n    simple_array_free(data->applications);\n    simple_array_free(data->application_ids);\n    simple_array_free(data->master_key_versions);\n    iso14443_4a_free(data->iso14443_4a_data);\n    furi_string_free(data->device_name);\n    free(data);\n}\n\nvoid mf_desfire_reset(MfDesfireData* data) {\n    furi_check(data);\n\n    iso14443_4a_reset(data->iso14443_4a_data);\n\n    memset(&data->version, 0, sizeof(MfDesfireVersion));\n    memset(&data->free_memory, 0, sizeof(MfDesfireFreeMemory));\n\n    simple_array_reset(data->master_key_versions);\n    simple_array_reset(data->application_ids);\n    simple_array_reset(data->applications);\n}\n\nvoid mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    mf_desfire_reset(data);\n\n    iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);\n\n    data->version = other->version;\n    data->free_memory = other->free_memory;\n    data->master_key_settings = other->master_key_settings;\n\n    simple_array_copy(data->master_key_versions, other->master_key_versions);\n    simple_array_copy(data->application_ids, other->application_ids);\n    simple_array_copy(data->applications, other->applications);\n}\n\nbool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) {\n    UNUSED(data);\n    return furi_string_equal_str(device_type, MF_DESFIRE_PROTOCOL_NAME);\n}\n\nbool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* prefix = furi_string_alloc();\n\n    bool success = false;\n\n    do {\n        if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;\n\n        if(!mf_desfire_version_load(&data->version, ff)) break;\n        if(!mf_desfire_free_memory_load(&data->free_memory, ff)) break;\n        if(!mf_desfire_key_settings_load(\n               &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))\n            break;\n\n        const uint32_t master_key_version_count = data->master_key_settings.max_keys;\n        simple_array_init(data->master_key_versions, master_key_version_count);\n\n        uint32_t i;\n        for(i = 0; i < master_key_version_count; ++i) {\n            if(!mf_desfire_key_version_load(\n                   simple_array_get(data->master_key_versions, i),\n                   MF_DESFIRE_FFF_PICC_PREFIX,\n                   i,\n                   ff))\n                break;\n        }\n\n        if(i != master_key_version_count) break;\n\n        uint32_t application_count;\n        if(!mf_desfire_application_count_load(&application_count, ff)) break;\n\n        if(application_count > 0) {\n            simple_array_init(data->application_ids, application_count);\n            if(!mf_desfire_application_ids_load(\n                   simple_array_get_data(data->application_ids), application_count, ff))\n                break;\n\n            simple_array_init(data->applications, application_count);\n            for(i = 0; i < application_count; ++i) {\n                const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);\n                furi_string_printf(\n                    prefix,\n                    \"%s %02x%02x%02x\",\n                    MF_DESFIRE_FFF_APP_PREFIX,\n                    app_id->data[0],\n                    app_id->data[1],\n                    app_id->data[2]);\n\n                if(!mf_desfire_application_load(\n                       simple_array_get(data->applications, i), furi_string_get_cstr(prefix), ff))\n                    break;\n            }\n\n            if(i != application_count) break;\n        }\n\n        success = true;\n    } while(false);\n\n    furi_string_free(prefix);\n    return success;\n}\n\nbool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* prefix = furi_string_alloc();\n\n    bool success = false;\n\n    do {\n        if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;\n\n        if(!flipper_format_write_comment_cstr(ff, MF_DESFIRE_PROTOCOL_NAME \" specific data\"))\n            break;\n        if(!mf_desfire_version_save(&data->version, ff)) break;\n        if(!mf_desfire_free_memory_save(&data->free_memory, ff)) break;\n        if(!mf_desfire_key_settings_save(\n               &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))\n            break;\n\n        const uint32_t master_key_version_count =\n            simple_array_get_count(data->master_key_versions);\n\n        uint32_t i;\n        for(i = 0; i < master_key_version_count; ++i) {\n            if(!mf_desfire_key_version_save(\n                   simple_array_cget(data->master_key_versions, i),\n                   MF_DESFIRE_FFF_PICC_PREFIX,\n                   i,\n                   ff))\n                break;\n        }\n\n        if(i != master_key_version_count) break;\n\n        const uint32_t application_count = simple_array_get_count(data->application_ids);\n        if(!mf_desfire_application_count_save(&application_count, ff)) break;\n\n        if(application_count > 0) {\n            if(!mf_desfire_application_ids_save(\n                   simple_array_cget_data(data->application_ids), application_count, ff))\n                break;\n\n            for(i = 0; i < application_count; ++i) {\n                const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);\n                furi_string_printf(\n                    prefix,\n                    \"%s %02x%02x%02x\",\n                    MF_DESFIRE_FFF_APP_PREFIX,\n                    app_id->data[0],\n                    app_id->data[1],\n                    app_id->data[2]);\n\n                const MfDesfireApplication* app = simple_array_cget(data->applications, i);\n                if(!mf_desfire_application_save(app, furi_string_get_cstr(prefix), ff)) break;\n            }\n\n            if(i != application_count) break;\n        }\n\n        success = true;\n    } while(false);\n\n    furi_string_free(prefix);\n    return success;\n}\n\nbool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&\n           memcmp(&data->version, &other->version, sizeof(MfDesfireVersion)) == 0 &&\n           memcmp( //-V1103\n               &data->free_memory,\n               &other->free_memory,\n               sizeof(MfDesfireFreeMemory)) == 0 &&\n           memcmp(\n               &data->master_key_settings,\n               &other->master_key_settings,\n               sizeof(MfDesfireKeySettings)) == 0 &&\n           simple_array_is_equal(data->master_key_versions, other->master_key_versions) &&\n           simple_array_is_equal(data->application_ids, other->application_ids) &&\n           simple_array_is_equal(data->applications, other->applications);\n}\n\nstatic MfDesfireType mf_desfire_get_type_from_version(const MfDesfireVersion* const version) {\n    MfDesfireType type = MfDesfireTypeUnknown;\n\n    switch(version->hw_major) {\n    case MF_DESFIRE_HW_MAJOR_TYPE_EV1:\n        type = MfDesfireTypeEV1;\n        break;\n    case MF_DESFIRE_HW_MAJOR_TYPE_EV2:\n        type = MfDesfireTypeEV2;\n        break;\n    case MF_DESFIRE_HW_MAJOR_TYPE_EV2_XL:\n        type = MfDesfireTypeEV2XL;\n        break;\n    case MF_DESFIRE_HW_MAJOR_TYPE_EV3:\n        type = MfDesfireTypeEV3;\n        break;\n    default:\n        if(MF_DESFIRE_TEST_TYPE_MF3ICD40(version->hw_major, version->hw_minor, version->hw_storage))\n            type = MfDesfireTypeMF3ICD40;\n        break;\n    }\n\n    return type;\n}\n\nstatic MfDesfireSize mf_desfire_get_size_from_version(const MfDesfireVersion* const version) {\n    MfDesfireSize size = MfDesfireSizeUnknown;\n\n    switch(version->hw_storage) {\n    case MF_DESFIRE_STORAGE_SIZE_2K:\n        size = MfDesfireSize2k;\n        break;\n    case MF_DESFIRE_STORAGE_SIZE_4K:\n        size = MfDesfireSize4k;\n        break;\n    case MF_DESFIRE_STORAGE_SIZE_8K:\n        size = MfDesfireSize8k;\n        break;\n    case MF_DESFIRE_STORAGE_SIZE_16K:\n        size = MfDesfireSize16k;\n        break;\n    case MF_DESFIRE_STORAGE_SIZE_32K:\n        size = MfDesfireSize32k;\n        break;\n    default:\n        if(MF_DESFIRE_TEST_TYPE_MF3ICD40(version->hw_major, version->hw_minor, version->hw_storage))\n            size = MfDesfireSize4k;\n        break;\n    }\n\n    return size;\n}\n\nconst char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n\n    const MfDesfireType type = mf_desfire_get_type_from_version(&data->version);\n    const MfDesfireSize size = mf_desfire_get_size_from_version(&data->version);\n\n    if(type == MfDesfireTypeUnknown) {\n        furi_string_printf(data->device_name, \"Unknown %s\", MF_DESFIRE_PROTOCOL_NAME);\n    } else if(name_type == NfcDeviceNameTypeFull) {\n        furi_string_printf(\n            data->device_name,\n            \"%s %s %s\",\n            MF_DESFIRE_PROTOCOL_NAME,\n            mf_desfire_type_strings[type],\n            mf_desfire_size_strings[size]);\n    } else {\n        furi_string_printf(\n            data->device_name,\n            \"%s %s\",\n            mf_desfire_type_strings[type],\n            mf_desfire_size_strings[size]);\n    }\n\n    return furi_string_get_cstr(data->device_name);\n}\n\nconst uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) {\n    furi_check(data);\n    furi_check(uid_len);\n\n    return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);\n}\n\nbool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);\n}\n\nIso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) {\n    furi_check(data);\n\n    return data->iso14443_4a_data;\n}\n\nconst MfDesfireApplication*\n    mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) {\n    furi_check(data);\n    furi_check(app_id);\n\n    MfDesfireApplication* app = NULL;\n\n    for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {\n        const MfDesfireApplicationId* current_app_id = simple_array_cget(data->application_ids, i);\n        if(memcmp(app_id, current_app_id, sizeof(MfDesfireApplicationId)) == 0) {\n            app = simple_array_get(data->applications, i);\n        }\n    }\n\n    return app;\n}\n\nconst MfDesfireFileSettings*\n    mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {\n    furi_check(data);\n    furi_check(file_id);\n\n    MfDesfireFileSettings* file_settings = NULL;\n\n    for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {\n        const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);\n        if(*file_id == *current_file_id) {\n            file_settings = simple_array_get(data->file_settings, i);\n        }\n    }\n\n    return file_settings;\n}\n\nconst MfDesfireFileData*\n    mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {\n    furi_check(data);\n    furi_check(file_id);\n\n    MfDesfireFileData* file_data = NULL;\n\n    for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {\n        const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);\n        if(*file_id == *current_file_id) {\n            file_data = simple_array_get(data->file_data, i);\n        }\n    }\n\n    return file_data;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>\n\n#include <lib/toolbox/simple_array.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_DESFIRE_CMD_GET_VERSION         (0x60)\n#define MF_DESFIRE_CMD_GET_FREE_MEMORY     (0x6E)\n#define MF_DESFIRE_CMD_GET_KEY_SETTINGS    (0x45)\n#define MF_DESFIRE_CMD_GET_KEY_VERSION     (0x64)\n#define MF_DESFIRE_CMD_GET_APPLICATION_IDS (0x6A)\n#define MF_DESFIRE_CMD_SELECT_APPLICATION  (0x5A)\n#define MF_DESFIRE_CMD_GET_FILE_IDS        (0x6F)\n#define MF_DESFIRE_CMD_GET_FILE_SETTINGS   (0xF5)\n\n#define MF_DESFIRE_CMD_READ_DATA    (0xBD)\n#define MF_DESFIRE_CMD_GET_VALUE    (0x6C)\n#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)\n\n#define MF_DESFIRE_MAX_KEYS  (14)\n#define MF_DESFIRE_MAX_FILES (32)\n\n#define MF_DESFIRE_UID_SIZE    (7)\n#define MF_DESFIRE_BATCH_SIZE  (5)\n#define MF_DESFIRE_APP_ID_SIZE (3)\n#define MF_DESFIRE_VALUE_SIZE  (4)\n\ntypedef enum {\n    MfDesfireTypeMF3ICD40,\n    MfDesfireTypeEV1,\n    MfDesfireTypeEV2,\n    MfDesfireTypeEV2XL,\n    MfDesfireTypeEV3,\n\n    MfDesfireTypeUnknown,\n    MfDesfireTypeNum,\n} MfDesfireType;\n\ntypedef enum {\n    MfDesfireSize2k,\n    MfDesfireSize4k,\n    MfDesfireSize8k,\n    MfDesfireSize16k,\n    MfDesfireSize32k,\n\n    MfDesfireSizeUnknown,\n    MfDesfireSizeNum,\n} MfDesfireSize;\n\ntypedef struct {\n    uint8_t hw_vendor;\n    uint8_t hw_type;\n    uint8_t hw_subtype;\n    uint8_t hw_major;\n    uint8_t hw_minor;\n    uint8_t hw_storage;\n    uint8_t hw_proto;\n\n    uint8_t sw_vendor;\n    uint8_t sw_type;\n    uint8_t sw_subtype;\n    uint8_t sw_major;\n    uint8_t sw_minor;\n    uint8_t sw_storage;\n    uint8_t sw_proto;\n\n    uint8_t uid[MF_DESFIRE_UID_SIZE];\n    uint8_t batch[MF_DESFIRE_BATCH_SIZE];\n    uint8_t prod_week;\n    uint8_t prod_year;\n} MfDesfireVersion;\n\ntypedef struct {\n    uint32_t bytes_free;\n    bool is_present;\n} MfDesfireFreeMemory; // EV1+ only\n\ntypedef struct {\n    bool is_master_key_changeable;\n    bool is_free_directory_list;\n    bool is_free_create_delete;\n    bool is_config_changeable;\n    uint8_t change_key_id;\n    uint8_t max_keys;\n    uint8_t flags;\n} MfDesfireKeySettings;\n\ntypedef uint8_t MfDesfireKeyVersion;\n\ntypedef enum {\n    MfDesfireFileTypeStandard = 0,\n    MfDesfireFileTypeBackup = 1,\n    MfDesfireFileTypeValue = 2,\n    MfDesfireFileTypeLinearRecord = 3,\n    MfDesfireFileTypeCyclicRecord = 4,\n    MfDesfireFileTypeTransactionMac = 5,\n} MfDesfireFileType;\n\ntypedef enum {\n    MfDesfireFileCommunicationSettingsPlaintext = 0,\n    MfDesfireFileCommunicationSettingsAuthenticated = 1,\n    MfDesfireFileCommunicationSettingsEnciphered = 3,\n} MfDesfireFileCommunicationSettings;\n\ntypedef uint8_t MfDesfireFileId;\ntypedef uint16_t MfDesfireFileAccessRights;\n\ntypedef struct {\n    MfDesfireFileType type;\n    MfDesfireFileCommunicationSettings comm;\n    MfDesfireFileAccessRights access_rights[MF_DESFIRE_MAX_KEYS];\n    uint8_t access_rights_len;\n    union {\n        struct {\n            uint32_t size;\n        } data;\n        struct {\n            uint32_t lo_limit;\n            uint32_t hi_limit;\n            uint32_t limited_credit_value;\n            bool limited_credit_enabled;\n        } value;\n        struct {\n            uint32_t size;\n            uint32_t max;\n            uint32_t cur;\n        } record;\n        struct {\n            uint8_t key_option;\n            uint8_t key_version;\n            uint32_t counter_limit;\n        } transaction_mac;\n    };\n} MfDesfireFileSettings;\n\ntypedef struct {\n    SimpleArray* data;\n} MfDesfireFileData;\n\ntypedef struct {\n    uint8_t data[MF_DESFIRE_APP_ID_SIZE];\n} MfDesfireApplicationId;\n\ntypedef struct MfDesfireApplication {\n    MfDesfireKeySettings key_settings;\n    SimpleArray* key_versions;\n    SimpleArray* file_ids;\n    SimpleArray* file_settings;\n    SimpleArray* file_data;\n} MfDesfireApplication;\n\ntypedef enum {\n    MfDesfireErrorNone,\n    MfDesfireErrorNotPresent,\n    MfDesfireErrorProtocol,\n    MfDesfireErrorTimeout,\n    MfDesfireErrorAuthentication,\n    MfDesfireErrorCommandNotSupported,\n} MfDesfireError;\n\ntypedef struct {\n    Iso14443_4aData* iso14443_4a_data;\n    MfDesfireVersion version;\n    MfDesfireFreeMemory free_memory;\n    MfDesfireKeySettings master_key_settings;\n    SimpleArray* master_key_versions;\n    SimpleArray* application_ids;\n    SimpleArray* applications;\n    FuriString* device_name;\n} MfDesfireData;\n\nextern const NfcDeviceBase nfc_device_mf_desfire;\n\n// Virtual methods\n\nMfDesfireData* mf_desfire_alloc(void);\n\nvoid mf_desfire_free(MfDesfireData* data);\n\nvoid mf_desfire_reset(MfDesfireData* data);\n\nvoid mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other);\n\nbool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type);\n\nbool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version);\n\nbool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff);\n\nbool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other);\n\nconst char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len);\n\nbool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data);\n\n// Getters and tests\n\nconst MfDesfireApplication*\n    mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id);\n\nconst MfDesfireFileSettings*\n    mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id);\n\nconst MfDesfireFileData*\n    mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_i.c",
    "content": "#include \"mf_desfire_i.h\"\n\n#include <bit_lib/bit_lib.h>\n\n#define TAG \"MfDesfire\"\n\n#define BITS_IN_BYTE (8U)\n\n#define MF_DESFIRE_FFF_VERSION_KEY \\\n    MF_DESFIRE_FFF_PICC_PREFIX \" \" \\\n                               \"Version\"\n#define MF_DESFIRE_FFF_FREE_MEM_KEY \\\n    MF_DESFIRE_FFF_PICC_PREFIX \" \"  \\\n                               \"Free Memory\"\n\n#define MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY      \"Change Key ID\"\n#define MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY  \"Config Changeable\"\n#define MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY \"Free Create Delete\"\n#define MF_DESFIRE_FFF_FREE_DIR_LIST_KEY      \"Free Directory List\"\n#define MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY     \"Key Changeable\"\n#define MF_DESFIRE_FFF_FLAGS_KEY              \"Flags\"\n#define MF_DESFIRE_FFF_MAX_KEYS_KEY           \"Max Keys\"\n\n#define MF_DESFIRE_FFF_KEY_SUB_PREFIX  \"Key\"\n#define MF_DESFIRE_FFF_KEY_VERSION_KEY \"Version\"\n\n#define MF_DESFIRE_FFF_APPLICATION_COUNT_KEY \\\n    MF_DESFIRE_FFF_APP_PREFIX \" \"            \\\n                              \"Count\"\n#define MF_DESFIRE_FFF_APPLICATION_IDS_KEY \\\n    MF_DESFIRE_FFF_APP_PREFIX \" \"          \\\n                              \"IDs\"\n\n#define MF_DESFIRE_FFF_FILE_SUB_PREFIX \"File\"\n#define MF_DESFIRE_FFF_FILE_IDS_KEY    \\\n    MF_DESFIRE_FFF_FILE_SUB_PREFIX \" \" \\\n                                   \"IDs\"\n#define MF_DESFIRE_FFF_FILE_TYPE_KEY          \"Type\"\n#define MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY \"Communication Settings\"\n#define MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY \"Access Rights\"\n\n#define MF_DESFIRE_FFF_FILE_SIZE_KEY \"Size\"\n\n#define MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY             \"Hi Limit\"\n#define MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY             \"Lo Limit\"\n#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY   \"Limited Credit Value\"\n#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY \"Limited Credit Enabled\"\n\n#define MF_DESFIRE_FFF_FILE_MAX_KEY \"Max\"\n#define MF_DESFIRE_FFF_FILE_CUR_KEY \"Cur\"\n\n#define MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY    \"Key Option\"\n#define MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY   \"Key Version\"\n#define MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY \"Counter Limit\"\n\nbool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {\n    const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion);\n\n    if(can_parse) {\n        bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion));\n    }\n\n    return can_parse;\n}\n\nbool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) {\n    typedef struct FURI_PACKED {\n        uint32_t bytes_free : 3 * BITS_IN_BYTE;\n    } MfDesfireFreeMemoryLayout;\n\n    const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireFreeMemoryLayout);\n\n    if(can_parse) {\n        MfDesfireFreeMemoryLayout layout;\n        bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFreeMemoryLayout));\n        data->bytes_free = layout.bytes_free;\n    }\n\n    data->is_present = can_parse;\n\n    return can_parse;\n}\n\nbool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {\n    typedef struct FURI_PACKED {\n        bool is_master_key_changeable : 1;\n        bool is_free_directory_list   : 1;\n        bool is_free_create_delete    : 1;\n        bool is_config_changeable     : 1;\n        uint8_t change_key_id         : 4;\n        uint8_t max_keys              : 4;\n        uint8_t flags                 : 4;\n    } MfDesfireKeySettingsLayout;\n\n    const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout);\n\n    if(can_parse) {\n        MfDesfireKeySettingsLayout layout;\n        bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireKeySettingsLayout));\n\n        data->is_master_key_changeable = layout.is_master_key_changeable;\n        data->is_free_directory_list = layout.is_free_directory_list;\n        data->is_free_create_delete = layout.is_free_create_delete;\n        data->is_config_changeable = layout.is_config_changeable;\n\n        data->change_key_id = layout.change_key_id;\n        data->max_keys = layout.max_keys;\n        data->flags = layout.flags;\n    }\n\n    return can_parse;\n}\n\nbool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) {\n    const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion);\n\n    if(can_parse) {\n        bit_buffer_write_bytes(buf, data, sizeof(MfDesfireKeyVersion));\n    }\n\n    return can_parse;\n}\n\nbool mf_desfire_application_id_parse(\n    MfDesfireApplicationId* data,\n    uint32_t index,\n    const BitBuffer* buf) {\n    const bool can_parse =\n        bit_buffer_get_size_bytes(buf) >=\n        (index * sizeof(MfDesfireApplicationId) + sizeof(MfDesfireApplicationId));\n\n    if(can_parse) {\n        bit_buffer_write_bytes_mid(\n            buf, data, index * sizeof(MfDesfireApplicationId), sizeof(MfDesfireApplicationId));\n    }\n\n    return can_parse;\n}\n\nbool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf) {\n    const bool can_parse = bit_buffer_get_size_bytes(buf) >=\n                           (index * sizeof(MfDesfireFileId) + sizeof(MfDesfireFileId));\n    if(can_parse) {\n        bit_buffer_write_bytes_mid(\n            buf, data, index * sizeof(MfDesfireFileId), sizeof(MfDesfireFileId));\n    }\n\n    return can_parse;\n}\n\nbool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf) {\n    bool parsed = false;\n\n    typedef struct FURI_PACKED {\n        uint8_t type;\n        uint8_t comm;\n        uint16_t access_rights;\n    } MfDesfireFileSettingsHeader;\n\n    typedef struct FURI_PACKED {\n        uint32_t size : 3 * BITS_IN_BYTE;\n    } MfDesfireFileSettingsData;\n\n    typedef struct FURI_PACKED {\n        uint32_t lo_limit;\n        uint32_t hi_limit;\n        uint32_t limited_credit_value;\n        uint8_t limited_credit_enabled;\n    } MfDesfireFileSettingsValue;\n\n    typedef struct FURI_PACKED {\n        uint32_t size : 3 * BITS_IN_BYTE;\n        uint32_t max  : 3 * BITS_IN_BYTE;\n        uint32_t cur  : 3 * BITS_IN_BYTE;\n    } MfDesfireFileSettingsRecord;\n\n    typedef struct FURI_PACKED {\n        uint8_t key_option;\n        uint8_t key_version;\n        uint8_t counter_limit[];\n    } MfDesfireFileSettingsTransactionMac;\n\n    typedef struct FURI_PACKED {\n        MfDesfireFileSettingsHeader header;\n        union {\n            MfDesfireFileSettingsData data;\n            MfDesfireFileSettingsValue value;\n            MfDesfireFileSettingsRecord record;\n            MfDesfireFileSettingsTransactionMac transaction_mac;\n        };\n    } MfDesfireFileSettingsLayout;\n\n    MfDesfireFileSettings file_settings_temp = {};\n    do {\n        const size_t data_size = bit_buffer_get_size_bytes(buf);\n        const uint8_t* data_ptr = bit_buffer_get_data(buf);\n        const size_t min_data_size =\n            sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsTransactionMac);\n\n        if(data_size < min_data_size) {\n            FURI_LOG_E(\n                TAG, \"File settings size %zu less than minimum %zu\", data_size, min_data_size);\n            break;\n        }\n\n        size_t bytes_processed = sizeof(MfDesfireFileSettingsHeader);\n        MfDesfireFileSettingsLayout layout = {};\n        memcpy(&layout.header, data_ptr, sizeof(MfDesfireFileSettingsHeader));\n        bool has_additional_access_rights = (layout.header.comm & 0x80) != 0;\n\n        file_settings_temp.type = layout.header.type;\n        file_settings_temp.comm = layout.header.comm & 0x03;\n        file_settings_temp.access_rights_len = 1;\n        file_settings_temp.access_rights[0] = layout.header.access_rights;\n\n        if(file_settings_temp.type == MfDesfireFileTypeStandard ||\n           file_settings_temp.type == MfDesfireFileTypeBackup) {\n            memcpy(&layout.data, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsData));\n            file_settings_temp.data.size = layout.data.size;\n            bytes_processed += sizeof(MfDesfireFileSettingsData);\n        } else if(file_settings_temp.type == MfDesfireFileTypeValue) {\n            memcpy(&layout.value, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsValue));\n            file_settings_temp.value.lo_limit = layout.value.lo_limit;\n            file_settings_temp.value.hi_limit = layout.value.hi_limit;\n            file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;\n            file_settings_temp.value.limited_credit_enabled = layout.value.limited_credit_enabled;\n            bytes_processed += sizeof(MfDesfireFileSettingsValue);\n        } else if(\n            file_settings_temp.type == MfDesfireFileTypeLinearRecord ||\n            file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {\n            memcpy(\n                &layout.record, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsRecord));\n            file_settings_temp.record.size = layout.record.size;\n            file_settings_temp.record.max = layout.record.max;\n            file_settings_temp.record.cur = layout.record.cur;\n            bytes_processed += sizeof(MfDesfireFileSettingsRecord);\n        } else if(file_settings_temp.type == MfDesfireFileTypeTransactionMac) {\n            const bool has_counter_limit = (layout.header.comm & 0x20) != 0;\n            memcpy(\n                &layout.transaction_mac,\n                &data_ptr[bytes_processed],\n                sizeof(MfDesfireFileSettingsTransactionMac));\n            file_settings_temp.transaction_mac.key_option = layout.transaction_mac.key_option;\n            file_settings_temp.transaction_mac.key_version = layout.transaction_mac.key_version;\n            if(!has_counter_limit) {\n                file_settings_temp.transaction_mac.counter_limit = 0;\n            } else {\n                // AES (4b) or LRP (2b)\n                const size_t counter_limit_size = (layout.transaction_mac.key_option & 0x02) ? 4 :\n                                                                                               2;\n                memcpy(\n                    &layout.transaction_mac,\n                    &data_ptr[bytes_processed],\n                    sizeof(MfDesfireFileSettingsTransactionMac) + counter_limit_size);\n                file_settings_temp.transaction_mac.counter_limit = bit_lib_bytes_to_num_be(\n                    layout.transaction_mac.counter_limit, counter_limit_size);\n                bytes_processed += counter_limit_size;\n            }\n            bytes_processed += sizeof(MfDesfireFileSettingsTransactionMac);\n        } else {\n            FURI_LOG_W(TAG, \"Unknown file type: %02x\", file_settings_temp.type);\n            break;\n        }\n\n        if(has_additional_access_rights) {\n            uint8_t additional_access_rights_len = bit_buffer_get_byte(buf, bytes_processed);\n            FURI_LOG_D(TAG, \"Has additional rights: %d\", additional_access_rights_len);\n            if(data_size != bytes_processed +\n                                additional_access_rights_len * sizeof(MfDesfireFileAccessRights) +\n                                1) {\n                FURI_LOG_W(TAG, \"Unexpected command length: %zu\", data_size);\n                for(size_t i = 0; i < bit_buffer_get_size_bytes(buf); i++) {\n                    printf(\"%02X \", bit_buffer_get_byte(buf, i));\n                }\n                printf(\"\\r\\n\");\n                break;\n            }\n            if(additional_access_rights_len >\n               MF_DESFIRE_MAX_KEYS * sizeof(MfDesfireFileAccessRights))\n                break;\n\n            memcpy(\n                &file_settings_temp.access_rights[1],\n                &data_ptr[bytes_processed],\n                additional_access_rights_len * sizeof(MfDesfireFileAccessRights));\n            file_settings_temp.access_rights_len += additional_access_rights_len;\n        }\n\n        *data = file_settings_temp;\n        parsed = true;\n    } while(false);\n\n    return parsed;\n}\n\nbool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf) {\n    const size_t data_size = bit_buffer_get_size_bytes(buf);\n\n    if(data_size > 0) {\n        simple_array_init(data->data, data_size);\n        bit_buffer_write_bytes(buf, simple_array_get_data(data->data), data_size);\n    }\n\n    // Success no matter whether there is data or not\n    return true;\n}\n\nvoid mf_desfire_file_data_init(MfDesfireFileData* data) {\n    data->data = simple_array_alloc(&simple_array_config_uint8_t);\n}\n\nvoid mf_desfire_application_init(MfDesfireApplication* data) {\n    data->key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);\n    data->file_ids = simple_array_alloc(&mf_desfire_file_id_array_config);\n    data->file_settings = simple_array_alloc(&mf_desfire_file_settings_array_config);\n    data->file_data = simple_array_alloc(&mf_desfire_file_data_array_config);\n}\n\nvoid mf_desfire_file_data_reset(MfDesfireFileData* data) {\n    simple_array_free(data->data);\n    memset(data, 0, sizeof(MfDesfireFileData));\n}\n\nvoid mf_desfire_application_reset(MfDesfireApplication* data) {\n    simple_array_free(data->key_versions);\n    simple_array_free(data->file_ids);\n    simple_array_free(data->file_settings);\n    simple_array_free(data->file_data);\n    memset(data, 0, sizeof(MfDesfireApplication));\n}\n\nvoid mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other) {\n    simple_array_copy(data->data, other->data);\n}\n\nvoid mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other) {\n    data->key_settings = other->key_settings;\n    simple_array_copy(data->key_versions, other->key_versions);\n    simple_array_copy(data->file_ids, other->file_ids);\n    simple_array_copy(data->file_settings, other->file_settings);\n    simple_array_copy(data->file_data, other->file_data);\n}\n\nbool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff) {\n    return flipper_format_read_hex(\n        ff, MF_DESFIRE_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfDesfireVersion));\n}\n\nbool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff) {\n    data->is_present = flipper_format_key_exist(ff, MF_DESFIRE_FFF_FREE_MEM_KEY);\n    return data->is_present ?\n               flipper_format_read_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :\n               true;\n}\n\nbool mf_desfire_key_settings_load(\n    MfDesfireKeySettings* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    bool success = false;\n\n    FuriString* key = furi_string_alloc();\n\n    do {\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);\n        if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);\n        if(!flipper_format_read_bool(ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);\n        if(!flipper_format_read_bool(\n               ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);\n        if(!flipper_format_read_bool(\n               ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);\n        if(!flipper_format_read_bool(\n               ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FLAGS_KEY);\n        if(flipper_format_key_exist(ff, furi_string_get_cstr(key))) {\n            if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;\n        }\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);\n        if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;\n\n        success = true;\n    } while(false);\n\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_key_version_load(\n    MfDesfireKeyVersion* data,\n    const char* prefix,\n    uint32_t index,\n    FlipperFormat* ff) {\n    FuriString* key = furi_string_alloc_printf(\n        \"%s %s %lu %s\",\n        prefix,\n        MF_DESFIRE_FFF_KEY_SUB_PREFIX,\n        index,\n        MF_DESFIRE_FFF_KEY_VERSION_KEY);\n    const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, 1);\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff) {\n    FuriString* key = furi_string_alloc_printf(\"%s %s\", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);\n    const bool success = flipper_format_get_value_count(ff, furi_string_get_cstr(key), data);\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_ids_load(\n    MfDesfireFileId* data,\n    uint32_t count,\n    const char* prefix,\n    FlipperFormat* ff) {\n    FuriString* key = furi_string_alloc_printf(\"%s %s\", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);\n    const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, count);\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_settings_load(\n    MfDesfireFileSettings* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    bool success = false;\n\n    FuriString* key = furi_string_alloc();\n\n    do {\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);\n        if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->type, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);\n        if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->comm, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);\n        uint32_t access_rights_len = 0;\n        if(!flipper_format_get_value_count(ff, furi_string_get_cstr(key), &access_rights_len))\n            break;\n        if((access_rights_len == 0) || ((access_rights_len % 2) != 0)) break;\n        if(!flipper_format_read_hex(\n               ff, furi_string_get_cstr(key), (uint8_t*)&data->access_rights, access_rights_len))\n            break;\n        data->access_rights_len = access_rights_len / sizeof(MfDesfireFileAccessRights);\n\n        if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))\n                break;\n        } else if(data->type == MfDesfireFileTypeValue) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);\n            if(!flipper_format_read_uint32(\n                   ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);\n            if(!flipper_format_read_bool(\n                   ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))\n                break;\n        } else if(\n            data->type == MfDesfireFileTypeLinearRecord ||\n            data->type == MfDesfireFileTypeCyclicRecord) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);\n            if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))\n                break;\n        } else if(data->type == MfDesfireFileTypeTransactionMac) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);\n            if(!flipper_format_read_hex(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);\n            if(!flipper_format_read_hex(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);\n            if(!flipper_format_read_uint32(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))\n                break;\n        }\n\n        success = true;\n    } while(false);\n\n    furi_string_free(key);\n\n    return success;\n}\n\nbool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff) {\n    bool success = false;\n    do {\n        if(!flipper_format_key_exist(ff, prefix)) {\n            success = true;\n            break;\n        }\n\n        uint32_t data_size;\n        if(!flipper_format_get_value_count(ff, prefix, &data_size)) break;\n\n        simple_array_init(data->data, data_size);\n\n        if(!flipper_format_read_hex(ff, prefix, simple_array_get_data(data->data), data_size))\n            break;\n\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff) {\n    return flipper_format_read_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);\n}\n\nbool mf_desfire_application_ids_load(\n    MfDesfireApplicationId* data,\n    uint32_t count,\n    FlipperFormat* ff) {\n    return flipper_format_read_hex(\n        ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));\n}\n\nbool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff) {\n    FuriString* sub_prefix = furi_string_alloc();\n    bool success = false;\n\n    do {\n        if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break;\n\n        uint32_t i;\n        const uint32_t key_version_count = data->key_settings.max_keys;\n        if(key_version_count) {\n            simple_array_init(data->key_versions, key_version_count);\n\n            for(i = 0; i < key_version_count; ++i) {\n                if(!mf_desfire_key_version_load(\n                       simple_array_get(data->key_versions, i), prefix, i, ff))\n                    break;\n            }\n\n            if(i != key_version_count) break;\n        }\n\n        uint32_t file_count;\n        if(!mf_desfire_file_count_load(&file_count, prefix, ff)) {\n            success = true;\n            break;\n        }\n\n        simple_array_init(data->file_ids, file_count);\n        if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff))\n            break;\n\n        simple_array_init(data->file_settings, file_count);\n        simple_array_init(data->file_data, file_count);\n\n        for(i = 0; i < file_count; ++i) {\n            const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);\n            furi_string_printf(\n                sub_prefix, \"%s %s %u\", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);\n\n            MfDesfireFileSettings* file_settings = simple_array_get(data->file_settings, i);\n            if(!mf_desfire_file_settings_load(file_settings, furi_string_get_cstr(sub_prefix), ff))\n                break;\n\n            MfDesfireFileData* file_data = simple_array_get(data->file_data, i);\n            if(!mf_desfire_file_data_load(file_data, furi_string_get_cstr(sub_prefix), ff)) break;\n        }\n\n        if(i != file_count) break;\n\n        success = true;\n    } while(false);\n\n    furi_string_free(sub_prefix);\n    return success;\n}\n\nbool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff) {\n    return flipper_format_write_hex(\n        ff, MF_DESFIRE_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfDesfireVersion));\n}\n\nbool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff) {\n    return data->is_present ?\n               flipper_format_write_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :\n               true;\n}\n\nbool mf_desfire_key_settings_save(\n    const MfDesfireKeySettings* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    bool success = false;\n\n    FuriString* key = furi_string_alloc();\n\n    do {\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);\n        if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);\n        if(!flipper_format_write_bool(\n               ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);\n        if(!flipper_format_write_bool(\n               ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);\n        if(!flipper_format_write_bool(\n               ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);\n        if(!flipper_format_write_bool(\n               ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FLAGS_KEY);\n        if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);\n        if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;\n\n        success = true;\n    } while(false);\n\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_key_version_save(\n    const MfDesfireKeyVersion* data,\n    const char* prefix,\n    uint32_t index,\n    FlipperFormat* ff) {\n    FuriString* key = furi_string_alloc_printf(\n        \"%s %s %lu %s\",\n        prefix,\n        MF_DESFIRE_FFF_KEY_SUB_PREFIX,\n        index,\n        MF_DESFIRE_FFF_KEY_VERSION_KEY);\n    const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, 1);\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_ids_save(\n    const MfDesfireFileId* data,\n    uint32_t count,\n    const char* prefix,\n    FlipperFormat* ff) {\n    FuriString* key = furi_string_alloc_printf(\"%s %s\", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);\n    const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, count);\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_settings_save(\n    const MfDesfireFileSettings* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    bool success = false;\n\n    FuriString* key = furi_string_alloc();\n\n    do {\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);\n        if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->type, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);\n        if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->comm, 1))\n            break;\n\n        furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);\n        if(!flipper_format_write_hex(\n               ff,\n               furi_string_get_cstr(key),\n               (const uint8_t*)data->access_rights,\n               data->access_rights_len * sizeof(MfDesfireFileAccessRights)))\n            break;\n\n        if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);\n            if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))\n                break;\n\n        } else if(data->type == MfDesfireFileTypeValue) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);\n            if(!flipper_format_write_uint32(\n                   ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);\n            if(!flipper_format_write_uint32(\n                   ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);\n            if(!flipper_format_write_uint32(\n                   ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);\n            if(!flipper_format_write_bool(\n                   ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))\n                break;\n        } else if(\n            data->type == MfDesfireFileTypeLinearRecord ||\n            data->type == MfDesfireFileTypeCyclicRecord) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);\n            if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);\n            if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);\n            if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))\n                break;\n        } else if(data->type == MfDesfireFileTypeTransactionMac) {\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);\n            if(!flipper_format_write_hex(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);\n            if(!flipper_format_write_hex(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))\n                break;\n\n            furi_string_printf(key, \"%s %s\", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);\n            if(!flipper_format_write_uint32(\n                   ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))\n                break;\n        }\n\n        success = true;\n    } while(false);\n\n    furi_string_free(key);\n    return success;\n}\n\nbool mf_desfire_file_data_save(\n    const MfDesfireFileData* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    const uint32_t data_size = simple_array_get_count(data->data);\n    return data_size > 0 ? flipper_format_write_hex(\n                               ff, prefix, simple_array_cget_data(data->data), data_size) :\n                           true;\n}\n\nbool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff) {\n    return flipper_format_write_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);\n}\n\nbool mf_desfire_application_ids_save(\n    const MfDesfireApplicationId* data,\n    uint32_t count,\n    FlipperFormat* ff) {\n    return flipper_format_write_hex(\n        ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));\n}\n\nbool mf_desfire_application_save(\n    const MfDesfireApplication* data,\n    const char* prefix,\n    FlipperFormat* ff) {\n    FuriString* sub_prefix = furi_string_alloc();\n    bool success = false;\n\n    do {\n        if(!mf_desfire_key_settings_save(&data->key_settings, prefix, ff)) break;\n\n        const uint32_t key_version_count = data->key_settings.max_keys;\n\n        uint32_t i;\n        for(i = 0; i < key_version_count; ++i) {\n            if(!mf_desfire_key_version_save(\n                   simple_array_cget(data->key_versions, i), prefix, i, ff))\n                break;\n        }\n\n        if(i != key_version_count) break;\n\n        const uint32_t file_count = simple_array_get_count(data->file_ids);\n        if(file_count > 0) {\n            if(!mf_desfire_file_ids_save(\n                   simple_array_get_data(data->file_ids), file_count, prefix, ff))\n                break;\n        }\n\n        for(i = 0; i < file_count; ++i) {\n            const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);\n            furi_string_printf(\n                sub_prefix, \"%s %s %u\", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);\n\n            const MfDesfireFileSettings* file_settings = simple_array_cget(data->file_settings, i);\n            if(!mf_desfire_file_settings_save(file_settings, furi_string_get_cstr(sub_prefix), ff))\n                break;\n\n            const MfDesfireFileData* file_data = simple_array_cget(data->file_data, i);\n            if(!mf_desfire_file_data_save(file_data, furi_string_get_cstr(sub_prefix), ff)) break;\n        }\n\n        if(i != file_count) break;\n\n        success = true;\n    } while(false);\n\n    furi_string_free(sub_prefix);\n    return success;\n}\n\nconst SimpleArrayConfig mf_desfire_key_version_array_config = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(MfDesfireKeyVersion),\n};\n\nconst SimpleArrayConfig mf_desfire_app_id_array_config = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(MfDesfireApplicationId),\n};\n\nconst SimpleArrayConfig mf_desfire_file_id_array_config = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(MfDesfireFileId),\n};\n\nconst SimpleArrayConfig mf_desfire_file_settings_array_config = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(MfDesfireFileSettings),\n};\n\nconst SimpleArrayConfig mf_desfire_file_data_array_config = {\n    .init = (SimpleArrayInit)mf_desfire_file_data_init,\n    .copy = (SimpleArrayCopy)mf_desfire_file_data_copy,\n    .reset = (SimpleArrayReset)mf_desfire_file_data_reset,\n    .type_size = sizeof(MfDesfireData),\n};\n\nconst SimpleArrayConfig mf_desfire_application_array_config = {\n    .init = (SimpleArrayInit)mf_desfire_application_init,\n    .copy = (SimpleArrayCopy)mf_desfire_application_copy,\n    .reset = (SimpleArrayReset)mf_desfire_application_reset,\n    .type_size = sizeof(MfDesfireApplication),\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_i.h",
    "content": "#pragma once\n\n#include \"mf_desfire.h\"\n\n#define MF_DESFIRE_FFF_PICC_PREFIX \"PICC\"\n#define MF_DESFIRE_FFF_APP_PREFIX  \"Application\"\n\n// Successful operation\n#define MF_DESFIRE_STATUS_OPERATION_OK          (0x00)\n// No changes done to backup files, CommitTransaction / AbortTransaction not necessary\n#define MF_DESFIRE_STATUS_NO_CHANGES            (0x0C)\n// Insufficient NV-Memory to complete command\n#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR   (0x0E)\n// Command code not supported\n#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE  (0x1C)\n// CRC or MAC does not match data Padding bytes not valid\n#define MF_DESFIRE_STATUS_INTEGRITY_ERROR       (0x1E)\n// Invalid key number specified\n#define MF_DESFIRE_STATUS_NO_SUCH_KEY           (0x40)\n// Length of command string invalid\n#define MF_DESFIRE_STATUS_LENGTH_ERROR          (0x7E)\n// Current configuration / status does not allow the requested command\n#define MF_DESFIRE_STATUS_PERMISSION_DENIED     (0x9D)\n// Value of the parameter(s) invalid\n#define MF_DESFIRE_STATUS_PARAMETER_ERROR       (0x9E)\n// Requested AID not present on PICC\n#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0)\n// Unrecoverable error within application, application will be disabled\n#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR  (0xA1)\n// Current authentication status does not allow the requested command\n#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR  (0xAE)\n// Additional data frame is expected to be sent\n#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME      (0xAF)\n// Attempt to read/write data from/to beyond the file's/record's limits\n// Attempt to exceed the limits of a value file.\n#define MF_DESFIRE_STATUS_BOUNDARY_ERROR        (0xBE)\n// Unrecoverable error within PICC, PICC will be disabled\n#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR  (0xC1)\n// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD\n#define MF_DESFIRE_STATUS_COMMAND_ABORTED       (0xCA)\n// PICC was disabled by an unrecoverable error\n#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR   (0xCD)\n// Number of Applications limited to 28, no additional CreateApplication possible\n#define MF_DESFIRE_STATUS_COUNT_ERROR           (0xCE)\n// Creation of file/application failed because file/application with same number already exists\n#define MF_DESFIRE_STATUS_DUBLICATE_ERROR       (0xDE)\n// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated\n#define MF_DESFIRE_STATUS_EEPROM_ERROR          (0xEE)\n// Specified file number does not exist\n#define MF_DESFIRE_STATUS_FILE_NOT_FOUND        (0xF0)\n// Unrecoverable error within file, file will be disabled\n#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR  (0xF1)\n\n// SimpleArray configurations\n\nextern const SimpleArrayConfig mf_desfire_key_version_array_config;\nextern const SimpleArrayConfig mf_desfire_app_id_array_config;\nextern const SimpleArrayConfig mf_desfire_file_id_array_config;\nextern const SimpleArrayConfig mf_desfire_file_settings_array_config;\nextern const SimpleArrayConfig mf_desfire_file_data_array_config;\nextern const SimpleArrayConfig mf_desfire_application_array_config;\n\n// Parse internal MfDesfire structures\n\nbool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf);\n\nbool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf);\n\nbool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf);\n\nbool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf);\n\nbool mf_desfire_application_id_parse(\n    MfDesfireApplicationId* data,\n    uint32_t index,\n    const BitBuffer* buf);\n\nbool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf);\n\nbool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf);\n\nbool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf);\n\n// Init internal MfDesfire structures\n\nvoid mf_desfire_file_data_init(MfDesfireFileData* data);\n\nvoid mf_desfire_application_init(MfDesfireApplication* data);\n\n// Reset internal MfDesfire structures\n\nvoid mf_desfire_file_data_reset(MfDesfireFileData* data);\n\nvoid mf_desfire_application_reset(MfDesfireApplication* data);\n\n// Copy internal MfDesfire structures\n\nvoid mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other);\n\nvoid mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other);\n\n// Load internal MfDesfire structures\n\nbool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff);\n\nbool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff);\n\nbool mf_desfire_key_settings_load(\n    MfDesfireKeySettings* data,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_key_version_load(\n    MfDesfireKeyVersion* data,\n    const char* prefix,\n    uint32_t index,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff);\n\nbool mf_desfire_file_ids_load(\n    MfDesfireFileId* data,\n    uint32_t count,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_settings_load(\n    MfDesfireFileSettings* data,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff);\n\nbool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff);\n\nbool mf_desfire_application_ids_load(\n    MfDesfireApplicationId* data,\n    uint32_t count,\n    FlipperFormat* ff);\n\nbool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff);\n\n// Save internal MFDesfire structures\n\nbool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff);\n\nbool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff);\n\nbool mf_desfire_key_settings_save(\n    const MfDesfireKeySettings* data,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_key_version_save(\n    const MfDesfireKeyVersion* data,\n    const char* prefix,\n    uint32_t index,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_ids_save(\n    const MfDesfireFileId* data,\n    uint32_t count,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_settings_save(\n    const MfDesfireFileSettings* data,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_file_data_save(\n    const MfDesfireFileData* data,\n    const char* prefix,\n    FlipperFormat* ff);\n\nbool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff);\n\nbool mf_desfire_application_ids_save(\n    const MfDesfireApplicationId* data,\n    uint32_t count,\n    FlipperFormat* ff);\n\nbool mf_desfire_application_save(\n    const MfDesfireApplication* data,\n    const char* prefix,\n    FlipperFormat* ff);\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_poller.c",
    "content": "#include \"mf_desfire_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"MfDesfirePoller\"\n\n#define MF_DESFIRE_BUF_SIZE        (64U)\n#define MF_DESFIRE_RESULT_BUF_SIZE (512U)\n\ntypedef NfcCommand (*MfDesfirePollerReadHandler)(MfDesfirePoller* instance);\n\nconst MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nstatic MfDesfirePoller* mf_desfire_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {\n    MfDesfirePoller* instance = malloc(sizeof(MfDesfirePoller));\n    instance->iso14443_4a_poller = iso14443_4a_poller;\n    instance->data = mf_desfire_alloc();\n    instance->tx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE);\n    instance->input_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE);\n    instance->result_buffer = bit_buffer_alloc(MF_DESFIRE_RESULT_BUF_SIZE);\n\n    instance->mf_desfire_event.data = &instance->mf_desfire_event_data;\n\n    instance->general_event.protocol = NfcProtocolMfDesfire;\n    instance->general_event.event_data = &instance->mf_desfire_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void mf_desfire_poller_free(MfDesfirePoller* instance) {\n    furi_assert(instance);\n\n    mf_desfire_free(instance->data);\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    bit_buffer_free(instance->input_buffer);\n    bit_buffer_free(instance->result_buffer);\n    free(instance);\n}\n\nstatic NfcCommand mf_desfire_poller_handler_idle(MfDesfirePoller* instance) {\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_reset(instance->result_buffer);\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    iso14443_4a_copy(\n        instance->data->iso14443_4a_data,\n        iso14443_4a_poller_get_data(instance->iso14443_4a_poller));\n\n    instance->state = MfDesfirePollerStateReadVersion;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instance) {\n    instance->error = mf_desfire_poller_read_version(instance, &instance->data->version);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read version success\");\n        instance->state = MfDesfirePollerStateReadFreeMemory;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read version\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    instance->error = mf_desfire_poller_read_free_memory(instance, &instance->data->free_memory);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read free memory success\");\n        instance->state = MfDesfirePollerStateReadMasterKeySettings;\n    } else if(instance->error == MfDesfireErrorNotPresent) {\n        FURI_LOG_D(TAG, \"Read free memory is not present\");\n        instance->state = MfDesfirePollerStateReadMasterKeySettings;\n        command = NfcCommandReset;\n    } else if(instance->error == MfDesfireErrorCommandNotSupported) {\n        FURI_LOG_D(TAG, \"Read free memory is unsupported\");\n        instance->state = MfDesfirePollerStateReadMasterKeySettings;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read free memory\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return command;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {\n    instance->error =\n        mf_desfire_poller_read_key_settings(instance, &instance->data->master_key_settings);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read master key settings success\");\n        instance->state = MfDesfirePollerStateReadMasterKeyVersion;\n    } else if(instance->error == MfDesfireErrorAuthentication) {\n        FURI_LOG_D(TAG, \"Auth is required to read master key settings and app ids\");\n        instance->data->master_key_settings.is_free_directory_list = false;\n        instance->data->master_key_settings.max_keys = 1;\n        instance->state = MfDesfirePollerStateReadMasterKeyVersion;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read master key settings\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePoller* instance) {\n    instance->error = mf_desfire_poller_read_key_versions(\n        instance,\n        instance->data->master_key_versions,\n        instance->data->master_key_settings.max_keys);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read master key version success\");\n        if(instance->data->master_key_settings.is_free_directory_list) {\n            instance->state = MfDesfirePollerStateReadApplicationIds;\n        } else {\n            instance->state = MfDesfirePollerStateReadSuccess;\n        }\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read master key version\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller* instance) {\n    instance->error =\n        mf_desfire_poller_read_application_ids(instance, instance->data->application_ids);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read application ids success\");\n        instance->state = MfDesfirePollerStateReadApplications;\n    } else if(instance->error == MfDesfireErrorAuthentication) {\n        FURI_LOG_D(TAG, \"Read application ids impossible without authentication\");\n        instance->state = MfDesfirePollerStateReadSuccess;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read application ids\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_applications(MfDesfirePoller* instance) {\n    instance->error = mf_desfire_poller_read_applications(\n        instance, instance->data->application_ids, instance->data->applications);\n    if(instance->error == MfDesfireErrorNone) {\n        FURI_LOG_D(TAG, \"Read applications success\");\n        instance->state = MfDesfirePollerStateReadSuccess;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to read applications\");\n        iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n        instance->state = MfDesfirePollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_fail(MfDesfirePoller* instance) {\n    FURI_LOG_D(TAG, \"Read Failed\");\n    iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n    instance->mf_desfire_event.data->error = instance->error;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->state = MfDesfirePollerStateIdle;\n    return command;\n}\n\nstatic NfcCommand mf_desfire_poller_handler_read_success(MfDesfirePoller* instance) {\n    FURI_LOG_D(TAG, \"Read success.\");\n    iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n    instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadSuccess;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic const MfDesfirePollerReadHandler mf_desfire_poller_read_handler[MfDesfirePollerStateNum] = {\n    [MfDesfirePollerStateIdle] = mf_desfire_poller_handler_idle,\n    [MfDesfirePollerStateReadVersion] = mf_desfire_poller_handler_read_version,\n    [MfDesfirePollerStateReadFreeMemory] = mf_desfire_poller_handler_read_free_memory,\n    [MfDesfirePollerStateReadMasterKeySettings] =\n        mf_desfire_poller_handler_read_master_key_settings,\n    [MfDesfirePollerStateReadMasterKeyVersion] = mf_desfire_poller_handler_read_master_key_version,\n    [MfDesfirePollerStateReadApplicationIds] = mf_desfire_poller_handler_read_application_ids,\n    [MfDesfirePollerStateReadApplications] = mf_desfire_poller_handler_read_applications,\n    [MfDesfirePollerStateReadFailed] = mf_desfire_poller_handler_read_fail,\n    [MfDesfirePollerStateReadSuccess] = mf_desfire_poller_handler_read_success,\n};\n\nstatic void mf_desfire_poller_set_callback(\n    MfDesfirePoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand mf_desfire_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n\n    MfDesfirePoller* instance = context;\n    furi_assert(instance);\n    furi_assert(instance->callback);\n\n    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n    furi_assert(iso14443_4a_event);\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        command = mf_desfire_poller_read_handler[instance->state](instance);\n    } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {\n        instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadFailed;\n        command = instance->callback(instance->general_event, instance->context);\n    }\n\n    return command;\n}\n\nstatic bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n\n    MfDesfirePoller* instance = context;\n    furi_assert(instance);\n\n    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n    furi_assert(iso14443_4a_event);\n\n    bool protocol_detected = false;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        do {\n            MfDesfireKeyVersion key_version = 0;\n            MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version);\n            if(error != MfDesfireErrorNone) break;\n\n            MfDesfireVersion version = {};\n            error = mf_desfire_poller_read_version(instance, &version);\n            if(error != MfDesfireErrorNone) break;\n\n            protocol_detected = true;\n        } while(false);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase mf_desfire_poller = {\n    .alloc = (NfcPollerAlloc)mf_desfire_poller_alloc,\n    .free = (NfcPollerFree)mf_desfire_poller_free,\n    .set_callback = (NfcPollerSetCallback)mf_desfire_poller_set_callback,\n    .run = (NfcPollerRun)mf_desfire_poller_run,\n    .detect = (NfcPollerDetect)mf_desfire_poller_detect,\n    .get_data = (NfcPollerGetData)mf_desfire_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_poller.h",
    "content": "#pragma once\n\n#include \"mf_desfire.h\"\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief MfDesfirePoller opaque type definition.\n */\ntypedef struct MfDesfirePoller MfDesfirePoller;\n\n/**\n * @brief Enumeration of possible MfDesfire poller event types.\n */\ntypedef enum {\n    MfDesfirePollerEventTypeReadSuccess, /**< Card was read successfully. */\n    MfDesfirePollerEventTypeReadFailed, /**< Poller failed to read card. */\n} MfDesfirePollerEventType;\n\n/**\n * @brief MfDesfire poller event data.\n */\ntypedef union {\n    MfDesfireError error; /**< Error code indicating card reading fail reason. */\n} MfDesfirePollerEventData;\n\n/**\n * @brief MfDesfire poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    MfDesfirePollerEventType type; /**< Type of emmitted event. */\n    MfDesfirePollerEventData* data; /**< Pointer to event specific data. */\n} MfDesfirePollerEvent;\n\n/**\n * @brief Transmit and receive MfDesfire chunks in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_send_chunks(\n    MfDesfirePoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer);\n\n/**\n * @brief Read MfDesfire card version.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfDesfireVersion structure to be filled with version data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data);\n\n/**\n * @brief Read free memory available on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfDesfireFreeMemory structure to be filled with free memory data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError\n    mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data);\n\n/**\n * @brief Read key settings on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfDesfireKeySettings structure to be filled with key settings data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError\n    mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data);\n\n/**\n * @brief Read key version on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] key_num key number.\n * @param[in] data pointer to the MfDesfireKeyVersion structure to be filled with key version data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_key_version(\n    MfDesfirePoller* instance,\n    uint8_t key_num,\n    MfDesfireKeyVersion* data);\n\n/**\n * @brief Read key versions on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SimpleArray structure to be filled with key versions data.\n * @param[in] count number of key versions to read.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_key_versions(\n    MfDesfirePoller* instance,\n    SimpleArray* data,\n    uint32_t count);\n\n/**\n * @brief Read applications IDs on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SimpleArray structure to be filled with application ids data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError\n    mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data);\n\n/**\n * @brief Select application on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] id pointer to the MfDesfireApplicationId structure with application id to select.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_select_application(\n    MfDesfirePoller* instance,\n    const MfDesfireApplicationId* id);\n\n/**\n * @brief Read file IDs for selected application on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SimpleArray structure to be filled with file ids data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data);\n\n/**\n * @brief Read file settings on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] id file id to read settings for.\n * @param[out] data pointer to the MfDesfireFileSettings structure to be filled with file settings data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_settings(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    MfDesfireFileSettings* data);\n\n/**\n * @brief Read multiple file settings on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] file_ids pointer to the SimpleArray structure array with file ids to read settings for.\n * @param[out] data pointer to the SimpleArray structure array to be filled with file settings data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_settings_multi(\n    MfDesfirePoller* instance,\n    const SimpleArray* file_ids,\n    SimpleArray* data);\n\n/**\n * @brief Read file data on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] id file id to read data from.\n * @param[in] offset offset in bytes to start reading from.\n * @param[in] size number of bytes to read.\n * @param[out] data pointer to the MfDesfireFileData structure to be filled with file data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_data(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    uint32_t offset,\n    size_t size,\n    MfDesfireFileData* data);\n\n/**\n * @brief Read file value on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] id file id to read value from.\n * @param[out] data pointer to the MfDesfireFileData structure to be filled with file value.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_value(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    MfDesfireFileData* data);\n\n/**\n * @brief Read file records on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] id file id to read data from.\n * @param[in] offset offset in bytes to start reading from.\n * @param[in] size number of bytes to read.\n * @param[out] data pointer to the MfDesfireFileData structure to be filled with file records data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_records(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    uint32_t offset,\n    size_t size,\n    MfDesfireFileData* data);\n\n/**\n * @brief Read data from multiple files on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] file_ids pointer to the SimpleArray structure array with files ids to read data from.\n * @param[in] file_settings pointer to the SimpleArray structure array with files settings to read data from.\n * @param[out] data pointer to the SimpleArray structure array to be filled with files data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_file_data_multi(\n    MfDesfirePoller* instance,\n    const SimpleArray* file_ids,\n    const SimpleArray* file_settings,\n    SimpleArray* data);\n\n/**\n * @brief Read application data for selected application on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfDesfireApplication structure to be filled with application data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError\n    mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data);\n\n/**\n * @brief Read multiple applications data on MfDesfire card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] app_ids pointer to the SimpleArray structure array with application ids to read data from.\n * @param[out] data pointer to the SimpleArray structure array to be filled with applications data.\n * @return MfDesfireErrorNone on success, an error code on failure.\n */\nMfDesfireError mf_desfire_poller_read_applications(\n    MfDesfirePoller* instance,\n    const SimpleArray* app_ids,\n    SimpleArray* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase mf_desfire_poller;\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c",
    "content": "#include \"mf_desfire_poller_i.h\"\n\n#include <furi.h>\n\n#include \"mf_desfire_i.h\"\n\n#define TAG \"MfDesfirePoller\"\n\nMfDesfireError mf_desfire_process_error(Iso14443_4aError error) {\n    switch(error) {\n    case Iso14443_4aErrorNone:\n        return MfDesfireErrorNone;\n    case Iso14443_4aErrorNotPresent:\n        return MfDesfireErrorNotPresent;\n    case Iso14443_4aErrorTimeout:\n        return MfDesfireErrorTimeout;\n    default:\n        return MfDesfireErrorProtocol;\n    }\n}\n\nMfDesfireError mf_desfire_process_status_code(uint8_t status_code) {\n    switch(status_code) {\n    case MF_DESFIRE_STATUS_OPERATION_OK:\n        return MfDesfireErrorNone;\n    case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR:\n        return MfDesfireErrorAuthentication;\n    case MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE:\n        return MfDesfireErrorCommandNotSupported;\n    default:\n        return MfDesfireErrorProtocol;\n    }\n}\n\nMfDesfireError mf_desfire_send_chunks(\n    MfDesfirePoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    furi_check(instance);\n    furi_check(instance->iso14443_4a_poller);\n    furi_check(instance->tx_buffer);\n    furi_check(instance->rx_buffer);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    MfDesfireError error = MfDesfireErrorNone;\n\n    do {\n        Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(\n            instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);\n\n        if(iso14443_4a_error != Iso14443_4aErrorNone) {\n            error = mf_desfire_process_error(iso14443_4a_error);\n            break;\n        }\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME);\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {\n            bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));\n        } else {\n            bit_buffer_reset(rx_buffer);\n        }\n\n        while(\n            bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) {\n            Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(\n                instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);\n\n            if(iso14443_4a_error != Iso14443_4aErrorNone) {\n                error = mf_desfire_process_error(iso14443_4a_error);\n                break;\n            }\n\n            const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer);\n            const size_t rx_capacity_remaining =\n                bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer);\n\n            if(rx_size <= rx_capacity_remaining + 1) {\n                bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));\n            } else {\n                FURI_LOG_W(TAG, \"RX buffer overflow: ignoring %zu bytes\", rx_size - 1);\n            }\n        }\n    } while(false);\n\n    if(error == MfDesfireErrorNone) {\n        uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0);\n        error = mf_desfire_process_status_code(err_code);\n    }\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        if(!mf_desfire_version_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorProtocol;\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError\n    mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorNotPresent;\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError\n    mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorProtocol;\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_key_version(\n    MfDesfirePoller* instance,\n    uint8_t key_num,\n    MfDesfireKeyVersion* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);\n    bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);\n    bit_buffer_set_byte(instance->input_buffer, 1, key_num);\n\n    MfDesfireError error =\n        mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n    if(error == MfDesfireErrorNone) {\n        if(!mf_desfire_key_version_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorProtocol;\n        }\n    }\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_key_versions(\n    MfDesfirePoller* instance,\n    SimpleArray* data,\n    uint32_t count) {\n    furi_check(instance);\n    furi_check(count > 0);\n\n    simple_array_init(data, count);\n\n    MfDesfireError error = MfDesfireErrorNone;\n\n    for(uint32_t i = 0; i < count; ++i) {\n        error = mf_desfire_poller_read_key_version(instance, i, simple_array_get(data, i));\n        if(error != MfDesfireErrorNone) break;\n    }\n\n    return error;\n}\n\nMfDesfireError\n    mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        const uint32_t app_id_count =\n            bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId);\n        if(app_id_count == 0) break;\n\n        simple_array_init(data, app_id_count);\n\n        for(uint32_t i = 0; i < app_id_count; ++i) {\n            if(!mf_desfire_application_id_parse(\n                   simple_array_get(data, i), i, instance->result_buffer)) {\n                error = MfDesfireErrorProtocol;\n                break;\n            }\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_select_application(\n    MfDesfirePoller* instance,\n    const MfDesfireApplicationId* id) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION);\n    bit_buffer_append_bytes(\n        instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));\n\n    MfDesfireError error =\n        mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        const uint32_t id_count =\n            bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId);\n\n        if(id_count == 0) break;\n        simple_array_init(data, id_count);\n\n        for(uint32_t i = 0; i < id_count; ++i) {\n            if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) {\n                error = MfDesfireErrorProtocol;\n                break;\n            }\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_file_settings(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    MfDesfireFileSettings* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS);\n    bit_buffer_append_byte(instance->input_buffer, id);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorProtocol;\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_file_settings_multi(\n    MfDesfirePoller* instance,\n    const SimpleArray* file_ids,\n    SimpleArray* data) {\n    furi_check(instance);\n    furi_check(file_ids);\n    furi_check(data);\n\n    MfDesfireError error = MfDesfireErrorNone;\n\n    const uint32_t file_id_count = simple_array_get_count(file_ids);\n    if(file_id_count > 0) {\n        simple_array_init(data, file_id_count);\n    }\n\n    for(uint32_t i = 0; i < file_id_count; ++i) {\n        const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);\n        error = mf_desfire_poller_read_file_settings(instance, file_id, simple_array_get(data, i));\n        if(error != MfDesfireErrorNone) break;\n    }\n\n    return error;\n}\n\nstatic MfDesfireError mf_desfire_poller_read_file(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    uint8_t read_cmd,\n    uint32_t offset,\n    size_t size,\n    MfDesfireFileData* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfDesfireError error = MfDesfireErrorNone;\n    simple_array_init(data->data, size);\n\n    size_t buffer_capacity = bit_buffer_get_capacity_bytes(instance->result_buffer);\n    uint32_t current_offset = offset;\n    uint32_t bytes_read = 0;\n\n    while(bytes_read < size) {\n        size_t bytes_to_read = MIN(buffer_capacity, size - bytes_read);\n        bit_buffer_reset(instance->input_buffer);\n        bit_buffer_append_byte(instance->input_buffer, read_cmd);\n        bit_buffer_append_byte(instance->input_buffer, id);\n        bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&current_offset, 3);\n        bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3);\n\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n        if(error != MfDesfireErrorNone) break;\n\n        size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer);\n        if(bytes_received != bytes_to_read) {\n            FURI_LOG_W(TAG, \"Read %zu out of %zu bytes\", bytes_received, bytes_to_read);\n            error = MfDesfireErrorProtocol;\n            break;\n        }\n\n        uint8_t* file_data = simple_array_get_data(data->data);\n        bit_buffer_write_bytes(instance->result_buffer, &file_data[current_offset], bytes_to_read);\n        bytes_read += bytes_to_read;\n        current_offset += bytes_to_read;\n    }\n\n    if(error != MfDesfireErrorNone) {\n        simple_array_reset(data->data);\n    }\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_file_data(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    uint32_t offset,\n    size_t size,\n    MfDesfireFileData* data) {\n    return mf_desfire_poller_read_file(instance, id, MF_DESFIRE_CMD_READ_DATA, offset, size, data);\n}\n\nMfDesfireError mf_desfire_poller_read_file_value(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    MfDesfireFileData* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE);\n    bit_buffer_append_byte(instance->input_buffer, id);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);\n\n        if(error != MfDesfireErrorNone) break;\n\n        if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {\n            error = MfDesfireErrorProtocol;\n        }\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_file_records(\n    MfDesfirePoller* instance,\n    MfDesfireFileId id,\n    uint32_t offset,\n    size_t size,\n    MfDesfireFileData* data) {\n    return mf_desfire_poller_read_file(\n        instance, id, MF_DESFIRE_CMD_READ_RECORDS, offset, size, data);\n}\n\nMfDesfireError mf_desfire_poller_read_file_data_multi(\n    MfDesfirePoller* instance,\n    const SimpleArray* file_ids,\n    const SimpleArray* file_settings,\n    SimpleArray* data) {\n    furi_check(instance);\n    furi_check(file_ids);\n    furi_check(file_settings);\n    furi_check(data);\n    furi_check(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));\n\n    MfDesfireError error = MfDesfireErrorNone;\n\n    const uint32_t file_id_count = simple_array_get_count(file_ids);\n    if(file_id_count > 0) {\n        simple_array_init(data, file_id_count);\n    }\n\n    for(size_t i = 0; i < file_id_count; ++i) {\n        const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);\n        const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);\n        const MfDesfireFileType file_type = file_settings_cur->type;\n\n        MfDesfireFileData* file_data = simple_array_get(data, i);\n\n        bool can_read_data = false;\n        for(size_t j = 0; j < file_settings_cur->access_rights_len; j++) {\n            uint8_t read_access = (file_settings_cur->access_rights[j] >> 12) & 0x0f;\n            uint8_t read_write_access = (file_settings_cur->access_rights[j] >> 4) & 0x0f;\n            can_read_data = (read_access == 0x0e) || (read_write_access == 0x0e);\n            if(can_read_data) break;\n        }\n        if(!can_read_data) {\n            FURI_LOG_D(TAG, \"Can't read file %zu data without authentication\", i);\n            continue;\n        }\n\n        if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {\n            error = mf_desfire_poller_read_file_data(\n                instance, file_id, 0, file_settings_cur->data.size, file_data);\n        } else if(file_type == MfDesfireFileTypeValue) {\n            error = mf_desfire_poller_read_file_value(instance, file_id, file_data);\n        } else if(\n            file_type == MfDesfireFileTypeLinearRecord ||\n            file_type == MfDesfireFileTypeCyclicRecord) {\n            error = mf_desfire_poller_read_file_records(\n                instance, file_id, 0, file_settings_cur->record.size, file_data);\n        }\n    }\n\n    return error;\n}\n\nMfDesfireError\n    mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfDesfireError error;\n\n    do {\n        error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);\n        if(error == MfDesfireErrorAuthentication) {\n            FURI_LOG_D(TAG, \"Auth is required to read master key settings and app ids\");\n            data->key_settings.is_free_directory_list = false;\n            error = MfDesfireErrorNone;\n            break;\n        }\n        if(error != MfDesfireErrorNone) break;\n\n        error = mf_desfire_poller_read_key_versions(\n            instance, data->key_versions, data->key_settings.max_keys);\n        if(error != MfDesfireErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to read key version: %d\", error);\n            break;\n        }\n\n        error = mf_desfire_poller_read_file_ids(instance, data->file_ids);\n        if(error != MfDesfireErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to read file ids: %d\", error);\n            break;\n        }\n\n        error = mf_desfire_poller_read_file_settings_multi(\n            instance, data->file_ids, data->file_settings);\n        if(error != MfDesfireErrorNone) {\n            FURI_LOG_E(TAG, \"Failed to read file settings: %d\", error);\n            break;\n        }\n\n        error = mf_desfire_poller_read_file_data_multi(\n            instance, data->file_ids, data->file_settings, data->file_data);\n\n    } while(false);\n\n    return error;\n}\n\nMfDesfireError mf_desfire_poller_read_applications(\n    MfDesfirePoller* instance,\n    const SimpleArray* app_ids,\n    SimpleArray* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfDesfireError error = MfDesfireErrorNone;\n\n    const uint32_t app_id_count = simple_array_get_count(app_ids);\n    if(app_id_count > 0) {\n        simple_array_init(data, app_id_count);\n    }\n\n    for(size_t i = 0; i < app_id_count; ++i) {\n        do {\n            FURI_LOG_D(TAG, \"Selecting app %zu\", i);\n            error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i));\n            if(error != MfDesfireErrorNone) break;\n\n            FURI_LOG_D(TAG, \"Reading app %zu\", i);\n            MfDesfireApplication* current_app = simple_array_get(data, i);\n            error = mf_desfire_poller_read_application(instance, current_app);\n\n        } while(false);\n    }\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h",
    "content": "#pragma once\n\n#include \"mf_desfire_poller.h\"\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    MfDesfirePollerStateIdle,\n    MfDesfirePollerStateReadVersion,\n    MfDesfirePollerStateReadFreeMemory,\n    MfDesfirePollerStateReadMasterKeySettings,\n    MfDesfirePollerStateReadMasterKeyVersion,\n    MfDesfirePollerStateReadApplicationIds,\n    MfDesfirePollerStateReadApplications,\n    MfDesfirePollerStateReadFailed,\n    MfDesfirePollerStateReadSuccess,\n\n    MfDesfirePollerStateNum,\n} MfDesfirePollerState;\n\ntypedef enum {\n    MfDesfirePollerSessionStateIdle,\n    MfDesfirePollerSessionStateActive,\n    MfDesfirePollerSessionStateStopRequest,\n} MfDesfirePollerSessionState;\n\nstruct MfDesfirePoller {\n    Iso14443_4aPoller* iso14443_4a_poller;\n    MfDesfirePollerSessionState session_state;\n    MfDesfirePollerState state;\n    MfDesfireError error;\n    MfDesfireData* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    BitBuffer* input_buffer;\n    BitBuffer* result_buffer;\n    MfDesfirePollerEventData mf_desfire_event_data;\n    MfDesfirePollerEvent mf_desfire_event;\n    NfcGenericEvent general_event;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nMfDesfireError mf_desfire_process_error(Iso14443_4aError error);\n\nMfDesfireError mf_desfire_process_status_code(uint8_t status_code);\n\nconst MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus.c",
    "content": "#include \"mf_plus_i.h\"\n\n#include <bit_lib/bit_lib.h>\n#include <furi.h>\n\n#define MF_PLUS_PROTOCOL_NAME \"Mifare Plus\"\n\nstatic const char* mf_plus_type_strings[] = {\n    [MfPlusTypeS] = \"Plus S\",\n    [MfPlusTypeX] = \"Plus X\",\n    [MfPlusTypeSE] = \"Plus SE\",\n    [MfPlusTypeEV1] = \"Plus EV1\",\n    [MfPlusTypeEV2] = \"Plus EV2\",\n    [MfPlusTypePlus] = \"Plus\",\n    [MfPlusTypeUnknown] = \"Unknown\",\n};\n\nstatic const char* mf_plus_size_strings[] = {\n    [MfPlusSize1K] = \"1K\",\n    [MfPlusSize2K] = \"2K\",\n    [MfPlusSize4K] = \"4K\",\n    [MfPlusSizeUnknown] = \"Unknown\",\n};\n\nstatic const char* mf_plus_security_level_strings[] = {\n    [MfPlusSecurityLevel0] = \"SL0\",\n    [MfPlusSecurityLevel1] = \"SL1\",\n    [MfPlusSecurityLevel2] = \"SL2\",\n    [MfPlusSecurityLevel3] = \"SL3\",\n    [MfPlusSecurityLevelUnknown] = \"Unknown\",\n};\n\nconst NfcDeviceBase nfc_device_mf_plus = {\n    .protocol_name = MF_PLUS_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)mf_plus_alloc,\n    .free = (NfcDeviceFree)mf_plus_free,\n    .reset = (NfcDeviceReset)mf_plus_reset,\n    .copy = (NfcDeviceCopy)mf_plus_copy,\n    .verify = (NfcDeviceVerify)mf_plus_verify,\n    .load = (NfcDeviceLoad)mf_plus_load,\n    .save = (NfcDeviceSave)mf_plus_save,\n    .is_equal = (NfcDeviceEqual)mf_plus_is_equal,\n    .get_name = (NfcDeviceGetName)mf_plus_get_device_name,\n    .get_uid = (NfcDeviceGetUid)mf_plus_get_uid,\n    .set_uid = (NfcDeviceSetUid)mf_plus_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)mf_plus_get_base_data,\n};\n\nMfPlusData* mf_plus_alloc(void) {\n    MfPlusData* data = malloc(sizeof(MfPlusData));\n    data->device_name = furi_string_alloc();\n    data->iso14443_4a_data = iso14443_4a_alloc();\n\n    data->type = MfPlusTypeUnknown;\n    data->security_level = MfPlusSecurityLevelUnknown;\n    data->size = MfPlusSizeUnknown;\n\n    return data;\n}\n\nvoid mf_plus_free(MfPlusData* data) {\n    furi_check(data);\n\n    furi_string_free(data->device_name);\n    iso14443_4a_free(data->iso14443_4a_data);\n    free(data);\n}\n\nvoid mf_plus_reset(MfPlusData* data) {\n    furi_check(data);\n\n    iso14443_4a_reset(data->iso14443_4a_data);\n    memset(&data->version, 0, sizeof(data->version));\n    furi_string_reset(data->device_name);\n    data->type = MfPlusTypeUnknown;\n    data->security_level = MfPlusSecurityLevelUnknown;\n    data->size = MfPlusSizeUnknown;\n}\n\nvoid mf_plus_copy(MfPlusData* data, const MfPlusData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);\n    data->version = other->version;\n    data->type = other->type;\n    data->security_level = other->security_level;\n    data->size = other->size;\n}\n\nbool mf_plus_verify(MfPlusData* data, const FuriString* device_type) {\n    UNUSED(data);\n    return furi_string_equal_str(device_type, MF_PLUS_PROTOCOL_NAME);\n}\n\nbool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n\n    bool success = false;\n    do {\n        if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;\n        if(!mf_plus_version_load(&data->version, ff)) break;\n        if(!mf_plus_type_load(&data->type, ff)) break;\n        if(!mf_plus_security_level_load(&data->security_level, ff)) break;\n        if(!mf_plus_size_load(&data->size, ff)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) {\n    furi_check(data);\n\n    bool success = false;\n    do {\n        if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;\n        if(!flipper_format_write_comment_cstr(ff, MF_PLUS_PROTOCOL_NAME \" specific data\")) break;\n        if(!mf_plus_version_save(&data->version, ff)) break;\n        if(!mf_plus_type_save(&data->type, ff)) break;\n        if(!mf_plus_security_level_save(&data->security_level, ff)) break;\n        if(!mf_plus_size_save(&data->size, ff)) break;\n        success = true;\n    } while(false);\n\n    return success;\n}\n\nbool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    bool equal = false;\n    do {\n        if(!iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data)) break;\n        if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break;\n        if(data->security_level != other->security_level) break;\n        if(data->type != other->type) break;\n        if(data->size != other->size) break;\n        equal = true;\n    } while(false);\n\n    return equal;\n}\n\nconst char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n\n    if(name_type == NfcDeviceNameTypeFull) {\n        furi_string_printf(\n            data->device_name,\n            \"Mifare %s %s %s\",\n            mf_plus_type_strings[data->type], // Includes \"Plus\" for regular Mifare Plus cards\n            mf_plus_size_strings[data->size],\n            mf_plus_security_level_strings[data->security_level]);\n    } else if(name_type == NfcDeviceNameTypeShort) {\n        furi_string_set_str(data->device_name, MF_PLUS_PROTOCOL_NAME);\n    } else {\n        furi_crash(\"Unexpected name type\");\n    }\n\n    return furi_string_get_cstr(data->device_name);\n}\n\nconst uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) {\n    furi_check(data);\n\n    return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);\n}\n\nbool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);\n}\nIso14443_4aData* mf_plus_get_base_data(const MfPlusData* data) {\n    furi_check(data);\n\n    return data->iso14443_4a_data;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_PLUS_UID_SIZE_MAX (7)\n#define MF_PLUS_BATCH_SIZE   (5)\n\n#define MF_PLUS_CMD_GET_VERSION (0x60)\n\ntypedef enum {\n    MfPlusErrorNone,\n    MfPlusErrorUnknown,\n    MfPlusErrorNotPresent,\n    MfPlusErrorProtocol,\n    MfPlusErrorAuth,\n    MfPlusErrorPartialRead,\n    MfPlusErrorTimeout,\n} MfPlusError;\n\ntypedef enum {\n    MfPlusTypePlus,\n    MfPlusTypeEV1,\n    MfPlusTypeEV2,\n    MfPlusTypeS,\n    MfPlusTypeSE,\n    MfPlusTypeX,\n\n    MfPlusTypeUnknown,\n    MfPlusTypeNum,\n} MfPlusType;\n\ntypedef enum {\n    MfPlusSize1K,\n    MfPlusSize2K,\n    MfPlusSize4K,\n\n    MfPlusSizeUnknown,\n    MfPlusSizeNum,\n} MfPlusSize;\n\ntypedef enum {\n    MfPlusSecurityLevel0,\n    MfPlusSecurityLevel1,\n    MfPlusSecurityLevel2,\n    MfPlusSecurityLevel3,\n\n    MfPlusSecurityLevelUnknown,\n    MfPlusSecurityLevelNum,\n} MfPlusSecurityLevel;\n\ntypedef struct {\n    uint8_t hw_vendor;\n    uint8_t hw_type;\n    uint8_t hw_subtype;\n    uint8_t hw_major;\n    uint8_t hw_minor;\n    uint8_t hw_storage;\n    uint8_t hw_proto;\n\n    uint8_t sw_vendor;\n    uint8_t sw_type;\n    uint8_t sw_subtype;\n    uint8_t sw_major;\n    uint8_t sw_minor;\n    uint8_t sw_storage;\n    uint8_t sw_proto;\n\n    uint8_t uid[MF_PLUS_UID_SIZE_MAX];\n    uint8_t batch[MF_PLUS_BATCH_SIZE];\n    uint8_t prod_week;\n    uint8_t prod_year;\n} MfPlusVersion;\n\ntypedef struct {\n    Iso14443_4aData* iso14443_4a_data;\n    MfPlusVersion version;\n    MfPlusType type;\n    MfPlusSize size;\n    MfPlusSecurityLevel security_level;\n    FuriString* device_name;\n} MfPlusData;\n\nextern const NfcDeviceBase nfc_device_mf_plus;\n\nMfPlusData* mf_plus_alloc(void);\n\nvoid mf_plus_free(MfPlusData* data);\n\nvoid mf_plus_reset(MfPlusData* data);\n\nvoid mf_plus_copy(MfPlusData* data, const MfPlusData* other);\n\nbool mf_plus_verify(MfPlusData* data, const FuriString* device_type);\n\nbool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version);\n\nbool mf_plus_save(const MfPlusData* data, FlipperFormat* ff);\n\nbool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other);\n\nconst char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len);\n\nbool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_4aData* mf_plus_get_base_data(const MfPlusData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_i.c",
    "content": "#include \"mf_plus_i.h\"\n\n#define MF_PLUS_FFF_VERSION_KEY \\\n    MF_PLUS_FFF_PICC_PREFIX \" \" \\\n                            \"Version\"\n\n#define MF_PLUS_T1_TK_VALUE_LEN 7\n\n#define MF_PLUS_FFF_SECURITY_LEVEL_KEY \"Security Level\"\n#define MF_PLUS_FFF_CARD_TYPE_KEY      \"Card Type\"\n#define MF_PLUS_FFF_MEMORY_SIZE_KEY    \"Memory Size\"\n\n#define TAG \"MfPlus\"\n\nconst uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = {\n    {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S\n    {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X\n    {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE\n    {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE\n};\n\nMfPlusError mf_plus_get_type_from_version(\n    const Iso14443_4aData* iso14443_4a_data,\n    MfPlusData* mf_plus_data) {\n    furi_assert(iso14443_4a_data);\n    furi_assert(mf_plus_data);\n\n    MfPlusError error = MfPlusErrorProtocol;\n\n    if(mf_plus_data->version.hw_type == 0x02 || mf_plus_data->version.hw_type == 0x82) {\n        error = MfPlusErrorNone;\n        // Mifare Plus EV1/EV2\n\n        // Revision\n        switch(mf_plus_data->version.hw_major) {\n        case 0x11:\n            mf_plus_data->type = MfPlusTypeEV1;\n            FURI_LOG_D(TAG, \"Mifare Plus EV1\");\n            break;\n        case 0x22:\n            mf_plus_data->type = MfPlusTypeEV2;\n            FURI_LOG_D(TAG, \"Mifare Plus EV2\");\n            break;\n        default:\n            mf_plus_data->type = MfPlusTypeUnknown;\n            FURI_LOG_D(TAG, \"Unknown Mifare Plus EV type\");\n            break;\n        }\n\n        // Storage size\n        switch(mf_plus_data->version.hw_storage) {\n        case 0x16:\n            mf_plus_data->size = MfPlusSize2K;\n            FURI_LOG_D(TAG, \"2K\");\n            break;\n        case 0x18:\n            mf_plus_data->size = MfPlusSize4K;\n            FURI_LOG_D(TAG, \"4K\");\n            break;\n        default:\n            mf_plus_data->size = MfPlusSizeUnknown;\n            FURI_LOG_D(TAG, \"Unknown storage size\");\n            break;\n        }\n\n        // Security level\n        if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) {\n            // Mifare Plus EV1/2 SL3\n            mf_plus_data->security_level = MfPlusSecurityLevel3;\n            FURI_LOG_D(TAG, \"Mifare Plus EV1/2 SL3\");\n        } else {\n            // Mifare Plus EV1/2 SL1\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n            FURI_LOG_D(TAG, \"Mifare Plus EV1/2 SL1\");\n        }\n    }\n\n    return error;\n}\n\nMfPlusError\n    mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) {\n    furi_assert(iso4_data);\n    furi_assert(mf_plus_data);\n\n    MfPlusError error = MfPlusErrorProtocol;\n\n    if(simple_array_get_count(iso4_data->ats_data.t1_tk) != MF_PLUS_T1_TK_VALUE_LEN) {\n        return MfPlusErrorProtocol;\n    }\n\n    switch(iso4_data->iso14443_3a_data->sak) {\n    case 0x08:\n        if(memcmp(\n               simple_array_get_data(iso4_data->ats_data.t1_tk),\n               mf_plus_ats_t1_tk_values[0],\n               simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus S 2K SL1\n            mf_plus_data->type = MfPlusTypeS;\n            mf_plus_data->size = MfPlusSize2K;\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n\n            FURI_LOG_D(TAG, \"Mifare Plus S 2K SL1\");\n            error = MfPlusErrorNone;\n        } else if(\n            memcmp(\n                simple_array_get_data(iso4_data->ats_data.t1_tk),\n                mf_plus_ats_t1_tk_values[1],\n                simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus X 2K SL1\n            mf_plus_data->type = MfPlusTypeX;\n            mf_plus_data->size = MfPlusSize2K;\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n\n            FURI_LOG_D(TAG, \"Mifare Plus X 2K SL1\");\n            error = MfPlusErrorNone;\n        } else if(\n            memcmp(\n                simple_array_get_data(iso4_data->ats_data.t1_tk),\n                mf_plus_ats_t1_tk_values[2],\n                simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 ||\n            memcmp(\n                simple_array_get_data(iso4_data->ats_data.t1_tk),\n                mf_plus_ats_t1_tk_values[3],\n                simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus SE 1K SL1\n            mf_plus_data->type = MfPlusTypeSE;\n            mf_plus_data->size = MfPlusSize1K;\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n\n            FURI_LOG_D(TAG, \"Mifare Plus SE 1K SL1\");\n            error = MfPlusErrorNone;\n        } else {\n            FURI_LOG_D(TAG, \"Sak 08 but no known Mifare Plus type\");\n        }\n\n        break;\n    case 0x10:\n        // Mifare Plus X 2K SL2\n        mf_plus_data->type = MfPlusTypeX;\n        mf_plus_data->size = MfPlusSize2K;\n        mf_plus_data->security_level = MfPlusSecurityLevel2;\n        FURI_LOG_D(TAG, \"Mifare Plus X 2K SL2\");\n        error = MfPlusErrorNone;\n\n        break;\n    case 0x11:\n        // Mifare Plus X 4K SL2\n        mf_plus_data->type = MfPlusTypeX;\n        mf_plus_data->size = MfPlusSize4K;\n        mf_plus_data->security_level = MfPlusSecurityLevel2;\n        FURI_LOG_D(TAG, \"Mifare Plus X 4K SL2\");\n        error = MfPlusErrorNone;\n\n        break;\n    case 0x18:\n        if(memcmp(\n               simple_array_get_data(iso4_data->ats_data.t1_tk),\n               mf_plus_ats_t1_tk_values[0],\n               simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus S 4K SL1\n            mf_plus_data->type = MfPlusTypeS;\n            mf_plus_data->size = MfPlusSize4K;\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n\n            FURI_LOG_D(TAG, \"Mifare Plus S 4K SL1\");\n            error = MfPlusErrorNone;\n        } else if(\n            memcmp(\n                simple_array_get_data(iso4_data->ats_data.t1_tk),\n                mf_plus_ats_t1_tk_values[1],\n                simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus X 4K SL1\n            mf_plus_data->type = MfPlusTypeX;\n            mf_plus_data->size = MfPlusSize4K;\n            mf_plus_data->security_level = MfPlusSecurityLevel1;\n\n            FURI_LOG_D(TAG, \"Mifare Plus X 4K SL1\");\n            error = MfPlusErrorNone;\n        } else {\n            FURI_LOG_D(TAG, \"Sak 18 but no known Mifare Plus type\");\n        }\n\n        break;\n    case 0x20:\n        if(memcmp(\n               simple_array_get_data(iso4_data->ats_data.t1_tk),\n               mf_plus_ats_t1_tk_values[0],\n               simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            // Mifare Plus S 2/4K SL3\n            FURI_LOG_D(TAG, \"Mifare Plus S SL3\");\n            mf_plus_data->type = MfPlusTypeS;\n            mf_plus_data->security_level = MfPlusSecurityLevel3;\n\n            if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {\n                // Mifare Plus S 2K SL3\n                mf_plus_data->size = MfPlusSize2K;\n\n                FURI_LOG_D(TAG, \"Mifare Plus S 2K SL3\");\n                error = MfPlusErrorNone;\n            } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {\n                // Mifare Plus S 4K SL3\n                mf_plus_data->size = MfPlusSize4K;\n\n                FURI_LOG_D(TAG, \"Mifare Plus S 4K SL3\");\n                error = MfPlusErrorNone;\n            } else {\n                FURI_LOG_D(TAG, \"Sak 20 but no known Mifare Plus type (S)\");\n            }\n        } else if(\n            memcmp(\n                simple_array_get_data(iso4_data->ats_data.t1_tk),\n                mf_plus_ats_t1_tk_values[1],\n                simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {\n            mf_plus_data->type = MfPlusTypeX;\n            mf_plus_data->security_level = MfPlusSecurityLevel3;\n            FURI_LOG_D(TAG, \"Mifare Plus X SL3\");\n\n            if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {\n                mf_plus_data->size = MfPlusSize2K;\n\n                FURI_LOG_D(TAG, \"Mifare Plus X 2K SL3\");\n                error = MfPlusErrorNone;\n            } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {\n                mf_plus_data->size = MfPlusSize4K;\n\n                FURI_LOG_D(TAG, \"Mifare Plus X 4K SL3\");\n                error = MfPlusErrorNone;\n            } else {\n                FURI_LOG_D(TAG, \"Sak 20 but no known Mifare Plus type (X)\");\n            }\n        } else {\n            FURI_LOG_D(TAG, \"Sak 20 but no known Mifare Plus type\");\n        }\n    }\n\n    return error;\n}\n\nMfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {\n    bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion);\n\n    if(can_parse) {\n        bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion));\n    } else if(\n        bit_buffer_get_size_bytes(buf) == 8 &&\n        bit_buffer_get_byte(buf, 0) == MF_PLUS_STATUS_ADDITIONAL_FRAME) {\n        // HACK(-nofl): There are supposed to be three parts to the GetVersion command,\n        // with the second and third parts fetched by sending the AdditionalFrame\n        // command. I don't know whether the entire MIFARE Plus line uses status as\n        // the first byte, so let's just assume we only have the first part of\n        // the response if it's size 8 and starts with the AF status. The second\n        // part of the response is the same size and status byte, but so far\n        // we're only reading one response.\n        can_parse = true;\n        bit_buffer_write_bytes_mid(buf, data, 1, bit_buffer_get_size_bytes(buf) - 1);\n    }\n\n    return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol;\n}\n\nbool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) {\n    return flipper_format_read_hex(\n        ff, MF_PLUS_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfPlusVersion));\n}\n\nbool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff) {\n    FuriString* security_level_string = furi_string_alloc();\n    flipper_format_read_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);\n\n    // Take the last character of the string\n    char security_level_char = furi_string_get_char(\n        security_level_string, furi_string_utf8_length(security_level_string) - 1);\n\n    switch(security_level_char) {\n    case '0':\n        *data = MfPlusSecurityLevel0;\n        break;\n    case '1':\n        *data = MfPlusSecurityLevel1;\n        break;\n    case '2':\n        *data = MfPlusSecurityLevel2;\n        break;\n    case '3':\n        *data = MfPlusSecurityLevel3;\n        break;\n    default:\n        *data = MfPlusSecurityLevelUnknown;\n        break;\n    }\n\n    furi_string_free(security_level_string);\n\n    return true;\n}\n\nbool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff) {\n    FuriString* type_string = furi_string_alloc();\n    flipper_format_read_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);\n\n    if(furi_string_equal_str(type_string, \"Mifare Plus\")) {\n        *data = MfPlusTypePlus;\n    } else if(furi_string_equal_str(type_string, \"Mifare Plus X\")) {\n        *data = MfPlusTypeX;\n    } else if(furi_string_equal_str(type_string, \"Mifare Plus S\")) {\n        *data = MfPlusTypeS;\n    } else if(furi_string_equal_str(type_string, \"Mifare Plus SE\")) {\n        *data = MfPlusTypeSE;\n    } else if(furi_string_equal_str(type_string, \"Mifare Plus EV1\")) {\n        *data = MfPlusTypeEV1;\n    } else if(furi_string_equal_str(type_string, \"Mifare Plus EV2\")) {\n        *data = MfPlusTypeEV2;\n    } else {\n        *data = MfPlusTypeUnknown;\n    }\n\n    furi_string_free(type_string);\n    return true;\n}\n\nbool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff) {\n    FuriString* size_string = furi_string_alloc();\n    flipper_format_read_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);\n\n    if(furi_string_equal_str(size_string, \"1K\")) {\n        *data = MfPlusSize1K;\n    } else if(furi_string_equal_str(size_string, \"2K\")) {\n        *data = MfPlusSize2K;\n    } else if(furi_string_equal_str(size_string, \"4K\")) {\n        *data = MfPlusSize4K;\n    } else {\n        *data = MfPlusSizeUnknown;\n    }\n\n    furi_string_free(size_string);\n    return true;\n}\n\nbool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff) {\n    return flipper_format_write_hex(\n        ff, MF_PLUS_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfPlusVersion));\n}\n\nbool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff) {\n    FuriString* security_level_string = furi_string_alloc();\n\n    switch(*data) {\n    case MfPlusSecurityLevel0:\n        furi_string_cat(security_level_string, \"SL0\");\n        break;\n    case MfPlusSecurityLevel1:\n        furi_string_cat(security_level_string, \"SL1\");\n        break;\n    case MfPlusSecurityLevel2:\n        furi_string_cat(security_level_string, \"SL2\");\n        break;\n    case MfPlusSecurityLevel3:\n        furi_string_cat(security_level_string, \"SL3\");\n        break;\n    default:\n        furi_string_cat(security_level_string, \"Unknown\");\n        break;\n    }\n\n    bool success =\n        flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);\n    furi_string_free(security_level_string);\n\n    return success;\n}\n\nbool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) {\n    FuriString* type_string = furi_string_alloc();\n\n    switch(*data) {\n    case MfPlusTypePlus:\n        furi_string_cat(type_string, \"Mifare Plus\");\n        break;\n    case MfPlusTypeX:\n        furi_string_cat(type_string, \"Mifare Plus X\");\n        break;\n    case MfPlusTypeS:\n        furi_string_cat(type_string, \"Mifare Plus S\");\n        break;\n    case MfPlusTypeSE:\n        furi_string_cat(type_string, \"Mifare Plus SE\");\n        break;\n    case MfPlusTypeEV1:\n        furi_string_cat(type_string, \"Mifare Plus EV1\");\n        break;\n    case MfPlusTypeEV2:\n        furi_string_cat(type_string, \"Mifare Plus EV2\");\n        break;\n    default:\n        furi_string_cat(type_string, \"Unknown\");\n        break;\n    }\n\n    bool success = flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);\n    furi_string_free(type_string);\n\n    return success;\n}\n\nbool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) {\n    FuriString* size_string = furi_string_alloc();\n\n    switch(*data) {\n    case MfPlusSize1K:\n        furi_string_cat(size_string, \"1K\");\n        break;\n    case MfPlusSize2K:\n        furi_string_cat(size_string, \"2K\");\n        break;\n    case MfPlusSize4K:\n        furi_string_cat(size_string, \"4K\");\n        break;\n    default:\n        furi_string_cat(size_string, \"Unknown\");\n        break;\n    }\n\n    bool success = flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);\n    furi_string_free(size_string);\n\n    return success;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_i.h",
    "content": "#pragma once\n\n#include \"mf_plus.h\"\n\n#define MF_PLUS_FFF_PICC_PREFIX \"PICC\"\n\n#define MF_PLUS_STATUS_OPERATION_OK     (0x90)\n#define MF_PLUS_STATUS_ADDITIONAL_FRAME (0xAF)\n\nMfPlusError mf_plus_get_type_from_version(\n    const Iso14443_4aData* iso14443_4a_data,\n    MfPlusData* mf_plus_data);\n\nMfPlusError mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data);\n\nMfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf);\n\nbool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff);\n\nbool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff);\n\nbool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff);\n\nbool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff);\n\nbool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff);\n\nbool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff);\n\nbool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff);\n\nbool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff);\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_poller.c",
    "content": "#include \"mf_plus_poller_i.h\"\n#include \"mf_plus_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"MfPlusPoller\"\n\n#define MF_PLUS_BUF_SIZE        (64U)\n#define MF_PLUS_RESULT_BUF_SIZE (512U)\n\ntypedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance);\n\nconst MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nMfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {\n    furi_assert(iso14443_4a_poller);\n\n    MfPlusPoller* instance = malloc(sizeof(MfPlusPoller));\n\n    instance->iso14443_4a_poller = iso14443_4a_poller;\n\n    instance->data = mf_plus_alloc();\n\n    instance->tx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);\n    instance->input_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);\n    instance->result_buffer = bit_buffer_alloc(MF_PLUS_RESULT_BUF_SIZE);\n\n    instance->general_event.protocol = NfcProtocolMfPlus;\n    instance->general_event.event_data = &instance->mfp_event;\n    instance->general_event.instance = instance;\n\n    instance->mfp_event.data = &instance->mfp_event_data;\n\n    return instance;\n}\n\nstatic NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_reset(instance->result_buffer);\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    iso14443_4a_copy(\n        instance->data->iso14443_4a_data,\n        iso14443_4a_poller_get_data(instance->iso14443_4a_poller));\n\n    instance->state = MfPlusPollerStateReadVersion;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) {\n    MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version);\n    if(error == MfPlusErrorNone) {\n        instance->state = MfPlusPollerStateParseVersion;\n    } else {\n        instance->state = MfPlusPollerStateParseIso4;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_plus_poller_handler_parse_version(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    MfPlusError error = mf_plus_get_type_from_version(\n        iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);\n    if(error == MfPlusErrorNone) {\n        instance->state = MfPlusPollerStateReadSuccess;\n    } else {\n        instance->error = error;\n        instance->state = MfPlusPollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_plus_poller_handler_parse_iso4(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    MfPlusError error = mf_plus_get_type_from_iso4(\n        iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);\n    if(error == MfPlusErrorNone) {\n        instance->state = MfPlusPollerStateReadSuccess;\n    } else {\n        instance->error = error;\n        instance->state = MfPlusPollerStateReadFailed;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_plus_poller_handler_read_failed(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    FURI_LOG_D(TAG, \"Read failed\");\n    iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n\n    instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;\n    instance->mfp_event.data->error = instance->error;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->state = MfPlusPollerStateIdle;\n\n    return command;\n}\n\nstatic NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) {\n    furi_assert(instance);\n\n    FURI_LOG_D(TAG, \"Read success\");\n    iso14443_4a_poller_halt(instance->iso14443_4a_poller);\n\n    instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n\n    return command;\n}\n\nstatic const MfPlusPollerReadHandler mf_plus_poller_read_handler[MfPlusPollerStateNum] = {\n    [MfPlusPollerStateIdle] = mf_plus_poller_handler_idle,\n    [MfPlusPollerStateReadVersion] = mf_plus_poller_handler_read_version,\n    [MfPlusPollerStateParseVersion] = mf_plus_poller_handler_parse_version,\n    [MfPlusPollerStateParseIso4] = mf_plus_poller_handler_parse_iso4,\n    [MfPlusPollerStateReadFailed] = mf_plus_poller_handler_read_failed,\n    [MfPlusPollerStateReadSuccess] = mf_plus_poller_handler_read_success,\n};\n\nstatic void mf_plus_poller_set_callback(\n    MfPlusPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n    furi_assert(event.event_data);\n\n    MfPlusPoller* instance = context;\n    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        command = mf_plus_poller_read_handler[instance->state](instance);\n    } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {\n        instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;\n        command = instance->callback(instance->general_event, instance->context);\n    }\n\n    return command;\n}\n\nvoid mf_plus_poller_free(MfPlusPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    bit_buffer_free(instance->input_buffer);\n    bit_buffer_free(instance->result_buffer);\n    mf_plus_free(instance->data);\n    free(instance);\n}\n\nstatic bool mf_plus_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_4a);\n    furi_assert(event.event_data);\n\n    MfPlusPoller* instance = context;\n    Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;\n\n    MfPlusError error = MfPlusErrorUnknown;\n\n    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {\n        error = mf_plus_poller_read_version(instance, &instance->data->version);\n        if(error == MfPlusErrorNone) {\n            error = mf_plus_get_type_from_version(\n                iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);\n        } else {\n            error = mf_plus_get_type_from_iso4(\n                iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);\n        }\n    }\n\n    return error == MfPlusErrorNone;\n}\n\nconst NfcPollerBase mf_plus_poller = {\n    .alloc = (NfcPollerAlloc)mf_plus_poller_alloc,\n    .free = (NfcPollerFree)mf_plus_poller_free,\n    .set_callback = (NfcPollerSetCallback)mf_plus_poller_set_callback,\n    .run = (NfcPollerRun)mf_plus_poller_run,\n    .detect = (NfcPollerDetect)mf_plus_poller_detect,\n    .get_data = (NfcPollerGetData)mf_plus_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_poller.h",
    "content": "#pragma once\n\n#include \"mf_plus.h\"\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief MIFARE Plus poller opaque type definition.\n */\ntypedef struct MfPlusPoller MfPlusPoller;\n\n/**\n * @brief Enumeration of possible MfPlus poller event types.\n */\n\ntypedef enum {\n    MfPlusPollerEventTypeReadSuccess, /**< Card was read successfully. */\n    MfPlusPollerEventTypeReadFailed, /**< Poller failed to read the card. */\n} MfPlusPollerEventType;\n\n/**\n * @brief MIFARE Plus poller event data.\n */\ntypedef union {\n    MfPlusError error; /**< Error code indicating card reading fail reason. */\n} MfPlusPollerEventData;\n\n/**\n * @brief MIFARE Plus poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    MfPlusPollerEventType type; /**< Type of emitted event. */\n    MfPlusPollerEventData* data; /**< Pointer to event specific data. */\n} MfPlusPollerEvent;\n\n/**\n * @brief Read MfPlus card version.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfPlusVersion structure to be filled with version data.\n * @return MfPlusErrorNone on success, an error code on failure.\n */\nMfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase mf_plus_poller;\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_poller_i.c",
    "content": "#include \"mf_plus_poller_i.h\"\n\n#include <furi.h>\n\n#include \"mf_plus_i.h\"\n\n#define TAG \"MfPlusPoller\"\n\nMfPlusError mf_plus_process_error(Iso14443_4aError error) {\n    switch(error) {\n    case Iso14443_4aErrorNone:\n        return MfPlusErrorNone;\n    case Iso14443_4aErrorNotPresent:\n        return MfPlusErrorNotPresent;\n    case Iso14443_4aErrorTimeout:\n        return MfPlusErrorTimeout;\n    default:\n        return MfPlusErrorProtocol;\n    }\n}\n\nMfPlusError mf_plus_poller_send_chunk(\n    MfPlusPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer) {\n    furi_assert(instance);\n    furi_assert(instance->iso14443_4a_poller);\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(tx_buffer);\n    furi_assert(rx_buffer);\n\n    Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(\n        instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);\n    MfPlusError error = mf_plus_process_error(iso14443_4a_error);\n\n    if(error == MfPlusErrorNone) {\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n    }\n\n    bit_buffer_reset(instance->tx_buffer);\n\n    return error;\n}\n\nMfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->input_buffer);\n    bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION);\n\n    MfPlusError error =\n        mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer);\n    if(error == MfPlusErrorNone) {\n        error = mf_plus_version_parse(data, instance->result_buffer);\n    }\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_plus/mf_plus_poller_i.h",
    "content": "#pragma once\n\n#include \"mf_plus_poller.h\"\n\n#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_PLUS_FWT_FC (60000)\n\ntypedef enum {\n    MfPlusCardStateDetected,\n    MfPlusCardStateLost,\n} MfPlusCardState;\n\ntypedef enum {\n    MfPlusPollerStateIdle,\n    MfPlusPollerStateReadVersion,\n    MfPlusPollerStateParseVersion,\n    MfPlusPollerStateParseIso4,\n    MfPlusPollerStateReadFailed,\n    MfPlusPollerStateReadSuccess,\n\n    MfPlusPollerStateNum,\n} MfPlusPollerState;\n\nstruct MfPlusPoller {\n    Iso14443_4aPoller* iso14443_4a_poller;\n\n    MfPlusData* data;\n    MfPlusPollerState state;\n\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    BitBuffer* input_buffer;\n    BitBuffer* result_buffer;\n\n    MfPlusError error;\n    NfcGenericEvent general_event;\n    MfPlusPollerEvent mfp_event;\n    MfPlusPollerEventData mfp_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nMfPlusError mf_plus_process_error(Iso14443_4aError error);\n\nMfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller);\n\nvoid mf_plus_poller_free(MfPlusPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight.c",
    "content": "#include \"mf_ultralight.h\"\n\n#include <bit_lib/bit_lib.h>\n#include <furi.h>\n\n#define MF_ULTRALIGHT_PROTOCOL_NAME \"NTAG/Ultralight\"\n\n#define MF_ULTRALIGHT_FORMAT_VERSION_KEY  \"Data format version\"\n#define MF_ULTRALIGHT_TYPE_KEY            MF_ULTRALIGHT_PROTOCOL_NAME \" type\"\n#define MF_ULTRALIGHT_SIGNATURE_KEY       \"Signature\"\n#define MF_ULTRALIGHT_MIFARE_VERSION_KEY  \"Mifare version\"\n#define MF_ULTRALIGHT_COUNTER_KEY         \"Counter\"\n#define MF_ULTRALIGHT_TEARING_KEY         \"Tearing\"\n#define MF_ULTRALIGHT_PAGES_TOTAL_KEY     \"Pages total\"\n#define MF_ULTRALIGHT_PAGES_READ_KEY      \"Pages read\"\n#define MF_ULTRALIGHT_PAGE_KEY            \"Page\"\n#define MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY \"Failed authentication attempts\"\n\ntypedef struct {\n    const char* device_name;\n    uint16_t total_pages;\n    uint16_t config_page;\n    uint32_t feature_set;\n} MfUltralightFeatures;\n\nstatic const uint32_t mf_ultralight_data_format_version = 2;\n\nstatic const MfUltralightFeatures mf_ultralight_features[MfUltralightTypeNum] = {\n    [MfUltralightTypeOrigin] =\n        {\n            .device_name = \"Mifare Ultralight\",\n            .total_pages = 16,\n            .config_page = 0,\n            .feature_set = MfUltralightFeatureSupportCompatibleWrite,\n        },\n    [MfUltralightTypeMfulC] =\n        {\n            .device_name = \"Mifare Ultralight C\",\n            .total_pages = 48,\n            .config_page = 0,\n            .feature_set = MfUltralightFeatureSupportCompatibleWrite |\n                           MfUltralightFeatureSupportAuthenticate,\n        },\n    [MfUltralightTypeNTAG203] =\n        {\n            .device_name = \"NTAG203\",\n            .total_pages = 42,\n            .config_page = 0,\n            .feature_set = MfUltralightFeatureSupportCompatibleWrite |\n                           MfUltralightFeatureSupportCounterInMemory,\n        },\n    [MfUltralightTypeUL11] =\n        {\n            .device_name = \"Mifare Ultralight 11\",\n            .total_pages = 20,\n            .config_page = 16,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportReadCounter |\n                MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |\n                MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl,\n        },\n    [MfUltralightTypeUL21] =\n        {\n            .device_name = \"Mifare Ultralight 21\",\n            .total_pages = 41,\n            .config_page = 37,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportReadCounter |\n                MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |\n                MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl |\n                MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAG213] =\n        {\n            .device_name = \"NTAG213\",\n            .total_pages = 45,\n            .config_page = 41,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportCompatibleWrite |\n                MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |\n                MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAG215] =\n        {\n            .device_name = \"NTAG215\",\n            .total_pages = 135,\n            .config_page = 131,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportCompatibleWrite |\n                MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |\n                MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAG216] =\n        {\n            .device_name = \"NTAG216\",\n            .total_pages = 231,\n            .config_page = 227,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportCompatibleWrite |\n                MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |\n                MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAGI2C1K] =\n        {\n            .device_name = \"NTAG I2C 1K\",\n            .total_pages = 231,\n            .config_page = 0,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAGI2C2K] =\n        {\n            .device_name = \"NTAG I2C 2K\",\n            .total_pages = 485,\n            .config_page = 0,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |\n                MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAGI2CPlus1K] =\n        {\n            .device_name = \"NTAG I2C Plus 1K\",\n            .total_pages = 236,\n            .config_page = 227,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth |\n                MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite |\n                MfUltralightFeatureSupportDynamicLock,\n        },\n    [MfUltralightTypeNTAGI2CPlus2K] =\n        {\n            .device_name = \"NTAG I2C Plus 2K\",\n            .total_pages = 492,\n            .config_page = 227,\n            .feature_set =\n                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |\n                MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth |\n                MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite |\n                MfUltralightFeatureSupportDynamicLock,\n        },\n};\n\nconst NfcDeviceBase nfc_device_mf_ultralight = {\n    .protocol_name = MF_ULTRALIGHT_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)mf_ultralight_alloc,\n    .free = (NfcDeviceFree)mf_ultralight_free,\n    .reset = (NfcDeviceReset)mf_ultralight_reset,\n    .copy = (NfcDeviceCopy)mf_ultralight_copy,\n    .verify = (NfcDeviceVerify)mf_ultralight_verify,\n    .load = (NfcDeviceLoad)mf_ultralight_load,\n    .save = (NfcDeviceSave)mf_ultralight_save,\n    .is_equal = (NfcDeviceEqual)mf_ultralight_is_equal,\n    .get_name = (NfcDeviceGetName)mf_ultralight_get_device_name,\n    .get_uid = (NfcDeviceGetUid)mf_ultralight_get_uid,\n    .set_uid = (NfcDeviceSetUid)mf_ultralight_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)mf_ultralight_get_base_data,\n};\n\nMfUltralightData* mf_ultralight_alloc(void) {\n    MfUltralightData* data = malloc(sizeof(MfUltralightData));\n    data->iso14443_3a_data = iso14443_3a_alloc();\n    return data;\n}\n\nvoid mf_ultralight_free(MfUltralightData* data) {\n    furi_check(data);\n\n    iso14443_3a_free(data->iso14443_3a_data);\n    free(data);\n}\n\nvoid mf_ultralight_reset(MfUltralightData* data) {\n    furi_check(data);\n\n    iso14443_3a_reset(data->iso14443_3a_data);\n}\n\nvoid mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);\n    for(size_t i = 0; i < COUNT_OF(data->counter); i++) {\n        data->counter[i] = other->counter[i];\n    }\n    for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) {\n        data->tearing_flag[i] = other->tearing_flag[i];\n    }\n    for(size_t i = 0; i < COUNT_OF(data->page); i++) {\n        data->page[i] = other->page[i];\n    }\n\n    data->type = other->type;\n    data->version = other->version;\n    data->signature = other->signature;\n\n    data->pages_read = other->pages_read;\n    data->pages_total = other->pages_total;\n    data->auth_attempts = other->auth_attempts;\n}\n\nstatic const char*\n    mf_ultralight_get_device_name_by_type(MfUltralightType type, NfcDeviceNameType name_type) {\n    if(name_type == NfcDeviceNameTypeShort &&\n       (type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21)) {\n        type = MfUltralightTypeOrigin;\n    }\n\n    return mf_ultralight_features[type].device_name;\n}\n\nbool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type) {\n    furi_check(data);\n    furi_check(device_type);\n\n    bool verified = false;\n\n    for(MfUltralightType i = 0; i < MfUltralightTypeNum; i++) {\n        const char* name = mf_ultralight_get_device_name_by_type(i, NfcDeviceNameTypeFull);\n        verified = furi_string_equal(device_type, name);\n        if(verified) {\n            data->type = i;\n            break;\n        }\n    }\n\n    return verified;\n}\n\nbool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool parsed = false;\n\n    do {\n        // Read ISO14443_3A data\n        if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;\n\n        // Read Ultralight specific data\n        // Read Mifare Ultralight format version\n        uint32_t data_format_version = 0;\n        if(!flipper_format_read_uint32(\n               ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &data_format_version, 1)) {\n            if(!flipper_format_rewind(ff)) break;\n        }\n\n        // Read Mifare Ultralight type\n        if(data_format_version > 1) {\n            if(!flipper_format_read_string(ff, MF_ULTRALIGHT_TYPE_KEY, temp_str)) break;\n            if(!mf_ultralight_verify(data, temp_str)) break;\n        }\n\n        // Read signature\n        if(!flipper_format_read_hex(\n               ff,\n               MF_ULTRALIGHT_SIGNATURE_KEY,\n               data->signature.data,\n               sizeof(MfUltralightSignature)))\n            break;\n        // Read Mifare version\n        if(!flipper_format_read_hex(\n               ff,\n               MF_ULTRALIGHT_MIFARE_VERSION_KEY,\n               (uint8_t*)&data->version,\n               sizeof(MfUltralightVersion)))\n            break;\n        // Read counters and tearing flags\n        bool counters_parsed = true;\n        for(size_t i = 0; i < 3; i++) {\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_COUNTER_KEY, i);\n            if(!flipper_format_read_uint32(\n                   ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) {\n                counters_parsed = false;\n                break;\n            }\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_TEARING_KEY, i);\n            if(!flipper_format_read_hex(\n                   ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) {\n                counters_parsed = false;\n                break;\n            }\n        }\n        if(!counters_parsed) break;\n        // Read pages\n        uint32_t pages_total = 0;\n        if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break;\n        uint32_t pages_read = 0;\n        if(data_format_version < mf_ultralight_data_format_version) { //-V547\n            pages_read = pages_total;\n        } else {\n            if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1))\n                break;\n        }\n        data->pages_total = pages_total;\n        data->pages_read = pages_read;\n\n        if((pages_read > MF_ULTRALIGHT_MAX_PAGE_NUM) || (pages_total > MF_ULTRALIGHT_MAX_PAGE_NUM))\n            break;\n\n        bool pages_parsed = true;\n        for(size_t i = 0; i < pages_total; i++) {\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_PAGE_KEY, i);\n            if(!flipper_format_read_hex(\n                   ff,\n                   furi_string_get_cstr(temp_str),\n                   data->page[i].data,\n                   sizeof(MfUltralightPage))) {\n                pages_parsed = false;\n                break;\n            }\n        }\n        if(!pages_parsed) break;\n\n        // Read authentication counter\n        if(!flipper_format_read_uint32(\n               ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) {\n            data->auth_attempts = 0;\n        }\n\n        parsed = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n\n    return parsed;\n}\n\nbool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool saved = false;\n\n    do {\n        if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;\n\n        if(!flipper_format_write_comment_cstr(ff, MF_ULTRALIGHT_PROTOCOL_NAME \" specific data\"))\n            break;\n        if(!flipper_format_write_uint32(\n               ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &mf_ultralight_data_format_version, 1))\n            break;\n\n        const char* device_type_name =\n            mf_ultralight_get_device_name_by_type(data->type, NfcDeviceNameTypeFull);\n        if(!flipper_format_write_string_cstr(ff, MF_ULTRALIGHT_TYPE_KEY, device_type_name)) break;\n        if(!flipper_format_write_hex(\n               ff,\n               MF_ULTRALIGHT_SIGNATURE_KEY,\n               data->signature.data,\n               sizeof(MfUltralightSignature)))\n            break;\n        if(!flipper_format_write_hex(\n               ff,\n               MF_ULTRALIGHT_MIFARE_VERSION_KEY,\n               (uint8_t*)&data->version,\n               sizeof(MfUltralightVersion)))\n            break;\n\n        // Write conters and tearing flags data\n        bool counters_saved = true;\n        for(size_t i = 0; i < 3; i++) {\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_COUNTER_KEY, i);\n            if(!flipper_format_write_uint32(\n                   ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) {\n                counters_saved = false;\n                break;\n            }\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_TEARING_KEY, i);\n            if(!flipper_format_write_hex(\n                   ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) {\n                counters_saved = false;\n                break;\n            }\n        }\n        if(!counters_saved) break;\n\n        // Write pages data\n        uint32_t pages_total = data->pages_total;\n        uint32_t pages_read = data->pages_read;\n        if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break;\n        if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) break;\n        bool pages_saved = true;\n        for(size_t i = 0; i < data->pages_total; i++) {\n            furi_string_printf(temp_str, \"%s %d\", MF_ULTRALIGHT_PAGE_KEY, i);\n            if(!flipper_format_write_hex(\n                   ff,\n                   furi_string_get_cstr(temp_str),\n                   data->page[i].data,\n                   sizeof(MfUltralightPage))) {\n                pages_saved = false;\n                break;\n            }\n        }\n        if(!pages_saved) break;\n\n        // Write authentication counter\n        if(!flipper_format_write_uint32(\n               ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1))\n            break;\n\n        saved = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n\n    return saved;\n}\n\nbool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    bool is_equal = false;\n    bool data_array_is_equal = true;\n\n    do {\n        if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break;\n        if(data->type != other->type) break;\n        if(data->pages_read != other->pages_read) break;\n        if(data->pages_total != other->pages_total) break;\n        if(data->auth_attempts != other->auth_attempts) break;\n\n        if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break;\n        if(memcmp(&data->signature, &other->signature, sizeof(data->signature)) != 0) break;\n\n        for(size_t i = 0; i < COUNT_OF(data->counter); i++) {\n            if(memcmp(&data->counter[i], &other->counter[i], sizeof(data->counter[i])) != 0) {\n                data_array_is_equal = false;\n                break;\n            }\n        }\n        if(!data_array_is_equal) break;\n\n        for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) {\n            if(memcmp(\n                   &data->tearing_flag[i],\n                   &other->tearing_flag[i],\n                   sizeof(data->tearing_flag[i])) != 0) {\n                data_array_is_equal = false;\n                break;\n            }\n        }\n        if(!data_array_is_equal) break;\n\n        for(size_t i = 0; i < COUNT_OF(data->page); i++) {\n            if(memcmp(&data->page[i], &other->page[i], sizeof(data->page[i])) != 0) {\n                data_array_is_equal = false;\n                break;\n            }\n        }\n        if(!data_array_is_equal) break;\n\n        is_equal = true;\n    } while(false);\n\n    return is_equal;\n}\n\nconst char*\n    mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n    furi_check(data->type < MfUltralightTypeNum);\n\n    return mf_ultralight_get_device_name_by_type(data->type, name_type);\n}\n\nconst uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len) {\n    furi_check(data);\n\n    return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);\n}\n\nbool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);\n\n    if(uid_valid) {\n        // Copy UID across first 2 pages\n        memcpy(data->page[0].data, data->iso14443_3a_data->uid, 3);\n        memcpy(data->page[1].data, &data->iso14443_3a_data->uid[3], 4);\n\n        // Calculate BCC bytes\n        data->page[0].data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2];\n        data->page[2].data[0] = uid[3] ^ uid[4] ^ uid[5] ^ uid[6];\n    }\n\n    return uid_valid;\n}\n\nIso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) {\n    furi_check(data);\n\n    return data->iso14443_3a_data;\n}\n\nMfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) {\n    furi_check(version);\n\n    MfUltralightType type = MfUltralightTypeOrigin;\n\n    if(version->storage_size == 0x0B || version->storage_size == 0x00) {\n        type = MfUltralightTypeUL11;\n    } else if(version->storage_size == 0x0E) {\n        type = MfUltralightTypeUL21;\n    } else if(version->storage_size == 0x0F) {\n        type = MfUltralightTypeNTAG213;\n    } else if(version->storage_size == 0x11) {\n        type = MfUltralightTypeNTAG215;\n    } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) {\n        if(version->prod_ver_minor == 1) {\n            if(version->storage_size == 0x13) {\n                type = MfUltralightTypeNTAGI2C1K;\n            } else if(version->storage_size == 0x15) {\n                type = MfUltralightTypeNTAGI2C2K;\n            }\n        } else if(version->prod_ver_minor == 2) {\n            if(version->storage_size == 0x13) {\n                type = MfUltralightTypeNTAGI2CPlus1K;\n            } else if(version->storage_size == 0x15) {\n                type = MfUltralightTypeNTAGI2CPlus2K;\n            }\n        }\n    } else if(version->storage_size == 0x13) {\n        type = MfUltralightTypeNTAG216;\n    }\n\n    return type;\n}\n\nuint16_t mf_ultralight_get_pages_total(MfUltralightType type) {\n    furi_check(type < MfUltralightTypeNum);\n\n    return mf_ultralight_features[type].total_pages;\n}\n\nuint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) {\n    furi_check(type < MfUltralightTypeNum);\n\n    return mf_ultralight_features[type].feature_set;\n}\n\nbool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) {\n    furi_check(iso14443_3a_data);\n\n    bool mfu_detected = (iso14443_3a_data->atqa[0] == 0x44) &&\n                        (iso14443_3a_data->atqa[1] == 0x00) && (iso14443_3a_data->sak == 0x00);\n\n    return mfu_detected;\n}\n\nuint16_t mf_ultralight_get_config_page_num(MfUltralightType type) {\n    furi_check(type < MfUltralightTypeNum);\n\n    return mf_ultralight_features[type].config_page;\n}\n\nuint8_t mf_ultralight_get_write_end_page(MfUltralightType type) {\n    furi_check(type < MfUltralightTypeNum);\n    furi_assert(\n        type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21 ||\n        type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||\n        type == MfUltralightTypeNTAG216 || type == MfUltralightTypeOrigin);\n\n    uint8_t end_page = mf_ultralight_get_config_page_num(type);\n    if(type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||\n       type == MfUltralightTypeNTAG216) {\n        end_page -= 1;\n    } else if(type == MfUltralightTypeOrigin) {\n        end_page = mf_ultralight_features[type].total_pages;\n    }\n\n    return end_page;\n}\n\nuint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) {\n    furi_check(type < MfUltralightTypeNum);\n\n    uint8_t config_page = mf_ultralight_features[type].config_page;\n    return (config_page != 0) ? config_page + 2 : 0;\n}\n\nbool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page) {\n    uint8_t pwd_page = mf_ultralight_get_pwd_page_num(type);\n    uint8_t pack_page = pwd_page + 1;\n    return (pwd_page != 0) && (page == pwd_page || page == pack_page);\n}\n\nbool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check) {\n    return (feature_set & features_to_check) != 0;\n}\n\nbool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config) {\n    furi_check(data);\n    furi_check(config);\n\n    bool config_pages_found = false;\n\n    uint16_t config_page = mf_ultralight_features[data->type].config_page;\n    if(config_page != 0) {\n        *config = (MfUltralightConfigPages*)&data->page[config_page]; //-V1027\n        config_pages_found = true;\n    }\n\n    return config_pages_found;\n}\n\nbool mf_ultralight_is_all_data_read(const MfUltralightData* data) {\n    furi_check(data);\n\n    bool all_read = false;\n\n    if(data->pages_read == data->pages_total) {\n        uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);\n        if((data->type == MfUltralightTypeMfulC) &&\n           mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportAuthenticate)) {\n            all_read = true;\n        } else if(!mf_ultralight_support_feature(\n                      feature_set, MfUltralightFeatureSupportPasswordAuth)) {\n            all_read = true;\n        } else {\n            // Having read all the pages doesn't mean that we've got everything.\n            // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,\n            // so a default read on an auth-supported NTAG is never complete.\n            MfUltralightConfigPages* config = NULL;\n            if(mf_ultralight_get_config_page(data, &config)) {\n                uint32_t pass = bit_lib_bytes_to_num_be(\n                    config->password.data, sizeof(MfUltralightAuthPassword));\n                uint16_t pack =\n                    bit_lib_bytes_to_num_be(config->pack.data, sizeof(MfUltralightAuthPack));\n                all_read = ((pass != 0) || (pack != 0));\n            }\n        }\n    }\n\n    return all_read;\n}\n\nbool mf_ultralight_is_counter_configured(const MfUltralightData* data) {\n    furi_check(data);\n\n    MfUltralightConfigPages* config = NULL;\n    bool configured = false;\n\n    switch(data->type) {\n    case MfUltralightTypeNTAG213:\n    case MfUltralightTypeNTAG215:\n    case MfUltralightTypeNTAG216:\n        if(mf_ultralight_get_config_page(data, &config)) {\n            configured = config->access.nfc_cnt_en;\n        }\n        break;\n\n    default:\n        configured = true;\n        break;\n    }\n\n    return configured;\n}\n\nvoid mf_ultralight_3des_shift_data(uint8_t* const data) {\n    furi_check(data);\n\n    uint8_t buf = data[0];\n    for(uint8_t i = 1; i < MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE; i++) {\n        data[i - 1] = data[i];\n    }\n    data[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE - 1] = buf;\n}\n\nbool mf_ultralight_3des_key_valid(const MfUltralightData* data) {\n    furi_check(data);\n    furi_check(data->type == MfUltralightTypeMfulC);\n\n    return !(data->pages_read == data->pages_total - 4);\n}\n\nconst uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data) {\n    furi_check(data);\n    furi_check(data->type == MfUltralightTypeMfulC);\n\n    return data->page[44].data;\n}\n\nvoid mf_ultralight_3des_encrypt(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* iv,\n    const uint8_t* input,\n    const uint8_t length,\n    uint8_t* out) {\n    furi_check(ctx);\n    furi_check(ck);\n    furi_check(iv);\n    furi_check(input);\n    furi_check(out);\n\n    mbedtls_des3_set2key_enc(ctx, ck);\n    mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, length, (uint8_t*)iv, input, out);\n}\n\nvoid mf_ultralight_3des_decrypt(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* iv,\n    const uint8_t* input,\n    const uint8_t length,\n    uint8_t* out) {\n    furi_check(ctx);\n    furi_check(ck);\n    furi_check(iv);\n    furi_check(input);\n    furi_check(out);\n\n    mbedtls_des3_set2key_dec(ctx, ck);\n    mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_DECRYPT, length, (uint8_t*)iv, input, out);\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>\n#include <mbedtls/include/mbedtls/des.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_ULTRALIGHT_TEARING_FLAG_DEFAULT (0xBD)\n\n#define MF_ULTRALIGHT_CMD_GET_VERSION   (0x60)\n#define MF_ULTRALIGHT_CMD_READ_PAGE     (0x30)\n#define MF_ULTRALIGHT_CMD_FAST_READ     (0x3A)\n#define MF_ULTRALIGHT_CMD_SECTOR_SELECT (0xC2)\n#define MF_ULTRALIGHT_CMD_COMP_WRITE    (0xA0)\n#define MF_ULTRALIGHT_CMD_WRITE_PAGE    (0xA2)\n#define MF_ULTRALIGHT_CMD_FAST_WRITE    (0xA6)\n#define MF_ULTRALIGHT_CMD_READ_SIG      (0x3C)\n#define MF_ULTRALIGHT_CMD_READ_CNT      (0x39)\n#define MF_ULTRALIGHT_CMD_INCR_CNT      (0xA5)\n#define MF_ULTRALIGHT_CMD_CHECK_TEARING (0x3E)\n#define MF_ULTRALIGHT_CMD_PWD_AUTH      (0x1B)\n#define MF_ULTRALIGHT_CMD_AUTH          (0x1A)\n#define MF_ULTRALIGHT_CMD_VCSL          (0x4B)\n\n#define MF_ULTRALIGHT_CMD_ACK      (0x0A)\n#define MF_ULTRALIGHT_CMD_NACK     (0x00)\n#define MF_ULTRALIGHT_CMD_AUTH_NAK (0x04)\n\n#define MF_ULTRALIGHT_MAX_CNTR_VAL       (0x00FFFFFF)\n#define MF_ULTRALIGHT_MAX_PAGE_NUM       (510)\n#define MF_ULTRALIGHT_PAGE_SIZE          (4U)\n#define MF_ULTRALIGHT_SIGNATURE_SIZE     (32)\n#define MF_ULTRALIGHT_COUNTER_SIZE       (3)\n#define MF_ULTRALIGHT_COUNTER_NUM        (3)\n#define MF_ULTRALIGHT_TEARING_FLAG_SIZE  (1)\n#define MF_ULTRALIGHT_TEARING_FLAG_NUM   (3)\n#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4)\n#define MF_ULTRALIGHT_AUTH_PACK_SIZE     (2)\n\n#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE      (9)\n#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE       (16)\n#define MF_ULTRALIGHT_C_AUTH_DATA_SIZE          (MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE)\n#define MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE      (8)\n#define MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE     (8)\n#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0)\n#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8)\n#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE     (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1)\n\ntypedef enum {\n    MfUltralightErrorNone,\n    MfUltralightErrorNotPresent,\n    MfUltralightErrorProtocol,\n    MfUltralightErrorAuth,\n    MfUltralightErrorTimeout,\n} MfUltralightError;\n\ntypedef enum {\n    MfUltralightTypeOrigin,\n    MfUltralightTypeNTAG203,\n    MfUltralightTypeMfulC,\n    MfUltralightTypeUL11,\n    MfUltralightTypeUL21,\n    MfUltralightTypeNTAG213,\n    MfUltralightTypeNTAG215,\n    MfUltralightTypeNTAG216,\n    MfUltralightTypeNTAGI2C1K,\n    MfUltralightTypeNTAGI2C2K,\n    MfUltralightTypeNTAGI2CPlus1K,\n    MfUltralightTypeNTAGI2CPlus2K,\n\n    MfUltralightTypeNum,\n} MfUltralightType;\n\ntypedef enum {\n    MfUltralightFeatureSupportReadVersion = (1U << 0),\n    MfUltralightFeatureSupportReadSignature = (1U << 1),\n    MfUltralightFeatureSupportReadCounter = (1U << 2),\n    MfUltralightFeatureSupportCheckTearingFlag = (1U << 3),\n    MfUltralightFeatureSupportFastRead = (1U << 4),\n    MfUltralightFeatureSupportIncCounter = (1U << 5),\n    MfUltralightFeatureSupportFastWrite = (1U << 6),\n    MfUltralightFeatureSupportCompatibleWrite = (1U << 7),\n    MfUltralightFeatureSupportPasswordAuth = (1U << 8),\n    MfUltralightFeatureSupportVcsl = (1U << 9),\n    MfUltralightFeatureSupportSectorSelect = (1U << 10),\n    MfUltralightFeatureSupportSingleCounter = (1U << 11),\n    MfUltralightFeatureSupportAsciiMirror = (1U << 12),\n    MfUltralightFeatureSupportCounterInMemory = (1U << 13),\n    MfUltralightFeatureSupportDynamicLock = (1U << 14),\n    MfUltralightFeatureSupportAuthenticate = (1U << 15),\n} MfUltralightFeatureSupport;\n\ntypedef struct {\n    uint8_t data[MF_ULTRALIGHT_PAGE_SIZE];\n} MfUltralightPage;\n\ntypedef struct {\n    MfUltralightPage page[4];\n} MfUltralightPageReadCommandData;\n\ntypedef struct {\n    uint8_t header;\n    uint8_t vendor_id;\n    uint8_t prod_type;\n    uint8_t prod_subtype;\n    uint8_t prod_ver_major;\n    uint8_t prod_ver_minor;\n    uint8_t storage_size;\n    uint8_t protocol_type;\n} MfUltralightVersion;\n\ntypedef struct {\n    uint8_t data[MF_ULTRALIGHT_SIGNATURE_SIZE];\n} MfUltralightSignature;\n\ntypedef union {\n    uint32_t counter;\n    uint8_t data[MF_ULTRALIGHT_COUNTER_SIZE];\n} MfUltralightCounter;\n\ntypedef struct {\n    uint8_t data;\n} MfUltralightTearingFlag;\n\ntypedef struct {\n    uint8_t data[MF_ULTRALIGHT_AUTH_PASSWORD_SIZE];\n} MfUltralightAuthPassword;\n\ntypedef struct {\n    uint8_t data[MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE];\n} MfUltralightC3DesAuthKey;\n\ntypedef struct {\n    uint8_t data[MF_ULTRALIGHT_AUTH_PACK_SIZE];\n} MfUltralightAuthPack;\n\ntypedef enum {\n    MfUltralightMirrorNone,\n    MfUltralightMirrorUid,\n    MfUltralightMirrorCounter,\n    MfUltralightMirrorUidCounter,\n} MfUltralightMirrorConf;\n\ntypedef struct FURI_PACKED {\n    union {\n        uint8_t value;\n        struct {\n            uint8_t rfui1                      : 2;\n            bool strg_mod_en                   : 1;\n            bool rfui2                         : 1;\n            uint8_t mirror_byte                : 2;\n            MfUltralightMirrorConf mirror_conf : 2;\n        };\n    } mirror;\n    uint8_t rfui1;\n    uint8_t mirror_page;\n    uint8_t auth0;\n    union {\n        uint8_t value;\n        struct {\n            uint8_t authlim       : 3;\n            bool nfc_cnt_pwd_prot : 1;\n            bool nfc_cnt_en       : 1;\n            bool nfc_dis_sec1     : 1; // NTAG I2C Plus only\n            bool cfglck           : 1;\n            bool prot             : 1;\n        };\n    } access;\n    uint8_t vctid;\n    uint8_t rfui2[2];\n    MfUltralightAuthPassword password;\n    MfUltralightAuthPack pack;\n    uint8_t rfui3[2];\n} MfUltralightConfigPages;\n\ntypedef struct {\n    Iso14443_3aData* iso14443_3a_data;\n    MfUltralightType type;\n    MfUltralightVersion version;\n    MfUltralightSignature signature;\n    MfUltralightCounter counter[MF_ULTRALIGHT_COUNTER_NUM];\n    MfUltralightTearingFlag tearing_flag[MF_ULTRALIGHT_TEARING_FLAG_NUM];\n    MfUltralightPage page[MF_ULTRALIGHT_MAX_PAGE_NUM];\n    uint16_t pages_read;\n    uint16_t pages_total;\n    uint32_t auth_attempts;\n} MfUltralightData;\n\nextern const NfcDeviceBase nfc_device_mf_ultralight;\n\nMfUltralightData* mf_ultralight_alloc(void);\n\nvoid mf_ultralight_free(MfUltralightData* data);\n\nvoid mf_ultralight_reset(MfUltralightData* data);\n\nvoid mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other);\n\nbool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type);\n\nbool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version);\n\nbool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff);\n\nbool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other);\n\nconst char*\n    mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len);\n\nbool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len);\n\nIso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data);\n\nMfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version);\n\nuint16_t mf_ultralight_get_pages_total(MfUltralightType type);\n\nuint32_t mf_ultralight_get_feature_support_set(MfUltralightType type);\n\nuint16_t mf_ultralight_get_config_page_num(MfUltralightType type);\n\nuint8_t mf_ultralight_get_write_end_page(MfUltralightType type);\n\nuint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type);\n\nbool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page_num);\n\nbool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check);\n\nbool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config);\n\nbool mf_ultralight_is_all_data_read(const MfUltralightData* data);\n\nbool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data);\n\nbool mf_ultralight_is_counter_configured(const MfUltralightData* data);\n\nvoid mf_ultralight_3des_shift_data(uint8_t* const arr);\n\nbool mf_ultralight_3des_key_valid(const MfUltralightData* data);\n\nconst uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data);\n\nvoid mf_ultralight_3des_encrypt(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* iv,\n    const uint8_t* input,\n    const uint8_t length,\n    uint8_t* out);\n\nvoid mf_ultralight_3des_decrypt(\n    mbedtls_des3_context* ctx,\n    const uint8_t* ck,\n    const uint8_t* iv,\n    const uint8_t* input,\n    const uint8_t length,\n    uint8_t* out);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c",
    "content": "#include \"mf_ultralight_listener_i.h\"\n#include \"mf_ultralight_listener_defs.h\"\n\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#define TAG \"MfUltralightListener\"\n\n#define MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE (256)\n\ntypedef struct {\n    uint8_t cmd;\n    size_t cmd_len_bits;\n    MfUltralightListenerCommandCallback callback;\n} MfUltralightListenerCmdHandler;\n\nstatic bool mf_ultralight_listener_check_access(\n    MfUltralightListener* instance,\n    uint16_t start_page,\n    MfUltralightListenerAccessType access_type) {\n    bool access_success = true;\n\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAuthenticate)) {\n        access_success = mf_ultralight_c_check_access(\n            instance->data, start_page, access_type, instance->auth_state);\n    } else if(mf_ultralight_support_feature(\n                  instance->features, MfUltralightFeatureSupportPasswordAuth)) {\n        access_success = mf_ultralight_common_check_access(instance, start_page, access_type);\n    }\n    return access_success;\n}\n\nstatic void mf_ultralight_listener_send_short_resp(MfUltralightListener* instance, uint8_t data) {\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_set_size(instance->tx_buffer, 4);\n    bit_buffer_set_byte(instance->tx_buffer, 0, data);\n    iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_buffer);\n}\n\nstatic void mf_ultralight_listener_perform_read(\n    MfUltralightPage* pages,\n    MfUltralightListener* instance,\n    uint16_t start_page,\n    uint8_t page_cnt,\n    bool do_i2c_page_check) {\n    uint16_t pages_total = instance->data->pages_total;\n    mf_ultralight_mirror_read_prepare(start_page, instance);\n    for(uint8_t i = 0, rollover = 0; i < page_cnt; i++) {\n        uint16_t page = start_page + i;\n\n        bool page_restricted = !mf_ultralight_listener_check_access(\n            instance, page, MfUltralightListenerAccessTypeRead);\n\n        if(do_i2c_page_check && !mf_ultralight_i2c_validate_pages(page, page, instance))\n            memset(pages[i].data, 0, sizeof(MfUltralightPage));\n        else if(mf_ultralight_is_page_pwd_or_pack(instance->data->type, page))\n            memset(pages[i].data, 0, sizeof(MfUltralightPage));\n        else {\n            if(do_i2c_page_check)\n                page = mf_ultralight_i2c_provide_page_by_requested(page, instance);\n\n            page = page_restricted ? rollover++ : page % pages_total;\n            pages[i] = instance->data->page[page];\n\n            mf_ultralight_mirror_read_handler(page, pages[i].data, instance);\n        }\n    }\n    mf_ultralight_single_counter_try_increase(instance);\n}\n\nstatic MfUltralightCommand mf_ultralight_listener_perform_write(\n    MfUltralightListener* instance,\n    const uint8_t* const rx_data,\n    uint16_t start_page,\n    bool do_i2c_check) {\n    MfUltralightCommand command = MfUltralightCommandProcessedACK;\n\n    if(start_page < 2 && instance->sector == 0)\n        command = MfUltralightCommandNotProcessedNAK;\n    else if(start_page == 2 && instance->sector == 0)\n        mf_ultralight_static_lock_bytes_write(instance->static_lock, *((uint16_t*)&rx_data[2]));\n    else if(start_page == 3 && instance->sector == 0)\n        mf_ultralight_capability_container_write(&instance->data->page[start_page], rx_data);\n    else if(mf_ultralight_is_page_dynamic_lock(instance, start_page))\n        mf_ultralight_dynamic_lock_bytes_write(instance->dynamic_lock, *((uint32_t*)rx_data));\n    else {\n        uint16_t page = start_page;\n        if(do_i2c_check) page = mf_ultralight_i2c_provide_page_by_requested(start_page, instance);\n\n        memcpy(instance->data->page[page].data, rx_data, sizeof(MfUltralightPage));\n    }\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_read_page_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    uint16_t start_page = bit_buffer_get_byte(buffer, 1);\n    uint16_t pages_total = instance->data->pages_total;\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_READ: %d\", start_page);\n\n    do {\n        bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type);\n\n        if(do_i2c_check) {\n            if(!mf_ultralight_i2c_validate_pages(start_page, start_page, instance)) break;\n        } else if(pages_total < start_page) {\n            break;\n        }\n\n        if(!mf_ultralight_listener_check_access(\n               instance, start_page, MfUltralightListenerAccessTypeRead)) {\n            break;\n        }\n\n        MfUltralightPage pages[4] = {};\n        mf_ultralight_listener_perform_read(pages, instance, start_page, 4, do_i2c_check);\n\n        bit_buffer_copy_bytes(instance->tx_buffer, (uint8_t*)pages, sizeof(pages));\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_fast_read_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n    FURI_LOG_T(TAG, \"CMD_FAST_READ\");\n\n    do {\n        if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportFastRead))\n            break;\n        uint16_t pages_total = instance->data->pages_total;\n        uint16_t start_page = bit_buffer_get_byte(buffer, 1);\n        uint16_t end_page = bit_buffer_get_byte(buffer, 2);\n        bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type);\n\n        if(do_i2c_check) {\n            if(!mf_ultralight_i2c_validate_pages(start_page, end_page, instance)) {\n                command = MfUltralightCommandNotProcessedNAK;\n                break;\n            }\n        } else if(end_page > pages_total - 1) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        if(end_page < start_page) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        if(!mf_ultralight_listener_check_access(\n               instance, start_page, MfUltralightListenerAccessTypeRead) ||\n           !mf_ultralight_listener_check_access(\n               instance, end_page, MfUltralightListenerAccessTypeRead)) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        MfUltralightPage pages[64] = {};\n        uint8_t page_cnt = (end_page - start_page) + 1;\n        mf_ultralight_listener_perform_read(pages, instance, start_page, page_cnt, do_i2c_check);\n\n        bit_buffer_copy_bytes(instance->tx_buffer, (uint8_t*)pages, page_cnt * 4);\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_write_page_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    uint8_t start_page = bit_buffer_get_byte(buffer, 1);\n    uint16_t pages_total = instance->data->pages_total;\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_WRITE\");\n\n    do {\n        bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type);\n\n        if(do_i2c_check) {\n            if(!mf_ultralight_i2c_validate_pages(start_page, start_page, instance)) break;\n        } else if(pages_total < start_page) {\n            break;\n        }\n\n        if(!mf_ultralight_listener_check_access(\n               instance, start_page, MfUltralightListenerAccessTypeWrite))\n            break;\n\n        if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page)) break;\n        if(mf_ultralight_dynamic_lock_check_page(instance, start_page)) break;\n\n        const uint8_t* rx_data = bit_buffer_get_data(buffer);\n        command =\n            mf_ultralight_listener_perform_write(instance, &rx_data[2], start_page, do_i2c_check);\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_fast_write_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n    FURI_LOG_T(TAG, \"CMD_FAST_WRITE\");\n\n    do {\n        if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportFastWrite))\n            break;\n\n        uint8_t start_page = bit_buffer_get_byte(buffer, 1);\n        uint8_t end_page = bit_buffer_get_byte(buffer, 2);\n        if(start_page != 0xF0 || end_page != 0xFF) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        // No SRAM emulation support\n\n        command = MfUltralightCommandProcessedACK;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_read_version_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    UNUSED(buffer);\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n\n    FURI_LOG_T(TAG, \"CMD_GET_VERSION\");\n\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadVersion)) {\n        bit_buffer_copy_bytes(\n            instance->tx_buffer, (uint8_t*)&instance->data->version, sizeof(MfUltralightVersion));\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n    }\n\n    return command;\n}\n\nstatic MfUltralightCommand mf_ultralight_listener_read_signature_handler(\n    MfUltralightListener* instance,\n    BitBuffer* buffer) {\n    UNUSED(buffer);\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n\n    FURI_LOG_T(TAG, \"CMD_READ_SIG\");\n\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadSignature)) {\n        bit_buffer_copy_bytes(\n            instance->tx_buffer, instance->data->signature.data, sizeof(MfUltralightSignature));\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n    }\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_read_counter_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_READ_CNT\");\n\n    do {\n        uint8_t counter_num = bit_buffer_get_byte(buffer, 1);\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportReadCounter))\n            break;\n\n        if(mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportSingleCounter)) {\n            if(instance->config == NULL) break;\n\n            if(!instance->config->access.nfc_cnt_en || counter_num != 2) break;\n\n            if(instance->config->access.nfc_cnt_pwd_prot) {\n                if(instance->auth_state != MfUltralightListenerAuthStateSuccess) {\n                    break;\n                }\n            }\n        }\n\n        if(counter_num > 2) break;\n        uint8_t cnt_value[3] = {\n            (instance->data->counter[counter_num].counter >> 0) & 0xff,\n            (instance->data->counter[counter_num].counter >> 8) & 0xff,\n            (instance->data->counter[counter_num].counter >> 16) & 0xff,\n        };\n        bit_buffer_copy_bytes(instance->tx_buffer, cnt_value, sizeof(cnt_value));\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand mf_ultralight_listener_increase_counter_handler(\n    MfUltralightListener* instance,\n    BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_INCR_CNT\");\n\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportIncCounter)) {\n            command = MfUltralightCommandNotProcessedSilent;\n            break;\n        }\n\n        uint8_t counter_num = bit_buffer_get_byte(buffer, 1);\n        if(counter_num > 2) break;\n\n        if(instance->data->counter[counter_num].counter == MF_ULTRALIGHT_MAX_CNTR_VAL) {\n            command = MfUltralightCommandProcessed;\n            break;\n        }\n\n        MfUltralightCounter buf_counter = {};\n        bit_buffer_write_bytes_mid(buffer, buf_counter.data, 2, sizeof(buf_counter.data));\n        uint32_t incr_value = buf_counter.counter;\n\n        if(instance->data->counter[counter_num].counter + incr_value > MF_ULTRALIGHT_MAX_CNTR_VAL)\n            break;\n\n        instance->data->counter[counter_num].counter += incr_value;\n        command = MfUltralightCommandProcessedACK;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand mf_ultralight_listener_check_tearing_handler(\n    MfUltralightListener* instance,\n    BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_CHECK_TEARING\");\n\n    do {\n        uint8_t tearing_flag_num = bit_buffer_get_byte(buffer, 1);\n        if(!mf_ultralight_support_feature(\n               instance->features,\n               MfUltralightFeatureSupportCheckTearingFlag |\n                   MfUltralightFeatureSupportSingleCounter)) {\n            break;\n        }\n        if(mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportSingleCounter) &&\n           (tearing_flag_num != 2)) {\n            break;\n        }\n        if(tearing_flag_num >= MF_ULTRALIGHT_TEARING_FLAG_NUM) {\n            break;\n        }\n\n        bit_buffer_set_size_bytes(instance->tx_buffer, 1);\n        bit_buffer_set_byte(\n            instance->tx_buffer, 0, instance->data->tearing_flag[tearing_flag_num].data);\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_vcsl_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n    UNUSED(instance);\n    UNUSED(buffer);\n    FURI_LOG_T(TAG, \"CMD_VCSL\");\n    do {\n        if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportVcsl))\n            break;\n\n        MfUltralightConfigPages* config;\n        if(!mf_ultralight_get_config_page(instance->data, &config)) break;\n\n        bit_buffer_set_size_bytes(instance->tx_buffer, 1);\n        bit_buffer_set_byte(instance->tx_buffer, 0, config->vctid);\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_listener_auth_handler(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n\n    FURI_LOG_T(TAG, \"CMD_AUTH\");\n\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportPasswordAuth))\n            break;\n\n        MfUltralightAuthPassword password;\n        bit_buffer_write_bytes_mid(buffer, password.data, 1, sizeof(password.data));\n\n        if(instance->callback) {\n            instance->mfu_event_data.password = password;\n            instance->mfu_event.type = MfUltralightListenerEventTypeAuth;\n            instance->callback(instance->generic_event, instance->context);\n        }\n\n        bool auth_success =\n            mf_ultralight_auth_check_password(&instance->config->password, &password);\n        bool card_locked = mf_ultralight_auth_limit_check_and_update(instance, auth_success);\n\n        if(card_locked) {\n            command = MfUltralightCommandNotProcessedAuthNAK;\n            break;\n        }\n\n        if(!auth_success) break;\n\n        bit_buffer_copy_bytes(\n            instance->tx_buffer, instance->config->pack.data, sizeof(MfUltralightAuthPack));\n        instance->auth_state = MfUltralightListenerAuthStateSuccess;\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n\n        command = MfUltralightCommandProcessed;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_comp_write_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n    FURI_LOG_T(TAG, \"CMD_CM_WR_2\");\n\n    do {\n        if(bit_buffer_get_size_bytes(buffer) != 16) break;\n\n        const uint8_t* rx_data = bit_buffer_get_data(buffer);\n        uint8_t start_page = instance->composite_cmd.data;\n\n        command = mf_ultralight_listener_perform_write(instance, rx_data, start_page, false);\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_comp_write_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedSilent;\n\n    FURI_LOG_T(TAG, \"CMD_CM_WR_1\");\n\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportCompatibleWrite))\n            break;\n\n        uint8_t start_page = bit_buffer_get_byte(buffer, 1);\n        uint16_t last_page = instance->data->pages_total - 1;\n\n        if(start_page < 2 || start_page > last_page) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        if(!mf_ultralight_listener_check_access(\n               instance, start_page, MfUltralightListenerAccessTypeWrite)) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page) ||\n           mf_ultralight_dynamic_lock_check_page(instance, start_page)) {\n            command = MfUltralightCommandNotProcessedNAK;\n            break;\n        }\n\n        instance->composite_cmd.data = start_page;\n        command = MfUltralightCommandProcessedACK;\n        mf_ultralight_composite_command_set_next(instance, mf_ultralight_comp_write_handler_p2);\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_sector_select_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n    UNUSED(instance);\n    UNUSED(buffer);\n    FURI_LOG_T(TAG, \"CMD_SEC_SEL_2\");\n\n    do {\n        if(bit_buffer_get_size_bytes(buffer) != 4) break;\n        uint8_t sector = bit_buffer_get_byte(buffer, 0);\n        if(sector == 0xFF) break;\n\n        instance->sector = sector;\n        command = MfUltralightCommandProcessedSilent;\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_sector_select_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n    UNUSED(buffer);\n    FURI_LOG_T(TAG, \"CMD_SEC_SEL_1\");\n\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportSectorSelect) &&\n           bit_buffer_get_byte(buffer, 1) == 0xFF)\n            break;\n\n        command = MfUltralightCommandProcessedACK;\n        mf_ultralight_composite_command_set_next(instance, mf_ultralight_sector_select_handler_p2);\n    } while(false);\n\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_c_authenticate_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n    FURI_LOG_T(TAG, \"CMD_ULC_AUTH_2\");\n    UNUSED(instance);\n    do {\n        if(bit_buffer_get_byte(buffer, 0) != 0xAF ||\n           bit_buffer_get_size_bytes(buffer) != MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE ||\n           !mf_ultralight_3des_key_valid(instance->data))\n            break;\n\n        const uint8_t* data = bit_buffer_get_data(buffer) + 1;\n        const uint8_t* iv = data + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;\n\n        uint8_t out[MF_ULTRALIGHT_C_AUTH_DATA_SIZE] = {0};\n\n        const uint8_t* ck = mf_ultralight_3des_get_key(instance->data);\n        mf_ultralight_3des_decrypt(\n            &instance->des_context, ck, instance->encB, data, sizeof(out), out);\n\n        uint8_t* rndA = out;\n        const uint8_t* decoded_shifted_rndB = out + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;\n\n        mf_ultralight_3des_shift_data(rndA);\n        mf_ultralight_3des_shift_data(instance->rndB);\n        if(memcmp(decoded_shifted_rndB, instance->rndB, sizeof(instance->rndB)) == 0) {\n            instance->auth_state = MfUltralightListenerAuthStateSuccess;\n        }\n\n        mf_ultralight_3des_encrypt(\n            &instance->des_context, ck, iv, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE, rndA);\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_byte(instance->tx_buffer, 0x00);\n        bit_buffer_append_bytes(instance->tx_buffer, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);\n\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n\n        command = MfUltralightCommandProcessed;\n    } while(false);\n    return command;\n}\n\nstatic MfUltralightCommand\n    mf_ultralight_c_authenticate_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;\n    FURI_LOG_T(TAG, \"CMD_ULC_AUTH_1\");\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->features, MfUltralightFeatureSupportAuthenticate) &&\n           bit_buffer_get_byte(buffer, 1) == 0x00)\n            break;\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_byte(instance->tx_buffer, 0xAF);\n\n        furi_hal_random_fill_buf(instance->rndB, sizeof(instance->rndB));\n\n        const uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0};\n        const uint8_t* ck = mf_ultralight_3des_get_key(instance->data);\n\n        mf_ultralight_3des_encrypt(\n            &instance->des_context, ck, iv, instance->rndB, sizeof(instance->rndB), instance->encB);\n\n        bit_buffer_append_bytes(instance->tx_buffer, instance->encB, sizeof(instance->encB));\n\n        iso14443_3a_listener_send_standard_frame(\n            instance->iso14443_3a_listener, instance->tx_buffer);\n        command = MfUltralightCommandProcessed;\n        mf_ultralight_composite_command_set_next(\n            instance, mf_ultralight_c_authenticate_handler_p2);\n    } while(false);\n    return command;\n}\n\nstatic const MfUltralightListenerCmdHandler mf_ultralight_command[] = {\n    {\n        .cmd = MF_ULTRALIGHT_CMD_READ_PAGE,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_listener_read_page_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_FAST_READ,\n        .cmd_len_bits = 3 * 8,\n        .callback = mf_ultralight_listener_fast_read_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_WRITE_PAGE,\n        .cmd_len_bits = 6 * 8,\n        .callback = mf_ultralight_listener_write_page_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_FAST_WRITE,\n        .cmd_len_bits = 67 * 8,\n        .callback = mf_ultralight_listener_fast_write_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_GET_VERSION,\n        .cmd_len_bits = 8,\n        .callback = mf_ultralight_listener_read_version_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_READ_SIG,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_listener_read_signature_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_READ_CNT,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_listener_read_counter_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_CHECK_TEARING,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_listener_check_tearing_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_PWD_AUTH,\n        .cmd_len_bits = 5 * 8,\n        .callback = mf_ultralight_listener_auth_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_INCR_CNT,\n        .cmd_len_bits = 6 * 8,\n        .callback = mf_ultralight_listener_increase_counter_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_SECTOR_SELECT,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_sector_select_handler_p1,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_COMP_WRITE,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_comp_write_handler_p1,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_VCSL,\n        .cmd_len_bits = 21 * 8,\n        .callback = mf_ultralight_listener_vcsl_handler,\n    },\n    {\n        .cmd = MF_ULTRALIGHT_CMD_AUTH,\n        .cmd_len_bits = 2 * 8,\n        .callback = mf_ultralight_c_authenticate_handler_p1,\n    }};\n\nstatic void mf_ultralight_listener_prepare_emulation(MfUltralightListener* instance) {\n    MfUltralightData* data = instance->data;\n    instance->features = mf_ultralight_get_feature_support_set(data->type);\n    mf_ultralight_get_config_page(data, &instance->config);\n    mf_ultralight_mirror_prepare_emulation(instance);\n    mf_ultralight_static_lock_bytes_prepare(instance);\n    mf_ultralight_dynamic_lock_bytes_prepare(instance);\n}\n\nstatic NfcCommand mf_ultralight_command_postprocess(\n    MfUltralightCommand mfu_command,\n    MfUltralightListener* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    if(mfu_command == MfUltralightCommandProcessedACK) {\n        mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK);\n        command = NfcCommandContinue;\n    } else if(mfu_command == MfUltralightCommandProcessedSilent) {\n        command = NfcCommandReset;\n    } else if(mfu_command != MfUltralightCommandProcessed) {\n        instance->auth_state = MfUltralightListenerAuthStateIdle;\n        command = NfcCommandSleep;\n\n        if(mfu_command == MfUltralightCommandNotProcessedNAK) {\n            mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_NACK);\n        } else if(mfu_command == MfUltralightCommandNotProcessedAuthNAK) {\n            mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_AUTH_NAK);\n        }\n    }\n\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_reset_listener_state(\n    MfUltralightListener* instance,\n    Iso14443_3aListenerEventType event_type) {\n    mf_ultralight_composite_command_reset(instance);\n    mf_ultralight_single_counter_try_to_unlock(instance, event_type);\n    instance->sector = 0;\n    instance->auth_state = MfUltralightListenerAuthStateIdle;\n    return NfcCommandSleep;\n}\n\nMfUltralightListener* mf_ultralight_listener_alloc(\n    Iso14443_3aListener* iso14443_3a_listener,\n    MfUltralightData* data) {\n    furi_assert(iso14443_3a_listener);\n\n    MfUltralightListener* instance = malloc(sizeof(MfUltralightListener));\n    instance->mirror.ascii_mirror_data = furi_string_alloc();\n    instance->iso14443_3a_listener = iso14443_3a_listener;\n    instance->data = data;\n    mf_ultralight_static_lock_bytes_prepare(instance);\n    mf_ultralight_listener_prepare_emulation(instance);\n    mf_ultralight_composite_command_reset(instance);\n    instance->sector = 0;\n    instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE);\n\n    instance->mfu_event.data = &instance->mfu_event_data;\n    instance->generic_event.protocol = NfcProtocolMfUltralight;\n    instance->generic_event.instance = instance;\n    instance->generic_event.event_data = &instance->mfu_event;\n    mbedtls_des3_init(&instance->des_context);\n\n    return instance;\n}\n\nvoid mf_ultralight_listener_free(MfUltralightListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    furi_string_free(instance->mirror.ascii_mirror_data);\n    mbedtls_des3_free(&instance->des_context);\n    free(instance);\n}\n\nconst MfUltralightData* mf_ultralight_listener_get_data(MfUltralightListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nvoid mf_ultralight_listener_set_callback(\n    MfUltralightListener* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nNfcCommand mf_ultralight_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n    furi_assert(event.event_data);\n\n    MfUltralightListener* instance = context;\n    Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;\n    BitBuffer* rx_buffer = iso14443_3a_event->data->buffer;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {\n        MfUltralightCommand mfu_command = MfUltralightCommandNotFound;\n        size_t size = bit_buffer_get_size(rx_buffer);\n        uint8_t cmd = bit_buffer_get_byte(rx_buffer, 0);\n\n        if(mf_ultralight_composite_command_in_progress(instance)) {\n            mfu_command = mf_ultralight_composite_command_run(instance, rx_buffer);\n        } else {\n            for(size_t i = 0; i < COUNT_OF(mf_ultralight_command); i++) {\n                if(size != mf_ultralight_command[i].cmd_len_bits) continue;\n                if(cmd != mf_ultralight_command[i].cmd) continue;\n                mfu_command = mf_ultralight_command[i].callback(instance, rx_buffer);\n\n                if(mfu_command != MfUltralightCommandNotFound) break;\n            }\n        }\n        command = mf_ultralight_command_postprocess(mfu_command, instance);\n    } else if(\n        iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedData ||\n        iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff ||\n        iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted) {\n        command = mf_ultralight_reset_listener_state(instance, iso14443_3a_event->type);\n    }\n\n    return command;\n}\n\nconst NfcListenerBase mf_ultralight_listener = {\n    .alloc = (NfcListenerAlloc)mf_ultralight_listener_alloc,\n    .free = (NfcListenerFree)mf_ultralight_listener_free,\n    .get_data = (NfcListenerGetData)mf_ultralight_listener_get_data,\n    .set_callback = (NfcListenerSetCallback)mf_ultralight_listener_set_callback,\n    .run = (NfcListenerRun)mf_ultralight_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h",
    "content": "#pragma once\n\n#include \"mf_ultralight.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct MfUltralightListener MfUltralightListener;\n\ntypedef enum {\n    MfUltralightListenerEventTypeAuth,\n} MfUltralightListenerEventType;\n\ntypedef struct {\n    union {\n        MfUltralightAuthPassword password;\n    };\n} MfUltralightListenerEventData;\n\ntypedef struct {\n    MfUltralightListenerEventType type;\n    MfUltralightListenerEventData* data;\n} MfUltralightListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcListenerBase mf_ultralight_listener;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c",
    "content": "#include \"mf_ultralight_listener_i.h\"\n\n#include <furi.h>\n\n#define MF_ULTRALIGHT_STATIC_BIT_LOCK_OTP_CC   0\n#define MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_9_4   1\n#define MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_15_10 2\n\n#define MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, bit) (((lock_bits) & (1U << (bit))) != 0)\n#define MF_ULTRALIGHT_BITS_SET(lock_bits, mask)  ((lock_bits) |= (mask))\n#define MF_ULTRALIGHT_BITS_CLR(lock_bits, mask)  ((lock_bits) &= ~(mask))\n\n#define MF_ULTRALIGHT_PAGE_LOCKED(lock_bits, page) MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, page)\n\n#define MF_ULTRALIGHT_STATIC_BIT_OTP_CC_LOCKED(lock_bits) \\\n    MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_OTP_CC)\n\n#define MF_ULTRALIGHT_STATIC_BITS_9_4_LOCKED(lock_bits) \\\n    MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_9_4)\n\n#define MF_ULTRALIGHT_STATIC_BITS_15_10_LOCKED(lock_bits) \\\n    MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_15_10)\n\n#define MF_ULTRALIGHT_STATIC_LOCK_L_OTP_CC_MASK (1U << 3)\n#define MF_ULTRALIGHT_STATIC_LOCK_L_9_4_MASK \\\n    ((1U << 9) | (1U << 8) | (1U << 7) | (1U << 6) | (1U << 5) | (1U << 4))\n#define MF_ULTRALIGHT_STATIC_LOCK_L_15_10_MASK \\\n    ((1U << 15) | (1U << 14) | (1U << 13) | (1U << 12) | (1U << 11) | (1U << 10))\n\n#define MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, start, end) (((page) >= (start)) && ((page) <= (end)))\n\n#define MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(page) \\\n    MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x00EC, 0x00ED)\n\n#define MF_ULTRALIGHT_I2C_PAGE_ON_MIRRORED_SESSION_REG(page) \\\n    MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x00F8, 0x00F9)\n\n#define MF_ULTRALIGHT_AUTH_RESET_ATTEMPTS(instance)    (instance->data->auth_attempts = 0)\n#define MF_ULTRALIGHT_AUTH_INCREASE_ATTEMPTS(instance) (instance->data->auth_attempts++)\n\nstatic MfUltralightMirrorConf mf_ultralight_mirror_check_mode(\n    const MfUltralightConfigPages* const config,\n    const MfUltralightListenerAuthState auth_state) {\n    MfUltralightMirrorConf mirror_mode = config->mirror.mirror_conf;\n\n    if(mirror_mode == MfUltralightMirrorNone || mirror_mode == MfUltralightMirrorUid)\n        return mirror_mode;\n\n    if(!config->access.nfc_cnt_en ||\n       (config->access.nfc_cnt_pwd_prot && auth_state != MfUltralightListenerAuthStateSuccess)) {\n        mirror_mode = mirror_mode == MfUltralightMirrorCounter ? MfUltralightMirrorNone :\n                                                                 MfUltralightMirrorUid;\n    }\n    return mirror_mode;\n}\n\nstatic bool mf_ultralight_mirror_check_boundaries(MfUltralightListener* instance) {\n    const MfUltralightConfigPages* const conf = instance->config;\n\n    uint8_t last_user_page = mf_ultralight_get_config_page_num(instance->data->type) - 2;\n\n    uint8_t max_page_offset = 0;\n    uint8_t max_byte_offset = 2;\n\n    MfUltralightMirrorConf mode = mf_ultralight_mirror_check_mode(conf, instance->auth_state);\n\n    bool result = false;\n    bool again = false;\n    do {\n        if(mode == MfUltralightMirrorNone) {\n            break;\n        } else if(mode == MfUltralightMirrorUid) {\n            max_page_offset = 3;\n        } else if(mode == MfUltralightMirrorCounter) {\n            max_page_offset = 1;\n        } else if(mode == MfUltralightMirrorUidCounter) {\n            max_page_offset = 5;\n            max_byte_offset = 3;\n        }\n\n        instance->mirror.actual_mode = mode;\n\n        if(conf->mirror_page <= 3) break;\n        if(conf->mirror_page < last_user_page - max_page_offset) {\n            result = true;\n            break;\n        }\n        if(conf->mirror_page == last_user_page - max_page_offset) {\n            result = (conf->mirror.mirror_byte <= max_byte_offset);\n            break;\n        }\n\n        if(conf->mirror_page > last_user_page - max_page_offset &&\n           mode == MfUltralightMirrorUidCounter) {\n            mode = MfUltralightMirrorUid;\n            again = true;\n        }\n    } while(again);\n\n    return result;\n}\n\nstatic bool mf_ultralight_mirror_enabled(MfUltralightListener* instance) {\n    bool mirror_enabled = false;\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAsciiMirror) &&\n       (instance->config != NULL) && mf_ultralight_mirror_check_boundaries(instance)) {\n        mirror_enabled = true;\n    }\n    instance->mirror.enabled = mirror_enabled;\n    return instance->mirror.enabled;\n}\n\nstatic uint8_t mf_ultralight_get_mirror_data_size(MfUltralightMirrorConf mode) {\n    switch(mode) {\n    case MfUltralightMirrorUid:\n        return 14;\n    case MfUltralightMirrorCounter:\n        return 6;\n    case MfUltralightMirrorUidCounter:\n        return 21;\n    default:\n        return 0;\n    }\n}\n\nstatic uint8_t mf_ultralight_get_mirror_last_page(MfUltralightListener* instance) {\n    uint8_t strSize = mf_ultralight_get_mirror_data_size(instance->mirror.actual_mode);\n    return instance->config->mirror_page + 1U + strSize / 4;\n}\n\nstatic uint8_t mf_ultralight_get_ascii_offset(uint8_t start_page, MfUltralightListener* instance) {\n    uint8_t start_offset = 0;\n    if(instance->config->mirror.mirror_conf == MfUltralightMirrorCounter) start_offset = 15;\n\n    uint8_t ascii_offset = start_offset;\n\n    if(start_page > instance->config->mirror_page)\n        ascii_offset = (start_page - instance->config->mirror_page) * 4 -\n                       instance->config->mirror.mirror_byte + start_offset;\n\n    return ascii_offset;\n}\n\nstatic uint8_t mf_ultralight_get_ascii_end(MfUltralightMirrorConf mode) {\n    return (mode == MfUltralightMirrorUid) ? 14 : 21;\n}\n\nstatic uint8_t mf_ultralight_get_byte_offset(\n    uint8_t current_page,\n    const MfUltralightConfigPages* const config) {\n    return (current_page > config->mirror_page) ? 0 : config->mirror.mirror_byte;\n}\n\nstatic void mf_ultralight_format_mirror_data(\n    FuriString* str,\n    const uint8_t* const data,\n    const uint8_t data_len) {\n    for(uint8_t i = 0; i < data_len; i++)\n        furi_string_cat_printf(str, \"%02X\", data[i]);\n}\n\nvoid mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance) {\n    if(mf_ultralight_mirror_enabled(instance)) {\n        instance->mirror.ascii_offset = mf_ultralight_get_ascii_offset(start_page, instance);\n        instance->mirror.ascii_end = mf_ultralight_get_ascii_end(instance->mirror.actual_mode);\n\n        instance->mirror.mirror_last_page = mf_ultralight_get_mirror_last_page(instance);\n    }\n}\n\nvoid mf_ultralight_mirror_read_handler(\n    uint8_t mirror_page_num,\n    uint8_t* dest,\n    MfUltralightListener* instance) {\n    if(instance->mirror.enabled && mirror_page_num >= instance->config->mirror_page &&\n       mirror_page_num <= instance->mirror.mirror_last_page) {\n        uint8_t byte_offset = mf_ultralight_get_byte_offset(mirror_page_num, instance->config);\n\n        uint8_t ascii_offset = instance->mirror.ascii_offset;\n        uint8_t ascii_end = instance->mirror.ascii_end;\n        uint8_t* source = (uint8_t*)furi_string_get_cstr(instance->mirror.ascii_mirror_data);\n        for(uint8_t j = byte_offset; (j < 4) && (ascii_offset < ascii_end); j++) {\n            dest[j] = source[ascii_offset];\n            ascii_offset++;\n        }\n        instance->mirror.ascii_offset = ascii_offset;\n    }\n}\n\nvoid mf_ultralight_mirror_prepare_emulation(MfUltralightListener* instance) {\n    mf_ultralight_format_mirror_data(\n        instance->mirror.ascii_mirror_data,\n        instance->data->iso14443_3a_data->uid,\n        instance->data->iso14443_3a_data->uid_len);\n\n    furi_string_push_back(instance->mirror.ascii_mirror_data, 'x');\n\n    mf_ultraligt_mirror_format_counter(instance);\n}\n\nvoid mf_ultraligt_mirror_format_counter(MfUltralightListener* instance) {\n    furi_string_left(\n        instance->mirror.ascii_mirror_data, instance->data->iso14443_3a_data->uid_len * 2 + 1);\n\n    uint8_t* c = instance->data->counter[2].data;\n    furi_string_cat_printf(instance->mirror.ascii_mirror_data, \"%02X%02X%02X\", c[2], c[1], c[0]);\n}\n\nbool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance) {\n    return instance->composite_cmd.callback != NULL;\n}\n\nMfUltralightCommand\n    mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer) {\n    MfUltralightCommand command = (instance->composite_cmd.callback)(instance, buffer);\n    mf_ultralight_composite_command_reset(instance);\n    return command;\n}\n\nvoid mf_ultralight_composite_command_reset(MfUltralightListener* instance) {\n    instance->composite_cmd.callback = NULL;\n    instance->composite_cmd.data = 0;\n}\n\nvoid mf_ultralight_composite_command_set_next(\n    MfUltralightListener* instance,\n    const MfUltralightListenerCommandCallback handler) {\n    instance->composite_cmd.callback = handler;\n}\n\nvoid mf_ultralight_single_counter_try_increase(MfUltralightListener* instance) {\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportSingleCounter) &&\n       instance->config->access.nfc_cnt_en && !instance->single_counter_increased) {\n        if(instance->data->counter[2].counter < MF_ULTRALIGHT_MAX_CNTR_VAL) {\n            instance->data->counter[2].counter++;\n            mf_ultraligt_mirror_format_counter(instance);\n        }\n        instance->single_counter_increased = true;\n    }\n}\n\nvoid mf_ultralight_single_counter_try_to_unlock(\n    MfUltralightListener* instance,\n    Iso14443_3aListenerEventType type) {\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportSingleCounter) &&\n       type == Iso14443_3aListenerEventTypeFieldOff) {\n        instance->single_counter_increased = false;\n    }\n}\n\nstatic bool mf_ultralight_i2c_page_validator_for_sector0(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightType type) {\n    UNUSED(type);\n    bool valid = false;\n    if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) {\n        if(start_page <= 0xE9 && end_page <= 0xE9) {\n            valid = true;\n        } else if(\n            MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(start_page) &&\n            MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(end_page)) {\n            valid = true;\n        }\n    } else if(type == MfUltralightTypeNTAGI2C1K) {\n        if((start_page <= 0xE2) || MF_ULTRALIGHT_PAGE_IN_BOUNDS(start_page, 0x00E8, 0x00E9)) {\n            valid = true;\n        }\n    } else if(type == MfUltralightTypeNTAGI2C2K) {\n        valid = (start_page <= 0xFF && end_page <= 0xFF);\n    }\n\n    return valid;\n}\n\nstatic bool mf_ultralight_i2c_page_validator_for_sector1(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightType type) {\n    bool valid = false;\n    if(type == MfUltralightTypeNTAGI2CPlus2K) {\n        valid = (start_page <= 0xFF && end_page <= 0xFF);\n    } else if(type == MfUltralightTypeNTAGI2C2K) {\n        valid = (MF_ULTRALIGHT_PAGE_IN_BOUNDS(start_page, 0x00E8, 0x00E9) || (start_page <= 0xE0));\n    } else if(type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2CPlus1K) {\n        valid = false;\n    }\n\n    return valid;\n}\n\nstatic bool mf_ultralight_i2c_page_validator_for_sector2(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightType type) {\n    UNUSED(start_page);\n    UNUSED(end_page);\n    UNUSED(type);\n    return false;\n}\n\nstatic bool mf_ultralight_i2c_page_validator_for_sector3(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightType type) {\n    UNUSED(type);\n    UNUSED(end_page);\n    return MF_ULTRALIGHT_I2C_PAGE_ON_MIRRORED_SESSION_REG(start_page);\n}\n\ntypedef bool (\n    *MfUltralightI2CValidator)(uint16_t start_page, uint16_t end_page, MfUltralightType type);\n\ntypedef uint16_t (*MfUltralightI2CPageProvider)(uint16_t page, MfUltralightType type);\n\nconst MfUltralightI2CValidator validation_methods[] = {\n    mf_ultralight_i2c_page_validator_for_sector0,\n    mf_ultralight_i2c_page_validator_for_sector1,\n    mf_ultralight_i2c_page_validator_for_sector2,\n    mf_ultralight_i2c_page_validator_for_sector3,\n};\n\nbool mf_ultralight_i2c_validate_pages(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightListener* instance) {\n    bool valid = false;\n    if(instance->sector < COUNT_OF(validation_methods)) {\n        MfUltralightI2CValidator validate = validation_methods[instance->sector];\n        valid = validate(start_page, end_page, instance->data->type);\n    }\n    return valid;\n}\n\nbool mf_ultralight_is_i2c_tag(MfUltralightType type) {\n    return type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2C2K ||\n           type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K;\n}\n\nstatic uint16_t mf_ultralight_i2c_page_provider_for_sector0(uint16_t page, MfUltralightType type) {\n    uint8_t new_page = page;\n    if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) {\n        if(page == 0x00EC) {\n            new_page = 234;\n        } else if(page == 0x00ED) {\n            new_page = 235;\n        }\n    } else if(type == MfUltralightTypeNTAGI2C1K) {\n        if(page == 0x00E8) {\n            new_page = 232;\n        } else if(page == 0x00E9) {\n            new_page = 233;\n        }\n    } else if(type == MfUltralightTypeNTAGI2C2K) {\n        new_page = page;\n    }\n    return new_page;\n}\n\nstatic uint16_t mf_ultralight_i2c_page_provider_for_sector1(uint16_t page, MfUltralightType type) {\n    UNUSED(type);\n    uint16_t new_page = page;\n    if(type == MfUltralightTypeNTAGI2CPlus2K) new_page = page + 236;\n    if(type == MfUltralightTypeNTAGI2C2K) new_page = page + 256;\n    return new_page;\n}\n\nstatic uint16_t mf_ultralight_i2c_page_provider_for_sector2(uint16_t page, MfUltralightType type) {\n    UNUSED(type);\n    return page;\n}\n\nstatic uint16_t mf_ultralight_i2c_page_provider_for_sector3(uint16_t page, MfUltralightType type) {\n    uint16_t new_page = page;\n    if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) {\n        if(page == 0x00F8)\n            new_page = 234;\n        else if(page == 0x00F9)\n            new_page = 235;\n    } else if(type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2C2K) {\n        if(page == 0x00F8)\n            new_page = (type == MfUltralightTypeNTAGI2C1K) ? 227 : 481;\n        else if(page == 0x00F9)\n            new_page = (type == MfUltralightTypeNTAGI2C1K) ? 228 : 482;\n    }\n    return new_page;\n}\n\nconst MfUltralightI2CPageProvider provider_methods[] = {\n    mf_ultralight_i2c_page_provider_for_sector0,\n    mf_ultralight_i2c_page_provider_for_sector1,\n    mf_ultralight_i2c_page_provider_for_sector2,\n    mf_ultralight_i2c_page_provider_for_sector3,\n};\n\nuint16_t\n    mf_ultralight_i2c_provide_page_by_requested(uint16_t page, MfUltralightListener* instance) {\n    uint16_t result = page;\n    if(instance->sector < COUNT_OF(provider_methods)) {\n        MfUltralightI2CPageProvider provider = provider_methods[instance->sector];\n        result = provider(page, instance->data->type);\n    }\n    return result;\n}\n\nvoid mf_ultralight_static_lock_bytes_prepare(MfUltralightListener* instance) {\n    instance->static_lock = (uint16_t*)&instance->data->page[2].data[2];\n}\n\nvoid mf_ultralight_static_lock_bytes_write(\n    MfUltralightStaticLockData* const lock_bits,\n    uint16_t new_bits) {\n    uint16_t current_locks = *lock_bits;\n\n    if(MF_ULTRALIGHT_STATIC_BIT_OTP_CC_LOCKED(current_locks))\n        MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_OTP_CC_MASK);\n\n    if(MF_ULTRALIGHT_STATIC_BITS_9_4_LOCKED(current_locks))\n        MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_9_4_MASK);\n\n    if(MF_ULTRALIGHT_STATIC_BITS_15_10_LOCKED(current_locks))\n        MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_15_10_MASK);\n\n    MF_ULTRALIGHT_BITS_SET(current_locks, new_bits);\n    *lock_bits = current_locks;\n}\n\nbool mf_ultralight_static_lock_check_page(\n    const MfUltralightStaticLockData* const lock_bits,\n    uint16_t page) {\n    bool locked = false;\n    if(MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x0003, 0x000F)) {\n        uint16_t current_locks = *lock_bits;\n        locked = MF_ULTRALIGHT_PAGE_LOCKED(current_locks, page);\n    }\n    return locked;\n}\n\nvoid mf_ultralight_capability_container_write(\n    MfUltralightPage* const current_page,\n    const uint8_t* const new_data) {\n    for(uint8_t i = 0; i < MF_ULTRALIGHT_PAGE_SIZE; i++) {\n        current_page->data[i] |= new_data[i];\n    }\n}\n\nstatic uint16_t mf_ultralight_dynamic_lock_page_num(const MfUltralightData* data) {\n    uint16_t lock_page;\n    if(data->type == MfUltralightTypeNTAGI2C1K)\n        lock_page = 226;\n    else if(data->type == MfUltralightTypeNTAGI2C2K)\n        lock_page = 480;\n    else\n        lock_page = mf_ultralight_get_config_page_num(data->type) - 1;\n    return lock_page;\n}\n\nvoid mf_ultralight_dynamic_lock_bytes_prepare(MfUltralightListener* instance) {\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock)) {\n        uint16_t lock_page = mf_ultralight_dynamic_lock_page_num(instance->data);\n        instance->dynamic_lock = (uint32_t*)instance->data->page[lock_page].data;\n    } else {\n        instance->dynamic_lock = NULL;\n    }\n}\n\nbool mf_ultralight_is_page_dynamic_lock(const MfUltralightListener* instance, uint16_t page) {\n    bool is_lock = false;\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock)) {\n        uint16_t linear_page = page + instance->sector * 256;\n\n        uint16_t lock_page = mf_ultralight_dynamic_lock_page_num(instance->data);\n        is_lock = linear_page == lock_page;\n    }\n    return is_lock;\n}\n\nvoid mf_ultralight_dynamic_lock_bytes_write(\n    MfUltralightDynamicLockData* const lock_bits,\n    uint32_t new_bits) {\n    furi_assert(lock_bits != NULL);\n    new_bits &= 0x00FFFFFF;\n    uint32_t current_lock = *lock_bits;\n    for(uint8_t i = 0; i < 8; i++) {\n        uint8_t bl_bit = i + 16;\n\n        if(MF_ULTRALIGHT_BIT_ACTIVE(current_lock, bl_bit)) {\n            uint8_t lock_bit = i * 2;\n            uint32_t mask = (1U << lock_bit) | (1U << (lock_bit + 1));\n            MF_ULTRALIGHT_BITS_CLR(new_bits, mask);\n        }\n    }\n    MF_ULTRALIGHT_BITS_SET(current_lock, new_bits);\n    *lock_bits = current_lock;\n}\n\nstatic uint8_t mf_ultralight_dynamic_lock_granularity(MfUltralightType type) {\n    switch(type) {\n    case MfUltralightTypeUL21:\n    case MfUltralightTypeNTAG213:\n        return 2;\n    case MfUltralightTypeNTAG215:\n    case MfUltralightTypeNTAG216:\n    case MfUltralightTypeNTAGI2C1K:\n    case MfUltralightTypeNTAGI2CPlus1K:\n        return 16;\n    case MfUltralightTypeNTAGI2C2K:\n    case MfUltralightTypeNTAGI2CPlus2K:\n        return 32;\n    default:\n        return 1;\n    }\n}\n\nstatic uint16_t mf_ultralight_get_upper_page_bound(MfUltralightType type) {\n    uint16_t upper_page_bound;\n\n    if(type == MfUltralightTypeNTAGI2CPlus2K)\n        upper_page_bound = 511;\n    else if(type == MfUltralightTypeNTAGI2C2K)\n        upper_page_bound = 479;\n    else if(type == MfUltralightTypeNTAGI2C1K)\n        upper_page_bound = 225;\n    else {\n        upper_page_bound = mf_ultralight_get_config_page_num(type) - 2;\n    }\n\n    return upper_page_bound;\n}\n\nbool mf_ultralight_dynamic_lock_check_page(const MfUltralightListener* instance, uint16_t page) {\n    UNUSED(page);\n    bool locked = false;\n    uint16_t upper_page_bound = mf_ultralight_get_upper_page_bound(instance->data->type);\n    uint16_t linear_page = page + instance->sector * 256;\n\n    if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock) &&\n       MF_ULTRALIGHT_PAGE_IN_BOUNDS(linear_page, 0x0010, upper_page_bound)) {\n        uint8_t granularity = mf_ultralight_dynamic_lock_granularity(instance->data->type);\n        uint8_t bit = (linear_page - 16) / granularity;\n        uint16_t current_locks = *instance->dynamic_lock;\n        locked = MF_ULTRALIGHT_PAGE_LOCKED(current_locks, bit);\n    }\n    return locked;\n}\n\nstatic bool mf_ultralight_auth_check_attempts(const MfUltralightListener* instance) {\n    uint8_t authlim = ((instance->data->type == MfUltralightTypeNTAGI2CPlus1K) ||\n                       (instance->data->type == MfUltralightTypeNTAGI2CPlus2K)) ?\n                          (1U << instance->config->access.authlim) :\n                          instance->config->access.authlim;\n\n    return instance->data->auth_attempts >= authlim;\n}\n\nbool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, bool auth_success) {\n    bool card_locked = false;\n\n    do {\n        if(instance->config->access.authlim == 0) break;\n        card_locked = mf_ultralight_auth_check_attempts(instance);\n        if(card_locked) break;\n\n        if(auth_success) {\n            MF_ULTRALIGHT_AUTH_RESET_ATTEMPTS(instance);\n        } else {\n            MF_ULTRALIGHT_AUTH_INCREASE_ATTEMPTS(instance);\n        }\n\n        card_locked = mf_ultralight_auth_check_attempts(instance);\n    } while(false);\n\n    return card_locked;\n}\n\nbool mf_ultralight_auth_check_password(\n    const MfUltralightAuthPassword* config_pass,\n    const MfUltralightAuthPassword* auth_pass) {\n    return memcmp(config_pass->data, auth_pass->data, sizeof(MfUltralightAuthPassword)) == 0;\n}\n\nbool mf_ultralight_common_check_access(\n    const MfUltralightListener* instance,\n    const uint16_t start_page,\n    const MfUltralightListenerAccessType access_type) {\n    bool access_success = false;\n    bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite);\n\n    do {\n        if(instance->auth_state != MfUltralightListenerAuthStateSuccess) {\n            if((instance->config->auth0 <= start_page) &&\n               (instance->config->access.prot || is_write_op)) {\n                break;\n            }\n        }\n\n        if(instance->config->access.cfglck && is_write_op) {\n            uint16_t config_page_start = instance->data->pages_total - 4;\n            if((start_page == config_page_start) || (start_page == config_page_start + 1)) {\n                break;\n            }\n        }\n\n        access_success = true;\n    } while(false);\n\n    return access_success;\n}\n\nbool mf_ultralight_c_check_access(\n    const MfUltralightData* data,\n    const uint16_t start_page,\n    const MfUltralightListenerAccessType access_type,\n    const MfUltralightListenerAuthState auth_state) {\n    bool access_success = false;\n    bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite);\n\n    do {\n        if(start_page >= 44) break;\n\n        const uint8_t auth0 = data->page[42].data[0];\n        const uint8_t auth1 = data->page[43].data[0] & 0x01;\n\n        if(auth0 < 0x03 || auth0 >= 0x30 || auth_state == MfUltralightListenerAuthStateSuccess) {\n            access_success = true;\n            break;\n        }\n\n        if((auth0 <= start_page) && ((auth1 == 0) || (auth1 == 1 || is_write_op))) { //-V560\n            break;\n        }\n\n        access_success = true;\n    } while(false);\n\n    return access_success;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h",
    "content": "#pragma once\n\n#include \"mf_ultralight_listener.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>\n#include <nfc/protocols/nfc_generic_event.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    MfUltralightListenerAuthStateIdle,\n    MfUltralightListenerAuthStateSuccess,\n} MfUltralightListenerAuthState;\n\ntypedef enum {\n    MfUltralightListenerAccessTypeRead,\n    MfUltralightListenerAccessTypeWrite,\n} MfUltralightListenerAccessType;\n\ntypedef enum {\n    MfUltralightCommandNotFound,\n    MfUltralightCommandProcessed,\n    MfUltralightCommandProcessedACK,\n    MfUltralightCommandProcessedSilent,\n    MfUltralightCommandNotProcessedNAK,\n    MfUltralightCommandNotProcessedSilent,\n    MfUltralightCommandNotProcessedAuthNAK,\n} MfUltralightCommand;\n\ntypedef MfUltralightCommand (\n    *MfUltralightListenerCommandCallback)(MfUltralightListener* instance, BitBuffer* buf);\n\ntypedef uint8_t MfUltralightListenerCompositeCommandData;\n\ntypedef struct {\n    MfUltralightListenerCompositeCommandData data;\n    MfUltralightListenerCommandCallback callback;\n} MfUltralightListenerCompositeCommandContext;\n\ntypedef struct {\n    uint8_t enabled;\n    uint8_t ascii_offset;\n    uint8_t ascii_end;\n    uint8_t mirror_last_page;\n    MfUltralightMirrorConf actual_mode;\n    FuriString* ascii_mirror_data;\n} MfUltralightMirrorMode;\n\ntypedef uint16_t MfUltralightStaticLockData;\ntypedef uint32_t MfUltralightDynamicLockData;\n\nstruct MfUltralightListener {\n    Iso14443_3aListener* iso14443_3a_listener;\n    MfUltralightListenerAuthState auth_state;\n    MfUltralightData* data;\n    BitBuffer* tx_buffer;\n    MfUltralightFeatureSupport features;\n    MfUltralightConfigPages* config;\n    MfUltralightStaticLockData* static_lock;\n    MfUltralightDynamicLockData* dynamic_lock;\n\n    NfcGenericEvent generic_event;\n    MfUltralightListenerEvent mfu_event;\n    MfUltralightListenerEventData mfu_event_data;\n    NfcGenericCallback callback;\n    uint8_t sector;\n    bool single_counter_increased;\n    MfUltralightMirrorMode mirror;\n    MfUltralightListenerCompositeCommandContext composite_cmd;\n    mbedtls_des3_context des_context;\n    uint8_t rndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];\n    uint8_t encB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];\n    void* context;\n};\n\nvoid mf_ultralight_single_counter_try_increase(MfUltralightListener* instance);\nvoid mf_ultralight_single_counter_try_to_unlock(\n    MfUltralightListener* instance,\n    Iso14443_3aListenerEventType type);\n\nvoid mf_ultralight_mirror_prepare_emulation(MfUltralightListener* instance);\nvoid mf_ultraligt_mirror_format_counter(MfUltralightListener* instance);\nvoid mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance);\nvoid mf_ultralight_mirror_read_handler(\n    uint8_t mirror_page_num,\n    uint8_t* dest,\n    MfUltralightListener* instance);\n\nvoid mf_ultralight_composite_command_set_next(\n    MfUltralightListener* instance,\n    const MfUltralightListenerCommandCallback handler);\nvoid mf_ultralight_composite_command_reset(MfUltralightListener* instance);\nbool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance);\nMfUltralightCommand\n    mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer);\n\nbool mf_ultralight_is_i2c_tag(MfUltralightType type);\nbool mf_ultralight_i2c_validate_pages(\n    uint16_t start_page,\n    uint16_t end_page,\n    MfUltralightListener* instance);\n\nuint16_t\n    mf_ultralight_i2c_provide_page_by_requested(uint16_t page, MfUltralightListener* instance);\n\nvoid mf_ultralight_static_lock_bytes_prepare(MfUltralightListener* instance);\nvoid mf_ultralight_static_lock_bytes_write(\n    MfUltralightStaticLockData* const lock_bits,\n    uint16_t new_bits);\nbool mf_ultralight_static_lock_check_page(\n    const MfUltralightStaticLockData* const lock_bits,\n    uint16_t page);\n\nvoid mf_ultralight_capability_container_write(\n    MfUltralightPage* const current_page,\n    const uint8_t* const new_data);\n\nvoid mf_ultralight_dynamic_lock_bytes_prepare(MfUltralightListener* instance);\nbool mf_ultralight_is_page_dynamic_lock(const MfUltralightListener* instance, uint16_t start_page);\nvoid mf_ultralight_dynamic_lock_bytes_write(\n    MfUltralightDynamicLockData* const lock_bits,\n    uint32_t new_bits);\nbool mf_ultralight_dynamic_lock_check_page(const MfUltralightListener* instance, uint16_t page);\nbool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, bool auth_success);\nbool mf_ultralight_auth_check_password(\n    const MfUltralightAuthPassword* config_pass,\n    const MfUltralightAuthPassword* auth_pass);\n\nbool mf_ultralight_common_check_access(\n    const MfUltralightListener* instance,\n    const uint16_t start_page,\n    const MfUltralightListenerAccessType access_type);\n\nbool mf_ultralight_c_check_access(\n    const MfUltralightData* data,\n    const uint16_t start_page,\n    const MfUltralightListenerAccessType access_type,\n    const MfUltralightListenerAuthState auth_state);\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c",
    "content": "#include \"mf_ultralight_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#define TAG \"MfUltralightPoller\"\n\ntypedef NfcCommand (*MfUltralightPollerReadHandler)(MfUltralightPoller* instance);\n\nstatic bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k(\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left) {\n    bool tag_calculated = false;\n    // 0 - 226: sector 0\n    // 227 - 228: config registers\n    // 229 - 230: session registers\n\n    if(lin_addr > 230) {\n        *pages_left = 0;\n    } else if(lin_addr >= 229) {\n        *sector = 3;\n        *pages_left = 2 - (lin_addr - 229);\n        *tag = lin_addr - 229 + 248;\n        tag_calculated = true;\n    } else if(lin_addr >= 227) {\n        *sector = 0;\n        *pages_left = 2 - (lin_addr - 227);\n        *tag = lin_addr - 227 + 232;\n        tag_calculated = true;\n    } else {\n        *sector = 0;\n        *pages_left = 227 - lin_addr;\n        *tag = lin_addr;\n        tag_calculated = true;\n    }\n\n    return tag_calculated;\n}\n\nstatic bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k(\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left) {\n    bool tag_calculated = false;\n    // 0 - 255: sector 0\n    // 256 - 480: sector 1\n    // 481 - 482: config registers\n    // 483 - 484: session registers\n\n    if(lin_addr > 484) {\n        *pages_left = 0;\n    } else if(lin_addr >= 483) {\n        *sector = 3;\n        *pages_left = 2 - (lin_addr - 483);\n        *tag = lin_addr - 483 + 248;\n        tag_calculated = true;\n    } else if(lin_addr >= 481) {\n        *sector = 1;\n        *pages_left = 2 - (lin_addr - 481);\n        *tag = lin_addr - 481 + 232;\n        tag_calculated = true;\n    } else if(lin_addr >= 256) {\n        *sector = 1;\n        *pages_left = 225 - (lin_addr - 256);\n        *tag = lin_addr - 256;\n        tag_calculated = true;\n    } else {\n        *sector = 0;\n        *pages_left = 256 - lin_addr;\n        *tag = lin_addr;\n        tag_calculated = true;\n    }\n\n    return tag_calculated;\n}\n\nstatic bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k(\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left) {\n    bool tag_calculated = false;\n    // 0 - 233: sector 0 + registers\n    // 234 - 235: session registers\n\n    if(lin_addr > 235) {\n        *pages_left = 0;\n    } else if(lin_addr >= 234) {\n        *sector = 0;\n        *pages_left = 2 - (lin_addr - 234);\n        *tag = lin_addr - 234 + 236;\n        tag_calculated = true;\n    } else {\n        *sector = 0;\n        *pages_left = 234 - lin_addr;\n        *tag = lin_addr;\n        tag_calculated = true;\n    }\n\n    return tag_calculated;\n}\n\nstatic bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k(\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left) {\n    bool tag_calculated = false;\n    // 0 - 233: sector 0 + registers\n    // 234 - 235: session registers\n    // 236 - 491: sector 1\n\n    if(lin_addr > 491) {\n        *pages_left = 0;\n    } else if(lin_addr >= 236) {\n        *sector = 1;\n        *pages_left = 256 - (lin_addr - 236);\n        *tag = lin_addr - 236;\n        tag_calculated = true;\n    } else if(lin_addr >= 234) {\n        *sector = 0;\n        *pages_left = 2 - (lin_addr - 234);\n        *tag = lin_addr - 234 + 236;\n        tag_calculated = true;\n    } else {\n        *sector = 0;\n        *pages_left = 234 - lin_addr;\n        *tag = lin_addr;\n        tag_calculated = true;\n    }\n\n    return tag_calculated;\n}\n\nbool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(\n    MfUltralightPoller* instance,\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left) {\n    furi_assert(instance);\n    furi_assert(sector);\n    furi_assert(tag);\n    furi_assert(pages_left);\n\n    bool tag_calculated = false;\n\n    if(instance->data->type == MfUltralightTypeNTAGI2C1K) {\n        tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k(\n            lin_addr, sector, tag, pages_left);\n    } else if(instance->data->type == MfUltralightTypeNTAGI2C2K) {\n        tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k(\n            lin_addr, sector, tag, pages_left);\n    } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus1K) {\n        tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k(\n            lin_addr, sector, tag, pages_left);\n    } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus2K) {\n        tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k(\n            lin_addr, sector, tag, pages_left);\n    }\n\n    return tag_calculated;\n}\n\nMfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {\n    furi_assert(iso14443_3a_poller);\n\n    MfUltralightPoller* instance = malloc(sizeof(MfUltralightPoller));\n    instance->iso14443_3a_poller = iso14443_3a_poller;\n    instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE);\n    instance->data = mf_ultralight_alloc();\n\n    instance->mfu_event.data = &instance->mfu_event_data;\n\n    instance->general_event.protocol = NfcProtocolMfUltralight;\n    instance->general_event.event_data = &instance->mfu_event;\n    instance->general_event.instance = instance;\n    mbedtls_des3_init(&instance->des_context);\n    return instance;\n}\n\nvoid mf_ultralight_poller_free(MfUltralightPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    mf_ultralight_free(instance->data);\n    mbedtls_des3_free(&instance->des_context);\n    free(instance);\n}\n\nstatic void mf_ultralight_poller_set_callback(\n    MfUltralightPoller* instance,\n    NfcGenericCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nconst MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance) {\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n    iso14443_3a_copy(\n        instance->data->iso14443_3a_data,\n        iso14443_3a_poller_get_data(instance->iso14443_3a_poller));\n    instance->counters_read = 0;\n    instance->counters_total = 3;\n    instance->tearing_flag_read = 0;\n    instance->tearing_flag_total = 3;\n    instance->pages_read = 0;\n    instance->state = MfUltralightPollerStateRequestMode;\n    instance->current_page = 0;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_request_mode(MfUltralightPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    instance->mfu_event.type = MfUltralightPollerEventTypeRequestMode;\n    instance->mfu_event.data->poller_mode = MfUltralightPollerModeRead;\n\n    command = instance->callback(instance->general_event, instance->context);\n    instance->mode = instance->mfu_event.data->poller_mode;\n\n    instance->state = MfUltralightPollerStateReadVersion;\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) {\n    instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version);\n    if(instance->error == MfUltralightErrorNone) {\n        FURI_LOG_D(TAG, \"Read version success\");\n        instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version);\n        instance->state = MfUltralightPollerStateGetFeatureSet;\n    } else {\n        FURI_LOG_D(TAG, \"Didn't respond. Check Ultralight C\");\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n        instance->state = MfUltralightPollerStateDetectMfulC;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) {\n    instance->error = mf_ultralight_poller_authentication_test(instance);\n    if(instance->error == MfUltralightErrorNone) {\n        FURI_LOG_D(TAG, \"Ultralight C detected\");\n        instance->data->type = MfUltralightTypeMfulC;\n        instance->state = MfUltralightPollerStateGetFeatureSet;\n    } else {\n        FURI_LOG_D(TAG, \"Didn't respond. Check NTAG 203\");\n        instance->state = MfUltralightPollerStateDetectNtag203;\n    }\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) {\n    MfUltralightPollerState next_state = MfUltralightPollerStateGetFeatureSet;\n    MfUltralightPageReadCommandData data = {};\n    instance->error = mf_ultralight_poller_read_page(instance, 41, &data);\n    if(instance->error == MfUltralightErrorNone) {\n        FURI_LOG_D(TAG, \"NTAG203 detected\");\n        instance->data->type = MfUltralightTypeNTAG203;\n    } else {\n        FURI_LOG_D(TAG, \"Original Ultralight detected\");\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n        instance->data->type = MfUltralightTypeOrigin;\n    }\n    instance->state = next_state;\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_get_feature_set(MfUltralightPoller* instance) {\n    instance->feature_set = mf_ultralight_get_feature_support_set(instance->data->type);\n    instance->pages_total = mf_ultralight_get_pages_total(instance->data->type);\n    instance->data->pages_total = instance->pages_total;\n    FURI_LOG_D(\n        TAG,\n        \"%s detected. Total pages: %d\",\n        mf_ultralight_get_device_name(instance->data, NfcDeviceNameTypeFull),\n        instance->pages_total);\n\n    instance->state = MfUltralightPollerStateReadSignature;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller* instance) {\n    MfUltralightPollerState next_state = MfUltralightPollerStateAuth;\n    if(mf_ultralight_support_feature(\n           instance->feature_set, MfUltralightFeatureSupportReadSignature)) {\n        FURI_LOG_D(TAG, \"Reading signature\");\n        instance->error =\n            mf_ultralight_poller_read_signature(instance, &instance->data->signature);\n        if(instance->error != MfUltralightErrorNone) {\n            FURI_LOG_D(TAG, \"Read signature failed\");\n            next_state = MfUltralightPollerStateReadFailed;\n        }\n    } else {\n        FURI_LOG_D(TAG, \"Skip reading signature\");\n        if(mf_ultralight_support_feature(\n               instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {\n            next_state = MfUltralightPollerStateAuthMfulC;\n        }\n    }\n    instance->state = next_state;\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* instance) {\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->feature_set, MfUltralightFeatureSupportReadCounter) ||\n           !mf_ultralight_is_counter_configured(instance->data)) {\n            FURI_LOG_D(TAG, \"Skip reading counters\");\n            instance->state = MfUltralightPollerStateReadTearingFlags;\n            break;\n        }\n\n        MfUltralightConfigPages* config = NULL;\n        mf_ultralight_get_config_page(instance->data, &config);\n\n        if(config->access.nfc_cnt_pwd_prot && !instance->auth_context.auth_success) {\n            FURI_LOG_D(TAG, \"Counter reading is protected with password\");\n            instance->state = MfUltralightPollerStateReadTearingFlags;\n            break;\n        }\n\n        if(instance->counters_read == instance->counters_total) {\n            instance->state = MfUltralightPollerStateReadTearingFlags;\n            break;\n        }\n\n        if(mf_ultralight_support_feature(\n               instance->feature_set, MfUltralightFeatureSupportSingleCounter)) {\n            instance->counters_read = 2;\n        }\n\n        FURI_LOG_D(TAG, \"Reading counter %d\", instance->counters_read);\n        instance->error = mf_ultralight_poller_read_counter(\n            instance, instance->counters_read, &instance->data->counter[instance->counters_read]);\n        if(instance->error != MfUltralightErrorNone) {\n            FURI_LOG_D(TAG, \"Failed to read %d counter\", instance->counters_read);\n            instance->state = MfUltralightPollerStateReadTearingFlags;\n        } else {\n            instance->counters_read++;\n        }\n\n    } while(false);\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    if(mf_ultralight_support_feature(\n           instance->feature_set,\n           MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) {\n        if(instance->tearing_flag_read == instance->tearing_flag_total) {\n            instance->state = MfUltralightPollerStateTryDefaultPass;\n            command = NfcCommandReset;\n        } else {\n            bool single_counter = mf_ultralight_support_feature(\n                instance->feature_set, MfUltralightFeatureSupportSingleCounter);\n            if(single_counter) instance->tearing_flag_read = 2;\n\n            FURI_LOG_D(TAG, \"Reading tearing flag %d\", instance->tearing_flag_read);\n            instance->error = mf_ultralight_poller_read_tearing_flag(\n                instance,\n                instance->tearing_flag_read,\n                &instance->data->tearing_flag[instance->tearing_flag_read]);\n            if((instance->error == MfUltralightErrorProtocol) && single_counter) {\n                instance->tearing_flag_read++;\n            } else if(instance->error != MfUltralightErrorNone) {\n                FURI_LOG_D(TAG, \"Reading tearing flag %d failed\", instance->tearing_flag_read);\n                instance->state = MfUltralightPollerStateTryDefaultPass;\n                command = NfcCommandReset;\n            } else {\n                instance->tearing_flag_read++;\n            }\n        }\n    } else {\n        FURI_LOG_D(TAG, \"Skip reading tearing flags\");\n        instance->state = MfUltralightPollerStateTryDefaultPass;\n        command = NfcCommandReset;\n    }\n\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    if(mf_ultralight_support_feature(\n           instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) {\n        instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;\n\n        command = instance->callback(instance->general_event, instance->context);\n        if(!instance->mfu_event.data->auth_context.skip_auth) {\n            instance->auth_context.password = instance->mfu_event.data->auth_context.password;\n            uint32_t pass = bit_lib_bytes_to_num_be(\n                instance->auth_context.password.data, sizeof(MfUltralightAuthPassword));\n            FURI_LOG_D(TAG, \"Trying to authenticate with password %08lX\", pass);\n            instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context);\n            if(instance->error == MfUltralightErrorNone) {\n                FURI_LOG_D(TAG, \"Auth success\");\n                instance->auth_context.auth_success = true;\n                instance->mfu_event.data->auth_context = instance->auth_context;\n                instance->mfu_event.type = MfUltralightPollerEventTypeAuthSuccess;\n                command = instance->callback(instance->general_event, instance->context);\n            } else {\n                FURI_LOG_D(TAG, \"Auth failed\");\n                instance->auth_context.auth_success = false;\n                instance->mfu_event.type = MfUltralightPollerEventTypeAuthFailed;\n                command = instance->callback(instance->general_event, instance->context);\n                iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n            }\n        }\n    }\n    instance->state = MfUltralightPollerStateReadPages;\n\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    FURI_LOG_D(TAG, \"MfulC auth\");\n    if(mf_ultralight_support_feature(\n           instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {\n        instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;\n\n        command = instance->callback(instance->general_event, instance->context);\n        if(!instance->mfu_event.data->auth_context.skip_auth) {\n            FURI_LOG_D(TAG, \"Trying to authenticate with 3des key\");\n            // Only use the key if it was actually provided\n            if(instance->mfu_event.data->key_request_data.key_provided) {\n                instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key;\n            } else if(instance->mode == MfUltralightPollerModeDictAttack) {\n                // TODO:  -nofl Can logic be rearranged to request this key\n                // before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller?\n                FURI_LOG_D(TAG, \"No initial key provided, requesting key from dictionary\");\n                // Trigger dictionary key request\n                instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;\n                command = instance->callback(instance->general_event, instance->context);\n                if(!instance->mfu_event.data->key_request_data.key_provided) {\n                    instance->state = MfUltralightPollerStateReadPages;\n                    return command;\n                } else {\n                    instance->auth_context.tdes_key =\n                        instance->mfu_event.data->key_request_data.key;\n                }\n            } else {\n                // Fallback: use key from auth context (for sync poller compatibility)\n                instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;\n            }\n            instance->auth_context.auth_success = false;\n            // For debugging\n            FURI_LOG_D(\n                \"TAG\",\n                \"Key data: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\",\n                instance->auth_context.tdes_key.data[0],\n                instance->auth_context.tdes_key.data[1],\n                instance->auth_context.tdes_key.data[2],\n                instance->auth_context.tdes_key.data[3],\n                instance->auth_context.tdes_key.data[4],\n                instance->auth_context.tdes_key.data[5],\n                instance->auth_context.tdes_key.data[6],\n                instance->auth_context.tdes_key.data[7],\n                instance->auth_context.tdes_key.data[8],\n                instance->auth_context.tdes_key.data[9],\n                instance->auth_context.tdes_key.data[10],\n                instance->auth_context.tdes_key.data[11],\n                instance->auth_context.tdes_key.data[12],\n                instance->auth_context.tdes_key.data[13],\n                instance->auth_context.tdes_key.data[14],\n                instance->auth_context.tdes_key.data[15]);\n            do {\n                uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];\n                uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};\n                furi_hal_random_fill_buf(RndA, sizeof(RndA));\n                instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output);\n                if(instance->error != MfUltralightErrorNone) break;\n\n                uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};\n                const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;\n                instance->error = mf_ultralight_poller_authenticate_end(\n                    instance, RndB, output, decoded_shifted_RndA);\n                if(instance->error != MfUltralightErrorNone) break;\n\n                mf_ultralight_3des_shift_data(RndA);\n                instance->auth_context.auth_success =\n                    (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);\n                if(instance->auth_context.auth_success) {\n                    FURI_LOG_E(TAG, \"Auth success\");\n                    if(instance->mode == MfUltralightPollerModeDictAttack) {\n                        memcpy(\n                            &instance->data->page[44],\n                            instance->auth_context.tdes_key.data,\n                            MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);\n                        // Continue to read pages after successful authentication\n                        instance->state = MfUltralightPollerStateReadPages;\n                    }\n                }\n            } while(false);\n            if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {\n                FURI_LOG_E(TAG, \"Auth failed\");\n                iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n                if(instance->mode == MfUltralightPollerModeDictAttack) {\n                    // Not needed? We already do a callback earlier?\n                    instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;\n                    command = instance->callback(instance->general_event, instance->context);\n                    if(!instance->mfu_event.data->key_request_data.key_provided) {\n                        instance->state = MfUltralightPollerStateReadPages;\n                    } else {\n                        instance->auth_context.tdes_key =\n                            instance->mfu_event.data->key_request_data.key;\n                        instance->state = MfUltralightPollerStateAuthMfulC;\n                    }\n                }\n            }\n        }\n    }\n    // Regression review\n    if(instance->mode != MfUltralightPollerModeDictAttack) {\n        instance->state = MfUltralightPollerStateReadPages;\n    }\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) {\n    MfUltralightPageReadCommandData data = {};\n    uint16_t start_page = instance->pages_read;\n    if(MF_ULTRALIGHT_IS_NTAG_I2C(instance->data->type)) {\n        uint8_t tag = 0;\n        uint8_t sector = 0;\n        uint8_t pages_left = 0;\n        if(mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(\n               instance, start_page, &sector, &tag, &pages_left)) {\n            instance->error =\n                mf_ultralight_poller_read_page_from_sector(instance, sector, tag, &data);\n        } else {\n            FURI_LOG_D(TAG, \"Failed to calculate sector and tag from %d page\", start_page);\n            instance->error = MfUltralightErrorProtocol;\n        }\n    } else {\n        instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);\n    }\n\n    if(instance->error == MfUltralightErrorNone) {\n        if(start_page < instance->pages_total) {\n            FURI_LOG_D(TAG, \"Read page %d success\", start_page);\n            instance->data->page[start_page] = data.page[0];\n            instance->pages_read++;\n            instance->data->pages_read = instance->pages_read;\n        }\n\n        if(instance->pages_read == instance->pages_total) {\n            instance->state = MfUltralightPollerStateReadCounters;\n        }\n    } else {\n        if(instance->data->type == MfUltralightTypeMfulC &&\n           !mf_ultralight_3des_key_valid(instance->data)) {\n            instance->state = MfUltralightPollerStateCheckMfulCAuthStatus;\n        } else {\n            FURI_LOG_D(TAG, \"Read page %d failed\", instance->pages_read);\n            if(instance->pages_read) {\n                instance->state = MfUltralightPollerStateReadCounters;\n            } else {\n                instance->state = MfUltralightPollerStateReadFailed;\n            }\n        }\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoller* instance) {\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->feature_set, MfUltralightFeatureSupportPasswordAuth))\n            break;\n\n        MfUltralightConfigPages* config = NULL;\n        mf_ultralight_get_config_page(instance->data, &config);\n        if(instance->auth_context.auth_success) {\n            config->password = instance->auth_context.password;\n            config->pack = instance->auth_context.pack;\n        } else if(config->access.authlim == 0) {\n            FURI_LOG_D(TAG, \"No limits in authentication. Trying default password\");\n            bit_lib_num_to_bytes_be(\n                MF_ULTRALIGHT_DEFAULT_PASSWORD,\n                sizeof(MfUltralightAuthPassword),\n                instance->auth_context.password.data);\n            instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context);\n            if(instance->error == MfUltralightErrorNone) {\n                FURI_LOG_D(TAG, \"Default password detected\");\n                bit_lib_num_to_bytes_be(\n                    MF_ULTRALIGHT_DEFAULT_PASSWORD,\n                    sizeof(MfUltralightAuthPassword),\n                    config->password.data);\n                config->pack = instance->auth_context.pack;\n                instance->auth_context.auth_success = true;\n            }\n        }\n\n        if(instance->pages_read != instance->pages_total) {\n            // Probably password protected, fix AUTH0 and PROT so before AUTH0\n            // can be written and since AUTH0 won't be readable, like on the\n            // original card\n            config->auth0 = instance->pages_read;\n            config->access.prot = true;\n        } else if(!instance->auth_context.auth_success) {\n            instance->pages_read -= 2;\n            instance->data->pages_read -= 2;\n        }\n    } while(false);\n\n    instance->state = MfUltralightPollerStateReadSuccess;\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand\n    mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {\n    instance->state = MfUltralightPollerStateReadSuccess;\n\n    do {\n        if(!mf_ultralight_support_feature(\n               instance->feature_set, MfUltralightFeatureSupportAuthenticate))\n            break;\n\n        if(!instance->auth_context.auth_success) {\n            FURI_LOG_D(TAG, \"Skip 3des key populating\");\n            break;\n        }\n\n        memcpy(\n            &instance->data->page[44],\n            instance->auth_context.tdes_key.data,\n            MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);\n        instance->data->pages_read = instance->pages_total;\n        instance->pages_read = instance->pages_total;\n    } while(false);\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) {\n    FURI_LOG_D(TAG, \"Read Failed\");\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed;\n    instance->mfu_event.data->error = instance->error;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->state = MfUltralightPollerStateIdle;\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) {\n    FURI_LOG_D(TAG, \"Read success\");\n    instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n\n    if(instance->mode == MfUltralightPollerModeRead) {\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n        instance->state = MfUltralightPollerStateIdle;\n    } else {\n        instance->state = MfUltralightPollerStateRequestWriteData;\n    }\n\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPoller* instance) {\n    FURI_LOG_D(TAG, \"Check writing capability\");\n    NfcCommand command = NfcCommandContinue;\n    MfUltralightPollerState next_state = MfUltralightPollerStateWritePages;\n    instance->current_page = 4;\n\n    instance->mfu_event.type = MfUltralightPollerEventTypeRequestWriteData;\n    instance->callback(instance->general_event, instance->context);\n\n    const MfUltralightData* write_data = instance->mfu_event.data->write_data;\n    const MfUltralightData* tag_data = instance->data;\n    uint32_t features = mf_ultralight_get_feature_support_set(tag_data->type);\n\n    bool check_passed = false;\n    do {\n        if(write_data->type != tag_data->type) {\n            FURI_LOG_D(TAG, \"Incorrect tag type\");\n            instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch;\n            break;\n        }\n\n        if(mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth)) {\n            if(!instance->auth_context.auth_success) {\n                FURI_LOG_D(TAG, \"Unknown password\");\n                instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;\n                break;\n            }\n        }\n\n        const MfUltralightPage staticlock_page = tag_data->page[2];\n        if(staticlock_page.data[2] != 0 || staticlock_page.data[3] != 0) {\n            FURI_LOG_D(TAG, \"Static lock bits are set\");\n            instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;\n            break;\n        }\n\n        if(mf_ultralight_support_feature(features, MfUltralightFeatureSupportDynamicLock)) {\n            uint8_t dynlock_num = mf_ultralight_get_config_page_num(tag_data->type) - 1;\n            const MfUltralightPage dynlock_page = tag_data->page[dynlock_num];\n            if(dynlock_page.data[0] != 0 || dynlock_page.data[1] != 0) {\n                FURI_LOG_D(TAG, \"Dynamic lock bits are set\");\n                instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;\n                break;\n            }\n        }\n\n        check_passed = true;\n    } while(false);\n\n    if(!check_passed) {\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n        command = instance->callback(instance->general_event, instance->context);\n        next_state = MfUltralightPollerStateWriteFail;\n    }\n\n    instance->state = next_state;\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    do {\n        const MfUltralightData* write_data = instance->mfu_event.data->write_data;\n        uint8_t end_page = mf_ultralight_get_write_end_page(write_data->type);\n        if(instance->current_page == end_page) {\n            instance->state = MfUltralightPollerStateWriteSuccess;\n            break;\n        }\n        FURI_LOG_D(TAG, \"Writing page %d\", instance->current_page);\n        MfUltralightError error = mf_ultralight_poller_write_page(\n            instance, instance->current_page, &write_data->page[instance->current_page]);\n        if(error != MfUltralightErrorNone) {\n            instance->state = MfUltralightPollerStateWriteFail;\n            instance->error = error;\n            break;\n        }\n        instance->current_page++;\n    } while(false);\n\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_write_fail(MfUltralightPoller* instance) {\n    FURI_LOG_D(TAG, \"Write failed\");\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    instance->mfu_event.data->error = instance->error;\n    instance->mfu_event.type = MfUltralightPollerEventTypeWriteFail;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic NfcCommand mf_ultralight_poller_handler_write_success(MfUltralightPoller* instance) {\n    FURI_LOG_D(TAG, \"Write success\");\n    iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    instance->mfu_event.type = MfUltralightPollerEventTypeWriteSuccess;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic const MfUltralightPollerReadHandler\n    mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = {\n        [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle,\n        [MfUltralightPollerStateRequestMode] = mf_ultralight_poller_handler_request_mode,\n        [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version,\n        [MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c,\n        [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203,\n        [MfUltralightPollerStateGetFeatureSet] = mf_ultralight_poller_handler_get_feature_set,\n        [MfUltralightPollerStateReadSignature] = mf_ultralight_poller_handler_read_signature,\n        [MfUltralightPollerStateReadCounters] = mf_ultralight_poller_handler_read_counters,\n        [MfUltralightPollerStateReadTearingFlags] =\n            mf_ultralight_poller_handler_read_tearing_flags,\n        [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,\n        [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,\n        [MfUltralightPollerStateCheckMfulCAuthStatus] =\n            mf_ultralight_poller_handler_check_mfuc_auth_status,\n        [MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,\n        [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages,\n        [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail,\n        [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success,\n        [MfUltralightPollerStateRequestWriteData] =\n            mf_ultralight_poller_handler_request_write_data,\n        [MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages,\n        [MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail,\n        [MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success,\n};\n\nstatic NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    MfUltralightPoller* instance = context;\n    furi_assert(instance->callback);\n\n    const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        command = mf_ultralight_poller_read_handler[instance->state](instance);\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed;\n        command = instance->callback(instance->general_event, instance->context);\n    }\n\n    return command;\n}\n\nstatic bool mf_ultralight_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolIso14443_3a);\n\n    bool protocol_detected = false;\n    MfUltralightPoller* instance = context;\n    const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        MfUltralightPageReadCommandData read_page_cmd_data = {};\n        MfUltralightError error = mf_ultralight_poller_read_page(instance, 0, &read_page_cmd_data);\n        protocol_detected = (error == MfUltralightErrorNone);\n        iso14443_3a_poller_halt(instance->iso14443_3a_poller);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase mf_ultralight_poller = {\n    .alloc = (NfcPollerAlloc)mf_ultralight_poller_alloc,\n    .free = (NfcPollerFree)mf_ultralight_poller_free,\n    .set_callback = (NfcPollerSetCallback)mf_ultralight_poller_set_callback,\n    .run = (NfcPollerRun)mf_ultralight_poller_run,\n    .detect = (NfcPollerDetect)mf_ultralight_poller_detect,\n    .get_data = (NfcPollerGetData)mf_ultralight_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h",
    "content": "#pragma once\n\n#include \"mf_ultralight.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief MfUltralightPoller opaque type definition.\n */\ntypedef struct MfUltralightPoller MfUltralightPoller;\n\n/**\n * @brief Enumeration of possible MfUltralight poller event types.\n */\ntypedef enum {\n    MfUltralightPollerEventTypeRequestMode, /**< Poller requests for operating mode. */\n    MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */\n    MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */\n    MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */\n    MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */\n    MfUltralightPollerEventTypeReadFailed, /**< Poller failed to read card. */\n    MfUltralightPollerEventTypeRequestWriteData, /**< Poller request card data for write operation. */\n    MfUltralightPollerEventTypeCardMismatch, /**< Type of card for writing differs from presented one. */\n    MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */\n    MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */\n    MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */\n    MfUltralightPollerEventTypeRequestKey, /**< Poller requests key for dict attack. */\n} MfUltralightPollerEventType;\n\n/**\n * @brief Enumeration of possible MfUltralight poller operating modes.\n */\ntypedef enum {\n    MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */\n    MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */\n    MfUltralightPollerModeDictAttack, /**< Poller will perform dictionary attack against card. */\n} MfUltralightPollerMode;\n\n/**\n * @brief MfUltralight poller authentication context.\n */\ntypedef struct {\n    MfUltralightAuthPassword password; /**< Password to be used for authentication. */\n    MfUltralightC3DesAuthKey tdes_key; /**< 3DES key to be used for authentication. */\n    MfUltralightAuthPack pack; /**< Pack received on successful authentication. */\n    bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */\n    bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */\n} MfUltralightPollerAuthContext;\n\n/**\n * @brief MfUltralight poller key request data.\n */\ntypedef struct {\n    MfUltralightC3DesAuthKey key; /**< Key to try. */\n    bool key_provided; /**< Set to true if key was provided, false to stop attack. */\n} MfUltralightPollerKeyRequestData;\n\n/**\n * @brief MfUltralight poller event data.\n */\ntypedef union {\n    MfUltralightPollerAuthContext auth_context; /**< Authentication context. */\n    MfUltralightError error; /**< Error code indicating reading fail reason. */\n    const MfUltralightData* write_data; /**< Data to be written to card. */\n    MfUltralightPollerMode poller_mode; /**< Mode to operate in. */\n    MfUltralightPollerKeyRequestData key_request_data; /**< Key request data. */\n} MfUltralightPollerEventData;\n\n/**\n * @brief MfUltralight poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    MfUltralightPollerEventType type; /**< Type of emitted event. */\n    MfUltralightPollerEventData* data; /**< Pointer to event specific data. */\n} MfUltralightPollerEvent;\n\n/**\n * @brief Perform authentication with password.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in, out] data pointer to the authentication context.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_auth_pwd(\n    MfUltralightPoller* instance,\n    MfUltralightPollerAuthContext* data);\n\n/**\n * @brief Start authentication procedure.\n *\n * Must ONLY be used inside the callback function.\n *\n * This function is used to start authentication process for Ultralight C cards.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] RndA Randomly generated block which is required for authentication process.\n * @param[out] output Authentication encryption result.\n * @return MfUltralightErrorNone if card supports authentication command, an error code on otherwise.\n */\nMfUltralightError mf_ultralight_poller_authenticate_start(\n    MfUltralightPoller* instance,\n    const uint8_t* RndA,\n    uint8_t* output);\n\n/**\n * @brief End authentication procedure\n * \n * This function is used to end authentication process for Ultralight C cards.\n * \n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] RndB Block received from the card (card generates it randomly) which is required for authentication process.\n * @param[in] request Contains data of RndA + RndB', where RndB' is decoded and shifted RndB received from the card on previous step.\n * @param[out] response Must return RndA' which an encrypted shifted RndA value received from the card and decrypted by this function.\n*/\nMfUltralightError mf_ultralight_poller_authenticate_end(\n    MfUltralightPoller* instance,\n    const uint8_t* RndB,\n    const uint8_t* request,\n    uint8_t* response);\n\n/**\n * @brief Read page from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * Send read command and parse response. The response on this command is data of 4 pages starting\n * from the page specified in the command.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] start_page page number to be read.\n * @param[out] data pointer to the MfUltralightPageReadCommandData structure to be filled with page data.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_read_page(\n    MfUltralightPoller* instance,\n    uint8_t start_page,\n    MfUltralightPageReadCommandData* data);\n\n/**\n * @brief Read page from sector.\n *\n * Must ONLY be used inside the callback function.\n *\n * This command should be used for NTAGI2C tags.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] sector sector number to be read.\n * @param[in] tag tag number to be read.\n * @param[out] data pointer to the MfUltralightPageReadCommandData structure to be filled with page data.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_read_page_from_sector(\n    MfUltralightPoller* instance,\n    uint8_t sector,\n    uint8_t tag,\n    MfUltralightPageReadCommandData* data);\n\n/**\n * @brief Write page to card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] page page number to be written.\n * @param[in] data pointer to the MfUltralightPage structure to be written.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_write_page(\n    MfUltralightPoller* instance,\n    uint8_t page,\n    const MfUltralightPage* data);\n\n/**\n * @brief Read version from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfUltralightVersion structure to be filled.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError\n    mf_ultralight_poller_read_version(MfUltralightPoller* instance, MfUltralightVersion* data);\n\n/**\n * @brief Read signature from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the MfUltralightSignature structure to be filled.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError\n    mf_ultralight_poller_read_signature(MfUltralightPoller* instance, MfUltralightSignature* data);\n\n/**\n * @brief Read counter from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] counter_num counter number to be read.\n * @param[out] data pointer to the MfUltralightCounter structure to be filled.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_read_counter(\n    MfUltralightPoller* instance,\n    uint8_t counter_num,\n    MfUltralightCounter* data);\n\n/**\n * @brief Read tearing flag from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tearing_falg_num tearing flag number to be read.\n * @param[out] data pointer to the MfUltralightTearingFlag structure to be filled.\n * @return MfUltralightErrorNone on success, an error code on failure.\n */\nMfUltralightError mf_ultralight_poller_read_tearing_flag(\n    MfUltralightPoller* instance,\n    uint8_t tearing_falg_num,\n    MfUltralightTearingFlag* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase mf_ultralight_poller;\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c",
    "content": "#include \"mf_ultralight_poller_i.h\"\n\n#include <furi.h>\n\n#define TAG \"MfUltralightPoller\"\n\nMfUltralightError mf_ultralight_process_error(Iso14443_3aError error) {\n    MfUltralightError ret = MfUltralightErrorNone;\n\n    switch(error) {\n    case Iso14443_3aErrorNone:\n        ret = MfUltralightErrorNone;\n        break;\n    case Iso14443_3aErrorNotPresent:\n        ret = MfUltralightErrorNotPresent;\n        break;\n    case Iso14443_3aErrorColResFailed:\n    case Iso14443_3aErrorCommunication:\n    case Iso14443_3aErrorWrongCrc:\n        ret = MfUltralightErrorProtocol;\n        break;\n    case Iso14443_3aErrorTimeout:\n        ret = MfUltralightErrorTimeout;\n        break;\n    default:\n        ret = MfUltralightErrorProtocol;\n        break;\n    }\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_auth_pwd(\n    MfUltralightPoller* instance,\n    MfUltralightPollerAuthContext* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_PWD_AUTH}; //-V1009\n    memcpy(&auth_cmd[1], data->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE);\n    bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n    do {\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_PACK_SIZE) {\n            ret = MfUltralightErrorAuth;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE);\n    } while(false);\n\n    return ret;\n}\n\nstatic MfUltralightError mf_ultralight_poller_send_authenticate_cmd(\n    MfUltralightPoller* instance,\n    const uint8_t* cmd,\n    const uint8_t length,\n    const bool initial_cmd,\n    uint8_t* response) {\n    furi_check(instance);\n    furi_check(cmd);\n    furi_check(response);\n\n    bit_buffer_copy_bytes(instance->tx_buffer, cmd, length);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n    do {\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n\n        const uint8_t expected_response_code = initial_cmd ? 0xAF : 0x00;\n        if((bit_buffer_get_byte(instance->rx_buffer, 0) != expected_response_code) ||\n           (bit_buffer_get_size_bytes(instance->rx_buffer) !=\n            MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE)) {\n            ret = MfUltralightErrorAuth;\n            break;\n        }\n\n        memcpy(\n            response,\n            bit_buffer_get_data(instance->rx_buffer) + 1,\n            MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance) {\n    furi_check(instance);\n\n    uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};\n    uint8_t dummy[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];\n    return mf_ultralight_poller_send_authenticate_cmd(\n        instance, auth_cmd, sizeof(auth_cmd), true, dummy);\n}\n\nMfUltralightError mf_ultralight_poller_authenticate_start(\n    MfUltralightPoller* instance,\n    const uint8_t* RndA,\n    uint8_t* output) {\n    furi_check(instance);\n    furi_check(RndA);\n    furi_check(output);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    do {\n        uint8_t encRndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};\n        uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};\n        ret = mf_ultralight_poller_send_authenticate_cmd(\n            instance, auth_cmd, sizeof(auth_cmd), true, encRndB /* instance->encRndB */);\n\n        if(ret != MfUltralightErrorNone) break;\n\n        uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0};\n        uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;\n        mf_ultralight_3des_decrypt(\n            &instance->des_context,\n            instance->auth_context.tdes_key.data,\n            iv,\n            encRndB,\n            sizeof(encRndB),\n            RndB);\n        mf_ultralight_3des_shift_data(RndB);\n\n        memcpy(output, RndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);\n\n        mf_ultralight_3des_encrypt(\n            &instance->des_context,\n            instance->auth_context.tdes_key.data,\n            encRndB,\n            output,\n            MF_ULTRALIGHT_C_AUTH_DATA_SIZE,\n            output);\n\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_authenticate_end(\n    MfUltralightPoller* instance,\n    const uint8_t* RndB,\n    const uint8_t* request,\n    uint8_t* response) {\n    furi_check(instance);\n    furi_check(RndB);\n    furi_check(request);\n    furi_check(response);\n\n    uint8_t auth_cmd[MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE] = {0xAF}; //-V1009\n    memcpy(&auth_cmd[1], request, MF_ULTRALIGHT_C_AUTH_DATA_SIZE);\n    bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    do {\n        ret = mf_ultralight_poller_send_authenticate_cmd(\n            instance, auth_cmd, sizeof(auth_cmd), false, response);\n\n        if(ret != MfUltralightErrorNone) break;\n\n        mf_ultralight_3des_decrypt(\n            &instance->des_context,\n            instance->auth_context.tdes_key.data,\n            RndB,\n            bit_buffer_get_data(instance->rx_buffer) + 1,\n            MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,\n            response);\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_read_page_from_sector(\n    MfUltralightPoller* instance,\n    uint8_t sector,\n    uint8_t tag,\n    MfUltralightPageReadCommandData* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        const uint8_t select_sector_cmd[2] = {MF_ULTRALIGHT_CMD_SECTOR_SELECT, 0xff};\n        bit_buffer_copy_bytes(instance->tx_buffer, select_sector_cmd, sizeof(select_sector_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorWrongCrc) {\n            FURI_LOG_D(TAG, \"Failed to issue sector select command\");\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n\n        const uint8_t read_sector_cmd[4] = {sector, 0x00, 0x00, 0x00};\n        bit_buffer_copy_bytes(instance->tx_buffer, read_sector_cmd, sizeof(read_sector_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorTimeout) {\n            // This is NOT a typo! The tag ACKs by not sending a response within 1ms.\n            FURI_LOG_D(TAG, \"Sector %u select NAK'd\", sector);\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n\n        ret = mf_ultralight_poller_read_page(instance, tag, data);\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_read_page(\n    MfUltralightPoller* instance,\n    uint8_t start_page,\n    MfUltralightPageReadCommandData* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t read_page_cmd[2] = {MF_ULTRALIGHT_CMD_READ_PAGE, start_page};\n        bit_buffer_copy_bytes(instance->tx_buffer, read_page_cmd, sizeof(read_page_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) !=\n           sizeof(MfUltralightPageReadCommandData)) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightPageReadCommandData));\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_write_page(\n    MfUltralightPoller* instance,\n    uint8_t page,\n    const MfUltralightPage* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t write_page_cmd[MF_ULTRALIGHT_PAGE_SIZE + 2] = {MF_ULTRALIGHT_CMD_WRITE_PAGE, page};\n        memcpy(&write_page_cmd[2], data->data, MF_ULTRALIGHT_PAGE_SIZE);\n        bit_buffer_copy_bytes(instance->tx_buffer, write_page_cmd, sizeof(write_page_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorWrongCrc) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size(instance->rx_buffer) != 4) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        if(!bit_buffer_starts_with_byte(instance->rx_buffer, MF_ULTRALIGHT_CMD_ACK)) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError\n    mf_ultralight_poller_read_version(MfUltralightPoller* instance, MfUltralightVersion* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        const uint8_t get_version_cmd = MF_ULTRALIGHT_CMD_GET_VERSION;\n        bit_buffer_copy_bytes(instance->tx_buffer, &get_version_cmd, sizeof(get_version_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightVersion)) {\n            FURI_LOG_I(\n                TAG, \"Read Version failed: %zu\", bit_buffer_get_size_bytes(instance->rx_buffer));\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightVersion));\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError\n    mf_ultralight_poller_read_signature(MfUltralightPoller* instance, MfUltralightSignature* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        const uint8_t read_signature_cmd[2] = {MF_ULTRALIGHT_CMD_READ_SIG, 0x00};\n        bit_buffer_copy_bytes(instance->tx_buffer, read_signature_cmd, sizeof(read_signature_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightSignature)) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightSignature));\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_read_counter(\n    MfUltralightPoller* instance,\n    uint8_t counter_num,\n    MfUltralightCounter* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t read_counter_cmd[2] = {MF_ULTRALIGHT_CMD_READ_CNT, counter_num};\n        bit_buffer_copy_bytes(instance->tx_buffer, read_counter_cmd, sizeof(read_counter_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_COUNTER_SIZE) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data->data, MF_ULTRALIGHT_COUNTER_SIZE);\n    } while(false);\n\n    return ret;\n}\n\nMfUltralightError mf_ultralight_poller_read_tearing_flag(\n    MfUltralightPoller* instance,\n    uint8_t tearing_falg_num,\n    MfUltralightTearingFlag* data) {\n    furi_check(instance);\n    furi_check(data);\n\n    MfUltralightError ret = MfUltralightErrorNone;\n    Iso14443_3aError error = Iso14443_3aErrorNone;\n\n    do {\n        uint8_t check_tearing_cmd[2] = {MF_ULTRALIGHT_CMD_CHECK_TEARING, tearing_falg_num};\n        bit_buffer_copy_bytes(instance->tx_buffer, check_tearing_cmd, sizeof(check_tearing_cmd));\n        error = iso14443_3a_poller_send_standard_frame(\n            instance->iso14443_3a_poller,\n            instance->tx_buffer,\n            instance->rx_buffer,\n            MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC);\n        if(error != Iso14443_3aErrorNone) {\n            ret = mf_ultralight_process_error(error);\n            break;\n        }\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightTearingFlag)) {\n            ret = MfUltralightErrorProtocol;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightTearingFlag));\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h",
    "content": "#pragma once\n\n#include \"mf_ultralight_poller.h\"\n#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>\n#include <lib/bit_lib/bit_lib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000)\n#define MF_ULTRALIGHT_MAX_BUFF_SIZE          (64)\n\n#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL)\n\n#define MF_ULTRALIGHT_IS_NTAG_I2C(type)                                                \\\n    (((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \\\n     ((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K))\n\ntypedef struct {\n    MfUltralightPage page;\n    uint8_t page_to_write;\n} MfUltralightPollerWritePageCommand;\n\ntypedef struct {\n    MfUltralightPageReadCommandData data;\n    uint8_t start_page;\n} MfUltralightPollerReadPageCommand;\n\ntypedef struct {\n    MfUltralightCounter data;\n    uint8_t counter_num;\n} MfUltralightPollerReadCounterCommand;\n\ntypedef struct {\n    MfUltralightTearingFlag data;\n    uint8_t tearing_flag_num;\n} MfUltralightPollerReadTearingFlagCommand;\n\ntypedef union {\n    MfUltralightPollerWritePageCommand write_cmd;\n    MfUltralightPollerReadPageCommand read_cmd;\n    MfUltralightVersion version;\n    MfUltralightSignature signature;\n    MfUltralightPollerReadCounterCommand counter_cmd;\n    MfUltralightPollerReadTearingFlagCommand tearing_flag_cmd;\n    MfUltralightData* data;\n} MfUltralightPollerContextData;\n\ntypedef enum {\n    MfUltralightPollerStateIdle,\n    MfUltralightPollerStateRequestMode,\n    MfUltralightPollerStateReadVersion,\n    MfUltralightPollerStateDetectMfulC,\n    MfUltralightPollerStateDetectNtag203,\n    MfUltralightPollerStateGetFeatureSet,\n    MfUltralightPollerStateReadSignature,\n    MfUltralightPollerStateReadCounters,\n    MfUltralightPollerStateReadTearingFlags,\n    MfUltralightPollerStateAuth,\n    MfUltralightPollerStateAuthMfulC,\n    MfUltralightPollerStateReadPages,\n    MfUltralightPollerStateTryDefaultPass,\n    MfUltralightPollerStateCheckMfulCAuthStatus,\n    MfUltralightPollerStateReadFailed,\n    MfUltralightPollerStateReadSuccess,\n    MfUltralightPollerStateRequestWriteData,\n    MfUltralightPollerStateWritePages,\n    MfUltralightPollerStateWriteFail,\n    MfUltralightPollerStateWriteSuccess,\n\n    MfUltralightPollerStateNum,\n} MfUltralightPollerState;\n\nstruct MfUltralightPoller {\n    Iso14443_3aPoller* iso14443_3a_poller;\n    MfUltralightPollerState state;\n    MfUltralightPollerMode mode;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n    MfUltralightData* data;\n    MfUltralightPollerAuthContext auth_context;\n    uint32_t feature_set;\n    uint16_t pages_read;\n    uint16_t pages_total;\n    uint8_t counters_read;\n    uint8_t counters_total;\n    uint8_t tearing_flag_read;\n    uint8_t tearing_flag_total;\n    uint16_t current_page;\n    MfUltralightError error;\n    mbedtls_des3_context des_context;\n\n    NfcGenericEvent general_event;\n    MfUltralightPollerEvent mfu_event;\n    MfUltralightPollerEventData mfu_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nMfUltralightError mf_ultralight_process_error(Iso14443_3aError error);\n\nMfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller);\n\nvoid mf_ultralight_poller_free(MfUltralightPoller* instance);\n\nconst MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance);\n\nbool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(\n    MfUltralightPoller* instance,\n    uint16_t lin_addr,\n    uint8_t* sector,\n    uint8_t* tag,\n    uint8_t* pages_left);\n\nMfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c",
    "content": "#include \"mf_ultralight_poller_sync.h\"\n#include \"mf_ultralight_poller_i.h\"\n\n#include <nfc/nfc_poller.h>\n\n#include <furi.h>\n\n#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)\n\ntypedef enum {\n    MfUltralightPollerCmdTypeReadPage,\n    MfUltralightPollerCmdTypeWritePage,\n    MfUltralightPollerCmdTypeReadVersion,\n    MfUltralightPollerCmdTypeReadSignature,\n    MfUltralightPollerCmdTypeReadCounter,\n    MfUltralightPollerCmdTypeReadTearingFlag,\n\n    MfUltralightPollerCmdTypeNum,\n} MfUltralightPollerCmdType;\n\ntypedef struct {\n    MfUltralightPollerCmdType cmd_type;\n    FuriThreadId thread_id;\n    MfUltralightError error;\n    MfUltralightPollerContextData data;\n    const MfUltralightPollerAuthContext* auth_context;\n} MfUltralightPollerContext;\n\ntypedef MfUltralightError (*MfUltralightPollerCmdHandler)(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data);\n\nMfUltralightError mf_ultralight_poller_read_page_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_read_page(poller, data->read_cmd.start_page, &data->read_cmd.data);\n}\n\nMfUltralightError mf_ultralight_poller_write_page_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_write_page(\n        poller, data->write_cmd.page_to_write, &data->write_cmd.page);\n}\n\nMfUltralightError mf_ultralight_poller_read_version_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_read_version(poller, &data->version);\n}\n\nMfUltralightError mf_ultralight_poller_read_signature_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_read_signature(poller, &data->signature);\n}\n\nMfUltralightError mf_ultralight_poller_read_counter_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_read_counter(\n        poller, data->counter_cmd.counter_num, &data->counter_cmd.data);\n}\n\nMfUltralightError mf_ultralight_poller_read_tearing_flag_handler(\n    MfUltralightPoller* poller,\n    MfUltralightPollerContextData* data) {\n    return mf_ultralight_poller_read_tearing_flag(\n        poller, data->tearing_flag_cmd.tearing_flag_num, &data->tearing_flag_cmd.data);\n}\n\nstatic const MfUltralightPollerCmdHandler\n    mf_ultralight_poller_cmd_handlers[MfUltralightPollerCmdTypeNum] = {\n        [MfUltralightPollerCmdTypeReadPage] = mf_ultralight_poller_read_page_handler,\n        [MfUltralightPollerCmdTypeWritePage] = mf_ultralight_poller_write_page_handler,\n        [MfUltralightPollerCmdTypeReadVersion] = mf_ultralight_poller_read_version_handler,\n        [MfUltralightPollerCmdTypeReadSignature] = mf_ultralight_poller_read_signature_handler,\n        [MfUltralightPollerCmdTypeReadCounter] = mf_ultralight_poller_read_counter_handler,\n        [MfUltralightPollerCmdTypeReadTearingFlag] =\n            mf_ultralight_poller_read_tearing_flag_handler,\n};\n\nstatic NfcCommand mf_ultralight_poller_cmd_callback(NfcGenericEventEx event, void* context) {\n    furi_assert(event.poller);\n    furi_assert(event.parent_event_data);\n    furi_assert(context);\n\n    MfUltralightPollerContext* poller_context = context;\n    Iso14443_3aPollerEvent* iso14443_3a_event = event.parent_event_data;\n    MfUltralightPoller* mfu_poller = event.poller;\n\n    if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {\n        poller_context->error = mf_ultralight_poller_cmd_handlers[poller_context->cmd_type](\n            mfu_poller, &poller_context->data);\n    } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {\n        poller_context->error = mf_ultralight_process_error(iso14443_3a_event->data->error);\n    }\n\n    furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT);\n\n    return NfcCommandStop;\n}\n\nstatic MfUltralightError\n    mf_ultralight_poller_cmd_execute(Nfc* nfc, MfUltralightPollerContext* poller_ctx) {\n    furi_assert(poller_ctx->cmd_type < MfUltralightPollerCmdTypeNum);\n\n    poller_ctx->thread_id = furi_thread_get_current_id();\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfUltralight);\n    nfc_poller_start_ex(poller, mf_ultralight_poller_cmd_callback, poller_ctx);\n    furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    return poller_ctx->error;\n}\n\nMfUltralightError\n    mf_ultralight_poller_sync_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeReadPage,\n        .data.read_cmd.start_page = page,\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfUltralightErrorNone) {\n        *data = poller_context.data.read_cmd.data.page[0];\n    }\n\n    return error;\n}\n\nMfUltralightError\n    mf_ultralight_poller_sync_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeWritePage,\n        .data.write_cmd =\n            {\n                .page_to_write = page,\n                .page = *data,\n            },\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    return error;\n}\n\nMfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightVersion* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeReadVersion,\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfUltralightErrorNone) {\n        *data = poller_context.data.version;\n    }\n\n    return error;\n}\n\nMfUltralightError mf_ultralight_poller_sync_read_signature(Nfc* nfc, MfUltralightSignature* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeReadSignature,\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfUltralightErrorNone) {\n        *data = poller_context.data.signature;\n    }\n\n    return error;\n}\n\nMfUltralightError mf_ultralight_poller_sync_read_counter(\n    Nfc* nfc,\n    uint8_t counter_num,\n    MfUltralightCounter* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeReadCounter,\n        .data.counter_cmd.counter_num = counter_num,\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfUltralightErrorNone) {\n        *data = poller_context.data.counter_cmd.data;\n    }\n\n    return error;\n}\n\nMfUltralightError mf_ultralight_poller_sync_read_tearing_flag(\n    Nfc* nfc,\n    uint8_t flag_num,\n    MfUltralightTearingFlag* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {\n        .cmd_type = MfUltralightPollerCmdTypeReadTearingFlag,\n        .data.tearing_flag_cmd.tearing_flag_num = flag_num,\n    };\n\n    MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context);\n\n    if(error == MfUltralightErrorNone) {\n        *data = poller_context.data.tearing_flag_cmd.data;\n    }\n\n    return error;\n}\n\nstatic NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.instance);\n    furi_assert(event.event_data);\n    furi_assert(event.protocol == NfcProtocolMfUltralight);\n\n    NfcCommand command = NfcCommandContinue;\n    MfUltralightPollerContext* poller_context = context;\n    MfUltralightPoller* mfu_poller = event.instance;\n    MfUltralightPollerEvent* mfu_event = event.event_data;\n\n    if(mfu_event->type == MfUltralightPollerEventTypeReadSuccess) {\n        mf_ultralight_copy(poller_context->data.data, mf_ultralight_poller_get_data(mfu_poller));\n        poller_context->error = MfUltralightErrorNone;\n        command = NfcCommandStop;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeReadFailed) {\n        poller_context->error = mf_ultralight_process_error(\n            mfu_poller->iso14443_3a_poller->iso14443_3a_event_data.error);\n        command = NfcCommandStop;\n    } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {\n        if(poller_context->auth_context != NULL) {\n            mfu_event->data->auth_context = *poller_context->auth_context;\n        } else {\n            mfu_event->data->auth_context.skip_auth = true;\n            if(mfu_poller->data->type == MfUltralightTypeMfulC) {\n                mfu_event->data->auth_context.skip_auth = false;\n                memset(\n                    mfu_poller->auth_context.tdes_key.data,\n                    0x00,\n                    MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);\n            }\n        }\n    }\n\n    if(command == NfcCommandStop) {\n        furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT);\n    }\n\n    return command;\n}\n\nMfUltralightError mf_ultralight_poller_sync_read_card(\n    Nfc* nfc,\n    MfUltralightData* data,\n    const MfUltralightPollerAuthContext* auth_context) {\n    furi_check(nfc);\n    furi_check(data);\n\n    MfUltralightPollerContext poller_context = {};\n    poller_context.thread_id = furi_thread_get_current_id();\n    poller_context.data.data = mf_ultralight_alloc();\n    poller_context.auth_context = auth_context;\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfUltralight);\n    nfc_poller_start(poller, mf_ultralight_poller_read_callback, &poller_context);\n    furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    if(poller_context.error == MfUltralightErrorNone) {\n        mf_ultralight_copy(data, poller_context.data.data);\n    }\n\n    mf_ultralight_free(poller_context.data.data);\n\n    return poller_context.error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h",
    "content": "#pragma once\n\n#include \"mf_ultralight.h\"\n#include \"mf_ultralight_poller.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nMfUltralightError\n    mf_ultralight_poller_sync_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data);\n\nMfUltralightError\n    mf_ultralight_poller_sync_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data);\n\nMfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightVersion* data);\n\nMfUltralightError mf_ultralight_poller_sync_read_signature(Nfc* nfc, MfUltralightSignature* data);\n\nMfUltralightError mf_ultralight_poller_sync_read_counter(\n    Nfc* nfc,\n    uint8_t counter_num,\n    MfUltralightCounter* data);\n\nMfUltralightError mf_ultralight_poller_sync_read_tearing_flag(\n    Nfc* nfc,\n    uint8_t flag_num,\n    MfUltralightTearingFlag* data);\n\nMfUltralightError mf_ultralight_poller_sync_read_card(\n    Nfc* nfc,\n    MfUltralightData* data,\n    const MfUltralightPollerAuthContext* auth_context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_device_base.h",
    "content": "/**\n * @file nfc_device_base.h\n * @brief Common top-level types for the NFC protocol stack.\n */\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Verbosity level of the displayed NFC device name.\n */\ntypedef enum {\n    NfcDeviceNameTypeFull, /**< Display full(verbose) name. */\n    NfcDeviceNameTypeShort, /**< Display shortened name. */\n} NfcDeviceNameType;\n\n/**\n * @brief Generic opaque type for protocol-specific NFC device data.\n */\ntypedef void NfcDeviceData;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_device_base_i.h",
    "content": "/**\n * @file nfc_device_base_i.h\n * @brief Abstract interface definitions for the NFC device system.\n *\n * This file is an implementation detail. It must not be included in\n * any public API-related headers.\n */\n#pragma once\n\n#include \"nfc_device_base.h\"\n\n#include <flipper_format.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Allocate the protocol-specific NFC device data instance.\n *\n * @returns pointer to the allocated instance.\n */\ntypedef NfcDeviceData* (*NfcDeviceAlloc)(void);\n\n/**\n * @brief Delete the protocol-specific NFC device data instance.\n *\n * @param[in,out] data pointer to the instance to be deleted.\n */\ntypedef void (*NfcDeviceFree)(NfcDeviceData* data);\n\n/**\n * @brief Reset the NFC device data instance.\n *\n * The behaviour is protocol-specific. Usually, required fields are zeroed or\n * set to their initial values.\n *\n * @param[in,out] data pointer to the instance to be reset.\n */\ntypedef void (*NfcDeviceReset)(NfcDeviceData* data);\n\n/**\n * @brief Copy source instance's data into the destination so that they become equal.\n *\n * @param[in,out] data pointer to the destination instance.\n * @param[in] other pointer to the source instance.\n */\ntypedef void (*NfcDeviceCopy)(NfcDeviceData* data, const NfcDeviceData* other);\n\n/**\n * @brief Deprecated. Do not use in new protocols.\n * @deprecated do not use in new protocols.\n *\n * @param[in,out] data pointer to the instance to be tested.\n * @param[in] device_type pointer to a FuriString containing a device type identifier.\n * @returns true if data was verified, false otherwise.\n */\ntypedef bool (*NfcDeviceVerify)(NfcDeviceData* data, const FuriString* device_type);\n\n/**\n * @brief Load NFC device data from a FlipperFormat file.\n *\n * The FlipperFormat file structure must be initialised and open by the calling code.\n *\n * @param[in,out] data pointer to the instance to be loaded into.\n * @param[in] ff pointer to the FlipperFormat file instance.\n * @param[in] version file format version to use when loading.\n * @returns true if loaded successfully, false otherwise.\n */\ntypedef bool (*NfcDeviceLoad)(NfcDeviceData* data, FlipperFormat* ff, uint32_t version);\n\n/**\n * @brief Save NFC device data to a FlipperFormat file.\n *\n * The FlipperFormat file structure must be initialised and open by the calling code.\n *\n * @param[in] data pointer to the instance to be saved.\n * @param[in] ff pointer to the FlipperFormat file instance.\n * @returns true if saved successfully, false otherwise.\n */\ntypedef bool (*NfcDeviceSave)(const NfcDeviceData* data, FlipperFormat* ff);\n\n/**\n * @brief Compare two NFC device data instances.\n *\n * @param[in] data pointer to the first instance to be compared.\n * @param[in] other pointer to the second instance to be compared.\n * @returns true if instances are equal, false otherwise.\n */\ntypedef bool (*NfcDeviceEqual)(const NfcDeviceData* data, const NfcDeviceData* other);\n\n/**\n * @brief Get a protocol-specific stateful NFC device name.\n *\n * The return value may change depending on the instance's internal state and the name_type parameter.\n *\n * @param[in] data pointer to the instance to be queried.\n * @param[in] name_type type of the name to be displayed.\n * @returns pointer to a statically allocated character string containing the appropriate name.\n */\ntypedef const char* (*NfcDeviceGetName)(const NfcDeviceData* data, NfcDeviceNameType name_type);\n\n/**\n * @brief Get the NFC device's unique identifier (UID).\n *\n * The UID length is protocol-dependent. Additionally, a particular protocol might support\n * several UID lengths.\n *\n * @param[in] data pointer to the instance to be queried.\n * @param[out] uid_len pointer to the variable to contain the UID length.\n * @returns pointer to the byte array containing the device's UID.\n */\ntypedef const uint8_t* (*NfcDeviceGetUid)(const NfcDeviceData* data, size_t* uid_len);\n\n/**\n * @brief Set the NFC device's unique identifier (UID).\n *\n * The UID length must be supported by the protocol in question.\n *\n * @param[in,out] data pointer to the instance to be modified.\n * @param[in] uid pointer to the byte array containing the new UID.\n * @param[in] uid_len length of the UID.\n * @return true if the UID was valid and set, false otherwise.\n */\ntypedef bool (*NfcDeviceSetUid)(NfcDeviceData* data, const uint8_t* uid, size_t uid_len);\n\n/**\n * @brief Get the NFC device data associated with the parent protocol.\n *\n * The protocol the instance's data is associated with must have a parent.\n *\n * @param[in] data pointer to the instance to be queried.\n * @returns pointer to the data instance associated with the parent protocol.\n */\ntypedef NfcDeviceData* (*NfcDeviceGetBaseData)(const NfcDeviceData* data);\n\n/**\n * @brief Generic NFC device interface.\n *\n * Each protocol must fill this structure with its own function implementations.\n */\ntypedef struct {\n    const char*\n        protocol_name; /**< Pointer to a statically-allocated string with the protocol name. */\n    NfcDeviceAlloc alloc; /**< Pointer to the alloc() function. */\n    NfcDeviceFree free; /**< Pointer to the free() function. */\n    NfcDeviceReset reset; /**< Pointer to the reset() function. */\n    NfcDeviceCopy copy; /**< Pointer to the copy() function. */\n    NfcDeviceVerify verify; /**< Deprecated. Set to NULL in new protocols. */\n    NfcDeviceLoad load; /**< Pointer to the load() function. */\n    NfcDeviceSave save; /**< Pointer to the save() function. */\n    NfcDeviceEqual is_equal; /**< Pointer to the is_equal() function. */\n    NfcDeviceGetName get_name; /**< Pointer to the get_name() function. */\n    NfcDeviceGetUid get_uid; /**< Pointer to the get_uid() function. */\n    NfcDeviceSetUid set_uid; /**< Pointer to the set_uid() function. */\n    NfcDeviceGetBaseData get_base_data; /**< Pointer to the get_base_data() function. */\n} NfcDeviceBase;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_device_defs.c",
    "content": "/**\n * @file nfc_device_defs.c\n * @brief Main NFC device implementation definitions.\n *\n * All NFC device implementations must be registered here in order to be used\n * by the NfcDevice library.\n *\n * @see nfc_device.h\n *\n * This file is to be modified upon adding a new protocol (see below).\n */\n#include \"nfc_device_base_i.h\"\n#include \"nfc_protocol.h\"\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_device_defs.h>\n#include <nfc/protocols/iso14443_3b/iso14443_3b_device_defs.h>\n#include <nfc/protocols/iso14443_4a/iso14443_4a_device_defs.h>\n#include <nfc/protocols/iso14443_4b/iso14443_4b_device_defs.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_device_defs.h>\n#include <nfc/protocols/felica/felica.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight.h>\n#include <nfc/protocols/mf_classic/mf_classic.h>\n#include <nfc/protocols/mf_plus/mf_plus.h>\n#include <nfc/protocols/mf_desfire/mf_desfire.h>\n#include <nfc/protocols/slix/slix_device_defs.h>\n#include <nfc/protocols/st25tb/st25tb.h>\n\n/**\n * @brief List of registered NFC device implementations.\n *\n * When implementing a new protocol, add its implementation\n * here under its own index defined in nfc_protocol.h.\n */\nconst NfcDeviceBase* const nfc_devices[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a,\n    [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b,\n    [NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a,\n    [NfcProtocolIso14443_4b] = &nfc_device_iso14443_4b,\n    [NfcProtocolIso15693_3] = &nfc_device_iso15693_3,\n    [NfcProtocolFelica] = &nfc_device_felica,\n    [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight,\n    [NfcProtocolMfClassic] = &nfc_device_mf_classic,\n    [NfcProtocolMfPlus] = &nfc_device_mf_plus,\n    [NfcProtocolMfDesfire] = &nfc_device_mf_desfire,\n    [NfcProtocolSlix] = &nfc_device_slix,\n    [NfcProtocolSt25tb] = &nfc_device_st25tb,\n    /* Add new protocols here */\n};\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_device_defs.h",
    "content": "#pragma once\n\n#include \"nfc_device_base_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcDeviceBase* const nfc_devices[];\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_generic_event.h",
    "content": "/**\n * @file nfc_generic_event.h\n * @brief Generic Nfc stack event definitions.\n *\n * Events are the main way of passing information about, well, various events\n * that occur across the Nfc protocol stack.\n *\n * In order to subscribe to events from a certain instance, the user code must call\n * its corresponding start() function while providing the appropriate callback.\n * During this call, an additional context pointer can be provided, which will be passed\n * to the context parameter at the time of the callback execution.\n *\n * For additional information on how events are passed around and processed, see protocol-specific\n * poller and listener implementations found in the respectively named subfolders.\n *\n */\n#pragma once\n\n#include \"nfc_protocol.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Generic Nfc instance type.\n *\n * Must be cast to a concrete type before use.\n * Depending on the context, a pointer of this type\n * may point to an object of the following types:\n * - Nfc type,\n * - Concrete poller type,\n * - Concrete listener type.\n */\ntypedef void NfcGenericInstance;\n\n/**\n * @brief Generic Nfc event data type.\n *\n * Must be cast to a concrete type before use.\n * Usually, it will be the protocol-specific event type.\n */\ntypedef void NfcGenericEventData;\n\n/**\n * @brief Generic Nfc event type.\n *\n * A generic Nfc event contains a protocol identifier, can be used to determine\n * the remaing fields' type.\n *\n * If the value of the protocol field is NfcProtocolInvalid, then it means that\n * the event was emitted from an Nfc instance, otherwise it originated from\n * a concrete poller or listener instance.\n *\n * The event_data field is protocol-specific and should be cast to the appropriate type before use.\n */\ntypedef struct {\n    NfcProtocol protocol; /**< Protocol identifier of the instance that produced the event. */\n    NfcGenericInstance*\n        instance; /**< Pointer to the protocol-specific instance that produced the event. */\n    NfcGenericEventData* event_data; /**< Pointer to the protocol-specific event. */\n} NfcGenericEvent;\n\n/**\n * @brief Generic Nfc event callback type.\n *\n * A function of this type must be passed as the callback parameter upon start\n * of a poller, listener or Nfc instance.\n *\n * @param [in] event Nfc generic event, passed by value, complete with protocol type and data.\n * @param [in,out] context pointer to the user-specific context (set when starting a poller/listener instance).\n * @returns the command which the event producer must execute.\n */\ntypedef NfcCommand (*NfcGenericCallback)(NfcGenericEvent event, void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_listener_base.h",
    "content": "/**\n * @file nfc_listener_base.h\n * @brief Abstract interface definitions for the NFC listener system.\n *\n * This file is an implementation detail. It must not be included in\n * any public API-related headers.\n *\n * @see nfc_listener.h\n *\n */\n#pragma once\n\n#include \"nfc_generic_event.h\"\n#include \"nfc_device_base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Allocate a protocol-specific listener instance.\n *\n * For base listeners pass a pointer to an instance of type Nfc\n * as the base_listener parameter, otherwise it must be a pointer to another listener instance\n * (compare iso14443_3a/iso14443_3a_listener.c and iso14443_4a/iso14443_4a_listener.c).\n *\n * @see nfc_protocol.c\n *\n * The NFC device data passed as the data parameter is copied to the instance and may\n * change during the emulation in response to reader commands.\n *\n * To retrieve the modified data, NfcListenerGetData gets called by the NfcListener\n * implementation when the user code calls nfc_listener_get_data().\n *\n * @param[in] base_listener pointer to the parent listener instance.\n * @param[in] data pointer to the protocol-specific data to use during emulation.\n * @returns pointer to the allocated listener instance.\n */\ntypedef NfcGenericInstance* (\n    *NfcListenerAlloc)(NfcGenericInstance* base_listener, NfcDeviceData* data);\n\n/**\n * @brief Delete a protocol-specific listener instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\ntypedef void (*NfcListenerFree)(NfcGenericInstance* instance);\n\n/**\n * @brief Set the callback function to handle events emitted by the listener instance.\n *\n * @see nfc_generic_event.h\n *\n * @param[in,out] listener\n * @param[in] callback pointer to the user-defined callback function which will receive events.\n * @param[in] context pointer to the user-specific context (will be passed to the callback).\n */\ntypedef void (*NfcListenerSetCallback)(\n    NfcGenericInstance* listener,\n    NfcGenericCallback callback,\n    void* context);\n\n/**\n * @brief Emulate a supported NFC card with given device data.\n *\n * @param[in] event protocol-specific event passed by the parent listener instance.\n * @param[in,out] context pointer to the protocol-specific listener instance.\n * @returns command to be executed by the parent listener instance.\n */\ntypedef NfcCommand (*NfcListenerRun)(NfcGenericEvent event, void* context);\n\n/**\n * @brief Get the protocol-specific data that was that was provided for emulation.\n *\n * @param[in] instance pointer to the protocol-specific listener instance.\n * @returns pointer to the NFC device data.\n */\ntypedef const NfcDeviceData* (*NfcListenerGetData)(const NfcGenericInstance* instance);\n\n/**\n * @brief Generic NFC listener interface.\n *\n * Each protocol must fill this structure with its own function implementations.\n * See above for the function signatures and descriptions.\n *\n * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_listener.c for usage examples.\n */\ntypedef struct {\n    NfcListenerAlloc alloc; /**< Pointer to the alloc() function. */\n    NfcListenerFree free; /**< Pointer to the free() function. */\n    NfcListenerSetCallback set_callback; /**< Pointer to the set_callback() function. */\n    NfcListenerRun run; /**< Pointer to the run() function. */\n    NfcListenerGetData get_data; /**< Pointer to the get_data() function. */\n} NfcListenerBase;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_listener_defs.c",
    "content": "#include \"nfc_listener_defs.h\"\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h>\n#include <nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_listener_defs.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h>\n#include <nfc/protocols/mf_classic/mf_classic_listener_defs.h>\n#include <nfc/protocols/slix/slix_listener_defs.h>\n#include <nfc/protocols/felica/felica_listener_defs.h>\n\nconst NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a,\n    [NfcProtocolIso14443_3b] = NULL,\n    [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a,\n    [NfcProtocolIso14443_4b] = NULL,\n    [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3,\n    [NfcProtocolMfUltralight] = &mf_ultralight_listener,\n    [NfcProtocolMfClassic] = &mf_classic_listener,\n    [NfcProtocolMfDesfire] = NULL,\n    [NfcProtocolSlix] = &nfc_listener_slix,\n    [NfcProtocolSt25tb] = NULL,\n    [NfcProtocolFelica] = &nfc_listener_felica,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_listener_defs.h",
    "content": "#pragma once\n\n#include \"nfc_listener_base.h\"\n#include \"nfc_protocol.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum];\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_poller_base.h",
    "content": "/**\n * @file nfc_poller_base.h\n * @brief Abstract interface definitions for the NFC poller system.\n *\n * This file is an implementation detail. It must not be included in\n * any public API-related headers.\n *\n * @see nfc_poller.h\n *\n */\n#pragma once\n\n#include \"nfc_generic_event.h\"\n#include \"nfc_device_base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Allocate a protocol-specific poller instance.\n *\n * For base pollers pass a pointer to an instance of type Nfc\n * as the base_poller parameter, otherwise it must be a pointer to another poller instance\n * (compare iso14443_3a/iso14443_3a_poller.c and iso14443_4a/iso14443_4a_poller.c).\n *\n * @see nfc_protocol.c\n *\n * @param[in] base_poller pointer to the parent poller instance.\n * @returns pointer to the allocated poller instance.\n */\ntypedef NfcGenericInstance* (*NfcPollerAlloc)(NfcGenericInstance* base_poller);\n\n/**\n * @brief Delete a protocol-specific poller instance.\n *\n * @param[in,out] instance pointer to the instance to be deleted.\n */\ntypedef void (*NfcPollerFree)(NfcGenericInstance* instance);\n\n/**\n * @brief Set the callback function to handle events emitted by the poller instance.\n *\n * @see nfc_generic_event.h\n *\n * @param[in,out] poller pointer to the protocol-specific poller instance.\n * @param[in] callback pointer to the user-defined callback function which will receive events.\n * @param[in] context pointer to the user-specific context (will be passed to the callback).\n */\ntypedef void (\n    *NfcPollerSetCallback)(NfcGenericInstance* poller, NfcGenericCallback callback, void* context);\n\n/**\n * @brief Activate and read a supported NFC card.\n *\n * Ths function is passed to the parent poller's ${POLLER_NAME}_set_callback function as\n * the callback parameter. This is done automatically by the NfcPoller implementation based\n * on the protocol hierarchy defined in nfc_protocol.c, so there is no need to call it explicitly.\n *\n * Thus, it will be called each time the parent poller emits an event. Usually it happens\n * only after the parent poller has successfully completed its job.\n *\n * Example for an application reading a card with a compound (non-base) protocol (simplified):\n *\n * ```\n * start()   <--     set_callback()    <--    set_callback()     <--  nfc_poller_start()\n *            |                         |                         |\n *   Nfc      |        Base Poller      |      Child Poller       |      Application\n *            |                         |                         |\n * worker()  -->         run()         -->         run()         --->    handle_event()\n * ```\n *\n * The base poller receives events directly from an Nfc instance, from which they are\n * propagated as needed to however many other pollers there are in the current hierarchy.\n *\n * This function can be thought of as the poller's \"main loop\" function. Depending\n * on the particular poller implementation, it may perform actions such as reading\n * and writing to an NFC card, state changes and control of the parent poller.\n *\n * @see nfc_generic_event.h\n *\n * @param[in] event protocol-specific event passed by the parent poller instance.\n * @param[in,out] context pointer to the protocol-specific poller instance.\n * @returns command to be executed by the parent poller instance.\n */\ntypedef NfcCommand (*NfcPollerRun)(NfcGenericEvent event, void* context);\n\n/**\n * @brief Determine whether there is a supported card in the vicinity.\n *\n * The behaviour is mostly the same as of NfcPollerRun, with the difference in the\n * procedure and return value.\n * The procedure implemented in this function must do whatever it needs to unambigiously\n * determine whether a supported and valid NFC card is in the vicinity.\n *\n * Like the previously described NfcPollerRun, it is called automatically by the NfcPoller\n * implementation, so there is no need to call it explicitly.\n *\n * @param[in] event protocol-specific event passed by the parent poller instance.\n * @param[in,out] context pointer to the protocol-specific poller instance.\n * @returns true if a supported card was detected, false otherwise.\n */\ntypedef bool (*NfcPollerDetect)(NfcGenericEvent event, void* context);\n\n/**\n * @brief Get the data that was that was gathered during the reading process.\n *\n * @param[in] instance pointer to the protocol-specific poller instance.\n * @returns pointer to the NFC device data.\n */\ntypedef const NfcDeviceData* (*NfcPollerGetData)(const NfcGenericInstance* instance);\n\n/**\n * @brief Generic NFC poller interface.\n *\n * Each protocol must fill this structure with its own function implementations.\n * See above for the function signatures and descriptions.\n *\n * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_poller.c for usage examples.\n */\ntypedef struct {\n    NfcPollerAlloc alloc; /**< Pointer to the alloc() function. */\n    NfcPollerFree free; /**< Pointer to the free() function. */\n    NfcPollerSetCallback set_callback; /**< Pointer to the set_callback() function. */\n    NfcPollerRun run; /**< Pointer to the run() function. */\n    NfcPollerDetect detect; /**< Pointer to the detect() function. */\n    NfcPollerGetData get_data; /**< Pointer to the get_data() function. */\n} NfcPollerBase;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_poller_defs.c",
    "content": "#include \"nfc_poller_defs.h\"\n\n#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h>\n#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h>\n#include <nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h>\n#include <nfc/protocols/iso14443_4b/iso14443_4b_poller_defs.h>\n#include <nfc/protocols/iso15693_3/iso15693_3_poller_defs.h>\n#include <nfc/protocols/felica/felica_poller_defs.h>\n#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h>\n#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>\n#include <nfc/protocols/mf_plus/mf_plus_poller_defs.h>\n#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>\n#include <nfc/protocols/slix/slix_poller_defs.h>\n#include <nfc/protocols/st25tb/st25tb_poller_defs.h>\n\nconst NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a,\n    [NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b,\n    [NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a,\n    [NfcProtocolIso14443_4b] = &nfc_poller_iso14443_4b,\n    [NfcProtocolIso15693_3] = &nfc_poller_iso15693_3,\n    [NfcProtocolFelica] = &nfc_poller_felica,\n    [NfcProtocolMfUltralight] = &mf_ultralight_poller,\n    [NfcProtocolMfClassic] = &mf_classic_poller,\n    [NfcProtocolMfPlus] = &mf_plus_poller,\n    [NfcProtocolMfDesfire] = &mf_desfire_poller,\n    [NfcProtocolSlix] = &nfc_poller_slix,\n    /* Add new pollers here */\n    [NfcProtocolSt25tb] = &nfc_poller_st25tb,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_poller_defs.h",
    "content": "#pragma once\n\n#include \"nfc_poller_base.h\"\n#include \"nfc_protocol.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum];\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_protocol.c",
    "content": "/**\n * @file nfc_protocol.c\n * @brief Main protocol hierarchy definitions.\n *\n * To reduce code duplication, all NFC protocols are described as a tree, whose\n * structure is shown in the diagram below. The (Start) node is actually\n * nonexistent and is there only for clarity.\n *\n * All its child protocols are considered base protocols, which in turn serve\n * as parents to other, usually vendor-specific ones.\n *\n * ```\n * **************************** Protocol tree structure ***************************\n *\n *                                                  (Start)\n *                                                     |\n *                            +------------------------+-----------+---------+------------+\n *                            |                        |           |         |            |\n *                       ISO14443-3A              ISO14443-3B    Felica  ISO15693-3    ST25TB\n *                            |                        |                     |\n *            +---------------+-------------+     ISO14443-4B              SLIX\n *            |               |             |\n *       ISO14443-4A   Mf Ultralight   Mf Classic\n *            |\n *       Mf Desfire\n * ```\n *\n * When implementing a new protocol, its place in the tree must be determined first.\n * If no appropriate base protocols exists, then it must be a base protocol itself.\n *\n * This file is to be modified upon adding a new protocol (see below).\n *\n */\n#include \"nfc_protocol.h\"\n\n#include <furi/furi.h>\n\n/**\n * @brief Tree node describing a protocol.\n *\n * All base protocols (see above) have NfcProtocolInvalid\n * in the parent_protocol field.\n */\ntypedef struct {\n    NfcProtocol parent_protocol; /**< Parent protocol identifier. */\n    size_t children_num; /** < Number of the child protocols. */\n    const NfcProtocol* children_protocol; /**< Pointer to an array of child protocol identifiers. */\n} NfcProtocolTreeNode;\n\n/** List of ISO14443-3A child protocols. */\nstatic const NfcProtocol nfc_protocol_iso14443_3a_children_protocol[] = {\n    NfcProtocolIso14443_4a,\n    NfcProtocolMfUltralight,\n};\n\n/** List of ISO14443-3B child protocols. */\nstatic const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {\n    NfcProtocolIso14443_4b,\n};\n\n/** List of ISO14443-4A child protocols. */\nstatic const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {\n    NfcProtocolMfDesfire,\n    NfcProtocolMfPlus,\n};\n\n/** List of ISO115693-3 child protocols. */\nstatic const NfcProtocol nfc_protocol_iso15693_3_children_protocol[] = {\n    NfcProtocolSlix,\n};\n\n/* Add new child protocol lists here (if necessary) */\n\n/**\n * @brief Flattened description of the NFC protocol tree.\n *\n * When implementing a new protocol, add the node here under its\n * own index defined in nfc_protocol.h.\n *\n * Additionally, if it has an already implemented protocol as a parent,\n * add its identifier to its respective list of child protocols (see above).\n */\nstatic const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {\n    [NfcProtocolIso14443_3a] =\n        {\n            .parent_protocol = NfcProtocolInvalid,\n            .children_num = COUNT_OF(nfc_protocol_iso14443_3a_children_protocol),\n            .children_protocol = nfc_protocol_iso14443_3a_children_protocol,\n        },\n    [NfcProtocolIso14443_3b] =\n        {\n            .parent_protocol = NfcProtocolInvalid,\n            .children_num = COUNT_OF(nfc_protocol_iso14443_3b_children_protocol),\n            .children_protocol = nfc_protocol_iso14443_3b_children_protocol,\n        },\n    [NfcProtocolIso14443_4a] =\n        {\n            .parent_protocol = NfcProtocolIso14443_3a,\n            .children_num = COUNT_OF(nfc_protocol_iso14443_4a_children_protocol),\n            .children_protocol = nfc_protocol_iso14443_4a_children_protocol,\n        },\n    [NfcProtocolIso14443_4b] =\n        {\n            .parent_protocol = NfcProtocolIso14443_3b,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolIso15693_3] =\n        {\n            .parent_protocol = NfcProtocolInvalid,\n            .children_num = COUNT_OF(nfc_protocol_iso15693_3_children_protocol),\n            .children_protocol = nfc_protocol_iso15693_3_children_protocol,\n        },\n    [NfcProtocolFelica] =\n        {\n            .parent_protocol = NfcProtocolInvalid,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolMfUltralight] =\n        {\n            .parent_protocol = NfcProtocolIso14443_3a,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolMfClassic] =\n        {\n            .parent_protocol = NfcProtocolIso14443_3a,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolMfPlus] =\n        {\n            .parent_protocol = NfcProtocolIso14443_4a,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolMfDesfire] =\n        {\n            .parent_protocol = NfcProtocolIso14443_4a,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolSlix] =\n        {\n            .parent_protocol = NfcProtocolIso15693_3,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    [NfcProtocolSt25tb] =\n        {\n            .parent_protocol = NfcProtocolInvalid,\n            .children_num = 0,\n            .children_protocol = NULL,\n        },\n    /* Add new protocols here */\n};\n\nNfcProtocol nfc_protocol_get_parent(NfcProtocol protocol) {\n    furi_check(protocol < NfcProtocolNum);\n\n    return nfc_protocol_nodes[protocol].parent_protocol;\n}\n\nbool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol) {\n    furi_check(protocol < NfcProtocolNum);\n    furi_check(parent_protocol < NfcProtocolNum);\n\n    bool parent_found = false;\n    const NfcProtocolTreeNode* iter = &nfc_protocol_nodes[protocol];\n\n    while(iter->parent_protocol != NfcProtocolInvalid) {\n        if(iter->parent_protocol == parent_protocol) {\n            parent_found = true;\n            break;\n        }\n        iter = &nfc_protocol_nodes[iter->parent_protocol];\n    }\n\n    return parent_found;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/nfc_protocol.h",
    "content": "/**\n * @file nfc_protocol.h\n * @brief Top-level NFC protocol definitions.\n *\n * This file is to be modified upon adding a new protocol (see below).\n *\n * # How to add a new NFC protocol\n *\n * ## 1. Gather information\n *\n * Having proper protocol documentation would be ideal, although they are often available only for a fee, or given under an NDA.\n * As an alternative, reading code from relevant open-source projects or notes gathered by means of reverse engineering will do.\n *\n * ### 1.1 Technology\n *\n * Check whether the NFC technology required for the protocol is supported. If no support exists, adding the protocol may\n * be problematic, since it is highly hardware-dependent.\n *\n * @see NfcTech for the enumeration of supported NFC technologies.\n *\n * ### 1.2 Base protocols\n *\n * Check whether the protocol to be implemented is built on top of some already supported protocol.\n *\n * @see NfcProtocol for the enumeration of supported NFC protocols.\n *\n * If the answer is \"yes\", then the protocol to be implemented is a child protocol. If no, then it will become a base protocol.\n * Sometimes it will be necessary to implement both, e.g. when the target protocol is built on top of a base one,\n * but the latter is currently not supported.\n *\n * ## 2. Create the files\n *\n * ### 2.1 Recommended file structure\n *\n * The recommended file structure for a protocol is as follows:\n *\n * ```text\n * protocols\n *     |\n *     +- protocol_name\n *             |\n *             +- protocol_name.h\n *             |\n *             +- protocol_name.c\n *             |\n *             +- protocol_name_device_defs.h\n *             |\n *             +- protocol_name_poller.h\n *             |\n *             +- protocol_name_poller.c\n *             |\n *             +- protocol_name_poller_defs.h\n *             |\n *             .\n *             . (files below are optional)\n *             .\n *             |\n *             +- protocol_name_listener.h       |\n *             |                                 |\n *             +- protocol_name_listener.c       |- add for emulation support\n *             |                                 |\n *             +- protocol_name_listener_defs.h  |\n *             |\n *             +- protocol_name_sync.h           |\n *             |                                 |- add for synchronous API support\n *             +- protocol_name_sync.c           |\n *             |\n * ```\n *\n * Additionally, an arbitrary amount of private `protocol_name_*_i.h` header files may be created. Do not put implementation\n * details in the regular header files, as they will be exposed in the public firmware API later on.\n *\n * ### 2.2 File structure explanation\n *\n * | Filename                      | Explanation |\n * |:------------------------------|:------------|\n * | protocol_name.h               | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. |\n * | protocol_name.c               | Implementations of functions declared in `protocol_name.h`. |\n * | protocol_name_device_defs.h   | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. |\n * | protocol_name_poller.h        | Protocol-specific poller and associated functions declarations. |\n * | protocol_name_poller.c        | Implementation of functions declared in `protocol_name_poller.h`. |\n * | protocol_name_poller_defs.h   | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. |\n * | protocol_name_listener.h      | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. |\n * | protocol_name_listener.c      | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. |\n * | protocol_name_listener_defs.h | Declarations for use by the NfcListener library. See nfc_listener_base.h for more info. Optional, needed for emulation support. |\n * | protocol_name_sync.h          | Synchronous API declarations. (See below for sync API explanation). Optional.|\n * | protocol_name_sync.c          | Synchronous API implementation. Optional. |\n *\n * ## 3 Implement the code\n *\n * ### 3.1 Protocol data structure\n *\n * A protocol data structure is what holds all data that can be possibly read from a card of a certain type. It may include a unique identifier (UID),\n * configuration bytes and flags, built-in memory, and so on.\n * Additionally, it must implement the NfcDevice interface so that it could be used by the firmware.\n *\n * @see nfc_device_base_i.h for the device interface description.\n *\n * @note It is strongly recommended to implement such a structure as an opaque type and access it via specialised methods only.\n *\n * If the protocol being implemented is a child protocol, then its data must include a pointer to the parent protocol data structure.\n * It is the protocol's responsibility to correctly create and delete the instance the pointer is pointing to.\n *\n * ### 3.2 Poller (reading functionality)\n *\n * A poller contains the functions necessary to successfully read a card of supported type. It must also implement a specific interface,\n * namely described by the NfcPollerBase type.\n *\n * Upon creation, a poller instance will receive either a pointer to the Nfc instance (if it's a base protocol), or a pointer to another poller\n * instance (if it is a child protocol) as the `alloc()` parameter.\n *\n * @see nfc_poller_base.h for the poller interface description.\n *\n * ### 3.3 Listener (emulation functionality)\n *\n * A listener implementation is optional, needed only when emulation is required/possible.\n *\n * Same considerations apply to the listener as for the poller. Additionally, upon creation it will receive an additional parameter\n * in the form of a pointer to the matching protocol data structure, which will be used during emulation. The listener instance\n * does not own this data and does not need to worry about its deletion.\n *\n * @see nfc_listener_base.h for the listener interface description.\n *\n * ### 3.4 Synchronous API\n *\n * Synchronous API does exaclty what it says -- it provides a set of blocking functions for easier use of pollers.\n * Instead of creating all necessary instances and setting up callbacks manually, it does it automatically, at the\n * expense of some flexibility.\n *\n * The most notable use of sync API is in the supported card plugins, so it's a good idea to implement it if such a plugin\n * is to be implemented afterwards.\n *\n * ### 3.5 Registering the protocol\n *\n * After completing the protocol, it must be registered within the NfcProtocol system in order for it to be usable.\n *\n * 1. In nfc_protocol.h, add a new entry in the NfcProtocol enum in the form of NfcProtocolProtocolName.\n * 2. In nfc_protocol.c, add a new entry in the `nfc_protocol_nodes[]` array under the appropriate index.\n *  1. If it is a child protocol, register it as a child in the respective `nfc_base_protocol_name_children_protocol[]` array.\n *  2. If the protocol has children on its own, create a `nfc_protocol_name_children_protocol[]` array\n *     with respective identifiers and register it in the protocol entry added in step 2.\n * 3. In nfc_device_defs.c, include `protocol_name_device_defs.h` and add a pointer to the\n *    `protocol_name_device_defs` structure under the appropriate index.\n * 4. In nfc_poller_defs.c, include `protocol_name_poller_defs.h` and add a pointer to the\n *    `protocol_name_poller_defs` structure under the appropriate index.\n * 5. (Optional) If emulation support was implemented, do the step 4, but for the listener.\n * 6. Add `protocol_name.h`, `protocol_name_poller.h`, and optionally, `protocol_name_listener.h`\n *    and `protocol_name_sync.h` into the `SDK_HEADERS` list in the SConscript file.\n *    This will export the protocol's types and functions for use by the applications.\n * 7. Done!\n *\n * ## What's next?\n *\n * It's about time to integrate the newly implemented protocol into the main NFC application. Without that, reading a card\n * of this type would crash it.\n *\n * @see nfc_protocol_support.h for more information on protocol integration.\n *\n * After having done that, a supported card plugin may be implemented to take further advantage of the new protocol.\n *\n * @see nfc_supported_card_plugin.h for more information on supported card plugins.\n *\n */\n#pragma once\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Enumeration of all available NFC protocols.\n *\n * When implementing a new protocol, add its identifier before the\n * NfcProtocolNum entry.\n */\ntypedef enum {\n    NfcProtocolIso14443_3a,\n    NfcProtocolIso14443_3b,\n    NfcProtocolIso14443_4a,\n    NfcProtocolIso14443_4b,\n    NfcProtocolIso15693_3,\n    NfcProtocolFelica,\n    NfcProtocolMfUltralight,\n    NfcProtocolMfClassic,\n    NfcProtocolMfPlus,\n    NfcProtocolMfDesfire,\n    NfcProtocolSlix,\n    NfcProtocolSt25tb,\n    /* Add new protocols here */\n\n    NfcProtocolNum, /**< Special value representing the number of available protocols. */\n\n    NfcProtocolInvalid, /**< Special value representing an invalid state. */\n} NfcProtocol;\n\n/**\n * @brief Get the immediate parent of a specific protocol.\n *\n * @param[in] protocol identifier of the protocol in question.\n * @returns parent protocol identifier if it has one, or NfcProtocolInvalid otherwise.\n */\nNfcProtocol nfc_protocol_get_parent(NfcProtocol protocol);\n\n/**\n * @brief Determine if a specific protocol has a parent on an arbitrary level.\n *\n * Unlike nfc_protocol_get_parent(), this function will traverse the full protocol hierarchy\n * and check each parent node for the matching protocol type.\n *\n * @param[in] protocol identifier of the protocol in question.\n * @param[in] parent_protocol identifier of the parent protocol in question.\n * @returns true if the parent of given type exists, false otherwise.\n */\nbool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix.c",
    "content": "#include \"slix_i.h\"\n#include \"slix_device_defs.h\"\n\n#include <furi.h>\n#include <nfc/nfc_common.h>\n\n#define SLIX_PROTOCOL_NAME \"SLIX\"\n#define SLIX_DEVICE_NAME   \"SLIX\"\n\n#define SLIX_TYPE_SLIX_SLIX2 (0x01U)\n#define SLIX_TYPE_SLIX_S     (0x02U)\n#define SLIX_TYPE_SLIX_L     (0x03U)\n\n#define SLIX_TYPE_INDICATOR_SLIX  (0x02U)\n#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U)\n\n#define SLIX_CAPABILITIES_KEY         \"Capabilities\"\n#define SLIX_PASSWORD_READ_KEY        \"Password Read\"\n#define SLIX_PASSWORD_WRITE_KEY       \"Password Write\"\n#define SLIX_PASSWORD_PRIVACY_KEY     \"Password Privacy\"\n#define SLIX_PASSWORD_DESTROY_KEY     \"Password Destroy\"\n#define SLIX_PASSWORD_EAS_KEY         \"Password EAS\"\n#define SLIX_SIGNATURE_KEY            \"Signature\"\n#define SLIX_PRIVACY_MODE_KEY         \"Privacy Mode\"\n#define SLIX_PROTECTION_POINTER_KEY   \"Protection Pointer\"\n#define SLIX_PROTECTION_CONDITION_KEY \"Protection Condition\"\n#define SLIX_LOCK_EAS_KEY             \"Lock EAS\"\n#define SLIX_LOCK_PPL_KEY             \"Lock PPL\"\n\ntypedef struct {\n    uint8_t iso15693_3[2];\n    uint8_t icode_type;\n    union {\n        struct {\n            uint8_t unused_1       : 3;\n            uint8_t type_indicator : 2;\n            uint8_t unused_2       : 3;\n        };\n        uint8_t serial_num[5];\n    };\n} SlixUidLayout;\n\nconst NfcDeviceBase nfc_device_slix = {\n    .protocol_name = SLIX_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)slix_alloc,\n    .free = (NfcDeviceFree)slix_free,\n    .reset = (NfcDeviceReset)slix_reset,\n    .copy = (NfcDeviceCopy)slix_copy,\n    .verify = (NfcDeviceVerify)slix_verify,\n    .load = (NfcDeviceLoad)slix_load,\n    .save = (NfcDeviceSave)slix_save,\n    .is_equal = (NfcDeviceEqual)slix_is_equal,\n    .get_name = (NfcDeviceGetName)slix_get_device_name,\n    .get_uid = (NfcDeviceGetUid)slix_get_uid,\n    .set_uid = (NfcDeviceSetUid)slix_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)slix_get_base_data,\n};\n\nstatic const char* slix_nfc_device_name[] = {\n    [SlixTypeSlix] = SLIX_DEVICE_NAME,\n    [SlixTypeSlixS] = SLIX_DEVICE_NAME \"-S\",\n    [SlixTypeSlixL] = SLIX_DEVICE_NAME \"-L\",\n    [SlixTypeSlix2] = SLIX_DEVICE_NAME \"2\",\n};\n\nstatic const SlixTypeFeatures slix_type_features[] = {\n    [SlixTypeSlix] = SLIX_TYPE_FEATURES_SLIX,\n    [SlixTypeSlixS] = SLIX_TYPE_FEATURES_SLIX_S,\n    [SlixTypeSlixL] = SLIX_TYPE_FEATURES_SLIX_L,\n    [SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2,\n};\n\nstatic const char* slix_capabilities_names[SlixCapabilitiesCount] = {\n    [SlixCapabilitiesDefault] = \"Default\",\n    [SlixCapabilitiesAcceptAllPasswords] = \"AcceptAllPasswords\",\n};\n\ntypedef struct {\n    const char* key;\n    SlixTypeFeatures feature_flag;\n    SlixPassword default_value;\n} SlixPasswordConfig;\n\nstatic const SlixPasswordConfig slix_password_configs[] = {\n    [SlixPasswordTypeRead] = {SLIX_PASSWORD_READ_KEY, SLIX_TYPE_FEATURE_READ, 0x00000000U},\n    [SlixPasswordTypeWrite] = {SLIX_PASSWORD_WRITE_KEY, SLIX_TYPE_FEATURE_WRITE, 0x00000000U},\n    [SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0x0F0F0F0FU},\n    [SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0x0F0F0F0FU},\n    [SlixPasswordTypeEasAfi] = {SLIX_PASSWORD_EAS_KEY, SLIX_TYPE_FEATURE_EAS, 0x00000000U},\n};\n\nstatic void slix_password_set_defaults(SlixPassword* passwords) {\n    for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {\n        passwords[i] = slix_password_configs[i].default_value;\n    }\n}\n\nSlixData* slix_alloc(void) {\n    SlixData* data = malloc(sizeof(SlixData));\n\n    data->iso15693_3_data = iso15693_3_alloc();\n    slix_password_set_defaults(data->passwords);\n\n    return data;\n}\n\nvoid slix_free(SlixData* data) {\n    furi_check(data);\n\n    iso15693_3_free(data->iso15693_3_data);\n\n    free(data);\n}\n\nvoid slix_reset(SlixData* data) {\n    furi_check(data);\n\n    iso15693_3_reset(data->iso15693_3_data);\n    data->capabilities = SlixCapabilitiesDefault;\n    slix_password_set_defaults(data->passwords);\n\n    memset(&data->system_info, 0, sizeof(SlixSystemInfo));\n    memset(data->signature, 0, sizeof(SlixSignature));\n\n    data->privacy = false;\n}\n\nvoid slix_copy(SlixData* data, const SlixData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);\n    data->capabilities = other->capabilities;\n\n    memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount);\n    memcpy(data->signature, other->signature, sizeof(SlixSignature));\n\n    data->system_info = other->system_info;\n    data->privacy = other->privacy;\n}\n\nbool slix_verify(SlixData* data, const FuriString* device_type) {\n    UNUSED(data);\n    UNUSED(device_type);\n    // No backward compatibility, unified format only\n    return false;\n}\n\nstatic bool slix_load_capabilities(SlixData* data, FlipperFormat* ff) {\n    bool capabilities_loaded = false;\n    FuriString* capabilities_str = furi_string_alloc();\n\n    if(!flipper_format_read_string(ff, SLIX_CAPABILITIES_KEY, capabilities_str)) {\n        if(flipper_format_rewind(ff)) {\n            data->capabilities = SlixCapabilitiesDefault;\n            capabilities_loaded = true;\n        }\n    } else {\n        for(size_t i = 0; i < COUNT_OF(slix_capabilities_names); i++) {\n            if(furi_string_cmp_str(capabilities_str, slix_capabilities_names[i]) == 0) {\n                data->capabilities = i;\n                capabilities_loaded = true;\n                break;\n            }\n        }\n    }\n\n    furi_string_free(capabilities_str);\n\n    return capabilities_loaded;\n}\n\nstatic bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {\n    bool ret = true;\n\n    for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {\n        const SlixPasswordConfig* password_config = &slix_password_configs[i];\n\n        if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue;\n        if(!flipper_format_key_exist(ff, password_config->key)) {\n            passwords[i] = password_config->default_value;\n            continue;\n        }\n        if(!flipper_format_read_hex(\n               ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) {\n            ret = false;\n            break;\n        }\n    }\n\n    return ret;\n}\n\nbool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool loaded = false;\n    do {\n        if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break;\n\n        const SlixType slix_type = slix_get_type(data);\n        if(slix_type >= SlixTypeCount) break;\n\n        if(!slix_load_capabilities(data, ff)) break;\n\n        if(!slix_load_passwords(data->passwords, slix_type, ff)) break;\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {\n            if(flipper_format_key_exist(ff, SLIX_SIGNATURE_KEY)) {\n                if(!flipper_format_read_hex(\n                       ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE))\n                    break;\n            }\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {\n            if(flipper_format_key_exist(ff, SLIX_PRIVACY_MODE_KEY)) {\n                if(!flipper_format_read_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break;\n            }\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n            SlixProtection* protection = &data->system_info.protection;\n            if(flipper_format_key_exist(ff, SLIX_PROTECTION_POINTER_KEY) &&\n               flipper_format_key_exist(ff, SLIX_PROTECTION_CONDITION_KEY)) {\n                if(!flipper_format_read_hex(\n                       ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, 1))\n                    break;\n                if(!flipper_format_read_hex(\n                       ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, 1))\n                    break;\n            }\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {\n            if(flipper_format_key_exist(ff, SLIX_LOCK_EAS_KEY)) {\n                SlixLockBits* lock_bits = &data->system_info.lock_bits;\n                if(!flipper_format_read_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break;\n            }\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n            if(flipper_format_key_exist(ff, SLIX_LOCK_PPL_KEY)) {\n                SlixLockBits* lock_bits = &data->system_info.lock_bits;\n                if(!flipper_format_read_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break;\n            }\n        }\n\n        loaded = true;\n    } while(false);\n\n    return loaded;\n}\n\nstatic bool slix_save_capabilities(const SlixData* data, FlipperFormat* ff) {\n    bool save_success = false;\n\n    FuriString* tmp_str = furi_string_alloc();\n    do {\n        furi_string_set_str(\n            tmp_str, \"SLIX capabilities field affects emulation modes. Possible options: \");\n        for(size_t i = 0; i < SlixCapabilitiesCount; i++) {\n            furi_string_cat_str(tmp_str, slix_capabilities_names[i]);\n            if(i < SlixCapabilitiesCount - 1) {\n                furi_string_cat(tmp_str, \", \");\n            }\n        }\n        if(!flipper_format_write_comment_cstr(ff, furi_string_get_cstr(tmp_str))) break;\n\n        if(!flipper_format_write_string_cstr(\n               ff, SLIX_CAPABILITIES_KEY, slix_capabilities_names[data->capabilities]))\n            break;\n\n        save_success = true;\n    } while(false);\n\n    furi_string_free(tmp_str);\n\n    return save_success;\n}\n\nstatic bool\n    slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {\n    bool ret = true;\n\n    for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {\n        const SlixPasswordConfig* password_config = &slix_password_configs[i];\n\n        if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue;\n        if(!flipper_format_write_hex(\n               ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) {\n            ret = false;\n            break;\n        }\n    }\n\n    return ret;\n}\n\nbool slix_save(const SlixData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool saved = false;\n\n    do {\n        const SlixType slix_type = slix_get_type(data);\n        if(slix_type >= SlixTypeCount) break;\n\n        if(!iso15693_3_save(data->iso15693_3_data, ff)) break;\n        if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME \" specific data\")) break;\n\n        if(!slix_save_capabilities(data, ff)) break;\n\n        if(!flipper_format_write_comment_cstr(\n               ff,\n               \"Passwords are optional. If a password is omitted, a default value will be used\"))\n            break;\n\n        if(!slix_save_passwords(data->passwords, slix_type, ff)) break;\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {\n            if(!flipper_format_write_comment_cstr(\n                   ff,\n                   \"This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.\"))\n                break;\n            if(!flipper_format_write_hex(\n                   ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE))\n                break;\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {\n            if(!flipper_format_write_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break;\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n            const SlixProtection* protection = &data->system_info.protection;\n            if(!flipper_format_write_comment_cstr(ff, \"Protection pointer configuration\")) break;\n            if(!flipper_format_write_hex(\n                   ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, sizeof(uint8_t)))\n                break;\n            if(!flipper_format_write_hex(\n                   ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, sizeof(uint8_t)))\n                break;\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS) ||\n           slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n            if(!flipper_format_write_comment_cstr(ff, \"SLIX Lock Bits\")) break;\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {\n            const SlixLockBits* lock_bits = &data->system_info.lock_bits;\n            if(!flipper_format_write_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break;\n        }\n\n        if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {\n            const SlixLockBits* lock_bits = &data->system_info.lock_bits;\n            if(!flipper_format_write_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break;\n        }\n\n        saved = true;\n    } while(false);\n\n    return saved;\n}\n\nbool slix_is_equal(const SlixData* data, const SlixData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return iso15693_3_is_equal(data->iso15693_3_data, other->iso15693_3_data) &&\n           memcmp(&data->system_info, &other->system_info, sizeof(SlixSystemInfo)) == 0 &&\n           memcmp(\n               data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount) ==\n               0 &&\n           memcmp(&data->signature, &other->signature, sizeof(SlixSignature)) == 0 &&\n           data->privacy == other->privacy;\n}\n\nconst char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n    UNUSED(name_type);\n\n    const SlixType slix_type = slix_get_type(data);\n    furi_assert(slix_type < SlixTypeCount);\n\n    return slix_nfc_device_name[slix_type];\n}\n\nconst uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len) {\n    furi_check(data);\n    return iso15693_3_get_uid(data->iso15693_3_data, uid_len);\n}\n\nbool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n\n    return iso15693_3_set_uid(data->iso15693_3_data, uid, uid_len);\n}\n\nconst Iso15693_3Data* slix_get_base_data(const SlixData* data) {\n    furi_check(data);\n\n    return data->iso15693_3_data;\n}\n\nSlixType slix_get_type(const SlixData* data) {\n    furi_check(data);\n\n    SlixType type = SlixTypeUnknown;\n\n    do {\n        if(iso15693_3_get_manufacturer_id(data->iso15693_3_data) != SLIX_NXP_MANUFACTURER_CODE)\n            break;\n\n        const SlixUidLayout* uid = (const SlixUidLayout*)data->iso15693_3_data->uid;\n\n        if(uid->icode_type == SLIX_TYPE_SLIX_SLIX2) {\n            if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX) {\n                type = SlixTypeSlix;\n            } else if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX2) {\n                type = SlixTypeSlix2;\n            }\n        } else if(uid->icode_type == SLIX_TYPE_SLIX_S) {\n            type = SlixTypeSlixS;\n        } else if(uid->icode_type == SLIX_TYPE_SLIX_L) {\n            type = SlixTypeSlixL;\n        }\n\n    } while(false);\n\n    return type;\n}\n\nSlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type) {\n    furi_check(data);\n    furi_check(password_type < SlixPasswordTypeCount);\n\n    return data->passwords[password_type];\n}\n\nuint16_t slix_get_counter(const SlixData* data) {\n    furi_check(data);\n    const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(\n        data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);\n\n    return counter->value;\n}\n\nbool slix_is_privacy_mode(const SlixData* data) {\n    furi_check(data);\n\n    return data->privacy;\n}\n\nbool slix_is_block_protected(\n    const SlixData* data,\n    SlixPasswordType password_type,\n    uint8_t block_num) {\n    furi_check(data);\n    furi_check(password_type < SlixPasswordTypeCount);\n\n    bool ret = false;\n\n    do {\n        if(password_type != SlixPasswordTypeRead && password_type != SlixPasswordTypeWrite) break;\n        if(block_num >= iso15693_3_get_block_count(data->iso15693_3_data)) break;\n        if(block_num == SLIX_COUNTER_BLOCK_NUM) break;\n\n        const bool high = block_num >= data->system_info.protection.pointer;\n        const bool read = password_type == SlixPasswordTypeRead;\n\n        const uint8_t condition = high ? (read ? SLIX_PP_CONDITION_RH : SLIX_PP_CONDITION_WH) :\n                                         (read ? SLIX_PP_CONDITION_RL : SLIX_PP_CONDITION_WL);\n\n        ret = data->system_info.protection.condition & condition;\n    } while(false);\n\n    return ret;\n}\n\nbool slix_is_counter_increment_protected(const SlixData* data) {\n    furi_check(data);\n\n    const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(\n        data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);\n\n    return counter->protection != 0;\n}\n\nbool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features) {\n    furi_check(slix_type < SlixTypeCount);\n\n    return (slix_type_features[slix_type] & features) == features;\n}\n\nbool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type) {\n    furi_check(slix_type < SlixTypeCount);\n    furi_check(password_type < SlixPasswordTypeCount);\n\n    return slix_type_features[slix_type] & slix_password_configs[password_type].feature_flag;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso15693_3/iso15693_3.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define SLIX_BLOCK_SIZE     (4U)\n#define SLIX_SIGNATURE_SIZE (32U)\n\n#define SLIX_COUNTER_BLOCK_NUM (79U)\n\n#define SLIX_PP_CONDITION_RL (1U << 0)\n#define SLIX_PP_CONDITION_WL (1U << 1)\n#define SLIX_PP_CONDITION_RH (1U << 4)\n#define SLIX_PP_CONDITION_WH (1U << 5)\n\n#define SLIX_FEATURE_FLAG_UM_PP              (1UL << 0)\n#define SLIX_FEATURE_FLAG_COUNTER            (1UL << 1)\n#define SLIX_FEATURE_FLAG_EAS_ID             (1UL << 2)\n#define SLIX_FEATURE_FLAG_EAS_PP             (1UL << 3)\n#define SLIX_FEATURE_FLAG_AFI_PP             (1UL << 4)\n#define SLIX_FEATURE_FLAG_INVENTORY_READ_EXT (1UL << 5)\n#define SLIX_FEATURE_FLAG_EAS_IR             (1UL << 6)\n#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG    (1UL << 8)\n#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG_PP (1UL << 9)\n#define SLIX_FEATURE_FLAG_PERSISTENT_QUIET   (1UL << 10)\n#define SLIX_FEATURE_FLAG_PRIVACY            (1UL << 12)\n#define SLIX_FEATURE_FLAG_DESTROY            (1UL << 13)\n#define SLIX_FEATURE_EXT                     (1UL << 31)\n\n#define SLIX_TYPE_FEATURE_READ            (1U << 0)\n#define SLIX_TYPE_FEATURE_WRITE           (1U << 1)\n#define SLIX_TYPE_FEATURE_PRIVACY         (1U << 2)\n#define SLIX_TYPE_FEATURE_DESTROY         (1U << 3)\n#define SLIX_TYPE_FEATURE_EAS             (1U << 4)\n#define SLIX_TYPE_FEATURE_SIGNATURE       (1U << 5)\n#define SLIX_TYPE_FEATURE_PROTECTION      (1U << 6)\n#define SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO (1U << 7)\n\ntypedef uint32_t SlixTypeFeatures;\n\ntypedef enum {\n    SlixErrorNone,\n    SlixErrorTimeout,\n    SlixErrorFormat,\n    SlixErrorNotSupported,\n    SlixErrorInternal,\n    SlixErrorWrongPassword,\n    SlixErrorUidMismatch,\n    SlixErrorUnknown,\n} SlixError;\n\ntypedef enum {\n    SlixTypeSlix,\n    SlixTypeSlixS,\n    SlixTypeSlixL,\n    SlixTypeSlix2,\n\n    SlixTypeCount,\n    SlixTypeUnknown,\n} SlixType;\n\ntypedef enum {\n    SlixPasswordTypeRead,\n    SlixPasswordTypeWrite,\n    SlixPasswordTypePrivacy,\n    SlixPasswordTypeDestroy,\n    SlixPasswordTypeEasAfi,\n    SlixPasswordTypeCount,\n} SlixPasswordType;\n\ntypedef uint32_t SlixPassword;\ntypedef uint8_t SlixSignature[SLIX_SIGNATURE_SIZE];\ntypedef bool SlixPrivacy;\ntypedef uint16_t SlixRandomNumber;\n\ntypedef struct {\n    uint8_t pointer;\n    uint8_t condition;\n} SlixProtection;\n\ntypedef struct {\n    bool eas;\n    bool ppl;\n} SlixLockBits;\n\ntypedef struct {\n    SlixProtection protection;\n    SlixLockBits lock_bits;\n} SlixSystemInfo;\n\ntypedef enum {\n    SlixCapabilitiesDefault,\n    SlixCapabilitiesAcceptAllPasswords,\n\n    SlixCapabilitiesCount,\n} SlixCapabilities;\n\ntypedef struct {\n    Iso15693_3Data* iso15693_3_data;\n    SlixSystemInfo system_info;\n    SlixSignature signature;\n    SlixPassword passwords[SlixPasswordTypeCount];\n    SlixPrivacy privacy;\n    SlixCapabilities capabilities;\n} SlixData;\n\nSlixData* slix_alloc(void);\n\nvoid slix_free(SlixData* data);\n\nvoid slix_reset(SlixData* data);\n\nvoid slix_copy(SlixData* data, const SlixData* other);\n\nbool slix_verify(SlixData* data, const FuriString* device_type);\n\nbool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version);\n\nbool slix_save(const SlixData* data, FlipperFormat* ff);\n\nbool slix_is_equal(const SlixData* data, const SlixData* other);\n\nconst char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len);\n\nbool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len);\n\nconst Iso15693_3Data* slix_get_base_data(const SlixData* data);\n\n// Getters and tests\n\nSlixType slix_get_type(const SlixData* data);\n\nSlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type);\n\nuint16_t slix_get_counter(const SlixData* data);\n\nbool slix_is_privacy_mode(const SlixData* data);\n\nbool slix_is_block_protected(\n    const SlixData* data,\n    SlixPasswordType password_type,\n    uint8_t block_num);\n\nbool slix_is_counter_increment_protected(const SlixData* data);\n\n// Static methods\nbool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features);\n\nbool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_device_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\nextern const NfcDeviceBase nfc_device_slix;\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_i.c",
    "content": "#include \"slix_i.h\"\n\n#include <nfc/protocols/iso15693_3/iso15693_3_i.h>\n\nbool slix_error_response_parse(SlixError* error, const BitBuffer* buf) {\n    Iso15693_3Error iso15693_3_error;\n    const bool ret = iso15693_3_error_response_parse(&iso15693_3_error, buf);\n\n    if(ret) {\n        *error = slix_process_iso15693_3_error(iso15693_3_error);\n    }\n\n    return ret;\n}\n\nSlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error) {\n    switch(iso15693_3_error) {\n    case Iso15693_3ErrorNone:\n        return SlixErrorNone;\n    case Iso15693_3ErrorTimeout:\n        return SlixErrorTimeout;\n    case Iso15693_3ErrorFormat:\n        return SlixErrorFormat;\n    case Iso15693_3ErrorInternal:\n        return SlixErrorInternal;\n    default:\n        return SlixErrorUnknown;\n    }\n}\n\nSlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf) {\n    furi_assert(data);\n    SlixError error = SlixErrorNone;\n\n    do {\n        if(slix_error_response_parse(&error, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t pp_pointer;\n            uint8_t pp_condition;\n            uint8_t lock_bits;\n            uint32_t feature_flags;\n        } SlixGetNxpSystemInfoResponseLayout;\n\n        const size_t size_received = bit_buffer_get_size_bytes(buf);\n        const size_t size_required = sizeof(SlixGetNxpSystemInfoResponseLayout);\n\n        if(size_received != size_required) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixGetNxpSystemInfoResponseLayout* response =\n            (const SlixGetNxpSystemInfoResponseLayout*)bit_buffer_get_data(buf);\n\n        SlixProtection* protection = &data->system_info.protection;\n        protection->pointer = response->pp_pointer;\n        protection->condition = response->pp_condition;\n\n        Iso15693_3LockBits* iso15693_3_lock_bits = &data->iso15693_3_data->settings.lock_bits;\n        iso15693_3_lock_bits->dsfid = response->lock_bits & SLIX_LOCK_BITS_DSFID;\n        iso15693_3_lock_bits->afi = response->lock_bits & SLIX_LOCK_BITS_AFI;\n\n        SlixLockBits* slix_lock_bits = &data->system_info.lock_bits;\n        slix_lock_bits->eas = response->lock_bits & SLIX_LOCK_BITS_EAS;\n        slix_lock_bits->ppl = response->lock_bits & SLIX_LOCK_BITS_PPL;\n\n    } while(false);\n\n    return error;\n}\n\nSlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf) {\n    SlixError error = SlixErrorNone;\n\n    do {\n        if(slix_error_response_parse(&error, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t signature[SLIX_SIGNATURE_SIZE];\n        } SlixReadSignatureResponseLayout;\n\n        const size_t size_received = bit_buffer_get_size_bytes(buf);\n        const size_t size_required = sizeof(SlixReadSignatureResponseLayout);\n\n        if(size_received != size_required) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixReadSignatureResponseLayout* response =\n            (const SlixReadSignatureResponseLayout*)bit_buffer_get_data(buf);\n\n        memcpy(data, response->signature, sizeof(SlixSignature));\n    } while(false);\n\n    return error;\n}\n\nSlixError slix_get_random_number_response_parse(SlixRandomNumber* data, const BitBuffer* buf) {\n    SlixError error = SlixErrorNone;\n\n    do {\n        if(slix_error_response_parse(&error, buf)) break;\n\n        typedef struct {\n            uint8_t flags;\n            uint8_t random_number[2];\n        } SlixGetRandomNumberResponseLayout;\n\n        const size_t size_received = bit_buffer_get_size_bytes(buf);\n        const size_t size_required = sizeof(SlixGetRandomNumberResponseLayout);\n\n        if(size_received != size_required) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixGetRandomNumberResponseLayout* response =\n            (const SlixGetRandomNumberResponseLayout*)bit_buffer_get_data(buf);\n        *data = (response->random_number[1] << 8) | response->random_number[0];\n    } while(false);\n\n    return error;\n}\n\nvoid slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password) {\n    furi_assert(data);\n    furi_assert(password_type < SlixPasswordTypeCount);\n\n    data->passwords[password_type] = password;\n}\n\nvoid slix_set_privacy_mode(SlixData* data, bool set) {\n    furi_assert(data);\n\n    data->privacy = set;\n}\n\nvoid slix_increment_counter(SlixData* data) {\n    furi_assert(data);\n\n    const uint8_t* block_data =\n        iso15693_3_get_block_data(data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);\n\n    SlixCounter counter;\n    memcpy(counter.bytes, block_data, SLIX_BLOCK_SIZE);\n    counter.value += 1;\n\n    iso15693_3_set_block_data(\n        data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM, counter.bytes, sizeof(SlixCounter));\n}\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_i.h",
    "content": "#pragma once\n\n#include \"slix.h\"\n\n#include <nfc/protocols/iso15693_3/iso15693_3_i.h>\n#include <toolbox/bit_buffer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define SLIX_NXP_MANUFACTURER_CODE (0x04U)\n\n#define SLIX_LOCK_BITS_AFI   (1U << 0)\n#define SLIX_LOCK_BITS_EAS   (1U << 1)\n#define SLIX_LOCK_BITS_DSFID (1U << 2)\n#define SLIX_LOCK_BITS_PPL   (1U << 3)\n\n#define SLIX_CMD_CUSTOM_START                   (0xA2U)\n#define SLIX_CMD_SET_EAS                        (0xA2U)\n#define SLIX_CMD_RESET_EAS                      (0xA3U)\n#define SLIX_CMD_LOCK_EAS                       (0xA4U)\n#define SLIX_CMD_EAS_ALARM                      (0xA5U)\n#define SLIX_CMD_PASSWORD_PROTECT_EAS_AFI       (0xA6U)\n#define SLIX_CMD_WRITE_EAS_ID                   (0xA7U)\n#define SLIX_CMD_GET_NXP_SYSTEM_INFORMATION     (0xABU)\n#define SLIX_CMD_INVENTORY_PAGE_READ            (0xB0U)\n#define SLIX_CMD_INVENTORY_PAGE_READ_FAST       (0xB1U)\n#define SLIX_CMD_GET_RANDOM_NUMBER              (0xB2U)\n#define SLIX_CMD_SET_PASSWORD                   (0xB3U)\n#define SLIX_CMD_WRITE_PASSWORD                 (0xB4U)\n#define SLIX_CMD_64_BIT_PASSWORD_PROTECTION     (0xB5U)\n#define SLIX_CMD_PROTECT_PAGE                   (0xB6U)\n#define SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U)\n#define SLIX_CMD_DESTROY                        (0xB9U)\n#define SLIX_CMD_ENABLE_PRIVACY                 (0xBAU)\n#define SLIX_CMD_STAY_QUIET_PERSISTENT          (0xBCU)\n#define SLIX_CMD_READ_SIGNATURE                 (0xBDU)\n#define SLIX_CMD_CUSTOM_END                     (0xBEU)\n#define SLIX_CMD_CUSTOM_COUNT                   (SLIX_CMD_CUSTOM_END - SLIX_CMD_CUSTOM_START)\n\n#define SLIX_TYPE_FEATURES_SLIX (SLIX_TYPE_FEATURE_EAS)\n#define SLIX_TYPE_FEATURES_SLIX_S                                                   \\\n    (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \\\n     SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS)\n#define SLIX_TYPE_FEATURES_SLIX_L \\\n    (SLIX_TYPE_FEATURE_PRIVACY | SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS)\n#define SLIX_TYPE_FEATURES_SLIX2                                                       \\\n    (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY |    \\\n     SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS | SLIX_TYPE_FEATURE_SIGNATURE | \\\n     SLIX_TYPE_FEATURE_PROTECTION | SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO)\n\n#define SLIX2_FEATURE_FLAGS                                                                       \\\n    (SLIX_FEATURE_FLAG_UM_PP | SLIX_FEATURE_FLAG_COUNTER | SLIX_FEATURE_FLAG_EAS_ID |             \\\n     SLIX_FEATURE_FLAG_EAS_PP | SLIX_FEATURE_FLAG_AFI_PP | SLIX_FEATURE_FLAG_INVENTORY_READ_EXT | \\\n     SLIX_FEATURE_FLAG_EAS_IR | SLIX_FEATURE_FLAG_ORIGINALITY_SIG |                               \\\n     SLIX_FEATURE_FLAG_PERSISTENT_QUIET | SLIX_FEATURE_FLAG_PRIVACY | SLIX_FEATURE_FLAG_DESTROY)\n\ntypedef union {\n    struct {\n        uint16_t value;\n        uint8_t reserved;\n        uint8_t protection;\n    };\n    uint8_t bytes[SLIX_BLOCK_SIZE];\n} SlixCounter;\n\n// Same behaviour as iso15693_3_error_response_parse\nbool slix_error_response_parse(SlixError* error, const BitBuffer* buf);\n\nSlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error);\n\nSlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf);\n\nSlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf);\n\nSlixError slix_get_random_number_response_parse(SlixRandomNumber* data, const BitBuffer* buf);\n\n// Setters\nvoid slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password);\n\nvoid slix_set_privacy_mode(SlixData* data, bool set);\n\nvoid slix_increment_counter(SlixData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_listener.c",
    "content": "#include \"slix_listener_i.h\"\n\n#include <furi.h>\n#include <nfc/protocols/nfc_listener_base.h>\n\n#define TAG \"SlixListener\"\n\n#define SLIX_LISTENER_BUF_SIZE (64U)\n\nstatic SlixListener* slix_listener_alloc(Iso15693_3Listener* iso15693_3_listener, SlixData* data) {\n    furi_assert(iso15693_3_listener);\n\n    SlixListener* instance = malloc(sizeof(SlixListener));\n    instance->iso15693_3_listener = iso15693_3_listener;\n    instance->data = data;\n\n    instance->tx_buffer = bit_buffer_alloc(SLIX_LISTENER_BUF_SIZE);\n\n    instance->slix_event.data = &instance->slix_event_data;\n    instance->generic_event.protocol = NfcProtocolSlix;\n    instance->generic_event.instance = instance;\n    instance->generic_event.event_data = &instance->slix_event;\n\n    slix_listener_init_iso15693_3_extensions(instance);\n\n    return instance;\n}\n\nstatic void slix_listener_free(SlixListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n    furi_assert(instance->tx_buffer);\n\n    bit_buffer_free(instance->tx_buffer);\n    free(instance);\n}\n\nstatic void\n    slix_listener_set_callback(SlixListener* instance, NfcGenericCallback callback, void* context) {\n    furi_assert(instance);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic const SlixData* slix_listener_get_data(SlixListener* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic NfcCommand slix_listener_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n    furi_assert(event.event_data);\n\n    SlixListener* instance = context;\n    Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;\n    BitBuffer* rx_buffer = iso15693_3_event->data->buffer;\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {\n        const SlixError error = slix_listener_process_request(instance, rx_buffer);\n        if(error == SlixErrorWrongPassword) {\n            command = NfcCommandSleep;\n        }\n    }\n\n    return command;\n}\n\nconst NfcListenerBase nfc_listener_slix = {\n    .alloc = (NfcListenerAlloc)slix_listener_alloc,\n    .free = (NfcListenerFree)slix_listener_free,\n    .set_callback = (NfcListenerSetCallback)slix_listener_set_callback,\n    .get_data = (NfcListenerGetData)slix_listener_get_data,\n    .run = (NfcListenerRun)slix_listener_run,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_listener.h",
    "content": "#pragma once\n\n#include <lib/nfc/protocols/iso15693_3/iso15693_3_listener.h>\n\n#include \"slix.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SlixListener SlixListener;\n\ntypedef enum {\n    SlixListenerEventTypeFieldOff,\n    SlixListenerEventTypeCustomCommand,\n} SlixListenerEventType;\n\ntypedef struct {\n    BitBuffer* buffer;\n} SlixListenerEventData;\n\ntypedef struct {\n    SlixListenerEventType type;\n    SlixListenerEventData* data;\n} SlixListenerEvent;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_listener_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_listener_base.h>\n\nextern const NfcListenerBase nfc_listener_slix;\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_listener_i.c",
    "content": "#include \"slix_listener_i.h\"\n\n#include <nfc/protocols/iso15693_3/iso15693_3_listener_i.h>\n\n#include <furi_hal_random.h>\n\n#define TAG \"SlixListener\"\n\ntypedef SlixError (*SlixRequestHandler)(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags);\n\n// Helper functions\n\nstatic bool\n    slix_listener_is_password_lock_enabled(SlixListener* instance, SlixPasswordType password_type) {\n    return !instance->session_state.password_match[password_type];\n}\n\nstatic SlixPasswordType slix_listener_get_password_type_by_id(uint8_t id) {\n    uint32_t type;\n\n    for(type = 0; type < SlixPasswordTypeCount; ++type) {\n        if(id >> type == 0x01U) break;\n    }\n\n    return type;\n}\n\nstatic SlixPassword\n    slix_listener_unxor_password(const SlixPassword password_xored, uint16_t random) {\n    return REVERSE_BYTES_U32(password_xored ^ ((SlixPassword)random << 16 | random));\n}\n\nstatic SlixError slix_listener_set_password(\n    SlixListener* instance,\n    SlixPasswordType password_type,\n    SlixPassword password) {\n    SlixError error = SlixErrorNone;\n\n    do {\n        if(password_type >= SlixPasswordTypeCount) {\n            error = SlixErrorInternal;\n            break;\n        }\n\n        SlixData* slix_data = instance->data;\n\n        if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) {\n            error = SlixErrorNotSupported;\n            break;\n        }\n\n        SlixListenerSessionState* session_state = &instance->session_state;\n\n        // With AcceptAllPassword capability set skip password validation\n        if(instance->data->capabilities == SlixCapabilitiesAcceptAllPasswords) {\n            session_state->password_match[password_type] = true;\n            break;\n        }\n\n        session_state->password_match[password_type] =\n            (password == slix_get_password(slix_data, password_type));\n\n        if(!session_state->password_match[password_type]) {\n            error = SlixErrorWrongPassword;\n            break;\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic SlixError slix_listener_write_password(\n    SlixListener* instance,\n    SlixPasswordType password_type,\n    SlixPassword password) {\n    SlixError error = SlixErrorNone;\n\n    do {\n        if(password_type >= SlixPasswordTypeCount) {\n            error = SlixErrorInternal;\n            break;\n        }\n\n        SlixData* slix_data = instance->data;\n\n        if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) {\n            error = SlixErrorNotSupported;\n            break;\n        }\n\n        SlixListenerSessionState* session_state = &instance->session_state;\n\n        if(session_state->password_match[password_type]) {\n            // TODO FL-3634: check for password lock\n            slix_set_password(slix_data, password_type, password);\n            // Require another SET_PASSWORD command with the new password\n            session_state->password_match[password_type] = false;\n        } else {\n            error = SlixErrorWrongPassword;\n            break;\n        }\n    } while(false);\n\n    return error;\n}\n\n// Custom SLIX request handlers\nstatic SlixError slix_listener_default_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(instance);\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    // Empty placeholder handler\n    return SlixErrorNotSupported;\n}\n\nstatic SlixError slix_listener_get_nxp_system_info_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    const SlixData* slix_data = instance->data;\n    const Iso15693_3Data* iso15693_data = instance->data->iso15693_3_data;\n\n    const SlixProtection* protection = &slix_data->system_info.protection;\n    bit_buffer_append_byte(instance->tx_buffer, protection->pointer);\n    bit_buffer_append_byte(instance->tx_buffer, protection->condition);\n\n    uint8_t lock_bits = 0;\n    if(iso15693_data->settings.lock_bits.dsfid) {\n        lock_bits |= SLIX_LOCK_BITS_DSFID;\n    }\n    if(iso15693_data->settings.lock_bits.afi) {\n        lock_bits |= SLIX_LOCK_BITS_AFI;\n    }\n    if(slix_data->system_info.lock_bits.eas) {\n        lock_bits |= SLIX_LOCK_BITS_EAS;\n    }\n    if(slix_data->system_info.lock_bits.ppl) {\n        lock_bits |= SLIX_LOCK_BITS_PPL;\n    }\n    bit_buffer_append_byte(instance->tx_buffer, lock_bits);\n\n    const uint32_t feature_flags = SLIX2_FEATURE_FLAGS;\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&feature_flags, sizeof(uint32_t));\n\n    return SlixErrorNone;\n}\n\nstatic SlixError slix_listener_get_random_number_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    SlixListenerSessionState* session_state = &instance->session_state;\n    session_state->random = furi_hal_random_get();\n    bit_buffer_append_bytes(\n        instance->tx_buffer, (uint8_t*)&session_state->random, sizeof(uint16_t));\n\n    return SlixErrorNone;\n}\n\nstatic SlixError slix_listener_set_password_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(flags);\n    SlixError error = SlixErrorNone;\n\n    do {\n#pragma pack(push, 1)\n        typedef struct {\n            uint8_t password_id;\n            SlixPassword password_xored;\n        } SlixSetPasswordRequestLayout;\n#pragma pack(pop)\n\n        if(data_size != sizeof(SlixSetPasswordRequestLayout)) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixSetPasswordRequestLayout* request = (const SlixSetPasswordRequestLayout*)data;\n        const SlixPasswordType password_type =\n            slix_listener_get_password_type_by_id(request->password_id);\n        const SlixPassword password_received =\n            slix_listener_unxor_password(request->password_xored, instance->session_state.random);\n\n        error = slix_listener_set_password(instance, password_type, password_received);\n        if(error != SlixErrorNone) break;\n\n        if(password_type == SlixPasswordTypePrivacy) {\n            slix_set_privacy_mode(instance->data, false);\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic SlixError slix_listener_write_password_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(flags);\n    SlixError error = SlixErrorNone;\n\n    do {\n#pragma pack(push, 1)\n        typedef struct {\n            uint8_t password_id;\n            SlixPassword password;\n        } SlixWritePasswordRequestLayout;\n#pragma pack(pop)\n\n        if(data_size != sizeof(SlixWritePasswordRequestLayout)) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixWritePasswordRequestLayout* request =\n            (const SlixWritePasswordRequestLayout*)data;\n        const SlixPasswordType password_type =\n            slix_listener_get_password_type_by_id(request->password_id);\n\n        error = slix_listener_write_password(instance, password_type, request->password);\n        if(error != SlixErrorNone) break;\n\n    } while(false);\n\n    return error;\n}\n\nstatic SlixError slix_listener_protect_page_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(flags);\n    SlixError error = SlixErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t pointer;\n            uint8_t condition;\n        } SlixProtectPageRequestLayout;\n\n        if(data_size != sizeof(SlixProtectPageRequestLayout)) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        SlixData* slix_data = instance->data;\n\n        if(slix_data->system_info.lock_bits.ppl) {\n            error = SlixErrorInternal;\n            break;\n        }\n\n        const SlixListenerSessionState* session_state = &instance->session_state;\n        if(!session_state->password_match[SlixPasswordTypeRead] ||\n           !session_state->password_match[SlixPasswordTypeWrite]) {\n            error = SlixErrorInternal;\n            break;\n        }\n\n        const SlixProtectPageRequestLayout* request = (const SlixProtectPageRequestLayout*)data;\n\n        if(request->pointer >= SLIX_COUNTER_BLOCK_NUM) {\n            error = SlixErrorInternal;\n            break;\n        }\n\n        SlixProtection* protection = &slix_data->system_info.protection;\n\n        protection->pointer = request->pointer;\n        protection->condition = request->condition;\n    } while(false);\n\n    return error;\n}\n\nstatic SlixError slix_listener_enable_privacy_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(flags);\n    SlixError error = SlixErrorNone;\n\n    do {\n        typedef struct {\n            SlixPassword password_xored;\n        } SlixEnablePrivacyRequestLayout;\n\n        if(data_size != sizeof(SlixEnablePrivacyRequestLayout)) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixEnablePrivacyRequestLayout* request =\n            (const SlixEnablePrivacyRequestLayout*)data;\n\n        const SlixPassword password_received =\n            slix_listener_unxor_password(request->password_xored, instance->session_state.random);\n\n        error = slix_listener_set_password(instance, SlixPasswordTypePrivacy, password_received);\n        if(error != SlixErrorNone) break;\n\n        slix_set_privacy_mode(instance->data, true);\n    } while(false);\n\n    return error;\n}\n\nstatic SlixError slix_listener_read_signature_handler(\n    SlixListener* instance,\n    const uint8_t* data,\n    size_t data_size,\n    uint8_t flags) {\n    UNUSED(data);\n    UNUSED(data_size);\n    UNUSED(flags);\n\n    bit_buffer_append_bytes(instance->tx_buffer, instance->data->signature, sizeof(SlixSignature));\n    return SlixErrorNone;\n}\n\n// Custom SLIX commands handler table\nstatic const SlixRequestHandler slix_request_handler_table[SLIX_CMD_CUSTOM_COUNT] = {\n    slix_listener_default_handler, // SLIX_CMD_SET_EAS (0xA2U)\n    slix_listener_default_handler, // SLIX_CMD_RESET_EAS (0xA3U)\n    slix_listener_default_handler, // SLIX_CMD_LOCK_EAS (0xA4U)\n    slix_listener_default_handler, // SLIX_CMD_EAS_ALARM (0xA5U)\n    slix_listener_default_handler, // SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U)\n    slix_listener_default_handler, // SLIX_CMD_WRITE_EAS_ID (0xA7U)\n    slix_listener_default_handler, // UNUSED (0xA8U)\n    slix_listener_default_handler, // UNUSED (0xA9U)\n    slix_listener_default_handler, // UNUSED (0xAAU)\n    slix_listener_get_nxp_system_info_handler,\n    slix_listener_default_handler, // UNUSED (0xACU)\n    slix_listener_default_handler, // UNUSED (0xADU)\n    slix_listener_default_handler, // UNUSED (0xAEU)\n    slix_listener_default_handler, // UNUSED (0xAFU)\n    slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ (0xB0U)\n    slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U)\n    slix_listener_get_random_number_handler,\n    slix_listener_set_password_handler,\n    slix_listener_write_password_handler,\n    slix_listener_default_handler, // SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U)\n    slix_listener_protect_page_handler,\n    slix_listener_default_handler, // SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U)\n    slix_listener_default_handler, // UNUSED (0xB8U)\n    slix_listener_default_handler, // SLIX_CMD_DESTROY (0xB9U)\n    slix_listener_enable_privacy_handler,\n    slix_listener_default_handler, // UNUSED (0xBBU)\n    slix_listener_default_handler, // SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU)\n    slix_listener_read_signature_handler,\n};\n\n// ISO15693-3 Protocol extension handlers\n\nstatic Iso15693_3Error\n    slix_listener_iso15693_3_inventory_extension_handler(SlixListener* instance, va_list args) {\n    UNUSED(args);\n\n    return instance->data->privacy ? Iso15693_3ErrorIgnore : Iso15693_3ErrorNone;\n}\n\nstatic Iso15693_3Error\n    slix_iso15693_3_read_block_extension_handler(SlixListener* instance, va_list args) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        const uint32_t block_num = va_arg(args, uint32_t);\n        // SLIX Counter has no read protection\n        if(block_num == SLIX_COUNTER_BLOCK_NUM) break;\n\n        if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error\n    slix_listener_iso15693_3_write_block_extension_handler(SlixListener* instance, va_list args) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        const uint32_t block_num = va_arg(args, uint32_t);\n\n        if(block_num == SLIX_COUNTER_BLOCK_NUM) {\n            const uint32_t counter = *(va_arg(args, uint32_t*));\n            if(counter == 0x00000001U) {\n                if(slix_is_counter_increment_protected(instance->data) &&\n                   slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {\n                    error = Iso15693_3ErrorInternal;\n                    break;\n                }\n                slix_increment_counter(instance->data);\n                error = Iso15693_3ErrorFullyHandled;\n                break;\n            }\n        } else if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n\n        if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error\n    slix_listener_iso15693_3_lock_block_extension_handler(SlixListener* instance, va_list args) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    do {\n        const uint32_t block_num = va_arg(args, uint32_t);\n\n        // SLIX counter cannot be locked\n        if(block_num == SLIX_COUNTER_BLOCK_NUM) {\n            error = Iso15693_3ErrorInternal;\n            break;\n        }\n\n        if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n\n        if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n\n    } while(false);\n\n    return error;\n}\n\nstatic Iso15693_3Error slix_listener_iso15693_3_read_multi_block_extension_handler(\n    SlixListener* instance,\n    va_list args) {\n    Iso15693_3Error error = Iso15693_3ErrorNone;\n\n    const uint32_t block_index_start = va_arg(args, uint32_t);\n    const uint32_t block_index_end = va_arg(args, uint32_t);\n\n    for(uint32_t i = block_index_start; i <= block_index_end; ++i) {\n        // SLIX Counter has no read protection\n        if(i == SLIX_COUNTER_BLOCK_NUM) continue;\n\n        if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, i)) {\n            if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {\n                error = Iso15693_3ErrorInternal;\n                break;\n            }\n        }\n    }\n\n    return error;\n}\n\nstatic Iso15693_3Error slix_listener_iso15693_3_write_multi_block_extension_handler(\n    SlixListener* instance,\n    va_list args) {\n    UNUSED(instance);\n    UNUSED(args);\n    // No mention of this command in SLIX docs, assuming not supported\n    return Iso15693_3ErrorNotSupported;\n}\n\nstatic Iso15693_3Error slix_listener_iso15693_3_write_lock_afi_extension_handler(\n    SlixListener* instance,\n    va_list args) {\n    UNUSED(args);\n\n    return slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeEasAfi) ?\n               Iso15693_3ErrorInternal :\n               Iso15693_3ErrorNone;\n}\n\n// Extended ISO15693-3 standard commands handler table (NULL = no extension)\nstatic const Iso15693_3ExtensionHandlerTable slix_iso15693_extension_handler_table = {\n    .mandatory =\n        {\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_inventory_extension_handler,\n            (Iso15693_3ExtensionHandler)NULL // ISO15693_3_CMD_STAY_QUIET (0x02U)\n        },\n    .optional =\n        {\n            (Iso15693_3ExtensionHandler)slix_iso15693_3_read_block_extension_handler,\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_block_extension_handler,\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_lock_block_extension_handler,\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_read_multi_block_extension_handler,\n            (Iso15693_3ExtensionHandler)\n                slix_listener_iso15693_3_write_multi_block_extension_handler,\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_SELECT (0x25U)\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_RESET_TO_READY (0x26U)\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler,\n            (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler,\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_WRITE_DSFID (0x29U)\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_LOCK_DSFID (0x2AU)\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_SYS_INFO (0x2BU)\n            (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU)\n        },\n};\n\nSlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance) {\n    iso15693_3_listener_set_extension_handler_table(\n        instance->iso15693_3_listener, &slix_iso15693_extension_handler_table, instance);\n    return SlixErrorNone;\n}\n\nSlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer) {\n    SlixError error = SlixErrorNone;\n\n    do {\n        typedef struct {\n            uint8_t flags;\n            uint8_t command;\n            uint8_t manufacturer;\n            uint8_t data[];\n        } SlixRequestLayout;\n\n        const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);\n\n        if(buf_size < sizeof(SlixRequestLayout)) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        const SlixRequestLayout* request =\n            (const SlixRequestLayout*)bit_buffer_get_data(rx_buffer);\n\n        const bool addressed_mode = instance->iso15693_3_listener->session_state.addressed;\n\n        const size_t uid_field_size = addressed_mode ? ISO15693_3_UID_SIZE : 0;\n        const size_t buf_size_min = sizeof(SlixRequestLayout) + uid_field_size;\n\n        if(buf_size < buf_size_min) {\n            error = SlixErrorFormat;\n            break;\n        }\n\n        if(addressed_mode) {\n            if(!iso15693_3_is_equal_uid(instance->data->iso15693_3_data, request->data)) {\n                error = SlixErrorUidMismatch;\n                break;\n            }\n        }\n\n        const uint8_t command = request->command;\n        const bool is_valid_slix_command = command >= SLIX_CMD_CUSTOM_START &&\n                                           command < SLIX_CMD_CUSTOM_END;\n        if(!is_valid_slix_command) {\n            error = SlixErrorNotSupported;\n            break;\n        }\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE);\n\n        const uint8_t* request_data = &request->data[uid_field_size];\n        const size_t request_data_size = buf_size - buf_size_min;\n\n        SlixRequestHandler handler = slix_request_handler_table[command - SLIX_CMD_CUSTOM_START];\n        error = handler(instance, request_data, request_data_size, request->flags);\n\n        // It's a trick! Send no reply.\n        if(error == SlixErrorFormat || error == SlixErrorWrongPassword ||\n           error == SlixErrorNotSupported)\n            break;\n\n        if(error != SlixErrorNone) {\n            bit_buffer_reset(instance->tx_buffer);\n            bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR);\n            bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN);\n        }\n\n        const Iso15693_3Error iso15693_error =\n            iso15693_3_listener_send_frame(instance->iso15693_3_listener, instance->tx_buffer);\n        error = slix_process_iso15693_3_error(iso15693_error);\n    } while(false);\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_listener_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_generic_event.h>\n\n#include \"slix_listener.h\"\n#include \"slix_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    uint16_t random;\n    bool password_match[SlixPasswordTypeCount];\n} SlixListenerSessionState;\n\nstruct SlixListener {\n    Iso15693_3Listener* iso15693_3_listener;\n    SlixData* data;\n    SlixListenerSessionState session_state;\n\n    BitBuffer* tx_buffer;\n\n    NfcGenericEvent generic_event;\n    SlixListenerEvent slix_event;\n    SlixListenerEventData slix_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nSlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance);\n\nSlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_poller.c",
    "content": "#include \"slix_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#include <furi.h>\n\n#define TAG \"SlixPoller\"\n\n#define SLIX_POLLER_BUF_SIZE (64U)\n\ntypedef NfcCommand (*SlixPollerStateHandler)(SlixPoller* instance);\n\nconst SlixData* slix_poller_get_data(SlixPoller* instance) {\n    furi_assert(instance);\n\n    return instance->data;\n}\n\nstatic SlixPoller* slix_poller_alloc(Iso15693_3Poller* iso15693_3_poller) {\n    SlixPoller* instance = malloc(sizeof(SlixPoller));\n    instance->iso15693_3_poller = iso15693_3_poller;\n    instance->data = slix_alloc();\n    instance->tx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE);\n\n    instance->slix_event.data = &instance->slix_event_data;\n\n    instance->general_event.protocol = NfcProtocolSlix;\n    instance->general_event.event_data = &instance->slix_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void slix_poller_free(SlixPoller* instance) {\n    furi_assert(instance);\n\n    slix_free(instance->data);\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    free(instance);\n}\n\nstatic NfcCommand slix_poller_handler_idle(SlixPoller* instance) {\n    iso15693_3_copy(\n        instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller));\n    instance->type = slix_get_type(instance->data);\n    if(instance->type >= SlixTypeCount) {\n        instance->error = SlixErrorNotSupported;\n        instance->poller_state = SlixPollerStateError;\n    } else {\n        instance->poller_state = SlixPollerStateGetNxpSysInfo;\n    }\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand slix_poller_handler_get_nfc_system_info(SlixPoller* instance) {\n    if(slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO)) {\n        instance->error = slix_poller_get_nxp_system_info(instance, &instance->data->system_info);\n        if(instance->error == SlixErrorNone) {\n            instance->poller_state = SlixPollerStateReadSignature;\n        } else {\n            instance->poller_state = SlixPollerStateError;\n        }\n    } else {\n        instance->poller_state = SlixPollerStateReadSignature;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) {\n    if(slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_SIGNATURE)) {\n        instance->error = slix_poller_read_signature(instance, &instance->data->signature);\n        if(instance->error == SlixErrorNone) {\n            instance->poller_state = SlixPollerStateCheckPrivacyPassword;\n        } else {\n            instance->poller_state = SlixPollerStateError;\n        }\n    } else {\n        instance->poller_state = SlixPollerStateCheckPrivacyPassword;\n    }\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    do {\n        if(!slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_PRIVACY)) {\n            instance->poller_state = SlixPollerStateReady;\n            break;\n        }\n        if(instance->privacy_password_checked) {\n            instance->poller_state = SlixPollerStateReady;\n            break;\n        }\n\n        instance->slix_event.type = SlixPollerEventTypePrivacyUnlockRequest;\n        command = instance->callback(instance->general_event, instance->context);\n\n        if(!instance->slix_event_data.privacy_password.password_set) {\n            instance->poller_state = SlixPollerStateReady;\n            break;\n        }\n\n        SlixPassword pwd = instance->slix_event_data.privacy_password.password;\n        FURI_LOG_I(TAG, \"Trying to check privacy password: %08lX\", pwd);\n\n        instance->error = slix_poller_get_random_number(instance, &instance->random_number);\n        if(instance->error != SlixErrorNone) {\n            instance->poller_state = SlixPollerStateReady;\n            break;\n        }\n\n        instance->error = slix_poller_set_password(\n            instance, SlixPasswordTypePrivacy, pwd, instance->random_number);\n        if(instance->error != SlixErrorNone) {\n            command = NfcCommandReset;\n            break;\n        }\n\n        FURI_LOG_I(TAG, \"Found privacy password\");\n        instance->data->passwords[SlixPasswordTypePrivacy] = pwd;\n        instance->privacy_password_checked = true;\n        instance->poller_state = SlixPollerStateReady;\n    } while(false);\n\n    return command;\n}\n\nstatic NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->poller_state = SlixPollerStateError;\n\n    instance->slix_event.type = SlixPollerEventTypePrivacyUnlockRequest;\n    command = instance->callback(instance->general_event, instance->context);\n\n    bool slix_unlocked = false;\n    do {\n        if(!instance->slix_event_data.privacy_password.password_set) break;\n        SlixPassword pwd = instance->slix_event_data.privacy_password.password;\n        FURI_LOG_I(TAG, \"Trying to disable privacy mode with password: %08lX\", pwd);\n\n        instance->error = slix_poller_get_random_number(instance, &instance->random_number);\n        if(instance->error != SlixErrorNone) break;\n\n        instance->error = slix_poller_set_password(\n            instance, SlixPasswordTypePrivacy, pwd, instance->random_number);\n        if(instance->error != SlixErrorNone) {\n            command = NfcCommandReset;\n            break;\n        }\n\n        FURI_LOG_I(TAG, \"Privacy mode disabled\");\n        instance->data->passwords[SlixPasswordTypePrivacy] = pwd;\n        instance->privacy_password_checked = true;\n        instance->poller_state = SlixPollerStateIdle;\n        slix_unlocked = true;\n    } while(false);\n\n    if(!slix_unlocked) {\n        instance->error = SlixErrorTimeout;\n        instance->poller_state = SlixPollerStateError;\n        furi_delay_ms(100);\n    }\n\n    return command;\n}\n\nstatic NfcCommand slix_poller_handler_error(SlixPoller* instance) {\n    instance->slix_event_data.error = instance->error;\n    instance->slix_event.type = SlixPollerEventTypeError;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    instance->poller_state = SlixPollerStateIdle;\n    return command;\n}\n\nstatic NfcCommand slix_poller_handler_ready(SlixPoller* instance) {\n    instance->slix_event.type = SlixPollerEventTypeReady;\n    NfcCommand command = instance->callback(instance->general_event, instance->context);\n    return command;\n}\n\nstatic const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum] = {\n    [SlixPollerStateIdle] = slix_poller_handler_idle,\n    [SlixPollerStateError] = slix_poller_handler_error,\n    [SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info,\n    [SlixPollerStateReadSignature] = slix_poller_handler_read_signature,\n    [SlixPollerStateCheckPrivacyPassword] = slix_poller_handler_check_privacy_password,\n    [SlixPollerStatePrivacyUnlock] = slix_poller_handler_privacy_unlock,\n    [SlixPollerStateReady] = slix_poller_handler_ready,\n};\n\nstatic void\n    slix_poller_set_callback(SlixPoller* instance, NfcGenericCallback callback, void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand slix_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n\n    SlixPoller* instance = context;\n    furi_assert(instance);\n    furi_assert(instance->callback);\n\n    Iso15693_3PollerEvent* iso15693_3_event = event.event_data;\n    furi_assert(iso15693_3_event);\n\n    NfcCommand command = NfcCommandContinue;\n\n    if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {\n        command = slix_poller_state_handler[instance->poller_state](instance);\n    } else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) {\n        instance->poller_state = SlixPollerStatePrivacyUnlock;\n        command = slix_poller_state_handler[instance->poller_state](instance);\n    }\n\n    return command;\n}\n\nstatic bool slix_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(event.protocol == NfcProtocolIso15693_3);\n\n    SlixPoller* instance = context;\n    furi_assert(instance);\n\n    const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;\n    furi_assert(iso15693_3_event);\n    iso15693_3_copy(\n        instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller));\n\n    bool protocol_detected = false;\n\n    if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {\n        protocol_detected = (slix_get_type(instance->data) < SlixTypeCount);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_slix = {\n    .alloc = (NfcPollerAlloc)slix_poller_alloc,\n    .free = (NfcPollerFree)slix_poller_free,\n    .set_callback = (NfcPollerSetCallback)slix_poller_set_callback,\n    .run = (NfcPollerRun)slix_poller_run,\n    .detect = (NfcPollerDetect)slix_poller_detect,\n    .get_data = (NfcPollerGetData)slix_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_poller.h",
    "content": "#pragma once\n\n#include \"slix.h\"\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief SlixPoller opaque type definition.\n */\ntypedef struct SlixPoller SlixPoller;\n\n/**\n * @brief Enumeration of possible Slix poller event types.\n */\ntypedef enum {\n    SlixPollerEventTypeError, /**< An error occured while reading card. */\n    SlixPollerEventTypePrivacyUnlockRequest, /**< Poller requests password to disable privacy mode. */\n    SlixPollerEventTypeReady, /**< The card was successfully read by the poller. */\n} SlixPollerEventType;\n\n/**\n * @brief Slix poller privacy unlock context data.\n */\ntypedef struct {\n    SlixPassword password; /**< Privacy password. */\n    bool password_set; /**< Filed to indicate that password was set or not. */\n} SlixPollerEventDataPrivacyUnlockContext;\n\n/**\n * @brief Slixs poller event data.\n */\ntypedef union {\n    SlixError error; /**< Error code indicating card reaing fail reason. */\n    SlixPollerEventDataPrivacyUnlockContext privacy_password; /**< Privacy unlock event context. */\n} SlixPollerEventData;\n\n/**\n * @brief Slix poller event structure.\n *\n * Upon emission of an event, an instance of this struct will be passed to the callback.\n */\ntypedef struct {\n    SlixPollerEventType type; /**< Type of emmitted event. */\n    SlixPollerEventData* data; /**< Pointer to event specific data. */\n} SlixPollerEvent;\n\n/**\n * @brief Transmit and receive Slix frames in poller mode.\n *\n * Must ONLY be used inside the callback function.\n *\n * The rx_buffer will be filled with any data received as a response to data\n * sent from tx_buffer, with a timeout defined by the fwt parameter.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.\n * @param[out] rx_buffer pointer to the buffer to be filled with received data.\n * @param[in] fwt frame wait time (response timeout), in carrier cycles.\n * @return SlixErrorNone on success, an error code on failure.\n */\nSlixError slix_poller_send_frame(\n    SlixPoller* instance,\n    const BitBuffer* tx_data,\n    BitBuffer* rx_data,\n    uint32_t fwt);\n\n/**\n * @brief Send get nxp system info command and parse response.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SlixSystemInfo structure to be filled.\n * @return SlixErrorNone on success, an error code on failure.\n */\nSlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data);\n\n/**\n * @brief Read signature from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SlixSignature structure to be filled.\n * @return SlixErrorNone on success, an error code on failure.\n */\nSlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data);\n\n/**\n * @brief Get random number from card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[out] data pointer to the SlixRandomNumber structure to be filled.\n * @return SlixErrorNone on success, an error code on failure.\n */\nSlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* data);\n\n/**\n * @brief Set password to card.\n *\n * Must ONLY be used inside the callback function.\n *\n * @param[in, out] instance pointer to the instance to be used in the transaction.\n * @param[in] type SlixPasswordType instance.\n * @param[in] password SlixPassword instance.\n * @param[in] random_number SlixRandomNumber instance.\n * @return SlixErrorNone on success, an error code on failure.\n */\nSlixError slix_poller_set_password(\n    SlixPoller* instance,\n    SlixPasswordType type,\n    SlixPassword password,\n    SlixRandomNumber random_number);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\nextern const NfcPollerBase nfc_poller_slix;\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_poller_i.c",
    "content": "#include \"slix_poller_i.h\"\n#include <bit_lib/bit_lib.h>\n\n#include <furi.h>\n\n#include \"slix_i.h\"\n\n#define TAG \"SlixPoller\"\n\nstatic void slix_poller_prepare_request(SlixPoller* instance, uint8_t command, bool skip_uid) {\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    uint8_t flags = ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI;\n    if(!skip_uid) {\n        flags |= ISO15693_3_REQ_FLAG_T4_ADDRESSED;\n    }\n\n    bit_buffer_append_byte(instance->tx_buffer, flags);\n    bit_buffer_append_byte(instance->tx_buffer, command);\n    bit_buffer_append_byte(instance->tx_buffer, SLIX_NXP_MANUFACTURER_CODE);\n\n    if(!skip_uid) {\n        iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer);\n    }\n}\n\nSlixError slix_poller_send_frame(\n    SlixPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_assert(instance);\n\n    const Iso15693_3Error iso15693_3_error =\n        iso15693_3_poller_send_frame(instance->iso15693_3_poller, tx_buffer, rx_buffer, fwt);\n    return slix_process_iso15693_3_error(iso15693_3_error);\n}\n\nSlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION, false);\n\n    SlixError error = SlixErrorNone;\n\n    do {\n        error = slix_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(error != SlixErrorNone) break;\n        error = slix_get_nxp_system_info_response_parse(instance->data, instance->rx_buffer);\n    } while(false);\n\n    return error;\n}\n\nSlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE, false);\n\n    SlixError error = SlixErrorNone;\n\n    do {\n        error = slix_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC * 2);\n        if(error != SlixErrorNone) break;\n        error = slix_read_signature_response_parse(instance->data->signature, instance->rx_buffer);\n    } while(false);\n\n    return error;\n}\n\nSlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* data) {\n    furi_assert(instance);\n    furi_assert(data);\n\n    slix_poller_prepare_request(instance, SLIX_CMD_GET_RANDOM_NUMBER, true);\n\n    SlixError error = SlixErrorNone;\n\n    do {\n        error = slix_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);\n        if(error != SlixErrorNone) break;\n\n        error = slix_get_random_number_response_parse(data, instance->rx_buffer);\n    } while(false);\n\n    return error;\n}\n\nSlixError slix_poller_set_password(\n    SlixPoller* instance,\n    SlixPasswordType type,\n    SlixPassword password,\n    SlixRandomNumber random_number) {\n    furi_assert(instance);\n\n    bool skip_uid = (type == SlixPasswordTypePrivacy);\n    slix_poller_prepare_request(instance, SLIX_CMD_SET_PASSWORD, skip_uid);\n\n    uint8_t password_type = (0x01 << type);\n    bit_buffer_append_byte(instance->tx_buffer, password_type);\n\n    uint8_t rn_l = random_number >> 8;\n    uint8_t rn_h = random_number;\n    uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l;\n    uint32_t xored_password = double_rand_num ^ password;\n    uint8_t xored_password_arr[4] = {};\n    bit_lib_num_to_bytes_be(xored_password, 4, xored_password_arr);\n    bit_buffer_append_bytes(instance->tx_buffer, xored_password_arr, 4);\n\n    SlixError error = SlixErrorNone;\n\n    do {\n        error = slix_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, SLIX_POLLER_SET_PASSWORD_FWT);\n        if(error != SlixErrorNone) break;\n\n        size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer);\n        if(rx_len != 1) {\n            error = SlixErrorWrongPassword;\n        }\n    } while(false);\n\n    return error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/slix/slix_poller_i.h",
    "content": "#pragma once\n\n#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>\n\n#include \"slix_poller.h\"\n\n#define SLIX_POLLER_SET_PASSWORD_FWT (100000)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    SlixPollerStateIdle,\n    SlixPollerStateGetNxpSysInfo,\n    SlixPollerStateReadSignature,\n    SlixPollerStateCheckPrivacyPassword,\n    SlixPollerStatePrivacyUnlock,\n    SlixPollerStateReady,\n    SlixPollerStateError,\n    SlixPollerStateNum,\n} SlixPollerState;\n\nstruct SlixPoller {\n    Iso15693_3Poller* iso15693_3_poller;\n    SlixType type;\n    SlixData* data;\n    SlixPollerState poller_state;\n    SlixError error;\n    SlixRandomNumber random_number;\n    bool privacy_password_checked;\n\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    SlixPollerEventData slix_event_data;\n    SlixPollerEvent slix_event;\n    NfcGenericEvent general_event;\n    NfcGenericCallback callback;\n    void* context;\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb.c",
    "content": "#include \"st25tb.h\"\n\n#include \"flipper_format.h\"\n#include <furi.h>\n\n#include <nfc/nfc_common.h>\n#include <nfc/helpers/iso14443_crc.h>\n\n#define ST25TB_PROTOCOL_NAME    \"ST25TB\"\n#define ST25TB_TYPE_KEY         \"ST25TB Type\"\n#define ST25TB_BLOCK_KEY        \"Block %d\"\n#define ST25TB_SYSTEM_BLOCK_KEY \"System OTP Block\"\n\ntypedef struct {\n    uint8_t blocks_total;\n    bool has_otp;\n    const char* full_name;\n    const char* type_name;\n} St25tbFeatures;\n\nstatic const St25tbFeatures st25tb_features[St25tbTypeNum] = {\n    [St25tbType512At] =\n        {\n            .blocks_total = 16,\n            .has_otp = false,\n            .full_name = \"ST25TB512-AT/SRI512\",\n            .type_name = \"512AT\",\n        },\n    [St25tbType512Ac] =\n        {\n            .blocks_total = 16,\n            .has_otp = true,\n            .full_name = \"ST25TB512-AC/SRT512\",\n            .type_name = \"512AC\",\n        },\n    [St25tbTypeX512] =\n        {\n            .blocks_total = 16,\n            .has_otp = true,\n            .full_name = \"SRIX512\",\n            .type_name = \"X512\",\n        },\n    [St25tbType02k] =\n        {\n            .blocks_total = 64,\n            .has_otp = true,\n            .full_name = \"ST25TB02K/SRI2K\",\n            .type_name = \"2K\",\n        },\n    [St25tbType04k] =\n        {\n            .blocks_total = 128,\n            .has_otp = true,\n            .full_name = \"ST25TB04K/SRI4K\",\n            .type_name = \"4K\",\n        },\n    [St25tbTypeX4k] =\n        {\n            .blocks_total = 128,\n            .has_otp = true,\n            .full_name = \"SRIX4K\",\n            .type_name = \"X4K\",\n        },\n};\n\nconst NfcDeviceBase nfc_device_st25tb = {\n    .protocol_name = ST25TB_PROTOCOL_NAME,\n    .alloc = (NfcDeviceAlloc)st25tb_alloc,\n    .free = (NfcDeviceFree)st25tb_free,\n    .reset = (NfcDeviceReset)st25tb_reset,\n    .copy = (NfcDeviceCopy)st25tb_copy,\n    .verify = (NfcDeviceVerify)st25tb_verify,\n    .load = (NfcDeviceLoad)st25tb_load,\n    .save = (NfcDeviceSave)st25tb_save,\n    .is_equal = (NfcDeviceEqual)st25tb_is_equal,\n    .get_name = (NfcDeviceGetName)st25tb_get_device_name,\n    .get_uid = (NfcDeviceGetUid)st25tb_get_uid,\n    .set_uid = (NfcDeviceSetUid)st25tb_set_uid,\n    .get_base_data = (NfcDeviceGetBaseData)st25tb_get_base_data,\n};\n\nSt25tbData* st25tb_alloc(void) {\n    St25tbData* data = malloc(sizeof(St25tbData));\n    return data;\n}\n\nvoid st25tb_free(St25tbData* data) {\n    furi_check(data);\n\n    free(data);\n}\n\nvoid st25tb_reset(St25tbData* data) {\n    furi_check(data);\n    memset(data, 0, sizeof(St25tbData));\n}\n\nvoid st25tb_copy(St25tbData* data, const St25tbData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    *data = *other;\n}\n\nbool st25tb_verify(St25tbData* data, const FuriString* device_type) {\n    furi_check(device_type);\n    UNUSED(data);\n\n    return furi_string_equal_str(device_type, ST25TB_PROTOCOL_NAME);\n}\n\nbool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version) {\n    furi_check(data);\n    furi_check(ff);\n\n    bool parsed = false;\n\n    FuriString* temp_str = furi_string_alloc();\n\n    do {\n        if(version < NFC_UNIFIED_FORMAT_VERSION) break;\n        if(!flipper_format_read_string(ff, ST25TB_TYPE_KEY, temp_str)) break;\n\n        bool type_parsed = false;\n        for(size_t i = 0; i < St25tbTypeNum; i++) {\n            if(furi_string_equal_str(temp_str, st25tb_features[i].type_name)) {\n                data->type = i;\n                type_parsed = true;\n            }\n        }\n        if(!type_parsed) break;\n\n        bool blocks_parsed = true;\n        for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) {\n            furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block);\n            if(!flipper_format_read_hex(\n                   ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) {\n                blocks_parsed = false;\n                break;\n            }\n        }\n        if(!blocks_parsed) break;\n\n        if(!flipper_format_read_hex(\n               ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4))\n            break;\n\n        parsed = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n\n    return parsed;\n}\n\nbool st25tb_save(const St25tbData* data, FlipperFormat* ff) {\n    furi_check(data);\n    furi_check(ff);\n\n    FuriString* temp_str = furi_string_alloc();\n    bool saved = false;\n\n    do {\n        if(!flipper_format_write_comment_cstr(ff, ST25TB_PROTOCOL_NAME \" specific data\")) break;\n        if(!flipper_format_write_string_cstr(\n               ff, ST25TB_TYPE_KEY, st25tb_features[data->type].type_name))\n            break;\n\n        bool blocks_saved = true;\n        for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) {\n            furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block);\n            if(!flipper_format_write_hex(\n                   ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) {\n                blocks_saved = false;\n                break;\n            }\n        }\n        if(!blocks_saved) break;\n\n        if(!flipper_format_write_hex(\n               ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4))\n            break;\n\n        saved = true;\n    } while(false);\n\n    furi_string_free(temp_str);\n    return saved;\n}\n\nbool st25tb_is_equal(const St25tbData* data, const St25tbData* other) {\n    furi_check(data);\n    furi_check(other);\n\n    return memcmp(data, other, sizeof(St25tbData)) == 0; //-V1103\n}\n\nuint8_t st25tb_get_block_count(St25tbType type) {\n    furi_check(type < St25tbTypeNum);\n\n    return st25tb_features[type].blocks_total;\n}\n\nconst char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type) {\n    furi_check(data);\n    furi_check(data->type < St25tbTypeNum);\n\n    if(name_type == NfcDeviceNameTypeFull) {\n        return st25tb_features[data->type].full_name;\n    } else {\n        return st25tb_features[data->type].type_name;\n    }\n}\n\nconst uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len) {\n    furi_check(data);\n\n    if(uid_len) {\n        *uid_len = ST25TB_UID_SIZE;\n    }\n\n    return data->uid;\n}\n\nbool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len) {\n    furi_check(data);\n    furi_check(uid);\n\n    const bool uid_valid = uid_len == ST25TB_UID_SIZE;\n\n    if(uid_valid) {\n        memcpy(data->uid, uid, uid_len);\n    }\n\n    return uid_valid;\n}\n\nSt25tbData* st25tb_get_base_data(const St25tbData* data) {\n    UNUSED(data);\n    furi_crash(\"No base data\");\n}\n\nSt25tbType st25tb_get_type_from_uid(const uint8_t* uid) {\n    furi_check(uid);\n\n    switch(uid[2] >> 2) {\n    case 0x0:\n    case 0x3:\n        return St25tbTypeX4k;\n    case 0x4:\n        return St25tbTypeX512;\n    case 0x6:\n        return St25tbType512Ac;\n    case 0x7:\n        return St25tbType04k;\n    case 0xc:\n        return St25tbType512At;\n    case 0xf:\n        return St25tbType02k;\n    default:\n        furi_crash(\"unsupported st25tb type\");\n    }\n}\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_device_base_i.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ST25TB_UID_SIZE (8U)\n\n//#define ST25TB_FDT_FC (4205U)\n#define ST25TB_FDT_FC           (8494U)\n#define ST25TB_GUARD_TIME_US    (5000U)\n#define ST25TB_POLL_POLL_MIN_US (1280U)\n\n#define ST25TB_MAX_BLOCKS       (128U)\n#define ST25TB_SYSTEM_OTP_BLOCK (0xFFU)\n#define ST25TB_BLOCK_SIZE       (4U)\n\ntypedef enum {\n    St25tbErrorNone,\n    St25tbErrorNotPresent,\n    St25tbErrorColResFailed,\n    St25tbErrorBufferOverflow,\n    St25tbErrorCommunication,\n    St25tbErrorFieldOff,\n    St25tbErrorWrongCrc,\n    St25tbErrorTimeout,\n    St25tbErrorWriteFailed,\n} St25tbError;\n\ntypedef enum {\n    St25tbType512At,\n    St25tbType512Ac,\n    St25tbTypeX512,\n    St25tbType02k,\n    St25tbType04k,\n    St25tbTypeX4k,\n    St25tbTypeNum,\n} St25tbType;\n\ntypedef struct {\n    uint8_t uid[ST25TB_UID_SIZE];\n    St25tbType type;\n    uint32_t blocks[ST25TB_MAX_BLOCKS];\n    uint32_t system_otp_block;\n} St25tbData;\n\nextern const NfcDeviceBase nfc_device_st25tb;\n\nSt25tbData* st25tb_alloc(void);\n\nvoid st25tb_free(St25tbData* data);\n\nvoid st25tb_reset(St25tbData* data);\n\nvoid st25tb_copy(St25tbData* data, const St25tbData* other);\n\nbool st25tb_verify(St25tbData* data, const FuriString* device_type);\n\nbool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version);\n\nbool st25tb_save(const St25tbData* data, FlipperFormat* ff);\n\nbool st25tb_is_equal(const St25tbData* data, const St25tbData* other);\n\nuint8_t st25tb_get_block_count(St25tbType type);\n\nconst char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type);\n\nconst uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len);\n\nbool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len);\n\nSt25tbData* st25tb_get_base_data(const St25tbData* data);\n\nSt25tbType st25tb_get_type_from_uid(const uint8_t* uid);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller.c",
    "content": "#include \"st25tb_poller.h\"\n#include \"st25tb_poller_i.h\"\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#define TAG \"ST25TBPoller\"\n\ntypedef NfcCommand (*St25tbPollerStateHandler)(St25tbPoller* instance);\n\nconst St25tbData* st25tb_poller_get_data(St25tbPoller* instance) {\n    furi_assert(instance);\n    furi_assert(instance->data);\n\n    return instance->data;\n}\n\nstatic St25tbPoller* st25tb_poller_alloc(Nfc* nfc) {\n    furi_assert(nfc);\n\n    St25tbPoller* instance = malloc(sizeof(St25tbPoller));\n    instance->nfc = nfc;\n    instance->state = St25tbPollerStateSelect;\n    instance->tx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE);\n    instance->rx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE);\n\n    // RF configuration is the same as 14b\n    nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b);\n    nfc_set_guard_time_us(instance->nfc, ST25TB_GUARD_TIME_US);\n    nfc_set_fdt_poll_fc(instance->nfc, ST25TB_FDT_FC);\n    nfc_set_fdt_poll_poll_us(instance->nfc, ST25TB_POLL_POLL_MIN_US);\n    instance->data = st25tb_alloc();\n\n    instance->st25tb_event.data = &instance->st25tb_event_data;\n    instance->general_event.protocol = NfcProtocolSt25tb;\n    instance->general_event.event_data = &instance->st25tb_event;\n    instance->general_event.instance = instance;\n\n    return instance;\n}\n\nstatic void st25tb_poller_free(St25tbPoller* instance) {\n    furi_assert(instance);\n\n    furi_assert(instance->tx_buffer);\n    furi_assert(instance->rx_buffer);\n    furi_assert(instance->data);\n\n    bit_buffer_free(instance->tx_buffer);\n    bit_buffer_free(instance->rx_buffer);\n    st25tb_free(instance->data);\n    free(instance);\n}\n\nstatic void\n    st25tb_poller_set_callback(St25tbPoller* instance, NfcGenericCallback callback, void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nstatic NfcCommand st25tb_poller_select_handler(St25tbPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n\n    do {\n        St25tbError error = st25tb_poller_select(instance, NULL);\n        if(error != St25tbErrorNone) {\n            instance->state = St25tbPollerStateFailure;\n            instance->st25tb_event_data.error = error;\n            break;\n        }\n\n        instance->st25tb_event.type = St25tbPollerEventTypeReady;\n        instance->st25tb_event.data->ready.type = instance->data->type;\n        command = instance->callback(instance->general_event, instance->context);\n        instance->state = St25tbPollerStateRequestMode;\n    } while(false);\n\n    return command;\n}\n\nstatic NfcCommand st25tb_poller_request_mode_handler(St25tbPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->st25tb_event.type = St25tbPollerEventTypeRequestMode;\n    command = instance->callback(instance->general_event, instance->context);\n\n    St25tbPollerEventDataModeRequest* mode_request_data =\n        &instance->st25tb_event_data.mode_request;\n\n    furi_check(mode_request_data->mode < St25tbPollerModeNum);\n\n    if(mode_request_data->mode == St25tbPollerModeRead) {\n        instance->state = St25tbPollerStateRead;\n        instance->poller_ctx.read.current_block = 0;\n    } else {\n        instance->state = St25tbPollerStateWrite;\n        instance->poller_ctx.write.block_number =\n            mode_request_data->params.write_params.block_number;\n        instance->poller_ctx.write.block_data = mode_request_data->params.write_params.block_data;\n    }\n\n    return command;\n}\n\nstatic NfcCommand st25tb_poller_read_handler(St25tbPoller* instance) {\n    St25tbError error = St25tbErrorNone;\n\n    do {\n        uint8_t total_blocks = st25tb_get_block_count(instance->data->type);\n        uint8_t* current_block = &instance->poller_ctx.read.current_block;\n        if(*current_block == total_blocks) {\n            error = st25tb_poller_read_block(\n                instance, &instance->data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK);\n            if(error != St25tbErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to read OTP block\");\n                instance->state = St25tbPollerStateFailure;\n                instance->st25tb_event_data.error = error;\n                break;\n            } else {\n                instance->state = St25tbPollerStateSuccess;\n                break;\n            }\n        } else {\n            error = st25tb_poller_read_block(\n                instance, &instance->data->blocks[*current_block], *current_block);\n            if(error != St25tbErrorNone) {\n                FURI_LOG_E(TAG, \"Failed to read block %d\", *current_block);\n                instance->state = St25tbPollerStateFailure;\n                instance->st25tb_event_data.error = error;\n                break;\n            }\n\n            *current_block += 1;\n        }\n    } while(false);\n\n    return NfcCommandContinue;\n}\n\nstatic NfcCommand st25tb_poller_write_handler(St25tbPoller* instance) {\n    St25tbPollerWriteContext* write_ctx = &instance->poller_ctx.write;\n    St25tbError error =\n        st25tb_poller_write_block(instance, write_ctx->block_data, write_ctx->block_number);\n\n    if(error == St25tbErrorNone) {\n        instance->state = St25tbPollerStateSuccess;\n    } else {\n        instance->state = St25tbPollerStateFailure;\n        instance->st25tb_event_data.error = error;\n    }\n\n    return NfcCommandContinue;\n}\n\nNfcCommand st25tb_poller_success_handler(St25tbPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->st25tb_event.type = St25tbPollerEventTypeSuccess;\n    command = instance->callback(instance->general_event, instance->context);\n    furi_delay_ms(100);\n    instance->state = St25tbPollerStateRequestMode;\n\n    return command;\n}\n\nNfcCommand st25tb_poller_failure_handler(St25tbPoller* instance) {\n    NfcCommand command = NfcCommandContinue;\n    instance->st25tb_event.type = St25tbPollerEventTypeFailure;\n    command = instance->callback(instance->general_event, instance->context);\n    furi_delay_ms(100);\n    instance->state = St25tbPollerStateSelect;\n\n    return command;\n}\n\nstatic St25tbPollerStateHandler st25tb_poller_state_handlers[St25tbPollerStateNum] = {\n    [St25tbPollerStateSelect] = st25tb_poller_select_handler,\n    [St25tbPollerStateRequestMode] = st25tb_poller_request_mode_handler,\n    [St25tbPollerStateRead] = st25tb_poller_read_handler,\n    [St25tbPollerStateWrite] = st25tb_poller_write_handler,\n    [St25tbPollerStateSuccess] = st25tb_poller_success_handler,\n    [St25tbPollerStateFailure] = st25tb_poller_failure_handler,\n};\n\nstatic NfcCommand st25tb_poller_run(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n    furi_assert(event.event_data);\n\n    St25tbPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    NfcCommand command = NfcCommandContinue;\n\n    furi_assert(instance->state < St25tbPollerStateNum);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        command = st25tb_poller_state_handlers[instance->state](instance);\n    }\n\n    return command;\n}\n\nstatic bool st25tb_poller_detect(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolInvalid);\n\n    bool protocol_detected = false;\n    St25tbPoller* instance = context;\n    NfcEvent* nfc_event = event.event_data;\n    furi_assert(instance->state == St25tbPollerStateSelect);\n\n    if(nfc_event->type == NfcEventTypePollerReady) {\n        St25tbError error = st25tb_poller_initiate(instance, NULL);\n        protocol_detected = (error == St25tbErrorNone);\n    }\n\n    return protocol_detected;\n}\n\nconst NfcPollerBase nfc_poller_st25tb = {\n    .alloc = (NfcPollerAlloc)st25tb_poller_alloc,\n    .free = (NfcPollerFree)st25tb_poller_free,\n    .set_callback = (NfcPollerSetCallback)st25tb_poller_set_callback,\n    .run = (NfcPollerRun)st25tb_poller_run,\n    .detect = (NfcPollerDetect)st25tb_poller_detect,\n    .get_data = (NfcPollerGetData)st25tb_poller_get_data,\n};\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller.h",
    "content": "#pragma once\n\n#include \"st25tb.h\"\n#include <lib/nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct St25tbPoller St25tbPoller;\n\ntypedef enum {\n    St25tbPollerEventTypeReady,\n    St25tbPollerEventTypeRequestMode,\n    St25tbPollerEventTypeFailure,\n    St25tbPollerEventTypeSuccess,\n} St25tbPollerEventType;\n\ntypedef struct {\n    St25tbType type;\n} St25tbPollerReadyData;\n\ntypedef enum {\n    St25tbPollerModeRead,\n    St25tbPollerModeWrite,\n\n    St25tbPollerModeNum,\n} St25tbPollerMode;\n\ntypedef struct {\n    uint8_t block_number;\n    uint32_t block_data;\n} St25tbPollerEventDataModeRequestWriteParams;\n\ntypedef union {\n    St25tbPollerEventDataModeRequestWriteParams write_params;\n} St25tbPollerEventDataModeRequestParams;\n\ntypedef struct {\n    St25tbPollerMode mode;\n    St25tbPollerEventDataModeRequestParams params;\n} St25tbPollerEventDataModeRequest;\n\ntypedef union {\n    St25tbPollerReadyData ready;\n    St25tbPollerEventDataModeRequest mode_request;\n    St25tbError error;\n} St25tbPollerEventData;\n\ntypedef struct {\n    St25tbPollerEventType type;\n    St25tbPollerEventData* data;\n} St25tbPollerEvent;\n\nSt25tbError st25tb_poller_send_frame(\n    St25tbPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt);\n\nSt25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr);\n\nSt25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr);\n\nSt25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid);\n\nSt25tbError\n    st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number);\n\nSt25tbError\n    st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number);\n\nSt25tbError st25tb_poller_halt(St25tbPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller_defs.h",
    "content": "#pragma once\n\n#include <nfc/protocols/nfc_poller_base.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const NfcPollerBase nfc_poller_st25tb;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller_i.c",
    "content": "#include \"st25tb_poller_i.h\"\n\n#include <nfc/helpers/iso14443_crc.h>\n\n#define TAG \"ST25TBPoller\"\n\nstatic St25tbError st25tb_poller_process_error(NfcError error) {\n    switch(error) {\n    case NfcErrorNone:\n        return St25tbErrorNone;\n    case NfcErrorTimeout:\n        return St25tbErrorTimeout;\n    default:\n        return St25tbErrorNotPresent;\n    }\n}\n\nSt25tbError st25tb_poller_send_frame(\n    St25tbPoller* instance,\n    const BitBuffer* tx_buffer,\n    BitBuffer* rx_buffer,\n    uint32_t fwt) {\n    furi_check(instance);\n    furi_check(tx_buffer);\n    furi_check(rx_buffer);\n\n    const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);\n    furi_assert(\n        tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);\n\n    bit_buffer_copy(instance->tx_buffer, tx_buffer);\n    iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);\n\n    St25tbError ret = St25tbErrorNone;\n\n    do {\n        NfcError error =\n            nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);\n        if(error != NfcErrorNone) {\n            FURI_LOG_T(TAG, \"error during trx: %d\", error);\n            ret = st25tb_poller_process_error(error);\n            break;\n        }\n\n        bit_buffer_copy(rx_buffer, instance->rx_buffer);\n        if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {\n            ret = St25tbErrorWrongCrc;\n            break;\n        }\n\n        iso14443_crc_trim(rx_buffer);\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr) {\n    // Send Initiate()\n    furi_check(instance);\n    furi_check(instance->nfc);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n    bit_buffer_append_byte(instance->tx_buffer, 0x06);\n    bit_buffer_append_byte(instance->tx_buffer, 0x00);\n\n    St25tbError ret;\n    do {\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) {\n            FURI_LOG_E(TAG, \"Unexpected Initiate response size\");\n            ret = St25tbErrorCommunication;\n            break;\n        }\n        uint8_t chip_id = bit_buffer_get_byte(instance->rx_buffer, 0);\n        FURI_LOG_D(TAG, \"Got chip_id=0x%02X\", chip_id);\n        if(chip_id_ptr) {\n            *chip_id_ptr = bit_buffer_get_byte(instance->rx_buffer, 0);\n        }\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n\n    St25tbError ret;\n\n    do {\n        uint8_t chip_id;\n\n        if(chip_id_ptr != NULL) {\n            chip_id = *chip_id_ptr;\n        } else {\n            ret = st25tb_poller_initiate(instance, &chip_id);\n            if(ret != St25tbErrorNone) {\n                break;\n            }\n        }\n\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        // Send Select(Chip_ID), let's just assume that collisions won't ever happen :D\n        bit_buffer_append_byte(instance->tx_buffer, 0x0E);\n        bit_buffer_append_byte(instance->tx_buffer, chip_id);\n\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) {\n            FURI_LOG_E(TAG, \"Unexpected Select response size\");\n            ret = St25tbErrorCommunication;\n            break;\n        }\n\n        if(bit_buffer_get_byte(instance->rx_buffer, 0) != chip_id) {\n            FURI_LOG_E(TAG, \"ChipID mismatch\");\n            ret = St25tbErrorColResFailed;\n            break;\n        }\n\n        ret = st25tb_poller_get_uid(instance, instance->data->uid);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n\n        instance->data->type = st25tb_get_type_from_uid(instance->data->uid);\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError st25tb_poller_read(St25tbPoller* instance, St25tbData* data) {\n    furi_assert(instance);\n    furi_assert(instance->nfc);\n\n    St25tbError ret;\n\n    memcpy(data, instance->data, sizeof(St25tbData));\n\n    do {\n        bool read_blocks = true;\n        for(uint8_t i = 0; i < st25tb_get_block_count(data->type); i++) {\n            ret = st25tb_poller_read_block(instance, &data->blocks[i], i);\n            if(ret != St25tbErrorNone) {\n                read_blocks = false;\n                break;\n            }\n        }\n        if(!read_blocks) {\n            break;\n        }\n        ret = st25tb_poller_read_block(instance, &data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(uid);\n\n    St25tbError ret;\n\n    do {\n        bit_buffer_reset(instance->tx_buffer);\n        bit_buffer_reset(instance->rx_buffer);\n\n        bit_buffer_append_byte(instance->tx_buffer, 0x0B);\n\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_UID_SIZE) {\n            FURI_LOG_E(TAG, \"Unexpected Get_UID() response size\");\n            ret = St25tbErrorCommunication;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, uid, ST25TB_UID_SIZE);\n        FURI_SWAP(uid[0], uid[7]);\n        FURI_SWAP(uid[1], uid[6]);\n        FURI_SWAP(uid[2], uid[5]);\n        FURI_SWAP(uid[3], uid[4]);\n        FURI_LOG_I(\n            TAG,\n            \"Got tag with uid: %02X %02X %02X %02X %02X %02X %02X %02X\",\n            uid[0],\n            uid[1],\n            uid[2],\n            uid[3],\n            uid[4],\n            uid[5],\n            uid[6],\n            uid[7]);\n    } while(false);\n    return ret;\n}\n\nSt25tbError\n    st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(block);\n    furi_check(\n        (block_number <= st25tb_get_block_count(instance->data->type)) ||\n        block_number == ST25TB_SYSTEM_OTP_BLOCK);\n    FURI_LOG_T(TAG, \"reading block %d\", block_number);\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    // Send Read_block(Addr)\n    bit_buffer_append_byte(instance->tx_buffer, 0x08);\n    bit_buffer_append_byte(instance->tx_buffer, block_number);\n    St25tbError ret;\n    do {\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorNone) {\n            break;\n        }\n\n        if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_BLOCK_SIZE) {\n            FURI_LOG_E(TAG, \"Unexpected Read_block(Addr) response size\");\n            ret = St25tbErrorCommunication;\n            break;\n        }\n        bit_buffer_write_bytes(instance->rx_buffer, block, ST25TB_BLOCK_SIZE);\n        FURI_LOG_D(TAG, \"Read_block(%d) result: %08lX\", block_number, *block);\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError\n    st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number) {\n    furi_check(instance);\n    furi_check(instance->nfc);\n    furi_check(\n        (block_number <= st25tb_get_block_count(instance->data->type)) ||\n        block_number == ST25TB_SYSTEM_OTP_BLOCK);\n    FURI_LOG_T(TAG, \"writing block %d\", block_number);\n    bit_buffer_reset(instance->tx_buffer);\n\n    // Send Write_block(Addr, Data)\n    bit_buffer_append_byte(instance->tx_buffer, 0x09);\n    bit_buffer_append_byte(instance->tx_buffer, block_number);\n    bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block, ST25TB_BLOCK_SIZE);\n    St25tbError ret;\n    do {\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorTimeout) { // tag doesn't ack writes so timeout are expected.\n            break;\n        }\n\n        furi_delay_ms(7); // 7ms is the max programming time as per datasheet\n\n        uint32_t block_check;\n        ret = st25tb_poller_read_block(instance, &block_check, block_number);\n        if(ret != St25tbErrorNone) {\n            FURI_LOG_E(TAG, \"write verification failed: read error\");\n            break;\n        }\n        if(block_check != block) {\n            FURI_LOG_E(\n                TAG,\n                \"write verification failed: wrote %08lX but read back %08lX\",\n                block,\n                block_check);\n            ret = St25tbErrorWriteFailed;\n            break;\n        }\n        FURI_LOG_D(TAG, \"wrote %08lX to block %d\", block, block_number);\n    } while(false);\n\n    return ret;\n}\n\nSt25tbError st25tb_poller_halt(St25tbPoller* instance) {\n    furi_check(instance);\n\n    bit_buffer_reset(instance->tx_buffer);\n    bit_buffer_reset(instance->rx_buffer);\n\n    // Send Completion()\n    bit_buffer_append_byte(instance->tx_buffer, 0x0F);\n\n    St25tbError ret;\n\n    do {\n        ret = st25tb_poller_send_frame(\n            instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);\n        if(ret != St25tbErrorTimeout) {\n            break;\n        }\n\n        instance->state = St25tbPollerStateSelect;\n    } while(false);\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller_i.h",
    "content": "#pragma once\n\n#include \"st25tb_poller.h\"\n\n#include <nfc/nfc_poller.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ST25TB_POLLER_MAX_BUFFER_SIZE (16U)\n\ntypedef enum {\n    St25tbPollerStateSelect,\n    St25tbPollerStateRequestMode,\n    St25tbPollerStateRead,\n    St25tbPollerStateWrite,\n    St25tbPollerStateSuccess,\n    St25tbPollerStateFailure,\n\n    St25tbPollerStateNum,\n} St25tbPollerState;\n\ntypedef struct {\n    uint8_t current_block;\n} St25tbPollerReadContext;\n\ntypedef struct {\n    uint8_t block_number;\n    uint32_t block_data;\n} St25tbPollerWriteContext;\n\ntypedef union {\n    St25tbPollerReadContext read;\n    St25tbPollerWriteContext write;\n} St25tbPollerContext;\n\nstruct St25tbPoller {\n    Nfc* nfc;\n    St25tbPollerState state;\n    St25tbData* data;\n    BitBuffer* tx_buffer;\n    BitBuffer* rx_buffer;\n\n    St25tbPollerContext poller_ctx;\n\n    NfcGenericEvent general_event;\n    St25tbPollerEvent st25tb_event;\n    St25tbPollerEventData st25tb_event_data;\n    NfcGenericCallback callback;\n    void* context;\n};\n\nconst St25tbData* st25tb_poller_get_data(St25tbPoller* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller_sync.c",
    "content": "#include \"st25tb_poller_sync.h\"\n#include \"st25tb_poller_i.h\"\n\n#define ST25TB_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)\n\ntypedef enum {\n    St25tbPollerCmdTypeDetectType,\n    St25tbPollerCmdTypeReadBlock,\n    St25tbPollerCmdTypeWriteBlock,\n\n    St25tbPollerCmdTypeNum,\n} St25tbPollerCmdType;\n\ntypedef struct {\n    St25tbType* type;\n} St25tbPollerCmdDetectTypeData;\n\ntypedef struct {\n    St25tbData* data;\n} St25tbPollerCmdReadData;\n\ntypedef struct {\n    uint8_t block_num;\n    uint32_t* block;\n} St25tbPollerCmdReadBlockData;\n\ntypedef struct {\n    uint8_t block_num;\n    uint32_t block;\n} St25tbPollerCmdWriteBlockData;\n\ntypedef union {\n    St25tbPollerCmdDetectTypeData detect_type;\n    St25tbPollerCmdReadData read;\n    St25tbPollerCmdReadBlockData read_block;\n    St25tbPollerCmdWriteBlockData write_block;\n} St25tbPollerCmdData;\n\ntypedef struct {\n    FuriThreadId thread_id;\n    St25tbError error;\n    St25tbPollerCmdType cmd_type;\n    St25tbPollerCmdData cmd_data;\n} St25tbPollerSyncContext;\n\ntypedef St25tbError (*St25tbPollerCmdHandler)(St25tbPoller* poller, St25tbPollerCmdData* data);\n\nstatic St25tbError st25tb_poller_detect_handler(St25tbPoller* poller, St25tbPollerCmdData* data) {\n    uint8_t uid[ST25TB_UID_SIZE];\n    St25tbError error = st25tb_poller_get_uid(poller, uid);\n    if(error == St25tbErrorNone) {\n        *data->detect_type.type = st25tb_get_type_from_uid(uid);\n    }\n    return error;\n}\n\nstatic St25tbError\n    st25tb_poller_read_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) {\n    return st25tb_poller_read_block(poller, data->read_block.block, data->read_block.block_num);\n}\n\nstatic St25tbError\n    st25tb_poller_write_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) {\n    return st25tb_poller_write_block(poller, data->write_block.block, data->write_block.block_num);\n}\n\nstatic St25tbPollerCmdHandler st25tb_poller_cmd_handlers[St25tbPollerCmdTypeNum] = {\n    [St25tbPollerCmdTypeDetectType] = st25tb_poller_detect_handler,\n    [St25tbPollerCmdTypeReadBlock] = st25tb_poller_read_block_handler,\n    [St25tbPollerCmdTypeWriteBlock] = st25tb_poller_write_block_handler,\n};\n\nstatic NfcCommand st25tb_poller_cmd_callback(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolSt25tb);\n\n    St25tbPollerSyncContext* poller_context = context;\n    St25tbPoller* st25tb_poller = event.instance;\n    St25tbPollerEvent* st25tb_event = event.event_data;\n\n    if(st25tb_event->type == St25tbPollerEventTypeReady) {\n        poller_context->error = st25tb_poller_cmd_handlers[poller_context->cmd_type](\n            st25tb_poller, &poller_context->cmd_data);\n    } else {\n        poller_context->error = st25tb_event->data->error;\n    }\n\n    furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE);\n\n    return NfcCommandStop;\n}\n\nstatic St25tbError st25tb_poller_cmd_execute(Nfc* nfc, St25tbPollerSyncContext* poller_ctx) {\n    furi_assert(nfc);\n    furi_assert(poller_ctx->cmd_type < St25tbPollerCmdTypeNum);\n    poller_ctx->thread_id = furi_thread_get_current_id();\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb);\n    nfc_poller_start(poller, st25tb_poller_cmd_callback, poller_ctx);\n    furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    return poller_ctx->error;\n}\n\nSt25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block) {\n    furi_check(nfc);\n    furi_check(block);\n    St25tbPollerSyncContext poller_context = {\n        .cmd_type = St25tbPollerCmdTypeReadBlock,\n        .cmd_data =\n            {\n                .read_block =\n                    {\n                        .block = block,\n                        .block_num = block_num,\n                    },\n            },\n    };\n    return st25tb_poller_cmd_execute(nfc, &poller_context);\n}\n\nSt25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block) {\n    furi_check(nfc);\n    St25tbPollerSyncContext poller_context = {\n        .cmd_type = St25tbPollerCmdTypeWriteBlock,\n        .cmd_data =\n            {\n                .write_block =\n                    {\n                        .block = block,\n                        .block_num = block_num,\n                    },\n            },\n    };\n    return st25tb_poller_cmd_execute(nfc, &poller_context);\n}\n\nSt25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type) {\n    furi_check(nfc);\n    furi_check(type);\n    St25tbPollerSyncContext poller_context = {\n        .cmd_type = St25tbPollerCmdTypeDetectType,\n        .cmd_data =\n            {\n                .detect_type =\n                    {\n                        .type = type,\n                    },\n            },\n    };\n    return st25tb_poller_cmd_execute(nfc, &poller_context);\n}\n\nstatic NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.event_data);\n    furi_assert(event.instance);\n    furi_assert(event.protocol == NfcProtocolSt25tb);\n\n    St25tbPollerSyncContext* poller_context = context;\n    St25tbPollerEvent* st25tb_event = event.event_data;\n\n    NfcCommand command = NfcCommandContinue;\n    if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {\n        st25tb_event->data->mode_request.mode = St25tbPollerModeRead;\n    } else if(\n        st25tb_event->type == St25tbPollerEventTypeSuccess ||\n        st25tb_event->type == St25tbPollerEventTypeFailure) {\n        if(st25tb_event->type == St25tbPollerEventTypeSuccess) {\n            memcpy(\n                poller_context->cmd_data.read.data,\n                st25tb_poller_get_data(event.instance),\n                sizeof(St25tbData));\n        } else {\n            poller_context->error = st25tb_event->data->error;\n        }\n        command = NfcCommandStop;\n        furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE);\n    }\n\n    return command;\n}\n\nSt25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data) {\n    furi_check(nfc);\n    furi_check(data);\n\n    St25tbPollerSyncContext poller_context = {\n        .thread_id = furi_thread_get_current_id(),\n        .cmd_data =\n            {\n                .read =\n                    {\n                        .data = data,\n                    },\n            },\n    };\n\n    NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb);\n    nfc_poller_start(poller, nfc_scene_read_poller_callback_st25tb, &poller_context);\n    furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);\n    furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE);\n\n    nfc_poller_stop(poller);\n    nfc_poller_free(poller);\n\n    return poller_context.error;\n}\n"
  },
  {
    "path": "lib/nfc/protocols/st25tb/st25tb_poller_sync.h",
    "content": "#pragma once\n\n#include \"st25tb.h\"\n#include <nfc/nfc.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nSt25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block);\n\nSt25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block);\n\nSt25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type);\n\nSt25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/one_wire/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    CPPPATH=[\n        \"#/lib/one_wire\",\n    ],\n    SDK_HEADERS=[\n        File(\"one_wire_host.h\"),\n        File(\"one_wire_slave.h\"),\n        File(\"maxim_crc.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"one_wire\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/one_wire/maxim_crc.c",
    "content": "#include \"maxim_crc.h\"\n#include <furi.h>\n\nuint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init) {\n    furi_check(data);\n\n    uint8_t crc = crc_init;\n\n    for(uint8_t index = 0; index < data_size; ++index) {\n        uint8_t input_byte = data[index];\n        for(uint8_t bit_position = 0; bit_position < 8; ++bit_position) {\n            const uint8_t mix = (crc ^ input_byte) & (uint8_t)(0x01);\n            crc >>= 1;\n            if(mix != 0) crc ^= 0x8C;\n            input_byte >>= 1;\n        }\n    }\n    return crc;\n}\n"
  },
  {
    "path": "lib/one_wire/maxim_crc.h",
    "content": "#pragma once\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAXIM_CRC8_INIT 0\n\nuint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/one_wire/one_wire_host.c",
    "content": "#include <furi.h>\n\n/**\n * Timings based on Application Note 126:\n * https://www.analog.com/media/en/technical-documentation/tech-articles/1wire-communication-through-software--maxim-integrated.pdf\n */\n\n#include \"one_wire_host.h\"\n\ntypedef struct {\n    uint16_t a; // Write 1 low time\n    uint16_t b; // Write 1 high time\n    uint16_t c; // Write 0 low time\n    uint16_t d; // Write 0 high time\n    uint16_t e; // Read low time\n    uint16_t f; // Read high time\n    uint16_t g; // Reset pre-delay\n    uint16_t h; // Reset pulse\n    uint16_t i; // Presence detect\n    uint16_t j; // Reset post-delay\n} OneWireHostTimings;\n\nstatic const OneWireHostTimings onewire_host_timings_normal = {\n    .a = 9,\n    .b = 64,\n    .c = 64,\n    .d = 14,\n    .e = 9,\n    .f = 55,\n    .g = 0,\n    .h = 480,\n    .i = 70,\n    .j = 410,\n};\n\nstatic const OneWireHostTimings onewire_host_timings_overdrive = {\n    .a = 1,\n    .b = 8,\n    .c = 8,\n    .d = 3,\n    .e = 1,\n    .f = 7,\n    .g = 3,\n    .h = 70,\n    .i = 9,\n    .j = 40,\n};\n\n// TM01x specific timings\nstatic const OneWireHostTimings onewire_host_timings_tm01x = {\n    .a = 5,\n    .b = 80,\n    .c = 70,\n    .d = 10,\n    .e = 5,\n    .f = 70,\n    .g = 0,\n    .h = 740,\n    .i = 140,\n    .j = 410,\n};\n\nstruct OneWireHost {\n    const GpioPin* gpio_pin;\n    const OneWireHostTimings* timings;\n    unsigned char saved_rom[8]; /** < global search state */\n    uint8_t last_discrepancy;\n    uint8_t last_family_discrepancy;\n    bool last_device_flag;\n};\n\nOneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) {\n    furi_check(gpio_pin);\n\n    OneWireHost* host = malloc(sizeof(OneWireHost));\n    host->gpio_pin = gpio_pin;\n    onewire_host_reset_search(host);\n    onewire_host_set_overdrive(host, false);\n\n    return host;\n}\n\nvoid onewire_host_free(OneWireHost* host) {\n    furi_check(host);\n\n    onewire_host_stop(host);\n    free(host);\n}\n\nbool onewire_host_reset(OneWireHost* host) {\n    furi_check(host);\n\n    uint8_t r;\n    uint8_t retries = 125;\n\n    const OneWireHostTimings* timings = host->timings;\n\n    // wait until the gpio is high\n    furi_hal_gpio_write(host->gpio_pin, true);\n    do {\n        if(--retries == 0) return 0;\n        furi_delay_us(2);\n    } while(!furi_hal_gpio_read(host->gpio_pin));\n\n    // pre delay\n    furi_delay_us(timings->g);\n\n    // drive low\n    furi_hal_gpio_write(host->gpio_pin, false);\n    furi_delay_us(timings->h);\n\n    // release\n    furi_hal_gpio_write(host->gpio_pin, true);\n    furi_delay_us(timings->i);\n\n    // read and post delay\n    r = !furi_hal_gpio_read(host->gpio_pin);\n    furi_delay_us(timings->j);\n\n    return r;\n}\n\nbool onewire_host_read_bit(OneWireHost* host) {\n    furi_check(host);\n\n    bool result;\n\n    const OneWireHostTimings* timings = host->timings;\n\n    // drive low\n    furi_hal_gpio_write(host->gpio_pin, false);\n    furi_delay_us(timings->a);\n\n    // release\n    furi_hal_gpio_write(host->gpio_pin, true);\n    furi_delay_us(timings->e);\n\n    // read and post delay\n    result = furi_hal_gpio_read(host->gpio_pin);\n    furi_delay_us(timings->f);\n\n    return result;\n}\n\nuint8_t onewire_host_read(OneWireHost* host) {\n    furi_check(host);\n\n    uint8_t result = 0;\n\n    for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {\n        if(onewire_host_read_bit(host)) {\n            result |= bitMask;\n        }\n    }\n\n    return result;\n}\n\nvoid onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) {\n    furi_check(host);\n    furi_check(buffer);\n\n    for(uint16_t i = 0; i < count; i++) {\n        buffer[i] = onewire_host_read(host);\n    }\n}\n\nvoid onewire_host_write_bit(OneWireHost* host, bool value) {\n    furi_check(host);\n\n    const OneWireHostTimings* timings = host->timings;\n\n    if(value) {\n        // drive low\n        furi_hal_gpio_write(host->gpio_pin, false);\n        furi_delay_us(timings->a);\n\n        // release\n        furi_hal_gpio_write(host->gpio_pin, true);\n        furi_delay_us(timings->b);\n    } else {\n        // drive low\n        furi_hal_gpio_write(host->gpio_pin, false);\n        furi_delay_us(timings->c);\n\n        // release\n        furi_hal_gpio_write(host->gpio_pin, true);\n        furi_delay_us(timings->d);\n    }\n}\n\nvoid onewire_host_write(OneWireHost* host, uint8_t value) {\n    furi_check(host);\n\n    uint8_t bitMask;\n\n    for(bitMask = 0x01; bitMask; bitMask <<= 1) {\n        onewire_host_write_bit(host, (bitMask & value) ? 1 : 0);\n    }\n}\n\nvoid onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count) {\n    furi_check(host);\n    furi_check(buffer);\n\n    for(uint16_t i = 0; i < count; ++i) {\n        onewire_host_write(host, buffer[i]);\n    }\n}\n\nvoid onewire_host_start(OneWireHost* host) {\n    furi_check(host);\n\n    furi_hal_gpio_write(host->gpio_pin, true);\n    furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);\n}\n\nvoid onewire_host_stop(OneWireHost* host) {\n    furi_check(host);\n\n    furi_hal_gpio_write(host->gpio_pin, true);\n    furi_hal_gpio_init(host->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n}\n\nvoid onewire_host_reset_search(OneWireHost* host) {\n    furi_check(host);\n\n    host->last_discrepancy = 0;\n    host->last_device_flag = false;\n    host->last_family_discrepancy = 0;\n    for(int i = 7;; i--) {\n        host->saved_rom[i] = 0;\n        if(i == 0) break;\n    }\n}\n\nvoid onewire_host_target_search(OneWireHost* host, uint8_t family_code) {\n    furi_check(host);\n\n    host->saved_rom[0] = family_code;\n    for(uint8_t i = 1; i < 8; i++)\n        host->saved_rom[i] = 0;\n    host->last_discrepancy = 64;\n    host->last_family_discrepancy = 0;\n    host->last_device_flag = false;\n}\n\nbool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) {\n    furi_check(host);\n\n    uint8_t id_bit_number;\n    uint8_t last_zero, rom_byte_number, search_result;\n    uint8_t id_bit, cmp_id_bit;\n\n    unsigned char rom_byte_mask, search_direction;\n\n    // initialize for search\n    id_bit_number = 1;\n    last_zero = 0;\n    rom_byte_number = 0;\n    rom_byte_mask = 1;\n    search_result = 0;\n\n    // if the last call was not the last one\n    if(!host->last_device_flag) {\n        // 1-Wire reset\n        if(!onewire_host_reset(host)) {\n            // reset the search\n            host->last_discrepancy = 0;\n            host->last_device_flag = false;\n            host->last_family_discrepancy = 0;\n            return false;\n        }\n\n        // issue the search command\n        switch(mode) {\n        case OneWireHostSearchModeConditional:\n            onewire_host_write(host, 0xEC);\n            break;\n        case OneWireHostSearchModeNormal:\n            onewire_host_write(host, 0xF0);\n            break;\n        }\n\n        // loop to do the search\n        do {\n            // read a bit and its complement\n            id_bit = onewire_host_read_bit(host);\n            cmp_id_bit = onewire_host_read_bit(host);\n\n            // check for no devices on 1-wire\n            if((id_bit == 1) && (cmp_id_bit == 1))\n                break;\n            else {\n                // all devices coupled have 0 or 1\n                if(id_bit != cmp_id_bit)\n                    search_direction = id_bit; // bit write value for search\n                else {\n                    // if this discrepancy if before the Last Discrepancy\n                    // on a previous next then pick the same as last time\n                    if(id_bit_number < host->last_discrepancy)\n                        search_direction =\n                            ((host->saved_rom[rom_byte_number] & rom_byte_mask) > 0);\n                    else\n                        // if equal to last pick 1, if not then pick 0\n                        search_direction = (id_bit_number == host->last_discrepancy);\n\n                    // if 0 was picked then record its position in LastZero\n                    if(search_direction == 0) {\n                        last_zero = id_bit_number;\n\n                        // check for Last discrepancy in family\n                        if(last_zero < 9) host->last_family_discrepancy = last_zero;\n                    }\n                }\n\n                // set or clear the bit in the ROM byte rom_byte_number\n                // with mask rom_byte_mask\n                if(search_direction == 1)\n                    host->saved_rom[rom_byte_number] |= rom_byte_mask;\n                else\n                    host->saved_rom[rom_byte_number] &= ~rom_byte_mask;\n\n                // serial number search direction write bit\n                onewire_host_write_bit(host, search_direction);\n\n                // increment the byte counter id_bit_number\n                // and shift the mask rom_byte_mask\n                id_bit_number++;\n                rom_byte_mask <<= 1;\n\n                // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask\n                if(rom_byte_mask == 0) {\n                    rom_byte_number++;\n                    rom_byte_mask = 1;\n                }\n            }\n        } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7\n\n        // if the search was successful then\n        if(!(id_bit_number < 65)) {\n            // search successful so set last_Discrepancy, last_device_flag, search_result\n            host->last_discrepancy = last_zero;\n\n            // check for last device\n            if(host->last_discrepancy == 0) host->last_device_flag = true;\n\n            search_result = true;\n        }\n    }\n\n    // if no device found then reset counters so next 'search' will be like a first\n    if(!search_result || !host->saved_rom[0]) {\n        host->last_discrepancy = 0;\n        host->last_device_flag = false;\n        host->last_family_discrepancy = 0;\n        search_result = false;\n    } else {\n        for(int i = 0; i < 8; i++)\n            new_addr[i] = host->saved_rom[i];\n    }\n\n    return search_result;\n}\n\nvoid onewire_host_set_overdrive(OneWireHost* host, bool set) {\n    furi_check(host);\n\n    host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal;\n}\n\nvoid onewire_host_set_timings_default(OneWireHost* host) {\n    furi_check(host);\n\n    host->timings = &onewire_host_timings_normal;\n}\n\nvoid onewire_host_set_timings_tm01x(OneWireHost* host) {\n    furi_check(host);\n\n    host->timings = &onewire_host_timings_tm01x;\n}\n"
  },
  {
    "path": "lib/one_wire/one_wire_host.h",
    "content": "/**\n * @file one_wire_host.h\n * \n * 1-Wire host (master) library\n */\n\n#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */\n    OneWireHostSearchModeNormal = 1, /**< Search for all devices */\n} OneWireHostSearchMode;\n\ntypedef struct OneWireHost OneWireHost;\n\n/**\n * Allocate OneWireHost instance\n * @param [in] gpio_pin connection pin\n * @return pointer to OneWireHost instance\n */\nOneWireHost* onewire_host_alloc(const GpioPin* gpio_pin);\n\n/**\n * Destroy OneWireHost instance, free resources\n * @param [in] host pointer to OneWireHost instance\n */\nvoid onewire_host_free(OneWireHost* host);\n\n/**\n * Reset the 1-Wire bus\n * @param [in] host pointer to OneWireHost instance\n * @return true if presence was detected, false otherwise\n */\nbool onewire_host_reset(OneWireHost* host);\n\n/**\n * Read one bit\n * @param [in] host pointer to OneWireHost instance\n * @return received bit value\n */\nbool onewire_host_read_bit(OneWireHost* host);\n\n/**\n * Read one byte\n * @param [in] host pointer to OneWireHost instance\n * @return received byte value\n */\nuint8_t onewire_host_read(OneWireHost* host);\n\n/**\n * Read one or more bytes\n * @param [in] host pointer to OneWireHost instance\n * @param [out] buffer received data buffer\n * @param [in] count number of bytes to read\n */\nvoid onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count);\n\n/**\n * Write one bit\n * @param [in] host pointer to OneWireHost instance\n * @param value bit value to write\n */\nvoid onewire_host_write_bit(OneWireHost* host, bool value);\n\n/**\n * Write one byte\n * @param [in] host pointer to OneWireHost instance\n * @param value byte value to write\n */\nvoid onewire_host_write(OneWireHost* host, uint8_t value);\n\n/**\n * Write one or more bytes\n * @param [in] host pointer to OneWireHost instance\n * @param [in] buffer pointer to the data to write\n * @param [in] count size of the data to write\n */\nvoid onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count);\n\n/**\n * Start working with the bus\n * @param [in] host pointer to OneWireHost instance\n */\nvoid onewire_host_start(OneWireHost* host);\n\n/**\n * Stop working with the bus\n * @param [in] host pointer to OneWireHost instance\n */\nvoid onewire_host_stop(OneWireHost* host);\n\n/**\n * Reset previous search results\n * @param [in] host pointer to OneWireHost instance\n */\nvoid onewire_host_reset_search(OneWireHost* host);\n\n/**\n * Set the family code to search for\n * @param [in] host pointer to OneWireHost instance\n * @param [in] family_code device family code\n */\nvoid onewire_host_target_search(OneWireHost* host, uint8_t family_code);\n\n/**\n * Search for devices on the 1-Wire bus\n * @param [in] host pointer to OneWireHost instance\n * @param [out] new_addr pointer to the buffer to contain the unique ROM of the found device\n * @param [in] mode search mode\n * @return true on success, false otherwise\n */\nbool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode);\n\n/**\n * Enable overdrive mode\n * @param [in] host pointer to OneWireHost instance\n * @param [in] set true to turn overdrive on, false to turn it off\n */\nvoid onewire_host_set_overdrive(OneWireHost* host, bool set);\n\nvoid onewire_host_set_timings_default(OneWireHost* host);\n\nvoid onewire_host_set_timings_tm01x(OneWireHost* host);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/one_wire/one_wire_slave.c",
    "content": "#include \"one_wire_slave.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#define TH_TIMEOUT_MAX 15000 /* Maximum time before general timeout */\n\ntypedef enum {\n    OneWireSlaveErrorNone = 0,\n    OneWireSlaveErrorResetInProgress,\n    OneWireSlaveErrorPresenceConflict,\n    OneWireSlaveErrorInvalidCommand,\n    OneWireSlaveErrorTimeout,\n} OneWireSlaveError;\n\ntypedef struct {\n    uint16_t trstl_min; /* Minimum Reset Low time */\n    uint16_t trstl_max; /* Maximum Reset Low time */\n\n    uint16_t tpdh_typ; /* Typical Presence Detect High time */\n    uint16_t tpdl_min; /* Minimum Presence Detect Low time */\n    uint16_t tpdl_max; /* Maximum Presence Detect Low time */\n\n    uint16_t tslot_min; /* Minimum Read/Write Slot time */\n    uint16_t tslot_max; /* Maximum Read/Write Slot time */\n\n    uint16_t tw1l_max; /* Maximum Master Write 1 time */\n    uint16_t trl_tmsr_max; /* Maximum Master Read Low + Read Sample time */\n} OneWireSlaveTimings;\n\nstruct OneWireSlave {\n    const GpioPin* gpio_pin;\n    const OneWireSlaveTimings* timings;\n    OneWireSlaveError error;\n\n    bool is_first_reset;\n    bool is_short_reset;\n\n    OneWireSlaveResetCallback reset_callback;\n    OneWireSlaveCommandCallback command_callback;\n    OneWireSlaveResultCallback result_callback;\n\n    void* reset_callback_context;\n    void* result_callback_context;\n    void* command_callback_context;\n};\n\nstatic const OneWireSlaveTimings onewire_slave_timings_normal = {\n    .trstl_min = 270,\n    .trstl_max = 1200,\n\n    .tpdh_typ = 20,\n    .tpdl_min = 100,\n    .tpdl_max = 480,\n\n    .tslot_min = 60,\n    .tslot_max = 135,\n\n    .tw1l_max = 20,\n    .trl_tmsr_max = 30,\n};\n\nstatic const OneWireSlaveTimings onewire_slave_timings_overdrive = {\n    .trstl_min = 48,\n    .trstl_max = 80,\n\n    .tpdh_typ = 0,\n    .tpdl_min = 8,\n    .tpdl_max = 24,\n\n    .tslot_min = 6,\n    .tslot_max = 16,\n\n    .tw1l_max = 2,\n    .trl_tmsr_max = 3,\n};\n\n/*********************** PRIVATE ***********************/\n\nstatic bool\n    onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time_us, const bool pin_value) {\n    const uint32_t time_start = DWT->CYCCNT;\n    const uint32_t time_ticks = time_us * furi_hal_cortex_instructions_per_microsecond();\n\n    uint32_t time_elapsed;\n\n    do { //-V1044\n        time_elapsed = DWT->CYCCNT - time_start;\n        if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) {\n            return time_ticks >= time_elapsed;\n        }\n    } while(time_elapsed < time_ticks);\n\n    return false;\n}\n\nstatic inline bool onewire_slave_show_presence(OneWireSlave* bus) {\n    const OneWireSlaveTimings* timings = bus->timings;\n    // wait until the bus is high (might return immediately)\n    onewire_slave_wait_while_gpio_is(bus, timings->trstl_max, false);\n    // wait while master delay presence check\n    furi_delay_us(timings->tpdh_typ);\n\n    // show presence\n    furi_hal_gpio_write(bus->gpio_pin, false);\n    furi_delay_us(timings->tpdl_min);\n    furi_hal_gpio_write(bus->gpio_pin, true);\n\n    // somebody also can show presence\n    const uint32_t wait_low_time = timings->tpdl_max - timings->tpdl_min;\n\n    // so we will wait\n    if(!onewire_slave_wait_while_gpio_is(bus, wait_low_time, false)) {\n        bus->error = OneWireSlaveErrorPresenceConflict;\n        return false;\n    }\n\n    return true;\n}\n\nstatic inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) {\n    /* Reset condition detected, send a presence pulse and reset protocol state */\n    if(bus->error == OneWireSlaveErrorResetInProgress) {\n        if(!bus->is_first_reset) {\n            /* Guess the reset type */\n            bus->is_short_reset = onewire_slave_wait_while_gpio_is(\n                bus,\n                onewire_slave_timings_overdrive.trstl_max -\n                    onewire_slave_timings_overdrive.tslot_max,\n                false);\n        } else {\n            bus->is_first_reset = false;\n        }\n\n        furi_assert(bus->reset_callback);\n\n        if(bus->reset_callback(bus->is_short_reset, bus->reset_callback_context)) {\n            if(onewire_slave_show_presence(bus)) {\n                bus->error = OneWireSlaveErrorNone;\n                return true;\n            }\n        }\n\n    } else if(bus->error == OneWireSlaveErrorNone) {\n        uint8_t command;\n        if(onewire_slave_receive(bus, &command, sizeof(command))) {\n            furi_assert(bus->command_callback);\n            if(bus->command_callback(command, bus->command_callback_context)) {\n                return true;\n            }\n        }\n\n        return bus->error == OneWireSlaveErrorResetInProgress;\n    }\n\n    return false;\n}\n\nstatic inline bool onewire_slave_bus_start(OneWireSlave* bus) {\n    FURI_CRITICAL_ENTER();\n\n    furi_hal_gpio_disable_int_callback(bus->gpio_pin);\n    furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);\n\n    while(onewire_slave_receive_and_process_command(bus))\n        ;\n\n    const bool result = (bus->error == OneWireSlaveErrorNone);\n\n    furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow);\n    furi_hal_gpio_enable_int_callback(bus->gpio_pin);\n\n    FURI_CRITICAL_EXIT();\n\n    return result;\n}\n\nstatic void onewire_slave_exti_callback(void* context) {\n    OneWireSlave* bus = context;\n\n    const volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin);\n    static uint32_t pulse_start = 0;\n\n    if(input_state) {\n        const uint32_t pulse_length =\n            (DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond();\n\n        if((pulse_length >= onewire_slave_timings_overdrive.trstl_min) &&\n           (pulse_length <= onewire_slave_timings_normal.trstl_max)) {\n            /* Start in reset state in order to send a presence pulse immediately */\n            bus->error = OneWireSlaveErrorResetInProgress;\n            /* Determine reset type (chooses speed mode if supported by the emulated device) */\n            bus->is_short_reset = pulse_length <= onewire_slave_timings_overdrive.trstl_max;\n            /* Initial reset allows going directly into overdrive mode */\n            bus->is_first_reset = true;\n\n            const bool result = onewire_slave_bus_start(bus);\n\n            if(result && bus->result_callback != NULL) {\n                bus->result_callback(bus->result_callback_context);\n            }\n        }\n\n    } else {\n        pulse_start = DWT->CYCCNT;\n    }\n}\n\n/*********************** PUBLIC ***********************/\n\nOneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) {\n    furi_check(gpio_pin);\n    OneWireSlave* bus = malloc(sizeof(OneWireSlave));\n\n    bus->gpio_pin = gpio_pin;\n    bus->timings = &onewire_slave_timings_normal;\n    bus->error = OneWireSlaveErrorNone;\n\n    return bus;\n}\n\nvoid onewire_slave_free(OneWireSlave* bus) {\n    furi_check(bus);\n\n    onewire_slave_stop(bus);\n    free(bus);\n}\n\nvoid onewire_slave_start(OneWireSlave* bus) {\n    furi_check(bus);\n\n    furi_hal_gpio_add_int_callback(bus->gpio_pin, onewire_slave_exti_callback, bus);\n    furi_hal_gpio_write(bus->gpio_pin, true);\n    furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow);\n}\n\nvoid onewire_slave_stop(OneWireSlave* bus) {\n    furi_check(bus);\n\n    furi_hal_gpio_write(bus->gpio_pin, true);\n    furi_hal_gpio_init(bus->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);\n    furi_hal_gpio_remove_int_callback(bus->gpio_pin);\n}\n\nvoid onewire_slave_set_reset_callback(\n    OneWireSlave* bus,\n    OneWireSlaveResetCallback callback,\n    void* context) {\n    furi_check(bus);\n    bus->reset_callback = callback;\n    bus->reset_callback_context = context;\n}\n\nvoid onewire_slave_set_command_callback(\n    OneWireSlave* bus,\n    OneWireSlaveCommandCallback callback,\n    void* context) {\n    furi_check(bus);\n\n    bus->command_callback = callback;\n    bus->command_callback_context = context;\n}\n\nvoid onewire_slave_set_result_callback(\n    OneWireSlave* bus,\n    OneWireSlaveResultCallback result_cb,\n    void* context) {\n    furi_check(bus);\n    bus->result_callback = result_cb;\n    bus->result_callback_context = context;\n}\n\nbool onewire_slave_receive_bit(OneWireSlave* bus) {\n    furi_check(bus);\n\n    const OneWireSlaveTimings* timings = bus->timings;\n    // wait while bus is low\n    if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {\n        bus->error = OneWireSlaveErrorResetInProgress;\n        return false;\n    }\n\n    // wait while bus is high\n    if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {\n        bus->error = OneWireSlaveErrorTimeout;\n        return false;\n    }\n\n    // wait a time of zero\n    return onewire_slave_wait_while_gpio_is(bus, timings->tw1l_max, false);\n}\n\nbool onewire_slave_send_bit(OneWireSlave* bus, bool value) {\n    furi_check(bus);\n\n    const OneWireSlaveTimings* timings = bus->timings;\n    // wait while bus is low\n    if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {\n        bus->error = OneWireSlaveErrorResetInProgress;\n        return false;\n    }\n\n    // wait while bus is high\n    if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {\n        bus->error = OneWireSlaveErrorTimeout;\n        return false;\n    }\n\n    // choose write time\n    uint32_t time;\n\n    if(!value) {\n        furi_hal_gpio_write(bus->gpio_pin, false);\n        time = timings->trl_tmsr_max;\n    } else {\n        time = timings->tslot_min;\n    }\n\n    // hold line for ZERO or ONE time\n    furi_delay_us(time);\n    furi_hal_gpio_write(bus->gpio_pin, true);\n\n    return true;\n}\n\nbool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size) {\n    furi_check(bus);\n\n    furi_hal_gpio_write(bus->gpio_pin, true);\n\n    size_t bytes_sent = 0;\n\n    // bytes loop\n    for(; bytes_sent < data_size; ++bytes_sent) {\n        const uint8_t data_byte = data[bytes_sent];\n\n        // bit loop\n        for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) {\n            if(!onewire_slave_send_bit(bus, bit_mask & data_byte)) {\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nbool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size) {\n    furi_check(bus);\n\n    furi_hal_gpio_write(bus->gpio_pin, true);\n\n    size_t bytes_received = 0;\n\n    for(; bytes_received < data_size; ++bytes_received) {\n        uint8_t value = 0;\n\n        for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) {\n            if(onewire_slave_receive_bit(bus)) {\n                value |= bit_mask;\n            }\n\n            if(bus->error != OneWireSlaveErrorNone) {\n                return false;\n            }\n        }\n\n        data[bytes_received] = value;\n    }\n    return true;\n}\n\nvoid onewire_slave_set_overdrive(OneWireSlave* bus, bool set) {\n    furi_check(bus);\n    const OneWireSlaveTimings* new_timings = set ? &onewire_slave_timings_overdrive :\n                                                   &onewire_slave_timings_normal;\n    if(bus->timings != new_timings) {\n        /* Prevent erroneous reset by waiting for the previous time slot to finish */\n        onewire_slave_wait_while_gpio_is(bus, bus->timings->tslot_max, false);\n        bus->timings = new_timings;\n    }\n}\n"
  },
  {
    "path": "lib/one_wire/one_wire_slave.h",
    "content": "/**\n * @file one_wire_slave.h\n * \n * 1-Wire slave library.\n */\n\n#pragma once\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct OneWireDevice OneWireDevice;\ntypedef struct OneWireSlave OneWireSlave;\n\ntypedef bool (*OneWireSlaveResetCallback)(bool is_short, void* context);\ntypedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context);\ntypedef void (*OneWireSlaveResultCallback)(void* context);\n\n/**\n * Allocate OneWireSlave instance\n * @param [in] gpio_pin connection pin\n * @return pointer to OneWireSlave instance\n */\nOneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin);\n\n/**\n * Destroy OneWireSlave instance, free resources\n * @param [in] bus pointer to OneWireSlave instance\n */\nvoid onewire_slave_free(OneWireSlave* bus);\n\n/**\n * Start working with the bus\n * @param [in] bus pointer to OneWireSlave instance\n */\nvoid onewire_slave_start(OneWireSlave* bus);\n\n/**\n * Stop working with the bus\n * @param [in] bus pointer to OneWireSlave instance\n */\nvoid onewire_slave_stop(OneWireSlave* bus);\n\n/**\n * Receive one bit\n * @param [in] bus pointer to OneWireSlave instance\n * @return received bit value\n */\nbool onewire_slave_receive_bit(OneWireSlave* bus);\n\n/**\n * Send one bit\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] value bit value to send\n * @return true on success, false on failure\n */\nbool onewire_slave_send_bit(OneWireSlave* bus, bool value);\n\n/**\n * Send one or more bytes of data\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] data pointer to the data to send\n * @param [in] data_size size of the data to send\n * @return true on success, false on failure\n */\nbool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size);\n\n/**\n * Receive one or more bytes of data\n * @param [in] bus pointer to OneWireSlave instance\n * @param [out] data pointer to the receive buffer\n * @param [in] data_size number of bytes to receive\n * @return true on success, false on failure\n */\nbool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size);\n\n/**\n * Enable overdrive mode\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] set true to turn overdrive on, false to turn it off\n */\nvoid onewire_slave_set_overdrive(OneWireSlave* bus, bool set);\n\n/**\n * Set a callback function to be called on each reset.\n * The return value of the callback determines whether the emulated device\n * supports the short reset (passed as the is_short parameter).\n * In most applications, it should also call onewire_slave_set_overdrive()\n * to set the appropriate speed mode.\n *\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] callback pointer to a callback function\n * @param [in] context additional parameter to be passed to the callback\n */\nvoid onewire_slave_set_reset_callback(\n    OneWireSlave* bus,\n    OneWireSlaveResetCallback callback,\n    void* context);\n\n/**\n * Set a callback function to be called on each command.\n * The return value of the callback determines whether further operation\n * is possible. As a rule of thumb, return true unless a critical error happened.\n *\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] callback pointer to a callback function\n * @param [in] context additional parameter to be passed to the callback\n */\nvoid onewire_slave_set_command_callback(\n    OneWireSlave* bus,\n    OneWireSlaveCommandCallback callback,\n    void* context);\n\n/**\n * Set a callback to report emulation success\n * @param [in] bus pointer to OneWireSlave instance\n * @param [in] result_cb pointer to a callback function\n * @param [in] context additional parameter to be passed to the callback\n */\nvoid onewire_slave_set_result_callback(\n    OneWireSlave* bus,\n    OneWireSlaveResultCallback result_cb,\n    void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/print/SConscript",
    "content": "Import(\"env\")\n\nwrapped_fn_list = [\n    #\n    # used by our firmware, so we provide their realizations\n    #\n    \"fflush\",\n    \"printf\",\n    \"putc\",  # fallback from printf, thanks gcc\n    \"putchar\",  # storage cli\n    \"puts\",  # fallback from printf, thanks gcc\n    \"snprintf\",\n    \"vsnprintf\",  # m-string\n    \"__assert\",  # ???\n    \"__assert_func\",  # ???\n    #\n    # wrap other functions to make sure they are not called\n    # realization is not provided\n    #\n    \"setbuf\",\n    \"setvbuf\",\n    \"fprintf\",\n    \"vfprintf\",\n    \"vprintf\",\n    \"fputc\",\n    \"fputs\",\n    \"sprintf\",  # specially, because this function is dangerous\n    \"asprintf\",\n    \"vasprintf\",\n    \"asiprintf\",\n    \"asniprintf\",\n    \"asnprintf\",\n    \"diprintf\",\n    \"fiprintf\",\n    \"iprintf\",\n    \"siprintf\",\n    \"sniprintf\",\n    \"vasiprintf\",\n    \"vasniprintf\",\n    \"vasnprintf\",\n    \"vdiprintf\",\n    \"vfiprintf\",\n    \"viprintf\",\n    \"vsiprintf\",\n    \"vsniprintf\",\n    #\n    # standard input\n    #\n    \"fgetc\",\n    \"getc\",\n    \"getchar\",\n    \"fgets\",\n    \"ungetc\",\n    #\n    # standard input, but unimplemented\n    #\n    \"gets\",\n    #\n    # scanf, not implemented for now\n    #\n    # \"fscanf\",\n    # \"scanf\",\n    # \"sscanf\",\n    # \"vsprintf\",\n    # \"vfscanf\",\n    # \"vscanf\",\n    # \"vsscanf\",\n    # \"fiscanf\",\n    # \"iscanf\",\n    # \"siscanf\",\n    # \"vfiscanf\",\n    # \"viscanf\",\n    # \"vsiscanf\",\n    #\n    # File management\n    #\n    # \"fclose\",\n    # \"freopen\",\n    # \"fread\",\n    # \"fwrite\",\n    # \"fgetpos\",\n    # \"fseek\",\n    # \"fsetpos\",\n    # \"ftell\",\n    # \"rewind\",\n    # \"feof\",\n    # \"ferror\",\n    # \"fopen\",\n    # \"remove\",\n    # \"rename\",\n    # \"fseeko\",\n    # \"ftello\",\n]\n\nfor wrapped_fn in wrapped_fn_list:\n    env.Append(\n        LINKFLAGS=[\n            \"-Wl,--wrap,\" + wrapped_fn,\n            \"-Wl,--wrap,\" + wrapped_fn + \"_unlocked\",\n            \"-Wl,--wrap,_\" + wrapped_fn + \"_r\",\n            \"-Wl,--wrap,_\" + wrapped_fn + \"_unlocked_r\",\n        ]\n    )\n\nenv.Append(\n    SDK_HEADERS=[\n        File(\"wrappers.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"print\")\nlibenv.ApplyLibFlags()\nif env[\"RAM_EXEC\"]:\n    libenv.AppendUnique(CPPDEFINES=[\"PRINTF_DISABLE_SUPPORT_FLOAT\"])\n\nlibenv.Append(CCFLAGS=[\"-Wno-double-promotion\"])\n\nsources = libenv.GlobRecursive(\"*.c*\", \".\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/print/printf_tiny.c",
    "content": "///////////////////////////////////////////////////////////////////////////////\n// \\author (c) Marco Paland (info@paland.com)\n//             2014-2019, PALANDesign Hannover, Germany\n//\n// \\license The MIT License (MIT)\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// \\brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on\n//        embedded systems with a very limited resources. These routines are thread\n//        safe and reentrant!\n//        Use this instead of the bloated standard/newlib printf cause these use\n//        malloc for printf (and may not be thread safe).\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"printf_tiny.h\"\n\n// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the\n// printf_config.h header file\n// default: undefined\n#ifdef PRINTF_INCLUDE_CONFIG_H\n#include \"printf_config.h\"\n#endif\n\n// 'ntoa' conversion buffer size, this must be big enough to hold one converted\n// numeric number including padded zeros (dynamically created on stack)\n// default: 32 byte\n#ifndef PRINTF_NTOA_BUFFER_SIZE\n#define PRINTF_NTOA_BUFFER_SIZE 32U\n#endif\n\n// 'ftoa' conversion buffer size, this must be big enough to hold one converted\n// float number including padded zeros (dynamically created on stack)\n// default: 32 byte\n#ifndef PRINTF_FTOA_BUFFER_SIZE\n#define PRINTF_FTOA_BUFFER_SIZE 32U\n#endif\n\n// support for the floating point type (%f)\n// default: activated\n#ifndef PRINTF_DISABLE_SUPPORT_FLOAT\n#define PRINTF_SUPPORT_FLOAT\n#endif\n\n// support for exponential floating point notation (%e/%g)\n// default: activated\n#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL\n#define PRINTF_SUPPORT_EXPONENTIAL\n#endif\n\n// define the default floating point precision\n// default: 6 digits\n#ifndef PRINTF_DEFAULT_FLOAT_PRECISION\n#define PRINTF_DEFAULT_FLOAT_PRECISION 6U\n#endif\n\n// define the largest float suitable to print with %f\n// default: 1e9\n#ifndef PRINTF_MAX_FLOAT\n#define PRINTF_MAX_FLOAT 1e9\n#endif\n\n// support for the long long types (%llu or %p)\n// default: activated\n#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG\n#define PRINTF_SUPPORT_LONG_LONG\n#endif\n\n// support for the ptrdiff_t type (%t)\n// ptrdiff_t is normally defined in <stddef.h> as long or long long type\n// default: activated\n#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T\n#define PRINTF_SUPPORT_PTRDIFF_T\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n\n// internal flag definitions\n#define FLAGS_ZEROPAD   (1U << 0U)\n#define FLAGS_LEFT      (1U << 1U)\n#define FLAGS_PLUS      (1U << 2U)\n#define FLAGS_SPACE     (1U << 3U)\n#define FLAGS_HASH      (1U << 4U)\n#define FLAGS_UPPERCASE (1U << 5U)\n#define FLAGS_CHAR      (1U << 6U)\n#define FLAGS_SHORT     (1U << 7U)\n#define FLAGS_LONG      (1U << 8U)\n#define FLAGS_LONG_LONG (1U << 9U)\n#define FLAGS_PRECISION (1U << 10U)\n#define FLAGS_ADAPT_EXP (1U << 11U)\n\n// import float.h for DBL_MAX\n#if defined(PRINTF_SUPPORT_FLOAT)\n#include <float.h>\n#endif\n\n// output function type\ntypedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);\n\n// wrapper (used as buffer) for output function type\ntypedef struct {\n    void (*fct)(char character, void* arg);\n    void* arg;\n} out_fct_wrap_type;\n\n// internal buffer output\nstatic inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) {\n    if(idx < maxlen) {\n        ((char*)buffer)[idx] = character;\n    }\n}\n\n// internal null output\nstatic inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) {\n    (void)character;\n    (void)buffer;\n    (void)idx;\n    (void)maxlen;\n}\n\n// internal _putchar wrapper\nstatic inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) {\n    (void)buffer;\n    (void)idx;\n    (void)maxlen;\n    if(character) {\n        _putchar(character);\n    }\n}\n\n// internal output function wrapper\nstatic inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) {\n    (void)idx;\n    (void)maxlen;\n    if(character) {\n        // buffer is the output fct pointer\n        ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);\n    }\n}\n\n// internal secure strlen\n// \\return The length of the string (excluding the terminating 0) limited by 'maxsize'\nstatic inline unsigned int _strnlen_s(const char* str, size_t maxsize) {\n    const char* s;\n    for(s = str; *s && maxsize--; ++s)\n        ;\n    return (unsigned int)(s - str);\n}\n\n// internal test if char is a digit (0-9)\n// \\return true if char is a digit\nstatic inline bool _is_digit(char ch) {\n    return (ch >= '0') && (ch <= '9');\n}\n\n// internal ASCII string to unsigned int conversion\nstatic unsigned int _atoi(const char** str) {\n    unsigned int i = 0U;\n    while(_is_digit(**str)) {\n        i = i * 10U + (unsigned int)(*((*str)++) - '0');\n    }\n    return i;\n}\n\n// output the specified string in reverse, taking care of any zero-padding\nstatic size_t _out_rev(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    const char* buf,\n    size_t len,\n    unsigned int width,\n    unsigned int flags) {\n    const size_t start_idx = idx;\n\n    // pad spaces up to given width\n    if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {\n        for(size_t i = len; i < width; i++) {\n            out(' ', buffer, idx++, maxlen);\n        }\n    }\n\n    // reverse string\n    while(len) {\n        out(buf[--len], buffer, idx++, maxlen);\n    }\n\n    // append pad spaces up to given width\n    if(flags & FLAGS_LEFT) {\n        while(idx - start_idx < width) {\n            out(' ', buffer, idx++, maxlen);\n        }\n    }\n\n    return idx;\n}\n\n// internal itoa format\nstatic size_t _ntoa_format(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    char* buf,\n    size_t len,\n    bool negative,\n    unsigned int base,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags) {\n    // pad leading zeros\n    if(!(flags & FLAGS_LEFT)) {\n        if(width && (flags & FLAGS_ZEROPAD) &&\n           (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {\n            width--;\n        }\n        while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {\n            buf[len++] = '0';\n        }\n        while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {\n            buf[len++] = '0';\n        }\n    }\n\n    // handle hash\n    if(flags & FLAGS_HASH) {\n        if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {\n            len--;\n            if(len && (base == 16U)) {\n                len--;\n            }\n        }\n        if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {\n            buf[len++] = 'x';\n        } else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {\n            buf[len++] = 'X';\n        } else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {\n            buf[len++] = 'b';\n        }\n        if(len < PRINTF_NTOA_BUFFER_SIZE) {\n            buf[len++] = '0';\n        }\n    }\n\n    if(len < PRINTF_NTOA_BUFFER_SIZE) {\n        if(negative) {\n            buf[len++] = '-';\n        } else if(flags & FLAGS_PLUS) {\n            buf[len++] = '+'; // ignore the space if the '+' exists\n        } else if(flags & FLAGS_SPACE) {\n            buf[len++] = ' ';\n        }\n    }\n\n    return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);\n}\n\n// internal itoa for 'long' type\nstatic size_t _ntoa_long(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    unsigned long value,\n    bool negative,\n    unsigned long base,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags) {\n    char buf[PRINTF_NTOA_BUFFER_SIZE];\n    size_t len = 0U;\n\n    // no hash for 0 values\n    if(!value) {\n        flags &= ~FLAGS_HASH;\n    }\n\n    // write if precision != 0 and value is != 0\n    if(!(flags & FLAGS_PRECISION) || value) {\n        do {\n            const char digit = (char)(value % base);\n            buf[len++] = digit < 10 ? '0' + digit :\n                                      (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;\n            value /= base;\n        } while(value && (len < PRINTF_NTOA_BUFFER_SIZE));\n    }\n\n    return _ntoa_format(\n        out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);\n}\n\n// internal itoa for 'long long' type\n#if defined(PRINTF_SUPPORT_LONG_LONG)\nstatic size_t _ntoa_long_long(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    unsigned long long value,\n    bool negative,\n    unsigned long long base,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags) {\n    char buf[PRINTF_NTOA_BUFFER_SIZE];\n    size_t len = 0U;\n\n    // no hash for 0 values\n    if(!value) {\n        flags &= ~FLAGS_HASH;\n    }\n\n    // write if precision != 0 and value is != 0\n    if(!(flags & FLAGS_PRECISION) || value) {\n        do {\n            const char digit = (char)(value % base);\n            buf[len++] = digit < 10 ? '0' + digit :\n                                      (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;\n            value /= base;\n        } while(value && (len < PRINTF_NTOA_BUFFER_SIZE));\n    }\n\n    return _ntoa_format(\n        out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);\n}\n#endif // PRINTF_SUPPORT_LONG_LONG\n\n#if defined(PRINTF_SUPPORT_FLOAT)\n\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT\nstatic size_t _etoa(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    double value,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags);\n#endif\n\n// internal ftoa for fixed decimal floating point\nstatic size_t _ftoa(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    double value,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags) {\n    char buf[PRINTF_FTOA_BUFFER_SIZE];\n    size_t len = 0U;\n    double diff = 0.0;\n\n    // powers of 10\n    static const double pow10[] = {\n        1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};\n\n    // test for special values\n    if(value != value) return _out_rev(out, buffer, idx, maxlen, \"nan\", 3, width, flags);\n    if(value < -DBL_MAX) return _out_rev(out, buffer, idx, maxlen, \"fni-\", 4, width, flags);\n    if(value > DBL_MAX)\n        return _out_rev(\n            out,\n            buffer,\n            idx,\n            maxlen,\n            (flags & FLAGS_PLUS) ? \"fni+\" : \"fni\",\n            (flags & FLAGS_PLUS) ? 4U : 3U,\n            width,\n            flags);\n\n    // test for very large values\n    // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad\n    if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n        return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);\n#else\n        return 0U;\n#endif\n    }\n\n    // test for negative\n    bool negative = false;\n    if(value < 0) {\n        negative = true;\n        value = 0 - value;\n    }\n\n    // set default precision, if not set explicitly\n    if(!(flags & FLAGS_PRECISION)) {\n        prec = PRINTF_DEFAULT_FLOAT_PRECISION;\n    }\n    // limit precision to 9, cause a prec >= 10 can lead to overflow errors\n    while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {\n        buf[len++] = '0';\n        prec--;\n    }\n\n    int whole = (int)value;\n    double tmp = (value - whole) * pow10[prec];\n    unsigned long frac = (unsigned long)tmp;\n    diff = tmp - frac;\n\n    if(diff > 0.5) {\n        ++frac;\n        // handle rollover, e.g. case 0.99 with prec 1 is 1.0\n        if(frac >= pow10[prec]) {\n            frac = 0;\n            ++whole;\n        }\n    } else if(diff < 0.5) {\n    } else if((frac == 0U) || (frac & 1U)) {\n        // if halfway, round up if odd OR if last digit is 0\n        ++frac;\n    }\n\n    if(prec == 0U) {\n        diff = value - (double)whole;\n        if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {\n            // exactly 0.5 and ODD, then round up\n            // 1.5 -> 2, but 2.5 -> 2\n            ++whole;\n        }\n    } else {\n        unsigned int count = prec;\n        // now do fractional part, as an unsigned number\n        while(len < PRINTF_FTOA_BUFFER_SIZE) {\n            --count;\n            buf[len++] = (char)(48U + (frac % 10U));\n            if(!(frac /= 10U)) {\n                break;\n            }\n        }\n        // add extra 0s\n        while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {\n            buf[len++] = '0';\n        }\n        if(len < PRINTF_FTOA_BUFFER_SIZE) {\n            // add decimal\n            buf[len++] = '.';\n        }\n    }\n\n    // do whole part, number is reversed\n    while(len < PRINTF_FTOA_BUFFER_SIZE) {\n        buf[len++] = (char)(48 + (whole % 10));\n        if(!(whole /= 10)) {\n            break;\n        }\n    }\n\n    // pad leading zeros\n    if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {\n        if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {\n            width--;\n        }\n        while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {\n            buf[len++] = '0';\n        }\n    }\n\n    if(len < PRINTF_FTOA_BUFFER_SIZE) {\n        if(negative) {\n            buf[len++] = '-';\n        } else if(flags & FLAGS_PLUS) {\n            buf[len++] = '+'; // ignore the space if the '+' exists\n        } else if(flags & FLAGS_SPACE) {\n            buf[len++] = ' ';\n        }\n    }\n\n    return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);\n}\n\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>\nstatic size_t _etoa(\n    out_fct_type out,\n    char* buffer,\n    size_t idx,\n    size_t maxlen,\n    double value,\n    unsigned int prec,\n    unsigned int width,\n    unsigned int flags) {\n    // check for NaN and special values\n    if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {\n        return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);\n    }\n\n    // determine the sign\n    const bool negative = value < 0;\n    if(negative) {\n        value = -value;\n    }\n\n    // default precision\n    if(!(flags & FLAGS_PRECISION)) {\n        prec = PRINTF_DEFAULT_FLOAT_PRECISION;\n    }\n\n    // determine the decimal exponent\n    // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)\n    union {\n        uint64_t U;\n        double F;\n    } conv;\n\n    conv.F = value;\n    int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2\n    conv.U = (conv.U & ((1ULL << 52U) - 1U)) |\n             (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)\n    // now approximate log10 from the log2 integer part and an expansion of ln around 1.5\n    int expval =\n        (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);\n    // now we want to compute 10^expval but we want to be sure it won't overflow\n    exp2 = (int)(expval * 3.321928094887362 + 0.5);\n    const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;\n    const double z2 = z * z;\n    conv.U = ((uint64_t)exp2 + 1023) << 52U; //-V519\n    // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex\n    conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));\n    // correct for rounding errors\n    if(value < conv.F) {\n        expval--;\n        conv.F /= 10;\n    }\n\n    // the exponent format is \"%+03d\" and largest value is \"307\", so set aside 4-5 characters\n    unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;\n\n    // in \"%g\" mode, \"prec\" is the number of *significant figures* not decimals\n    if(flags & FLAGS_ADAPT_EXP) {\n        // do we want to fall-back to \"%f\" mode?\n        if((value >= 1e-4) && (value < 1e6)) {\n            if((int)prec > expval) {\n                prec = (unsigned)((int)prec - expval - 1);\n            } else {\n                prec = 0;\n            }\n            flags |= FLAGS_PRECISION; // make sure _ftoa respects precision\n            // no characters in exponent\n            minwidth = 0U;\n            expval = 0;\n        } else {\n            // we use one sigfig for the whole part\n            if((prec > 0) && (flags & FLAGS_PRECISION)) {\n                --prec;\n            }\n        }\n    }\n\n    // will everything fit?\n    unsigned int fwidth = width;\n    if(width > minwidth) {\n        // we didn't fall-back so subtract the characters required for the exponent\n        fwidth -= minwidth;\n    } else {\n        // not enough characters, so go back to default sizing\n        fwidth = 0U;\n    }\n    if((flags & FLAGS_LEFT) && minwidth) {\n        // if we're padding on the right, DON'T pad the floating part\n        fwidth = 0U;\n    }\n\n    // rescale the float value\n    if(expval) {\n        value /= conv.F;\n    }\n\n    // output the floating part\n    const size_t start_idx = idx;\n    idx = _ftoa(\n        out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);\n\n    // output the exponent part\n    if(minwidth) {\n        // output the exponential symbol\n        out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);\n        // output the exponent value\n        idx = _ntoa_long(\n            out,\n            buffer,\n            idx,\n            maxlen,\n            (expval < 0) ? -expval : expval,\n            expval < 0,\n            10,\n            0,\n            minwidth - 1,\n            FLAGS_ZEROPAD | FLAGS_PLUS);\n        // might need to right-pad spaces\n        if(flags & FLAGS_LEFT) {\n            while(idx - start_idx < width)\n                out(' ', buffer, idx++, maxlen);\n        }\n    }\n    return idx;\n}\n#endif // PRINTF_SUPPORT_EXPONENTIAL\n#endif // PRINTF_SUPPORT_FLOAT\n\n// internal vsnprintf\nstatic int\n    _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) {\n    unsigned int flags, width, precision, n;\n    size_t idx = 0U;\n\n    if(!buffer) {\n        // use null output function\n        out = _out_null;\n    }\n\n    while(*format) {\n        // format specifier?  %[flags][width][.precision][length]\n        if(*format != '%') {\n            // no\n            out(*format, buffer, idx++, maxlen);\n            format++;\n            continue;\n        } else {\n            // yes, evaluate it\n            format++;\n        }\n\n        // evaluate flags\n        flags = 0U;\n        do { //-V1044\n            switch(*format) {\n            case '0':\n                flags |= FLAGS_ZEROPAD;\n                format++;\n                n = 1U;\n                break;\n            case '-':\n                flags |= FLAGS_LEFT;\n                format++;\n                n = 1U;\n                break;\n            case '+':\n                flags |= FLAGS_PLUS;\n                format++;\n                n = 1U;\n                break;\n            case ' ':\n                flags |= FLAGS_SPACE;\n                format++;\n                n = 1U;\n                break;\n            case '#':\n                flags |= FLAGS_HASH;\n                format++;\n                n = 1U;\n                break;\n            default:\n                n = 0U;\n                break;\n            }\n        } while(n);\n\n        // evaluate width field\n        width = 0U;\n        if(_is_digit(*format)) {\n            width = _atoi(&format);\n        } else if(*format == '*') {\n            const int w = va_arg(va, int);\n            if(w < 0) {\n                flags |= FLAGS_LEFT; // reverse padding\n                width = (unsigned int)-w;\n            } else {\n                width = (unsigned int)w;\n            }\n            format++;\n        }\n\n        // evaluate precision field\n        precision = 0U;\n        if(*format == '.') {\n            flags |= FLAGS_PRECISION;\n            format++;\n            if(_is_digit(*format)) {\n                precision = _atoi(&format);\n            } else if(*format == '*') {\n                const int prec = (int)va_arg(va, int);\n                precision = prec > 0 ? (unsigned int)prec : 0U;\n                format++;\n            }\n        }\n\n        // evaluate length field\n        switch(*format) {\n        case 'l':\n            flags |= FLAGS_LONG;\n            format++;\n            if(*format == 'l') {\n                flags |= FLAGS_LONG_LONG;\n                format++;\n            }\n            break;\n        case 'h':\n            flags |= FLAGS_SHORT;\n            format++;\n            if(*format == 'h') {\n                flags |= FLAGS_CHAR;\n                format++;\n            }\n            break;\n#if defined(PRINTF_SUPPORT_PTRDIFF_T)\n        case 't':\n            flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n            format++;\n            break;\n#endif\n        case 'j':\n            flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n            format++;\n            break;\n        case 'z':\n            flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n            format++;\n            break;\n        default:\n            break;\n        }\n\n        // evaluate specifier\n        switch(*format) {\n        case 'd':\n        case 'i':\n        case 'u':\n        case 'x':\n        case 'X':\n        case 'o':\n        case 'b': {\n            // set the base\n            unsigned int base;\n            if(*format == 'x' || *format == 'X') {\n                base = 16U;\n            } else if(*format == 'o') {\n                base = 8U;\n            } else if(*format == 'b') {\n                base = 2U;\n            } else {\n                base = 10U;\n                flags &= ~FLAGS_HASH; // no hash for dec format\n            }\n            // uppercase\n            if(*format == 'X') {\n                flags |= FLAGS_UPPERCASE;\n            }\n\n            // no plus or space flag for u, x, X, o, b\n            if((*format != 'i') && (*format != 'd')) {\n                flags &= ~(FLAGS_PLUS | FLAGS_SPACE);\n            }\n\n            // ignore '0' flag when precision is given\n            if(flags & FLAGS_PRECISION) {\n                flags &= ~FLAGS_ZEROPAD;\n            }\n\n            // convert the integer\n            if((*format == 'i') || (*format == 'd')) {\n                // signed\n                if(flags & FLAGS_LONG_LONG) {\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n                    const long long value = va_arg(va, long long);\n                    idx = _ntoa_long_long(\n                        out,\n                        buffer,\n                        idx,\n                        maxlen,\n                        (unsigned long long)(value > 0 ? value : 0 - value),\n                        value < 0,\n                        base,\n                        precision,\n                        width,\n                        flags);\n#endif\n                } else if(flags & FLAGS_LONG) {\n                    const long value = va_arg(va, long);\n                    idx = _ntoa_long(\n                        out,\n                        buffer,\n                        idx,\n                        maxlen,\n                        (unsigned long)(value > 0 ? value : 0 - value),\n                        value < 0,\n                        base,\n                        precision,\n                        width,\n                        flags);\n                } else {\n                    const int value = (flags & FLAGS_CHAR)  ? (char)va_arg(va, int) :\n                                      (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) :\n                                                              va_arg(va, int);\n                    idx = _ntoa_long(\n                        out,\n                        buffer,\n                        idx,\n                        maxlen,\n                        (unsigned int)(value > 0 ? value : 0 - value),\n                        value < 0,\n                        base,\n                        precision,\n                        width,\n                        flags);\n                }\n            } else {\n                // unsigned\n                if(flags & FLAGS_LONG_LONG) {\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n                    idx = _ntoa_long_long(\n                        out,\n                        buffer,\n                        idx,\n                        maxlen,\n                        va_arg(va, unsigned long long),\n                        false,\n                        base,\n                        precision,\n                        width,\n                        flags);\n#endif\n                } else if(flags & FLAGS_LONG) {\n                    idx = _ntoa_long(\n                        out,\n                        buffer,\n                        idx,\n                        maxlen,\n                        va_arg(va, unsigned long),\n                        false,\n                        base,\n                        precision,\n                        width,\n                        flags);\n                } else {\n                    const unsigned int value =\n                        (flags & FLAGS_CHAR)  ? (unsigned char)va_arg(va, unsigned int) :\n                        (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) :\n                                                va_arg(va, unsigned int);\n                    idx = _ntoa_long(\n                        out, buffer, idx, maxlen, value, false, base, precision, width, flags);\n                }\n            }\n            format++;\n            break;\n        }\n#if defined(PRINTF_SUPPORT_FLOAT)\n        case 'f':\n        case 'F':\n            if(*format == 'F') flags |= FLAGS_UPPERCASE;\n            idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);\n            format++;\n            break;\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n        case 'e':\n        case 'E':\n        case 'g':\n        case 'G':\n            if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP;\n            if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE;\n            idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);\n            format++;\n            break;\n#endif // PRINTF_SUPPORT_EXPONENTIAL\n#endif // PRINTF_SUPPORT_FLOAT\n        case 'c': {\n            unsigned int l = 1U;\n            // pre padding\n            if(!(flags & FLAGS_LEFT)) {\n                while(l++ < width) {\n                    out(' ', buffer, idx++, maxlen);\n                }\n            }\n            // char output\n            out((char)va_arg(va, int), buffer, idx++, maxlen);\n            // post padding\n            if(flags & FLAGS_LEFT) {\n                while(l++ < width) {\n                    out(' ', buffer, idx++, maxlen);\n                }\n            }\n            format++;\n            break;\n        }\n\n        case 's': {\n            const char* p = va_arg(va, char*);\n            unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);\n            // pre padding\n            if(flags & FLAGS_PRECISION) {\n                l = (l < precision ? l : precision);\n            }\n            if(!(flags & FLAGS_LEFT)) {\n                while(l++ < width) {\n                    out(' ', buffer, idx++, maxlen);\n                }\n            }\n            // string output\n            while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {\n                out(*(p++), buffer, idx++, maxlen);\n            }\n            // post padding\n            if(flags & FLAGS_LEFT) {\n                while(l++ < width) {\n                    out(' ', buffer, idx++, maxlen);\n                }\n            }\n            format++;\n            break;\n        }\n\n        case 'p': {\n            width = sizeof(void*) * 2U;\n            flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n            const bool is_ll = sizeof(uintptr_t) == sizeof(long long);\n            if(is_ll) {\n                idx = _ntoa_long_long(\n                    out,\n                    buffer,\n                    idx,\n                    maxlen,\n                    (uintptr_t)va_arg(va, void*),\n                    false,\n                    16U,\n                    precision,\n                    width,\n                    flags);\n            } else {\n#endif\n                idx = _ntoa_long(\n                    out,\n                    buffer,\n                    idx,\n                    maxlen,\n                    (unsigned long)((uintptr_t)va_arg(va, void*)),\n                    false,\n                    16U,\n                    precision,\n                    width,\n                    flags);\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n            }\n#endif\n            format++;\n            break;\n        }\n\n        case '%':\n            out('%', buffer, idx++, maxlen);\n            format++;\n            break;\n\n        default:\n            out(*format, buffer, idx++, maxlen);\n            format++;\n            break;\n        }\n    }\n\n    // termination\n    out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);\n\n    // return written chars without terminating \\0\n    return (int)idx;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nint printf_(const char* format, ...) {\n    va_list va;\n    va_start(va, format);\n    char buffer[1];\n    const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);\n    va_end(va);\n    return ret;\n}\n\nint sprintf_(char* buffer, const char* format, ...) {\n    va_list va;\n    va_start(va, format);\n    const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);\n    va_end(va);\n    return ret;\n}\n\nint snprintf_(char* buffer, size_t count, const char* format, ...) {\n    va_list va;\n    va_start(va, format);\n    const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);\n    va_end(va);\n    return ret;\n}\n\nint vprintf_(const char* format, va_list va) {\n    char buffer[1];\n    return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);\n}\n\nint vsnprintf_(char* buffer, size_t count, const char* format, va_list va) {\n    return _vsnprintf(_out_buffer, buffer, count, format, va);\n}\n\nint fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) {\n    va_list va;\n    va_start(va, format);\n    const out_fct_wrap_type out_fct_wrap = {out, arg};\n    const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);\n    va_end(va);\n    return ret;\n}\n"
  },
  {
    "path": "lib/print/printf_tiny.h",
    "content": "///////////////////////////////////////////////////////////////////////////////\n// \\author (c) Marco Paland (info@paland.com)\n//             2014-2019, PALANDesign Hannover, Germany\n//\n// \\license The MIT License (MIT)\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// \\brief Tiny printf, sprintf and snprintf implementation, optimized for speed on\n//        embedded systems with a very limited resources.\n//        Use this instead of bloated standard/newlib printf.\n//        These routines are thread safe and reentrant.\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _PRINTF_H_\n#define _PRINTF_H_\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Output a character to a custom device like UART, used by the printf() function\n * This function is declared here only. You have to write your custom implementation somewhere\n * \\param character Character to output\n */\nvoid _putchar(char character);\n\n/**\n * Tiny printf implementation\n * You have to implement _putchar if you use printf()\n * To avoid conflicts with the regular printf() API it is overridden by macro defines\n * and internal underscore-appended functions like printf_() are used\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are written into the array, not counting the terminating null character\n */\nint printf_(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));\n\n/**\n * Tiny sprintf implementation\n * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!\n * \\param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are WRITTEN into the buffer, not counting the terminating null character\n */\nint sprintf_(char* buffer, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/**\n * Tiny snprintf/vsnprintf implementation\n * \\param buffer A pointer to the buffer where to store the formatted string\n * \\param count The maximum number of characters to store in the buffer, including a terminating null character\n * \\param format A string that specifies the format of the output\n * \\param va A value identifying a variable arguments list\n * \\return The number of characters that COULD have been written into the buffer, not counting the terminating\n *         null character. A value equal or larger than count indicates truncation. Only when the returned value\n *         is non-negative and less than count, the string has been completely written.\n */\nint snprintf_(char* buffer, size_t count, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 3, 4)));\nint vsnprintf_(char* buffer, size_t count, const char* format, va_list va);\n\n/**\n * Tiny vprintf implementation\n * \\param format A string that specifies the format of the output\n * \\param va A value identifying a variable arguments list\n * \\return The number of characters that are WRITTEN into the buffer, not counting the terminating null character\n */\nint vprintf_(const char* format, va_list va);\n\n/**\n * printf with output function\n * You may use this as dynamic alternative to printf() with its fixed _putchar() output\n * \\param out An output function which takes one character and an argument pointer\n * \\param arg An argument pointer for user data passed to output function\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are sent to the output function, not counting the terminating null character\n */\nint fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 3, 4)));\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // _PRINTF_H_\n"
  },
  {
    "path": "lib/print/wrappers.c",
    "content": "#include \"wrappers.h\"\n\n#include <stdbool.h>\n#include <stdarg.h>\n#include <furi/core/check.h>\n#include <furi/core/thread.h>\n#include <furi/core/common_defines.h>\n#include \"printf_tiny.h\"\n\nvoid _putchar(char character) {\n    furi_thread_stdout_write(&character, 1);\n}\n\nint __wrap_printf(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    int ret = vprintf_(format, args);\n    va_end(args);\n\n    return ret;\n}\n\nint __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args) {\n    return vsnprintf_(str, size, format, args);\n}\n\nint __wrap_puts(const char* str) {\n    size_t size = furi_thread_stdout_write(str, strlen(str));\n    size += furi_thread_stdout_write(\"\\n\", 1);\n    return size;\n}\n\nint __wrap_putchar(int ch) {\n    size_t size = furi_thread_stdout_write((char*)&ch, 1);\n    return size;\n}\n\nint __wrap_putc(int ch, FILE* stream) {\n    UNUSED(stream);\n    size_t size = furi_thread_stdout_write((char*)&ch, 1);\n    return size;\n}\n\nint __wrap_snprintf(char* str, size_t size, const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    int ret = __wrap_vsnprintf(str, size, format, args);\n    va_end(args);\n\n    return ret;\n}\n\nint __wrap_fflush(FILE* stream) {\n    if(stream == stdout) furi_thread_stdout_flush();\n    return 0;\n}\n\nint __wrap_fgetc(FILE* stream) {\n    if(stream != stdin) return EOF;\n    char c;\n    if(furi_thread_stdin_read(&c, 1, FuriWaitForever) == 0) return EOF;\n    return c;\n}\n\nint __wrap_getc(FILE* stream) {\n    return __wrap_fgetc(stream);\n}\n\nint __wrap_getchar(void) {\n    return __wrap_fgetc(stdin);\n}\n\nchar* __wrap_fgets(char* str, size_t n, FILE* stream) {\n    // leave space for the zero terminator\n    furi_check(n >= 1);\n    n--;\n\n    if(stream != stdin) {\n        *str = '\\0';\n        return str;\n    }\n\n    // read characters\n    int c;\n    do {\n        c = __wrap_fgetc(stdin);\n        if(c > 0) *(str++) = c;\n    } while(c != EOF && c != '\\n' && --n);\n\n    // place zero terminator\n    *str = '\\0';\n    return str;\n}\n\nint __wrap_ungetc(int ch, FILE* stream) {\n    char c = ch;\n    if(stream != stdin) return EOF;\n    furi_thread_stdin_unread(&c, 1);\n    return ch;\n}\n\n__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) {\n    UNUSED(file);\n    UNUSED(line);\n    furi_crash(e);\n}\n\n__attribute__((__noreturn__)) void\n    __wrap___assert_func(const char* file, int line, const char* func, const char* e) {\n    UNUSED(file);\n    UNUSED(line);\n    UNUSED(func);\n    furi_crash(e);\n}\n"
  },
  {
    "path": "lib/print/wrappers.h",
    "content": "#pragma once\n\n#include <stdio.h>\n#include <stdint.h>\n#include <string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint __wrap_printf(const char* format, ...);\nint __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args);\nint __wrap_puts(const char* str);\nint __wrap_putchar(int ch);\nint __wrap_putc(int ch, FILE* stream);\nint __wrap_snprintf(char* str, size_t size, const char* format, ...);\nint __wrap_fflush(FILE* stream);\n\nint __wrap_fgetc(FILE* stream);\nint __wrap_getc(FILE* stream);\nint __wrap_getchar(void);\nchar* __wrap_fgets(char* str, size_t n, FILE* stream);\nint __wrap_ungetc(int ch, FILE* stream);\n\n__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e);\n\n__attribute__((__noreturn__)) void\n    __wrap___assert_func(const char* file, int line, const char* func, const char* e);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/pulse_reader/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/pulse_reader\",\n    ],\n    SDK_HEADERS=[\n        File(\"pulse_reader.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"pulse_reader\")\nlibenv.ApplyLibFlags()\n\nlibenv.AppendUnique(\n    CCFLAGS=[\n        # Required for lib to be linkable with .faps\n        \"-mword-relocations\",\n        \"-mlong-calls\",\n    ],\n)\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/pulse_reader/pulse_reader.c",
    "content": "#include \"pulse_reader.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_gpio.h>\n\n#include <stm32wbxx_ll_dma.h>\n#include <stm32wbxx_ll_dmamux.h>\n#include <stm32wbxx_ll_tim.h>\n#include <stm32wbxx_ll_exti.h>\n\nstruct PulseReader {\n    uint32_t* timer_buffer;\n    uint32_t* gpio_buffer;\n    uint32_t size;\n    uint32_t pos;\n    uint32_t timer_value;\n    uint32_t gpio_value;\n    uint32_t gpio_mask;\n    uint32_t unit_multiplier;\n    uint32_t unit_divider;\n    uint32_t bit_time;\n    uint32_t dma_channel;\n    const GpioPin* gpio;\n    GpioPull pull;\n    LL_DMA_InitTypeDef dma_config_timer;\n    LL_DMA_InitTypeDef dma_config_gpio;\n};\n\n#define GPIO_PIN_MAP(pin, prefix)               \\\n    (((pin) == (LL_GPIO_PIN_0))  ? prefix##0 :  \\\n     ((pin) == (LL_GPIO_PIN_1))  ? prefix##1 :  \\\n     ((pin) == (LL_GPIO_PIN_2))  ? prefix##2 :  \\\n     ((pin) == (LL_GPIO_PIN_3))  ? prefix##3 :  \\\n     ((pin) == (LL_GPIO_PIN_4))  ? prefix##4 :  \\\n     ((pin) == (LL_GPIO_PIN_5))  ? prefix##5 :  \\\n     ((pin) == (LL_GPIO_PIN_6))  ? prefix##6 :  \\\n     ((pin) == (LL_GPIO_PIN_7))  ? prefix##7 :  \\\n     ((pin) == (LL_GPIO_PIN_8))  ? prefix##8 :  \\\n     ((pin) == (LL_GPIO_PIN_9))  ? prefix##9 :  \\\n     ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \\\n     ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \\\n     ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \\\n     ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \\\n     ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \\\n                                   prefix##15)\n\n#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)\n\nPulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) {\n    PulseReader* signal = malloc(sizeof(PulseReader));\n    signal->timer_buffer = malloc(size * sizeof(uint32_t));\n    signal->gpio_buffer = malloc(size * sizeof(uint32_t));\n    signal->dma_channel = LL_DMA_CHANNEL_4;\n    signal->gpio = gpio;\n    signal->pull = GpioPullNo;\n    signal->size = size;\n    signal->timer_value = 0;\n    signal->pos = 0;\n\n    pulse_reader_set_timebase(signal, PulseReaderUnit64MHz);\n    pulse_reader_set_bittime(signal, 1);\n\n    signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;\n    signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT);\n    signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;\n    signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;\n    signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer;\n    signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;\n    signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;\n    signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;\n    signal->dma_config_timer.PeriphRequest =\n        LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */\n    signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH;\n\n    signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;\n    signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;\n    signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;\n    signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;\n    signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;\n    signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;\n    signal->dma_config_gpio.PeriphRequest =\n        LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */\n    signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;\n\n    return signal;\n}\n\nvoid pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) {\n    switch(unit) {\n    case PulseReaderUnit64MHz:\n        signal->unit_multiplier = 1;\n        signal->unit_divider = 1;\n        break;\n    case PulseReaderUnitPicosecond:\n        signal->unit_multiplier = 15625;\n        signal->unit_divider = 1;\n        break;\n    case PulseReaderUnitNanosecond:\n        signal->unit_multiplier = 15625;\n        signal->unit_divider = 1000;\n        break;\n    case PulseReaderUnitMicrosecond:\n        signal->unit_multiplier = 15625;\n        signal->unit_divider = 1000000;\n        break;\n    }\n}\n\nvoid pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) {\n    signal->bit_time = bit_time;\n}\n\nvoid pulse_reader_set_pull(PulseReader* signal, GpioPull pull) {\n    signal->pull = pull;\n}\n\nvoid pulse_reader_free(PulseReader* signal) {\n    furi_assert(signal);\n\n    free(signal->timer_buffer);\n    free(signal->gpio_buffer);\n    free(signal);\n}\n\nuint32_t pulse_reader_samples(PulseReader* signal) {\n    uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);\n\n    return ((signal->pos + signal->size) - dma_pos) % signal->size;\n}\n\nvoid pulse_reader_stop(PulseReader* signal) {\n    LL_DMA_DisableChannel(DMA1, signal->dma_channel);\n    LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1);\n    LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);\n    LL_TIM_DisableCounter(TIM2);\n    furi_hal_bus_disable(FuriHalBusTIM2);\n    furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog);\n}\n\nvoid pulse_reader_start(PulseReader* signal) {\n    /* configure DMA to read from a timer peripheral */\n    signal->dma_config_timer.NbData = signal->size;\n\n    signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR);\n    signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer;\n    signal->dma_config_gpio.NbData = signal->size;\n\n    furi_hal_bus_enable(FuriHalBusTIM2);\n\n    /* start counter */\n    LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);\n    LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);\n    LL_TIM_SetPrescaler(TIM2, 0);\n    LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);\n    LL_TIM_SetCounter(TIM2, 0);\n    LL_TIM_EnableCounter(TIM2);\n\n    /* generator 0 gets fed by EXTI_LINEn */\n    LL_DMAMUX_SetRequestSignalID(\n        NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin));\n    /* trigger on rising edge of the interrupt */\n    LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);\n    /* now enable request generation again */\n    LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);\n\n    /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */\n    furi_hal_gpio_init_ex(\n        signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused);\n\n    /* capture current timer */\n    signal->pos = 0;\n    signal->timer_value = TIM2->CNT;\n    signal->gpio_mask = signal->gpio->pin;\n    signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask;\n\n    /* now set up DMA with these settings */\n    LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer);\n    LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio);\n    LL_DMA_EnableChannel(DMA1, signal->dma_channel);\n    LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1);\n}\n\nuint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) {\n    uint32_t start_time = DWT->CYCCNT;\n    uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000);\n\n    do {\n        /* get the DMA's next write position by reading \"remaining length\" register */\n        uint32_t dma_pos =\n            signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);\n\n        /* the DMA has advanced in the ringbuffer */\n        if(dma_pos != signal->pos) {\n            uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value;\n            uint32_t last_gpio_value = signal->gpio_value;\n\n            signal->gpio_value = signal->gpio_buffer[signal->pos];\n\n            /* check if the GPIO really toggled. if not, we lost an edge :( */\n            if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) {\n                signal->gpio_value ^= signal->gpio_mask;\n                return PULSE_READER_LOST_EDGE;\n            }\n            signal->timer_value = signal->timer_buffer[signal->pos];\n\n            signal->pos++;\n            signal->pos %= signal->size;\n\n            uint32_t delta_unit = 0;\n\n            /* probably larger values, so choose a wider data type */\n            if(signal->unit_divider > 1) {\n                delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier /\n                                        signal->unit_divider);\n            } else {\n                delta_unit = delta * signal->unit_multiplier;\n            }\n\n            /* if to be scaled to bit times, save a few instructions. should be faster */\n            if(signal->bit_time > 1) {\n                return (delta_unit + signal->bit_time / 2) / signal->bit_time;\n            }\n\n            return delta_unit;\n        }\n\n        /* check for timeout */\n        uint32_t elapsed = DWT->CYCCNT - start_time;\n\n        if(elapsed > timeout_ticks) {\n            return PULSE_READER_NO_EDGE;\n        }\n    } while(true);\n}\n"
  },
  {
    "path": "lib/pulse_reader/pulse_reader.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PULSE_READER_NO_EDGE   (0xFFFFFFFFUL)\n#define PULSE_READER_LOST_EDGE (0xFFFFFFFEUL)\n#define F_TIM2                 (64000000UL)\n\n/**\n * unit of the edge durations to return\n */\ntypedef enum {\n    PulseReaderUnit64MHz,\n    PulseReaderUnitPicosecond,\n    PulseReaderUnitNanosecond,\n    PulseReaderUnitMicrosecond,\n} PulseReaderUnit;\n\n/* using an anonymous type */\ntypedef struct PulseReader PulseReader;\n\n/** Allocate a PulseReader object\n *\n * Allocates memory for a ringbuffer and initalizes the object\n *\n * @param[in]  gpio        the GPIO to use. will get configured as input.\n * @param[in]  size        number of edges to buffer\n */\nPulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size);\n\n/** Free a PulseReader object\n *\n * Frees all memory of the given object\n *\n * @param[in]  signal      previously allocated PulseReader object.\n */\nvoid pulse_reader_free(PulseReader* signal);\n\n/** Start signal capturing\n *\n * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values.\n * Ensure that interrupts are always enabled, as the used EXTI line is handled as one.\n *\n * @param[in]  signal      previously allocated PulseReader object.\n */\nvoid pulse_reader_start(PulseReader* signal);\n\n/** Stop signal capturing\n *\n * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0\n *\n * @param[in]  signal      previously allocated PulseReader object.\n */\nvoid pulse_reader_stop(PulseReader* signal);\n\n/** Recevie a sample from ringbuffer\n *\n * Waits for the specified time until a new edge gets detected.\n * If not configured otherwise, the pulse duration will be in picosecond resolution.\n * If a bittime was configured, the return value will contain the properly rounded\n * number of bit times measured.\n *\n * @param[in]  signal      previously allocated PulseReader object.\n * @param[in]  timeout_us  time to wait for a signal [µs]\n *\n * @returns the scaled value of the pulse duration\n */\nuint32_t pulse_reader_receive(PulseReader* signal, int timeout_us);\n\n/** Get available samples\n *\n * Get the number of available samples in the ringbuffer\n *\n * @param[in]  signal  previously allocated PulseReader object.\n *\n * @returns the number of samples in buffer\n */\nuint32_t pulse_reader_samples(PulseReader* signal);\n\n/** Set timebase\n *\n * Set the timebase to be used when returning pulse duration.\n *\n * @param[in]  signal  previously allocated PulseReader object.\n * @param[in]  unit  PulseReaderUnit64MHz or PulseReaderUnitPicosecond\n */\nvoid pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit);\n\n/** Set bit time\n *\n * Set the number of timebase units per bit.\n * When set, the pulse_reader_receive() will return an already rounded\n * bit count value instead of the raw duration.\n *\n * Set to 1 to return duration again.\n *\n * @param[in]  signal    previously allocated PulseReader object.\n * @param[in]  bit_time\n */\nvoid pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time);\n\n/** Set GPIO pull direction\n *\n * Some GPIOs need pulldown, others don't. By default the\n * pull direction is GpioPullNo.\n *\n * @param[in]  signal    previously allocated PulseReader object.\n * @param[in]  pull      GPIO pull direction\n */\nvoid pulse_reader_set_pull(PulseReader* signal, GpioPull pull);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/signal_reader/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/signal_reader\",\n    ],\n    SDK_HEADERS=[\n        File(\"signal_reader.h\"),\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"signal_reader\")\nlibenv.ApplyLibFlags()\nlibenv.AppendUnique(CCFLAGS=[\"-O3\", \"-funroll-loops\", \"-Ofast\"])\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/signal_reader/parsers/iso15693/iso15693_parser.c",
    "content": "#include \"iso15693_parser.h\"\n\n#include <toolbox/bit_buffer.h>\n\n#include <furi/furi.h>\n\n#define ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE (2)\n#define ISO15693_PARSER_BITSTREAM_BUFF_SIZE     (32)\n#define ISO15693_PARSER_BITRATE_F64MHZ          (603U)\n\n#define TAG \"Iso15693Parser\"\n\ntypedef enum {\n    Iso15693ParserStateParseSoF,\n    Iso15693ParserStateParseFrame,\n    Iso15693ParserStateFail,\n} Iso15693ParserState;\n\ntypedef enum {\n    Iso15693ParserMode1OutOf4,\n    Iso15693ParserMode1OutOf256,\n\n    Iso15693ParserModeNum,\n} Iso15693ParserMode;\n\nstruct Iso15693Parser {\n    Iso15693ParserState state;\n    Iso15693ParserMode mode;\n\n    SignalReader* signal_reader;\n\n    uint8_t bitstream_buff[ISO15693_PARSER_BITSTREAM_BUFF_SIZE];\n    size_t bitstream_idx;\n    uint8_t last_byte;\n\n    bool signal_detected;\n    bool bit_offset_calculated;\n    uint8_t bit_offset;\n    size_t byte_idx;\n    size_t bytes_to_process;\n\n    uint8_t next_byte;\n    uint16_t next_byte_part;\n    bool zero_found;\n\n    BitBuffer* parsed_frame;\n    bool eof_received;\n    bool frame_parsed;\n\n    Iso15693ParserCallback callback;\n    void* context;\n};\n\ntypedef enum {\n    Iso15693ParserCommandProcessed,\n    Iso15693ParserCommandWaitData,\n    Iso15693ParserCommandFail,\n    Iso15693ParserCommandSuccess,\n} Iso15693ParserCommand;\n\ntypedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* instance);\n\nIso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) {\n    Iso15693Parser* instance = malloc(sizeof(Iso15693Parser));\n    instance->parsed_frame = bit_buffer_alloc(max_frame_size);\n\n    instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE);\n    signal_reader_set_sample_rate(\n        instance->signal_reader, SignalReaderTimeUnit64Mhz, ISO15693_PARSER_BITRATE_F64MHZ);\n    signal_reader_set_pull(instance->signal_reader, GpioPullDown);\n    signal_reader_set_polarity(instance->signal_reader, SignalReaderPolarityNormal);\n    signal_reader_set_trigger(instance->signal_reader, SignalReaderTriggerRisingFallingEdge);\n\n    return instance;\n}\n\nvoid iso15693_parser_free(Iso15693Parser* instance) {\n    furi_assert(instance);\n\n    bit_buffer_free(instance->parsed_frame);\n    signal_reader_free(instance->signal_reader);\n    free(instance);\n}\n\nvoid iso15693_parser_reset(Iso15693Parser* instance) {\n    furi_assert(instance);\n\n    instance->state = Iso15693ParserStateParseSoF;\n    instance->mode = Iso15693ParserMode1OutOf4;\n    memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff));\n    instance->bitstream_idx = 0;\n\n    instance->next_byte = 0;\n    instance->next_byte_part = 0;\n\n    instance->bit_offset = 0;\n    instance->byte_idx = 0;\n    instance->bytes_to_process = 0;\n    instance->signal_detected = false;\n    instance->bit_offset_calculated = false;\n\n    instance->last_byte = 0x00;\n    instance->zero_found = false;\n    instance->eof_received = false;\n\n    bit_buffer_reset(instance->parsed_frame);\n    instance->frame_parsed = false;\n}\n\nstatic void signal_reader_callback(SignalReaderEvent event, void* context) {\n    furi_assert(context);\n    furi_assert(event.data->data);\n    furi_assert(event.data->len == ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE / 2);\n\n    Iso15693Parser* instance = context;\n    furi_assert(instance->callback);\n\n    const uint8_t sof_1_out_of_4 = 0x21;\n    const uint8_t sof_1_out_of_256 = 0x81;\n    const uint8_t eof_single = 0x01;\n    const uint8_t eof = 0x04;\n\n    if(instance->state == Iso15693ParserStateParseSoF) {\n        if(event.data->data[0] == sof_1_out_of_4) {\n            instance->mode = Iso15693ParserMode1OutOf4;\n            instance->state = Iso15693ParserStateParseFrame;\n        } else if(event.data->data[0] == sof_1_out_of_256) {\n            instance->mode = Iso15693ParserMode1OutOf256;\n            instance->state = Iso15693ParserStateParseFrame;\n        } else if(event.data->data[0] == eof_single) {\n            instance->eof_received = true;\n            instance->callback(Iso15693ParserEventDataReceived, instance->context);\n        } else {\n            instance->state = Iso15693ParserStateFail;\n            instance->callback(Iso15693ParserEventDataReceived, instance->context);\n        }\n    } else {\n        if(instance->mode == Iso15693ParserMode1OutOf4) {\n            if(event.data->data[0] == eof) {\n                instance->eof_received = true;\n                instance->callback(Iso15693ParserEventDataReceived, instance->context);\n            } else {\n                instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0];\n                instance->bytes_to_process++;\n                if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) {\n                    instance->callback(Iso15693ParserEventDataReceived, instance->context);\n                }\n            }\n        } else {\n            instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0];\n            instance->bytes_to_process++;\n            if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) {\n                instance->callback(Iso15693ParserEventDataReceived, instance->context);\n            }\n        }\n    }\n}\n\nstatic void iso15693_parser_start_signal_reader(Iso15693Parser* instance) {\n    iso15693_parser_reset(instance);\n    signal_reader_start(instance->signal_reader, signal_reader_callback, instance);\n}\n\nvoid iso15693_parser_start(\n    Iso15693Parser* instance,\n    Iso15693ParserCallback callback,\n    void* context) {\n    furi_assert(instance);\n    furi_assert(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n    iso15693_parser_start_signal_reader(instance);\n}\n\nvoid iso15693_parser_stop(Iso15693Parser* instance) {\n    furi_assert(instance);\n\n    signal_reader_stop(instance->signal_reader);\n}\n\nstatic Iso15693ParserCommand iso15693_parser_parse_1_out_of_4(Iso15693Parser* instance) {\n    Iso15693ParserCommand command = Iso15693ParserCommandWaitData;\n    const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80};\n\n    for(size_t i = 0; i < instance->bytes_to_process; i++) {\n        // Check next pattern\n        size_t j = 0;\n        for(j = 0; j < COUNT_OF(bit_patterns_1_out_of_4); j++) {\n            if(instance->bitstream_buff[i] == bit_patterns_1_out_of_4[j]) {\n                instance->next_byte |= j << (instance->next_byte_part * 2);\n                instance->next_byte_part++;\n                if(instance->next_byte_part == 4) {\n                    instance->next_byte_part = 0;\n                    bit_buffer_append_byte(instance->parsed_frame, instance->next_byte);\n                    instance->next_byte = 0;\n                }\n                break;\n            }\n        }\n        if(j == COUNT_OF(bit_patterns_1_out_of_4)) {\n            command = Iso15693ParserCommandFail;\n            break;\n        }\n    }\n\n    if(command != Iso15693ParserCommandFail) {\n        if(instance->eof_received) {\n            command = Iso15693ParserCommandSuccess;\n            instance->frame_parsed = true;\n        }\n    }\n\n    instance->bytes_to_process = 0;\n\n    return command;\n}\n\nstatic Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance) {\n    Iso15693ParserCommand command = Iso15693ParserCommandWaitData;\n    const uint8_t eof = 0x04;\n\n    for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) {\n        // Check EoF\n        if(instance->next_byte_part == 0) {\n            if(instance->bitstream_buff[i] == eof) {\n                instance->frame_parsed = true;\n                command = Iso15693ParserCommandSuccess;\n                break;\n            }\n        }\n\n        if(instance->zero_found) {\n            if(instance->bitstream_buff[i] != 0x00) {\n                command = Iso15693ParserCommandFail;\n                break;\n            }\n        } else {\n            if(instance->bitstream_buff[i] != 0x00) {\n                for(size_t j = 0; j < 8; j++) {\n                    if(FURI_BIT(instance->bitstream_buff[i], j) == 1) {\n                        bit_buffer_append_byte(\n                            instance->parsed_frame, instance->next_byte_part * 4 + j / 2);\n                    }\n                }\n            } else {\n                instance->zero_found = true;\n            }\n        }\n        instance->next_byte_part = (instance->next_byte_part + 1) % 64;\n    }\n    instance->bytes_to_process = 0;\n    instance->byte_idx = 0;\n\n    return command;\n}\n\nstatic const Iso15693ParserStateHandler iso15693_parser_state_handlers[Iso15693ParserModeNum] = {\n    [Iso15693ParserMode1OutOf4] = iso15693_parser_parse_1_out_of_4,\n    [Iso15693ParserMode1OutOf256] = iso15693_parser_parse_1_out_of_256,\n};\n\nbool iso15693_parser_run(Iso15693Parser* instance) {\n    if(instance->state == Iso15693ParserStateFail) {\n        iso15693_parser_stop(instance);\n        iso15693_parser_start_signal_reader(instance);\n    } else if((instance->state == Iso15693ParserStateParseSoF) && (instance->eof_received)) {\n        instance->frame_parsed = true;\n    } else if(instance->bytes_to_process) {\n        Iso15693ParserCommand command = Iso15693ParserCommandProcessed;\n        while(command == Iso15693ParserCommandProcessed) {\n            command = iso15693_parser_state_handlers[instance->mode](instance);\n        }\n\n        if(command == Iso15693ParserCommandFail) {\n            iso15693_parser_stop(instance);\n            iso15693_parser_start_signal_reader(instance);\n            FURI_LOG_D(TAG, \"Frame parse failed\");\n        }\n    }\n\n    return instance->frame_parsed;\n}\n\nsize_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance) {\n    furi_assert(instance);\n\n    return bit_buffer_get_size_bytes(instance->parsed_frame);\n}\n\nvoid iso15693_parser_get_data(\n    Iso15693Parser* instance,\n    uint8_t* buff,\n    size_t buff_size,\n    size_t* data_bits) {\n    furi_assert(instance);\n    furi_assert(buff);\n    furi_assert(data_bits);\n\n    bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size);\n    *data_bits = bit_buffer_get_size(instance->parsed_frame);\n}\n"
  },
  {
    "path": "lib/signal_reader/parsers/iso15693/iso15693_parser.h",
    "content": "#pragma once\n\n#include \"../../signal_reader.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Iso15693Parser Iso15693Parser;\n\ntypedef enum {\n    Iso15693ParserEventDataReceived,\n} Iso15693ParserEvent;\n\ntypedef void (*Iso15693ParserCallback)(Iso15693ParserEvent event, void* context);\n\nIso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size);\n\nvoid iso15693_parser_free(Iso15693Parser* instance);\n\nvoid iso15693_parser_reset(Iso15693Parser* instance);\n\nvoid iso15693_parser_start(\n    Iso15693Parser* instance,\n    Iso15693ParserCallback callback,\n    void* context);\n\nvoid iso15693_parser_stop(Iso15693Parser* instance);\n\nbool iso15693_parser_run(Iso15693Parser* instance);\n\nsize_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance);\n\nvoid iso15693_parser_get_data(\n    Iso15693Parser* instance,\n    uint8_t* buff,\n    size_t buff_size,\n    size_t* data_bits);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/signal_reader/signal_reader.c",
    "content": "#include \"signal_reader.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_gpio.h>\n\n#include <stm32wbxx_ll_dma.h>\n#include <stm32wbxx_ll_dmamux.h>\n#include <stm32wbxx_ll_tim.h>\n#include <stm32wbxx_ll_exti.h>\n\n#include <furi_hal_bus.h>\n\n#define SIGNAL_READER_DMA DMA2\n\n#define SIGNAL_READER_CAPTURE_TIM         (TIM16)\n#define SIGNAL_READER_CAPTURE_TIM_CHANNEL LL_TIM_CHANNEL_CH1\n\n#define SIGNAL_READER_DMA_GPIO     LL_DMA_CHANNEL_2\n#define SIGNAL_READER_DMA_GPIO_IRQ FuriHalInterruptIdDma2Ch2\n#define SIGNAL_READER_DMA_GPIO_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_GPIO\n\n#define SIGNAL_READER_DMA_TRIGGER     LL_DMA_CHANNEL_3\n#define SIGNAL_READER_DMA_TRIGGER_IRQ FuriHalInterruptIdDma2Ch3\n#define SIGNAL_READER_DMA_TRIGGER_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_TRIGGER\n\n#define SIGNAL_READER_DMA_CNT_SYNC     LL_DMA_CHANNEL_5\n#define SIGNAL_READER_DMA_CNT_SYNC_IRQ FuriHalInterruptIdDma2Ch5\n#define SIGNAL_READER_DMA_CNT_SYNC_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_CNT_SYNC\n\nstruct SignalReader {\n    size_t buffer_size;\n    const GpioPin* pin;\n    GpioPull pull;\n    SignalReaderPolarity polarity;\n    SignalReaderTrigger trigger;\n\n    uint16_t* gpio_buffer;\n    uint8_t* bitstream_buffer;\n    uint32_t cnt_en;\n\n    uint32_t tim_cnt_compensation;\n    uint32_t tim_arr;\n\n    SignalReaderEvent event;\n    SignalReaderEventData event_data;\n\n    SignalReaderCallback callback;\n    void* context;\n};\n\n#define GPIO_PIN_MAP(pin, prefix)               \\\n    (((pin) == (LL_GPIO_PIN_0))  ? prefix##0 :  \\\n     ((pin) == (LL_GPIO_PIN_1))  ? prefix##1 :  \\\n     ((pin) == (LL_GPIO_PIN_2))  ? prefix##2 :  \\\n     ((pin) == (LL_GPIO_PIN_3))  ? prefix##3 :  \\\n     ((pin) == (LL_GPIO_PIN_4))  ? prefix##4 :  \\\n     ((pin) == (LL_GPIO_PIN_5))  ? prefix##5 :  \\\n     ((pin) == (LL_GPIO_PIN_6))  ? prefix##6 :  \\\n     ((pin) == (LL_GPIO_PIN_7))  ? prefix##7 :  \\\n     ((pin) == (LL_GPIO_PIN_8))  ? prefix##8 :  \\\n     ((pin) == (LL_GPIO_PIN_9))  ? prefix##9 :  \\\n     ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \\\n     ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \\\n     ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \\\n     ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \\\n     ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \\\n                                   prefix##15)\n\n#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)\n\nSignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size) {\n    SignalReader* instance = malloc(sizeof(SignalReader));\n\n    instance->pin = gpio_pin;\n    instance->pull = GpioPullNo;\n\n    instance->buffer_size = size;\n    instance->gpio_buffer = malloc(sizeof(uint16_t) * size * 8);\n    instance->bitstream_buffer = malloc(size);\n\n    instance->event.data = &instance->event_data;\n\n    return instance;\n}\n\nvoid signal_reader_free(SignalReader* instance) {\n    furi_check(instance);\n    furi_check(instance->gpio_buffer);\n    furi_check(instance->bitstream_buffer);\n\n    free(instance->gpio_buffer);\n    free(instance->bitstream_buffer);\n    free(instance);\n}\n\nvoid signal_reader_set_pull(SignalReader* instance, GpioPull pull) {\n    furi_check(instance);\n\n    instance->pull = pull;\n}\n\nvoid signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity) {\n    furi_check(instance);\n\n    instance->polarity = polarity;\n}\n\nvoid signal_reader_set_sample_rate(\n    SignalReader* instance,\n    SignalReaderTimeUnit time_unit,\n    uint32_t time) {\n    furi_check(instance);\n    UNUSED(time_unit);\n\n    instance->tim_arr = time;\n}\n\nvoid signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger) {\n    furi_check(instance);\n\n    instance->trigger = trigger;\n}\n\nstatic void furi_hal_sw_digital_pin_dma_rx_isr(void* context) {\n    SignalReader* instance = context;\n\n    uint16_t* gpio_buff_start = NULL;\n    uint8_t* bitstream_buff_start = NULL;\n\n    if(LL_DMA_IsActiveFlag_HT2(SIGNAL_READER_DMA)) {\n        LL_DMA_ClearFlag_HT2(SIGNAL_READER_DMA);\n        instance->event.type = SignalReaderEventTypeHalfBufferFilled;\n        gpio_buff_start = instance->gpio_buffer;\n        bitstream_buff_start = instance->bitstream_buffer;\n\n        if(instance->callback) {\n            furi_assert(gpio_buff_start);\n            furi_assert(bitstream_buff_start);\n\n            for(size_t i = 0; i < instance->buffer_size * 4; i++) {\n                if((i % 8) == 0) {\n                    bitstream_buff_start[i / 8] = 0;\n                }\n                uint8_t bit = 0;\n                if(instance->polarity == SignalReaderPolarityNormal) {\n                    bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin;\n                } else {\n                    bit = (gpio_buff_start[i] & instance->pin->pin) == 0;\n                }\n                bitstream_buff_start[i / 8] |= bit << (i % 8);\n            }\n            instance->event_data.data = bitstream_buff_start;\n            instance->event_data.len = instance->buffer_size / 2;\n            instance->callback(instance->event, instance->context);\n        }\n    }\n    if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) {\n        LL_DMA_ClearFlag_TC2(SIGNAL_READER_DMA);\n        instance->event.type = SignalReaderEventTypeFullBufferFilled;\n        gpio_buff_start = &instance->gpio_buffer[instance->buffer_size * 4];\n        bitstream_buff_start = &instance->bitstream_buffer[instance->buffer_size / 2];\n\n        if(instance->callback) {\n            furi_assert(gpio_buff_start);\n            furi_assert(bitstream_buff_start);\n\n            for(size_t i = 0; i < instance->buffer_size * 4; i++) {\n                if((i % 8) == 0) {\n                    bitstream_buff_start[i / 8] = 0;\n                }\n                uint8_t bit = 0;\n                if(instance->polarity == SignalReaderPolarityNormal) {\n                    bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin;\n                } else {\n                    bit = (gpio_buff_start[i] & instance->pin->pin) == 0;\n                }\n                bitstream_buff_start[i / 8] |= bit << (i % 8);\n            }\n            instance->event_data.data = bitstream_buff_start;\n            instance->event_data.len = instance->buffer_size / 2;\n            instance->callback(instance->event, instance->context);\n        }\n    }\n}\n\nvoid signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context) {\n    furi_check(instance);\n    furi_check(callback);\n\n    instance->callback = callback;\n    instance->context = context;\n\n    // EXTI delay compensation\n    instance->tim_cnt_compensation = 9;\n    instance->cnt_en = SIGNAL_READER_CAPTURE_TIM->CR1;\n    instance->cnt_en |= TIM_CR1_CEN;\n\n    furi_hal_bus_enable(FuriHalBusTIM16);\n\n    // Capture timer config\n    LL_TIM_SetPrescaler(SIGNAL_READER_CAPTURE_TIM, 0);\n    LL_TIM_SetCounterMode(SIGNAL_READER_CAPTURE_TIM, LL_TIM_COUNTERMODE_UP);\n    LL_TIM_SetAutoReload(SIGNAL_READER_CAPTURE_TIM, instance->tim_arr);\n    LL_TIM_SetClockDivision(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKDIVISION_DIV1);\n\n    LL_TIM_DisableARRPreload(SIGNAL_READER_CAPTURE_TIM);\n    LL_TIM_SetClockSource(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL);\n\n    // Configure TIM channel CC1\n    LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {};\n    TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN;\n    TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;\n    TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;\n    TIM_OC_InitStruct.CompareValue = (instance->tim_arr / 2);\n    TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;\n    LL_TIM_OC_Init(\n        SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL, &TIM_OC_InitStruct);\n    LL_TIM_OC_DisableFast(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL);\n\n    LL_TIM_SetTriggerOutput(SIGNAL_READER_CAPTURE_TIM, LL_TIM_TRGO_RESET);\n    LL_TIM_DisableMasterSlaveMode(SIGNAL_READER_CAPTURE_TIM);\n\n    // Start\n    LL_TIM_GenerateEvent_UPDATE(SIGNAL_READER_CAPTURE_TIM);\n\n    /* We need the EXTI to be configured as interrupt generating line, but no ISR registered */\n    furi_hal_gpio_init(\n        instance->pin, GpioModeInterruptRiseFall, instance->pull, GpioSpeedVeryHigh);\n    furi_hal_gpio_enable_int_callback(instance->pin);\n\n    /* Set DMAMUX request generation signal ID on specified DMAMUX channel */\n    LL_DMAMUX_SetRequestSignalID(\n        DMAMUX1, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(instance->pin->pin));\n    /* Set the polarity of the signal on which the DMA request is generated */\n    LL_DMAMUX_SetRequestGenPolarity(DMAMUX1, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);\n    /* Set the number of DMA requests that will be authorized after a generation event */\n    LL_DMAMUX_SetGenRequestNb(DMAMUX1, LL_DMAMUX_REQ_GEN_0, 1);\n\n    // Configure DMA Sync\n    LL_DMA_SetMemoryAddress(\n        SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t)&instance->tim_cnt_compensation);\n    LL_DMA_SetPeriphAddress(\n        SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CNT));\n    LL_DMA_ConfigTransfer(\n        SIGNAL_READER_DMA_CNT_SYNC_DEF,\n        LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |\n            LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD |\n            LL_DMA_PRIORITY_VERYHIGH);\n    LL_DMA_SetDataLength(SIGNAL_READER_DMA_CNT_SYNC_DEF, 1);\n    LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_CNT_SYNC_DEF, LL_DMAMUX_REQ_GENERATOR0);\n\n    // Configure DMA Sync\n    LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t)&instance->cnt_en);\n    LL_DMA_SetPeriphAddress(\n        SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CR1));\n    LL_DMA_ConfigTransfer(\n        SIGNAL_READER_DMA_TRIGGER_DEF,\n        LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_NOINCREMENT |\n            LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | LL_DMA_PRIORITY_VERYHIGH);\n    LL_DMA_SetDataLength(SIGNAL_READER_DMA_TRIGGER_DEF, 1);\n    LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_TRIGGER_DEF, LL_DMAMUX_REQ_GENERATOR0);\n\n    // Configure DMA Rx pin\n    LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t)instance->gpio_buffer);\n    LL_DMA_SetPeriphAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t) & (instance->pin->port->IDR));\n    LL_DMA_ConfigTransfer(\n        SIGNAL_READER_DMA_GPIO_DEF,\n        LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |\n            LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD |\n            LL_DMA_PRIORITY_HIGH);\n    LL_DMA_SetDataLength(SIGNAL_READER_DMA_GPIO_DEF, instance->buffer_size * 8);\n    LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_GPIO_DEF, LL_DMAMUX_REQ_TIM16_CH1);\n\n    // Configure DMA Channel CC1\n    LL_TIM_EnableDMAReq_CC1(SIGNAL_READER_CAPTURE_TIM);\n    LL_TIM_CC_EnableChannel(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL);\n\n    // Start DMA irq, higher priority than normal\n    furi_hal_interrupt_set_isr_ex(\n        SIGNAL_READER_DMA_GPIO_IRQ,\n        FuriHalInterruptPriorityHighest,\n        furi_hal_sw_digital_pin_dma_rx_isr,\n        instance);\n\n    // Start DMA Sync timer\n    LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF);\n\n    // Start DMA Rx pin\n    LL_DMA_EnableChannel(SIGNAL_READER_DMA_GPIO_DEF);\n    // Strat timer\n    LL_TIM_SetCounter(SIGNAL_READER_CAPTURE_TIM, 0);\n    if(instance->trigger == SignalReaderTriggerNone) {\n        LL_TIM_EnableCounter(SIGNAL_READER_CAPTURE_TIM);\n    } else {\n        LL_DMA_EnableChannel(SIGNAL_READER_DMA_TRIGGER_DEF);\n    }\n\n    LL_DMAMUX_EnableRequestGen(DMAMUX1, LL_DMAMUX_REQ_GEN_0);\n    // Need to clear flags before enabling DMA !!!!\n    if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TC1(SIGNAL_READER_DMA);\n    if(LL_DMA_IsActiveFlag_TE2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TE1(SIGNAL_READER_DMA);\n    LL_DMA_EnableIT_TC(SIGNAL_READER_DMA_GPIO_DEF);\n    LL_DMA_EnableIT_HT(SIGNAL_READER_DMA_GPIO_DEF);\n}\n\nvoid signal_reader_stop(SignalReader* instance) {\n    furi_check(instance);\n\n    furi_hal_interrupt_set_isr(SIGNAL_READER_DMA_GPIO_IRQ, NULL, NULL);\n\n    furi_hal_gpio_disable_int_callback(instance->pin);\n\n    // Deinit DMA Rx pin\n    LL_DMA_DeInit(SIGNAL_READER_DMA_GPIO_DEF);\n    // Deinit DMA Sync timer\n    LL_DMA_DeInit(SIGNAL_READER_DMA_CNT_SYNC_DEF);\n    // Deinit DMA Trigger timer\n    LL_DMA_DeInit(SIGNAL_READER_DMA_TRIGGER_DEF);\n\n    furi_hal_bus_disable(FuriHalBusTIM16);\n}\n"
  },
  {
    "path": "lib/signal_reader/signal_reader.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n#include <furi_hal_gpio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    SignalReaderEventTypeHalfBufferFilled,\n    SignalReaderEventTypeFullBufferFilled,\n} SignalReaderEventType;\n\ntypedef struct {\n    uint8_t* data;\n    size_t len;\n} SignalReaderEventData;\n\ntypedef struct {\n    SignalReaderEventType type;\n    SignalReaderEventData* data;\n} SignalReaderEvent;\n\ntypedef enum {\n    SignalReaderTimeUnit64Mhz,\n} SignalReaderTimeUnit;\n\ntypedef enum {\n    SignalReaderPolarityNormal,\n    SignalReaderPolarityInverted,\n} SignalReaderPolarity;\n\ntypedef enum {\n    SignalReaderTriggerNone,\n    SignalReaderTriggerRisingFallingEdge,\n} SignalReaderTrigger;\n\ntypedef void (*SignalReaderCallback)(SignalReaderEvent event, void* context);\n\ntypedef struct SignalReader SignalReader;\n\nSignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size);\n\nvoid signal_reader_free(SignalReader* instance);\n\nvoid signal_reader_set_pull(SignalReader* instance, GpioPull pull);\n\nvoid signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity);\n\nvoid signal_reader_set_sample_rate(\n    SignalReader* instance,\n    SignalReaderTimeUnit time_unit,\n    uint32_t time);\n\nvoid signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger);\n\nvoid signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context);\n\nvoid signal_reader_stop(SignalReader* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/stm32wb.scons",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/cmsis_core\",\n        \"#/lib/stm32wb_cmsis/Include\",\n        \"#/lib/stm32wb_hal/Inc\",\n        \"#/lib/stm32wb_copro/wpan\",\n    ],\n    CPPDEFINES=[\n        \"STM32WB\",\n        \"STM32WB55xx\",\n        \"USE_FULL_ASSERT\",\n        \"USE_FULL_LL_DRIVER\",\n    ],\n    SDK_HEADERS=env.GlobRecursive(\n        \"*_ll_*.h\",\n        Dir(\"stm32wb_hal/Inc\"),\n        exclude=\"*usb.h\",\n    ),\n)\n\nif env[\"RAM_EXEC\"]:\n    env.Append(\n        CPPDEFINES=[\n            \"VECT_TAB_SRAM\",\n        ],\n    )\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"stm32wb\")\nlibenv.Append(\n    CPPPATH=[\n        \"#/lib/stm32wb_copro/wpan/ble\",\n        \"#/lib/stm32wb_copro/wpan/ble/core\",\n        \"#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread\",\n        \"#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread/shci\",\n        \"#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread/tl\",\n        \"#/lib/stm32wb_copro/wpan/utilities\",\n    ]\n)\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*_ll_*.c\", \"stm32wb_hal/Src/\", exclude=\"*usb.c\")\nsources += Glob(\n    \"stm32wb_copro/wpan/interface/patterns/ble_thread/shci/*.c\",\n    source=True,\n)\nsources += Glob(\n    \"stm32wb_copro/wpan/interface/patterns/ble_thread/tl/*_tl*.c\",\n    exclude=[\n        \"stm32wb_copro/wpan/interface/patterns/ble_thread/tl/hci_tl_if.c\",\n        \"stm32wb_copro/wpan/interface/patterns/ble_thread/tl/shci_tl_if.c\",\n    ],\n    source=True,\n)\nsources += [\n    \"stm32wb_copro/wpan/interface/patterns/ble_thread/tl/tl_mbox.c\",\n    \"stm32wb_copro/wpan/ble/core/auto/ble_gap_aci.c\",\n    \"stm32wb_copro/wpan/ble/core/auto/ble_gatt_aci.c\",\n    \"stm32wb_copro/wpan/ble/core/auto/ble_hal_aci.c\",\n    \"stm32wb_copro/wpan/ble/core/auto/ble_hci_le.c\",\n    \"stm32wb_copro/wpan/ble/core/auto/ble_l2cap_aci.c\",\n    \"stm32wb_copro/wpan/ble/core/template/osal.c\",\n    \"stm32wb_copro/wpan/utilities/dbg_trace.c\",\n    \"stm32wb_copro/wpan/utilities/stm_list.c\",\n]\n\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/subghz/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/subghz\",\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    SDK_HEADERS=[\n        File(\"environment.h\"),\n        File(\"receiver.h\"),\n        File(\"registry.h\"),\n        File(\"subghz_worker.h\"),\n        File(\"subghz_tx_rx_worker.h\"),\n        File(\"transmitter.h\"),\n        File(\"protocols/raw.h\"),\n        File(\"protocols/public_api.h\"),\n        File(\"blocks/const.h\"),\n        File(\"blocks/decoder.h\"),\n        File(\"blocks/encoder.h\"),\n        File(\"blocks/generic.h\"),\n        File(\"blocks/math.h\"),\n        File(\"subghz_setting.h\"),\n        File(\"subghz_protocol_registry.h\"),\n        File(\"devices/cc1101_configs.h\"),\n        File(\"devices/cc1101_int/cc1101_int_interconnect.h\"),\n        File(\"subghz_file_encoder_worker.h\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"subghz\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c*\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/subghz/blocks/const.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    const uint16_t te_long;\n    const uint16_t te_short;\n    const uint16_t te_delta;\n    const uint8_t min_count_bit_for_found;\n} SubGhzBlockConst;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/blocks/decoder.c",
    "content": "#include \"decoder.h\"\n\n#define TAG \"SubGhzBlockDecoder\"\n\nvoid subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) {\n    decoder->decode_data = decoder->decode_data << 1 | bit;\n    decoder->decode_count_bit++;\n}\n\nvoid subghz_protocol_blocks_add_to_128_bit(\n    SubGhzBlockDecoder* decoder,\n    uint8_t bit,\n    uint64_t* head_64_bit) {\n    if(++decoder->decode_count_bit > 64) {\n        (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63);\n    }\n    decoder->decode_data = decoder->decode_data << 1 | bit;\n}\n\nuint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) {\n    uint8_t hash = 0;\n    uint8_t* p = (uint8_t*)&decoder->decode_data;\n    for(size_t i = 0; i < len; i++) {\n        hash ^= p[i];\n    }\n    return hash;\n}\n"
  },
  {
    "path": "lib/subghz/blocks/decoder.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzBlockDecoder SubGhzBlockDecoder;\n\nstruct SubGhzBlockDecoder {\n    uint32_t parser_step;\n    uint32_t te_last;\n    uint64_t decode_data;\n    uint8_t decode_count_bit;\n};\n\n/**\n * Add data bit when decoding.\n * @param decoder Pointer to a SubGhzBlockDecoder instance\n * @param bit data, 1bit\n */\nvoid subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit);\n\n/**\n * Add data to_128 bit when decoding.\n * @param decoder Pointer to a SubGhzBlockDecoder instance\n * @param head_64_bit Pointer to a head_64_bit\n * @param bit data, 1bit\n */\nvoid subghz_protocol_blocks_add_to_128_bit(\n    SubGhzBlockDecoder* decoder,\n    uint8_t bit,\n    uint64_t* head_64_bit);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param decoder Pointer to a SubGhzBlockDecoder instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/blocks/encoder.c",
    "content": "#include \"encoder.h\"\n#include \"math.h\"\n#include <core/check.h>\n\n#define TAG \"SubGhzBlockEncoder\"\n\nvoid subghz_protocol_blocks_set_bit_array(\n    bool bit_value,\n    uint8_t data_array[],\n    size_t set_index_bit,\n    size_t max_size_array) {\n    furi_check(set_index_bit < max_size_array * 8);\n    bit_write(data_array[set_index_bit >> 3], 7 - (set_index_bit & 0x7), bit_value);\n}\n\nbool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit) {\n    return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7));\n}\n\nsize_t subghz_protocol_blocks_get_upload_from_bit_array(\n    uint8_t data_array[],\n    size_t count_bit_data_array,\n    LevelDuration* upload,\n    size_t max_size_upload,\n    uint32_t duration_bit,\n    SubGhzProtocolBlockAlignBit align_bit) {\n    size_t bias_bit = 0;\n    size_t size_upload = 0;\n    uint32_t duration = duration_bit;\n\n    if(align_bit == SubGhzProtocolBlockAlignBitRight) {\n        if(count_bit_data_array & 0x7) {\n            bias_bit = 8 - (count_bit_data_array & 0x7);\n        }\n    }\n    size_t index_bit = bias_bit;\n\n    bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++);\n    for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) {\n        if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) {\n            duration += duration_bit;\n        } else {\n            if(size_upload > max_size_upload) {\n                furi_crash(\"SubGhz: Encoder buffer overflow\");\n            }\n            upload[size_upload++] = level_duration_make(\n                subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration);\n            last_bit = !last_bit;\n            duration = duration_bit;\n        }\n        index_bit++;\n    }\n    upload[size_upload++] = level_duration_make(\n        subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration);\n    return size_upload;\n}\n"
  },
  {
    "path": "lib/subghz/blocks/encoder.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#include <lib/toolbox/level_duration.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    bool is_running;\n    size_t repeat;\n    size_t front;\n    size_t size_upload;\n    LevelDuration* upload;\n\n} SubGhzProtocolBlockEncoder;\n\ntypedef enum {\n    SubGhzProtocolBlockAlignBitLeft,\n    SubGhzProtocolBlockAlignBitRight,\n} SubGhzProtocolBlockAlignBit;\n\n/**\n * Set data bit when encoding HEX array.\n * @param bit_value The value of the bit to be set\n * @param data_array Pointer to a HEX array\n * @param set_index_bit Number set a bit in the array starting from the left\n * @param max_size_array array size, check not to overflow\n */\nvoid subghz_protocol_blocks_set_bit_array(\n    bool bit_value,\n    uint8_t data_array[],\n    size_t set_index_bit,\n    size_t max_size_array);\n\n/**\n * Get data bit when encoding HEX array.\n * @param data_array Pointer to a HEX array\n * @param read_index_bit Number get a bit in the array starting from the left\n * @return bool value bit\n */\nbool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit);\n\n/**\n * Generating an upload from data.\n * @param data_array Pointer to a HEX array\n * @param count_bit_data_array How many bits in the array are processed\n * @param upload Pointer to a LevelDuration\n * @param max_size_upload upload size, check not to overflow\n * @param duration_bit duration 1 bit\n * @param align_bit alignment of useful bits in an array\n */\nsize_t subghz_protocol_blocks_get_upload_from_bit_array(\n    uint8_t data_array[],\n    size_t count_bit_data_array,\n    LevelDuration* upload,\n    size_t max_size_upload,\n    uint32_t duration_bit,\n    SubGhzProtocolBlockAlignBit align_bit);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/blocks/generic.c",
    "content": "#include \"generic.h\"\n#include <lib/toolbox/stream/stream.h>\n#include <lib/flipper_format/flipper_format_i.h>\n\n#define TAG \"SubGhzBlockGeneric\"\n\nvoid subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) {\n    const char* preset_name_temp;\n    if(!strcmp(preset_name, \"AM270\")) {\n        preset_name_temp = \"FuriHalSubGhzPresetOok270Async\";\n    } else if(!strcmp(preset_name, \"AM650\")) {\n        preset_name_temp = \"FuriHalSubGhzPresetOok650Async\";\n    } else if(!strcmp(preset_name, \"FM238\")) {\n        preset_name_temp = \"FuriHalSubGhzPreset2FSKDev238Async\";\n    } else if(!strcmp(preset_name, \"FM476\")) {\n        preset_name_temp = \"FuriHalSubGhzPreset2FSKDev476Async\";\n    } else {\n        preset_name_temp = \"FuriHalSubGhzPresetCustom\";\n    }\n    furi_string_set(preset_str, preset_name_temp);\n}\n\nSubGhzProtocolStatus subghz_block_generic_serialize(\n    SubGhzBlockGeneric* instance,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_check(instance);\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    do {\n        stream_clean(flipper_format_get_raw_stream(flipper_format));\n        if(!flipper_format_write_header_cstr(\n               flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) {\n            FURI_LOG_E(TAG, \"Unable to add header\");\n            res = SubGhzProtocolStatusErrorParserHeader;\n            break;\n        }\n\n        if(!flipper_format_write_uint32(flipper_format, \"Frequency\", &preset->frequency, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Frequency\");\n            res = SubGhzProtocolStatusErrorParserFrequency;\n            break;\n        }\n\n        subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);\n        if(!flipper_format_write_string_cstr(\n               flipper_format, \"Preset\", furi_string_get_cstr(temp_str))) {\n            FURI_LOG_E(TAG, \"Unable to add Preset\");\n            res = SubGhzProtocolStatusErrorParserPreset;\n            break;\n        }\n        if(!strcmp(furi_string_get_cstr(temp_str), \"FuriHalSubGhzPresetCustom\")) {\n            if(!flipper_format_write_string_cstr(\n                   flipper_format, \"Custom_preset_module\", \"CC1101\")) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_module\");\n                res = SubGhzProtocolStatusErrorParserCustomPreset;\n                break;\n            }\n            if(!flipper_format_write_hex(\n                   flipper_format, \"Custom_preset_data\", preset->data, preset->data_size)) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_data\");\n                res = SubGhzProtocolStatusErrorParserCustomPreset;\n                break;\n            }\n        }\n        if(!flipper_format_write_string_cstr(flipper_format, \"Protocol\", instance->protocol_name)) {\n            FURI_LOG_E(TAG, \"Unable to add Protocol\");\n            res = SubGhzProtocolStatusErrorParserProtocolName;\n            break;\n        }\n        uint32_t temp = instance->data_count_bit;\n        if(!flipper_format_write_uint32(flipper_format, \"Bit\", &temp, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Bit\");\n            res = SubGhzProtocolStatusErrorParserBitCount;\n            break;\n        }\n\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF;\n        }\n\n        if(!flipper_format_write_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            res = SubGhzProtocolStatusErrorParserKey;\n            break;\n        }\n        res = SubGhzProtocolStatusOk;\n    } while(false);\n    furi_string_free(temp_str);\n    return res;\n}\n\nSubGhzProtocolStatus\n    subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) {\n    furi_check(instance);\n\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    uint32_t temp_data = 0;\n\n    do {\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"Bit\", (uint32_t*)&temp_data, 1)) {\n            FURI_LOG_E(TAG, \"Missing Bit\");\n            res = SubGhzProtocolStatusErrorParserBitCount;\n            break;\n        }\n        instance->data_count_bit = (uint16_t)temp_data;\n\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        if(!flipper_format_read_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Missing Key\");\n            res = SubGhzProtocolStatusErrorParserKey;\n            break;\n        }\n        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {\n            instance->data = instance->data << 8 | key_data[i];\n        }\n\n        res = SubGhzProtocolStatusOk;\n    } while(0);\n\n    furi_string_free(temp_str);\n\n    return res;\n}\n\nSubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit(\n    SubGhzBlockGeneric* instance,\n    FlipperFormat* flipper_format,\n    uint16_t count_bit) {\n    furi_check(instance);\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(instance, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(instance->data_count_bit != count_bit) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n"
  },
  {
    "path": "lib/subghz/blocks/generic.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#include <lib/flipper_format/flipper_format.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include \"../types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzBlockGeneric SubGhzBlockGeneric;\n\nstruct SubGhzBlockGeneric {\n    const char* protocol_name;\n    uint64_t data;\n    uint32_t serial;\n    uint16_t data_count_bit;\n    uint8_t btn;\n    uint32_t cnt;\n};\n\n/**\n * Get name preset.\n * @param preset_name name preset\n * @param preset_str Output name preset\n */\nvoid subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str);\n\n/**\n * Serialize data SubGhzBlockGeneric.\n * @param instance Pointer to a SubGhzBlockGeneric instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return Status Error\n */\nSubGhzProtocolStatus subghz_block_generic_serialize(\n    SubGhzBlockGeneric* instance,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzBlockGeneric.\n * @param instance Pointer to a SubGhzBlockGeneric instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return Status Error\n */\nSubGhzProtocolStatus\n    subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format);\n\n/**\n * Deserialize data SubGhzBlockGeneric.\n * @param instance Pointer to a SubGhzBlockGeneric instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param count_bit Count bit protocol\n * @return Status Error\n */\nSubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit(\n    SubGhzBlockGeneric* instance,\n    FlipperFormat* flipper_format,\n    uint16_t count_bit);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/blocks/math.c",
    "content": "#include \"math.h\"\n\nuint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {\n    uint64_t reverse_key = 0;\n    for(uint8_t i = 0; i < bit_count; i++) {\n        reverse_key = reverse_key << 1 | bit_read(key, i);\n    }\n    return reverse_key;\n}\n\nuint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {\n    uint8_t parity = 0;\n    for(uint8_t i = 0; i < bit_count; i++) {\n        parity += bit_read(key, i);\n    }\n    return parity & 0x01;\n}\n\nuint8_t subghz_protocol_blocks_crc4(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init << 4; // LSBs are unused\n    uint8_t poly = polynomial << 4;\n    uint8_t bit;\n\n    while(size--) {\n        remainder ^= *message++;\n        for(bit = 0; bit < 8; bit++) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ poly;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder >> 4 & 0x0f; // discard the LSBs\n}\n\nuint8_t subghz_protocol_blocks_crc7(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init << 1; // LSB is unused\n    uint8_t poly = polynomial << 1;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ poly;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder >> 1 & 0x7f; // discard the LSB\n}\n\nuint8_t subghz_protocol_blocks_crc8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x80) {\n                remainder = (remainder << 1) ^ polynomial;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint8_t subghz_protocol_blocks_crc8le(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init) {\n    uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8);\n    polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8);\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 1) {\n                remainder = (remainder >> 1) ^ polynomial;\n            } else {\n                remainder = (remainder >> 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint16_t subghz_protocol_blocks_crc16lsb(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init) {\n    uint16_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte];\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 1) {\n                remainder = (remainder >> 1) ^ polynomial;\n            } else {\n                remainder = (remainder >> 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint16_t subghz_protocol_blocks_crc16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init) {\n    uint16_t remainder = init;\n\n    for(size_t byte = 0; byte < size; ++byte) {\n        remainder ^= message[byte] << 8;\n        for(uint8_t bit = 0; bit < 8; ++bit) {\n            if(remainder & 0x8000) {\n                remainder = (remainder << 1) ^ polynomial;\n            } else {\n                remainder = (remainder << 1);\n            }\n        }\n    }\n    return remainder;\n}\n\nuint8_t subghz_protocol_blocks_lfsr_digest8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key) {\n    uint8_t sum = 0;\n    for(size_t byte = 0; byte < size; ++byte) {\n        uint8_t data = message[byte];\n        for(int i = 7; i >= 0; --i) {\n            // XOR key into sum if data bit is set\n            if((data >> i) & 1) sum ^= key;\n\n            // roll the key right (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped LSB as MSB)\n            if(key & 1)\n                key = (key >> 1) ^ gen;\n            else\n                key = (key >> 1);\n        }\n    }\n    return sum;\n}\n\nuint8_t subghz_protocol_blocks_lfsr_digest8_reflect(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key) {\n    uint8_t sum = 0;\n    // Process message from last byte to first byte (reflected)\n    for(int byte = size - 1; byte >= 0; --byte) {\n        uint8_t data = message[byte];\n        // Process individual bits of each byte (reflected)\n        for(uint8_t i = 0; i < 8; ++i) {\n            // XOR key into sum if data bit is set\n            if((data >> i) & 1) {\n                sum ^= key;\n            }\n\n            // roll the key left (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped lsb as MSB)\n            if(key & 0x80)\n                key = (key << 1) ^ gen;\n            else\n                key = (key << 1);\n        }\n    }\n    return sum;\n}\n\nuint16_t subghz_protocol_blocks_lfsr_digest16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t gen,\n    uint16_t key) {\n    uint16_t sum = 0;\n    for(size_t byte = 0; byte < size; ++byte) {\n        uint8_t data = message[byte];\n        for(int8_t i = 7; i >= 0; --i) {\n            // if data bit is set then xor with key\n            if((data >> i) & 1) sum ^= key;\n\n            // roll the key right (actually the LSB is dropped here)\n            // and apply the gen (needs to include the dropped LSB as MSB)\n            if(key & 1)\n                key = (key >> 1) ^ gen;\n            else\n                key = (key >> 1);\n        }\n    }\n    return sum;\n}\n\nuint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {\n    uint32_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result += message[i];\n    }\n    return (uint8_t)result;\n}\n\nuint8_t subghz_protocol_blocks_parity8(uint8_t byte) {\n    byte ^= byte >> 4;\n    byte &= 0xf;\n    return (0x6996 >> byte) & 1;\n}\n\nuint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {\n    uint8_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result ^= subghz_protocol_blocks_parity8(message[i]);\n    }\n    return result;\n}\n\nuint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) {\n    uint8_t result = 0;\n    for(size_t i = 0; i < size; ++i) {\n        result ^= message[i];\n    }\n    return result;\n}\n"
  },
  {
    "path": "lib/subghz/blocks/math.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#define bit_read(value, bit) (((value) >> (bit)) & 0x01)\n#define bit_set(value, bit)           \\\n    ({                                \\\n        __typeof__(value) _one = (1); \\\n        (value) |= (_one << (bit));   \\\n    })\n#define bit_clear(value, bit)         \\\n    ({                                \\\n        __typeof__(value) _one = (1); \\\n        (value) &= ~(_one << (bit));  \\\n    })\n#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))\n#define DURATION_DIFF(x, y)             (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Flip the data bitwise\n *\n * @param      key        In data\n * @param      bit_count  number of data bits\n *\n * @return     Reverse data\n */\nuint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count);\n\n/** Get parity the data bitwise\n *\n * @param      key        In data\n * @param      bit_count  number of data bits\n *\n * @return     parity\n */\nuint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count);\n\n/** CRC-4\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc4(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** CRC-7\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc7(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 +\n * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal\n * bit-by-bit parity XOR)\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  byte is from x^7 to x^0 (x^8 is implicitly one)\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** \"Little-endian\" Cyclic Redundancy Check CRC-8 LE Input and output are\n * reflected, i.e. least significant bit is shifted in first\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint8_t subghz_protocol_blocks_crc8le(\n    uint8_t const message[],\n    size_t size,\n    uint8_t polynomial,\n    uint8_t init);\n\n/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is\n * shifted in first. Note that poly and init already need to be reflected\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint16_t subghz_protocol_blocks_crc16lsb(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init);\n\n/** CRC-16\n *\n * @param      message     array of bytes to check\n * @param      size        number of bytes in message\n * @param      polynomial  CRC polynomial\n * @param      init        starting crc value\n *\n * @return     CRC value\n */\nuint16_t subghz_protocol_blocks_crc16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t polynomial,\n    uint16_t init);\n\n/** Digest-8 by \"LFSR-based Toeplitz hash\"\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint8_t subghz_protocol_blocks_lfsr_digest8(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key);\n\n/** Digest-8 by \"LFSR-based Toeplitz hash\", byte reflect, bit reflect\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint8_t subghz_protocol_blocks_lfsr_digest8_reflect(\n    uint8_t const message[],\n    size_t size,\n    uint8_t gen,\n    uint8_t key);\n\n/** Digest-16 by \"LFSR-based Toeplitz hash\"\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to digest\n * @param      gen      key stream generator, needs to includes the MSB if the\n *                      LFSR is rolling\n * @param      key      initial key\n *\n * @return     digest value\n */\nuint16_t subghz_protocol_blocks_lfsr_digest16(\n    uint8_t const message[],\n    size_t size,\n    uint16_t gen,\n    uint16_t key);\n\n/** Compute Addition of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     summation value\n */\nuint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size);\n\n/** Compute bit parity of a single byte (8 bits)\n *\n * @param      byte  single byte to check\n *\n * @return     1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_parity8(uint8_t byte);\n\n/** Compute bit parity of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size);\n\n/** Compute XOR (byte-wide parity) of a number of bytes\n *\n * @param      message  bytes of message data\n * @param      size     number of bytes to sum\n *\n * @return     summation value, per bit-position 1 odd parity, 0 even parity\n */\nuint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/devices/cc1101_configs.c",
    "content": "#include \"cc1101_configs.h\"\n#include <cc1101_regs.h>\n\nconst uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[] = {\n    // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration\n\n    /* GPIO GD0 */\n    CC1101_IOCFG0,\n    0x0D, // GD0 as async serial data output/input\n\n    /* FIFO and internals */\n    CC1101_FIFOTHR,\n    0x47, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32\n\n    /* Packet engine */\n    CC1101_PKTCTRL0,\n    0x32, // Async, continious, no whitening\n\n    /* Frequency Synthesizer Control */\n    CC1101_FSCTRL1,\n    0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz\n\n    // Modem Configuration\n    CC1101_MDMCFG0,\n    0x00, // Channel spacing is 25kHz\n    CC1101_MDMCFG1,\n    0x00, // Channel spacing is 25kHz\n    CC1101_MDMCFG2,\n    0x30, // Format ASK/OOK, No preamble/sync\n    CC1101_MDMCFG3,\n    0x32, // Data rate is 3.79372 kBaud\n    CC1101_MDMCFG4,\n    0x67, // Rx BW filter is 270.833333kHz\n\n    /* Main Radio Control State Machine */\n    CC1101_MCSM0,\n    0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)\n\n    /* Frequency Offset Compensation Configuration */\n    CC1101_FOCCFG,\n    0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off\n\n    /* Automatic Gain Control */\n    CC1101_AGCCTRL0,\n    0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary\n    CC1101_AGCCTRL1,\n    0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET\n    CC1101_AGCCTRL2,\n    0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB\n\n    /* Wake on radio and timeouts control */\n    CC1101_WORCTRL,\n    0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours\n\n    /* Frontend configuration */\n    CC1101_FREND0,\n    0x11, // Adjusts current TX LO buffer + high is PATABLE[1]\n    CC1101_FREND1,\n    0xB6, //\n\n    /* End load reg */\n    0,\n    0,\n\n    //ook_async_patable[8]\n    0x00,\n    0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n\nconst uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[] = {\n    // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration\n\n    /* GPIO GD0 */\n    CC1101_IOCFG0,\n    0x0D, // GD0 as async serial data output/input\n\n    /* FIFO and internals */\n    CC1101_FIFOTHR,\n    0x07, // The only important bit is ADC_RETENTION\n\n    /* Packet engine */\n    CC1101_PKTCTRL0,\n    0x32, // Async, continious, no whitening\n\n    /* Frequency Synthesizer Control */\n    CC1101_FSCTRL1,\n    0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz\n\n    // Modem Configuration\n    CC1101_MDMCFG0,\n    0x00, // Channel spacing is 25kHz\n    CC1101_MDMCFG1,\n    0x00, // Channel spacing is 25kHz\n    CC1101_MDMCFG2,\n    0x30, // Format ASK/OOK, No preamble/sync\n    CC1101_MDMCFG3,\n    0x32, // Data rate is 3.79372 kBaud\n    CC1101_MDMCFG4,\n    0x17, // Rx BW filter is 650.000kHz\n\n    /* Main Radio Control State Machine */\n    CC1101_MCSM0,\n    0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)\n\n    /* Frequency Offset Compensation Configuration */\n    CC1101_FOCCFG,\n    0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off\n\n    /* Automatic Gain Control */\n    // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary\n    // CC1101_AGCTRL1,0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET\n    // CC1101_AGCCTRL2, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB\n    //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7.\n    CC1101_AGCCTRL0,\n    0x91, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary\n    CC1101_AGCCTRL1,\n    0x0, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET\n    CC1101_AGCCTRL2,\n    0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB\n\n    /* Wake on radio and timeouts control */\n    CC1101_WORCTRL,\n    0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours\n\n    /* Frontend configuration */\n    CC1101_FREND0,\n    0x11, // Adjusts current TX LO buffer + high is PATABLE[1]\n    CC1101_FREND1,\n    0xB6, //\n\n    /* End load reg */\n    0,\n    0,\n\n    //ook_async_patable[8]\n    0x00,\n    0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n\nconst uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[] = {\n\n    /* GPIO GD0 */\n    CC1101_IOCFG0,\n    0x0D, // GD0 as async serial data output/input\n\n    /* Frequency Synthesizer Control */\n    CC1101_FSCTRL1,\n    0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz\n\n    /* Packet engine */\n    CC1101_PKTCTRL0,\n    0x32, // Async, continious, no whitening\n    CC1101_PKTCTRL1,\n    0x04,\n\n    // // Modem Configuration\n    CC1101_MDMCFG0,\n    0x00,\n    CC1101_MDMCFG1,\n    0x02,\n    CC1101_MDMCFG2,\n    0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized)\n    CC1101_MDMCFG3,\n    0x83, // Data rate is 4.79794 kBaud\n    CC1101_MDMCFG4,\n    0x67, //Rx BW filter is 270.833333 kHz\n    CC1101_DEVIATN,\n    0x04, //Deviation 2.380371 kHz\n\n    /* Main Radio Control State Machine */\n    CC1101_MCSM0,\n    0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)\n\n    /* Frequency Offset Compensation Configuration */\n    CC1101_FOCCFG,\n    0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off\n\n    /* Automatic Gain Control */\n    CC1101_AGCCTRL0,\n    0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary\n    CC1101_AGCCTRL1,\n    0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET\n    CC1101_AGCCTRL2,\n    0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB\n\n    /* Wake on radio and timeouts control */\n    CC1101_WORCTRL,\n    0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours\n\n    /* Frontend configuration */\n    CC1101_FREND0,\n    0x10, // Adjusts current TX LO buffer\n    CC1101_FREND1,\n    0x56,\n\n    /* End load reg */\n    0,\n    0,\n\n    // 2fsk_async_patable[8]\n    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n\nconst uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[] = {\n\n    /* GPIO GD0 */\n    CC1101_IOCFG0,\n    0x0D, // GD0 as async serial data output/input\n\n    /* Frequency Synthesizer Control */\n    CC1101_FSCTRL1,\n    0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz\n\n    /* Packet engine */\n    CC1101_PKTCTRL0,\n    0x32, // Async, continious, no whitening\n    CC1101_PKTCTRL1,\n    0x04,\n\n    // // Modem Configuration\n    CC1101_MDMCFG0,\n    0x00,\n    CC1101_MDMCFG1,\n    0x02,\n    CC1101_MDMCFG2,\n    0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized)\n    CC1101_MDMCFG3,\n    0x83, // Data rate is 4.79794 kBaud\n    CC1101_MDMCFG4,\n    0x67, //Rx BW filter is 270.833333 kHz\n    CC1101_DEVIATN,\n    0x47, //Deviation 47.60742 kHz\n\n    /* Main Radio Control State Machine */\n    CC1101_MCSM0,\n    0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)\n\n    /* Frequency Offset Compensation Configuration */\n    CC1101_FOCCFG,\n    0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off\n\n    /* Automatic Gain Control */\n    CC1101_AGCCTRL0,\n    0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary\n    CC1101_AGCCTRL1,\n    0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET\n    CC1101_AGCCTRL2,\n    0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB\n\n    /* Wake on radio and timeouts control */\n    CC1101_WORCTRL,\n    0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours\n\n    /* Frontend configuration */\n    CC1101_FREND0,\n    0x10, // Adjusts current TX LO buffer\n    CC1101_FREND1,\n    0x56,\n\n    /* End load reg */\n    0,\n    0,\n\n    // 2fsk_async_patable[8]\n    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n\nconst uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[] = {\n    /* GPIO GD0 */\n    CC1101_IOCFG0,\n    0x06,\n\n    CC1101_FIFOTHR,\n    0x07, // The only important bit is ADC_RETENTION\n    CC1101_SYNC1,\n    0x46,\n    CC1101_SYNC0,\n    0x4C,\n    CC1101_ADDR,\n    0x00,\n    CC1101_PKTLEN,\n    0x00,\n    CC1101_CHANNR,\n    0x00,\n\n    CC1101_PKTCTRL0,\n    0x05,\n\n    CC1101_FSCTRL0,\n    0x23,\n    CC1101_FSCTRL1,\n    0x06,\n\n    CC1101_MDMCFG0,\n    0xF8,\n    CC1101_MDMCFG1,\n    0x22,\n    CC1101_MDMCFG2,\n    0x72,\n    CC1101_MDMCFG3,\n    0xF8,\n    CC1101_MDMCFG4,\n    0x5B,\n    CC1101_DEVIATN,\n    0x47,\n\n    CC1101_MCSM0,\n    0x18,\n    CC1101_FOCCFG,\n    0x16,\n\n    CC1101_AGCCTRL0,\n    0xB2,\n    CC1101_AGCCTRL1,\n    0x00,\n    CC1101_AGCCTRL2,\n    0xC7,\n\n    CC1101_FREND0,\n    0x10,\n    CC1101_FREND1,\n    0x56,\n\n    CC1101_BSCFG,\n    0x1C,\n    CC1101_FSTEST,\n    0x59,\n\n    /* End load reg */\n    0,\n    0,\n\n    // msk_async_patable[8]\n    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n\nconst uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[] = {\n\n    CC1101_IOCFG0,\n    0x06, //GDO0 Output Pin Configuration\n    CC1101_FIFOTHR,\n    0x47, //RX FIFO and TX FIFO Thresholds\n\n    //1 : CRC calculation in TX and CRC check in RX enabled,\n    //1 : Variable packet length mode. Packet length configured by the first byte after sync word\n    CC1101_PKTCTRL0,\n    0x05,\n\n    CC1101_FSCTRL1,\n    0x06, //Frequency Synthesizer Control\n\n    CC1101_SYNC1,\n    0x46,\n    CC1101_SYNC0,\n    0x4C,\n    CC1101_ADDR,\n    0x00,\n    CC1101_PKTLEN,\n    0x00,\n\n    CC1101_MDMCFG4,\n    0xC8, //Modem Configuration 9.99\n    CC1101_MDMCFG3,\n    0x93, //Modem Configuration\n    CC1101_MDMCFG2,\n    0x12, // 2: 16/16 sync word bits detected\n\n    CC1101_DEVIATN,\n    0x34, //Deviation = 19.042969\n    CC1101_MCSM0,\n    0x18, //Main Radio Control State Machine Configuration\n    CC1101_FOCCFG,\n    0x16, //Frequency Offset Compensation Configuration\n\n    CC1101_AGCCTRL2,\n    0x43, //AGC Control\n    CC1101_AGCCTRL1,\n    0x40,\n    CC1101_AGCCTRL0,\n    0x91,\n\n    CC1101_WORCTRL,\n    0xFB, //Wake On Radio Control\n\n    /* End load reg */\n    0,\n    0,\n\n    // gfsk_async_patable[8]\n    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n    0x00,\n};\n"
  },
  {
    "path": "lib/subghz/devices/cc1101_configs.h",
    "content": "#pragma once\n#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[];\nextern const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[];\nextern const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[];\nextern const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[];\nextern const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[];\nextern const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[];\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c",
    "content": "#include \"cc1101_int_interconnect.h\"\n#include <furi_hal.h>\n#include \"../cc1101_configs.h\"\n\n#define TAG \"SubGhzDeviceCc1101Int\"\n\nstatic bool subghz_device_cc1101_int_interconnect_is_frequency_valid(uint32_t frequency) {\n    bool ret = furi_hal_subghz_is_frequency_valid(frequency);\n    if(!ret) {\n        furi_crash(\"SubGhz: Incorrect frequency.\");\n    }\n    return ret;\n}\n\nstatic uint32_t subghz_device_cc1101_int_interconnect_set_frequency(uint32_t frequency) {\n    subghz_device_cc1101_int_interconnect_is_frequency_valid(frequency);\n    return furi_hal_subghz_set_frequency_and_path(frequency);\n}\n\nstatic bool subghz_device_cc1101_int_interconnect_start_async_tx(void* callback, void* context) {\n    return furi_hal_subghz_start_async_tx((FuriHalSubGhzAsyncTxCallback)callback, context);\n}\n\nstatic void subghz_device_cc1101_int_interconnect_start_async_rx(void* callback, void* context) {\n    furi_hal_subghz_start_async_rx((FuriHalSubGhzCaptureCallback)callback, context);\n}\n\nstatic void subghz_device_cc1101_int_interconnect_load_preset(\n    FuriHalSubGhzPreset preset,\n    uint8_t* preset_data) {\n    switch(preset) {\n    case FuriHalSubGhzPresetOok650Async:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs);\n        break;\n    case FuriHalSubGhzPresetOok270Async:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs);\n        break;\n    case FuriHalSubGhzPreset2FSKDev238Async:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);\n        break;\n    case FuriHalSubGhzPreset2FSKDev476Async:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);\n        break;\n    case FuriHalSubGhzPresetMSK99_97KbAsync:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_msk_99_97kb_async_regs);\n        break;\n    case FuriHalSubGhzPresetGFSK9_99KbAsync:\n        furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_gfsk_9_99kb_async_regs);\n        break;\n\n    default:\n        furi_hal_subghz_load_custom_preset(preset_data);\n    }\n}\n\nstatic bool subghz_device_cc1101_int_interconnect_is_connect(void) {\n    return true;\n}\n\nconst SubGhzDeviceInterconnect subghz_device_cc1101_int_interconnect = {\n    .begin = NULL,\n    .end = furi_hal_subghz_shutdown,\n    .is_connect = subghz_device_cc1101_int_interconnect_is_connect,\n    .reset = furi_hal_subghz_reset,\n    .sleep = furi_hal_subghz_sleep,\n    .idle = furi_hal_subghz_idle,\n    .load_preset = subghz_device_cc1101_int_interconnect_load_preset,\n    .set_frequency = subghz_device_cc1101_int_interconnect_set_frequency,\n    .is_frequency_valid = furi_hal_subghz_is_frequency_valid,\n    .set_async_mirror_pin = furi_hal_subghz_set_async_mirror_pin,\n    .get_data_gpio = furi_hal_subghz_get_data_gpio,\n\n    .set_tx = furi_hal_subghz_tx,\n    .flush_tx = furi_hal_subghz_flush_tx,\n    .start_async_tx = subghz_device_cc1101_int_interconnect_start_async_tx,\n    .is_async_complete_tx = furi_hal_subghz_is_async_tx_complete,\n    .stop_async_tx = furi_hal_subghz_stop_async_tx,\n\n    .set_rx = furi_hal_subghz_rx,\n    .flush_rx = furi_hal_subghz_flush_rx,\n    .start_async_rx = subghz_device_cc1101_int_interconnect_start_async_rx,\n    .stop_async_rx = furi_hal_subghz_stop_async_rx,\n\n    .get_rssi = furi_hal_subghz_get_rssi,\n    .get_lqi = furi_hal_subghz_get_lqi,\n\n    .rx_pipe_not_empty = furi_hal_subghz_rx_pipe_not_empty,\n    .is_rx_data_crc_valid = furi_hal_subghz_is_rx_data_crc_valid,\n    .read_packet = furi_hal_subghz_read_packet,\n    .write_packet = furi_hal_subghz_write_packet,\n};\n\nconst SubGhzDevice subghz_device_cc1101_int = {\n    .name = SUBGHZ_DEVICE_CC1101_INT_NAME,\n    .interconnect = &subghz_device_cc1101_int_interconnect,\n};\n"
  },
  {
    "path": "lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h",
    "content": "#pragma once\n#include \"../types.h\"\n\n#define SUBGHZ_DEVICE_CC1101_INT_NAME \"cc1101_int\"\n\ntypedef struct SubGhzDeviceCC1101Int SubGhzDeviceCC1101Int;\n\nextern const SubGhzDevice subghz_device_cc1101_int;\n"
  },
  {
    "path": "lib/subghz/devices/device_registry.h",
    "content": "#pragma once\n\n#include \"registry.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const SubGhzDeviceRegistry subghz_device_registry;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/devices/devices.c",
    "content": "#include \"devices.h\"\n\n#include \"registry.h\"\n\nvoid subghz_devices_init(void) {\n    furi_check(!subghz_device_registry_is_valid());\n    subghz_device_registry_init();\n}\n\nvoid subghz_devices_deinit(void) {\n    furi_check(subghz_device_registry_is_valid());\n    subghz_device_registry_deinit();\n}\n\nconst SubGhzDevice* subghz_devices_get_by_name(const char* device_name) {\n    furi_check(subghz_device_registry_is_valid());\n    const SubGhzDevice* device = subghz_device_registry_get_by_name(device_name);\n    return device;\n}\n\nconst char* subghz_devices_get_name(const SubGhzDevice* device) {\n    const char* ret = NULL;\n    if(device) {\n        ret = device->name;\n    }\n    return ret;\n}\n\nbool subghz_devices_begin(const SubGhzDevice* device) {\n    furi_check(device);\n    bool ret = false;\n    if(device->interconnect->begin) {\n        ret = device->interconnect->begin();\n    }\n    return ret;\n}\n\nvoid subghz_devices_end(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->end) {\n        device->interconnect->end();\n    }\n}\n\nbool subghz_devices_is_connect(const SubGhzDevice* device) {\n    furi_check(device);\n    bool ret = false;\n    if(device->interconnect->is_connect) {\n        ret = device->interconnect->is_connect();\n    }\n    return ret;\n}\n\nvoid subghz_devices_reset(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->reset) {\n        device->interconnect->reset();\n    }\n}\n\nvoid subghz_devices_sleep(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->sleep) {\n        device->interconnect->sleep();\n    }\n}\n\nvoid subghz_devices_idle(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->idle) {\n        device->interconnect->idle();\n    }\n}\n\nvoid subghz_devices_load_preset(\n    const SubGhzDevice* device,\n    FuriHalSubGhzPreset preset,\n    uint8_t* preset_data) {\n    furi_check(device);\n    if(device->interconnect->load_preset) {\n        device->interconnect->load_preset(preset, preset_data);\n    }\n}\n\nuint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency) {\n    furi_check(device);\n    uint32_t ret = 0;\n    if(device->interconnect->set_frequency) {\n        ret = device->interconnect->set_frequency(frequency);\n    }\n    return ret;\n}\n\nbool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency) {\n    bool ret = false;\n    furi_check(device);\n    if(device->interconnect->is_frequency_valid) {\n        ret = device->interconnect->is_frequency_valid(frequency);\n    }\n    return ret;\n}\n\nvoid subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio) {\n    furi_check(device);\n    if(device->interconnect->set_async_mirror_pin) {\n        device->interconnect->set_async_mirror_pin(gpio);\n    }\n}\n\nconst GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device) {\n    furi_check(device);\n    const GpioPin* ret = NULL;\n    if(device->interconnect->get_data_gpio) {\n        ret = device->interconnect->get_data_gpio();\n    }\n    return ret;\n}\n\nbool subghz_devices_set_tx(const SubGhzDevice* device) {\n    bool ret = 0;\n    furi_check(device);\n    if(device->interconnect->set_tx) {\n        ret = device->interconnect->set_tx();\n    }\n    return ret;\n}\n\nvoid subghz_devices_flush_tx(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->flush_tx) {\n        device->interconnect->flush_tx();\n    }\n}\n\nbool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context) {\n    bool ret = false;\n    furi_check(device);\n    if(device->interconnect->start_async_tx) {\n        ret = device->interconnect->start_async_tx(callback, context);\n    }\n    return ret;\n}\n\nbool subghz_devices_is_async_complete_tx(const SubGhzDevice* device) {\n    bool ret = false;\n    furi_check(device);\n    if(device->interconnect->is_async_complete_tx) {\n        ret = device->interconnect->is_async_complete_tx();\n    }\n    return ret;\n}\n\nvoid subghz_devices_stop_async_tx(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->stop_async_tx) {\n        device->interconnect->stop_async_tx();\n    }\n}\n\nvoid subghz_devices_set_rx(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->set_rx) {\n        device->interconnect->set_rx();\n    }\n}\n\nvoid subghz_devices_flush_rx(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->flush_rx) {\n        device->interconnect->flush_rx();\n    }\n}\n\nvoid subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context) {\n    furi_check(device);\n    if(device->interconnect->start_async_rx) {\n        device->interconnect->start_async_rx(callback, context);\n    }\n}\n\nvoid subghz_devices_stop_async_rx(const SubGhzDevice* device) {\n    furi_check(device);\n    if(device->interconnect->stop_async_rx) {\n        device->interconnect->stop_async_rx();\n    }\n}\n\nfloat subghz_devices_get_rssi(const SubGhzDevice* device) {\n    float ret = 0;\n    furi_check(device);\n    if(device->interconnect->get_rssi) {\n        ret = device->interconnect->get_rssi();\n    }\n    return ret;\n}\n\nuint8_t subghz_devices_get_lqi(const SubGhzDevice* device) {\n    furi_check(device);\n    uint8_t ret = 0;\n    if(device->interconnect->get_lqi) {\n        ret = device->interconnect->get_lqi();\n    }\n    return ret;\n}\n\nbool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device) {\n    furi_check(device);\n    bool ret = false;\n    if(device->interconnect->rx_pipe_not_empty) {\n        ret = device->interconnect->rx_pipe_not_empty();\n    }\n    return ret;\n}\n\nbool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device) {\n    bool ret = false;\n    furi_check(device);\n    if(device->interconnect->is_rx_data_crc_valid) {\n        ret = device->interconnect->is_rx_data_crc_valid();\n    }\n    return ret;\n}\n\nvoid subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size) {\n    furi_check(device);\n    if(device->interconnect->read_packet) {\n        device->interconnect->read_packet(data, size);\n    }\n}\n\nvoid subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size) {\n    furi_check(device);\n    if(device->interconnect->write_packet) {\n        device->interconnect->write_packet(data, size);\n    }\n}\n"
  },
  {
    "path": "lib/subghz/devices/devices.h",
    "content": "#pragma once\n\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzDevice SubGhzDevice;\n\nvoid subghz_devices_init(void);\nvoid subghz_devices_deinit(void);\n\nconst SubGhzDevice* subghz_devices_get_by_name(const char* device_name);\nconst char* subghz_devices_get_name(const SubGhzDevice* device);\nbool subghz_devices_begin(const SubGhzDevice* device);\nvoid subghz_devices_end(const SubGhzDevice* device);\nbool subghz_devices_is_connect(const SubGhzDevice* device);\nvoid subghz_devices_reset(const SubGhzDevice* device);\nvoid subghz_devices_sleep(const SubGhzDevice* device);\nvoid subghz_devices_idle(const SubGhzDevice* device);\nvoid subghz_devices_load_preset(\n    const SubGhzDevice* device,\n    FuriHalSubGhzPreset preset,\n    uint8_t* preset_data);\nuint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency);\nbool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency);\nvoid subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio);\nconst GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device);\n\nbool subghz_devices_set_tx(const SubGhzDevice* device);\nvoid subghz_devices_flush_tx(const SubGhzDevice* device);\nbool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context);\nbool subghz_devices_is_async_complete_tx(const SubGhzDevice* device);\nvoid subghz_devices_stop_async_tx(const SubGhzDevice* device);\n\nvoid subghz_devices_set_rx(const SubGhzDevice* device);\nvoid subghz_devices_flush_rx(const SubGhzDevice* device);\nvoid subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context);\nvoid subghz_devices_stop_async_rx(const SubGhzDevice* device);\n\nfloat subghz_devices_get_rssi(const SubGhzDevice* device);\nuint8_t subghz_devices_get_lqi(const SubGhzDevice* device);\n\nbool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device);\nbool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device);\nvoid subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size);\nvoid subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/devices/preset.h",
    "content": "#pragma once\n\n/** Radio Presets */\ntypedef enum {\n    FuriHalSubGhzPresetIDLE, /**< default configuration */\n    FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */\n    FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */\n    FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */\n    FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */\n    FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */\n    FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */\n    FuriHalSubGhzPresetCustom, /**Custom Preset*/\n} FuriHalSubGhzPreset;\n"
  },
  {
    "path": "lib/subghz/devices/registry.c",
    "content": "#include \"registry.h\"\n\n#include \"cc1101_int/cc1101_int_interconnect.h\"\n#include <flipper_application/plugins/plugin_manager.h>\n#include <loader/firmware_api/firmware_api.h>\n\n#define TAG \"SubGhzDeviceRegistry\"\n\nstruct SubGhzDeviceRegistry {\n    const SubGhzDevice** items;\n    size_t size;\n    PluginManager* manager;\n};\n\nstatic SubGhzDeviceRegistry* subghz_device_registry = NULL;\n\nvoid subghz_device_registry_init(void) {\n    SubGhzDeviceRegistry* subghz_device =\n        (SubGhzDeviceRegistry*)malloc(sizeof(SubGhzDeviceRegistry));\n    subghz_device->manager = plugin_manager_alloc(\n        SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID,\n        SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION,\n        firmware_api_interface);\n\n    //TODO FL-3556: fix path to plugins\n    if(plugin_manager_load_all(subghz_device->manager, EXT_PATH(\"apps_data/subghz/plugins\")) !=\n       //if(plugin_manager_load_all(subghz_device->manager, APP_DATA_PATH(\"plugins\")) !=\n       PluginManagerErrorNone) {\n        FURI_LOG_E(TAG, \"Failed to load all libs\");\n    }\n\n    subghz_device->size = plugin_manager_get_count(subghz_device->manager) + 1;\n    subghz_device->items =\n        (const SubGhzDevice**)malloc(sizeof(SubGhzDevice*) * subghz_device->size);\n    subghz_device->items[0] = &subghz_device_cc1101_int;\n    for(uint32_t i = 1; i < subghz_device->size; i++) {\n        const SubGhzDevice* plugin = plugin_manager_get_ep(subghz_device->manager, i - 1);\n        subghz_device->items[i] = plugin;\n    }\n\n    FURI_LOG_I(TAG, \"Loaded %zu radio device\", subghz_device->size);\n    subghz_device_registry = subghz_device;\n}\n\nvoid subghz_device_registry_deinit(void) {\n    plugin_manager_free(subghz_device_registry->manager);\n    free(subghz_device_registry->items);\n    free(subghz_device_registry);\n    subghz_device_registry = NULL;\n}\n\nbool subghz_device_registry_is_valid(void) {\n    return subghz_device_registry != NULL;\n}\n\nconst SubGhzDevice* subghz_device_registry_get_by_name(const char* name) {\n    furi_assert(subghz_device_registry);\n\n    if(name != NULL) {\n        for(size_t i = 0; i < subghz_device_registry->size; i++) {\n            if(strcmp(name, subghz_device_registry->items[i]->name) == 0) {\n                return subghz_device_registry->items[i];\n            }\n        }\n    }\n    return NULL;\n}\n\nconst SubGhzDevice* subghz_device_registry_get_by_index(size_t index) {\n    furi_assert(subghz_device_registry);\n    if(index < subghz_device_registry->size) {\n        return subghz_device_registry->items[index];\n    } else {\n        return NULL;\n    }\n}\n"
  },
  {
    "path": "lib/subghz/devices/registry.h",
    "content": "#pragma once\n\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzDevice SubGhzDevice;\n\nvoid subghz_device_registry_init(void);\n\nvoid subghz_device_registry_deinit(void);\n\nbool subghz_device_registry_is_valid(void);\n\n/**\n * Registration by name SubGhzDevice.\n * @param name SubGhzDevice name\n * @return SubGhzDevice* pointer to a SubGhzDevice instance\n */\nconst SubGhzDevice* subghz_device_registry_get_by_name(const char* name);\n\n/**\n * Registration subghzdevice by index in array SubGhzDevice.\n * @param index SubGhzDevice by index in array\n * @return SubGhzDevice* pointer to a SubGhzDevice instance\n */\nconst SubGhzDevice* subghz_device_registry_get_by_index(size_t index);\n\n/**\n * Getting the number of registered subghzdevices.\n * @param subghz_device SubGhzDeviceRegistry\n * @return Number of subghzdevices\n */\nsize_t subghz_device_registry_count(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/devices/types.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include \"preset.h\"\n\n#include <flipper_application/flipper_application.h>\n\n#define SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID      \"subghz_radio_device\"\n#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 1\n\ntypedef struct SubGhzDeviceRegistry SubGhzDeviceRegistry;\ntypedef struct SubGhzDevice SubGhzDevice;\n\ntypedef bool (*SubGhzBegin)(void);\ntypedef void (*SubGhzEnd)(void);\ntypedef bool (*SubGhzIsConnect)(void);\ntypedef void (*SubGhzReset)(void);\ntypedef void (*SubGhzSleep)(void);\ntypedef void (*SubGhzIdle)(void);\ntypedef void (*SubGhzLoadPreset)(FuriHalSubGhzPreset preset, uint8_t* preset_data);\ntypedef uint32_t (*SubGhzSetFrequency)(uint32_t frequency);\ntypedef bool (*SubGhzIsFrequencyValid)(uint32_t frequency);\n\ntypedef void (*SubGhzSetAsyncMirrorPin)(const GpioPin* gpio);\ntypedef const GpioPin* (*SubGhzGetDataGpio)(void);\n\ntypedef bool (*SubGhzSetTx)(void);\ntypedef void (*SubGhzFlushTx)(void);\ntypedef bool (*SubGhzStartAsyncTx)(void* callback, void* context);\ntypedef bool (*SubGhzIsAsyncCompleteTx)(void);\ntypedef void (*SubGhzStopAsyncTx)(void);\n\ntypedef void (*SubGhzSetRx)(void);\ntypedef void (*SubGhzFlushRx)(void);\ntypedef void (*SubGhzStartAsyncRx)(void* callback, void* context);\ntypedef void (*SubGhzStopAsyncRx)(void);\n\ntypedef float (*SubGhzGetRSSI)(void);\ntypedef uint8_t (*SubGhzGetLQI)(void);\n\ntypedef bool (*SubGhzRxPipeNotEmpty)(void);\ntypedef bool (*SubGhzRxIsDataCrcValid)(void);\ntypedef void (*SubGhzReadPacket)(uint8_t* data, uint8_t* size);\ntypedef void (*SubGhzWritePacket)(const uint8_t* data, uint8_t size);\n\ntypedef struct {\n    SubGhzBegin begin;\n    SubGhzEnd end;\n\n    SubGhzIsConnect is_connect;\n    SubGhzReset reset;\n    SubGhzSleep sleep;\n    SubGhzIdle idle;\n\n    SubGhzLoadPreset load_preset;\n    SubGhzSetFrequency set_frequency;\n    SubGhzIsFrequencyValid is_frequency_valid;\n    SubGhzSetAsyncMirrorPin set_async_mirror_pin;\n    SubGhzGetDataGpio get_data_gpio;\n\n    SubGhzSetTx set_tx;\n    SubGhzFlushTx flush_tx;\n    SubGhzStartAsyncTx start_async_tx;\n    SubGhzIsAsyncCompleteTx is_async_complete_tx;\n    SubGhzStopAsyncTx stop_async_tx;\n\n    SubGhzSetRx set_rx;\n    SubGhzFlushRx flush_rx;\n    SubGhzStartAsyncRx start_async_rx;\n    SubGhzStopAsyncRx stop_async_rx;\n\n    SubGhzGetRSSI get_rssi;\n    SubGhzGetLQI get_lqi;\n\n    SubGhzRxPipeNotEmpty rx_pipe_not_empty;\n    SubGhzRxIsDataCrcValid is_rx_data_crc_valid;\n    SubGhzReadPacket read_packet;\n    SubGhzWritePacket write_packet;\n\n} SubGhzDeviceInterconnect;\n\nstruct SubGhzDevice {\n    const char* name;\n    const SubGhzDeviceInterconnect* interconnect;\n};\n"
  },
  {
    "path": "lib/subghz/environment.c",
    "content": "#include \"environment.h\"\n#include \"registry.h\"\n\nstruct SubGhzEnvironment {\n    SubGhzKeystore* keystore;\n    const SubGhzProtocolRegistry* protocol_registry;\n    const char* came_atomo_rainbow_table_file_name;\n    const char* nice_flor_s_rainbow_table_file_name;\n    const char* alutech_at_4n_rainbow_table_file_name;\n};\n\nSubGhzEnvironment* subghz_environment_alloc(void) {\n    SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment));\n\n    instance->keystore = subghz_keystore_alloc();\n    instance->protocol_registry = NULL;\n    instance->came_atomo_rainbow_table_file_name = NULL;\n    instance->nice_flor_s_rainbow_table_file_name = NULL;\n    instance->alutech_at_4n_rainbow_table_file_name = NULL;\n\n    return instance;\n}\n\nvoid subghz_environment_free(SubGhzEnvironment* instance) {\n    furi_check(instance);\n\n    instance->protocol_registry = NULL;\n    instance->came_atomo_rainbow_table_file_name = NULL;\n    instance->nice_flor_s_rainbow_table_file_name = NULL;\n    instance->alutech_at_4n_rainbow_table_file_name = NULL;\n    subghz_keystore_free(instance->keystore);\n\n    free(instance);\n}\n\nbool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename) {\n    furi_check(instance);\n\n    return subghz_keystore_load(instance->keystore, filename);\n}\n\nSubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance) {\n    furi_check(instance);\n\n    return instance->keystore;\n}\n\nvoid subghz_environment_set_came_atomo_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename) {\n    furi_check(instance);\n\n    instance->came_atomo_rainbow_table_file_name = filename;\n}\n\nconst char*\n    subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance) {\n    furi_check(instance);\n\n    return instance->came_atomo_rainbow_table_file_name;\n}\n\nvoid subghz_environment_set_alutech_at_4n_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename) {\n    furi_check(instance);\n\n    instance->alutech_at_4n_rainbow_table_file_name = filename;\n}\n\nconst char*\n    subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) {\n    furi_check(instance);\n\n    return instance->alutech_at_4n_rainbow_table_file_name;\n}\n\nvoid subghz_environment_set_nice_flor_s_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename) {\n    furi_check(instance);\n\n    instance->nice_flor_s_rainbow_table_file_name = filename;\n}\n\nconst char*\n    subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance) {\n    furi_check(instance);\n\n    return instance->nice_flor_s_rainbow_table_file_name;\n}\n\nvoid subghz_environment_set_protocol_registry(\n    SubGhzEnvironment* instance,\n    const SubGhzProtocolRegistry* protocol_registry_items) {\n    furi_check(instance);\n    const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items;\n    instance->protocol_registry = protocol_registry;\n}\n\nconst SubGhzProtocolRegistry*\n    subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) {\n    furi_check(instance);\n    furi_check(instance->protocol_registry);\n    return instance->protocol_registry;\n}\n\nconst char*\n    subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) {\n    furi_check(instance);\n    furi_check(instance->protocol_registry);\n    const SubGhzProtocol* protocol =\n        subghz_protocol_registry_get_by_index(instance->protocol_registry, idx);\n    if(protocol != NULL) {\n        return protocol->name;\n    } else {\n        return NULL;\n    }\n}\n"
  },
  {
    "path": "lib/subghz/environment.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include \"registry.h\"\n\n#include \"subghz_keystore.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzEnvironment SubGhzEnvironment;\ntypedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;\n\n/**\n * Allocate SubGhzEnvironment.\n * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance\n */\nSubGhzEnvironment* subghz_environment_alloc(void);\n\n/**\n * Free SubGhzEnvironment.\n * @param instance Pointer to a SubGhzEnvironment instance\n */\nvoid subghz_environment_free(SubGhzEnvironment* instance);\n\n/**\n * Downloading the manufacture key file.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param filename Full path to the file\n * @return true On success\n */\nbool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename);\n\n/**\n * Get pointer to a SubGhzKeystore* instance.\n * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance\n */\nSubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance);\n\n/**\n * Set filename to work with Came Atomo.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param filename Full path to the file\n */\nvoid subghz_environment_set_came_atomo_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename);\n\n/**\n * Get filename to work with Came Atomo.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @return Full path to the file\n */\nconst char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance);\n\n/**\n * Set filename to work with Alutech at-4n.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param filename Full path to the file\n */\nvoid subghz_environment_set_alutech_at_4n_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename);\n\n/**\n * Get filename to work with Alutech at-4n.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @return Full path to the file\n */\nconst char*\n    subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance);\n\n/**\n * Set filename to work with Nice Flor-S.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param filename Full path to the file\n */\nvoid subghz_environment_set_nice_flor_s_rainbow_table_file_name(\n    SubGhzEnvironment* instance,\n    const char* filename);\n\n/**\n * Get filename to work with Nice Flor-S.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @return Full path to the file\n */\nconst char*\n    subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance);\n\n/**\n * Set list of protocols to work.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry\n */\nvoid subghz_environment_set_protocol_registry(\n    SubGhzEnvironment* instance,\n    const SubGhzProtocolRegistry* protocol_registry_items);\n\n/**\n * Get list of protocols to work.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @return Pointer to a SubGhzProtocolRegistry\n */\nconst SubGhzProtocolRegistry*\n    subghz_environment_get_protocol_registry(SubGhzEnvironment* instance);\n\n/**\n * Get list of protocols names.\n * @param instance Pointer to a SubGhzEnvironment instance\n * @param idx index protocols\n * @return Pointer to a SubGhzProtocolRegistry\n */\nconst char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/protocols/alutech_at_4n.c",
    "content": "#include \"alutech_at_4n.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocoAlutechAt4n\"\n\n#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF\n\nstatic const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = {\n    .te_short = 400,\n    .te_long = 800,\n    .te_delta = 140,\n    .min_count_bit_for_found = 72,\n};\n\nstruct SubGhzProtocolDecoderAlutech_at_4n {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint64_t data;\n    uint32_t crc;\n    uint16_t header_count;\n\n    const char* alutech_at_4n_rainbow_table_file_name;\n};\n\nstruct SubGhzProtocolEncoderAlutech_at_4n {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Alutech_at_4nDecoderStepReset = 0,\n    Alutech_at_4nDecoderStepCheckPreambula,\n    Alutech_at_4nDecoderStepSaveDuration,\n    Alutech_at_4nDecoderStepCheckDuration,\n} Alutech_at_4nDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = {\n    .alloc = subghz_protocol_decoder_alutech_at_4n_alloc,\n    .free = subghz_protocol_decoder_alutech_at_4n_free,\n\n    .feed = subghz_protocol_decoder_alutech_at_4n_feed,\n    .reset = subghz_protocol_decoder_alutech_at_4n_reset,\n\n    .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data,\n    .serialize = subghz_protocol_decoder_alutech_at_4n_serialize,\n    .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize,\n    .get_string = subghz_protocol_decoder_alutech_at_4n_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_alutech_at_4n = {\n    .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_alutech_at_4n_decoder,\n    .encoder = &subghz_protocol_alutech_at_4n_encoder,\n};\n\n/**\n * Read bytes from rainbow table\n * @param file_name Full path to rainbow table the file\n * @param number_alutech_at_4n_magic_data number in the array\n * @return alutech_at_4n_magic_data\n */\nstatic uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file(\n    const char* file_name,\n    uint8_t number_alutech_at_4n_magic_data) {\n    if(!strcmp(file_name, \"\")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE;\n\n    uint8_t buffer[sizeof(uint32_t)] = {0};\n    uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t);\n    uint32_t alutech_at_4n_magic_data = 0;\n\n    if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) {\n        for(size_t i = 0; i < sizeof(uint32_t); i++) {\n            alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i];\n        }\n    } else {\n        alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE;\n    }\n    return alutech_at_4n_magic_data;\n}\n\nstatic uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) {\n    uint8_t* p = (uint8_t*)&data;\n    uint8_t crc = 0xff;\n    for(uint8_t y = 0; y < 8; y++) {\n        crc = crc ^ p[y];\n        for(uint8_t i = 0; i < 8; i++) {\n            if((crc & 0x80) != 0) {\n                crc <<= 1;\n                crc ^= 0x31;\n            } else {\n                crc <<= 1;\n            }\n        }\n    }\n    return crc;\n}\n\nstatic uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) {\n    uint8_t crc = data;\n    for(uint8_t i = 0; i < 8; i++) {\n        if((crc & 0x80) != 0) {\n            crc <<= 1;\n            crc ^= 0x31;\n        } else {\n            crc <<= 1;\n        }\n    }\n    return ~crc;\n}\n\nstatic uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) {\n    uint8_t* p = (uint8_t*)&data;\n    uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];\n    uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];\n    uint32_t data3 = 0;\n    uint32_t magic_data[] = {\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0),\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1),\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2),\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3),\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4),\n        subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)};\n\n    uint32_t i = magic_data[0];\n    do {\n        data2 = data2 -\n                ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i)));\n        data3 = data2 + i;\n        i += magic_data[3];\n        data1 =\n            data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3));\n    } while(i != 0);\n\n    p[0] = (uint8_t)(data1 >> 24);\n    p[1] = (uint8_t)(data1 >> 16);\n    p[3] = (uint8_t)data1;\n    p[4] = (uint8_t)(data2 >> 24);\n    p[5] = (uint8_t)(data2 >> 16);\n    p[2] = (uint8_t)(data1 >> 8);\n    p[6] = (uint8_t)(data2 >> 8);\n    p[7] = (uint8_t)data2;\n\n    return data;\n}\n\n// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) {\n//     uint8_t* p = (uint8_t*)&data;\n//     uint32_t data1 = 0;\n//     uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];\n//     uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];\n//     uint32_t magic_data[] = {\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6),\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4),\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5),\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1),\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2),\n//         subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)};\n\n//     do {\n//         data1 = data1 + magic_data[0];\n//         data2 = data2 + ((magic_data[1] + (data3 << 4)) ^\n//                          ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3)));\n//         data3 = data3 + ((magic_data[3] + (data2 << 4)) ^\n//                          ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2)));\n//     } while(data1 != magic_data[5]);\n//     p[0] = (uint8_t)(data2 >> 24);\n//     p[1] = (uint8_t)(data2 >> 16);\n//     p[3] = (uint8_t)data2;\n//     p[4] = (uint8_t)(data3 >> 24);\n//     p[5] = (uint8_t)(data3 >> 16);\n//     p[2] = (uint8_t)(data2 >> 8);\n//     p[6] = (uint8_t)(data3 >> 8);\n//     p[7] = (uint8_t)data3;\n\n//     return data;\n// }\n\nvoid* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderAlutech_at_4n* instance =\n        malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n));\n    instance->base.protocol = &subghz_protocol_alutech_at_4n;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->alutech_at_4n_rainbow_table_file_name =\n        subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment);\n    if(instance->alutech_at_4n_rainbow_table_file_name) {\n        FURI_LOG_I(\n            TAG, \"Loading rainbow table from %s\", instance->alutech_at_4n_rainbow_table_file_name);\n    }\n    return instance;\n}\n\nvoid subghz_protocol_decoder_alutech_at_4n_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    instance->alutech_at_4n_rainbow_table_file_name = NULL;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_alutech_at_4n_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case Alutech_at_4nDecoderStepReset:\n        if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <\n                          subghz_protocol_alutech_at_4n_const.te_delta) {\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula;\n            instance->header_count++;\n        }\n        break;\n    case Alutech_at_4nDecoderStepCheckPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <\n                        subghz_protocol_alutech_at_4n_const.te_delta)) {\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n            break;\n        }\n        if((instance->header_count > 2) &&\n           (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) <\n            subghz_protocol_alutech_at_4n_const.te_delta * 10)) {\n            // Found header\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    case Alutech_at_4nDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration;\n        }\n        break;\n    case Alutech_at_4nDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 +\n                            subghz_protocol_alutech_at_4n_const.te_delta)) {\n                //add last bit\n                if(DURATION_DIFF(\n                       instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) <\n                   subghz_protocol_alutech_at_4n_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else if(\n                    DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) <\n                    subghz_protocol_alutech_at_4n_const.te_delta * 2) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n\n                // Found end TX\n                instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) {\n                    if(instance->generic.data != instance->data) {\n                        instance->generic.data = instance->data;\n\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                        instance->crc = instance->decoder.decode_data;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->decoder.decode_data = 0;\n                    instance->data = 0;\n                    instance->decoder.decode_count_bit = 0;\n                    instance->header_count = 0;\n                }\n                break;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) <\n                 subghz_protocol_alutech_at_4n_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) <\n                 subghz_protocol_alutech_at_4n_const.te_delta * 2)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                if(instance->decoder.decode_count_bit == 64) {\n                    instance->data = instance->decoder.decode_data;\n                    instance->decoder.decode_data = 0;\n                }\n                instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) <\n                 subghz_protocol_alutech_at_4n_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <\n                 subghz_protocol_alutech_at_4n_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                if(instance->decoder.decode_count_bit == 64) {\n                    instance->data = instance->decoder.decode_data;\n                    instance->decoder.decode_data = 0;\n                }\n                instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n                instance->header_count = 0;\n            }\n        } else {\n            instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param file_name Full path to rainbow table the file\n */\nstatic void subghz_protocol_alutech_at_4n_remote_controller(\n    SubGhzBlockGeneric* instance,\n    uint8_t crc,\n    const char* file_name) {\n    /**\n *  Message format 72bit LSB first\n *           data        crc\n *      XXXXXXXXXXXXXXXX  CC\n *  \n *  For analysis, you need to turn the package MSB\n *  in decoded messages format\n * \n *     crc1 serial  cnt  key\n *      cc SSSSSSSS XXxx BB \n * \n *  crc1 is calculated from the lower part of cnt\n *  key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44\n * \n */\n\n    bool status = false;\n    uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64);\n    crc = subghz_protocol_blocks_reverse_key(crc, 8);\n\n    if(crc == subghz_protocol_alutech_at_4n_crc(data)) {\n        data = subghz_protocol_alutech_at_4n_decrypt(data, file_name);\n        status = true;\n    }\n\n    if(status && ((uint8_t)(data >> 56) ==\n                  subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) {\n        instance->btn = (uint8_t)data & 0xFF;\n        instance->cnt = (uint16_t)(data >> 8) & 0xFFFF;\n        instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF;\n    }\n\n    if(!status) {\n        instance->btn = 0;\n        instance->cnt = 0;\n        instance->serial = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    return (uint8_t)instance->crc;\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    SubGhzProtocolStatus res =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((res == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"CRC\", &instance->crc, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add CRC\");\n        res = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return res;\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_alutech_at_4n_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"CRC\", (uint32_t*)&instance->crc, 1)) {\n            FURI_LOG_E(TAG, \"Missing CRC\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAlutech_at_4n* instance = context;\n    subghz_protocol_alutech_at_4n_remote_controller(\n        &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name);\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %d\\r\\n\"\n        \"Key:0x%08lX%08lX%02X\\r\\n\"\n        \"Sn:0x%08lX  Btn:0x%01X\\r\\n\"\n        \"Cnt:0x%03lX\\r\\n\",\n\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        (uint8_t)instance->crc,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/alutech_at_4n.h",
    "content": "#pragma once\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME \"Alutech AT-4N\"\n\ntypedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n;\ntypedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n;\n\nextern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder;\nextern const SubGhzProtocol subghz_protocol_alutech_at_4n;\n\n/**\n * Allocate SubGhzProtocolDecoderAlutech_at_4n.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n */\nvoid* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderAlutech_at_4n.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n */\nvoid subghz_protocol_decoder_alutech_at_4n_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderAlutech_at_4n.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n */\nvoid subghz_protocol_decoder_alutech_at_4n_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderAlutech_at_4n.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderAlutech_at_4n.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_alutech_at_4n_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/ansonic.c",
    "content": "#include \"ansonic.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolAnsonic\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c%c%c\"\n#define CNT_TO_DIP(dip)                                                                     \\\n    (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'),     \\\n        (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \\\n        (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \\\n        (dip & 0x0008 ? '1' : '0')\n\nstatic const SubGhzBlockConst subghz_protocol_ansonic_const = {\n    .te_short = 555,\n    .te_long = 1111,\n    .te_delta = 120,\n    .min_count_bit_for_found = 12,\n};\n\nstruct SubGhzProtocolDecoderAnsonic {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderAnsonic {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    AnsonicDecoderStepReset = 0,\n    AnsonicDecoderStepFoundStartBit,\n    AnsonicDecoderStepSaveDuration,\n    AnsonicDecoderStepCheckDuration,\n} AnsonicDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = {\n    .alloc = subghz_protocol_decoder_ansonic_alloc,\n    .free = subghz_protocol_decoder_ansonic_free,\n\n    .feed = subghz_protocol_decoder_ansonic_feed,\n    .reset = subghz_protocol_decoder_ansonic_reset,\n\n    .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data,\n    .serialize = subghz_protocol_decoder_ansonic_serialize,\n    .deserialize = subghz_protocol_decoder_ansonic_deserialize,\n    .get_string = subghz_protocol_decoder_ansonic_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = {\n    .alloc = subghz_protocol_encoder_ansonic_alloc,\n    .free = subghz_protocol_encoder_ansonic_free,\n\n    .deserialize = subghz_protocol_encoder_ansonic_deserialize,\n    .stop = subghz_protocol_encoder_ansonic_stop,\n    .yield = subghz_protocol_encoder_ansonic_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_ansonic = {\n    .name = SUBGHZ_PROTOCOL_ANSONIC_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_ansonic_decoder,\n    .encoder = &subghz_protocol_ansonic_encoder,\n};\n\nvoid* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic));\n\n    instance->base.protocol = &subghz_protocol_ansonic;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_ansonic_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderAnsonic* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderAnsonic* instance = context;\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    do {\n        res = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_ansonic_const.min_count_bit_for_found);\n        if(res != SubGhzProtocolStatusOk) {\n            FURI_LOG_E(TAG, \"Deserialize error\");\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_ansonic_get_upload(instance)) {\n            res = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return res;\n}\n\nvoid subghz_protocol_encoder_ansonic_stop(void* context) {\n    SubGhzProtocolEncoderAnsonic* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_ansonic_yield(void* context) {\n    SubGhzProtocolEncoderAnsonic* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic));\n    instance->base.protocol = &subghz_protocol_ansonic;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_ansonic_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_ansonic_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    instance->decoder.parser_step = AnsonicDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case AnsonicDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) <\n                        subghz_protocol_ansonic_const.te_delta * 35)) {\n            //Found header Ansonic\n            instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;\n        }\n        break;\n    case AnsonicDecoderStepFoundStartBit:\n        if(!level) {\n            break;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <\n            subghz_protocol_ansonic_const.te_delta) {\n            //Found start bit Ansonic\n            instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = AnsonicDecoderStepReset;\n        }\n        break;\n    case AnsonicDecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) {\n                instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_ansonic_const.min_count_bit_for_found) {\n                    instance->generic.serial = 0x0;\n                    instance->generic.btn = 0x0;\n\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = AnsonicDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = AnsonicDecoderStepReset;\n        }\n        break;\n    case AnsonicDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) <\n                subghz_protocol_ansonic_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) <\n                subghz_protocol_ansonic_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) <\n                 subghz_protocol_ansonic_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <\n                 subghz_protocol_ansonic_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = AnsonicDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = AnsonicDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n *        12345678(10) k   9                    \n * AAA => 10101010 1   01  0\n *\n * 1...10 - DIP\n * k- KEY\n */\n    instance->cnt = instance->data & 0xFFF;\n    instance->btn = ((instance->data >> 1) & 0x3);\n}\n\nuint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_ansonic_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderAnsonic* instance = context;\n    subghz_protocol_ansonic_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%03lX\\r\\n\"\n        \"Btn:%X\\r\\n\"\n        \"DIP:\" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.btn,\n        CNT_TO_DIP(instance->generic.cnt));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/ansonic.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_ANSONIC_NAME \"Ansonic\"\n\ntypedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic;\ntypedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic;\n\nextern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder;\nextern const SubGhzProtocol subghz_protocol_ansonic;\n\n/**\n * Allocate SubGhzProtocolEncoderAnsonic.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance\n */\nvoid* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderAnsonic.\n * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance\n */\nvoid subghz_protocol_encoder_ansonic_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return true On success\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance\n */\nvoid subghz_protocol_encoder_ansonic_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_ansonic_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderAnsonic.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance\n */\nvoid* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderAnsonic.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n */\nvoid subghz_protocol_decoder_ansonic_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderAnsonic.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n */\nvoid subghz_protocol_decoder_ansonic_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderAnsonic.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return true On success\n */\nSubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderAnsonic.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return true On success\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/base.c",
    "content": "#include \"base.h\"\n#include \"registry.h\"\n\nvoid subghz_protocol_decoder_base_set_decoder_callback(\n    SubGhzProtocolDecoderBase* decoder_base,\n    SubGhzProtocolDecoderBaseRxCallback callback,\n    void* context) {\n    furi_check(decoder_base);\n\n    decoder_base->callback = callback;\n    decoder_base->context = context;\n}\n\nbool subghz_protocol_decoder_base_get_string(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FuriString* output) {\n    furi_check(decoder_base);\n    furi_check(output);\n\n    bool status = false;\n\n    if(decoder_base->protocol && decoder_base->protocol->decoder &&\n       decoder_base->protocol->decoder->get_string) {\n        decoder_base->protocol->decoder->get_string(decoder_base, output);\n        status = true;\n    }\n\n    return status;\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_base_serialize(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_check(decoder_base);\n    furi_check(flipper_format);\n\n    SubGhzProtocolStatus status = SubGhzProtocolStatusError;\n\n    if(decoder_base->protocol && decoder_base->protocol->decoder &&\n       decoder_base->protocol->decoder->serialize) {\n        status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset);\n    }\n\n    return status;\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_base_deserialize(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FlipperFormat* flipper_format) {\n    furi_check(decoder_base);\n\n    SubGhzProtocolStatus status = SubGhzProtocolStatusError;\n\n    if(decoder_base->protocol && decoder_base->protocol->decoder &&\n       decoder_base->protocol->decoder->deserialize) {\n        status = decoder_base->protocol->decoder->deserialize(decoder_base, flipper_format);\n    }\n\n    return status;\n}\n\nuint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) {\n    furi_check(decoder_base);\n\n    uint8_t hash = 0;\n\n    if(decoder_base->protocol && decoder_base->protocol->decoder &&\n       decoder_base->protocol->decoder->get_hash_data) {\n        hash = decoder_base->protocol->decoder->get_hash_data(decoder_base);\n    }\n\n    return hash;\n}\n"
  },
  {
    "path": "lib/subghz/protocols/base.h",
    "content": "#pragma once\n\n#include \"../types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase;\n\ntypedef void (\n    *SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context);\n\ntypedef void (*SubGhzProtocolDecoderBaseSerialize)(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FuriString* output);\n\nstruct SubGhzProtocolDecoderBase {\n    // Decoder general section\n    const SubGhzProtocol* protocol;\n\n    // Callback section\n    SubGhzProtocolDecoderBaseRxCallback callback;\n    void* context;\n};\n\n/**\n * Set a callback upon completion of successful decoding of one of the protocols.\n * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance\n * @param callback Callback, SubGhzProtocolDecoderBaseRxCallback\n * @param context Context\n */\nvoid subghz_protocol_decoder_base_set_decoder_callback(\n    SubGhzProtocolDecoderBase* decoder_base,\n    SubGhzProtocolDecoderBaseRxCallback callback,\n    void* context);\n\n/**\n * Getting a textual representation of the received data.\n * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance\n * @param output Resulting text\n */\nbool subghz_protocol_decoder_base_get_string(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FuriString* output);\n\n/**\n * Serialize data SubGhzProtocolDecoderBase.\n * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return Status Error\n */\nSubGhzProtocolStatus subghz_protocol_decoder_base_serialize(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderBase.\n * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return Status Error\n */\nSubGhzProtocolStatus subghz_protocol_decoder_base_deserialize(\n    SubGhzProtocolDecoderBase* decoder_base,\n    FlipperFormat* flipper_format);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base);\n\n// Encoder Base\ntypedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase;\n\nstruct SubGhzProtocolEncoderBase {\n    // Decoder general section\n    const SubGhzProtocol* protocol;\n\n    // Callback section\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/protocols/bett.c",
    "content": "#include \"bett.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n// protocol BERNER / ELKA / TEDSEN / TELETASTER\n#define TAG \"SubGhzProtocolBett\"\n\n#define DIP_P 0b11 //(+)\n#define DIP_O 0b10 //(0)\n#define DIP_N 0b00 //(-)\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c%c\"\n#define SHOW_DIP_P(dip, check_dip)                         \\\n    ((((dip >> 0x8) >> 0x8) == check_dip) ? '*' : '_'),    \\\n        ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')\n\nstatic const SubGhzBlockConst subghz_protocol_bett_const = {\n    .te_short = 340,\n    .te_long = 2000,\n    .te_delta = 150,\n    .min_count_bit_for_found = 18,\n};\n\nstruct SubGhzProtocolDecoderBETT {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderBETT {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    BETTDecoderStepReset = 0,\n    BETTDecoderStepSaveDuration,\n    BETTDecoderStepCheckDuration,\n} BETTDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_bett_decoder = {\n    .alloc = subghz_protocol_decoder_bett_alloc,\n    .free = subghz_protocol_decoder_bett_free,\n\n    .feed = subghz_protocol_decoder_bett_feed,\n    .reset = subghz_protocol_decoder_bett_reset,\n\n    .get_hash_data = subghz_protocol_decoder_bett_get_hash_data,\n    .serialize = subghz_protocol_decoder_bett_serialize,\n    .deserialize = subghz_protocol_decoder_bett_deserialize,\n    .get_string = subghz_protocol_decoder_bett_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_bett_encoder = {\n    .alloc = subghz_protocol_encoder_bett_alloc,\n    .free = subghz_protocol_encoder_bett_free,\n\n    .deserialize = subghz_protocol_encoder_bett_deserialize,\n    .stop = subghz_protocol_encoder_bett_stop,\n    .yield = subghz_protocol_encoder_bett_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_bett = {\n    .name = SUBGHZ_PROTOCOL_BETT_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_bett_decoder,\n    .encoder = &subghz_protocol_bett_encoder,\n};\n\nvoid* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT));\n\n    instance->base.protocol = &subghz_protocol_bett;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_bett_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderBETT* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderBETT instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_long);\n        }\n    }\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_bett_const.te_short +\n                subghz_protocol_bett_const.te_long * 7);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7);\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderBETT* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_bett_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            FURI_LOG_E(TAG, \"Deserialize error\");\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_bett_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_bett_stop(void* context) {\n    SubGhzProtocolEncoderBETT* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_bett_yield(void* context) {\n    SubGhzProtocolEncoderBETT* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT));\n    instance->base.protocol = &subghz_protocol_bett;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_bett_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_bett_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    instance->decoder.parser_step = BETTDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case BETTDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <\n                        (subghz_protocol_bett_const.te_delta * 15))) {\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = BETTDecoderStepCheckDuration;\n        }\n        break;\n    case BETTDecoderStepSaveDuration:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <\n               (subghz_protocol_bett_const.te_delta * 15)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_bett_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                } else {\n                    instance->decoder.parser_step = BETTDecoderStepReset;\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <\n                    subghz_protocol_bett_const.te_delta) ||\n                   (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <\n                    subghz_protocol_bett_const.te_delta * 3)) {\n                    instance->decoder.parser_step = BETTDecoderStepCheckDuration;\n                } else {\n                    instance->decoder.parser_step = BETTDecoderStepReset;\n                }\n            }\n        }\n        break;\n    case BETTDecoderStepCheckDuration:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <\n               subghz_protocol_bett_const.te_delta * 3) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = BETTDecoderStepSaveDuration;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <\n                subghz_protocol_bett_const.te_delta) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = BETTDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = BETTDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = BETTDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_bett_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_bett_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_bett_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBETT* instance = context;\n    uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%05lX\\r\\n\"\n        \"  +:   \" DIP_PATTERN \"\\r\\n\"\n        \"  o:   \" DIP_PATTERN \"\\r\\n\"\n        \"  -:   \" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        data,\n        SHOW_DIP_P(data, DIP_P),\n        SHOW_DIP_P(data, DIP_O),\n        SHOW_DIP_P(data, DIP_N));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/bett.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_BETT_NAME \"BETT\"\n\ntypedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT;\ntypedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT;\n\nextern const SubGhzProtocolDecoder subghz_protocol_bett_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_bett_encoder;\nextern const SubGhzProtocol subghz_protocol_bett;\n\n/**\n * Allocate SubGhzProtocolEncoderBETT.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance\n */\nvoid* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderBETT.\n * @param context Pointer to a SubGhzProtocolEncoderBETT instance\n */\nvoid subghz_protocol_encoder_bett_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderBETT instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderBETT instance\n */\nvoid subghz_protocol_encoder_bett_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderBETT instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_bett_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderBETT.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance\n */\nvoid* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderBETT.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n */\nvoid subghz_protocol_decoder_bett_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderBETT.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n */\nvoid subghz_protocol_decoder_bett_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_bett_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderBETT.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_bett_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderBETT.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderBETT instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_bett_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/bin_raw.c",
    "content": "#include \"bin_raw.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n#include <lib/toolbox/float_tools.h>\n#include <lib/toolbox/stream/stream.h>\n#include <lib/flipper_format/flipper_format_i.h>\n\n#include <math.h>\n\n#define TAG \"SubGhzProtocolBinRaw\"\n\n//change very carefully, RAM ends at the most inopportune moment\n#define BIN_RAW_BUF_RAW_SIZE  2048\n#define BIN_RAW_BUF_DATA_SIZE 512\n\n#define BIN_RAW_THRESHOLD_RSSI     -85.0f\n#define BIN_RAW_DELTA_RSSI         7.0f\n#define BIN_RAW_SEARCH_CLASSES     20\n#define BIN_RAW_TE_MIN_COUNT       40\n#define BIN_RAW_BUF_MIN_DATA_COUNT 128\n#define BIN_RAW_MAX_MARKUP_COUNT   20\n\n//#define BIN_RAW_DEBUG\n\n#ifdef BIN_RAW_DEBUG\n#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__)\n#define bin_raw_debug_tag(tag, ...)                \\\n    FURI_LOG_RAW_D(\"\\033[0;32m[\" tag \"]\\033[0m \"); \\\n    FURI_LOG_RAW_D(__VA_ARGS__)\n#else\n#define bin_raw_debug(...)\n#define bin_raw_debug_tag(...)\n#endif\n\nstatic const SubGhzBlockConst subghz_protocol_bin_raw_const = {\n    .te_short = 30,\n    .te_long = 65000,\n    .te_delta = 0,\n    .min_count_bit_for_found = 0,\n};\n\ntypedef enum {\n    BinRAWDecoderStepReset = 0,\n    BinRAWDecoderStepWrite,\n    BinRAWDecoderStepBufFull,\n    BinRAWDecoderStepNoParse,\n} BinRAWDecoderStep;\n\ntypedef enum {\n    BinRAWTypeUnknown = 0,\n    BinRAWTypeNoGap,\n    BinRAWTypeGap,\n    BinRAWTypeGapRecurring,\n    BinRAWTypeGapRolling,\n    BinRAWTypeGapUnknown,\n} BinRAWType;\n\nstruct BinRAW_Markup {\n    uint16_t byte_bias;\n    uint16_t bit_count;\n};\ntypedef struct BinRAW_Markup BinRAW_Markup;\n\nstruct SubGhzProtocolDecoderBinRAW {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    int32_t* data_raw;\n    uint8_t* data;\n    BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];\n    size_t data_raw_ind;\n    uint32_t te;\n    float adaptive_threshold_rssi;\n};\n\nstruct SubGhzProtocolEncoderBinRAW {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint8_t* data;\n    BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];\n    uint32_t te;\n};\n\nconst SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = {\n    .alloc = subghz_protocol_decoder_bin_raw_alloc,\n    .free = subghz_protocol_decoder_bin_raw_free,\n\n    .feed = subghz_protocol_decoder_bin_raw_feed,\n    .reset = subghz_protocol_decoder_bin_raw_reset,\n\n    .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data,\n    .serialize = subghz_protocol_decoder_bin_raw_serialize,\n    .deserialize = subghz_protocol_decoder_bin_raw_deserialize,\n    .get_string = subghz_protocol_decoder_bin_raw_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = {\n    .alloc = subghz_protocol_encoder_bin_raw_alloc,\n    .free = subghz_protocol_encoder_bin_raw_free,\n\n    .deserialize = subghz_protocol_encoder_bin_raw_deserialize,\n    .stop = subghz_protocol_encoder_bin_raw_stop,\n    .yield = subghz_protocol_encoder_bin_raw_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_bin_raw = {\n    .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME,\n    .type = SubGhzProtocolTypeStatic,\n#ifdef BIN_RAW_DEBUG\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n#else\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n#endif\n    .decoder = &subghz_protocol_bin_raw_decoder,\n    .encoder = &subghz_protocol_bin_raw_encoder,\n};\n\nstatic uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) {\n    if(bit_count & 0x7) {\n        return (bit_count >> 3) + 1;\n    } else {\n        return bit_count >> 3;\n    }\n}\n\nvoid* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW));\n\n    instance->base.protocol = &subghz_protocol_bin_raw;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t));\n    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_bin_raw_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderBinRAW* instance = context;\n    free(instance->encoder.upload);\n    free(instance->data);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) {\n    furi_assert(instance);\n\n    //we glue all the pieces of the package into 1 long sequence with left alignment,\n    //in the uploaded data we have right alignment.\n\n    bin_raw_debug_tag(TAG, \"Recovery of offset bits in sequences\\r\\n\");\n    uint16_t i = 0;\n    uint16_t ind = 0;\n    bin_raw_debug(\"\\tind  byte_bias\\tbit_count\\tbit_bias\\r\\n\");\n    while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {\n        uint8_t bit_bias =\n            subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -\n            instance->data_markup[i].bit_count;\n        bin_raw_debug(\n            \"\\t%d\\t%d\\t%d :\\t\\t%d\\r\\n\",\n            i,\n            instance->data_markup[i].byte_bias,\n            instance->data_markup[i].bit_count,\n            bit_bias);\n        for(uint16_t y = instance->data_markup[i].byte_bias * 8;\n            y < instance->data_markup[i].byte_bias * 8 +\n                    subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -\n                    bit_bias;\n            y++) {\n            subghz_protocol_blocks_set_bit_array(\n                subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias),\n                instance->data,\n                ind++,\n                BIN_RAW_BUF_DATA_SIZE);\n        }\n        i++;\n    }\n    bin_raw_debug(\"\\r\\n\");\n#ifdef BIN_RAW_DEBUG\n    bin_raw_debug_tag(TAG, \"Restored Sequence left aligned\\r\\n\");\n    for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) {\n        bin_raw_debug(\"%02X \", instance->data[y]);\n    }\n    bin_raw_debug(\"\\r\\n\\tbin_count_result= %d\\r\\n\\r\\n\", ind);\n\n    bin_raw_debug_tag(\n        TAG, \"Maximum levels encoded in upload %zu\\r\\n\", instance->encoder.size_upload);\n#endif\n    instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(\n        instance->data,\n        ind,\n        instance->encoder.upload,\n        instance->encoder.size_upload,\n        instance->te,\n        SubGhzProtocolBlockAlignBitLeft);\n\n    bin_raw_debug_tag(TAG, \"The result %zu is levels\\r\\n\", instance->encoder.size_upload);\n    bin_raw_debug_tag(TAG, \"Remaining free memory %zu\\r\\n\", memmgr_get_free_heap());\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderBinRAW* instance = context;\n\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    uint32_t temp_data = 0;\n\n    do {\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"Bit\", (uint32_t*)&temp_data, 1)) {\n            FURI_LOG_E(TAG, \"Missing Bit\");\n            res = SubGhzProtocolStatusErrorParserBitCount;\n            break;\n        }\n\n        instance->generic.data_count_bit = (uint16_t)temp_data;\n\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            res = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n\n        temp_data = 0;\n        uint16_t ind = 0;\n        uint16_t byte_bias = 0;\n        uint16_t byte_count = 0;\n        memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n        while(flipper_format_read_uint32(flipper_format, \"Bit_RAW\", (uint32_t*)&temp_data, 1)) {\n            if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {\n                FURI_LOG_E(TAG, \"Markup overflow\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);\n            if(byte_count > BIN_RAW_BUF_DATA_SIZE) {\n                FURI_LOG_E(TAG, \"Receive buffer overflow\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n\n            instance->data_markup[ind].bit_count = temp_data;\n            instance->data_markup[ind].byte_bias = byte_bias;\n            byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);\n\n            if(!flipper_format_read_hex(\n                   flipper_format,\n                   \"Data_RAW\",\n                   instance->data + instance->data_markup[ind].byte_bias,\n                   subghz_protocol_bin_raw_get_full_byte(temp_data))) {\n                instance->data_markup[ind].bit_count = 0;\n                FURI_LOG_E(TAG, \"Missing Data_RAW\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            ind++;\n        }\n\n#ifdef BIN_RAW_DEBUG\n        uint16_t i = 0;\n        bin_raw_debug_tag(TAG, \"Download data to encoder\\r\\n\");\n        bin_raw_debug(\"\\tind  byte_bias\\tbit_count\\t\\tbin_data\");\n        while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {\n            bin_raw_debug(\n                \"\\r\\n\\t%d\\t%d\\t%d :\\t\",\n                i,\n                instance->data_markup[i].byte_bias,\n                instance->data_markup[i].bit_count);\n            for(uint16_t y = instance->data_markup[i].byte_bias;\n                y < instance->data_markup[i].byte_bias +\n                        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);\n                y++) {\n                bin_raw_debug(\"%02X \", instance->data[y]);\n            }\n            i++;\n        }\n        bin_raw_debug(\"\\r\\n\\r\\n\");\n#endif\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) {\n            res = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n\n        instance->encoder.is_running = true;\n\n        res = SubGhzProtocolStatusOk;\n    } while(0);\n\n    return res;\n}\n\nvoid subghz_protocol_encoder_bin_raw_stop(void* context) {\n    SubGhzProtocolEncoderBinRAW* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) {\n    SubGhzProtocolEncoderBinRAW* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW));\n    instance->base.protocol = &subghz_protocol_bin_raw;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->data_raw_ind = 0;\n    instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));\n    instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));\n    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n    instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_bin_raw_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n    free(instance->data_raw);\n    free(instance->data);\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_bin_raw_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n#ifdef BIN_RAW_DEBUG\n    UNUSED(instance);\n#else\n    instance->decoder.parser_step = BinRAWDecoderStepNoParse;\n    instance->data_raw_ind = 0;\n#endif\n}\n\nvoid subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n\n    if(instance->decoder.parser_step == BinRAWDecoderStepWrite) {\n        if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) {\n            instance->decoder.parser_step = BinRAWDecoderStepBufFull;\n        } else {\n            instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration);\n        }\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance\n */\nstatic bool\n    subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) {\n    struct {\n        float data;\n        uint16_t count;\n    } classes[BIN_RAW_SEARCH_CLASSES];\n\n    size_t ind = 0;\n\n    memset(classes, 0x00, sizeof(classes));\n\n    uint16_t data_markup_ind = 0;\n    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n\n    if(instance->data_raw_ind < 512) {\n        ind =\n            instance->data_raw_ind -\n            100; //there is usually garbage at the end of the record, we exclude it from the classification\n    } else {\n        ind = 512;\n    }\n\n    //sort the durations to find the shortest correlated interval\n    for(size_t i = 0; i < ind; i++) {\n        for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {\n            if(classes[k].count == 0) {\n                classes[k].data = (float)(abs(instance->data_raw[i]));\n                classes[k].count++;\n                break;\n            } else if(\n                DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) <\n                (classes[k].data / 4)) { //if the test value does not differ by more than 25%\n                classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) *\n                                   0.05f; //running average k=0.05\n                classes[k].count++;\n                break;\n            }\n        }\n    }\n\n    // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) {\n    //     //filling the classifier, it means that they received an unclean signal\n    //     return false;\n    // }\n\n    //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT\n    instance->te = subghz_protocol_bin_raw_const.te_long * 2;\n\n    bool te_ok = false;\n    uint16_t gap_ind = 0;\n    uint16_t gap_delta = 0;\n    uint32_t gap = 0;\n    int data_temp = 0;\n    BinRAWType bin_raw_type = BinRAWTypeUnknown;\n\n    //sort by number of occurrences\n    bool swap = true;\n    while(swap) { //-V1044\n        swap = false;\n        for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) {\n            if(classes[i].count > classes[i - 1].count) {\n                uint32_t data = classes[i - 1].data;\n                uint32_t count = classes[i - 1].count;\n                classes[i - 1].data = classes[i].data;\n                classes[i - 1].count = classes[i].count;\n                classes[i].data = data;\n                classes[i].count = count;\n                swap = true;\n            }\n        }\n    }\n#ifdef BIN_RAW_DEBUG\n    bin_raw_debug_tag(TAG, \"Sorted durations\\r\\n\");\n    bin_raw_debug(\"\\t\\tind\\tcount\\tus\\r\\n\");\n    for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {\n        bin_raw_debug(\"\\t\\t%zu\\t%u\\t%lu\\r\\n\", k, classes[k].count, (uint32_t)classes[k].data);\n    }\n    bin_raw_debug(\"\\r\\n\");\n#endif\n    if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) {\n        //adopted only the preamble\n        instance->te = (uint32_t)classes[0].data;\n        te_ok = true;\n        gap = 0; //gap no\n    } else {\n        //take the 2 most common durations\n        //check that there are enough\n        if((classes[0].count < BIN_RAW_TE_MIN_COUNT) ||\n           (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1)))\n            return false;\n        //arrange the first 2 date values in ascending order\n        if(classes[0].data > classes[1].data) {\n            uint32_t data = classes[1].data;\n            classes[0].data = classes[1].data;\n            classes[1].data = data;\n        }\n\n        //determine the value to be corrected\n        for(uint8_t k = 1; k < 5; k++) {\n            float delta = (classes[1].data / (classes[0].data / k));\n            bin_raw_debug_tag(TAG, \"K_div= %f\\r\\n\", (double)(delta));\n            delta -= (uint32_t)delta;\n\n            if((delta < 0.20f) || (delta > 0.80f)) {\n                instance->te = (uint32_t)classes[0].data / k;\n                bin_raw_debug_tag(TAG, \"K= %d\\r\\n\", k);\n                te_ok = true; //found a correlated duration\n                break;\n            }\n        }\n        if(!te_ok) {\n            //did not find the minimum TE satisfying the condition\n            return false;\n        }\n        bin_raw_debug_tag(TAG, \"TE= %lu\\r\\n\\r\\n\", instance->te);\n\n        //looking for a gap\n        for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) {\n            if((classes[k].count > 2) && (classes[k].data > gap)) {\n                gap = (uint32_t)classes[k].data;\n                gap_delta = gap / 5; //calculate 20% deviation from ideal value\n            }\n        }\n\n        if((gap / instance->te) <\n           10) { //make an assumption, the longest gap should be more than 10 TE\n            gap = 0; //check that our signal has a gap greater than 10*TE\n            bin_raw_type = BinRAWTypeNoGap;\n        } else {\n            bin_raw_type = BinRAWTypeGap;\n            //looking for the last occurrence of gap\n            ind = instance->data_raw_ind - 1;\n            while((ind > 0) &&\n                  (DURATION_DIFF(abs(instance->data_raw[ind]), (int32_t)gap) > gap_delta)) {\n                ind--;\n            }\n            gap_ind = ind;\n        }\n    }\n\n    //if we consider that there is a gap, then we divide the signal with respect to this gap\n    //processing input data from the end\n    if(bin_raw_type == BinRAWTypeGap) {\n        bin_raw_debug_tag(TAG, \"Tinted sequence\\r\\n\");\n        ind = (BIN_RAW_BUF_DATA_SIZE * 8);\n        uint16_t bit_count = 0;\n        do {\n            gap_ind--;\n            data_temp = (int)(roundf((float)(instance->data_raw[gap_ind]) / instance->te));\n            bin_raw_debug(\"%d \", data_temp);\n            if(data_temp == 0) bit_count++; //there is noise in the package\n            for(size_t i = 0; i < (size_t)abs(data_temp); i++) {\n                bit_count++;\n                if(ind) {\n                    ind--;\n                } else {\n                    break;\n                }\n                if(data_temp > 0) {\n                    subghz_protocol_blocks_set_bit_array(\n                        true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);\n                } else {\n                    subghz_protocol_blocks_set_bit_array(\n                        false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);\n                }\n            }\n            //split into full bytes if gap is caught\n            if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), (int32_t)gap) < gap_delta) {\n                instance->data_markup[data_markup_ind].byte_bias = ind >> 3;\n                instance->data_markup[data_markup_ind++].bit_count = bit_count;\n                bit_count = 0;\n\n                if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break;\n                ind &= 0xFFFFFFF8; //jump to the pre whole byte //-V784\n            }\n        } while(gap_ind != 0);\n        if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) {\n            instance->data_markup[data_markup_ind].byte_bias = ind >> 3;\n            instance->data_markup[data_markup_ind++].bit_count = bit_count;\n        }\n\n        bin_raw_debug(\"\\r\\n\\t count bit= %zu\\r\\n\\r\\n\", (BIN_RAW_BUF_DATA_SIZE * 8) - ind);\n\n        //reset the classifier and classify the received data\n        memset(classes, 0x00, sizeof(classes));\n\n        bin_raw_debug_tag(TAG, \"Sort the found pieces by the number of bits in them\\r\\n\");\n        for(size_t i = 0; i < data_markup_ind; i++) {\n            for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {\n                if(classes[k].count == 0) {\n                    classes[k].data = instance->data_markup[i].bit_count;\n                    classes[k].count++;\n                    break;\n                } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) {\n                    classes[k].count++;\n                    break;\n                }\n            }\n        }\n\n#ifdef BIN_RAW_DEBUG\n        bin_raw_debug(\"\\t\\tind\\tcount\\tus\\r\\n\");\n        for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {\n            bin_raw_debug(\"\\t\\t%zu\\t%u\\t%lu\\r\\n\", k, classes[k].count, (uint32_t)classes[k].data);\n        }\n        bin_raw_debug(\"\\r\\n\");\n#endif\n\n        //choose the value with the maximum repetition\n        data_temp = 0;\n        for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) {\n            if((classes[i].count > 1) && (data_temp < classes[i].count))\n                data_temp = (int)classes[i].data;\n        }\n\n        //if(data_markup_ind == 0) return false;\n\n#ifdef BIN_RAW_DEBUG\n        //output in reverse order\n        bin_raw_debug_tag(TAG, \"Found sequences\\r\\n\");\n        bin_raw_debug(\"\\tind  byte_bias\\tbit_count\\t\\tbin_data\\r\\n\");\n        uint16_t data_markup_ind_temp = data_markup_ind;\n        if(data_markup_ind) {\n            data_markup_ind_temp--;\n            for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) {\n                if(instance->data_markup[data_markup_ind_temp].byte_bias == i) {\n                    bin_raw_debug(\n                        \"\\r\\n\\t%d\\t%d\\t%d :\\t\",\n                        data_markup_ind_temp,\n                        instance->data_markup[data_markup_ind_temp].byte_bias,\n                        instance->data_markup[data_markup_ind_temp].bit_count);\n                    if(data_markup_ind_temp != 0) data_markup_ind_temp--;\n                }\n                bin_raw_debug(\"%02X \", instance->data[i]);\n            }\n            bin_raw_debug(\"\\r\\n\\r\\n\");\n        }\n        //compare data in chunks with the same number of bits\n        bin_raw_debug_tag(TAG, \"Analyze sequences of long %d bit\\r\\n\\r\\n\", data_temp);\n#endif\n\n        //if(data_temp == 0) data_temp = (int)classes[0].data;\n\n        if(data_temp != 0) {\n            //check that data in transmission is repeated every packet\n            for(uint16_t i = 0; i < data_markup_ind - 1; i++) {\n                if((instance->data_markup[i].bit_count == data_temp) &&\n                   (instance->data_markup[i + 1].bit_count == data_temp)) {\n                    //if the number of bits in adjacent parcels is the same, compare the data\n                    bin_raw_debug_tag(\n                        TAG,\n                        \"Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\\r\\n\",\n                        i,\n                        i + 1,\n                        instance->data[instance->data_markup[i].byte_bias],\n                        instance->data[instance->data_markup[i + 1].byte_bias],\n                        instance->data\n                            [instance->data_markup[i].byte_bias +\n                             subghz_protocol_bin_raw_get_full_byte(\n                                 instance->data_markup[i].bit_count) -\n                             1],\n                        instance->data\n                            [instance->data_markup[i + 1].byte_bias +\n                             subghz_protocol_bin_raw_get_full_byte(\n                                 instance->data_markup[i + 1].bit_count) -\n                             1]);\n\n                    uint16_t byte_count =\n                        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);\n                    if(memcmp(\n                           instance->data + instance->data_markup[i].byte_bias,\n                           instance->data + instance->data_markup[i + 1].byte_bias,\n                           byte_count - 1) == 0) {\n                        bin_raw_debug_tag(\n                            TAG, \"Match found bin_raw_type=BinRAWTypeGapRecurring\\r\\n\\r\\n\");\n\n                        //place in 1 element the offset to valid data\n                        instance->data_markup[0].bit_count = instance->data_markup[i].bit_count;\n                        instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias;\n                        //markup end sign\n                        instance->data_markup[1].bit_count = 0;\n                        instance->data_markup[1].byte_bias = 0;\n\n                        bin_raw_type = BinRAWTypeGapRecurring;\n                        i = data_markup_ind;\n                        break;\n                    }\n                }\n            }\n        }\n\n        if(bin_raw_type == BinRAWTypeGap) {\n            // check that retry occurs every n packets\n            for(uint16_t i = 0; i < data_markup_ind - 2; i++) {\n                uint16_t byte_count =\n                    subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);\n                for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) {\n                    bin_raw_debug_tag(\n                        TAG,\n                        \"Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\\r\\n\",\n                        i,\n                        y,\n                        instance->data[instance->data_markup[i].byte_bias],\n                        instance->data[instance->data_markup[y].byte_bias],\n                        instance->data\n                            [instance->data_markup[i].byte_bias +\n                             subghz_protocol_bin_raw_get_full_byte(\n                                 instance->data_markup[i].bit_count) -\n                             1],\n                        instance->data\n                            [instance->data_markup[y].byte_bias +\n                             subghz_protocol_bin_raw_get_full_byte(\n                                 instance->data_markup[y].bit_count) -\n                             1]);\n\n                    if(byte_count ==\n                       subghz_protocol_bin_raw_get_full_byte(\n                           instance->data_markup[y].bit_count)) { //if the length in bytes matches\n\n                        if((memcmp(\n                                instance->data + instance->data_markup[i].byte_bias,\n                                instance->data + instance->data_markup[y].byte_bias,\n                                byte_count - 1) == 0) &&\n                           (memcmp(\n                                instance->data + instance->data_markup[i + 1].byte_bias,\n                                instance->data + instance->data_markup[y + 1].byte_bias,\n                                byte_count - 1) == 0)) {\n                            uint8_t index = 0;\n#ifdef BIN_RAW_DEBUG\n                            bin_raw_debug_tag(\n                                TAG, \"Match found bin_raw_type=BinRAWTypeGapRolling\\r\\n\\r\\n\");\n                            //output in reverse order\n                            bin_raw_debug(\"\\tind  byte_bias\\tbit_count\\t\\tbin_data\\r\\n\");\n                            index = y - 1;\n                            for(size_t z = instance->data_markup[y].byte_bias + byte_count;\n                                z < instance->data_markup[i].byte_bias + byte_count;\n                                z++) {\n                                if(instance->data_markup[index].byte_bias == z) {\n                                    bin_raw_debug(\n                                        \"\\r\\n\\t%d\\t%d\\t%d :\\t\",\n                                        index,\n                                        instance->data_markup[index].byte_bias,\n                                        instance->data_markup[index].bit_count);\n                                    if(index != 0) index--;\n                                }\n                                bin_raw_debug(\"%02X \", instance->data[z]);\n                            }\n\n                            bin_raw_debug(\"\\r\\n\\r\\n\");\n#endif\n                            BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];\n                            memcpy(\n                                markup_temp,\n                                instance->data_markup,\n                                BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n                            memset(\n                                instance->data_markup,\n                                0x00,\n                                BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n\n                            for(index = i; index < y; index++) {\n                                instance->data_markup[index - i].bit_count =\n                                    markup_temp[y - index - 1].bit_count;\n                                instance->data_markup[index - i].byte_bias =\n                                    markup_temp[y - index - 1].byte_bias;\n                            }\n\n                            bin_raw_type = BinRAWTypeGapRolling;\n                            i = data_markup_ind;\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n        if(bin_raw_type == BinRAWTypeGap) {\n            if(data_temp != 0) { //there are sequences with the same number of bits\n\n                BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];\n                memcpy(\n                    markup_temp,\n                    instance->data_markup,\n                    BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n                memset(\n                    instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n                uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp);\n                uint16_t index = 0;\n                uint16_t it = BIN_RAW_MAX_MARKUP_COUNT;\n                do {\n                    it--;\n                    if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) ==\n                       byte_count) {\n                        instance->data_markup[index].bit_count = markup_temp[it].bit_count;\n                        instance->data_markup[index].byte_bias = markup_temp[it].byte_bias;\n                        index++;\n                        bin_raw_type = BinRAWTypeGapUnknown;\n                    }\n                } while(it != 0);\n            }\n        }\n\n        if(bin_raw_type != BinRAWTypeGap)\n            return true;\n        else\n            return false;\n\n    } else {\n        // if bin_raw_type == BinRAWTypeGap\n        bin_raw_debug_tag(TAG, \"Sequence analysis without gap\\r\\n\");\n        ind = 0;\n        for(size_t i = 0; i < instance->data_raw_ind; i++) {\n            int data_temp = (int)(roundf((float)(instance->data_raw[i]) / instance->te));\n            if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise\n            bin_raw_debug(\"%d  \", data_temp);\n\n            for(size_t k = 0; k < (size_t)abs(data_temp); k++) {\n                if(data_temp > 0) {\n                    subghz_protocol_blocks_set_bit_array(\n                        true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);\n                } else {\n                    subghz_protocol_blocks_set_bit_array(\n                        false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);\n                }\n                if(ind == BIN_RAW_BUF_DATA_SIZE * 8) {\n                    i = instance->data_raw_ind;\n                    break;\n                }\n            }\n        }\n\n        if(ind != 0) {\n            bin_raw_type = BinRAWTypeNoGap;\n            //right alignment\n            uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind;\n#ifdef BIN_RAW_DEBUG\n            bin_raw_debug(\n                \"\\r\\n\\t count bit= %zu\\tcount full byte= %d\\tbias bit= %d\\r\\n\\r\\n\",\n                ind,\n                subghz_protocol_bin_raw_get_full_byte(ind),\n                bit_bias);\n\n            for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {\n                bin_raw_debug(\"%02X \", instance->data[i]);\n            }\n            bin_raw_debug(\"\\r\\n\\r\\n\");\n#endif\n            //checking that the received sequence contains useful data\n            bool data_check = false;\n            for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {\n                if(instance->data[i] != 0) {\n                    data_check = true;\n                    break;\n                }\n            }\n\n            if(data_check) {\n                for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) {\n                    instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) |\n                                        (instance->data[i] >> bit_bias);\n                }\n                instance->data[0] = (instance->data[0] >> bit_bias);\n\n#ifdef BIN_RAW_DEBUG\n                bin_raw_debug_tag(TAG, \"Data right alignment\\r\\n\");\n                for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {\n                    bin_raw_debug(\"%02X \", instance->data[i]);\n                }\n                bin_raw_debug(\"\\r\\n\\r\\n\");\n#endif\n                instance->data_markup[0].bit_count = ind;\n                instance->data_markup[0].byte_bias = 0;\n\n                return true;\n            } else {\n                return false;\n            }\n        } else {\n            return false;\n        }\n    }\n    return false;\n}\n\nvoid subghz_protocol_decoder_bin_raw_data_input_rssi(\n    SubGhzProtocolDecoderBinRAW* instance,\n    float rssi) {\n    furi_check(instance);\n    switch(instance->decoder.parser_step) {\n    case BinRAWDecoderStepReset:\n\n        bin_raw_debug(\"%ld %ld :\", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi);\n        if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {\n            instance->data_raw_ind = 0;\n            memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));\n            memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));\n            instance->decoder.parser_step = BinRAWDecoderStepWrite;\n            bin_raw_debug_tag(TAG, \"RSSI\\r\\n\");\n        } else {\n            //adaptive noise level adjustment\n            instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f;\n        }\n        break;\n\n    case BinRAWDecoderStepBufFull:\n    case BinRAWDecoderStepWrite:\n#ifdef BIN_RAW_DEBUG\n        if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {\n            bin_raw_debug(\"\\033[0;32m%ld \\033[0m \", (int32_t)rssi);\n        } else {\n            bin_raw_debug(\"%ld \", (int32_t)rssi);\n        }\n#endif\n        if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {\n#ifdef BIN_RAW_DEBUG\n            bin_raw_debug(\"\\r\\n\\r\\n\");\n            bin_raw_debug_tag(TAG, \"Data for analysis, positive high, negative low, us\\r\\n\");\n            for(size_t i = 0; i < instance->data_raw_ind; i++) {\n                bin_raw_debug(\"%ld \", instance->data_raw[i]);\n            }\n            bin_raw_debug(\"\\r\\n\\t count data= %zu\\r\\n\\r\\n\", instance->data_raw_ind);\n#endif\n            instance->decoder.parser_step = BinRAWDecoderStepReset;\n            instance->generic.data_count_bit = 0;\n            if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) {\n                if(subghz_protocol_bin_raw_check_remote_controller(instance)) {\n                    bin_raw_debug_tag(TAG, \"Sequence found\\r\\n\");\n                    bin_raw_debug(\"\\tind  byte_bias\\tbit_count\\t\\tbin_data\");\n                    uint16_t i = 0;\n                    while((i < BIN_RAW_MAX_MARKUP_COUNT) &&\n                          (instance->data_markup[i].bit_count != 0)) {\n                        instance->generic.data_count_bit += instance->data_markup[i].bit_count;\n#ifdef BIN_RAW_DEBUG\n                        bin_raw_debug(\n                            \"\\r\\n\\t%d\\t%d\\t%d :\\t\",\n                            i,\n                            instance->data_markup[i].byte_bias,\n                            instance->data_markup[i].bit_count);\n                        for(uint16_t y = instance->data_markup[i].byte_bias;\n                            y < instance->data_markup[i].byte_bias +\n                                    subghz_protocol_bin_raw_get_full_byte(\n                                        instance->data_markup[i].bit_count);\n                            y++) {\n                            bin_raw_debug(\"%02X \", instance->data[y]);\n                        }\n#endif\n                        i++;\n                    }\n                    bin_raw_debug(\"\\r\\n\");\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n            }\n        }\n        break;\n\n    default:\n        //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state\n        if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {\n            instance->decoder.parser_step = BinRAWDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n    return subghz_protocol_blocks_add_bytes(\n        instance->data + instance->data_markup[0].byte_bias,\n        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count));\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    do {\n        stream_clean(flipper_format_get_raw_stream(flipper_format));\n        if(!flipper_format_write_header_cstr(\n               flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) {\n            FURI_LOG_E(TAG, \"Unable to add header\");\n            res = SubGhzProtocolStatusErrorParserHeader;\n            break;\n        }\n\n        if(!flipper_format_write_uint32(flipper_format, \"Frequency\", &preset->frequency, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Frequency\");\n            res = SubGhzProtocolStatusErrorParserFrequency;\n            break;\n        }\n\n        subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);\n        if(!flipper_format_write_string_cstr(\n               flipper_format, \"Preset\", furi_string_get_cstr(temp_str))) {\n            FURI_LOG_E(TAG, \"Unable to add Preset\");\n            res = SubGhzProtocolStatusErrorParserPreset;\n            break;\n        }\n        if(!strcmp(furi_string_get_cstr(temp_str), \"FuriHalSubGhzPresetCustom\")) {\n            if(!flipper_format_write_string_cstr(\n                   flipper_format, \"Custom_preset_module\", \"CC1101\")) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_module\");\n                res = SubGhzProtocolStatusErrorParserCustomPreset;\n                break;\n            }\n            if(!flipper_format_write_hex(\n                   flipper_format, \"Custom_preset_data\", preset->data, preset->data_size)) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_data\");\n                res = SubGhzProtocolStatusErrorParserCustomPreset;\n                break;\n            }\n        }\n        if(!flipper_format_write_string_cstr(\n               flipper_format, \"Protocol\", instance->generic.protocol_name)) {\n            FURI_LOG_E(TAG, \"Unable to add Protocol\");\n            res = SubGhzProtocolStatusErrorParserProtocolName;\n            break;\n        }\n\n        uint32_t temp = instance->generic.data_count_bit;\n        if(!flipper_format_write_uint32(flipper_format, \"Bit\", &temp, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Bit\");\n            res = SubGhzProtocolStatusErrorParserBitCount;\n            break;\n        }\n\n        if(!flipper_format_write_uint32(flipper_format, \"TE\", &instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add TE\");\n            res = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n\n        uint16_t i = 0;\n        while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {\n            temp = instance->data_markup[i].bit_count;\n            if(!flipper_format_write_uint32(flipper_format, \"Bit_RAW\", &temp, 1)) {\n                FURI_LOG_E(TAG, \"Bit_RAW\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            if(!flipper_format_write_hex(\n                   flipper_format,\n                   \"Data_RAW\",\n                   instance->data + instance->data_markup[i].byte_bias,\n                   subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) {\n                FURI_LOG_E(TAG, \"Unable to add Data_RAW\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            i++;\n        }\n\n        res = SubGhzProtocolStatusOk;\n    } while(false);\n    furi_string_free(temp_str);\n    return res;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    uint32_t temp_data = 0;\n\n    do {\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"Bit\", (uint32_t*)&temp_data, 1)) {\n            FURI_LOG_E(TAG, \"Missing Bit\");\n            res = SubGhzProtocolStatusErrorParserBitCount;\n            break;\n        }\n\n        instance->generic.data_count_bit = (uint16_t)temp_data;\n\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            res = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n\n        temp_data = 0;\n        uint16_t ind = 0;\n        uint16_t byte_bias = 0;\n        uint16_t byte_count = 0;\n        memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));\n        while(flipper_format_read_uint32(flipper_format, \"Bit_RAW\", (uint32_t*)&temp_data, 1)) {\n            if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {\n                FURI_LOG_E(TAG, \"Markup overflow\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);\n            if(byte_count > BIN_RAW_BUF_DATA_SIZE) {\n                FURI_LOG_E(TAG, \"Receive buffer overflow\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n\n            instance->data_markup[ind].bit_count = temp_data;\n            instance->data_markup[ind].byte_bias = byte_bias;\n            byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);\n\n            if(!flipper_format_read_hex(\n                   flipper_format,\n                   \"Data_RAW\",\n                   instance->data + instance->data_markup[ind].byte_bias,\n                   subghz_protocol_bin_raw_get_full_byte(temp_data))) {\n                instance->data_markup[ind].bit_count = 0;\n                FURI_LOG_E(TAG, \"Missing Data_RAW\");\n                res = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            ind++;\n        }\n\n        res = SubGhzProtocolStatusOk;\n    } while(0);\n\n    return res;\n}\n\nvoid subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderBinRAW* instance = context;\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit);\n\n    uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit);\n    for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) {\n        furi_string_cat_printf(output, \"%02X\", instance->data[i]);\n    }\n\n    furi_string_cat_printf(output, \"\\r\\nTe:%luus\\r\\n\", instance->te);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/bin_raw.h",
    "content": "#pragma once\n\n#include \"base.h\"\n#include \"public_api.h\"\n\n#define SUBGHZ_PROTOCOL_BIN_RAW_NAME \"BinRAW\"\n\ntypedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;\ntypedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW;\n\nextern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder;\nextern const SubGhzProtocol subghz_protocol_bin_raw;\n\n/**\n * Allocate SubGhzProtocolEncoderBinRAW.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance\n */\nvoid* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderBinRAW.\n * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance\n */\nvoid subghz_protocol_encoder_bin_raw_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance\n */\nvoid subghz_protocol_encoder_bin_raw_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_bin_raw_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderBinRAW.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance\n */\nvoid* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderBinRAW.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n */\nvoid subghz_protocol_decoder_bin_raw_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderBinRAW.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n */\nvoid subghz_protocol_decoder_bin_raw_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderBinRAW.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderBinRAW.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/came.c",
    "content": "#include \"came.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://phreakerclub.com/447\n *\n */\n\n#define TAG \"SubGhzProtocolCame\"\n\n#define CAME_12_COUNT_BIT    12\n#define CAME_24_COUNT_BIT    24\n#define PRASTEL_25_COUNT_BIT 25\n#define PRASTEL_42_COUNT_BIT 42\n#define PRASTEL_NAME         \"Prastel\"\n#define AIRFORCE_COUNT_BIT   18\n#define AIRFORCE_NAME        \"Airforce\"\n\nstatic const SubGhzBlockConst subghz_protocol_came_const = {\n    .te_short = 320,\n    .te_long = 640,\n    .te_delta = 150,\n    .min_count_bit_for_found = 12,\n};\n\nstruct SubGhzProtocolDecoderCame {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderCame {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    CameDecoderStepReset = 0,\n    CameDecoderStepFoundStartBit,\n    CameDecoderStepSaveDuration,\n    CameDecoderStepCheckDuration,\n} CameDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_came_decoder = {\n    .alloc = subghz_protocol_decoder_came_alloc,\n    .free = subghz_protocol_decoder_came_free,\n\n    .feed = subghz_protocol_decoder_came_feed,\n    .reset = subghz_protocol_decoder_came_reset,\n\n    .get_hash_data = subghz_protocol_decoder_came_get_hash_data,\n    .serialize = subghz_protocol_decoder_came_serialize,\n    .deserialize = subghz_protocol_decoder_came_deserialize,\n    .get_string = subghz_protocol_decoder_came_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_came_encoder = {\n    .alloc = subghz_protocol_encoder_came_alloc,\n    .free = subghz_protocol_encoder_came_free,\n\n    .deserialize = subghz_protocol_encoder_came_deserialize,\n    .stop = subghz_protocol_encoder_came_stop,\n    .yield = subghz_protocol_encoder_came_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_came = {\n    .name = SUBGHZ_PROTOCOL_CAME_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_came_decoder,\n    .encoder = &subghz_protocol_came_encoder,\n};\n\nvoid* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderCame* instance = malloc(sizeof(SubGhzProtocolEncoderCame));\n\n    instance->base.protocol = &subghz_protocol_came;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_came_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderCame* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderCame instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) {\n    furi_assert(instance);\n    uint32_t header_te = 0;\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n\n    switch(instance->generic.data_count_bit) {\n    case CAME_24_COUNT_BIT:\n    case PRASTEL_42_COUNT_BIT:\n        // CAME 24 Bit = 24320 us\n        header_te = 76;\n        break;\n    case CAME_12_COUNT_BIT:\n    case AIRFORCE_COUNT_BIT:\n        // CAME 12 Bit Original only! and Airforce protocol = 15040 us\n        header_te = 47;\n        break;\n    case PRASTEL_25_COUNT_BIT:\n        // PRASTEL = 11520 us\n        header_te = 36;\n        break;\n    default:\n        // Some wrong detected protocols, 5120 us\n        header_te = 16;\n        break;\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short * header_te);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderCame* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_came_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_came_stop(void* context) {\n    SubGhzProtocolEncoderCame* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_came_yield(void* context) {\n    SubGhzProtocolEncoderCame* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderCame* instance = malloc(sizeof(SubGhzProtocolDecoderCame));\n    instance->base.protocol = &subghz_protocol_came;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_came_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_came_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    instance->decoder.parser_step = CameDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    switch(instance->decoder.parser_step) {\n    case CameDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) <\n                        subghz_protocol_came_const.te_delta * 63)) {\n            // 17920 us + 7050 us = 24970 us max possible value old one\n            // delta = 150 us x 63 = 9450 us + 17920 us = 27370 us max possible value\n            //Found header CAME\n            // 26700 us or 24000 us max possible values\n            instance->decoder.parser_step = CameDecoderStepFoundStartBit;\n        }\n        break;\n    case CameDecoderStepFoundStartBit:\n        if(!level) {\n            break;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_came_const.te_short) <\n            subghz_protocol_came_const.te_delta) {\n            //Found start bit CAME\n            instance->decoder.parser_step = CameDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = CameDecoderStepReset;\n        }\n        break;\n    case CameDecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_came_const.te_short * 4)) {\n                instance->decoder.parser_step = CameDecoderStepFoundStartBit;\n                if((instance->decoder.decode_count_bit ==\n                    subghz_protocol_came_const.min_count_bit_for_found) ||\n                   (instance->decoder.decode_count_bit == AIRFORCE_COUNT_BIT) ||\n                   (instance->decoder.decode_count_bit == PRASTEL_25_COUNT_BIT) ||\n                   (instance->decoder.decode_count_bit == PRASTEL_42_COUNT_BIT) ||\n                   (instance->decoder.decode_count_bit == CAME_24_COUNT_BIT)) {\n                    instance->generic.serial = 0x0;\n                    instance->generic.btn = 0x0;\n\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = CameDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = CameDecoderStepReset;\n        }\n        break;\n    case CameDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_short) <\n                subghz_protocol_came_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_came_const.te_long) <\n                subghz_protocol_came_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = CameDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_long) <\n                 subghz_protocol_came_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_came_const.te_short) <\n                 subghz_protocol_came_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = CameDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = CameDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = CameDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_came_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_came_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_came_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCame* instance = context;\n\n    uint32_t code_found_lo = instance->generic.data & 0x000003ffffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x000003ffffffffff;\n\n    const char* name = instance->generic.protocol_name;\n    switch(instance->generic.data_count_bit) {\n    case PRASTEL_25_COUNT_BIT:\n    case PRASTEL_42_COUNT_BIT:\n        name = PRASTEL_NAME;\n        break;\n    case AIRFORCE_COUNT_BIT:\n        name = AIRFORCE_NAME;\n        break;\n    }\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%08lX\\r\\n\"\n        \"Yek:0x%08lX\\r\\n\",\n        name,\n        instance->generic.data_count_bit,\n        code_found_lo,\n        code_found_reverse_lo);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/came.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_CAME_NAME \"CAME\"\n\ntypedef struct SubGhzProtocolDecoderCame SubGhzProtocolDecoderCame;\ntypedef struct SubGhzProtocolEncoderCame SubGhzProtocolEncoderCame;\n\nextern const SubGhzProtocolDecoder subghz_protocol_came_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_came_encoder;\nextern const SubGhzProtocol subghz_protocol_came;\n\n/**\n * Allocate SubGhzProtocolEncoderCame.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderCame* pointer to a SubGhzProtocolEncoderCame instance\n */\nvoid* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderCame.\n * @param context Pointer to a SubGhzProtocolEncoderCame instance\n */\nvoid subghz_protocol_encoder_came_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderCame instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderCame instance\n */\nvoid subghz_protocol_encoder_came_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderCame instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_came_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderCame.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderCame* pointer to a SubGhzProtocolDecoderCame instance\n */\nvoid* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderCame.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n */\nvoid subghz_protocol_decoder_came_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderCame.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n */\nvoid subghz_protocol_decoder_came_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_came_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderCame.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_came_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderCame.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderCame instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_came_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/came_atomo.c",
    "content": "#include \"came_atomo.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocoCameAtomo\"\n\n#define SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE 0xFFFFFFFFFFFFFFFF\n\nstatic const SubGhzBlockConst subghz_protocol_came_atomo_const = {\n    .te_short = 600,\n    .te_long = 1200,\n    .te_delta = 250,\n    .min_count_bit_for_found = 62,\n};\n\nstruct SubGhzProtocolDecoderCameAtomo {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    ManchesterState manchester_saved_state;\n    const char* came_atomo_rainbow_table_file_name;\n};\n\nstruct SubGhzProtocolEncoderCameAtomo {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    CameAtomoDecoderStepReset = 0,\n    CameAtomoDecoderStepDecoderData,\n} CameAtomoDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder = {\n    .alloc = subghz_protocol_decoder_came_atomo_alloc,\n    .free = subghz_protocol_decoder_came_atomo_free,\n\n    .feed = subghz_protocol_decoder_came_atomo_feed,\n    .reset = subghz_protocol_decoder_came_atomo_reset,\n\n    .get_hash_data = subghz_protocol_decoder_came_atomo_get_hash_data,\n    .serialize = subghz_protocol_decoder_came_atomo_serialize,\n    .deserialize = subghz_protocol_decoder_came_atomo_deserialize,\n    .get_string = subghz_protocol_decoder_came_atomo_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_came_atomo = {\n    .name = SUBGHZ_PROTOCOL_CAME_ATOMO_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_came_atomo_decoder,\n    .encoder = &subghz_protocol_came_atomo_encoder,\n};\n\nvoid* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolDecoderCameAtomo));\n    instance->base.protocol = &subghz_protocol_came_atomo;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->came_atomo_rainbow_table_file_name =\n        subghz_environment_get_came_atomo_rainbow_table_file_name(environment);\n    if(instance->came_atomo_rainbow_table_file_name) {\n        FURI_LOG_I(\n            TAG, \"Loading rainbow table from %s\", instance->came_atomo_rainbow_table_file_name);\n    }\n    return instance;\n}\n\nvoid subghz_protocol_decoder_came_atomo_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    instance->came_atomo_rainbow_table_file_name = NULL;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_came_atomo_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    instance->decoder.parser_step = CameAtomoDecoderStepReset;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\nvoid subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n\n    ManchesterEvent event = ManchesterEventReset;\n    switch(instance->decoder.parser_step) {\n    case CameAtomoDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) <\n                        subghz_protocol_came_atomo_const.te_delta * 40)) {\n            //Found header CAME\n            instance->decoder.parser_step = CameAtomoDecoderStepDecoderData;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 1;\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventReset,\n                &instance->manchester_saved_state,\n                NULL);\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventShortLow,\n                &instance->manchester_saved_state,\n                NULL);\n        }\n        break;\n    case CameAtomoDecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) <\n               subghz_protocol_came_atomo_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) <\n                subghz_protocol_came_atomo_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= ((uint32_t)subghz_protocol_came_atomo_const.te_long * 2 +\n                             subghz_protocol_came_atomo_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_came_atomo_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 1;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventShortLow,\n                    &instance->manchester_saved_state,\n                    NULL);\n            } else {\n                instance->decoder.parser_step = CameAtomoDecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) <\n               subghz_protocol_came_atomo_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) <\n                subghz_protocol_came_atomo_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = CameAtomoDecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\n/** \n * Read bytes from rainbow table\n * @param file_name Full path to rainbow table the file \n * @param number_atomo_magic_xor number in the array\n * @return atomo_magic_xor\n */\nstatic uint64_t subghz_protocol_came_atomo_get_magic_xor_in_file(\n    const char* file_name,\n    uint8_t number_atomo_magic_xor) {\n    if(!strcmp(file_name, \"\")) return SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE;\n\n    uint8_t buffer[sizeof(uint64_t)] = {0};\n    uint32_t address = number_atomo_magic_xor * sizeof(uint64_t);\n    uint64_t atomo_magic_xor = 0;\n\n    if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint64_t))) {\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            atomo_magic_xor = (atomo_magic_xor << 8) | buffer[i];\n        }\n    } else {\n        atomo_magic_xor = SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE;\n    }\n    return atomo_magic_xor;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param file_name Full path to rainbow table the file\n */\nstatic void subghz_protocol_came_atomo_remote_controller(\n    SubGhzBlockGeneric* instance,\n    const char* file_name) {\n    /* \n    * 0x1fafef3ed0f7d9ef\n    * 0x185fcc1531ee86e7\n    * 0x184fa96912c567ff\n    * 0x187f8a42f3dc38f7\n    * 0x186f63915492a5cd\n    * 0x181f40bab58bfac5\n    * 0x180f25c696a01bdd\n    * 0x183f06ed77b944d5\n    * 0x182ef661d83d21a9\n    * 0x18ded54a39247ea1\n    * 0x18ceb0361a0f9fb9\n    * 0x18fe931dfb16c0b1\n    * 0x18ee7ace5c585d8b\n    * ........ \n    * transmission consists of 99 parcels with increasing counter while holding down the button\n    * with each new press, the counter in the encrypted part increases\n    * \n    * 0x1FAFF13ED0F7D9EF\n    * 0x1FAFF11ED0F7D9EF\n    * 0x1FAFF10ED0F7D9EF\n    * 0x1FAFF0FED0F7D9EF\n    * 0x1FAFF0EED0F7D9EF\n    * 0x1FAFF0DED0F7D9EF\n    * 0x1FAFF0CED0F7D9EF\n    * 0x1FAFF0BED0F7D9EF\n    * 0x1FAFF0AED0F7D9EF \n    * \n    *                   where     0x1FAF - parcel counter, 0хF0A - button press counter,\n    *                           0xED0F7D9E - serial number, 0хF -  key\n    * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F =  0x07F0\n    * 0x185f ^ 0x185F = 0x0000\n    * 0x184f ^ 0x185F = 0x0010\n    * 0x187f ^ 0x185F = 0x0020\n    * .....\n    * 0x182e ^ 0x185F = 0x0071 \n    * 0x18de ^ 0x185F = 0x0081\n    * .....\n    * 0x1e43 ^ 0x185F = 0x061C\n    *                           where the last nibble is incremented every 8 samples\n    * \n    * Decode\n    * \n    * 0x1cf6931dfb16c0b1 => 0x1cf6\n    * 0x1cf6 ^ 0x185F = 0x04A9\n    * 0x04A9 => 0x04A = 74 (dec)\n    * 74+1 % 32(atomo_magic_xor) = 11\n    * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX\n    * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX =  0xEF3ED0F7D9EF\n    * 0xEF3 ED0F7D9E F  => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key\n    * \n    * */\n\n    uint16_t parcel_counter = instance->data >> 48;\n    parcel_counter = parcel_counter ^ 0x185F;\n    parcel_counter >>= 4;\n    uint8_t ind = (parcel_counter + 1) % 32;\n    uint64_t temp_data = instance->data & 0x0000FFFFFFFFFFFF;\n    uint64_t atomo_magic_xor = subghz_protocol_came_atomo_get_magic_xor_in_file(file_name, ind);\n\n    if(atomo_magic_xor != SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE) {\n        temp_data = temp_data ^ atomo_magic_xor;\n        instance->cnt = temp_data >> 36;\n        instance->serial = (temp_data >> 4) & 0x000FFFFFFFF;\n        instance->btn = temp_data & 0xF;\n    } else {\n        instance->cnt = 0;\n        instance->serial = 0;\n        instance->btn = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_came_atomo_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_came_atomo_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameAtomo* instance = context;\n    subghz_protocol_came_atomo_remote_controller(\n        &instance->generic, instance->came_atomo_rainbow_table_file_name);\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%08lX  Btn:0x%01X\\r\\n\"\n        \"Cnt:0x%03lX\\r\\n\",\n\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/came_atomo.h",
    "content": "#pragma once\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_CAME_ATOMO_NAME \"CAME Atomo\"\n\ntypedef struct SubGhzProtocolDecoderCameAtomo SubGhzProtocolDecoderCameAtomo;\ntypedef struct SubGhzProtocolEncoderCameAtomo SubGhzProtocolEncoderCameAtomo;\n\nextern const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder;\nextern const SubGhzProtocol subghz_protocol_came_atomo;\n\n/**\n * Allocate SubGhzProtocolDecoderCameAtomo.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderCameAtomo* pointer to a SubGhzProtocolDecoderCameAtomo instance\n */\nvoid* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderCameAtomo.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n */\nvoid subghz_protocol_decoder_came_atomo_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderCameAtomo.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n */\nvoid subghz_protocol_decoder_came_atomo_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderCameAtomo.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_came_atomo_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderCameAtomo.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/came_twee.c",
    "content": "#include \"came_twee.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include <lib/toolbox/manchester_encoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin\n *\n */\n\n#define TAG \"SubGhzProtocolCameTwee\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c%c%c\"\n#define CNT_TO_DIP(dip)                                                                     \\\n    (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'),     \\\n        (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \\\n        (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \\\n        (dip & 0x0001 ? '1' : '0')\n\n/** \n * Rainbow table Came Twee.\n */\nstatic const uint32_t came_twee_magic_numbers_xor[15] = {\n    0x0E0E0E00,\n    0x1D1D1D11,\n    0x2C2C2C22,\n    0x3B3B3B33,\n    0x4A4A4A44,\n    0x59595955,\n    0x68686866,\n    0x77777777,\n    0x86868688,\n    0x95959599,\n    0xA4A4A4AA,\n    0xB3B3B3BB,\n    0xC2C2C2CC,\n    0xD1D1D1DD,\n    0xE0E0E0EE,\n};\n\nstatic const SubGhzBlockConst subghz_protocol_came_twee_const = {\n    .te_short = 500,\n    .te_long = 1000,\n    .te_delta = 250,\n    .min_count_bit_for_found = 54,\n};\n\nstruct SubGhzProtocolDecoderCameTwee {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    ManchesterState manchester_saved_state;\n};\n\nstruct SubGhzProtocolEncoderCameTwee {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    CameTweeDecoderStepReset = 0,\n    CameTweeDecoderStepDecoderData,\n} CameTweeDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = {\n    .alloc = subghz_protocol_decoder_came_twee_alloc,\n    .free = subghz_protocol_decoder_came_twee_free,\n\n    .feed = subghz_protocol_decoder_came_twee_feed,\n    .reset = subghz_protocol_decoder_came_twee_reset,\n\n    .get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data,\n    .serialize = subghz_protocol_decoder_came_twee_serialize,\n    .deserialize = subghz_protocol_decoder_came_twee_deserialize,\n    .get_string = subghz_protocol_decoder_came_twee_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = {\n    .alloc = subghz_protocol_encoder_came_twee_alloc,\n    .free = subghz_protocol_encoder_came_twee_free,\n\n    .deserialize = subghz_protocol_encoder_came_twee_deserialize,\n    .stop = subghz_protocol_encoder_came_twee_stop,\n    .yield = subghz_protocol_encoder_came_twee_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_came_twee = {\n    .name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_came_twee_decoder,\n    .encoder = &subghz_protocol_came_twee_encoder,\n};\n\nvoid* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee));\n\n    instance->base.protocol = &subghz_protocol_came_twee;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!!\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_came_twee_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderCameTwee* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic LevelDuration\n    subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) {\n    LevelDuration data = {.duration = 0, .level = 0};\n    switch(result) {\n    case ManchesterEncoderResultShortLow:\n        data.duration = subghz_protocol_came_twee_const.te_short;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongLow:\n        data.duration = subghz_protocol_came_twee_const.te_long;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongHigh:\n        data.duration = subghz_protocol_came_twee_const.te_long;\n        data.level = true;\n        break;\n    case ManchesterEncoderResultShortHigh:\n        data.duration = subghz_protocol_came_twee_const.te_short;\n        data.level = true;\n        break;\n\n    default:\n        furi_crash(\"SubGhz: ManchesterEncoderResult is incorrect.\");\n        break;\n    }\n    return level_duration_make(data.level, data.duration);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance\n */\nstatic void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    ManchesterEncoderState enc_state;\n    manchester_encoder_reset(&enc_state);\n    ManchesterEncoderResult result;\n\n    uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask\n\n    for(int i = 14; i >= 0; i--) {\n        temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) |\n                      (instance->generic.serial ^ came_twee_magic_numbers_xor[i]);\n\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) {\n                instance->encoder.upload[index++] =\n                    subghz_protocol_encoder_came_twee_add_duration_to_upload(result);\n                manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result);\n            }\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_came_twee_add_duration_to_upload(result);\n        }\n        instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload(\n            manchester_encoder_finish(&enc_state));\n        if(level_duration_get_level(instance->encoder.upload[index])) {\n            index++;\n        }\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51);\n    }\n    instance->encoder.size_upload = index;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) {\n    /*      Came Twee 54 bit, rolling code 15 parcels with\n    *       a decreasing counter from 0xE to 0x0\n    *       with originally coded dip switches on the console 10 bit code\n    * \n    *  0x003FFF72E04A6FEE\n    *  0x003FFF72D17B5EDD\n    *  0x003FFF72C2684DCC\n    *  0x003FFF72B3193CBB\n    *  0x003FFF72A40E2BAA\n    *  0x003FFF72953F1A99\n    *  0x003FFF72862C0988\n    *  0x003FFF7277DDF877\n    *  0x003FFF7268C2E766\n    *  0x003FFF7259F3D655\n    *  0x003FFF724AE0C544\n    *  0x003FFF723B91B433\n    *  0x003FFF722C86A322\n    *  0x003FFF721DB79211\n    *  0x003FFF720EA48100\n    * \n    *   decryption\n    * the last 32 bits, do XOR by the desired number, divide the result by 4,\n    * convert the first 16 bits of the resulting 32-bit number to bin and do\n    * bit-by-bit mirroring, adding up to 10 bits\n    * \n    * Example\n    * Step 1. 0x003FFF721DB79211        => 0x1DB79211\n    * Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00\n    * Step 4. 0x00AA8F00 / 4            => 0x002AA3C0\n    * Step 5. 0x002AA3C0                => 0x002A\n    * Step 6. 0x002A    bin             => b101010\n    * Step 7. b101010                   => b0101010000\n    * Step 8. b0101010000               => (Dip) Off ON Off ON Off ON Off Off Off Off\n    */\n\n    uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF);\n    uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF);\n\n    data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);\n    instance->serial = data;\n    data /= 4;\n    instance->btn = (data >> 4) & 0x0F;\n    data >>= 16;\n    data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16);\n    instance->cnt = data >> 6;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderCameTwee* instance = context;\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    do {\n        res = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_came_twee_const.min_count_bit_for_found);\n        if(res != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_came_twee_remote_controller(&instance->generic);\n        subghz_protocol_encoder_came_twee_get_upload(instance);\n        instance->encoder.front = 0; // reset position before start\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return res;\n}\n\nvoid subghz_protocol_encoder_came_twee_stop(void* context) {\n    SubGhzProtocolEncoderCameTwee* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_came_twee_yield(void* context) {\n    SubGhzProtocolEncoderCameTwee* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee));\n    instance->base.protocol = &subghz_protocol_came_twee;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_came_twee_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_came_twee_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    instance->decoder.parser_step = CameTweeDecoderStepReset;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\nvoid subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    ManchesterEvent event = ManchesterEventReset;\n    switch(instance->decoder.parser_step) {\n    case CameTweeDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) <\n                        subghz_protocol_came_twee_const.te_delta * 20)) {\n            //Found header CAME\n            instance->decoder.parser_step = CameTweeDecoderStepDecoderData;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventLongLow,\n                &instance->manchester_saved_state,\n                NULL);\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventLongHigh,\n                &instance->manchester_saved_state,\n                NULL);\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventShortLow,\n                &instance->manchester_saved_state,\n                NULL);\n        }\n        break;\n    case CameTweeDecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <\n               subghz_protocol_came_twee_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <\n                subghz_protocol_came_twee_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 +\n                             subghz_protocol_came_twee_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_came_twee_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongLow,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventShortLow,\n                    &instance->manchester_saved_state,\n                    NULL);\n            } else {\n                instance->decoder.parser_step = CameTweeDecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <\n               subghz_protocol_came_twee_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <\n                subghz_protocol_came_twee_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = CameTweeDecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_came_twee_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderCameTwee* instance = context;\n    subghz_protocol_came_twee_remote_controller(&instance->generic);\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Btn:%X\\r\\n\"\n        \"DIP:\" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        instance->generic.btn,\n        CNT_TO_DIP(instance->generic.cnt));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/came_twee.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_CAME_TWEE_NAME \"CAME TWEE\"\n\ntypedef struct SubGhzProtocolDecoderCameTwee SubGhzProtocolDecoderCameTwee;\ntypedef struct SubGhzProtocolEncoderCameTwee SubGhzProtocolEncoderCameTwee;\n\nextern const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder;\nextern const SubGhzProtocol subghz_protocol_came_twee;\n\n/**\n * Allocate SubGhzProtocolEncoderCameTwee.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderCameTwee* pointer to a SubGhzProtocolEncoderCameTwee instance\n */\nvoid* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderCameTwee.\n * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance\n */\nvoid subghz_protocol_encoder_came_twee_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance\n */\nvoid subghz_protocol_encoder_came_twee_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_came_twee_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderCameTwee.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderCameTwee* pointer to a SubGhzProtocolDecoderCameTwee instance\n */\nvoid* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderCameTwee.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n */\nvoid subghz_protocol_decoder_came_twee_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderCameTwee.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n */\nvoid subghz_protocol_decoder_came_twee_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderCameTwee.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderCameTwee.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/chamberlain_code.c",
    "content": "#include \"chamberlain_code.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolChambCode\"\n\n#define CHAMBERLAIN_CODE_BIT_STOP 0b0001\n#define CHAMBERLAIN_CODE_BIT_1    0b0011\n#define CHAMBERLAIN_CODE_BIT_0    0b0111\n\n#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F\n#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F\n#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F\n\n#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101\n#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001\n#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001\n\n#define CHAMBERLAIN_7_CODE_DIP_PATTERN \"%c%c%c%c%c%c%c\"\n#define CHAMBERLAIN_7_CODE_DATA_TO_DIP(dip)                                                 \\\n    (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'),     \\\n        (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \\\n        (dip & 0x0001 ? '1' : '0')\n\n#define CHAMBERLAIN_8_CODE_DIP_PATTERN \"%c%c%c%c%cx%c%c\"\n#define CHAMBERLAIN_8_CODE_DATA_TO_DIP(dip)                                                 \\\n    (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'),     \\\n        (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \\\n        (dip & 0x0002 ? '1' : '0')\n\n#define CHAMBERLAIN_9_CODE_DIP_PATTERN \"%c%c%c%c%c%c%c%c%c\"\n#define CHAMBERLAIN_9_CODE_DATA_TO_DIP(dip)                                                 \\\n    (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'),     \\\n        (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), \\\n        (dip & 0x0001 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), (dip & 0x0004 ? '1' : '0')\n\nstatic const SubGhzBlockConst subghz_protocol_chamb_code_const = {\n    .te_short = 1000,\n    .te_long = 3000,\n    .te_delta = 200,\n    .min_count_bit_for_found = 10,\n};\n\nstruct SubGhzProtocolDecoderChamb_Code {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderChamb_Code {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Chamb_CodeDecoderStepReset = 0,\n    Chamb_CodeDecoderStepFoundStartBit,\n    Chamb_CodeDecoderStepSaveDuration,\n    Chamb_CodeDecoderStepCheckDuration,\n} Chamb_CodeDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder = {\n    .alloc = subghz_protocol_decoder_chamb_code_alloc,\n    .free = subghz_protocol_decoder_chamb_code_free,\n\n    .feed = subghz_protocol_decoder_chamb_code_feed,\n    .reset = subghz_protocol_decoder_chamb_code_reset,\n\n    .get_hash_data = subghz_protocol_decoder_chamb_code_get_hash_data,\n    .serialize = subghz_protocol_decoder_chamb_code_serialize,\n    .deserialize = subghz_protocol_decoder_chamb_code_deserialize,\n    .get_string = subghz_protocol_decoder_chamb_code_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder = {\n    .alloc = subghz_protocol_encoder_chamb_code_alloc,\n    .free = subghz_protocol_encoder_chamb_code_free,\n\n    .deserialize = subghz_protocol_encoder_chamb_code_deserialize,\n    .stop = subghz_protocol_encoder_chamb_code_stop,\n    .yield = subghz_protocol_encoder_chamb_code_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_chamb_code = {\n    .name = SUBGHZ_PROTOCOL_CHAMB_CODE_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_chamb_code_decoder,\n    .encoder = &subghz_protocol_chamb_code_encoder,\n};\n\nvoid* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolEncoderChamb_Code));\n\n    instance->base.protocol = &subghz_protocol_chamb_code;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 24;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_chamb_code_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderChamb_Code* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic uint64_t subghz_protocol_chamb_bit_to_code(uint64_t data, uint8_t size) {\n    uint64_t data_res = 0;\n    for(uint8_t i = 0; i < size; i++) {\n        if(!(bit_read(data, size - i - 1))) {\n            data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_0;\n        } else {\n            data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_1;\n        }\n    }\n    return data_res;\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderChamb_Code instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_chamb_code_get_upload(SubGhzProtocolEncoderChamb_Code* instance) {\n    furi_assert(instance);\n\n    uint64_t data = subghz_protocol_chamb_bit_to_code(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    switch(instance->generic.data_count_bit) {\n    case 7:\n        data = ((data >> 4) << 16) | (data & 0xF) << 4 | CHAMBERLAIN_7_CODE_MASK_CHECK;\n        break;\n    case 8:\n        data = ((data >> 12) << 16) | (data & 0xFF) << 4 | CHAMBERLAIN_8_CODE_MASK_CHECK;\n        break;\n    case 9:\n        data = (data << 4) | CHAMBERLAIN_9_CODE_MASK_CHECK;\n        break;\n\n    default:\n        FURI_LOG_E(TAG, \"Invalid bits count\");\n        return false;\n        break;\n    }\n#define UPLOAD_HEX_DATA_SIZE 10\n    uint8_t upload_hex_data[UPLOAD_HEX_DATA_SIZE] = {0};\n    size_t upload_hex_count_bit = 0;\n\n    //insert guard time\n    for(uint8_t i = 0; i < 36; i++) {\n        subghz_protocol_blocks_set_bit_array(\n            0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);\n    }\n\n    //insert data\n    switch(instance->generic.data_count_bit) {\n    case 7:\n    case 9:\n        for(uint8_t i = 44; i > 0; i--) {\n            if(!bit_read(data, i - 1)) {\n                subghz_protocol_blocks_set_bit_array(\n                    0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);\n            } else {\n                subghz_protocol_blocks_set_bit_array(\n                    1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);\n            }\n        }\n        break;\n    case 8:\n        for(uint8_t i = 40; i > 0; i--) {\n            if(!bit_read(data, i - 1)) {\n                subghz_protocol_blocks_set_bit_array(\n                    0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);\n            } else {\n                subghz_protocol_blocks_set_bit_array(\n                    1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);\n            }\n        }\n        break;\n    }\n\n    instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(\n        upload_hex_data,\n        upload_hex_count_bit,\n        instance->encoder.upload,\n        instance->encoder.size_upload,\n        subghz_protocol_chamb_code_const.te_short,\n        SubGhzProtocolBlockAlignBitLeft);\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderChamb_Code* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(instance->generic.data_count_bit >\n           subghz_protocol_chamb_code_const.min_count_bit_for_found) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_chamb_code_stop(void* context) {\n    SubGhzProtocolEncoderChamb_Code* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_chamb_code_yield(void* context) {\n    SubGhzProtocolEncoderChamb_Code* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolDecoderChamb_Code));\n    instance->base.protocol = &subghz_protocol_chamb_code;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_chamb_code_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_chamb_code_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n}\n\nstatic bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) {\n    uint64_t data_tmp = data[0];\n    uint64_t data_res = 0;\n    for(uint8_t i = 0; i < size; i++) {\n        if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) {\n            bit_write(data_res, i, 0);\n        } else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) {\n            bit_write(data_res, i, 1);\n        } else {\n            return false;\n        }\n        data_tmp >>= 4;\n    }\n    data[0] = data_res;\n    return true;\n}\n\nstatic bool subghz_protocol_decoder_chamb_code_check_mask_and_parse(\n    SubGhzProtocolDecoderChamb_Code* instance) {\n    furi_assert(instance);\n    if(instance->decoder.decode_count_bit >\n       subghz_protocol_chamb_code_const.min_count_bit_for_found + 1)\n        return false;\n\n    if((instance->decoder.decode_data & CHAMBERLAIN_7_CODE_MASK) ==\n       CHAMBERLAIN_7_CODE_MASK_CHECK) {\n        instance->decoder.decode_count_bit = 7;\n        instance->decoder.decode_data &= ~CHAMBERLAIN_7_CODE_MASK;\n        instance->decoder.decode_data = (instance->decoder.decode_data >> 12) |\n                                        ((instance->decoder.decode_data >> 4) & 0xF);\n    } else if(\n        (instance->decoder.decode_data & CHAMBERLAIN_8_CODE_MASK) ==\n        CHAMBERLAIN_8_CODE_MASK_CHECK) {\n        instance->decoder.decode_count_bit = 8;\n        instance->decoder.decode_data &= ~CHAMBERLAIN_8_CODE_MASK;\n        instance->decoder.decode_data = instance->decoder.decode_data >> 4 |\n                                        CHAMBERLAIN_CODE_BIT_0 << 8; //DIP 6 no use\n    } else if(\n        (instance->decoder.decode_data & CHAMBERLAIN_9_CODE_MASK) ==\n        CHAMBERLAIN_9_CODE_MASK_CHECK) {\n        instance->decoder.decode_count_bit = 9;\n        instance->decoder.decode_data &= ~CHAMBERLAIN_9_CODE_MASK;\n        instance->decoder.decode_data >>= 4;\n    } else {\n        return false;\n    }\n    return subghz_protocol_chamb_code_to_bit(\n        &instance->decoder.decode_data, instance->decoder.decode_count_bit);\n}\n\nvoid subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    switch(instance->decoder.parser_step) {\n    case Chamb_CodeDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 39) <\n                        subghz_protocol_chamb_code_const.te_delta * 20)) {\n            //Found header Chamb_Code\n            instance->decoder.parser_step = Chamb_CodeDecoderStepFoundStartBit;\n        }\n        break;\n    case Chamb_CodeDecoderStepFoundStartBit:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) <\n                       subghz_protocol_chamb_code_const.te_delta)) {\n            //Found start bit Chamb_Code\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.decode_data = instance->decoder.decode_data << 4 |\n                                            CHAMBERLAIN_CODE_BIT_STOP;\n            instance->decoder.decode_count_bit++;\n            instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;\n        } else {\n            instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n        }\n        break;\n    case Chamb_CodeDecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration > subghz_protocol_chamb_code_const.te_short * 5) {\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_chamb_code_const.min_count_bit_for_found) {\n                    instance->generic.serial = 0x0;\n                    instance->generic.btn = 0x0;\n                    if(subghz_protocol_decoder_chamb_code_check_mask_and_parse(instance)) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = Chamb_CodeDecoderStepCheckDuration;\n            }\n        } else {\n            instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n        }\n        break;\n    case Chamb_CodeDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF( //Found stop bit Chamb_Code\n                    instance->decoder.te_last,\n                    subghz_protocol_chamb_code_const.te_short * 3) <\n                subghz_protocol_chamb_code_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) <\n                subghz_protocol_chamb_code_const.te_delta)) {\n                instance->decoder.decode_data = instance->decoder.decode_data << 4 |\n                                                CHAMBERLAIN_CODE_BIT_STOP;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short * 2) <\n                 subghz_protocol_chamb_code_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 2) <\n                 subghz_protocol_chamb_code_const.te_delta)) {\n                instance->decoder.decode_data = instance->decoder.decode_data << 4 |\n                                                CHAMBERLAIN_CODE_BIT_1;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short) <\n                 subghz_protocol_chamb_code_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 3) <\n                 subghz_protocol_chamb_code_const.te_delta)) {\n                instance->decoder.decode_data = instance->decoder.decode_data << 4 |\n                                                CHAMBERLAIN_CODE_BIT_0;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n            }\n\n        } else {\n            instance->decoder.parser_step = Chamb_CodeDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_chamb_code_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(instance->generic.data_count_bit >\n           subghz_protocol_chamb_code_const.min_count_bit_for_found) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderChamb_Code* instance = context;\n\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%03lX\\r\\n\"\n        \"Yek:0x%03lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_lo,\n        code_found_reverse_lo);\n\n    switch(instance->generic.data_count_bit) {\n    case 7:\n        furi_string_cat_printf(\n            output,\n            \"DIP:\" CHAMBERLAIN_7_CODE_DIP_PATTERN \"\\r\\n\",\n            CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo));\n        break;\n    case 8:\n        furi_string_cat_printf(\n            output,\n            \"DIP:\" CHAMBERLAIN_8_CODE_DIP_PATTERN \"\\r\\n\",\n            CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo));\n        break;\n    case 9:\n        furi_string_cat_printf(\n            output,\n            \"DIP:\" CHAMBERLAIN_9_CODE_DIP_PATTERN \"\\r\\n\",\n            CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo));\n        break;\n\n    default:\n        break;\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/chamberlain_code.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_CHAMB_CODE_NAME \"Cham_Code\"\n\ntypedef struct SubGhzProtocolDecoderChamb_Code SubGhzProtocolDecoderChamb_Code;\ntypedef struct SubGhzProtocolEncoderChamb_Code SubGhzProtocolEncoderChamb_Code;\n\nextern const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder;\nextern const SubGhzProtocol subghz_protocol_chamb_code;\n\n/**\n * Allocate SubGhzProtocolEncoderChamb_Code.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderChamb_Code* pointer to a SubGhzProtocolEncoderChamb_Code instance\n */\nvoid* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderChamb_Code.\n * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance\n */\nvoid subghz_protocol_encoder_chamb_code_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance\n */\nvoid subghz_protocol_encoder_chamb_code_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_chamb_code_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderChamb_Code.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderChamb_Code* pointer to a SubGhzProtocolDecoderChamb_Code instance\n */\nvoid* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderChamb_Code.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n */\nvoid subghz_protocol_decoder_chamb_code_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderChamb_Code.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n */\nvoid subghz_protocol_decoder_chamb_code_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderChamb_Code.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_chamb_code_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderChamb_Code.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/clemsa.c",
    "content": "#include \"clemsa.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n// protocol BERNER / ELKA / TEDSEN / TELETASTER\n#define TAG \"SubGhzProtocolClemsa\"\n\n#define DIP_P 0b11 //(+)\n#define DIP_O 0b10 //(0)\n#define DIP_N 0b00 //(-)\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c\"\n#define SHOW_DIP_P(dip, check_dip)                         \\\n    ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'),     \\\n        ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')\n\nstatic const SubGhzBlockConst subghz_protocol_clemsa_const = {\n    .te_short = 385,\n    .te_long = 2695,\n    .te_delta = 150,\n    .min_count_bit_for_found = 18,\n};\n\nstruct SubGhzProtocolDecoderClemsa {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderClemsa {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    ClemsaDecoderStepReset = 0,\n    ClemsaDecoderStepSaveDuration,\n    ClemsaDecoderStepCheckDuration,\n} ClemsaDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = {\n    .alloc = subghz_protocol_decoder_clemsa_alloc,\n    .free = subghz_protocol_decoder_clemsa_free,\n\n    .feed = subghz_protocol_decoder_clemsa_feed,\n    .reset = subghz_protocol_decoder_clemsa_reset,\n\n    .get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data,\n    .serialize = subghz_protocol_decoder_clemsa_serialize,\n    .deserialize = subghz_protocol_decoder_clemsa_deserialize,\n    .get_string = subghz_protocol_decoder_clemsa_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = {\n    .alloc = subghz_protocol_encoder_clemsa_alloc,\n    .free = subghz_protocol_encoder_clemsa_free,\n\n    .deserialize = subghz_protocol_encoder_clemsa_deserialize,\n    .stop = subghz_protocol_encoder_clemsa_stop,\n    .yield = subghz_protocol_encoder_clemsa_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_clemsa = {\n    .name = SUBGHZ_PROTOCOL_CLEMSA_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_clemsa_decoder,\n    .encoder = &subghz_protocol_clemsa_encoder,\n};\n\nvoid* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa));\n\n    instance->base.protocol = &subghz_protocol_clemsa;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_clemsa_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderClemsa* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderClemsa instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long);\n        }\n    }\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_clemsa_const.te_short +\n                subghz_protocol_clemsa_const.te_long * 7);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_clemsa_const.te_long +\n                subghz_protocol_clemsa_const.te_long * 7);\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderClemsa* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_clemsa_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_clemsa_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_clemsa_stop(void* context) {\n    SubGhzProtocolEncoderClemsa* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_clemsa_yield(void* context) {\n    SubGhzProtocolEncoderClemsa* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa));\n    instance->base.protocol = &subghz_protocol_clemsa;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_clemsa_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_clemsa_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    instance->decoder.parser_step = ClemsaDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case ClemsaDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <\n                        subghz_protocol_clemsa_const.te_delta * 25)) {\n            instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        }\n        break;\n\n    case ClemsaDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = ClemsaDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = ClemsaDecoderStepReset;\n        }\n        break;\n\n    case ClemsaDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <\n                subghz_protocol_clemsa_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) <\n                subghz_protocol_clemsa_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <\n                 subghz_protocol_clemsa_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) <\n                 subghz_protocol_clemsa_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <\n                subghz_protocol_clemsa_const.te_delta * 25) {\n                if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <\n                   subghz_protocol_clemsa_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else if(\n                    DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <\n                    subghz_protocol_clemsa_const.te_delta * 3) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else {\n                    instance->decoder.parser_step = ClemsaDecoderStepReset;\n                }\n\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_clemsa_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n\n            } else {\n                instance->decoder.parser_step = ClemsaDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = ClemsaDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->serial = (instance->data >> 2) & 0xFFFF;\n    instance->btn = (instance->data & 0x03);\n}\n\nuint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_clemsa_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderClemsa* instance = context;\n    subghz_protocol_clemsa_check_remote_controller(&instance->generic);\n    //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%05lX   Btn %X\\r\\n\"\n        \"  +:   \" DIP_PATTERN \"\\r\\n\"\n        \"  o:   \" DIP_PATTERN \"\\r\\n\"\n        \"  -:   \" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0x3FFFF),\n        instance->generic.btn,\n        SHOW_DIP_P(instance->generic.serial, DIP_P),\n        SHOW_DIP_P(instance->generic.serial, DIP_O),\n        SHOW_DIP_P(instance->generic.serial, DIP_N));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/clemsa.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_CLEMSA_NAME \"Clemsa\"\n\ntypedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa;\ntypedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa;\n\nextern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder;\nextern const SubGhzProtocol subghz_protocol_clemsa;\n\n/**\n * Allocate SubGhzProtocolEncoderClemsa.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance\n */\nvoid* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderClemsa.\n * @param context Pointer to a SubGhzProtocolEncoderClemsa instance\n */\nvoid subghz_protocol_encoder_clemsa_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderClemsa instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderClemsa instance\n */\nvoid subghz_protocol_encoder_clemsa_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderClemsa instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_clemsa_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderClemsa.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance\n */\nvoid* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderClemsa.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n */\nvoid subghz_protocol_decoder_clemsa_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderClemsa.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n */\nvoid subghz_protocol_decoder_clemsa_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderClemsa.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderClemsa.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderClemsa instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/dickert_mahs.c",
    "content": "#include \"dickert_mahs.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n#include <furi_hal_rtc.h>\n\n#define TAG \"SubGhzProtocolDicketMAHS\"\n\nstatic const SubGhzBlockConst subghz_protocol_dickert_mahs_const = {\n    .te_short = 400,\n    .te_long = 800,\n    .te_delta = 100,\n    .min_count_bit_for_found = 36,\n};\n\nstruct SubGhzProtocolDecoderDickertMAHS {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t tmp[2];\n    uint8_t tmp_cnt;\n};\n\nstruct SubGhzProtocolEncoderDickertMAHS {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    DickertMAHSDecoderStepReset = 0,\n    DickertMAHSDecoderStepInitial,\n    DickertMAHSDecoderStepRecording,\n} DickertMAHSDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_dickert_mahs_decoder = {\n    .alloc = subghz_protocol_decoder_dickert_mahs_alloc,\n    .free = subghz_protocol_decoder_dickert_mahs_free,\n\n    .feed = subghz_protocol_decoder_dickert_mahs_feed,\n    .reset = subghz_protocol_decoder_dickert_mahs_reset,\n\n    .get_hash_data = subghz_protocol_decoder_dickert_mahs_get_hash_data,\n    .serialize = subghz_protocol_decoder_dickert_mahs_serialize,\n    .deserialize = subghz_protocol_decoder_dickert_mahs_deserialize,\n    .get_string = subghz_protocol_decoder_dickert_mahs_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_dickert_mahs_encoder = {\n    .alloc = subghz_protocol_encoder_dickert_mahs_alloc,\n    .free = subghz_protocol_encoder_dickert_mahs_free,\n\n    .deserialize = subghz_protocol_encoder_dickert_mahs_deserialize,\n    .stop = subghz_protocol_encoder_dickert_mahs_stop,\n    .yield = subghz_protocol_encoder_dickert_mahs_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_dickert_mahs = {\n    .name = SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_dickert_mahs_decoder,\n    .encoder = &subghz_protocol_dickert_mahs_encoder,\n};\n\nstatic void subghz_protocol_encoder_dickert_mahs_parse_buffer(\n    SubGhzProtocolDecoderDickertMAHS* instance,\n    FuriString* output) {\n    // We assume we have only decodes < 64 bit!\n    uint64_t data = instance->generic.data;\n    uint8_t bits[36] = {};\n\n    // Convert uint64_t into bit array\n    for(int i = 35; i >= 0; i--) {\n        if(data & 1) {\n            bits[i] = 1;\n        }\n        data >>= 1;\n    }\n\n    // Decode symbols\n    FuriString* code = furi_string_alloc();\n    for(size_t i = 0; i < 35; i += 2) {\n        uint8_t dip = (bits[i] << 1) + bits[i + 1];\n        //  PLUS  = 3, // 0b11\n        //  ZERO  = 1, // 0b01\n        //  MINUS = 0, // 0x00\n        if(dip == 0x01) {\n            furi_string_cat(code, \"0\");\n        } else if(dip == 0x00) {\n            furi_string_cat(code, \"-\");\n        } else if(dip == 0x03) {\n            furi_string_cat(code, \"+\");\n        } else {\n            furi_string_cat(code, \"?\");\n        }\n    }\n\n    FuriString* user_dips = furi_string_alloc();\n    FuriString* fact_dips = furi_string_alloc();\n    furi_string_set_n(user_dips, code, 0, 10);\n    furi_string_set_n(fact_dips, code, 10, 8);\n\n    furi_string_cat_printf(\n        output,\n        \"%s\\r\\n\"\n        \"User-Dips:\\t%s\\r\\n\"\n        \"Fac-Code:\\t%s\\r\\n\",\n        instance->generic.protocol_name,\n        furi_string_get_cstr(user_dips),\n        furi_string_get_cstr(fact_dips));\n    furi_string_free(user_dips);\n    furi_string_free(fact_dips);\n    furi_string_free(code);\n}\n\nvoid* subghz_protocol_encoder_dickert_mahs_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderDickertMAHS* instance = malloc(sizeof(SubGhzProtocolEncoderDickertMAHS));\n\n    instance->base.protocol = &subghz_protocol_dickert_mahs;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_dickert_mahs_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDickertMAHS* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderDickertMAHS instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_dickert_mahs_get_upload(SubGhzProtocolEncoderDickertMAHS* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_short * 112);\n    // Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_long);\n        }\n    }\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDickertMAHS* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n\n        // Allow for longer keys (<) instead of !=\n        if((instance->generic.data_count_bit <\n            subghz_protocol_dickert_mahs_const.min_count_bit_for_found)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_dickert_mahs_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_dickert_mahs_stop(void* context) {\n    SubGhzProtocolEncoderDickertMAHS* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_dickert_mahs_yield(void* context) {\n    SubGhzProtocolEncoderDickertMAHS* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_dickert_mahs_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderDickertMAHS* instance = malloc(sizeof(SubGhzProtocolDecoderDickertMAHS));\n    instance->base.protocol = &subghz_protocol_dickert_mahs;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->tmp_cnt = 0;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_dickert_mahs_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_dickert_mahs_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n    instance->decoder.parser_step = DickertMAHSDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_dickert_mahs_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case DickertMAHSDecoderStepReset:\n        // Check if done\n        if(instance->decoder.decode_count_bit >=\n           subghz_protocol_dickert_mahs_const.min_count_bit_for_found) {\n            instance->generic.serial = 0x0;\n            instance->generic.btn = 0x0;\n\n            instance->generic.data = instance->decoder.decode_data;\n            instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n            if(instance->base.callback)\n                instance->base.callback(&instance->base, instance->base.context);\n\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        }\n\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_dickert_mahs_const.te_long * 50) <\n                        subghz_protocol_dickert_mahs_const.te_delta * 70)) {\n            //Found header DICKERT_MAHS 44k us\n            instance->decoder.parser_step = DickertMAHSDecoderStepInitial;\n        }\n        break;\n    case DickertMAHSDecoderStepInitial:\n        if(!level) {\n            break;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_dickert_mahs_const.te_short) <\n            subghz_protocol_dickert_mahs_const.te_delta) {\n            //Found start bit DICKERT_MAHS\n            instance->decoder.parser_step = DickertMAHSDecoderStepRecording;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = DickertMAHSDecoderStepReset;\n        }\n        break;\n    case DickertMAHSDecoderStepRecording:\n        if((!level && instance->tmp_cnt == 0) || (level && instance->tmp_cnt == 1)) {\n            instance->tmp[instance->tmp_cnt] = duration;\n\n            instance->tmp_cnt++;\n\n            if(instance->tmp_cnt == 2) {\n                if(DURATION_DIFF(instance->tmp[0] + instance->tmp[1], 1200) <\n                   subghz_protocol_dickert_mahs_const.te_delta) {\n                    if(DURATION_DIFF(instance->tmp[0], subghz_protocol_dickert_mahs_const.te_long) <\n                       subghz_protocol_dickert_mahs_const.te_delta) {\n                        subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                    } else if(\n                        DURATION_DIFF(\n                            instance->tmp[0], subghz_protocol_dickert_mahs_const.te_short) <\n                        subghz_protocol_dickert_mahs_const.te_delta) {\n                        subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                    }\n\n                    instance->tmp_cnt = 0;\n                } else {\n                    instance->tmp_cnt = 0;\n                    instance->decoder.parser_step = DickertMAHSDecoderStepReset;\n                }\n            }\n        } else {\n            instance->tmp_cnt = 0;\n            instance->decoder.parser_step = DickertMAHSDecoderStepReset;\n        }\n\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_dickert_mahs_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_dickert_mahs_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDickertMAHS* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n\n        // Allow for longer keys (<) instead of !=\n        if((instance->generic.data_count_bit <\n            subghz_protocol_dickert_mahs_const.min_count_bit_for_found)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_dickert_mahs_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    subghz_protocol_encoder_dickert_mahs_parse_buffer(context, output);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/dickert_mahs.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME \"Dickert_MAHS\"\n\ntypedef struct SubGhzProtocolDecoderDickertMAHS SubGhzProtocolDecoderDickertMAHS;\ntypedef struct SubGhzProtocolEncoderDickertMAHS SubGhzProtocolEncoderDickertMAHS;\n\nextern const SubGhzProtocolDecoder subghz_protocol_dickert_mahs_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_dickert_mahs_encoder;\nextern const SubGhzProtocol subghz_protocol_dickert_mahs;\n\n/** Allocate SubGhzProtocolEncoderDickertMAHS.\n *\n * @param      environment  Pointer to a SubGhzEnvironment instance\n *\n * @return     pointer to a SubGhzProtocolEncoderDickertMAHS instance\n */\nvoid* subghz_protocol_encoder_dickert_mahs_alloc(SubGhzEnvironment* environment);\n\n/** Free SubGhzProtocolEncoderDickertMAHS.\n *\n * @param      context  Pointer to a SubGhzProtocolEncoderDickertMAHS instance\n */\nvoid subghz_protocol_encoder_dickert_mahs_free(void* context);\n\n/** Deserialize and generating an upload to send.\n *\n * @param      context         Pointer to a SubGhzProtocolEncoderDickertMAHS\n *                             instance\n * @param      flipper_format  Pointer to a FlipperFormat instance\n *\n * @return     status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format);\n\n/** Forced transmission stop.\n *\n * @param      context  Pointer to a SubGhzProtocolEncoderDickertMAHS instance\n */\nvoid subghz_protocol_encoder_dickert_mahs_stop(void* context);\n\n/** Getting the level and duration of the upload to be loaded into DMA.\n *\n * @param      context  Pointer to a SubGhzProtocolEncoderDickertMAHS instance\n *\n * @return     LevelDuration\n */\nLevelDuration subghz_protocol_encoder_dickert_mahs_yield(void* context);\n\n/** Allocate SubGhzProtocolDecoderDickertMAHS.\n *\n * @param      environment  Pointer to a SubGhzEnvironment instance\n *\n * @return     pointer to a SubGhzProtocolDecoderDickertMAHS instance\n */\nvoid* subghz_protocol_decoder_dickert_mahs_alloc(SubGhzEnvironment* environment);\n\n/** Free SubGhzProtocolDecoderDickertMAHS.\n *\n * @param      context  Pointer to a SubGhzProtocolDecoderDickertMAHS instance\n */\nvoid subghz_protocol_decoder_dickert_mahs_free(void* context);\n\n/** Reset decoder SubGhzProtocolDecoderDickertMAHS.\n *\n * @param      context  Pointer to a SubGhzProtocolDecoderDickertMAHS instance\n */\nvoid subghz_protocol_decoder_dickert_mahs_reset(void* context);\n\n/** Parse a raw sequence of levels and durations received from the air.\n *\n * @param      context   Pointer to a SubGhzProtocolDecoderDickertMAHS instance\n * @param      level     Signal level true-high false-low\n * @param      duration  Duration of this level in, us\n */\nvoid subghz_protocol_decoder_dickert_mahs_feed(void* context, bool level, uint32_t duration);\n\n/** Getting the hash sum of the last randomly received parcel.\n *\n * @param      context  Pointer to a SubGhzProtocolDecoderDickertMAHS instance\n *\n * @return     hash Hash sum\n */\nuint8_t subghz_protocol_decoder_dickert_mahs_get_hash_data(void* context);\n\n/** Serialize data SubGhzProtocolDecoderDickertMAHS.\n *\n * @param      context         Pointer to a SubGhzProtocolDecoderDickertMAHS\n *                             instance\n * @param      flipper_format  Pointer to a FlipperFormat instance\n * @param      preset          The modulation on which the signal was received,\n *                             SubGhzRadioPreset\n *\n * @return     status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_dickert_mahs_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/** Deserialize data SubGhzProtocolDecoderDickertMAHS.\n *\n * @param      context         Pointer to a SubGhzProtocolDecoderDickertMAHS\n *                             instance\n * @param      flipper_format  Pointer to a FlipperFormat instance\n *\n * @return     status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format);\n\n/** Getting a textual representation of the received data.\n *\n * @param      context  Pointer to a SubGhzProtocolDecoderDickertMAHS instance\n * @param      output   Resulting text\n */\nvoid subghz_protocol_decoder_dickert_mahs_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/doitrand.c",
    "content": "#include \"doitrand.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolDoitrand\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c%c%c\"\n#define CNT_TO_DIP(dip)                                                                     \\\n    (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'),     \\\n        (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \\\n        (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \\\n        (dip & 0x0002 ? '1' : '0')\n\nstatic const SubGhzBlockConst subghz_protocol_doitrand_const = {\n    .te_short = 400,\n    .te_long = 1100,\n    .te_delta = 150,\n    .min_count_bit_for_found = 37,\n};\n\nstruct SubGhzProtocolDecoderDoitrand {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderDoitrand {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    DoitrandDecoderStepReset = 0,\n    DoitrandDecoderStepFoundStartBit,\n    DoitrandDecoderStepSaveDuration,\n    DoitrandDecoderStepCheckDuration,\n} DoitrandDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = {\n    .alloc = subghz_protocol_decoder_doitrand_alloc,\n    .free = subghz_protocol_decoder_doitrand_free,\n\n    .feed = subghz_protocol_decoder_doitrand_feed,\n    .reset = subghz_protocol_decoder_doitrand_reset,\n\n    .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data,\n    .serialize = subghz_protocol_decoder_doitrand_serialize,\n    .deserialize = subghz_protocol_decoder_doitrand_deserialize,\n    .get_string = subghz_protocol_decoder_doitrand_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = {\n    .alloc = subghz_protocol_encoder_doitrand_alloc,\n    .free = subghz_protocol_encoder_doitrand_free,\n\n    .deserialize = subghz_protocol_encoder_doitrand_deserialize,\n    .stop = subghz_protocol_encoder_doitrand_stop,\n    .yield = subghz_protocol_encoder_doitrand_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_doitrand = {\n    .name = SUBGHZ_PROTOCOL_DOITRAND_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_doitrand_decoder,\n    .encoder = &subghz_protocol_doitrand_encoder,\n};\n\nvoid* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand));\n\n    instance->base.protocol = &subghz_protocol_doitrand;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_doitrand_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDoitrand* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDoitrand* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_doitrand_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_doitrand_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_doitrand_stop(void* context) {\n    SubGhzProtocolEncoderDoitrand* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_doitrand_yield(void* context) {\n    SubGhzProtocolEncoderDoitrand* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand));\n    instance->base.protocol = &subghz_protocol_doitrand;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_doitrand_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_doitrand_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    instance->decoder.parser_step = DoitrandDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case DoitrandDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) <\n                        subghz_protocol_doitrand_const.te_delta * 30)) {\n            //Found Preambula\n            instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;\n        }\n        break;\n    case DoitrandDecoderStepFoundStartBit:\n        if(level && (DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) <\n                     subghz_protocol_doitrand_const.te_delta * 3)) {\n            //Found start bit\n            instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = DoitrandDecoderStepReset;\n        }\n        break;\n    case DoitrandDecoderStepSaveDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 +\n                            subghz_protocol_doitrand_const.te_delta)) {\n                instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_doitrand_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = DoitrandDecoderStepCheckDuration;\n            }\n        }\n        break;\n    case DoitrandDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) <\n                subghz_protocol_doitrand_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) <\n                subghz_protocol_doitrand_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) <\n                 subghz_protocol_doitrand_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) <\n                 subghz_protocol_doitrand_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = DoitrandDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = DoitrandDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n*               67892345   0      k    1                    \n* 0000082F5F => 00000000000000000 10 000010111101011111 \n* 0002082F5F => 00000000000100000 10 000010111101011111\n* 0200082F5F => 00010000000000000 10 000010111101011111\n* 0400082F5F => 00100000000000000 10 000010111101011111\n* 0800082F5F => 01000000000000000 10 000010111101011111\n* 1000082F5F => 10000000000000000 10 000010111101011111\n* 0020082F5F => 00000001000000000 10 000010111101011111\n* 0040082F5F => 00000010000000000 10 000010111101011111\n* 0080082F5F => 00000100000000000 10 000010111101011111\n* 0100082F5F => 00001000000000000 10 000010111101011111\n* 000008AF5F => 00000000000000000 10 001010111101011111\n* 1FE208AF5F => 11111111000100000 10 001010111101011111\n*\n* 0...9 - DIP\n* k- KEY\n*/\n    instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1);\n    instance->btn = ((instance->data >> 18) & 0x3);\n}\n\nuint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_doitrand_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_doitrand_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDoitrand* instance = context;\n    subghz_protocol_doitrand_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%02lX%08lX\\r\\n\"\n        \"Btn:%X\\r\\n\"\n        \"DIP:\" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.btn,\n        CNT_TO_DIP(instance->generic.cnt));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/doitrand.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_DOITRAND_NAME \"Doitrand\"\n\ntypedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand;\ntypedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand;\n\nextern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder;\nextern const SubGhzProtocol subghz_protocol_doitrand;\n\n/**\n * Allocate SubGhzProtocolEncoderDoitrand.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance\n */\nvoid* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderDoitrand.\n * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance\n */\nvoid subghz_protocol_encoder_doitrand_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance\n */\nvoid subghz_protocol_encoder_doitrand_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_doitrand_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderDoitrand.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance\n */\nvoid* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderDoitrand.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n */\nvoid subghz_protocol_decoder_doitrand_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderDoitrand.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n */\nvoid subghz_protocol_decoder_doitrand_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderDoitrand.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_doitrand_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderDoitrand.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/dooya.c",
    "content": "#include \"dooya.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolDooya\"\n\n#define DOYA_SINGLE_CHANNEL 0xFF\n\nstatic const SubGhzBlockConst subghz_protocol_dooya_const = {\n    .te_short = 366,\n    .te_long = 733,\n    .te_delta = 120,\n    .min_count_bit_for_found = 40,\n};\n\nstruct SubGhzProtocolDecoderDooya {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderDooya {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    DooyaDecoderStepReset = 0,\n    DooyaDecoderStepFoundStartBit,\n    DooyaDecoderStepSaveDuration,\n    DooyaDecoderStepCheckDuration,\n} DooyaDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_dooya_decoder = {\n    .alloc = subghz_protocol_decoder_dooya_alloc,\n    .free = subghz_protocol_decoder_dooya_free,\n\n    .feed = subghz_protocol_decoder_dooya_feed,\n    .reset = subghz_protocol_decoder_dooya_reset,\n\n    .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data,\n    .serialize = subghz_protocol_decoder_dooya_serialize,\n    .deserialize = subghz_protocol_decoder_dooya_deserialize,\n    .get_string = subghz_protocol_decoder_dooya_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_dooya_encoder = {\n    .alloc = subghz_protocol_encoder_dooya_alloc,\n    .free = subghz_protocol_encoder_dooya_free,\n\n    .deserialize = subghz_protocol_encoder_dooya_deserialize,\n    .stop = subghz_protocol_encoder_dooya_stop,\n    .yield = subghz_protocol_encoder_dooya_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_dooya = {\n    .name = SUBGHZ_PROTOCOL_DOOYA_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_dooya_decoder,\n    .encoder = &subghz_protocol_dooya_encoder,\n};\n\nvoid* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya));\n\n    instance->base.protocol = &subghz_protocol_dooya;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_dooya_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDooya* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderDooya instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    if(bit_read(instance->generic.data, 0)) {\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_dooya_const.te_long * 12 +\n                subghz_protocol_dooya_const.te_long);\n    } else {\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_dooya_const.te_long * 12 +\n                subghz_protocol_dooya_const.te_short);\n    }\n\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderDooya* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_dooya_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_dooya_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_dooya_stop(void* context) {\n    SubGhzProtocolEncoderDooya* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_dooya_yield(void* context) {\n    SubGhzProtocolEncoderDooya* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya));\n    instance->base.protocol = &subghz_protocol_dooya;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_dooya_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_dooya_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n    instance->decoder.parser_step = DooyaDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case DooyaDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) <\n                        subghz_protocol_dooya_const.te_delta * 20)) {\n            instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;\n        }\n        break;\n\n    case DooyaDecoderStepFoundStartBit:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) <\n               subghz_protocol_dooya_const.te_delta * 3) {\n                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n            } else {\n                instance->decoder.parser_step = DooyaDecoderStepReset;\n            }\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) <\n            subghz_protocol_dooya_const.te_delta * 5) {\n            break;\n        } else {\n            instance->decoder.parser_step = DooyaDecoderStepReset;\n        }\n        break;\n\n    case DooyaDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = DooyaDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = DooyaDecoderStepReset;\n        }\n        break;\n\n    case DooyaDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= (subghz_protocol_dooya_const.te_long * 4)) {\n                //add last bit\n                if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <\n                   subghz_protocol_dooya_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else if(\n                    DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <\n                    subghz_protocol_dooya_const.te_delta * 2) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else {\n                    instance->decoder.parser_step = DooyaDecoderStepReset;\n                    break;\n                }\n                instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_dooya_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <\n                 subghz_protocol_dooya_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) <\n                 subghz_protocol_dooya_const.te_delta * 2)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <\n                 subghz_protocol_dooya_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) <\n                 subghz_protocol_dooya_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = DooyaDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = DooyaDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_dooya_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n * \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tserial       s/m  ch      key   \t\n * long press down   X * E1DC030533, 40b \t\t\t111000011101110000000011 0000 0101 0011 0011\n * \n * short press down  3 * E1DC030533, 40b\t\t\t111000011101110000000011 0000 0101 0011 0011\n *                   3 * E1DC03053C, 40b \t        111000011101110000000011 0000 0101 0011 1100\n * \t\n * press stop        X * E1DC030555, 40b\t\t\t111000011101110000000011 0000 0101 0101 0101\n * \n * long press up     X * E1DC030511, 40b\t\t\t111000011101110000000011 0000 0101 0001 0001\n * \n * short press up    3 * E1DC030511, 40b\t\t\t111000011101110000000011 0000 0101 0001 0001\n *                   3 * E1DC03051E, 40b\t\t    111000011101110000000011 0000 0101 0001 1110\n *\n * serial: 3 byte serial number\n * s/m: single (b0000) / multi (b0001) channel console  \n * ch: channel if single (always b0101) or multi \n * key: 0b00010001 - long press up\n *      0b00011110 - short press up\n *      0b00110011 - long press down\n *      0b00111100 - short press down\n *      0b01010101 - press stop \n *      0b01111001 - press up + down\n *      0b10000000 - press up + stop\n *      0b10000001 - press down + stop\n *      0b11001100 - press P2\n *      \n*/\n\n    instance->serial = (instance->data >> 16);\n    if((instance->data >> 12) & 0x0F) {\n        instance->cnt = (instance->data >> 8) & 0x0F;\n    } else {\n        instance->cnt = DOYA_SINGLE_CHANNEL;\n    }\n    instance->btn = instance->data & 0xFF;\n}\n\nuint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_dooya_const.min_count_bit_for_found);\n}\n\n/**\n * Get button name.\n * @param btn Button number, 8 bit\n */\nstatic const char* subghz_protocol_dooya_get_name_button(uint8_t btn) {\n    const char* btn_name;\n    switch(btn) {\n    case 0b00010001:\n        btn_name = \"Up_Long\";\n        break;\n    case 0b00011110:\n        btn_name = \"Up_Short\";\n        break;\n    case 0b00110011:\n        btn_name = \"Down_Long\";\n        break;\n    case 0b00111100:\n        btn_name = \"Down_Short\";\n        break;\n    case 0b01010101:\n        btn_name = \"Stop\";\n        break;\n    case 0b01111001:\n        btn_name = \"Up+Down\";\n        break;\n    case 0b10000000:\n        btn_name = \"Up+Stop\";\n        break;\n    case 0b10000001:\n        btn_name = \"Down+Stop\";\n        break;\n    case 0b11001100:\n        btn_name = \"P2\";\n        break;\n    default:\n        btn_name = \"Unknown\";\n        break;\n    }\n    return btn_name;\n}\n\nvoid subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderDooya* instance = context;\n\n    subghz_protocol_dooya_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%010llX\\r\\n\"\n        \"Sn:0x%08lX\\r\\n\"\n        \"Btn:%s\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        instance->generic.data,\n        instance->generic.serial,\n        subghz_protocol_dooya_get_name_button(instance->generic.btn));\n    if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) {\n        furi_string_cat_printf(output, \"Ch:Single\\r\\n\");\n    } else {\n        furi_string_cat_printf(output, \"Ch:%lu\\r\\n\", instance->generic.cnt);\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/dooya.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_DOOYA_NAME \"Dooya\"\n\ntypedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya;\ntypedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya;\n\nextern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder;\nextern const SubGhzProtocol subghz_protocol_dooya;\n\n/**\n * Allocate SubGhzProtocolEncoderDooya.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance\n */\nvoid* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderDooya.\n * @param context Pointer to a SubGhzProtocolEncoderDooya instance\n */\nvoid subghz_protocol_encoder_dooya_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderDooya instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderDooya instance\n */\nvoid subghz_protocol_encoder_dooya_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderDooya instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_dooya_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderDooya.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance\n */\nvoid* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderDooya.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n */\nvoid subghz_protocol_decoder_dooya_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderDooya.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n */\nvoid subghz_protocol_decoder_dooya_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderDooya.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderDooya.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderDooya instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/faac_slh.c",
    "content": "#include \"faac_slh.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolFaacShl\"\n\nstatic const SubGhzBlockConst subghz_protocol_faac_slh_const = {\n    .te_short = 255,\n    .te_long = 595,\n    .te_delta = 100,\n    .min_count_bit_for_found = 64,\n};\n\nstruct SubGhzProtocolDecoderFaacSLH {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderFaacSLH {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    FaacSLHDecoderStepReset = 0,\n    FaacSLHDecoderStepFoundPreambula,\n    FaacSLHDecoderStepSaveDuration,\n    FaacSLHDecoderStepCheckDuration,\n} FaacSLHDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder = {\n    .alloc = subghz_protocol_decoder_faac_slh_alloc,\n    .free = subghz_protocol_decoder_faac_slh_free,\n\n    .feed = subghz_protocol_decoder_faac_slh_feed,\n    .reset = subghz_protocol_decoder_faac_slh_reset,\n\n    .get_hash_data = subghz_protocol_decoder_faac_slh_get_hash_data,\n    .serialize = subghz_protocol_decoder_faac_slh_serialize,\n    .deserialize = subghz_protocol_decoder_faac_slh_deserialize,\n    .get_string = subghz_protocol_decoder_faac_slh_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_faac_slh = {\n    .name = SUBGHZ_PROTOCOL_FAAC_SLH_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_faac_slh_decoder,\n    .encoder = &subghz_protocol_faac_slh_encoder,\n};\n\nvoid* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolDecoderFaacSLH));\n    instance->base.protocol = &subghz_protocol_faac_slh;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_faac_slh_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_faac_slh_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    instance->decoder.parser_step = FaacSLHDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case FaacSLHDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) <\n                       subghz_protocol_faac_slh_const.te_delta * 3)) {\n            instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula;\n        }\n        break;\n    case FaacSLHDecoderStepFoundPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) <\n                        subghz_protocol_faac_slh_const.te_delta * 3)) {\n            //Found Preambula\n            instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = FaacSLHDecoderStepReset;\n        }\n        break;\n    case FaacSLHDecoderStepSaveDuration:\n        if(level) {\n            if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 +\n                            subghz_protocol_faac_slh_const.te_delta)) {\n                instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_faac_slh_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = FaacSLHDecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = FaacSLHDecoderStepReset;\n        }\n        break;\n    case FaacSLHDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_short) <\n                subghz_protocol_faac_slh_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long) <\n                subghz_protocol_faac_slh_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_long) <\n                 subghz_protocol_faac_slh_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_short) <\n                 subghz_protocol_faac_slh_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = FaacSLHDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = FaacSLHDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_faac_slh_check_remote_controller(SubGhzBlockGeneric* instance) {\n    uint64_t code_found_reverse =\n        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);\n    uint32_t code_fix = code_found_reverse & 0xFFFFFFFF;\n\n    instance->serial = code_fix & 0xFFFFFFF;\n    instance->btn = (code_fix >> 28) & 0x0F;\n}\n\nuint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_faac_slh_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFaacSLH* instance = context;\n    subghz_protocol_faac_slh_check_remote_controller(&instance->generic);\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n    uint32_t code_fix = code_found_reverse & 0xFFFFFFFF;\n    uint32_t code_hop = (code_found_reverse >> 32) & 0xFFFFFFFF;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%lX%08lX\\r\\n\"\n        \"Fix:%08lX \\r\\n\"\n        \"Hop:%08lX \\r\\n\"\n        \"Sn:%07lX Btn:%X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        code_fix,\n        code_hop,\n        instance->generic.serial,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/faac_slh.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_FAAC_SLH_NAME \"Faac SLH\"\n\ntypedef struct SubGhzProtocolDecoderFaacSLH SubGhzProtocolDecoderFaacSLH;\ntypedef struct SubGhzProtocolEncoderFaacSLH SubGhzProtocolEncoderFaacSLH;\n\nextern const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder;\nextern const SubGhzProtocol subghz_protocol_faac_slh;\n\n/**\n * Allocate SubGhzProtocolDecoderFaacSLH.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderFaacSLH* pointer to a SubGhzProtocolDecoderFaacSLH instance\n */\nvoid* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderFaacSLH.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n */\nvoid subghz_protocol_decoder_faac_slh_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderFaacSLH.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n */\nvoid subghz_protocol_decoder_faac_slh_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderFaacSLH.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderFaacSLH.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/feron.c",
    "content": "#include \"feron.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolFeron\"\n\nstatic const SubGhzBlockConst subghz_protocol_feron_const = {\n    .te_short = 350,\n    .te_long = 750,\n    .te_delta = 150,\n    .min_count_bit_for_found = 32,\n};\n\nstruct SubGhzProtocolDecoderFeron {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderFeron {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    FeronDecoderStepReset = 0,\n    FeronDecoderStepSaveDuration,\n    FeronDecoderStepCheckDuration,\n} FeronDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_feron_decoder = {\n    .alloc = subghz_protocol_decoder_feron_alloc,\n    .free = subghz_protocol_decoder_feron_free,\n\n    .feed = subghz_protocol_decoder_feron_feed,\n    .reset = subghz_protocol_decoder_feron_reset,\n\n    .get_hash_data = subghz_protocol_decoder_feron_get_hash_data,\n    .serialize = subghz_protocol_decoder_feron_serialize,\n    .deserialize = subghz_protocol_decoder_feron_deserialize,\n    .get_string = subghz_protocol_decoder_feron_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_feron_encoder = {\n    .alloc = subghz_protocol_encoder_feron_alloc,\n    .free = subghz_protocol_encoder_feron_free,\n\n    .deserialize = subghz_protocol_encoder_feron_deserialize,\n    .stop = subghz_protocol_encoder_feron_stop,\n    .yield = subghz_protocol_encoder_feron_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_feron = {\n    .name = SUBGHZ_PROTOCOL_FERON_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_feron_decoder,\n    .encoder = &subghz_protocol_feron_encoder,\n};\n\nvoid* subghz_protocol_encoder_feron_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderFeron* instance = malloc(sizeof(SubGhzProtocolEncoderFeron));\n\n    instance->base.protocol = &subghz_protocol_feron;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_feron_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderFeron* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderFeron instance\n */\nstatic void subghz_protocol_encoder_feron_get_upload(SubGhzProtocolEncoderFeron* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    // Send key and GAP\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            // Send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_feron_const.te_long);\n            if(i == 1) {\n                //Send 500/500 and gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_feron_const.te_short + 150);\n                instance->encoder.upload[index++] = level_duration_make(\n                    true, (uint32_t)subghz_protocol_feron_const.te_short + 150);\n                // Gap\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long * 6);\n            } else {\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_short);\n            }\n        } else {\n            // Send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_feron_const.te_short);\n            if(i == 1) {\n                //Send 500/500 and gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_feron_const.te_short + 150);\n                instance->encoder.upload[index++] = level_duration_make(\n                    true, (uint32_t)subghz_protocol_feron_const.te_short + 150);\n                // Gap\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long * 6);\n            } else {\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long);\n            }\n        }\n    }\n\n    instance->encoder.size_upload = index;\n    return;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_feron_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->serial = instance->data >> 16;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_feron_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderFeron* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_feron_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_feron_check_remote_controller(&instance->generic);\n        subghz_protocol_encoder_feron_get_upload(instance);\n        instance->encoder.front = 0;\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_feron_stop(void* context) {\n    SubGhzProtocolEncoderFeron* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_feron_yield(void* context) {\n    SubGhzProtocolEncoderFeron* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_feron_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderFeron* instance = malloc(sizeof(SubGhzProtocolDecoderFeron));\n    instance->base.protocol = &subghz_protocol_feron;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_feron_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_feron_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n    instance->decoder.parser_step = FeronDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_feron_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n\n    // Feron Decoder\n    // 2025.04 - @xMasterX (MMX)\n\n    // Key samples\n    /*\n    0110001100111000 1000010101111010 - ON\n    0110001100111000 1000010001111011 - OFF\n\n    0110001100111000 1000011001111001 - brightness up\n    0110001100111000 1000011101111000 - brightness down\n \n    0110001100111000 1000001001111101 - scroll mode command\n\n    ------------------------------------------\n    0110001100111000 0111000010001111 - R\n    0110001100111000 0001101011100101 - B\n    0110001100111000 0100000010111111 - G\n    */\n\n    switch(instance->decoder.parser_step) {\n    case FeronDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_feron_const.te_long * 6) <\n                        subghz_protocol_feron_const.te_delta * 4)) {\n            //Found GAP\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = FeronDecoderStepSaveDuration;\n        }\n        break;\n    case FeronDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = FeronDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = FeronDecoderStepReset;\n        }\n        break;\n    case FeronDecoderStepCheckDuration:\n        if(!level) {\n            // Bit 0 is short and long timing = 350us HIGH (te_last) and 750us LOW\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_short) <\n                subghz_protocol_feron_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_feron_const.te_long) <\n                subghz_protocol_feron_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = FeronDecoderStepSaveDuration;\n                // Bit 1 is long and short timing = 750us HIGH (te_last) and 350us LOW\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_long) <\n                 subghz_protocol_feron_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_feron_const.te_short) <\n                 subghz_protocol_feron_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = FeronDecoderStepSaveDuration;\n            } else if(\n                // End of the key 500Low(we are here)/500High us\n                DURATION_DIFF(\n                    duration, (uint16_t)(subghz_protocol_feron_const.te_short + (uint16_t)150)) <\n                subghz_protocol_feron_const.te_delta) {\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_short) <\n                    subghz_protocol_feron_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_long) <\n                    subghz_protocol_feron_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                // If got 32 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_feron_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = FeronDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = FeronDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = FeronDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_feron_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_feron_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_feron_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_feron_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_feron_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderFeron* instance = context;\n\n    subghz_protocol_feron_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%08lX\\r\\n\"\n        \"Serial: 0x%04lX\\r\\n\"\n        \"Command: 0x%04lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        (uint32_t)(instance->generic.data & 0xFFFF));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/feron.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_FERON_NAME \"Feron\"\n\ntypedef struct SubGhzProtocolDecoderFeron SubGhzProtocolDecoderFeron;\ntypedef struct SubGhzProtocolEncoderFeron SubGhzProtocolEncoderFeron;\n\nextern const SubGhzProtocolDecoder subghz_protocol_feron_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_feron_encoder;\nextern const SubGhzProtocol subghz_protocol_feron;\n\n/**\n * Allocate SubGhzProtocolEncoderFeron.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderFeron* pointer to a SubGhzProtocolEncoderFeron instance\n */\nvoid* subghz_protocol_encoder_feron_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderFeron.\n * @param context Pointer to a SubGhzProtocolEncoderFeron instance\n */\nvoid subghz_protocol_encoder_feron_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderFeron instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_feron_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderFeron instance\n */\nvoid subghz_protocol_encoder_feron_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderFeron instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_feron_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderFeron.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderFeron* pointer to a SubGhzProtocolDecoderFeron instance\n */\nvoid* subghz_protocol_decoder_feron_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderFeron.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n */\nvoid subghz_protocol_decoder_feron_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderFeron.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n */\nvoid subghz_protocol_decoder_feron_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_feron_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_feron_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderFeron.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_feron_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderFeron.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_feron_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderFeron instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_feron_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/gangqi.c",
    "content": "#include \"gangqi.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolGangQi\"\n\nstatic const SubGhzBlockConst subghz_protocol_gangqi_const = {\n    .te_short = 500,\n    .te_long = 1200,\n    .te_delta = 200,\n    .min_count_bit_for_found = 34,\n};\n\nstruct SubGhzProtocolDecoderGangQi {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderGangQi {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    GangQiDecoderStepReset = 0,\n    GangQiDecoderStepSaveDuration,\n    GangQiDecoderStepCheckDuration,\n} GangQiDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_gangqi_decoder = {\n    .alloc = subghz_protocol_decoder_gangqi_alloc,\n    .free = subghz_protocol_decoder_gangqi_free,\n\n    .feed = subghz_protocol_decoder_gangqi_feed,\n    .reset = subghz_protocol_decoder_gangqi_reset,\n\n    .get_hash_data = subghz_protocol_decoder_gangqi_get_hash_data,\n    .serialize = subghz_protocol_decoder_gangqi_serialize,\n    .deserialize = subghz_protocol_decoder_gangqi_deserialize,\n    .get_string = subghz_protocol_decoder_gangqi_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_gangqi_encoder = {\n    .alloc = subghz_protocol_encoder_gangqi_alloc,\n    .free = subghz_protocol_encoder_gangqi_free,\n\n    .deserialize = subghz_protocol_encoder_gangqi_deserialize,\n    .stop = subghz_protocol_encoder_gangqi_stop,\n    .yield = subghz_protocol_encoder_gangqi_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_gangqi = {\n    .name = SUBGHZ_PROTOCOL_GANGQI_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_gangqi_decoder,\n    .encoder = &subghz_protocol_gangqi_encoder,\n};\n\nvoid* subghz_protocol_encoder_gangqi_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderGangQi* instance = malloc(sizeof(SubGhzProtocolEncoderGangQi));\n\n    instance->base.protocol = &subghz_protocol_gangqi;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 1024;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_gangqi_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderGangQi* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderGangQi instance\n */\nstatic void subghz_protocol_encoder_gangqi_get_upload(SubGhzProtocolEncoderGangQi* instance) {\n    furi_assert(instance);\n\n    // Generate new key\n    uint16_t serial = (uint16_t)((instance->generic.data >> 18) & 0xFFFF);\n    uint8_t const_and_button = (uint8_t)(0xD0 | instance->generic.btn);\n    uint8_t serial_high = (uint8_t)(serial >> 8);\n    uint8_t serial_low = (uint8_t)(serial & 0xFF);\n    uint8_t bytesum = (uint8_t)(0xC8 - serial_high - serial_low - const_and_button);\n\n    instance->generic.data = (instance->generic.data >> 14) << 14 | (instance->generic.btn << 10) |\n                             (bytesum << 2);\n\n    size_t index = 0;\n\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_gangqi_const.te_long * 2);\n\n    for(size_t r = 0; r < 5; r++) {\n        // Send key and GAP between parcels\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(bit_read(instance->generic.data, i - 1)) {\n                // Send bit 1\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_gangqi_const.te_long);\n                if(i == 1) {\n                    //Send gap if bit was last\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false,\n                        (uint32_t)subghz_protocol_gangqi_const.te_short * 4 +\n                            subghz_protocol_gangqi_const.te_delta);\n                } else {\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false, (uint32_t)subghz_protocol_gangqi_const.te_short);\n                }\n            } else {\n                // Send bit 0\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_gangqi_const.te_short);\n                if(i == 1) {\n                    //Send gap if bit was last\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false,\n                        (uint32_t)subghz_protocol_gangqi_const.te_short * 4 +\n                            subghz_protocol_gangqi_const.te_delta);\n                } else {\n                    instance->encoder.upload[index++] =\n                        level_duration_make(false, (uint32_t)subghz_protocol_gangqi_const.te_long);\n                }\n            }\n        }\n    }\n\n    instance->encoder.size_upload = index;\n    return;\n}\n\n/** \n * Analysis of received data and parsing serial number\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_gangqi_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->btn = (instance->data >> 10) & 0xF;\n    instance->serial = (instance->data & 0xFFFFF0000) >> 16;\n\n    // GangQi Decoder\n    // 09.2024 - @xMasterX (MMX) (last update - bytesum calculation at 02.2025)\n    // Thanks @Skorpionm for support!\n    // Thanks @Drone1950 and @mishamyte (who spent 2 weeks on this) for making this work properly\n\n    // Example of correct bytesum calculation\n    // 0xC8 - serial_high - serial_low - constant_and_button\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderGangQi* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_gangqi_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_gangqi_remote_controller(&instance->generic);\n        subghz_protocol_encoder_gangqi_get_upload(instance);\n        instance->encoder.front = 0;\n\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            break;\n        }\n\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_gangqi_stop(void* context) {\n    SubGhzProtocolEncoderGangQi* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_gangqi_yield(void* context) {\n    SubGhzProtocolEncoderGangQi* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_gangqi_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderGangQi* instance = malloc(sizeof(SubGhzProtocolDecoderGangQi));\n    instance->base.protocol = &subghz_protocol_gangqi;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_gangqi_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_gangqi_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n    instance->decoder.parser_step = GangQiDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_gangqi_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case GangQiDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_long * 2) <\n                        subghz_protocol_gangqi_const.te_delta * 5)) {\n            //Found GAP\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = GangQiDecoderStepSaveDuration;\n        }\n        break;\n    case GangQiDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = GangQiDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = GangQiDecoderStepReset;\n        }\n        break;\n    case GangQiDecoderStepCheckDuration:\n        if(!level) {\n            // Bit 0 is short and long timing\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_short) <\n                subghz_protocol_gangqi_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_long) <\n                subghz_protocol_gangqi_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = GangQiDecoderStepSaveDuration;\n                // Bit 1 is long and short timing\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_long) <\n                 subghz_protocol_gangqi_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_short) <\n                 subghz_protocol_gangqi_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = GangQiDecoderStepSaveDuration;\n            } else if(\n                // End of the key\n                DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_long * 2) <\n                subghz_protocol_gangqi_const.te_delta * 5) {\n                //Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)\n                if((DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_gangqi_const.te_short) <\n                    subghz_protocol_gangqi_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_long) <\n                    subghz_protocol_gangqi_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                // If got 34 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_gangqi_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = GangQiDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = GangQiDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = GangQiDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Get button name.\n * @param btn Button number, 4 bit\n */\nstatic const char* subghz_protocol_gangqi_get_button_name(uint8_t btn) {\n    const char* name_btn[16] = {\n        \"Unknown\",\n        \"Exit settings\",\n        \"Volume setting\",\n        \"0x3\",\n        \"Vibro sens. setting\",\n        \"Settings mode\",\n        \"Ringtone setting\",\n        \"Ring\", // D\n        \"0x8\",\n        \"0x9\",\n        \"0xA\",\n        \"Alarm\", // C\n        \"0xC\",\n        \"Arm\", // A\n        \"Disarm\", // B\n        \"0xF\"};\n    return btn <= 0xf ? name_btn[btn] : name_btn[0];\n}\n\nuint8_t subghz_protocol_decoder_gangqi_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_gangqi_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_gangqi_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_gangqi_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGangQi* instance = context;\n\n    // Parse serial\n    subghz_protocol_gangqi_remote_controller(&instance->generic);\n\n    // Get byte sum\n    uint16_t serial = (uint16_t)((instance->generic.data >> 18) & 0xFFFF);\n    uint8_t const_and_button = (uint8_t)(0xD0 | instance->generic.btn);\n    uint8_t serial_high = (uint8_t)(serial >> 8);\n    uint8_t serial_low = (uint8_t)(serial & 0xFF);\n    // Type 1 is what original remotes use, type 2 is \"backdoor\" sum that receiver accepts too\n    uint8_t sum_type1 = (uint8_t)(0xC8 - serial_high - serial_low - const_and_button);\n    uint8_t sum_type2 = (uint8_t)(0x02 + serial_high + serial_low + const_and_button);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%X%08lX\\r\\n\"\n        \"Serial: 0x%05lX\\r\\n\"\n        \"Sum: 0x%02X   Sum2: 0x%02X\\r\\n\"\n        \"Btn: 0x%01X - %s\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint8_t)(instance->generic.data >> 32),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        sum_type1,\n        sum_type2,\n        instance->generic.btn,\n        subghz_protocol_gangqi_get_button_name(instance->generic.btn));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/gangqi.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_GANGQI_NAME \"GangQi\"\n\ntypedef struct SubGhzProtocolDecoderGangQi SubGhzProtocolDecoderGangQi;\ntypedef struct SubGhzProtocolEncoderGangQi SubGhzProtocolEncoderGangQi;\n\nextern const SubGhzProtocolDecoder subghz_protocol_gangqi_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_gangqi_encoder;\nextern const SubGhzProtocol subghz_protocol_gangqi;\n\n/**\n * Allocate SubGhzProtocolEncoderGangQi.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderGangQi* pointer to a SubGhzProtocolEncoderGangQi instance\n */\nvoid* subghz_protocol_encoder_gangqi_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderGangQi.\n * @param context Pointer to a SubGhzProtocolEncoderGangQi instance\n */\nvoid subghz_protocol_encoder_gangqi_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderGangQi instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderGangQi instance\n */\nvoid subghz_protocol_encoder_gangqi_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderGangQi instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_gangqi_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderGangQi.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderGangQi* pointer to a SubGhzProtocolDecoderGangQi instance\n */\nvoid* subghz_protocol_decoder_gangqi_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderGangQi.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n */\nvoid subghz_protocol_decoder_gangqi_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderGangQi.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n */\nvoid subghz_protocol_decoder_gangqi_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_gangqi_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_gangqi_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderGangQi.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_gangqi_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderGangQi.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderGangQi instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_gangqi_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/gate_tx.c",
    "content": "#include \"gate_tx.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolGateTx\"\n\nstatic const SubGhzBlockConst subghz_protocol_gate_tx_const = {\n    .te_short = 350,\n    .te_long = 700,\n    .te_delta = 100,\n    .min_count_bit_for_found = 24,\n};\n\nstruct SubGhzProtocolDecoderGateTx {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderGateTx {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    GateTXDecoderStepReset = 0,\n    GateTXDecoderStepFoundStartBit,\n    GateTXDecoderStepSaveDuration,\n    GateTXDecoderStepCheckDuration,\n} GateTXDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder = {\n    .alloc = subghz_protocol_decoder_gate_tx_alloc,\n    .free = subghz_protocol_decoder_gate_tx_free,\n\n    .feed = subghz_protocol_decoder_gate_tx_feed,\n    .reset = subghz_protocol_decoder_gate_tx_reset,\n\n    .get_hash_data = subghz_protocol_decoder_gate_tx_get_hash_data,\n    .serialize = subghz_protocol_decoder_gate_tx_serialize,\n    .deserialize = subghz_protocol_decoder_gate_tx_deserialize,\n    .get_string = subghz_protocol_decoder_gate_tx_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder = {\n    .alloc = subghz_protocol_encoder_gate_tx_alloc,\n    .free = subghz_protocol_encoder_gate_tx_free,\n\n    .deserialize = subghz_protocol_encoder_gate_tx_deserialize,\n    .stop = subghz_protocol_encoder_gate_tx_stop,\n    .yield = subghz_protocol_encoder_gate_tx_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_gate_tx = {\n    .name = SUBGHZ_PROTOCOL_GATE_TX_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_gate_tx_decoder,\n    .encoder = &subghz_protocol_gate_tx_encoder,\n};\n\nvoid* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderGateTx* instance = malloc(sizeof(SubGhzProtocolEncoderGateTx));\n\n    instance->base.protocol = &subghz_protocol_gate_tx;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_gate_tx_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderGateTx* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderGateTx instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGateTx* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short * 49);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderGateTx* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_gate_tx_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_gate_tx_stop(void* context) {\n    SubGhzProtocolEncoderGateTx* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_gate_tx_yield(void* context) {\n    SubGhzProtocolEncoderGateTx* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderGateTx* instance = malloc(sizeof(SubGhzProtocolDecoderGateTx));\n    instance->base.protocol = &subghz_protocol_gate_tx;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_gate_tx_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_gate_tx_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    instance->decoder.parser_step = GateTXDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case GateTXDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short * 47) <\n                        subghz_protocol_gate_tx_const.te_delta * 47)) {\n            //Found Preambula\n            instance->decoder.parser_step = GateTXDecoderStepFoundStartBit;\n        }\n        break;\n    case GateTXDecoderStepFoundStartBit:\n        if(level && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) <\n                     subghz_protocol_gate_tx_const.te_delta * 3)) {\n            //Found start bit\n            instance->decoder.parser_step = GateTXDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = GateTXDecoderStepReset;\n        }\n        break;\n    case GateTXDecoderStepSaveDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 +\n                            subghz_protocol_gate_tx_const.te_delta)) {\n                instance->decoder.parser_step = GateTXDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_gate_tx_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = GateTXDecoderStepCheckDuration;\n            }\n        }\n        break;\n    case GateTXDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_short) <\n                subghz_protocol_gate_tx_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) <\n                subghz_protocol_gate_tx_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = GateTXDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_long) <\n                 subghz_protocol_gate_tx_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short) <\n                 subghz_protocol_gate_tx_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = GateTXDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = GateTXDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = GateTXDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_gate_tx_check_remote_controller(SubGhzBlockGeneric* instance) {\n    uint32_t code_found_reverse =\n        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);\n\n    instance->serial = (code_found_reverse & 0xFF) << 12 |\n                       ((code_found_reverse >> 8) & 0xFF) << 4 |\n                       ((code_found_reverse >> 20) & 0x0F);\n    instance->btn = ((code_found_reverse >> 16) & 0x0F);\n}\n\nuint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_gate_tx_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_gate_tx_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderGateTx* instance = context;\n    subghz_protocol_gate_tx_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%06lX\\r\\n\"\n        \"Sn:%05lX  Btn:%X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFF),\n        instance->generic.serial,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/gate_tx.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_GATE_TX_NAME \"GateTX\"\n\ntypedef struct SubGhzProtocolDecoderGateTx SubGhzProtocolDecoderGateTx;\ntypedef struct SubGhzProtocolEncoderGateTx SubGhzProtocolEncoderGateTx;\n\nextern const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder;\nextern const SubGhzProtocol subghz_protocol_gate_tx;\n\n/**\n * Allocate SubGhzProtocolEncoderGateTx.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderGateTx* pointer to a SubGhzProtocolEncoderGateTx instance\n */\nvoid* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderGateTx.\n * @param context Pointer to a SubGhzProtocolEncoderGateTx instance\n */\nvoid subghz_protocol_encoder_gate_tx_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderGateTx instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderGateTx instance\n */\nvoid subghz_protocol_encoder_gate_tx_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderGateTx instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_gate_tx_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderGateTx.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderGateTx* pointer to a SubGhzProtocolDecoderGateTx instance\n */\nvoid* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderGateTx.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n */\nvoid subghz_protocol_decoder_gate_tx_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderGateTx.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n */\nvoid subghz_protocol_decoder_gate_tx_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderGateTx.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_gate_tx_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderGateTx.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderGateTx instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/hay21.c",
    "content": "#include \"hay21.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolHay21\"\n\nstatic const SubGhzBlockConst subghz_protocol_hay21_const = {\n    .te_short = 300,\n    .te_long = 700,\n    .te_delta = 150,\n    .min_count_bit_for_found = 21,\n};\n\nstruct SubGhzProtocolDecoderHay21 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderHay21 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Hay21DecoderStepReset = 0,\n    Hay21DecoderStepSaveDuration,\n    Hay21DecoderStepCheckDuration,\n} Hay21DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_hay21_decoder = {\n    .alloc = subghz_protocol_decoder_hay21_alloc,\n    .free = subghz_protocol_decoder_hay21_free,\n\n    .feed = subghz_protocol_decoder_hay21_feed,\n    .reset = subghz_protocol_decoder_hay21_reset,\n\n    .get_hash_data = subghz_protocol_decoder_hay21_get_hash_data,\n    .serialize = subghz_protocol_decoder_hay21_serialize,\n    .deserialize = subghz_protocol_decoder_hay21_deserialize,\n    .get_string = subghz_protocol_decoder_hay21_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_hay21_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_hay21 = {\n    .name = SUBGHZ_PROTOCOL_HAY21_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_hay21_decoder,\n    .encoder = &subghz_protocol_hay21_encoder,\n};\n\n/** \n * Analysis of received data and parsing serial number\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_hay21_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->btn = (instance->data >> 13) & 0xFF;\n    instance->serial = (instance->data >> 5) & 0xFF;\n    instance->cnt = (instance->data >> 1) & 0xF;\n\n    // Hay21 Decoder\n    // 09.2024 - @xMasterX (MMX)\n\n    // Key samples (inverted)\n    //              button   serial     CNT (goes lower since 0/1 are inverted)\n    //14A84A = 000 10100101 01000010 0101 0 (cnt 5)\n    //14A848 = 000 10100101 01000010 0100 0 (cnt 4)\n    //14A846 = 000 10100101 01000010 0011 0 (cnt 3)\n    //14A844 = 000 10100101 01000010 0010 0 (cnt 2)\n    //14A842 = 000 10100101 01000010 0001 0 (cnt 1)\n    //14A840 = 000 10100101 01000010 0000 0 (cnt 0)\n    //14A85E = 000 10100101 01000010 1111 0 (cnt F)\n    //14A85C = 000 10100101 01000010 1110 0 (cnt E)\n    //14A85A = 000 10100101 01000010 1101 0 (cnt D)\n    //14A858 = 000 10100101 01000010 1100 0 (cnt C)\n    //14A856 = 000 10100101 01000010 1011 0 (cnt B)\n    //          0xA5 (Labeled as On/Off on the remote board)\n    //          0x3C (Labeled as Mode on the remote board)\n    //          0x42 (Serial)\n    //                BTN   Serial   CNT\n    //078854 = 000 00111100 01000010 1010 0 (cnt A)\n    //078852 = 000 00111100 01000010 1001 0 (cnt 9)\n    //078850 = 000 00111100 01000010 1000 0 (cnt 8)\n    //07884E = 000 00111100 01000010 0111 0 (cnt 7)\n    // Inverted back\n    //1877B9 = 000 11000011 10111101 1100 1\n    //1877BB = 000 11000011 10111101 1101 1\n    //1877BD = 000 11000011 10111101 1110 1\n    //0B57BF = 000 01011010 10111101 1111 1\n}\n\nvoid* subghz_protocol_decoder_hay21_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHay21* instance = malloc(sizeof(SubGhzProtocolDecoderHay21));\n    instance->base.protocol = &subghz_protocol_hay21;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_hay21_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_hay21_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n    instance->decoder.parser_step = Hay21DecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_hay21_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case Hay21DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long * 6) <\n                        subghz_protocol_hay21_const.te_delta * 3)) {\n            //Found GAP\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = Hay21DecoderStepSaveDuration;\n        }\n        break;\n    case Hay21DecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = Hay21DecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = Hay21DecoderStepReset;\n        }\n        break;\n    case Hay21DecoderStepCheckDuration:\n        if(!level) {\n            // Bit 1 is long + short timing\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_long) <\n                subghz_protocol_hay21_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_short) <\n                subghz_protocol_hay21_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = Hay21DecoderStepSaveDuration;\n                // Bit 0 is short + long timing\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_short) <\n                 subghz_protocol_hay21_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long) <\n                 subghz_protocol_hay21_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = Hay21DecoderStepSaveDuration;\n            } else if(\n                // End of the key\n                DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long * 6) <\n                subghz_protocol_hay21_const.te_delta * 2) {\n                //Found next GAP and add bit 0 or 1\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_long) <\n                    subghz_protocol_hay21_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_short) <\n                    subghz_protocol_hay21_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                // If got 21 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_hay21_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = Hay21DecoderStepReset;\n            } else {\n                instance->decoder.parser_step = Hay21DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = Hay21DecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Get button name.\n * @param btn Button number, 4 bit\n */\nstatic const char* subghz_protocol_hay21_get_button_name(uint8_t btn) {\n    const char* btn_name;\n    switch(btn) {\n    case 0x5A:\n        btn_name = \"On/Off\";\n        break;\n    case 0xC3:\n        btn_name = \"Mode\";\n        break;\n    case 0x88:\n        btn_name = \"Hold\";\n        break;\n    default:\n        btn_name = \"Unknown\";\n        break;\n    }\n    return btn_name;\n}\n\nuint8_t subghz_protocol_decoder_hay21_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_hay21_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hay21_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_hay21_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_hay21_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHay21* instance = context;\n\n    // Parse serial, button, counter\n    subghz_protocol_hay21_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s - %dbit\\r\\n\"\n        \"Key: 0x%06lX\\r\\n\"\n        \"Serial: 0x%02X\\r\\n\"\n        \"Btn: 0x%01X - %s\\r\\n\"\n        \"Cnt: 0x%01X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        (uint8_t)(instance->generic.serial & 0xFF),\n        instance->generic.btn,\n        subghz_protocol_hay21_get_button_name(instance->generic.btn),\n        (uint8_t)(instance->generic.cnt & 0xF));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/hay21.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HAY21_NAME \"Hay21\"\n\ntypedef struct SubGhzProtocolDecoderHay21 SubGhzProtocolDecoderHay21;\ntypedef struct SubGhzProtocolEncoderHay21 SubGhzProtocolEncoderHay21;\n\nextern const SubGhzProtocolDecoder subghz_protocol_hay21_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_hay21_encoder;\nextern const SubGhzProtocol subghz_protocol_hay21;\n\n/**\n * Allocate SubGhzProtocolDecoderHay21.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHay21* pointer to a SubGhzProtocolDecoderHay21 instance\n */\nvoid* subghz_protocol_decoder_hay21_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHay21.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n */\nvoid subghz_protocol_decoder_hay21_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHay21.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n */\nvoid subghz_protocol_decoder_hay21_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_hay21_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_hay21_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHay21.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_hay21_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHay21.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hay21_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHay21 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_hay21_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/hollarm.c",
    "content": "#include \"hollarm.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolHollarm\"\n\nstatic const SubGhzBlockConst subghz_protocol_hollarm_const = {\n    .te_short = 200,\n    .te_long = 1000,\n    .te_delta = 200,\n    .min_count_bit_for_found = 42,\n};\n\nstruct SubGhzProtocolDecoderHollarm {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderHollarm {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    HollarmDecoderStepReset = 0,\n    HollarmDecoderStepSaveDuration,\n    HollarmDecoderStepCheckDuration,\n} HollarmDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_hollarm_decoder = {\n    .alloc = subghz_protocol_decoder_hollarm_alloc,\n    .free = subghz_protocol_decoder_hollarm_free,\n\n    .feed = subghz_protocol_decoder_hollarm_feed,\n    .reset = subghz_protocol_decoder_hollarm_reset,\n\n    .get_hash_data = subghz_protocol_decoder_hollarm_get_hash_data,\n    .serialize = subghz_protocol_decoder_hollarm_serialize,\n    .deserialize = subghz_protocol_decoder_hollarm_deserialize,\n    .get_string = subghz_protocol_decoder_hollarm_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_hollarm_encoder = {\n    .alloc = subghz_protocol_encoder_hollarm_alloc,\n    .free = subghz_protocol_encoder_hollarm_free,\n\n    .deserialize = subghz_protocol_encoder_hollarm_deserialize,\n    .stop = subghz_protocol_encoder_hollarm_stop,\n    .yield = subghz_protocol_encoder_hollarm_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_hollarm = {\n    .name = SUBGHZ_PROTOCOL_HOLLARM_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_hollarm_decoder,\n    .encoder = &subghz_protocol_hollarm_encoder,\n};\n\nvoid* subghz_protocol_encoder_hollarm_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderHollarm* instance = malloc(sizeof(SubGhzProtocolEncoderHollarm));\n\n    instance->base.protocol = &subghz_protocol_hollarm;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_hollarm_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHollarm* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderHollarm instance\n */\nstatic void subghz_protocol_encoder_hollarm_get_upload(SubGhzProtocolEncoderHollarm* instance) {\n    furi_assert(instance);\n\n    // Generate new key\n    uint64_t new_key = (instance->generic.data >> 12) << 12 | (instance->generic.btn << 8);\n\n    uint8_t bytesum = ((new_key >> 32) & 0xFF) + ((new_key >> 24) & 0xFF) +\n                      ((new_key >> 16) & 0xFF) + ((new_key >> 8) & 0xFF);\n\n    instance->generic.data = (new_key | bytesum);\n\n    size_t index = 0;\n\n    // Send key and GAP between parcels\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        // Read and prepare levels with 2 bit (was saved for better parsing) to the left offset to fit with the original remote transmission\n        if(bit_read((instance->generic.data << 2), i - 1)) {\n            // Send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_hollarm_const.te_short);\n            if(i == 1) {\n                //Send gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_hollarm_const.te_short * 12);\n            } else {\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_hollarm_const.te_short * 8);\n            }\n        } else {\n            // Send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_hollarm_const.te_short);\n            if(i == 1) {\n                //Send gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_hollarm_const.te_short * 12);\n            } else {\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_hollarm_const.te_long);\n            }\n        }\n    }\n\n    instance->encoder.size_upload = index;\n    return;\n}\n\n/** \n * Analysis of received data and parsing serial number\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_hollarm_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->btn = (instance->data >> 8) & 0xF;\n    instance->serial = (instance->data & 0xFFFFFFF0000) >> 16;\n\n    // Hollarm Decoder\n    // 09.2024 - @xMasterX (MMX)\n    // Thanks @Skorpionm for support!\n\n    // F0B93422FF = FF 8bit Sum\n    // F0B93421FE = FE 8bit Sum\n    // F0B9342401 = 01 8bit Sum\n    // F0B9342805 = 05 8bit Sum\n\n    // Serial (moved 2bit to right)    | Btn | 8b previous 4 bytes sum\n    // 00001111000010111001001101000010 0010  11111111 btn = (0x2)\n    // 00001111000010111001001101000010 0001  11111110 btn = (0x1)\n    // 00001111000010111001001101000010 0100  00000001 btn = (0x4)\n    // 00001111000010111001001101000010 1000  00000101 btn = (0x8)\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHollarm* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_hollarm_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_hollarm_remote_controller(&instance->generic);\n        subghz_protocol_encoder_hollarm_get_upload(instance);\n        instance->encoder.front = 0;\n\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            break;\n        }\n\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_hollarm_stop(void* context) {\n    SubGhzProtocolEncoderHollarm* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_hollarm_yield(void* context) {\n    SubGhzProtocolEncoderHollarm* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_hollarm_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHollarm* instance = malloc(sizeof(SubGhzProtocolDecoderHollarm));\n    instance->base.protocol = &subghz_protocol_hollarm;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_hollarm_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_hollarm_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n    instance->decoder.parser_step = HollarmDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_hollarm_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case HollarmDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 12) <\n                        subghz_protocol_hollarm_const.te_delta * 2)) {\n            //Found GAP between parcels\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = HollarmDecoderStepSaveDuration;\n        }\n        break;\n    case HollarmDecoderStepSaveDuration:\n        // Save HIGH level timing for next step\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = HollarmDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = HollarmDecoderStepReset;\n        }\n        break;\n    case HollarmDecoderStepCheckDuration:\n        if(!level) {\n            // Bit 0 is short 200us HIGH + long 1000us LOW timing\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hollarm_const.te_short) <\n                subghz_protocol_hollarm_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_long) <\n                subghz_protocol_hollarm_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = HollarmDecoderStepSaveDuration;\n                // Bit 1 is short 200us HIGH + short x8 = 1600us LOW timing\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hollarm_const.te_short) <\n                 subghz_protocol_hollarm_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 8) <\n                 subghz_protocol_hollarm_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = HollarmDecoderStepSaveDuration;\n            } else if(\n                // End of the key\n                DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 12) <\n                subghz_protocol_hollarm_const.te_delta) {\n                // When next GAP is found add bit 0 and do check for read finish\n                // (we have 42 high level pulses, last or first one may be a stop/start bit but we will parse it as zero)\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n\n                // If got 42 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_hollarm_const.min_count_bit_for_found) {\n                    // Saving with 2bit to the right offset for proper parsing\n                    instance->generic.data = (instance->decoder.decode_data >> 2);\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    uint8_t bytesum = ((instance->generic.data >> 32) & 0xFF) +\n                                      ((instance->generic.data >> 24) & 0xFF) +\n                                      ((instance->generic.data >> 16) & 0xFF) +\n                                      ((instance->generic.data >> 8) & 0xFF);\n\n                    if(bytesum != (instance->generic.data & 0xFF)) {\n                        // Check if the key is valid by verifying the sum\n                        instance->generic.data = 0;\n                        instance->generic.data_count_bit = 0;\n                        instance->decoder.decode_data = 0;\n                        instance->decoder.decode_count_bit = 0;\n                        instance->decoder.parser_step = HollarmDecoderStepReset;\n                        break;\n                    }\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = HollarmDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = HollarmDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = HollarmDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Get button name.\n * @param btn Button number, 4 bit\n */\nstatic const char* subghz_protocol_hollarm_get_button_name(uint8_t btn) {\n    const char* name_btn[16] = {\n        \"Unknown\",\n        \"Disarm\", // B (2)\n        \"Arm\", // A (1)\n        \"0x3\",\n        \"Ringtone/Alarm\", // C (3)\n        \"0x5\",\n        \"0x6\",\n        \"0x7\",\n        \"Ring\", // D (4)\n        \"Settings mode\",\n        \"Exit settings\",\n        \"Vibro sens. setting\",\n        \"Not used\\n(in settings)\",\n        \"Volume setting\",\n        \"0xE\",\n        \"0xF\"};\n    return btn <= 0xf ? name_btn[btn] : name_btn[0];\n}\n\nuint8_t subghz_protocol_decoder_hollarm_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_hollarm_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_hollarm_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_hollarm_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHollarm* instance = context;\n\n    // Parse serial\n    subghz_protocol_hollarm_remote_controller(&instance->generic);\n    // Get byte sum\n    uint8_t bytesum =\n        ((instance->generic.data >> 32) & 0xFF) + ((instance->generic.data >> 24) & 0xFF) +\n        ((instance->generic.data >> 16) & 0xFF) + ((instance->generic.data >> 8) & 0xFF);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%02lX%08lX\\r\\n\"\n        \"Serial: 0x%06lX  Sum: %02X\\r\\n\"\n        \"Btn: 0x%01X - %s\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->generic.serial,\n        bytesum,\n        instance->generic.btn,\n        subghz_protocol_hollarm_get_button_name(instance->generic.btn));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/hollarm.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HOLLARM_NAME \"Hollarm\"\n\ntypedef struct SubGhzProtocolDecoderHollarm SubGhzProtocolDecoderHollarm;\ntypedef struct SubGhzProtocolEncoderHollarm SubGhzProtocolEncoderHollarm;\n\nextern const SubGhzProtocolDecoder subghz_protocol_hollarm_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_hollarm_encoder;\nextern const SubGhzProtocol subghz_protocol_hollarm;\n\n/**\n * Allocate SubGhzProtocolEncoderHollarm.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderHollarm* pointer to a SubGhzProtocolEncoderHollarm instance\n */\nvoid* subghz_protocol_encoder_hollarm_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderHollarm.\n * @param context Pointer to a SubGhzProtocolEncoderHollarm instance\n */\nvoid subghz_protocol_encoder_hollarm_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderHollarm instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderHollarm instance\n */\nvoid subghz_protocol_encoder_hollarm_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderHollarm instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_hollarm_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderHollarm.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHollarm* pointer to a SubGhzProtocolDecoderHollarm instance\n */\nvoid* subghz_protocol_decoder_hollarm_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHollarm.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n */\nvoid subghz_protocol_decoder_hollarm_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHollarm.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n */\nvoid subghz_protocol_decoder_hollarm_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_hollarm_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_hollarm_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHollarm.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_hollarm_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHollarm.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHollarm instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_hollarm_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/holtek.c",
    "content": "#include \"holtek.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://pdf1.alldatasheet.com/datasheet-pdf/view/82103/HOLTEK/HT640.html\n * https://fccid.io/OJM-CMD-HHLR-XXXA\n *\n */\n\n#define TAG \"SubGhzProtocolHoltek\"\n\n#define HOLTEK_HEADER_MASK 0xF000000000\n#define HOLTEK_HEADER      0x5000000000\n\nstatic const SubGhzBlockConst subghz_protocol_holtek_const = {\n    .te_short = 430,\n    .te_long = 870,\n    .te_delta = 100,\n    .min_count_bit_for_found = 40,\n};\n\nstruct SubGhzProtocolDecoderHoltek {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderHoltek {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    HoltekDecoderStepReset = 0,\n    HoltekDecoderStepFoundStartBit,\n    HoltekDecoderStepSaveDuration,\n    HoltekDecoderStepCheckDuration,\n} HoltekDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_holtek_decoder = {\n    .alloc = subghz_protocol_decoder_holtek_alloc,\n    .free = subghz_protocol_decoder_holtek_free,\n\n    .feed = subghz_protocol_decoder_holtek_feed,\n    .reset = subghz_protocol_decoder_holtek_reset,\n\n    .get_hash_data = subghz_protocol_decoder_holtek_get_hash_data,\n    .serialize = subghz_protocol_decoder_holtek_serialize,\n    .deserialize = subghz_protocol_decoder_holtek_deserialize,\n    .get_string = subghz_protocol_decoder_holtek_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_holtek_encoder = {\n    .alloc = subghz_protocol_encoder_holtek_alloc,\n    .free = subghz_protocol_encoder_holtek_free,\n\n    .deserialize = subghz_protocol_encoder_holtek_deserialize,\n    .stop = subghz_protocol_encoder_holtek_stop,\n    .yield = subghz_protocol_encoder_holtek_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_holtek = {\n    .name = SUBGHZ_PROTOCOL_HOLTEK_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |\n            SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_holtek_decoder,\n    .encoder = &subghz_protocol_holtek_encoder,\n};\n\nvoid* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderHoltek* instance = malloc(sizeof(SubGhzProtocolEncoderHoltek));\n\n    instance->base.protocol = &subghz_protocol_holtek;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_holtek_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoltek* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderHoltek instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHoltek* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short * 36);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoltek* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_holtek_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_holtek_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_holtek_stop(void* context) {\n    SubGhzProtocolEncoderHoltek* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_holtek_yield(void* context) {\n    SubGhzProtocolEncoderHoltek* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHoltek* instance = malloc(sizeof(SubGhzProtocolDecoderHoltek));\n    instance->base.protocol = &subghz_protocol_holtek;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_holtek_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_holtek_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    instance->decoder.parser_step = HoltekDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case HoltekDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short * 36) <\n                        subghz_protocol_holtek_const.te_delta * 36)) {\n            //Found Preambula\n            instance->decoder.parser_step = HoltekDecoderStepFoundStartBit;\n        }\n        break;\n    case HoltekDecoderStepFoundStartBit:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) <\n                       subghz_protocol_holtek_const.te_delta)) {\n            //Found StartBit\n            instance->decoder.parser_step = HoltekDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = HoltekDecoderStepReset;\n        }\n        break;\n    case HoltekDecoderStepSaveDuration:\n        //save duration\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 +\n                            subghz_protocol_holtek_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_holtek_const.min_count_bit_for_found) {\n                    if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = HoltekDecoderStepFoundStartBit;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n\n                instance->decoder.parser_step = HoltekDecoderStepCheckDuration;\n            }\n        } else {\n            instance->decoder.parser_step = HoltekDecoderStepReset;\n        }\n        break;\n    case HoltekDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_short) <\n                subghz_protocol_holtek_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_long) <\n                subghz_protocol_holtek_const.te_delta * 2)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = HoltekDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_long) <\n                 subghz_protocol_holtek_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) <\n                 subghz_protocol_holtek_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = HoltekDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = HoltekDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = HoltekDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* instance) {\n    if((instance->data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {\n        instance->serial =\n            subghz_protocol_blocks_reverse_key((instance->data >> 16) & 0xFFFFF, 20);\n        uint16_t btn = instance->data & 0xFFFF;\n        if((btn & 0xf) != 0xA) {\n            instance->btn = 0x1 << 4 | (btn & 0xF);\n        } else if(((btn >> 4) & 0xF) != 0xA) {\n            instance->btn = 0x2 << 4 | ((btn >> 4) & 0xF);\n        } else if(((btn >> 8) & 0xF) != 0xA) {\n            instance->btn = 0x3 << 4 | ((btn >> 8) & 0xF);\n        } else if(((btn >> 12) & 0xF) != 0xA) {\n            instance->btn = 0x4 << 4 | ((btn >> 12) & 0xF);\n        } else {\n            instance->btn = 0;\n        }\n    } else {\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_holtek_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_holtek_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek* instance = context;\n    subghz_protocol_holtek_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%05lX Btn:%X \",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        instance->generic.btn >> 4);\n\n    if((instance->generic.btn & 0xF) == 0xE) {\n        furi_string_cat_printf(output, \"ON\\r\\n\");\n    } else if((instance->generic.btn & 0xF) == 0xB) {\n        furi_string_cat_printf(output, \"OFF\\r\\n\");\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/holtek.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HOLTEK_NAME \"Holtek\"\n\ntypedef struct SubGhzProtocolDecoderHoltek SubGhzProtocolDecoderHoltek;\ntypedef struct SubGhzProtocolEncoderHoltek SubGhzProtocolEncoderHoltek;\n\nextern const SubGhzProtocolDecoder subghz_protocol_holtek_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_holtek_encoder;\nextern const SubGhzProtocol subghz_protocol_holtek;\n\n/**\n * Allocate SubGhzProtocolEncoderHoltek.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderHoltek* pointer to a SubGhzProtocolEncoderHoltek instance\n */\nvoid* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderHoltek.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek instance\n */\nvoid subghz_protocol_encoder_holtek_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek instance\n */\nvoid subghz_protocol_encoder_holtek_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_holtek_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderHoltek.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHoltek* pointer to a SubGhzProtocolDecoderHoltek instance\n */\nvoid* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHoltek.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n */\nvoid subghz_protocol_decoder_holtek_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHoltek.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n */\nvoid subghz_protocol_decoder_holtek_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHoltek.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_holtek_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHoltek.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/holtek_ht12x.c",
    "content": "#include \"holtek_ht12x.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf\n *\n */\n\n#define TAG \"SubGhzProtocolHoltekHt12x\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c\"\n#define CNT_TO_DIP(dip)                                                                     \\\n    (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'),     \\\n        (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \\\n        (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1')\n\nstatic const SubGhzBlockConst subghz_protocol_holtek_th12x_const = {\n    .te_short = 320,\n    .te_long = 640,\n    .te_delta = 200,\n    .min_count_bit_for_found = 12,\n};\n\nstruct SubGhzProtocolDecoderHoltek_HT12X {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n    uint32_t last_data;\n};\n\nstruct SubGhzProtocolEncoderHoltek_HT12X {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n};\n\ntypedef enum {\n    Holtek_HT12XDecoderStepReset = 0,\n    Holtek_HT12XDecoderStepFoundStartBit,\n    Holtek_HT12XDecoderStepSaveDuration,\n    Holtek_HT12XDecoderStepCheckDuration,\n} Holtek_HT12XDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = {\n    .alloc = subghz_protocol_decoder_holtek_th12x_alloc,\n    .free = subghz_protocol_decoder_holtek_th12x_free,\n\n    .feed = subghz_protocol_decoder_holtek_th12x_feed,\n    .reset = subghz_protocol_decoder_holtek_th12x_reset,\n\n    .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data,\n    .serialize = subghz_protocol_decoder_holtek_th12x_serialize,\n    .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize,\n    .get_string = subghz_protocol_decoder_holtek_th12x_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = {\n    .alloc = subghz_protocol_encoder_holtek_th12x_alloc,\n    .free = subghz_protocol_encoder_holtek_th12x_free,\n\n    .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize,\n    .stop = subghz_protocol_encoder_holtek_th12x_stop,\n    .yield = subghz_protocol_encoder_holtek_th12x_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_holtek_th12x = {\n    .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_holtek_th12x_decoder,\n    .encoder = &subghz_protocol_holtek_th12x_encoder,\n};\n\nvoid* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderHoltek_HT12X* instance =\n        malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X));\n\n    instance->base.protocol = &subghz_protocol_holtek_th12x;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_holtek_th12x_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoltek_HT12X* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36);\n    //Send start bit\n    instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)instance->te * 2);\n            instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)instance->te * 2);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoltek_HT12X* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_holtek_th12x_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_holtek_th12x_stop(void* context) {\n    SubGhzProtocolEncoderHoltek_HT12X* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) {\n    SubGhzProtocolEncoderHoltek_HT12X* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHoltek_HT12X* instance =\n        malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X));\n    instance->base.protocol = &subghz_protocol_holtek_th12x;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_holtek_th12x_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_holtek_th12x_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case Holtek_HT12XDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 28) <\n                        subghz_protocol_holtek_th12x_const.te_delta * 20)) {\n            // 18720 us old max value\n            // 12960 us corrected max value\n            //Found Preambula\n            instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;\n        }\n        break;\n    case Holtek_HT12XDecoderStepFoundStartBit:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <\n                       subghz_protocol_holtek_th12x_const.te_delta)) {\n            //Found StartBit\n            instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->te = duration;\n        } else {\n            instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;\n        }\n        break;\n    case Holtek_HT12XDecoderStepSaveDuration:\n        //save duration\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 +\n                            subghz_protocol_holtek_th12x_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {\n                    if((instance->last_data == instance->decoder.decode_data) &&\n                       instance->last_data) {\n                        instance->te /= (instance->decoder.decode_count_bit * 3 + 1);\n\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->last_data = instance->decoder.decode_data;\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->te = 0;\n                instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->te += duration;\n                instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration;\n            }\n        } else {\n            instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;\n        }\n        break;\n    case Holtek_HT12XDecoderStepCheckDuration:\n        if(level) {\n            instance->te += duration;\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) <\n                subghz_protocol_holtek_th12x_const.te_delta * 2) &&\n               (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <\n                subghz_protocol_holtek_th12x_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) <\n                 subghz_protocol_holtek_th12x_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) <\n                 subghz_protocol_holtek_th12x_const.te_delta * 2)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->btn = instance->data & 0x0F;\n    instance->cnt = (instance->data >> 4) & 0xFF;\n}\n\nuint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_holtek_th12x_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"TE\", &instance->te, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add TE\");\n        ret = SubGhzProtocolStatusErrorParserTe;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_holtek_th12x_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nstatic void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) {\n    furi_string_cat_printf(\n        output,\n        \"%s%s%s%s\\r\\n\",\n        (((event >> 3) & 0x1) == 0x0 ? \"B1 \" : \"\"),\n        (((event >> 2) & 0x1) == 0x0 ? \"B2 \" : \"\"),\n        (((event >> 1) & 0x1) == 0x0 ? \"B3 \" : \"\"),\n        (((event >> 0) & 0x1) == 0x0 ? \"B4 \" : \"\"));\n}\n\nvoid subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoltek_HT12X* instance = context;\n    subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%03lX\\r\\n\"\n        \"Btn: \",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFF));\n    subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output);\n    furi_string_cat_printf(\n        output,\n        \"DIP:\" DIP_PATTERN \"\\r\\n\"\n        \"Te:%luus\\r\\n\",\n        CNT_TO_DIP(instance->generic.cnt),\n        instance->te);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/holtek_ht12x.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME \"Holtek_HT12X\"\n\ntypedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X;\ntypedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X;\n\nextern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder;\nextern const SubGhzProtocol subghz_protocol_holtek_th12x;\n\n/**\n * Allocate SubGhzProtocolEncoderHoltek_HT12X.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n */\nvoid* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderHoltek_HT12X.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n */\nvoid subghz_protocol_encoder_holtek_th12x_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n */\nvoid subghz_protocol_encoder_holtek_th12x_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderHoltek_HT12X.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n */\nvoid* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHoltek_HT12X.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n */\nvoid subghz_protocol_decoder_holtek_th12x_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHoltek_HT12X.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n */\nvoid subghz_protocol_decoder_holtek_th12x_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHoltek_HT12X.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_holtek_th12x_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHoltek_HT12X.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/honeywell_wdb.c",
    "content": "#include \"honeywell_wdb.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolHoneywellWdb\"\n\n/*\n * \n * https://github.com/klohner/honeywell-wireless-doorbell\n *\n */\n\nstatic const SubGhzBlockConst subghz_protocol_honeywell_wdb_const = {\n    .te_short = 160,\n    .te_long = 320,\n    .te_delta = 60,\n    .min_count_bit_for_found = 48,\n};\n\nstruct SubGhzProtocolDecoderHoneywell_WDB {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    const char* device_type;\n    const char* alert;\n    uint8_t secret_knock;\n    uint8_t relay;\n    uint8_t lowbat;\n};\n\nstruct SubGhzProtocolEncoderHoneywell_WDB {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Honeywell_WDBDecoderStepReset = 0,\n    Honeywell_WDBDecoderStepFoundStartBit,\n    Honeywell_WDBDecoderStepSaveDuration,\n    Honeywell_WDBDecoderStepCheckDuration,\n} Honeywell_WDBDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder = {\n    .alloc = subghz_protocol_decoder_honeywell_wdb_alloc,\n    .free = subghz_protocol_decoder_honeywell_wdb_free,\n\n    .feed = subghz_protocol_decoder_honeywell_wdb_feed,\n    .reset = subghz_protocol_decoder_honeywell_wdb_reset,\n\n    .get_hash_data = subghz_protocol_decoder_honeywell_wdb_get_hash_data,\n    .serialize = subghz_protocol_decoder_honeywell_wdb_serialize,\n    .deserialize = subghz_protocol_decoder_honeywell_wdb_deserialize,\n    .get_string = subghz_protocol_decoder_honeywell_wdb_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder = {\n    .alloc = subghz_protocol_encoder_honeywell_wdb_alloc,\n    .free = subghz_protocol_encoder_honeywell_wdb_free,\n\n    .deserialize = subghz_protocol_encoder_honeywell_wdb_deserialize,\n    .stop = subghz_protocol_encoder_honeywell_wdb_stop,\n    .yield = subghz_protocol_encoder_honeywell_wdb_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_honeywell_wdb = {\n    .name = SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_honeywell_wdb_decoder,\n    .encoder = &subghz_protocol_honeywell_wdb_encoder,\n};\n\nvoid* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderHoneywell_WDB* instance =\n        malloc(sizeof(SubGhzProtocolEncoderHoneywell_WDB));\n\n    instance->base.protocol = &subghz_protocol_honeywell_wdb;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_honeywell_wdb_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoneywell_WDB* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_honeywell_wdb_get_upload(\n    SubGhzProtocolEncoderHoneywell_WDB* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long);\n        }\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3);\n    return true;\n}\n\nSubGhzProtocolStatus subghz_protocol_encoder_honeywell_wdb_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHoneywell_WDB* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_honeywell_wdb_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_honeywell_wdb_stop(void* context) {\n    SubGhzProtocolEncoderHoneywell_WDB* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) {\n    SubGhzProtocolEncoderHoneywell_WDB* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHoneywell_WDB* instance =\n        malloc(sizeof(SubGhzProtocolDecoderHoneywell_WDB));\n    instance->base.protocol = &subghz_protocol_honeywell_wdb;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_honeywell_wdb_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_honeywell_wdb_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    switch(instance->decoder.parser_step) {\n    case Honeywell_WDBDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) <\n                        subghz_protocol_honeywell_wdb_const.te_delta)) {\n            //Found header Honeywell_WDB\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.decode_data = 0;\n            instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;\n        }\n        break;\n    case Honeywell_WDBDecoderStepSaveDuration:\n        if(level) { //save interval\n            if(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) <\n               subghz_protocol_honeywell_wdb_const.te_delta) {\n                if((instance->decoder.decode_count_bit ==\n                    subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) &&\n                   ((instance->decoder.decode_data & 0x01) ==\n                    subghz_protocol_blocks_get_parity(\n                        instance->decoder.decode_data >> 1,\n                        subghz_protocol_honeywell_wdb_const.min_count_bit_for_found - 1))) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = Honeywell_WDBDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;\n        }\n        break;\n    case Honeywell_WDBDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_short) <\n                subghz_protocol_honeywell_wdb_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_long) <\n                subghz_protocol_honeywell_wdb_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_long) <\n                 subghz_protocol_honeywell_wdb_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short) <\n                 subghz_protocol_honeywell_wdb_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzProtocolDecoderHoneywell_WDB* instance\n */\nstatic void subghz_protocol_honeywell_wdb_check_remote_controller(\n    SubGhzProtocolDecoderHoneywell_WDB* instance) {\n    /*\n *\n * Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series Wireless Chimes\n * 0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555\n * 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210\n * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to recognize signal)\n * XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter)\n * .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested)\n * .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor)\n * .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on receiver)\n * .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light pattern, 11 = full volume alarm)\n * .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x rapidly)\n * .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received transmission, only some models)\n * .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't observe any effects)\n * .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery alert)\n * .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits)\n * \n */\n\n    instance->generic.serial = (instance->generic.data >> 28) & 0xFFFFF;\n    switch((instance->generic.data >> 20) & 0x3) {\n    case 0x02:\n        instance->device_type = \"Doorbell\";\n        break;\n    case 0x01:\n        instance->device_type = \"PIR-Motion\";\n        break;\n    default:\n        instance->device_type = \"Unknown\";\n        break;\n    }\n\n    switch((instance->generic.data >> 16) & 0x3) {\n    case 0x00:\n        instance->alert = \"Normal\";\n        break;\n    case 0x01:\n    case 0x02:\n        instance->alert = \"High\";\n        break;\n    case 0x03:\n        instance->alert = \"Full\";\n        break;\n    default:\n        instance->alert = \"Unknown\";\n        break;\n    }\n\n    instance->secret_knock = (uint8_t)((instance->generic.data >> 4) & 0x1);\n    instance->relay = (uint8_t)((instance->generic.data >> 3) & 0x1);\n    instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1);\n}\n\nuint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_honeywell_wdb_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHoneywell_WDB* instance = context;\n    subghz_protocol_honeywell_wdb_check_remote_controller(instance);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%05lX\\r\\n\"\n        \"DT:%s  Al:%s\\r\\n\"\n        \"SK:%01X R:%01X LBat:%01X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        instance->device_type,\n        instance->alert,\n        instance->secret_knock,\n        instance->relay,\n        instance->lowbat);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/honeywell_wdb.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME \"Honeywell\"\n\ntypedef struct SubGhzProtocolDecoderHoneywell_WDB SubGhzProtocolDecoderHoneywell_WDB;\ntypedef struct SubGhzProtocolEncoderHoneywell_WDB SubGhzProtocolEncoderHoneywell_WDB;\n\nextern const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder;\nextern const SubGhzProtocol subghz_protocol_honeywell_wdb;\n\n/**\n * Allocate SubGhzProtocolEncoderHoneywell_WDB.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderHoneywell_WDB* pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n */\nvoid* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderHoneywell_WDB.\n * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n */\nvoid subghz_protocol_encoder_honeywell_wdb_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_honeywell_wdb_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n */\nvoid subghz_protocol_encoder_honeywell_wdb_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderHoneywell_WDB.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHoneywell_WDB* pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n */\nvoid* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHoneywell_WDB.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n */\nvoid subghz_protocol_decoder_honeywell_wdb_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHoneywell_WDB.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n */\nvoid subghz_protocol_decoder_honeywell_wdb_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHoneywell_WDB.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHoneywell_WDB.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_honeywell_wdb_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/hormann.c",
    "content": "#include \"hormann.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolHormannHsm\"\n\n#define HORMANN_HSM_PATTERN 0xFF000000003\n\nstatic const SubGhzBlockConst subghz_protocol_hormann_const = {\n    .te_short = 500,\n    .te_long = 1000,\n    .te_delta = 200,\n    .min_count_bit_for_found = 44,\n};\n\nstruct SubGhzProtocolDecoderHormann {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderHormann {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    HormannDecoderStepReset = 0,\n    HormannDecoderStepFoundStartHeader,\n    HormannDecoderStepFoundHeader,\n    HormannDecoderStepFoundStartBit,\n    HormannDecoderStepSaveDuration,\n    HormannDecoderStepCheckDuration,\n} HormannDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_hormann_decoder = {\n    .alloc = subghz_protocol_decoder_hormann_alloc,\n    .free = subghz_protocol_decoder_hormann_free,\n\n    .feed = subghz_protocol_decoder_hormann_feed,\n    .reset = subghz_protocol_decoder_hormann_reset,\n\n    .get_hash_data = subghz_protocol_decoder_hormann_get_hash_data,\n    .serialize = subghz_protocol_decoder_hormann_serialize,\n    .deserialize = subghz_protocol_decoder_hormann_deserialize,\n    .get_string = subghz_protocol_decoder_hormann_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_hormann_encoder = {\n    .alloc = subghz_protocol_encoder_hormann_alloc,\n    .free = subghz_protocol_encoder_hormann_free,\n\n    .deserialize = subghz_protocol_encoder_hormann_deserialize,\n    .stop = subghz_protocol_encoder_hormann_stop,\n    .yield = subghz_protocol_encoder_hormann_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_hormann = {\n    .name = SUBGHZ_PROTOCOL_HORMANN_HSM_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_hormann_decoder,\n    .encoder = &subghz_protocol_hormann_encoder,\n};\n\nvoid* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderHormann* instance = malloc(sizeof(SubGhzProtocolEncoderHormann));\n\n    instance->base.protocol = &subghz_protocol_hormann;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 2048;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_hormann_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHormann* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderHormann instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHormann* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    instance->encoder.repeat = 10; //original remote does 10 repeats\n\n    for(size_t repeat = 0; repeat < 20; repeat++) {\n        //Send start bit\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short);\n        //Send key data\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(bit_read(instance->generic.data, i - 1)) {\n                //send bit 1\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_long);\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short);\n            } else {\n                //send bit 0\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short);\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_long);\n            }\n        }\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24);\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderHormann* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_hormann_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_hormann_get_upload(instance)) {\n            instance->encoder.front = 0; // reset position before start\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_hormann_stop(void* context) {\n    SubGhzProtocolEncoderHormann* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_hormann_yield(void* context) {\n    SubGhzProtocolEncoderHormann* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderHormann* instance = malloc(sizeof(SubGhzProtocolDecoderHormann));\n    instance->base.protocol = &subghz_protocol_hormann;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_hormann_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    free(instance);\n}\n\nstatic bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) {\n    return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN;\n}\n\nvoid subghz_protocol_decoder_hormann_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    instance->decoder.parser_step = HormannDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case HormannDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) <\n                       subghz_protocol_hormann_const.te_delta * 24)) {\n            instance->decoder.parser_step = HormannDecoderStepFoundStartBit;\n        }\n        break;\n    case HormannDecoderStepFoundStartBit:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) <\n                        subghz_protocol_hormann_const.te_delta)) {\n            instance->decoder.parser_step = HormannDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = HormannDecoderStepReset;\n        }\n        break;\n    case HormannDecoderStepSaveDuration:\n        if(level) { //save interval\n            if(duration >= (subghz_protocol_hormann_const.te_short * 5) &&\n               subghz_protocol_decoder_hormann_check_pattern(instance)) {\n                instance->decoder.parser_step = HormannDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_hormann_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = HormannDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = HormannDecoderStepReset;\n        }\n        break;\n    case HormannDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_short) <\n                subghz_protocol_hormann_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_long) <\n                subghz_protocol_hormann_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = HormannDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_long) <\n                 subghz_protocol_hormann_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) <\n                 subghz_protocol_hormann_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = HormannDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = HormannDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = HormannDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->btn = (instance->data >> 8) & 0xF;\n}\n\nuint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_hormann_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_hormann_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderHormann* instance = context;\n    subghz_protocol_hormann_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s\\r\\n\"\n        \"%dbit\\r\\n\"\n        \"Key:0x%03lX%08lX\\r\\n\"\n        \"Btn:0x%01X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/hormann.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_HORMANN_HSM_NAME \"Hormann HSM\"\n\ntypedef struct SubGhzProtocolDecoderHormann SubGhzProtocolDecoderHormann;\ntypedef struct SubGhzProtocolEncoderHormann SubGhzProtocolEncoderHormann;\n\nextern const SubGhzProtocolDecoder subghz_protocol_hormann_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_hormann_encoder;\nextern const SubGhzProtocol subghz_protocol_hormann;\n\n/**\n * Allocate SubGhzProtocolEncoderHormann.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderHormann* pointer to a SubGhzProtocolEncoderHormann instance\n */\nvoid* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderHormann.\n * @param context Pointer to a SubGhzProtocolEncoderHormann instance\n */\nvoid subghz_protocol_encoder_hormann_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderHormann instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderHormann instance\n */\nvoid subghz_protocol_encoder_hormann_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderHormann instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_hormann_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderHormann.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderHormann* pointer to a SubGhzProtocolDecoderHormann instance\n */\nvoid* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderHormann.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n */\nvoid subghz_protocol_decoder_hormann_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderHormann.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n */\nvoid subghz_protocol_decoder_hormann_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderHormann.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_hormann_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderHormann.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderHormann instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/ido.c",
    "content": "#include \"ido.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolIdo117/111\"\n\nstatic const SubGhzBlockConst subghz_protocol_ido_const = {\n    .te_short = 450,\n    .te_long = 1450,\n    .te_delta = 150,\n    .min_count_bit_for_found = 48,\n};\n\nstruct SubGhzProtocolDecoderIDo {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderIDo {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    IDoDecoderStepReset = 0,\n    IDoDecoderStepFoundPreambula,\n    IDoDecoderStepSaveDuration,\n    IDoDecoderStepCheckDuration,\n} IDoDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_ido_decoder = {\n    .alloc = subghz_protocol_decoder_ido_alloc,\n    .free = subghz_protocol_decoder_ido_free,\n\n    .feed = subghz_protocol_decoder_ido_feed,\n    .reset = subghz_protocol_decoder_ido_reset,\n\n    .get_hash_data = subghz_protocol_decoder_ido_get_hash_data,\n    .deserialize = subghz_protocol_decoder_ido_deserialize,\n    .serialize = subghz_protocol_decoder_ido_serialize,\n    .get_string = subghz_protocol_decoder_ido_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_ido_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_ido = {\n    .name = SUBGHZ_PROTOCOL_IDO_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_ido_decoder,\n    .encoder = &subghz_protocol_ido_encoder,\n};\n\nvoid* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderIDo* instance = malloc(sizeof(SubGhzProtocolDecoderIDo));\n    instance->base.protocol = &subghz_protocol_ido;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_ido_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_ido_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n    instance->decoder.parser_step = IDoDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case IDoDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) <\n                       subghz_protocol_ido_const.te_delta * 5)) {\n            instance->decoder.parser_step = IDoDecoderStepFoundPreambula;\n        }\n        break;\n    case IDoDecoderStepFoundPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) <\n                        subghz_protocol_ido_const.te_delta * 5)) {\n            //Found Preambula\n            instance->decoder.parser_step = IDoDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = IDoDecoderStepReset;\n        }\n        break;\n    case IDoDecoderStepSaveDuration:\n        if(level) {\n            if(duration >= ((uint32_t)subghz_protocol_ido_const.te_short * 5 +\n                            subghz_protocol_ido_const.te_delta)) {\n                instance->decoder.parser_step = IDoDecoderStepFoundPreambula;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_ido_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = IDoDecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = IDoDecoderStepReset;\n        }\n        break;\n    case IDoDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) <\n                subghz_protocol_ido_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_ido_const.te_long) <\n                subghz_protocol_ido_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = IDoDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) <\n                 subghz_protocol_ido_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short) <\n                 subghz_protocol_ido_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = IDoDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = IDoDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = IDoDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* instance) {\n    uint64_t code_found_reverse =\n        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);\n    uint32_t code_fix = code_found_reverse & 0xFFFFFF;\n\n    instance->serial = code_fix & 0xFFFFF;\n    instance->btn = (code_fix >> 20) & 0x0F;\n}\n\nuint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_ido_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_ido_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_ido_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIDo* instance = context;\n\n    subghz_protocol_ido_check_remote_controller(&instance->generic);\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n    uint32_t code_fix = code_found_reverse & 0xFFFFFF;\n    uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Fix:%06lX \\r\\n\"\n        \"Hop:%06lX \\r\\n\"\n        \"Sn:%05lX Btn:%X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        code_fix,\n        code_hop,\n        instance->generic.serial,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/ido.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_IDO_NAME \"iDo 117/111\"\n\ntypedef struct SubGhzProtocolDecoderIDo SubGhzProtocolDecoderIDo;\ntypedef struct SubGhzProtocolEncoderIDo SubGhzProtocolEncoderIDo;\n\nextern const SubGhzProtocolDecoder subghz_protocol_ido_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_ido_encoder;\nextern const SubGhzProtocol subghz_protocol_ido;\n\n/**\n * Allocate SubGhzProtocolDecoderIDo.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderIDo* pointer to a SubGhzProtocolDecoderIDo instance\n */\nvoid* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderIDo.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n */\nvoid subghz_protocol_decoder_ido_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderIDo.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n */\nvoid subghz_protocol_decoder_ido_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_ido_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderIDo.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_ido_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderIDo.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderIDo instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_ido_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/intertechno_v3.c",
    "content": "#include \"intertechno_v3.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolIntertechnoV3\"\n\n#define CH_PATTERN \"%c%c%c%c\"\n#define CNT_TO_CH(ch) \\\n    (ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0')\n\n#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36\n\nstatic const SubGhzBlockConst subghz_protocol_intertechno_v3_const = {\n    .te_short = 275,\n    .te_long = 1375,\n    .te_delta = 150,\n    .min_count_bit_for_found = 32,\n};\n\nstruct SubGhzProtocolDecoderIntertechno_V3 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderIntertechno_V3 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    IntertechnoV3DecoderStepReset = 0,\n    IntertechnoV3DecoderStepStartSync,\n    IntertechnoV3DecoderStepFoundSync,\n    IntertechnoV3DecoderStepStartDuration,\n    IntertechnoV3DecoderStepSaveDuration,\n    IntertechnoV3DecoderStepCheckDuration,\n    IntertechnoV3DecoderStepEndDuration,\n} IntertechnoV3DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = {\n    .alloc = subghz_protocol_decoder_intertechno_v3_alloc,\n    .free = subghz_protocol_decoder_intertechno_v3_free,\n\n    .feed = subghz_protocol_decoder_intertechno_v3_feed,\n    .reset = subghz_protocol_decoder_intertechno_v3_reset,\n\n    .get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data,\n    .serialize = subghz_protocol_decoder_intertechno_v3_serialize,\n    .deserialize = subghz_protocol_decoder_intertechno_v3_deserialize,\n    .get_string = subghz_protocol_decoder_intertechno_v3_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = {\n    .alloc = subghz_protocol_encoder_intertechno_v3_alloc,\n    .free = subghz_protocol_encoder_intertechno_v3_free,\n\n    .deserialize = subghz_protocol_encoder_intertechno_v3_deserialize,\n    .stop = subghz_protocol_encoder_intertechno_v3_stop,\n    .yield = subghz_protocol_encoder_intertechno_v3_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_intertechno_v3 = {\n    .name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |\n            SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_intertechno_v3_decoder,\n    .encoder = &subghz_protocol_intertechno_v3_encoder,\n};\n\nvoid* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderIntertechno_V3* instance =\n        malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3));\n\n    instance->base.protocol = &subghz_protocol_intertechno_v3;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_intertechno_v3_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderIntertechno_V3* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_intertechno_v3_get_upload(\n    SubGhzProtocolEncoderIntertechno_V3* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38);\n    //Send sync\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) {\n            //send bit dimm\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n        } else if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long);\n        }\n    }\n    instance->encoder.size_upload = index;\n    return true;\n}\n\nSubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderIntertechno_V3* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if((instance->generic.data_count_bit !=\n            subghz_protocol_intertechno_v3_const.min_count_bit_for_found) &&\n           (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_intertechno_v3_stop(void* context) {\n    SubGhzProtocolEncoderIntertechno_V3* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) {\n    SubGhzProtocolEncoderIntertechno_V3* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderIntertechno_V3* instance =\n        malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3));\n    instance->base.protocol = &subghz_protocol_intertechno_v3;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_intertechno_v3_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_intertechno_v3_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    switch(instance->decoder.parser_step) {\n    case IntertechnoV3DecoderStepReset:\n        if((!level) &&\n           (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) <\n            subghz_protocol_intertechno_v3_const.te_delta * 15)) {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync;\n        }\n        break;\n    case IntertechnoV3DecoderStepStartSync:\n        if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                     subghz_protocol_intertechno_v3_const.te_delta)) {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n\n    case IntertechnoV3DecoderStepFoundSync:\n        if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) <\n                      subghz_protocol_intertechno_v3_const.te_delta * 3)) {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n\n    case IntertechnoV3DecoderStepStartDuration:\n        if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                     subghz_protocol_intertechno_v3_const.te_delta)) {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n\n    case IntertechnoV3DecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) {\n                instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync;\n                if((instance->decoder.decode_count_bit ==\n                    subghz_protocol_intertechno_v3_const.min_count_bit_for_found) ||\n                   (instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n    case IntertechnoV3DecoderStepCheckDuration:\n        if(level) {\n            //Add 0 bit\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) <\n                subghz_protocol_intertechno_v3_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                subghz_protocol_intertechno_v3_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;\n            } else if(\n                //Add 1 bit\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) <\n                 subghz_protocol_intertechno_v3_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                 subghz_protocol_intertechno_v3_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;\n\n            } else if(\n                //Add dimm_state\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) <\n                 subghz_protocol_intertechno_v3_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                 subghz_protocol_intertechno_v3_const.te_delta) &&\n                (instance->decoder.decode_count_bit == 27)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;\n\n            } else\n                instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n\n    case IntertechnoV3DecoderStepEndDuration:\n        if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <\n                       subghz_protocol_intertechno_v3_const.te_delta) ||\n                      (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) <\n                       subghz_protocol_intertechno_v3_const.te_delta * 2))) {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration;\n        } else {\n            instance->decoder.parser_step = IntertechnoV3DecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n *  A frame is either 32 or 36 bits:\n *     \n *               _\n *   start bit: | |__________ (T,10T)\n *          _   _\n *   '0':  | |_| |_____  (T,T,T,5T)\n *          _       _\n *   '1':  | |_____| |_  (T,5T,T,T)\n *          _   _\n *   dimm: | |_| |_     (T,T,T,T)\n * \n *              _\n *   stop bit: | |____...____ (T,38T)\n * \n *  if frame 32 bits\n *                     SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch  on/off  ~ch\n *  Key:0x3F86C59F  => 00111111100001101100010110   0       1     1111\n * \n *  if frame 36 bits\n *                     SSSSSSSSSSSSSSSSSSSSSSSSSS  all_ch dimm  ~ch   dimm_level\n *  Key:0x42D2E8856 => 01000010110100101110100010   0      X    0101  0110\n * \n */\n\n    if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) {\n        instance->serial = (instance->data >> 6) & 0x3FFFFFF;\n        if((instance->data >> 5) & 0x1) {\n            instance->cnt = 1 << 5;\n        } else {\n            instance->cnt = (~instance->data & 0xF);\n        }\n        instance->btn = (instance->data >> 4) & 0x1;\n    } else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {\n        instance->serial = (instance->data >> 10) & 0x3FFFFFF;\n        if((instance->data >> 9) & 0x1) {\n            instance->cnt = 1 << 5;\n        } else {\n            instance->cnt = (~(instance->data >> 4) & 0xF);\n        }\n        instance->btn = (instance->data) & 0xF;\n    } else {\n        instance->serial = 0;\n        instance->cnt = 0;\n        instance->btn = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if((instance->generic.data_count_bit !=\n            subghz_protocol_intertechno_v3_const.min_count_bit_for_found) &&\n           (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderIntertechno_V3* instance = context;\n\n    subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%.11s %db\\r\\n\"\n        \"Key:0x%08llX\\r\\n\"\n        \"Sn:%07lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        instance->generic.data,\n        instance->generic.serial);\n\n    if(instance->generic.data_count_bit ==\n       subghz_protocol_intertechno_v3_const.min_count_bit_for_found) {\n        if(instance->generic.cnt >> 5) {\n            furi_string_cat_printf(\n                output, \"Ch: All Btn:%s\\r\\n\", (instance->generic.btn ? \"On\" : \"Off\"));\n        } else {\n            furi_string_cat_printf(\n                output,\n                \"Ch:\" CH_PATTERN \" Btn:%s\\r\\n\",\n                CNT_TO_CH(instance->generic.cnt),\n                (instance->generic.btn ? \"On\" : \"Off\"));\n        }\n    } else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {\n        furi_string_cat_printf(\n            output,\n            \"Ch:\" CH_PATTERN \" Dimm:%d%%\\r\\n\",\n            CNT_TO_CH(instance->generic.cnt),\n            (int)(6.67f * (float)instance->generic.btn));\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/intertechno_v3.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME \"Intertechno_V3\"\n\ntypedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3;\ntypedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3;\n\nextern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder;\nextern const SubGhzProtocol subghz_protocol_intertechno_v3;\n\n/**\n * Allocate SubGhzProtocolEncoderIntertechno_V3.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n */\nvoid* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderIntertechno_V3.\n * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n */\nvoid subghz_protocol_encoder_intertechno_v3_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return Starus error\n */\nSubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n */\nvoid subghz_protocol_encoder_intertechno_v3_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderIntertechno_V3.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n */\nvoid* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderIntertechno_V3.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n */\nvoid subghz_protocol_decoder_intertechno_v3_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderIntertechno_V3.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n */\nvoid subghz_protocol_decoder_intertechno_v3_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderIntertechno_V3.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return Starus error\n */\nSubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderIntertechno_V3.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return Starus error\n */\nSubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/keeloq.c",
    "content": "#include \"keeloq.h\"\n#include \"keeloq_common.h\"\n\n#include \"../subghz_keystore.h\"\n#include <m-array.h>\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolKeeloq\"\n\nstatic const SubGhzBlockConst subghz_protocol_keeloq_const = {\n    .te_short = 400,\n    .te_long = 800,\n    .te_delta = 140,\n    .min_count_bit_for_found = 64,\n};\n\nstruct SubGhzProtocolDecoderKeeloq {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n    SubGhzKeystore* keystore;\n    const char* manufacture_name;\n};\n\nstruct SubGhzProtocolEncoderKeeloq {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    SubGhzKeystore* keystore;\n    const char* manufacture_name;\n};\n\ntypedef enum {\n    KeeloqDecoderStepReset = 0,\n    KeeloqDecoderStepCheckPreambula,\n    KeeloqDecoderStepSaveDuration,\n    KeeloqDecoderStepCheckDuration,\n} KeeloqDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_keeloq_decoder = {\n    .alloc = subghz_protocol_decoder_keeloq_alloc,\n    .free = subghz_protocol_decoder_keeloq_free,\n\n    .feed = subghz_protocol_decoder_keeloq_feed,\n    .reset = subghz_protocol_decoder_keeloq_reset,\n\n    .get_hash_data = subghz_protocol_decoder_keeloq_get_hash_data,\n    .serialize = subghz_protocol_decoder_keeloq_serialize,\n    .deserialize = subghz_protocol_decoder_keeloq_deserialize,\n    .get_string = subghz_protocol_decoder_keeloq_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_keeloq_encoder = {\n    .alloc = subghz_protocol_encoder_keeloq_alloc,\n    .free = subghz_protocol_encoder_keeloq_free,\n\n    .deserialize = subghz_protocol_encoder_keeloq_deserialize,\n    .stop = subghz_protocol_encoder_keeloq_stop,\n    .yield = subghz_protocol_encoder_keeloq_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_keeloq = {\n    .name = SUBGHZ_PROTOCOL_KEELOQ_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_keeloq_decoder,\n    .encoder = &subghz_protocol_keeloq_encoder,\n};\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param keystore Pointer to a SubGhzKeystore* instance\n * @param manufacture_name\n */\nstatic void subghz_protocol_keeloq_check_remote_controller(\n    SubGhzBlockGeneric* instance,\n    SubGhzKeystore* keystore,\n    const char** manufacture_name);\n\nvoid* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolEncoderKeeloq* instance = malloc(sizeof(SubGhzProtocolEncoderKeeloq));\n\n    instance->base.protocol = &subghz_protocol_keeloq;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->keystore = subghz_environment_get_keystore(environment);\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_keeloq_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderKeeloq* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/** \n * Key generation from simple data\n * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance\n * @param btn Button number, 4 bit\n */\nstatic bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) {\n    instance->generic.cnt++;\n    uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial;\n    uint32_t decrypt = (uint32_t)btn << 28 |\n                       (instance->generic.serial & 0x3FF)\n                           << 16 | // In some protocols the discriminator is 0\n                       instance->generic.cnt;\n    uint32_t hop = 0;\n    uint64_t man = 0;\n    int res = 0;\n\n    for\n        M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {\n            res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name);\n            if(res == 0) {\n                switch(manufacture_code->type) {\n                case KEELOQ_LEARNING_SIMPLE:\n                    //Simple Learning\n                    hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key);\n                    break;\n                case KEELOQ_LEARNING_NORMAL:\n                    //Simple Learning\n                    man =\n                        subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key);\n                    hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);\n                    break;\n                case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1:\n                    man = subghz_protocol_keeloq_common_magic_xor_type1_learning(\n                        instance->generic.serial, manufacture_code->key);\n                    hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);\n                    break;\n                case KEELOQ_LEARNING_UNKNOWN:\n                    //Invalid or missing encoding type in keeloq_mfcodes\n                    hop = 0;\n                    break;\n                }\n                break;\n            }\n        }\n    if(hop) {\n        uint64_t yek = (uint64_t)fix << 32 | hop;\n        instance->generic.data =\n            subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit);\n        return true;\n    } else {\n        instance->manufacture_name = \"Unknown\";\n        return false;\n    }\n}\n\nbool subghz_protocol_keeloq_create_data(\n    void* context,\n    FlipperFormat* flipper_format,\n    uint32_t serial,\n    uint8_t btn,\n    uint16_t cnt,\n    const char* manufacture_name,\n    SubGhzRadioPreset* preset) {\n    furi_check(context);\n    SubGhzProtocolEncoderKeeloq* instance = context;\n    instance->generic.serial = serial;\n    instance->generic.cnt = cnt;\n    instance->manufacture_name = manufacture_name;\n    instance->generic.data_count_bit = 64;\n    bool res = subghz_protocol_keeloq_gen_data(instance, btn);\n    if(res) {\n        if(subghz_block_generic_serialize(&instance->generic, flipper_format, preset) !=\n           SubGhzProtocolStatusOk)\n            res = false;\n    }\n    return res;\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) {\n    furi_assert(instance);\n\n    //gen new key\n    if(!subghz_protocol_keeloq_gen_data(instance, btn)) {\n        return false;\n    }\n\n    size_t index = 0;\n    size_t size_upload = 11 * 2 + 2 + (instance->generic.data_count_bit * 2) + 4;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    for(uint8_t i = 11; i > 0; i--) {\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short);\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 10);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short);\n        }\n    }\n    // +send 2 status bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long);\n    // send end\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 40);\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderKeeloq* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_keeloq_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        subghz_protocol_keeloq_check_remote_controller(\n            &instance->generic, instance->keystore, &instance->manufacture_name);\n\n        if(strcmp(instance->manufacture_name, \"DoorHan\") != 0) {\n            FURI_LOG_E(TAG, \"Wrong manufacturer name\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            ret = SubGhzProtocolStatusErrorParserKey;\n            break;\n        }\n        instance->encoder.front = 0; // reset before start\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_keeloq_stop(void* context) {\n    SubGhzProtocolEncoderKeeloq* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_keeloq_yield(void* context) {\n    SubGhzProtocolEncoderKeeloq* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderKeeloq* instance = malloc(sizeof(SubGhzProtocolDecoderKeeloq));\n    instance->base.protocol = &subghz_protocol_keeloq;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->keystore = subghz_environment_get_keystore(environment);\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_keeloq_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_keeloq_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n    instance->decoder.parser_step = KeeloqDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case KeeloqDecoderStepReset:\n        if((level) && DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) <\n                          subghz_protocol_keeloq_const.te_delta) {\n            instance->decoder.parser_step = KeeloqDecoderStepCheckPreambula;\n            instance->header_count++;\n        }\n        break;\n    case KeeloqDecoderStepCheckPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) <\n                        subghz_protocol_keeloq_const.te_delta)) {\n            instance->decoder.parser_step = KeeloqDecoderStepReset;\n            break;\n        }\n        if((instance->header_count > 2) &&\n           (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short * 10) <\n            subghz_protocol_keeloq_const.te_delta * 10)) {\n            // Found header\n            instance->decoder.parser_step = KeeloqDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = KeeloqDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    case KeeloqDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = KeeloqDecoderStepCheckDuration;\n        }\n        break;\n    case KeeloqDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_keeloq_const.te_short * 2 +\n                            subghz_protocol_keeloq_const.te_delta)) {\n                // Found end TX\n                instance->decoder.parser_step = KeeloqDecoderStepReset;\n                if((instance->decoder.decode_count_bit >=\n                    subghz_protocol_keeloq_const.min_count_bit_for_found) &&\n                   (instance->decoder.decode_count_bit <=\n                    subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) {\n                    if(instance->generic.data != instance->decoder.decode_data) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit =\n                            subghz_protocol_keeloq_const.min_count_bit_for_found;\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->decoder.decode_data = 0;\n                    instance->decoder.decode_count_bit = 0;\n                    instance->header_count = 0;\n                }\n                break;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_short) <\n                 subghz_protocol_keeloq_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_long) <\n                 subghz_protocol_keeloq_const.te_delta * 2)) {\n                if(instance->decoder.decode_count_bit <\n                   subghz_protocol_keeloq_const.min_count_bit_for_found) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else {\n                    instance->decoder.decode_count_bit++;\n                }\n                instance->decoder.parser_step = KeeloqDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_long) <\n                 subghz_protocol_keeloq_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) <\n                 subghz_protocol_keeloq_const.te_delta)) {\n                if(instance->decoder.decode_count_bit <\n                   subghz_protocol_keeloq_const.min_count_bit_for_found) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else {\n                    instance->decoder.decode_count_bit++;\n                }\n                instance->decoder.parser_step = KeeloqDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = KeeloqDecoderStepReset;\n                instance->header_count = 0;\n            }\n        } else {\n            instance->decoder.parser_step = KeeloqDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    }\n}\n\n/**\n * Validation of decrypt data.\n * @param instance Pointer to a SubGhzBlockGeneric instance\n * @param decrypt Decrypd data\n * @param btn Button number, 4 bit\n * @param end_serial decrement the last 10 bits of the serial number\n * @return true On success\n */\nstatic inline bool subghz_protocol_keeloq_check_decrypt(\n    SubGhzBlockGeneric* instance,\n    uint32_t decrypt,\n    uint8_t btn,\n    uint32_t end_serial) {\n    furi_assert(instance);\n    if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) ||\n                                  ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) {\n        instance->cnt = decrypt & 0x0000FFFF;\n        return true;\n    }\n    return false;\n}\n// Centurion specific check\nstatic inline bool subghz_protocol_keeloq_check_decrypt_centurion(\n    SubGhzBlockGeneric* instance,\n    uint32_t decrypt,\n    uint8_t btn) {\n    furi_assert(instance);\n\n    if((decrypt >> 28 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x3FF) == 0x1CE)) {\n        instance->cnt = decrypt & 0x0000FFFF;\n        return true;\n    }\n    return false;\n}\n\n/** \n * Checking the accepted code against the database manafacture key\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param fix Fix part of the parcel\n * @param hop Hop encrypted part of the parcel\n * @param keystore Pointer to a SubGhzKeystore* instance\n * @param manufacture_name \n * @return true on successful search\n */\nstatic uint8_t subghz_protocol_keeloq_check_remote_controller_selector(\n    SubGhzBlockGeneric* instance,\n    uint32_t fix,\n    uint32_t hop,\n    SubGhzKeystore* keystore,\n    const char** manufacture_name) {\n    // protocol HCS300 uses 10 bits in discriminator, HCS200 uses 8 bits, for backward compatibility, we are looking for the 8-bit pattern\n    // HCS300 -> uint16_t end_serial = (uint16_t)(fix & 0x3FF);\n    // HCS200 -> uint16_t end_serial = (uint16_t)(fix & 0xFF);\n\n    uint16_t end_serial = (uint16_t)(fix & 0xFF);\n    uint8_t btn = (uint8_t)(fix >> 28);\n    uint32_t decrypt = 0;\n    uint64_t man;\n    uint32_t seed = 0;\n\n    for\n        M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {\n            switch(manufacture_code->type) {\n            case KEELOQ_LEARNING_SIMPLE:\n                // Simple Learning\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_NORMAL:\n                // Normal Learning\n                // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37\n                man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(strcmp(furi_string_get_cstr(manufacture_code->name), \"Centurion\") == 0) {\n                    if(subghz_protocol_keeloq_check_decrypt_centurion(instance, decrypt, btn)) {\n                        *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                        return 1;\n                    }\n                } else {\n                    if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                        *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                        return 1;\n                    }\n                }\n                break;\n            case KEELOQ_LEARNING_SECURE:\n                man = subghz_protocol_keeloq_common_secure_learning(\n                    fix, seed, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1:\n                man = subghz_protocol_keeloq_common_magic_xor_type1_learning(\n                    fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1:\n                man = subghz_protocol_keeloq_common_magic_serial_type1_learning(\n                    fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2:\n                man = subghz_protocol_keeloq_common_magic_serial_type2_learning(\n                    fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3:\n                man = subghz_protocol_keeloq_common_magic_serial_type3_learning(\n                    fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_UNKNOWN:\n                // Simple Learning\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Check for mirrored man\n                uint64_t man_rev = 0;\n                uint64_t man_rev_byte = 0;\n                for(uint8_t i = 0; i < 64; i += 8) {\n                    man_rev_byte = (uint8_t)(manufacture_code->key >> i);\n                    man_rev = man_rev | man_rev_byte << (56 - i);\n                }\n\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                //###########################\n                // Normal Learning\n                // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37\n                man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Check for mirrored man\n                man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Secure Learning\n                man = subghz_protocol_keeloq_common_secure_learning(\n                    fix, seed, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Check for mirrored man\n                man = subghz_protocol_keeloq_common_secure_learning(fix, seed, man_rev);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Magic xor type1 learning\n                man = subghz_protocol_keeloq_common_magic_xor_type1_learning(\n                    fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n\n                // Check for mirrored man\n                man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);\n                if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            }\n        }\n\n    *manufacture_name = \"Unknown\";\n    instance->cnt = 0;\n\n    return 0;\n}\n\nstatic void subghz_protocol_keeloq_check_remote_controller(\n    SubGhzBlockGeneric* instance,\n    SubGhzKeystore* keystore,\n    const char** manufacture_name) {\n    uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);\n    uint32_t key_fix = key >> 32;\n    uint32_t key_hop = key & 0x00000000ffffffff;\n    // Check key AN-Motors\n    if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) &&\n       (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) {\n        *manufacture_name = \"AN-Motors\";\n        instance->cnt = key_hop >> 16;\n    } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) {\n        *manufacture_name = \"HCS101\";\n        instance->cnt = key_hop >> 16;\n    } else {\n        subghz_protocol_keeloq_check_remote_controller_selector(\n            instance, key_fix, key_hop, keystore, manufacture_name);\n    }\n\n    instance->serial = key_fix & 0x0FFFFFFF;\n    instance->btn = key_fix >> 28;\n}\n\nuint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_keeloq_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n    subghz_protocol_keeloq_check_remote_controller(\n        &instance->generic, instance->keystore, &instance->manufacture_name);\n\n    SubGhzProtocolStatus res =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n\n    if((res == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_string_cstr(\n           flipper_format, \"Manufacture\", instance->manufacture_name)) {\n        FURI_LOG_E(TAG, \"Unable to add manufacture name\");\n        res = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return res;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_keeloq_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKeeloq* instance = context;\n    subghz_protocol_keeloq_check_remote_controller(\n        &instance->generic, instance->keystore, &instance->manufacture_name);\n\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n    uint32_t code_found_reverse_hi = code_found_reverse >> 32;\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%08lX%08lX\\r\\n\"\n        \"Fix:0x%08lX    Cnt:%04lX\\r\\n\"\n        \"Hop:0x%08lX    Btn:%01X\\r\\n\"\n        \"MF:%s\\r\\n\"\n        \"Sn:0x%07lX \\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        code_found_reverse_hi,\n        instance->generic.cnt,\n        code_found_reverse_lo,\n        instance->generic.btn,\n        instance->manufacture_name,\n        instance->generic.serial);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/keeloq.h",
    "content": "#pragma once\n\n#include \"base.h\"\n#include \"public_api.h\"\n\n#define SUBGHZ_PROTOCOL_KEELOQ_NAME \"KeeLoq\"\n\ntypedef struct SubGhzProtocolDecoderKeeloq SubGhzProtocolDecoderKeeloq;\ntypedef struct SubGhzProtocolEncoderKeeloq SubGhzProtocolEncoderKeeloq;\n\nextern const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder;\nextern const SubGhzProtocol subghz_protocol_keeloq;\n\n/**\n * Allocate SubGhzProtocolEncoderKeeloq.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance\n */\nvoid* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderKeeloq.\n * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance\n */\nvoid subghz_protocol_encoder_keeloq_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance\n */\nvoid subghz_protocol_encoder_keeloq_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_keeloq_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderKeeloq.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderKeeloq* pointer to a SubGhzProtocolDecoderKeeloq instance\n */\nvoid* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderKeeloq.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n */\nvoid subghz_protocol_decoder_keeloq_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderKeeloq.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n */\nvoid subghz_protocol_decoder_keeloq_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderKeeloq.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus subghz_protocol_decoder_keeloq_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderKeeloq.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return SubGhzProtocolStatus\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/keeloq_common.c",
    "content": "#include \"keeloq_common.h\"\n\n#include <furi.h>\n\n#include <m-array.h>\n\n#define bit(x, n) (((x) >> (n)) & 1)\n#define g5(x, a, b, c, d, e) \\\n    (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16)\n\n/** Simple Learning Encrypt\n * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter\n * @param key - manufacture (64bit)\n * @return keeloq encrypt data\n */\ninline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) {\n    uint32_t x = data, r;\n    for(r = 0; r < 528; r++)\n        x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^\n                         bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31)))\n                        << 31);\n    return x;\n}\n\n/** Simple Learning Decrypt\n * @param data - keeloq encrypt data\n * @param key - manufacture (64bit)\n * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter\n */\ninline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) {\n    uint32_t x = data, r;\n    for(r = 0; r < 528; r++)\n        x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^\n            bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30));\n    return x;\n}\n\n/** Normal Learning\n * @param data - serial number (28bit)\n * @param key - manufacture (64bit)\n * @return manufacture for this serial number (64bit)\n */\ninline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) {\n    uint32_t k1, k2;\n\n    data &= 0x0FFFFFFF;\n    data |= 0x20000000;\n    k1 = subghz_protocol_keeloq_common_decrypt(data, key);\n\n    data &= 0x0FFFFFFF;\n    data |= 0x60000000;\n    k2 = subghz_protocol_keeloq_common_decrypt(data, key);\n\n    return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya\n}\n\n/** Secure Learning\n * @param data - serial number (28bit)\n * @param seed - seed number (32bit)\n * @param key - manufacture (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\ninline uint64_t subghz_protocol_keeloq_common_secure_learning(\n    uint32_t data,\n    uint32_t seed,\n    const uint64_t key) {\n    uint32_t k1, k2;\n\n    data &= 0x0FFFFFFF;\n    k1 = subghz_protocol_keeloq_common_decrypt(data, key);\n    k2 = subghz_protocol_keeloq_common_decrypt(seed, key);\n\n    return ((uint64_t)k1 << 32) | k2;\n}\n\n/** Magic_xor_type1 Learning\n * @param data - serial number (28bit)\n * @param xor - magic xor (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\ninline uint64_t\n    subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) {\n    data &= 0x0FFFFFFF;\n    return (((uint64_t)data << 32) | data) ^ xor;\n}\n\n/** Magic_serial_type1 Learning\n * @param data - serial number (28bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\ninline uint64_t\n    subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {\n    return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |\n           ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);\n}\n\n/** Magic_serial_type2 Learning\n * @param data - btn+serial number (32bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\ninline uint64_t\n    subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {\n    uint8_t* p = (uint8_t*)&data;\n    uint8_t* m = (uint8_t*)&man;\n    m[7] = p[0];\n    m[6] = p[1];\n    m[5] = p[2];\n    m[4] = p[3];\n    return man;\n}\n\n/** Magic_serial_type3 Learning\n * @param data - serial number (24bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\ninline uint64_t\n    subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {\n    return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/keeloq_common.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#include <furi.h>\n\n/*\n * Keeloq\n * https://ru.wikipedia.org/wiki/KeeLoq\n * https://phreakerclub.com/forum/showthread.php?t=1094\n *\n */\n#define KEELOQ_NLF 0x3A5C742E\n\n/*\n * KeeLoq learning types\n * https://phreakerclub.com/forum/showthread.php?t=67\n */\n#define KEELOQ_LEARNING_UNKNOWN             0u\n#define KEELOQ_LEARNING_SIMPLE              1u\n#define KEELOQ_LEARNING_NORMAL              2u\n#define KEELOQ_LEARNING_SECURE              3u\n#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1    4u\n#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 5u\n#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 6u\n#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 7u\n\n/**\n * Simple Learning Encrypt\n * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter\n * @param key - manufacture (64bit)\n * @return keeloq encrypt data\n */\nuint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key);\n\n/** \n * Simple Learning Decrypt\n * @param data - keeloq encrypt data\n * @param key - manufacture (64bit)\n * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter\n */\nuint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key);\n\n/** \n * Normal Learning\n * @param data - serial number (28bit)\n * @param key - manufacture (64bit)\n * @return manufacture for this serial number (64bit)\n */\nuint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key);\n\n/** \n * Secure Learning\n * @param data - serial number (28bit)\n * @param seed - seed number (32bit)\n * @param key - manufacture (64bit)\n * @return manufacture for this serial number (64bit)\n */\nuint64_t\n    subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key);\n\n/** \n * Magic_xor_type1 Learning\n * @param data - serial number (28bit)\n * @param xor - magic xor (64bit)\n * @return manufacture for this serial number (64bit)\n */\nuint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor);\n\n/** Magic_serial_type1 Learning\n * @param data - serial number (28bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\nuint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man);\n\n/** Magic_serial_type2 Learning\n * @param data - btn+serial number (32bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\nuint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man);\n\n/** Magic_serial_type3 Learning\n * @param data - btn+serial number (32bit)\n * @param man - magic man (64bit)\n * @return manufacture for this serial number (64bit)\n */\n\nuint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);\n"
  },
  {
    "path": "lib/subghz/protocols/kia.c",
    "content": "#include \"kia.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocoKia\"\n\nstatic const SubGhzBlockConst subghz_protocol_kia_const = {\n    .te_short = 250,\n    .te_long = 500,\n    .te_delta = 100,\n    .min_count_bit_for_found = 61,\n};\n\nstruct SubGhzProtocolDecoderKIA {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderKIA {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    KIADecoderStepReset = 0,\n    KIADecoderStepCheckPreambula,\n    KIADecoderStepSaveDuration,\n    KIADecoderStepCheckDuration,\n} KIADecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_kia_decoder = {\n    .alloc = subghz_protocol_decoder_kia_alloc,\n    .free = subghz_protocol_decoder_kia_free,\n\n    .feed = subghz_protocol_decoder_kia_feed,\n    .reset = subghz_protocol_decoder_kia_reset,\n\n    .get_hash_data = subghz_protocol_decoder_kia_get_hash_data,\n    .serialize = subghz_protocol_decoder_kia_serialize,\n    .deserialize = subghz_protocol_decoder_kia_deserialize,\n    .get_string = subghz_protocol_decoder_kia_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_kia_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_kia = {\n    .name = SUBGHZ_PROTOCOL_KIA_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_kia_decoder,\n    .encoder = &subghz_protocol_kia_encoder,\n};\n\nvoid* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA));\n    instance->base.protocol = &subghz_protocol_kia;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_kia_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_kia_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n    instance->decoder.parser_step = KIADecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case KIADecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <\n                       subghz_protocol_kia_const.te_delta)) {\n            instance->decoder.parser_step = KIADecoderStepCheckPreambula;\n            instance->decoder.te_last = duration;\n            instance->header_count = 0;\n        }\n        break;\n    case KIADecoderStepCheckPreambula:\n        if(level) {\n            if((DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <\n                subghz_protocol_kia_const.te_delta) ||\n               (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <\n                subghz_protocol_kia_const.te_delta)) {\n                instance->decoder.te_last = duration;\n            } else {\n                instance->decoder.parser_step = KIADecoderStepReset;\n            }\n        } else if(\n            (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <\n             subghz_protocol_kia_const.te_delta) &&\n            (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) <\n             subghz_protocol_kia_const.te_delta)) {\n            // Found header\n            instance->header_count++;\n            break;\n        } else if(\n            (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <\n             subghz_protocol_kia_const.te_delta) &&\n            (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) <\n             subghz_protocol_kia_const.te_delta)) {\n            // Found start bit\n            if(instance->header_count > 15) {\n                instance->decoder.parser_step = KIADecoderStepSaveDuration;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 1;\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n            } else {\n                instance->decoder.parser_step = KIADecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = KIADecoderStepReset;\n        }\n        break;\n    case KIADecoderStepSaveDuration:\n        if(level) {\n            if(duration >=\n               (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) {\n                //Found stop bit\n                instance->decoder.parser_step = KIADecoderStepReset;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_kia_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = KIADecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = KIADecoderStepReset;\n        }\n        break;\n    case KIADecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) <\n                subghz_protocol_kia_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <\n                subghz_protocol_kia_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = KIADecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) <\n                 subghz_protocol_kia_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <\n                 subghz_protocol_kia_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = KIADecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = KIADecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = KIADecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_kia_crc8(uint8_t* data, size_t len) {\n    uint8_t crc = 0x08;\n    size_t i, j;\n    for(i = 0; i < len; i++) {\n        crc ^= data[i];\n        for(j = 0; j < 8; j++) {\n            if((crc & 0x80) != 0)\n                crc = (uint8_t)((crc << 1) ^ 0x7F);\n            else\n                crc <<= 1;\n        }\n    }\n    return crc;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n    *   0x0F 0112 43B04EC 1 7D\n    *   0x0F 0113 43B04EC 1 DF\n    *   0x0F 0114 43B04EC 1 30\n    *   0x0F 0115 43B04EC 2 13\n    *   0x0F 0116 43B04EC 3 F5\n    *         CNT  Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x08)\n    */\n\n    instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF);\n    instance->btn = (instance->data >> 8) & 0x0F;\n    instance->cnt = (instance->data >> 40) & 0xFFFF;\n}\n\nuint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_kia_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_kia_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKIA* instance = context;\n\n    subghz_protocol_kia_check_remote_controller(&instance->generic);\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%08lX%08lX\\r\\n\"\n        \"Sn:%07lX Btn:%X Cnt:%04lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/kia.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_KIA_NAME \"KIA Seed\"\n\ntypedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA;\ntypedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA;\n\nextern const SubGhzProtocolDecoder subghz_protocol_kia_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_kia_encoder;\nextern const SubGhzProtocol subghz_protocol_kia;\n\n/**\n * Allocate SubGhzProtocolDecoderKIA.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderKIA* pointer to a SubGhzProtocolDecoderKIA instance\n */\nvoid* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderKIA.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n */\nvoid subghz_protocol_decoder_kia_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderKIA.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n */\nvoid subghz_protocol_decoder_kia_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_kia_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderKIA.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_kia_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderKIA.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderKIA instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_kia_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/kinggates_stylo_4k.c",
    "content": "#include \"kinggates_stylo_4k.h\"\n#include \"keeloq_common.h\"\n\n#include \"../subghz_keystore.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocoKingGatesStylo4k\"\n\nstatic const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = {\n    .te_short = 400,\n    .te_long = 1100,\n    .te_delta = 140,\n    .min_count_bit_for_found = 89,\n};\n\nstruct SubGhzProtocolDecoderKingGates_stylo_4k {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint64_t data;\n    uint16_t header_count;\n    SubGhzKeystore* keystore;\n};\n\nstruct SubGhzProtocolEncoderKingGates_stylo_4k {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    KingGates_stylo_4kDecoderStepReset = 0,\n    KingGates_stylo_4kDecoderStepCheckPreambula,\n    KingGates_stylo_4kDecoderStepCheckStartBit,\n    KingGates_stylo_4kDecoderStepSaveDuration,\n    KingGates_stylo_4kDecoderStepCheckDuration,\n} KingGates_stylo_4kDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = {\n    .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc,\n    .free = subghz_protocol_decoder_kinggates_stylo_4k_free,\n\n    .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed,\n    .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset,\n\n    .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,\n    .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize,\n    .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize,\n    .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_kinggates_stylo_4k = {\n    .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_kinggates_stylo_4k_decoder,\n    .encoder = &subghz_protocol_kinggates_stylo_4k_encoder,\n};\n\nvoid* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance =\n        malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k));\n    instance->base.protocol = &subghz_protocol_kinggates_stylo_4k;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->keystore = subghz_environment_get_keystore(environment);\n    return instance;\n}\n\nvoid subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case KingGates_stylo_4kDecoderStepReset:\n        if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <\n                          subghz_protocol_kinggates_stylo_4k_const.te_delta) {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula;\n            instance->header_count++;\n        }\n        break;\n    case KingGates_stylo_4kDecoderStepCheckPreambula:\n        if((!level) &&\n           (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <\n            subghz_protocol_kinggates_stylo_4k_const.te_delta)) {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n            break;\n        }\n        if((instance->header_count > 2) &&\n           (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) <\n            subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) {\n            // Found header\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit;\n        } else {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    case KingGates_stylo_4kDecoderStepCheckStartBit:\n        if((level) &&\n           DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) <\n               subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->header_count = 0;\n        }\n        break;\n    case KingGates_stylo_4kDecoderStepSaveDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->data;\n                    instance->data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n\n                instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n                instance->decoder.decode_data = 0;\n                instance->data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->header_count = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration;\n            }\n        } else {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    case KingGates_stylo_4kDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) <\n                subghz_protocol_kinggates_stylo_4k_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) <\n                subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) <\n                 subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <\n                 subghz_protocol_kinggates_stylo_4k_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n                instance->header_count = 0;\n            }\n            if(instance->decoder.decode_count_bit == 53) {\n                instance->data = instance->decoder.decode_data;\n                instance->decoder.decode_data = 0;\n            }\n        } else {\n            instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;\n            instance->header_count = 0;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param file_name Full path to rainbow table the file\n */\nstatic void subghz_protocol_kinggates_stylo_4k_remote_controller(\n    SubGhzBlockGeneric* instance,\n    uint64_t data,\n    SubGhzKeystore* keystore) {\n    /**\n *  9500us   12*(400/400)  2200/800|1-bit|0-bit|\n *           _   _       _       __   ___     _ \n *  ________| |_| |_..._| |_____|  |_|   |___| |.....\n * \n *  1-bit 400/1100 us\n *  0-bit 1100/400 us\n *  \n *  The package consists of 89 bits of data, LSB first  \n *  Data        - 1C9037F0C80000 CE280BA00\n *    S[3]     S[2]   1 key    S[1]     S[0]    2 byte always 0   Hop[3]   Hop[2]  Hop[1]   Hop[0]    0\n *  11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000\n * \n *  Encryption  - keeloq Simple Learning\n *                                         key C  S[3]  CNT \n *  Decrypt     - 0xEC270B9C        =>  0x  E  C   27   0B9C\n * \n * \n * \n*/\n\n    uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32);\n    uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53);\n    bool ret = false;\n    uint32_t decrypt = 0;\n    instance->btn = (fix >> 17) & 0x0F;\n    instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);\n\n    for\n        M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {\n            if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) {\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);\n                if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) &&\n                   (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) {\n                    ret = true;\n                    break;\n                }\n            }\n        }\n    if(ret) {\n        instance->cnt = decrypt & 0xFFFF;\n    } else {\n        instance->btn = 0;\n        instance->serial = 0;\n        instance->cnt = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n\n    uint8_t key_data[sizeof(uint64_t)] = {0};\n    for(size_t i = 0; i < sizeof(uint64_t); i++) {\n        key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF;\n    }\n\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_hex(flipper_format, \"Data\", key_data, sizeof(uint64_t))) {\n        FURI_LOG_E(TAG, \"Unable to add Data\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        if(!flipper_format_read_hex(flipper_format, \"Data\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Missing Data\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {\n            instance->data = instance->data << 8 | key_data[i];\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;\n    subghz_protocol_kinggates_stylo_4k_remote_controller(\n        &instance->generic, instance->data, instance->keystore);\n\n    furi_string_cat_printf(\n        output,\n        \"%s\\r\\n\"\n        \"Key:0x%llX%07llX  %dbit\\r\\n\"\n        \"Sn:0x%08lX  Btn:0x%01X\\r\\n\"\n        \"Cnt:0x%04lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data,\n        instance->data,\n        instance->generic.data_count_bit,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/kinggates_stylo_4k.h",
    "content": "#pragma once\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME \"KingGates Stylo4k\"\n\ntypedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k;\ntypedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k;\n\nextern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder;\nextern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k;\n\n/**\n * Allocate SubGhzProtocolDecoderKingGates_stylo_4k.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n */\nvoid* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderKingGates_stylo_4k.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n */\nvoid subghz_protocol_decoder_kinggates_stylo_4k_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n */\nvoid subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize(\n    void* context,\n    FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/legrand.c",
    "content": "#include \"legrand.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolLegrand\"\n\nstatic const SubGhzBlockConst subghz_protocol_legrand_const = {\n    .te_short = 375,\n    .te_long = 1125,\n    .te_delta = 150,\n    .min_count_bit_for_found = 18,\n};\n\nstruct SubGhzProtocolDecoderLegrand {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n    uint32_t last_data;\n};\n\nstruct SubGhzProtocolEncoderLegrand {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n};\n\ntypedef enum {\n    LegrandDecoderStepReset = 0,\n    LegrandDecoderStepFirstBit,\n    LegrandDecoderStepSaveDuration,\n    LegrandDecoderStepCheckDuration,\n} LegrandDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_legrand_decoder = {\n    .alloc = subghz_protocol_decoder_legrand_alloc,\n    .free = subghz_protocol_decoder_legrand_free,\n\n    .feed = subghz_protocol_decoder_legrand_feed,\n    .reset = subghz_protocol_decoder_legrand_reset,\n\n    .get_hash_data = subghz_protocol_decoder_legrand_get_hash_data,\n    .serialize = subghz_protocol_decoder_legrand_serialize,\n    .deserialize = subghz_protocol_decoder_legrand_deserialize,\n    .get_string = subghz_protocol_decoder_legrand_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_legrand_encoder = {\n    .alloc = subghz_protocol_encoder_legrand_alloc,\n    .free = subghz_protocol_encoder_legrand_free,\n\n    .deserialize = subghz_protocol_encoder_legrand_deserialize,\n    .stop = subghz_protocol_encoder_legrand_stop,\n    .yield = subghz_protocol_encoder_legrand_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_legrand = {\n    .name = SUBGHZ_PROTOCOL_LEGRAND_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_legrand_decoder,\n    .encoder = &subghz_protocol_legrand_encoder,\n};\n\nvoid* subghz_protocol_encoder_legrand_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderLegrand* instance = malloc(sizeof(SubGhzProtocolEncoderLegrand));\n\n    instance->base.protocol = &subghz_protocol_legrand;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload =\n        (subghz_protocol_legrand_const.min_count_bit_for_found * 6) * 2 + 2;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_legrand_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLegrand* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderLegrand instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_legrand_get_upload(SubGhzProtocolEncoderLegrand* instance) {\n    furi_assert(instance);\n\n    //size_t size_upload = (instance->generic.data_count_bit * 2) + 1;\n    //if(size_upload != instance->encoder.size_upload) {\n    //    FURI_LOG_E(TAG, \"Invalid data bit count\");\n    //    return false;\n    //}\n\n    size_t index = 0;\n\n    for(size_t r = 0; r < 5; r++) {\n        // Send sync\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)instance->te * 16); // 5728\n        // Send key data\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(bit_read(instance->generic.data, i - 1)) {\n                // send bit 1\n                if(i == instance->generic.data_count_bit) {\n                    //Send first bit\n                    instance->encoder.upload[index++] =\n                        level_duration_make(true, (uint32_t)instance->te * 3);\n                } else {\n                    // send bit 1 regular\n                    instance->encoder.upload[index++] =\n                        level_duration_make(false, (uint32_t)instance->te);\n                    instance->encoder.upload[index++] =\n                        level_duration_make(true, (uint32_t)instance->te * 3);\n                }\n            } else {\n                // send bit 0\n                if(i == instance->generic.data_count_bit) {\n                    //Send first bit\n                    instance->encoder.upload[index++] =\n                        level_duration_make(true, (uint32_t)instance->te);\n                } else {\n                    // send bit 0 regular\n                    instance->encoder.upload[index++] =\n                        level_duration_make(false, (uint32_t)instance->te * 3);\n                    instance->encoder.upload[index++] =\n                        level_duration_make(true, (uint32_t)instance->te);\n                }\n            }\n        }\n    }\n    instance->encoder.size_upload = index;\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_legrand_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLegrand* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_legrand_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n        // optional parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_legrand_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_legrand_stop(void* context) {\n    SubGhzProtocolEncoderLegrand* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_legrand_yield(void* context) {\n    SubGhzProtocolEncoderLegrand* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_legrand_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderLegrand* instance = malloc(sizeof(SubGhzProtocolDecoderLegrand));\n    instance->base.protocol = &subghz_protocol_legrand;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_legrand_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_legrand_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n    instance->decoder.parser_step = LegrandDecoderStepReset;\n    instance->last_data = 0;\n}\n\nvoid subghz_protocol_decoder_legrand_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case LegrandDecoderStepReset:\n        if(!level && DURATION_DIFF(duration, subghz_protocol_legrand_const.te_short * 16) <\n                         subghz_protocol_legrand_const.te_delta * 8) { // 6000 +- 1200\n            instance->decoder.parser_step = LegrandDecoderStepFirstBit;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->te = 0;\n        }\n        break;\n    case LegrandDecoderStepFirstBit:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_legrand_const.te_short) <\n               subghz_protocol_legrand_const.te_delta) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->te += duration * 4; // long low that is part of sync, then short high\n            }\n\n            if(DURATION_DIFF(duration, subghz_protocol_legrand_const.te_long) <\n               subghz_protocol_legrand_const.te_delta * 3) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->te += duration / 3 * 4; // short low that is part of sync, then long high\n            }\n\n            if(instance->decoder.decode_count_bit > 0) {\n                // advance to the next step if either short or long is found\n                instance->decoder.parser_step = LegrandDecoderStepSaveDuration;\n                break;\n            }\n        }\n\n        instance->decoder.parser_step = LegrandDecoderStepReset;\n        break;\n    case LegrandDecoderStepSaveDuration:\n        if(!level) {\n            instance->decoder.te_last = duration;\n            instance->te += duration;\n            instance->decoder.parser_step = LegrandDecoderStepCheckDuration;\n            break;\n        }\n\n        instance->decoder.parser_step = LegrandDecoderStepReset;\n        break;\n    case LegrandDecoderStepCheckDuration:\n        if(level) {\n            uint8_t found = 0;\n\n            if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_legrand_const.te_long) <\n                   subghz_protocol_legrand_const.te_delta * 3 &&\n               DURATION_DIFF(duration, subghz_protocol_legrand_const.te_short) <\n                   subghz_protocol_legrand_const.te_delta) {\n                found = 1;\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n            }\n\n            if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_legrand_const.te_short) <\n                   subghz_protocol_legrand_const.te_delta &&\n               DURATION_DIFF(duration, subghz_protocol_legrand_const.te_long) <\n                   subghz_protocol_legrand_const.te_delta * 3) {\n                found = 1;\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n            }\n\n            if(found) {\n                instance->te += duration;\n\n                if(instance->decoder.decode_count_bit <\n                   subghz_protocol_legrand_const.min_count_bit_for_found) {\n                    instance->decoder.parser_step = LegrandDecoderStepSaveDuration;\n                    break;\n                }\n\n                // enough bits for a packet found, save it only if there was a previous packet\n                // with the same data\n                if(instance->last_data && (instance->last_data == instance->decoder.decode_data)) {\n                    instance->te /= instance->decoder.decode_count_bit * 4;\n\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback) {\n                        instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->last_data = instance->decoder.decode_data;\n                // fallthrough to reset, the next bit is expected to be a sync\n                // it also takes care of resetting the decoder state\n            }\n        }\n\n        instance->decoder.parser_step = LegrandDecoderStepReset;\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_legrand_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_legrand_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"TE\", &instance->te, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add TE\");\n        ret = SubGhzProtocolStatusErrorParserTe;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_legrand_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_legrand_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_decoder_legrand_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLegrand* instance = context;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%05lX\\r\\n\"\n        \"Te:%luus\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFF),\n        instance->te);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/legrand.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_LEGRAND_NAME \"Legrand\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzProtocolDecoderLegrand SubGhzProtocolDecoderLegrand;\ntypedef struct SubGhzProtocolEncoderLegrand SubGhzProtocolEncoderLegrand;\n\nextern const SubGhzProtocolDecoder subghz_protocol_legrand_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_legrand_encoder;\nextern const SubGhzProtocol subghz_protocol_legrand;\n\n/**\n * Allocate SubGhzProtocolEncoderLegrand.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderLegrand* pointer to a SubGhzProtocolEncoderLegrand instance\n */\nvoid* subghz_protocol_encoder_legrand_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderLegrand.\n * @param context Pointer to a SubGhzProtocolEncoderLegrand instance\n */\nvoid subghz_protocol_encoder_legrand_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderLegrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_legrand_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderLegrand instance\n */\nvoid subghz_protocol_encoder_legrand_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderLegrand instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_legrand_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderLegrand.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderLegrand* pointer to a SubGhzProtocolDecoderLegrand instance\n */\nvoid* subghz_protocol_decoder_legrand_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderLegrand.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n */\nvoid subghz_protocol_decoder_legrand_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderLegrand.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n */\nvoid subghz_protocol_decoder_legrand_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_legrand_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_legrand_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderLegrand.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_legrand_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderLegrand.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_legrand_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderLegrand instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_legrand_get_string(void* context, FuriString* output);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/protocols/linear.c",
    "content": "#include \"linear.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolLinear\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c%c%c\"\n#define DATA_TO_DIP(dip)                                                                    \\\n    (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'),     \\\n        (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \\\n        (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \\\n        (dip & 0x0001 ? '1' : '0')\n\nstatic const SubGhzBlockConst subghz_protocol_linear_const = {\n    .te_short = 500,\n    .te_long = 1500,\n    .te_delta = 350,\n    .min_count_bit_for_found = 10,\n};\n\nstruct SubGhzProtocolDecoderLinear {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderLinear {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    LinearDecoderStepReset = 0,\n    LinearDecoderStepSaveDuration,\n    LinearDecoderStepCheckDuration,\n} LinearDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_linear_decoder = {\n    .alloc = subghz_protocol_decoder_linear_alloc,\n    .free = subghz_protocol_decoder_linear_free,\n\n    .feed = subghz_protocol_decoder_linear_feed,\n    .reset = subghz_protocol_decoder_linear_reset,\n\n    .get_hash_data = subghz_protocol_decoder_linear_get_hash_data,\n    .serialize = subghz_protocol_decoder_linear_serialize,\n    .deserialize = subghz_protocol_decoder_linear_deserialize,\n    .get_string = subghz_protocol_decoder_linear_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_linear_encoder = {\n    .alloc = subghz_protocol_encoder_linear_alloc,\n    .free = subghz_protocol_encoder_linear_free,\n\n    .deserialize = subghz_protocol_encoder_linear_deserialize,\n    .stop = subghz_protocol_encoder_linear_stop,\n    .yield = subghz_protocol_encoder_linear_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_linear = {\n    .name = SUBGHZ_PROTOCOL_LINEAR_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_linear_decoder,\n    .encoder = &subghz_protocol_linear_encoder,\n};\n\nvoid* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderLinear* instance = malloc(sizeof(SubGhzProtocolEncoderLinear));\n\n    instance->base.protocol = &subghz_protocol_linear;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop)\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_linear_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLinear* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderLinear instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinear* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_long);\n        }\n    }\n    //Send end bit\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_long);\n        //Send gap\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 42);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short);\n        //Send gap\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 44);\n    }\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLinear* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_linear_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_linear_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_linear_stop(void* context) {\n    SubGhzProtocolEncoderLinear* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_linear_yield(void* context) {\n    SubGhzProtocolEncoderLinear* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderLinear* instance = malloc(sizeof(SubGhzProtocolDecoderLinear));\n    instance->base.protocol = &subghz_protocol_linear;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_linear_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_linear_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    instance->decoder.parser_step = LinearDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    switch(instance->decoder.parser_step) {\n    case LinearDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) <\n                        subghz_protocol_linear_const.te_delta * 15)) {\n            //Found header Linear\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n        }\n        break;\n    case LinearDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = LinearDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = LinearDecoderStepReset;\n        }\n        break;\n    case LinearDecoderStepCheckDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_linear_const.te_short * 5)) {\n                instance->decoder.parser_step = LinearDecoderStepReset;\n                //checking that the duration matches the guardtime\n                if(DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) >\n                   subghz_protocol_linear_const.te_delta * 15) {\n                    break;\n                }\n                if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) <\n                   subghz_protocol_linear_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else if(\n                    DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) <\n                    subghz_protocol_linear_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_linear_const.min_count_bit_for_found) {\n                    instance->generic.serial = 0x0;\n                    instance->generic.btn = 0x0;\n\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) <\n                subghz_protocol_linear_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_linear_const.te_long) <\n                subghz_protocol_linear_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) <\n                 subghz_protocol_linear_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short) <\n                 subghz_protocol_linear_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = LinearDecoderStepReset;\n            }\n\n        } else {\n            instance->decoder.parser_step = LinearDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_linear_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_linear_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_linear_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinear* instance = context;\n\n    // Protocol is actually implemented wrong way around, bits are inverted.\n    // Instead of fixing it and breaking old saved remotes,\n    // only the display here is inverted (~) to show correct values.\n    uint32_t code_found_lo = ~instance->generic.data & 0x00000000000003ff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        ~instance->generic.data, instance->generic.data_count_bit);\n\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000000003ff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%03lX\\r\\n\"\n        \"Yek:0x%03lX\\r\\n\"\n        \"DIP:\" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_lo,\n        code_found_reverse_lo,\n        DATA_TO_DIP(code_found_lo));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/linear.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_LINEAR_NAME \"Linear\"\n\ntypedef struct SubGhzProtocolDecoderLinear SubGhzProtocolDecoderLinear;\ntypedef struct SubGhzProtocolEncoderLinear SubGhzProtocolEncoderLinear;\n\nextern const SubGhzProtocolDecoder subghz_protocol_linear_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_linear_encoder;\nextern const SubGhzProtocol subghz_protocol_linear;\n\n/**\n * Allocate SubGhzProtocolEncoderLinear.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderLinear* pointer to a SubGhzProtocolEncoderLinear instance\n */\nvoid* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderLinear.\n * @param context Pointer to a SubGhzProtocolEncoderLinear instance\n */\nvoid subghz_protocol_encoder_linear_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderLinear instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderLinear instance\n */\nvoid subghz_protocol_encoder_linear_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderLinear instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_linear_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderLinear.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderLinear* pointer to a SubGhzProtocolDecoderLinear instance\n */\nvoid* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderLinear.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n */\nvoid subghz_protocol_decoder_linear_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderLinear.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n */\nvoid subghz_protocol_decoder_linear_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_linear_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderLinear.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_linear_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderLinear.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderLinear instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_linear_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/linear_delta3.c",
    "content": "#include \"linear_delta3.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolLinearDelta3\"\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c\"\n#define DATA_TO_DIP(dip)                                                                    \\\n    (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'),     \\\n        (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \\\n        (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1')\n\nstatic const SubGhzBlockConst subghz_protocol_linear_delta3_const = {\n    .te_short = 500,\n    .te_long = 2000,\n    .te_delta = 150,\n    .min_count_bit_for_found = 8,\n};\n\nstruct SubGhzProtocolDecoderLinearDelta3 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t last_data;\n};\n\nstruct SubGhzProtocolEncoderLinearDelta3 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    LinearDecoderStepReset = 0,\n    LinearDecoderStepSaveDuration,\n    LinearDecoderStepCheckDuration,\n} LinearDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = {\n    .alloc = subghz_protocol_decoder_linear_delta3_alloc,\n    .free = subghz_protocol_decoder_linear_delta3_free,\n\n    .feed = subghz_protocol_decoder_linear_delta3_feed,\n    .reset = subghz_protocol_decoder_linear_delta3_reset,\n\n    .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data,\n    .serialize = subghz_protocol_decoder_linear_delta3_serialize,\n    .deserialize = subghz_protocol_decoder_linear_delta3_deserialize,\n    .get_string = subghz_protocol_decoder_linear_delta3_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = {\n    .alloc = subghz_protocol_encoder_linear_delta3_alloc,\n    .free = subghz_protocol_encoder_linear_delta3_free,\n\n    .deserialize = subghz_protocol_encoder_linear_delta3_deserialize,\n    .stop = subghz_protocol_encoder_linear_delta3_stop,\n    .yield = subghz_protocol_encoder_linear_delta3_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_linear_delta3 = {\n    .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_linear_delta3_decoder,\n    .encoder = &subghz_protocol_linear_delta3_encoder,\n};\n\nvoid* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderLinearDelta3* instance =\n        malloc(sizeof(SubGhzProtocolEncoderLinearDelta3));\n\n    instance->base.protocol = &subghz_protocol_linear_delta3;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 16;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_linear_delta3_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLinearDelta3* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short);\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long);\n        }\n    }\n    //Send end bit\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short);\n        //Send PT_GUARD\n        instance->encoder.upload[index] = level_duration_make(\n            false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long);\n        //Send PT_GUARD\n        instance->encoder.upload[index] = level_duration_make(\n            false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70);\n    }\n\n    return true;\n}\n\nSubGhzProtocolStatus subghz_protocol_encoder_linear_delta3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderLinearDelta3* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_linear_delta3_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_linear_delta3_stop(void* context) {\n    SubGhzProtocolEncoderLinearDelta3* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) {\n    SubGhzProtocolEncoderLinearDelta3* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderLinearDelta3* instance =\n        malloc(sizeof(SubGhzProtocolDecoderLinearDelta3));\n    instance->base.protocol = &subghz_protocol_linear_delta3;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_linear_delta3_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_linear_delta3_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    instance->decoder.parser_step = LinearDecoderStepReset;\n    instance->last_data = 0;\n}\n\nvoid subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    switch(instance->decoder.parser_step) {\n    case LinearDecoderStepReset:\n        if((!level) &&\n           (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) <\n            subghz_protocol_linear_delta3_const.te_delta * 24)) {\n            //Found header Linear\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n        }\n        break;\n    case LinearDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = LinearDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = LinearDecoderStepReset;\n        }\n        break;\n    case LinearDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) {\n                instance->decoder.parser_step = LinearDecoderStepReset;\n                if(DURATION_DIFF(\n                       instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) <\n                   subghz_protocol_linear_delta3_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else if(\n                    DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) <\n                    subghz_protocol_linear_delta3_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_linear_delta3_const.min_count_bit_for_found) {\n                    if((instance->last_data == instance->decoder.decode_data) &&\n                       instance->last_data) {\n                        instance->generic.serial = 0x0;\n                        instance->generic.btn = 0x0;\n\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n                    instance->last_data = instance->decoder.decode_data;\n                }\n                break;\n            }\n\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) <\n                subghz_protocol_linear_delta3_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) <\n                subghz_protocol_linear_delta3_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) <\n                 subghz_protocol_linear_delta3_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) <\n                 subghz_protocol_linear_delta3_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = LinearDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = LinearDecoderStepReset;\n            }\n\n        } else {\n            instance->decoder.parser_step = LinearDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8));\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_deserialize(\n    void* context,\n    FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_linear_delta3_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderLinearDelta3* instance = context;\n\n    uint32_t data = instance->generic.data & 0xFF;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX\\r\\n\"\n        \"DIP:\" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        data,\n        DATA_TO_DIP(data));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/linear_delta3.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME \"LinearDelta3\"\n\ntypedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3;\ntypedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3;\n\nextern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder;\nextern const SubGhzProtocol subghz_protocol_linear_delta3;\n\n/**\n * Allocate SubGhzProtocolEncoderLinearDelta3.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n */\nvoid* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderLinearDelta3.\n * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n */\nvoid subghz_protocol_encoder_linear_delta3_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n */\nvoid subghz_protocol_encoder_linear_delta3_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderLinearDelta3.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n */\nvoid* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderLinearDelta3.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n */\nvoid subghz_protocol_decoder_linear_delta3_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderLinearDelta3.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n */\nvoid subghz_protocol_decoder_linear_delta3_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderLinearDelta3.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderLinearDelta3.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/magellan.c",
    "content": "#include \"magellan.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolMagellan\"\n\nstatic const SubGhzBlockConst subghz_protocol_magellan_const = {\n    .te_short = 200,\n    .te_long = 400,\n    .te_delta = 100,\n    .min_count_bit_for_found = 32,\n};\n\nstruct SubGhzProtocolDecoderMagellan {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderMagellan {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    MagellanDecoderStepReset = 0,\n    MagellanDecoderStepCheckPreambula,\n    MagellanDecoderStepFoundPreambula,\n    MagellanDecoderStepSaveDuration,\n    MagellanDecoderStepCheckDuration,\n} MagellanDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_magellan_decoder = {\n    .alloc = subghz_protocol_decoder_magellan_alloc,\n    .free = subghz_protocol_decoder_magellan_free,\n\n    .feed = subghz_protocol_decoder_magellan_feed,\n    .reset = subghz_protocol_decoder_magellan_reset,\n\n    .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data,\n    .serialize = subghz_protocol_decoder_magellan_serialize,\n    .deserialize = subghz_protocol_decoder_magellan_deserialize,\n    .get_string = subghz_protocol_decoder_magellan_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_magellan_encoder = {\n    .alloc = subghz_protocol_encoder_magellan_alloc,\n    .free = subghz_protocol_encoder_magellan_free,\n\n    .deserialize = subghz_protocol_encoder_magellan_deserialize,\n    .stop = subghz_protocol_encoder_magellan_stop,\n    .yield = subghz_protocol_encoder_magellan_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_magellan = {\n    .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_magellan_decoder,\n    .encoder = &subghz_protocol_magellan_encoder,\n};\n\nvoid* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan));\n\n    instance->base.protocol = &subghz_protocol_magellan;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_magellan_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMagellan* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);\n    for(uint8_t i = 0; i < 12; i++) {\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);\n\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);\n        }\n    }\n\n    //Send stop bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100);\n\n    instance->encoder.size_upload = index;\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMagellan* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_magellan_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_magellan_get_upload(instance)) {\n            instance->encoder.front = 0; // reset before start\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_magellan_stop(void* context) {\n    SubGhzProtocolEncoderMagellan* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_magellan_yield(void* context) {\n    SubGhzProtocolEncoderMagellan* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan));\n    instance->base.protocol = &subghz_protocol_magellan;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_magellan_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_magellan_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    instance->decoder.parser_step = MagellanDecoderStepReset;\n}\n\nuint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) {\n    uint8_t crc = 0x00;\n    size_t i, j;\n    for(i = 0; i < len; i++) {\n        crc ^= data[i];\n        for(j = 0; j < 8; j++) {\n            if((crc & 0x80) != 0)\n                crc = (uint8_t)((crc << 1) ^ 0x31);\n            else\n                crc <<= 1;\n        }\n    }\n    return crc;\n}\n\nstatic bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) {\n    uint8_t data[3] = {\n        instance->decoder.decode_data >> 24,\n        instance->decoder.decode_data >> 16,\n        instance->decoder.decode_data >> 8};\n    return (instance->decoder.decode_data & 0xFF) ==\n           subghz_protocol_magellan_crc8(data, sizeof(data));\n}\n\nvoid subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case MagellanDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <\n                       subghz_protocol_magellan_const.te_delta)) {\n            instance->decoder.parser_step = MagellanDecoderStepCheckPreambula;\n            instance->decoder.te_last = duration;\n            instance->header_count = 0;\n        }\n        break;\n\n    case MagellanDecoderStepCheckPreambula:\n        if(level) {\n            instance->decoder.te_last = duration;\n        } else {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <\n                subghz_protocol_magellan_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <\n                subghz_protocol_magellan_const.te_delta)) {\n                // Found header\n                instance->header_count++;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <\n                 subghz_protocol_magellan_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <\n                 subghz_protocol_magellan_const.te_delta * 2) &&\n                (instance->header_count > 10)) {\n                instance->decoder.parser_step = MagellanDecoderStepFoundPreambula;\n            } else {\n                instance->decoder.parser_step = MagellanDecoderStepReset;\n            }\n        }\n        break;\n\n    case MagellanDecoderStepFoundPreambula:\n        if(level) {\n            instance->decoder.te_last = duration;\n        } else {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) <\n                subghz_protocol_magellan_const.te_delta * 3) &&\n               (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <\n                subghz_protocol_magellan_const.te_delta * 2)) {\n                instance->decoder.parser_step = MagellanDecoderStepSaveDuration;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n            } else {\n                instance->decoder.parser_step = MagellanDecoderStepReset;\n            }\n        }\n        break;\n\n    case MagellanDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = MagellanDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = MagellanDecoderStepReset;\n        }\n        break;\n\n    case MagellanDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <\n                subghz_protocol_magellan_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <\n                subghz_protocol_magellan_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = MagellanDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) <\n                 subghz_protocol_magellan_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <\n                 subghz_protocol_magellan_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = MagellanDecoderStepSaveDuration;\n            } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) {\n                //Found stop bit\n                if((instance->decoder.decode_count_bit ==\n                    subghz_protocol_magellan_const.min_count_bit_for_found) &&\n                   subghz_protocol_magellan_check_crc(instance)) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = MagellanDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = MagellanDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = MagellanDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n*   package 32b            data 24b           CRC8\n*   0x037AE4828 => 001101111010111001001000 00101000\n*   \n*   0x037AE48 (flipped in reverse bit sequence) => 0x1275EC\n*\n*   0x1275EC =>  0x12-event codes, 0x75EC-serial (dec 117236)\n*\n* Event codes consist of two parts:\n* - The upper nibble (bits 7-4) represents the event type:\n*     - 0x00: Nothing\n*     - 0x01: Door\n*     - 0x02: Motion\n*     - 0x03: Smoke Alarm\n*     - 0x04: REM1\n*     - 0x05: REM1 with subtype Off1\n*     - 0x06: REM2\n*     - 0x07: REM2 with subtype Off1\n*     - Others: Unknown\n* - The lower nibble (bits 3-0) represents the event subtype, which varies based on the model type:\n*     - If the model type is greater than 0x03 (e.g., REM1 or REM2):\n*         - 0x00: Arm1\n*         - 0x01: Btn1\n*         - 0x02: Btn2\n*         - 0x03: Btn3\n*         - 0x08: Reset\n*         - 0x09: LowBatt\n*         - 0x0A: BattOk\n*         - 0x0B: Learn\n*         - Others: Unknown\n*     - Otherwise:\n*         - 0x00: Sealed\n*         - 0x01: Alarm\n*         - 0x02: Tamper\n*         - 0x03: Alarm + Tamper\n*         - 0x08: Reset\n*         - 0x09: LowBatt\n*         - 0x0A: BattOk\n*         - 0x0B: Learn\n*         - Others: Unknown\n*\n*/\n    uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24);\n    instance->serial = data_rev & 0xFFFF;\n    instance->btn = (data_rev >> 16) & 0xFF;\n}\n\nstatic void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) {\n    const char* event_type;\n    const char* event_subtype;\n\n    switch((event >> 4) & 0x0F) {\n    case 0x00:\n        event_type = \"Nothing\";\n        break;\n    case 0x01:\n        event_type = \"Door\";\n        break;\n    case 0x02:\n        event_type = \"Motion\";\n        break;\n    case 0x03:\n        event_type = \"Smoke Alarm\";\n        break;\n    case 0x04:\n        event_type = \"REM1\";\n        break;\n    case 0x05:\n        event_type = \"REM1\";\n        event_subtype = \"Off1\";\n        furi_string_cat_printf(output, \"%s - %s\", event_type, event_subtype);\n        return;\n    case 0x06:\n        event_type = \"REM2\";\n        event_subtype = \"Off1\";\n        furi_string_cat_printf(output, \"%s - %s\", event_type, event_subtype);\n        return;\n    default:\n        event_type = \"Unknown\";\n        break;\n    }\n\n    switch(event & 0x0F) {\n    case 0x00:\n        event_subtype = (((event >> 4) & 0x0F) > 0x03) ? \"Arm1\" : \"Sealed\";\n        break;\n    case 0x01:\n        event_subtype = (((event >> 4) & 0x0F) > 0x03) ? \"Btn1\" : \"Alarm\";\n        break;\n    case 0x02:\n        event_subtype = (((event >> 4) & 0x0F) > 0x03) ? \"Btn2\" : \"Tamper\";\n        break;\n    case 0x03:\n        event_subtype = (((event >> 4) & 0x0F) > 0x03) ? \"Btn3\" : \"Alarm + Tamper\";\n        break;\n    case 0x08:\n        event_subtype = \"Reset\";\n        break;\n    case 0x09:\n        event_subtype = \"LowBatt\";\n        break;\n    case 0x0A:\n        event_subtype = \"BattOk\";\n        break;\n    case 0x0B:\n        event_subtype = \"Learn\";\n        break;\n    default:\n        event_subtype = \"Unknown\";\n        break;\n    }\n\n    furi_string_cat_printf(output, \"%s - %s\", event_type, event_subtype);\n}\n\nuint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_magellan_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMagellan* instance = context;\n    subghz_protocol_magellan_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%08lX\\r\\n\"\n        \"Sn:%03ld%03ld, Event:0x%02X\\r\\n\"\n        \"Stat:\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        (instance->generic.serial >> 8) & 0xFF,\n        instance->generic.serial & 0xFF,\n        instance->generic.btn);\n\n    subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/magellan.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_MAGELLAN_NAME \"Magellan\"\n\ntypedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan;\ntypedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan;\n\nextern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder;\nextern const SubGhzProtocol subghz_protocol_magellan;\n\n/**\n * Allocate SubGhzProtocolEncoderMagellan.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance\n */\nvoid* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderMagellan.\n * @param context Pointer to a SubGhzProtocolEncoderMagellan instance\n */\nvoid subghz_protocol_encoder_magellan_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderMagellan instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderMagellan instance\n */\nvoid subghz_protocol_encoder_magellan_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderMagellan instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_magellan_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderMagellan.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance\n */\nvoid* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderMagellan.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n */\nvoid subghz_protocol_decoder_magellan_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderMagellan.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n */\nvoid subghz_protocol_decoder_magellan_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderMagellan.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderMagellan.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderMagellan instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/marantec.c",
    "content": "#include \"marantec.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include <lib/toolbox/manchester_encoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolMarantec\"\n\nstatic const SubGhzBlockConst subghz_protocol_marantec_const = {\n    .te_short = 1000,\n    .te_long = 2000,\n    .te_delta = 200,\n    .min_count_bit_for_found = 49,\n};\n\nstruct SubGhzProtocolDecoderMarantec {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    ManchesterState manchester_saved_state;\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderMarantec {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    MarantecDecoderStepReset = 0,\n    MarantecDecoderFoundHeader,\n    MarantecDecoderStepDecoderData,\n} MarantecDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_marantec_decoder = {\n    .alloc = subghz_protocol_decoder_marantec_alloc,\n    .free = subghz_protocol_decoder_marantec_free,\n\n    .feed = subghz_protocol_decoder_marantec_feed,\n    .reset = subghz_protocol_decoder_marantec_reset,\n\n    .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data,\n    .serialize = subghz_protocol_decoder_marantec_serialize,\n    .deserialize = subghz_protocol_decoder_marantec_deserialize,\n    .get_string = subghz_protocol_decoder_marantec_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_marantec_encoder = {\n    .alloc = subghz_protocol_encoder_marantec_alloc,\n    .free = subghz_protocol_encoder_marantec_free,\n\n    .deserialize = subghz_protocol_encoder_marantec_deserialize,\n    .stop = subghz_protocol_encoder_marantec_stop,\n    .yield = subghz_protocol_encoder_marantec_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_marantec = {\n    .name = SUBGHZ_PROTOCOL_MARANTEC_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_marantec_decoder,\n    .encoder = &subghz_protocol_marantec_encoder,\n};\n\nvoid* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec));\n\n    instance->base.protocol = &subghz_protocol_marantec;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_marantec_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMarantec* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic LevelDuration\n    subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) {\n    LevelDuration data = {.duration = 0, .level = 0};\n    switch(result) {\n    case ManchesterEncoderResultShortLow:\n        data.duration = subghz_protocol_marantec_const.te_short;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongLow:\n        data.duration = subghz_protocol_marantec_const.te_long;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongHigh:\n        data.duration = subghz_protocol_marantec_const.te_long;\n        data.level = true;\n        break;\n    case ManchesterEncoderResultShortHigh:\n        data.duration = subghz_protocol_marantec_const.te_short;\n        data.level = true;\n        break;\n\n    default:\n        furi_crash(\"SubGhz: ManchesterEncoderResult is incorrect.\");\n        break;\n    }\n    return level_duration_make(data.level, data.duration);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance\n */\nstatic void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    ManchesterEncoderState enc_state;\n    manchester_encoder_reset(&enc_state);\n    ManchesterEncoderResult result;\n\n    if(!manchester_encoder_advance(\n           &enc_state,\n           bit_read(instance->generic.data, instance->generic.data_count_bit - 1),\n           &result)) {\n        instance->encoder.upload[index++] =\n            subghz_protocol_encoder_marantec_add_duration_to_upload(result);\n        manchester_encoder_advance(\n            &enc_state,\n            bit_read(instance->generic.data, instance->generic.data_count_bit - 1),\n            &result);\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5);\n\n    for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) {\n        if(!manchester_encoder_advance(\n               &enc_state, bit_read(instance->generic.data, i - 1), &result)) {\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_marantec_add_duration_to_upload(result);\n            manchester_encoder_advance(\n                &enc_state, bit_read(instance->generic.data, i - 1), &result);\n        }\n        instance->encoder.upload[index++] =\n            subghz_protocol_encoder_marantec_add_duration_to_upload(result);\n    }\n    instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload(\n        manchester_encoder_finish(&enc_state));\n    if(level_duration_get_level(instance->encoder.upload[index])) {\n        index++;\n    }\n    instance->encoder.size_upload = index;\n}\n\nuint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) {\n    uint8_t crc = 0x01;\n    size_t i, j;\n    for(i = 0; i < len; i++) {\n        crc ^= data[i];\n        for(j = 0; j < 8; j++) {\n            if((crc & 0x80) != 0)\n                crc = (uint8_t)((crc << 1) ^ 0x1D);\n            else\n                crc <<= 1;\n        }\n    }\n    return crc;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) {\n    // Key samples\n    // 1307EDF6486C5 = 000 100110000 01111110110111110110 0100 10000110 11000101\n    // 1303EFAFD8683 = 000 100110000 00111110111110101111 1101 10000110 10000011\n\n    // From unittests\n    // 1300710DF869F\n\n    // const serial button serial crc\n    // 130   7EDF6  4      86     C5\n    // 130   3EFAF  D      86     83\n    // 130   0710D  F      86     9F\n\n    instance->btn = (instance->data >> 16) & 0xF;\n    instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMarantec* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_marantec_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_marantec_remote_controller(&instance->generic);\n        subghz_protocol_encoder_marantec_get_upload(instance);\n        instance->encoder.front = 0;\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_marantec_stop(void* context) {\n    SubGhzProtocolEncoderMarantec* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_marantec_yield(void* context) {\n    SubGhzProtocolEncoderMarantec* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec));\n    instance->base.protocol = &subghz_protocol_marantec;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_marantec_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_marantec_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    instance->decoder.parser_step = MarantecDecoderStepReset;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\nvoid subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    ManchesterEvent event = ManchesterEventReset;\n\n    switch(instance->decoder.parser_step) {\n    case MarantecDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) <\n                        subghz_protocol_marantec_const.te_delta * 8)) {\n            //Found header marantec\n            instance->decoder.parser_step = MarantecDecoderStepDecoderData;\n            instance->decoder.decode_data = 1;\n            instance->decoder.decode_count_bit = 1;\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventReset,\n                &instance->manchester_saved_state,\n                NULL);\n        }\n        break;\n    case MarantecDecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <\n               subghz_protocol_marantec_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <\n                subghz_protocol_marantec_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 +\n                             subghz_protocol_marantec_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_marantec_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 1;\n                instance->decoder.decode_count_bit = 1;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n            } else {\n                instance->decoder.parser_step = MarantecDecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <\n               subghz_protocol_marantec_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <\n                subghz_protocol_marantec_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = MarantecDecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_marantec_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_marantec_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec* instance = context;\n    subghz_protocol_marantec_remote_controller(&instance->generic);\n\n    uint8_t tdata[6] = {\n        instance->generic.data >> 48,\n        instance->generic.data >> 40,\n        instance->generic.data >> 32,\n        instance->generic.data >> 24,\n        instance->generic.data >> 16,\n        instance->generic.data >> 8};\n\n    uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata));\n    bool crc_ok = (crc == (instance->generic.data & 0xFF));\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%lX%08lX\\r\\n\"\n        \"Sn: 0x%07lX \\r\\n\"\n        \"CRC: 0x%02X - %s\\r\\n\"\n        \"Btn: %X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        crc,\n        crc_ok ? \"Valid\" : \"Invalid\",\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/marantec.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_MARANTEC_NAME \"Marantec\"\n\ntypedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec;\ntypedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec;\n\nextern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder;\nextern const SubGhzProtocol subghz_protocol_marantec;\n\n/**\n * Allocate SubGhzProtocolEncoderMarantec.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance\n */\nvoid* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderMarantec.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec instance\n */\nvoid subghz_protocol_encoder_marantec_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec instance\n */\nvoid subghz_protocol_encoder_marantec_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_marantec_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderMarantec.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance\n */\nvoid* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderMarantec.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n */\nvoid subghz_protocol_decoder_marantec_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderMarantec.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n */\nvoid subghz_protocol_decoder_marantec_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderMarantec.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_marantec_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderMarantec.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output);\n\n/**\n * Calculate CRC8 for Marantec protocol.\n * @param data Pointer to the data buffer\n * @param len Length of the data buffer\n * @return CRC8 value\n */\nuint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len);\n"
  },
  {
    "path": "lib/subghz/protocols/marantec24.c",
    "content": "#include \"marantec24.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolMarantec24\"\n\nstatic const SubGhzBlockConst subghz_protocol_marantec24_const = {\n    .te_short = 800,\n    .te_long = 1600,\n    .te_delta = 200,\n    .min_count_bit_for_found = 24,\n};\n\nstruct SubGhzProtocolDecoderMarantec24 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderMarantec24 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Marantec24DecoderStepReset = 0,\n    Marantec24DecoderStepSaveDuration,\n    Marantec24DecoderStepCheckDuration,\n} Marantec24DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_marantec24_decoder = {\n    .alloc = subghz_protocol_decoder_marantec24_alloc,\n    .free = subghz_protocol_decoder_marantec24_free,\n\n    .feed = subghz_protocol_decoder_marantec24_feed,\n    .reset = subghz_protocol_decoder_marantec24_reset,\n\n    .get_hash_data = subghz_protocol_decoder_marantec24_get_hash_data,\n    .serialize = subghz_protocol_decoder_marantec24_serialize,\n    .deserialize = subghz_protocol_decoder_marantec24_deserialize,\n    .get_string = subghz_protocol_decoder_marantec24_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_marantec24_encoder = {\n    .alloc = subghz_protocol_encoder_marantec24_alloc,\n    .free = subghz_protocol_encoder_marantec24_free,\n\n    .deserialize = subghz_protocol_encoder_marantec24_deserialize,\n    .stop = subghz_protocol_encoder_marantec24_stop,\n    .yield = subghz_protocol_encoder_marantec24_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_marantec24 = {\n    .name = SUBGHZ_PROTOCOL_MARANTEC24_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_marantec24_decoder,\n    .encoder = &subghz_protocol_marantec24_encoder,\n};\n\nvoid* subghz_protocol_encoder_marantec24_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderMarantec24* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec24));\n\n    instance->base.protocol = &subghz_protocol_marantec24;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 512;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_marantec24_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMarantec24* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderMarantec24 instance\n */\nstatic void\n    subghz_protocol_encoder_marantec24_get_upload(SubGhzProtocolEncoderMarantec24* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    // Send initial GAP to trigger decoder\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_marantec24_const.te_long * 9);\n    for(size_t r = 0; r < 4; r++) {\n        // Send key and GAP\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(bit_read(instance->generic.data, i - 1)) {\n                // Send bit 1\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_marantec24_const.te_short);\n                if(i == 1) {\n                    //Send gap if bit was last\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false,\n                        (uint32_t)subghz_protocol_marantec24_const.te_long * 9 +\n                            subghz_protocol_marantec24_const.te_short);\n                } else {\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false, (uint32_t)subghz_protocol_marantec24_const.te_long * 2);\n                }\n            } else {\n                // Send bit 0\n                instance->encoder.upload[index++] =\n                    level_duration_make(true, (uint32_t)subghz_protocol_marantec24_const.te_long);\n                if(i == 1) {\n                    //Send gap if bit was last\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false,\n                        (uint32_t)subghz_protocol_marantec24_const.te_long * 9 +\n                            subghz_protocol_marantec24_const.te_short); // 15200\n                } else {\n                    instance->encoder.upload[index++] = level_duration_make(\n                        false, (uint32_t)subghz_protocol_marantec24_const.te_short * 3);\n                }\n            }\n        }\n    }\n\n    instance->encoder.size_upload = index;\n    return;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_marantec24_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->serial = instance->data >> 4;\n    instance->btn = instance->data & 0xF;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMarantec24* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_marantec24_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_marantec24_check_remote_controller(&instance->generic);\n        subghz_protocol_encoder_marantec24_get_upload(instance);\n        instance->encoder.front = 0; // reset before start\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_marantec24_stop(void* context) {\n    SubGhzProtocolEncoderMarantec24* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_marantec24_yield(void* context) {\n    SubGhzProtocolEncoderMarantec24* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_marantec24_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderMarantec24* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec24));\n    instance->base.protocol = &subghz_protocol_marantec24;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_marantec24_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_marantec24_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n    instance->decoder.parser_step = Marantec24DecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n\n    // Marantec24 Decoder\n    // 2024 - @xMasterX (MMX)\n\n    // 2025 update - The protocol is not real marantec,\n    // it comes from chinese remote that pretends to be replica of original marantec, actually it was a cloner\n    // which had some thing written on it, which is uknown, but since its pretentding to be marantec,\n    // it was decided to keep the name of the protocol as marantec24 (24 bits)\n\n    // Key samples\n    // 101011000000010111001000 = AC05C8\n    // 101011000000010111000100 = AC05C4\n    // 101011000000010111001100 = AC05CC\n    // 101011000000010111000000 = AC05C0\n\n    switch(instance->decoder.parser_step) {\n    case Marantec24DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) <\n                        subghz_protocol_marantec24_const.te_delta * 6)) {\n            //Found GAP\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = Marantec24DecoderStepSaveDuration;\n        }\n        break;\n    case Marantec24DecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = Marantec24DecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = Marantec24DecoderStepReset;\n        }\n        break;\n    case Marantec24DecoderStepCheckDuration:\n        if(!level) {\n            // Bit 0 is long and short x2 timing = 1600us HIGH (te_last) and 2400us LOW\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) <\n                subghz_protocol_marantec24_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_short * 3) <\n                subghz_protocol_marantec24_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = Marantec24DecoderStepSaveDuration;\n                // Bit 1 is short and long x2 timing = 800us HIGH (te_last) and 3200us LOW\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) <\n                 subghz_protocol_marantec24_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 2) <\n                 subghz_protocol_marantec24_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = Marantec24DecoderStepSaveDuration;\n            } else if(\n                // End of the key\n                DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) <\n                subghz_protocol_marantec24_const.te_delta * 6) {\n                //Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)\n                if((DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) <\n                    subghz_protocol_marantec24_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                if((DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) <\n                    subghz_protocol_marantec24_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                // If got 24 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_marantec24_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = Marantec24DecoderStepReset;\n            } else {\n                instance->decoder.parser_step = Marantec24DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = Marantec24DecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_marantec24_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_marantec24_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_marantec24_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_marantec24_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMarantec24* instance = context;\n\n    subghz_protocol_marantec24_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%06lX\\r\\n\"\n        \"Serial: 0x%05lX\\r\\n\"\n        \"Btn: %01X\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFF),\n        instance->generic.serial,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/marantec24.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_MARANTEC24_NAME \"Marantec24\"\n\ntypedef struct SubGhzProtocolDecoderMarantec24 SubGhzProtocolDecoderMarantec24;\ntypedef struct SubGhzProtocolEncoderMarantec24 SubGhzProtocolEncoderMarantec24;\n\nextern const SubGhzProtocolDecoder subghz_protocol_marantec24_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_marantec24_encoder;\nextern const SubGhzProtocol subghz_protocol_marantec24;\n\n/**\n * Allocate SubGhzProtocolEncoderMarantec24.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderMarantec24* pointer to a SubGhzProtocolEncoderMarantec24 instance\n */\nvoid* subghz_protocol_encoder_marantec24_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderMarantec24.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance\n */\nvoid subghz_protocol_encoder_marantec24_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance\n */\nvoid subghz_protocol_encoder_marantec24_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_marantec24_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderMarantec24.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderMarantec24* pointer to a SubGhzProtocolDecoderMarantec24 instance\n */\nvoid* subghz_protocol_decoder_marantec24_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderMarantec24.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n */\nvoid subghz_protocol_decoder_marantec24_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderMarantec24.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n */\nvoid subghz_protocol_decoder_marantec24_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_marantec24_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_marantec24_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderMarantec24.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_marantec24_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderMarantec24.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_marantec24_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/mastercode.c",
    "content": "#include \"mastercode.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n// protocol MASTERCODE Clemsa MV1/MV12\n#define TAG \"SubGhzProtocolMastercode\"\n\n#define DIP_P 0b11 //(+)\n#define DIP_O 0b10 //(0)\n#define DIP_N 0b00 //(-)\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c\"\n\n#define SHOW_DIP_P(dip, check_dip)                         \\\n    ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_'),     \\\n        ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_')\n\nstatic const SubGhzBlockConst subghz_protocol_mastercode_const = {\n    .te_short = 1072,\n    .te_long = 2145,\n    .te_delta = 150,\n    .min_count_bit_for_found = 36,\n};\n\nstruct SubGhzProtocolDecoderMastercode {\n    SubGhzProtocolDecoderBase base;\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderMastercode {\n    SubGhzProtocolEncoderBase base;\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    MastercodeDecoderStepReset = 0,\n    MastercodeDecoderStepSaveDuration,\n    MastercodeDecoderStepCheckDuration,\n} MastercodeDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_mastercode_decoder = {\n    .alloc = subghz_protocol_decoder_mastercode_alloc,\n    .free = subghz_protocol_decoder_mastercode_free,\n\n    .feed = subghz_protocol_decoder_mastercode_feed,\n    .reset = subghz_protocol_decoder_mastercode_reset,\n\n    .get_hash_data = subghz_protocol_decoder_mastercode_get_hash_data,\n    .serialize = subghz_protocol_decoder_mastercode_serialize,\n    .deserialize = subghz_protocol_decoder_mastercode_deserialize,\n    .get_string = subghz_protocol_decoder_mastercode_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_mastercode_encoder = {\n    .alloc = subghz_protocol_encoder_mastercode_alloc,\n    .free = subghz_protocol_encoder_mastercode_free,\n\n    .deserialize = subghz_protocol_encoder_mastercode_deserialize,\n    .stop = subghz_protocol_encoder_mastercode_stop,\n    .yield = subghz_protocol_encoder_mastercode_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_mastercode = {\n    .name = SUBGHZ_PROTOCOL_MASTERCODE_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_mastercode_decoder,\n    .encoder = &subghz_protocol_mastercode_encoder,\n};\n\nvoid* subghz_protocol_encoder_mastercode_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderMastercode* instance = malloc(sizeof(SubGhzProtocolEncoderMastercode));\n\n    instance->base.protocol = &subghz_protocol_mastercode;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 72;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_mastercode_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMastercode* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderMastercode instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_mastercode_get_upload(SubGhzProtocolEncoderMastercode* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_mastercode_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_mastercode_const.te_long);\n        }\n    }\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_long);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_mastercode_const.te_short +\n                subghz_protocol_mastercode_const.te_short * 13);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_short);\n        instance->encoder.upload[index++] = level_duration_make(\n            false,\n            (uint32_t)subghz_protocol_mastercode_const.te_long +\n                subghz_protocol_mastercode_const.te_short * 13);\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMastercode* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_mastercode_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_mastercode_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_mastercode_stop(void* context) {\n    SubGhzProtocolEncoderMastercode* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_mastercode_yield(void* context) {\n    SubGhzProtocolEncoderMastercode* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_mastercode_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderMastercode* instance = malloc(sizeof(SubGhzProtocolDecoderMastercode));\n    instance->base.protocol = &subghz_protocol_mastercode;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_mastercode_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_mastercode_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    instance->decoder.parser_step = MastercodeDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_mastercode_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case MastercodeDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short * 15) <\n                        subghz_protocol_mastercode_const.te_delta * 15)) {\n            instance->decoder.parser_step = MastercodeDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        }\n        break;\n\n    case MastercodeDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = MastercodeDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = MastercodeDecoderStepReset;\n        }\n        break;\n\n    case MastercodeDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_mastercode_const.te_short) <\n                subghz_protocol_mastercode_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_long) <\n                subghz_protocol_mastercode_const.te_delta * 8)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = MastercodeDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_mastercode_const.te_long) <\n                 subghz_protocol_mastercode_const.te_delta * 8) &&\n                (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short) <\n                 subghz_protocol_mastercode_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = MastercodeDecoderStepSaveDuration;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short * 15) <\n                subghz_protocol_mastercode_const.te_delta * 15) {\n                if(DURATION_DIFF(\n                       instance->decoder.te_last, subghz_protocol_mastercode_const.te_short) <\n                   subghz_protocol_mastercode_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else if(\n                    DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_mastercode_const.te_long) <\n                    subghz_protocol_mastercode_const.te_delta * 8) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                } else {\n                    instance->decoder.parser_step = MastercodeDecoderStepReset;\n                }\n\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_mastercode_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.parser_step = MastercodeDecoderStepSaveDuration;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n\n            } else {\n                instance->decoder.parser_step = MastercodeDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = MastercodeDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_mastercode_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->serial = (instance->data >> 4) & 0xFFFF;\n    instance->btn = (instance->data >> 2 & 0x03);\n}\n\nuint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_mastercode_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_mastercode_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_mastercode_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMastercode* instance = context;\n    subghz_protocol_mastercode_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%llX   Btn %X\\r\\n\"\n        \"  +:   \" DIP_PATTERN \"\\r\\n\"\n        \"  o:   \" DIP_PATTERN \"\\r\\n\"\n        \"  -:   \" DIP_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint64_t)(instance->generic.data),\n        instance->generic.btn,\n        SHOW_DIP_P(instance->generic.serial, DIP_P),\n        SHOW_DIP_P(instance->generic.serial, DIP_O),\n        SHOW_DIP_P(instance->generic.serial, DIP_N));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/mastercode.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_MASTERCODE_NAME \"Mastercode\"\n\ntypedef struct SubGhzProtocolDecoderMastercode SubGhzProtocolDecoderMastercode;\ntypedef struct SubGhzProtocolEncoderMastercode SubGhzProtocolEncoderMastercode;\n\nextern const SubGhzProtocolDecoder subghz_protocol_mastercode_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_mastercode_encoder;\nextern const SubGhzProtocol subghz_protocol_mastercode;\n\n/**\n * Allocate SubGhzProtocolEncoderMastercode.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderMastercode* pointer to a SubGhzProtocolEncoderMastercode instance\n */\nvoid* subghz_protocol_encoder_mastercode_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderMastercode.\n * @param context Pointer to a SubGhzProtocolEncoderMastercode instance\n */\nvoid subghz_protocol_encoder_mastercode_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderMastercode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderMastercode instance\n */\nvoid subghz_protocol_encoder_mastercode_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderMastercode instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_mastercode_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderMastercode.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderMastercode* pointer to a SubGhzProtocolDecoderMastercode instance\n */\nvoid* subghz_protocol_decoder_mastercode_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderMastercode.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n */\nvoid subghz_protocol_decoder_mastercode_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderMastercode.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n */\nvoid subghz_protocol_decoder_mastercode_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_mastercode_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderMastercode.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_mastercode_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderMastercode.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderMastercode instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_mastercode_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/megacode.c",
    "content": "#include \"megacode.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://wiki.cuvoodoo.info/doku.php?id=megacode\n * https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf\n * https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf\n * https://github.com/aaronsp777/megadecoder\n * https://github.com/rjmendez/Linear_keyfob\n * https://github.com/j07rdi/Linear_MegaCode_Garage_Remote\n *\n */\n\n#define TAG \"SubGhzProtocolMegaCode\"\n\nstatic const SubGhzBlockConst subghz_protocol_megacode_const = {\n    .te_short = 1000,\n    .te_long = 1000,\n    .te_delta = 200,\n    .min_count_bit_for_found = 24,\n};\n\nstruct SubGhzProtocolDecoderMegaCode {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    uint8_t last_bit;\n};\n\nstruct SubGhzProtocolEncoderMegaCode {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    MegaCodeDecoderStepReset = 0,\n    MegaCodeDecoderStepFoundStartBit,\n    MegaCodeDecoderStepSaveDuration,\n    MegaCodeDecoderStepCheckDuration,\n} MegaCodeDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_megacode_decoder = {\n    .alloc = subghz_protocol_decoder_megacode_alloc,\n    .free = subghz_protocol_decoder_megacode_free,\n\n    .feed = subghz_protocol_decoder_megacode_feed,\n    .reset = subghz_protocol_decoder_megacode_reset,\n\n    .get_hash_data = subghz_protocol_decoder_megacode_get_hash_data,\n    .serialize = subghz_protocol_decoder_megacode_serialize,\n    .deserialize = subghz_protocol_decoder_megacode_deserialize,\n    .get_string = subghz_protocol_decoder_megacode_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_megacode_encoder = {\n    .alloc = subghz_protocol_encoder_megacode_alloc,\n    .free = subghz_protocol_encoder_megacode_free,\n\n    .deserialize = subghz_protocol_encoder_megacode_deserialize,\n    .stop = subghz_protocol_encoder_megacode_stop,\n    .yield = subghz_protocol_encoder_megacode_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_megacode = {\n    .name = SUBGHZ_PROTOCOL_MEGACODE_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_megacode_decoder,\n    .encoder = &subghz_protocol_megacode_encoder,\n};\n\nvoid* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode));\n\n    instance->base.protocol = &subghz_protocol_megacode;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_megacode_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMegaCode* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) {\n    furi_assert(instance);\n    uint8_t last_bit = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    /*\n    * Due to the nature of the protocol\n    *\n    *  00000 1\n    *  _____|-| = 1 becomes\n    * \n    *  00 1 000\n    *  __|-|___ = 0 becomes\n    * \n    * it's easier for us to generate an upload backwards\n    */\n\n    size_t index = size_upload - 1;\n\n    // Send end level\n    instance->encoder.upload[index--] =\n        level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);\n    if(bit_read(instance->generic.data, 0)) {\n        last_bit = 1;\n    } else {\n        last_bit = 0;\n    }\n\n    //Send key data\n    for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) {\n        if(bit_read(instance->generic.data, i)) {\n            //if bit 1\n            instance->encoder.upload[index--] = level_duration_make(\n                false,\n                last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 :\n                           (uint32_t)subghz_protocol_megacode_const.te_short * 2);\n            last_bit = 1;\n        } else {\n            //if bit 0\n            instance->encoder.upload[index--] = level_duration_make(\n                false,\n                last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 :\n                           (uint32_t)subghz_protocol_megacode_const.te_short * 5);\n            last_bit = 0;\n        }\n        instance->encoder.upload[index--] =\n            level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);\n    }\n\n    //Send PT_GUARD\n    if(bit_read(instance->generic.data, 0)) {\n        //if end bit 1\n        instance->encoder.upload[index] =\n            level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11);\n    } else {\n        //if end bit 1\n        instance->encoder.upload[index] =\n            level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14);\n    }\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderMegaCode* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_megacode_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_megacode_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_megacode_stop(void* context) {\n    SubGhzProtocolEncoderMegaCode* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_megacode_yield(void* context) {\n    SubGhzProtocolEncoderMegaCode* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode));\n    instance->base.protocol = &subghz_protocol_megacode;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_megacode_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_megacode_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    instance->decoder.parser_step = MegaCodeDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    switch(instance->decoder.parser_step) {\n    case MegaCodeDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) <\n                        subghz_protocol_megacode_const.te_delta * 17)) { //10..16ms\n            //Found header MegaCode\n            instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit;\n        }\n        break;\n    case MegaCodeDecoderStepFoundStartBit:\n        if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <\n                     subghz_protocol_megacode_const.te_delta)) {\n            //Found start bit MegaCode\n            instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n            instance->last_bit = 1;\n\n        } else {\n            instance->decoder.parser_step = MegaCodeDecoderStepReset;\n        }\n        break;\n    case MegaCodeDecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_megacode_const.te_short * 10)) {\n                instance->decoder.parser_step = MegaCodeDecoderStepReset;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_megacode_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n\n            if(!instance->last_bit) {\n                instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3;\n            } else {\n                instance->decoder.te_last = duration;\n            }\n            instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = MegaCodeDecoderStepReset;\n        }\n        break;\n    case MegaCodeDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) <\n                subghz_protocol_megacode_const.te_delta * 5) &&\n               (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <\n                subghz_protocol_megacode_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->last_bit = 1;\n                instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) <\n                 subghz_protocol_megacode_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <\n                 subghz_protocol_megacode_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->last_bit = 0;\n                instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = MegaCodeDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = MegaCodeDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n    * Short: 1000 µs\n    * Long: 1000 µs\n    * Gap: 11000 .. 14000 µs\n    * A Linear Megacode transmission consists of 24 bit frames starting with \n    * the most significant bit and ending with the least. Each of the 24 bit \n    * frames is 6 milliseconds wide and always contains a single 1 millisecond \n    * pulse. A frame with more than 1 pulse or a frame with no pulse is invalid \n    * and a receiver should reset and begin watching for another start bit. \n    * Start bit is always 1.\n    * \n    * \n    * Example (I created with my own remote):\n    * Remote “A” has the code “17316”, a Facility Code of “3”, and a single button.\n    * Start bit (S) = 1\n    * Facility Code 3 (F) = 0011\n    * Remote Code (Key) 17316 = 43A4 = 0100001110100100\n    * Button (Btn) 1 = 001\n    *          S  F        Key         Btn\n    * Result = 1|0011|0100001110100100|001\n    * \n    *  00000 1\n    *  _____|-| = 1 becomes\n    * \n    *  00 1 000\n    *  __|-|___ = 0 becomes\n    * \n    * The device needs to transmit with a 9000 µs gap between retransmissions:\n    * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001\n    * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001\n    * wait 9000 µs\n    * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001\n    * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001\n    * \n    */\n    if((instance->data >> 23) == 1) {\n        instance->serial = (instance->data >> 3) & 0xFFFF;\n        instance->btn = instance->data & 0b111;\n        instance->cnt = (instance->data >> 19) & 0b1111;\n    } else {\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n    }\n}\n\nuint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_megacode_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_megacode_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderMegaCode* instance = context;\n    subghz_protocol_megacode_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%06lX\\r\\n\"\n        \"Sn:0x%04lX - %lu\\r\\n\"\n        \"Facility:%lX Btn:%X\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)instance->generic.data,\n        instance->generic.serial,\n        instance->generic.serial,\n        instance->generic.cnt,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/megacode.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_MEGACODE_NAME \"MegaCode\"\n\ntypedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode;\ntypedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode;\n\nextern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder;\nextern const SubGhzProtocol subghz_protocol_megacode;\n\n/**\n * Allocate SubGhzProtocolEncoderMegaCode.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance\n */\nvoid* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderMegaCode.\n * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance\n */\nvoid subghz_protocol_encoder_megacode_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance\n */\nvoid subghz_protocol_encoder_megacode_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_megacode_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderMegaCode.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance\n */\nvoid* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderMegaCode.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n */\nvoid subghz_protocol_decoder_megacode_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderMegaCode.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n */\nvoid subghz_protocol_decoder_megacode_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderMegaCode.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_megacode_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderMegaCode.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/nero_radio.c",
    "content": "#include \"nero_radio.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolNeroRadio\"\n\nstatic const SubGhzBlockConst subghz_protocol_nero_radio_const = {\n    .te_short = 200,\n    .te_long = 400,\n    .te_delta = 80,\n    .min_count_bit_for_found = 56,\n};\n\nstruct SubGhzProtocolDecoderNeroRadio {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderNeroRadio {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    NeroRadioDecoderStepReset = 0,\n    NeroRadioDecoderStepCheckPreambula,\n    NeroRadioDecoderStepSaveDuration,\n    NeroRadioDecoderStepCheckDuration,\n} NeroRadioDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder = {\n    .alloc = subghz_protocol_decoder_nero_radio_alloc,\n    .free = subghz_protocol_decoder_nero_radio_free,\n\n    .feed = subghz_protocol_decoder_nero_radio_feed,\n    .reset = subghz_protocol_decoder_nero_radio_reset,\n\n    .get_hash_data = subghz_protocol_decoder_nero_radio_get_hash_data,\n    .serialize = subghz_protocol_decoder_nero_radio_serialize,\n    .deserialize = subghz_protocol_decoder_nero_radio_deserialize,\n    .get_string = subghz_protocol_decoder_nero_radio_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder = {\n    .alloc = subghz_protocol_encoder_nero_radio_alloc,\n    .free = subghz_protocol_encoder_nero_radio_free,\n\n    .deserialize = subghz_protocol_encoder_nero_radio_deserialize,\n    .stop = subghz_protocol_encoder_nero_radio_stop,\n    .yield = subghz_protocol_encoder_nero_radio_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_nero_radio = {\n    .name = SUBGHZ_PROTOCOL_NERO_RADIO_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_nero_radio_decoder,\n    .encoder = &subghz_protocol_nero_radio_encoder,\n};\n\nvoid* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolEncoderNeroRadio));\n\n    instance->base.protocol = &subghz_protocol_nero_radio;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_nero_radio_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNeroRadio* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderNeroRadio instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_nero_radio_get_upload(SubGhzProtocolEncoderNeroRadio* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = 49 * 2 + 2 + (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    for(uint8_t i = 0; i < 49; i++) {\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n    }\n\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_long);\n        }\n    }\n    if(bit_read(instance->generic.data, 0)) {\n        //send bit 1\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37);\n    } else {\n        //send bit 0\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37);\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNeroRadio* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_nero_radio_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_nero_radio_stop(void* context) {\n    SubGhzProtocolEncoderNeroRadio* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_nero_radio_yield(void* context) {\n    SubGhzProtocolEncoderNeroRadio* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolDecoderNeroRadio));\n    instance->base.protocol = &subghz_protocol_nero_radio;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_nero_radio_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_nero_radio_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n    instance->decoder.parser_step = NeroRadioDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case NeroRadioDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <\n                       subghz_protocol_nero_radio_const.te_delta)) {\n            instance->decoder.parser_step = NeroRadioDecoderStepCheckPreambula;\n            instance->decoder.te_last = duration;\n            instance->header_count = 0;\n        }\n        break;\n    case NeroRadioDecoderStepCheckPreambula:\n        if(level) {\n            if((DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <\n                subghz_protocol_nero_radio_const.te_delta) ||\n               (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short * 4) <\n                subghz_protocol_nero_radio_const.te_delta)) {\n                instance->decoder.te_last = duration;\n            } else {\n                instance->decoder.parser_step = NeroRadioDecoderStepReset;\n            }\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <\n            subghz_protocol_nero_radio_const.te_delta) {\n            if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <\n               subghz_protocol_nero_radio_const.te_delta) {\n                // Found header\n                instance->header_count++;\n                break;\n            } else if(\n                DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short * 4) <\n                subghz_protocol_nero_radio_const.te_delta) {\n                // Found start bit\n                if(instance->header_count > 40) {\n                    instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;\n                    instance->decoder.decode_data = 0;\n                    instance->decoder.decode_count_bit = 0;\n                } else {\n                    instance->decoder.parser_step = NeroRadioDecoderStepReset;\n                }\n            } else {\n                instance->decoder.parser_step = NeroRadioDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = NeroRadioDecoderStepReset;\n        }\n        break;\n    case NeroRadioDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = NeroRadioDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = NeroRadioDecoderStepReset;\n        }\n        break;\n    case NeroRadioDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 +\n                            subghz_protocol_nero_radio_const.te_delta * 2)) {\n                //Found stop bit\n                if(DURATION_DIFF(\n                       instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <\n                   subghz_protocol_nero_radio_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                } else if(\n                    DURATION_DIFF(\n                        instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) <\n                    subghz_protocol_nero_radio_const.te_delta) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                instance->decoder.parser_step = NeroRadioDecoderStepReset;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_nero_radio_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048\n                break;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <\n                 subghz_protocol_nero_radio_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_long) <\n                 subghz_protocol_nero_radio_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) <\n                 subghz_protocol_nero_radio_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <\n                 subghz_protocol_nero_radio_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = NeroRadioDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = NeroRadioDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_nero_radio_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_nero_radio_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroRadio* instance = context;\n\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    uint32_t code_found_reverse_hi = code_found_reverse >> 32;\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Yek:0x%lX%08lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        code_found_reverse_hi,\n        code_found_reverse_lo);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/nero_radio.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_NERO_RADIO_NAME \"Nero Radio\"\n\ntypedef struct SubGhzProtocolDecoderNeroRadio SubGhzProtocolDecoderNeroRadio;\ntypedef struct SubGhzProtocolEncoderNeroRadio SubGhzProtocolEncoderNeroRadio;\n\nextern const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder;\nextern const SubGhzProtocol subghz_protocol_nero_radio;\n\n/**\n * Allocate SubGhzProtocolEncoderNeroRadio.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderNeroRadio* pointer to a SubGhzProtocolEncoderNeroRadio instance\n */\nvoid* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderNeroRadio.\n * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance\n */\nvoid subghz_protocol_encoder_nero_radio_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance\n */\nvoid subghz_protocol_encoder_nero_radio_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_nero_radio_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderNeroRadio.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderNeroRadio* pointer to a SubGhzProtocolDecoderNeroRadio instance\n */\nvoid* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderNeroRadio.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n */\nvoid subghz_protocol_decoder_nero_radio_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderNeroRadio.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n */\nvoid subghz_protocol_decoder_nero_radio_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderNeroRadio.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_nero_radio_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderNeroRadio.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/nero_sketch.c",
    "content": "#include \"nero_sketch.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolNeroSketch\"\n\nstatic const SubGhzBlockConst subghz_protocol_nero_sketch_const = {\n    .te_short = 330,\n    .te_long = 660,\n    .te_delta = 150,\n    .min_count_bit_for_found = 40,\n};\n\nstruct SubGhzProtocolDecoderNeroSketch {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderNeroSketch {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    NeroSketchDecoderStepReset = 0,\n    NeroSketchDecoderStepCheckPreambula,\n    NeroSketchDecoderStepSaveDuration,\n    NeroSketchDecoderStepCheckDuration,\n} NeroSketchDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder = {\n    .alloc = subghz_protocol_decoder_nero_sketch_alloc,\n    .free = subghz_protocol_decoder_nero_sketch_free,\n\n    .feed = subghz_protocol_decoder_nero_sketch_feed,\n    .reset = subghz_protocol_decoder_nero_sketch_reset,\n\n    .get_hash_data = subghz_protocol_decoder_nero_sketch_get_hash_data,\n    .serialize = subghz_protocol_decoder_nero_sketch_serialize,\n    .deserialize = subghz_protocol_decoder_nero_sketch_deserialize,\n    .get_string = subghz_protocol_decoder_nero_sketch_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder = {\n    .alloc = subghz_protocol_encoder_nero_sketch_alloc,\n    .free = subghz_protocol_encoder_nero_sketch_free,\n\n    .deserialize = subghz_protocol_encoder_nero_sketch_deserialize,\n    .stop = subghz_protocol_encoder_nero_sketch_stop,\n    .yield = subghz_protocol_encoder_nero_sketch_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_nero_sketch = {\n    .name = SUBGHZ_PROTOCOL_NERO_SKETCH_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_nero_sketch_decoder,\n    .encoder = &subghz_protocol_nero_sketch_encoder,\n};\n\nvoid* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolEncoderNeroSketch));\n\n    instance->base.protocol = &subghz_protocol_nero_sketch;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_nero_sketch_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNeroSketch* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderNeroSketch instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_nero_sketch_get_upload(SubGhzProtocolEncoderNeroSketch* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = 47 * 2 + 2 + (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    for(uint8_t i = 0; i < 47; i++) {\n        instance->encoder.upload[index++] =\n            level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n        instance->encoder.upload[index++] =\n            level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n    }\n\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 4);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_long);\n        }\n    }\n\n    //Send stop bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 3);\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNeroSketch* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_nero_sketch_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_nero_sketch_stop(void* context) {\n    SubGhzProtocolEncoderNeroSketch* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context) {\n    SubGhzProtocolEncoderNeroSketch* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolDecoderNeroSketch));\n    instance->base.protocol = &subghz_protocol_nero_sketch;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_nero_sketch_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_nero_sketch_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n    instance->decoder.parser_step = NeroSketchDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case NeroSketchDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <\n                       subghz_protocol_nero_sketch_const.te_delta)) {\n            instance->decoder.parser_step = NeroSketchDecoderStepCheckPreambula;\n            instance->decoder.te_last = duration;\n            instance->header_count = 0;\n        }\n        break;\n    case NeroSketchDecoderStepCheckPreambula:\n        if(level) {\n            if((DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <\n                subghz_protocol_nero_sketch_const.te_delta) ||\n               (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short * 4) <\n                subghz_protocol_nero_sketch_const.te_delta)) {\n                instance->decoder.te_last = duration;\n            } else {\n                instance->decoder.parser_step = NeroSketchDecoderStepReset;\n            }\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <\n            subghz_protocol_nero_sketch_const.te_delta) {\n            if(DURATION_DIFF(\n                   instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) <\n               subghz_protocol_nero_sketch_const.te_delta) {\n                // Found header\n                instance->header_count++;\n                break;\n            } else if(\n                DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short * 4) <\n                subghz_protocol_nero_sketch_const.te_delta) {\n                // Found start bit\n                if(instance->header_count > 40) {\n                    instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;\n                    instance->decoder.decode_data = 0;\n                    instance->decoder.decode_count_bit = 0;\n                } else {\n                    instance->decoder.parser_step = NeroSketchDecoderStepReset;\n                }\n            } else {\n                instance->decoder.parser_step = NeroSketchDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = NeroSketchDecoderStepReset;\n        }\n        break;\n    case NeroSketchDecoderStepSaveDuration:\n        if(level) {\n            if(duration >= (subghz_protocol_nero_sketch_const.te_short * 2 +\n                            subghz_protocol_nero_sketch_const.te_delta * 2)) {\n                //Found stop bit\n                instance->decoder.parser_step = NeroSketchDecoderStepReset;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_nero_sketch_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = NeroSketchDecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = NeroSketchDecoderStepReset;\n        }\n        break;\n    case NeroSketchDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) <\n                subghz_protocol_nero_sketch_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_long) <\n                subghz_protocol_nero_sketch_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_long) <\n                 subghz_protocol_nero_sketch_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <\n                 subghz_protocol_nero_sketch_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = NeroSketchDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = NeroSketchDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_nero_sketch_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_nero_sketch_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNeroSketch* instance = context;\n\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    uint32_t code_found_reverse_hi = code_found_reverse >> 32;\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Yek:0x%lX%08lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        code_found_reverse_hi,\n        code_found_reverse_lo);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/nero_sketch.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_NERO_SKETCH_NAME \"Nero Sketch\"\n\ntypedef struct SubGhzProtocolDecoderNeroSketch SubGhzProtocolDecoderNeroSketch;\ntypedef struct SubGhzProtocolEncoderNeroSketch SubGhzProtocolEncoderNeroSketch;\n\nextern const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder;\nextern const SubGhzProtocol subghz_protocol_nero_sketch;\n\n/**\n * Allocate SubGhzProtocolEncoderNeroSketch.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderNeroSketch* pointer to a SubGhzProtocolEncoderNeroSketch instance\n */\nvoid* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderNeroSketch.\n * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance\n */\nvoid subghz_protocol_encoder_nero_sketch_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance\n */\nvoid subghz_protocol_encoder_nero_sketch_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderNeroSketch.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderNeroSketch* pointer to a SubGhzProtocolDecoderNeroSketch instance\n */\nvoid* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderNeroSketch.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n */\nvoid subghz_protocol_decoder_nero_sketch_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderNeroSketch.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n */\nvoid subghz_protocol_decoder_nero_sketch_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderNeroSketch.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_nero_sketch_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderNeroSketch.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/nice_flo.c",
    "content": "#include \"nice_flo.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolNiceFlo\"\n\nstatic const SubGhzBlockConst subghz_protocol_nice_flo_const = {\n    .te_short = 700,\n    .te_long = 1400,\n    .te_delta = 200,\n    .min_count_bit_for_found = 12,\n};\n\nstruct SubGhzProtocolDecoderNiceFlo {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderNiceFlo {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    NiceFloDecoderStepReset = 0,\n    NiceFloDecoderStepFoundStartBit,\n    NiceFloDecoderStepSaveDuration,\n    NiceFloDecoderStepCheckDuration,\n} NiceFloDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder = {\n    .alloc = subghz_protocol_decoder_nice_flo_alloc,\n    .free = subghz_protocol_decoder_nice_flo_free,\n\n    .feed = subghz_protocol_decoder_nice_flo_feed,\n    .reset = subghz_protocol_decoder_nice_flo_reset,\n\n    .get_hash_data = subghz_protocol_decoder_nice_flo_get_hash_data,\n    .serialize = subghz_protocol_decoder_nice_flo_serialize,\n    .deserialize = subghz_protocol_decoder_nice_flo_deserialize,\n    .get_string = subghz_protocol_decoder_nice_flo_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder = {\n    .alloc = subghz_protocol_encoder_nice_flo_alloc,\n    .free = subghz_protocol_encoder_nice_flo_free,\n\n    .deserialize = subghz_protocol_encoder_nice_flo_deserialize,\n    .stop = subghz_protocol_encoder_nice_flo_stop,\n    .yield = subghz_protocol_encoder_nice_flo_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_nice_flo = {\n    .name = SUBGHZ_PROTOCOL_NICE_FLO_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_nice_flo_decoder,\n    .encoder = &subghz_protocol_nice_flo_encoder,\n};\n\nvoid* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlo));\n\n    instance->base.protocol = &subghz_protocol_nice_flo;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_nice_flo_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNiceFlo* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderNiceFlo instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNiceFlo* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short * 36);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderNiceFlo* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if((instance->generic.data_count_bit <\n            subghz_protocol_nice_flo_const.min_count_bit_for_found) ||\n           (instance->generic.data_count_bit >\n            2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_nice_flo_stop(void* context) {\n    SubGhzProtocolEncoderNiceFlo* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_nice_flo_yield(void* context) {\n    SubGhzProtocolEncoderNiceFlo* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlo));\n    instance->base.protocol = &subghz_protocol_nice_flo;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_nice_flo_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_nice_flo_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n    instance->decoder.parser_step = NiceFloDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case NiceFloDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short * 36) <\n                        subghz_protocol_nice_flo_const.te_delta * 36)) {\n            //Found header Nice Flo\n            instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit;\n        }\n        break;\n    case NiceFloDecoderStepFoundStartBit:\n        if(!level) {\n            break;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) <\n            subghz_protocol_nice_flo_const.te_delta) {\n            //Found start bit Nice Flo\n            instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = NiceFloDecoderStepReset;\n        }\n        break;\n    case NiceFloDecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(duration >= (subghz_protocol_nice_flo_const.te_short * 4)) {\n                instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_nice_flo_const.min_count_bit_for_found) {\n                    instance->generic.serial = 0x0;\n                    instance->generic.btn = 0x0;\n\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            }\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = NiceFloDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = NiceFloDecoderStepReset;\n        }\n        break;\n    case NiceFloDecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_short) <\n                subghz_protocol_nice_flo_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_long) <\n                subghz_protocol_nice_flo_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_long) <\n                 subghz_protocol_nice_flo_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) <\n                 subghz_protocol_nice_flo_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = NiceFloDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = NiceFloDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_nice_flo_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if((instance->generic.data_count_bit <\n            subghz_protocol_nice_flo_const.min_count_bit_for_found) ||\n           (instance->generic.data_count_bit >\n            2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlo* instance = context;\n\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%08lX\\r\\n\"\n        \"Yek:0x%08lX\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_lo,\n        code_found_reverse_lo);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/nice_flo.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_NICE_FLO_NAME \"Nice FLO\"\n\ntypedef struct SubGhzProtocolDecoderNiceFlo SubGhzProtocolDecoderNiceFlo;\ntypedef struct SubGhzProtocolEncoderNiceFlo SubGhzProtocolEncoderNiceFlo;\n\nextern const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder;\nextern const SubGhzProtocol subghz_protocol_nice_flo;\n\n/**\n * Allocate SubGhzProtocolEncoderNiceFlo.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderNiceFlo* pointer to a SubGhzProtocolEncoderNiceFlo instance\n */\nvoid* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderNiceFlo.\n * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance\n */\nvoid subghz_protocol_encoder_nice_flo_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance\n */\nvoid subghz_protocol_encoder_nice_flo_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_nice_flo_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderNiceFlo.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderNiceFlo* pointer to a SubGhzProtocolDecoderNiceFlo instance\n */\nvoid* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderNiceFlo.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n */\nvoid subghz_protocol_decoder_nice_flo_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderNiceFlo.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n */\nvoid subghz_protocol_decoder_nice_flo_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderNiceFlo.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_nice_flo_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderNiceFlo.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/nice_flor_s.c",
    "content": "#include \"nice_flor_s.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * https://phreakerclub.com/1615\n * https://phreakerclub.com/forum/showthread.php?t=2360\n * https://vrtp.ru/index.php?showtopic=27867\n */\n\n#define TAG \"SubGhzProtocoNiceFlorS\"\n\n#define NICE_ONE_COUNT_BIT 72\n#define NICE_ONE_NAME      \"Nice One\"\n\nstatic const SubGhzBlockConst subghz_protocol_nice_flor_s_const = {\n    .te_short = 500,\n    .te_long = 1000,\n    .te_delta = 300,\n    .min_count_bit_for_found = 52,\n};\n\nstruct SubGhzProtocolDecoderNiceFlorS {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    const char* nice_flor_s_rainbow_table_file_name;\n    uint64_t data;\n};\n\nstruct SubGhzProtocolEncoderNiceFlorS {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    NiceFlorSDecoderStepReset = 0,\n    NiceFlorSDecoderStepCheckHeader,\n    NiceFlorSDecoderStepFoundHeader,\n    NiceFlorSDecoderStepSaveDuration,\n    NiceFlorSDecoderStepCheckDuration,\n} NiceFlorSDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder = {\n    .alloc = subghz_protocol_decoder_nice_flor_s_alloc,\n    .free = subghz_protocol_decoder_nice_flor_s_free,\n\n    .feed = subghz_protocol_decoder_nice_flor_s_feed,\n    .reset = subghz_protocol_decoder_nice_flor_s_reset,\n\n    .get_hash_data = subghz_protocol_decoder_nice_flor_s_get_hash_data,\n    .serialize = subghz_protocol_decoder_nice_flor_s_serialize,\n    .deserialize = subghz_protocol_decoder_nice_flor_s_deserialize,\n    .get_string = subghz_protocol_decoder_nice_flor_s_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_nice_flor_s = {\n    .name = SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_nice_flor_s_decoder,\n    .encoder = &subghz_protocol_nice_flor_s_encoder,\n};\n\n// /**\n//  * Read bytes from rainbow table\n//  * @param p array[10]  P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10\n//  * @return crc\n//  */\n// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) {\n//     uint8_t crc = 0;\n//     uint8_t crc_data = 0xff;\n//     for(uint8_t i = 4; i < 68; i++) {\n//         if(subghz_protocol_blocks_get_bit_array(p, i)) {\n//             crc = crc_data ^ 1;\n//         } else {\n//             crc = crc_data;\n//         }\n//         crc_data >>= 1;\n//         if((crc & 0x01)) {\n//             crc_data ^= 0x97;\n//         }\n//     }\n//     crc = 0;\n//     for(uint8_t i = 0; i < 8; i++) {\n//         crc <<= 1;\n//         if((crc_data >> i) & 0x01) crc = crc | 1;\n//     }\n//     return crc;\n// }\n\n// /**\n//  * Read bytes from rainbow table\n//  * @param p array[10]  P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX\n//  * @param num_parcel  parcel number 0..15\n//  * @param hold_bit  0 - the button was only pressed, 1 - the button was held down\n//  */\n// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) {\n//     uint8_t k = 0;\n//     uint8_t crc = 0;\n//     p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4);\n//     if(num_parcel < 4) {\n//         k = 0x8f;\n//     } else {\n//         k = 0x80;\n//     }\n\n//     if(!hold_bit) {\n//         hold_bit = 0;\n//     } else {\n//         hold_bit = 0x10;\n//     }\n//     k = num_parcel ^ k;\n//     p[7] = k;\n//     p[8] = hold_bit ^ (k << 4);\n\n//     crc = subghz_protocol_nice_one_crc(p);\n\n//     p[8] |= crc >> 4;\n//     p[9] = crc << 4;\n// }\n\n/** \n * Read bytes from rainbow table\n * @param file_name Full path to rainbow table the file \n * @param address Byte address in file\n * @return data\n */\nstatic uint8_t\n    subghz_protocol_nice_flor_s_get_byte_in_file(const char* file_name, uint32_t address) {\n    if(!file_name) return 0;\n\n    uint8_t buffer[1] = {0};\n    if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint8_t))) {\n        return buffer[0];\n    } else {\n        return 0;\n    }\n}\n\nstatic inline void subghz_protocol_decoder_nice_flor_s_magic_xor(uint8_t* p, uint8_t k) {\n    for(uint8_t i = 1; i < 6; i++) {\n        p[i] ^= k;\n    }\n}\n\nuint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name) {\n    uint8_t* p = (uint8_t*)&data;\n\n    uint8_t k = 0;\n    for(uint8_t y = 0; y < 2; y++) {\n        k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f);\n        subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);\n\n        p[5] &= 0x0f;\n        p[0] ^= k & 0xe0;\n        k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25;\n        subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);\n\n        p[5] &= 0x0f;\n        p[0] ^= k & 0x7;\n        if(y == 0) {\n            k = p[0];\n            p[0] = p[1];\n            p[1] = k;\n        }\n    }\n\n    p[5] = ~p[5] & 0x0f;\n    k = ~p[4];\n    p[4] = ~p[0];\n    p[0] = ~p[2];\n    p[2] = k;\n    k = ~p[3];\n    p[3] = ~p[1];\n    p[1] = k;\n\n    return data;\n}\n\nstatic uint64_t\n    subghz_protocol_nice_flor_s_decrypt(SubGhzBlockGeneric* instance, const char* file_name) {\n    furi_assert(instance);\n    uint64_t data = instance->data;\n    uint8_t* p = (uint8_t*)&data;\n\n    uint8_t k = 0;\n\n    k = ~p[4];\n    p[5] = ~p[5];\n    p[4] = ~p[2];\n    p[2] = ~p[0];\n    p[0] = k;\n    k = ~p[3];\n    p[3] = ~p[1];\n    p[1] = k;\n\n    for(uint8_t y = 0; y < 2; y++) {\n        k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25;\n        subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);\n\n        p[5] &= 0x0f;\n        p[0] ^= k & 0x7;\n        k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f);\n        subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);\n\n        p[5] &= 0x0f;\n        p[0] ^= k & 0xe0;\n\n        if(y == 0) {\n            k = p[0];\n            p[0] = p[1];\n            p[1] = k;\n        }\n    }\n    return data;\n}\n\nvoid* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlorS));\n    instance->base.protocol = &subghz_protocol_nice_flor_s;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->nice_flor_s_rainbow_table_file_name =\n        subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment);\n    if(instance->nice_flor_s_rainbow_table_file_name) {\n        FURI_LOG_I(\n            TAG, \"Loading rainbow table from %s\", instance->nice_flor_s_rainbow_table_file_name);\n    }\n    return instance;\n}\n\nvoid subghz_protocol_decoder_nice_flor_s_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n    instance->nice_flor_s_rainbow_table_file_name = NULL;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_nice_flor_s_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n    instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case NiceFlorSDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 38) <\n                        subghz_protocol_nice_flor_s_const.te_delta * 38)) {\n            //Found start header Nice Flor-S\n            instance->decoder.parser_step = NiceFlorSDecoderStepCheckHeader;\n        }\n        break;\n    case NiceFlorSDecoderStepCheckHeader:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <\n                       subghz_protocol_nice_flor_s_const.te_delta * 3)) {\n            //Found next header Nice Flor-S\n            instance->decoder.parser_step = NiceFlorSDecoderStepFoundHeader;\n        } else {\n            instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n        }\n        break;\n    case NiceFlorSDecoderStepFoundHeader:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <\n                        subghz_protocol_nice_flor_s_const.te_delta * 3)) {\n            //Found header Nice Flor-S\n            instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n        }\n        break;\n    case NiceFlorSDecoderStepSaveDuration:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <\n               subghz_protocol_nice_flor_s_const.te_delta) {\n                //Found STOP bit\n                instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n                if((instance->decoder.decode_count_bit ==\n                    subghz_protocol_nice_flor_s_const.min_count_bit_for_found) ||\n                   (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) {\n                    instance->generic.data = instance->data;\n                    instance->data = instance->decoder.decode_data;\n                    instance->decoder.decode_data = instance->generic.data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                break;\n            } else {\n                //save interval\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = NiceFlorSDecoderStepCheckDuration;\n            }\n        }\n        break;\n    case NiceFlorSDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_short) <\n                subghz_protocol_nice_flor_s_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_long) <\n                subghz_protocol_nice_flor_s_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_long) <\n                 subghz_protocol_nice_flor_s_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short) <\n                 subghz_protocol_nice_flor_s_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;\n            } else\n                instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n        } else {\n            instance->decoder.parser_step = NiceFlorSDecoderStepReset;\n        }\n        if(instance->decoder.decode_count_bit ==\n           subghz_protocol_nice_flor_s_const.min_count_bit_for_found) {\n            instance->data = instance->decoder.decode_data;\n            instance->decoder.decode_data = 0;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param file_name Full path to rainbow table the file \n */\nstatic void subghz_protocol_nice_flor_s_remote_controller(\n    SubGhzBlockGeneric* instance,\n    const char* file_name) {\n    /*\n    * Protocol Nice Flor-S\n    * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP\n    * P0 (4-bit)    - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8;\n    * P1 (4-bit)    - batch repetition number, calculated by the formula:\n    * P1 = 0xF ^ P0 ^ n; where n changes from 1 to 15, then 0, and then in a circle\n    * key 1: {0xE,0xF,0xC,0xD,0xA,0xB,0x8,0x9,0x6,0x7,0x4,0x5,0x2,0x3,0x0,0x1};\n    * key 2: {0xD,0xC,0xF,0xE,0x9,0x8,0xB,0xA,0x5,0x4,0x7,0x6,0x1,0x0,0x3,0x2};\n    * key 3: {0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC,0x3,0x2,0x1,0x0,0x7,0x6,0x5,0x4};\n    * key 4: {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8};\n    * P2 (4-bit)    - part of the serial number, P2 = (K ^ S3) & 0xF;\n    * P3 (byte)     - the major part of the encrypted index\n    * P4 (byte)     - the low-order part of the encrypted index\n    * P5 (byte)     - part of the serial number, P5 = K ^ S2;\n    * P6 (byte)     - part of the serial number, P6 = K ^ S1;\n    * P7 (byte)     - part of the serial number, P7 = K ^ S0;\n    * K (byte)      - depends on P3 and P4, K = Fk(P3, P4);\n    * S3,S2,S1,S0   - serial number of the console 28 bit.\n    *\n    * data    => 0x1c5783607f7b3     key  serial  cnt\n    * decrypt => 0x10436c6820444 => 0x1  0436c682 0444\n    * \n    * Protocol Nice One\n    * Generally repeats the Nice Flor-S protocol, but there are a few changes\n    * Packet format first 52 bytes repeat Nice Flor-S protocol\n    * The additional 20 bytes contain the code of the pressed button,\n    *    the button hold bit and the CRC of the entire message.\n    *       START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP\n    * P7 (byte)     - if (n<4) k=0x8f : k=0x80; P7= k^n;\n    * P8 (byte)     - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc\n    * P10 (4-bit)   - 4 lo bit crc \n    *                            key+b crc  \n    * data    => 0x1724A7D9A522F  899  D6 hold bit = 0 - just pressed the button\n    * data    => 0x1424A7D9A522F  8AB  03 hold bit = 1 - button hold\n    * \n    * A small button hold counter (0..15) is stored between each press,\n    *  i.e. if 1 press of the button stops counter 6, then the next press \n    *  of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)...\n    *  further up to 15 with overflow\n    * \n    */\n    if(!file_name) {\n        instance->cnt = 0;\n        instance->serial = 0;\n        instance->btn = 0;\n    } else {\n        uint64_t decrypt = subghz_protocol_nice_flor_s_decrypt(instance, file_name);\n        instance->cnt = decrypt & 0xFFFF;\n        instance->serial = (decrypt >> 16) & 0xFFFFFFF;\n        instance->btn = (decrypt >> 48) & 0xF;\n    }\n}\n\nuint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_nice_flor_s_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {\n        if((ret == SubGhzProtocolStatusOk) &&\n           !flipper_format_write_uint32(flipper_format, \"Data\", (uint32_t*)&instance->data, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Data\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n        }\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if((instance->generic.data_count_bit !=\n            subghz_protocol_nice_flor_s_const.min_count_bit_for_found) &&\n           (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) {\n            FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n            ret = SubGhzProtocolStatusErrorValueBitCount;\n            break;\n        }\n        if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {\n            if(!flipper_format_rewind(flipper_format)) {\n                FURI_LOG_E(TAG, \"Rewind error\");\n                ret = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            uint32_t temp = 0;\n            if(!flipper_format_read_uint32(flipper_format, \"Data\", (uint32_t*)&temp, 1)) {\n                FURI_LOG_E(TAG, \"Missing Data\");\n                ret = SubGhzProtocolStatusErrorParserOthers;\n                break;\n            }\n            instance->data = (uint64_t)temp;\n        }\n    } while(false);\n    return ret;\n}\n\nvoid subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderNiceFlorS* instance = context;\n\n    subghz_protocol_nice_flor_s_remote_controller(\n        &instance->generic, instance->nice_flor_s_rainbow_table_file_name);\n\n    if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {\n        furi_string_cat_printf(\n            output,\n            \"%s %dbit\\r\\n\"\n            \"Key:0x%013llX%llX\\r\\n\"\n            \"Sn:%05lX\\r\\n\"\n            \"Cnt:%04lX Btn:%02X\\r\\n\",\n            NICE_ONE_NAME,\n            instance->generic.data_count_bit,\n            instance->generic.data,\n            instance->data,\n            instance->generic.serial,\n            instance->generic.cnt,\n            instance->generic.btn);\n    } else {\n        furi_string_cat_printf(\n            output,\n            \"%s %dbit\\r\\n\"\n            \"Key:0x%013llX\\r\\n\"\n            \"Sn:%05lX\\r\\n\"\n            \"Cnt:%04lX Btn:%02X\\r\\n\",\n            instance->generic.protocol_name,\n            instance->generic.data_count_bit,\n            instance->generic.data,\n            instance->generic.serial,\n            instance->generic.cnt,\n            instance->generic.btn);\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/nice_flor_s.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME \"Nice FloR-S\"\n\ntypedef struct SubGhzProtocolDecoderNiceFlorS SubGhzProtocolDecoderNiceFlorS;\ntypedef struct SubGhzProtocolEncoderNiceFlorS SubGhzProtocolEncoderNiceFlorS;\n\nextern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder;\nextern const SubGhzProtocol subghz_protocol_nice_flor_s;\n\n/**\n * Allocate SubGhzProtocolDecoderNiceFlorS.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderNiceFlorS* pointer to a SubGhzProtocolDecoderNiceFlorS instance\n */\nvoid* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderNiceFlorS.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n */\nvoid subghz_protocol_decoder_nice_flor_s_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderNiceFlorS.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n */\nvoid subghz_protocol_decoder_nice_flor_s_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderNiceFlorS.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_nice_flor_s_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderNiceFlorS.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/phoenix_v2.c",
    "content": "#include \"phoenix_v2.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolPhoenixV2\"\n//transmission only static mode\n\nstatic const SubGhzBlockConst subghz_protocol_phoenix_v2_const = {\n    .te_short = 427,\n    .te_long = 853,\n    .te_delta = 100,\n    .min_count_bit_for_found = 52,\n};\n\nstruct SubGhzProtocolDecoderPhoenix_V2 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderPhoenix_V2 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Phoenix_V2DecoderStepReset = 0,\n    Phoenix_V2DecoderStepFoundStartBit,\n    Phoenix_V2DecoderStepSaveDuration,\n    Phoenix_V2DecoderStepCheckDuration,\n} Phoenix_V2DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = {\n    .alloc = subghz_protocol_decoder_phoenix_v2_alloc,\n    .free = subghz_protocol_decoder_phoenix_v2_free,\n\n    .feed = subghz_protocol_decoder_phoenix_v2_feed,\n    .reset = subghz_protocol_decoder_phoenix_v2_reset,\n\n    .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data,\n    .serialize = subghz_protocol_decoder_phoenix_v2_serialize,\n    .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize,\n    .get_string = subghz_protocol_decoder_phoenix_v2_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = {\n    .alloc = subghz_protocol_encoder_phoenix_v2_alloc,\n    .free = subghz_protocol_encoder_phoenix_v2_free,\n\n    .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize,\n    .stop = subghz_protocol_encoder_phoenix_v2_stop,\n    .yield = subghz_protocol_encoder_phoenix_v2_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_phoenix_v2 = {\n    .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_phoenix_v2_decoder,\n    .encoder = &subghz_protocol_phoenix_v2_encoder,\n};\n\nvoid* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2));\n\n    instance->base.protocol = &subghz_protocol_phoenix_v2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_phoenix_v2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPhoenix_V2* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n// Pre define functions\nstatic void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance);\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60);\n    //Send start bit\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6);\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(!bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);\n        }\n    }\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPhoenix_V2* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_phoenix_v2_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_phoenix_v2_stop(void* context) {\n    SubGhzProtocolEncoderPhoenix_V2* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) {\n    SubGhzProtocolEncoderPhoenix_V2* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2));\n    instance->base.protocol = &subghz_protocol_phoenix_v2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_phoenix_v2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_phoenix_v2_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    instance->decoder.parser_step = Phoenix_V2DecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case Phoenix_V2DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) <\n                        subghz_protocol_phoenix_v2_const.te_delta * 30)) {\n            //Found Preambula\n            instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;\n        }\n        break;\n    case Phoenix_V2DecoderStepFoundStartBit:\n        if(level && (DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) <\n                     subghz_protocol_phoenix_v2_const.te_delta * 4)) {\n            //Found start bit\n            instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n        } else {\n            instance->decoder.parser_step = Phoenix_V2DecoderStepReset;\n        }\n        break;\n    case Phoenix_V2DecoderStepSaveDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 +\n                            subghz_protocol_phoenix_v2_const.te_delta)) {\n                instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_phoenix_v2_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration;\n            }\n        }\n        break;\n    case Phoenix_V2DecoderStepCheckDuration:\n        if(level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) <\n                subghz_protocol_phoenix_v2_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) <\n                subghz_protocol_phoenix_v2_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) <\n                 subghz_protocol_phoenix_v2_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) <\n                 subghz_protocol_phoenix_v2_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = Phoenix_V2DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = Phoenix_V2DecoderStepReset;\n        }\n        break;\n    }\n}\n\nstatic uint16_t subghz_protocol_phoenix_v2_decrypt_counter(uint64_t full_key) {\n    uint16_t encrypted_value = (uint16_t)((full_key >> 40) & 0xFFFF);\n\n    uint8_t byte1 = (uint8_t)(encrypted_value >> 8); // First encrypted counter byte\n    uint8_t byte2 = (uint8_t)(encrypted_value & 0xFF); // Second encrypted counter byte\n\n    uint8_t xor_key1 = (uint8_t)(full_key >> 24); // First byte of serial\n    uint8_t xor_key2 = (uint8_t)((full_key >> 16) & 0xFF); // Second byte of serial\n\n    for(int i = 0; i < 16; i++) {\n        // Store the most significant bit (MSB) of byte1.\n        // The check `(msb_of_byte1 == 0)` will determine if we apply the XOR keys.\n        uint8_t msb_of_byte1 = byte1 & 0x80;\n\n        // Store the least significant bit (LSB) of byte2.\n        uint8_t lsb_of_byte2 = byte2 & 1;\n\n        // Perform a bit shuffle between the two bytes\n        byte2 = (byte2 >> 1) | msb_of_byte1;\n        byte1 = (byte1 << 1) | lsb_of_byte2;\n\n        // Conditionally apply the XOR keys based on the original MSB of byte1.\n        if(msb_of_byte1 == 0) {\n            byte1 ^= xor_key1;\n            // The mask `& 0x7F` clears the MSB of byte2 after the XOR.\n            byte2 = (byte2 ^ xor_key2) & 0x7F;\n        }\n    }\n\n    return (uint16_t)byte2 << 8 | byte1;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) {\n    // 2022.08 - @Skorpionm\n    // 2025.07 - @xMasterX & @RocketGod-git\n    // Fully supported now, with button switch and add manually\n    //\n    // Key samples\n    // Full key example: 0xC63E01B9615720 - after subghz_protocol_blocks_reverse_key was applied\n    // Serial - B9615720\n    // Button - 01\n    // Encrypted -> Decrypted counters\n    // C63E - 025C\n    // BCC1 - 025D\n    // 3341 - 025E\n    // 49BE - 025F\n    // 99D3 - 0260\n    // E32C - 0261\n\n    uint64_t data_rev =\n        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4);\n\n    instance->serial = data_rev & 0xFFFFFFFF;\n    instance->cnt = subghz_protocol_phoenix_v2_decrypt_counter(data_rev);\n    instance->btn = (data_rev >> 32) & 0xF;\n    // encrypted cnt is (data_rev >> 40) & 0xFFFF\n}\n\nuint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_phoenix_v2_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPhoenix_V2* instance = context;\n    subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic);\n    furi_string_cat_printf(\n        output,\n        \"V2 Phoenix %dbit\\r\\n\"\n        \"Key:%05lX%08lX\\r\\n\"\n        \"Sn:0x%07lX \\r\\n\"\n        \"Cnt: 0x%04lX\\r\\n\"\n        \"Btn: %X\\r\\n\",\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        instance->generic.cnt,\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/phoenix_v2.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME \"Phoenix_V2\"\n\ntypedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2;\ntypedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2;\n\nextern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder;\nextern const SubGhzProtocol subghz_protocol_phoenix_v2;\n\n/**\n * Allocate SubGhzProtocolEncoderPhoenix_V2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n */\nvoid* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderPhoenix_V2.\n * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n */\nvoid subghz_protocol_encoder_phoenix_v2_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n */\nvoid subghz_protocol_encoder_phoenix_v2_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderPhoenix_V2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n */\nvoid* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderPhoenix_V2.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n */\nvoid subghz_protocol_decoder_phoenix_v2_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderPhoenix_V2.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n */\nvoid subghz_protocol_decoder_phoenix_v2_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderPhoenix_V2.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderPhoenix_V2.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/power_smart.c",
    "content": "#include \"power_smart.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include <lib/toolbox/manchester_encoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolPowerSmart\"\n\n#define POWER_SMART_PACKET_HEADER      0xFD000000AA000000\n#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000\n\n#define CHANNEL_PATTERN \"%c%c%c%c%c%c\"\n#define CNT_TO_CHANNEL(dip)                                                             \\\n    (dip & 0x0001 ? '*' : '-'), (dip & 0x0002 ? '*' : '-'), (dip & 0x0004 ? '*' : '-'), \\\n        (dip & 0x0008 ? '*' : '-'), (dip & 0x0010 ? '*' : '-'), (dip & 0x0020 ? '*' : '-')\n\nstatic const SubGhzBlockConst subghz_protocol_power_smart_const = {\n    .te_short = 225,\n    .te_long = 450,\n    .te_delta = 100,\n    .min_count_bit_for_found = 64,\n};\n\nstruct SubGhzProtocolDecoderPowerSmart {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    ManchesterState manchester_saved_state;\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderPowerSmart {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    PowerSmartDecoderStepReset = 0,\n    PowerSmartDecoderFoundHeader,\n    PowerSmartDecoderStepDecoderData,\n} PowerSmartDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_power_smart_decoder = {\n    .alloc = subghz_protocol_decoder_power_smart_alloc,\n    .free = subghz_protocol_decoder_power_smart_free,\n\n    .feed = subghz_protocol_decoder_power_smart_feed,\n    .reset = subghz_protocol_decoder_power_smart_reset,\n\n    .get_hash_data = subghz_protocol_decoder_power_smart_get_hash_data,\n    .serialize = subghz_protocol_decoder_power_smart_serialize,\n    .deserialize = subghz_protocol_decoder_power_smart_deserialize,\n    .get_string = subghz_protocol_decoder_power_smart_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_power_smart_encoder = {\n    .alloc = subghz_protocol_encoder_power_smart_alloc,\n    .free = subghz_protocol_encoder_power_smart_free,\n\n    .deserialize = subghz_protocol_encoder_power_smart_deserialize,\n    .stop = subghz_protocol_encoder_power_smart_stop,\n    .yield = subghz_protocol_encoder_power_smart_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_power_smart = {\n    .name = SUBGHZ_PROTOCOL_POWER_SMART_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_power_smart_decoder,\n    .encoder = &subghz_protocol_power_smart_encoder,\n};\n\nvoid* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolEncoderPowerSmart));\n\n    instance->base.protocol = &subghz_protocol_power_smart;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 1024;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_power_smart_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPowerSmart* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic LevelDuration\n    subghz_protocol_encoder_power_smart_add_duration_to_upload(ManchesterEncoderResult result) {\n    LevelDuration data = {.duration = 0, .level = 0};\n    switch(result) {\n    case ManchesterEncoderResultShortLow:\n        data.duration = subghz_protocol_power_smart_const.te_short;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongLow:\n        data.duration = subghz_protocol_power_smart_const.te_long;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongHigh:\n        data.duration = subghz_protocol_power_smart_const.te_long;\n        data.level = true;\n        break;\n    case ManchesterEncoderResultShortHigh:\n        data.duration = subghz_protocol_power_smart_const.te_short;\n        data.level = true;\n        break;\n\n    default:\n        furi_crash(\"SubGhz: ManchesterEncoderResult is incorrect.\");\n        break;\n    }\n    return level_duration_make(data.level, data.duration);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderPowerSmart instance\n */\nstatic void\n    subghz_protocol_encoder_power_smart_get_upload(SubGhzProtocolEncoderPowerSmart* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    ManchesterEncoderState enc_state;\n    manchester_encoder_reset(&enc_state);\n    ManchesterEncoderResult result;\n\n    for(int i = 8; i > 0; i--) {\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(!manchester_encoder_advance(\n                   &enc_state, !bit_read(instance->generic.data, i - 1), &result)) {\n                instance->encoder.upload[index++] =\n                    subghz_protocol_encoder_power_smart_add_duration_to_upload(result);\n                manchester_encoder_advance(\n                    &enc_state, !bit_read(instance->generic.data, i - 1), &result);\n            }\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_power_smart_add_duration_to_upload(result);\n        }\n    }\n    instance->encoder.upload[index] = subghz_protocol_encoder_power_smart_add_duration_to_upload(\n        manchester_encoder_finish(&enc_state));\n    if(level_duration_get_level(instance->encoder.upload[index])) {\n        index++;\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_power_smart_const.te_long * 1111);\n    instance->encoder.size_upload = index;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* instance) {\n    /*\n    * Protocol: Manchester encoding, symbol rate ~2222.\n    * Packet Format: \n    *       0xFDXXXXYYAAZZZZWW where 0xFD and 0xAA sync word\n    *                           XXXX = ~ZZZZ, YY=(~WW)-1 \n    * Example:\n    *                               SYNC1 K1 CHANNEL DATA1   K2 DATA2    SYNC2  ~K1 ~CHANNEL ~DATA2  ~K2 (~DATA2)-1\n    *       0xFD2137ACAADEC852 => 11111101 0 010000 10011011 1 10101100 10101010  1  1011110 1100100  0  01010010\n    *       0xFDA137ACAA5EC852 => 11111101 1 010000 10011011 1 10101100 10101010  0  1011110 1100100  0  01010010\n    *       0xFDA136ACAA5EC952 => 11111101 1 010000 10011011 0 10101100 10101010  0  1011110 1100100  1  01010010\n    * \n    * Key:\n    *       K1K2\n    *        0 0 - key_unknown\n    *        0 1 - key_down\n    *        1 0 - key_up\n    *        1 1 - key_stop\n    *        \n    */\n\n    instance->btn = ((instance->data >> 54) & 0x02) | ((instance->data >> 40) & 0x1);\n    instance->serial = ((instance->data >> 33) & 0x3FFF00) | ((instance->data >> 32) & 0xFF);\n    instance->cnt = ((instance->data >> 49) & 0x3F);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPowerSmart* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_power_smart_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_power_smart_remote_controller(&instance->generic);\n        subghz_protocol_encoder_power_smart_get_upload(instance);\n        instance->encoder.front = 0; // reset before start\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_power_smart_stop(void* context) {\n    SubGhzProtocolEncoderPowerSmart* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_power_smart_yield(void* context) {\n    SubGhzProtocolEncoderPowerSmart* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolDecoderPowerSmart));\n    instance->base.protocol = &subghz_protocol_power_smart;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_power_smart_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_power_smart_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\nbool subghz_protocol_power_smart_chek_valid(uint64_t packet) {\n    uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF);\n    uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF);\n    uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF;\n    uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1);\n    return (data_1 == data_2) && (data_3 == data_4);\n}\n\nvoid subghz_protocol_decoder_power_smart_feed(\n    void* context,\n    bool level,\n    volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    ManchesterEvent event = ManchesterEventReset;\n    if(!level) {\n        if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) <\n           subghz_protocol_power_smart_const.te_delta) {\n            event = ManchesterEventShortLow;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) <\n            subghz_protocol_power_smart_const.te_delta * 2) {\n            event = ManchesterEventLongLow;\n        }\n    } else {\n        if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) <\n           subghz_protocol_power_smart_const.te_delta) {\n            event = ManchesterEventShortHigh;\n        } else if(\n            DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) <\n            subghz_protocol_power_smart_const.te_delta * 2) {\n            event = ManchesterEventLongHigh;\n        }\n    }\n    if(event != ManchesterEventReset) {\n        bool data;\n        bool data_ok = manchester_advance(\n            instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n        if(data_ok) {\n            instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;\n        }\n        if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==\n           POWER_SMART_PACKET_HEADER) {\n            if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {\n                instance->generic.data = instance->decoder.decode_data;\n                instance->generic.data_count_bit =\n                    subghz_protocol_power_smart_const.min_count_bit_for_found;\n                if(instance->base.callback)\n                    instance->base.callback(&instance->base, instance->base.context);\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n            }\n        }\n    } else {\n        instance->decoder.decode_data = 0;\n        instance->decoder.decode_count_bit = 0;\n        manchester_advance(\n            instance->manchester_saved_state,\n            ManchesterEventReset,\n            &instance->manchester_saved_state,\n            NULL);\n    }\n}\n\nstatic const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) {\n    btn &= 0x3;\n    const char* name_btn[0x4] = {\"Unknown\", \"Down\", \"Up\", \"Stop\"};\n    return name_btn[btn];\n}\n\nuint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_power_smart_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_power_smart_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPowerSmart* instance = context;\n    subghz_protocol_power_smart_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%07lX \\r\\n\"\n        \"Btn:%s\\r\\n\"\n        \"Channel:\" CHANNEL_PATTERN \"\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial,\n        subghz_protocol_power_smart_get_name_button(instance->generic.btn),\n        CNT_TO_CHANNEL(instance->generic.cnt));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/power_smart.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_POWER_SMART_NAME \"Power Smart\"\n\ntypedef struct SubGhzProtocolDecoderPowerSmart SubGhzProtocolDecoderPowerSmart;\ntypedef struct SubGhzProtocolEncoderPowerSmart SubGhzProtocolEncoderPowerSmart;\n\nextern const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder;\nextern const SubGhzProtocol subghz_protocol_power_smart;\n\n/**\n * Allocate SubGhzProtocolEncoderPowerSmart.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderPowerSmart* pointer to a SubGhzProtocolEncoderPowerSmart instance\n */\nvoid* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderPowerSmart.\n * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance\n */\nvoid subghz_protocol_encoder_power_smart_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance\n */\nvoid subghz_protocol_encoder_power_smart_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_power_smart_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderPowerSmart.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderPowerSmart* pointer to a SubGhzProtocolDecoderPowerSmart instance\n */\nvoid* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderPowerSmart.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n */\nvoid subghz_protocol_decoder_power_smart_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderPowerSmart.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n */\nvoid subghz_protocol_decoder_power_smart_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderPowerSmart.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_power_smart_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderPowerSmart.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/princeton.c",
    "content": "#include \"princeton.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://phreakerclub.com/447\n *\n */\n\n#define TAG \"SubGhzProtocolPrinceton\"\n\n#define PRINCETON_GUARD_TIME_DEFALUT 30 //GUARD_TIME = PRINCETON_GUARD_TIME_DEFALUT * TE\n// Guard Time value should be between 15 -> 72 otherwise default value will be used\n\nstatic const SubGhzBlockConst subghz_protocol_princeton_const = {\n    .te_short = 390,\n    .te_long = 1170,\n    .te_delta = 300,\n    .min_count_bit_for_found = 24,\n};\n\nstruct SubGhzProtocolDecoderPrinceton {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n    uint32_t last_data;\n    uint32_t guard_time;\n};\n\nstruct SubGhzProtocolEncoderPrinceton {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n    uint32_t guard_time;\n};\n\ntypedef enum {\n    PrincetonDecoderStepReset = 0,\n    PrincetonDecoderStepSaveDuration,\n    PrincetonDecoderStepCheckDuration,\n} PrincetonDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_princeton_decoder = {\n    .alloc = subghz_protocol_decoder_princeton_alloc,\n    .free = subghz_protocol_decoder_princeton_free,\n\n    .feed = subghz_protocol_decoder_princeton_feed,\n    .reset = subghz_protocol_decoder_princeton_reset,\n\n    .get_hash_data = subghz_protocol_decoder_princeton_get_hash_data,\n    .serialize = subghz_protocol_decoder_princeton_serialize,\n    .deserialize = subghz_protocol_decoder_princeton_deserialize,\n    .get_string = subghz_protocol_decoder_princeton_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_princeton_encoder = {\n    .alloc = subghz_protocol_encoder_princeton_alloc,\n    .free = subghz_protocol_encoder_princeton_free,\n\n    .deserialize = subghz_protocol_encoder_princeton_deserialize,\n    .stop = subghz_protocol_encoder_princeton_stop,\n    .yield = subghz_protocol_encoder_princeton_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_princeton = {\n    .name = SUBGHZ_PROTOCOL_PRINCETON_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |\n            SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_princeton_decoder,\n    .encoder = &subghz_protocol_princeton_encoder,\n};\n\nvoid* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderPrinceton* instance = malloc(sizeof(SubGhzProtocolEncoderPrinceton));\n\n    instance->base.protocol = &subghz_protocol_princeton;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_princeton_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPrinceton* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)instance->te * 3);\n            instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)instance->te * 3);\n        }\n    }\n\n    //Send Stop bit\n    instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n    //Send PT_GUARD_TIME\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)instance->te * instance->guard_time);\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderPrinceton* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_princeton_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n        //optional parameter parameter\n        if(!flipper_format_read_uint32(\n               flipper_format, \"Guard_time\", (uint32_t*)&instance->guard_time, 1)) {\n            instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n        } else {\n            // Guard Time value should be between 15 -> 72 otherwise default value will be used\n            if((instance->guard_time < 15) || (instance->guard_time > 72)) {\n                instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n            }\n        }\n\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_princeton_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_princeton_stop(void* context) {\n    SubGhzProtocolEncoderPrinceton* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_princeton_yield(void* context) {\n    SubGhzProtocolEncoderPrinceton* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderPrinceton* instance = malloc(sizeof(SubGhzProtocolDecoderPrinceton));\n    instance->base.protocol = &subghz_protocol_princeton;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_princeton_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_princeton_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    instance->decoder.parser_step = PrincetonDecoderStepReset;\n    instance->last_data = 0;\n}\n\nvoid subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case PrincetonDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short * 36) <\n                        subghz_protocol_princeton_const.te_delta * 36)) {\n            //Found Preambula\n            instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->te = 0;\n            instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n        }\n        break;\n    case PrincetonDecoderStepSaveDuration:\n        //save duration\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->te += duration;\n            instance->decoder.parser_step = PrincetonDecoderStepCheckDuration;\n        }\n        break;\n    case PrincetonDecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) {\n                instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_princeton_const.min_count_bit_for_found) {\n                    if((instance->last_data == instance->decoder.decode_data) &&\n                       instance->last_data) {\n                        instance->te /= (instance->decoder.decode_count_bit * 4 + 1);\n\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                        instance->guard_time = roundf((float)duration / instance->te);\n                        // Guard Time value should be between 15 -> 72 otherwise default value will be used\n                        if((instance->guard_time < 15) || (instance->guard_time > 72)) {\n                            instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n                        }\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->last_data = instance->decoder.decode_data;\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->te = 0;\n                break;\n            }\n\n            instance->te += duration;\n\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_short) <\n                subghz_protocol_princeton_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_long) <\n                subghz_protocol_princeton_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_long) <\n                 subghz_protocol_princeton_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short) <\n                 subghz_protocol_princeton_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = PrincetonDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = PrincetonDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) {\n    instance->serial = instance->data >> 4;\n    instance->btn = instance->data & 0xF;\n}\n\nuint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"TE\", &instance->te, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add TE\");\n        ret = SubGhzProtocolStatusErrorParserTe;\n    }\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"Guard_time\", &instance->guard_time, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add Guard_time\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_princeton_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n        if(!flipper_format_read_uint32(\n               flipper_format, \"Guard_time\", (uint32_t*)&instance->guard_time, 1)) {\n            instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n        } else {\n            // Guard Time value should be between 15 -> 72 otherwise default value will be used\n            if((instance->guard_time < 15) || (instance->guard_time > 72)) {\n                instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT;\n            }\n        }\n\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderPrinceton* instance = context;\n    subghz_protocol_princeton_check_remote_controller(&instance->generic);\n    uint32_t data_rev = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%08lX\\r\\n\"\n        \"Yek:0x%08lX\\r\\n\"\n        \"Sn:0x%05lX Btn:%01X\\r\\n\"\n        \"Te:%luus  GT:Te*%lu\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFF),\n        data_rev,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->te,\n        instance->guard_time);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/princeton.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_PRINCETON_NAME \"Princeton\"\n\ntypedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton;\ntypedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton;\n\nextern const SubGhzProtocolDecoder subghz_protocol_princeton_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_princeton_encoder;\nextern const SubGhzProtocol subghz_protocol_princeton;\n\n/**\n * Allocate SubGhzProtocolEncoderPrinceton.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderPrinceton* pointer to a SubGhzProtocolEncoderPrinceton instance\n */\nvoid* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderPrinceton.\n * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance\n */\nvoid subghz_protocol_encoder_princeton_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance\n */\nvoid subghz_protocol_encoder_princeton_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_princeton_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderPrinceton.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderPrinceton* pointer to a SubGhzProtocolDecoderPrinceton instance\n */\nvoid* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderPrinceton.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n */\nvoid subghz_protocol_decoder_princeton_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderPrinceton.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n */\nvoid subghz_protocol_decoder_princeton_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderPrinceton.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderPrinceton.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/protocol_items.c",
    "content": "#include \"protocol_items.h\" // IWYU pragma: keep\n\nconst SubGhzProtocol* const subghz_protocol_registry_items[] = {\n    &subghz_protocol_gate_tx,\n    &subghz_protocol_keeloq,\n    &subghz_protocol_star_line,\n    &subghz_protocol_nice_flo,\n    &subghz_protocol_came,\n    &subghz_protocol_faac_slh,\n    &subghz_protocol_nice_flor_s,\n    &subghz_protocol_came_twee,\n    &subghz_protocol_came_atomo,\n    &subghz_protocol_nero_sketch,\n    &subghz_protocol_ido,\n    &subghz_protocol_kia,\n    &subghz_protocol_hormann,\n    &subghz_protocol_nero_radio,\n    &subghz_protocol_somfy_telis,\n    &subghz_protocol_somfy_keytis,\n    &subghz_protocol_scher_khan,\n    &subghz_protocol_princeton,\n    &subghz_protocol_raw,\n    &subghz_protocol_linear,\n    &subghz_protocol_secplus_v2,\n    &subghz_protocol_secplus_v1,\n    &subghz_protocol_megacode,\n    &subghz_protocol_holtek,\n    &subghz_protocol_chamb_code,\n    &subghz_protocol_power_smart,\n    &subghz_protocol_marantec,\n    &subghz_protocol_bett,\n    &subghz_protocol_doitrand,\n    &subghz_protocol_phoenix_v2,\n    &subghz_protocol_honeywell_wdb,\n    &subghz_protocol_magellan,\n    &subghz_protocol_intertechno_v3,\n    &subghz_protocol_clemsa,\n    &subghz_protocol_ansonic,\n    &subghz_protocol_smc5326,\n    &subghz_protocol_holtek_th12x,\n    &subghz_protocol_linear_delta3,\n    &subghz_protocol_dooya,\n    &subghz_protocol_alutech_at_4n,\n    &subghz_protocol_kinggates_stylo_4k,\n    &subghz_protocol_bin_raw,\n    &subghz_protocol_mastercode,\n    &subghz_protocol_legrand,\n    &subghz_protocol_dickert_mahs,\n    &subghz_protocol_gangqi,\n    &subghz_protocol_marantec24,\n    &subghz_protocol_hollarm,\n    &subghz_protocol_hay21,\n    &subghz_protocol_revers_rb2,\n    &subghz_protocol_feron,\n    &subghz_protocol_roger,\n};\n\nconst SubGhzProtocolRegistry subghz_protocol_registry = {\n    .items = subghz_protocol_registry_items,\n    .size = COUNT_OF(subghz_protocol_registry_items)};\n"
  },
  {
    "path": "lib/subghz/protocols/protocol_items.h",
    "content": "#pragma once\n#include \"../registry.h\"\n#include \"../subghz_protocol_registry.h\"\n\n#include \"princeton.h\"\n#include \"keeloq.h\"\n#include \"star_line.h\"\n#include \"nice_flo.h\"\n#include \"came.h\"\n#include \"faac_slh.h\"\n#include \"nice_flor_s.h\"\n#include \"came_twee.h\"\n#include \"came_atomo.h\"\n#include \"nero_sketch.h\"\n#include \"ido.h\"\n#include \"kia.h\"\n#include \"hormann.h\"\n#include \"nero_radio.h\"\n#include \"somfy_telis.h\"\n#include \"somfy_keytis.h\"\n#include \"scher_khan.h\"\n#include \"gate_tx.h\"\n#include \"raw.h\"\n#include \"linear.h\"\n#include \"linear_delta3.h\"\n#include \"secplus_v2.h\"\n#include \"secplus_v1.h\"\n#include \"megacode.h\"\n#include \"holtek.h\"\n#include \"chamberlain_code.h\"\n#include \"power_smart.h\"\n#include \"marantec.h\"\n#include \"bett.h\"\n#include \"doitrand.h\"\n#include \"phoenix_v2.h\"\n#include \"honeywell_wdb.h\"\n#include \"magellan.h\"\n#include \"intertechno_v3.h\"\n#include \"clemsa.h\"\n#include \"ansonic.h\"\n#include \"smc5326.h\"\n#include \"holtek_ht12x.h\"\n#include \"dooya.h\"\n#include \"alutech_at_4n.h\"\n#include \"kinggates_stylo_4k.h\"\n#include \"bin_raw.h\"\n#include \"mastercode.h\"\n#include \"legrand.h\"\n#include \"dickert_mahs.h\"\n#include \"gangqi.h\"\n#include \"marantec24.h\"\n#include \"hollarm.h\"\n#include \"hay21.h\"\n#include \"revers_rb2.h\"\n#include \"feron.h\"\n#include \"roger.h\"\n"
  },
  {
    "path": "lib/subghz/protocols/public_api.h",
    "content": "#pragma once\n\n#include \"../types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Key generation from simple data.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param serial Serial number, 32 bit\n * @param btn Button number, 8 bit\n * @param cnt Container value, 28 bit\n * @param manufacture_name Name of manufacturer's key\n * @param preset Modulation, SubGhzRadioPreset\n * @return true On success\n */\nbool subghz_protocol_secplus_v2_create_data(\n    void* context,\n    FlipperFormat* flipper_format,\n    uint32_t serial,\n    uint8_t btn,\n    uint32_t cnt,\n    SubGhzRadioPreset* preset);\n\n/**\n * Key generation from simple data.\n * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param serial Serial number, 28 bit\n * @param btn Button number, 4 bit\n * @param cnt Counter value, 16 bit\n * @param manufacture_name Name of manufacturer's key\n * @param preset Modulation, SubGhzRadioPreset\n * @return true On success\n */\nbool subghz_protocol_keeloq_create_data(\n    void* context,\n    FlipperFormat* flipper_format,\n    uint32_t serial,\n    uint8_t btn,\n    uint16_t cnt,\n    const char* manufacture_name,\n    SubGhzRadioPreset* preset);\n\ntypedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;\n\nvoid subghz_protocol_decoder_bin_raw_data_input_rssi(\n    SubGhzProtocolDecoderBinRAW* instance,\n    float rssi);\n\n/**\n * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1.\n * @param fixed fixed parts\n * @return true On success\n */\nbool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/protocols/raw.c",
    "content": "#include \"raw.h\"\n#include <lib/flipper_format/flipper_format.h>\n#include \"../subghz_file_encoder_worker.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/generic.h\"\n\n#include <flipper_format/flipper_format_i.h>\n#include <lib/toolbox/stream/stream.h>\n\n#define TAG \"SubGhzProtocolRaw\"\n\n#define SUBGHZ_DOWNLOAD_MAX_SIZE 512\n\nstatic const SubGhzBlockConst subghz_protocol_raw_const = {\n    .te_short = 50,\n    .te_long = 32700,\n    .te_delta = 0,\n    .min_count_bit_for_found = 0,\n};\n\nstruct SubGhzProtocolDecoderRAW {\n    SubGhzProtocolDecoderBase base;\n\n    int32_t* upload_raw;\n    uint16_t ind_write;\n    Storage* storage;\n    FlipperFormat* flipper_file;\n    uint32_t file_is_open;\n    FuriString* file_name;\n    size_t sample_write;\n    bool last_level;\n    bool pause;\n};\n\nstruct SubGhzProtocolEncoderRAW {\n    SubGhzProtocolEncoderBase base;\n\n    bool is_running;\n    FuriString* file_name;\n    FuriString* radio_device_name;\n    SubGhzFileEncoderWorker* file_worker_encoder;\n};\n\ntypedef enum {\n    RAWFileIsOpenClose = 0,\n    RAWFileIsOpenWrite,\n    RAWFileIsOpenRead,\n} RAWFilIsOpen;\n\nconst SubGhzProtocolDecoder subghz_protocol_raw_decoder = {\n    .alloc = subghz_protocol_decoder_raw_alloc,\n    .free = subghz_protocol_decoder_raw_free,\n\n    .feed = subghz_protocol_decoder_raw_feed,\n    .reset = subghz_protocol_decoder_raw_reset,\n\n    .get_hash_data = NULL,\n    .serialize = NULL,\n    .deserialize = subghz_protocol_decoder_raw_deserialize,\n    .get_string = subghz_protocol_decoder_raw_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_raw_encoder = {\n    .alloc = subghz_protocol_encoder_raw_alloc,\n    .free = subghz_protocol_encoder_raw_free,\n\n    .deserialize = subghz_protocol_encoder_raw_deserialize,\n    .stop = subghz_protocol_encoder_raw_stop,\n    .yield = subghz_protocol_encoder_raw_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_raw = {\n    .name = SUBGHZ_PROTOCOL_RAW_NAME,\n    .type = SubGhzProtocolTypeRAW,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_RAW |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_raw_decoder,\n    .encoder = &subghz_protocol_raw_encoder,\n};\n\nbool subghz_protocol_raw_save_to_file_init(\n    SubGhzProtocolDecoderRAW* instance,\n    const char* dev_name,\n    SubGhzRadioPreset* preset) {\n    furi_check(instance);\n\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    instance->flipper_file = flipper_format_file_alloc(instance->storage);\n\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    bool init = false;\n\n    do {\n        // Create subghz folder directory if necessary\n        if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) {\n            break;\n        }\n        // Create saved directory if necessary\n        if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) {\n            break;\n        }\n\n        furi_string_set(instance->file_name, dev_name);\n        // First remove subghz device file if it was saved\n        furi_string_printf(\n            temp_str, \"%s/%s%s\", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_FILENAME_EXTENSION);\n\n        if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) {\n            break;\n        }\n\n        // Open file\n        if(!flipper_format_file_open_always(\n               instance->flipper_file, furi_string_get_cstr(temp_str))) {\n            FURI_LOG_E(TAG, \"Unable to open file for write: %s\", furi_string_get_cstr(temp_str));\n            break;\n        }\n\n        if(!flipper_format_write_header_cstr(\n               instance->flipper_file, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) {\n            FURI_LOG_E(TAG, \"Unable to add header\");\n            break;\n        }\n\n        if(!flipper_format_write_uint32(\n               instance->flipper_file, \"Frequency\", &preset->frequency, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Frequency\");\n            break;\n        }\n\n        subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);\n        if(!flipper_format_write_string_cstr(\n               instance->flipper_file, \"Preset\", furi_string_get_cstr(temp_str))) {\n            FURI_LOG_E(TAG, \"Unable to add Preset\");\n            break;\n        }\n        if(!strcmp(furi_string_get_cstr(temp_str), \"FuriHalSubGhzPresetCustom\")) {\n            if(!flipper_format_write_string_cstr(\n                   instance->flipper_file, \"Custom_preset_module\", \"CC1101\")) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_module\");\n                break;\n            }\n            if(!flipper_format_write_hex(\n                   instance->flipper_file, \"Custom_preset_data\", preset->data, preset->data_size)) {\n                FURI_LOG_E(TAG, \"Unable to add Custom_preset_data\");\n                break;\n            }\n        }\n        if(!flipper_format_write_string_cstr(\n               instance->flipper_file, \"Protocol\", instance->base.protocol->name)) {\n            FURI_LOG_E(TAG, \"Unable to add Protocol\");\n            break;\n        }\n\n        instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t));\n        instance->file_is_open = RAWFileIsOpenWrite;\n        instance->sample_write = 0;\n        instance->last_level = false;\n        instance->pause = false;\n        init = true;\n    } while(0);\n\n    furi_string_free(temp_str);\n\n    return init;\n}\n\nstatic bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolDecoderRAW* instance) {\n    furi_assert(instance);\n\n    bool is_write = false;\n    if(instance->file_is_open == RAWFileIsOpenWrite) {\n        if(!flipper_format_write_int32(\n               instance->flipper_file, \"RAW_Data\", instance->upload_raw, instance->ind_write)) {\n            FURI_LOG_E(TAG, \"Unable to add RAW_Data\");\n        } else {\n            instance->sample_write += instance->ind_write;\n            instance->ind_write = 0;\n            is_write = true;\n        }\n    }\n    return is_write;\n}\n\nvoid subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) {\n    furi_check(instance);\n\n    if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write)\n        subghz_protocol_raw_save_to_file_write(instance);\n    if(instance->file_is_open != RAWFileIsOpenClose) {\n        free(instance->upload_raw);\n        instance->upload_raw = NULL;\n        flipper_format_file_close(instance->flipper_file);\n        flipper_format_free(instance->flipper_file);\n        furi_record_close(RECORD_STORAGE);\n    }\n\n    instance->file_is_open = RAWFileIsOpenClose;\n}\n\nvoid subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) {\n    furi_check(instance);\n\n    if(instance->pause != pause) {\n        instance->pause = pause;\n    }\n}\n\nsize_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) {\n    furi_check(instance);\n    return instance->sample_write + instance->ind_write;\n}\n\nvoid* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderRAW* instance = malloc(sizeof(SubGhzProtocolDecoderRAW));\n    instance->base.protocol = &subghz_protocol_raw;\n    instance->upload_raw = NULL;\n    instance->ind_write = 0;\n    instance->last_level = false;\n    instance->file_is_open = RAWFileIsOpenClose;\n    instance->file_name = furi_string_alloc();\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_raw_free(void* context) {\n    furi_check(context);\n    SubGhzProtocolDecoderRAW* instance = context;\n    furi_string_free(instance->file_name);\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_raw_reset(void* context) {\n    furi_check(context);\n    SubGhzProtocolDecoderRAW* instance = context;\n    instance->ind_write = 0;\n    instance->last_level = false;\n}\n\nvoid subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) {\n    furi_check(context);\n    SubGhzProtocolDecoderRAW* instance = context;\n    // Add check if we got duration higher than 1 second, we skipping it, temp fix\n    if((!instance->pause && (instance->upload_raw != NULL)) && (duration < ((uint32_t)1000000))) {\n        if(duration > subghz_protocol_raw_const.te_short) {\n            if(instance->last_level != level) {\n                instance->last_level = (level ? true : false);\n                instance->upload_raw[instance->ind_write++] = (level ? duration : -duration);\n            }\n        }\n\n        if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) {\n            subghz_protocol_raw_save_to_file_write(instance);\n        }\n    }\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_check(context);\n    UNUSED(context);\n    UNUSED(flipper_format);\n    // stub, for backwards compatibility\n    return SubGhzProtocolStatusOk;\n}\n\nvoid subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) {\n    furi_check(context);\n    //SubGhzProtocolDecoderRAW* instance = context;\n    UNUSED(context);\n    furi_string_cat_printf(output, \"RAW Data\");\n}\n\nvoid* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW));\n\n    instance->base.protocol = &subghz_protocol_raw;\n    instance->file_name = furi_string_alloc();\n    instance->radio_device_name = furi_string_alloc();\n    instance->is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_raw_stop(void* context) {\n    furi_check(context);\n    SubGhzProtocolEncoderRAW* instance = context;\n    instance->is_running = false;\n    if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) {\n        subghz_file_encoder_worker_stop(instance->file_worker_encoder);\n        subghz_file_encoder_worker_free(instance->file_worker_encoder);\n    }\n}\n\nvoid subghz_protocol_encoder_raw_free(void* context) {\n    furi_check(context);\n    SubGhzProtocolEncoderRAW* instance = context;\n    subghz_protocol_encoder_raw_stop(instance);\n    furi_string_free(instance->file_name);\n    furi_string_free(instance->radio_device_name);\n    free(instance);\n}\n\nvoid subghz_protocol_raw_file_encoder_worker_set_callback_end(\n    SubGhzProtocolEncoderRAW* instance,\n    SubGhzProtocolEncoderRAWCallbackEnd callback_end,\n    void* context_end) {\n    furi_check(instance);\n    furi_check(callback_end);\n    subghz_file_encoder_worker_callback_end(\n        instance->file_worker_encoder, callback_end, context_end);\n}\n\nstatic bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* instance) {\n    furi_assert(instance);\n\n    instance->file_worker_encoder = subghz_file_encoder_worker_alloc();\n    if(subghz_file_encoder_worker_start(\n           instance->file_worker_encoder,\n           furi_string_get_cstr(instance->file_name),\n           furi_string_get_cstr(instance->radio_device_name))) {\n        //the worker needs a file in order to open and read part of the file\n        furi_delay_ms(100);\n        instance->is_running = true;\n    } else {\n        subghz_protocol_encoder_raw_stop(instance);\n    }\n    return instance->is_running;\n}\n\nvoid subghz_protocol_raw_gen_fff_data(\n    FlipperFormat* flipper_format,\n    const char* file_path,\n    const char* radio_device_name) {\n    furi_check(flipper_format);\n\n    do {\n        stream_clean(flipper_format_get_raw_stream(flipper_format));\n        if(!flipper_format_write_string_cstr(flipper_format, \"Protocol\", \"RAW\")) {\n            FURI_LOG_E(TAG, \"Unable to add Protocol\");\n            break;\n        }\n\n        if(!flipper_format_write_string_cstr(flipper_format, \"File_name\", file_path)) {\n            FURI_LOG_E(TAG, \"Unable to add File_name\");\n            break;\n        }\n\n        if(!flipper_format_write_string_cstr(\n               flipper_format, \"Radio_device_name\", radio_device_name)) {\n            FURI_LOG_E(TAG, \"Unable to add Radio_device_name\");\n            break;\n        }\n    } while(false);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_check(context);\n    SubGhzProtocolEncoderRAW* instance = context;\n    SubGhzProtocolStatus res = SubGhzProtocolStatusError;\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    do {\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n\n        if(!flipper_format_read_string(flipper_format, \"File_name\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing File_name\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        furi_string_set(instance->file_name, temp_str);\n\n        if(!flipper_format_read_string(flipper_format, \"Radio_device_name\", temp_str)) {\n            FURI_LOG_E(TAG, \"Missing Radio_device_name\");\n            res = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        furi_string_set(instance->radio_device_name, temp_str);\n\n        if(!subghz_protocol_encoder_raw_worker_init(instance)) {\n            res = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        res = SubGhzProtocolStatusOk;\n    } while(false);\n    furi_string_free(temp_str);\n    return res;\n}\n\nLevelDuration subghz_protocol_encoder_raw_yield(void* context) {\n    furi_check(context);\n    SubGhzProtocolEncoderRAW* instance = context;\n\n    if(!instance->is_running) return level_duration_reset();\n    return subghz_file_encoder_worker_get_level_duration(instance->file_worker_encoder);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/raw.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_RAW_NAME \"RAW\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context);\n\ntypedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW;\ntypedef struct SubGhzProtocolEncoderRAW SubGhzProtocolEncoderRAW;\n\nextern const SubGhzProtocolDecoder subghz_protocol_raw_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_raw_encoder;\nextern const SubGhzProtocol subghz_protocol_raw;\n\n/**\n * Open file for writing\n * @param instance Pointer to a SubGhzProtocolDecoderRAW instance\n * @param dev_name  File name\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return true On success\n */\nbool subghz_protocol_raw_save_to_file_init(\n    SubGhzProtocolDecoderRAW* instance,\n    const char* dev_name,\n    SubGhzRadioPreset* preset);\n\n/**\n * Stop writing file to flash\n * @param instance Pointer to a SubGhzProtocolDecoderRAW instance\n */\nvoid subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance);\n\n/**\n * Get the number of samples received SubGhzProtocolDecoderRAW.\n * @param instance Pointer to a SubGhzProtocolDecoderRAW instance\n * @return count of samples\n */\nsize_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance);\n\n/**\n * Allocate SubGhzProtocolDecoderRAW.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderRAW* pointer to a SubGhzProtocolDecoderRAW instance\n */\nvoid* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderRAW.\n * @param context Pointer to a SubGhzProtocolDecoderRAW instance\n */\nvoid subghz_protocol_decoder_raw_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderRAW.\n * @param context Pointer to a SubGhzProtocolDecoderRAW instance\n */\nvoid subghz_protocol_decoder_raw_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderRAW instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Deserialize data SubGhzProtocolDecoderRAW.\n * @param context Pointer to a SubGhzProtocolDecoderRAW instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderRAW instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_raw_get_string(void* context, FuriString* output);\n\n/**\n * Allocate SubGhzProtocolEncoderRAW.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderRAW* pointer to a SubGhzProtocolEncoderRAW instance\n */\nvoid* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderRAW.\n * @param context Pointer to a SubGhzProtocolEncoderRAW instance\n */\nvoid subghz_protocol_encoder_raw_free(void* context);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderRAW instance\n */\nvoid subghz_protocol_encoder_raw_stop(void* context);\n\n/**\n * pause writing to flash.\n * @param context Pointer to a SubGhzProtocolEncoderRAW instance\n * @param pause pause writing\n */\nvoid subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause);\n\n/**\n * Set callback on completion of file transfer.\n * @param instance Pointer to a SubGhzProtocolEncoderRAW instance\n * @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd\n * @param context_end Context\n */\nvoid subghz_protocol_raw_file_encoder_worker_set_callback_end(\n    SubGhzProtocolEncoderRAW* instance,\n    SubGhzProtocolEncoderRAWCallbackEnd callback_end,\n    void* context_end);\n\n/**\n * File generation for RAW work.\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param file_path File path\n * @param radio_dev_name Radio device name\n */\nvoid subghz_protocol_raw_gen_fff_data(\n    FlipperFormat* flipper_format,\n    const char* file_path,\n    const char* radio_dev_name);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderRAW instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderRAW instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_raw_yield(void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/protocols/revers_rb2.c",
    "content": "#include \"revers_rb2.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include <lib/toolbox/manchester_encoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolRevers_RB2\"\n\nstatic const SubGhzBlockConst subghz_protocol_revers_rb2_const = {\n    .te_short = 250,\n    .te_long = 500,\n    .te_delta = 160,\n    .min_count_bit_for_found = 64,\n};\n\nstruct SubGhzProtocolDecoderRevers_RB2 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n    ManchesterState manchester_saved_state;\n    uint16_t header_count;\n};\n\nstruct SubGhzProtocolEncoderRevers_RB2 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    Revers_RB2DecoderStepReset = 0,\n    Revers_RB2DecoderStepHeader,\n    Revers_RB2DecoderStepDecoderData,\n} Revers_RB2DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_revers_rb2_decoder = {\n    .alloc = subghz_protocol_decoder_revers_rb2_alloc,\n    .free = subghz_protocol_decoder_revers_rb2_free,\n\n    .feed = subghz_protocol_decoder_revers_rb2_feed,\n    .reset = subghz_protocol_decoder_revers_rb2_reset,\n\n    .get_hash_data = subghz_protocol_decoder_revers_rb2_get_hash_data,\n    .serialize = subghz_protocol_decoder_revers_rb2_serialize,\n    .deserialize = subghz_protocol_decoder_revers_rb2_deserialize,\n    .get_string = subghz_protocol_decoder_revers_rb2_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_revers_rb2_encoder = {\n    .alloc = subghz_protocol_encoder_revers_rb2_alloc,\n    .free = subghz_protocol_encoder_revers_rb2_free,\n\n    .deserialize = subghz_protocol_encoder_revers_rb2_deserialize,\n    .stop = subghz_protocol_encoder_revers_rb2_stop,\n    .yield = subghz_protocol_encoder_revers_rb2_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_revers_rb2 = {\n    .name = SUBGHZ_PROTOCOL_REVERSRB2_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_revers_rb2_decoder,\n    .encoder = &subghz_protocol_revers_rb2_encoder,\n};\n\nvoid* subghz_protocol_encoder_revers_rb2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderRevers_RB2* instance = malloc(sizeof(SubGhzProtocolEncoderRevers_RB2));\n\n    instance->base.protocol = &subghz_protocol_revers_rb2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 1768;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_revers_rb2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderRevers_RB2* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic LevelDuration\n    subghz_protocol_encoder_revers_rb2_add_duration_to_upload(ManchesterEncoderResult result) {\n    LevelDuration data = {.duration = 0, .level = 0};\n    switch(result) {\n    case ManchesterEncoderResultShortLow:\n        data.duration = subghz_protocol_revers_rb2_const.te_short;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongLow:\n        data.duration = subghz_protocol_revers_rb2_const.te_long;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongHigh:\n        data.duration = subghz_protocol_revers_rb2_const.te_long;\n        data.level = true;\n        break;\n    case ManchesterEncoderResultShortHigh:\n        data.duration = subghz_protocol_revers_rb2_const.te_short;\n        data.level = true;\n        break;\n\n    default:\n        furi_crash(\"SubGhz: ManchesterEncoderResult is incorrect.\");\n        break;\n    }\n    return level_duration_make(data.level, data.duration);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n */\nstatic void\n    subghz_protocol_encoder_revers_rb2_get_upload(SubGhzProtocolEncoderRevers_RB2* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    for(size_t r = 0; r < 6; r++) {\n        ManchesterEncoderState enc_state;\n        manchester_encoder_reset(&enc_state);\n        ManchesterEncoderResult result;\n\n        for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n            if(!manchester_encoder_advance(\n                   &enc_state, bit_read(instance->generic.data, i - 1), &result)) {\n                instance->encoder.upload[index++] =\n                    subghz_protocol_encoder_revers_rb2_add_duration_to_upload(result);\n                manchester_encoder_advance(\n                    &enc_state, bit_read(instance->generic.data, i - 1), &result);\n            }\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_revers_rb2_add_duration_to_upload(result);\n        }\n        instance->encoder.upload[index] =\n            subghz_protocol_encoder_revers_rb2_add_duration_to_upload(\n                manchester_encoder_finish(&enc_state));\n        if(level_duration_get_level(instance->encoder.upload[index])) {\n            index++;\n        }\n        instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)320);\n    }\n    instance->encoder.size_upload = index;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_revers_rb2_remote_controller(SubGhzBlockGeneric* instance) {\n    // Revers RB2 / RB2M Decoder\n    // 02.2025 - @xMasterX (MMX)\n    instance->serial = (((instance->data << 16) >> 16) >> 10);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_revers_rb2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderRevers_RB2* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_revers_rb2_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_revers_rb2_remote_controller(&instance->generic);\n        subghz_protocol_encoder_revers_rb2_get_upload(instance);\n        instance->encoder.front = 0;\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_revers_rb2_stop(void* context) {\n    SubGhzProtocolEncoderRevers_RB2* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_revers_rb2_yield(void* context) {\n    SubGhzProtocolEncoderRevers_RB2* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_revers_rb2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderRevers_RB2* instance = malloc(sizeof(SubGhzProtocolDecoderRevers_RB2));\n    instance->base.protocol = &subghz_protocol_revers_rb2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_revers_rb2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_revers_rb2_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    instance->decoder.parser_step = Revers_RB2DecoderStepReset;\n    instance->header_count = 0;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\nvoid subghz_protocol_decoder_revers_rb2_addbit(void* context, bool data) {\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;\n    instance->decoder.decode_count_bit++;\n\n    if(instance->decoder.decode_count_bit >= 65) {\n        instance->decoder.decode_data = 0;\n        instance->decoder.decode_count_bit = 0;\n        return;\n    }\n\n    if(instance->decoder.decode_count_bit <\n       subghz_protocol_revers_rb2_const.min_count_bit_for_found) {\n        return;\n    }\n\n    // Revers RB2 / RB2M Decoder\n    // 02.2025 - @xMasterX (MMX)\n\n    uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFF;\n    uint16_t stop_code = (instance->decoder.decode_data & 0x3FF);\n\n    if(preamble == 0xFF && stop_code == 0x200) {\n        //Found header and stop code\n        instance->generic.data = instance->decoder.decode_data;\n        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n        if(instance->base.callback)\n            instance->base.callback(&instance->base, instance->base.context);\n\n        instance->decoder.decode_data = 0;\n        instance->decoder.decode_count_bit = 0;\n        manchester_advance(\n            instance->manchester_saved_state,\n            ManchesterEventReset,\n            &instance->manchester_saved_state,\n            NULL);\n    }\n}\n\nvoid subghz_protocol_decoder_revers_rb2_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    ManchesterEvent event = ManchesterEventReset;\n\n    switch(instance->decoder.parser_step) {\n    case Revers_RB2DecoderStepReset:\n        if((!level) &&\n           (DURATION_DIFF(duration, 600) < subghz_protocol_revers_rb2_const.te_delta)) {\n            instance->decoder.parser_step = Revers_RB2DecoderStepHeader;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventReset,\n                &instance->manchester_saved_state,\n                NULL);\n        }\n        break;\n    case Revers_RB2DecoderStepHeader:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_short) <\n               subghz_protocol_revers_rb2_const.te_delta) {\n                if(instance->decoder.te_last == 1) {\n                    instance->header_count++;\n                }\n                instance->decoder.te_last = level;\n            } else {\n                instance->header_count = 0;\n                instance->decoder.te_last = 0;\n                instance->decoder.parser_step = Revers_RB2DecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_short) <\n               subghz_protocol_revers_rb2_const.te_delta) {\n                if(instance->decoder.te_last == 0) {\n                    instance->header_count++;\n                }\n                instance->decoder.te_last = level;\n            } else {\n                instance->header_count = 0;\n                instance->decoder.te_last = 0;\n                instance->decoder.parser_step = Revers_RB2DecoderStepReset;\n            }\n        }\n\n        if(instance->header_count == 4) {\n            instance->header_count = 0;\n            instance->decoder.decode_data = 0xF;\n            instance->decoder.decode_count_bit = 4;\n            instance->decoder.parser_step = Revers_RB2DecoderStepDecoderData;\n        }\n        break;\n    case Revers_RB2DecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_short) <\n               subghz_protocol_revers_rb2_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_long) <\n                subghz_protocol_revers_rb2_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else {\n                instance->decoder.parser_step = Revers_RB2DecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_short) <\n               subghz_protocol_revers_rb2_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_revers_rb2_const.te_long) <\n                subghz_protocol_revers_rb2_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = Revers_RB2DecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                subghz_protocol_decoder_revers_rb2_addbit(instance, data);\n            }\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_revers_rb2_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_revers_rb2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_revers_rb2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_revers_rb2_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_revers_rb2_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRevers_RB2* instance = context;\n    subghz_protocol_revers_rb2_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:%lX%08lX\\r\\n\"\n        \"Sn:0x%08lX \\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)(instance->generic.data & 0xFFFFFFFF),\n        instance->generic.serial);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/revers_rb2.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_REVERSRB2_NAME \"Revers_RB2\"\n\ntypedef struct SubGhzProtocolDecoderRevers_RB2 SubGhzProtocolDecoderRevers_RB2;\ntypedef struct SubGhzProtocolEncoderRevers_RB2 SubGhzProtocolEncoderRevers_RB2;\n\nextern const SubGhzProtocolDecoder subghz_protocol_revers_rb2_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_revers_rb2_encoder;\nextern const SubGhzProtocol subghz_protocol_revers_rb2;\n\n/**\n * Allocate SubGhzProtocolEncoderRevers_RB2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderRevers_RB2* pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n */\nvoid* subghz_protocol_encoder_revers_rb2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderRevers_RB2.\n * @param context Pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n */\nvoid subghz_protocol_encoder_revers_rb2_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_revers_rb2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n */\nvoid subghz_protocol_encoder_revers_rb2_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderRevers_RB2 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_revers_rb2_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderRevers_RB2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderRevers_RB2* pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n */\nvoid* subghz_protocol_decoder_revers_rb2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderRevers_RB2.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n */\nvoid subghz_protocol_decoder_revers_rb2_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderRevers_RB2.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n */\nvoid subghz_protocol_decoder_revers_rb2_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_revers_rb2_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_revers_rb2_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderRevers_RB2.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_revers_rb2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderRevers_RB2.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_revers_rb2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderRevers_RB2 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_revers_rb2_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/roger.c",
    "content": "#include \"roger.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolRoger\"\n\nstatic const SubGhzBlockConst subghz_protocol_roger_const = {\n    .te_short = 500,\n    .te_long = 1000,\n    .te_delta = 270,\n    .min_count_bit_for_found = 28,\n};\n\nstruct SubGhzProtocolDecoderRoger {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n};\n\nstruct SubGhzProtocolEncoderRoger {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    RogerDecoderStepReset = 0,\n    RogerDecoderStepSaveDuration,\n    RogerDecoderStepCheckDuration,\n} RogerDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_roger_decoder = {\n    .alloc = subghz_protocol_decoder_roger_alloc,\n    .free = subghz_protocol_decoder_roger_free,\n\n    .feed = subghz_protocol_decoder_roger_feed,\n    .reset = subghz_protocol_decoder_roger_reset,\n\n    .get_hash_data = subghz_protocol_decoder_roger_get_hash_data,\n    .serialize = subghz_protocol_decoder_roger_serialize,\n    .deserialize = subghz_protocol_decoder_roger_deserialize,\n    .get_string = subghz_protocol_decoder_roger_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_roger_encoder = {\n    .alloc = subghz_protocol_encoder_roger_alloc,\n    .free = subghz_protocol_encoder_roger_free,\n\n    .deserialize = subghz_protocol_encoder_roger_deserialize,\n    .stop = subghz_protocol_encoder_roger_stop,\n    .yield = subghz_protocol_encoder_roger_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_roger = {\n    .name = SUBGHZ_PROTOCOL_ROGER_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |\n            SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_roger_decoder,\n    .encoder = &subghz_protocol_roger_encoder,\n};\n\nvoid* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderRoger* instance = malloc(sizeof(SubGhzProtocolEncoderRoger));\n\n    instance->base.protocol = &subghz_protocol_roger;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_roger_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderRoger* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderRoger instance\n */\nstatic void subghz_protocol_encoder_roger_get_upload(SubGhzProtocolEncoderRoger* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    // Send key and GAP\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            // Send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_long);\n            if(i == 1) {\n                //Send gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_roger_const.te_short * 19);\n            } else {\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_short);\n            }\n        } else {\n            // Send bit 0\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_short);\n            if(i == 1) {\n                //Send gap if bit was last\n                instance->encoder.upload[index++] = level_duration_make(\n                    false, (uint32_t)subghz_protocol_roger_const.te_short * 19);\n            } else {\n                instance->encoder.upload[index++] =\n                    level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_long);\n            }\n        }\n    }\n\n    instance->encoder.size_upload = index;\n    return;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_roger_check_remote_controller(SubGhzBlockGeneric* instance) {\n    // Roger Decoder\n    // 2025.07 - @xMasterX (MMX)\n\n    // Key samples\n    // 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 End: 0x20\n    // 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 End: 0x23\n    // 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 End: 0x01\n    // 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 End: 0x02\n    // 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 End: 0x01\n    // 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 End: 0x04\n    // 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 End: 0x02\n    // 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 End: 0x08\n\n    instance->serial = instance->data >> 12;\n    instance->btn = (instance->data >> 8) & 0xF;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderRoger* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_roger_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        subghz_protocol_roger_check_remote_controller(&instance->generic);\n        subghz_protocol_encoder_roger_get_upload(instance);\n        instance->encoder.front = 0;\n\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_roger_stop(void* context) {\n    SubGhzProtocolEncoderRoger* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0;\n}\n\nLevelDuration subghz_protocol_encoder_roger_yield(void* context) {\n    SubGhzProtocolEncoderRoger* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderRoger* instance = malloc(sizeof(SubGhzProtocolDecoderRoger));\n    instance->base.protocol = &subghz_protocol_roger;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_roger_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_roger_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n    instance->decoder.parser_step = RogerDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case RogerDecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <\n                        subghz_protocol_roger_const.te_delta * 5)) {\n            //Found GAP\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->decoder.parser_step = RogerDecoderStepSaveDuration;\n        }\n        break;\n    case RogerDecoderStepSaveDuration:\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->decoder.parser_step = RogerDecoderStepCheckDuration;\n        } else {\n            instance->decoder.parser_step = RogerDecoderStepReset;\n        }\n        break;\n    case RogerDecoderStepCheckDuration:\n        if(!level) {\n            // Bit 1 is long and short timing = 1000us HIGH (te_last) and 500us LOW\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <\n                subghz_protocol_roger_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short) <\n                subghz_protocol_roger_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = RogerDecoderStepSaveDuration;\n                // Bit 0 is short and long timing = 500us HIGH (te_last) and 1000us LOW\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <\n                 subghz_protocol_roger_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_roger_const.te_long) <\n                 subghz_protocol_roger_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = RogerDecoderStepSaveDuration;\n            } else if(\n                // End of the key\n                DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <\n                subghz_protocol_roger_const.te_delta * 5) {\n                //Found next GAP and add bit 1 or 0\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <\n                    subghz_protocol_roger_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                }\n                if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <\n                    subghz_protocol_roger_const.te_delta)) {\n                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                }\n                // If got full 28 bits key reading is finished\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_roger_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.parser_step = RogerDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = RogerDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = RogerDecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_roger_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_roger_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic, flipper_format, subghz_protocol_roger_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_roger_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderRoger* instance = context;\n\n    subghz_protocol_roger_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key: 0x%07lX\\r\\n\"\n        \"Serial: 0x%04lX\\r\\n\"\n        \"End: 0x%02lX\\r\\n\"\n        \"Btn: %01X\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0xFFFFFFF),\n        instance->generic.serial,\n        (uint32_t)(instance->generic.data & 0xFF),\n        instance->generic.btn);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/roger.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_ROGER_NAME \"Roger\"\n\ntypedef struct SubGhzProtocolDecoderRoger SubGhzProtocolDecoderRoger;\ntypedef struct SubGhzProtocolEncoderRoger SubGhzProtocolEncoderRoger;\n\nextern const SubGhzProtocolDecoder subghz_protocol_roger_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_roger_encoder;\nextern const SubGhzProtocol subghz_protocol_roger;\n\n/**\n * Allocate SubGhzProtocolEncoderRoger.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderRoger* pointer to a SubGhzProtocolEncoderRoger instance\n */\nvoid* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderRoger.\n * @param context Pointer to a SubGhzProtocolEncoderRoger instance\n */\nvoid subghz_protocol_encoder_roger_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderRoger instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderRoger instance\n */\nvoid subghz_protocol_encoder_roger_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderRoger instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_roger_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderRoger.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderRoger* pointer to a SubGhzProtocolDecoderRoger instance\n */\nvoid* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderRoger.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n */\nvoid subghz_protocol_decoder_roger_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderRoger.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n */\nvoid subghz_protocol_decoder_roger_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_roger_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_roger_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderRoger.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_roger_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderRoger.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderRoger instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_roger_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/scher_khan.c",
    "content": "#include \"scher_khan.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n//https://phreakerclub.com/72\n//https://phreakerclub.com/forum/showthread.php?t=7&page=2\n//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar\n//!!!  https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5\n\n#define TAG \"SubGhzProtocolScherKhan\"\n\nstatic const SubGhzBlockConst subghz_protocol_scher_khan_const = {\n    .te_short = 750,\n    .te_long = 1100,\n    .te_delta = 150,\n    .min_count_bit_for_found = 35,\n};\n\nstruct SubGhzProtocolDecoderScherKhan {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n    const char* protocol_name;\n};\n\nstruct SubGhzProtocolEncoderScherKhan {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    ScherKhanDecoderStepReset = 0,\n    ScherKhanDecoderStepCheckPreambula,\n    ScherKhanDecoderStepSaveDuration,\n    ScherKhanDecoderStepCheckDuration,\n} ScherKhanDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = {\n    .alloc = subghz_protocol_decoder_scher_khan_alloc,\n    .free = subghz_protocol_decoder_scher_khan_free,\n\n    .feed = subghz_protocol_decoder_scher_khan_feed,\n    .reset = subghz_protocol_decoder_scher_khan_reset,\n\n    .get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data,\n    .serialize = subghz_protocol_decoder_scher_khan_serialize,\n    .deserialize = subghz_protocol_decoder_scher_khan_deserialize,\n    .get_string = subghz_protocol_decoder_scher_khan_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_scher_khan = {\n    .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_scher_khan_decoder,\n    .encoder = &subghz_protocol_scher_khan_encoder,\n};\n\nvoid* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan));\n    instance->base.protocol = &subghz_protocol_scher_khan;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_scher_khan_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_scher_khan_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n    instance->decoder.parser_step = ScherKhanDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case ScherKhanDecoderStepReset:\n        if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <\n                       subghz_protocol_scher_khan_const.te_delta)) {\n            instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula;\n            instance->decoder.te_last = duration;\n            instance->header_count = 0;\n        }\n        break;\n    case ScherKhanDecoderStepCheckPreambula:\n        if(level) {\n            if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <\n                subghz_protocol_scher_khan_const.te_delta) ||\n               (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <\n                subghz_protocol_scher_khan_const.te_delta)) {\n                instance->decoder.te_last = duration;\n            } else {\n                instance->decoder.parser_step = ScherKhanDecoderStepReset;\n            }\n        } else if(\n            (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <\n             subghz_protocol_scher_khan_const.te_delta) ||\n            (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <\n             subghz_protocol_scher_khan_const.te_delta)) {\n            if(DURATION_DIFF(\n                   instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) <\n               subghz_protocol_scher_khan_const.te_delta) {\n                // Found header\n                instance->header_count++;\n                break;\n            } else if(\n                DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <\n                subghz_protocol_scher_khan_const.te_delta) {\n                // Found start bit\n                if(instance->header_count >= 2) {\n                    instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;\n                    instance->decoder.decode_data = 0;\n                    instance->decoder.decode_count_bit = 1;\n                } else {\n                    instance->decoder.parser_step = ScherKhanDecoderStepReset;\n                }\n            } else {\n                instance->decoder.parser_step = ScherKhanDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = ScherKhanDecoderStepReset;\n        }\n        break;\n    case ScherKhanDecoderStepSaveDuration:\n        if(level) {\n            if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL +\n                            subghz_protocol_scher_khan_const.te_long)) {\n                //Found stop bit\n                instance->decoder.parser_step = ScherKhanDecoderStepReset;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_scher_khan_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(instance->base.callback)\n                        instance->base.callback(&instance->base, instance->base.context);\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = ScherKhanDecoderStepReset;\n        }\n        break;\n    case ScherKhanDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <\n                subghz_protocol_scher_khan_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <\n                subghz_protocol_scher_khan_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) <\n                 subghz_protocol_scher_khan_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) <\n                 subghz_protocol_scher_khan_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = ScherKhanDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = ScherKhanDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param protocol_name \n */\nstatic void subghz_protocol_scher_khan_check_remote_controller(\n    SubGhzBlockGeneric* instance,\n    const char** protocol_name) {\n    /* \n    * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic\n    * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100\n    * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101\n    * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110\n    * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111\n    *                             Serial         Key  Ser ~Key   CNT\n    */\n\n    switch(instance->data_count_bit) {\n    case 35: //MAGIC CODE, Static\n        *protocol_name = \"MAGIC CODE, Static\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n    case 51: //MAGIC CODE, Dynamic\n        *protocol_name = \"MAGIC CODE, Dynamic\";\n        instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F);\n        instance->btn = (instance->data >> 24) & 0x0F;\n        instance->cnt = instance->data & 0xFFFF;\n        break;\n    case 57: //MAGIC CODE PRO / PRO2\n        *protocol_name = \"MAGIC CODE PRO/PRO2\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n    case 63: //MAGIC CODE, Dynamic Response\n        *protocol_name = \"MAGIC CODE, Response\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n    case 64: //MAGICAR, Response ???\n        *protocol_name = \"MAGICAR, Response\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n    case 81: // MAGIC CODE PRO / PRO2 Response ???\n    case 82: // MAGIC CODE PRO / PRO2 Response ???\n        *protocol_name = \"MAGIC CODE PRO,\\n Response\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n\n    default:\n        *protocol_name = \"Unknown\";\n        instance->serial = 0;\n        instance->btn = 0;\n        instance->cnt = 0;\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n    return subghz_block_generic_deserialize(&instance->generic, flipper_format);\n}\n\nvoid subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderScherKhan* instance = context;\n\n    subghz_protocol_scher_khan_check_remote_controller(\n        &instance->generic, &instance->protocol_name);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:%07lX Btn:%X Cnt:%04lX\\r\\n\"\n        \"Pt: %s\\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt,\n        instance->protocol_name);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/scher_khan.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME \"Scher-Khan\"\n\ntypedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan;\ntypedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan;\n\nextern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder;\nextern const SubGhzProtocol subghz_protocol_scher_khan;\n\n/**\n * Allocate SubGhzProtocolDecoderScherKhan.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance\n */\nvoid* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderScherKhan.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n */\nvoid subghz_protocol_decoder_scher_khan_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderScherKhan.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n */\nvoid subghz_protocol_decoder_scher_khan_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderScherKhan.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderScherKhan.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/secplus_v1.c",
    "content": "#include \"secplus_v1.h\"\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n* Help\n* https://github.com/argilo/secplus\n* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c\n*/\n\n#define TAG \"SubGhzProtocoSecPlusV1\"\n\n#define SECPLUS_V1_BIT_ERR -1 //0b0000\n#define SECPLUS_V1_BIT_0   0 //0b0001\n#define SECPLUS_V1_BIT_1   1 //0b0011\n#define SECPLUS_V1_BIT_2   2 //0b0111\n\n#define SECPLUS_V1_PACKET_1_HEADER     0x00\n#define SECPLUS_V1_PACKET_2_HEADER     0x02\n#define SECPLUS_V1_PACKET_1_INDEX_BASE 0\n#define SECPLUS_V1_PACKET_2_INDEX_BASE 21\n#define SECPLUS_V1_PACKET_1_ACCEPTED   (1 << 0)\n#define SECPLUS_V1_PACKET_2_ACCEPTED   (1 << 1)\n\nstatic const SubGhzBlockConst subghz_protocol_secplus_v1_const = {\n    .te_short = 500,\n    .te_long = 1500,\n    .te_delta = 100,\n    .min_count_bit_for_found = 21,\n};\n\nstruct SubGhzProtocolDecoderSecPlus_v1 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint8_t packet_accepted;\n    uint8_t base_packet_index;\n    uint8_t data_array[44];\n};\n\nstruct SubGhzProtocolEncoderSecPlus_v1 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint8_t data_array[44];\n};\n\ntypedef enum {\n    SecPlus_v1DecoderStepReset = 0,\n    SecPlus_v1DecoderStepSearchStartBit,\n    SecPlus_v1DecoderStepSaveDuration,\n    SecPlus_v1DecoderStepDecoderData,\n} SecPlus_v1DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = {\n    .alloc = subghz_protocol_decoder_secplus_v1_alloc,\n    .free = subghz_protocol_decoder_secplus_v1_free,\n\n    .feed = subghz_protocol_decoder_secplus_v1_feed,\n    .reset = subghz_protocol_decoder_secplus_v1_reset,\n\n    .get_hash_data = subghz_protocol_decoder_secplus_v1_get_hash_data,\n    .serialize = subghz_protocol_decoder_secplus_v1_serialize,\n    .deserialize = subghz_protocol_decoder_secplus_v1_deserialize,\n    .get_string = subghz_protocol_decoder_secplus_v1_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = {\n    .alloc = subghz_protocol_encoder_secplus_v1_alloc,\n    .free = subghz_protocol_encoder_secplus_v1_free,\n\n    .deserialize = subghz_protocol_encoder_secplus_v1_deserialize,\n    .stop = subghz_protocol_encoder_secplus_v1_stop,\n    .yield = subghz_protocol_encoder_secplus_v1_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_secplus_v1 = {\n    .name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_secplus_v1_decoder,\n    .encoder = &subghz_protocol_secplus_v1_encoder,\n};\n\nvoid* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1));\n\n    instance->base.protocol = &subghz_protocol_secplus_v1;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_secplus_v1_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSecPlus_v1* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n * @return true On success\n */\nstatic bool\n    subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2);\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Encoder size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send header packet 1\n    instance->encoder.upload[index++] = level_duration_make(\n        false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3));\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);\n\n    //Send data packet 1\n    for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21;\n        i++) {\n        switch(instance->data_array[i]) {\n        case SECPLUS_V1_BIT_0:\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);\n            break;\n        case SECPLUS_V1_BIT_1:\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);\n            break;\n        case SECPLUS_V1_BIT_2:\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);\n            break;\n\n        default:\n            FURI_LOG_E(TAG, \"Encoder error, wrong bit type\");\n            return false;\n            break;\n        }\n    }\n\n    //Send header packet 2\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116));\n    instance->encoder.upload[index++] =\n        level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);\n\n    //Send data packet 2\n    for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21;\n        i++) {\n        switch(instance->data_array[i]) {\n        case SECPLUS_V1_BIT_0:\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);\n            break;\n        case SECPLUS_V1_BIT_1:\n            instance->encoder.upload[index++] = level_duration_make(\n                false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);\n            break;\n        case SECPLUS_V1_BIT_2:\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short);\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);\n            break;\n\n        default:\n            FURI_LOG_E(TAG, \"Encoder error, wrong bit type.\");\n            return false;\n            break;\n        }\n    }\n\n    return true;\n}\n\n/** \n * Security+ 1.0 message encoding\n * @param instance SubGhzProtocolEncoderSecPlus_v1* \n */\n\nstatic bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) {\n    uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF;\n    uint32_t rolling = instance->generic.data & 0xFFFFFFFF;\n\n    uint8_t rolling_array[20] = {0};\n    uint8_t fixed_array[20] = {0};\n    uint32_t acc = 0;\n\n    //increment the counter\n    rolling += 2;\n\n    //update data\n    instance->generic.data &= 0xFFFFFFFF00000000;\n    instance->generic.data |= rolling;\n\n    if(rolling == 0xFFFFFFFF) {\n        rolling = 0xE6000000;\n    }\n    if(fixed > 0xCFD41B90) {\n        FURI_LOG_E(TAG, \"Encode wrong fixed data\");\n        return false;\n    }\n\n    rolling = subghz_protocol_blocks_reverse_key(rolling, 32);\n\n    for(int i = 19; i > -1; i--) {\n        rolling_array[i] = rolling % 3;\n        rolling /= 3;\n        fixed_array[i] = fixed % 3;\n        fixed /= 3;\n    }\n\n    instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER;\n    instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER;\n\n    //encode packet 1\n    for(uint8_t i = 1; i < 11; i++) {\n        acc += rolling_array[i - 1];\n        instance->data_array[i * 2 - 1] = rolling_array[i - 1];\n        acc += fixed_array[i - 1];\n        instance->data_array[i * 2] = acc % 3;\n    }\n\n    acc = 0;\n    //encode packet 2\n    for(uint8_t i = 11; i < 21; i++) {\n        acc += rolling_array[i - 1];\n        instance->data_array[i * 2] = rolling_array[i - 1];\n        acc += fixed_array[i - 1];\n        instance->data_array[i * 2 + 1] = acc % 3;\n    }\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSecPlus_v1* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_secplus_v1_encode(instance)) {\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            ;\n            break;\n        }\n\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            ret = SubGhzProtocolStatusErrorParserKey;\n            break;\n        }\n\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_secplus_v1_stop(void* context) {\n    SubGhzProtocolEncoderSecPlus_v1* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) {\n    SubGhzProtocolEncoderSecPlus_v1* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1));\n    instance->base.protocol = &subghz_protocol_secplus_v1;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_secplus_v1_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_secplus_v1_reset(void* context) {\n    furi_assert(context);\n    // SubGhzProtocolDecoderSecPlus_v1* instance = context;\n    // does not reset the decoder because you need to get 2 parts of the package\n}\n\n/** \n * Security+ 1.0 message decoding\n * @param instance SubGhzProtocolDecoderSecPlus_v1* \n */\n\nstatic void subghz_protocol_secplus_v1_decode(SubGhzProtocolDecoderSecPlus_v1* instance) {\n    uint32_t rolling = 0;\n    uint32_t fixed = 0;\n    uint32_t acc = 0;\n    uint8_t digit = 0;\n\n    //decode packet 1\n    for(uint8_t i = 1; i < 21; i += 2) {\n        digit = instance->data_array[i];\n        rolling = (rolling * 3) + digit;\n        acc += digit;\n\n        digit = (60 + instance->data_array[i + 1] - acc) % 3;\n        fixed = (fixed * 3) + digit;\n        acc += digit;\n    }\n\n    acc = 0;\n    //decode packet 2\n    for(uint8_t i = 22; i < 42; i += 2) {\n        digit = instance->data_array[i];\n        rolling = (rolling * 3) + digit;\n        acc += digit;\n\n        digit = (60 + instance->data_array[i + 1] - acc) % 3;\n        fixed = (fixed * 3) + digit;\n        acc += digit;\n    }\n\n    rolling = subghz_protocol_blocks_reverse_key(rolling, 32);\n    instance->generic.data = (uint64_t)fixed << 32 | rolling;\n\n    instance->generic.data_count_bit =\n        subghz_protocol_secplus_v1_const.min_count_bit_for_found * 2;\n}\n\nvoid subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case SecPlus_v1DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) <\n                        subghz_protocol_secplus_v1_const.te_delta * 120)) {\n            //Found header Security+ 1.0\n            instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->packet_accepted = 0;\n            memset(instance->data_array, 0, sizeof(instance->data_array));\n        }\n        break;\n    case SecPlus_v1DecoderStepSearchStartBit:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) <\n               subghz_protocol_secplus_v1_const.te_delta) {\n                instance->base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE;\n                instance\n                    ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =\n                    SECPLUS_V1_BIT_0;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_long) <\n                subghz_protocol_secplus_v1_const.te_delta) {\n                instance->base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE;\n                instance\n                    ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =\n                    SECPLUS_V1_BIT_2;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n        }\n        break;\n    case SecPlus_v1DecoderStepSaveDuration:\n        if(!level) { //save interval\n            if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) <\n               subghz_protocol_secplus_v1_const.te_delta * 120) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_secplus_v1_const.min_count_bit_for_found) {\n                    if(instance->base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE)\n                        instance->packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED;\n                    if(instance->base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE)\n                        instance->packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED;\n\n                    if(instance->packet_accepted ==\n                       (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) {\n                        subghz_protocol_secplus_v1_decode(instance);\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                        instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n                    }\n                }\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepDecoderData;\n            }\n        } else {\n            instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n        }\n        break;\n    case SecPlus_v1DecoderStepDecoderData:\n        if(level && (instance->decoder.decode_count_bit <=\n                     subghz_protocol_secplus_v1_const.min_count_bit_for_found)) {\n            if((DURATION_DIFF(\n                    instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 3) <\n                subghz_protocol_secplus_v1_const.te_delta * 3) &&\n               (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) <\n                subghz_protocol_secplus_v1_const.te_delta)) {\n                instance\n                    ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =\n                    SECPLUS_V1_BIT_0;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 2) <\n                 subghz_protocol_secplus_v1_const.te_delta * 2) &&\n                (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 2) <\n                 subghz_protocol_secplus_v1_const.te_delta * 2)) {\n                instance\n                    ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =\n                    SECPLUS_V1_BIT_1;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(\n                     instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short) <\n                 subghz_protocol_secplus_v1_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 3) <\n                 subghz_protocol_secplus_v1_const.te_delta * 3)) {\n                instance\n                    ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =\n                    SECPLUS_V1_BIT_2;\n                instance->decoder.decode_count_bit++;\n                instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = SecPlus_v1DecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_secplus_v1_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found);\n}\n\nbool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) {\n    //uint8_t id0 = (fixed / 3) % 3;\n    uint8_t id1 = (fixed / 9) % 3;\n    uint8_t btn = fixed % 3;\n\n    do {\n        if(id1 == 0) return false;\n        if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560\n    } while(false);\n    return true;\n}\n\nvoid subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v1* instance = context;\n\n    uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF;\n    instance->generic.cnt = instance->generic.data & 0xFFFFFFFF;\n\n    instance->generic.btn = fixed % 3;\n    uint8_t id0 = (fixed / 3) % 3;\n    uint8_t id1 = (fixed / 9) % 3;\n    uint16_t pin = 0;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"id1:%d id0:%d\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        id1,\n        id0);\n\n    if(id1 == 0) {\n        // (fixed // 3**3) % (3**7)    3^3=27  3^73=72187\n\n        instance->generic.serial = (fixed / 27) % 2187;\n        // pin = (fixed // 3**10) % (3**9)  3^10=59049 3^9=19683\n        pin = (fixed / 59049) % 19683;\n\n        if(pin <= 9999) {\n            furi_string_cat_printf(output, \" pin:%d\", pin);\n        } else if(pin <= 11029) {\n            furi_string_cat_printf(output, \" pin:enter\");\n        }\n\n        int pin_suffix = 0;\n        // pin_suffix = (fixed // 3**19) % 3   3^19=1162261467\n        pin_suffix = (fixed / 1162261467) % 3;\n\n        if(pin_suffix == 1) {\n            furi_string_cat_printf(output, \" #\\r\\n\");\n        } else if(pin_suffix == 2) {\n            furi_string_cat_printf(output, \" *\\r\\n\");\n        } else {\n            furi_string_cat_printf(output, \"\\r\\n\");\n        }\n        furi_string_cat_printf(\n            output,\n            \"Sn:0x%08lX\\r\\n\"\n            \"Cnt:0x%03lX\\r\\n\"\n            \"Sw_id:0x%X\\r\\n\",\n            instance->generic.serial,\n            instance->generic.cnt,\n            instance->generic.btn);\n    } else {\n        //id = fixed / 27;\n        instance->generic.serial = fixed / 27;\n        if(instance->generic.btn == 1) {\n            furi_string_cat_printf(output, \" Btn:left\\r\\n\");\n        } else if(instance->generic.btn == 0) {\n            furi_string_cat_printf(output, \" Btn:middle\\r\\n\");\n        } else if(instance->generic.btn == 2) { //-V547\n            furi_string_cat_printf(output, \" Btn:right\\r\\n\");\n        }\n\n        furi_string_cat_printf(\n            output,\n            \"Sn:0x%08lX\\r\\n\"\n            \"Cnt:0x%03lX\\r\\n\"\n            \"Sw_id:0x%X\\r\\n\",\n            instance->generic.serial,\n            instance->generic.cnt,\n            instance->generic.btn);\n    }\n}\n"
  },
  {
    "path": "lib/subghz/protocols/secplus_v1.h",
    "content": "#pragma once\n\n#include \"base.h\"\n#include \"public_api.h\"\n\n#define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME \"Security+ 1.0\"\n\ntypedef struct SubGhzProtocolDecoderSecPlus_v1 SubGhzProtocolDecoderSecPlus_v1;\ntypedef struct SubGhzProtocolEncoderSecPlus_v1 SubGhzProtocolEncoderSecPlus_v1;\n\nextern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder;\nextern const SubGhzProtocol subghz_protocol_secplus_v1;\n\n/**\n * Allocate SubGhzProtocolEncoderSecPlus_v1.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n */\nvoid* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderSecPlus_v1.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n */\nvoid subghz_protocol_encoder_secplus_v1_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n */\nvoid subghz_protocol_encoder_secplus_v1_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderSecPlus_v1.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderSecPlus_v1* pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n */\nvoid* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderSecPlus_v1.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n */\nvoid subghz_protocol_decoder_secplus_v1_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderSecPlus_v1.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n */\nvoid subghz_protocol_decoder_secplus_v1_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderSecPlus_v1.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_secplus_v1_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderSecPlus_v1.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/secplus_v2.c",
    "content": "#include \"secplus_v2.h\"\n#include <lib/toolbox/manchester_decoder.h>\n#include <lib/toolbox/manchester_encoder.h>\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n* Help\n* https://github.com/argilo/secplus\n* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c\n*/\n\n#define TAG \"SubGhzProtocoSecPlusV2\"\n\n#define SECPLUS_V2_HEADER      0x3C0000000000\n#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000\n#define SECPLUS_V2_PACKET_1    0x000000000000\n#define SECPLUS_V2_PACKET_2    0x010000000000\n#define SECPLUS_V2_PACKET_MASK 0x30000000000\n\nstatic const SubGhzBlockConst subghz_protocol_secplus_v2_const = {\n    .te_short = 250,\n    .te_long = 500,\n    .te_delta = 110,\n    .min_count_bit_for_found = 62,\n};\n\nstruct SubGhzProtocolDecoderSecPlus_v2 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    ManchesterState manchester_saved_state;\n    uint64_t secplus_packet_1;\n};\n\nstruct SubGhzProtocolEncoderSecPlus_v2 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n    uint64_t secplus_packet_1;\n};\n\ntypedef enum {\n    SecPlus_v2DecoderStepReset = 0,\n    SecPlus_v2DecoderStepDecoderData,\n} SecPlus_v2DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = {\n    .alloc = subghz_protocol_decoder_secplus_v2_alloc,\n    .free = subghz_protocol_decoder_secplus_v2_free,\n\n    .feed = subghz_protocol_decoder_secplus_v2_feed,\n    .reset = subghz_protocol_decoder_secplus_v2_reset,\n\n    .get_hash_data = subghz_protocol_decoder_secplus_v2_get_hash_data,\n    .serialize = subghz_protocol_decoder_secplus_v2_serialize,\n    .deserialize = subghz_protocol_decoder_secplus_v2_deserialize,\n    .get_string = subghz_protocol_decoder_secplus_v2_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = {\n    .alloc = subghz_protocol_encoder_secplus_v2_alloc,\n    .free = subghz_protocol_encoder_secplus_v2_free,\n\n    .deserialize = subghz_protocol_encoder_secplus_v2_deserialize,\n    .stop = subghz_protocol_encoder_secplus_v2_stop,\n    .yield = subghz_protocol_encoder_secplus_v2_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_secplus_v2 = {\n    .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |\n            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_secplus_v2_decoder,\n    .encoder = &subghz_protocol_secplus_v2_encoder,\n};\n\nvoid* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2));\n\n    instance->base.protocol = &subghz_protocol_secplus_v2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 256;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_secplus_v2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSecPlus_v2* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\nstatic bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) {\n    // selectively invert buffers\n    switch(invert) {\n    case 0x00: // 0b0000 (True, True, False),\n        p[0] = ~p[0] & 0x03FF;\n        p[1] = ~p[1] & 0x03FF;\n        break;\n    case 0x01: // 0b0001 (False, True, False),\n        p[1] = ~p[1] & 0x03FF;\n        break;\n    case 0x02: // 0b0010 (False, False, True),\n        p[2] = ~p[2] & 0x03FF;\n        break;\n    case 0x04: // 0b0100 (True, True, True),\n        p[0] = ~p[0] & 0x03FF;\n        p[1] = ~p[1] & 0x03FF;\n        p[2] = ~p[2] & 0x03FF;\n        break;\n    case 0x05: // 0b0101 (True, False, True),\n    case 0x0a: // 0b1010 (True, False, True),\n        p[0] = ~p[0] & 0x03FF;\n        p[2] = ~p[2] & 0x03FF;\n        break;\n    case 0x06: // 0b0110 (False, True, True),\n        p[1] = ~p[1] & 0x03FF;\n        p[2] = ~p[2] & 0x03FF;\n        break;\n    case 0x08: // 0b1000 (True, False, False),\n        p[0] = ~p[0] & 0x03FF;\n        break;\n    case 0x09: // 0b1001 (False, False, False),\n        break;\n    default:\n        FURI_LOG_E(TAG, \"Invert FAIL\");\n        return false;\n    }\n    return true;\n}\n\nstatic bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) {\n    uint16_t a = p[0], b = p[1], c = p[2];\n\n    // selectively reorder buffers\n    switch(order) {\n    case 0x06: // 0b0110  2, 1, 0],\n    case 0x09: // 0b1001  2, 1, 0],\n        p[2] = a;\n        // p[1]: no change\n        p[0] = c;\n        break;\n    case 0x08: // 0b1000  1, 2, 0],\n    case 0x04: // 0b0100  1, 2, 0],\n        p[1] = a;\n        p[2] = b;\n        p[0] = c;\n        break;\n    case 0x01: // 0b0001 2, 0, 1],\n        p[2] = a;\n        p[0] = b;\n        p[1] = c;\n        break;\n    case 0x00: // 0b0000  0, 2, 1],\n        // p[0]: no change\n        p[2] = b;\n        p[1] = c;\n        break;\n    case 0x05: // 0b0101 1, 0, 2],\n        p[1] = a;\n        p[0] = b;\n        // p[2]: no change\n        break;\n    case 0x02: // 0b0010 0, 1, 2],\n    case 0x0A: // 0b1010 0, 1, 2],\n        // no reordering\n        break;\n    default:\n        FURI_LOG_E(TAG, \"Order FAIL\");\n        return false;\n    }\n    return true;\n}\n\nstatic bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) {\n    uint16_t a, b, c;\n\n    // selectively reorder buffers\n    switch(order) {\n    case 0x06: // 0b0110  2, 1, 0],\n    case 0x09: // 0b1001  2, 1, 0],\n        a = p[2];\n        b = p[1];\n        c = p[0];\n        break;\n    case 0x08: // 0b1000  1, 2, 0],\n    case 0x04: // 0b0100  1, 2, 0],\n        a = p[1];\n        b = p[2];\n        c = p[0];\n        break;\n    case 0x01: // 0b0001 2, 0, 1],\n        a = p[2];\n        b = p[0];\n        c = p[1];\n        break;\n    case 0x00: // 0b0000  0, 2, 1],\n        a = p[0];\n        b = p[2];\n        c = p[1];\n        break;\n    case 0x05: // 0b0101 1, 0, 2],\n        a = p[1];\n        b = p[0];\n        c = p[2];\n        break;\n    case 0x02: // 0b0010 0, 1, 2],\n    case 0x0A: // 0b1010 0, 1, 2],\n        a = p[0];\n        b = p[1];\n        c = p[2];\n        break;\n    default:\n        FURI_LOG_E(TAG, \"Order FAIL\");\n        return false;\n    }\n\n    p[0] = a;\n    p[1] = b;\n    p[2] = c;\n    return true;\n}\n\n/** \n * Security+ 2.0 half-message decoding\n * @param data data \n * @param roll_array[] return roll_array part\n * @param fixed[] return fixed part\n * @return true On success\n */\n\nstatic bool\n    subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) {\n    uint8_t order = (data >> 34) & 0x0f;\n    uint8_t invert = (data >> 30) & 0x0f;\n    uint16_t p[3] = {0};\n\n    for(int i = 29; i >= 0; i -= 3) {\n        p[0] = p[0] << 1 | bit_read(data, i);\n        p[1] = p[1] << 1 | bit_read(data, i - 1);\n        p[2] = p[2] << 1 | bit_read(data, i - 2);\n    }\n\n    if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false;\n    if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false;\n\n    data = order << 4 | invert;\n    int k = 0;\n    for(int i = 6; i >= 0; i -= 2) {\n        roll_array[k] = (data >> i) & 0x03;\n        if(roll_array[k++] == 3) {\n            FURI_LOG_E(TAG, \"Roll_Array FAIL\");\n            return false;\n        }\n    }\n\n    for(int i = 8; i >= 0; i -= 2) {\n        roll_array[k] = (p[2] >> i) & 0x03;\n        if(roll_array[k++] == 3) {\n            FURI_LOG_E(TAG, \"Roll_Array FAIL\");\n            return false;\n        }\n    }\n\n    fixed[0] = p[0] << 10 | p[1];\n    return true;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param packet_1 first part of the message\n */\nstatic void\n    subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) {\n    uint32_t fixed_1[1];\n    uint8_t roll_1[9] = {0};\n    uint32_t fixed_2[1];\n    uint8_t roll_2[9] = {0};\n    uint8_t rolling_digits[18] = {0};\n\n    if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) &&\n       subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) {\n        rolling_digits[0] = roll_2[8];\n        rolling_digits[1] = roll_1[8];\n\n        rolling_digits[2] = roll_2[4];\n        rolling_digits[3] = roll_2[5];\n        rolling_digits[4] = roll_2[6];\n        rolling_digits[5] = roll_2[7];\n\n        rolling_digits[6] = roll_1[4];\n        rolling_digits[7] = roll_1[5];\n        rolling_digits[8] = roll_1[6];\n        rolling_digits[9] = roll_1[7];\n\n        rolling_digits[10] = roll_2[0];\n        rolling_digits[11] = roll_2[1];\n        rolling_digits[12] = roll_2[2];\n        rolling_digits[13] = roll_2[3];\n\n        rolling_digits[14] = roll_1[0];\n        rolling_digits[15] = roll_1[1];\n        rolling_digits[16] = roll_1[2];\n        rolling_digits[17] = roll_1[3];\n\n        uint32_t rolling = 0;\n        for(int i = 0; i < 18; i++) {\n            rolling = (rolling * 3) + rolling_digits[i];\n        }\n        // Max value = 2^28 (268435456)\n        if(rolling >= 0x10000000) {\n            FURI_LOG_E(TAG, \"Rolling FAIL\");\n            instance->cnt = 0;\n            instance->btn = 0;\n            instance->serial = 0;\n        } else {\n            instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28);\n            instance->btn = fixed_1[0] >> 12;\n            instance->serial = fixed_1[0] << 20 | fixed_2[0];\n        }\n    } else {\n        instance->cnt = 0;\n        instance->btn = 0;\n        instance->serial = 0;\n    }\n}\n\n/** \n * Security+ 2.0 half-message encoding\n * @param roll_array[] roll_array part\n * @param fixed[] fixed part\n * @return return data \n */\n\nstatic uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) {\n    uint64_t data = 0;\n    uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0};\n    uint8_t order = roll_array[0] << 2 | roll_array[1];\n    uint8_t invert = roll_array[2] << 2 | roll_array[3];\n    p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 |\n           roll_array[7] << 2 | roll_array[8];\n\n    if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0;\n    if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0;\n\n    for(int i = 0; i < 10; i++) {\n        data <<= 3;\n        data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i);\n    }\n    data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30;\n\n    return data;\n}\n\n/** \n * Security+ 2.0 message encoding\n * @param instance SubGhzProtocolEncoderSecPlus_v2* \n */\n\nstatic void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) {\n    uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20};\n    uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF};\n    uint8_t rolling_digits[18] = {0};\n    uint8_t roll_1[9] = {0};\n    uint8_t roll_2[9] = {0};\n\n    instance->generic.cnt++;\n    if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000;\n    uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28);\n\n    for(int8_t i = 17; i > -1; i--) {\n        rolling_digits[i] = rolling % 3;\n        rolling /= 3;\n    }\n\n    roll_2[8] = rolling_digits[0];\n    roll_1[8] = rolling_digits[1];\n\n    roll_2[4] = rolling_digits[2];\n    roll_2[5] = rolling_digits[3];\n    roll_2[6] = rolling_digits[4];\n    roll_2[7] = rolling_digits[5];\n\n    roll_1[4] = rolling_digits[6];\n    roll_1[5] = rolling_digits[7];\n    roll_1[6] = rolling_digits[8];\n    roll_1[7] = rolling_digits[9];\n\n    roll_2[0] = rolling_digits[10];\n    roll_2[1] = rolling_digits[11];\n    roll_2[2] = rolling_digits[12];\n    roll_2[3] = rolling_digits[13];\n\n    roll_1[0] = rolling_digits[14];\n    roll_1[1] = rolling_digits[15];\n    roll_1[2] = rolling_digits[16];\n    roll_1[3] = rolling_digits[17];\n\n    instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 |\n                                 subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]);\n    instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 |\n                             subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]);\n}\n\nstatic LevelDuration\n    subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) {\n    LevelDuration data = {.duration = 0, .level = 0};\n    switch(result) {\n    case ManchesterEncoderResultShortLow:\n        data.duration = subghz_protocol_secplus_v2_const.te_short;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongLow:\n        data.duration = subghz_protocol_secplus_v2_const.te_long;\n        data.level = false;\n        break;\n    case ManchesterEncoderResultLongHigh:\n        data.duration = subghz_protocol_secplus_v2_const.te_long;\n        data.level = true;\n        break;\n    case ManchesterEncoderResultShortHigh:\n        data.duration = subghz_protocol_secplus_v2_const.te_short;\n        data.level = true;\n        break;\n\n    default:\n        furi_crash(\"SubGhz: ManchesterEncoderResult is incorrect.\");\n        break;\n    }\n    return level_duration_make(data.level, data.duration);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n */\nstatic void\n    subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) {\n    furi_assert(instance);\n    size_t index = 0;\n\n    ManchesterEncoderState enc_state;\n    manchester_encoder_reset(&enc_state);\n    ManchesterEncoderResult result;\n\n    //Send data packet 1\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(!manchester_encoder_advance(\n               &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) {\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);\n            manchester_encoder_advance(\n                &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result);\n        }\n        instance->encoder.upload[index++] =\n            subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);\n    }\n    instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload(\n        manchester_encoder_finish(&enc_state));\n    if(level_duration_get_level(instance->encoder.upload[index])) {\n        index++;\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136);\n\n    //Send data packet 2\n    manchester_encoder_reset(&enc_state);\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(!manchester_encoder_advance(\n               &enc_state, bit_read(instance->generic.data, i - 1), &result)) {\n            instance->encoder.upload[index++] =\n                subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);\n            manchester_encoder_advance(\n                &enc_state, bit_read(instance->generic.data, i - 1), &result);\n        }\n        instance->encoder.upload[index++] =\n            subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);\n    }\n    instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload(\n        manchester_encoder_finish(&enc_state));\n    if(level_duration_get_level(instance->encoder.upload[index])) {\n        index++;\n    }\n    instance->encoder.upload[index++] =\n        level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136);\n\n    instance->encoder.size_upload = index;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSecPlus_v2* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_secplus_v2_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        if(!flipper_format_read_hex(\n               flipper_format, \"Secplus_packet_1\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Secplus_packet_1\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {\n            instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i];\n        }\n\n        subghz_protocol_secplus_v2_remote_controller(\n            &instance->generic, instance->secplus_packet_1);\n        subghz_protocol_secplus_v2_encode(instance);\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n        subghz_protocol_encoder_secplus_v2_get_upload(instance);\n\n        //update data\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(flipper_format, \"Key\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Key\");\n            ret = SubGhzProtocolStatusErrorParserKey;\n            break;\n        }\n\n        for(size_t i = 0; i < sizeof(uint64_t); i++) {\n            key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF;\n        }\n        if(!flipper_format_update_hex(\n               flipper_format, \"Secplus_packet_1\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Unable to add Secplus_packet_1\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n\n        instance->encoder.front = 0; // reset before start\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_secplus_v2_stop(void* context) {\n    SubGhzProtocolEncoderSecPlus_v2* instance = context;\n    instance->encoder.is_running = false;\n    instance->encoder.front = 0; // reset position\n}\n\nLevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) {\n    SubGhzProtocolEncoderSecPlus_v2* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nbool subghz_protocol_secplus_v2_create_data(\n    void* context,\n    FlipperFormat* flipper_format,\n    uint32_t serial,\n    uint8_t btn,\n    uint32_t cnt,\n    SubGhzRadioPreset* preset) {\n    furi_check(context);\n\n    SubGhzProtocolEncoderSecPlus_v2* instance = context;\n    instance->generic.serial = serial;\n    instance->generic.cnt = cnt;\n    instance->generic.btn = btn;\n    instance->generic.data_count_bit =\n        (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found;\n    subghz_protocol_secplus_v2_encode(instance);\n    SubGhzProtocolStatus res =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n\n    uint8_t key_data[sizeof(uint64_t)] = {0};\n    for(size_t i = 0; i < sizeof(uint64_t); i++) {\n        key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF;\n    }\n\n    if((res == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_hex(flipper_format, \"Secplus_packet_1\", key_data, sizeof(uint64_t))) {\n        FURI_LOG_E(TAG, \"Unable to add Secplus_packet_1\");\n        res = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return res == SubGhzProtocolStatusOk;\n}\n\nvoid* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2));\n    instance->base.protocol = &subghz_protocol_secplus_v2;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_secplus_v2_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_secplus_v2_reset(void* context) {\n    furi_assert(context);\n    // SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    // does not reset the decoder because you need to get 2 parts of the package\n}\n\nstatic bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) {\n    if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) {\n        if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) {\n            instance->secplus_packet_1 = instance->decoder.decode_data;\n        } else if(\n            ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) &&\n            (instance->secplus_packet_1)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n\n    ManchesterEvent event = ManchesterEventReset;\n    switch(instance->decoder.parser_step) {\n    case SecPlus_v2DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) <\n                        subghz_protocol_secplus_v2_const.te_delta * 100)) {\n            //Found header Security+ 2.0\n            instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->secplus_packet_1 = 0;\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventReset,\n                &instance->manchester_saved_state,\n                NULL);\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventLongHigh,\n                &instance->manchester_saved_state,\n                NULL);\n            manchester_advance(\n                instance->manchester_saved_state,\n                ManchesterEventShortLow,\n                &instance->manchester_saved_state,\n                NULL);\n        }\n        break;\n    case SecPlus_v2DecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <\n               subghz_protocol_secplus_v2_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <\n                subghz_protocol_secplus_v2_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL +\n                             subghz_protocol_secplus_v2_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_secplus_v2_const.min_count_bit_for_found) {\n                    instance->generic.data = instance->decoder.decode_data;\n                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                    if(subghz_protocol_secplus_v2_check_packet(instance)) {\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                        instance->decoder.parser_step = SecPlus_v2DecoderStepReset;\n                    }\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventShortLow,\n                    &instance->manchester_saved_state,\n                    NULL);\n            } else {\n                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <\n               subghz_protocol_secplus_v2_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <\n                subghz_protocol_secplus_v2_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_secplus_v2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n\n    uint8_t key_data[sizeof(uint64_t)] = {0};\n    for(size_t i = 0; i < sizeof(uint64_t); i++) {\n        key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF;\n    }\n\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_hex(flipper_format, \"Secplus_packet_1\", key_data, sizeof(uint64_t))) {\n        FURI_LOG_E(TAG, \"Unable to add Secplus_packet_1\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_secplus_v2_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        uint8_t key_data[sizeof(uint64_t)] = {0};\n        if(!flipper_format_read_hex(\n               flipper_format, \"Secplus_packet_1\", key_data, sizeof(uint64_t))) {\n            FURI_LOG_E(TAG, \"Missing Secplus_packet_1\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {\n            instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i];\n        }\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSecPlus_v2* instance = context;\n    subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Pk1:0x%lX%08lX\\r\\n\"\n        \"Pk2:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%08lX  Btn:0x%01X\\r\\n\"\n        \"Cnt:0x%03lX\\r\\n\",\n\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->secplus_packet_1 >> 32),\n        (uint32_t)instance->secplus_packet_1,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->generic.serial,\n        instance->generic.btn,\n        instance->generic.cnt);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/secplus_v2.h",
    "content": "#pragma once\n\n#include \"base.h\"\n#include \"public_api.h\"\n\n#define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME \"Security+ 2.0\"\n\ntypedef struct SubGhzProtocolDecoderSecPlus_v2 SubGhzProtocolDecoderSecPlus_v2;\ntypedef struct SubGhzProtocolEncoderSecPlus_v2 SubGhzProtocolEncoderSecPlus_v2;\n\nextern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder;\nextern const SubGhzProtocol subghz_protocol_secplus_v2;\n\n/**\n * Allocate SubGhzProtocolEncoderSecPlus_v2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n */\nvoid* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderSecPlus_v2.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n */\nvoid subghz_protocol_encoder_secplus_v2_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n */\nvoid subghz_protocol_encoder_secplus_v2_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderSecPlus_v2.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderSecPlus_v2* pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n */\nvoid* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderSecPlus_v2.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n */\nvoid subghz_protocol_decoder_secplus_v2_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderSecPlus_v2.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n */\nvoid subghz_protocol_decoder_secplus_v2_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderSecPlus_v2.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_secplus_v2_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderSecPlus_v2.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/smc5326.c",
    "content": "#include \"smc5326.h\"\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n/*\n * Help\n * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1\n *\n */\n\n#define TAG \"SubGhzProtocolSmc5326\"\n\n#define DIP_P 0b11 //(+)\n#define DIP_O 0b10 //(0)\n#define DIP_N 0b00 //(-)\n\n#define DIP_PATTERN \"%c%c%c%c%c%c%c%c\"\n#define SHOW_DIP_P(dip, check_dip)                         \\\n    ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'),     \\\n        ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \\\n        ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')\n\nstatic const SubGhzBlockConst subghz_protocol_smc5326_const = {\n    .te_short = 300,\n    .te_long = 900,\n    .te_delta = 200,\n    .min_count_bit_for_found = 25,\n};\n\nstruct SubGhzProtocolDecoderSMC5326 {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n    uint32_t last_data;\n};\n\nstruct SubGhzProtocolEncoderSMC5326 {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n\n    uint32_t te;\n};\n\ntypedef enum {\n    SMC5326DecoderStepReset = 0,\n    SMC5326DecoderStepSaveDuration,\n    SMC5326DecoderStepCheckDuration,\n} SMC5326DecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = {\n    .alloc = subghz_protocol_decoder_smc5326_alloc,\n    .free = subghz_protocol_decoder_smc5326_free,\n\n    .feed = subghz_protocol_decoder_smc5326_feed,\n    .reset = subghz_protocol_decoder_smc5326_reset,\n\n    .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data,\n    .serialize = subghz_protocol_decoder_smc5326_serialize,\n    .deserialize = subghz_protocol_decoder_smc5326_deserialize,\n    .get_string = subghz_protocol_decoder_smc5326_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = {\n    .alloc = subghz_protocol_encoder_smc5326_alloc,\n    .free = subghz_protocol_encoder_smc5326_free,\n\n    .deserialize = subghz_protocol_encoder_smc5326_deserialize,\n    .stop = subghz_protocol_encoder_smc5326_stop,\n    .yield = subghz_protocol_encoder_smc5326_yield,\n};\n\nconst SubGhzProtocol subghz_protocol_smc5326 = {\n    .name = SUBGHZ_PROTOCOL_SMC5326_NAME,\n    .type = SubGhzProtocolTypeStatic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |\n            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |\n            SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,\n\n    .decoder = &subghz_protocol_smc5326_decoder,\n    .encoder = &subghz_protocol_smc5326_encoder,\n};\n\nvoid* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326));\n\n    instance->base.protocol = &subghz_protocol_smc5326;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    instance->encoder.repeat = 10;\n    instance->encoder.size_upload = 128;\n    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));\n    instance->encoder.is_running = false;\n    return instance;\n}\n\nvoid subghz_protocol_encoder_smc5326_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSMC5326* instance = context;\n    free(instance->encoder.upload);\n    free(instance);\n}\n\n/**\n * Generating an upload from data.\n * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance\n * @return true On success\n */\nstatic bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) {\n    furi_assert(instance);\n\n    size_t index = 0;\n    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;\n    if(size_upload > instance->encoder.size_upload) {\n        FURI_LOG_E(TAG, \"Size upload exceeds allocated encoder buffer.\");\n        return false;\n    } else {\n        instance->encoder.size_upload = size_upload;\n    }\n\n    //Send key data\n    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {\n        if(bit_read(instance->generic.data, i - 1)) {\n            //send bit 1\n            instance->encoder.upload[index++] =\n                level_duration_make(true, (uint32_t)instance->te * 3);\n            instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);\n        } else {\n            //send bit 0\n            instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n            instance->encoder.upload[index++] =\n                level_duration_make(false, (uint32_t)instance->te * 3);\n        }\n    }\n\n    //Send Stop bit\n    instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);\n    //Send PT_GUARD\n    instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25);\n\n    return true;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolEncoderSMC5326* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_smc5326_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n        //optional parameter parameter\n        flipper_format_read_uint32(\n            flipper_format, \"Repeat\", (uint32_t*)&instance->encoder.repeat, 1);\n\n        if(!subghz_protocol_encoder_smc5326_get_upload(instance)) {\n            ret = SubGhzProtocolStatusErrorEncoderGetUpload;\n            break;\n        }\n        instance->encoder.is_running = true;\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_encoder_smc5326_stop(void* context) {\n    SubGhzProtocolEncoderSMC5326* instance = context;\n    instance->encoder.is_running = false;\n}\n\nLevelDuration subghz_protocol_encoder_smc5326_yield(void* context) {\n    SubGhzProtocolEncoderSMC5326* instance = context;\n\n    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {\n        instance->encoder.is_running = false;\n        return level_duration_reset();\n    }\n\n    LevelDuration ret = instance->encoder.upload[instance->encoder.front];\n\n    if(++instance->encoder.front == instance->encoder.size_upload) {\n        instance->encoder.repeat--;\n        instance->encoder.front = 0;\n    }\n\n    return ret;\n}\n\nvoid* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326));\n    instance->base.protocol = &subghz_protocol_smc5326;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    return instance;\n}\n\nvoid subghz_protocol_decoder_smc5326_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_smc5326_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    instance->decoder.parser_step = SMC5326DecoderStepReset;\n    instance->last_data = 0;\n}\n\nvoid subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case SMC5326DecoderStepReset:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) <\n                        subghz_protocol_smc5326_const.te_delta * 12)) {\n            //Found Preambula\n            instance->decoder.parser_step = SMC5326DecoderStepSaveDuration;\n            instance->decoder.decode_data = 0;\n            instance->decoder.decode_count_bit = 0;\n            instance->te = 0;\n        }\n        break;\n    case SMC5326DecoderStepSaveDuration:\n        //save duration\n        if(level) {\n            instance->decoder.te_last = duration;\n            instance->te += duration;\n            instance->decoder.parser_step = SMC5326DecoderStepCheckDuration;\n        }\n        break;\n    case SMC5326DecoderStepCheckDuration:\n        if(!level) {\n            if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) {\n                instance->decoder.parser_step = SMC5326DecoderStepSaveDuration;\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_smc5326_const.min_count_bit_for_found) {\n                    if((instance->last_data == instance->decoder.decode_data) &&\n                       instance->last_data) {\n                        instance->te /= (instance->decoder.decode_count_bit * 4 + 1);\n\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                    instance->last_data = instance->decoder.decode_data;\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->te = 0;\n                break;\n            }\n\n            instance->te += duration;\n\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) <\n                subghz_protocol_smc5326_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) <\n                subghz_protocol_smc5326_const.te_delta * 3)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = SMC5326DecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) <\n                 subghz_protocol_smc5326_const.te_delta * 3) &&\n                (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) <\n                 subghz_protocol_smc5326_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = SMC5326DecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = SMC5326DecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = SMC5326DecoderStepReset;\n        }\n        break;\n    }\n}\n\nuint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(flipper_format, \"TE\", &instance->te, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add TE\");\n        ret = SubGhzProtocolStatusErrorParserTe;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_smc5326_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"TE\", (uint32_t*)&instance->te, 1)) {\n            FURI_LOG_E(TAG, \"Missing TE\");\n            ret = SubGhzProtocolStatusErrorParserTe;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nstatic void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) {\n    furi_string_cat_printf(\n        output,\n        \"%s%s%s%s\\r\\n\",\n        (((event >> 6) & 0x3) == 0x3 ? \"B1 \" : \"\"),\n        (((event >> 4) & 0x3) == 0x3 ? \"B2 \" : \"\"),\n        (((event >> 2) & 0x3) == 0x3 ? \"B3 \" : \"\"),\n        (((event >> 0) & 0x3) == 0x3 ? \"B4 \" : \"\"));\n}\n\nvoid subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSMC5326* instance = context;\n    uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %ubit\\r\\n\"\n        \"Key:%07lX         Te:%luus\\r\\n\"\n        \"  +:   \" DIP_PATTERN \"\\r\\n\"\n        \"  o:   \" DIP_PATTERN \"    \",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data & 0x1FFFFFF),\n        instance->te,\n        SHOW_DIP_P(data, DIP_P),\n        SHOW_DIP_P(data, DIP_O));\n    subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output);\n    furi_string_cat_printf(output, \"  -:   \" DIP_PATTERN \"\\r\\n\", SHOW_DIP_P(data, DIP_N));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/smc5326.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_SMC5326_NAME \"SMC5326\"\n\ntypedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326;\ntypedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326;\n\nextern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder;\nextern const SubGhzProtocol subghz_protocol_smc5326;\n\n/**\n * Allocate SubGhzProtocolEncoderSMC5326.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance\n */\nvoid* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolEncoderSMC5326.\n * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance\n */\nvoid subghz_protocol_encoder_smc5326_free(void* context);\n\n/**\n * Deserialize and generating an upload to send.\n * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Forced transmission stop.\n * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance\n */\nvoid subghz_protocol_encoder_smc5326_stop(void* context);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance\n * @return LevelDuration \n */\nLevelDuration subghz_protocol_encoder_smc5326_yield(void* context);\n\n/**\n * Allocate SubGhzProtocolDecoderSMC5326.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance\n */\nvoid* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderSMC5326.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n */\nvoid subghz_protocol_decoder_smc5326_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderSMC5326.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n */\nvoid subghz_protocol_decoder_smc5326_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderSMC5326.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderSMC5326.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/somfy_keytis.c",
    "content": "#include \"somfy_keytis.h\"\n#include <lib/toolbox/manchester_decoder.h>\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolSomfyKeytis\"\n\nstatic const SubGhzBlockConst subghz_protocol_somfy_keytis_const = {\n    .te_short = 640,\n    .te_long = 1280,\n    .te_delta = 250,\n    .min_count_bit_for_found = 80,\n};\n\nstruct SubGhzProtocolDecoderSomfyKeytis {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n    ManchesterState manchester_saved_state;\n    uint32_t press_duration_counter;\n};\n\nstruct SubGhzProtocolEncoderSomfyKeytis {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    SomfyKeytisDecoderStepReset = 0,\n    SomfyKeytisDecoderStepCheckPreambula,\n    SomfyKeytisDecoderStepFoundPreambula,\n    SomfyKeytisDecoderStepStartDecode,\n    SomfyKeytisDecoderStepDecoderData,\n} SomfyKeytisDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = {\n    .alloc = subghz_protocol_decoder_somfy_keytis_alloc,\n    .free = subghz_protocol_decoder_somfy_keytis_free,\n\n    .feed = subghz_protocol_decoder_somfy_keytis_feed,\n    .reset = subghz_protocol_decoder_somfy_keytis_reset,\n\n    .get_hash_data = subghz_protocol_decoder_somfy_keytis_get_hash_data,\n    .serialize = subghz_protocol_decoder_somfy_keytis_serialize,\n    .deserialize = subghz_protocol_decoder_somfy_keytis_deserialize,\n    .get_string = subghz_protocol_decoder_somfy_keytis_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_somfy_keytis = {\n    .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_somfy_keytis_decoder,\n    .encoder = &subghz_protocol_somfy_keytis_encoder,\n};\n\nvoid* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis));\n    instance->base.protocol = &subghz_protocol_somfy_keytis;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_somfy_keytis_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_somfy_keytis_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n    instance->decoder.parser_step = SomfyKeytisDecoderStepReset;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\n/** \n * Сhecksum calculation.\n * @param data Вata for checksum calculation\n * @return CRC\n */\nstatic uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) {\n    uint8_t crc = 0;\n    data &= 0xFFF0FFFFFFFFFF;\n    for(uint8_t i = 0; i < 56; i += 8) {\n        crc = crc ^ data >> i ^ (data >> (i + 4));\n    }\n    return crc & 0xf;\n}\n\nvoid subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n\n    ManchesterEvent event = ManchesterEventReset;\n    switch(instance->decoder.parser_step) {\n    case SomfyKeytisDecoderStepReset:\n        if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <\n                          subghz_protocol_somfy_keytis_const.te_delta * 4) {\n            instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula;\n            instance->header_count++;\n        }\n        break;\n    case SomfyKeytisDecoderStepFoundPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <\n                        subghz_protocol_somfy_keytis_const.te_delta * 4)) {\n            instance->decoder.parser_step = SomfyKeytisDecoderStepCheckPreambula;\n        } else {\n            instance->header_count = 0;\n            instance->decoder.parser_step = SomfyKeytisDecoderStepReset;\n        }\n        break;\n    case SomfyKeytisDecoderStepCheckPreambula:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <\n               subghz_protocol_somfy_keytis_const.te_delta * 4) {\n                instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula;\n                instance->header_count++;\n            } else if(\n                (instance->header_count > 1) &&\n                (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 7) <\n                 subghz_protocol_somfy_keytis_const.te_delta * 4)) {\n                instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->press_duration_counter = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n            }\n        }\n\n        break;\n\n    case SomfyKeytisDecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) <\n               subghz_protocol_somfy_keytis_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) <\n                subghz_protocol_somfy_keytis_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= (subghz_protocol_somfy_keytis_const.te_long +\n                             subghz_protocol_somfy_keytis_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_somfy_keytis_const.min_count_bit_for_found) {\n                    //check crc\n                    uint64_t data_tmp = instance->generic.data ^ (instance->generic.data >> 8);\n                    if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n                instance->decoder.parser_step = SomfyKeytisDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = SomfyKeytisDecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) <\n               subghz_protocol_somfy_keytis_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) <\n                subghz_protocol_somfy_keytis_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = SomfyKeytisDecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                if(instance->decoder.decode_count_bit < 56) {\n                    instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;\n                } else {\n                    instance->press_duration_counter = (instance->press_duration_counter << 1) |\n                                                       data;\n                }\n\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance) {\n    //https://pushstack.wordpress.com/somfy-rts-protocol/\n    /*\n *                                                  604 us\n *                                                  /\n *  | 2416us | 2416us | 2416us | 2416us | 4550 us |  | \n *  \n *  +--------+        +--------+        +---...---+\n *  +        +--------+        +--------+         +--+XXXX...XXX+\n *  \n *  |              hw. sync.            |   soft.    |          |\n *  |                                   |   sync.    |   data   |\n *  \n * \n *     encrypt              |           decrypt\n *  \n *  package 80 bit   pdc      key btn   crc cnt serial\n * \n * 0xA453537C4B9855 C40019 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 C80026 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 CC0033 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 D00049 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 D4005C => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 D80063 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 DC0076 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 E00086 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 E40093 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 E800AC => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 EC00B9 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 F000C3 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 F400D6 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 F800E9 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 FC00FC => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 FC0102 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 FC0113 => 0xA  4  F  7 002F 37D3CD\n * 0xA453537C4B9855 FC0120 => 0xA  4  F  7 002F 37D3CD\n * ..........\n * 0xA453537C4B9855 FC048F => 0xA  4  F  7 002F 37D3CD\n *\n * Pdc: \"Press Duration Counter\" the total delay of the button is sent 72 parcels,\n *            pdc          cnt4b           cnt8b  pdc_crc\n *           C40019\t =>\t 11 0001 00 0000 00000001 1001\n *           C80026  =>  11 0010 00 0000 00000010 0110\n *           CC0033  =>  11 0011 00 0000 00000011 0011\n *           D00049  =>  11 0100 00 0000 00000100 1001\n *           D4005C  =>  11 0101 00 0000 00000101 1100\n *           D80063  =>  11 0110 00 0000 00000110 0011\n *           DC0076  =>  11 0111 00 0000 00000111 0110\n *           E00086  =>  11 1000 00 0000 00001000 0110\n *           E40093  =>  11 1001 00 0000 00001001 0011\n *           E800AC  =>  11 1010 00 0000 00001010 1100\n *           EC00B9  =>  11 1011 00 0000 00001011 1001\n *           F000C3  =>  11 1100 00 0000 00001100 0011\n *           F400D6  =>  11 1101 00 0000 00001101 0110\n *           F800E9  =>  11 1110 00 0000 00001110 1001\n *           FC00FC  =>  11 1111 00 0000 00001111 1100\n *           FC0102  =>  11 1111 00 0000 00010000 0010\n *           FC0113  =>  11 1111 00 0000 00010001 0011\n *           FC0120  =>  11 1111 00 0000 00010010 0000\n * \n *           Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15\n *           Cnt8b: 8-bit counter changes from 1 to 72 (0x48)\n *           Ppdc_crc: \n *                  uint8_t crc=0;\n *                  for(i=4; i<24; i+=4){\n *                      crc ^=(pdc>>i);\n *                  }\n *                  return crc;\n *               example: crc = 1^0^0^4^C = 9\n *           11, 00, 0000: const\n *          \n * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is \n *      a linear counter. In the Smoove Origin this counter is increased together with the \n *      rolling code. But leaving this on a constant value also works. Gerardwr notes that \n *      for some other types of remotes the MSB is not constant.\n * Btn: 4-bit Control codes, this indicates the button that is pressed\n * CRC: 4-bit Checksum.\n * Ctn: 16-bit rolling code (big-endian) increased with every button press.\n * Serial: 24-bit identifier of sending device (little-endian)\n * \n * \n *      Decrypt\n *  \n *      uint8_t frame[7];\n *      for (i=1; i < 7; i++) {\n *          frame[i] = frame[i] ^ frame[i-1];\n *      }\n *      or\n *      uint64 Decrypt = frame ^ (frame>>8);\n * \n *      CRC\n * \n *      uint8_t frame[7];\n *      for (i=0; i < 7; i++) {\n *          crc = crc ^ frame[i] ^ (frame[i] >> 4);\n *      }\n *      crc = crc & 0xf;\n *\n */\n\n    uint64_t data = instance->data ^ (instance->data >> 8);\n    instance->btn = (data >> 48) & 0xF;\n    instance->cnt = (data >> 24) & 0xFFFF;\n    instance->serial = data & 0xFFFFFF;\n}\n\n/** \n * Get button name.\n * @param btn Button number, 4 bit\n */\nstatic const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) {\n    const char* name_btn[0x10] = {\n        \"Unknown\",\n        \"0x01\",\n        \"0x02\",\n        \"Prog\",\n        \"Key_1\",\n        \"0x05\",\n        \"0x06\",\n        \"0x07\",\n        \"0x08\",\n        \"0x09\",\n        \"0x0A\",\n        \"0x0B\",\n        \"0x0C\",\n        \"0x0D\",\n        \"0x0E\",\n        \"0x0F\"};\n    return btn <= 0xf ? name_btn[btn] : name_btn[0];\n}\n\nuint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_uint32(\n           flipper_format, \"Duration_Counter\", &instance->press_duration_counter, 1)) {\n        FURI_LOG_E(TAG, \"Unable to add Duration_Counter\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    do {\n        ret = subghz_block_generic_deserialize_check_count_bit(\n            &instance->generic,\n            flipper_format,\n            subghz_protocol_somfy_keytis_const.min_count_bit_for_found);\n        if(ret != SubGhzProtocolStatusOk) {\n            break;\n        }\n        if(!flipper_format_rewind(flipper_format)) {\n            FURI_LOG_E(TAG, \"Rewind error\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n        if(!flipper_format_read_uint32(\n               flipper_format,\n               \"Duration_Counter\",\n               (uint32_t*)&instance->press_duration_counter,\n               1)) {\n            FURI_LOG_E(TAG, \"Missing Duration_Counter\");\n            ret = SubGhzProtocolStatusErrorParserOthers;\n            break;\n        }\n    } while(false);\n\n    return ret;\n}\n\nvoid subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyKeytis* instance = context;\n\n    subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"%lX%08lX%06lX\\r\\n\"\n        \"Sn:0x%06lX \\r\\n\"\n        \"Cnt:0x%04lX\\r\\n\"\n        \"Btn:%s\\r\\n\",\n\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->press_duration_counter,\n        instance->generic.serial,\n        instance->generic.cnt,\n        subghz_protocol_somfy_keytis_get_name_button(instance->generic.btn));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/somfy_keytis.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME \"Somfy Keytis\"\n\ntypedef struct SubGhzProtocolDecoderSomfyKeytis SubGhzProtocolDecoderSomfyKeytis;\ntypedef struct SubGhzProtocolEncoderSomfyKeytis SubGhzProtocolEncoderSomfyKeytis;\n\nextern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder;\nextern const SubGhzProtocol subghz_protocol_somfy_keytis;\n\n/**\n * Allocate SubGhzProtocolDecoderSomfyKeytis.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderSomfyKeytis* pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n */\nvoid* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderSomfyKeytis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n */\nvoid subghz_protocol_decoder_somfy_keytis_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderSomfyKeytis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n */\nvoid subghz_protocol_decoder_somfy_keytis_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderSomfyKeytis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderSomfyKeytis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/somfy_telis.c",
    "content": "#include \"somfy_telis.h\"\n#include <lib/toolbox/manchester_decoder.h>\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolSomfyTelis\"\n\nstatic const SubGhzBlockConst subghz_protocol_somfy_telis_const = {\n    .te_short = 640,\n    .te_long = 1280,\n    .te_delta = 250,\n    .min_count_bit_for_found = 56,\n};\n\nstruct SubGhzProtocolDecoderSomfyTelis {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n    ManchesterState manchester_saved_state;\n};\n\nstruct SubGhzProtocolEncoderSomfyTelis {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    SomfyTelisDecoderStepReset = 0,\n    SomfyTelisDecoderStepCheckPreambula,\n    SomfyTelisDecoderStepFoundPreambula,\n    SomfyTelisDecoderStepStartDecode,\n    SomfyTelisDecoderStepDecoderData,\n} SomfyTelisDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = {\n    .alloc = subghz_protocol_decoder_somfy_telis_alloc,\n    .free = subghz_protocol_decoder_somfy_telis_free,\n\n    .feed = subghz_protocol_decoder_somfy_telis_feed,\n    .reset = subghz_protocol_decoder_somfy_telis_reset,\n\n    .get_hash_data = subghz_protocol_decoder_somfy_telis_get_hash_data,\n    .serialize = subghz_protocol_decoder_somfy_telis_serialize,\n    .deserialize = subghz_protocol_decoder_somfy_telis_deserialize,\n    .get_string = subghz_protocol_decoder_somfy_telis_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_somfy_telis = {\n    .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |\n            SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_somfy_telis_decoder,\n    .encoder = &subghz_protocol_somfy_telis_encoder,\n};\n\nvoid* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment) {\n    UNUSED(environment);\n    SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis));\n    instance->base.protocol = &subghz_protocol_somfy_telis;\n    instance->generic.protocol_name = instance->base.protocol->name;\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_somfy_telis_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_somfy_telis_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n    instance->decoder.parser_step = SomfyTelisDecoderStepReset;\n    manchester_advance(\n        instance->manchester_saved_state,\n        ManchesterEventReset,\n        &instance->manchester_saved_state,\n        NULL);\n}\n\n/** \n * Сhecksum calculation.\n * @param data Вata for checksum calculation\n * @return CRC\n */\nstatic uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) {\n    uint8_t crc = 0;\n    data &= 0xFFF0FFFFFFFFFF;\n    for(uint8_t i = 0; i < 56; i += 8) {\n        crc = crc ^ data >> i ^ (data >> (i + 4));\n    }\n    return crc & 0xf;\n}\n\nvoid subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n\n    ManchesterEvent event = ManchesterEventReset;\n    switch(instance->decoder.parser_step) {\n    case SomfyTelisDecoderStepReset:\n        if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) <\n                          subghz_protocol_somfy_telis_const.te_delta * 4) {\n            instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula;\n            instance->header_count++;\n        }\n        break;\n    case SomfyTelisDecoderStepFoundPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) <\n                        subghz_protocol_somfy_telis_const.te_delta * 4)) {\n            instance->decoder.parser_step = SomfyTelisDecoderStepCheckPreambula;\n        } else {\n            instance->header_count = 0;\n            instance->decoder.parser_step = SomfyTelisDecoderStepReset;\n        }\n        break;\n    case SomfyTelisDecoderStepCheckPreambula:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) <\n               subghz_protocol_somfy_telis_const.te_delta * 4) {\n                instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula;\n                instance->header_count++;\n            } else if(\n                (instance->header_count > 1) &&\n                (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 7) <\n                 subghz_protocol_somfy_telis_const.te_delta * 4)) {\n                instance->decoder.parser_step = SomfyTelisDecoderStepDecoderData;\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->header_count = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n            }\n        }\n\n        break;\n\n    case SomfyTelisDecoderStepDecoderData:\n        if(!level) {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) <\n               subghz_protocol_somfy_telis_const.te_delta) {\n                event = ManchesterEventShortLow;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) <\n                subghz_protocol_somfy_telis_const.te_delta) {\n                event = ManchesterEventLongLow;\n            } else if(\n                duration >= (subghz_protocol_somfy_telis_const.te_long +\n                             subghz_protocol_somfy_telis_const.te_delta)) {\n                if(instance->decoder.decode_count_bit ==\n                   subghz_protocol_somfy_telis_const.min_count_bit_for_found) {\n                    //check crc\n                    uint64_t data_tmp = instance->decoder.decode_data ^\n                                        (instance->decoder.decode_data >> 8);\n                    if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventReset,\n                    &instance->manchester_saved_state,\n                    NULL);\n                manchester_advance(\n                    instance->manchester_saved_state,\n                    ManchesterEventLongHigh,\n                    &instance->manchester_saved_state,\n                    NULL);\n                instance->decoder.parser_step = SomfyTelisDecoderStepReset;\n            } else {\n                instance->decoder.parser_step = SomfyTelisDecoderStepReset;\n            }\n        } else {\n            if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) <\n               subghz_protocol_somfy_telis_const.te_delta) {\n                event = ManchesterEventShortHigh;\n            } else if(\n                DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) <\n                subghz_protocol_somfy_telis_const.te_delta) {\n                event = ManchesterEventLongHigh;\n            } else {\n                instance->decoder.parser_step = SomfyTelisDecoderStepReset;\n            }\n        }\n        if(event != ManchesterEventReset) {\n            bool data;\n            bool data_ok = manchester_advance(\n                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);\n\n            if(data_ok) {\n                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;\n                instance->decoder.decode_count_bit++;\n            }\n        }\n        break;\n    }\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n */\nstatic void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) {\n    //https://pushstack.wordpress.com/somfy-rts-protocol/\n    /*\n *                                                  604 us\n *                                                  /\n *  | 2416us | 2416us | 2416us | 2416us | 4550 us |  | 67648 us |  30415 us  |\n *  \n *  +--------+        +--------+        +---...---+\n *  +        +--------+        +--------+         +--+XXXX...XXX+-----...-----\n *  \n *  |              hw. sync.            |   soft.    |          | Inter-frame\n *  |                                   |   sync.    |   data   |     gap\n *  \n * \n *     encrypt              |           decrypt\n *  \n *  package 56 bit    cnt    key  btn|crc    cnt     serial\n *  0xA7232323312222 - 0   => A7    8 0   | 00 00 | 12 13 00\n *  0xA7222223312222 - 1   => A7    8 5   | 00 01 | 12 13 00\n *  0xA7212123312222 - 2   => A7    8 6   | 00 02 | 12 13 00\n * \n * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is \n *      a linear counter. In the Smoove Origin this counter is increased together with the \n *      rolling code. But leaving this on a constant value also works. Gerardwr notes that \n *      for some other types of remotes the MSB is not constant.\n * Btn: 4-bit Control codes, this indicates the button that is pressed\n * CRC: 4-bit Checksum.\n * Ctn: 16-bit rolling code (big-endian) increased with every button press.\n * Serial: 24-bit identifier of sending device (little-endian)\n * \n * \n *      Decrypt\n *  \n *      uint8_t frame[7];\n *      for (i=1; i < 7; i++) {\n *          frame[i] = frame[i] ^ frame[i-1];\n *      }\n *      or\n *      uint64 Decrypt = frame ^ (frame>>8);\n * \n *      Btn\n *\n *     Value  Button(s)       Description\n *      0x1     My          Stop or move to favourite position\n *      0x2     Up          Move up\n *      0x3     My + Up     Set upper motor limit in initial programming mode\n *      0x4     Down        Move down\n *      0x5     My + Down   Set lower motor limit in initial programming mode\n *      0x6     Up + Down   Change motor limit and initial programming mode\n *      0x8     Prog        Used for (de-)registering remotes, see below\n *      0x9     Sun + Flag  Enable sun and wind detector (SUN and FLAG symbol on the Telis Soliris RC)\n *      0xA     Flag        Disable sun detector (FLAG symbol on the Telis Soliris RC)\n * \n *      CRC\n * \n *      uint8_t frame[7];\n *      for (i=0; i < 7; i++) {\n *          cksum = cksum ^ frame[i] ^ (frame[i] >> 4);\n *      }\n *      cksum = cksum & 0xf;\n *\n */\n\n    uint64_t data = instance->data ^ (instance->data >> 8);\n    instance->btn = (data >> 44) & 0xF;\n    instance->cnt = (data >> 24) & 0xFFFF;\n    instance->serial = data & 0xFFFFFF;\n}\n\n/** \n * Get button name.\n * @param btn Button number, 4 bit\n */\nstatic const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) {\n    const char* name_btn[0x10] = {\n        \"Unknown\",\n        \"My\",\n        \"Up\",\n        \"My+Up\",\n        \"Down\",\n        \"My+Down\",\n        \"Up+Down\",\n        \"0x07\",\n        \"Prog\",\n        \"Sun+Flag\",\n        \"Flag\",\n        \"0x0B\",\n        \"0x0C\",\n        \"0x0D\",\n        \"0x0E\",\n        \"0x0F\"};\n    return btn <= 0xf ? name_btn[btn] : name_btn[0];\n}\n\nuint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_somfy_telis_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n    return subghz_block_generic_deserialize_check_count_bit(\n        &instance->generic,\n        flipper_format,\n        subghz_protocol_somfy_telis_const.min_count_bit_for_found);\n}\n\nvoid subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderSomfyTelis* instance = context;\n\n    subghz_protocol_somfy_telis_check_remote_controller(&instance->generic);\n\n    furi_string_cat_printf(\n        output,\n        \"%s %db\\r\\n\"\n        \"Key:0x%lX%08lX\\r\\n\"\n        \"Sn:0x%06lX \\r\\n\"\n        \"Cnt:0x%04lX\\r\\n\"\n        \"Btn:%s\\r\\n\",\n\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        (uint32_t)(instance->generic.data >> 32),\n        (uint32_t)instance->generic.data,\n        instance->generic.serial,\n        instance->generic.cnt,\n        subghz_protocol_somfy_telis_get_name_button(instance->generic.btn));\n}\n"
  },
  {
    "path": "lib/subghz/protocols/somfy_telis.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME \"Somfy Telis\"\n\ntypedef struct SubGhzProtocolDecoderSomfyTelis SubGhzProtocolDecoderSomfyTelis;\ntypedef struct SubGhzProtocolEncoderSomfyTelis SubGhzProtocolEncoderSomfyTelis;\n\nextern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder;\nextern const SubGhzProtocol subghz_protocol_somfy_telis;\n\n/**\n * Allocate SubGhzProtocolDecoderSomfyTelis.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderSomfyTelis* pointer to a SubGhzProtocolDecoderSomfyTelis instance\n */\nvoid* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderSomfyTelis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n */\nvoid subghz_protocol_decoder_somfy_telis_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderSomfyTelis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n */\nvoid subghz_protocol_decoder_somfy_telis_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderSomfyTelis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_somfy_telis_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderSomfyTelis.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/protocols/star_line.c",
    "content": "#include \"star_line.h\"\n#include \"keeloq_common.h\"\n\n#include \"../subghz_keystore.h\"\n#include <m-array.h>\n\n#include \"../blocks/const.h\"\n#include \"../blocks/decoder.h\"\n#include \"../blocks/encoder.h\"\n#include \"../blocks/generic.h\"\n#include \"../blocks/math.h\"\n\n#define TAG \"SubGhzProtocolStarLine\"\n\nstatic const SubGhzBlockConst subghz_protocol_star_line_const = {\n    .te_short = 250,\n    .te_long = 500,\n    .te_delta = 120,\n    .min_count_bit_for_found = 64,\n};\n\nstruct SubGhzProtocolDecoderStarLine {\n    SubGhzProtocolDecoderBase base;\n\n    SubGhzBlockDecoder decoder;\n    SubGhzBlockGeneric generic;\n\n    uint16_t header_count;\n    SubGhzKeystore* keystore;\n    const char* manufacture_name;\n};\n\nstruct SubGhzProtocolEncoderStarLine {\n    SubGhzProtocolEncoderBase base;\n\n    SubGhzProtocolBlockEncoder encoder;\n    SubGhzBlockGeneric generic;\n};\n\ntypedef enum {\n    StarLineDecoderStepReset = 0,\n    StarLineDecoderStepCheckPreambula,\n    StarLineDecoderStepSaveDuration,\n    StarLineDecoderStepCheckDuration,\n} StarLineDecoderStep;\n\nconst SubGhzProtocolDecoder subghz_protocol_star_line_decoder = {\n    .alloc = subghz_protocol_decoder_star_line_alloc,\n    .free = subghz_protocol_decoder_star_line_free,\n\n    .feed = subghz_protocol_decoder_star_line_feed,\n    .reset = subghz_protocol_decoder_star_line_reset,\n\n    .get_hash_data = subghz_protocol_decoder_star_line_get_hash_data,\n    .serialize = subghz_protocol_decoder_star_line_serialize,\n    .deserialize = subghz_protocol_decoder_star_line_deserialize,\n    .get_string = subghz_protocol_decoder_star_line_get_string,\n};\n\nconst SubGhzProtocolEncoder subghz_protocol_star_line_encoder = {\n    .alloc = NULL,\n    .free = NULL,\n\n    .deserialize = NULL,\n    .stop = NULL,\n    .yield = NULL,\n};\n\nconst SubGhzProtocol subghz_protocol_star_line = {\n    .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME,\n    .type = SubGhzProtocolTypeDynamic,\n    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,\n\n    .decoder = &subghz_protocol_star_line_decoder,\n    .encoder = &subghz_protocol_star_line_encoder,\n};\n\nvoid* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment) {\n    SubGhzProtocolDecoderStarLine* instance = malloc(sizeof(SubGhzProtocolDecoderStarLine));\n    instance->base.protocol = &subghz_protocol_star_line;\n    instance->generic.protocol_name = instance->base.protocol->name;\n    instance->keystore = subghz_environment_get_keystore(environment);\n\n    return instance;\n}\n\nvoid subghz_protocol_decoder_star_line_free(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n\n    free(instance);\n}\n\nvoid subghz_protocol_decoder_star_line_reset(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n    instance->decoder.parser_step = StarLineDecoderStepReset;\n}\n\nvoid subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n\n    switch(instance->decoder.parser_step) {\n    case StarLineDecoderStepReset:\n        if(level) {\n            if(DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) <\n               subghz_protocol_star_line_const.te_delta * 2) {\n                instance->decoder.parser_step = StarLineDecoderStepCheckPreambula;\n                instance->header_count++;\n            } else if(instance->header_count > 4) {\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = StarLineDecoderStepCheckDuration;\n            }\n        } else {\n            instance->header_count = 0;\n        }\n        break;\n    case StarLineDecoderStepCheckPreambula:\n        if((!level) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) <\n                        subghz_protocol_star_line_const.te_delta * 2)) {\n            //Found Preambula\n            instance->decoder.parser_step = StarLineDecoderStepReset;\n        } else {\n            instance->header_count = 0;\n            instance->decoder.parser_step = StarLineDecoderStepReset;\n        }\n        break;\n    case StarLineDecoderStepSaveDuration:\n        if(level) {\n            if(duration >= (subghz_protocol_star_line_const.te_long +\n                            subghz_protocol_star_line_const.te_delta)) {\n                instance->decoder.parser_step = StarLineDecoderStepReset;\n                if(instance->decoder.decode_count_bit >=\n                   subghz_protocol_star_line_const.min_count_bit_for_found) {\n                    if(instance->generic.data != instance->decoder.decode_data) {\n                        instance->generic.data = instance->decoder.decode_data;\n                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;\n                        if(instance->base.callback)\n                            instance->base.callback(&instance->base, instance->base.context);\n                    }\n                }\n                instance->decoder.decode_data = 0;\n                instance->decoder.decode_count_bit = 0;\n                instance->header_count = 0;\n                break;\n            } else {\n                instance->decoder.te_last = duration;\n                instance->decoder.parser_step = StarLineDecoderStepCheckDuration;\n            }\n\n        } else {\n            instance->decoder.parser_step = StarLineDecoderStepReset;\n        }\n        break;\n    case StarLineDecoderStepCheckDuration:\n        if(!level) {\n            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_short) <\n                subghz_protocol_star_line_const.te_delta) &&\n               (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_short) <\n                subghz_protocol_star_line_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 0);\n                instance->decoder.parser_step = StarLineDecoderStepSaveDuration;\n            } else if(\n                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_long) <\n                 subghz_protocol_star_line_const.te_delta) &&\n                (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long) <\n                 subghz_protocol_star_line_const.te_delta)) {\n                subghz_protocol_blocks_add_bit(&instance->decoder, 1);\n                instance->decoder.parser_step = StarLineDecoderStepSaveDuration;\n            } else {\n                instance->decoder.parser_step = StarLineDecoderStepReset;\n            }\n        } else {\n            instance->decoder.parser_step = StarLineDecoderStepReset;\n        }\n        break;\n    }\n}\n\n/**\n * Validation of decrypt data.\n * @param instance Pointer to a SubGhzBlockGeneric instance\n * @param decrypt Decrypd data\n * @param btn Button number, 4 bit\n * @param end_serial decrement the last 10 bits of the serial number\n * @return true On success\n */\nstatic inline bool subghz_protocol_star_line_check_decrypt(\n    SubGhzBlockGeneric* instance,\n    uint32_t decrypt,\n    uint8_t btn,\n    uint32_t end_serial) {\n    furi_assert(instance);\n    if((decrypt >> 24 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x00FF) == end_serial)) {\n        instance->cnt = decrypt & 0x0000FFFF;\n        return true;\n    }\n    return false;\n}\n\n/** \n * Checking the accepted code against the database manafacture key\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param fix Fix part of the parcel\n * @param hop Hop encrypted part of the parcel\n * @param keystore Pointer to a SubGhzKeystore* instance\n * @param manufacture_name \n * @return true on successful search\n */\nstatic uint8_t subghz_protocol_star_line_check_remote_controller_selector(\n    SubGhzBlockGeneric* instance,\n    uint32_t fix,\n    uint32_t hop,\n    SubGhzKeystore* keystore,\n    const char** manufacture_name) {\n    uint16_t end_serial = (uint16_t)(fix & 0xFF);\n    uint8_t btn = (uint8_t)(fix >> 24);\n    uint32_t decrypt = 0;\n    uint64_t man_normal_learning;\n\n    for\n        M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {\n            switch(manufacture_code->type) {\n            case KEELOQ_LEARNING_SIMPLE:\n                //Simple Learning\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_NORMAL:\n                // Normal_Learning\n                // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37\n                man_normal_learning =\n                    subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            case KEELOQ_LEARNING_UNKNOWN:\n                // Simple Learning\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                // Check for mirrored man\n                uint64_t man_rev = 0;\n                uint64_t man_rev_byte = 0;\n                for(uint8_t i = 0; i < 64; i += 8) {\n                    man_rev_byte = (uint8_t)(manufacture_code->key >> i);\n                    man_rev = man_rev | man_rev_byte << (56 - i);\n                }\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                //###########################\n                // Normal_Learning\n                // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37\n                man_normal_learning =\n                    subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev);\n                decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning);\n                if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) {\n                    *manufacture_name = furi_string_get_cstr(manufacture_code->name);\n                    return 1;\n                }\n                break;\n            }\n        }\n\n    *manufacture_name = \"Unknown\";\n    instance->cnt = 0;\n\n    return 0;\n}\n\n/** \n * Analysis of received data\n * @param instance Pointer to a SubGhzBlockGeneric* instance\n * @param keystore Pointer to a SubGhzKeystore* instance\n * @param manufacture_name\n */\nstatic void subghz_protocol_star_line_check_remote_controller(\n    SubGhzBlockGeneric* instance,\n    SubGhzKeystore* keystore,\n    const char** manufacture_name) {\n    uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);\n    uint32_t key_fix = key >> 32;\n    uint32_t key_hop = key & 0x00000000ffffffff;\n\n    subghz_protocol_star_line_check_remote_controller_selector(\n        instance, key_fix, key_hop, keystore, manufacture_name);\n\n    instance->serial = key_fix & 0x00FFFFFF;\n    instance->btn = key_fix >> 24;\n}\n\nuint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n    return subghz_protocol_blocks_get_hash_data(\n        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);\n}\n\nSubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n    subghz_protocol_star_line_check_remote_controller(\n        &instance->generic, instance->keystore, &instance->manufacture_name);\n    SubGhzProtocolStatus ret =\n        subghz_block_generic_serialize(&instance->generic, flipper_format, preset);\n\n    if((ret == SubGhzProtocolStatusOk) &&\n       !flipper_format_write_string_cstr(\n           flipper_format, \"Manufacture\", instance->manufacture_name)) {\n        FURI_LOG_E(TAG, \"Unable to add manufacture name\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n    if((ret == SubGhzProtocolStatusOk) &&\n       instance->generic.data_count_bit !=\n           subghz_protocol_star_line_const.min_count_bit_for_found) {\n        FURI_LOG_E(TAG, \"Wrong number of bits in key\");\n        ret = SubGhzProtocolStatusErrorParserOthers;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n    return subghz_block_generic_deserialize(&instance->generic, flipper_format);\n}\n\nvoid subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output) {\n    furi_assert(context);\n    SubGhzProtocolDecoderStarLine* instance = context;\n\n    subghz_protocol_star_line_check_remote_controller(\n        &instance->generic, instance->keystore, &instance->manufacture_name);\n\n    uint32_t code_found_hi = instance->generic.data >> 32;\n    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;\n\n    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(\n        instance->generic.data, instance->generic.data_count_bit);\n    uint32_t code_found_reverse_hi = code_found_reverse >> 32;\n    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;\n\n    furi_string_cat_printf(\n        output,\n        \"%s %dbit\\r\\n\"\n        \"Key:%08lX%08lX\\r\\n\"\n        \"Fix:0x%08lX    Cnt:%04lX\\r\\n\"\n        \"Hop:0x%08lX    Btn:%02X\\r\\n\"\n        \"MF:%s\\r\\n\"\n        \"Sn:0x%07lX \\r\\n\",\n        instance->generic.protocol_name,\n        instance->generic.data_count_bit,\n        code_found_hi,\n        code_found_lo,\n        code_found_reverse_hi,\n        instance->generic.cnt,\n        code_found_reverse_lo,\n        instance->generic.btn,\n        instance->manufacture_name,\n        instance->generic.serial);\n}\n"
  },
  {
    "path": "lib/subghz/protocols/star_line.h",
    "content": "#pragma once\n\n#include \"base.h\"\n\n#define SUBGHZ_PROTOCOL_STAR_LINE_NAME \"Star Line\"\n\ntypedef struct SubGhzProtocolDecoderStarLine SubGhzProtocolDecoderStarLine;\ntypedef struct SubGhzProtocolEncoderStarLine SubGhzProtocolEncoderStarLine;\n\nextern const SubGhzProtocolDecoder subghz_protocol_star_line_decoder;\nextern const SubGhzProtocolEncoder subghz_protocol_star_line_encoder;\nextern const SubGhzProtocol subghz_protocol_star_line;\n\n/**\n * Allocate SubGhzProtocolDecoderStarLine.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzProtocolDecoderStarLine* pointer to a SubGhzProtocolDecoderStarLine instance\n */\nvoid* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzProtocolDecoderStarLine.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n */\nvoid subghz_protocol_decoder_star_line_free(void* context);\n\n/**\n * Reset decoder SubGhzProtocolDecoderStarLine.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n */\nvoid subghz_protocol_decoder_star_line_reset(void* context);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration);\n\n/**\n * Getting the hash sum of the last randomly received parcel.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n * @return hash Hash sum\n */\nuint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context);\n\n/**\n * Serialize data SubGhzProtocolDecoderStarLine.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @param preset The modulation on which the signal was received, SubGhzRadioPreset\n * @return status\n */\nSubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize(\n    void* context,\n    FlipperFormat* flipper_format,\n    SubGhzRadioPreset* preset);\n\n/**\n * Deserialize data SubGhzProtocolDecoderStarLine.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format);\n\n/**\n * Getting a textual representation of the received data.\n * @param context Pointer to a SubGhzProtocolDecoderStarLine instance\n * @param output Resulting text\n */\nvoid subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output);\n"
  },
  {
    "path": "lib/subghz/receiver.c",
    "content": "#include \"receiver.h\"\n\n#include \"registry.h\"\n\n#include <m-array.h>\n\ntypedef struct {\n    SubGhzProtocolEncoderBase* base;\n} SubGhzReceiverSlot;\n\nARRAY_DEF(SubGhzReceiverSlotArray, SubGhzReceiverSlot, M_POD_OPLIST); //-V658\n#define M_OPL_SubGhzReceiverSlotArray_t() ARRAY_OPLIST(SubGhzReceiverSlotArray, M_POD_OPLIST)\n\nstruct SubGhzReceiver {\n    SubGhzReceiverSlotArray_t slots;\n    SubGhzProtocolFlag filter;\n\n    SubGhzReceiverCallback callback;\n    void* context;\n};\n\nSubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) {\n    SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver));\n    SubGhzReceiverSlotArray_init(instance->slots);\n    const SubGhzProtocolRegistry* protocol_registry_items =\n        subghz_environment_get_protocol_registry(environment);\n\n    for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) {\n        const SubGhzProtocol* protocol =\n            subghz_protocol_registry_get_by_index(protocol_registry_items, i);\n\n        if(protocol->decoder && protocol->decoder->alloc) {\n            SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots);\n            slot->base = protocol->decoder->alloc(environment);\n        }\n    }\n\n    instance->callback = NULL;\n    instance->context = NULL;\n    return instance;\n}\n\nvoid subghz_receiver_free(SubGhzReceiver* instance) {\n    furi_check(instance);\n\n    instance->callback = NULL;\n    instance->context = NULL;\n\n    // Release allocated slots\n    for\n        M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {\n            slot->base->protocol->decoder->free(slot->base);\n            slot->base = NULL;\n        }\n    SubGhzReceiverSlotArray_clear(instance->slots);\n\n    free(instance);\n}\n\nvoid subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration) {\n    furi_check(instance);\n    furi_check(instance->slots);\n\n    for\n        M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {\n            if((slot->base->protocol->flag & instance->filter) != 0) {\n                slot->base->protocol->decoder->feed(slot->base, level, duration);\n            }\n        }\n}\n\nvoid subghz_receiver_reset(SubGhzReceiver* instance) {\n    furi_check(instance);\n    furi_check(instance->slots);\n\n    for\n        M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {\n            slot->base->protocol->decoder->reset(slot->base);\n        }\n}\n\nstatic void subghz_receiver_rx_callback(SubGhzProtocolDecoderBase* decoder_base, void* context) {\n    SubGhzReceiver* instance = context;\n    if(instance->callback) {\n        instance->callback(instance, decoder_base, instance->context);\n    }\n}\n\nvoid subghz_receiver_set_rx_callback(\n    SubGhzReceiver* instance,\n    SubGhzReceiverCallback callback,\n    void* context) {\n    furi_check(instance);\n\n    for\n        M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {\n            subghz_protocol_decoder_base_set_decoder_callback(\n                (SubGhzProtocolDecoderBase*)slot->base, subghz_receiver_rx_callback, instance);\n        }\n\n    instance->callback = callback;\n    instance->context = context;\n}\n\nvoid subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter) {\n    furi_check(instance);\n    instance->filter = filter;\n}\n\nSubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name(\n    SubGhzReceiver* instance,\n    const char* decoder_name) {\n    furi_check(instance);\n\n    SubGhzProtocolDecoderBase* result = NULL;\n\n    for\n        M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {\n            if(strcmp(slot->base->protocol->name, decoder_name) == 0) {\n                result = (SubGhzProtocolDecoderBase*)slot->base;\n                break;\n            }\n        }\n    return result;\n}\n"
  },
  {
    "path": "lib/subghz/receiver.h",
    "content": "#pragma once\n\n#include \"types.h\"\n#include \"protocols/base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzReceiver SubGhzReceiver;\n\ntypedef void (*SubGhzReceiverCallback)(\n    SubGhzReceiver* decoder,\n    SubGhzProtocolDecoderBase* decoder_base,\n    void* context);\n\n/**\n * Allocate and init SubGhzReceiver.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzReceiver* pointer to a SubGhzReceiver instance\n */\nSubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment);\n\n/**\n * Free SubGhzReceiver.\n * @param instance Pointer to a SubGhzReceiver instance\n */\nvoid subghz_receiver_free(SubGhzReceiver* instance);\n\n/**\n * Parse a raw sequence of levels and durations received from the air.\n * @param instance Pointer to a SubGhzReceiver instance\n * @param level Signal level true-high false-low\n * @param duration Duration of this level in, us\n */\nvoid subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration);\n\n/**\n * Reset decoder SubGhzReceiver.\n * @param instance Pointer to a SubGhzReceiver instance\n */\nvoid subghz_receiver_reset(SubGhzReceiver* instance);\n\n/**\n * Set a callback upon completion of successful decoding of one of the protocols.\n * @param instance Pointer to a SubGhzReceiver instance\n * @param callback Callback, SubGhzReceiverCallback\n * @param context Context\n */\nvoid subghz_receiver_set_rx_callback(\n    SubGhzReceiver* instance,\n    SubGhzReceiverCallback callback,\n    void* context);\n\n/**\n * Set the filter of receivers that will work at the moment.\n * @param instance Pointer to a SubGhzReceiver instance\n * @param filter Filter, SubGhzProtocolFlag\n */\nvoid subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter);\n\n/**\n * Search for a cattery by his name.\n * @param instance Pointer to a SubGhzReceiver instance\n * @param decoder_name Receiver name\n * @return SubGhzProtocolDecoderBase* pointer to a SubGhzProtocolDecoderBase instance\n */\nSubGhzProtocolDecoderBase*\n    subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/registry.c",
    "content": "#include \"registry.h\"\n\nconst SubGhzProtocol* subghz_protocol_registry_get_by_name(\n    const SubGhzProtocolRegistry* protocol_registry,\n    const char* name) {\n    furi_check(protocol_registry);\n\n    for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) {\n        if(strcmp(name, protocol_registry->items[i]->name) == 0) {\n            return protocol_registry->items[i];\n        }\n    }\n    return NULL;\n}\n\nconst SubGhzProtocol* subghz_protocol_registry_get_by_index(\n    const SubGhzProtocolRegistry* protocol_registry,\n    size_t index) {\n    furi_check(protocol_registry);\n    if(index < subghz_protocol_registry_count(protocol_registry)) {\n        return protocol_registry->items[index];\n    } else {\n        return NULL;\n    }\n}\n\nsize_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) {\n    furi_check(protocol_registry);\n    return protocol_registry->size;\n}\n"
  },
  {
    "path": "lib/subghz/registry.h",
    "content": "#pragma once\n\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzEnvironment SubGhzEnvironment;\n\ntypedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;\ntypedef struct SubGhzProtocol SubGhzProtocol;\n\nstruct SubGhzProtocolRegistry {\n    const SubGhzProtocol* const* items;\n    const size_t size;\n};\n\n/**\n * Registration by name SubGhzProtocol.\n * @param protocol_registry SubGhzProtocolRegistry\n * @param name Protocol name\n * @return SubGhzProtocol* pointer to a SubGhzProtocol instance\n */\nconst SubGhzProtocol* subghz_protocol_registry_get_by_name(\n    const SubGhzProtocolRegistry* protocol_registry,\n    const char* name);\n\n/**\n * Registration protocol by index in array SubGhzProtocol.\n * @param protocol_registry SubGhzProtocolRegistry\n * @param index Protocol by index in array\n * @return SubGhzProtocol* pointer to a SubGhzProtocol instance\n */\nconst SubGhzProtocol* subghz_protocol_registry_get_by_index(\n    const SubGhzProtocolRegistry* protocol_registry,\n    size_t index);\n\n/**\n * Getting the number of registered protocols.\n * @param protocol_registry SubGhzProtocolRegistry\n * @return Number of protocols\n */\nsize_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_file_encoder_worker.c",
    "content": "#include \"subghz_file_encoder_worker.h\"\n\n#include <toolbox/stream/stream.h>\n#include <flipper_format/flipper_format.h>\n#include <flipper_format/flipper_format_i.h>\n#include <lib/subghz/devices/devices.h>\n#include <lib/toolbox/strint.h>\n\n#define TAG \"SubGhzFileEncoderWorker\"\n\n#define SUBGHZ_FILE_ENCODER_LOAD 512\n\nstruct SubGhzFileEncoderWorker {\n    FuriThread* thread;\n    FuriStreamBuffer* stream;\n\n    Storage* storage;\n    FlipperFormat* flipper_format;\n\n    volatile bool worker_running;\n    volatile bool worker_stoping;\n    bool is_storage_slow;\n    FuriString* str_data;\n    FuriString* file_path;\n    const SubGhzDevice* device;\n\n    SubGhzFileEncoderWorkerCallbackEnd callback_end;\n    void* context_end;\n};\n\nvoid subghz_file_encoder_worker_callback_end(\n    SubGhzFileEncoderWorker* instance,\n    SubGhzFileEncoderWorkerCallbackEnd callback_end,\n    void* context_end) {\n    furi_assert(instance);\n    furi_assert(callback_end);\n    instance->callback_end = callback_end;\n    instance->context_end = context_end;\n}\n\nvoid subghz_file_encoder_worker_add_level_duration(\n    SubGhzFileEncoderWorker* instance,\n    int32_t duration) {\n    size_t ret = furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100);\n    if(sizeof(int32_t) != ret) FURI_LOG_E(TAG, \"Invalid add duration in the stream\");\n}\n\nbool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) {\n    // Line sample: \"RAW_Data: -1, 2, -2...\"\n\n    // Look for the key in the line\n    char* str = strstr(strStart, \"RAW_Data: \");\n    bool res = false;\n\n    if(str) {\n        // Skip key\n        str = strchr(str, ' ');\n\n        // Parse next element\n        int32_t duration;\n        while(strint_to_int32(str, &str, &duration, 10) == StrintParseNoError) {\n            subghz_file_encoder_worker_add_level_duration(instance, duration);\n            if(*str == ',') str++; // could also be `\\0`\n        }\n\n        res = true;\n    }\n\n    return res;\n}\n\nLevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {\n    furi_assert(context);\n    SubGhzFileEncoderWorker* instance = context;\n    int32_t duration;\n    int ret = furi_stream_buffer_receive(instance->stream, &duration, sizeof(int32_t), 0);\n    if(ret == sizeof(int32_t)) {\n        LevelDuration level_duration = {.level = LEVEL_DURATION_RESET};\n        if(duration < 0) {\n            level_duration = level_duration_make(false, -duration);\n        } else if(duration > 0) {\n            level_duration = level_duration_make(true, duration);\n        } else if(duration == 0) { //-V547\n            level_duration = level_duration_reset();\n            FURI_LOG_I(TAG, \"Stop transmission\");\n            instance->worker_stoping = true;\n        }\n        return level_duration;\n    } else {\n        instance->is_storage_slow = true;\n        return level_duration_wait();\n    }\n}\n\n/** Worker thread\n * \n * @param context \n * @return exit code \n */\nstatic int32_t subghz_file_encoder_worker_thread(void* context) {\n    SubGhzFileEncoderWorker* instance = context;\n    FURI_LOG_I(TAG, \"Worker start\");\n    bool res = false;\n    instance->is_storage_slow = false;\n    Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);\n    do {\n        if(!flipper_format_file_open_existing(\n               instance->flipper_format, furi_string_get_cstr(instance->file_path))) {\n            FURI_LOG_E(\n                TAG,\n                \"Unable to open file for read: %s\",\n                furi_string_get_cstr(instance->file_path));\n            break;\n        }\n        if(!flipper_format_read_string(instance->flipper_format, \"Protocol\", instance->str_data)) {\n            FURI_LOG_E(TAG, \"Missing Protocol\");\n            break;\n        }\n\n        //skip the end of the previous line \"\\n\"\n        stream_seek(stream, 1, StreamOffsetFromCurrent);\n        res = true;\n        instance->worker_stoping = false;\n        FURI_LOG_I(TAG, \"Start transmission\");\n    } while(0);\n\n    while(res && instance->worker_running) {\n        size_t stream_free_byte = furi_stream_buffer_spaces_available(instance->stream);\n        if((stream_free_byte / sizeof(int32_t)) >= SUBGHZ_FILE_ENCODER_LOAD) {\n            if(stream_read_line(stream, instance->str_data)) {\n                furi_string_trim(instance->str_data);\n                if(!subghz_file_encoder_worker_data_parse(\n                       instance, furi_string_get_cstr(instance->str_data))) {\n                    subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);\n                    break;\n                }\n            } else {\n                subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);\n                break;\n            }\n        } else {\n            furi_delay_ms(1);\n        }\n    }\n    //waiting for the end of the transfer\n    if(instance->is_storage_slow) {\n        FURI_LOG_E(TAG, \"Storage is slow\");\n    }\n\n    FURI_LOG_I(TAG, \"End read file\");\n    while(instance->device && !subghz_devices_is_async_complete_tx(instance->device) &&\n          instance->worker_running) {\n        furi_delay_ms(5);\n    }\n\n    FURI_LOG_I(TAG, \"End transmission\");\n    while(instance->worker_running) {\n        if(instance->worker_stoping) {\n            if(instance->callback_end) instance->callback_end(instance->context_end);\n        }\n        furi_delay_ms(50);\n    }\n    flipper_format_file_close(instance->flipper_format);\n\n    FURI_LOG_I(TAG, \"Worker stop\");\n    return 0;\n}\n\nSubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(void) {\n    SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker));\n\n    instance->thread =\n        furi_thread_alloc_ex(\"SubGhzFEWorker\", 2048, subghz_file_encoder_worker_thread, instance);\n    instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t));\n\n    instance->storage = furi_record_open(RECORD_STORAGE);\n    instance->flipper_format = flipper_format_file_alloc(instance->storage);\n\n    instance->str_data = furi_string_alloc();\n    instance->file_path = furi_string_alloc();\n    instance->worker_stoping = true;\n\n    return instance;\n}\n\nvoid subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) {\n    furi_assert(instance);\n\n    furi_stream_buffer_free(instance->stream);\n    furi_thread_free(instance->thread);\n\n    furi_string_free(instance->str_data);\n    furi_string_free(instance->file_path);\n\n    flipper_format_free(instance->flipper_format);\n    furi_record_close(RECORD_STORAGE);\n\n    free(instance);\n}\n\nbool subghz_file_encoder_worker_start(\n    SubGhzFileEncoderWorker* instance,\n    const char* file_path,\n    const char* radio_device_name) {\n    furi_assert(instance);\n    furi_assert(!instance->worker_running);\n\n    furi_stream_buffer_reset(instance->stream);\n    furi_string_set(instance->file_path, file_path);\n    if(radio_device_name) {\n        instance->device = subghz_devices_get_by_name(radio_device_name);\n    }\n    instance->worker_running = true;\n    furi_thread_start(instance->thread);\n\n    return true;\n}\n\nvoid subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) {\n    furi_assert(instance);\n    furi_assert(instance->worker_running);\n\n    instance->worker_running = false;\n    furi_thread_join(instance->thread);\n}\n\nbool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) {\n    furi_assert(instance);\n    return instance->worker_running;\n}\n"
  },
  {
    "path": "lib/subghz/subghz_file_encoder_worker.h",
    "content": "#pragma once\n\n#include <furi_hal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context);\n\ntypedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;\n\n/** \n * End callback SubGhzWorker.\n * @param instance SubGhzFileEncoderWorker instance\n * @param callback SubGhzFileEncoderWorkerCallbackEnd callback\n */\nvoid subghz_file_encoder_worker_callback_end(\n    SubGhzFileEncoderWorker* instance,\n    SubGhzFileEncoderWorkerCallbackEnd callback_end,\n    void* context_end);\n\n/** \n * Allocate SubGhzFileEncoderWorker.\n * @return SubGhzFileEncoderWorker* pointer to a SubGhzFileEncoderWorker instance \n */\nSubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(void);\n\n/** \n * Free SubGhzFileEncoderWorker.\n * @param instance Pointer to a SubGhzFileEncoderWorker instance\n */\nvoid subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzFileEncoderWorker instance\n * @return LevelDuration \n */\nLevelDuration subghz_file_encoder_worker_get_level_duration(void* context);\n\n/** \n * Start SubGhzFileEncoderWorker.\n * @param instance Pointer to a SubGhzFileEncoderWorker instance\n * @param file_path File path\n * @param radio_device_name Radio device name\n * @return bool - true if ok\n */\nbool subghz_file_encoder_worker_start(\n    SubGhzFileEncoderWorker* instance,\n    const char* file_path,\n    const char* radio_device_name);\n\n/** \n * Stop SubGhzFileEncoderWorker\n * @param instance Pointer to a SubGhzFileEncoderWorker instance\n */\nvoid subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance);\n\n/** \n * Check if worker is running\n * @param instance Pointer to a SubGhzFileEncoderWorker instance\n * @return bool - true if running\n */\nbool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_keystore.c",
    "content": "#include \"subghz_keystore.h\"\n\n#include <furi.h>\n#include <furi_hal.h>\n\n#include <storage/storage.h>\n#include <toolbox/hex.h>\n#include <toolbox/stream/stream.h>\n#include <flipper_format/flipper_format.h>\n#include <flipper_format/flipper_format_i.h>\n\n#define TAG \"SubGhzKeystore\"\n\n#define FILE_BUFFER_SIZE 64\n\n#define SUBGHZ_KEYSTORE_FILE_TYPE     \"Flipper SubGhz Keystore File\"\n#define SUBGHZ_KEYSTORE_FILE_RAW_TYPE \"Flipper SubGhz Keystore RAW File\"\n#define SUBGHZ_KEYSTORE_FILE_VERSION  0\n\n#define SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT 1\n#define SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE 512\n#define SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE (SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE * 2)\n\ntypedef enum {\n    SubGhzKeystoreEncryptionNone,\n    SubGhzKeystoreEncryptionAES256,\n} SubGhzKeystoreEncryption;\n\nstruct SubGhzKeystore {\n    SubGhzKeyArray_t data;\n};\n\nSubGhzKeystore* subghz_keystore_alloc(void) {\n    SubGhzKeystore* instance = malloc(sizeof(SubGhzKeystore));\n\n    SubGhzKeyArray_init(instance->data);\n\n    return instance;\n}\n\nvoid subghz_keystore_free(SubGhzKeystore* instance) {\n    furi_assert(instance);\n\n    for\n        M_EACH(manufacture_code, instance->data, SubGhzKeyArray_t) {\n            furi_string_free(manufacture_code->name);\n            manufacture_code->key = 0;\n        }\n    SubGhzKeyArray_clear(instance->data);\n\n    free(instance);\n}\n\nstatic void subghz_keystore_add_key(\n    SubGhzKeystore* instance,\n    const char* name,\n    uint64_t key,\n    uint16_t type) {\n    SubGhzKey* manufacture_code = SubGhzKeyArray_push_raw(instance->data);\n    manufacture_code->name = furi_string_alloc_set(name);\n    manufacture_code->key = key;\n    manufacture_code->type = type;\n}\n\nstatic bool subghz_keystore_process_line(SubGhzKeystore* instance, char* line) {\n    uint64_t key = 0;\n    uint16_t type = 0;\n    char skey[17] = {0};\n    char name[65] = {0};\n    int ret = sscanf(line, \"%16s:%hu:%64s\", skey, &type, name);\n    key = strtoull(skey, NULL, 16);\n    if(ret == 3) {\n        subghz_keystore_add_key(instance, name, key, type);\n        return true;\n    } else {\n        FURI_LOG_E(TAG, \"Failed to load line: %s\\r\\n\", line);\n        return false;\n    }\n}\n\nstatic void subghz_keystore_mess_with_iv(uint8_t* iv) {\n    // Alignment check for `ldrd` instruction\n    furi_assert(((uint32_t)iv) % 4 == 0);\n    // Please do not share decrypted manufacture keys\n    // Sharing them will bring some discomfort to legal owners\n    // And potential legal action against you\n    // While you reading this code think about your own personal responsibility\n    asm volatile(\"nani%=:                  \\n\"\n                 \"ldrd  r0, r2, [%0, #0x0] \\n\"\n                 \"lsl   r1, r0, #8         \\n\"\n                 \"lsl   r3, r2, #8         \\n\"\n                 \"orr   r3, r3, r0, lsr #24\\n\"\n                 \"uadd8 r1, r1, r0         \\n\"\n                 \"uadd8 r3, r3, r2         \\n\"\n                 \"strd  r1, r3, [%0, #0x0] \\n\"\n                 \"ldrd  r1, r3, [%0, #0x8] \\n\"\n                 \"lsl   r0, r1, #8         \\n\"\n                 \"orr   r0, r0, r2, lsr #24\\n\"\n                 \"lsl   r2, r3, #8         \\n\"\n                 \"orr   r2, r2, r1, lsr #24\\n\"\n                 \"uadd8 r1, r1, r0         \\n\"\n                 \"uadd8 r3, r3, r2         \\n\"\n                 \"strd  r1, r3, [%0, #0x8] \\n\"\n                 :\n                 : \"r\"(iv)\n                 : \"r0\", \"r1\", \"r2\", \"r3\", \"memory\");\n}\n\nstatic bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, uint8_t* iv) {\n    bool result = true;\n    uint8_t buffer[FILE_BUFFER_SIZE];\n\n    char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n    char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n    size_t encrypted_line_cursor = 0;\n\n    do {\n        if(iv) {\n            if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {\n                FURI_LOG_E(TAG, \"Unable to load decryption key\");\n                break;\n            }\n        }\n\n        size_t ret = 0;\n        do {\n            ret = stream_read(stream, buffer, FILE_BUFFER_SIZE);\n            for(uint16_t i = 0; i < ret; i++) {\n                if(buffer[i] == '\\n' && encrypted_line_cursor > 0) {\n                    // Process line\n                    if(iv) {\n                        // Data alignment check, 32 instead of 16 because of hex encoding\n                        size_t len = strlen(encrypted_line);\n                        if(len % 32 == 0) {\n                            // Inplace hex to bin conversion\n                            for(size_t i = 0; i < len; i += 2) {\n                                uint8_t hi_nibble = 0;\n                                uint8_t lo_nibble = 0;\n                                hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble);\n                                hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble);\n                                encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble;\n                            }\n                            len /= 2;\n\n                            if(furi_hal_crypto_decrypt(\n                                   (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) {\n                                subghz_keystore_process_line(instance, decrypted_line);\n                            } else {\n                                FURI_LOG_E(TAG, \"Decryption failed\");\n                                result = false;\n                                break;\n                            }\n                        } else {\n                            FURI_LOG_E(TAG, \"Invalid encrypted data: %s\", encrypted_line);\n                        }\n                    } else {\n                        subghz_keystore_process_line(instance, encrypted_line);\n                    }\n                    // reset line buffer\n                    memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n                    memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n                    encrypted_line_cursor = 0;\n                } else if(buffer[i] == '\\r' || buffer[i] == '\\n') {\n                    // do not add line endings to the buffer\n                } else {\n                    if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) {\n                        encrypted_line[encrypted_line_cursor] = buffer[i];\n                        encrypted_line_cursor++;\n                    } else {\n                        FURI_LOG_E(TAG, \"Malformed file\");\n                        result = false;\n                        break;\n                    }\n                }\n            }\n        } while(ret > 0 && result);\n\n        if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);\n    } while(false);\n\n    free(encrypted_line);\n    free(decrypted_line);\n\n    return result;\n}\n\nbool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) {\n    furi_assert(instance);\n    bool result = false;\n    uint8_t iv[16];\n    uint32_t version;\n    uint32_t encryption;\n\n    FuriString* filetype;\n    filetype = furi_string_alloc();\n\n    FURI_LOG_I(TAG, \"Loading keystore %s\", file_name);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n    do {\n        if(!flipper_format_file_open_existing(flipper_format, file_name)) {\n            FURI_LOG_E(TAG, \"Unable to open file for read: %s\", file_name);\n            break;\n        }\n        if(!flipper_format_read_header(flipper_format, filetype, &version)) {\n            FURI_LOG_E(TAG, \"Missing or incorrect header\");\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"Encryption\", (uint32_t*)&encryption, 1)) {\n            FURI_LOG_E(TAG, \"Missing encryption type\");\n            break;\n        }\n\n        if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 ||\n           version != SUBGHZ_KEYSTORE_FILE_VERSION) {\n            FURI_LOG_E(TAG, \"Type or version mismatch\");\n            break;\n        }\n\n        Stream* stream = flipper_format_get_raw_stream(flipper_format);\n        if(encryption == SubGhzKeystoreEncryptionNone) {\n            result = subghz_keystore_read_file(instance, stream, NULL);\n        } else if(encryption == SubGhzKeystoreEncryptionAES256) {\n            if(!flipper_format_read_hex(flipper_format, \"IV\", iv, 16)) {\n                FURI_LOG_E(TAG, \"Missing IV\");\n                break;\n            }\n            subghz_keystore_mess_with_iv(iv);\n            result = subghz_keystore_read_file(instance, stream, iv);\n        } else {\n            FURI_LOG_E(TAG, \"Unknown encryption\");\n            break;\n        }\n    } while(0);\n    flipper_format_free(flipper_format);\n\n    furi_record_close(RECORD_STORAGE);\n\n    furi_string_free(filetype);\n\n    return result;\n}\n\nbool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8_t* iv) {\n    furi_assert(instance);\n    bool result = false;\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n    char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n    do {\n        if(!flipper_format_file_open_always(flipper_format, file_name)) {\n            FURI_LOG_E(TAG, \"Unable to open file for write: %s\", file_name);\n            break;\n        }\n        if(!flipper_format_write_header_cstr(\n               flipper_format, SUBGHZ_KEYSTORE_FILE_TYPE, SUBGHZ_KEYSTORE_FILE_VERSION)) {\n            FURI_LOG_E(TAG, \"Unable to add header\");\n            break;\n        }\n        uint32_t encryption = SubGhzKeystoreEncryptionAES256;\n        if(!flipper_format_write_uint32(flipper_format, \"Encryption\", &encryption, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Encryption\");\n            break;\n        }\n        if(!flipper_format_write_hex(flipper_format, \"IV\", iv, 16)) {\n            FURI_LOG_E(TAG, \"Unable to add IV\");\n            break;\n        }\n\n        subghz_keystore_mess_with_iv(iv);\n\n        if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {\n            FURI_LOG_E(TAG, \"Unable to load encryption key\");\n            break;\n        }\n\n        Stream* stream = flipper_format_get_raw_stream(flipper_format);\n        size_t encrypted_line_count = 0;\n        for\n            M_EACH(key, instance->data, SubGhzKeyArray_t) {\n                // Wipe buffer before packing\n                memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n                memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n                // Form unecreypted line\n                int len = snprintf(\n                    decrypted_line,\n                    SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE,\n                    \"%08lX%08lX:%hu:%s\",\n                    (uint32_t)(key->key >> 32),\n                    (uint32_t)key->key,\n                    key->type,\n                    furi_string_get_cstr(key->name));\n                // Verify length and align\n                furi_assert(len > 0);\n                if(len % 16 != 0) {\n                    len += (16 - len % 16);\n                }\n                furi_assert(len % 16 == 0);\n                furi_assert(len <= SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n                // Form encrypted line\n                if(!furi_hal_crypto_encrypt(\n                       (uint8_t*)decrypted_line, (uint8_t*)encrypted_line, len)) {\n                    FURI_LOG_E(TAG, \"Encryption failed\");\n                    break;\n                }\n                // HEX Encode encrypted line\n                const char xx[] = \"0123456789ABCDEF\";\n                for(int i = 0; i < len; i++) {\n                    size_t cursor = len - i - 1;\n                    size_t hex_cursor = len * 2 - i * 2 - 1;\n                    encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF];\n                    encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF];\n                }\n                stream_write_cstring(stream, encrypted_line);\n                stream_write_char(stream, '\\n');\n                encrypted_line_count++;\n            }\n        furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);\n        size_t total_keys = SubGhzKeyArray_size(instance->data);\n        result = encrypted_line_count == total_keys;\n        if(result) {\n            FURI_LOG_I(TAG, \"Success. Encrypted: %zu of %zu\", encrypted_line_count, total_keys);\n        } else {\n            FURI_LOG_E(TAG, \"Failure. Encrypted: %zu of %zu\", encrypted_line_count, total_keys);\n        }\n    } while(0);\n    flipper_format_free(flipper_format);\n\n    free(encrypted_line);\n    free(decrypted_line);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n\nSubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance) {\n    furi_assert(instance);\n    return &instance->data;\n}\n\nbool subghz_keystore_raw_encrypted_save(\n    const char* input_file_name,\n    const char* output_file_name,\n    uint8_t* iv) {\n    bool encrypted = false;\n    uint32_t version;\n    uint32_t encryption;\n    FuriString* filetype;\n    filetype = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n\n    FlipperFormat* input_flipper_format = flipper_format_file_alloc(storage);\n    do {\n        if(!flipper_format_file_open_existing(input_flipper_format, input_file_name)) {\n            FURI_LOG_E(TAG, \"Unable to open file for read: %s\", input_file_name);\n            break;\n        }\n        if(!flipper_format_read_header(input_flipper_format, filetype, &version)) {\n            FURI_LOG_E(TAG, \"Missing or incorrect header\");\n            break;\n        }\n        if(!flipper_format_read_uint32(\n               input_flipper_format, \"Encryption\", (uint32_t*)&encryption, 1)) {\n            FURI_LOG_E(TAG, \"Missing encryption type\");\n            break;\n        }\n\n        if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 ||\n           version != SUBGHZ_KEYSTORE_FILE_VERSION) {\n            FURI_LOG_E(TAG, \"Type or version mismatch\");\n            break;\n        }\n\n        if(encryption != SubGhzKeystoreEncryptionNone) {\n            FURI_LOG_E(TAG, \"Already encryption\");\n            break;\n        }\n        Stream* input_stream = flipper_format_get_raw_stream(input_flipper_format);\n\n        FlipperFormat* output_flipper_format = flipper_format_file_alloc(storage);\n\n        if(!flipper_format_file_open_always(output_flipper_format, output_file_name)) {\n            FURI_LOG_E(TAG, \"Unable to open file for write: %s\", output_file_name);\n            break;\n        }\n        if(!flipper_format_write_header_cstr(\n               output_flipper_format,\n               furi_string_get_cstr(filetype),\n               SUBGHZ_KEYSTORE_FILE_VERSION)) {\n            FURI_LOG_E(TAG, \"Unable to add header\");\n            break;\n        }\n        uint32_t encryption = SubGhzKeystoreEncryptionAES256;\n        if(!flipper_format_write_uint32(output_flipper_format, \"Encryption\", &encryption, 1)) {\n            FURI_LOG_E(TAG, \"Unable to add Encryption\");\n            break;\n        }\n        if(!flipper_format_write_hex(output_flipper_format, \"IV\", iv, 16)) {\n            FURI_LOG_E(TAG, \"Unable to add IV\");\n            break;\n        }\n\n        if(!flipper_format_write_string_cstr(output_flipper_format, \"Encrypt_data\", \"RAW\")) {\n            FURI_LOG_E(TAG, \"Unable to add Encrypt_data\");\n            break;\n        }\n\n        subghz_keystore_mess_with_iv(iv);\n\n        if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {\n            FURI_LOG_E(TAG, \"Unable to load encryption key\");\n            break;\n        }\n\n        Stream* output_stream = flipper_format_get_raw_stream(output_flipper_format);\n        uint8_t buffer[FILE_BUFFER_SIZE];\n        bool result = true;\n\n        size_t ret = 0;\n        furi_assert(FILE_BUFFER_SIZE % 16 == 0);\n\n        //skip the end of the previous line \"\\n\"\n        stream_read(input_stream, buffer, 1);\n\n        do {\n            memset(buffer, 0, FILE_BUFFER_SIZE);\n            ret = stream_read(input_stream, buffer, FILE_BUFFER_SIZE);\n            if(ret == 0) {\n                break;\n            }\n\n            for(uint16_t i = 0; i < FILE_BUFFER_SIZE - 1; i += 2) {\n                uint8_t hi_nibble = 0;\n                uint8_t lo_nibble = 0;\n                hex_char_to_hex_nibble(buffer[i], &hi_nibble);\n                hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble);\n                buffer[i / 2] = (hi_nibble << 4) | lo_nibble;\n            }\n\n            memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE);\n            // Form encrypted line\n            if(!furi_hal_crypto_encrypt(\n                   (uint8_t*)buffer, (uint8_t*)encrypted_line, FILE_BUFFER_SIZE / 2)) {\n                FURI_LOG_E(TAG, \"Encryption failed\");\n                result = false;\n                break;\n            }\n\n            // HEX Encode encrypted line\n            const char xx[] = \"0123456789ABCDEF\";\n            for(size_t i = 0; i < FILE_BUFFER_SIZE / 2; i++) {\n                size_t cursor = FILE_BUFFER_SIZE / 2 - i - 1;\n                size_t hex_cursor = FILE_BUFFER_SIZE - i * 2 - 1;\n                encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF];\n                encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF];\n            }\n            stream_write_cstring(output_stream, encrypted_line);\n\n        } while(true);\n\n        flipper_format_free(output_flipper_format);\n\n        furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);\n\n        if(!result) break;\n\n        encrypted = true;\n    } while(0);\n\n    flipper_format_free(input_flipper_format);\n\n    free(encrypted_line);\n\n    furi_record_close(RECORD_STORAGE);\n\n    return encrypted;\n}\n\nbool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len) {\n    bool result = false;\n    uint8_t iv[16];\n    uint32_t version;\n    uint32_t encryption;\n\n    FuriString* str_temp;\n    str_temp = furi_string_alloc();\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n\n    FlipperFormat* flipper_format = flipper_format_file_alloc(storage);\n    do {\n        if(!flipper_format_file_open_existing(flipper_format, file_name)) {\n            FURI_LOG_E(TAG, \"Unable to open file for read: %s\", file_name);\n            break;\n        }\n        if(!flipper_format_read_header(flipper_format, str_temp, &version)) {\n            FURI_LOG_E(TAG, \"Missing or incorrect header\");\n            break;\n        }\n        if(!flipper_format_read_uint32(flipper_format, \"Encryption\", (uint32_t*)&encryption, 1)) {\n            FURI_LOG_E(TAG, \"Missing encryption type\");\n            break;\n        }\n\n        if(strcmp(furi_string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 ||\n           version != SUBGHZ_KEYSTORE_FILE_VERSION) {\n            FURI_LOG_E(TAG, \"Type or version mismatch\");\n            break;\n        }\n\n        Stream* stream = flipper_format_get_raw_stream(flipper_format);\n        if(encryption != SubGhzKeystoreEncryptionAES256) {\n            FURI_LOG_E(TAG, \"Unknown encryption\");\n            break;\n        }\n\n        if(offset < 16) {\n            if(!flipper_format_read_hex(flipper_format, \"IV\", iv, 16)) {\n                FURI_LOG_E(TAG, \"Missing IV\");\n                break;\n            }\n            subghz_keystore_mess_with_iv(iv);\n        }\n\n        if(!flipper_format_read_string(flipper_format, \"Encrypt_data\", str_temp)) {\n            FURI_LOG_E(TAG, \"Missing Encrypt_data\");\n            break;\n        }\n\n        size_t bufer_size;\n        if(len <= (16 - offset % 16)) {\n            bufer_size = 32;\n        } else {\n            bufer_size = (((len) / 16) + 2) * 32;\n        }\n        furi_assert(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE >= bufer_size / 2);\n\n        uint8_t buffer[bufer_size];\n        size_t ret = 0;\n        bool decrypted = true;\n        //skip the end of the previous line \"\\n\"\n        stream_read(stream, buffer, 1);\n\n        size_t size = stream_size(stream);\n        size -= stream_tell(stream);\n        if(size < (offset * 2 + len * 2)) {\n            FURI_LOG_E(TAG, \"Seek position exceeds file size\");\n            break;\n        }\n\n        if(offset >= 16) {\n            stream_seek(stream, ((offset / 16) - 1) * 32, StreamOffsetFromCurrent);\n            ret = stream_read(stream, buffer, 32);\n            furi_assert(ret == 32);\n            for(uint16_t i = 0; i < ret - 1; i += 2) {\n                uint8_t hi_nibble = 0;\n                uint8_t lo_nibble = 0;\n                hex_char_to_hex_nibble(buffer[i], &hi_nibble);\n                hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble);\n                iv[i / 2] = (hi_nibble << 4) | lo_nibble;\n            }\n        }\n\n        if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {\n            FURI_LOG_E(TAG, \"Unable to load encryption key\");\n            break;\n        }\n\n        do {\n            memset(buffer, 0, bufer_size);\n            ret = stream_read(stream, buffer, bufer_size);\n            furi_assert(ret == bufer_size);\n            for(uint16_t i = 0; i < ret - 1; i += 2) {\n                uint8_t hi_nibble = 0;\n                uint8_t lo_nibble = 0;\n                hex_char_to_hex_nibble(buffer[i], &hi_nibble);\n                hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble);\n                buffer[i / 2] = (hi_nibble << 4) | lo_nibble;\n            }\n\n            memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE);\n\n            if(!furi_hal_crypto_decrypt(\n                   (uint8_t*)buffer, (uint8_t*)decrypted_line, bufer_size / 2)) {\n                decrypted = false;\n                FURI_LOG_E(TAG, \"Decryption failed\");\n                break;\n            }\n            memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len);\n\n        } while(0);\n        furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);\n        if(decrypted) result = true;\n    } while(0);\n    flipper_format_free(flipper_format);\n\n    furi_record_close(RECORD_STORAGE);\n\n    free(decrypted_line);\n\n    furi_string_free(str_temp);\n\n    return result;\n}\n"
  },
  {
    "path": "lib/subghz/subghz_keystore.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <m-array.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    FuriString* name;\n    uint64_t key;\n    uint16_t type;\n} SubGhzKey;\n\nARRAY_DEF(SubGhzKeyArray, SubGhzKey, M_POD_OPLIST) //-V658\n\n#define M_OPL_SubGhzKeyArray_t() ARRAY_OPLIST(SubGhzKeyArray, M_POD_OPLIST)\n\ntypedef struct SubGhzKeystore SubGhzKeystore;\n\n/**\n * Allocate SubGhzKeystore.\n * @return SubGhzKeystore* pointer to a SubGhzKeystore instance\n */\nSubGhzKeystore* subghz_keystore_alloc(void);\n\n/**\n * Free SubGhzKeystore.\n * @param instance Pointer to a SubGhzKeystore instance\n */\nvoid subghz_keystore_free(SubGhzKeystore* instance);\n\n/** \n * Loading manufacture key from file\n * @param instance Pointer to a SubGhzKeystore instance\n * @param filename Full path to the file\n */\nbool subghz_keystore_load(SubGhzKeystore* instance, const char* filename);\n\n/** \n * Save manufacture key to file\n * @param instance Pointer to a SubGhzKeystore instance\n * @param filename Full path to the file\n * @return true On success\n */\nbool subghz_keystore_save(SubGhzKeystore* instance, const char* filename, uint8_t* iv);\n\n/** \n * Get array of keys and names manufacture\n * @param instance Pointer to a SubGhzKeystore instance\n * @return SubGhzKeyArray_t*\n */\nSubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance);\n\n/** \n * Save RAW encrypted to file\n * @param input_file_name Full path to the input file\n * @param output_file_name Full path to the output file\n * @param iv IV, 16 bytes in hex\n */\nbool subghz_keystore_raw_encrypted_save(\n    const char* input_file_name,\n    const char* output_file_name,\n    uint8_t* iv);\n\n/** \n * Get decrypt RAW data to file\n * @param file_name Full path to the input file\n * @param offset Offset from the start of the RAW data\n * @param data Returned array\n * @param len Required data length\n * @return true On success\n */\nbool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_protocol_registry.h",
    "content": "#pragma once\n\n#include \"registry.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern const SubGhzProtocolRegistry subghz_protocol_registry;\n\ntypedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_setting.c",
    "content": "#include \"subghz_setting.h\"\n#include \"types.h\" // IWYU pragma: keep\n\n#include <furi.h>\n#include <m-list.h>\n#include <lib/subghz/devices/cc1101_configs.h>\n\n#define TAG \"SubGhzSetting\"\n\n#define SUBGHZ_SETTING_FILE_TYPE    \"Flipper SubGhz Setting File\"\n#define SUBGHZ_SETTING_FILE_VERSION 1\n\n#define FREQUENCY_FLAG_DEFAULT (1 << 31)\n#define FREQUENCY_MASK         (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT)\n\n/* Default */\nstatic const uint32_t subghz_frequency_list[] = {\n    /* 300 - 348 */\n    300000000,\n    303875000,\n    304250000,\n    310000000,\n    315000000,\n    318000000,\n\n    /* 387 - 464 */\n    390000000,\n    418000000,\n    433075000, /* LPD433 first */\n    433420000,\n    433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */\n    434420000,\n    434775000, /* LPD433 last channels */\n    438900000,\n\n    /* 779 - 928 */\n    868350000,\n    915000000,\n    925000000,\n    0,\n};\n\nstatic const uint32_t subghz_hopper_frequency_list[] = {\n    310000000,\n    315000000,\n    318000000,\n    390000000,\n    433920000,\n    868350000,\n    0,\n};\n\n/* Europe and Russia */\nstatic const uint32_t subghz_frequency_list_region_eu_ru[] = {\n    /* 300 - 348 */\n    300000000,\n    303875000,\n    304250000,\n    310000000,\n    315000000,\n    318000000,\n\n    /* 387 - 464 */\n    390000000,\n    418000000,\n    433075000, /* LPD433 first */\n    433420000,\n    433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */\n    434420000,\n    434775000, /* LPD433 last channels */\n    438900000,\n\n    /* 779 - 928 */\n    868350000,\n    915000000,\n    925000000,\n    0,\n};\nstatic const uint32_t subghz_hopper_frequency_list_region_eu_ru[] = {\n    310000000,\n    315000000,\n    318000000,\n    390000000,\n    433920000,\n    868350000,\n    0,\n};\n\n/* Region 0 */\nstatic const uint32_t subghz_frequency_list_region_us_ca_au[] = {\n    /* 300 - 348 */\n    300000000,\n    303875000,\n    304250000,\n    310000000,\n    315000000,\n    318000000,\n\n    /* 387 - 464 */\n    390000000,\n    418000000,\n    433075000, /* LPD433 first */\n    433420000,\n    433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */\n    434420000,\n    434775000, /* LPD433 last channels */\n    438900000,\n\n    /* 779 - 928 */\n    868350000,\n    915000000,\n    925000000,\n    0,\n};\nstatic const uint32_t subghz_hopper_frequency_list_region_us_ca_au[] = {\n    310000000,\n    315000000,\n    318000000,\n    390000000,\n    433920000,\n    868350000,\n    0,\n};\n\nstatic const uint32_t subghz_frequency_list_region_jp[] = {\n    /* 300 - 348 */\n    300000000,\n    303875000,\n    304250000,\n    310000000,\n    315000000,\n    318000000,\n\n    /* 387 - 464 */\n    390000000,\n    418000000,\n    433075000, /* LPD433 first */\n    433420000,\n    433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */\n    434420000,\n    434775000, /* LPD433 last channels */\n    438900000,\n\n    /* 779 - 928 */\n    868350000,\n    915000000,\n    925000000,\n    0,\n};\nstatic const uint32_t subghz_hopper_frequency_list_region_jp[] = {\n    310000000,\n    315000000,\n    318000000,\n    390000000,\n    433920000,\n    868350000,\n    0,\n};\n\ntypedef struct {\n    FuriString* custom_preset_name;\n    uint8_t* custom_preset_data;\n    size_t custom_preset_data_size;\n} SubGhzSettingCustomPresetItem;\n\nARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST) //-V658\n\n#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \\\n    ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST)\n\nLIST_DEF(FrequencyList, uint32_t)\n\n#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList)\n\ntypedef struct {\n    SubGhzSettingCustomPresetItemArray_t data;\n} SubGhzSettingCustomPresetStruct;\n\nstruct SubGhzSetting {\n    FrequencyList_t frequencies;\n    FrequencyList_t hopper_frequencies;\n    SubGhzSettingCustomPresetStruct* preset;\n};\n\nSubGhzSetting* subghz_setting_alloc(void) {\n    SubGhzSetting* instance = malloc(sizeof(SubGhzSetting));\n    FrequencyList_init(instance->frequencies);\n    FrequencyList_init(instance->hopper_frequencies);\n    instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct));\n    SubGhzSettingCustomPresetItemArray_init(instance->preset->data);\n    return instance;\n}\n\nstatic void subghz_setting_preset_reset(SubGhzSetting* instance) {\n    for\n        M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {\n            furi_string_free(item->custom_preset_name);\n            free(item->custom_preset_data);\n        }\n    SubGhzSettingCustomPresetItemArray_reset(instance->preset->data);\n}\n\nvoid subghz_setting_free(SubGhzSetting* instance) {\n    furi_check(instance);\n    FrequencyList_clear(instance->frequencies);\n    FrequencyList_clear(instance->hopper_frequencies);\n    for\n        M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {\n            furi_string_free(item->custom_preset_name);\n            free(item->custom_preset_data);\n        }\n    SubGhzSettingCustomPresetItemArray_clear(instance->preset->data);\n    free(instance->preset);\n    free(instance);\n}\n\nstatic void subghz_setting_load_default_preset(\n    SubGhzSetting* instance,\n    const char* preset_name,\n    const uint8_t* preset_data) {\n    furi_assert(instance);\n    furi_assert(preset_data);\n    uint32_t preset_data_count = 0;\n    SubGhzSettingCustomPresetItem* item =\n        SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data);\n\n    item->custom_preset_name = furi_string_alloc();\n    furi_string_set(item->custom_preset_name, preset_name);\n\n    while(preset_data[preset_data_count]) {\n        preset_data_count += 2;\n    }\n    preset_data_count += 2;\n    item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8;\n    item->custom_preset_data = malloc(item->custom_preset_data_size);\n    //load preset register + pa table\n    memcpy(&item->custom_preset_data[0], &preset_data[0], item->custom_preset_data_size);\n}\n\nstatic void subghz_setting_load_default_region(\n    SubGhzSetting* instance,\n    const uint32_t frequencies[],\n    const uint32_t hopper_frequencies[]) {\n    furi_assert(instance);\n\n    FrequencyList_reset(instance->frequencies);\n    FrequencyList_reset(instance->hopper_frequencies);\n    subghz_setting_preset_reset(instance);\n\n    while(*frequencies) {\n        FrequencyList_push_back(instance->frequencies, *frequencies);\n        frequencies++;\n    }\n\n    while(*hopper_frequencies) {\n        FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies);\n        hopper_frequencies++;\n    }\n\n    subghz_setting_load_default_preset(\n        instance, \"AM270\", subghz_device_cc1101_preset_ook_270khz_async_regs);\n    subghz_setting_load_default_preset(\n        instance, \"AM650\", subghz_device_cc1101_preset_ook_650khz_async_regs);\n    subghz_setting_load_default_preset(\n        instance, \"FM238\", subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);\n    subghz_setting_load_default_preset(\n        instance, \"FM476\", subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);\n}\n\nvoid subghz_setting_load_default(SubGhzSetting* instance) {\n    switch(furi_hal_version_get_hw_region()) {\n    case FuriHalVersionRegionEuRu:\n        subghz_setting_load_default_region(\n            instance,\n            subghz_frequency_list_region_eu_ru,\n            subghz_hopper_frequency_list_region_eu_ru);\n        break;\n    case FuriHalVersionRegionUsCaAu:\n        subghz_setting_load_default_region(\n            instance,\n            subghz_frequency_list_region_us_ca_au,\n            subghz_hopper_frequency_list_region_us_ca_au);\n        break;\n    case FuriHalVersionRegionJp:\n        subghz_setting_load_default_region(\n            instance, subghz_frequency_list_region_jp, subghz_hopper_frequency_list_region_jp);\n        break;\n\n    default:\n        subghz_setting_load_default_region(\n            instance, subghz_frequency_list, subghz_hopper_frequency_list);\n        break;\n    }\n}\n\nvoid subghz_setting_load(SubGhzSetting* instance, const char* file_path) {\n    furi_check(instance);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);\n\n    FuriString* temp_str;\n    temp_str = furi_string_alloc();\n    uint32_t temp_data32;\n    bool temp_bool;\n\n    subghz_setting_load_default(instance);\n\n    if(file_path) {\n        do {\n            if(!flipper_format_file_open_existing(fff_data_file, file_path)) {\n                FURI_LOG_I(TAG, \"File is not used %s\", file_path);\n                break;\n            }\n\n            if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {\n                FURI_LOG_E(TAG, \"Missing or incorrect header\");\n                break;\n            }\n\n            if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) &&\n               temp_data32 == SUBGHZ_SETTING_FILE_VERSION) {\n            } else {\n                FURI_LOG_E(TAG, \"Type or version mismatch\");\n                break;\n            }\n\n            // Standard frequencies (optional)\n            temp_bool = true;\n            flipper_format_read_bool(fff_data_file, \"Add_standard_frequencies\", &temp_bool, 1);\n            if(!temp_bool) {\n                FURI_LOG_I(TAG, \"Removing standard frequencies\");\n                FrequencyList_reset(instance->frequencies);\n                FrequencyList_reset(instance->hopper_frequencies);\n            } else {\n                FURI_LOG_I(TAG, \"Keeping standard frequencies\");\n            }\n\n            // Load frequencies\n            if(!flipper_format_rewind(fff_data_file)) {\n                FURI_LOG_E(TAG, \"Rewind error\");\n                break;\n            }\n            while(flipper_format_read_uint32(\n                fff_data_file, \"Frequency\", (uint32_t*)&temp_data32, 1)) {\n                //Todo FL-3535: add a frequency support check depending on the selected radio device\n                if(furi_hal_subghz_is_frequency_valid(temp_data32)) {\n                    FURI_LOG_I(TAG, \"Frequency loaded %lu\", temp_data32);\n                    FrequencyList_push_back(instance->frequencies, temp_data32);\n                } else {\n                    FURI_LOG_E(TAG, \"Frequency not supported %lu\", temp_data32);\n                }\n            }\n\n            // Load hopper frequencies\n            if(!flipper_format_rewind(fff_data_file)) {\n                FURI_LOG_E(TAG, \"Rewind error\");\n                break;\n            }\n            while(flipper_format_read_uint32(\n                fff_data_file, \"Hopper_frequency\", (uint32_t*)&temp_data32, 1)) {\n                if(furi_hal_subghz_is_frequency_valid(temp_data32)) {\n                    FURI_LOG_I(TAG, \"Hopper frequency loaded %lu\", temp_data32);\n                    FrequencyList_push_back(instance->hopper_frequencies, temp_data32);\n                } else {\n                    FURI_LOG_E(TAG, \"Hopper frequency not supported %lu\", temp_data32);\n                }\n            }\n\n            // Default frequency (optional)\n            if(!flipper_format_rewind(fff_data_file)) {\n                FURI_LOG_E(TAG, \"Rewind error\");\n                break;\n            }\n            if(flipper_format_read_uint32(fff_data_file, \"Default_frequency\", &temp_data32, 1)) {\n                for\n                    M_EACH(frequency, instance->frequencies, FrequencyList_t) {\n                        *frequency &= FREQUENCY_MASK;\n                        if(*frequency == temp_data32) {\n                            *frequency |= FREQUENCY_FLAG_DEFAULT;\n                        }\n                    }\n            }\n\n            // custom preset (optional)\n            if(!flipper_format_rewind(fff_data_file)) {\n                FURI_LOG_E(TAG, \"Rewind error\");\n                break;\n            }\n            while(flipper_format_read_string(fff_data_file, \"Custom_preset_name\", temp_str)) {\n                FURI_LOG_I(TAG, \"Custom preset loaded %s\", furi_string_get_cstr(temp_str));\n                subghz_setting_load_custom_preset(\n                    instance, furi_string_get_cstr(temp_str), fff_data_file);\n            }\n\n        } while(false);\n    }\n\n    furi_string_free(temp_str);\n    flipper_format_free(fff_data_file);\n    furi_record_close(RECORD_STORAGE);\n\n    if(!FrequencyList_size(instance->frequencies) ||\n       !FrequencyList_size(instance->hopper_frequencies)) {\n        FURI_LOG_E(TAG, \"Error loading user settings, loading default settings\");\n        subghz_setting_load_default(instance);\n    }\n}\n\nsize_t subghz_setting_get_frequency_count(SubGhzSetting* instance) {\n    furi_check(instance);\n    return FrequencyList_size(instance->frequencies);\n}\n\nsize_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) {\n    furi_check(instance);\n    return FrequencyList_size(instance->hopper_frequencies);\n}\n\nsize_t subghz_setting_get_preset_count(SubGhzSetting* instance) {\n    furi_check(instance);\n    return SubGhzSettingCustomPresetItemArray_size(instance->preset->data);\n}\n\nconst char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) {\n    furi_check(instance);\n    SubGhzSettingCustomPresetItem* item =\n        SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);\n    return furi_string_get_cstr(item->custom_preset_name);\n}\n\nint subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) {\n    furi_check(instance);\n    size_t idx = 0;\n    for\n        M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {\n            if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) {\n                return idx;\n            }\n            idx++;\n        }\n    furi_crash(\"SubGhz: No name preset.\");\n    return -1;\n}\n\nbool subghz_setting_load_custom_preset(\n    SubGhzSetting* instance,\n    const char* preset_name,\n    FlipperFormat* fff_data_file) {\n    furi_check(instance);\n    furi_check(preset_name);\n    uint32_t temp_data32;\n    SubGhzSettingCustomPresetItem* item =\n        SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data);\n    item->custom_preset_name = furi_string_alloc();\n    furi_string_set(item->custom_preset_name, preset_name);\n    do {\n        if(!flipper_format_get_value_count(fff_data_file, \"Custom_preset_data\", &temp_data32))\n            break;\n        if(!temp_data32 || (temp_data32 % 2)) {\n            FURI_LOG_E(TAG, \"Integrity error Custom_preset_data\");\n            break;\n        }\n        item->custom_preset_data_size = sizeof(uint8_t) * temp_data32;\n        item->custom_preset_data = malloc(item->custom_preset_data_size);\n        if(!flipper_format_read_hex(\n               fff_data_file,\n               \"Custom_preset_data\",\n               item->custom_preset_data,\n               item->custom_preset_data_size)) {\n            FURI_LOG_E(TAG, \"Missing Custom_preset_data\");\n            break;\n        }\n        return true;\n    } while(true);\n    return false;\n}\n\nbool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) {\n    furi_check(instance);\n    furi_check(preset_name);\n    SubGhzSettingCustomPresetItemArray_it_t it;\n    SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data);\n    while(!SubGhzSettingCustomPresetItemArray_end_p(it)) {\n        SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it);\n        if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) {\n            furi_string_free(item->custom_preset_name);\n            free(item->custom_preset_data);\n            SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it);\n            return true;\n        }\n        SubGhzSettingCustomPresetItemArray_previous(it);\n    }\n    return false;\n}\n\nuint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) {\n    furi_check(instance);\n    SubGhzSettingCustomPresetItem* item =\n        SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);\n    return item->custom_preset_data;\n}\n\nsize_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) {\n    furi_check(instance);\n    SubGhzSettingCustomPresetItem* item =\n        SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);\n    return item->custom_preset_data_size;\n}\n\nuint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) {\n    furi_check(instance);\n    SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get(\n        instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name));\n    return item->custom_preset_data;\n}\n\nuint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) {\n    furi_check(instance);\n    if(idx < FrequencyList_size(instance->frequencies)) {\n        return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK;\n    } else {\n        return 0;\n    }\n}\n\nuint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) {\n    furi_check(instance);\n    if(idx < FrequencyList_size(instance->hopper_frequencies)) {\n        return *FrequencyList_get(instance->hopper_frequencies, idx);\n    } else {\n        return 0;\n    }\n}\n\nuint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) {\n    furi_check(instance);\n    for(size_t i = 0; i < FrequencyList_size(instance->frequencies); i++) {\n        uint32_t frequency = *FrequencyList_get(instance->frequencies, i);\n        if(frequency & FREQUENCY_FLAG_DEFAULT) {\n            return i;\n        }\n    }\n    return 0;\n}\n\nuint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) {\n    furi_check(instance);\n    return subghz_setting_get_frequency(\n        instance, subghz_setting_get_frequency_default_index(instance));\n}\n"
  },
  {
    "path": "lib/subghz/subghz_setting.h",
    "content": "\n#pragma once\n\n#include <math.h>\n#include <furi.h>\n#include <furi_hal.h>\n#include <lib/flipper_format/flipper_format.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4\n\ntypedef struct SubGhzSetting SubGhzSetting;\n\nSubGhzSetting* subghz_setting_alloc(void);\n\nvoid subghz_setting_free(SubGhzSetting* instance);\n\nvoid subghz_setting_load(SubGhzSetting* instance, const char* file_path);\n\nsize_t subghz_setting_get_frequency_count(SubGhzSetting* instance);\n\nsize_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance);\n\nsize_t subghz_setting_get_preset_count(SubGhzSetting* instance);\n\nconst char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx);\n\nint subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name);\n\nuint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx);\n\nsize_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx);\n\nuint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name);\n\nbool subghz_setting_load_custom_preset(\n    SubGhzSetting* instance,\n    const char* preset_name,\n    FlipperFormat* fff_data_file);\n\nbool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name);\n\nuint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx);\n\nuint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx);\n\nuint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance);\n\nuint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_tx_rx_worker.c",
    "content": "#include \"subghz_tx_rx_worker.h\"\n\n#include <furi.h>\n\n#define TAG \"SubGhzTxRxWorker\"\n\n#define SUBGHZ_TXRX_WORKER_BUF_SIZE      2048\n//you can not set more than 62 because it will not fit into the FIFO CC1101\n#define SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60\n\n#define SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40\n\nstruct SubGhzTxRxWorker {\n    FuriThread* thread;\n    FuriStreamBuffer* stream_tx;\n    FuriStreamBuffer* stream_rx;\n\n    volatile bool worker_running;\n    volatile bool worker_stoping;\n\n    SubGhzTxRxWorkerStatus status;\n\n    uint32_t frequency;\n    const SubGhzDevice* device;\n    const GpioPin* device_data_gpio;\n\n    SubGhzTxRxWorkerCallbackHaveRead callback_have_read;\n    void* context_have_read;\n};\n\nbool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {\n    furi_check(instance);\n    bool ret = false;\n    size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx);\n    if(size && (stream_tx_free_byte >= size)) {\n        if(furi_stream_buffer_send(\n               instance->stream_tx, data, size, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) ==\n           size) {\n            ret = true;\n        }\n    }\n    return ret;\n}\n\nsize_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) {\n    furi_check(instance);\n    return furi_stream_buffer_bytes_available(instance->stream_rx);\n}\n\nsize_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {\n    furi_check(instance);\n    return furi_stream_buffer_receive(instance->stream_rx, data, size, 0);\n}\n\nvoid subghz_tx_rx_worker_set_callback_have_read(\n    SubGhzTxRxWorker* instance,\n    SubGhzTxRxWorkerCallbackHaveRead callback,\n    void* context) {\n    furi_check(instance);\n    furi_check(callback);\n    furi_check(context);\n    instance->callback_have_read = callback;\n    instance->context_have_read = context;\n}\n\nbool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) {\n    uint8_t timeout = 100;\n    bool ret = false;\n    if(instance->status != SubGhzTxRxWorkerStatusRx) {\n        subghz_devices_set_rx(instance->device);\n        instance->status = SubGhzTxRxWorkerStatusRx;\n        furi_delay_tick(1);\n    }\n    //waiting for reception to complete\n    while(furi_hal_gpio_read(instance->device_data_gpio)) {\n        furi_delay_tick(1);\n        if(!--timeout) {\n            FURI_LOG_W(TAG, \"RX cc1101_g0 timeout\");\n            subghz_devices_flush_rx(instance->device);\n            subghz_devices_set_rx(instance->device);\n            break;\n        }\n    }\n\n    if(subghz_devices_rx_pipe_not_empty(instance->device)) {\n        FURI_LOG_I(\n            TAG,\n            \"RSSI: %03.1fdbm LQI: %d\",\n            (double)subghz_devices_get_rssi(instance->device),\n            subghz_devices_get_lqi(instance->device));\n        if(subghz_devices_is_rx_data_crc_valid(instance->device)) {\n            subghz_devices_read_packet(instance->device, data, size);\n            ret = true;\n        }\n        subghz_devices_flush_rx(instance->device);\n        subghz_devices_set_rx(instance->device);\n    }\n    return ret;\n}\n\nvoid subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {\n    uint8_t timeout = 200;\n    if(instance->status != SubGhzTxRxWorkerStatusIDLE) {\n        subghz_devices_idle(instance->device);\n    }\n    subghz_devices_write_packet(instance->device, data, size);\n    subghz_devices_set_tx(instance->device); //start send\n    instance->status = SubGhzTxRxWorkerStatusTx;\n    while(!furi_hal_gpio_read(\n        instance->device_data_gpio)) { // Wait for GDO0 to be set -> sync transmitted\n        furi_delay_tick(1);\n        if(!--timeout) {\n            FURI_LOG_W(TAG, \"TX !cc1101_g0 timeout\");\n            break;\n        }\n    }\n    while(furi_hal_gpio_read(\n        instance->device_data_gpio)) { // Wait for GDO0 to be cleared -> end of packet\n        furi_delay_tick(1);\n        if(!--timeout) {\n            FURI_LOG_W(TAG, \"TX cc1101_g0 timeout\");\n            break;\n        }\n    }\n    subghz_devices_idle(instance->device);\n    instance->status = SubGhzTxRxWorkerStatusIDLE;\n}\n/** Worker thread\n * \n * @param context \n * @return exit code \n */\nstatic int32_t subghz_tx_rx_worker_thread(void* context) {\n    SubGhzTxRxWorker* instance = context;\n    furi_check(instance->device);\n    FURI_LOG_I(TAG, \"Worker start\");\n\n    subghz_devices_begin(instance->device);\n    instance->device_data_gpio = subghz_devices_get_data_gpio(instance->device);\n    subghz_devices_reset(instance->device);\n    subghz_devices_idle(instance->device);\n    subghz_devices_load_preset(instance->device, FuriHalSubGhzPresetGFSK9_99KbAsync, NULL);\n\n    furi_hal_gpio_init(instance->device_data_gpio, GpioModeInput, GpioPullNo, GpioSpeedLow);\n\n    subghz_devices_set_frequency(instance->device, instance->frequency);\n    subghz_devices_flush_rx(instance->device);\n\n    uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0};\n    size_t size_tx = 0;\n    uint8_t size_rx[1] = {0};\n    uint8_t timeout_tx = 0;\n    bool callback_rx = false;\n\n    while(instance->worker_running) {\n        //transmit\n        size_tx = furi_stream_buffer_bytes_available(instance->stream_tx);\n        if(size_tx > 0 && !timeout_tx) {\n            timeout_tx = 10; //20ms\n            if(size_tx > SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) {\n                furi_stream_buffer_receive(\n                    instance->stream_tx,\n                    &data,\n                    SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE,\n                    SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);\n                subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE);\n            } else {\n                furi_stream_buffer_receive(\n                    instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);\n                subghz_tx_rx_worker_tx(instance, data, size_tx);\n            }\n        } else {\n            //recive\n            if(subghz_tx_rx_worker_rx(instance, data, size_rx)) {\n                if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) {\n                    if(instance->callback_have_read &&\n                       furi_stream_buffer_bytes_available(instance->stream_rx) == 0) {\n                        callback_rx = true;\n                    }\n                    furi_stream_buffer_send(\n                        instance->stream_rx,\n                        &data,\n                        size_rx[0],\n                        SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);\n                    if(callback_rx) {\n                        instance->callback_have_read(instance->context_have_read);\n                        callback_rx = false;\n                    }\n                } else {\n                    FURI_LOG_E(TAG, \"Receive buffer overflow, over-the-air transmission too fast\");\n                }\n            }\n        }\n\n        if(timeout_tx) timeout_tx--;\n        furi_delay_tick(1);\n    }\n\n    subghz_devices_sleep(instance->device);\n    subghz_devices_end(instance->device);\n\n    FURI_LOG_I(TAG, \"Worker stop\");\n    return 0;\n}\n\nSubGhzTxRxWorker* subghz_tx_rx_worker_alloc(void) {\n    SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker));\n\n    instance->thread =\n        furi_thread_alloc_ex(\"SubGhzTxRxWorker\", 2048, subghz_tx_rx_worker_thread, instance);\n    instance->stream_tx =\n        furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));\n    instance->stream_rx =\n        furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));\n\n    instance->status = SubGhzTxRxWorkerStatusIDLE;\n    instance->worker_stoping = true;\n\n    return instance;\n}\n\nvoid subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) {\n    furi_check(instance);\n    furi_check(!instance->worker_running);\n    furi_stream_buffer_free(instance->stream_tx);\n    furi_stream_buffer_free(instance->stream_rx);\n    furi_thread_free(instance->thread);\n\n    free(instance);\n}\n\nbool subghz_tx_rx_worker_start(\n    SubGhzTxRxWorker* instance,\n    const SubGhzDevice* device,\n    uint32_t frequency) {\n    furi_check(instance);\n    furi_check(!instance->worker_running);\n    bool res = false;\n    furi_stream_buffer_reset(instance->stream_tx);\n    furi_stream_buffer_reset(instance->stream_rx);\n\n    instance->worker_running = true;\n\n    if(furi_hal_region_is_frequency_allowed(frequency)) {\n        instance->frequency = frequency;\n        instance->device = device;\n        res = true;\n    }\n\n    furi_thread_start(instance->thread);\n\n    return res;\n}\n\nvoid subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->worker_running);\n\n    instance->worker_running = false;\n\n    furi_thread_join(instance->thread);\n}\n\nbool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) {\n    furi_check(instance);\n    return instance->worker_running;\n}\n"
  },
  {
    "path": "lib/subghz/subghz_tx_rx_worker.h",
    "content": "#pragma once\n\n#include <furi_hal.h>\n#include <devices/devices.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context);\n\ntypedef struct SubGhzTxRxWorker SubGhzTxRxWorker;\n\ntypedef enum {\n    SubGhzTxRxWorkerStatusIDLE,\n    SubGhzTxRxWorkerStatusTx,\n    SubGhzTxRxWorkerStatusRx,\n} SubGhzTxRxWorkerStatus;\n\n/** \n * SubGhzTxRxWorker, add data to transfer\n * @param instance  Pointer to a SubGhzTxRxWorker instance\n * @param data      *data\n * @param size      data size\n * @return bool     true if ok\n */\nbool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);\n\n/** \n * SubGhzTxRxWorker, get available data\n * @param instance   Pointer to a SubGhzTxRxWorker instance\n * @return size_t    data size\n */\nsize_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance);\n\n/** \n * SubGhzTxRxWorker, read data\n * @param instance   Pointer to a SubGhzTxRxWorker instance\n * @param data       *data\n * @param size       max data size, which can be read\n * @return size_t    data size, how much is actually read\n */\nsize_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);\n\n/** \n * Сallback SubGhzTxRxWorker when there is data to read in an empty buffer\n * @param instance Pointer to a SubGhzTxRxWorker instance\n * @param callback SubGhzTxRxWorkerCallbackHaveRead callback\n * @param context\n */\nvoid subghz_tx_rx_worker_set_callback_have_read(\n    SubGhzTxRxWorker* instance,\n    SubGhzTxRxWorkerCallbackHaveRead callback,\n    void* context);\n\n/** \n * Allocate SubGhzTxRxWorker\n * @return SubGhzTxRxWorker* Pointer to a SubGhzTxRxWorker instance\n */\nSubGhzTxRxWorker* subghz_tx_rx_worker_alloc(void);\n\n/** \n * Free SubGhzTxRxWorker\n * @param instance Pointer to a SubGhzTxRxWorker instance\n */\nvoid subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance);\n\n/** \n * Start SubGhzTxRxWorker\n * @param instance Pointer to a SubGhzTxRxWorker instance\n * @param device Pointer to a SubGhzDevice instance\n * @return bool - true if ok\n */\nbool subghz_tx_rx_worker_start(\n    SubGhzTxRxWorker* instance,\n    const SubGhzDevice* device,\n    uint32_t frequency);\n\n/** \n * Stop SubGhzTxRxWorker\n * @param instance Pointer to a SubGhzTxRxWorker instance\n */\nvoid subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance);\n\n/** \n * Check if worker is running\n * @param instance Pointer to a SubGhzTxRxWorker instance\n * @return bool - true if running\n */\nbool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/subghz_worker.c",
    "content": "#include \"subghz_worker.h\"\n\n#include <furi.h>\n\n#define TAG \"SubGhzWorker\"\n\nstruct SubGhzWorker {\n    FuriThread* thread;\n    FuriStreamBuffer* stream;\n\n    volatile bool running;\n    volatile bool overrun;\n\n    LevelDuration filter_level_duration;\n    uint16_t filter_duration;\n\n    SubGhzWorkerOverrunCallback overrun_callback;\n    SubGhzWorkerPairCallback pair_callback;\n    void* context;\n};\n\n/** Rx callback timer\n * \n * @param level received signal level\n * @param duration received signal duration\n * @param context \n */\nvoid subghz_worker_rx_callback(bool level, uint32_t duration, void* context) {\n    SubGhzWorker* instance = context;\n\n    LevelDuration level_duration = level_duration_make(level, duration);\n    if(instance->overrun) {\n        instance->overrun = false;\n        level_duration = level_duration_reset();\n    }\n    size_t ret =\n        furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);\n    if(sizeof(LevelDuration) != ret) instance->overrun = true;\n}\n\n/** Worker callback thread\n * \n * @param context \n * @return exit code \n */\nstatic int32_t subghz_worker_thread_callback(void* context) {\n    SubGhzWorker* instance = context;\n\n    LevelDuration level_duration;\n    while(instance->running) {\n        int ret = furi_stream_buffer_receive(\n            instance->stream, &level_duration, sizeof(LevelDuration), 10);\n        if(ret == sizeof(LevelDuration)) {\n            if(level_duration_is_reset(level_duration)) {\n                FURI_LOG_E(TAG, \"Overrun buffer\");\n                if(instance->overrun_callback) instance->overrun_callback(instance->context);\n            } else {\n                bool level = level_duration_get_level(level_duration);\n                uint32_t duration = level_duration_get_duration(level_duration);\n\n                if((duration < instance->filter_duration) ||\n                   (instance->filter_level_duration.level == level)) {\n                    instance->filter_level_duration.duration += duration;\n\n                } else if(instance->filter_level_duration.level != level) {\n                    if(instance->pair_callback)\n                        instance->pair_callback(\n                            instance->context,\n                            instance->filter_level_duration.level,\n                            instance->filter_level_duration.duration);\n\n                    instance->filter_level_duration.duration = duration;\n                    instance->filter_level_duration.level = level;\n                }\n            }\n        }\n    }\n\n    return 0;\n}\n\nSubGhzWorker* subghz_worker_alloc(void) {\n    SubGhzWorker* instance = malloc(sizeof(SubGhzWorker));\n\n    instance->thread =\n        furi_thread_alloc_ex(\"SubGhzWorker\", 2048, subghz_worker_thread_callback, instance);\n\n    instance->stream =\n        furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration));\n\n    //setting default filter in us\n    instance->filter_duration = 30;\n\n    return instance;\n}\n\nvoid subghz_worker_free(SubGhzWorker* instance) {\n    furi_check(instance);\n\n    furi_stream_buffer_free(instance->stream);\n    furi_thread_free(instance->thread);\n\n    free(instance);\n}\n\nvoid subghz_worker_set_overrun_callback(\n    SubGhzWorker* instance,\n    SubGhzWorkerOverrunCallback callback) {\n    furi_check(instance);\n    instance->overrun_callback = callback;\n}\n\nvoid subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback) {\n    furi_check(instance);\n    instance->pair_callback = callback;\n}\n\nvoid subghz_worker_set_context(SubGhzWorker* instance, void* context) {\n    furi_check(instance);\n    instance->context = context;\n}\n\nvoid subghz_worker_start(SubGhzWorker* instance) {\n    furi_check(instance);\n    furi_check(!instance->running);\n\n    instance->running = true;\n\n    furi_thread_start(instance->thread);\n}\n\nvoid subghz_worker_stop(SubGhzWorker* instance) {\n    furi_check(instance);\n    furi_check(instance->running);\n\n    instance->running = false;\n\n    furi_thread_join(instance->thread);\n}\n\nbool subghz_worker_is_running(SubGhzWorker* instance) {\n    furi_check(instance);\n    return instance->running;\n}\n\nvoid subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) {\n    furi_check(instance);\n    instance->filter_duration = timeout;\n}\n"
  },
  {
    "path": "lib/subghz/subghz_worker.h",
    "content": "#pragma once\n\n#include <furi_hal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzWorker SubGhzWorker;\n\ntypedef void (*SubGhzWorkerOverrunCallback)(void* context);\n\ntypedef void (*SubGhzWorkerPairCallback)(void* context, bool level, uint32_t duration);\n\nvoid subghz_worker_rx_callback(bool level, uint32_t duration, void* context);\n\n/** \n * Allocate SubGhzWorker.\n * @return SubGhzWorker* Pointer to a SubGhzWorker instance\n */\nSubGhzWorker* subghz_worker_alloc(void);\n\n/** \n * Free SubGhzWorker.\n * @param instance Pointer to a SubGhzWorker instance\n */\nvoid subghz_worker_free(SubGhzWorker* instance);\n\n/** \n * Overrun callback SubGhzWorker.\n * @param instance Pointer to a SubGhzWorker instance\n * @param callback SubGhzWorkerOverrunCallback callback\n */\nvoid subghz_worker_set_overrun_callback(\n    SubGhzWorker* instance,\n    SubGhzWorkerOverrunCallback callback);\n\n/** \n * Pair callback SubGhzWorker.\n * @param instance Pointer to a SubGhzWorker instance\n * @param callback SubGhzWorkerOverrunCallback callback\n */\nvoid subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback);\n\n/** \n * Context callback SubGhzWorker.\n * @param instance Pointer to a SubGhzWorker instance\n * @param context \n */\nvoid subghz_worker_set_context(SubGhzWorker* instance, void* context);\n\n/** \n * Start SubGhzWorker.\n * @param instance Pointer to a SubGhzWorker instance\n */\nvoid subghz_worker_start(SubGhzWorker* instance);\n\n/** Stop SubGhzWorker\n * @param instance Pointer to a SubGhzWorker instance\n */\nvoid subghz_worker_stop(SubGhzWorker* instance);\n\n/** \n * Check if worker is running.\n * @param instance Pointer to a SubGhzWorker instance\n * @return bool - true if running\n */\nbool subghz_worker_is_running(SubGhzWorker* instance);\n\n/** \n * Short duration filter setting.\n * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled\n * @param instance Pointer to a SubGhzWorker instance\n * @param timeout time in us\n */\nvoid subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/transmitter.c",
    "content": "#include \"transmitter.h\"\n\n#include \"protocols/base.h\"\n#include \"registry.h\"\n\nstruct SubGhzTransmitter {\n    const SubGhzProtocol* protocol;\n    SubGhzProtocolEncoderBase* protocol_instance;\n};\n\nSubGhzTransmitter*\n    subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) {\n    SubGhzTransmitter* instance = NULL;\n    const SubGhzProtocolRegistry* protocol_registry_items =\n        subghz_environment_get_protocol_registry(environment);\n\n    const SubGhzProtocol* protocol =\n        subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name);\n\n    if(protocol && protocol->encoder && protocol->encoder->alloc) {\n        instance = malloc(sizeof(SubGhzTransmitter));\n        instance->protocol = protocol;\n        instance->protocol_instance = instance->protocol->encoder->alloc(environment);\n    }\n    return instance;\n}\n\nvoid subghz_transmitter_free(SubGhzTransmitter* instance) {\n    furi_check(instance);\n    instance->protocol->encoder->free(instance->protocol_instance);\n    free(instance);\n}\n\nSubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) {\n    furi_check(instance);\n    return instance->protocol_instance;\n}\n\nbool subghz_transmitter_stop(SubGhzTransmitter* instance) {\n    furi_check(instance);\n    bool ret = false;\n    if(instance->protocol && instance->protocol->encoder && instance->protocol->encoder->stop) {\n        instance->protocol->encoder->stop(instance->protocol_instance);\n        ret = true;\n    }\n    return ret;\n}\n\nSubGhzProtocolStatus\n    subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) {\n    furi_check(instance);\n    SubGhzProtocolStatus ret = SubGhzProtocolStatusError;\n    if(instance->protocol && instance->protocol->encoder &&\n       instance->protocol->encoder->deserialize) {\n        ret =\n            instance->protocol->encoder->deserialize(instance->protocol_instance, flipper_format);\n    }\n    return ret;\n}\n\nLevelDuration subghz_transmitter_yield(void* context) {\n    SubGhzTransmitter* instance = context;\n    return instance->protocol->encoder->yield(instance->protocol_instance);\n}\n"
  },
  {
    "path": "lib/subghz/transmitter.h",
    "content": "#pragma once\n\n#include \"types.h\"\n#include \"environment.h\"\n#include \"protocols/base.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SubGhzTransmitter SubGhzTransmitter;\n\n/**\n * Allocate and init SubGhzTransmitter.\n * @param environment Pointer to a SubGhzEnvironment instance\n * @return SubGhzTransmitter* pointer to a SubGhzTransmitter instance\n */\nSubGhzTransmitter*\n    subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name);\n\n/**\n * Free SubGhzTransmitter.\n * @param instance Pointer to a SubGhzTransmitter instance\n */\nvoid subghz_transmitter_free(SubGhzTransmitter* instance);\n\n/** Get protocol instance.\n * @param instance Pointer to a SubGhzTransmitter instance\n */\nSubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance);\n\n/**\n * Forced transmission stop.\n * @param instance Pointer to a SubGhzTransmitter instance\n */\nbool subghz_transmitter_stop(SubGhzTransmitter* instance);\n\n/**\n * Deserialize and generating an upload to send.\n * @param instance Pointer to a SubGhzTransmitter instance\n * @param flipper_format Pointer to a FlipperFormat instance\n * @return status\n */\nSubGhzProtocolStatus\n    subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format);\n\n/**\n * Getting the level and duration of the upload to be loaded into DMA.\n * @param context Pointer to a SubGhzTransmitter instance\n * @return LevelDuration \n */\nLevelDuration subghz_transmitter_yield(void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/subghz/types.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#include <lib/flipper_format/flipper_format.h>\n#include <lib/toolbox/level_duration.h>\n\n#include \"environment.h\"\n#include <furi.h>\n#include <furi_hal.h>\n\n#define SUBGHZ_APP_FOLDER             EXT_PATH(\"subghz\")\n#define SUBGHZ_RAW_FOLDER             EXT_PATH(\"subghz\")\n#define SUBGHZ_APP_FILENAME_PREFIX    \"SubGHz\"\n#define SUBGHZ_APP_FILENAME_EXTENSION \".sub\"\n\n#define SUBGHZ_KEY_FILE_VERSION 1\n#define SUBGHZ_KEY_FILE_TYPE    \"Flipper SubGhz Key File\"\n\n#define SUBGHZ_RAW_FILE_VERSION 1\n#define SUBGHZ_RAW_FILE_TYPE    \"Flipper SubGhz RAW File\"\n\n#define SUBGHZ_KEYSTORE_DIR_NAME      EXT_PATH(\"subghz/assets/keeloq_mfcodes\")\n#define SUBGHZ_KEYSTORE_DIR_USER_NAME EXT_PATH(\"subghz/assets/keeloq_mfcodes_user\")\n#define SUBGHZ_CAME_ATOMO_DIR_NAME    EXT_PATH(\"subghz/assets/came_atomo\")\n#define SUBGHZ_NICE_FLOR_S_DIR_NAME   EXT_PATH(\"subghz/assets/nice_flor_s\")\n#define SUBGHZ_ALUTECH_AT_4N_DIR_NAME EXT_PATH(\"subghz/assets/alutech_at_4n\")\n\ntypedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;\ntypedef struct SubGhzEnvironment SubGhzEnvironment;\n\n// Radio Preset\ntypedef struct {\n    FuriString* name;\n    uint32_t frequency;\n    uint8_t* data;\n    size_t data_size;\n} SubGhzRadioPreset;\n\ntypedef enum {\n    SubGhzProtocolStatusOk = 0,\n    // Errors\n    SubGhzProtocolStatusError = (-1), ///< General unclassified error\n    // Serialize/De-serialize\n    SubGhzProtocolStatusErrorParserHeader = (-2), ///< Missing or invalid file header\n    SubGhzProtocolStatusErrorParserFrequency = (-3), ///< Missing `Frequency`\n    SubGhzProtocolStatusErrorParserPreset = (-4), ///< Missing `Preset`\n    SubGhzProtocolStatusErrorParserCustomPreset = (-5), ///< Missing `Custom_preset_module`\n    SubGhzProtocolStatusErrorParserProtocolName = (-6), ///< Missing `Protocol` name\n    SubGhzProtocolStatusErrorParserBitCount = (-7), ///< Missing `Bit`\n    SubGhzProtocolStatusErrorParserKey = (-8), ///< Missing `Key`\n    SubGhzProtocolStatusErrorParserTe = (-9), ///< Missing `Te`\n    SubGhzProtocolStatusErrorParserOthers = (-10), ///< Missing some other mandatory keys\n    // Invalid data\n    SubGhzProtocolStatusErrorValueBitCount = (-11), ///< Invalid bit count value\n    // Encoder issue\n    SubGhzProtocolStatusErrorEncoderGetUpload = (-12), ///< Payload encoder failure\n    // Special Values\n    SubGhzProtocolStatusErrorProtocolNotFound = (-13), ///< Protocol not found\n    SubGhzProtocolStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.\n} SubGhzProtocolStatus;\n\n// Allocator and Deallocator\ntypedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment);\ntypedef void (*SubGhzFree)(void* context);\n\n// Serialize and Deserialize\ntypedef SubGhzProtocolStatus (\n    *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset);\ntypedef SubGhzProtocolStatus (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format);\n\n// Decoder specific\ntypedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration);\ntypedef void (*SubGhzDecoderReset)(void* decoder);\ntypedef uint8_t (*SubGhzGetHashData)(void* decoder);\ntypedef void (*SubGhzGetString)(void* decoder, FuriString* output);\n\n// Encoder specific\ntypedef void (*SubGhzEncoderStop)(void* encoder);\ntypedef LevelDuration (*SubGhzEncoderYield)(void* context);\n\ntypedef struct {\n    SubGhzAlloc alloc;\n    SubGhzFree free;\n\n    SubGhzDecoderFeed feed;\n    SubGhzDecoderReset reset;\n\n    SubGhzGetHashData get_hash_data;\n    SubGhzGetString get_string;\n    SubGhzSerialize serialize;\n    SubGhzDeserialize deserialize;\n} SubGhzProtocolDecoder;\n\ntypedef struct {\n    SubGhzAlloc alloc;\n    SubGhzFree free;\n\n    SubGhzDeserialize deserialize;\n    SubGhzEncoderStop stop;\n    SubGhzEncoderYield yield;\n} SubGhzProtocolEncoder;\n\ntypedef enum {\n    SubGhzProtocolTypeUnknown = 0,\n    SubGhzProtocolTypeStatic,\n    SubGhzProtocolTypeDynamic,\n    SubGhzProtocolTypeRAW,\n    SubGhzProtocolWeatherStation,\n    SubGhzProtocolCustom,\n} SubGhzProtocolType;\n\ntypedef enum {\n    SubGhzProtocolFlag_RAW = (1 << 0),\n    SubGhzProtocolFlag_Decodable = (1 << 1),\n    SubGhzProtocolFlag_315 = (1 << 2),\n    SubGhzProtocolFlag_433 = (1 << 3),\n    SubGhzProtocolFlag_868 = (1 << 4),\n    SubGhzProtocolFlag_AM = (1 << 5),\n    SubGhzProtocolFlag_FM = (1 << 6),\n    SubGhzProtocolFlag_Save = (1 << 7),\n    SubGhzProtocolFlag_Load = (1 << 8),\n    SubGhzProtocolFlag_Send = (1 << 9),\n    SubGhzProtocolFlag_BinRAW = (1 << 10),\n} SubGhzProtocolFlag;\n\nstruct SubGhzProtocol {\n    const char* name;\n    SubGhzProtocolType type;\n    SubGhzProtocolFlag flag;\n\n    const SubGhzProtocolEncoder* encoder;\n    const SubGhzProtocolDecoder* decoder;\n};\n"
  },
  {
    "path": "lib/toolbox/.gitignore",
    "content": "version.inc.h\n"
  },
  {
    "path": "lib/toolbox/SConscript",
    "content": "from fbt.version import get_fast_git_version_id\n\n\nImport(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/toolbox\",\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n    SDK_HEADERS=[\n        File(\"api_lock.h\"),\n        File(\"cli/cli_ansi.h\"),\n        File(\"cli/cli_command.h\"),\n        File(\"cli/cli_registry.h\"),\n        File(\"cli/shell/cli_shell.h\"),\n        File(\"compress.h\"),\n        File(\"manchester_decoder.h\"),\n        File(\"manchester_encoder.h\"),\n        File(\"path.h\"),\n        File(\"name_generator.h\"),\n        File(\"crc32_calc.h\"),\n        File(\"dir_walk.h\"),\n        File(\"args.h\"),\n        File(\"saved_struct.h\"),\n        File(\"version.h\"),\n        File(\"float_tools.h\"),\n        File(\"value_index.h\"),\n        File(\"tar/tar_archive.h\"),\n        File(\"str_buffer.h\"),\n        File(\"stream/stream.h\"),\n        File(\"stream/file_stream.h\"),\n        File(\"stream/string_stream.h\"),\n        File(\"stream/buffered_file_stream.h\"),\n        File(\"strint.h\"),\n        File(\"pipe.h\"),\n        File(\"protocols/protocol_dict.h\"),\n        File(\"pretty_format.h\"),\n        File(\"hex.h\"),\n        File(\"simple_array.h\"),\n        File(\"bit_buffer.h\"),\n        File(\"keys_dict.h\"),\n        File(\"pulse_protocols/pulse_glue.h\"),\n        File(\"md5_calc.h\"),\n        File(\"varint.h\"),\n    ],\n)\n\n\nlibenv = env.Clone(tools=[\"fbt_version\"], FW_LIB_NAME=\"toolbox\")\nlibenv.ApplyLibFlags()\n\n\n# Git Version management\nversion_depends = []\nversion_id_data = get_fast_git_version_id()\nif version_id_data:\n    version_depends = Value(version_id_data)\n\n# Only invoke version generator if preliminary check target (version_depends) has changed\nbuild_version = libenv.VersionBuilder(\n    Dir(\".\"),\n    version_depends,\n)\n\nfw_version_json = libenv.InstallAs(\n    \"${BUILD_DIR}/${FIRMWARE_BUILD_CFG}.json\", \"version.json\"\n)\nAlias(\"version_json\", fw_version_json)\nenv.Append(FW_VERSION_JSON=fw_version_json)\n\n# Default(fw_version_json)\n\n\nif not version_depends:\n    libenv.Precious(build_version)\n    libenv.AlwaysBuild(build_version)\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlibenv.Append(CPPPATH=[libenv.Dir(\".\")])\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/toolbox/api_lock.h",
    "content": "#pragma once\n#include <furi/furi.h>\n\n/*\nTesting 10000 api calls\n\nNo lock\n    Time diff: 445269.218750 us\n    Time per call: 44.526920 us\n\nfuri_thread_flags\n    Time diff: 430279.875000 us // lol wtf? smaller than no lock?\n    Time per call: 43.027988 us // I tested it many times, it's always smaller\n\nFuriEventFlag\n    Time diff: 831523.625000 us\n    Time per call: 83.152359 us\n\nFuriSemaphore\n    Time diff: 999807.125000 us\n    Time per call: 99.980713 us\n\nFuriMutex\n    Time diff: 1071417.500000 us\n    Time per call: 107.141747 us\n*/\n\ntypedef FuriEventFlag* FuriApiLock;\n\n#define API_LOCK_EVENT (1U << 0)\n\n#define api_lock_alloc_locked() furi_event_flag_alloc()\n\n#define api_lock_wait_unlock(_lock) \\\n    furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever)\n\n#define api_lock_free(_lock) furi_event_flag_free(_lock)\n\n#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT)\n\n#define api_lock_wait_unlock_and_free(_lock) \\\n    api_lock_wait_unlock(_lock);             \\\n    api_lock_free(_lock);\n\n#define api_lock_is_locked(_lock) (!(furi_event_flag_get(_lock) & API_LOCK_EVENT))\n\n#define api_lock_relock(_lock) furi_event_flag_clear(_lock, API_LOCK_EVENT)\n"
  },
  {
    "path": "lib/toolbox/args.c",
    "content": "#include \"args.h\"\n#include \"hex.h\"\n#include \"strint.h\"\n#include \"m-core.h\"\n#include <errno.h>\n\nsize_t args_get_first_word_length(FuriString* args) {\n    size_t ws = furi_string_search_char(args, ' ');\n    if(ws == FURI_STRING_FAILURE) {\n        ws = furi_string_size(args);\n    }\n\n    return ws;\n}\n\nsize_t args_length(FuriString* args) {\n    return furi_string_size(args);\n}\n\nbool args_read_int_and_trim(FuriString* args, int* value) {\n    size_t cmd_length = args_get_first_word_length(args);\n\n    if(cmd_length == 0) {\n        return false;\n    }\n\n    int32_t temp;\n    if(strint_to_int32(furi_string_get_cstr(args), NULL, &temp, 10) == StrintParseNoError) {\n        *value = temp;\n        furi_string_right(args, cmd_length);\n        furi_string_trim(args);\n        return true;\n    }\n\n    return false;\n}\n\nbool args_read_float_and_trim(FuriString* args, float* value) {\n    size_t cmd_length = args_get_first_word_length(args);\n    if(cmd_length == 0) {\n        return false;\n    }\n\n    char* end_ptr;\n    float temp = strtof(furi_string_get_cstr(args), &end_ptr);\n    if(end_ptr == furi_string_get_cstr(args)) {\n        return false;\n    }\n\n    *value = temp;\n    furi_string_right(args, cmd_length);\n    furi_string_trim(args);\n    return true;\n}\n\nbool args_read_string_and_trim(FuriString* args, FuriString* word) {\n    size_t cmd_length = args_get_first_word_length(args);\n\n    if(cmd_length == 0) {\n        return false;\n    }\n\n    furi_string_set_n(word, args, 0, cmd_length);\n    furi_string_right(args, cmd_length);\n    furi_string_trim(args);\n\n    return true;\n}\n\nbool args_read_probably_quoted_string_and_trim(FuriString* args, FuriString* word) {\n    if(furi_string_size(args) > 1 && furi_string_get_char(args, 0) == '\\\"') {\n        size_t second_quote_pos = furi_string_search_char(args, '\\\"', 1);\n\n        if(second_quote_pos == 0) {\n            return false;\n        }\n\n        furi_string_set_n(word, args, 1, second_quote_pos - 1);\n        furi_string_right(args, second_quote_pos + 1);\n        furi_string_trim(args);\n        return true;\n    } else {\n        return args_read_string_and_trim(args, word);\n    }\n}\n\nbool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte) {\n    uint8_t hi_nibble_value = 0;\n    uint8_t low_nibble_value = 0;\n    bool result = false;\n\n    if(hex_char_to_hex_nibble(hi_nibble, &hi_nibble_value)) {\n        if(hex_char_to_hex_nibble(low_nibble, &low_nibble_value)) {\n            result = true;\n            *byte = (hi_nibble_value << 4) | low_nibble_value;\n        }\n    }\n\n    return result;\n}\n\nbool args_read_hex_bytes(FuriString* args, uint8_t* bytes, size_t bytes_count) {\n    bool result = true;\n    const char* str_pointer = furi_string_get_cstr(args);\n\n    if(args_get_first_word_length(args) == (bytes_count * 2)) {\n        for(size_t i = 0; i < bytes_count; i++) {\n            if(!args_char_to_hex(str_pointer[i * 2], str_pointer[i * 2 + 1], &(bytes[i]))) {\n                result = false;\n                break;\n            }\n        }\n    } else {\n        result = false;\n    }\n\n    return result;\n}\n\nbool args_read_duration(FuriString* args, uint32_t* value, const char* default_unit) {\n    const char* args_cstr = furi_string_get_cstr(args);\n\n    const char* unit;\n    errno = 0;\n    double duration_ms = strtod(args_cstr, (char**)&unit);\n    if(errno) return false;\n    if(duration_ms < 0) return false;\n    if(unit == args_cstr) return false;\n\n    if(strcmp(unit, \"\") == 0) {\n        unit = default_unit;\n        if(!unit) unit = \"ms\";\n    }\n\n    uint32_t multiplier;\n    if(strcasecmp(unit, \"ms\") == 0) {\n        multiplier = 1;\n    } else if(strcasecmp(unit, \"s\") == 0) {\n        multiplier = 1000;\n    } else if(strcasecmp(unit, \"m\") == 0) {\n        multiplier = 60 * 1000;\n    } else if(strcasecmp(unit, \"h\") == 0) {\n        multiplier = 60 * 60 * 1000;\n    } else {\n        return false;\n    }\n\n    const uint32_t max_pre_multiplication = UINT32_MAX / multiplier;\n    if(duration_ms > max_pre_multiplication) return false;\n\n    *value = round(duration_ms * multiplier);\n    return true;\n}\n"
  },
  {
    "path": "lib/toolbox/args.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Extract int value and trim arguments string\n *\n * @param args - arguments string\n * @param value first argument, output\n * @return true - success\n * @return false - arguments string does not contain int\n */\nbool args_read_int_and_trim(FuriString* args, int* value);\n\n/** Extract float value and trim arguments string\n *\n * @param [in, out] args arguments string\n * @param [out] value first argument\n * @return true - success\n * @return false - arguments string does not contain float\n */\nbool args_read_float_and_trim(FuriString* args, float* value);\n\n/**\n * @brief Extract first argument from arguments string and trim arguments string\n *\n * @param args arguments string\n * @param word first argument, output\n * @return true - success\n * @return false - arguments string does not contain anything\n */\nbool args_read_string_and_trim(FuriString* args, FuriString* word);\n\n/**\n * @brief Extract the first quoted argument from the argument string and trim the argument string. If the argument is not quoted, calls args_read_string_and_trim.\n *\n * @param args arguments string\n * @param word first argument, output, without quotes\n * @return true - success\n * @return false - arguments string does not contain anything\n */\nbool args_read_probably_quoted_string_and_trim(FuriString* args, FuriString* word);\n\n/**\n * @brief Convert hex ASCII values to byte array\n *\n * @param args arguments string\n * @param bytes byte array pointer, output\n * @param bytes_count needed bytes count\n * @return true - success\n * @return false - arguments string does not contain enough values, or contain non-hex ASCII values\n */\nbool args_read_hex_bytes(FuriString* args, uint8_t* bytes, size_t bytes_count);\n\n/**\n * @brief Parses a duration value from a given string and converts it to milliseconds\n *\n * @param [in] args the input string containing the duration value. The string may include units (e.g., \"10s\", \"0.5m\").\n * @param [out] value pointer to store the parsed value in milliseconds\n * @param [in] default_unit  A default unit to be used if the input string does not contain a valid suffix.\n *                          Supported units: `\"ms\"`, `\"s\"`, `\"m\"`, `\"h\"`\n *                          If NULL, the function will assume milliseconds by default.\n * @return `true` if the parsing and conversion succeeded, `false` otherwise.\n */\nbool args_read_duration(FuriString* args, uint32_t* value, const char* default_unit);\n\n/************************************ HELPERS ***************************************/\n\n/**\n * @brief Get length of first word from arguments string\n *\n * @param args arguments string\n * @return size_t length of first word\n */\nsize_t args_get_first_word_length(FuriString* args);\n\n/**\n * @brief Get length of arguments string\n *\n * @param args arguments string\n * @return size_t length of arguments string\n */\nsize_t args_length(FuriString* args);\n\n/**\n * @brief Convert ASCII hex values to byte\n *\n * @param hi_nibble ASCII hi nibble character\n * @param low_nibble ASCII low nibble character\n * @param byte byte pointer, output\n * @return bool conversion status\n */\nbool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/bit_buffer.c",
    "content": "#include \"bit_buffer.h\"\n\n#include <furi.h>\n\n#define BITS_IN_BYTE (8)\n\nstruct BitBuffer {\n    uint8_t* data;\n    uint8_t* parity;\n    size_t capacity_bytes;\n    size_t size_bits;\n};\n\nBitBuffer* bit_buffer_alloc(size_t capacity_bytes) {\n    furi_check(capacity_bytes);\n\n    BitBuffer* buf = malloc(sizeof(BitBuffer));\n\n    buf->data = malloc(capacity_bytes);\n    size_t parity_buf_size = (capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;\n    buf->parity = malloc(parity_buf_size);\n    buf->capacity_bytes = capacity_bytes;\n    buf->size_bits = 0;\n\n    return buf;\n}\n\nvoid bit_buffer_free(BitBuffer* buf) {\n    furi_check(buf);\n\n    free(buf->data);\n    free(buf->parity);\n    free(buf);\n}\n\nvoid bit_buffer_reset(BitBuffer* buf) {\n    furi_check(buf);\n\n    memset(buf->data, 0, buf->capacity_bytes);\n    size_t parity_buf_size = (buf->capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;\n    memset(buf->parity, 0, parity_buf_size);\n    buf->size_bits = 0;\n}\n\nvoid bit_buffer_copy(BitBuffer* buf, const BitBuffer* other) {\n    furi_check(buf);\n    furi_check(other);\n\n    if(buf == other) return;\n\n    furi_check(buf->capacity_bytes * BITS_IN_BYTE >= other->size_bits);\n\n    memcpy(buf->data, other->data, bit_buffer_get_size_bytes(other));\n    buf->size_bits = other->size_bits;\n}\n\nvoid bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {\n    furi_check(buf);\n    furi_check(other);\n    furi_check(bit_buffer_get_size_bytes(other) > start_index);\n    furi_check(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index);\n\n    memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index);\n    buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index) {\n    furi_check(buf);\n    furi_check(other);\n    furi_check(bit_buffer_get_capacity_bytes(buf) >= end_index);\n    furi_check(bit_buffer_get_size_bytes(other) >= end_index);\n\n    memcpy(buf->data, other->data, end_index);\n    buf->size_bits = end_index * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {\n    furi_check(buf);\n    furi_check(data);\n    furi_check(buf->capacity_bytes >= size_bytes);\n\n    memcpy(buf->data, data, size_bytes);\n    buf->size_bits = size_bytes * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits) {\n    furi_check(buf);\n    furi_check(data);\n    furi_check(buf->capacity_bytes * BITS_IN_BYTE >= size_bits);\n\n    size_t size_bytes = (size_bits + BITS_IN_BYTE - 1) / BITS_IN_BYTE;\n    memcpy(buf->data, data, size_bytes);\n    buf->size_bits = size_bits;\n}\n\nvoid bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits) {\n    furi_check(buf);\n    furi_check(data);\n\n    size_t bits_processed = 0;\n    size_t curr_byte = 0;\n\n    if(size_bits < BITS_IN_BYTE + 1) {\n        buf->size_bits = size_bits;\n        buf->data[0] = data[0];\n    } else {\n        furi_check(size_bits % (BITS_IN_BYTE + 1) == 0);\n        while(bits_processed < size_bits) {\n            buf->data[curr_byte] = data[bits_processed / BITS_IN_BYTE] >>\n                                   (bits_processed % BITS_IN_BYTE);\n            buf->data[curr_byte] |= data[bits_processed / BITS_IN_BYTE + 1]\n                                    << (BITS_IN_BYTE - bits_processed % BITS_IN_BYTE);\n            uint8_t bit =\n                FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], bits_processed % BITS_IN_BYTE);\n\n            if((bits_processed % BITS_IN_BYTE) == 0) {\n                buf->parity[curr_byte / BITS_IN_BYTE] = bit;\n            } else {\n                buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bits_processed % BITS_IN_BYTE);\n            }\n            bits_processed += BITS_IN_BYTE + 1;\n            curr_byte++;\n        }\n        buf->size_bits = curr_byte * BITS_IN_BYTE;\n    }\n}\n\nvoid bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes) {\n    furi_check(buf);\n    furi_check(dest);\n    furi_check(bit_buffer_get_size_bytes(buf) <= size_bytes);\n\n    memcpy(dest, buf->data, bit_buffer_get_size_bytes(buf));\n}\n\nvoid bit_buffer_write_bytes_with_parity(\n    const BitBuffer* buf,\n    void* dest,\n    size_t size_bytes,\n    size_t* bits_written) {\n    furi_check(buf);\n    furi_check(dest);\n    furi_check(bits_written);\n\n    size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);\n    size_t buf_size_with_parity_bytes =\n        (buf_size_bytes * (BITS_IN_BYTE + 1) + BITS_IN_BYTE) / BITS_IN_BYTE;\n    furi_check(buf_size_with_parity_bytes <= size_bytes);\n\n    uint8_t next_par_bit = 0;\n    uint16_t curr_bit_pos = 0;\n    uint8_t* bitstream = dest;\n\n    for(size_t i = 0; i < buf_size_bytes; i++) {\n        next_par_bit = FURI_BIT(buf->parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE);\n        if(curr_bit_pos % BITS_IN_BYTE == 0) {\n            bitstream[curr_bit_pos / BITS_IN_BYTE] = buf->data[i];\n            curr_bit_pos += BITS_IN_BYTE;\n            bitstream[curr_bit_pos / BITS_IN_BYTE] = next_par_bit;\n            curr_bit_pos++;\n        } else {\n            bitstream[curr_bit_pos / BITS_IN_BYTE] |= buf->data[i]\n                                                      << (curr_bit_pos % BITS_IN_BYTE);\n            bitstream[curr_bit_pos / BITS_IN_BYTE + 1] =\n                buf->data[i] >> (BITS_IN_BYTE - curr_bit_pos % BITS_IN_BYTE);\n            bitstream[curr_bit_pos / BITS_IN_BYTE + 1] |= next_par_bit\n                                                          << (curr_bit_pos % BITS_IN_BYTE);\n            curr_bit_pos += BITS_IN_BYTE + 1;\n        }\n    }\n\n    *bits_written = curr_bit_pos;\n}\n\nvoid bit_buffer_write_bytes_mid(\n    const BitBuffer* buf,\n    void* dest,\n    size_t start_index,\n    size_t size_bytes) {\n    furi_check(buf);\n    furi_check(dest);\n    furi_check(start_index + size_bytes <= bit_buffer_get_size_bytes(buf));\n\n    memcpy(dest, buf->data + start_index, size_bytes);\n}\n\nbool bit_buffer_has_partial_byte(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return (buf->size_bits % BITS_IN_BYTE) != 0;\n}\n\nbool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte) {\n    furi_check(buf);\n\n    return bit_buffer_get_size_bytes(buf) && (buf->data[0] == byte);\n}\n\nsize_t bit_buffer_get_capacity_bytes(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return buf->capacity_bytes;\n}\n\nsize_t bit_buffer_get_size(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return buf->size_bits;\n}\n\nsize_t bit_buffer_get_size_bytes(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return (buf->size_bits / BITS_IN_BYTE) + (buf->size_bits % BITS_IN_BYTE ? 1 : 0);\n}\n\nuint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index) {\n    furi_check(buf);\n    furi_check(buf->capacity_bytes > index);\n\n    return buf->data[index];\n}\n\nuint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits) {\n    furi_check(buf);\n    furi_check(buf->capacity_bytes * BITS_IN_BYTE > index_bits);\n\n    const size_t byte_index = index_bits / BITS_IN_BYTE;\n    const size_t bit_offset = index_bits % BITS_IN_BYTE;\n\n    const uint8_t lo = buf->data[byte_index] >> bit_offset;\n    const uint8_t hi = buf->data[byte_index + 1] << (BITS_IN_BYTE - bit_offset);\n\n    return lo | hi;\n}\n\nconst uint8_t* bit_buffer_get_data(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return buf->data;\n}\n\nconst uint8_t* bit_buffer_get_parity(const BitBuffer* buf) {\n    furi_check(buf);\n\n    return buf->parity;\n}\n\nvoid bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte) {\n    furi_check(buf);\n\n    const size_t size_bytes = bit_buffer_get_size_bytes(buf);\n    furi_check(size_bytes > index);\n\n    buf->data[index] = byte;\n}\n\nvoid bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity) {\n    furi_check(buff);\n    furi_check(buff->size_bits / BITS_IN_BYTE > index);\n\n    buff->data[index] = byte;\n    if((index % BITS_IN_BYTE) == 0) {\n        buff->parity[index / BITS_IN_BYTE] = parity;\n    } else {\n        buff->parity[index / BITS_IN_BYTE] |= parity << (index % BITS_IN_BYTE);\n    }\n}\n\nvoid bit_buffer_set_size(BitBuffer* buf, size_t new_size) {\n    furi_check(buf);\n    furi_check(buf->capacity_bytes * BITS_IN_BYTE >= new_size);\n\n    buf->size_bits = new_size;\n}\n\nvoid bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes) {\n    furi_check(buf);\n    furi_check(buf->capacity_bytes >= new_size_bytes);\n\n    buf->size_bits = new_size_bytes * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_append(BitBuffer* buf, const BitBuffer* other) {\n    bit_buffer_append_right(buf, other, 0);\n}\n\nvoid bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {\n    furi_check(buf);\n    furi_check(other);\n\n    const size_t size_bytes = bit_buffer_get_size_bytes(buf);\n    const size_t other_size_bytes = bit_buffer_get_size_bytes(other) - start_index;\n\n    furi_check(buf->capacity_bytes >= size_bytes + other_size_bytes);\n\n    memcpy(buf->data + size_bytes, other->data + start_index, other_size_bytes);\n    buf->size_bits += other->size_bits - start_index * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_append_byte(BitBuffer* buf, uint8_t byte) {\n    furi_check(buf);\n\n    const size_t data_size_bytes = bit_buffer_get_size_bytes(buf);\n    const size_t new_data_size_bytes = data_size_bytes + 1;\n    furi_check(new_data_size_bytes <= buf->capacity_bytes);\n\n    buf->data[data_size_bytes] = byte;\n    buf->size_bits = new_data_size_bytes * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {\n    furi_check(buf);\n    furi_check(data);\n\n    const size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);\n    furi_check(buf->capacity_bytes >= buf_size_bytes + size_bytes);\n\n    memcpy(&buf->data[buf_size_bytes], data, size_bytes);\n    buf->size_bits += size_bytes * BITS_IN_BYTE;\n}\n\nvoid bit_buffer_append_bit(BitBuffer* buf, bool bit) {\n    furi_check(buf);\n    furi_check(\n        bit_buffer_get_size_bytes(buf) <=\n        (buf->capacity_bytes - (bit_buffer_has_partial_byte(buf) ? 0 : 1)));\n\n    if(bit) {\n        const size_t byte_index = buf->size_bits / BITS_IN_BYTE;\n        const size_t bit_offset = (buf->size_bits % BITS_IN_BYTE);\n        buf->data[byte_index] |= 1U << bit_offset;\n    }\n\n    buf->size_bits++;\n}\n"
  },
  {
    "path": "lib/toolbox/bit_buffer.h",
    "content": "/** Bit Buffer\n *\n * Various bits and bytes manipulation tools.\n *\n * @file bit_buffer.h\n */\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct BitBuffer BitBuffer;\n\n/** Allocate a BitBuffer instance.\n *\n * @param[in]  capacity_bytes  maximum buffer capacity, in bytes\n *\n * @return     pointer to the allocated BitBuffer instance\n */\nBitBuffer* bit_buffer_alloc(size_t capacity_bytes);\n\n/** Delete a BitBuffer instance.\n *\n * @param[in,out] buf   pointer to a BitBuffer instance\n */\nvoid bit_buffer_free(BitBuffer* buf);\n\n/** Clear all data from a BitBuffer instance.\n *\n * @param[in,out] buf   pointer to a BitBuffer instance\n */\nvoid bit_buffer_reset(BitBuffer* buf);\n\n// Copy and write\n\n/** Copy another BitBuffer instance's contents to this one, replacing all of the\n * original data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size.\n *\n * @param[in,out] buf    pointer to a BitBuffer instance to copy into\n * @param[in]     other  pointer to a BitBuffer instance to copy from\n */\nvoid bit_buffer_copy(BitBuffer* buf, const BitBuffer* other);\n\n/** Copy all BitBuffer instance's contents to this one, starting from\n * start_index, replacing all of the original data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size counting from start_index.\n *\n * @param[in,out] buf          pointer to a BitBuffer instance to copy into\n * @param[in]     other        pointer to a BitBuffer instance to copy from\n * @param[in]     start_index  index to begin copying source data from\n */\nvoid bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index);\n\n/** Copy all BitBuffer instance's contents to this one, ending with end_index,\n * replacing all of the original data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size counting to end_index.\n *\n * @param[in,out] buf        pointer to a BitBuffer instance to copy into\n * @param[in]     other      pointer to a BitBuffer instance to copy from\n * @param[in]     end_index  index to end copying source data at\n */\nvoid bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index);\n\n/** Copy a byte array to a BitBuffer instance, replacing all of the original\n * data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size.\n *\n * @param[in,out] buf         pointer to a BitBuffer instance to copy into\n * @param[in]     data        pointer to the byte array to be copied\n * @param[in]     size_bytes  size of the data to be copied, in bytes\n */\nvoid bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes);\n\n/** Copy a byte array to a BitBuffer instance, replacing all of the original\n * data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size.\n *\n * @param[in,out] buf        pointer to a BitBuffer instance to copy into\n * @param[in]     data       pointer to the byte array to be copied\n * @param[in]     size_bits  size of the data to be copied, in bits\n */\nvoid bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits);\n\n/** Copy a byte with parity array to a BitBuffer instance, replacing all of the\n * original data.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size.\n *\n * @param[in,out] buf        pointer to a BitBuffer instance to copy into\n * @param[in]     data       pointer to the byte array to be copied\n * @param[in]     size_bits  size of the data to be copied, in bits\n * @note          Parity bits are placed starting with the most significant bit\n *                of each byte and moving up.\n * @note          Example: DDDDDDDD PDDDDDDD DPDDDDDD DDP...\n */\nvoid bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits);\n\n/** Write a BitBuffer instance's entire contents to an arbitrary memory location.\n *\n * @warning    The destination memory must be allocated. Additionally, the\n *             destination capacity must be no less than the source data size.\n *\n * @param[in]  buf         pointer to a BitBuffer instance to write from\n * @param[out] dest        pointer to the destination memory location\n * @param[in]  size_bytes  maximum destination data size, in bytes\n */\nvoid bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes);\n\n/** Write a BitBuffer instance's entire contents to an arbitrary memory location.\n *\n * Additionally, place a parity bit after each byte.\n *\n * @warning    The destination memory must be allocated. Additionally, the\n *             destination capacity must be no less than the source data size\n *             plus parity.\n *\n * @param[in]  buf           pointer to a BitBuffer instance to write from\n * @param[out] dest          pointer to the destination memory location\n * @param[in]  size_bytes    maximum destination data size, in bytes\n * @param[out] bits_written  actual number of bits written, in bits\n * @note       Parity bits are placed starting with the most significant bit of\n *             each byte and moving up.\n * @note       Example: DDDDDDDD PDDDDDDD DPDDDDDD DDP...\n */\nvoid bit_buffer_write_bytes_with_parity(\n    const BitBuffer* buf,\n    void* dest,\n    size_t size_bytes,\n    size_t* bits_written);\n\n/** Write a slice of BitBuffer instance's contents to an arbitrary memory\n * location.\n *\n * @warning    The destination memory must be allocated. Additionally, the\n *             destination capacity must be no less than the requested slice\n *             size.\n *\n * @param[in]  buf          pointer to a BitBuffer instance to write from\n * @param[out] dest         pointer to the destination memory location\n * @param[in]  start_index  index to begin copying source data from\n * @param[in]  size_bytes   data slice size, in bytes\n */\nvoid bit_buffer_write_bytes_mid(\n    const BitBuffer* buf,\n    void* dest,\n    size_t start_index,\n    size_t size_bytes);\n\n// Checks\n\n/** Check whether a BitBuffer instance contains a partial byte (i.e.\\ the bit\n * count is not divisible by 8).\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be checked\n *\n * @return     true if the instance contains a partial byte, false otherwise\n */\nbool bit_buffer_has_partial_byte(const BitBuffer* buf);\n\n/** Check whether a BitBuffer instance's contents start with the designated byte.\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be checked\n * @param[in]  byte  byte value to be checked against\n *\n * @return     true if data starts with designated byte, false otherwise\n */\nbool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte);\n\n// Getters\n\n/** Get a BitBuffer instance's capacity (i.e.\\ the maximum possible amount of\n * data), in bytes.\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be queried\n *\n * @return     capacity, in bytes\n */\nsize_t bit_buffer_get_capacity_bytes(const BitBuffer* buf);\n\n/** Get a BitBuffer instance's data size (i.e.\\ the amount of stored data), in\n * bits.\n *\n * @warning    Might be not divisible by 8 (see bit_buffer_is_partial_byte).\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be queried\n *\n * @return     data size, in bits.\n */\nsize_t bit_buffer_get_size(const BitBuffer* buf);\n\n/**\n * Get a BitBuffer instance's data size (i.e.\\ the amount of stored data), in\n * bytes.\n *\n * @warning    If a partial byte is present, it is also counted.\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be queried\n *\n * @return     data size, in bytes.\n */\nsize_t bit_buffer_get_size_bytes(const BitBuffer* buf);\n\n/** Get a byte value at a specified index in a BitBuffer instance.\n *\n * @warning    The index must be valid (i.e.\\ less than the instance's data size\n *             in bytes).\n *\n * @param[in]  buf    pointer to a BitBuffer instance to be queried\n * @param[in]  index  index of the byte in question\n *\n * @return     byte value\n */\nuint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index);\n\n/** Get a byte value starting from the specified bit index in a BitBuffer\n * instance.\n *\n * @warning    The resulting byte might correspond to a single byte (if the\n *             index is a multiple of 8), or two overlapping bytes combined. The\n *             index must be valid (i.e.\\ less than the instance's data size in\n *             bits).\n *\n * @param[in]  buf         pointer to a BitBuffer instance to be queried\n * @param[in]  index_bits  bit index of the byte in question\n *\n * @return     byte value\n */\nuint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits);\n\n/** Get the pointer to a BitBuffer instance's underlying data.\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be queried\n *\n * @return     pointer to the underlying data\n */\nconst uint8_t* bit_buffer_get_data(const BitBuffer* buf);\n\n/** Get the pointer to the parity data of a BitBuffer instance.\n *\n * @param[in]  buf   pointer to a BitBuffer instance to be queried\n *\n * @return     pointer to the parity data\n */\nconst uint8_t* bit_buffer_get_parity(const BitBuffer* buf);\n\n// Setters\n\n/** Set byte value at a specified index in a BitBuffer instance.\n *\n * @warning       The index must be valid (i.e.\\ less than the instance's data\n *                size in bytes).\n *\n * @param[in,out] buf    pointer to a BitBuffer instance to be modified\n * @param[in]     index  index of the byte in question\n * @param[in]     byte   byte value to be set at index\n */\nvoid bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte);\n\n/** Set byte and parity bit value at a specified index in a BitBuffer instance.\n *\n * @warning       The index must be valid (i.e.\\ less than the instance's data\n *                size in bytes).\n *\n * @param[in,out] buff    pointer to a BitBuffer instance to be modified\n * @param[in]     index   index of the byte in question\n * @param[in]     byte    byte value to be set at index\n * @param[in]     parity  parity bit value to be set at index\n */\nvoid bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity);\n\n/** Resize a BitBuffer instance to a new size, in bits.\n *\n * @warning       May cause bugs. Use only if absolutely necessary.\n *\n * @param[in,out] buf       pointer to a BitBuffer instance to be resized\n * @param[in]     new_size  the new size of the buffer, in bits\n */\nvoid bit_buffer_set_size(BitBuffer* buf, size_t new_size);\n\n/** Resize a BitBuffer instance to a new size, in bytes.\n *\n * @warning       May cause bugs. Use only if absolutely necessary.\n *\n * @param[in,out] buf             pointer to a BitBuffer instance to be resized\n * @param[in]     new_size_bytes  the new size of the buffer, in bytes\n */\nvoid bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes);\n\n// Modification\n\n/** Append all BitBuffer's instance contents to this one.\n *\n * @warning       The destination capacity must be no less than its original\n *                data size plus source data size.\n *\n * @param[in,out] buf    pointer to a BitBuffer instance to be appended to\n * @param[in]     other  pointer to a BitBuffer instance to be appended\n */\nvoid bit_buffer_append(BitBuffer* buf, const BitBuffer* other);\n\n/** Append a BitBuffer's instance contents to this one, starting from\n * start_index.\n *\n * @warning       The destination capacity must be no less than the source data\n *                size counting from start_index.\n *\n * @param[in,out] buf          pointer to a BitBuffer instance to be appended to\n * @param[in]     other        pointer to a BitBuffer instance to be appended\n * @param[in]     start_index  index to begin copying source data from\n */\nvoid bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index);\n\n/** Append a byte to a BitBuffer instance.\n *\n * @warning       The destination capacity must be no less its original data\n *                size plus one.\n *\n * @param[in,out] buf   pointer to a BitBuffer instance to be appended to\n * @param[in]     byte  byte value to be appended\n */\nvoid bit_buffer_append_byte(BitBuffer* buf, uint8_t byte);\n\n/** Append a byte array to a BitBuffer instance.\n *\n * @warning       The destination capacity must be no less its original data\n *                size plus source data size.\n *\n * @param[in,out] buf         pointer to a BitBuffer instance to be appended to\n * @param[in]     data        pointer to the byte array to be appended\n * @param[in]     size_bytes  size of the data to be appended, in bytes\n */\nvoid bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes);\n\n/** Append a bit to a BitBuffer instance.\n *\n * @warning       The destination capacity must be sufficient to accommodate the\n *                additional bit.\n *\n * @param[in,out] buf   pointer to a BitBuffer instance to be appended to\n * @param[in]     bit   bit value to be appended\n */\nvoid bit_buffer_append_bit(BitBuffer* buf, bool bit);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/buffer_stream.c",
    "content": "#include \"buffer_stream.h\"\n\nstruct Buffer {\n    volatile bool occupied;\n    volatile size_t size;\n    uint8_t* data;\n    size_t max_data_size;\n};\n\nstruct BufferStream {\n    size_t stream_overrun_count;\n    FuriStreamBuffer* stream;\n\n    size_t index;\n    size_t max_buffers_count;\n    Buffer buffers[];\n};\n\nbool buffer_write(Buffer* buffer, const uint8_t* data, size_t size) {\n    if(buffer->occupied) {\n        return false;\n    }\n    if((buffer->size + size) > buffer->max_data_size) {\n        return false;\n    }\n    memcpy(buffer->data + buffer->size, data, size);\n    buffer->size += size;\n    return true;\n}\n\nuint8_t* buffer_get_data(Buffer* buffer) {\n    return buffer->data;\n}\n\nsize_t buffer_get_size(Buffer* buffer) {\n    return buffer->size;\n}\n\nvoid buffer_reset(Buffer* buffer) {\n    buffer->occupied = false;\n    buffer->size = 0;\n}\n\nBufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count) {\n    furi_assert(buffer_size > 0);\n    furi_assert(buffers_count > 0);\n    BufferStream* buffer_stream = malloc(sizeof(BufferStream) + (sizeof(Buffer) * buffers_count));\n    buffer_stream->max_buffers_count = buffers_count;\n    for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) {\n        buffer_stream->buffers[i].occupied = false;\n        buffer_stream->buffers[i].size = 0;\n        buffer_stream->buffers[i].data = malloc(buffer_size);\n        buffer_stream->buffers[i].max_data_size = buffer_size;\n    }\n    buffer_stream->stream = furi_stream_buffer_alloc(\n        sizeof(BufferStream*) * buffer_stream->max_buffers_count, sizeof(BufferStream*));\n    buffer_stream->stream_overrun_count = 0;\n    buffer_stream->index = 0;\n\n    return buffer_stream;\n}\n\nvoid buffer_stream_free(BufferStream* buffer_stream) {\n    for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) {\n        free(buffer_stream->buffers[i].data);\n    }\n    furi_stream_buffer_free(buffer_stream->stream);\n    free(buffer_stream);\n}\n\nstatic inline int8_t buffer_stream_get_free_buffer(BufferStream* buffer_stream) {\n    int8_t id = -1;\n    for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) {\n        if(buffer_stream->buffers[i].occupied == false) {\n            id = i;\n            break;\n        }\n    }\n\n    return id;\n}\n\nbool buffer_stream_send_from_isr(BufferStream* buffer_stream, const uint8_t* data, size_t size) {\n    Buffer* buffer = &buffer_stream->buffers[buffer_stream->index];\n    bool result = true;\n\n    // write to buffer\n    if(!buffer_write(buffer, data, size)) {\n        // if buffer is full - send it\n        buffer->occupied = true;\n        // we always have space for buffer in stream\n        furi_stream_buffer_send(buffer_stream->stream, &buffer, sizeof(Buffer*), 0);\n\n        // get new buffer from the pool\n        int8_t index = buffer_stream_get_free_buffer(buffer_stream);\n\n        // check that we have valid buffer\n        if(index == -1) {\n            // no free buffer\n            buffer_stream->stream_overrun_count++;\n            result = false;\n        } else {\n            // write to new buffer\n            buffer_stream->index = index;\n            buffer = &buffer_stream->buffers[buffer_stream->index];\n            buffer_write(buffer, data, size);\n        }\n    }\n\n    return result;\n}\n\nBuffer* buffer_stream_receive(BufferStream* buffer_stream, uint32_t timeout) {\n    Buffer* buffer;\n    size_t size =\n        furi_stream_buffer_receive(buffer_stream->stream, &buffer, sizeof(Buffer*), timeout);\n\n    if(size == sizeof(Buffer*)) {\n        return buffer;\n    } else {\n        return NULL;\n    }\n}\n\nsize_t buffer_stream_get_overrun_count(BufferStream* buffer_stream) {\n    return buffer_stream->stream_overrun_count;\n}\n\nvoid buffer_stream_reset(BufferStream* buffer_stream) {\n    FURI_CRITICAL_ENTER();\n    furi_stream_buffer_reset(buffer_stream->stream);\n\n    buffer_stream->stream_overrun_count = 0;\n    for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) {\n        buffer_reset(&buffer_stream->buffers[i]);\n    }\n    FURI_CRITICAL_EXIT();\n}\n"
  },
  {
    "path": "lib/toolbox/buffer_stream.h",
    "content": "/**\n * @file buffer_stream.h\n * \n * This file implements the concept of a buffer stream.\n * Data is written to the buffer until the buffer is full.\n * Then the buffer pointer is written to the stream, and the new write buffer is taken from the buffer pool.\n * After the buffer has been read by the receiving thread, it is sent to the free buffer pool.\n * \n * This will speed up sending large chunks of data between threads, compared to using a stream directly.\n */\n#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Buffer Buffer;\n\n/**\n * @brief Get buffer data pointer\n * @param buffer \n * @return uint8_t* \n */\nuint8_t* buffer_get_data(Buffer* buffer);\n\n/**\n * @brief Get buffer size\n * @param buffer \n * @return size_t \n */\nsize_t buffer_get_size(Buffer* buffer);\n\n/**\n * @brief Reset buffer and send to free buffer pool\n * @param buffer \n */\nvoid buffer_reset(Buffer* buffer);\n\ntypedef struct BufferStream BufferStream;\n\n/**\n * @brief Allocate a new BufferStream instance\n * @param buffer_size \n * @param buffers_count \n * @return BufferStream* \n */\nBufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count);\n\n/**\n * @brief Free a BufferStream instance\n * @param buffer_stream \n */\nvoid buffer_stream_free(BufferStream* buffer_stream);\n\n/**\n * @brief Write data to buffer stream, from ISR context\n * Data will be written to the buffer until the buffer is full, and only then will the buffer be sent.\n * @param buffer_stream \n * @param data \n * @param size \n * @return bool \n */\nbool buffer_stream_send_from_isr(BufferStream* buffer_stream, const uint8_t* data, size_t size);\n\n/**\n * @brief Receive buffer from stream\n * @param buffer_stream \n * @param timeout \n * @return Buffer* \n */\nBuffer* buffer_stream_receive(BufferStream* buffer_stream, uint32_t timeout);\n\n/**\n * @brief Get stream overrun count\n * @param buffer_stream \n * @return size_t \n */\nsize_t buffer_stream_get_overrun_count(BufferStream* buffer_stream);\n\n/**\n * @brief Reset stream and buffer pool\n * @param buffer_stream \n */\nvoid buffer_stream_reset(BufferStream* buffer_stream);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/cli_ansi.c",
    "content": "#include \"cli_ansi.h\"\n\ntypedef enum {\n    CliAnsiParserStateInitial,\n    CliAnsiParserStateEscape,\n    CliAnsiParserStateEscapeBrace,\n    CliAnsiParserStateEscapeBraceOne,\n    CliAnsiParserStateEscapeBraceOneSemicolon,\n    CliAnsiParserStateEscapeBraceOneSemicolonModifiers,\n} CliAnsiParserState;\n\nstruct CliAnsiParser {\n    CliAnsiParserState state;\n    CliModKey modifiers;\n};\n\nCliAnsiParser* cli_ansi_parser_alloc(void) {\n    CliAnsiParser* parser = malloc(sizeof(CliAnsiParser));\n    return parser;\n}\n\nvoid cli_ansi_parser_free(CliAnsiParser* parser) {\n    free(parser);\n}\n\n/**\n * @brief Converts a single character representing a special key into the enum\n * representation\n */\nstatic CliKey cli_ansi_key_from_mnemonic(char c) {\n    switch(c) {\n    case 'A':\n        return CliKeyUp;\n    case 'B':\n        return CliKeyDown;\n    case 'C':\n        return CliKeyRight;\n    case 'D':\n        return CliKeyLeft;\n    case 'F':\n        return CliKeyEnd;\n    case 'H':\n        return CliKeyHome;\n    default:\n        return CliKeyUnrecognized;\n    }\n}\n\n#define PARSER_RESET_AND_RETURN(parser, modifiers_val, key_val) \\\n    do {                                                        \\\n        parser->state = CliAnsiParserStateInitial;              \\\n        return (CliAnsiParserResult){                           \\\n            .is_done = true,                                    \\\n            .result = (CliKeyCombo){                            \\\n                .modifiers = modifiers_val,                     \\\n                .key = key_val,                                 \\\n            }};                                                 \\\n    } while(0);\n\nCliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c) {\n    switch(parser->state) {\n    case CliAnsiParserStateInitial:\n        // <key> -> <key>\n        if(c != CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c); // -V1048\n\n        // <ESC> ...\n        parser->state = CliAnsiParserStateEscape;\n        break;\n\n    case CliAnsiParserStateEscape:\n        // <ESC> <ESC> -> <ESC>\n        if(c == CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c);\n\n        // <ESC> <key> -> Alt + <key>\n        if(c != '[') PARSER_RESET_AND_RETURN(parser, CliModKeyAlt, c);\n\n        // <ESC> [ ...\n        parser->state = CliAnsiParserStateEscapeBrace;\n        break;\n\n    case CliAnsiParserStateEscapeBrace:\n        // <ESC> [ <key mnemonic> -> <key>\n        if(c != '1') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, cli_ansi_key_from_mnemonic(c));\n\n        // <ESC> [ 1 ...\n        parser->state = CliAnsiParserStateEscapeBraceOne;\n        break;\n\n    case CliAnsiParserStateEscapeBraceOne:\n        // <ESC> [ 1 <non-;> -> error\n        if(c != ';') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, CliKeyUnrecognized);\n\n        // <ESC> [ 1 ; ...\n        parser->state = CliAnsiParserStateEscapeBraceOneSemicolon;\n        break;\n\n    case CliAnsiParserStateEscapeBraceOneSemicolon:\n        // <ESC> [ 1 ; <modifiers> ...\n        parser->modifiers = (c - '0');\n        parser->modifiers &= ~1;\n        parser->state = CliAnsiParserStateEscapeBraceOneSemicolonModifiers;\n        break;\n\n    case CliAnsiParserStateEscapeBraceOneSemicolonModifiers:\n        // <ESC> [ 1 ; <modifiers> <key mnemonic> -> <modifiers> + <key>\n        PARSER_RESET_AND_RETURN(parser, parser->modifiers, cli_ansi_key_from_mnemonic(c));\n    }\n\n    return (CliAnsiParserResult){.is_done = false};\n}\n\nCliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser) {\n    CliAnsiParserResult result = {.is_done = false};\n\n    if(parser->state == CliAnsiParserStateEscape) {\n        result.is_done = true;\n        result.result.key = CliKeyEsc;\n        result.result.modifiers = CliModKeyNo;\n    }\n\n    parser->state = CliAnsiParserStateInitial;\n    return result;\n}\n"
  },
  {
    "path": "lib/toolbox/cli/cli_ansi.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// text styling\n\n#define ANSI_RESET  \"\\e[0m\"\n#define ANSI_BOLD   \"\\e[1m\"\n#define ANSI_FAINT  \"\\e[2m\"\n#define ANSI_INVERT \"\\e[7m\"\n\n#define ANSI_FG_BLACK      \"\\e[30m\"\n#define ANSI_FG_RED        \"\\e[31m\"\n#define ANSI_FG_GREEN      \"\\e[32m\"\n#define ANSI_FG_YELLOW     \"\\e[33m\"\n#define ANSI_FG_BLUE       \"\\e[34m\"\n#define ANSI_FG_MAGENTA    \"\\e[35m\"\n#define ANSI_FG_CYAN       \"\\e[36m\"\n#define ANSI_FG_WHITE      \"\\e[37m\"\n#define ANSI_FG_BR_BLACK   \"\\e[90m\"\n#define ANSI_FG_BR_RED     \"\\e[91m\"\n#define ANSI_FG_BR_GREEN   \"\\e[92m\"\n#define ANSI_FG_BR_YELLOW  \"\\e[93m\"\n#define ANSI_FG_BR_BLUE    \"\\e[94m\"\n#define ANSI_FG_BR_MAGENTA \"\\e[95m\"\n#define ANSI_FG_BR_CYAN    \"\\e[96m\"\n#define ANSI_FG_BR_WHITE   \"\\e[97m\"\n\n#define ANSI_BG_BLACK      \"\\e[40m\"\n#define ANSI_BG_RED        \"\\e[41m\"\n#define ANSI_BG_GREEN      \"\\e[42m\"\n#define ANSI_BG_YELLOW     \"\\e[43m\"\n#define ANSI_BG_BLUE       \"\\e[44m\"\n#define ANSI_BG_MAGENTA    \"\\e[45m\"\n#define ANSI_BG_CYAN       \"\\e[46m\"\n#define ANSI_BG_WHITE      \"\\e[47m\"\n#define ANSI_BG_BR_BLACK   \"\\e[100m\"\n#define ANSI_BG_BR_RED     \"\\e[101m\"\n#define ANSI_BG_BR_GREEN   \"\\e[102m\"\n#define ANSI_BG_BR_YELLOW  \"\\e[103m\"\n#define ANSI_BG_BR_BLUE    \"\\e[104m\"\n#define ANSI_BG_BR_MAGENTA \"\\e[105m\"\n#define ANSI_BG_BR_CYAN    \"\\e[106m\"\n#define ANSI_BG_BR_WHITE   \"\\e[107m\"\n\n#define ANSI_FLIPPER_BRAND_ORANGE \"\\e[38;2;255;130;0m\"\n\n// cursor positioning\n\n#define ANSI_CURSOR_UP_BY(rows)                    \"\\e[\" rows \"A\"\n#define ANSI_CURSOR_DOWN_BY(rows)                  \"\\e[\" rows \"B\"\n#define ANSI_CURSOR_RIGHT_BY(cols)                 \"\\e[\" cols \"C\"\n#define ANSI_CURSOR_LEFT_BY(cols)                  \"\\e[\" cols \"D\"\n#define ANSI_CURSOR_DOWN_BY_AND_FIRST_COLUMN(rows) \"\\e[\" rows \"E\"\n#define ANSI_CURSOR_UP_BY_AND_FIRST_COLUMN(rows)   \"\\e[\" rows \"F\"\n#define ANSI_CURSOR_HOR_POS(pos)                   \"\\e[\" pos \"G\"\n#define ANSI_CURSOR_POS(row, col)                  \"\\e[\" row \";\" col \"H\"\n\n// erasing\n\n#define ANSI_ERASE_FROM_CURSOR_TO_END   \"0\"\n#define ANSI_ERASE_FROM_START_TO_CURSOR \"1\"\n#define ANSI_ERASE_ENTIRE               \"2\"\n\n#define ANSI_ERASE_DISPLAY(portion)  \"\\e[\" portion \"J\"\n#define ANSI_ERASE_LINE(portion)     \"\\e[\" portion \"K\"\n#define ANSI_ERASE_SCROLLBACK_BUFFER ANSI_ERASE_DISPLAY(\"3\")\n\n// misc\n\n#define ANSI_INSERT_MODE_ENABLE  \"\\e[4h\"\n#define ANSI_INSERT_MODE_DISABLE \"\\e[4l\"\n\ntypedef enum {\n    CliKeyUnrecognized = 0,\n\n    CliKeySOH = 0x01,\n    CliKeyETX = 0x03,\n    CliKeyEOT = 0x04,\n    CliKeyBell = 0x07,\n    CliKeyBackspace = 0x08,\n    CliKeyTab = 0x09,\n    CliKeyLF = 0x0A,\n    CliKeyFF = 0x0C,\n    CliKeyCR = 0x0D,\n    CliKeyETB = 0x17,\n    CliKeyEsc = 0x1B,\n    CliKeyUS = 0x1F,\n    CliKeySpace = 0x20,\n    CliKeyDEL = 0x7F,\n\n    CliKeySpecial = 0x80,\n    CliKeyLeft,\n    CliKeyRight,\n    CliKeyUp,\n    CliKeyDown,\n    CliKeyHome,\n    CliKeyEnd,\n} CliKey;\n\ntypedef enum {\n    CliModKeyNo = 0,\n    CliModKeyAlt = 2,\n    CliModKeyCtrl = 4,\n    CliModKeyMeta = 8,\n} CliModKey;\n\ntypedef struct {\n    CliModKey modifiers;\n    CliKey key;\n} CliKeyCombo;\n\ntypedef struct CliAnsiParser CliAnsiParser;\n\ntypedef struct {\n    bool is_done;\n    CliKeyCombo result;\n} CliAnsiParserResult;\n\n/**\n * @brief Allocates an ANSI parser\n */\nCliAnsiParser* cli_ansi_parser_alloc(void);\n\n/**\n * @brief Frees an ANSI parser\n */\nvoid cli_ansi_parser_free(CliAnsiParser* parser);\n\n/**\n * @brief Feeds an ANSI parser a character\n */\nCliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c);\n\n/**\n * @brief Feeds an ANSI parser a timeout event\n * \n * As a user of the ANSI parser API, you are responsible for calling this\n * function some time after the last character was fed into the parser. The\n * recommended timeout is about 10 ms. The exact value does not matter as long\n * as it is small enough for the user not notice a delay, but big enough that\n * when a terminal is sending an escape sequence, this function does not get\n * called in between the characters of the sequence.\n */\nCliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/cli_command.c",
    "content": "#include \"cli_command.h\"\n#include \"cli_ansi.h\"\n\nbool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side) {\n    if(pipe_state(side) == PipeStateBroken) return true;\n    if(!pipe_bytes_available(side)) return false;\n    char c = getchar();\n    return c == CliKeyETX;\n}\n\nvoid cli_print_usage(const char* cmd, const char* usage, const char* arg) {\n    furi_check(cmd);\n    furi_check(arg);\n    furi_check(usage);\n\n    printf(\"%s: illegal option -- %s\\r\\nusage: %s %s\", cmd, arg, cmd, usage);\n}\n\nbool cli_sleep(PipeSide* side, uint32_t duration_in_ms) {\n    uint32_t passed_time = 0;\n    bool is_interrupted = false;\n\n    do {\n        uint32_t left_time = duration_in_ms - passed_time;\n        uint32_t check_interval = left_time >= 100 ? 100 : left_time;\n        furi_delay_ms(check_interval);\n        passed_time += check_interval;\n        is_interrupted = cli_is_pipe_broken_or_is_etx_next_char(side);\n    } while(!is_interrupted && passed_time < duration_in_ms);\n\n    return !is_interrupted;\n}\n"
  },
  {
    "path": "lib/toolbox/cli/cli_command.h",
    "content": "/**\n * @file cli_command.h\n * Command metadata and helpers\n */\n\n#pragma once\n\n#include <furi.h>\n#include <toolbox/pipe.h>\n#include <lib/flipper_application/flipper_application.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_PLUGIN_API_VERSION 1\n\ntypedef enum {\n    CliCommandFlagDefault = 0, /**< Default */\n    CliCommandFlagParallelSafe = (1 << 0), /**< Safe to run in parallel with other apps */\n    CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */\n    CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */\n    CliCommandFlagUseShellThread =\n        (1\n         << 3), /**< Don't start a separate thread to run the command in. Incompatible with DontAttachStdio */\n\n    // internal flags (do not set them yourselves!)\n\n    CliCommandFlagExternal = (1 << 4), /**< The command comes from a .fal file */\n} CliCommandFlag;\n\n/**\n * @brief CLI command execution callback pointer\n *\n * This callback will be called from a separate thread spawned just for your\n * command. The pipe will be installed as the thread's stdio, so you can use\n * `printf`, `getchar` and other standard functions to communicate with the\n * user.\n *\n * @param [in] pipe     Pipe that can be used to send and receive data. If\n *                      `CliCommandFlagDontAttachStdio` was not set, you can\n *                      also use standard C functions (printf, getc, etc.) to\n *                      access this pipe.\n * @param [in] args     String with what was passed after the command\n * @param [in] context  Whatever you provided to `cli_add_command`\n */\ntypedef void (*CliCommandExecuteCallback)(PipeSide* pipe, FuriString* args, void* context);\n\ntypedef struct {\n    char* name;\n    CliCommandExecuteCallback execute_callback;\n    CliCommandFlag flags;\n    size_t stack_depth;\n} CliCommandDescriptor;\n\n/**\n * @brief Configuration for locating external commands\n */\ntypedef struct {\n    const char* search_directory; //<! The directory to look in\n    const char* fal_prefix; //<! File name prefix that commands should have\n    const char* appid; //<! Expected plugin-reported appid\n} CliCommandExternalConfig;\n\n/**\n * @brief Detects if Ctrl+C has been pressed or session has been terminated\n *\n * @param [in] side Pointer to pipe side given to the command thread\n * @warning This function also assumes that the pipe is installed as the\n *          thread's stdio\n * @warning This function will consume 0 or 1 bytes from the pipe\n */\nbool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side);\n\n/** Print unified cmd usage tip\n *\n * @param      cmd    cmd name\n * @param      usage  usage tip\n * @param      arg    arg passed by user\n */\nvoid cli_print_usage(const char* cmd, const char* usage, const char* arg);\n\n/**\n * @brief Pause for a specified duration or until Ctrl+C is pressed or the\n * session is terminated.\n *\n * @param [in] side Pointer to pipe side given to the command thread.\n * @param [in] duration_in_ms Duration of sleep in milliseconds.\n * @return `true` if the sleep completed without interruption.\n * @return `false` if interrupted.\n *\n * @warning This function also assumes that the pipe is installed as the\n *          thread's stdio.\n * @warning This function will consume 0 or 1 bytes from the pipe.\n */\nbool cli_sleep(PipeSide* side, uint32_t duration_in_ms);\n\n#define CLI_COMMAND_INTERFACE(name, execute_callback, flags, stack_depth, app_id) \\\n    static const CliCommandDescriptor cli_##name##_desc = {                       \\\n        #name,                                                                    \\\n        &execute_callback,                                                        \\\n        flags,                                                                    \\\n        stack_depth,                                                              \\\n    };                                                                            \\\n                                                                                  \\\n    static const FlipperAppPluginDescriptor plugin_descriptor = {                 \\\n        .appid = app_id,                                                          \\\n        .ep_api_version = CLI_PLUGIN_API_VERSION,                                 \\\n        .entry_point = &cli_##name##_desc,                                        \\\n    };                                                                            \\\n                                                                                  \\\n    const FlipperAppPluginDescriptor* cli_##name##_ep(void) {                     \\\n        return &plugin_descriptor;                                                \\\n    }\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/cli_registry.c",
    "content": "#include \"cli_registry.h\"\n#include \"cli_registry_i.h\"\n#include <toolbox/pipe.h>\n#include <storage/storage.h>\n\n#define TAG \"CliRegistry\"\n\nstruct CliRegistry {\n    CliCommandDict_t commands;\n    FuriMutex* mutex;\n};\n\nCliRegistry* cli_registry_alloc(void) {\n    CliRegistry* registry = malloc(sizeof(CliRegistry));\n    CliCommandDict_init(registry->commands);\n    registry->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);\n    return registry;\n}\n\nvoid cli_registry_free(CliRegistry* registry) {\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n    furi_mutex_free(registry->mutex);\n    CliCommandDict_clear(registry->commands);\n    free(registry);\n}\n\nvoid cli_registry_add_command(\n    CliRegistry* registry,\n    const char* name,\n    CliCommandFlag flags,\n    CliCommandExecuteCallback callback,\n    void* context) {\n    cli_registry_add_command_ex(\n        registry, name, flags, callback, context, CLI_BUILTIN_COMMAND_STACK_SIZE);\n}\n\nvoid cli_registry_add_command_ex(\n    CliRegistry* registry,\n    const char* name,\n    CliCommandFlag flags,\n    CliCommandExecuteCallback callback,\n    void* context,\n    size_t stack_size) {\n    furi_check(registry);\n    furi_check(name);\n    furi_check(callback);\n\n    // the shell always attaches the pipe to the stdio, thus both flags can't be used at once\n    if(flags & CliCommandFlagUseShellThread) furi_check(!(flags & CliCommandFlagDontAttachStdio));\n\n    FuriString* name_str;\n    name_str = furi_string_alloc_set(name);\n    // command cannot contain spaces\n    furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE);\n\n    CliRegistryCommand command = {\n        .context = context,\n        .execute_callback = callback,\n        .flags = flags,\n        .stack_depth = stack_size,\n    };\n\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n    CliCommandDict_set_at(registry->commands, name_str, command);\n    furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);\n\n    furi_string_free(name_str);\n}\n\nvoid cli_registry_delete_command(CliRegistry* registry, const char* name) {\n    furi_check(registry);\n    FuriString* name_str;\n    name_str = furi_string_alloc_set(name);\n    furi_string_trim(name_str);\n\n    size_t name_replace;\n    do {\n        name_replace = furi_string_replace(name_str, \" \", \"_\");\n    } while(name_replace != FURI_STRING_FAILURE);\n\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n    CliCommandDict_erase(registry->commands, name_str);\n    furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);\n\n    furi_string_free(name_str);\n}\n\nbool cli_registry_get_command(\n    CliRegistry* registry,\n    FuriString* command,\n    CliRegistryCommand* result) {\n    furi_assert(registry);\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n    CliRegistryCommand* data = CliCommandDict_get(registry->commands, command);\n    if(data) *result = *data;\n\n    furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);\n\n    return !!data;\n}\n\nvoid cli_registry_remove_external_commands(CliRegistry* registry) {\n    furi_check(registry);\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n\n    CliCommandDict_t internal_cmds;\n    CliCommandDict_init(internal_cmds);\n    for\n        M_EACH(item, registry->commands, CliCommandDict_t) {\n            if(!(item->value.flags & CliCommandFlagExternal))\n                CliCommandDict_set_at(internal_cmds, item->key, item->value);\n        }\n    CliCommandDict_move(registry->commands, internal_cmds);\n\n    furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);\n}\n\nvoid cli_registry_reload_external_commands(\n    CliRegistry* registry,\n    const CliCommandExternalConfig* config) {\n    furi_check(registry);\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n    FURI_LOG_D(TAG, \"Reloading ext commands\");\n\n    cli_registry_remove_external_commands(registry);\n\n    // iterate over files in plugin directory\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* plugin_dir = storage_file_alloc(storage);\n\n    if(storage_dir_open(plugin_dir, config->search_directory)) {\n        char plugin_filename[64];\n        FuriString* plugin_name = furi_string_alloc();\n\n        while(storage_dir_read(plugin_dir, NULL, plugin_filename, sizeof(plugin_filename))) {\n            FURI_LOG_T(TAG, \"Plugin: %s\", plugin_filename);\n            furi_string_set_str(plugin_name, plugin_filename);\n\n            if(!furi_string_end_with_str(plugin_name, \".fal\")) continue;\n            furi_string_replace_all_str(plugin_name, \".fal\", \"\");\n            if(!furi_string_start_with_str(plugin_name, config->fal_prefix)) continue;\n            furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), \"\");\n\n            CliRegistryCommand command = {\n                .context = NULL,\n                .execute_callback = NULL,\n                .flags = CliCommandFlagExternal,\n            };\n            CliCommandDict_set_at(registry->commands, plugin_name, command);\n        }\n\n        furi_string_free(plugin_name);\n    }\n\n    storage_dir_close(plugin_dir);\n    storage_file_free(plugin_dir);\n    furi_record_close(RECORD_STORAGE);\n\n    FURI_LOG_D(TAG, \"Done reloading ext commands\");\n    furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);\n}\n\nvoid cli_registry_lock(CliRegistry* registry) {\n    furi_assert(registry);\n    furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);\n}\n\nvoid cli_registry_unlock(CliRegistry* registry) {\n    furi_assert(registry);\n    furi_mutex_release(registry->mutex);\n}\n\nCliCommandDict_t* cli_registry_get_commands(CliRegistry* registry) {\n    furi_assert(registry);\n    return &registry->commands;\n}\n"
  },
  {
    "path": "lib/toolbox/cli/cli_registry.h",
    "content": "/**\n * @file cli_registry.h\n * API for registering commands with a CLI shell\n */\n\n#pragma once\n\n#include <furi.h>\n#include <m-array.h>\n#include <toolbox/pipe.h>\n#include \"cli_command.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct CliRegistry CliRegistry;\n\n/**\n * @brief Allocates a `CliRegistry`.\n */\nCliRegistry* cli_registry_alloc(void);\n\n/**\n * @brief Frees a `CliRegistry`.\n */\nvoid cli_registry_free(CliRegistry* registry);\n\n/**\n * @brief Registers a command with the registry. Provides less options than the\n * `_ex` counterpart.\n *\n * @param [in] registry  Pointer to registry instance\n * @param [in] name      Command name\n * @param [in] flags     see CliCommandFlag\n * @param [in] callback  Callback function\n * @param [in] context   Custom context\n */\nvoid cli_registry_add_command(\n    CliRegistry* registry,\n    const char* name,\n    CliCommandFlag flags,\n    CliCommandExecuteCallback callback,\n    void* context);\n\n/**\n * @brief Registers a command with the registry. Provides more options than the\n * non-`_ex` counterpart.\n *\n * @param [in] registry   Pointer to registry instance\n * @param [in] name       Command name\n * @param [in] flags      see CliCommandFlag\n * @param [in] callback   Callback function\n * @param [in] context    Custom context\n * @param [in] stack_size Thread stack size\n */\nvoid cli_registry_add_command_ex(\n    CliRegistry* registry,\n    const char* name,\n    CliCommandFlag flags,\n    CliCommandExecuteCallback callback,\n    void* context,\n    size_t stack_size);\n\n/**\n * @brief Deletes a cli command\n *\n * @param [in] registry Pointer to registry instance\n * @param [in] name     Command name\n */\nvoid cli_registry_delete_command(CliRegistry* registry, const char* name);\n\n/**\n * @brief Unregisters all external commands\n * \n * @param [in] registry Pointer to registry instance\n */\nvoid cli_registry_remove_external_commands(CliRegistry* registry);\n\n/**\n * @brief Reloads the list of externally available commands\n * \n * @param [in] registry Pointer to registry instance\n * @param [in] config   See `CliCommandExternalConfig`\n */\nvoid cli_registry_reload_external_commands(\n    CliRegistry* registry,\n    const CliCommandExternalConfig* config);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/cli_registry_i.h",
    "content": "/**\n * @file cli_registry_i.h\n * Internal API for getting commands registered with the CLI\n */\n\n#pragma once\n\n#include <furi.h>\n#include <m-dict.h>\n#include \"cli_registry.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_BUILTIN_COMMAND_STACK_SIZE (4 * 1024U)\n\ntypedef struct {\n    void* context; //<! Context passed to callbacks\n    CliCommandExecuteCallback execute_callback; //<! Callback for command execution\n    CliCommandFlag flags;\n    size_t stack_depth;\n} CliRegistryCommand;\n\nDICT_DEF2(CliCommandDict, FuriString*, FURI_STRING_OPLIST, CliRegistryCommand, M_POD_OPLIST);\n\n#define M_OPL_CliCommandDict_t() DICT_OPLIST(CliCommandDict, FURI_STRING_OPLIST, M_POD_OPLIST)\n\nbool cli_registry_get_command(\n    CliRegistry* registry,\n    FuriString* command,\n    CliRegistryCommand* result);\n\nvoid cli_registry_lock(CliRegistry* registry);\n\nvoid cli_registry_unlock(CliRegistry* registry);\n\n/**\n * @warning Surround calls to this function with `cli_registry_[un]lock`\n */\nCliCommandDict_t* cli_registry_get_commands(CliRegistry* registry);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell.c",
    "content": "#include \"cli_shell.h\"\n#include \"cli_shell_i.h\"\n#include \"../cli_ansi.h\"\n#include \"../cli_registry_i.h\"\n#include \"../cli_command.h\"\n#include \"cli_shell_line.h\"\n#include \"cli_shell_completions.h\"\n#include <stdio.h>\n#include <furi_hal_version.h>\n#include <m-array.h>\n#include <loader/loader.h>\n#include <toolbox/pipe.h>\n#include <flipper_application/plugins/plugin_manager.h>\n#include <loader/firmware_api/firmware_api.h>\n#include <storage/storage.h>\n\n#define TAG \"CliShell\"\n\n#define ANSI_TIMEOUT_MS             10\n#define TRANSIENT_SESSION_WINDOW_MS 100\n\ntypedef enum {\n    CliShellComponentCompletions,\n    CliShellComponentLine,\n    CliShellComponentMAX, //<! do not use\n} CliShellComponent;\n\nCliShellKeyComboSet* component_key_combo_sets[] = {\n    [CliShellComponentCompletions] = &cli_shell_completions_key_combo_set,\n    [CliShellComponentLine] = &cli_shell_line_key_combo_set,\n};\nstatic_assert(CliShellComponentMAX == COUNT_OF(component_key_combo_sets));\n\ntypedef enum {\n    CliShellStorageEventMount = (1 << 0),\n    CliShellStorageEventUnmount = (1 << 1),\n} CliShellStorageEvent;\n\n#define CliShellStorageEventAll (CliShellStorageEventMount | CliShellStorageEventUnmount)\n\ntypedef struct {\n    Storage* storage;\n    FuriPubSubSubscription* subscription;\n    FuriEventFlag* event_flag;\n} CliShellStorage;\n\nstruct CliShell {\n    // Set and freed by external thread\n    CliShellMotd motd;\n    void* callback_context;\n    PipeSide* pipe;\n    CliRegistry* registry;\n    const CliCommandExternalConfig* ext_config;\n    FuriThread* thread;\n    const char* prompt;\n\n    // Set and freed by shell thread\n    FuriEventLoop* event_loop;\n    CliAnsiParser* ansi_parser;\n    FuriEventLoopTimer* ansi_parsing_timer;\n    CliShellStorage storage;\n    void* components[CliShellComponentMAX];\n};\n\ntypedef struct {\n    CliRegistryCommand* command;\n    PipeSide* pipe;\n    FuriString* args;\n} CliCommandThreadData;\n\nstatic void cli_shell_data_available(PipeSide* pipe, void* context);\nstatic void cli_shell_pipe_broken(PipeSide* pipe, void* context);\n\nstatic void cli_shell_install_pipe(CliShell* cli_shell) {\n    pipe_install_as_stdio(cli_shell->pipe);\n    pipe_attach_to_event_loop(cli_shell->pipe, cli_shell->event_loop);\n    pipe_set_callback_context(cli_shell->pipe, cli_shell);\n    pipe_set_data_arrived_callback(cli_shell->pipe, cli_shell_data_available, 0);\n    pipe_set_broken_callback(cli_shell->pipe, cli_shell_pipe_broken, 0);\n}\n\nstatic void cli_shell_detach_pipe(CliShell* cli_shell) {\n    pipe_detach_from_event_loop(cli_shell->pipe);\n    furi_thread_set_stdin_callback(NULL, NULL);\n    furi_thread_set_stdout_callback(NULL, NULL);\n}\n\n// =================\n// Built-in commands\n// =================\n\nvoid cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    CliShell* shell = context;\n    furi_check(shell->ext_config);\n    cli_registry_reload_external_commands(shell->registry, shell->ext_config);\n    printf(\"OK!\");\n}\n\nvoid cli_command_help(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    CliShell* shell = context;\n    CliRegistry* registry = shell->registry;\n\n    const size_t columns = 3;\n\n    printf(\"Available commands:\\r\\n\" ANSI_FG_GREEN);\n    cli_registry_lock(registry);\n    CliCommandDict_t* commands = cli_registry_get_commands(registry);\n    size_t commands_count = CliCommandDict_size(*commands);\n\n    CliCommandDict_it_t iterator;\n    CliCommandDict_it(iterator, *commands);\n    for(size_t i = 0; i < commands_count; i++) {\n        const CliCommandDict_itref_t* item = CliCommandDict_cref(iterator);\n        printf(\"%-30s\", furi_string_get_cstr(item->key));\n        CliCommandDict_next(iterator);\n\n        if(i % columns == columns - 1) printf(\"\\r\\n\");\n    }\n\n    if(shell->ext_config)\n        printf(\n            ANSI_RESET\n            \"\\r\\nIf you added a new external command and can't see it above, run `reload_ext_cmds`\");\n    printf(ANSI_RESET \"\\r\\nFind out more: https://docs.flipper.net/development/cli\");\n\n    cli_registry_unlock(registry);\n}\n\nvoid cli_command_exit(PipeSide* pipe, FuriString* args, void* context) {\n    UNUSED(pipe);\n    UNUSED(args);\n    CliShell* shell = context;\n    cli_shell_line_set_about_to_exit(shell->components[CliShellComponentLine]);\n    furi_event_loop_stop(shell->event_loop);\n}\n\n// ==================\n// Internal functions\n// ==================\n\nstatic int32_t cli_command_thread(void* context) {\n    CliCommandThreadData* thread_data = context;\n    if(!(thread_data->command->flags & CliCommandFlagDontAttachStdio))\n        pipe_install_as_stdio(thread_data->pipe);\n\n    thread_data->command->execute_callback(\n        thread_data->pipe, thread_data->args, thread_data->command->context);\n\n    fflush(stdout);\n    return 0;\n}\n\nvoid cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {\n    // split command into command and args\n    size_t space = furi_string_search_char(command, ' ');\n    if(space == FURI_STRING_FAILURE) space = furi_string_size(command);\n    FuriString* command_name = furi_string_alloc_set(command);\n    furi_string_left(command_name, space);\n    FuriString* args = furi_string_alloc_set(command);\n    furi_string_right(args, space + 1);\n\n    PluginManager* plugin_manager = NULL;\n    Loader* loader = furi_record_open(RECORD_LOADER);\n    bool loader_locked = false;\n    CliRegistryCommand command_data;\n\n    do {\n        // find handler\n        if(!cli_registry_get_command(cli_shell->registry, command_name, &command_data)) {\n            printf(\n                ANSI_FG_RED \"could not find command `%s`, try `help`\" ANSI_RESET,\n                furi_string_get_cstr(command_name));\n            break;\n        }\n\n        // load external command\n        if(command_data.flags & CliCommandFlagExternal) {\n            const CliCommandExternalConfig* ext_config = cli_shell->ext_config;\n            plugin_manager = plugin_manager_alloc(\n                ext_config->appid, CLI_PLUGIN_API_VERSION, firmware_api_interface);\n            FuriString* path = furi_string_alloc_printf(\n                \"%s/%s%s.fal\",\n                ext_config->search_directory,\n                ext_config->fal_prefix,\n                furi_string_get_cstr(command_name));\n            uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager);\n            PluginManagerError error =\n                plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path));\n            furi_string_free(path);\n\n            if(error != PluginManagerErrorNone) {\n                printf(ANSI_FG_RED \"failed to load external command\" ANSI_RESET);\n                break;\n            }\n\n            const CliCommandDescriptor* plugin =\n                plugin_manager_get_ep(plugin_manager, plugin_cnt_last);\n            furi_assert(plugin);\n            furi_check(furi_string_cmp_str(command_name, plugin->name) == 0);\n            command_data.execute_callback = plugin->execute_callback;\n            command_data.flags = plugin->flags | CliCommandFlagExternal;\n            command_data.stack_depth = plugin->stack_depth;\n\n            // external commands have to run in an external thread\n            furi_check(!(command_data.flags & CliCommandFlagUseShellThread));\n        }\n\n        // lock loader\n        if(!(command_data.flags & CliCommandFlagParallelSafe)) {\n            loader_locked = loader_lock(loader);\n            if(!loader_locked) {\n                printf(ANSI_FG_RED\n                       \"this command cannot be run while an application is open\" ANSI_RESET);\n                break;\n            }\n        }\n\n        if(command_data.flags & CliCommandFlagUseShellThread) {\n            // run command in this thread\n            command_data.execute_callback(cli_shell->pipe, args, command_data.context);\n        } else {\n            // run command in separate thread\n            cli_shell_detach_pipe(cli_shell);\n            CliCommandThreadData thread_data = {\n                .command = &command_data,\n                .pipe = cli_shell->pipe,\n                .args = args,\n            };\n            FuriThread* thread = furi_thread_alloc_ex(\n                furi_string_get_cstr(command_name),\n                command_data.stack_depth,\n                cli_command_thread,\n                &thread_data);\n            furi_thread_start(thread);\n            furi_thread_join(thread);\n            furi_thread_free(thread);\n            cli_shell_install_pipe(cli_shell);\n        }\n    } while(0);\n\n    furi_string_free(command_name);\n    furi_string_free(args);\n\n    // unlock loader\n    if(loader_locked) loader_unlock(loader);\n    furi_record_close(RECORD_LOADER);\n\n    // unload external command\n    if(plugin_manager) plugin_manager_free(plugin_manager);\n}\n\nconst char* cli_shell_get_prompt(CliShell* cli_shell) {\n    return cli_shell->prompt;\n}\n\n// ==============\n// Event handlers\n// ==============\n\nstatic void cli_shell_signal_storage_event(CliShell* cli_shell, CliShellStorageEvent event) {\n    furi_check(!(furi_event_flag_set(cli_shell->storage.event_flag, event) & FuriFlagError));\n}\n\nstatic void cli_shell_storage_event(const void* message, void* context) {\n    CliShell* cli_shell = context;\n    const StorageEvent* event = message;\n\n    if(event->type == StorageEventTypeCardMount) {\n        cli_shell_signal_storage_event(cli_shell, CliShellStorageEventMount);\n    } else if(event->type == StorageEventTypeCardUnmount) {\n        cli_shell_signal_storage_event(cli_shell, CliShellStorageEventUnmount);\n    }\n}\n\nstatic void cli_shell_storage_internal_event(FuriEventLoopObject* object, void* context) {\n    CliShell* cli_shell = context;\n    FuriEventFlag* event_flag = object;\n    CliShellStorageEvent event =\n        furi_event_flag_wait(event_flag, FuriFlagWaitAll, FuriFlagWaitAny, 0);\n    furi_check(!(event & FuriFlagError));\n\n    if(event & CliShellStorageEventUnmount) {\n        cli_registry_remove_external_commands(cli_shell->registry);\n    } else if(event & CliShellStorageEventMount) {\n        cli_registry_reload_external_commands(cli_shell->registry, cli_shell->ext_config);\n    } else {\n        furi_crash();\n    }\n}\n\nstatic void\n    cli_shell_process_parser_result(CliShell* cli_shell, CliAnsiParserResult parse_result) {\n    if(!parse_result.is_done) return;\n    CliKeyCombo key_combo = parse_result.result;\n    if(key_combo.key == CliKeyUnrecognized) return;\n\n    for(size_t i = 0; i < CliShellComponentMAX; i++) { // -V1008\n        CliShellKeyComboSet* set = component_key_combo_sets[i];\n        void* component_context = cli_shell->components[i];\n\n        for(size_t j = 0; j < set->count; j++) {\n            if(set->records[j].combo.modifiers == key_combo.modifiers &&\n               set->records[j].combo.key == key_combo.key)\n                if(set->records[j].action(key_combo, component_context)) return;\n        }\n\n        if(set->fallback)\n            if(set->fallback(key_combo, component_context)) return;\n    }\n}\n\nstatic void cli_shell_pipe_broken(PipeSide* pipe, void* context) {\n    // allow commands to be processed before we stop the shell\n    if(pipe_bytes_available(pipe)) return;\n\n    CliShell* cli_shell = context;\n    furi_event_loop_stop(cli_shell->event_loop);\n}\n\nstatic void cli_shell_data_available(PipeSide* pipe, void* context) {\n    UNUSED(pipe);\n    CliShell* cli_shell = context;\n\n    furi_event_loop_timer_start(cli_shell->ansi_parsing_timer, furi_ms_to_ticks(ANSI_TIMEOUT_MS));\n\n    // process ANSI escape sequences\n    int c = getchar();\n    furi_assert(c >= 0);\n    cli_shell_process_parser_result(cli_shell, cli_ansi_parser_feed(cli_shell->ansi_parser, c));\n}\n\nstatic void cli_shell_timer_expired(void* context) {\n    CliShell* cli_shell = context;\n    cli_shell_process_parser_result(\n        cli_shell, cli_ansi_parser_feed_timeout(cli_shell->ansi_parser));\n}\n\n// ===========\n// Thread code\n// ===========\n\nstatic void cli_shell_init(CliShell* shell) {\n    cli_registry_add_command(\n        shell->registry,\n        \"help\",\n        CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,\n        cli_command_help,\n        shell);\n    cli_registry_add_command(\n        shell->registry,\n        \"?\",\n        CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,\n        cli_command_help,\n        shell);\n    cli_registry_add_command(\n        shell->registry,\n        \"exit\",\n        CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,\n        cli_command_exit,\n        shell);\n\n    if(shell->ext_config) {\n        cli_registry_add_command(\n            shell->registry,\n            \"reload_ext_cmds\",\n            CliCommandFlagUseShellThread,\n            cli_command_reload_external,\n            shell);\n        cli_registry_reload_external_commands(shell->registry, shell->ext_config);\n    }\n\n    shell->components[CliShellComponentLine] = cli_shell_line_alloc(shell);\n    shell->components[CliShellComponentCompletions] = cli_shell_completions_alloc(\n        shell->registry, shell, shell->components[CliShellComponentLine]);\n\n    shell->ansi_parser = cli_ansi_parser_alloc();\n\n    shell->event_loop = furi_event_loop_alloc();\n    shell->ansi_parsing_timer = furi_event_loop_timer_alloc(\n        shell->event_loop, cli_shell_timer_expired, FuriEventLoopTimerTypeOnce, shell);\n\n    shell->storage.event_flag = furi_event_flag_alloc();\n    furi_event_loop_subscribe_event_flag(\n        shell->event_loop,\n        shell->storage.event_flag,\n        FuriEventLoopEventIn,\n        cli_shell_storage_internal_event,\n        shell);\n    shell->storage.storage = furi_record_open(RECORD_STORAGE);\n    shell->storage.subscription = furi_pubsub_subscribe(\n        storage_get_pubsub(shell->storage.storage), cli_shell_storage_event, shell);\n\n    cli_shell_install_pipe(shell);\n}\n\nstatic void cli_shell_deinit(CliShell* shell) {\n    furi_pubsub_unsubscribe(\n        storage_get_pubsub(shell->storage.storage), shell->storage.subscription);\n    furi_record_close(RECORD_STORAGE);\n    furi_event_loop_unsubscribe(shell->event_loop, shell->storage.event_flag);\n    furi_event_flag_free(shell->storage.event_flag);\n\n    cli_shell_completions_free(shell->components[CliShellComponentCompletions]);\n    cli_shell_line_free(shell->components[CliShellComponentLine]);\n\n    cli_shell_detach_pipe(shell);\n    furi_event_loop_timer_free(shell->ansi_parsing_timer);\n    furi_event_loop_free(shell->event_loop);\n    cli_ansi_parser_free(shell->ansi_parser);\n}\n\nstatic int32_t cli_shell_thread(void* context) {\n    CliShell* shell = context;\n\n    // Sometimes, the other side (e.g. qFlipper) closes the pipe even before our thread is started.\n    // Although the rest of the code will eventually find this out if this check is removed,\n    // there's no point in wasting time. This gives qFlipper a chance to quickly close and re-open\n    // the session.\n    const size_t delay_step = 10;\n    for(size_t i = 0; i < TRANSIENT_SESSION_WINDOW_MS / delay_step; i++) {\n        furi_delay_ms(delay_step);\n        if(pipe_state(shell->pipe) == PipeStateBroken) return 0;\n    }\n\n    cli_shell_init(shell);\n    FURI_LOG_D(TAG, \"Started\");\n\n    shell->motd(shell->callback_context);\n    cli_shell_line_prompt(shell->components[CliShellComponentLine]);\n\n    furi_event_loop_run(shell->event_loop);\n\n    FURI_LOG_D(TAG, \"Stopped\");\n    cli_shell_deinit(shell);\n    return 0;\n}\n\n// ==========\n// Public API\n// ==========\nCliShell* cli_shell_alloc(\n    CliShellMotd motd,\n    void* context,\n    PipeSide* pipe,\n    CliRegistry* registry,\n    const CliCommandExternalConfig* ext_config) {\n    furi_check(motd);\n    furi_check(pipe);\n    furi_check(registry);\n\n    CliShell* shell = malloc(sizeof(CliShell));\n    *shell = (CliShell){\n        .motd = motd,\n        .callback_context = context,\n        .pipe = pipe,\n        .registry = registry,\n        .ext_config = ext_config,\n    };\n\n    shell->thread =\n        furi_thread_alloc_ex(\"CliShell\", CLI_SHELL_STACK_SIZE, cli_shell_thread, shell);\n\n    return shell;\n}\n\nvoid cli_shell_free(CliShell* shell) {\n    furi_check(shell);\n    furi_thread_free(shell->thread);\n    free(shell);\n}\n\nvoid cli_shell_start(CliShell* shell) {\n    furi_check(shell);\n    furi_thread_start(shell->thread);\n}\n\nvoid cli_shell_join(CliShell* shell) {\n    furi_check(shell);\n    furi_thread_join(shell->thread);\n}\n\nvoid cli_shell_set_prompt(CliShell* shell, const char* prompt) {\n    furi_check(shell);\n    furi_check(furi_thread_get_state(shell->thread) == FuriThreadStateStopped);\n    shell->prompt = prompt;\n}\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <toolbox/pipe.h>\n#include \"../cli_registry.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_SHELL_STACK_SIZE (4 * 1024U)\n\ntypedef struct CliShell CliShell;\n\n/**\n * Called from the shell thread to print the Message of the Day when the shell\n * is started.\n */\ntypedef void (*CliShellMotd)(void* context);\n\n/**\n * @brief Allocates a shell\n * \n * @param [in] motd       Message of the Day callback\n * @param [in] context    Callback context\n * @param [in] pipe       Pipe side to be used by the shell\n * @param [in] registry   Command registry\n * @param [in] ext_config External command configuration. See\n *                        `CliCommandExternalConfig`. May be NULL if support for\n *                        external commands is not required.\n * \n * @return Shell instance\n */\nCliShell* cli_shell_alloc(\n    CliShellMotd motd,\n    void* context,\n    PipeSide* pipe,\n    CliRegistry* registry,\n    const CliCommandExternalConfig* ext_config);\n\n/**\n * @brief Frees a shell\n * \n * @param [in] shell Shell instance\n */\nvoid cli_shell_free(CliShell* shell);\n\n/**\n * @brief Starts a shell\n * \n * The shell runs in a separate thread. This call is non-blocking.\n * \n * @param [in] shell Shell instance\n */\nvoid cli_shell_start(CliShell* shell);\n\n/**\n * @brief Joins the shell thread\n * \n * @warning This call is blocking.\n * \n * @param [in] shell Shell instance\n */\nvoid cli_shell_join(CliShell* shell);\n\n/**\n * @brief Sets optional text before prompt (`>:`)\n * \n * @param [in] shell Shell instance\n */\nvoid cli_shell_set_prompt(CliShell* shell, const char* prompt);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell_completions.c",
    "content": "#include \"cli_shell_completions.h\"\n\nARRAY_DEF(CommandCompletions, FuriString*, FURI_STRING_OPLIST); // -V524 //-V658\n#define M_OPL_CommandCompletions_t() ARRAY_OPLIST(CommandCompletions)\n\nstruct CliShellCompletions {\n    CliRegistry* registry;\n    CliShell* shell;\n    CliShellLine* line;\n    CommandCompletions_t variants;\n    size_t selected;\n    bool is_displaying;\n};\n\n#define COMPLETION_COLUMNS        3\n#define COMPLETION_COLUMN_WIDTH   \"30\"\n#define COMPLETION_COLUMN_WIDTH_I 30\n\n/**\n * @brief Update for the completions menu\n */\ntypedef enum {\n    CliShellCompletionsActionOpen,\n    CliShellCompletionsActionClose,\n    CliShellCompletionsActionUp,\n    CliShellCompletionsActionDown,\n    CliShellCompletionsActionLeft,\n    CliShellCompletionsActionRight,\n    CliShellCompletionsActionSelect,\n    CliShellCompletionsActionSelectNoClose,\n} CliShellCompletionsAction;\n\ntypedef enum {\n    CliShellCompletionSegmentTypeCommand,\n    CliShellCompletionSegmentTypeArguments,\n} CliShellCompletionSegmentType;\n\ntypedef struct {\n    CliShellCompletionSegmentType type;\n    size_t start;\n    size_t length;\n} CliShellCompletionSegment;\n\n// ==========\n// Public API\n// ==========\n\nCliShellCompletions*\n    cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line) {\n    CliShellCompletions* completions = malloc(sizeof(CliShellCompletions));\n\n    completions->registry = registry;\n    completions->shell = shell;\n    completions->line = line;\n    CommandCompletions_init(completions->variants);\n\n    return completions;\n}\n\nvoid cli_shell_completions_free(CliShellCompletions* completions) {\n    CommandCompletions_clear(completions->variants);\n    free(completions);\n}\n\n// =======\n// Helpers\n// =======\n\nCliShellCompletionSegment cli_shell_completions_segment(CliShellCompletions* completions) {\n    furi_assert(completions);\n    CliShellCompletionSegment segment;\n\n    FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line));\n    furi_string_left(input, cli_shell_line_get_line_position(completions->line));\n\n    // find index of first non-space character\n    size_t first_non_space = 0;\n    while(1) {\n        size_t ret = furi_string_search_char(input, ' ', first_non_space);\n        if(ret == FURI_STRING_FAILURE) break;\n        if(ret - first_non_space > 1) break;\n        first_non_space++;\n    }\n\n    size_t first_space_in_command = furi_string_search_char(input, ' ', first_non_space);\n\n    if(first_space_in_command == FURI_STRING_FAILURE) {\n        segment.type = CliShellCompletionSegmentTypeCommand;\n        segment.start = first_non_space;\n        segment.length = furi_string_size(input) - first_non_space;\n    } else {\n        segment.type = CliShellCompletionSegmentTypeArguments;\n        segment.start = 0;\n        segment.length = 0;\n        // support removed, might reimplement in the future\n    }\n\n    furi_string_free(input);\n    return segment;\n}\n\nvoid cli_shell_completions_fill_variants(CliShellCompletions* completions) {\n    furi_assert(completions);\n    CommandCompletions_reset(completions->variants);\n\n    CliShellCompletionSegment segment = cli_shell_completions_segment(completions);\n    FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line));\n    furi_string_right(input, segment.start);\n    furi_string_left(input, segment.length);\n\n    if(segment.type == CliShellCompletionSegmentTypeCommand) {\n        CliRegistry* registry = completions->registry;\n        cli_registry_lock(registry);\n        CliCommandDict_t* commands = cli_registry_get_commands(registry);\n        for\n            M_EACH(registered_command, *commands, CliCommandDict_t) {\n                FuriString* command_name = registered_command->key;\n                if(furi_string_start_with(command_name, input)) {\n                    CommandCompletions_push_back(completions->variants, command_name);\n                }\n            }\n        cli_registry_unlock(registry);\n\n    } else {\n        // support removed, might reimplement in the future\n    }\n\n    furi_string_free(input);\n}\n\nstatic size_t cli_shell_completions_rows_at_column(CliShellCompletions* completions, size_t x) {\n    size_t completions_size = CommandCompletions_size(completions->variants);\n    size_t n_full_rows = completions_size / COMPLETION_COLUMNS;\n    size_t n_cols_in_last_row = completions_size % COMPLETION_COLUMNS;\n    size_t n_rows_at_x = n_full_rows + ((x >= n_cols_in_last_row) ? 0 : 1);\n    return n_rows_at_x;\n}\n\nvoid cli_shell_completions_render(\n    CliShellCompletions* completions,\n    CliShellCompletionsAction action) {\n    furi_assert(completions);\n    if(action == CliShellCompletionsActionOpen) furi_check(!completions->is_displaying);\n    if(action == CliShellCompletionsActionClose) furi_check(completions->is_displaying);\n\n    char prompt[64];\n    cli_shell_line_format_prompt(completions->line, prompt, sizeof(prompt));\n\n    if(action == CliShellCompletionsActionOpen) {\n        cli_shell_completions_fill_variants(completions);\n        completions->selected = 0;\n\n        if(CommandCompletions_size(completions->variants) == 1) {\n            cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose);\n            return;\n        }\n\n        // show completions menu (full re-render)\n        printf(\"\\n\\r\");\n        size_t position = 0;\n        for\n            M_EACH(completion, completions->variants, CommandCompletions_t) {\n                if(position == completions->selected) printf(ANSI_INVERT);\n                printf(\"%-\" COMPLETION_COLUMN_WIDTH \"s\", furi_string_get_cstr(*completion));\n                if(position == completions->selected) printf(ANSI_RESET);\n                if((position % COMPLETION_COLUMNS == COMPLETION_COLUMNS - 1) &&\n                   position != CommandCompletions_size(completions->variants)) {\n                    printf(\"\\r\\n\");\n                }\n                position++;\n            }\n\n        if(!position) {\n            printf(ANSI_FG_RED \"no completions\" ANSI_RESET);\n        }\n\n        size_t total_rows = (position / COMPLETION_COLUMNS) + 1;\n        printf(\n            ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END) ANSI_CURSOR_UP_BY(\"%zu\")\n                ANSI_CURSOR_HOR_POS(\"%zu\"),\n            total_rows,\n            strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1);\n\n        completions->is_displaying = true;\n\n    } else if(action == CliShellCompletionsActionClose) {\n        // clear completions menu\n        printf(\n            ANSI_CURSOR_HOR_POS(\"%zu\") ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END)\n                ANSI_CURSOR_HOR_POS(\"%zu\"),\n            strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) + 1,\n            strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1);\n        completions->is_displaying = false;\n\n    } else if(\n        action == CliShellCompletionsActionUp || action == CliShellCompletionsActionDown ||\n        action == CliShellCompletionsActionLeft || action == CliShellCompletionsActionRight) {\n        if(CommandCompletions_empty_p(completions->variants)) return;\n\n        // move selection\n        size_t completions_size = CommandCompletions_size(completions->variants);\n        size_t old_selection = completions->selected;\n        int n_columns = (completions_size >= COMPLETION_COLUMNS) ? COMPLETION_COLUMNS :\n                                                                   completions_size;\n        int selection_unclamped = old_selection;\n        if(action == CliShellCompletionsActionLeft) {\n            selection_unclamped--;\n        } else if(action == CliShellCompletionsActionRight) {\n            selection_unclamped++;\n        } else {\n            int selection_x = old_selection % COMPLETION_COLUMNS;\n            int selection_y_unclamped = old_selection / COMPLETION_COLUMNS;\n            if(action == CliShellCompletionsActionUp) selection_y_unclamped--;\n            if(action == CliShellCompletionsActionDown) selection_y_unclamped++;\n            size_t selection_y = 0;\n            if(selection_y_unclamped < 0) {\n                selection_x = CLAMP_WRAPAROUND(selection_x - 1, n_columns - 1, 0);\n                selection_y =\n                    cli_shell_completions_rows_at_column(completions, selection_x) - 1; // -V537\n            } else if(\n                (size_t)selection_y_unclamped >\n                cli_shell_completions_rows_at_column(completions, selection_x) - 1) {\n                selection_x = CLAMP_WRAPAROUND(selection_x + 1, n_columns - 1, 0);\n                selection_y = 0;\n            } else {\n                selection_y = selection_y_unclamped;\n            }\n            selection_unclamped = (selection_y * COMPLETION_COLUMNS) + selection_x;\n        }\n        size_t new_selection = CLAMP_WRAPAROUND(selection_unclamped, (int)completions_size - 1, 0);\n        completions->selected = new_selection;\n\n        if(new_selection != old_selection) {\n            // determine selection coordinates relative to top-left of suggestion menu\n            size_t old_x = (old_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I;\n            size_t old_y = old_selection / COMPLETION_COLUMNS;\n            size_t new_x = (new_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I;\n            size_t new_y = new_selection / COMPLETION_COLUMNS;\n            printf(\"\\n\\r\");\n\n            // print old selection in normal colors\n            if(old_y) printf(ANSI_CURSOR_DOWN_BY(\"%zu\"), old_y);\n            printf(ANSI_CURSOR_HOR_POS(\"%zu\"), old_x + 1);\n            printf(\n                \"%-\" COMPLETION_COLUMN_WIDTH \"s\",\n                furi_string_get_cstr(\n                    *CommandCompletions_cget(completions->variants, old_selection)));\n            if(old_y) printf(ANSI_CURSOR_UP_BY(\"%zu\"), old_y);\n            printf(ANSI_CURSOR_HOR_POS(\"1\"));\n\n            // print new selection in inverted colors\n            if(new_y) printf(ANSI_CURSOR_DOWN_BY(\"%zu\"), new_y);\n            printf(ANSI_CURSOR_HOR_POS(\"%zu\"), new_x + 1);\n            printf(\n                ANSI_INVERT \"%-\" COMPLETION_COLUMN_WIDTH \"s\" ANSI_RESET,\n                furi_string_get_cstr(\n                    *CommandCompletions_cget(completions->variants, new_selection)));\n\n            // return cursor\n            printf(ANSI_CURSOR_UP_BY(\"%zu\"), new_y + 1);\n            printf(\n                ANSI_CURSOR_HOR_POS(\"%zu\"),\n                strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) +\n                    1);\n        }\n\n    } else if(action == CliShellCompletionsActionSelectNoClose) {\n        if(!CommandCompletions_size(completions->variants)) return;\n        // insert selection into prompt\n        CliShellCompletionSegment segment = cli_shell_completions_segment(completions);\n        FuriString* input = cli_shell_line_get_selected(completions->line);\n        FuriString* completion =\n            *CommandCompletions_cget(completions->variants, completions->selected);\n        furi_string_replace_at(\n            input, segment.start, segment.length, furi_string_get_cstr(completion));\n        printf(\n            ANSI_CURSOR_HOR_POS(\"%zu\") \"%s\" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),\n            strlen(prompt) + 1,\n            furi_string_get_cstr(input));\n\n        int position_change = (int)furi_string_size(completion) - (int)segment.length;\n        cli_shell_line_set_line_position(\n            completions->line,\n            MAX(0, (int)cli_shell_line_get_line_position(completions->line) + position_change));\n\n    } else if(action == CliShellCompletionsActionSelect) {\n        cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose);\n        cli_shell_completions_render(completions, CliShellCompletionsActionClose);\n\n    } else {\n        furi_crash();\n    }\n\n    fflush(stdout);\n}\n\n// ==============\n// Input handlers\n// ==============\n\nstatic bool hide_if_open_and_continue_handling(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellCompletions* completions = context;\n    if(completions->is_displaying)\n        cli_shell_completions_render(completions, CliShellCompletionsActionClose);\n    return false; // process other home events\n}\n\nstatic bool key_combo_cr(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellCompletions* completions = context;\n    if(!completions->is_displaying) return false;\n    cli_shell_completions_render(completions, CliShellCompletionsActionSelect);\n    return true;\n}\n\nstatic bool key_combo_up_down(CliKeyCombo combo, void* context) {\n    CliShellCompletions* completions = context;\n    if(!completions->is_displaying) return false;\n    cli_shell_completions_render(\n        completions,\n        (combo.key == CliKeyUp) ? CliShellCompletionsActionUp : CliShellCompletionsActionDown);\n    return true;\n}\n\nstatic bool key_combo_left_right(CliKeyCombo combo, void* context) {\n    CliShellCompletions* completions = context;\n    if(!completions->is_displaying) return false;\n    cli_shell_completions_render(\n        completions,\n        (combo.key == CliKeyLeft) ? CliShellCompletionsActionLeft :\n                                    CliShellCompletionsActionRight);\n    return true;\n}\n\nstatic bool key_combo_tab(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellCompletions* completions = context;\n    cli_shell_completions_render(\n        completions,\n        completions->is_displaying ? CliShellCompletionsActionRight :\n                                     CliShellCompletionsActionOpen);\n    return true;\n}\n\nstatic bool key_combo_esc(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellCompletions* completions = context;\n    if(!completions->is_displaying) return false;\n    cli_shell_completions_render(completions, CliShellCompletionsActionClose);\n    return true;\n}\n\nCliShellKeyComboSet cli_shell_completions_key_combo_set = {\n    .fallback = hide_if_open_and_continue_handling,\n    .count = 7,\n    .records =\n        {\n            {{CliModKeyNo, CliKeyCR}, key_combo_cr},\n            {{CliModKeyNo, CliKeyUp}, key_combo_up_down},\n            {{CliModKeyNo, CliKeyDown}, key_combo_up_down},\n            {{CliModKeyNo, CliKeyLeft}, key_combo_left_right},\n            {{CliModKeyNo, CliKeyRight}, key_combo_left_right},\n            {{CliModKeyNo, CliKeyTab}, key_combo_tab},\n            {{CliModKeyNo, CliKeyEsc}, key_combo_esc},\n        },\n};\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell_completions.h",
    "content": "#pragma once\n\n#include <furi.h>\n#include <m-array.h>\n#include \"cli_shell_i.h\"\n#include \"cli_shell_line.h\"\n#include \"../cli_registry.h\"\n#include \"../cli_registry_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct CliShellCompletions CliShellCompletions;\n\nCliShellCompletions*\n    cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line);\n\nvoid cli_shell_completions_free(CliShellCompletions* completions);\n\nextern CliShellKeyComboSet cli_shell_completions_key_combo_set;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell_i.h",
    "content": "#pragma once\n\n#include \"../cli_ansi.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct CliShell CliShell;\n\n/**\n * @brief Key combo handler\n * @return true if the event was handled, false otherwise\n */\ntypedef bool (*CliShellKeyComboAction)(CliKeyCombo combo, void* context);\n\ntypedef struct {\n    CliKeyCombo combo;\n    CliShellKeyComboAction action;\n} CliShellKeyComboRecord;\n\ntypedef struct {\n    CliShellKeyComboAction fallback;\n    size_t count;\n    CliShellKeyComboRecord records[];\n} CliShellKeyComboSet;\n\nvoid cli_shell_execute_command(CliShell* cli_shell, FuriString* command);\n\nconst char* cli_shell_get_prompt(CliShell* cli_shell);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell_line.c",
    "content": "#include \"cli_shell_line.h\"\n\n#define HISTORY_DEPTH 10\n\nstruct CliShellLine {\n    size_t history_position;\n    size_t line_position;\n    FuriString* history[HISTORY_DEPTH];\n    size_t history_entries;\n    CliShell* shell;\n    bool about_to_exit;\n};\n\n// ==========\n// Public API\n// ==========\n\nCliShellLine* cli_shell_line_alloc(CliShell* shell) {\n    CliShellLine* line = malloc(sizeof(CliShellLine));\n    line->shell = shell;\n\n    line->history[0] = furi_string_alloc();\n    line->history_entries = 1;\n\n    return line;\n}\n\nvoid cli_shell_line_free(CliShellLine* line) {\n    for(size_t i = 0; i < line->history_entries; i++)\n        furi_string_free(line->history[i]);\n\n    free(line);\n}\n\nFuriString* cli_shell_line_get_selected(CliShellLine* line) {\n    return line->history[line->history_position];\n}\n\nFuriString* cli_shell_line_get_editing(CliShellLine* line) {\n    return line->history[0];\n}\n\nvoid cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length) {\n    UNUSED(line);\n    const char* prompt = cli_shell_get_prompt(line->shell);\n    snprintf(buf, length - 1, \"%s>: \", prompt ? prompt : \"\");\n}\n\nsize_t cli_shell_line_prompt_length(CliShellLine* line) {\n    char buffer[128];\n    cli_shell_line_format_prompt(line, buffer, sizeof(buffer));\n    return strlen(buffer);\n}\n\nvoid cli_shell_line_prompt(CliShellLine* line) {\n    char buffer[32];\n    cli_shell_line_format_prompt(line, buffer, sizeof(buffer));\n    printf(\"\\r\\n%s\", buffer);\n    fflush(stdout);\n}\n\nvoid cli_shell_line_ensure_not_overwriting_history(CliShellLine* line) {\n    if(line->history_position > 0) {\n        FuriString* source = cli_shell_line_get_selected(line);\n        FuriString* destination = cli_shell_line_get_editing(line);\n        furi_string_set(destination, source);\n        line->history_position = 0;\n    }\n}\n\nvoid cli_shell_line_set_about_to_exit(CliShellLine* line) {\n    line->about_to_exit = true;\n}\n\nsize_t cli_shell_line_get_line_position(CliShellLine* line) {\n    return line->line_position;\n}\n\nvoid cli_shell_line_set_line_position(CliShellLine* line, size_t position) {\n    line->line_position = position;\n}\n\n// =======\n// Helpers\n// =======\n\ntypedef enum {\n    CliCharClassWord,\n    CliCharClassSpace,\n    CliCharClassOther,\n} CliCharClass;\n\ntypedef enum {\n    CliSkipDirectionLeft,\n    CliSkipDirectionRight,\n} CliSkipDirection;\n\nCliCharClass cli_shell_line_char_class(char c) {\n    if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') {\n        return CliCharClassWord;\n    } else if(c == ' ') {\n        return CliCharClassSpace;\n    } else {\n        return CliCharClassOther;\n    }\n}\n\nsize_t\n    cli_shell_line_skip_run(FuriString* string, size_t original_pos, CliSkipDirection direction) {\n    if(furi_string_size(string) == 0) return original_pos;\n    if(direction == CliSkipDirectionLeft && original_pos == 0) return original_pos;\n    if(direction == CliSkipDirectionRight && original_pos == furi_string_size(string))\n        return original_pos;\n\n    int8_t look_offset = (direction == CliSkipDirectionLeft) ? -1 : 0;\n    int8_t increment = (direction == CliSkipDirectionLeft) ? -1 : 1;\n    int32_t position = original_pos;\n    CliCharClass start_class =\n        cli_shell_line_char_class(furi_string_get_char(string, position + look_offset));\n\n    while(true) {\n        position += increment;\n        if(position < 0) break;\n        if(position >= (int32_t)furi_string_size(string)) break;\n        if(cli_shell_line_char_class(furi_string_get_char(string, position + look_offset)) !=\n           start_class)\n            break;\n    }\n\n    return MAX(0, position);\n}\n\n// ==============\n// Input handlers\n// ==============\n\nstatic bool cli_shell_line_input_ctrl_c(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // reset input\n    furi_string_reset(cli_shell_line_get_editing(line));\n    line->line_position = 0;\n    line->history_position = 0;\n    printf(\"^C\");\n    cli_shell_line_prompt(line);\n    return true;\n}\n\nstatic bool cli_shell_line_input_cr(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n\n    FuriString* command = cli_shell_line_get_selected(line);\n    furi_string_trim(command);\n    if(furi_string_empty(command)) {\n        cli_shell_line_prompt(line);\n        return true;\n    }\n\n    FuriString* command_copy = furi_string_alloc_set(command);\n\n    if(line->history_position == 0) {\n        for(size_t i = 1; i < line->history_entries; i++) {\n            if(furi_string_cmp(line->history[i], command) == 0) {\n                line->history_position = i;\n                command = cli_shell_line_get_selected(line);\n                furi_string_trim(command);\n                break;\n            }\n        }\n    }\n\n    // move selected command to the front\n    if(line->history_position > 0) {\n        size_t pos = line->history_position;\n        size_t len = line->history_entries;\n        memmove(\n            &line->history[pos], &line->history[pos + 1], (len - pos - 1) * sizeof(FuriString*));\n        furi_string_move(line->history[0], command);\n        line->history_entries--;\n    }\n\n    // insert empty command\n    if(line->history_entries == HISTORY_DEPTH) {\n        furi_string_free(line->history[HISTORY_DEPTH - 1]);\n        line->history_entries--;\n    }\n    memmove(&line->history[1], &line->history[0], line->history_entries * sizeof(FuriString*));\n    line->history[0] = furi_string_alloc();\n    line->history_entries++;\n    line->line_position = 0;\n    line->history_position = 0;\n\n    // execute command\n    printf(\"\\r\\n\");\n    cli_shell_execute_command(line->shell, command_copy);\n    furi_string_free(command_copy);\n    if(!line->about_to_exit) cli_shell_line_prompt(line);\n    return true;\n}\n\nstatic bool cli_shell_line_input_up_down(CliKeyCombo combo, void* context) {\n    CliShellLine* line = context;\n    // go up and down in history\n    int increment = (combo.key == CliKeyUp) ? 1 : -1;\n    size_t new_pos =\n        CLAMP((int)line->history_position + increment, (int)line->history_entries - 1, 0);\n\n    // print prompt with selected command\n    if(new_pos != line->history_position) {\n        char prompt[64];\n        cli_shell_line_format_prompt(line, prompt, sizeof(prompt));\n        line->history_position = new_pos;\n        FuriString* command = cli_shell_line_get_selected(line);\n        printf(\n            ANSI_CURSOR_HOR_POS(\"1\") \"%s%s\" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),\n            prompt,\n            furi_string_get_cstr(command));\n        fflush(stdout);\n        line->line_position = furi_string_size(command);\n    }\n    return true;\n}\n\nstatic bool cli_shell_line_input_left_right(CliKeyCombo combo, void* context) {\n    CliShellLine* line = context;\n    // go left and right in the current line\n    FuriString* command = cli_shell_line_get_selected(line);\n    int increment = (combo.key == CliKeyRight) ? 1 : -1;\n    size_t new_pos =\n        CLAMP((int)line->line_position + increment, (int)furi_string_size(command), 0);\n\n    // move cursor\n    if(new_pos != line->line_position) {\n        line->line_position = new_pos;\n        printf(\"%s\", (increment == 1) ? ANSI_CURSOR_RIGHT_BY(\"1\") : ANSI_CURSOR_LEFT_BY(\"1\"));\n        fflush(stdout);\n    }\n    return true;\n}\n\nstatic bool cli_shell_line_input_home(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // go to the start\n    line->line_position = 0;\n    printf(ANSI_CURSOR_HOR_POS(\"%zu\"), cli_shell_line_prompt_length(line) + 1);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_end(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // go to the end\n    line->line_position = furi_string_size(cli_shell_line_get_selected(line));\n    printf(\n        ANSI_CURSOR_HOR_POS(\"%zu\"), cli_shell_line_prompt_length(line) + line->line_position + 1);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_bksp(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // erase one character\n    cli_shell_line_ensure_not_overwriting_history(line);\n    FuriString* editing_line = cli_shell_line_get_editing(line);\n    if(line->line_position == 0) {\n        putc(CliKeyBell, stdout);\n        fflush(stdout);\n        return true;\n    }\n    line->line_position--;\n    furi_string_replace_at(editing_line, line->line_position, 1, \"\");\n\n    // move cursor, print the rest of the line, restore cursor\n    printf(\n        ANSI_CURSOR_LEFT_BY(\"1\") \"%s\" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),\n        furi_string_get_cstr(editing_line) + line->line_position);\n    size_t left_by = furi_string_size(editing_line) - line->line_position;\n    if(left_by) // apparently LEFT_BY(\"0\") still shifts left by one ._ .\n        printf(ANSI_CURSOR_LEFT_BY(\"%zu\"), left_by);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_ctrl_l(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // clear screen\n    FuriString* command = cli_shell_line_get_selected(line);\n    char prompt[64];\n    cli_shell_line_format_prompt(line, prompt, sizeof(prompt));\n    printf(\n        ANSI_ERASE_DISPLAY(ANSI_ERASE_ENTIRE) ANSI_ERASE_SCROLLBACK_BUFFER ANSI_CURSOR_POS(\n            \"1\", \"1\") \"%s%s\" ANSI_CURSOR_HOR_POS(\"%zu\"),\n        prompt,\n        furi_string_get_cstr(command),\n        strlen(prompt) + line->line_position + 1 /* 1-based column indexing */);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_ctrl_left_right(CliKeyCombo combo, void* context) {\n    CliShellLine* line = context;\n    // skip run of similar chars to the left or right\n    FuriString* selected_line = cli_shell_line_get_selected(line);\n    CliSkipDirection direction = (combo.key == CliKeyLeft) ? CliSkipDirectionLeft :\n                                                             CliSkipDirectionRight;\n    line->line_position = cli_shell_line_skip_run(selected_line, line->line_position, direction);\n    printf(\n        ANSI_CURSOR_HOR_POS(\"%zu\"), cli_shell_line_prompt_length(line) + line->line_position + 1);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_ctrl_bksp(CliKeyCombo combo, void* context) {\n    UNUSED(combo);\n    CliShellLine* line = context;\n    // delete run of similar chars to the left\n    cli_shell_line_ensure_not_overwriting_history(line);\n    FuriString* selected_line = cli_shell_line_get_selected(line);\n    size_t run_start =\n        cli_shell_line_skip_run(selected_line, line->line_position, CliSkipDirectionLeft);\n    furi_string_replace_at(selected_line, run_start, line->line_position - run_start, \"\");\n    line->line_position = run_start;\n    printf(\n        ANSI_CURSOR_HOR_POS(\"%zu\") \"%s\" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END)\n            ANSI_CURSOR_HOR_POS(\"%zu\"),\n        cli_shell_line_prompt_length(line) + line->line_position + 1,\n        furi_string_get_cstr(selected_line) + run_start,\n        cli_shell_line_prompt_length(line) + run_start + 1);\n    fflush(stdout);\n    return true;\n}\n\nstatic bool cli_shell_line_input_normal(CliKeyCombo combo, void* context) {\n    CliShellLine* line = context;\n    if(combo.modifiers != CliModKeyNo) return false;\n    if(combo.key < CliKeySpace || combo.key >= CliKeyDEL) return false;\n    // insert character\n    cli_shell_line_ensure_not_overwriting_history(line);\n    FuriString* editing_line = cli_shell_line_get_editing(line);\n    if(line->line_position == furi_string_size(editing_line)) {\n        furi_string_push_back(editing_line, combo.key);\n        printf(\"%c\", combo.key);\n    } else {\n        const char in_str[2] = {combo.key, 0};\n        furi_string_replace_at(editing_line, line->line_position, 0, in_str);\n        printf(ANSI_INSERT_MODE_ENABLE \"%c\" ANSI_INSERT_MODE_DISABLE, combo.key);\n    }\n    fflush(stdout);\n    line->line_position++;\n    return true;\n}\n\nCliShellKeyComboSet cli_shell_line_key_combo_set = {\n    .fallback = cli_shell_line_input_normal,\n    .count = 14,\n    .records =\n        {\n            {{CliModKeyNo, CliKeyETX}, cli_shell_line_input_ctrl_c},\n            {{CliModKeyNo, CliKeyCR}, cli_shell_line_input_cr},\n            {{CliModKeyNo, CliKeyUp}, cli_shell_line_input_up_down},\n            {{CliModKeyNo, CliKeyDown}, cli_shell_line_input_up_down},\n            {{CliModKeyNo, CliKeyLeft}, cli_shell_line_input_left_right},\n            {{CliModKeyNo, CliKeyRight}, cli_shell_line_input_left_right},\n            {{CliModKeyNo, CliKeyHome}, cli_shell_line_input_home},\n            {{CliModKeyNo, CliKeyEnd}, cli_shell_line_input_end},\n            {{CliModKeyNo, CliKeyBackspace}, cli_shell_line_input_bksp},\n            {{CliModKeyNo, CliKeyDEL}, cli_shell_line_input_bksp},\n            {{CliModKeyNo, CliKeyFF}, cli_shell_line_input_ctrl_l},\n            {{CliModKeyCtrl, CliKeyLeft}, cli_shell_line_input_ctrl_left_right},\n            {{CliModKeyCtrl, CliKeyRight}, cli_shell_line_input_ctrl_left_right},\n            {{CliModKeyNo, CliKeyETB}, cli_shell_line_input_ctrl_bksp},\n        },\n};\n"
  },
  {
    "path": "lib/toolbox/cli/shell/cli_shell_line.h",
    "content": "#pragma once\n\n#include <furi.h>\n\n#include \"cli_shell_i.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct CliShellLine CliShellLine;\n\nCliShellLine* cli_shell_line_alloc(CliShell* shell);\n\nvoid cli_shell_line_free(CliShellLine* line);\n\nFuriString* cli_shell_line_get_selected(CliShellLine* line);\n\nFuriString* cli_shell_line_get_editing(CliShellLine* line);\n\nsize_t cli_shell_line_prompt_length(CliShellLine* line);\n\nvoid cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length);\n\nvoid cli_shell_line_prompt(CliShellLine* line);\n\nsize_t cli_shell_line_get_line_position(CliShellLine* line);\n\nvoid cli_shell_line_set_line_position(CliShellLine* line, size_t position);\n\n/**\n * @brief If a line from history has been selected, moves it into the active line\n */\nvoid cli_shell_line_ensure_not_overwriting_history(CliShellLine* line);\n\nvoid cli_shell_line_set_about_to_exit(CliShellLine* line);\n\nextern CliShellKeyComboSet cli_shell_line_key_combo_set;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/compress.c",
    "content": "#include \"compress.h\"\n\n#include <furi.h>\n#include <lib/heatshrink/heatshrink_encoder.h>\n#include <lib/heatshrink/heatshrink_decoder.h>\n#include <stdint.h>\n\n#define TAG \"Compress\"\n\n/** Defines encoder and decoder window size */\n#define COMPRESS_EXP_BUFF_SIZE_LOG (8u)\n\n/** Defines encoder and decoder lookahead buffer size */\n#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u)\n\n#define COMPRESS_ICON_ENCODED_BUFF_SIZE (256u)\n\nconst CompressConfigHeatshrink compress_config_heatshrink_default = {\n    .window_sz2 = COMPRESS_EXP_BUFF_SIZE_LOG,\n    .lookahead_sz2 = COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG,\n    .input_buffer_sz = COMPRESS_ICON_ENCODED_BUFF_SIZE,\n};\n\n/** Buffer size for input data */\nstatic bool compress_decode_internal(\n    heatshrink_decoder* decoder,\n    const uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size);\n\ntypedef struct {\n    uint8_t is_compressed;\n    uint8_t reserved;\n    uint16_t compressed_buff_size;\n} CompressHeader;\n\n_Static_assert(sizeof(CompressHeader) == 4, \"Incorrect CompressHeader size\");\n\nstruct CompressIcon {\n    heatshrink_decoder* decoder;\n    uint8_t* buffer;\n    size_t buffer_size;\n};\n\nCompressIcon* compress_icon_alloc(size_t decode_buf_size) {\n    CompressIcon* instance = malloc(sizeof(CompressIcon));\n    instance->decoder = heatshrink_decoder_alloc(\n        COMPRESS_ICON_ENCODED_BUFF_SIZE,\n        COMPRESS_EXP_BUFF_SIZE_LOG,\n        COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);\n    heatshrink_decoder_reset(instance->decoder);\n\n    instance->buffer_size = decode_buf_size + 4; /* To account for heatshrink's poller quirks */\n    instance->buffer = malloc(instance->buffer_size);\n\n    return instance;\n}\n\nvoid compress_icon_free(CompressIcon* instance) {\n    furi_check(instance);\n    free(instance->buffer);\n    heatshrink_decoder_free(instance->decoder);\n    free(instance);\n}\n\nvoid compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output) {\n    furi_check(instance);\n    furi_check(icon_data);\n    furi_check(output);\n\n    CompressHeader* header = (CompressHeader*)icon_data;\n    if(header->is_compressed) {\n        size_t decoded_size = 0;\n        /* If decompression fails - check that decode_buf_size is large enough */\n        furi_check(compress_decode_internal(\n            instance->decoder,\n            icon_data,\n            /* Decoder will check/process headers again - need to pass them */\n            sizeof(CompressHeader) + header->compressed_buff_size,\n            instance->buffer,\n            instance->buffer_size,\n            &decoded_size));\n        *output = instance->buffer;\n    } else {\n        *output = (uint8_t*)&icon_data[1];\n    }\n}\n\nstruct Compress {\n    const void* config;\n    heatshrink_encoder* encoder;\n    heatshrink_decoder* decoder;\n};\n\nCompress* compress_alloc(CompressType type, const void* config) {\n    furi_check(type == CompressTypeHeatshrink);\n    furi_check(config);\n\n    Compress* compress = malloc(sizeof(Compress));\n    compress->config = config;\n    compress->encoder = NULL;\n    compress->decoder = NULL;\n\n    return compress;\n}\n\nvoid compress_free(Compress* compress) {\n    furi_check(compress);\n\n    if(compress->encoder) {\n        heatshrink_encoder_free(compress->encoder);\n    }\n    if(compress->decoder) {\n        heatshrink_decoder_free(compress->decoder);\n    }\n    free(compress);\n}\n\nstatic bool compress_encode_internal(\n    heatshrink_encoder* encoder,\n    uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size) {\n    furi_check(encoder);\n    furi_check(data_in);\n    furi_check(data_in_size);\n\n    size_t sink_size = 0;\n    size_t poll_size = 0;\n    HSE_sink_res sink_res;\n    HSE_poll_res poll_res;\n    HSE_finish_res finish_res;\n    bool encode_failed = false;\n    size_t sunk = 0;\n    size_t res_buff_size = sizeof(CompressHeader);\n\n    heatshrink_encoder_reset(encoder);\n    /* Sink data to encoding buffer */\n    while((sunk < data_in_size) && !encode_failed) {\n        sink_res =\n            heatshrink_encoder_sink(encoder, &data_in[sunk], data_in_size - sunk, &sink_size);\n        if(sink_res != HSER_SINK_OK) {\n            encode_failed = true;\n            break;\n        }\n        sunk += sink_size;\n        do {\n            poll_res = heatshrink_encoder_poll(\n                encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);\n            if(poll_res < 0) {\n                encode_failed = true;\n                break;\n            }\n            res_buff_size += poll_size;\n        } while(poll_res == HSER_POLL_MORE);\n    }\n\n    /* Notify sinking complete and poll encoded data */\n    finish_res = heatshrink_encoder_finish(encoder);\n    if(finish_res < 0) {\n        encode_failed = true;\n    } else {\n        do {\n            poll_res = heatshrink_encoder_poll(\n                encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);\n            if(poll_res < 0) {\n                encode_failed = true;\n                break;\n            }\n            res_buff_size += poll_size;\n            finish_res = heatshrink_encoder_finish(encoder);\n        } while(finish_res != HSER_FINISH_DONE);\n    }\n\n    bool result = true;\n    /* Write encoded data to output buffer if compression is efficient. Otherwise, write header and original data */\n    if(!encode_failed && (res_buff_size < data_in_size + 1)) {\n        CompressHeader header = {\n            .is_compressed = 0x01,\n            .reserved = 0x00,\n            .compressed_buff_size = res_buff_size - sizeof(CompressHeader)};\n        memcpy(data_out, &header, sizeof(header));\n        *data_res_size = res_buff_size;\n    } else if(data_out_size > data_in_size) {\n        data_out[0] = 0x00;\n        memcpy(&data_out[1], data_in, data_in_size);\n        *data_res_size = data_in_size + 1;\n    } else {\n        *data_res_size = 0;\n        result = false;\n    }\n    return result;\n}\n\nstatic inline bool compress_decoder_poll(\n    heatshrink_decoder* decoder,\n    uint8_t* decompressed_chunk,\n    size_t decomp_buffer_size,\n    CompressIoCallback write_cb,\n    void* write_context) {\n    HSD_poll_res poll_res;\n    size_t poll_size;\n\n    do {\n        poll_res =\n            heatshrink_decoder_poll(decoder, decompressed_chunk, decomp_buffer_size, &poll_size);\n        if(poll_res < 0) {\n            return false;\n        }\n\n        size_t write_size = write_cb(write_context, decompressed_chunk, poll_size);\n        if(write_size != poll_size) {\n            return false;\n        }\n    } while(poll_res == HSDR_POLL_MORE);\n\n    return true;\n}\n\nstatic bool compress_decode_stream_internal(\n    heatshrink_decoder* decoder,\n    const size_t work_buffer_size,\n    CompressIoCallback read_cb,\n    void* read_context,\n    CompressIoCallback write_cb,\n    void* write_context) {\n    bool decode_failed = false;\n    HSD_sink_res sink_res;\n    HSD_finish_res finish_res;\n    size_t read_size = 0;\n    size_t sink_size = 0;\n\n    uint8_t* compressed_chunk = malloc(work_buffer_size);\n    uint8_t* decompressed_chunk = malloc(work_buffer_size);\n\n    /* Sink data to decoding buffer */\n    do {\n        read_size = read_cb(read_context, compressed_chunk, work_buffer_size);\n\n        size_t sunk = 0;\n        while(sunk < read_size && !decode_failed) {\n            sink_res = heatshrink_decoder_sink(\n                decoder, &compressed_chunk[sunk], read_size - sunk, &sink_size);\n            if(sink_res < 0) {\n                decode_failed = true;\n                break;\n            }\n            sunk += sink_size;\n\n            if(!compress_decoder_poll(\n                   decoder, decompressed_chunk, work_buffer_size, write_cb, write_context)) {\n                decode_failed = true;\n                break;\n            }\n        }\n    } while(!decode_failed && read_size);\n\n    /* Notify sinking complete and poll decoded data */\n    if(!decode_failed) {\n        while((finish_res = heatshrink_decoder_finish(decoder)) != HSDR_FINISH_DONE) {\n            if(finish_res < 0) {\n                decode_failed = true;\n                break;\n            }\n\n            if(!compress_decoder_poll(\n                   decoder, decompressed_chunk, work_buffer_size, write_cb, write_context)) {\n                decode_failed = true;\n                break;\n            }\n        }\n    }\n\n    free(compressed_chunk);\n    free(decompressed_chunk);\n\n    return !decode_failed;\n}\n\ntypedef struct {\n    uint8_t* data_ptr;\n    size_t data_size;\n    bool is_source;\n} MemoryStreamState;\n\nstatic int32_t memory_stream_io_callback(void* context, uint8_t* ptr, size_t size) {\n    MemoryStreamState* state = (MemoryStreamState*)context;\n\n    if(size > state->data_size) {\n        size = state->data_size;\n    }\n    if(state->is_source) {\n        memcpy(ptr, state->data_ptr, size);\n    } else {\n        memcpy(state->data_ptr, ptr, size);\n    }\n    state->data_ptr += size;\n    state->data_size -= size;\n    return size;\n}\n\nstatic bool compress_decode_internal(\n    heatshrink_decoder* decoder,\n    const uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size) {\n    furi_check(decoder);\n    furi_check(data_in);\n    furi_check(data_out);\n    furi_check(data_res_size);\n\n    bool result = false;\n\n    CompressHeader* header = (CompressHeader*)data_in;\n    if(header->is_compressed) {\n        MemoryStreamState compressed_context = {\n            .data_ptr = (uint8_t*)&data_in[sizeof(CompressHeader)],\n            .data_size = header->compressed_buff_size,\n            .is_source = true,\n        };\n        MemoryStreamState decompressed_context = {\n            .data_ptr = data_out,\n            .data_size = data_out_size,\n            .is_source = false,\n        };\n        heatshrink_decoder_reset(decoder);\n        if((result = compress_decode_stream_internal(\n                decoder,\n                COMPRESS_ICON_ENCODED_BUFF_SIZE,\n                memory_stream_io_callback,\n                &compressed_context,\n                memory_stream_io_callback,\n                &decompressed_context))) {\n            *data_res_size = data_out_size - decompressed_context.data_size;\n        }\n    } else if(data_out_size >= data_in_size - 1) {\n        memcpy(data_out, &data_in[1], data_in_size);\n        *data_res_size = data_in_size - 1;\n        result = true;\n    } else {\n        /* Not enough space in output buffer */\n        result = false;\n    }\n    return result;\n}\n\nbool compress_encode(\n    Compress* compress,\n    uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size) {\n    if(!compress->encoder) {\n        CompressConfigHeatshrink* hs_config = (CompressConfigHeatshrink*)compress->config;\n        compress->encoder =\n            heatshrink_encoder_alloc(hs_config->window_sz2, hs_config->lookahead_sz2);\n    }\n    return compress_encode_internal(\n        compress->encoder, data_in, data_in_size, data_out, data_out_size, data_res_size);\n}\n\nbool compress_decode(\n    Compress* compress,\n    uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size) {\n    if(!compress->decoder) {\n        CompressConfigHeatshrink* hs_config = (CompressConfigHeatshrink*)compress->config;\n        compress->decoder = heatshrink_decoder_alloc(\n            hs_config->input_buffer_sz, hs_config->window_sz2, hs_config->lookahead_sz2);\n    }\n    return compress_decode_internal(\n        compress->decoder, data_in, data_in_size, data_out, data_out_size, data_res_size);\n}\n\nbool compress_decode_streamed(\n    Compress* compress,\n    CompressIoCallback read_cb,\n    void* read_context,\n    CompressIoCallback write_cb,\n    void* write_context) {\n    CompressConfigHeatshrink* hs_config = (CompressConfigHeatshrink*)compress->config;\n    if(!compress->decoder) {\n        compress->decoder = heatshrink_decoder_alloc(\n            hs_config->input_buffer_sz, hs_config->window_sz2, hs_config->lookahead_sz2);\n    }\n\n    heatshrink_decoder_reset(compress->decoder);\n    return compress_decode_stream_internal(\n        compress->decoder,\n        hs_config->input_buffer_sz,\n        read_cb,\n        read_context,\n        write_cb,\n        write_context);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstruct CompressStreamDecoder {\n    heatshrink_decoder* decoder;\n    size_t stream_position;\n    size_t decode_buffer_size;\n    size_t decode_buffer_position;\n    uint8_t* decode_buffer;\n    CompressIoCallback read_cb;\n    void* read_context;\n};\n\nCompressStreamDecoder* compress_stream_decoder_alloc(\n    CompressType type,\n    const void* config,\n    CompressIoCallback read_cb,\n    void* read_context) {\n    furi_check(type == CompressTypeHeatshrink);\n    furi_check(config);\n\n    const CompressConfigHeatshrink* hs_config = (const CompressConfigHeatshrink*)config;\n    CompressStreamDecoder* instance = malloc(sizeof(CompressStreamDecoder));\n    instance->decoder = heatshrink_decoder_alloc(\n        hs_config->input_buffer_sz, hs_config->window_sz2, hs_config->lookahead_sz2);\n    instance->stream_position = 0;\n    instance->decode_buffer_size = hs_config->input_buffer_sz;\n    instance->decode_buffer_position = 0;\n    instance->decode_buffer = malloc(hs_config->input_buffer_sz);\n    instance->read_cb = read_cb;\n    instance->read_context = read_context;\n\n    return instance;\n}\n\nvoid compress_stream_decoder_free(CompressStreamDecoder* instance) {\n    furi_check(instance);\n    heatshrink_decoder_free(instance->decoder);\n    free(instance->decode_buffer);\n    free(instance);\n}\n\nstatic bool compress_decode_stream_chunk(\n    CompressStreamDecoder* sd,\n    CompressIoCallback read_cb,\n    void* read_context,\n    uint8_t* decompressed_chunk,\n    size_t decomp_chunk_size) {\n    HSD_sink_res sink_res;\n    HSD_poll_res poll_res;\n\n    /* \n    First, try to output data from decoder to the output buffer. \n    If the we could fill the output buffer, return\n    If the output buffer is not full, keep polling the decoder \n        until it has no more data to output.\n    Then, read more data from the input and sink it to the decoder.\n    Repeat until the input is exhausted or output buffer is full.\n    */\n\n    bool failed = false;\n    bool can_sink_more = true;\n    bool can_read_more = true;\n\n    do {\n        do {\n            size_t poll_size = 0;\n            poll_res = heatshrink_decoder_poll(\n                sd->decoder, decompressed_chunk, decomp_chunk_size, &poll_size);\n            if(poll_res < 0) {\n                return false;\n            }\n\n            decomp_chunk_size -= poll_size;\n            decompressed_chunk += poll_size;\n        } while((poll_res == HSDR_POLL_MORE) && decomp_chunk_size);\n\n        if(!decomp_chunk_size) {\n            break;\n        }\n\n        if(can_read_more && (sd->decode_buffer_position < sd->decode_buffer_size)) {\n            size_t read_size = read_cb(\n                read_context,\n                &sd->decode_buffer[sd->decode_buffer_position],\n                sd->decode_buffer_size - sd->decode_buffer_position);\n            sd->decode_buffer_position += read_size;\n            can_read_more = read_size > 0;\n        }\n\n        while(sd->decode_buffer_position && can_sink_more) {\n            size_t sink_size = 0;\n            sink_res = heatshrink_decoder_sink(\n                sd->decoder, sd->decode_buffer, sd->decode_buffer_position, &sink_size);\n            can_sink_more = sink_res == HSDR_SINK_OK;\n            if(sink_res < 0) {\n                failed = true;\n                break;\n            }\n            sd->decode_buffer_position -= sink_size;\n\n            /* If some data was left in the buffer, move it to the beginning */\n            if(sink_size && sd->decode_buffer_position) {\n                memmove(\n                    sd->decode_buffer, &sd->decode_buffer[sink_size], sd->decode_buffer_position);\n            }\n        }\n    } while(!failed);\n\n    return decomp_chunk_size == 0;\n}\n\nbool compress_stream_decoder_read(\n    CompressStreamDecoder* instance,\n    uint8_t* data_out,\n    size_t data_out_size) {\n    furi_check(instance);\n    furi_check(data_out);\n\n    if(compress_decode_stream_chunk(\n           instance, instance->read_cb, instance->read_context, data_out, data_out_size)) {\n        instance->stream_position += data_out_size;\n        return true;\n    }\n    return false;\n}\n\nbool compress_stream_decoder_seek(CompressStreamDecoder* instance, size_t position) {\n    furi_check(instance);\n\n    /* Check if requested position is ahead of current position \n       we can't rewind the input stream */\n    furi_check(position >= instance->stream_position);\n\n    /* Read and discard data up to requested position */\n    uint8_t* dummy_buffer = malloc(instance->decode_buffer_size);\n    bool success = true;\n\n    while(instance->stream_position < position) {\n        size_t bytes_to_read = position - instance->stream_position;\n        if(bytes_to_read > instance->decode_buffer_size) {\n            bytes_to_read = instance->decode_buffer_size;\n        }\n        if(!compress_stream_decoder_read(instance, dummy_buffer, bytes_to_read)) {\n            success = false;\n            break;\n        }\n    }\n\n    free(dummy_buffer);\n    return success;\n}\n\nsize_t compress_stream_decoder_tell(CompressStreamDecoder* instance) {\n    furi_check(instance);\n    return instance->stream_position;\n}\n\nbool compress_stream_decoder_rewind(CompressStreamDecoder* instance) {\n    furi_check(instance);\n\n    /* Reset decoder and read buffer */\n    heatshrink_decoder_reset(instance->decoder);\n    instance->stream_position = 0;\n    instance->decode_buffer_position = 0;\n\n    return true;\n}\n"
  },
  {
    "path": "lib/toolbox/compress.h",
    "content": "/**\n * @file compress.h\n * LZSS based compression HAL API\n */\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Compress Icon control structure */\ntypedef struct CompressIcon CompressIcon;\n\n/** Initialize icon compressor\n *\n * @param[in]  decode_buf_size  The icon buffer size for decoding. Ensure that\n *                              it's big enough for any icons that you are\n *                              planning to decode with it.\n *\n * @return     Compress Icon instance\n */\nCompressIcon* compress_icon_alloc(size_t decode_buf_size);\n\n/** Free icon compressor\n *\n * @param      instance  The Compress Icon instance\n */\nvoid compress_icon_free(CompressIcon* instance);\n\n/** Decompress icon\n *\n * @warning    output pointer set by this function is valid till next\n *             `compress_icon_decode` or `compress_icon_free` call\n *\n * @param      instance   The Compress Icon instance\n * @param      icon_data  pointer to icon data.\n * @param[in]  output     pointer to decoded buffer pointer. Data in buffer is\n *                        valid till next call. If icon data was not compressed,\n *                        pointer within icon_data is returned\n */\nvoid compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output);\n\n//////////////////////////////////////////////////////////////////////////\n\n/** Compress control structure */\ntypedef struct Compress Compress;\n\n/** Supported compression types */\ntypedef enum {\n    CompressTypeHeatshrink = 0,\n} CompressType;\n\n/** Configuration for heatshrink compression */\ntypedef struct {\n    uint16_t window_sz2;\n    uint16_t lookahead_sz2;\n    uint16_t input_buffer_sz;\n} CompressConfigHeatshrink;\n\n/** Default configuration for heatshrink compression. Used for image assets. */\nextern const CompressConfigHeatshrink compress_config_heatshrink_default;\n\n/** Allocate encoder and decoder\n *\n * @param      type     Compression type\n * @param[in]  config   Configuration for compression, specific to type\n *\n * @return     Compress instance\n */\nCompress* compress_alloc(CompressType type, const void* config);\n\n/** Free encoder and decoder\n *\n * @param      compress  Compress instance\n */\nvoid compress_free(Compress* compress);\n\n/** Encode data\n *\n * @param      compress       Compress instance\n * @param      data_in        pointer to input data\n * @param      data_in_size   size of input data\n * @param      data_out       maximum size of output data\n * @param[in]  data_out_size  The data out size\n * @param      data_res_size  pointer to result output data size\n *\n * @note       Prepends compressed stream with a header. If data is not compressible,\n *             it will be stored as is after the header.\n * @return     true on success\n */\nbool compress_encode(\n    Compress* compress,\n    uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size);\n\n/** Decode data\n *\n * @param      compress       Compress instance\n * @param      data_in        pointer to input data\n * @param      data_in_size   size of input data\n * @param      data_out       maximum size of output data\n * @param[in]  data_out_size  The data out size\n * @param      data_res_size  pointer to result output data size\n *\n * @note       Expects compressed stream with a header, as produced by `compress_encode`.\n * @return     true on success\n */\nbool compress_decode(\n    Compress* compress,\n    uint8_t* data_in,\n    size_t data_in_size,\n    uint8_t* data_out,\n    size_t data_out_size,\n    size_t* data_res_size);\n\n/** I/O callback for streamed compression/decompression\n * \n * @param context user context\n * @param buffer buffer to read/write\n * @param size size of buffer\n * \n * @return number of bytes read/written, 0 on end of stream, negative on error\n */\ntypedef int32_t (*CompressIoCallback)(void* context, uint8_t* buffer, size_t size);\n\n/** Decompress streamed data\n *\n * @param      compress       Compress instance\n * @param      read_cb        read callback\n * @param      read_context   read callback context\n * @param      write_cb       write callback\n * @param      write_context  write callback context\n *\n * @note       Does not expect a header, just compressed data stream.\n * @return     true on success\n */\nbool compress_decode_streamed(\n    Compress* compress,\n    CompressIoCallback read_cb,\n    void* read_context,\n    CompressIoCallback write_cb,\n    void* write_context);\n\n//////////////////////////////////////////////////////////////////////////\n\n/** CompressStreamDecoder control structure */\ntypedef struct CompressStreamDecoder CompressStreamDecoder;\n\n/** Allocate stream decoder\n *\n * @param      type          Compression type\n * @param[in]  config        Configuration for compression, specific to type\n * @param      read_cb       The read callback for input (compressed) data\n * @param      read_context  The read context\n *\n * @return     CompressStreamDecoder instance\n */\nCompressStreamDecoder* compress_stream_decoder_alloc(\n    CompressType type,\n    const void* config,\n    CompressIoCallback read_cb,\n    void* read_context);\n\n/** Free stream decoder\n *\n * @param      instance  The CompressStreamDecoder instance\n */\nvoid compress_stream_decoder_free(CompressStreamDecoder* instance);\n\n/** Read uncompressed data chunk from stream decoder\n *\n * @param      instance       The CompressStreamDecoder instance\n * @param      data_out       The data out\n * @param[in]  data_out_size  The data out size\n *\n * @return     true on success\n */\nbool compress_stream_decoder_read(\n    CompressStreamDecoder* instance,\n    uint8_t* data_out,\n    size_t data_out_size);\n\n/** Seek to position in uncompressed data stream\n *\n * @param      instance   The CompressStreamDecoder instance\n * @param[in]  position   The position\n * \n * @return     true on success\n * @warning    Backward seeking is not supported\n */\nbool compress_stream_decoder_seek(CompressStreamDecoder* instance, size_t position);\n\n/** Get current position in uncompressed data stream\n *\n * @param      instance  The CompressStreamDecoder instance\n *\n * @return     current position\n */\nsize_t compress_stream_decoder_tell(CompressStreamDecoder* instance);\n\n/** Reset stream decoder to the beginning\n * @warning    Read callback must be repositioned by caller separately\n *\n * @param      instance  The CompressStreamDecoder instance\n *\n * @return     true on success\n */\nbool compress_stream_decoder_rewind(CompressStreamDecoder* instance);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/crc32_calc.c",
    "content": "#include \"crc32_calc.h\"\n\n#define CRC_DATA_BUFFER_MAX_LEN 512\n\nuint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size) {\n    crc = ~crc;\n\n    static const uint32_t rtable[16] = {\n        0x00000000,\n        0x1db71064,\n        0x3b6e20c8,\n        0x26d930ac,\n        0x76dc4190,\n        0x6b6b51f4,\n        0x4db26158,\n        0x5005713c,\n        0xedb88320,\n        0xf00f9344,\n        0xd6d6a3e8,\n        0xcb61b38c,\n        0x9b64c2b0,\n        0x86d3d2d4,\n        0xa00ae278,\n        0xbdbdf21c,\n    };\n\n    const uint8_t* data = buffer;\n\n    for(size_t i = 0; i < size; i++) {\n        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];\n        crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];\n    }\n\n    return ~crc;\n}\n\nuint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* context) {\n    furi_check(storage_file_is_open(file) && storage_file_seek(file, 0, true));\n\n    uint32_t file_crc = 0;\n\n    uint8_t* data_buffer = malloc(CRC_DATA_BUFFER_MAX_LEN);\n    size_t data_buffer_valid_len;\n\n    uint32_t file_size = storage_file_size(file);\n\n    /* Feed file contents per sector into CRC calc */\n    for(uint32_t fptr = 0; fptr < file_size;) {\n        data_buffer_valid_len = storage_file_read(file, data_buffer, CRC_DATA_BUFFER_MAX_LEN);\n        if(data_buffer_valid_len == 0) {\n            break;\n        }\n        fptr += data_buffer_valid_len;\n\n        if(progress_cb && (fptr % CRC_DATA_BUFFER_MAX_LEN == 0)) {\n            progress_cb(fptr * 100 / file_size, context);\n        }\n\n        file_crc = crc32_calc_buffer(file_crc, data_buffer, data_buffer_valid_len);\n    }\n    free(data_buffer);\n\n    return file_crc;\n}\n"
  },
  {
    "path": "lib/toolbox/crc32_calc.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nuint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size);\n\ntypedef void (*FileCrcProgressCb)(const uint8_t progress, void* context);\n\nuint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* context);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/dir_walk.c",
    "content": "#include \"dir_walk.h\"\n#include <m-list.h>\n\nLIST_DEF(DirIndexList, uint32_t);\n\nstruct DirWalk {\n    File* file;\n    FuriString* path;\n    DirIndexList_t index_list;\n    uint32_t current_index;\n    bool recursive;\n    DirWalkFilterCb filter_cb;\n    void* filter_context;\n};\n\nDirWalk* dir_walk_alloc(Storage* storage) {\n    furi_check(storage);\n\n    DirWalk* dir_walk = malloc(sizeof(DirWalk));\n    dir_walk->path = furi_string_alloc();\n    dir_walk->file = storage_file_alloc(storage);\n    DirIndexList_init(dir_walk->index_list);\n    dir_walk->recursive = true;\n    dir_walk->filter_cb = NULL;\n    return dir_walk;\n}\n\nvoid dir_walk_free(DirWalk* dir_walk) {\n    furi_check(dir_walk);\n\n    storage_file_free(dir_walk->file);\n    furi_string_free(dir_walk->path);\n    DirIndexList_clear(dir_walk->index_list);\n    free(dir_walk);\n}\n\nvoid dir_walk_set_recursive(DirWalk* dir_walk, bool recursive) {\n    furi_check(dir_walk);\n    dir_walk->recursive = recursive;\n}\n\nvoid dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context) {\n    furi_check(dir_walk);\n    dir_walk->filter_cb = cb;\n    dir_walk->filter_context = context;\n}\n\nbool dir_walk_open(DirWalk* dir_walk, const char* path) {\n    furi_check(dir_walk);\n    furi_string_set(dir_walk->path, path);\n    dir_walk->current_index = 0;\n    return storage_dir_open(dir_walk->file, path);\n}\n\nstatic bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* fileinfo) {\n    if(dir_walk->filter_cb) {\n        return dir_walk->filter_cb(name, fileinfo, dir_walk->filter_context);\n    } else {\n        return true;\n    }\n}\n\nstatic DirWalkResult\n    dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) {\n    DirWalkResult result = DirWalkError;\n    char* name = malloc(256); // FIXME: remove magic number\n    FileInfo info;\n    bool end = false;\n\n    while(!end) {\n        storage_dir_read(dir_walk->file, &info, name, 255);\n\n        if(storage_file_get_error(dir_walk->file) == FSE_OK) {\n            result = DirWalkOK;\n            dir_walk->current_index++;\n\n            if(dir_walk_filter(dir_walk, name, &info)) {\n                if(return_path != NULL) {\n                    furi_string_printf( //-V576\n                        return_path,\n                        \"%s/%s\",\n                        furi_string_get_cstr(dir_walk->path),\n                        name);\n                }\n\n                if(fileinfo != NULL) {\n                    memcpy(fileinfo, &info, sizeof(FileInfo));\n                }\n\n                end = true;\n            }\n\n            if(file_info_is_dir(&info) && dir_walk->recursive) {\n                // step into\n                DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index);\n                dir_walk->current_index = 0;\n                storage_dir_close(dir_walk->file);\n\n                furi_string_cat_printf(dir_walk->path, \"/%s\", name);\n                storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path));\n            }\n        } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) {\n            if(DirIndexList_size(dir_walk->index_list) == 0) {\n                // last\n                result = DirWalkLast;\n                end = true;\n            } else {\n                // step out\n                uint32_t index;\n                DirIndexList_pop_back(&index, dir_walk->index_list);\n                dir_walk->current_index = 0;\n\n                storage_dir_close(dir_walk->file);\n\n                size_t last_char = furi_string_search_rchar(dir_walk->path, '/');\n                if(last_char != FURI_STRING_FAILURE) {\n                    furi_string_left(dir_walk->path, last_char);\n                }\n\n                storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path));\n\n                // rewind\n                while(true) {\n                    if(index == dir_walk->current_index) {\n                        result = DirWalkOK;\n                        break;\n                    }\n\n                    if(!storage_dir_read(dir_walk->file, &info, name, 255)) {\n                        result = DirWalkError;\n                        end = true;\n                        break;\n                    }\n\n                    dir_walk->current_index++;\n                }\n            }\n        } else {\n            result = DirWalkError;\n            end = true;\n        }\n    }\n\n    free(name);\n    return result;\n}\n\nFS_Error dir_walk_get_error(DirWalk* dir_walk) {\n    furi_check(dir_walk);\n    return storage_file_get_error(dir_walk->file);\n}\n\nDirWalkResult dir_walk_read(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) {\n    furi_check(dir_walk);\n    return dir_walk_iter(dir_walk, return_path, fileinfo);\n}\n\nvoid dir_walk_close(DirWalk* dir_walk) {\n    furi_check(dir_walk);\n    if(storage_file_is_open(dir_walk->file)) {\n        storage_dir_close(dir_walk->file);\n    }\n\n    DirIndexList_reset(dir_walk->index_list);\n    furi_string_reset(dir_walk->path);\n    dir_walk->current_index = 0;\n}\n"
  },
  {
    "path": "lib/toolbox/dir_walk.h",
    "content": "#pragma once\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct DirWalk DirWalk;\n\ntypedef enum {\n    DirWalkOK, /**< OK */\n    DirWalkError, /**< Error */\n    DirWalkLast, /**< Last element */\n} DirWalkResult;\n\ntypedef bool (*DirWalkFilterCb)(const char* name, FileInfo* fileinfo, void* ctx);\n\n/**\n * Allocate DirWalk\n * @param storage \n * @return DirWalk* \n */\nDirWalk* dir_walk_alloc(Storage* storage);\n\n/**\n * Free DirWalk\n * @param dir_walk \n */\nvoid dir_walk_free(DirWalk* dir_walk);\n\n/**\n * Set recursive mode (true by default)\n * @param dir_walk \n * @param recursive \n */\nvoid dir_walk_set_recursive(DirWalk* dir_walk, bool recursive);\n\n/**\n * Set filter callback (Should return true if the data is valid)\n * @param dir_walk \n * @param cb \n * @param context \n */\nvoid dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context);\n\n/**\n * Open directory \n * @param dir_walk \n * @param path \n * @return true \n * @return false \n */\nbool dir_walk_open(DirWalk* dir_walk, const char* path);\n\n/**\n * Get error id\n * @param dir_walk \n * @return FS_Error \n */\nFS_Error dir_walk_get_error(DirWalk* dir_walk);\n\n/**\n * Read next element from directory\n * @param dir_walk \n * @param return_path \n * @param fileinfo \n * @return DirWalkResult \n */\nDirWalkResult dir_walk_read(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo);\n\n/**\n * Close directory\n * @param dir_walk \n */\nvoid dir_walk_close(DirWalk* dir_walk);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/float_tools.c",
    "content": "#include \"float_tools.h\"\n\n#include <math.h>\n#include <float.h>\n\nbool float_is_equal(float a, float b) {\n    return fabsf(a - b) <= FLT_EPSILON * fmaxf(fabsf(a), fabsf(b));\n}\n"
  },
  {
    "path": "lib/toolbox/float_tools.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdbool.h>\n\n/** Compare two floating point numbers\n * @param a         First number to compare\n * @param b         Second number to compare\n *\n * @return          bool true if a equals b, false otherwise\n */\nbool float_is_equal(float a, float b);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/hex.c",
    "content": "#include \"hex.h\"\n#include <furi.h>\n\nbool hex_char_to_hex_nibble(char c, uint8_t* nibble) {\n    furi_check(nibble);\n\n    if(c >= '0' && c <= '9') {\n        *nibble = c - '0';\n        return true;\n    } else if(c >= 'A' && c <= 'F') {\n        *nibble = c - 'A' + 10;\n        return true;\n    } else if(c >= 'a' && c <= 'f') {\n        *nibble = c - 'a' + 10;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool hex_char_to_uint8(char hi, char low, uint8_t* value) {\n    furi_check(value);\n\n    uint8_t hi_nibble_value, low_nibble_value;\n\n    if(hex_char_to_hex_nibble(hi, &hi_nibble_value) &&\n       hex_char_to_hex_nibble(low, &low_nibble_value)) {\n        *value = (hi_nibble_value << 4) | low_nibble_value;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool hex_chars_to_uint8(const char* value_str, uint8_t* value) {\n    furi_check(value_str);\n    furi_check(value);\n\n    bool parse_success = false;\n    while(*value_str && value_str[1]) {\n        parse_success = hex_char_to_uint8(*value_str, value_str[1], value++);\n        if(!parse_success) break;\n        value_str += 2;\n    }\n    return parse_success;\n}\n\nbool hex_chars_to_uint64(const char* value_str, uint64_t* value) {\n    furi_check(value_str);\n    furi_check(value);\n\n    uint8_t* _value = (uint8_t*)value;\n    bool parse_success = false;\n\n    for(uint8_t i = 0; i < 8; i++) {\n        parse_success = hex_char_to_uint8(value_str[i * 2], value_str[i * 2 + 1], &_value[7 - i]);\n        if(!parse_success) break;\n    }\n\n    return parse_success;\n}\n\nvoid uint8_to_hex_chars(const uint8_t* src, uint8_t* target, int length) {\n    furi_check(src);\n    furi_check(target);\n\n    const char chars[] = \"0123456789ABCDEF\";\n    while(--length >= 0)\n        target[length] = chars[(src[length >> 1] >> ((1 - (length & 1)) << 2)) & 0xF];\n}\n"
  },
  {
    "path": "lib/toolbox/hex.h",
    "content": "#pragma once\n#include \"stdint.h\"\n#include \"stdbool.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Convert ASCII hex value to nibble\n * @param c         ASCII character\n * @param nibble    nibble pointer, output\n *\n * @return          bool conversion status\n */\nbool hex_char_to_hex_nibble(char c, uint8_t* nibble);\n\n/** Convert ASCII hex value to byte\n * @param hi        hi nibble text\n * @param low       low nibble text\n * @param value     output value\n *\n * @return          bool conversion status\n */\nbool hex_char_to_uint8(char hi, char low, uint8_t* value);\n\n/** Convert ASCII hex values to uint8_t\n * @param value_str ASCII data\n * @param value     output value\n *\n * @return          bool conversion status\n */\nbool hex_chars_to_uint8(const char* value_str, uint8_t* value);\n\n/** Convert ASCII hex values to uint64_t\n * @param value_str ASCII 64 bi data\n * @param value     output value\n *\n * @return          bool conversion status\n */\nbool hex_chars_to_uint64(const char* value_str, uint64_t* value);\n\n/** Convert uint8_t to ASCII hex values\n * @param src       source data\n * @param target    output value\n * @param length    data length\n * \n */\nvoid uint8_to_hex_chars(const uint8_t* src, uint8_t* target, int length);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/keys_dict.c",
    "content": "#include \"keys_dict.h\"\n\n#include <storage/storage.h>\n#include <flipper_format/flipper_format.h>\n#include <toolbox/stream/file_stream.h>\n#include <toolbox/stream/buffered_file_stream.h>\n#include <toolbox/args.h>\n\n#define TAG \"KeysDict\"\n\nstruct KeysDict {\n    Stream* stream;\n    size_t key_size;\n    size_t key_size_symbols;\n    size_t total_keys;\n};\n\nstatic inline void keys_dict_add_ending_new_line(KeysDict* instance) {\n    if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) {\n        uint8_t last_char = 0;\n\n        // Check if the last char is new line or add a new line\n        if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\\n') {\n            FURI_LOG_D(TAG, \"Adding new line ending\");\n            stream_write_char(instance->stream, '\\n');\n        }\n\n        stream_rewind(instance->stream);\n    }\n}\n\nstatic bool keys_dict_read_key_line(KeysDict* instance, FuriString* line, bool* is_endfile) {\n    if(stream_read_line(instance->stream, line) == false) {\n        *is_endfile = true;\n    }\n\n    else {\n        FURI_LOG_T(\n            TAG, \"Read line: %s, len: %zu\", furi_string_get_cstr(line), furi_string_size(line));\n\n        bool is_comment = furi_string_get_char(line, 0) == '#';\n\n        if(!is_comment) {\n            furi_string_left(line, instance->key_size_symbols - 1);\n        }\n\n        bool is_correct_size = furi_string_size(line) == instance->key_size_symbols - 1;\n\n        return !is_comment && is_correct_size;\n    }\n\n    return false;\n}\n\nbool keys_dict_check_presence(const char* path) {\n    furi_check(path);\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n\n    bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK;\n\n    furi_record_close(RECORD_STORAGE);\n\n    return dict_present;\n}\n\nKeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size) {\n    furi_check(path);\n    furi_check(key_size > 0);\n\n    KeysDict* instance = malloc(sizeof(KeysDict));\n\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    instance->stream = buffered_file_stream_alloc(storage);\n\n    FS_OpenMode open_mode = (mode == KeysDictModeOpenAlways) ? FSOM_OPEN_ALWAYS :\n                                                               FSOM_OPEN_EXISTING;\n\n    // Byte = 2 symbols + 1 end of line\n    instance->key_size = key_size;\n    instance->key_size_symbols = key_size * 2 + 1;\n\n    instance->total_keys = 0;\n\n    bool file_exists =\n        buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode);\n\n    if(!file_exists) {\n        buffered_file_stream_close(instance->stream);\n    } else {\n        // Eventually add new line character in the last line to avoid skipping keys\n        keys_dict_add_ending_new_line(instance);\n    }\n\n    FuriString* line = furi_string_alloc();\n\n    bool is_endfile = false;\n\n    // In this loop we only count the entries in the file\n    // We prefer not to load the whole file in memory for space reasons\n    while(file_exists && !is_endfile) {\n        bool read_key = keys_dict_read_key_line(instance, line, &is_endfile);\n        if(read_key) {\n            instance->total_keys++;\n        }\n    }\n    stream_rewind(instance->stream);\n    FURI_LOG_I(TAG, \"Loaded dictionary with %zu keys\", instance->total_keys);\n\n    furi_string_free(line);\n\n    return instance;\n}\n\nvoid keys_dict_free(KeysDict* instance) {\n    furi_check(instance);\n    furi_check(instance->stream);\n\n    buffered_file_stream_close(instance->stream);\n    stream_free(instance->stream);\n    free(instance);\n\n    furi_record_close(RECORD_STORAGE);\n}\n\nstatic void keys_dict_int_to_str(KeysDict* instance, const uint8_t* key_int, FuriString* key_str) {\n    furi_assert(instance);\n    furi_assert(key_str);\n    furi_assert(key_int);\n\n    furi_string_reset(key_str);\n\n    for(size_t i = 0; i < instance->key_size; i++)\n        furi_string_cat_printf(key_str, \"%02X\", key_int[i]);\n}\n\nstatic void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint8_t* key_out) {\n    furi_assert(instance);\n    furi_assert(key_str);\n    furi_assert(key_out);\n\n    uint8_t key_byte_tmp;\n    char h, l;\n\n    // Process two hex characters at a time to create each byte\n    for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) {\n        h = furi_string_get_char(key_str, i);\n        l = furi_string_get_char(key_str, i + 1);\n\n        args_char_to_hex(h, l, &key_byte_tmp);\n        key_out[i / 2] = key_byte_tmp;\n    }\n}\n\nsize_t keys_dict_get_total_keys(KeysDict* instance) {\n    furi_check(instance);\n\n    return instance->total_keys;\n}\n\nbool keys_dict_rewind(KeysDict* instance) {\n    furi_check(instance);\n    furi_check(instance->stream);\n\n    return stream_rewind(instance->stream);\n}\n\nstatic bool keys_dict_get_next_key_str(KeysDict* instance, FuriString* key) {\n    furi_assert(instance);\n    furi_assert(instance->stream);\n    furi_assert(key);\n\n    bool key_read = false;\n    bool is_endfile = false;\n\n    furi_string_reset(key);\n\n    while(!key_read && !is_endfile)\n        key_read = keys_dict_read_key_line(instance, key, &is_endfile);\n\n    return key_read;\n}\n\nbool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size) {\n    furi_check(instance);\n    furi_check(instance->stream);\n    furi_check(instance->key_size == key_size);\n    furi_check(key);\n\n    FuriString* temp_key = furi_string_alloc();\n\n    bool key_read = keys_dict_get_next_key_str(instance, temp_key);\n\n    if(key_read) {\n        keys_dict_str_to_int(instance, temp_key, key);\n    }\n\n    furi_string_free(temp_key);\n    return key_read;\n}\n\nstatic bool keys_dict_is_key_present_str(KeysDict* instance, FuriString* key) {\n    furi_assert(instance);\n    furi_assert(instance->stream);\n    furi_assert(key);\n\n    FuriString* line = furi_string_alloc();\n\n    bool is_endfile = false;\n    bool line_found = false;\n\n    uint32_t actual_pos = stream_tell(instance->stream);\n    stream_rewind(instance->stream);\n\n    while(!line_found && !is_endfile)\n        line_found = // The line is found if the line was read and the key is equal to the line\n            (keys_dict_read_key_line(instance, line, &is_endfile)) &&\n            (furi_string_equal(key, line));\n\n    furi_string_free(line);\n\n    // Restore the position of the stream\n    stream_seek(instance->stream, actual_pos, StreamOffsetFromStart);\n\n    return line_found;\n}\n\nbool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size) {\n    furi_check(instance);\n    furi_check(instance->stream);\n    furi_check(instance->key_size == key_size);\n    furi_check(key);\n\n    FuriString* temp_key = furi_string_alloc();\n\n    keys_dict_int_to_str(instance, key, temp_key);\n    bool key_found = keys_dict_is_key_present_str(instance, temp_key);\n    furi_string_free(temp_key);\n\n    return key_found;\n}\n\nstatic bool keys_dict_add_key_str(KeysDict* instance, FuriString* key) {\n    furi_assert(instance);\n    furi_assert(instance->stream);\n    furi_assert(key);\n\n    furi_string_cat_str(key, \"\\n\");\n\n    bool key_added = false;\n\n    uint32_t actual_pos = stream_tell(instance->stream);\n\n    if(stream_seek(instance->stream, 0, StreamOffsetFromEnd) &&\n       stream_insert_string(instance->stream, key)) {\n        instance->total_keys++;\n        key_added = true;\n    }\n\n    stream_seek(instance->stream, actual_pos, StreamOffsetFromStart);\n\n    return key_added;\n}\n\nbool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size) {\n    furi_check(instance);\n    furi_check(instance->stream);\n    furi_check(instance->key_size == key_size);\n    furi_check(key);\n\n    FuriString* temp_key = furi_string_alloc();\n\n    keys_dict_int_to_str(instance, key, temp_key);\n    bool key_added = keys_dict_add_key_str(instance, temp_key);\n\n    FURI_LOG_I(TAG, \"Added key %s\", furi_string_get_cstr(temp_key));\n\n    furi_string_free(temp_key);\n\n    return key_added;\n}\n\nbool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size) {\n    furi_check(instance);\n    furi_check(instance->stream);\n    furi_check(instance->key_size == key_size);\n    furi_check(key);\n\n    bool key_removed = false;\n\n    uint8_t* temp_key = malloc(key_size);\n\n    stream_rewind(instance->stream);\n\n    while(!key_removed) {\n        if(!keys_dict_get_next_key(instance, temp_key, key_size)) {\n            break;\n        }\n\n        if(memcmp(temp_key, key, key_size) == 0) {\n            stream_seek(instance->stream, -instance->key_size_symbols, StreamOffsetFromCurrent);\n            if(stream_delete(instance->stream, instance->key_size_symbols) == false) {\n                break;\n            }\n            instance->total_keys--;\n            key_removed = true;\n        }\n    }\n\n    FuriString* tmp = furi_string_alloc();\n\n    keys_dict_int_to_str(instance, key, tmp);\n\n    FURI_LOG_I(TAG, \"Removed key %s\", furi_string_get_cstr(tmp));\n\n    furi_string_free(tmp);\n\n    stream_rewind(instance->stream);\n    free(temp_key);\n\n    return key_removed;\n}\n"
  },
  {
    "path": "lib/toolbox/keys_dict.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    KeysDictModeOpenExisting,\n    KeysDictModeOpenAlways,\n} KeysDictMode;\n\ntypedef struct KeysDict KeysDict;\n\n/** Check if the file list exists\n *\n * @param path      - list path\n *\n * @return true if list exists, false otherwise\n*/\nbool keys_dict_check_presence(const char* path);\n\n/** Open or create list\n * Depending on mode, list will be opened or created.\n *\n * @param path      - Path of the file that contain the list\n * @param mode      - ListKeysMode value\n * @param key_size  - Size of each key in bytes\n *\n * @return Returns KeysDict list instance\n*/\nKeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size);\n\n/** Close list\n *\n * @param instance  - KeysDict list instance\n*/\nvoid keys_dict_free(KeysDict* instance);\n\n/** Get total number of keys in list\n *\n * @param instance  - KeysDict list instance\n *\n * @return Returns total number of keys in list\n*/\nsize_t keys_dict_get_total_keys(KeysDict* instance);\n\n/** Rewind list\n *\n * @param instance  - KeysDict list instance\n *\n * @return Returns true if rewind was successful, false otherwise\n*/\nbool keys_dict_rewind(KeysDict* instance);\n\n/** Check if key is present in list\n *\n * @param instance  - KeysDict list instance\n * @param key       - key to check\n * @param key_size  - Size of the key in bytes\n *\n * @return Returns true if key is present, false otherwise\n*/\nbool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size);\n\n/** Get next key from the list\n * This function will return next key from list. If there are no more\n * keys, it will return false, and keys_dict_rewind() should be called.\n *\n * @param instance  - KeysDict list instance\n * @param key       - Array where to store key\n * @param key_size  - Size of key in bytes\n *\n * @return Returns true if key was successfully retrieved, false otherwise\n*/\nbool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size);\n\n/** Add key to list\n *\n * @param instance  - KeysDict list instance\n * @param key       - Key to add\n * @param key_size  - Size of the key in bytes\n *\n * @return Returns true if key was successfully added, false otherwise\n*/\nbool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size);\n\n/** Delete key from list\n *\n * @param instance  - KeysDict list instance\n * @param key       - Key to delete\n * @param key_size  - Size of the key in bytes\n *\n * @return Returns true if key was successfully deleted, false otherwise\n*/\nbool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/level_duration.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#define LEVEL_DURATION_BIG\n\n#ifdef LEVEL_DURATION_BIG\n\n#define LEVEL_DURATION_RESET      0U\n#define LEVEL_DURATION_LEVEL_LOW  1U\n#define LEVEL_DURATION_LEVEL_HIGH 2U\n#define LEVEL_DURATION_WAIT       3U\n#define LEVEL_DURATION_RESERVED   0x800000U\n\ntypedef struct {\n    uint32_t duration : 30;\n    uint8_t level     : 2;\n} LevelDuration;\n\nstatic inline LevelDuration level_duration_make(bool level, uint32_t duration) {\n    LevelDuration level_duration;\n    level_duration.level = level ? LEVEL_DURATION_LEVEL_HIGH : LEVEL_DURATION_LEVEL_LOW;\n    level_duration.duration = duration;\n    return level_duration;\n}\n\nstatic inline LevelDuration level_duration_reset() {\n    LevelDuration level_duration;\n    level_duration.level = LEVEL_DURATION_RESET;\n    return level_duration;\n}\n\nstatic inline LevelDuration level_duration_wait() {\n    LevelDuration level_duration;\n    level_duration.level = LEVEL_DURATION_WAIT;\n    return level_duration;\n}\n\nstatic inline bool level_duration_is_reset(LevelDuration level_duration) {\n    return level_duration.level == LEVEL_DURATION_RESET;\n}\n\nstatic inline bool level_duration_is_wait(LevelDuration level_duration) {\n    return level_duration.level == LEVEL_DURATION_WAIT;\n}\n\nstatic inline bool level_duration_get_level(LevelDuration level_duration) {\n    return level_duration.level == LEVEL_DURATION_LEVEL_HIGH;\n}\n\nstatic inline uint32_t level_duration_get_duration(LevelDuration level_duration) {\n    return level_duration.duration;\n}\n\n#else\n\n#define LEVEL_DURATION_RESET    0U\n#define LEVEL_DURATION_RESERVED 0x800000U\n\ntypedef int32_t LevelDuration;\n\nstatic inline LevelDuration level_duration(bool level, uint32_t duration) {\n    return level ? duration : -(int32_t)duration;\n}\n\nstatic inline LevelDuration level_duration_reset() {\n    return LEVEL_DURATION_RESET;\n}\n\nstatic inline bool level_duration_is_reset(LevelDuration level_duration) {\n    return level_duration == LEVEL_DURATION_RESET;\n}\n\nstatic inline bool level_duration_get_level(LevelDuration level_duration) {\n    return level_duration > 0;\n}\n\nstatic inline uint32_t level_duration_get_duration(LevelDuration level_duration) {\n    return (level_duration >= 0) ? level_duration : -level_duration;\n}\n\n#endif\n"
  },
  {
    "path": "lib/toolbox/m_cstr_dup.h",
    "content": "#pragma once\n#include <m-core.h>\n\n#define M_INIT_DUP(a)        ((a) = strdup(\"\"))\n#define M_INIT_SET_DUP(a, b) ((a) = strdup(b))\n#define M_SET_DUP(a, b)      (free((void*)a), (a) = strdup(b))\n#define M_CLEAR_DUP(a)       (free((void*)a))\n\n#define M_CSTR_DUP_OPLIST      \\\n    (INIT(M_INIT_DUP),         \\\n     INIT_SET(M_INIT_SET_DUP), \\\n     SET(M_SET_DUP),           \\\n     CLEAR(M_CLEAR_DUP),       \\\n     HASH(m_core_cstr_hash),   \\\n     EQUAL(M_CSTR_EQUAL),      \\\n     CMP(strcmp),              \\\n     TYPE(const char*))\n"
  },
  {
    "path": "lib/toolbox/manchester_decoder.c",
    "content": "#include \"manchester_decoder.h\"\n#include <stdint.h>\n\nstatic const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};\nstatic const ManchesterState manchester_reset_state = ManchesterStateMid1;\n\nbool manchester_advance(\n    ManchesterState state,\n    ManchesterEvent event,\n    ManchesterState* next_state,\n    bool* data) {\n    bool result = false;\n    ManchesterState new_state;\n\n    if(event == ManchesterEventReset) {\n        new_state = manchester_reset_state;\n    } else {\n        new_state = transitions[state] >> event & 0x3;\n        if(new_state == state) {\n            new_state = manchester_reset_state;\n        } else {\n            if(new_state == ManchesterStateMid0) {\n                if(data) *data = false;\n                result = true;\n            } else if(new_state == ManchesterStateMid1) {\n                if(data) *data = true;\n                result = true;\n            }\n        }\n    }\n\n    *next_state = new_state;\n    return result;\n}\n"
  },
  {
    "path": "lib/toolbox/manchester_decoder.h",
    "content": "#pragma once\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    ManchesterEventShortLow = 0,\n    ManchesterEventShortHigh = 2,\n    ManchesterEventLongLow = 4,\n    ManchesterEventLongHigh = 6,\n    ManchesterEventReset = 8\n} ManchesterEvent;\n\ntypedef enum {\n    ManchesterStateStart1 = 0,\n    ManchesterStateMid1 = 1,\n    ManchesterStateMid0 = 2,\n    ManchesterStateStart0 = 3\n} ManchesterState;\n\nbool manchester_advance(\n    ManchesterState state,\n    ManchesterEvent event,\n    ManchesterState* next_state,\n    bool* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/manchester_encoder.c",
    "content": "#include \"manchester_encoder.h\"\n#include <furi.h>\n\nvoid manchester_encoder_reset(ManchesterEncoderState* state) {\n    furi_check(state);\n    state->step = 0;\n}\n\nbool manchester_encoder_advance(\n    ManchesterEncoderState* state,\n    const bool curr_bit,\n    ManchesterEncoderResult* result) {\n    furi_check(state);\n    furi_check(result);\n\n    bool advance = false;\n    switch(state->step) {\n    case 0:\n        state->prev_bit = curr_bit;\n        if(state->prev_bit) {\n            *result = ManchesterEncoderResultShortLow;\n        } else {\n            *result = ManchesterEncoderResultShortHigh;\n        }\n        state->step = 1;\n        advance = true;\n        break;\n    case 1:\n        *result = (state->prev_bit << 1) + curr_bit;\n        if(curr_bit == state->prev_bit) {\n            state->step = 2;\n        } else {\n            state->prev_bit = curr_bit;\n            advance = true;\n        }\n        break;\n    case 2:\n        if(curr_bit) {\n            *result = ManchesterEncoderResultShortLow;\n        } else {\n            *result = ManchesterEncoderResultShortHigh;\n        }\n        state->prev_bit = curr_bit;\n        state->step = 1;\n        advance = true;\n        break;\n    default:\n        furi_crash();\n        break;\n    }\n    return advance;\n}\n\nManchesterEncoderResult manchester_encoder_finish(ManchesterEncoderState* state) {\n    furi_check(state);\n\n    state->step = 0;\n\n    return (state->prev_bit << 1) + state->prev_bit;\n}\n"
  },
  {
    "path": "lib/toolbox/manchester_encoder.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    bool prev_bit;\n    uint8_t step;\n} ManchesterEncoderState;\n\ntypedef enum {\n    ManchesterEncoderResultShortLow = 0b00,\n    ManchesterEncoderResultLongLow = 0b01,\n    ManchesterEncoderResultLongHigh = 0b10,\n    ManchesterEncoderResultShortHigh = 0b11,\n} ManchesterEncoderResult;\n\nvoid manchester_encoder_reset(ManchesterEncoderState* state);\n\nbool manchester_encoder_advance(\n    ManchesterEncoderState* state,\n    const bool curr_bit,\n    ManchesterEncoderResult* result);\n\nManchesterEncoderResult manchester_encoder_finish(ManchesterEncoderState* state);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/md5_calc.c",
    "content": "#include \"md5_calc.h\"\n\n#include <storage/filesystem_api_defines.h>\n#include <storage/storage.h>\n#include <mbedtls/md5.h>\n\nbool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) {\n    if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n        if(file_error != NULL) {\n            *file_error = storage_file_get_error(file);\n        }\n        return false;\n    }\n\n    const size_t size_to_read = 512;\n    uint8_t* data = malloc(size_to_read);\n    bool result = true;\n\n    mbedtls_md5_context* md5_ctx = malloc(sizeof(mbedtls_md5_context));\n    mbedtls_md5_init(md5_ctx);\n    mbedtls_md5_starts(md5_ctx);\n    while(true) {\n        size_t read_size = storage_file_read(file, data, size_to_read);\n        if(storage_file_get_error(file) != FSE_OK) {\n            result = false;\n            break;\n        }\n        if(read_size == 0) {\n            break;\n        }\n        mbedtls_md5_update(md5_ctx, data, read_size);\n    }\n    mbedtls_md5_finish(md5_ctx, output);\n    free(md5_ctx);\n    free(data);\n\n    if(file_error != NULL) {\n        *file_error = storage_file_get_error(file);\n    }\n\n    storage_file_close(file);\n    return result;\n}\n\nbool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) {\n    const size_t hash_size = 16;\n    unsigned char hash[hash_size];\n    bool result = md5_calc_file(file, path, hash, file_error);\n\n    if(result) {\n        furi_string_set(output, \"\");\n        for(size_t i = 0; i < hash_size; i++) {\n            furi_string_cat_printf(output, \"%02x\", hash[i]);\n        }\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "lib/toolbox/md5_calc.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error);\n\nbool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/name_generator.c",
    "content": "#include \"name_generator.h\"\n\n#include <stdio.h>\n#include <stdint.h>\n#include <furi_hal_rtc.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <furi.h>\n\nconst char* const name_generator_left[] = {\n    \"ancient\",  \"hollow\", \"strange\",   \"disappeared\", \"unknown\",    \"unthinkable\", \"unnameable\",\n    \"nameless\", \"my\",     \"concealed\", \"forgotten\",   \"hidden\",     \"mysterious\",  \"obscure\",\n    \"random\",   \"remote\", \"uncharted\", \"undefined\",   \"untraveled\", \"untold\",\n};\n\nconst char* const name_generator_right[] = {\n    \"door\",\n    \"entrance\",\n    \"doorway\",\n    \"entry\",\n    \"portal\",\n    \"entree\",\n    \"opening\",\n    \"crack\",\n    \"access\",\n    \"corridor\",\n    \"passage\",\n    \"port\",\n};\n\nvoid name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) {\n    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDetailedFilename)) {\n        name_generator_make_detailed(name, max_name_size, prefix);\n    } else {\n        name_generator_make_random(name, max_name_size);\n    }\n}\n\nvoid name_generator_make_random(char* name, size_t max_name_size) {\n    furi_check(name);\n    furi_check(max_name_size);\n\n    uint8_t name_generator_left_i = rand() % COUNT_OF(name_generator_left);\n    uint8_t name_generator_right_i = rand() % COUNT_OF(name_generator_right);\n\n    snprintf(\n        name,\n        max_name_size,\n        \"%s_%s\",\n        name_generator_left[name_generator_left_i],\n        name_generator_right[name_generator_right_i]);\n\n    // Set first symbol to upper case\n    name[0] = name[0] - 0x20;\n}\n\nvoid name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) {\n    furi_check(name);\n    furi_check(max_name_size);\n    furi_check(prefix);\n\n    DateTime dateTime;\n    furi_hal_rtc_get_datetime(&dateTime);\n\n    snprintf(\n        name,\n        max_name_size,\n        \"%s-%.4d_%.2d_%.2d-%.2d_%.2d\",\n        prefix,\n        dateTime.year,\n        dateTime.month,\n        dateTime.day,\n        dateTime.hour,\n        dateTime.minute);\n}\n"
  },
  {
    "path": "lib/toolbox/name_generator.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Generates detailed/random name based on furi_hal flags\n *\n * @param      name           buffer to write random name\n * @param      max_name_size  length of given buffer\n * @param[in]  prefix         The prefix of the name\n */\nvoid name_generator_make_auto(char* name, size_t max_name_size, const char* prefix);\n\n/** Generates random name\n *\n * @param      name           buffer to write random name\n * @param      max_name_size  length of given buffer\n */\nvoid name_generator_make_random(char* name, size_t max_name_size);\n\n/** Generates detailed name\n *\n * @param      name           buffer to write random name\n * @param      max_name_size  length of given buffer\n * @param[in]  prefix         The prefix of the name\n */\nvoid name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/path.c",
    "content": "#include \"path.h\"\n#include <stddef.h>\n\nvoid path_extract_filename_no_ext(const char* path, FuriString* filename) {\n    furi_check(path);\n    furi_check(filename);\n\n    furi_string_set(filename, path);\n\n    size_t start_position = furi_string_search_rchar(filename, '/');\n    size_t end_position = furi_string_search_rchar(filename, '.');\n\n    if(start_position == FURI_STRING_FAILURE) {\n        start_position = 0;\n    } else {\n        start_position += 1;\n    }\n\n    if(end_position == FURI_STRING_FAILURE) {\n        end_position = furi_string_size(filename);\n    }\n\n    furi_string_mid(filename, start_position, end_position - start_position);\n}\n\nvoid path_extract_filename(FuriString* path, FuriString* name, bool trim_ext) {\n    furi_check(path);\n    furi_check(name);\n\n    size_t filename_start = furi_string_search_rchar(path, '/');\n    if(filename_start > 0) {\n        filename_start++;\n        furi_string_set_n(name, path, filename_start, furi_string_size(path) - filename_start);\n    }\n    if(trim_ext) {\n        size_t dot = furi_string_search_rchar(name, '.');\n        if(dot > 0) {\n            furi_string_left(name, dot);\n        }\n    }\n}\n\nvoid path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) {\n    furi_check(path);\n    furi_check(ext);\n    furi_check(ext_len_max > 0);\n\n    size_t dot = furi_string_search_rchar(path, '.');\n    size_t filename_start = furi_string_search_rchar(path, '/');\n\n    if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) {\n        strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max);\n    }\n}\n\nstatic inline void path_cleanup(FuriString* path) {\n    furi_string_trim(path);\n    while(furi_string_end_with(path, \"/\")) {\n        furi_string_left(path, furi_string_size(path) - 1);\n    }\n}\n\nvoid path_extract_basename(const char* path, FuriString* basename) {\n    furi_check(path);\n    furi_check(basename);\n\n    furi_string_set(basename, path);\n    path_cleanup(basename);\n    size_t pos = furi_string_search_rchar(basename, '/');\n    if(pos != FURI_STRING_FAILURE) {\n        furi_string_right(basename, pos + 1);\n    }\n}\n\nvoid path_extract_dirname(const char* path, FuriString* dirname) {\n    furi_check(path);\n    furi_check(dirname);\n\n    furi_string_set(dirname, path);\n    path_cleanup(dirname);\n    size_t pos = furi_string_search_rchar(dirname, '/');\n    if(pos != FURI_STRING_FAILURE) {\n        furi_string_left(dirname, pos);\n    }\n}\n\nvoid path_append(FuriString* path, const char* suffix) {\n    furi_check(path);\n    furi_check(suffix);\n\n    path_cleanup(path);\n    FuriString* suffix_str;\n    suffix_str = furi_string_alloc_set(suffix);\n    furi_string_trim(suffix_str);\n    furi_string_trim(suffix_str, \"/\");\n    furi_string_cat_printf(path, \"/%s\", furi_string_get_cstr(suffix_str));\n    furi_string_free(suffix_str);\n}\n\nvoid path_concat(const char* path, const char* suffix, FuriString* out_path) {\n    furi_check(path);\n    furi_check(suffix);\n    furi_check(out_path);\n\n    furi_string_set(out_path, path);\n    path_append(out_path, suffix);\n}\n\nbool path_contains_only_ascii(const char* path) {\n    if(!path) {\n        return false;\n    }\n\n    const char* name_pos = strrchr(path, '/');\n    if(name_pos == NULL) {\n        name_pos = path;\n    } else {\n        name_pos++;\n    }\n\n    for(; *name_pos; ++name_pos) {\n        const char c = *name_pos;\n\n        // Regular ASCII characters from 0x20 to 0x7e\n        const bool is_out_of_range = (c < ' ') || (c > '~');\n        // Cross-platform forbidden character set\n        const bool is_forbidden = strchr(\"\\\\<>*|\\\":?\", c);\n\n        if(is_out_of_range || is_forbidden) {\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "lib/toolbox/path.h",
    "content": "#pragma once\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Extract filename without extension from path.\n * \n * @param path path string\n * @param filename output filename string. Must be initialized before.\n */\nvoid path_extract_filename_no_ext(const char* path, FuriString* filename);\n\n/**\n * @brief Extract filename string from path.\n * \n * @param path path string\n * @param filename output filename string. Must be initialized before.\n * @param trim_ext true - get filename without extension\n */\nvoid path_extract_filename(FuriString* path, FuriString* filename, bool trim_ext);\n\n/**\n * @brief Extract file extension from path.\n * \n * @param path path string\n * @param ext output extension string\n * @param ext_len_max maximum extension string length\n */\nvoid path_extract_extension(FuriString* path, char* ext, size_t ext_len_max);\n\n/**\n * @brief Extract last path component\n * \n * @param path path string\n * @param filename output string. Must be initialized before.\n */\nvoid path_extract_basename(const char* path, FuriString* basename);\n\n/**\n * @brief Extract path, except for last component\n * \n * @param path path string\n * @param filename output string. Must be initialized before.\n */\nvoid path_extract_dirname(const char* path, FuriString* dirname);\n\n/**\n * @brief Appends new component to path, adding path delimiter\n * \n * @param path path string\n * @param suffix path part to apply\n */\nvoid path_append(FuriString* path, const char* suffix);\n\n/**\n * @brief Appends new component to path, adding path delimiter\n * \n * @param path first path part\n * @param suffix second path part\n * @param out_path output string to combine parts into. Must be initialized\n */\nvoid path_concat(const char* path, const char* suffix, FuriString* out_path);\n\n/**\n * @brief Check that path contains only ascii characters\n * \n * @param path \n * @return true \n * @return false \n */\nbool path_contains_only_ascii(const char* path);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/pipe.c",
    "content": "#include \"pipe.h\"\n#include <furi.h>\n\n#define PIPE_DEFAULT_STATE_CHECK_PERIOD furi_ms_to_ticks(100)\n\n/**\n * Data shared between both sides.\n */\ntypedef struct {\n    FuriSemaphore* instance_count; // <! 1 = both sides, 0 = only one side\n    FuriMutex* state_transition;\n} PipeShared;\n\n/**\n * There are two PipeSides per pipe.\n */\nstruct PipeSide {\n    PipeRole role;\n    PipeShared* shared;\n    FuriStreamBuffer* sending;\n    FuriStreamBuffer* receiving;\n\n    FuriEventLoop* event_loop;\n    void* callback_context;\n    PipeSideDataArrivedCallback on_data_arrived;\n    PipeSideSpaceFreedCallback on_space_freed;\n    PipeSideBrokenCallback on_pipe_broken;\n    FuriWait state_check_period;\n};\n\nPipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level) {\n    PipeSideReceiveSettings settings = {\n        .capacity = capacity,\n        .trigger_level = trigger_level,\n    };\n    return pipe_alloc_ex(settings, settings);\n}\n\nPipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSettings bob) {\n    // the underlying primitives are shared\n    FuriStreamBuffer* alice_to_bob = furi_stream_buffer_alloc(bob.capacity, bob.trigger_level);\n    FuriStreamBuffer* bob_to_alice = furi_stream_buffer_alloc(alice.capacity, alice.trigger_level);\n\n    PipeShared* shared = malloc(sizeof(PipeShared));\n    *shared = (PipeShared){\n        .instance_count = furi_semaphore_alloc(1, 1),\n        .state_transition = furi_mutex_alloc(FuriMutexTypeNormal),\n    };\n\n    PipeSide* alices_side = malloc(sizeof(PipeSide));\n    PipeSide* bobs_side = malloc(sizeof(PipeSide));\n\n    *alices_side = (PipeSide){\n        .role = PipeRoleAlice,\n        .shared = shared,\n        .sending = alice_to_bob,\n        .receiving = bob_to_alice,\n        .state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD,\n    };\n    *bobs_side = (PipeSide){\n        .role = PipeRoleBob,\n        .shared = shared,\n        .sending = bob_to_alice,\n        .receiving = alice_to_bob,\n        .state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD,\n    };\n\n    return (PipeSideBundle){.alices_side = alices_side, .bobs_side = bobs_side};\n}\n\nPipeRole pipe_role(PipeSide* pipe) {\n    furi_check(pipe);\n    return pipe->role;\n}\n\nPipeState pipe_state(PipeSide* pipe) {\n    furi_check(pipe);\n    uint32_t count = furi_semaphore_get_count(pipe->shared->instance_count);\n    return (count == 1) ? PipeStateOpen : PipeStateBroken;\n}\n\nvoid pipe_free(PipeSide* pipe) {\n    furi_check(pipe);\n    furi_check(!pipe->event_loop);\n\n    furi_mutex_acquire(pipe->shared->state_transition, FuriWaitForever);\n    FuriStatus status = furi_semaphore_acquire(pipe->shared->instance_count, 0);\n\n    if(status == FuriStatusOk) {\n        // the other side is still intact\n        furi_mutex_release(pipe->shared->state_transition);\n        free(pipe);\n    } else {\n        // the other side is gone too\n        furi_stream_buffer_free(pipe->sending);\n        furi_stream_buffer_free(pipe->receiving);\n        furi_semaphore_free(pipe->shared->instance_count);\n        furi_mutex_free(pipe->shared->state_transition);\n        free(pipe->shared);\n        free(pipe);\n    }\n}\n\nstatic void pipe_stdout_cb(const char* data, size_t size, void* context) {\n    furi_assert(context);\n    PipeSide* pipe = context;\n    pipe_send(pipe, data, size);\n}\n\nstatic size_t pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) {\n    UNUSED(timeout);\n    furi_assert(context);\n    PipeSide* pipe = context;\n    return pipe_receive(pipe, data, size);\n}\n\nvoid pipe_install_as_stdio(PipeSide* pipe) {\n    furi_check(pipe);\n    furi_thread_set_stdout_callback(pipe_stdout_cb, pipe);\n    furi_thread_set_stdin_callback(pipe_stdin_cb, pipe);\n}\n\nvoid pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period) {\n    furi_check(pipe);\n    pipe->state_check_period = check_period;\n}\n\nsize_t pipe_receive(PipeSide* pipe, void* data, size_t length) {\n    furi_check(pipe);\n\n    size_t received = 0;\n    while(length) {\n        size_t received_this_time =\n            furi_stream_buffer_receive(pipe->receiving, data, length, pipe->state_check_period);\n        if(!received_this_time && pipe_state(pipe) == PipeStateBroken) break;\n\n        received += received_this_time;\n        length -= received_this_time;\n        data += received_this_time;\n    }\n\n    return received;\n}\n\nsize_t pipe_send(PipeSide* pipe, const void* data, size_t length) {\n    furi_check(pipe);\n\n    size_t sent = 0;\n    while(length) {\n        size_t sent_this_time =\n            furi_stream_buffer_send(pipe->sending, data, length, pipe->state_check_period);\n        if(!sent_this_time && pipe_state(pipe) == PipeStateBroken) break;\n\n        sent += sent_this_time;\n        length -= sent_this_time;\n        data += sent_this_time;\n    }\n\n    return sent;\n}\n\nsize_t pipe_bytes_available(PipeSide* pipe) {\n    furi_check(pipe);\n    return furi_stream_buffer_bytes_available(pipe->receiving);\n}\n\nsize_t pipe_spaces_available(PipeSide* pipe) {\n    furi_check(pipe);\n    return furi_stream_buffer_spaces_available(pipe->sending);\n}\n\nstatic void pipe_receiving_buffer_callback(FuriEventLoopObject* buffer, void* context) {\n    UNUSED(buffer);\n    PipeSide* pipe = context;\n    furi_assert(pipe);\n    if(pipe->on_data_arrived) pipe->on_data_arrived(pipe, pipe->callback_context);\n}\n\nstatic void pipe_sending_buffer_callback(FuriEventLoopObject* buffer, void* context) {\n    UNUSED(buffer);\n    PipeSide* pipe = context;\n    furi_assert(pipe);\n    if(pipe->on_space_freed) pipe->on_space_freed(pipe, pipe->callback_context);\n}\n\nstatic void pipe_semaphore_callback(FuriEventLoopObject* semaphore, void* context) {\n    UNUSED(semaphore);\n    PipeSide* pipe = context;\n    furi_assert(pipe);\n    if(pipe->on_pipe_broken) pipe->on_pipe_broken(pipe, pipe->callback_context);\n}\n\nvoid pipe_attach_to_event_loop(PipeSide* pipe, FuriEventLoop* event_loop) {\n    furi_check(pipe);\n    furi_check(event_loop);\n    furi_check(!pipe->event_loop);\n\n    pipe->event_loop = event_loop;\n}\n\nvoid pipe_detach_from_event_loop(PipeSide* pipe) {\n    furi_check(pipe);\n    furi_check(pipe->event_loop);\n\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->receiving);\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->sending);\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->shared->instance_count);\n\n    pipe->event_loop = NULL;\n}\n\nvoid pipe_set_callback_context(PipeSide* pipe, void* context) {\n    furi_check(pipe);\n    pipe->callback_context = context;\n}\n\nvoid pipe_set_data_arrived_callback(\n    PipeSide* pipe,\n    PipeSideDataArrivedCallback callback,\n    FuriEventLoopEvent event) {\n    furi_check(pipe);\n    furi_check(pipe->event_loop);\n    furi_check((event & FuriEventLoopEventMask) == 0);\n\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->receiving);\n    pipe->on_data_arrived = callback;\n    if(callback)\n        furi_event_loop_subscribe_stream_buffer(\n            pipe->event_loop,\n            pipe->receiving,\n            FuriEventLoopEventIn | event,\n            pipe_receiving_buffer_callback,\n            pipe);\n}\n\nvoid pipe_set_space_freed_callback(\n    PipeSide* pipe,\n    PipeSideSpaceFreedCallback callback,\n    FuriEventLoopEvent event) {\n    furi_check(pipe);\n    furi_check(pipe->event_loop);\n    furi_check((event & FuriEventLoopEventMask) == 0);\n\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->sending);\n    pipe->on_space_freed = callback;\n    if(callback)\n        furi_event_loop_subscribe_stream_buffer(\n            pipe->event_loop,\n            pipe->sending,\n            FuriEventLoopEventOut | event,\n            pipe_sending_buffer_callback,\n            pipe);\n}\n\nvoid pipe_set_broken_callback(\n    PipeSide* pipe,\n    PipeSideBrokenCallback callback,\n    FuriEventLoopEvent event) {\n    furi_check(pipe);\n    furi_check(pipe->event_loop);\n    furi_check((event & FuriEventLoopEventMask) == 0);\n\n    furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->shared->instance_count);\n    pipe->on_pipe_broken = callback;\n    if(callback)\n        furi_event_loop_subscribe_semaphore(\n            pipe->event_loop,\n            pipe->shared->instance_count,\n            FuriEventLoopEventOut | event,\n            pipe_semaphore_callback,\n            pipe);\n}\n"
  },
  {
    "path": "lib/toolbox/pipe.h",
    "content": "/**\n * @file pipe.h\n * Pipe convenience module\n * \n * Pipes are used to send bytes between two threads in both directions. The two\n * threads are referred to as Alice and Bob and their abilities regarding what\n * they can do with the pipe are equal.\n * \n * It is also possible to use both sides of the pipe within one thread.\n */\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <furi.h>\n#include <stddef.h>\n\n/**\n * @brief The role of a pipe side\n * \n * Both roles are equal, as they can both read and write the data. This status\n * might be helpful in determining the role of a thread w.r.t. another thread in\n * an application that builds on the pipe.\n */\ntypedef enum {\n    PipeRoleAlice,\n    PipeRoleBob,\n} PipeRole;\n\n/**\n * @brief The state of a pipe\n * \n *   - `PipeStateOpen`: Both pipe sides are in place, meaning data that is sent\n *     down the pipe _might_ be read by the peer, and new data sent by the peer\n *     _might_ arrive.\n *   - `PipeStateBroken`: The other side of the pipe has been freed, meaning\n *     data that is written will never reach its destination, and no new data\n *     will appear in the buffer.\n * \n * A broken pipe can never become open again, because there's no way to connect\n * a side of a pipe to another side of a pipe.\n */\ntypedef enum {\n    PipeStateOpen,\n    PipeStateBroken,\n} PipeState;\n\ntypedef struct PipeSide PipeSide;\n\ntypedef struct {\n    PipeSide* alices_side;\n    PipeSide* bobs_side;\n} PipeSideBundle;\n\ntypedef struct {\n    size_t capacity;\n    size_t trigger_level;\n} PipeSideReceiveSettings;\n\n/**\n * @brief Allocates two connected sides of one pipe.\n * \n * Creating a pair of sides using this function is the only way to connect two\n * pipe sides together. Two unrelated orphaned sides may never be connected back\n * together.\n * \n * The capacity and trigger level for both directions are the same when the pipe\n * is created using this function. Use `pipe_alloc_ex` if you want more\n * control.\n * \n * @param capacity Maximum number of bytes buffered in one direction\n * @param trigger_level Number of bytes that need to be available in the buffer\n *                      in order for a blocked thread to unblock\n * @returns Bundle with both sides of the pipe\n */\nPipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level);\n\n/**\n * @brief Allocates two connected sides of one pipe.\n * \n * Creating a pair of sides using this function is the only way to connect two\n * pipe sides together. Two unrelated orphaned sides may never be connected back\n * together.\n * \n * The capacity and trigger level may be different for the two directions when\n * the pipe is created using this function. Use `pipe_alloc` if you don't\n * need control this fine.\n * \n * @param alice `capacity` and `trigger_level` settings for Alice's receiving\n *              buffer\n * @param bob `capacity` and `trigger_level` settings for Bob's receiving buffer\n * @returns Bundle with both sides of the pipe\n */\nPipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSettings bob);\n\n/**\n * @brief Gets the role of a pipe side.\n * \n * The roles (Alice and Bob) are equal, as both can send and receive data. This\n * status might be helpful in determining the role of a thread w.r.t. another\n * thread.\n * \n * @param [in] pipe Pipe side to query\n * @returns Role of provided pipe side\n */\nPipeRole pipe_role(PipeSide* pipe);\n\n/**\n * @brief Gets the state of a pipe.\n * \n * When the state is `PipeStateOpen`, both sides are active and may send or\n * receive data. When the state is `PipeStateBroken`, only one side is active\n * (the one that this method has been called on). If you find yourself in that\n * state, the data that you send will never be heard by anyone, and the data you\n * receive are leftovers in the buffer.\n * \n * @param [in] pipe Pipe side to query\n * @returns State of the pipe\n */\nPipeState pipe_state(PipeSide* pipe);\n\n/**\n * @brief Frees a side of a pipe.\n * \n * When only one of the sides is freed, the pipe is transitioned from the \"Open\"\n * state into the \"Broken\" state. When both sides are freed, the underlying data\n * structures are freed too.\n * \n * @param [in] pipe Pipe side to free\n */\nvoid pipe_free(PipeSide* pipe);\n\n/**\n * @brief Connects the pipe to the `stdin` and `stdout` of the current thread.\n * \n * After performing this operation, you can use `getc`, `puts`, etc. to send and\n * receive data to and from the pipe. If the pipe becomes broken, C stdlib calls\n * will return `EOF` wherever possible.\n * \n * You can disconnect the pipe by manually calling\n * `furi_thread_set_stdout_callback` and `furi_thread_set_stdin_callback` with\n * `NULL`.\n * \n * @param [in] pipe Pipe side to connect to the stdio\n */\nvoid pipe_install_as_stdio(PipeSide* pipe);\n\n/**\n * @brief Sets the state check period for `send` and `receive` operations\n * \n * @note This value is set to 100 ms when the pipe is created\n * \n * `send` and `receive` will check the state of the pipe if exactly 0 bytes were\n * sent or received during any given `check_period`. Read the documentation for\n * `pipe_send` and `pipe_receive` for more info.\n * \n * @param [in] pipe Pipe side to set the check period of\n * @param [in] check_period Period in ticks\n */\nvoid pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period);\n\n/**\n * @brief Receives data from the pipe.\n * \n * This function will try to receive all of the requested bytes from the pipe.\n * If at some point during the operation the pipe becomes broken, this function\n * will return prematurely, in which case the return value will be less than the\n * requested `length`.\n * \n * @param [in] pipe The pipe side to read data out of\n * @param [out] data The buffer to fill with data\n * @param length Maximum length of data to read\n * @returns The number of bytes actually written into the provided buffer\n */\nsize_t pipe_receive(PipeSide* pipe, void* data, size_t length);\n\n/**\n * @brief Sends data into the pipe.\n * \n * This function will try to send all of the requested bytes to the pipe.\n * If at some point during the operation the pipe becomes broken, this function\n * will return prematurely, in which case the return value will be less than the\n * requested `length`.\n * \n * @param [in] pipe The pipe side to send data into\n * @param [out] data The buffer to get data from\n * @param length Maximum length of data to send\n * @returns The number of bytes actually read from the provided buffer\n */\nsize_t pipe_send(PipeSide* pipe, const void* data, size_t length);\n\n/**\n * @brief Determines how many bytes there are in the pipe available to be read.\n * \n * @param [in] pipe Pipe side to query\n * @returns Number of bytes available to be read out from that side of the pipe\n */\nsize_t pipe_bytes_available(PipeSide* pipe);\n\n/**\n * @brief Determines how many space there is in the pipe for data to be written\n * into.\n * \n * @param [in] pipe Pipe side to query\n * @returns Number of bytes available to be written into that side of the pipe\n */\nsize_t pipe_spaces_available(PipeSide* pipe);\n\n/**\n * @brief Attaches a `PipeSide` to a `FuriEventLoop`, allowing to attach\n * callbacks to the PipeSide.\n * \n * @param [in] pipe       Pipe side to attach to the event loop\n * @param [in] event_loop Event loop to attach the pipe side to\n */\nvoid pipe_attach_to_event_loop(PipeSide* pipe, FuriEventLoop* event_loop);\n\n/**\n * @brief Detaches a `PipeSide` from the `FuriEventLoop` that it was previously\n * attached to.\n * \n * @param [in] pipe Pipe side to detach to the event loop\n */\nvoid pipe_detach_from_event_loop(PipeSide* pipe);\n\n/**\n * @brief Callback for when data arrives to a `PipeSide`.\n * \n * @param [in]    pipe    Pipe side that called the callback\n * @param [inout] context Custom context\n */\ntypedef void (*PipeSideDataArrivedCallback)(PipeSide* pipe, void* context);\n\n/**\n * @brief Callback for when data is read out of the opposite `PipeSide`.\n * \n * @param [in]    pipe    Pipe side that called the callback\n * @param [inout] context Custom context\n */\ntypedef void (*PipeSideSpaceFreedCallback)(PipeSide* pipe, void* context);\n\n/**\n * @brief Callback for when the opposite `PipeSide` is freed, making the pipe\n * broken.\n * \n * @param [in]    pipe    Pipe side that called the callback\n * @param [inout] context Custom context\n */\ntypedef void (*PipeSideBrokenCallback)(PipeSide* pipe, void* context);\n\n/**\n * @brief Sets the custom context for all callbacks.\n * \n * @param [in]    pipe    Pipe side to set the context of\n * @param [inout] context Custom context that will be passed to callbacks\n */\nvoid pipe_set_callback_context(PipeSide* pipe, void* context);\n\n/**\n * @brief Sets the callback for when data arrives.\n * \n * @param [in] pipe     Pipe side to assign the callback to\n * @param [in] callback Callback to assign to the pipe side. Set to NULL to\n *                      unsubscribe.\n * @param [in] event    Additional event loop flags (e.g. `Edge`, `Once`, etc.).\n *                      Non-flag values of the enum are not allowed.\n * \n * @warning Attach the pipe side to an event loop first using\n * `pipe_attach_to_event_loop`.\n */\nvoid pipe_set_data_arrived_callback(\n    PipeSide* pipe,\n    PipeSideDataArrivedCallback callback,\n    FuriEventLoopEvent event);\n\n/**\n * @brief Sets the callback for when data is read out of the opposite `PipeSide`.\n * \n * @param [in] pipe     Pipe side to assign the callback to\n * @param [in] callback Callback to assign to the pipe side. Set to NULL to\n *                      unsubscribe.\n * @param [in] event    Additional event loop flags (e.g. `Edge`, `Once`, etc.).\n *                      Non-flag values of the enum are not allowed.\n * \n * @warning Attach the pipe side to an event loop first using\n * `pipe_attach_to_event_loop`.\n */\nvoid pipe_set_space_freed_callback(\n    PipeSide* pipe,\n    PipeSideSpaceFreedCallback callback,\n    FuriEventLoopEvent event);\n\n/**\n * @brief Sets the callback for when the opposite `PipeSide` is freed, making\n * the pipe broken.\n * \n * @param [in] pipe     Pipe side to assign the callback to\n * @param [in] callback Callback to assign to the pipe side. Set to NULL to\n *                      unsubscribe.\n * @param [in] event    Additional event loop flags (e.g. `Edge`, `Once`, etc.).\n *                      Non-flag values of the enum are not allowed.\n * \n * @warning Attach the pipe side to an event loop first using\n * `pipe_attach_to_event_loop`.\n */\nvoid pipe_set_broken_callback(\n    PipeSide* pipe,\n    PipeSideBrokenCallback callback,\n    FuriEventLoopEvent event);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/pretty_format.c",
    "content": "#include \"pretty_format.h\"\n\n#include <core/check.h>\n#include <core/core_defines.h>\n\n#define PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE 256U\n\nvoid pretty_format_bytes_hex_canonical(\n    FuriString* result,\n    size_t num_places,\n    const char* line_prefix,\n    const uint8_t* data,\n    size_t data_size) {\n    furi_check(data);\n\n    bool is_truncated = false;\n\n    if(data_size > PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE) {\n        data_size = PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE;\n        is_truncated = true;\n    }\n\n    /* Only num_places byte(s) can be on a single line, therefore: */\n    const size_t line_count =\n        data_size / num_places + (data_size % num_places != 0 ? 1 : 0) + (is_truncated ? 2 : 0);\n    /* Line length = Prefix length + 3 * num_places (2 hex digits + space) + 1 * num_places +\n       + 1 pipe character + 1 newline character */\n    const size_t line_length = (line_prefix ? strlen(line_prefix) : 0) + 4 * num_places + 2;\n\n    /* Reserve memory in adance in order to avoid unnecessary reallocs */\n    furi_string_reserve(result, furi_string_size(result) + line_count * line_length);\n\n    for(size_t i = 0; i < data_size; i += num_places) {\n        if(line_prefix) {\n            furi_string_cat(result, line_prefix);\n        }\n\n        const size_t begin_idx = i;\n        const size_t wrap_idx = i + num_places;\n        const size_t end_idx = MIN(wrap_idx, data_size);\n\n        for(size_t j = begin_idx; j < end_idx; j++) {\n            furi_string_cat_printf(result, \"%02X \", data[j]);\n        }\n        if(end_idx < wrap_idx) {\n            for(size_t j = end_idx; j < wrap_idx; j++) {\n                furi_string_cat_printf(result, \"   \");\n            }\n        }\n\n        furi_string_push_back(result, '|');\n\n        for(size_t j = begin_idx; j < end_idx; j++) {\n            const char c = data[j];\n            const char sep = ((j < end_idx - 1) ? ' ' : '\\n');\n            const char* fmt = ((j < data_size - 1) ? \"%c%c\" : \"%c\");\n            furi_string_cat_printf(result, fmt, (c > 0x1f && c < 0x7f) ? c : '.', sep);\n        }\n    }\n\n    if(is_truncated) {\n        furi_string_cat_printf(\n            result, \"\\n(Data is too big. Showing only the first %zu bytes.)\", data_size);\n    }\n}\n"
  },
  {
    "path": "lib/toolbox/pretty_format.h",
    "content": "#pragma once\n\n#include <core/string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PRETTY_FORMAT_FONT_BOLD      \"\\e#\"\n#define PRETTY_FORMAT_FONT_MONOSPACE \"\\e*\"\n\n/**\n * Format a data buffer as a canonical HEX dump\n * @param [out] result pointer to the output string (must be initialised)\n * @param [in] num_places the number of bytes on one line (both as HEX and ASCII)\n * @param [in] line_prefix if not NULL, prepend this string to each line\n * @param [in] data pointer to the input data buffer\n * @param [in] data_size input data size\n */\nvoid pretty_format_bytes_hex_canonical(\n    FuriString* result,\n    size_t num_places,\n    const char* line_prefix,\n    const uint8_t* data,\n    size_t data_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/profiler.c",
    "content": "#include \"profiler.h\"\n#include <stdlib.h>\n#include <m-dict.h>\n#include <furi.h>\n#include <furi_hal_gpio.h>\n\ntypedef struct {\n    uint32_t start;\n    uint32_t length;\n    uint32_t count;\n} ProfilerRecord;\n\nDICT_DEF2(ProfilerRecordDict, const char*, M_CSTR_OPLIST, ProfilerRecord, M_POD_OPLIST)\n#define M_OPL_ProfilerRecord_t() DICT_OPLIST(ProfilerRecord, M_CSTR_OPLIST, M_POD_OPLIST)\n\nstruct Profiler {\n    ProfilerRecordDict_t records;\n};\n\nProfiler* profiler_alloc(void) {\n    Profiler* profiler = malloc(sizeof(Profiler));\n    ProfilerRecordDict_init(profiler->records);\n    return profiler;\n}\n\nvoid profiler_free(Profiler* profiler) {\n    ProfilerRecordDict_clear(profiler->records);\n    free(profiler);\n}\n\nvoid profiler_prealloc(Profiler* profiler, const char* key) {\n    ProfilerRecord record = {\n        .start = 0,\n        .length = 0,\n        .count = 0,\n    };\n\n    ProfilerRecordDict_set_at(profiler->records, key, record);\n}\n\nvoid profiler_start(Profiler* profiler, const char* key) {\n    ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key);\n    if(record == NULL) {\n        profiler_prealloc(profiler, key);\n        record = ProfilerRecordDict_get(profiler->records, key);\n    }\n\n    furi_check(record->start == 0);\n    record->start = DWT->CYCCNT;\n}\n\nvoid profiler_stop(Profiler* profiler, const char* key) {\n    ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key);\n    furi_check(record != NULL);\n\n    record->length += DWT->CYCCNT - record->start;\n    record->start = 0;\n    record->count++;\n}\n\nvoid profiler_dump(Profiler* profiler) {\n    printf(\"Profiler:\\r\\n\");\n\n    ProfilerRecordDict_it_t it;\n    for(ProfilerRecordDict_it(it, profiler->records); !ProfilerRecordDict_end_p(it);\n        ProfilerRecordDict_next(it)) {\n        const ProfilerRecordDict_itref_t* itref = ProfilerRecordDict_cref(it);\n\n        uint32_t count = itref->value.count;\n\n        uint32_t clocks = itref->value.length;\n        double us = (double)clocks / (double)64.0;\n        double ms = (double)clocks / (double)64000.0;\n        double s = (double)clocks / (double)64000000.0;\n\n        printf(\"\\t%s[%lu]: %f s, %f ms, %f us, %lu clk\\r\\n\", itref->key, count, s, ms, us, clocks);\n\n        if(count > 1) {\n            us /= (double)count;\n            ms /= (double)count;\n            s /= (double)count;\n            clocks /= count;\n\n            printf(\"\\t%s[1]: %f s, %f ms, %f us, %lu clk\\r\\n\", itref->key, s, ms, us, clocks);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/toolbox/profiler.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Profiler Profiler;\n\nProfiler* profiler_alloc(void);\n\nvoid profiler_free(Profiler* profiler);\n\nvoid profiler_prealloc(Profiler* profiler, const char* key);\n\nvoid profiler_start(Profiler* profiler, const char* key);\n\nvoid profiler_stop(Profiler* profiler, const char* key);\n\nvoid profiler_dump(Profiler* profiler);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/property.c",
    "content": "#include \"property.h\"\n\n#include <core/check.h>\n\nvoid property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...) {\n    furi_check(ctx);\n    furi_string_reset(ctx->key);\n\n    va_list args;\n    va_start(args, nparts);\n\n    for(size_t i = 0; i < nparts; ++i) {\n        const char* keypart = va_arg(args, const char*);\n        furi_string_cat(ctx->key, keypart);\n        if(i < nparts - 1) {\n            furi_string_push_back(ctx->key, ctx->sep);\n        }\n    }\n\n    const char* value_str;\n\n    if(fmt) {\n        furi_string_vprintf(ctx->value, fmt, args);\n        value_str = furi_string_get_cstr(ctx->value);\n    } else {\n        // C string passthrough (no formatting)\n        value_str = va_arg(args, const char*);\n    }\n\n    va_end(args);\n\n    ctx->out(furi_string_get_cstr(ctx->key), value_str, ctx->last, ctx->context);\n}\n"
  },
  {
    "path": "lib/toolbox/property.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdbool.h>\n#include <core/string.h>\n\n/** Callback type called every time another key-value pair of device information is ready\n *\n * @param      key[in]      device information type identifier\n * @param      value[in]    device information value\n * @param      last[in]     whether the passed key-value pair is the last one\n * @param      context[in]  to pass to callback\n */\ntypedef void (*PropertyValueCallback)(const char* key, const char* value, bool last, void* context);\n\ntypedef struct {\n    FuriString* key; /**< key string buffer, must be initialised before use */\n    FuriString* value; /**< value string buffer, must be initialised before use */\n    PropertyValueCallback out; /**< output callback function */\n    char sep; /**< separator character between key parts */\n    bool last; /**< flag to indicate last element */\n    void* context; /**< user-defined context, passed through to out callback */\n} PropertyValueContext;\n\n/** Builds key and value strings and outputs them via a callback function\n *\n * @param       ctx[in]     local property context\n * @param       fmt[in]     value format, set to NULL to bypass formatting\n * @param       nparts[in]  number of key parts (separated by character)\n * @param       ...[in]     list of key parts followed by value\n */\nvoid property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/protocols/protocol.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stddef.h>\n#include <stdbool.h>\n#include <lib/toolbox/level_duration.h>\n#include <furi.h>\n\ntypedef void* (*ProtocolAlloc)(void);\ntypedef void (*ProtocolFree)(void* protocol);\ntypedef uint8_t* (*ProtocolGetData)(void* protocol);\n\ntypedef void (*ProtocolDecoderStart)(void* protocol);\ntypedef bool (*ProtocolDecoderFeed)(void* protocol, bool level, uint32_t duration);\n\ntypedef bool (*ProtocolEncoderStart)(void* protocol);\ntypedef LevelDuration (*ProtocolEncoderYield)(void* protocol);\n\ntypedef void (*ProtocolRenderData)(void* protocol, FuriString* result);\ntypedef bool (*ProtocolWriteData)(void* protocol, void* data);\n\ntypedef struct {\n    ProtocolDecoderStart start;\n    ProtocolDecoderFeed feed;\n} ProtocolDecoder;\n\ntypedef struct {\n    ProtocolEncoderStart start;\n    ProtocolEncoderYield yield;\n} ProtocolEncoder;\n\ntypedef struct {\n    const size_t data_size;\n    const char* name;\n    const char* manufacturer;\n    const uint32_t features;\n    const uint8_t validate_count;\n\n    ProtocolAlloc alloc;\n    ProtocolFree free;\n    ProtocolGetData get_data;\n    ProtocolDecoder decoder;\n    ProtocolEncoder encoder;\n    ProtocolRenderData render_uid;\n    ProtocolRenderData render_data;\n    ProtocolRenderData render_brief_data;\n    ProtocolWriteData write_data;\n} ProtocolBase;\n"
  },
  {
    "path": "lib/toolbox/protocols/protocol_dict.c",
    "content": "#include <furi.h>\n#include \"protocol_dict.h\"\n\nstruct ProtocolDict {\n    const ProtocolBase* const* base;\n    size_t count;\n    void* data[];\n};\n\nProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t count) {\n    furi_check(protocols);\n\n    ProtocolDict* dict = malloc(sizeof(ProtocolDict) + (sizeof(void*) * count));\n    dict->base = protocols;\n    dict->count = count;\n\n    for(size_t i = 0; i < dict->count; i++) {\n        dict->data[i] = dict->base[i]->alloc();\n    }\n\n    return dict;\n}\n\nvoid protocol_dict_free(ProtocolDict* dict) {\n    furi_check(dict);\n\n    for(size_t i = 0; i < dict->count; i++) {\n        dict->base[i]->free(dict->data[i]);\n    }\n\n    free(dict);\n}\n\nvoid protocol_dict_set_data(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    const uint8_t* data,\n    size_t data_size) {\n    furi_check(protocol_index < dict->count);\n    furi_check(dict->base[protocol_index]->get_data != NULL);\n\n    uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]);\n    size_t protocol_data_size = dict->base[protocol_index]->data_size;\n    furi_check(data_size >= protocol_data_size);\n    memcpy(protocol_data, data, protocol_data_size);\n}\n\nvoid protocol_dict_get_data(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    uint8_t* data,\n    size_t data_size) {\n    furi_check(protocol_index < dict->count);\n    furi_check(dict->base[protocol_index]->get_data != NULL);\n\n    uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]);\n    size_t protocol_data_size = dict->base[protocol_index]->data_size;\n    furi_check(data_size >= protocol_data_size);\n    memcpy(data, protocol_data, protocol_data_size);\n}\n\nsize_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    return dict->base[protocol_index]->data_size;\n}\n\nsize_t protocol_dict_get_max_data_size(ProtocolDict* dict) {\n    furi_check(dict);\n    size_t max_data_size = 0;\n    for(size_t i = 0; i < dict->count; i++) {\n        size_t data_size = dict->base[i]->data_size;\n        if(data_size > max_data_size) {\n            max_data_size = data_size;\n        }\n    }\n\n    return max_data_size;\n}\n\nconst char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    return dict->base[protocol_index]->name;\n}\n\nconst char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    return dict->base[protocol_index]->manufacturer;\n}\n\nvoid protocol_dict_decoders_start(ProtocolDict* dict) {\n    furi_check(dict);\n\n    for(size_t i = 0; i < dict->count; i++) {\n        ProtocolDecoderStart fn = dict->base[i]->decoder.start;\n\n        if(fn) {\n            fn(dict->data[i]);\n        }\n    }\n}\n\nuint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    return dict->base[protocol_index]->features;\n}\n\nProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration) {\n    furi_check(dict);\n\n    bool done = false;\n    ProtocolId ready_protocol_id = PROTOCOL_NO;\n\n    for(size_t i = 0; i < dict->count; i++) {\n        ProtocolDecoderFeed fn = dict->base[i]->decoder.feed;\n\n        if(fn) {\n            if(fn(dict->data[i], level, duration)) {\n                if(!done) {\n                    ready_protocol_id = i;\n                    done = true;\n                }\n            }\n        }\n    }\n\n    return ready_protocol_id;\n}\n\nProtocolId protocol_dict_decoders_feed_by_feature(\n    ProtocolDict* dict,\n    uint32_t feature,\n    bool level,\n    uint32_t duration) {\n    furi_check(dict);\n\n    bool done = false;\n    ProtocolId ready_protocol_id = PROTOCOL_NO;\n\n    for(size_t i = 0; i < dict->count; i++) {\n        uint32_t features = dict->base[i]->features;\n        if(features & feature) {\n            ProtocolDecoderFeed fn = dict->base[i]->decoder.feed;\n\n            if(fn) {\n                if(fn(dict->data[i], level, duration)) {\n                    if(!done) {\n                        ready_protocol_id = i;\n                        done = true;\n                    }\n                }\n            }\n        }\n    }\n\n    return ready_protocol_id;\n}\n\nProtocolId protocol_dict_decoders_feed_by_id(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    bool level,\n    uint32_t duration) {\n    furi_check(protocol_index < dict->count);\n\n    ProtocolId ready_protocol_id = PROTOCOL_NO;\n    ProtocolDecoderFeed fn = dict->base[protocol_index]->decoder.feed;\n\n    if(fn) {\n        if(fn(dict->data[protocol_index], level, duration)) {\n            ready_protocol_id = protocol_index;\n        }\n    }\n\n    return ready_protocol_id;\n}\n\nbool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    ProtocolEncoderStart fn = dict->base[protocol_index]->encoder.start;\n\n    if(fn) {\n        return fn(dict->data[protocol_index]);\n    } else {\n        return false;\n    }\n}\n\nLevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    ProtocolEncoderYield fn = dict->base[protocol_index]->encoder.yield;\n\n    if(fn) {\n        return fn(dict->data[protocol_index]);\n    } else {\n        return level_duration_reset();\n    }\n}\n\nvoid protocol_dict_render_uid(ProtocolDict* dict, FuriString* result, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    ProtocolRenderData fn = dict->base[protocol_index]->render_uid;\n\n    if(fn) {\n        fn(dict->data[protocol_index], result);\n    }\n}\n\nvoid protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    ProtocolRenderData fn = dict->base[protocol_index]->render_data;\n\n    if(fn) {\n        fn(dict->data[protocol_index], result);\n    }\n}\n\nvoid protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    ProtocolRenderData fn = dict->base[protocol_index]->render_brief_data;\n\n    if(fn) {\n        fn(dict->data[protocol_index], result);\n    }\n}\n\nuint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index) {\n    furi_check(protocol_index < dict->count);\n    return dict->base[protocol_index]->validate_count;\n}\n\nProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name) {\n    furi_check(dict);\n    furi_check(name);\n    for(size_t i = 0; i < dict->count; i++) {\n        if(strcmp(name, protocol_dict_get_name(dict, i)) == 0) {\n            return i;\n        }\n    }\n    return PROTOCOL_NO;\n}\n\nbool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data) {\n    furi_check(protocol_index < dict->count);\n    ProtocolWriteData fn = dict->base[protocol_index]->write_data;\n\n    furi_check(fn);\n    return fn(dict->data[protocol_index], data);\n}\n"
  },
  {
    "path": "lib/toolbox/protocols/protocol_dict.h",
    "content": "#pragma once\n#include \"protocol.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct ProtocolDict ProtocolDict;\n\ntypedef int32_t ProtocolId;\n\n#define PROTOCOL_NO           (-1)\n#define PROTOCOL_ALL_FEATURES (0xFFFFFFFF)\n\nProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t protocol_count);\n\nvoid protocol_dict_free(ProtocolDict* dict);\n\nvoid protocol_dict_set_data(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    const uint8_t* data,\n    size_t data_size);\n\nvoid protocol_dict_get_data(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    uint8_t* data,\n    size_t data_size);\n\nsize_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index);\n\nsize_t protocol_dict_get_max_data_size(ProtocolDict* dict);\n\nconst char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index);\n\nconst char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index);\n\nvoid protocol_dict_decoders_start(ProtocolDict* dict);\n\nuint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index);\n\nProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration);\n\nProtocolId protocol_dict_decoders_feed_by_feature(\n    ProtocolDict* dict,\n    uint32_t feature,\n    bool level,\n    uint32_t duration);\n\nProtocolId protocol_dict_decoders_feed_by_id(\n    ProtocolDict* dict,\n    size_t protocol_index,\n    bool level,\n    uint32_t duration);\n\nbool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index);\n\nLevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index);\n\nvoid protocol_dict_render_uid(ProtocolDict* dict, FuriString* result, size_t protocol_index);\n\nvoid protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index);\n\nvoid protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, size_t protocol_index);\n\nuint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index);\n\nProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name);\n\nbool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/pulse_joiner.c",
    "content": "#include \"pulse_joiner.h\"\n#include <furi.h>\n\n#define PULSE_MAX_COUNT 6\n\ntypedef struct {\n    bool polarity;\n    uint16_t time;\n} Pulse;\n\nstruct PulseJoiner {\n    size_t pulse_index;\n    Pulse pulses[PULSE_MAX_COUNT];\n};\n\nPulseJoiner* pulse_joiner_alloc(void) {\n    PulseJoiner* pulse_joiner = malloc(sizeof(PulseJoiner));\n\n    pulse_joiner->pulse_index = 0;\n    for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) {\n        pulse_joiner->pulses[i].polarity = false;\n        pulse_joiner->pulses[i].time = 0;\n    }\n\n    return pulse_joiner;\n}\n\nvoid pulse_joiner_free(PulseJoiner* pulse_joiner) {\n    free(pulse_joiner);\n}\n\nbool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse) {\n    bool result = false;\n    furi_check((pulse_joiner->pulse_index + 1) < PULSE_MAX_COUNT);\n\n    if(polarity == false && pulse_joiner->pulse_index == 0) {\n        // first negative pulse is omitted\n\n    } else {\n        pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = polarity;\n        pulse_joiner->pulses[pulse_joiner->pulse_index].time = pulse;\n        pulse_joiner->pulse_index++;\n    }\n\n    if(period > pulse) {\n        pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = !polarity;\n        pulse_joiner->pulses[pulse_joiner->pulse_index].time = period - pulse;\n        pulse_joiner->pulse_index++;\n    }\n\n    if(pulse_joiner->pulse_index >= 4) {\n        // we know that first pulse is always high\n        // so we wait 2 edges, hi2low and next low2hi\n\n        uint8_t edges_count = 0;\n        bool last_polarity = pulse_joiner->pulses[0].polarity;\n\n        for(uint8_t i = 1; i < pulse_joiner->pulse_index; i++) {\n            if(pulse_joiner->pulses[i].polarity != last_polarity) {\n                edges_count++;\n                last_polarity = pulse_joiner->pulses[i].polarity;\n            }\n        }\n\n        if(edges_count >= 2) {\n            result = true;\n        }\n    }\n\n    return result;\n}\n\nvoid pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse) {\n    furi_check(pulse_joiner->pulse_index <= (PULSE_MAX_COUNT + 1));\n\n    uint16_t tmp_period = 0;\n    uint16_t tmp_pulse = 0;\n    uint8_t edges_count = 0;\n    bool last_polarity = pulse_joiner->pulses[0].polarity;\n    uint8_t next_fist_pulse = 0;\n\n    for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) {\n        // count edges\n        if(pulse_joiner->pulses[i].polarity != last_polarity) {\n            edges_count++;\n            last_polarity = pulse_joiner->pulses[i].polarity;\n        }\n\n        // wait for 2 edges\n        if(edges_count == 2) {\n            next_fist_pulse = i;\n            break;\n        }\n\n        // sum pulse time\n        if(pulse_joiner->pulses[i].polarity) {\n            tmp_period += pulse_joiner->pulses[i].time;\n            tmp_pulse += pulse_joiner->pulses[i].time;\n        } else {\n            tmp_period += pulse_joiner->pulses[i].time;\n        }\n        pulse_joiner->pulse_index--;\n    }\n\n    *period = tmp_period;\n    *pulse = tmp_pulse;\n\n    // remove counted periods and shift data\n    for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) {\n        if((next_fist_pulse + i) < PULSE_MAX_COUNT) {\n            pulse_joiner->pulses[i].polarity = pulse_joiner->pulses[next_fist_pulse + i].polarity;\n            pulse_joiner->pulses[i].time = pulse_joiner->pulses[next_fist_pulse + i].time;\n        } else {\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/toolbox/pulse_joiner.h",
    "content": "#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct PulseJoiner PulseJoiner;\n\n/**\n * @brief Alloc PulseJoiner\n * \n * @return PulseJoiner* \n */\nPulseJoiner* pulse_joiner_alloc(void);\n\n/**\n * @brief Free PulseJoiner\n * \n * @param pulse_joiner \n */\nvoid pulse_joiner_free(PulseJoiner* pulse_joiner);\n\n/**\n * @brief Push timer pulse. First negative pulse is ommited.\n * \n * @param polarity pulse polarity: true = high2low, false = low2high\n * @param period overall period time in timer clicks\n * @param pulse pulse time in timer clicks\n * \n * @return true - next pulse can and must be popped immediatly\n */\nbool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse);\n\n/**\n * @brief Get the next timer pulse. Call only if push_pulse returns true.\n * \n * @param period overall period time in timer clicks\n * @param pulse pulse time in timer clicks\n */\nvoid pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/pulse_protocols/pulse_glue.c",
    "content": "#include \"pulse_glue.h\"\n\nstruct PulseGlue {\n    int32_t hi_period;\n    int32_t low_period;\n    int32_t next_hi_period;\n};\n\nPulseGlue* pulse_glue_alloc(void) {\n    PulseGlue* pulse_glue = malloc(sizeof(PulseGlue));\n    pulse_glue_reset(pulse_glue);\n    return pulse_glue;\n}\n\nvoid pulse_glue_free(PulseGlue* pulse_glue) {\n    free(pulse_glue);\n}\n\nvoid pulse_glue_reset(PulseGlue* pulse_glue) {\n    pulse_glue->hi_period = 0;\n    pulse_glue->low_period = 0;\n    pulse_glue->next_hi_period = 0;\n}\n\nbool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length) {\n    bool pop_ready = false;\n    if(polarity) {\n        if(pulse_glue->low_period == 0) {\n            // stage 1, accumulate hi period\n            pulse_glue->hi_period += length;\n        } else {\n            // stage 3, accumulate next hi period and be ready for pulse_glue_pop\n            pulse_glue->next_hi_period = length;\n\n            // data is ready\n            pop_ready = true;\n        }\n    } else {\n        if(pulse_glue->hi_period != 0) {\n            // stage 2, accumulate low period\n            pulse_glue->low_period += length;\n        }\n    }\n\n    return pop_ready;\n}\n\nvoid pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period) {\n    *length = pulse_glue->hi_period + pulse_glue->low_period;\n    *period = pulse_glue->hi_period;\n\n    pulse_glue->hi_period = pulse_glue->next_hi_period;\n    pulse_glue->low_period = 0;\n    pulse_glue->next_hi_period = 0;\n}\n"
  },
  {
    "path": "lib/toolbox/pulse_protocols/pulse_glue.h",
    "content": "/**\n * @file pulse_glue.h\n * \n * Simple tool to glue separated pulses to corret \n */\n#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct PulseGlue PulseGlue;\n\nPulseGlue* pulse_glue_alloc(void);\nvoid pulse_glue_free(PulseGlue* pulse_glue);\nvoid pulse_glue_reset(PulseGlue* pulse_glue);\n\nbool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length);\nvoid pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/saved_struct.c",
    "content": "#include \"saved_struct.h\"\n#include <furi.h>\n#include <stdint.h>\n#include <storage/storage.h>\n\n#define TAG \"SavedStruct\"\n\ntypedef struct {\n    uint8_t magic;\n    uint8_t version;\n    uint8_t checksum;\n    uint8_t flags;\n    uint32_t timestamp;\n} SavedStructHeader;\n\nbool saved_struct_save(\n    const char* path,\n    const void* data,\n    size_t size,\n    uint8_t magic,\n    uint8_t version) {\n    furi_check(path);\n    furi_check(data);\n    furi_check(size);\n    SavedStructHeader header;\n\n    FURI_LOG_I(TAG, \"Saving \\\"%s\\\"\", path);\n\n    // Store\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n    bool result = true;\n    bool saved = storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS);\n    if(!saved) {\n        FURI_LOG_E(\n            TAG, \"Open failed \\\"%s\\\". Error: \\'%s\\'\", path, storage_file_get_error_desc(file));\n        result = false;\n    }\n\n    if(result) {\n        // Calculate checksum\n        uint8_t checksum = 0;\n        const uint8_t* source = data;\n        for(size_t i = 0; i < size; i++) {\n            checksum += source[i];\n        }\n        // Set header\n        header.magic = magic;\n        header.version = version;\n        header.checksum = checksum;\n        header.flags = 0;\n        header.timestamp = 0;\n\n        size_t bytes_count = storage_file_write(file, &header, sizeof(header));\n        bytes_count += storage_file_write(file, data, size);\n\n        if(bytes_count != (size + sizeof(header))) {\n            FURI_LOG_E(\n                TAG, \"Write failed \\\"%s\\\". Error: \\'%s\\'\", path, storage_file_get_error_desc(file));\n            result = false;\n        }\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n    return result;\n}\n\nbool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version) {\n    furi_check(path);\n    furi_check(data);\n    furi_check(size);\n\n    FURI_LOG_I(TAG, \"Loading \\\"%s\\\"\", path);\n\n    SavedStructHeader header;\n\n    uint8_t* data_read = malloc(size);\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n    bool result = true;\n    bool loaded = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING);\n    if(!loaded) {\n        FURI_LOG_E(\n            TAG, \"Failed to read \\\"%s\\\". Error: %s\", path, storage_file_get_error_desc(file));\n        result = false;\n    }\n\n    if(result) {\n        size_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader));\n        bytes_count += storage_file_read(file, data_read, size);\n\n        if(bytes_count != (sizeof(SavedStructHeader) + size)) {\n            FURI_LOG_E(TAG, \"Size mismatch of file \\\"%s\\\"\", path);\n            result = false;\n        }\n    }\n\n    if(result && (header.magic != magic || header.version != version)) {\n        FURI_LOG_E(\n            TAG,\n            \"Magic(%d != %d) or Version(%d != %d) mismatch of file \\\"%s\\\"\",\n            header.magic,\n            magic,\n            header.version,\n            version,\n            path);\n        result = false;\n    }\n\n    if(result) {\n        uint8_t checksum = 0;\n        const uint8_t* source = (const uint8_t*)data_read;\n        for(size_t i = 0; i < size; i++) {\n            checksum += source[i];\n        }\n\n        if(header.checksum != checksum) {\n            FURI_LOG_E(\n                TAG, \"Checksum(%d != %d) mismatch of file \\\"%s\\\"\", header.checksum, checksum, path);\n            result = false;\n        }\n    }\n\n    if(result) {\n        memcpy(data, data_read, size);\n    }\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n    free(data_read);\n\n    return result;\n}\n\nbool saved_struct_get_metadata(\n    const char* path,\n    uint8_t* magic,\n    uint8_t* version,\n    size_t* payload_size) {\n    furi_check(path);\n\n    SavedStructHeader header;\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    File* file = storage_file_alloc(storage);\n\n    bool result = false;\n    do {\n        if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n            FURI_LOG_E(\n                TAG, \"Failed to read \\\"%s\\\". Error: %s\", path, storage_file_get_error_desc(file));\n            break;\n        }\n\n        if(storage_file_read(file, &header, sizeof(SavedStructHeader)) !=\n           sizeof(SavedStructHeader)) {\n            FURI_LOG_E(TAG, \"Failed to read header\");\n            break;\n        }\n\n        if(magic) {\n            *magic = header.magic;\n        }\n        if(version) {\n            *version = header.version;\n        }\n        if(payload_size) {\n            uint64_t file_size = storage_file_size(file);\n            *payload_size = file_size - sizeof(SavedStructHeader);\n        }\n\n        result = true;\n    } while(false);\n\n    storage_file_close(file);\n    storage_file_free(file);\n    furi_record_close(RECORD_STORAGE);\n\n    return result;\n}\n"
  },
  {
    "path": "lib/toolbox/saved_struct.h",
    "content": "/**\n * @file saved_struct.h\n * @brief SavedStruct - data serialization/de-serialization\n * \n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Load data from the file in saved structure format\n *\n * @param[in]  path     The path to the file\n * @param[out] data     Pointer to the memory where to load data\n * @param[in]  size     The size of the data\n * @param[in]  magic    The magic to embed into metadata\n * @param[in]  version  The version to embed into metadata\n *\n * @return     true on success, false otherwise\n */\nbool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version);\n\n/** Save data in saved structure format\n *\n * @param[in]  path     The path to the file\n * @param[in]  data     Pointer to the memory where data\n * @param[in]  size     The size of the data\n * @param[in]  magic    The magic to embed into metadata\n * @param[in]  version  The version to embed into metadata\n *\n * @return     true on success, false otherwise\n */\nbool saved_struct_save(\n    const char* path,\n    const void* data,\n    size_t size,\n    uint8_t magic,\n    uint8_t version);\n\n/** Get SavedStructure file metadata\n *\n * @param[in]  path          The path to the file\n * @param[out] magic         Pointer to store magic or NULL if you don't need it\n * @param[out] version       Pointer to store version or NULL if you don't need\n *                           it\n * @param[out] payload_size  Pointer to store payload size or NULL if you don't\n *                           need it\n *\n * @return     true on success, false otherwise\n */\nbool saved_struct_get_metadata(\n    const char* path,\n    uint8_t* magic,\n    uint8_t* version,\n    size_t* payload_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/settings_helpers/submenu_based.c",
    "content": "#include \"submenu_based.h\"\n#include <archive/helpers/archive_favorites.h>\n\nstruct SubmenuSettingsHelper {\n    const SubmenuSettingsHelperDescriptor* descriptor;\n    ViewDispatcher* view_dispatcher;\n    SceneManager* scene_manager;\n    Submenu* submenu;\n    uint32_t submenu_view_id;\n    uint32_t main_scene_id;\n};\n\nSubmenuSettingsHelper*\n    submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor) {\n    furi_check(descriptor);\n    SubmenuSettingsHelper* helper = malloc(sizeof(SubmenuSettingsHelper));\n    helper->descriptor = descriptor;\n    return helper;\n}\n\nvoid submenu_settings_helpers_assign_objects(\n    SubmenuSettingsHelper* helper,\n    ViewDispatcher* view_dispatcher,\n    SceneManager* scene_manager,\n    Submenu* submenu,\n    uint32_t submenu_view_id,\n    uint32_t main_scene_id) {\n    furi_check(helper);\n    furi_check(view_dispatcher);\n    furi_check(scene_manager);\n    furi_check(submenu);\n    helper->view_dispatcher = view_dispatcher;\n    helper->scene_manager = scene_manager;\n    helper->submenu = submenu;\n    helper->submenu_view_id = submenu_view_id;\n    helper->main_scene_id = main_scene_id;\n}\n\nvoid submenu_settings_helpers_free(SubmenuSettingsHelper* helper) {\n    free(helper);\n}\n\nbool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg) {\n    furi_check(helper);\n    if(!arg) return false;\n\n    const char* option = arg;\n    for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {\n        if(strcmp(helper->descriptor->options[i].name, option) == 0) {\n            scene_manager_next_scene(\n                helper->scene_manager, helper->descriptor->options[i].scene_id);\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic void\n    submenu_settings_helpers_callback(void* context, InputType input_type, uint32_t index) {\n    SubmenuSettingsHelper* helper = context;\n    if(input_type == InputTypeShort) {\n        view_dispatcher_send_custom_event(helper->view_dispatcher, index);\n    } else if(input_type == InputTypeLong) {\n        archive_favorites_handle_setting_pin_unpin(\n            helper->descriptor->app_name, helper->descriptor->options[index].name);\n    }\n}\n\nvoid submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper) {\n    furi_check(helper);\n    for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {\n        submenu_add_item_ex(\n            helper->submenu,\n            helper->descriptor->options[i].name,\n            i,\n            submenu_settings_helpers_callback,\n            helper);\n    }\n\n    submenu_set_selected_item(\n        helper->submenu,\n        scene_manager_get_scene_state(helper->scene_manager, helper->main_scene_id));\n    view_dispatcher_switch_to_view(helper->view_dispatcher, helper->submenu_view_id);\n}\n\nbool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event) {\n    furi_check(helper);\n\n    if(event.type == SceneManagerEventTypeCustom) {\n        scene_manager_next_scene(\n            helper->scene_manager, helper->descriptor->options[event.event].scene_id);\n        scene_manager_set_scene_state(helper->scene_manager, helper->main_scene_id, event.event);\n        return true;\n    }\n\n    return false;\n}\n\nvoid submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper) {\n    furi_check(helper);\n    submenu_reset(helper->submenu);\n}\n"
  },
  {
    "path": "lib/toolbox/settings_helpers/submenu_based.h",
    "content": "#include <gui/modules/submenu.h>\n#include <gui/view_dispatcher.h>\n#include <gui/scene_manager.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*SubmenuSettingsHelpherCallback)(void* context, uint32_t index);\n\ntypedef struct {\n    const char* name;\n    uint32_t scene_id;\n} SubmenuSettingsHelperOption;\n\ntypedef struct {\n    const char* app_name;\n    size_t options_cnt;\n    SubmenuSettingsHelperOption options[];\n} SubmenuSettingsHelperDescriptor;\n\ntypedef struct SubmenuSettingsHelper SubmenuSettingsHelper;\n\n/**\n * @brief Allocates a submenu-based settings helper\n * @param descriptor settings descriptor\n */\nSubmenuSettingsHelper*\n    submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor);\n\n/**\n * @brief Assigns dynamic objects to the submenu-based settings helper\n * @param helper          helper object\n * @param view_dispatcher ViewDispatcher\n * @param scene_manager   SceneManager\n * @param submenu         Submenu\n * @param submenu_view_id Submenu view id in the ViewDispatcher\n * @param main_scene_id   Main scene id in the SceneManager\n */\nvoid submenu_settings_helpers_assign_objects(\n    SubmenuSettingsHelper* helper,\n    ViewDispatcher* view_dispatcher,\n    SceneManager* scene_manager,\n    Submenu* submenu,\n    uint32_t submenu_view_id,\n    uint32_t main_scene_id);\n\n/**\n * @brief Frees a submenu-based settings helper\n * @param helper helper object\n */\nvoid submenu_settings_helpers_free(SubmenuSettingsHelper* helper);\n\n/**\n * @brief App start callback for the submenu-based settings helper\n * \n * If an argument containing one of the options was provided, launches the\n * corresponding scene.\n * \n * @param helper helper object\n * @param arg app argument, may be NULL\n * @returns true if a setting name was provided in the argument, false if normal\n *          app operation shall commence\n */\nbool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg);\n\n/**\n * @brief Main scene enter callback for the submenu-based settings helper\n * @param helper helper object\n */\nvoid submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper);\n\n/**\n * @brief Main scene event callback for the submenu-based settings helper\n * @param helper helper object\n * @param event  event data\n * @returns true if the event was consumed, false otherwise\n */\nbool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event);\n\n/**\n * @brief Main scene exit callback for the submenu-based settings helper\n * @param helper helper object\n */\nvoid submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/simple_array.c",
    "content": "#include \"simple_array.h\"\n\n#include <furi.h>\n\nstruct SimpleArray {\n    const SimpleArrayConfig* config;\n    SimpleArrayElement* data;\n    uint32_t count;\n};\n\nSimpleArray* simple_array_alloc(const SimpleArrayConfig* config) {\n    SimpleArray* instance = malloc(sizeof(SimpleArray));\n    instance->config = config;\n    return instance;\n}\n\nvoid simple_array_free(SimpleArray* instance) {\n    furi_check(instance);\n\n    simple_array_reset(instance);\n    free(instance);\n}\n\nvoid simple_array_init(SimpleArray* instance, uint32_t count) {\n    furi_check(instance);\n    furi_check(count > 0);\n\n    simple_array_reset(instance);\n\n    instance->data = malloc(count * instance->config->type_size);\n    instance->count = count;\n\n    SimpleArrayInit init = instance->config->init;\n    if(init) {\n        for(uint32_t i = 0; i < instance->count; ++i) {\n            init(simple_array_get(instance, i));\n        }\n    }\n}\n\nvoid simple_array_reset(SimpleArray* instance) {\n    furi_check(instance);\n\n    if(instance->data) {\n        SimpleArrayReset reset = instance->config->reset;\n\n        if(reset) {\n            for(uint32_t i = 0; i < instance->count; ++i) {\n                reset(simple_array_get(instance, i));\n            }\n        }\n\n        free(instance->data);\n\n        instance->count = 0;\n        instance->data = NULL;\n    }\n}\n\nvoid simple_array_copy(SimpleArray* instance, const SimpleArray* other) {\n    furi_check(instance);\n    furi_check(other);\n    furi_check(instance->config == other->config);\n\n    simple_array_reset(instance);\n\n    if(other->count == 0) {\n        return;\n    }\n\n    simple_array_init(instance, other->count);\n\n    SimpleArrayCopy copy = instance->config->copy;\n    if(copy) {\n        for(uint32_t i = 0; i < other->count; ++i) {\n            copy(simple_array_get(instance, i), simple_array_cget(other, i));\n        }\n    } else {\n        memcpy(instance->data, other->data, other->count * instance->config->type_size);\n    }\n}\n\nbool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other) {\n    furi_check(instance);\n    furi_check(other);\n\n    // Equal if the same object\n    if(instance == other) return true;\n\n    return (instance->config == other->config) && (instance->count == other->count) &&\n           ((instance->data == other->data) || (instance->data == NULL) || (other->data == NULL) ||\n            (memcmp(instance->data, other->data, other->count) == 0));\n}\n\nuint32_t simple_array_get_count(const SimpleArray* instance) {\n    furi_check(instance);\n    return instance->count;\n}\n\nSimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index) {\n    furi_check(instance);\n    furi_check(index < instance->count);\n\n    return instance->data + index * instance->config->type_size;\n}\n\nconst SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index) {\n    furi_check(instance);\n    return simple_array_get((SimpleArrayElement*)instance, index);\n}\n\nSimpleArrayData* simple_array_get_data(SimpleArray* instance) {\n    furi_check(instance);\n    furi_check(instance->data);\n\n    return instance->data;\n}\n\nconst SimpleArrayData* simple_array_cget_data(const SimpleArray* instance) {\n    furi_check(instance);\n    return simple_array_get_data((SimpleArray*)instance);\n}\n\nconst SimpleArrayConfig simple_array_config_uint8_t = {\n    .init = NULL,\n    .copy = NULL,\n    .reset = NULL,\n    .type_size = sizeof(uint8_t),\n};\n"
  },
  {
    "path": "lib/toolbox/simple_array.h",
    "content": "/**\n * @file simple_array.h\n *\n * @brief This file provides a simple (non-type safe) array for elements with\n * (optional) in-place construction support.\n *\n * No append, remove or take operations are supported.\n * Instead, the user must call simple_array_init() in order to reserve the memory for n elements.\n * In case if init() is specified in the configuration, then the elements are constructed in-place.\n *\n * To clear all elements from the array, call simple_array_reset(), which will also call reset()\n * for each element in case if it was specified in the configuration. This is useful if a custom\n * destructor is required for a particular type. Calling simple_array_free() will also result in a\n * simple_array_reset() call automatically.\n *\n */\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct SimpleArray SimpleArray;\n\ntypedef void SimpleArrayData;\ntypedef void SimpleArrayElement;\n\ntypedef void (*SimpleArrayInit)(SimpleArrayElement* elem);\ntypedef void (*SimpleArrayReset)(SimpleArrayElement* elem);\ntypedef void (*SimpleArrayCopy)(SimpleArrayElement* elem, const SimpleArrayElement* other);\n\n/** Simple Array configuration structure. Defined per type. */\ntypedef struct {\n    SimpleArrayInit init; /**< Initialisation (in-place constructor) method. */\n    SimpleArrayReset reset; /**< Reset (custom destructor) method. */\n    SimpleArrayCopy copy; /**< Copy (custom copy-constructor) method. */\n    const size_t type_size; /** Type size, in bytes. */\n} SimpleArrayConfig;\n\n/**\n * Allocate a SimpleArray instance with the given configuration.\n *\n * @param [in] config Pointer to the type-specific configuration\n * @return Pointer to the allocated SimpleArray instance\n */\nSimpleArray* simple_array_alloc(const SimpleArrayConfig* config);\n\n/**\n * Free a SimpleArray instance and release its contents.\n *\n * @param [in] instance Pointer to the SimpleArray instance to be freed\n */\nvoid simple_array_free(SimpleArray* instance);\n\n/**\n * Initialise a SimpleArray instance by allocating additional space to contain\n * the requested number of elements.\n * If init() is specified in the config, then it is called for each element,\n * otherwise the data is filled with zeroes.\n *\n * @param [in] instance Pointer to the SimpleArray instance to be init'd\n * @param [in] count Number of elements to be allocated and init'd\n */\nvoid simple_array_init(SimpleArray* instance, uint32_t count);\n\n/**\n * Reset a SimpleArray instance and delete all of its elements.\n * If reset() is specified in the config, then it is called for each element,\n * otherwise the data is simply free()'d.\n *\n * @param [in] instance Pointer to the SimpleArray instance to be reset\n */\nvoid simple_array_reset(SimpleArray* instance);\n\n/**\n * Copy (duplicate) another SimpleArray instance to this one.\n * If copy() is specified in the config, then it is called for each element,\n * otherwise the data is simply memcpy()'d.\n *\n * @param [in] instance Pointer to the SimpleArray instance to copy to\n * @param [in] other Pointer to the SimpleArray instance to copy from\n */\nvoid simple_array_copy(SimpleArray* instance, const SimpleArray* other);\n\n/**\n * Check if another SimpleArray instance is equal (the same object or holds the\n * same data) to this one.\n *\n * @param [in] instance Pointer to the SimpleArray instance to be compared\n * @param [in] other Pointer to the SimpleArray instance to be compared\n * @return True if instances are considered equal, false otherwise\n */\nbool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other);\n\n/**\n * Get the count of elements currently contained in a SimpleArray instance.\n *\n * @param [in] instance Pointer to the SimpleArray instance to query the count from\n * @return Count of elements contained in the instance\n */\nuint32_t simple_array_get_count(const SimpleArray* instance);\n\n/**\n * Get a pointer to an element contained in a SimpleArray instance.\n *\n * @param [in] instance Pointer to the SimpleArray instance to get an element from\n * @param [in] index Index of the element in question. MUST be less than total element count\n * @return Pointer to the element specified by index\n */\nSimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index);\n\n/**\n * Get a const pointer to an element contained in a SimpleArray instance.\n *\n * @param [in] instance Pointer to the SimpleArray instance to get an element from\n * @param [in] index Index of the element in question. MUST be less than total element count\n * @return Const pointer to the element specified by index\n */\nconst SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index);\n\n/**\n * Get a pointer to the internal data of a SimpleArray instance.\n *\n * @param [in] instance Pointer to the SimpleArray instance to get the data of\n * @return Pointer to the instance's internal data\n */\nSimpleArrayData* simple_array_get_data(SimpleArray* instance);\n\n/**\n * Get a constant pointer to the internal data of a SimpleArray instance.\n *\n * @param [in] instance Pointer to the SimpleArray instance to get the data of\n * @return Constant pointer to the instance's internal data\n */\nconst SimpleArrayData* simple_array_cget_data(const SimpleArray* instance);\n\n// Standard preset configurations\n\n// Preset configuration for a byte(uint8_t) array.\nextern const SimpleArrayConfig simple_array_config_uint8_t;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/str_buffer.c",
    "content": "#include \"str_buffer.h\"\n\nconst char* str_buffer_make_owned_clone(StrBuffer* buffer, const char* str) {\n    char* owned = strdup(str);\n    buffer->n_owned_strings++;\n    buffer->owned_strings =\n        realloc(buffer->owned_strings, buffer->n_owned_strings * sizeof(const char*)); // -V701\n    buffer->owned_strings[buffer->n_owned_strings - 1] = owned;\n    return owned;\n}\n\nvoid str_buffer_clear_all_clones(StrBuffer* buffer) {\n    for(size_t i = 0; i < buffer->n_owned_strings; i++) {\n        free(buffer->owned_strings[i]);\n    }\n    free(buffer->owned_strings);\n    buffer->owned_strings = NULL;\n}\n"
  },
  {
    "path": "lib/toolbox/str_buffer.h",
    "content": "/**\n * @file str_buffer.h\n * \n * Allows you to create an owned clone of however many strings that you need,\n * then free all of them at once. Essentially the simplest possible append-only\n * unindexable array of owned C-style strings.\n */\n#pragma once\n\n#include <furi.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief StrBuffer instance\n * \n * Place this struct directly wherever you want, it doesn't have to be `alloc`ed\n * and `free`d.\n */\ntypedef struct {\n    char** owned_strings;\n    size_t n_owned_strings;\n} StrBuffer;\n\n/**\n * @brief Makes a owned duplicate of the provided string\n * \n * @param[in] buffer StrBuffer instance\n * @param[in] str    Input C-style string\n * \n * @returns C-style string that contains to be valid event after `str` becomes\n *          invalid\n */\nconst char* str_buffer_make_owned_clone(StrBuffer* buffer, const char* str);\n\n/**\n * @brief Clears all owned duplicates\n * \n * @param[in] buffer StrBuffer instance\n */\nvoid str_buffer_clear_all_clones(StrBuffer* buffer);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/buffered_file_stream.c",
    "content": "#include \"buffered_file_stream.h\"\n\n#include \"stream_i.h\"\n#include \"file_stream.h\"\n#include \"stream_cache.h\"\n\ntypedef struct {\n    Stream stream_base;\n    Stream* file_stream;\n    StreamCache* cache;\n    bool sync_pending;\n} BufferedFileStream;\n\nstatic void buffered_file_stream_free(BufferedFileStream* stream);\nstatic bool buffered_file_stream_eof(BufferedFileStream* stream);\nstatic void buffered_file_stream_clean(BufferedFileStream* stream);\nstatic bool\n    buffered_file_stream_seek(BufferedFileStream* stream, int32_t offset, StreamOffset offset_type);\nstatic size_t buffered_file_stream_tell(BufferedFileStream* stream);\nstatic size_t buffered_file_stream_size(BufferedFileStream* stream);\nstatic size_t\n    buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size);\nstatic size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size);\nstatic bool buffered_file_stream_delete_and_insert(\n    BufferedFileStream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx);\n\nstatic bool buffered_file_stream_flush(BufferedFileStream* stream);\nstatic bool buffered_file_stream_unread(BufferedFileStream* stream);\n\nconst StreamVTable buffered_file_stream_vtable = {\n    .free = (StreamFreeFn)buffered_file_stream_free,\n    .eof = (StreamEOFFn)buffered_file_stream_eof,\n    .clean = (StreamCleanFn)buffered_file_stream_clean,\n    .seek = (StreamSeekFn)buffered_file_stream_seek,\n    .tell = (StreamTellFn)buffered_file_stream_tell,\n    .size = (StreamSizeFn)buffered_file_stream_size,\n    .write = (StreamWriteFn)buffered_file_stream_write,\n    .read = (StreamReadFn)buffered_file_stream_read,\n    .delete_and_insert = (StreamDeleteAndInsertFn)buffered_file_stream_delete_and_insert,\n};\n\nStream* buffered_file_stream_alloc(Storage* storage) {\n    BufferedFileStream* stream = malloc(sizeof(BufferedFileStream));\n\n    stream->file_stream = file_stream_alloc(storage);\n    stream->cache = stream_cache_alloc();\n    stream->sync_pending = false;\n\n    stream->stream_base.vtable = &buffered_file_stream_vtable;\n    return (Stream*)stream;\n}\n\nbool buffered_file_stream_open(\n    Stream* _stream,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    furi_check(_stream);\n    BufferedFileStream* stream = (BufferedFileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable);\n    return file_stream_open(stream->file_stream, path, access_mode, open_mode);\n}\n\nbool buffered_file_stream_close(Stream* _stream) {\n    furi_check(_stream);\n    BufferedFileStream* stream = (BufferedFileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable);\n    bool success = false;\n    do {\n        if(!(stream->sync_pending ? buffered_file_stream_flush(stream) :\n                                    buffered_file_stream_unread(stream)))\n            break;\n        if(!file_stream_close(stream->file_stream)) break;\n        success = true;\n    } while(false);\n    return success;\n}\n\nbool buffered_file_stream_sync(Stream* _stream) {\n    furi_check(_stream);\n    BufferedFileStream* stream = (BufferedFileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable);\n    return stream->sync_pending ? buffered_file_stream_flush(stream) : true;\n}\n\nFS_Error buffered_file_stream_get_error(Stream* _stream) {\n    furi_check(_stream);\n    BufferedFileStream* stream = (BufferedFileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable);\n    return file_stream_get_error(stream->file_stream);\n}\n\nstatic void buffered_file_stream_free(BufferedFileStream* stream) {\n    furi_check(stream);\n    buffered_file_stream_sync((Stream*)stream);\n    stream_free(stream->file_stream);\n    stream_cache_free(stream->cache);\n    free(stream);\n}\n\nstatic bool buffered_file_stream_eof(BufferedFileStream* stream) {\n    bool ret;\n    const bool file_stream_eof = stream_eof(stream->file_stream);\n    const bool cache_at_end = stream_cache_at_end(stream->cache);\n    if(!stream->sync_pending) {\n        ret = file_stream_eof && cache_at_end;\n    } else {\n        const size_t remaining_size =\n            stream_size(stream->file_stream) - stream_tell(stream->file_stream);\n        ret = stream_cache_size(stream->cache) >=\n              (remaining_size ? cache_at_end : file_stream_eof);\n    }\n    return ret;\n}\n\nstatic void buffered_file_stream_clean(BufferedFileStream* stream) {\n    // Not syncing because data will be deleted anyway\n    stream->sync_pending = false;\n    stream_cache_drop(stream->cache);\n    stream_clean(stream->file_stream);\n}\n\nstatic bool buffered_file_stream_seek(\n    BufferedFileStream* stream,\n    int32_t offset,\n    StreamOffset offset_type) {\n    bool success = true;\n    int32_t new_offset = offset;\n\n    if(offset_type == StreamOffsetFromCurrent) {\n        new_offset -= stream_cache_seek(stream->cache, offset);\n        if(new_offset < 0) {\n            new_offset -= (int32_t)stream_cache_size(stream->cache);\n        }\n    }\n\n    if((new_offset != 0) || (offset_type != StreamOffsetFromCurrent)) {\n        if(stream->sync_pending) {\n            success = buffered_file_stream_sync((Stream*)stream);\n        } else {\n            stream_cache_drop(stream->cache);\n        }\n        if(success) {\n            success = stream_seek(stream->file_stream, new_offset, offset_type);\n        }\n    }\n\n    return success;\n}\n\nstatic size_t buffered_file_stream_tell(BufferedFileStream* stream) {\n    size_t pos = stream_tell(stream->file_stream) + stream_cache_pos(stream->cache);\n    if(!stream->sync_pending) {\n        pos -= stream_cache_size(stream->cache);\n    }\n    return pos;\n}\n\nstatic size_t buffered_file_stream_size(BufferedFileStream* stream) {\n    size_t size = stream_size(stream->file_stream);\n    if(stream->sync_pending) {\n        const size_t remaining_size = size - stream_tell(stream->file_stream);\n        const size_t cache_size = stream_cache_size(stream->cache);\n        if(cache_size > remaining_size) {\n            size += (cache_size - remaining_size);\n        }\n    }\n    return size;\n}\n\nstatic size_t\n    buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) {\n    size_t need_to_write = size;\n    do {\n        if(!stream->sync_pending) {\n            if(!buffered_file_stream_unread(stream)) break;\n        }\n        while(need_to_write) {\n            stream->sync_pending = true;\n            need_to_write -=\n                stream_cache_write(stream->cache, data + (size - need_to_write), need_to_write);\n            if(need_to_write) {\n                stream->sync_pending = false;\n                if(!stream_cache_flush(stream->cache, stream->file_stream)) break;\n            }\n        }\n    } while(false);\n    return size - need_to_write;\n}\n\nstatic size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) {\n    size_t need_to_read = size;\n    while(need_to_read) {\n        need_to_read -=\n            stream_cache_read(stream->cache, data + (size - need_to_read), need_to_read);\n        if(need_to_read) {\n            if(stream->sync_pending) {\n                if(!buffered_file_stream_flush(stream)) break;\n            }\n            if(!stream_cache_fill(stream->cache, stream->file_stream)) break;\n        }\n    }\n    return size - need_to_read;\n}\n\nstatic bool buffered_file_stream_delete_and_insert(\n    BufferedFileStream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx) {\n    bool success = false;\n    do {\n        if(!(stream->sync_pending ? buffered_file_stream_flush(stream) :\n                                    buffered_file_stream_unread(stream)))\n            break;\n        if(!stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx)) break;\n        success = true;\n    } while(false);\n    return success;\n}\n\n// Write the cache into the underlying stream and adjust seek position\nstatic bool buffered_file_stream_flush(BufferedFileStream* stream) {\n    bool success = false;\n    do {\n        const int32_t offset = stream_cache_size(stream->cache) - stream_cache_pos(stream->cache);\n        if(!stream_cache_flush(stream->cache, stream->file_stream)) break;\n        if(offset > 0) {\n            if(!stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent)) break;\n        }\n        success = true;\n    } while(false);\n    stream->sync_pending = false;\n    return success;\n}\n\n// Drop read cache and adjust the underlying stream seek position\nstatic bool buffered_file_stream_unread(BufferedFileStream* stream) {\n    bool success = true;\n    const size_t cache_size = stream_cache_size(stream->cache);\n    if(cache_size > 0) {\n        const size_t cache_pos = stream_cache_pos(stream->cache);\n        if(cache_pos < cache_size) {\n            const int32_t offset = cache_size - cache_pos;\n            success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent);\n        }\n        stream_cache_drop(stream->cache);\n    }\n    return success;\n}\n"
  },
  {
    "path": "lib/toolbox/stream/buffered_file_stream.h",
    "content": "#pragma once\n#include <stdlib.h>\n#include <storage/storage.h>\n#include \"stream.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Allocate a file stream with buffered read operations\n * @return Stream*\n */\nStream* buffered_file_stream_alloc(Storage* storage);\n\n/**\n * Opens an existing file or creates a new one.\n * @param stream pointer to file stream object.\n * @param path path to file\n * @param access_mode access mode from FS_AccessMode\n * @param open_mode open mode from FS_OpenMode\n * @return True on success, False on failure. You need to close the file even if the open operation failed.\n */\nbool buffered_file_stream_open(\n    Stream* stream,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode);\n\n/**\n * Closes the file.\n * @param stream pointer to file stream object.\n * @return True on success, False on failure.\n */\nbool buffered_file_stream_close(Stream* stream);\n\n/**\n * Forces write from cache to the underlying file.\n * @param stream pointer to file stream object.\n * @return True on success, False on failure.\n */\nbool buffered_file_stream_sync(Stream* stream);\n\n/**\n * Retrieves the error id from the file object\n * @param stream pointer to stream object.\n * @return FS_Error error id\n */\nFS_Error buffered_file_stream_get_error(Stream* stream);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/file_stream.c",
    "content": "#include \"stream.h\"\n#include \"stream_i.h\"\n#include \"file_stream.h\"\n\ntypedef struct {\n    Stream stream_base;\n    Storage* storage;\n    File* file;\n} FileStream;\n\nstatic void file_stream_free(FileStream* stream);\nstatic bool file_stream_eof(FileStream* stream);\nstatic void file_stream_clean(FileStream* stream);\nstatic bool file_stream_seek(FileStream* stream, int32_t offset, StreamOffset offset_type);\nstatic size_t file_stream_tell(FileStream* stream);\nstatic size_t file_stream_size(FileStream* stream);\nstatic size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size);\nstatic size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size);\nstatic bool file_stream_delete_and_insert(\n    FileStream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx);\n\nconst StreamVTable file_stream_vtable = {\n    .free = (StreamFreeFn)file_stream_free,\n    .eof = (StreamEOFFn)file_stream_eof,\n    .clean = (StreamCleanFn)file_stream_clean,\n    .seek = (StreamSeekFn)file_stream_seek,\n    .tell = (StreamTellFn)file_stream_tell,\n    .size = (StreamSizeFn)file_stream_size,\n    .write = (StreamWriteFn)file_stream_write,\n    .read = (StreamReadFn)file_stream_read,\n    .delete_and_insert = (StreamDeleteAndInsertFn)file_stream_delete_and_insert,\n};\n\nStream* file_stream_alloc(Storage* storage) {\n    furi_check(storage);\n\n    FileStream* stream = malloc(sizeof(FileStream));\n    stream->file = storage_file_alloc(storage);\n    stream->storage = storage;\n\n    stream->stream_base.vtable = &file_stream_vtable;\n    return (Stream*)stream;\n}\n\nbool file_stream_open(\n    Stream* _stream,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode) {\n    furi_check(_stream);\n    FileStream* stream = (FileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &file_stream_vtable);\n    return storage_file_open(stream->file, path, access_mode, open_mode);\n}\n\nbool file_stream_close(Stream* _stream) {\n    furi_check(_stream);\n    FileStream* stream = (FileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &file_stream_vtable);\n    return storage_file_close(stream->file);\n}\n\nFS_Error file_stream_get_error(Stream* _stream) {\n    furi_check(_stream);\n    FileStream* stream = (FileStream*)_stream;\n    furi_check(stream->stream_base.vtable == &file_stream_vtable);\n    return storage_file_get_error(stream->file);\n}\n\nstatic void file_stream_free(FileStream* stream) {\n    storage_file_free(stream->file);\n    free(stream);\n}\n\nstatic bool file_stream_eof(FileStream* stream) {\n    return storage_file_eof(stream->file);\n}\n\nstatic void file_stream_clean(FileStream* stream) {\n    storage_file_seek(stream->file, 0, true);\n    storage_file_truncate(stream->file);\n}\n\nstatic bool file_stream_seek(FileStream* stream, int32_t offset, StreamOffset offset_type) {\n    bool result = false;\n    size_t seek_position = 0;\n    size_t current_position = file_stream_tell(stream);\n    size_t size = file_stream_size(stream);\n\n    // calc offset and limit to bottom\n    switch(offset_type) {\n    case StreamOffsetFromCurrent: {\n        if((int32_t)(current_position + offset) >= 0) {\n            seek_position = current_position + offset;\n            result = true;\n        }\n    } break;\n    case StreamOffsetFromStart: {\n        if(offset >= 0) {\n            seek_position = offset;\n            result = true;\n        }\n    } break;\n    case StreamOffsetFromEnd: {\n        if((int32_t)(size + offset) >= 0) {\n            seek_position = size + offset;\n            result = true;\n        }\n    } break;\n    }\n\n    if(result) {\n        // limit to top\n        if((int32_t)(seek_position - size) > 0) {\n            storage_file_seek(stream->file, size, true);\n            result = false;\n        } else {\n            result = storage_file_seek(stream->file, seek_position, true);\n        }\n    } else {\n        storage_file_seek(stream->file, 0, true);\n    }\n\n    return result;\n}\n\nstatic size_t file_stream_tell(FileStream* stream) {\n    return storage_file_tell(stream->file);\n}\n\nstatic size_t file_stream_size(FileStream* stream) {\n    return storage_file_size(stream->file);\n}\n\nstatic size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size) {\n    return storage_file_write(stream->file, data, size);\n}\n\nstatic size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size) {\n    return storage_file_read(stream->file, data, size);\n}\n\nstatic bool file_stream_delete_and_insert(\n    FileStream* _stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx) {\n    bool result = false;\n    Stream* stream = (Stream*)_stream;\n\n    // open scratchpad\n    Stream* scratch_stream = file_stream_alloc(_stream->storage);\n\n    // TODO FL-3546: we need something like \"storage_open_tmpfile and storage_close_tmpfile\"\n    FuriString* scratch_name;\n    FuriString* tmp_name;\n    tmp_name = furi_string_alloc();\n    storage_get_next_filename(\n        _stream->storage, STORAGE_EXT_PATH_PREFIX, \".scratch\", \".pad\", tmp_name, 255);\n    scratch_name = furi_string_alloc_printf(EXT_PATH(\"%s.pad\"), furi_string_get_cstr(tmp_name));\n    furi_string_free(tmp_name);\n\n    do {\n        if(!file_stream_open(\n               scratch_stream,\n               furi_string_get_cstr(scratch_name),\n               FSAM_READ_WRITE,\n               FSOM_CREATE_NEW))\n            break;\n\n        size_t current_position = stream_tell(stream);\n        size_t file_size = stream_size(stream);\n\n        size_t size_to_delete = file_size - current_position;\n        size_to_delete = MIN(delete_size, size_to_delete);\n\n        size_t size_to_copy_before = current_position;\n        size_t size_to_copy_after = file_size - current_position - size_to_delete;\n\n        // copy file from 0 to insert position to scratchpad\n        if(!stream_rewind(stream)) break;\n        if(stream_copy(stream, scratch_stream, size_to_copy_before) != size_to_copy_before) break;\n\n        if(write_callback) {\n            if(!write_callback(scratch_stream, ctx)) break;\n        }\n        size_t new_position = stream_tell(scratch_stream);\n\n        // copy key file after insert position + size_to_delete to scratchpad\n        if(!stream_seek(stream, size_to_delete, StreamOffsetFromCurrent)) break;\n        if(stream_copy(stream, scratch_stream, size_to_copy_after) != size_to_copy_after) break;\n\n        size_t new_file_size = stream_size(scratch_stream);\n\n        // copy whole scratchpad file to the original file\n        if(!stream_rewind(stream)) break;\n        if(!stream_rewind(scratch_stream)) break;\n        if(stream_copy(scratch_stream, stream, new_file_size) != new_file_size) break;\n\n        // and truncate original file\n        if(!storage_file_truncate(_stream->file)) break;\n\n        // move seek pointer at insert end\n        if(!stream_seek(stream, new_position, StreamOffsetFromStart)) break;\n\n        result = true;\n    } while(false);\n\n    stream_free(scratch_stream);\n    storage_common_remove(_stream->storage, furi_string_get_cstr(scratch_name));\n    furi_string_free(scratch_name);\n\n    return result;\n}\n"
  },
  {
    "path": "lib/toolbox/stream/file_stream.h",
    "content": "#pragma once\n#include <stdlib.h>\n#include <storage/storage.h>\n#include \"stream.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Allocate file stream\n * @return Stream* \n */\nStream* file_stream_alloc(Storage* storage);\n\n/**\n * Opens an existing file or create a new one.\n * @param stream pointer to file stream object.\n * @param path path to file \n * @param access_mode access mode from FS_AccessMode \n * @param open_mode open mode from FS_OpenMode \n * @return success flag. You need to close the file even if the open operation failed.\n */\nbool file_stream_open(\n    Stream* stream,\n    const char* path,\n    FS_AccessMode access_mode,\n    FS_OpenMode open_mode);\n\n/**\n * Closes the file.\n * @param stream \n * @return true \n * @return false \n */\nbool file_stream_close(Stream* stream);\n\n/** \n * Retrieves the error id from the file object\n * @param stream pointer to stream object.\n * @return FS_Error error id\n */\nFS_Error file_stream_get_error(Stream* stream);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/stream.c",
    "content": "#include \"stream.h\"\n#include \"stream_i.h\"\n#include \"file_stream.h\"\n#include <core/check.h>\n#include <core/common_defines.h>\n\n#define STREAM_BUFFER_SIZE (32U)\n\nvoid stream_free(Stream* stream) {\n    furi_check(stream);\n    stream->vtable->free(stream);\n}\n\nvoid stream_clean(Stream* stream) {\n    furi_check(stream);\n    stream->vtable->clean(stream);\n}\n\nbool stream_eof(Stream* stream) {\n    furi_check(stream);\n    return stream->vtable->eof(stream);\n}\n\nbool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type) {\n    furi_check(stream);\n    return stream->vtable->seek(stream, offset, offset_type);\n}\n\nstatic bool stream_seek_to_char_forward(Stream* stream, char c) {\n    // Search is starting from seconds character\n    if(!stream_seek(stream, 1, StreamOffsetFromCurrent)) {\n        return false;\n    }\n\n    // Search character in a stream\n    bool result = false;\n    while(!result) {\n        uint8_t buffer[STREAM_BUFFER_SIZE] = {0};\n        size_t ret = stream_read(stream, buffer, STREAM_BUFFER_SIZE);\n        for(size_t i = 0; i < ret; i++) {\n            if(buffer[i] == c) {\n                stream_seek(stream, (int32_t)i - ret, StreamOffsetFromCurrent);\n                result = true;\n                break;\n            }\n        }\n        if(ret != STREAM_BUFFER_SIZE) break;\n    }\n    return result;\n}\n\nstatic bool stream_seek_to_char_backward(Stream* stream, char c) {\n    size_t anchor = stream_tell(stream);\n\n    // Special case, no previous characters\n    if(anchor == 0) {\n        return false;\n    }\n\n    bool result = false;\n    while(!result) {\n        // Seek back\n        uint8_t buffer[STREAM_BUFFER_SIZE] = {0};\n        size_t to_read = STREAM_BUFFER_SIZE;\n        if(to_read > anchor) {\n            to_read = anchor;\n        }\n\n        anchor -= to_read;\n        furi_check(stream_seek(stream, anchor, StreamOffsetFromStart));\n\n        size_t ret = stream_read(stream, buffer, to_read);\n        for(size_t i = 0; i < ret; i++) {\n            size_t cursor = ret - i - 1;\n            if(buffer[cursor] == c) {\n                result = true;\n                furi_check(stream_seek(stream, anchor + cursor, StreamOffsetFromStart));\n                break;\n            } else {\n            }\n        }\n        if(ret != STREAM_BUFFER_SIZE) break;\n    }\n    return result;\n}\n\nbool stream_seek_to_char(Stream* stream, char c, StreamDirection direction) {\n    furi_check(stream);\n\n    const size_t old_position = stream_tell(stream);\n\n    bool result = false;\n    if(direction == StreamDirectionForward) {\n        result = stream_seek_to_char_forward(stream, c);\n    } else if(direction == StreamDirectionBackward) {\n        result = stream_seek_to_char_backward(stream, c);\n    }\n\n    // Rollback\n    if(!result) {\n        stream_seek(stream, old_position, StreamOffsetFromStart);\n    }\n\n    return result;\n}\n\nsize_t stream_tell(Stream* stream) {\n    furi_check(stream);\n    return stream->vtable->tell(stream);\n}\n\nsize_t stream_size(Stream* stream) {\n    furi_check(stream);\n    return stream->vtable->size(stream);\n}\n\nsize_t stream_write(Stream* stream, const uint8_t* data, size_t size) {\n    furi_check(stream);\n    return stream->vtable->write(stream, data, size);\n}\n\nsize_t stream_read(Stream* stream, uint8_t* data, size_t size) {\n    furi_check(stream);\n    return stream->vtable->read(stream, data, size);\n}\n\nbool stream_delete_and_insert(\n    Stream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx) {\n    furi_check(stream);\n    return stream->vtable->delete_and_insert(stream, delete_size, write_callback, ctx);\n}\n\n/********************************** Some random helpers starts here **********************************/\n\ntypedef struct {\n    const uint8_t* data;\n    size_t size;\n} StreamWriteData;\n\nstatic bool stream_write_struct(Stream* stream, const void* context) {\n    furi_check(stream);\n    furi_check(context);\n    const StreamWriteData* write_data = context;\n    return stream_write(stream, write_data->data, write_data->size) == write_data->size;\n}\n\nbool stream_read_line(Stream* stream, FuriString* str_result) {\n    furi_check(stream);\n    furi_check(str_result);\n\n    furi_string_reset(str_result);\n    uint8_t buffer[STREAM_BUFFER_SIZE];\n\n    do {\n        uint16_t bytes_were_read = stream_read(stream, buffer, STREAM_BUFFER_SIZE);\n        if(bytes_were_read == 0) break;\n\n        bool result = false;\n        bool error = false;\n        for(uint16_t i = 0; i < bytes_were_read; i++) {\n            if(buffer[i] == '\\n') {\n                if(!stream_seek(stream, i - bytes_were_read + 1, StreamOffsetFromCurrent)) {\n                    error = true;\n                    break;\n                }\n                furi_string_push_back(str_result, buffer[i]);\n                result = true;\n                break;\n            } else if(buffer[i] == '\\r') {\n                // Ignore\n            } else {\n                furi_string_push_back(str_result, buffer[i]);\n            }\n        }\n\n        if(result || error) {\n            break;\n        }\n    } while(true);\n\n    return furi_string_size(str_result) != 0;\n}\n\nbool stream_rewind(Stream* stream) {\n    furi_check(stream);\n    return stream_seek(stream, 0, StreamOffsetFromStart);\n}\n\nsize_t stream_write_char(Stream* stream, char c) {\n    furi_check(stream);\n    return stream_write(stream, (const uint8_t*)&c, 1);\n}\n\nsize_t stream_write_string(Stream* stream, FuriString* string) {\n    furi_check(stream);\n    return stream_write(\n        stream, (const uint8_t*)furi_string_get_cstr(string), furi_string_size(string));\n}\n\nsize_t stream_write_cstring(Stream* stream, const char* string) {\n    furi_check(stream);\n    return stream_write(stream, (const uint8_t*)string, strlen(string));\n}\n\nsize_t stream_write_format(Stream* stream, const char* format, ...) {\n    furi_check(stream);\n    size_t size;\n    va_list args;\n    va_start(args, format);\n    size = stream_write_vaformat(stream, format, args);\n    va_end(args);\n    return size;\n}\n\nsize_t stream_write_vaformat(Stream* stream, const char* format, va_list args) {\n    furi_check(stream);\n    FuriString* data;\n    data = furi_string_alloc_vprintf(format, args);\n    size_t size = stream_write_string(stream, data);\n    furi_string_free(data);\n\n    return size;\n}\n\nbool stream_insert(Stream* stream, const uint8_t* data, size_t size) {\n    furi_check(stream);\n    StreamWriteData write_data = {.data = data, .size = size};\n    return stream_delete_and_insert(stream, 0, stream_write_struct, &write_data);\n}\n\nbool stream_insert_char(Stream* stream, char c) {\n    furi_check(stream);\n    return stream_delete_and_insert_char(stream, 0, c);\n}\n\nbool stream_insert_string(Stream* stream, FuriString* string) {\n    furi_check(stream);\n    return stream_delete_and_insert_string(stream, 0, string);\n}\n\nbool stream_insert_cstring(Stream* stream, const char* string) {\n    furi_check(stream);\n    return stream_delete_and_insert_cstring(stream, 0, string);\n}\n\nbool stream_insert_format(Stream* stream, const char* format, ...) {\n    furi_check(stream);\n    va_list args;\n    va_start(args, format);\n    bool result = stream_insert_vaformat(stream, format, args);\n    va_end(args);\n\n    return result;\n}\n\nbool stream_insert_vaformat(Stream* stream, const char* format, va_list args) {\n    furi_check(stream);\n    return stream_delete_and_insert_vaformat(stream, 0, format, args);\n}\n\nbool stream_delete_and_insert_char(Stream* stream, size_t delete_size, char c) {\n    furi_check(stream);\n    StreamWriteData write_data = {.data = (uint8_t*)&c, .size = 1};\n    return stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data);\n}\n\nbool stream_delete_and_insert_string(Stream* stream, size_t delete_size, FuriString* string) {\n    furi_check(stream);\n    StreamWriteData write_data = {\n        .data = (uint8_t*)furi_string_get_cstr(string), .size = furi_string_size(string)};\n    return stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data);\n}\n\nbool stream_delete_and_insert_cstring(Stream* stream, size_t delete_size, const char* string) {\n    furi_check(stream);\n    StreamWriteData write_data = {.data = (uint8_t*)string, .size = strlen(string)};\n    return stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data);\n}\n\nbool stream_delete_and_insert_format(Stream* stream, size_t delete_size, const char* format, ...) {\n    furi_check(stream);\n    va_list args;\n    va_start(args, format);\n    bool result = stream_delete_and_insert_vaformat(stream, delete_size, format, args);\n    va_end(args);\n\n    return result;\n}\n\nbool stream_delete_and_insert_vaformat(\n    Stream* stream,\n    size_t delete_size,\n    const char* format,\n    va_list args) {\n    furi_check(stream);\n\n    FuriString* data = furi_string_alloc_vprintf(format, args);\n    StreamWriteData write_data = {\n        .data = (uint8_t*)furi_string_get_cstr(data), .size = furi_string_size(data)};\n    bool result = stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data);\n    furi_string_free(data);\n\n    return result;\n}\n\nbool stream_delete(Stream* stream, size_t size) {\n    furi_check(stream);\n    return stream_delete_and_insert(stream, size, NULL, NULL);\n}\n\nsize_t stream_copy(Stream* stream_from, Stream* stream_to, size_t size) {\n    furi_check(stream_from);\n    furi_check(stream_to);\n\n    uint8_t* buffer = malloc(STREAM_CACHE_SIZE);\n    size_t copied = 0;\n\n    do {\n        size_t bytes_count = MIN(STREAM_CACHE_SIZE, size - copied);\n        if(bytes_count <= 0) {\n            break;\n        }\n\n        uint16_t bytes_were_read = stream_read(stream_from, buffer, bytes_count);\n        if(bytes_were_read != bytes_count) break;\n\n        uint16_t bytes_were_written = stream_write(stream_to, buffer, bytes_count);\n        if(bytes_were_written != bytes_count) break;\n\n        copied += bytes_count;\n    } while(true);\n\n    free(buffer);\n    return copied;\n}\n\nsize_t stream_copy_full(Stream* stream_from, Stream* stream_to) {\n    furi_check(stream_from);\n    furi_check(stream_to);\n\n    size_t was_written = 0;\n\n    do {\n        if(!stream_seek(stream_from, 0, StreamOffsetFromStart)) break;\n        if(!stream_seek(stream_to, 0, StreamOffsetFromStart)) break;\n        was_written = stream_copy(stream_from, stream_to, stream_size(stream_from));\n    } while(false);\n\n    return was_written;\n}\n\nbool stream_split(Stream* stream, Stream* stream_left, Stream* stream_right) {\n    furi_check(stream);\n\n    bool result = false;\n    size_t size = stream_size(stream);\n    size_t tell = stream_tell(stream);\n\n    do {\n        // copy right\n        if(stream_copy(stream, stream_right, size - tell) != (size - tell)) break;\n\n        // copy left\n        if(!stream_rewind(stream)) break;\n        if(stream_copy(stream, stream_left, tell) != tell) break;\n\n        // restore RW pointer\n        if(!stream_seek(stream, tell, StreamOffsetFromStart)) break;\n        result = true;\n    } while(false);\n\n    return result;\n}\n\nsize_t stream_load_from_file(Stream* stream, Storage* storage, const char* path) {\n    furi_check(stream);\n    furi_check(storage);\n\n    size_t was_written = 0;\n    Stream* file = file_stream_alloc(storage);\n\n    do {\n        if(!file_stream_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) break;\n        was_written = stream_copy(file, stream, stream_size(file));\n    } while(false);\n\n    stream_free(file);\n    return was_written;\n}\n\nsize_t stream_save_to_file(Stream* stream, Storage* storage, const char* path, FS_OpenMode mode) {\n    furi_check(stream);\n    furi_check(storage);\n\n    size_t was_written = 0;\n    Stream* file = file_stream_alloc(storage);\n\n    do {\n        if(!file_stream_open(file, path, FSAM_WRITE, mode)) break;\n        was_written = stream_copy(stream, file, stream_size(stream));\n    } while(false);\n\n    stream_free(file);\n    return was_written;\n}\n\nvoid stream_dump_data(Stream* stream) {\n    furi_check(stream);\n\n    size_t size = stream_size(stream);\n    size_t tell = stream_tell(stream);\n    printf(\"stream %p\\r\\n\", stream);\n    printf(\"size = %zu\\r\\n\", size);\n    printf(\"tell = %zu\\r\\n\", tell);\n    printf(\"DATA START\\r\\n\");\n    uint8_t* data = malloc(STREAM_CACHE_SIZE);\n    stream_rewind(stream);\n\n    while(true) {\n        size_t was_read = stream_read(stream, data, STREAM_CACHE_SIZE);\n        if(was_read == 0) break;\n\n        for(size_t i = 0; i < was_read; i++) {\n            printf(\"%c\", data[i]);\n        }\n    }\n\n    free(data);\n    printf(\"\\r\\n\");\n    printf(\"DATA END\\r\\n\");\n    stream_seek(stream, tell, StreamOffsetFromStart);\n}\n"
  },
  {
    "path": "lib/toolbox/stream/stream.h",
    "content": "#pragma once\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Stream Stream;\n\ntypedef enum {\n    StreamOffsetFromCurrent,\n    StreamOffsetFromStart,\n    StreamOffsetFromEnd,\n} StreamOffset;\n\ntypedef enum {\n    StreamDirectionForward,\n    StreamDirectionBackward,\n} StreamDirection;\n\ntypedef bool (*StreamWriteCB)(Stream* stream, const void* context);\n\n/**\n * Free Stream\n * @param stream Stream instance\n */\nvoid stream_free(Stream* stream);\n\n/**\n * Clean (empty) Stream\n * @param stream Stream instance\n */\nvoid stream_clean(Stream* stream);\n\n/**\n * Indicates that the RW pointer is at the end of the stream\n * @param stream Stream instance\n * @return true if RW pointer is at the end of the stream\n * @return false if RW pointer is not at the end of the stream\n */\nbool stream_eof(Stream* stream);\n\n/**\n * Moves the RW pointer.\n * @param stream Stream instance\n * @param offset how much to move the pointer\n * @param offset_type starting from what\n * @return true \n * @return false \n */\nbool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type);\n\n/** Seek to next occurrence of the character\n *\n * @param      stream     Pointer to the stream instance\n * @param[in]  c          The Character\n * @param[in]  direction  The Direction\n *\n * @return     true on success\n */\nbool stream_seek_to_char(Stream* stream, char c, StreamDirection direction);\n\n/**\n * Gets the value of the RW pointer\n * @param stream Stream instance\n * @return size_t value of the RW pointer\n */\nsize_t stream_tell(Stream* stream);\n\n/**\n * Gets the size of the stream\n * @param stream Stream instance\n * @return size_t size of the stream\n */\nsize_t stream_size(Stream* stream);\n\n/**\n * Write N bytes to the stream\n * @param stream Stream instance\n * @param data data to write\n * @param size size of data to be written\n * @return size_t how many bytes was written\n */\nsize_t stream_write(Stream* stream, const uint8_t* data, size_t size);\n\n/**\n * Read N bytes from stream\n * @param stream Stream instance\n * @param data data to be read\n * @param count size of data to be read\n * @return size_t how many bytes was read\n */\nsize_t stream_read(Stream* stream, uint8_t* data, size_t count);\n\n/**\n * Delete N chars from the stream and write data by calling write_callback(context)\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param write_callback write callback\n * @param context write callback context\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert(\n    Stream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* context);\n\n/********************************** Some random helpers starts here **********************************/\n\n/**\n * Read line from a stream (supports LF and CRLF line endings)\n * @param stream \n * @param str_result \n * @return true if line length is not zero\n * @return false otherwise\n */\nbool stream_read_line(Stream* stream, FuriString* str_result);\n\n/**\n * Moves the RW pointer to the start\n * @param stream Stream instance\n */\nbool stream_rewind(Stream* stream);\n\n/**\n * Write char to the stream\n * @param stream Stream instance\n * @param c char value\n * @return size_t how many bytes was written\n */\nsize_t stream_write_char(Stream* stream, char c);\n\n/**\n * Write string to the stream\n * @param stream Stream instance\n * @param string string value\n * @return size_t how many bytes was written\n */\nsize_t stream_write_string(Stream* stream, FuriString* string);\n\n/**\n * Write const char* to the stream\n * @param stream Stream instance\n * @param string c-string value\n * @return size_t how many bytes was written\n */\nsize_t stream_write_cstring(Stream* stream, const char* string);\n\n/**\n * Write formatted string to the stream\n * @param stream Stream instance\n * @param format \n * @param ... \n * @return size_t how many bytes was written\n */\nsize_t stream_write_format(Stream* stream, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/**\n * Write formatted string to the stream, va_list version\n * @param stream Stream instance\n * @param format \n * @param args \n * @return size_t how many bytes was written\n */\nsize_t stream_write_vaformat(Stream* stream, const char* format, va_list args);\n\n/**\n * Insert N chars to the stream, starting at the current pointer.\n * Data will be inserted, not overwritten, so the stream will be increased in size.\n * @param stream Stream instance\n * @param data data to be inserted\n * @param size size of data to be inserted\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert(Stream* stream, const uint8_t* data, size_t size);\n\n/**\n * Insert char to the stream\n * @param stream Stream instance\n * @param c char value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert_char(Stream* stream, char c);\n\n/**\n * Insert string to the stream\n * @param stream Stream instance\n * @param string string value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert_string(Stream* stream, FuriString* string);\n\n/**\n * Insert const char* to the stream\n * @param stream Stream instance\n * @param string c-string value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert_cstring(Stream* stream, const char* string);\n\n/**\n * Insert formatted string to the stream\n * @param stream Stream instance\n * @param format \n * @param ... \n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert_format(Stream* stream, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 2, 3)));\n\n/**\n * Insert formatted string to the stream, va_list version\n * @param stream Stream instance\n * @param format \n * @param args \n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_insert_vaformat(Stream* stream, const char* format, va_list args);\n\n/**\n * Delete N chars from the stream and insert char to the stream\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param c char value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert_char(Stream* stream, size_t delete_size, char c);\n\n/**\n * Delete N chars from the stream and insert string to the stream\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param string string value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert_string(Stream* stream, size_t delete_size, FuriString* string);\n\n/**\n * Delete N chars from the stream and insert const char* to the stream\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param string c-string value\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert_cstring(Stream* stream, size_t delete_size, const char* string);\n\n/**\n * Delete N chars from the stream and insert formatted string to the stream\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param format \n * @param ... \n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert_format(Stream* stream, size_t delete_size, const char* format, ...)\n    _ATTRIBUTE((__format__(__printf__, 3, 4)));\n\n/**\n * Delete N chars from the stream and insert formatted string to the stream, va_list version\n * @param stream Stream instance\n * @param delete_size size of data to be deleted\n * @param format \n * @param args \n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete_and_insert_vaformat(\n    Stream* stream,\n    size_t delete_size,\n    const char* format,\n    va_list args);\n\n/**\n * Remove N chars from the stream, starting at the current pointer.\n * The size may be larger than stream size, the stream will be cleared from current RW pointer to the end.\n * @param stream Stream instance\n * @param size how many chars need to be deleted\n * @return true if the operation was successful\n * @return false on error\n */\nbool stream_delete(Stream* stream, size_t size);\n\n/**\n * Copy data from one stream to another. Data will be copied from current RW pointer and to current RW pointer.\n * @param stream_from \n * @param stream_to \n * @param size \n * @return size_t \n */\nsize_t stream_copy(Stream* stream_from, Stream* stream_to, size_t size);\n\n/**\n * Copy data from one stream to another. Data will be copied from start of one stream and to start of other stream.\n * @param stream_from \n * @param stream_to \n * @return size_t \n */\nsize_t stream_copy_full(Stream* stream_from, Stream* stream_to);\n\n/**\n * Splits one stream into two others. The original stream will remain untouched.\n * @param stream \n * @param stream_left \n * @param stream_right \n * @return true \n * @return false \n */\nbool stream_split(Stream* stream, Stream* stream_left, Stream* stream_right);\n\n/**\n * Loads data to the stream from a file. Data will be loaded to the current RW pointer. RW pointer will be moved to the end of the stream.\n * @param stream Stream instance \n * @param storage \n * @param path \n * @return size_t \n */\nsize_t stream_load_from_file(Stream* stream, Storage* storage, const char* path);\n\n/**\n * Writes data from a stream to a file. Data will be saved starting from the current RW pointer. RW pointer will be moved to the end of the stream.\n * @param stream Stream instance \n * @param storage \n * @param path \n * @param mode \n * @return size_t \n */\nsize_t stream_save_to_file(Stream* stream, Storage* storage, const char* path, FS_OpenMode mode);\n\n/**\n * Dump stream inner data (size, RW position, content)\n * @param stream Stream instance \n */\nvoid stream_dump_data(Stream* stream);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/stream_cache.c",
    "content": "#include \"stream_cache.h\"\n\n#define STREAM_CACHE_MAX_SIZE 1024U\n\nstruct StreamCache {\n    uint8_t data[STREAM_CACHE_MAX_SIZE];\n    size_t data_size;\n    size_t position;\n};\n\nStreamCache* stream_cache_alloc(void) {\n    StreamCache* cache = malloc(sizeof(StreamCache));\n    cache->data_size = 0;\n    cache->position = 0;\n    return cache;\n}\nvoid stream_cache_free(StreamCache* cache) {\n    furi_assert(cache);\n    cache->data_size = 0;\n    cache->position = 0;\n    free(cache);\n}\n\nvoid stream_cache_drop(StreamCache* cache) {\n    cache->data_size = 0;\n    cache->position = 0;\n}\n\nbool stream_cache_at_end(StreamCache* cache) {\n    furi_assert(cache->data_size >= cache->position);\n    return cache->data_size == cache->position;\n}\n\nsize_t stream_cache_size(StreamCache* cache) {\n    return cache->data_size;\n}\n\nsize_t stream_cache_pos(StreamCache* cache) {\n    return cache->position;\n}\n\nsize_t stream_cache_fill(StreamCache* cache, Stream* stream) {\n    const size_t size_read = stream_read(stream, cache->data, STREAM_CACHE_MAX_SIZE);\n    cache->data_size = size_read;\n    cache->position = 0;\n    return size_read;\n}\n\nbool stream_cache_flush(StreamCache* cache, Stream* stream) {\n    const size_t size_written = stream_write(stream, cache->data, cache->data_size);\n    const bool success = (size_written == cache->data_size);\n    cache->data_size = 0;\n    cache->position = 0;\n    return success;\n}\n\nsize_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size) {\n    furi_assert(cache->data_size >= cache->position);\n    const size_t size_read = MIN(size, cache->data_size - cache->position);\n    if(size_read > 0) {\n        memcpy(data, cache->data + cache->position, size_read);\n        cache->position += size_read;\n    }\n    return size_read;\n}\n\nsize_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size) {\n    furi_assert(cache->data_size >= cache->position);\n    const size_t size_written = MIN(size, STREAM_CACHE_MAX_SIZE - cache->position);\n    if(size_written > 0) {\n        memcpy(cache->data + cache->position, data, size_written);\n        cache->position += size_written;\n        if(cache->position > cache->data_size) {\n            cache->data_size = cache->position;\n        }\n    }\n    return size_written;\n}\n\nint32_t stream_cache_seek(StreamCache* cache, int32_t offset) {\n    furi_assert(cache->data_size >= cache->position);\n    int32_t actual_offset = 0;\n\n    if(offset > 0) {\n        actual_offset = MIN(cache->data_size - cache->position, (size_t)offset);\n    } else if(offset < 0) {\n        actual_offset = MAX(-((int32_t)cache->position), offset);\n    }\n\n    cache->position += actual_offset;\n    return actual_offset;\n}\n"
  },
  {
    "path": "lib/toolbox/stream/stream_cache.h",
    "content": "#pragma once\n\n#include \"stream.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct StreamCache StreamCache;\n\n/**\n * Allocate stream cache.\n * @return StreamCache* pointer to a StreamCache instance\n */\nStreamCache* stream_cache_alloc(void);\n\n/**\n * Free stream cache.\n * @param cache Pointer to a StreamCache instance\n */\nvoid stream_cache_free(StreamCache* cache);\n\n/**\n * Drop the cache contents and set it to initial state.\n * @param cache Pointer to a StreamCache instance\n */\nvoid stream_cache_drop(StreamCache* cache);\n\n/**\n * Determine if the internal cursor is at end the end of cached data.\n * @param cache Pointer to a StreamCache instance\n * @return True if cursor is at end, otherwise false.\n */\nbool stream_cache_at_end(StreamCache* cache);\n\n/**\n * Get the current size of cached data.\n * @param cache Pointer to a StreamCache instance\n * @return Size of cached data.\n */\nsize_t stream_cache_size(StreamCache* cache);\n\n/**\n * Get the internal cursor position.\n * @param cache Pointer to a StreamCache instance\n * @return Cursor position inside the cache.\n */\nsize_t stream_cache_pos(StreamCache* cache);\n\n/**\n * Load the cache with new data from a stream.\n * @param cache Pointer to a StreamCache instance\n * @param stream Pointer to a Stream instance\n * @return Size of newly cached data.\n */\nsize_t stream_cache_fill(StreamCache* cache, Stream* stream);\n\n/**\n * Write as much cached data as possible to a stream.\n * @param cache Pointer to a StreamCache instance\n * @param stream Pointer to a Stream instance\n * @return True on success, False on failure.\n */\nbool stream_cache_flush(StreamCache* cache, Stream* stream);\n\n/**\n * Read cached data and advance the internal cursor.\n * @param cache Pointer to a StreamCache instance.\n * @param data Pointer to a data buffer. Must be initialized.\n * @param size Maximum size in bytes to read from the cache.\n * @return Actual size that was read.\n */\nsize_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size);\n\n/**\n * Write to cached data and advance the internal cursor.\n * @param cache Pointer to a StreamCache instance.\n * @param data Pointer to a data buffer.\n * @param size Maximum size in bytes to write to the cache.\n * @return Actual size that was written.\n */\nsize_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size);\n\n/**\n * Move the internal cursor relatively to its current position.\n * @param cache Pointer to a StreamCache instance.\n * @param offset Cursor offset.\n * @return Actual cursor offset. Equal to offset parameter on hit.\n */\nint32_t stream_cache_seek(StreamCache* cache, int32_t offset);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/stream_i.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include \"stream.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define STREAM_CACHE_SIZE 512u\n\ntypedef struct StreamVTable StreamVTable;\n\ntypedef void (*StreamFreeFn)(Stream* stream);\ntypedef bool (*StreamEOFFn)(Stream* stream);\ntypedef void (*StreamCleanFn)(Stream* stream);\ntypedef bool (*StreamSeekFn)(Stream* stream, int32_t offset, StreamOffset offset_type);\ntypedef size_t (*StreamTellFn)(Stream* stream);\ntypedef size_t (*StreamSizeFn)(Stream* stream);\ntypedef size_t (*StreamWriteFn)(Stream* stream, const uint8_t* data, size_t size);\ntypedef size_t (*StreamReadFn)(Stream* stream, uint8_t* data, size_t count);\ntypedef bool (*StreamDeleteAndInsertFn)(\n    Stream* stream,\n    size_t delete_size,\n    StreamWriteCB write_cb,\n    const void* ctx);\n\nstruct StreamVTable {\n    const StreamFreeFn free;\n    const StreamEOFFn eof;\n    const StreamCleanFn clean;\n    const StreamSeekFn seek;\n    const StreamTellFn tell;\n    const StreamSizeFn size;\n    const StreamWriteFn write;\n    const StreamReadFn read;\n    const StreamDeleteAndInsertFn delete_and_insert;\n};\n\nstruct Stream {\n    const StreamVTable* vtable;\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/stream/string_stream.c",
    "content": "#include \"stream.h\"\n#include \"stream_i.h\"\n#include \"string_stream.h\"\n#include <core/common_defines.h>\n\ntypedef struct {\n    Stream stream_base;\n    FuriString* string;\n    size_t index;\n} StringStream;\n\nstatic size_t string_stream_write_char(StringStream* stream, char c);\n\nstatic void string_stream_free(StringStream* stream);\nstatic bool string_stream_eof(StringStream* stream);\nstatic void string_stream_clean(StringStream* stream);\nstatic bool string_stream_seek(StringStream* stream, int32_t offset, StreamOffset offset_type);\nstatic size_t string_stream_tell(StringStream* stream);\nstatic size_t string_stream_size(StringStream* stream);\nstatic size_t string_stream_write(StringStream* stream, const char* data, size_t size);\nstatic size_t string_stream_read(StringStream* stream, char* data, size_t size);\nstatic bool string_stream_delete_and_insert(\n    StringStream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx);\n\nconst StreamVTable string_stream_vtable = {\n    .free = (StreamFreeFn)string_stream_free,\n    .eof = (StreamEOFFn)string_stream_eof,\n    .clean = (StreamCleanFn)string_stream_clean,\n    .seek = (StreamSeekFn)string_stream_seek,\n    .tell = (StreamTellFn)string_stream_tell,\n    .size = (StreamSizeFn)string_stream_size,\n    .write = (StreamWriteFn)string_stream_write,\n    .read = (StreamReadFn)string_stream_read,\n    .delete_and_insert = (StreamDeleteAndInsertFn)string_stream_delete_and_insert,\n};\n\nStream* string_stream_alloc(void) {\n    StringStream* stream = malloc(sizeof(StringStream));\n    stream->string = furi_string_alloc();\n    stream->index = 0;\n    stream->stream_base.vtable = &string_stream_vtable;\n    return (Stream*)stream;\n}\n\nstatic void string_stream_free(StringStream* stream) {\n    furi_string_free(stream->string);\n    free(stream);\n}\n\nstatic bool string_stream_eof(StringStream* stream) {\n    return string_stream_tell(stream) >= string_stream_size(stream);\n}\n\nstatic void string_stream_clean(StringStream* stream) {\n    stream->index = 0;\n    furi_string_reset(stream->string);\n}\n\nstatic bool string_stream_seek(StringStream* stream, int32_t offset, StreamOffset offset_type) {\n    bool result = true;\n    switch(offset_type) {\n    case StreamOffsetFromStart:\n        if(offset >= 0) {\n            stream->index = offset;\n        } else {\n            result = false;\n            stream->index = 0;\n        }\n        break;\n    case StreamOffsetFromCurrent:\n        if(((int32_t)stream->index + offset) >= 0) {\n            stream->index += offset;\n        } else {\n            result = false;\n            stream->index = 0;\n        }\n        break;\n    case StreamOffsetFromEnd:\n        if(((int32_t)furi_string_size(stream->string) + offset) >= 0) {\n            stream->index = furi_string_size(stream->string) + offset;\n        } else {\n            result = false;\n            stream->index = 0;\n        }\n        break;\n    }\n\n    int32_t diff = (stream->index - furi_string_size(stream->string));\n    if(diff > 0) {\n        stream->index -= diff;\n        result = false;\n    }\n\n    return result;\n}\n\nstatic size_t string_stream_tell(StringStream* stream) {\n    return stream->index;\n}\n\nstatic size_t string_stream_size(StringStream* stream) {\n    return furi_string_size(stream->string);\n}\n\nstatic size_t string_stream_write(StringStream* stream, const char* data, size_t size) {\n    // TODO FL-3544: can be optimized for edge cases\n    size_t i;\n    for(i = 0; i < size; i++) {\n        string_stream_write_char(stream, data[i]);\n    }\n\n    return i;\n}\n\nstatic size_t string_stream_read(StringStream* stream, char* data, size_t size) {\n    size_t write_index = 0;\n    const char* cstr = furi_string_get_cstr(stream->string);\n\n    if(!string_stream_eof(stream)) {\n        while(true) {\n            if(write_index >= size) break;\n\n            data[write_index] = cstr[stream->index];\n            write_index++;\n            string_stream_seek(stream, 1, StreamOffsetFromCurrent);\n            if(string_stream_eof(stream)) break;\n        }\n    }\n\n    return write_index;\n}\n\nstatic bool string_stream_delete_and_insert(\n    StringStream* stream,\n    size_t delete_size,\n    StreamWriteCB write_callback,\n    const void* ctx) {\n    bool result = true;\n\n    if(delete_size) {\n        size_t remain_size = string_stream_size(stream) - string_stream_tell(stream);\n        remain_size = MIN(delete_size, remain_size);\n\n        if(remain_size != 0) {\n            furi_string_replace_at(stream->string, stream->index, remain_size, \"\");\n        }\n    }\n\n    if(write_callback) {\n        FuriString* right;\n        right = furi_string_alloc_set(&furi_string_get_cstr(stream->string)[stream->index]);\n        furi_string_left(stream->string, string_stream_tell(stream));\n        result &= write_callback((Stream*)stream, ctx);\n        furi_string_cat(stream->string, right);\n        furi_string_free(right);\n    }\n\n    return result;\n}\n\n/**\n * Write to string stream helper\n * @param stream \n * @param c \n * @return size_t \n */\nstatic size_t string_stream_write_char(StringStream* stream, char c) {\n    if(string_stream_eof(stream)) {\n        furi_string_push_back(stream->string, c);\n    } else {\n        furi_string_set_char(stream->string, stream->index, c);\n    }\n    stream->index++;\n\n    return 1;\n}\n"
  },
  {
    "path": "lib/toolbox/stream/string_stream.h",
    "content": "#pragma once\n#include <stdlib.h>\n#include \"stream.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Allocate string stream\n * @return Stream* \n */\nStream* string_stream_alloc(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/strint.c",
    "content": "#include \"strint.h\"\n\n#include <string.h>\n\n// Splitting out the actual parser helps reduce code size. The manually\n// monomorphized `strint_to_*`s are just wrappers around this generic\n// implementation.\n/**\n * @brief Converts a string to a `uint64_t` and an auxillary sign bit, checking\n * the bounds of the integer.\n * @param [in] str Input string\n * @param [out] end Pointer to first character after the number in input string\n * @param [out] abs_out Absolute part of result\n * @param [out] negative_out Sign part of result (true=negative, false=positive)\n * @param [in] base Integer base\n * @param [in] max_abs_negative Largest permissible absolute part of result if\n * the sign is negative\n * @param [in] max_positive Largest permissible absolute part of result if the\n * sign is positive\n */\nStrintParseError strint_to_uint64_internal(\n    const char* str,\n    char** end,\n    uint64_t* abs_out,\n    bool* negative_out,\n    uint8_t base,\n    uint64_t max_abs_negative,\n    uint64_t max_positive) {\n    // skip whitespace\n    while(((*str >= '\\t') && (*str <= '\\r')) || *str == ' ') {\n        str++;\n    }\n\n    // read sign\n    bool negative = false;\n    if(*str == '+' || *str == '-') {\n        if(*str == '-') negative = true;\n        str++;\n    }\n    if(*str == '+' || *str == '-') return StrintParseSignError;\n    if(max_abs_negative == 0 && negative) return StrintParseSignError;\n\n    // infer base\n    // not assigning directly to `base' to permit prefixes with explicit bases\n    uint8_t inferred_base = 0;\n    if(strncasecmp(str, \"0x\", 2) == 0) {\n        inferred_base = 16;\n        str += 2;\n    } else if(strncasecmp(str, \"0b\", 2) == 0) {\n        inferred_base = 2;\n        str += 2;\n    } else if(*str == '0') {\n        inferred_base = 8;\n        str++;\n    } else {\n        inferred_base = 10;\n    }\n    if(base == 0) base = inferred_base;\n\n    // read digits\n    uint64_t limit = negative ? max_abs_negative : max_positive;\n    uint64_t mul_limit = limit / base;\n    uint64_t result = 0;\n    int read_total = 0;\n    while(*str != 0) {\n        int digit_value;\n        if(*str >= '0' && *str <= '9') {\n            digit_value = *str - '0';\n        } else if(*str >= 'A' && *str <= 'Z') {\n            digit_value = *str - 'A' + 10;\n        } else if(*str >= 'a' && *str <= 'z') {\n            digit_value = *str - 'a' + 10;\n        } else {\n            break;\n        }\n\n        if(digit_value >= base) {\n            break;\n        }\n\n        if(result > mul_limit) return StrintParseOverflowError;\n        result *= base;\n        if(result > limit - digit_value) return StrintParseOverflowError; //-V658\n        result += digit_value;\n\n        read_total++;\n        str++;\n    }\n\n    if(read_total == 0) {\n        if(inferred_base == 8) {\n            // there's just a single zero\n            result = 0;\n        } else {\n            return StrintParseAbsentError;\n        }\n    }\n\n    if(abs_out) *abs_out = result;\n    if(negative_out) *negative_out = negative;\n    if(end) *end = (char*)str; // rabbit hole: https://c-faq.com/ansi/constmismatch.html\n    return StrintParseNoError;\n}\n\n#define STRINT_MONO(name, ret_type, neg_abs_limit, pos_limit)                         \\\n    StrintParseError name(const char* str, char** end, ret_type* out, uint8_t base) { \\\n        uint64_t absolute;                                                            \\\n        bool negative;                                                                \\\n        StrintParseError err = strint_to_uint64_internal(                             \\\n            str, end, &absolute, &negative, base, (neg_abs_limit), (pos_limit));      \\\n        if(err) return err;                                                           \\\n        if(out) *out = (negative ? (-(ret_type)absolute) : ((ret_type)absolute));     \\\n        return StrintParseNoError;                                                    \\\n    }\n\nSTRINT_MONO(strint_to_uint64, uint64_t, 0, UINT64_MAX)\nSTRINT_MONO(strint_to_int64, int64_t, (uint64_t)INT64_MAX + 1, INT64_MAX)\nSTRINT_MONO(strint_to_uint32, uint32_t, 0, UINT32_MAX)\nSTRINT_MONO(strint_to_int32, int32_t, (uint64_t)INT32_MAX + 1, INT32_MAX)\nSTRINT_MONO(strint_to_uint16, uint16_t, 0, UINT16_MAX)\nSTRINT_MONO(strint_to_int16, int16_t, (uint64_t)INT16_MAX + 1, INT16_MAX)\n"
  },
  {
    "path": "lib/toolbox/strint.h",
    "content": "/**\n * @file strint.h\n * Performs conversions between strings and integers.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** String to integer conversion error */\ntypedef enum {\n    StrintParseNoError, //!< Conversion performed successfully\n    StrintParseSignError, //!< Multiple leading `+` or `-` characters, or leading `-` character if the type is unsigned\n    StrintParseAbsentError, //!< No valid digits after the leading whitespace, sign and prefix\n    StrintParseOverflowError, //!< Result does not fit in the requested type\n} StrintParseError;\n\n/** See `strint_to_uint32` */\nStrintParseError strint_to_uint64(const char* str, char** end, uint64_t* out, uint8_t base);\n\n/** See `strint_to_uint32` */\nStrintParseError strint_to_int64(const char* str, char** end, int64_t* out, uint8_t base);\n\n/** Converts a string to a `uint32_t`\n *\n * @param[in]  str   Input string\n * @param[out] end   Pointer to first character after the number in input string\n * @param[out] out   Parse result\n * @param[in]  base  Integer base\n *\n * @return     Parse error\n *\n * Parses the number in the input string. The number may be surrounded by\n * whitespace characters to the left and any non-digit characters to the right.\n * What's considered a digit is determined by the input base in the following\n * order: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`. The number may be prefixed\n * with either a `+` or a `-` to indicate its sign. The pointer to the first\n * character after the leading whitespace, allowed prefixes and digits is\n * assigned to `end`.\n *\n * If the input base is 0, the base is inferred from the leading characters of\n * the number:\n *   - If it starts with `0x`, it's read in base 16;\n *   - If it starts with a `0`, it's read in base 8;\n *   - If it starts with `0b`, it's read in base 2.\n *   - Otherwise, it's read in base 10.\n *\n * For a description of the return codes, see `StrintParseError`. If the return\n * code is something other than `StrintParseNoError`, the values at `end` and\n * `out` are unaltered.\n */\nStrintParseError strint_to_uint32(const char* str, char** end, uint32_t* out, uint8_t base);\n\n/** See `strint_to_uint32` */\nStrintParseError strint_to_int32(const char* str, char** end, int32_t* out, uint8_t base);\n\n/** See `strint_to_uint32` */\nStrintParseError strint_to_uint16(const char* str, char** end, uint16_t* out, uint8_t base);\n\n/** See `strint_to_uint32` */\nStrintParseError strint_to_int16(const char* str, char** end, int16_t* out, uint8_t base);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/tar/tar_archive.c",
    "content": "#include \"tar_archive.h\"\n\n#include <microtar.h>\n#include <storage/storage.h>\n#include <furi.h>\n#include <toolbox/path.h>\n#include <toolbox/compress.h>\n\n#define TAG \"TarArch\"\n\n#define MAX_NAME_LEN    255\n#define FILE_BLOCK_SIZE 512\n\n#define FILE_OPEN_NTRIES      10\n#define FILE_OPEN_RETRY_DELAY 25\n\nTarOpenMode tar_archive_get_mode_for_path(const char* path) {\n    char ext[8];\n\n    FuriString* path_str = furi_string_alloc_set_str(path);\n    path_extract_extension(path_str, ext, sizeof(ext));\n    furi_string_free(path_str);\n\n    if(strcmp(ext, \".ths\") == 0) {\n        return TarOpenModeReadHeatshrink;\n    } else {\n        return TarOpenModeRead;\n    }\n}\n\ntypedef struct TarArchive {\n    Storage* storage;\n    File* stream;\n    mtar_t tar;\n    tar_unpack_file_cb unpack_cb;\n    void* unpack_cb_context;\n} TarArchive;\n\n/* Plain file backend - uncompressed, supports read and write */\nstatic int mtar_storage_file_write(void* stream, const void* data, unsigned size) {\n    uint16_t bytes_written = storage_file_write(stream, data, size);\n    return (bytes_written == size) ? bytes_written : MTAR_EWRITEFAIL;\n}\n\nstatic int mtar_storage_file_read(void* stream, void* data, unsigned size) {\n    uint16_t bytes_read = storage_file_read(stream, data, size);\n    return (bytes_read == size) ? bytes_read : MTAR_EREADFAIL;\n}\n\nstatic int mtar_storage_file_seek(void* stream, unsigned offset) {\n    bool res = storage_file_seek(stream, offset, true);\n    return res ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;\n}\n\nstatic int mtar_storage_file_close(void* stream) {\n    if(stream) {\n        storage_file_close(stream);\n    }\n    return MTAR_ESUCCESS;\n}\n\nconst struct mtar_ops filesystem_ops = {\n    .read = mtar_storage_file_read,\n    .write = mtar_storage_file_write,\n    .seek = mtar_storage_file_seek,\n    .close = mtar_storage_file_close,\n};\n\n/* Heatshrink stream backend - compressed, read-only */\n\ntypedef struct {\n    CompressConfigHeatshrink heatshrink_config;\n    File* stream;\n    CompressStreamDecoder* decoder;\n} HeatshrinkStream;\n\n/* HSDS 'heatshrink data stream' header magic */\nstatic const uint32_t HEATSHRINK_MAGIC = 0x53445348;\n\ntypedef struct {\n    uint32_t magic;\n    uint8_t version;\n    uint8_t window_sz2;\n    uint8_t lookahead_sz2;\n} FURI_PACKED HeatshrinkStreamHeader;\n_Static_assert(sizeof(HeatshrinkStreamHeader) == 7, \"Invalid HeatshrinkStreamHeader size\");\n\nstatic int mtar_heatshrink_file_close(void* stream) {\n    HeatshrinkStream* hs_stream = stream;\n    if(hs_stream) {\n        if(hs_stream->decoder) {\n            compress_stream_decoder_free(hs_stream->decoder);\n        }\n        storage_file_close(hs_stream->stream);\n        free(hs_stream);\n    }\n    return MTAR_ESUCCESS;\n}\n\nstatic int mtar_heatshrink_file_read(void* stream, void* data, unsigned size) {\n    HeatshrinkStream* hs_stream = stream;\n    bool read_success = compress_stream_decoder_read(hs_stream->decoder, data, size);\n    return read_success ? (int)size : MTAR_EREADFAIL;\n}\n\nstatic int mtar_heatshrink_file_seek(void* stream, unsigned offset) {\n    HeatshrinkStream* hs_stream = stream;\n    bool success = false;\n    if(offset == 0) {\n        success = storage_file_seek(hs_stream->stream, sizeof(HeatshrinkStreamHeader), true) &&\n                  compress_stream_decoder_rewind(hs_stream->decoder);\n    } else {\n        success = compress_stream_decoder_seek(hs_stream->decoder, offset);\n    }\n    return success ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;\n}\n\nconst struct mtar_ops heatshrink_ops = {\n    .read = mtar_heatshrink_file_read,\n    .write = NULL, // not supported\n    .seek = mtar_heatshrink_file_seek,\n    .close = mtar_heatshrink_file_close,\n};\n\n//////////////////////////////////////////////////////////////////////////\n\nTarArchive* tar_archive_alloc(Storage* storage) {\n    furi_check(storage);\n    TarArchive* archive = malloc(sizeof(TarArchive));\n    archive->storage = storage;\n    archive->stream = storage_file_alloc(archive->storage);\n    archive->unpack_cb = NULL;\n    return archive;\n}\n\nstatic int32_t file_read_cb(void* context, uint8_t* buffer, size_t buffer_size) {\n    File* file = context;\n    return storage_file_read(file, buffer, buffer_size);\n}\n\nbool tar_archive_open(TarArchive* archive, const char* path, TarOpenMode mode) {\n    furi_check(archive);\n    FS_AccessMode access_mode;\n    FS_OpenMode open_mode;\n    bool compressed = false;\n    int mtar_access = 0;\n\n    switch(mode) {\n    case TarOpenModeRead:\n        mtar_access = MTAR_READ;\n        access_mode = FSAM_READ;\n        open_mode = FSOM_OPEN_EXISTING;\n        break;\n    case TarOpenModeWrite:\n        mtar_access = MTAR_WRITE;\n        access_mode = FSAM_WRITE;\n        open_mode = FSOM_CREATE_ALWAYS;\n        break;\n    case TarOpenModeReadHeatshrink:\n        mtar_access = MTAR_READ;\n        access_mode = FSAM_READ;\n        open_mode = FSOM_OPEN_EXISTING;\n        compressed = true;\n        break;\n    default:\n        return false;\n    }\n\n    File* stream = archive->stream;\n    if(!storage_file_open(stream, path, access_mode, open_mode)) {\n        return false;\n    }\n\n    if(compressed) {\n        /* Read and validate stream header */\n        HeatshrinkStreamHeader header;\n        if(storage_file_read(stream, &header, sizeof(HeatshrinkStreamHeader)) !=\n               sizeof(HeatshrinkStreamHeader) ||\n           header.magic != HEATSHRINK_MAGIC) {\n            storage_file_close(stream);\n            return false;\n        }\n\n        HeatshrinkStream* hs_stream = malloc(sizeof(HeatshrinkStream));\n        hs_stream->stream = stream;\n        hs_stream->heatshrink_config.window_sz2 = header.window_sz2;\n        hs_stream->heatshrink_config.lookahead_sz2 = header.lookahead_sz2;\n        hs_stream->heatshrink_config.input_buffer_sz = FILE_BLOCK_SIZE;\n        hs_stream->decoder = compress_stream_decoder_alloc(\n            CompressTypeHeatshrink, &hs_stream->heatshrink_config, file_read_cb, stream);\n        mtar_init(&archive->tar, mtar_access, &heatshrink_ops, hs_stream);\n    } else {\n        mtar_init(&archive->tar, mtar_access, &filesystem_ops, stream);\n    }\n\n    return true;\n}\n\nvoid tar_archive_free(TarArchive* archive) {\n    furi_check(archive);\n    if(mtar_is_open(&archive->tar)) {\n        mtar_close(&archive->tar);\n    }\n    storage_file_free(archive->stream);\n    free(archive);\n}\n\nvoid tar_archive_set_file_callback(TarArchive* archive, tar_unpack_file_cb callback, void* context) {\n    furi_check(archive);\n    archive->unpack_cb = callback;\n    archive->unpack_cb_context = context;\n}\n\nstatic int tar_archive_entry_counter(mtar_t* tar, const mtar_header_t* header, void* param) {\n    UNUSED(tar);\n    UNUSED(header);\n    furi_assert(param);\n    int32_t* counter = param;\n    (*counter)++;\n    return 0;\n}\n\nint32_t tar_archive_get_entries_count(TarArchive* archive) {\n    furi_check(archive);\n    int32_t counter = 0;\n    if(mtar_foreach(&archive->tar, tar_archive_entry_counter, &counter) != MTAR_ESUCCESS) {\n        counter = -1;\n    }\n    return counter;\n}\n\nbool tar_archive_get_read_progress(TarArchive* archive, int32_t* processed, int32_t* total) {\n    furi_check(archive);\n    if(mtar_access_mode(&archive->tar) != MTAR_READ) {\n        return false;\n    }\n\n    if(processed) {\n        *processed = storage_file_tell(archive->stream);\n    }\n    if(total) {\n        *total = storage_file_size(archive->stream);\n    }\n    return true;\n}\n\nbool tar_archive_dir_add_element(TarArchive* archive, const char* dirpath) {\n    furi_check(archive);\n    return mtar_write_dir_header(&archive->tar, dirpath) == MTAR_ESUCCESS;\n}\n\nbool tar_archive_finalize(TarArchive* archive) {\n    furi_check(archive);\n    return mtar_finalize(&archive->tar) == MTAR_ESUCCESS;\n}\n\nbool tar_archive_store_data(\n    TarArchive* archive,\n    const char* path,\n    const uint8_t* data,\n    const int32_t data_len) {\n    furi_check(archive);\n\n    return tar_archive_file_add_header(archive, path, data_len) &&\n           tar_archive_file_add_data_block(archive, data, data_len) &&\n           tar_archive_file_finalize(archive);\n}\n\nbool tar_archive_file_add_header(TarArchive* archive, const char* path, const int32_t data_len) {\n    furi_check(archive);\n\n    return mtar_write_file_header(&archive->tar, path, data_len) == MTAR_ESUCCESS;\n}\n\nbool tar_archive_file_add_data_block(\n    TarArchive* archive,\n    const uint8_t* data_block,\n    const int32_t block_len) {\n    furi_check(archive);\n\n    return mtar_write_data(&archive->tar, data_block, block_len) == block_len;\n}\n\nbool tar_archive_file_finalize(TarArchive* archive) {\n    furi_check(archive);\n    return mtar_end_data(&archive->tar) == MTAR_ESUCCESS;\n}\n\ntypedef struct {\n    TarArchive* archive;\n    const char* work_dir;\n    TarArchiveNameConverter converter;\n} TarArchiveDirectoryOpParams;\n\nstatic bool archive_extract_current_file(TarArchive* archive, const char* dst_path) {\n    mtar_t* tar = &archive->tar;\n    File* out_file = storage_file_alloc(archive->storage);\n    uint8_t* readbuf = malloc(FILE_BLOCK_SIZE);\n\n    bool success = true;\n    uint8_t n_tries = FILE_OPEN_NTRIES;\n    do {\n        while(n_tries-- > 0) {\n            if(storage_file_open(out_file, dst_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {\n                break;\n            }\n            FURI_LOG_W(TAG, \"Failed to open '%s', reties: %d\", dst_path, n_tries);\n            storage_file_close(out_file);\n            furi_delay_ms(FILE_OPEN_RETRY_DELAY);\n        }\n\n        if(!storage_file_is_open(out_file)) {\n            success = false;\n            break;\n        }\n\n        while(!mtar_eof_data(tar)) {\n            int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE);\n            if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) {\n                success = false;\n                break;\n            }\n        }\n    } while(false);\n    storage_file_free(out_file);\n    free(readbuf);\n\n    return success;\n}\n\nstatic int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, void* param) {\n    UNUSED(tar);\n    TarArchiveDirectoryOpParams* op_params = param;\n    TarArchive* archive = op_params->archive;\n\n    bool skip_entry = false;\n    if(archive->unpack_cb) {\n        skip_entry = !archive->unpack_cb(\n            header->name, header->type == MTAR_TDIR, archive->unpack_cb_context);\n    }\n\n    if(skip_entry) {\n        FURI_LOG_W(TAG, \"filter: skipping entry \\\"%s\\\"\", header->name);\n        return 0;\n    }\n\n    FuriString* full_extracted_fname;\n    if(header->type == MTAR_TDIR) {\n        // Skip \"/\" entry since concat would leave it dangling, also want caller to mkdir destination\n        if(strcmp(header->name, \"/\") == 0) {\n            return 0;\n        }\n\n        full_extracted_fname = furi_string_alloc();\n        path_concat(op_params->work_dir, header->name, full_extracted_fname);\n\n        bool create_res =\n            storage_simply_mkdir(archive->storage, furi_string_get_cstr(full_extracted_fname));\n        furi_string_free(full_extracted_fname);\n        return create_res ? 0 : -1;\n    }\n\n    if(header->type != MTAR_TREG) {\n        FURI_LOG_W(TAG, \"not extracting unsupported type \\\"%s\\\"\", header->name);\n        return 0;\n    }\n\n    FURI_LOG_D(TAG, \"Extracting %u bytes to '%s'\", header->size, header->name);\n\n    FuriString* converted_fname = furi_string_alloc_set(header->name);\n    if(op_params->converter) {\n        op_params->converter(converted_fname);\n    }\n\n    full_extracted_fname = furi_string_alloc();\n    path_concat(op_params->work_dir, furi_string_get_cstr(converted_fname), full_extracted_fname);\n\n    bool success =\n        archive_extract_current_file(archive, furi_string_get_cstr(full_extracted_fname));\n\n    furi_string_free(converted_fname);\n    furi_string_free(full_extracted_fname);\n    return success ? 0 : MTAR_EFAILURE;\n}\n\nbool tar_archive_unpack_to(\n    TarArchive* archive,\n    const char* destination,\n    TarArchiveNameConverter converter) {\n    furi_check(archive);\n    TarArchiveDirectoryOpParams param = {\n        .archive = archive,\n        .work_dir = destination,\n        .converter = converter,\n    };\n\n    FURI_LOG_I(TAG, \"Restoring '%s'\", destination);\n\n    return mtar_foreach(&archive->tar, archive_extract_foreach_cb, &param) == MTAR_ESUCCESS;\n}\n\nbool tar_archive_add_file(\n    TarArchive* archive,\n    const char* fs_file_path,\n    const char* archive_fname,\n    const int32_t file_size) {\n    furi_check(archive);\n    uint8_t* file_buffer = malloc(FILE_BLOCK_SIZE);\n    bool success = false;\n    File* src_file = storage_file_alloc(archive->storage);\n    uint8_t n_tries = FILE_OPEN_NTRIES;\n    do {\n        while(n_tries-- > 0) {\n            if(storage_file_open(src_file, fs_file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {\n                break;\n            }\n            FURI_LOG_W(TAG, \"Failed to open '%s', reties: %d\", fs_file_path, n_tries);\n            storage_file_close(src_file);\n            furi_delay_ms(FILE_OPEN_RETRY_DELAY);\n        }\n\n        if(!storage_file_is_open(src_file) ||\n           !tar_archive_file_add_header(archive, archive_fname, file_size)) {\n            break;\n        }\n\n        success = true; // if file is empty, that's not an error\n        uint16_t bytes_read = 0;\n        while((bytes_read = storage_file_read(src_file, file_buffer, FILE_BLOCK_SIZE))) {\n            success = tar_archive_file_add_data_block(archive, file_buffer, bytes_read);\n            if(!success) {\n                break;\n            }\n        }\n\n        success = success && tar_archive_file_finalize(archive);\n    } while(false);\n\n    storage_file_free(src_file);\n    free(file_buffer);\n    return success;\n}\n\nbool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const char* path_prefix) {\n    furi_check(archive);\n    furi_check(path_prefix);\n\n    File* directory = storage_file_alloc(archive->storage);\n    FileInfo file_info;\n\n    FURI_LOG_I(TAG, \"Backing up '%s', '%s'\", fs_full_path, path_prefix);\n    char* name = malloc(MAX_NAME_LEN);\n    bool success = false;\n\n    do {\n        if(!storage_dir_open(directory, fs_full_path)) {\n            break;\n        }\n\n        while(true) {\n            if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {\n                success = true; /* empty dir / no more files */\n                break;\n            }\n\n            FuriString* element_name = furi_string_alloc();\n            FuriString* element_fs_abs_path = furi_string_alloc();\n\n            path_concat(fs_full_path, name, element_fs_abs_path);\n            if(strlen(path_prefix)) {\n                path_concat(path_prefix, name, element_name);\n            } else {\n                furi_string_set(element_name, name);\n            }\n\n            if(file_info_is_dir(&file_info)) {\n                success =\n                    tar_archive_dir_add_element(archive, furi_string_get_cstr(element_name)) &&\n                    tar_archive_add_dir(\n                        archive,\n                        furi_string_get_cstr(element_fs_abs_path),\n                        furi_string_get_cstr(element_name));\n            } else {\n                success = tar_archive_add_file(\n                    archive,\n                    furi_string_get_cstr(element_fs_abs_path),\n                    furi_string_get_cstr(element_name),\n                    file_info.size);\n            }\n            furi_string_free(element_name);\n            furi_string_free(element_fs_abs_path);\n\n            if(!success) {\n                break;\n            }\n        }\n    } while(false);\n\n    free(name);\n    storage_file_free(directory);\n    return success;\n}\n\nbool tar_archive_unpack_file(\n    TarArchive* archive,\n    const char* archive_fname,\n    const char* destination) {\n    furi_check(archive);\n    furi_check(archive_fname);\n    furi_check(destination);\n    if(mtar_find(&archive->tar, archive_fname) != MTAR_ESUCCESS) {\n        return false;\n    }\n    return archive_extract_current_file(archive, destination);\n}\n"
  },
  {
    "path": "lib/toolbox/tar/tar_archive.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <storage/storage.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct TarArchive TarArchive;\n\ntypedef struct Storage Storage;\n\n/** Tar archive open mode \n */\ntypedef enum {\n    TarOpenModeRead = 'r',\n    TarOpenModeWrite = 'w',\n    /* read-only heatshrink compressed tar */\n    TarOpenModeReadHeatshrink = 'h',\n} TarOpenMode;\n\n/** Get expected open mode for archive at the path.\n * Used for automatic mode detection based on the file extension.\n *\n * @param[in]   path          Path to the archive\n *\n * @return open mode from TarOpenMode enum\n */\nTarOpenMode tar_archive_get_mode_for_path(const char* path);\n\n/** Tar archive constructor\n *\n * @param storage             Storage API pointer\n *\n * @return allocated object\n */\nTarArchive* tar_archive_alloc(Storage* storage);\n\n/** Open tar archive\n *\n * @param       archive       Tar archive object\n * @param[in]   path          Path to the tar archive\n * @param       mode          Open mode\n *\n * @return true if successful\n */\nbool tar_archive_open(TarArchive* archive, const char* path, TarOpenMode mode);\n\n/** Tar archive destructor\n *\n * @param archive Tar archive object\n */\nvoid tar_archive_free(TarArchive* archive);\n\ntypedef void (*TarArchiveNameConverter)(FuriString*);\n\n/* High-level API  - assumes archive is open */\n\n/** Unpack tar archive to destination\n *\n * @param       archive       Tar archive object. Must be opened in read mode\n * @param[in]   destination   Destination path\n * @param       converter     Storage name converter\n *\n * @return true if successful\n */\nbool tar_archive_unpack_to(\n    TarArchive* archive,\n    const char* destination,\n    TarArchiveNameConverter converter);\n\n/** Add file to tar archive\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param[in]   fs_file_path  Path to the file on the filesystem\n * @param[in]   archive_fname Name of the file in the archive\n * @param       file_size     Size of the file\n *\n * @return true if successful\n */\nbool tar_archive_add_file(\n    TarArchive* archive,\n    const char* fs_file_path,\n    const char* archive_fname,\n    const int32_t file_size);\n\n/** Add directory to tar archive\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param       fs_full_path  Path to the directory on the filesystem\n * @param       path_prefix   Prefix to add to the directory name in the archive\n *\n * @return true if successful\n */\nbool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const char* path_prefix);\n\n/** Get number of entries in the archive\n *\n * @param archive Tar archive object\n *\n * @return number of entries. -1 on error\n */\nint32_t tar_archive_get_entries_count(TarArchive* archive);\n\n/** Get read progress\n *\n * @param       archive Tar archive object. Must be opened in read mode\n * @param[in]   processed Number of processed entries\n * @param[in]   total Total number of entries\n *\n * @return true if successful\n */\nbool tar_archive_get_read_progress(TarArchive* archive, int32_t* processed, int32_t* total);\n\n/** Unpack single file from tar archive\n *\n * @param       archive       Tar archive object. Must be opened in read mode\n * @param[in]   archive_fname Name of the file in the archive\n * @param[in]   destination   Destination path\n *\n * @return true if successful\n */\nbool tar_archive_unpack_file(\n    TarArchive* archive,\n    const char* archive_fname,\n    const char* destination);\n\n/** Optional per-entry callback on unpacking\n * @param       name          Name of the file or directory\n * @param       is_directory  True if the entry is a directory\n * @param[in]   context       User context\n * @return true to process the entry, false to skip\n */\ntypedef bool (*tar_unpack_file_cb)(const char* name, bool is_directory, void* context);\n\n/** Set per-entry callback on unpacking\n * @param       archive       Tar archive object\n * @param       callback      Callback function\n * @param[in]   context       User context\n */\nvoid tar_archive_set_file_callback(TarArchive* archive, tar_unpack_file_cb callback, void* context);\n\n/* Low-level API */\n\n/** Add tar archive directory header\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param[in]   dirpath       Path to the directory\n *\n * @return true if successful\n */\nbool tar_archive_dir_add_element(TarArchive* archive, const char* dirpath);\n\n/** Add tar archive file header\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param[in]   path          Path to the file\n * @param       data_len      Size of the file\n *\n * @return true if successful\n */\nbool tar_archive_file_add_header(TarArchive* archive, const char* path, const int32_t data_len);\n\n/** Add tar archive file data block\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param[in]   data_block    Data block\n * @param       block_len     Size of the data block\n *\n * @return true if successful\n */\nbool tar_archive_file_add_data_block(\n    TarArchive* archive,\n    const uint8_t* data_block,\n    const int32_t block_len);\n\n/** Finalize tar archive file\n *\n * @param archive       Tar archive object. Must be opened in write mode\n *\n * @return true if successful\n */\nbool tar_archive_file_finalize(TarArchive* archive);\n\n/** Store data in tar archive\n *\n * @param       archive       Tar archive object. Must be opened in write mode\n * @param[in]   path          Path to the file\n * @param[in]   data          Data to store\n * @param       data_len      Size of the data\n *\n * @return true if successful\n */\nbool tar_archive_store_data(\n    TarArchive* archive,\n    const char* path,\n    const uint8_t* data,\n    const int32_t data_len);\n\n/** Finalize tar archive\n *\n * @param archive       Tar archive object. Must be opened in write mode\n *\n * @return true if successful\n */\nbool tar_archive_finalize(TarArchive* archive);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/value_index.c",
    "content": "#include \"value_index.h\"\n#include <math.h>\n\nsize_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count) {\n    size_t index = 0;\n\n    for(size_t i = 0; i < values_count; i++) {\n        if(value == values[i]) {\n            index = i;\n            break;\n        }\n    }\n\n    return index;\n}\n\nsize_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count) {\n    size_t index = 0;\n\n    for(size_t i = 0; i < values_count; i++) {\n        if(value == values[i]) {\n            index = i;\n            break;\n        }\n    }\n\n    return index;\n}\n\nsize_t value_index_float(const float value, const float values[], size_t values_count) {\n    size_t index = 0;\n\n    for(size_t i = 0; i < values_count; i++) {\n        const float epsilon = fabsf(values[i] * 0.01f);\n        if(fabsf(values[i] - value) <= epsilon) {\n            index = i;\n            break;\n        }\n    }\n\n    return index;\n}\n\nsize_t value_index_bool(const bool value, const bool values[], size_t values_count) {\n    size_t index = 0;\n\n    for(size_t i = 0; i < values_count; i++) {\n        if(value == values[i]) {\n            index = i;\n            break;\n        }\n    }\n\n    return index;\n}\n"
  },
  {
    "path": "lib/toolbox/value_index.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Get the index of a int32_t array element which is closest to the given value.\n *\n * Returned index corresponds to the first element found.\n * If no suitable elements were found, the function returns 0.\n *\n * @param   value           value to be searched.\n * @param   values          pointer to the array to perform the search in.\n * @param   values_count    array size.\n *\n * @return value's index.\n */\nsize_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count);\n\n/** Get the index of a uint32_t array element which is closest to the given value.\n *\n * Returned index corresponds to the first element found.\n * If no suitable elements were found, the function returns 0.\n *\n * @param   value           value to be searched.\n * @param   values          pointer to the array to perform the search in.\n * @param   values_count    array size.\n *\n * @return value's index.\n */\nsize_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count);\n\n/** Get the index of a float array element which is closest to the given value.\n *\n * Returned index corresponds to the first element found.\n * If no suitable elements were found, the function returns 0.\n *\n * @param   value           value to be searched.\n * @param   values          pointer to the array to perform the search in.\n * @param   values_count    array size.\n *\n * @return value's index.\n */\nsize_t value_index_float(const float value, const float values[], size_t values_count);\n\n/** Get the index of a bool array element which is equal to the given value.\n *\n * Returned index corresponds to the first element found.\n * If no suitable elements were found, the function returns 0.\n *\n * @param   value           value to be searched.\n * @param   values          pointer to the array to perform the search in.\n * @param   values_count    array size.\n *\n * @return value's index.\n */\nsize_t value_index_bool(const bool value, const bool values[], size_t values_count);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/varint.c",
    "content": "#include \"varint.h\"\n\nsize_t varint_uint32_pack(uint32_t value, uint8_t* output) {\n    uint8_t* start = output;\n    while(value >= 0x80) {\n        *output++ = (value | 0x80);\n        value >>= 7;\n    }\n    *output++ = value;\n    return output - start;\n}\n\nsize_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size) {\n    size_t i;\n    uint32_t parsed = 0;\n\n    for(i = 0; i < input_size; i++) {\n        parsed |= (input[i] & 0x7FUL) << (7 * i);\n\n        if(!(input[i] & 0x80)) {\n            break;\n        }\n    }\n\n    *value = parsed;\n\n    return i + 1;\n}\n\nsize_t varint_uint32_length(uint32_t value) {\n    size_t size = 0;\n    while(value >= 0x80) {\n        value >>= 7;\n        size++;\n    }\n    size++;\n\n    return size;\n}\n\nsize_t varint_int32_pack(int32_t value, uint8_t* output) {\n    uint32_t v;\n\n    if(value >= 0) {\n        v = value * 2;\n    } else {\n        v = (value * -2) - 1;\n    }\n\n    return varint_uint32_pack(v, output);\n}\n\nsize_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size) {\n    uint32_t v;\n    size_t size = varint_uint32_unpack(&v, input, input_size);\n\n    if(v & 1) {\n        *value = (int32_t)(v + 1) / (-2);\n    } else {\n        *value = v / 2;\n    }\n\n    return size;\n}\n\nsize_t varint_int32_length(int32_t value) {\n    uint32_t v;\n\n    if(value >= 0) {\n        v = value * 2;\n    } else {\n        v = (value * -2) - 1;\n    }\n\n    return varint_uint32_length(v);\n}\n"
  },
  {
    "path": "lib/toolbox/varint.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <stdlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Pack uint32 to varint\n * @param value value from UINT32_MIN to UINT32_MAX\n * @param output output array, need to be at least 5 bytes long\n * @return size_t \n */\nsize_t varint_uint32_pack(uint32_t value, uint8_t* output);\n\nsize_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size);\n\nsize_t varint_uint32_length(uint32_t value);\n\n/**\n * Pack int32 to varint\n * @param value value from (INT32_MIN / 2 + 1) to INT32_MAX\n * @param output output array, need to be at least 5 bytes long\n * @return size_t \n */\nsize_t varint_int32_pack(int32_t value, uint8_t* output);\n\nsize_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size);\n\nsize_t varint_int32_length(int32_t value);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/toolbox/version.c",
    "content": "#include \"version.h\"\n#include <furi.h>\n/* This header is autogenerated by build system */\n#include \"version.inc.h\"\n\n#define VERSION_MAGIC (0xBE40u)\n#define VERSION_MAJOR (0x1u)\n#define VERSION_MINOR (0x1u)\n\nstruct Version {\n    // Header\n    const uint16_t magic;\n    const uint8_t major;\n    const uint8_t minor;\n    // Payload\n    const char* git_hash;\n    const char* git_branch;\n    const char* build_date;\n    const char* version;\n    // Payload bits and pieces\n    const uint8_t target;\n    const bool build_is_dirty;\n    // v 1.1\n    const char* firmware_origin;\n    const char* git_origin;\n};\n\n/* version of current running firmware (bootloader/flipper) */\nstatic const Version version = {\n    .magic = VERSION_MAGIC,\n    .major = VERSION_MAJOR,\n    .minor = VERSION_MINOR,\n    .git_hash = GIT_COMMIT,\n    .git_branch = GIT_BRANCH,\n    .build_date = BUILD_DATE,\n    .version = VERSION\n#ifdef FURI_RAM_EXEC\n    \" (RAM)\"\n#endif\n    ,\n    .target = TARGET,\n    .build_is_dirty = BUILD_DIRTY,\n    .firmware_origin = FIRMWARE_ORIGIN,\n    .git_origin = GIT_ORIGIN,\n};\n\nconst Version* version_get(void) {\n    return &version;\n}\n\nconst char* version_get_githash(const Version* v) {\n    return v ? v->git_hash : version.git_hash;\n}\n\nconst char* version_get_gitbranch(const Version* v) {\n    return v ? v->git_branch : version.git_branch;\n}\n\nconst char* version_get_gitbranchnum(const Version* v) {\n    UNUSED(v);\n    return \"0\";\n}\n\nconst char* version_get_builddate(const Version* v) {\n    return v ? v->build_date : version.build_date;\n}\n\nconst char* version_get_version(const Version* v) {\n    return v ? v->version : version.version;\n}\n\nuint8_t version_get_target(const Version* v) {\n    return v ? v->target : version.target;\n}\n\nbool version_get_dirty_flag(const Version* v) {\n    return v ? v->build_is_dirty : version.build_is_dirty;\n}\n\nconst char* version_get_firmware_origin(const Version* v) {\n    return v ? v->firmware_origin : version.firmware_origin;\n}\n\nconst char* version_get_git_origin(const Version* v) {\n    return v ? v->git_origin : version.git_origin;\n}\n"
  },
  {
    "path": "lib/toolbox/version.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct Version Version;\n\n/** Get current running firmware version handle.\n *\n * You can store it somewhere. But if you want to retrieve data, you have to use\n * 'version_*_get()' set of functions. Also, 'version_*_get()' imply to use this\n * handle if no handle (NULL_PTR) provided.\n *\n * @return     pointer to Version data.\n */\nconst Version* version_get(void);\n\n/** Get git commit hash.\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     git hash\n */\nconst char* version_get_githash(const Version* v);\n\n/** Get git branch.\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     git branch\n */\nconst char* version_get_gitbranch(const Version* v);\n\n/** Get number of commit in git branch.\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     number of commit\n */\nconst char* version_get_gitbranchnum(const Version* v);\n\n/** Get build date.\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     build date\n */\nconst char* version_get_builddate(const Version* v);\n\n/** Get build version. Build version is last tag in git history.\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     build date\n */\nconst char* version_get_version(const Version* v);\n\n/** Get hardware target this firmware was built for\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     build date\n */\nuint8_t version_get_target(const Version* v);\n\n/** Get flag indicating if this build is \"dirty\" (source code had uncommited changes)\n *\n * @param      v     pointer to Version data. NULL for currently running\n *                   software.\n *\n * @return     build date\n */\nbool version_get_dirty_flag(const Version* v);\n\n/** \n * Get firmware origin. \"Official\" for mainline firmware, fork name for forks.\n * Set by FIRMWARE_ORIGIN fbt argument.\n*/\nconst char* version_get_firmware_origin(const Version* v);\n\n/** \n * Get git repo origin\n*/\nconst char* version_get_git_origin(const Version* v);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/u8g2/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    CPPPATH=[\n        \"#/lib/u8g2\",\n    ],\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\n\nlibenv = env.Clone(FW_LIB_NAME=\"u8g2\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/u8g2/u8g2.h",
    "content": "/*\n\n  u8g2.h\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n\n  call sequence\n  \n  u8g2_SetupBuffer_XYZ\n    u8x8_Setup_XYZ\n      u8x8_SetupDefaults(u8g2);\n      assign u8x8 callbacks\n      u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_SETUP_MEMORY, 0, NULL);  \n    setup tile buffer\n    \n  \n  Arduino Uno Text Example\n>\tFONT_ROTATION\tINTERSECTION\tCLIPPING\ttext\t   \tdata\t\tbss\t\tdec\t\thex\t\n>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t8700\n>\tx\t\t\t\tx\t\t\t\tx\t\t\t7450\t104\t\t1116\t8670\t21de\n>\t-\t\t\t\tx\t\t\t\tx\t\t\t7132\t104\t\t1115\t8351\t209f\n>\tx\t\t\t\t-\t\t\t\tx\t\t\t7230\t104\t\t1116\t8450\t2102\n>\t-\t\t\t\t-\t\t\t\tx\t\t\t7010\t104\t\t1115\t8229\t2025\n>\t-\t\t\t\t-\t\t\t\t-\t\t\t6880\t104\t\t1115\t8099\t1fa3\n  \n  \n*/\n\n#ifndef U8G2_H\n#define U8G2_H\n\n#include \"u8x8.h\"\n\n/*\n  The following macro enables 16 Bit mode. \n  Without defining this macro all calulations are done with 8 Bit (1 Byte) variables.\n  Especially on AVR architecture, this will save some space. \n  If this macro is defined, then U8g2 will switch to 16 Bit mode.\n  Use 16 Bit mode for any display with more than 240 pixel in one \n  direction.\n*/\n#define U8G2_16BIT\n\n/*\n  The following macro switches the library into dynamic display buffer allocation mode.\n  Defining this constant will disable all static memory allocation for device memory buffer and thus allows the user to allocate device buffers statically.\n  Before using any display functions, the dynamic buffer *must* be assigned to the u8g2 struct using the u8g2_SetBufferPtr function.\n  When using dynamic allocation, the stack size must be increased by u8g2_GetBufferSize bytes.\n */\n//#define U8G2_USE_DYNAMIC_ALLOC\n\n/* U8g2 feature selection, see also https://github.com/olikraus/u8g2/wiki/u8g2optimization */\n\n/*\n  The following macro enables the HVLine speed optimization.\n  It will consume about 40 bytes more in flash memory of the AVR.\n  HVLine procedures are also used by the text drawing functions.\n*/\n#define U8G2_WITH_HVLINE_SPEED_OPTIMIZATION\n\n/*\n  The following macro activates the early intersection check with the current visible area.\n  Clipping (and low level intersection calculation) will still happen and is controlled by U8G2_WITH_CLIPPING.\n  This early intersection check only improves speed for the picture loop (u8g2_FirstPage/NextPage).\n  With a full framebuffer in RAM and if most graphical elements are drawn within the visible area, then this\n  macro can be commented to reduce code size.\n*/\n#define U8G2_WITH_INTERSECTION\n\n/*\n  Enable clip window support:\n    void u8g2_SetMaxClipWindow(u8g2_t *u8g2)\n    void u8g2_SetClipWindow(u8g2_t *u8g2, u8g2_uint_t clip_x0, u8g2_uint_t clip_y0, u8g2_uint_t clip_x1, u8g2_uint_t clip_y1 )\n  Setting a clip window will restrict all drawing to this window.\n  Clip window support requires about 200 bytes flash memory on AVR systems\n*/\n#define U8G2_WITH_CLIP_WINDOW_SUPPORT\n\n/*\n  The following macro enables all four drawing directions for glyphs and strings.\n  If this macro is not defined, than a string can be drawn only in horizontal direction.\n  \n  Jan 2020: Disabling this macro will save up to 600 bytes on AVR \n*/\n#define U8G2_WITH_FONT_ROTATION\n\n/*\n  U8glib V2 contains support for unicode plane 0 (Basic Multilingual Plane, BMP).\n  The following macro activates this support. Deactivation would save some ROM.\n  This definition also defines the behavior of the expected string encoding.\n  If the following macro is defined, then the DrawUTF8 function is enabled and \n  the string argument for this function is assumed \n  to be UTF-8 encoded.\n  If the following macro is not defined, then all strings in the c-code are assumed \n  to be ISO 8859-1/CP1252 encoded. \n  Independently from this macro, the Arduino print function never accepts UTF-8\n  strings.\n  \n  This macro does not affect the u8x8 string draw function.\n  u8x8 has also two function, one for pure strings and one for UTF8\n  \n  Conclusion:\n    U8G2_WITH_UNICODE defined\n      - C-Code Strings must be UTF-8 encoded\n      - Full support of all 65536 glyphs of the unicode basic multilingual plane\n      - Up to 65536 glyphs of the font file can be used.\n    U8G2_WITH_UNICODE not defined\n      - C-Code Strings are assumbed to be ISO 8859-1/CP1252 encoded\n      - Only character values 0 to 255 are supported in the font file.\n*/\n#define U8G2_WITH_UNICODE\n\n/*==========================================*/\n\n#ifdef __GNUC__\n#define U8G2_NOINLINE __attribute__((noinline))\n#else\n#define U8G2_NOINLINE\n#endif\n\n#define U8G2_FONT_SECTION(name) U8X8_FONT_SECTION(name)\n\n/* the macro U8G2_USE_LARGE_FONTS enables large fonts (>32K) */\n/* it can be enabled for those uC supporting larger arrays */\n#if defined(unix) || defined(__arm__) || defined(__arc__) || defined(ESP8266) || \\\n    defined(ESP_PLATFORM)\n#ifndef U8G2_USE_LARGE_FONTS\n#define U8G2_USE_LARGE_FONTS\n#endif\n#endif\n\n/*==========================================*/\n/* C++ compatible */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*==========================================*/\n\n#ifdef U8G2_16BIT\ntypedef uint16_t u8g2_uint_t; /* for pixel position only */\ntypedef int16_t u8g2_int_t; /* introduced for circle calculation */\ntypedef int32_t u8g2_long_t; /* introduced for ellipse calculation */\n#else\ntypedef uint8_t u8g2_uint_t; /* for pixel position only */\ntypedef int8_t u8g2_int_t; /* introduced for circle calculation */\ntypedef int16_t u8g2_long_t; /* introduced for ellipse calculation */\n#endif\n\ntypedef struct u8g2_struct u8g2_t;\ntypedef struct u8g2_cb_struct u8g2_cb_t;\n\ntypedef void (*u8g2_update_dimension_cb)(u8g2_t* u8g2);\ntypedef void (*u8g2_update_page_win_cb)(u8g2_t* u8g2);\ntypedef void (\n    *u8g2_draw_l90_cb)(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir);\ntypedef void (*u8g2_draw_ll_hvline_cb)(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir);\n\ntypedef uint8_t (*u8g2_get_kerning_cb)(u8g2_t* u8g2, uint16_t e1, uint16_t e2);\n\n/* from ucglib... */\nstruct _u8g2_font_info_t {\n    /* offset 0 */\n    uint8_t glyph_cnt;\n    uint8_t bbx_mode;\n    uint8_t bits_per_0;\n    uint8_t bits_per_1;\n\n    /* offset 4 */\n    uint8_t bits_per_char_width;\n    uint8_t bits_per_char_height;\n    uint8_t bits_per_char_x;\n    uint8_t bits_per_char_y;\n    uint8_t bits_per_delta_x;\n\n    /* offset 9 */\n    int8_t max_char_width;\n    int8_t\n        max_char_height; /* overall height, NOT ascent. Instead ascent = max_char_height + y_offset */\n    int8_t x_offset;\n    int8_t y_offset;\n\n    /* offset 13 */\n    int8_t ascent_A;\n    int8_t descent_g; /* usually a negative value */\n    int8_t ascent_para;\n    int8_t descent_para;\n\n    /* offset 17 */\n    uint16_t start_pos_upper_A;\n    uint16_t start_pos_lower_a;\n\n    /* offset 21 */\n#ifdef U8G2_WITH_UNICODE\n    uint16_t start_pos_unicode;\n#endif\n};\ntypedef struct _u8g2_font_info_t u8g2_font_info_t;\n\n/* from ucglib... */\nstruct _u8g2_font_decode_t {\n    const uint8_t* decode_ptr; /* pointer to the compressed data */\n\n    u8g2_uint_t target_x;\n    u8g2_uint_t target_y;\n\n    int8_t x; /* local coordinates, (0,0) is upper left */\n    int8_t y;\n    int8_t glyph_width;\n    int8_t glyph_height;\n\n    uint8_t decode_bit_pos; /* bitpos inside a byte of the compressed data */\n    uint8_t is_transparent;\n    uint8_t fg_color;\n    uint8_t bg_color;\n#ifdef U8G2_WITH_FONT_ROTATION\n    uint8_t dir; /* direction */\n#endif\n};\ntypedef struct _u8g2_font_decode_t u8g2_font_decode_t;\n\nstruct _u8g2_kerning_t {\n    uint16_t first_table_cnt;\n    uint16_t second_table_cnt;\n    const uint16_t* first_encoding_table;\n    const uint16_t* index_to_second_table;\n    const uint16_t* second_encoding_table;\n    const uint8_t* kerning_values;\n};\ntypedef struct _u8g2_kerning_t u8g2_kerning_t;\n\nstruct u8g2_cb_struct {\n    u8g2_update_dimension_cb update_dimension;\n    u8g2_update_page_win_cb update_page_win;\n    u8g2_draw_l90_cb draw_l90;\n};\n\ntypedef u8g2_uint_t (*u8g2_font_calc_vref_fnptr)(u8g2_t* u8g2);\n\nstruct u8g2_struct {\n    u8x8_t u8x8;\n    u8g2_draw_ll_hvline_cb ll_hvline; /* low level hvline procedure */\n    const u8g2_cb_t* cb; /* callback drawprocedures, can be replaced for rotation */\n\n    /* the following variables must be assigned during u8g2 setup */\n    uint8_t*\n        tile_buf_ptr; /* ptr to memory area with u8x8.display_info->tile_width * 8 * tile_buf_height bytes */\n    uint8_t tile_buf_height; /* height of the tile memory area in tile rows */\n    uint8_t tile_curr_row; /* current row for picture loop */\n\n    /* dimension of the buffer in pixel */\n    u8g2_uint_t pixel_buf_width; /* equal to tile_buf_width*8 */\n    u8g2_uint_t pixel_buf_height; /* tile_buf_height*8 */\n    u8g2_uint_t pixel_curr_row; /* u8g2.tile_curr_row*8 */\n\n    /* the following variables are set by the update dimension callback */\n    /* this is the clipbox after rotation for the hvline procedures */\n    //u8g2_uint_t buf_x0;\t/* left corner of the buffer */\n    //u8g2_uint_t buf_x1;\t/* right corner of the buffer (excluded) */\n    u8g2_uint_t buf_y0;\n    u8g2_uint_t buf_y1;\n\n    /* display dimensions in pixel for the user, calculated in u8g2_update_dimension_common()  */\n    u8g2_uint_t width;\n    u8g2_uint_t height;\n\n    /* ths is the clip box for the user to check if a specific box has an intersection */\n    /* use u8g2_IsIntersection from u8g2_intersection.c to test against this intersection */\n    /* actually, this window describes the positon of the current page */\n    u8g2_uint_t user_x0; /* left corner of the buffer */\n    u8g2_uint_t user_x1; /* right corner of the buffer (excluded) */\n    u8g2_uint_t user_y0; /* upper edge of the buffer */\n    u8g2_uint_t user_y1; /* lower edge of the buffer (excluded) */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    /* clip window */\n    u8g2_uint_t clip_x0; /* left corner of the clip window */\n    u8g2_uint_t clip_x1; /* right corner of the clip window (excluded) */\n    u8g2_uint_t clip_y0; /* upper edge of the clip window */\n    u8g2_uint_t clip_y1; /* lower edge of the clip window (excluded) */\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n\n    /* information about the current font */\n    const uint8_t* font; /* current font for all text procedures */\n    // removed: const u8g2_kerning_t *kerning;\t\t/* can be NULL */\n    // removed: u8g2_get_kerning_cb get_kerning_cb;\n\n    u8g2_font_calc_vref_fnptr font_calc_vref;\n    u8g2_font_decode_t font_decode; /* new font decode structure */\n    u8g2_font_info_t font_info; /* new font info structure */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    /* 1 of there is an intersection between user_?? and clip_?? box */\n    uint8_t is_page_clip_window_intersection;\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n\n    uint8_t font_height_mode;\n    int8_t font_ref_ascent;\n    int8_t font_ref_descent;\n\n    int8_t glyph_x_offset; /* set by u8g2_GetGlyphWidth as a side effect */\n\n    uint8_t bitmap_transparency; /* black pixels will be treated as transparent (not drawn) */\n\n    uint8_t draw_color; /* 0: clear pixel, 1: set pixel, modified and restored by font procedures */\n    /* draw_color can be used also directly by the user API */\n\n    // the following variable should be renamed to is_buffer_auto_clear\n    uint8_t\n        is_auto_page_clear; /* set to 0 to disable automatic clear of the buffer in firstPage() and nextPage() */\n};\n\n#define u8g2_GetU8x8(u8g2) ((u8x8_t*)(u8g2))\n//#define u8g2_GetU8x8(u8g2) (&((u8g2)->u8x8))\n\n#ifdef U8X8_WITH_USER_PTR\n#define u8g2_GetUserPtr(u8g2)    ((u8g2_GetU8x8(u8g2))->user_ptr)\n#define u8g2_SetUserPtr(u8g2, p) ((u8g2_GetU8x8(u8g2))->user_ptr = (p))\n#endif\n\n// this should be renamed to SetBufferAutoClear\n#define u8g2_SetAutoPageClear(u8g2, mode) ((u8g2)->is_auto_page_clear = (mode))\n\n/*==========================================*/\n/* u8x8 wrapper */\n\n#define u8g2_SetupDisplay(u8g2, display_cb, cad_cb, byte_cb, gpio_and_delay_cb) \\\n    u8x8_Setup(u8g2_GetU8x8(u8g2), (display_cb), (cad_cb), (byte_cb), (gpio_and_delay_cb))\n\n#define u8g2_InitDisplay(u8g2)             u8x8_InitDisplay(u8g2_GetU8x8(u8g2))\n#define u8g2_SetPowerSave(u8g2, is_enable) u8x8_SetPowerSave(u8g2_GetU8x8(u8g2), (is_enable))\n#define u8g2_SetFlipMode(u8g2, mode)       u8x8_SetFlipMode(u8g2_GetU8x8(u8g2), (mode))\n#define u8g2_SetContrast(u8g2, value)      u8x8_SetContrast(u8g2_GetU8x8(u8g2), (value))\n//#define u8g2_ClearDisplay(u8g2) u8x8_ClearDisplay(u8g2_GetU8x8(u8g2))  obsolete, can not be used in all cases\nvoid u8g2_ClearDisplay(u8g2_t* u8g2);\n\n#define u8g2_GetDisplayHeight(u8g2) ((u8g2)->height)\n#define u8g2_GetDisplayWidth(u8g2)  ((u8g2)->width)\n#define u8g2_GetDrawColor(u8g2)     ((u8g2)->draw_color)\n\n#define u8g2_SetI2CAddress(u8g2, address) ((u8g2_GetU8x8(u8g2))->i2c_address = (address))\n#define u8g2_GetI2CAddress(u8g2)          u8x8_GetI2CAddress(u8g2_GetU8x8(u8g2))\n\n#ifdef U8X8_USE_PINS\n#define u8g2_SetMenuSelectPin(u8g2, val) u8x8_SetMenuSelectPin(u8g2_GetU8x8(u8g2), (val))\n#define u8g2_SetMenuNextPin(u8g2, val)   u8x8_SetMenuNextPin(u8g2_GetU8x8(u8g2), (val))\n#define u8g2_SetMenuPrevPin(u8g2, val)   u8x8_SetMenuPrevPin(u8g2_GetU8x8(u8g2), (val))\n#define u8g2_SetMenuHomePin(u8g2, val)   u8x8_SetMenuHomePin(u8g2_GetU8x8(u8g2), (val))\n#define u8g2_SetMenuUpPin(u8g2, val)     u8x8_SetMenuUpPin(u8g2_GetU8x8(u8g2), (val))\n#define u8g2_SetMenuDownPin(u8g2, val)   u8x8_SetMenuDownPin(u8g2_GetU8x8(u8g2), (val))\n#endif\n\n/*==========================================*/\n/* u8g2_setup.c */\n\nvoid u8g2_draw_l90_r0(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir);\n\nextern const u8g2_cb_t u8g2_cb_r0;\nextern const u8g2_cb_t u8g2_cb_r1;\nextern const u8g2_cb_t u8g2_cb_r2;\nextern const u8g2_cb_t u8g2_cb_r3;\nextern const u8g2_cb_t u8g2_cb_mirror;\n\n#define U8G2_R0     (&u8g2_cb_r0)\n#define U8G2_R1     (&u8g2_cb_r1)\n#define U8G2_R2     (&u8g2_cb_r2)\n#define U8G2_R3     (&u8g2_cb_r3)\n#define U8G2_MIRROR (&u8g2_cb_mirror)\n/*\n  u8g2:\t\t\tA new, not yet initialized u8g2 memory areay\n  buf:\t\t\tMemory are of size tile_buf_height*<width of the display in pixel>\n  tile_buf_height:\tNumber of full lines\n  ll_hvline_cb:\t\tone of:\n    u8g2_ll_hvline_vertical_top_lsb\n    u8g2_ll_hvline_horizontal_right_lsb\n  u8g2_cb\t\t\tU8G2_R0 .. U8G2_R3\n      \n*/\n\nvoid u8g2_SetMaxClipWindow(u8g2_t* u8g2);\nvoid u8g2_SetClipWindow(\n    u8g2_t* u8g2,\n    u8g2_uint_t clip_x0,\n    u8g2_uint_t clip_y0,\n    u8g2_uint_t clip_x1,\n    u8g2_uint_t clip_y1);\n\nvoid u8g2_SetupBuffer(\n    u8g2_t* u8g2,\n    uint8_t* buf,\n    uint8_t tile_buf_height,\n    u8g2_draw_ll_hvline_cb ll_hvline_cb,\n    const u8g2_cb_t* u8g2_cb);\nvoid u8g2_SetDisplayRotation(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\n\nvoid u8g2_SendF(u8g2_t* u8g2, const char* fmt, ...);\n\n/* null device setup */\nvoid u8g2_Setup_null(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\n\n/*==========================================*/\n/* u8g2_d_memory.c generated code start */\nuint8_t* u8g2_m_16_4_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_4_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_4_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_9_5_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_9_5_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_9_5_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_4_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_4_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_4_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_12_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_12_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_12_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_20_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_20_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_20_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_6_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_6_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_6_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_6_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_6_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_6_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_2_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_2_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_2_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_12_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_12_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_16_12_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_4_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_4_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_4_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_4_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_4_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_4_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_50_30_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_50_30_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_50_30_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_18_21_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_18_21_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_18_21_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_13_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_13_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_13_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_11_6_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_11_6_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_11_6_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_9_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_9_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_12_9_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_15_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_15_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_15_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_13_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_13_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_13_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_20_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_20_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_30_20_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_40_30_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_40_30_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_40_30_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_4_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_4_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_4_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_8_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_8_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_17_8_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_17_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_17_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_17_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_20_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_20_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_32_20_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_13_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_13_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_13_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_12_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_12_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_24_12_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_10_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_10_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_20_10_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_9_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_9_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_22_9_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_25_25_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_25_25_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_25_25_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_37_16_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_37_16_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_37_16_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_1_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_1_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_8_1_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_4_1_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_4_1_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_4_1_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_1_1_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_1_1_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_1_1_f(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_30_1(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_30_2(uint8_t* page_cnt);\nuint8_t* u8g2_m_48_30_f(uint8_t* page_cnt);\n\n/* u8g2_d_memory.c generated code end */\n\n/*==========================================*/\n/* u8g2_d_setup.c generated code start */\nvoid u8g2_Setup_ssd1305_128x32_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x32_adafruit_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x32_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x32_adafruit_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x32_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x32_adafruit_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_adafruit_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_adafruit_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x32_adafruit_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x64_adafruit_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x64_adafruit_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_128x64_adafruit_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x64_adafruit_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x64_adafruit_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1305_i2c_128x64_adafruit_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_vcomh0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_alt0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_vcomh0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_alt0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_vcomh0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x64_alt0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_vcomh0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_alt0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_vcomh0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_alt0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_vcomh0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x64_alt0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_72x40_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_72x40_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_72x40_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_72x40_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_72x40_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_72x40_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_vcomh0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_vcomh0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_vcomh0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_128x64_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_vcomh0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_vcomh0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_vcomh0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_128x64_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_72x40_wise_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_72x40_wise_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_72x40_wise_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_72x40_wise_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_72x40_wise_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_72x40_wise_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_64x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_64x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_64x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_64x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_64x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1106_i2c_64x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_64x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_64x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_64x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_64x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_64x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_64x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_pimoroni_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_pimoroni_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_pimoroni_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_seeed_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_pimoroni_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_pimoroni_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_pimoroni_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1107_i2c_seeed_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_160x160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_160x160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_160x160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_i2c_160x160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_i2c_160x160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1108_i2c_160x160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_256x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_256x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_256x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_i2c_256x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_i2c_256x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sh1122_i2c_256x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_univision_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_univision_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_univision_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_128x32_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_univision_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_univision_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_univision_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_128x32_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x48_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x48_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x48_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x48_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x48_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x48_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_48x64_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_48x64_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_48x64_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_48x64_winstar_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_48x64_winstar_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_48x64_winstar_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_1f_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_1f_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_64x32_1f_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_1f_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_1f_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_64x32_1f_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_96x16_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_96x16_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_96x16_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_96x16_er_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_96x16_er_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1306_i2c_96x16_er_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname2_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname2_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname2_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname2_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname2_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname2_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_128x64_noname0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname0_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname0_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1309_i2c_128x64_noname0_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_128x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_128x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_128x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_i2c_128x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_i2c_128x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1316_i2c_128x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_i2c_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_i2c_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1317_i2c_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_xcp_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_xcp_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_128x96_xcp_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_xcp_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_xcp_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1318_i2c_128x96_xcp_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_nhd_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_nhd_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_nhd_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_i2c_nhd_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_i2c_nhd_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1325_i2c_nhd_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_os128064_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_os128064_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_os128064_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_i2c_os128064_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_i2c_os128064_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd0323_i2c_os128064_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_er_256x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_er_256x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_er_256x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_i2c_er_256x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_i2c_er_256x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1326_i2c_er_256x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_96x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_96x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_96x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_96x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_96x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_96x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_seeed_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_seeed_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_seeed_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_seeed_96x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_seeed_96x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_seeed_96x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ea_w128128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_midas_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ea_w128128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_midas_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ea_w128128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_midas_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_ws_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ea_w128128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_midas_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ea_w128128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_midas_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ea_w128128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_midas_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_ws_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_visionox_128x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_visionox_128x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_visionox_128x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_visionox_128x96_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_visionox_128x96_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1327_i2c_visionox_128x96_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1329_128x96_noname_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1329_128x96_noname_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1329_128x96_noname_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_60x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_60x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_60x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_i2c_60x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_i2c_60x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ld7032_i2c_60x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_192x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_192x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_192x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_192x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_192x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_192x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_192x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_192x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_192x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_p_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7920_s_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh03_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh03_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh03_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls027b7dh01_400x240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls027b7dh01_400x240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls027b7dh01_400x240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh05_144x168_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh05_144x168_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ls013b7dh05_144x168_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_ea_dogs102_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_ea_dogs102_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_ea_dogs102_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_mini12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_mini12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1701_mini12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcd8544_84x48_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcd8544_84x48_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcd8544_84x48_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcf8812_96x65_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcf8812_96x65_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_pcf8812_96x65_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_hx1230_96x68_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_hx1230_96x68_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_hx1230_96x68_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_jlx19264_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_jlx19264_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_jlx19264_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_i2c_jlx19264_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_i2c_jlx19264_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1604_i2c_jlx19264_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc24064_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc24064_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc24064_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc24064_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc24064_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc24064_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc240120_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc240120_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_erc240120_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc240120_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc240120_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_erc240120_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1608_i2c_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1638_160x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1638_160x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1638_160x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_ea_dogxl160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_ea_dogxl160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_ea_dogxl160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_i2c_ea_dogxl160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_i2c_ea_dogxl160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1610_i2c_ea_dogxl160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogm240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogm240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogm240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogm240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogm240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogm240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogxl240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogxl240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ea_dogxl240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogxl240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogxl240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ea_dogxl240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ew50850_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ew50850_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_ew50850_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ew50850_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ew50850_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_ew50850_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_cg160160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_cg160160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_cg160160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_cg160160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_cg160160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1611_i2c_cg160160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7511_avd_320x240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7511_avd_320x240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7511_avd_320x240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_nhd_c160100_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_nhd_c160100_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_nhd_c160100_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_i2c_nhd_c160100_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_i2c_nhd_c160100_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7528_i2c_nhd_c160100_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6063_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_64128n_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_zolen_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6059_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lx12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_erc12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_erc12864_alt_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_jlx12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6063_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_64128n_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_zolen_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6059_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lx12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_erc12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_erc12864_alt_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_jlx12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6063_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_64128n_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_zolen_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lm6059_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_lx12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_erc12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_jlx12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12832_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12832_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_nhd_c12832_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_128x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_128x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_128x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_i2c_128x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_i2c_128x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_uc1601_i2c_128x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm132_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm132_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7565_ea_dogm132_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_pi_132x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_pi_132x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_pi_132x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_jlx12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064i_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_os12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_jlx12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064i_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_os12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_jlx12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_enh_dg128064i_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_os12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_64x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_64x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_64x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_i2c_64x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_i2c_64x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7567_i2c_64x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_s028hn118a_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_s028hn118a_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_s028hn118a_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_erc240160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_erc240160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7586s_erc240160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_jlx12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_jlx12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_jlx12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_i2c_jlx12864_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_i2c_jlx12864_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st7588_i2c_jlx12864_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_wo256x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_wo256x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_wo256x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_wo256x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_wo256x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_wo256x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160m_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_alt_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160m_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_alt_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160m_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx256160_alt_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160m_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_alt_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160m_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_alt_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160m_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx256160_alt_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx240160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx240160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx240160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx240160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx240160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx240160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx25664_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx25664_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx25664_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx25664_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx25664_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx25664_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx172104_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx172104_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx172104_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx172104_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx172104_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx172104_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx19296_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx19296_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_jlx19296_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx19296_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx19296_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75256_i2c_jlx19296_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_jlx320240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_jlx320240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_jlx320240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_i2c_jlx320240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_i2c_jlx320240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_st75320_i2c_jlx320240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_nt7534_tg12864r_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_nt7534_tg12864r_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_nt7534_tg12864r_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist3020_erc19264_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist3020_erc19264_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist3020_erc19264_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist7920_128x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist7920_128x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ist7920_128x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sbn1661_122x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sbn1661_122x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sbn1661_122x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1520_122x32_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1520_122x32_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1520_122x32_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_erm19264_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_erm19264_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ks0108_erm19264_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x80_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x80_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x80_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x160_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x160_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_160x160_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_lc7981_240x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_240x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_256x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_256x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_256x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_alt_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_alt_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_128x64_alt_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_160x80_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_160x80_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_t6963_160x80_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_256x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_256x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_256x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_128x64_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_128x64_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1322_nhd_128x64_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1606_172x72_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1606_172x72_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1606_172x72_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_200x200_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_gd_200x200_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_ws_200x200_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_200x200_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_gd_200x200_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_ws_200x200_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_200x200_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_gd_200x200_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ssd1607_ws_200x200_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_296x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_v2_296x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_296x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_v2_296x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_296x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_il3820_v2_296x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1330_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1330_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_sed1330_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_nhd_240x128_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_nhd_240x128_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_nhd_240x128_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_320x240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_320x240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_ra8835_320x240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_64x8_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_64x8_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_64x8_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_32x8_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_32x8_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_32x8_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_8x8_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_8x8_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_max7219_8x8_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_a2printer_384x240_1(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_a2printer_384x240_2(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\nvoid u8g2_Setup_a2printer_384x240_f(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\n\n/* u8g2_d_setup.c generated code end */\n\n/*==========================================*/\n/* u8g2_buffer.c */\n\nvoid u8g2_SendBuffer(u8g2_t* u8g2);\nvoid u8g2_ClearBuffer(u8g2_t* u8g2);\n\nvoid u8g2_SetBufferCurrTileRow(u8g2_t* u8g2, uint8_t row) U8G2_NOINLINE;\n\nvoid u8g2_FirstPage(u8g2_t* u8g2);\nuint8_t u8g2_NextPage(u8g2_t* u8g2);\n\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n#define u8g2_SetBufferPtr(u8g2, buf) ((u8g2)->tile_buf_ptr = (buf));\n#define u8g2_GetBufferSize(u8g2) \\\n    ((u8g2)->u8x8.display_info->tile_width * 8 * (u8g2)->tile_buf_height)\n#endif\n#define u8g2_GetBufferPtr(u8g2)         ((u8g2)->tile_buf_ptr)\n#define u8g2_GetBufferTileHeight(u8g2)  ((u8g2)->tile_buf_height)\n#define u8g2_GetBufferTileWidth(u8g2)   (u8g2_GetU8x8(u8g2)->display_info->tile_width)\n/* the following variable is only valid after calling u8g2_FirstPage */\n/* renamed from Page to Buffer: the CurrTileRow is the current row of the buffer, issue #370 */\n#define u8g2_GetPageCurrTileRow(u8g2)   ((u8g2)->tile_curr_row)\n#define u8g2_GetBufferCurrTileRow(u8g2) ((u8g2)->tile_curr_row)\n\nvoid u8g2_UpdateDisplayArea(u8g2_t* u8g2, uint8_t tx, uint8_t ty, uint8_t tw, uint8_t th);\nvoid u8g2_UpdateDisplay(u8g2_t* u8g2);\n\nvoid u8g2_WriteBufferPBM(u8g2_t* u8g2, void (*out)(const char* s));\nvoid u8g2_WriteBufferXBM(u8g2_t* u8g2, void (*out)(const char* s));\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nvoid u8g2_WriteBufferPBM2(u8g2_t* u8g2, void (*out)(const char* s));\nvoid u8g2_WriteBufferXBM2(u8g2_t* u8g2, void (*out)(const char* s));\n\n/*==========================================*/\n/* u8g2_ll_hvline.c */\n/*\n  x,y\t\tUpper left position of the line within the local buffer (not the display!)\n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  asumption: \n    all clipping done\n*/\n\n/* SSD13xx, UC17xx, UC16xx */\nvoid u8g2_ll_hvline_vertical_top_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir);\n/* ST7920 */\nvoid u8g2_ll_hvline_horizontal_right_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir);\n\n/*==========================================*/\n/* u8g2_hvline.c */\n\n/* u8g2_DrawHVLine does not use u8g2_IsIntersection */\nvoid u8g2_DrawHVLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir);\n\n/* the following three function will do an intersection test of this is enabled with U8G2_WITH_INTERSECTION */\nvoid u8g2_DrawHLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len);\nvoid u8g2_DrawVLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len);\nvoid u8g2_DrawPixel(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y);\nvoid u8g2_SetDrawColor(u8g2_t* u8g2, uint8_t color)\n    U8G2_NOINLINE; /* u8g: u8g_SetColorIndex(u8g_t *u8g, uint8_t idx); */\n\n/*==========================================*/\n/* u8g2_bitmap.c */\nvoid u8g2_SetBitmapMode(u8g2_t* u8g2, uint8_t is_transparent);\nvoid u8g2_DrawHorizontalBitmap(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    const uint8_t* b);\nvoid u8g2_DrawBitmap(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t cnt,\n    u8g2_uint_t h,\n    const uint8_t* bitmap);\nvoid u8g2_DrawXBM(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    const uint8_t* bitmap);\nvoid u8g2_DrawXBMP(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    const uint8_t* bitmap); /* assumes bitmap in PROGMEM */\n\n/*==========================================*/\n/* u8g2_intersection.c */\n#ifdef U8G2_WITH_INTERSECTION\nuint8_t u8g2_IsIntersection(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t x1,\n    u8g2_uint_t y1);\n#endif /* U8G2_WITH_INTERSECTION */\n\n/*==========================================*/\n/* u8g2_circle.c */\n#define U8G2_DRAW_UPPER_RIGHT 0x01\n#define U8G2_DRAW_UPPER_LEFT  0x02\n#define U8G2_DRAW_LOWER_LEFT  0x04\n#define U8G2_DRAW_LOWER_RIGHT 0x08\n#define U8G2_DRAW_ALL \\\n    (U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_LOWER_RIGHT | U8G2_DRAW_LOWER_LEFT)\nvoid u8g2_DrawCircle(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option);\nvoid u8g2_DrawDisc(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option);\nvoid u8g2_DrawEllipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option);\nvoid u8g2_DrawFilledEllipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option);\n\n/*==========================================*/\n/* u8g2_line.c */\nvoid u8g2_DrawLine(u8g2_t* u8g2, u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2);\n\n/*==========================================*/\n/* u8g2_box.c */\nvoid u8g2_DrawBox(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h);\nvoid u8g2_DrawFrame(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h);\nvoid u8g2_DrawRBox(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    u8g2_uint_t r);\nvoid u8g2_DrawRFrame(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    u8g2_uint_t r);\n\n/*==========================================*/\n/* u8g2_polygon.c */\nvoid u8g2_ClearPolygonXY(void);\nvoid u8g2_AddPolygonXY(u8g2_t* u8g2, int16_t x, int16_t y);\nvoid u8g2_DrawPolygon(u8g2_t* u8g2);\nvoid u8g2_DrawTriangle(\n    u8g2_t* u8g2,\n    int16_t x0,\n    int16_t y0,\n    int16_t x1,\n    int16_t y1,\n    int16_t x2,\n    int16_t y2);\n\n/*==========================================*/\n/* u8g2_kerning.c */\n//uint8_t u8g2_GetNullKerning(u8g2_t *u8g2, uint16_t e1, uint16_t e2);\nuint8_t u8g2_GetKerning(u8g2_t* u8g2, u8g2_kerning_t* kerning, uint16_t e1, uint16_t e2);\nuint8_t u8g2_GetKerningByTable(u8g2_t* u8g2, const uint16_t* kt, uint16_t e1, uint16_t e2);\n\n/*==========================================*/\n/* u8g2_font.c */\n\nu8g2_uint_t u8g2_add_vector_y(u8g2_uint_t dy, int8_t x, int8_t y, uint8_t dir) U8G2_NOINLINE;\nu8g2_uint_t u8g2_add_vector_x(u8g2_uint_t dx, int8_t x, int8_t y, uint8_t dir) U8G2_NOINLINE;\n\nsize_t u8g2_GetFontSize(const uint8_t* font_arg);\n\n#define U8G2_FONT_HEIGHT_MODE_TEXT  0\n#define U8G2_FONT_HEIGHT_MODE_XTEXT 1\n#define U8G2_FONT_HEIGHT_MODE_ALL   2\n\nvoid u8g2_SetFont(u8g2_t* u8g2, const uint8_t* font);\nvoid u8g2_SetFontMode(u8g2_t* u8g2, uint8_t is_transparent);\n\nuint8_t u8g2_IsGlyph(u8g2_t* u8g2, uint16_t requested_encoding);\nint8_t u8g2_GetGlyphWidth(u8g2_t* u8g2, uint16_t requested_encoding);\nu8g2_uint_t u8g2_DrawGlyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding);\nint8_t u8g2_GetStrX(u8g2_t* u8g2, const char* s); /* for u8g compatibility */\n\nvoid u8g2_SetFontDirection(u8g2_t* u8g2, uint8_t dir);\nu8g2_uint_t u8g2_DrawStr(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str);\nu8g2_uint_t u8g2_DrawUTF8(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str);\nu8g2_uint_t u8g2_DrawExtendedUTF8(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    uint8_t to_left,\n    u8g2_kerning_t* kerning,\n    const char* str);\nu8g2_uint_t u8g2_DrawExtUTF8(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    uint8_t to_left,\n    const uint16_t* kerning_table,\n    const char* str);\n\n#define u8g2_GetMaxCharHeight(u8g2) ((u8g2)->font_info.max_char_height)\n#define u8g2_GetMaxCharWidth(u8g2)  ((u8g2)->font_info.max_char_width)\n#define u8g2_GetAscent(u8g2)        ((u8g2)->font_ref_ascent)\n#define u8g2_GetDescent(u8g2)       ((u8g2)->font_ref_descent)\n#define u8g2_GetFontAscent(u8g2)    ((u8g2)->font_ref_ascent)\n#define u8g2_GetFontDescent(u8g2)   ((u8g2)->font_ref_descent)\n\nuint8_t u8g2_IsAllValidUTF8(u8g2_t* u8g2, const char* str); // checks whether all codes are valid\n\nu8g2_long_t u8g2_GetStrWidth(u8g2_t* u8g2, const char* s);\nu8g2_uint_t u8g2_GetUTF8Width(u8g2_t* u8g2, const char* str);\n\nvoid u8g2_SetFontPosBaseline(u8g2_t* u8g2);\nvoid u8g2_SetFontPosBottom(u8g2_t* u8g2);\nvoid u8g2_SetFontPosTop(u8g2_t* u8g2);\nvoid u8g2_SetFontPosCenter(u8g2_t* u8g2);\n\nvoid u8g2_SetFontRefHeightText(u8g2_t* u8g2);\nvoid u8g2_SetFontRefHeightExtendedText(u8g2_t* u8g2);\nvoid u8g2_SetFontRefHeightAll(u8g2_t* u8g2);\n\n/*==========================================*/\n/* u8log_u8g2.c */\nvoid u8g2_DrawLog(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8log_t* u8log);\nvoid u8log_u8g2_cb(u8log_t* u8log);\n\n/*==========================================*/\n/* u8g2_selection_list.c */\nvoid u8g2_DrawUTF8Line(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    const char* s,\n    uint8_t border_size,\n    uint8_t is_invert);\nu8g2_uint_t u8g2_DrawUTF8Lines(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t line_height,\n    const char* s);\nuint8_t u8g2_UserInterfaceSelectionList(\n    u8g2_t* u8g2,\n    const char* title,\n    uint8_t start_pos,\n    const char* sl);\n\n/*==========================================*/\n/* u8g2_message.c */\nuint8_t u8g2_UserInterfaceMessage(\n    u8g2_t* u8g2,\n    const char* title1,\n    const char* title2,\n    const char* title3,\n    const char* buttons);\n\n/*==========================================*/\n/* u8g2_input_value.c */\nuint8_t u8g2_UserInterfaceInputValue(\n    u8g2_t* u8g2,\n    const char* title,\n    const char* pre,\n    uint8_t* value,\n    uint8_t lo,\n    uint8_t hi,\n    uint8_t digits,\n    const char* post);\n\n/*==========================================*/\n/* u8x8_d_sdl_128x64.c */\nvoid u8g2_SetupBuffer_SDL_128x64(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\nvoid u8g2_SetupBuffer_SDL_128x64_4(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\nvoid u8g2_SetupBuffer_SDL_128x64_1(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\n\n/*==========================================*/\n/* u8x8_d_tga.c */\nvoid u8g2_SetupBuffer_TGA_DESC(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\nvoid u8g2_SetupBuffer_TGA_LCD(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\n\n/*==========================================*/\n/* u8x8_d_bitmap.c */\nvoid u8g2_SetupBitmap(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* u8g2_cb,\n    uint16_t pixel_width,\n    uint16_t pixel_height);\n\n/*==========================================*/\n/* u8x8_d_utf8.c */\n/* 96x32 stdout */\nvoid u8g2_SetupBuffer_Utf8(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb);\n\n/*==========================================*/\n/* itoa procedures */\n#define u8g2_u8toa  u8x8_u8toa\n#define u8g2_u16toa u8x8_u16toa\n\n/*==========================================*/\n\n/* start font list */\nextern const uint8_t u8g2_font_u8glib_4_tf[] U8G2_FONT_SECTION(\"u8g2_font_u8glib_4_tf\");\nextern const uint8_t u8g2_font_u8glib_4_tr[] U8G2_FONT_SECTION(\"u8g2_font_u8glib_4_tr\");\nextern const uint8_t u8g2_font_u8glib_4_hf[] U8G2_FONT_SECTION(\"u8g2_font_u8glib_4_hf\");\nextern const uint8_t u8g2_font_u8glib_4_hr[] U8G2_FONT_SECTION(\"u8g2_font_u8glib_4_hr\");\nextern const uint8_t u8g2_font_m2icon_5_tf[] U8G2_FONT_SECTION(\"u8g2_font_m2icon_5_tf\");\nextern const uint8_t u8g2_font_m2icon_7_tf[] U8G2_FONT_SECTION(\"u8g2_font_m2icon_7_tf\");\nextern const uint8_t u8g2_font_m2icon_9_tf[] U8G2_FONT_SECTION(\"u8g2_font_m2icon_9_tf\");\nextern const uint8_t u8g2_font_emoticons21_tr[] U8G2_FONT_SECTION(\"u8g2_font_emoticons21_tr\");\nextern const uint8_t u8g2_font_battery19_tn[] U8G2_FONT_SECTION(\"u8g2_font_battery19_tn\");\nextern const uint8_t u8g2_font_freedoomr10_tu[] U8G2_FONT_SECTION(\"u8g2_font_freedoomr10_tu\");\nextern const uint8_t u8g2_font_freedoomr10_mu[] U8G2_FONT_SECTION(\"u8g2_font_freedoomr10_mu\");\nextern const uint8_t u8g2_font_freedoomr25_tn[] U8G2_FONT_SECTION(\"u8g2_font_freedoomr25_tn\");\nextern const uint8_t u8g2_font_freedoomr25_mn[] U8G2_FONT_SECTION(\"u8g2_font_freedoomr25_mn\");\nextern const uint8_t\n    u8g2_font_7Segments_26x42_mn[] U8G2_FONT_SECTION(\"u8g2_font_7Segments_26x42_mn\");\nextern const uint8_t\n    u8g2_font_amstrad_cpc_extended_8f[] U8G2_FONT_SECTION(\"u8g2_font_amstrad_cpc_extended_8f\");\nextern const uint8_t\n    u8g2_font_amstrad_cpc_extended_8r[] U8G2_FONT_SECTION(\"u8g2_font_amstrad_cpc_extended_8r\");\nextern const uint8_t\n    u8g2_font_amstrad_cpc_extended_8n[] U8G2_FONT_SECTION(\"u8g2_font_amstrad_cpc_extended_8n\");\nextern const uint8_t\n    u8g2_font_amstrad_cpc_extended_8u[] U8G2_FONT_SECTION(\"u8g2_font_amstrad_cpc_extended_8u\");\nextern const uint8_t u8g2_font_cursor_tf[] U8G2_FONT_SECTION(\"u8g2_font_cursor_tf\");\nextern const uint8_t u8g2_font_cursor_tr[] U8G2_FONT_SECTION(\"u8g2_font_cursor_tr\");\nextern const uint8_t u8g2_font_micro_tr[] U8G2_FONT_SECTION(\"u8g2_font_micro_tr\");\nextern const uint8_t u8g2_font_micro_tn[] U8G2_FONT_SECTION(\"u8g2_font_micro_tn\");\nextern const uint8_t u8g2_font_micro_mr[] U8G2_FONT_SECTION(\"u8g2_font_micro_mr\");\nextern const uint8_t u8g2_font_micro_mn[] U8G2_FONT_SECTION(\"u8g2_font_micro_mn\");\nextern const uint8_t u8g2_font_4x6_tf[] U8G2_FONT_SECTION(\"u8g2_font_4x6_tf\");\nextern const uint8_t u8g2_font_4x6_tr[] U8G2_FONT_SECTION(\"u8g2_font_4x6_tr\");\nextern const uint8_t u8g2_font_4x6_tn[] U8G2_FONT_SECTION(\"u8g2_font_4x6_tn\");\nextern const uint8_t u8g2_font_4x6_mf[] U8G2_FONT_SECTION(\"u8g2_font_4x6_mf\");\nextern const uint8_t u8g2_font_4x6_mr[] U8G2_FONT_SECTION(\"u8g2_font_4x6_mr\");\nextern const uint8_t u8g2_font_4x6_mn[] U8G2_FONT_SECTION(\"u8g2_font_4x6_mn\");\nextern const uint8_t u8g2_font_4x6_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_4x6_t_cyrillic\");\nextern const uint8_t u8g2_font_5x7_tf[] U8G2_FONT_SECTION(\"u8g2_font_5x7_tf\");\nextern const uint8_t u8g2_font_5x7_tr[] U8G2_FONT_SECTION(\"u8g2_font_5x7_tr\");\nextern const uint8_t u8g2_font_5x7_tn[] U8G2_FONT_SECTION(\"u8g2_font_5x7_tn\");\nextern const uint8_t u8g2_font_5x7_mf[] U8G2_FONT_SECTION(\"u8g2_font_5x7_mf\");\nextern const uint8_t u8g2_font_5x7_mr[] U8G2_FONT_SECTION(\"u8g2_font_5x7_mr\");\nextern const uint8_t u8g2_font_5x7_mn[] U8G2_FONT_SECTION(\"u8g2_font_5x7_mn\");\nextern const uint8_t u8g2_font_5x7_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_5x7_t_cyrillic\");\nextern const uint8_t u8g2_font_5x8_tf[] U8G2_FONT_SECTION(\"u8g2_font_5x8_tf\");\nextern const uint8_t u8g2_font_5x8_tr[] U8G2_FONT_SECTION(\"u8g2_font_5x8_tr\");\nextern const uint8_t u8g2_font_5x8_tn[] U8G2_FONT_SECTION(\"u8g2_font_5x8_tn\");\nextern const uint8_t u8g2_font_5x8_mf[] U8G2_FONT_SECTION(\"u8g2_font_5x8_mf\");\nextern const uint8_t u8g2_font_5x8_mr[] U8G2_FONT_SECTION(\"u8g2_font_5x8_mr\");\nextern const uint8_t u8g2_font_5x8_mn[] U8G2_FONT_SECTION(\"u8g2_font_5x8_mn\");\nextern const uint8_t u8g2_font_5x8_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_5x8_t_cyrillic\");\nextern const uint8_t u8g2_font_6x10_tf[] U8G2_FONT_SECTION(\"u8g2_font_6x10_tf\");\nextern const uint8_t u8g2_font_6x10_tr[] U8G2_FONT_SECTION(\"u8g2_font_6x10_tr\");\nextern const uint8_t u8g2_font_6x10_tn[] U8G2_FONT_SECTION(\"u8g2_font_6x10_tn\");\nextern const uint8_t u8g2_font_6x10_mf[] U8G2_FONT_SECTION(\"u8g2_font_6x10_mf\");\nextern const uint8_t u8g2_font_6x10_mr[] U8G2_FONT_SECTION(\"u8g2_font_6x10_mr\");\nextern const uint8_t u8g2_font_6x10_mn[] U8G2_FONT_SECTION(\"u8g2_font_6x10_mn\");\nextern const uint8_t u8g2_font_6x12_tf[] U8G2_FONT_SECTION(\"u8g2_font_6x12_tf\");\nextern const uint8_t u8g2_font_6x12_tr[] U8G2_FONT_SECTION(\"u8g2_font_6x12_tr\");\nextern const uint8_t u8g2_font_6x12_tn[] U8G2_FONT_SECTION(\"u8g2_font_6x12_tn\");\nextern const uint8_t u8g2_font_6x12_te[] U8G2_FONT_SECTION(\"u8g2_font_6x12_te\");\nextern const uint8_t u8g2_font_6x12_mf[] U8G2_FONT_SECTION(\"u8g2_font_6x12_mf\");\nextern const uint8_t u8g2_font_6x12_mr[] U8G2_FONT_SECTION(\"u8g2_font_6x12_mr\");\nextern const uint8_t u8g2_font_6x12_mn[] U8G2_FONT_SECTION(\"u8g2_font_6x12_mn\");\nextern const uint8_t u8g2_font_6x12_me[] U8G2_FONT_SECTION(\"u8g2_font_6x12_me\");\nextern const uint8_t u8g2_font_6x12_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_6x12_t_symbols\");\nextern const uint8_t u8g2_font_6x12_m_symbols[] U8G2_FONT_SECTION(\"u8g2_font_6x12_m_symbols\");\nextern const uint8_t u8g2_font_6x12_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_6x12_t_cyrillic\");\nextern const uint8_t u8g2_font_6x13_tf[] U8G2_FONT_SECTION(\"u8g2_font_6x13_tf\");\nextern const uint8_t u8g2_font_6x13_tr[] U8G2_FONT_SECTION(\"u8g2_font_6x13_tr\");\nextern const uint8_t u8g2_font_6x13_tn[] U8G2_FONT_SECTION(\"u8g2_font_6x13_tn\");\nextern const uint8_t u8g2_font_6x13_te[] U8G2_FONT_SECTION(\"u8g2_font_6x13_te\");\nextern const uint8_t u8g2_font_6x13_mf[] U8G2_FONT_SECTION(\"u8g2_font_6x13_mf\");\nextern const uint8_t u8g2_font_6x13_mr[] U8G2_FONT_SECTION(\"u8g2_font_6x13_mr\");\nextern const uint8_t u8g2_font_6x13_mn[] U8G2_FONT_SECTION(\"u8g2_font_6x13_mn\");\nextern const uint8_t u8g2_font_6x13_me[] U8G2_FONT_SECTION(\"u8g2_font_6x13_me\");\nextern const uint8_t u8g2_font_6x13_t_hebrew[] U8G2_FONT_SECTION(\"u8g2_font_6x13_t_hebrew\");\nextern const uint8_t u8g2_font_6x13_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_6x13_t_cyrillic\");\nextern const uint8_t u8g2_font_6x13B_tf[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_tf\");\nextern const uint8_t u8g2_font_6x13B_tr[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_tr\");\nextern const uint8_t u8g2_font_6x13B_tn[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_tn\");\nextern const uint8_t u8g2_font_6x13B_mf[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_mf\");\nextern const uint8_t u8g2_font_6x13B_mr[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_mr\");\nextern const uint8_t u8g2_font_6x13B_mn[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_mn\");\nextern const uint8_t u8g2_font_6x13B_t_hebrew[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_t_hebrew\");\nextern const uint8_t u8g2_font_6x13B_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_6x13B_t_cyrillic\");\nextern const uint8_t u8g2_font_6x13O_tf[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_tf\");\nextern const uint8_t u8g2_font_6x13O_tr[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_tr\");\nextern const uint8_t u8g2_font_6x13O_tn[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_tn\");\nextern const uint8_t u8g2_font_6x13O_mf[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_mf\");\nextern const uint8_t u8g2_font_6x13O_mr[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_mr\");\nextern const uint8_t u8g2_font_6x13O_mn[] U8G2_FONT_SECTION(\"u8g2_font_6x13O_mn\");\nextern const uint8_t u8g2_font_7x13_tf[] U8G2_FONT_SECTION(\"u8g2_font_7x13_tf\");\nextern const uint8_t u8g2_font_7x13_tr[] U8G2_FONT_SECTION(\"u8g2_font_7x13_tr\");\nextern const uint8_t u8g2_font_7x13_tn[] U8G2_FONT_SECTION(\"u8g2_font_7x13_tn\");\nextern const uint8_t u8g2_font_7x13_te[] U8G2_FONT_SECTION(\"u8g2_font_7x13_te\");\nextern const uint8_t u8g2_font_7x13_mf[] U8G2_FONT_SECTION(\"u8g2_font_7x13_mf\");\nextern const uint8_t u8g2_font_7x13_mr[] U8G2_FONT_SECTION(\"u8g2_font_7x13_mr\");\nextern const uint8_t u8g2_font_7x13_mn[] U8G2_FONT_SECTION(\"u8g2_font_7x13_mn\");\nextern const uint8_t u8g2_font_7x13_me[] U8G2_FONT_SECTION(\"u8g2_font_7x13_me\");\nextern const uint8_t u8g2_font_7x13_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_7x13_t_symbols\");\nextern const uint8_t u8g2_font_7x13_m_symbols[] U8G2_FONT_SECTION(\"u8g2_font_7x13_m_symbols\");\nextern const uint8_t u8g2_font_7x13_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_7x13_t_cyrillic\");\nextern const uint8_t u8g2_font_7x13B_tf[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_tf\");\nextern const uint8_t u8g2_font_7x13B_tr[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_tr\");\nextern const uint8_t u8g2_font_7x13B_tn[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_tn\");\nextern const uint8_t u8g2_font_7x13B_mf[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_mf\");\nextern const uint8_t u8g2_font_7x13B_mr[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_mr\");\nextern const uint8_t u8g2_font_7x13B_mn[] U8G2_FONT_SECTION(\"u8g2_font_7x13B_mn\");\nextern const uint8_t u8g2_font_7x13O_tf[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_tf\");\nextern const uint8_t u8g2_font_7x13O_tr[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_tr\");\nextern const uint8_t u8g2_font_7x13O_tn[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_tn\");\nextern const uint8_t u8g2_font_7x13O_mf[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_mf\");\nextern const uint8_t u8g2_font_7x13O_mr[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_mr\");\nextern const uint8_t u8g2_font_7x13O_mn[] U8G2_FONT_SECTION(\"u8g2_font_7x13O_mn\");\nextern const uint8_t u8g2_font_7x14_tf[] U8G2_FONT_SECTION(\"u8g2_font_7x14_tf\");\nextern const uint8_t u8g2_font_7x14_tr[] U8G2_FONT_SECTION(\"u8g2_font_7x14_tr\");\nextern const uint8_t u8g2_font_7x14_tn[] U8G2_FONT_SECTION(\"u8g2_font_7x14_tn\");\nextern const uint8_t u8g2_font_7x14_mf[] U8G2_FONT_SECTION(\"u8g2_font_7x14_mf\");\nextern const uint8_t u8g2_font_7x14_mr[] U8G2_FONT_SECTION(\"u8g2_font_7x14_mr\");\nextern const uint8_t u8g2_font_7x14_mn[] U8G2_FONT_SECTION(\"u8g2_font_7x14_mn\");\nextern const uint8_t u8g2_font_7x14B_tf[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_tf\");\nextern const uint8_t u8g2_font_7x14B_tr[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_tr\");\nextern const uint8_t u8g2_font_7x14B_tn[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_tn\");\nextern const uint8_t u8g2_font_7x14B_mf[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_mf\");\nextern const uint8_t u8g2_font_7x14B_mr[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_mr\");\nextern const uint8_t u8g2_font_7x14B_mn[] U8G2_FONT_SECTION(\"u8g2_font_7x14B_mn\");\nextern const uint8_t u8g2_font_8x13_tf[] U8G2_FONT_SECTION(\"u8g2_font_8x13_tf\");\nextern const uint8_t u8g2_font_8x13_tr[] U8G2_FONT_SECTION(\"u8g2_font_8x13_tr\");\nextern const uint8_t u8g2_font_8x13_tn[] U8G2_FONT_SECTION(\"u8g2_font_8x13_tn\");\nextern const uint8_t u8g2_font_8x13_te[] U8G2_FONT_SECTION(\"u8g2_font_8x13_te\");\nextern const uint8_t u8g2_font_8x13_mf[] U8G2_FONT_SECTION(\"u8g2_font_8x13_mf\");\nextern const uint8_t u8g2_font_8x13_mr[] U8G2_FONT_SECTION(\"u8g2_font_8x13_mr\");\nextern const uint8_t u8g2_font_8x13_mn[] U8G2_FONT_SECTION(\"u8g2_font_8x13_mn\");\nextern const uint8_t u8g2_font_8x13_me[] U8G2_FONT_SECTION(\"u8g2_font_8x13_me\");\nextern const uint8_t u8g2_font_8x13_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_8x13_t_symbols\");\nextern const uint8_t u8g2_font_8x13_m_symbols[] U8G2_FONT_SECTION(\"u8g2_font_8x13_m_symbols\");\nextern const uint8_t u8g2_font_8x13_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_8x13_t_cyrillic\");\nextern const uint8_t u8g2_font_8x13B_tf[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_tf\");\nextern const uint8_t u8g2_font_8x13B_tr[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_tr\");\nextern const uint8_t u8g2_font_8x13B_tn[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_tn\");\nextern const uint8_t u8g2_font_8x13B_mf[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_mf\");\nextern const uint8_t u8g2_font_8x13B_mr[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_mr\");\nextern const uint8_t u8g2_font_8x13B_mn[] U8G2_FONT_SECTION(\"u8g2_font_8x13B_mn\");\nextern const uint8_t u8g2_font_8x13O_tf[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_tf\");\nextern const uint8_t u8g2_font_8x13O_tr[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_tr\");\nextern const uint8_t u8g2_font_8x13O_tn[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_tn\");\nextern const uint8_t u8g2_font_8x13O_mf[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_mf\");\nextern const uint8_t u8g2_font_8x13O_mr[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_mr\");\nextern const uint8_t u8g2_font_8x13O_mn[] U8G2_FONT_SECTION(\"u8g2_font_8x13O_mn\");\nextern const uint8_t u8g2_font_9x15_tf[] U8G2_FONT_SECTION(\"u8g2_font_9x15_tf\");\nextern const uint8_t u8g2_font_9x15_tr[] U8G2_FONT_SECTION(\"u8g2_font_9x15_tr\");\nextern const uint8_t u8g2_font_9x15_tn[] U8G2_FONT_SECTION(\"u8g2_font_9x15_tn\");\nextern const uint8_t u8g2_font_9x15_te[] U8G2_FONT_SECTION(\"u8g2_font_9x15_te\");\nextern const uint8_t u8g2_font_9x15_mf[] U8G2_FONT_SECTION(\"u8g2_font_9x15_mf\");\nextern const uint8_t u8g2_font_9x15_mr[] U8G2_FONT_SECTION(\"u8g2_font_9x15_mr\");\nextern const uint8_t u8g2_font_9x15_mn[] U8G2_FONT_SECTION(\"u8g2_font_9x15_mn\");\nextern const uint8_t u8g2_font_9x15_me[] U8G2_FONT_SECTION(\"u8g2_font_9x15_me\");\nextern const uint8_t u8g2_font_9x15_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_9x15_t_symbols\");\nextern const uint8_t u8g2_font_9x15_m_symbols[] U8G2_FONT_SECTION(\"u8g2_font_9x15_m_symbols\");\nextern const uint8_t u8g2_font_9x15_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_9x15_t_cyrillic\");\nextern const uint8_t u8g2_font_9x15B_tf[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_tf\");\nextern const uint8_t u8g2_font_9x15B_tr[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_tr\");\nextern const uint8_t u8g2_font_9x15B_tn[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_tn\");\nextern const uint8_t u8g2_font_9x15B_mf[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_mf\");\nextern const uint8_t u8g2_font_9x15B_mr[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_mr\");\nextern const uint8_t u8g2_font_9x15B_mn[] U8G2_FONT_SECTION(\"u8g2_font_9x15B_mn\");\nextern const uint8_t u8g2_font_9x18_tf[] U8G2_FONT_SECTION(\"u8g2_font_9x18_tf\");\nextern const uint8_t u8g2_font_9x18_tr[] U8G2_FONT_SECTION(\"u8g2_font_9x18_tr\");\nextern const uint8_t u8g2_font_9x18_tn[] U8G2_FONT_SECTION(\"u8g2_font_9x18_tn\");\nextern const uint8_t u8g2_font_9x18_mf[] U8G2_FONT_SECTION(\"u8g2_font_9x18_mf\");\nextern const uint8_t u8g2_font_9x18_mr[] U8G2_FONT_SECTION(\"u8g2_font_9x18_mr\");\nextern const uint8_t u8g2_font_9x18_mn[] U8G2_FONT_SECTION(\"u8g2_font_9x18_mn\");\nextern const uint8_t u8g2_font_9x18B_tf[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_tf\");\nextern const uint8_t u8g2_font_9x18B_tr[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_tr\");\nextern const uint8_t u8g2_font_9x18B_tn[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_tn\");\nextern const uint8_t u8g2_font_9x18B_mf[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_mf\");\nextern const uint8_t u8g2_font_9x18B_mr[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_mr\");\nextern const uint8_t u8g2_font_9x18B_mn[] U8G2_FONT_SECTION(\"u8g2_font_9x18B_mn\");\nextern const uint8_t u8g2_font_10x20_tf[] U8G2_FONT_SECTION(\"u8g2_font_10x20_tf\");\nextern const uint8_t u8g2_font_10x20_tr[] U8G2_FONT_SECTION(\"u8g2_font_10x20_tr\");\nextern const uint8_t u8g2_font_10x20_tn[] U8G2_FONT_SECTION(\"u8g2_font_10x20_tn\");\nextern const uint8_t u8g2_font_10x20_te[] U8G2_FONT_SECTION(\"u8g2_font_10x20_te\");\nextern const uint8_t u8g2_font_10x20_mf[] U8G2_FONT_SECTION(\"u8g2_font_10x20_mf\");\nextern const uint8_t u8g2_font_10x20_mr[] U8G2_FONT_SECTION(\"u8g2_font_10x20_mr\");\nextern const uint8_t u8g2_font_10x20_mn[] U8G2_FONT_SECTION(\"u8g2_font_10x20_mn\");\nextern const uint8_t u8g2_font_10x20_me[] U8G2_FONT_SECTION(\"u8g2_font_10x20_me\");\nextern const uint8_t u8g2_font_10x20_t_greek[] U8G2_FONT_SECTION(\"u8g2_font_10x20_t_greek\");\nextern const uint8_t u8g2_font_10x20_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_10x20_t_cyrillic\");\nextern const uint8_t u8g2_font_10x20_t_arabic[] U8G2_FONT_SECTION(\"u8g2_font_10x20_t_arabic\");\nextern const uint8_t u8g2_font_siji_t_6x10[] U8G2_FONT_SECTION(\"u8g2_font_siji_t_6x10\");\nextern const uint8_t\n    u8g2_font_tom_thumb_4x6_t_all[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_t_all\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_tf[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_tf\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_tr[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_tr\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_tn[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_tn\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_te[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_te\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_mf[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_mf\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_mr[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_mr\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_mn[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_mn\");\nextern const uint8_t u8g2_font_tom_thumb_4x6_me[] U8G2_FONT_SECTION(\"u8g2_font_tom_thumb_4x6_me\");\nextern const uint8_t u8g2_font_t0_11_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_tf\");\nextern const uint8_t u8g2_font_t0_11_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_tr\");\nextern const uint8_t u8g2_font_t0_11_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_tn\");\nextern const uint8_t u8g2_font_t0_11_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_te\");\nextern const uint8_t u8g2_font_t0_11_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_mf\");\nextern const uint8_t u8g2_font_t0_11_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_mr\");\nextern const uint8_t u8g2_font_t0_11_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_mn\");\nextern const uint8_t u8g2_font_t0_11_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_me\");\nextern const uint8_t u8g2_font_t0_11_t_all[] U8G2_FONT_SECTION(\"u8g2_font_t0_11_t_all\");\nextern const uint8_t u8g2_font_t0_11b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_tf\");\nextern const uint8_t u8g2_font_t0_11b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_tr\");\nextern const uint8_t u8g2_font_t0_11b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_tn\");\nextern const uint8_t u8g2_font_t0_11b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_te\");\nextern const uint8_t u8g2_font_t0_11b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_mf\");\nextern const uint8_t u8g2_font_t0_11b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_mr\");\nextern const uint8_t u8g2_font_t0_11b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_mn\");\nextern const uint8_t u8g2_font_t0_11b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_11b_me\");\nextern const uint8_t u8g2_font_t0_12_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_tf\");\nextern const uint8_t u8g2_font_t0_12_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_tr\");\nextern const uint8_t u8g2_font_t0_12_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_tn\");\nextern const uint8_t u8g2_font_t0_12_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_te\");\nextern const uint8_t u8g2_font_t0_12_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_mf\");\nextern const uint8_t u8g2_font_t0_12_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_mr\");\nextern const uint8_t u8g2_font_t0_12_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_mn\");\nextern const uint8_t u8g2_font_t0_12_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_12_me\");\nextern const uint8_t u8g2_font_t0_12b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_tf\");\nextern const uint8_t u8g2_font_t0_12b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_tr\");\nextern const uint8_t u8g2_font_t0_12b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_tn\");\nextern const uint8_t u8g2_font_t0_12b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_te\");\nextern const uint8_t u8g2_font_t0_12b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_mf\");\nextern const uint8_t u8g2_font_t0_12b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_mr\");\nextern const uint8_t u8g2_font_t0_12b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_mn\");\nextern const uint8_t u8g2_font_t0_12b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_12b_me\");\nextern const uint8_t u8g2_font_t0_13_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_tf\");\nextern const uint8_t u8g2_font_t0_13_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_tr\");\nextern const uint8_t u8g2_font_t0_13_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_tn\");\nextern const uint8_t u8g2_font_t0_13_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_te\");\nextern const uint8_t u8g2_font_t0_13_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_mf\");\nextern const uint8_t u8g2_font_t0_13_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_mr\");\nextern const uint8_t u8g2_font_t0_13_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_mn\");\nextern const uint8_t u8g2_font_t0_13_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_13_me\");\nextern const uint8_t u8g2_font_t0_13b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_tf\");\nextern const uint8_t u8g2_font_t0_13b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_tr\");\nextern const uint8_t u8g2_font_t0_13b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_tn\");\nextern const uint8_t u8g2_font_t0_13b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_te\");\nextern const uint8_t u8g2_font_t0_13b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_mf\");\nextern const uint8_t u8g2_font_t0_13b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_mr\");\nextern const uint8_t u8g2_font_t0_13b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_mn\");\nextern const uint8_t u8g2_font_t0_13b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_13b_me\");\nextern const uint8_t u8g2_font_t0_14_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_tf\");\nextern const uint8_t u8g2_font_t0_14_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_tr\");\nextern const uint8_t u8g2_font_t0_14_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_tn\");\nextern const uint8_t u8g2_font_t0_14_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_te\");\nextern const uint8_t u8g2_font_t0_14_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_mf\");\nextern const uint8_t u8g2_font_t0_14_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_mr\");\nextern const uint8_t u8g2_font_t0_14_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_mn\");\nextern const uint8_t u8g2_font_t0_14_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_14_me\");\nextern const uint8_t u8g2_font_t0_14b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_tf\");\nextern const uint8_t u8g2_font_t0_14b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_tr\");\nextern const uint8_t u8g2_font_t0_14b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_tn\");\nextern const uint8_t u8g2_font_t0_14b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_te\");\nextern const uint8_t u8g2_font_t0_14b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_mf\");\nextern const uint8_t u8g2_font_t0_14b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_mr\");\nextern const uint8_t u8g2_font_t0_14b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_mn\");\nextern const uint8_t u8g2_font_t0_14b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_14b_me\");\nextern const uint8_t u8g2_font_t0_15_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_tf\");\nextern const uint8_t u8g2_font_t0_15_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_tr\");\nextern const uint8_t u8g2_font_t0_15_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_tn\");\nextern const uint8_t u8g2_font_t0_15_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_te\");\nextern const uint8_t u8g2_font_t0_15_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_mf\");\nextern const uint8_t u8g2_font_t0_15_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_mr\");\nextern const uint8_t u8g2_font_t0_15_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_mn\");\nextern const uint8_t u8g2_font_t0_15_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_15_me\");\nextern const uint8_t u8g2_font_t0_15b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_tf\");\nextern const uint8_t u8g2_font_t0_15b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_tr\");\nextern const uint8_t u8g2_font_t0_15b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_tn\");\nextern const uint8_t u8g2_font_t0_15b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_te\");\nextern const uint8_t u8g2_font_t0_15b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_mf\");\nextern const uint8_t u8g2_font_t0_15b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_mr\");\nextern const uint8_t u8g2_font_t0_15b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_mn\");\nextern const uint8_t u8g2_font_t0_15b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_15b_me\");\nextern const uint8_t u8g2_font_t0_16_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_tf\");\nextern const uint8_t u8g2_font_t0_16_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_tr\");\nextern const uint8_t u8g2_font_t0_16_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_tn\");\nextern const uint8_t u8g2_font_t0_16_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_te\");\nextern const uint8_t u8g2_font_t0_16_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_mf\");\nextern const uint8_t u8g2_font_t0_16_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_mr\");\nextern const uint8_t u8g2_font_t0_16_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_mn\");\nextern const uint8_t u8g2_font_t0_16_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_16_me\");\nextern const uint8_t u8g2_font_t0_16b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_tf\");\nextern const uint8_t u8g2_font_t0_16b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_tr\");\nextern const uint8_t u8g2_font_t0_16b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_tn\");\nextern const uint8_t u8g2_font_t0_16b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_te\");\nextern const uint8_t u8g2_font_t0_16b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_mf\");\nextern const uint8_t u8g2_font_t0_16b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_mr\");\nextern const uint8_t u8g2_font_t0_16b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_mn\");\nextern const uint8_t u8g2_font_t0_16b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_16b_me\");\nextern const uint8_t u8g2_font_t0_17_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_tf\");\nextern const uint8_t u8g2_font_t0_17_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_tr\");\nextern const uint8_t u8g2_font_t0_17_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_tn\");\nextern const uint8_t u8g2_font_t0_17_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_te\");\nextern const uint8_t u8g2_font_t0_17_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_mf\");\nextern const uint8_t u8g2_font_t0_17_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_mr\");\nextern const uint8_t u8g2_font_t0_17_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_mn\");\nextern const uint8_t u8g2_font_t0_17_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_17_me\");\nextern const uint8_t u8g2_font_t0_17b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_tf\");\nextern const uint8_t u8g2_font_t0_17b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_tr\");\nextern const uint8_t u8g2_font_t0_17b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_tn\");\nextern const uint8_t u8g2_font_t0_17b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_te\");\nextern const uint8_t u8g2_font_t0_17b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_mf\");\nextern const uint8_t u8g2_font_t0_17b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_mr\");\nextern const uint8_t u8g2_font_t0_17b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_mn\");\nextern const uint8_t u8g2_font_t0_17b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_17b_me\");\nextern const uint8_t u8g2_font_t0_18_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_tf\");\nextern const uint8_t u8g2_font_t0_18_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_tr\");\nextern const uint8_t u8g2_font_t0_18_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_tn\");\nextern const uint8_t u8g2_font_t0_18_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_te\");\nextern const uint8_t u8g2_font_t0_18_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_mf\");\nextern const uint8_t u8g2_font_t0_18_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_mr\");\nextern const uint8_t u8g2_font_t0_18_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_mn\");\nextern const uint8_t u8g2_font_t0_18_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_18_me\");\nextern const uint8_t u8g2_font_t0_18b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_tf\");\nextern const uint8_t u8g2_font_t0_18b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_tr\");\nextern const uint8_t u8g2_font_t0_18b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_tn\");\nextern const uint8_t u8g2_font_t0_18b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_te\");\nextern const uint8_t u8g2_font_t0_18b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_mf\");\nextern const uint8_t u8g2_font_t0_18b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_mr\");\nextern const uint8_t u8g2_font_t0_18b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_mn\");\nextern const uint8_t u8g2_font_t0_18b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_18b_me\");\nextern const uint8_t u8g2_font_t0_22_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_tf\");\nextern const uint8_t u8g2_font_t0_22_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_tr\");\nextern const uint8_t u8g2_font_t0_22_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_tn\");\nextern const uint8_t u8g2_font_t0_22_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_te\");\nextern const uint8_t u8g2_font_t0_22_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_mf\");\nextern const uint8_t u8g2_font_t0_22_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_mr\");\nextern const uint8_t u8g2_font_t0_22_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_mn\");\nextern const uint8_t u8g2_font_t0_22_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_22_me\");\nextern const uint8_t u8g2_font_t0_22b_tf[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_tf\");\nextern const uint8_t u8g2_font_t0_22b_tr[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_tr\");\nextern const uint8_t u8g2_font_t0_22b_tn[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_tn\");\nextern const uint8_t u8g2_font_t0_22b_te[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_te\");\nextern const uint8_t u8g2_font_t0_22b_mf[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_mf\");\nextern const uint8_t u8g2_font_t0_22b_mr[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_mr\");\nextern const uint8_t u8g2_font_t0_22b_mn[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_mn\");\nextern const uint8_t u8g2_font_t0_22b_me[] U8G2_FONT_SECTION(\"u8g2_font_t0_22b_me\");\nextern const uint8_t\n    u8g2_font_open_iconic_all_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_all_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_app_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_app_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_arrow_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_arrow_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_check_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_check_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_email_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_email_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_embedded_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_embedded_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_gui_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_gui_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_human_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_human_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_mime_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_mime_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_other_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_other_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_play_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_play_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_text_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_text_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_thing_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_thing_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_weather_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_weather_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_www_1x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_www_1x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_all_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_all_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_app_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_app_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_arrow_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_arrow_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_check_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_check_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_email_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_email_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_embedded_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_embedded_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_gui_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_gui_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_human_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_human_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_mime_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_mime_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_other_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_other_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_play_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_play_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_text_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_text_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_thing_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_thing_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_weather_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_weather_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_www_2x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_www_2x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_all_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_all_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_app_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_app_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_arrow_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_arrow_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_check_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_check_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_email_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_email_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_embedded_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_embedded_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_gui_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_gui_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_human_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_human_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_mime_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_mime_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_other_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_other_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_play_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_play_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_text_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_text_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_thing_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_thing_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_weather_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_weather_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_www_4x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_www_4x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_all_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_all_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_app_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_app_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_arrow_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_arrow_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_check_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_check_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_email_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_email_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_embedded_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_embedded_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_gui_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_gui_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_human_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_human_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_mime_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_mime_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_other_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_other_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_play_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_play_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_text_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_text_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_thing_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_thing_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_weather_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_weather_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_www_6x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_www_6x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_all_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_all_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_app_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_app_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_arrow_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_arrow_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_check_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_check_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_email_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_email_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_embedded_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_embedded_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_gui_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_gui_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_human_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_human_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_mime_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_mime_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_other_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_other_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_play_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_play_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_text_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_text_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_thing_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_thing_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_weather_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_weather_8x_t\");\nextern const uint8_t\n    u8g2_font_open_iconic_www_8x_t[] U8G2_FONT_SECTION(\"u8g2_font_open_iconic_www_8x_t\");\nextern const uint8_t u8g2_font_profont10_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont10_tf\");\nextern const uint8_t u8g2_font_profont10_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont10_tr\");\nextern const uint8_t u8g2_font_profont10_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont10_tn\");\nextern const uint8_t u8g2_font_profont10_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont10_mf\");\nextern const uint8_t u8g2_font_profont10_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont10_mr\");\nextern const uint8_t u8g2_font_profont10_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont10_mn\");\nextern const uint8_t u8g2_font_profont11_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont11_tf\");\nextern const uint8_t u8g2_font_profont11_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont11_tr\");\nextern const uint8_t u8g2_font_profont11_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont11_tn\");\nextern const uint8_t u8g2_font_profont11_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont11_mf\");\nextern const uint8_t u8g2_font_profont11_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont11_mr\");\nextern const uint8_t u8g2_font_profont11_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont11_mn\");\nextern const uint8_t u8g2_font_profont12_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont12_tf\");\nextern const uint8_t u8g2_font_profont12_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont12_tr\");\nextern const uint8_t u8g2_font_profont12_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont12_tn\");\nextern const uint8_t u8g2_font_profont12_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont12_mf\");\nextern const uint8_t u8g2_font_profont12_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont12_mr\");\nextern const uint8_t u8g2_font_profont12_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont12_mn\");\nextern const uint8_t u8g2_font_profont15_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont15_tf\");\nextern const uint8_t u8g2_font_profont15_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont15_tr\");\nextern const uint8_t u8g2_font_profont15_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont15_tn\");\nextern const uint8_t u8g2_font_profont15_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont15_mf\");\nextern const uint8_t u8g2_font_profont15_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont15_mr\");\nextern const uint8_t u8g2_font_profont15_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont15_mn\");\nextern const uint8_t u8g2_font_profont17_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont17_tf\");\nextern const uint8_t u8g2_font_profont17_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont17_tr\");\nextern const uint8_t u8g2_font_profont17_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont17_tn\");\nextern const uint8_t u8g2_font_profont17_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont17_mf\");\nextern const uint8_t u8g2_font_profont17_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont17_mr\");\nextern const uint8_t u8g2_font_profont17_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont17_mn\");\nextern const uint8_t u8g2_font_profont22_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont22_tf\");\nextern const uint8_t u8g2_font_profont22_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont22_tr\");\nextern const uint8_t u8g2_font_profont22_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont22_tn\");\nextern const uint8_t u8g2_font_profont22_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont22_mf\");\nextern const uint8_t u8g2_font_profont22_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont22_mr\");\nextern const uint8_t u8g2_font_profont22_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont22_mn\");\nextern const uint8_t u8g2_font_profont29_tf[] U8G2_FONT_SECTION(\"u8g2_font_profont29_tf\");\nextern const uint8_t u8g2_font_profont29_tr[] U8G2_FONT_SECTION(\"u8g2_font_profont29_tr\");\nextern const uint8_t u8g2_font_profont29_tn[] U8G2_FONT_SECTION(\"u8g2_font_profont29_tn\");\nextern const uint8_t u8g2_font_profont29_mf[] U8G2_FONT_SECTION(\"u8g2_font_profont29_mf\");\nextern const uint8_t u8g2_font_profont29_mr[] U8G2_FONT_SECTION(\"u8g2_font_profont29_mr\");\nextern const uint8_t u8g2_font_profont29_mn[] U8G2_FONT_SECTION(\"u8g2_font_profont29_mn\");\nextern const uint8_t u8g2_font_samim_10_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_10_t_all\");\nextern const uint8_t u8g2_font_samim_12_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_12_t_all\");\nextern const uint8_t u8g2_font_samim_14_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_14_t_all\");\nextern const uint8_t u8g2_font_samim_16_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_16_t_all\");\nextern const uint8_t\n    u8g2_font_samim_fd_10_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_fd_10_t_all\");\nextern const uint8_t\n    u8g2_font_samim_fd_12_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_fd_12_t_all\");\nextern const uint8_t\n    u8g2_font_samim_fd_14_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_fd_14_t_all\");\nextern const uint8_t\n    u8g2_font_samim_fd_16_t_all[] U8G2_FONT_SECTION(\"u8g2_font_samim_fd_16_t_all\");\nextern const uint8_t\n    u8g2_font_ganj_nameh_sans10_t_all[] U8G2_FONT_SECTION(\"u8g2_font_ganj_nameh_sans10_t_all\");\nextern const uint8_t\n    u8g2_font_ganj_nameh_sans12_t_all[] U8G2_FONT_SECTION(\"u8g2_font_ganj_nameh_sans12_t_all\");\nextern const uint8_t\n    u8g2_font_ganj_nameh_sans14_t_all[] U8G2_FONT_SECTION(\"u8g2_font_ganj_nameh_sans14_t_all\");\nextern const uint8_t\n    u8g2_font_ganj_nameh_sans16_t_all[] U8G2_FONT_SECTION(\"u8g2_font_ganj_nameh_sans16_t_all\");\nextern const uint8_t\n    u8g2_font_iranian_sans_8_t_all[] U8G2_FONT_SECTION(\"u8g2_font_iranian_sans_8_t_all\");\nextern const uint8_t\n    u8g2_font_iranian_sans_10_t_all[] U8G2_FONT_SECTION(\"u8g2_font_iranian_sans_10_t_all\");\nextern const uint8_t\n    u8g2_font_iranian_sans_12_t_all[] U8G2_FONT_SECTION(\"u8g2_font_iranian_sans_12_t_all\");\nextern const uint8_t\n    u8g2_font_iranian_sans_14_t_all[] U8G2_FONT_SECTION(\"u8g2_font_iranian_sans_14_t_all\");\nextern const uint8_t\n    u8g2_font_iranian_sans_16_t_all[] U8G2_FONT_SECTION(\"u8g2_font_iranian_sans_16_t_all\");\nextern const uint8_t u8g2_font_mozart_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_mozart_nbp_tf\");\nextern const uint8_t u8g2_font_mozart_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_mozart_nbp_tr\");\nextern const uint8_t u8g2_font_mozart_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_mozart_nbp_tn\");\nextern const uint8_t u8g2_font_mozart_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_mozart_nbp_t_all\");\nextern const uint8_t u8g2_font_mozart_nbp_h_all[] U8G2_FONT_SECTION(\"u8g2_font_mozart_nbp_h_all\");\nextern const uint8_t u8g2_font_glasstown_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_glasstown_nbp_tf\");\nextern const uint8_t u8g2_font_glasstown_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_glasstown_nbp_tr\");\nextern const uint8_t u8g2_font_glasstown_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_glasstown_nbp_tn\");\nextern const uint8_t\n    u8g2_font_glasstown_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_glasstown_nbp_t_all\");\nextern const uint8_t u8g2_font_shylock_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_shylock_nbp_tf\");\nextern const uint8_t u8g2_font_shylock_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_shylock_nbp_tr\");\nextern const uint8_t u8g2_font_shylock_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_shylock_nbp_tn\");\nextern const uint8_t\n    u8g2_font_shylock_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_shylock_nbp_t_all\");\nextern const uint8_t u8g2_font_roentgen_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_roentgen_nbp_tf\");\nextern const uint8_t u8g2_font_roentgen_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_roentgen_nbp_tr\");\nextern const uint8_t u8g2_font_roentgen_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_roentgen_nbp_tn\");\nextern const uint8_t\n    u8g2_font_roentgen_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_roentgen_nbp_t_all\");\nextern const uint8_t\n    u8g2_font_roentgen_nbp_h_all[] U8G2_FONT_SECTION(\"u8g2_font_roentgen_nbp_h_all\");\nextern const uint8_t\n    u8g2_font_calibration_gothic_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_calibration_gothic_nbp_tf\");\nextern const uint8_t\n    u8g2_font_calibration_gothic_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_calibration_gothic_nbp_tr\");\nextern const uint8_t\n    u8g2_font_calibration_gothic_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_calibration_gothic_nbp_tn\");\nextern const uint8_t u8g2_font_calibration_gothic_nbp_t_all[] U8G2_FONT_SECTION(\n    \"u8g2_font_calibration_gothic_nbp_t_all\");\nextern const uint8_t\n    u8g2_font_smart_patrol_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_smart_patrol_nbp_tf\");\nextern const uint8_t\n    u8g2_font_smart_patrol_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_smart_patrol_nbp_tr\");\nextern const uint8_t\n    u8g2_font_smart_patrol_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_smart_patrol_nbp_tn\");\nextern const uint8_t\n    u8g2_font_prospero_bold_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_prospero_bold_nbp_tf\");\nextern const uint8_t\n    u8g2_font_prospero_bold_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_prospero_bold_nbp_tr\");\nextern const uint8_t\n    u8g2_font_prospero_bold_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_prospero_bold_nbp_tn\");\nextern const uint8_t u8g2_font_prospero_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_prospero_nbp_tf\");\nextern const uint8_t u8g2_font_prospero_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_prospero_nbp_tr\");\nextern const uint8_t u8g2_font_prospero_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_prospero_nbp_tn\");\nextern const uint8_t\n    u8g2_font_balthasar_regular_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_regular_nbp_tf\");\nextern const uint8_t\n    u8g2_font_balthasar_regular_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_regular_nbp_tr\");\nextern const uint8_t\n    u8g2_font_balthasar_regular_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_regular_nbp_tn\");\nextern const uint8_t\n    u8g2_font_balthasar_titling_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_titling_nbp_tf\");\nextern const uint8_t\n    u8g2_font_balthasar_titling_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_titling_nbp_tr\");\nextern const uint8_t\n    u8g2_font_balthasar_titling_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_balthasar_titling_nbp_tn\");\nextern const uint8_t\n    u8g2_font_synchronizer_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_synchronizer_nbp_tf\");\nextern const uint8_t\n    u8g2_font_synchronizer_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_synchronizer_nbp_tr\");\nextern const uint8_t\n    u8g2_font_synchronizer_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_synchronizer_nbp_tn\");\nextern const uint8_t\n    u8g2_font_mercutio_basic_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_basic_nbp_tf\");\nextern const uint8_t\n    u8g2_font_mercutio_basic_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_basic_nbp_tr\");\nextern const uint8_t\n    u8g2_font_mercutio_basic_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_basic_nbp_tn\");\nextern const uint8_t\n    u8g2_font_mercutio_basic_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_basic_nbp_t_all\");\nextern const uint8_t\n    u8g2_font_mercutio_sc_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_sc_nbp_tf\");\nextern const uint8_t\n    u8g2_font_mercutio_sc_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_sc_nbp_tr\");\nextern const uint8_t\n    u8g2_font_mercutio_sc_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_sc_nbp_tn\");\nextern const uint8_t\n    u8g2_font_mercutio_sc_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_mercutio_sc_nbp_t_all\");\nextern const uint8_t u8g2_font_miranda_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_miranda_nbp_tf\");\nextern const uint8_t u8g2_font_miranda_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_miranda_nbp_tr\");\nextern const uint8_t u8g2_font_miranda_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_miranda_nbp_tn\");\nextern const uint8_t\n    u8g2_font_nine_by_five_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_nine_by_five_nbp_tf\");\nextern const uint8_t\n    u8g2_font_nine_by_five_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_nine_by_five_nbp_tr\");\nextern const uint8_t\n    u8g2_font_nine_by_five_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_nine_by_five_nbp_tn\");\nextern const uint8_t\n    u8g2_font_nine_by_five_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_nine_by_five_nbp_t_all\");\nextern const uint8_t\n    u8g2_font_rosencrantz_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_rosencrantz_nbp_tf\");\nextern const uint8_t\n    u8g2_font_rosencrantz_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_rosencrantz_nbp_tr\");\nextern const uint8_t\n    u8g2_font_rosencrantz_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_rosencrantz_nbp_tn\");\nextern const uint8_t\n    u8g2_font_rosencrantz_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_rosencrantz_nbp_t_all\");\nextern const uint8_t\n    u8g2_font_guildenstern_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_guildenstern_nbp_tf\");\nextern const uint8_t\n    u8g2_font_guildenstern_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_guildenstern_nbp_tr\");\nextern const uint8_t\n    u8g2_font_guildenstern_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_guildenstern_nbp_tn\");\nextern const uint8_t\n    u8g2_font_guildenstern_nbp_t_all[] U8G2_FONT_SECTION(\"u8g2_font_guildenstern_nbp_t_all\");\nextern const uint8_t u8g2_font_astragal_nbp_tf[] U8G2_FONT_SECTION(\"u8g2_font_astragal_nbp_tf\");\nextern const uint8_t u8g2_font_astragal_nbp_tr[] U8G2_FONT_SECTION(\"u8g2_font_astragal_nbp_tr\");\nextern const uint8_t u8g2_font_astragal_nbp_tn[] U8G2_FONT_SECTION(\"u8g2_font_astragal_nbp_tn\");\nextern const uint8_t\n    u8g2_font_habsburgchancery_tf[] U8G2_FONT_SECTION(\"u8g2_font_habsburgchancery_tf\");\nextern const uint8_t\n    u8g2_font_habsburgchancery_tr[] U8G2_FONT_SECTION(\"u8g2_font_habsburgchancery_tr\");\nextern const uint8_t\n    u8g2_font_habsburgchancery_tn[] U8G2_FONT_SECTION(\"u8g2_font_habsburgchancery_tn\");\nextern const uint8_t\n    u8g2_font_habsburgchancery_t_all[] U8G2_FONT_SECTION(\"u8g2_font_habsburgchancery_t_all\");\nextern const uint8_t u8g2_font_missingplanet_tf[] U8G2_FONT_SECTION(\"u8g2_font_missingplanet_tf\");\nextern const uint8_t u8g2_font_missingplanet_tr[] U8G2_FONT_SECTION(\"u8g2_font_missingplanet_tr\");\nextern const uint8_t u8g2_font_missingplanet_tn[] U8G2_FONT_SECTION(\"u8g2_font_missingplanet_tn\");\nextern const uint8_t\n    u8g2_font_missingplanet_t_all[] U8G2_FONT_SECTION(\"u8g2_font_missingplanet_t_all\");\nextern const uint8_t u8g2_font_ordinarybasis_tf[] U8G2_FONT_SECTION(\"u8g2_font_ordinarybasis_tf\");\nextern const uint8_t u8g2_font_ordinarybasis_tr[] U8G2_FONT_SECTION(\"u8g2_font_ordinarybasis_tr\");\nextern const uint8_t u8g2_font_ordinarybasis_tn[] U8G2_FONT_SECTION(\"u8g2_font_ordinarybasis_tn\");\nextern const uint8_t\n    u8g2_font_ordinarybasis_t_all[] U8G2_FONT_SECTION(\"u8g2_font_ordinarybasis_t_all\");\nextern const uint8_t u8g2_font_pixelmordred_tf[] U8G2_FONT_SECTION(\"u8g2_font_pixelmordred_tf\");\nextern const uint8_t u8g2_font_pixelmordred_tr[] U8G2_FONT_SECTION(\"u8g2_font_pixelmordred_tr\");\nextern const uint8_t u8g2_font_pixelmordred_tn[] U8G2_FONT_SECTION(\"u8g2_font_pixelmordred_tn\");\nextern const uint8_t\n    u8g2_font_pixelmordred_t_all[] U8G2_FONT_SECTION(\"u8g2_font_pixelmordred_t_all\");\nextern const uint8_t u8g2_font_secretaryhand_tf[] U8G2_FONT_SECTION(\"u8g2_font_secretaryhand_tf\");\nextern const uint8_t u8g2_font_secretaryhand_tr[] U8G2_FONT_SECTION(\"u8g2_font_secretaryhand_tr\");\nextern const uint8_t u8g2_font_secretaryhand_tn[] U8G2_FONT_SECTION(\"u8g2_font_secretaryhand_tn\");\nextern const uint8_t\n    u8g2_font_secretaryhand_t_all[] U8G2_FONT_SECTION(\"u8g2_font_secretaryhand_t_all\");\nextern const uint8_t u8g2_font_beanstalk_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_beanstalk_mel_tr\");\nextern const uint8_t u8g2_font_beanstalk_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_beanstalk_mel_tn\");\nextern const uint8_t u8g2_font_cube_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_cube_mel_tr\");\nextern const uint8_t u8g2_font_cube_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_cube_mel_tn\");\nextern const uint8_t\n    u8g2_font_mademoiselle_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_mademoiselle_mel_tr\");\nextern const uint8_t\n    u8g2_font_mademoiselle_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_mademoiselle_mel_tn\");\nextern const uint8_t\n    u8g2_font_pieceofcake_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_pieceofcake_mel_tr\");\nextern const uint8_t\n    u8g2_font_pieceofcake_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_pieceofcake_mel_tn\");\nextern const uint8_t u8g2_font_press_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_press_mel_tr\");\nextern const uint8_t u8g2_font_press_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_press_mel_tn\");\nextern const uint8_t u8g2_font_repress_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_repress_mel_tr\");\nextern const uint8_t u8g2_font_repress_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_repress_mel_tn\");\nextern const uint8_t u8g2_font_sticker_mel_tr[] U8G2_FONT_SECTION(\"u8g2_font_sticker_mel_tr\");\nextern const uint8_t u8g2_font_sticker_mel_tn[] U8G2_FONT_SECTION(\"u8g2_font_sticker_mel_tn\");\nextern const uint8_t u8g2_font_celibatemonk_tr[] U8G2_FONT_SECTION(\"u8g2_font_celibatemonk_tr\");\nextern const uint8_t\n    u8g2_font_disrespectfulteenager_tu[] U8G2_FONT_SECTION(\"u8g2_font_disrespectfulteenager_tu\");\nextern const uint8_t u8g2_font_michaelmouse_tu[] U8G2_FONT_SECTION(\"u8g2_font_michaelmouse_tu\");\nextern const uint8_t u8g2_font_sandyforest_tr[] U8G2_FONT_SECTION(\"u8g2_font_sandyforest_tr\");\nextern const uint8_t u8g2_font_sandyforest_tn[] U8G2_FONT_SECTION(\"u8g2_font_sandyforest_tn\");\nextern const uint8_t u8g2_font_sandyforest_tu[] U8G2_FONT_SECTION(\"u8g2_font_sandyforest_tu\");\nextern const uint8_t\n    u8g2_font_cupcakemetoyourleader_tr[] U8G2_FONT_SECTION(\"u8g2_font_cupcakemetoyourleader_tr\");\nextern const uint8_t\n    u8g2_font_cupcakemetoyourleader_tn[] U8G2_FONT_SECTION(\"u8g2_font_cupcakemetoyourleader_tn\");\nextern const uint8_t\n    u8g2_font_cupcakemetoyourleader_tu[] U8G2_FONT_SECTION(\"u8g2_font_cupcakemetoyourleader_tu\");\nextern const uint8_t u8g2_font_oldwizard_tf[] U8G2_FONT_SECTION(\"u8g2_font_oldwizard_tf\");\nextern const uint8_t u8g2_font_oldwizard_tr[] U8G2_FONT_SECTION(\"u8g2_font_oldwizard_tr\");\nextern const uint8_t u8g2_font_oldwizard_tn[] U8G2_FONT_SECTION(\"u8g2_font_oldwizard_tn\");\nextern const uint8_t u8g2_font_oldwizard_tu[] U8G2_FONT_SECTION(\"u8g2_font_oldwizard_tu\");\nextern const uint8_t u8g2_font_squirrel_tr[] U8G2_FONT_SECTION(\"u8g2_font_squirrel_tr\");\nextern const uint8_t u8g2_font_squirrel_tn[] U8G2_FONT_SECTION(\"u8g2_font_squirrel_tn\");\nextern const uint8_t u8g2_font_squirrel_tu[] U8G2_FONT_SECTION(\"u8g2_font_squirrel_tu\");\nextern const uint8_t u8g2_font_diodesemimono_tr[] U8G2_FONT_SECTION(\"u8g2_font_diodesemimono_tr\");\nextern const uint8_t u8g2_font_questgiver_tr[] U8G2_FONT_SECTION(\"u8g2_font_questgiver_tr\");\nextern const uint8_t u8g2_font_seraphimb1_tr[] U8G2_FONT_SECTION(\"u8g2_font_seraphimb1_tr\");\nextern const uint8_t u8g2_font_jinxedwizards_tr[] U8G2_FONT_SECTION(\"u8g2_font_jinxedwizards_tr\");\nextern const uint8_t u8g2_font_lastpriestess_tr[] U8G2_FONT_SECTION(\"u8g2_font_lastpriestess_tr\");\nextern const uint8_t u8g2_font_lastpriestess_tu[] U8G2_FONT_SECTION(\"u8g2_font_lastpriestess_tu\");\nextern const uint8_t u8g2_font_bitcasual_tf[] U8G2_FONT_SECTION(\"u8g2_font_bitcasual_tf\");\nextern const uint8_t u8g2_font_bitcasual_tr[] U8G2_FONT_SECTION(\"u8g2_font_bitcasual_tr\");\nextern const uint8_t u8g2_font_bitcasual_tn[] U8G2_FONT_SECTION(\"u8g2_font_bitcasual_tn\");\nextern const uint8_t u8g2_font_bitcasual_tu[] U8G2_FONT_SECTION(\"u8g2_font_bitcasual_tu\");\nextern const uint8_t u8g2_font_bitcasual_t_all[] U8G2_FONT_SECTION(\"u8g2_font_bitcasual_t_all\");\nextern const uint8_t u8g2_font_koleeko_tf[] U8G2_FONT_SECTION(\"u8g2_font_koleeko_tf\");\nextern const uint8_t u8g2_font_koleeko_tr[] U8G2_FONT_SECTION(\"u8g2_font_koleeko_tr\");\nextern const uint8_t u8g2_font_koleeko_tn[] U8G2_FONT_SECTION(\"u8g2_font_koleeko_tn\");\nextern const uint8_t u8g2_font_koleeko_tu[] U8G2_FONT_SECTION(\"u8g2_font_koleeko_tu\");\nextern const uint8_t u8g2_font_tenfatguys_tf[] U8G2_FONT_SECTION(\"u8g2_font_tenfatguys_tf\");\nextern const uint8_t u8g2_font_tenfatguys_tr[] U8G2_FONT_SECTION(\"u8g2_font_tenfatguys_tr\");\nextern const uint8_t u8g2_font_tenfatguys_tn[] U8G2_FONT_SECTION(\"u8g2_font_tenfatguys_tn\");\nextern const uint8_t u8g2_font_tenfatguys_tu[] U8G2_FONT_SECTION(\"u8g2_font_tenfatguys_tu\");\nextern const uint8_t u8g2_font_tenfatguys_t_all[] U8G2_FONT_SECTION(\"u8g2_font_tenfatguys_t_all\");\nextern const uint8_t u8g2_font_tenstamps_mf[] U8G2_FONT_SECTION(\"u8g2_font_tenstamps_mf\");\nextern const uint8_t u8g2_font_tenstamps_mr[] U8G2_FONT_SECTION(\"u8g2_font_tenstamps_mr\");\nextern const uint8_t u8g2_font_tenstamps_mn[] U8G2_FONT_SECTION(\"u8g2_font_tenstamps_mn\");\nextern const uint8_t u8g2_font_tenstamps_mu[] U8G2_FONT_SECTION(\"u8g2_font_tenstamps_mu\");\nextern const uint8_t u8g2_font_tenthinguys_tf[] U8G2_FONT_SECTION(\"u8g2_font_tenthinguys_tf\");\nextern const uint8_t u8g2_font_tenthinguys_tr[] U8G2_FONT_SECTION(\"u8g2_font_tenthinguys_tr\");\nextern const uint8_t u8g2_font_tenthinguys_tn[] U8G2_FONT_SECTION(\"u8g2_font_tenthinguys_tn\");\nextern const uint8_t u8g2_font_tenthinguys_tu[] U8G2_FONT_SECTION(\"u8g2_font_tenthinguys_tu\");\nextern const uint8_t\n    u8g2_font_tenthinguys_t_all[] U8G2_FONT_SECTION(\"u8g2_font_tenthinguys_t_all\");\nextern const uint8_t\n    u8g2_font_tenthinnerguys_tf[] U8G2_FONT_SECTION(\"u8g2_font_tenthinnerguys_tf\");\nextern const uint8_t\n    u8g2_font_tenthinnerguys_tr[] U8G2_FONT_SECTION(\"u8g2_font_tenthinnerguys_tr\");\nextern const uint8_t\n    u8g2_font_tenthinnerguys_tn[] U8G2_FONT_SECTION(\"u8g2_font_tenthinnerguys_tn\");\nextern const uint8_t\n    u8g2_font_tenthinnerguys_tu[] U8G2_FONT_SECTION(\"u8g2_font_tenthinnerguys_tu\");\nextern const uint8_t\n    u8g2_font_tenthinnerguys_t_all[] U8G2_FONT_SECTION(\"u8g2_font_tenthinnerguys_t_all\");\nextern const uint8_t\n    u8g2_font_twelvedings_t_all[] U8G2_FONT_SECTION(\"u8g2_font_twelvedings_t_all\");\nextern const uint8_t u8g2_font_fewture_tf[] U8G2_FONT_SECTION(\"u8g2_font_fewture_tf\");\nextern const uint8_t u8g2_font_fewture_tr[] U8G2_FONT_SECTION(\"u8g2_font_fewture_tr\");\nextern const uint8_t u8g2_font_fewture_tn[] U8G2_FONT_SECTION(\"u8g2_font_fewture_tn\");\nextern const uint8_t u8g2_font_halftone_tf[] U8G2_FONT_SECTION(\"u8g2_font_halftone_tf\");\nextern const uint8_t u8g2_font_halftone_tr[] U8G2_FONT_SECTION(\"u8g2_font_halftone_tr\");\nextern const uint8_t u8g2_font_halftone_tn[] U8G2_FONT_SECTION(\"u8g2_font_halftone_tn\");\nextern const uint8_t u8g2_font_nerhoe_tf[] U8G2_FONT_SECTION(\"u8g2_font_nerhoe_tf\");\nextern const uint8_t u8g2_font_nerhoe_tr[] U8G2_FONT_SECTION(\"u8g2_font_nerhoe_tr\");\nextern const uint8_t u8g2_font_nerhoe_tn[] U8G2_FONT_SECTION(\"u8g2_font_nerhoe_tn\");\nextern const uint8_t u8g2_font_oskool_tf[] U8G2_FONT_SECTION(\"u8g2_font_oskool_tf\");\nextern const uint8_t u8g2_font_oskool_tr[] U8G2_FONT_SECTION(\"u8g2_font_oskool_tr\");\nextern const uint8_t u8g2_font_oskool_tn[] U8G2_FONT_SECTION(\"u8g2_font_oskool_tn\");\nextern const uint8_t u8g2_font_tinytim_tf[] U8G2_FONT_SECTION(\"u8g2_font_tinytim_tf\");\nextern const uint8_t u8g2_font_tinytim_tr[] U8G2_FONT_SECTION(\"u8g2_font_tinytim_tr\");\nextern const uint8_t u8g2_font_tinytim_tn[] U8G2_FONT_SECTION(\"u8g2_font_tinytim_tn\");\nextern const uint8_t u8g2_font_tooseornament_tf[] U8G2_FONT_SECTION(\"u8g2_font_tooseornament_tf\");\nextern const uint8_t u8g2_font_tooseornament_tr[] U8G2_FONT_SECTION(\"u8g2_font_tooseornament_tr\");\nextern const uint8_t u8g2_font_tooseornament_tn[] U8G2_FONT_SECTION(\"u8g2_font_tooseornament_tn\");\nextern const uint8_t u8g2_font_bauhaus2015_tr[] U8G2_FONT_SECTION(\"u8g2_font_bauhaus2015_tr\");\nextern const uint8_t u8g2_font_bauhaus2015_tn[] U8G2_FONT_SECTION(\"u8g2_font_bauhaus2015_tn\");\nextern const uint8_t\n    u8g2_font_finderskeepers_tf[] U8G2_FONT_SECTION(\"u8g2_font_finderskeepers_tf\");\nextern const uint8_t\n    u8g2_font_finderskeepers_tr[] U8G2_FONT_SECTION(\"u8g2_font_finderskeepers_tr\");\nextern const uint8_t\n    u8g2_font_finderskeepers_tn[] U8G2_FONT_SECTION(\"u8g2_font_finderskeepers_tn\");\nextern const uint8_t\n    u8g2_font_sirclivethebold_tr[] U8G2_FONT_SECTION(\"u8g2_font_sirclivethebold_tr\");\nextern const uint8_t\n    u8g2_font_sirclivethebold_tn[] U8G2_FONT_SECTION(\"u8g2_font_sirclivethebold_tn\");\nextern const uint8_t u8g2_font_sirclive_tr[] U8G2_FONT_SECTION(\"u8g2_font_sirclive_tr\");\nextern const uint8_t u8g2_font_sirclive_tn[] U8G2_FONT_SECTION(\"u8g2_font_sirclive_tn\");\nextern const uint8_t u8g2_font_adventurer_tf[] U8G2_FONT_SECTION(\"u8g2_font_adventurer_tf\");\nextern const uint8_t u8g2_font_adventurer_tr[] U8G2_FONT_SECTION(\"u8g2_font_adventurer_tr\");\nextern const uint8_t u8g2_font_adventurer_t_all[] U8G2_FONT_SECTION(\"u8g2_font_adventurer_t_all\");\nextern const uint8_t\n    u8g2_font_bracketedbabies_tr[] U8G2_FONT_SECTION(\"u8g2_font_bracketedbabies_tr\");\nextern const uint8_t u8g2_font_frikativ_tf[] U8G2_FONT_SECTION(\"u8g2_font_frikativ_tf\");\nextern const uint8_t u8g2_font_frikativ_tr[] U8G2_FONT_SECTION(\"u8g2_font_frikativ_tr\");\nextern const uint8_t u8g2_font_frikativ_t_all[] U8G2_FONT_SECTION(\"u8g2_font_frikativ_t_all\");\nextern const uint8_t u8g2_font_fancypixels_tf[] U8G2_FONT_SECTION(\"u8g2_font_fancypixels_tf\");\nextern const uint8_t u8g2_font_fancypixels_tr[] U8G2_FONT_SECTION(\"u8g2_font_fancypixels_tr\");\nextern const uint8_t u8g2_font_heavybottom_tr[] U8G2_FONT_SECTION(\"u8g2_font_heavybottom_tr\");\nextern const uint8_t\n    u8g2_font_iconquadpix_m_all[] U8G2_FONT_SECTION(\"u8g2_font_iconquadpix_m_all\");\nextern const uint8_t\n    u8g2_font_lastapprenticebold_tr[] U8G2_FONT_SECTION(\"u8g2_font_lastapprenticebold_tr\");\nextern const uint8_t\n    u8g2_font_lastapprenticethin_tr[] U8G2_FONT_SECTION(\"u8g2_font_lastapprenticethin_tr\");\nextern const uint8_t u8g2_font_tallpix_tr[] U8G2_FONT_SECTION(\"u8g2_font_tallpix_tr\");\nextern const uint8_t u8g2_font_BBSesque_tf[] U8G2_FONT_SECTION(\"u8g2_font_BBSesque_tf\");\nextern const uint8_t u8g2_font_BBSesque_tr[] U8G2_FONT_SECTION(\"u8g2_font_BBSesque_tr\");\nextern const uint8_t u8g2_font_BBSesque_te[] U8G2_FONT_SECTION(\"u8g2_font_BBSesque_te\");\nextern const uint8_t\n    u8g2_font_Born2bSportySlab_tf[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportySlab_tf\");\nextern const uint8_t\n    u8g2_font_Born2bSportySlab_tr[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportySlab_tr\");\nextern const uint8_t\n    u8g2_font_Born2bSportySlab_te[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportySlab_te\");\nextern const uint8_t\n    u8g2_font_Born2bSportySlab_t_all[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportySlab_t_all\");\nextern const uint8_t\n    u8g2_font_Born2bSportyV2_tf[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportyV2_tf\");\nextern const uint8_t\n    u8g2_font_Born2bSportyV2_tr[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportyV2_tr\");\nextern const uint8_t\n    u8g2_font_Born2bSportyV2_te[] U8G2_FONT_SECTION(\"u8g2_font_Born2bSportyV2_te\");\nextern const uint8_t u8g2_font_CursivePixel_tr[] U8G2_FONT_SECTION(\"u8g2_font_CursivePixel_tr\");\nextern const uint8_t u8g2_font_Engrish_tf[] U8G2_FONT_SECTION(\"u8g2_font_Engrish_tf\");\nextern const uint8_t u8g2_font_Engrish_tr[] U8G2_FONT_SECTION(\"u8g2_font_Engrish_tr\");\nextern const uint8_t u8g2_font_ImpactBits_tr[] U8G2_FONT_SECTION(\"u8g2_font_ImpactBits_tr\");\nextern const uint8_t u8g2_font_IPAandRUSLCD_tf[] U8G2_FONT_SECTION(\"u8g2_font_IPAandRUSLCD_tf\");\nextern const uint8_t u8g2_font_IPAandRUSLCD_tr[] U8G2_FONT_SECTION(\"u8g2_font_IPAandRUSLCD_tr\");\nextern const uint8_t u8g2_font_IPAandRUSLCD_te[] U8G2_FONT_SECTION(\"u8g2_font_IPAandRUSLCD_te\");\nextern const uint8_t u8g2_font_HelvetiPixel_tr[] U8G2_FONT_SECTION(\"u8g2_font_HelvetiPixel_tr\");\nextern const uint8_t u8g2_font_TimesNewPixel_tr[] U8G2_FONT_SECTION(\"u8g2_font_TimesNewPixel_tr\");\nextern const uint8_t u8g2_font_BitTypeWriter_tr[] U8G2_FONT_SECTION(\"u8g2_font_BitTypeWriter_tr\");\nextern const uint8_t u8g2_font_BitTypeWriter_te[] U8G2_FONT_SECTION(\"u8g2_font_BitTypeWriter_te\");\nextern const uint8_t u8g2_font_Georgia7px_tf[] U8G2_FONT_SECTION(\"u8g2_font_Georgia7px_tf\");\nextern const uint8_t u8g2_font_Georgia7px_tr[] U8G2_FONT_SECTION(\"u8g2_font_Georgia7px_tr\");\nextern const uint8_t u8g2_font_Georgia7px_te[] U8G2_FONT_SECTION(\"u8g2_font_Georgia7px_te\");\nextern const uint8_t u8g2_font_Wizzard_tr[] U8G2_FONT_SECTION(\"u8g2_font_Wizzard_tr\");\nextern const uint8_t\n    u8g2_font_HelvetiPixelOutline_tr[] U8G2_FONT_SECTION(\"u8g2_font_HelvetiPixelOutline_tr\");\nextern const uint8_t\n    u8g2_font_HelvetiPixelOutline_te[] U8G2_FONT_SECTION(\"u8g2_font_HelvetiPixelOutline_te\");\nextern const uint8_t u8g2_font_Untitled16PixelSansSerifBitmap_tr[] U8G2_FONT_SECTION(\n    \"u8g2_font_Untitled16PixelSansSerifBitmap_tr\");\nextern const uint8_t u8g2_font_nokiafc22_tf[] U8G2_FONT_SECTION(\"u8g2_font_nokiafc22_tf\");\nextern const uint8_t u8g2_font_nokiafc22_tr[] U8G2_FONT_SECTION(\"u8g2_font_nokiafc22_tr\");\nextern const uint8_t u8g2_font_nokiafc22_tn[] U8G2_FONT_SECTION(\"u8g2_font_nokiafc22_tn\");\nextern const uint8_t u8g2_font_nokiafc22_tu[] U8G2_FONT_SECTION(\"u8g2_font_nokiafc22_tu\");\nextern const uint8_t u8g2_font_VCR_OSD_tf[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_tf\");\nextern const uint8_t u8g2_font_VCR_OSD_tr[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_tr\");\nextern const uint8_t u8g2_font_VCR_OSD_tn[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_tn\");\nextern const uint8_t u8g2_font_VCR_OSD_tu[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_tu\");\nextern const uint8_t u8g2_font_VCR_OSD_mf[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_mf\");\nextern const uint8_t u8g2_font_VCR_OSD_mr[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_mr\");\nextern const uint8_t u8g2_font_VCR_OSD_mn[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_mn\");\nextern const uint8_t u8g2_font_VCR_OSD_mu[] U8G2_FONT_SECTION(\"u8g2_font_VCR_OSD_mu\");\nextern const uint8_t u8g2_font_Pixellari_tf[] U8G2_FONT_SECTION(\"u8g2_font_Pixellari_tf\");\nextern const uint8_t u8g2_font_Pixellari_tr[] U8G2_FONT_SECTION(\"u8g2_font_Pixellari_tr\");\nextern const uint8_t u8g2_font_Pixellari_tn[] U8G2_FONT_SECTION(\"u8g2_font_Pixellari_tn\");\nextern const uint8_t u8g2_font_Pixellari_tu[] U8G2_FONT_SECTION(\"u8g2_font_Pixellari_tu\");\nextern const uint8_t u8g2_font_Pixellari_te[] U8G2_FONT_SECTION(\"u8g2_font_Pixellari_te\");\nextern const uint8_t u8g2_font_pixelpoiiz_tr[] U8G2_FONT_SECTION(\"u8g2_font_pixelpoiiz_tr\");\nextern const uint8_t\n    u8g2_font_DigitalDiscoThin_tf[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDiscoThin_tf\");\nextern const uint8_t\n    u8g2_font_DigitalDiscoThin_tr[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDiscoThin_tr\");\nextern const uint8_t\n    u8g2_font_DigitalDiscoThin_tn[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDiscoThin_tn\");\nextern const uint8_t\n    u8g2_font_DigitalDiscoThin_tu[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDiscoThin_tu\");\nextern const uint8_t\n    u8g2_font_DigitalDiscoThin_te[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDiscoThin_te\");\nextern const uint8_t u8g2_font_DigitalDisco_tf[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDisco_tf\");\nextern const uint8_t u8g2_font_DigitalDisco_tr[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDisco_tr\");\nextern const uint8_t u8g2_font_DigitalDisco_tn[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDisco_tn\");\nextern const uint8_t u8g2_font_DigitalDisco_tu[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDisco_tu\");\nextern const uint8_t u8g2_font_DigitalDisco_te[] U8G2_FONT_SECTION(\"u8g2_font_DigitalDisco_te\");\nextern const uint8_t u8g2_font_pearfont_tr[] U8G2_FONT_SECTION(\"u8g2_font_pearfont_tr\");\nextern const uint8_t u8g2_font_etl14thai_t[] U8G2_FONT_SECTION(\"u8g2_font_etl14thai_t\");\nextern const uint8_t u8g2_font_etl16thai_t[] U8G2_FONT_SECTION(\"u8g2_font_etl16thai_t\");\nextern const uint8_t u8g2_font_etl24thai_t[] U8G2_FONT_SECTION(\"u8g2_font_etl24thai_t\");\nextern const uint8_t u8g2_font_crox1cb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_tf\");\nextern const uint8_t u8g2_font_crox1cb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_tr\");\nextern const uint8_t u8g2_font_crox1cb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_tn\");\nextern const uint8_t u8g2_font_crox1cb_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_mf\");\nextern const uint8_t u8g2_font_crox1cb_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_mr\");\nextern const uint8_t u8g2_font_crox1cb_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox1cb_mn\");\nextern const uint8_t u8g2_font_crox1c_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_tf\");\nextern const uint8_t u8g2_font_crox1c_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_tr\");\nextern const uint8_t u8g2_font_crox1c_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_tn\");\nextern const uint8_t u8g2_font_crox1c_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_mf\");\nextern const uint8_t u8g2_font_crox1c_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_mr\");\nextern const uint8_t u8g2_font_crox1c_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox1c_mn\");\nextern const uint8_t u8g2_font_crox1hb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1hb_tf\");\nextern const uint8_t u8g2_font_crox1hb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1hb_tr\");\nextern const uint8_t u8g2_font_crox1hb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1hb_tn\");\nextern const uint8_t u8g2_font_crox1h_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1h_tf\");\nextern const uint8_t u8g2_font_crox1h_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1h_tr\");\nextern const uint8_t u8g2_font_crox1h_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1h_tn\");\nextern const uint8_t u8g2_font_crox1tb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1tb_tf\");\nextern const uint8_t u8g2_font_crox1tb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1tb_tr\");\nextern const uint8_t u8g2_font_crox1tb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1tb_tn\");\nextern const uint8_t u8g2_font_crox1t_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox1t_tf\");\nextern const uint8_t u8g2_font_crox1t_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox1t_tr\");\nextern const uint8_t u8g2_font_crox1t_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox1t_tn\");\nextern const uint8_t u8g2_font_crox2cb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_tf\");\nextern const uint8_t u8g2_font_crox2cb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_tr\");\nextern const uint8_t u8g2_font_crox2cb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_tn\");\nextern const uint8_t u8g2_font_crox2cb_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_mf\");\nextern const uint8_t u8g2_font_crox2cb_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_mr\");\nextern const uint8_t u8g2_font_crox2cb_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox2cb_mn\");\nextern const uint8_t u8g2_font_crox2c_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_tf\");\nextern const uint8_t u8g2_font_crox2c_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_tr\");\nextern const uint8_t u8g2_font_crox2c_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_tn\");\nextern const uint8_t u8g2_font_crox2c_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_mf\");\nextern const uint8_t u8g2_font_crox2c_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_mr\");\nextern const uint8_t u8g2_font_crox2c_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox2c_mn\");\nextern const uint8_t u8g2_font_crox2hb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2hb_tf\");\nextern const uint8_t u8g2_font_crox2hb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2hb_tr\");\nextern const uint8_t u8g2_font_crox2hb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2hb_tn\");\nextern const uint8_t u8g2_font_crox2h_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2h_tf\");\nextern const uint8_t u8g2_font_crox2h_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2h_tr\");\nextern const uint8_t u8g2_font_crox2h_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2h_tn\");\nextern const uint8_t u8g2_font_crox2tb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2tb_tf\");\nextern const uint8_t u8g2_font_crox2tb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2tb_tr\");\nextern const uint8_t u8g2_font_crox2tb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2tb_tn\");\nextern const uint8_t u8g2_font_crox2t_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox2t_tf\");\nextern const uint8_t u8g2_font_crox2t_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox2t_tr\");\nextern const uint8_t u8g2_font_crox2t_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox2t_tn\");\nextern const uint8_t u8g2_font_crox3cb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_tf\");\nextern const uint8_t u8g2_font_crox3cb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_tr\");\nextern const uint8_t u8g2_font_crox3cb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_tn\");\nextern const uint8_t u8g2_font_crox3cb_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_mf\");\nextern const uint8_t u8g2_font_crox3cb_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_mr\");\nextern const uint8_t u8g2_font_crox3cb_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox3cb_mn\");\nextern const uint8_t u8g2_font_crox3c_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_tf\");\nextern const uint8_t u8g2_font_crox3c_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_tr\");\nextern const uint8_t u8g2_font_crox3c_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_tn\");\nextern const uint8_t u8g2_font_crox3c_mf[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_mf\");\nextern const uint8_t u8g2_font_crox3c_mr[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_mr\");\nextern const uint8_t u8g2_font_crox3c_mn[] U8G2_FONT_SECTION(\"u8g2_font_crox3c_mn\");\nextern const uint8_t u8g2_font_crox3hb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3hb_tf\");\nextern const uint8_t u8g2_font_crox3hb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3hb_tr\");\nextern const uint8_t u8g2_font_crox3hb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3hb_tn\");\nextern const uint8_t u8g2_font_crox3h_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3h_tf\");\nextern const uint8_t u8g2_font_crox3h_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3h_tr\");\nextern const uint8_t u8g2_font_crox3h_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3h_tn\");\nextern const uint8_t u8g2_font_crox3tb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3tb_tf\");\nextern const uint8_t u8g2_font_crox3tb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3tb_tr\");\nextern const uint8_t u8g2_font_crox3tb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3tb_tn\");\nextern const uint8_t u8g2_font_crox3t_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox3t_tf\");\nextern const uint8_t u8g2_font_crox3t_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox3t_tr\");\nextern const uint8_t u8g2_font_crox3t_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox3t_tn\");\nextern const uint8_t u8g2_font_crox4hb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox4hb_tf\");\nextern const uint8_t u8g2_font_crox4hb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox4hb_tr\");\nextern const uint8_t u8g2_font_crox4hb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox4hb_tn\");\nextern const uint8_t u8g2_font_crox4h_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox4h_tf\");\nextern const uint8_t u8g2_font_crox4h_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox4h_tr\");\nextern const uint8_t u8g2_font_crox4h_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox4h_tn\");\nextern const uint8_t u8g2_font_crox4tb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox4tb_tf\");\nextern const uint8_t u8g2_font_crox4tb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox4tb_tr\");\nextern const uint8_t u8g2_font_crox4tb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox4tb_tn\");\nextern const uint8_t u8g2_font_crox4t_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox4t_tf\");\nextern const uint8_t u8g2_font_crox4t_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox4t_tr\");\nextern const uint8_t u8g2_font_crox4t_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox4t_tn\");\nextern const uint8_t u8g2_font_crox5hb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox5hb_tf\");\nextern const uint8_t u8g2_font_crox5hb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox5hb_tr\");\nextern const uint8_t u8g2_font_crox5hb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox5hb_tn\");\nextern const uint8_t u8g2_font_crox5h_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox5h_tf\");\nextern const uint8_t u8g2_font_crox5h_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox5h_tr\");\nextern const uint8_t u8g2_font_crox5h_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox5h_tn\");\nextern const uint8_t u8g2_font_crox5tb_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox5tb_tf\");\nextern const uint8_t u8g2_font_crox5tb_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox5tb_tr\");\nextern const uint8_t u8g2_font_crox5tb_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox5tb_tn\");\nextern const uint8_t u8g2_font_crox5t_tf[] U8G2_FONT_SECTION(\"u8g2_font_crox5t_tf\");\nextern const uint8_t u8g2_font_crox5t_tr[] U8G2_FONT_SECTION(\"u8g2_font_crox5t_tr\");\nextern const uint8_t u8g2_font_crox5t_tn[] U8G2_FONT_SECTION(\"u8g2_font_crox5t_tn\");\nextern const uint8_t u8g2_font_cu12_tf[] U8G2_FONT_SECTION(\"u8g2_font_cu12_tf\");\nextern const uint8_t u8g2_font_cu12_tr[] U8G2_FONT_SECTION(\"u8g2_font_cu12_tr\");\nextern const uint8_t u8g2_font_cu12_tn[] U8G2_FONT_SECTION(\"u8g2_font_cu12_tn\");\nextern const uint8_t u8g2_font_cu12_te[] U8G2_FONT_SECTION(\"u8g2_font_cu12_te\");\nextern const uint8_t u8g2_font_cu12_hf[] U8G2_FONT_SECTION(\"u8g2_font_cu12_hf\");\nextern const uint8_t u8g2_font_cu12_hr[] U8G2_FONT_SECTION(\"u8g2_font_cu12_hr\");\nextern const uint8_t u8g2_font_cu12_hn[] U8G2_FONT_SECTION(\"u8g2_font_cu12_hn\");\nextern const uint8_t u8g2_font_cu12_he[] U8G2_FONT_SECTION(\"u8g2_font_cu12_he\");\nextern const uint8_t u8g2_font_cu12_mf[] U8G2_FONT_SECTION(\"u8g2_font_cu12_mf\");\nextern const uint8_t u8g2_font_cu12_mr[] U8G2_FONT_SECTION(\"u8g2_font_cu12_mr\");\nextern const uint8_t u8g2_font_cu12_mn[] U8G2_FONT_SECTION(\"u8g2_font_cu12_mn\");\nextern const uint8_t u8g2_font_cu12_me[] U8G2_FONT_SECTION(\"u8g2_font_cu12_me\");\nextern const uint8_t u8g2_font_cu12_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_symbols\");\nextern const uint8_t u8g2_font_cu12_h_symbols[] U8G2_FONT_SECTION(\"u8g2_font_cu12_h_symbols\");\nextern const uint8_t u8g2_font_cu12_t_greek[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_greek\");\nextern const uint8_t u8g2_font_cu12_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_cyrillic\");\nextern const uint8_t u8g2_font_cu12_t_tibetan[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_tibetan\");\nextern const uint8_t u8g2_font_cu12_t_hebrew[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_hebrew\");\nextern const uint8_t u8g2_font_cu12_t_arabic[] U8G2_FONT_SECTION(\"u8g2_font_cu12_t_arabic\");\nextern const uint8_t u8g2_font_unifont_tf[] U8G2_FONT_SECTION(\"u8g2_font_unifont_tf\");\nextern const uint8_t u8g2_font_unifont_tr[] U8G2_FONT_SECTION(\"u8g2_font_unifont_tr\");\nextern const uint8_t u8g2_font_unifont_te[] U8G2_FONT_SECTION(\"u8g2_font_unifont_te\");\nextern const uint8_t u8g2_font_unifont_t_latin[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_latin\");\nextern const uint8_t\n    u8g2_font_unifont_t_extended[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_extended\");\nextern const uint8_t u8g2_font_unifont_t_72_73[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_72_73\");\nextern const uint8_t\n    u8g2_font_unifont_t_0_72_73[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_72_73\");\nextern const uint8_t u8g2_font_unifont_t_75[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_75\");\nextern const uint8_t u8g2_font_unifont_t_0_75[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_75\");\nextern const uint8_t u8g2_font_unifont_t_76[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_76\");\nextern const uint8_t u8g2_font_unifont_t_0_76[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_76\");\nextern const uint8_t u8g2_font_unifont_t_77[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_77\");\nextern const uint8_t u8g2_font_unifont_t_0_77[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_77\");\nextern const uint8_t u8g2_font_unifont_t_78_79[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_78_79\");\nextern const uint8_t\n    u8g2_font_unifont_t_0_78_79[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_78_79\");\nextern const uint8_t u8g2_font_unifont_t_86[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_86\");\nextern const uint8_t u8g2_font_unifont_t_0_86[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_0_86\");\nextern const uint8_t u8g2_font_unifont_t_greek[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_greek\");\nextern const uint8_t\n    u8g2_font_unifont_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_cyrillic\");\nextern const uint8_t u8g2_font_unifont_t_hebrew[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_hebrew\");\nextern const uint8_t\n    u8g2_font_unifont_t_bengali[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_bengali\");\nextern const uint8_t\n    u8g2_font_unifont_t_tibetan[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_tibetan\");\nextern const uint8_t u8g2_font_unifont_t_urdu[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_urdu\");\nextern const uint8_t u8g2_font_unifont_t_polish[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_polish\");\nextern const uint8_t\n    u8g2_font_unifont_t_devanagari[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_devanagari\");\nextern const uint8_t u8g2_font_unifont_t_arabic[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_arabic\");\nextern const uint8_t\n    u8g2_font_unifont_t_symbols[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_symbols\");\nextern const uint8_t\n    u8g2_font_unifont_h_symbols[] U8G2_FONT_SECTION(\"u8g2_font_unifont_h_symbols\");\nextern const uint8_t\n    u8g2_font_unifont_t_emoticons[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_emoticons\");\nextern const uint8_t\n    u8g2_font_unifont_t_animals[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_animals\");\nextern const uint8_t u8g2_font_unifont_t_domino[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_domino\");\nextern const uint8_t u8g2_font_unifont_t_cards[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_cards\");\nextern const uint8_t\n    u8g2_font_unifont_t_weather[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_weather\");\nextern const uint8_t\n    u8g2_font_unifont_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_chinese1\");\nextern const uint8_t\n    u8g2_font_unifont_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_chinese2\");\nextern const uint8_t\n    u8g2_font_unifont_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_chinese3\");\nextern const uint8_t\n    u8g2_font_unifont_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_japanese1\");\nextern const uint8_t\n    u8g2_font_unifont_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_japanese2\");\nextern const uint8_t\n    u8g2_font_unifont_t_japanese3[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_japanese3\");\nextern const uint8_t\n    u8g2_font_unifont_t_korean1[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_korean1\");\nextern const uint8_t\n    u8g2_font_unifont_t_korean2[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_korean2\");\nextern const uint8_t\n    u8g2_font_unifont_t_vietnamese1[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_vietnamese1\");\nextern const uint8_t\n    u8g2_font_unifont_t_vietnamese2[] U8G2_FONT_SECTION(\"u8g2_font_unifont_t_vietnamese2\");\nextern const uint8_t u8g2_font_gb16st_t_1[] U8G2_FONT_SECTION(\"u8g2_font_gb16st_t_1\");\nextern const uint8_t u8g2_font_gb16st_t_2[] U8G2_FONT_SECTION(\"u8g2_font_gb16st_t_2\");\nextern const uint8_t u8g2_font_gb16st_t_3[] U8G2_FONT_SECTION(\"u8g2_font_gb16st_t_3\");\nextern const uint8_t u8g2_font_gb24st_t_1[] U8G2_FONT_SECTION(\"u8g2_font_gb24st_t_1\");\nextern const uint8_t u8g2_font_gb24st_t_2[] U8G2_FONT_SECTION(\"u8g2_font_gb24st_t_2\");\nextern const uint8_t u8g2_font_gb24st_t_3[] U8G2_FONT_SECTION(\"u8g2_font_gb24st_t_3\");\nextern const uint8_t u8g2_font_wqy12_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_chinese1\");\nextern const uint8_t u8g2_font_wqy12_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_chinese2\");\nextern const uint8_t u8g2_font_wqy12_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_chinese3\");\nextern const uint8_t u8g2_font_wqy12_t_gb2312[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_gb2312\");\nextern const uint8_t u8g2_font_wqy12_t_gb2312a[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_gb2312a\");\nextern const uint8_t u8g2_font_wqy12_t_gb2312b[] U8G2_FONT_SECTION(\"u8g2_font_wqy12_t_gb2312b\");\nextern const uint8_t u8g2_font_wqy13_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_chinese1\");\nextern const uint8_t u8g2_font_wqy13_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_chinese2\");\nextern const uint8_t u8g2_font_wqy13_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_chinese3\");\nextern const uint8_t u8g2_font_wqy13_t_gb2312[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_gb2312\");\nextern const uint8_t u8g2_font_wqy13_t_gb2312a[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_gb2312a\");\nextern const uint8_t u8g2_font_wqy13_t_gb2312b[] U8G2_FONT_SECTION(\"u8g2_font_wqy13_t_gb2312b\");\nextern const uint8_t u8g2_font_wqy14_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_chinese1\");\nextern const uint8_t u8g2_font_wqy14_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_chinese2\");\nextern const uint8_t u8g2_font_wqy14_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_chinese3\");\nextern const uint8_t u8g2_font_wqy14_t_gb2312[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_gb2312\");\nextern const uint8_t u8g2_font_wqy14_t_gb2312a[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_gb2312a\");\nextern const uint8_t u8g2_font_wqy14_t_gb2312b[] U8G2_FONT_SECTION(\"u8g2_font_wqy14_t_gb2312b\");\nextern const uint8_t u8g2_font_wqy15_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_chinese1\");\nextern const uint8_t u8g2_font_wqy15_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_chinese2\");\nextern const uint8_t u8g2_font_wqy15_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_chinese3\");\nextern const uint8_t u8g2_font_wqy15_t_gb2312[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_gb2312\");\nextern const uint8_t u8g2_font_wqy15_t_gb2312a[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_gb2312a\");\nextern const uint8_t u8g2_font_wqy15_t_gb2312b[] U8G2_FONT_SECTION(\"u8g2_font_wqy15_t_gb2312b\");\nextern const uint8_t u8g2_font_wqy16_t_chinese1[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_chinese1\");\nextern const uint8_t u8g2_font_wqy16_t_chinese2[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_chinese2\");\nextern const uint8_t u8g2_font_wqy16_t_chinese3[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_chinese3\");\nextern const uint8_t u8g2_font_wqy16_t_gb2312[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_gb2312\");\nextern const uint8_t u8g2_font_wqy16_t_gb2312a[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_gb2312a\");\nextern const uint8_t u8g2_font_wqy16_t_gb2312b[] U8G2_FONT_SECTION(\"u8g2_font_wqy16_t_gb2312b\");\nextern const uint8_t u8g2_font_b10_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b10_t_japanese1\");\nextern const uint8_t u8g2_font_b10_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b10_t_japanese2\");\nextern const uint8_t\n    u8g2_font_b10_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b10_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_b10_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b10_b_t_japanese2\");\nextern const uint8_t u8g2_font_f10_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f10_t_japanese1\");\nextern const uint8_t u8g2_font_f10_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f10_t_japanese2\");\nextern const uint8_t\n    u8g2_font_f10_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f10_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_f10_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f10_b_t_japanese2\");\nextern const uint8_t u8g2_font_b12_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b12_t_japanese1\");\nextern const uint8_t u8g2_font_b12_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b12_t_japanese2\");\nextern const uint8_t u8g2_font_b12_t_japanese3[] U8G2_FONT_SECTION(\"u8g2_font_b12_t_japanese3\");\nextern const uint8_t\n    u8g2_font_b12_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b12_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_b12_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b12_b_t_japanese2\");\nextern const uint8_t\n    u8g2_font_b12_b_t_japanese3[] U8G2_FONT_SECTION(\"u8g2_font_b12_b_t_japanese3\");\nextern const uint8_t u8g2_font_f12_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f12_t_japanese1\");\nextern const uint8_t u8g2_font_f12_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f12_t_japanese2\");\nextern const uint8_t\n    u8g2_font_f12_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f12_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_f12_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f12_b_t_japanese2\");\nextern const uint8_t u8g2_font_b16_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b16_t_japanese1\");\nextern const uint8_t u8g2_font_b16_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b16_t_japanese2\");\nextern const uint8_t u8g2_font_b16_t_japanese3[] U8G2_FONT_SECTION(\"u8g2_font_b16_t_japanese3\");\nextern const uint8_t\n    u8g2_font_b16_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_b16_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_b16_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_b16_b_t_japanese2\");\nextern const uint8_t\n    u8g2_font_b16_b_t_japanese3[] U8G2_FONT_SECTION(\"u8g2_font_b16_b_t_japanese3\");\nextern const uint8_t u8g2_font_f16_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f16_t_japanese1\");\nextern const uint8_t u8g2_font_f16_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f16_t_japanese2\");\nextern const uint8_t\n    u8g2_font_f16_b_t_japanese1[] U8G2_FONT_SECTION(\"u8g2_font_f16_b_t_japanese1\");\nextern const uint8_t\n    u8g2_font_f16_b_t_japanese2[] U8G2_FONT_SECTION(\"u8g2_font_f16_b_t_japanese2\");\nextern const uint8_t u8g2_font_artossans8_8r[] U8G2_FONT_SECTION(\"u8g2_font_artossans8_8r\");\nextern const uint8_t u8g2_font_artossans8_8n[] U8G2_FONT_SECTION(\"u8g2_font_artossans8_8n\");\nextern const uint8_t u8g2_font_artossans8_8u[] U8G2_FONT_SECTION(\"u8g2_font_artossans8_8u\");\nextern const uint8_t u8g2_font_artosserif8_8r[] U8G2_FONT_SECTION(\"u8g2_font_artosserif8_8r\");\nextern const uint8_t u8g2_font_artosserif8_8n[] U8G2_FONT_SECTION(\"u8g2_font_artosserif8_8n\");\nextern const uint8_t u8g2_font_artosserif8_8u[] U8G2_FONT_SECTION(\"u8g2_font_artosserif8_8u\");\nextern const uint8_t\n    u8g2_font_chroma48medium8_8r[] U8G2_FONT_SECTION(\"u8g2_font_chroma48medium8_8r\");\nextern const uint8_t\n    u8g2_font_chroma48medium8_8n[] U8G2_FONT_SECTION(\"u8g2_font_chroma48medium8_8n\");\nextern const uint8_t\n    u8g2_font_chroma48medium8_8u[] U8G2_FONT_SECTION(\"u8g2_font_chroma48medium8_8u\");\nextern const uint8_t\n    u8g2_font_saikyosansbold8_8n[] U8G2_FONT_SECTION(\"u8g2_font_saikyosansbold8_8n\");\nextern const uint8_t\n    u8g2_font_saikyosansbold8_8u[] U8G2_FONT_SECTION(\"u8g2_font_saikyosansbold8_8u\");\nextern const uint8_t\n    u8g2_font_torussansbold8_8r[] U8G2_FONT_SECTION(\"u8g2_font_torussansbold8_8r\");\nextern const uint8_t\n    u8g2_font_torussansbold8_8n[] U8G2_FONT_SECTION(\"u8g2_font_torussansbold8_8n\");\nextern const uint8_t\n    u8g2_font_torussansbold8_8u[] U8G2_FONT_SECTION(\"u8g2_font_torussansbold8_8u\");\nextern const uint8_t u8g2_font_victoriabold8_8r[] U8G2_FONT_SECTION(\"u8g2_font_victoriabold8_8r\");\nextern const uint8_t u8g2_font_victoriabold8_8n[] U8G2_FONT_SECTION(\"u8g2_font_victoriabold8_8n\");\nextern const uint8_t u8g2_font_victoriabold8_8u[] U8G2_FONT_SECTION(\"u8g2_font_victoriabold8_8u\");\nextern const uint8_t\n    u8g2_font_victoriamedium8_8r[] U8G2_FONT_SECTION(\"u8g2_font_victoriamedium8_8r\");\nextern const uint8_t\n    u8g2_font_victoriamedium8_8n[] U8G2_FONT_SECTION(\"u8g2_font_victoriamedium8_8n\");\nextern const uint8_t\n    u8g2_font_victoriamedium8_8u[] U8G2_FONT_SECTION(\"u8g2_font_victoriamedium8_8u\");\nextern const uint8_t u8g2_font_courB08_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB08_tf\");\nextern const uint8_t u8g2_font_courB08_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB08_tr\");\nextern const uint8_t u8g2_font_courB08_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB08_tn\");\nextern const uint8_t u8g2_font_courB10_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB10_tf\");\nextern const uint8_t u8g2_font_courB10_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB10_tr\");\nextern const uint8_t u8g2_font_courB10_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB10_tn\");\nextern const uint8_t u8g2_font_courB12_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB12_tf\");\nextern const uint8_t u8g2_font_courB12_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB12_tr\");\nextern const uint8_t u8g2_font_courB12_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB12_tn\");\nextern const uint8_t u8g2_font_courB14_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB14_tf\");\nextern const uint8_t u8g2_font_courB14_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB14_tr\");\nextern const uint8_t u8g2_font_courB14_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB14_tn\");\nextern const uint8_t u8g2_font_courB18_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB18_tf\");\nextern const uint8_t u8g2_font_courB18_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB18_tr\");\nextern const uint8_t u8g2_font_courB18_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB18_tn\");\nextern const uint8_t u8g2_font_courB24_tf[] U8G2_FONT_SECTION(\"u8g2_font_courB24_tf\");\nextern const uint8_t u8g2_font_courB24_tr[] U8G2_FONT_SECTION(\"u8g2_font_courB24_tr\");\nextern const uint8_t u8g2_font_courB24_tn[] U8G2_FONT_SECTION(\"u8g2_font_courB24_tn\");\nextern const uint8_t u8g2_font_courR08_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR08_tf\");\nextern const uint8_t u8g2_font_courR08_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR08_tr\");\nextern const uint8_t u8g2_font_courR08_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR08_tn\");\nextern const uint8_t u8g2_font_courR10_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR10_tf\");\nextern const uint8_t u8g2_font_courR10_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR10_tr\");\nextern const uint8_t u8g2_font_courR10_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR10_tn\");\nextern const uint8_t u8g2_font_courR12_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR12_tf\");\nextern const uint8_t u8g2_font_courR12_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR12_tr\");\nextern const uint8_t u8g2_font_courR12_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR12_tn\");\nextern const uint8_t u8g2_font_courR14_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR14_tf\");\nextern const uint8_t u8g2_font_courR14_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR14_tr\");\nextern const uint8_t u8g2_font_courR14_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR14_tn\");\nextern const uint8_t u8g2_font_courR18_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR18_tf\");\nextern const uint8_t u8g2_font_courR18_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR18_tr\");\nextern const uint8_t u8g2_font_courR18_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR18_tn\");\nextern const uint8_t u8g2_font_courR24_tf[] U8G2_FONT_SECTION(\"u8g2_font_courR24_tf\");\nextern const uint8_t u8g2_font_courR24_tr[] U8G2_FONT_SECTION(\"u8g2_font_courR24_tr\");\nextern const uint8_t u8g2_font_courR24_tn[] U8G2_FONT_SECTION(\"u8g2_font_courR24_tn\");\nextern const uint8_t u8g2_font_helvB08_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB08_tf\");\nextern const uint8_t u8g2_font_helvB08_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB08_tr\");\nextern const uint8_t u8g2_font_helvB08_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB08_tn\");\nextern const uint8_t u8g2_font_helvB08_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB08_te\");\nextern const uint8_t u8g2_font_helvB10_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB10_tf\");\nextern const uint8_t u8g2_font_helvB10_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB10_tr\");\nextern const uint8_t u8g2_font_helvB10_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB10_tn\");\nextern const uint8_t u8g2_font_helvB10_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB10_te\");\nextern const uint8_t u8g2_font_helvB12_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB12_tf\");\nextern const uint8_t u8g2_font_helvB12_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB12_tr\");\nextern const uint8_t u8g2_font_helvB12_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB12_tn\");\nextern const uint8_t u8g2_font_helvB12_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB12_te\");\nextern const uint8_t u8g2_font_helvB14_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB14_tf\");\nextern const uint8_t u8g2_font_helvB14_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB14_tr\");\nextern const uint8_t u8g2_font_helvB14_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB14_tn\");\nextern const uint8_t u8g2_font_helvB14_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB14_te\");\nextern const uint8_t u8g2_font_helvB18_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB18_tf\");\nextern const uint8_t u8g2_font_helvB18_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB18_tr\");\nextern const uint8_t u8g2_font_helvB18_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB18_tn\");\nextern const uint8_t u8g2_font_helvB18_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB18_te\");\nextern const uint8_t u8g2_font_helvB24_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvB24_tf\");\nextern const uint8_t u8g2_font_helvB24_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvB24_tr\");\nextern const uint8_t u8g2_font_helvB24_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvB24_tn\");\nextern const uint8_t u8g2_font_helvB24_te[] U8G2_FONT_SECTION(\"u8g2_font_helvB24_te\");\nextern const uint8_t u8g2_font_helvR08_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR08_tf\");\nextern const uint8_t u8g2_font_helvR08_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR08_tr\");\nextern const uint8_t u8g2_font_helvR08_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR08_tn\");\nextern const uint8_t u8g2_font_helvR08_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR08_te\");\nextern const uint8_t u8g2_font_helvR10_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR10_tf\");\nextern const uint8_t u8g2_font_helvR10_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR10_tr\");\nextern const uint8_t u8g2_font_helvR10_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR10_tn\");\nextern const uint8_t u8g2_font_helvR10_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR10_te\");\nextern const uint8_t u8g2_font_helvR12_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR12_tf\");\nextern const uint8_t u8g2_font_helvR12_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR12_tr\");\nextern const uint8_t u8g2_font_helvR12_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR12_tn\");\nextern const uint8_t u8g2_font_helvR12_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR12_te\");\nextern const uint8_t u8g2_font_helvR14_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR14_tf\");\nextern const uint8_t u8g2_font_helvR14_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR14_tr\");\nextern const uint8_t u8g2_font_helvR14_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR14_tn\");\nextern const uint8_t u8g2_font_helvR14_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR14_te\");\nextern const uint8_t u8g2_font_helvR18_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR18_tf\");\nextern const uint8_t u8g2_font_helvR18_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR18_tr\");\nextern const uint8_t u8g2_font_helvR18_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR18_tn\");\nextern const uint8_t u8g2_font_helvR18_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR18_te\");\nextern const uint8_t u8g2_font_helvR24_tf[] U8G2_FONT_SECTION(\"u8g2_font_helvR24_tf\");\nextern const uint8_t u8g2_font_helvR24_tr[] U8G2_FONT_SECTION(\"u8g2_font_helvR24_tr\");\nextern const uint8_t u8g2_font_helvR24_tn[] U8G2_FONT_SECTION(\"u8g2_font_helvR24_tn\");\nextern const uint8_t u8g2_font_helvR24_te[] U8G2_FONT_SECTION(\"u8g2_font_helvR24_te\");\nextern const uint8_t u8g2_font_ncenB08_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB08_tf\");\nextern const uint8_t u8g2_font_ncenB08_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB08_tr\");\nextern const uint8_t u8g2_font_ncenB08_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB08_tn\");\nextern const uint8_t u8g2_font_ncenB08_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB08_te\");\nextern const uint8_t u8g2_font_ncenB10_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB10_tf\");\nextern const uint8_t u8g2_font_ncenB10_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB10_tr\");\nextern const uint8_t u8g2_font_ncenB10_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB10_tn\");\nextern const uint8_t u8g2_font_ncenB10_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB10_te\");\nextern const uint8_t u8g2_font_ncenB12_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB12_tf\");\nextern const uint8_t u8g2_font_ncenB12_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB12_tr\");\nextern const uint8_t u8g2_font_ncenB12_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB12_tn\");\nextern const uint8_t u8g2_font_ncenB12_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB12_te\");\nextern const uint8_t u8g2_font_ncenB14_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB14_tf\");\nextern const uint8_t u8g2_font_ncenB14_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB14_tr\");\nextern const uint8_t u8g2_font_ncenB14_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB14_tn\");\nextern const uint8_t u8g2_font_ncenB14_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB14_te\");\nextern const uint8_t u8g2_font_ncenB18_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB18_tf\");\nextern const uint8_t u8g2_font_ncenB18_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB18_tr\");\nextern const uint8_t u8g2_font_ncenB18_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB18_tn\");\nextern const uint8_t u8g2_font_ncenB18_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB18_te\");\nextern const uint8_t u8g2_font_ncenB24_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenB24_tf\");\nextern const uint8_t u8g2_font_ncenB24_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenB24_tr\");\nextern const uint8_t u8g2_font_ncenB24_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenB24_tn\");\nextern const uint8_t u8g2_font_ncenB24_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenB24_te\");\nextern const uint8_t u8g2_font_ncenR08_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR08_tf\");\nextern const uint8_t u8g2_font_ncenR08_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR08_tr\");\nextern const uint8_t u8g2_font_ncenR08_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR08_tn\");\nextern const uint8_t u8g2_font_ncenR08_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR08_te\");\nextern const uint8_t u8g2_font_ncenR10_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR10_tf\");\nextern const uint8_t u8g2_font_ncenR10_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR10_tr\");\nextern const uint8_t u8g2_font_ncenR10_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR10_tn\");\nextern const uint8_t u8g2_font_ncenR10_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR10_te\");\nextern const uint8_t u8g2_font_ncenR12_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR12_tf\");\nextern const uint8_t u8g2_font_ncenR12_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR12_tr\");\nextern const uint8_t u8g2_font_ncenR12_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR12_tn\");\nextern const uint8_t u8g2_font_ncenR12_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR12_te\");\nextern const uint8_t u8g2_font_ncenR14_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR14_tf\");\nextern const uint8_t u8g2_font_ncenR14_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR14_tr\");\nextern const uint8_t u8g2_font_ncenR14_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR14_tn\");\nextern const uint8_t u8g2_font_ncenR14_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR14_te\");\nextern const uint8_t u8g2_font_ncenR18_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR18_tf\");\nextern const uint8_t u8g2_font_ncenR18_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR18_tr\");\nextern const uint8_t u8g2_font_ncenR18_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR18_tn\");\nextern const uint8_t u8g2_font_ncenR18_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR18_te\");\nextern const uint8_t u8g2_font_ncenR24_tf[] U8G2_FONT_SECTION(\"u8g2_font_ncenR24_tf\");\nextern const uint8_t u8g2_font_ncenR24_tr[] U8G2_FONT_SECTION(\"u8g2_font_ncenR24_tr\");\nextern const uint8_t u8g2_font_ncenR24_tn[] U8G2_FONT_SECTION(\"u8g2_font_ncenR24_tn\");\nextern const uint8_t u8g2_font_ncenR24_te[] U8G2_FONT_SECTION(\"u8g2_font_ncenR24_te\");\nextern const uint8_t u8g2_font_timB08_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB08_tf\");\nextern const uint8_t u8g2_font_timB08_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB08_tr\");\nextern const uint8_t u8g2_font_timB08_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB08_tn\");\nextern const uint8_t u8g2_font_timB10_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB10_tf\");\nextern const uint8_t u8g2_font_timB10_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB10_tr\");\nextern const uint8_t u8g2_font_timB10_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB10_tn\");\nextern const uint8_t u8g2_font_timB12_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB12_tf\");\nextern const uint8_t u8g2_font_timB12_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB12_tr\");\nextern const uint8_t u8g2_font_timB12_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB12_tn\");\nextern const uint8_t u8g2_font_timB14_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB14_tf\");\nextern const uint8_t u8g2_font_timB14_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB14_tr\");\nextern const uint8_t u8g2_font_timB14_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB14_tn\");\nextern const uint8_t u8g2_font_timB18_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB18_tf\");\nextern const uint8_t u8g2_font_timB18_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB18_tr\");\nextern const uint8_t u8g2_font_timB18_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB18_tn\");\nextern const uint8_t u8g2_font_timB24_tf[] U8G2_FONT_SECTION(\"u8g2_font_timB24_tf\");\nextern const uint8_t u8g2_font_timB24_tr[] U8G2_FONT_SECTION(\"u8g2_font_timB24_tr\");\nextern const uint8_t u8g2_font_timB24_tn[] U8G2_FONT_SECTION(\"u8g2_font_timB24_tn\");\nextern const uint8_t u8g2_font_timR08_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR08_tf\");\nextern const uint8_t u8g2_font_timR08_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR08_tr\");\nextern const uint8_t u8g2_font_timR08_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR08_tn\");\nextern const uint8_t u8g2_font_timR10_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR10_tf\");\nextern const uint8_t u8g2_font_timR10_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR10_tr\");\nextern const uint8_t u8g2_font_timR10_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR10_tn\");\nextern const uint8_t u8g2_font_timR12_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR12_tf\");\nextern const uint8_t u8g2_font_timR12_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR12_tr\");\nextern const uint8_t u8g2_font_timR12_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR12_tn\");\nextern const uint8_t u8g2_font_timR14_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR14_tf\");\nextern const uint8_t u8g2_font_timR14_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR14_tr\");\nextern const uint8_t u8g2_font_timR14_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR14_tn\");\nextern const uint8_t u8g2_font_timR18_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR18_tf\");\nextern const uint8_t u8g2_font_timR18_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR18_tr\");\nextern const uint8_t u8g2_font_timR18_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR18_tn\");\nextern const uint8_t u8g2_font_timR24_tf[] U8G2_FONT_SECTION(\"u8g2_font_timR24_tf\");\nextern const uint8_t u8g2_font_timR24_tr[] U8G2_FONT_SECTION(\"u8g2_font_timR24_tr\");\nextern const uint8_t u8g2_font_timR24_tn[] U8G2_FONT_SECTION(\"u8g2_font_timR24_tn\");\nextern const uint8_t u8g2_font_lubB08_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB08_tf\");\nextern const uint8_t u8g2_font_lubB08_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB08_tr\");\nextern const uint8_t u8g2_font_lubB08_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB08_tn\");\nextern const uint8_t u8g2_font_lubB08_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB08_te\");\nextern const uint8_t u8g2_font_lubB10_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB10_tf\");\nextern const uint8_t u8g2_font_lubB10_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB10_tr\");\nextern const uint8_t u8g2_font_lubB10_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB10_tn\");\nextern const uint8_t u8g2_font_lubB10_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB10_te\");\nextern const uint8_t u8g2_font_lubB12_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB12_tf\");\nextern const uint8_t u8g2_font_lubB12_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB12_tr\");\nextern const uint8_t u8g2_font_lubB12_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB12_tn\");\nextern const uint8_t u8g2_font_lubB12_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB12_te\");\nextern const uint8_t u8g2_font_lubB14_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB14_tf\");\nextern const uint8_t u8g2_font_lubB14_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB14_tr\");\nextern const uint8_t u8g2_font_lubB14_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB14_tn\");\nextern const uint8_t u8g2_font_lubB14_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB14_te\");\nextern const uint8_t u8g2_font_lubB18_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB18_tf\");\nextern const uint8_t u8g2_font_lubB18_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB18_tr\");\nextern const uint8_t u8g2_font_lubB18_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB18_tn\");\nextern const uint8_t u8g2_font_lubB18_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB18_te\");\nextern const uint8_t u8g2_font_lubB19_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB19_tf\");\nextern const uint8_t u8g2_font_lubB19_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB19_tr\");\nextern const uint8_t u8g2_font_lubB19_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB19_tn\");\nextern const uint8_t u8g2_font_lubB19_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB19_te\");\nextern const uint8_t u8g2_font_lubB24_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubB24_tf\");\nextern const uint8_t u8g2_font_lubB24_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubB24_tr\");\nextern const uint8_t u8g2_font_lubB24_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubB24_tn\");\nextern const uint8_t u8g2_font_lubB24_te[] U8G2_FONT_SECTION(\"u8g2_font_lubB24_te\");\nextern const uint8_t u8g2_font_lubBI08_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI08_tf\");\nextern const uint8_t u8g2_font_lubBI08_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI08_tr\");\nextern const uint8_t u8g2_font_lubBI08_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI08_tn\");\nextern const uint8_t u8g2_font_lubBI08_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI08_te\");\nextern const uint8_t u8g2_font_lubBI10_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI10_tf\");\nextern const uint8_t u8g2_font_lubBI10_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI10_tr\");\nextern const uint8_t u8g2_font_lubBI10_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI10_tn\");\nextern const uint8_t u8g2_font_lubBI10_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI10_te\");\nextern const uint8_t u8g2_font_lubBI12_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI12_tf\");\nextern const uint8_t u8g2_font_lubBI12_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI12_tr\");\nextern const uint8_t u8g2_font_lubBI12_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI12_tn\");\nextern const uint8_t u8g2_font_lubBI12_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI12_te\");\nextern const uint8_t u8g2_font_lubBI14_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI14_tf\");\nextern const uint8_t u8g2_font_lubBI14_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI14_tr\");\nextern const uint8_t u8g2_font_lubBI14_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI14_tn\");\nextern const uint8_t u8g2_font_lubBI14_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI14_te\");\nextern const uint8_t u8g2_font_lubBI18_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI18_tf\");\nextern const uint8_t u8g2_font_lubBI18_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI18_tr\");\nextern const uint8_t u8g2_font_lubBI18_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI18_tn\");\nextern const uint8_t u8g2_font_lubBI18_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI18_te\");\nextern const uint8_t u8g2_font_lubBI19_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI19_tf\");\nextern const uint8_t u8g2_font_lubBI19_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI19_tr\");\nextern const uint8_t u8g2_font_lubBI19_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI19_tn\");\nextern const uint8_t u8g2_font_lubBI19_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI19_te\");\nextern const uint8_t u8g2_font_lubBI24_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubBI24_tf\");\nextern const uint8_t u8g2_font_lubBI24_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubBI24_tr\");\nextern const uint8_t u8g2_font_lubBI24_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubBI24_tn\");\nextern const uint8_t u8g2_font_lubBI24_te[] U8G2_FONT_SECTION(\"u8g2_font_lubBI24_te\");\nextern const uint8_t u8g2_font_lubI08_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI08_tf\");\nextern const uint8_t u8g2_font_lubI08_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI08_tr\");\nextern const uint8_t u8g2_font_lubI08_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI08_tn\");\nextern const uint8_t u8g2_font_lubI08_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI08_te\");\nextern const uint8_t u8g2_font_lubI10_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI10_tf\");\nextern const uint8_t u8g2_font_lubI10_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI10_tr\");\nextern const uint8_t u8g2_font_lubI10_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI10_tn\");\nextern const uint8_t u8g2_font_lubI10_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI10_te\");\nextern const uint8_t u8g2_font_lubI12_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI12_tf\");\nextern const uint8_t u8g2_font_lubI12_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI12_tr\");\nextern const uint8_t u8g2_font_lubI12_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI12_tn\");\nextern const uint8_t u8g2_font_lubI12_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI12_te\");\nextern const uint8_t u8g2_font_lubI14_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI14_tf\");\nextern const uint8_t u8g2_font_lubI14_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI14_tr\");\nextern const uint8_t u8g2_font_lubI14_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI14_tn\");\nextern const uint8_t u8g2_font_lubI14_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI14_te\");\nextern const uint8_t u8g2_font_lubI18_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI18_tf\");\nextern const uint8_t u8g2_font_lubI18_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI18_tr\");\nextern const uint8_t u8g2_font_lubI18_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI18_tn\");\nextern const uint8_t u8g2_font_lubI18_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI18_te\");\nextern const uint8_t u8g2_font_lubI19_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI19_tf\");\nextern const uint8_t u8g2_font_lubI19_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI19_tr\");\nextern const uint8_t u8g2_font_lubI19_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI19_tn\");\nextern const uint8_t u8g2_font_lubI19_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI19_te\");\nextern const uint8_t u8g2_font_lubI24_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubI24_tf\");\nextern const uint8_t u8g2_font_lubI24_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubI24_tr\");\nextern const uint8_t u8g2_font_lubI24_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubI24_tn\");\nextern const uint8_t u8g2_font_lubI24_te[] U8G2_FONT_SECTION(\"u8g2_font_lubI24_te\");\nextern const uint8_t u8g2_font_luBIS08_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS08_tf\");\nextern const uint8_t u8g2_font_luBIS08_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS08_tr\");\nextern const uint8_t u8g2_font_luBIS08_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS08_tn\");\nextern const uint8_t u8g2_font_luBIS08_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS08_te\");\nextern const uint8_t u8g2_font_luBIS10_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS10_tf\");\nextern const uint8_t u8g2_font_luBIS10_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS10_tr\");\nextern const uint8_t u8g2_font_luBIS10_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS10_tn\");\nextern const uint8_t u8g2_font_luBIS10_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS10_te\");\nextern const uint8_t u8g2_font_luBIS12_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS12_tf\");\nextern const uint8_t u8g2_font_luBIS12_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS12_tr\");\nextern const uint8_t u8g2_font_luBIS12_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS12_tn\");\nextern const uint8_t u8g2_font_luBIS12_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS12_te\");\nextern const uint8_t u8g2_font_luBIS14_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS14_tf\");\nextern const uint8_t u8g2_font_luBIS14_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS14_tr\");\nextern const uint8_t u8g2_font_luBIS14_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS14_tn\");\nextern const uint8_t u8g2_font_luBIS14_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS14_te\");\nextern const uint8_t u8g2_font_luBIS18_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS18_tf\");\nextern const uint8_t u8g2_font_luBIS18_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS18_tr\");\nextern const uint8_t u8g2_font_luBIS18_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS18_tn\");\nextern const uint8_t u8g2_font_luBIS18_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS18_te\");\nextern const uint8_t u8g2_font_luBIS19_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS19_tf\");\nextern const uint8_t u8g2_font_luBIS19_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS19_tr\");\nextern const uint8_t u8g2_font_luBIS19_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS19_tn\");\nextern const uint8_t u8g2_font_luBIS19_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS19_te\");\nextern const uint8_t u8g2_font_luBIS24_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBIS24_tf\");\nextern const uint8_t u8g2_font_luBIS24_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBIS24_tr\");\nextern const uint8_t u8g2_font_luBIS24_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBIS24_tn\");\nextern const uint8_t u8g2_font_luBIS24_te[] U8G2_FONT_SECTION(\"u8g2_font_luBIS24_te\");\nextern const uint8_t u8g2_font_lubR08_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR08_tf\");\nextern const uint8_t u8g2_font_lubR08_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR08_tr\");\nextern const uint8_t u8g2_font_lubR08_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR08_tn\");\nextern const uint8_t u8g2_font_lubR08_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR08_te\");\nextern const uint8_t u8g2_font_lubR10_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR10_tf\");\nextern const uint8_t u8g2_font_lubR10_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR10_tr\");\nextern const uint8_t u8g2_font_lubR10_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR10_tn\");\nextern const uint8_t u8g2_font_lubR10_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR10_te\");\nextern const uint8_t u8g2_font_lubR12_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR12_tf\");\nextern const uint8_t u8g2_font_lubR12_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR12_tr\");\nextern const uint8_t u8g2_font_lubR12_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR12_tn\");\nextern const uint8_t u8g2_font_lubR12_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR12_te\");\nextern const uint8_t u8g2_font_lubR14_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR14_tf\");\nextern const uint8_t u8g2_font_lubR14_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR14_tr\");\nextern const uint8_t u8g2_font_lubR14_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR14_tn\");\nextern const uint8_t u8g2_font_lubR14_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR14_te\");\nextern const uint8_t u8g2_font_lubR18_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR18_tf\");\nextern const uint8_t u8g2_font_lubR18_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR18_tr\");\nextern const uint8_t u8g2_font_lubR18_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR18_tn\");\nextern const uint8_t u8g2_font_lubR18_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR18_te\");\nextern const uint8_t u8g2_font_lubR19_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR19_tf\");\nextern const uint8_t u8g2_font_lubR19_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR19_tr\");\nextern const uint8_t u8g2_font_lubR19_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR19_tn\");\nextern const uint8_t u8g2_font_lubR19_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR19_te\");\nextern const uint8_t u8g2_font_lubR24_tf[] U8G2_FONT_SECTION(\"u8g2_font_lubR24_tf\");\nextern const uint8_t u8g2_font_lubR24_tr[] U8G2_FONT_SECTION(\"u8g2_font_lubR24_tr\");\nextern const uint8_t u8g2_font_lubR24_tn[] U8G2_FONT_SECTION(\"u8g2_font_lubR24_tn\");\nextern const uint8_t u8g2_font_lubR24_te[] U8G2_FONT_SECTION(\"u8g2_font_lubR24_te\");\nextern const uint8_t u8g2_font_luBS08_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS08_tf\");\nextern const uint8_t u8g2_font_luBS08_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS08_tr\");\nextern const uint8_t u8g2_font_luBS08_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS08_tn\");\nextern const uint8_t u8g2_font_luBS08_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS08_te\");\nextern const uint8_t u8g2_font_luBS10_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS10_tf\");\nextern const uint8_t u8g2_font_luBS10_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS10_tr\");\nextern const uint8_t u8g2_font_luBS10_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS10_tn\");\nextern const uint8_t u8g2_font_luBS10_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS10_te\");\nextern const uint8_t u8g2_font_luBS12_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS12_tf\");\nextern const uint8_t u8g2_font_luBS12_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS12_tr\");\nextern const uint8_t u8g2_font_luBS12_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS12_tn\");\nextern const uint8_t u8g2_font_luBS12_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS12_te\");\nextern const uint8_t u8g2_font_luBS14_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS14_tf\");\nextern const uint8_t u8g2_font_luBS14_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS14_tr\");\nextern const uint8_t u8g2_font_luBS14_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS14_tn\");\nextern const uint8_t u8g2_font_luBS14_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS14_te\");\nextern const uint8_t u8g2_font_luBS18_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS18_tf\");\nextern const uint8_t u8g2_font_luBS18_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS18_tr\");\nextern const uint8_t u8g2_font_luBS18_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS18_tn\");\nextern const uint8_t u8g2_font_luBS18_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS18_te\");\nextern const uint8_t u8g2_font_luBS19_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS19_tf\");\nextern const uint8_t u8g2_font_luBS19_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS19_tr\");\nextern const uint8_t u8g2_font_luBS19_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS19_tn\");\nextern const uint8_t u8g2_font_luBS19_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS19_te\");\nextern const uint8_t u8g2_font_luBS24_tf[] U8G2_FONT_SECTION(\"u8g2_font_luBS24_tf\");\nextern const uint8_t u8g2_font_luBS24_tr[] U8G2_FONT_SECTION(\"u8g2_font_luBS24_tr\");\nextern const uint8_t u8g2_font_luBS24_tn[] U8G2_FONT_SECTION(\"u8g2_font_luBS24_tn\");\nextern const uint8_t u8g2_font_luBS24_te[] U8G2_FONT_SECTION(\"u8g2_font_luBS24_te\");\nextern const uint8_t u8g2_font_luIS08_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS08_tf\");\nextern const uint8_t u8g2_font_luIS08_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS08_tr\");\nextern const uint8_t u8g2_font_luIS08_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS08_tn\");\nextern const uint8_t u8g2_font_luIS08_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS08_te\");\nextern const uint8_t u8g2_font_luIS10_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS10_tf\");\nextern const uint8_t u8g2_font_luIS10_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS10_tr\");\nextern const uint8_t u8g2_font_luIS10_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS10_tn\");\nextern const uint8_t u8g2_font_luIS10_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS10_te\");\nextern const uint8_t u8g2_font_luIS12_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS12_tf\");\nextern const uint8_t u8g2_font_luIS12_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS12_tr\");\nextern const uint8_t u8g2_font_luIS12_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS12_tn\");\nextern const uint8_t u8g2_font_luIS12_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS12_te\");\nextern const uint8_t u8g2_font_luIS14_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS14_tf\");\nextern const uint8_t u8g2_font_luIS14_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS14_tr\");\nextern const uint8_t u8g2_font_luIS14_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS14_tn\");\nextern const uint8_t u8g2_font_luIS14_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS14_te\");\nextern const uint8_t u8g2_font_luIS18_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS18_tf\");\nextern const uint8_t u8g2_font_luIS18_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS18_tr\");\nextern const uint8_t u8g2_font_luIS18_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS18_tn\");\nextern const uint8_t u8g2_font_luIS18_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS18_te\");\nextern const uint8_t u8g2_font_luIS19_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS19_tf\");\nextern const uint8_t u8g2_font_luIS19_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS19_tr\");\nextern const uint8_t u8g2_font_luIS19_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS19_tn\");\nextern const uint8_t u8g2_font_luIS19_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS19_te\");\nextern const uint8_t u8g2_font_luIS24_tf[] U8G2_FONT_SECTION(\"u8g2_font_luIS24_tf\");\nextern const uint8_t u8g2_font_luIS24_tr[] U8G2_FONT_SECTION(\"u8g2_font_luIS24_tr\");\nextern const uint8_t u8g2_font_luIS24_tn[] U8G2_FONT_SECTION(\"u8g2_font_luIS24_tn\");\nextern const uint8_t u8g2_font_luIS24_te[] U8G2_FONT_SECTION(\"u8g2_font_luIS24_te\");\nextern const uint8_t u8g2_font_luRS08_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS08_tf\");\nextern const uint8_t u8g2_font_luRS08_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS08_tr\");\nextern const uint8_t u8g2_font_luRS08_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS08_tn\");\nextern const uint8_t u8g2_font_luRS08_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS08_te\");\nextern const uint8_t u8g2_font_luRS10_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS10_tf\");\nextern const uint8_t u8g2_font_luRS10_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS10_tr\");\nextern const uint8_t u8g2_font_luRS10_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS10_tn\");\nextern const uint8_t u8g2_font_luRS10_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS10_te\");\nextern const uint8_t u8g2_font_luRS12_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS12_tf\");\nextern const uint8_t u8g2_font_luRS12_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS12_tr\");\nextern const uint8_t u8g2_font_luRS12_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS12_tn\");\nextern const uint8_t u8g2_font_luRS12_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS12_te\");\nextern const uint8_t u8g2_font_luRS14_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS14_tf\");\nextern const uint8_t u8g2_font_luRS14_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS14_tr\");\nextern const uint8_t u8g2_font_luRS14_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS14_tn\");\nextern const uint8_t u8g2_font_luRS14_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS14_te\");\nextern const uint8_t u8g2_font_luRS18_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS18_tf\");\nextern const uint8_t u8g2_font_luRS18_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS18_tr\");\nextern const uint8_t u8g2_font_luRS18_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS18_tn\");\nextern const uint8_t u8g2_font_luRS18_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS18_te\");\nextern const uint8_t u8g2_font_luRS19_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS19_tf\");\nextern const uint8_t u8g2_font_luRS19_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS19_tr\");\nextern const uint8_t u8g2_font_luRS19_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS19_tn\");\nextern const uint8_t u8g2_font_luRS19_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS19_te\");\nextern const uint8_t u8g2_font_luRS24_tf[] U8G2_FONT_SECTION(\"u8g2_font_luRS24_tf\");\nextern const uint8_t u8g2_font_luRS24_tr[] U8G2_FONT_SECTION(\"u8g2_font_luRS24_tr\");\nextern const uint8_t u8g2_font_luRS24_tn[] U8G2_FONT_SECTION(\"u8g2_font_luRS24_tn\");\nextern const uint8_t u8g2_font_luRS24_te[] U8G2_FONT_SECTION(\"u8g2_font_luRS24_te\");\nextern const uint8_t u8g2_font_baby_tf[] U8G2_FONT_SECTION(\"u8g2_font_baby_tf\");\nextern const uint8_t u8g2_font_baby_tr[] U8G2_FONT_SECTION(\"u8g2_font_baby_tr\");\nextern const uint8_t u8g2_font_baby_tn[] U8G2_FONT_SECTION(\"u8g2_font_baby_tn\");\nextern const uint8_t u8g2_font_blipfest_07_tr[] U8G2_FONT_SECTION(\"u8g2_font_blipfest_07_tr\");\nextern const uint8_t u8g2_font_blipfest_07_tn[] U8G2_FONT_SECTION(\"u8g2_font_blipfest_07_tn\");\nextern const uint8_t u8g2_font_chikita_tf[] U8G2_FONT_SECTION(\"u8g2_font_chikita_tf\");\nextern const uint8_t u8g2_font_chikita_tr[] U8G2_FONT_SECTION(\"u8g2_font_chikita_tr\");\nextern const uint8_t u8g2_font_chikita_tn[] U8G2_FONT_SECTION(\"u8g2_font_chikita_tn\");\nextern const uint8_t\n    u8g2_font_lucasfont_alternate_tf[] U8G2_FONT_SECTION(\"u8g2_font_lucasfont_alternate_tf\");\nextern const uint8_t\n    u8g2_font_lucasfont_alternate_tr[] U8G2_FONT_SECTION(\"u8g2_font_lucasfont_alternate_tr\");\nextern const uint8_t\n    u8g2_font_lucasfont_alternate_tn[] U8G2_FONT_SECTION(\"u8g2_font_lucasfont_alternate_tn\");\nextern const uint8_t u8g2_font_p01type_tf[] U8G2_FONT_SECTION(\"u8g2_font_p01type_tf\");\nextern const uint8_t u8g2_font_p01type_tr[] U8G2_FONT_SECTION(\"u8g2_font_p01type_tr\");\nextern const uint8_t u8g2_font_p01type_tn[] U8G2_FONT_SECTION(\"u8g2_font_p01type_tn\");\nextern const uint8_t u8g2_font_pixelle_micro_tr[] U8G2_FONT_SECTION(\"u8g2_font_pixelle_micro_tr\");\nextern const uint8_t u8g2_font_pixelle_micro_tn[] U8G2_FONT_SECTION(\"u8g2_font_pixelle_micro_tn\");\nextern const uint8_t u8g2_font_robot_de_niro_tf[] U8G2_FONT_SECTION(\"u8g2_font_robot_de_niro_tf\");\nextern const uint8_t u8g2_font_robot_de_niro_tr[] U8G2_FONT_SECTION(\"u8g2_font_robot_de_niro_tr\");\nextern const uint8_t u8g2_font_robot_de_niro_tn[] U8G2_FONT_SECTION(\"u8g2_font_robot_de_niro_tn\");\nextern const uint8_t u8g2_font_trixel_square_tf[] U8G2_FONT_SECTION(\"u8g2_font_trixel_square_tf\");\nextern const uint8_t u8g2_font_trixel_square_tr[] U8G2_FONT_SECTION(\"u8g2_font_trixel_square_tr\");\nextern const uint8_t u8g2_font_trixel_square_tn[] U8G2_FONT_SECTION(\"u8g2_font_trixel_square_tn\");\nextern const uint8_t u8g2_font_haxrcorp4089_tr[] U8G2_FONT_SECTION(\"u8g2_font_haxrcorp4089_tr\");\nextern const uint8_t u8g2_font_haxrcorp4089_tn[] U8G2_FONT_SECTION(\"u8g2_font_haxrcorp4089_tn\");\nextern const uint8_t\n    u8g2_font_haxrcorp4089_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_haxrcorp4089_t_cyrillic\");\nextern const uint8_t u8g2_font_bubble_tr[] U8G2_FONT_SECTION(\"u8g2_font_bubble_tr\");\nextern const uint8_t u8g2_font_bubble_tn[] U8G2_FONT_SECTION(\"u8g2_font_bubble_tn\");\nextern const uint8_t\n    u8g2_font_cardimon_pixel_tf[] U8G2_FONT_SECTION(\"u8g2_font_cardimon_pixel_tf\");\nextern const uint8_t\n    u8g2_font_cardimon_pixel_tr[] U8G2_FONT_SECTION(\"u8g2_font_cardimon_pixel_tr\");\nextern const uint8_t\n    u8g2_font_cardimon_pixel_tn[] U8G2_FONT_SECTION(\"u8g2_font_cardimon_pixel_tn\");\nextern const uint8_t u8g2_font_maniac_tf[] U8G2_FONT_SECTION(\"u8g2_font_maniac_tf\");\nextern const uint8_t u8g2_font_maniac_tr[] U8G2_FONT_SECTION(\"u8g2_font_maniac_tr\");\nextern const uint8_t u8g2_font_maniac_tn[] U8G2_FONT_SECTION(\"u8g2_font_maniac_tn\");\nextern const uint8_t u8g2_font_maniac_te[] U8G2_FONT_SECTION(\"u8g2_font_maniac_te\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_o_tf[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_o_tf\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_o_tr[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_o_tr\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_o_tn[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_o_tn\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_r_tf[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_r_tf\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_r_tr[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_r_tr\");\nextern const uint8_t u8g2_font_lucasarts_scumm_subtitle_r_tn[] U8G2_FONT_SECTION(\n    \"u8g2_font_lucasarts_scumm_subtitle_r_tn\");\nextern const uint8_t u8g2_font_fub11_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub11_tf\");\nextern const uint8_t u8g2_font_fub11_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub11_tr\");\nextern const uint8_t u8g2_font_fub11_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub11_tn\");\nextern const uint8_t u8g2_font_fub14_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub14_tf\");\nextern const uint8_t u8g2_font_fub14_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub14_tr\");\nextern const uint8_t u8g2_font_fub14_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub14_tn\");\nextern const uint8_t u8g2_font_fub17_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub17_tf\");\nextern const uint8_t u8g2_font_fub17_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub17_tr\");\nextern const uint8_t u8g2_font_fub17_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub17_tn\");\nextern const uint8_t u8g2_font_fub20_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub20_tf\");\nextern const uint8_t u8g2_font_fub20_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub20_tr\");\nextern const uint8_t u8g2_font_fub20_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub20_tn\");\nextern const uint8_t u8g2_font_fub25_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub25_tf\");\nextern const uint8_t u8g2_font_fub25_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub25_tr\");\nextern const uint8_t u8g2_font_fub25_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub25_tn\");\nextern const uint8_t u8g2_font_fub30_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub30_tf\");\nextern const uint8_t u8g2_font_fub30_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub30_tr\");\nextern const uint8_t u8g2_font_fub30_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub30_tn\");\nextern const uint8_t u8g2_font_fub35_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub35_tf\");\nextern const uint8_t u8g2_font_fub35_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub35_tr\");\nextern const uint8_t u8g2_font_fub35_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub35_tn\");\nextern const uint8_t u8g2_font_fub42_tf[] U8G2_FONT_SECTION(\"u8g2_font_fub42_tf\");\nextern const uint8_t u8g2_font_fub42_tr[] U8G2_FONT_SECTION(\"u8g2_font_fub42_tr\");\nextern const uint8_t u8g2_font_fub42_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub42_tn\");\nextern const uint8_t u8g2_font_fub49_tn[] U8G2_FONT_SECTION(\"u8g2_font_fub49_tn\");\nextern const uint8_t u8g2_font_fub11_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub11_t_symbol\");\nextern const uint8_t u8g2_font_fub14_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub14_t_symbol\");\nextern const uint8_t u8g2_font_fub17_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub17_t_symbol\");\nextern const uint8_t u8g2_font_fub20_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub20_t_symbol\");\nextern const uint8_t u8g2_font_fub25_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub25_t_symbol\");\nextern const uint8_t u8g2_font_fub30_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub30_t_symbol\");\nextern const uint8_t u8g2_font_fub35_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub35_t_symbol\");\nextern const uint8_t u8g2_font_fub42_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub42_t_symbol\");\nextern const uint8_t u8g2_font_fub49_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fub49_t_symbol\");\nextern const uint8_t u8g2_font_fur11_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur11_tf\");\nextern const uint8_t u8g2_font_fur11_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur11_tr\");\nextern const uint8_t u8g2_font_fur11_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur11_tn\");\nextern const uint8_t u8g2_font_fur14_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur14_tf\");\nextern const uint8_t u8g2_font_fur14_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur14_tr\");\nextern const uint8_t u8g2_font_fur14_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur14_tn\");\nextern const uint8_t u8g2_font_fur17_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur17_tf\");\nextern const uint8_t u8g2_font_fur17_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur17_tr\");\nextern const uint8_t u8g2_font_fur17_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur17_tn\");\nextern const uint8_t u8g2_font_fur20_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur20_tf\");\nextern const uint8_t u8g2_font_fur20_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur20_tr\");\nextern const uint8_t u8g2_font_fur20_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur20_tn\");\nextern const uint8_t u8g2_font_fur25_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur25_tf\");\nextern const uint8_t u8g2_font_fur25_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur25_tr\");\nextern const uint8_t u8g2_font_fur25_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur25_tn\");\nextern const uint8_t u8g2_font_fur30_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur30_tf\");\nextern const uint8_t u8g2_font_fur30_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur30_tr\");\nextern const uint8_t u8g2_font_fur30_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur30_tn\");\nextern const uint8_t u8g2_font_fur35_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur35_tf\");\nextern const uint8_t u8g2_font_fur35_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur35_tr\");\nextern const uint8_t u8g2_font_fur35_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur35_tn\");\nextern const uint8_t u8g2_font_fur42_tf[] U8G2_FONT_SECTION(\"u8g2_font_fur42_tf\");\nextern const uint8_t u8g2_font_fur42_tr[] U8G2_FONT_SECTION(\"u8g2_font_fur42_tr\");\nextern const uint8_t u8g2_font_fur42_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur42_tn\");\nextern const uint8_t u8g2_font_fur49_tn[] U8G2_FONT_SECTION(\"u8g2_font_fur49_tn\");\nextern const uint8_t u8g2_font_fur11_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur11_t_symbol\");\nextern const uint8_t u8g2_font_fur14_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur14_t_symbol\");\nextern const uint8_t u8g2_font_fur17_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur17_t_symbol\");\nextern const uint8_t u8g2_font_fur20_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur20_t_symbol\");\nextern const uint8_t u8g2_font_fur25_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur25_t_symbol\");\nextern const uint8_t u8g2_font_fur30_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur30_t_symbol\");\nextern const uint8_t u8g2_font_fur35_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur35_t_symbol\");\nextern const uint8_t u8g2_font_fur42_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur42_t_symbol\");\nextern const uint8_t u8g2_font_fur49_t_symbol[] U8G2_FONT_SECTION(\"u8g2_font_fur49_t_symbol\");\nextern const uint8_t u8g2_font_osb18_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb18_tf\");\nextern const uint8_t u8g2_font_osb18_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb18_tr\");\nextern const uint8_t u8g2_font_osb18_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb18_tn\");\nextern const uint8_t u8g2_font_osb21_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb21_tf\");\nextern const uint8_t u8g2_font_osb21_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb21_tr\");\nextern const uint8_t u8g2_font_osb21_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb21_tn\");\nextern const uint8_t u8g2_font_osb26_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb26_tf\");\nextern const uint8_t u8g2_font_osb26_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb26_tr\");\nextern const uint8_t u8g2_font_osb26_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb26_tn\");\nextern const uint8_t u8g2_font_osb29_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb29_tf\");\nextern const uint8_t u8g2_font_osb29_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb29_tr\");\nextern const uint8_t u8g2_font_osb29_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb29_tn\");\nextern const uint8_t u8g2_font_osb35_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb35_tf\");\nextern const uint8_t u8g2_font_osb35_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb35_tr\");\nextern const uint8_t u8g2_font_osb35_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb35_tn\");\nextern const uint8_t u8g2_font_osb41_tf[] U8G2_FONT_SECTION(\"u8g2_font_osb41_tf\");\nextern const uint8_t u8g2_font_osb41_tr[] U8G2_FONT_SECTION(\"u8g2_font_osb41_tr\");\nextern const uint8_t u8g2_font_osb41_tn[] U8G2_FONT_SECTION(\"u8g2_font_osb41_tn\");\nextern const uint8_t u8g2_font_osr18_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr18_tf\");\nextern const uint8_t u8g2_font_osr18_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr18_tr\");\nextern const uint8_t u8g2_font_osr18_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr18_tn\");\nextern const uint8_t u8g2_font_osr21_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr21_tf\");\nextern const uint8_t u8g2_font_osr21_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr21_tr\");\nextern const uint8_t u8g2_font_osr21_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr21_tn\");\nextern const uint8_t u8g2_font_osr26_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr26_tf\");\nextern const uint8_t u8g2_font_osr26_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr26_tr\");\nextern const uint8_t u8g2_font_osr26_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr26_tn\");\nextern const uint8_t u8g2_font_osr29_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr29_tf\");\nextern const uint8_t u8g2_font_osr29_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr29_tr\");\nextern const uint8_t u8g2_font_osr29_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr29_tn\");\nextern const uint8_t u8g2_font_osr35_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr35_tf\");\nextern const uint8_t u8g2_font_osr35_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr35_tr\");\nextern const uint8_t u8g2_font_osr35_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr35_tn\");\nextern const uint8_t u8g2_font_osr41_tf[] U8G2_FONT_SECTION(\"u8g2_font_osr41_tf\");\nextern const uint8_t u8g2_font_osr41_tr[] U8G2_FONT_SECTION(\"u8g2_font_osr41_tr\");\nextern const uint8_t u8g2_font_osr41_tn[] U8G2_FONT_SECTION(\"u8g2_font_osr41_tn\");\nextern const uint8_t u8g2_font_inr16_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr16_mf\");\nextern const uint8_t u8g2_font_inr16_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr16_mr\");\nextern const uint8_t u8g2_font_inr16_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr16_mn\");\nextern const uint8_t u8g2_font_inr19_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr19_mf\");\nextern const uint8_t u8g2_font_inr19_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr19_mr\");\nextern const uint8_t u8g2_font_inr19_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr19_mn\");\nextern const uint8_t u8g2_font_inr21_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr21_mf\");\nextern const uint8_t u8g2_font_inr21_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr21_mr\");\nextern const uint8_t u8g2_font_inr21_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr21_mn\");\nextern const uint8_t u8g2_font_inr24_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr24_mf\");\nextern const uint8_t u8g2_font_inr24_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr24_mr\");\nextern const uint8_t u8g2_font_inr24_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr24_mn\");\nextern const uint8_t u8g2_font_inr24_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr24_t_cyrillic\");\nextern const uint8_t u8g2_font_inr27_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr27_mf\");\nextern const uint8_t u8g2_font_inr27_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr27_mr\");\nextern const uint8_t u8g2_font_inr27_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr27_mn\");\nextern const uint8_t u8g2_font_inr27_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr27_t_cyrillic\");\nextern const uint8_t u8g2_font_inr30_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr30_mf\");\nextern const uint8_t u8g2_font_inr30_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr30_mr\");\nextern const uint8_t u8g2_font_inr30_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr30_mn\");\nextern const uint8_t u8g2_font_inr30_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr30_t_cyrillic\");\nextern const uint8_t u8g2_font_inr33_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr33_mf\");\nextern const uint8_t u8g2_font_inr33_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr33_mr\");\nextern const uint8_t u8g2_font_inr33_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr33_mn\");\nextern const uint8_t u8g2_font_inr33_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr33_t_cyrillic\");\nextern const uint8_t u8g2_font_inr38_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr38_mf\");\nextern const uint8_t u8g2_font_inr38_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr38_mr\");\nextern const uint8_t u8g2_font_inr38_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr38_mn\");\nextern const uint8_t u8g2_font_inr38_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr38_t_cyrillic\");\nextern const uint8_t u8g2_font_inr42_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr42_mf\");\nextern const uint8_t u8g2_font_inr42_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr42_mr\");\nextern const uint8_t u8g2_font_inr42_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr42_mn\");\nextern const uint8_t u8g2_font_inr42_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr42_t_cyrillic\");\nextern const uint8_t u8g2_font_inr46_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr46_mf\");\nextern const uint8_t u8g2_font_inr46_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr46_mr\");\nextern const uint8_t u8g2_font_inr46_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr46_mn\");\nextern const uint8_t u8g2_font_inr46_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr46_t_cyrillic\");\nextern const uint8_t u8g2_font_inr49_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr49_mf\");\nextern const uint8_t u8g2_font_inr49_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr49_mr\");\nextern const uint8_t u8g2_font_inr49_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr49_mn\");\nextern const uint8_t u8g2_font_inr49_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr49_t_cyrillic\");\nextern const uint8_t u8g2_font_inr53_mf[] U8G2_FONT_SECTION(\"u8g2_font_inr53_mf\");\nextern const uint8_t u8g2_font_inr53_mr[] U8G2_FONT_SECTION(\"u8g2_font_inr53_mr\");\nextern const uint8_t u8g2_font_inr53_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr53_mn\");\nextern const uint8_t u8g2_font_inr53_t_cyrillic[] U8G2_FONT_SECTION(\"u8g2_font_inr53_t_cyrillic\");\nextern const uint8_t u8g2_font_inr57_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr57_mn\");\nextern const uint8_t u8g2_font_inr62_mn[] U8G2_FONT_SECTION(\"u8g2_font_inr62_mn\");\nextern const uint8_t u8g2_font_inb16_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb16_mf\");\nextern const uint8_t u8g2_font_inb16_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb16_mr\");\nextern const uint8_t u8g2_font_inb16_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb16_mn\");\nextern const uint8_t u8g2_font_inb19_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb19_mf\");\nextern const uint8_t u8g2_font_inb19_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb19_mr\");\nextern const uint8_t u8g2_font_inb19_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb19_mn\");\nextern const uint8_t u8g2_font_inb21_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb21_mf\");\nextern const uint8_t u8g2_font_inb21_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb21_mr\");\nextern const uint8_t u8g2_font_inb21_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb21_mn\");\nextern const uint8_t u8g2_font_inb24_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb24_mf\");\nextern const uint8_t u8g2_font_inb24_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb24_mr\");\nextern const uint8_t u8g2_font_inb24_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb24_mn\");\nextern const uint8_t u8g2_font_inb27_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb27_mf\");\nextern const uint8_t u8g2_font_inb27_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb27_mr\");\nextern const uint8_t u8g2_font_inb27_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb27_mn\");\nextern const uint8_t u8g2_font_inb30_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb30_mf\");\nextern const uint8_t u8g2_font_inb30_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb30_mr\");\nextern const uint8_t u8g2_font_inb30_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb30_mn\");\nextern const uint8_t u8g2_font_inb33_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb33_mf\");\nextern const uint8_t u8g2_font_inb33_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb33_mr\");\nextern const uint8_t u8g2_font_inb33_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb33_mn\");\nextern const uint8_t u8g2_font_inb38_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb38_mf\");\nextern const uint8_t u8g2_font_inb38_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb38_mr\");\nextern const uint8_t u8g2_font_inb38_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb38_mn\");\nextern const uint8_t u8g2_font_inb42_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb42_mf\");\nextern const uint8_t u8g2_font_inb42_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb42_mr\");\nextern const uint8_t u8g2_font_inb42_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb42_mn\");\nextern const uint8_t u8g2_font_inb46_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb46_mf\");\nextern const uint8_t u8g2_font_inb46_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb46_mr\");\nextern const uint8_t u8g2_font_inb46_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb46_mn\");\nextern const uint8_t u8g2_font_inb49_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb49_mf\");\nextern const uint8_t u8g2_font_inb49_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb49_mr\");\nextern const uint8_t u8g2_font_inb49_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb49_mn\");\nextern const uint8_t u8g2_font_inb53_mf[] U8G2_FONT_SECTION(\"u8g2_font_inb53_mf\");\nextern const uint8_t u8g2_font_inb53_mr[] U8G2_FONT_SECTION(\"u8g2_font_inb53_mr\");\nextern const uint8_t u8g2_font_inb53_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb53_mn\");\nextern const uint8_t u8g2_font_inb57_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb57_mn\");\nextern const uint8_t u8g2_font_inb63_mn[] U8G2_FONT_SECTION(\"u8g2_font_inb63_mn\");\nextern const uint8_t u8g2_font_logisoso16_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso16_tf\");\nextern const uint8_t u8g2_font_logisoso16_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso16_tr\");\nextern const uint8_t u8g2_font_logisoso16_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso16_tn\");\nextern const uint8_t u8g2_font_logisoso18_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso18_tf\");\nextern const uint8_t u8g2_font_logisoso18_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso18_tr\");\nextern const uint8_t u8g2_font_logisoso18_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso18_tn\");\nextern const uint8_t u8g2_font_logisoso20_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso20_tf\");\nextern const uint8_t u8g2_font_logisoso20_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso20_tr\");\nextern const uint8_t u8g2_font_logisoso20_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso20_tn\");\nextern const uint8_t u8g2_font_logisoso22_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso22_tf\");\nextern const uint8_t u8g2_font_logisoso22_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso22_tr\");\nextern const uint8_t u8g2_font_logisoso22_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso22_tn\");\nextern const uint8_t u8g2_font_logisoso24_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso24_tf\");\nextern const uint8_t u8g2_font_logisoso24_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso24_tr\");\nextern const uint8_t u8g2_font_logisoso24_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso24_tn\");\nextern const uint8_t u8g2_font_logisoso26_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso26_tf\");\nextern const uint8_t u8g2_font_logisoso26_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso26_tr\");\nextern const uint8_t u8g2_font_logisoso26_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso26_tn\");\nextern const uint8_t u8g2_font_logisoso28_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso28_tf\");\nextern const uint8_t u8g2_font_logisoso28_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso28_tr\");\nextern const uint8_t u8g2_font_logisoso28_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso28_tn\");\nextern const uint8_t u8g2_font_logisoso30_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso30_tf\");\nextern const uint8_t u8g2_font_logisoso30_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso30_tr\");\nextern const uint8_t u8g2_font_logisoso30_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso30_tn\");\nextern const uint8_t u8g2_font_logisoso32_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso32_tf\");\nextern const uint8_t u8g2_font_logisoso32_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso32_tr\");\nextern const uint8_t u8g2_font_logisoso32_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso32_tn\");\nextern const uint8_t u8g2_font_logisoso34_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso34_tf\");\nextern const uint8_t u8g2_font_logisoso34_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso34_tr\");\nextern const uint8_t u8g2_font_logisoso34_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso34_tn\");\nextern const uint8_t u8g2_font_logisoso38_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso38_tf\");\nextern const uint8_t u8g2_font_logisoso38_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso38_tr\");\nextern const uint8_t u8g2_font_logisoso38_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso38_tn\");\nextern const uint8_t u8g2_font_logisoso42_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso42_tf\");\nextern const uint8_t u8g2_font_logisoso42_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso42_tr\");\nextern const uint8_t u8g2_font_logisoso42_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso42_tn\");\nextern const uint8_t u8g2_font_logisoso46_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso46_tf\");\nextern const uint8_t u8g2_font_logisoso46_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso46_tr\");\nextern const uint8_t u8g2_font_logisoso46_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso46_tn\");\nextern const uint8_t u8g2_font_logisoso50_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso50_tf\");\nextern const uint8_t u8g2_font_logisoso50_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso50_tr\");\nextern const uint8_t u8g2_font_logisoso50_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso50_tn\");\nextern const uint8_t u8g2_font_logisoso54_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso54_tf\");\nextern const uint8_t u8g2_font_logisoso54_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso54_tr\");\nextern const uint8_t u8g2_font_logisoso54_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso54_tn\");\nextern const uint8_t u8g2_font_logisoso58_tf[] U8G2_FONT_SECTION(\"u8g2_font_logisoso58_tf\");\nextern const uint8_t u8g2_font_logisoso58_tr[] U8G2_FONT_SECTION(\"u8g2_font_logisoso58_tr\");\nextern const uint8_t u8g2_font_logisoso58_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso58_tn\");\nextern const uint8_t u8g2_font_logisoso62_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso62_tn\");\nextern const uint8_t u8g2_font_logisoso78_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso78_tn\");\nextern const uint8_t u8g2_font_logisoso92_tn[] U8G2_FONT_SECTION(\"u8g2_font_logisoso92_tn\");\nextern const uint8_t u8g2_font_pressstart2p_8f[] U8G2_FONT_SECTION(\"u8g2_font_pressstart2p_8f\");\nextern const uint8_t u8g2_font_pressstart2p_8r[] U8G2_FONT_SECTION(\"u8g2_font_pressstart2p_8r\");\nextern const uint8_t u8g2_font_pressstart2p_8n[] U8G2_FONT_SECTION(\"u8g2_font_pressstart2p_8n\");\nextern const uint8_t u8g2_font_pressstart2p_8u[] U8G2_FONT_SECTION(\"u8g2_font_pressstart2p_8u\");\nextern const uint8_t u8g2_font_pcsenior_8f[] U8G2_FONT_SECTION(\"u8g2_font_pcsenior_8f\");\nextern const uint8_t u8g2_font_pcsenior_8r[] U8G2_FONT_SECTION(\"u8g2_font_pcsenior_8r\");\nextern const uint8_t u8g2_font_pcsenior_8n[] U8G2_FONT_SECTION(\"u8g2_font_pcsenior_8n\");\nextern const uint8_t u8g2_font_pcsenior_8u[] U8G2_FONT_SECTION(\"u8g2_font_pcsenior_8u\");\nextern const uint8_t\n    u8g2_font_pxplusibmcgathin_8f[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcgathin_8f\");\nextern const uint8_t\n    u8g2_font_pxplusibmcgathin_8r[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcgathin_8r\");\nextern const uint8_t\n    u8g2_font_pxplusibmcgathin_8n[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcgathin_8n\");\nextern const uint8_t\n    u8g2_font_pxplusibmcgathin_8u[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcgathin_8u\");\nextern const uint8_t u8g2_font_pxplusibmcga_8f[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcga_8f\");\nextern const uint8_t u8g2_font_pxplusibmcga_8r[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcga_8r\");\nextern const uint8_t u8g2_font_pxplusibmcga_8n[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcga_8n\");\nextern const uint8_t u8g2_font_pxplusibmcga_8u[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmcga_8u\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_8f[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_8f\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_8r[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_8r\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_8n[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_8n\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_8u[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_8u\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_t_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_t_all\");\nextern const uint8_t\n    u8g2_font_pxplustandynewtv_8_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplustandynewtv_8_all\");\nextern const uint8_t u8g2_font_pxplusibmvga9_tf[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_tf\");\nextern const uint8_t u8g2_font_pxplusibmvga9_tr[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_tr\");\nextern const uint8_t u8g2_font_pxplusibmvga9_tn[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_tn\");\nextern const uint8_t u8g2_font_pxplusibmvga9_mf[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_mf\");\nextern const uint8_t u8g2_font_pxplusibmvga9_mr[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_mr\");\nextern const uint8_t u8g2_font_pxplusibmvga9_mn[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_mn\");\nextern const uint8_t\n    u8g2_font_pxplusibmvga9_t_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_t_all\");\nextern const uint8_t\n    u8g2_font_pxplusibmvga9_m_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga9_m_all\");\nextern const uint8_t u8g2_font_pxplusibmvga8_tf[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_tf\");\nextern const uint8_t u8g2_font_pxplusibmvga8_tr[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_tr\");\nextern const uint8_t u8g2_font_pxplusibmvga8_tn[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_tn\");\nextern const uint8_t u8g2_font_pxplusibmvga8_mf[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_mf\");\nextern const uint8_t u8g2_font_pxplusibmvga8_mr[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_mr\");\nextern const uint8_t u8g2_font_pxplusibmvga8_mn[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_mn\");\nextern const uint8_t\n    u8g2_font_pxplusibmvga8_t_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_t_all\");\nextern const uint8_t\n    u8g2_font_pxplusibmvga8_m_all[] U8G2_FONT_SECTION(\"u8g2_font_pxplusibmvga8_m_all\");\nextern const uint8_t u8g2_font_px437wyse700a_tf[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_tf\");\nextern const uint8_t u8g2_font_px437wyse700a_tr[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_tr\");\nextern const uint8_t u8g2_font_px437wyse700a_tn[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_tn\");\nextern const uint8_t u8g2_font_px437wyse700a_mf[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_mf\");\nextern const uint8_t u8g2_font_px437wyse700a_mr[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_mr\");\nextern const uint8_t u8g2_font_px437wyse700a_mn[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700a_mn\");\nextern const uint8_t u8g2_font_px437wyse700b_tf[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_tf\");\nextern const uint8_t u8g2_font_px437wyse700b_tr[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_tr\");\nextern const uint8_t u8g2_font_px437wyse700b_tn[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_tn\");\nextern const uint8_t u8g2_font_px437wyse700b_mf[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_mf\");\nextern const uint8_t u8g2_font_px437wyse700b_mr[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_mr\");\nextern const uint8_t u8g2_font_px437wyse700b_mn[] U8G2_FONT_SECTION(\"u8g2_font_px437wyse700b_mn\");\n\n/* end font list */\n\n/*==========================================*/\n/* u8g font mapping, might be incomplete.... */\n\n#define u8g_font_10x20                u8g2_font_10x20_tf\n#define u8g_font_10x20r               u8g2_font_10x20_tr\n#define u8g_font_4x6                  u8g2_font_4x6_tf\n#define u8g_font_4x6r                 u8g2_font_4x6_tr\n#define u8g_font_5x7                  u8g2_font_5x7_tf\n#define u8g_font_5x7r                 u8g2_font_5x7_tr\n#define u8g_font_5x8                  u8g2_font_5x8_tf\n#define u8g_font_5x8r                 u8g2_font_5x8_tr\n#define u8g_font_6x10                 u8g2_font_6x10_tf\n#define u8g_font_6x10r                u8g2_font_6x10_tr\n#define u8g_font_6x12                 u8g2_font_6x12_tf\n#define u8g_font_6x12r                u8g2_font_6x12_tr\n#define u8g_font_6x13B                u8g2_font_6x13B_tf\n#define u8g_font_6x13Br               u8g2_font_6x13B_tr\n#define u8g_font_6x13                 u8g2_font_6x13_tf\n#define u8g_font_6x13r                u8g2_font_6x13_tr\n#define u8g_font_6x13O                u8g2_font_6x13O_tf\n#define u8g_font_6x13Or               u8g2_font_6x13O_tr\n#define u8g_font_7x13B                u8g2_font_7x13B_tf\n#define u8g_font_7x13Br               u8g2_font_7x13B_tr\n#define u8g_font_7x13                 u8g2_font_7x13_tf\n#define u8g_font_7x13r                u8g2_font_7x13_tr\n#define u8g_font_7x13O                u8g2_font_7x13O_tf\n#define u8g_font_7x13Or               u8g2_font_7x13O_tr\n#define u8g_font_7x14B                u8g2_font_7x14B_tf\n#define u8g_font_7x14Br               u8g2_font_7x14B_tr\n#define u8g_font_7x14                 u8g2_font_7x14_tf\n#define u8g_font_7x14r                u8g2_font_7x14_tr\n#define u8g_font_8x13B                u8g2_font_8x13B_tf\n#define u8g_font_8x13Br               u8g2_font_8x13B_tr\n#define u8g_font_8x13                 u8g2_font_8x13_tf\n#define u8g_font_8x13r                u8g2_font_8x13_tr\n#define u8g_font_8x13O                u8g2_font_8x13O_tf\n#define u8g_font_8x13Or               u8g2_font_8x13O_tr\n#define u8g_font_9x15B                u8g2_font_9x15B_tf\n#define u8g_font_9x15Br               u8g2_font_9x15B_tr\n#define u8g_font_9x15                 u8g2_font_9x15_tf\n#define u8g_font_9x15r                u8g2_font_9x15_tr\n#define u8g_font_9x18B                u8g2_font_9x18B_tf\n#define u8g_font_9x18                 u8g2_font_9x18_tf\n#define u8g_font_9x18Br               u8g2_font_9x18B_tr\n#define u8g_font_9x18r                u8g2_font_9x18_tr\n#define u8g_font_cu12                 u8g2_font_cu12_tf\n#define u8g_font_micro                u8g2_font_micro_tf\n#define u8g_font_unifont              u8g2_font_unifont_t_latin\n#define u8g_font_unifontr             u8g2_font_unifont_t_latin\n#define u8g_font_courB08              u8g2_font_courB08_tf\n#define u8g_font_courB08r             u8g2_font_courB08_tr\n#define u8g_font_courB10              u8g2_font_courB10_tf\n#define u8g_font_courB10r             u8g2_font_courB10_tr\n#define u8g_font_courB12              u8g2_font_courB12_tf\n#define u8g_font_courB12r             u8g2_font_courB12_tr\n#define u8g_font_courB14              u8g2_font_courB14_tf\n#define u8g_font_courB14r             u8g2_font_courB14_tr\n#define u8g_font_courB18              u8g2_font_courB18_tf\n#define u8g_font_courB18r             u8g2_font_courB18_tr\n#define u8g_font_courB24              u8g2_font_courB24_tf\n#define u8g_font_courB24r             u8g2_font_courB24_tr\n#define u8g_font_courB24n             u8g2_font_courB24_tn\n#define u8g_font_courR08              u8g2_font_courR08_tf\n#define u8g_font_courR08r             u8g2_font_courR08_tr\n#define u8g_font_courR10              u8g2_font_courR10_tf\n#define u8g_font_courR10r             u8g2_font_courR10_tr\n#define u8g_font_courR12              u8g2_font_courR12_tf\n#define u8g_font_courR12r             u8g2_font_courR12_tr\n#define u8g_font_courR14              u8g2_font_courR14_tf\n#define u8g_font_courR14r             u8g2_font_courR14_tr\n#define u8g_font_courR18              u8g2_font_courR18_tf\n#define u8g_font_courR18r             u8g2_font_courR18_tr\n#define u8g_font_courR24              u8g2_font_courR24_tf\n#define u8g_font_courR24r             u8g2_font_courR24_tr\n#define u8g_font_courR24n             u8g2_font_courR24_tn\n#define u8g_font_helvB08              u8g2_font_helvB08_tf\n#define u8g_font_helvB08r             u8g2_font_helvB08_tr\n#define u8g_font_helvB08n             u8g2_font_helvB08_tn\n#define u8g_font_helvB10              u8g2_font_helvB10_tf\n#define u8g_font_helvB10r             u8g2_font_helvB10_tr\n#define u8g_font_helvB10n             u8g2_font_helvB10_tn\n#define u8g_font_helvB12              u8g2_font_helvB12_tf\n#define u8g_font_helvB12r             u8g2_font_helvB12_tr\n#define u8g_font_helvB12n             u8g2_font_helvB12_tn\n#define u8g_font_helvB14              u8g2_font_helvB14_tf\n#define u8g_font_helvB14r             u8g2_font_helvB14_tr\n#define u8g_font_helvB14n             u8g2_font_helvB14_tn\n#define u8g_font_helvB18              u8g2_font_helvB18_tf\n#define u8g_font_helvB18r             u8g2_font_helvB18_tr\n#define u8g_font_helvB18n             u8g2_font_helvB18_tn\n#define u8g_font_helvB24              u8g2_font_helvB24_tf\n#define u8g_font_helvB24r             u8g2_font_helvB24_tr\n#define u8g_font_helvB24n             u8g2_font_helvB24_tn\n#define u8g_font_helvR08              u8g2_font_helvR08_tf\n#define u8g_font_helvR08r             u8g2_font_helvR08_tr\n#define u8g_font_helvR08n             u8g2_font_helvR08_tn\n#define u8g_font_helvR10              u8g2_font_helvR10_tf\n#define u8g_font_helvR10r             u8g2_font_helvR10_tr\n#define u8g_font_helvR10n             u8g2_font_helvR10_tn\n#define u8g_font_helvR12              u8g2_font_helvR12_tf\n#define u8g_font_helvR12r             u8g2_font_helvR12_tr\n#define u8g_font_helvR12n             u8g2_font_helvR12_tn\n#define u8g_font_helvR14              u8g2_font_helvR14_tf\n#define u8g_font_helvR14r             u8g2_font_helvR14_tr\n#define u8g_font_helvR14n             u8g2_font_helvR14_tn\n#define u8g_font_helvR18              u8g2_font_helvR18_tf\n#define u8g_font_helvR18r             u8g2_font_helvR18_tr\n#define u8g_font_helvR18n             u8g2_font_helvR18_tn\n#define u8g_font_helvR24              u8g2_font_helvR24_tf\n#define u8g_font_helvR24r             u8g2_font_helvR24_tr\n#define u8g_font_helvR24n             u8g2_font_helvR24_tn\n#define u8g_font_ncenB08              u8g2_font_ncenB08_tf\n#define u8g_font_ncenB08r             u8g2_font_ncenB08_tr\n#define u8g_font_ncenB10              u8g2_font_ncenB10_tf\n#define u8g_font_ncenB10r             u8g2_font_ncenB10_tr\n#define u8g_font_ncenB12              u8g2_font_ncenB12_tf\n#define u8g_font_ncenB12r             u8g2_font_ncenB12_tr\n#define u8g_font_ncenB14              u8g2_font_ncenB14_tf\n#define u8g_font_ncenB14r             u8g2_font_ncenB14_tr\n#define u8g_font_ncenB18              u8g2_font_ncenB18_tf\n#define u8g_font_ncenB18r             u8g2_font_ncenB18_tr\n#define u8g_font_ncenB24              u8g2_font_ncenB24_tf\n#define u8g_font_ncenB24r             u8g2_font_ncenB24_tr\n#define u8g_font_ncenB24n             u8g2_font_ncenB24_tn\n#define u8g_font_ncenR08              u8g2_font_ncenR08_tf\n#define u8g_font_ncenR08r             u8g2_font_ncenR08_tr\n#define u8g_font_ncenR10              u8g2_font_ncenR10_tf\n#define u8g_font_ncenR10r             u8g2_font_ncenR10_tr\n#define u8g_font_ncenR12              u8g2_font_ncenR12_tf\n#define u8g_font_ncenR12r             u8g2_font_ncenR12_tr\n#define u8g_font_ncenR14              u8g2_font_ncenR14_tf\n#define u8g_font_ncenR14r             u8g2_font_ncenR14_tr\n#define u8g_font_ncenR18              u8g2_font_ncenR18_tf\n#define u8g_font_ncenR18r             u8g2_font_ncenR18_tr\n#define u8g_font_ncenR24              u8g2_font_ncenR24_tf\n#define u8g_font_ncenR24r             u8g2_font_ncenR24_tr\n#define u8g_font_ncenR24n             u8g2_font_ncenR24_tn\n#define u8g_font_timB08               u8g2_font_timB08_tf\n#define u8g_font_timB08r              u8g2_font_timB08_tr\n#define u8g_font_timB10               u8g2_font_timB10_tf\n#define u8g_font_timB10r              u8g2_font_timB10_tr\n#define u8g_font_timB12               u8g2_font_timB12_tf\n#define u8g_font_timB12r              u8g2_font_timB12_tr\n#define u8g_font_timB14               u8g2_font_timB14_tf\n#define u8g_font_timB14r              u8g2_font_timB14_tr\n#define u8g_font_timB18               u8g2_font_timB18_tf\n#define u8g_font_timB18r              u8g2_font_timB18_tr\n#define u8g_font_timB24               u8g2_font_timB24_tf\n#define u8g_font_timB24r              u8g2_font_timB24_tr\n#define u8g_font_timB24n              u8g2_font_timB24_tn\n#define u8g_font_timR08               u8g2_font_timR08_tf\n#define u8g_font_timR08r              u8g2_font_timR08_tr\n#define u8g_font_timR10               u8g2_font_timR10_tf\n#define u8g_font_timR10r              u8g2_font_timR10_tr\n#define u8g_font_timR12               u8g2_font_timR12_tf\n#define u8g_font_timR12r              u8g2_font_timR12_tr\n#define u8g_font_timR14               u8g2_font_timR14_tf\n#define u8g_font_timR14r              u8g2_font_timR14_tr\n#define u8g_font_timR18               u8g2_font_timR18_tf\n#define u8g_font_timR18r              u8g2_font_timR18_tr\n#define u8g_font_timR24               u8g2_font_timR24_tf\n#define u8g_font_timR24r              u8g2_font_timR24_tr\n#define u8g_font_timR24n              u8g2_font_timR24_tn\n#define u8g_font_p01type              u8g2_font_p01type_tf\n#define u8g_font_p01typer             u8g2_font_p01type_tr\n#define u8g_font_lucasfont_alternate  u8g2_font_lucasfont_alternate_tf\n#define u8g_font_lucasfont_alternater u8g2_font_lucasfont_alternate_tr\n#define u8g_font_chikita              u8g2_font_chikita_tf\n#define u8g_font_chikitar             u8g2_font_chikita_tr\n#define u8g_font_pixelle_micro        u8g2_font_pixelle_micro_tf\n#define u8g_font_pixelle_micror       u8g2_font_pixelle_micro_tr\n#define u8g_font_trixel_square        u8g2_font_trixel_square_tf\n#define u8g_font_trixel_squarer       u8g2_font_trixel_square_tr\n#define u8g_font_robot_de_niro        u8g2_font_robot_de_niro_tf\n#define u8g_font_robot_de_niror       u8g2_font_robot_de_niro_tr\n#define u8g_font_baby                 u8g2_font_baby_tf\n#define u8g_font_babyr                u8g2_font_baby_tr\n#define u8g_font_blipfest_07          u8g2_font_blipfest_07_tr\n#define u8g_font_blipfest_07r         u8g2_font_blipfest_07_tr\n#define u8g_font_blipfest_07n         u8g2_font_blipfest_07_tn\n#define u8g_font_profont10            u8g2_font_profont10_tf\n#define u8g_font_profont10r           u8g2_font_profont10_tr\n#define u8g_font_profont11            u8g2_font_profont11_tf\n#define u8g_font_profont11r           u8g2_font_profont11_tr\n#define u8g_font_profont12            u8g2_font_profont12_tf\n#define u8g_font_profont12r           u8g2_font_profont12_tr\n#define u8g_font_profont15            u8g2_font_profont15_tf\n#define u8g_font_profont15r           u8g2_font_profont15_tr\n#define u8g_font_profont17            u8g2_font_profont17_tf\n#define u8g_font_profont17r           u8g2_font_profont17_tr\n#define u8g_font_profont22            u8g2_font_profont22_tf\n#define u8g_font_profont22r           u8g2_font_profont22_tr\n#define u8g_font_profont29            u8g2_font_profont29_tf\n#define u8g_font_profont29r           u8g2_font_profont29_tr\n\n/*==========================================*/\n/* C++ compatible */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/u8g2/u8g2_bitmap.c",
    "content": "/*\n\n  u8g2_bitmap.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\nvoid u8g2_SetBitmapMode(u8g2_t* u8g2, uint8_t is_transparent) {\n    u8g2->bitmap_transparency = is_transparent;\n}\n\n/*\n  x,y \tPosition on the display\n  len\t\tLength of bitmap line in pixel. Note: This differs from u8glib which had a bytecount here.\n  b\t\tPointer to the bitmap line.\n  Only draw pixels which are set.\n*/\n\nvoid u8g2_DrawHorizontalBitmap(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    const uint8_t* b) {\n    uint8_t mask;\n    uint8_t color = u8g2->draw_color;\n    uint8_t ncolor = (color == 0 ? 1 : 0);\n\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + len, y + 1) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    mask = 128;\n    while(len > 0) {\n        if(*b & mask) {\n            u8g2->draw_color = color;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        } else if(u8g2->bitmap_transparency == 0) {\n            u8g2->draw_color = ncolor;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        }\n\n        x++;\n        mask >>= 1;\n        if(mask == 0) {\n            mask = 128;\n            b++;\n        }\n        len--;\n    }\n    u8g2->draw_color = color;\n}\n\n/* u8glib compatible bitmap draw function */\nvoid u8g2_DrawBitmap(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t cnt,\n    u8g2_uint_t h,\n    const uint8_t* bitmap) {\n    u8g2_uint_t w;\n    w = cnt;\n    w *= 8;\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    while(h > 0) {\n        u8g2_DrawHorizontalBitmap(u8g2, x, y, w, bitmap);\n        bitmap += cnt;\n        y++;\n        h--;\n    }\n}\n\nvoid u8g2_DrawHXBM(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, const uint8_t* b) {\n    uint8_t mask;\n    uint8_t color = u8g2->draw_color;\n    uint8_t ncolor = (color == 0 ? 1 : 0);\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + len, y + 1) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    mask = 1;\n    while(len > 0) {\n        if(*b & mask) {\n            u8g2->draw_color = color;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        } else if(u8g2->bitmap_transparency == 0) {\n            u8g2->draw_color = ncolor;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        }\n        x++;\n        mask <<= 1;\n        if(mask == 0) {\n            mask = 1;\n            b++;\n        }\n        len--;\n    }\n    u8g2->draw_color = color;\n}\n\nvoid u8g2_DrawXBM(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    const uint8_t* bitmap) {\n    u8g2_uint_t blen;\n    blen = w;\n    blen += 7;\n    blen >>= 3;\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    while(h > 0) {\n        u8g2_DrawHXBM(u8g2, x, y, w, bitmap);\n        bitmap += blen;\n        y++;\n        h--;\n    }\n}\n\nvoid u8g2_DrawHXBMP(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, const uint8_t* b) {\n    uint8_t mask;\n    uint8_t color = u8g2->draw_color;\n    uint8_t ncolor = (color == 0 ? 1 : 0);\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + len, y + 1) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    mask = 1;\n    while(len > 0) {\n        if(u8x8_pgm_read(b) & mask) {\n            u8g2->draw_color = color;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        } else if(u8g2->bitmap_transparency == 0) {\n            u8g2->draw_color = ncolor;\n            u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n        }\n\n        x++;\n        mask <<= 1;\n        if(mask == 0) {\n            mask = 1;\n            b++;\n        }\n        len--;\n    }\n    u8g2->draw_color = color;\n}\n\nvoid u8g2_DrawXBMP(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    const uint8_t* bitmap) {\n    u8g2_uint_t blen;\n    blen = w;\n    blen += 7;\n    blen >>= 3;\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    while(h > 0) {\n        u8g2_DrawHXBMP(u8g2, x, y, w, bitmap);\n        bitmap += blen;\n        y++;\n        h--;\n    }\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_box.c",
    "content": "/*\n\n  u8g2_box.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\n/*\n  draw a filled box\n  restriction: does not work for w = 0 or h = 0\n*/\nvoid u8g2_DrawBox(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h) {\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n    while(h != 0) {\n        u8g2_DrawHVLine(u8g2, x, y, w, 0);\n        y++;\n        h--;\n    }\n}\n\n/*\n  draw a frame (empty box)\n  restriction: does not work for w = 0 or h = 0\n  ToDo:\n    pixel in the corners are drawn twice. This could be optimized.\n*/\nvoid u8g2_DrawFrame(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h) {\n    u8g2_uint_t xtmp = x;\n\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    u8g2_DrawHVLine(u8g2, x, y, w, 0);\n    u8g2_DrawHVLine(u8g2, x, y, h, 1);\n    x += w;\n    x--;\n    u8g2_DrawHVLine(u8g2, x, y, h, 1);\n    y += h;\n    y--;\n    u8g2_DrawHVLine(u8g2, xtmp, y, w, 0);\n}\n\nvoid u8g2_DrawRBox(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    u8g2_uint_t r) {\n    u8g2_uint_t xl, yu;\n    u8g2_uint_t yl, xr;\n\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    xl = x;\n    xl += r;\n    yu = y;\n    yu += r;\n\n    xr = x;\n    xr += w;\n    xr -= r;\n    xr -= 1;\n\n    yl = y;\n    yl += h;\n    yl -= r;\n    yl -= 1;\n\n    u8g2_DrawDisc(u8g2, xl, yu, r, U8G2_DRAW_UPPER_LEFT);\n    u8g2_DrawDisc(u8g2, xr, yu, r, U8G2_DRAW_UPPER_RIGHT);\n    u8g2_DrawDisc(u8g2, xl, yl, r, U8G2_DRAW_LOWER_LEFT);\n    u8g2_DrawDisc(u8g2, xr, yl, r, U8G2_DRAW_LOWER_RIGHT);\n\n    {\n        u8g2_uint_t ww, hh;\n\n        ww = w;\n        ww -= r;\n        ww -= r;\n        xl++;\n        yu++;\n\n        if(ww >= 3) {\n            ww -= 2;\n            u8g2_DrawBox(u8g2, xl, y, ww, r + 1);\n            u8g2_DrawBox(u8g2, xl, yl, ww, r + 1);\n        }\n\n        hh = h;\n        hh -= r;\n        hh -= r;\n        //h--;\n        if(hh >= 3) {\n            hh -= 2;\n            u8g2_DrawBox(u8g2, x, yu, w, hh);\n        }\n    }\n}\n\nvoid u8g2_DrawRFrame(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t w,\n    u8g2_uint_t h,\n    u8g2_uint_t r) {\n    u8g2_uint_t xl, yu;\n\n#ifdef U8G2_WITH_INTERSECTION\n    if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;\n#endif /* U8G2_WITH_INTERSECTION */\n\n    xl = x;\n    xl += r;\n    yu = y;\n    yu += r;\n\n    {\n        u8g2_uint_t yl, xr;\n\n        xr = x;\n        xr += w;\n        xr -= r;\n        xr -= 1;\n\n        yl = y;\n        yl += h;\n        yl -= r;\n        yl -= 1;\n\n        u8g2_DrawCircle(u8g2, xl, yu, r, U8G2_DRAW_UPPER_LEFT);\n        u8g2_DrawCircle(u8g2, xr, yu, r, U8G2_DRAW_UPPER_RIGHT);\n        u8g2_DrawCircle(u8g2, xl, yl, r, U8G2_DRAW_LOWER_LEFT);\n        u8g2_DrawCircle(u8g2, xr, yl, r, U8G2_DRAW_LOWER_RIGHT);\n    }\n\n    {\n        u8g2_uint_t ww, hh;\n\n        ww = w;\n        ww -= r;\n        ww -= r;\n        hh = h;\n        hh -= r;\n        hh -= r;\n\n        xl++;\n        yu++;\n\n        if(ww >= 3) {\n            ww -= 2;\n            h--;\n            u8g2_DrawHLine(u8g2, xl, y, ww);\n            u8g2_DrawHLine(u8g2, xl, y + h, ww);\n        }\n\n        if(hh >= 3) {\n            hh -= 2;\n            w--;\n            u8g2_DrawVLine(u8g2, x, yu, hh);\n            u8g2_DrawVLine(u8g2, x + w, yu, hh);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_buffer.c",
    "content": "/* \n\n  u8g2_buffer.c \n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n#include <string.h>\n\n/*============================================*/\nvoid u8g2_ClearBuffer(u8g2_t* u8g2) {\n    size_t cnt;\n    cnt = u8g2_GetU8x8(u8g2)->display_info->tile_width;\n    cnt *= u8g2->tile_buf_height;\n    cnt *= 8;\n    memset(u8g2->tile_buf_ptr, 0, cnt);\n}\n\n/*============================================*/\n\nstatic void u8g2_send_tile_row(u8g2_t* u8g2, uint8_t src_tile_row, uint8_t dest_tile_row) {\n    uint8_t* ptr;\n    uint16_t offset;\n    uint8_t w;\n\n    w = u8g2_GetU8x8(u8g2)->display_info->tile_width;\n    offset = src_tile_row;\n    ptr = u8g2->tile_buf_ptr;\n    offset *= w;\n    offset *= 8;\n    ptr += offset;\n    u8x8_DrawTile(u8g2_GetU8x8(u8g2), 0, dest_tile_row, w, ptr);\n}\n\n/* \n  write the buffer to the display RAM. \n  For most displays, this will make the content visible to the user.\n  Some displays (like the SSD1606) require a u8x8_RefreshDisplay()\n*/\nstatic void u8g2_send_buffer(u8g2_t* u8g2) U8X8_NOINLINE;\nstatic void u8g2_send_buffer(u8g2_t* u8g2) {\n    uint8_t src_row;\n    uint8_t src_max;\n    uint8_t dest_row;\n    uint8_t dest_max;\n\n    src_row = 0;\n    src_max = u8g2->tile_buf_height;\n    dest_row = u8g2->tile_curr_row;\n    dest_max = u8g2_GetU8x8(u8g2)->display_info->tile_height;\n\n    do {\n        u8g2_send_tile_row(u8g2, src_row, dest_row);\n        src_row++;\n        dest_row++;\n    } while(src_row < src_max && dest_row < dest_max);\n}\n\n/* same as u8g2_send_buffer but also send the DISPLAY_REFRESH message (used by SSD1606) */\nvoid u8g2_SendBuffer(u8g2_t* u8g2) {\n    u8g2_send_buffer(u8g2);\n    u8x8_RefreshDisplay(u8g2_GetU8x8(u8g2));\n}\n\n/*============================================*/\nvoid u8g2_SetBufferCurrTileRow(u8g2_t* u8g2, uint8_t row) {\n    u8g2->tile_curr_row = row;\n    u8g2->cb->update_dimension(u8g2);\n    u8g2->cb->update_page_win(u8g2);\n}\n\nvoid u8g2_FirstPage(u8g2_t* u8g2) {\n    if(u8g2->is_auto_page_clear) {\n        u8g2_ClearBuffer(u8g2);\n    }\n    u8g2_SetBufferCurrTileRow(u8g2, 0);\n}\n\nuint8_t u8g2_NextPage(u8g2_t* u8g2) {\n    uint8_t row;\n    u8g2_send_buffer(u8g2);\n    row = u8g2->tile_curr_row;\n    row += u8g2->tile_buf_height;\n    if(row >= u8g2_GetU8x8(u8g2)->display_info->tile_height) {\n        u8x8_RefreshDisplay(u8g2_GetU8x8(u8g2));\n        return 0;\n    }\n    if(u8g2->is_auto_page_clear) {\n        u8g2_ClearBuffer(u8g2);\n    }\n    u8g2_SetBufferCurrTileRow(u8g2, row);\n    return 1;\n}\n\n/*============================================*/\n/*\n  Description:\n    Update a sub area of the display, given by tile position, width and height.\n    The arguments are \"tile\" coordinates. Any u8g2 rotation is ignored.\n    This procedure only checks whether full buffer mode is active.\n    There is no error checking for the arguments: It is the responsibility of the\n    user to ensure, that the provided arguments are correct.\n\n  Limitations:\n    - Only available in full buffer mode (will not do anything in page mode)\n    - Tile positions and sizes (pixel position divided by 8)\n    - Any display rotation/mirror is ignored\n    - Only works with displays, which support U8x8 API\n    - Will not send the e-paper refresh message (will probably not work with e-paper devices)\n*/\nvoid u8g2_UpdateDisplayArea(u8g2_t* u8g2, uint8_t tx, uint8_t ty, uint8_t tw, uint8_t th) {\n    uint16_t page_size;\n    uint8_t* ptr;\n\n    /* check, whether we are in full buffer mode */\n    if(u8g2->tile_buf_height != u8g2_GetU8x8(u8g2)->display_info->tile_height)\n        return; /* not in full buffer mode, do nothing */\n\n    page_size = u8g2->pixel_buf_width; /* 8*u8g2->u8g2_GetU8x8(u8g2)->display_info->tile_width */\n\n    ptr = u8g2_GetBufferPtr(u8g2);\n    ptr += tx * 8;\n    ptr += page_size * ty;\n\n    while(th > 0) {\n        u8x8_DrawTile(u8g2_GetU8x8(u8g2), tx, ty, tw, ptr);\n        ptr += page_size;\n        ty++;\n        th--;\n    }\n}\n\n/* same as sendBuffer, but does not send the ePaper refresh message */\nvoid u8g2_UpdateDisplay(u8g2_t* u8g2) {\n    u8g2_send_buffer(u8g2);\n}\n\n/*============================================*/\n\n/* vertical_top memory architecture */\nvoid u8g2_WriteBufferPBM(u8g2_t* u8g2, void (*out)(const char* s)) {\n    u8x8_capture_write_pbm_pre(u8g2_GetBufferTileWidth(u8g2), u8g2_GetBufferTileHeight(u8g2), out);\n    u8x8_capture_write_pbm_buffer(\n        u8g2_GetBufferPtr(u8g2),\n        u8g2_GetBufferTileWidth(u8g2),\n        u8g2_GetBufferTileHeight(u8g2),\n        u8x8_capture_get_pixel_1,\n        out);\n}\n\nvoid u8g2_WriteBufferXBM(u8g2_t* u8g2, void (*out)(const char* s)) {\n    u8x8_capture_write_xbm_pre(u8g2_GetBufferTileWidth(u8g2), u8g2_GetBufferTileHeight(u8g2), out);\n    u8x8_capture_write_xbm_buffer(\n        u8g2_GetBufferPtr(u8g2),\n        u8g2_GetBufferTileWidth(u8g2),\n        u8g2_GetBufferTileHeight(u8g2),\n        u8x8_capture_get_pixel_1,\n        out);\n}\n\n/* horizontal right memory architecture */\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nvoid u8g2_WriteBufferPBM2(u8g2_t* u8g2, void (*out)(const char* s)) {\n    u8x8_capture_write_pbm_pre(u8g2_GetBufferTileWidth(u8g2), u8g2_GetBufferTileHeight(u8g2), out);\n    u8x8_capture_write_pbm_buffer(\n        u8g2_GetBufferPtr(u8g2),\n        u8g2_GetBufferTileWidth(u8g2),\n        u8g2_GetBufferTileHeight(u8g2),\n        u8x8_capture_get_pixel_2,\n        out);\n}\n\nvoid u8g2_WriteBufferXBM2(u8g2_t* u8g2, void (*out)(const char* s)) {\n    u8x8_capture_write_xbm_pre(u8g2_GetBufferTileWidth(u8g2), u8g2_GetBufferTileHeight(u8g2), out);\n    u8x8_capture_write_xbm_buffer(\n        u8g2_GetBufferPtr(u8g2),\n        u8g2_GetBufferTileWidth(u8g2),\n        u8g2_GetBufferTileHeight(u8g2),\n        u8x8_capture_get_pixel_2,\n        out);\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_circle.c",
    "content": "/*\n\n  u8g2_circle.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\n/*==============================================*/\n/* Circle */\n\nstatic void u8g2_draw_circle_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) U8G2_NOINLINE;\n\nstatic void u8g2_draw_circle_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) {\n    /* upper right */\n    if(option & U8G2_DRAW_UPPER_RIGHT) {\n        u8g2_DrawPixel(u8g2, x0 + x, y0 - y);\n        u8g2_DrawPixel(u8g2, x0 + y, y0 - x);\n    }\n\n    /* upper left */\n    if(option & U8G2_DRAW_UPPER_LEFT) {\n        u8g2_DrawPixel(u8g2, x0 - x, y0 - y);\n        u8g2_DrawPixel(u8g2, x0 - y, y0 - x);\n    }\n\n    /* lower right */\n    if(option & U8G2_DRAW_LOWER_RIGHT) {\n        u8g2_DrawPixel(u8g2, x0 + x, y0 + y);\n        u8g2_DrawPixel(u8g2, x0 + y, y0 + x);\n    }\n\n    /* lower left */\n    if(option & U8G2_DRAW_LOWER_LEFT) {\n        u8g2_DrawPixel(u8g2, x0 - x, y0 + y);\n        u8g2_DrawPixel(u8g2, x0 - y, y0 + x);\n    }\n}\n\nstatic void\n    u8g2_draw_circle(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option) {\n    u8g2_int_t f;\n    u8g2_int_t ddF_x;\n    u8g2_int_t ddF_y;\n    u8g2_uint_t x;\n    u8g2_uint_t y;\n\n    f = 1;\n    f -= rad;\n    ddF_x = 1;\n    ddF_y = 0;\n    ddF_y -= rad;\n    ddF_y *= 2;\n    x = 0;\n    y = rad;\n\n    u8g2_draw_circle_section(u8g2, x, y, x0, y0, option);\n\n    while(x < y) {\n        if(f >= 0) {\n            y--;\n            ddF_y += 2;\n            f += ddF_y;\n        }\n        x++;\n        ddF_x += 2;\n        f += ddF_x;\n\n        u8g2_draw_circle_section(u8g2, x, y, x0, y0, option);\n    }\n}\n\nvoid u8g2_DrawCircle(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option) {\n    /* check for bounding box */\n#ifdef U8G2_WITH_INTERSECTION\n    {\n        if(u8g2_IsIntersection(u8g2, x0 - rad, y0 - rad, x0 + rad + 1, y0 + rad + 1) == 0) return;\n    }\n#endif /* U8G2_WITH_INTERSECTION */\n\n    /* draw circle */\n    u8g2_draw_circle(u8g2, x0, y0, rad, option);\n}\n\n/*==============================================*/\n/* Disk */\n\nstatic void u8g2_draw_disc_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) U8G2_NOINLINE;\n\nstatic void u8g2_draw_disc_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) {\n    /* upper right */\n    if(option & U8G2_DRAW_UPPER_RIGHT) {\n        u8g2_DrawVLine(u8g2, x0 + x, y0 - y, y + 1);\n        u8g2_DrawVLine(u8g2, x0 + y, y0 - x, x + 1);\n    }\n\n    /* upper left */\n    if(option & U8G2_DRAW_UPPER_LEFT) {\n        u8g2_DrawVLine(u8g2, x0 - x, y0 - y, y + 1);\n        u8g2_DrawVLine(u8g2, x0 - y, y0 - x, x + 1);\n    }\n\n    /* lower right */\n    if(option & U8G2_DRAW_LOWER_RIGHT) {\n        u8g2_DrawVLine(u8g2, x0 + x, y0, y + 1);\n        u8g2_DrawVLine(u8g2, x0 + y, y0, x + 1);\n    }\n\n    /* lower left */\n    if(option & U8G2_DRAW_LOWER_LEFT) {\n        u8g2_DrawVLine(u8g2, x0 - x, y0, y + 1);\n        u8g2_DrawVLine(u8g2, x0 - y, y0, x + 1);\n    }\n}\n\nstatic void\n    u8g2_draw_disc(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option) {\n    u8g2_int_t f;\n    u8g2_int_t ddF_x;\n    u8g2_int_t ddF_y;\n    u8g2_uint_t x;\n    u8g2_uint_t y;\n\n    f = 1;\n    f -= rad;\n    ddF_x = 1;\n    ddF_y = 0;\n    ddF_y -= rad;\n    ddF_y *= 2;\n    x = 0;\n    y = rad;\n\n    u8g2_draw_disc_section(u8g2, x, y, x0, y0, option);\n\n    while(x < y) {\n        if(f >= 0) {\n            y--;\n            ddF_y += 2;\n            f += ddF_y;\n        }\n        x++;\n        ddF_x += 2;\n        f += ddF_x;\n\n        u8g2_draw_disc_section(u8g2, x, y, x0, y0, option);\n    }\n}\n\nvoid u8g2_DrawDisc(u8g2_t* u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option) {\n    /* check for bounding box */\n#ifdef U8G2_WITH_INTERSECTION\n    {\n        if(u8g2_IsIntersection(u8g2, x0 - rad, y0 - rad, x0 + rad + 1, y0 + rad + 1) == 0) return;\n    }\n#endif /* U8G2_WITH_INTERSECTION */\n\n    /* draw disc */\n    u8g2_draw_disc(u8g2, x0, y0, rad, option);\n}\n\n/*==============================================*/\n/* Ellipse */\n\n/*\n  Source: \n    Foley, Computer Graphics, p 90\n*/\nstatic void u8g2_draw_ellipse_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) U8G2_NOINLINE;\nstatic void u8g2_draw_ellipse_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) {\n    /* upper right */\n    if(option & U8G2_DRAW_UPPER_RIGHT) {\n        u8g2_DrawPixel(u8g2, x0 + x, y0 - y);\n    }\n\n    /* upper left */\n    if(option & U8G2_DRAW_UPPER_LEFT) {\n        u8g2_DrawPixel(u8g2, x0 - x, y0 - y);\n    }\n\n    /* lower right */\n    if(option & U8G2_DRAW_LOWER_RIGHT) {\n        u8g2_DrawPixel(u8g2, x0 + x, y0 + y);\n    }\n\n    /* lower left */\n    if(option & U8G2_DRAW_LOWER_LEFT) {\n        u8g2_DrawPixel(u8g2, x0 - x, y0 + y);\n    }\n}\n\nstatic void u8g2_draw_ellipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option) {\n    u8g2_uint_t x, y;\n    u8g2_long_t xchg, ychg;\n    u8g2_long_t err;\n    u8g2_long_t rxrx2;\n    u8g2_long_t ryry2;\n    u8g2_long_t stopx, stopy;\n\n    rxrx2 = rx;\n    rxrx2 *= rx;\n    rxrx2 *= 2;\n\n    ryry2 = ry;\n    ryry2 *= ry;\n    ryry2 *= 2;\n\n    x = rx;\n    y = 0;\n\n    xchg = 1;\n    xchg -= rx;\n    xchg -= rx;\n    xchg *= ry;\n    xchg *= ry;\n\n    ychg = rx;\n    ychg *= rx;\n\n    err = 0;\n\n    stopx = ryry2;\n    stopx *= rx;\n    stopy = 0;\n\n    while(stopx >= stopy) {\n        u8g2_draw_ellipse_section(u8g2, x, y, x0, y0, option);\n        y++;\n        stopy += rxrx2;\n        err += ychg;\n        ychg += rxrx2;\n        if(2 * err + xchg > 0) {\n            x--;\n            stopx -= ryry2;\n            err += xchg;\n            xchg += ryry2;\n        }\n    }\n\n    x = 0;\n    y = ry;\n\n    xchg = ry;\n    xchg *= ry;\n\n    ychg = 1;\n    ychg -= ry;\n    ychg -= ry;\n    ychg *= rx;\n    ychg *= rx;\n\n    err = 0;\n\n    stopx = 0;\n\n    stopy = rxrx2;\n    stopy *= ry;\n\n    while(stopx <= stopy) {\n        u8g2_draw_ellipse_section(u8g2, x, y, x0, y0, option);\n        x++;\n        stopx += ryry2;\n        err += xchg;\n        xchg += ryry2;\n        if(2 * err + ychg > 0) {\n            y--;\n            stopy -= rxrx2;\n            err += ychg;\n            ychg += rxrx2;\n        }\n    }\n}\n\nvoid u8g2_DrawEllipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option) {\n    /* check for bounding box */\n#ifdef U8G2_WITH_INTERSECTION\n    {\n        if(u8g2_IsIntersection(u8g2, x0 - rx, y0 - ry, x0 + rx + 1, y0 + ry + 1) == 0) return;\n    }\n#endif /* U8G2_WITH_INTERSECTION */\n\n    u8g2_draw_ellipse(u8g2, x0, y0, rx, ry, option);\n}\n\n/*==============================================*/\n/* Filled Ellipse */\n\nstatic void u8g2_draw_filled_ellipse_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) U8G2_NOINLINE;\nstatic void u8g2_draw_filled_ellipse_section(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    uint8_t option) {\n    /* upper right */\n    if(option & U8G2_DRAW_UPPER_RIGHT) {\n        u8g2_DrawVLine(u8g2, x0 + x, y0 - y, y + 1);\n    }\n\n    /* upper left */\n    if(option & U8G2_DRAW_UPPER_LEFT) {\n        u8g2_DrawVLine(u8g2, x0 - x, y0 - y, y + 1);\n    }\n\n    /* lower right */\n    if(option & U8G2_DRAW_LOWER_RIGHT) {\n        u8g2_DrawVLine(u8g2, x0 + x, y0, y + 1);\n    }\n\n    /* lower left */\n    if(option & U8G2_DRAW_LOWER_LEFT) {\n        u8g2_DrawVLine(u8g2, x0 - x, y0, y + 1);\n    }\n}\n\nstatic void u8g2_draw_filled_ellipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option) {\n    u8g2_uint_t x, y;\n    u8g2_long_t xchg, ychg;\n    u8g2_long_t err;\n    u8g2_long_t rxrx2;\n    u8g2_long_t ryry2;\n    u8g2_long_t stopx, stopy;\n\n    rxrx2 = rx;\n    rxrx2 *= rx;\n    rxrx2 *= 2;\n\n    ryry2 = ry;\n    ryry2 *= ry;\n    ryry2 *= 2;\n\n    x = rx;\n    y = 0;\n\n    xchg = 1;\n    xchg -= rx;\n    xchg -= rx;\n    xchg *= ry;\n    xchg *= ry;\n\n    ychg = rx;\n    ychg *= rx;\n\n    err = 0;\n\n    stopx = ryry2;\n    stopx *= rx;\n    stopy = 0;\n\n    while(stopx >= stopy) {\n        u8g2_draw_filled_ellipse_section(u8g2, x, y, x0, y0, option);\n        y++;\n        stopy += rxrx2;\n        err += ychg;\n        ychg += rxrx2;\n        if(2 * err + xchg > 0) {\n            x--;\n            stopx -= ryry2;\n            err += xchg;\n            xchg += ryry2;\n        }\n    }\n\n    x = 0;\n    y = ry;\n\n    xchg = ry;\n    xchg *= ry;\n\n    ychg = 1;\n    ychg -= ry;\n    ychg -= ry;\n    ychg *= rx;\n    ychg *= rx;\n\n    err = 0;\n\n    stopx = 0;\n\n    stopy = rxrx2;\n    stopy *= ry;\n\n    while(stopx <= stopy) {\n        u8g2_draw_filled_ellipse_section(u8g2, x, y, x0, y0, option);\n        x++;\n        stopx += ryry2;\n        err += xchg;\n        xchg += ryry2;\n        if(2 * err + ychg > 0) {\n            y--;\n            stopy -= rxrx2;\n            err += ychg;\n            ychg += rxrx2;\n        }\n    }\n}\n\nvoid u8g2_DrawFilledEllipse(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t rx,\n    u8g2_uint_t ry,\n    uint8_t option) {\n    /* check for bounding box */\n#ifdef U8G2_WITH_INTERSECTION\n    {\n        if(u8g2_IsIntersection(u8g2, x0 - rx, y0 - ry, x0 + rx + 1, y0 + ry + 1) == 0) return;\n    }\n#endif /* U8G2_WITH_INTERSECTION */\n\n    u8g2_draw_filled_ellipse(u8g2, x0, y0, rx, ry, option);\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_d_memory.c",
    "content": "/* u8g2_d_memory.c */\n/* generated code, codebuild, u8g2 project */\n\n#include \"u8g2.h\"\n\nuint8_t* u8g2_m_16_4_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_4_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_4_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 4;\n    return 0;\n#else\n    static uint8_t buf[512];\n    *page_cnt = 4;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[1024];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_9_5_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[72];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_9_5_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[144];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_9_5_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 5;\n    return 0;\n#else\n    static uint8_t buf[360];\n    *page_cnt = 5;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_4_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_4_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_4_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 4;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 4;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[1024];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_12_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[96];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_12_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_12_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 12;\n    return 0;\n#else\n    static uint8_t buf[1152];\n    *page_cnt = 12;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[2048];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_20_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[160];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_20_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[320];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_20_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 20;\n    return 0;\n#else\n    static uint8_t buf[3200];\n    *page_cnt = 20;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[512];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[2048];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_6_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_6_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_6_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 6;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 6;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_6_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[48];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_6_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[96];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_6_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_2_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[96];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_2_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_2_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_12_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_12_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_16_12_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 12;\n    return 0;\n#else\n    static uint8_t buf[1536];\n    *page_cnt = 12;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_4_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_4_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[512];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_4_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 4;\n    return 0;\n#else\n    static uint8_t buf[1024];\n    *page_cnt = 4;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[96];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[768];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_4_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_4_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_4_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 4;\n    return 0;\n#else\n    static uint8_t buf[768];\n    *page_cnt = 4;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_50_30_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[400];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_50_30_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[800];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_50_30_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 30;\n    return 0;\n#else\n    static uint8_t buf[12000];\n    *page_cnt = 30;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_18_21_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[144];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_18_21_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[288];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_18_21_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 21;\n    return 0;\n#else\n    static uint8_t buf[3024];\n    *page_cnt = 21;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_13_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[104];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_13_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[208];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_13_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[832];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_11_6_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[88];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_11_6_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[176];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_11_6_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 6;\n    return 0;\n#else\n    static uint8_t buf[528];\n    *page_cnt = 6;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_9_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[96];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_9_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_12_9_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 9;\n    return 0;\n#else\n    static uint8_t buf[864];\n    *page_cnt = 9;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[1536];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[240];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[480];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[1920];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_15_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[240];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_15_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[480];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_15_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 15;\n    return 0;\n#else\n    static uint8_t buf[3600];\n    *page_cnt = 15;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[240];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[480];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[3840];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[160];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[320];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[2560];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_13_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[160];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_13_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[320];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_13_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 13;\n    return 0;\n#else\n    static uint8_t buf[2080];\n    *page_cnt = 13;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_20_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[240];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_20_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[480];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_30_20_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 20;\n    return 0;\n#else\n    static uint8_t buf[4800];\n    *page_cnt = 20;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_40_30_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[320];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_40_30_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[640];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_40_30_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 30;\n    return 0;\n#else\n    static uint8_t buf[9600];\n    *page_cnt = 30;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_4_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[136];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_4_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[272];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_4_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 4;\n    return 0;\n#else\n    static uint8_t buf[544];\n    *page_cnt = 4;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_8_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[136];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_8_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[272];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_17_8_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 8;\n    return 0;\n#else\n    static uint8_t buf[1088];\n    *page_cnt = 8;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_17_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_17_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[768];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_17_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 17;\n    return 0;\n#else\n    static uint8_t buf[6528];\n    *page_cnt = 17;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[512];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[4096];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_20_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[256];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_20_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[512];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_32_20_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 20;\n    return 0;\n#else\n    static uint8_t buf[5120];\n    *page_cnt = 20;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_13_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[176];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_13_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[352];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_13_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 13;\n    return 0;\n#else\n    static uint8_t buf[2288];\n    *page_cnt = 13;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_12_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[192];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_12_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_24_12_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 12;\n    return 0;\n#else\n    static uint8_t buf[2304];\n    *page_cnt = 12;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_10_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[160];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_10_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[320];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_20_10_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 10;\n    return 0;\n#else\n    static uint8_t buf[1600];\n    *page_cnt = 10;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_9_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[176];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_9_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[352];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_22_9_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 9;\n    return 0;\n#else\n    static uint8_t buf[1584];\n    *page_cnt = 9;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_25_25_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[200];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_25_25_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[400];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_25_25_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 25;\n    return 0;\n#else\n    static uint8_t buf[5000];\n    *page_cnt = 25;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_37_16_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[296];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_37_16_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[592];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_37_16_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 16;\n    return 0;\n#else\n    static uint8_t buf[4736];\n    *page_cnt = 16;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_1_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_1_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[128];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_8_1_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_4_1_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[32];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_4_1_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[64];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_4_1_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[32];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_1_1_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[8];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_1_1_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[16];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_1_1_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[8];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_30_1(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 1;\n    return 0;\n#else\n    static uint8_t buf[384];\n    *page_cnt = 1;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_30_2(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 2;\n    return 0;\n#else\n    static uint8_t buf[768];\n    *page_cnt = 2;\n    return buf;\n#endif\n}\nuint8_t* u8g2_m_48_30_f(uint8_t* page_cnt) {\n#ifdef U8G2_USE_DYNAMIC_ALLOC\n    *page_cnt = 30;\n    return 0;\n#else\n    static uint8_t buf[11520];\n    *page_cnt = 30;\n    return buf;\n#endif\n}\n/* end of generated code */\n"
  },
  {
    "path": "lib/u8g2/u8g2_font.c",
    "content": "/*\n\n  u8g2_font.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\n/* size of the font data structure, there is no struct or class... */\n/* this is the size for the new font format */\n#define U8G2_FONT_DATA_STRUCT_SIZE 23\n\n/*\n  font data:\n\n  offset\tbytes\tdescription\n  0\t\t1\t\tglyph_cnt\t\tnumber of glyphs\n  1\t\t1\t\tbbx_mode\t0: proportional, 1: common height, 2: monospace, 3: multiple of 8\n  2\t\t1\t\tbits_per_0\tglyph rle parameter\n  3\t\t1\t\tbits_per_1\tglyph rle parameter\n\n  4\t\t1\t\tbits_per_char_width\t\tglyph rle parameter\n  5\t\t1\t\tbits_per_char_height\tglyph rle parameter\n  6\t\t1\t\tbits_per_char_x\t\tglyph rle parameter\n  7\t\t1\t\tbits_per_char_y\t\tglyph rle parameter\n  8\t\t1\t\tbits_per_delta_x\t\tglyph rle parameter\n\n  9\t\t1\t\tmax_char_width\n  10\t\t1\t\tmax_char_height\n  11\t\t1\t\tx offset\n  12\t\t1\t\ty offset (descent)\n  \n  13\t\t1\t\tascent (capital A)\n  14\t\t1\t\tdescent (lower g)\n  15\t\t1\t\tascent '('\n  16\t\t1\t\tdescent ')'\n  \n  17\t\t1\t\tstart pos 'A' high byte\n  18\t\t1\t\tstart pos 'A' low byte\n\n  19\t\t1\t\tstart pos 'a' high byte\n  20\t\t1\t\tstart pos 'a' low byte\n\n  21\t\t1\t\tstart pos unicode high byte\n  22\t\t1\t\tstart pos unicode low byte\n\n  Font build mode, 0: proportional, 1: common height, 2: monospace, 3: multiple of 8\n\n  Font build mode 0:\t\t\n    - \"t\"\n    - Ref height mode: U8G2_FONT_HEIGHT_MODE_TEXT, U8G2_FONT_HEIGHT_MODE_XTEXT or U8G2_FONT_HEIGHT_MODE_ALL\n    - use in transparent mode only (does not look good in solid mode)\n    - most compact format\n    - different font heights possible\n    \n  Font build mode 1:\t\t\n    - \"h\"\n    - Ref height mode: U8G2_FONT_HEIGHT_MODE_ALL\n    - transparent or solid mode\n    - The height of the glyphs depend on the largest glyph in the font. This means font height depends on postfix \"r\", \"f\" and \"n\".\n\n*/\n\n/* use case: What is the width and the height of the minimal box into which string s fints? */\nvoid u8g2_font_GetStrSize(const void* font, const char* s, u8g2_uint_t* width, u8g2_uint_t* height);\nvoid u8g2_font_GetStrSizeP(\n    const void* font,\n    const char* s,\n    u8g2_uint_t* width,\n    u8g2_uint_t* height);\n\n/* use case: lower left edge of a minimal box is known, what is the correct x, y position for the string draw procedure */\nvoid u8g2_font_AdjustXYToDraw(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y);\nvoid u8g2_font_AdjustXYToDrawP(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y);\n\n/* use case: Baseline origin known, return minimal box */\nvoid u8g2_font_GetStrMinBox(\n    u8g2_t* u8g2,\n    const void* font,\n    const char* s,\n    u8g2_uint_t* x,\n    u8g2_uint_t* y,\n    u8g2_uint_t* width,\n    u8g2_uint_t* height);\n\n/* procedures */\n\n/*========================================================================*/\n/* low level byte and word access */\n\n/* removed NOINLINE, because it leads to smaller code, might also be faster */\n//static uint8_t u8g2_font_get_byte(const uint8_t *font, uint8_t offset) U8G2_NOINLINE;\nstatic uint8_t u8g2_font_get_byte(const uint8_t* font, uint8_t offset) {\n    font += offset;\n    return u8x8_pgm_read(font);\n}\n\nstatic uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) U8G2_NOINLINE;\nstatic uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) {\n    uint16_t pos;\n    font += offset;\n    pos = u8x8_pgm_read(font);\n    font++;\n    pos <<= 8;\n    pos += u8x8_pgm_read(font);\n    return pos;\n}\n\n/*========================================================================*/\n/* new font format */\nvoid u8g2_read_font_info(u8g2_font_info_t* font_info, const uint8_t* font) {\n    /* offset 0 */\n    font_info->glyph_cnt = u8g2_font_get_byte(font, 0);\n    font_info->bbx_mode = u8g2_font_get_byte(font, 1);\n    font_info->bits_per_0 = u8g2_font_get_byte(font, 2);\n    font_info->bits_per_1 = u8g2_font_get_byte(font, 3);\n\n    /* offset 4 */\n    font_info->bits_per_char_width = u8g2_font_get_byte(font, 4);\n    font_info->bits_per_char_height = u8g2_font_get_byte(font, 5);\n    font_info->bits_per_char_x = u8g2_font_get_byte(font, 6);\n    font_info->bits_per_char_y = u8g2_font_get_byte(font, 7);\n    font_info->bits_per_delta_x = u8g2_font_get_byte(font, 8);\n\n    /* offset 9 */\n    font_info->max_char_width = u8g2_font_get_byte(font, 9);\n    font_info->max_char_height = u8g2_font_get_byte(font, 10);\n    font_info->x_offset = u8g2_font_get_byte(font, 11);\n    font_info->y_offset = u8g2_font_get_byte(font, 12);\n\n    /* offset 13 */\n    font_info->ascent_A = u8g2_font_get_byte(font, 13);\n    font_info->descent_g = u8g2_font_get_byte(font, 14);\n    font_info->ascent_para = u8g2_font_get_byte(font, 15);\n    font_info->descent_para = u8g2_font_get_byte(font, 16);\n\n    /* offset 17 */\n    font_info->start_pos_upper_A = u8g2_font_get_word(font, 17);\n    font_info->start_pos_lower_a = u8g2_font_get_word(font, 19);\n\n    /* offset 21 */\n#ifdef U8G2_WITH_UNICODE\n    font_info->start_pos_unicode = u8g2_font_get_word(font, 21);\n#endif\n}\n\n/* calculate the overall length of the font, only used to create the picture for the google wiki */\nsize_t u8g2_GetFontSize(const uint8_t* font_arg) {\n    uint16_t e;\n    const uint8_t* font = font_arg;\n    font += U8G2_FONT_DATA_STRUCT_SIZE;\n\n    for(;;) {\n        if(u8x8_pgm_read(font + 1) == 0) break;\n        font += u8x8_pgm_read(font + 1);\n    }\n\n    /* continue with unicode section */\n    font += 2;\n\n    /* skip unicode lookup table */\n    font += u8g2_font_get_word(font, 0);\n\n    for(;;) {\n        e = u8x8_pgm_read(font);\n        e <<= 8;\n        e |= u8x8_pgm_read(font + 1);\n        if(e == 0) break;\n        font += u8x8_pgm_read(font + 2);\n    }\n\n    return (font - font_arg) + 2;\n}\n\n/*========================================================================*/\n/* u8g2 interface, font access */\n\nuint8_t u8g2_GetFontBBXWidth(u8g2_t* u8g2) {\n    return u8g2->font_info.max_char_width; /* new font info structure */\n}\n\nuint8_t u8g2_GetFontBBXHeight(u8g2_t* u8g2) {\n    return u8g2->font_info.max_char_height; /* new font info structure */\n}\n\nint8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) U8G2_NOINLINE;\nint8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) {\n    return u8g2->font_info.x_offset; /* new font info structure */\n}\n\nint8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) U8G2_NOINLINE;\nint8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) {\n    return u8g2->font_info.y_offset; /* new font info structure */\n}\n\nuint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) U8G2_NOINLINE;\nuint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) {\n    return u8g2->font_info.ascent_A; /* new font info structure */\n}\n\n/*========================================================================*/\n/* glyph handling */\n\n/* optimized */\nuint8_t u8g2_font_decode_get_unsigned_bits(u8g2_font_decode_t* f, uint8_t cnt) {\n    uint8_t val;\n    uint8_t bit_pos = f->decode_bit_pos;\n    uint8_t bit_pos_plus_cnt;\n\n    //val = *(f->decode_ptr);\n    val = u8x8_pgm_read(f->decode_ptr);\n\n    val >>= bit_pos;\n    bit_pos_plus_cnt = bit_pos;\n    bit_pos_plus_cnt += cnt;\n    if(bit_pos_plus_cnt >= 8) {\n        uint8_t s = 8;\n        s -= bit_pos;\n        f->decode_ptr++;\n        //val |= *(f->decode_ptr) << (8-bit_pos);\n        val |= u8x8_pgm_read(f->decode_ptr) << (s);\n        //bit_pos -= 8;\n        bit_pos_plus_cnt -= 8;\n    }\n    val &= (1U << cnt) - 1;\n    //bit_pos += cnt;\n\n    f->decode_bit_pos = bit_pos_plus_cnt;\n    return val;\n}\n\n/*\n    2 bit --> cnt = 2\n      -2,-1,0. 1\n\n    3 bit --> cnt = 3\n      -2,-1,0. 1\n      -4,-3,-2,-1,0,1,2,3\n\n      if ( x < 0 )\n\tr = bits(x-1)+1;\n    else\n\tr = bits(x)+1;\n\n*/\n/* optimized */\nint8_t u8g2_font_decode_get_signed_bits(u8g2_font_decode_t* f, uint8_t cnt) {\n    int8_t v, d;\n    v = (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt);\n    d = 1;\n    cnt--;\n    d <<= cnt;\n    v -= d;\n    return v;\n    //return (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt) - ((1<<cnt)>>1);\n}\n\n#ifdef U8G2_WITH_FONT_ROTATION\nu8g2_uint_t u8g2_add_vector_y(u8g2_uint_t dy, int8_t x, int8_t y, uint8_t dir) {\n    switch(dir) {\n    case 0:\n        dy += y;\n        break;\n    case 1:\n        dy += x;\n        break;\n    case 2:\n        dy -= y;\n        break;\n    default:\n        dy -= x;\n        break;\n    }\n    return dy;\n}\n\nu8g2_uint_t u8g2_add_vector_x(u8g2_uint_t dx, int8_t x, int8_t y, uint8_t dir) {\n    switch(dir) {\n    case 0:\n        dx += x;\n        break;\n    case 1:\n        dx -= y;\n        break;\n    case 2:\n        dx -= x;\n        break;\n    default:\n        dx += y;\n        break;\n    }\n    return dx;\n}\n\n/*\n// does not make sense, 50 bytes more required on avr\nvoid u8g2_add_vector(u8g2_uint_t *xp, u8g2_uint_t *yp, int8_t x, int8_t y, uint8_t dir)\n{\n  u8g2_uint_t x_ = *xp;\n  u8g2_uint_t y_ = *yp;\n  switch(dir)\n  {\n    case 0:\n      y_ += y;\n      x_ += x;\n      break;\n    case 1:\n      y_ += x;\n      x_ -= y;\n      break;\n    case 2:\n      y_ -= y;\n      x_ -= x;\n      break;\n    default:\n      y_ -= x;\n      x_ += y;\n      break;      \n  }\n  *xp = x_;\n  *yp = y_;\n}\n*/\n#endif\n\n/*\n  Description:\n    Draw a run-length area of the glyph. \"len\" can have any size and the line\n    length has to be wrapped at the glyph border.\n  Args:\n    len: \t\t\t\t\tLength of the line\n    is_foreground\t\t\tforeground/background?\n    u8g2->font_decode.target_x\t\tX position\n    u8g2->font_decode.target_y\t\tY position\n    u8g2->font_decode.is_transparent\tTransparent mode\n  Return:\n    -\n  Calls:\n    u8g2_Draw90Line()\n  Called by:\n    u8g2_font_decode_glyph()\n*/\n/* optimized */\nvoid u8g2_font_decode_len(u8g2_t* u8g2, uint8_t len, uint8_t is_foreground) {\n    uint8_t cnt; /* total number of remaining pixels, which have to be drawn */\n    uint8_t rem; /* remaining pixel to the right edge of the glyph */\n    uint8_t current; /* number of pixels, which need to be drawn for the draw procedure */\n    /* current is either equal to cnt or equal to rem */\n\n    /* local coordinates of the glyph */\n    uint8_t lx, ly;\n\n    /* target position on the screen */\n    u8g2_uint_t x, y;\n\n    u8g2_font_decode_t* decode = &(u8g2->font_decode);\n\n    cnt = len;\n\n    /* get the local position */\n    lx = decode->x;\n    ly = decode->y;\n\n    for(;;) {\n        /* calculate the number of pixel to the right edge of the glyph */\n        rem = decode->glyph_width;\n        rem -= lx;\n\n        /* calculate how many pixel to draw. This is either to the right edge */\n        /* or lesser, if not enough pixel are left */\n        current = rem;\n        if(cnt < rem) current = cnt;\n\n        /* now draw the line, but apply the rotation around the glyph target position */\n        //u8g2_font_decode_draw_pixel(u8g2, lx,ly,current, is_foreground);\n\n        /* get target position */\n        x = decode->target_x;\n        y = decode->target_y;\n\n        /* apply rotation */\n#ifdef U8G2_WITH_FONT_ROTATION\n\n        x = u8g2_add_vector_x(x, lx, ly, decode->dir);\n        y = u8g2_add_vector_y(y, lx, ly, decode->dir);\n\n        //u8g2_add_vector(&x, &y, lx, ly, decode->dir);\n\n#else\n        x += lx;\n        y += ly;\n#endif\n\n        /* draw foreground and background (if required) */\n        if(is_foreground) {\n            u8g2->draw_color = decode->fg_color; /* draw_color will be restored later */\n            u8g2_DrawHVLine(\n                u8g2,\n                x,\n                y,\n                current,\n#ifdef U8G2_WITH_FONT_ROTATION\n                /* dir */ decode->dir\n#else\n                0\n#endif\n            );\n        } else if(decode->is_transparent == 0) {\n            u8g2->draw_color = decode->bg_color; /* draw_color will be restored later */\n            u8g2_DrawHVLine(\n                u8g2,\n                x,\n                y,\n                current,\n#ifdef U8G2_WITH_FONT_ROTATION\n                /* dir */ decode->dir\n#else\n                0\n#endif\n            );\n        }\n\n        /* check, whether the end of the run length code has been reached */\n        if(cnt < rem) break;\n        cnt -= rem;\n        lx = 0;\n        ly++;\n    }\n    lx += cnt;\n\n    decode->x = lx;\n    decode->y = ly;\n}\n\nstatic void u8g2_font_setup_decode(u8g2_t* u8g2, const uint8_t* glyph_data) {\n    u8g2_font_decode_t* decode = &(u8g2->font_decode);\n    decode->decode_ptr = glyph_data;\n    decode->decode_bit_pos = 0;\n\n    /* 8 Nov 2015, this is already done in the glyph data search procedure */\n    /*\n  decode->decode_ptr += 1;\n  decode->decode_ptr += 1;\n  */\n\n    decode->glyph_width =\n        u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_width);\n    decode->glyph_height =\n        u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_height);\n\n    decode->fg_color = u8g2->draw_color;\n    decode->bg_color = (decode->fg_color == 0 ? 1 : 0);\n}\n\n/*\n  Description:\n    Decode and draw a glyph.\n  Args:\n    glyph_data: \t\t\t\t\tPointer to the compressed glyph data of the font\n    u8g2->font_decode.target_x\t\tX position\n    u8g2->font_decode.target_y\t\tY position\n    u8g2->font_decode.is_transparent\tTransparent mode\n  Return:\n    Width (delta x advance) of the glyph.\n  Calls:\n    u8g2_font_decode_len()\n*/\n/* optimized */\nint8_t u8g2_font_decode_glyph(u8g2_t* u8g2, const uint8_t* glyph_data) {\n    uint8_t a, b;\n    int8_t x, y;\n    int8_t d;\n    int8_t h;\n    u8g2_font_decode_t* decode = &(u8g2->font_decode);\n\n    u8g2_font_setup_decode(u8g2, glyph_data);\n    h = u8g2->font_decode.glyph_height;\n\n    x = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_x);\n    y = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_y);\n    d = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_delta_x);\n\n    if(decode->glyph_width > 0) {\n#ifdef U8G2_WITH_FONT_ROTATION\n        decode->target_x = u8g2_add_vector_x(decode->target_x, x, -(h + y), decode->dir);\n        decode->target_y = u8g2_add_vector_y(decode->target_y, x, -(h + y), decode->dir);\n\n        //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir);\n\n#else\n        decode->target_x += x;\n        decode->target_y -= h + y;\n#endif\n        //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir);\n\n#ifdef U8G2_WITH_INTERSECTION\n        {\n            u8g2_uint_t x0, x1, y0, y1;\n            x0 = decode->target_x;\n            y0 = decode->target_y;\n            x1 = x0;\n            y1 = y0;\n\n#ifdef U8G2_WITH_FONT_ROTATION\n            switch(decode->dir) {\n            case 0:\n                x1 += decode->glyph_width;\n                y1 += h;\n                break;\n            case 1:\n                x0 -= h;\n                x0++; /* shift down, because of assymetric boundaries for the interseciton test */\n                x1++;\n                y1 += decode->glyph_width;\n                break;\n            case 2:\n                x0 -= decode->glyph_width;\n                x0++; /* shift down, because of assymetric boundaries for the interseciton test */\n                x1++;\n                y0 -= h;\n                y0++; /* shift down, because of assymetric boundaries for the interseciton test */\n                y1++;\n                break;\n            case 3:\n                x1 += h;\n                y0 -= decode->glyph_width;\n                y0++; /* shift down, because of assymetric boundaries for the interseciton test */\n                y1++;\n                break;\n            }\n#else /* U8G2_WITH_FONT_ROTATION */\n            x1 += decode->glyph_width;\n            y1 += h;\n#endif\n\n            if(u8g2_IsIntersection(u8g2, x0, y0, x1, y1) == 0) return d;\n        }\n#endif /* U8G2_WITH_INTERSECTION */\n\n        /* reset local x/y position */\n        decode->x = 0;\n        decode->y = 0;\n\n        /* decode glyph */\n        for(;;) {\n            a = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_0);\n            b = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_1);\n            do {\n                u8g2_font_decode_len(u8g2, a, 0);\n                u8g2_font_decode_len(u8g2, b, 1);\n            } while(u8g2_font_decode_get_unsigned_bits(decode, 1) != 0);\n\n            if(decode->y >= h) break;\n        }\n\n        /* restore the u8g2 draw color, because this is modified by the decode algo */\n        u8g2->draw_color = decode->fg_color;\n    }\n    return d;\n}\n\n/*\n  Description:\n    Find the starting point of the glyph data.\n  Args:\n    encoding: Encoding (ASCII or Unicode) of the glyph\n  Return:\n    Address of the glyph data or NULL, if the encoding is not avialable in the font.\n*/\nconst uint8_t* u8g2_font_get_glyph_data(u8g2_t* u8g2, uint16_t encoding) {\n    const uint8_t* font = u8g2->font;\n    font += U8G2_FONT_DATA_STRUCT_SIZE;\n\n    if(encoding <= 255) {\n        if(encoding >= 'a') {\n            font += u8g2->font_info.start_pos_lower_a;\n        } else if(encoding >= 'A') {\n            font += u8g2->font_info.start_pos_upper_A;\n        }\n\n        for(;;) {\n            if(u8x8_pgm_read(font + 1) == 0) break;\n            if(u8x8_pgm_read(font) == encoding) {\n                return font + 2; /* skip encoding and glyph size */\n            }\n            font += u8x8_pgm_read(font + 1);\n        }\n    }\n#ifdef U8G2_WITH_UNICODE\n    else {\n        uint16_t e;\n        const uint8_t* unicode_lookup_table;\n\n        // removed, there is now the new index table\n        //#ifdef  __unix__\n        //    if ( u8g2->last_font_data != NULL && encoding >= u8g2->last_unicode )\n        //    {\n        //\tfont = u8g2->last_font_data;\n        //    }\n        //    else\n        //#endif\n\n        font += u8g2->font_info.start_pos_unicode;\n        unicode_lookup_table = font;\n\n        /* issue 596: search for the glyph start in the unicode lookup table */\n        do {\n            font += u8g2_font_get_word(unicode_lookup_table, 0);\n            e = u8g2_font_get_word(unicode_lookup_table, 2);\n            unicode_lookup_table += 4;\n        } while(e < encoding);\n\n        for(;;) {\n            e = u8x8_pgm_read(font);\n            e <<= 8;\n            e |= u8x8_pgm_read(font + 1);\n\n            // removed, there is now the new index table\n            //#ifdef  __unix__\n            //      if ( encoding < e )\n            //        break;\n            //#endif\n\n            if(e == 0) break;\n\n            if(e == encoding) {\n                // removed, there is now the new index table\n                //#ifdef  __unix__\n                //\tu8g2->last_font_data = font;\n                //\tu8g2->last_unicode = encoding;\n                //#endif\n                return font + 3; /* skip encoding and glyph size */\n            }\n            font += u8x8_pgm_read(font + 2);\n        }\n    }\n#endif\n\n    return NULL;\n}\n\nstatic u8g2_uint_t\n    u8g2_font_draw_glyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) {\n    u8g2_uint_t dx = 0;\n    u8g2->font_decode.target_x = x;\n    u8g2->font_decode.target_y = y;\n    //u8g2->font_decode.is_transparent = is_transparent; this is already set\n    //u8g2->font_decode.dir = dir;\n    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, encoding);\n    if(glyph_data != NULL) {\n        dx = u8g2_font_decode_glyph(u8g2, glyph_data);\n    }\n    return dx;\n}\n\nuint8_t u8g2_IsGlyph(u8g2_t* u8g2, uint16_t requested_encoding) {\n    /* updated to new code */\n    if(u8g2_font_get_glyph_data(u8g2, requested_encoding) != NULL) return 1;\n    return 0;\n}\n\n/* side effect: updates u8g2->font_decode and u8g2->glyph_x_offset */\nint8_t u8g2_GetGlyphWidth(u8g2_t* u8g2, uint16_t requested_encoding) {\n    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding);\n    if(glyph_data == NULL) return 0;\n\n    u8g2_font_setup_decode(u8g2, glyph_data);\n    u8g2->glyph_x_offset =\n        u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x);\n    u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y);\n\n    /* glyph width is here: u8g2->font_decode.glyph_width */\n\n    return u8g2_font_decode_get_signed_bits(\n        &(u8g2->font_decode), u8g2->font_info.bits_per_delta_x);\n}\n\n/*\n  set one of:\n    U8G2_FONT_MODE_TRANSPARENT\n    U8G2_FONT_MODE_SOLID\n    U8G2_FONT_MODE_NONE\n  This has been changed for the new font procedures  \n*/\nvoid u8g2_SetFontMode(u8g2_t* u8g2, uint8_t is_transparent) {\n    u8g2->font_decode.is_transparent = is_transparent; // new font procedures\n}\n\nu8g2_uint_t u8g2_DrawGlyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) {\n#ifdef U8G2_WITH_FONT_ROTATION\n    switch(u8g2->font_decode.dir) {\n    case 0:\n        y += u8g2->font_calc_vref(u8g2);\n        break;\n    case 1:\n        x -= u8g2->font_calc_vref(u8g2);\n        break;\n    case 2:\n        y -= u8g2->font_calc_vref(u8g2);\n        break;\n    case 3:\n        x += u8g2->font_calc_vref(u8g2);\n        break;\n    }\n#else\n    y += u8g2->font_calc_vref(u8g2);\n#endif\n    return u8g2_font_draw_glyph(u8g2, x, y, encoding);\n}\n\nstatic u8g2_uint_t\n    u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) U8G2_NOINLINE;\nstatic u8g2_uint_t u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {\n    uint16_t e;\n    u8g2_uint_t delta, sum;\n    u8x8_utf8_init(u8g2_GetU8x8(u8g2));\n    sum = 0;\n    for(;;) {\n        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        if(e == 0x0ffff) break;\n        str++;\n        if(e != 0x0fffe) {\n            delta = u8g2_DrawGlyph(u8g2, x, y, e);\n\n#ifdef U8G2_WITH_FONT_ROTATION\n            switch(u8g2->font_decode.dir) {\n            case 0:\n                x += delta;\n                break;\n            case 1:\n                y += delta;\n                break;\n            case 2:\n                x -= delta;\n                break;\n            case 3:\n                y -= delta;\n                break;\n            }\n\n            /*\n      // requires 10 bytes more on avr\n      x = u8g2_add_vector_x(x, delta, 0, u8g2->font_decode.dir);\n      y = u8g2_add_vector_y(y, delta, 0, u8g2->font_decode.dir);\n      */\n\n#else\n            x += delta;\n#endif\n\n            sum += delta;\n        }\n    }\n    return sum;\n}\n\nu8g2_uint_t u8g2_DrawStr(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {\n    u8g2->u8x8.next_cb = u8x8_ascii_next;\n    return u8g2_draw_string(u8g2, x, y, str);\n}\n\n/*\nsource: https://en.wikipedia.org/wiki/UTF-8\nBits\tfrom \t\tto\t\t\tbytes\tByte 1 \t\tByte 2 \t\tByte 3 \t\tByte 4 \t\tByte 5 \t\tByte 6\n  7 \tU+0000 \t\tU+007F \t\t1 \t\t0xxxxxxx\n11 \tU+0080 \t\tU+07FF \t\t2 \t\t110xxxxx \t10xxxxxx\n16 \tU+0800 \t\tU+FFFF \t\t3 \t\t1110xxxx \t10xxxxxx \t10xxxxxx\n21 \tU+10000 \tU+1FFFFF \t4 \t\t11110xxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n26 \tU+200000 \tU+3FFFFFF \t5 \t\t111110xx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n31 \tU+4000000 \tU+7FFFFFFF \t6 \t\t1111110x \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx  \n*/\nu8g2_uint_t u8g2_DrawUTF8(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {\n    u8g2->u8x8.next_cb = u8x8_utf8_next;\n    return u8g2_draw_string(u8g2, x, y, str);\n}\n\nu8g2_uint_t u8g2_DrawExtendedUTF8(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    uint8_t to_left,\n    u8g2_kerning_t* kerning,\n    const char* str) {\n    u8g2->u8x8.next_cb = u8x8_utf8_next;\n    uint16_t e_prev = 0x0ffff;\n    uint16_t e;\n    u8g2_uint_t delta, sum, k;\n    u8x8_utf8_init(u8g2_GetU8x8(u8g2));\n    sum = 0;\n    for(;;) {\n        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        if(e == 0x0ffff) break;\n        str++;\n        if(e != 0x0fffe) {\n            delta = u8g2_GetGlyphWidth(u8g2, e);\n\n            if(to_left) {\n                k = u8g2_GetKerning(u8g2, kerning, e, e_prev);\n                delta -= k;\n                x -= delta;\n            } else {\n                k = u8g2_GetKerning(u8g2, kerning, e_prev, e);\n                delta -= k;\n            }\n            e_prev = e;\n\n            u8g2_DrawGlyph(u8g2, x, y, e);\n            if(to_left) {\n            } else {\n                x += delta;\n                x -= k;\n            }\n\n            sum += delta;\n        }\n    }\n    return sum;\n}\n\nu8g2_uint_t u8g2_DrawExtUTF8(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    uint8_t to_left,\n    const uint16_t* kerning_table,\n    const char* str) {\n    u8g2->u8x8.next_cb = u8x8_utf8_next;\n    uint16_t e_prev = 0x0ffff;\n    uint16_t e;\n    u8g2_uint_t delta, sum, k;\n    u8x8_utf8_init(u8g2_GetU8x8(u8g2));\n    sum = 0;\n    for(;;) {\n        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        if(e == 0x0ffff) break;\n        str++;\n        if(e != 0x0fffe) {\n            delta = u8g2_GetGlyphWidth(u8g2, e);\n\n            if(to_left) {\n                k = u8g2_GetKerningByTable(u8g2, kerning_table, e, e_prev);\n                delta -= k;\n                x -= delta;\n            } else {\n                k = u8g2_GetKerningByTable(u8g2, kerning_table, e_prev, e);\n                delta -= k;\n            }\n            e_prev = e;\n\n            if(to_left) {\n            } else {\n                x += delta;\n            }\n            u8g2_DrawGlyph(u8g2, x, y, e);\n            if(to_left) {\n            } else {\n                //x += delta;\n                //x -= k;\n            }\n\n            sum += delta;\n        }\n    }\n    return sum;\n}\n\n/*===============================================*/\n\n/* set ascent/descent for reference point calculation */\n\nvoid u8g2_UpdateRefHeight(u8g2_t* u8g2) {\n    if(u8g2->font == NULL) return;\n    u8g2->font_ref_ascent = u8g2->font_info.ascent_A;\n    u8g2->font_ref_descent = u8g2->font_info.descent_g;\n    if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_TEXT) {\n    } else if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_XTEXT) {\n        if(u8g2->font_ref_ascent < u8g2->font_info.ascent_para)\n            u8g2->font_ref_ascent = u8g2->font_info.ascent_para;\n        if(u8g2->font_ref_descent > u8g2->font_info.descent_para)\n            u8g2->font_ref_descent = u8g2->font_info.descent_para;\n    } else {\n        if(u8g2->font_ref_ascent < u8g2->font_info.max_char_height + u8g2->font_info.y_offset)\n            u8g2->font_ref_ascent = u8g2->font_info.max_char_height + u8g2->font_info.y_offset;\n        if(u8g2->font_ref_descent > u8g2->font_info.y_offset)\n            u8g2->font_ref_descent = u8g2->font_info.y_offset;\n    }\n}\n\nvoid u8g2_SetFontRefHeightText(u8g2_t* u8g2) {\n    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_TEXT;\n    u8g2_UpdateRefHeight(u8g2);\n}\n\nvoid u8g2_SetFontRefHeightExtendedText(u8g2_t* u8g2) {\n    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_XTEXT;\n    u8g2_UpdateRefHeight(u8g2);\n}\n\nvoid u8g2_SetFontRefHeightAll(u8g2_t* u8g2) {\n    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_ALL;\n    u8g2_UpdateRefHeight(u8g2);\n}\n\n/*===============================================*/\n/* callback procedures to correct the y position */\n\nu8g2_uint_t u8g2_font_calc_vref_font(U8X8_UNUSED u8g2_t* u8g2) {\n    return 0;\n}\n\nvoid u8g2_SetFontPosBaseline(u8g2_t* u8g2) {\n    u8g2->font_calc_vref = u8g2_font_calc_vref_font;\n}\n\nu8g2_uint_t u8g2_font_calc_vref_bottom(u8g2_t* u8g2) {\n    return (u8g2_uint_t)(u8g2->font_ref_descent);\n}\n\nvoid u8g2_SetFontPosBottom(u8g2_t* u8g2) {\n    u8g2->font_calc_vref = u8g2_font_calc_vref_bottom;\n}\n\nu8g2_uint_t u8g2_font_calc_vref_top(u8g2_t* u8g2) {\n    u8g2_uint_t tmp;\n    /* reference pos is one pixel above the upper edge of the reference glyph */\n    tmp = (u8g2_uint_t)(u8g2->font_ref_ascent);\n    tmp++;\n    return tmp;\n}\n\nvoid u8g2_SetFontPosTop(u8g2_t* u8g2) {\n    u8g2->font_calc_vref = u8g2_font_calc_vref_top;\n}\n\nu8g2_uint_t u8g2_font_calc_vref_center(u8g2_t* u8g2) {\n    int8_t tmp;\n    tmp = u8g2->font_ref_ascent;\n    tmp -= u8g2->font_ref_descent;\n    tmp /= 2;\n    tmp += u8g2->font_ref_descent;\n    return tmp;\n}\n\nvoid u8g2_SetFontPosCenter(u8g2_t* u8g2) {\n    u8g2->font_calc_vref = u8g2_font_calc_vref_center;\n}\n\n/*===============================================*/\n\nvoid u8g2_SetFont(u8g2_t* u8g2, const uint8_t* font) {\n    if(u8g2->font != font) {\n        //#ifdef  __unix__\n        //\tu8g2->last_font_data = NULL;\n        //\tu8g2->last_unicode = 0x0ffff;\n        //#endif\n        u8g2->font = font;\n        u8g2_read_font_info(&(u8g2->font_info), font);\n        u8g2_UpdateRefHeight(u8g2);\n        /* u8g2_SetFontPosBaseline(u8g2); */ /* removed with issue 195 */\n    }\n}\n\n/*===============================================*/\n\nstatic uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) U8G2_NOINLINE;\nstatic uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) {\n    uint16_t e;\n    u8x8_utf8_init(u8g2_GetU8x8(u8g2));\n    for(;;) {\n        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        if(e == 0x0ffff) break;\n        str++;\n        if(e != 0x0fffe) {\n            if(u8g2_font_get_glyph_data(u8g2, e) == NULL) return 0;\n        }\n    }\n    return 1;\n}\n\nuint8_t u8g2_IsAllValidUTF8(u8g2_t* u8g2, const char* str) {\n    u8g2->u8x8.next_cb = u8x8_utf8_next;\n    return u8g2_is_all_valid(u8g2, str);\n}\n\n/* string calculation is stilll not 100% perfect as it addes the initial string offset to the overall size */\nstatic u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) U8G2_NOINLINE;\nstatic u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) {\n    uint16_t e;\n    u8g2_uint_t dx;\n    u8g2_long_t w;\n\n    u8g2->font_decode.glyph_width = 0;\n    u8x8_utf8_init(u8g2_GetU8x8(u8g2));\n\n    /* reset the total width to zero, this will be expanded during calculation */\n    w = 0;\n    dx = 0;\n\n    // printf(\"str=<%s>\\n\", str);\n\n    for(;;) {\n        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        if(e == 0x0ffff) break;\n        str++;\n        if(e != 0x0fffe) {\n            dx = u8g2_GetGlyphWidth(u8g2, e); /* delta x value of the glyph */\n            w += dx;\n        }\n    }\n\n    /* adjust the last glyph, check for issue #16: do not adjust if width is 0 */\n    if(u8g2->font_decode.glyph_width != 0) {\n        w -= dx;\n        w += u8g2->font_decode\n                 .glyph_width; /* the real pixel width of the glyph, sideeffect of GetGlyphWidth */\n        /* issue #46: we have to add the x offset also */\n        w += u8g2->glyph_x_offset; /* this value is set as a side effect of u8g2_GetGlyphWidth() */\n    }\n    // printf(\"w=%d \\n\", w);\n\n    return w;\n}\n\nstatic void u8g2_GetGlyphHorizontalProperties(\n    u8g2_t* u8g2,\n    uint16_t requested_encoding,\n    uint8_t* w,\n    int8_t* ox,\n    int8_t* dx) {\n    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding);\n    if(glyph_data == NULL) return;\n\n    u8g2_font_setup_decode(u8g2, glyph_data);\n    *w = u8g2->font_decode.glyph_width;\n    *ox = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x);\n    u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y);\n    *dx = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_delta_x);\n}\n\n/* u8g compatible GetStrX function */\nint8_t u8g2_GetStrX(u8g2_t* u8g2, const char* s) {\n    uint8_t w;\n    int8_t ox, dx;\n    u8g2_GetGlyphHorizontalProperties(u8g2, *s, &w, &ox, &dx);\n    return ox;\n}\n\nstatic u8g2_uint_t u8g2_calculate_exact_string_width(u8g2_t* u8g2, const char* str) {\n    u8g2_uint_t w;\n    uint16_t enc;\n    uint8_t gw;\n    int8_t ox, dx;\n\n    /* reset the total minimal width to zero, this will be expanded during calculation */\n    w = 0;\n\n    /* check for empty string, width is already 0 */\n    do {\n        enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n        str++;\n    } while(enc == 0x0fffe);\n\n    if(enc == 0x0ffff) return w;\n\n    /* get the glyph information of the first char. This must be valid, because we already checked for the empty string */\n    /* if *s is not inside the font, then the cached parameters of the glyph are all zero */\n    u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx);\n\n    /* strlen(s) == 1:       width = width(s[0]) */\n    /* strlen(s) == 2:       width = - offx(s[0]) + deltax(s[0]) + offx(s[1]) + width(s[1]) */\n    /* strlen(s) == 3:       width = - offx(s[0]) + deltax(s[0]) + deltax(s[1]) + offx(s[2]) + width(s[2]) */\n\n    /* assume that the string has size 2 or more, than start with negative offset-x */\n    /* for string with size 1, this will be nullified after the loop */\n    w = -ox;\n    for(;;) {\n        /* check and stop if the end of the string is reached */\n        do {\n            enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);\n            str++;\n        } while(enc == 0x0fffe);\n        if(enc == 0x0ffff) break;\n\n        u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx);\n\n        /* if there are still more characters, add the delta to the next glyph */\n        w += dx;\n    }\n\n    /* finally calculate the width of the last char */\n    /* here is another exception, if the last char is a black, use the dx value instead */\n    if(enc != ' ') {\n        /* if g was not updated in the for loop (strlen() == 1), then the initial offset x gets removed */\n        w += gw;\n        w += ox;\n    } else {\n        w += dx;\n    }\n\n    return w;\n}\n\nu8g2_long_t u8g2_GetStrWidth(u8g2_t* u8g2, const char* s) {\n    u8g2->u8x8.next_cb = u8x8_ascii_next;\n    return u8g2_string_width(u8g2, s);\n}\n\nu8g2_uint_t u8g2_GetExactStrWidth(u8g2_t* u8g2, const char* s) {\n    u8g2->u8x8.next_cb = u8x8_ascii_next;\n    return u8g2_calculate_exact_string_width(u8g2, s);\n}\n\n/*\nsource: https://en.wikipedia.org/wiki/UTF-8\nBits\tfrom \t\tto\t\t\tbytes\tByte 1 \t\tByte 2 \t\tByte 3 \t\tByte 4 \t\tByte 5 \t\tByte 6\n  7 \tU+0000 \t\tU+007F \t\t1 \t\t0xxxxxxx\n11 \tU+0080 \t\tU+07FF \t\t2 \t\t110xxxxx \t10xxxxxx\n16 \tU+0800 \t\tU+FFFF \t\t3 \t\t1110xxxx \t10xxxxxx \t10xxxxxx\n21 \tU+10000 \tU+1FFFFF \t4 \t\t11110xxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n26 \tU+200000 \tU+3FFFFFF \t5 \t\t111110xx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n31 \tU+4000000 \tU+7FFFFFFF \t6 \t\t1111110x \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx  \n*/\nu8g2_uint_t u8g2_GetUTF8Width(u8g2_t* u8g2, const char* str) {\n    u8g2->u8x8.next_cb = u8x8_utf8_next;\n    return u8g2_string_width(u8g2, str);\n}\n\nvoid u8g2_SetFontDirection(u8g2_t* u8g2, uint8_t dir) {\n#ifdef U8G2_WITH_FONT_ROTATION\n    u8g2->font_decode.dir = dir;\n#endif\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_glue.c",
    "content": "#include \"u8g2_glue.h\"\n\n#include <furi_hal.h>\n\n#define CONTRAST_ERC 32\n#define CONTRAST_MGG 28\n\nuint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    UNUSED(u8x8);\n    UNUSED(arg_ptr);\n    switch(msg) {\n    case U8X8_MSG_GPIO_AND_DELAY_INIT:\n        /* HAL initialization contains all what we need so we can skip this part. */\n        break;\n    case U8X8_MSG_DELAY_MILLI:\n        furi_delay_ms(arg_int);\n        break;\n    case U8X8_MSG_DELAY_10MICRO:\n        furi_delay_us(10);\n        break;\n    case U8X8_MSG_DELAY_100NANO:\n        asm(\"nop\");\n        break;\n    case U8X8_MSG_GPIO_RESET:\n        furi_hal_gpio_write(&gpio_display_rst_n, arg_int);\n        break;\n    default:\n        return 0;\n    }\n\n    return 1;\n}\n\nuint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    UNUSED(u8x8);\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_display, (uint8_t*)arg_ptr, arg_int, 10000);\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        furi_hal_gpio_write(&gpio_display_di, arg_int);\n        break;\n    case U8X8_MSG_BYTE_INIT:\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        furi_hal_spi_acquire(&furi_hal_spi_bus_handle_display);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        furi_hal_spi_release(&furi_hal_spi_bus_handle_display);\n        break;\n    default:\n        return 0;\n    }\n\n    return 1;\n}\n\n#define ST756X_CMD_ON_OFF           0b10101110 /**< 0:0 Switch Display ON/OFF: last bit */\n#define ST756X_CMD_SET_LINE         0b01000000 /**< 0:0 Set Start Line: last 6 bits  */\n#define ST756X_CMD_SET_PAGE         0b10110000 /**< 0:0 Set Page address: last 4 bits */\n#define ST756X_CMD_SET_COLUMN_MSB   0b00010000 /**< 0:0 Set Column MSB: last 4 bits */\n#define ST756X_CMD_SET_COLUMN_LSB   0b00000000 /**< 0:0 Set Column LSB: last 4 bits */\n#define ST756X_CMD_SEG_DIRECTION    0b10100000 /**< 0:0 Reverse scan direction of SEG: last bit */\n#define ST756X_CMD_INVERSE_DISPLAY  0b10100110 /**< 0:0 Invert display: last bit */\n#define ST756X_CMD_ALL_PIXEL_ON     0b10100100 /**< 0:0 Set all pixel on: last bit */\n#define ST756X_CMD_BIAS_SELECT      0b10100010 /**< 0:0 Select 1/9(0) or 1/7(1) bias: last bit */\n#define ST756X_CMD_R_M_W            0b11100000 /**< 0:0 Enter Read Modify Write mode: read+0, write+1 */\n#define ST756X_CMD_END              0b11101110 /**< 0:0 Exit Read Modify Write mode */\n#define ST756X_CMD_RESET            0b11100010 /**< 0:0 Software Reset */\n#define ST756X_CMD_COM_DIRECTION    0b11000000 /**< 0:0 Com direction reverse: +0b1000 */\n#define ST756X_CMD_POWER_CONTROL    0b00101000 /**< 0:0 Power control: last 3 bits VB:VR:VF */\n#define ST756X_CMD_REGULATION_RATIO 0b00100000 /**< 0:0 Regulation resistor ration: last 3bits */\n#define ST756X_CMD_SET_EV           0b10000001 /**< 0:0 Set electronic volume: 5 bits in next byte */\n#define ST756X_CMD_SET_BOOSTER \\\n    0b11111000 /**< 0:0 Set Booster level, 4X(0) or 5X(1): last bit in next byte */\n#define ST756X_CMD_NOP 0b11100011 /**< 0:0 No operation */\n\nstatic const uint8_t u8x8_d_st756x_powersave0_seq[] = {\n    U8X8_START_TRANSFER(), /* enable chip, delay is part of the transfer start */\n    U8X8_C(ST756X_CMD_ALL_PIXEL_ON | 0b0), /* all pixel off */\n    U8X8_C(ST756X_CMD_ON_OFF | 0b1), /* display on */\n    U8X8_END_TRANSFER(), /* disable chip */\n    U8X8_END() /* end of sequence */\n};\n\nstatic const uint8_t u8x8_d_st756x_powersave1_seq[] = {\n    U8X8_START_TRANSFER(), /* enable chip, delay is part of the transfer start */\n    U8X8_C(ST756X_CMD_ON_OFF | 0b0), /* display off */\n    U8X8_C(ST756X_CMD_ALL_PIXEL_ON | 0b1), /* all pixel on */\n    U8X8_END_TRANSFER(), /* disable chip */\n    U8X8_END() /* end of sequence */\n};\n\nstatic const uint8_t u8x8_d_st756x_flip0_seq[] = {\n    U8X8_START_TRANSFER(), /* enable chip, delay is part of the transfer start */\n    U8X8_C(0x0a1), /* segment remap a0/a1*/\n    U8X8_C(0x0c0), /* c0: scan dir normal, c8: reverse */\n    U8X8_END_TRANSFER(), /* disable chip */\n    U8X8_END() /* end of sequence */\n};\n\nstatic const uint8_t u8x8_d_st756x_flip1_seq[] = {\n    U8X8_START_TRANSFER(), /* enable chip, delay is part of the transfer start */\n    U8X8_C(0x0a0), /* segment remap a0/a1*/\n    U8X8_C(0x0c8), /* c0: scan dir normal, c8: reverse */\n    U8X8_END_TRANSFER(), /* disable chip */\n    U8X8_END() /* end of sequence */\n};\n\nstatic const u8x8_display_info_t u8x8_st756x_128x64_display_info = {\n    .chip_enable_level = 0,\n    .chip_disable_level = 1,\n    .post_chip_enable_wait_ns = 150, /* st7565 datasheet, table 26, tcsh */\n    .pre_chip_disable_wait_ns = 50, /* st7565 datasheet, table 26, tcss */\n    .reset_pulse_width_ms = 1,\n    .post_reset_wait_ms = 1,\n    .sda_setup_time_ns = 50, /* st7565 datasheet, table 26, tsds */\n    .sck_pulse_width_ns =\n        120, /* half of cycle time (100ns according to datasheet), AVR: below 70: 8 MHz, >= 70 --> 4MHz clock */\n    .sck_clock_hz =\n        4000000UL, /* since Arduino 1.6.0, the SPI bus speed in Hz. Should be  1000000000/sck_pulse_width_ns */\n    .spi_mode = 0, /* active high, rising edge */\n    .i2c_bus_clock_100kHz = 4,\n    .data_setup_time_ns = 40, /* st7565 datasheet, table 24, tds8 */\n    .write_pulse_width_ns = 80, /* st7565 datasheet, table 24, tcclw */\n    .tile_width = 16, /* width of 16*8=128 pixel */\n    .tile_height = 8,\n    .default_x_offset = 0,\n    .flipmode_x_offset = 4,\n    .pixel_width = 128,\n    .pixel_height = 64};\n\nuint8_t u8x8_d_st756x_common(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t x, c;\n    uint8_t* ptr;\n\n    switch(msg) {\n    case U8X8_MSG_DISPLAY_DRAW_TILE:\n        u8x8_cad_StartTransfer(u8x8);\n\n        x = ((u8x8_tile_t*)arg_ptr)->x_pos;\n        x *= 8;\n        x += u8x8->x_offset;\n        u8x8_cad_SendCmd(u8x8, 0x010 | (x >> 4));\n        u8x8_cad_SendCmd(u8x8, 0x000 | (x & 15));\n        u8x8_cad_SendCmd(u8x8, 0x0b0 | (((u8x8_tile_t*)arg_ptr)->y_pos));\n\n        c = ((u8x8_tile_t*)arg_ptr)->cnt;\n        c *= 8;\n        ptr = ((u8x8_tile_t*)arg_ptr)->tile_ptr;\n        /* \n                The following if condition checks the hardware limits of the st7565 \n                controller: It is not allowed to write beyond the display limits.\n                This is in fact an issue within flip mode.\n            */\n        if(c + x > 132u) {\n            c = 132u;\n            c -= x;\n        }\n\n        do {\n            u8x8_cad_SendData(\n                u8x8, c, ptr); /* note: SendData can not handle more than 255 bytes */\n            arg_int--;\n        } while(arg_int > 0);\n\n        u8x8_cad_EndTransfer(u8x8);\n        break;\n    case U8X8_MSG_DISPLAY_SET_POWER_SAVE:\n        if(arg_int == 0)\n            u8x8_cad_SendSequence(u8x8, u8x8_d_st756x_powersave0_seq);\n        else\n            u8x8_cad_SendSequence(u8x8, u8x8_d_st756x_powersave1_seq);\n        break;\n#ifdef U8X8_WITH_SET_CONTRAST\n    case U8X8_MSG_DISPLAY_SET_CONTRAST:\n        u8x8_cad_StartTransfer(u8x8);\n        u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV);\n        u8x8_cad_SendArg(u8x8, arg_int >> 2); /* st7565 has range from 0 to 63 */\n        u8x8_cad_EndTransfer(u8x8);\n        break;\n#endif\n    default:\n        return 0;\n    }\n    return 1;\n}\n\nvoid u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias) {\n    contrast = contrast & 0b00111111;\n    regulation_ratio = regulation_ratio & 0b111;\n\n    u8x8_cad_StartTransfer(u8x8);\n    // Reset\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_RESET);\n    // Bias: 1/7(0b1) or 1/9(0b0)\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_BIAS_SELECT | bias);\n    // Page, Line and Segment config\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_SEG_DIRECTION);\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_COM_DIRECTION | 0b1000);\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_LINE);\n    // Set Regulation Ratio\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_REGULATION_RATIO | regulation_ratio);\n    // Set EV\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV);\n    u8x8_cad_SendArg(u8x8, contrast);\n    // Enable power\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_POWER_CONTROL | 0b111);\n\n    u8x8_cad_EndTransfer(u8x8);\n}\n\nvoid u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) {\n    uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ?\n                           CONTRAST_MGG :\n                           CONTRAST_ERC;\n    contrast += contrast_offset;\n    contrast = contrast & 0b00111111;\n\n    u8x8_cad_StartTransfer(u8x8);\n    u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV);\n    u8x8_cad_SendArg(u8x8, contrast);\n    u8x8_cad_EndTransfer(u8x8);\n}\n\nuint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    /* call common procedure first and handle messages there */\n    if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) {\n        /* msg not handled, then try here */\n        switch(msg) {\n        case U8X8_MSG_DISPLAY_SETUP_MEMORY:\n            u8x8_d_helper_display_setup_memory(u8x8, &u8x8_st756x_128x64_display_info);\n            break;\n        case U8X8_MSG_DISPLAY_INIT:\n            u8x8_d_helper_display_init(u8x8);\n            FuriHalVersionDisplay display = furi_hal_version_get_hw_display();\n            if(display == FuriHalVersionDisplayMgg) {\n                /* MGG v0+(ST7567)\n                 * EV = 32\n                 * RR = V0 / ((1 - (63 - EV) / 162) * 2.1)\n                 * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110)\n                 * Bias = 1/9 (false)\n                 */\n                u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false);\n            } else {\n                /* ERC v1(ST7565) and v2(ST7567)\n                 * EV = 33\n                 * RR = V0 / ((1 - (63 - EV) / 162) * 2.1)\n                 * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101)\n                 * Bias = 1/9 (false)\n                 */\n                u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false);\n            }\n            break;\n        case U8X8_MSG_DISPLAY_SET_FLIP_MODE:\n            if(arg_int == 0) {\n                u8x8_cad_SendSequence(u8x8, u8x8_d_st756x_flip1_seq);\n                u8x8->x_offset = u8x8->display_info->default_x_offset;\n            } else {\n                u8x8_cad_SendSequence(u8x8, u8x8_d_st756x_flip0_seq);\n                u8x8->x_offset = u8x8->display_info->flipmode_x_offset;\n            }\n            break;\n        default:\n            /* msg unknown */\n            return 0;\n        }\n    }\n    return 1;\n}\n\nvoid u8g2_Setup_st756x_flipper(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb) {\n    uint8_t tile_buf_height;\n    uint8_t* buf;\n    u8g2_SetupDisplay(u8g2, u8x8_d_st756x_flipper, u8x8_cad_001, byte_cb, gpio_and_delay_cb);\n    buf = u8g2_m_16_8_f(&tile_buf_height);\n    u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_glue.h",
    "content": "#pragma once\n\n#include \"u8g2.h\"\n#include <stdbool.h>\n\nuint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\nuint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\nvoid u8g2_Setup_st756x_flipper(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\n\nvoid u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias);\n\nvoid u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset);\n"
  },
  {
    "path": "lib/u8g2/u8g2_hvline.c",
    "content": "/*\n\n  u8g2_hvline.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n\n  Calltree\n  \n    void u8g2_DrawHVLine(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir)\n    u8g2->cb->draw_l90\n    u8g2_draw_hv_line_2dir\n    u8g2->ll_hvline(u8g2, x, y, len, dir);\n    \n\n*/\n\n#include \"u8g2.h\"\n#include <assert.h>\n\n/*==========================================================*/\n/* intersection procedure */\n\n/*\n  Description:\n    clip range from pos a (included) with line len (a+len excluded) agains c (included) to d (excluded)\n  Assumptions:\n    len > 0\n    c <= d\t\t(this is not checked)\n  will return 0 if there is no intersection and if a > b\n\n*/\n\nstatic uint8_t\n    u8g2_clip_intersection2(u8g2_uint_t* ap, u8g2_uint_t* len, u8g2_uint_t c, u8g2_uint_t d) {\n    u8g2_uint_t a = *ap;\n    u8g2_uint_t b;\n    b = a;\n    b += *len;\n\n    /*\n    Description:\n      clip range from a (included) to b (excluded) agains c (included) to d (excluded)\n    Assumptions:\n      a <= b\t\t(violation is checked and handled correctly)\n      c <= d\t\t(this is not checked)\n    will return 0 if there is no intersection and if a > b\n\n    optimized clipping: c is set to 0 --> 27 Oct 2018: again removed the c==0 assumption\n    \n    replaced by uint8_t u8g2_clip_intersection2\n  */\n\n    /* handle the a>b case correctly. If code and time is critical, this could */\n    /* be removed completly (be aware about memory curruption for wrong */\n    /* arguments) or return 0 for a>b (will lead to skipped lines for wrong */\n    /* arguments) */\n\n    /* removing the following if clause completly may lead to memory corruption of a>b */\n    if(a > b) {\n        /* replacing this if with a simple \"return 0;\" will not handle the case with negative a */\n        if(a < d) {\n            b = d;\n            b--;\n        } else {\n            a = c;\n        }\n    }\n\n    /* from now on, the asumption a <= b is ok */\n\n    if(a >= d) return 0;\n    if(b <= c) return 0;\n    if(a < c) a = c;\n    if(b > d) b = d;\n\n    *ap = a;\n    b -= a;\n    *len = b;\n    return 1;\n}\n\n/*==========================================================*/\n/* draw procedures */\n\n/*\n  x,y\t\tUpper left position of the line within the pixel buffer \n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  This function first adjusts the y position to the local buffer. Then it\n  will clip the line and call u8g2_draw_low_level_hv_line()\n\n*/\nvoid u8g2_draw_hv_line_2dir(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    /* clipping happens before the display rotation */\n\n    /* transform to pixel buffer coordinates */\n    y -= u8g2->pixel_curr_row;\n\n    u8g2->ll_hvline(u8g2, x, y, len, dir);\n}\n\n/*\n  This is the toplevel function for the hv line draw procedures.\n  This function should be called by the user.\n  \n  \"dir\" may have 4 directions: 0 (left to right), 1, 2, 3 (down up)\n*/\nvoid u8g2_DrawHVLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir) {\n    /* Make a call to the callback function (e.g. u8g2_draw_l90_r0). */\n    /* The callback may rotate the hv line */\n    /* after rotation this will call u8g2_draw_hv_line_4dir() */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    if(u8g2->is_page_clip_window_intersection != 0)\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n        if(len != 0) {\n            /* convert to two directions */\n            if(len > 1) {\n                if(dir == 2) {\n                    x -= len;\n                    x++;\n                } else if(dir == 3) {\n                    y -= len;\n                    y++;\n                }\n            }\n            dir &= 1;\n\n            /* clip against the user window */\n            if(dir == 0) {\n                if(y < u8g2->user_y0) return;\n                if(y >= u8g2->user_y1) return;\n                if(u8g2_clip_intersection2(&x, &len, u8g2->user_x0, u8g2->user_x1) == 0) return;\n            } else {\n                if(x < u8g2->user_x0) return;\n                if(x >= u8g2->user_x1) return;\n                if(u8g2_clip_intersection2(&y, &len, u8g2->user_y0, u8g2->user_y1) == 0) return;\n            }\n\n            u8g2->cb->draw_l90(u8g2, x, y, len, dir);\n        }\n}\n\nvoid u8g2_DrawHLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len) {\n    // #ifdef U8G2_WITH_INTERSECTION\n    //   if ( u8g2_IsIntersection(u8g2, x, y, x+len, y+1) == 0 )\n    //     return;\n    // #endif /* U8G2_WITH_INTERSECTION */\n    u8g2_DrawHVLine(u8g2, x, y, len, 0);\n}\n\nvoid u8g2_DrawVLine(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len) {\n    // #ifdef U8G2_WITH_INTERSECTION\n    //   if ( u8g2_IsIntersection(u8g2, x, y, x+1, y+len) == 0 )\n    //     return;\n    // #endif /* U8G2_WITH_INTERSECTION */\n    u8g2_DrawHVLine(u8g2, x, y, len, 1);\n}\n\nvoid u8g2_DrawPixel(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y) {\n#ifdef U8G2_WITH_INTERSECTION\n    if(y < u8g2->user_y0) return;\n    if(y >= u8g2->user_y1) return;\n    if(x < u8g2->user_x0) return;\n    if(x >= u8g2->user_x1) return;\n#endif /* U8G2_WITH_INTERSECTION */\n    u8g2_DrawHVLine(u8g2, x, y, 1, 0);\n}\n\n/*\n  Assign the draw color for all drawing functions.\n  color may be 0 or 1. The actual color is defined by the display.\n  With color = 1 the drawing function will set the display memory to 1.\n  For OLEDs this ususally means, that the pixel is enabled and the LED \n  at the pixel is turned on.\n  On an LCD it usually means that the LCD segment of the pixel is enabled, \n  which absorbs the light.\n  For eInk/ePaper it means black ink.\n\n  7 Jan 2017: Allow color value 2 for XOR operation.\n  \n*/\nvoid u8g2_SetDrawColor(u8g2_t* u8g2, uint8_t color) {\n    u8g2->draw_color = color; /* u8g2_SetDrawColor: just assign the argument */\n    if(color >= 3)\n        u8g2->draw_color = 1; /* u8g2_SetDrawColor: make color as one if arg is invalid */\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_intersection.c",
    "content": "/*\n\n  u8g2_intersection.c \n  \n  Intersection calculation, code taken from u8g_clip.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\n#ifdef __GNUC__\n#define U8G2_ALWAYS_INLINE __inline__ __attribute__((always_inline))\n#else\n#define U8G2_ALWAYS_INLINE\n#endif\n\n#if defined(U8G2_WITH_INTERSECTION) || defined(U8G2_WITH_CLIP_WINDOW_SUPPORT)\n\n#ifdef OLD_VERSION_WITH_SYMETRIC_BOUNDARIES\n\n/*\n  intersection assumptions:\n    a1 <= a2 is always true    \n    \n    minimized version\n    ---1----0 1             b1 <= a2 && b1 > b2\n    -----1--0 1             b2 >= a1 && b1 > b2\n    ---1-1--- 1             b1 <= a2 && b2 >= a1\n  */\n\n/*\n  calculate the intersection between a0/a1 and v0/v1\n  The intersection check returns one if the range of a0/a1 has an intersection with v0/v1.\n  The intersection check includes the boundary values v1 and a1.\n\n  The following asserts will succeed:\n    assert( u8g2_is_intersection_decision_tree(4, 6, 7, 9) == 0 );\n    assert( u8g2_is_intersection_decision_tree(4, 6, 6, 9) != 0 );\n    assert( u8g2_is_intersection_decision_tree(6, 9, 4, 6) != 0 );\n    assert( u8g2_is_intersection_decision_tree(7, 9, 4, 6) == 0 );  \n*/\n\n//static uint8_t U8G2_ALWAYS_INLINE u8g2_is_intersection_decision_tree(u8g_uint_t a0, u8g_uint_t a1, u8g_uint_t v0, u8g_uint_t v1)\nstatic uint8_t u8g2_is_intersection_decision_tree(\n    u8g2_uint_t a0,\n    u8g2_uint_t a1,\n    u8g2_uint_t v0,\n    u8g2_uint_t v1) {\n    if(v0 <= a1) {\n        if(v1 >= a0) {\n            return 1;\n        } else {\n            if(v0 > v1) {\n                return 1;\n            } else {\n                return 0;\n            }\n        }\n    } else {\n        if(v1 >= a0) {\n            if(v0 > v1) {\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            return 0;\n        }\n    }\n}\n\n#endif /* OLD_VERSION_WITH_SYMETRIC_BOUNDARIES */\n\n/*\n  version with asymetric boundaries.\n  a1 and v1 are excluded\n  v0 == v1 is not support end return 1\n*/\nuint8_t u8g2_is_intersection_decision_tree(\n    u8g2_uint_t a0,\n    u8g2_uint_t a1,\n    u8g2_uint_t v0,\n    u8g2_uint_t v1) {\n    if(v0 < a1) // v0 <= a1\n    {\n        if(v1 > a0) // v1 >= a0\n        {\n            return 1;\n        } else {\n            if(v0 > v1) // v0 > v1\n            {\n                return 1;\n            } else {\n                return 0;\n            }\n        }\n    } else {\n        if(v1 > a0) // v1 >= a0\n        {\n            if(v0 > v1) // v0 > v1\n            {\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            return 0;\n        }\n    }\n}\n\n/* upper limits are not included (asymetric boundaries) */\nuint8_t u8g2_IsIntersection(\n    u8g2_t* u8g2,\n    u8g2_uint_t x0,\n    u8g2_uint_t y0,\n    u8g2_uint_t x1,\n    u8g2_uint_t y1) {\n    if(u8g2_is_intersection_decision_tree(u8g2->user_y0, u8g2->user_y1, y0, y1) == 0) return 0;\n\n    return u8g2_is_intersection_decision_tree(u8g2->user_x0, u8g2->user_x1, x0, x1);\n}\n\n#endif /* U8G2_WITH_INTERSECTION */\n"
  },
  {
    "path": "lib/u8g2/u8g2_line.c",
    "content": "/*\n\n  u8g2_box.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n\nvoid u8g2_DrawLine(u8g2_t* u8g2, u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2) {\n    u8g2_uint_t tmp;\n    u8g2_uint_t x, y;\n    u8g2_uint_t dx, dy;\n    u8g2_int_t err;\n    u8g2_int_t ystep;\n\n    uint8_t swapxy = 0;\n\n    /* no intersection check at the moment, should be added... */\n\n    if(x1 > x2)\n        dx = x1 - x2;\n    else\n        dx = x2 - x1;\n    if(y1 > y2)\n        dy = y1 - y2;\n    else\n        dy = y2 - y1;\n\n    if(dy > dx) {\n        swapxy = 1;\n        tmp = dx;\n        dx = dy;\n        dy = tmp;\n        tmp = x1;\n        x1 = y1;\n        y1 = tmp;\n        tmp = x2;\n        x2 = y2;\n        y2 = tmp;\n    }\n    if(x1 > x2) {\n        tmp = x1;\n        x1 = x2;\n        x2 = tmp;\n        tmp = y1;\n        y1 = y2;\n        y2 = tmp;\n    }\n    err = dx >> 1;\n    if(y2 > y1)\n        ystep = 1;\n    else\n        ystep = -1;\n    y = y1;\n\n#ifndef U8G2_16BIT\n    if(x2 == 255) x2--;\n#else\n    if(x2 == 0xffff) x2--;\n#endif\n\n    for(x = x1; x <= x2; x++) {\n        if(swapxy == 0)\n            u8g2_DrawPixel(u8g2, x, y);\n        else\n            u8g2_DrawPixel(u8g2, y, x);\n        err -= (uint8_t)dy;\n        if(err < 0) {\n            y += (u8g2_uint_t)ystep;\n            err += (u8g2_uint_t)dx;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/u8g2/u8g2_ll_hvline.c",
    "content": "/*\n\n  u8g2_ll_hvline.c\n  \n  low level hvline\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n\n  *ptr |= or_mask\n  *ptr ^= xor_mask\n  \n  color = 0:   or_mask = 1, xor_mask = 1\n  color = 1:   or_mask = 1, xor_mask = 0\n  color = 2:   or_mask = 0, xor_mask = 1\n\n  if ( color <= 1 )\n    or_mask  = mask;\n  if ( color != 1 )\n    xor_mask = mask;\n    \n*/\n\n#include \"u8g2.h\"\n#include <assert.h>\n\n/*=================================================*/\n/*\n  u8g2_ll_hvline_vertical_top_lsb\n    SSD13xx\n    UC1701    \n*/\n\n#ifdef U8G2_WITH_HVLINE_SPEED_OPTIMIZATION\n\n/*\n  x,y\t\tUpper left position of the line within the local buffer (not the display!)\n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  asumption: \n    all clipping done\n*/\nvoid u8g2_ll_hvline_vertical_top_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    uint16_t offset;\n    uint8_t* ptr;\n    uint8_t bit_pos, mask;\n    uint8_t or_mask, xor_mask;\n#ifdef __unix\n    uint8_t* max_ptr = u8g2->tile_buf_ptr +\n                       u8g2_GetU8x8(u8g2)->display_info->tile_width * u8g2->tile_buf_height * 8;\n#endif\n\n    //assert(x >= u8g2->buf_x0);\n    //assert(x < u8g2_GetU8x8(u8g2)->display_info->tile_width*8);\n    //assert(y >= u8g2->buf_y0);\n    //assert(y < u8g2_GetU8x8(u8g2)->display_info->tile_height*8);\n\n    /* bytes are vertical, lsb on top (y=0), msb at bottom (y=7) */\n    bit_pos = y; /* overflow truncate is ok here... */\n    bit_pos &= 7; /* ... because only the lowest 3 bits are needed */\n    mask = 1;\n    mask <<= bit_pos;\n\n    or_mask = 0;\n    xor_mask = 0;\n    if(u8g2->draw_color <= 1) or_mask = mask;\n    if(u8g2->draw_color != 1) xor_mask = mask;\n\n    offset = y; /* y might be 8 or 16 bit, but we need 16 bit, so use a 16 bit variable */\n    offset &= ~7;\n    offset *= u8g2_GetU8x8(u8g2)->display_info->tile_width;\n    ptr = u8g2->tile_buf_ptr;\n    ptr += offset;\n    ptr += x;\n\n    if(dir == 0) {\n        do {\n#ifdef __unix\n            assert(ptr < max_ptr);\n#endif\n            *ptr |= or_mask;\n            *ptr ^= xor_mask;\n            ptr++;\n            len--;\n        } while(len != 0);\n    } else {\n        do {\n#ifdef __unix\n            assert(ptr < max_ptr);\n#endif\n            *ptr |= or_mask;\n            *ptr ^= xor_mask;\n\n            bit_pos++;\n            bit_pos &= 7;\n\n            len--;\n\n            if(bit_pos == 0) {\n                ptr +=\n                    u8g2->pixel_buf_width; /* 6 Jan 17: Changed u8g2->width to u8g2->pixel_buf_width, issue #148 */\n\n                if(u8g2->draw_color <= 1) or_mask = 1;\n                if(u8g2->draw_color != 1) xor_mask = 1;\n            } else {\n                or_mask <<= 1;\n                xor_mask <<= 1;\n            }\n        } while(len != 0);\n    }\n}\n\n#else /* U8G2_WITH_HVLINE_SPEED_OPTIMIZATION */\n\n/*\n  x,y position within the buffer\n*/\nstatic void u8g2_draw_pixel_vertical_top_lsb(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y) {\n    uint16_t offset;\n    uint8_t* ptr;\n    uint8_t bit_pos, mask;\n\n    //assert(x >= u8g2->buf_x0);\n    //assert(x < u8g2_GetU8x8(u8g2)->display_info->tile_width*8);\n    //assert(y >= u8g2->buf_y0);\n    //assert(y < u8g2_GetU8x8(u8g2)->display_info->tile_height*8);\n\n    /* bytes are vertical, lsb on top (y=0), msb at bottom (y=7) */\n    bit_pos = y; /* overflow truncate is ok here... */\n    bit_pos &= 7; /* ... because only the lowest 3 bits are needed */\n    mask = 1;\n    mask <<= bit_pos;\n\n    offset = y; /* y might be 8 or 16 bit, but we need 16 bit, so use a 16 bit variable */\n    offset &= ~7;\n    offset *= u8g2_GetU8x8(u8g2)->display_info->tile_width;\n    ptr = u8g2->tile_buf_ptr;\n    ptr += offset;\n    ptr += x;\n\n    if(u8g2->draw_color <= 1) *ptr |= mask;\n    if(u8g2->draw_color != 1) *ptr ^= mask;\n}\n\n/*\n  x,y\t\tUpper left position of the line within the local buffer (not the display!)\n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  asumption: \n    all clipping done\n*/\nvoid u8g2_ll_hvline_vertical_top_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    if(dir == 0) {\n        do {\n            u8g2_draw_pixel_vertical_top_lsb(u8g2, x, y);\n            x++;\n            len--;\n        } while(len != 0);\n    } else {\n        do {\n            u8g2_draw_pixel_vertical_top_lsb(u8g2, x, y);\n            y++;\n            len--;\n        } while(len != 0);\n    }\n}\n\n#endif /* U8G2_WITH_HVLINE_SPEED_OPTIMIZATION */\n\n/*=================================================*/\n/*\n  u8g2_ll_hvline_horizontal_right_lsb\n    ST7920\n*/\n\n#ifdef U8G2_WITH_HVLINE_SPEED_OPTIMIZATION\n\n/*\n  x,y\t\tUpper left position of the line within the local buffer (not the display!)\n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  asumption: \n    all clipping done\n*/\n\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nvoid u8g2_ll_hvline_horizontal_right_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    uint16_t offset;\n    uint8_t* ptr;\n    uint8_t bit_pos;\n    uint8_t mask;\n    uint8_t tile_width = u8g2_GetU8x8(u8g2)->display_info->tile_width;\n\n    bit_pos = x; /* overflow truncate is ok here... */\n    bit_pos &= 7; /* ... because only the lowest 3 bits are needed */\n    mask = 128;\n    mask >>= bit_pos;\n\n    offset = y; /* y might be 8 or 16 bit, but we need 16 bit, so use a 16 bit variable */\n    offset *= tile_width;\n    offset += x >> 3;\n    ptr = u8g2->tile_buf_ptr;\n    ptr += offset;\n\n    if(dir == 0) {\n        do {\n            if(u8g2->draw_color <= 1) *ptr |= mask;\n            if(u8g2->draw_color != 1) *ptr ^= mask;\n\n            mask >>= 1;\n            if(mask == 0) {\n                mask = 128;\n                ptr++;\n            }\n\n            //x++;\n            len--;\n        } while(len != 0);\n    } else {\n        do {\n            if(u8g2->draw_color <= 1) *ptr |= mask;\n            if(u8g2->draw_color != 1) *ptr ^= mask;\n\n            ptr += tile_width;\n            //y++;\n            len--;\n        } while(len != 0);\n    }\n}\n\n#else /* U8G2_WITH_HVLINE_SPEED_OPTIMIZATION */\n\n/*\n  x,y position within the buffer\n*/\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nstatic void u8g2_draw_pixel_horizontal_right_lsb(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y) {\n    uint16_t offset;\n    uint8_t* ptr;\n    uint8_t bit_pos, mask;\n\n    //assert(x >= u8g2->buf_x0);\n    //assert(x < u8g2_GetU8x8(u8g2)->display_info->tile_width*8);\n    //assert(y >= u8g2->buf_y0);\n    //assert(y < u8g2_GetU8x8(u8g2)->display_info->tile_height*8);\n\n    /* bytes are vertical, lsb on top (y=0), msb at bottom (y=7) */\n    bit_pos = x; /* overflow truncate is ok here... */\n    bit_pos &= 7; /* ... because only the lowest 3 bits are needed */\n    mask = 128;\n    mask >>= bit_pos;\n    x >>= 3;\n\n    offset = y; /* y might be 8 or 16 bit, but we need 16 bit, so use a 16 bit variable */\n    offset *= u8g2_GetU8x8(u8g2)->display_info->tile_width;\n    offset += x;\n    ptr = u8g2->tile_buf_ptr;\n    ptr += offset;\n\n    if(u8g2->draw_color <= 1) *ptr |= mask;\n    if(u8g2->draw_color != 1) *ptr ^= mask;\n}\n\n/*\n  x,y\t\tUpper left position of the line within the local buffer (not the display!)\n  len\t\tlength of the line in pixel, len must not be 0\n  dir\t\t0: horizontal line (left to right)\n\t\t1: vertical line (top to bottom)\n  asumption: \n    all clipping done\n*/\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nvoid u8g2_ll_hvline_horizontal_right_lsb(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    if(dir == 0) {\n        do {\n            u8g2_draw_pixel_horizontal_right_lsb(u8g2, x, y);\n            x++;\n            len--;\n        } while(len != 0);\n    } else {\n        do {\n            u8g2_draw_pixel_horizontal_right_lsb(u8g2, x, y);\n            y++;\n            len--;\n        } while(len != 0);\n    }\n}\n\n#endif /* U8G2_WITH_HVLINE_SPEED_OPTIMIZATION */\n"
  },
  {
    "path": "lib/u8g2/u8g2_setup.c",
    "content": "/*\n\n  u8g2_setup.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8g2.h\"\n#include <string.h>\n#include <assert.h>\n\n/*============================================*/\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n\nvoid u8g2_SetMaxClipWindow(u8g2_t* u8g2) {\n    u8g2->clip_x0 = 0;\n    u8g2->clip_y0 = 0;\n    u8g2->clip_x1 = (u8g2_uint_t) ~(u8g2_uint_t)0;\n    u8g2->clip_y1 = (u8g2_uint_t) ~(u8g2_uint_t)0;\n\n    u8g2->cb->update_page_win(u8g2);\n}\n\nvoid u8g2_SetClipWindow(\n    u8g2_t* u8g2,\n    u8g2_uint_t clip_x0,\n    u8g2_uint_t clip_y0,\n    u8g2_uint_t clip_x1,\n    u8g2_uint_t clip_y1) {\n    u8g2->clip_x0 = clip_x0;\n    u8g2->clip_y0 = clip_y0;\n    u8g2->clip_x1 = clip_x1;\n    u8g2->clip_y1 = clip_y1;\n    u8g2->cb->update_page_win(u8g2);\n}\n#endif\n\n/*============================================*/\n/*\n  This procedure is called after setting up the display (u8x8 structure).\n  --> This is the central init procedure for u8g2 object\n*/\nvoid u8g2_SetupBuffer(\n    u8g2_t* u8g2,\n    uint8_t* buf,\n    uint8_t tile_buf_height,\n    u8g2_draw_ll_hvline_cb ll_hvline_cb,\n    const u8g2_cb_t* u8g2_cb) {\n    u8g2->font = NULL;\n    //u8g2->kerning = NULL;\n    //u8g2->get_kerning_cb = u8g2_GetNullKerning;\n\n    //u8g2->ll_hvline = u8g2_ll_hvline_vertical_top_lsb;\n    u8g2->ll_hvline = ll_hvline_cb;\n\n    u8g2->tile_buf_ptr = buf;\n    u8g2->tile_buf_height = tile_buf_height;\n\n    u8g2->tile_curr_row = 0;\n\n    u8g2->font_decode.is_transparent = 0; /* issue 443 */\n    u8g2->bitmap_transparency = 0;\n\n    u8g2->draw_color = 1;\n    u8g2->is_auto_page_clear = 1;\n\n    u8g2->cb = u8g2_cb;\n    u8g2->cb->update_dimension(u8g2);\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    u8g2_SetMaxClipWindow(u8g2); /* assign a clip window and call the update() procedure */\n#else\n    u8g2->cb->update_page_win(u8g2);\n#endif\n\n    u8g2_SetFontPosBaseline(u8g2); /* issue 195 */\n\n#ifdef U8G2_WITH_FONT_ROTATION\n    u8g2->font_decode.dir = 0;\n#endif\n}\n\n/*\n  Usually the display rotation is set initially, but it could be done later also\n  u8g2_cb can be U8G2_R0..U8G2_R3\n*/\nvoid u8g2_SetDisplayRotation(u8g2_t* u8g2, const u8g2_cb_t* u8g2_cb) {\n    u8g2->cb = u8g2_cb;\n    u8g2->cb->update_dimension(u8g2);\n    u8g2->cb->update_page_win(u8g2);\n}\n\n/*============================================*/\n\nvoid u8g2_SendF(u8g2_t* u8g2, const char* fmt, ...) {\n    va_list va;\n    va_start(va, fmt);\n    u8x8_cad_vsendf(u8g2_GetU8x8(u8g2), fmt, va);\n    va_end(va);\n}\n\n/*============================================*/\n/* \n  update dimension: \n  calculate the following variables:\n    u8g2_uint_t buf_x0;\tleft corner of the buffer\n    u8g2_uint_t buf_x1;\tright corner of the buffer (excluded)\n    u8g2_uint_t buf_y0;\n    u8g2_uint_t buf_y1; \t\n*/\n\nstatic void u8g2_update_dimension_common(u8g2_t* u8g2) {\n    const u8x8_display_info_t* display_info = u8g2_GetU8x8(u8g2)->display_info;\n    u8g2_uint_t t;\n\n    t = u8g2->tile_buf_height;\n    t *= 8;\n    u8g2->pixel_buf_height = t;\n\n    t = display_info->tile_width;\n#ifndef U8G2_16BIT\n    if(t >= 32) t = 31;\n#endif\n    t *= 8;\n    u8g2->pixel_buf_width = t;\n\n    t = u8g2->tile_curr_row;\n    t *= 8;\n    u8g2->pixel_curr_row = t;\n\n    t = u8g2->tile_buf_height;\n    /* handle the case, where the buffer is larger than the (remaining) part of the display */\n    if(t + u8g2->tile_curr_row > display_info->tile_height)\n        t = display_info->tile_height - u8g2->tile_curr_row;\n    t *= 8;\n\n    u8g2->buf_y0 = u8g2->pixel_curr_row;\n    u8g2->buf_y1 = u8g2->buf_y0;\n    u8g2->buf_y1 += t;\n\n#ifdef U8G2_16BIT\n    u8g2->width = display_info->pixel_width;\n    u8g2->height = display_info->pixel_height;\n#else\n    u8g2->width = 240;\n    if(display_info->pixel_width <= 240) u8g2->width = display_info->pixel_width;\n    u8g2->height = display_info->pixel_height;\n#endif\n}\n\n/*==========================================================*/\n/* apply clip window */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\nstatic void u8g2_apply_clip_window(u8g2_t* u8g2) {\n    /* check aganst the current user_??? window */\n    if(u8g2_IsIntersection(u8g2, u8g2->clip_x0, u8g2->clip_y0, u8g2->clip_x1, u8g2->clip_y1) ==\n       0) {\n        u8g2->is_page_clip_window_intersection = 0;\n    } else {\n        u8g2->is_page_clip_window_intersection = 1;\n\n        if(u8g2->user_x0 < u8g2->clip_x0) u8g2->user_x0 = u8g2->clip_x0;\n        if(u8g2->user_x1 > u8g2->clip_x1) u8g2->user_x1 = u8g2->clip_x1;\n        if(u8g2->user_y0 < u8g2->clip_y0) u8g2->user_y0 = u8g2->clip_y0;\n        if(u8g2->user_y1 > u8g2->clip_y1) u8g2->user_y1 = u8g2->clip_y1;\n    }\n}\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n\n/*==========================================================*/\n\nvoid u8g2_update_dimension_r0(u8g2_t* u8g2) {\n    u8g2_update_dimension_common(u8g2);\n}\n\nvoid u8g2_update_page_win_r0(u8g2_t* u8g2) {\n    u8g2->user_x0 = 0;\n    u8g2->user_x1 = u8g2->width; /* pixel_buf_width replaced with width */\n\n    u8g2->user_y0 = u8g2->buf_y0;\n    u8g2->user_y1 = u8g2->buf_y1;\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    u8g2_apply_clip_window(u8g2);\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n}\n\nvoid u8g2_update_dimension_r1(u8g2_t* u8g2) {\n    u8g2_update_dimension_common(u8g2);\n\n    u8g2->height = u8g2_GetU8x8(u8g2)->display_info->pixel_width;\n    u8g2->width = u8g2_GetU8x8(u8g2)->display_info->pixel_height;\n}\n\nvoid u8g2_update_page_win_r1(u8g2_t* u8g2) {\n    u8g2->user_x0 = u8g2->buf_y0;\n    u8g2->user_x1 = u8g2->buf_y1;\n\n    u8g2->user_y0 = 0;\n    u8g2->user_y1 =\n        u8g2->height; /* pixel_buf_width replaced with height (which is the real pixel width) */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    u8g2_apply_clip_window(u8g2);\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n}\n\nvoid u8g2_update_dimension_r2(u8g2_t* u8g2) {\n    u8g2_update_dimension_common(u8g2);\n}\n\nvoid u8g2_update_page_win_r2(u8g2_t* u8g2) {\n    u8g2->user_x0 = 0;\n    u8g2->user_x1 = u8g2->width; /* pixel_buf_width replaced with width */\n\n    /* there are ases where the height is not a multiple of 8. */\n    /* in such a case u8g2->buf_y1 might be heigher than u8g2->height */\n    u8g2->user_y0 = 0;\n    if(u8g2->height >= u8g2->buf_y1) u8g2->user_y0 = u8g2->height - u8g2->buf_y1;\n    u8g2->user_y1 = u8g2->height - u8g2->buf_y0;\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    u8g2_apply_clip_window(u8g2);\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n}\n\nvoid u8g2_update_dimension_r3(u8g2_t* u8g2) {\n    u8g2_update_dimension_common(u8g2);\n\n    u8g2->height = u8g2_GetU8x8(u8g2)->display_info->pixel_width;\n    u8g2->width = u8g2_GetU8x8(u8g2)->display_info->pixel_height;\n}\n\nvoid u8g2_update_page_win_r3(u8g2_t* u8g2) {\n    /* there are ases where the height is not a multiple of 8. */\n    /* in such a case u8g2->buf_y1 might be heigher than u8g2->width */\n    u8g2->user_x0 = 0;\n    if(u8g2->width >= u8g2->buf_y1) u8g2->user_x0 = u8g2->width - u8g2->buf_y1;\n    u8g2->user_x1 = u8g2->width - u8g2->buf_y0;\n\n    u8g2->user_y0 = 0;\n    u8g2->user_y1 = u8g2->height; /* pixel_buf_width replaced with height (pixel_width) */\n\n#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT\n    u8g2_apply_clip_window(u8g2);\n#endif /* U8G2_WITH_CLIP_WINDOW_SUPPORT */\n}\n\n/*============================================*/\nextern void u8g2_draw_hv_line_2dir(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir);\n\nvoid u8g2_draw_l90_r0(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir) {\n#ifdef __unix\n    assert(dir <= 1);\n#endif\n    u8g2_draw_hv_line_2dir(u8g2, x, y, len, dir);\n}\n\nvoid u8g2_draw_l90_mirrorr_r0(\n    u8g2_t* u8g2,\n    u8g2_uint_t x,\n    u8g2_uint_t y,\n    u8g2_uint_t len,\n    uint8_t dir) {\n    u8g2_uint_t xx;\n    xx = u8g2->width;\n    xx -= x;\n    if((dir & 1) == 0) {\n        xx -= len;\n    } else {\n        xx--;\n    }\n    u8g2_draw_hv_line_2dir(u8g2, xx, y, len, dir);\n}\n\n/* dir = 0 or 1 */\nvoid u8g2_draw_l90_r1(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir) {\n    u8g2_uint_t xx, yy;\n\n#ifdef __unix\n    assert(dir <= 1);\n#endif\n\n    yy = x;\n\n    xx = u8g2->height;\n    xx -= y;\n    xx--;\n\n    dir++;\n    if(dir == 2) {\n        xx -= len;\n        xx++;\n        dir = 0;\n    }\n\n    u8g2_draw_hv_line_2dir(u8g2, xx, yy, len, dir);\n}\n\nvoid u8g2_draw_l90_r2(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir) {\n    u8g2_uint_t xx, yy;\n\n    /*\n  yy = u8g2->height;\n  yy -= y;\n  yy--;\n  \n  xx = u8g2->width;\n  xx -= x;\n  xx--;\n  \n  if ( dir == 0 )\n  {\n    xx -= len;\n    xx++;\n  }\n  else if ( dir == 1 )\n  {\n    yy -= len;\n    yy++;\n  }\n  */\n\n    yy = u8g2->height;\n    yy -= y;\n\n    xx = u8g2->width;\n    xx -= x;\n\n    if(dir == 0) {\n        yy--;\n        xx -= len;\n    } else if(dir == 1) {\n        xx--;\n        yy -= len;\n    }\n\n    u8g2_draw_hv_line_2dir(u8g2, xx, yy, len, dir);\n}\n\nvoid u8g2_draw_l90_r3(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len, uint8_t dir) {\n    u8g2_uint_t xx, yy;\n\n    xx = y;\n\n    yy = u8g2->width;\n    yy -= x;\n\n    if(dir == 0) {\n        yy--;\n        yy -= len;\n        yy++;\n        dir = 1;\n    } else {\n        yy--;\n        dir = 0;\n    }\n\n    u8g2_draw_hv_line_2dir(u8g2, xx, yy, len, dir);\n}\n\n/*============================================*/\nconst u8g2_cb_t u8g2_cb_r0 = {u8g2_update_dimension_r0, u8g2_update_page_win_r0, u8g2_draw_l90_r0};\nconst u8g2_cb_t u8g2_cb_r1 = {u8g2_update_dimension_r1, u8g2_update_page_win_r1, u8g2_draw_l90_r1};\nconst u8g2_cb_t u8g2_cb_r2 = {u8g2_update_dimension_r2, u8g2_update_page_win_r2, u8g2_draw_l90_r2};\nconst u8g2_cb_t u8g2_cb_r3 = {u8g2_update_dimension_r3, u8g2_update_page_win_r3, u8g2_draw_l90_r3};\n\nconst u8g2_cb_t u8g2_cb_mirror = {\n    u8g2_update_dimension_r0,\n    u8g2_update_page_win_r0,\n    u8g2_draw_l90_mirrorr_r0};\n\n/*============================================*/\n/* setup for the null device */\n\n/* setup for the null (empty) device */\nvoid u8g2_Setup_null(\n    u8g2_t* u8g2,\n    const u8g2_cb_t* rotation,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb) {\n    static uint8_t buf[8];\n    u8g2_SetupDisplay(u8g2, u8x8_d_null_cb, u8x8_cad_empty, byte_cb, gpio_and_delay_cb);\n    u8g2_SetupBuffer(u8g2, buf, 1, u8g2_ll_hvline_vertical_top_lsb, rotation);\n}\n"
  },
  {
    "path": "lib/u8g2/u8x8.h",
    "content": "/*\n\n  u8x8.h\n  \n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n  \n  \n  \n  U8glib has several layers. Each layer is implemented with a callback function. \n  This callback function handels the messages for the layer.\n\n  The topmost level is the display layer. It includes the following messages:\n  \n    U8X8_MSG_DISPLAY_SETUP_MEMORY\t\t\tno communicaation with the display, setup memory ony\n    U8X8_MSG_DISPLAY_INIT\n    U8X8_MSG_DISPLAY_SET_FLIP_MODE\n    U8X8_MSG_DISPLAY_SET_POWER_SAVE\n    U8X8_MSG_DISPLAY_SET_CONTRAST\n    U8X8_MSG_DISPLAY_DRAW_TILE\n\n  A display driver may decided to breakdown these messages to a lower level interface or\n  implement this functionality directly.\n  \n\n  One layer is the Command/Arg/Data interface. It can be used by the display layer\n  to communicate with the display hardware.\n  This layer only deals with data, commands and arguments. D/C line is unknown.\n    U8X8_MSG_CAD_INIT\n    U8X8_MSG_CAD_SET_I2C_ADR\t(obsolete)\n    U8X8_MSG_CAD_SET_DEVICE (obsolete)\n    U8X8_MSG_CAD_START_TRANSFER\n    U8X8_MSG_CAD_SEND_CMD\n    U8X8_MSG_CAD_SEND_ARG\n    U8X8_MSG_CAD_SEND_DATA\n    U8X8_MSG_CAD_END_TRANSFER\n    \n  The byte interface is there to send 1 byte (8 bits) to the display hardware.\n  This layer depends on the hardware of a microcontroller, if a specific hardware \n  should be used (I2C or SPI). \n  If this interface is implemented via software, it may use the GPIO level for sending\n  bytes.\n    U8X8_MSG_BYTE_INIT\n    U8X8_MSG_BYTE_SEND 30\n    U8X8_MSG_BYTE_SET_DC 31\n    U8X8_MSG_BYTE_START_TRANSFER\n    U8X8_MSG_BYTE_END_TRANSFER\n    U8X8_MSG_BYTE_SET_I2C_ADR (obsolete)\n    U8X8_MSG_BYTE_SET_DEVICE (obsolete)\n\n  GPIO and Delay\n    U8X8_MSG_GPIO_INIT\n    U8X8_MSG_DELAY_MILLI\n    U8X8_MSG_DELAY_10MICRO\n    U8X8_MSG_DELAY_100NANO\n    U8X8_MSG_DELAY_NANO\n*/\n\n#ifndef U8X8_H\n#define U8X8_H\n\n/*==========================================*/\n/* Global Defines */\n\n/* Undefine this to remove u8x8_SetContrast function */\n// #define U8X8_WITH_SET_CONTRAST\n\n/* Define this for an additional user pointer inside the u8x8 data struct */\n//#define U8X8_WITH_USER_PTR\n\n/* Undefine this to remove u8x8_SetFlipMode function */\n/* 26 May 2016: Obsolete */\n//#define U8X8_WITH_SET_FLIP_MODE\n\n/* Select 0 or 1 for the default flip mode. This is not affected by U8X8_WITH_FLIP_MODE */\n/* Note: Not all display types support a mirror functon for the frame buffer */\n/* 26 May 2016: Obsolete */\n//#define U8X8_DEFAULT_FLIP_MODE 0\n\n/*==========================================*/\n/* Includes */\n\n#include <stdint.h>\n#include <stdarg.h>\n#include <stddef.h>\n#include <limits.h>\n\n#if defined(__GNUC__) && defined(__AVR__)\n#include <avr/pgmspace.h>\n#endif\n\n/*==========================================*/\n/* C++ compatible */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*==========================================*/\n/* U8G2 internal defines */\n\n/* the following macro returns the first value for the normal mode */\n/* or the second argument for the flip mode */\n\n/* 26 May 2016: Obsolete\n#if U8X8_DEFAULT_FLIP_MODE == 0\n#define U8X8_IF_DEFAULT_NORMAL_OR_FLIP(normal, flipmode) (normal)\n#else\n#define U8X8_IF_DEFAULT_NORMAL_OR_FLIP(normal, flipmode) (flipmode)\n#endif\n*/\n\n#ifdef __GNUC__\n#define U8X8_NOINLINE      __attribute__((noinline))\n#define U8X8_SECTION(name) __attribute__((section(name)))\n#define U8X8_UNUSED        __attribute__((unused))\n#else\n#define U8X8_SECTION(name)\n#define U8X8_NOINLINE\n#define U8X8_UNUSED\n#endif\n\n#if defined(__GNUC__) && defined(__AVR__)\n#define U8X8_FONT_SECTION(name) U8X8_SECTION(\".progmem.\" name)\n#define u8x8_pgm_read(adr)      pgm_read_byte_near(adr)\n#define U8X8_PROGMEM            PROGMEM\n#endif\n\n#if defined(ESP8266)\nuint8_t u8x8_pgm_read_esp(const uint8_t* addr); /* u8x8_8x8.c */\n#define U8X8_FONT_SECTION(name) __attribute__((section(\".text.\" name)))\n#define u8x8_pgm_read(adr)      u8x8_pgm_read_esp(adr)\n#define U8X8_PROGMEM\n#endif\n\n#ifndef U8X8_FONT_SECTION\n#define U8X8_FONT_SECTION(name)\n#endif\n\n#ifndef u8x8_pgm_read\n#ifndef CHAR_BIT\n#define u8x8_pgm_read(adr) (*(const uint8_t*)(adr))\n#else\n#if CHAR_BIT > 8\n#define u8x8_pgm_read(adr) ((*(const uint8_t*)(adr)) & 0x0ff)\n#else\n#define u8x8_pgm_read(adr) (*(const uint8_t*)(adr))\n#endif\n#endif\n#endif\n\n#ifndef U8X8_PROGMEM\n#define U8X8_PROGMEM\n#endif\n\n#ifdef ARDUINO\n#define U8X8_USE_PINS\n#endif\n\n/*==========================================*/\n/* U8X8 typedefs and data structures */\n\ntypedef struct u8x8_struct u8x8_t;\ntypedef struct u8x8_display_info_struct u8x8_display_info_t;\ntypedef struct u8x8_tile_struct u8x8_tile_t;\n\ntypedef uint8_t (*u8x8_msg_cb)(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\ntypedef uint16_t (*u8x8_char_cb)(u8x8_t* u8x8, uint8_t b);\n\n//struct u8x8_mcd_struct\n//{\n//  u8x8_msg_cb cb;\t\t/* current callback function */\n//  u8x8_t *u8g2;\t\t/* pointer to the u8g2 parent to minimize the number of args */\n//  u8x8_mcd_t *next;\n//};\n\nstruct u8x8_tile_struct {\n    uint8_t* tile_ptr; /* pointer to one or more tiles... should be \"const\" */\n    uint8_t cnt; /* number of tiles */\n    uint8_t x_pos; /* tile x position */\n    uint8_t y_pos; /* tile x position */\n};\n\nstruct u8x8_display_info_struct {\n    /* == general == */\n\n    uint8_t chip_enable_level; /* UC1601: 0 */\n    uint8_t chip_disable_level; /* opposite of chip_enable_level */\n\n    uint8_t post_chip_enable_wait_ns; /* UC1601: 5ns */\n    uint8_t pre_chip_disable_wait_ns; /* UC1601: 5ns */\n    uint8_t reset_pulse_width_ms; /* UC1601: 0.003ms --> 1ms */\n    uint8_t post_reset_wait_ms; /* UC1601: 6ms  */\n\n    /* == SPI interface == */\n\n    /* after SDA has been applied, wait this much time for the SCK data takeover edge */\n    /* if this is smaller than sck_pulse_width_ns, then use the value from sck_pulse_width_ns */\n    uint8_t sda_setup_time_ns; /* UC1601: 12ns */\n    /* the pulse width of the the clock signal, cycle time is twice this value */\n    /* max freq is 1/(2*sck_pulse_width_ns) */\n    /* AVR: below 70: DIV2, 8 MHz, >= 70 --> 4MHz clock (DIV4) */\n    uint8_t sck_pulse_width_ns; /* UC1701: 50ns */\n\n    /* until here we have 8 bytes (uint8_t). Newly introduced for SPI.beginTransaction */\n    uint32_t sck_clock_hz;\n\n    /* previous name \"sck_takeover_edge\" renamed to \"spi_mode\" */\n    /* bit 0 of spi_mode is equal to the value of the previous variable sck_takeover_edge, 20 Aug 16: This is wrong the bit is actually inverted */\n    /* SPI has four clock modes: */\n    /*   0: clock active high, data out on falling edge, clock default value is zero, takover on rising edge */\n    /*   1: clock active high, data out on rising edge, clock default value is zero, takover on falling edge */\n    /*   2: clock active low, data out on rising edge */\n    /*   3: clock active low, data out on falling edge */\n    /* most displays have clock mode 1 */\n    uint8_t spi_mode;\n\n    /* == I2C == */\n    uint8_t i2c_bus_clock_100kHz; /* UC1601: 1000000000/275 = 37 *100k */\n\n    /* == 8 bit interface == */\n\n    /* how long to wait after all data line are set */\n    uint8_t data_setup_time_ns; /* UC1601: 30ns */\n    /* write enable pulse width */\n    uint8_t write_pulse_width_ns; /* UC1601: 40ns */\n\n    /* == layout == */\n    uint8_t tile_width;\n    uint8_t tile_height;\n\n    uint8_t default_x_offset; /* default x offset for the display */\n    uint8_t flipmode_x_offset; /* x offset, if flip mode is enabled */\n\n    /* pixel width is not used by the u8x8 procedures */\n    /* instead it will be used by the u8g2 procedures, because the pixel dimension can */\n    /* not always be calculated from the tile_width/_height */\n    /* the following conditions must be true: */\n    /* pixel_width <= tile_width*8 */\n    /* pixel_height <= tile_height*8 */\n    uint16_t pixel_width;\n    uint16_t pixel_height;\n};\n\n/* list of U8x8 pins */\n#define U8X8_PIN_D0        0\n#define U8X8_PIN_SPI_CLOCK 0\n#define U8X8_PIN_D1        1\n#define U8X8_PIN_SPI_DATA  1\n#define U8X8_PIN_D2        2\n#define U8X8_PIN_D3        3\n#define U8X8_PIN_D4        4\n#define U8X8_PIN_D5        5\n#define U8X8_PIN_D6        6\n#define U8X8_PIN_D7        7\n\n#define U8X8_PIN_E     8\n#define U8X8_PIN_CS    9 /* parallel, SPI */\n#define U8X8_PIN_DC    10 /* parallel, SPI */\n#define U8X8_PIN_RESET 11 /* parallel, SPI, I2C */\n\n#define U8X8_PIN_I2C_CLOCK 12 /* 1 = Input/high impedance, 0 = drive low */\n#define U8X8_PIN_I2C_DATA  13 /* 1 = Input/high impedance, 0 = drive low */\n\n#define U8X8_PIN_CS1 14 /* KS0108 extra chip select */\n#define U8X8_PIN_CS2 15 /* KS0108 extra chip select */\n\n#define U8X8_PIN_OUTPUT_CNT 16\n\n#define U8X8_PIN_MENU_SELECT 16\n#define U8X8_PIN_MENU_NEXT   17\n#define U8X8_PIN_MENU_PREV   18\n#define U8X8_PIN_MENU_HOME   19\n#define U8X8_PIN_MENU_UP     20\n#define U8X8_PIN_MENU_DOWN   21\n\n#define U8X8_PIN_INPUT_CNT 6\n\n#ifdef U8X8_USE_PINS\n#define U8X8_PIN_CNT  (U8X8_PIN_OUTPUT_CNT + U8X8_PIN_INPUT_CNT)\n#define U8X8_PIN_NONE 255\n#endif\n\nstruct u8x8_struct {\n    const u8x8_display_info_t* display_info;\n    u8x8_char_cb next_cb; /*  procedure, which will be used to get the next char from the string */\n    u8x8_msg_cb display_cb;\n    u8x8_msg_cb cad_cb;\n    u8x8_msg_cb byte_cb;\n    u8x8_msg_cb gpio_and_delay_cb;\n    uint32_t bus_clock; /* can be used by the byte function to store the clock speed of the bus */\n    const uint8_t* font;\n    uint16_t encoding; /* encoding result for utf8 decoder in next_cb */\n    uint8_t x_offset; /* copied from info struct, can be modified in flip mode */\n    uint8_t is_font_inverse_mode; /* 0: normal, 1: font glyphs are inverted */\n    uint8_t\n        i2c_address; /* a valid i2c adr. Initially this is 255, but this is set to something usefull during DISPLAY_INIT */\n    /* i2c_address is the address for writing data to the display */\n    /* usually, the lowest bit must be zero for a valid address */\n    uint8_t i2c_started; /* for i2c interface */\n    //uint8_t device_address;\t/* OBSOLETE???? - this is the device address, replacement for U8X8_MSG_CAD_SET_DEVICE */\n    uint8_t utf8_state; /* number of chars which are still to scan */\n    uint8_t gpio_result; /* return value from the gpio call (only for MENU keys at the moment) */\n    uint8_t debounce_default_pin_state;\n    uint8_t debounce_last_pin_state;\n    uint8_t debounce_state;\n    uint8_t debounce_result_msg; /* result msg or event after debounce */\n#ifdef U8X8_WITH_USER_PTR\n    void* user_ptr;\n#endif\n#ifdef U8X8_USE_PINS\n    uint8_t pins\n        [U8X8_PIN_CNT]; /* defines a pinlist: Mainly a list of pins for the Arduino Envionment, use U8X8_PIN_xxx to access */\n#endif\n};\n\n#ifdef U8X8_WITH_USER_PTR\n#define u8x8_GetUserPtr(u8x8)    ((u8x8)->user_ptr)\n#define u8x8_SetUserPtr(u8x8, p) ((u8x8)->user_ptr = (p))\n#endif\n\n#define u8x8_GetCols(u8x8)                ((u8x8)->display_info->tile_width)\n#define u8x8_GetRows(u8x8)                ((u8x8)->display_info->tile_height)\n#define u8x8_GetI2CAddress(u8x8)          ((u8x8)->i2c_address)\n#define u8x8_SetI2CAddress(u8x8, address) ((u8x8)->i2c_address = (address))\n\n#define u8x8_SetGPIOResult(u8x8, val) ((u8x8)->gpio_result = (val))\n#define u8x8_GetSPIClockPhase(u8x8) \\\n    ((u8x8)->display_info->spi_mode & 0x01) /* 0 means rising edge */\n#define u8x8_GetSPIClockPolarity(u8x8)     (((u8x8)->display_info->spi_mode & 0x02) >> 1)\n#define u8x8_GetSPIClockDefaultLevel(u8x8) (((u8x8)->display_info->spi_mode & 0x02) >> 1)\n\n#define u8x8_GetFontCharWidth(u8x8)  u8x8_pgm_read((u8x8)->font + 2)\n#define u8x8_GetFontCharHeight(u8x8) u8x8_pgm_read((u8x8)->font + 3)\n\n#ifdef U8X8_USE_PINS\n#define u8x8_SetPin(u8x8, pin, val)      (u8x8)->pins[pin] = (val)\n#define u8x8_SetMenuSelectPin(u8x8, val) u8x8_SetPin((u8x8), U8X8_PIN_MENU_SELECT, (val))\n#define u8x8_SetMenuNextPin(u8x8, val)   u8x8_SetPin((u8x8), U8X8_PIN_MENU_NEXT, (val))\n#define u8x8_SetMenuPrevPin(u8x8, val)   u8x8_SetPin((u8x8), U8X8_PIN_MENU_PREV, (val))\n#define u8x8_SetMenuHomePin(u8x8, val)   u8x8_SetPin((u8x8), U8X8_PIN_MENU_HOME, (val))\n#define u8x8_SetMenuUpPin(u8x8, val)     u8x8_SetPin((u8x8), U8X8_PIN_MENU_UP, (val))\n#define u8x8_SetMenuDownPin(u8x8, val)   u8x8_SetPin((u8x8), U8X8_PIN_MENU_DOWN, (val))\n#endif\n\n/*==========================================*/\n/* u8log extension for u8x8 and u8g2 */\n\ntypedef struct u8log_struct u8log_t;\n\n/* redraw the specified line. */\ntypedef void (*u8log_cb)(u8log_t* u8log);\n\nstruct u8log_struct {\n    /* configuration */\n    void* aux_data; /* pointer to u8x8 or u8g2 */\n    uint8_t width, height; /* size of the terminal */\n    u8log_cb cb; /* callback redraw function */\n    uint8_t* screen_buffer; /* size must be width*heigh bytes */\n    uint8_t is_redraw_line_for_each_char;\n    int8_t line_height_offset; /* extra offset for the line height (u8g2 only) */\n\n    /* internal data */\n    //uint8_t last_x, last_y;\t/* position of the last printed char */\n    uint8_t cursor_x, cursor_y; /* position of the cursor, might be off screen */\n    uint8_t redraw_line; /* redraw specific line if is_redraw_line is not 0 */\n    uint8_t is_redraw_line;\n    uint8_t is_redraw_all;\n    uint8_t is_redraw_all_required_for_next_nl; /* in nl mode, redraw all instead of current line */\n};\n\n/*==========================================*/\n\n/* helper functions */\nvoid u8x8_d_helper_display_setup_memory(u8x8_t* u8x8, const u8x8_display_info_t* display_info);\nvoid u8x8_d_helper_display_init(u8x8_t* u8g2);\n\n/* Display Interface */\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_SETUP_MEMORY\n  Args:\tNone\n  Tasks:\n    1) setup u8g2->display_info\n      copy u8g2->display_info->default_x_offset to u8g2->x_offset\n      \n   usually calls u8x8_d_helper_display_setup_memory()\n*/\n#define U8X8_MSG_DISPLAY_SETUP_MEMORY 9\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_INIT\n  Args:\tNone\n  Tasks:\n\n    2) put interface into default state: \n\t  execute u8x8_gpio_Init for port directions\n\t  execute u8x8_cad_Init for default port levels\n    3) set CS status (not clear, may be done in cad/byte interface\n    4) execute display reset (gpio interface)\n    5) send setup sequence to display, do not activate display, disable \"power save\" will follow \n*/\n#define U8X8_MSG_DISPLAY_INIT 10\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_SET_POWER_SAVE\n  Args:\targ_int: 0: normal mode (RAM is visible on the display), 1: nothing is shown\n  Tasks:\n    Depending on arg_int, put the display into normal or power save mode.\n    Send the corresponding sequence to the display.\n    In power save mode, it must be possible to modify the RAM content.\n*/\n#define U8X8_MSG_DISPLAY_SET_POWER_SAVE 11\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_SET_FLIP_MODE\n  Args:\targ_int: 0: normal mode, 1: flipped HW screen (180 degree)\n  Tasks:\n    Reprogramms the display controller to rotate the display by \n    180 degree (arg_int = 1) or not (arg_int = 0)\n    This may change u8g2->x_offset if the display is smaller than the controller ram\n    This message should only be supported if U8X8_WITH_FLIP_MODE is defined.\n*/\n#define U8X8_MSG_DISPLAY_SET_FLIP_MODE 13\n\n/*  arg_int: 0..255 contrast value */\n#define U8X8_MSG_DISPLAY_SET_CONTRAST 14\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_DRAW_TILE\n  Args:\t\n    arg_int: How often to repeat this tile pattern\n    arg_ptr: pointer to u8x8_tile_t\n        uint8_t *tile_ptr;\tpointer to one or more tiles (number is \"cnt\")\n\tuint8_t cnt;\t\tnumber of tiles\n\tuint8_t x_pos;\t\tfirst tile x position\n\tuint8_t y_pos;\t\tfirst tile y position \n  Tasks:\n    One tile has exactly 8 bytes (8x8 pixel monochrome bitmap). \n    The lowest bit of the first byte is the upper left corner\n    The highest bit of the first byte is the lower left corner\n    The lowest bit of the last byte is the upper right corner\n    The highest bit of the last byte is the lower left corner\n    \"tile_ptr\" is the address of a memory area, which contains\n    one or more tiles. \"cnt\" will contain the exact number of\n    tiles in the memory areay. The size of the memory area is 8*cnt;\n    Multiple tiles in the memory area form a horizontal sequence, this \n    means the first tile is drawn at x_pos/y_pos, the second tile is drawn\n    at x_pos+1/y_pos, third at x_pos+2/y_pos.\n    \"arg_int\" tells how often the tile sequence should be repeated:\n    For example if \"cnt\" is two and tile_ptr points to tiles A and B,\n    then for arg_int = 3, the following tile sequence will be drawn:\n    ABABAB. Totally, cnt*arg_int tiles will be drawn. \n        \n*/\n#define U8X8_MSG_DISPLAY_DRAW_TILE 15\n\n/*\n  Name: \tU8X8_MSG_DISPLAY_REFRESH\n  Args:\t\n    arg_int: -\n    arg_ptr: -\n  \n  This was introduced for the SSD1606 eInk display.\n  The problem is, that all RAM access will not appear on the screen\n  unless a special command is executed. With this message, this command\n  sequence is executed.\n  Use\n    void u8x8_RefreshDisplay(u8x8_t *u8x8)\n  to send the message to the display handler.\n*/\n#define U8X8_MSG_DISPLAY_REFRESH 16\n\n/*==========================================*/\n/* u8x8_setup.c */\n\nuint8_t u8x8_dummy_cb(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\n/* \n  Setup u8x8 object itself. This should be the very first function \n  called on the new u8x8 object. After this call, assign the callback\n  functions. Optional: Set the pins \n*/\n\nvoid u8x8_SetupDefaults(u8x8_t* u8x8); /* do not use this, use u8x8_Setup() instead */\n\nvoid u8x8_Setup(\n    u8x8_t* u8x8,\n    u8x8_msg_cb display_cb,\n    u8x8_msg_cb cad_cb,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb);\n\n/*==========================================*/\n/* u8x8_display.c */\nuint8_t u8x8_DrawTile(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t cnt, uint8_t* tile_ptr);\n\n/* \n  After a call to u8x8_SetupDefaults, \n  setup u8x8 memory structures & inform callbacks \n  This function is also called from u8x8_Setup(), so do not call u8x8_SetupMemory()\n  directly, but use u8x8_Setup() instead.\n*/\nvoid u8x8_SetupMemory(u8x8_t* u8x8);\n\n/*\n  After calling u8x8_SetupMemory()/u8x8_Setup(), init the display hardware itself.\n  This will will the first time, u8x8 talks to the display.\n  It will init the display, but keep display in power save mode. \n  Usually this command must be followed by u8x8_SetPowerSave() \n*/\nvoid u8x8_InitDisplay(u8x8_t* u8x8);\n/* wake up display from power save mode */\nvoid u8x8_SetPowerSave(u8x8_t* u8x8, uint8_t is_enable);\nvoid u8x8_SetFlipMode(u8x8_t* u8x8, uint8_t mode);\nvoid u8x8_SetContrast(u8x8_t* u8x8, uint8_t value);\nvoid u8x8_ClearDisplayWithTile(u8x8_t* u8x8, const uint8_t* buf) U8X8_NOINLINE;\nvoid u8x8_ClearDisplay(u8x8_t* u8x8); // this does not work for u8g2 in some cases\nvoid u8x8_FillDisplay(u8x8_t* u8x8);\nvoid u8x8_RefreshDisplay(\n    u8x8_t* u8x8); // make RAM content visible on the display (Dec 16: SSD1606 only)\nvoid u8x8_ClearLine(u8x8_t* u8x8, uint8_t line);\n\n/*==========================================*/\n/* Command Arg Data (CAD) Interface */\n\n/*\n  U8X8_MSG_CAD_INIT\n    no args\n    call U8X8_MSG_BYTE_INIT\n    setup default values for the I/O lines\n*/\n#define U8X8_MSG_CAD_INIT 20\n\n#define U8X8_MSG_CAD_SEND_CMD       21\n/*  arg_int: cmd byte */\n#define U8X8_MSG_CAD_SEND_ARG       22\n/*  arg_int: arg byte */\n#define U8X8_MSG_CAD_SEND_DATA      23\n/* arg_int: expected cs level after processing this msg */\n#define U8X8_MSG_CAD_START_TRANSFER 24\n/* arg_int: expected cs level after processing this msg */\n#define U8X8_MSG_CAD_END_TRANSFER   25\n/* arg_int = 0: disable chip, arg_int = 1: enable chip */\n//#define U8X8_MSG_CAD_SET_I2C_ADR 26\n//#define U8X8_MSG_CAD_SET_DEVICE 27\n\n/* u8g_cad.c */\n\n#define u8x8_cad_Init(u8x8) ((u8x8)->cad_cb((u8x8), U8X8_MSG_CAD_INIT, 0, NULL))\n\nuint8_t u8x8_cad_SendCmd(u8x8_t* u8x8, uint8_t cmd) U8X8_NOINLINE;\nuint8_t u8x8_cad_SendArg(u8x8_t* u8x8, uint8_t arg) U8X8_NOINLINE;\nuint8_t u8x8_cad_SendMultipleArg(u8x8_t* u8x8, uint8_t cnt, uint8_t arg) U8X8_NOINLINE;\nuint8_t u8x8_cad_SendData(u8x8_t* u8x8, uint8_t cnt, uint8_t* data) U8X8_NOINLINE;\nuint8_t u8x8_cad_StartTransfer(u8x8_t* u8x8) U8X8_NOINLINE;\nuint8_t u8x8_cad_EndTransfer(u8x8_t* u8x8) U8X8_NOINLINE;\nvoid u8x8_cad_vsendf(u8x8_t* u8x8, const char* fmt, va_list va);\nvoid u8x8_SendF(u8x8_t* u8x8, const char* fmt, ...);\n\n/*\n#define U8X8_C(c0)\t\t\t\t(0x04), (c0)\n#define U8X8_CA(c0,a0)\t\t\t(0x05), (c0), (a0)\n#define U8X8_CAA(c0,a0,a1)\t\t(0x06), (c0), (a0), (a1)\n#define U8X8_DATA()\t\t\t(0x10)\n#define U8X8_D1(d0)\t\t\t(0x11), (d0)\n*/\n\n#define U8X8_C(c0)      (U8X8_MSG_CAD_SEND_CMD), (c0)\n#define U8X8_A(a0)      (U8X8_MSG_CAD_SEND_ARG), (a0)\n#define U8X8_CA(c0, a0) (U8X8_MSG_CAD_SEND_CMD), (c0), (U8X8_MSG_CAD_SEND_ARG), (a0)\n#define U8X8_CAA(c0, a0, a1) \\\n    (U8X8_MSG_CAD_SEND_CMD), (c0), (U8X8_MSG_CAD_SEND_ARG), (a0), (U8X8_MSG_CAD_SEND_ARG), (a1)\n#define U8X8_CAAA(c0, a0, a1, a2)                                                                \\\n    (U8X8_MSG_CAD_SEND_CMD), (c0), (U8X8_MSG_CAD_SEND_ARG), (a0), (U8X8_MSG_CAD_SEND_ARG), (a1), \\\n        (U8X8_MSG_CAD_SEND_ARG), (a2)\n#define U8X8_CAAAA(c0, a0, a1, a2, a3)                                                           \\\n    (U8X8_MSG_CAD_SEND_CMD), (c0), (U8X8_MSG_CAD_SEND_ARG), (a0), (U8X8_MSG_CAD_SEND_ARG), (a1), \\\n        (U8X8_MSG_CAD_SEND_ARG), (a2), (U8X8_MSG_CAD_SEND_ARG), (a3)\n#define U8X8_AAC(a0, a1, c0) \\\n    (U8X8_MSG_CAD_SEND_ARG), (a0), (U8X8_MSG_CAD_SEND_ARG), (a1), (U8X8_MSG_CAD_SEND_CMD), (c0)\n#define U8X8_D1(d0) (U8X8_MSG_CAD_SEND_DATA), (d0)\n\n#define U8X8_A4(a0, a1, a2, a3) U8X8_A(a0), U8X8_A(a1), U8X8_A(a2), U8X8_A(a3)\n#define U8X8_A8(a0, a1, a2, a3, a4, a5, a6, a7) \\\n    U8X8_A4((a0), (a1), (a2), (a3)), U8X8_A4((a4), (a5), (a6), (a7))\n\n#define U8X8_START_TRANSFER() (U8X8_MSG_CAD_START_TRANSFER)\n#define U8X8_END_TRANSFER()   (U8X8_MSG_CAD_END_TRANSFER)\n#define U8X8_DLY(m)           (0xfe), (m) /* delay in milli seconds */\n#define U8X8_END()            (0xff)\n\nvoid u8x8_cad_SendSequence(u8x8_t* u8x8, uint8_t const* data);\nuint8_t u8x8_cad_empty(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_110(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_001(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_011(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_100(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_st7920_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_ssd13xx_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_ssd13xx_fast_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_st75256_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_ld7032_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_cad_uc16xx_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\n/*==========================================*/\n/* Byte Interface */\n\n#define U8X8_MSG_BYTE_INIT   U8X8_MSG_CAD_INIT\n#define U8X8_MSG_BYTE_SET_DC 32\n\n#define U8X8_MSG_BYTE_SEND U8X8_MSG_CAD_SEND_DATA\n\n#define U8X8_MSG_BYTE_START_TRANSFER U8X8_MSG_CAD_START_TRANSFER\n#define U8X8_MSG_BYTE_END_TRANSFER   U8X8_MSG_CAD_END_TRANSFER\n\n//#define U8X8_MSG_BYTE_SET_I2C_ADR U8X8_MSG_CAD_SET_I2C_ADR\n//#define U8X8_MSG_BYTE_SET_DEVICE U8X8_MSG_CAD_SET_DEVICE\n\nuint8_t u8x8_byte_SetDC(u8x8_t* u8x8, uint8_t dc) U8X8_NOINLINE;\nuint8_t u8x8_byte_SendByte(u8x8_t* u8x8, uint8_t byte) U8X8_NOINLINE;\nuint8_t u8x8_byte_SendBytes(u8x8_t* u8x8, uint8_t cnt, uint8_t* data) U8X8_NOINLINE;\nuint8_t u8x8_byte_StartTransfer(u8x8_t* u8x8);\nuint8_t u8x8_byte_EndTransfer(u8x8_t* u8x8);\n\nuint8_t u8x8_byte_empty(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_4wire_sw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_8bit_6800mode(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_8bit_8080mode(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_3wire_sw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n/* uint8_t u8x8_byte_st7920_sw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); */\nvoid u8x8_byte_set_ks0108_cs(u8x8_t* u8x8, uint8_t arg) U8X8_NOINLINE;\nuint8_t u8x8_byte_ks0108(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_ssd13xx_sw_i2c(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    uint8_t arg_int,\n    void* arg_ptr); /* OBSOLETE! */\nuint8_t u8x8_byte_sw_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_byte_sed1520(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\n/*==========================================*/\n/* GPIO Interface */\n\n/*\n  U8X8_MSG_GPIO_AND_DELAY_INIT\n  no args\n  setup port directions, do not set IO levels, this is done with BYTE/CAD_INIT\n*/\n#define U8X8_MSG_GPIO_AND_DELAY_INIT 40\n\n/* arg_int: milliseconds */\n#define U8X8_MSG_DELAY_MILLI 41\n\n/* 10MICRO and 100NANO are not used at the moment */\n#define U8X8_MSG_DELAY_10MICRO 42\n#define U8X8_MSG_DELAY_100NANO 43\n\n#define U8X8_MSG_DELAY_NANO 44\n/* delay of one i2c unit, should be 5us for 100K, and 1.25us for 400K */\n#define U8X8_MSG_DELAY_I2C  45\n\n#define U8X8_MSG_GPIO(x) (64 + (x))\n#ifdef U8X8_USE_PINS\n#define u8x8_GetPinIndex(u8x8, msg) ((msg) & 0x3f)\n#define u8x8_GetPinValue(u8x8, msg) ((u8x8)->pins[(msg) & 0x3f])\n#endif\n\n#define U8X8_MSG_GPIO_D0        U8X8_MSG_GPIO(U8X8_PIN_D0)\n#define U8X8_MSG_GPIO_SPI_CLOCK U8X8_MSG_GPIO(U8X8_PIN_SPI_CLOCK)\n#define U8X8_MSG_GPIO_D1        U8X8_MSG_GPIO(U8X8_PIN_D1)\n#define U8X8_MSG_GPIO_SPI_DATA  U8X8_MSG_GPIO(U8X8_PIN_SPI_DATA)\n#define U8X8_MSG_GPIO_D2        U8X8_MSG_GPIO(U8X8_PIN_D2)\n#define U8X8_MSG_GPIO_D3        U8X8_MSG_GPIO(U8X8_PIN_D3)\n#define U8X8_MSG_GPIO_D4        U8X8_MSG_GPIO(U8X8_PIN_D4)\n#define U8X8_MSG_GPIO_D5        U8X8_MSG_GPIO(U8X8_PIN_D5)\n#define U8X8_MSG_GPIO_D6        U8X8_MSG_GPIO(U8X8_PIN_D6)\n#define U8X8_MSG_GPIO_D7        U8X8_MSG_GPIO(U8X8_PIN_D7)\n#define U8X8_MSG_GPIO_E         U8X8_MSG_GPIO(U8X8_PIN_E) // used as E1 for the SED1520\n#define U8X8_MSG_GPIO_CS        U8X8_MSG_GPIO(U8X8_PIN_CS) // used as E2 for the SED1520\n#define U8X8_MSG_GPIO_DC        U8X8_MSG_GPIO(U8X8_PIN_DC)\n#define U8X8_MSG_GPIO_RESET     U8X8_MSG_GPIO(U8X8_PIN_RESET)\n#define U8X8_MSG_GPIO_I2C_CLOCK U8X8_MSG_GPIO(U8X8_PIN_I2C_CLOCK)\n#define U8X8_MSG_GPIO_I2C_DATA  U8X8_MSG_GPIO(U8X8_PIN_I2C_DATA)\n\n#define U8X8_MSG_GPIO_CS1 U8X8_MSG_GPIO(U8X8_PIN_CS1) /* KS0108 extra chip select */\n#define U8X8_MSG_GPIO_CS2 U8X8_MSG_GPIO(U8X8_PIN_CS2) /* KS0108 extra chip select */\n\n/* these message expect the return value in u8x8->gpio_result */\n#define U8X8_MSG_GPIO_MENU_SELECT U8X8_MSG_GPIO(U8X8_PIN_MENU_SELECT)\n#define U8X8_MSG_GPIO_MENU_NEXT   U8X8_MSG_GPIO(U8X8_PIN_MENU_NEXT)\n#define U8X8_MSG_GPIO_MENU_PREV   U8X8_MSG_GPIO(U8X8_PIN_MENU_PREV)\n#define U8X8_MSG_GPIO_MENU_HOME   U8X8_MSG_GPIO(U8X8_PIN_MENU_HOME)\n#define U8X8_MSG_GPIO_MENU_UP     U8X8_MSG_GPIO(U8X8_PIN_MENU_UP)\n#define U8X8_MSG_GPIO_MENU_DOWN   U8X8_MSG_GPIO(U8X8_PIN_MENU_DOWN)\n\n#define u8x8_gpio_Init(u8x8) \\\n    ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_AND_DELAY_INIT, 0, NULL))\n\n/*\n#define u8x8_gpio_SetDC(u8x8, v) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_DC, (v), NULL ))\n#define u8x8_gpio_SetCS(u8x8, v) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_CS, (v), NULL ))\n#define u8x8_gpio_SetReset(u8x8, v) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_RESET, (v), NULL ))\n*/\n\n#define u8x8_gpio_SetDC(u8x8, v)       u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_DC, (v))\n#define u8x8_gpio_SetCS(u8x8, v)       u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_CS, (v))\n#define u8x8_gpio_SetReset(u8x8, v)    u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_RESET, (v))\n#define u8x8_gpio_SetSPIClock(u8x8, v) u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_SPI_CLOCK, (v))\n#define u8x8_gpio_SetSPIData(u8x8, v)  u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_SPI_DATA, (v))\n#define u8x8_gpio_SetI2CClock(u8x8, v) u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_I2C_CLOCK, (v))\n#define u8x8_gpio_SetI2CData(u8x8, v)  u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_I2C_DATA, (v))\n\nvoid u8x8_gpio_call(u8x8_t* u8x8, uint8_t msg, uint8_t arg) U8X8_NOINLINE;\n\n#define u8x8_gpio_Delay(u8x8, msg, dly) u8x8_gpio_call((u8x8), (msg), (dly))\n//void u8x8_gpio_Delay(u8x8_t *u8x8, uint8_t msg, uint8_t dly) U8X8_NOINLINE;\n\n/*==========================================*/\n/* u8x8_debounce.c */\n/* return U8X8_MSG_GPIO_MENU_xxxxx messages */\nuint8_t u8x8_GetMenuEvent(u8x8_t* u8x8);\n\n/*==========================================*/\n/* u8x8_d_stdio.c */\nvoid u8x8_SetupStdio(u8x8_t* u8x8);\n\n/*==========================================*/\n/* u8x8_d_sdl_128x64.c */\nvoid u8x8_Setup_SDL_128x64(u8x8_t* u8x8);\nvoid u8x8_Setup_SDL_240x160(u8x8_t* u8x8);\nint u8g_sdl_get_key(void);\n\n/*==========================================*/\n/* u8x8_d_tga.c */\nvoid u8x8_Setup_TGA_DESC(u8x8_t* u8x8);\nvoid u8x8_Setup_TGA_LCD(u8x8_t* u8x8);\nvoid tga_save(const char* name);\n\n/*==========================================*/\n/* u8x8_d_bitmap.c */\nuint8_t u8x8_GetBitmapPixel(u8x8_t* u8x8, uint16_t x, uint16_t y);\nvoid u8x8_SaveBitmapTGA(u8x8_t* u8x8, const char* filename);\nvoid u8x8_SetupBitmap(u8x8_t* u8x8, uint16_t pixel_width, uint16_t pixel_height);\nuint8_t u8x8_ConnectBitmapToU8x8(u8x8_t* u8x8);\n\n/*==========================================*/\n/* u8x8_d_utf8.c */\nvoid u8x8_Setup_Utf8(u8x8_t* u8x8); /* stdout UTF-8 display */\nvoid utf8_show(void); /* show content of UTF-8 frame buffer */\n\n/*==========================================*/\n\n/* u8x8_setup.c */\nuint8_t u8x8_d_null_cb(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\n/* u8x8_d_XXX.c */\nuint8_t u8x8_d_uc1701_ea_dogs102(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1701_mini12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1305_128x32_noname(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1305_128x32_adafruit(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1305_128x64_adafruit(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_128x64_noname(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_128x64_vcomh0(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_128x64_alt0(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1309_128x64_noname0(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1309_128x64_noname2(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1106_128x64_noname(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1106_128x64_vcomh0(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1106_128x64_winstar(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1106_72x40_wise(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1106_64x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1107_64x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1107_seeed_96x96(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1107_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1107_pimoroni_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1107_seeed_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1108_160x160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sh1122_256x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7920_192x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7920_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_128x32_univision(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_128x32_winstar(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_64x48_er(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_48x64_winstar(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_64x32_noname(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_64x32_1f(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_96x16_er(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1306_72x40_er(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ls013b7dh03_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ls027b7dh01_400x240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ls013b7dh05_144x168(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7511_avd_320x240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7528_nhd_c160100(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_ea_dogm128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_lm6063(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_64128n(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_ea_dogm132(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_zolen_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_nhd_c12832(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_nhd_c12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_jlx12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_lm6059(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_lx12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_erc12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7565_erc12864_alt(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    uint8_t arg_int,\n    void* arg_ptr); /* issue #790 */\nuint8_t u8x8_d_st7567_pi_132x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7567_jlx12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7567_enh_dg128064(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7567_enh_dg128064i(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7567_64x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7567_os12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7586s_s028hn118a(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7586s_erc240160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st7588_jlx12864(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx256128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_wo256x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx256160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx256160m(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx256160_alt(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx240160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx25664(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx172104(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75256_jlx19296(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_st75320_jlx320240(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    uint8_t arg_int,\n    void* arg_ptr); /* https://github.com/olikraus/u8g2/issues/921 */\nuint8_t u8x8_d_nt7534_tg12864r(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    uint8_t arg_int,\n    void* arg_ptr); /* u8x8_d_st7565.c */\nuint8_t u8x8_d_ld7032_60x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_240x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_240x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_128x64_alt(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_160x80(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_t6963_256x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1316_128x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1317_96x96(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1318_128x96(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1318_128x96_xcp(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1322_nhd_256x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1322_nhd_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_a2printer_384x240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sed1330_240x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ra8835_nhd_240x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ra8835_320x240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1325_nhd_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd0323_os128064(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_ws_96x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_seeed_96x96(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_ea_w128128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_midas_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_ws_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1327_visionox_128x96(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1326_er_256x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1329_128x96_noname(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1601_128x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1604_jlx19264(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1608_erc24064(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1608_erc240120(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1608_240x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1610_ea_dogxl160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1611_ea_dogm240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1611_ea_dogxl240(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t\n    u8x8_d_uc1611_ew50850(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); /* 240x160 */\nuint8_t\n    u8x8_d_uc1611_cg160160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); /* 160x160 */\nuint8_t u8x8_d_uc1617_jlx128128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_uc1638_160x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ks0108_128x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ks0108_erm19264(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sbn1661_122x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_sed1520_122x32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_pcd8544_84x48(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_pcf8812_96x65(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_hx1230_96x68(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1606_172x72(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1607_200x200(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1607_v2_200x200(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1607_gd_200x200(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ssd1607_ws_200x200(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    uint8_t arg_int,\n    void* arg_ptr); /* issue 637 */\nuint8_t u8x8_d_il3820_296x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_il3820_v2_296x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_lc7981_160x80(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_lc7981_160x160(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_lc7981_240x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_lc7981_240x64(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ist3020_erc19264(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_ist7920_128x128(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_max7219_64x8(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_max7219_32x8(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_max7219_16x16(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\nuint8_t u8x8_d_max7219_8x8(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);\n\n/*==========================================*/\n/* u8x8_8x8.c */\n\nuint16_t u8x8_upscale_byte(uint8_t x) U8X8_NOINLINE;\n\nvoid u8x8_utf8_init(u8x8_t* u8x8);\nuint16_t u8x8_ascii_next(u8x8_t* u8x8, uint8_t b);\nuint16_t u8x8_utf8_next(u8x8_t* u8x8, uint8_t b);\n// the following two functions are replaced by the init/next functions\n//uint16_t u8x8_get_encoding_from_utf8_string(const char **str);\n//uint16_t u8x8_get_char_from_string(const char **str);\n\nvoid u8x8_SetFont(u8x8_t* u8x8, const uint8_t* font_8x8);\nvoid u8x8_DrawGlyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding);\nvoid u8x8_Draw2x2Glyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding);\nvoid u8x8_Draw1x2Glyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding);\nuint8_t u8x8_DrawString(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s);\nuint8_t\n    u8x8_DrawUTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s); /* return number of glyps */\nuint8_t u8x8_Draw2x2String(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s);\nuint8_t u8x8_Draw2x2UTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s);\nuint8_t u8x8_Draw1x2String(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s);\nuint8_t u8x8_Draw1x2UTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s);\nuint8_t u8x8_GetUTF8Len(u8x8_t* u8x8, const char* s);\n#define u8x8_SetInverseFont(u8x8, b) (u8x8)->is_font_inverse_mode = (b)\n\n/*==========================================*/\n/* itoa procedures */\nconst char* u8x8_u8toa(uint8_t v, uint8_t d);\nconst char* u8x8_u16toa(uint16_t v, uint8_t d);\nconst char* u8x8_utoa(uint16_t v);\n\n/*==========================================*/\n/* u8x8_string.c */\n\nuint8_t u8x8_GetStringLineCnt(const char* str); /* return 0 for str==NULL */\nconst char* u8x8_GetStringLineStart(uint8_t line_idx, const char* str);\nvoid u8x8_CopyStringLine(char* dest, uint8_t line_idx, const char* str);\n/* draw one line, consider \\t for center */\nuint8_t u8x8_DrawUTF8Line(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t w, const char* s);\n/* draw multiple lines, handle \\t */\nuint8_t u8x8_DrawUTF8Lines(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t w, const char* s);\n\n/*==========================================*/\n\n/* u8x8_selection_list.c */\nstruct _u8sl_struct {\n    uint8_t visible; /* number of visible elements in the menu */\n    uint8_t total; /* total number of elements in the menu */\n    uint8_t first_pos; /* position of the first visible line */\n    uint8_t current_pos; /* current cursor position, starts at 0 */\n\n    uint8_t x; /* u8x8 only, not used in u8g2 */\n    uint8_t y; /* u8x8 only, not used in u8g2 */\n};\ntypedef struct _u8sl_struct u8sl_t;\n\ntypedef void (*u8x8_sl_cb)(u8x8_t* u8x8, u8sl_t* u8sl, uint8_t idx, const void* aux);\n\nvoid u8sl_Next(u8sl_t* u8sl);\nvoid u8sl_Prev(u8sl_t* u8sl);\n\nuint8_t u8x8_UserInterfaceSelectionList(\n    u8x8_t* u8x8,\n    const char* title,\n    uint8_t start_pos,\n    const char* sl);\n\n/*==========================================*/\n\n/* u8x8_message.c  */\nuint8_t u8x8_UserInterfaceMessage(\n    u8x8_t* u8x8,\n    const char* title1,\n    const char* title2,\n    const char* title3,\n    const char* buttons);\n\n/*==========================================*/\n/* u8x8_capture.c */\n\n/* vertical_top memory architecture */\nuint8_t u8x8_capture_get_pixel_1(uint16_t x, uint16_t y, uint8_t* dest_ptr, uint8_t tile_width);\n\n/* horizontal right memory architecture */\n/* SH1122, LD7032, ST7920, ST7986, LC7981, T6963, SED1330, RA8835, MAX7219, LS0 */\nuint8_t u8x8_capture_get_pixel_2(uint16_t x, uint16_t y, uint8_t* dest_ptr, uint8_t tile_width);\n\nvoid u8x8_capture_write_pbm_pre(\n    uint8_t tile_width,\n    uint8_t tile_height,\n    void (*out)(const char* s));\nvoid u8x8_capture_write_pbm_buffer(\n    uint8_t* buffer,\n    uint8_t tile_width,\n    uint8_t tile_height,\n    uint8_t (*get_pixel)(uint16_t x, uint16_t y, uint8_t* dest_ptr, uint8_t tile_width),\n    void (*out)(const char* s));\n\nvoid u8x8_capture_write_xbm_pre(\n    uint8_t tile_width,\n    uint8_t tile_height,\n    void (*out)(const char* s));\nvoid u8x8_capture_write_xbm_buffer(\n    uint8_t* buffer,\n    uint8_t tile_width,\n    uint8_t tile_height,\n    uint8_t (*get_pixel)(uint16_t x, uint16_t y, uint8_t* dest_ptr, uint8_t tile_width),\n    void (*out)(const char* s));\n\n/*==========================================*/\n\n/* u8x8_input_value.c  */\n\nuint8_t u8x8_UserInterfaceInputValue(\n    u8x8_t* u8x8,\n    const char* title,\n    const char* pre,\n    uint8_t* value,\n    uint8_t lo,\n    uint8_t hi,\n    uint8_t digits,\n    const char* post);\n\n/*==========================================*/\n/* u8log.c */\nvoid u8log_Init(u8log_t* u8log, uint8_t width, uint8_t height, uint8_t* buf);\nvoid u8log_SetCallback(u8log_t* u8log, u8log_cb cb, void* aux_data);\nvoid u8log_SetRedrawMode(u8log_t* u8log, uint8_t is_redraw_line_for_each_char);\nvoid u8log_SetLineHeightOffset(u8log_t* u8log, int8_t line_height_offset);\nvoid u8log_WriteString(u8log_t* u8log, const char* s) U8X8_NOINLINE;\nvoid u8log_WriteChar(u8log_t* u8log, uint8_t c) U8X8_NOINLINE;\nvoid u8log_WriteHex8(u8log_t* u8log, uint8_t b) U8X8_NOINLINE;\nvoid u8log_WriteHex16(u8log_t* u8log, uint16_t v);\nvoid u8log_WriteHex32(u8log_t* u8log, uint32_t v);\nvoid u8log_WriteDec8(u8log_t* u8log, uint8_t v, uint8_t d);\nvoid u8log_WriteDec16(u8log_t* u8log, uint16_t v, uint8_t d);\n\n/*==========================================*/\n/* u8log_u8x8.c */\nvoid u8x8_DrawLog(u8x8_t* u8x8, uint8_t x, uint8_t y, u8log_t* u8log);\nvoid u8log_u8x8_cb(u8log_t* u8log);\n\n/*==========================================*/\n/* start font list */\nextern const uint8_t\n    u8x8_font_amstrad_cpc_extended_f[] U8X8_FONT_SECTION(\"u8x8_font_amstrad_cpc_extended_f\");\nextern const uint8_t\n    u8x8_font_amstrad_cpc_extended_r[] U8X8_FONT_SECTION(\"u8x8_font_amstrad_cpc_extended_r\");\nextern const uint8_t\n    u8x8_font_amstrad_cpc_extended_n[] U8X8_FONT_SECTION(\"u8x8_font_amstrad_cpc_extended_n\");\nextern const uint8_t\n    u8x8_font_amstrad_cpc_extended_u[] U8X8_FONT_SECTION(\"u8x8_font_amstrad_cpc_extended_u\");\nextern const uint8_t u8x8_font_5x7_f[] U8X8_FONT_SECTION(\"u8x8_font_5x7_f\");\nextern const uint8_t u8x8_font_5x7_r[] U8X8_FONT_SECTION(\"u8x8_font_5x7_r\");\nextern const uint8_t u8x8_font_5x7_n[] U8X8_FONT_SECTION(\"u8x8_font_5x7_n\");\nextern const uint8_t u8x8_font_5x8_f[] U8X8_FONT_SECTION(\"u8x8_font_5x8_f\");\nextern const uint8_t u8x8_font_5x8_r[] U8X8_FONT_SECTION(\"u8x8_font_5x8_r\");\nextern const uint8_t u8x8_font_5x8_n[] U8X8_FONT_SECTION(\"u8x8_font_5x8_n\");\nextern const uint8_t u8x8_font_8x13_1x2_f[] U8X8_FONT_SECTION(\"u8x8_font_8x13_1x2_f\");\nextern const uint8_t u8x8_font_8x13_1x2_r[] U8X8_FONT_SECTION(\"u8x8_font_8x13_1x2_r\");\nextern const uint8_t u8x8_font_8x13_1x2_n[] U8X8_FONT_SECTION(\"u8x8_font_8x13_1x2_n\");\nextern const uint8_t u8x8_font_8x13B_1x2_f[] U8X8_FONT_SECTION(\"u8x8_font_8x13B_1x2_f\");\nextern const uint8_t u8x8_font_8x13B_1x2_r[] U8X8_FONT_SECTION(\"u8x8_font_8x13B_1x2_r\");\nextern const uint8_t u8x8_font_8x13B_1x2_n[] U8X8_FONT_SECTION(\"u8x8_font_8x13B_1x2_n\");\nextern const uint8_t u8x8_font_7x14_1x2_f[] U8X8_FONT_SECTION(\"u8x8_font_7x14_1x2_f\");\nextern const uint8_t u8x8_font_7x14_1x2_r[] U8X8_FONT_SECTION(\"u8x8_font_7x14_1x2_r\");\nextern const uint8_t u8x8_font_7x14_1x2_n[] U8X8_FONT_SECTION(\"u8x8_font_7x14_1x2_n\");\nextern const uint8_t u8x8_font_7x14B_1x2_f[] U8X8_FONT_SECTION(\"u8x8_font_7x14B_1x2_f\");\nextern const uint8_t u8x8_font_7x14B_1x2_r[] U8X8_FONT_SECTION(\"u8x8_font_7x14B_1x2_r\");\nextern const uint8_t u8x8_font_7x14B_1x2_n[] U8X8_FONT_SECTION(\"u8x8_font_7x14B_1x2_n\");\nextern const uint8_t\n    u8x8_font_open_iconic_arrow_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_arrow_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_check_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_check_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_embedded_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_embedded_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_play_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_play_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_thing_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_thing_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_weather_1x1[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_weather_1x1\");\nextern const uint8_t\n    u8x8_font_open_iconic_arrow_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_arrow_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_check_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_check_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_embedded_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_embedded_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_play_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_play_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_thing_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_thing_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_weather_2x2[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_weather_2x2\");\nextern const uint8_t\n    u8x8_font_open_iconic_arrow_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_arrow_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_check_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_check_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_embedded_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_embedded_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_play_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_play_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_thing_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_thing_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_weather_4x4[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_weather_4x4\");\nextern const uint8_t\n    u8x8_font_open_iconic_arrow_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_arrow_8x8\");\nextern const uint8_t\n    u8x8_font_open_iconic_check_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_check_8x8\");\nextern const uint8_t\n    u8x8_font_open_iconic_embedded_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_embedded_8x8\");\nextern const uint8_t\n    u8x8_font_open_iconic_play_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_play_8x8\");\nextern const uint8_t\n    u8x8_font_open_iconic_thing_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_thing_8x8\");\nextern const uint8_t\n    u8x8_font_open_iconic_weather_8x8[] U8X8_FONT_SECTION(\"u8x8_font_open_iconic_weather_8x8\");\nextern const uint8_t u8x8_font_profont29_2x3_f[] U8X8_FONT_SECTION(\"u8x8_font_profont29_2x3_f\");\nextern const uint8_t u8x8_font_profont29_2x3_r[] U8X8_FONT_SECTION(\"u8x8_font_profont29_2x3_r\");\nextern const uint8_t u8x8_font_profont29_2x3_n[] U8X8_FONT_SECTION(\"u8x8_font_profont29_2x3_n\");\nextern const uint8_t u8x8_font_artossans8_r[] U8X8_FONT_SECTION(\"u8x8_font_artossans8_r\");\nextern const uint8_t u8x8_font_artossans8_n[] U8X8_FONT_SECTION(\"u8x8_font_artossans8_n\");\nextern const uint8_t u8x8_font_artossans8_u[] U8X8_FONT_SECTION(\"u8x8_font_artossans8_u\");\nextern const uint8_t u8x8_font_artosserif8_r[] U8X8_FONT_SECTION(\"u8x8_font_artosserif8_r\");\nextern const uint8_t u8x8_font_artosserif8_n[] U8X8_FONT_SECTION(\"u8x8_font_artosserif8_n\");\nextern const uint8_t u8x8_font_artosserif8_u[] U8X8_FONT_SECTION(\"u8x8_font_artosserif8_u\");\nextern const uint8_t\n    u8x8_font_chroma48medium8_r[] U8X8_FONT_SECTION(\"u8x8_font_chroma48medium8_r\");\nextern const uint8_t\n    u8x8_font_chroma48medium8_n[] U8X8_FONT_SECTION(\"u8x8_font_chroma48medium8_n\");\nextern const uint8_t\n    u8x8_font_chroma48medium8_u[] U8X8_FONT_SECTION(\"u8x8_font_chroma48medium8_u\");\nextern const uint8_t\n    u8x8_font_saikyosansbold8_n[] U8X8_FONT_SECTION(\"u8x8_font_saikyosansbold8_n\");\nextern const uint8_t\n    u8x8_font_saikyosansbold8_u[] U8X8_FONT_SECTION(\"u8x8_font_saikyosansbold8_u\");\nextern const uint8_t u8x8_font_torussansbold8_r[] U8X8_FONT_SECTION(\"u8x8_font_torussansbold8_r\");\nextern const uint8_t u8x8_font_torussansbold8_n[] U8X8_FONT_SECTION(\"u8x8_font_torussansbold8_n\");\nextern const uint8_t u8x8_font_torussansbold8_u[] U8X8_FONT_SECTION(\"u8x8_font_torussansbold8_u\");\nextern const uint8_t u8x8_font_victoriabold8_r[] U8X8_FONT_SECTION(\"u8x8_font_victoriabold8_r\");\nextern const uint8_t u8x8_font_victoriabold8_n[] U8X8_FONT_SECTION(\"u8x8_font_victoriabold8_n\");\nextern const uint8_t u8x8_font_victoriabold8_u[] U8X8_FONT_SECTION(\"u8x8_font_victoriabold8_u\");\nextern const uint8_t\n    u8x8_font_victoriamedium8_r[] U8X8_FONT_SECTION(\"u8x8_font_victoriamedium8_r\");\nextern const uint8_t\n    u8x8_font_victoriamedium8_n[] U8X8_FONT_SECTION(\"u8x8_font_victoriamedium8_n\");\nextern const uint8_t\n    u8x8_font_victoriamedium8_u[] U8X8_FONT_SECTION(\"u8x8_font_victoriamedium8_u\");\nextern const uint8_t u8x8_font_courB18_2x3_f[] U8X8_FONT_SECTION(\"u8x8_font_courB18_2x3_f\");\nextern const uint8_t u8x8_font_courB18_2x3_r[] U8X8_FONT_SECTION(\"u8x8_font_courB18_2x3_r\");\nextern const uint8_t u8x8_font_courB18_2x3_n[] U8X8_FONT_SECTION(\"u8x8_font_courB18_2x3_n\");\nextern const uint8_t u8x8_font_courR18_2x3_f[] U8X8_FONT_SECTION(\"u8x8_font_courR18_2x3_f\");\nextern const uint8_t u8x8_font_courR18_2x3_r[] U8X8_FONT_SECTION(\"u8x8_font_courR18_2x3_r\");\nextern const uint8_t u8x8_font_courR18_2x3_n[] U8X8_FONT_SECTION(\"u8x8_font_courR18_2x3_n\");\nextern const uint8_t u8x8_font_courB24_3x4_f[] U8X8_FONT_SECTION(\"u8x8_font_courB24_3x4_f\");\nextern const uint8_t u8x8_font_courB24_3x4_r[] U8X8_FONT_SECTION(\"u8x8_font_courB24_3x4_r\");\nextern const uint8_t u8x8_font_courB24_3x4_n[] U8X8_FONT_SECTION(\"u8x8_font_courB24_3x4_n\");\nextern const uint8_t u8x8_font_courR24_3x4_f[] U8X8_FONT_SECTION(\"u8x8_font_courR24_3x4_f\");\nextern const uint8_t u8x8_font_courR24_3x4_r[] U8X8_FONT_SECTION(\"u8x8_font_courR24_3x4_r\");\nextern const uint8_t u8x8_font_courR24_3x4_n[] U8X8_FONT_SECTION(\"u8x8_font_courR24_3x4_n\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_o_2x2_f[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_o_2x2_f\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_o_2x2_r[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_o_2x2_r\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_o_2x2_n[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_o_2x2_n\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_r_2x2_f[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_r_2x2_f\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_r_2x2_r[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_r_2x2_r\");\nextern const uint8_t u8x8_font_lucasarts_scumm_subtitle_r_2x2_n[] U8X8_FONT_SECTION(\n    \"u8x8_font_lucasarts_scumm_subtitle_r_2x2_n\");\nextern const uint8_t u8x8_font_inr21_2x4_f[] U8X8_FONT_SECTION(\"u8x8_font_inr21_2x4_f\");\nextern const uint8_t u8x8_font_inr21_2x4_r[] U8X8_FONT_SECTION(\"u8x8_font_inr21_2x4_r\");\nextern const uint8_t u8x8_font_inr21_2x4_n[] U8X8_FONT_SECTION(\"u8x8_font_inr21_2x4_n\");\nextern const uint8_t u8x8_font_inr33_3x6_f[] U8X8_FONT_SECTION(\"u8x8_font_inr33_3x6_f\");\nextern const uint8_t u8x8_font_inr33_3x6_r[] U8X8_FONT_SECTION(\"u8x8_font_inr33_3x6_r\");\nextern const uint8_t u8x8_font_inr33_3x6_n[] U8X8_FONT_SECTION(\"u8x8_font_inr33_3x6_n\");\nextern const uint8_t u8x8_font_inr46_4x8_f[] U8X8_FONT_SECTION(\"u8x8_font_inr46_4x8_f\");\nextern const uint8_t u8x8_font_inr46_4x8_r[] U8X8_FONT_SECTION(\"u8x8_font_inr46_4x8_r\");\nextern const uint8_t u8x8_font_inr46_4x8_n[] U8X8_FONT_SECTION(\"u8x8_font_inr46_4x8_n\");\nextern const uint8_t u8x8_font_inb21_2x4_f[] U8X8_FONT_SECTION(\"u8x8_font_inb21_2x4_f\");\nextern const uint8_t u8x8_font_inb21_2x4_r[] U8X8_FONT_SECTION(\"u8x8_font_inb21_2x4_r\");\nextern const uint8_t u8x8_font_inb21_2x4_n[] U8X8_FONT_SECTION(\"u8x8_font_inb21_2x4_n\");\nextern const uint8_t u8x8_font_inb33_3x6_f[] U8X8_FONT_SECTION(\"u8x8_font_inb33_3x6_f\");\nextern const uint8_t u8x8_font_inb33_3x6_r[] U8X8_FONT_SECTION(\"u8x8_font_inb33_3x6_r\");\nextern const uint8_t u8x8_font_inb33_3x6_n[] U8X8_FONT_SECTION(\"u8x8_font_inb33_3x6_n\");\nextern const uint8_t u8x8_font_inb46_4x8_f[] U8X8_FONT_SECTION(\"u8x8_font_inb46_4x8_f\");\nextern const uint8_t u8x8_font_inb46_4x8_r[] U8X8_FONT_SECTION(\"u8x8_font_inb46_4x8_r\");\nextern const uint8_t u8x8_font_inb46_4x8_n[] U8X8_FONT_SECTION(\"u8x8_font_inb46_4x8_n\");\nextern const uint8_t u8x8_font_pressstart2p_f[] U8X8_FONT_SECTION(\"u8x8_font_pressstart2p_f\");\nextern const uint8_t u8x8_font_pressstart2p_r[] U8X8_FONT_SECTION(\"u8x8_font_pressstart2p_r\");\nextern const uint8_t u8x8_font_pressstart2p_n[] U8X8_FONT_SECTION(\"u8x8_font_pressstart2p_n\");\nextern const uint8_t u8x8_font_pressstart2p_u[] U8X8_FONT_SECTION(\"u8x8_font_pressstart2p_u\");\nextern const uint8_t u8x8_font_pcsenior_f[] U8X8_FONT_SECTION(\"u8x8_font_pcsenior_f\");\nextern const uint8_t u8x8_font_pcsenior_r[] U8X8_FONT_SECTION(\"u8x8_font_pcsenior_r\");\nextern const uint8_t u8x8_font_pcsenior_n[] U8X8_FONT_SECTION(\"u8x8_font_pcsenior_n\");\nextern const uint8_t u8x8_font_pcsenior_u[] U8X8_FONT_SECTION(\"u8x8_font_pcsenior_u\");\nextern const uint8_t\n    u8x8_font_pxplusibmcgathin_f[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcgathin_f\");\nextern const uint8_t\n    u8x8_font_pxplusibmcgathin_r[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcgathin_r\");\nextern const uint8_t\n    u8x8_font_pxplusibmcgathin_n[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcgathin_n\");\nextern const uint8_t\n    u8x8_font_pxplusibmcgathin_u[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcgathin_u\");\nextern const uint8_t u8x8_font_pxplusibmcga_f[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcga_f\");\nextern const uint8_t u8x8_font_pxplusibmcga_r[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcga_r\");\nextern const uint8_t u8x8_font_pxplusibmcga_n[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcga_n\");\nextern const uint8_t u8x8_font_pxplusibmcga_u[] U8X8_FONT_SECTION(\"u8x8_font_pxplusibmcga_u\");\nextern const uint8_t\n    u8x8_font_pxplustandynewtv_f[] U8X8_FONT_SECTION(\"u8x8_font_pxplustandynewtv_f\");\nextern const uint8_t\n    u8x8_font_pxplustandynewtv_r[] U8X8_FONT_SECTION(\"u8x8_font_pxplustandynewtv_r\");\nextern const uint8_t\n    u8x8_font_pxplustandynewtv_n[] U8X8_FONT_SECTION(\"u8x8_font_pxplustandynewtv_n\");\nextern const uint8_t\n    u8x8_font_pxplustandynewtv_u[] U8X8_FONT_SECTION(\"u8x8_font_pxplustandynewtv_u\");\nextern const uint8_t\n    u8x8_font_px437wyse700a_2x2_f[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700a_2x2_f\");\nextern const uint8_t\n    u8x8_font_px437wyse700a_2x2_r[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700a_2x2_r\");\nextern const uint8_t\n    u8x8_font_px437wyse700a_2x2_n[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700a_2x2_n\");\nextern const uint8_t\n    u8x8_font_px437wyse700b_2x2_f[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700b_2x2_f\");\nextern const uint8_t\n    u8x8_font_px437wyse700b_2x2_r[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700b_2x2_r\");\nextern const uint8_t\n    u8x8_font_px437wyse700b_2x2_n[] U8X8_FONT_SECTION(\"u8x8_font_px437wyse700b_2x2_n\");\n\n/* end font list */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _U8X8_H */\n"
  },
  {
    "path": "lib/u8g2/u8x8_8x8.c",
    "content": "/*\n\n  u8x8_8x8.c\n  \n  font procedures, directly interfaces display procedures\n  \n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    \n\n*/\n\n#include \"u8x8.h\"\n\n#if defined(ESP8266)\nuint8_t u8x8_pgm_read_esp(const uint8_t* addr) {\n    uint32_t bytes;\n    bytes = *(uint32_t*)((uint32_t)addr & ~3);\n    return ((uint8_t*)&bytes)[(uint32_t)addr & 3];\n}\n#endif\n\nvoid u8x8_SetFont(u8x8_t* u8x8, const uint8_t* font_8x8) {\n    u8x8->font = font_8x8;\n}\n\n/*\n Args:\n   u8x8: ptr to u8x8 structure\n   encoding: glyph for which the data is requested (must be between 0 and 255)\n   buf: pointer to 8 bytes\n*/\nstatic void u8x8_get_glyph_data(u8x8_t* u8x8, uint8_t encoding, uint8_t* buf, uint8_t tile_offset)\n    U8X8_NOINLINE;\nstatic void\n    u8x8_get_glyph_data(u8x8_t* u8x8, uint8_t encoding, uint8_t* buf, uint8_t tile_offset) {\n    uint8_t first, last, tiles, i;\n    uint16_t offset;\n    first = u8x8_pgm_read(u8x8->font + 0);\n    last = u8x8_pgm_read(u8x8->font + 1);\n    tiles = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n    tiles *= u8x8_pgm_read(u8x8->font + 3); /* new 2019 format */\n\n    /* get the glyph bitmap from the font */\n    if(first <= encoding && encoding <= last) {\n        offset = encoding;\n        offset -= first;\n        offset *= tiles; /* new 2019 format */\n        offset += tile_offset; /* new 2019 format */\n        offset *= 8;\n        offset += 4; /* changed from 2 to 4, new 2019 format */\n        for(i = 0; i < 8; i++) {\n            buf[i] = u8x8_pgm_read(u8x8->font + offset);\n            offset++;\n        }\n    } else {\n        for(i = 0; i < 8; i++) {\n            buf[i] = 0;\n        }\n    }\n\n    /* invert the bitmap if required */\n    if(u8x8->is_font_inverse_mode) {\n        for(i = 0; i < 8; i++) {\n            buf[i] ^= 255;\n        }\n    }\n}\n\nvoid u8x8_DrawGlyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding) {\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n    uint8_t tv = u8x8_pgm_read(u8x8->font + 3); /* new 2019 format */\n    uint8_t xx, tile;\n    uint8_t buf[8];\n    th += x;\n    tv += y;\n    tile = 0;\n    do {\n        xx = x;\n        do {\n            u8x8_get_glyph_data(u8x8, encoding, buf, tile);\n            u8x8_DrawTile(u8x8, xx, y, 1, buf);\n            tile++;\n            xx++;\n        } while(xx < th);\n        y++;\n    } while(y < tv);\n}\n\n/*\n  Source: http://graphics.stanford.edu/~seander/bithacks.html\n\tSection: Interleave bits by Binary Magic Numbers \n   Original codes is here:\n\t\tstatic const unsigned int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF};\n\t\tstatic const unsigned int S[] = {1, 2, 4, 8};\n\n\t\tunsigned int x; // Interleave lower 16 bits of x and y, so the bits of x\n\t\tunsigned int y; // are in the even positions and bits from y in the odd;\n\t\tunsigned int z; // z gets the resulting 32-bit Morton Number.  \n\t\t\t\t// x and y must initially be less than 65536.\n\n\t\tx = (x | (x << S[3])) & B[3];\n\t\tx = (x | (x << S[2])) & B[2];\n\t\tx = (x | (x << S[1])) & B[1];\n\t\tx = (x | (x << S[0])) & B[0];\n\n\t\ty = (y | (y << S[3])) & B[3];\n\t\ty = (y | (y << S[2])) & B[2];\n\t\ty = (y | (y << S[1])) & B[1];\n\t\ty = (y | (y << S[0])) & B[0];\n\n\t\tz = x | (y << 1);\n*/\nuint16_t u8x8_upscale_byte(uint8_t x) {\n    uint16_t y = x;\n    y |= (y << 4); // x = (x | (x << S[2])) & B[2];\n    y &= 0x0f0f;\n    y |= (y << 2); // x = (x | (x << S[1])) & B[1];\n    y &= 0x3333;\n    y |= (y << 1); // x = (x | (x << S[0])) & B[0];\n    y &= 0x5555;\n\n    y |= (y << 1); // z = x | (y << 1);\n    return y;\n}\n\nstatic void u8x8_upscale_buf(uint8_t* src, uint8_t* dest) U8X8_NOINLINE;\nstatic void u8x8_upscale_buf(uint8_t* src, uint8_t* dest) {\n    uint8_t i = 4;\n    do {\n        *dest++ = *src;\n        *dest++ = *src++;\n        i--;\n    } while(i > 0);\n}\n\nstatic void\n    u8x8_draw_2x2_subglyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding, uint8_t tile) {\n    uint8_t i;\n    uint16_t t;\n    uint8_t buf[8];\n    uint8_t buf1[8];\n    uint8_t buf2[8];\n    u8x8_get_glyph_data(u8x8, encoding, buf, tile);\n    for(i = 0; i < 8; i++) {\n        t = u8x8_upscale_byte(buf[i]);\n        buf1[i] = t >> 8;\n        buf2[i] = t & 255;\n    }\n    u8x8_upscale_buf(buf2, buf);\n    u8x8_DrawTile(u8x8, x, y, 1, buf);\n\n    u8x8_upscale_buf(buf2 + 4, buf);\n    u8x8_DrawTile(u8x8, x + 1, y, 1, buf);\n\n    u8x8_upscale_buf(buf1, buf);\n    u8x8_DrawTile(u8x8, x, y + 1, 1, buf);\n\n    u8x8_upscale_buf(buf1 + 4, buf);\n    u8x8_DrawTile(u8x8, x + 1, y + 1, 1, buf);\n}\n\nvoid u8x8_Draw2x2Glyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding) {\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n    uint8_t tv = u8x8_pgm_read(u8x8->font + 3); /* new 2019 format */\n    uint8_t xx, tile;\n    th *= 2;\n    th += x;\n    tv *= 2;\n    tv += y;\n    tile = 0;\n    do {\n        xx = x;\n        do {\n            u8x8_draw_2x2_subglyph(u8x8, xx, y, encoding, tile);\n            tile++;\n            xx += 2;\n        } while(xx < th);\n        y += 2;\n    } while(y < tv);\n}\n\n/* https://github.com/olikraus/u8g2/issues/474 */\nstatic void\n    u8x8_draw_1x2_subglyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding, uint8_t tile) {\n    uint8_t i;\n    uint16_t t;\n    uint8_t buf[8];\n    uint8_t buf1[8];\n    uint8_t buf2[8];\n    u8x8_get_glyph_data(u8x8, encoding, buf, tile);\n    for(i = 0; i < 8; i++) {\n        t = u8x8_upscale_byte(buf[i]);\n        buf1[i] = t >> 8;\n        buf2[i] = t & 255;\n    }\n    u8x8_DrawTile(u8x8, x, y, 1, buf2);\n    u8x8_DrawTile(u8x8, x, y + 1, 1, buf1);\n}\n\nvoid u8x8_Draw1x2Glyph(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t encoding) {\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n    uint8_t tv = u8x8_pgm_read(u8x8->font + 3); /* new 2019 format */\n    uint8_t xx, tile;\n    th += x;\n    tv *= 2;\n    tv += y;\n    tile = 0;\n    do {\n        xx = x;\n        do {\n            u8x8_draw_1x2_subglyph(u8x8, xx, y, encoding, tile);\n            tile++;\n            xx++;\n        } while(xx < th);\n        y += 2;\n    } while(y < tv);\n}\n\n/*\nsource: https://en.wikipedia.org/wiki/UTF-8\nBits\tfrom \t\tto\t\t\tbytes\tByte 1 \t\tByte 2 \t\tByte 3 \t\tByte 4 \t\tByte 5 \t\tByte 6\n  7 \tU+0000 \t\tU+007F \t\t1 \t\t0xxxxxxx\n11 \tU+0080 \t\tU+07FF \t\t2 \t\t110xxxxx \t10xxxxxx\n16 \tU+0800 \t\tU+FFFF \t\t3 \t\t1110xxxx \t10xxxxxx \t10xxxxxx\n21 \tU+10000 \tU+1FFFFF \t4 \t\t11110xxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n26 \tU+200000 \tU+3FFFFFF \t5 \t\t111110xx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx\n31 \tU+4000000 \tU+7FFFFFFF \t6 \t\t1111110x \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx \t10xxxxxx  \n\n\n*/\n\n/* reset the internal state machine */\nvoid u8x8_utf8_init(u8x8_t* u8x8) {\n    u8x8->utf8_state = 0; /* also reset during u8x8_SetupDefaults() */\n}\n\nuint16_t u8x8_ascii_next(U8X8_UNUSED u8x8_t* u8x8, uint8_t b) {\n    if(b == 0 || b == '\\n') /* '\\n' terminates the string to support the string list procedures */\n        return 0x0ffff; /* end of string detected*/\n    return b;\n}\n\n/*\n  pass a byte from an utf8 encoded string to the utf8 decoder state machine\n  returns \n    0x0fffe: no glyph, just continue\n    0x0ffff: end of string\n    anything else: The decoded encoding\n*/\nuint16_t u8x8_utf8_next(u8x8_t* u8x8, uint8_t b) {\n    if(b == 0 || b == '\\n') /* '\\n' terminates the string to support the string list procedures */\n        return 0x0ffff; /* end of string detected, pending UTF8 is discarded */\n    if(u8x8->utf8_state == 0) {\n        if(b >= 0xfc) /* 6 byte sequence */\n        {\n            u8x8->utf8_state = 5;\n            b &= 1;\n        } else if(b >= 0xf8) {\n            u8x8->utf8_state = 4;\n            b &= 3;\n        } else if(b >= 0xf0) {\n            u8x8->utf8_state = 3;\n            b &= 7;\n        } else if(b >= 0xe0) {\n            u8x8->utf8_state = 2;\n            b &= 15;\n        } else if(b >= 0xc0) {\n            u8x8->utf8_state = 1;\n            b &= 0x01f;\n        } else {\n            /* do nothing, just use the value as encoding */\n            return b;\n        }\n        u8x8->encoding = b;\n        return 0x0fffe;\n    } else {\n        u8x8->utf8_state--;\n        /* The case b < 0x080 (an illegal UTF8 encoding) is not checked here. */\n        u8x8->encoding <<= 6;\n        b &= 0x03f;\n        u8x8->encoding |= b;\n        if(u8x8->utf8_state != 0) return 0x0fffe; /* nothing to do yet */\n    }\n    return u8x8->encoding;\n}\n\nstatic uint8_t u8x8_draw_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) U8X8_NOINLINE;\nstatic uint8_t u8x8_draw_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    uint16_t e;\n    uint8_t cnt = 0;\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n\n    u8x8_utf8_init(u8x8);\n    for(;;) {\n        e = u8x8->next_cb(u8x8, (uint8_t)*s);\n        if(e == 0x0ffff) break;\n        s++;\n        if(e != 0x0fffe) {\n            u8x8_DrawGlyph(u8x8, x, y, e);\n            x += th;\n            cnt++;\n        }\n    }\n    return cnt;\n}\n\nuint8_t u8x8_DrawString(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_ascii_next;\n    return u8x8_draw_string(u8x8, x, y, s);\n}\n\nuint8_t u8x8_DrawUTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_utf8_next;\n    return u8x8_draw_string(u8x8, x, y, s);\n}\n\nstatic uint8_t\n    u8x8_draw_2x2_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) U8X8_NOINLINE;\nstatic uint8_t u8x8_draw_2x2_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    uint16_t e;\n    uint8_t cnt = 0;\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n\n    th <<= 1;\n\n    u8x8_utf8_init(u8x8);\n    for(;;) {\n        e = u8x8->next_cb(u8x8, (uint8_t)*s);\n        if(e == 0x0ffff) break;\n        s++;\n        if(e != 0x0fffe) {\n            u8x8_Draw2x2Glyph(u8x8, x, y, e);\n            x += th;\n            cnt++;\n        }\n    }\n    return cnt;\n}\n\nuint8_t u8x8_Draw2x2String(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_ascii_next;\n    return u8x8_draw_2x2_string(u8x8, x, y, s);\n}\n\nuint8_t u8x8_Draw2x2UTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_utf8_next;\n    return u8x8_draw_2x2_string(u8x8, x, y, s);\n}\n\nstatic uint8_t\n    u8x8_draw_1x2_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) U8X8_NOINLINE;\nstatic uint8_t u8x8_draw_1x2_string(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    uint16_t e;\n    uint8_t cnt = 0;\n    uint8_t th = u8x8_pgm_read(u8x8->font + 2); /* new 2019 format */\n    u8x8_utf8_init(u8x8);\n    for(;;) {\n        e = u8x8->next_cb(u8x8, (uint8_t)*s);\n        if(e == 0x0ffff) break;\n        s++;\n        if(e != 0x0fffe) {\n            u8x8_Draw1x2Glyph(u8x8, x, y, e);\n            x += th;\n            cnt++;\n        }\n    }\n    return cnt;\n}\n\nuint8_t u8x8_Draw1x2String(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_ascii_next;\n    return u8x8_draw_1x2_string(u8x8, x, y, s);\n}\n\nuint8_t u8x8_Draw1x2UTF8(u8x8_t* u8x8, uint8_t x, uint8_t y, const char* s) {\n    u8x8->next_cb = u8x8_utf8_next;\n    return u8x8_draw_1x2_string(u8x8, x, y, s);\n}\n\nuint8_t u8x8_GetUTF8Len(u8x8_t* u8x8, const char* s) {\n    uint16_t e;\n    uint8_t cnt = 0;\n    u8x8_utf8_init(u8x8);\n    for(;;) {\n        e = u8x8_utf8_next(u8x8, *s);\n        if(e == 0x0ffff) break;\n        s++;\n        if(e != 0x0fffe) cnt++;\n    }\n    return cnt;\n}\n"
  },
  {
    "path": "lib/u8g2/u8x8_byte.c",
    "content": "/*\n\n  u8x8_byte.c \n  \n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n  \n  \n*/\n\n#include \"u8x8.h\"\n\nuint8_t u8x8_byte_SetDC(u8x8_t* u8x8, uint8_t dc) {\n    return u8x8->byte_cb(u8x8, U8X8_MSG_BYTE_SET_DC, dc, NULL);\n}\n\nuint8_t u8x8_byte_SendBytes(u8x8_t* u8x8, uint8_t cnt, uint8_t* data) {\n    return u8x8->byte_cb(u8x8, U8X8_MSG_BYTE_SEND, cnt, (void*)data);\n}\n\nuint8_t u8x8_byte_SendByte(u8x8_t* u8x8, uint8_t byte) {\n    return u8x8_byte_SendBytes(u8x8, 1, &byte);\n}\n\nuint8_t u8x8_byte_StartTransfer(u8x8_t* u8x8) {\n    return u8x8->byte_cb(u8x8, U8X8_MSG_BYTE_START_TRANSFER, 0, NULL);\n}\n\nuint8_t u8x8_byte_EndTransfer(u8x8_t* u8x8) {\n    return u8x8->byte_cb(u8x8, U8X8_MSG_BYTE_END_TRANSFER, 0, NULL);\n}\n\n/*=========================================*/\n\nuint8_t u8x8_byte_empty(\n    U8X8_UNUSED u8x8_t* u8x8,\n    uint8_t msg,\n    U8X8_UNUSED uint8_t arg_int,\n    U8X8_UNUSED void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n    case U8X8_MSG_BYTE_INIT:\n    case U8X8_MSG_BYTE_SET_DC:\n    case U8X8_MSG_BYTE_START_TRANSFER:\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        break; /* do nothing */\n    }\n    return 1; /* always succeed */\n}\n\n/*=========================================*/\n\n/*\n  Uses:\n    u8x8->display_info->sda_setup_time_ns\n    u8x8->display_info->sck_pulse_width_ns\n    u8x8->display_info->spi_mode\n    u8x8->display_info->chip_disable_level\n    u8x8->display_info->chip_enable_level\n    u8x8->display_info->post_chip_enable_wait_ns\n    u8x8->display_info->pre_chip_disable_wait_ns\n  Calls to GPIO and DELAY:\n    U8X8_MSG_DELAY_NANO\n    U8X8_MSG_GPIO_DC\n    U8X8_MSG_GPIO_CS\n    U8X8_MSG_GPIO_CLOCK\n    U8X8_MSG_GPIO_DATA\n  Handles:\n    U8X8_MSG_BYTE_INIT\n    U8X8_MSG_BYTE_SEND\n    U8X8_MSG_BYTE_SET_DC\n    U8X8_MSG_BYTE_START_TRANSFER\n    U8X8_MSG_BYTE_END_TRANSFER\n*/\n\nuint8_t u8x8_byte_4wire_sw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i, b;\n    uint8_t* data;\n    uint8_t takeover_edge = u8x8_GetSPIClockPhase(u8x8);\n    uint8_t not_takeover_edge = 1 - takeover_edge;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            data++;\n            arg_int--;\n            for(i = 0; i < 8; i++) {\n                if(b & 128)\n                    u8x8_gpio_SetSPIData(u8x8, 1);\n                else\n                    u8x8_gpio_SetSPIData(u8x8, 0);\n                b <<= 1;\n\n                u8x8_gpio_SetSPIClock(u8x8, not_takeover_edge);\n                u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->sda_setup_time_ns);\n                u8x8_gpio_SetSPIClock(u8x8, takeover_edge);\n                u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->sck_pulse_width_ns);\n            }\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* no wait required here */\n\n        /* for SPI: setup correct level of the clock signal */\n        u8x8_gpio_SetSPIClock(u8x8, u8x8_GetSPIClockPhase(u8x8));\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        u8x8_gpio_SetDC(u8x8, arg_int);\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*=========================================*/\n\nuint8_t u8x8_byte_8bit_6800mode(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i, b;\n    uint8_t* data;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            data++;\n            arg_int--;\n            for(i = U8X8_MSG_GPIO_D0; i <= U8X8_MSG_GPIO_D7; i++) {\n                u8x8_gpio_call(u8x8, i, b & 1);\n                b >>= 1;\n            }\n\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* ensure that the enable signal is high */\n        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        u8x8_gpio_SetDC(u8x8, arg_int);\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\nuint8_t u8x8_byte_8bit_8080mode(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i, b;\n    uint8_t* data;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            data++;\n            arg_int--;\n            for(i = U8X8_MSG_GPIO_D0; i <= U8X8_MSG_GPIO_D7; i++) {\n                u8x8_gpio_call(u8x8, i, b & 1);\n                b >>= 1;\n            }\n\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* ensure that the enable signal is high */\n        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        u8x8_gpio_SetDC(u8x8, arg_int);\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*=========================================*/\n\nuint8_t u8x8_byte_3wire_sw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i;\n    uint8_t* data;\n    uint8_t takeover_edge = u8x8_GetSPIClockPhase(u8x8);\n    uint8_t not_takeover_edge = 1 - takeover_edge;\n    uint16_t b;\n    static uint8_t last_dc;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            if(last_dc != 0) b |= 256;\n            data++;\n            arg_int--;\n            for(i = 0; i < 9; i++) {\n                if(b & 256)\n                    u8x8_gpio_SetSPIData(u8x8, 1);\n                else\n                    u8x8_gpio_SetSPIData(u8x8, 0);\n                b <<= 1;\n\n                u8x8_gpio_SetSPIClock(u8x8, not_takeover_edge);\n                u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->sda_setup_time_ns);\n                u8x8_gpio_SetSPIClock(u8x8, takeover_edge);\n                u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->sck_pulse_width_ns);\n            }\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* no wait required here */\n\n        /* for SPI: setup correct level of the clock signal */\n        u8x8_gpio_SetSPIClock(u8x8, u8x8_GetSPIClockPhase(u8x8));\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        last_dc = arg_int;\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*=========================================*/\n\nvoid u8x8_byte_set_ks0108_cs(u8x8_t* u8x8, uint8_t arg) {\n    u8x8_gpio_SetCS(u8x8, arg & 1);\n    arg = arg >> 1;\n    u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_CS1, arg & 1);\n    arg = arg >> 1;\n    u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_CS2, arg & 1);\n}\n\n/* 6800 mode */\nuint8_t u8x8_byte_ks0108(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i, b;\n    uint8_t* data;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            data++;\n            arg_int--;\n            for(i = U8X8_MSG_GPIO_D0; i <= U8X8_MSG_GPIO_D7; i++) {\n                u8x8_gpio_call(u8x8, i, b & 1);\n                b >>= 1;\n            }\n\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);\n            u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* ensure that the enable signal is low */\n        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        u8x8_gpio_SetDC(u8x8, arg_int);\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        /* expects 3 bits in arg_int for the chip select lines */\n        u8x8_byte_set_ks0108_cs(u8x8, arg_int);\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        u8x8->gpio_and_delay_cb(\n            u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);\n        u8x8_byte_set_ks0108_cs(u8x8, arg_int);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* sed1520 or sbn1661 \n  U8X8_MSG_GPIO_E --> E1\n  U8X8_MSG_GPIO_CS --> E2\n*/\nuint8_t u8x8_byte_sed1520(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t i, b;\n    uint8_t* data;\n    static uint8_t enable_pin;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            b = *data;\n            data++;\n            arg_int--;\n            for(i = U8X8_MSG_GPIO_D0; i <= U8X8_MSG_GPIO_D7; i++) {\n                u8x8_gpio_call(u8x8, i, b & 1);\n                b >>= 1;\n            }\n\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);\n            u8x8_gpio_call(u8x8, enable_pin, 1);\n            u8x8_gpio_Delay(\n                u8x8, U8X8_MSG_DELAY_NANO, 200); /* KS0108 requires 450 ns, use 200 here */\n            u8x8_gpio_Delay(\n                u8x8,\n                U8X8_MSG_DELAY_NANO,\n                u8x8->display_info->write_pulse_width_ns); /* expect 250 here */\n            u8x8_gpio_call(u8x8, enable_pin, 0);\n        }\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        /* disable chipselect */\n        u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);\n        /* ensure that the enable signals are low */\n        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);\n        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_CS, 0);\n        enable_pin = U8X8_MSG_GPIO_E;\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        u8x8_gpio_SetDC(u8x8, arg_int);\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        /* cs lines are not supported for the SED1520/SBN1661 */\n        /* instead, this will select the E1 or E2 line */\n        enable_pin = U8X8_MSG_GPIO_E;\n        if(arg_int != 0) enable_pin = U8X8_MSG_GPIO_CS;\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*=========================================*/\n\n/*\n  software i2c,\n  ignores ACK response (which is anyway not provided by some displays)\n  also does not allow reading from the device\n*/\nstatic void i2c_delay(u8x8_t* u8x8) U8X8_NOINLINE;\nstatic void i2c_delay(u8x8_t* u8x8) {\n    //u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_10MICRO, u8x8->display_info->i2c_bus_clock_100kHz);\n    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_I2C, u8x8->display_info->i2c_bus_clock_100kHz);\n}\n\nstatic void i2c_init(u8x8_t* u8x8) {\n    u8x8_gpio_SetI2CClock(u8x8, 1);\n    u8x8_gpio_SetI2CData(u8x8, 1);\n\n    i2c_delay(u8x8);\n}\n\n/* actually, the scl line is not observed, so this procedure does not return a value */\n\nstatic void i2c_read_scl_and_delay(u8x8_t* u8x8) {\n    /* set as input (line will be high) */\n    u8x8_gpio_SetI2CClock(u8x8, 1);\n\n    i2c_delay(u8x8);\n}\n\nstatic void i2c_clear_scl(u8x8_t* u8x8) {\n    u8x8_gpio_SetI2CClock(u8x8, 0);\n}\n\nstatic void i2c_read_sda(u8x8_t* u8x8) {\n    /* set as input (line will be high) */\n    u8x8_gpio_SetI2CData(u8x8, 1);\n}\n\nstatic void i2c_clear_sda(u8x8_t* u8x8) {\n    /* set open collector and drive low */\n    u8x8_gpio_SetI2CData(u8x8, 0);\n}\n\nstatic void i2c_start(u8x8_t* u8x8) {\n    if(u8x8->i2c_started != 0) {\n        /* if already started: do restart */\n        i2c_read_sda(u8x8); /* SDA = 1 */\n        i2c_delay(u8x8);\n        i2c_read_scl_and_delay(u8x8);\n    }\n    i2c_read_sda(u8x8);\n    /* send the start condition, both lines go from 1 to 0 */\n    i2c_clear_sda(u8x8);\n    i2c_delay(u8x8);\n    i2c_clear_scl(u8x8);\n    u8x8->i2c_started = 1;\n}\n\nstatic void i2c_stop(u8x8_t* u8x8) {\n    /* set SDA to 0 */\n    i2c_clear_sda(u8x8);\n    i2c_delay(u8x8);\n\n    /* now release all lines */\n    i2c_read_scl_and_delay(u8x8);\n\n    /* set SDA to 1 */\n    i2c_read_sda(u8x8);\n    i2c_delay(u8x8);\n    u8x8->i2c_started = 0;\n}\n\nstatic void i2c_write_bit(u8x8_t* u8x8, uint8_t val) {\n    if(val)\n        i2c_read_sda(u8x8);\n    else\n        i2c_clear_sda(u8x8);\n\n    i2c_delay(u8x8);\n    i2c_read_scl_and_delay(u8x8);\n    i2c_clear_scl(u8x8);\n}\n\nstatic void i2c_read_bit(u8x8_t* u8x8) {\n    //uint8_t val;\n    /* do not drive SDA */\n    i2c_read_sda(u8x8);\n    i2c_delay(u8x8);\n    i2c_read_scl_and_delay(u8x8);\n    i2c_read_sda(u8x8);\n    i2c_delay(u8x8);\n    i2c_clear_scl(u8x8);\n    //return val;\n}\n\nstatic void i2c_write_byte(u8x8_t* u8x8, uint8_t b) {\n    i2c_write_bit(u8x8, b & 128);\n    i2c_write_bit(u8x8, b & 64);\n    i2c_write_bit(u8x8, b & 32);\n    i2c_write_bit(u8x8, b & 16);\n    i2c_write_bit(u8x8, b & 8);\n    i2c_write_bit(u8x8, b & 4);\n    i2c_write_bit(u8x8, b & 2);\n    i2c_write_bit(u8x8, b & 1);\n\n    /* read ack from client */\n    /* 0: ack was given by client */\n    /* 1: nothing happend during ack cycle */\n    i2c_read_bit(u8x8);\n}\n\nuint8_t u8x8_byte_sw_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t* data;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n\n        while(arg_int > 0) {\n            i2c_write_byte(u8x8, *data);\n            data++;\n            arg_int--;\n        }\n\n        break;\n\n    case U8X8_MSG_BYTE_INIT:\n        i2c_init(u8x8);\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        i2c_start(u8x8);\n        i2c_write_byte(u8x8, u8x8_GetI2CAddress(u8x8));\n        //i2c_write_byte(u8x8, 0x078);\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        i2c_stop(u8x8);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*=========================================*/\n\n/* alternative i2c byte procedure */\n#ifdef ALTERNATIVE_I2C_BYTE_PROCEDURE\n\nvoid i2c_transfer(u8x8_t* u8x8, uint8_t adr, uint8_t cnt, uint8_t* data) {\n    uint8_t i;\n    i2c_start(u8x8);\n    i2c_write_byte(u8x8, adr);\n    for(i = 0; i < cnt; i++)\n        i2c_write_byte(u8x8, data[i]);\n    i2c_stop(u8x8);\n}\n\nuint8_t u8x8_byte_sw_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    static uint8_t buffer[32]; /* u8g2/u8x8 will never send more than 32 bytes */\n    static uint8_t buf_idx;\n    uint8_t* data;\n\n    switch(msg) {\n    case U8X8_MSG_BYTE_SEND:\n        data = (uint8_t*)arg_ptr;\n        while(arg_int > 0) {\n            buffer[buf_idx++] = *data;\n            data++;\n            arg_int--;\n        }\n        break;\n    case U8X8_MSG_BYTE_INIT:\n        i2c_init(u8x8); /* init i2c communication */\n        break;\n    case U8X8_MSG_BYTE_SET_DC:\n        /* ignored for i2c */\n        break;\n    case U8X8_MSG_BYTE_START_TRANSFER:\n        buf_idx = 0;\n        break;\n    case U8X8_MSG_BYTE_END_TRANSFER:\n        i2c_transfer(u8x8, u8x8_GetI2CAddress(u8x8), buf_idx, buffer);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n#endif\n"
  },
  {
    "path": "lib/u8g2/u8x8_cad.c",
    "content": "/*\n  \n  u8x8_cad.c\n  \n  \"command arg data\" interface to the graphics controller\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n\n  The following sequence must be used for any data, which is set to the display:\n  \n  \n  uint8_t u8x8_cad_StartTransfer(u8x8_t *u8x8)\n\n  any of the following calls\n    uint8_t u8x8_cad_SendCmd(u8x8_t *u8x8, uint8_t cmd)\n    uint8_t u8x8_cad_SendArg(u8x8_t *u8x8, uint8_t arg)\n    uint8_t u8x8_cad_SendData(u8x8_t *u8x8, uint8_t cnt, uint8_t *data)\n  \n  uint8_t u8x8_cad_EndTransfer(u8x8_t *u8x8)\n\n\n\n*/\n/*\nuint8_t u8x8_cad_template(u8x8_t *u8x8, uint8_t msg, uint16_t arg_int, void *arg_ptr)\n{\n  uint8_t i;\n  \n  switch(msg)\n  {\n    case U8X8_MSG_CAD_SEND_CMD:\n      u8x8_mcd_byte_SetDC(mcd->next, 1);\n      u8x8_mcd_byte_Send(mcd->next, arg_int);\n      break;\n    case U8X8_MSG_CAD_SEND_ARG:\n      u8x8_mcd_byte_SetDC(mcd->next, 1);\n      u8x8_mcd_byte_Send(mcd->next, arg_int);\n      break;\n    case U8X8_MSG_CAD_SEND_DATA:\n      u8x8_mcd_byte_SetDC(mcd->next, 0);\n      for( i = 0; i < 8; i++ )\n\tu8x8_mcd_byte_Send(mcd->next, ((uint8_t *)arg_ptr)[i]);\n      break;\n    case U8X8_MSG_CAD_RESET:\n      return mcd->next->cb(mcd->next, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n      return mcd->next->cb(mcd->next, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_END_TRANSFER:\n      return mcd->next->cb(mcd->next, msg, arg_int, arg_ptr);\n    default:\n      break;\n  }\n  return 1;\n}\n\n*/\n\n#include \"u8x8.h\"\n\nuint8_t u8x8_cad_SendCmd(u8x8_t* u8x8, uint8_t cmd) {\n    return u8x8->cad_cb(u8x8, U8X8_MSG_CAD_SEND_CMD, cmd, NULL);\n}\n\nuint8_t u8x8_cad_SendArg(u8x8_t* u8x8, uint8_t arg) {\n    return u8x8->cad_cb(u8x8, U8X8_MSG_CAD_SEND_ARG, arg, NULL);\n}\n\nuint8_t u8x8_cad_SendMultipleArg(u8x8_t* u8x8, uint8_t cnt, uint8_t arg) {\n    while(cnt > 0) {\n        u8x8->cad_cb(u8x8, U8X8_MSG_CAD_SEND_ARG, arg, NULL);\n        cnt--;\n    }\n    return 1;\n}\n\nuint8_t u8x8_cad_SendData(u8x8_t* u8x8, uint8_t cnt, uint8_t* data) {\n    return u8x8->cad_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, cnt, data);\n}\n\nuint8_t u8x8_cad_StartTransfer(u8x8_t* u8x8) {\n    return u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 0, NULL);\n}\n\nuint8_t u8x8_cad_EndTransfer(u8x8_t* u8x8) {\n    return u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 0, NULL);\n}\n\nvoid u8x8_cad_vsendf(u8x8_t* u8x8, const char* fmt, va_list va) {\n    uint8_t d;\n    u8x8_cad_StartTransfer(u8x8);\n    while(*fmt != '\\0') {\n        d = (uint8_t)va_arg(va, int);\n        switch(*fmt) {\n        case 'a':\n            u8x8_cad_SendArg(u8x8, d);\n            break;\n        case 'c':\n            u8x8_cad_SendCmd(u8x8, d);\n            break;\n        case 'd':\n            u8x8_cad_SendData(u8x8, 1, &d);\n            break;\n        }\n        fmt++;\n    }\n    u8x8_cad_EndTransfer(u8x8);\n}\n\nvoid u8x8_SendF(u8x8_t* u8x8, const char* fmt, ...) {\n    va_list va;\n    va_start(va, fmt);\n    u8x8_cad_vsendf(u8x8, fmt, va);\n    va_end(va);\n}\n\n/*\n  21 c\t\tsend command c\n  22 a\t\tsend arg a\n  23 d\t\tsend data d\n  24\t\t\tCS on\n  25\t\t\tCS off\n  254 milli\tdelay by milliseconds\n  255\t\tend of sequence\n*/\n\nvoid u8x8_cad_SendSequence(u8x8_t* u8x8, uint8_t const* data) {\n    uint8_t cmd;\n    uint8_t v;\n\n    for(;;) {\n        cmd = *data;\n        data++;\n        switch(cmd) {\n        case U8X8_MSG_CAD_SEND_CMD:\n        case U8X8_MSG_CAD_SEND_ARG:\n            v = *data;\n            u8x8->cad_cb(u8x8, cmd, v, NULL);\n            data++;\n            break;\n        case U8X8_MSG_CAD_SEND_DATA:\n            v = *data;\n            u8x8_cad_SendData(u8x8, 1, &v);\n            data++;\n            break;\n        case U8X8_MSG_CAD_START_TRANSFER:\n        case U8X8_MSG_CAD_END_TRANSFER:\n            u8x8->cad_cb(u8x8, cmd, 0, NULL);\n            break;\n        case 0x0fe:\n            v = *data;\n            u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, v);\n            data++;\n            break;\n        default:\n            return;\n        }\n    }\n}\n\nuint8_t u8x8_cad_empty(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*\n  convert to bytes by using \n    dc = 1 for commands and args and\n    dc = 0 for data\n*/\nuint8_t u8x8_cad_110(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SetDC(u8x8, 1);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SetDC(u8x8, 1);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        u8x8_byte_SetDC(u8x8, 0);\n        //u8x8_byte_SendBytes(u8x8, arg_int, arg_ptr);\n        //break;\n        /* fall through */\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*\n  convert to bytes by using \n    dc = 1 for commands and args and\n    dc = 0 for data\n    t6963\n*/\nuint8_t u8x8_cad_100(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SetDC(u8x8, 1);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SetDC(u8x8, 0);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        u8x8_byte_SetDC(u8x8, 0);\n        //u8x8_byte_SendBytes(u8x8, arg_int, arg_ptr);\n        //break;\n        /* fall through */\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*\n  convert to bytes by using \n    dc = 0 for commands and args and\n    dc = 1 for data\n*/\nuint8_t u8x8_cad_001(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SetDC(u8x8, 0);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SetDC(u8x8, 0);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        u8x8_byte_SetDC(u8x8, 1);\n        //u8x8_byte_SendBytes(u8x8, arg_int, arg_ptr);\n        //break;\n        /* fall through */\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/*\n  convert to bytes by using \n    dc = 0 for commands \n    dc = 1 for args and data\n*/\nuint8_t u8x8_cad_011(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SetDC(u8x8, 0);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SetDC(u8x8, 1);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        u8x8_byte_SetDC(u8x8, 1);\n        //u8x8_byte_SendBytes(u8x8, arg_int, arg_ptr);\n        //break;\n        /* fall through */\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* cad procedure for the ST7920 in SPI mode */\n/* u8x8_byte_SetDC is not used */\nuint8_t u8x8_cad_st7920_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t* data;\n    uint8_t b;\n    uint8_t i;\n    static uint8_t buf[16];\n    uint8_t* ptr;\n\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_SendByte(u8x8, 0x0f8);\n        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, 1);\n        u8x8_byte_SendByte(u8x8, arg_int & 0x0f0);\n        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, 1);\n        u8x8_byte_SendByte(u8x8, arg_int << 4);\n        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, 1);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SendByte(u8x8, 0x0f8);\n        u8x8_byte_SendByte(u8x8, arg_int & 0x0f0);\n        u8x8_byte_SendByte(u8x8, arg_int << 4);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n\n        u8x8_byte_SendByte(u8x8, 0x0fa);\n        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, 1);\n\n        /* this loop should be optimized: multiple bytes should be sent */\n        /* u8x8_byte_SendBytes(u8x8, arg_int, arg_ptr); */\n        data = (uint8_t*)arg_ptr;\n\n        /* the following loop increases speed by 20% */\n        while(arg_int >= 8) {\n            i = 8;\n            ptr = buf;\n            do {\n                b = *data++;\n                *ptr++ = b & 0x0f0;\n                b <<= 4;\n                *ptr++ = b;\n                i--;\n            } while(i > 0);\n            arg_int -= 8;\n            u8x8_byte_SendBytes(u8x8, 16, buf);\n        }\n\n        while(arg_int > 0) {\n            b = *data;\n            u8x8_byte_SendByte(u8x8, b & 0x0f0);\n            u8x8_byte_SendByte(u8x8, b << 4);\n            data++;\n            arg_int--;\n        }\n        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, 1);\n        break;\n    case U8X8_MSG_CAD_INIT:\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* cad procedure for the SSD13xx family in I2C mode */\n/* this procedure is also used by the ST7588 */\n/* u8x8_byte_SetDC is not used */\n/* U8X8_MSG_BYTE_START_TRANSFER starts i2c transfer, U8X8_MSG_BYTE_END_TRANSFER stops transfer */\n/* After transfer start, a full byte indicates command or data mode */\n\nstatic void u8x8_i2c_data_transfer(u8x8_t* u8x8, uint8_t arg_int, void* arg_ptr) U8X8_NOINLINE;\nstatic void u8x8_i2c_data_transfer(u8x8_t* u8x8, uint8_t arg_int, void* arg_ptr) {\n    u8x8_byte_StartTransfer(u8x8);\n    u8x8_byte_SendByte(u8x8, 0x040);\n    u8x8->byte_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, arg_int, arg_ptr);\n    u8x8_byte_EndTransfer(u8x8);\n}\n\n/* classic version: will put a start/stop condition around each command and arg */\nuint8_t u8x8_cad_ssd13xx_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t* p;\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n    case U8X8_MSG_CAD_SEND_ARG:\n        /* 7 Nov 2016: Can this be improved?  */\n        //u8x8_byte_SetDC(u8x8, 0);\n        u8x8_byte_StartTransfer(u8x8);\n        //u8x8_byte_SendByte(u8x8, u8x8_GetI2CAddress(u8x8));\n        u8x8_byte_SendByte(u8x8, 0x000);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        u8x8_byte_EndTransfer(u8x8);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        //u8x8_byte_SetDC(u8x8, 1);\n\n        /* the FeatherWing OLED with the 32u4 transfer of long byte */\n        /* streams was not possible. This is broken down to */\n        /* smaller streams, 32 seems to be the limit... */\n        /* I guess this is related to the size of the Wire buffers in Arduino */\n        /* Unfortunately, this can not be handled in the byte level drivers, */\n        /* so this is done here. Even further, only 24 bytes will be sent, */\n        /* because there will be another byte (DC) required during the transfer */\n        p = arg_ptr;\n        while(arg_int > 24) {\n            u8x8_i2c_data_transfer(u8x8, 24, p);\n            arg_int -= 24;\n            p += 24;\n        }\n        u8x8_i2c_data_transfer(u8x8, arg_int, p);\n        break;\n    case U8X8_MSG_CAD_INIT:\n        /* apply default i2c adr if required so that the start transfer msg can use this */\n        if(u8x8->i2c_address == 255) u8x8->i2c_address = 0x078;\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        /* cad transfer commands are ignored */\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* fast version with reduced data start/stops, issue 735 */\nuint8_t u8x8_cad_ssd13xx_fast_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    static uint8_t in_transfer = 0;\n    uint8_t* p;\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        /* improved version, takeover from ld7032 */\n        /* assumes, that the args of a command is not longer than 31 bytes */\n        /* speed improvement is about 4% compared to the classic version */\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n\n        u8x8_byte_StartTransfer(u8x8);\n        u8x8_byte_SendByte(u8x8, 0x000); /* cmd byte for ssd13xx controller */\n        u8x8_byte_SendByte(u8x8, arg_int);\n        in_transfer = 1;\n        /* lightning version: can replace the improved version from above */\n        /* the drawback of the lightning version is this: The complete init sequence */\n        /* must fit into the 32 byte Arduino Wire buffer, which might not always be the case */\n        /* speed improvement is about 6% compared to the classic version */\n        // if ( in_transfer == 0 )\n        // {\n        //   u8x8_byte_StartTransfer(u8x8);\n        //   u8x8_byte_SendByte(u8x8, 0x000);\t/* cmd byte for ssd13xx controller */\n        //   in_transfer = 1;\n        // }\n        //u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n\n        /* the FeatherWing OLED with the 32u4 transfer of long byte */\n        /* streams was not possible. This is broken down to */\n        /* smaller streams, 32 seems to be the limit... */\n        /* I guess this is related to the size of the Wire buffers in Arduino */\n        /* Unfortunately, this can not be handled in the byte level drivers, */\n        /* so this is done here. Even further, only 24 bytes will be sent, */\n        /* because there will be another byte (DC) required during the transfer */\n        p = arg_ptr;\n        while(arg_int > 24) {\n            u8x8_i2c_data_transfer(u8x8, 24, p);\n            arg_int -= 24;\n            p += 24;\n        }\n        u8x8_i2c_data_transfer(u8x8, arg_int, p);\n        in_transfer = 0;\n        break;\n    case U8X8_MSG_CAD_INIT:\n        /* apply default i2c adr if required so that the start transfer msg can use this */\n        if(u8x8->i2c_address == 255) u8x8->i2c_address = 0x078;\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n        in_transfer = 0;\n        break;\n    case U8X8_MSG_CAD_END_TRANSFER:\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n        in_transfer = 0;\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* the st75256 i2c driver is a copy of the ssd13xx driver, but with arg=1 */\n/* modified from cad001 (ssd13xx) to cad011 */\nuint8_t u8x8_cad_st75256_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    uint8_t* p;\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        u8x8_byte_StartTransfer(u8x8);\n        u8x8_byte_SendByte(u8x8, 0x000);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        u8x8_byte_EndTransfer(u8x8);\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_StartTransfer(u8x8);\n        u8x8_byte_SendByte(u8x8, 0x040);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        u8x8_byte_EndTransfer(u8x8);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        /* see ssd13xx driver */\n        p = arg_ptr;\n        while(arg_int > 24) {\n            u8x8_i2c_data_transfer(u8x8, 24, p);\n            arg_int -= 24;\n            p += 24;\n        }\n        u8x8_i2c_data_transfer(u8x8, arg_int, p);\n        break;\n    case U8X8_MSG_CAD_INIT:\n        /* apply default i2c adr if required so that the start transfer msg can use this */\n        if(u8x8->i2c_address == 255) u8x8->i2c_address = 0x078; /* ST75256, often this is 0x07e */\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n    case U8X8_MSG_CAD_END_TRANSFER:\n        /* cad transfer commands are ignored */\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* cad i2c procedure for the ld7032 controller */\n/* Issue https://github.com/olikraus/u8g2/issues/865 mentiones, that I2C does not work */\n/* Workaround is to remove the while loop (or increase the value in the condition) */\nuint8_t u8x8_cad_ld7032_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    static uint8_t in_transfer = 0;\n    uint8_t* p;\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n        u8x8_byte_StartTransfer(u8x8);\n        u8x8_byte_SendByte(u8x8, arg_int);\n        in_transfer = 1;\n        break;\n    case U8X8_MSG_CAD_SEND_ARG:\n        u8x8_byte_SendByte(u8x8, arg_int);\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        //u8x8_byte_SetDC(u8x8, 1);\n\n        /* the FeatherWing OLED with the 32u4 transfer of long byte */\n        /* streams was not possible. This is broken down to */\n        /* smaller streams, 32 seems to be the limit... */\n        /* I guess this is related to the size of the Wire buffers in Arduino */\n        /* Unfortunately, this can not be handled in the byte level drivers, */\n        /* so this is done here. Even further, only 24 bytes will be sent, */\n        /* because there will be another byte (DC) required during the transfer */\n        p = arg_ptr;\n        while(arg_int > 24) {\n            u8x8->byte_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, 24, p);\n            arg_int -= 24;\n            p += 24;\n            u8x8_byte_EndTransfer(u8x8);\n            u8x8_byte_StartTransfer(u8x8);\n            u8x8_byte_SendByte(u8x8, 0x08); /* data write for LD7032 */\n        }\n        u8x8->byte_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, arg_int, p);\n        break;\n    case U8X8_MSG_CAD_INIT:\n        /* apply default i2c adr if required so that the start transfer msg can use this */\n        if(u8x8->i2c_address == 255) u8x8->i2c_address = 0x060;\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n        in_transfer = 0;\n        break;\n    case U8X8_MSG_CAD_END_TRANSFER:\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n\n/* cad procedure for the UC16xx family in I2C mode */\n/* u8x8_byte_SetDC is not used */\n/* DC bit is encoded into the adr byte */\nuint8_t u8x8_cad_uc16xx_i2c(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {\n    static uint8_t in_transfer = 0;\n    static uint8_t is_data = 0;\n    uint8_t* p;\n    switch(msg) {\n    case U8X8_MSG_CAD_SEND_CMD:\n    case U8X8_MSG_CAD_SEND_ARG:\n        if(in_transfer != 0) {\n            if(is_data != 0) {\n                /* transfer mode is active, but data transfer */\n                u8x8_byte_EndTransfer(u8x8);\n                /* clear the lowest two bits of the adr */\n                u8x8_SetI2CAddress(u8x8, u8x8_GetI2CAddress(u8x8) & 0x0fc);\n                u8x8_byte_StartTransfer(u8x8);\n            }\n        } else {\n            /* clear the lowest two bits of the adr */\n            u8x8_SetI2CAddress(u8x8, u8x8_GetI2CAddress(u8x8) & 0x0fc);\n            u8x8_byte_StartTransfer(u8x8);\n        }\n        u8x8_byte_SendByte(u8x8, arg_int);\n        in_transfer = 1;\n        break;\n    case U8X8_MSG_CAD_SEND_DATA:\n        if(in_transfer != 0) {\n            if(is_data == 0) {\n                /* transfer mode is active, but data transfer */\n                u8x8_byte_EndTransfer(u8x8);\n                /* clear the lowest two bits of the adr */\n                u8x8_SetI2CAddress(u8x8, (u8x8_GetI2CAddress(u8x8) & 0x0fc) | 2);\n                u8x8_byte_StartTransfer(u8x8);\n            }\n        } else {\n            /* clear the lowest two bits of the adr */\n            u8x8_SetI2CAddress(u8x8, (u8x8_GetI2CAddress(u8x8) & 0x0fc) | 2);\n            u8x8_byte_StartTransfer(u8x8);\n        }\n        in_transfer = 1;\n\n        p = arg_ptr;\n        while(arg_int > 24) {\n            u8x8->byte_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, 24, p);\n            arg_int -= 24;\n            p += 24;\n            u8x8_byte_EndTransfer(u8x8);\n            u8x8_byte_StartTransfer(u8x8);\n        }\n        u8x8->byte_cb(u8x8, U8X8_MSG_CAD_SEND_DATA, arg_int, p);\n\n        break;\n    case U8X8_MSG_CAD_INIT:\n        /* apply default i2c adr if required so that the start transfer msg can use this */\n        if(u8x8->i2c_address == 255) u8x8->i2c_address = 0x070;\n        return u8x8->byte_cb(u8x8, msg, arg_int, arg_ptr);\n    case U8X8_MSG_CAD_START_TRANSFER:\n        in_transfer = 0;\n        /* actual start is delayed, because we do not whether this is data or cmd transfer */\n        break;\n    case U8X8_MSG_CAD_END_TRANSFER:\n        if(in_transfer != 0) u8x8_byte_EndTransfer(u8x8);\n        in_transfer = 0;\n        break;\n    default:\n        return 0;\n    }\n    return 1;\n}\n"
  },
  {
    "path": "lib/u8g2/u8x8_display.c",
    "content": "/*\n  \n  u8x8_display.c\n  \n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n  \n  \n  Abstraction layer for the graphics controller.\n  Main goal is the placement of a 8x8 pixel block (tile) on the display.\n  \n*/\n\n#include \"u8x8.h\"\n\n/*==========================================*/\n/* internal library function */\n\n/*\n  this is a helper function for the U8X8_MSG_DISPLAY_SETUP_MEMORY function.\n  It can be called within the display callback function to carry out the usual standard tasks.\n  \n*/\nvoid u8x8_d_helper_display_setup_memory(u8x8_t* u8x8, const u8x8_display_info_t* display_info) {\n    /* 1) set display info struct */\n    u8x8->display_info = display_info;\n    u8x8->x_offset = u8x8->display_info->default_x_offset;\n}\n\n/*\n  this is a helper function for the U8X8_MSG_DISPLAY_INIT function.\n  It can be called within the display callback function to carry out the usual standard tasks.\n  \n*/\nvoid u8x8_d_helper_display_init(u8x8_t* u8x8) {\n    /* 2) apply port directions to the GPIO lines and apply default values for the IO lines*/\n    u8x8_gpio_Init(u8x8);\n    u8x8_cad_Init(u8x8);\n\n    /* 3) do reset */\n    u8x8_gpio_SetReset(u8x8, 1);\n    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, u8x8->display_info->reset_pulse_width_ms);\n    u8x8_gpio_SetReset(u8x8, 0);\n    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, u8x8->display_info->reset_pulse_width_ms);\n    u8x8_gpio_SetReset(u8x8, 1);\n    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, u8x8->display_info->post_reset_wait_ms);\n}\n\n/*==========================================*/\n/* official functions */\n\nuint8_t u8x8_DrawTile(u8x8_t* u8x8, uint8_t x, uint8_t y, uint8_t cnt, uint8_t* tile_ptr) {\n    u8x8_tile_t tile;\n    tile.x_pos = x;\n    tile.y_pos = y;\n    tile.cnt = cnt;\n    tile.tile_ptr = tile_ptr;\n    return u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_DRAW_TILE, 1, (void*)&tile);\n}\n\n/* should be implemented as macro */\nvoid u8x8_SetupMemory(u8x8_t* u8x8) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_SETUP_MEMORY, 0, NULL);\n}\n\nvoid u8x8_InitDisplay(u8x8_t* u8x8) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_INIT, 0, NULL);\n}\n\nvoid u8x8_SetPowerSave(u8x8_t* u8x8, uint8_t is_enable) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_SET_POWER_SAVE, is_enable, NULL);\n}\n\nvoid u8x8_SetFlipMode(u8x8_t* u8x8, uint8_t mode) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_SET_FLIP_MODE, mode, NULL);\n}\n\nvoid u8x8_SetContrast(u8x8_t* u8x8, uint8_t value) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_SET_CONTRAST, value, NULL);\n}\n\nvoid u8x8_RefreshDisplay(u8x8_t* u8x8) {\n    u8x8->display_cb(u8x8, U8X8_MSG_DISPLAY_REFRESH, 0, NULL);\n}\n\nvoid u8x8_ClearDisplayWithTile(u8x8_t* u8x8, const uint8_t* buf) {\n    u8x8_tile_t tile;\n    uint8_t h;\n\n    tile.x_pos = 0;\n    tile.cnt = 1;\n    tile.tile_ptr = (uint8_t*)buf; /* tile_ptr should be const, but isn't */\n\n    h = u8x8->display_info->tile_height;\n    tile.y_pos = 0;\n    do {\n        u8x8->display_cb(\n            u8x8, U8X8_MSG_DISPLAY_DRAW_TILE, u8x8->display_info->tile_width, (void*)&tile);\n        tile.y_pos++;\n    } while(tile.y_pos < h);\n}\n\nvoid u8x8_ClearDisplay(u8x8_t* u8x8) {\n    uint8_t buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};\n    u8x8_ClearDisplayWithTile(u8x8, buf);\n}\n\nvoid u8x8_FillDisplay(u8x8_t* u8x8) {\n    uint8_t buf[8] = {255, 255, 255, 255, 255, 255, 255, 255};\n    u8x8_ClearDisplayWithTile(u8x8, buf);\n}\n\nvoid u8x8_ClearLine(u8x8_t* u8x8, uint8_t line) {\n    uint8_t buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};\n    u8x8_tile_t tile;\n    if(line < u8x8->display_info->tile_height) {\n        tile.x_pos = 0;\n        tile.y_pos = line;\n        tile.cnt = 1;\n        tile.tile_ptr = (uint8_t*)buf; /* tile_ptr should be const, but isn't */\n        u8x8->display_cb(\n            u8x8, U8X8_MSG_DISPLAY_DRAW_TILE, u8x8->display_info->tile_width, (void*)&tile);\n    }\n}\n"
  },
  {
    "path": "lib/u8g2/u8x8_gpio.c",
    "content": "/*\n\n  u8x8_gpio.c\n  \n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8x8.h\"\n\nvoid u8x8_gpio_call(u8x8_t* u8x8, uint8_t msg, uint8_t arg) {\n    u8x8->gpio_and_delay_cb(u8x8, msg, arg, NULL);\n}\n\n/*\nvoid u8x8_gpio_Delay(u8x8_t *u8x8, uint8_t msg, uint8_t dly)\n{\n  u8x8->gpio_and_delay_cb(u8x8, msg, dly, NULL);\n}\n*/\n"
  },
  {
    "path": "lib/u8g2/u8x8_setup.c",
    "content": "/*\n\n  u8x8_setup.c\n\n  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)\n\n  Copyright (c) 2016, olikraus@gmail.com\n  All rights reserved.\n\n  Redistribution and use in source and binary forms, with or without modification, \n  are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this list \n    of conditions and the following disclaimer.\n    \n  * Redistributions in binary form must reproduce the above copyright notice, this \n    list of conditions and the following disclaimer in the documentation and/or other \n    materials provided with the distribution.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \n  CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, \n  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR \n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \n  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \n  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \n  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \n  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF \n  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  \n\n*/\n\n#include \"u8x8.h\"\n\n/* universal dummy callback, which will be default for all callbacks */\nuint8_t u8x8_dummy_cb(\n    U8X8_UNUSED u8x8_t* u8x8,\n    U8X8_UNUSED uint8_t msg,\n    U8X8_UNUSED uint8_t arg_int,\n    U8X8_UNUSED void* arg_ptr) {\n    /* the dummy callback will not handle any message and will fail for all messages */\n    return 0;\n}\n\nstatic const u8x8_display_info_t u8x8_null_display_info = {\n    /* chip_enable_level = */ 0,\n    /* chip_disable_level = */ 1,\n\n    /* post_chip_enable_wait_ns = */ 0,\n    /* pre_chip_disable_wait_ns = */ 0,\n    /* reset_pulse_width_ms = */ 0,\n    /* post_reset_wait_ms = */ 0,\n    /* sda_setup_time_ns = */ 0,\n    /* sck_pulse_width_ns = */\n    0, /* half of cycle time (100ns according to datasheet), AVR: below 70: 8 MHz, >= 70 --> 4MHz clock */\n    /* sck_clock_hz = */\n    4000000UL, /* since Arduino 1.6.0, the SPI bus speed in Hz. Should be  1000000000/sck_pulse_width_ns */\n    /* spi_mode = */ 0, /* active high, rising edge */\n    /* i2c_bus_clock_100kHz = */ 4,\n    /* data_setup_time_ns = */ 0,\n    /* write_pulse_width_ns = */ 0,\n    /* tile_width = */ 1, /* 8x8 */\n    /* tile_hight = */ 1,\n    /* default_x_offset = */ 0,\n    /* flipmode_x_offset = */ 0,\n    /* pixel_width = */ 8,\n    /* pixel_height = */ 8};\n\n/* a special null device */\nuint8_t u8x8_d_null_cb(\n    u8x8_t* u8x8,\n    uint8_t msg,\n    U8X8_UNUSED uint8_t arg_int,\n    U8X8_UNUSED void* arg_ptr) {\n    switch(msg) {\n    case U8X8_MSG_DISPLAY_SETUP_MEMORY:\n        u8x8_d_helper_display_setup_memory(u8x8, &u8x8_null_display_info);\n        break;\n    case U8X8_MSG_DISPLAY_INIT:\n        u8x8_d_helper_display_init(u8x8);\n        break;\n    }\n    /* the null device callback will succeed for all messages */\n    return 1;\n}\n\n/*\n  Description:\n    Setup u8x8\n  Args:\n    u8x8\tAn empty u8x8 structure\n*/\nvoid u8x8_SetupDefaults(u8x8_t* u8x8) {\n    u8x8->display_info = NULL;\n    u8x8->display_cb = u8x8_dummy_cb;\n    u8x8->cad_cb = u8x8_dummy_cb;\n    u8x8->byte_cb = u8x8_dummy_cb;\n    u8x8->gpio_and_delay_cb = u8x8_dummy_cb;\n    u8x8->is_font_inverse_mode = 0;\n    //u8x8->device_address = 0;\n    u8x8->utf8_state = 0; /* also reset by u8x8_utf8_init */\n    u8x8->bus_clock = 0; /* issue 769 */\n    u8x8->i2c_address = 255;\n    u8x8->debounce_default_pin_state = 255; /* assume all low active buttons */\n\n#ifdef U8X8_USE_PINS\n    {\n        uint8_t i;\n        for(i = 0; i < U8X8_PIN_CNT; i++)\n            u8x8->pins[i] = U8X8_PIN_NONE;\n    }\n#endif\n}\n\n/*\n  Description:\n    Setup u8x8 and assign the callback function. The dummy \n    callback \"u8x8_dummy_cb\" can be used, if no callback is required.\n    This setup will not communicate with the display itself.\n    Use u8x8_InitDisplay() to send the startup code to the Display.\n  Args:\n    u8x8\t\t\t\tAn empty u8x8 structure\n    display_cb\t\t\tDisplay/controller specific callback function\n    cad_cb\t\t\t\tDisplay controller specific communication callback function\n    byte_cb\t\t\tDisplay controller/communication specific callback funtion\n    gpio_and_delay_cb\tEnvironment specific callback function\n\n*/\nvoid u8x8_Setup(\n    u8x8_t* u8x8,\n    u8x8_msg_cb display_cb,\n    u8x8_msg_cb cad_cb,\n    u8x8_msg_cb byte_cb,\n    u8x8_msg_cb gpio_and_delay_cb) {\n    /* setup defaults and reset pins to U8X8_PIN_NONE */\n    u8x8_SetupDefaults(u8x8);\n\n    /* setup specific callbacks */\n    u8x8->display_cb = display_cb;\n    u8x8->cad_cb = cad_cb;\n    u8x8->byte_cb = byte_cb;\n    u8x8->gpio_and_delay_cb = gpio_and_delay_cb;\n\n    /* setup display info */\n    u8x8_SetupMemory(u8x8);\n}\n"
  },
  {
    "path": "lib/update_util/SConscript",
    "content": "Import(\"env\")\n\nenv.Append(\n    LINT_SOURCES=[\n        Dir(\".\"),\n    ],\n)\n\nlibenv = env.Clone(FW_LIB_NAME=\"update_util\")\nlibenv.ApplyLibFlags()\n\nsources = libenv.GlobRecursive(\"*.c\")\n\nlib = libenv.StaticLibrary(\"${FW_LIB_NAME}\", sources)\nlibenv.Install(\"${LIB_DIST_DIR}\", lib)\nReturn(\"lib\")\n"
  },
  {
    "path": "lib/update_util/dfu_file.c",
    "content": "#include \"dfu_file.h\"\n\n#include <furi_hal.h>\n#include <toolbox/crc32_calc.h>\n\n#define VALID_WHOLE_FILE_CRC 0xFFFFFFFF\n#define DFU_SUFFIX_VERSION   0x011A\n#define DFU_SIGNATURE        \"DfuSe\"\n\nbool dfu_file_validate_crc(File* dfuf, const DfuPageTaskProgressCb progress_cb, void* context) {\n    uint32_t file_crc = crc32_calc_file(dfuf, progress_cb, context);\n\n    /* Last 4 bytes of DFU file = CRC of previous file contents, inverted\n     * If we calculate whole file CRC32, incl. embedded CRC,\n     * that should give us 0xFFFFFFFF \n     */\n    return file_crc == VALID_WHOLE_FILE_CRC;\n}\n\nuint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* reference_params) {\n    furi_assert(reference_params);\n\n    DfuPrefix dfu_prefix = {0};\n    DfuSuffix dfu_suffix = {0};\n    size_t bytes_read = 0;\n\n    if(!storage_file_is_open(dfuf) || !storage_file_seek(dfuf, 0, true)) {\n        return 0;\n    }\n\n    const uint32_t dfu_suffix_offset = storage_file_size(dfuf) - sizeof(DfuSuffix);\n\n    bytes_read = storage_file_read(dfuf, &dfu_prefix, sizeof(DfuPrefix));\n    if(bytes_read != sizeof(DfuPrefix)) {\n        return 0;\n    }\n\n    if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature)) != 0) {\n        return 0;\n    }\n\n    if((dfu_prefix.bVersion != 1) || (dfu_prefix.DFUImageSize != dfu_suffix_offset)) {\n        return 0;\n    }\n\n    if(!storage_file_seek(dfuf, dfu_suffix_offset, true)) {\n        return 0;\n    }\n\n    bytes_read = storage_file_read(dfuf, &dfu_suffix, sizeof(DfuSuffix));\n    if(bytes_read != sizeof(DfuSuffix)) {\n        return 0;\n    }\n\n    if((dfu_suffix.bLength != sizeof(DfuSuffix)) || (dfu_suffix.bcdDFU != DFU_SUFFIX_VERSION)) {\n        return 0;\n    }\n    /* TODO FL-3561: check DfuSignature?.. */\n\n    if((dfu_suffix.idVendor != reference_params->vendor) ||\n       (dfu_suffix.idProduct != reference_params->product) ||\n       (dfu_suffix.bcdDevice != reference_params->device)) {\n        return 0;\n    }\n\n    return dfu_prefix.bTargets;\n}\n\n/* Assumes file is open, valid and read pointer is set at the start of image data\n */\nstatic DfuUpdateBlockResult dfu_file_perform_task_for_update_pages(\n    const DfuUpdateTask* task,\n    File* dfuf,\n    const ImageElementHeader* header) {\n    furi_assert(task);\n    furi_assert(header);\n    task->progress_cb(0, task->context);\n    const size_t FLASH_PAGE_SIZE = furi_hal_flash_get_page_size();\n    const size_t FLASH_PAGE_ALIGNMENT_MASK = FLASH_PAGE_SIZE - 1;\n    if((header->dwElementAddress & FLASH_PAGE_ALIGNMENT_MASK) != 0) {\n        /* start address is not aligned by page boundary -- we don't support that. Yet. */\n        return UpdateBlockResult_Failed;\n    }\n\n    if(task->address_cb && (!task->address_cb(header->dwElementAddress) ||\n                            !task->address_cb(header->dwElementAddress + header->dwElementSize))) {\n        storage_file_seek(dfuf, header->dwElementSize, false);\n        task->progress_cb(100, task->context);\n        return UpdateBlockResult_Skipped;\n    }\n\n    uint8_t* fw_block = malloc(FLASH_PAGE_SIZE);\n    size_t bytes_read = 0;\n    uint32_t element_offs = 0;\n\n    while(element_offs < header->dwElementSize) {\n        uint32_t n_bytes_to_read = FLASH_PAGE_SIZE;\n        if((element_offs + n_bytes_to_read) > header->dwElementSize) {\n            n_bytes_to_read = header->dwElementSize - element_offs;\n        }\n\n        bytes_read = storage_file_read(dfuf, fw_block, n_bytes_to_read);\n        if(bytes_read == 0) {\n            break;\n        }\n\n        int16_t i_page = furi_hal_flash_get_page_number(header->dwElementAddress + element_offs);\n        if(i_page < 0) {\n            break;\n        }\n\n        if(!task->task_cb(i_page, fw_block, bytes_read)) {\n            break;\n        }\n\n        element_offs += bytes_read;\n        task->progress_cb(element_offs * 100 / header->dwElementSize, task->context);\n    }\n\n    free(fw_block);\n    return (element_offs == header->dwElementSize) ? UpdateBlockResult_OK :\n                                                     UpdateBlockResult_Failed;\n}\n\nbool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8_t n_targets) {\n    TargetPrefix target_prefix = {0};\n    ImageElementHeader image_element = {0};\n    size_t bytes_read = 0;\n\n    if(!storage_file_seek(dfuf, sizeof(DfuPrefix), true)) {\n        return UpdateBlockResult_Failed;\n    };\n\n    for(uint8_t i_target = 0; i_target < n_targets; ++i_target) {\n        bytes_read = storage_file_read(dfuf, &target_prefix, sizeof(TargetPrefix));\n        if(bytes_read != sizeof(TargetPrefix)) {\n            return UpdateBlockResult_Failed;\n        }\n\n        /* TODO FL-3562: look into TargetPrefix and validate/filter?.. */\n        for(uint32_t i_element = 0; i_element < target_prefix.dwNbElements; ++i_element) {\n            bytes_read = storage_file_read(dfuf, &image_element, sizeof(ImageElementHeader));\n            if(bytes_read != sizeof(ImageElementHeader)) {\n                return UpdateBlockResult_Failed;\n            }\n\n            if(dfu_file_perform_task_for_update_pages(task, dfuf, &image_element) ==\n               UpdateBlockResult_Failed) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "lib/update_util/dfu_file.h",
    "content": "#pragma once\n\n#include \"dfu_headers.h\"\n\n#include <stdbool.h>\n#include <storage/storage.h>\n\ntypedef enum {\n    UpdateBlockResult_Unknown,\n    UpdateBlockResult_OK,\n    UpdateBlockResult_Skipped,\n    UpdateBlockResult_Failed\n} DfuUpdateBlockResult;\n\ntypedef bool (\n    *DfuPageTaskCb)(const uint8_t i_page, const uint8_t* update_block, uint16_t update_block_len);\ntypedef void (*DfuPageTaskProgressCb)(const uint8_t progress, void* context);\ntypedef bool (*DfuAddressValidationCb)(const size_t address);\n\ntypedef struct {\n    DfuPageTaskCb task_cb;\n    DfuPageTaskProgressCb progress_cb;\n    DfuAddressValidationCb address_cb;\n    void* context;\n} DfuUpdateTask;\n\ntypedef struct {\n    uint16_t vendor;\n    uint16_t product;\n    uint16_t device;\n} DfuValidationParams;\n\nbool dfu_file_validate_crc(File* dfuf, const DfuPageTaskProgressCb progress_cb, void* context);\n\n/* Returns number of valid targets from file header\n * If file is invalid, returns 0\n */\nuint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* reference_params);\n\nbool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8_t n_targets);\n"
  },
  {
    "path": "lib/update_util/dfu_headers.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#pragma pack(push, 1)\n\ntypedef struct {\n    char szSignature[5];\n    uint8_t bVersion;\n    uint32_t DFUImageSize;\n    uint8_t bTargets;\n} DfuPrefix;\n\ntypedef struct {\n    uint16_t bcdDevice;\n    uint16_t idProduct;\n    uint16_t idVendor;\n    uint16_t bcdDFU;\n    uint8_t ucDfuSignature_U;\n    uint8_t ucDfuSignature_F;\n    uint8_t ucDfuSignature_D;\n    uint8_t bLength;\n    uint32_t dwCRC;\n} DfuSuffix;\n\ntypedef struct {\n    char szSignature[6];\n    uint8_t bAlternateSetting;\n    uint8_t bTargetNamed;\n    uint8_t _pad[3];\n    char szTargetName[255];\n    uint32_t dwTargetSize;\n    uint32_t dwNbElements;\n} TargetPrefix;\n\ntypedef struct {\n    uint32_t dwElementAddress;\n    uint32_t dwElementSize;\n} ImageElementHeader;\n\n#pragma pack(pop)\n"
  },
  {
    "path": "lib/update_util/int_backup.c",
    "content": "#include \"int_backup.h\"\n\n#include <toolbox/tar/tar_archive.h>\n\n#include <bt/bt_settings_filename.h>\n#include <bt/bt_service/bt_keys_filename.h>\n#include <dolphin/helpers/dolphin_state_filename.h>\n#include <desktop/helpers/slideshow_filename.h>\n#include <desktop/desktop_settings_filename.h>\n#include <notification/notification_settings_filename.h>\n\n#define INT_BACKUP_DEFAULT_LOCATION EXT_PATH(INT_BACKUP_DEFAULT_FILENAME)\n\nstatic void backup_name_converter(FuriString* filename) {\n    if(furi_string_empty(filename) || (furi_string_get_char(filename, 0) == '.')) {\n        return;\n    }\n\n    /* Filenames are already prefixed with '.' */\n    const char* const names[] = {\n        BT_SETTINGS_FILE_NAME,\n        BT_KEYS_STORAGE_FILE_NAME,\n        DESKTOP_SETTINGS_FILE_NAME,\n        NOTIFICATION_SETTINGS_FILE_NAME,\n        SLIDESHOW_FILE_NAME,\n        DOLPHIN_STATE_FILE_NAME,\n    };\n\n    for(size_t i = 0; i < COUNT_OF(names); i++) {\n        if(furi_string_equal(filename, &names[i][1])) {\n            furi_string_set(filename, names[i]);\n            return;\n        }\n    }\n}\n\nbool int_backup_create(Storage* storage, const char* destination) {\n    const char* final_destination =\n        destination && strlen(destination) ? destination : INT_BACKUP_DEFAULT_LOCATION;\n    return storage_int_backup(storage, final_destination) == FSE_OK;\n}\n\nbool int_backup_exists(Storage* storage, const char* source) {\n    const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION;\n    return storage_common_stat(storage, final_source, NULL) == FSE_OK;\n}\n\nbool int_backup_unpack(Storage* storage, const char* source) {\n    const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION;\n    return storage_int_restore(storage, final_source, backup_name_converter) == FSE_OK;\n}\n"
  },
  {
    "path": "lib/update_util/int_backup.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <storage/storage.h>\n\n#define INT_BACKUP_DEFAULT_FILENAME \"backup.tar\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool int_backup_create(Storage* storage, const char* destination);\nbool int_backup_exists(Storage* storage, const char* source);\nbool int_backup_unpack(Storage* storage, const char* source);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/update_util/resources/manifest.c",
    "content": "#include \"manifest.h\"\n\n#include <toolbox/stream/buffered_file_stream.h>\n#include <toolbox/strint.h>\n#include <toolbox/hex.h>\n\nstruct ResourceManifestReader {\n    Storage* storage;\n    Stream* stream;\n    FuriString* linebuf;\n    ResourceManifestEntry entry;\n};\n\nResourceManifestReader* resource_manifest_reader_alloc(Storage* storage) {\n    ResourceManifestReader* resource_manifest =\n        (ResourceManifestReader*)malloc(sizeof(ResourceManifestReader));\n    resource_manifest->storage = storage;\n    resource_manifest->stream = buffered_file_stream_alloc(resource_manifest->storage);\n    memset(&resource_manifest->entry, 0, sizeof(ResourceManifestEntry));\n    resource_manifest->entry.name = furi_string_alloc();\n    resource_manifest->linebuf = furi_string_alloc();\n    return resource_manifest;\n}\n\nvoid resource_manifest_reader_free(ResourceManifestReader* resource_manifest) {\n    furi_assert(resource_manifest);\n\n    furi_string_free(resource_manifest->linebuf);\n    furi_string_free(resource_manifest->entry.name);\n    buffered_file_stream_close(resource_manifest->stream);\n    stream_free(resource_manifest->stream);\n    free(resource_manifest);\n}\n\nbool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename) {\n    furi_assert(resource_manifest);\n\n    return buffered_file_stream_open(\n        resource_manifest->stream, filename, FSAM_READ, FSOM_OPEN_EXISTING);\n}\n\n/* Read entries in format of\n * F:<hash>:<size>:<name>\n * D:<name> \n */\nResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest) {\n    furi_assert(resource_manifest);\n\n    furi_string_reset(resource_manifest->entry.name);\n    resource_manifest->entry.type = ResourceManifestEntryTypeUnknown;\n    resource_manifest->entry.size = 0;\n    memset(resource_manifest->entry.hash, 0, sizeof(resource_manifest->entry.hash));\n\n    do {\n        if(!stream_read_line(resource_manifest->stream, resource_manifest->linebuf)) {\n            return NULL;\n        }\n\n        /* Trim end of line */\n        furi_string_trim(resource_manifest->linebuf);\n\n        char type_code = furi_string_get_char(resource_manifest->linebuf, 0);\n        switch(type_code) {\n        case 'V':\n            resource_manifest->entry.type = ResourceManifestEntryTypeVersion;\n            break;\n        case 'T':\n            resource_manifest->entry.type = ResourceManifestEntryTypeTimestamp;\n            break;\n        case 'F':\n            resource_manifest->entry.type = ResourceManifestEntryTypeFile;\n            break;\n        case 'D':\n            resource_manifest->entry.type = ResourceManifestEntryTypeDirectory;\n            break;\n        default: /* Skip other entries - version, timestamp, etc */\n            continue;\n        };\n\n        if(resource_manifest->entry.type == ResourceManifestEntryTypeFile) {\n            /* Parse file entry\n              F:<hash>:<size>:<name> */\n\n            /* Remove entry type code */\n            furi_string_right(resource_manifest->linebuf, 2);\n\n            if(furi_string_search_char(resource_manifest->linebuf, ':') !=\n               sizeof(resource_manifest->entry.hash) * 2) {\n                /* Invalid hash */\n                continue;\n            }\n\n            /* Read hash */\n            hex_chars_to_uint8(\n                furi_string_get_cstr(resource_manifest->linebuf), resource_manifest->entry.hash);\n\n            /* Remove hash */\n            furi_string_right(\n                resource_manifest->linebuf, sizeof(resource_manifest->entry.hash) * 2 + 1);\n\n            if(strint_to_uint32(\n                   furi_string_get_cstr(resource_manifest->linebuf),\n                   NULL,\n                   &resource_manifest->entry.size,\n                   10) != StrintParseNoError)\n                break;\n\n            /* Remove size */\n            size_t offs = furi_string_search_char(resource_manifest->linebuf, ':');\n            furi_string_right(resource_manifest->linebuf, offs + 1);\n\n            furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf);\n        } else { //-V547\n            /* Everything else is plain key value. Parse version, timestamp or directory entry\n               <Type>:<Value> */\n\n            /* Remove entry type code */\n            furi_string_right(resource_manifest->linebuf, 2);\n\n            furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf);\n        }\n\n        return &resource_manifest->entry;\n    } while(true);\n\n    return NULL;\n}\n\nResourceManifestEntry*\n    resource_manifest_reader_previous(ResourceManifestReader* resource_manifest) {\n    furi_assert(resource_manifest);\n\n    // Snapshot position for rollback\n    const size_t previous_position = stream_tell(resource_manifest->stream);\n\n    // We need to jump 2 lines back\n    size_t jumps = 2;\n    // Special case: end of the file.\n    const bool was_eof = stream_eof(resource_manifest->stream);\n    if(was_eof) {\n        jumps = 1;\n    }\n    while(jumps) {\n        if(!stream_seek_to_char(resource_manifest->stream, '\\n', StreamDirectionBackward)) {\n            break;\n        }\n        if(stream_tell(resource_manifest->stream) < (previous_position - 1)) {\n            jumps--;\n        }\n    }\n\n    // Special case: first line. Force seek to zero\n    if(jumps == 1) {\n        jumps = 0;\n        stream_seek(resource_manifest->stream, 0, StreamOffsetFromStart);\n    }\n\n    if(jumps == 0) {\n        ResourceManifestEntry* entry = resource_manifest_reader_next(resource_manifest);\n        // Special case: was end of the file, prevent loop\n        if(was_eof) {\n            stream_seek(resource_manifest->stream, -1, StreamOffsetFromCurrent);\n        }\n        return entry;\n    } else {\n        stream_seek(resource_manifest->stream, previous_position, StreamOffsetFromStart);\n        return NULL;\n    }\n}\n\nbool resource_manifest_rewind(ResourceManifestReader* resource_manifest) {\n    furi_assert(resource_manifest);\n\n    return stream_seek(resource_manifest->stream, 0, StreamOffsetFromStart);\n}\n"
  },
  {
    "path": "lib/update_util/resources/manifest.h",
    "content": "#pragma once\n\n#include <storage/storage.h>\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    ResourceManifestEntryTypeUnknown = 0,\n    ResourceManifestEntryTypeVersion,\n    ResourceManifestEntryTypeTimestamp,\n    ResourceManifestEntryTypeDirectory,\n    ResourceManifestEntryTypeFile,\n} ResourceManifestEntryType;\n\ntypedef struct {\n    ResourceManifestEntryType type;\n    FuriString* name;\n    uint32_t size;\n    uint8_t hash[16];\n} ResourceManifestEntry;\n\ntypedef struct ResourceManifestReader ResourceManifestReader;\n\n/**\n * @brief Initialize resource manifest reader\n * @param storage Storage API pointer\n * @return allocated object\n */\nResourceManifestReader* resource_manifest_reader_alloc(Storage* storage);\n\n/**\n * @brief Release resource manifest reader\n * @param resource_manifest allocated object\n */\nvoid resource_manifest_reader_free(ResourceManifestReader* resource_manifest);\n\n/**\n * @brief Initialize resource manifest reader iteration\n * @param resource_manifest allocated object\n * @param filename manifest file name\n * @return true if file opened\n */\nbool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename);\n\n/**\n * @brief Rewind manifest to the beginning\n * @param resource_manifest allocated object\n * @return true if successful\n */\nbool resource_manifest_rewind(ResourceManifestReader* resource_manifest);\n\n/**\n * @brief Read next file/dir entry from manifest\n * @param resource_manifest allocated object\n * @return entry or NULL if end of file\n */\nResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest);\n\n/** Read previous file/dir entry from manifest\n *\n * You must be at the end of the manifest to use this function.\n * Intended to be used after reaching end with resource_manifest_reader_next\n *\n * @param      resource_manifest  Pointer to the ResourceManifestReader instance\n *\n * @return     entry or NULL if end of file\n */\nResourceManifestEntry*\n    resource_manifest_reader_previous(ResourceManifestReader* resource_manifest);\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n"
  },
  {
    "path": "lib/update_util/update_manifest.c",
    "content": "#include \"update_manifest.h\"\n\n#include <storage/storage.h>\n#include <flipper_format/flipper_format.h>\n#include <flipper_format/flipper_format_i.h>\n\n#define MANIFEST_KEY_INFO          \"Info\"\n#define MANIFEST_KEY_TARGET        \"Target\"\n#define MANIFEST_KEY_LOADER_FILE   \"Loader\"\n#define MANIFEST_KEY_LOADER_CRC    \"Loader CRC\"\n#define MANIFEST_KEY_DFU_FILE      \"Firmware\"\n#define MANIFEST_KEY_RADIO_FILE    \"Radio\"\n#define MANIFEST_KEY_RADIO_ADDRESS \"Radio address\"\n#define MANIFEST_KEY_RADIO_VERSION \"Radio version\"\n#define MANIFEST_KEY_RADIO_CRC     \"Radio CRC\"\n#define MANIFEST_KEY_ASSETS_FILE   \"Resources\"\n#define MANIFEST_KEY_OB_REFERENCE  \"OB reference\"\n#define MANIFEST_KEY_OB_MASK       \"OB mask\"\n#define MANIFEST_KEY_OB_WRITE_MASK \"OB write mask\"\n#define MANIFEST_KEY_SPLASH_FILE   \"Splashscreen\"\n\nUpdateManifest* update_manifest_alloc(void) {\n    UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest));\n    update_manifest->version = furi_string_alloc();\n    update_manifest->firmware_dfu_image = furi_string_alloc();\n    update_manifest->radio_image = furi_string_alloc();\n    update_manifest->staged_loader_file = furi_string_alloc();\n    update_manifest->resource_bundle = furi_string_alloc();\n    update_manifest->splash_file = furi_string_alloc();\n    update_manifest->target = 0;\n    update_manifest->manifest_version = 0;\n    memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n    memset(update_manifest->ob_compare_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n    memset(update_manifest->ob_write_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n    update_manifest->valid = false;\n    return update_manifest;\n}\n\nvoid update_manifest_free(UpdateManifest* update_manifest) {\n    furi_assert(update_manifest);\n    furi_string_free(update_manifest->version);\n    furi_string_free(update_manifest->firmware_dfu_image);\n    furi_string_free(update_manifest->radio_image);\n    furi_string_free(update_manifest->staged_loader_file);\n    furi_string_free(update_manifest->resource_bundle);\n    furi_string_free(update_manifest->splash_file);\n    free(update_manifest);\n}\n\nstatic bool\n    update_manifest_init_from_ff(UpdateManifest* update_manifest, FlipperFormat* flipper_file) {\n    furi_assert(update_manifest);\n    furi_assert(flipper_file);\n\n    FuriString* filetype;\n\n    filetype = furi_string_alloc();\n    update_manifest->valid =\n        flipper_format_read_header(flipper_file, filetype, &update_manifest->manifest_version) &&\n        furi_string_cmp_str(filetype, \"Flipper firmware upgrade configuration\") == 0 &&\n        flipper_format_read_string(flipper_file, MANIFEST_KEY_INFO, update_manifest->version) &&\n        flipper_format_read_uint32(\n            flipper_file, MANIFEST_KEY_TARGET, &update_manifest->target, 1) &&\n        flipper_format_read_string(\n            flipper_file, MANIFEST_KEY_LOADER_FILE, update_manifest->staged_loader_file) &&\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_LOADER_CRC,\n            (uint8_t*)&update_manifest->staged_loader_crc,\n            sizeof(uint32_t));\n    furi_string_free(filetype);\n\n    if(update_manifest->valid) {\n        /* Optional fields - we can have dfu, radio, resources, or any combination */\n        flipper_format_read_string(\n            flipper_file, MANIFEST_KEY_DFU_FILE, update_manifest->firmware_dfu_image);\n        flipper_format_read_string(\n            flipper_file, MANIFEST_KEY_RADIO_FILE, update_manifest->radio_image);\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_RADIO_ADDRESS,\n            (uint8_t*)&update_manifest->radio_address,\n            sizeof(uint32_t));\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_RADIO_VERSION,\n            update_manifest->radio_version.raw,\n            sizeof(UpdateManifestRadioVersion));\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_RADIO_CRC,\n            (uint8_t*)&update_manifest->radio_crc,\n            sizeof(uint32_t));\n        flipper_format_read_string(\n            flipper_file, MANIFEST_KEY_ASSETS_FILE, update_manifest->resource_bundle);\n\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_OB_REFERENCE,\n            update_manifest->ob_reference.bytes,\n            FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_OB_MASK,\n            update_manifest->ob_compare_mask.bytes,\n            FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n        flipper_format_read_hex(\n            flipper_file,\n            MANIFEST_KEY_OB_WRITE_MASK,\n            update_manifest->ob_write_mask.bytes,\n            FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);\n\n        flipper_format_read_string(\n            flipper_file, MANIFEST_KEY_SPLASH_FILE, update_manifest->splash_file);\n\n        update_manifest->valid =\n            (!furi_string_empty(update_manifest->firmware_dfu_image) ||\n             !furi_string_empty(update_manifest->radio_image) ||\n             !furi_string_empty(update_manifest->resource_bundle));\n    }\n\n    return update_manifest->valid;\n}\n\n// Verifies that mask values are same for adjacent words (value & inverted)\nstatic bool ob_data_check_mask_valid(const FuriHalFlashRawOptionByteData* mask) {\n    bool mask_valid = true;\n    for(size_t idx = 0; mask_valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) {\n        mask_valid &= mask->obs[idx].values.base == mask->obs[idx].values.complementary_value;\n    }\n    return mask_valid;\n}\n\n// Verifies that all reference values have no unmasked bits\nstatic bool ob_data_check_masked_values_valid(\n    const FuriHalFlashRawOptionByteData* data,\n    const FuriHalFlashRawOptionByteData* mask) {\n    bool valid = true;\n    for(size_t idx = 0; valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) {\n        valid &= (data->obs[idx].dword & mask->obs[idx].dword) == data->obs[idx].dword;\n    }\n    return valid;\n}\n\nbool update_manifest_has_obdata(UpdateManifest* update_manifest) {\n    bool ob_data_valid = false;\n    // do we have at least 1 value?\n    for(size_t idx = 0; !ob_data_valid && (idx < FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); ++idx) {\n        ob_data_valid |= update_manifest->ob_reference.bytes[idx] != 0;\n    }\n    // sanity checks\n    ob_data_valid &= ob_data_check_mask_valid(&update_manifest->ob_write_mask);\n    ob_data_valid &= ob_data_check_mask_valid(&update_manifest->ob_compare_mask);\n    ob_data_valid &= ob_data_check_masked_values_valid(\n        &update_manifest->ob_reference, &update_manifest->ob_compare_mask);\n    return ob_data_valid;\n}\n\nbool update_manifest_init(UpdateManifest* update_manifest, const char* manifest_filename) {\n    Storage* storage = furi_record_open(RECORD_STORAGE);\n    FlipperFormat* flipper_file = flipper_format_file_alloc(storage);\n    if(flipper_format_file_open_existing(flipper_file, manifest_filename)) {\n        update_manifest_init_from_ff(update_manifest, flipper_file);\n    }\n\n    flipper_format_free(flipper_file);\n    furi_record_close(RECORD_STORAGE);\n\n    return update_manifest->valid;\n}\n\nbool update_manifest_init_mem(\n    UpdateManifest* update_manifest,\n    const uint8_t* manifest_data,\n    const uint16_t length) {\n    FlipperFormat* flipper_file = flipper_format_string_alloc();\n    Stream* sstream = flipper_format_get_raw_stream(flipper_file);\n\n    stream_write(sstream, manifest_data, length);\n    stream_seek(sstream, 0, StreamOffsetFromStart);\n\n    update_manifest_init_from_ff(update_manifest, flipper_file);\n\n    flipper_format_free(flipper_file);\n\n    return update_manifest->valid;\n}\n"
  },
  {
    "path": "lib/update_util/update_manifest.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <furi.h>\n#include <furi_hal_flash.h>\n\n/* Paths don't include /ext -- because at startup SD card is mounted as FS root */\n#define UPDATE_MANIFEST_DEFAULT_NAME      \"update.fuf\"\n#define UPDATE_MANIFEST_POINTER_FILE_NAME \".fupdate\"\n\ntypedef union {\n    uint8_t raw[6];\n    struct {\n        uint8_t major;\n        uint8_t minor;\n        uint8_t sub;\n        uint8_t branch;\n        uint8_t release;\n        uint8_t type;\n    } version;\n} UpdateManifestRadioVersion;\n_Static_assert(sizeof(UpdateManifestRadioVersion) == 6, \"UpdateManifestRadioVersion size error\");\n\ntypedef struct {\n    uint32_t manifest_version;\n    FuriString* version;\n    uint32_t target;\n    FuriString* staged_loader_file;\n    uint32_t staged_loader_crc;\n    FuriString* firmware_dfu_image;\n    FuriString* radio_image;\n    uint32_t radio_address;\n    UpdateManifestRadioVersion radio_version;\n    uint32_t radio_crc;\n    FuriString* resource_bundle;\n    FuriHalFlashRawOptionByteData ob_reference;\n    FuriHalFlashRawOptionByteData ob_compare_mask;\n    FuriHalFlashRawOptionByteData ob_write_mask;\n    FuriString* splash_file;\n    bool valid;\n} UpdateManifest;\n\nUpdateManifest* update_manifest_alloc(void);\n\nvoid update_manifest_free(UpdateManifest* update_manifest);\n\nbool update_manifest_init(UpdateManifest* update_manifest, const char* manifest_filename);\n\nbool update_manifest_init_mem(\n    UpdateManifest* update_manifest,\n    const uint8_t* manifest_data,\n    const uint16_t length);\n\nbool update_manifest_has_obdata(UpdateManifest* update_manifest);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "scripts/assets.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport shutil\n\nfrom flipper.app import App\nfrom flipper.assets.icon import file2image\n\nICONS_SUPPORTED_FORMATS = [\"png\"]\n\nICONS_TEMPLATE_H_HEADER = \"\"\"#pragma once\n\n#include <gui/icon.h>\n\n\"\"\"\nICONS_TEMPLATE_H_ICON_NAME = \"extern const Icon {name};\\n\"\n\nICONS_TEMPLATE_C_HEADER = \"\"\"#include \"{assets_filename}.h\"\n\n#include <gui/icon_i.h>\n\n\"\"\"\nICONS_TEMPLATE_C_FRAME = \"const uint8_t {name}[] = {data};\\n\"\nICONS_TEMPLATE_C_DATA = \"const uint8_t* const {name}[] = {data};\\n\"\nICONS_TEMPLATE_C_ICONS = \"const Icon {name} = {{.width={width},.height={height},.frame_count={frame_count},.frame_rate={frame_rate},.frames=_{name}}};\\n\"\n\nMAX_IMAGE_WIDTH = 2**16 - 1\nMAX_IMAGE_HEIGHT = 2**16 - 1\n\n\nclass Main(App):\n    def init(self):\n        # command args\n        self.subparsers = self.parser.add_subparsers(help=\"sub-command help\")\n        self.parser_icons = self.subparsers.add_parser(\n            \"icons\", help=\"Process icons and build icon registry\"\n        )\n        self.parser_icons.add_argument(\"input_directory\", help=\"Source directory\")\n        self.parser_icons.add_argument(\"output_directory\", help=\"Output directory\")\n        self.parser_icons.add_argument(\n            \"--filename\",\n            help=\"Base filename for file with icon data\",\n            required=False,\n            default=\"assets_icons\",\n        )\n\n        self.parser_icons.set_defaults(func=self.icons)\n\n        self.parser_manifest = self.subparsers.add_parser(\n            \"manifest\", help=\"Create directory Manifest\"\n        )\n        self.parser_manifest.add_argument(\"local_path\", help=\"local_path\")\n        self.parser_manifest.add_argument(\n            \"--timestamp\",\n            help=\"timestamp value to embed\",\n            default=0,\n            type=int,\n            required=False,\n        )\n        self.parser_manifest.set_defaults(func=self.manifest)\n\n        self.parser_copro = self.subparsers.add_parser(\n            \"copro\", help=\"Gather copro binaries for packaging\"\n        )\n        self.parser_copro.add_argument(\"cube_dir\", help=\"Path to Cube folder\")\n        self.parser_copro.add_argument(\"output_dir\", help=\"Path to output folder\")\n        self.parser_copro.add_argument(\n            \"--cube_ver\", dest=\"cube_ver\", help=\"Cube version\", required=True\n        )\n        self.parser_copro.add_argument(\n            \"--stack_type\", dest=\"stack_type\", help=\"Stack type\", required=True\n        )\n        self.parser_copro.add_argument(\n            \"--stack_file\",\n            dest=\"stack_file\",\n            help=\"Stack file name in copro folder\",\n            required=True,\n        )\n        self.parser_copro.add_argument(\n            \"--stack_addr\",\n            dest=\"stack_addr\",\n            help=\"Stack flash address, as per release_notes\",\n            type=lambda x: int(x, 16),\n            default=0,\n            required=False,\n        )\n        self.parser_copro.set_defaults(func=self.copro)\n\n        self.parser_dolphin = self.subparsers.add_parser(\n            \"dolphin\", help=\"Assemble dolphin resources\"\n        )\n        self.parser_dolphin.add_argument(\n            \"-s\",\n            \"--symbol-name\",\n            help=\"Symbol and file name in dolphin output directory\",\n            default=None,\n        )\n        self.parser_dolphin.add_argument(\n            \"input_directory\", help=\"Dolphin source directory\"\n        )\n        self.parser_dolphin.add_argument(\n            \"output_directory\", help=\"Dolphin output directory\"\n        )\n        self.parser_dolphin.set_defaults(func=self.dolphin)\n\n    def _icon2header(self, file):\n        image = file2image(file)\n        if image.width > MAX_IMAGE_WIDTH or image.height > MAX_IMAGE_HEIGHT:\n            raise Exception(\n                f\"Image {file} is too big ({image.width}x{image.height} vs. {MAX_IMAGE_WIDTH}x{MAX_IMAGE_HEIGHT})\"\n            )\n        return image.width, image.height, image.data_as_carray()\n\n    def _iconIsSupported(self, filename):\n        extension = filename.lower().split(\".\")[-1]\n        return extension in ICONS_SUPPORTED_FORMATS\n\n    def icons(self):\n        self.logger.debug(\"Converting icons\")\n        icons_c = open(\n            os.path.join(self.args.output_directory, f\"{self.args.filename}.c\"),\n            \"w\",\n            newline=\"\\n\",\n        )\n        icons_c.write(\n            ICONS_TEMPLATE_C_HEADER.format(assets_filename=self.args.filename)\n        )\n        icons = []\n        # Traverse icons tree, append image data to source file\n        for dirpath, dirnames, filenames in os.walk(self.args.input_directory):\n            self.logger.debug(f\"Processing directory {dirpath}\")\n            dirnames.sort()\n            filenames.sort()\n            if not filenames:\n                continue\n            if \"frame_rate\" in filenames:\n                self.logger.debug(\"Folder contains animation\")\n                icon_name = \"A_\" + os.path.split(dirpath)[1].replace(\"-\", \"_\")\n                width = height = None\n                frame_count = 0\n                frame_rate = 0\n                frame_names = []\n                for filename in sorted(filenames):\n                    fullfilename = os.path.join(dirpath, filename)\n                    if filename == \"frame_rate\":\n                        frame_rate = int(open(fullfilename, \"r\").read().strip())\n                        continue\n                    elif not self._iconIsSupported(filename):\n                        continue\n                    self.logger.debug(f\"Processing animation frame {filename}\")\n                    temp_width, temp_height, data = self._icon2header(fullfilename)\n                    if width is None:\n                        width = temp_width\n                    if height is None:\n                        height = temp_height\n                    assert width == temp_width\n                    assert height == temp_height\n                    frame_name = f\"_{icon_name}_{frame_count}\"\n                    frame_names.append(frame_name)\n                    icons_c.write(\n                        ICONS_TEMPLATE_C_FRAME.format(name=frame_name, data=data)\n                    )\n                    frame_count += 1\n                assert frame_rate > 0\n                assert frame_count > 0\n                icons_c.write(\n                    ICONS_TEMPLATE_C_DATA.format(\n                        name=f\"_{icon_name}\", data=f'{{{\",\".join(frame_names)}}}'\n                    )\n                )\n                icons_c.write(\"\\n\")\n                icons.append((icon_name, width, height, frame_rate, frame_count))\n            else:\n                # process icons\n                for filename in filenames:\n                    if not self._iconIsSupported(filename):\n                        continue\n                    self.logger.debug(f\"Processing icon {filename}\")\n                    icon_name = \"I_\" + \"_\".join(filename.split(\".\")[:-1]).replace(\n                        \"-\", \"_\"\n                    )\n                    fullfilename = os.path.join(dirpath, filename)\n                    width, height, data = self._icon2header(fullfilename)\n                    frame_name = f\"_{icon_name}_0\"\n                    icons_c.write(\n                        ICONS_TEMPLATE_C_FRAME.format(name=frame_name, data=data)\n                    )\n                    icons_c.write(\n                        ICONS_TEMPLATE_C_DATA.format(\n                            name=f\"_{icon_name}\", data=f\"{{{frame_name}}}\"\n                        )\n                    )\n                    icons_c.write(\"\\n\")\n                    icons.append((icon_name, width, height, 0, 1))\n        # Create array of images:\n        self.logger.debug(\"Finalizing source file\")\n        for name, width, height, frame_rate, frame_count in icons:\n            icons_c.write(\n                ICONS_TEMPLATE_C_ICONS.format(\n                    name=name,\n                    width=width,\n                    height=height,\n                    frame_rate=frame_rate,\n                    frame_count=frame_count,\n                )\n            )\n        icons_c.write(\"\\n\")\n        icons_c.close()\n\n        # Create Public Header\n        self.logger.debug(\"Creating header\")\n        icons_h = open(\n            os.path.join(self.args.output_directory, f\"{self.args.filename}.h\"),\n            \"w\",\n            newline=\"\\n\",\n        )\n        icons_h.write(ICONS_TEMPLATE_H_HEADER)\n        for name, width, height, frame_rate, frame_count in icons:\n            icons_h.write(ICONS_TEMPLATE_H_ICON_NAME.format(name=name))\n        icons_h.close()\n        self.logger.debug(\"Done\")\n        return 0\n\n    def manifest(self):\n        from flipper.assets.manifest import Manifest\n\n        directory_path = os.path.normpath(self.args.local_path)\n        if not os.path.isdir(directory_path):\n            self.logger.error(f'\"{directory_path}\" is not a directory')\n            exit(255)\n\n        manifest_file = os.path.join(directory_path, \"Manifest\")\n        old_manifest = Manifest()\n        if os.path.exists(manifest_file):\n            self.logger.info(\"Manifest is present, loading to compare\")\n            old_manifest.load(manifest_file)\n        self.logger.info(\n            f'Creating temporary Manifest for directory \"{directory_path}\"'\n        )\n        new_manifest = Manifest(self.args.timestamp)\n        new_manifest.create(directory_path)\n\n        self.logger.info(\"Comparing new manifest with existing\")\n        only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest)\n        for record in only_in_old:\n            self.logger.debug(f\"Only in old: {record}\")\n        for record in changed:\n            self.logger.info(f\"Changed: {record}\")\n        for record in only_in_new:\n            self.logger.debug(f\"Only in new: {record}\")\n        if any((only_in_old, changed, only_in_new)):\n            self.logger.info(\n                f\"Manifest updated ({len(only_in_new)} new, {len(only_in_old)} removed, {len(changed)} changed)\"\n            )\n            new_manifest.save(manifest_file)\n        else:\n            self.logger.info(\"Manifest is up-to-date!\")\n\n        self.logger.info(\"Complete\")\n\n        return 0\n\n    def copro(self):\n        from flipper.assets.copro import Copro\n\n        self.logger.info(\"Bundling coprocessor binaries\")\n        copro = Copro()\n        try:\n            self.logger.info(\"Loading CUBE info\")\n            copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver)\n            self.logger.info(\"Bundling\")\n            copro.bundle(\n                self.args.output_dir,\n                self.args.stack_file,\n                self.args.stack_type,\n                self.args.stack_addr,\n            )\n        except Exception as e:\n            self.logger.error(f\"Failed to bundle: {e}\")\n            return 1\n        self.logger.info(\"Complete\")\n\n        return 0\n\n    def dolphin(self):\n        from flipper.assets.dolphin import Dolphin\n\n        self.logger.info(\"Processing Dolphin sources\")\n        dolphin = Dolphin()\n        self.logger.info(\"Loading data\")\n        dolphin.load(self.args.input_directory)\n        self.logger.info(\"Packing\")\n        dolphin.pack(self.args.output_directory, self.args.symbol_name)\n        self.logger.info(\"Complete\")\n\n        return 0\n\n\nif __name__ == \"__main__\":\n    Main()()\n"
  },
  {
    "path": "scripts/bin2dfu.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport struct\nfrom zlib import crc32\n\nfrom flipper.app import App\n\n\nclass Main(App):\n    def init(self):\n        self.parser.add_argument(\"-i\", \"--input\", help=\".bin input path\", required=True)\n        self.parser.add_argument(\n            \"-o\", \"--output\", help=\".dfu output path\", required=True\n        )\n        self.parser.add_argument(\n            \"-a\",\n            \"--address\",\n            help=\"Flash address\",\n            type=lambda x: int(x, 0),\n            required=True,\n        )\n        self.parser.add_argument(\n            \"-l\", \"--label\", help=\"DFU Target label\", required=True\n        )\n        self.parser.add_argument(\n            \"--vid\", help=\"USB Vendor ID\", default=0x0483, type=lambda x: int(x, 0)\n        )\n        self.parser.add_argument(\n            \"--pid\", help=\"USB Product ID\", default=0xDF11, type=lambda x: int(x, 0)\n        )\n\n        self.parser.set_defaults(func=self.convert)\n\n    def convert(self):\n        if not os.path.exists(self.args.input):\n            self.logger.error(f'\"{self.args.input}\" does not exist')\n            return 1\n\n        with open(self.args.input, mode=\"rb\") as file:\n            bindata = file.read()\n\n        data = struct.pack(\"<II\", self.args.address, len(bindata)) + bindata\n\n        # Target prefix\n        szTargetName = self.args.label.encode(\"ascii\")\n\n        data = (\n            struct.pack(\"<6sBI255sII\", b\"Target\", 0, 1, szTargetName, len(data), 1)\n            + data\n        )\n\n        # Prefix\n        data = struct.pack(\"<5sBIB\", b\"DfuSe\", 0x01, len(data) + 11, 1) + data\n\n        # Suffix\n        data += struct.pack(\n            \"<HHHH3sB\", 0xFFFF, self.args.pid, self.args.vid, 0x011A, b\"UFD\", 16\n        )\n\n        dwCRC = ~crc32(data) & 0xFFFFFFFF\n\n        data += struct.pack(\"<I\", dwCRC)\n\n        with open(self.args.output, \"wb\") as file:\n            file.write(data)\n        return 0\n\n\nif __name__ == \"__main__\":\n    Main()()\n"
  },
  {
    "path": "scripts/debug/FreeRTOS/FreeRTOSgdb/Task.py",
    "content": "# File: Task.py\n# Author: Carl Allendorph\n# Date: 05NOV2014\n#\n# Description:\n#  This file contains the implementation of a class to use for\n# inspecting the state of a FreeRTOS Task in GDB\n#\n\nimport gdb\n\n\nclass TaskInspector:\n    TCBType = gdb.lookup_type(\"TCB_t\")\n\n    def __init__(self, handle):\n        self._tcb = None\n        # print(\"Task: Pass Handle: %s\" % str(handle))\n\n        try:\n            if handle.type == TaskInspector.TCBType:\n                self._tcb = handle\n                return\n            else:\n                print(\"Handle Type: %s\" % str(handle.type))\n\n        except AttributeError as aexc:\n            print(\"Attribute Error: %s\" % str(aexc))\n            pass\n        except Exception as exc:\n            print(\"Error Initializing Task Inspector: %s\" % str(exc))\n            raise\n\n        try:\n            tcbPtr = gdb.Value(handle).cast(TaskInspector.TCBType.pointer())\n            self._tcb = tcbPtr.dereference()\n            return\n        except Exception as exc:\n            print(\"Failed to convert Handle Pointer: %s\" % str(handle))\n\n        self._tcb = handle\n\n    def GetName(self):\n        if self._tcb != None:\n            return self._tcb[\"pcTaskName\"].string()\n        else:\n            raise ValueError(\"Invalid TCB\")\n\n    def GetPriority(self):\n        if self._tcb != None:\n            return self._tcb[\"uxPriority\"]\n        else:\n            raise ValueError(\"Invalid TCB\")\n\n    def GetStackMargin(self):\n        if self._tcb != None:\n            topStack = self._tcb[\"pxTopOfStack\"]\n            stackBase = self._tcb[\"pxStack\"]\n            highWater = topStack - stackBase\n            return highWater\n        else:\n            raise ValueError(\"Invalid TCB\")\n"
  },
  {
    "path": "scripts/debug/FreeRTOS/FreeRTOSgdb/__init__.py",
    "content": ""
  },
  {
    "path": "scripts/debug/PyCortexMDebug/cmdebug/__init__.py",
    "content": ""
  },
  {
    "path": "scripts/fbt/__init__.py",
    "content": ""
  },
  {
    "path": "scripts/flipper/__init__.py",
    "content": ""
  },
  {
    "path": "scripts/flipper/assets/__init__.py",
    "content": ""
  }
]